aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CREDITS9
-rw-r--r--Documentation/ABI/testing/configfs-iio21
-rw-r--r--Documentation/ABI/testing/configfs-usb-gadget-sourcesink2
-rw-r--r--Documentation/ABI/testing/sysfs-bus-iio-ina2xx-adc24
-rw-r--r--Documentation/ABI/testing/sysfs-bus-usb27
-rw-r--r--Documentation/ABI/testing/sysfs-class-net-cdc_ncm19
-rw-r--r--Documentation/ABI/testing/sysfs-class-net-mesh4
-rw-r--r--Documentation/ABI/testing/sysfs-class-net-qmi23
-rw-r--r--Documentation/ABI/testing/sysfs-fs-f2fs6
-rw-r--r--Documentation/ABI/testing/sysfs-kernel-livepatch6
-rw-r--r--Documentation/ABI/testing/sysfs-ptp2
-rw-r--r--Documentation/DocBook/device-drivers.tmpl85
-rw-r--r--Documentation/DocBook/media/Makefile6
-rw-r--r--Documentation/DocBook/media/dvb/dvbproperty.xml2
-rw-r--r--Documentation/DocBook/media/dvb/examples.xml2
-rw-r--r--Documentation/DocBook/media/dvb/intro.xml2
-rw-r--r--Documentation/DocBook/media/v4l/capture.c.xml2
-rw-r--r--Documentation/DocBook/media/v4l/compat.xml2
-rw-r--r--Documentation/DocBook/media/v4l/io.xml10
-rw-r--r--Documentation/DocBook/media/v4l/media-controller.xml44
-rw-r--r--Documentation/DocBook/media/v4l/media-ioc-enum-entities.xml104
-rw-r--r--Documentation/DocBook/media/v4l/media-ioc-enum-links.xml56
-rw-r--r--Documentation/DocBook/media/v4l/media-ioc-g-topology.xml394
-rw-r--r--Documentation/DocBook/media/v4l/media-types.xml240
-rw-r--r--Documentation/DocBook/media/v4l/v4l2.xml10
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-create-bufs.xml30
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-info.xml2
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-dbg-g-register.xml2
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-enumstd.xml2
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml28
-rw-r--r--Documentation/DocBook/media_api.tmpl6
-rw-r--r--Documentation/DocBook/mtdnand.tmpl35
-rw-r--r--Documentation/Makefile2
-rw-r--r--Documentation/RCU/Design/Requirements/2013-08-is-it-dead.pngbin0 -> 100825 bytes
-rw-r--r--Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg374
-rw-r--r--Documentation/RCU/Design/Requirements/RCUApplicability.svg237
-rw-r--r--Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg639
-rw-r--r--Documentation/RCU/Design/Requirements/Requirements.html2897
-rw-r--r--Documentation/RCU/Design/Requirements/Requirements.htmlx2741
-rwxr-xr-xDocumentation/RCU/Design/htmlqqz.sh108
-rw-r--r--Documentation/arm/keystone/Overview.txt18
-rw-r--r--Documentation/arm64/silicon-errata.txt58
-rw-r--r--Documentation/block/null_blk.txt3
-rw-r--r--Documentation/cgroup-v1/00-INDEX (renamed from Documentation/cgroups/00-INDEX)2
-rw-r--r--Documentation/cgroup-v1/blkio-controller.txt (renamed from Documentation/cgroups/blkio-controller.txt)82
-rw-r--r--Documentation/cgroup-v1/cgroups.txt (renamed from Documentation/cgroups/cgroups.txt)0
-rw-r--r--Documentation/cgroup-v1/cpuacct.txt (renamed from Documentation/cgroups/cpuacct.txt)0
-rw-r--r--Documentation/cgroup-v1/cpusets.txt (renamed from Documentation/cgroups/cpusets.txt)0
-rw-r--r--Documentation/cgroup-v1/devices.txt (renamed from Documentation/cgroups/devices.txt)0
-rw-r--r--Documentation/cgroup-v1/freezer-subsystem.txt (renamed from Documentation/cgroups/freezer-subsystem.txt)0
-rw-r--r--Documentation/cgroup-v1/hugetlb.txt (renamed from Documentation/cgroups/hugetlb.txt)0
-rw-r--r--Documentation/cgroup-v1/memcg_test.txt (renamed from Documentation/cgroups/memcg_test.txt)0
-rw-r--r--Documentation/cgroup-v1/memory.txt (renamed from Documentation/cgroups/memory.txt)0
-rw-r--r--Documentation/cgroup-v1/net_cls.txt (renamed from Documentation/cgroups/net_cls.txt)0
-rw-r--r--Documentation/cgroup-v1/net_prio.txt (renamed from Documentation/cgroups/net_prio.txt)0
-rw-r--r--Documentation/cgroup-v1/pids.txt (renamed from Documentation/cgroups/pids.txt)0
-rw-r--r--Documentation/cgroup-v2.txt1293
-rw-r--r--Documentation/cgroups/unified-hierarchy.txt647
-rw-r--r--Documentation/cpu-freq/intel-pstate.txt241
-rw-r--r--Documentation/cpu-freq/pcc-cpufreq.txt4
-rw-r--r--Documentation/device-mapper/verity.txt40
-rw-r--r--Documentation/devicetree/bindings/arm/arm,scpi.txt2
-rw-r--r--Documentation/devicetree/bindings/arm/cpus.txt18
-rw-r--r--Documentation/devicetree/bindings/arm/l2c2x0.txt (renamed from Documentation/devicetree/bindings/arm/l2cc.txt)24
-rw-r--r--Documentation/devicetree/bindings/arm/pmu.txt5
-rw-r--r--Documentation/devicetree/bindings/arm/psci.txt25
-rw-r--r--Documentation/devicetree/bindings/arm/secure.txt53
-rw-r--r--Documentation/devicetree/bindings/ata/brcm,sata-brcmstb.txt4
-rw-r--r--Documentation/devicetree/bindings/ata/sata_rcar.txt1
-rw-r--r--Documentation/devicetree/bindings/clock/samsung,s2mps11.txt49
-rw-r--r--Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt2
-rw-r--r--Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt2
-rw-r--r--Documentation/devicetree/bindings/cpufreq/cpufreq-st.txt91
-rw-r--r--Documentation/devicetree/bindings/crypto/rockchip-crypto.txt29
-rw-r--r--Documentation/devicetree/bindings/display/bridge/tda998x.txt4
-rw-r--r--Documentation/devicetree/bindings/dma/renesas,usb-dmac.txt10
-rw-r--r--Documentation/devicetree/bindings/dma/stm32-dma.txt82
-rw-r--r--Documentation/devicetree/bindings/dma/ti-dma-crossbar.txt6
-rw-r--r--Documentation/devicetree/bindings/dma/ti-edma.txt10
-rw-r--r--Documentation/devicetree/bindings/eeprom/eeprom.txt21
-rw-r--r--Documentation/devicetree/bindings/extcon/extcon-arizona.txt60
-rw-r--r--Documentation/devicetree/bindings/extcon/extcon-max3355.txt21
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-mpc8xxx.txt4
-rw-r--r--Documentation/devicetree/bindings/gpio/snps-dwapb-gpio.txt2
-rw-r--r--Documentation/devicetree/bindings/i2c/i2c-at91.txt5
-rw-r--r--Documentation/devicetree/bindings/i2c/i2c-brcmstb.txt2
-rw-r--r--Documentation/devicetree/bindings/i2c/i2c-rcar.txt4
-rw-r--r--Documentation/devicetree/bindings/i2c/i2c.txt36
-rw-r--r--Documentation/devicetree/bindings/i2c/trivial-devices.txt16
-rw-r--r--Documentation/devicetree/bindings/iio/accel/mma8452.txt6
-rw-r--r--Documentation/devicetree/bindings/iio/adc/imx7d-adc.txt22
-rw-r--r--Documentation/devicetree/bindings/iio/adc/mcp320x.txt30
-rw-r--r--Documentation/devicetree/bindings/iio/adc/mcp3422.txt3
-rw-r--r--Documentation/devicetree/bindings/iio/adc/palmas-gpadc.txt48
-rw-r--r--Documentation/devicetree/bindings/iio/adc/ti-adc128s052.txt4
-rw-r--r--Documentation/devicetree/bindings/iio/adc/ti-ads8688.txt20
-rw-r--r--Documentation/devicetree/bindings/iio/health/max30100.txt21
-rw-r--r--Documentation/devicetree/bindings/iio/light/us5182d.txt11
-rw-r--r--Documentation/devicetree/bindings/iio/st-sensors.txt1
-rw-r--r--Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt2
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/goodix.txt14
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt4
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt11
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/allwinner,sunxi-nmi.txt (renamed from Documentation/devicetree/bindings/interrupt-controller/allwinner,sun67i-sc-nmi.txt)2
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt1
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/hisilicon,mbigen-v2.txt74
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/qca,ath79-misc-intc.txt2
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/technologic,ts4800.txt16
-rw-r--r--Documentation/devicetree/bindings/media/i2c/adp1653.txt7
-rw-r--r--Documentation/devicetree/bindings/media/stih407-c8sectpfe.txt20
-rw-r--r--Documentation/devicetree/bindings/memory-controllers/ath79-ddr-controller.txt8
-rw-r--r--Documentation/devicetree/bindings/mfd/arizona.txt24
-rw-r--r--Documentation/devicetree/bindings/mfd/palmas.txt2
-rw-r--r--Documentation/devicetree/bindings/mfd/s2mpa01.txt90
-rw-r--r--Documentation/devicetree/bindings/mfd/s2mps11.txt153
-rw-r--r--Documentation/devicetree/bindings/mfd/samsung,sec-core.txt88
-rw-r--r--Documentation/devicetree/bindings/mfd/syscon.txt4
-rw-r--r--Documentation/devicetree/bindings/mmc/renesas,mmcif.txt1
-rw-r--r--Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt32
-rw-r--r--Documentation/devicetree/bindings/mtd/fsl-quadspi.txt3
-rw-r--r--Documentation/devicetree/bindings/mtd/ingenic,jz4780-nand.txt86
-rw-r--r--Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt56
-rw-r--r--Documentation/devicetree/bindings/mtd/mtk-quadspi.txt41
-rw-r--r--Documentation/devicetree/bindings/mtd/partition.txt9
-rw-r--r--Documentation/devicetree/bindings/net/cdns-emac.txt20
-rw-r--r--Documentation/devicetree/bindings/net/cpsw.txt6
-rw-r--r--Documentation/devicetree/bindings/net/dsa/dsa.txt3
-rw-r--r--Documentation/devicetree/bindings/net/hisilicon-hns-mdio.txt7
-rw-r--r--Documentation/devicetree/bindings/net/ieee802154/adf7242.txt18
-rw-r--r--Documentation/devicetree/bindings/net/macb.txt11
-rw-r--r--Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt6
-rw-r--r--Documentation/devicetree/bindings/net/micrel-ksz90x1.txt17
-rw-r--r--Documentation/devicetree/bindings/net/nfc/st95hf.txt50
-rw-r--r--Documentation/devicetree/bindings/net/renesas,ravb.txt12
-rw-r--r--Documentation/devicetree/bindings/net/socfpga-dwmac.txt2
-rw-r--r--Documentation/devicetree/bindings/net/stmmac.txt25
-rw-r--r--Documentation/devicetree/bindings/opp/opp.txt132
-rw-r--r--Documentation/devicetree/bindings/phy/brcm,brcmstb-sata-phy.txt1
-rw-r--r--Documentation/devicetree/bindings/phy/phy-hi6220-usb.txt16
-rw-r--r--Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt39
-rw-r--r--Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt6
-rw-r--r--Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt1
-rw-r--r--Documentation/devicetree/bindings/phy/ti-phy.txt20
-rw-r--r--Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt3
-rw-r--r--Documentation/devicetree/bindings/pinctrl/brcm,iproc-gpio.txt (renamed from Documentation/devicetree/bindings/pinctrl/brcm,cygnus-gpio.txt)9
-rw-r--r--Documentation/devicetree/bindings/pinctrl/brcm,nsp-gpio.txt80
-rw-r--r--Documentation/devicetree/bindings/pinctrl/lantiq,pinctrl-xway.txt110
-rw-r--r--Documentation/devicetree/bindings/pinctrl/pinctrl-mt65xx.txt9
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,msm8996-pinctrl.txt199
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt1
-rw-r--r--Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt3
-rw-r--r--Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt1
-rw-r--r--Documentation/devicetree/bindings/regulator/lm363x-regulator.txt34
-rw-r--r--Documentation/devicetree/bindings/regulator/pv88060.txt124
-rw-r--r--Documentation/devicetree/bindings/regulator/pv88090.txt65
-rw-r--r--Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.txt (renamed from Documentation/devicetree/bindings/soc/qcom,smd-rpm.txt)84
-rw-r--r--Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt163
-rw-r--r--Documentation/devicetree/bindings/regulator/samsung,s2mpa01.txt79
-rw-r--r--Documentation/devicetree/bindings/regulator/samsung,s2mps11.txt102
-rw-r--r--Documentation/devicetree/bindings/regulator/samsung,s5m8767.txt145
-rw-r--r--Documentation/devicetree/bindings/scsi/hisilicon-sas.txt69
-rw-r--r--Documentation/devicetree/bindings/serial/renesas,sci-serial.txt44
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.txt58
-rw-r--r--Documentation/devicetree/bindings/sound/atmel-classd.txt6
-rw-r--r--Documentation/devicetree/bindings/sound/wm8994.txt2
-rw-r--r--Documentation/devicetree/bindings/spi/sh-msiof.txt1
-rw-r--r--Documentation/devicetree/bindings/spi/spi-mt65xx.txt9
-rw-r--r--Documentation/devicetree/bindings/sram/rockchip-pmu-sram.txt (renamed from Documentation/devicetree/bindings/arm/rockchip/pmu-sram.txt)0
-rw-r--r--Documentation/devicetree/bindings/sram/rockchip-smp-sram.txt (renamed from Documentation/devicetree/bindings/arm/rockchip/smp-sram.txt)2
-rw-r--r--Documentation/devicetree/bindings/sram/samsung-sram.txt (renamed from Documentation/devicetree/bindings/arm/exynos/smp-sysram.txt)2
-rw-r--r--Documentation/devicetree/bindings/sram/sram.txt (renamed from Documentation/devicetree/bindings/misc/sram.txt)0
-rw-r--r--Documentation/devicetree/bindings/sram/sunxi-sram.txt (renamed from Documentation/devicetree/bindings/soc/sunxi/sram.txt)2
-rw-r--r--Documentation/devicetree/bindings/staging/ion/hi6220-ion.txt31
-rw-r--r--Documentation/devicetree/bindings/thermal/rockchip-thermal.txt4
-rw-r--r--Documentation/devicetree/bindings/usb/dwc2.txt1
-rw-r--r--Documentation/devicetree/bindings/usb/dwc3-xilinx.txt33
-rw-r--r--Documentation/devicetree/bindings/usb/mt8173-xhci.txt51
-rw-r--r--Documentation/devicetree/bindings/usb/octeon-usb.txt62
-rw-r--r--Documentation/devicetree/bindings/usb/renesas_usb3.txt23
-rw-r--r--Documentation/devicetree/bindings/usb/renesas_usbhs.txt22
-rw-r--r--Documentation/devicetree/bindings/usb/usb-xhci.txt4
-rw-r--r--Documentation/devicetree/bindings/usb/usb3503.txt5
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.txt1
-rw-r--r--Documentation/dmaengine/client.txt59
-rw-r--r--Documentation/dmaengine/provider.txt20
-rw-r--r--Documentation/dvb/README.dvb-usb4
-rw-r--r--Documentation/dvb/faq.txt2
-rwxr-xr-xDocumentation/dvb/get_dvb_firmware22
-rw-r--r--Documentation/dvb/readme.txt10
-rw-r--r--Documentation/edac.txt10
-rw-r--r--Documentation/fault-injection/notifier-error-inject.txt25
-rw-r--r--Documentation/features/seccomp/seccomp-filter/arch-support.txt2
-rw-r--r--Documentation/features/time/irq-time-acct/arch-support.txt2
-rw-r--r--Documentation/filesystems/Locking6
-rw-r--r--Documentation/filesystems/configfs/configfs.txt57
-rw-r--r--Documentation/filesystems/f2fs.txt10
-rw-r--r--Documentation/filesystems/porting21
-rw-r--r--Documentation/filesystems/proc.txt23
-rw-r--r--Documentation/filesystems/tmpfs.txt8
-rw-r--r--Documentation/filesystems/vfs.txt21
-rw-r--r--Documentation/hwmon/htu2146
-rw-r--r--Documentation/hwmon/ltc381561
-rw-r--r--Documentation/iio/iio_configfs.txt93
-rw-r--r--Documentation/kernel-parameters.txt73
-rw-r--r--Documentation/leds/leds-class.txt13
-rw-r--r--Documentation/md-cluster.txt314
-rw-r--r--Documentation/media-framework.txt372
-rw-r--r--Documentation/memory-barriers.txt12
-rw-r--r--Documentation/mtd/nand_ecc.txt58
-rw-r--r--Documentation/networking/batman-adv.txt9
-rw-r--r--Documentation/networking/e100.txt14
-rw-r--r--Documentation/networking/ip-sysctl.txt31
-rw-r--r--Documentation/networking/switchdev.txt8
-rw-r--r--Documentation/power/pci.txt2
-rw-r--r--Documentation/power/runtime_pm.txt6
-rw-r--r--Documentation/printk-formats.txt6
-rw-r--r--Documentation/s390/zfcpdump.txt22
-rw-r--r--Documentation/spi/00-INDEX4
-rw-r--r--Documentation/spi/Makefile8
-rw-r--r--Documentation/sysctl/kernel.txt15
-rw-r--r--Documentation/sysctl/vm.txt33
-rw-r--r--Documentation/trace/events-msr.txt37
-rw-r--r--Documentation/trace/postprocess/decode_msr.py37
-rw-r--r--Documentation/usb/chipidea.txt4
-rw-r--r--Documentation/usb/gadget-testing.txt4
-rw-r--r--Documentation/usb/power-management.txt11
-rw-r--r--Documentation/video4linux/API.html2
-rw-r--r--Documentation/video4linux/CARDLIST.em28xx4
-rw-r--r--Documentation/video4linux/fimc.txt6
-rw-r--r--Documentation/video4linux/omap4_camera.txt2
-rw-r--r--Documentation/video4linux/si4713.txt2
-rw-r--r--Documentation/video4linux/v4l2-framework.txt12
-rw-r--r--Documentation/video4linux/v4l2-pci-skeleton.c13
-rw-r--r--Documentation/virtual/kvm/api.txt41
-rw-r--r--Documentation/virtual/kvm/devices/vm.txt3
-rw-r--r--Documentation/virtual/kvm/mmu.txt4
-rw-r--r--Documentation/zh_CN/video4linux/v4l2-framework.txt8
-rw-r--r--MAINTAINERS431
-rw-r--r--Makefile2
-rw-r--r--arch/Kconfig68
-rw-r--r--arch/alpha/include/uapi/asm/socket.h3
-rw-r--r--arch/alpha/kernel/module.c2
-rw-r--r--arch/arc/Kconfig1
-rw-r--r--arch/arc/Makefile2
-rw-r--r--arch/arc/boot/dts/axs10x_mb.dtsi1
-rw-r--r--arch/arc/boot/dts/nsim_hs.dts3
-rw-r--r--arch/arc/configs/axs101_defconfig2
-rw-r--r--arch/arc/configs/axs103_defconfig2
-rw-r--r--arch/arc/configs/axs103_smp_defconfig2
-rw-r--r--arch/arc/configs/nsim_hs_defconfig2
-rw-r--r--arch/arc/configs/nsim_hs_smp_defconfig2
-rw-r--r--arch/arc/configs/nsimosci_hs_defconfig2
-rw-r--r--arch/arc/configs/nsimosci_hs_smp_defconfig2
-rw-r--r--arch/arc/configs/vdk_hs38_defconfig2
-rw-r--r--arch/arc/configs/vdk_hs38_smp_defconfig2
-rw-r--r--arch/arc/include/asm/cache.h2
-rw-r--r--arch/arc/include/asm/irqflags-arcv2.h3
-rw-r--r--arch/arc/include/asm/irqflags-compact.h2
-rw-r--r--arch/arc/include/asm/mach_desc.h4
-rw-r--r--arch/arc/include/asm/smp.h4
-rw-r--r--arch/arc/include/asm/unwind.h4
-rw-r--r--arch/arc/kernel/ctx_sw.c2
-rw-r--r--arch/arc/kernel/ctx_sw_asm.S3
-rw-r--r--arch/arc/kernel/intc-arcv2.c15
-rw-r--r--arch/arc/kernel/irq.c33
-rw-r--r--arch/arc/kernel/mcip.c2
-rw-r--r--arch/arc/kernel/perf_event.c32
-rw-r--r--arch/arc/kernel/process.c9
-rw-r--r--arch/arc/kernel/setup.c1
-rw-r--r--arch/arc/kernel/smp.c8
-rw-r--r--arch/arc/kernel/unwind.c98
-rw-r--r--arch/arc/mm/highmem.c4
-rw-r--r--arch/arc/mm/init.c4
-rw-r--r--arch/arc/mm/tlb.c4
-rw-r--r--arch/arm/Kconfig87
-rw-r--r--arch/arm/Kconfig.debug14
-rw-r--r--arch/arm/boot/compressed/Makefile4
-rw-r--r--arch/arm/boot/compressed/efi-header.S130
-rw-r--r--arch/arm/boot/compressed/head.S54
-rw-r--r--arch/arm/boot/compressed/vmlinux.lds.S7
-rw-r--r--arch/arm/boot/dts/am4372.dtsi4
-rw-r--r--arch/arm/boot/dts/am43xx-clocks.dtsi8
-rw-r--r--arch/arm/boot/dts/am57xx-beagle-x15.dts1
-rw-r--r--arch/arm/boot/dts/animeo_ip.dts6
-rw-r--r--arch/arm/boot/dts/armada-38x.dtsi1
-rw-r--r--arch/arm/boot/dts/at91-foxg20.dts2
-rw-r--r--arch/arm/boot/dts/at91-kizbox.dts13
-rw-r--r--arch/arm/boot/dts/at91-kizbox2.dts6
-rw-r--r--arch/arm/boot/dts/at91-kizboxmini.dts4
-rw-r--r--arch/arm/boot/dts/at91-qil_a9260.dts2
-rw-r--r--arch/arm/boot/dts/at91-sama5d2_xplained.dts116
-rw-r--r--arch/arm/boot/dts/at91-sama5d3_xplained.dts2
-rw-r--r--arch/arm/boot/dts/at91-sama5d4_xplained.dts12
-rw-r--r--arch/arm/boot/dts/at91-sama5d4ek.dts12
-rw-r--r--arch/arm/boot/dts/at91rm9200ek.dts9
-rw-r--r--arch/arm/boot/dts/at91sam9261ek.dts19
-rw-r--r--arch/arm/boot/dts/at91sam9263ek.dts13
-rw-r--r--arch/arm/boot/dts/at91sam9g20ek_common.dtsi13
-rw-r--r--arch/arm/boot/dts/at91sam9m10g45ek.dts13
-rw-r--r--arch/arm/boot/dts/at91sam9n12ek.dts11
-rw-r--r--arch/arm/boot/dts/at91sam9rlek.dts13
-rw-r--r--arch/arm/boot/dts/at91sam9x5cm.dtsi11
-rw-r--r--arch/arm/boot/dts/berlin2q.dtsi8
-rw-r--r--arch/arm/boot/dts/dm816x.dtsi8
-rw-r--r--arch/arm/boot/dts/dra7.dtsi4
-rw-r--r--arch/arm/boot/dts/exynos4412.dtsi28
-rw-r--r--arch/arm/boot/dts/imx6q-gw5400-a.dts2
-rw-r--r--arch/arm/boot/dts/imx6qdl-gw51xx.dtsi2
-rw-r--r--arch/arm/boot/dts/imx6qdl-gw52xx.dtsi2
-rw-r--r--arch/arm/boot/dts/imx6qdl-gw53xx.dtsi2
-rw-r--r--arch/arm/boot/dts/imx6qdl-gw54xx.dtsi2
-rw-r--r--arch/arm/boot/dts/imx6qdl-sabreauto.dtsi6
-rw-r--r--arch/arm/boot/dts/k2l-netcp.dtsi2
-rw-r--r--arch/arm/boot/dts/kirkwood-ts219.dtsi2
-rw-r--r--arch/arm/boot/dts/omap4-duovero-parlor.dts4
-rw-r--r--arch/arm/boot/dts/rk3288-veyron-minnie.dts4
-rw-r--r--arch/arm/boot/dts/rk3288.dtsi10
-rw-r--r--arch/arm/boot/dts/sama5d35ek.dts2
-rw-r--r--arch/arm/boot/dts/sama5d4.dtsi2
-rw-r--r--arch/arm/boot/dts/ste-nomadik-stn8815.dtsi6
-rw-r--r--arch/arm/boot/dts/stihxxx-b2120.dtsi46
-rw-r--r--arch/arm/boot/dts/sun6i-a31s-primo81.dts1
-rw-r--r--arch/arm/boot/dts/tegra124-nyan.dtsi2
-rw-r--r--arch/arm/boot/dts/usb_a9260_common.dtsi2
-rw-r--r--arch/arm/boot/dts/usb_a9263.dts2
-rw-r--r--arch/arm/boot/dts/versatile-ab.dts10
-rw-r--r--arch/arm/boot/dts/versatile-pb.dts20
-rw-r--r--arch/arm/boot/dts/vf610-colibri.dtsi5
-rw-r--r--arch/arm/boot/dts/vf610.dtsi2
-rw-r--r--arch/arm/boot/dts/vfxxx.dtsi14
-rw-r--r--arch/arm/boot/dts/wm8650.dtsi9
-rw-r--r--arch/arm/configs/at91_dt_defconfig1
-rw-r--r--arch/arm/configs/multi_v7_defconfig1
-rw-r--r--arch/arm/configs/sama5_defconfig1
-rw-r--r--arch/arm/configs/stm32_defconfig2
-rw-r--r--arch/arm/configs/sunxi_defconfig1
-rw-r--r--arch/arm/include/asm/Kbuild1
-rw-r--r--arch/arm/include/asm/arch_gicv3.h1
-rw-r--r--arch/arm/include/asm/bug.h5
-rw-r--r--arch/arm/include/asm/cpuidle.h2
-rw-r--r--arch/arm/include/asm/efi.h83
-rw-r--r--arch/arm/include/asm/fixmap.h29
-rw-r--r--arch/arm/include/asm/hardirq.h2
-rw-r--r--arch/arm/include/asm/irq.h5
-rw-r--r--arch/arm/include/asm/kvm_arm.h34
-rw-r--r--arch/arm/include/asm/kvm_emulate.h12
-rw-r--r--arch/arm/include/asm/kvm_host.h6
-rw-r--r--arch/arm/include/asm/kvm_mmu.h5
-rw-r--r--arch/arm/include/asm/mach/map.h2
-rw-r--r--arch/arm/include/asm/mmu_context.h2
-rw-r--r--arch/arm/include/asm/paravirt.h20
-rw-r--r--arch/arm/include/asm/psci.h2
-rw-r--r--arch/arm/include/asm/setup.h6
-rw-r--r--arch/arm/include/asm/uaccess.h4
-rw-r--r--arch/arm/include/asm/xen/hypercall.h7
-rw-r--r--arch/arm/include/asm/xen/interface.h3
-rw-r--r--arch/arm/include/uapi/asm/unistd.h1
-rw-r--r--arch/arm/kernel/Makefile6
-rw-r--r--arch/arm/kernel/armksyms.c6
-rw-r--r--arch/arm/kernel/atags.h6
-rw-r--r--arch/arm/kernel/bios32.c19
-rw-r--r--arch/arm/kernel/calls.S1
-rw-r--r--arch/arm/kernel/cpuidle.c2
-rw-r--r--arch/arm/kernel/efi.c38
-rw-r--r--arch/arm/kernel/entry-v7m.S2
-rw-r--r--arch/arm/kernel/module-plts.c2
-rw-r--r--arch/arm/kernel/paravirt.c (renamed from arch/arm64/kernel/psci-call.S)23
-rw-r--r--arch/arm/kernel/perf_event_v7.c321
-rw-r--r--arch/arm/kernel/pj4-cp0.c4
-rw-r--r--arch/arm/kernel/process.c33
-rw-r--r--arch/arm/kernel/setup.c77
-rw-r--r--arch/arm/kernel/smccc-call.S62
-rw-r--r--arch/arm/kernel/smp.c17
-rw-r--r--arch/arm/kernel/swp_emulate.c6
-rw-r--r--arch/arm/kernel/sys_oabi-compat.c73
-rw-r--r--arch/arm/kernel/vdso.c2
-rw-r--r--arch/arm/kvm/arm.c47
-rw-r--r--arch/arm/kvm/emulate.c74
-rw-r--r--arch/arm/kvm/guest.c6
-rw-r--r--arch/arm/kvm/handle_exit.c3
-rw-r--r--arch/arm/kvm/mmio.c8
-rw-r--r--arch/arm/kvm/mmu.c21
-rw-r--r--arch/arm/kvm/psci.c20
-rw-r--r--arch/arm/lib/lib1funcs.S8
-rw-r--r--arch/arm/lib/uaccess_with_memcpy.c29
-rw-r--r--arch/arm/mach-at91/Kconfig6
-rw-r--r--arch/arm/mach-at91/pm.c7
-rw-r--r--arch/arm/mach-davinci/board-da850-evm.c4
-rw-r--r--arch/arm/mach-davinci/board-dm355-evm.c2
-rw-r--r--arch/arm/mach-davinci/board-dm365-evm.c4
-rw-r--r--arch/arm/mach-davinci/board-dm644x-evm.c2
-rw-r--r--arch/arm/mach-davinci/board-dm646x-evm.c4
-rw-r--r--arch/arm/mach-dove/include/mach/entry-macro.S4
-rw-r--r--arch/arm/mach-ep93xx/snappercl15.c4
-rw-r--r--arch/arm/mach-ep93xx/ts72xx.c4
-rw-r--r--arch/arm/mach-exynos/Kconfig1
-rw-r--r--arch/arm/mach-exynos/pmu.c6
-rw-r--r--arch/arm/mach-imx/devices/devices-common.h4
-rw-r--r--arch/arm/mach-imx/gpc.c1
-rw-r--r--arch/arm/mach-imx/mach-qong.c2
-rw-r--r--arch/arm/mach-ixp4xx/include/mach/io.h12
-rw-r--r--arch/arm/mach-ixp4xx/ixdp425-setup.c6
-rw-r--r--arch/arm/mach-omap1/board-nand.c2
-rw-r--r--arch/arm/mach-omap1/include/mach/camera.h2
-rw-r--r--arch/arm/mach-omap2/Kconfig4
-rw-r--r--arch/arm/mach-omap2/board-generic.c12
-rw-r--r--arch/arm/mach-omap2/board-rx51-peripherals.c4
-rw-r--r--arch/arm/mach-omap2/gpmc-onenand.c14
-rw-r--r--arch/arm/mach-omap2/omap-smp.c6
-rw-r--r--arch/arm/mach-omap2/omap_hwmod.c66
-rw-r--r--arch/arm/mach-omap2/omap_hwmod.h3
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_7xx_data.c56
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_81xx_data.c3
-rw-r--r--arch/arm/mach-omap2/pdata-quirks.c29
-rw-r--r--arch/arm/mach-omap2/pm34xx.c4
-rw-r--r--arch/arm/mach-omap2/timer.c6
-rw-r--r--arch/arm/mach-orion5x/include/mach/entry-macro.S2
-rw-r--r--arch/arm/mach-orion5x/ts78xx-setup.c6
-rw-r--r--arch/arm/mach-pxa/balloon3.c2
-rw-r--r--arch/arm/mach-pxa/devices.c2
-rw-r--r--arch/arm/mach-pxa/em-x270.c4
-rw-r--r--arch/arm/mach-pxa/ezx.c7
-rw-r--r--arch/arm/mach-pxa/mioa701.c2
-rw-r--r--arch/arm/mach-pxa/palm27x.c2
-rw-r--r--arch/arm/mach-pxa/palmtc.c2
-rw-r--r--arch/arm/mach-pxa/palmtreo.c2
-rw-r--r--arch/arm/mach-pxa/palmtx.c2
-rw-r--r--arch/arm/mach-pxa/palmz72.c2
-rw-r--r--arch/arm/mach-pxa/pcm990-baseboard.c4
-rw-r--r--arch/arm/mach-s3c24xx/pll-s3c2440-12000000.c2
-rw-r--r--arch/arm/mach-s3c24xx/pll-s3c2440-16934400.c2
-rw-r--r--arch/arm/mach-shmobile/setup-r8a7793.c2
-rw-r--r--arch/arm/mach-sti/Kconfig1
-rw-r--r--arch/arm/mach-ux500/Kconfig1
-rw-r--r--arch/arm/mach-zx/Kconfig2
-rw-r--r--arch/arm/mm/cache-l2x0.c33
-rw-r--r--arch/arm/mm/cache-uniphier.c13
-rw-r--r--arch/arm/mm/context.c38
-rw-r--r--arch/arm/mm/dma-mapping.c2
-rw-r--r--arch/arm/mm/init.c97
-rw-r--r--arch/arm/mm/ioremap.c9
-rw-r--r--arch/arm/mm/mmap.c3
-rw-r--r--arch/arm/mm/mmu.c130
-rw-r--r--arch/arm/mm/proc-v7.S27
-rw-r--r--arch/arm/mm/proc-v7m.S14
-rw-r--r--arch/arm/net/bpf_jit_32.c19
-rw-r--r--arch/arm/plat-samsung/devs.c21
-rw-r--r--arch/arm/xen/enlighten.c95
-rw-r--r--arch/arm/xen/hypercall.S1
-rw-r--r--arch/arm64/Kconfig78
-rw-r--r--arch/arm64/Kconfig.debug14
-rw-r--r--arch/arm64/boot/dts/freescale/fsl-ls2080a.dtsi5
-rw-r--r--arch/arm64/boot/dts/mediatek/mt8173-evb.dts16
-rw-r--r--arch/arm64/boot/dts/mediatek/mt8173.dtsi42
-rw-r--r--arch/arm64/include/asm/alternative.h1
-rw-r--r--arch/arm64/include/asm/arch_gicv3.h1
-rw-r--r--arch/arm64/include/asm/assembler.h11
-rw-r--r--arch/arm64/include/asm/cacheflush.h1
-rw-r--r--arch/arm64/include/asm/cmpxchg.h1
-rw-r--r--arch/arm64/include/asm/cpufeature.h25
-rw-r--r--arch/arm64/include/asm/efi.h9
-rw-r--r--arch/arm64/include/asm/ftrace.h2
-rw-r--r--arch/arm64/include/asm/hugetlb.h44
-rw-r--r--arch/arm64/include/asm/hw_breakpoint.h6
-rw-r--r--arch/arm64/include/asm/irq.h50
-rw-r--r--arch/arm64/include/asm/kvm_arm.h3
-rw-r--r--arch/arm64/include/asm/kvm_asm.h76
-rw-r--r--arch/arm64/include/asm/kvm_emulate.h19
-rw-r--r--arch/arm64/include/asm/kvm_host.h87
-rw-r--r--arch/arm64/include/asm/kvm_mmio.h1
-rw-r--r--arch/arm64/include/asm/kvm_mmu.h9
-rw-r--r--arch/arm64/include/asm/paravirt.h20
-rw-r--r--arch/arm64/include/asm/pgtable-hwdef.h18
-rw-r--r--arch/arm64/include/asm/pgtable.h35
-rw-r--r--arch/arm64/include/asm/shmparam.h2
-rw-r--r--arch/arm64/include/asm/spinlock.h23
-rw-r--r--arch/arm64/include/asm/stacktrace.h9
-rw-r--r--arch/arm64/include/asm/sysreg.h21
-rw-r--r--arch/arm64/include/asm/thread_info.h10
-rw-r--r--arch/arm64/kernel/Makefile5
-rw-r--r--arch/arm64/kernel/alternative.c6
-rw-r--r--arch/arm64/kernel/arm64ksyms.c5
-rw-r--r--arch/arm64/kernel/armv8_deprecated.c6
-rw-r--r--arch/arm64/kernel/asm-offsets.c43
-rw-r--r--arch/arm64/kernel/cpu_errata.c9
-rw-r--r--arch/arm64/kernel/cpufeature.c46
-rw-r--r--arch/arm64/kernel/efi.c327
-rw-r--r--arch/arm64/kernel/entry.S69
-rw-r--r--arch/arm64/kernel/fpsimd.c2
-rw-r--r--arch/arm64/kernel/ftrace.c27
-rw-r--r--arch/arm64/kernel/head.S20
-rw-r--r--arch/arm64/kernel/irq.c3
-rw-r--r--arch/arm64/kernel/module.c71
-rw-r--r--arch/arm64/kernel/paravirt.c25
-rw-r--r--arch/arm64/kernel/perf_callchain.c5
-rw-r--r--arch/arm64/kernel/perf_event.c263
-rw-r--r--arch/arm64/kernel/process.c5
-rw-r--r--arch/arm64/kernel/ptrace.c6
-rw-r--r--arch/arm64/kernel/return_address.c5
-rw-r--r--arch/arm64/kernel/sleep.S3
-rw-r--r--arch/arm64/kernel/smccc-call.S43
-rw-r--r--arch/arm64/kernel/stacktrace.c75
-rw-r--r--arch/arm64/kernel/time.c5
-rw-r--r--arch/arm64/kernel/traps.c52
-rw-r--r--arch/arm64/kernel/vmlinux.lds.S12
-rw-r--r--arch/arm64/kvm/Makefile3
-rw-r--r--arch/arm64/kvm/guest.c10
-rw-r--r--arch/arm64/kvm/handle_exit.c6
-rw-r--r--arch/arm64/kvm/hyp-init.S9
-rw-r--r--arch/arm64/kvm/hyp.S1071
-rw-r--r--arch/arm64/kvm/hyp/Makefile14
-rw-r--r--arch/arm64/kvm/hyp/debug-sr.c140
-rw-r--r--arch/arm64/kvm/hyp/entry.S160
-rw-r--r--arch/arm64/kvm/hyp/fpsimd.S (renamed from arch/arm/kernel/psci-call.S)32
-rw-r--r--arch/arm64/kvm/hyp/hyp-entry.S212
-rw-r--r--arch/arm64/kvm/hyp/hyp.h90
-rw-r--r--arch/arm64/kvm/hyp/switch.c175
-rw-r--r--arch/arm64/kvm/hyp/sysreg-sr.c138
-rw-r--r--arch/arm64/kvm/hyp/timer-sr.c71
-rw-r--r--arch/arm64/kvm/hyp/tlb.c80
-rw-r--r--arch/arm64/kvm/hyp/vgic-v2-sr.c84
-rw-r--r--arch/arm64/kvm/hyp/vgic-v3-sr.c228
-rw-r--r--arch/arm64/kvm/inject_fault.c2
-rw-r--r--arch/arm64/kvm/sys_regs.c168
-rw-r--r--arch/arm64/kvm/sys_regs.h8
-rw-r--r--arch/arm64/kvm/sys_regs_generic_v8.c4
-rw-r--r--arch/arm64/kvm/vgic-v2-switch.S134
-rw-r--r--arch/arm64/kvm/vgic-v3-switch.S269
-rw-r--r--arch/arm64/mm/cache.S28
-rw-r--r--arch/arm64/mm/context.c38
-rw-r--r--arch/arm64/mm/copypage.c3
-rw-r--r--arch/arm64/mm/dma-mapping.c4
-rw-r--r--arch/arm64/mm/fault.c28
-rw-r--r--arch/arm64/mm/flush.c33
-rw-r--r--arch/arm64/mm/hugetlbpage.c274
-rw-r--r--arch/arm64/mm/init.c9
-rw-r--r--arch/arm64/mm/mmap.c8
-rw-r--r--arch/arm64/mm/mmu.c94
-rw-r--r--arch/arm64/mm/pgd.c12
-rw-r--r--arch/arm64/mm/proc-macros.S22
-rw-r--r--arch/arm64/mm/proc.S4
-rw-r--r--arch/arm64/net/bpf_jit_comp.c53
-rw-r--r--arch/arm64/xen/hypercall.S1
-rw-r--r--arch/avr32/include/uapi/asm/socket.h3
-rw-r--r--arch/avr32/kernel/module.c12
-rw-r--r--arch/blackfin/include/asm/cmpxchg.h1
-rw-r--r--arch/blackfin/include/asm/uaccess.h6
-rw-r--r--arch/blackfin/kernel/perf_event.c2
-rw-r--r--arch/blackfin/mach-bf537/boards/stamp.c2
-rw-r--r--arch/blackfin/mach-bf561/boards/acvilon.c2
-rw-r--r--arch/blackfin/mach-bf561/boards/ezkit.c2
-rw-r--r--arch/blackfin/mach-bf609/boards/ezkit.c6
-rw-r--r--arch/c6x/include/asm/Kbuild1
-rw-r--r--arch/c6x/include/asm/clkdev.h22
-rw-r--r--arch/c6x/include/asm/cmpxchg.h2
-rw-r--r--arch/cris/arch-v32/drivers/mach-a3/nandflash.c8
-rw-r--r--arch/cris/arch-v32/drivers/mach-fs/nandflash.c8
-rw-r--r--arch/frv/Kconfig1
-rw-r--r--arch/frv/include/asm/cmpxchg.h2
-rw-r--r--arch/frv/include/uapi/asm/socket.h3
-rw-r--r--arch/h8300/Kconfig1
-rw-r--r--arch/h8300/include/asm/io.h39
-rw-r--r--arch/h8300/kernel/setup.c8
-rw-r--r--arch/ia64/include/asm/barrier.h2
-rw-r--r--arch/ia64/include/asm/early_ioremap.h10
-rw-r--r--arch/ia64/include/asm/io.h5
-rw-r--r--arch/ia64/include/asm/percpu.h2
-rw-r--r--arch/ia64/include/asm/unistd.h2
-rw-r--r--arch/ia64/include/uapi/asm/socket.h3
-rw-r--r--arch/ia64/include/uapi/asm/unistd.h1
-rw-r--r--arch/ia64/kernel/entry.S1
-rw-r--r--arch/ia64/kernel/ftrace.c12
-rw-r--r--arch/ia64/kernel/module.c14
-rw-r--r--arch/ia64/kernel/perfmon.c3
-rw-r--r--arch/m32r/Kconfig1
-rw-r--r--arch/m32r/include/asm/Kbuild1
-rw-r--r--arch/m32r/include/asm/io.h10
-rw-r--r--arch/m32r/include/uapi/asm/socket.h3
-rw-r--r--arch/m32r/kernel/setup.c3
-rw-r--r--arch/m68k/atari/config.c4
-rw-r--r--arch/m68k/coldfire/gpio.c2
-rw-r--r--arch/m68k/coldfire/m54xx.c2
-rw-r--r--arch/m68k/configs/amiga_defconfig4
-rw-r--r--arch/m68k/configs/apollo_defconfig5
-rw-r--r--arch/m68k/configs/atari_defconfig4
-rw-r--r--arch/m68k/configs/bvme6000_defconfig4
-rw-r--r--arch/m68k/configs/hp300_defconfig5
-rw-r--r--arch/m68k/configs/mac_defconfig5
-rw-r--r--arch/m68k/configs/multi_defconfig5
-rw-r--r--arch/m68k/configs/mvme147_defconfig4
-rw-r--r--arch/m68k/configs/mvme16x_defconfig4
-rw-r--r--arch/m68k/configs/q40_defconfig5
-rw-r--r--arch/m68k/configs/sun3_defconfig5
-rw-r--r--arch/m68k/configs/sun3x_defconfig5
-rw-r--r--arch/m68k/include/asm/mac_psc.h1
-rw-r--r--arch/m68k/include/asm/page.h3
-rw-r--r--arch/m68k/include/asm/uaccess_no.h4
-rw-r--r--arch/m68k/include/asm/unistd.h2
-rw-r--r--arch/m68k/include/uapi/asm/unistd.h1
-rw-r--r--arch/m68k/kernel/setup_no.c9
-rw-r--r--arch/m68k/kernel/syscalltable.S1
-rw-r--r--arch/m68k/mac/macints.c6
-rw-r--r--arch/m68k/mac/psc.c7
-rw-r--r--arch/m68k/mm/motorola.c2
-rw-r--r--arch/m68k/sun3/config.c6
-rw-r--r--arch/metag/kernel/ftrace.c11
-rw-r--r--arch/metag/kernel/module.c4
-rw-r--r--arch/microblaze/kernel/dma.c3
-rw-r--r--arch/mips/alchemy/devboards/db1200.c2
-rw-r--r--arch/mips/alchemy/devboards/db1300.c2
-rw-r--r--arch/mips/alchemy/devboards/db1550.c2
-rw-r--r--arch/mips/bcm47xx/setup.c39
-rw-r--r--arch/mips/boot/dts/brcm/bcm6328.dtsi1
-rw-r--r--arch/mips/boot/dts/brcm/bcm7125.dtsi1
-rw-r--r--arch/mips/boot/dts/brcm/bcm7346.dtsi1
-rw-r--r--arch/mips/boot/dts/brcm/bcm7358.dtsi1
-rw-r--r--arch/mips/boot/dts/brcm/bcm7360.dtsi1
-rw-r--r--arch/mips/boot/dts/brcm/bcm7362.dtsi1
-rw-r--r--arch/mips/boot/dts/brcm/bcm7420.dtsi1
-rw-r--r--arch/mips/boot/dts/brcm/bcm7425.dtsi1
-rw-r--r--arch/mips/boot/dts/brcm/bcm7435.dtsi1
-rw-r--r--arch/mips/include/asm/uaccess.h52
-rw-r--r--arch/mips/include/uapi/asm/socket.h3
-rw-r--r--arch/mips/kernel/cps-vec.S2
-rw-r--r--arch/mips/kernel/mips_ksyms.c2
-rw-r--r--arch/mips/kernel/vpe.c6
-rw-r--r--arch/mips/kvm/emulate.c2
-rw-r--r--arch/mips/kvm/locore.S16
-rw-r--r--arch/mips/kvm/mips.c5
-rw-r--r--arch/mips/lasat/picvue_proc.c18
-rw-r--r--arch/mips/lib/memset.S2
-rw-r--r--arch/mips/mm/dma-default.c2
-rw-r--r--arch/mips/net/bpf_jit.c16
-rw-r--r--arch/mips/pci/pci-rt2880.c5
-rw-r--r--arch/mips/pmcs-msp71xx/msp_setup.c5
-rw-r--r--arch/mips/pnx833x/common/platform.c2
-rw-r--r--arch/mips/rb532/devices.c2
-rw-r--r--arch/mips/sni/reset.c8
-rw-r--r--arch/mips/vdso/Makefile4
-rw-r--r--arch/mn10300/Kconfig4
-rw-r--r--arch/mn10300/include/asm/uaccess.h15
-rw-r--r--arch/mn10300/include/uapi/asm/socket.h3
-rw-r--r--arch/nios2/mm/cacheflush.c24
-rw-r--r--arch/parisc/include/asm/pgtable.h3
-rw-r--r--arch/parisc/include/uapi/asm/socket.h3
-rw-r--r--arch/parisc/include/uapi/asm/unistd.h3
-rw-r--r--arch/parisc/kernel/module.c32
-rw-r--r--arch/parisc/kernel/pci.c18
-rw-r--r--arch/parisc/kernel/signal.c64
-rw-r--r--arch/parisc/kernel/syscall_table.S1
-rw-r--r--arch/powerpc/Kconfig1
-rw-r--r--arch/powerpc/Kconfig.debug12
-rw-r--r--arch/powerpc/boot/dts/sbc8641d.dts8
-rw-r--r--arch/powerpc/include/asm/barrier.h2
-rw-r--r--arch/powerpc/include/asm/hvcall.h20
-rw-r--r--arch/powerpc/include/asm/icswx.h1
-rw-r--r--arch/powerpc/include/asm/kvm_host.h4
-rw-r--r--arch/powerpc/include/asm/systbl.h24
-rw-r--r--arch/powerpc/include/asm/uaccess.h15
-rw-r--r--arch/powerpc/include/uapi/asm/socket.h3
-rw-r--r--arch/powerpc/include/uapi/asm/unistd.h12
-rw-r--r--arch/powerpc/kernel/eeh_driver.c14
-rw-r--r--arch/powerpc/kernel/module_32.c6
-rw-r--r--arch/powerpc/kernel/nvram_64.c19
-rw-r--r--arch/powerpc/kvm/book3s_hv.c16
-rw-r--r--arch/powerpc/kvm/book3s_pr.c4
-rw-r--r--arch/powerpc/net/bpf_jit_comp.c13
-rw-r--r--arch/powerpc/platforms/82xx/ep8248e.c10
-rw-r--r--arch/powerpc/platforms/cell/spufs/inode.c2
-rw-r--r--arch/powerpc/platforms/pasemi/gpio_mdio.c3
-rw-r--r--arch/powerpc/platforms/powernv/opal-irqchip.c64
-rw-r--r--arch/powerpc/platforms/powernv/opal.c2
-rw-r--r--arch/powerpc/sysdev/fsl_pci.c28
-rw-r--r--arch/powerpc/sysdev/fsl_pci.h9
-rw-r--r--arch/s390/Kconfig12
-rw-r--r--arch/s390/Kconfig.debug12
-rw-r--r--arch/s390/Makefile9
-rw-r--r--arch/s390/configs/default_defconfig27
-rw-r--r--arch/s390/configs/gcov_defconfig24
-rw-r--r--arch/s390/configs/performance_defconfig24
-rw-r--r--arch/s390/configs/zfcpdump_defconfig10
-rw-r--r--arch/s390/defconfig30
-rw-r--r--arch/s390/include/asm/barrier.h2
-rw-r--r--arch/s390/include/asm/compat.h2
-rw-r--r--arch/s390/include/asm/crw.h14
-rw-r--r--arch/s390/include/asm/elf.h18
-rw-r--r--arch/s390/include/asm/facilities_src.h58
-rw-r--r--arch/s390/include/asm/facility.h17
-rw-r--r--arch/s390/include/asm/fpu/internal.h10
-rw-r--r--arch/s390/include/asm/ipl.h14
-rw-r--r--arch/s390/include/asm/kvm_host.h57
-rw-r--r--arch/s390/include/asm/lowcore.h27
-rw-r--r--arch/s390/include/asm/os_info.h2
-rw-r--r--arch/s390/include/asm/pci_dma.h2
-rw-r--r--arch/s390/include/asm/processor.h12
-rw-r--r--arch/s390/include/asm/ptrace.h38
-rw-r--r--arch/s390/include/asm/reset.h3
-rw-r--r--arch/s390/include/asm/sclp.h21
-rw-r--r--arch/s390/include/asm/setup.h31
-rw-r--r--arch/s390/include/asm/smp.h2
-rw-r--r--arch/s390/include/asm/sysinfo.h17
-rw-r--r--arch/s390/include/asm/thread_info.h2
-rw-r--r--arch/s390/include/asm/topology.h6
-rw-r--r--arch/s390/include/asm/vdso.h6
-rw-r--r--arch/s390/include/uapi/asm/kvm.h5
-rw-r--r--arch/s390/include/uapi/asm/socket.h3
-rw-r--r--arch/s390/kernel/Makefile8
-rw-r--r--arch/s390/kernel/asm-offsets.c175
-rw-r--r--arch/s390/kernel/crash_dump.c454
-rw-r--r--arch/s390/kernel/dis.c21
-rw-r--r--arch/s390/kernel/early.c9
-rw-r--r--arch/s390/kernel/entry.S2
-rw-r--r--arch/s390/kernel/head.S47
-rw-r--r--arch/s390/kernel/head64.S2
-rw-r--r--arch/s390/kernel/ipl.c21
-rw-r--r--arch/s390/kernel/machine_kexec.c110
-rw-r--r--arch/s390/kernel/module.c22
-rw-r--r--arch/s390/kernel/os_info.c7
-rw-r--r--arch/s390/kernel/processor.c6
-rw-r--r--arch/s390/kernel/reipl.S94
-rw-r--r--arch/s390/kernel/sclp.c65
-rw-r--r--arch/s390/kernel/setup.c32
-rw-r--r--arch/s390/kernel/smp.c161
-rw-r--r--arch/s390/kernel/sysinfo.c20
-rw-r--r--arch/s390/kernel/traps.c3
-rw-r--r--arch/s390/kernel/vdso.c17
-rw-r--r--arch/s390/kernel/vdso32/Makefile2
-rw-r--r--arch/s390/kernel/vdso32/getcpu.S43
-rw-r--r--arch/s390/kernel/vdso32/vdso32.lds.S1
-rw-r--r--arch/s390/kernel/vdso64/Makefile2
-rw-r--r--arch/s390/kernel/vdso64/getcpu.S42
-rw-r--r--arch/s390/kernel/vdso64/vdso64.lds.S1
-rw-r--r--arch/s390/kvm/diag.c11
-rw-r--r--arch/s390/kvm/gaccess.c38
-rw-r--r--arch/s390/kvm/intercept.c7
-rw-r--r--arch/s390/kvm/interrupt.c144
-rw-r--r--arch/s390/kvm/kvm-s390.c336
-rw-r--r--arch/s390/kvm/kvm-s390.h7
-rw-r--r--arch/s390/kvm/priv.c4
-rw-r--r--arch/s390/kvm/sigp.c8
-rw-r--r--arch/s390/kvm/trace-s390.h6
-rw-r--r--arch/s390/lib/spinlock.c45
-rw-r--r--arch/s390/mm/extable.c8
-rw-r--r--arch/s390/mm/extmem.c4
-rw-r--r--arch/s390/mm/fault.c2
-rw-r--r--arch/s390/mm/gup.c1
-rw-r--r--arch/s390/mm/maccess.c4
-rw-r--r--arch/s390/mm/mem_detect.c7
-rw-r--r--arch/s390/mm/pgtable.c9
-rw-r--r--arch/s390/net/bpf_jit_comp.c13
-rw-r--r--arch/s390/pci/pci.c3
-rw-r--r--arch/s390/pci/pci_dma.c22
-rw-r--r--arch/s390/tools/.gitignore1
-rw-r--r--arch/s390/tools/Makefile15
-rw-r--r--arch/s390/tools/gen_facilities.c67
-rw-r--r--arch/sh/boards/mach-ap325rxa/setup.c6
-rw-r--r--arch/sh/boards/mach-ecovec24/setup.c10
-rw-r--r--arch/sh/boards/mach-kfr2r09/setup.c4
-rw-r--r--arch/sh/boards/mach-migor/setup.c8
-rw-r--r--arch/sh/boards/mach-se/7724/setup.c6
-rw-r--r--arch/sh/include/uapi/asm/unistd_64.h2
-rw-r--r--arch/sh/kernel/cpu/clock-cpg.c1
-rw-r--r--arch/sh/kernel/cpu/sh2a/clock-sh7264.c9
-rw-r--r--arch/sh/kernel/cpu/sh2a/clock-sh7269.c16
-rw-r--r--arch/sh/kernel/cpu/sh4a/clock-sh7343.c8
-rw-r--r--arch/sh/kernel/cpu/sh4a/clock-sh7366.c6
-rw-r--r--arch/sh/kernel/cpu/sh4a/clock-sh7723.c12
-rw-r--r--arch/sh/kernel/cpu/sh4a/clock-sh7734.c12
-rw-r--r--arch/sh/kernel/cpu/sh4a/clock-sh7757.c6
-rw-r--r--arch/sh/kernel/cpu/sh4a/clock-sh7785.c12
-rw-r--r--arch/sh/kernel/cpu/sh4a/clock-sh7786.c12
-rw-r--r--arch/sh/kernel/cpu/sh4a/clock-shx3.c8
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7734.c12
-rw-r--r--arch/sh/kernel/ftrace.c12
-rw-r--r--arch/sh/kernel/perf_event.c2
-rw-r--r--arch/sparc/include/asm/elf_64.h1
-rw-r--r--arch/sparc/include/asm/uaccess_32.h65
-rw-r--r--arch/sparc/include/asm/uaccess_64.h40
-rw-r--r--arch/sparc/include/uapi/asm/socket.h3
-rw-r--r--arch/sparc/include/uapi/asm/unistd.h7
-rw-r--r--arch/sparc/kernel/head_64.S13
-rw-r--r--arch/sparc/kernel/idprom.c7
-rw-r--r--arch/sparc/kernel/mdesc.c20
-rw-r--r--arch/sparc/kernel/perf_event.c13
-rw-r--r--arch/sparc/kernel/rtrap_64.S8
-rw-r--r--arch/sparc/kernel/setup_64.c9
-rw-r--r--arch/sparc/kernel/systbls_32.S19
-rw-r--r--arch/sparc/kernel/systbls_64.S18
-rw-r--r--arch/sparc/lib/NG2copy_from_user.S8
-rw-r--r--arch/sparc/lib/NG2copy_to_user.S8
-rw-r--r--arch/sparc/lib/NG2memcpy.S118
-rw-r--r--arch/sparc/lib/NG4copy_from_user.S8
-rw-r--r--arch/sparc/lib/NG4copy_to_user.S8
-rw-r--r--arch/sparc/lib/NG4memcpy.S40
-rw-r--r--arch/sparc/lib/U1copy_from_user.S8
-rw-r--r--arch/sparc/lib/U1copy_to_user.S8
-rw-r--r--arch/sparc/lib/U1memcpy.S48
-rw-r--r--arch/sparc/lib/U3copy_from_user.S8
-rw-r--r--arch/sparc/lib/U3copy_to_user.S8
-rw-r--r--arch/sparc/lib/U3memcpy.S86
-rw-r--r--arch/sparc/net/bpf_jit_comp.c17
-rw-r--r--arch/tile/Kconfig15
-rw-r--r--arch/tile/include/asm/cmpxchg.h2
-rw-r--r--arch/tile/include/asm/page.h8
-rw-r--r--arch/tile/kernel/perf_event.c2
-rw-r--r--arch/um/Kconfig.common1
-rw-r--r--arch/um/Kconfig.um16
-rw-r--r--arch/um/Makefile2
-rw-r--r--arch/um/drivers/hostaudio_kern.c10
-rw-r--r--arch/um/drivers/mconsole_kern.c14
-rw-r--r--arch/um/drivers/net_user.c10
-rw-r--r--arch/um/drivers/ubd_kern.c27
-rw-r--r--arch/um/include/asm/hardirq.h23
-rw-r--r--arch/um/include/asm/syscall-generic.h138
-rw-r--r--arch/um/include/asm/thread_info.h2
-rw-r--r--arch/um/include/shared/os.h3
-rw-r--r--arch/um/kernel/signal.c2
-rw-r--r--arch/um/kernel/skas/syscall.c31
-rw-r--r--arch/um/os-Linux/file.c19
-rw-r--r--arch/um/os-Linux/mem.c17
-rw-r--r--arch/um/os-Linux/signal.c16
-rw-r--r--arch/um/os-Linux/skas/process.c7
-rw-r--r--arch/um/os-Linux/start_up.c2
-rw-r--r--arch/unicore32/Kconfig1
-rw-r--r--arch/unicore32/Kconfig.debug14
-rw-r--r--arch/x86/Kconfig39
-rw-r--r--arch/x86/Kconfig.debug19
-rw-r--r--arch/x86/boot/boot.h1
-rw-r--r--arch/x86/boot/video-mode.c2
-rw-r--r--arch/x86/boot/video.c2
-rw-r--r--arch/x86/crypto/chacha20_glue.c2
-rw-r--r--arch/x86/crypto/crc32c-intel_glue.c2
-rw-r--r--arch/x86/crypto/ghash-clmulni-intel_glue.c26
-rw-r--r--arch/x86/entry/calling.h15
-rw-r--r--arch/x86/entry/common.c6
-rw-r--r--arch/x86/entry/entry_32.S15
-rw-r--r--arch/x86/entry/entry_64.S27
-rw-r--r--arch/x86/entry/entry_64_compat.S40
-rw-r--r--arch/x86/entry/syscalls/syscall_32.tbl1
-rw-r--r--arch/x86/entry/syscalls/syscall_64.tbl1
-rw-r--r--arch/x86/entry/vdso/vclock_gettime.c151
-rw-r--r--arch/x86/entry/vdso/vdso-layout.lds.S3
-rw-r--r--arch/x86/entry/vdso/vdso2c.c3
-rw-r--r--arch/x86/entry/vdso/vdso32/system_call.S54
-rw-r--r--arch/x86/entry/vdso/vma.c14
-rw-r--r--arch/x86/include/asm/apic.h6
-rw-r--r--arch/x86/include/asm/atomic.h1
-rw-r--r--arch/x86/include/asm/atomic64_32.h1
-rw-r--r--arch/x86/include/asm/boot.h2
-rw-r--r--arch/x86/include/asm/calgary.h2
-rw-r--r--arch/x86/include/asm/cmpxchg_32.h2
-rw-r--r--arch/x86/include/asm/cmpxchg_64.h2
-rw-r--r--arch/x86/include/asm/cpu.h3
-rw-r--r--arch/x86/include/asm/cpufeature.h116
-rw-r--r--arch/x86/include/asm/fixmap.h5
-rw-r--r--arch/x86/include/asm/fpu/internal.h174
-rw-r--r--arch/x86/include/asm/fpu/xstate.h11
-rw-r--r--arch/x86/include/asm/intel_pt.h10
-rw-r--r--arch/x86/include/asm/iosf_mbi.h51
-rw-r--r--arch/x86/include/asm/ipi.h2
-rw-r--r--arch/x86/include/asm/jump_label.h63
-rw-r--r--arch/x86/include/asm/kvm_host.h75
-rw-r--r--arch/x86/include/asm/lguest.h4
-rw-r--r--arch/x86/include/asm/microcode.h39
-rw-r--r--arch/x86/include/asm/mmu_context.h34
-rw-r--r--arch/x86/include/asm/msi.h6
-rw-r--r--arch/x86/include/asm/msr-index.h1
-rw-r--r--arch/x86/include/asm/msr-trace.h57
-rw-r--r--arch/x86/include/asm/msr.h43
-rw-r--r--arch/x86/include/asm/page_types.h22
-rw-r--r--arch/x86/include/asm/paravirt.h44
-rw-r--r--arch/x86/include/asm/paravirt_types.h40
-rw-r--r--arch/x86/include/asm/pgtable.h15
-rw-r--r--arch/x86/include/asm/pgtable_types.h14
-rw-r--r--arch/x86/include/asm/processor.h1
-rw-r--r--arch/x86/include/asm/pvclock.h14
-rw-r--r--arch/x86/include/asm/qspinlock_paravirt.h59
-rw-r--r--arch/x86/include/asm/reboot.h1
-rw-r--r--arch/x86/include/asm/smp.h12
-rw-r--r--arch/x86/include/asm/suspend_32.h1
-rw-r--r--arch/x86/include/asm/suspend_64.h1
-rw-r--r--arch/x86/include/asm/uaccess.h9
-rw-r--r--arch/x86/include/asm/vdso.h1
-rw-r--r--arch/x86/include/asm/x86_init.h3
-rw-r--r--arch/x86/include/asm/xen/hypercall.h6
-rw-r--r--arch/x86/include/asm/xor_32.h2
-rw-r--r--arch/x86/include/uapi/asm/hyperv.h92
-rw-r--r--arch/x86/include/uapi/asm/mce.h2
-rw-r--r--arch/x86/kernel/apic/apic.c45
-rw-r--r--arch/x86/kernel/apic/apic_flat_64.c19
-rw-r--r--arch/x86/kernel/apic/apic_noop.c2
-rw-r--r--arch/x86/kernel/apic/apic_numachip.c7
-rw-r--r--arch/x86/kernel/apic/bigsmp_32.c10
-rw-r--r--arch/x86/kernel/apic/ipi.c18
-rw-r--r--arch/x86/kernel/apic/msi.c8
-rw-r--r--arch/x86/kernel/apic/probe_32.c1
-rw-r--r--arch/x86/kernel/apic/vector.c2
-rw-r--r--arch/x86/kernel/apic/x2apic_cluster.c9
-rw-r--r--arch/x86/kernel/apic/x2apic_phys.c9
-rw-r--r--arch/x86/kernel/apic/x2apic_uv_x.c1
-rw-r--r--arch/x86/kernel/asm-offsets.c3
-rw-r--r--arch/x86/kernel/asm-offsets_64.c1
-rw-r--r--arch/x86/kernel/cpu/amd.c11
-rw-r--r--arch/x86/kernel/cpu/centaur.c2
-rw-r--r--arch/x86/kernel/cpu/common.c65
-rw-r--r--arch/x86/kernel/cpu/intel.c3
-rw-r--r--arch/x86/kernel/cpu/intel_cacheinfo.c6
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce.c93
-rw-r--r--arch/x86/kernel/cpu/microcode/core.c13
-rw-r--r--arch/x86/kernel/cpu/microcode/intel.c16
-rw-r--r--arch/x86/kernel/cpu/mtrr/cleanup.c11
-rw-r--r--arch/x86/kernel/cpu/mtrr/generic.c2
-rw-r--r--arch/x86/kernel/cpu/mtrr/main.c2
-rw-r--r--arch/x86/kernel/cpu/perf_event.c38
-rw-r--r--arch/x86/kernel/cpu/perf_event.h26
-rw-r--r--arch/x86/kernel/cpu/perf_event_amd.c6
-rw-r--r--arch/x86/kernel/cpu/perf_event_amd_uncore.c11
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel.c117
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_cqm.c2
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_ds.c39
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_lbr.c46
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_pt.c9
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_rapl.c25
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_uncore.c17
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_uncore.h3
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_uncore_snb.c2
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c635
-rw-r--r--arch/x86/kernel/cpu/rdrand.c25
-rw-r--r--arch/x86/kernel/cpu/scattered.c20
-rw-r--r--arch/x86/kernel/cpu/transmeta.c4
-rw-r--r--arch/x86/kernel/cpuid.c24
-rw-r--r--arch/x86/kernel/crash.c11
-rw-r--r--arch/x86/kernel/fpu/init.c182
-rw-r--r--arch/x86/kernel/fpu/xstate.c8
-rw-r--r--arch/x86/kernel/ftrace.c21
-rw-r--r--arch/x86/kernel/hw_breakpoint.c6
-rw-r--r--arch/x86/kernel/irq_work.c2
-rw-r--r--arch/x86/kernel/kvmclock.c11
-rw-r--r--arch/x86/kernel/livepatch.c29
-rw-r--r--arch/x86/kernel/msr.c24
-rw-r--r--arch/x86/kernel/nmi.c32
-rw-r--r--arch/x86/kernel/paravirt.c36
-rw-r--r--arch/x86/kernel/paravirt_patch_32.c2
-rw-r--r--arch/x86/kernel/paravirt_patch_64.c3
-rw-r--r--arch/x86/kernel/pci-calgary_64.c4
-rw-r--r--arch/x86/kernel/pci-swiotlb.c2
-rw-r--r--arch/x86/kernel/pmem.c12
-rw-r--r--arch/x86/kernel/process_64.c2
-rw-r--r--arch/x86/kernel/ptrace.c15
-rw-r--r--arch/x86/kernel/pvclock.c24
-rw-r--r--arch/x86/kernel/reboot.c38
-rw-r--r--arch/x86/kernel/rtc.c3
-rw-r--r--arch/x86/kernel/setup.c4
-rw-r--r--arch/x86/kernel/signal.c17
-rw-r--r--arch/x86/kernel/smp.c4
-rw-r--r--arch/x86/kernel/smpboot.c18
-rw-r--r--arch/x86/kernel/tsc.c2
-rw-r--r--arch/x86/kernel/verify_cpu.S50
-rw-r--r--arch/x86/kernel/vm86_32.c4
-rw-r--r--arch/x86/kernel/x86_init.c1
-rw-r--r--arch/x86/kvm/cpuid.h42
-rw-r--r--arch/x86/kvm/hyperv.c708
-rw-r--r--arch/x86/kvm/hyperv.h55
-rw-r--r--arch/x86/kvm/i8254.c1
-rw-r--r--arch/x86/kvm/ioapic.c4
-rw-r--r--arch/x86/kvm/ioapic.h7
-rw-r--r--arch/x86/kvm/irq.c2
-rw-r--r--arch/x86/kvm/irq_comm.c41
-rw-r--r--arch/x86/kvm/lapic.c40
-rw-r--r--arch/x86/kvm/lapic.h9
-rw-r--r--arch/x86/kvm/mmu.c397
-rw-r--r--arch/x86/kvm/mmu_audit.c15
-rw-r--r--arch/x86/kvm/mtrr.c25
-rw-r--r--arch/x86/kvm/paging_tmpl.h20
-rw-r--r--arch/x86/kvm/svm.c58
-rw-r--r--arch/x86/kvm/trace.h265
-rw-r--r--arch/x86/kvm/vmx.c214
-rw-r--r--arch/x86/kvm/x86.c184
-rw-r--r--arch/x86/lguest/boot.c2
-rw-r--r--arch/x86/lib/Makefile2
-rw-r--r--arch/x86/lib/cpu.c35
-rw-r--r--arch/x86/lib/msr.c26
-rw-r--r--arch/x86/mm/Makefile1
-rw-r--r--arch/x86/mm/debug_pagetables.c46
-rw-r--r--arch/x86/mm/dump_pagetables.c36
-rw-r--r--arch/x86/mm/init_64.c3
-rw-r--r--arch/x86/mm/ioremap.c4
-rw-r--r--arch/x86/mm/mmap.c12
-rw-r--r--arch/x86/mm/mpx.c6
-rw-r--r--arch/x86/mm/pageattr.c13
-rw-r--r--arch/x86/mm/pat.c12
-rw-r--r--arch/x86/mm/pat_rbtree.c52
-rw-r--r--arch/x86/mm/pgtable.c7
-rw-r--r--arch/x86/mm/setup_nx.c4
-rw-r--r--arch/x86/mm/srat.c2
-rw-r--r--arch/x86/mm/tlb.c29
-rw-r--r--arch/x86/net/bpf_jit_comp.c40
-rw-r--r--arch/x86/pci/bus_numa.c13
-rw-r--r--arch/x86/platform/atom/punit_atom_debug.c7
-rw-r--r--arch/x86/platform/intel-quark/imr.c28
-rw-r--r--arch/x86/platform/uv/uv_nmi.c1
-rw-r--r--arch/x86/power/cpu.c92
-rw-r--r--arch/x86/um/Makefile2
-rw-r--r--arch/x86/um/asm/syscall.h1
-rw-r--r--arch/x86/um/ptrace_32.c8
-rw-r--r--arch/x86/um/signal.c18
-rw-r--r--arch/x86/xen/apic.c2
-rw-r--r--arch/x86/xen/enlighten.c26
-rw-r--r--arch/x86/xen/mmu.c10
-rw-r--r--arch/x86/xen/suspend.c24
-rw-r--r--arch/x86/xen/time.c115
-rw-r--r--arch/x86/xen/xen-asm_32.S14
-rw-r--r--arch/x86/xen/xen-asm_64.S19
-rw-r--r--arch/x86/xen/xen-ops.h3
-rw-r--r--arch/xtensa/include/uapi/asm/socket.h3
-rw-r--r--arch/xtensa/platforms/iss/simdisk.c12
-rw-r--r--block/Makefile2
-rw-r--r--block/badblocks.c585
-rw-r--r--block/blk-cgroup.c6
-rw-r--r--block/blk-core.c53
-rw-r--r--block/blk-merge.c35
-rw-r--r--block/blk-mq.c14
-rw-r--r--block/blk-settings.c36
-rw-r--r--block/blk-sysfs.c3
-rw-r--r--block/blk-timeout.c8
-rw-r--r--block/genhd.c30
-rw-r--r--block/ioctl.c71
-rw-r--r--block/noop-iosched.c10
-rw-r--r--block/partition-generic.c2
-rw-r--r--block/partitions/mac.c10
-rw-r--r--crypto/Makefile1
-rw-r--r--crypto/ablkcipher.c2
-rw-r--r--crypto/akcipher.c34
-rw-r--r--crypto/algapi.c9
-rw-r--r--crypto/algif_aead.c14
-rw-r--r--crypto/algif_skcipher.c81
-rw-r--r--crypto/asymmetric_keys/signature.c2
-rw-r--r--crypto/async_tx/async_memcpy.c2
-rw-r--r--crypto/async_tx/async_pq.c4
-rw-r--r--crypto/async_tx/async_raid6_recov.c4
-rw-r--r--crypto/async_tx/async_xor.c4
-rw-r--r--crypto/blkcipher.c2
-rw-r--r--crypto/chacha20poly1305.c8
-rw-r--r--crypto/cryptd.c4
-rw-r--r--crypto/drbg.c6
-rw-r--r--crypto/mcryptd.c8
-rw-r--r--crypto/md5.c6
-rw-r--r--crypto/rsa-pkcs1pad.c628
-rw-r--r--crypto/rsa.c40
-rw-r--r--crypto/sha1_generic.c7
-rw-r--r--crypto/sha256_generic.c16
-rw-r--r--crypto/tcrypt.c2
-rw-r--r--drivers/Makefile4
-rw-r--r--drivers/acpi/Kconfig17
-rw-r--r--drivers/acpi/Makefile9
-rw-r--r--drivers/acpi/acpi_apd.c16
-rw-r--r--drivers/acpi/acpi_dbg.c804
-rw-r--r--drivers/acpi/acpi_lpss.c213
-rw-r--r--drivers/acpi/acpi_pnp.c2
-rw-r--r--drivers/acpi/acpi_video.c76
-rw-r--r--drivers/acpi/acpica/Makefile4
-rw-r--r--drivers/acpi/acpica/acapps.h58
-rw-r--r--drivers/acpi/acpica/acdebug.h41
-rw-r--r--drivers/acpi/acpica/acevents.h11
-rw-r--r--drivers/acpi/acpica/acglobal.h8
-rw-r--r--drivers/acpi/acpica/aclocal.h12
-rw-r--r--drivers/acpi/acpica/acmacros.h11
-rw-r--r--drivers/acpi/acpica/acnamesp.h12
-rw-r--r--drivers/acpi/acpica/acobject.h7
-rw-r--r--drivers/acpi/acpica/acopcode.h10
-rw-r--r--drivers/acpi/acpica/acparser.h8
-rw-r--r--drivers/acpi/acpica/acutils.h26
-rw-r--r--drivers/acpi/acpica/amlcode.h5
-rw-r--r--drivers/acpi/acpica/dbcmds.c11
-rw-r--r--drivers/acpi/acpica/dbdisply.c96
-rw-r--r--drivers/acpi/acpica/dbfileio.c123
-rw-r--r--drivers/acpi/acpica/dbinput.c122
-rw-r--r--drivers/acpi/acpica/dbnames.c2
-rw-r--r--drivers/acpi/acpica/dbstats.c1
-rw-r--r--drivers/acpi/acpica/dbtest.c2
-rw-r--r--drivers/acpi/acpica/dbutils.c1
-rw-r--r--drivers/acpi/acpica/dbxface.c93
-rw-r--r--drivers/acpi/acpica/dsargs.c7
-rw-r--r--drivers/acpi/acpica/dscontrol.c10
-rw-r--r--drivers/acpi/acpica/dsdebug.c5
-rw-r--r--drivers/acpi/acpica/dsfield.c39
-rw-r--r--drivers/acpi/acpica/dsinit.c2
-rw-r--r--drivers/acpi/acpica/dsmethod.c39
-rw-r--r--drivers/acpi/acpica/dsmthdat.c20
-rw-r--r--drivers/acpi/acpica/dsobject.c19
-rw-r--r--drivers/acpi/acpica/dsopcode.c21
-rw-r--r--drivers/acpi/acpica/dsutils.c45
-rw-r--r--drivers/acpi/acpica/dswexec.c35
-rw-r--r--drivers/acpi/acpica/dswload.c10
-rw-r--r--drivers/acpi/acpica/dswload2.c10
-rw-r--r--drivers/acpi/acpica/dswscope.c1
-rw-r--r--drivers/acpi/acpica/evgpe.c2
-rw-r--r--drivers/acpi/acpica/evgpeblk.c1
-rw-r--r--drivers/acpi/acpica/evgpeutil.c1
-rw-r--r--drivers/acpi/acpica/evhandler.c165
-rw-r--r--drivers/acpi/acpica/evmisc.c5
-rw-r--r--drivers/acpi/acpica/evregion.c114
-rw-r--r--drivers/acpi/acpica/evrgnini.c115
-rw-r--r--drivers/acpi/acpica/evxface.c9
-rw-r--r--drivers/acpi/acpica/evxfregn.c38
-rw-r--r--drivers/acpi/acpica/exconfig.c8
-rw-r--r--drivers/acpi/acpica/exconvrt.c9
-rw-r--r--drivers/acpi/acpica/excreate.c20
-rw-r--r--drivers/acpi/acpica/exdebug.c403
-rw-r--r--drivers/acpi/acpica/exdump.c6
-rw-r--r--drivers/acpi/acpica/exfield.c74
-rw-r--r--drivers/acpi/acpica/exfldio.c35
-rw-r--r--drivers/acpi/acpica/exmisc.c49
-rw-r--r--drivers/acpi/acpica/exmutex.c82
-rw-r--r--drivers/acpi/acpica/exnames.c4
-rw-r--r--drivers/acpi/acpica/exoparg1.c33
-rw-r--r--drivers/acpi/acpica/exoparg2.c12
-rw-r--r--drivers/acpi/acpica/exoparg3.c25
-rw-r--r--drivers/acpi/acpica/exoparg6.c1
-rw-r--r--drivers/acpi/acpica/exprep.c25
-rw-r--r--drivers/acpi/acpica/exregion.c13
-rw-r--r--drivers/acpi/acpica/exresnte.c2
-rw-r--r--drivers/acpi/acpica/exresolv.c10
-rw-r--r--drivers/acpi/acpica/exresop.c43
-rw-r--r--drivers/acpi/acpica/exstore.c17
-rw-r--r--drivers/acpi/acpica/exstorob.c7
-rw-r--r--drivers/acpi/acpica/exsystem.c6
-rw-r--r--drivers/acpi/acpica/extrace.c377
-rw-r--r--drivers/acpi/acpica/exutils.c7
-rw-r--r--drivers/acpi/acpica/hwesleep.c4
-rw-r--r--drivers/acpi/acpica/hwgpe.c6
-rw-r--r--drivers/acpi/acpica/hwsleep.c4
-rw-r--r--drivers/acpi/acpica/hwxface.c24
-rw-r--r--drivers/acpi/acpica/hwxfsleep.c97
-rw-r--r--drivers/acpi/acpica/nsconvert.c105
-rw-r--r--drivers/acpi/acpica/nsdump.c5
-rw-r--r--drivers/acpi/acpica/nseval.c2
-rw-r--r--drivers/acpi/acpica/nsinit.c3
-rw-r--r--drivers/acpi/acpica/nsload.c18
-rw-r--r--drivers/acpi/acpica/nsnames.c6
-rw-r--r--drivers/acpi/acpica/nsparse.c5
-rw-r--r--drivers/acpi/acpica/nsprepkg.c10
-rw-r--r--drivers/acpi/acpica/nsrepair.c19
-rw-r--r--drivers/acpi/acpica/nsrepair2.c24
-rw-r--r--drivers/acpi/acpica/nssearch.c2
-rw-r--r--drivers/acpi/acpica/nsutils.c8
-rw-r--r--drivers/acpi/acpica/nsxfeval.c4
-rw-r--r--drivers/acpi/acpica/nsxfname.c39
-rw-r--r--drivers/acpi/acpica/nsxfobj.c6
-rw-r--r--drivers/acpi/acpica/psargs.c19
-rw-r--r--drivers/acpi/acpica/psloop.c12
-rw-r--r--drivers/acpi/acpica/psopcode.c606
-rw-r--r--drivers/acpi/acpica/psparse.c8
-rw-r--r--drivers/acpi/acpica/psutils.c4
-rw-r--r--drivers/acpi/acpica/pswalk.c1
-rw-r--r--drivers/acpi/acpica/rsaddr.c4
-rw-r--r--drivers/acpi/acpica/rscalc.c81
-rw-r--r--drivers/acpi/acpica/rscreate.c32
-rw-r--r--drivers/acpi/acpica/rsdump.c6
-rw-r--r--drivers/acpi/acpica/rslist.c9
-rw-r--r--drivers/acpi/acpica/rsmisc.c22
-rw-r--r--drivers/acpi/acpica/rsutils.c42
-rw-r--r--drivers/acpi/acpica/rsxface.c2
-rw-r--r--drivers/acpi/acpica/tbdata.c1
-rw-r--r--drivers/acpi/acpica/tbinstal.c4
-rw-r--r--drivers/acpi/acpica/tbprint.c1
-rw-r--r--drivers/acpi/acpica/tbutils.c10
-rw-r--r--drivers/acpi/acpica/tbxfload.c3
-rw-r--r--drivers/acpi/acpica/utaddress.c5
-rw-r--r--drivers/acpi/acpica/utcopy.c23
-rw-r--r--drivers/acpi/acpica/utdecode.c32
-rw-r--r--drivers/acpi/acpica/utdelete.c5
-rw-r--r--drivers/acpi/acpica/uterror.c10
-rw-r--r--drivers/acpi/acpica/utfileio.c334
-rw-r--r--drivers/acpi/acpica/uthex.c2
-rw-r--r--drivers/acpi/acpica/utids.c67
-rw-r--r--drivers/acpi/acpica/utinit.c1
-rw-r--r--drivers/acpi/acpica/utmath.c10
-rw-r--r--drivers/acpi/acpica/utmisc.c11
-rw-r--r--drivers/acpi/acpica/utmutex.c22
-rw-r--r--drivers/acpi/acpica/utnonansi.c4
-rw-r--r--drivers/acpi/acpica/utobject.c24
-rw-r--r--drivers/acpi/acpica/utosi.c11
-rw-r--r--drivers/acpi/acpica/utownerid.c12
-rw-r--r--drivers/acpi/acpica/utpredef.c6
-rw-r--r--drivers/acpi/acpica/utprint.c15
-rw-r--r--drivers/acpi/acpica/utresrc.c8
-rw-r--r--drivers/acpi/acpica/utstate.c3
-rw-r--r--drivers/acpi/acpica/utstring.c9
-rw-r--r--drivers/acpi/acpica/uttrack.c50
-rw-r--r--drivers/acpi/acpica/utxface.c2
-rw-r--r--drivers/acpi/acpica/utxferror.c1
-rw-r--r--drivers/acpi/acpica/utxfinit.c47
-rw-r--r--drivers/acpi/acpica/utxfmutex.c6
-rw-r--r--drivers/acpi/bus.c10
-rw-r--r--drivers/acpi/device_sysfs.c2
-rw-r--r--drivers/acpi/gsi.c21
-rw-r--r--drivers/acpi/internal.h8
-rw-r--r--drivers/acpi/nfit.c270
-rw-r--r--drivers/acpi/nfit.h3
-rw-r--r--drivers/acpi/osl.c277
-rw-r--r--drivers/acpi/pci_irq.c3
-rw-r--r--drivers/acpi/pci_link.c132
-rw-r--r--drivers/acpi/pci_root.c7
-rw-r--r--drivers/acpi/processor_driver.c3
-rw-r--r--drivers/acpi/property.c10
-rw-r--r--drivers/acpi/resource.c26
-rw-r--r--drivers/acpi/sbs.c6
-rw-r--r--drivers/acpi/scan.c24
-rw-r--r--drivers/acpi/sleep.c4
-rw-r--r--drivers/acpi/sleep.h6
-rw-r--r--drivers/acpi/utils.c31
-rw-r--r--drivers/acpi/video_detect.c17
-rw-r--r--drivers/ata/Kconfig2
-rw-r--r--drivers/ata/ahci.c89
-rw-r--r--drivers/ata/ahci.h27
-rw-r--r--drivers/ata/ahci_brcmstb.c61
-rw-r--r--drivers/ata/ahci_mvebu.c5
-rw-r--r--drivers/ata/ahci_qoriq.c31
-rw-r--r--drivers/ata/libahci.c55
-rw-r--r--drivers/ata/libata-core.c20
-rw-r--r--drivers/ata/libata-eh.c8
-rw-r--r--drivers/ata/sata_fsl.c3
-rw-r--r--drivers/ata/sata_rcar.c19
-rw-r--r--drivers/ata/sata_sil.c3
-rw-r--r--drivers/ata/sata_sx4.c2
-rw-r--r--drivers/atm/solos-pci.c34
-rw-r--r--drivers/base/base.h2
-rw-r--r--drivers/base/component.c281
-rw-r--r--drivers/base/core.c5
-rw-r--r--drivers/base/dd.c60
-rw-r--r--drivers/base/memory.c26
-rw-r--r--drivers/base/platform-msi.c256
-rw-r--r--drivers/base/platform.c45
-rw-r--r--drivers/base/power/clock_ops.c1
-rw-r--r--drivers/base/power/common.c2
-rw-r--r--drivers/base/power/domain.c38
-rw-r--r--drivers/base/power/domain_governor.c3
-rw-r--r--drivers/base/power/main.c17
-rw-r--r--drivers/base/power/opp/Makefile1
-rw-r--r--drivers/base/power/opp/core.c336
-rw-r--r--drivers/base/power/opp/cpu.c3
-rw-r--r--drivers/base/power/opp/debugfs.c219
-rw-r--r--drivers/base/power/opp/opp.h53
-rw-r--r--drivers/base/power/power.h2
-rw-r--r--drivers/base/power/runtime.c50
-rw-r--r--drivers/base/property.c495
-rw-r--r--drivers/base/regmap/regcache-flat.c2
-rw-r--r--drivers/base/regmap/regcache-lzo.c6
-rw-r--r--drivers/base/regmap/regcache-rbtree.c18
-rw-r--r--drivers/base/regmap/regcache.c41
-rw-r--r--drivers/base/regmap/regmap-debugfs.c69
-rw-r--r--drivers/base/regmap/regmap-irq.c113
-rw-r--r--drivers/base/regmap/regmap-mmio.c66
-rw-r--r--drivers/base/regmap/regmap.c118
-rw-r--r--drivers/bcma/main.c29
-rw-r--r--drivers/block/cciss.c16
-rw-r--r--drivers/block/floppy.c5
-rw-r--r--drivers/block/mtip32xx/mtip32xx.c29
-rw-r--r--drivers/block/nbd.c1
-rw-r--r--drivers/block/null_blk.c314
-rw-r--r--drivers/block/rbd.c1
-rw-r--r--drivers/block/rsxx/core.c11
-rw-r--r--drivers/block/xen-blkback/blkback.c15
-rw-r--r--drivers/block/xen-blkback/common.h8
-rw-r--r--drivers/block/zram/zcomp.c24
-rw-r--r--drivers/block/zram/zcomp.h2
-rw-r--r--drivers/block/zram/zcomp_lz4.c15
-rw-r--r--drivers/block/zram/zcomp_lzo.c15
-rw-r--r--drivers/bluetooth/bcm203x.c4
-rw-r--r--drivers/bluetooth/bfusb.c15
-rw-r--r--drivers/bluetooth/bluecard_cs.c25
-rw-r--r--drivers/bluetooth/bpa10x.c4
-rw-r--r--drivers/bluetooth/bt3c_cs.c11
-rw-r--r--drivers/bluetooth/btintel.c129
-rw-r--r--drivers/bluetooth/btintel.h19
-rw-r--r--drivers/bluetooth/btmrvl_drv.h1
-rw-r--r--drivers/bluetooth/btmrvl_main.c21
-rw-r--r--drivers/bluetooth/btmrvl_sdio.c10
-rw-r--r--drivers/bluetooth/btsdio.c6
-rw-r--r--drivers/bluetooth/btuart_cs.c11
-rw-r--r--drivers/bluetooth/btusb.c186
-rw-r--r--drivers/bluetooth/btwilink.c8
-rw-r--r--drivers/bluetooth/dtl1_cs.c11
-rw-r--r--drivers/bluetooth/hci_ath.c6
-rw-r--r--drivers/bluetooth/hci_bcm.c10
-rw-r--r--drivers/bluetooth/hci_bcsp.c25
-rw-r--r--drivers/bluetooth/hci_h4.c16
-rw-r--r--drivers/bluetooth/hci_h5.c28
-rw-r--r--drivers/bluetooth/hci_intel.c66
-rw-r--r--drivers/bluetooth/hci_ldisc.c13
-rw-r--r--drivers/bluetooth/hci_ll.c4
-rw-r--r--drivers/bluetooth/hci_qca.c4
-rw-r--r--drivers/bluetooth/hci_vhci.c8
-rw-r--r--drivers/bus/omap-ocp2scp.c2
-rw-r--r--drivers/bus/sunxi-rsb.c8
-rw-r--r--drivers/cdrom/cdrom.c10
-rw-r--r--drivers/char/generic_nvram.c21
-rw-r--r--drivers/char/hw_random/core.c6
-rw-r--r--drivers/char/hw_random/omap3-rom-rng.c13
-rw-r--r--drivers/char/hw_random/via-rng.c5
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c7
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c36
-rw-r--r--drivers/char/ipmi/ipmi_ssif.c1
-rw-r--r--drivers/char/mbcs.c28
-rw-r--r--drivers/char/nvram.c18
-rw-r--r--drivers/char/nwflash.c31
-rw-r--r--drivers/clk/clk-gpio.c33
-rw-r--r--drivers/clk/clk-qoriq.c4
-rw-r--r--drivers/clk/clk-scpi.c1
-rw-r--r--drivers/clk/imx/clk-pllv1.c14
-rw-r--r--drivers/clk/imx/clk-pllv2.c9
-rw-r--r--drivers/clk/imx/clk-vf610.c8
-rw-r--r--drivers/clk/mmp/clk-mmp2.c1
-rw-r--r--drivers/clk/mmp/clk-pxa168.c1
-rw-r--r--drivers/clk/mmp/clk-pxa910.c1
-rw-r--r--drivers/clk/sunxi/clk-a10-pll2.c23
-rw-r--r--drivers/clk/ti/clk-816x.c2
-rw-r--r--drivers/clk/ti/clkt_dpll.c4
-rw-r--r--drivers/clk/ti/divider.c16
-rw-r--r--drivers/clk/ti/fapll.c4
-rw-r--r--drivers/clk/ti/mux.c15
-rw-r--r--drivers/clocksource/Kconfig135
-rw-r--r--drivers/clocksource/Makefile2
-rw-r--r--drivers/clocksource/acpi_pm.c27
-rw-r--r--drivers/clocksource/arm_global_timer.c21
-rw-r--r--drivers/clocksource/dw_apb_timer.c46
-rw-r--r--drivers/clocksource/dw_apb_timer_of.c16
-rw-r--r--drivers/clocksource/h8300_timer16.c222
-rw-r--r--drivers/clocksource/h8300_timer8.c264
-rw-r--r--drivers/clocksource/h8300_tpu.c159
-rw-r--r--drivers/clocksource/mmio.c2
-rw-r--r--drivers/clocksource/mtk_timer.c20
-rw-r--r--drivers/clocksource/rockchip_timer.c23
-rw-r--r--drivers/clocksource/tango_xtal.c18
-rw-r--r--drivers/clocksource/tegra20_timer.c3
-rw-r--r--drivers/clocksource/time-lpc32xx.c4
-rw-r--r--drivers/clocksource/time-pistachio.c2
-rw-r--r--drivers/clocksource/timer-sun5i.c16
-rw-r--r--drivers/clocksource/vt8500_timer.c7
-rw-r--r--drivers/connector/connector.c11
-rw-r--r--drivers/cpufreq/Kconfig.arm16
-rw-r--r--drivers/cpufreq/Makefile1
-rw-r--r--drivers/cpufreq/acpi-cpufreq.c24
-rw-r--r--drivers/cpufreq/arm_big_little.c41
-rw-r--r--drivers/cpufreq/blackfin-cpufreq.c2
-rw-r--r--drivers/cpufreq/cppc_cpufreq.c3
-rw-r--r--drivers/cpufreq/cpufreq-dt.c14
-rw-r--r--drivers/cpufreq/cpufreq.c49
-rw-r--r--drivers/cpufreq/cpufreq_conservative.c6
-rw-r--r--drivers/cpufreq/cpufreq_governor.c146
-rw-r--r--drivers/cpufreq/cpufreq_governor.h18
-rw-r--r--drivers/cpufreq/cpufreq_ondemand.c61
-rw-r--r--drivers/cpufreq/intel_pstate.c79
-rw-r--r--drivers/cpufreq/mt8173-cpufreq.c135
-rw-r--r--drivers/cpufreq/pcc-cpufreq.c2
-rw-r--r--drivers/cpufreq/qoriq-cpufreq.c24
-rw-r--r--drivers/cpufreq/s3c24xx-cpufreq.c2
-rw-r--r--drivers/cpufreq/scpi-cpufreq.c2
-rw-r--r--drivers/cpufreq/sti-cpufreq.c294
-rw-r--r--drivers/cpuidle/cpuidle-clps711x.c8
-rw-r--r--drivers/cpuidle/cpuidle-exynos.c5
-rw-r--r--drivers/cpuidle/cpuidle-ux500.c5
-rw-r--r--drivers/cpuidle/governors/menu.c6
-rw-r--r--drivers/crypto/Kconfig18
-rw-r--r--drivers/crypto/Makefile1
-rw-r--r--drivers/crypto/amcc/crypto4xx_core.c4
-rw-r--r--drivers/crypto/atmel-aes-regs.h10
-rw-r--r--drivers/crypto/atmel-aes.c1843
-rw-r--r--drivers/crypto/atmel-sha.c3
-rw-r--r--drivers/crypto/caam/caamhash.c26
-rw-r--r--drivers/crypto/ccp/Kconfig2
-rw-r--r--drivers/crypto/ccp/ccp-ops.c39
-rw-r--r--drivers/crypto/ccp/ccp-pci.c8
-rw-r--r--drivers/crypto/ccp/ccp-platform.c6
-rw-r--r--drivers/crypto/hifn_795x.c512
-rw-r--r--drivers/crypto/ixp4xx_crypto.c6
-rw-r--r--drivers/crypto/marvell/cipher.c8
-rw-r--r--drivers/crypto/marvell/hash.c4
-rw-r--r--drivers/crypto/n2_core.c50
-rw-r--r--drivers/crypto/nx/nx-842-powernv.c23
-rw-r--r--drivers/crypto/nx/nx-aes-ccm.c2
-rw-r--r--drivers/crypto/nx/nx-aes-gcm.c3
-rw-r--r--drivers/crypto/omap-aes.c4
-rw-r--r--drivers/crypto/omap-des.c5
-rw-r--r--drivers/crypto/padlock-aes.c6
-rw-r--r--drivers/crypto/padlock-sha.c2
-rw-r--r--drivers/crypto/picoxcell_crypto.c56
-rw-r--r--drivers/crypto/qat/Kconfig46
-rw-r--r--drivers/crypto/qat/Makefile4
-rw-r--r--drivers/crypto/qat/qat_c3xxx/Makefile3
-rw-r--r--drivers/crypto/qat/qat_c3xxx/adf_c3xxx_hw_data.c238
-rw-r--r--drivers/crypto/qat/qat_c3xxx/adf_c3xxx_hw_data.h83
-rw-r--r--drivers/crypto/qat/qat_c3xxx/adf_drv.c335
-rw-r--r--drivers/crypto/qat/qat_c3xxxvf/Makefile3
-rw-r--r--drivers/crypto/qat/qat_c3xxxvf/adf_c3xxxvf_hw_data.c173
-rw-r--r--drivers/crypto/qat/qat_c3xxxvf/adf_c3xxxvf_hw_data.h (renamed from drivers/crypto/qat/qat_dh895xccvf/adf_drv.h)31
-rw-r--r--drivers/crypto/qat/qat_c3xxxvf/adf_drv.c305
-rw-r--r--drivers/crypto/qat/qat_c62x/Makefile3
-rw-r--r--drivers/crypto/qat/qat_c62x/adf_c62x_hw_data.c248
-rw-r--r--drivers/crypto/qat/qat_c62x/adf_c62x_hw_data.h84
-rw-r--r--drivers/crypto/qat/qat_c62x/adf_drv.c335
-rw-r--r--drivers/crypto/qat/qat_c62xvf/Makefile3
-rw-r--r--drivers/crypto/qat/qat_c62xvf/adf_c62xvf_hw_data.c173
-rw-r--r--drivers/crypto/qat/qat_c62xvf/adf_c62xvf_hw_data.h (renamed from drivers/crypto/qat/qat_dh895xcc/adf_drv.h)32
-rw-r--r--drivers/crypto/qat/qat_c62xvf/adf_drv.c305
-rw-r--r--drivers/crypto/qat/qat_common/Makefile4
-rw-r--r--drivers/crypto/qat/qat_common/adf_accel_devices.h16
-rw-r--r--drivers/crypto/qat/qat_common/adf_accel_engine.c9
-rw-r--r--drivers/crypto/qat/qat_common/adf_admin.c4
-rw-r--r--drivers/crypto/qat/qat_common/adf_aer.c4
-rw-r--r--drivers/crypto/qat/qat_common/adf_cfg_common.h8
-rw-r--r--drivers/crypto/qat/qat_common/adf_common_drv.h31
-rw-r--r--drivers/crypto/qat/qat_common/adf_ctl_drv.c19
-rw-r--r--drivers/crypto/qat/qat_common/adf_dev_mgr.c36
-rw-r--r--drivers/crypto/qat/qat_common/adf_hw_arbiter.c8
-rw-r--r--drivers/crypto/qat/qat_common/adf_init.c21
-rw-r--r--drivers/crypto/qat/qat_common/adf_isr.c (renamed from drivers/crypto/qat/qat_dh895xcc/adf_isr.c)44
-rw-r--r--drivers/crypto/qat/qat_common/adf_pf2vf_msg.c23
-rw-r--r--drivers/crypto/qat/qat_common/adf_transport.c28
-rw-r--r--drivers/crypto/qat/qat_common/adf_transport_access_macros.h5
-rw-r--r--drivers/crypto/qat/qat_common/adf_transport_internal.h2
-rw-r--r--drivers/crypto/qat/qat_common/adf_vf_isr.c (renamed from drivers/crypto/qat/qat_dh895xccvf/adf_isr.c)64
-rw-r--r--drivers/crypto/qat/qat_common/icp_qat_fw_loader_handle.h10
-rw-r--r--drivers/crypto/qat/qat_common/icp_qat_hal.h37
-rw-r--r--drivers/crypto/qat/qat_common/icp_qat_uclo.h165
-rw-r--r--drivers/crypto/qat/qat_common/qat_crypto.c136
-rw-r--r--drivers/crypto/qat/qat_common/qat_hal.c124
-rw-r--r--drivers/crypto/qat/qat_common/qat_uclo.c555
-rw-r--r--drivers/crypto/qat/qat_dh895xcc/Makefile4
-rw-r--r--drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.c5
-rw-r--r--drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.h9
-rw-r--r--drivers/crypto/qat/qat_dh895xcc/adf_drv.c103
-rw-r--r--drivers/crypto/qat/qat_dh895xccvf/Makefile4
-rw-r--r--drivers/crypto/qat/qat_dh895xccvf/adf_dh895xccvf_hw_data.c5
-rw-r--r--drivers/crypto/qat/qat_dh895xccvf/adf_dh895xccvf_hw_data.h10
-rw-r--r--drivers/crypto/qat/qat_dh895xccvf/adf_drv.c96
-rw-r--r--drivers/crypto/qce/ablkcipher.c8
-rw-r--r--drivers/crypto/qce/sha.c5
-rw-r--r--drivers/crypto/rockchip/Makefile3
-rw-r--r--drivers/crypto/rockchip/rk3288_crypto.c394
-rw-r--r--drivers/crypto/rockchip/rk3288_crypto.h216
-rw-r--r--drivers/crypto/rockchip/rk3288_crypto_ablkcipher.c505
-rw-r--r--drivers/crypto/sahara.c42
-rw-r--r--drivers/crypto/sunxi-ss/sun4i-ss-core.c2
-rw-r--r--drivers/crypto/talitos.c126
-rw-r--r--drivers/crypto/talitos.h1
-rw-r--r--drivers/crypto/ux500/Kconfig2
-rw-r--r--drivers/crypto/ux500/hash/hash_core.c20
-rw-r--r--drivers/crypto/vmx/aes_cbc.c2
-rw-r--r--drivers/crypto/vmx/aes_ctr.c2
-rw-r--r--drivers/dca/dca-core.c3
-rw-r--r--drivers/dma/Kconfig12
-rw-r--r--drivers/dma/Makefile1
-rw-r--r--drivers/dma/acpi-dma.c5
-rw-r--r--drivers/dma/at_xdmac.c29
-rw-r--r--drivers/dma/bcm2835-dma.c78
-rw-r--r--drivers/dma/dma-axi-dmac.c8
-rw-r--r--drivers/dma/dmaengine.c178
-rw-r--r--drivers/dma/dw/core.c9
-rw-r--r--drivers/dma/dw/platform.c36
-rw-r--r--drivers/dma/edma.c63
-rw-r--r--drivers/dma/fsl-edma.c85
-rw-r--r--drivers/dma/hsu/hsu.c17
-rw-r--r--drivers/dma/hsu/hsu.h1
-rw-r--r--drivers/dma/idma64.c22
-rw-r--r--drivers/dma/idma64.h3
-rw-r--r--drivers/dma/img-mdc-dma.c78
-rw-r--r--drivers/dma/ioat/dca.c2
-rw-r--r--drivers/dma/ioat/dma.h34
-rw-r--r--drivers/dma/ioat/registers.h16
-rw-r--r--drivers/dma/mv_xor.c95
-rw-r--r--drivers/dma/mv_xor.h2
-rw-r--r--drivers/dma/omap-dma.c82
-rw-r--r--drivers/dma/pxa_dma.c1
-rw-r--r--drivers/dma/sh/Kconfig6
-rw-r--r--drivers/dma/sh/Makefile1
-rw-r--r--drivers/dma/sh/rcar-hpbdma.c669
-rw-r--r--drivers/dma/sh/usb-dmac.c4
-rw-r--r--drivers/dma/ste_dma40.c87
-rw-r--r--drivers/dma/stm32-dma.c1141
-rw-r--r--drivers/dma/tegra20-apb-dma.c73
-rw-r--r--drivers/dma/ti-dma-crossbar.c81
-rw-r--r--drivers/dma/virt-dma.c46
-rw-r--r--drivers/dma/virt-dma.h25
-rw-r--r--drivers/dma/xgene-dma.c4
-rw-r--r--drivers/edac/Makefile2
-rw-r--r--drivers/edac/edac_device.c41
-rw-r--r--drivers/edac/edac_device_sysfs.c11
-rw-r--r--drivers/edac/edac_mc.c29
-rw-r--r--drivers/edac/edac_mc_sysfs.c37
-rw-r--r--drivers/edac/edac_module.c52
-rw-r--r--drivers/edac/edac_module.h10
-rw-r--r--drivers/edac/edac_pci.c70
-rw-r--r--drivers/edac/edac_pci_sysfs.c16
-rw-r--r--drivers/edac/edac_stub.c41
-rw-r--r--drivers/edac/i5100_edac.c4
-rw-r--r--drivers/edac/mpc85xx_edac.c54
-rw-r--r--drivers/edac/mv64x60_edac.c39
-rw-r--r--drivers/edac/sb_edac.c1071
-rw-r--r--drivers/edac/wq.c42
-rw-r--r--drivers/extcon/Kconfig9
-rw-r--r--drivers/extcon/Makefile1
-rw-r--r--drivers/extcon/extcon-arizona.c71
-rw-r--r--drivers/extcon/extcon-max14577.c2
-rw-r--r--drivers/extcon/extcon-max3355.c146
-rw-r--r--drivers/extcon/extcon-max77693.c4
-rw-r--r--drivers/extcon/extcon-max77843.c2
-rw-r--r--drivers/extcon/extcon-rt8973a.c2
-rw-r--r--drivers/firmware/Kconfig3
-rw-r--r--drivers/firmware/dmi_scan.c6
-rw-r--r--drivers/firmware/efi/Makefile4
-rw-r--r--drivers/firmware/efi/arm-init.c209
-rw-r--r--drivers/firmware/efi/arm-runtime.c135
-rw-r--r--drivers/firmware/efi/efi.c2
-rw-r--r--drivers/firmware/efi/libstub/Makefile13
-rw-r--r--drivers/firmware/efi/libstub/arm-stub.c4
-rw-r--r--drivers/firmware/efi/libstub/arm32-stub.c85
-rw-r--r--drivers/firmware/efi/libstub/fdt.c2
-rw-r--r--drivers/firmware/psci.c23
-rw-r--r--drivers/fpga/fpga-mgr.c13
-rw-r--r--drivers/gpio/gpio-74xx-mmio.c7
-rw-r--r--drivers/gpio/gpio-arizona.c4
-rw-r--r--drivers/gpio/gpio-ath79.c2
-rw-r--r--drivers/gpio/gpio-generic.c4
-rw-r--r--drivers/gpio/gpio-omap.c2
-rw-r--r--drivers/gpio/gpio-palmas.c2
-rw-r--r--drivers/gpio/gpio-syscon.c6
-rw-r--r--drivers/gpio/gpio-tegra.c105
-rw-r--r--drivers/gpio/gpiolib-acpi.c76
-rw-r--r--drivers/gpio/gpiolib.c17
-rw-r--r--drivers/gpio/gpiolib.h11
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu.h9
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c67
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_display.c108
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c27
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c48
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_object.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c17
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c11
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c40
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v10_0.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v11_0.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v8_0.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vce_v3_0.c24
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_chardev.c34
-rw-r--r--drivers/gpu/drm/amd/scheduler/gpu_scheduler.c127
-rw-r--r--drivers/gpu/drm/amd/scheduler/gpu_scheduler.h5
-rw-r--r--drivers/gpu/drm/amd/scheduler/sched_fence.c13
-rw-r--r--drivers/gpu/drm/drm_drv.c5
-rw-r--r--drivers/gpu/drm/drm_fops.c84
-rw-r--r--drivers/gpu/drm/drm_irq.c54
-rw-r--r--drivers/gpu/drm/drm_probe_helper.c3
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.c3
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fb.h2
-rw-r--r--drivers/gpu/drm/i915/i915_debugfs.c2
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h29
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c123
-rw-r--r--drivers/gpu/drm/i915/i915_gem_context.c2
-rw-r--r--drivers/gpu/drm/i915/i915_gem_fence.c36
-rw-r--r--drivers/gpu/drm/i915/i915_gem_gtt.c1
-rw-r--r--drivers/gpu/drm/i915/i915_gem_stolen.c1
-rw-r--r--drivers/gpu/drm/i915/intel_display.c135
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c51
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h5
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c19
-rw-r--r--drivers/gpu/drm/i915/intel_i2c.c6
-rw-r--r--drivers/gpu/drm/i915/intel_pm.c5
-rw-r--r--drivers/gpu/drm/i915/intel_runtime_pm.c34
-rw-r--r--drivers/gpu/drm/imx/imx-drm-core.c7
-rw-r--r--drivers/gpu/drm/imx/imx-drm.h3
-rw-r--r--drivers/gpu/drm/imx/imx-tve.c1
-rw-r--r--drivers/gpu/drm/imx/ipuv3-crtc.c63
-rw-r--r--drivers/gpu/drm/imx/ipuv3-plane.c9
-rw-r--r--drivers/gpu/drm/imx/ipuv3-plane.h2
-rw-r--r--drivers/gpu/drm/imx/parallel-display.c4
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/device.h1
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/instmem.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_acpi.c1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c19
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.h4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_usif.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/device/pci.c19
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf117.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpc.fuc8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3.h344
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3.h344
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3.h344
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5.h308
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5.h474
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c6
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/fan.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk104.c2
-rw-r--r--drivers/gpu/drm/omapdrm/omap_fbdev.c5
-rw-r--r--drivers/gpu/drm/radeon/cik.c11
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c5
-rw-r--r--drivers/gpu/drm/radeon/r100.c12
-rw-r--r--drivers/gpu/drm/radeon/r600.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon.h2
-rw-r--r--drivers/gpu/drm/radeon/radeon_agp.c3
-rw-r--r--drivers/gpu/drm/radeon/radeon_connectors.c21
-rw-r--r--drivers/gpu/drm/radeon/radeon_display.c106
-rw-r--r--drivers/gpu/drm/radeon/radeon_irq_kms.c8
-rw-r--r--drivers/gpu/drm/radeon/radeon_kms.c50
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h5
-rw-r--r--drivers/gpu/drm/radeon/radeon_pm.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_vce.c100
-rw-r--r--drivers/gpu/drm/radeon/rs600.c2
-rw-r--r--drivers/gpu/drm/radeon/rs690.c10
-rw-r--r--drivers/gpu/drm/radeon/rv730_dpm.c2
-rw-r--r--drivers/gpu/drm/radeon/rv770_dpm.c4
-rw-r--r--drivers/gpu/drm/radeon/si.c5
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_gem.c1
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop.c43
-rw-r--r--drivers/gpu/drm/ttm/ttm_lock.c2
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_display.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.c1
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.h1
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.c64
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.h7
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c2
-rw-r--r--drivers/gpu/ipu-v3/ipu-common.c69
-rw-r--r--drivers/gpu/ipu-v3/ipu-cpmem.c2
-rw-r--r--drivers/gpu/vga/vgaarb.c13
-rw-r--r--drivers/hid/hid-core.c49
-rw-r--r--drivers/hid/hid-corsair.c13
-rw-r--r--drivers/hid/hid-cp2112.c8
-rw-r--r--drivers/hid/hid-debug.c4
-rw-r--r--drivers/hid/hid-gt683r.c8
-rw-r--r--drivers/hid/hid-ids.h9
-rw-r--r--drivers/hid/hid-input.c21
-rw-r--r--drivers/hid/hid-lenovo.c36
-rw-r--r--drivers/hid/hid-lg.c5
-rw-r--r--drivers/hid/hid-lg4ff.c6
-rw-r--r--drivers/hid/hid-logitech-hidpp.c286
-rw-r--r--drivers/hid/hid-multitouch.c24
-rw-r--r--drivers/hid/hid-ntrig.c32
-rw-r--r--drivers/hid/hid-picolcd_leds.c4
-rw-r--r--drivers/hid/hid-prodikeys.c12
-rw-r--r--drivers/hid/hid-roccat-arvo.c6
-rw-r--r--drivers/hid/hid-roccat-common.c6
-rw-r--r--drivers/hid/hid-roccat-isku.c6
-rw-r--r--drivers/hid/hid-roccat-kone.c12
-rw-r--r--drivers/hid/hid-roccat-koneplus.c12
-rw-r--r--drivers/hid/hid-roccat-kovaplus.c12
-rw-r--r--drivers/hid/hid-roccat-lua.c4
-rw-r--r--drivers/hid/hid-roccat-pyra.c15
-rw-r--r--drivers/hid/hid-sensor-hub.c3
-rw-r--r--drivers/hid/hid-sony.c107
-rw-r--r--drivers/hid/hid-steelseries.c8
-rw-r--r--drivers/hid/hid-wiimote-modules.c8
-rw-r--r--drivers/hid/hid-wiimote.h3
-rw-r--r--drivers/hid/i2c-hid/i2c-hid.c22
-rw-r--r--drivers/hid/usbhid/hid-core.c6
-rw-r--r--drivers/hid/usbhid/hid-quirks.c10
-rw-r--r--drivers/hid/usbhid/usbhid.h2
-rw-r--r--drivers/hid/wacom_sys.c23
-rw-r--r--drivers/hid/wacom_wac.c617
-rw-r--r--drivers/hid/wacom_wac.h5
-rw-r--r--drivers/hsi/controllers/omap_ssi.c7
-rw-r--r--drivers/hsi/controllers/omap_ssi_port.c8
-rw-r--r--drivers/hv/channel.c138
-rw-r--r--drivers/hv/channel_mgmt.c48
-rw-r--r--drivers/hv/connection.c18
-rw-r--r--drivers/hv/hv.c29
-rw-r--r--drivers/hv/hv_fcopy.c37
-rw-r--r--drivers/hv/hv_kvp.c33
-rw-r--r--drivers/hv/hv_snapshot.c36
-rw-r--r--drivers/hv/hv_utils_transport.c130
-rw-r--r--drivers/hv/hv_utils_transport.h3
-rw-r--r--drivers/hv/hyperv_vmbus.h114
-rw-r--r--drivers/hv/ring_buffer.c218
-rw-r--r--drivers/hv/vmbus_drv.c82
-rw-r--r--drivers/hwmon/Kconfig11
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/fam15h_power.c7
-rw-r--r--drivers/hwmon/htu21.c174
-rw-r--r--drivers/hwmon/ibmaem.c12
-rw-r--r--drivers/hwmon/nct6683.c89
-rw-r--r--drivers/hwmon/nct6775.c11
-rw-r--r--drivers/hwmon/pmbus/Kconfig10
-rw-r--r--drivers/hwmon/pmbus/Makefile1
-rw-r--r--drivers/hwmon/pmbus/ltc3815.c215
-rw-r--r--drivers/hwmon/tmp102.c16
-rw-r--r--drivers/hwtracing/coresight/Kconfig2
-rw-r--r--drivers/hwtracing/coresight/coresight.c2
-rw-r--r--drivers/i2c/algos/i2c-algo-bit.c6
-rw-r--r--drivers/i2c/busses/Kconfig9
-rw-r--r--drivers/i2c/busses/i2c-at91.c53
-rw-r--r--drivers/i2c/busses/i2c-bcm2835.c10
-rw-r--r--drivers/i2c/busses/i2c-brcmstb.c80
-rw-r--r--drivers/i2c/busses/i2c-cadence.c73
-rw-r--r--drivers/i2c/busses/i2c-davinci.c11
-rw-r--r--drivers/i2c/busses/i2c-designware-baytrail.c17
-rw-r--r--drivers/i2c/busses/i2c-designware-core.c29
-rw-r--r--drivers/i2c/busses/i2c-designware-core.h1
-rw-r--r--drivers/i2c/busses/i2c-designware-pcidrv.c4
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c99
-rw-r--r--drivers/i2c/busses/i2c-eg20t.c1
-rw-r--r--drivers/i2c/busses/i2c-emev2.c112
-rw-r--r--drivers/i2c/busses/i2c-ibm_iic.c4
-rw-r--r--drivers/i2c/busses/i2c-img-scb.c92
-rw-r--r--drivers/i2c/busses/i2c-imx.c113
-rw-r--r--drivers/i2c/busses/i2c-mt65xx.c54
-rw-r--r--drivers/i2c/busses/i2c-mv64xxx.c27
-rw-r--r--drivers/i2c/busses/i2c-piix4.c202
-rw-r--r--drivers/i2c/busses/i2c-rcar.c323
-rw-r--r--drivers/i2c/busses/i2c-rk3x.c2
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c6
-rw-r--r--drivers/i2c/busses/i2c-st.c5
-rw-r--r--drivers/i2c/busses/i2c-taos-evm.c8
-rw-r--r--drivers/i2c/busses/i2c-uniphier-f.c11
-rw-r--r--drivers/i2c/busses/i2c-uniphier.c11
-rw-r--r--drivers/i2c/busses/i2c-xiic.c12
-rw-r--r--drivers/i2c/busses/i2c-xlr.c250
-rw-r--r--drivers/i2c/i2c-core.c56
-rw-r--r--drivers/iio/Kconfig18
-rw-r--r--drivers/iio/Makefile4
-rw-r--r--drivers/iio/accel/Kconfig42
-rw-r--r--drivers/iio/accel/Makefile6
-rw-r--r--drivers/iio/accel/bmc150-accel-core.c20
-rw-r--r--drivers/iio/accel/kxcjk-1013.c20
-rw-r--r--drivers/iio/accel/mma7455.h19
-rw-r--r--drivers/iio/accel/mma7455_core.c311
-rw-r--r--drivers/iio/accel/mma7455_i2c.c56
-rw-r--r--drivers/iio/accel/mma7455_spi.c52
-rw-r--r--drivers/iio/accel/mma8452.c61
-rw-r--r--drivers/iio/accel/mma9551.c19
-rw-r--r--drivers/iio/accel/mma9553.c20
-rw-r--r--drivers/iio/accel/mxc6255.c198
-rw-r--r--drivers/iio/accel/st_accel.h1
-rw-r--r--drivers/iio/accel/st_accel_core.c1
-rw-r--r--drivers/iio/accel/st_accel_i2c.c5
-rw-r--r--drivers/iio/accel/st_accel_spi.c1
-rw-r--r--drivers/iio/adc/Kconfig43
-rw-r--r--drivers/iio/adc/Makefile4
-rw-r--r--drivers/iio/adc/ad7793.c5
-rw-r--r--drivers/iio/adc/at91_adc.c2
-rw-r--r--drivers/iio/adc/imx7d_adc.c609
-rw-r--r--drivers/iio/adc/ina2xx-adc.c745
-rw-r--r--drivers/iio/adc/mcp320x.c28
-rw-r--r--drivers/iio/adc/mcp3422.c9
-rw-r--r--drivers/iio/adc/palmas_gpadc.c859
-rw-r--r--drivers/iio/adc/qcom-spmi-vadc.c4
-rw-r--r--drivers/iio/adc/ti-adc128s052.c13
-rw-r--r--drivers/iio/adc/ti-ads8688.c486
-rw-r--r--drivers/iio/adc/xilinx-xadc-core.c2
-rw-r--r--drivers/iio/buffer/Kconfig20
-rw-r--r--drivers/iio/buffer/Makefile2
-rw-r--r--drivers/iio/buffer/industrialio-buffer-dma.c683
-rw-r--r--drivers/iio/buffer/industrialio-buffer-dmaengine.c213
-rw-r--r--drivers/iio/chemical/Kconfig8
-rw-r--r--drivers/iio/chemical/Makefile1
-rw-r--r--drivers/iio/chemical/ams-iaq-core.c200
-rw-r--r--drivers/iio/chemical/vz89x.c66
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_core.c3
-rw-r--r--drivers/iio/dummy/Kconfig36
-rw-r--r--drivers/iio/dummy/Makefile10
-rw-r--r--drivers/iio/dummy/iio_dummy_evgen.c (renamed from drivers/staging/iio/iio_dummy_evgen.c)0
-rw-r--r--drivers/iio/dummy/iio_dummy_evgen.h (renamed from drivers/staging/iio/iio_dummy_evgen.h)0
-rw-r--r--drivers/iio/dummy/iio_simple_dummy.c (renamed from drivers/staging/iio/iio_simple_dummy.c)0
-rw-r--r--drivers/iio/dummy/iio_simple_dummy.h (renamed from drivers/staging/iio/iio_simple_dummy.h)0
-rw-r--r--drivers/iio/dummy/iio_simple_dummy_buffer.c (renamed from drivers/staging/iio/iio_simple_dummy_buffer.c)0
-rw-r--r--drivers/iio/dummy/iio_simple_dummy_events.c (renamed from drivers/staging/iio/iio_simple_dummy_events.c)2
-rw-r--r--drivers/iio/gyro/adis16136.c4
-rw-r--r--drivers/iio/gyro/bmg160_core.c19
-rw-r--r--drivers/iio/health/Kconfig21
-rw-r--r--drivers/iio/health/Makefile7
-rw-r--r--drivers/iio/health/max30100.c453
-rw-r--r--drivers/iio/imu/adis16400_core.c6
-rw-r--r--drivers/iio/imu/adis16480.c4
-rw-r--r--drivers/iio/imu/kmx61.c24
-rw-r--r--drivers/iio/industrialio-buffer.c60
-rw-r--r--drivers/iio/industrialio-configfs.c51
-rw-r--r--drivers/iio/industrialio-core.c12
-rw-r--r--drivers/iio/industrialio-sw-trigger.c184
-rw-r--r--drivers/iio/inkern.c6
-rw-r--r--drivers/iio/light/apds9960.c1
-rw-r--r--drivers/iio/light/lm3533-als.c4
-rw-r--r--drivers/iio/light/pa12203001.c16
-rw-r--r--drivers/iio/light/rpr0521.c14
-rw-r--r--drivers/iio/light/us5182d.c609
-rw-r--r--drivers/iio/magnetometer/bmc150_magn.c20
-rw-r--r--drivers/iio/proximity/pulsedlight-lidar-lite-v2.c157
-rw-r--r--drivers/iio/trigger/Kconfig10
-rw-r--r--drivers/iio/trigger/Makefile2
-rw-r--r--drivers/iio/trigger/iio-trig-hrtimer.c193
-rw-r--r--drivers/infiniband/core/cma.c21
-rw-r--r--drivers/infiniband/core/mad.c5
-rw-r--r--drivers/infiniband/core/sa_query.c32
-rw-r--r--drivers/infiniband/core/uverbs_cmd.c27
-rw-r--r--drivers/infiniband/core/verbs.c43
-rw-r--r--drivers/infiniband/hw/cxgb4/cm.c2
-rw-r--r--drivers/infiniband/hw/mlx4/mad.c17
-rw-r--r--drivers/infiniband/hw/mlx4/main.c2
-rw-r--r--drivers/infiniband/hw/mlx4/qp.c19
-rw-r--r--drivers/infiniband/hw/mlx4/srq.c13
-rw-r--r--drivers/infiniband/hw/mlx5/main.c463
-rw-r--r--drivers/infiniband/hw/mlx5/mlx5_ib.h45
-rw-r--r--drivers/infiniband/hw/mlx5/mr.c14
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma.h10
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_hw.c49
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_hw.h4
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_main.c57
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_sli.h49
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_verbs.c2
-rw-r--r--drivers/infiniband/hw/qib/qib_qsfp.c4
-rw-r--r--drivers/infiniband/hw/qib/qib_verbs.h2
-rw-r--r--drivers/infiniband/ulp/iser/iser_verbs.c2
-rw-r--r--drivers/infiniband/ulp/isert/ib_isert.c13
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.c48
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.h5
-rw-r--r--drivers/input/joystick/db9.c1
-rw-r--r--drivers/input/joystick/gamecon.c1
-rw-r--r--drivers/input/joystick/turbografx.c1
-rw-r--r--drivers/input/joystick/walkera0701.c1
-rw-r--r--drivers/input/keyboard/gpio_keys.c29
-rw-r--r--drivers/input/keyboard/omap-keypad.c10
-rw-r--r--drivers/input/misc/arizona-haptics.c3
-rw-r--r--drivers/input/misc/bma150.c2
-rw-r--r--drivers/input/misc/da9063_onkey.c8
-rw-r--r--drivers/input/misc/sparcspkr.c18
-rw-r--r--drivers/input/misc/uinput.c158
-rw-r--r--drivers/input/mouse/alps.c22
-rw-r--r--drivers/input/mouse/elan_i2c_core.c3
-rw-r--r--drivers/input/mouse/elantech.c2
-rw-r--r--drivers/input/mouse/focaltech.c22
-rw-r--r--drivers/input/mouse/focaltech.h8
-rw-r--r--drivers/input/mouse/logips2pp.c2
-rw-r--r--drivers/input/mouse/logips2pp.h4
-rw-r--r--drivers/input/mouse/psmouse-base.c770
-rw-r--r--drivers/input/serio/hyperv-keyboard.c10
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h7
-rw-r--r--drivers/input/serio/parkbd.c1
-rw-r--r--drivers/input/tablet/aiptek.c9
-rw-r--r--drivers/input/touchscreen/Kconfig27
-rw-r--r--drivers/input/touchscreen/Makefile2
-rw-r--r--drivers/input/touchscreen/atmel_mxt_ts.c34
-rw-r--r--drivers/input/touchscreen/egalax_ts_serial.c194
-rw-r--r--drivers/input/touchscreen/elants_i2c.c21
-rw-r--r--drivers/input/touchscreen/goodix.c513
-rw-r--r--drivers/input/touchscreen/pcap_ts.c2
-rw-r--r--drivers/input/touchscreen/pixcir_i2c_ts.c41
-rw-r--r--drivers/input/touchscreen/rohm_bu21023.c2
-rw-r--r--drivers/input/touchscreen/sur40.c13
-rw-r--r--drivers/input/touchscreen/ti_am335x_tsc.c2
-rw-r--r--drivers/input/touchscreen/ts4800-ts.c216
-rw-r--r--drivers/input/touchscreen/wacom_w8001.c325
-rw-r--r--drivers/iommu/amd_iommu_v2.c20
-rw-r--r--drivers/iommu/dma-iommu.c11
-rw-r--r--drivers/iommu/intel-iommu.c4
-rw-r--r--drivers/iommu/intel-svm.c20
-rw-r--r--drivers/iommu/intel_irq_remapping.c2
-rw-r--r--drivers/iommu/iommu.c2
-rw-r--r--drivers/iommu/ipmmu-vmsa.c2
-rw-r--r--drivers/irqchip/Kconfig19
-rw-r--r--drivers/irqchip/Makefile3
-rw-r--r--drivers/irqchip/irq-bcm2836.c55
-rw-r--r--drivers/irqchip/irq-gic-realview.c43
-rw-r--r--drivers/irqchip/irq-gic-v2m.c165
-rw-r--r--drivers/irqchip/irq-gic.c64
-rw-r--r--drivers/irqchip/irq-mbigen.c297
-rw-r--r--drivers/irqchip/irq-omap-intc.c28
-rw-r--r--drivers/irqchip/irq-renesas-h8300h.c8
-rw-r--r--drivers/irqchip/irq-renesas-intc-irqpin.c96
-rw-r--r--drivers/irqchip/irq-sunxi-nmi.c13
-rw-r--r--drivers/irqchip/irq-ts4800.c163
-rw-r--r--drivers/irqchip/irq-versatile-fpga.c5
-rw-r--r--drivers/irqchip/irq-zevio.c3
-rw-r--r--drivers/isdn/Makefile1
-rw-r--r--drivers/isdn/act2000/module.c2
-rw-r--r--drivers/isdn/gigaset/ser-gigaset.c23
-rw-r--r--drivers/isdn/hardware/mISDN/mISDNipac.c7
-rw-r--r--drivers/isdn/hisax/config.c2
-rw-r--r--drivers/isdn/hisax/hfc_pci.c2
-rw-r--r--drivers/isdn/hisax/hfc_sx.c2
-rw-r--r--drivers/isdn/hisax/q931.c6
-rw-r--r--drivers/isdn/i4l/Kconfig2
-rw-r--r--drivers/isdn/sc/Kconfig8
-rw-r--r--drivers/isdn/sc/Makefile10
-rw-r--r--drivers/isdn/sc/card.h131
-rw-r--r--drivers/isdn/sc/command.c363
-rw-r--r--drivers/isdn/sc/event.c68
-rw-r--r--drivers/isdn/sc/hardware.h110
-rw-r--r--drivers/isdn/sc/includes.h16
-rw-r--r--drivers/isdn/sc/init.c549
-rw-r--r--drivers/isdn/sc/interrupt.c247
-rw-r--r--drivers/isdn/sc/ioctl.c582
-rw-r--r--drivers/isdn/sc/message.c230
-rw-r--r--drivers/isdn/sc/message.h245
-rw-r--r--drivers/isdn/sc/packet.c204
-rw-r--r--drivers/isdn/sc/scioc.h110
-rw-r--r--drivers/isdn/sc/shmem.c138
-rw-r--r--drivers/isdn/sc/timer.c122
-rw-r--r--drivers/leds/Kconfig2
-rw-r--r--drivers/leds/led-class-flash.c8
-rw-r--r--drivers/leds/led-class.c11
-rw-r--r--drivers/leds/led-core.c120
-rw-r--r--drivers/leds/led-triggers.c28
-rw-r--r--drivers/leds/leds-88pm860x.c23
-rw-r--r--drivers/leds/leds-aat1290.c59
-rw-r--r--drivers/leds/leds-adp5520.c26
-rw-r--r--drivers/leds/leds-bcm6328.c63
-rw-r--r--drivers/leds/leds-bcm6358.c37
-rw-r--r--drivers/leds/leds-bd2802.c39
-rw-r--r--drivers/leds/leds-blinkm.c87
-rw-r--r--drivers/leds/leds-da903x.c46
-rw-r--r--drivers/leds/leds-da9052.c39
-rw-r--r--drivers/leds/leds-dac124s085.c38
-rw-r--r--drivers/leds/leds-gpio.c62
-rw-r--r--drivers/leds/leds-ipaq-micro.c6
-rw-r--r--drivers/leds/leds-ktd2692.c50
-rw-r--r--drivers/leds/leds-lm3533.c30
-rw-r--r--drivers/leds/leds-lm355x.c85
-rw-r--r--drivers/leds/leds-lm3642.c73
-rw-r--r--drivers/leds/leds-lp3944.c32
-rw-r--r--drivers/leds/leds-lp5521.c11
-rw-r--r--drivers/leds/leds-lp5523.c10
-rw-r--r--drivers/leds/leds-lp5562.c11
-rw-r--r--drivers/leds/leds-lp55xx-common.c12
-rw-r--r--drivers/leds/leds-lp55xx-common.h6
-rw-r--r--drivers/leds/leds-lp8501.c11
-rw-r--r--drivers/leds/leds-lp8788.c48
-rw-r--r--drivers/leds/leds-lp8860.c27
-rw-r--r--drivers/leds/leds-lt3593.c33
-rw-r--r--drivers/leds/leds-max77693.c58
-rw-r--r--drivers/leds/leds-max8997.c1
-rw-r--r--drivers/leds/leds-mc13783.c35
-rw-r--r--drivers/leds/leds-ns2.c31
-rw-r--r--drivers/leds/leds-pca9532.c28
-rw-r--r--drivers/leds/leds-pca955x.c39
-rw-r--r--drivers/leds/leds-pca963x.c80
-rw-r--r--drivers/leds/leds-powernv.c18
-rw-r--r--drivers/leds/leds-pwm.c39
-rw-r--r--drivers/leds/leds-regulator.c46
-rw-r--r--drivers/leds/leds-sunfire.c23
-rw-r--r--drivers/leds/leds-syscon.c18
-rw-r--r--drivers/leds/leds-tlc591xx.c31
-rw-r--r--drivers/leds/leds-wm831x-status.c25
-rw-r--r--drivers/leds/leds-wm8350.c64
-rw-r--r--drivers/leds/leds.h27
-rw-r--r--drivers/leds/trigger/ledtrig-backlight.c8
-rw-r--r--drivers/leds/trigger/ledtrig-cpu.c26
-rw-r--r--drivers/leds/trigger/ledtrig-default-on.c2
-rw-r--r--drivers/leds/trigger/ledtrig-gpio.c6
-rw-r--r--drivers/leds/trigger/ledtrig-heartbeat.c4
-rw-r--r--drivers/leds/trigger/ledtrig-ide-disk.c14
-rw-r--r--drivers/leds/trigger/ledtrig-oneshot.c6
-rw-r--r--drivers/leds/trigger/ledtrig-transient.c10
-rw-r--r--drivers/lguest/core.c74
-rw-r--r--drivers/lightnvm/Kconfig1
-rw-r--r--drivers/lightnvm/core.c158
-rw-r--r--drivers/lightnvm/gennvm.c105
-rw-r--r--drivers/lightnvm/gennvm.h2
-rw-r--r--drivers/lightnvm/rrpc.c57
-rw-r--r--drivers/mailbox/mailbox-sti.c2
-rw-r--r--drivers/md/Kconfig21
-rw-r--r--drivers/md/Makefile5
-rw-r--r--drivers/md/bcache/util.c2
-rw-r--r--drivers/md/dm-bufio.c46
-rw-r--r--drivers/md/dm-cache-target.c3
-rw-r--r--drivers/md/dm-crypt.c22
-rw-r--r--drivers/md/dm-exception-store.h2
-rw-r--r--drivers/md/dm-io.c4
-rw-r--r--drivers/md/dm-mpath.c30
-rw-r--r--drivers/md/dm-snap-persistent.c5
-rw-r--r--drivers/md/dm-snap-transient.c4
-rw-r--r--drivers/md/dm-snap.c26
-rw-r--r--drivers/md/dm-thin-metadata.c148
-rw-r--r--drivers/md/dm-thin.c14
-rw-r--r--drivers/md/dm-verity-fec.c818
-rw-r--r--drivers/md/dm-verity-fec.h152
-rw-r--r--drivers/md/dm-verity-target.c (renamed from drivers/md/dm-verity.c)602
-rw-r--r--drivers/md/dm-verity.h129
-rw-r--r--drivers/md/dm.c7
-rw-r--r--drivers/md/md-cluster.c164
-rw-r--r--drivers/md/md-cluster.h2
-rw-r--r--drivers/md/md.c720
-rw-r--r--drivers/md/md.h59
-rw-r--r--drivers/md/multipath.c6
-rw-r--r--drivers/md/persistent-data/Kconfig9
-rw-r--r--drivers/md/persistent-data/dm-block-manager.c11
-rw-r--r--drivers/md/persistent-data/dm-btree.c118
-rw-r--r--drivers/md/persistent-data/dm-btree.h14
-rw-r--r--drivers/md/persistent-data/dm-space-map-metadata.c29
-rw-r--r--drivers/md/raid0.c4
-rw-r--r--drivers/md/raid1.c6
-rw-r--r--drivers/md/raid10.c10
-rw-r--r--drivers/md/raid5-cache.c158
-rw-r--r--drivers/md/raid5.c36
-rw-r--r--drivers/media/Kconfig5
-rw-r--r--drivers/media/common/cx2341x.c2
-rw-r--r--drivers/media/common/saa7146/saa7146_core.c2
-rw-r--r--drivers/media/common/saa7146/saa7146_fops.c2
-rw-r--r--drivers/media/common/saa7146/saa7146_hlp.c2
-rw-r--r--drivers/media/common/saa7146/saa7146_i2c.c2
-rw-r--r--drivers/media/common/saa7146/saa7146_vbi.c2
-rw-r--r--drivers/media/common/saa7146/saa7146_video.c4
-rw-r--r--drivers/media/common/siano/smsdvb-main.c7
-rw-r--r--drivers/media/common/siano/smsir.h2
-rw-r--r--drivers/media/dvb-core/demux.h67
-rw-r--r--drivers/media/dvb-core/dmxdev.c4
-rw-r--r--drivers/media/dvb-core/dvb-usb-ids.h1
-rw-r--r--drivers/media/dvb-core/dvb_ca_en50221.c2
-rw-r--r--drivers/media/dvb-core/dvb_frontend.c38
-rw-r--r--drivers/media/dvb-core/dvb_frontend.h221
-rw-r--r--drivers/media/dvb-core/dvb_net.c2
-rw-r--r--drivers/media/dvb-core/dvbdev.c488
-rw-r--r--drivers/media/dvb-core/dvbdev.h58
-rw-r--r--drivers/media/dvb-frontends/Kconfig2
-rw-r--r--drivers/media/dvb-frontends/au8522_common.c10
-rw-r--r--drivers/media/dvb-frontends/au8522_decoder.c31
-rw-r--r--drivers/media/dvb-frontends/au8522_dig.c16
-rw-r--r--drivers/media/dvb-frontends/au8522_priv.h14
-rw-r--r--drivers/media/dvb-frontends/bsbe1-d01a.h2
-rw-r--r--drivers/media/dvb-frontends/bsbe1.h2
-rw-r--r--drivers/media/dvb-frontends/bsru6.h2
-rw-r--r--drivers/media/dvb-frontends/isl6405.c2
-rw-r--r--drivers/media/dvb-frontends/isl6405.h2
-rw-r--r--drivers/media/dvb-frontends/isl6421.c2
-rw-r--r--drivers/media/dvb-frontends/isl6421.h2
-rw-r--r--drivers/media/dvb-frontends/lnbp21.c2
-rw-r--r--drivers/media/dvb-frontends/lnbp21.h2
-rw-r--r--drivers/media/dvb-frontends/lnbp22.c2
-rw-r--r--drivers/media/dvb-frontends/lnbp22.h2
-rw-r--r--drivers/media/dvb-frontends/m88ds3103.c2
-rw-r--r--drivers/media/dvb-frontends/rtl2832.c21
-rw-r--r--drivers/media/dvb-frontends/rtl2832_sdr.c4
-rw-r--r--drivers/media/dvb-frontends/si2165.c355
-rw-r--r--drivers/media/dvb-frontends/stb6100.c76
-rw-r--r--drivers/media/dvb-frontends/stb6100.h1
-rw-r--r--drivers/media/dvb-frontends/stb6100_cfg.h37
-rw-r--r--drivers/media/dvb-frontends/stb6100_proc.h43
-rw-r--r--drivers/media/dvb-frontends/tda665x.c183
-rw-r--r--drivers/media/dvb-frontends/tda8261.c125
-rw-r--r--drivers/media/dvb-frontends/tda8261_cfg.h37
-rw-r--r--drivers/media/dvb-frontends/tdhd1.h2
-rw-r--r--drivers/media/firewire/firedtv-ci.c2
-rw-r--r--drivers/media/i2c/Kconfig10
-rw-r--r--drivers/media/i2c/Makefile1
-rw-r--r--drivers/media/i2c/ad9389b.c4
-rw-r--r--drivers/media/i2c/adp1653.c6
-rw-r--r--drivers/media/i2c/adv7180.c6
-rw-r--r--drivers/media/i2c/adv7183.c2
-rw-r--r--drivers/media/i2c/adv7343.c2
-rw-r--r--drivers/media/i2c/adv7393.c2
-rw-r--r--drivers/media/i2c/adv7511.c6
-rw-r--r--drivers/media/i2c/adv7604.c12
-rw-r--r--drivers/media/i2c/adv7842.c10
-rw-r--r--drivers/media/i2c/ak881x.c2
-rw-r--r--drivers/media/i2c/as3645a.c6
-rw-r--r--drivers/media/i2c/bt819.c2
-rw-r--r--drivers/media/i2c/cs3308.c138
-rw-r--r--drivers/media/i2c/cx25840/cx25840-audio.c2
-rw-r--r--drivers/media/i2c/cx25840/cx25840-core.c123
-rw-r--r--drivers/media/i2c/cx25840/cx25840-core.h1
-rw-r--r--drivers/media/i2c/cx25840/cx25840-firmware.c2
-rw-r--r--drivers/media/i2c/cx25840/cx25840-ir.c2
-rw-r--r--drivers/media/i2c/cx25840/cx25840-vbi.c34
-rw-r--r--drivers/media/i2c/ir-kbd-i2c.c2
-rw-r--r--drivers/media/i2c/lm3560.c6
-rw-r--r--drivers/media/i2c/lm3646.c6
-rw-r--r--drivers/media/i2c/m52790.c2
-rw-r--r--drivers/media/i2c/m5mols/m5mols_capture.c4
-rw-r--r--drivers/media/i2c/m5mols/m5mols_core.c6
-rw-r--r--drivers/media/i2c/msp3400-driver.c4
-rw-r--r--drivers/media/i2c/msp3400-driver.h2
-rw-r--r--drivers/media/i2c/msp3400-kthreads.c2
-rw-r--r--drivers/media/i2c/mt9m032.c6
-rw-r--r--drivers/media/i2c/mt9p031.c6
-rw-r--r--drivers/media/i2c/mt9t001.c6
-rw-r--r--drivers/media/i2c/mt9v011.c4
-rw-r--r--drivers/media/i2c/mt9v032.c6
-rw-r--r--drivers/media/i2c/noon010pc30.c6
-rw-r--r--drivers/media/i2c/ov2659.c8
-rw-r--r--drivers/media/i2c/ov7670.c2
-rw-r--r--drivers/media/i2c/ov9650.c6
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-core.c18
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c2
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-spi.c1
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3.h2
-rw-r--r--drivers/media/i2c/s5k4ecgx.c6
-rw-r--r--drivers/media/i2c/s5k5baf.c12
-rw-r--r--drivers/media/i2c/s5k6a3.c2
-rw-r--r--drivers/media/i2c/s5k6aa.c6
-rw-r--r--drivers/media/i2c/saa6588.c2
-rw-r--r--drivers/media/i2c/saa7115.c2
-rw-r--r--drivers/media/i2c/saa7127.c2
-rw-r--r--drivers/media/i2c/smiapp/smiapp-core.c32
-rw-r--r--drivers/media/i2c/smiapp/smiapp.h2
-rw-r--r--drivers/media/i2c/soc_camera/mt9m001.c2
-rw-r--r--drivers/media/i2c/soc_camera/mt9t112.c2
-rw-r--r--drivers/media/i2c/soc_camera/mt9v022.c4
-rw-r--r--drivers/media/i2c/soc_camera/ov772x.c2
-rw-r--r--drivers/media/i2c/soc_camera/rj54n1cb0c.c2
-rw-r--r--drivers/media/i2c/soc_camera/tw9910.c2
-rw-r--r--drivers/media/i2c/sr030pc30.c2
-rw-r--r--drivers/media/i2c/tc358743.c8
-rw-r--r--drivers/media/i2c/ths7303.c2
-rw-r--r--drivers/media/i2c/tvaudio.c2
-rw-r--r--drivers/media/i2c/tvp514x.c6
-rw-r--r--drivers/media/i2c/tvp5150.c2
-rw-r--r--drivers/media/i2c/tvp7002.c6
-rw-r--r--drivers/media/i2c/uda1342.c2
-rw-r--r--drivers/media/i2c/upd64031a.c2
-rw-r--r--drivers/media/i2c/upd64083.c2
-rw-r--r--drivers/media/i2c/wm8775.c2
-rw-r--r--drivers/media/media-device.c441
-rw-r--r--drivers/media/media-devnode.c24
-rw-r--r--drivers/media/media-entity.c724
-rw-r--r--drivers/media/pci/bt8xx/bttv-cards.c4
-rw-r--r--drivers/media/pci/bt8xx/bttv-driver.c6
-rw-r--r--drivers/media/pci/bt8xx/bttvp.h4
-rw-r--r--drivers/media/pci/bt8xx/dst_ca.c3
-rw-r--r--drivers/media/pci/cobalt/cobalt-driver.c6
-rw-r--r--drivers/media/pci/cobalt/cobalt-irq.c4
-rw-r--r--drivers/media/pci/cobalt/cobalt-v4l2.c18
-rw-r--r--drivers/media/pci/cx18/cx18-cards.c2
-rw-r--r--drivers/media/pci/cx18/cx18-controls.c2
-rw-r--r--drivers/media/pci/cx18/cx18-controls.h2
-rw-r--r--drivers/media/pci/cx18/cx18-driver.h2
-rw-r--r--drivers/media/pci/cx18/cx18-ioctl.c4
-rw-r--r--drivers/media/pci/cx18/cx23418.h2
-rw-r--r--drivers/media/pci/cx23885/Kconfig1
-rw-r--r--drivers/media/pci/cx23885/cx23885-417.c4
-rw-r--r--drivers/media/pci/cx23885/cx23885-cards.c116
-rw-r--r--drivers/media/pci/cx23885/cx23885-core.c12
-rw-r--r--drivers/media/pci/cx23885/cx23885-dvb.c8
-rw-r--r--drivers/media/pci/cx23885/cx23885-i2c.c2
-rw-r--r--drivers/media/pci/cx23885/cx23885-input.c2
-rw-r--r--drivers/media/pci/cx23885/cx23885-vbi.c5
-rw-r--r--drivers/media/pci/cx23885/cx23885-video.c49
-rw-r--r--drivers/media/pci/cx23885/cx23885.h9
-rw-r--r--drivers/media/pci/cx25821/cx25821-video.c14
-rw-r--r--drivers/media/pci/cx88/cx88-alsa.c2
-rw-r--r--drivers/media/pci/cx88/cx88-blackbird.c4
-rw-r--r--drivers/media/pci/cx88/cx88-core.c2
-rw-r--r--drivers/media/pci/cx88/cx88-dvb.c2
-rw-r--r--drivers/media/pci/cx88/cx88-vbi.c2
-rw-r--r--drivers/media/pci/cx88/cx88-video.c4
-rw-r--r--drivers/media/pci/cx88/cx88.h6
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-core.c6
-rw-r--r--drivers/media/pci/dm1105/dm1105.c1
-rw-r--r--drivers/media/pci/dt3155/dt3155.c13
-rw-r--r--drivers/media/pci/ivtv/ivtv-cards.c12
-rw-r--r--drivers/media/pci/ivtv/ivtv-controls.c2
-rw-r--r--drivers/media/pci/ivtv/ivtv-controls.h2
-rw-r--r--drivers/media/pci/ivtv/ivtv-driver.c8
-rw-r--r--drivers/media/pci/ivtv/ivtv-driver.h4
-rw-r--r--drivers/media/pci/ivtv/ivtv-fileops.c2
-rw-r--r--drivers/media/pci/ivtv/ivtv-firmware.c2
-rw-r--r--drivers/media/pci/ivtv/ivtv-i2c.c2
-rw-r--r--drivers/media/pci/ivtv/ivtv-ioctl.c10
-rw-r--r--drivers/media/pci/ivtv/ivtv-routing.c8
-rw-r--r--drivers/media/pci/netup_unidvb/netup_unidvb_core.c5
-rw-r--r--drivers/media/pci/ngene/ngene-core.c2
-rw-r--r--drivers/media/pci/saa7134/saa7134-core.c2
-rw-r--r--drivers/media/pci/saa7134/saa7134-ts.c2
-rw-r--r--drivers/media/pci/saa7134/saa7134-vbi.c2
-rw-r--r--drivers/media/pci/saa7134/saa7134-video.c4
-rw-r--r--drivers/media/pci/saa7134/saa7134.h4
-rw-r--r--drivers/media/pci/saa7146/hexium_gemini.c2
-rw-r--r--drivers/media/pci/saa7146/hexium_orion.c2
-rw-r--r--drivers/media/pci/saa7146/mxb.c4
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c4
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-v4l2.c4
-rw-r--r--drivers/media/pci/sta2x11/sta2x11_vip.c4
-rw-r--r--drivers/media/pci/ttpci/av7110.c8
-rw-r--r--drivers/media/pci/ttpci/av7110.h2
-rw-r--r--drivers/media/pci/ttpci/av7110_av.c15
-rw-r--r--drivers/media/pci/ttpci/av7110_av.h3
-rw-r--r--drivers/media/pci/ttpci/av7110_ca.c4
-rw-r--r--drivers/media/pci/ttpci/av7110_hw.c2
-rw-r--r--drivers/media/pci/ttpci/av7110_v4l.c2
-rw-r--r--drivers/media/pci/ttpci/budget-av.c4
-rw-r--r--drivers/media/pci/ttpci/budget-ci.c2
-rw-r--r--drivers/media/pci/ttpci/budget-core.c2
-rw-r--r--drivers/media/pci/ttpci/budget-patch.c2
-rw-r--r--drivers/media/pci/ttpci/budget.c2
-rw-r--r--drivers/media/pci/ttpci/budget.h2
-rw-r--r--drivers/media/pci/tw68/tw68-video.c22
-rw-r--r--drivers/media/pci/zoran/zoran_card.c2
-rw-r--r--drivers/media/platform/Kconfig2
-rw-r--r--drivers/media/platform/am437x/am437x-vpfe.c19
-rw-r--r--drivers/media/platform/blackfin/bfin_capture.c14
-rw-r--r--drivers/media/platform/coda/coda-bit.c8
-rw-r--r--drivers/media/platform/coda/coda-common.c25
-rw-r--r--drivers/media/platform/coda/coda-jpeg.c26
-rw-r--r--drivers/media/platform/coda/coda.h4
-rw-r--r--drivers/media/platform/davinci/Kconfig6
-rw-r--r--drivers/media/platform/davinci/vpbe_display.c15
-rw-r--r--drivers/media/platform/davinci/vpif_capture.c19
-rw-r--r--drivers/media/platform/davinci/vpif_display.c19
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-m2m.c5
-rw-r--r--drivers/media/platform/exynos4-is/common.c5
-rw-r--r--drivers/media/platform/exynos4-is/fimc-capture.c44
-rw-r--r--drivers/media/platform/exynos4-is/fimc-core.h2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp-video.c46
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp.c4
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp.h2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite-reg.c2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite.c59
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite.h2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-m2m.c6
-rw-r--r--drivers/media/platform/exynos4-is/fimc-reg.c2
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.c75
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.h11
-rw-r--r--drivers/media/platform/exynos4-is/mipi-csis.c6
-rw-r--r--drivers/media/platform/m2m-deinterlace.c3
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.c17
-rw-r--r--drivers/media/platform/marvell-ccic/mmp-driver.c2
-rw-r--r--drivers/media/platform/mx2_emmaprp.c3
-rw-r--r--drivers/media/platform/omap/omap_vout_vrfb.c10
-rw-r--r--drivers/media/platform/omap3isp/isp.c306
-rw-r--r--drivers/media/platform/omap3isp/isp.h9
-rw-r--r--drivers/media/platform/omap3isp/ispccdc.c31
-rw-r--r--drivers/media/platform/omap3isp/ispccp2.c27
-rw-r--r--drivers/media/platform/omap3isp/ispcsi2.c21
-rw-r--r--drivers/media/platform/omap3isp/isppreview.c30
-rw-r--r--drivers/media/platform/omap3isp/ispresizer.c28
-rw-r--r--drivers/media/platform/omap3isp/ispstat.c2
-rw-r--r--drivers/media/platform/omap3isp/ispvideo.c52
-rw-r--r--drivers/media/platform/omap3isp/ispvideo.h5
-rw-r--r--drivers/media/platform/rcar_jpu.c40
-rw-r--r--drivers/media/platform/s3c-camif/camif-capture.c43
-rw-r--r--drivers/media/platform/s3c-camif/camif-core.c21
-rw-r--r--drivers/media/platform/s3c-camif/camif-core.h2
-rw-r--r--drivers/media/platform/s3c-camif/camif-regs.h2
-rw-r--r--drivers/media/platform/s5p-g2d/g2d.c4
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-core.c5
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc.c103
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_common.h14
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c16
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_dec.c39
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_dec.h2
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_enc.c47
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_enc.h2
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr.h507
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c94
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c121
-rw-r--r--drivers/media/platform/s5p-tv/hdmi_drv.c4
-rw-r--r--drivers/media/platform/s5p-tv/mixer_video.c2
-rw-r--r--drivers/media/platform/s5p-tv/sii9234_drv.c2
-rw-r--r--drivers/media/platform/sh_veu.c33
-rw-r--r--drivers/media/platform/sh_vou.c15
-rw-r--r--drivers/media/platform/soc_camera/atmel-isi.c168
-rw-r--r--drivers/media/platform/soc_camera/atmel-isi.h10
-rw-r--r--drivers/media/platform/soc_camera/mx2_camera.c12
-rw-r--r--drivers/media/platform/soc_camera/mx3_camera.c44
-rw-r--r--drivers/media/platform/soc_camera/omap1_camera.c4
-rw-r--r--drivers/media/platform/soc_camera/pxa_camera.c4
-rw-r--r--drivers/media/platform/soc_camera/rcar_vin.c119
-rw-r--r--drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c45
-rw-r--r--drivers/media/platform/soc_camera/sh_mobile_csi2.c6
-rw-r--r--drivers/media/platform/soc_camera/soc_camera.c25
-rw-r--r--drivers/media/platform/soc_camera/soc_camera_platform.c2
-rw-r--r--drivers/media/platform/soc_camera/soc_mediabus.c2
-rw-r--r--drivers/media/platform/sti/bdisp/bdisp-v4l2.c14
-rw-r--r--drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c16
-rw-r--r--drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c2
-rw-r--r--drivers/media/platform/ti-vpe/vpe.c3
-rw-r--r--drivers/media/platform/timblogiw.c2
-rw-r--r--drivers/media/platform/via-camera.c2
-rw-r--r--drivers/media/platform/vim2m.c15
-rw-r--r--drivers/media/platform/vivid/vivid-core.h3
-rw-r--r--drivers/media/platform/vivid/vivid-ctrls.c35
-rw-r--r--drivers/media/platform/vivid/vivid-kthread-cap.c6
-rw-r--r--drivers/media/platform/vivid/vivid-kthread-out.c8
-rw-r--r--drivers/media/platform/vivid/vivid-sdr-cap.c6
-rw-r--r--drivers/media/platform/vivid/vivid-vbi-cap.c8
-rw-r--r--drivers/media/platform/vivid/vivid-vbi-out.c2
-rw-r--r--drivers/media/platform/vivid/vivid-vid-cap.c34
-rw-r--r--drivers/media/platform/vivid/vivid-vid-out.c30
-rw-r--r--drivers/media/platform/vsp1/vsp1_drv.c50
-rw-r--r--drivers/media/platform/vsp1/vsp1_entity.c4
-rw-r--r--drivers/media/platform/vsp1/vsp1_rpf.c29
-rw-r--r--drivers/media/platform/vsp1/vsp1_rwpf.h5
-rw-r--r--drivers/media/platform/vsp1/vsp1_video.c117
-rw-r--r--drivers/media/platform/vsp1/vsp1_wpf.c40
-rw-r--r--drivers/media/platform/xilinx/xilinx-dma.c35
-rw-r--r--drivers/media/platform/xilinx/xilinx-tpg.c4
-rw-r--r--drivers/media/platform/xilinx/xilinx-vipp.c20
-rw-r--r--drivers/media/radio/radio-maxiradio.c4
-rw-r--r--drivers/media/radio/radio-sf16fmr2.c4
-rw-r--r--drivers/media/radio/radio-shark.c4
-rw-r--r--drivers/media/radio/radio-shark2.c2
-rw-r--r--drivers/media/radio/radio-si476x.c2
-rw-r--r--drivers/media/radio/radio-tea5777.h2
-rw-r--r--drivers/media/radio/radio-timb.c2
-rw-r--r--drivers/media/radio/si4713/radio-usb-si4713.c2
-rw-r--r--drivers/media/radio/si4713/si4713.h2
-rw-r--r--drivers/media/radio/tea575x.c2
-rw-r--r--drivers/media/rc/Kconfig3
-rw-r--r--drivers/media/rc/gpio-ir-recv.c24
-rw-r--r--drivers/media/rc/ir-jvc-decoder.c3
-rw-r--r--drivers/media/rc/ir-lirc-codec.c1
-rw-r--r--drivers/media/rc/ir-mce_kbd-decoder.c3
-rw-r--r--drivers/media/rc/ir-nec-decoder.c3
-rw-r--r--drivers/media/rc/ir-rc5-decoder.c3
-rw-r--r--drivers/media/rc/ir-rc6-decoder.c5
-rw-r--r--drivers/media/rc/ir-rx51.c2
-rw-r--r--drivers/media/rc/ir-sanyo-decoder.c3
-rw-r--r--drivers/media/rc/ir-sharp-decoder.c7
-rw-r--r--drivers/media/rc/ir-sony-decoder.c4
-rw-r--r--drivers/media/rc/ir-xmp-decoder.c3
-rw-r--r--drivers/media/rc/nuvoton-cir.c156
-rw-r--r--drivers/media/rc/nuvoton-cir.h28
-rw-r--r--drivers/media/rc/rc-core-priv.h71
-rw-r--r--drivers/media/rc/rc-ir-raw.c41
-rw-r--r--drivers/media/rc/rc-main.c88
-rw-r--r--drivers/media/rc/st_rc.c14
-rw-r--r--drivers/media/rc/streamzap.c19
-rw-r--r--drivers/media/rc/sunxi-cir.c2
-rw-r--r--drivers/media/tuners/max2165.c2
-rw-r--r--drivers/media/tuners/mt2063.c1
-rw-r--r--drivers/media/tuners/si2157.c1
-rw-r--r--drivers/media/usb/airspy/airspy.c6
-rw-r--r--drivers/media/usb/as102/as102_fw.c1
-rw-r--r--drivers/media/usb/au0828/au0828-cards.c4
-rw-r--r--drivers/media/usb/au0828/au0828-core.c171
-rw-r--r--drivers/media/usb/au0828/au0828-dvb.c12
-rw-r--r--drivers/media/usb/au0828/au0828-vbi.c14
-rw-r--r--drivers/media/usb/au0828/au0828-video.c140
-rw-r--r--drivers/media/usb/au0828/au0828.h10
-rw-r--r--drivers/media/usb/cpia2/cpia2_usb.c3
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-417.c26
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-cards.c91
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-core.c15
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-dvb.c14
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-vbi.c2
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-video.c21
-rw-r--r--drivers/media/usb/cx231xx/cx231xx.h4
-rw-r--r--drivers/media/usb/dvb-usb-v2/Kconfig2
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb_core.c42
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c4
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h4
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf.c26
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf.h5
-rw-r--r--drivers/media/usb/dvb-usb-v2/rtl28xxu.c16
-rw-r--r--drivers/media/usb/dvb-usb/Kconfig2
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-dvb.c43
-rw-r--r--drivers/media/usb/em28xx/em28xx-camera.c4
-rw-r--r--drivers/media/usb/em28xx/em28xx-cards.c18
-rw-r--r--drivers/media/usb/em28xx/em28xx-dvb.c15
-rw-r--r--drivers/media/usb/em28xx/em28xx-vbi.c20
-rw-r--r--drivers/media/usb/em28xx/em28xx-video.c23
-rw-r--r--drivers/media/usb/em28xx/em28xx.h2
-rw-r--r--drivers/media/usb/go7007/go7007-driver.c2
-rw-r--r--drivers/media/usb/go7007/go7007-usb.c6
-rw-r--r--drivers/media/usb/go7007/go7007-v4l2.c3
-rw-r--r--drivers/media/usb/gspca/ov534.c9
-rw-r--r--drivers/media/usb/gspca/topro.c6
-rw-r--r--drivers/media/usb/hackrf/hackrf.c19
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-video.c2
-rw-r--r--drivers/media/usb/hdpvr/hdpvr.h2
-rw-r--r--drivers/media/usb/msi2500/msi2500.c1
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-audio.c2
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c2
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h4
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c2
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-v4l2.c16
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c2
-rw-r--r--drivers/media/usb/pwc/pwc-if.c5
-rw-r--r--drivers/media/usb/s2255/s2255drv.c4
-rw-r--r--drivers/media/usb/siano/smsusb.c5
-rw-r--r--drivers/media/usb/stk1160/stk1160-core.c2
-rw-r--r--drivers/media/usb/stk1160/stk1160-v4l.c4
-rw-r--r--drivers/media/usb/stk1160/stk1160-video.c2
-rw-r--r--drivers/media/usb/tm6000/tm6000-cards.c2
-rw-r--r--drivers/media/usb/ttusb-dec/ttusb_dec.c2
-rw-r--r--drivers/media/usb/usbtv/usbtv-video.c11
-rw-r--r--drivers/media/usb/usbvision/usbvision-core.c2
-rw-r--r--drivers/media/usb/usbvision/usbvision-video.c25
-rw-r--r--drivers/media/usb/uvc/uvc_ctrl.c3
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c14
-rw-r--r--drivers/media/usb/uvc/uvc_entity.c38
-rw-r--r--drivers/media/usb/uvc/uvc_queue.c14
-rw-r--r--drivers/media/usb/uvc/uvc_v4l2.c20
-rw-r--r--drivers/media/usb/uvc/uvc_video.c17
-rw-r--r--drivers/media/v4l2-core/tuner-core.c10
-rw-r--r--drivers/media/v4l2-core/v4l2-clk.c9
-rw-r--r--drivers/media/v4l2-core/v4l2-compat-ioctl32.c6
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls.c77
-rw-r--r--drivers/media/v4l2-core/v4l2-dev.c111
-rw-r--r--drivers/media/v4l2-core/v4l2-device.c60
-rw-r--r--drivers/media/v4l2-core/v4l2-dv-timings.c16
-rw-r--r--drivers/media/v4l2-core/v4l2-flash-led-class.c12
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c14
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c8
-rw-r--r--drivers/media/v4l2-core/videobuf2-core.c926
-rw-r--r--drivers/media/v4l2-core/videobuf2-internal.h161
-rw-r--r--drivers/media/v4l2-core/videobuf2-v4l2.c703
-rw-r--r--drivers/memory/fsl_ifc.c1
-rw-r--r--drivers/mfd/88pm80x.c4
-rw-r--r--drivers/mfd/88pm860x-core.c13
-rw-r--r--drivers/mfd/Kconfig20
-rw-r--r--drivers/mfd/Makefile6
-rw-r--r--drivers/mfd/aat2870-core.c5
-rw-r--r--drivers/mfd/ab3100-core.c23
-rw-r--r--drivers/mfd/ab3100-otp.c5
-rw-r--r--drivers/mfd/ab8500-core.c506
-rw-r--r--drivers/mfd/ab8500-debugfs.c25
-rw-r--r--drivers/mfd/ab8500-gpadc.c145
-rw-r--r--drivers/mfd/ab8500-sysctrl.c13
-rw-r--r--drivers/mfd/adp5520.c6
-rw-r--r--drivers/mfd/arizona-core.c92
-rw-r--r--drivers/mfd/arizona-i2c.c2
-rw-r--r--drivers/mfd/arizona-irq.c95
-rw-r--r--drivers/mfd/arizona-spi.c7
-rw-r--r--drivers/mfd/arizona.h4
-rw-r--r--drivers/mfd/as3711.c28
-rw-r--r--drivers/mfd/as3722.c30
-rw-r--r--drivers/mfd/asic3.c30
-rw-r--r--drivers/mfd/cros_ec_i2c.c2
-rw-r--r--drivers/mfd/cros_ec_spi.c34
-rw-r--r--drivers/mfd/cs47l24-tables.c1629
-rw-r--r--drivers/mfd/cs5535-mfd.c6
-rw-r--r--drivers/mfd/da903x.c14
-rw-r--r--drivers/mfd/da9052-i2c.c2
-rw-r--r--drivers/mfd/da9052-irq.c2
-rw-r--r--drivers/mfd/davinci_voicecodec.c5
-rw-r--r--drivers/mfd/dm355evm_msp.c2
-rw-r--r--drivers/mfd/htc-egpio.c2
-rw-r--r--drivers/mfd/intel-lpss-acpi.c19
-rw-r--r--drivers/mfd/intel-lpss-pci.c44
-rw-r--r--drivers/mfd/intel-lpss.c16
-rw-r--r--drivers/mfd/intel-lpss.h2
-rw-r--r--drivers/mfd/lpc_ich.c15
-rw-r--r--drivers/mfd/max14577.c4
-rw-r--r--drivers/mfd/max77686.c4
-rw-r--r--drivers/mfd/max77693.c4
-rw-r--r--drivers/mfd/max77843.c4
-rw-r--r--drivers/mfd/max8925-i2c.c4
-rw-r--r--drivers/mfd/max8997.c8
-rw-r--r--drivers/mfd/max8998.c8
-rw-r--r--drivers/mfd/mc13xxx-core.c8
-rw-r--r--drivers/mfd/mfd-core.c7
-rw-r--r--drivers/mfd/qcom-spmi-pmic.c4
-rw-r--r--drivers/mfd/qcom_rpm.c2
-rw-r--r--drivers/mfd/sec-core.c43
-rw-r--r--drivers/mfd/sec-irq.c8
-rw-r--r--drivers/mfd/sta2x11-mfd.c36
-rw-r--r--drivers/mfd/syscon.c13
-rw-r--r--drivers/mfd/tc6393xb.c4
-rw-r--r--drivers/mfd/timberdale.c4
-rw-r--r--drivers/mfd/tps65010.c4
-rw-r--r--drivers/mfd/ucb1x00-core.c2
-rw-r--r--drivers/mfd/wm5110-tables.c6
-rw-r--r--drivers/mfd/wm831x-otp.c10
-rw-r--r--drivers/misc/cxl/native.c2
-rw-r--r--drivers/misc/mei/main.c6
-rw-r--r--drivers/mmc/card/block.c11
-rw-r--r--drivers/mmc/core/bus.c2
-rw-r--r--drivers/mmc/core/core.c85
-rw-r--r--drivers/mmc/core/core.h8
-rw-r--r--drivers/mmc/core/host.c11
-rw-r--r--drivers/mmc/core/mmc.c23
-rw-r--r--drivers/mmc/core/mmc_ops.c9
-rw-r--r--drivers/mmc/core/pwrseq.h2
-rw-r--r--drivers/mmc/core/pwrseq_emmc.c2
-rw-r--r--drivers/mmc/core/pwrseq_simple.c2
-rw-r--r--drivers/mmc/core/sd.c17
-rw-r--r--drivers/mmc/core/sdio.c2
-rw-r--r--drivers/mmc/core/sdio_bus.c1
-rw-r--r--drivers/mmc/host/Kconfig1
-rw-r--r--drivers/mmc/host/atmel-mci-regs.h171
-rw-r--r--drivers/mmc/host/atmel-mci.c165
-rw-r--r--drivers/mmc/host/cb710-mmc.h3
-rw-r--r--drivers/mmc/host/dw_mmc-pltfm.c2
-rw-r--r--drivers/mmc/host/dw_mmc-rockchip.c8
-rw-r--r--drivers/mmc/host/dw_mmc.c21
-rw-r--r--drivers/mmc/host/mtk-sd.c9
-rw-r--r--drivers/mmc/host/mvsdio.c58
-rw-r--r--drivers/mmc/host/of_mmc_spi.c4
-rw-r--r--drivers/mmc/host/omap_hsmmc.c6
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c7
-rw-r--r--drivers/mmc/host/sdhci-of-at91.c75
-rw-r--r--drivers/mmc/host/sdhci-of-esdhc.c10
-rw-r--r--drivers/mmc/host/sdhci-pci-core.c6
-rw-r--r--drivers/mmc/host/sdhci-pltfm.c3
-rw-r--r--drivers/mmc/host/sdhci-tegra.c178
-rw-r--r--drivers/mmc/host/sdhci.c124
-rw-r--r--drivers/mmc/host/sdhci.h21
-rw-r--r--drivers/mmc/host/sh_mmcif.c84
-rw-r--r--drivers/mmc/host/usdhi6rol0.c3
-rw-r--r--drivers/mtd/Kconfig2
-rw-r--r--drivers/mtd/afs.c82
-rw-r--r--drivers/mtd/ar7part.c18
-rw-r--r--drivers/mtd/bcm47xxpart.c18
-rw-r--r--drivers/mtd/bcm63xxpart.c18
-rw-r--r--drivers/mtd/chips/Kconfig4
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0001.c8
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0002.c6
-rw-r--r--drivers/mtd/cmdlinepart.c3
-rw-r--r--drivers/mtd/devices/m25p80.c44
-rw-r--r--drivers/mtd/devices/mtd_dataflash.c5
-rw-r--r--drivers/mtd/devices/spear_smi.c6
-rw-r--r--drivers/mtd/devices/st_spi_fsm.c5
-rw-r--r--drivers/mtd/ftl.c8
-rw-r--r--drivers/mtd/maps/lantiq-flash.c5
-rw-r--r--drivers/mtd/maps/pcmciamtd.c28
-rw-r--r--drivers/mtd/maps/physmap_of.c6
-rw-r--r--drivers/mtd/mtdcore.c68
-rw-r--r--drivers/mtd/mtdcore.h7
-rw-r--r--drivers/mtd/mtdpart.c133
-rw-r--r--drivers/mtd/nand/Kconfig11
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/ams-delta.c26
-rw-r--r--drivers/mtd/nand/atmel_nand.c134
-rw-r--r--drivers/mtd/nand/au1550nd.c40
-rw-r--r--drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h1
-rw-r--r--drivers/mtd/nand/bcm47xxnflash/main.c16
-rw-r--r--drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c34
-rw-r--r--drivers/mtd/nand/bf5xx_nand.c45
-rw-r--r--drivers/mtd/nand/brcmnand/Makefile1
-rw-r--r--drivers/mtd/nand/brcmnand/bcm6368_nand.c142
-rw-r--r--drivers/mtd/nand/brcmnand/brcmnand.c166
-rw-r--r--drivers/mtd/nand/cafe_nand.c43
-rw-r--r--drivers/mtd/nand/cmx270_nand.c23
-rw-r--r--drivers/mtd/nand/cs553x_nand.c34
-rw-r--r--drivers/mtd/nand/davinci_nand.c60
-rw-r--r--drivers/mtd/nand/denali.c75
-rw-r--r--drivers/mtd/nand/denali.h1
-rw-r--r--drivers/mtd/nand/diskonchip.c189
-rw-r--r--drivers/mtd/nand/docg4.c102
-rw-r--r--drivers/mtd/nand/fsl_elbc_nand.c64
-rw-r--r--drivers/mtd/nand/fsl_ifc_nand.c74
-rw-r--r--drivers/mtd/nand/fsl_upm.c35
-rw-r--r--drivers/mtd/nand/fsmc_nand.c69
-rw-r--r--drivers/mtd/nand/gpio.c26
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-lib.c2
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.c71
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.h1
-rw-r--r--drivers/mtd/nand/hisi504_nand.c53
-rw-r--r--drivers/mtd/nand/jz4740_nand.c37
-rw-r--r--drivers/mtd/nand/jz4780_bch.c381
-rw-r--r--drivers/mtd/nand/jz4780_bch.h43
-rw-r--r--drivers/mtd/nand/jz4780_nand.c428
-rw-r--r--drivers/mtd/nand/lpc32xx_mlc.c40
-rw-r--r--drivers/mtd/nand/lpc32xx_slc.c49
-rw-r--r--drivers/mtd/nand/mpc5121_nfc.c57
-rw-r--r--drivers/mtd/nand/mxc_nand.c88
-rw-r--r--drivers/mtd/nand/nand_base.c157
-rw-r--r--drivers/mtd/nand/nand_bbt.c32
-rw-r--r--drivers/mtd/nand/nand_bch.c6
-rw-r--r--drivers/mtd/nand/nand_ecc.c6
-rw-r--r--drivers/mtd/nand/nandsim.c41
-rw-r--r--drivers/mtd/nand/ndfc.c55
-rw-r--r--drivers/mtd/nand/nuc900_nand.c40
-rw-r--r--drivers/mtd/nand/omap2.c92
-rw-r--r--drivers/mtd/nand/omap_elm.c2
-rw-r--r--drivers/mtd/nand/orion_nand.c19
-rw-r--r--drivers/mtd/nand/pasemi_nand.c21
-rw-r--r--drivers/mtd/nand/plat_nand.c18
-rw-r--r--drivers/mtd/nand/pxa3xx_nand.c169
-rw-r--r--drivers/mtd/nand/r852.c46
-rw-r--r--drivers/mtd/nand/r852.h1
-rw-r--r--drivers/mtd/nand/s3c2410.c31
-rw-r--r--drivers/mtd/nand/sh_flctl.c20
-rw-r--r--drivers/mtd/nand/sharpsl.c23
-rw-r--r--drivers/mtd/nand/sm_common.c2
-rw-r--r--drivers/mtd/nand/socrates_nand.c30
-rw-r--r--drivers/mtd/nand/sunxi_nand.c50
-rw-r--r--drivers/mtd/nand/tmio_nand.c13
-rw-r--r--drivers/mtd/nand/txx9ndfmc.c25
-rw-r--r--drivers/mtd/nand/vf610_nfc.c23
-rw-r--r--drivers/mtd/nand/xway_nand.c4
-rw-r--r--drivers/mtd/ofpart.c65
-rw-r--r--drivers/mtd/onenand/omap2.c8
-rw-r--r--drivers/mtd/redboot.c19
-rw-r--r--drivers/mtd/sm_ftl.c3
-rw-r--r--drivers/mtd/spi-nor/Kconfig7
-rw-r--r--drivers/mtd/spi-nor/Makefile1
-rw-r--r--drivers/mtd/spi-nor/fsl-quadspi.c14
-rw-r--r--drivers/mtd/spi-nor/mtk-quadspi.c485
-rw-r--r--drivers/mtd/spi-nor/nxp-spifi.c6
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c87
-rw-r--r--drivers/mtd/tests/pagetest.c3
-rw-r--r--drivers/mtd/ubi/debug.c2
-rw-r--r--drivers/mtd/ubi/io.c2
-rw-r--r--drivers/mtd/ubi/wl.c74
-rw-r--r--drivers/net/bonding/bond_3ad.c11
-rw-r--r--drivers/net/bonding/bond_main.c123
-rw-r--r--drivers/net/bonding/bond_sysfs.c3
-rw-r--r--drivers/net/can/bfin_can.c2
-rw-r--r--drivers/net/can/c_can/c_can.c7
-rw-r--r--drivers/net/can/cc770/cc770.c2
-rw-r--r--drivers/net/can/flexcan.c4
-rw-r--r--drivers/net/can/janz-ican3.c1
-rw-r--r--drivers/net/can/m_can/m_can.c7
-rw-r--r--drivers/net/can/pch_can.c3
-rw-r--r--drivers/net/can/rcar_can.c11
-rw-r--r--drivers/net/can/sja1000/sja1000.c4
-rw-r--r--drivers/net/can/sun4i_can.c1
-rw-r--r--drivers/net/can/ti_hecc.c7
-rw-r--r--drivers/net/can/usb/ems_usb.c1
-rw-r--r--drivers/net/can/usb/esd_usb2.c1
-rw-r--r--drivers/net/can/usb/kvaser_usb.c5
-rw-r--r--drivers/net/can/usb/usb_8dev.c4
-rw-r--r--drivers/net/can/xilinx_can.c185
-rw-r--r--drivers/net/dsa/mv88e6xxx.c240
-rw-r--r--drivers/net/dsa/mv88e6xxx.h8
-rw-r--r--drivers/net/ethernet/3com/3c509.c2
-rw-r--r--drivers/net/ethernet/3com/3c59x.c2
-rw-r--r--drivers/net/ethernet/8390/ax88796.c17
-rw-r--r--drivers/net/ethernet/Kconfig2
-rw-r--r--drivers/net/ethernet/Makefile2
-rw-r--r--drivers/net/ethernet/adi/bfin_mac.c49
-rw-r--r--drivers/net/ethernet/aeroflex/greth.c5
-rw-r--r--drivers/net/ethernet/aeroflex/greth.h1
-rw-r--r--drivers/net/ethernet/agere/et131x.c49
-rw-r--r--drivers/net/ethernet/altera/altera_tse_main.c15
-rw-r--r--drivers/net/ethernet/amd/au1000_eth.c25
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-dev.c4
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-drv.c1
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_hw.c4
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_hw.h4
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_main.c102
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_main.h8
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c4
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h4
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c4
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h4
-rw-r--r--drivers/net/ethernet/arc/Kconfig4
-rw-r--r--drivers/net/ethernet/arc/emac_rockchip.c88
-rw-r--r--drivers/net/ethernet/atheros/alx/hw.c10
-rw-r--r--drivers/net/ethernet/atheros/alx/hw.h9
-rw-r--r--drivers/net/ethernet/atheros/alx/main.c10
-rw-r--r--drivers/net/ethernet/atheros/alx/reg.h1
-rw-r--r--drivers/net/ethernet/atheros/atl1c/atl1c_main.c7
-rw-r--r--drivers/net/ethernet/aurora/Kconfig21
-rw-r--r--drivers/net/ethernet/aurora/Makefile1
-rw-r--r--drivers/net/ethernet/aurora/nb8800.c1552
-rw-r--r--drivers/net/ethernet/aurora/nb8800.h316
-rw-r--r--drivers/net/ethernet/broadcom/b44.c21
-rw-r--r--drivers/net/ethernet/broadcom/bcm63xx_enet.c40
-rw-r--r--drivers/net/ethernet/broadcom/bcmsysport.c2
-rw-r--r--drivers/net/ethernet/broadcom/bgmac.c15
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x.h115
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c131
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h7
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c124
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h43
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c7
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.c818
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.h34
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c110
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h865
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c71
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.c4
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmmii.c14
-rw-r--r--drivers/net/ethernet/broadcom/sb1250-mac.c22
-rw-r--r--drivers/net/ethernet/broadcom/tg3.c46
-rw-r--r--drivers/net/ethernet/broadcom/tg3.h1
-rw-r--r--drivers/net/ethernet/cadence/macb.c71
-rw-r--r--drivers/net/ethernet/cadence/macb.h7
-rw-r--r--drivers/net/ethernet/cavium/thunder/nic.h11
-rw-r--r--drivers/net/ethernet/cavium/thunder/nic_main.c33
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c16
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_main.c19
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_queues.c213
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_queues.h14
-rw-r--r--drivers/net/ethernet/cavium/thunder/q_struct.h30
-rw-r--r--drivers/net/ethernet/cavium/thunder/thunder_bgx.c28
-rw-r--r--drivers/net/ethernet/cavium/thunder/thunder_bgx.h2
-rw-r--r--drivers/net/ethernet/chelsio/Kconfig17
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/cphy.h2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/mv88x201x.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/my3126.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/pm3393.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb/vsc7326.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/ael1002.c12
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/aq100x.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/common.h2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c14
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/t3_hw.c28
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/vsc8211.c4
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c15
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4.h19
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c144
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c148
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c232
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/l2t.c179
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/l2t.h10
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/sge.c187
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_hw.c162
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_hw.h1
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h3
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_regs.h31
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_values.h3
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/sge.c11
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h1
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c11
-rw-r--r--drivers/net/ethernet/cisco/enic/enic_main.c2
-rw-r--r--drivers/net/ethernet/dec/tulip/de4x5.c2
-rw-r--r--drivers/net/ethernet/dec/tulip/tulip_core.c9
-rw-r--r--drivers/net/ethernet/dec/tulip/winbond-840.c2
-rw-r--r--drivers/net/ethernet/dlink/dl2k.c314
-rw-r--r--drivers/net/ethernet/dnet.c28
-rw-r--r--drivers/net/ethernet/emulex/benet/be.h15
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.c756
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.h178
-rw-r--r--drivers/net/ethernet/emulex/benet/be_ethtool.c88
-rw-r--r--drivers/net/ethernet/emulex/benet/be_main.c600
-rw-r--r--drivers/net/ethernet/emulex/benet/be_roce.c36
-rw-r--r--drivers/net/ethernet/emulex/benet/be_roce.h4
-rw-r--r--drivers/net/ethernet/ethoc.c18
-rw-r--r--drivers/net/ethernet/ezchip/nps_enet.c30
-rw-r--r--drivers/net/ethernet/faraday/ftgmac100.c23
-rw-r--r--drivers/net/ethernet/freescale/Kconfig4
-rw-r--r--drivers/net/ethernet/freescale/Makefile2
-rw-r--r--drivers/net/ethernet/freescale/fec_main.c36
-rw-r--r--drivers/net/ethernet/freescale/fec_mpc52xx.c22
-rw-r--r--drivers/net/ethernet/freescale/fec_mpc52xx_phy.c4
-rw-r--r--drivers/net/ethernet/freescale/fman/Kconfig9
-rw-r--r--drivers/net/ethernet/freescale/fman/Makefile7
-rw-r--r--drivers/net/ethernet/freescale/fman/fman.c2871
-rw-r--r--drivers/net/ethernet/freescale/fman/fman.h325
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_dtsec.c1453
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_dtsec.h59
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_mac.h278
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_memac.c1170
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_memac.h60
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_muram.c158
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_muram.h51
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_port.c1778
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_port.h151
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_sp.c166
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_sp.h103
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_tgec.c786
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_tgec.h55
-rw-r--r--drivers/net/ethernet/freescale/fman/mac.c977
-rw-r--r--drivers/net/ethernet/freescale/fman/mac.h97
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c2
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mac-fcc.c2
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mac-fec.c4
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c10
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mii-fec.c10
-rw-r--r--drivers/net/ethernet/freescale/fsl_pq_mdio.c4
-rw-r--r--drivers/net/ethernet/freescale/gianfar.c27
-rw-r--r--drivers/net/ethernet/freescale/gianfar.h1
-rw-r--r--drivers/net/ethernet/freescale/gianfar_ptp.c2
-rw-r--r--drivers/net/ethernet/freescale/ucc_geth.c6
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hnae.h63
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c99
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c14
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h4
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c262
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h24
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c6
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c72
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h32
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c75
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h8
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h199
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_enet.c553
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_enet.h12
-rw-r--r--drivers/net/ethernet/hisilicon/hns/hns_ethtool.c201
-rw-r--r--drivers/net/ethernet/hisilicon/hns_mdio.c5
-rw-r--r--drivers/net/ethernet/hp/hp100.c2
-rw-r--r--drivers/net/ethernet/ibm/Kconfig10
-rw-r--r--drivers/net/ethernet/ibm/Makefile1
-rw-r--r--drivers/net/ethernet/ibm/ibmveth.c9
-rw-r--r--drivers/net/ethernet/ibm/ibmvnic.c3585
-rw-r--r--drivers/net/ethernet/ibm/ibmvnic.h1046
-rw-r--r--drivers/net/ethernet/intel/Kconfig10
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000.h7
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_hw.c216
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_main.c133
-rw-r--r--drivers/net/ethernet/intel/e1000e/defines.h3
-rw-r--r--drivers/net/ethernet/intel/e1000e/e1000.h2
-rw-r--r--drivers/net/ethernet/intel/e1000e/hw.h1
-rw-r--r--drivers/net/ethernet/intel/e1000e/ich8lan.c45
-rw-r--r--drivers/net/ethernet/intel/e1000e/netdev.c92
-rw-r--r--drivers/net/ethernet/intel/fm10k/Makefile20
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k.h23
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c4
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c4
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c30
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_main.c80
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_mbx.c57
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_mbx.h8
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_netdev.c79
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_pci.c211
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_pf.c151
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_pf.h20
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_tlv.c20
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_tlv.h11
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_type.h43
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_vf.c81
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_vf.h2
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e.h46
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_adminq.c6
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h23
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_common.c1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_debugfs.c28
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_devids.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_ethtool.c108
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_fcoe.c2
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c2
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_main.c1015
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_txrx.c110
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_txrx.h3
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_virtchnl.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c100
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_adminq.c6
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h23
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_common.c1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_devids.h1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_txrx.c226
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_txrx.h18
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h1
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf.h19
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c79
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf_main.c447
-rw-r--r--drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c86
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_82575.c21
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_defines.h6
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_hw.h1
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_i210.c32
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_i210.h3
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_phy.c229
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_phy.h16
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_regs.h4
-rw-r--r--drivers/net/ethernet/intel/igb/igb.h2
-rw-r--r--drivers/net/ethernet/intel/igb/igb_ethtool.c38
-rw-r--r--drivers/net/ethernet/intel/igb/igb_main.c26
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe.h50
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c8
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c13
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_common.c250
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_common.h2
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c7
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c6
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c70
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c19
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c1
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_main.c556
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c3
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c720
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c237
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_type.h17
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c16
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c220
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ethtool.c2
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf.h3
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c104
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/vf.c4
-rw-r--r--drivers/net/ethernet/jme.c2
-rw-r--r--drivers/net/ethernet/lantiq_etop.c30
-rw-r--r--drivers/net/ethernet/marvell/mv643xx_eth.c21
-rw-r--r--drivers/net/ethernet/marvell/mvmdio.c10
-rw-r--r--drivers/net/ethernet/marvell/mvneta.c462
-rw-r--r--drivers/net/ethernet/marvell/mvpp2.c52
-rw-r--r--drivers/net/ethernet/marvell/sky2.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/cmd.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_clock.c7
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_cq.c10
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_ethtool.c17
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_main.c7
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_netdev.c50
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_rx.c18
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/eq.c24
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/fw.c12
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/fw.h1
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/main.c107
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4.h8
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4_en.h126
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/port.c598
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/resource_tracker.c198
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/Kconfig1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/Makefile8
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en.h57
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_clock.c287
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c30
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c907
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_fs.c1224
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_main.c120
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_rx.c24
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_tx.c85
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c11
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eq.c13
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch.c1097
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch.h147
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/flow_table.c422
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c289
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h (renamed from include/linux/mlx5/flow_table.h)56
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_core.c1514
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_core.h169
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fw.c24
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/main.c119
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h9
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c38
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sriov.c233
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/vport.c435
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/Kconfig10
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/Makefile1
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core.c85
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core.h34
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c372
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/pci.c19
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/pci.h4
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/reg.h790
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.c1210
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum.h154
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c765
-rw-r--r--drivers/net/ethernet/microchip/encx24j600.c24
-rw-r--r--drivers/net/ethernet/myricom/myri10ge/myri10ge.c3
-rw-r--r--drivers/net/ethernet/natsemi/natsemi.c12
-rw-r--r--drivers/net/ethernet/netronome/Kconfig36
-rw-r--r--drivers/net/ethernet/netronome/Makefile5
-rw-r--r--drivers/net/ethernet/netronome/nfp/Makefile8
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net.h748
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_common.c2435
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h323
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c235
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c640
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c385
-rw-r--r--drivers/net/ethernet/nxp/lpc_eth.c25
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c2
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed.h3
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_dev.c53
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_hsi.h6
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_int.c33
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_int.h15
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_main.c74
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_mcp.c27
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_mcp.h13
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_reg_addr.h4
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_sp.h8
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_spq.c63
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede.h5
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_ethtool.c211
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_main.c7
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c5
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c6
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c4
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h2
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c4
-rw-r--r--drivers/net/ethernet/qlogic/qlge/qlge_main.c5
-rw-r--r--drivers/net/ethernet/qualcomm/qca_spi.c5
-rw-r--r--drivers/net/ethernet/rdc/r6040.c20
-rw-r--r--drivers/net/ethernet/realtek/r8169.c29
-rw-r--r--drivers/net/ethernet/renesas/ravb.h4
-rw-r--r--drivers/net/ethernet/renesas/ravb_main.c126
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.c450
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.h59
-rw-r--r--drivers/net/ethernet/rocker/rocker.c2
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c4
-rw-r--r--drivers/net/ethernet/sfc/ef10.c120
-rw-r--r--drivers/net/ethernet/sfc/efx.c18
-rw-r--r--drivers/net/ethernet/sfc/efx.h5
-rw-r--r--drivers/net/ethernet/sfc/farch.c2
-rw-r--r--drivers/net/ethernet/sfc/mcdi.c250
-rw-r--r--drivers/net/ethernet/sfc/mcdi.h10
-rw-r--r--drivers/net/ethernet/sfc/net_driver.h5
-rw-r--r--drivers/net/ethernet/sfc/rx.c1
-rw-r--r--drivers/net/ethernet/sfc/tx.c8
-rw-r--r--drivers/net/ethernet/sfc/txc43128_phy.c2
-rw-r--r--drivers/net/ethernet/smsc/smsc911x.c21
-rw-r--r--drivers/net/ethernet/smsc/smsc9420.c23
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/common.h28
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c24
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c9
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c13
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c6
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000.h42
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c75
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c30
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c9
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c37
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c74
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c2
-rw-r--r--drivers/net/ethernet/synopsys/dwc_eth_qos.c29
-rw-r--r--drivers/net/ethernet/ti/cpmac.c5
-rw-r--r--drivers/net/ethernet/ti/cpsw-common.c3
-rw-r--r--drivers/net/ethernet/ti/cpsw.c70
-rw-r--r--drivers/net/ethernet/ti/davinci_emac.c5
-rw-r--r--drivers/net/ethernet/ti/davinci_mdio.c4
-rw-r--r--drivers/net/ethernet/ti/netcp.h2
-rw-r--r--drivers/net/ethernet/ti/netcp_core.c134
-rw-r--r--drivers/net/ethernet/ti/netcp_ethss.c4
-rw-r--r--drivers/net/ethernet/tile/tilepro.c3
-rw-r--r--drivers/net/ethernet/toshiba/tc35815.c40
-rw-r--r--drivers/net/ethernet/xilinx/ll_temac.h1
-rw-r--r--drivers/net/ethernet/xilinx/ll_temac_mdio.c2
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_axienet.h2
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c2
-rw-r--r--drivers/net/ethernet/xilinx/xilinx_emaclite.c6
-rw-r--r--drivers/net/geneve.c165
-rw-r--r--drivers/net/hamradio/6pack.c12
-rw-r--r--drivers/net/hamradio/mkiss.c9
-rw-r--r--drivers/net/hyperv/hyperv_net.h62
-rw-r--r--drivers/net/hyperv/netvsc.c111
-rw-r--r--drivers/net/hyperv/netvsc_drv.c235
-rw-r--r--drivers/net/hyperv/rndis_filter.c66
-rw-r--r--drivers/net/ieee802154/Kconfig11
-rw-r--r--drivers/net/ieee802154/Makefile1
-rw-r--r--drivers/net/ieee802154/adf7242.c1285
-rw-r--r--drivers/net/ieee802154/atusb.c3
-rw-r--r--drivers/net/ieee802154/cc2520.c145
-rw-r--r--drivers/net/ipvlan/ipvlan_main.c2
-rw-r--r--drivers/net/irda/toim3232-sir.c10
-rw-r--r--drivers/net/loopback.c2
-rw-r--r--drivers/net/macvlan.c4
-rw-r--r--drivers/net/macvtap.c6
-rw-r--r--drivers/net/phy/Makefile2
-rw-r--r--drivers/net/phy/amd.c1
-rw-r--r--drivers/net/phy/aquantia.c4
-rw-r--r--drivers/net/phy/at803x.c15
-rw-r--r--drivers/net/phy/bcm-phy-lib.c8
-rw-r--r--drivers/net/phy/bcm63xx.c2
-rw-r--r--drivers/net/phy/bcm7xxx.c20
-rw-r--r--drivers/net/phy/bcm87xx.c11
-rw-r--r--drivers/net/phy/broadcom.c14
-rw-r--r--drivers/net/phy/cicada.c2
-rw-r--r--drivers/net/phy/davicom.c4
-rw-r--r--drivers/net/phy/dp83640.c25
-rw-r--r--drivers/net/phy/dp83848.c2
-rw-r--r--drivers/net/phy/dp83867.c17
-rw-r--r--drivers/net/phy/et1011c.c1
-rw-r--r--drivers/net/phy/fixed_phy.c14
-rw-r--r--drivers/net/phy/icplus.c21
-rw-r--r--drivers/net/phy/lxt.c4
-rw-r--r--drivers/net/phy/marvell.c153
-rw-r--r--drivers/net/phy/mdio-bcm-unimac.c11
-rw-r--r--drivers/net/phy/mdio-gpio.c2
-rw-r--r--drivers/net/phy/mdio-moxart.c7
-rw-r--r--drivers/net/phy/mdio-mux.c8
-rw-r--r--drivers/net/phy/mdio-octeon.c14
-rw-r--r--drivers/net/phy/mdio-sun4i.c12
-rw-r--r--drivers/net/phy/mdio_bus.c294
-rw-r--r--drivers/net/phy/mdio_device.c171
-rw-r--r--drivers/net/phy/micrel.c160
-rw-r--r--drivers/net/phy/microchip.c11
-rw-r--r--drivers/net/phy/national.c1
-rw-r--r--drivers/net/phy/phy.c63
-rw-r--r--drivers/net/phy/phy_device.c336
-rw-r--r--drivers/net/phy/qsemi.c1
-rw-r--r--drivers/net/phy/realtek.c5
-rw-r--r--drivers/net/phy/smsc.c32
-rw-r--r--drivers/net/phy/ste10Xp.c2
-rw-r--r--drivers/net/phy/teranetics.c1
-rw-r--r--drivers/net/phy/vitesse.c8
-rw-r--r--drivers/net/plip/plip.c36
-rw-r--r--drivers/net/ppp/ppp_generic.c9
-rw-r--r--drivers/net/ppp/pppoe.c42
-rw-r--r--drivers/net/ppp/pppox.c5
-rw-r--r--drivers/net/ppp/pptp.c7
-rw-r--r--drivers/net/team/team.c69
-rw-r--r--drivers/net/team/team_mode_activebackup.c1
-rw-r--r--drivers/net/team/team_mode_broadcast.c1
-rw-r--r--drivers/net/team/team_mode_loadbalance.c1
-rw-r--r--drivers/net/team/team_mode_random.c1
-rw-r--r--drivers/net/team/team_mode_roundrobin.c1
-rw-r--r--drivers/net/tun.c7
-rw-r--r--drivers/net/usb/asix_common.c2
-rw-r--r--drivers/net/usb/ax88172a.c14
-rw-r--r--drivers/net/usb/cdc_ether.c8
-rw-r--r--drivers/net/usb/cdc_mbim.c28
-rw-r--r--drivers/net/usb/cdc_ncm.c110
-rw-r--r--drivers/net/usb/lan78xx.c66
-rw-r--r--drivers/net/usb/qmi_wwan.c154
-rw-r--r--drivers/net/usb/r8152.c165
-rw-r--r--drivers/net/usb/usbnet.c5
-rw-r--r--drivers/net/veth.c6
-rw-r--r--drivers/net/virtio_net.c37
-rw-r--r--drivers/net/vmxnet3/vmxnet3_drv.c71
-rw-r--r--drivers/net/vmxnet3/vmxnet3_int.h4
-rw-r--r--drivers/net/vrf.c91
-rw-r--r--drivers/net/vxlan.c98
-rw-r--r--drivers/net/wan/hdlc.c21
-rw-r--r--drivers/net/wan/hdlc_cisco.c1
-rw-r--r--drivers/net/wan/hdlc_fr.c11
-rw-r--r--drivers/net/wan/hdlc_ppp.c1
-rw-r--r--drivers/net/wan/hdlc_raw.c1
-rw-r--r--drivers/net/wan/hdlc_raw_eth.c1
-rw-r--r--drivers/net/wan/hdlc_x25.c1
-rw-r--r--drivers/net/wan/wanxl.c1
-rw-r--r--drivers/net/wan/x25_asy.c6
-rw-r--r--drivers/net/wireless/Kconfig238
-rw-r--r--drivers/net/wireless/Makefile65
-rw-r--r--drivers/net/wireless/admtek/Kconfig41
-rw-r--r--drivers/net/wireless/admtek/Makefile1
-rw-r--r--drivers/net/wireless/admtek/adm8211.c (renamed from drivers/net/wireless/adm8211.c)0
-rw-r--r--drivers/net/wireless/admtek/adm8211.h (renamed from drivers/net/wireless/adm8211.h)0
-rw-r--r--drivers/net/wireless/ath/Kconfig17
-rw-r--r--drivers/net/wireless/ath/ath10k/Kconfig1
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c65
-rw-r--r--drivers/net/wireless/ath/ath10k/core.h39
-rw-r--r--drivers/net/wireless/ath/ath10k/debug.c199
-rw-r--r--drivers/net/wireless/ath/ath10k/debug.h4
-rw-r--r--drivers/net/wireless/ath/ath10k/htt.h15
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_rx.c33
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_tx.c130
-rw-r--r--drivers/net/wireless/ath/ath10k/hw.h32
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c212
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.h3
-rw-r--r--drivers/net/wireless/ath/ath10k/pci.c102
-rw-r--r--drivers/net/wireless/ath/ath10k/thermal.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/txrx.c15
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.c19
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.h18
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.c208
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.h130
-rw-r--r--drivers/net/wireless/ath/ath5k/base.c2
-rw-r--r--drivers/net/wireless/ath/ath6kl/cfg80211.c4
-rw-r--r--drivers/net/wireless/ath/ath6kl/htc_mbox.c5
-rw-r--r--drivers/net/wireless/ath/ath6kl/init.c16
-rw-r--r--drivers/net/wireless/ath/ath9k/Kconfig11
-rw-r--r--drivers/net/wireless/ath/ath9k/Makefile1
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h23
-rw-r--r--drivers/net/wireless/ath/ath9k/beacon.c3
-rw-r--r--drivers/net/wireless/ath/ath9k/channel.c56
-rw-r--r--drivers/net/wireless/ath/ath9k/common-beacon.c22
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom.c74
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom.h3
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom_4k.c76
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom_9287.c68
-rw-r--r--drivers/net/wireless/ath/ath9k/eeprom_def.c61
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_main.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_hst.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.c11
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c1
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/recv.c3
-rw-r--r--drivers/net/wireless/ath/ath9k/rng.c107
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c23
-rw-r--r--drivers/net/wireless/ath/wcn36xx/dxe.c43
-rw-r--r--drivers/net/wireless/ath/wcn36xx/hal.h2
-rw-r--r--drivers/net/wireless/ath/wcn36xx/smd.c27
-rw-r--r--drivers/net/wireless/ath/wcn36xx/smd.h9
-rw-r--r--drivers/net/wireless/ath/wil6210/debugfs.c12
-rw-r--r--drivers/net/wireless/ath/wil6210/interrupt.c8
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c24
-rw-r--r--drivers/net/wireless/ath/wil6210/netdev.c2
-rw-r--r--drivers/net/wireless/ath/wil6210/pcie_bus.c30
-rw-r--r--drivers/net/wireless/ath/wil6210/rx_reorder.c12
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.c5
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h1
-rw-r--r--drivers/net/wireless/ath/wil6210/wil_crash_dump.c3
-rw-r--r--drivers/net/wireless/ath/wil6210/wil_platform.c3
-rw-r--r--drivers/net/wireless/ath/wil6210/wil_platform.h38
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c4
-rw-r--r--drivers/net/wireless/atmel/Kconfig57
-rw-r--r--drivers/net/wireless/atmel/Makefile5
-rw-r--r--drivers/net/wireless/atmel/at76c50x-usb.c (renamed from drivers/net/wireless/at76c50x-usb.c)0
-rw-r--r--drivers/net/wireless/atmel/at76c50x-usb.h (renamed from drivers/net/wireless/at76c50x-usb.h)0
-rw-r--r--drivers/net/wireless/atmel/atmel.c (renamed from drivers/net/wireless/atmel.c)0
-rw-r--r--drivers/net/wireless/atmel/atmel.h (renamed from drivers/net/wireless/atmel.h)0
-rw-r--r--drivers/net/wireless/atmel/atmel_cs.c (renamed from drivers/net/wireless/atmel_cs.c)0
-rw-r--r--drivers/net/wireless/atmel/atmel_pci.c (renamed from drivers/net/wireless/atmel_pci.c)0
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/common.h23
-rw-r--r--drivers/net/wireless/broadcom/Kconfig18
-rw-r--r--drivers/net/wireless/broadcom/Makefile5
-rw-r--r--drivers/net/wireless/broadcom/b43/Kconfig (renamed from drivers/net/wireless/b43/Kconfig)0
-rw-r--r--drivers/net/wireless/broadcom/b43/Makefile (renamed from drivers/net/wireless/b43/Makefile)0
-rw-r--r--drivers/net/wireless/broadcom/b43/b43.h (renamed from drivers/net/wireless/b43/b43.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/bus.c (renamed from drivers/net/wireless/b43/bus.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/bus.h (renamed from drivers/net/wireless/b43/bus.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/debugfs.c (renamed from drivers/net/wireless/b43/debugfs.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/debugfs.h (renamed from drivers/net/wireless/b43/debugfs.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/dma.c (renamed from drivers/net/wireless/b43/dma.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/dma.h (renamed from drivers/net/wireless/b43/dma.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/leds.c (renamed from drivers/net/wireless/b43/leds.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/leds.h (renamed from drivers/net/wireless/b43/leds.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/lo.c (renamed from drivers/net/wireless/b43/lo.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/lo.h (renamed from drivers/net/wireless/b43/lo.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/main.c (renamed from drivers/net/wireless/b43/main.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/main.h (renamed from drivers/net/wireless/b43/main.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_a.c (renamed from drivers/net/wireless/b43/phy_a.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_a.h (renamed from drivers/net/wireless/b43/phy_a.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_ac.c (renamed from drivers/net/wireless/b43/phy_ac.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_ac.h (renamed from drivers/net/wireless/b43/phy_ac.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_common.c (renamed from drivers/net/wireless/b43/phy_common.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_common.h (renamed from drivers/net/wireless/b43/phy_common.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_g.c (renamed from drivers/net/wireless/b43/phy_g.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_g.h (renamed from drivers/net/wireless/b43/phy_g.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_ht.c (renamed from drivers/net/wireless/b43/phy_ht.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_ht.h (renamed from drivers/net/wireless/b43/phy_ht.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_lcn.c (renamed from drivers/net/wireless/b43/phy_lcn.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_lcn.h (renamed from drivers/net/wireless/b43/phy_lcn.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_lp.c (renamed from drivers/net/wireless/b43/phy_lp.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_lp.h (renamed from drivers/net/wireless/b43/phy_lp.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_n.c (renamed from drivers/net/wireless/b43/phy_n.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/phy_n.h (renamed from drivers/net/wireless/b43/phy_n.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/pio.c (renamed from drivers/net/wireless/b43/pio.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/pio.h (renamed from drivers/net/wireless/b43/pio.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/ppr.c (renamed from drivers/net/wireless/b43/ppr.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/ppr.h (renamed from drivers/net/wireless/b43/ppr.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/radio_2055.c (renamed from drivers/net/wireless/b43/radio_2055.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/radio_2055.h (renamed from drivers/net/wireless/b43/radio_2055.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/radio_2056.c (renamed from drivers/net/wireless/b43/radio_2056.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/radio_2056.h (renamed from drivers/net/wireless/b43/radio_2056.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/radio_2057.c (renamed from drivers/net/wireless/b43/radio_2057.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/radio_2057.h (renamed from drivers/net/wireless/b43/radio_2057.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/radio_2059.c (renamed from drivers/net/wireless/b43/radio_2059.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/radio_2059.h (renamed from drivers/net/wireless/b43/radio_2059.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/rfkill.c (renamed from drivers/net/wireless/b43/rfkill.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/rfkill.h (renamed from drivers/net/wireless/b43/rfkill.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/sdio.c (renamed from drivers/net/wireless/b43/sdio.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/sdio.h (renamed from drivers/net/wireless/b43/sdio.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/sysfs.c (renamed from drivers/net/wireless/b43/sysfs.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/sysfs.h (renamed from drivers/net/wireless/b43/sysfs.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/tables.c (renamed from drivers/net/wireless/b43/tables.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/tables.h (renamed from drivers/net/wireless/b43/tables.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/tables_lpphy.c (renamed from drivers/net/wireless/b43/tables_lpphy.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/tables_lpphy.h (renamed from drivers/net/wireless/b43/tables_lpphy.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/tables_nphy.c (renamed from drivers/net/wireless/b43/tables_nphy.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/tables_nphy.h (renamed from drivers/net/wireless/b43/tables_nphy.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/tables_phy_ht.c (renamed from drivers/net/wireless/b43/tables_phy_ht.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/tables_phy_ht.h (renamed from drivers/net/wireless/b43/tables_phy_ht.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/tables_phy_lcn.c (renamed from drivers/net/wireless/b43/tables_phy_lcn.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/tables_phy_lcn.h (renamed from drivers/net/wireless/b43/tables_phy_lcn.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/wa.c (renamed from drivers/net/wireless/b43/wa.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/wa.h (renamed from drivers/net/wireless/b43/wa.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43/xmit.c (renamed from drivers/net/wireless/b43/xmit.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43/xmit.h (renamed from drivers/net/wireless/b43/xmit.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/Kconfig (renamed from drivers/net/wireless/b43legacy/Kconfig)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/Makefile (renamed from drivers/net/wireless/b43legacy/Makefile)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/b43legacy.h (renamed from drivers/net/wireless/b43legacy/b43legacy.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/debugfs.c (renamed from drivers/net/wireless/b43legacy/debugfs.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/debugfs.h (renamed from drivers/net/wireless/b43legacy/debugfs.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/dma.c (renamed from drivers/net/wireless/b43legacy/dma.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/dma.h (renamed from drivers/net/wireless/b43legacy/dma.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/ilt.c (renamed from drivers/net/wireless/b43legacy/ilt.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/ilt.h (renamed from drivers/net/wireless/b43legacy/ilt.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/leds.c (renamed from drivers/net/wireless/b43legacy/leds.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/leds.h (renamed from drivers/net/wireless/b43legacy/leds.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/main.c (renamed from drivers/net/wireless/b43legacy/main.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/main.h (renamed from drivers/net/wireless/b43legacy/main.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/phy.c (renamed from drivers/net/wireless/b43legacy/phy.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/phy.h (renamed from drivers/net/wireless/b43legacy/phy.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/pio.c (renamed from drivers/net/wireless/b43legacy/pio.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/pio.h (renamed from drivers/net/wireless/b43legacy/pio.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/radio.c (renamed from drivers/net/wireless/b43legacy/radio.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/radio.h (renamed from drivers/net/wireless/b43legacy/radio.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/rfkill.c (renamed from drivers/net/wireless/b43legacy/rfkill.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/rfkill.h (renamed from drivers/net/wireless/b43legacy/rfkill.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/sysfs.c (renamed from drivers/net/wireless/b43legacy/sysfs.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/sysfs.h (renamed from drivers/net/wireless/b43legacy/sysfs.h)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/xmit.c (renamed from drivers/net/wireless/b43legacy/xmit.c)0
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/xmit.h (renamed from drivers/net/wireless/b43legacy/xmit.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/Kconfig (renamed from drivers/net/wireless/brcm80211/Kconfig)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/Makefile (renamed from drivers/net/wireless/brcm80211/Makefile)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile (renamed from drivers/net/wireless/brcm80211/brcmfmac/Makefile)4
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/bcdc.c)10
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/bcdc.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c)12
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/btcoex.c)12
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/btcoex.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/bus.h)2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c)1145
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h)150
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/chip.c)1
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/chip.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/common.c)96
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h79
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/commonring.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/commonring.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/core.c)250
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/core.h)15
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/debug.c)2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/debug.h)2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/feature.c)80
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/feature.h)9
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/firmware.c)51
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/firmware.h)46
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/flowring.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/flowring.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/fweh.c)8
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/fweh.h)2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/fwil.c)31
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/fwil.h)1
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h)167
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c)20
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c)7
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/of.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/of.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/p2p.c)57
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/p2p.h)2
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/pcie.c)252
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/pcie.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/proto.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/proto.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/sdio.c)255
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/sdio.h)10
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/usb.c)98
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/usb.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c (renamed from drivers/net/wireless/brcm80211/brcmfmac/vendor.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h (renamed from drivers/net/wireless/brcm80211/brcmfmac/vendor.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile (renamed from drivers/net/wireless/brcm80211/brcmsmac/Makefile)6
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/aiutils.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/aiutils.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/ampdu.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/ampdu.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/antsel.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/antsel.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_events.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_events.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/channel.c)4
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/channel.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/d11.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/d11.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/debug.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/debug.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/dma.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/dma.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/led.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/led.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/main.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/main.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_hal.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_int.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_radio.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phy_radio.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phyreg_n.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phyreg_n.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_n.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_n.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy_shim.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/pmu.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/pmu.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/pub.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/pub.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/rate.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/rate.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/rate.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/rate.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/scb.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/scb.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/stf.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/stf.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/stf.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/stf.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/types.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/types.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/ucode_loader.c (renamed from drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmsmac/ucode_loader.h (renamed from drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmutil/Makefile (renamed from drivers/net/wireless/brcm80211/brcmutil/Makefile)4
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c (renamed from drivers/net/wireless/brcm80211/brcmutil/d11.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmutil/utils.c (renamed from drivers/net/wireless/brcm80211/brcmutil/utils.c)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h (renamed from drivers/net/wireless/brcm80211/include/brcm_hw_ids.h)4
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h (renamed from drivers/net/wireless/brcm80211/include/brcmu_d11.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h (renamed from drivers/net/wireless/brcm80211/include/brcmu_utils.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h (renamed from drivers/net/wireless/brcm80211/include/brcmu_wifi.h)23
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h (renamed from drivers/net/wireless/brcm80211/include/chipcommon.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/include/defs.h (renamed from drivers/net/wireless/brcm80211/include/defs.h)0
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/include/soc.h (renamed from drivers/net/wireless/brcm80211/include/soc.h)0
-rw-r--r--drivers/net/wireless/cisco/Kconfig56
-rw-r--r--drivers/net/wireless/cisco/Makefile2
-rw-r--r--drivers/net/wireless/cisco/airo.c (renamed from drivers/net/wireless/airo.c)18
-rw-r--r--drivers/net/wireless/cisco/airo.h (renamed from drivers/net/wireless/airo.h)0
-rw-r--r--drivers/net/wireless/cisco/airo_cs.c (renamed from drivers/net/wireless/airo_cs.c)0
-rw-r--r--drivers/net/wireless/intel/Kconfig18
-rw-r--r--drivers/net/wireless/intel/Makefile6
-rw-r--r--drivers/net/wireless/intel/ipw2x00/Kconfig (renamed from drivers/net/wireless/ipw2x00/Kconfig)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/Makefile (renamed from drivers/net/wireless/ipw2x00/Makefile)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw.h (renamed from drivers/net/wireless/ipw2x00/ipw.h)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw2100.c (renamed from drivers/net/wireless/ipw2x00/ipw2100.c)11
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw2100.h (renamed from drivers/net/wireless/ipw2x00/ipw2100.h)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw2200.c (renamed from drivers/net/wireless/ipw2x00/ipw2200.c)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw2200.h (renamed from drivers/net/wireless/ipw2x00/ipw2200.h)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw.h (renamed from drivers/net/wireless/ipw2x00/libipw.h)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_geo.c (renamed from drivers/net/wireless/ipw2x00/libipw_geo.c)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_module.c (renamed from drivers/net/wireless/ipw2x00/libipw_module.c)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_rx.c (renamed from drivers/net/wireless/ipw2x00/libipw_rx.c)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_tx.c (renamed from drivers/net/wireless/ipw2x00/libipw_tx.c)0
-rw-r--r--drivers/net/wireless/intel/ipw2x00/libipw_wx.c (renamed from drivers/net/wireless/ipw2x00/libipw_wx.c)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945-debug.c (renamed from drivers/net/wireless/iwlegacy/3945-debug.c)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945-mac.c (renamed from drivers/net/wireless/iwlegacy/3945-mac.c)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945-rs.c (renamed from drivers/net/wireless/iwlegacy/3945-rs.c)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945.c (renamed from drivers/net/wireless/iwlegacy/3945.c)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945.h (renamed from drivers/net/wireless/iwlegacy/3945.h)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965-calib.c (renamed from drivers/net/wireless/iwlegacy/4965-calib.c)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965-debug.c (renamed from drivers/net/wireless/iwlegacy/4965-debug.c)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965-mac.c (renamed from drivers/net/wireless/iwlegacy/4965-mac.c)2
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965-rs.c (renamed from drivers/net/wireless/iwlegacy/4965-rs.c)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965.c (renamed from drivers/net/wireless/iwlegacy/4965.c)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/4965.h (renamed from drivers/net/wireless/iwlegacy/4965.h)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/Kconfig (renamed from drivers/net/wireless/iwlegacy/Kconfig)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/Makefile (renamed from drivers/net/wireless/iwlegacy/Makefile)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/commands.h (renamed from drivers/net/wireless/iwlegacy/commands.h)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/common.c (renamed from drivers/net/wireless/iwlegacy/common.c)14
-rw-r--r--drivers/net/wireless/intel/iwlegacy/common.h (renamed from drivers/net/wireless/iwlegacy/common.h)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/csr.h (renamed from drivers/net/wireless/iwlegacy/csr.h)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/debug.c (renamed from drivers/net/wireless/iwlegacy/debug.c)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/iwl-spectrum.h (renamed from drivers/net/wireless/iwlegacy/iwl-spectrum.h)0
-rw-r--r--drivers/net/wireless/intel/iwlegacy/prph.h (renamed from drivers/net/wireless/iwlegacy/prph.h)0
-rw-r--r--drivers/net/wireless/intel/iwlwifi/Kconfig (renamed from drivers/net/wireless/iwlwifi/Kconfig)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/Makefile (renamed from drivers/net/wireless/iwlwifi/Makefile)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/Makefile (renamed from drivers/net/wireless/iwlwifi/dvm/Makefile)0
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/agn.h (renamed from drivers/net/wireless/iwlwifi/dvm/agn.h)11
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/calib.c (renamed from drivers/net/wireless/iwlwifi/dvm/calib.c)4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/calib.h (renamed from drivers/net/wireless/iwlwifi/dvm/calib.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/commands.h (renamed from drivers/net/wireless/iwlwifi/dvm/commands.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c (renamed from drivers/net/wireless/iwlwifi/dvm/debugfs.c)6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/dev.h (renamed from drivers/net/wireless/iwlwifi/dvm/dev.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/devices.c (renamed from drivers/net/wireless/iwlwifi/dvm/devices.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/led.c (renamed from drivers/net/wireless/iwlwifi/dvm/led.c)4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/led.h (renamed from drivers/net/wireless/iwlwifi/dvm/led.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/lib.c (renamed from drivers/net/wireless/iwlwifi/dvm/lib.c)7
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c (renamed from drivers/net/wireless/iwlwifi/dvm/mac80211.c)13
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/main.c (renamed from drivers/net/wireless/iwlwifi/dvm/main.c)116
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/power.c (renamed from drivers/net/wireless/iwlwifi/dvm/power.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/power.h (renamed from drivers/net/wireless/iwlwifi/dvm/power.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/rs.c (renamed from drivers/net/wireless/iwlwifi/dvm/rs.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/rs.h (renamed from drivers/net/wireless/iwlwifi/dvm/rs.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/rx.c (renamed from drivers/net/wireless/iwlwifi/dvm/rx.c)89
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/rxon.c (renamed from drivers/net/wireless/iwlwifi/dvm/rxon.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/scan.c (renamed from drivers/net/wireless/iwlwifi/dvm/scan.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/sta.c (renamed from drivers/net/wireless/iwlwifi/dvm/sta.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/tt.c (renamed from drivers/net/wireless/iwlwifi/dvm/tt.c)4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/tt.h (renamed from drivers/net/wireless/iwlwifi/dvm/tt.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/tx.c (renamed from drivers/net/wireless/iwlwifi/dvm/tx.c)3
-rw-r--r--drivers/net/wireless/intel/iwlwifi/dvm/ucode.c (renamed from drivers/net/wireless/iwlwifi/dvm/ucode.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-1000.c (renamed from drivers/net/wireless/iwlwifi/iwl-1000.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-2000.c (renamed from drivers/net/wireless/iwlwifi/iwl-2000.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-5000.c (renamed from drivers/net/wireless/iwlwifi/iwl-5000.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-6000.c (renamed from drivers/net/wireless/iwlwifi/iwl-6000.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-7000.c (renamed from drivers/net/wireless/iwlwifi/iwl-7000.c)60
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-8000.c (renamed from drivers/net/wireless/iwlwifi/iwl-8000.c)15
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-9000.c163
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h (renamed from drivers/net/wireless/iwlwifi/iwl-agn-hw.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-config.h (renamed from drivers/net/wireless/iwlwifi/iwl-config.h)9
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-csr.h (renamed from drivers/net/wireless/iwlwifi/iwl-csr.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-debug.c (renamed from drivers/net/wireless/iwlwifi/iwl-debug.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-debug.h (renamed from drivers/net/wireless/iwlwifi/iwl-debug.h)4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h (renamed from drivers/net/wireless/iwlwifi/iwl-devtrace-data.h)19
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h (renamed from drivers/net/wireless/iwlwifi/iwl-devtrace-io.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h (renamed from drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h (renamed from drivers/net/wireless/iwlwifi/iwl-devtrace-msg.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace-ucode.h (renamed from drivers/net/wireless/iwlwifi/iwl-devtrace-ucode.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c (renamed from drivers/net/wireless/iwlwifi/iwl-devtrace.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h (renamed from drivers/net/wireless/iwlwifi/iwl-devtrace.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-drv.c (renamed from drivers/net/wireless/iwlwifi/iwl-drv.c)32
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-drv.h (renamed from drivers/net/wireless/iwlwifi/iwl-drv.h)4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c (renamed from drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c)8
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h (renamed from drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c (renamed from drivers/net/wireless/iwlwifi/iwl-eeprom-read.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h (renamed from drivers/net/wireless/iwlwifi/iwl-eeprom-read.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-fh.h (renamed from drivers/net/wireless/iwlwifi/iwl-fh.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h (renamed from drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h)9
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h (renamed from drivers/net/wireless/iwlwifi/iwl-fw-file.h)29
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-fw.h (renamed from drivers/net/wireless/iwlwifi/iwl-fw.h)16
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-io.c (renamed from drivers/net/wireless/iwlwifi/iwl-io.c)41
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-io.h (renamed from drivers/net/wireless/iwlwifi/iwl-io.h)6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-modparams.h (renamed from drivers/net/wireless/iwlwifi/iwl-modparams.h)15
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c (renamed from drivers/net/wireless/iwlwifi/iwl-notif-wait.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h (renamed from drivers/net/wireless/iwlwifi/iwl-notif-wait.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c (renamed from drivers/net/wireless/iwlwifi/iwl-nvm-parse.c)30
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h (renamed from drivers/net/wireless/iwlwifi/iwl-nvm-parse.h)4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h (renamed from drivers/net/wireless/iwlwifi/iwl-op-mode.h)13
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c (renamed from drivers/net/wireless/iwlwifi/iwl-phy-db.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h (renamed from drivers/net/wireless/iwlwifi/iwl-phy-db.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-prph.h (renamed from drivers/net/wireless/iwlwifi/iwl-prph.h)8
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-scd.h (renamed from drivers/net/wireless/iwlwifi/iwl-scd.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-trans.c (renamed from drivers/net/wireless/iwlwifi/iwl-trans.c)93
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-trans.h (renamed from drivers/net/wireless/iwlwifi/iwl-trans.h)234
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/Makefile (renamed from drivers/net/wireless/iwlwifi/mvm/Makefile)6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/binding.c (renamed from drivers/net/wireless/iwlwifi/mvm/binding.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/coex.c (renamed from drivers/net/wireless/iwlwifi/mvm/coex.c)43
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/coex_legacy.c (renamed from drivers/net/wireless/iwlwifi/mvm/coex_legacy.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/constants.h (renamed from drivers/net/wireless/iwlwifi/mvm/constants.h)3
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/d3.c (renamed from drivers/net/wireless/iwlwifi/mvm/d3.c)456
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c (renamed from drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c (renamed from drivers/net/wireless/iwlwifi/mvm/debugfs.c)68
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h (renamed from drivers/net/wireless/iwlwifi/mvm/debugfs.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h (renamed from drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h (renamed from drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h)19
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h (renamed from drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h (renamed from drivers/net/wireless/iwlwifi/mvm/fw-api-power.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h (renamed from drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h (renamed from drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h)136
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h (renamed from drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h)24
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h (renamed from drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h (renamed from drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tof.h (renamed from drivers/net/wireless/iwlwifi/mvm/fw-api-tof.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h (renamed from drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h (renamed from drivers/net/wireless/iwlwifi/mvm/fw-api.h)89
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c817
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h174
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw.c (renamed from drivers/net/wireless/iwlwifi/mvm/fw.c)135
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/led.c (renamed from drivers/net/wireless/iwlwifi/mvm/led.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c (renamed from drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c)72
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c (renamed from drivers/net/wireless/iwlwifi/mvm/mac80211.c)653
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mvm.h (renamed from drivers/net/wireless/iwlwifi/mvm/mvm.h)154
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/nvm.c (renamed from drivers/net/wireless/iwlwifi/mvm/nvm.c)110
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/offloading.c (renamed from drivers/net/wireless/iwlwifi/mvm/offloading.c)76
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/ops.c (renamed from drivers/net/wireless/iwlwifi/mvm/ops.c)359
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c (renamed from drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/power.c (renamed from drivers/net/wireless/iwlwifi/mvm/power.c)4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/quota.c (renamed from drivers/net/wireless/iwlwifi/mvm/quota.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rs.c (renamed from drivers/net/wireless/iwlwifi/mvm/rs.c)84
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rs.h (renamed from drivers/net/wireless/iwlwifi/mvm/rs.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rx.c (renamed from drivers/net/wireless/iwlwifi/mvm/rx.c)46
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c458
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/scan.c (renamed from drivers/net/wireless/iwlwifi/mvm/scan.c)97
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/sf.c (renamed from drivers/net/wireless/iwlwifi/mvm/sf.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/sta.c (renamed from drivers/net/wireless/iwlwifi/mvm/sta.c)193
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/sta.h (renamed from drivers/net/wireless/iwlwifi/mvm/sta.h)30
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tdls.c (renamed from drivers/net/wireless/iwlwifi/mvm/tdls.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/testmode.h (renamed from drivers/net/wireless/iwlwifi/mvm/testmode.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/time-event.c (renamed from drivers/net/wireless/iwlwifi/mvm/time-event.c)41
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/time-event.h (renamed from drivers/net/wireless/iwlwifi/mvm/time-event.h)3
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tof.c (renamed from drivers/net/wireless/iwlwifi/mvm/tof.c)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tof.h (renamed from drivers/net/wireless/iwlwifi/mvm/tof.h)2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tt.c (renamed from drivers/net/wireless/iwlwifi/mvm/tt.c)4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tx.c (renamed from drivers/net/wireless/iwlwifi/mvm/tx.c)113
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/utils.c (renamed from drivers/net/wireless/iwlwifi/mvm/utils.c)4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/drv.c (renamed from drivers/net/wireless/iwlwifi/pcie/drv.c)48
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/internal.h (renamed from drivers/net/wireless/iwlwifi/pcie/internal.h)43
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/rx.c (renamed from drivers/net/wireless/iwlwifi/pcie/rx.c)30
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/trans.c (renamed from drivers/net/wireless/iwlwifi/pcie/trans.c)290
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/tx.c (renamed from drivers/net/wireless/iwlwifi/pcie/tx.c)447
-rw-r--r--drivers/net/wireless/intersil/Kconfig38
-rw-r--r--drivers/net/wireless/intersil/Makefile4
-rw-r--r--drivers/net/wireless/intersil/hostap/Kconfig (renamed from drivers/net/wireless/hostap/Kconfig)0
-rw-r--r--drivers/net/wireless/intersil/hostap/Makefile (renamed from drivers/net/wireless/hostap/Makefile)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap.h (renamed from drivers/net/wireless/hostap/hostap.h)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_80211.h (renamed from drivers/net/wireless/hostap/hostap_80211.h)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_80211_rx.c (renamed from drivers/net/wireless/hostap/hostap_80211_rx.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_80211_tx.c (renamed from drivers/net/wireless/hostap/hostap_80211_tx.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_ap.c (renamed from drivers/net/wireless/hostap/hostap_ap.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_ap.h (renamed from drivers/net/wireless/hostap/hostap_ap.h)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_common.h (renamed from drivers/net/wireless/hostap/hostap_common.h)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_config.h (renamed from drivers/net/wireless/hostap/hostap_config.h)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_cs.c (renamed from drivers/net/wireless/hostap/hostap_cs.c)6
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_download.c (renamed from drivers/net/wireless/hostap/hostap_download.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_hw.c (renamed from drivers/net/wireless/hostap/hostap_hw.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_info.c (renamed from drivers/net/wireless/hostap/hostap_info.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_ioctl.c (renamed from drivers/net/wireless/hostap/hostap_ioctl.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_main.c (renamed from drivers/net/wireless/hostap/hostap_main.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_pci.c (renamed from drivers/net/wireless/hostap/hostap_pci.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_plx.c (renamed from drivers/net/wireless/hostap/hostap_plx.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_proc.c (renamed from drivers/net/wireless/hostap/hostap_proc.c)0
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_wlan.h (renamed from drivers/net/wireless/hostap/hostap_wlan.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/Kconfig (renamed from drivers/net/wireless/orinoco/Kconfig)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/Makefile (renamed from drivers/net/wireless/orinoco/Makefile)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/airport.c (renamed from drivers/net/wireless/orinoco/airport.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/cfg.c (renamed from drivers/net/wireless/orinoco/cfg.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/cfg.h (renamed from drivers/net/wireless/orinoco/cfg.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/fw.c (renamed from drivers/net/wireless/orinoco/fw.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/fw.h (renamed from drivers/net/wireless/orinoco/fw.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/hermes.c (renamed from drivers/net/wireless/orinoco/hermes.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/hermes.h (renamed from drivers/net/wireless/orinoco/hermes.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/hermes_dld.c (renamed from drivers/net/wireless/orinoco/hermes_dld.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/hermes_dld.h (renamed from drivers/net/wireless/orinoco/hermes_dld.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/hermes_rid.h (renamed from drivers/net/wireless/orinoco/hermes_rid.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/hw.c (renamed from drivers/net/wireless/orinoco/hw.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/hw.h (renamed from drivers/net/wireless/orinoco/hw.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/main.c (renamed from drivers/net/wireless/orinoco/main.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/main.h (renamed from drivers/net/wireless/orinoco/main.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/mic.c (renamed from drivers/net/wireless/orinoco/mic.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/mic.h (renamed from drivers/net/wireless/orinoco/mic.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco.h (renamed from drivers/net/wireless/orinoco/orinoco.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_cs.c (renamed from drivers/net/wireless/orinoco/orinoco_cs.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_nortel.c (renamed from drivers/net/wireless/orinoco/orinoco_nortel.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_pci.c (renamed from drivers/net/wireless/orinoco/orinoco_pci.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_pci.h (renamed from drivers/net/wireless/orinoco/orinoco_pci.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_plx.c (renamed from drivers/net/wireless/orinoco/orinoco_plx.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_tmd.c (renamed from drivers/net/wireless/orinoco/orinoco_tmd.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/orinoco_usb.c (renamed from drivers/net/wireless/orinoco/orinoco_usb.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/scan.c (renamed from drivers/net/wireless/orinoco/scan.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/scan.h (renamed from drivers/net/wireless/orinoco/scan.h)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/spectrum_cs.c (renamed from drivers/net/wireless/orinoco/spectrum_cs.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/wext.c (renamed from drivers/net/wireless/orinoco/wext.c)0
-rw-r--r--drivers/net/wireless/intersil/orinoco/wext.h (renamed from drivers/net/wireless/orinoco/wext.h)0
-rw-r--r--drivers/net/wireless/intersil/p54/Kconfig (renamed from drivers/net/wireless/p54/Kconfig)0
-rw-r--r--drivers/net/wireless/intersil/p54/Makefile (renamed from drivers/net/wireless/p54/Makefile)0
-rw-r--r--drivers/net/wireless/intersil/p54/eeprom.c (renamed from drivers/net/wireless/p54/eeprom.c)0
-rw-r--r--drivers/net/wireless/intersil/p54/eeprom.h (renamed from drivers/net/wireless/p54/eeprom.h)0
-rw-r--r--drivers/net/wireless/intersil/p54/fwio.c (renamed from drivers/net/wireless/p54/fwio.c)0
-rw-r--r--drivers/net/wireless/intersil/p54/led.c (renamed from drivers/net/wireless/p54/led.c)0
-rw-r--r--drivers/net/wireless/intersil/p54/lmac.h (renamed from drivers/net/wireless/p54/lmac.h)0
-rw-r--r--drivers/net/wireless/intersil/p54/main.c (renamed from drivers/net/wireless/p54/main.c)0
-rw-r--r--drivers/net/wireless/intersil/p54/p54.h (renamed from drivers/net/wireless/p54/p54.h)0
-rw-r--r--drivers/net/wireless/intersil/p54/p54pci.c (renamed from drivers/net/wireless/p54/p54pci.c)0
-rw-r--r--drivers/net/wireless/intersil/p54/p54pci.h (renamed from drivers/net/wireless/p54/p54pci.h)0
-rw-r--r--drivers/net/wireless/intersil/p54/p54spi.c (renamed from drivers/net/wireless/p54/p54spi.c)0
-rw-r--r--drivers/net/wireless/intersil/p54/p54spi.h (renamed from drivers/net/wireless/p54/p54spi.h)0
-rw-r--r--drivers/net/wireless/intersil/p54/p54spi_eeprom.h (renamed from drivers/net/wireless/p54/p54spi_eeprom.h)0
-rw-r--r--drivers/net/wireless/intersil/p54/p54usb.c (renamed from drivers/net/wireless/p54/p54usb.c)0
-rw-r--r--drivers/net/wireless/intersil/p54/p54usb.h (renamed from drivers/net/wireless/p54/p54usb.h)0
-rw-r--r--drivers/net/wireless/intersil/p54/txrx.c (renamed from drivers/net/wireless/p54/txrx.c)0
-rw-r--r--drivers/net/wireless/intersil/prism54/Makefile (renamed from drivers/net/wireless/prism54/Makefile)0
-rw-r--r--drivers/net/wireless/intersil/prism54/isl_38xx.c (renamed from drivers/net/wireless/prism54/isl_38xx.c)0
-rw-r--r--drivers/net/wireless/intersil/prism54/isl_38xx.h (renamed from drivers/net/wireless/prism54/isl_38xx.h)0
-rw-r--r--drivers/net/wireless/intersil/prism54/isl_ioctl.c (renamed from drivers/net/wireless/prism54/isl_ioctl.c)2
-rw-r--r--drivers/net/wireless/intersil/prism54/isl_ioctl.h (renamed from drivers/net/wireless/prism54/isl_ioctl.h)0
-rw-r--r--drivers/net/wireless/intersil/prism54/isl_oid.h (renamed from drivers/net/wireless/prism54/isl_oid.h)0
-rw-r--r--drivers/net/wireless/intersil/prism54/islpci_dev.c (renamed from drivers/net/wireless/prism54/islpci_dev.c)4
-rw-r--r--drivers/net/wireless/intersil/prism54/islpci_dev.h (renamed from drivers/net/wireless/prism54/islpci_dev.h)0
-rw-r--r--drivers/net/wireless/intersil/prism54/islpci_eth.c (renamed from drivers/net/wireless/prism54/islpci_eth.c)5
-rw-r--r--drivers/net/wireless/intersil/prism54/islpci_eth.h (renamed from drivers/net/wireless/prism54/islpci_eth.h)0
-rw-r--r--drivers/net/wireless/intersil/prism54/islpci_hotplug.c (renamed from drivers/net/wireless/prism54/islpci_hotplug.c)0
-rw-r--r--drivers/net/wireless/intersil/prism54/islpci_mgt.c (renamed from drivers/net/wireless/prism54/islpci_mgt.c)4
-rw-r--r--drivers/net/wireless/intersil/prism54/islpci_mgt.h (renamed from drivers/net/wireless/prism54/islpci_mgt.h)0
-rw-r--r--drivers/net/wireless/intersil/prism54/oid_mgt.c (renamed from drivers/net/wireless/prism54/oid_mgt.c)10
-rw-r--r--drivers/net/wireless/intersil/prism54/oid_mgt.h (renamed from drivers/net/wireless/prism54/oid_mgt.h)0
-rw-r--r--drivers/net/wireless/intersil/prism54/prismcompat.h (renamed from drivers/net/wireless/prism54/prismcompat.h)0
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c95
-rw-r--r--drivers/net/wireless/marvell/Kconfig27
-rw-r--r--drivers/net/wireless/marvell/Makefile6
-rw-r--r--drivers/net/wireless/marvell/libertas/Kconfig (renamed from drivers/net/wireless/libertas/Kconfig)0
-rw-r--r--drivers/net/wireless/marvell/libertas/LICENSE (renamed from drivers/net/wireless/libertas/LICENSE)0
-rw-r--r--drivers/net/wireless/marvell/libertas/Makefile (renamed from drivers/net/wireless/libertas/Makefile)0
-rw-r--r--drivers/net/wireless/marvell/libertas/README (renamed from drivers/net/wireless/libertas/README)0
-rw-r--r--drivers/net/wireless/marvell/libertas/cfg.c (renamed from drivers/net/wireless/libertas/cfg.c)3
-rw-r--r--drivers/net/wireless/marvell/libertas/cfg.h (renamed from drivers/net/wireless/libertas/cfg.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/cmd.c (renamed from drivers/net/wireless/libertas/cmd.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/cmd.h (renamed from drivers/net/wireless/libertas/cmd.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/cmdresp.c (renamed from drivers/net/wireless/libertas/cmdresp.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/debugfs.c (renamed from drivers/net/wireless/libertas/debugfs.c)181
-rw-r--r--drivers/net/wireless/marvell/libertas/debugfs.h (renamed from drivers/net/wireless/libertas/debugfs.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/decl.h (renamed from drivers/net/wireless/libertas/decl.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/defs.h (renamed from drivers/net/wireless/libertas/defs.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/dev.h (renamed from drivers/net/wireless/libertas/dev.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/ethtool.c (renamed from drivers/net/wireless/libertas/ethtool.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/firmware.c (renamed from drivers/net/wireless/libertas/firmware.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/host.h (renamed from drivers/net/wireless/libertas/host.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/if_cs.c (renamed from drivers/net/wireless/libertas/if_cs.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/if_sdio.c (renamed from drivers/net/wireless/libertas/if_sdio.c)2
-rw-r--r--drivers/net/wireless/marvell/libertas/if_sdio.h (renamed from drivers/net/wireless/libertas/if_sdio.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/if_spi.c (renamed from drivers/net/wireless/libertas/if_spi.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/if_spi.h (renamed from drivers/net/wireless/libertas/if_spi.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/if_usb.c (renamed from drivers/net/wireless/libertas/if_usb.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/if_usb.h (renamed from drivers/net/wireless/libertas/if_usb.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/main.c (renamed from drivers/net/wireless/libertas/main.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/mesh.c (renamed from drivers/net/wireless/libertas/mesh.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/mesh.h (renamed from drivers/net/wireless/libertas/mesh.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/radiotap.h (renamed from drivers/net/wireless/libertas/radiotap.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas/rx.c (renamed from drivers/net/wireless/libertas/rx.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/tx.c (renamed from drivers/net/wireless/libertas/tx.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas/types.h (renamed from drivers/net/wireless/libertas/types.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/Kconfig18
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/Makefile (renamed from drivers/net/wireless/libertas_tf/Makefile)0
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/cmd.c (renamed from drivers/net/wireless/libertas_tf/cmd.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/deb_defs.h (renamed from drivers/net/wireless/libertas_tf/deb_defs.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/if_usb.c (renamed from drivers/net/wireless/libertas_tf/if_usb.c)0
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/if_usb.h (renamed from drivers/net/wireless/libertas_tf/if_usb.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/libertas_tf.h (renamed from drivers/net/wireless/libertas_tf/libertas_tf.h)0
-rw-r--r--drivers/net/wireless/marvell/libertas_tf/main.c (renamed from drivers/net/wireless/libertas_tf/main.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11ac.c (renamed from drivers/net/wireless/mwifiex/11ac.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11ac.h (renamed from drivers/net/wireless/mwifiex/11ac.h)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11h.c (renamed from drivers/net/wireless/mwifiex/11h.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11n.c (renamed from drivers/net/wireless/mwifiex/11n.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11n.h (renamed from drivers/net/wireless/mwifiex/11n.h)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11n_aggr.c (renamed from drivers/net/wireless/mwifiex/11n_aggr.c)2
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11n_aggr.h (renamed from drivers/net/wireless/mwifiex/11n_aggr.h)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c (renamed from drivers/net/wireless/mwifiex/11n_rxreorder.c)10
-rw-r--r--drivers/net/wireless/marvell/mwifiex/11n_rxreorder.h (renamed from drivers/net/wireless/mwifiex/11n_rxreorder.h)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/Kconfig (renamed from drivers/net/wireless/mwifiex/Kconfig)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/Makefile (renamed from drivers/net/wireless/mwifiex/Makefile)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/README (renamed from drivers/net/wireless/mwifiex/README)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/cfg80211.c (renamed from drivers/net/wireless/mwifiex/cfg80211.c)62
-rw-r--r--drivers/net/wireless/marvell/mwifiex/cfg80211.h (renamed from drivers/net/wireless/mwifiex/cfg80211.h)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/cfp.c (renamed from drivers/net/wireless/mwifiex/cfp.c)6
-rw-r--r--drivers/net/wireless/marvell/mwifiex/cmdevt.c (renamed from drivers/net/wireless/mwifiex/cmdevt.c)4
-rw-r--r--drivers/net/wireless/marvell/mwifiex/debugfs.c (renamed from drivers/net/wireless/mwifiex/debugfs.c)112
-rw-r--r--drivers/net/wireless/marvell/mwifiex/decl.h (renamed from drivers/net/wireless/mwifiex/decl.h)6
-rw-r--r--drivers/net/wireless/marvell/mwifiex/ethtool.c (renamed from drivers/net/wireless/mwifiex/ethtool.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/fw.h (renamed from drivers/net/wireless/mwifiex/fw.h)13
-rw-r--r--drivers/net/wireless/marvell/mwifiex/ie.c (renamed from drivers/net/wireless/mwifiex/ie.c)2
-rw-r--r--drivers/net/wireless/marvell/mwifiex/init.c (renamed from drivers/net/wireless/mwifiex/init.c)2
-rw-r--r--drivers/net/wireless/marvell/mwifiex/ioctl.h (renamed from drivers/net/wireless/mwifiex/ioctl.h)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/join.c (renamed from drivers/net/wireless/mwifiex/join.c)20
-rw-r--r--drivers/net/wireless/marvell/mwifiex/main.c (renamed from drivers/net/wireless/mwifiex/main.c)2
-rw-r--r--drivers/net/wireless/marvell/mwifiex/main.h (renamed from drivers/net/wireless/mwifiex/main.h)50
-rw-r--r--drivers/net/wireless/marvell/mwifiex/pcie.c (renamed from drivers/net/wireless/mwifiex/pcie.c)65
-rw-r--r--drivers/net/wireless/marvell/mwifiex/pcie.h (renamed from drivers/net/wireless/mwifiex/pcie.h)10
-rw-r--r--drivers/net/wireless/marvell/mwifiex/scan.c (renamed from drivers/net/wireless/mwifiex/scan.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sdio.c (renamed from drivers/net/wireless/mwifiex/sdio.c)15
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sdio.h (renamed from drivers/net/wireless/mwifiex/sdio.h)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_cmd.c (renamed from drivers/net/wireless/mwifiex/sta_cmd.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c (renamed from drivers/net/wireless/mwifiex/sta_cmdresp.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_event.c (renamed from drivers/net/wireless/mwifiex/sta_event.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_ioctl.c (renamed from drivers/net/wireless/mwifiex/sta_ioctl.c)85
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_rx.c (renamed from drivers/net/wireless/mwifiex/sta_rx.c)2
-rw-r--r--drivers/net/wireless/marvell/mwifiex/sta_tx.c (renamed from drivers/net/wireless/mwifiex/sta_tx.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/tdls.c (renamed from drivers/net/wireless/mwifiex/tdls.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/txrx.c (renamed from drivers/net/wireless/mwifiex/txrx.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/uap_cmd.c (renamed from drivers/net/wireless/mwifiex/uap_cmd.c)6
-rw-r--r--drivers/net/wireless/marvell/mwifiex/uap_event.c (renamed from drivers/net/wireless/mwifiex/uap_event.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/uap_txrx.c (renamed from drivers/net/wireless/mwifiex/uap_txrx.c)3
-rw-r--r--drivers/net/wireless/marvell/mwifiex/usb.c (renamed from drivers/net/wireless/mwifiex/usb.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/usb.h (renamed from drivers/net/wireless/mwifiex/usb.h)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/util.c (renamed from drivers/net/wireless/mwifiex/util.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/util.h (renamed from drivers/net/wireless/mwifiex/util.h)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/wmm.c (renamed from drivers/net/wireless/mwifiex/wmm.c)0
-rw-r--r--drivers/net/wireless/marvell/mwifiex/wmm.h (renamed from drivers/net/wireless/mwifiex/wmm.h)0
-rw-r--r--drivers/net/wireless/marvell/mwl8k.c (renamed from drivers/net/wireless/mwl8k.c)0
-rw-r--r--drivers/net/wireless/mediatek/Kconfig16
-rw-r--r--drivers/net/wireless/ralink/Kconfig16
-rw-r--r--drivers/net/wireless/ralink/Makefile1
-rw-r--r--drivers/net/wireless/ralink/rt2x00/Kconfig (renamed from drivers/net/wireless/rt2x00/Kconfig)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/Makefile (renamed from drivers/net/wireless/rt2x00/Makefile)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2400pci.c (renamed from drivers/net/wireless/rt2x00/rt2400pci.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2400pci.h (renamed from drivers/net/wireless/rt2x00/rt2400pci.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2500pci.c (renamed from drivers/net/wireless/rt2x00/rt2500pci.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2500pci.h (renamed from drivers/net/wireless/rt2x00/rt2500pci.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2500usb.c (renamed from drivers/net/wireless/rt2x00/rt2500usb.c)5
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2500usb.h (renamed from drivers/net/wireless/rt2x00/rt2500usb.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800.h (renamed from drivers/net/wireless/rt2x00/rt2800.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800lib.c (renamed from drivers/net/wireless/rt2x00/rt2800lib.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800lib.h (renamed from drivers/net/wireless/rt2x00/rt2800lib.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800mmio.c (renamed from drivers/net/wireless/rt2x00/rt2800mmio.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800mmio.h (renamed from drivers/net/wireless/rt2x00/rt2800mmio.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800pci.c (renamed from drivers/net/wireless/rt2x00/rt2800pci.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800pci.h (renamed from drivers/net/wireless/rt2x00/rt2800pci.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800soc.c (renamed from drivers/net/wireless/rt2x00/rt2800soc.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800usb.c (renamed from drivers/net/wireless/rt2x00/rt2800usb.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800usb.h (renamed from drivers/net/wireless/rt2x00/rt2800usb.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00.h (renamed from drivers/net/wireless/rt2x00/rt2x00.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00config.c (renamed from drivers/net/wireless/rt2x00/rt2x00config.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00crypto.c (renamed from drivers/net/wireless/rt2x00/rt2x00crypto.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00debug.c (renamed from drivers/net/wireless/rt2x00/rt2x00debug.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00debug.h (renamed from drivers/net/wireless/rt2x00/rt2x00debug.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00dev.c (renamed from drivers/net/wireless/rt2x00/rt2x00dev.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00dump.h (renamed from drivers/net/wireless/rt2x00/rt2x00dump.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00firmware.c (renamed from drivers/net/wireless/rt2x00/rt2x00firmware.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00leds.c (renamed from drivers/net/wireless/rt2x00/rt2x00leds.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00leds.h (renamed from drivers/net/wireless/rt2x00/rt2x00leds.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00lib.h (renamed from drivers/net/wireless/rt2x00/rt2x00lib.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00link.c (renamed from drivers/net/wireless/rt2x00/rt2x00link.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00mac.c (renamed from drivers/net/wireless/rt2x00/rt2x00mac.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00mmio.c (renamed from drivers/net/wireless/rt2x00/rt2x00mmio.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00mmio.h (renamed from drivers/net/wireless/rt2x00/rt2x00mmio.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00pci.c (renamed from drivers/net/wireless/rt2x00/rt2x00pci.c)2
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00pci.h (renamed from drivers/net/wireless/rt2x00/rt2x00pci.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00queue.c (renamed from drivers/net/wireless/rt2x00/rt2x00queue.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00queue.h (renamed from drivers/net/wireless/rt2x00/rt2x00queue.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00reg.h (renamed from drivers/net/wireless/rt2x00/rt2x00reg.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00soc.c (renamed from drivers/net/wireless/rt2x00/rt2x00soc.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00soc.h (renamed from drivers/net/wireless/rt2x00/rt2x00soc.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00usb.c (renamed from drivers/net/wireless/rt2x00/rt2x00usb.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00usb.h (renamed from drivers/net/wireless/rt2x00/rt2x00usb.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt61pci.c (renamed from drivers/net/wireless/rt2x00/rt61pci.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt61pci.h (renamed from drivers/net/wireless/rt2x00/rt61pci.h)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt73usb.c (renamed from drivers/net/wireless/rt2x00/rt73usb.c)0
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt73usb.h (renamed from drivers/net/wireless/rt2x00/rt73usb.h)0
-rw-r--r--drivers/net/wireless/realtek/Kconfig18
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c23
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c21
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c22
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c23
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c21
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/core.c3
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/pci.c11
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c7
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c6
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c9
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c8
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8723com/fw_common.c4
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c11
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/usb.c2
-rw-r--r--drivers/net/wireless/rsi/Kconfig15
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_mgmt.c5
-rw-r--r--drivers/net/wireless/st/Kconfig16
-rw-r--r--drivers/net/wireless/st/Makefile1
-rw-r--r--drivers/net/wireless/st/cw1200/Kconfig (renamed from drivers/net/wireless/cw1200/Kconfig)0
-rw-r--r--drivers/net/wireless/st/cw1200/Makefile (renamed from drivers/net/wireless/cw1200/Makefile)0
-rw-r--r--drivers/net/wireless/st/cw1200/bh.c (renamed from drivers/net/wireless/cw1200/bh.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/bh.h (renamed from drivers/net/wireless/cw1200/bh.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/cw1200.h (renamed from drivers/net/wireless/cw1200/cw1200.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/cw1200_sdio.c (renamed from drivers/net/wireless/cw1200/cw1200_sdio.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/cw1200_spi.c (renamed from drivers/net/wireless/cw1200/cw1200_spi.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/debug.c (renamed from drivers/net/wireless/cw1200/debug.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/debug.h (renamed from drivers/net/wireless/cw1200/debug.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/fwio.c (renamed from drivers/net/wireless/cw1200/fwio.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/fwio.h (renamed from drivers/net/wireless/cw1200/fwio.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/hwbus.h (renamed from drivers/net/wireless/cw1200/hwbus.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/hwio.c (renamed from drivers/net/wireless/cw1200/hwio.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/hwio.h (renamed from drivers/net/wireless/cw1200/hwio.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/main.c (renamed from drivers/net/wireless/cw1200/main.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/pm.c (renamed from drivers/net/wireless/cw1200/pm.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/pm.h (renamed from drivers/net/wireless/cw1200/pm.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/queue.c (renamed from drivers/net/wireless/cw1200/queue.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/queue.h (renamed from drivers/net/wireless/cw1200/queue.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/scan.c (renamed from drivers/net/wireless/cw1200/scan.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/scan.h (renamed from drivers/net/wireless/cw1200/scan.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/sta.c (renamed from drivers/net/wireless/cw1200/sta.c)6
-rw-r--r--drivers/net/wireless/st/cw1200/sta.h (renamed from drivers/net/wireless/cw1200/sta.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/txrx.c (renamed from drivers/net/wireless/cw1200/txrx.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/txrx.h (renamed from drivers/net/wireless/cw1200/txrx.h)0
-rw-r--r--drivers/net/wireless/st/cw1200/wsm.c (renamed from drivers/net/wireless/cw1200/wsm.c)0
-rw-r--r--drivers/net/wireless/st/cw1200/wsm.h (renamed from drivers/net/wireless/cw1200/wsm.h)0
-rw-r--r--drivers/net/wireless/ti/Kconfig18
-rw-r--r--drivers/net/wireless/ti/wl1251/Kconfig2
-rw-r--r--drivers/net/wireless/ti/wl12xx/conf.h233
-rw-r--r--drivers/net/wireless/ti/wl12xx/main.c116
-rw-r--r--drivers/net/wireless/ti/wl18xx/conf.h90
-rw-r--r--drivers/net/wireless/ti/wl18xx/event.c2
-rw-r--r--drivers/net/wireless/ti/wl18xx/event.h1
-rw-r--r--drivers/net/wireless/ti/wl18xx/main.c147
-rw-r--r--drivers/net/wireless/ti/wlcore/Kconfig2
-rw-r--r--drivers/net/wireless/ti/wlcore/acx.c4
-rw-r--r--drivers/net/wireless/ti/wlcore/acx.h2
-rw-r--r--drivers/net/wireless/ti/wlcore/cmd.h1
-rw-r--r--drivers/net/wireless/ti/wlcore/conf.h237
-rw-r--r--drivers/net/wireless/ti/wlcore/debugfs.c77
-rw-r--r--drivers/net/wireless/ti/wlcore/event.c82
-rw-r--r--drivers/net/wireless/ti/wlcore/event.h9
-rw-r--r--drivers/net/wireless/ti/wlcore/io.c11
-rw-r--r--drivers/net/wireless/ti/wlcore/io.h14
-rw-r--r--drivers/net/wireless/ti/wlcore/main.c96
-rw-r--r--drivers/net/wireless/ti/wlcore/rx.c1
-rw-r--r--drivers/net/wireless/ti/wlcore/spi.c10
-rw-r--r--drivers/net/wireless/ti/wlcore/sysfs.c26
-rw-r--r--drivers/net/wireless/ti/wlcore/wlcore.h3
-rw-r--r--drivers/net/wireless/zydas/Kconfig35
-rw-r--r--drivers/net/wireless/zydas/Makefile3
-rw-r--r--drivers/net/wireless/zydas/zd1201.c (renamed from drivers/net/wireless/zd1201.c)0
-rw-r--r--drivers/net/wireless/zydas/zd1201.h (renamed from drivers/net/wireless/zd1201.h)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/Kconfig (renamed from drivers/net/wireless/zd1211rw/Kconfig)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/Makefile (renamed from drivers/net/wireless/zd1211rw/Makefile)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_chip.c (renamed from drivers/net/wireless/zd1211rw/zd_chip.c)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_chip.h (renamed from drivers/net/wireless/zd1211rw/zd_chip.h)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_def.h (renamed from drivers/net/wireless/zd1211rw/zd_def.h)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_mac.c (renamed from drivers/net/wireless/zd1211rw/zd_mac.c)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_mac.h (renamed from drivers/net/wireless/zd1211rw/zd_mac.h)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_rf.c (renamed from drivers/net/wireless/zd1211rw/zd_rf.c)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_rf.h (renamed from drivers/net/wireless/zd1211rw/zd_rf.h)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_rf_al2230.c (renamed from drivers/net/wireless/zd1211rw/zd_rf_al2230.c)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_rf_al7230b.c (renamed from drivers/net/wireless/zd1211rw/zd_rf_al7230b.c)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_rf_rf2959.c (renamed from drivers/net/wireless/zd1211rw/zd_rf_rf2959.c)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_rf_uw2453.c (renamed from drivers/net/wireless/zd1211rw/zd_rf_uw2453.c)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_usb.c (renamed from drivers/net/wireless/zd1211rw/zd_usb.c)0
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_usb.h (renamed from drivers/net/wireless/zd1211rw/zd_usb.h)0
-rw-r--r--drivers/net/xen-netback/netback.c34
-rw-r--r--drivers/nfc/Kconfig1
-rw-r--r--drivers/nfc/Makefile1
-rw-r--r--drivers/nfc/fdp/i2c.c12
-rw-r--r--drivers/nfc/microread/i2c.c2
-rw-r--r--drivers/nfc/nfcsim.c10
-rw-r--r--drivers/nfc/nxp-nci/i2c.c34
-rw-r--r--drivers/nfc/pn544/i2c.c46
-rw-r--r--drivers/nfc/s3fwrn5/core.c2
-rw-r--r--drivers/nfc/s3fwrn5/i2c.c2
-rw-r--r--drivers/nfc/s3fwrn5/s3fwrn5.h4
-rw-r--r--drivers/nfc/st-nci/Kconfig18
-rw-r--r--drivers/nfc/st-nci/i2c.c80
-rw-r--r--drivers/nfc/st-nci/ndlc.c1
-rw-r--r--drivers/nfc/st-nci/se.c3
-rw-r--r--drivers/nfc/st-nci/spi.c81
-rw-r--r--drivers/nfc/st21nfca/Kconfig13
-rw-r--r--drivers/nfc/st21nfca/i2c.c80
-rw-r--r--drivers/nfc/st21nfca/se.c5
-rw-r--r--drivers/nfc/st95hf/Kconfig10
-rw-r--r--drivers/nfc/st95hf/Makefile6
-rw-r--r--drivers/nfc/st95hf/core.c1273
-rw-r--r--drivers/nfc/st95hf/spi.c167
-rw-r--r--drivers/nfc/st95hf/spi.h64
-rw-r--r--drivers/nfc/trf7970a.c8
-rw-r--r--drivers/nvdimm/core.c169
-rw-r--r--drivers/nvdimm/namespace_devs.c132
-rw-r--r--drivers/nvdimm/nd-core.h2
-rw-r--r--drivers/nvdimm/nd.h16
-rw-r--r--drivers/nvdimm/pfn_devs.c93
-rw-r--r--drivers/nvdimm/pmem.c90
-rw-r--r--drivers/nvdimm/region_devs.c66
-rw-r--r--drivers/nvme/host/Makefile3
-rw-r--r--drivers/nvme/host/lightnvm.c187
-rw-r--r--drivers/nvme/host/nvme.h14
-rw-r--r--drivers/nvme/host/pci.c71
-rw-r--r--drivers/of/address.c7
-rw-r--r--drivers/of/dynamic.c65
-rw-r--r--drivers/of/fdt.c7
-rw-r--r--drivers/of/irq.c30
-rw-r--r--drivers/of/of_mdio.c95
-rw-r--r--drivers/of/of_private.h2
-rw-r--r--drivers/of/of_reserved_mem.c8
-rw-r--r--drivers/of/overlay.c8
-rw-r--r--drivers/of/platform.c1
-rw-r--r--drivers/of/unittest.c18
-rw-r--r--drivers/parisc/iommu-helpers.h15
-rw-r--r--drivers/parport/share.c246
-rw-r--r--drivers/pci/host/Kconfig1
-rw-r--r--drivers/pci/host/pcie-altera.c23
-rw-r--r--drivers/pci/host/pcie-designware.c1
-rw-r--r--drivers/pci/host/pcie-hisi.c8
-rw-r--r--drivers/pci/msi.c8
-rw-r--r--drivers/pci/pci-acpi.c44
-rw-r--r--drivers/pci/pci-driver.c16
-rw-r--r--drivers/pci/pci-sysfs.c5
-rw-r--r--drivers/pci/pci.c4
-rw-r--r--drivers/pci/pci.h4
-rw-r--r--drivers/pci/probe.c2
-rw-r--r--drivers/perf/arm_pmu.c15
-rw-r--r--drivers/phy/Kconfig21
-rw-r--r--drivers/phy/Makefile2
-rw-r--r--drivers/phy/phy-bcm-cygnus-pcie.c16
-rw-r--r--drivers/phy/phy-berlin-sata.c20
-rw-r--r--drivers/phy/phy-berlin-usb.c3
-rw-r--r--drivers/phy/phy-brcmstb-sata.c64
-rw-r--r--drivers/phy/phy-core.c21
-rw-r--r--drivers/phy/phy-hi6220-usb.c168
-rw-r--r--drivers/phy/phy-miphy28lp.c16
-rw-r--r--drivers/phy/phy-miphy365x.c16
-rw-r--r--drivers/phy/phy-mt65xx-usb3.c126
-rw-r--r--drivers/phy/phy-omap-usb2.c94
-rw-r--r--drivers/phy/phy-rcar-gen3-usb2.c378
-rw-r--r--drivers/phy/phy-rockchip-usb.c280
-rw-r--r--drivers/phy/phy-sun4i-usb.c158
-rw-r--r--drivers/phy/phy-ti-pipe3.c302
-rw-r--r--drivers/phy/phy-twl4030-usb.c32
-rw-r--r--drivers/pinctrl/Kconfig7
-rw-r--r--drivers/pinctrl/Makefile7
-rw-r--r--drivers/pinctrl/bcm/Kconfig48
-rw-r--r--drivers/pinctrl/bcm/Makefile3
-rw-r--r--drivers/pinctrl/bcm/pinctrl-bcm2835.c15
-rw-r--r--drivers/pinctrl/bcm/pinctrl-iproc-gpio.c (renamed from drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c)358
-rw-r--r--drivers/pinctrl/bcm/pinctrl-nsp-gpio.c749
-rw-r--r--drivers/pinctrl/berlin/Makefile2
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx1-core.c8
-rw-r--r--drivers/pinctrl/freescale/pinctrl-vf610.c2
-rw-r--r--drivers/pinctrl/intel/pinctrl-broxton.c1
-rw-r--r--drivers/pinctrl/intel/pinctrl-intel.c41
-rw-r--r--drivers/pinctrl/intel/pinctrl-intel.h3
-rw-r--r--drivers/pinctrl/intel/pinctrl-sunrisepoint.c1
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt8127.c2
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt8135.c2
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt8173.c2
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mtk-common.c39
-rw-r--r--drivers/pinctrl/mvebu/Makefile2
-rw-r--r--drivers/pinctrl/mvebu/pinctrl-mvebu.c29
-rw-r--r--drivers/pinctrl/pinconf-generic.c1
-rw-r--r--drivers/pinctrl/pinctrl-adi2.c24
-rw-r--r--drivers/pinctrl/pinctrl-at91-pio4.c17
-rw-r--r--drivers/pinctrl/pinctrl-at91.c14
-rw-r--r--drivers/pinctrl/pinctrl-lantiq.h8
-rw-r--r--drivers/pinctrl/pinctrl-rockchip.c58
-rw-r--r--drivers/pinctrl/pinctrl-single.c5
-rw-r--r--drivers/pinctrl/pinctrl-tegra-xusb.c4
-rw-r--r--drivers/pinctrl/pinctrl-tegra.c1
-rw-r--r--drivers/pinctrl/pinctrl-xway.c1187
-rw-r--r--drivers/pinctrl/pxa/Kconfig17
-rw-r--r--drivers/pinctrl/pxa/Makefile2
-rw-r--r--drivers/pinctrl/pxa/pinctrl-pxa27x.c566
-rw-r--r--drivers/pinctrl/pxa/pinctrl-pxa2xx.c436
-rw-r--r--drivers/pinctrl/pxa/pinctrl-pxa2xx.h92
-rw-r--r--drivers/pinctrl/qcom/Kconfig8
-rw-r--r--drivers/pinctrl/qcom/Makefile1
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm8996.c1942
-rw-r--r--drivers/pinctrl/qcom/pinctrl-qdf2xxx.c14
-rw-r--r--drivers/pinctrl/qcom/pinctrl-spmi-gpio.c15
-rw-r--r--drivers/pinctrl/qcom/pinctrl-spmi-mpp.c14
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c21
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c21
-rw-r--r--drivers/pinctrl/samsung/pinctrl-exynos.c103
-rw-r--r--drivers/pinctrl/samsung/pinctrl-samsung.c2
-rw-r--r--drivers/pinctrl/samsung/pinctrl-samsung.h1
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-emev2.c136
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7740.c2
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7778.c22
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7779.c62
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7790.c58
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7791.c167
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7794.c86
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-r8a7795.c1505
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-sh73a0.c548
-rw-r--r--drivers/pinctrl/sh-pfc/pfc-sh7734.c35
-rw-r--r--drivers/pinctrl/sh-pfc/pinctrl.c4
-rw-r--r--drivers/pinctrl/sh-pfc/sh_pfc.h74
-rw-r--r--drivers/pinctrl/sirf/pinctrl-atlas7.c134
-rw-r--r--drivers/pinctrl/sirf/pinctrl-sirf.c8
-rw-r--r--drivers/pinctrl/spear/Makefile2
-rw-r--r--drivers/pinctrl/sunxi/Kconfig9
-rw-r--r--drivers/pinctrl/sunxi/Makefile2
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun8i-h3.c515
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c181
-rw-r--r--drivers/pinctrl/uniphier/Kconfig23
-rw-r--r--drivers/platform/x86/dell-wmi.c6
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c2
-rw-r--r--drivers/pnp/driver.c6
-rw-r--r--drivers/pnp/quirks.c1
-rw-r--r--drivers/power/Kconfig17
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/bq2415x_charger.c6
-rw-r--r--drivers/power/bq27xxx_battery.c348
-rw-r--r--drivers/power/bq27xxx_battery_i2c.c150
-rw-r--r--drivers/power/ds2782_battery.c4
-rw-r--r--drivers/power/generic-adc-battery.c2
-rw-r--r--drivers/power/isp1704_charger.c2
-rw-r--r--drivers/power/max8903_charger.c24
-rw-r--r--drivers/power/reset/at91-reset.c1
-rw-r--r--drivers/power/test_power.c2
-rw-r--r--drivers/powercap/intel_rapl.c21
-rw-r--r--drivers/powercap/powercap_sys.c18
-rw-r--r--drivers/regulator/Kconfig37
-rw-r--r--drivers/regulator/Makefile4
-rw-r--r--drivers/regulator/axp20x-regulator.c4
-rw-r--r--drivers/regulator/core.c41
-rw-r--r--drivers/regulator/da903x.c10
-rw-r--r--drivers/regulator/da9052-regulator.c4
-rw-r--r--drivers/regulator/da9055-regulator.c4
-rw-r--r--drivers/regulator/da9062-regulator.c4
-rw-r--r--drivers/regulator/da9063-regulator.c4
-rw-r--r--drivers/regulator/da9210-regulator.c2
-rw-r--r--drivers/regulator/da9211-regulator.c2
-rw-r--r--drivers/regulator/devres.c7
-rw-r--r--drivers/regulator/lm363x-regulator.c291
-rw-r--r--drivers/regulator/lp872x.c17
-rw-r--r--drivers/regulator/lp8788-buck.c4
-rw-r--r--drivers/regulator/lp8788-ldo.c20
-rw-r--r--drivers/regulator/mt6311-regulator.c1
-rw-r--r--drivers/regulator/palmas-regulator.c39
-rw-r--r--drivers/regulator/pv88060-regulator.c437
-rw-r--r--drivers/regulator/pv88060-regulator.h69
-rw-r--r--drivers/regulator/pv88090-regulator.c458
-rw-r--r--drivers/regulator/pv88090-regulator.h98
-rw-r--r--drivers/regulator/qcom_smd-regulator.c159
-rw-r--r--drivers/regulator/s2mps11.c143
-rw-r--r--drivers/regulator/tps6105x-regulator.c95
-rw-r--r--drivers/regulator/tps65086-regulator.c251
-rw-r--r--drivers/regulator/tps65218-regulator.c137
-rw-r--r--drivers/regulator/wm831x-dcdc.c39
-rw-r--r--drivers/regulator/wm831x-isink.c2
-rw-r--r--drivers/regulator/wm831x-ldo.c33
-rw-r--r--drivers/regulator/wm8350-regulator.c8
-rw-r--r--drivers/regulator/wm8400-regulator.c4
-rw-r--r--drivers/regulator/wm8994-regulator.c4
-rw-r--r--drivers/remoteproc/remoteproc_core.c2
-rw-r--r--drivers/remoteproc/remoteproc_debugfs.c2
-rw-r--r--drivers/rtc/rtc-da9063.c19
-rw-r--r--drivers/rtc/rtc-ds1307.c44
-rw-r--r--drivers/rtc/rtc-rk808.c48
-rw-r--r--drivers/rtc/rtc-s5m.c37
-rw-r--r--drivers/s390/block/dasd.c8
-rw-r--r--drivers/s390/char/Kconfig21
-rw-r--r--drivers/s390/char/Makefile5
-rw-r--r--drivers/s390/char/con3215.c2
-rw-r--r--drivers/s390/char/con3270.c2
-rw-r--r--drivers/s390/char/hmcdrv_ftp.c6
-rw-r--r--drivers/s390/char/sclp.c5
-rw-r--r--drivers/s390/char/sclp_config.c102
-rw-r--r--drivers/s390/char/sclp_cpi.c40
-rw-r--r--drivers/s390/char/sclp_early.c16
-rw-r--r--drivers/s390/char/vmcp.c11
-rw-r--r--drivers/s390/char/vmur.c15
-rw-r--r--drivers/s390/char/zcore.c461
-rw-r--r--drivers/s390/cio/Makefile5
-rw-r--r--drivers/s390/cio/airq.c1
-rw-r--r--drivers/s390/cio/chsc_sch.c5
-rw-r--r--drivers/s390/cio/cio.c37
-rw-r--r--drivers/s390/cio/cio.h12
-rw-r--r--drivers/s390/cio/crw.c1
-rw-r--r--drivers/s390/cio/css.c2
-rw-r--r--drivers/s390/cio/device_fsm.c2
-rw-r--r--drivers/s390/cio/io_sch.h45
-rw-r--r--drivers/s390/cio/ioasm.c224
-rw-r--r--drivers/s390/cio/ioasm.h169
-rw-r--r--drivers/s390/cio/qdio_debug.c6
-rw-r--r--drivers/s390/cio/trace.c24
-rw-r--r--drivers/s390/cio/trace.h363
-rw-r--r--drivers/s390/crypto/ap_bus.c4
-rw-r--r--drivers/s390/crypto/zcrypt_api.c6
-rw-r--r--drivers/s390/net/ctcm_main.c7
-rw-r--r--drivers/s390/net/qeth_core.h4
-rw-r--r--drivers/s390/net/qeth_core_main.c11
-rw-r--r--drivers/s390/net/qeth_l2_main.c1
-rw-r--r--drivers/s390/net/qeth_l3_main.c40
-rw-r--r--drivers/s390/virtio/virtio_ccw.c62
-rw-r--r--drivers/sbus/char/openprom.c13
-rw-r--r--drivers/scsi/Kconfig4
-rw-r--r--drivers/scsi/Makefile1
-rw-r--r--drivers/scsi/aacraid/linit.c2
-rw-r--r--drivers/scsi/advansys.c2
-rw-r--r--drivers/scsi/aic94xx/aic94xx_sas.h49
-rw-r--r--drivers/scsi/arcmsr/arcmsr.h14
-rw-r--r--drivers/scsi/arcmsr/arcmsr_hba.c209
-rw-r--r--drivers/scsi/atp870u.c3635
-rw-r--r--drivers/scsi/atp870u.h4
-rw-r--r--drivers/scsi/bfa/bfa.h7
-rw-r--r--drivers/scsi/bfa/bfa_core.c7
-rw-r--r--drivers/scsi/bfa/bfa_cs.h7
-rw-r--r--drivers/scsi/bfa/bfa_defs.h7
-rw-r--r--drivers/scsi/bfa/bfa_defs_fcs.h7
-rw-r--r--drivers/scsi/bfa/bfa_defs_svc.h7
-rw-r--r--drivers/scsi/bfa/bfa_fc.h7
-rw-r--r--drivers/scsi/bfa/bfa_fcbuild.c7
-rw-r--r--drivers/scsi/bfa/bfa_fcbuild.h7
-rw-r--r--drivers/scsi/bfa/bfa_fcpim.c7
-rw-r--r--drivers/scsi/bfa/bfa_fcpim.h7
-rw-r--r--drivers/scsi/bfa/bfa_fcs.c7
-rw-r--r--drivers/scsi/bfa/bfa_fcs.h9
-rw-r--r--drivers/scsi/bfa/bfa_fcs_fcpim.c7
-rw-r--r--drivers/scsi/bfa/bfa_fcs_lport.c9
-rw-r--r--drivers/scsi/bfa/bfa_fcs_rport.c7
-rw-r--r--drivers/scsi/bfa/bfa_hw_cb.c7
-rw-r--r--drivers/scsi/bfa/bfa_hw_ct.c7
-rw-r--r--drivers/scsi/bfa/bfa_ioc.c9
-rw-r--r--drivers/scsi/bfa/bfa_ioc.h7
-rw-r--r--drivers/scsi/bfa/bfa_ioc_cb.c7
-rw-r--r--drivers/scsi/bfa/bfa_ioc_ct.c7
-rw-r--r--drivers/scsi/bfa/bfa_modules.h7
-rw-r--r--drivers/scsi/bfa/bfa_plog.h7
-rw-r--r--drivers/scsi/bfa/bfa_port.c7
-rw-r--r--drivers/scsi/bfa/bfa_port.h7
-rw-r--r--drivers/scsi/bfa/bfa_svc.c7
-rw-r--r--drivers/scsi/bfa/bfa_svc.h7
-rw-r--r--drivers/scsi/bfa/bfad.c24
-rw-r--r--drivers/scsi/bfa/bfad_attr.c75
-rw-r--r--drivers/scsi/bfa/bfad_bsg.c7
-rw-r--r--drivers/scsi/bfa/bfad_bsg.h7
-rw-r--r--drivers/scsi/bfa/bfad_debugfs.c7
-rw-r--r--drivers/scsi/bfa/bfad_drv.h9
-rw-r--r--drivers/scsi/bfa/bfad_im.c35
-rw-r--r--drivers/scsi/bfa/bfad_im.h7
-rw-r--r--drivers/scsi/bfa/bfi.h7
-rw-r--r--drivers/scsi/bfa/bfi_ms.h7
-rw-r--r--drivers/scsi/bfa/bfi_reg.h9
-rw-r--r--drivers/scsi/cxlflash/common.h2
-rw-r--r--drivers/scsi/cxlflash/main.c54
-rw-r--r--drivers/scsi/cxlflash/main.h5
-rw-r--r--drivers/scsi/cxlflash/superpipe.c8
-rw-r--r--drivers/scsi/cxlflash/vlun.c2
-rw-r--r--drivers/scsi/device_handler/scsi_dh_alua.c294
-rw-r--r--drivers/scsi/fcoe/fcoe.c2
-rw-r--r--drivers/scsi/hisi_sas/Kconfig6
-rw-r--r--drivers/scsi/hisi_sas/Makefile2
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas.h341
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_main.c1358
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_v1_hw.c1839
-rw-r--r--drivers/scsi/hosts.c11
-rw-r--r--drivers/scsi/hpsa.c119
-rw-r--r--drivers/scsi/hpsa.h2
-rw-r--r--drivers/scsi/hpsa_cmd.h13
-rw-r--r--drivers/scsi/initio.c16
-rw-r--r--drivers/scsi/lpfc/lpfc.h16
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c48
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h6
-rw-r--r--drivers/scsi/lpfc/lpfc_ct.c1864
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c484
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c65
-rw-r--r--drivers/scsi/lpfc/lpfc_hw.h184
-rw-r--r--drivers/scsi/lpfc/lpfc_hw4.h52
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c198
-rw-r--r--drivers/scsi/lpfc/lpfc_mem.c6
-rw-r--r--drivers/scsi/lpfc/lpfc_nportdisc.c134
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c41
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c23
-rw-r--r--drivers/scsi/lpfc/lpfc_sli4.h1
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_vport.c8
-rw-r--r--drivers/scsi/mpt3sas/Kconfig9
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_base.c4
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_scsih.c3
-rw-r--r--drivers/scsi/mvsas/mv_94xx.c134
-rw-r--r--drivers/scsi/mvsas/mv_94xx.h71
-rw-r--r--drivers/scsi/mvsas/mv_init.c6
-rw-r--r--drivers/scsi/mvsas/mv_sas.c17
-rw-r--r--drivers/scsi/mvsas/mv_sas.h5
-rw-r--r--drivers/scsi/osd/osd_initiator.c5
-rw-r--r--drivers/scsi/qla2xxx/Kconfig3
-rw-r--r--drivers/scsi/qla2xxx/qla_nx.c3
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c3
-rw-r--r--drivers/scsi/qla2xxx/tcm_qla2xxx.c2
-rw-r--r--drivers/scsi/scsi.c20
-rw-r--r--drivers/scsi/scsi_debug.c23
-rw-r--r--drivers/scsi/scsi_dh.c72
-rw-r--r--drivers/scsi/scsi_lib.c188
-rw-r--r--drivers/scsi/scsi_pm.c20
-rw-r--r--drivers/scsi/scsi_priv.h3
-rw-r--r--drivers/scsi/scsi_scan.c13
-rw-r--r--drivers/scsi/scsi_sysfs.c112
-rw-r--r--drivers/scsi/scsi_transport_fc.c12
-rw-r--r--drivers/scsi/scsi_transport_sas.c30
-rw-r--r--drivers/scsi/sd.c72
-rw-r--r--drivers/scsi/sd.h1
-rw-r--r--drivers/scsi/ses.c52
-rw-r--r--drivers/scsi/st.c5
-rw-r--r--drivers/scsi/st.h2
-rw-r--r--drivers/scsi/storvsc_drv.c309
-rw-r--r--drivers/scsi/ufs/ufshcd-pltfrm.c2
-rw-r--r--drivers/scsi/vmw_pvscsi.c45
-rw-r--r--drivers/scsi/vmw_pvscsi.h2
-rw-r--r--drivers/soc/mediatek/Kconfig1
-rw-r--r--drivers/soc/qcom/spm.c10
-rw-r--r--drivers/soc/ti/knav_dma.c2
-rw-r--r--drivers/soc/ti/knav_qmss_queue.c8
-rw-r--r--drivers/spi/Kconfig11
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/spi-bcm63xx.c11
-rw-r--r--drivers/spi/spi-butterfly.c30
-rw-r--r--drivers/spi/spi-cadence.c6
-rw-r--r--drivers/spi/spi-davinci.c17
-rw-r--r--drivers/spi/spi-dw-mid.c2
-rw-r--r--drivers/spi/spi-dw.c2
-rw-r--r--drivers/spi/spi-dw.h2
-rw-r--r--drivers/spi/spi-fsl-dspi.c12
-rw-r--r--drivers/spi/spi-fsl-espi.c6
-rw-r--r--drivers/spi/spi-imx.c101
-rw-r--r--drivers/spi/spi-lm70llp.c43
-rw-r--r--drivers/spi/spi-loopback-test.c1005
-rw-r--r--drivers/spi/spi-mt65xx.c86
-rw-r--r--drivers/spi/spi-omap2-mcspi.c31
-rw-r--r--drivers/spi/spi-pl022.c28
-rw-r--r--drivers/spi/spi-pxa2xx.c3
-rw-r--r--drivers/spi/spi-s3c64xx.c33
-rw-r--r--drivers/spi/spi-sun4i.c14
-rw-r--r--drivers/spi/spi-sun6i.c8
-rw-r--r--drivers/spi/spi-test.h136
-rw-r--r--drivers/spi/spi-zynqmp-gqspi.c8
-rw-r--r--drivers/spi/spi.c39
-rw-r--r--drivers/spi/spidev.c4
-rw-r--r--drivers/ssb/Kconfig2
-rw-r--r--drivers/ssb/host_soc.c37
-rw-r--r--drivers/ssb/main.c5
-rw-r--r--drivers/ssb/ssb_private.h3
-rw-r--r--drivers/staging/android/TODO8
-rw-r--r--drivers/staging/android/ashmem.c15
-rw-r--r--drivers/staging/android/ion/Kconfig7
-rw-r--r--drivers/staging/android/ion/Makefile1
-rw-r--r--drivers/staging/android/ion/compat_ion.c6
-rw-r--r--drivers/staging/android/ion/hisilicon/Kconfig5
-rw-r--r--drivers/staging/android/ion/hisilicon/Makefile1
-rw-r--r--drivers/staging/android/ion/hisilicon/hi6220_ion.c223
-rw-r--r--drivers/staging/android/ion/ion_chunk_heap.c4
-rw-r--r--drivers/staging/android/lowmemorykiller.c6
-rw-r--r--drivers/staging/android/sync.c41
-rw-r--r--drivers/staging/android/sync.h10
-rw-r--r--drivers/staging/android/sync_debug.c42
-rw-r--r--drivers/staging/android/timed_gpio.c11
-rw-r--r--drivers/staging/comedi/Kconfig24
-rw-r--r--drivers/staging/comedi/comedi.h307
-rw-r--r--drivers/staging/comedi/comedi_fops.c124
-rw-r--r--drivers/staging/comedi/comedilib.h32
-rw-r--r--drivers/staging/comedi/drivers/Makefile2
-rw-r--r--drivers/staging/comedi/drivers/adl_pci9118.c3
-rw-r--r--drivers/staging/comedi/drivers/adv_pci1710.c542
-rw-r--r--drivers/staging/comedi/drivers/adv_pci1720.c195
-rw-r--r--drivers/staging/comedi/drivers/adv_pci1760.c432
-rw-r--r--drivers/staging/comedi/drivers/adv_pci_dio.c1125
-rw-r--r--drivers/staging/comedi/drivers/amplc_pci224.c11
-rw-r--r--drivers/staging/comedi/drivers/cb_pcidda.c6
-rw-r--r--drivers/staging/comedi/drivers/comedi_parport.c4
-rw-r--r--drivers/staging/comedi/drivers/das16.c3
-rw-r--r--drivers/staging/comedi/drivers/ni_6527.c24
-rw-r--r--drivers/staging/comedi/drivers/ni_65xx.c54
-rw-r--r--drivers/staging/comedi/drivers/ni_670x.c5
-rw-r--r--drivers/staging/comedi/drivers/ni_mio_common.c100
-rw-r--r--drivers/staging/comedi/drivers/plx9080.h126
-rw-r--r--drivers/staging/comedi/drivers/s526.c197
-rw-r--r--drivers/staging/dgnc/dgnc_cls.c2
-rw-r--r--drivers/staging/dgnc/dgnc_neo.c10
-rw-r--r--drivers/staging/dgnc/dgnc_tty.c2
-rw-r--r--drivers/staging/dgnc/dgnc_utils.c1
-rw-r--r--drivers/staging/emxx_udc/emxx_udc.c40
-rw-r--r--drivers/staging/fwserial/fwserial.c3
-rw-r--r--drivers/staging/fwserial/fwserial.h10
-rw-r--r--drivers/staging/gdm724x/gdm_lte.c2
-rw-r--r--drivers/staging/gdm724x/gdm_tty.c2
-rw-r--r--drivers/staging/gdm724x/netlink_k.c5
-rw-r--r--drivers/staging/gdm724x/netlink_k.h1
-rw-r--r--drivers/staging/gdm72xx/gdm_qos.c52
-rw-r--r--drivers/staging/gdm72xx/gdm_sdio.c13
-rw-r--r--drivers/staging/gdm72xx/gdm_sdio.h4
-rw-r--r--drivers/staging/gdm72xx/gdm_wimax.c21
-rw-r--r--drivers/staging/gdm72xx/wm_ioctl.h7
-rw-r--r--drivers/staging/iio/Kconfig29
-rw-r--r--drivers/staging/iio/Makefile7
-rw-r--r--drivers/staging/iio/accel/sca3000_ring.c2
-rw-r--r--drivers/staging/iio/adc/ad7192.c2
-rw-r--r--drivers/staging/iio/adc/ad7280a.c2
-rw-r--r--drivers/staging/iio/adc/ad7780.c36
-rw-r--r--drivers/staging/iio/adc/ad7780.h30
-rw-r--r--drivers/staging/iio/adc/ad7816.c2
-rw-r--r--drivers/staging/iio/adc/mxs-lradc.c4
-rw-r--r--drivers/staging/iio/frequency/ad9832.c2
-rw-r--r--drivers/staging/iio/frequency/ad9834.c2
-rw-r--r--drivers/staging/iio/light/tsl2x7x_core.c2
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs.h9
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs_cpu.h3
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs_hash.h156
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs_ioctl.h1
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs_kernelcomm.h2
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs_private.h2
-rw-r--r--drivers/staging/lustre/include/linux/lnet/api.h6
-rw-r--r--drivers/staging/lustre/include/linux/lnet/lib-lnet.h2
-rw-r--r--drivers/staging/lustre/include/linux/lnet/lib-types.h2
-rw-r--r--drivers/staging/lustre/include/linux/lnet/nidstr.h2
-rw-r--r--drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c4
-rw-r--r--drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.h2
-rw-r--r--drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c2
-rw-r--r--drivers/staging/lustre/lnet/klnds/socklnd/socklnd.c13
-rw-r--r--drivers/staging/lustre/lnet/klnds/socklnd/socklnd.h3
-rw-r--r--drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c4
-rw-r--r--drivers/staging/lustre/lnet/lnet/acceptor.c2
-rw-r--r--drivers/staging/lustre/lnet/lnet/api-ni.c21
-rw-r--r--drivers/staging/lustre/lnet/lnet/config.c26
-rw-r--r--drivers/staging/lustre/lnet/lnet/lib-eq.c18
-rw-r--r--drivers/staging/lustre/lnet/lnet/lib-move.c3
-rw-r--r--drivers/staging/lustre/lnet/lnet/lib-ptl.c2
-rw-r--r--drivers/staging/lustre/lnet/lnet/lib-socket.c2
-rw-r--r--drivers/staging/lustre/lnet/lnet/module.c4
-rw-r--r--drivers/staging/lustre/lnet/lnet/router.c2
-rw-r--r--drivers/staging/lustre/lnet/selftest/brw_test.c6
-rw-r--r--drivers/staging/lustre/lnet/selftest/conctl.c2
-rw-r--r--drivers/staging/lustre/lnet/selftest/conrpc.c4
-rw-r--r--drivers/staging/lustre/lnet/selftest/console.c88
-rw-r--r--drivers/staging/lustre/lnet/selftest/framework.c8
-rw-r--r--drivers/staging/lustre/lnet/selftest/rpc.c25
-rw-r--r--drivers/staging/lustre/lnet/selftest/selftest.h16
-rw-r--r--drivers/staging/lustre/lustre/fid/fid_internal.h2
-rw-r--r--drivers/staging/lustre/lustre/fid/fid_request.c25
-rw-r--r--drivers/staging/lustre/lustre/fid/lproc_fid.c2
-rw-r--r--drivers/staging/lustre/lustre/fld/fld_cache.c35
-rw-r--r--drivers/staging/lustre/lustre/fld/fld_internal.h11
-rw-r--r--drivers/staging/lustre/lustre/fld/fld_request.c4
-rw-r--r--drivers/staging/lustre/lustre/fld/lproc_fld.c2
-rw-r--r--drivers/staging/lustre/lustre/include/cl_object.h3
-rw-r--r--drivers/staging/lustre/lustre/include/lprocfs_status.h5
-rw-r--r--drivers/staging/lustre/lustre/include/lu_object.h2
-rw-r--r--drivers/staging/lustre/lustre/include/lustre/ll_fiemap.h2
-rw-r--r--drivers/staging/lustre/lustre/include/lustre/lustre_idl.h2
-rw-r--r--drivers/staging/lustre/lustre/include/lustre/lustre_user.h2
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_disk.h1
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_dlm.h4
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_eacl.h2
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_export.h2
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_fid.h2
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_fld.h2
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_ha.h2
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_log.h5
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_mds.h6
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_net.h2
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_param.h2
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_req_layout.h2
-rw-r--r--drivers/staging/lustre/lustre/include/obd.h324
-rw-r--r--drivers/staging/lustre/lustre/include/obd_cksum.h23
-rw-r--r--drivers/staging/lustre/lustre/include/obd_class.h18
-rw-r--r--drivers/staging/lustre/lustre/include/obd_support.h2
-rw-r--r--drivers/staging/lustre/lustre/lclient/lcommon_cl.c6
-rw-r--r--drivers/staging/lustre/lustre/ldlm/interval_tree.c16
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_extent.c2
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_internal.h2
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_lib.c2
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_lock.c2
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_lockd.c2
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_pool.c49
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_request.c2
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_resource.c4
-rw-r--r--drivers/staging/lustre/lustre/libcfs/debug.c23
-rw-r--r--drivers/staging/lustre/lustre/libcfs/fail.c4
-rw-r--r--drivers/staging/lustre/lustre/libcfs/hash.c383
-rw-r--r--drivers/staging/lustre/lustre/libcfs/kernel_user_comm.c3
-rw-r--r--drivers/staging/lustre/lustre/libcfs/libcfs_lock.c2
-rw-r--r--drivers/staging/lustre/lustre/libcfs/libcfs_mem.c2
-rw-r--r--drivers/staging/lustre/lustre/libcfs/libcfs_string.c2
-rw-r--r--drivers/staging/lustre/lustre/libcfs/linux/linux-cpu.c33
-rw-r--r--drivers/staging/lustre/lustre/libcfs/linux/linux-crypto-adler.c9
-rw-r--r--drivers/staging/lustre/lustre/libcfs/linux/linux-curproc.c2
-rw-r--r--drivers/staging/lustre/lustre/libcfs/linux/linux-debug.c1
-rw-r--r--drivers/staging/lustre/lustre/libcfs/linux/linux-tracefile.h48
-rw-r--r--drivers/staging/lustre/lustre/libcfs/module.c25
-rw-r--r--drivers/staging/lustre/lustre/libcfs/tracefile.c27
-rw-r--r--drivers/staging/lustre/lustre/libcfs/tracefile.h29
-rw-r--r--drivers/staging/lustre/lustre/libcfs/workitem.c58
-rw-r--r--drivers/staging/lustre/lustre/llite/dcache.c2
-rw-r--r--drivers/staging/lustre/lustre/llite/dir.c13
-rw-r--r--drivers/staging/lustre/lustre/llite/file.c4
-rw-r--r--drivers/staging/lustre/lustre/llite/llite_internal.h16
-rw-r--r--drivers/staging/lustre/lustre/llite/llite_lib.c2
-rw-r--r--drivers/staging/lustre/lustre/llite/llite_mmap.c2
-rw-r--r--drivers/staging/lustre/lustre/llite/lloop.c2
-rw-r--r--drivers/staging/lustre/lustre/llite/lproc_llite.c2
-rw-r--r--drivers/staging/lustre/lustre/llite/namei.c7
-rw-r--r--drivers/staging/lustre/lustre/llite/rw.c14
-rw-r--r--drivers/staging/lustre/lustre/llite/rw26.c5
-rw-r--r--drivers/staging/lustre/lustre/llite/statahead.c78
-rw-r--r--drivers/staging/lustre/lustre/llite/super25.c5
-rw-r--r--drivers/staging/lustre/lustre/llite/symlink.c26
-rw-r--r--drivers/staging/lustre/lustre/llite/vvp_dev.c2
-rw-r--r--drivers/staging/lustre/lustre/llite/vvp_internal.h2
-rw-r--r--drivers/staging/lustre/lustre/llite/vvp_io.c4
-rw-r--r--drivers/staging/lustre/lustre/llite/vvp_lock.c2
-rw-r--r--drivers/staging/lustre/lustre/llite/vvp_object.c2
-rw-r--r--drivers/staging/lustre/lustre/llite/vvp_page.c2
-rw-r--r--drivers/staging/lustre/lustre/llite/xattr.c11
-rw-r--r--drivers/staging/lustre/lustre/llite/xattr_cache.c2
-rw-r--r--drivers/staging/lustre/lustre/lmv/lmv_intent.c23
-rw-r--r--drivers/staging/lustre/lustre/lmv/lmv_internal.h16
-rw-r--r--drivers/staging/lustre/lustre/lmv/lmv_obd.c96
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_cl_internal.h2
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_dev.c2
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_ea.c2
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_internal.h17
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_io.c2
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_merge.c2
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_obd.c62
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_object.c2
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_offset.c2
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_pack.c17
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_page.c2
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_pool.c3
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_request.c18
-rw-r--r--drivers/staging/lustre/lustre/lov/lovsub_dev.c2
-rw-r--r--drivers/staging/lustre/lustre/lov/lovsub_object.c2
-rw-r--r--drivers/staging/lustre/lustre/lov/lproc_lov.c2
-rw-r--r--drivers/staging/lustre/lustre/mdc/lproc_mdc.c79
-rw-r--r--drivers/staging/lustre/lustre/mdc/mdc_internal.h17
-rw-r--r--drivers/staging/lustre/lustre/mdc/mdc_lib.c2
-rw-r--r--drivers/staging/lustre/lustre/mdc/mdc_locks.c2
-rw-r--r--drivers/staging/lustre/lustre/mdc/mdc_reint.c2
-rw-r--r--drivers/staging/lustre/lustre/mdc/mdc_request.c141
-rw-r--r--drivers/staging/lustre/lustre/mgc/mgc_request.c36
-rw-r--r--drivers/staging/lustre/lustre/obdclass/acl.c9
-rw-r--r--drivers/staging/lustre/lustre/obdclass/cl_io.c28
-rw-r--r--drivers/staging/lustre/lustre/obdclass/cl_lock.c3
-rw-r--r--drivers/staging/lustre/lustre/obdclass/cl_object.c5
-rw-r--r--drivers/staging/lustre/lustre/obdclass/cl_page.c2
-rw-r--r--drivers/staging/lustre/lustre/obdclass/class_obd.c4
-rw-r--r--drivers/staging/lustre/lustre/obdclass/genops.c8
-rw-r--r--drivers/staging/lustre/lustre/obdclass/linux/linux-sysctl.c2
-rw-r--r--drivers/staging/lustre/lustre/obdclass/llog.c2
-rw-r--r--drivers/staging/lustre/lustre/obdclass/llog_cat.c2
-rw-r--r--drivers/staging/lustre/lustre/obdclass/llog_internal.h2
-rw-r--r--drivers/staging/lustre/lustre/obdclass/llog_obd.c2
-rw-r--r--drivers/staging/lustre/lustre/obdclass/llog_swab.c2
-rw-r--r--drivers/staging/lustre/lustre/obdclass/lprocfs_status.c23
-rw-r--r--drivers/staging/lustre/lustre/obdclass/lu_object.c4
-rw-r--r--drivers/staging/lustre/lustre/obdclass/obd_config.c2
-rw-r--r--drivers/staging/lustre/lustre/obdclass/obd_mount.c12
-rw-r--r--drivers/staging/lustre/lustre/obdecho/echo_client.c38
-rw-r--r--drivers/staging/lustre/lustre/osc/lproc_osc.c2
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_cache.c12
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_cl_internal.h2
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_dev.c2
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_internal.h8
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_io.c2
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_lock.c2
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_object.c6
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_page.c2
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_quota.c2
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_request.c73
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/client.c2
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/events.c2
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/import.c2
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/layout.c2
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/llog_client.c2
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/lproc_ptlrpc.c8
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/niobuf.c2
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/pers.c2
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/pinger.c2
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/ptlrpc_internal.h5
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/ptlrpc_module.c2
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/ptlrpcd.c3
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/recover.c2
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/sec_bulk.c2
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/sec_config.c5
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/sec_plain.c2
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/service.c2
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/wiretest.c2
-rw-r--r--drivers/staging/media/bcm2048/radio-bcm2048.c397
-rw-r--r--drivers/staging/media/davinci_vpfe/Kconfig2
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_ipipe.c16
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c2
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_ipipeif.c17
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_isif.c17
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_resizer.c46
-rw-r--r--drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c12
-rw-r--r--drivers/staging/media/davinci_vpfe/vpfe_video.c62
-rw-r--r--drivers/staging/media/davinci_vpfe/vpfe_video.h1
-rw-r--r--drivers/staging/media/lirc/lirc_imon.c2
-rw-r--r--drivers/staging/media/lirc/lirc_parallel.c35
-rw-r--r--drivers/staging/media/lirc/lirc_sasem.c20
-rw-r--r--drivers/staging/media/lirc/lirc_serial.c50
-rw-r--r--drivers/staging/media/omap4iss/Kconfig2
-rw-r--r--drivers/staging/media/omap4iss/iss.c203
-rw-r--r--drivers/staging/media/omap4iss/iss.h10
-rw-r--r--drivers/staging/media/omap4iss/iss_csi2.c54
-rw-r--r--drivers/staging/media/omap4iss/iss_csi2.h1
-rw-r--r--drivers/staging/media/omap4iss/iss_csiphy.h2
-rw-r--r--drivers/staging/media/omap4iss/iss_ipipe.c11
-rw-r--r--drivers/staging/media/omap4iss/iss_ipipeif.c44
-rw-r--r--drivers/staging/media/omap4iss/iss_ipipeif.h1
-rw-r--r--drivers/staging/media/omap4iss/iss_resizer.c46
-rw-r--r--drivers/staging/media/omap4iss/iss_resizer.h1
-rw-r--r--drivers/staging/media/omap4iss/iss_video.c64
-rw-r--r--drivers/staging/media/omap4iss/iss_video.h5
-rw-r--r--drivers/staging/most/aim-network/networking.h2
-rw-r--r--drivers/staging/most/hdm-dim2/dim2_errors.h2
-rw-r--r--drivers/staging/most/hdm-dim2/dim2_hal.c133
-rw-r--r--drivers/staging/most/hdm-dim2/dim2_hal.h53
-rw-r--r--drivers/staging/most/hdm-dim2/dim2_hdm.c60
-rw-r--r--drivers/staging/most/hdm-dim2/dim2_reg.h5
-rw-r--r--drivers/staging/most/hdm-dim2/dim2_sysfs.h3
-rw-r--r--drivers/staging/most/mostcore/core.c3
-rw-r--r--drivers/staging/most/mostcore/mostcore.h2
-rw-r--r--drivers/staging/mt29f_spinand/mt29f_spinand.c24
-rw-r--r--drivers/staging/netlogic/xlr_net.c36
-rw-r--r--drivers/staging/nvec/README2
-rw-r--r--drivers/staging/nvec/nvec.c20
-rw-r--r--drivers/staging/octeon/ethernet-defines.h2
-rw-r--r--drivers/staging/octeon/ethernet-rgmii.c6
-rw-r--r--drivers/staging/rdma/amso1100/c2.c17
-rw-r--r--drivers/staging/rdma/amso1100/c2.h80
-rw-r--r--drivers/staging/rdma/amso1100/c2_mq.h14
-rw-r--r--drivers/staging/rdma/amso1100/c2_provider.c10
-rw-r--r--drivers/staging/rdma/amso1100/c2_rnic.c5
-rw-r--r--drivers/staging/rdma/amso1100/c2_vq.h20
-rw-r--r--drivers/staging/rdma/ehca/ehca_av.c6
-rw-r--r--drivers/staging/rdma/ehca/ehca_cq.c3
-rw-r--r--drivers/staging/rdma/ehca/ehca_main.c3
-rw-r--r--drivers/staging/rdma/ehca/ehca_mrmw.c6
-rw-r--r--drivers/staging/rdma/ehca/ehca_pd.c3
-rw-r--r--drivers/staging/rdma/ehca/ehca_qp.c3
-rw-r--r--drivers/staging/rdma/hfi1/Makefile2
-rw-r--r--drivers/staging/rdma/hfi1/chip.c3550
-rw-r--r--drivers/staging/rdma/hfi1/chip.h273
-rw-r--r--drivers/staging/rdma/hfi1/chip_registers.h4
-rw-r--r--drivers/staging/rdma/hfi1/common.h15
-rw-r--r--drivers/staging/rdma/hfi1/diag.c499
-rw-r--r--drivers/staging/rdma/hfi1/driver.c215
-rw-r--r--drivers/staging/rdma/hfi1/efivar.c169
-rw-r--r--drivers/staging/rdma/hfi1/efivar.h60
-rw-r--r--drivers/staging/rdma/hfi1/eprom.c119
-rw-r--r--drivers/staging/rdma/hfi1/file_ops.c207
-rw-r--r--drivers/staging/rdma/hfi1/firmware.c193
-rw-r--r--drivers/staging/rdma/hfi1/hfi.h104
-rw-r--r--drivers/staging/rdma/hfi1/init.c101
-rw-r--r--drivers/staging/rdma/hfi1/iowait.h6
-rw-r--r--drivers/staging/rdma/hfi1/mad.c227
-rw-r--r--drivers/staging/rdma/hfi1/mad.h114
-rw-r--r--drivers/staging/rdma/hfi1/pcie.c36
-rw-r--r--drivers/staging/rdma/hfi1/pio.c68
-rw-r--r--drivers/staging/rdma/hfi1/pio.h2
-rw-r--r--drivers/staging/rdma/hfi1/pio_copy.c6
-rw-r--r--drivers/staging/rdma/hfi1/qp.c48
-rw-r--r--drivers/staging/rdma/hfi1/qp.h46
-rw-r--r--drivers/staging/rdma/hfi1/qsfp.c5
-rw-r--r--drivers/staging/rdma/hfi1/rc.c81
-rw-r--r--drivers/staging/rdma/hfi1/ruc.c55
-rw-r--r--drivers/staging/rdma/hfi1/sdma.c67
-rw-r--r--drivers/staging/rdma/hfi1/sdma.h15
-rw-r--r--drivers/staging/rdma/hfi1/trace.c29
-rw-r--r--drivers/staging/rdma/hfi1/trace.h3
-rw-r--r--drivers/staging/rdma/hfi1/uc.c15
-rw-r--r--drivers/staging/rdma/hfi1/ud.c22
-rw-r--r--drivers/staging/rdma/hfi1/user_exp_rcv.h74
-rw-r--r--drivers/staging/rdma/hfi1/user_pages.c97
-rw-r--r--drivers/staging/rdma/hfi1/user_sdma.c363
-rw-r--r--drivers/staging/rdma/hfi1/user_sdma.h12
-rw-r--r--drivers/staging/rdma/hfi1/verbs.c130
-rw-r--r--drivers/staging/rdma/hfi1/verbs.h50
-rw-r--r--drivers/staging/rdma/ipath/ipath_file_ops.c8
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_ap.c54
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_cmd.c100
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_efuse.c4
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_ioctl_set.c7
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_mlme.c22
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_mlme_ext.c84
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_pwrctrl.c14
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_xmit.c11
-rw-r--r--drivers/staging/rtl8188eu/hal/fw.c40
-rw-r--r--drivers/staging/rtl8188eu/hal/hal_com.c14
-rw-r--r--drivers/staging/rtl8188eu/hal/rtl8188e_dm.c2
-rw-r--r--drivers/staging/rtl8188eu/hal/rtl8188e_hal_init.c7
-rw-r--r--drivers/staging/rtl8188eu/hal/rtl8188eu_xmit.c34
-rw-r--r--drivers/staging/rtl8188eu/hal/usb_halinit.c10
-rw-r--r--drivers/staging/rtl8188eu/include/HalVerDef.h33
-rw-r--r--drivers/staging/rtl8188eu/include/osdep_service.h2
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_xmit.h1
-rw-r--r--drivers/staging/rtl8188eu/os_dep/os_intfs.c10
-rw-r--r--drivers/staging/rtl8188eu/os_dep/osdep_service.c6
-rw-r--r--drivers/staging/rtl8188eu/os_dep/usb_intf.c8
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c5
-rw-r--r--drivers/staging/rtl8192u/r8192U_core.c15
-rw-r--r--drivers/staging/rtl8192u/r819xU_phy.c25
-rw-r--r--drivers/staging/rtl8712/rtl871x_mlme.c8
-rw-r--r--drivers/staging/rtl8723au/core/rtw_efuse.c45
-rw-r--r--drivers/staging/sm750fb/modedb.h233
-rw-r--r--drivers/staging/sm750fb/sm750.c2
-rw-r--r--drivers/staging/unisys/include/channel.h114
-rw-r--r--drivers/staging/unisys/include/iochannel.h157
-rw-r--r--drivers/staging/unisys/include/vbushelper.h3
-rw-r--r--drivers/staging/unisys/include/visorbus.h4
-rw-r--r--drivers/staging/unisys/visorbus/Kconfig7
-rw-r--r--drivers/staging/unisys/visorbus/controlvmcompletionstatus.h24
-rw-r--r--drivers/staging/unisys/visorbus/periodic_work.c11
-rw-r--r--drivers/staging/unisys/visorbus/vbuschannel.h9
-rw-r--r--drivers/staging/unisys/visorbus/visorbus_main.c15
-rw-r--r--drivers/staging/unisys/visorbus/visorchannel.c4
-rw-r--r--drivers/staging/unisys/visorbus/vmcallinterface.h34
-rw-r--r--drivers/staging/unisys/visorhba/visorhba_main.c16
-rw-r--r--drivers/staging/unisys/visorinput/Kconfig7
-rw-r--r--drivers/staging/unisys/visorinput/visorinput.c4
-rw-r--r--drivers/staging/unisys/visornic/visornic_main.c1
-rw-r--r--drivers/staging/vme/devices/vme_pio2_cntr.c2
-rw-r--r--drivers/staging/vme/devices/vme_pio2_core.c22
-rw-r--r--drivers/staging/vme/devices/vme_pio2_gpio.c32
-rw-r--r--drivers/staging/vme/devices/vme_user.c22
-rw-r--r--drivers/staging/vme/devices/vme_user.h2
-rw-r--r--drivers/staging/vt6656/baseband.c4
-rw-r--r--drivers/staging/vt6656/baseband.h4
-rw-r--r--drivers/staging/vt6656/card.c3
-rw-r--r--drivers/staging/vt6656/card.h3
-rw-r--r--drivers/staging/vt6656/channel.c4
-rw-r--r--drivers/staging/vt6656/channel.h4
-rw-r--r--drivers/staging/vt6656/desc.h3
-rw-r--r--drivers/staging/vt6656/device.h3
-rw-r--r--drivers/staging/vt6656/dpc.c3
-rw-r--r--drivers/staging/vt6656/dpc.h3
-rw-r--r--drivers/staging/vt6656/firmware.c4
-rw-r--r--drivers/staging/vt6656/firmware.h4
-rw-r--r--drivers/staging/vt6656/int.c4
-rw-r--r--drivers/staging/vt6656/int.h4
-rw-r--r--drivers/staging/vt6656/key.c5
-rw-r--r--drivers/staging/vt6656/key.h4
-rw-r--r--drivers/staging/vt6656/mac.c4
-rw-r--r--drivers/staging/vt6656/mac.h4
-rw-r--r--drivers/staging/vt6656/main_usb.c3
-rw-r--r--drivers/staging/vt6656/power.c4
-rw-r--r--drivers/staging/vt6656/power.h3
-rw-r--r--drivers/staging/vt6656/rf.c4
-rw-r--r--drivers/staging/vt6656/rf.h4
-rw-r--r--drivers/staging/vt6656/rxtx.c3
-rw-r--r--drivers/staging/vt6656/rxtx.h3
-rw-r--r--drivers/staging/vt6656/usbpipe.c4
-rw-r--r--drivers/staging/vt6656/usbpipe.h4
-rw-r--r--drivers/staging/vt6656/wcmd.c3
-rw-r--r--drivers/staging/vt6656/wcmd.h3
-rw-r--r--drivers/staging/wilc1000/Kconfig46
-rw-r--r--drivers/staging/wilc1000/Makefile22
-rw-r--r--drivers/staging/wilc1000/coreconfigurator.c34
-rw-r--r--drivers/staging/wilc1000/coreconfigurator.h24
-rw-r--r--drivers/staging/wilc1000/host_interface.c2360
-rw-r--r--drivers/staging/wilc1000/host_interface.h1034
-rw-r--r--drivers/staging/wilc1000/linux_mon.c13
-rw-r--r--drivers/staging/wilc1000/linux_wlan.c1252
-rw-r--r--drivers/staging/wilc1000/linux_wlan_common.h24
-rw-r--r--drivers/staging/wilc1000/linux_wlan_sdio.c251
-rw-r--r--drivers/staging/wilc1000/linux_wlan_sdio.h14
-rw-r--r--drivers/staging/wilc1000/linux_wlan_spi.c409
-rw-r--r--drivers/staging/wilc1000/linux_wlan_spi.h14
-rw-r--r--drivers/staging/wilc1000/wilc_debugfs.c26
-rw-r--r--drivers/staging/wilc1000/wilc_msgqueue.c9
-rw-r--r--drivers/staging/wilc1000/wilc_msgqueue.h12
-rw-r--r--drivers/staging/wilc1000/wilc_sdio.c717
-rw-r--r--drivers/staging/wilc1000/wilc_spi.c714
-rw-r--r--drivers/staging/wilc1000/wilc_wfi_cfgoperations.c2061
-rw-r--r--drivers/staging/wilc1000/wilc_wfi_cfgoperations.h87
-rw-r--r--drivers/staging/wilc1000/wilc_wfi_netdevice.h80
-rw-r--r--drivers/staging/wilc1000/wilc_wlan.c1542
-rw-r--r--drivers/staging/wilc1000/wilc_wlan.h384
-rw-r--r--drivers/staging/wilc1000/wilc_wlan_cfg.c25
-rw-r--r--drivers/staging/wilc1000/wilc_wlan_cfg.h4
-rw-r--r--drivers/staging/wilc1000/wilc_wlan_if.h38
-rw-r--r--drivers/staging/wlan-ng/hfa384x_usb.c34
-rw-r--r--drivers/staging/wlan-ng/p80211netdev.h1
-rw-r--r--drivers/staging/wlan-ng/prism2mgmt.h1
-rw-r--r--drivers/staging/wlan-ng/prism2mib.c5
-rw-r--r--drivers/staging/wlan-ng/prism2sta.c21
-rw-r--r--drivers/target/iscsi/iscsi_target.c13
-rw-r--r--drivers/target/iscsi/iscsi_target_nego.c1
-rw-r--r--drivers/target/iscsi/iscsi_target_parameters.c10
-rw-r--r--drivers/target/target_core_sbc.c17
-rw-r--r--drivers/target/target_core_stat.c2
-rw-r--r--drivers/target/target_core_tmr.c7
-rw-r--r--drivers/target/target_core_transport.c26
-rw-r--r--drivers/target/target_core_user.c4
-rw-r--r--drivers/thermal/Kconfig2
-rw-r--r--drivers/thermal/imx_thermal.c56
-rw-r--r--drivers/thermal/intel_quark_dts_thermal.c61
-rw-r--r--drivers/thermal/intel_soc_dts_iosf.c43
-rw-r--r--drivers/thermal/of-thermal.c2
-rw-r--r--drivers/thermal/power_allocator.c24
-rw-r--r--drivers/thermal/rcar_thermal.c49
-rw-r--r--drivers/thermal/rockchip_thermal.c328
-rw-r--r--drivers/tty/amiserial.c1
-rw-r--r--drivers/tty/cyclades.c8
-rw-r--r--drivers/tty/isicom.c4
-rw-r--r--drivers/tty/moxa.c1
-rw-r--r--drivers/tty/n_tty.c77
-rw-r--r--drivers/tty/pty.c2
-rw-r--r--drivers/tty/serial/68328serial.c130
-rw-r--r--drivers/tty/serial/8250/8250_core.c7
-rw-r--r--drivers/tty/serial/8250/8250_early.c29
-rw-r--r--drivers/tty/serial/8250/8250_ingenic.c2
-rw-r--r--drivers/tty/serial/8250/8250_mtk.c35
-rw-r--r--drivers/tty/serial/8250/8250_of.c (renamed from drivers/tty/serial/of_serial.c)38
-rw-r--r--drivers/tty/serial/8250/8250_port.c20
-rw-r--r--drivers/tty/serial/8250/8250_uniphier.c32
-rw-r--r--drivers/tty/serial/8250/Kconfig14
-rw-r--r--drivers/tty/serial/8250/Makefile1
-rw-r--r--drivers/tty/serial/Kconfig33
-rw-r--r--drivers/tty/serial/Makefile2
-rw-r--r--drivers/tty/serial/amba-pl011.c341
-rw-r--r--drivers/tty/serial/amba-pl011.h34
-rw-r--r--drivers/tty/serial/atmel_serial.c187
-rw-r--r--drivers/tty/serial/bcm63xx_uart.c2
-rw-r--r--drivers/tty/serial/bfin_uart.c27
-rw-r--r--drivers/tty/serial/earlycon.c17
-rw-r--r--drivers/tty/serial/icom.c1
-rw-r--r--drivers/tty/serial/imx.c189
-rw-r--r--drivers/tty/serial/jsm/jsm_driver.c4
-rw-r--r--drivers/tty/serial/jsm/jsm_neo.c2
-rw-r--r--drivers/tty/serial/m32r_sio.c2
-rw-r--r--drivers/tty/serial/men_z135_uart.c2
-rw-r--r--drivers/tty/serial/meson_uart.c80
-rw-r--r--drivers/tty/serial/nwpserial.c477
-rw-r--r--drivers/tty/serial/omap-serial.c2
-rw-r--r--drivers/tty/serial/pxa.c2
-rw-r--r--drivers/tty/serial/sc16is7xx.c13
-rw-r--r--drivers/tty/serial/serial_core.c11
-rw-r--r--drivers/tty/serial/serial_mctrl_gpio.c2
-rw-r--r--drivers/tty/serial/sh-sci.c591
-rw-r--r--drivers/tty/serial/sh-sci.h10
-rw-r--r--drivers/tty/serial/sprd_serial.c2
-rw-r--r--drivers/tty/serial/sunhv.c12
-rw-r--r--drivers/tty/serial/sunsu.c2
-rw-r--r--drivers/tty/serial/vt8500_serial.c2
-rw-r--r--drivers/tty/synclink_gt.c4
-rw-r--r--drivers/tty/sysrq.c6
-rw-r--r--drivers/tty/tty_buffer.c2
-rw-r--r--drivers/tty/tty_io.c88
-rw-r--r--drivers/tty/tty_ioctl.c21
-rw-r--r--drivers/tty/tty_ldisc.c51
-rw-r--r--drivers/tty/tty_ldsem.c4
-rw-r--r--drivers/tty/tty_mutex.c10
-rw-r--r--drivers/tty/tty_port.c9
-rw-r--r--drivers/tty/vt/vt.c2
-rw-r--r--drivers/usb/chipidea/Kconfig5
-rw-r--r--drivers/usb/chipidea/Makefile5
-rw-r--r--drivers/usb/chipidea/ci.h3
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c6
-rw-r--r--drivers/usb/chipidea/ci_hdrc_msm.c3
-rw-r--r--drivers/usb/chipidea/core.c59
-rw-r--r--drivers/usb/chipidea/debug.c1
-rw-r--r--drivers/usb/chipidea/debug.h30
-rw-r--r--drivers/usb/chipidea/host.c2
-rw-r--r--drivers/usb/chipidea/otg_fsm.c26
-rw-r--r--drivers/usb/chipidea/udc.c31
-rw-r--r--drivers/usb/class/cdc-acm.c5
-rw-r--r--drivers/usb/common/common.c60
-rw-r--r--drivers/usb/core/config.c3
-rw-r--r--drivers/usb/core/devices.c26
-rw-r--r--drivers/usb/core/devio.c51
-rw-r--r--drivers/usb/core/hcd.c4
-rw-r--r--drivers/usb/core/hub.c112
-rw-r--r--drivers/usb/core/hub.h5
-rw-r--r--drivers/usb/core/port.c93
-rw-r--r--drivers/usb/core/quirks.c9
-rw-r--r--drivers/usb/core/sysfs.c31
-rw-r--r--drivers/usb/core/usb.c5
-rw-r--r--drivers/usb/dwc2/core.c429
-rw-r--r--drivers/usb/dwc2/core.h66
-rw-r--r--drivers/usb/dwc2/core_intr.c53
-rw-r--r--drivers/usb/dwc2/gadget.c109
-rw-r--r--drivers/usb/dwc2/hcd.c128
-rw-r--r--drivers/usb/dwc2/hcd.h31
-rw-r--r--drivers/usb/dwc2/hcd_ddma.c240
-rw-r--r--drivers/usb/dwc2/hcd_intr.c39
-rw-r--r--drivers/usb/dwc2/hcd_queue.c2
-rw-r--r--drivers/usb/dwc2/hw.h4
-rw-r--r--drivers/usb/dwc2/platform.c229
-rw-r--r--drivers/usb/dwc3/Kconfig17
-rw-r--r--drivers/usb/dwc3/Makefile2
-rw-r--r--drivers/usb/dwc3/core.c8
-rw-r--r--drivers/usb/dwc3/core.h3
-rw-r--r--drivers/usb/dwc3/dwc3-of-simple.c180
-rw-r--r--drivers/usb/dwc3/dwc3-qcom.c130
-rw-r--r--drivers/usb/dwc3/ep0.c12
-rw-r--r--drivers/usb/dwc3/gadget.c134
-rw-r--r--drivers/usb/dwc3/trace.h11
-rw-r--r--drivers/usb/gadget/Kconfig6
-rw-r--r--drivers/usb/gadget/configfs.c29
-rw-r--r--drivers/usb/gadget/function/f_fs.c6
-rw-r--r--drivers/usb/gadget/function/f_midi.c193
-rw-r--r--drivers/usb/gadget/function/f_sourcesink.c149
-rw-r--r--drivers/usb/gadget/function/g_zero.h7
-rw-r--r--drivers/usb/gadget/function/u_ether.c18
-rw-r--r--drivers/usb/gadget/function/u_serial.c258
-rw-r--r--drivers/usb/gadget/function/uvc_configfs.c2
-rw-r--r--drivers/usb/gadget/function/uvc_queue.c4
-rw-r--r--drivers/usb/gadget/legacy/acm_ms.c2
-rw-r--r--drivers/usb/gadget/legacy/audio.c2
-rw-r--r--drivers/usb/gadget/legacy/cdc2.c2
-rw-r--r--drivers/usb/gadget/legacy/ether.c2
-rw-r--r--drivers/usb/gadget/legacy/g_ffs.c2
-rw-r--r--drivers/usb/gadget/legacy/gmidi.c12
-rw-r--r--drivers/usb/gadget/legacy/hid.c2
-rw-r--r--drivers/usb/gadget/legacy/inode.c3
-rw-r--r--drivers/usb/gadget/legacy/mass_storage.c2
-rw-r--r--drivers/usb/gadget/legacy/multi.c2
-rw-r--r--drivers/usb/gadget/legacy/ncm.c2
-rw-r--r--drivers/usb/gadget/legacy/nokia.c2
-rw-r--r--drivers/usb/gadget/legacy/printer.c2
-rw-r--r--drivers/usb/gadget/legacy/serial.c2
-rw-r--r--drivers/usb/gadget/legacy/tcm_usb_gadget.c2
-rw-r--r--drivers/usb/gadget/legacy/webcam.c2
-rw-r--r--drivers/usb/gadget/legacy/zero.c14
-rw-r--r--drivers/usb/gadget/u_f.c1
-rw-r--r--drivers/usb/gadget/u_f.h10
-rw-r--r--drivers/usb/gadget/udc/Kconfig11
-rw-r--r--drivers/usb/gadget/udc/Makefile1
-rw-r--r--drivers/usb/gadget/udc/bcm63xx_udc.c7
-rw-r--r--drivers/usb/gadget/udc/gr_udc.c3
-rw-r--r--drivers/usb/gadget/udc/lpc32xx_udc.c35
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.c3
-rw-r--r--drivers/usb/gadget/udc/renesas_usb3.c1975
-rw-r--r--drivers/usb/gadget/udc/s3c-hsudc.c2
-rw-r--r--drivers/usb/gadget/udc/udc-core.c79
-rw-r--r--drivers/usb/host/Kconfig9
-rw-r--r--drivers/usb/host/Makefile4
-rw-r--r--drivers/usb/host/bcma-hcd.c21
-rw-r--r--drivers/usb/host/ehci-dbg.c15
-rw-r--r--drivers/usb/host/ehci-hcd.c2
-rw-r--r--drivers/usb/host/ehci-mem.c18
-rw-r--r--drivers/usb/host/ehci-msm.c4
-rw-r--r--drivers/usb/host/ehci-q.c9
-rw-r--r--drivers/usb/host/ehci.h1
-rw-r--r--drivers/usb/host/fhci-tds.c2
-rw-r--r--drivers/usb/host/ohci-at91.c11
-rw-r--r--drivers/usb/host/ohci-hcd.c4
-rw-r--r--drivers/usb/host/ohci-pxa27x.c14
-rw-r--r--drivers/usb/host/oxu210hp-hcd.c8
-rw-r--r--drivers/usb/host/pci-quirks.c25
-rw-r--r--drivers/usb/host/u132-hcd.c2
-rw-r--r--drivers/usb/host/uhci-debug.c23
-rw-r--r--drivers/usb/host/uhci-q.c3
-rw-r--r--drivers/usb/host/whci/qset.c9
-rw-r--r--drivers/usb/host/xhci-ext-caps.h83
-rw-r--r--drivers/usb/host/xhci-hub.c49
-rw-r--r--drivers/usb/host/xhci-mem.c81
-rw-r--r--drivers/usb/host/xhci-mtk-sch.c415
-rw-r--r--drivers/usb/host/xhci-mtk.c763
-rw-r--r--drivers/usb/host/xhci-mtk.h162
-rw-r--r--drivers/usb/host/xhci-pci.c9
-rw-r--r--drivers/usb/host/xhci-plat.c88
-rw-r--r--drivers/usb/host/xhci-plat.h39
-rw-r--r--drivers/usb/host/xhci-rcar.c44
-rw-r--r--drivers/usb/host/xhci-rcar.h3
-rw-r--r--drivers/usb/host/xhci-ring.c19
-rw-r--r--drivers/usb/host/xhci.c33
-rw-r--r--drivers/usb/host/xhci.h4
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.c16
-rw-r--r--drivers/usb/misc/usbtest.c233
-rw-r--r--drivers/usb/mon/mon_bin.c17
-rw-r--r--drivers/usb/mon/mon_main.c2
-rw-r--r--drivers/usb/mon/mon_text.c15
-rw-r--r--drivers/usb/musb/Kconfig2
-rw-r--r--drivers/usb/musb/musb_core.c56
-rw-r--r--drivers/usb/musb/musb_core.h2
-rw-r--r--drivers/usb/musb/musb_gadget.c5
-rw-r--r--drivers/usb/musb/omap2430.c30
-rw-r--r--drivers/usb/phy/Kconfig14
-rw-r--r--drivers/usb/phy/Makefile1
-rw-r--r--drivers/usb/phy/phy-am335x-control.c16
-rw-r--r--drivers/usb/phy/phy-am335x-control.h (renamed from drivers/usb/phy/am35x-phy-control.h)8
-rw-r--r--drivers/usb/phy/phy-am335x.c17
-rw-r--r--drivers/usb/phy/phy-msm-usb.c6
-rw-r--r--drivers/usb/phy/phy-mxs-usb.c5
-rw-r--r--drivers/usb/phy/phy-rcar-usb.c247
-rw-r--r--drivers/usb/phy/phy-twl6030-usb.c30
-rw-r--r--drivers/usb/renesas_usbhs/common.c78
-rw-r--r--drivers/usb/renesas_usbhs/mod_gadget.c30
-rw-r--r--drivers/usb/renesas_usbhs/mod_host.c11
-rw-r--r--drivers/usb/renesas_usbhs/pipe.c112
-rw-r--r--drivers/usb/renesas_usbhs/pipe.h1
-rw-r--r--drivers/usb/serial/Kconfig16
-rw-r--r--drivers/usb/serial/Makefile1
-rw-r--r--drivers/usb/serial/cp210x.c188
-rw-r--r--drivers/usb/serial/io_edgeport.c35
-rw-r--r--drivers/usb/serial/ipaq.c3
-rw-r--r--drivers/usb/serial/mos7840.c2
-rw-r--r--drivers/usb/serial/mxu11x0.c986
-rw-r--r--drivers/usb/serial/usb-serial-simple.c1
-rw-r--r--drivers/usb/storage/uas.c52
-rw-r--r--drivers/usb/storage/unusual_devs.h2
-rw-r--r--drivers/usb/storage/unusual_uas.h2
-rw-r--r--drivers/uwb/uwbd.c1
-rw-r--r--drivers/vfio/pci/vfio_pci.c2
-rw-r--r--drivers/vfio/platform/reset/vfio_platform_amdxgbe.c2
-rw-r--r--drivers/vfio/platform/vfio_platform.c1
-rw-r--r--drivers/vfio/platform/vfio_platform_common.c5
-rw-r--r--drivers/vfio/vfio.c64
-rw-r--r--drivers/vfio/vfio_iommu_type1.c2
-rw-r--r--drivers/vhost/vhost.c8
-rw-r--r--drivers/video/backlight/adp8860_bl.c18
-rw-r--r--drivers/video/backlight/adp8870_bl.c10
-rw-r--r--drivers/video/backlight/gpio_backlight.c10
-rw-r--r--drivers/video/backlight/pwm_bl.c28
-rw-r--r--drivers/video/backlight/tps65217_bl.c9
-rw-r--r--drivers/video/fbdev/fsl-diu-fb.c13
-rw-r--r--drivers/video/fbdev/omap2/dss/venc.c12
-rw-r--r--drivers/virtio/virtio.c1
-rw-r--r--drivers/virtio/virtio_ring.c48
-rw-r--r--drivers/watchdog/Kconfig2
-rw-r--r--drivers/watchdog/mtk_wdt.c1
-rw-r--r--drivers/watchdog/omap_wdt.c2
-rw-r--r--drivers/watchdog/pnx4008_wdt.c8
-rw-r--r--drivers/watchdog/tegra_wdt.c4
-rw-r--r--drivers/watchdog/w83977f_wdt.c2
-rw-r--r--drivers/xen/Makefile2
-rw-r--r--drivers/xen/acpi.c2
-rw-r--r--drivers/xen/efi.c30
-rw-r--r--drivers/xen/events/events_base.c5
-rw-r--r--drivers/xen/events/events_fifo.c23
-rw-r--r--drivers/xen/evtchn.c123
-rw-r--r--drivers/xen/gntdev.c207
-rw-r--r--drivers/xen/grant-table.c4
-rw-r--r--drivers/xen/pcpu.c8
-rw-r--r--drivers/xen/time.c88
-rw-r--r--drivers/xen/xen-acpi-cpuhotplug.c2
-rw-r--r--drivers/xen/xen-acpi-pad.c4
-rw-r--r--drivers/xen/xen-acpi-processor.c8
-rw-r--r--drivers/xen/xen-pciback/pciback.h1
-rw-r--r--drivers/xen/xen-pciback/pciback_ops.c75
-rw-r--r--drivers/xen/xen-pciback/xenbus.c4
-rw-r--r--drivers/xen/xen-scsiback.c2
-rw-r--r--drivers/xen/xenfs/xensyms.c4
-rw-r--r--firmware/WHENCE2
-rw-r--r--fs/9p/acl.c24
-rw-r--r--fs/9p/cache.c8
-rw-r--r--fs/9p/v9fs.c2
-rw-r--r--fs/9p/v9fs.h2
-rw-r--r--fs/9p/vfs_inode.c30
-rw-r--r--fs/9p/vfs_inode_dotl.c21
-rw-r--r--fs/9p/xattr.c4
-rw-r--r--fs/Kconfig10
-rw-r--r--fs/adfs/adfs.h6
-rw-r--r--fs/adfs/dir.c6
-rw-r--r--fs/adfs/dir_f.c2
-rw-r--r--fs/adfs/dir_fplus.c2
-rw-r--r--fs/adfs/super.c2
-rw-r--r--fs/affs/affs.h2
-rw-r--r--fs/affs/amigaffs.c13
-rw-r--r--fs/affs/inode.c1
-rw-r--r--fs/affs/namei.c1
-rw-r--r--fs/affs/super.c4
-rw-r--r--fs/affs/symlink.c9
-rw-r--r--fs/afs/inode.c1
-rw-r--r--fs/afs/proc.c25
-rw-r--r--fs/afs/super.c2
-rw-r--r--fs/autofs4/symlink.c14
-rw-r--r--fs/bad_inode.c2
-rw-r--r--fs/befs/linuxvfs.c42
-rw-r--r--fs/bfs/inode.c2
-rw-r--r--fs/block_dev.c147
-rw-r--r--fs/btrfs/acl.c8
-rw-r--r--fs/btrfs/backref.c2
-rw-r--r--fs/btrfs/ctree.h12
-rw-r--r--fs/btrfs/disk-io.c2
-rw-r--r--fs/btrfs/extent-tree.c133
-rw-r--r--fs/btrfs/file.c31
-rw-r--r--fs/btrfs/free-space-cache.c10
-rw-r--r--fs/btrfs/inode.c48
-rw-r--r--fs/btrfs/ioctl.c186
-rw-r--r--fs/btrfs/qgroup.c5
-rw-r--r--fs/btrfs/scrub.c62
-rw-r--r--fs/btrfs/super.c4
-rw-r--r--fs/btrfs/tests/free-space-tests.c4
-rw-r--r--fs/btrfs/transaction.c33
-rw-r--r--fs/btrfs/transaction.h6
-rw-r--r--fs/btrfs/volumes.c16
-rw-r--r--fs/btrfs/volumes.h2
-rw-r--r--fs/btrfs/xattr.c166
-rw-r--r--fs/btrfs/xattr.h2
-rw-r--r--fs/buffer.c21
-rw-r--r--fs/cachefiles/daemon.c12
-rw-r--r--fs/ceph/acl.c16
-rw-r--r--fs/ceph/inode.c2
-rw-r--r--fs/ceph/super.c4
-rw-r--r--fs/cifs/cifsfs.c66
-rw-r--r--fs/cifs/cifsfs.h6
-rw-r--r--fs/cifs/inode.c6
-rw-r--r--fs/cifs/ioctl.c126
-rw-r--r--fs/cifs/link.c10
-rw-r--r--fs/cifs/xattr.c16
-rw-r--r--fs/coda/cnode.c5
-rw-r--r--fs/coda/inode.c6
-rw-r--r--fs/coda/symlink.c4
-rw-r--r--fs/compat.c21
-rw-r--r--fs/compat_ioctl.c269
-rw-r--r--fs/configfs/configfs_internal.h14
-rw-r--r--fs/configfs/dir.c18
-rw-r--r--fs/configfs/file.c255
-rw-r--r--fs/configfs/inode.c2
-rw-r--r--fs/configfs/symlink.c22
-rw-r--r--fs/coredump.c8
-rw-r--r--fs/cramfs/inode.c1
-rw-r--r--fs/dcache.c21
-rw-r--r--fs/direct-io.c11
-rw-r--r--fs/dlm/lowcomms.c4
-rw-r--r--fs/dlm/user.c11
-rw-r--r--fs/ecryptfs/inode.c21
-rw-r--r--fs/ecryptfs/main.c6
-rw-r--r--fs/efs/inode.c1
-rw-r--r--fs/efs/super.c6
-rw-r--r--fs/efs/symlink.c4
-rw-r--r--fs/eventfd.c4
-rw-r--r--fs/exec.c4
-rw-r--r--fs/exofs/inode.c6
-rw-r--r--fs/exofs/namei.c1
-rw-r--r--fs/exofs/super.c4
-rw-r--r--fs/ext2/inode.c1
-rw-r--r--fs/ext2/namei.c1
-rw-r--r--fs/ext2/super.c2
-rw-r--r--fs/ext2/symlink.c5
-rw-r--r--fs/ext2/xattr.c21
-rw-r--r--fs/ext2/xattr_security.c21
-rw-r--r--fs/ext2/xattr_trusted.c23
-rw-r--r--fs/ext2/xattr_user.c23
-rw-r--r--fs/ext4/crypto.c2
-rw-r--r--fs/ext4/ext4.h51
-rw-r--r--fs/ext4/inode.c1
-rw-r--r--fs/ext4/namei.c1
-rw-r--r--fs/ext4/page-io.c5
-rw-r--r--fs/ext4/super.c2
-rw-r--r--fs/ext4/symlink.c31
-rw-r--r--fs/ext4/sysfs.c2
-rw-r--r--fs/ext4/xattr.c23
-rw-r--r--fs/ext4/xattr_security.c22
-rw-r--r--fs/ext4/xattr_trusted.c23
-rw-r--r--fs/ext4/xattr_user.c23
-rw-r--r--fs/f2fs/checkpoint.c177
-rw-r--r--fs/f2fs/data.c377
-rw-r--r--fs/f2fs/debug.c35
-rw-r--r--fs/f2fs/dir.c38
-rw-r--r--fs/f2fs/extent_cache.c122
-rw-r--r--fs/f2fs/f2fs.h148
-rw-r--r--fs/f2fs/file.c344
-rw-r--r--fs/f2fs/gc.c9
-rw-r--r--fs/f2fs/gc.h8
-rw-r--r--fs/f2fs/inline.c9
-rw-r--r--fs/f2fs/inode.c29
-rw-r--r--fs/f2fs/namei.c90
-rw-r--r--fs/f2fs/node.c170
-rw-r--r--fs/f2fs/node.h6
-rw-r--r--fs/f2fs/recovery.c40
-rw-r--r--fs/f2fs/segment.c122
-rw-r--r--fs/f2fs/shrinker.c3
-rw-r--r--fs/f2fs/super.c258
-rw-r--r--fs/f2fs/xattr.c95
-rw-r--r--fs/f2fs/xattr.h2
-rw-r--r--fs/fat/inode.c2
-rw-r--r--fs/fcntl.c3
-rw-r--r--fs/file.c13
-rw-r--r--fs/freevxfs/vxfs_inode.c1
-rw-r--r--fs/fuse/cuse.c2
-rw-r--r--fs/fuse/dir.c17
-rw-r--r--fs/fuse/file.c2
-rw-r--r--fs/fuse/inode.c4
-rw-r--r--fs/gfs2/acl.c4
-rw-r--r--fs/gfs2/acl.h2
-rw-r--r--fs/gfs2/aops.c2
-rw-r--r--fs/gfs2/bmap.c13
-rw-r--r--fs/gfs2/dir.c175
-rw-r--r--fs/gfs2/file.c34
-rw-r--r--fs/gfs2/glock.c18
-rw-r--r--fs/gfs2/glock.h26
-rw-r--r--fs/gfs2/incore.h23
-rw-r--r--fs/gfs2/inode.c67
-rw-r--r--fs/gfs2/log.c3
-rw-r--r--fs/gfs2/main.c19
-rw-r--r--fs/gfs2/meta_io.c82
-rw-r--r--fs/gfs2/meta_io.h2
-rw-r--r--fs/gfs2/ops_fstype.c10
-rw-r--r--fs/gfs2/quota.c117
-rw-r--r--fs/gfs2/quota.h2
-rw-r--r--fs/gfs2/rgrp.c57
-rw-r--r--fs/gfs2/rgrp.h6
-rw-r--r--fs/gfs2/super.c43
-rw-r--r--fs/gfs2/util.c2
-rw-r--r--fs/gfs2/util.h2
-rw-r--r--fs/gfs2/xattr.c60
-rw-r--r--fs/gfs2/xattr.h1
-rw-r--r--fs/hfs/mdb.c4
-rw-r--r--fs/hfs/super.c4
-rw-r--r--fs/hfsplus/inode.c2
-rw-r--r--fs/hfsplus/posix_acl.c8
-rw-r--r--fs/hfsplus/super.c2
-rw-r--r--fs/hfsplus/xattr.c12
-rw-r--r--fs/hostfs/hostfs_kern.c28
-rw-r--r--fs/hpfs/inode.c1
-rw-r--r--fs/hpfs/map.c2
-rw-r--r--fs/hpfs/namei.c5
-rw-r--r--fs/hpfs/super.c2
-rw-r--r--fs/hugetlbfs/inode.c32
-rw-r--r--fs/inode.c10
-rw-r--r--fs/internal.h9
-rw-r--r--fs/ioctl.c71
-rw-r--r--fs/isofs/inode.c3
-rw-r--r--fs/isofs/rock.c4
-rw-r--r--fs/jbd2/transaction.c18
-rw-r--r--fs/jffs2/security.c22
-rw-r--r--fs/jffs2/super.c2
-rw-r--r--fs/jffs2/symlink.c2
-rw-r--r--fs/jffs2/wbuf.c2
-rw-r--r--fs/jffs2/xattr.c26
-rw-r--r--fs/jffs2/xattr_trusted.c21
-rw-r--r--fs/jffs2/xattr_user.c20
-rw-r--r--fs/jfs/acl.c8
-rw-r--r--fs/jfs/inode.c1
-rw-r--r--fs/jfs/jfs_logmgr.c9
-rw-r--r--fs/jfs/namei.c1
-rw-r--r--fs/jfs/super.c2
-rw-r--r--fs/jfs/symlink.c5
-rw-r--r--fs/kernfs/dir.c55
-rw-r--r--fs/kernfs/inode.c4
-rw-r--r--fs/kernfs/symlink.c24
-rw-r--r--fs/libfs.c22
-rw-r--r--fs/lockd/svc.c79
-rw-r--r--fs/locks.c160
-rw-r--r--fs/logfs/Kconfig2
-rw-r--r--fs/logfs/dir.c9
-rw-r--r--fs/logfs/inode.c6
-rw-r--r--fs/logfs/logfs.h5
-rw-r--r--fs/logfs/readwrite.c4
-rw-r--r--fs/logfs/segment.c2
-rw-r--r--fs/minix/inode.c6
-rw-r--r--fs/minix/itree_v1.c9
-rw-r--r--fs/minix/itree_v2.c9
-rw-r--r--fs/namei.c327
-rw-r--r--fs/namespace.c49
-rw-r--r--fs/ncpfs/inode.c6
-rw-r--r--fs/nfs/callback_proc.c52
-rw-r--r--fs/nfs/dir.c26
-rw-r--r--fs/nfs/direct.c48
-rw-r--r--fs/nfs/file.c6
-rw-r--r--fs/nfs/filelayout/filelayout.c18
-rw-r--r--fs/nfs/flexfilelayout/flexfilelayout.c205
-rw-r--r--fs/nfs/flexfilelayout/flexfilelayout.h1
-rw-r--r--fs/nfs/flexfilelayout/flexfilelayoutdev.c16
-rw-r--r--fs/nfs/inode.c119
-rw-r--r--fs/nfs/internal.h41
-rw-r--r--fs/nfs/nfs3acl.c4
-rw-r--r--fs/nfs/nfs42proc.c32
-rw-r--r--fs/nfs/nfs4client.c2
-rw-r--r--fs/nfs/nfs4file.c120
-rw-r--r--fs/nfs/nfs4proc.c148
-rw-r--r--fs/nfs/nfs4sysctl.c2
-rw-r--r--fs/nfs/nfs4trace.c1
-rw-r--r--fs/nfs/nfs4trace.h431
-rw-r--r--fs/nfs/nfs4xdr.c1
-rw-r--r--fs/nfs/nfstrace.h1
-rw-r--r--fs/nfs/objlayout/objio_osd.c5
-rw-r--r--fs/nfs/pagelist.c126
-rw-r--r--fs/nfs/pnfs.c234
-rw-r--r--fs/nfs/pnfs.h54
-rw-r--r--fs/nfs/pnfs_nfs.c10
-rw-r--r--fs/nfs/read.c43
-rw-r--r--fs/nfs/symlink.c39
-rw-r--r--fs/nfs/write.c133
-rw-r--r--fs/nfsd/lockd.c2
-rw-r--r--fs/nfsd/netns.h2
-rw-r--r--fs/nfsd/nfs3xdr.c2
-rw-r--r--fs/nfsd/nfs4callback.c6
-rw-r--r--fs/nfsd/nfs4layouts.c41
-rw-r--r--fs/nfsd/nfs4proc.c63
-rw-r--r--fs/nfsd/nfs4recover.c6
-rw-r--r--fs/nfsd/nfs4state.c70
-rw-r--r--fs/nfsd/nfs4xdr.c29
-rw-r--r--fs/nfsd/nfsfh.h23
-rw-r--r--fs/nfsd/nfssvc.c75
-rw-r--r--fs/nfsd/state.h8
-rw-r--r--fs/nfsd/trace.h41
-rw-r--r--fs/nfsd/vfs.c46
-rw-r--r--fs/nfsd/vfs.h2
-rw-r--r--fs/nfsd/xdr4.h10
-rw-r--r--fs/nilfs2/inode.c1
-rw-r--r--fs/nilfs2/namei.c4
-rw-r--r--fs/nilfs2/super.c9
-rw-r--r--fs/notify/inode_mark.c3
-rw-r--r--fs/notify/mark.c66
-rw-r--r--fs/ntfs/super.c4
-rw-r--r--fs/ocfs2/alloc.c15
-rw-r--r--fs/ocfs2/alloc.h2
-rw-r--r--fs/ocfs2/cluster/heartbeat.c4
-rw-r--r--fs/ocfs2/dlm/dlmcommon.h11
-rw-r--r--fs/ocfs2/dlm/dlmmaster.c37
-rw-r--r--fs/ocfs2/dlm/dlmrecovery.c15
-rw-r--r--fs/ocfs2/dlm/dlmunlock.c2
-rw-r--r--fs/ocfs2/dlmfs/dlmfs.c2
-rw-r--r--fs/ocfs2/dlmglue.c8
-rw-r--r--fs/ocfs2/file.c8
-rw-r--r--fs/ocfs2/inode.c1
-rw-r--r--fs/ocfs2/ioctl.c4
-rw-r--r--fs/ocfs2/journal.c10
-rw-r--r--fs/ocfs2/localalloc.c10
-rw-r--r--fs/ocfs2/locks.c5
-rw-r--r--fs/ocfs2/namei.c26
-rw-r--r--fs/ocfs2/quota.h2
-rw-r--r--fs/ocfs2/quota_global.c2
-rw-r--r--fs/ocfs2/resize.c15
-rw-r--r--fs/ocfs2/slot_map.c14
-rw-r--r--fs/ocfs2/super.c13
-rw-r--r--fs/ocfs2/symlink.c3
-rw-r--r--fs/ocfs2/xattr.c168
-rw-r--r--fs/open.c5
-rw-r--r--fs/openpromfs/inode.c2
-rw-r--r--fs/overlayfs/copy_up.c23
-rw-r--r--fs/overlayfs/inode.c72
-rw-r--r--fs/overlayfs/overlayfs.h3
-rw-r--r--fs/posix_acl.c25
-rw-r--r--fs/proc/base.c42
-rw-r--r--fs/proc/fd.c1
-rw-r--r--fs/proc/inode.c24
-rw-r--r--fs/proc/meminfo.c5
-rw-r--r--fs/proc/namespaces.c10
-rw-r--r--fs/proc/self.c18
-rw-r--r--fs/proc/task_mmu.c78
-rw-r--r--fs/proc/thread_self.c19
-rw-r--r--fs/proc_namespace.c27
-rw-r--r--fs/qnx4/inode.c3
-rw-r--r--fs/qnx6/inode.c3
-rw-r--r--fs/quota/dquot.c2
-rw-r--r--fs/quota/netlink.c5
-rw-r--r--fs/quota/quota_v2.c4
-rw-r--r--fs/ramfs/inode.c1
-rw-r--r--fs/read_write.c341
-rw-r--r--fs/reiserfs/inode.c1
-rw-r--r--fs/reiserfs/journal.c24
-rw-r--r--fs/reiserfs/namei.c4
-rw-r--r--fs/reiserfs/prints.c9
-rw-r--r--fs/reiserfs/procfs.c5
-rw-r--r--fs/reiserfs/super.c3
-rw-r--r--fs/reiserfs/xattr.c16
-rw-r--r--fs/reiserfs/xattr_acl.c8
-rw-r--r--fs/reiserfs/xattr_security.c16
-rw-r--r--fs/reiserfs/xattr_trusted.c15
-rw-r--r--fs/reiserfs/xattr_user.c14
-rw-r--r--fs/romfs/super.c5
-rw-r--r--fs/select.c6
-rw-r--r--fs/splice.c16
-rw-r--r--fs/squashfs/inode.c2
-rw-r--r--fs/squashfs/super.c10
-rw-r--r--fs/squashfs/symlink.c3
-rw-r--r--fs/squashfs/xattr.c38
-rw-r--r--fs/super.c6
-rw-r--r--fs/sysv/inode.c17
-rw-r--r--fs/ubifs/file.c2
-rw-r--r--fs/ubifs/key.h6
-rw-r--r--fs/ubifs/super.c4
-rw-r--r--fs/ubifs/xattr.c4
-rw-r--r--fs/udf/balloc.c98
-rw-r--r--fs/udf/inode.c246
-rw-r--r--fs/udf/namei.c8
-rw-r--r--fs/udf/super.c17
-rw-r--r--fs/udf/symlink.c4
-rw-r--r--fs/udf/udfdecl.h5
-rw-r--r--fs/udf/unicode.c21
-rw-r--r--fs/ufs/Makefile2
-rw-r--r--fs/ufs/inode.c5
-rw-r--r--fs/ufs/namei.c5
-rw-r--r--fs/ufs/super.c2
-rw-r--r--fs/ufs/symlink.c42
-rw-r--r--fs/ufs/ufs.h4
-rw-r--r--fs/xattr.c203
-rw-r--r--fs/xfs/kmem.h1
-rw-r--r--fs/xfs/libxfs/xfs_alloc.c4
-rw-r--r--fs/xfs/libxfs/xfs_alloc.h1
-rw-r--r--fs/xfs/libxfs/xfs_alloc_btree.c35
-rw-r--r--fs/xfs/libxfs/xfs_attr.c141
-rw-r--r--fs/xfs/libxfs/xfs_attr_leaf.c1
-rw-r--r--fs/xfs/libxfs/xfs_attr_remote.c32
-rw-r--r--fs/xfs/libxfs/xfs_bit.c6
-rw-r--r--fs/xfs/libxfs/xfs_bmap.c43
-rw-r--r--fs/xfs/libxfs/xfs_bmap.h2
-rw-r--r--fs/xfs/libxfs/xfs_bmap_btree.c1
-rw-r--r--fs/xfs/libxfs/xfs_btree.c58
-rw-r--r--fs/xfs/libxfs/xfs_btree.h3
-rw-r--r--fs/xfs/libxfs/xfs_da_btree.c1
-rw-r--r--fs/xfs/libxfs/xfs_dir2_block.c1
-rw-r--r--fs/xfs/libxfs/xfs_dir2_data.c2
-rw-r--r--fs/xfs/libxfs/xfs_dir2_leaf.c2
-rw-r--r--fs/xfs/libxfs/xfs_dir2_node.c1
-rw-r--r--fs/xfs/libxfs/xfs_dquot_buf.c37
-rw-r--r--fs/xfs/libxfs/xfs_format.h2
-rw-r--r--fs/xfs/libxfs/xfs_ialloc.c1
-rw-r--r--fs/xfs/libxfs/xfs_ialloc_btree.c27
-rw-r--r--fs/xfs/libxfs/xfs_inode_buf.c16
-rw-r--r--fs/xfs/libxfs/xfs_log_recover.h1
-rw-r--r--fs/xfs/libxfs/xfs_quota_defs.h2
-rw-r--r--fs/xfs/libxfs/xfs_sb.c2
-rw-r--r--fs/xfs/libxfs/xfs_shared.h1
-rw-r--r--fs/xfs/libxfs/xfs_symlink_remote.c1
-rw-r--r--fs/xfs/xfs_acl.c23
-rw-r--r--fs/xfs/xfs_acl.h4
-rw-r--r--fs/xfs/xfs_aops.c2
-rw-r--r--fs/xfs/xfs_bmap_util.c43
-rw-r--r--fs/xfs/xfs_buf.c17
-rw-r--r--fs/xfs/xfs_buf.h1
-rw-r--r--fs/xfs/xfs_dquot.c13
-rw-r--r--fs/xfs/xfs_error.c4
-rw-r--r--fs/xfs/xfs_file.c25
-rw-r--r--fs/xfs/xfs_inode.c25
-rw-r--r--fs/xfs/xfs_iomap.c21
-rw-r--r--fs/xfs/xfs_iops.c14
-rw-r--r--fs/xfs/xfs_log.c51
-rw-r--r--fs/xfs/xfs_log_priv.h3
-rw-r--r--fs/xfs/xfs_log_recover.c581
-rw-r--r--fs/xfs/xfs_rtalloc.c3
-rw-r--r--fs/xfs/xfs_super.c6
-rw-r--r--fs/xfs/xfs_symlink.c12
-rw-r--r--fs/xfs/xfs_sysfs.c36
-rw-r--r--fs/xfs/xfs_trace.h26
-rw-r--r--fs/xfs/xfs_trans_dquot.c14
-rw-r--r--fs/xfs/xfs_xattr.c143
-rw-r--r--include/acpi/acexcep.h6
-rw-r--r--include/acpi/acpi_bus.h6
-rw-r--r--include/acpi/acpiosxf.h18
-rw-r--r--include/acpi/acpixf.h55
-rw-r--r--include/acpi/actypes.h6
-rw-r--r--include/acpi/platform/aclinux.h2
-rw-r--r--include/acpi/platform/aclinuxex.h10
-rw-r--r--include/acpi/video.h6
-rw-r--r--include/asm-generic/barrier.h2
-rw-r--r--include/asm-generic/memory_model.h4
-rw-r--r--include/asm-generic/pgtable.h10
-rw-r--r--include/asm-generic/qspinlock.h9
-rw-r--r--include/asm-generic/tlb.h2
-rw-r--r--include/clocksource/arm_arch_timer.h6
-rw-r--r--include/crypto/aead.h1
-rw-r--r--include/crypto/akcipher.h10
-rw-r--r--include/crypto/internal/akcipher.h78
-rw-r--r--include/crypto/internal/rsa.h2
-rw-r--r--include/crypto/md5.h2
-rw-r--r--include/crypto/sha.h6
-rw-r--r--include/drm/drmP.h10
-rw-r--r--include/dt-bindings/leds/common.h2
-rw-r--r--include/dt-bindings/mfd/palmas.h2
-rw-r--r--include/kvm/arm_vgic.h8
-rw-r--r--include/linux/acpi.h76
-rw-r--r--include/linux/amba/serial.h18
-rw-r--r--include/linux/arm-smccc.h104
-rw-r--r--include/linux/badblocks.h65
-rw-r--r--include/linux/bcma/bcma_driver_chipcommon.h6
-rw-r--r--include/linux/bitops.h2
-rw-r--r--include/linux/blkdev.h4
-rw-r--r--include/linux/bootmem.h4
-rw-r--r--include/linux/bpf.h5
-rw-r--r--include/linux/brcmphy.h1
-rw-r--r--include/linux/cgroup-defs.h152
-rw-r--r--include/linux/cgroup.h132
-rw-r--r--include/linux/cgroup_subsys.h18
-rw-r--r--include/linux/clocksource.h16
-rw-r--r--include/linux/compiler.h17
-rw-r--r--include/linux/component.h33
-rw-r--r--include/linux/configfs.h50
-rw-r--r--include/linux/context_tracking.h4
-rw-r--r--include/linux/context_tracking_state.h4
-rw-r--r--include/linux/cpufreq.h7
-rw-r--r--include/linux/dca.h5
-rw-r--r--include/linux/dcache.h4
-rw-r--r--include/linux/delayed_call.h34
-rw-r--r--include/linux/device.h1
-rw-r--r--include/linux/dmaengine.h145
-rw-r--r--include/linux/dns_resolver.h2
-rw-r--r--include/linux/dqblk_qtree.h2
-rw-r--r--include/linux/edac.h8
-rw-r--r--include/linux/enclosure.h4
-rw-r--r--include/linux/etherdevice.h3
-rw-r--r--include/linux/f2fs_fs.h3
-rw-r--r--include/linux/filter.h60
-rw-r--r--include/linux/fs.h155
-rw-r--r--include/linux/fsl/edac.h8
-rw-r--r--include/linux/fsnotify_backend.h5
-rw-r--r--include/linux/ftrace.h57
-rw-r--r--include/linux/genhd.h2
-rw-r--r--include/linux/gfp.h22
-rw-r--r--include/linux/hashtable.h4
-rw-r--r--include/linux/hdlc.h2
-rw-r--r--include/linux/hid.h8
-rw-r--r--include/linux/hsi/hsi.h3
-rw-r--r--include/linux/hugetlb.h12
-rw-r--r--include/linux/hyperv.h133
-rw-r--r--include/linux/i2c.h47
-rw-r--r--include/linux/if_pppox.h1
-rw-r--r--include/linux/if_team.h1
-rw-r--r--include/linux/if_vlan.h4
-rw-r--r--include/linux/iio/buffer-dma.h152
-rw-r--r--include/linux/iio/buffer-dmaengine.h18
-rw-r--r--include/linux/iio/buffer.h16
-rw-r--r--include/linux/iio/configfs.h15
-rw-r--r--include/linux/iio/iio.h2
-rw-r--r--include/linux/iio/sw_trigger.h70
-rw-r--r--include/linux/inet_diag.h9
-rw-r--r--include/linux/init_task.h2
-rw-r--r--include/linux/interrupt.h1
-rw-r--r--include/linux/ipv6.h2
-rw-r--r--include/linux/irqchip/arm-gic-v3.h1
-rw-r--r--include/linux/irqchip/arm-gic.h13
-rw-r--r--include/linux/irqdesc.h6
-rw-r--r--include/linux/irqdomain.h13
-rw-r--r--include/linux/jump_label.h2
-rw-r--r--include/linux/kernel.h28
-rw-r--r--include/linux/kernfs.h12
-rw-r--r--include/linux/kexec.h2
-rw-r--r--include/linux/kmemleak.h2
-rw-r--r--include/linux/kref.h33
-rw-r--r--include/linux/kvm_host.h77
-rw-r--r--include/linux/kvm_para.h6
-rw-r--r--include/linux/leds.h29
-rw-r--r--include/linux/libata.h2
-rw-r--r--include/linux/libnvdimm.h1
-rw-r--r--include/linux/lightnvm.h197
-rw-r--r--include/linux/list.h14
-rw-r--r--include/linux/list_bl.h2
-rw-r--r--include/linux/list_nulls.h2
-rw-r--r--include/linux/livepatch.h24
-rw-r--r--include/linux/lockd/bind.h2
-rw-r--r--include/linux/lockdep.h2
-rw-r--r--include/linux/mdio.h78
-rw-r--r--include/linux/memblock.h21
-rw-r--r--include/linux/memcontrol.h102
-rw-r--r--include/linux/mempolicy.h2
-rw-r--r--include/linux/mfd/arizona/core.h3
-rw-r--r--include/linux/mfd/arizona/pdata.h2
-rw-r--r--include/linux/mfd/core.h5
-rw-r--r--include/linux/mfd/palmas.h75
-rw-r--r--include/linux/mfd/samsung/core.h1
-rw-r--r--include/linux/mfd/samsung/rtc.h2
-rw-r--r--include/linux/mfd/samsung/s2mps15.h158
-rw-r--r--include/linux/mfd/tps65218.h7
-rw-r--r--include/linux/mfd/wm8350/pmic.h1
-rw-r--r--include/linux/mlx4/device.h11
-rw-r--r--include/linux/mlx4/driver.h5
-rw-r--r--include/linux/mlx5/device.h66
-rw-r--r--include/linux/mlx5/driver.h30
-rw-r--r--include/linux/mlx5/fs.h111
-rw-r--r--include/linux/mlx5/mlx5_ifc.h311
-rw-r--r--include/linux/mlx5/vport.h37
-rw-r--r--include/linux/mm.h46
-rw-r--r--include/linux/mm_inline.h2
-rw-r--r--include/linux/mm_types.h9
-rw-r--r--include/linux/mmc/dw_mmc.h12
-rw-r--r--include/linux/mmc/host.h6
-rw-r--r--include/linux/mmdebug.h1
-rw-r--r--include/linux/mmzone.h37
-rw-r--r--include/linux/mod_devicetable.h2
-rw-r--r--include/linux/module.h68
-rw-r--r--include/linux/mroute.h76
-rw-r--r--include/linux/msi.h18
-rw-r--r--include/linux/mtd/map.h2
-rw-r--r--include/linux/mtd/mtd.h11
-rw-r--r--include/linux/mtd/nand.h71
-rw-r--r--include/linux/mtd/nand_bch.h2
-rw-r--r--include/linux/mtd/partitions.h27
-rw-r--r--include/linux/mtd/sh_flctl.h4
-rw-r--r--include/linux/mtd/spi-nor.h21
-rw-r--r--include/linux/namei.h1
-rw-r--r--include/linux/net.h13
-rw-r--r--include/linux/netdev_features.h14
-rw-r--r--include/linux/netdevice.h287
-rw-r--r--include/linux/netfilter/nf_conntrack_sctp.h13
-rw-r--r--include/linux/netfilter/nfnetlink.h14
-rw-r--r--include/linux/netlink.h2
-rw-r--r--include/linux/nfs4.h18
-rw-r--r--include/linux/nfs_fs.h24
-rw-r--r--include/linux/nfs_fs_sb.h1
-rw-r--r--include/linux/nfs_xdr.h7
-rw-r--r--include/linux/nwpserial.h18
-rw-r--r--include/linux/of_address.h19
-rw-r--r--include/linux/of_irq.h19
-rw-r--r--include/linux/omap-dma.h6
-rw-r--r--include/linux/pci.h19
-rw-r--r--include/linux/pci_ids.h2
-rw-r--r--include/linux/percpu-refcount.h2
-rw-r--r--include/linux/percpu.h6
-rw-r--r--include/linux/perf/arm_pmu.h2
-rw-r--r--include/linux/perf_event.h6
-rw-r--r--include/linux/pfn.h1
-rw-r--r--include/linux/phy.h80
-rw-r--r--include/linux/phy/omap_usb.h23
-rw-r--r--include/linux/pim.h5
-rw-r--r--include/linux/platform_data/camera-rcar.h25
-rw-r--r--include/linux/platform_data/dma-rcar-hpbdma.h103
-rw-r--r--include/linux/platform_data/edma.h9
-rw-r--r--include/linux/platform_data/irq-renesas-intc-irqpin.h29
-rw-r--r--include/linux/platform_data/media/camera-mx2.h (renamed from include/linux/platform_data/camera-mx2.h)0
-rw-r--r--include/linux/platform_data/media/camera-mx3.h (renamed from include/linux/platform_data/camera-mx3.h)0
-rw-r--r--include/linux/platform_data/media/camera-pxa.h (renamed from include/linux/platform_data/camera-pxa.h)0
-rw-r--r--include/linux/platform_data/media/coda.h (renamed from include/linux/platform_data/coda.h)0
-rw-r--r--include/linux/platform_data/media/gpio-ir-recv.h (renamed from include/media/gpio-ir-recv.h)1
-rw-r--r--include/linux/platform_data/media/ir-rx51.h (renamed from include/media/ir-rx51.h)0
-rw-r--r--include/linux/platform_data/media/mmp-camera.h (renamed from include/media/mmp-camera.h)0
-rw-r--r--include/linux/platform_data/media/omap1_camera.h (renamed from include/media/omap1_camera.h)0
-rw-r--r--include/linux/platform_data/media/omap4iss.h (renamed from include/media/omap4iss.h)0
-rw-r--r--include/linux/platform_data/media/s5p_hdmi.h (renamed from include/media/s5p_hdmi.h)1
-rw-r--r--include/linux/platform_data/media/si4713.h (renamed from include/media/si4713.h)2
-rw-r--r--include/linux/platform_data/media/sii9234.h (renamed from include/media/sii9234.h)0
-rw-r--r--include/linux/platform_data/media/soc_camera_platform.h (renamed from include/media/soc_camera_platform.h)0
-rw-r--r--include/linux/platform_data/media/timb_radio.h (renamed from include/media/timb_radio.h)0
-rw-r--r--include/linux/platform_data/media/timb_video.h (renamed from include/media/timb_video.h)0
-rw-r--r--include/linux/platform_data/microread.h2
-rw-r--r--include/linux/platform_data/mmc-mvsdio.h18
-rw-r--r--include/linux/platform_data/spi-s3c64xx.h2
-rw-r--r--include/linux/platform_data/usb-rcar-phy.h28
-rw-r--r--include/linux/platform_device.h6
-rw-r--r--include/linux/pm_opp.h22
-rw-r--r--include/linux/pm_runtime.h5
-rw-r--r--include/linux/posix_acl_xattr.h6
-rw-r--r--include/linux/power/bq27xxx_battery.h57
-rw-r--r--include/linux/powercap.h4
-rw-r--r--include/linux/property.h107
-rw-r--r--include/linux/proportions.h2
-rw-r--r--include/linux/qed/common_hsi.h2
-rw-r--r--include/linux/qed/qed_chain.h3
-rw-r--r--include/linux/qed/qed_if.h17
-rw-r--r--include/linux/rculist.h105
-rw-r--r--include/linux/rcupdate.h21
-rw-r--r--include/linux/rcutiny.h8
-rw-r--r--include/linux/rcutree.h4
-rw-r--r--include/linux/regmap.h18
-rw-r--r--include/linux/regulator/consumer.h3
-rw-r--r--include/linux/regulator/driver.h2
-rw-r--r--include/linux/rhashtable.h100
-rw-r--r--include/linux/rtnetlink.h5
-rw-r--r--include/linux/sched.h41
-rw-r--r--include/linux/sched_clock.h12
-rw-r--r--include/linux/scpi_protocol.h2
-rw-r--r--include/linux/serial_core.h3
-rw-r--r--include/linux/serial_sci.h1
-rw-r--r--include/linux/sh_eth.h2
-rw-r--r--include/linux/shmem_fs.h4
-rw-r--r--include/linux/skbuff.h171
-rw-r--r--include/linux/slab.h5
-rw-r--r--include/linux/soc/ti/knav_dma.h22
-rw-r--r--include/linux/sock_diag.h2
-rw-r--r--include/linux/spi/spi.h29
-rw-r--r--include/linux/ssb/ssb.h10
-rw-r--r--include/linux/stop_machine.h13
-rw-r--r--include/linux/string.h1
-rw-r--r--include/linux/sunrpc/svc_xprt.h1
-rw-r--r--include/linux/sunrpc/svcauth.h9
-rw-r--r--include/linux/swap.h1
-rw-r--r--include/linux/syscalls.h5
-rw-r--r--include/linux/thermal.h3
-rw-r--r--include/linux/thread_info.h5
-rw-r--r--include/linux/time.h26
-rw-r--r--include/linux/tracepoint-defs.h27
-rw-r--r--include/linux/tracepoint.h24
-rw-r--r--include/linux/tty.h23
-rw-r--r--include/linux/types.h2
-rw-r--r--include/linux/uinput.h5
-rw-r--r--include/linux/uio.h4
-rw-r--r--include/linux/uprobes.h2
-rw-r--r--include/linux/usb.h6
-rw-r--r--include/linux/usb/cdc_ncm.h1
-rw-r--r--include/linux/usb/gadget.h11
-rw-r--r--include/linux/usb/hcd.h4
-rw-r--r--include/linux/usb/musb-omap.h30
-rw-r--r--include/linux/usb/musb.h15
-rw-r--r--include/linux/usb/of.h6
-rw-r--r--include/linux/usb/quirks.h3
-rw-r--r--include/linux/usb/renesas_usbhs.h18
-rw-r--r--include/linux/videodev2.h2
-rw-r--r--include/linux/vmalloc.h1
-rw-r--r--include/linux/vmpressure.h7
-rw-r--r--include/linux/vmstat.h8
-rw-r--r--include/linux/vtime.h25
-rw-r--r--include/linux/wait.h61
-rw-r--r--include/linux/workqueue.h6
-rw-r--r--include/linux/xattr.h20
-rw-r--r--include/media/drv-intf/cx2341x.h (renamed from include/media/cx2341x.h)0
-rw-r--r--include/media/drv-intf/cx25840.h (renamed from include/media/cx25840.h)0
-rw-r--r--include/media/drv-intf/exynos-fimc.h (renamed from include/media/exynos-fimc.h)0
-rw-r--r--include/media/drv-intf/msp3400.h (renamed from include/media/msp3400.h)1
-rw-r--r--include/media/drv-intf/s3c_camif.h (renamed from include/media/s3c_camif.h)0
-rw-r--r--include/media/drv-intf/saa7146.h (renamed from include/media/saa7146.h)0
-rw-r--r--include/media/drv-intf/saa7146_vv.h (renamed from include/media/saa7146_vv.h)2
-rw-r--r--include/media/drv-intf/sh_mobile_ceu.h (renamed from include/media/sh_mobile_ceu.h)0
-rw-r--r--include/media/drv-intf/sh_mobile_csi2.h (renamed from include/media/sh_mobile_csi2.h)0
-rw-r--r--include/media/drv-intf/sh_vou.h (renamed from include/media/sh_vou.h)0
-rw-r--r--include/media/drv-intf/si476x.h (renamed from include/media/si476x.h)2
-rw-r--r--include/media/drv-intf/soc_mediabus.h (renamed from include/media/soc_mediabus.h)0
-rw-r--r--include/media/drv-intf/tea575x.h (renamed from include/media/tea575x.h)2
-rw-r--r--include/media/i2c/ad9389b.h (renamed from include/media/ad9389b.h)0
-rw-r--r--include/media/i2c/adp1653.h (renamed from include/media/adp1653.h)2
-rw-r--r--include/media/i2c/adv7183.h (renamed from include/media/adv7183.h)0
-rw-r--r--include/media/i2c/adv7343.h (renamed from include/media/adv7343.h)0
-rw-r--r--include/media/i2c/adv7393.h (renamed from include/media/adv7393.h)0
-rw-r--r--include/media/i2c/adv7511.h (renamed from include/media/adv7511.h)0
-rw-r--r--include/media/i2c/adv7604.h (renamed from include/media/adv7604.h)0
-rw-r--r--include/media/i2c/adv7842.h (renamed from include/media/adv7842.h)0
-rw-r--r--include/media/i2c/ak881x.h (renamed from include/media/ak881x.h)0
-rw-r--r--include/media/i2c/as3645a.h (renamed from include/media/as3645a.h)2
-rw-r--r--include/media/i2c/bt819.h (renamed from include/media/bt819.h)0
-rw-r--r--include/media/i2c/cs5345.h (renamed from include/media/cs5345.h)0
-rw-r--r--include/media/i2c/cs53l32a.h (renamed from include/media/cs53l32a.h)0
-rw-r--r--include/media/i2c/ir-kbd-i2c.h (renamed from include/media/ir-kbd-i2c.h)0
-rw-r--r--include/media/i2c/lm3560.h (renamed from include/media/lm3560.h)2
-rw-r--r--include/media/i2c/lm3646.h (renamed from include/media/lm3646.h)2
-rw-r--r--include/media/i2c/m52790.h (renamed from include/media/m52790.h)0
-rw-r--r--include/media/i2c/m5mols.h (renamed from include/media/m5mols.h)0
-rw-r--r--include/media/i2c/mt9m032.h (renamed from include/media/mt9m032.h)0
-rw-r--r--include/media/i2c/mt9p031.h (renamed from include/media/mt9p031.h)0
-rw-r--r--include/media/i2c/mt9t001.h (renamed from include/media/mt9t001.h)0
-rw-r--r--include/media/i2c/mt9t112.h (renamed from include/media/mt9t112.h)0
-rw-r--r--include/media/i2c/mt9v011.h (renamed from include/media/mt9v011.h)0
-rw-r--r--include/media/i2c/mt9v022.h (renamed from include/media/mt9v022.h)0
-rw-r--r--include/media/i2c/mt9v032.h (renamed from include/media/mt9v032.h)0
-rw-r--r--include/media/i2c/noon010pc30.h (renamed from include/media/noon010pc30.h)0
-rw-r--r--include/media/i2c/ov2659.h (renamed from include/media/ov2659.h)0
-rw-r--r--include/media/i2c/ov7670.h (renamed from include/media/ov7670.h)0
-rw-r--r--include/media/i2c/ov772x.h (renamed from include/media/ov772x.h)0
-rw-r--r--include/media/i2c/ov9650.h (renamed from include/media/ov9650.h)0
-rw-r--r--include/media/i2c/rj54n1cb0c.h (renamed from include/media/rj54n1cb0c.h)0
-rw-r--r--include/media/i2c/s5c73m3.h (renamed from include/media/s5c73m3.h)0
-rw-r--r--include/media/i2c/s5k4ecgx.h (renamed from include/media/s5k4ecgx.h)0
-rw-r--r--include/media/i2c/s5k6aa.h (renamed from include/media/s5k6aa.h)0
-rw-r--r--include/media/i2c/saa6588.h (renamed from include/media/saa6588.h)0
-rw-r--r--include/media/i2c/saa7115.h (renamed from include/media/saa7115.h)1
-rw-r--r--include/media/i2c/saa7127.h (renamed from include/media/saa7127.h)1
-rw-r--r--include/media/i2c/smiapp.h (renamed from include/media/smiapp.h)2
-rw-r--r--include/media/i2c/sr030pc30.h (renamed from include/media/sr030pc30.h)0
-rw-r--r--include/media/i2c/tc358743.h (renamed from include/media/tc358743.h)0
-rw-r--r--include/media/i2c/ths7303.h (renamed from include/media/ths7303.h)0
-rw-r--r--include/media/i2c/tvaudio.h (renamed from include/media/tvaudio.h)0
-rw-r--r--include/media/i2c/tvp514x.h (renamed from include/media/tvp514x.h)0
-rw-r--r--include/media/i2c/tvp5150.h (renamed from include/media/tvp5150.h)1
-rw-r--r--include/media/i2c/tvp7002.h (renamed from include/media/tvp7002.h)0
-rw-r--r--include/media/i2c/tw9910.h (renamed from include/media/tw9910.h)0
-rw-r--r--include/media/i2c/uda1342.h (renamed from include/media/uda1342.h)0
-rw-r--r--include/media/i2c/upd64031a.h (renamed from include/media/upd64031a.h)0
-rw-r--r--include/media/i2c/upd64083.h (renamed from include/media/upd64083.h)0
-rw-r--r--include/media/i2c/wm8775.h (renamed from include/media/wm8775.h)0
-rw-r--r--include/media/lirc.h169
-rw-r--r--include/media/media-device.h469
-rw-r--r--include/media/media-devnode.h54
-rw-r--r--include/media/media-entity.h904
-rw-r--r--include/media/rc-core.h1
-rw-r--r--include/media/rc-map.h40
-rw-r--r--include/media/tuner.h8
-rw-r--r--include/media/v4l2-clk.h5
-rw-r--r--include/media/v4l2-dev.h1
-rw-r--r--include/media/v4l2-dv-timings.h25
-rw-r--r--include/media/videobuf2-core.h108
-rw-r--r--include/media/videobuf2-v4l2.h40
-rw-r--r--include/net/6lowpan.h10
-rw-r--r--include/net/addrconf.h3
-rw-r--r--include/net/af_unix.h1
-rw-r--r--include/net/bluetooth/bluetooth.h25
-rw-r--r--include/net/bluetooth/hci.h4
-rw-r--r--include/net/bluetooth/hci_core.h62
-rw-r--r--include/net/bluetooth/hci_mon.h2
-rw-r--r--include/net/bluetooth/hci_sock.h1
-rw-r--r--include/net/bluetooth/mgmt.h15
-rw-r--r--include/net/bonding.h39
-rw-r--r--include/net/busy_poll.h45
-rw-r--r--include/net/cfg80211.h10
-rw-r--r--include/net/checksum.h3
-rw-r--r--include/net/cls_cgroup.h11
-rw-r--r--include/net/dsa.h20
-rw-r--r--include/net/dst.h33
-rw-r--r--include/net/genetlink.h2
-rw-r--r--include/net/geneve.h8
-rw-r--r--include/net/ieee802154_netdev.h2
-rw-r--r--include/net/ila.h18
-rw-r--r--include/net/inet_frag.h1
-rw-r--r--include/net/inet_sock.h41
-rw-r--r--include/net/inetpeer.h1
-rw-r--r--include/net/ip6_route.h17
-rw-r--r--include/net/ip6_tunnel.h19
-rw-r--r--include/net/ip_fib.h3
-rw-r--r--include/net/ip_tunnels.h28
-rw-r--r--include/net/ipv6.h37
-rw-r--r--include/net/l3mdev.h39
-rw-r--r--include/net/mac80211.h62
-rw-r--r--include/net/ndisc.h3
-rw-r--r--include/net/net_namespace.h3
-rw-r--r--include/net/netfilter/ipv6/nf_defrag_ipv6.h3
-rw-r--r--include/net/netfilter/nf_conntrack_timeout.h2
-rw-r--r--include/net/netfilter/nf_dup_netdev.h6
-rw-r--r--include/net/netfilter/nf_tables.h45
-rw-r--r--include/net/netfilter/nf_tables_core.h10
-rw-r--r--include/net/netfilter/nft_meta.h3
-rw-r--r--include/net/netns/ipv4.h7
-rw-r--r--include/net/netns/sctp.h7
-rw-r--r--include/net/netprio_cgroup.h16
-rw-r--r--include/net/nfc/nfc.h1
-rw-r--r--include/net/protocol.h2
-rw-r--r--include/net/request_sock.h2
-rw-r--r--include/net/route.h7
-rw-r--r--include/net/sch_generic.h12
-rw-r--r--include/net/sctp/sctp.h32
-rw-r--r--include/net/sctp/structs.h27
-rw-r--r--include/net/sock.h221
-rw-r--r--include/net/sock_reuseport.h28
-rw-r--r--include/net/switchdev.h15
-rw-r--r--include/net/tcp.h34
-rw-r--r--include/net/tcp_memcontrol.h1
-rw-r--r--include/net/udp.h7
-rw-r--r--include/net/udp_tunnel.h8
-rw-r--r--include/net/vxlan.h4
-rw-r--r--include/net/xfrm.h25
-rw-r--r--include/rdma/ib_mad.h2
-rw-r--r--include/rdma/ib_verbs.h1
-rw-r--r--include/scsi/sas.h74
-rw-r--r--include/scsi/scsi_dbg.h2
-rw-r--r--include/scsi/scsi_device.h7
-rw-r--r--include/scsi/scsi_host.h3
-rw-r--r--include/scsi/scsi_transport_sas.h10
-rw-r--r--include/sound/hda_register.h3
-rw-r--r--include/sound/soc-dapm.h1
-rw-r--r--include/sound/soc.h2
-rw-r--r--include/target/target_core_base.h2
-rw-r--r--include/trace/define_trace.h6
-rw-r--r--include/trace/events/f2fs.h38
-rw-r--r--include/trace/events/fib6.h76
-rw-r--r--include/trace/events/filelock.h77
-rw-r--r--include/trace/events/huge_memory.h136
-rw-r--r--include/trace/events/page_isolation.h38
-rw-r--r--include/trace/events/v4l2.h4
-rw-r--r--include/trace/events/vb2.h7
-rw-r--r--include/trace/events/vmscan.h21
-rw-r--r--include/trace/trace_events.h6
-rw-r--r--include/uapi/asm-generic/socket.h3
-rw-r--r--include/uapi/asm-generic/unistd.h4
-rw-r--r--include/uapi/drm/drm_fourcc.h2
-rw-r--r--include/uapi/linux/Kbuild1
-rw-r--r--include/uapi/linux/bpf.h27
-rw-r--r--include/uapi/linux/dvb/video.h1
-rw-r--r--include/uapi/linux/ethtool.h3
-rw-r--r--include/uapi/linux/fs.h41
-rw-r--r--include/uapi/linux/gfs2_ondisk.h9
-rw-r--r--include/uapi/linux/hyperv.h1
-rw-r--r--include/uapi/linux/if_link.h4
-rw-r--r--include/uapi/linux/ila.h22
-rw-r--r--include/uapi/linux/in6.h1
-rw-r--r--include/uapi/linux/kvm.h26
-rw-r--r--include/uapi/linux/lirc.h168
-rw-r--r--include/uapi/linux/magic.h1
-rw-r--r--include/uapi/linux/media.h228
-rw-r--r--include/uapi/linux/mroute.h59
-rw-r--r--include/uapi/linux/netfilter/ipset/ip_set_bitmap.h2
-rw-r--r--include/uapi/linux/netfilter/ipset/ip_set_hash.h2
-rw-r--r--include/uapi/linux/netfilter/ipset/ip_set_list.h2
-rw-r--r--include/uapi/linux/netfilter/nf_conntrack_sctp.h12
-rw-r--r--include/uapi/linux/netfilter/nf_conntrack_tuple_common.h3
-rw-r--r--include/uapi/linux/netfilter/nf_tables.h91
-rw-r--r--include/uapi/linux/netfilter/nfnetlink.h2
-rw-r--r--include/uapi/linux/netfilter/xt_HMARK.h1
-rw-r--r--include/uapi/linux/netfilter/xt_RATEEST.h1
-rw-r--r--include/uapi/linux/netfilter/xt_TEE.h2
-rw-r--r--include/uapi/linux/netfilter/xt_TPROXY.h1
-rw-r--r--include/uapi/linux/netfilter/xt_cgroup.h15
-rw-r--r--include/uapi/linux/netfilter/xt_hashlimit.h1
-rw-r--r--include/uapi/linux/netfilter/xt_ipvs.h1
-rw-r--r--include/uapi/linux/netfilter/xt_mac.h2
-rw-r--r--include/uapi/linux/netfilter/xt_osf.h2
-rw-r--r--include/uapi/linux/netfilter/xt_physdev.h2
-rw-r--r--include/uapi/linux/netfilter/xt_policy.h2
-rw-r--r--include/uapi/linux/netfilter/xt_rateest.h1
-rw-r--r--include/uapi/linux/netfilter/xt_recent.h1
-rw-r--r--include/uapi/linux/netfilter/xt_sctp.h12
-rw-r--r--include/uapi/linux/netfilter_arp/arp_tables.h1
-rw-r--r--include/uapi/linux/netfilter_bridge.h1
-rw-r--r--include/uapi/linux/netfilter_bridge/ebt_arp.h1
-rw-r--r--include/uapi/linux/netfilter_bridge/ebt_arpreply.h2
-rw-r--r--include/uapi/linux/netfilter_bridge/ebt_ip6.h1
-rw-r--r--include/uapi/linux/netfilter_bridge/ebt_nat.h2
-rw-r--r--include/uapi/linux/netfilter_bridge/ebtables.h6
-rw-r--r--include/uapi/linux/netfilter_ipv4/ip_tables.h1
-rw-r--r--include/uapi/linux/netfilter_ipv6/ip6_tables.h1
-rw-r--r--include/uapi/linux/netfilter_ipv6/ip6t_rt.h2
-rw-r--r--include/uapi/linux/nfs.h11
-rw-r--r--include/uapi/linux/nl80211.h11
-rw-r--r--include/uapi/linux/openvswitch.h2
-rw-r--r--include/uapi/linux/perf_event.h6
-rw-r--r--include/uapi/linux/pkt_sched.h4
-rw-r--r--include/uapi/linux/raid/md_u.h4
-rw-r--r--include/uapi/linux/rtnetlink.h1
-rw-r--r--include/uapi/linux/serial.h1
-rw-r--r--include/uapi/linux/serial_core.h2
-rw-r--r--include/uapi/linux/serio.h1
-rw-r--r--include/uapi/linux/sock_diag.h1
-rw-r--r--include/uapi/linux/sockios.h2
-rw-r--r--include/uapi/linux/uinput.h80
-rw-r--r--include/uapi/linux/usb/video.h1
-rw-r--r--include/uapi/linux/v4l2-controls.h6
-rw-r--r--include/uapi/linux/vfio.h2
-rw-r--r--include/uapi/linux/videodev2.h14
-rw-r--r--include/uapi/rdma/hfi/hfi1_user.h38
-rw-r--r--include/uapi/scsi/cxlflash_ioctl.h10
-rw-r--r--include/uapi/xen/gntdev.h50
-rw-r--r--include/video/imx-ipu-v3.h1
-rw-r--r--include/xen/interface/io/ring.h14
-rw-r--r--include/xen/interface/platform.h18
-rw-r--r--include/xen/interface/xen.h2
-rw-r--r--include/xen/xen-ops.h5
-rw-r--r--init/Kconfig248
-rw-r--r--init/main.c2
-rw-r--r--ipc/mqueue.c2
-rw-r--r--kernel/bpf/arraymap.c10
-rw-r--r--kernel/bpf/core.c4
-rw-r--r--kernel/bpf/hashtab.c94
-rw-r--r--kernel/bpf/inode.c26
-rw-r--r--kernel/bpf/syscall.c60
-rw-r--r--kernel/bpf/verifier.c13
-rw-r--r--kernel/cgroup.c306
-rw-r--r--kernel/cgroup_freezer.c25
-rw-r--r--kernel/cgroup_pids.c83
-rw-r--r--kernel/context_tracking.c4
-rw-r--r--kernel/cpuset.c45
-rw-r--r--kernel/cred.c4
-rw-r--r--kernel/debug/kdb/kdb_main.c4
-rw-r--r--kernel/delayacct.c2
-rw-r--r--kernel/events/callchain.c2
-rw-r--r--kernel/events/core.c395
-rw-r--r--kernel/events/ring_buffer.c2
-rw-r--r--kernel/events/uprobes.c4
-rw-r--r--kernel/fork.c48
-rw-r--r--kernel/futex.c83
-rw-r--r--kernel/gcov/base.c7
-rw-r--r--kernel/irq/chip.c9
-rw-r--r--kernel/irq/irqdesc.c19
-rw-r--r--kernel/irq/irqdomain.c12
-rw-r--r--kernel/irq/manage.c31
-rw-r--r--kernel/irq/msi.c58
-rw-r--r--kernel/irq_work.c2
-rw-r--r--kernel/jump_label.c2
-rw-r--r--kernel/kexec_core.c30
-rw-r--r--kernel/ksysfs.c26
-rw-r--r--kernel/livepatch/core.c176
-rw-r--r--kernel/locking/lockdep.c2
-rw-r--r--kernel/locking/lockdep_proc.c2
-rw-r--r--kernel/locking/osq_lock.c8
-rw-r--r--kernel/locking/qspinlock.c82
-rw-r--r--kernel/locking/qspinlock_paravirt.h252
-rw-r--r--kernel/locking/qspinlock_stat.h300
-rw-r--r--kernel/module.c355
-rw-r--r--kernel/panic.c33
-rw-r--r--kernel/pid.c6
-rw-r--r--kernel/power/main.c17
-rw-r--r--kernel/power/power.h9
-rw-r--r--kernel/rcu/rcutorture.c24
-rw-r--r--kernel/rcu/srcu.c2
-rw-r--r--kernel/rcu/tree.c313
-rw-r--r--kernel/rcu/tree.h61
-rw-r--r--kernel/rcu/tree_plugin.h66
-rw-r--r--kernel/rcu/tree_trace.c39
-rw-r--r--kernel/rcu/update.c22
-rw-r--r--kernel/resource.c11
-rw-r--r--kernel/sched/auto_group.c2
-rw-r--r--kernel/sched/clock.c4
-rw-r--r--kernel/sched/core.c227
-rw-r--r--kernel/sched/cputime.c78
-rw-r--r--kernel/sched/deadline.c59
-rw-r--r--kernel/sched/fair.c316
-rw-r--r--kernel/sched/idle.c1
-rw-r--r--kernel/sched/idle_task.c1
-rw-r--r--kernel/sched/rt.c2
-rw-r--r--kernel/sched/sched.h73
-rw-r--r--kernel/sched/wait.c28
-rw-r--r--kernel/stop_machine.c88
-rw-r--r--kernel/sys_ni.c1
-rw-r--r--kernel/sysctl.c101
-rw-r--r--kernel/time/alarmtimer.c17
-rw-r--r--kernel/time/clocksource.c4
-rw-r--r--kernel/time/ntp.c44
-rw-r--r--kernel/time/ntp_internal.h2
-rw-r--r--kernel/time/posix-clock.c4
-rw-r--r--kernel/time/tick-sched.c36
-rw-r--r--kernel/time/timekeeping.c49
-rw-r--r--kernel/time/timekeeping_internal.h8
-rw-r--r--kernel/trace/blktrace.c12
-rw-r--r--kernel/trace/bpf_trace.c2
-rw-r--r--kernel/trace/ftrace.c451
-rw-r--r--kernel/trace/ring_buffer.c74
-rw-r--r--kernel/trace/trace.h6
-rw-r--r--kernel/trace/trace_event_perf.c4
-rw-r--r--kernel/trace/trace_events.c44
-rw-r--r--kernel/trace/trace_events_trigger.c25
-rw-r--r--kernel/trace/trace_printk.c1
-rw-r--r--kernel/user_namespace.c21
-rw-r--r--kernel/watchdog.c20
-rw-r--r--kernel/workqueue.c220
-rw-r--r--lib/842/842_decompress.c14
-rw-r--r--lib/Kconfig6
-rw-r--r--lib/Kconfig.debug76
-rw-r--r--lib/Makefile1
-rw-r--r--lib/atomic64_test.c124
-rw-r--r--lib/btree.c2
-rw-r--r--lib/dma-debug.c6
-rw-r--r--lib/dynamic_debug.c11
-rw-r--r--lib/iov_iter.c11
-rw-r--r--lib/list_debug.c2
-rw-r--r--lib/mpi/mpicoder.c21
-rw-r--r--lib/netdev-notifier-error-inject.c55
-rw-r--r--lib/proportions.c2
-rw-r--r--lib/rhashtable.c73
-rw-r--r--lib/seq_buf.c6
-rw-r--r--lib/test_bpf.c120
-rw-r--r--lib/test_rhashtable.c76
-rw-r--r--lib/vsprintf.c29
-rw-r--r--mm/backing-dev.c21
-rw-r--r--mm/bootmem.c1
-rw-r--r--mm/compaction.c18
-rw-r--r--mm/debug.c4
-rw-r--r--mm/filemap.c9
-rw-r--r--mm/huge_memory.c166
-rw-r--r--mm/hugetlb.c66
-rw-r--r--mm/kmemleak.c3
-rw-r--r--mm/ksm.c20
-rw-r--r--mm/memblock.c73
-rw-r--r--mm/memcontrol.c419
-rw-r--r--mm/memory.c47
-rw-r--r--mm/memory_hotplug.c40
-rw-r--r--mm/mempolicy.c30
-rw-r--r--mm/mlock.c2
-rw-r--r--mm/mmap.c81
-rw-r--r--mm/mmzone.c8
-rw-r--r--mm/mprotect.c8
-rw-r--r--mm/mremap.c11
-rw-r--r--mm/nobootmem.c1
-rw-r--r--mm/nommu.c2
-rw-r--r--mm/oom_kill.c7
-rw-r--r--mm/page-writeback.c16
-rw-r--r--mm/page_alloc.c161
-rw-r--r--mm/page_isolation.c22
-rw-r--r--mm/pgtable-generic.c9
-rw-r--r--mm/readahead.c9
-rw-r--r--mm/rmap.c18
-rw-r--r--mm/shmem.c298
-rw-r--r--mm/slab.c48
-rw-r--r--mm/slab.h5
-rw-r--r--mm/slab_common.c3
-rw-r--r--mm/slub.c2
-rw-r--r--mm/swapfile.c23
-rw-r--r--mm/util.c31
-rw-r--r--mm/vmalloc.c23
-rw-r--r--mm/vmpressure.c78
-rw-r--r--mm/vmscan.c40
-rw-r--r--mm/vmstat.c87
-rw-r--r--mm/zbud.c5
-rw-r--r--mm/zsmalloc.c4
-rw-r--r--mm/zswap.c6
-rw-r--r--net/6lowpan/6lowpan_i.h28
-rw-r--r--net/6lowpan/Kconfig47
-rw-r--r--net/6lowpan/Makefile9
-rw-r--r--net/6lowpan/core.c59
-rw-r--r--net/6lowpan/debugfs.c53
-rw-r--r--net/6lowpan/nhc_ghc_ext_dest.c27
-rw-r--r--net/6lowpan/nhc_ghc_ext_frag.c28
-rw-r--r--net/6lowpan/nhc_ghc_ext_hop.c27
-rw-r--r--net/6lowpan/nhc_ghc_ext_route.c27
-rw-r--r--net/6lowpan/nhc_ghc_icmpv6.c27
-rw-r--r--net/6lowpan/nhc_ghc_udp.c27
-rw-r--r--net/8021q/vlan_dev.c11
-rw-r--r--net/9p/trans_virtio.c16
-rw-r--r--net/Kconfig9
-rw-r--r--net/atm/common.c4
-rw-r--r--net/atm/mpc.h4
-rw-r--r--net/atm/mpoa_caches.c4
-rw-r--r--net/ax25/af_ax25.c3
-rw-r--r--net/batman-adv/bat_iv_ogm.c80
-rw-r--r--net/batman-adv/bridge_loop_avoidance.c55
-rw-r--r--net/batman-adv/bridge_loop_avoidance.h2
-rw-r--r--net/batman-adv/debugfs.c9
-rw-r--r--net/batman-adv/distributed-arp-table.c5
-rw-r--r--net/batman-adv/fragmentation.c8
-rw-r--r--net/batman-adv/gateway_common.c117
-rw-r--r--net/batman-adv/hard-interface.c11
-rw-r--r--net/batman-adv/main.c23
-rw-r--r--net/batman-adv/main.h4
-rw-r--r--net/batman-adv/network-coding.c4
-rw-r--r--net/batman-adv/originator.c195
-rw-r--r--net/batman-adv/originator.h7
-rw-r--r--net/batman-adv/packet.h3
-rw-r--r--net/batman-adv/routing.c25
-rw-r--r--net/batman-adv/send.c3
-rw-r--r--net/batman-adv/sysfs.c16
-rw-r--r--net/batman-adv/translation-table.c40
-rw-r--r--net/batman-adv/types.h66
-rw-r--r--net/bluetooth/6lowpan.c8
-rw-r--r--net/bluetooth/af_bluetooth.c32
-rw-r--r--net/bluetooth/bnep/core.c7
-rw-r--r--net/bluetooth/cmtp/capi.c8
-rw-r--r--net/bluetooth/cmtp/core.c3
-rw-r--r--net/bluetooth/hci_conn.c123
-rw-r--r--net/bluetooth/hci_core.c647
-rw-r--r--net/bluetooth/hci_event.c14
-rw-r--r--net/bluetooth/hci_request.c1778
-rw-r--r--net/bluetooth/hci_request.h53
-rw-r--r--net/bluetooth/hci_sock.c209
-rw-r--r--net/bluetooth/l2cap_core.c19
-rw-r--r--net/bluetooth/mgmt.c1899
-rw-r--r--net/bluetooth/rfcomm/core.c46
-rw-r--r--net/bluetooth/sco.c3
-rw-r--r--net/bluetooth/smp.c7
-rw-r--r--net/bridge/br_fdb.c1
-rw-r--r--net/bridge/br_if.c13
-rw-r--r--net/bridge/br_mdb.c23
-rw-r--r--net/bridge/br_stp.c2
-rw-r--r--net/bridge/br_stp_if.c8
-rw-r--r--net/bridge/br_sysfs_br.c3
-rw-r--r--net/bridge/br_vlan.c31
-rw-r--r--net/bridge/netfilter/ebt_ip6.c4
-rw-r--r--net/bridge/netfilter/ebt_log.c9
-rw-r--r--net/bridge/netfilter/ebt_stp.c2
-rw-r--r--net/bridge/netfilter/ebt_vlan.c15
-rw-r--r--net/bridge/netfilter/ebtable_filter.c2
-rw-r--r--net/bridge/netfilter/ebtable_nat.c2
-rw-r--r--net/bridge/netfilter/ebtables.c139
-rw-r--r--net/bridge/netfilter/nf_tables_bridge.c2
-rw-r--r--net/bridge/netfilter/nft_meta_bridge.c1
-rw-r--r--net/caif/caif_socket.c4
-rw-r--r--net/core/Makefile2
-rw-r--r--net/core/datagram.c79
-rw-r--r--net/core/dev.c414
-rw-r--r--net/core/dst.c3
-rw-r--r--net/core/ethtool.c85
-rw-r--r--net/core/filter.c299
-rw-r--r--net/core/neighbour.c4
-rw-r--r--net/core/net-sysfs.c5
-rw-r--r--net/core/net-traces.c4
-rw-r--r--net/core/netclassid_cgroup.c39
-rw-r--r--net/core/netprio_cgroup.c28
-rw-r--r--net/core/pktgen.c8
-rw-r--r--net/core/rtnetlink.c21
-rw-r--r--net/core/scm.c6
-rw-r--r--net/core/skbuff.c5
-rw-r--r--net/core/sock.c151
-rw-r--r--net/core/sock_diag.c23
-rw-r--r--net/core/sock_reuseport.c251
-rw-r--r--net/core/stream.c8
-rw-r--r--net/dccp/ipv6.c37
-rw-r--r--net/dccp/output.c2
-rw-r--r--net/dccp/proto.c3
-rw-r--r--net/decnet/af_decnet.c11
-rw-r--r--net/dns_resolver/dns_query.c2
-rw-r--r--net/dsa/dsa.c80
-rw-r--r--net/dsa/dsa_priv.h1
-rw-r--r--net/dsa/slave.c33
-rw-r--r--net/ethernet/eth.c31
-rw-r--r--net/hsr/hsr_device.c2
-rw-r--r--net/ieee802154/6lowpan/core.c6
-rw-r--r--net/ieee802154/6lowpan/reassembly.c1
-rw-r--r--net/ipv4/Kconfig13
-rw-r--r--net/ipv4/af_inet.c3
-rw-r--r--net/ipv4/fib_frontend.c9
-rw-r--r--net/ipv4/fou.c5
-rw-r--r--net/ipv4/igmp.c5
-rw-r--r--net/ipv4/inet_diag.c65
-rw-r--r--net/ipv4/inet_fragment.c10
-rw-r--r--net/ipv4/ip_fragment.c1
-rw-r--r--net/ipv4/ip_gre.c8
-rw-r--r--net/ipv4/ip_output.c5
-rw-r--r--net/ipv4/ip_tunnel.c8
-rw-r--r--net/ipv4/ip_tunnel_core.c26
-rw-r--r--net/ipv4/ip_vti.c3
-rw-r--r--net/ipv4/ipconfig.c62
-rw-r--r--net/ipv4/ipip.c4
-rw-r--r--net/ipv4/ipmr.c764
-rw-r--r--net/ipv4/netfilter/Kconfig1
-rw-r--r--net/ipv4/netfilter/arp_tables.c6
-rw-r--r--net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c2
-rw-r--r--net/ipv4/netfilter/nf_nat_l3proto_ipv4.c3
-rw-r--r--net/ipv4/netfilter/nf_nat_snmp_basic.c22
-rw-r--r--net/ipv4/netfilter/nf_reject_ipv4.c1
-rw-r--r--net/ipv4/netfilter/nf_tables_arp.c2
-rw-r--r--net/ipv4/netfilter/nf_tables_ipv4.c2
-rw-r--r--net/ipv4/ping.c2
-rw-r--r--net/ipv4/raw.c7
-rw-r--r--net/ipv4/syncookies.c4
-rw-r--r--net/ipv4/sysctl_net_ipv4.c53
-rw-r--r--net/ipv4/tcp.c60
-rw-r--r--net/ipv4/tcp_diag.c19
-rw-r--r--net/ipv4/tcp_input.c28
-rw-r--r--net/ipv4/tcp_ipv4.c44
-rw-r--r--net/ipv4/tcp_memcontrol.c91
-rw-r--r--net/ipv4/tcp_minisocks.c7
-rw-r--r--net/ipv4/tcp_output.c44
-rw-r--r--net/ipv4/tcp_timer.c17
-rw-r--r--net/ipv4/tcp_yeah.c2
-rw-r--r--net/ipv4/udp.c152
-rw-r--r--net/ipv4/udp_diag.c4
-rw-r--r--net/ipv4/udp_offload.c15
-rw-r--r--net/ipv4/udp_tunnel.c11
-rw-r--r--net/ipv4/xfrm4_policy.c46
-rw-r--r--net/ipv6/Kconfig1
-rw-r--r--net/ipv6/Makefile2
-rw-r--r--net/ipv6/addrconf.c98
-rw-r--r--net/ipv6/addrlabel.c2
-rw-r--r--net/ipv6/af_inet6.c18
-rw-r--r--net/ipv6/datagram.c4
-rw-r--r--net/ipv6/exthdrs.c3
-rw-r--r--net/ipv6/icmp.c14
-rw-r--r--net/ipv6/ila/Makefile7
-rw-r--r--net/ipv6/ila/ila.h48
-rw-r--r--net/ipv6/ila/ila_common.c103
-rw-r--r--net/ipv6/ila/ila_lwt.c (renamed from net/ipv6/ila.c)83
-rw-r--r--net/ipv6/ila/ila_xlat.c680
-rw-r--r--net/ipv6/inet6_connection_sock.c25
-rw-r--r--net/ipv6/ip6_gre.c9
-rw-r--r--net/ipv6/ip6_output.c4
-rw-r--r--net/ipv6/ip6_tunnel.c2
-rw-r--r--net/ipv6/ip6mr.c19
-rw-r--r--net/ipv6/ipv6_sockglue.c33
-rw-r--r--net/ipv6/ndisc.c14
-rw-r--r--net/ipv6/netfilter/Kconfig1
-rw-r--r--net/ipv6/netfilter/nf_conntrack_reasm.c172
-rw-r--r--net/ipv6/netfilter/nf_defrag_ipv6_hooks.c20
-rw-r--r--net/ipv6/netfilter/nf_nat_l3proto_ipv6.c3
-rw-r--r--net/ipv6/netfilter/nf_reject_ipv6.c1
-rw-r--r--net/ipv6/netfilter/nf_tables_ipv6.c2
-rw-r--r--net/ipv6/raw.c28
-rw-r--r--net/ipv6/reassembly.c11
-rw-r--r--net/ipv6/route.c22
-rw-r--r--net/ipv6/sit.c7
-rw-r--r--net/ipv6/syncookies.c6
-rw-r--r--net/ipv6/tcp_ipv6.c57
-rw-r--r--net/ipv6/udp.c78
-rw-r--r--net/ipv6/xfrm6_policy.c53
-rw-r--r--net/irda/af_irda.c3
-rw-r--r--net/iucv/af_iucv.c26
-rw-r--r--net/l2tp/l2tp_ip6.c8
-rw-r--r--net/l2tp/l2tp_ppp.c19
-rw-r--r--net/mac80211/agg-tx.c3
-rw-r--r--net/mac80211/cfg.c531
-rw-r--r--net/mac80211/debugfs.c1
-rw-r--r--net/mac80211/ibss.c1
-rw-r--r--net/mac80211/ieee80211_i.h29
-rw-r--r--net/mac80211/iface.c5
-rw-r--r--net/mac80211/key.c56
-rw-r--r--net/mac80211/main.c1
-rw-r--r--net/mac80211/mesh_pathtbl.c12
-rw-r--r--net/mac80211/mlme.c23
-rw-r--r--net/mac80211/offchannel.c829
-rw-r--r--net/mac80211/rx.c6
-rw-r--r--net/mac80211/scan.c9
-rw-r--r--net/mac80211/sta_info.c163
-rw-r--r--net/mac80211/sta_info.h2
-rw-r--r--net/mac80211/trace.h25
-rw-r--r--net/mac80211/tx.c2
-rw-r--r--net/mac80211/util.c120
-rw-r--r--net/mac80211/vht.c10
-rw-r--r--net/mac802154/driver-ops.h3
-rw-r--r--net/mac802154/mac_cmd.c2
-rw-r--r--net/mac802154/rx.c3
-rw-r--r--net/mac802154/tx.c9
-rw-r--r--net/mpls/af_mpls.c228
-rw-r--r--net/mpls/internal.h2
-rw-r--r--net/mpls/mpls_iptunnel.c6
-rw-r--r--net/netfilter/Kconfig22
-rw-r--r--net/netfilter/Makefile9
-rw-r--r--net/netfilter/ipset/ip_set_core.c108
-rw-r--r--net/netfilter/ipvs/ip_vs_proto_sctp.c2
-rw-r--r--net/netfilter/nf_conntrack_expect.c7
-rw-r--r--net/netfilter/nf_conntrack_ftp.c17
-rw-r--r--net/netfilter/nf_conntrack_irc.c7
-rw-r--r--net/netfilter/nf_conntrack_netlink.c96
-rw-r--r--net/netfilter/nf_conntrack_sane.c19
-rw-r--r--net/netfilter/nf_conntrack_sip.c5
-rw-r--r--net/netfilter/nf_conntrack_standalone.c7
-rw-r--r--net/netfilter/nf_conntrack_tftp.c7
-rw-r--r--net/netfilter/nf_conntrack_timeout.c2
-rw-r--r--net/netfilter/nf_dup_netdev.c40
-rw-r--r--net/netfilter/nf_tables_api.c263
-rw-r--r--net/netfilter/nf_tables_core.c62
-rw-r--r--net/netfilter/nf_tables_inet.c2
-rw-r--r--net/netfilter/nf_tables_netdev.c49
-rw-r--r--net/netfilter/nf_tables_trace.c275
-rw-r--r--net/netfilter/nfnetlink.c30
-rw-r--r--net/netfilter/nfnetlink_acct.c21
-rw-r--r--net/netfilter/nfnetlink_cthelper.c18
-rw-r--r--net/netfilter/nfnetlink_cttimeout.c120
-rw-r--r--net/netfilter/nfnetlink_log.c40
-rw-r--r--net/netfilter/nfnetlink_queue.c132
-rw-r--r--net/netfilter/nft_byteorder.c23
-rw-r--r--net/netfilter/nft_compat.c6
-rw-r--r--net/netfilter/nft_ct.c39
-rw-r--r--net/netfilter/nft_dup_netdev.c97
-rw-r--r--net/netfilter/nft_fwd_netdev.c98
-rw-r--r--net/netfilter/nft_limit.c16
-rw-r--r--net/netfilter/nft_meta.c56
-rw-r--r--net/netfilter/nft_payload.c135
-rw-r--r--net/netfilter/x_tables.c12
-rw-r--r--net/netfilter/xt_CT.c2
-rw-r--r--net/netfilter/xt_cgroup.c108
-rw-r--r--net/netfilter/xt_osf.c7
-rw-r--r--net/netlink/af_netlink.c4
-rw-r--r--net/netlink/genetlink.c16
-rw-r--r--net/nfc/core.c13
-rw-r--r--net/nfc/digital_core.c3
-rw-r--r--net/nfc/llcp_sock.c2
-rw-r--r--net/nfc/nci/core.c6
-rw-r--r--net/nfc/nci/hci.c2
-rw-r--r--net/nfc/netlink.c37
-rw-r--r--net/nfc/nfc.h1
-rw-r--r--net/openvswitch/conntrack.c47
-rw-r--r--net/openvswitch/dp_notify.c2
-rw-r--r--net/openvswitch/flow_netlink.c5
-rw-r--r--net/openvswitch/vport-geneve.c7
-rw-r--r--net/openvswitch/vport-gre.c1
-rw-r--r--net/openvswitch/vport-netdev.c10
-rw-r--r--net/openvswitch/vport.c8
-rw-r--r--net/openvswitch/vport.h31
-rw-r--r--net/packet/af_packet.c4
-rw-r--r--net/phonet/af_phonet.c4
-rw-r--r--net/rds/connection.c6
-rw-r--r--net/rds/page.c31
-rw-r--r--net/rds/send.c4
-rw-r--r--net/rfkill/core.c6
-rw-r--r--net/rfkill/rfkill-gpio.c4
-rw-r--r--net/rxrpc/af_rxrpc.c2
-rw-r--r--net/rxrpc/ar-ack.c4
-rw-r--r--net/rxrpc/ar-key.c24
-rw-r--r--net/rxrpc/ar-output.c2
-rw-r--r--net/sched/Kconfig14
-rw-r--r--net/sched/cls_bpf.c8
-rw-r--r--net/sched/cls_flower.c10
-rw-r--r--net/sched/sch_api.c25
-rw-r--r--net/sched/sch_generic.c6
-rw-r--r--net/sched/sch_ingress.c88
-rw-r--r--net/sched/sch_mq.c4
-rw-r--r--net/sched/sch_mqprio.c4
-rw-r--r--net/sctp/associola.c5
-rw-r--r--net/sctp/endpointola.c52
-rw-r--r--net/sctp/input.c187
-rw-r--r--net/sctp/ipv6.c24
-rw-r--r--net/sctp/output.c2
-rw-r--r--net/sctp/outqueue.c2
-rw-r--r--net/sctp/proc.c316
-rw-r--r--net/sctp/protocol.c41
-rw-r--r--net/sctp/sm_make_chunk.c4
-rw-r--r--net/sctp/sm_sideeffect.c18
-rw-r--r--net/sctp/sm_statefuns.c26
-rw-r--r--net/sctp/socket.c73
-rw-r--r--net/sctp/sysctl.c9
-rw-r--r--net/socket.c27
-rw-r--r--net/sunrpc/auth_gss/gss_rpc_upcall.c3
-rw-r--r--net/sunrpc/clnt.c1
-rw-r--r--net/sunrpc/rpc_pipe.c2
-rw-r--r--net/sunrpc/sched.c6
-rw-r--r--net/sunrpc/svc.c13
-rw-r--r--net/sunrpc/svc_xprt.c45
-rw-r--r--net/sunrpc/svcauth.c2
-rw-r--r--net/sunrpc/svcauth_unix.c8
-rw-r--r--net/sunrpc/xprtrdma/backchannel.c26
-rw-r--r--net/sunrpc/xprtrdma/fmr_ops.c64
-rw-r--r--net/sunrpc/xprtrdma/frwr_ops.c174
-rw-r--r--net/sunrpc/xprtrdma/physical_ops.c13
-rw-r--r--net/sunrpc/xprtrdma/rpc_rdma.c16
-rw-r--r--net/sunrpc/xprtrdma/transport.c3
-rw-r--r--net/sunrpc/xprtrdma/verbs.c16
-rw-r--r--net/sunrpc/xprtrdma/xprt_rdma.h14
-rw-r--r--net/sunrpc/xprtsock.c63
-rw-r--r--net/switchdev/switchdev.c14
-rw-r--r--net/tipc/bcast.c126
-rw-r--r--net/tipc/bcast.h1
-rw-r--r--net/tipc/bearer.c140
-rw-r--r--net/tipc/bearer.h8
-rw-r--r--net/tipc/core.h5
-rw-r--r--net/tipc/discover.c38
-rw-r--r--net/tipc/link.c628
-rw-r--r--net/tipc/link.h175
-rw-r--r--net/tipc/name_distr.c68
-rw-r--r--net/tipc/name_distr.h1
-rw-r--r--net/tipc/name_table.c5
-rw-r--r--net/tipc/netlink.c8
-rw-r--r--net/tipc/netlink_compat.c8
-rw-r--r--net/tipc/node.c875
-rw-r--r--net/tipc/node.h127
-rw-r--r--net/tipc/socket.c14
-rw-r--r--net/tipc/udp_media.c24
-rw-r--r--net/unix/af_unix.c414
-rw-r--r--net/unix/garbage.c13
-rw-r--r--net/vmw_vsock/vmci_transport.h2
-rw-r--r--net/vmw_vsock/vmci_transport_notify.c2
-rw-r--r--net/vmw_vsock/vmci_transport_notify.h5
-rw-r--r--net/vmw_vsock/vmci_transport_notify_qstate.c2
-rw-r--r--net/wireless/core.h7
-rw-r--r--net/wireless/lib80211_crypt_ccmp.c4
-rw-r--r--net/wireless/lib80211_crypt_tkip.c4
-rw-r--r--net/wireless/nl80211.c67
-rw-r--r--net/wireless/ocb.c3
-rw-r--r--net/wireless/rdev-ops.h51
-rw-r--r--net/wireless/reg.c103
-rw-r--r--net/wireless/trace.h103
-rw-r--r--net/wireless/util.c121
-rw-r--r--net/xfrm/xfrm_policy.c88
-rw-r--r--scripts/Makefile.lib3
-rwxr-xr-xscripts/bloat-o-meter8
-rwxr-xr-xscripts/checkkconfigsymbols.py234
-rwxr-xr-xscripts/ld-version.sh4
-rwxr-xr-xscripts/link-vmlinux.sh2
-rw-r--r--scripts/mod/file2alias.c5
-rw-r--r--scripts/recordmcount.c137
-rw-r--r--security/integrity/iint.c11
-rw-r--r--security/keys/encrypted-keys/encrypted.c2
-rw-r--r--security/keys/keyctl.c18
-rw-r--r--security/keys/trusted.c5
-rw-r--r--security/keys/user_defined.c5
-rw-r--r--security/selinux/selinuxfs.c114
-rw-r--r--security/selinux/ss/conditional.c4
-rw-r--r--security/smack/smack_lsm.c2
-rw-r--r--security/smack/smackfs.c114
-rw-r--r--security/tomoyo/securityfs_if.c11
-rw-r--r--sound/core/pcm_dmaengine.c9
-rw-r--r--sound/drivers/pcm-indirect2.c2
-rw-r--r--sound/firewire/dice/dice.c4
-rw-r--r--sound/pci/es1968.c4
-rw-r--r--sound/pci/fm801.c4
-rw-r--r--sound/pci/hda/hda_intel.c64
-rw-r--r--sound/pci/hda/patch_ca0132.c3
-rw-r--r--sound/pci/hda/patch_conexant.c5
-rw-r--r--sound/pci/hda/patch_hdmi.c9
-rw-r--r--sound/pci/hda/patch_realtek.c197
-rw-r--r--sound/pci/hda/patch_sigmatel.c45
-rw-r--r--sound/pci/rme96.c41
-rw-r--r--sound/soc/codecs/arizona.c18
-rw-r--r--sound/soc/codecs/es8328.c41
-rw-r--r--sound/soc/codecs/es8328.h1
-rw-r--r--sound/soc/codecs/nau8825.c31
-rw-r--r--sound/soc/codecs/rl6231.c6
-rw-r--r--sound/soc/codecs/rt5645.c65
-rw-r--r--sound/soc/codecs/rt5645.h4
-rw-r--r--sound/soc/codecs/rt5670.h12
-rw-r--r--sound/soc/codecs/rt5677.c100
-rw-r--r--sound/soc/codecs/sgtl5000.c1
-rw-r--r--sound/soc/codecs/wm8960.c2
-rw-r--r--sound/soc/codecs/wm8962.c4
-rw-r--r--sound/soc/codecs/wm8974.c1
-rw-r--r--sound/soc/davinci/davinci-mcasp.c16
-rw-r--r--sound/soc/fsl/Kconfig2
-rw-r--r--sound/soc/fsl/fsl_sai.c21
-rw-r--r--sound/soc/intel/Kconfig2
-rw-r--r--sound/soc/intel/skylake/skl-topology.c2
-rw-r--r--sound/soc/intel/skylake/skl.c4
-rw-r--r--sound/soc/intel/skylake/skl.h2
-rw-r--r--sound/soc/rockchip/rockchip_spdif.c8
-rw-r--r--sound/soc/rockchip/rockchip_spdif.h8
-rw-r--r--sound/soc/sh/rcar/gen.c2
-rw-r--r--sound/soc/sh/rcar/src.c7
-rw-r--r--sound/soc/soc-core.c6
-rw-r--r--sound/soc/soc-dapm.c7
-rw-r--r--sound/soc/soc-ops.c2
-rw-r--r--sound/soc/soc-topology.c3
-rw-r--r--sound/soc/sti/uniperif_player.c9
-rw-r--r--sound/soc/sti/uniperif_reader.c3
-rw-r--r--sound/soc/sunxi/sun4i-codec.c27
-rw-r--r--sound/usb/6fire/firmware.c2
-rw-r--r--sound/usb/midi.c46
-rw-r--r--sound/usb/mixer.c2
-rw-r--r--sound/usb/mixer_maps.c12
-rw-r--r--sound/usb/mixer_quirks.c37
-rw-r--r--sound/usb/mixer_quirks.h4
-rw-r--r--sound/usb/quirks-table.h11
-rw-r--r--sound/usb/quirks.c2
-rw-r--r--sound/usb/usbaudio.h1
-rw-r--r--tools/Makefile52
-rw-r--r--tools/build/Makefile2
-rw-r--r--tools/build/Makefile.feature44
-rw-r--r--tools/build/Makefile.include2
-rw-r--r--tools/build/feature/Makefile93
-rw-r--r--tools/build/feature/test-all.c5
-rw-r--r--tools/build/feature/test-bpf.c20
-rw-r--r--tools/hv/hv_fcopy_daemon.c24
-rw-r--r--tools/hv/hv_vss_daemon.c2
-rw-r--r--tools/include/linux/bitmap.h (renamed from tools/perf/util/include/linux/bitmap.h)2
-rw-r--r--tools/include/linux/list.h753
-rw-r--r--tools/include/linux/string.h15
-rw-r--r--tools/lib/bitmap.c (renamed from tools/perf/util/bitmap.c)0
-rw-r--r--tools/lib/bpf/Makefile32
-rw-r--r--tools/lib/bpf/bpf.c18
-rw-r--r--tools/lib/bpf/bpf.h2
-rw-r--r--tools/lib/bpf/libbpf.c412
-rw-r--r--tools/lib/bpf/libbpf.h88
-rw-r--r--tools/lib/find_bit.c84
-rw-r--r--tools/lib/lockdep/Makefile2
-rw-r--r--tools/lib/string.c89
-rw-r--r--tools/lib/subcmd/Build7
-rw-r--r--tools/lib/subcmd/Makefile48
-rw-r--r--tools/lib/subcmd/exec-cmd.c209
-rw-r--r--tools/lib/subcmd/exec-cmd.h16
-rw-r--r--tools/lib/subcmd/help.c (renamed from tools/perf/util/help.c)179
-rw-r--r--tools/lib/subcmd/help.h (renamed from tools/perf/util/help.h)13
-rw-r--r--tools/lib/subcmd/pager.c (renamed from tools/perf/util/pager.c)23
-rw-r--r--tools/lib/subcmd/pager.h9
-rw-r--r--tools/lib/subcmd/parse-options.c (renamed from tools/perf/util/parse-options.c)250
-rw-r--r--tools/lib/subcmd/parse-options.h (renamed from tools/perf/util/parse-options.h)30
-rw-r--r--tools/lib/subcmd/run-command.c (renamed from tools/perf/util/run-command.c)24
-rw-r--r--tools/lib/subcmd/run-command.h (renamed from tools/perf/util/run-command.h)12
-rw-r--r--tools/lib/subcmd/sigchain.c (renamed from tools/perf/util/sigchain.c)3
-rw-r--r--tools/lib/subcmd/sigchain.h (renamed from tools/perf/util/sigchain.h)6
-rw-r--r--tools/lib/subcmd/subcmd-config.c11
-rw-r--r--tools/lib/subcmd/subcmd-config.h14
-rw-r--r--tools/lib/subcmd/subcmd-util.h91
-rw-r--r--tools/lib/traceevent/event-parse.c134
-rw-r--r--tools/lib/traceevent/event-parse.h4
-rw-r--r--tools/lib/util/find_next_bit.c89
-rw-r--r--tools/perf/Build9
-rw-r--r--tools/perf/Documentation/perf-config.txt103
-rw-r--r--tools/perf/Documentation/perf-evlist.txt3
-rw-r--r--tools/perf/Documentation/perf-record.txt27
-rw-r--r--tools/perf/Documentation/perf-report.txt41
-rw-r--r--tools/perf/Documentation/perf-stat.txt34
-rw-r--r--tools/perf/Documentation/perf-top.txt3
-rw-r--r--tools/perf/Documentation/tips.txt29
-rw-r--r--tools/perf/MANIFEST9
-rw-r--r--tools/perf/Makefile.perf44
-rw-r--r--tools/perf/arch/x86/include/arch-tests.h8
-rw-r--r--tools/perf/arch/x86/tests/insn-x86.c2
-rw-r--r--tools/perf/arch/x86/tests/intel-cqm.c4
-rw-r--r--tools/perf/arch/x86/tests/perf-time-to-tsc.c3
-rw-r--r--tools/perf/arch/x86/tests/rdpmc.c2
-rw-r--r--tools/perf/arch/x86/util/Build1
-rw-r--r--tools/perf/arch/x86/util/intel-bts.c4
-rw-r--r--tools/perf/arch/x86/util/intel-pt.c6
-rw-r--r--tools/perf/bench/futex-hash.c2
-rw-r--r--tools/perf/bench/futex-lock-pi.c2
-rw-r--r--tools/perf/bench/futex-requeue.c2
-rw-r--r--tools/perf/bench/futex-wake-parallel.c2
-rw-r--r--tools/perf/bench/futex-wake.c2
-rw-r--r--tools/perf/bench/mem-functions.c2
-rw-r--r--tools/perf/bench/numa.c2
-rw-r--r--tools/perf/bench/sched-messaging.c2
-rw-r--r--tools/perf/bench/sched-pipe.c2
-rw-r--r--tools/perf/builtin-annotate.c44
-rw-r--r--tools/perf/builtin-bench.c2
-rw-r--r--tools/perf/builtin-buildid-cache.c2
-rw-r--r--tools/perf/builtin-buildid-list.c4
-rw-r--r--tools/perf/builtin-config.c66
-rw-r--r--tools/perf/builtin-data.c2
-rw-r--r--tools/perf/builtin-diff.c15
-rw-r--r--tools/perf/builtin-evlist.c13
-rw-r--r--tools/perf/builtin-help.c10
-rw-r--r--tools/perf/builtin-inject.c2
-rw-r--r--tools/perf/builtin-kmem.c2
-rw-r--r--tools/perf/builtin-kvm.c5
-rw-r--r--tools/perf/builtin-list.c2
-rw-r--r--tools/perf/builtin-lock.c2
-rw-r--r--tools/perf/builtin-mem.c2
-rw-r--r--tools/perf/builtin-probe.c17
-rw-r--r--tools/perf/builtin-record.c74
-rw-r--r--tools/perf/builtin-report.c60
-rw-r--r--tools/perf/builtin-sched.c2
-rw-r--r--tools/perf/builtin-script.c245
-rw-r--r--tools/perf/builtin-stat.c679
-rw-r--r--tools/perf/builtin-timechart.c2
-rw-r--r--tools/perf/builtin-top.c75
-rw-r--r--tools/perf/builtin-trace.c4
-rw-r--r--tools/perf/builtin-version.c10
-rw-r--r--tools/perf/builtin.h1
-rw-r--r--tools/perf/command-list.txt3
-rw-r--r--tools/perf/config/Makefile31
-rw-r--r--tools/perf/config/utilities.mak19
-rw-r--r--tools/perf/perf.c24
-rw-r--r--tools/perf/scripts/python/stat-cpi.py77
-rw-r--r--tools/perf/tests/.gitignore1
-rw-r--r--tools/perf/tests/Build16
-rw-r--r--tools/perf/tests/attr.c6
-rw-r--r--tools/perf/tests/bp_signal.c2
-rw-r--r--tools/perf/tests/bp_signal_overflow.c2
-rw-r--r--tools/perf/tests/bpf-script-test-prologue.c35
-rw-r--r--tools/perf/tests/bpf.c93
-rw-r--r--tools/perf/tests/builtin-test.c141
-rw-r--r--tools/perf/tests/code-reading.c16
-rw-r--r--tools/perf/tests/cpumap.c88
-rw-r--r--tools/perf/tests/dso-data.c6
-rw-r--r--tools/perf/tests/dwarf-unwind.c37
-rw-r--r--tools/perf/tests/event_update.c117
-rw-r--r--tools/perf/tests/evsel-roundtrip-name.c5
-rw-r--r--tools/perf/tests/evsel-tp-sched.c2
-rw-r--r--tools/perf/tests/fdarray.c4
-rw-r--r--tools/perf/tests/hists_common.c1
-rw-r--r--tools/perf/tests/hists_cumulate.c11
-rw-r--r--tools/perf/tests/hists_filter.c5
-rw-r--r--tools/perf/tests/hists_link.c11
-rw-r--r--tools/perf/tests/hists_output.c13
-rw-r--r--tools/perf/tests/keep-tracking.c5
-rw-r--r--tools/perf/tests/kmod-path.c2
-rw-r--r--tools/perf/tests/llvm.c75
-rw-r--r--tools/perf/tests/llvm.h2
-rw-r--r--tools/perf/tests/make21
-rw-r--r--tools/perf/tests/mmap-basic.c2
-rw-r--r--tools/perf/tests/mmap-thread-lookup.c8
-rw-r--r--tools/perf/tests/openat-syscall-all-cpus.c2
-rw-r--r--tools/perf/tests/openat-syscall-tp-fields.c2
-rw-r--r--tools/perf/tests/openat-syscall.c2
-rw-r--r--tools/perf/tests/parse-events.c2
-rw-r--r--tools/perf/tests/parse-no-sample-id-all.c2
-rw-r--r--tools/perf/tests/perf-record.c8
-rw-r--r--tools/perf/tests/pmu.c2
-rw-r--r--tools/perf/tests/python-use.c3
-rw-r--r--tools/perf/tests/sample-parsing.c2
-rw-r--r--tools/perf/tests/stat.c111
-rw-r--r--tools/perf/tests/sw-clock.c2
-rw-r--r--tools/perf/tests/switch-tracking.c8
-rw-r--r--tools/perf/tests/task-exit.c2
-rw-r--r--tools/perf/tests/tests.h95
-rw-r--r--tools/perf/tests/thread-map.c45
-rw-r--r--tools/perf/tests/thread-mg-share.c2
-rw-r--r--tools/perf/tests/topology.c2
-rw-r--r--tools/perf/tests/vmlinux-kallsyms.c2
-rw-r--r--tools/perf/ui/browser.c2
-rw-r--r--tools/perf/ui/browsers/hists.c337
-rw-r--r--tools/perf/ui/gtk/hists.c152
-rw-r--r--tools/perf/ui/hist.c14
-rw-r--r--tools/perf/ui/stdio/hist.c100
-rw-r--r--tools/perf/util/Build28
-rw-r--r--tools/perf/util/annotate.c23
-rw-r--r--tools/perf/util/auxtrace.c2
-rw-r--r--tools/perf/util/bpf-loader.c433
-rw-r--r--tools/perf/util/bpf-loader.h4
-rw-r--r--tools/perf/util/bpf-prologue.c455
-rw-r--r--tools/perf/util/bpf-prologue.h34
-rw-r--r--tools/perf/util/build-id.c2
-rw-r--r--tools/perf/util/cache.h14
-rw-r--r--tools/perf/util/callchain.c164
-rw-r--r--tools/perf/util/callchain.h30
-rw-r--r--tools/perf/util/cgroup.c2
-rw-r--r--tools/perf/util/color.c2
-rw-r--r--tools/perf/util/config.c2
-rw-r--r--tools/perf/util/cpumap.c51
-rw-r--r--tools/perf/util/cpumap.h1
-rw-r--r--tools/perf/util/data-convert-bt.c2
-rw-r--r--tools/perf/util/dso.c2
-rw-r--r--tools/perf/util/env.c9
-rw-r--r--tools/perf/util/environment.c8
-rw-r--r--tools/perf/util/event.c312
-rw-r--r--tools/perf/util/event.h150
-rw-r--r--tools/perf/util/evlist.c112
-rw-r--r--tools/perf/util/evlist.h10
-rw-r--r--tools/perf/util/evsel.c53
-rw-r--r--tools/perf/util/evsel.h4
-rw-r--r--tools/perf/util/exec_cmd.c148
-rw-r--r--tools/perf/util/exec_cmd.h12
-rwxr-xr-xtools/perf/util/generate-cmdlist.sh15
-rw-r--r--tools/perf/util/header.c207
-rw-r--r--tools/perf/util/header.h17
-rw-r--r--tools/perf/util/help-unknown-cmd.c103
-rw-r--r--tools/perf/util/help-unknown-cmd.h0
-rw-r--r--tools/perf/util/hist.c118
-rw-r--r--tools/perf/util/hist.h24
-rw-r--r--tools/perf/util/include/linux/string.h3
-rw-r--r--tools/perf/util/intel-pt.c4
-rw-r--r--tools/perf/util/machine.c74
-rw-r--r--tools/perf/util/map.c7
-rw-r--r--tools/perf/util/parse-branch-options.c2
-rw-r--r--tools/perf/util/parse-events.c10
-rw-r--r--tools/perf/util/parse-regs-options.c2
-rw-r--r--tools/perf/util/path.c18
-rw-r--r--tools/perf/util/pmu.c1
-rw-r--r--tools/perf/util/probe-event.c7
-rw-r--r--tools/perf/util/probe-finder.c6
-rw-r--r--tools/perf/util/python-ext-sources2
-rw-r--r--tools/perf/util/scripting-engines/trace-event-python.c115
-rw-r--r--tools/perf/util/session.c200
-rw-r--r--tools/perf/util/session.h2
-rw-r--r--tools/perf/util/sort.c601
-rw-r--r--tools/perf/util/sort.h14
-rw-r--r--tools/perf/util/stat.c62
-rw-r--r--tools/perf/util/stat.h10
-rw-r--r--tools/perf/util/string.c16
-rw-r--r--tools/perf/util/strlist.c8
-rw-r--r--tools/perf/util/strlist.h9
-rw-r--r--tools/perf/util/symbol-elf.c9
-rw-r--r--tools/perf/util/symbol.c66
-rw-r--r--tools/perf/util/symbol.h4
-rw-r--r--tools/perf/util/term.c35
-rw-r--r--tools/perf/util/term.h10
-rw-r--r--tools/perf/util/thread.c10
-rw-r--r--tools/perf/util/thread_map.c28
-rw-r--r--tools/perf/util/thread_map.h3
-rw-r--r--tools/perf/util/tool.h8
-rw-r--r--tools/perf/util/trace-event.h4
-rw-r--r--tools/perf/util/unwind-libdw.c63
-rw-r--r--tools/perf/util/unwind-libdw.h2
-rw-r--r--tools/perf/util/unwind-libunwind.c80
-rw-r--r--tools/perf/util/util.c68
-rw-r--r--tools/perf/util/util.h20
-rw-r--r--tools/power/acpi/Makefile16
-rw-r--r--tools/power/acpi/common/getopt.c2
-rw-r--r--tools/power/acpi/os_specific/service_layers/oslibcfs.c3
-rw-r--r--tools/power/acpi/tools/acpidbg/Makefile27
-rw-r--r--tools/power/acpi/tools/acpidbg/acpidbg.c438
-rw-r--r--tools/power/acpi/tools/acpidump/apfiles.c13
-rw-r--r--tools/power/cpupower/Makefile19
-rw-r--r--tools/power/cpupower/bench/Makefile8
-rw-r--r--tools/power/cpupower/utils/cpufreq-info.c246
-rw-r--r--tools/power/cpupower/utils/cpuidle-info.c16
-rw-r--r--tools/power/cpupower/utils/cpupower-info.c9
-rw-r--r--tools/power/cpupower/utils/cpupower-set.c10
-rw-r--r--tools/power/cpupower/utils/helpers/topology.c2
-rw-r--r--tools/scripts/Makefile.arch (renamed from tools/perf/config/Makefile.arch)0
-rw-r--r--tools/spi/.gitignore (renamed from Documentation/spi/.gitignore)0
-rw-r--r--tools/spi/Makefile4
-rw-r--r--tools/spi/spidev_fdx.c (renamed from Documentation/spi/spidev_fdx.c)0
-rw-r--r--tools/spi/spidev_test.c (renamed from Documentation/spi/spidev_test.c)119
-rw-r--r--tools/testing/nvdimm/Kbuild2
-rw-r--r--tools/testing/nvdimm/test/iomap.c93
-rw-r--r--tools/testing/nvdimm/test/nfit.c60
-rw-r--r--tools/testing/selftests/ftrace/test.d/instances/instance.tc90
-rw-r--r--tools/testing/selftests/futex/README2
-rw-r--r--tools/testing/selftests/net/.gitignore1
-rw-r--r--tools/testing/selftests/net/Makefile2
-rw-r--r--tools/testing/selftests/net/reuseport_bpf.c514
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh9
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/kvm.sh22
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/parse-console.sh41
-rw-r--r--tools/testing/selftests/rcutorture/doc/TINY_RCU.txt1
-rw-r--r--tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt4
-rw-r--r--tools/testing/selftests/seccomp/seccomp_bpf.c38
-rw-r--r--tools/testing/selftests/timers/clocksource-switch.c2
-rw-r--r--tools/testing/selftests/x86/Makefile6
-rw-r--r--tools/testing/selftests/x86/vdso_restorer.c88
-rw-r--r--tools/virtio/linux/kernel.h6
-rw-r--r--tools/virtio/linux/virtio.h6
-rw-r--r--tools/virtio/linux/virtio_config.h20
-rw-r--r--virt/kvm/arm/arch_timer.c28
-rw-r--r--virt/kvm/arm/vgic-v3.c11
-rw-r--r--virt/kvm/arm/vgic.c52
-rw-r--r--virt/kvm/async_pf.c3
-rw-r--r--virt/kvm/irqchip.c7
-rw-r--r--virt/kvm/kvm_main.c46
6635 files changed, 228142 insertions, 96172 deletions
diff --git a/CREDITS b/CREDITS
index 8207cc62ee9d..25133c5adae7 100644
--- a/CREDITS
+++ b/CREDITS
@@ -534,6 +534,7 @@ N: NeilBrown
E: neil@brown.name
P: 4096R/566281B9 1BC6 29EB D390 D870 7B5F 497A 39EC 9EDD 5662 81B9
D: NFSD Maintainer 2000-2007
+D: MD Maintainer 2001-2016
N: Zach Brown
E: zab@zabbo.net
@@ -1507,6 +1508,14 @@ S: 312/107 Canberra Avenue
S: Griffith, ACT 2603
S: Australia
+N: Andreas Herrmann
+E: herrmann.der.user@gmail.com
+E: herrmann.der.user@googlemail.com
+D: Key developer of x86/AMD64
+D: Author of AMD family 15h processor power monitoring driver
+D: Maintainer of AMD Athlon 64 and Opteron processor frequency driver
+S: Germany
+
N: Sebastian Hetze
E: she@lunetix.de
D: German Linux Documentation,
diff --git a/Documentation/ABI/testing/configfs-iio b/Documentation/ABI/testing/configfs-iio
new file mode 100644
index 000000000000..2483756fccf5
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-iio
@@ -0,0 +1,21 @@
+What: /config/iio
+Date: October 2015
+KernelVersion: 4.4
+Contact: linux-iio@vger.kernel.org
+Description:
+ This represents Industrial IO configuration entry point
+ directory. It contains sub-groups corresponding to IIO
+ objects.
+
+What: /config/iio/triggers
+Date: October 2015
+KernelVersion: 4.4
+Description:
+ Industrial IO software triggers directory.
+
+What: /config/iio/triggers/hrtimers
+Date: October 2015
+KernelVersion: 4.4
+Description:
+ High resolution timers directory. Creating a directory here
+ will result in creating a hrtimer trigger in the IIO subsystem.
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-sourcesink b/Documentation/ABI/testing/configfs-usb-gadget-sourcesink
index bc7ff731aa0c..f56335af2d88 100644
--- a/Documentation/ABI/testing/configfs-usb-gadget-sourcesink
+++ b/Documentation/ABI/testing/configfs-usb-gadget-sourcesink
@@ -10,3 +10,5 @@ Description:
isoc_mult - 0..2 (hs/ss only)
isoc_maxburst - 0..15 (ss only)
buflen - buffer length
+ bulk_qlen - depth of queue for bulk
+ iso_qlen - depth of queue for iso
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-ina2xx-adc b/Documentation/ABI/testing/sysfs-bus-iio-ina2xx-adc
new file mode 100644
index 000000000000..8916f7ec6507
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-ina2xx-adc
@@ -0,0 +1,24 @@
+What: /sys/bus/iio/devices/iio:deviceX/in_allow_async_readout
+Date: December 2015
+KernelVersion: 4.4
+Contact: linux-iio@vger.kernel.org
+Description:
+ By default (value '0'), the capture thread checks for the Conversion
+ Ready Flag to being set prior to committing a new value to the sample
+ buffer. This synchronizes the in-chip conversion rate with the
+ in-driver readout rate at the cost of an additional register read.
+
+ Writing '1' will remove the polling for the Conversion Ready Flags to
+ save the additional i2c transaction, which will improve the bandwidth
+ available for reading data. However, samples can be occasionally skipped
+ or repeated, depending on the beat between the capture and conversion
+ rates.
+
+What: /sys/bus/iio/devices/iio:deviceX/in_shunt_resistor
+Date: December 2015
+KernelVersion: 4.4
+Contact: linux-iio@vger.kernel.org
+Description:
+ The value of the shunt resistor may be known only at runtime fom an
+ eeprom content read by a client application. This attribute allows to
+ set its value in ohms.
diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb
index 3a4abfc44f5e..0bd731cbb50c 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -134,19 +134,21 @@ Description:
enabled for the device. Developer can write y/Y/1 or n/N/0 to
the file to enable/disable the feature.
-What: /sys/bus/usb/devices/.../power/usb3_hardware_lpm
-Date: June 2015
+What: /sys/bus/usb/devices/.../power/usb3_hardware_lpm_u1
+ /sys/bus/usb/devices/.../power/usb3_hardware_lpm_u2
+Date: November 2015
Contact: Kevin Strasser <kevin.strasser@linux.intel.com>
+ Lu Baolu <baolu.lu@linux.intel.com>
Description:
If CONFIG_PM is set and a USB 3.0 lpm-capable device is plugged
in to a xHCI host which supports link PM, it will check if U1
and U2 exit latencies have been set in the BOS descriptor; if
- the check is is passed and the host supports USB3 hardware LPM,
+ the check is passed and the host supports USB3 hardware LPM,
USB3 hardware LPM will be enabled for the device and the USB
- device directory will contain a file named
- power/usb3_hardware_lpm. The file holds a string value (enable
- or disable) indicating whether or not USB3 hardware LPM is
- enabled for the device.
+ device directory will contain two files named
+ power/usb3_hardware_lpm_u1 and power/usb3_hardware_lpm_u2. These
+ files hold a string value (enable or disable) indicating whether
+ or not USB3 hardware LPM U1 or U2 is enabled for the device.
What: /sys/bus/usb/devices/.../removable
Date: February 2012
@@ -187,6 +189,17 @@ Description:
The file will read "hotplug", "wired" and "not used" if the
information is available, and "unknown" otherwise.
+What: /sys/bus/usb/devices/.../(hub interface)/portX/usb3_lpm_permit
+Date: November 2015
+Contact: Lu Baolu <baolu.lu@linux.intel.com>
+Description:
+ Some USB3.0 devices are not friendly to USB3 LPM. usb3_lpm_permit
+ attribute allows enabling/disabling usb3 lpm of a port. It takes
+ effect both before and after a usb device is enumerated. Supported
+ values are "0" if both u1 and u2 are NOT permitted, "u1" if only u1
+ is permitted, "u2" if only u2 is permitted, "u1_u2" if both u1 and
+ u2 are permitted.
+
What: /sys/bus/usb/devices/.../power/usb2_lpm_l1_timeout
Date: May 2013
Contact: Mathias Nyman <mathias.nyman@linux.intel.com>
diff --git a/Documentation/ABI/testing/sysfs-class-net-cdc_ncm b/Documentation/ABI/testing/sysfs-class-net-cdc_ncm
index 5cedf72df358..f7be0e88b139 100644
--- a/Documentation/ABI/testing/sysfs-class-net-cdc_ncm
+++ b/Documentation/ABI/testing/sysfs-class-net-cdc_ncm
@@ -19,6 +19,25 @@ Description:
Set to 0 to pad all frames. Set greater than tx_max to
disable all padding.
+What: /sys/class/net/<iface>/cdc_ncm/ndp_to_end
+Date: Dec 2015
+KernelVersion: 4.5
+Contact: Bjørn Mork <bjorn@mork.no>
+Description:
+ Boolean attribute showing the status of the "NDP to
+ end" quirk. Defaults to 'N', except for devices
+ already known to need it enabled.
+
+ The "NDP to end" quirk makes the driver place the NDP
+ (the packet index table) after the payload. The NCM
+ specification does not mandate this, but some devices
+ are known to be more restrictive. Write 'Y' to this
+ attribute for temporary testing of a suspect device
+ failing to work with the default driver settings.
+
+ A device entry should be added to the driver if this
+ quirk is found to be required.
+
What: /sys/class/net/<iface>/cdc_ncm/rx_max
Date: May 2014
KernelVersion: 3.16
diff --git a/Documentation/ABI/testing/sysfs-class-net-mesh b/Documentation/ABI/testing/sysfs-class-net-mesh
index c46406296631..c2b956d44a95 100644
--- a/Documentation/ABI/testing/sysfs-class-net-mesh
+++ b/Documentation/ABI/testing/sysfs-class-net-mesh
@@ -8,7 +8,7 @@ Description:
What: /sys/class/net/<mesh_iface>/mesh/<vlan_subdir>/ap_isolation
Date: May 2011
-Contact: Antonio Quartulli <antonio@meshcoding.com>
+Contact: Antonio Quartulli <a@unstable.cc>
Description:
Indicates whether the data traffic going from a
wireless client to another wireless client will be
@@ -70,7 +70,7 @@ Description:
What: /sys/class/net/<mesh_iface>/mesh/isolation_mark
Date: Nov 2013
-Contact: Antonio Quartulli <antonio@meshcoding.com>
+Contact: Antonio Quartulli <a@unstable.cc>
Description:
Defines the isolation mark (and its bitmask) which
is used to classify clients as "isolated" by the
diff --git a/Documentation/ABI/testing/sysfs-class-net-qmi b/Documentation/ABI/testing/sysfs-class-net-qmi
new file mode 100644
index 000000000000..fa5a00bb1143
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-net-qmi
@@ -0,0 +1,23 @@
+What: /sys/class/net/<iface>/qmi/raw_ip
+Date: Dec 2015
+KernelVersion: 4.4
+Contact: Bjørn Mork <bjorn@mork.no>
+Description:
+ Boolean. Default: 'N'
+
+ Set this to 'Y' to change the network device link
+ framing from '802.3' to 'raw-ip'.
+
+ The netdev will change to reflect the link framing
+ mode. The netdev is an ordinary ethernet device in
+ '802.3' mode, and the driver expects to exchange
+ frames with an ethernet header over the USB link. The
+ netdev is a headerless p-t-p device in 'raw-ip' mode,
+ and the driver expects to echange IPv4 or IPv6 packets
+ without any L2 header over the USB link.
+
+ Userspace is in full control of firmware configuration
+ through the delegation of the QMI protocol. Userspace
+ is responsible for coordination of driver and firmware
+ link framing mode, changing this setting to 'Y' if the
+ firmware is configured for 'raw-ip' mode.
diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs
index 0345f2d1c727..e5200f354abf 100644
--- a/Documentation/ABI/testing/sysfs-fs-f2fs
+++ b/Documentation/ABI/testing/sysfs-fs-f2fs
@@ -87,6 +87,12 @@ Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
Description:
Controls the checkpoint timing.
+What: /sys/fs/f2fs/<disk>/idle_interval
+Date: January 2016
+Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
+Description:
+ Controls the idle timing.
+
What: /sys/fs/f2fs/<disk>/ra_nid_pages
Date: October 2015
Contact: "Chao Yu" <chao2.yu@samsung.com>
diff --git a/Documentation/ABI/testing/sysfs-kernel-livepatch b/Documentation/ABI/testing/sysfs-kernel-livepatch
index 5bf42a840b22..da87f43aec58 100644
--- a/Documentation/ABI/testing/sysfs-kernel-livepatch
+++ b/Documentation/ABI/testing/sysfs-kernel-livepatch
@@ -33,7 +33,7 @@ Description:
The object directory contains subdirectories for each function
that is patched within the object.
-What: /sys/kernel/livepatch/<patch>/<object>/<function>
+What: /sys/kernel/livepatch/<patch>/<object>/<function,sympos>
Date: Nov 2014
KernelVersion: 3.19.0
Contact: live-patching@vger.kernel.org
@@ -41,4 +41,8 @@ Description:
The function directory contains attributes regarding the
properties and state of the patched function.
+ The directory name contains the patched function name and a
+ sympos number corresponding to the nth occurrence of the symbol
+ name in kallsyms for the patched object.
+
There are currently no such attributes.
diff --git a/Documentation/ABI/testing/sysfs-ptp b/Documentation/ABI/testing/sysfs-ptp
index 44806a678f12..a17f817a9309 100644
--- a/Documentation/ABI/testing/sysfs-ptp
+++ b/Documentation/ABI/testing/sysfs-ptp
@@ -74,7 +74,7 @@ Description:
assignment may be changed by two writing numbers into
the file.
-What: /sys/class/ptp/ptpN/pps_avaiable
+What: /sys/class/ptp/ptpN/pps_available
Date: September 2010
Contact: Richard Cochran <richardcochran@gmail.com>
Description:
diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl
index 42a2d8593e39..cdd8b24db68d 100644
--- a/Documentation/DocBook/device-drivers.tmpl
+++ b/Documentation/DocBook/device-drivers.tmpl
@@ -238,83 +238,32 @@ X!Isound/sound_firmware.c
!Iinclude/media/videobuf2-memops.h
</sect1>
<sect1><title>Digital TV (DVB) devices</title>
-!Idrivers/media/dvb-core/dvb_ca_en50221.h
-!Idrivers/media/dvb-core/dvb_frontend.h
+ <sect1><title>Digital TV Common functions</title>
!Idrivers/media/dvb-core/dvb_math.h
!Idrivers/media/dvb-core/dvb_ringbuffer.h
!Idrivers/media/dvb-core/dvbdev.h
- <sect1><title>Digital TV Demux API</title>
- <para>The kernel demux API defines a driver-internal interface for
- registering low-level, hardware specific driver to a hardware
- independent demux layer. It is only of interest for Digital TV
- device driver writers. The header file for this API is named
- <constant>demux.h</constant> and located in
- <constant>drivers/media/dvb-core</constant>.</para>
-
- <para>The demux API should be implemented for each demux in the
- system. It is used to select the TS source of a demux and to manage
- the demux resources. When the demux client allocates a resource via
- the demux API, it receives a pointer to the API of that
- resource.</para>
- <para>Each demux receives its TS input from a DVB front-end or from
- memory, as set via this demux API. In a system with more than one
- front-end, the API can be used to select one of the DVB front-ends
- as a TS source for a demux, unless this is fixed in the HW platform.
- The demux API only controls front-ends regarding to their connections
- with demuxes; the APIs used to set the other front-end parameters,
- such as tuning, are not defined in this document.</para>
- <para>The functions that implement the abstract interface demux should
- be defined static or module private and registered to the Demux
- core for external access. It is not necessary to implement every
- function in the struct <constant>dmx_demux</constant>. For example,
- a demux interface might support Section filtering, but not PES
- filtering. The API client is expected to check the value of any
- function pointer before calling the function: the value of NULL means
- that the &#8220;function is not available&#8221;.</para>
- <para>Whenever the functions of the demux API modify shared data,
- the possibilities of lost update and race condition problems should
- be addressed, e.g. by protecting parts of code with mutexes.</para>
- <para>Note that functions called from a bottom half context must not
- sleep. Even a simple memory allocation without using GFP_ATOMIC can
- result in a kernel thread being put to sleep if swapping is needed.
- For example, the Linux kernel calls the functions of a network device
- interface from a bottom half context. Thus, if a demux API function
- is called from network device code, the function must not sleep.
- </para>
- </sect1>
-
- <section id="demux_callback_api">
- <title>Demux Callback API</title>
- <para>This kernel-space API comprises the callback functions that
- deliver filtered data to the demux client. Unlike the other DVB
- kABIs, these functions are provided by the client and called from
- the demux code.</para>
- <para>The function pointers of this abstract interface are not
- packed into a structure as in the other demux APIs, because the
- callback functions are registered and used independent of each
- other. As an example, it is possible for the API client to provide
- several callback functions for receiving TS packets and no
- callbacks for PES packets or sections.</para>
- <para>The functions that implement the callback API need not be
- re-entrant: when a demux driver calls one of these functions,
- the driver is not allowed to call the function again before
- the original call returns. If a callback is triggered by a
- hardware interrupt, it is recommended to use the Linux
- &#8220;bottom half&#8221; mechanism or start a tasklet instead of
- making the callback function call directly from a hardware
- interrupt.</para>
- <para>This mechanism is implemented by
- <link linkend='API-dmx-ts-cb'>dmx_ts_cb()</link> and
- <link linkend='API-dmx-section-cb'>dmx_section_cb()</link>.</para>
- </section>
-
+ </sect1>
+ <sect1><title>Digital TV Frontend kABI</title>
+!Pdrivers/media/dvb-core/dvb_frontend.h Digital TV Frontend
+!Idrivers/media/dvb-core/dvb_frontend.h
+ </sect1>
+ <sect1><title>Digital TV Demux kABI</title>
+!Pdrivers/media/dvb-core/demux.h Digital TV Demux
+ <sect1><title>Demux Callback API</title>
+!Pdrivers/media/dvb-core/demux.h Demux Callback
+ </sect1>
!Idrivers/media/dvb-core/demux.h
- </sect1>
+ </sect1>
+ <sect1><title>Digital TV Conditional Access kABI</title>
+!Idrivers/media/dvb-core/dvb_ca_en50221.h
+ </sect1>
+ </sect1>
<sect1><title>Remote Controller devices</title>
!Iinclude/media/rc-core.h
!Iinclude/media/lirc_dev.h
</sect1>
<sect1><title>Media Controller devices</title>
+!Pinclude/media/media-device.h Media Controller
!Iinclude/media/media-device.h
!Iinclude/media/media-devnode.h
!Iinclude/media/media-entity.h
diff --git a/Documentation/DocBook/media/Makefile b/Documentation/DocBook/media/Makefile
index 08527e7ea4d0..2840ff483d5a 100644
--- a/Documentation/DocBook/media/Makefile
+++ b/Documentation/DocBook/media/Makefile
@@ -199,8 +199,10 @@ DVB_DOCUMENTED = \
#
install_media_images = \
- $(Q)-mkdir $(MEDIA_OBJ_DIR)/media_api; \
- cp $(OBJIMGFILES) $(MEDIA_SRC_DIR)/*.svg $(MEDIA_SRC_DIR)/v4l/*.svg $(MEDIA_OBJ_DIR)/media_api
+ $(Q)if [ "x$(findstring media_api.xml,$(DOCBOOKS))" != "x" ]; then \
+ mkdir -p $(MEDIA_OBJ_DIR)/media_api; \
+ cp $(OBJIMGFILES) $(MEDIA_SRC_DIR)/*.svg $(MEDIA_SRC_DIR)/v4l/*.svg $(MEDIA_OBJ_DIR)/media_api; \
+ fi
$(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/%.b64
$(Q)base64 -d $< >$@
diff --git a/Documentation/DocBook/media/dvb/dvbproperty.xml b/Documentation/DocBook/media/dvb/dvbproperty.xml
index 08227d4e9150..e579ae5088ae 100644
--- a/Documentation/DocBook/media/dvb/dvbproperty.xml
+++ b/Documentation/DocBook/media/dvb/dvbproperty.xml
@@ -76,7 +76,7 @@ int main(void)
<para>NOTE: While it is possible to directly call the Kernel code like the
above example, it is strongly recommended to use
- <ulink url="http://linuxtv.org/docs/libdvbv5/index.html">libdvbv5</ulink>,
+ <ulink url="https://linuxtv.org/docs/libdvbv5/index.html">libdvbv5</ulink>,
as it provides abstraction to work with the supported digital TV standards
and provides methods for usual operations like program scanning and to
read/write channel descriptor files.</para>
diff --git a/Documentation/DocBook/media/dvb/examples.xml b/Documentation/DocBook/media/dvb/examples.xml
index c9f68c7183cc..837fb3b64b72 100644
--- a/Documentation/DocBook/media/dvb/examples.xml
+++ b/Documentation/DocBook/media/dvb/examples.xml
@@ -3,7 +3,7 @@
</para>
<para>NOTE: This section is out of date, and the code below won't even
compile. Please refer to the
- <ulink url="http://linuxtv.org/docs/libdvbv5/index.html">libdvbv5</ulink>
+ <ulink url="https://linuxtv.org/docs/libdvbv5/index.html">libdvbv5</ulink>
for updated/recommended examples.
</para>
diff --git a/Documentation/DocBook/media/dvb/intro.xml b/Documentation/DocBook/media/dvb/intro.xml
index 51db15648099..b5b701f5d8c2 100644
--- a/Documentation/DocBook/media/dvb/intro.xml
+++ b/Documentation/DocBook/media/dvb/intro.xml
@@ -32,7 +32,7 @@ and filtering several section and PES data streams at the same time.
new standard Linux DVB API. As a commitment to the development of
terminals based on open standards, Nokia and Convergence made it
available to all Linux developers and published it on
-<ulink url="http://www.linuxtv.org/" /> in September 2000.
+<ulink url="https://linuxtv.org" /> in September 2000.
Convergence is the maintainer of the Linux DVB API. Together with the
LinuxTV community (i.e. you, the reader of this document), the Linux DVB
API will be constantly reviewed and improved. With the Linux driver for
diff --git a/Documentation/DocBook/media/v4l/capture.c.xml b/Documentation/DocBook/media/v4l/capture.c.xml
index 1c5c49a2de59..22126a991b34 100644
--- a/Documentation/DocBook/media/v4l/capture.c.xml
+++ b/Documentation/DocBook/media/v4l/capture.c.xml
@@ -5,7 +5,7 @@
* This program can be used and distributed without restrictions.
*
* This program is provided with the V4L2 API
- * see http://linuxtv.org/docs.php for more information
+ * see https://linuxtv.org/docs.php for more information
*/
#include &lt;stdio.h&gt;
diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml
index 5701a08ed792..5399e8904715 100644
--- a/Documentation/DocBook/media/v4l/compat.xml
+++ b/Documentation/DocBook/media/v4l/compat.xml
@@ -2666,7 +2666,7 @@ is useful to display images captured with V4L2 devices.</para>
<para>V4L2 does not support digital terrestrial, cable or
satellite broadcast. A separate project aiming at digital receivers
exists. You can find its homepage at <ulink
-url="http://linuxtv.org">http://linuxtv.org</ulink>. The Linux DVB API
+url="https://linuxtv.org">https://linuxtv.org</ulink>. The Linux DVB API
has no connection to the V4L2 API except that drivers for hybrid
hardware may support both.</para>
</section>
diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml
index da654031ef3f..144158b3a5ac 100644
--- a/Documentation/DocBook/media/v4l/io.xml
+++ b/Documentation/DocBook/media/v4l/io.xml
@@ -699,7 +699,7 @@ linkend="v4l2-buf-type" /></entry>
buffer. It depends on the negotiated data format and may change with
each buffer for compressed variable size data like JPEG images.
Drivers must set this field when <structfield>type</structfield>
-refers to an input stream, applications when it refers to an output stream.
+refers to a capture stream, applications when it refers to an output stream.
If the application sets this to 0 for an output stream, then
<structfield>bytesused</structfield> will be set to the size of the
buffer (see the <structfield>length</structfield> field of this struct) by
@@ -720,14 +720,14 @@ linkend="buffer-flags" />.</entry>
<entry>Indicates the field order of the image in the
buffer, see <xref linkend="v4l2-field" />. This field is not used when
the buffer contains VBI data. Drivers must set it when
-<structfield>type</structfield> refers to an input stream,
+<structfield>type</structfield> refers to a capture stream,
applications when it refers to an output stream.</entry>
</row>
<row>
<entry>struct timeval</entry>
<entry><structfield>timestamp</structfield></entry>
<entry></entry>
- <entry><para>For input streams this is time when the first data
+ <entry><para>For capture streams this is time when the first data
byte was captured, as returned by the
<function>clock_gettime()</function> function for the relevant
clock id; see <constant>V4L2_BUF_FLAG_TIMESTAMP_*</constant> in
@@ -866,7 +866,7 @@ must set this to 0.</entry>
<entry></entry>
<entry>The number of bytes occupied by data in the plane
(its payload). Drivers must set this field when <structfield>type</structfield>
- refers to an input stream, applications when it refers to an output stream.
+ refers to a capture stream, applications when it refers to an output stream.
If the application sets this to 0 for an output stream, then
<structfield>bytesused</structfield> will be set to the size of the
plane (see the <structfield>length</structfield> field of this struct)
@@ -919,7 +919,7 @@ must set this to 0.</entry>
<entry></entry>
<entry>Offset in bytes to video data in the plane.
Drivers must set this field when <structfield>type</structfield>
- refers to an input stream, applications when it refers to an output stream.
+ refers to a capture stream, applications when it refers to an output stream.
Note that data_offset is included in <structfield>bytesused</structfield>.
So the size of the image in the plane is
<structfield>bytesused</structfield>-<structfield>data_offset</structfield> at
diff --git a/Documentation/DocBook/media/v4l/media-controller.xml b/Documentation/DocBook/media/v4l/media-controller.xml
index 873ac3a621f0..5f2fc07a93d7 100644
--- a/Documentation/DocBook/media/v4l/media-controller.xml
+++ b/Documentation/DocBook/media/v4l/media-controller.xml
@@ -58,21 +58,36 @@
<title>Media device model</title>
<para>Discovering a device internal topology, and configuring it at runtime,
is one of the goals of the media controller API. To achieve this, hardware
- devices are modelled as an oriented graph of building blocks called entities
- connected through pads.</para>
- <para>An entity is a basic media hardware or software building block. It can
- correspond to a large variety of logical blocks such as physical hardware
- devices (CMOS sensor for instance), logical hardware devices (a building
- block in a System-on-Chip image processing pipeline), DMA channels or
- physical connectors.</para>
- <para>A pad is a connection endpoint through which an entity can interact
- with other entities. Data (not restricted to video) produced by an entity
- flows from the entity's output to one or more entity inputs. Pads should not
- be confused with physical pins at chip boundaries.</para>
- <para>A link is a point-to-point oriented connection between two pads,
- either on the same entity or on different entities. Data flows from a source
- pad to a sink pad.</para>
+ devices and Linux Kernel interfaces are modelled as graph objects on
+ an oriented graph. The object types that constitute the graph are:</para>
+ <itemizedlist>
+ <listitem><para>An <emphasis role="bold">entity</emphasis>
+ is a basic media hardware or software building block. It can correspond to
+ a large variety of logical blocks such as physical hardware devices
+ (CMOS sensor for instance), logical hardware devices (a building block in
+ a System-on-Chip image processing pipeline), DMA channels or physical
+ connectors.</para></listitem>
+ <listitem><para>An <emphasis role="bold">interface</emphasis>
+ is a graph representation of a Linux Kernel userspace API interface,
+ like a device node or a sysfs file that controls one or more entities
+ in the graph.</para></listitem>
+ <listitem><para>A <emphasis role="bold">pad</emphasis>
+ is a data connection endpoint through which an entity can interact with
+ other entities. Data (not restricted to video) produced by an entity
+ flows from the entity's output to one or more entity inputs. Pads should
+ not be confused with physical pins at chip boundaries.</para></listitem>
+ <listitem><para>A <emphasis role="bold">data link</emphasis>
+ is a point-to-point oriented connection between two pads, either on the
+ same entity or on different entities. Data flows from a source pad to a
+ sink pad.</para></listitem>
+ <listitem><para>An <emphasis role="bold">interface link</emphasis>
+ is a point-to-point bidirectional control connection between a Linux
+ Kernel interface and an entity.m</para></listitem>
+ </itemizedlist>
</section>
+
+ <!-- All non-ioctl specific data types go here. -->
+ &sub-media-types;
</chapter>
<appendix id="media-user-func">
@@ -83,6 +98,7 @@
&sub-media-func-ioctl;
<!-- All ioctls go here. -->
&sub-media-ioc-device-info;
+ &sub-media-ioc-g-topology;
&sub-media-ioc-enum-entities;
&sub-media-ioc-enum-links;
&sub-media-ioc-setup-link;
diff --git a/Documentation/DocBook/media/v4l/media-ioc-enum-entities.xml b/Documentation/DocBook/media/v4l/media-ioc-enum-entities.xml
index 5872f8bbf774..0c4f96bfc2de 100644
--- a/Documentation/DocBook/media/v4l/media-ioc-enum-entities.xml
+++ b/Documentation/DocBook/media/v4l/media-ioc-enum-entities.xml
@@ -59,15 +59,6 @@
<para>Entity IDs can be non-contiguous. Applications must
<emphasis>not</emphasis> try to enumerate entities by calling
MEDIA_IOC_ENUM_ENTITIES with increasing id's until they get an error.</para>
- <para>Two or more entities that share a common non-zero
- <structfield>group_id</structfield> value are considered as logically
- grouped. Groups are used to report
- <itemizedlist>
- <listitem><para>ALSA, VBI and video nodes that carry the same media
- stream</para></listitem>
- <listitem><para>lens and flash controllers associated with a sensor</para></listitem>
- </itemizedlist>
- </para>
<table pgwide="1" frame="none" id="media-entity-desc">
<title>struct <structname>media_entity_desc</structname></title>
@@ -106,7 +97,7 @@
<entry><structfield>revision</structfield></entry>
<entry></entry>
<entry></entry>
- <entry>Entity revision in a driver/hardware specific format.</entry>
+ <entry>Entity revision. Always zero (obsolete)</entry>
</row>
<row>
<entry>__u32</entry>
@@ -120,7 +111,7 @@
<entry><structfield>group_id</structfield></entry>
<entry></entry>
<entry></entry>
- <entry>Entity group ID</entry>
+ <entry>Entity group ID. Always zero (obsolete)</entry>
</row>
<row>
<entry>__u16</entry>
@@ -171,97 +162,6 @@
</tbody>
</tgroup>
</table>
-
- <table frame="none" pgwide="1" id="media-entity-type">
- <title>Media entity types</title>
- <tgroup cols="2">
- <colspec colname="c1"/>
- <colspec colname="c2"/>
- <tbody valign="top">
- <row>
- <entry><constant>MEDIA_ENT_T_DEVNODE</constant></entry>
- <entry>Unknown device node</entry>
- </row>
- <row>
- <entry><constant>MEDIA_ENT_T_DEVNODE_V4L</constant></entry>
- <entry>V4L video, radio or vbi device node</entry>
- </row>
- <row>
- <entry><constant>MEDIA_ENT_T_DEVNODE_FB</constant></entry>
- <entry>Frame buffer device node</entry>
- </row>
- <row>
- <entry><constant>MEDIA_ENT_T_DEVNODE_ALSA</constant></entry>
- <entry>ALSA card</entry>
- </row>
- <row>
- <entry><constant>MEDIA_ENT_T_DEVNODE_DVB_FE</constant></entry>
- <entry>DVB frontend devnode</entry>
- </row>
- <row>
- <entry><constant>MEDIA_ENT_T_DEVNODE_DVB_DEMUX</constant></entry>
- <entry>DVB demux devnode</entry>
- </row>
- <row>
- <entry><constant>MEDIA_ENT_T_DEVNODE_DVB_DVR</constant></entry>
- <entry>DVB DVR devnode</entry>
- </row>
- <row>
- <entry><constant>MEDIA_ENT_T_DEVNODE_DVB_CA</constant></entry>
- <entry>DVB CAM devnode</entry>
- </row>
- <row>
- <entry><constant>MEDIA_ENT_T_DEVNODE_DVB_NET</constant></entry>
- <entry>DVB network devnode</entry>
- </row>
- <row>
- <entry><constant>MEDIA_ENT_T_V4L2_SUBDEV</constant></entry>
- <entry>Unknown V4L sub-device</entry>
- </row>
- <row>
- <entry><constant>MEDIA_ENT_T_V4L2_SUBDEV_SENSOR</constant></entry>
- <entry>Video sensor</entry>
- </row>
- <row>
- <entry><constant>MEDIA_ENT_T_V4L2_SUBDEV_FLASH</constant></entry>
- <entry>Flash controller</entry>
- </row>
- <row>
- <entry><constant>MEDIA_ENT_T_V4L2_SUBDEV_LENS</constant></entry>
- <entry>Lens controller</entry>
- </row>
- <row>
- <entry><constant>MEDIA_ENT_T_V4L2_SUBDEV_DECODER</constant></entry>
- <entry>Video decoder, the basic function of the video decoder is to
- accept analogue video from a wide variety of sources such as
- broadcast, DVD players, cameras and video cassette recorders, in
- either NTSC, PAL or HD format and still occasionally SECAM, separate
- it into its component parts, luminance and chrominance, and output
- it in some digital video standard, with appropriate embedded timing
- signals.</entry>
- </row>
- <row>
- <entry><constant>MEDIA_ENT_T_V4L2_SUBDEV_TUNER</constant></entry>
- <entry>TV and/or radio tuner</entry>
- </row>
- </tbody>
- </tgroup>
- </table>
-
- <table frame="none" pgwide="1" id="media-entity-flag">
- <title>Media entity flags</title>
- <tgroup cols="2">
- <colspec colname="c1"/>
- <colspec colname="c2"/>
- <tbody valign="top">
- <row>
- <entry><constant>MEDIA_ENT_FL_DEFAULT</constant></entry>
- <entry>Default entity for its type. Used to discover the default
- audio, VBI and video devices, the default camera sensor, ...</entry>
- </row>
- </tbody>
- </tgroup>
- </table>
</refsect1>
<refsect1>
diff --git a/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml b/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml
index 74fb394ec667..2bbeea9f3e18 100644
--- a/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml
+++ b/Documentation/DocBook/media/v4l/media-ioc-enum-links.xml
@@ -118,35 +118,6 @@
</tgroup>
</table>
- <table frame="none" pgwide="1" id="media-pad-flag">
- <title>Media pad flags</title>
- <tgroup cols="2">
- <colspec colname="c1"/>
- <colspec colname="c2"/>
- <tbody valign="top">
- <row>
- <entry><constant>MEDIA_PAD_FL_SINK</constant></entry>
- <entry>Input pad, relative to the entity. Input pads sink data and
- are targets of links.</entry>
- </row>
- <row>
- <entry><constant>MEDIA_PAD_FL_SOURCE</constant></entry>
- <entry>Output pad, relative to the entity. Output pads source data
- and are origins of links.</entry>
- </row>
- <row>
- <entry><constant>MEDIA_PAD_FL_MUST_CONNECT</constant></entry>
- <entry>If this flag is set and the pad is linked to any other
- pad, then at least one of those links must be enabled for the
- entity to be able to stream. There could be temporary reasons
- (e.g. device configuration dependent) for the pad to need
- enabled links even when this flag isn't set; the absence of the
- flag doesn't imply there is none.</entry>
- </row>
- </tbody>
- </tgroup>
- </table>
-
<table pgwide="1" frame="none" id="media-link-desc">
<title>struct <structname>media_link_desc</structname></title>
<tgroup cols="3">
@@ -171,33 +142,6 @@
</tgroup>
</table>
- <table frame="none" pgwide="1" id="media-link-flag">
- <title>Media link flags</title>
- <tgroup cols="2">
- <colspec colname="c1"/>
- <colspec colname="c2"/>
- <tbody valign="top">
- <row>
- <entry><constant>MEDIA_LNK_FL_ENABLED</constant></entry>
- <entry>The link is enabled and can be used to transfer media data.
- When two or more links target a sink pad, only one of them can be
- enabled at a time.</entry>
- </row>
- <row>
- <entry><constant>MEDIA_LNK_FL_IMMUTABLE</constant></entry>
- <entry>The link enabled state can't be modified at runtime. An
- immutable link is always enabled.</entry>
- </row>
- <row>
- <entry><constant>MEDIA_LNK_FL_DYNAMIC</constant></entry>
- <entry>The link enabled state can be modified during streaming. This
- flag is set by drivers and is read-only for applications.</entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- <para>One and only one of <constant>MEDIA_PAD_FL_SINK</constant> and
- <constant>MEDIA_PAD_FL_SOURCE</constant> must be set for every pad.</para>
</refsect1>
<refsect1>
diff --git a/Documentation/DocBook/media/v4l/media-ioc-g-topology.xml b/Documentation/DocBook/media/v4l/media-ioc-g-topology.xml
new file mode 100644
index 000000000000..63152ab9efba
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/media-ioc-g-topology.xml
@@ -0,0 +1,394 @@
+<refentry id="media-g-topology">
+ <refmeta>
+ <refentrytitle>ioctl MEDIA_IOC_G_TOPOLOGY</refentrytitle>
+ &manvol;
+ </refmeta>
+
+ <refnamediv>
+ <refname>MEDIA_IOC_G_TOPOLOGY</refname>
+ <refpurpose>Enumerate the graph topology and graph element properties</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>ioctl</function></funcdef>
+ <paramdef>int <parameter>fd</parameter></paramdef>
+ <paramdef>int <parameter>request</parameter></paramdef>
+ <paramdef>struct media_v2_topology *<parameter>argp</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Arguments</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><parameter>fd</parameter></term>
+ <listitem>
+ <para>File descriptor returned by
+ <link linkend='media-func-open'><function>open()</function></link>.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>request</parameter></term>
+ <listitem>
+ <para>MEDIA_IOC_G_TOPOLOGY</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>argp</parameter></term>
+ <listitem>
+ <para></para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><emphasis role="bold">NOTE:</emphasis> This new ioctl is programmed to be added on Kernel 4.6. Its definition/arguments may change until its final version.</para>
+
+ <para>The typical usage of this ioctl is to call it twice.
+ On the first call, the structure defined at &media-v2-topology; should
+ be zeroed. At return, if no errors happen, this ioctl will return the
+ <constant>topology_version</constant> and the total number of entities,
+ interfaces, pads and links.</para>
+ <para>Before the second call, the userspace should allocate arrays to
+ store the graph elements that are desired, putting the pointers to them
+ at the ptr_entities, ptr_interfaces, ptr_links and/or ptr_pads, keeping
+ the other values untouched.</para>
+ <para>If the <constant>topology_version</constant> remains the same, the
+ ioctl should fill the desired arrays with the media graph elements.</para>
+
+ <table pgwide="1" frame="none" id="media-v2-topology">
+ <title>struct <structname>media_v2_topology</structname></title>
+ <tgroup cols="5">
+ <colspec colname="c1" />
+ <colspec colname="c2" />
+ <colspec colname="c3" />
+ <colspec colname="c4" />
+ <colspec colname="c5" />
+ <tbody valign="top">
+ <row>
+ <entry>__u64</entry>
+ <entry><structfield>topology_version</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Version of the media graph topology. When the graph is
+ created, this field starts with zero. Every time a graph
+ element is added or removed, this field is
+ incremented.</entry>
+ </row>
+ <row>
+ <entry>__u64</entry>
+ <entry><structfield>num_entities</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Number of entities in the graph</entry>
+ </row>
+ <row>
+ <entry>__u64</entry>
+ <entry><structfield>ptr_entities</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>A pointer to a memory area where the entities array
+ will be stored, converted to a 64-bits integer.
+ It can be zero. if zero, the ioctl won't store the
+ entities. It will just update
+ <constant>num_entities</constant></entry>
+ </row>
+ <row>
+ <entry>__u64</entry>
+ <entry><structfield>num_interfaces</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Number of interfaces in the graph</entry>
+ </row>
+ <row>
+ <entry>__u64</entry>
+ <entry><structfield>ptr_interfaces</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>A pointer to a memory area where the interfaces array
+ will be stored, converted to a 64-bits integer.
+ It can be zero. if zero, the ioctl won't store the
+ interfaces. It will just update
+ <constant>num_interfaces</constant></entry>
+ </row>
+ <row>
+ <entry>__u64</entry>
+ <entry><structfield>num_pads</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Total number of pads in the graph</entry>
+ </row>
+ <row>
+ <entry>__u64</entry>
+ <entry><structfield>ptr_pads</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>A pointer to a memory area where the pads array
+ will be stored, converted to a 64-bits integer.
+ It can be zero. if zero, the ioctl won't store the
+ pads. It will just update
+ <constant>num_pads</constant></entry>
+ </row>
+ <row>
+ <entry>__u64</entry>
+ <entry><structfield>num_links</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Total number of data and interface links in the graph</entry>
+ </row>
+ <row>
+ <entry>__u64</entry>
+ <entry><structfield>ptr_links</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>A pointer to a memory area where the links array
+ will be stored, converted to a 64-bits integer.
+ It can be zero. if zero, the ioctl won't store the
+ links. It will just update
+ <constant>num_links</constant></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table pgwide="1" frame="none" id="media-v2-entity">
+ <title>struct <structname>media_v2_entity</structname></title>
+ <tgroup cols="5">
+ <colspec colname="c1" />
+ <colspec colname="c2" />
+ <colspec colname="c3" />
+ <colspec colname="c4" />
+ <colspec colname="c5" />
+ <tbody valign="top">
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>id</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Unique ID for the entity.</entry>
+ </row>
+ <row>
+ <entry>char</entry>
+ <entry><structfield>name</structfield>[64]</entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Entity name as an UTF-8 NULL-terminated string.</entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>function</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Entity main function, see <xref linkend="media-entity-type" /> for details.</entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>reserved</structfield>[12]</entry>
+ <entry>Reserved for future extensions. Drivers and applications must
+ set this array to zero.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table pgwide="1" frame="none" id="media-v2-interface">
+ <title>struct <structname>media_v2_interface</structname></title>
+ <tgroup cols="5">
+ <colspec colname="c1" />
+ <colspec colname="c2" />
+ <colspec colname="c3" />
+ <colspec colname="c4" />
+ <colspec colname="c5" />
+ <tbody valign="top">
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>id</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Unique ID for the interface.</entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>intf_type</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Interface type, see <xref linkend="media-intf-type" /> for details.</entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>flags</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Interface flags. Currently unused.</entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>reserved</structfield>[9]</entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Reserved for future extensions. Drivers and applications must
+ set this array to zero.</entry>
+ </row>
+ <row>
+ <entry>struct media_v2_intf_devnode</entry>
+ <entry><structfield>devnode</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Used only for device node interfaces. See <xref linkend="media-v2-intf-devnode" /> for details..</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table pgwide="1" frame="none" id="media-v2-intf-devnode">
+ <title>struct <structname>media_v2_interface</structname></title>
+ <tgroup cols="5">
+ <colspec colname="c1" />
+ <colspec colname="c2" />
+ <colspec colname="c3" />
+ <colspec colname="c4" />
+ <colspec colname="c5" />
+ <tbody valign="top">
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>major</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Device node major number.</entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>minor</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Device node minor number.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table pgwide="1" frame="none" id="media-v2-pad">
+ <title>struct <structname>media_v2_pad</structname></title>
+ <tgroup cols="5">
+ <colspec colname="c1" />
+ <colspec colname="c2" />
+ <colspec colname="c3" />
+ <colspec colname="c4" />
+ <colspec colname="c5" />
+ <tbody valign="top">
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>id</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Unique ID for the pad.</entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>entity_id</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Unique ID for the entity where this pad belongs.</entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>flags</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Pad flags, see <xref linkend="media-pad-flag" /> for more details.</entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>reserved</structfield>[9]</entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Reserved for future extensions. Drivers and applications must
+ set this array to zero.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table pgwide="1" frame="none" id="media-v2-link">
+ <title>struct <structname>media_v2_pad</structname></title>
+ <tgroup cols="5">
+ <colspec colname="c1" />
+ <colspec colname="c2" />
+ <colspec colname="c3" />
+ <colspec colname="c4" />
+ <colspec colname="c5" />
+ <tbody valign="top">
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>id</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Unique ID for the pad.</entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>source_id</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>
+ <para>On pad to pad links: unique ID for the source pad.</para>
+ <para>On interface to entity links: unique ID for the interface.</para>
+ </entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>sink_id</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>
+ <para>On pad to pad links: unique ID for the sink pad.</para>
+ <para>On interface to entity links: unique ID for the entity.</para>
+ </entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>flags</structfield></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Link flags, see <xref linkend="media-link-flag" /> for more details.</entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>reserved</structfield>[5]</entry>
+ <entry></entry>
+ <entry></entry>
+ <entry>Reserved for future extensions. Drivers and applications must
+ set this array to zero.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </refsect1>
+
+ <refsect1>
+ &return-value;
+
+ <variablelist>
+ <varlistentry>
+ <term><errorcode>ENOSPC</errorcode></term>
+ <listitem>
+ <para>This is returned when either one or more of the num_entities,
+ num_interfaces, num_links or num_pads are non-zero and are smaller
+ than the actual number of elements inside the graph. This may happen
+ if the <constant>topology_version</constant> changed when compared
+ to the last time this ioctl was called. Userspace should usually
+ free the area for the pointers, zero the struct elements and call
+ this ioctl again.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/media-types.xml b/Documentation/DocBook/media/v4l/media-types.xml
new file mode 100644
index 000000000000..1af384250910
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/media-types.xml
@@ -0,0 +1,240 @@
+<section id="media-controller-types">
+<title>Types and flags used to represent the media graph elements</title>
+
+ <table frame="none" pgwide="1" id="media-entity-type">
+ <title>Media entity types</title>
+ <tgroup cols="2">
+ <colspec colname="c1"/>
+ <colspec colname="c2"/>
+ <tbody valign="top">
+ <row>
+ <entry><constant>MEDIA_ENT_F_UNKNOWN</constant> and <constant>MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN</constant></entry>
+ <entry>Unknown entity. That generally indicates that
+ a driver didn't initialize properly the entity, with is a Kernel bug</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_IO_V4L</constant></entry>
+ <entry>Data streaming input and/or output entity.</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_IO_VBI</constant></entry>
+ <entry>V4L VBI streaming input or output entity</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_IO_SWRADIO</constant></entry>
+ <entry>V4L Software Digital Radio (SDR) streaming input or output entity</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_IO_DTV</constant></entry>
+ <entry>DVB Digital TV streaming input or output entity</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_DTV_DEMOD</constant></entry>
+ <entry>Digital TV demodulator entity.</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_TS_DEMUX</constant></entry>
+ <entry>MPEG Transport stream demux entity. Could be implemented on hardware or in Kernelspace by the Linux DVB subsystem.</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_DTV_CA</constant></entry>
+ <entry>Digital TV Conditional Access module (CAM) entity</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_DTV_NET_DECAP</constant></entry>
+ <entry>Digital TV network ULE/MLE desencapsulation entity. Could be implemented on hardware or in Kernelspace</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_CONN_RF</constant></entry>
+ <entry>Connector for a Radio Frequency (RF) signal.</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_CONN_SVIDEO</constant></entry>
+ <entry>Connector for a S-Video signal.</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_CONN_COMPOSITE</constant></entry>
+ <entry>Connector for a RGB composite signal.</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_CONN_TEST</constant></entry>
+ <entry>Connector for a test generator.</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_CAM_SENSOR</constant></entry>
+ <entry>Camera video sensor entity.</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_FLASH</constant></entry>
+ <entry>Flash controller entity.</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_LENS</constant></entry>
+ <entry>Lens controller entity.</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_ATV_DECODER</constant></entry>
+ <entry>Analog video decoder, the basic function of the video decoder
+ is to accept analogue video from a wide variety of sources such as
+ broadcast, DVD players, cameras and video cassette recorders, in
+ either NTSC, PAL, SECAM or HD format, separating the stream
+ into its component parts, luminance and chrominance, and output
+ it in some digital video standard, with appropriate timing
+ signals.</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_F_TUNER</constant></entry>
+ <entry>Digital TV, analog TV, radio and/or software radio tuner.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="none" pgwide="1" id="media-entity-flag">
+ <title>Media entity flags</title>
+ <tgroup cols="2">
+ <colspec colname="c1"/>
+ <colspec colname="c2"/>
+ <tbody valign="top">
+ <row>
+ <entry><constant>MEDIA_ENT_FL_DEFAULT</constant></entry>
+ <entry>Default entity for its type. Used to discover the default
+ audio, VBI and video devices, the default camera sensor, ...</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_ENT_FL_CONNECTOR</constant></entry>
+ <entry>The entity represents a data conector</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="none" pgwide="1" id="media-intf-type">
+ <title>Media interface types</title>
+ <tgroup cols="3">
+ <colspec colname="c1"/>
+ <colspec colname="c2"/>
+ <colspec colname="c3"/>
+ <tbody valign="top">
+ <row>
+ <entry><constant>MEDIA_INTF_T_DVB_FE</constant></entry>
+ <entry>Device node interface for the Digital TV frontend</entry>
+ <entry>typically, /dev/dvb/adapter?/frontend?</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_INTF_T_DVB_DEMUX</constant></entry>
+ <entry>Device node interface for the Digital TV demux</entry>
+ <entry>typically, /dev/dvb/adapter?/demux?</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_INTF_T_DVB_DVR</constant></entry>
+ <entry>Device node interface for the Digital TV DVR</entry>
+ <entry>typically, /dev/dvb/adapter?/dvr?</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_INTF_T_DVB_CA</constant></entry>
+ <entry>Device node interface for the Digital TV Conditional Access</entry>
+ <entry>typically, /dev/dvb/adapter?/ca?</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_INTF_T_DVB_FE</constant></entry>
+ <entry>Device node interface for the Digital TV network control</entry>
+ <entry>typically, /dev/dvb/adapter?/net?</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_INTF_T_V4L_VIDEO</constant></entry>
+ <entry>Device node interface for video (V4L)</entry>
+ <entry>typically, /dev/video?</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_INTF_T_V4L_VBI</constant></entry>
+ <entry>Device node interface for VBI (V4L)</entry>
+ <entry>typically, /dev/vbi?</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_INTF_T_V4L_RADIO</constant></entry>
+ <entry>Device node interface for radio (V4L)</entry>
+ <entry>typically, /dev/vbi?</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_INTF_T_V4L_SUBDEV</constant></entry>
+ <entry>Device node interface for a V4L subdevice</entry>
+ <entry>typically, /dev/v4l-subdev?</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_INTF_T_V4L_SWRADIO</constant></entry>
+ <entry>Device node interface for Software Defined Radio (V4L)</entry>
+ <entry>typically, /dev/swradio?</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <table frame="none" pgwide="1" id="media-pad-flag">
+ <title>Media pad flags</title>
+ <tgroup cols="2">
+ <colspec colname="c1"/>
+ <colspec colname="c2"/>
+ <tbody valign="top">
+ <row>
+ <entry><constant>MEDIA_PAD_FL_SINK</constant></entry>
+ <entry>Input pad, relative to the entity. Input pads sink data and
+ are targets of links.</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_PAD_FL_SOURCE</constant></entry>
+ <entry>Output pad, relative to the entity. Output pads source data
+ and are origins of links.</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_PAD_FL_MUST_CONNECT</constant></entry>
+ <entry>If this flag is set and the pad is linked to any other
+ pad, then at least one of those links must be enabled for the
+ entity to be able to stream. There could be temporary reasons
+ (e.g. device configuration dependent) for the pad to need
+ enabled links even when this flag isn't set; the absence of the
+ flag doesn't imply there is none.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>One and only one of <constant>MEDIA_PAD_FL_SINK</constant> and
+ <constant>MEDIA_PAD_FL_SOURCE</constant> must be set for every pad.</para>
+
+ <table frame="none" pgwide="1" id="media-link-flag">
+ <title>Media link flags</title>
+ <tgroup cols="2">
+ <colspec colname="c1"/>
+ <colspec colname="c2"/>
+ <tbody valign="top">
+ <row>
+ <entry><constant>MEDIA_LNK_FL_ENABLED</constant></entry>
+ <entry>The link is enabled and can be used to transfer media data.
+ When two or more links target a sink pad, only one of them can be
+ enabled at a time.</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_LNK_FL_IMMUTABLE</constant></entry>
+ <entry>The link enabled state can't be modified at runtime. An
+ immutable link is always enabled.</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_LNK_FL_DYNAMIC</constant></entry>
+ <entry>The link enabled state can be modified during streaming. This
+ flag is set by drivers and is read-only for applications.</entry>
+ </row>
+ <row>
+ <entry><constant>MEDIA_LNK_FL_LINK_TYPE</constant></entry>
+ <entry><para>This is a bitmask that defines the type of the link.
+ Currently, two types of links are supported:</para>
+ <para><constant>MEDIA_LNK_FL_DATA_LINK</constant>
+ if the link is between two pads</para>
+ <para><constant>MEDIA_LNK_FL_INTERFACE_LINK</constant>
+ if the link is between an interface and an entity</para></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+</section>
diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml
index 7e61643358de..42e626d6c936 100644
--- a/Documentation/DocBook/media/v4l/v4l2.xml
+++ b/Documentation/DocBook/media/v4l/v4l2.xml
@@ -152,6 +152,16 @@ structs, ioctls) must be noted in more detail in the history chapter
(compat.xml), along with the possible impact on existing drivers and
applications. -->
<revision>
+ <revnumber>4.5</revnumber>
+ <date>2015-10-29</date>
+ <authorinitials>rr</authorinitials>
+ <revremark>Extend vidioc-g-ext-ctrls;. Replace ctrl_class with a new
+union with ctrl_class and which. Which is used to select the current value of
+the control or the default value.
+ </revremark>
+ </revision>
+
+ <revision>
<revnumber>4.4</revnumber>
<date>2015-05-26</date>
<authorinitials>ap</authorinitials>
diff --git a/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml b/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml
index 8ffe74f84af1..d81fa0d4016b 100644
--- a/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml
@@ -58,7 +58,7 @@
<para>This ioctl is used to create buffers for <link linkend="mmap">memory
mapped</link> or <link linkend="userp">user pointer</link> or <link
linkend="dmabuf">DMA buffer</link> I/O. It can be used as an alternative or in
-addition to the <constant>VIDIOC_REQBUFS</constant> ioctl, when a tighter
+addition to the &VIDIOC-REQBUFS; ioctl, when a tighter
control over buffers is required. This ioctl can be called multiple times to
create buffers of different sizes.</para>
@@ -71,30 +71,28 @@ zeroed.</para>
<para>The <structfield>format</structfield> field specifies the image format
that the buffers must be able to handle. The application has to fill in this
-&v4l2-format;. Usually this will be done using the
-<constant>VIDIOC_TRY_FMT</constant> or <constant>VIDIOC_G_FMT</constant> ioctl()
-to ensure that the requested format is supported by the driver. Unsupported
-formats will result in an error.</para>
+&v4l2-format;. Usually this will be done using the &VIDIOC-TRY-FMT; or &VIDIOC-G-FMT; ioctls
+to ensure that the requested format is supported by the driver.
+Based on the format's <structfield>type</structfield> field the requested buffer
+size (for single-planar) or plane sizes (for multi-planar formats) will be
+used for the allocated buffers. The driver may return an error if the size(s)
+are not supported by the hardware (usually because they are too small).</para>
<para>The buffers created by this ioctl will have as minimum size the size
-defined by the <structfield>format.pix.sizeimage</structfield> field. If the
+defined by the <structfield>format.pix.sizeimage</structfield> field (or the
+corresponding fields for other format types). Usually if the
<structfield>format.pix.sizeimage</structfield> field is less than the minimum
-required for the given format, then <structfield>sizeimage</structfield> will be
-increased by the driver to that minimum to allocate the buffers. If it is
-larger, then the value will be used as-is. The same applies to the
-<structfield>sizeimage</structfield> field of the
-<structname>v4l2_plane_pix_format</structname> structure in the case of
-multiplanar formats.</para>
+required for the given format, then an error will be returned since drivers will
+typically not allow this. If it is larger, then the value will be used as-is.
+In other words, the driver may reject the requested size, but if it is accepted
+the driver will use it unchanged.</para>
<para>When the ioctl is called with a pointer to this structure the driver
will attempt to allocate up to the requested number of buffers and store the
actual number allocated and the starting index in the
<structfield>count</structfield> and the <structfield>index</structfield> fields
respectively. On return <structfield>count</structfield> can be smaller than
-the number requested. The driver may also increase buffer sizes if required,
-however, it will not update <structfield>sizeimage</structfield> field values.
-The user has to use <constant>VIDIOC_QUERYBUF</constant> to retrieve that
-information.</para>
+the number requested.</para>
<table pgwide="1" frame="none" id="v4l2-create-buffers">
<title>struct <structname>v4l2_create_buffers</structname></title>
diff --git a/Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-info.xml b/Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-info.xml
index 4c4603c135fe..f14a3bb1afaa 100644
--- a/Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-info.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-dbg-g-chip-info.xml
@@ -99,7 +99,7 @@ if the driver supports writing registers to the device.</para>
<para>We recommended the <application>v4l2-dbg</application>
utility over calling this ioctl directly. It is available from the
LinuxTV v4l-dvb repository; see <ulink
-url="http://linuxtv.org/repo/">http://linuxtv.org/repo/</ulink> for
+url="https://linuxtv.org/repo/">https://linuxtv.org/repo/</ulink> for
access instructions.</para>
<!-- Note for convenience vidioc-dbg-g-register.sgml
diff --git a/Documentation/DocBook/media/v4l/vidioc-dbg-g-register.xml b/Documentation/DocBook/media/v4l/vidioc-dbg-g-register.xml
index 3d038e75d12b..5877f68a5820 100644
--- a/Documentation/DocBook/media/v4l/vidioc-dbg-g-register.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-dbg-g-register.xml
@@ -117,7 +117,7 @@ However when a driver supports these ioctls it must also support
<para>We recommended the <application>v4l2-dbg</application>
utility over calling these ioctls directly. It is available from the
LinuxTV v4l-dvb repository; see <ulink
-url="http://linuxtv.org/repo/">http://linuxtv.org/repo/</ulink> for
+url="https://linuxtv.org/repo/">https://linuxtv.org/repo/</ulink> for
access instructions.</para>
<!-- Note for convenience vidioc-dbg-g-chip-info.sgml
diff --git a/Documentation/DocBook/media/v4l/vidioc-enumstd.xml b/Documentation/DocBook/media/v4l/vidioc-enumstd.xml
index 8065099401d1..f18454e91752 100644
--- a/Documentation/DocBook/media/v4l/vidioc-enumstd.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-enumstd.xml
@@ -198,7 +198,7 @@ video4linux-list@redhat.com on 17 Oct 2002
<constant>V4L2_STD_ATSC_16_VSB</constant> are U.S. terrestrial digital
TV standards. Presently the V4L2 API does not support digital TV. See
also the Linux DVB API at <ulink
-url="http://linuxtv.org">http://linuxtv.org</ulink>.</para>
+url="https://linuxtv.org">https://linuxtv.org</ulink>.</para>
<para><programlisting>
#define V4L2_STD_PAL_BG (V4L2_STD_PAL_B |\
V4L2_STD_PAL_B1 |\
diff --git a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml
index 842536aae8b4..eb82f7e7d06b 100644
--- a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml
@@ -61,7 +61,7 @@ must belong to the same control class.</para>
<para>Applications must always fill in the
<structfield>count</structfield>,
-<structfield>ctrl_class</structfield>,
+<structfield>which</structfield>,
<structfield>controls</structfield> and
<structfield>reserved</structfield> fields of &v4l2-ext-controls;, and
initialize the &v4l2-ext-control; array pointed to by the
@@ -109,7 +109,7 @@ the driver whether wrong values are automatically adjusted to a valid
value or if an error is returned.</para>
<para>When the <structfield>id</structfield> or
-<structfield>ctrl_class</structfield> is invalid drivers return an
+<structfield>which</structfield> is invalid drivers return an
&EINVAL;. When the value is out of bounds drivers can choose to take
the closest valid value or return an &ERANGE;, whatever seems more
appropriate. In the first case the new value is set in
@@ -223,7 +223,12 @@ Valid if <constant>V4L2_CTRL_FLAG_HAS_PAYLOAD</constant> is set for this control
<tgroup cols="3">
&cs-str;
<tbody valign="top">
+ <row>
+ <entry>union</entry>
+ <entry>(anonymous)</entry>
+ </row>
<row>
+ <entry></entry>
<entry>__u32</entry>
<entry><structfield>ctrl_class</structfield></entry>
<entry>The control class to which all controls belong, see
@@ -235,6 +240,23 @@ with a <structfield>count</structfield> of 0. If that succeeds, then the driver
supports this feature.</entry>
</row>
<row>
+ <entry></entry>
+ <entry>__u32</entry>
+ <entry><structfield>which</structfield></entry>
+ <entry><para>Which value of the control to get/set/try. <constant>V4L2_CTRL_WHICH_CUR_VAL</constant>
+will return the current value of the control and <constant>V4L2_CTRL_WHICH_DEF_VAL</constant> will
+return the default value of the control. Please note that you can only get the default value of the
+control, you cannot set or try it.</para>
+<para>For backwards compatibility you can also use a control class here (see
+<xref linkend="ctrl-class" />). In that case all controls have to belong to that
+control class. This usage is deprecated, instead just use <constant>V4L2_CTRL_WHICH_CUR_VAL</constant>.
+There are some very old drivers that do not yet support <constant>V4L2_CTRL_WHICH_CUR_VAL</constant>
+and that require a control class here. You can test for such drivers by setting ctrl_class to
+<constant>V4L2_CTRL_WHICH_CUR_VAL</constant> and calling VIDIOC_TRY_EXT_CTRLS with a count of 0.
+If that fails, then the driver does not support <constant>V4L2_CTRL_WHICH_CUR_VAL</constant>.</para>
+</entry>
+ </row>
+ <row>
<entry>__u32</entry>
<entry><structfield>count</structfield></entry>
<entry>The number of controls in the controls array. May
@@ -390,7 +412,7 @@ These controls are described in <xref linkend="rf-tuner-controls" />.</entry>
<listitem>
<para>The &v4l2-ext-control; <structfield>id</structfield>
is invalid, the &v4l2-ext-controls;
-<structfield>ctrl_class</structfield> is invalid, or the &v4l2-ext-control;
+<structfield>which</structfield> is invalid, or the &v4l2-ext-control;
<structfield>value</structfield> was inappropriate (e.g. the given menu
index is not supported by the driver). This error code is
also returned by the <constant>VIDIOC_S_EXT_CTRLS</constant> and
diff --git a/Documentation/DocBook/media_api.tmpl b/Documentation/DocBook/media_api.tmpl
index 92037033f5eb..7b77e0f7b87d 100644
--- a/Documentation/DocBook/media_api.tmpl
+++ b/Documentation/DocBook/media_api.tmpl
@@ -19,10 +19,10 @@
<!ENTITY cs-def "<colspec colname='c1' colwidth='3*' /><colspec colname='c2' colwidth='1*' /><colspec colname='c3' colwidth='4*' /><spanspec spanname='hspan' namest='c1' nameend='c3' />">
<!-- Video for Linux mailing list address. -->
-<!ENTITY v4l-ml "<ulink url='http://www.linuxtv.org/lists.php'>http://www.linuxtv.org/lists.php</ulink>">
+<!ENTITY v4l-ml "<ulink url='https://linuxtv.org/lists.php'>https://linuxtv.org/lists.php</ulink>">
<!-- LinuxTV v4l-dvb repository. -->
-<!ENTITY v4l-dvb "<ulink url='http://linuxtv.org/repo/'>http://linuxtv.org/repo/</ulink>">
+<!ENTITY v4l-dvb "<ulink url='https://linuxtv.org/repo/'>https://linuxtv.org/repo/</ulink>">
<!ENTITY dash-ent-8 "<entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry>">
<!ENTITY dash-ent-10 "<entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry>">
<!ENTITY dash-ent-12 "<entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry><entry>-</entry>">
@@ -91,7 +91,7 @@
components, like mixers, PCM capture, PCM playback, etc, which
are controlled via ALSA API.</para>
<para>For additional information and for the latest development code,
- see: <ulink url="http://linuxtv.org">http://linuxtv.org</ulink>.</para>
+ see: <ulink url="https://linuxtv.org">https://linuxtv.org</ulink>.</para>
<para>For discussing improvements, reporting troubles, sending new drivers, etc, please mail to: <ulink url="http://vger.kernel.org/vger-lists.html#linux-media">Linux Media Mailing List (LMML).</ulink>.</para>
</preface>
diff --git a/Documentation/DocBook/mtdnand.tmpl b/Documentation/DocBook/mtdnand.tmpl
index 7da8f0402af5..b442921bca54 100644
--- a/Documentation/DocBook/mtdnand.tmpl
+++ b/Documentation/DocBook/mtdnand.tmpl
@@ -162,12 +162,15 @@
<sect1 id="Basic_defines">
<title>Basic defines</title>
<para>
- At least you have to provide a mtd structure and
- a storage for the ioremap'ed chip address.
- You can allocate the mtd structure using kmalloc
- or you can allocate it statically.
- In case of static allocation you have to allocate
- a nand_chip structure too.
+ At least you have to provide a nand_chip structure
+ and a storage for the ioremap'ed chip address.
+ You can allocate the nand_chip structure using
+ kmalloc or you can allocate it statically.
+ The NAND chip structure embeds an mtd structure
+ which will be registered to the MTD subsystem.
+ You can extract a pointer to the mtd structure
+ from a nand_chip pointer using the nand_to_mtd()
+ helper.
</para>
<para>
Kmalloc based example
@@ -180,7 +183,6 @@ static void __iomem *baseaddr;
Static example
</para>
<programlisting>
-static struct mtd_info board_mtd;
static struct nand_chip board_chip;
static void __iomem *baseaddr;
</programlisting>
@@ -235,7 +237,7 @@ static void board_hwcontrol(struct mtd_info *mtd, int cmd)
<programlisting>
static void board_hwcontrol(struct mtd_info *mtd, int cmd)
{
- struct nand_chip *this = (struct nand_chip *) mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
switch(cmd){
case NAND_CTL_SETCLE: this->IO_ADDR_W |= CLE_ADRR_BIT; break;
case NAND_CTL_CLRCLE: this->IO_ADDR_W &amp;= ~CLE_ADRR_BIT; break;
@@ -274,13 +276,15 @@ static int __init board_init (void)
int err = 0;
/* Allocate memory for MTD device structure and private data */
- board_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
- if (!board_mtd) {
+ this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
+ if (!this) {
printk ("Unable to allocate NAND MTD device structure.\n");
err = -ENOMEM;
goto out;
}
+ board_mtd = nand_to_mtd(this);
+
/* map physical address */
baseaddr = ioremap(CHIP_PHYSICAL_ADDRESS, 1024);
if (!baseaddr) {
@@ -289,11 +293,6 @@ static int __init board_init (void)
goto out_mtd;
}
- /* Get pointer to private data */
- this = (struct nand_chip *) ();
- /* Link the private data with the MTD structure */
- board_mtd->priv = this;
-
/* Set address of NAND IO lines */
this->IO_ADDR_R = baseaddr;
this->IO_ADDR_W = baseaddr;
@@ -317,7 +316,7 @@ static int __init board_init (void)
out_ior:
iounmap(baseaddr);
out_mtd:
- kfree (board_mtd);
+ kfree (this);
out:
return err;
}
@@ -343,7 +342,7 @@ static void __exit board_cleanup (void)
iounmap(baseaddr);
/* Free the MTD device structure */
- kfree (board_mtd);
+ kfree (mtd_to_nand(board_mtd));
}
module_exit(board_cleanup);
#endif
@@ -399,7 +398,7 @@ static void board_select_chip (struct mtd_info *mtd, int chip)
<programlisting>
static void board_select_chip (struct mtd_info *mtd, int chip)
{
- struct nand_chip *this = (struct nand_chip *) mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
/* Deselect all chips */
this->IO_ADDR_R &amp;= ~BOARD_NAND_ADDR_MASK;
diff --git a/Documentation/Makefile b/Documentation/Makefile
index bc0548201755..1207d7907650 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -1,4 +1,4 @@
subdir-y := accounting auxdisplay blackfin connector \
filesystems filesystems ia64 laptops mic misc-devices \
- networking pcmcia prctl ptp spi timers vDSO video4linux \
+ networking pcmcia prctl ptp timers vDSO video4linux \
watchdog
diff --git a/Documentation/RCU/Design/Requirements/2013-08-is-it-dead.png b/Documentation/RCU/Design/Requirements/2013-08-is-it-dead.png
new file mode 100644
index 000000000000..7496a55e4e7b
--- /dev/null
+++ b/Documentation/RCU/Design/Requirements/2013-08-is-it-dead.png
Binary files differ
diff --git a/Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg b/Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg
new file mode 100644
index 000000000000..4b4014fda770
--- /dev/null
+++ b/Documentation/RCU/Design/Requirements/GPpartitionReaders1.svg
@@ -0,0 +1,374 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="447.99197"
+ height="428.19299"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="GPpartitionReaders1.svg">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path3792"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible">
+ <path
+ id="path3789"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.6184291"
+ inkscape:cx="223.99599"
+ inkscape:cy="214.0965"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="979"
+ inkscape:window-height="836"
+ inkscape:window-x="571"
+ inkscape:window-y="335"
+ inkscape:window-maximized="0"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-28.441125,-185.60612)">
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot2985"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"><flowRegion
+ id="flowRegion2987"><rect
+ id="rect2989"
+ width="82.85714"
+ height="11.428572"
+ x="240"
+ y="492.36218" /></flowRegion><flowPara
+ id="flowPara2991"></flowPara></flowRoot> <g
+ id="g4433"
+ transform="translate(2,0)">
+ <text
+ sodipodi:linespacing="125%"
+ id="text2993"
+ y="-261.66608"
+ x="412.12299"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ xml:space="preserve"
+ transform="matrix(0,1,-1,0,0,0)"><tspan
+ y="-261.66608"
+ x="412.12299"
+ id="tspan2995"
+ sodipodi:role="line">synchronize_rcu()</tspan></text>
+ <g
+ id="g4417"
+ transform="matrix(0,1,-1,0,730.90257,222.4928)">
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow2Lstart);marker-end:url(#Arrow2Lend)"
+ d="m 97.580736,477.4048 183.140664,0"
+ id="path2997"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 96.752718,465.38398 0,22.62742"
+ id="path4397"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 281.54942,465.38397 0,22.62742"
+ id="path4397-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ </g>
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="112.04738"
+ y="268.18076"
+ id="text4429"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4431"
+ x="112.04738"
+ y="268.18076">WRITE_ONCE(a, 1);</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="112.04738"
+ y="439.13766"
+ id="text4441"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4443"
+ x="112.04738"
+ y="439.13766">WRITE_ONCE(b, 1);</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="255.60869"
+ y="309.29346"
+ id="text4445"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4447"
+ x="255.60869"
+ y="309.29346">r1 = READ_ONCE(a);</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="255.14423"
+ y="520.61786"
+ id="text4449"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4451"
+ x="255.14423"
+ y="520.61786">WRITE_ONCE(c, 1);</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="396.10254"
+ y="384.71124"
+ id="text4453"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4455"
+ x="396.10254"
+ y="384.71124">r2 = READ_ONCE(b);</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="396.10254"
+ y="582.13617"
+ id="text4457"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4459"
+ x="396.10254"
+ y="582.13617">r3 = READ_ONCE(c);</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="112.08231"
+ y="213.91006"
+ id="text4461"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4463"
+ x="112.08231"
+ y="213.91006">thread0()</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="252.34512"
+ y="213.91006"
+ id="text4461-6"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4463-0"
+ x="252.34512"
+ y="213.91006">thread1()</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="396.42557"
+ y="213.91006"
+ id="text4461-2"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4463-2"
+ x="396.42557"
+ y="213.91006">thread2()</tspan></text>
+ <rect
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect4495"
+ width="436.28488"
+ height="416.4859"
+ x="34.648232"
+ y="191.10612" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ d="m 183.14066,191.10612 0,417.193 -0.70711,0"
+ id="path4497"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ d="m 325.13867,191.10612 0,417.193 -0.70711,0"
+ id="path4497-5"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="111.75929"
+ y="251.53981"
+ id="text4429-8"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4431-9"
+ x="111.75929"
+ y="251.53981">rcu_read_lock();</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="396.10254"
+ y="367.91556"
+ id="text4429-8-9"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4431-9-4"
+ x="396.10254"
+ y="367.91556">rcu_read_lock();</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="396.10254"
+ y="597.40289"
+ id="text4429-8-9-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4431-9-4-4"
+ x="396.10254"
+ y="597.40289">rcu_read_unlock();</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="111.75929"
+ y="453.15311"
+ id="text4429-8-9-3-1"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4431-9-4-4-6"
+ x="111.75929"
+ y="453.15311">rcu_read_unlock();</tspan></text>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 33.941125,227.87568 436.284885,0 0,0.7071"
+ id="path4608"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="394.94427"
+ y="345.66351"
+ id="text4648"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4650"
+ x="394.94427"
+ y="345.66351">QS</tspan></text>
+ <path
+ sodipodi:type="arc"
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="path4652"
+ sodipodi:cx="358.85669"
+ sodipodi:cy="142.87541"
+ sodipodi:rx="10.960155"
+ sodipodi:ry="10.253048"
+ d="m 358.86939,132.62237 a 10.960155,10.253048 0 1 1 -0.0228,0"
+ transform="translate(36.441125,199.60612)"
+ sodipodi:start="4.7135481"
+ sodipodi:end="10.994651"
+ sodipodi:open="true" />
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="112.11968"
+ y="475.77856"
+ id="text4648-4"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4650-4"
+ x="112.11968"
+ y="475.77856">QS</tspan></text>
+ <path
+ sodipodi:type="arc"
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="path4652-7"
+ sodipodi:cx="358.85669"
+ sodipodi:cy="142.87541"
+ sodipodi:rx="10.960155"
+ sodipodi:ry="10.253048"
+ d="m 358.86939,132.62237 a 10.960155,10.253048 0 1 1 -0.0228,0"
+ transform="translate(-246.38346,329.72117)"
+ sodipodi:start="4.7135481"
+ sodipodi:end="10.994651"
+ sodipodi:open="true" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="path4652-7-7"
+ sodipodi:cx="358.85669"
+ sodipodi:cy="142.87541"
+ sodipodi:rx="10.960155"
+ sodipodi:ry="10.253048"
+ d="m 358.86939,132.62237 a 10.960155,10.253048 0 1 1 -0.0228,0"
+ transform="translate(-103.65246,202.90878)"
+ sodipodi:start="4.7135481"
+ sodipodi:end="10.994651"
+ sodipodi:open="true" />
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="254.85066"
+ y="348.96619"
+ id="text4648-4-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4650-4-5"
+ x="254.85066"
+ y="348.96619">QS</tspan></text>
+ </g>
+</svg>
diff --git a/Documentation/RCU/Design/Requirements/RCUApplicability.svg b/Documentation/RCU/Design/Requirements/RCUApplicability.svg
new file mode 100644
index 000000000000..ebcbeee391ed
--- /dev/null
+++ b/Documentation/RCU/Design/Requirements/RCUApplicability.svg
@@ -0,0 +1,237 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Creator: fig2dev Version 3.2 Patchlevel 5d -->
+
+<!-- CreationDate: Tue Mar 4 18:34:25 2014 -->
+
+<!-- Magnification: 3.000 -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="1089.1382"
+ height="668.21368"
+ viewBox="-2121 -36 14554.634 8876.4061"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="RCUApplicability.svg">
+ <metadata
+ id="metadata40">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs38" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="849"
+ inkscape:window-height="639"
+ id="namedview36"
+ showgrid="false"
+ inkscape:zoom="0.51326165"
+ inkscape:cx="544.56912"
+ inkscape:cy="334.10686"
+ inkscape:window-x="149"
+ inkscape:window-y="448"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g4"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5" />
+ <g
+ style="fill:none;stroke-width:0.025in"
+ id="g4"
+ transform="translate(-2043.6828,14.791398)">
+ <!-- Line: box -->
+ <rect
+ x="0"
+ y="0"
+ width="14400"
+ height="8775"
+ rx="0"
+ style="fill:#ffa1a1;stroke:#000000;stroke-width:21;stroke-linecap:butt;stroke-linejoin:miter"
+ id="rect6" />
+ <!-- Line: box -->
+ <rect
+ x="1350"
+ y="0"
+ width="11700"
+ height="6075"
+ rx="0"
+ style="fill:#ffff00;stroke:#000000;stroke-width:21;stroke-linecap:butt;stroke-linejoin:miter"
+ id="rect8" />
+ <!-- Line: box -->
+ <rect
+ x="2700"
+ y="0"
+ width="9000"
+ height="4275"
+ rx="0"
+ style="fill:#00ff00;stroke:#000000;stroke-width:21;stroke-linecap:butt;stroke-linejoin:miter"
+ id="rect10" />
+ <!-- Line: box -->
+ <rect
+ x="4050"
+ y="0"
+ width="6300"
+ height="2475"
+ rx="0"
+ style="fill:#87cfff;stroke:#000000;stroke-width:21;stroke-linecap:butt;stroke-linejoin:miter"
+ id="rect12" />
+ <!-- Text -->
+ <text
+ xml:space="preserve"
+ x="7200"
+ y="900"
+ font-style="normal"
+ font-weight="normal"
+ font-size="324"
+ id="text14"
+ sodipodi:linespacing="125%"
+ style="font-size:427.63009644px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"><tspan
+ style="font-size:427.63009644px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
+ id="tspan3017">Read-Mostly, Stale &amp;</tspan></text>
+ <!-- Text -->
+ <text
+ xml:space="preserve"
+ x="7200"
+ y="1350"
+ font-style="normal"
+ font-weight="normal"
+ font-size="324"
+ id="text16"
+ sodipodi:linespacing="125%"
+ style="font-size:427.63009644px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"><tspan
+ style="font-size:427.63009644px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
+ id="tspan3019">Inconsistent Data OK</tspan></text>
+ <!-- Text -->
+ <text
+ xml:space="preserve"
+ x="7200"
+ y="1800"
+ font-style="normal"
+ font-weight="normal"
+ font-size="324"
+ id="text18"
+ sodipodi:linespacing="125%"
+ style="font-size:427.63009644px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"><tspan
+ style="font-size:427.63009644px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
+ id="tspan3021">(RCU Works Great!!!)</tspan></text>
+ <!-- Text -->
+ <text
+ xml:space="preserve"
+ x="7200"
+ y="3825"
+ font-style="normal"
+ font-weight="normal"
+ font-size="324"
+ id="text20"
+ sodipodi:linespacing="125%"
+ style="font-size:427.63009644px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"><tspan
+ style="font-size:427.63009644px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
+ id="tspan3023">(RCU Works Well)</tspan></text>
+ <!-- Text -->
+ <text
+ xml:space="preserve"
+ x="7200"
+ y="3375"
+ font-style="normal"
+ font-weight="normal"
+ font-size="324"
+ id="text22"
+ sodipodi:linespacing="125%"
+ style="font-size:427.63009644px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"><tspan
+ style="font-size:427.63009644px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
+ id="tspan3025">Read-Mostly, Need Consistent Data</tspan></text>
+ <!-- Text -->
+ <text
+ xml:space="preserve"
+ x="7200"
+ y="5175"
+ font-style="normal"
+ font-weight="normal"
+ font-size="324"
+ id="text24"
+ sodipodi:linespacing="125%"
+ style="font-size:427.63009644px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"><tspan
+ style="font-size:427.63009644px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
+ id="tspan3027">Read-Write, Need Consistent Data</tspan></text>
+ <!-- Text -->
+ <text
+ xml:space="preserve"
+ x="7200"
+ y="6975"
+ font-style="normal"
+ font-weight="normal"
+ font-size="324"
+ id="text26"
+ style="font-size:427.63009644px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
+ sodipodi:linespacing="125%">Update-Mostly, Need Consistent Data</text>
+ <!-- Text -->
+ <text
+ xml:space="preserve"
+ x="7200"
+ y="5625"
+ font-style="normal"
+ font-weight="normal"
+ font-size="324"
+ id="text28"
+ sodipodi:linespacing="125%"
+ style="font-size:427.63009644px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"><tspan
+ style="font-size:427.63009644px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
+ id="tspan3029">(RCU Might Be OK...)</tspan></text>
+ <!-- Text -->
+ <text
+ xml:space="preserve"
+ x="7200"
+ y="7875"
+ font-style="normal"
+ font-weight="normal"
+ font-size="324"
+ id="text30"
+ style="font-size:427.63009644px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
+ sodipodi:linespacing="125%">(1) Provide Existence Guarantees For Update-Friendly Mechanisms</text>
+ <!-- Text -->
+ <text
+ xml:space="preserve"
+ x="7200"
+ y="8325"
+ font-style="normal"
+ font-weight="normal"
+ font-size="324"
+ id="text32"
+ style="font-size:427.63009644px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
+ sodipodi:linespacing="125%">(2) Provide Wait-Free Read-Side Primitives for Real-Time Use)</text>
+ <!-- Text -->
+ <text
+ xml:space="preserve"
+ x="7200"
+ y="7425"
+ font-style="normal"
+ font-weight="normal"
+ font-size="324"
+ id="text34"
+ style="font-size:427.63009644px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
+ sodipodi:linespacing="125%">(RCU is Very Unlikely to be the Right Tool For The Job, But it Can:</text>
+ </g>
+</svg>
diff --git a/Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg b/Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg
new file mode 100644
index 000000000000..48cd1623d4d4
--- /dev/null
+++ b/Documentation/RCU/Design/Requirements/ReadersPartitionGP1.svg
@@ -0,0 +1,639 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="735.25"
+ height="516.21875"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="ReadersPartitionGP1.svg">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path3792"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible">
+ <path
+ id="path3789"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart-4"
+ style="overflow:visible">
+ <path
+ id="path3789-9"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(1.1,0,0,1.1,1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-4"
+ style="overflow:visible">
+ <path
+ id="path3792-4"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.3670394"
+ inkscape:cx="367.26465"
+ inkscape:cy="258.46182"
+ inkscape:document-units="px"
+ inkscape:current-layer="g4433-6"
+ showgrid="false"
+ inkscape:window-width="1351"
+ inkscape:window-height="836"
+ inkscape:window-x="438"
+ inkscape:window-y="335"
+ inkscape:window-maximized="0"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-29.15625,-185.59375)">
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot2985"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"><flowRegion
+ id="flowRegion2987"><rect
+ id="rect2989"
+ width="82.85714"
+ height="11.428572"
+ x="240"
+ y="492.36218" /></flowRegion><flowPara
+ id="flowPara2991" /></flowRoot> <g
+ id="g4433"
+ transform="translate(2,-12)">
+ <text
+ sodipodi:linespacing="125%"
+ id="text2993"
+ y="-261.66608"
+ x="436.12299"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ xml:space="preserve"
+ transform="matrix(0,1,-1,0,0,0)"><tspan
+ y="-261.66608"
+ x="436.12299"
+ id="tspan2995"
+ sodipodi:role="line">synchronize_rcu()</tspan></text>
+ <g
+ id="g4417"
+ transform="matrix(0,1,-1,0,730.90257,222.4928)">
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow2Lstart);marker-end:url(#Arrow2Lend)"
+ d="M 97.580736,477.4048 327.57913,476.09759"
+ id="path2997"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 96.752718,465.38398 0,22.62742"
+ id="path4397"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 328.40703,465.38397 0,22.62742"
+ id="path4397-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ </g>
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="112.04738"
+ y="268.18076"
+ id="text4429"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4431"
+ x="112.04738"
+ y="268.18076">WRITE_ONCE(a, 1);</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="112.04738"
+ y="487.13766"
+ id="text4441"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4443"
+ x="112.04738"
+ y="487.13766">WRITE_ONCE(b, 1);</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="255.60869"
+ y="297.29346"
+ id="text4445"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4447"
+ x="255.60869"
+ y="297.29346">r1 = READ_ONCE(a);</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="255.14423"
+ y="554.61786"
+ id="text4449"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4451"
+ x="255.14423"
+ y="554.61786">WRITE_ONCE(c, 1);</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="396.10254"
+ y="370.71124"
+ id="text4453"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4455"
+ x="396.10254"
+ y="370.71124">WRITE_ONCE(d, 1);</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="396.10254"
+ y="572.13617"
+ id="text4457"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4459"
+ x="396.10254"
+ y="572.13617">r2 = READ_ONCE(c);</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="112.08231"
+ y="213.91006"
+ id="text4461"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4463"
+ x="112.08231"
+ y="213.91006">thread0()</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="252.34512"
+ y="213.91006"
+ id="text4461-6"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4463-0"
+ x="252.34512"
+ y="213.91006">thread1()</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="396.42557"
+ y="213.91006"
+ id="text4461-2"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4463-2"
+ x="396.42557"
+ y="213.91006">thread2()</tspan></text>
+ <rect
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="rect4495"
+ width="724.25244"
+ height="505.21201"
+ x="34.648232"
+ y="191.10612" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ d="m 183.14066,191.10612 0,504.24243"
+ id="path4497"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ d="m 325.13867,191.10612 0,504.24243"
+ id="path4497-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="111.75929"
+ y="251.53981"
+ id="text4429-8"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4431-9"
+ x="111.75929"
+ y="251.53981">rcu_read_lock();</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="396.10254"
+ y="353.91556"
+ id="text4429-8-9"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4431-9-4"
+ x="396.10254"
+ y="353.91556">rcu_read_lock();</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="396.10254"
+ y="587.40289"
+ id="text4429-8-9-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4431-9-4-4"
+ x="396.10254"
+ y="587.40289">rcu_read_unlock();</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="111.75929"
+ y="501.15311"
+ id="text4429-8-9-3-1"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4431-9-4-4-6"
+ x="111.75929"
+ y="501.15311">rcu_read_unlock();</tspan></text>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 33.941125,227.87568 724.941765,0"
+ id="path4608"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="394.94427"
+ y="331.66351"
+ id="text4648"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4650"
+ x="394.94427"
+ y="331.66351">QS</tspan></text>
+ <path
+ sodipodi:type="arc"
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="path4652"
+ sodipodi:cx="358.85669"
+ sodipodi:cy="142.87541"
+ sodipodi:rx="10.960155"
+ sodipodi:ry="10.253048"
+ d="m 358.86939,132.62237 a 10.960155,10.253048 0 1 1 -0.0228,0"
+ transform="translate(36.441125,185.60612)"
+ sodipodi:start="4.7135481"
+ sodipodi:end="10.994651"
+ sodipodi:open="true" />
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="112.11968"
+ y="523.77856"
+ id="text4648-4"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4650-4"
+ x="112.11968"
+ y="523.77856">QS</tspan></text>
+ <path
+ sodipodi:type="arc"
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="path4652-7"
+ sodipodi:cx="358.85669"
+ sodipodi:cy="142.87541"
+ sodipodi:rx="10.960155"
+ sodipodi:ry="10.253048"
+ d="m 358.86939,132.62237 a 10.960155,10.253048 0 1 1 -0.0228,0"
+ transform="translate(-246.38346,377.72117)"
+ sodipodi:start="4.7135481"
+ sodipodi:end="10.994651"
+ sodipodi:open="true" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="path4652-7-7"
+ sodipodi:cx="358.85669"
+ sodipodi:cy="142.87541"
+ sodipodi:rx="10.960155"
+ sodipodi:ry="10.253048"
+ d="m 358.86939,132.62237 a 10.960155,10.253048 0 1 1 -0.0228,0"
+ transform="translate(-103.65246,190.90878)"
+ sodipodi:start="4.7135481"
+ sodipodi:end="10.994651"
+ sodipodi:open="true" />
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="254.85066"
+ y="336.96619"
+ id="text4648-4-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4650-4-5"
+ x="254.85066"
+ y="336.96619">QS</tspan></text>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ d="m 470.93311,190.39903 0,504.24243"
+ id="path4497-5-6"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ d="m 616.22755,190.38323 0,504.24243"
+ id="path4497-5-2"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <g
+ id="g4433-6"
+ transform="translate(288.0964,78.32827)">
+ <text
+ sodipodi:linespacing="125%"
+ id="text2993-7"
+ y="-261.66608"
+ x="440.12299"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ xml:space="preserve"
+ transform="matrix(0,1,-1,0,0,0)"><tspan
+ y="-261.66608"
+ x="440.12299"
+ id="tspan2995-1"
+ sodipodi:role="line">synchronize_rcu()</tspan></text>
+ <g
+ id="g4417-1"
+ transform="matrix(0,1,-1,0,730.90257,222.4928)">
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow2Lstart);marker-end:url(#Arrow2Lend)"
+ d="M 97.580736,477.4048 328.5624,477.07246"
+ id="path2997-2"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 96.752718,465.38398 0,22.62742"
+ id="path4397-3"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 329.39039,465.38397 0,22.62742"
+ id="path4397-5-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ </g>
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="541.70508"
+ y="387.6217"
+ id="text4445-0"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4447-5"
+ x="541.70508"
+ y="387.6217">r3 = READ_ONCE(d);</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="541.2406"
+ y="646.94611"
+ id="text4449-6"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4451-6"
+ x="541.2406"
+ y="646.94611">WRITE_ONCE(e, 1);</tspan></text>
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="path4652-7-7-5"
+ sodipodi:cx="358.85669"
+ sodipodi:cy="142.87541"
+ sodipodi:rx="10.960155"
+ sodipodi:ry="10.253048"
+ d="m 358.86939,132.62237 a 10.960155,10.253048 0 1 1 -0.0228,0"
+ transform="translate(182.44393,281.23704)"
+ sodipodi:start="4.7135481"
+ sodipodi:end="10.994651"
+ sodipodi:open="true" />
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="540.94702"
+ y="427.29443"
+ id="text4648-4-3-1"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4650-4-5-7"
+ x="540.94702"
+ y="427.29443">QS</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="686.27747"
+ y="461.83929"
+ id="text4453-7"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4455-1"
+ x="686.27747"
+ y="461.83929">r4 = READ_ONCE(b);</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="686.27747"
+ y="669.26422"
+ id="text4457-9"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4459-2"
+ x="686.27747"
+ y="669.26422">r5 = READ_ONCE(e);</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="686.27747"
+ y="445.04358"
+ id="text4429-8-9-33"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4431-9-4-2"
+ x="686.27747"
+ y="445.04358">rcu_read_lock();</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="686.27747"
+ y="684.53094"
+ id="text4429-8-9-3-8"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4431-9-4-4-5"
+ x="686.27747"
+ y="684.53094">rcu_read_unlock();</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="685.11914"
+ y="422.79153"
+ id="text4648-9"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4650-7"
+ x="685.11914"
+ y="422.79153">QS</tspan></text>
+ <path
+ sodipodi:type="arc"
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="path4652-8"
+ sodipodi:cx="358.85669"
+ sodipodi:cy="142.87541"
+ sodipodi:rx="10.960155"
+ sodipodi:ry="10.253048"
+ d="m 358.86939,132.62237 a 10.960155,10.253048 0 1 1 -0.0228,0"
+ transform="translate(326.61602,276.73415)"
+ sodipodi:start="4.7135481"
+ sodipodi:end="10.994651"
+ sodipodi:open="true" />
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="397.85934"
+ y="609.59003"
+ id="text4648-5"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4650-77"
+ x="397.85934"
+ y="609.59003">QS</tspan></text>
+ <path
+ sodipodi:type="arc"
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="path4652-80"
+ sodipodi:cx="358.85669"
+ sodipodi:cy="142.87541"
+ sodipodi:rx="10.960155"
+ sodipodi:ry="10.253048"
+ d="m 358.86939,132.62237 a 10.960155,10.253048 0 1 1 -0.0228,0"
+ transform="translate(39.356201,463.53264)"
+ sodipodi:start="4.7135481"
+ sodipodi:end="10.994651"
+ sodipodi:open="true" />
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="256.75986"
+ y="586.99133"
+ id="text4648-5-2"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4650-77-7"
+ x="256.75986"
+ y="586.99133">QS</tspan></text>
+ <path
+ sodipodi:type="arc"
+ style="fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+ id="path4652-80-5"
+ sodipodi:cx="358.85669"
+ sodipodi:cy="142.87541"
+ sodipodi:rx="10.960155"
+ sodipodi:ry="10.253048"
+ d="m 358.86939,132.62237 a 10.960155,10.253048 0 1 1 -0.0228,0"
+ transform="translate(-101.74328,440.93395)"
+ sodipodi:start="4.7135481"
+ sodipodi:end="10.994651"
+ sodipodi:open="true" />
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="546.22791"
+ y="213.91006"
+ id="text4461-2-5"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4463-2-6"
+ x="546.22791"
+ y="213.91006">thread3()</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Symbol;-inkscape-font-specification:Symbol"
+ x="684.00067"
+ y="213.91006"
+ id="text4461-2-1"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4463-2-0"
+ x="684.00067"
+ y="213.91006">thread4()</tspan></text>
+ </g>
+</svg>
diff --git a/Documentation/RCU/Design/Requirements/Requirements.html b/Documentation/RCU/Design/Requirements/Requirements.html
new file mode 100644
index 000000000000..a725f9900ec8
--- /dev/null
+++ b/Documentation/RCU/Design/Requirements/Requirements.html
@@ -0,0 +1,2897 @@
+<!-- DO NOT HAND EDIT. -->
+<!-- Instead, edit Documentation/RCU/Design/Requirements/Requirements.htmlx and run 'sh htmlqqz.sh Documentation/RCU/Design/Requirements/Requirements' -->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+ <html>
+ <head><title>A Tour Through RCU's Requirements [LWN.net]</title>
+ <meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
+
+<h1>A Tour Through RCU's Requirements</h1>
+
+<p>Copyright IBM Corporation, 2015</p>
+<p>Author: Paul E.&nbsp;McKenney</p>
+<p><i>The initial version of this document appeared in the
+<a href="https://lwn.net/">LWN</a> articles
+<a href="https://lwn.net/Articles/652156/">here</a>,
+<a href="https://lwn.net/Articles/652677/">here</a>, and
+<a href="https://lwn.net/Articles/653326/">here</a>.</i></p>
+
+<h2>Introduction</h2>
+
+<p>
+Read-copy update (RCU) is a synchronization mechanism that is often
+used as a replacement for reader-writer locking.
+RCU is unusual in that updaters do not block readers,
+which means that RCU's read-side primitives can be exceedingly fast
+and scalable.
+In addition, updaters can make useful forward progress concurrently
+with readers.
+However, all this concurrency between RCU readers and updaters does raise
+the question of exactly what RCU readers are doing, which in turn
+raises the question of exactly what RCU's requirements are.
+
+<p>
+This document therefore summarizes RCU's requirements, and can be thought
+of as an informal, high-level specification for RCU.
+It is important to understand that RCU's specification is primarily
+empirical in nature;
+in fact, I learned about many of these requirements the hard way.
+This situation might cause some consternation, however, not only
+has this learning process been a lot of fun, but it has also been
+a great privilege to work with so many people willing to apply
+technologies in interesting new ways.
+
+<p>
+All that aside, here are the categories of currently known RCU requirements:
+</p>
+
+<ol>
+<li> <a href="#Fundamental Requirements">
+ Fundamental Requirements</a>
+<li> <a href="#Fundamental Non-Requirements">Fundamental Non-Requirements</a>
+<li> <a href="#Parallelism Facts of Life">
+ Parallelism Facts of Life</a>
+<li> <a href="#Quality-of-Implementation Requirements">
+ Quality-of-Implementation Requirements</a>
+<li> <a href="#Linux Kernel Complications">
+ Linux Kernel Complications</a>
+<li> <a href="#Software-Engineering Requirements">
+ Software-Engineering Requirements</a>
+<li> <a href="#Other RCU Flavors">
+ Other RCU Flavors</a>
+<li> <a href="#Possible Future Changes">
+ Possible Future Changes</a>
+</ol>
+
+<p>
+This is followed by a <a href="#Summary">summary</a>,
+which is in turn followed by the inevitable
+<a href="#Answers to Quick Quizzes">answers to the quick quizzes</a>.
+
+<h2><a name="Fundamental Requirements">Fundamental Requirements</a></h2>
+
+<p>
+RCU's fundamental requirements are the closest thing RCU has to hard
+mathematical requirements.
+These are:
+
+<ol>
+<li> <a href="#Grace-Period Guarantee">
+ Grace-Period Guarantee</a>
+<li> <a href="#Publish-Subscribe Guarantee">
+ Publish-Subscribe Guarantee</a>
+<li> <a href="#Memory-Barrier Guarantees">
+ Memory-Barrier Guarantees</a>
+<li> <a href="#RCU Primitives Guaranteed to Execute Unconditionally">
+ RCU Primitives Guaranteed to Execute Unconditionally</a>
+<li> <a href="#Guaranteed Read-to-Write Upgrade">
+ Guaranteed Read-to-Write Upgrade</a>
+</ol>
+
+<h3><a name="Grace-Period Guarantee">Grace-Period Guarantee</a></h3>
+
+<p>
+RCU's grace-period guarantee is unusual in being premeditated:
+Jack Slingwine and I had this guarantee firmly in mind when we started
+work on RCU (then called &ldquo;rclock&rdquo;) in the early 1990s.
+That said, the past two decades of experience with RCU have produced
+a much more detailed understanding of this guarantee.
+
+<p>
+RCU's grace-period guarantee allows updaters to wait for the completion
+of all pre-existing RCU read-side critical sections.
+An RCU read-side critical section
+begins with the marker <tt>rcu_read_lock()</tt> and ends with
+the marker <tt>rcu_read_unlock()</tt>.
+These markers may be nested, and RCU treats a nested set as one
+big RCU read-side critical section.
+Production-quality implementations of <tt>rcu_read_lock()</tt> and
+<tt>rcu_read_unlock()</tt> are extremely lightweight, and in
+fact have exactly zero overhead in Linux kernels built for production
+use with <tt>CONFIG_PREEMPT=n</tt>.
+
+<p>
+This guarantee allows ordering to be enforced with extremely low
+overhead to readers, for example:
+
+<blockquote>
+<pre>
+ 1 int x, y;
+ 2
+ 3 void thread0(void)
+ 4 {
+ 5 rcu_read_lock();
+ 6 r1 = READ_ONCE(x);
+ 7 r2 = READ_ONCE(y);
+ 8 rcu_read_unlock();
+ 9 }
+10
+11 void thread1(void)
+12 {
+13 WRITE_ONCE(x, 1);
+14 synchronize_rcu();
+15 WRITE_ONCE(y, 1);
+16 }
+</pre>
+</blockquote>
+
+<p>
+Because the <tt>synchronize_rcu()</tt> on line&nbsp;14 waits for
+all pre-existing readers, any instance of <tt>thread0()</tt> that
+loads a value of zero from <tt>x</tt> must complete before
+<tt>thread1()</tt> stores to <tt>y</tt>, so that instance must
+also load a value of zero from <tt>y</tt>.
+Similarly, any instance of <tt>thread0()</tt> that loads a value of
+one from <tt>y</tt> must have started after the
+<tt>synchronize_rcu()</tt> started, and must therefore also load
+a value of one from <tt>x</tt>.
+Therefore, the outcome:
+<blockquote>
+<pre>
+(r1 == 0 &amp;&amp; r2 == 1)
+</pre>
+</blockquote>
+cannot happen.
+
+<p><a name="Quick Quiz 1"><b>Quick Quiz 1</b>:</a>
+Wait a minute!
+You said that updaters can make useful forward progress concurrently
+with readers, but pre-existing readers will block
+<tt>synchronize_rcu()</tt>!!!
+Just who are you trying to fool???
+<br><a href="#qq1answer">Answer</a>
+
+<p>
+This scenario resembles one of the first uses of RCU in
+<a href="https://en.wikipedia.org/wiki/DYNIX">DYNIX/ptx</a>,
+which managed a distributed lock manager's transition into
+a state suitable for handling recovery from node failure,
+more or less as follows:
+
+<blockquote>
+<pre>
+ 1 #define STATE_NORMAL 0
+ 2 #define STATE_WANT_RECOVERY 1
+ 3 #define STATE_RECOVERING 2
+ 4 #define STATE_WANT_NORMAL 3
+ 5
+ 6 int state = STATE_NORMAL;
+ 7
+ 8 void do_something_dlm(void)
+ 9 {
+10 int state_snap;
+11
+12 rcu_read_lock();
+13 state_snap = READ_ONCE(state);
+14 if (state_snap == STATE_NORMAL)
+15 do_something();
+16 else
+17 do_something_carefully();
+18 rcu_read_unlock();
+19 }
+20
+21 void start_recovery(void)
+22 {
+23 WRITE_ONCE(state, STATE_WANT_RECOVERY);
+24 synchronize_rcu();
+25 WRITE_ONCE(state, STATE_RECOVERING);
+26 recovery();
+27 WRITE_ONCE(state, STATE_WANT_NORMAL);
+28 synchronize_rcu();
+29 WRITE_ONCE(state, STATE_NORMAL);
+30 }
+</pre>
+</blockquote>
+
+<p>
+The RCU read-side critical section in <tt>do_something_dlm()</tt>
+works with the <tt>synchronize_rcu()</tt> in <tt>start_recovery()</tt>
+to guarantee that <tt>do_something()</tt> never runs concurrently
+with <tt>recovery()</tt>, but with little or no synchronization
+overhead in <tt>do_something_dlm()</tt>.
+
+<p><a name="Quick Quiz 2"><b>Quick Quiz 2</b>:</a>
+Why is the <tt>synchronize_rcu()</tt> on line&nbsp;28 needed?
+<br><a href="#qq2answer">Answer</a>
+
+<p>
+In order to avoid fatal problems such as deadlocks,
+an RCU read-side critical section must not contain calls to
+<tt>synchronize_rcu()</tt>.
+Similarly, an RCU read-side critical section must not
+contain anything that waits, directly or indirectly, on completion of
+an invocation of <tt>synchronize_rcu()</tt>.
+
+<p>
+Although RCU's grace-period guarantee is useful in and of itself, with
+<a href="https://lwn.net/Articles/573497/">quite a few use cases</a>,
+it would be good to be able to use RCU to coordinate read-side
+access to linked data structures.
+For this, the grace-period guarantee is not sufficient, as can
+be seen in function <tt>add_gp_buggy()</tt> below.
+We will look at the reader's code later, but in the meantime, just think of
+the reader as locklessly picking up the <tt>gp</tt> pointer,
+and, if the value loaded is non-<tt>NULL</tt>, locklessly accessing the
+<tt>-&gt;a</tt> and <tt>-&gt;b</tt> fields.
+
+<blockquote>
+<pre>
+ 1 bool add_gp_buggy(int a, int b)
+ 2 {
+ 3 p = kmalloc(sizeof(*p), GFP_KERNEL);
+ 4 if (!p)
+ 5 return -ENOMEM;
+ 6 spin_lock(&amp;gp_lock);
+ 7 if (rcu_access_pointer(gp)) {
+ 8 spin_unlock(&amp;gp_lock);
+ 9 return false;
+10 }
+11 p-&gt;a = a;
+12 p-&gt;b = a;
+13 gp = p; /* ORDERING BUG */
+14 spin_unlock(&amp;gp_lock);
+15 return true;
+16 }
+</pre>
+</blockquote>
+
+<p>
+The problem is that both the compiler and weakly ordered CPUs are within
+their rights to reorder this code as follows:
+
+<blockquote>
+<pre>
+ 1 bool add_gp_buggy_optimized(int a, int b)
+ 2 {
+ 3 p = kmalloc(sizeof(*p), GFP_KERNEL);
+ 4 if (!p)
+ 5 return -ENOMEM;
+ 6 spin_lock(&amp;gp_lock);
+ 7 if (rcu_access_pointer(gp)) {
+ 8 spin_unlock(&amp;gp_lock);
+ 9 return false;
+10 }
+<b>11 gp = p; /* ORDERING BUG */
+12 p-&gt;a = a;
+13 p-&gt;b = a;</b>
+14 spin_unlock(&amp;gp_lock);
+15 return true;
+16 }
+</pre>
+</blockquote>
+
+<p>
+If an RCU reader fetches <tt>gp</tt> just after
+<tt>add_gp_buggy_optimized</tt> executes line&nbsp;11,
+it will see garbage in the <tt>-&gt;a</tt> and <tt>-&gt;b</tt>
+fields.
+And this is but one of many ways in which compiler and hardware optimizations
+could cause trouble.
+Therefore, we clearly need some way to prevent the compiler and the CPU from
+reordering in this manner, which brings us to the publish-subscribe
+guarantee discussed in the next section.
+
+<h3><a name="Publish-Subscribe Guarantee">Publish/Subscribe Guarantee</a></h3>
+
+<p>
+RCU's publish-subscribe guarantee allows data to be inserted
+into a linked data structure without disrupting RCU readers.
+The updater uses <tt>rcu_assign_pointer()</tt> to insert the
+new data, and readers use <tt>rcu_dereference()</tt> to
+access data, whether new or old.
+The following shows an example of insertion:
+
+<blockquote>
+<pre>
+ 1 bool add_gp(int a, int b)
+ 2 {
+ 3 p = kmalloc(sizeof(*p), GFP_KERNEL);
+ 4 if (!p)
+ 5 return -ENOMEM;
+ 6 spin_lock(&amp;gp_lock);
+ 7 if (rcu_access_pointer(gp)) {
+ 8 spin_unlock(&amp;gp_lock);
+ 9 return false;
+10 }
+11 p-&gt;a = a;
+12 p-&gt;b = a;
+13 rcu_assign_pointer(gp, p);
+14 spin_unlock(&amp;gp_lock);
+15 return true;
+16 }
+</pre>
+</blockquote>
+
+<p>
+The <tt>rcu_assign_pointer()</tt> on line&nbsp;13 is conceptually
+equivalent to a simple assignment statement, but also guarantees
+that its assignment will
+happen after the two assignments in lines&nbsp;11 and&nbsp;12,
+similar to the C11 <tt>memory_order_release</tt> store operation.
+It also prevents any number of &ldquo;interesting&rdquo; compiler
+optimizations, for example, the use of <tt>gp</tt> as a scratch
+location immediately preceding the assignment.
+
+<p><a name="Quick Quiz 3"><b>Quick Quiz 3</b>:</a>
+But <tt>rcu_assign_pointer()</tt> does nothing to prevent the
+two assignments to <tt>p-&gt;a</tt> and <tt>p-&gt;b</tt>
+from being reordered.
+Can't that also cause problems?
+<br><a href="#qq3answer">Answer</a>
+
+<p>
+It is tempting to assume that the reader need not do anything special
+to control its accesses to the RCU-protected data,
+as shown in <tt>do_something_gp_buggy()</tt> below:
+
+<blockquote>
+<pre>
+ 1 bool do_something_gp_buggy(void)
+ 2 {
+ 3 rcu_read_lock();
+ 4 p = gp; /* OPTIMIZATIONS GALORE!!! */
+ 5 if (p) {
+ 6 do_something(p-&gt;a, p-&gt;b);
+ 7 rcu_read_unlock();
+ 8 return true;
+ 9 }
+10 rcu_read_unlock();
+11 return false;
+12 }
+</pre>
+</blockquote>
+
+<p>
+However, this temptation must be resisted because there are a
+surprisingly large number of ways that the compiler
+(to say nothing of
+<a href="https://h71000.www7.hp.com/wizard/wiz_2637.html">DEC Alpha CPUs</a>)
+can trip this code up.
+For but one example, if the compiler were short of registers, it
+might choose to refetch from <tt>gp</tt> rather than keeping
+a separate copy in <tt>p</tt> as follows:
+
+<blockquote>
+<pre>
+ 1 bool do_something_gp_buggy_optimized(void)
+ 2 {
+ 3 rcu_read_lock();
+ 4 if (gp) { /* OPTIMIZATIONS GALORE!!! */
+<b> 5 do_something(gp-&gt;a, gp-&gt;b);</b>
+ 6 rcu_read_unlock();
+ 7 return true;
+ 8 }
+ 9 rcu_read_unlock();
+10 return false;
+11 }
+</pre>
+</blockquote>
+
+<p>
+If this function ran concurrently with a series of updates that
+replaced the current structure with a new one,
+the fetches of <tt>gp-&gt;a</tt>
+and <tt>gp-&gt;b</tt> might well come from two different structures,
+which could cause serious confusion.
+To prevent this (and much else besides), <tt>do_something_gp()</tt> uses
+<tt>rcu_dereference()</tt> to fetch from <tt>gp</tt>:
+
+<blockquote>
+<pre>
+ 1 bool do_something_gp(void)
+ 2 {
+ 3 rcu_read_lock();
+ 4 p = rcu_dereference(gp);
+ 5 if (p) {
+ 6 do_something(p-&gt;a, p-&gt;b);
+ 7 rcu_read_unlock();
+ 8 return true;
+ 9 }
+10 rcu_read_unlock();
+11 return false;
+12 }
+</pre>
+</blockquote>
+
+<p>
+The <tt>rcu_dereference()</tt> uses volatile casts and (for DEC Alpha)
+memory barriers in the Linux kernel.
+Should a
+<a href="http://www.rdrop.com/users/paulmck/RCU/consume.2015.07.13a.pdf">high-quality implementation of C11 <tt>memory_order_consume</tt> [PDF]</a>
+ever appear, then <tt>rcu_dereference()</tt> could be implemented
+as a <tt>memory_order_consume</tt> load.
+Regardless of the exact implementation, a pointer fetched by
+<tt>rcu_dereference()</tt> may not be used outside of the
+outermost RCU read-side critical section containing that
+<tt>rcu_dereference()</tt>, unless protection of
+the corresponding data element has been passed from RCU to some
+other synchronization mechanism, most commonly locking or
+<a href="https://www.kernel.org/doc/Documentation/RCU/rcuref.txt">reference counting</a>.
+
+<p>
+In short, updaters use <tt>rcu_assign_pointer()</tt> and readers
+use <tt>rcu_dereference()</tt>, and these two RCU API elements
+work together to ensure that readers have a consistent view of
+newly added data elements.
+
+<p>
+Of course, it is also necessary to remove elements from RCU-protected
+data structures, for example, using the following process:
+
+<ol>
+<li> Remove the data element from the enclosing structure.
+<li> Wait for all pre-existing RCU read-side critical sections
+ to complete (because only pre-existing readers can possibly have
+ a reference to the newly removed data element).
+<li> At this point, only the updater has a reference to the
+ newly removed data element, so it can safely reclaim
+ the data element, for example, by passing it to <tt>kfree()</tt>.
+</ol>
+
+This process is implemented by <tt>remove_gp_synchronous()</tt>:
+
+<blockquote>
+<pre>
+ 1 bool remove_gp_synchronous(void)
+ 2 {
+ 3 struct foo *p;
+ 4
+ 5 spin_lock(&amp;gp_lock);
+ 6 p = rcu_access_pointer(gp);
+ 7 if (!p) {
+ 8 spin_unlock(&amp;gp_lock);
+ 9 return false;
+10 }
+11 rcu_assign_pointer(gp, NULL);
+12 spin_unlock(&amp;gp_lock);
+13 synchronize_rcu();
+14 kfree(p);
+15 return true;
+16 }
+</pre>
+</blockquote>
+
+<p>
+This function is straightforward, with line&nbsp;13 waiting for a grace
+period before line&nbsp;14 frees the old data element.
+This waiting ensures that readers will reach line&nbsp;7 of
+<tt>do_something_gp()</tt> before the data element referenced by
+<tt>p</tt> is freed.
+The <tt>rcu_access_pointer()</tt> on line&nbsp;6 is similar to
+<tt>rcu_dereference()</tt>, except that:
+
+<ol>
+<li> The value returned by <tt>rcu_access_pointer()</tt>
+ cannot be dereferenced.
+ If you want to access the value pointed to as well as
+ the pointer itself, use <tt>rcu_dereference()</tt>
+ instead of <tt>rcu_access_pointer()</tt>.
+<li> The call to <tt>rcu_access_pointer()</tt> need not be
+ protected.
+ In contrast, <tt>rcu_dereference()</tt> must either be
+ within an RCU read-side critical section or in a code
+ segment where the pointer cannot change, for example, in
+ code protected by the corresponding update-side lock.
+</ol>
+
+<p><a name="Quick Quiz 4"><b>Quick Quiz 4</b>:</a>
+Without the <tt>rcu_dereference()</tt> or the
+<tt>rcu_access_pointer()</tt>, what destructive optimizations
+might the compiler make use of?
+<br><a href="#qq4answer">Answer</a>
+
+<p>
+In short, RCU's publish-subscribe guarantee is provided by the combination
+of <tt>rcu_assign_pointer()</tt> and <tt>rcu_dereference()</tt>.
+This guarantee allows data elements to be safely added to RCU-protected
+linked data structures without disrupting RCU readers.
+This guarantee can be used in combination with the grace-period
+guarantee to also allow data elements to be removed from RCU-protected
+linked data structures, again without disrupting RCU readers.
+
+<p>
+This guarantee was only partially premeditated.
+DYNIX/ptx used an explicit memory barrier for publication, but had nothing
+resembling <tt>rcu_dereference()</tt> for subscription, nor did it
+have anything resembling the <tt>smp_read_barrier_depends()</tt>
+that was later subsumed into <tt>rcu_dereference()</tt>.
+The need for these operations made itself known quite suddenly at a
+late-1990s meeting with the DEC Alpha architects, back in the days when
+DEC was still a free-standing company.
+It took the Alpha architects a good hour to convince me that any sort
+of barrier would ever be needed, and it then took me a good <i>two</i> hours
+to convince them that their documentation did not make this point clear.
+More recent work with the C and C++ standards committees have provided
+much education on tricks and traps from the compiler.
+In short, compilers were much less tricky in the early 1990s, but in
+2015, don't even think about omitting <tt>rcu_dereference()</tt>!
+
+<h3><a name="Memory-Barrier Guarantees">Memory-Barrier Guarantees</a></h3>
+
+<p>
+The previous section's simple linked-data-structure scenario clearly
+demonstrates the need for RCU's stringent memory-ordering guarantees on
+systems with more than one CPU:
+
+<ol>
+<li> Each CPU that has an RCU read-side critical section that
+ begins before <tt>synchronize_rcu()</tt> starts is
+ guaranteed to execute a full memory barrier between the time
+ that the RCU read-side critical section ends and the time that
+ <tt>synchronize_rcu()</tt> returns.
+ Without this guarantee, a pre-existing RCU read-side critical section
+ might hold a reference to the newly removed <tt>struct foo</tt>
+ after the <tt>kfree()</tt> on line&nbsp;14 of
+ <tt>remove_gp_synchronous()</tt>.
+<li> Each CPU that has an RCU read-side critical section that ends
+ after <tt>synchronize_rcu()</tt> returns is guaranteed
+ to execute a full memory barrier between the time that
+ <tt>synchronize_rcu()</tt> begins and the time that the RCU
+ read-side critical section begins.
+ Without this guarantee, a later RCU read-side critical section
+ running after the <tt>kfree()</tt> on line&nbsp;14 of
+ <tt>remove_gp_synchronous()</tt> might
+ later run <tt>do_something_gp()</tt> and find the
+ newly deleted <tt>struct foo</tt>.
+<li> If the task invoking <tt>synchronize_rcu()</tt> remains
+ on a given CPU, then that CPU is guaranteed to execute a full
+ memory barrier sometime during the execution of
+ <tt>synchronize_rcu()</tt>.
+ This guarantee ensures that the <tt>kfree()</tt> on
+ line&nbsp;14 of <tt>remove_gp_synchronous()</tt> really does
+ execute after the removal on line&nbsp;11.
+<li> If the task invoking <tt>synchronize_rcu()</tt> migrates
+ among a group of CPUs during that invocation, then each of the
+ CPUs in that group is guaranteed to execute a full memory barrier
+ sometime during the execution of <tt>synchronize_rcu()</tt>.
+ This guarantee also ensures that the <tt>kfree()</tt> on
+ line&nbsp;14 of <tt>remove_gp_synchronous()</tt> really does
+ execute after the removal on
+ line&nbsp;11, but also in the case where the thread executing the
+ <tt>synchronize_rcu()</tt> migrates in the meantime.
+</ol>
+
+<p><a name="Quick Quiz 5"><b>Quick Quiz 5</b>:</a>
+Given that multiple CPUs can start RCU read-side critical sections
+at any time without any ordering whatsoever, how can RCU possibly tell whether
+or not a given RCU read-side critical section starts before a
+given instance of <tt>synchronize_rcu()</tt>?
+<br><a href="#qq5answer">Answer</a>
+
+<p><a name="Quick Quiz 6"><b>Quick Quiz 6</b>:</a>
+The first and second guarantees require unbelievably strict ordering!
+Are all these memory barriers <i> really</i> required?
+<br><a href="#qq6answer">Answer</a>
+
+<p>
+Note that these memory-barrier requirements do not replace the fundamental
+RCU requirement that a grace period wait for all pre-existing readers.
+On the contrary, the memory barriers called out in this section must operate in
+such a way as to <i>enforce</i> this fundamental requirement.
+Of course, different implementations enforce this requirement in different
+ways, but enforce it they must.
+
+<h3><a name="RCU Primitives Guaranteed to Execute Unconditionally">RCU Primitives Guaranteed to Execute Unconditionally</a></h3>
+
+<p>
+The common-case RCU primitives are unconditional.
+They are invoked, they do their job, and they return, with no possibility
+of error, and no need to retry.
+This is a key RCU design philosophy.
+
+<p>
+However, this philosophy is pragmatic rather than pigheaded.
+If someone comes up with a good justification for a particular conditional
+RCU primitive, it might well be implemented and added.
+After all, this guarantee was reverse-engineered, not premeditated.
+The unconditional nature of the RCU primitives was initially an
+accident of implementation, and later experience with synchronization
+primitives with conditional primitives caused me to elevate this
+accident to a guarantee.
+Therefore, the justification for adding a conditional primitive to
+RCU would need to be based on detailed and compelling use cases.
+
+<h3><a name="Guaranteed Read-to-Write Upgrade">Guaranteed Read-to-Write Upgrade</a></h3>
+
+<p>
+As far as RCU is concerned, it is always possible to carry out an
+update within an RCU read-side critical section.
+For example, that RCU read-side critical section might search for
+a given data element, and then might acquire the update-side
+spinlock in order to update that element, all while remaining
+in that RCU read-side critical section.
+Of course, it is necessary to exit the RCU read-side critical section
+before invoking <tt>synchronize_rcu()</tt>, however, this
+inconvenience can be avoided through use of the
+<tt>call_rcu()</tt> and <tt>kfree_rcu()</tt> API members
+described later in this document.
+
+<p><a name="Quick Quiz 7"><b>Quick Quiz 7</b>:</a>
+But how does the upgrade-to-write operation exclude other readers?
+<br><a href="#qq7answer">Answer</a>
+
+<p>
+This guarantee allows lookup code to be shared between read-side
+and update-side code, and was premeditated, appearing in the earliest
+DYNIX/ptx RCU documentation.
+
+<h2><a name="Fundamental Non-Requirements">Fundamental Non-Requirements</a></h2>
+
+<p>
+RCU provides extremely lightweight readers, and its read-side guarantees,
+though quite useful, are correspondingly lightweight.
+It is therefore all too easy to assume that RCU is guaranteeing more
+than it really is.
+Of course, the list of things that RCU does not guarantee is infinitely
+long, however, the following sections list a few non-guarantees that
+have caused confusion.
+Except where otherwise noted, these non-guarantees were premeditated.
+
+<ol>
+<li> <a href="#Readers Impose Minimal Ordering">
+ Readers Impose Minimal Ordering</a>
+<li> <a href="#Readers Do Not Exclude Updaters">
+ Readers Do Not Exclude Updaters</a>
+<li> <a href="#Updaters Only Wait For Old Readers">
+ Updaters Only Wait For Old Readers</a>
+<li> <a href="#Grace Periods Don't Partition Read-Side Critical Sections">
+ Grace Periods Don't Partition Read-Side Critical Sections</a>
+<li> <a href="#Read-Side Critical Sections Don't Partition Grace Periods">
+ Read-Side Critical Sections Don't Partition Grace Periods</a>
+<li> <a href="#Disabling Preemption Does Not Block Grace Periods">
+ Disabling Preemption Does Not Block Grace Periods</a>
+</ol>
+
+<h3><a name="Readers Impose Minimal Ordering">Readers Impose Minimal Ordering</a></h3>
+
+<p>
+Reader-side markers such as <tt>rcu_read_lock()</tt> and
+<tt>rcu_read_unlock()</tt> provide absolutely no ordering guarantees
+except through their interaction with the grace-period APIs such as
+<tt>synchronize_rcu()</tt>.
+To see this, consider the following pair of threads:
+
+<blockquote>
+<pre>
+ 1 void thread0(void)
+ 2 {
+ 3 rcu_read_lock();
+ 4 WRITE_ONCE(x, 1);
+ 5 rcu_read_unlock();
+ 6 rcu_read_lock();
+ 7 WRITE_ONCE(y, 1);
+ 8 rcu_read_unlock();
+ 9 }
+10
+11 void thread1(void)
+12 {
+13 rcu_read_lock();
+14 r1 = READ_ONCE(y);
+15 rcu_read_unlock();
+16 rcu_read_lock();
+17 r2 = READ_ONCE(x);
+18 rcu_read_unlock();
+19 }
+</pre>
+</blockquote>
+
+<p>
+After <tt>thread0()</tt> and <tt>thread1()</tt> execute
+concurrently, it is quite possible to have
+
+<blockquote>
+<pre>
+(r1 == 1 &amp;&amp; r2 == 0)
+</pre>
+</blockquote>
+
+(that is, <tt>y</tt> appears to have been assigned before <tt>x</tt>),
+which would not be possible if <tt>rcu_read_lock()</tt> and
+<tt>rcu_read_unlock()</tt> had much in the way of ordering
+properties.
+But they do not, so the CPU is within its rights
+to do significant reordering.
+This is by design: Any significant ordering constraints would slow down
+these fast-path APIs.
+
+<p><a name="Quick Quiz 8"><b>Quick Quiz 8</b>:</a>
+Can't the compiler also reorder this code?
+<br><a href="#qq8answer">Answer</a>
+
+<h3><a name="Readers Do Not Exclude Updaters">Readers Do Not Exclude Updaters</a></h3>
+
+<p>
+Neither <tt>rcu_read_lock()</tt> nor <tt>rcu_read_unlock()</tt>
+exclude updates.
+All they do is to prevent grace periods from ending.
+The following example illustrates this:
+
+<blockquote>
+<pre>
+ 1 void thread0(void)
+ 2 {
+ 3 rcu_read_lock();
+ 4 r1 = READ_ONCE(y);
+ 5 if (r1) {
+ 6 do_something_with_nonzero_x();
+ 7 r2 = READ_ONCE(x);
+ 8 WARN_ON(!r2); /* BUG!!! */
+ 9 }
+10 rcu_read_unlock();
+11 }
+12
+13 void thread1(void)
+14 {
+15 spin_lock(&amp;my_lock);
+16 WRITE_ONCE(x, 1);
+17 WRITE_ONCE(y, 1);
+18 spin_unlock(&amp;my_lock);
+19 }
+</pre>
+</blockquote>
+
+<p>
+If the <tt>thread0()</tt> function's <tt>rcu_read_lock()</tt>
+excluded the <tt>thread1()</tt> function's update,
+the <tt>WARN_ON()</tt> could never fire.
+But the fact is that <tt>rcu_read_lock()</tt> does not exclude
+much of anything aside from subsequent grace periods, of which
+<tt>thread1()</tt> has none, so the
+<tt>WARN_ON()</tt> can and does fire.
+
+<h3><a name="Updaters Only Wait For Old Readers">Updaters Only Wait For Old Readers</a></h3>
+
+<p>
+It might be tempting to assume that after <tt>synchronize_rcu()</tt>
+completes, there are no readers executing.
+This temptation must be avoided because
+new readers can start immediately after <tt>synchronize_rcu()</tt>
+starts, and <tt>synchronize_rcu()</tt> is under no
+obligation to wait for these new readers.
+
+<p><a name="Quick Quiz 9"><b>Quick Quiz 9</b>:</a>
+Suppose that synchronize_rcu() did wait until all readers had completed.
+Would the updater be able to rely on this?
+<br><a href="#qq9answer">Answer</a>
+
+<h3><a name="Grace Periods Don't Partition Read-Side Critical Sections">
+Grace Periods Don't Partition Read-Side Critical Sections</a></h3>
+
+<p>
+It is tempting to assume that if any part of one RCU read-side critical
+section precedes a given grace period, and if any part of another RCU
+read-side critical section follows that same grace period, then all of
+the first RCU read-side critical section must precede all of the second.
+However, this just isn't the case: A single grace period does not
+partition the set of RCU read-side critical sections.
+An example of this situation can be illustrated as follows, where
+<tt>x</tt>, <tt>y</tt>, and <tt>z</tt> are initially all zero:
+
+<blockquote>
+<pre>
+ 1 void thread0(void)
+ 2 {
+ 3 rcu_read_lock();
+ 4 WRITE_ONCE(a, 1);
+ 5 WRITE_ONCE(b, 1);
+ 6 rcu_read_unlock();
+ 7 }
+ 8
+ 9 void thread1(void)
+10 {
+11 r1 = READ_ONCE(a);
+12 synchronize_rcu();
+13 WRITE_ONCE(c, 1);
+14 }
+15
+16 void thread2(void)
+17 {
+18 rcu_read_lock();
+19 r2 = READ_ONCE(b);
+20 r3 = READ_ONCE(c);
+21 rcu_read_unlock();
+22 }
+</pre>
+</blockquote>
+
+<p>
+It turns out that the outcome:
+
+<blockquote>
+<pre>
+(r1 == 1 &amp;&amp; r2 == 0 &amp;&amp; r3 == 1)
+</pre>
+</blockquote>
+
+is entirely possible.
+The following figure show how this can happen, with each circled
+<tt>QS</tt> indicating the point at which RCU recorded a
+<i>quiescent state</i> for each thread, that is, a state in which
+RCU knows that the thread cannot be in the midst of an RCU read-side
+critical section that started before the current grace period:
+
+<p><img src="GPpartitionReaders1.svg" alt="GPpartitionReaders1.svg" width="60%"></p>
+
+<p>
+If it is necessary to partition RCU read-side critical sections in this
+manner, it is necessary to use two grace periods, where the first
+grace period is known to end before the second grace period starts:
+
+<blockquote>
+<pre>
+ 1 void thread0(void)
+ 2 {
+ 3 rcu_read_lock();
+ 4 WRITE_ONCE(a, 1);
+ 5 WRITE_ONCE(b, 1);
+ 6 rcu_read_unlock();
+ 7 }
+ 8
+ 9 void thread1(void)
+10 {
+11 r1 = READ_ONCE(a);
+12 synchronize_rcu();
+13 WRITE_ONCE(c, 1);
+14 }
+15
+16 void thread2(void)
+17 {
+18 r2 = READ_ONCE(c);
+19 synchronize_rcu();
+20 WRITE_ONCE(d, 1);
+21 }
+22
+23 void thread3(void)
+24 {
+25 rcu_read_lock();
+26 r3 = READ_ONCE(b);
+27 r4 = READ_ONCE(d);
+28 rcu_read_unlock();
+29 }
+</pre>
+</blockquote>
+
+<p>
+Here, if <tt>(r1 == 1)</tt>, then
+<tt>thread0()</tt>'s write to <tt>b</tt> must happen
+before the end of <tt>thread1()</tt>'s grace period.
+If in addition <tt>(r4 == 1)</tt>, then
+<tt>thread3()</tt>'s read from <tt>b</tt> must happen
+after the beginning of <tt>thread2()</tt>'s grace period.
+If it is also the case that <tt>(r2 == 1)</tt>, then the
+end of <tt>thread1()</tt>'s grace period must precede the
+beginning of <tt>thread2()</tt>'s grace period.
+This mean that the two RCU read-side critical sections cannot overlap,
+guaranteeing that <tt>(r3 == 1)</tt>.
+As a result, the outcome:
+
+<blockquote>
+<pre>
+(r1 == 1 &amp;&amp; r2 == 1 &amp;&amp; r3 == 0 &amp;&amp; r4 == 1)
+</pre>
+</blockquote>
+
+cannot happen.
+
+<p>
+This non-requirement was also non-premeditated, but became apparent
+when studying RCU's interaction with memory ordering.
+
+<h3><a name="Read-Side Critical Sections Don't Partition Grace Periods">
+Read-Side Critical Sections Don't Partition Grace Periods</a></h3>
+
+<p>
+It is also tempting to assume that if an RCU read-side critical section
+happens between a pair of grace periods, then those grace periods cannot
+overlap.
+However, this temptation leads nowhere good, as can be illustrated by
+the following, with all variables initially zero:
+
+<blockquote>
+<pre>
+ 1 void thread0(void)
+ 2 {
+ 3 rcu_read_lock();
+ 4 WRITE_ONCE(a, 1);
+ 5 WRITE_ONCE(b, 1);
+ 6 rcu_read_unlock();
+ 7 }
+ 8
+ 9 void thread1(void)
+10 {
+11 r1 = READ_ONCE(a);
+12 synchronize_rcu();
+13 WRITE_ONCE(c, 1);
+14 }
+15
+16 void thread2(void)
+17 {
+18 rcu_read_lock();
+19 WRITE_ONCE(d, 1);
+20 r2 = READ_ONCE(c);
+21 rcu_read_unlock();
+22 }
+23
+24 void thread3(void)
+25 {
+26 r3 = READ_ONCE(d);
+27 synchronize_rcu();
+28 WRITE_ONCE(e, 1);
+29 }
+30
+31 void thread4(void)
+32 {
+33 rcu_read_lock();
+34 r4 = READ_ONCE(b);
+35 r5 = READ_ONCE(e);
+36 rcu_read_unlock();
+37 }
+</pre>
+</blockquote>
+
+<p>
+In this case, the outcome:
+
+<blockquote>
+<pre>
+(r1 == 1 &amp;&amp; r2 == 1 &amp;&amp; r3 == 1 &amp;&amp; r4 == 0 &amp&amp; r5 == 1)
+</pre>
+</blockquote>
+
+is entirely possible, as illustrated below:
+
+<p><img src="ReadersPartitionGP1.svg" alt="ReadersPartitionGP1.svg" width="100%"></p>
+
+<p>
+Again, an RCU read-side critical section can overlap almost all of a
+given grace period, just so long as it does not overlap the entire
+grace period.
+As a result, an RCU read-side critical section cannot partition a pair
+of RCU grace periods.
+
+<p><a name="Quick Quiz 10"><b>Quick Quiz 10</b>:</a>
+How long a sequence of grace periods, each separated by an RCU read-side
+critical section, would be required to partition the RCU read-side
+critical sections at the beginning and end of the chain?
+<br><a href="#qq10answer">Answer</a>
+
+<h3><a name="Disabling Preemption Does Not Block Grace Periods">
+Disabling Preemption Does Not Block Grace Periods</a></h3>
+
+<p>
+There was a time when disabling preemption on any given CPU would block
+subsequent grace periods.
+However, this was an accident of implementation and is not a requirement.
+And in the current Linux-kernel implementation, disabling preemption
+on a given CPU in fact does not block grace periods, as Oleg Nesterov
+<a href="https://lkml.kernel.org/g/20150614193825.GA19582@redhat.com">demonstrated</a>.
+
+<p>
+If you need a preempt-disable region to block grace periods, you need to add
+<tt>rcu_read_lock()</tt> and <tt>rcu_read_unlock()</tt>, for example
+as follows:
+
+<blockquote>
+<pre>
+ 1 preempt_disable();
+ 2 rcu_read_lock();
+ 3 do_something();
+ 4 rcu_read_unlock();
+ 5 preempt_enable();
+ 6
+ 7 /* Spinlocks implicitly disable preemption. */
+ 8 spin_lock(&amp;mylock);
+ 9 rcu_read_lock();
+10 do_something();
+11 rcu_read_unlock();
+12 spin_unlock(&amp;mylock);
+</pre>
+</blockquote>
+
+<p>
+In theory, you could enter the RCU read-side critical section first,
+but it is more efficient to keep the entire RCU read-side critical
+section contained in the preempt-disable region as shown above.
+Of course, RCU read-side critical sections that extend outside of
+preempt-disable regions will work correctly, but such critical sections
+can be preempted, which forces <tt>rcu_read_unlock()</tt> to do
+more work.
+And no, this is <i>not</i> an invitation to enclose all of your RCU
+read-side critical sections within preempt-disable regions, because
+doing so would degrade real-time response.
+
+<p>
+This non-requirement appeared with preemptible RCU.
+If you need a grace period that waits on non-preemptible code regions, use
+<a href="#Sched Flavor">RCU-sched</a>.
+
+<h2><a name="Parallelism Facts of Life">Parallelism Facts of Life</a></h2>
+
+<p>
+These parallelism facts of life are by no means specific to RCU, but
+the RCU implementation must abide by them.
+They therefore bear repeating:
+
+<ol>
+<li> Any CPU or task may be delayed at any time,
+ and any attempts to avoid these delays by disabling
+ preemption, interrupts, or whatever are completely futile.
+ This is most obvious in preemptible user-level
+ environments and in virtualized environments (where
+ a given guest OS's VCPUs can be preempted at any time by
+ the underlying hypervisor), but can also happen in bare-metal
+ environments due to ECC errors, NMIs, and other hardware
+ events.
+ Although a delay of more than about 20 seconds can result
+ in splats, the RCU implementation is obligated to use
+ algorithms that can tolerate extremely long delays, but where
+ &ldquo;extremely long&rdquo; is not long enough to allow
+ wrap-around when incrementing a 64-bit counter.
+<li> Both the compiler and the CPU can reorder memory accesses.
+ Where it matters, RCU must use compiler directives and
+ memory-barrier instructions to preserve ordering.
+<li> Conflicting writes to memory locations in any given cache line
+ will result in expensive cache misses.
+ Greater numbers of concurrent writes and more-frequent
+ concurrent writes will result in more dramatic slowdowns.
+ RCU is therefore obligated to use algorithms that have
+ sufficient locality to avoid significant performance and
+ scalability problems.
+<li> As a rough rule of thumb, only one CPU's worth of processing
+ may be carried out under the protection of any given exclusive
+ lock.
+ RCU must therefore use scalable locking designs.
+<li> Counters are finite, especially on 32-bit systems.
+ RCU's use of counters must therefore tolerate counter wrap,
+ or be designed such that counter wrap would take way more
+ time than a single system is likely to run.
+ An uptime of ten years is quite possible, a runtime
+ of a century much less so.
+ As an example of the latter, RCU's dyntick-idle nesting counter
+ allows 54 bits for interrupt nesting level (this counter
+ is 64 bits even on a 32-bit system).
+ Overflowing this counter requires 2<sup>54</sup>
+ half-interrupts on a given CPU without that CPU ever going idle.
+ If a half-interrupt happened every microsecond, it would take
+ 570 years of runtime to overflow this counter, which is currently
+ believed to be an acceptably long time.
+<li> Linux systems can have thousands of CPUs running a single
+ Linux kernel in a single shared-memory environment.
+ RCU must therefore pay close attention to high-end scalability.
+</ol>
+
+<p>
+This last parallelism fact of life means that RCU must pay special
+attention to the preceding facts of life.
+The idea that Linux might scale to systems with thousands of CPUs would
+have been met with some skepticism in the 1990s, but these requirements
+would have otherwise have been unsurprising, even in the early 1990s.
+
+<h2><a name="Quality-of-Implementation Requirements">Quality-of-Implementation Requirements</a></h2>
+
+<p>
+These sections list quality-of-implementation requirements.
+Although an RCU implementation that ignores these requirements could
+still be used, it would likely be subject to limitations that would
+make it inappropriate for industrial-strength production use.
+Classes of quality-of-implementation requirements are as follows:
+
+<ol>
+<li> <a href="#Specialization">Specialization</a>
+<li> <a href="#Performance and Scalability">Performance and Scalability</a>
+<li> <a href="#Composability">Composability</a>
+<li> <a href="#Corner Cases">Corner Cases</a>
+</ol>
+
+<p>
+These classes is covered in the following sections.
+
+<h3><a name="Specialization">Specialization</a></h3>
+
+<p>
+RCU is and always has been intended primarily for read-mostly situations, as
+illustrated by the following figure.
+This means that RCU's read-side primitives are optimized, often at the
+expense of its update-side primitives.
+
+<p><img src="RCUApplicability.svg" alt="RCUApplicability.svg" width="70%"></p>
+
+<p>
+This focus on read-mostly situations means that RCU must interoperate
+with other synchronization primitives.
+For example, the <tt>add_gp()</tt> and <tt>remove_gp_synchronous()</tt>
+examples discussed earlier use RCU to protect readers and locking to
+coordinate updaters.
+However, the need extends much farther, requiring that a variety of
+synchronization primitives be legal within RCU read-side critical sections,
+including spinlocks, sequence locks, atomic operations, reference
+counters, and memory barriers.
+
+<p><a name="Quick Quiz 11"><b>Quick Quiz 11</b>:</a>
+What about sleeping locks?
+<br><a href="#qq11answer">Answer</a>
+
+<p>
+It often comes as a surprise that many algorithms do not require a
+consistent view of data, but many can function in that mode,
+with network routing being the poster child.
+Internet routing algorithms take significant time to propagate
+updates, so that by the time an update arrives at a given system,
+that system has been sending network traffic the wrong way for
+a considerable length of time.
+Having a few threads continue to send traffic the wrong way for a
+few more milliseconds is clearly not a problem: In the worst case,
+TCP retransmissions will eventually get the data where it needs to go.
+In general, when tracking the state of the universe outside of the
+computer, some level of inconsistency must be tolerated due to
+speed-of-light delays if nothing else.
+
+<p>
+Furthermore, uncertainty about external state is inherent in many cases.
+For example, a pair of veternarians might use heartbeat to determine
+whether or not a given cat was alive.
+But how long should they wait after the last heartbeat to decide that
+the cat is in fact dead?
+Waiting less than 400 milliseconds makes no sense because this would
+mean that a relaxed cat would be considered to cycle between death
+and life more than 100 times per minute.
+Moreover, just as with human beings, a cat's heart might stop for
+some period of time, so the exact wait period is a judgment call.
+One of our pair of veternarians might wait 30 seconds before pronouncing
+the cat dead, while the other might insist on waiting a full minute.
+The two veternarians would then disagree on the state of the cat during
+the final 30 seconds of the minute following the last heartbeat, as
+fancifully illustrated below:
+
+<p><img src="2013-08-is-it-dead.png" alt="2013-08-is-it-dead.png" width="431"></p>
+
+<p>
+Interestingly enough, this same situation applies to hardware.
+When push comes to shove, how do we tell whether or not some
+external server has failed?
+We send messages to it periodically, and declare it failed if we
+don't receive a response within a given period of time.
+Policy decisions can usually tolerate short
+periods of inconsistency.
+The policy was decided some time ago, and is only now being put into
+effect, so a few milliseconds of delay is normally inconsequential.
+
+<p>
+However, there are algorithms that absolutely must see consistent data.
+For example, the translation between a user-level SystemV semaphore
+ID to the corresponding in-kernel data structure is protected by RCU,
+but it is absolutely forbidden to update a semaphore that has just been
+removed.
+In the Linux kernel, this need for consistency is accommodated by acquiring
+spinlocks located in the in-kernel data structure from within
+the RCU read-side critical section, and this is indicated by the
+green box in the figure above.
+Many other techniques may be used, and are in fact used within the
+Linux kernel.
+
+<p>
+In short, RCU is not required to maintain consistency, and other
+mechanisms may be used in concert with RCU when consistency is required.
+RCU's specialization allows it to do its job extremely well, and its
+ability to interoperate with other synchronization mechanisms allows
+the right mix of synchronization tools to be used for a given job.
+
+<h3><a name="Performance and Scalability">Performance and Scalability</a></h3>
+
+<p>
+Energy efficiency is a critical component of performance today,
+and Linux-kernel RCU implementations must therefore avoid unnecessarily
+awakening idle CPUs.
+I cannot claim that this requirement was premeditated.
+In fact, I learned of it during a telephone conversation in which I
+was given &ldquo;frank and open&rdquo; feedback on the importance
+of energy efficiency in battery-powered systems and on specific
+energy-efficiency shortcomings of the Linux-kernel RCU implementation.
+In my experience, the battery-powered embedded community will consider
+any unnecessary wakeups to be extremely unfriendly acts.
+So much so that mere Linux-kernel-mailing-list posts are
+insufficient to vent their ire.
+
+<p>
+Memory consumption is not particularly important for in most
+situations, and has become decreasingly
+so as memory sizes have expanded and memory
+costs have plummeted.
+However, as I learned from Matt Mackall's
+<a href="http://elinux.org/Linux_Tiny-FAQ">bloatwatch</a>
+efforts, memory footprint is critically important on single-CPU systems with
+non-preemptible (<tt>CONFIG_PREEMPT=n</tt>) kernels, and thus
+<a href="https://lkml.kernel.org/g/20090113221724.GA15307@linux.vnet.ibm.com">tiny RCU</a>
+was born.
+Josh Triplett has since taken over the small-memory banner with his
+<a href="https://tiny.wiki.kernel.org/">Linux kernel tinification</a>
+project, which resulted in
+<a href="#Sleepable RCU">SRCU</a>
+becoming optional for those kernels not needing it.
+
+<p>
+The remaining performance requirements are, for the most part,
+unsurprising.
+For example, in keeping with RCU's read-side specialization,
+<tt>rcu_dereference()</tt> should have negligible overhead (for
+example, suppression of a few minor compiler optimizations).
+Similarly, in non-preemptible environments, <tt>rcu_read_lock()</tt> and
+<tt>rcu_read_unlock()</tt> should have exactly zero overhead.
+
+<p>
+In preemptible environments, in the case where the RCU read-side
+critical section was not preempted (as will be the case for the
+highest-priority real-time process), <tt>rcu_read_lock()</tt> and
+<tt>rcu_read_unlock()</tt> should have minimal overhead.
+In particular, they should not contain atomic read-modify-write
+operations, memory-barrier instructions, preemption disabling,
+interrupt disabling, or backwards branches.
+However, in the case where the RCU read-side critical section was preempted,
+<tt>rcu_read_unlock()</tt> may acquire spinlocks and disable interrupts.
+This is why it is better to nest an RCU read-side critical section
+within a preempt-disable region than vice versa, at least in cases
+where that critical section is short enough to avoid unduly degrading
+real-time latencies.
+
+<p>
+The <tt>synchronize_rcu()</tt> grace-period-wait primitive is
+optimized for throughput.
+It may therefore incur several milliseconds of latency in addition to
+the duration of the longest RCU read-side critical section.
+On the other hand, multiple concurrent invocations of
+<tt>synchronize_rcu()</tt> are required to use batching optimizations
+so that they can be satisfied by a single underlying grace-period-wait
+operation.
+For example, in the Linux kernel, it is not unusual for a single
+grace-period-wait operation to serve more than
+<a href="https://www.usenix.org/conference/2004-usenix-annual-technical-conference/making-rcu-safe-deep-sub-millisecond-response">1,000 separate invocations</a>
+of <tt>synchronize_rcu()</tt>, thus amortizing the per-invocation
+overhead down to nearly zero.
+However, the grace-period optimization is also required to avoid
+measurable degradation of real-time scheduling and interrupt latencies.
+
+<p>
+In some cases, the multi-millisecond <tt>synchronize_rcu()</tt>
+latencies are unacceptable.
+In these cases, <tt>synchronize_rcu_expedited()</tt> may be used
+instead, reducing the grace-period latency down to a few tens of
+microseconds on small systems, at least in cases where the RCU read-side
+critical sections are short.
+There are currently no special latency requirements for
+<tt>synchronize_rcu_expedited()</tt> on large systems, but,
+consistent with the empirical nature of the RCU specification,
+that is subject to change.
+However, there most definitely are scalability requirements:
+A storm of <tt>synchronize_rcu_expedited()</tt> invocations on 4096
+CPUs should at least make reasonable forward progress.
+In return for its shorter latencies, <tt>synchronize_rcu_expedited()</tt>
+is permitted to impose modest degradation of real-time latency
+on non-idle online CPUs.
+That said, it will likely be necessary to take further steps to reduce this
+degradation, hopefully to roughly that of a scheduling-clock interrupt.
+
+<p>
+There are a number of situations where even
+<tt>synchronize_rcu_expedited()</tt>'s reduced grace-period
+latency is unacceptable.
+In these situations, the asynchronous <tt>call_rcu()</tt> can be
+used in place of <tt>synchronize_rcu()</tt> as follows:
+
+<blockquote>
+<pre>
+ 1 struct foo {
+ 2 int a;
+ 3 int b;
+ 4 struct rcu_head rh;
+ 5 };
+ 6
+ 7 static void remove_gp_cb(struct rcu_head *rhp)
+ 8 {
+ 9 struct foo *p = container_of(rhp, struct foo, rh);
+10
+11 kfree(p);
+12 }
+13
+14 bool remove_gp_asynchronous(void)
+15 {
+16 struct foo *p;
+17
+18 spin_lock(&amp;gp_lock);
+19 p = rcu_dereference(gp);
+20 if (!p) {
+21 spin_unlock(&amp;gp_lock);
+22 return false;
+23 }
+24 rcu_assign_pointer(gp, NULL);
+25 call_rcu(&amp;p-&gt;rh, remove_gp_cb);
+26 spin_unlock(&amp;gp_lock);
+27 return true;
+28 }
+</pre>
+</blockquote>
+
+<p>
+A definition of <tt>struct foo</tt> is finally needed, and appears
+on lines&nbsp;1-5.
+The function <tt>remove_gp_cb()</tt> is passed to <tt>call_rcu()</tt>
+on line&nbsp;25, and will be invoked after the end of a subsequent
+grace period.
+This gets the same effect as <tt>remove_gp_synchronous()</tt>,
+but without forcing the updater to wait for a grace period to elapse.
+The <tt>call_rcu()</tt> function may be used in a number of
+situations where neither <tt>synchronize_rcu()</tt> nor
+<tt>synchronize_rcu_expedited()</tt> would be legal,
+including within preempt-disable code, <tt>local_bh_disable()</tt> code,
+interrupt-disable code, and interrupt handlers.
+However, even <tt>call_rcu()</tt> is illegal within NMI handlers.
+The callback function (<tt>remove_gp_cb()</tt> in this case) will be
+executed within softirq (software interrupt) environment within the
+Linux kernel,
+either within a real softirq handler or under the protection
+of <tt>local_bh_disable()</tt>.
+In both the Linux kernel and in userspace, it is bad practice to
+write an RCU callback function that takes too long.
+Long-running operations should be relegated to separate threads or
+(in the Linux kernel) workqueues.
+
+<p><a name="Quick Quiz 12"><b>Quick Quiz 12</b>:</a>
+Why does line&nbsp;19 use <tt>rcu_access_pointer()</tt>?
+After all, <tt>call_rcu()</tt> on line&nbsp;25 stores into the
+structure, which would interact badly with concurrent insertions.
+Doesn't this mean that <tt>rcu_dereference()</tt> is required?
+<br><a href="#qq12answer">Answer</a>
+
+<p>
+However, all that <tt>remove_gp_cb()</tt> is doing is
+invoking <tt>kfree()</tt> on the data element.
+This is a common idiom, and is supported by <tt>kfree_rcu()</tt>,
+which allows &ldquo;fire and forget&rdquo; operation as shown below:
+
+<blockquote>
+<pre>
+ 1 struct foo {
+ 2 int a;
+ 3 int b;
+ 4 struct rcu_head rh;
+ 5 };
+ 6
+ 7 bool remove_gp_faf(void)
+ 8 {
+ 9 struct foo *p;
+10
+11 spin_lock(&amp;gp_lock);
+12 p = rcu_dereference(gp);
+13 if (!p) {
+14 spin_unlock(&amp;gp_lock);
+15 return false;
+16 }
+17 rcu_assign_pointer(gp, NULL);
+18 kfree_rcu(p, rh);
+19 spin_unlock(&amp;gp_lock);
+20 return true;
+21 }
+</pre>
+</blockquote>
+
+<p>
+Note that <tt>remove_gp_faf()</tt> simply invokes
+<tt>kfree_rcu()</tt> and proceeds, without any need to pay any
+further attention to the subsequent grace period and <tt>kfree()</tt>.
+It is permissible to invoke <tt>kfree_rcu()</tt> from the same
+environments as for <tt>call_rcu()</tt>.
+Interestingly enough, DYNIX/ptx had the equivalents of
+<tt>call_rcu()</tt> and <tt>kfree_rcu()</tt>, but not
+<tt>synchronize_rcu()</tt>.
+This was due to the fact that RCU was not heavily used within DYNIX/ptx,
+so the very few places that needed something like
+<tt>synchronize_rcu()</tt> simply open-coded it.
+
+<p><a name="Quick Quiz 13"><b>Quick Quiz 13</b>:</a>
+Earlier it was claimed that <tt>call_rcu()</tt> and
+<tt>kfree_rcu()</tt> allowed updaters to avoid being blocked
+by readers.
+But how can that be correct, given that the invocation of the callback
+and the freeing of the memory (respectively) must still wait for
+a grace period to elapse?
+<br><a href="#qq13answer">Answer</a>
+
+<p>
+But what if the updater must wait for the completion of code to be
+executed after the end of the grace period, but has other tasks
+that can be carried out in the meantime?
+The polling-style <tt>get_state_synchronize_rcu()</tt> and
+<tt>cond_synchronize_rcu()</tt> functions may be used for this
+purpose, as shown below:
+
+<blockquote>
+<pre>
+ 1 bool remove_gp_poll(void)
+ 2 {
+ 3 struct foo *p;
+ 4 unsigned long s;
+ 5
+ 6 spin_lock(&amp;gp_lock);
+ 7 p = rcu_access_pointer(gp);
+ 8 if (!p) {
+ 9 spin_unlock(&amp;gp_lock);
+10 return false;
+11 }
+12 rcu_assign_pointer(gp, NULL);
+13 spin_unlock(&amp;gp_lock);
+14 s = get_state_synchronize_rcu();
+15 do_something_while_waiting();
+16 cond_synchronize_rcu(s);
+17 kfree(p);
+18 return true;
+19 }
+</pre>
+</blockquote>
+
+<p>
+On line&nbsp;14, <tt>get_state_synchronize_rcu()</tt> obtains a
+&ldquo;cookie&rdquo; from RCU,
+then line&nbsp;15 carries out other tasks,
+and finally, line&nbsp;16 returns immediately if a grace period has
+elapsed in the meantime, but otherwise waits as required.
+The need for <tt>get_state_synchronize_rcu</tt> and
+<tt>cond_synchronize_rcu()</tt> has appeared quite recently,
+so it is too early to tell whether they will stand the test of time.
+
+<p>
+RCU thus provides a range of tools to allow updaters to strike the
+required tradeoff between latency, flexibility and CPU overhead.
+
+<h3><a name="Composability">Composability</a></h3>
+
+<p>
+Composability has received much attention in recent years, perhaps in part
+due to the collision of multicore hardware with object-oriented techniques
+designed in single-threaded environments for single-threaded use.
+And in theory, RCU read-side critical sections may be composed, and in
+fact may be nested arbitrarily deeply.
+In practice, as with all real-world implementations of composable
+constructs, there are limitations.
+
+<p>
+Implementations of RCU for which <tt>rcu_read_lock()</tt>
+and <tt>rcu_read_unlock()</tt> generate no code, such as
+Linux-kernel RCU when <tt>CONFIG_PREEMPT=n</tt>, can be
+nested arbitrarily deeply.
+After all, there is no overhead.
+Except that if all these instances of <tt>rcu_read_lock()</tt>
+and <tt>rcu_read_unlock()</tt> are visible to the compiler,
+compilation will eventually fail due to exhausting memory,
+mass storage, or user patience, whichever comes first.
+If the nesting is not visible to the compiler, as is the case with
+mutually recursive functions each in its own translation unit,
+stack overflow will result.
+If the nesting takes the form of loops, either the control variable
+will overflow or (in the Linux kernel) you will get an RCU CPU stall warning.
+Nevertheless, this class of RCU implementations is one
+of the most composable constructs in existence.
+
+<p>
+RCU implementations that explicitly track nesting depth
+are limited by the nesting-depth counter.
+For example, the Linux kernel's preemptible RCU limits nesting to
+<tt>INT_MAX</tt>.
+This should suffice for almost all practical purposes.
+That said, a consecutive pair of RCU read-side critical sections
+between which there is an operation that waits for a grace period
+cannot be enclosed in another RCU read-side critical section.
+This is because it is not legal to wait for a grace period within
+an RCU read-side critical section: To do so would result either
+in deadlock or
+in RCU implicitly splitting the enclosing RCU read-side critical
+section, neither of which is conducive to a long-lived and prosperous
+kernel.
+
+<p>
+It is worth noting that RCU is not alone in limiting composability.
+For example, many transactional-memory implementations prohibit
+composing a pair of transactions separated by an irrevocable
+operation (for example, a network receive operation).
+For another example, lock-based critical sections can be composed
+surprisingly freely, but only if deadlock is avoided.
+
+<p>
+In short, although RCU read-side critical sections are highly composable,
+care is required in some situations, just as is the case for any other
+composable synchronization mechanism.
+
+<h3><a name="Corner Cases">Corner Cases</a></h3>
+
+<p>
+A given RCU workload might have an endless and intense stream of
+RCU read-side critical sections, perhaps even so intense that there
+was never a point in time during which there was not at least one
+RCU read-side critical section in flight.
+RCU cannot allow this situation to block grace periods: As long as
+all the RCU read-side critical sections are finite, grace periods
+must also be finite.
+
+<p>
+That said, preemptible RCU implementations could potentially result
+in RCU read-side critical sections being preempted for long durations,
+which has the effect of creating a long-duration RCU read-side
+critical section.
+This situation can arise only in heavily loaded systems, but systems using
+real-time priorities are of course more vulnerable.
+Therefore, RCU priority boosting is provided to help deal with this
+case.
+That said, the exact requirements on RCU priority boosting will likely
+evolve as more experience accumulates.
+
+<p>
+Other workloads might have very high update rates.
+Although one can argue that such workloads should instead use
+something other than RCU, the fact remains that RCU must
+handle such workloads gracefully.
+This requirement is another factor driving batching of grace periods,
+but it is also the driving force behind the checks for large numbers
+of queued RCU callbacks in the <tt>call_rcu()</tt> code path.
+Finally, high update rates should not delay RCU read-side critical
+sections, although some read-side delays can occur when using
+<tt>synchronize_rcu_expedited()</tt>, courtesy of this function's use
+of <tt>try_stop_cpus()</tt>.
+(In the future, <tt>synchronize_rcu_expedited()</tt> will be
+converted to use lighter-weight inter-processor interrupts (IPIs),
+but this will still disturb readers, though to a much smaller degree.)
+
+<p>
+Although all three of these corner cases were understood in the early
+1990s, a simple user-level test consisting of <tt>close(open(path))</tt>
+in a tight loop
+in the early 2000s suddenly provided a much deeper appreciation of the
+high-update-rate corner case.
+This test also motivated addition of some RCU code to react to high update
+rates, for example, if a given CPU finds itself with more than 10,000
+RCU callbacks queued, it will cause RCU to take evasive action by
+more aggressively starting grace periods and more aggressively forcing
+completion of grace-period processing.
+This evasive action causes the grace period to complete more quickly,
+but at the cost of restricting RCU's batching optimizations, thus
+increasing the CPU overhead incurred by that grace period.
+
+<h2><a name="Software-Engineering Requirements">
+Software-Engineering Requirements</a></h2>
+
+<p>
+Between Murphy's Law and &ldquo;To err is human&rdquo;, it is necessary to
+guard against mishaps and misuse:
+
+<ol>
+<li> It is all too easy to forget to use <tt>rcu_read_lock()</tt>
+ everywhere that it is needed, so kernels built with
+ <tt>CONFIG_PROVE_RCU=y</tt> will spat if
+ <tt>rcu_dereference()</tt> is used outside of an
+ RCU read-side critical section.
+ Update-side code can use <tt>rcu_dereference_protected()</tt>,
+ which takes a
+ <a href="https://lwn.net/Articles/371986/">lockdep expression</a>
+ to indicate what is providing the protection.
+ If the indicated protection is not provided, a lockdep splat
+ is emitted.
+
+ <p>
+ Code shared between readers and updaters can use
+ <tt>rcu_dereference_check()</tt>, which also takes a
+ lockdep expression, and emits a lockdep splat if neither
+ <tt>rcu_read_lock()</tt> nor the indicated protection
+ is in place.
+ In addition, <tt>rcu_dereference_raw()</tt> is used in those
+ (hopefully rare) cases where the required protection cannot
+ be easily described.
+ Finally, <tt>rcu_read_lock_held()</tt> is provided to
+ allow a function to verify that it has been invoked within
+ an RCU read-side critical section.
+ I was made aware of this set of requirements shortly after Thomas
+ Gleixner audited a number of RCU uses.
+<li> A given function might wish to check for RCU-related preconditions
+ upon entry, before using any other RCU API.
+ The <tt>rcu_lockdep_assert()</tt> does this job,
+ asserting the expression in kernels having lockdep enabled
+ and doing nothing otherwise.
+<li> It is also easy to forget to use <tt>rcu_assign_pointer()</tt>
+ and <tt>rcu_dereference()</tt>, perhaps (incorrectly)
+ substituting a simple assignment.
+ To catch this sort of error, a given RCU-protected pointer may be
+ tagged with <tt>__rcu</tt>, after which running sparse
+ with <tt>CONFIG_SPARSE_RCU_POINTER=y</tt> will complain
+ about simple-assignment accesses to that pointer.
+ Arnd Bergmann made me aware of this requirement, and also
+ supplied the needed
+ <a href="https://lwn.net/Articles/376011/">patch series</a>.
+<li> Kernels built with <tt>CONFIG_DEBUG_OBJECTS_RCU_HEAD=y</tt>
+ will splat if a data element is passed to <tt>call_rcu()</tt>
+ twice in a row, without a grace period in between.
+ (This error is similar to a double free.)
+ The corresponding <tt>rcu_head</tt> structures that are
+ dynamically allocated are automatically tracked, but
+ <tt>rcu_head</tt> structures allocated on the stack
+ must be initialized with <tt>init_rcu_head_on_stack()</tt>
+ and cleaned up with <tt>destroy_rcu_head_on_stack()</tt>.
+ Similarly, statically allocated non-stack <tt>rcu_head</tt>
+ structures must be initialized with <tt>init_rcu_head()</tt>
+ and cleaned up with <tt>destroy_rcu_head()</tt>.
+ Mathieu Desnoyers made me aware of this requirement, and also
+ supplied the needed
+ <a href="https://lkml.kernel.org/g/20100319013024.GA28456@Krystal">patch</a>.
+<li> An infinite loop in an RCU read-side critical section will
+ eventually trigger an RCU CPU stall warning splat, with
+ the duration of &ldquo;eventually&rdquo; being controlled by the
+ <tt>RCU_CPU_STALL_TIMEOUT</tt> <tt>Kconfig</tt> option, or,
+ alternatively, by the
+ <tt>rcupdate.rcu_cpu_stall_timeout</tt> boot/sysfs
+ parameter.
+ However, RCU is not obligated to produce this splat
+ unless there is a grace period waiting on that particular
+ RCU read-side critical section.
+ <p>
+ Some extreme workloads might intentionally delay
+ RCU grace periods, and systems running those workloads can
+ be booted with <tt>rcupdate.rcu_cpu_stall_suppress</tt>
+ to suppress the splats.
+ This kernel parameter may also be set via <tt>sysfs</tt>.
+ Furthermore, RCU CPU stall warnings are counter-productive
+ during sysrq dumps and during panics.
+ RCU therefore supplies the <tt>rcu_sysrq_start()</tt> and
+ <tt>rcu_sysrq_end()</tt> API members to be called before
+ and after long sysrq dumps.
+ RCU also supplies the <tt>rcu_panic()</tt> notifier that is
+ automatically invoked at the beginning of a panic to suppress
+ further RCU CPU stall warnings.
+
+ <p>
+ This requirement made itself known in the early 1990s, pretty
+ much the first time that it was necessary to debug a CPU stall.
+ That said, the initial implementation in DYNIX/ptx was quite
+ generic in comparison with that of Linux.
+<li> Although it would be very good to detect pointers leaking out
+ of RCU read-side critical sections, there is currently no
+ good way of doing this.
+ One complication is the need to distinguish between pointers
+ leaking and pointers that have been handed off from RCU to
+ some other synchronization mechanism, for example, reference
+ counting.
+<li> In kernels built with <tt>CONFIG_RCU_TRACE=y</tt>, RCU-related
+ information is provided via both debugfs and event tracing.
+<li> Open-coded use of <tt>rcu_assign_pointer()</tt> and
+ <tt>rcu_dereference()</tt> to create typical linked
+ data structures can be surprisingly error-prone.
+ Therefore, RCU-protected
+ <a href="https://lwn.net/Articles/609973/#RCU List APIs">linked lists</a>
+ and, more recently, RCU-protected
+ <a href="https://lwn.net/Articles/612100/">hash tables</a>
+ are available.
+ Many other special-purpose RCU-protected data structures are
+ available in the Linux kernel and the userspace RCU library.
+<li> Some linked structures are created at compile time, but still
+ require <tt>__rcu</tt> checking.
+ The <tt>RCU_POINTER_INITIALIZER()</tt> macro serves this
+ purpose.
+<li> It is not necessary to use <tt>rcu_assign_pointer()</tt>
+ when creating linked structures that are to be published via
+ a single external pointer.
+ The <tt>RCU_INIT_POINTER()</tt> macro is provided for
+ this task and also for assigning <tt>NULL</tt> pointers
+ at runtime.
+</ol>
+
+<p>
+This not a hard-and-fast list: RCU's diagnostic capabilities will
+continue to be guided by the number and type of usage bugs found
+in real-world RCU usage.
+
+<h2><a name="Linux Kernel Complications">Linux Kernel Complications</a></h2>
+
+<p>
+The Linux kernel provides an interesting environment for all kinds of
+software, including RCU.
+Some of the relevant points of interest are as follows:
+
+<ol>
+<li> <a href="#Configuration">Configuration</a>.
+<li> <a href="#Firmware Interface">Firmware Interface</a>.
+<li> <a href="#Early Boot">Early Boot</a>.
+<li> <a href="#Interrupts and NMIs">
+ Interrupts and non-maskable interrupts (NMIs)</a>.
+<li> <a href="#Loadable Modules">Loadable Modules</a>.
+<li> <a href="#Hotplug CPU">Hotplug CPU</a>.
+<li> <a href="#Scheduler and RCU">Scheduler and RCU</a>.
+<li> <a href="#Tracing and RCU">Tracing and RCU</a>.
+<li> <a href="#Energy Efficiency">Energy Efficiency</a>.
+<li> <a href="#Memory Efficiency">Memory Efficiency</a>.
+<li> <a href="#Performance, Scalability, Response Time, and Reliability">
+ Performance, Scalability, Response Time, and Reliability</a>.
+</ol>
+
+<p>
+This list is probably incomplete, but it does give a feel for the
+most notable Linux-kernel complications.
+Each of the following sections covers one of the above topics.
+
+<h3><a name="Configuration">Configuration</a></h3>
+
+<p>
+RCU's goal is automatic configuration, so that almost nobody
+needs to worry about RCU's <tt>Kconfig</tt> options.
+And for almost all users, RCU does in fact work well
+&ldquo;out of the box.&rdquo;
+
+<p>
+However, there are specialized use cases that are handled by
+kernel boot parameters and <tt>Kconfig</tt> options.
+Unfortunately, the <tt>Kconfig</tt> system will explicitly ask users
+about new <tt>Kconfig</tt> options, which requires almost all of them
+be hidden behind a <tt>CONFIG_RCU_EXPERT</tt> <tt>Kconfig</tt> option.
+
+<p>
+This all should be quite obvious, but the fact remains that
+Linus Torvalds recently had to
+<a href="https://lkml.kernel.org/g/CA+55aFy4wcCwaL4okTs8wXhGZ5h-ibecy_Meg9C4MNQrUnwMcg@mail.gmail.com">remind</a>
+me of this requirement.
+
+<h3><a name="Firmware Interface">Firmware Interface</a></h3>
+
+<p>
+In many cases, kernel obtains information about the system from the
+firmware, and sometimes things are lost in translation.
+Or the translation is accurate, but the original message is bogus.
+
+<p>
+For example, some systems' firmware overreports the number of CPUs,
+sometimes by a large factor.
+If RCU naively believed the firmware, as it used to do,
+it would create too many per-CPU kthreads.
+Although the resulting system will still run correctly, the extra
+kthreads needlessly consume memory and can cause confusion
+when they show up in <tt>ps</tt> listings.
+
+<p>
+RCU must therefore wait for a given CPU to actually come online before
+it can allow itself to believe that the CPU actually exists.
+The resulting &ldquo;ghost CPUs&rdquo; (which are never going to
+come online) cause a number of
+<a href="https://paulmck.livejournal.com/37494.html">interesting complications</a>.
+
+<h3><a name="Early Boot">Early Boot</a></h3>
+
+<p>
+The Linux kernel's boot sequence is an interesting process,
+and RCU is used early, even before <tt>rcu_init()</tt>
+is invoked.
+In fact, a number of RCU's primitives can be used as soon as the
+initial task's <tt>task_struct</tt> is available and the
+boot CPU's per-CPU variables are set up.
+The read-side primitives (<tt>rcu_read_lock()</tt>,
+<tt>rcu_read_unlock()</tt>, <tt>rcu_dereference()</tt>,
+and <tt>rcu_access_pointer()</tt>) will operate normally very early on,
+as will <tt>rcu_assign_pointer()</tt>.
+
+<p>
+Although <tt>call_rcu()</tt> may be invoked at any
+time during boot, callbacks are not guaranteed to be invoked until after
+the scheduler is fully up and running.
+This delay in callback invocation is due to the fact that RCU does not
+invoke callbacks until it is fully initialized, and this full initialization
+cannot occur until after the scheduler has initialized itself to the
+point where RCU can spawn and run its kthreads.
+In theory, it would be possible to invoke callbacks earlier,
+however, this is not a panacea because there would be severe restrictions
+on what operations those callbacks could invoke.
+
+<p>
+Perhaps surprisingly, <tt>synchronize_rcu()</tt>,
+<a href="#Bottom-Half Flavor"><tt>synchronize_rcu_bh()</tt></a>
+(<a href="#Bottom-Half Flavor">discussed below</a>),
+and
+<a href="#Sched Flavor"><tt>synchronize_sched()</tt></a>
+will all operate normally
+during very early boot, the reason being that there is only one CPU
+and preemption is disabled.
+This means that the call <tt>synchronize_rcu()</tt> (or friends)
+itself is a quiescent
+state and thus a grace period, so the early-boot implementation can
+be a no-op.
+
+<p>
+Both <tt>synchronize_rcu_bh()</tt> and <tt>synchronize_sched()</tt>
+continue to operate normally through the remainder of boot, courtesy
+of the fact that preemption is disabled across their RCU read-side
+critical sections and also courtesy of the fact that there is still
+only one CPU.
+However, once the scheduler starts initializing, preemption is enabled.
+There is still only a single CPU, but the fact that preemption is enabled
+means that the no-op implementation of <tt>synchronize_rcu()</tt> no
+longer works in <tt>CONFIG_PREEMPT=y</tt> kernels.
+Therefore, as soon as the scheduler starts initializing, the early-boot
+fastpath is disabled.
+This means that <tt>synchronize_rcu()</tt> switches to its runtime
+mode of operation where it posts callbacks, which in turn means that
+any call to <tt>synchronize_rcu()</tt> will block until the corresponding
+callback is invoked.
+Unfortunately, the callback cannot be invoked until RCU's runtime
+grace-period machinery is up and running, which cannot happen until
+the scheduler has initialized itself sufficiently to allow RCU's
+kthreads to be spawned.
+Therefore, invoking <tt>synchronize_rcu()</tt> during scheduler
+initialization can result in deadlock.
+
+<p><a name="Quick Quiz 14"><b>Quick Quiz 14</b>:</a>
+So what happens with <tt>synchronize_rcu()</tt> during
+scheduler initialization for <tt>CONFIG_PREEMPT=n</tt>
+kernels?
+<br><a href="#qq14answer">Answer</a>
+
+<p>
+I learned of these boot-time requirements as a result of a series of
+system hangs.
+
+<h3><a name="Interrupts and NMIs">Interrupts and NMIs</a></h3>
+
+<p>
+The Linux kernel has interrupts, and RCU read-side critical sections are
+legal within interrupt handlers and within interrupt-disabled regions
+of code, as are invocations of <tt>call_rcu()</tt>.
+
+<p>
+Some Linux-kernel architectures can enter an interrupt handler from
+non-idle process context, and then just never leave it, instead stealthily
+transitioning back to process context.
+This trick is sometimes used to invoke system calls from inside the kernel.
+These &ldquo;half-interrupts&rdquo; mean that RCU has to be very careful
+about how it counts interrupt nesting levels.
+I learned of this requirement the hard way during a rewrite
+of RCU's dyntick-idle code.
+
+<p>
+The Linux kernel has non-maskable interrupts (NMIs), and
+RCU read-side critical sections are legal within NMI handlers.
+Thankfully, RCU update-side primitives, including
+<tt>call_rcu()</tt>, are prohibited within NMI handlers.
+
+<p>
+The name notwithstanding, some Linux-kernel architectures
+can have nested NMIs, which RCU must handle correctly.
+Andy Lutomirski
+<a href="https://lkml.kernel.org/g/CALCETrXLq1y7e_dKFPgou-FKHB6Pu-r8+t-6Ds+8=va7anBWDA@mail.gmail.com">surprised me</a>
+with this requirement;
+he also kindly surprised me with
+<a href="https://lkml.kernel.org/g/CALCETrXSY9JpW3uE6H8WYk81sg56qasA2aqmjMPsq5dOtzso=g@mail.gmail.com">an algorithm</a>
+that meets this requirement.
+
+<h3><a name="Loadable Modules">Loadable Modules</a></h3>
+
+<p>
+The Linux kernel has loadable modules, and these modules can
+also be unloaded.
+After a given module has been unloaded, any attempt to call
+one of its functions results in a segmentation fault.
+The module-unload functions must therefore cancel any
+delayed calls to loadable-module functions, for example,
+any outstanding <tt>mod_timer()</tt> must be dealt with
+via <tt>del_timer_sync()</tt> or similar.
+
+<p>
+Unfortunately, there is no way to cancel an RCU callback;
+once you invoke <tt>call_rcu()</tt>, the callback function is
+going to eventually be invoked, unless the system goes down first.
+Because it is normally considered socially irresponsible to crash the system
+in response to a module unload request, we need some other way
+to deal with in-flight RCU callbacks.
+
+<p>
+RCU therefore provides
+<tt><a href="https://lwn.net/Articles/217484/">rcu_barrier()</a></tt>,
+which waits until all in-flight RCU callbacks have been invoked.
+If a module uses <tt>call_rcu()</tt>, its exit function should therefore
+prevent any future invocation of <tt>call_rcu()</tt>, then invoke
+<tt>rcu_barrier()</tt>.
+In theory, the underlying module-unload code could invoke
+<tt>rcu_barrier()</tt> unconditionally, but in practice this would
+incur unacceptable latencies.
+
+<p>
+Nikita Danilov noted this requirement for an analogous filesystem-unmount
+situation, and Dipankar Sarma incorporated <tt>rcu_barrier()</tt> into RCU.
+The need for <tt>rcu_barrier()</tt> for module unloading became
+apparent later.
+
+<h3><a name="Hotplug CPU">Hotplug CPU</a></h3>
+
+<p>
+The Linux kernel supports CPU hotplug, which means that CPUs
+can come and go.
+It is of course illegal to use any RCU API member from an offline CPU.
+This requirement was present from day one in DYNIX/ptx, but
+on the other hand, the Linux kernel's CPU-hotplug implementation
+is &ldquo;interesting.&rdquo;
+
+<p>
+The Linux-kernel CPU-hotplug implementation has notifiers that
+are used to allow the various kernel subsystems (including RCU)
+to respond appropriately to a given CPU-hotplug operation.
+Most RCU operations may be invoked from CPU-hotplug notifiers,
+including even normal synchronous grace-period operations
+such as <tt>synchronize_rcu()</tt>.
+However, expedited grace-period operations such as
+<tt>synchronize_rcu_expedited()</tt> are not supported,
+due to the fact that current implementations block CPU-hotplug
+operations, which could result in deadlock.
+
+<p>
+In addition, all-callback-wait operations such as
+<tt>rcu_barrier()</tt> are also not supported, due to the
+fact that there are phases of CPU-hotplug operations where
+the outgoing CPU's callbacks will not be invoked until after
+the CPU-hotplug operation ends, which could also result in deadlock.
+
+<h3><a name="Scheduler and RCU">Scheduler and RCU</a></h3>
+
+<p>
+RCU depends on the scheduler, and the scheduler uses RCU to
+protect some of its data structures.
+This means the scheduler is forbidden from acquiring
+the runqueue locks and the priority-inheritance locks
+in the middle of an outermost RCU read-side critical section unless either
+(1)&nbsp;it releases them before exiting that same
+RCU read-side critical section, or
+(2)&nbsp;interrupts are disabled across
+that entire RCU read-side critical section.
+This same prohibition also applies (recursively!) to any lock that is acquired
+while holding any lock to which this prohibition applies.
+Adhering to this rule prevents preemptible RCU from invoking
+<tt>rcu_read_unlock_special()</tt> while either runqueue or
+priority-inheritance locks are held, thus avoiding deadlock.
+
+<p>
+Prior to v4.4, it was only necessary to disable preemption across
+RCU read-side critical sections that acquired scheduler locks.
+In v4.4, expedited grace periods started using IPIs, and these
+IPIs could force a <tt>rcu_read_unlock()</tt> to take the slowpath.
+Therefore, this expedited-grace-period change required disabling of
+interrupts, not just preemption.
+
+<p>
+For RCU's part, the preemptible-RCU <tt>rcu_read_unlock()</tt>
+implementation must be written carefully to avoid similar deadlocks.
+In particular, <tt>rcu_read_unlock()</tt> must tolerate an
+interrupt where the interrupt handler invokes both
+<tt>rcu_read_lock()</tt> and <tt>rcu_read_unlock()</tt>.
+This possibility requires <tt>rcu_read_unlock()</tt> to use
+negative nesting levels to avoid destructive recursion via
+interrupt handler's use of RCU.
+
+<p>
+This pair of mutual scheduler-RCU requirements came as a
+<a href="https://lwn.net/Articles/453002/">complete surprise</a>.
+
+<p>
+As noted above, RCU makes use of kthreads, and it is necessary to
+avoid excessive CPU-time accumulation by these kthreads.
+This requirement was no surprise, but RCU's violation of it
+when running context-switch-heavy workloads when built with
+<tt>CONFIG_NO_HZ_FULL=y</tt>
+<a href="http://www.rdrop.com/users/paulmck/scalability/paper/BareMetal.2015.01.15b.pdf">did come as a surprise [PDF]</a>.
+RCU has made good progress towards meeting this requirement, even
+for context-switch-have <tt>CONFIG_NO_HZ_FULL=y</tt> workloads,
+but there is room for further improvement.
+
+<h3><a name="Tracing and RCU">Tracing and RCU</a></h3>
+
+<p>
+It is possible to use tracing on RCU code, but tracing itself
+uses RCU.
+For this reason, <tt>rcu_dereference_raw_notrace()</tt>
+is provided for use by tracing, which avoids the destructive
+recursion that could otherwise ensue.
+This API is also used by virtualization in some architectures,
+where RCU readers execute in environments in which tracing
+cannot be used.
+The tracing folks both located the requirement and provided the
+needed fix, so this surprise requirement was relatively painless.
+
+<h3><a name="Energy Efficiency">Energy Efficiency</a></h3>
+
+<p>
+Interrupting idle CPUs is considered socially unacceptable,
+especially by people with battery-powered embedded systems.
+RCU therefore conserves energy by detecting which CPUs are
+idle, including tracking CPUs that have been interrupted from idle.
+This is a large part of the energy-efficiency requirement,
+so I learned of this via an irate phone call.
+
+<p>
+Because RCU avoids interrupting idle CPUs, it is illegal to
+execute an RCU read-side critical section on an idle CPU.
+(Kernels built with <tt>CONFIG_PROVE_RCU=y</tt> will splat
+if you try it.)
+The <tt>RCU_NONIDLE()</tt> macro and <tt>_rcuidle</tt>
+event tracing is provided to work around this restriction.
+In addition, <tt>rcu_is_watching()</tt> may be used to
+test whether or not it is currently legal to run RCU read-side
+critical sections on this CPU.
+I learned of the need for diagnostics on the one hand
+and <tt>RCU_NONIDLE()</tt> on the other while inspecting
+idle-loop code.
+Steven Rostedt supplied <tt>_rcuidle</tt> event tracing,
+which is used quite heavily in the idle loop.
+
+<p>
+It is similarly socially unacceptable to interrupt an
+<tt>nohz_full</tt> CPU running in userspace.
+RCU must therefore track <tt>nohz_full</tt> userspace
+execution.
+And in
+<a href="https://lwn.net/Articles/558284/"><tt>CONFIG_NO_HZ_FULL_SYSIDLE=y</tt></a>
+kernels, RCU must separately track idle CPUs on the one hand and
+CPUs that are either idle or executing in userspace on the other.
+In both cases, RCU must be able to sample state at two points in
+time, and be able to determine whether or not some other CPU spent
+any time idle and/or executing in userspace.
+
+<p>
+These energy-efficiency requirements have proven quite difficult to
+understand and to meet, for example, there have been more than five
+clean-sheet rewrites of RCU's energy-efficiency code, the last of
+which was finally able to demonstrate
+<a href="http://www.rdrop.com/users/paulmck/realtime/paper/AMPenergy.2013.04.19a.pdf">real energy savings running on real hardware [PDF]</a>.
+As noted earlier,
+I learned of many of these requirements via angry phone calls:
+Flaming me on the Linux-kernel mailing list was apparently not
+sufficient to fully vent their ire at RCU's energy-efficiency bugs!
+
+<h3><a name="Memory Efficiency">Memory Efficiency</a></h3>
+
+<p>
+Although small-memory non-realtime systems can simply use Tiny RCU,
+code size is only one aspect of memory efficiency.
+Another aspect is the size of the <tt>rcu_head</tt> structure
+used by <tt>call_rcu()</tt> and <tt>kfree_rcu()</tt>.
+Although this structure contains nothing more than a pair of pointers,
+it does appear in many RCU-protected data structures, including
+some that are size critical.
+The <tt>page</tt> structure is a case in point, as evidenced by
+the many occurrences of the <tt>union</tt> keyword within that structure.
+
+<p>
+This need for memory efficiency is one reason that RCU uses hand-crafted
+singly linked lists to track the <tt>rcu_head</tt> structures that
+are waiting for a grace period to elapse.
+It is also the reason why <tt>rcu_head</tt> structures do not contain
+debug information, such as fields tracking the file and line of the
+<tt>call_rcu()</tt> or <tt>kfree_rcu()</tt> that posted them.
+Although this information might appear in debug-only kernel builds at some
+point, in the meantime, the <tt>-&gt;func</tt> field will often provide
+the needed debug information.
+
+<p>
+However, in some cases, the need for memory efficiency leads to even
+more extreme measures.
+Returning to the <tt>page</tt> structure, the <tt>rcu_head</tt> field
+shares storage with a great many other structures that are used at
+various points in the corresponding page's lifetime.
+In order to correctly resolve certain
+<a href="https://lkml.kernel.org/g/1439976106-137226-1-git-send-email-kirill.shutemov@linux.intel.com">race conditions</a>,
+the Linux kernel's memory-management subsystem needs a particular bit
+to remain zero during all phases of grace-period processing,
+and that bit happens to map to the bottom bit of the
+<tt>rcu_head</tt> structure's <tt>-&gt;next</tt> field.
+RCU makes this guarantee as long as <tt>call_rcu()</tt>
+is used to post the callback, as opposed to <tt>kfree_rcu()</tt>
+or some future &ldquo;lazy&rdquo;
+variant of <tt>call_rcu()</tt> that might one day be created for
+energy-efficiency purposes.
+
+<h3><a name="Performance, Scalability, Response Time, and Reliability">
+Performance, Scalability, Response Time, and Reliability</a></h3>
+
+<p>
+Expanding on the
+<a href="#Performance and Scalability">earlier discussion</a>,
+RCU is used heavily by hot code paths in performance-critical
+portions of the Linux kernel's networking, security, virtualization,
+and scheduling code paths.
+RCU must therefore use efficient implementations, especially in its
+read-side primitives.
+To that end, it would be good if preemptible RCU's implementation
+of <tt>rcu_read_lock()</tt> could be inlined, however, doing
+this requires resolving <tt>#include</tt> issues with the
+<tt>task_struct</tt> structure.
+
+<p>
+The Linux kernel supports hardware configurations with up to
+4096 CPUs, which means that RCU must be extremely scalable.
+Algorithms that involve frequent acquisitions of global locks or
+frequent atomic operations on global variables simply cannot be
+tolerated within the RCU implementation.
+RCU therefore makes heavy use of a combining tree based on the
+<tt>rcu_node</tt> structure.
+RCU is required to tolerate all CPUs continuously invoking any
+combination of RCU's runtime primitives with minimal per-operation
+overhead.
+In fact, in many cases, increasing load must <i>decrease</i> the
+per-operation overhead, witness the batching optimizations for
+<tt>synchronize_rcu()</tt>, <tt>call_rcu()</tt>,
+<tt>synchronize_rcu_expedited()</tt>, and <tt>rcu_barrier()</tt>.
+As a general rule, RCU must cheerfully accept whatever the
+rest of the Linux kernel decides to throw at it.
+
+<p>
+The Linux kernel is used for real-time workloads, especially
+in conjunction with the
+<a href="https://rt.wiki.kernel.org/index.php/Main_Page">-rt patchset</a>.
+The real-time-latency response requirements are such that the
+traditional approach of disabling preemption across RCU
+read-side critical sections is inappropriate.
+Kernels built with <tt>CONFIG_PREEMPT=y</tt> therefore
+use an RCU implementation that allows RCU read-side critical
+sections to be preempted.
+This requirement made its presence known after users made it
+clear that an earlier
+<a href="https://lwn.net/Articles/107930/">real-time patch</a>
+did not meet their needs, in conjunction with some
+<a href="https://lkml.kernel.org/g/20050318002026.GA2693@us.ibm.com">RCU issues</a>
+encountered by a very early version of the -rt patchset.
+
+<p>
+In addition, RCU must make do with a sub-100-microsecond real-time latency
+budget.
+In fact, on smaller systems with the -rt patchset, the Linux kernel
+provides sub-20-microsecond real-time latencies for the whole kernel,
+including RCU.
+RCU's scalability and latency must therefore be sufficient for
+these sorts of configurations.
+To my surprise, the sub-100-microsecond real-time latency budget
+<a href="http://www.rdrop.com/users/paulmck/realtime/paper/bigrt.2013.01.31a.LCA.pdf">
+applies to even the largest systems [PDF]</a>,
+up to and including systems with 4096 CPUs.
+This real-time requirement motivated the grace-period kthread, which
+also simplified handling of a number of race conditions.
+
+<p>
+Finally, RCU's status as a synchronization primitive means that
+any RCU failure can result in arbitrary memory corruption that can be
+extremely difficult to debug.
+This means that RCU must be extremely reliable, which in
+practice also means that RCU must have an aggressive stress-test
+suite.
+This stress-test suite is called <tt>rcutorture</tt>.
+
+<p>
+Although the need for <tt>rcutorture</tt> was no surprise,
+the current immense popularity of the Linux kernel is posing
+interesting&mdash;and perhaps unprecedented&mdash;validation
+challenges.
+To see this, keep in mind that there are well over one billion
+instances of the Linux kernel running today, given Android
+smartphones, Linux-powered televisions, and servers.
+This number can be expected to increase sharply with the advent of
+the celebrated Internet of Things.
+
+<p>
+Suppose that RCU contains a race condition that manifests on average
+once per million years of runtime.
+This bug will be occurring about three times per <i>day</i> across
+the installed base.
+RCU could simply hide behind hardware error rates, given that no one
+should really expect their smartphone to last for a million years.
+However, anyone taking too much comfort from this thought should
+consider the fact that in most jurisdictions, a successful multi-year
+test of a given mechanism, which might include a Linux kernel,
+suffices for a number of types of safety-critical certifications.
+In fact, rumor has it that the Linux kernel is already being used
+in production for safety-critical applications.
+I don't know about you, but I would feel quite bad if a bug in RCU
+killed someone.
+Which might explain my recent focus on validation and verification.
+
+<h2><a name="Other RCU Flavors">Other RCU Flavors</a></h2>
+
+<p>
+One of the more surprising things about RCU is that there are now
+no fewer than five <i>flavors</i>, or API families.
+In addition, the primary flavor that has been the sole focus up to
+this point has two different implementations, non-preemptible and
+preemptible.
+The other four flavors are listed below, with requirements for each
+described in a separate section.
+
+<ol>
+<li> <a href="#Bottom-Half Flavor">Bottom-Half Flavor</a>
+<li> <a href="#Sched Flavor">Sched Flavor</a>
+<li> <a href="#Sleepable RCU">Sleepable RCU</a>
+<li> <a href="#Tasks RCU">Tasks RCU</a>
+</ol>
+
+<h3><a name="Bottom-Half Flavor">Bottom-Half Flavor</a></h3>
+
+<p>
+The softirq-disable (AKA &ldquo;bottom-half&rdquo;,
+hence the &ldquo;_bh&rdquo; abbreviations)
+flavor of RCU, or <i>RCU-bh</i>, was developed by
+Dipankar Sarma to provide a flavor of RCU that could withstand the
+network-based denial-of-service attacks researched by Robert
+Olsson.
+These attacks placed so much networking load on the system
+that some of the CPUs never exited softirq execution,
+which in turn prevented those CPUs from ever executing a context switch,
+which, in the RCU implementation of that time, prevented grace periods
+from ever ending.
+The result was an out-of-memory condition and a system hang.
+
+<p>
+The solution was the creation of RCU-bh, which does
+<tt>local_bh_disable()</tt>
+across its read-side critical sections, and which uses the transition
+from one type of softirq processing to another as a quiescent state
+in addition to context switch, idle, user mode, and offline.
+This means that RCU-bh grace periods can complete even when some of
+the CPUs execute in softirq indefinitely, thus allowing algorithms
+based on RCU-bh to withstand network-based denial-of-service attacks.
+
+<p>
+Because
+<tt>rcu_read_lock_bh()</tt> and <tt>rcu_read_unlock_bh()</tt>
+disable and re-enable softirq handlers, any attempt to start a softirq
+handlers during the
+RCU-bh read-side critical section will be deferred.
+In this case, <tt>rcu_read_unlock_bh()</tt>
+will invoke softirq processing, which can take considerable time.
+One can of course argue that this softirq overhead should be associated
+with the code following the RCU-bh read-side critical section rather
+than <tt>rcu_read_unlock_bh()</tt>, but the fact
+is that most profiling tools cannot be expected to make this sort
+of fine distinction.
+For example, suppose that a three-millisecond-long RCU-bh read-side
+critical section executes during a time of heavy networking load.
+There will very likely be an attempt to invoke at least one softirq
+handler during that three milliseconds, but any such invocation will
+be delayed until the time of the <tt>rcu_read_unlock_bh()</tt>.
+This can of course make it appear at first glance as if
+<tt>rcu_read_unlock_bh()</tt> was executing very slowly.
+
+<p>
+The
+<a href="https://lwn.net/Articles/609973/#RCU Per-Flavor API Table">RCU-bh API</a>
+includes
+<tt>rcu_read_lock_bh()</tt>,
+<tt>rcu_read_unlock_bh()</tt>,
+<tt>rcu_dereference_bh()</tt>,
+<tt>rcu_dereference_bh_check()</tt>,
+<tt>synchronize_rcu_bh()</tt>,
+<tt>synchronize_rcu_bh_expedited()</tt>,
+<tt>call_rcu_bh()</tt>,
+<tt>rcu_barrier_bh()</tt>, and
+<tt>rcu_read_lock_bh_held()</tt>.
+
+<h3><a name="Sched Flavor">Sched Flavor</a></h3>
+
+<p>
+Before preemptible RCU, waiting for an RCU grace period had the
+side effect of also waiting for all pre-existing interrupt
+and NMI handlers.
+However, there are legitimate preemptible-RCU implementations that
+do not have this property, given that any point in the code outside
+of an RCU read-side critical section can be a quiescent state.
+Therefore, <i>RCU-sched</i> was created, which follows &ldquo;classic&rdquo;
+RCU in that an RCU-sched grace period waits for for pre-existing
+interrupt and NMI handlers.
+In kernels built with <tt>CONFIG_PREEMPT=n</tt>, the RCU and RCU-sched
+APIs have identical implementations, while kernels built with
+<tt>CONFIG_PREEMPT=y</tt> provide a separate implementation for each.
+
+<p>
+Note well that in <tt>CONFIG_PREEMPT=y</tt> kernels,
+<tt>rcu_read_lock_sched()</tt> and <tt>rcu_read_unlock_sched()</tt>
+disable and re-enable preemption, respectively.
+This means that if there was a preemption attempt during the
+RCU-sched read-side critical section, <tt>rcu_read_unlock_sched()</tt>
+will enter the scheduler, with all the latency and overhead entailed.
+Just as with <tt>rcu_read_unlock_bh()</tt>, this can make it look
+as if <tt>rcu_read_unlock_sched()</tt> was executing very slowly.
+However, the highest-priority task won't be preempted, so that task
+will enjoy low-overhead <tt>rcu_read_unlock_sched()</tt> invocations.
+
+<p>
+The
+<a href="https://lwn.net/Articles/609973/#RCU Per-Flavor API Table">RCU-sched API</a>
+includes
+<tt>rcu_read_lock_sched()</tt>,
+<tt>rcu_read_unlock_sched()</tt>,
+<tt>rcu_read_lock_sched_notrace()</tt>,
+<tt>rcu_read_unlock_sched_notrace()</tt>,
+<tt>rcu_dereference_sched()</tt>,
+<tt>rcu_dereference_sched_check()</tt>,
+<tt>synchronize_sched()</tt>,
+<tt>synchronize_rcu_sched_expedited()</tt>,
+<tt>call_rcu_sched()</tt>,
+<tt>rcu_barrier_sched()</tt>, and
+<tt>rcu_read_lock_sched_held()</tt>.
+However, anything that disables preemption also marks an RCU-sched
+read-side critical section, including
+<tt>preempt_disable()</tt> and <tt>preempt_enable()</tt>,
+<tt>local_irq_save()</tt> and <tt>local_irq_restore()</tt>,
+and so on.
+
+<h3><a name="Sleepable RCU">Sleepable RCU</a></h3>
+
+<p>
+For well over a decade, someone saying &ldquo;I need to block within
+an RCU read-side critical section&rdquo; was a reliable indication
+that this someone did not understand RCU.
+After all, if you are always blocking in an RCU read-side critical
+section, you can probably afford to use a higher-overhead synchronization
+mechanism.
+However, that changed with the advent of the Linux kernel's notifiers,
+whose RCU read-side critical
+sections almost never sleep, but sometimes need to.
+This resulted in the introduction of
+<a href="https://lwn.net/Articles/202847/">sleepable RCU</a>,
+or <i>SRCU</i>.
+
+<p>
+SRCU allows different domains to be defined, with each such domain
+defined by an instance of an <tt>srcu_struct</tt> structure.
+A pointer to this structure must be passed in to each SRCU function,
+for example, <tt>synchronize_srcu(&amp;ss)</tt>, where
+<tt>ss</tt> is the <tt>srcu_struct</tt> structure.
+The key benefit of these domains is that a slow SRCU reader in one
+domain does not delay an SRCU grace period in some other domain.
+That said, one consequence of these domains is that read-side code
+must pass a &ldquo;cookie&rdquo; from <tt>srcu_read_lock()</tt>
+to <tt>srcu_read_unlock()</tt>, for example, as follows:
+
+<blockquote>
+<pre>
+ 1 int idx;
+ 2
+ 3 idx = srcu_read_lock(&amp;ss);
+ 4 do_something();
+ 5 srcu_read_unlock(&amp;ss, idx);
+</pre>
+</blockquote>
+
+<p>
+As noted above, it is legal to block within SRCU read-side critical sections,
+however, with great power comes great responsibility.
+If you block forever in one of a given domain's SRCU read-side critical
+sections, then that domain's grace periods will also be blocked forever.
+Of course, one good way to block forever is to deadlock, which can
+happen if any operation in a given domain's SRCU read-side critical
+section can block waiting, either directly or indirectly, for that domain's
+grace period to elapse.
+For example, this results in a self-deadlock:
+
+<blockquote>
+<pre>
+ 1 int idx;
+ 2
+ 3 idx = srcu_read_lock(&amp;ss);
+ 4 do_something();
+ 5 synchronize_srcu(&amp;ss);
+ 6 srcu_read_unlock(&amp;ss, idx);
+</pre>
+</blockquote>
+
+<p>
+However, if line&nbsp;5 acquired a mutex that was held across
+a <tt>synchronize_srcu()</tt> for domain <tt>ss</tt>,
+deadlock would still be possible.
+Furthermore, if line&nbsp;5 acquired a mutex that was held across
+a <tt>synchronize_srcu()</tt> for some other domain <tt>ss1</tt>,
+and if an <tt>ss1</tt>-domain SRCU read-side critical section
+acquired another mutex that was held across as <tt>ss</tt>-domain
+<tt>synchronize_srcu()</tt>,
+deadlock would again be possible.
+Such a deadlock cycle could extend across an arbitrarily large number
+of different SRCU domains.
+Again, with great power comes great responsibility.
+
+<p>
+Unlike the other RCU flavors, SRCU read-side critical sections can
+run on idle and even offline CPUs.
+This ability requires that <tt>srcu_read_lock()</tt> and
+<tt>srcu_read_unlock()</tt> contain memory barriers, which means
+that SRCU readers will run a bit slower than would RCU readers.
+It also motivates the <tt>smp_mb__after_srcu_read_unlock()</tt>
+API, which, in combination with <tt>srcu_read_unlock()</tt>,
+guarantees a full memory barrier.
+
+<p>
+The
+<a href="https://lwn.net/Articles/609973/#RCU Per-Flavor API Table">SRCU API</a>
+includes
+<tt>srcu_read_lock()</tt>,
+<tt>srcu_read_unlock()</tt>,
+<tt>srcu_dereference()</tt>,
+<tt>srcu_dereference_check()</tt>,
+<tt>synchronize_srcu()</tt>,
+<tt>synchronize_srcu_expedited()</tt>,
+<tt>call_srcu()</tt>,
+<tt>srcu_barrier()</tt>, and
+<tt>srcu_read_lock_held()</tt>.
+It also includes
+<tt>DEFINE_SRCU()</tt>,
+<tt>DEFINE_STATIC_SRCU()</tt>, and
+<tt>init_srcu_struct()</tt>
+APIs for defining and initializing <tt>srcu_struct</tt> structures.
+
+<h3><a name="Tasks RCU">Tasks RCU</a></h3>
+
+<p>
+Some forms of tracing use &ldquo;tramopolines&rdquo; to handle the
+binary rewriting required to install different types of probes.
+It would be good to be able to free old trampolines, which sounds
+like a job for some form of RCU.
+However, because it is necessary to be able to install a trace
+anywhere in the code, it is not possible to use read-side markers
+such as <tt>rcu_read_lock()</tt> and <tt>rcu_read_unlock()</tt>.
+In addition, it does not work to have these markers in the trampoline
+itself, because there would need to be instructions following
+<tt>rcu_read_unlock()</tt>.
+Although <tt>synchronize_rcu()</tt> would guarantee that execution
+reached the <tt>rcu_read_unlock()</tt>, it would not be able to
+guarantee that execution had completely left the trampoline.
+
+<p>
+The solution, in the form of
+<a href="https://lwn.net/Articles/607117/"><i>Tasks RCU</i></a>,
+is to have implicit
+read-side critical sections that are delimited by voluntary context
+switches, that is, calls to <tt>schedule()</tt>,
+<tt>cond_resched_rcu_qs()</tt>, and
+<tt>synchronize_rcu_tasks()</tt>.
+In addition, transitions to and from userspace execution also delimit
+tasks-RCU read-side critical sections.
+
+<p>
+The tasks-RCU API is quite compact, consisting only of
+<tt>call_rcu_tasks()</tt>,
+<tt>synchronize_rcu_tasks()</tt>, and
+<tt>rcu_barrier_tasks()</tt>.
+
+<h2><a name="Possible Future Changes">Possible Future Changes</a></h2>
+
+<p>
+One of the tricks that RCU uses to attain update-side scalability is
+to increase grace-period latency with increasing numbers of CPUs.
+If this becomes a serious problem, it will be necessary to rework the
+grace-period state machine so as to avoid the need for the additional
+latency.
+
+<p>
+Expedited grace periods scan the CPUs, so their latency and overhead
+increases with increasing numbers of CPUs.
+If this becomes a serious problem on large systems, it will be necessary
+to do some redesign to avoid this scalability problem.
+
+<p>
+RCU disables CPU hotplug in a few places, perhaps most notably in the
+expedited grace-period and <tt>rcu_barrier()</tt> operations.
+If there is a strong reason to use expedited grace periods in CPU-hotplug
+notifiers, it will be necessary to avoid disabling CPU hotplug.
+This would introduce some complexity, so there had better be a <i>very</i>
+good reason.
+
+<p>
+The tradeoff between grace-period latency on the one hand and interruptions
+of other CPUs on the other hand may need to be re-examined.
+The desire is of course for zero grace-period latency as well as zero
+interprocessor interrupts undertaken during an expedited grace period
+operation.
+While this ideal is unlikely to be achievable, it is quite possible that
+further improvements can be made.
+
+<p>
+The multiprocessor implementations of RCU use a combining tree that
+groups CPUs so as to reduce lock contention and increase cache locality.
+However, this combining tree does not spread its memory across NUMA
+nodes nor does it align the CPU groups with hardware features such
+as sockets or cores.
+Such spreading and alignment is currently believed to be unnecessary
+because the hotpath read-side primitives do not access the combining
+tree, nor does <tt>call_rcu()</tt> in the common case.
+If you believe that your architecture needs such spreading and alignment,
+then your architecture should also benefit from the
+<tt>rcutree.rcu_fanout_leaf</tt> boot parameter, which can be set
+to the number of CPUs in a socket, NUMA node, or whatever.
+If the number of CPUs is too large, use a fraction of the number of
+CPUs.
+If the number of CPUs is a large prime number, well, that certainly
+is an &ldquo;interesting&rdquo; architectural choice!
+More flexible arrangements might be considered, but only if
+<tt>rcutree.rcu_fanout_leaf</tt> has proven inadequate, and only
+if the inadequacy has been demonstrated by a carefully run and
+realistic system-level workload.
+
+<p>
+Please note that arrangements that require RCU to remap CPU numbers will
+require extremely good demonstration of need and full exploration of
+alternatives.
+
+<p>
+There is an embarrassingly large number of flavors of RCU, and this
+number has been increasing over time.
+Perhaps it will be possible to combine some at some future date.
+
+<p>
+RCU's various kthreads are reasonably recent additions.
+It is quite likely that adjustments will be required to more gracefully
+handle extreme loads.
+It might also be necessary to be able to relate CPU utilization by
+RCU's kthreads and softirq handlers to the code that instigated this
+CPU utilization.
+For example, RCU callback overhead might be charged back to the
+originating <tt>call_rcu()</tt> instance, though probably not
+in production kernels.
+
+<h2><a name="Summary">Summary</a></h2>
+
+<p>
+This document has presented more than two decade's worth of RCU
+requirements.
+Given that the requirements keep changing, this will not be the last
+word on this subject, but at least it serves to get an important
+subset of the requirements set forth.
+
+<h2><a name="Acknowledgments">Acknowledgments</a></h2>
+
+I am grateful to Steven Rostedt, Lai Jiangshan, Ingo Molnar,
+Oleg Nesterov, Borislav Petkov, Peter Zijlstra, Boqun Feng, and
+Andy Lutomirski for their help in rendering
+this article human readable, and to Michelle Rankin for her support
+of this effort.
+Other contributions are acknowledged in the Linux kernel's git archive.
+The cartoon is copyright (c) 2013 by Melissa Broussard,
+and is provided
+under the terms of the Creative Commons Attribution-Share Alike 3.0
+United States license.
+
+<h3><a name="Answers to Quick Quizzes">
+Answers to Quick Quizzes</a></h3>
+
+<a name="qq1answer"></a>
+<p><b>Quick Quiz 1</b>:
+Wait a minute!
+You said that updaters can make useful forward progress concurrently
+with readers, but pre-existing readers will block
+<tt>synchronize_rcu()</tt>!!!
+Just who are you trying to fool???
+
+
+</p><p><b>Answer</b>:
+First, if updaters do not wish to be blocked by readers, they can use
+<tt>call_rcu()</tt> or <tt>kfree_rcu()</tt>, which will
+be discussed later.
+Second, even when using <tt>synchronize_rcu()</tt>, the other
+update-side code does run concurrently with readers, whether pre-existing
+or not.
+
+
+</p><p><a href="#Quick%20Quiz%201"><b>Back to Quick Quiz 1</b>.</a>
+
+<a name="qq2answer"></a>
+<p><b>Quick Quiz 2</b>:
+Why is the <tt>synchronize_rcu()</tt> on line&nbsp;28 needed?
+
+
+</p><p><b>Answer</b>:
+Without that extra grace period, memory reordering could result in
+<tt>do_something_dlm()</tt> executing <tt>do_something()</tt>
+concurrently with the last bits of <tt>recovery()</tt>.
+
+
+</p><p><a href="#Quick%20Quiz%202"><b>Back to Quick Quiz 2</b>.</a>
+
+<a name="qq3answer"></a>
+<p><b>Quick Quiz 3</b>:
+But <tt>rcu_assign_pointer()</tt> does nothing to prevent the
+two assignments to <tt>p-&gt;a</tt> and <tt>p-&gt;b</tt>
+from being reordered.
+Can't that also cause problems?
+
+
+</p><p><b>Answer</b>:
+No, it cannot.
+The readers cannot see either of these two fields until
+the assignment to <tt>gp</tt>, by which time both fields are
+fully initialized.
+So reordering the assignments
+to <tt>p-&gt;a</tt> and <tt>p-&gt;b</tt> cannot possibly
+cause any problems.
+
+
+</p><p><a href="#Quick%20Quiz%203"><b>Back to Quick Quiz 3</b>.</a>
+
+<a name="qq4answer"></a>
+<p><b>Quick Quiz 4</b>:
+Without the <tt>rcu_dereference()</tt> or the
+<tt>rcu_access_pointer()</tt>, what destructive optimizations
+might the compiler make use of?
+
+
+</p><p><b>Answer</b>:
+Let's start with what happens to <tt>do_something_gp()</tt>
+if it fails to use <tt>rcu_dereference()</tt>.
+It could reuse a value formerly fetched from this same pointer.
+It could also fetch the pointer from <tt>gp</tt> in a byte-at-a-time
+manner, resulting in <i>load tearing</i>, in turn resulting a bytewise
+mash-up of two distince pointer values.
+It might even use value-speculation optimizations, where it makes a wrong
+guess, but by the time it gets around to checking the value, an update
+has changed the pointer to match the wrong guess.
+Too bad about any dereferences that returned pre-initialization garbage
+in the meantime!
+
+<p>
+For <tt>remove_gp_synchronous()</tt>, as long as all modifications
+to <tt>gp</tt> are carried out while holding <tt>gp_lock</tt>,
+the above optimizations are harmless.
+However,
+with <tt>CONFIG_SPARSE_RCU_POINTER=y</tt>,
+<tt>sparse</tt> will complain if you
+define <tt>gp</tt> with <tt>__rcu</tt> and then
+access it without using
+either <tt>rcu_access_pointer()</tt> or <tt>rcu_dereference()</tt>.
+
+
+</p><p><a href="#Quick%20Quiz%204"><b>Back to Quick Quiz 4</b>.</a>
+
+<a name="qq5answer"></a>
+<p><b>Quick Quiz 5</b>:
+Given that multiple CPUs can start RCU read-side critical sections
+at any time without any ordering whatsoever, how can RCU possibly tell whether
+or not a given RCU read-side critical section starts before a
+given instance of <tt>synchronize_rcu()</tt>?
+
+
+</p><p><b>Answer</b>:
+If RCU cannot tell whether or not a given
+RCU read-side critical section starts before a
+given instance of <tt>synchronize_rcu()</tt>,
+then it must assume that the RCU read-side critical section
+started first.
+In other words, a given instance of <tt>synchronize_rcu()</tt>
+can avoid waiting on a given RCU read-side critical section only
+if it can prove that <tt>synchronize_rcu()</tt> started first.
+
+
+</p><p><a href="#Quick%20Quiz%205"><b>Back to Quick Quiz 5</b>.</a>
+
+<a name="qq6answer"></a>
+<p><b>Quick Quiz 6</b>:
+The first and second guarantees require unbelievably strict ordering!
+Are all these memory barriers <i> really</i> required?
+
+
+</p><p><b>Answer</b>:
+Yes, they really are required.
+To see why the first guarantee is required, consider the following
+sequence of events:
+
+<ol>
+<li> CPU 1: <tt>rcu_read_lock()</tt>
+<li> CPU 1: <tt>q = rcu_dereference(gp);
+ /* Very likely to return p. */</tt>
+<li> CPU 0: <tt>list_del_rcu(p);</tt>
+<li> CPU 0: <tt>synchronize_rcu()</tt> starts.
+<li> CPU 1: <tt>do_something_with(q-&gt;a);
+ /* No smp_mb(), so might happen after kfree(). */</tt>
+<li> CPU 1: <tt>rcu_read_unlock()</tt>
+<li> CPU 0: <tt>synchronize_rcu()</tt> returns.
+<li> CPU 0: <tt>kfree(p);</tt>
+</ol>
+
+<p>
+Therefore, there absolutely must be a full memory barrier between the
+end of the RCU read-side critical section and the end of the
+grace period.
+
+<p>
+The sequence of events demonstrating the necessity of the second rule
+is roughly similar:
+
+<ol>
+<li> CPU 0: <tt>list_del_rcu(p);</tt>
+<li> CPU 0: <tt>synchronize_rcu()</tt> starts.
+<li> CPU 1: <tt>rcu_read_lock()</tt>
+<li> CPU 1: <tt>q = rcu_dereference(gp);
+ /* Might return p if no memory barrier. */</tt>
+<li> CPU 0: <tt>synchronize_rcu()</tt> returns.
+<li> CPU 0: <tt>kfree(p);</tt>
+<li> CPU 1: <tt>do_something_with(q-&gt;a); /* Boom!!! */</tt>
+<li> CPU 1: <tt>rcu_read_unlock()</tt>
+</ol>
+
+<p>
+And similarly, without a memory barrier between the beginning of the
+grace period and the beginning of the RCU read-side critical section,
+CPU&nbsp;1 might end up accessing the freelist.
+
+<p>
+The &ldquo;as if&rdquo; rule of course applies, so that any implementation
+that acts as if the appropriate memory barriers were in place is a
+correct implementation.
+That said, it is much easier to fool yourself into believing that you have
+adhered to the as-if rule than it is to actually adhere to it!
+
+
+</p><p><a href="#Quick%20Quiz%206"><b>Back to Quick Quiz 6</b>.</a>
+
+<a name="qq7answer"></a>
+<p><b>Quick Quiz 7</b>:
+But how does the upgrade-to-write operation exclude other readers?
+
+
+</p><p><b>Answer</b>:
+It doesn't, just like normal RCU updates, which also do not exclude
+RCU readers.
+
+
+</p><p><a href="#Quick%20Quiz%207"><b>Back to Quick Quiz 7</b>.</a>
+
+<a name="qq8answer"></a>
+<p><b>Quick Quiz 8</b>:
+Can't the compiler also reorder this code?
+
+
+</p><p><b>Answer</b>:
+No, the volatile casts in <tt>READ_ONCE()</tt> and
+<tt>WRITE_ONCE()</tt> prevent the compiler from reordering in
+this particular case.
+
+
+</p><p><a href="#Quick%20Quiz%208"><b>Back to Quick Quiz 8</b>.</a>
+
+<a name="qq9answer"></a>
+<p><b>Quick Quiz 9</b>:
+Suppose that synchronize_rcu() did wait until all readers had completed.
+Would the updater be able to rely on this?
+
+
+</p><p><b>Answer</b>:
+No.
+Even if <tt>synchronize_rcu()</tt> were to wait until
+all readers had completed, a new reader might start immediately after
+<tt>synchronize_rcu()</tt> completed.
+Therefore, the code following
+<tt>synchronize_rcu()</tt> cannot rely on there being no readers
+in any case.
+
+
+</p><p><a href="#Quick%20Quiz%209"><b>Back to Quick Quiz 9</b>.</a>
+
+<a name="qq10answer"></a>
+<p><b>Quick Quiz 10</b>:
+How long a sequence of grace periods, each separated by an RCU read-side
+critical section, would be required to partition the RCU read-side
+critical sections at the beginning and end of the chain?
+
+
+</p><p><b>Answer</b>:
+In theory, an infinite number.
+In practice, an unknown number that is sensitive to both implementation
+details and timing considerations.
+Therefore, even in practice, RCU users must abide by the theoretical rather
+than the practical answer.
+
+
+</p><p><a href="#Quick%20Quiz%2010"><b>Back to Quick Quiz 10</b>.</a>
+
+<a name="qq11answer"></a>
+<p><b>Quick Quiz 11</b>:
+What about sleeping locks?
+
+
+</p><p><b>Answer</b>:
+These are forbidden within Linux-kernel RCU read-side critical sections
+because it is not legal to place a quiescent state (in this case,
+voluntary context switch) within an RCU read-side critical section.
+However, sleeping locks may be used within userspace RCU read-side critical
+sections, and also within Linux-kernel sleepable RCU
+<a href="#Sleepable RCU">(SRCU)</a>
+read-side critical sections.
+In addition, the -rt patchset turns spinlocks into a sleeping locks so
+that the corresponding critical sections can be preempted, which
+also means that these sleeplockified spinlocks (but not other sleeping locks!)
+may be acquire within -rt-Linux-kernel RCU read-side critical sections.
+
+<p>
+Note that it <i>is</i> legal for a normal RCU read-side critical section
+to conditionally acquire a sleeping locks (as in <tt>mutex_trylock()</tt>),
+but only as long as it does not loop indefinitely attempting to
+conditionally acquire that sleeping locks.
+The key point is that things like <tt>mutex_trylock()</tt>
+either return with the mutex held, or return an error indication if
+the mutex was not immediately available.
+Either way, <tt>mutex_trylock()</tt> returns immediately without sleeping.
+
+
+</p><p><a href="#Quick%20Quiz%2011"><b>Back to Quick Quiz 11</b>.</a>
+
+<a name="qq12answer"></a>
+<p><b>Quick Quiz 12</b>:
+Why does line&nbsp;19 use <tt>rcu_access_pointer()</tt>?
+After all, <tt>call_rcu()</tt> on line&nbsp;25 stores into the
+structure, which would interact badly with concurrent insertions.
+Doesn't this mean that <tt>rcu_dereference()</tt> is required?
+
+
+</p><p><b>Answer</b>:
+Presumably the <tt>-&gt;gp_lock</tt> acquired on line&nbsp;18 excludes
+any changes, including any insertions that <tt>rcu_dereference()</tt>
+would protect against.
+Therefore, any insertions will be delayed until after <tt>-&gt;gp_lock</tt>
+is released on line&nbsp;25, which in turn means that
+<tt>rcu_access_pointer()</tt> suffices.
+
+
+</p><p><a href="#Quick%20Quiz%2012"><b>Back to Quick Quiz 12</b>.</a>
+
+<a name="qq13answer"></a>
+<p><b>Quick Quiz 13</b>:
+Earlier it was claimed that <tt>call_rcu()</tt> and
+<tt>kfree_rcu()</tt> allowed updaters to avoid being blocked
+by readers.
+But how can that be correct, given that the invocation of the callback
+and the freeing of the memory (respectively) must still wait for
+a grace period to elapse?
+
+
+</p><p><b>Answer</b>:
+We could define things this way, but keep in mind that this sort of
+definition would say that updates in garbage-collected languages
+cannot complete until the next time the garbage collector runs,
+which does not seem at all reasonable.
+The key point is that in most cases, an updater using either
+<tt>call_rcu()</tt> or <tt>kfree_rcu()</tt> can proceed to the
+next update as soon as it has invoked <tt>call_rcu()</tt> or
+<tt>kfree_rcu()</tt>, without having to wait for a subsequent
+grace period.
+
+
+</p><p><a href="#Quick%20Quiz%2013"><b>Back to Quick Quiz 13</b>.</a>
+
+<a name="qq14answer"></a>
+<p><b>Quick Quiz 14</b>:
+So what happens with <tt>synchronize_rcu()</tt> during
+scheduler initialization for <tt>CONFIG_PREEMPT=n</tt>
+kernels?
+
+
+</p><p><b>Answer</b>:
+In <tt>CONFIG_PREEMPT=n</tt> kernel, <tt>synchronize_rcu()</tt>
+maps directly to <tt>synchronize_sched()</tt>.
+Therefore, <tt>synchronize_rcu()</tt> works normally throughout
+boot in <tt>CONFIG_PREEMPT=n</tt> kernels.
+However, your code must also work in <tt>CONFIG_PREEMPT=y</tt> kernels,
+so it is still necessary to avoid invoking <tt>synchronize_rcu()</tt>
+during scheduler initialization.
+
+
+</p><p><a href="#Quick%20Quiz%2014"><b>Back to Quick Quiz 14</b>.</a>
+
+
+</body></html>
diff --git a/Documentation/RCU/Design/Requirements/Requirements.htmlx b/Documentation/RCU/Design/Requirements/Requirements.htmlx
new file mode 100644
index 000000000000..3a97ba490c42
--- /dev/null
+++ b/Documentation/RCU/Design/Requirements/Requirements.htmlx
@@ -0,0 +1,2741 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+ <html>
+ <head><title>A Tour Through RCU's Requirements [LWN.net]</title>
+ <meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
+
+<h1>A Tour Through RCU's Requirements</h1>
+
+<p>Copyright IBM Corporation, 2015</p>
+<p>Author: Paul E.&nbsp;McKenney</p>
+<p><i>The initial version of this document appeared in the
+<a href="https://lwn.net/">LWN</a> articles
+<a href="https://lwn.net/Articles/652156/">here</a>,
+<a href="https://lwn.net/Articles/652677/">here</a>, and
+<a href="https://lwn.net/Articles/653326/">here</a>.</i></p>
+
+<h2>Introduction</h2>
+
+<p>
+Read-copy update (RCU) is a synchronization mechanism that is often
+used as a replacement for reader-writer locking.
+RCU is unusual in that updaters do not block readers,
+which means that RCU's read-side primitives can be exceedingly fast
+and scalable.
+In addition, updaters can make useful forward progress concurrently
+with readers.
+However, all this concurrency between RCU readers and updaters does raise
+the question of exactly what RCU readers are doing, which in turn
+raises the question of exactly what RCU's requirements are.
+
+<p>
+This document therefore summarizes RCU's requirements, and can be thought
+of as an informal, high-level specification for RCU.
+It is important to understand that RCU's specification is primarily
+empirical in nature;
+in fact, I learned about many of these requirements the hard way.
+This situation might cause some consternation, however, not only
+has this learning process been a lot of fun, but it has also been
+a great privilege to work with so many people willing to apply
+technologies in interesting new ways.
+
+<p>
+All that aside, here are the categories of currently known RCU requirements:
+</p>
+
+<ol>
+<li> <a href="#Fundamental Requirements">
+ Fundamental Requirements</a>
+<li> <a href="#Fundamental Non-Requirements">Fundamental Non-Requirements</a>
+<li> <a href="#Parallelism Facts of Life">
+ Parallelism Facts of Life</a>
+<li> <a href="#Quality-of-Implementation Requirements">
+ Quality-of-Implementation Requirements</a>
+<li> <a href="#Linux Kernel Complications">
+ Linux Kernel Complications</a>
+<li> <a href="#Software-Engineering Requirements">
+ Software-Engineering Requirements</a>
+<li> <a href="#Other RCU Flavors">
+ Other RCU Flavors</a>
+<li> <a href="#Possible Future Changes">
+ Possible Future Changes</a>
+</ol>
+
+<p>
+This is followed by a <a href="#Summary">summary</a>,
+which is in turn followed by the inevitable
+<a href="#Answers to Quick Quizzes">answers to the quick quizzes</a>.
+
+<h2><a name="Fundamental Requirements">Fundamental Requirements</a></h2>
+
+<p>
+RCU's fundamental requirements are the closest thing RCU has to hard
+mathematical requirements.
+These are:
+
+<ol>
+<li> <a href="#Grace-Period Guarantee">
+ Grace-Period Guarantee</a>
+<li> <a href="#Publish-Subscribe Guarantee">
+ Publish-Subscribe Guarantee</a>
+<li> <a href="#Memory-Barrier Guarantees">
+ Memory-Barrier Guarantees</a>
+<li> <a href="#RCU Primitives Guaranteed to Execute Unconditionally">
+ RCU Primitives Guaranteed to Execute Unconditionally</a>
+<li> <a href="#Guaranteed Read-to-Write Upgrade">
+ Guaranteed Read-to-Write Upgrade</a>
+</ol>
+
+<h3><a name="Grace-Period Guarantee">Grace-Period Guarantee</a></h3>
+
+<p>
+RCU's grace-period guarantee is unusual in being premeditated:
+Jack Slingwine and I had this guarantee firmly in mind when we started
+work on RCU (then called &ldquo;rclock&rdquo;) in the early 1990s.
+That said, the past two decades of experience with RCU have produced
+a much more detailed understanding of this guarantee.
+
+<p>
+RCU's grace-period guarantee allows updaters to wait for the completion
+of all pre-existing RCU read-side critical sections.
+An RCU read-side critical section
+begins with the marker <tt>rcu_read_lock()</tt> and ends with
+the marker <tt>rcu_read_unlock()</tt>.
+These markers may be nested, and RCU treats a nested set as one
+big RCU read-side critical section.
+Production-quality implementations of <tt>rcu_read_lock()</tt> and
+<tt>rcu_read_unlock()</tt> are extremely lightweight, and in
+fact have exactly zero overhead in Linux kernels built for production
+use with <tt>CONFIG_PREEMPT=n</tt>.
+
+<p>
+This guarantee allows ordering to be enforced with extremely low
+overhead to readers, for example:
+
+<blockquote>
+<pre>
+ 1 int x, y;
+ 2
+ 3 void thread0(void)
+ 4 {
+ 5 rcu_read_lock();
+ 6 r1 = READ_ONCE(x);
+ 7 r2 = READ_ONCE(y);
+ 8 rcu_read_unlock();
+ 9 }
+10
+11 void thread1(void)
+12 {
+13 WRITE_ONCE(x, 1);
+14 synchronize_rcu();
+15 WRITE_ONCE(y, 1);
+16 }
+</pre>
+</blockquote>
+
+<p>
+Because the <tt>synchronize_rcu()</tt> on line&nbsp;14 waits for
+all pre-existing readers, any instance of <tt>thread0()</tt> that
+loads a value of zero from <tt>x</tt> must complete before
+<tt>thread1()</tt> stores to <tt>y</tt>, so that instance must
+also load a value of zero from <tt>y</tt>.
+Similarly, any instance of <tt>thread0()</tt> that loads a value of
+one from <tt>y</tt> must have started after the
+<tt>synchronize_rcu()</tt> started, and must therefore also load
+a value of one from <tt>x</tt>.
+Therefore, the outcome:
+<blockquote>
+<pre>
+(r1 == 0 &amp;&amp; r2 == 1)
+</pre>
+</blockquote>
+cannot happen.
+
+<p>@@QQ@@
+Wait a minute!
+You said that updaters can make useful forward progress concurrently
+with readers, but pre-existing readers will block
+<tt>synchronize_rcu()</tt>!!!
+Just who are you trying to fool???
+<p>@@QQA@@
+First, if updaters do not wish to be blocked by readers, they can use
+<tt>call_rcu()</tt> or <tt>kfree_rcu()</tt>, which will
+be discussed later.
+Second, even when using <tt>synchronize_rcu()</tt>, the other
+update-side code does run concurrently with readers, whether pre-existing
+or not.
+<p>@@QQE@@
+
+<p>
+This scenario resembles one of the first uses of RCU in
+<a href="https://en.wikipedia.org/wiki/DYNIX">DYNIX/ptx</a>,
+which managed a distributed lock manager's transition into
+a state suitable for handling recovery from node failure,
+more or less as follows:
+
+<blockquote>
+<pre>
+ 1 #define STATE_NORMAL 0
+ 2 #define STATE_WANT_RECOVERY 1
+ 3 #define STATE_RECOVERING 2
+ 4 #define STATE_WANT_NORMAL 3
+ 5
+ 6 int state = STATE_NORMAL;
+ 7
+ 8 void do_something_dlm(void)
+ 9 {
+10 int state_snap;
+11
+12 rcu_read_lock();
+13 state_snap = READ_ONCE(state);
+14 if (state_snap == STATE_NORMAL)
+15 do_something();
+16 else
+17 do_something_carefully();
+18 rcu_read_unlock();
+19 }
+20
+21 void start_recovery(void)
+22 {
+23 WRITE_ONCE(state, STATE_WANT_RECOVERY);
+24 synchronize_rcu();
+25 WRITE_ONCE(state, STATE_RECOVERING);
+26 recovery();
+27 WRITE_ONCE(state, STATE_WANT_NORMAL);
+28 synchronize_rcu();
+29 WRITE_ONCE(state, STATE_NORMAL);
+30 }
+</pre>
+</blockquote>
+
+<p>
+The RCU read-side critical section in <tt>do_something_dlm()</tt>
+works with the <tt>synchronize_rcu()</tt> in <tt>start_recovery()</tt>
+to guarantee that <tt>do_something()</tt> never runs concurrently
+with <tt>recovery()</tt>, but with little or no synchronization
+overhead in <tt>do_something_dlm()</tt>.
+
+<p>@@QQ@@
+Why is the <tt>synchronize_rcu()</tt> on line&nbsp;28 needed?
+<p>@@QQA@@
+Without that extra grace period, memory reordering could result in
+<tt>do_something_dlm()</tt> executing <tt>do_something()</tt>
+concurrently with the last bits of <tt>recovery()</tt>.
+<p>@@QQE@@
+
+<p>
+In order to avoid fatal problems such as deadlocks,
+an RCU read-side critical section must not contain calls to
+<tt>synchronize_rcu()</tt>.
+Similarly, an RCU read-side critical section must not
+contain anything that waits, directly or indirectly, on completion of
+an invocation of <tt>synchronize_rcu()</tt>.
+
+<p>
+Although RCU's grace-period guarantee is useful in and of itself, with
+<a href="https://lwn.net/Articles/573497/">quite a few use cases</a>,
+it would be good to be able to use RCU to coordinate read-side
+access to linked data structures.
+For this, the grace-period guarantee is not sufficient, as can
+be seen in function <tt>add_gp_buggy()</tt> below.
+We will look at the reader's code later, but in the meantime, just think of
+the reader as locklessly picking up the <tt>gp</tt> pointer,
+and, if the value loaded is non-<tt>NULL</tt>, locklessly accessing the
+<tt>-&gt;a</tt> and <tt>-&gt;b</tt> fields.
+
+<blockquote>
+<pre>
+ 1 bool add_gp_buggy(int a, int b)
+ 2 {
+ 3 p = kmalloc(sizeof(*p), GFP_KERNEL);
+ 4 if (!p)
+ 5 return -ENOMEM;
+ 6 spin_lock(&amp;gp_lock);
+ 7 if (rcu_access_pointer(gp)) {
+ 8 spin_unlock(&amp;gp_lock);
+ 9 return false;
+10 }
+11 p-&gt;a = a;
+12 p-&gt;b = a;
+13 gp = p; /* ORDERING BUG */
+14 spin_unlock(&amp;gp_lock);
+15 return true;
+16 }
+</pre>
+</blockquote>
+
+<p>
+The problem is that both the compiler and weakly ordered CPUs are within
+their rights to reorder this code as follows:
+
+<blockquote>
+<pre>
+ 1 bool add_gp_buggy_optimized(int a, int b)
+ 2 {
+ 3 p = kmalloc(sizeof(*p), GFP_KERNEL);
+ 4 if (!p)
+ 5 return -ENOMEM;
+ 6 spin_lock(&amp;gp_lock);
+ 7 if (rcu_access_pointer(gp)) {
+ 8 spin_unlock(&amp;gp_lock);
+ 9 return false;
+10 }
+<b>11 gp = p; /* ORDERING BUG */
+12 p-&gt;a = a;
+13 p-&gt;b = a;</b>
+14 spin_unlock(&amp;gp_lock);
+15 return true;
+16 }
+</pre>
+</blockquote>
+
+<p>
+If an RCU reader fetches <tt>gp</tt> just after
+<tt>add_gp_buggy_optimized</tt> executes line&nbsp;11,
+it will see garbage in the <tt>-&gt;a</tt> and <tt>-&gt;b</tt>
+fields.
+And this is but one of many ways in which compiler and hardware optimizations
+could cause trouble.
+Therefore, we clearly need some way to prevent the compiler and the CPU from
+reordering in this manner, which brings us to the publish-subscribe
+guarantee discussed in the next section.
+
+<h3><a name="Publish-Subscribe Guarantee">Publish/Subscribe Guarantee</a></h3>
+
+<p>
+RCU's publish-subscribe guarantee allows data to be inserted
+into a linked data structure without disrupting RCU readers.
+The updater uses <tt>rcu_assign_pointer()</tt> to insert the
+new data, and readers use <tt>rcu_dereference()</tt> to
+access data, whether new or old.
+The following shows an example of insertion:
+
+<blockquote>
+<pre>
+ 1 bool add_gp(int a, int b)
+ 2 {
+ 3 p = kmalloc(sizeof(*p), GFP_KERNEL);
+ 4 if (!p)
+ 5 return -ENOMEM;
+ 6 spin_lock(&amp;gp_lock);
+ 7 if (rcu_access_pointer(gp)) {
+ 8 spin_unlock(&amp;gp_lock);
+ 9 return false;
+10 }
+11 p-&gt;a = a;
+12 p-&gt;b = a;
+13 rcu_assign_pointer(gp, p);
+14 spin_unlock(&amp;gp_lock);
+15 return true;
+16 }
+</pre>
+</blockquote>
+
+<p>
+The <tt>rcu_assign_pointer()</tt> on line&nbsp;13 is conceptually
+equivalent to a simple assignment statement, but also guarantees
+that its assignment will
+happen after the two assignments in lines&nbsp;11 and&nbsp;12,
+similar to the C11 <tt>memory_order_release</tt> store operation.
+It also prevents any number of &ldquo;interesting&rdquo; compiler
+optimizations, for example, the use of <tt>gp</tt> as a scratch
+location immediately preceding the assignment.
+
+<p>@@QQ@@
+But <tt>rcu_assign_pointer()</tt> does nothing to prevent the
+two assignments to <tt>p-&gt;a</tt> and <tt>p-&gt;b</tt>
+from being reordered.
+Can't that also cause problems?
+<p>@@QQA@@
+No, it cannot.
+The readers cannot see either of these two fields until
+the assignment to <tt>gp</tt>, by which time both fields are
+fully initialized.
+So reordering the assignments
+to <tt>p-&gt;a</tt> and <tt>p-&gt;b</tt> cannot possibly
+cause any problems.
+<p>@@QQE@@
+
+<p>
+It is tempting to assume that the reader need not do anything special
+to control its accesses to the RCU-protected data,
+as shown in <tt>do_something_gp_buggy()</tt> below:
+
+<blockquote>
+<pre>
+ 1 bool do_something_gp_buggy(void)
+ 2 {
+ 3 rcu_read_lock();
+ 4 p = gp; /* OPTIMIZATIONS GALORE!!! */
+ 5 if (p) {
+ 6 do_something(p-&gt;a, p-&gt;b);
+ 7 rcu_read_unlock();
+ 8 return true;
+ 9 }
+10 rcu_read_unlock();
+11 return false;
+12 }
+</pre>
+</blockquote>
+
+<p>
+However, this temptation must be resisted because there are a
+surprisingly large number of ways that the compiler
+(to say nothing of
+<a href="https://h71000.www7.hp.com/wizard/wiz_2637.html">DEC Alpha CPUs</a>)
+can trip this code up.
+For but one example, if the compiler were short of registers, it
+might choose to refetch from <tt>gp</tt> rather than keeping
+a separate copy in <tt>p</tt> as follows:
+
+<blockquote>
+<pre>
+ 1 bool do_something_gp_buggy_optimized(void)
+ 2 {
+ 3 rcu_read_lock();
+ 4 if (gp) { /* OPTIMIZATIONS GALORE!!! */
+<b> 5 do_something(gp-&gt;a, gp-&gt;b);</b>
+ 6 rcu_read_unlock();
+ 7 return true;
+ 8 }
+ 9 rcu_read_unlock();
+10 return false;
+11 }
+</pre>
+</blockquote>
+
+<p>
+If this function ran concurrently with a series of updates that
+replaced the current structure with a new one,
+the fetches of <tt>gp-&gt;a</tt>
+and <tt>gp-&gt;b</tt> might well come from two different structures,
+which could cause serious confusion.
+To prevent this (and much else besides), <tt>do_something_gp()</tt> uses
+<tt>rcu_dereference()</tt> to fetch from <tt>gp</tt>:
+
+<blockquote>
+<pre>
+ 1 bool do_something_gp(void)
+ 2 {
+ 3 rcu_read_lock();
+ 4 p = rcu_dereference(gp);
+ 5 if (p) {
+ 6 do_something(p-&gt;a, p-&gt;b);
+ 7 rcu_read_unlock();
+ 8 return true;
+ 9 }
+10 rcu_read_unlock();
+11 return false;
+12 }
+</pre>
+</blockquote>
+
+<p>
+The <tt>rcu_dereference()</tt> uses volatile casts and (for DEC Alpha)
+memory barriers in the Linux kernel.
+Should a
+<a href="http://www.rdrop.com/users/paulmck/RCU/consume.2015.07.13a.pdf">high-quality implementation of C11 <tt>memory_order_consume</tt> [PDF]</a>
+ever appear, then <tt>rcu_dereference()</tt> could be implemented
+as a <tt>memory_order_consume</tt> load.
+Regardless of the exact implementation, a pointer fetched by
+<tt>rcu_dereference()</tt> may not be used outside of the
+outermost RCU read-side critical section containing that
+<tt>rcu_dereference()</tt>, unless protection of
+the corresponding data element has been passed from RCU to some
+other synchronization mechanism, most commonly locking or
+<a href="https://www.kernel.org/doc/Documentation/RCU/rcuref.txt">reference counting</a>.
+
+<p>
+In short, updaters use <tt>rcu_assign_pointer()</tt> and readers
+use <tt>rcu_dereference()</tt>, and these two RCU API elements
+work together to ensure that readers have a consistent view of
+newly added data elements.
+
+<p>
+Of course, it is also necessary to remove elements from RCU-protected
+data structures, for example, using the following process:
+
+<ol>
+<li> Remove the data element from the enclosing structure.
+<li> Wait for all pre-existing RCU read-side critical sections
+ to complete (because only pre-existing readers can possibly have
+ a reference to the newly removed data element).
+<li> At this point, only the updater has a reference to the
+ newly removed data element, so it can safely reclaim
+ the data element, for example, by passing it to <tt>kfree()</tt>.
+</ol>
+
+This process is implemented by <tt>remove_gp_synchronous()</tt>:
+
+<blockquote>
+<pre>
+ 1 bool remove_gp_synchronous(void)
+ 2 {
+ 3 struct foo *p;
+ 4
+ 5 spin_lock(&amp;gp_lock);
+ 6 p = rcu_access_pointer(gp);
+ 7 if (!p) {
+ 8 spin_unlock(&amp;gp_lock);
+ 9 return false;
+10 }
+11 rcu_assign_pointer(gp, NULL);
+12 spin_unlock(&amp;gp_lock);
+13 synchronize_rcu();
+14 kfree(p);
+15 return true;
+16 }
+</pre>
+</blockquote>
+
+<p>
+This function is straightforward, with line&nbsp;13 waiting for a grace
+period before line&nbsp;14 frees the old data element.
+This waiting ensures that readers will reach line&nbsp;7 of
+<tt>do_something_gp()</tt> before the data element referenced by
+<tt>p</tt> is freed.
+The <tt>rcu_access_pointer()</tt> on line&nbsp;6 is similar to
+<tt>rcu_dereference()</tt>, except that:
+
+<ol>
+<li> The value returned by <tt>rcu_access_pointer()</tt>
+ cannot be dereferenced.
+ If you want to access the value pointed to as well as
+ the pointer itself, use <tt>rcu_dereference()</tt>
+ instead of <tt>rcu_access_pointer()</tt>.
+<li> The call to <tt>rcu_access_pointer()</tt> need not be
+ protected.
+ In contrast, <tt>rcu_dereference()</tt> must either be
+ within an RCU read-side critical section or in a code
+ segment where the pointer cannot change, for example, in
+ code protected by the corresponding update-side lock.
+</ol>
+
+<p>@@QQ@@
+Without the <tt>rcu_dereference()</tt> or the
+<tt>rcu_access_pointer()</tt>, what destructive optimizations
+might the compiler make use of?
+<p>@@QQA@@
+Let's start with what happens to <tt>do_something_gp()</tt>
+if it fails to use <tt>rcu_dereference()</tt>.
+It could reuse a value formerly fetched from this same pointer.
+It could also fetch the pointer from <tt>gp</tt> in a byte-at-a-time
+manner, resulting in <i>load tearing</i>, in turn resulting a bytewise
+mash-up of two distince pointer values.
+It might even use value-speculation optimizations, where it makes a wrong
+guess, but by the time it gets around to checking the value, an update
+has changed the pointer to match the wrong guess.
+Too bad about any dereferences that returned pre-initialization garbage
+in the meantime!
+
+<p>
+For <tt>remove_gp_synchronous()</tt>, as long as all modifications
+to <tt>gp</tt> are carried out while holding <tt>gp_lock</tt>,
+the above optimizations are harmless.
+However,
+with <tt>CONFIG_SPARSE_RCU_POINTER=y</tt>,
+<tt>sparse</tt> will complain if you
+define <tt>gp</tt> with <tt>__rcu</tt> and then
+access it without using
+either <tt>rcu_access_pointer()</tt> or <tt>rcu_dereference()</tt>.
+<p>@@QQE@@
+
+<p>
+In short, RCU's publish-subscribe guarantee is provided by the combination
+of <tt>rcu_assign_pointer()</tt> and <tt>rcu_dereference()</tt>.
+This guarantee allows data elements to be safely added to RCU-protected
+linked data structures without disrupting RCU readers.
+This guarantee can be used in combination with the grace-period
+guarantee to also allow data elements to be removed from RCU-protected
+linked data structures, again without disrupting RCU readers.
+
+<p>
+This guarantee was only partially premeditated.
+DYNIX/ptx used an explicit memory barrier for publication, but had nothing
+resembling <tt>rcu_dereference()</tt> for subscription, nor did it
+have anything resembling the <tt>smp_read_barrier_depends()</tt>
+that was later subsumed into <tt>rcu_dereference()</tt>.
+The need for these operations made itself known quite suddenly at a
+late-1990s meeting with the DEC Alpha architects, back in the days when
+DEC was still a free-standing company.
+It took the Alpha architects a good hour to convince me that any sort
+of barrier would ever be needed, and it then took me a good <i>two</i> hours
+to convince them that their documentation did not make this point clear.
+More recent work with the C and C++ standards committees have provided
+much education on tricks and traps from the compiler.
+In short, compilers were much less tricky in the early 1990s, but in
+2015, don't even think about omitting <tt>rcu_dereference()</tt>!
+
+<h3><a name="Memory-Barrier Guarantees">Memory-Barrier Guarantees</a></h3>
+
+<p>
+The previous section's simple linked-data-structure scenario clearly
+demonstrates the need for RCU's stringent memory-ordering guarantees on
+systems with more than one CPU:
+
+<ol>
+<li> Each CPU that has an RCU read-side critical section that
+ begins before <tt>synchronize_rcu()</tt> starts is
+ guaranteed to execute a full memory barrier between the time
+ that the RCU read-side critical section ends and the time that
+ <tt>synchronize_rcu()</tt> returns.
+ Without this guarantee, a pre-existing RCU read-side critical section
+ might hold a reference to the newly removed <tt>struct foo</tt>
+ after the <tt>kfree()</tt> on line&nbsp;14 of
+ <tt>remove_gp_synchronous()</tt>.
+<li> Each CPU that has an RCU read-side critical section that ends
+ after <tt>synchronize_rcu()</tt> returns is guaranteed
+ to execute a full memory barrier between the time that
+ <tt>synchronize_rcu()</tt> begins and the time that the RCU
+ read-side critical section begins.
+ Without this guarantee, a later RCU read-side critical section
+ running after the <tt>kfree()</tt> on line&nbsp;14 of
+ <tt>remove_gp_synchronous()</tt> might
+ later run <tt>do_something_gp()</tt> and find the
+ newly deleted <tt>struct foo</tt>.
+<li> If the task invoking <tt>synchronize_rcu()</tt> remains
+ on a given CPU, then that CPU is guaranteed to execute a full
+ memory barrier sometime during the execution of
+ <tt>synchronize_rcu()</tt>.
+ This guarantee ensures that the <tt>kfree()</tt> on
+ line&nbsp;14 of <tt>remove_gp_synchronous()</tt> really does
+ execute after the removal on line&nbsp;11.
+<li> If the task invoking <tt>synchronize_rcu()</tt> migrates
+ among a group of CPUs during that invocation, then each of the
+ CPUs in that group is guaranteed to execute a full memory barrier
+ sometime during the execution of <tt>synchronize_rcu()</tt>.
+ This guarantee also ensures that the <tt>kfree()</tt> on
+ line&nbsp;14 of <tt>remove_gp_synchronous()</tt> really does
+ execute after the removal on
+ line&nbsp;11, but also in the case where the thread executing the
+ <tt>synchronize_rcu()</tt> migrates in the meantime.
+</ol>
+
+<p>@@QQ@@
+Given that multiple CPUs can start RCU read-side critical sections
+at any time without any ordering whatsoever, how can RCU possibly tell whether
+or not a given RCU read-side critical section starts before a
+given instance of <tt>synchronize_rcu()</tt>?
+<p>@@QQA@@
+If RCU cannot tell whether or not a given
+RCU read-side critical section starts before a
+given instance of <tt>synchronize_rcu()</tt>,
+then it must assume that the RCU read-side critical section
+started first.
+In other words, a given instance of <tt>synchronize_rcu()</tt>
+can avoid waiting on a given RCU read-side critical section only
+if it can prove that <tt>synchronize_rcu()</tt> started first.
+<p>@@QQE@@
+
+<p>@@QQ@@
+The first and second guarantees require unbelievably strict ordering!
+Are all these memory barriers <i> really</i> required?
+<p>@@QQA@@
+Yes, they really are required.
+To see why the first guarantee is required, consider the following
+sequence of events:
+
+<ol>
+<li> CPU 1: <tt>rcu_read_lock()</tt>
+<li> CPU 1: <tt>q = rcu_dereference(gp);
+ /* Very likely to return p. */</tt>
+<li> CPU 0: <tt>list_del_rcu(p);</tt>
+<li> CPU 0: <tt>synchronize_rcu()</tt> starts.
+<li> CPU 1: <tt>do_something_with(q-&gt;a);
+ /* No smp_mb(), so might happen after kfree(). */</tt>
+<li> CPU 1: <tt>rcu_read_unlock()</tt>
+<li> CPU 0: <tt>synchronize_rcu()</tt> returns.
+<li> CPU 0: <tt>kfree(p);</tt>
+</ol>
+
+<p>
+Therefore, there absolutely must be a full memory barrier between the
+end of the RCU read-side critical section and the end of the
+grace period.
+
+<p>
+The sequence of events demonstrating the necessity of the second rule
+is roughly similar:
+
+<ol>
+<li> CPU 0: <tt>list_del_rcu(p);</tt>
+<li> CPU 0: <tt>synchronize_rcu()</tt> starts.
+<li> CPU 1: <tt>rcu_read_lock()</tt>
+<li> CPU 1: <tt>q = rcu_dereference(gp);
+ /* Might return p if no memory barrier. */</tt>
+<li> CPU 0: <tt>synchronize_rcu()</tt> returns.
+<li> CPU 0: <tt>kfree(p);</tt>
+<li> CPU 1: <tt>do_something_with(q-&gt;a); /* Boom!!! */</tt>
+<li> CPU 1: <tt>rcu_read_unlock()</tt>
+</ol>
+
+<p>
+And similarly, without a memory barrier between the beginning of the
+grace period and the beginning of the RCU read-side critical section,
+CPU&nbsp;1 might end up accessing the freelist.
+
+<p>
+The &ldquo;as if&rdquo; rule of course applies, so that any implementation
+that acts as if the appropriate memory barriers were in place is a
+correct implementation.
+That said, it is much easier to fool yourself into believing that you have
+adhered to the as-if rule than it is to actually adhere to it!
+<p>@@QQE@@
+
+<p>
+Note that these memory-barrier requirements do not replace the fundamental
+RCU requirement that a grace period wait for all pre-existing readers.
+On the contrary, the memory barriers called out in this section must operate in
+such a way as to <i>enforce</i> this fundamental requirement.
+Of course, different implementations enforce this requirement in different
+ways, but enforce it they must.
+
+<h3><a name="RCU Primitives Guaranteed to Execute Unconditionally">RCU Primitives Guaranteed to Execute Unconditionally</a></h3>
+
+<p>
+The common-case RCU primitives are unconditional.
+They are invoked, they do their job, and they return, with no possibility
+of error, and no need to retry.
+This is a key RCU design philosophy.
+
+<p>
+However, this philosophy is pragmatic rather than pigheaded.
+If someone comes up with a good justification for a particular conditional
+RCU primitive, it might well be implemented and added.
+After all, this guarantee was reverse-engineered, not premeditated.
+The unconditional nature of the RCU primitives was initially an
+accident of implementation, and later experience with synchronization
+primitives with conditional primitives caused me to elevate this
+accident to a guarantee.
+Therefore, the justification for adding a conditional primitive to
+RCU would need to be based on detailed and compelling use cases.
+
+<h3><a name="Guaranteed Read-to-Write Upgrade">Guaranteed Read-to-Write Upgrade</a></h3>
+
+<p>
+As far as RCU is concerned, it is always possible to carry out an
+update within an RCU read-side critical section.
+For example, that RCU read-side critical section might search for
+a given data element, and then might acquire the update-side
+spinlock in order to update that element, all while remaining
+in that RCU read-side critical section.
+Of course, it is necessary to exit the RCU read-side critical section
+before invoking <tt>synchronize_rcu()</tt>, however, this
+inconvenience can be avoided through use of the
+<tt>call_rcu()</tt> and <tt>kfree_rcu()</tt> API members
+described later in this document.
+
+<p>@@QQ@@
+But how does the upgrade-to-write operation exclude other readers?
+<p>@@QQA@@
+It doesn't, just like normal RCU updates, which also do not exclude
+RCU readers.
+<p>@@QQE@@
+
+<p>
+This guarantee allows lookup code to be shared between read-side
+and update-side code, and was premeditated, appearing in the earliest
+DYNIX/ptx RCU documentation.
+
+<h2><a name="Fundamental Non-Requirements">Fundamental Non-Requirements</a></h2>
+
+<p>
+RCU provides extremely lightweight readers, and its read-side guarantees,
+though quite useful, are correspondingly lightweight.
+It is therefore all too easy to assume that RCU is guaranteeing more
+than it really is.
+Of course, the list of things that RCU does not guarantee is infinitely
+long, however, the following sections list a few non-guarantees that
+have caused confusion.
+Except where otherwise noted, these non-guarantees were premeditated.
+
+<ol>
+<li> <a href="#Readers Impose Minimal Ordering">
+ Readers Impose Minimal Ordering</a>
+<li> <a href="#Readers Do Not Exclude Updaters">
+ Readers Do Not Exclude Updaters</a>
+<li> <a href="#Updaters Only Wait For Old Readers">
+ Updaters Only Wait For Old Readers</a>
+<li> <a href="#Grace Periods Don't Partition Read-Side Critical Sections">
+ Grace Periods Don't Partition Read-Side Critical Sections</a>
+<li> <a href="#Read-Side Critical Sections Don't Partition Grace Periods">
+ Read-Side Critical Sections Don't Partition Grace Periods</a>
+<li> <a href="#Disabling Preemption Does Not Block Grace Periods">
+ Disabling Preemption Does Not Block Grace Periods</a>
+</ol>
+
+<h3><a name="Readers Impose Minimal Ordering">Readers Impose Minimal Ordering</a></h3>
+
+<p>
+Reader-side markers such as <tt>rcu_read_lock()</tt> and
+<tt>rcu_read_unlock()</tt> provide absolutely no ordering guarantees
+except through their interaction with the grace-period APIs such as
+<tt>synchronize_rcu()</tt>.
+To see this, consider the following pair of threads:
+
+<blockquote>
+<pre>
+ 1 void thread0(void)
+ 2 {
+ 3 rcu_read_lock();
+ 4 WRITE_ONCE(x, 1);
+ 5 rcu_read_unlock();
+ 6 rcu_read_lock();
+ 7 WRITE_ONCE(y, 1);
+ 8 rcu_read_unlock();
+ 9 }
+10
+11 void thread1(void)
+12 {
+13 rcu_read_lock();
+14 r1 = READ_ONCE(y);
+15 rcu_read_unlock();
+16 rcu_read_lock();
+17 r2 = READ_ONCE(x);
+18 rcu_read_unlock();
+19 }
+</pre>
+</blockquote>
+
+<p>
+After <tt>thread0()</tt> and <tt>thread1()</tt> execute
+concurrently, it is quite possible to have
+
+<blockquote>
+<pre>
+(r1 == 1 &amp;&amp; r2 == 0)
+</pre>
+</blockquote>
+
+(that is, <tt>y</tt> appears to have been assigned before <tt>x</tt>),
+which would not be possible if <tt>rcu_read_lock()</tt> and
+<tt>rcu_read_unlock()</tt> had much in the way of ordering
+properties.
+But they do not, so the CPU is within its rights
+to do significant reordering.
+This is by design: Any significant ordering constraints would slow down
+these fast-path APIs.
+
+<p>@@QQ@@
+Can't the compiler also reorder this code?
+<p>@@QQA@@
+No, the volatile casts in <tt>READ_ONCE()</tt> and
+<tt>WRITE_ONCE()</tt> prevent the compiler from reordering in
+this particular case.
+<p>@@QQE@@
+
+<h3><a name="Readers Do Not Exclude Updaters">Readers Do Not Exclude Updaters</a></h3>
+
+<p>
+Neither <tt>rcu_read_lock()</tt> nor <tt>rcu_read_unlock()</tt>
+exclude updates.
+All they do is to prevent grace periods from ending.
+The following example illustrates this:
+
+<blockquote>
+<pre>
+ 1 void thread0(void)
+ 2 {
+ 3 rcu_read_lock();
+ 4 r1 = READ_ONCE(y);
+ 5 if (r1) {
+ 6 do_something_with_nonzero_x();
+ 7 r2 = READ_ONCE(x);
+ 8 WARN_ON(!r2); /* BUG!!! */
+ 9 }
+10 rcu_read_unlock();
+11 }
+12
+13 void thread1(void)
+14 {
+15 spin_lock(&amp;my_lock);
+16 WRITE_ONCE(x, 1);
+17 WRITE_ONCE(y, 1);
+18 spin_unlock(&amp;my_lock);
+19 }
+</pre>
+</blockquote>
+
+<p>
+If the <tt>thread0()</tt> function's <tt>rcu_read_lock()</tt>
+excluded the <tt>thread1()</tt> function's update,
+the <tt>WARN_ON()</tt> could never fire.
+But the fact is that <tt>rcu_read_lock()</tt> does not exclude
+much of anything aside from subsequent grace periods, of which
+<tt>thread1()</tt> has none, so the
+<tt>WARN_ON()</tt> can and does fire.
+
+<h3><a name="Updaters Only Wait For Old Readers">Updaters Only Wait For Old Readers</a></h3>
+
+<p>
+It might be tempting to assume that after <tt>synchronize_rcu()</tt>
+completes, there are no readers executing.
+This temptation must be avoided because
+new readers can start immediately after <tt>synchronize_rcu()</tt>
+starts, and <tt>synchronize_rcu()</tt> is under no
+obligation to wait for these new readers.
+
+<p>@@QQ@@
+Suppose that synchronize_rcu() did wait until all readers had completed.
+Would the updater be able to rely on this?
+<p>@@QQA@@
+No.
+Even if <tt>synchronize_rcu()</tt> were to wait until
+all readers had completed, a new reader might start immediately after
+<tt>synchronize_rcu()</tt> completed.
+Therefore, the code following
+<tt>synchronize_rcu()</tt> cannot rely on there being no readers
+in any case.
+<p>@@QQE@@
+
+<h3><a name="Grace Periods Don't Partition Read-Side Critical Sections">
+Grace Periods Don't Partition Read-Side Critical Sections</a></h3>
+
+<p>
+It is tempting to assume that if any part of one RCU read-side critical
+section precedes a given grace period, and if any part of another RCU
+read-side critical section follows that same grace period, then all of
+the first RCU read-side critical section must precede all of the second.
+However, this just isn't the case: A single grace period does not
+partition the set of RCU read-side critical sections.
+An example of this situation can be illustrated as follows, where
+<tt>x</tt>, <tt>y</tt>, and <tt>z</tt> are initially all zero:
+
+<blockquote>
+<pre>
+ 1 void thread0(void)
+ 2 {
+ 3 rcu_read_lock();
+ 4 WRITE_ONCE(a, 1);
+ 5 WRITE_ONCE(b, 1);
+ 6 rcu_read_unlock();
+ 7 }
+ 8
+ 9 void thread1(void)
+10 {
+11 r1 = READ_ONCE(a);
+12 synchronize_rcu();
+13 WRITE_ONCE(c, 1);
+14 }
+15
+16 void thread2(void)
+17 {
+18 rcu_read_lock();
+19 r2 = READ_ONCE(b);
+20 r3 = READ_ONCE(c);
+21 rcu_read_unlock();
+22 }
+</pre>
+</blockquote>
+
+<p>
+It turns out that the outcome:
+
+<blockquote>
+<pre>
+(r1 == 1 &amp;&amp; r2 == 0 &amp;&amp; r3 == 1)
+</pre>
+</blockquote>
+
+is entirely possible.
+The following figure show how this can happen, with each circled
+<tt>QS</tt> indicating the point at which RCU recorded a
+<i>quiescent state</i> for each thread, that is, a state in which
+RCU knows that the thread cannot be in the midst of an RCU read-side
+critical section that started before the current grace period:
+
+<p><img src="GPpartitionReaders1.svg" alt="GPpartitionReaders1.svg" width="60%"></p>
+
+<p>
+If it is necessary to partition RCU read-side critical sections in this
+manner, it is necessary to use two grace periods, where the first
+grace period is known to end before the second grace period starts:
+
+<blockquote>
+<pre>
+ 1 void thread0(void)
+ 2 {
+ 3 rcu_read_lock();
+ 4 WRITE_ONCE(a, 1);
+ 5 WRITE_ONCE(b, 1);
+ 6 rcu_read_unlock();
+ 7 }
+ 8
+ 9 void thread1(void)
+10 {
+11 r1 = READ_ONCE(a);
+12 synchronize_rcu();
+13 WRITE_ONCE(c, 1);
+14 }
+15
+16 void thread2(void)
+17 {
+18 r2 = READ_ONCE(c);
+19 synchronize_rcu();
+20 WRITE_ONCE(d, 1);
+21 }
+22
+23 void thread3(void)
+24 {
+25 rcu_read_lock();
+26 r3 = READ_ONCE(b);
+27 r4 = READ_ONCE(d);
+28 rcu_read_unlock();
+29 }
+</pre>
+</blockquote>
+
+<p>
+Here, if <tt>(r1 == 1)</tt>, then
+<tt>thread0()</tt>'s write to <tt>b</tt> must happen
+before the end of <tt>thread1()</tt>'s grace period.
+If in addition <tt>(r4 == 1)</tt>, then
+<tt>thread3()</tt>'s read from <tt>b</tt> must happen
+after the beginning of <tt>thread2()</tt>'s grace period.
+If it is also the case that <tt>(r2 == 1)</tt>, then the
+end of <tt>thread1()</tt>'s grace period must precede the
+beginning of <tt>thread2()</tt>'s grace period.
+This mean that the two RCU read-side critical sections cannot overlap,
+guaranteeing that <tt>(r3 == 1)</tt>.
+As a result, the outcome:
+
+<blockquote>
+<pre>
+(r1 == 1 &amp;&amp; r2 == 1 &amp;&amp; r3 == 0 &amp;&amp; r4 == 1)
+</pre>
+</blockquote>
+
+cannot happen.
+
+<p>
+This non-requirement was also non-premeditated, but became apparent
+when studying RCU's interaction with memory ordering.
+
+<h3><a name="Read-Side Critical Sections Don't Partition Grace Periods">
+Read-Side Critical Sections Don't Partition Grace Periods</a></h3>
+
+<p>
+It is also tempting to assume that if an RCU read-side critical section
+happens between a pair of grace periods, then those grace periods cannot
+overlap.
+However, this temptation leads nowhere good, as can be illustrated by
+the following, with all variables initially zero:
+
+<blockquote>
+<pre>
+ 1 void thread0(void)
+ 2 {
+ 3 rcu_read_lock();
+ 4 WRITE_ONCE(a, 1);
+ 5 WRITE_ONCE(b, 1);
+ 6 rcu_read_unlock();
+ 7 }
+ 8
+ 9 void thread1(void)
+10 {
+11 r1 = READ_ONCE(a);
+12 synchronize_rcu();
+13 WRITE_ONCE(c, 1);
+14 }
+15
+16 void thread2(void)
+17 {
+18 rcu_read_lock();
+19 WRITE_ONCE(d, 1);
+20 r2 = READ_ONCE(c);
+21 rcu_read_unlock();
+22 }
+23
+24 void thread3(void)
+25 {
+26 r3 = READ_ONCE(d);
+27 synchronize_rcu();
+28 WRITE_ONCE(e, 1);
+29 }
+30
+31 void thread4(void)
+32 {
+33 rcu_read_lock();
+34 r4 = READ_ONCE(b);
+35 r5 = READ_ONCE(e);
+36 rcu_read_unlock();
+37 }
+</pre>
+</blockquote>
+
+<p>
+In this case, the outcome:
+
+<blockquote>
+<pre>
+(r1 == 1 &amp;&amp; r2 == 1 &amp;&amp; r3 == 1 &amp;&amp; r4 == 0 &amp&amp; r5 == 1)
+</pre>
+</blockquote>
+
+is entirely possible, as illustrated below:
+
+<p><img src="ReadersPartitionGP1.svg" alt="ReadersPartitionGP1.svg" width="100%"></p>
+
+<p>
+Again, an RCU read-side critical section can overlap almost all of a
+given grace period, just so long as it does not overlap the entire
+grace period.
+As a result, an RCU read-side critical section cannot partition a pair
+of RCU grace periods.
+
+<p>@@QQ@@
+How long a sequence of grace periods, each separated by an RCU read-side
+critical section, would be required to partition the RCU read-side
+critical sections at the beginning and end of the chain?
+<p>@@QQA@@
+In theory, an infinite number.
+In practice, an unknown number that is sensitive to both implementation
+details and timing considerations.
+Therefore, even in practice, RCU users must abide by the theoretical rather
+than the practical answer.
+<p>@@QQE@@
+
+<h3><a name="Disabling Preemption Does Not Block Grace Periods">
+Disabling Preemption Does Not Block Grace Periods</a></h3>
+
+<p>
+There was a time when disabling preemption on any given CPU would block
+subsequent grace periods.
+However, this was an accident of implementation and is not a requirement.
+And in the current Linux-kernel implementation, disabling preemption
+on a given CPU in fact does not block grace periods, as Oleg Nesterov
+<a href="https://lkml.kernel.org/g/20150614193825.GA19582@redhat.com">demonstrated</a>.
+
+<p>
+If you need a preempt-disable region to block grace periods, you need to add
+<tt>rcu_read_lock()</tt> and <tt>rcu_read_unlock()</tt>, for example
+as follows:
+
+<blockquote>
+<pre>
+ 1 preempt_disable();
+ 2 rcu_read_lock();
+ 3 do_something();
+ 4 rcu_read_unlock();
+ 5 preempt_enable();
+ 6
+ 7 /* Spinlocks implicitly disable preemption. */
+ 8 spin_lock(&amp;mylock);
+ 9 rcu_read_lock();
+10 do_something();
+11 rcu_read_unlock();
+12 spin_unlock(&amp;mylock);
+</pre>
+</blockquote>
+
+<p>
+In theory, you could enter the RCU read-side critical section first,
+but it is more efficient to keep the entire RCU read-side critical
+section contained in the preempt-disable region as shown above.
+Of course, RCU read-side critical sections that extend outside of
+preempt-disable regions will work correctly, but such critical sections
+can be preempted, which forces <tt>rcu_read_unlock()</tt> to do
+more work.
+And no, this is <i>not</i> an invitation to enclose all of your RCU
+read-side critical sections within preempt-disable regions, because
+doing so would degrade real-time response.
+
+<p>
+This non-requirement appeared with preemptible RCU.
+If you need a grace period that waits on non-preemptible code regions, use
+<a href="#Sched Flavor">RCU-sched</a>.
+
+<h2><a name="Parallelism Facts of Life">Parallelism Facts of Life</a></h2>
+
+<p>
+These parallelism facts of life are by no means specific to RCU, but
+the RCU implementation must abide by them.
+They therefore bear repeating:
+
+<ol>
+<li> Any CPU or task may be delayed at any time,
+ and any attempts to avoid these delays by disabling
+ preemption, interrupts, or whatever are completely futile.
+ This is most obvious in preemptible user-level
+ environments and in virtualized environments (where
+ a given guest OS's VCPUs can be preempted at any time by
+ the underlying hypervisor), but can also happen in bare-metal
+ environments due to ECC errors, NMIs, and other hardware
+ events.
+ Although a delay of more than about 20 seconds can result
+ in splats, the RCU implementation is obligated to use
+ algorithms that can tolerate extremely long delays, but where
+ &ldquo;extremely long&rdquo; is not long enough to allow
+ wrap-around when incrementing a 64-bit counter.
+<li> Both the compiler and the CPU can reorder memory accesses.
+ Where it matters, RCU must use compiler directives and
+ memory-barrier instructions to preserve ordering.
+<li> Conflicting writes to memory locations in any given cache line
+ will result in expensive cache misses.
+ Greater numbers of concurrent writes and more-frequent
+ concurrent writes will result in more dramatic slowdowns.
+ RCU is therefore obligated to use algorithms that have
+ sufficient locality to avoid significant performance and
+ scalability problems.
+<li> As a rough rule of thumb, only one CPU's worth of processing
+ may be carried out under the protection of any given exclusive
+ lock.
+ RCU must therefore use scalable locking designs.
+<li> Counters are finite, especially on 32-bit systems.
+ RCU's use of counters must therefore tolerate counter wrap,
+ or be designed such that counter wrap would take way more
+ time than a single system is likely to run.
+ An uptime of ten years is quite possible, a runtime
+ of a century much less so.
+ As an example of the latter, RCU's dyntick-idle nesting counter
+ allows 54 bits for interrupt nesting level (this counter
+ is 64 bits even on a 32-bit system).
+ Overflowing this counter requires 2<sup>54</sup>
+ half-interrupts on a given CPU without that CPU ever going idle.
+ If a half-interrupt happened every microsecond, it would take
+ 570 years of runtime to overflow this counter, which is currently
+ believed to be an acceptably long time.
+<li> Linux systems can have thousands of CPUs running a single
+ Linux kernel in a single shared-memory environment.
+ RCU must therefore pay close attention to high-end scalability.
+</ol>
+
+<p>
+This last parallelism fact of life means that RCU must pay special
+attention to the preceding facts of life.
+The idea that Linux might scale to systems with thousands of CPUs would
+have been met with some skepticism in the 1990s, but these requirements
+would have otherwise have been unsurprising, even in the early 1990s.
+
+<h2><a name="Quality-of-Implementation Requirements">Quality-of-Implementation Requirements</a></h2>
+
+<p>
+These sections list quality-of-implementation requirements.
+Although an RCU implementation that ignores these requirements could
+still be used, it would likely be subject to limitations that would
+make it inappropriate for industrial-strength production use.
+Classes of quality-of-implementation requirements are as follows:
+
+<ol>
+<li> <a href="#Specialization">Specialization</a>
+<li> <a href="#Performance and Scalability">Performance and Scalability</a>
+<li> <a href="#Composability">Composability</a>
+<li> <a href="#Corner Cases">Corner Cases</a>
+</ol>
+
+<p>
+These classes is covered in the following sections.
+
+<h3><a name="Specialization">Specialization</a></h3>
+
+<p>
+RCU is and always has been intended primarily for read-mostly situations, as
+illustrated by the following figure.
+This means that RCU's read-side primitives are optimized, often at the
+expense of its update-side primitives.
+
+<p><img src="RCUApplicability.svg" alt="RCUApplicability.svg" width="70%"></p>
+
+<p>
+This focus on read-mostly situations means that RCU must interoperate
+with other synchronization primitives.
+For example, the <tt>add_gp()</tt> and <tt>remove_gp_synchronous()</tt>
+examples discussed earlier use RCU to protect readers and locking to
+coordinate updaters.
+However, the need extends much farther, requiring that a variety of
+synchronization primitives be legal within RCU read-side critical sections,
+including spinlocks, sequence locks, atomic operations, reference
+counters, and memory barriers.
+
+<p>@@QQ@@
+What about sleeping locks?
+<p>@@QQA@@
+These are forbidden within Linux-kernel RCU read-side critical sections
+because it is not legal to place a quiescent state (in this case,
+voluntary context switch) within an RCU read-side critical section.
+However, sleeping locks may be used within userspace RCU read-side critical
+sections, and also within Linux-kernel sleepable RCU
+<a href="#Sleepable RCU">(SRCU)</a>
+read-side critical sections.
+In addition, the -rt patchset turns spinlocks into a sleeping locks so
+that the corresponding critical sections can be preempted, which
+also means that these sleeplockified spinlocks (but not other sleeping locks!)
+may be acquire within -rt-Linux-kernel RCU read-side critical sections.
+
+<p>
+Note that it <i>is</i> legal for a normal RCU read-side critical section
+to conditionally acquire a sleeping locks (as in <tt>mutex_trylock()</tt>),
+but only as long as it does not loop indefinitely attempting to
+conditionally acquire that sleeping locks.
+The key point is that things like <tt>mutex_trylock()</tt>
+either return with the mutex held, or return an error indication if
+the mutex was not immediately available.
+Either way, <tt>mutex_trylock()</tt> returns immediately without sleeping.
+<p>@@QQE@@
+
+<p>
+It often comes as a surprise that many algorithms do not require a
+consistent view of data, but many can function in that mode,
+with network routing being the poster child.
+Internet routing algorithms take significant time to propagate
+updates, so that by the time an update arrives at a given system,
+that system has been sending network traffic the wrong way for
+a considerable length of time.
+Having a few threads continue to send traffic the wrong way for a
+few more milliseconds is clearly not a problem: In the worst case,
+TCP retransmissions will eventually get the data where it needs to go.
+In general, when tracking the state of the universe outside of the
+computer, some level of inconsistency must be tolerated due to
+speed-of-light delays if nothing else.
+
+<p>
+Furthermore, uncertainty about external state is inherent in many cases.
+For example, a pair of veternarians might use heartbeat to determine
+whether or not a given cat was alive.
+But how long should they wait after the last heartbeat to decide that
+the cat is in fact dead?
+Waiting less than 400 milliseconds makes no sense because this would
+mean that a relaxed cat would be considered to cycle between death
+and life more than 100 times per minute.
+Moreover, just as with human beings, a cat's heart might stop for
+some period of time, so the exact wait period is a judgment call.
+One of our pair of veternarians might wait 30 seconds before pronouncing
+the cat dead, while the other might insist on waiting a full minute.
+The two veternarians would then disagree on the state of the cat during
+the final 30 seconds of the minute following the last heartbeat, as
+fancifully illustrated below:
+
+<p><img src="2013-08-is-it-dead.png" alt="2013-08-is-it-dead.png" width="431"></p>
+
+<p>
+Interestingly enough, this same situation applies to hardware.
+When push comes to shove, how do we tell whether or not some
+external server has failed?
+We send messages to it periodically, and declare it failed if we
+don't receive a response within a given period of time.
+Policy decisions can usually tolerate short
+periods of inconsistency.
+The policy was decided some time ago, and is only now being put into
+effect, so a few milliseconds of delay is normally inconsequential.
+
+<p>
+However, there are algorithms that absolutely must see consistent data.
+For example, the translation between a user-level SystemV semaphore
+ID to the corresponding in-kernel data structure is protected by RCU,
+but it is absolutely forbidden to update a semaphore that has just been
+removed.
+In the Linux kernel, this need for consistency is accommodated by acquiring
+spinlocks located in the in-kernel data structure from within
+the RCU read-side critical section, and this is indicated by the
+green box in the figure above.
+Many other techniques may be used, and are in fact used within the
+Linux kernel.
+
+<p>
+In short, RCU is not required to maintain consistency, and other
+mechanisms may be used in concert with RCU when consistency is required.
+RCU's specialization allows it to do its job extremely well, and its
+ability to interoperate with other synchronization mechanisms allows
+the right mix of synchronization tools to be used for a given job.
+
+<h3><a name="Performance and Scalability">Performance and Scalability</a></h3>
+
+<p>
+Energy efficiency is a critical component of performance today,
+and Linux-kernel RCU implementations must therefore avoid unnecessarily
+awakening idle CPUs.
+I cannot claim that this requirement was premeditated.
+In fact, I learned of it during a telephone conversation in which I
+was given &ldquo;frank and open&rdquo; feedback on the importance
+of energy efficiency in battery-powered systems and on specific
+energy-efficiency shortcomings of the Linux-kernel RCU implementation.
+In my experience, the battery-powered embedded community will consider
+any unnecessary wakeups to be extremely unfriendly acts.
+So much so that mere Linux-kernel-mailing-list posts are
+insufficient to vent their ire.
+
+<p>
+Memory consumption is not particularly important for in most
+situations, and has become decreasingly
+so as memory sizes have expanded and memory
+costs have plummeted.
+However, as I learned from Matt Mackall's
+<a href="http://elinux.org/Linux_Tiny-FAQ">bloatwatch</a>
+efforts, memory footprint is critically important on single-CPU systems with
+non-preemptible (<tt>CONFIG_PREEMPT=n</tt>) kernels, and thus
+<a href="https://lkml.kernel.org/g/20090113221724.GA15307@linux.vnet.ibm.com">tiny RCU</a>
+was born.
+Josh Triplett has since taken over the small-memory banner with his
+<a href="https://tiny.wiki.kernel.org/">Linux kernel tinification</a>
+project, which resulted in
+<a href="#Sleepable RCU">SRCU</a>
+becoming optional for those kernels not needing it.
+
+<p>
+The remaining performance requirements are, for the most part,
+unsurprising.
+For example, in keeping with RCU's read-side specialization,
+<tt>rcu_dereference()</tt> should have negligible overhead (for
+example, suppression of a few minor compiler optimizations).
+Similarly, in non-preemptible environments, <tt>rcu_read_lock()</tt> and
+<tt>rcu_read_unlock()</tt> should have exactly zero overhead.
+
+<p>
+In preemptible environments, in the case where the RCU read-side
+critical section was not preempted (as will be the case for the
+highest-priority real-time process), <tt>rcu_read_lock()</tt> and
+<tt>rcu_read_unlock()</tt> should have minimal overhead.
+In particular, they should not contain atomic read-modify-write
+operations, memory-barrier instructions, preemption disabling,
+interrupt disabling, or backwards branches.
+However, in the case where the RCU read-side critical section was preempted,
+<tt>rcu_read_unlock()</tt> may acquire spinlocks and disable interrupts.
+This is why it is better to nest an RCU read-side critical section
+within a preempt-disable region than vice versa, at least in cases
+where that critical section is short enough to avoid unduly degrading
+real-time latencies.
+
+<p>
+The <tt>synchronize_rcu()</tt> grace-period-wait primitive is
+optimized for throughput.
+It may therefore incur several milliseconds of latency in addition to
+the duration of the longest RCU read-side critical section.
+On the other hand, multiple concurrent invocations of
+<tt>synchronize_rcu()</tt> are required to use batching optimizations
+so that they can be satisfied by a single underlying grace-period-wait
+operation.
+For example, in the Linux kernel, it is not unusual for a single
+grace-period-wait operation to serve more than
+<a href="https://www.usenix.org/conference/2004-usenix-annual-technical-conference/making-rcu-safe-deep-sub-millisecond-response">1,000 separate invocations</a>
+of <tt>synchronize_rcu()</tt>, thus amortizing the per-invocation
+overhead down to nearly zero.
+However, the grace-period optimization is also required to avoid
+measurable degradation of real-time scheduling and interrupt latencies.
+
+<p>
+In some cases, the multi-millisecond <tt>synchronize_rcu()</tt>
+latencies are unacceptable.
+In these cases, <tt>synchronize_rcu_expedited()</tt> may be used
+instead, reducing the grace-period latency down to a few tens of
+microseconds on small systems, at least in cases where the RCU read-side
+critical sections are short.
+There are currently no special latency requirements for
+<tt>synchronize_rcu_expedited()</tt> on large systems, but,
+consistent with the empirical nature of the RCU specification,
+that is subject to change.
+However, there most definitely are scalability requirements:
+A storm of <tt>synchronize_rcu_expedited()</tt> invocations on 4096
+CPUs should at least make reasonable forward progress.
+In return for its shorter latencies, <tt>synchronize_rcu_expedited()</tt>
+is permitted to impose modest degradation of real-time latency
+on non-idle online CPUs.
+That said, it will likely be necessary to take further steps to reduce this
+degradation, hopefully to roughly that of a scheduling-clock interrupt.
+
+<p>
+There are a number of situations where even
+<tt>synchronize_rcu_expedited()</tt>'s reduced grace-period
+latency is unacceptable.
+In these situations, the asynchronous <tt>call_rcu()</tt> can be
+used in place of <tt>synchronize_rcu()</tt> as follows:
+
+<blockquote>
+<pre>
+ 1 struct foo {
+ 2 int a;
+ 3 int b;
+ 4 struct rcu_head rh;
+ 5 };
+ 6
+ 7 static void remove_gp_cb(struct rcu_head *rhp)
+ 8 {
+ 9 struct foo *p = container_of(rhp, struct foo, rh);
+10
+11 kfree(p);
+12 }
+13
+14 bool remove_gp_asynchronous(void)
+15 {
+16 struct foo *p;
+17
+18 spin_lock(&amp;gp_lock);
+19 p = rcu_dereference(gp);
+20 if (!p) {
+21 spin_unlock(&amp;gp_lock);
+22 return false;
+23 }
+24 rcu_assign_pointer(gp, NULL);
+25 call_rcu(&amp;p-&gt;rh, remove_gp_cb);
+26 spin_unlock(&amp;gp_lock);
+27 return true;
+28 }
+</pre>
+</blockquote>
+
+<p>
+A definition of <tt>struct foo</tt> is finally needed, and appears
+on lines&nbsp;1-5.
+The function <tt>remove_gp_cb()</tt> is passed to <tt>call_rcu()</tt>
+on line&nbsp;25, and will be invoked after the end of a subsequent
+grace period.
+This gets the same effect as <tt>remove_gp_synchronous()</tt>,
+but without forcing the updater to wait for a grace period to elapse.
+The <tt>call_rcu()</tt> function may be used in a number of
+situations where neither <tt>synchronize_rcu()</tt> nor
+<tt>synchronize_rcu_expedited()</tt> would be legal,
+including within preempt-disable code, <tt>local_bh_disable()</tt> code,
+interrupt-disable code, and interrupt handlers.
+However, even <tt>call_rcu()</tt> is illegal within NMI handlers.
+The callback function (<tt>remove_gp_cb()</tt> in this case) will be
+executed within softirq (software interrupt) environment within the
+Linux kernel,
+either within a real softirq handler or under the protection
+of <tt>local_bh_disable()</tt>.
+In both the Linux kernel and in userspace, it is bad practice to
+write an RCU callback function that takes too long.
+Long-running operations should be relegated to separate threads or
+(in the Linux kernel) workqueues.
+
+<p>@@QQ@@
+Why does line&nbsp;19 use <tt>rcu_access_pointer()</tt>?
+After all, <tt>call_rcu()</tt> on line&nbsp;25 stores into the
+structure, which would interact badly with concurrent insertions.
+Doesn't this mean that <tt>rcu_dereference()</tt> is required?
+<p>@@QQA@@
+Presumably the <tt>-&gt;gp_lock</tt> acquired on line&nbsp;18 excludes
+any changes, including any insertions that <tt>rcu_dereference()</tt>
+would protect against.
+Therefore, any insertions will be delayed until after <tt>-&gt;gp_lock</tt>
+is released on line&nbsp;25, which in turn means that
+<tt>rcu_access_pointer()</tt> suffices.
+<p>@@QQE@@
+
+<p>
+However, all that <tt>remove_gp_cb()</tt> is doing is
+invoking <tt>kfree()</tt> on the data element.
+This is a common idiom, and is supported by <tt>kfree_rcu()</tt>,
+which allows &ldquo;fire and forget&rdquo; operation as shown below:
+
+<blockquote>
+<pre>
+ 1 struct foo {
+ 2 int a;
+ 3 int b;
+ 4 struct rcu_head rh;
+ 5 };
+ 6
+ 7 bool remove_gp_faf(void)
+ 8 {
+ 9 struct foo *p;
+10
+11 spin_lock(&amp;gp_lock);
+12 p = rcu_dereference(gp);
+13 if (!p) {
+14 spin_unlock(&amp;gp_lock);
+15 return false;
+16 }
+17 rcu_assign_pointer(gp, NULL);
+18 kfree_rcu(p, rh);
+19 spin_unlock(&amp;gp_lock);
+20 return true;
+21 }
+</pre>
+</blockquote>
+
+<p>
+Note that <tt>remove_gp_faf()</tt> simply invokes
+<tt>kfree_rcu()</tt> and proceeds, without any need to pay any
+further attention to the subsequent grace period and <tt>kfree()</tt>.
+It is permissible to invoke <tt>kfree_rcu()</tt> from the same
+environments as for <tt>call_rcu()</tt>.
+Interestingly enough, DYNIX/ptx had the equivalents of
+<tt>call_rcu()</tt> and <tt>kfree_rcu()</tt>, but not
+<tt>synchronize_rcu()</tt>.
+This was due to the fact that RCU was not heavily used within DYNIX/ptx,
+so the very few places that needed something like
+<tt>synchronize_rcu()</tt> simply open-coded it.
+
+<p>@@QQ@@
+Earlier it was claimed that <tt>call_rcu()</tt> and
+<tt>kfree_rcu()</tt> allowed updaters to avoid being blocked
+by readers.
+But how can that be correct, given that the invocation of the callback
+and the freeing of the memory (respectively) must still wait for
+a grace period to elapse?
+<p>@@QQA@@
+We could define things this way, but keep in mind that this sort of
+definition would say that updates in garbage-collected languages
+cannot complete until the next time the garbage collector runs,
+which does not seem at all reasonable.
+The key point is that in most cases, an updater using either
+<tt>call_rcu()</tt> or <tt>kfree_rcu()</tt> can proceed to the
+next update as soon as it has invoked <tt>call_rcu()</tt> or
+<tt>kfree_rcu()</tt>, without having to wait for a subsequent
+grace period.
+<p>@@QQE@@
+
+<p>
+But what if the updater must wait for the completion of code to be
+executed after the end of the grace period, but has other tasks
+that can be carried out in the meantime?
+The polling-style <tt>get_state_synchronize_rcu()</tt> and
+<tt>cond_synchronize_rcu()</tt> functions may be used for this
+purpose, as shown below:
+
+<blockquote>
+<pre>
+ 1 bool remove_gp_poll(void)
+ 2 {
+ 3 struct foo *p;
+ 4 unsigned long s;
+ 5
+ 6 spin_lock(&amp;gp_lock);
+ 7 p = rcu_access_pointer(gp);
+ 8 if (!p) {
+ 9 spin_unlock(&amp;gp_lock);
+10 return false;
+11 }
+12 rcu_assign_pointer(gp, NULL);
+13 spin_unlock(&amp;gp_lock);
+14 s = get_state_synchronize_rcu();
+15 do_something_while_waiting();
+16 cond_synchronize_rcu(s);
+17 kfree(p);
+18 return true;
+19 }
+</pre>
+</blockquote>
+
+<p>
+On line&nbsp;14, <tt>get_state_synchronize_rcu()</tt> obtains a
+&ldquo;cookie&rdquo; from RCU,
+then line&nbsp;15 carries out other tasks,
+and finally, line&nbsp;16 returns immediately if a grace period has
+elapsed in the meantime, but otherwise waits as required.
+The need for <tt>get_state_synchronize_rcu</tt> and
+<tt>cond_synchronize_rcu()</tt> has appeared quite recently,
+so it is too early to tell whether they will stand the test of time.
+
+<p>
+RCU thus provides a range of tools to allow updaters to strike the
+required tradeoff between latency, flexibility and CPU overhead.
+
+<h3><a name="Composability">Composability</a></h3>
+
+<p>
+Composability has received much attention in recent years, perhaps in part
+due to the collision of multicore hardware with object-oriented techniques
+designed in single-threaded environments for single-threaded use.
+And in theory, RCU read-side critical sections may be composed, and in
+fact may be nested arbitrarily deeply.
+In practice, as with all real-world implementations of composable
+constructs, there are limitations.
+
+<p>
+Implementations of RCU for which <tt>rcu_read_lock()</tt>
+and <tt>rcu_read_unlock()</tt> generate no code, such as
+Linux-kernel RCU when <tt>CONFIG_PREEMPT=n</tt>, can be
+nested arbitrarily deeply.
+After all, there is no overhead.
+Except that if all these instances of <tt>rcu_read_lock()</tt>
+and <tt>rcu_read_unlock()</tt> are visible to the compiler,
+compilation will eventually fail due to exhausting memory,
+mass storage, or user patience, whichever comes first.
+If the nesting is not visible to the compiler, as is the case with
+mutually recursive functions each in its own translation unit,
+stack overflow will result.
+If the nesting takes the form of loops, either the control variable
+will overflow or (in the Linux kernel) you will get an RCU CPU stall warning.
+Nevertheless, this class of RCU implementations is one
+of the most composable constructs in existence.
+
+<p>
+RCU implementations that explicitly track nesting depth
+are limited by the nesting-depth counter.
+For example, the Linux kernel's preemptible RCU limits nesting to
+<tt>INT_MAX</tt>.
+This should suffice for almost all practical purposes.
+That said, a consecutive pair of RCU read-side critical sections
+between which there is an operation that waits for a grace period
+cannot be enclosed in another RCU read-side critical section.
+This is because it is not legal to wait for a grace period within
+an RCU read-side critical section: To do so would result either
+in deadlock or
+in RCU implicitly splitting the enclosing RCU read-side critical
+section, neither of which is conducive to a long-lived and prosperous
+kernel.
+
+<p>
+It is worth noting that RCU is not alone in limiting composability.
+For example, many transactional-memory implementations prohibit
+composing a pair of transactions separated by an irrevocable
+operation (for example, a network receive operation).
+For another example, lock-based critical sections can be composed
+surprisingly freely, but only if deadlock is avoided.
+
+<p>
+In short, although RCU read-side critical sections are highly composable,
+care is required in some situations, just as is the case for any other
+composable synchronization mechanism.
+
+<h3><a name="Corner Cases">Corner Cases</a></h3>
+
+<p>
+A given RCU workload might have an endless and intense stream of
+RCU read-side critical sections, perhaps even so intense that there
+was never a point in time during which there was not at least one
+RCU read-side critical section in flight.
+RCU cannot allow this situation to block grace periods: As long as
+all the RCU read-side critical sections are finite, grace periods
+must also be finite.
+
+<p>
+That said, preemptible RCU implementations could potentially result
+in RCU read-side critical sections being preempted for long durations,
+which has the effect of creating a long-duration RCU read-side
+critical section.
+This situation can arise only in heavily loaded systems, but systems using
+real-time priorities are of course more vulnerable.
+Therefore, RCU priority boosting is provided to help deal with this
+case.
+That said, the exact requirements on RCU priority boosting will likely
+evolve as more experience accumulates.
+
+<p>
+Other workloads might have very high update rates.
+Although one can argue that such workloads should instead use
+something other than RCU, the fact remains that RCU must
+handle such workloads gracefully.
+This requirement is another factor driving batching of grace periods,
+but it is also the driving force behind the checks for large numbers
+of queued RCU callbacks in the <tt>call_rcu()</tt> code path.
+Finally, high update rates should not delay RCU read-side critical
+sections, although some read-side delays can occur when using
+<tt>synchronize_rcu_expedited()</tt>, courtesy of this function's use
+of <tt>try_stop_cpus()</tt>.
+(In the future, <tt>synchronize_rcu_expedited()</tt> will be
+converted to use lighter-weight inter-processor interrupts (IPIs),
+but this will still disturb readers, though to a much smaller degree.)
+
+<p>
+Although all three of these corner cases were understood in the early
+1990s, a simple user-level test consisting of <tt>close(open(path))</tt>
+in a tight loop
+in the early 2000s suddenly provided a much deeper appreciation of the
+high-update-rate corner case.
+This test also motivated addition of some RCU code to react to high update
+rates, for example, if a given CPU finds itself with more than 10,000
+RCU callbacks queued, it will cause RCU to take evasive action by
+more aggressively starting grace periods and more aggressively forcing
+completion of grace-period processing.
+This evasive action causes the grace period to complete more quickly,
+but at the cost of restricting RCU's batching optimizations, thus
+increasing the CPU overhead incurred by that grace period.
+
+<h2><a name="Software-Engineering Requirements">
+Software-Engineering Requirements</a></h2>
+
+<p>
+Between Murphy's Law and &ldquo;To err is human&rdquo;, it is necessary to
+guard against mishaps and misuse:
+
+<ol>
+<li> It is all too easy to forget to use <tt>rcu_read_lock()</tt>
+ everywhere that it is needed, so kernels built with
+ <tt>CONFIG_PROVE_RCU=y</tt> will spat if
+ <tt>rcu_dereference()</tt> is used outside of an
+ RCU read-side critical section.
+ Update-side code can use <tt>rcu_dereference_protected()</tt>,
+ which takes a
+ <a href="https://lwn.net/Articles/371986/">lockdep expression</a>
+ to indicate what is providing the protection.
+ If the indicated protection is not provided, a lockdep splat
+ is emitted.
+
+ <p>
+ Code shared between readers and updaters can use
+ <tt>rcu_dereference_check()</tt>, which also takes a
+ lockdep expression, and emits a lockdep splat if neither
+ <tt>rcu_read_lock()</tt> nor the indicated protection
+ is in place.
+ In addition, <tt>rcu_dereference_raw()</tt> is used in those
+ (hopefully rare) cases where the required protection cannot
+ be easily described.
+ Finally, <tt>rcu_read_lock_held()</tt> is provided to
+ allow a function to verify that it has been invoked within
+ an RCU read-side critical section.
+ I was made aware of this set of requirements shortly after Thomas
+ Gleixner audited a number of RCU uses.
+<li> A given function might wish to check for RCU-related preconditions
+ upon entry, before using any other RCU API.
+ The <tt>rcu_lockdep_assert()</tt> does this job,
+ asserting the expression in kernels having lockdep enabled
+ and doing nothing otherwise.
+<li> It is also easy to forget to use <tt>rcu_assign_pointer()</tt>
+ and <tt>rcu_dereference()</tt>, perhaps (incorrectly)
+ substituting a simple assignment.
+ To catch this sort of error, a given RCU-protected pointer may be
+ tagged with <tt>__rcu</tt>, after which running sparse
+ with <tt>CONFIG_SPARSE_RCU_POINTER=y</tt> will complain
+ about simple-assignment accesses to that pointer.
+ Arnd Bergmann made me aware of this requirement, and also
+ supplied the needed
+ <a href="https://lwn.net/Articles/376011/">patch series</a>.
+<li> Kernels built with <tt>CONFIG_DEBUG_OBJECTS_RCU_HEAD=y</tt>
+ will splat if a data element is passed to <tt>call_rcu()</tt>
+ twice in a row, without a grace period in between.
+ (This error is similar to a double free.)
+ The corresponding <tt>rcu_head</tt> structures that are
+ dynamically allocated are automatically tracked, but
+ <tt>rcu_head</tt> structures allocated on the stack
+ must be initialized with <tt>init_rcu_head_on_stack()</tt>
+ and cleaned up with <tt>destroy_rcu_head_on_stack()</tt>.
+ Similarly, statically allocated non-stack <tt>rcu_head</tt>
+ structures must be initialized with <tt>init_rcu_head()</tt>
+ and cleaned up with <tt>destroy_rcu_head()</tt>.
+ Mathieu Desnoyers made me aware of this requirement, and also
+ supplied the needed
+ <a href="https://lkml.kernel.org/g/20100319013024.GA28456@Krystal">patch</a>.
+<li> An infinite loop in an RCU read-side critical section will
+ eventually trigger an RCU CPU stall warning splat, with
+ the duration of &ldquo;eventually&rdquo; being controlled by the
+ <tt>RCU_CPU_STALL_TIMEOUT</tt> <tt>Kconfig</tt> option, or,
+ alternatively, by the
+ <tt>rcupdate.rcu_cpu_stall_timeout</tt> boot/sysfs
+ parameter.
+ However, RCU is not obligated to produce this splat
+ unless there is a grace period waiting on that particular
+ RCU read-side critical section.
+ <p>
+ Some extreme workloads might intentionally delay
+ RCU grace periods, and systems running those workloads can
+ be booted with <tt>rcupdate.rcu_cpu_stall_suppress</tt>
+ to suppress the splats.
+ This kernel parameter may also be set via <tt>sysfs</tt>.
+ Furthermore, RCU CPU stall warnings are counter-productive
+ during sysrq dumps and during panics.
+ RCU therefore supplies the <tt>rcu_sysrq_start()</tt> and
+ <tt>rcu_sysrq_end()</tt> API members to be called before
+ and after long sysrq dumps.
+ RCU also supplies the <tt>rcu_panic()</tt> notifier that is
+ automatically invoked at the beginning of a panic to suppress
+ further RCU CPU stall warnings.
+
+ <p>
+ This requirement made itself known in the early 1990s, pretty
+ much the first time that it was necessary to debug a CPU stall.
+ That said, the initial implementation in DYNIX/ptx was quite
+ generic in comparison with that of Linux.
+<li> Although it would be very good to detect pointers leaking out
+ of RCU read-side critical sections, there is currently no
+ good way of doing this.
+ One complication is the need to distinguish between pointers
+ leaking and pointers that have been handed off from RCU to
+ some other synchronization mechanism, for example, reference
+ counting.
+<li> In kernels built with <tt>CONFIG_RCU_TRACE=y</tt>, RCU-related
+ information is provided via both debugfs and event tracing.
+<li> Open-coded use of <tt>rcu_assign_pointer()</tt> and
+ <tt>rcu_dereference()</tt> to create typical linked
+ data structures can be surprisingly error-prone.
+ Therefore, RCU-protected
+ <a href="https://lwn.net/Articles/609973/#RCU List APIs">linked lists</a>
+ and, more recently, RCU-protected
+ <a href="https://lwn.net/Articles/612100/">hash tables</a>
+ are available.
+ Many other special-purpose RCU-protected data structures are
+ available in the Linux kernel and the userspace RCU library.
+<li> Some linked structures are created at compile time, but still
+ require <tt>__rcu</tt> checking.
+ The <tt>RCU_POINTER_INITIALIZER()</tt> macro serves this
+ purpose.
+<li> It is not necessary to use <tt>rcu_assign_pointer()</tt>
+ when creating linked structures that are to be published via
+ a single external pointer.
+ The <tt>RCU_INIT_POINTER()</tt> macro is provided for
+ this task and also for assigning <tt>NULL</tt> pointers
+ at runtime.
+</ol>
+
+<p>
+This not a hard-and-fast list: RCU's diagnostic capabilities will
+continue to be guided by the number and type of usage bugs found
+in real-world RCU usage.
+
+<h2><a name="Linux Kernel Complications">Linux Kernel Complications</a></h2>
+
+<p>
+The Linux kernel provides an interesting environment for all kinds of
+software, including RCU.
+Some of the relevant points of interest are as follows:
+
+<ol>
+<li> <a href="#Configuration">Configuration</a>.
+<li> <a href="#Firmware Interface">Firmware Interface</a>.
+<li> <a href="#Early Boot">Early Boot</a>.
+<li> <a href="#Interrupts and NMIs">
+ Interrupts and non-maskable interrupts (NMIs)</a>.
+<li> <a href="#Loadable Modules">Loadable Modules</a>.
+<li> <a href="#Hotplug CPU">Hotplug CPU</a>.
+<li> <a href="#Scheduler and RCU">Scheduler and RCU</a>.
+<li> <a href="#Tracing and RCU">Tracing and RCU</a>.
+<li> <a href="#Energy Efficiency">Energy Efficiency</a>.
+<li> <a href="#Memory Efficiency">Memory Efficiency</a>.
+<li> <a href="#Performance, Scalability, Response Time, and Reliability">
+ Performance, Scalability, Response Time, and Reliability</a>.
+</ol>
+
+<p>
+This list is probably incomplete, but it does give a feel for the
+most notable Linux-kernel complications.
+Each of the following sections covers one of the above topics.
+
+<h3><a name="Configuration">Configuration</a></h3>
+
+<p>
+RCU's goal is automatic configuration, so that almost nobody
+needs to worry about RCU's <tt>Kconfig</tt> options.
+And for almost all users, RCU does in fact work well
+&ldquo;out of the box.&rdquo;
+
+<p>
+However, there are specialized use cases that are handled by
+kernel boot parameters and <tt>Kconfig</tt> options.
+Unfortunately, the <tt>Kconfig</tt> system will explicitly ask users
+about new <tt>Kconfig</tt> options, which requires almost all of them
+be hidden behind a <tt>CONFIG_RCU_EXPERT</tt> <tt>Kconfig</tt> option.
+
+<p>
+This all should be quite obvious, but the fact remains that
+Linus Torvalds recently had to
+<a href="https://lkml.kernel.org/g/CA+55aFy4wcCwaL4okTs8wXhGZ5h-ibecy_Meg9C4MNQrUnwMcg@mail.gmail.com">remind</a>
+me of this requirement.
+
+<h3><a name="Firmware Interface">Firmware Interface</a></h3>
+
+<p>
+In many cases, kernel obtains information about the system from the
+firmware, and sometimes things are lost in translation.
+Or the translation is accurate, but the original message is bogus.
+
+<p>
+For example, some systems' firmware overreports the number of CPUs,
+sometimes by a large factor.
+If RCU naively believed the firmware, as it used to do,
+it would create too many per-CPU kthreads.
+Although the resulting system will still run correctly, the extra
+kthreads needlessly consume memory and can cause confusion
+when they show up in <tt>ps</tt> listings.
+
+<p>
+RCU must therefore wait for a given CPU to actually come online before
+it can allow itself to believe that the CPU actually exists.
+The resulting &ldquo;ghost CPUs&rdquo; (which are never going to
+come online) cause a number of
+<a href="https://paulmck.livejournal.com/37494.html">interesting complications</a>.
+
+<h3><a name="Early Boot">Early Boot</a></h3>
+
+<p>
+The Linux kernel's boot sequence is an interesting process,
+and RCU is used early, even before <tt>rcu_init()</tt>
+is invoked.
+In fact, a number of RCU's primitives can be used as soon as the
+initial task's <tt>task_struct</tt> is available and the
+boot CPU's per-CPU variables are set up.
+The read-side primitives (<tt>rcu_read_lock()</tt>,
+<tt>rcu_read_unlock()</tt>, <tt>rcu_dereference()</tt>,
+and <tt>rcu_access_pointer()</tt>) will operate normally very early on,
+as will <tt>rcu_assign_pointer()</tt>.
+
+<p>
+Although <tt>call_rcu()</tt> may be invoked at any
+time during boot, callbacks are not guaranteed to be invoked until after
+the scheduler is fully up and running.
+This delay in callback invocation is due to the fact that RCU does not
+invoke callbacks until it is fully initialized, and this full initialization
+cannot occur until after the scheduler has initialized itself to the
+point where RCU can spawn and run its kthreads.
+In theory, it would be possible to invoke callbacks earlier,
+however, this is not a panacea because there would be severe restrictions
+on what operations those callbacks could invoke.
+
+<p>
+Perhaps surprisingly, <tt>synchronize_rcu()</tt>,
+<a href="#Bottom-Half Flavor"><tt>synchronize_rcu_bh()</tt></a>
+(<a href="#Bottom-Half Flavor">discussed below</a>),
+and
+<a href="#Sched Flavor"><tt>synchronize_sched()</tt></a>
+will all operate normally
+during very early boot, the reason being that there is only one CPU
+and preemption is disabled.
+This means that the call <tt>synchronize_rcu()</tt> (or friends)
+itself is a quiescent
+state and thus a grace period, so the early-boot implementation can
+be a no-op.
+
+<p>
+Both <tt>synchronize_rcu_bh()</tt> and <tt>synchronize_sched()</tt>
+continue to operate normally through the remainder of boot, courtesy
+of the fact that preemption is disabled across their RCU read-side
+critical sections and also courtesy of the fact that there is still
+only one CPU.
+However, once the scheduler starts initializing, preemption is enabled.
+There is still only a single CPU, but the fact that preemption is enabled
+means that the no-op implementation of <tt>synchronize_rcu()</tt> no
+longer works in <tt>CONFIG_PREEMPT=y</tt> kernels.
+Therefore, as soon as the scheduler starts initializing, the early-boot
+fastpath is disabled.
+This means that <tt>synchronize_rcu()</tt> switches to its runtime
+mode of operation where it posts callbacks, which in turn means that
+any call to <tt>synchronize_rcu()</tt> will block until the corresponding
+callback is invoked.
+Unfortunately, the callback cannot be invoked until RCU's runtime
+grace-period machinery is up and running, which cannot happen until
+the scheduler has initialized itself sufficiently to allow RCU's
+kthreads to be spawned.
+Therefore, invoking <tt>synchronize_rcu()</tt> during scheduler
+initialization can result in deadlock.
+
+<p>@@QQ@@
+So what happens with <tt>synchronize_rcu()</tt> during
+scheduler initialization for <tt>CONFIG_PREEMPT=n</tt>
+kernels?
+<p>@@QQA@@
+In <tt>CONFIG_PREEMPT=n</tt> kernel, <tt>synchronize_rcu()</tt>
+maps directly to <tt>synchronize_sched()</tt>.
+Therefore, <tt>synchronize_rcu()</tt> works normally throughout
+boot in <tt>CONFIG_PREEMPT=n</tt> kernels.
+However, your code must also work in <tt>CONFIG_PREEMPT=y</tt> kernels,
+so it is still necessary to avoid invoking <tt>synchronize_rcu()</tt>
+during scheduler initialization.
+<p>@@QQE@@
+
+<p>
+I learned of these boot-time requirements as a result of a series of
+system hangs.
+
+<h3><a name="Interrupts and NMIs">Interrupts and NMIs</a></h3>
+
+<p>
+The Linux kernel has interrupts, and RCU read-side critical sections are
+legal within interrupt handlers and within interrupt-disabled regions
+of code, as are invocations of <tt>call_rcu()</tt>.
+
+<p>
+Some Linux-kernel architectures can enter an interrupt handler from
+non-idle process context, and then just never leave it, instead stealthily
+transitioning back to process context.
+This trick is sometimes used to invoke system calls from inside the kernel.
+These &ldquo;half-interrupts&rdquo; mean that RCU has to be very careful
+about how it counts interrupt nesting levels.
+I learned of this requirement the hard way during a rewrite
+of RCU's dyntick-idle code.
+
+<p>
+The Linux kernel has non-maskable interrupts (NMIs), and
+RCU read-side critical sections are legal within NMI handlers.
+Thankfully, RCU update-side primitives, including
+<tt>call_rcu()</tt>, are prohibited within NMI handlers.
+
+<p>
+The name notwithstanding, some Linux-kernel architectures
+can have nested NMIs, which RCU must handle correctly.
+Andy Lutomirski
+<a href="https://lkml.kernel.org/g/CALCETrXLq1y7e_dKFPgou-FKHB6Pu-r8+t-6Ds+8=va7anBWDA@mail.gmail.com">surprised me</a>
+with this requirement;
+he also kindly surprised me with
+<a href="https://lkml.kernel.org/g/CALCETrXSY9JpW3uE6H8WYk81sg56qasA2aqmjMPsq5dOtzso=g@mail.gmail.com">an algorithm</a>
+that meets this requirement.
+
+<h3><a name="Loadable Modules">Loadable Modules</a></h3>
+
+<p>
+The Linux kernel has loadable modules, and these modules can
+also be unloaded.
+After a given module has been unloaded, any attempt to call
+one of its functions results in a segmentation fault.
+The module-unload functions must therefore cancel any
+delayed calls to loadable-module functions, for example,
+any outstanding <tt>mod_timer()</tt> must be dealt with
+via <tt>del_timer_sync()</tt> or similar.
+
+<p>
+Unfortunately, there is no way to cancel an RCU callback;
+once you invoke <tt>call_rcu()</tt>, the callback function is
+going to eventually be invoked, unless the system goes down first.
+Because it is normally considered socially irresponsible to crash the system
+in response to a module unload request, we need some other way
+to deal with in-flight RCU callbacks.
+
+<p>
+RCU therefore provides
+<tt><a href="https://lwn.net/Articles/217484/">rcu_barrier()</a></tt>,
+which waits until all in-flight RCU callbacks have been invoked.
+If a module uses <tt>call_rcu()</tt>, its exit function should therefore
+prevent any future invocation of <tt>call_rcu()</tt>, then invoke
+<tt>rcu_barrier()</tt>.
+In theory, the underlying module-unload code could invoke
+<tt>rcu_barrier()</tt> unconditionally, but in practice this would
+incur unacceptable latencies.
+
+<p>
+Nikita Danilov noted this requirement for an analogous filesystem-unmount
+situation, and Dipankar Sarma incorporated <tt>rcu_barrier()</tt> into RCU.
+The need for <tt>rcu_barrier()</tt> for module unloading became
+apparent later.
+
+<h3><a name="Hotplug CPU">Hotplug CPU</a></h3>
+
+<p>
+The Linux kernel supports CPU hotplug, which means that CPUs
+can come and go.
+It is of course illegal to use any RCU API member from an offline CPU.
+This requirement was present from day one in DYNIX/ptx, but
+on the other hand, the Linux kernel's CPU-hotplug implementation
+is &ldquo;interesting.&rdquo;
+
+<p>
+The Linux-kernel CPU-hotplug implementation has notifiers that
+are used to allow the various kernel subsystems (including RCU)
+to respond appropriately to a given CPU-hotplug operation.
+Most RCU operations may be invoked from CPU-hotplug notifiers,
+including even normal synchronous grace-period operations
+such as <tt>synchronize_rcu()</tt>.
+However, expedited grace-period operations such as
+<tt>synchronize_rcu_expedited()</tt> are not supported,
+due to the fact that current implementations block CPU-hotplug
+operations, which could result in deadlock.
+
+<p>
+In addition, all-callback-wait operations such as
+<tt>rcu_barrier()</tt> are also not supported, due to the
+fact that there are phases of CPU-hotplug operations where
+the outgoing CPU's callbacks will not be invoked until after
+the CPU-hotplug operation ends, which could also result in deadlock.
+
+<h3><a name="Scheduler and RCU">Scheduler and RCU</a></h3>
+
+<p>
+RCU depends on the scheduler, and the scheduler uses RCU to
+protect some of its data structures.
+This means the scheduler is forbidden from acquiring
+the runqueue locks and the priority-inheritance locks
+in the middle of an outermost RCU read-side critical section unless either
+(1)&nbsp;it releases them before exiting that same
+RCU read-side critical section, or
+(2)&nbsp;interrupts are disabled across
+that entire RCU read-side critical section.
+This same prohibition also applies (recursively!) to any lock that is acquired
+while holding any lock to which this prohibition applies.
+Adhering to this rule prevents preemptible RCU from invoking
+<tt>rcu_read_unlock_special()</tt> while either runqueue or
+priority-inheritance locks are held, thus avoiding deadlock.
+
+<p>
+Prior to v4.4, it was only necessary to disable preemption across
+RCU read-side critical sections that acquired scheduler locks.
+In v4.4, expedited grace periods started using IPIs, and these
+IPIs could force a <tt>rcu_read_unlock()</tt> to take the slowpath.
+Therefore, this expedited-grace-period change required disabling of
+interrupts, not just preemption.
+
+<p>
+For RCU's part, the preemptible-RCU <tt>rcu_read_unlock()</tt>
+implementation must be written carefully to avoid similar deadlocks.
+In particular, <tt>rcu_read_unlock()</tt> must tolerate an
+interrupt where the interrupt handler invokes both
+<tt>rcu_read_lock()</tt> and <tt>rcu_read_unlock()</tt>.
+This possibility requires <tt>rcu_read_unlock()</tt> to use
+negative nesting levels to avoid destructive recursion via
+interrupt handler's use of RCU.
+
+<p>
+This pair of mutual scheduler-RCU requirements came as a
+<a href="https://lwn.net/Articles/453002/">complete surprise</a>.
+
+<p>
+As noted above, RCU makes use of kthreads, and it is necessary to
+avoid excessive CPU-time accumulation by these kthreads.
+This requirement was no surprise, but RCU's violation of it
+when running context-switch-heavy workloads when built with
+<tt>CONFIG_NO_HZ_FULL=y</tt>
+<a href="http://www.rdrop.com/users/paulmck/scalability/paper/BareMetal.2015.01.15b.pdf">did come as a surprise [PDF]</a>.
+RCU has made good progress towards meeting this requirement, even
+for context-switch-have <tt>CONFIG_NO_HZ_FULL=y</tt> workloads,
+but there is room for further improvement.
+
+<h3><a name="Tracing and RCU">Tracing and RCU</a></h3>
+
+<p>
+It is possible to use tracing on RCU code, but tracing itself
+uses RCU.
+For this reason, <tt>rcu_dereference_raw_notrace()</tt>
+is provided for use by tracing, which avoids the destructive
+recursion that could otherwise ensue.
+This API is also used by virtualization in some architectures,
+where RCU readers execute in environments in which tracing
+cannot be used.
+The tracing folks both located the requirement and provided the
+needed fix, so this surprise requirement was relatively painless.
+
+<h3><a name="Energy Efficiency">Energy Efficiency</a></h3>
+
+<p>
+Interrupting idle CPUs is considered socially unacceptable,
+especially by people with battery-powered embedded systems.
+RCU therefore conserves energy by detecting which CPUs are
+idle, including tracking CPUs that have been interrupted from idle.
+This is a large part of the energy-efficiency requirement,
+so I learned of this via an irate phone call.
+
+<p>
+Because RCU avoids interrupting idle CPUs, it is illegal to
+execute an RCU read-side critical section on an idle CPU.
+(Kernels built with <tt>CONFIG_PROVE_RCU=y</tt> will splat
+if you try it.)
+The <tt>RCU_NONIDLE()</tt> macro and <tt>_rcuidle</tt>
+event tracing is provided to work around this restriction.
+In addition, <tt>rcu_is_watching()</tt> may be used to
+test whether or not it is currently legal to run RCU read-side
+critical sections on this CPU.
+I learned of the need for diagnostics on the one hand
+and <tt>RCU_NONIDLE()</tt> on the other while inspecting
+idle-loop code.
+Steven Rostedt supplied <tt>_rcuidle</tt> event tracing,
+which is used quite heavily in the idle loop.
+
+<p>
+It is similarly socially unacceptable to interrupt an
+<tt>nohz_full</tt> CPU running in userspace.
+RCU must therefore track <tt>nohz_full</tt> userspace
+execution.
+And in
+<a href="https://lwn.net/Articles/558284/"><tt>CONFIG_NO_HZ_FULL_SYSIDLE=y</tt></a>
+kernels, RCU must separately track idle CPUs on the one hand and
+CPUs that are either idle or executing in userspace on the other.
+In both cases, RCU must be able to sample state at two points in
+time, and be able to determine whether or not some other CPU spent
+any time idle and/or executing in userspace.
+
+<p>
+These energy-efficiency requirements have proven quite difficult to
+understand and to meet, for example, there have been more than five
+clean-sheet rewrites of RCU's energy-efficiency code, the last of
+which was finally able to demonstrate
+<a href="http://www.rdrop.com/users/paulmck/realtime/paper/AMPenergy.2013.04.19a.pdf">real energy savings running on real hardware [PDF]</a>.
+As noted earlier,
+I learned of many of these requirements via angry phone calls:
+Flaming me on the Linux-kernel mailing list was apparently not
+sufficient to fully vent their ire at RCU's energy-efficiency bugs!
+
+<h3><a name="Memory Efficiency">Memory Efficiency</a></h3>
+
+<p>
+Although small-memory non-realtime systems can simply use Tiny RCU,
+code size is only one aspect of memory efficiency.
+Another aspect is the size of the <tt>rcu_head</tt> structure
+used by <tt>call_rcu()</tt> and <tt>kfree_rcu()</tt>.
+Although this structure contains nothing more than a pair of pointers,
+it does appear in many RCU-protected data structures, including
+some that are size critical.
+The <tt>page</tt> structure is a case in point, as evidenced by
+the many occurrences of the <tt>union</tt> keyword within that structure.
+
+<p>
+This need for memory efficiency is one reason that RCU uses hand-crafted
+singly linked lists to track the <tt>rcu_head</tt> structures that
+are waiting for a grace period to elapse.
+It is also the reason why <tt>rcu_head</tt> structures do not contain
+debug information, such as fields tracking the file and line of the
+<tt>call_rcu()</tt> or <tt>kfree_rcu()</tt> that posted them.
+Although this information might appear in debug-only kernel builds at some
+point, in the meantime, the <tt>-&gt;func</tt> field will often provide
+the needed debug information.
+
+<p>
+However, in some cases, the need for memory efficiency leads to even
+more extreme measures.
+Returning to the <tt>page</tt> structure, the <tt>rcu_head</tt> field
+shares storage with a great many other structures that are used at
+various points in the corresponding page's lifetime.
+In order to correctly resolve certain
+<a href="https://lkml.kernel.org/g/1439976106-137226-1-git-send-email-kirill.shutemov@linux.intel.com">race conditions</a>,
+the Linux kernel's memory-management subsystem needs a particular bit
+to remain zero during all phases of grace-period processing,
+and that bit happens to map to the bottom bit of the
+<tt>rcu_head</tt> structure's <tt>-&gt;next</tt> field.
+RCU makes this guarantee as long as <tt>call_rcu()</tt>
+is used to post the callback, as opposed to <tt>kfree_rcu()</tt>
+or some future &ldquo;lazy&rdquo;
+variant of <tt>call_rcu()</tt> that might one day be created for
+energy-efficiency purposes.
+
+<h3><a name="Performance, Scalability, Response Time, and Reliability">
+Performance, Scalability, Response Time, and Reliability</a></h3>
+
+<p>
+Expanding on the
+<a href="#Performance and Scalability">earlier discussion</a>,
+RCU is used heavily by hot code paths in performance-critical
+portions of the Linux kernel's networking, security, virtualization,
+and scheduling code paths.
+RCU must therefore use efficient implementations, especially in its
+read-side primitives.
+To that end, it would be good if preemptible RCU's implementation
+of <tt>rcu_read_lock()</tt> could be inlined, however, doing
+this requires resolving <tt>#include</tt> issues with the
+<tt>task_struct</tt> structure.
+
+<p>
+The Linux kernel supports hardware configurations with up to
+4096 CPUs, which means that RCU must be extremely scalable.
+Algorithms that involve frequent acquisitions of global locks or
+frequent atomic operations on global variables simply cannot be
+tolerated within the RCU implementation.
+RCU therefore makes heavy use of a combining tree based on the
+<tt>rcu_node</tt> structure.
+RCU is required to tolerate all CPUs continuously invoking any
+combination of RCU's runtime primitives with minimal per-operation
+overhead.
+In fact, in many cases, increasing load must <i>decrease</i> the
+per-operation overhead, witness the batching optimizations for
+<tt>synchronize_rcu()</tt>, <tt>call_rcu()</tt>,
+<tt>synchronize_rcu_expedited()</tt>, and <tt>rcu_barrier()</tt>.
+As a general rule, RCU must cheerfully accept whatever the
+rest of the Linux kernel decides to throw at it.
+
+<p>
+The Linux kernel is used for real-time workloads, especially
+in conjunction with the
+<a href="https://rt.wiki.kernel.org/index.php/Main_Page">-rt patchset</a>.
+The real-time-latency response requirements are such that the
+traditional approach of disabling preemption across RCU
+read-side critical sections is inappropriate.
+Kernels built with <tt>CONFIG_PREEMPT=y</tt> therefore
+use an RCU implementation that allows RCU read-side critical
+sections to be preempted.
+This requirement made its presence known after users made it
+clear that an earlier
+<a href="https://lwn.net/Articles/107930/">real-time patch</a>
+did not meet their needs, in conjunction with some
+<a href="https://lkml.kernel.org/g/20050318002026.GA2693@us.ibm.com">RCU issues</a>
+encountered by a very early version of the -rt patchset.
+
+<p>
+In addition, RCU must make do with a sub-100-microsecond real-time latency
+budget.
+In fact, on smaller systems with the -rt patchset, the Linux kernel
+provides sub-20-microsecond real-time latencies for the whole kernel,
+including RCU.
+RCU's scalability and latency must therefore be sufficient for
+these sorts of configurations.
+To my surprise, the sub-100-microsecond real-time latency budget
+<a href="http://www.rdrop.com/users/paulmck/realtime/paper/bigrt.2013.01.31a.LCA.pdf">
+applies to even the largest systems [PDF]</a>,
+up to and including systems with 4096 CPUs.
+This real-time requirement motivated the grace-period kthread, which
+also simplified handling of a number of race conditions.
+
+<p>
+Finally, RCU's status as a synchronization primitive means that
+any RCU failure can result in arbitrary memory corruption that can be
+extremely difficult to debug.
+This means that RCU must be extremely reliable, which in
+practice also means that RCU must have an aggressive stress-test
+suite.
+This stress-test suite is called <tt>rcutorture</tt>.
+
+<p>
+Although the need for <tt>rcutorture</tt> was no surprise,
+the current immense popularity of the Linux kernel is posing
+interesting&mdash;and perhaps unprecedented&mdash;validation
+challenges.
+To see this, keep in mind that there are well over one billion
+instances of the Linux kernel running today, given Android
+smartphones, Linux-powered televisions, and servers.
+This number can be expected to increase sharply with the advent of
+the celebrated Internet of Things.
+
+<p>
+Suppose that RCU contains a race condition that manifests on average
+once per million years of runtime.
+This bug will be occurring about three times per <i>day</i> across
+the installed base.
+RCU could simply hide behind hardware error rates, given that no one
+should really expect their smartphone to last for a million years.
+However, anyone taking too much comfort from this thought should
+consider the fact that in most jurisdictions, a successful multi-year
+test of a given mechanism, which might include a Linux kernel,
+suffices for a number of types of safety-critical certifications.
+In fact, rumor has it that the Linux kernel is already being used
+in production for safety-critical applications.
+I don't know about you, but I would feel quite bad if a bug in RCU
+killed someone.
+Which might explain my recent focus on validation and verification.
+
+<h2><a name="Other RCU Flavors">Other RCU Flavors</a></h2>
+
+<p>
+One of the more surprising things about RCU is that there are now
+no fewer than five <i>flavors</i>, or API families.
+In addition, the primary flavor that has been the sole focus up to
+this point has two different implementations, non-preemptible and
+preemptible.
+The other four flavors are listed below, with requirements for each
+described in a separate section.
+
+<ol>
+<li> <a href="#Bottom-Half Flavor">Bottom-Half Flavor</a>
+<li> <a href="#Sched Flavor">Sched Flavor</a>
+<li> <a href="#Sleepable RCU">Sleepable RCU</a>
+<li> <a href="#Tasks RCU">Tasks RCU</a>
+</ol>
+
+<h3><a name="Bottom-Half Flavor">Bottom-Half Flavor</a></h3>
+
+<p>
+The softirq-disable (AKA &ldquo;bottom-half&rdquo;,
+hence the &ldquo;_bh&rdquo; abbreviations)
+flavor of RCU, or <i>RCU-bh</i>, was developed by
+Dipankar Sarma to provide a flavor of RCU that could withstand the
+network-based denial-of-service attacks researched by Robert
+Olsson.
+These attacks placed so much networking load on the system
+that some of the CPUs never exited softirq execution,
+which in turn prevented those CPUs from ever executing a context switch,
+which, in the RCU implementation of that time, prevented grace periods
+from ever ending.
+The result was an out-of-memory condition and a system hang.
+
+<p>
+The solution was the creation of RCU-bh, which does
+<tt>local_bh_disable()</tt>
+across its read-side critical sections, and which uses the transition
+from one type of softirq processing to another as a quiescent state
+in addition to context switch, idle, user mode, and offline.
+This means that RCU-bh grace periods can complete even when some of
+the CPUs execute in softirq indefinitely, thus allowing algorithms
+based on RCU-bh to withstand network-based denial-of-service attacks.
+
+<p>
+Because
+<tt>rcu_read_lock_bh()</tt> and <tt>rcu_read_unlock_bh()</tt>
+disable and re-enable softirq handlers, any attempt to start a softirq
+handlers during the
+RCU-bh read-side critical section will be deferred.
+In this case, <tt>rcu_read_unlock_bh()</tt>
+will invoke softirq processing, which can take considerable time.
+One can of course argue that this softirq overhead should be associated
+with the code following the RCU-bh read-side critical section rather
+than <tt>rcu_read_unlock_bh()</tt>, but the fact
+is that most profiling tools cannot be expected to make this sort
+of fine distinction.
+For example, suppose that a three-millisecond-long RCU-bh read-side
+critical section executes during a time of heavy networking load.
+There will very likely be an attempt to invoke at least one softirq
+handler during that three milliseconds, but any such invocation will
+be delayed until the time of the <tt>rcu_read_unlock_bh()</tt>.
+This can of course make it appear at first glance as if
+<tt>rcu_read_unlock_bh()</tt> was executing very slowly.
+
+<p>
+The
+<a href="https://lwn.net/Articles/609973/#RCU Per-Flavor API Table">RCU-bh API</a>
+includes
+<tt>rcu_read_lock_bh()</tt>,
+<tt>rcu_read_unlock_bh()</tt>,
+<tt>rcu_dereference_bh()</tt>,
+<tt>rcu_dereference_bh_check()</tt>,
+<tt>synchronize_rcu_bh()</tt>,
+<tt>synchronize_rcu_bh_expedited()</tt>,
+<tt>call_rcu_bh()</tt>,
+<tt>rcu_barrier_bh()</tt>, and
+<tt>rcu_read_lock_bh_held()</tt>.
+
+<h3><a name="Sched Flavor">Sched Flavor</a></h3>
+
+<p>
+Before preemptible RCU, waiting for an RCU grace period had the
+side effect of also waiting for all pre-existing interrupt
+and NMI handlers.
+However, there are legitimate preemptible-RCU implementations that
+do not have this property, given that any point in the code outside
+of an RCU read-side critical section can be a quiescent state.
+Therefore, <i>RCU-sched</i> was created, which follows &ldquo;classic&rdquo;
+RCU in that an RCU-sched grace period waits for for pre-existing
+interrupt and NMI handlers.
+In kernels built with <tt>CONFIG_PREEMPT=n</tt>, the RCU and RCU-sched
+APIs have identical implementations, while kernels built with
+<tt>CONFIG_PREEMPT=y</tt> provide a separate implementation for each.
+
+<p>
+Note well that in <tt>CONFIG_PREEMPT=y</tt> kernels,
+<tt>rcu_read_lock_sched()</tt> and <tt>rcu_read_unlock_sched()</tt>
+disable and re-enable preemption, respectively.
+This means that if there was a preemption attempt during the
+RCU-sched read-side critical section, <tt>rcu_read_unlock_sched()</tt>
+will enter the scheduler, with all the latency and overhead entailed.
+Just as with <tt>rcu_read_unlock_bh()</tt>, this can make it look
+as if <tt>rcu_read_unlock_sched()</tt> was executing very slowly.
+However, the highest-priority task won't be preempted, so that task
+will enjoy low-overhead <tt>rcu_read_unlock_sched()</tt> invocations.
+
+<p>
+The
+<a href="https://lwn.net/Articles/609973/#RCU Per-Flavor API Table">RCU-sched API</a>
+includes
+<tt>rcu_read_lock_sched()</tt>,
+<tt>rcu_read_unlock_sched()</tt>,
+<tt>rcu_read_lock_sched_notrace()</tt>,
+<tt>rcu_read_unlock_sched_notrace()</tt>,
+<tt>rcu_dereference_sched()</tt>,
+<tt>rcu_dereference_sched_check()</tt>,
+<tt>synchronize_sched()</tt>,
+<tt>synchronize_rcu_sched_expedited()</tt>,
+<tt>call_rcu_sched()</tt>,
+<tt>rcu_barrier_sched()</tt>, and
+<tt>rcu_read_lock_sched_held()</tt>.
+However, anything that disables preemption also marks an RCU-sched
+read-side critical section, including
+<tt>preempt_disable()</tt> and <tt>preempt_enable()</tt>,
+<tt>local_irq_save()</tt> and <tt>local_irq_restore()</tt>,
+and so on.
+
+<h3><a name="Sleepable RCU">Sleepable RCU</a></h3>
+
+<p>
+For well over a decade, someone saying &ldquo;I need to block within
+an RCU read-side critical section&rdquo; was a reliable indication
+that this someone did not understand RCU.
+After all, if you are always blocking in an RCU read-side critical
+section, you can probably afford to use a higher-overhead synchronization
+mechanism.
+However, that changed with the advent of the Linux kernel's notifiers,
+whose RCU read-side critical
+sections almost never sleep, but sometimes need to.
+This resulted in the introduction of
+<a href="https://lwn.net/Articles/202847/">sleepable RCU</a>,
+or <i>SRCU</i>.
+
+<p>
+SRCU allows different domains to be defined, with each such domain
+defined by an instance of an <tt>srcu_struct</tt> structure.
+A pointer to this structure must be passed in to each SRCU function,
+for example, <tt>synchronize_srcu(&amp;ss)</tt>, where
+<tt>ss</tt> is the <tt>srcu_struct</tt> structure.
+The key benefit of these domains is that a slow SRCU reader in one
+domain does not delay an SRCU grace period in some other domain.
+That said, one consequence of these domains is that read-side code
+must pass a &ldquo;cookie&rdquo; from <tt>srcu_read_lock()</tt>
+to <tt>srcu_read_unlock()</tt>, for example, as follows:
+
+<blockquote>
+<pre>
+ 1 int idx;
+ 2
+ 3 idx = srcu_read_lock(&amp;ss);
+ 4 do_something();
+ 5 srcu_read_unlock(&amp;ss, idx);
+</pre>
+</blockquote>
+
+<p>
+As noted above, it is legal to block within SRCU read-side critical sections,
+however, with great power comes great responsibility.
+If you block forever in one of a given domain's SRCU read-side critical
+sections, then that domain's grace periods will also be blocked forever.
+Of course, one good way to block forever is to deadlock, which can
+happen if any operation in a given domain's SRCU read-side critical
+section can block waiting, either directly or indirectly, for that domain's
+grace period to elapse.
+For example, this results in a self-deadlock:
+
+<blockquote>
+<pre>
+ 1 int idx;
+ 2
+ 3 idx = srcu_read_lock(&amp;ss);
+ 4 do_something();
+ 5 synchronize_srcu(&amp;ss);
+ 6 srcu_read_unlock(&amp;ss, idx);
+</pre>
+</blockquote>
+
+<p>
+However, if line&nbsp;5 acquired a mutex that was held across
+a <tt>synchronize_srcu()</tt> for domain <tt>ss</tt>,
+deadlock would still be possible.
+Furthermore, if line&nbsp;5 acquired a mutex that was held across
+a <tt>synchronize_srcu()</tt> for some other domain <tt>ss1</tt>,
+and if an <tt>ss1</tt>-domain SRCU read-side critical section
+acquired another mutex that was held across as <tt>ss</tt>-domain
+<tt>synchronize_srcu()</tt>,
+deadlock would again be possible.
+Such a deadlock cycle could extend across an arbitrarily large number
+of different SRCU domains.
+Again, with great power comes great responsibility.
+
+<p>
+Unlike the other RCU flavors, SRCU read-side critical sections can
+run on idle and even offline CPUs.
+This ability requires that <tt>srcu_read_lock()</tt> and
+<tt>srcu_read_unlock()</tt> contain memory barriers, which means
+that SRCU readers will run a bit slower than would RCU readers.
+It also motivates the <tt>smp_mb__after_srcu_read_unlock()</tt>
+API, which, in combination with <tt>srcu_read_unlock()</tt>,
+guarantees a full memory barrier.
+
+<p>
+The
+<a href="https://lwn.net/Articles/609973/#RCU Per-Flavor API Table">SRCU API</a>
+includes
+<tt>srcu_read_lock()</tt>,
+<tt>srcu_read_unlock()</tt>,
+<tt>srcu_dereference()</tt>,
+<tt>srcu_dereference_check()</tt>,
+<tt>synchronize_srcu()</tt>,
+<tt>synchronize_srcu_expedited()</tt>,
+<tt>call_srcu()</tt>,
+<tt>srcu_barrier()</tt>, and
+<tt>srcu_read_lock_held()</tt>.
+It also includes
+<tt>DEFINE_SRCU()</tt>,
+<tt>DEFINE_STATIC_SRCU()</tt>, and
+<tt>init_srcu_struct()</tt>
+APIs for defining and initializing <tt>srcu_struct</tt> structures.
+
+<h3><a name="Tasks RCU">Tasks RCU</a></h3>
+
+<p>
+Some forms of tracing use &ldquo;tramopolines&rdquo; to handle the
+binary rewriting required to install different types of probes.
+It would be good to be able to free old trampolines, which sounds
+like a job for some form of RCU.
+However, because it is necessary to be able to install a trace
+anywhere in the code, it is not possible to use read-side markers
+such as <tt>rcu_read_lock()</tt> and <tt>rcu_read_unlock()</tt>.
+In addition, it does not work to have these markers in the trampoline
+itself, because there would need to be instructions following
+<tt>rcu_read_unlock()</tt>.
+Although <tt>synchronize_rcu()</tt> would guarantee that execution
+reached the <tt>rcu_read_unlock()</tt>, it would not be able to
+guarantee that execution had completely left the trampoline.
+
+<p>
+The solution, in the form of
+<a href="https://lwn.net/Articles/607117/"><i>Tasks RCU</i></a>,
+is to have implicit
+read-side critical sections that are delimited by voluntary context
+switches, that is, calls to <tt>schedule()</tt>,
+<tt>cond_resched_rcu_qs()</tt>, and
+<tt>synchronize_rcu_tasks()</tt>.
+In addition, transitions to and from userspace execution also delimit
+tasks-RCU read-side critical sections.
+
+<p>
+The tasks-RCU API is quite compact, consisting only of
+<tt>call_rcu_tasks()</tt>,
+<tt>synchronize_rcu_tasks()</tt>, and
+<tt>rcu_barrier_tasks()</tt>.
+
+<h2><a name="Possible Future Changes">Possible Future Changes</a></h2>
+
+<p>
+One of the tricks that RCU uses to attain update-side scalability is
+to increase grace-period latency with increasing numbers of CPUs.
+If this becomes a serious problem, it will be necessary to rework the
+grace-period state machine so as to avoid the need for the additional
+latency.
+
+<p>
+Expedited grace periods scan the CPUs, so their latency and overhead
+increases with increasing numbers of CPUs.
+If this becomes a serious problem on large systems, it will be necessary
+to do some redesign to avoid this scalability problem.
+
+<p>
+RCU disables CPU hotplug in a few places, perhaps most notably in the
+expedited grace-period and <tt>rcu_barrier()</tt> operations.
+If there is a strong reason to use expedited grace periods in CPU-hotplug
+notifiers, it will be necessary to avoid disabling CPU hotplug.
+This would introduce some complexity, so there had better be a <i>very</i>
+good reason.
+
+<p>
+The tradeoff between grace-period latency on the one hand and interruptions
+of other CPUs on the other hand may need to be re-examined.
+The desire is of course for zero grace-period latency as well as zero
+interprocessor interrupts undertaken during an expedited grace period
+operation.
+While this ideal is unlikely to be achievable, it is quite possible that
+further improvements can be made.
+
+<p>
+The multiprocessor implementations of RCU use a combining tree that
+groups CPUs so as to reduce lock contention and increase cache locality.
+However, this combining tree does not spread its memory across NUMA
+nodes nor does it align the CPU groups with hardware features such
+as sockets or cores.
+Such spreading and alignment is currently believed to be unnecessary
+because the hotpath read-side primitives do not access the combining
+tree, nor does <tt>call_rcu()</tt> in the common case.
+If you believe that your architecture needs such spreading and alignment,
+then your architecture should also benefit from the
+<tt>rcutree.rcu_fanout_leaf</tt> boot parameter, which can be set
+to the number of CPUs in a socket, NUMA node, or whatever.
+If the number of CPUs is too large, use a fraction of the number of
+CPUs.
+If the number of CPUs is a large prime number, well, that certainly
+is an &ldquo;interesting&rdquo; architectural choice!
+More flexible arrangements might be considered, but only if
+<tt>rcutree.rcu_fanout_leaf</tt> has proven inadequate, and only
+if the inadequacy has been demonstrated by a carefully run and
+realistic system-level workload.
+
+<p>
+Please note that arrangements that require RCU to remap CPU numbers will
+require extremely good demonstration of need and full exploration of
+alternatives.
+
+<p>
+There is an embarrassingly large number of flavors of RCU, and this
+number has been increasing over time.
+Perhaps it will be possible to combine some at some future date.
+
+<p>
+RCU's various kthreads are reasonably recent additions.
+It is quite likely that adjustments will be required to more gracefully
+handle extreme loads.
+It might also be necessary to be able to relate CPU utilization by
+RCU's kthreads and softirq handlers to the code that instigated this
+CPU utilization.
+For example, RCU callback overhead might be charged back to the
+originating <tt>call_rcu()</tt> instance, though probably not
+in production kernels.
+
+<h2><a name="Summary">Summary</a></h2>
+
+<p>
+This document has presented more than two decade's worth of RCU
+requirements.
+Given that the requirements keep changing, this will not be the last
+word on this subject, but at least it serves to get an important
+subset of the requirements set forth.
+
+<h2><a name="Acknowledgments">Acknowledgments</a></h2>
+
+I am grateful to Steven Rostedt, Lai Jiangshan, Ingo Molnar,
+Oleg Nesterov, Borislav Petkov, Peter Zijlstra, Boqun Feng, and
+Andy Lutomirski for their help in rendering
+this article human readable, and to Michelle Rankin for her support
+of this effort.
+Other contributions are acknowledged in the Linux kernel's git archive.
+The cartoon is copyright (c) 2013 by Melissa Broussard,
+and is provided
+under the terms of the Creative Commons Attribution-Share Alike 3.0
+United States license.
+
+<p>@@QQAL@@
+
+</body></html>
diff --git a/Documentation/RCU/Design/htmlqqz.sh b/Documentation/RCU/Design/htmlqqz.sh
new file mode 100755
index 000000000000..d354f069559b
--- /dev/null
+++ b/Documentation/RCU/Design/htmlqqz.sh
@@ -0,0 +1,108 @@
+#!/bin/sh
+#
+# Usage: sh htmlqqz.sh file
+#
+# Extracts and converts quick quizzes in a proto-HTML document file.htmlx.
+# Commands, all of which must be on a line by themselves:
+#
+# "<p>@@QQ@@": Start of a quick quiz.
+# "<p>@@QQA@@": Start of a quick-quiz answer.
+# "<p>@@QQE@@": End of a quick-quiz answer, and thus of the quick quiz.
+# "<p>@@QQAL@@": Place to put quick-quiz answer list.
+#
+# Places the result in file.html.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, you can access it online at
+# http://www.gnu.org/licenses/gpl-2.0.html.
+#
+# Copyright (c) 2013 Paul E. McKenney, IBM Corporation.
+
+fn=$1
+if test ! -r $fn.htmlx
+then
+ echo "Error: $fn.htmlx unreadable."
+ exit 1
+fi
+
+echo "<!-- DO NOT HAND EDIT. -->" > $fn.html
+echo "<!-- Instead, edit $fn.htmlx and run 'sh htmlqqz.sh $fn' -->" >> $fn.html
+awk < $fn.htmlx >> $fn.html '
+
+state == "" && $1 != "<p>@@QQ@@" && $1 != "<p>@@QQAL@@" {
+ print $0;
+ if ($0 ~ /^<p>@@QQ/)
+ print "Bad Quick Quiz command: " NR " (expected <p>@@QQ@@ or <p>@@QQAL@@)." > "/dev/stderr"
+ next;
+}
+
+state == "" && $1 == "<p>@@QQ@@" {
+ qqn++;
+ qqlineno = NR;
+ haveqq = 1;
+ state = "qq";
+ print "<p><a name=\"Quick Quiz " qqn "\"><b>Quick Quiz " qqn "</b>:</a>"
+ next;
+}
+
+state == "qq" && $1 != "<p>@@QQA@@" {
+ qq[qqn] = qq[qqn] $0 "\n";
+ print $0
+ if ($0 ~ /^<p>@@QQ/)
+ print "Bad Quick Quiz command: " NR ". (expected <p>@@QQA@@)" > "/dev/stderr"
+ next;
+}
+
+state == "qq" && $1 == "<p>@@QQA@@" {
+ state = "qqa";
+ print "<br><a href=\"#qq" qqn "answer\">Answer</a>"
+ next;
+}
+
+state == "qqa" && $1 != "<p>@@QQE@@" {
+ qqa[qqn] = qqa[qqn] $0 "\n";
+ if ($0 ~ /^<p>@@QQ/)
+ print "Bad Quick Quiz command: " NR " (expected <p>@@QQE@@)." > "/dev/stderr"
+ next;
+}
+
+state == "qqa" && $1 == "<p>@@QQE@@" {
+ state = "";
+ next;
+}
+
+state == "" && $1 == "<p>@@QQAL@@" {
+ haveqq = "";
+ print "<h3><a name=\"Answers to Quick Quizzes\">"
+ print "Answers to Quick Quizzes</a></h3>"
+ print "";
+ for (i = 1; i <= qqn; i++) {
+ print "<a name=\"qq" i "answer\"></a>"
+ print "<p><b>Quick Quiz " i "</b>:"
+ print qq[i];
+ print "";
+ print "</p><p><b>Answer</b>:"
+ print qqa[i];
+ print "";
+ print "</p><p><a href=\"#Quick%20Quiz%20" i "\"><b>Back to Quick Quiz " i "</b>.</a>"
+ print "";
+ }
+ next;
+}
+
+END {
+ if (state != "")
+ print "Unterminated Quick Quiz: " qqlineno "." > "/dev/stderr"
+ else if (haveqq)
+ print "Missing \"<p>@@QQAL@@\", no Quick Quiz." > "/dev/stderr"
+}'
diff --git a/Documentation/arm/keystone/Overview.txt b/Documentation/arm/keystone/Overview.txt
index f17bc4c9dff9..400c0c270d2e 100644
--- a/Documentation/arm/keystone/Overview.txt
+++ b/Documentation/arm/keystone/Overview.txt
@@ -49,24 +49,6 @@ specified through DTS. Following are the DTS used:-
The device tree documentation for the keystone machines are located at
Documentation/devicetree/bindings/arm/keystone/keystone.txt
-Known issues & workaround
--------------------------
-
-Some of the device drivers used on keystone are re-used from that from
-DaVinci and other TI SoCs. These device drivers may use clock APIs directly.
-Some of the keystone specific drivers such as netcp uses run time power
-management API instead to enable clock. As this API has limitations on
-keystone, following workaround is needed to boot Linux.
-
- Add 'clk_ignore_unused' to the bootargs env variable in u-boot. Otherwise
- clock frameworks will try to disable clocks that are unused and disable
- the hardware. This is because netcp related power domain and clock
- domains are enabled in u-boot as run time power management API currently
- doesn't enable clocks for netcp due to a limitation. This workaround is
- expected to be removed in the future when proper API support becomes
- available. Until then, this work around is needed.
-
-
Document Author
---------------
Murali Karicheri <m-karicheri2@ti.com>
diff --git a/Documentation/arm64/silicon-errata.txt b/Documentation/arm64/silicon-errata.txt
new file mode 100644
index 000000000000..58b71ddf9b60
--- /dev/null
+++ b/Documentation/arm64/silicon-errata.txt
@@ -0,0 +1,58 @@
+ Silicon Errata and Software Workarounds
+ =======================================
+
+Author: Will Deacon <will.deacon@arm.com>
+Date : 27 November 2015
+
+It is an unfortunate fact of life that hardware is often produced with
+so-called "errata", which can cause it to deviate from the architecture
+under specific circumstances. For hardware produced by ARM, these
+errata are broadly classified into the following categories:
+
+ Category A: A critical error without a viable workaround.
+ Category B: A significant or critical error with an acceptable
+ workaround.
+ Category C: A minor error that is not expected to occur under normal
+ operation.
+
+For more information, consult one of the "Software Developers Errata
+Notice" documents available on infocenter.arm.com (registration
+required).
+
+As far as Linux is concerned, Category B errata may require some special
+treatment in the operating system. For example, avoiding a particular
+sequence of code, or configuring the processor in a particular way. A
+less common situation may require similar actions in order to declassify
+a Category A erratum into a Category C erratum. These are collectively
+known as "software workarounds" and are only required in the minority of
+cases (e.g. those cases that both require a non-secure workaround *and*
+can be triggered by Linux).
+
+For software workarounds that may adversely impact systems unaffected by
+the erratum in question, a Kconfig entry is added under "Kernel
+Features" -> "ARM errata workarounds via the alternatives framework".
+These are enabled by default and patched in at runtime when an affected
+CPU is detected. For less-intrusive workarounds, a Kconfig option is not
+available and the code is structured (preferably with a comment) in such
+a way that the erratum will not be hit.
+
+This approach can make it slightly onerous to determine exactly which
+errata are worked around in an arbitrary kernel source tree, so this
+file acts as a registry of software workarounds in the Linux Kernel and
+will be updated when new workarounds are committed and backported to
+stable kernels.
+
+| Implementor | Component | Erratum ID | Kconfig |
++----------------+-----------------+-----------------+-------------------------+
+| ARM | Cortex-A53 | #826319 | ARM64_ERRATUM_826319 |
+| ARM | Cortex-A53 | #827319 | ARM64_ERRATUM_827319 |
+| ARM | Cortex-A53 | #824069 | ARM64_ERRATUM_824069 |
+| ARM | Cortex-A53 | #819472 | ARM64_ERRATUM_819472 |
+| ARM | Cortex-A53 | #845719 | ARM64_ERRATUM_845719 |
+| ARM | Cortex-A53 | #843419 | ARM64_ERRATUM_843419 |
+| ARM | Cortex-A57 | #832075 | ARM64_ERRATUM_832075 |
+| ARM | Cortex-A57 | #852523 | N/A |
+| ARM | Cortex-A57 | #834220 | ARM64_ERRATUM_834220 |
+| | | | |
+| Cavium | ThunderX ITS | #22375, #24313 | CAVIUM_ERRATUM_22375 |
+| Cavium | ThunderX GICv3 | #23154 | CAVIUM_ERRATUM_23154 |
diff --git a/Documentation/block/null_blk.txt b/Documentation/block/null_blk.txt
index 2f6c6ff7161d..d8880ca30af4 100644
--- a/Documentation/block/null_blk.txt
+++ b/Documentation/block/null_blk.txt
@@ -70,3 +70,6 @@ use_per_node_hctx=[0/1]: Default: 0
parameter.
1: The multi-queue block layer is instantiated with a hardware dispatch
queue for each CPU node in the system.
+
+use_lightnvm=[0/1]: Default: 0
+ Register device with LightNVM. Requires blk-mq to be used.
diff --git a/Documentation/cgroups/00-INDEX b/Documentation/cgroup-v1/00-INDEX
index 3f5a40f57d4a..6ad425f7cf56 100644
--- a/Documentation/cgroups/00-INDEX
+++ b/Documentation/cgroup-v1/00-INDEX
@@ -24,7 +24,5 @@ net_prio.txt
- Network priority cgroups details and usages.
pids.txt
- Process number cgroups details and usages.
-resource_counter.txt
- - Resource Counter API.
unified-hierarchy.txt
- Description the new/next cgroup interface.
diff --git a/Documentation/cgroups/blkio-controller.txt b/Documentation/cgroup-v1/blkio-controller.txt
index 52fa9f353342..673dc34d3f78 100644
--- a/Documentation/cgroups/blkio-controller.txt
+++ b/Documentation/cgroup-v1/blkio-controller.txt
@@ -84,8 +84,7 @@ Throttling/Upper Limit policy
- Run dd to read a file and see if rate is throttled to 1MB/s or not.
- # dd if=/mnt/common/zerofile of=/dev/null bs=4K count=1024
- # iflag=direct
+ # dd iflag=direct if=/mnt/common/zerofile of=/dev/null bs=4K count=1024
1024+0 records in
1024+0 records out
4194304 bytes (4.2 MB) copied, 4.0001 s, 1.0 MB/s
@@ -374,82 +373,3 @@ One can experience an overall throughput drop if you have created multiple
groups and put applications in that group which are not driving enough
IO to keep disk busy. In that case set group_idle=0, and CFQ will not idle
on individual groups and throughput should improve.
-
-Writeback
-=========
-
-Page cache is dirtied through buffered writes and shared mmaps and
-written asynchronously to the backing filesystem by the writeback
-mechanism. Writeback sits between the memory and IO domains and
-regulates the proportion of dirty memory by balancing dirtying and
-write IOs.
-
-On traditional cgroup hierarchies, relationships between different
-controllers cannot be established making it impossible for writeback
-to operate accounting for cgroup resource restrictions and all
-writeback IOs are attributed to the root cgroup.
-
-If both the blkio and memory controllers are used on the v2 hierarchy
-and the filesystem supports cgroup writeback, writeback operations
-correctly follow the resource restrictions imposed by both memory and
-blkio controllers.
-
-Writeback examines both system-wide and per-cgroup dirty memory status
-and enforces the more restrictive of the two. Also, writeback control
-parameters which are absolute values - vm.dirty_bytes and
-vm.dirty_background_bytes - are distributed across cgroups according
-to their current writeback bandwidth.
-
-There's a peculiarity stemming from the discrepancy in ownership
-granularity between memory controller and writeback. While memory
-controller tracks ownership per page, writeback operates on inode
-basis. cgroup writeback bridges the gap by tracking ownership by
-inode but migrating ownership if too many foreign pages, pages which
-don't match the current inode ownership, have been encountered while
-writing back the inode.
-
-This is a conscious design choice as writeback operations are
-inherently tied to inodes making strictly following page ownership
-complicated and inefficient. The only use case which suffers from
-this compromise is multiple cgroups concurrently dirtying disjoint
-regions of the same inode, which is an unlikely use case and decided
-to be unsupported. Note that as memory controller assigns page
-ownership on the first use and doesn't update it until the page is
-released, even if cgroup writeback strictly follows page ownership,
-multiple cgroups dirtying overlapping areas wouldn't work as expected.
-In general, write-sharing an inode across multiple cgroups is not well
-supported.
-
-Filesystem support for cgroup writeback
----------------------------------------
-
-A filesystem can make writeback IOs cgroup-aware by updating
-address_space_operations->writepage[s]() to annotate bio's using the
-following two functions.
-
-* wbc_init_bio(@wbc, @bio)
-
- Should be called for each bio carrying writeback data and associates
- the bio with the inode's owner cgroup. Can be called anytime
- between bio allocation and submission.
-
-* wbc_account_io(@wbc, @page, @bytes)
-
- Should be called for each data segment being written out. While
- this function doesn't care exactly when it's called during the
- writeback session, it's the easiest and most natural to call it as
- data segments are added to a bio.
-
-With writeback bio's annotated, cgroup support can be enabled per
-super_block by setting MS_CGROUPWB in ->s_flags. This allows for
-selective disabling of cgroup writeback support which is helpful when
-certain filesystem features, e.g. journaled data mode, are
-incompatible.
-
-wbc_init_bio() binds the specified bio to its cgroup. Depending on
-the configuration, the bio may be executed at a lower priority and if
-the writeback session is holding shared resources, e.g. a journal
-entry, may lead to priority inversion. There is no one easy solution
-for the problem. Filesystems can try to work around specific problem
-cases by skipping wbc_init_bio() or using bio_associate_blkcg()
-directly.
diff --git a/Documentation/cgroups/cgroups.txt b/Documentation/cgroup-v1/cgroups.txt
index c6256ae9885b..c6256ae9885b 100644
--- a/Documentation/cgroups/cgroups.txt
+++ b/Documentation/cgroup-v1/cgroups.txt
diff --git a/Documentation/cgroups/cpuacct.txt b/Documentation/cgroup-v1/cpuacct.txt
index 9d73cc0cadb9..9d73cc0cadb9 100644
--- a/Documentation/cgroups/cpuacct.txt
+++ b/Documentation/cgroup-v1/cpuacct.txt
diff --git a/Documentation/cgroups/cpusets.txt b/Documentation/cgroup-v1/cpusets.txt
index fdf7dff3f607..fdf7dff3f607 100644
--- a/Documentation/cgroups/cpusets.txt
+++ b/Documentation/cgroup-v1/cpusets.txt
diff --git a/Documentation/cgroups/devices.txt b/Documentation/cgroup-v1/devices.txt
index 3c1095ca02ea..3c1095ca02ea 100644
--- a/Documentation/cgroups/devices.txt
+++ b/Documentation/cgroup-v1/devices.txt
diff --git a/Documentation/cgroups/freezer-subsystem.txt b/Documentation/cgroup-v1/freezer-subsystem.txt
index e831cb2b8394..e831cb2b8394 100644
--- a/Documentation/cgroups/freezer-subsystem.txt
+++ b/Documentation/cgroup-v1/freezer-subsystem.txt
diff --git a/Documentation/cgroups/hugetlb.txt b/Documentation/cgroup-v1/hugetlb.txt
index 106245c3aecc..106245c3aecc 100644
--- a/Documentation/cgroups/hugetlb.txt
+++ b/Documentation/cgroup-v1/hugetlb.txt
diff --git a/Documentation/cgroups/memcg_test.txt b/Documentation/cgroup-v1/memcg_test.txt
index 8870b0212150..8870b0212150 100644
--- a/Documentation/cgroups/memcg_test.txt
+++ b/Documentation/cgroup-v1/memcg_test.txt
diff --git a/Documentation/cgroups/memory.txt b/Documentation/cgroup-v1/memory.txt
index ff71e16cc752..ff71e16cc752 100644
--- a/Documentation/cgroups/memory.txt
+++ b/Documentation/cgroup-v1/memory.txt
diff --git a/Documentation/cgroups/net_cls.txt b/Documentation/cgroup-v1/net_cls.txt
index ec182346dea2..ec182346dea2 100644
--- a/Documentation/cgroups/net_cls.txt
+++ b/Documentation/cgroup-v1/net_cls.txt
diff --git a/Documentation/cgroups/net_prio.txt b/Documentation/cgroup-v1/net_prio.txt
index a82cbd28ea8a..a82cbd28ea8a 100644
--- a/Documentation/cgroups/net_prio.txt
+++ b/Documentation/cgroup-v1/net_prio.txt
diff --git a/Documentation/cgroups/pids.txt b/Documentation/cgroup-v1/pids.txt
index 1a078b5d281a..1a078b5d281a 100644
--- a/Documentation/cgroups/pids.txt
+++ b/Documentation/cgroup-v1/pids.txt
diff --git a/Documentation/cgroup-v2.txt b/Documentation/cgroup-v2.txt
new file mode 100644
index 000000000000..31d1f7bf12a1
--- /dev/null
+++ b/Documentation/cgroup-v2.txt
@@ -0,0 +1,1293 @@
+
+Control Group v2
+
+October, 2015 Tejun Heo <tj@kernel.org>
+
+This is the authoritative documentation on the design, interface and
+conventions of cgroup v2. It describes all userland-visible aspects
+of cgroup including core and specific controller behaviors. All
+future changes must be reflected in this document. Documentation for
+v1 is available under Documentation/cgroup-legacy/.
+
+CONTENTS
+
+1. Introduction
+ 1-1. Terminology
+ 1-2. What is cgroup?
+2. Basic Operations
+ 2-1. Mounting
+ 2-2. Organizing Processes
+ 2-3. [Un]populated Notification
+ 2-4. Controlling Controllers
+ 2-4-1. Enabling and Disabling
+ 2-4-2. Top-down Constraint
+ 2-4-3. No Internal Process Constraint
+ 2-5. Delegation
+ 2-5-1. Model of Delegation
+ 2-5-2. Delegation Containment
+ 2-6. Guidelines
+ 2-6-1. Organize Once and Control
+ 2-6-2. Avoid Name Collisions
+3. Resource Distribution Models
+ 3-1. Weights
+ 3-2. Limits
+ 3-3. Protections
+ 3-4. Allocations
+4. Interface Files
+ 4-1. Format
+ 4-2. Conventions
+ 4-3. Core Interface Files
+5. Controllers
+ 5-1. CPU
+ 5-1-1. CPU Interface Files
+ 5-2. Memory
+ 5-2-1. Memory Interface Files
+ 5-2-2. Usage Guidelines
+ 5-2-3. Memory Ownership
+ 5-3. IO
+ 5-3-1. IO Interface Files
+ 5-3-2. Writeback
+P. Information on Kernel Programming
+ P-1. Filesystem Support for Writeback
+D. Deprecated v1 Core Features
+R. Issues with v1 and Rationales for v2
+ R-1. Multiple Hierarchies
+ R-2. Thread Granularity
+ R-3. Competition Between Inner Nodes and Threads
+ R-4. Other Interface Issues
+ R-5. Controller Issues and Remedies
+ R-5-1. Memory
+
+
+1. Introduction
+
+1-1. Terminology
+
+"cgroup" stands for "control group" and is never capitalized. The
+singular form is used to designate the whole feature and also as a
+qualifier as in "cgroup controllers". When explicitly referring to
+multiple individual control groups, the plural form "cgroups" is used.
+
+
+1-2. What is cgroup?
+
+cgroup is a mechanism to organize processes hierarchically and
+distribute system resources along the hierarchy in a controlled and
+configurable manner.
+
+cgroup is largely composed of two parts - the core and controllers.
+cgroup core is primarily responsible for hierarchically organizing
+processes. A cgroup controller is usually responsible for
+distributing a specific type of system resource along the hierarchy
+although there are utility controllers which serve purposes other than
+resource distribution.
+
+cgroups form a tree structure and every process in the system belongs
+to one and only one cgroup. All threads of a process belong to the
+same cgroup. On creation, all processes are put in the cgroup that
+the parent process belongs to at the time. A process can be migrated
+to another cgroup. Migration of a process doesn't affect already
+existing descendant processes.
+
+Following certain structural constraints, controllers may be enabled or
+disabled selectively on a cgroup. All controller behaviors are
+hierarchical - if a controller is enabled on a cgroup, it affects all
+processes which belong to the cgroups consisting the inclusive
+sub-hierarchy of the cgroup. When a controller is enabled on a nested
+cgroup, it always restricts the resource distribution further. The
+restrictions set closer to the root in the hierarchy can not be
+overridden from further away.
+
+
+2. Basic Operations
+
+2-1. Mounting
+
+Unlike v1, cgroup v2 has only single hierarchy. The cgroup v2
+hierarchy can be mounted with the following mount command.
+
+ # mount -t cgroup2 none $MOUNT_POINT
+
+cgroup2 filesystem has the magic number 0x63677270 ("cgrp"). All
+controllers which support v2 and are not bound to a v1 hierarchy are
+automatically bound to the v2 hierarchy and show up at the root.
+Controllers which are not in active use in the v2 hierarchy can be
+bound to other hierarchies. This allows mixing v2 hierarchy with the
+legacy v1 multiple hierarchies in a fully backward compatible way.
+
+A controller can be moved across hierarchies only after the controller
+is no longer referenced in its current hierarchy. Because per-cgroup
+controller states are destroyed asynchronously and controllers may
+have lingering references, a controller may not show up immediately on
+the v2 hierarchy after the final umount of the previous hierarchy.
+Similarly, a controller should be fully disabled to be moved out of
+the unified hierarchy and it may take some time for the disabled
+controller to become available for other hierarchies; furthermore, due
+to inter-controller dependencies, other controllers may need to be
+disabled too.
+
+While useful for development and manual configurations, moving
+controllers dynamically between the v2 and other hierarchies is
+strongly discouraged for production use. It is recommended to decide
+the hierarchies and controller associations before starting using the
+controllers after system boot.
+
+
+2-2. Organizing Processes
+
+Initially, only the root cgroup exists to which all processes belong.
+A child cgroup can be created by creating a sub-directory.
+
+ # mkdir $CGROUP_NAME
+
+A given cgroup may have multiple child cgroups forming a tree
+structure. Each cgroup has a read-writable interface file
+"cgroup.procs". When read, it lists the PIDs of all processes which
+belong to the cgroup one-per-line. The PIDs are not ordered and the
+same PID may show up more than once if the process got moved to
+another cgroup and then back or the PID got recycled while reading.
+
+A process can be migrated into a cgroup by writing its PID to the
+target cgroup's "cgroup.procs" file. Only one process can be migrated
+on a single write(2) call. If a process is composed of multiple
+threads, writing the PID of any thread migrates all threads of the
+process.
+
+When a process forks a child process, the new process is born into the
+cgroup that the forking process belongs to at the time of the
+operation. After exit, a process stays associated with the cgroup
+that it belonged to at the time of exit until it's reaped; however, a
+zombie process does not appear in "cgroup.procs" and thus can't be
+moved to another cgroup.
+
+A cgroup which doesn't have any children or live processes can be
+destroyed by removing the directory. Note that a cgroup which doesn't
+have any children and is associated only with zombie processes is
+considered empty and can be removed.
+
+ # rmdir $CGROUP_NAME
+
+"/proc/$PID/cgroup" lists a process's cgroup membership. If legacy
+cgroup is in use in the system, this file may contain multiple lines,
+one for each hierarchy. The entry for cgroup v2 is always in the
+format "0::$PATH".
+
+ # cat /proc/842/cgroup
+ ...
+ 0::/test-cgroup/test-cgroup-nested
+
+If the process becomes a zombie and the cgroup it was associated with
+is removed subsequently, " (deleted)" is appended to the path.
+
+ # cat /proc/842/cgroup
+ ...
+ 0::/test-cgroup/test-cgroup-nested (deleted)
+
+
+2-3. [Un]populated Notification
+
+Each non-root cgroup has a "cgroup.events" file which contains
+"populated" field indicating whether the cgroup's sub-hierarchy has
+live processes in it. Its value is 0 if there is no live process in
+the cgroup and its descendants; otherwise, 1. poll and [id]notify
+events are triggered when the value changes. This can be used, for
+example, to start a clean-up operation after all processes of a given
+sub-hierarchy have exited. The populated state updates and
+notifications are recursive. Consider the following sub-hierarchy
+where the numbers in the parentheses represent the numbers of processes
+in each cgroup.
+
+ A(4) - B(0) - C(1)
+ \ D(0)
+
+A, B and C's "populated" fields would be 1 while D's 0. After the one
+process in C exits, B and C's "populated" fields would flip to "0" and
+file modified events will be generated on the "cgroup.events" files of
+both cgroups.
+
+
+2-4. Controlling Controllers
+
+2-4-1. Enabling and Disabling
+
+Each cgroup has a "cgroup.controllers" file which lists all
+controllers available for the cgroup to enable.
+
+ # cat cgroup.controllers
+ cpu io memory
+
+No controller is enabled by default. Controllers can be enabled and
+disabled by writing to the "cgroup.subtree_control" file.
+
+ # echo "+cpu +memory -io" > cgroup.subtree_control
+
+Only controllers which are listed in "cgroup.controllers" can be
+enabled. When multiple operations are specified as above, either they
+all succeed or fail. If multiple operations on the same controller
+are specified, the last one is effective.
+
+Enabling a controller in a cgroup indicates that the distribution of
+the target resource across its immediate children will be controlled.
+Consider the following sub-hierarchy. The enabled controllers are
+listed in parentheses.
+
+ A(cpu,memory) - B(memory) - C()
+ \ D()
+
+As A has "cpu" and "memory" enabled, A will control the distribution
+of CPU cycles and memory to its children, in this case, B. As B has
+"memory" enabled but not "CPU", C and D will compete freely on CPU
+cycles but their division of memory available to B will be controlled.
+
+As a controller regulates the distribution of the target resource to
+the cgroup's children, enabling it creates the controller's interface
+files in the child cgroups. In the above example, enabling "cpu" on B
+would create the "cpu." prefixed controller interface files in C and
+D. Likewise, disabling "memory" from B would remove the "memory."
+prefixed controller interface files from C and D. This means that the
+controller interface files - anything which doesn't start with
+"cgroup." are owned by the parent rather than the cgroup itself.
+
+
+2-4-2. Top-down Constraint
+
+Resources are distributed top-down and a cgroup can further distribute
+a resource only if the resource has been distributed to it from the
+parent. This means that all non-root "cgroup.subtree_control" files
+can only contain controllers which are enabled in the parent's
+"cgroup.subtree_control" file. A controller can be enabled only if
+the parent has the controller enabled and a controller can't be
+disabled if one or more children have it enabled.
+
+
+2-4-3. No Internal Process Constraint
+
+Non-root cgroups can only distribute resources to their children when
+they don't have any processes of their own. In other words, only
+cgroups which don't contain any processes can have controllers enabled
+in their "cgroup.subtree_control" files.
+
+This guarantees that, when a controller is looking at the part of the
+hierarchy which has it enabled, processes are always only on the
+leaves. This rules out situations where child cgroups compete against
+internal processes of the parent.
+
+The root cgroup is exempt from this restriction. Root contains
+processes and anonymous resource consumption which can't be associated
+with any other cgroups and requires special treatment from most
+controllers. How resource consumption in the root cgroup is governed
+is up to each controller.
+
+Note that the restriction doesn't get in the way if there is no
+enabled controller in the cgroup's "cgroup.subtree_control". This is
+important as otherwise it wouldn't be possible to create children of a
+populated cgroup. To control resource distribution of a cgroup, the
+cgroup must create children and transfer all its processes to the
+children before enabling controllers in its "cgroup.subtree_control"
+file.
+
+
+2-5. Delegation
+
+2-5-1. Model of Delegation
+
+A cgroup can be delegated to a less privileged user by granting write
+access of the directory and its "cgroup.procs" file to the user. Note
+that resource control interface files in a given directory control the
+distribution of the parent's resources and thus must not be delegated
+along with the directory.
+
+Once delegated, the user can build sub-hierarchy under the directory,
+organize processes as it sees fit and further distribute the resources
+it received from the parent. The limits and other settings of all
+resource controllers are hierarchical and regardless of what happens
+in the delegated sub-hierarchy, nothing can escape the resource
+restrictions imposed by the parent.
+
+Currently, cgroup doesn't impose any restrictions on the number of
+cgroups in or nesting depth of a delegated sub-hierarchy; however,
+this may be limited explicitly in the future.
+
+
+2-5-2. Delegation Containment
+
+A delegated sub-hierarchy is contained in the sense that processes
+can't be moved into or out of the sub-hierarchy by the delegatee. For
+a process with a non-root euid to migrate a target process into a
+cgroup by writing its PID to the "cgroup.procs" file, the following
+conditions must be met.
+
+- The writer's euid must match either uid or suid of the target process.
+
+- The writer must have write access to the "cgroup.procs" file.
+
+- The writer must have write access to the "cgroup.procs" file of the
+ common ancestor of the source and destination cgroups.
+
+The above three constraints ensure that while a delegatee may migrate
+processes around freely in the delegated sub-hierarchy it can't pull
+in from or push out to outside the sub-hierarchy.
+
+For an example, let's assume cgroups C0 and C1 have been delegated to
+user U0 who created C00, C01 under C0 and C10 under C1 as follows and
+all processes under C0 and C1 belong to U0.
+
+ ~~~~~~~~~~~~~ - C0 - C00
+ ~ cgroup ~ \ C01
+ ~ hierarchy ~
+ ~~~~~~~~~~~~~ - C1 - C10
+
+Let's also say U0 wants to write the PID of a process which is
+currently in C10 into "C00/cgroup.procs". U0 has write access to the
+file and uid match on the process; however, the common ancestor of the
+source cgroup C10 and the destination cgroup C00 is above the points
+of delegation and U0 would not have write access to its "cgroup.procs"
+files and thus the write will be denied with -EACCES.
+
+
+2-6. Guidelines
+
+2-6-1. Organize Once and Control
+
+Migrating a process across cgroups is a relatively expensive operation
+and stateful resources such as memory are not moved together with the
+process. This is an explicit design decision as there often exist
+inherent trade-offs between migration and various hot paths in terms
+of synchronization cost.
+
+As such, migrating processes across cgroups frequently as a means to
+apply different resource restrictions is discouraged. A workload
+should be assigned to a cgroup according to the system's logical and
+resource structure once on start-up. Dynamic adjustments to resource
+distribution can be made by changing controller configuration through
+the interface files.
+
+
+2-6-2. Avoid Name Collisions
+
+Interface files for a cgroup and its children cgroups occupy the same
+directory and it is possible to create children cgroups which collide
+with interface files.
+
+All cgroup core interface files are prefixed with "cgroup." and each
+controller's interface files are prefixed with the controller name and
+a dot. A controller's name is composed of lower case alphabets and
+'_'s but never begins with an '_' so it can be used as the prefix
+character for collision avoidance. Also, interface file names won't
+start or end with terms which are often used in categorizing workloads
+such as job, service, slice, unit or workload.
+
+cgroup doesn't do anything to prevent name collisions and it's the
+user's responsibility to avoid them.
+
+
+3. Resource Distribution Models
+
+cgroup controllers implement several resource distribution schemes
+depending on the resource type and expected use cases. This section
+describes major schemes in use along with their expected behaviors.
+
+
+3-1. Weights
+
+A parent's resource is distributed by adding up the weights of all
+active children and giving each the fraction matching the ratio of its
+weight against the sum. As only children which can make use of the
+resource at the moment participate in the distribution, this is
+work-conserving. Due to the dynamic nature, this model is usually
+used for stateless resources.
+
+All weights are in the range [1, 10000] with the default at 100. This
+allows symmetric multiplicative biases in both directions at fine
+enough granularity while staying in the intuitive range.
+
+As long as the weight is in range, all configuration combinations are
+valid and there is no reason to reject configuration changes or
+process migrations.
+
+"cpu.weight" proportionally distributes CPU cycles to active children
+and is an example of this type.
+
+
+3-2. Limits
+
+A child can only consume upto the configured amount of the resource.
+Limits can be over-committed - the sum of the limits of children can
+exceed the amount of resource available to the parent.
+
+Limits are in the range [0, max] and defaults to "max", which is noop.
+
+As limits can be over-committed, all configuration combinations are
+valid and there is no reason to reject configuration changes or
+process migrations.
+
+"io.max" limits the maximum BPS and/or IOPS that a cgroup can consume
+on an IO device and is an example of this type.
+
+
+3-3. Protections
+
+A cgroup is protected to be allocated upto the configured amount of
+the resource if the usages of all its ancestors are under their
+protected levels. Protections can be hard guarantees or best effort
+soft boundaries. Protections can also be over-committed in which case
+only upto the amount available to the parent is protected among
+children.
+
+Protections are in the range [0, max] and defaults to 0, which is
+noop.
+
+As protections can be over-committed, all configuration combinations
+are valid and there is no reason to reject configuration changes or
+process migrations.
+
+"memory.low" implements best-effort memory protection and is an
+example of this type.
+
+
+3-4. Allocations
+
+A cgroup is exclusively allocated a certain amount of a finite
+resource. Allocations can't be over-committed - the sum of the
+allocations of children can not exceed the amount of resource
+available to the parent.
+
+Allocations are in the range [0, max] and defaults to 0, which is no
+resource.
+
+As allocations can't be over-committed, some configuration
+combinations are invalid and should be rejected. Also, if the
+resource is mandatory for execution of processes, process migrations
+may be rejected.
+
+"cpu.rt.max" hard-allocates realtime slices and is an example of this
+type.
+
+
+4. Interface Files
+
+4-1. Format
+
+All interface files should be in one of the following formats whenever
+possible.
+
+ New-line separated values
+ (when only one value can be written at once)
+
+ VAL0\n
+ VAL1\n
+ ...
+
+ Space separated values
+ (when read-only or multiple values can be written at once)
+
+ VAL0 VAL1 ...\n
+
+ Flat keyed
+
+ KEY0 VAL0\n
+ KEY1 VAL1\n
+ ...
+
+ Nested keyed
+
+ KEY0 SUB_KEY0=VAL00 SUB_KEY1=VAL01...
+ KEY1 SUB_KEY0=VAL10 SUB_KEY1=VAL11...
+ ...
+
+For a writable file, the format for writing should generally match
+reading; however, controllers may allow omitting later fields or
+implement restricted shortcuts for most common use cases.
+
+For both flat and nested keyed files, only the values for a single key
+can be written at a time. For nested keyed files, the sub key pairs
+may be specified in any order and not all pairs have to be specified.
+
+
+4-2. Conventions
+
+- Settings for a single feature should be contained in a single file.
+
+- The root cgroup should be exempt from resource control and thus
+ shouldn't have resource control interface files. Also,
+ informational files on the root cgroup which end up showing global
+ information available elsewhere shouldn't exist.
+
+- If a controller implements weight based resource distribution, its
+ interface file should be named "weight" and have the range [1,
+ 10000] with 100 as the default. The values are chosen to allow
+ enough and symmetric bias in both directions while keeping it
+ intuitive (the default is 100%).
+
+- If a controller implements an absolute resource guarantee and/or
+ limit, the interface files should be named "min" and "max"
+ respectively. If a controller implements best effort resource
+ guarantee and/or limit, the interface files should be named "low"
+ and "high" respectively.
+
+ In the above four control files, the special token "max" should be
+ used to represent upward infinity for both reading and writing.
+
+- If a setting has a configurable default value and keyed specific
+ overrides, the default entry should be keyed with "default" and
+ appear as the first entry in the file.
+
+ The default value can be updated by writing either "default $VAL" or
+ "$VAL".
+
+ When writing to update a specific override, "default" can be used as
+ the value to indicate removal of the override. Override entries
+ with "default" as the value must not appear when read.
+
+ For example, a setting which is keyed by major:minor device numbers
+ with integer values may look like the following.
+
+ # cat cgroup-example-interface-file
+ default 150
+ 8:0 300
+
+ The default value can be updated by
+
+ # echo 125 > cgroup-example-interface-file
+
+ or
+
+ # echo "default 125" > cgroup-example-interface-file
+
+ An override can be set by
+
+ # echo "8:16 170" > cgroup-example-interface-file
+
+ and cleared by
+
+ # echo "8:0 default" > cgroup-example-interface-file
+ # cat cgroup-example-interface-file
+ default 125
+ 8:16 170
+
+- For events which are not very high frequency, an interface file
+ "events" should be created which lists event key value pairs.
+ Whenever a notifiable event happens, file modified event should be
+ generated on the file.
+
+
+4-3. Core Interface Files
+
+All cgroup core files are prefixed with "cgroup."
+
+ cgroup.procs
+
+ A read-write new-line separated values file which exists on
+ all cgroups.
+
+ When read, it lists the PIDs of all processes which belong to
+ the cgroup one-per-line. The PIDs are not ordered and the
+ same PID may show up more than once if the process got moved
+ to another cgroup and then back or the PID got recycled while
+ reading.
+
+ A PID can be written to migrate the process associated with
+ the PID to the cgroup. The writer should match all of the
+ following conditions.
+
+ - Its euid is either root or must match either uid or suid of
+ the target process.
+
+ - It must have write access to the "cgroup.procs" file.
+
+ - It must have write access to the "cgroup.procs" file of the
+ common ancestor of the source and destination cgroups.
+
+ When delegating a sub-hierarchy, write access to this file
+ should be granted along with the containing directory.
+
+ cgroup.controllers
+
+ A read-only space separated values file which exists on all
+ cgroups.
+
+ It shows space separated list of all controllers available to
+ the cgroup. The controllers are not ordered.
+
+ cgroup.subtree_control
+
+ A read-write space separated values file which exists on all
+ cgroups. Starts out empty.
+
+ When read, it shows space separated list of the controllers
+ which are enabled to control resource distribution from the
+ cgroup to its children.
+
+ Space separated list of controllers prefixed with '+' or '-'
+ can be written to enable or disable controllers. A controller
+ name prefixed with '+' enables the controller and '-'
+ disables. If a controller appears more than once on the list,
+ the last one is effective. When multiple enable and disable
+ operations are specified, either all succeed or all fail.
+
+ cgroup.events
+
+ A read-only flat-keyed file which exists on non-root cgroups.
+ The following entries are defined. Unless specified
+ otherwise, a value change in this file generates a file
+ modified event.
+
+ populated
+
+ 1 if the cgroup or its descendants contains any live
+ processes; otherwise, 0.
+
+
+5. Controllers
+
+5-1. CPU
+
+[NOTE: The interface for the cpu controller hasn't been merged yet]
+
+The "cpu" controllers regulates distribution of CPU cycles. This
+controller implements weight and absolute bandwidth limit models for
+normal scheduling policy and absolute bandwidth allocation model for
+realtime scheduling policy.
+
+
+5-1-1. CPU Interface Files
+
+All time durations are in microseconds.
+
+ cpu.stat
+
+ A read-only flat-keyed file which exists on non-root cgroups.
+
+ It reports the following six stats.
+
+ usage_usec
+ user_usec
+ system_usec
+ nr_periods
+ nr_throttled
+ throttled_usec
+
+ cpu.weight
+
+ A read-write single value file which exists on non-root
+ cgroups. The default is "100".
+
+ The weight in the range [1, 10000].
+
+ cpu.max
+
+ A read-write two value file which exists on non-root cgroups.
+ The default is "max 100000".
+
+ The maximum bandwidth limit. It's in the following format.
+
+ $MAX $PERIOD
+
+ which indicates that the group may consume upto $MAX in each
+ $PERIOD duration. "max" for $MAX indicates no limit. If only
+ one number is written, $MAX is updated.
+
+ cpu.rt.max
+
+ [NOTE: The semantics of this file is still under discussion and the
+ interface hasn't been merged yet]
+
+ A read-write two value file which exists on all cgroups.
+ The default is "0 100000".
+
+ The maximum realtime runtime allocation. Over-committing
+ configurations are disallowed and process migrations are
+ rejected if not enough bandwidth is available. It's in the
+ following format.
+
+ $MAX $PERIOD
+
+ which indicates that the group may consume upto $MAX in each
+ $PERIOD duration. If only one number is written, $MAX is
+ updated.
+
+
+5-2. Memory
+
+The "memory" controller regulates distribution of memory. Memory is
+stateful and implements both limit and protection models. Due to the
+intertwining between memory usage and reclaim pressure and the
+stateful nature of memory, the distribution model is relatively
+complex.
+
+While not completely water-tight, all major memory usages by a given
+cgroup are tracked so that the total memory consumption can be
+accounted and controlled to a reasonable extent. Currently, the
+following types of memory usages are tracked.
+
+- Userland memory - page cache and anonymous memory.
+
+- Kernel data structures such as dentries and inodes.
+
+- TCP socket buffers.
+
+The above list may expand in the future for better coverage.
+
+
+5-2-1. Memory Interface Files
+
+All memory amounts are in bytes. If a value which is not aligned to
+PAGE_SIZE is written, the value may be rounded up to the closest
+PAGE_SIZE multiple when read back.
+
+ memory.current
+
+ A read-only single value file which exists on non-root
+ cgroups.
+
+ The total amount of memory currently being used by the cgroup
+ and its descendants.
+
+ memory.low
+
+ A read-write single value file which exists on non-root
+ cgroups. The default is "0".
+
+ Best-effort memory protection. If the memory usages of a
+ cgroup and all its ancestors are below their low boundaries,
+ the cgroup's memory won't be reclaimed unless memory can be
+ reclaimed from unprotected cgroups.
+
+ Putting more memory than generally available under this
+ protection is discouraged.
+
+ memory.high
+
+ A read-write single value file which exists on non-root
+ cgroups. The default is "max".
+
+ Memory usage throttle limit. This is the main mechanism to
+ control memory usage of a cgroup. If a cgroup's usage goes
+ over the high boundary, the processes of the cgroup are
+ throttled and put under heavy reclaim pressure.
+
+ Going over the high limit never invokes the OOM killer and
+ under extreme conditions the limit may be breached.
+
+ memory.max
+
+ A read-write single value file which exists on non-root
+ cgroups. The default is "max".
+
+ Memory usage hard limit. This is the final protection
+ mechanism. If a cgroup's memory usage reaches this limit and
+ can't be reduced, the OOM killer is invoked in the cgroup.
+ Under certain circumstances, the usage may go over the limit
+ temporarily.
+
+ This is the ultimate protection mechanism. As long as the
+ high limit is used and monitored properly, this limit's
+ utility is limited to providing the final safety net.
+
+ memory.events
+
+ A read-only flat-keyed file which exists on non-root cgroups.
+ The following entries are defined. Unless specified
+ otherwise, a value change in this file generates a file
+ modified event.
+
+ low
+
+ The number of times the cgroup is reclaimed due to
+ high memory pressure even though its usage is under
+ the low boundary. This usually indicates that the low
+ boundary is over-committed.
+
+ high
+
+ The number of times processes of the cgroup are
+ throttled and routed to perform direct memory reclaim
+ because the high memory boundary was exceeded. For a
+ cgroup whose memory usage is capped by the high limit
+ rather than global memory pressure, this event's
+ occurrences are expected.
+
+ max
+
+ The number of times the cgroup's memory usage was
+ about to go over the max boundary. If direct reclaim
+ fails to bring it down, the OOM killer is invoked.
+
+ oom
+
+ The number of times the OOM killer has been invoked in
+ the cgroup. This may not exactly match the number of
+ processes killed but should generally be close.
+
+
+5-2-2. General Usage
+
+"memory.high" is the main mechanism to control memory usage.
+Over-committing on high limit (sum of high limits > available memory)
+and letting global memory pressure to distribute memory according to
+usage is a viable strategy.
+
+Because breach of the high limit doesn't trigger the OOM killer but
+throttles the offending cgroup, a management agent has ample
+opportunities to monitor and take appropriate actions such as granting
+more memory or terminating the workload.
+
+Determining whether a cgroup has enough memory is not trivial as
+memory usage doesn't indicate whether the workload can benefit from
+more memory. For example, a workload which writes data received from
+network to a file can use all available memory but can also operate as
+performant with a small amount of memory. A measure of memory
+pressure - how much the workload is being impacted due to lack of
+memory - is necessary to determine whether a workload needs more
+memory; unfortunately, memory pressure monitoring mechanism isn't
+implemented yet.
+
+
+5-2-3. Memory Ownership
+
+A memory area is charged to the cgroup which instantiated it and stays
+charged to the cgroup until the area is released. Migrating a process
+to a different cgroup doesn't move the memory usages that it
+instantiated while in the previous cgroup to the new cgroup.
+
+A memory area may be used by processes belonging to different cgroups.
+To which cgroup the area will be charged is in-deterministic; however,
+over time, the memory area is likely to end up in a cgroup which has
+enough memory allowance to avoid high reclaim pressure.
+
+If a cgroup sweeps a considerable amount of memory which is expected
+to be accessed repeatedly by other cgroups, it may make sense to use
+POSIX_FADV_DONTNEED to relinquish the ownership of memory areas
+belonging to the affected files to ensure correct memory ownership.
+
+
+5-3. IO
+
+The "io" controller regulates the distribution of IO resources. This
+controller implements both weight based and absolute bandwidth or IOPS
+limit distribution; however, weight based distribution is available
+only if cfq-iosched is in use and neither scheme is available for
+blk-mq devices.
+
+
+5-3-1. IO Interface Files
+
+ io.stat
+
+ A read-only nested-keyed file which exists on non-root
+ cgroups.
+
+ Lines are keyed by $MAJ:$MIN device numbers and not ordered.
+ The following nested keys are defined.
+
+ rbytes Bytes read
+ wbytes Bytes written
+ rios Number of read IOs
+ wios Number of write IOs
+
+ An example read output follows.
+
+ 8:16 rbytes=1459200 wbytes=314773504 rios=192 wios=353
+ 8:0 rbytes=90430464 wbytes=299008000 rios=8950 wios=1252
+
+ io.weight
+
+ A read-write flat-keyed file which exists on non-root cgroups.
+ The default is "default 100".
+
+ The first line is the default weight applied to devices
+ without specific override. The rest are overrides keyed by
+ $MAJ:$MIN device numbers and not ordered. The weights are in
+ the range [1, 10000] and specifies the relative amount IO time
+ the cgroup can use in relation to its siblings.
+
+ The default weight can be updated by writing either "default
+ $WEIGHT" or simply "$WEIGHT". Overrides can be set by writing
+ "$MAJ:$MIN $WEIGHT" and unset by writing "$MAJ:$MIN default".
+
+ An example read output follows.
+
+ default 100
+ 8:16 200
+ 8:0 50
+
+ io.max
+
+ A read-write nested-keyed file which exists on non-root
+ cgroups.
+
+ BPS and IOPS based IO limit. Lines are keyed by $MAJ:$MIN
+ device numbers and not ordered. The following nested keys are
+ defined.
+
+ rbps Max read bytes per second
+ wbps Max write bytes per second
+ riops Max read IO operations per second
+ wiops Max write IO operations per second
+
+ When writing, any number of nested key-value pairs can be
+ specified in any order. "max" can be specified as the value
+ to remove a specific limit. If the same key is specified
+ multiple times, the outcome is undefined.
+
+ BPS and IOPS are measured in each IO direction and IOs are
+ delayed if limit is reached. Temporary bursts are allowed.
+
+ Setting read limit at 2M BPS and write at 120 IOPS for 8:16.
+
+ echo "8:16 rbps=2097152 wiops=120" > io.max
+
+ Reading returns the following.
+
+ 8:16 rbps=2097152 wbps=max riops=max wiops=120
+
+ Write IOPS limit can be removed by writing the following.
+
+ echo "8:16 wiops=max" > io.max
+
+ Reading now returns the following.
+
+ 8:16 rbps=2097152 wbps=max riops=max wiops=max
+
+
+5-3-2. Writeback
+
+Page cache is dirtied through buffered writes and shared mmaps and
+written asynchronously to the backing filesystem by the writeback
+mechanism. Writeback sits between the memory and IO domains and
+regulates the proportion of dirty memory by balancing dirtying and
+write IOs.
+
+The io controller, in conjunction with the memory controller,
+implements control of page cache writeback IOs. The memory controller
+defines the memory domain that dirty memory ratio is calculated and
+maintained for and the io controller defines the io domain which
+writes out dirty pages for the memory domain. Both system-wide and
+per-cgroup dirty memory states are examined and the more restrictive
+of the two is enforced.
+
+cgroup writeback requires explicit support from the underlying
+filesystem. Currently, cgroup writeback is implemented on ext2, ext4
+and btrfs. On other filesystems, all writeback IOs are attributed to
+the root cgroup.
+
+There are inherent differences in memory and writeback management
+which affects how cgroup ownership is tracked. Memory is tracked per
+page while writeback per inode. For the purpose of writeback, an
+inode is assigned to a cgroup and all IO requests to write dirty pages
+from the inode are attributed to that cgroup.
+
+As cgroup ownership for memory is tracked per page, there can be pages
+which are associated with different cgroups than the one the inode is
+associated with. These are called foreign pages. The writeback
+constantly keeps track of foreign pages and, if a particular foreign
+cgroup becomes the majority over a certain period of time, switches
+the ownership of the inode to that cgroup.
+
+While this model is enough for most use cases where a given inode is
+mostly dirtied by a single cgroup even when the main writing cgroup
+changes over time, use cases where multiple cgroups write to a single
+inode simultaneously are not supported well. In such circumstances, a
+significant portion of IOs are likely to be attributed incorrectly.
+As memory controller assigns page ownership on the first use and
+doesn't update it until the page is released, even if writeback
+strictly follows page ownership, multiple cgroups dirtying overlapping
+areas wouldn't work as expected. It's recommended to avoid such usage
+patterns.
+
+The sysctl knobs which affect writeback behavior are applied to cgroup
+writeback as follows.
+
+ vm.dirty_background_ratio
+ vm.dirty_ratio
+
+ These ratios apply the same to cgroup writeback with the
+ amount of available memory capped by limits imposed by the
+ memory controller and system-wide clean memory.
+
+ vm.dirty_background_bytes
+ vm.dirty_bytes
+
+ For cgroup writeback, this is calculated into ratio against
+ total available memory and applied the same way as
+ vm.dirty[_background]_ratio.
+
+
+P. Information on Kernel Programming
+
+This section contains kernel programming information in the areas
+where interacting with cgroup is necessary. cgroup core and
+controllers are not covered.
+
+
+P-1. Filesystem Support for Writeback
+
+A filesystem can support cgroup writeback by updating
+address_space_operations->writepage[s]() to annotate bio's using the
+following two functions.
+
+ wbc_init_bio(@wbc, @bio)
+
+ Should be called for each bio carrying writeback data and
+ associates the bio with the inode's owner cgroup. Can be
+ called anytime between bio allocation and submission.
+
+ wbc_account_io(@wbc, @page, @bytes)
+
+ Should be called for each data segment being written out.
+ While this function doesn't care exactly when it's called
+ during the writeback session, it's the easiest and most
+ natural to call it as data segments are added to a bio.
+
+With writeback bio's annotated, cgroup support can be enabled per
+super_block by setting SB_I_CGROUPWB in ->s_iflags. This allows for
+selective disabling of cgroup writeback support which is helpful when
+certain filesystem features, e.g. journaled data mode, are
+incompatible.
+
+wbc_init_bio() binds the specified bio to its cgroup. Depending on
+the configuration, the bio may be executed at a lower priority and if
+the writeback session is holding shared resources, e.g. a journal
+entry, may lead to priority inversion. There is no one easy solution
+for the problem. Filesystems can try to work around specific problem
+cases by skipping wbc_init_bio() or using bio_associate_blkcg()
+directly.
+
+
+D. Deprecated v1 Core Features
+
+- Multiple hierarchies including named ones are not supported.
+
+- All mount options and remounting are not supported.
+
+- The "tasks" file is removed and "cgroup.procs" is not sorted.
+
+- "cgroup.clone_children" is removed.
+
+- /proc/cgroups is meaningless for v2. Use "cgroup.controllers" file
+ at the root instead.
+
+
+R. Issues with v1 and Rationales for v2
+
+R-1. Multiple Hierarchies
+
+cgroup v1 allowed an arbitrary number of hierarchies and each
+hierarchy could host any number of controllers. While this seemed to
+provide a high level of flexibility, it wasn't useful in practice.
+
+For example, as there is only one instance of each controller, utility
+type controllers such as freezer which can be useful in all
+hierarchies could only be used in one. The issue is exacerbated by
+the fact that controllers couldn't be moved to another hierarchy once
+hierarchies were populated. Another issue was that all controllers
+bound to a hierarchy were forced to have exactly the same view of the
+hierarchy. It wasn't possible to vary the granularity depending on
+the specific controller.
+
+In practice, these issues heavily limited which controllers could be
+put on the same hierarchy and most configurations resorted to putting
+each controller on its own hierarchy. Only closely related ones, such
+as the cpu and cpuacct controllers, made sense to be put on the same
+hierarchy. This often meant that userland ended up managing multiple
+similar hierarchies repeating the same steps on each hierarchy
+whenever a hierarchy management operation was necessary.
+
+Furthermore, support for multiple hierarchies came at a steep cost.
+It greatly complicated cgroup core implementation but more importantly
+the support for multiple hierarchies restricted how cgroup could be
+used in general and what controllers was able to do.
+
+There was no limit on how many hierarchies there might be, which meant
+that a thread's cgroup membership couldn't be described in finite
+length. The key might contain any number of entries and was unlimited
+in length, which made it highly awkward to manipulate and led to
+addition of controllers which existed only to identify membership,
+which in turn exacerbated the original problem of proliferating number
+of hierarchies.
+
+Also, as a controller couldn't have any expectation regarding the
+topologies of hierarchies other controllers might be on, each
+controller had to assume that all other controllers were attached to
+completely orthogonal hierarchies. This made it impossible, or at
+least very cumbersome, for controllers to cooperate with each other.
+
+In most use cases, putting controllers on hierarchies which are
+completely orthogonal to each other isn't necessary. What usually is
+called for is the ability to have differing levels of granularity
+depending on the specific controller. In other words, hierarchy may
+be collapsed from leaf towards root when viewed from specific
+controllers. For example, a given configuration might not care about
+how memory is distributed beyond a certain level while still wanting
+to control how CPU cycles are distributed.
+
+
+R-2. Thread Granularity
+
+cgroup v1 allowed threads of a process to belong to different cgroups.
+This didn't make sense for some controllers and those controllers
+ended up implementing different ways to ignore such situations but
+much more importantly it blurred the line between API exposed to
+individual applications and system management interface.
+
+Generally, in-process knowledge is available only to the process
+itself; thus, unlike service-level organization of processes,
+categorizing threads of a process requires active participation from
+the application which owns the target process.
+
+cgroup v1 had an ambiguously defined delegation model which got abused
+in combination with thread granularity. cgroups were delegated to
+individual applications so that they can create and manage their own
+sub-hierarchies and control resource distributions along them. This
+effectively raised cgroup to the status of a syscall-like API exposed
+to lay programs.
+
+First of all, cgroup has a fundamentally inadequate interface to be
+exposed this way. For a process to access its own knobs, it has to
+extract the path on the target hierarchy from /proc/self/cgroup,
+construct the path by appending the name of the knob to the path, open
+and then read and/or write to it. This is not only extremely clunky
+and unusual but also inherently racy. There is no conventional way to
+define transaction across the required steps and nothing can guarantee
+that the process would actually be operating on its own sub-hierarchy.
+
+cgroup controllers implemented a number of knobs which would never be
+accepted as public APIs because they were just adding control knobs to
+system-management pseudo filesystem. cgroup ended up with interface
+knobs which were not properly abstracted or refined and directly
+revealed kernel internal details. These knobs got exposed to
+individual applications through the ill-defined delegation mechanism
+effectively abusing cgroup as a shortcut to implementing public APIs
+without going through the required scrutiny.
+
+This was painful for both userland and kernel. Userland ended up with
+misbehaving and poorly abstracted interfaces and kernel exposing and
+locked into constructs inadvertently.
+
+
+R-3. Competition Between Inner Nodes and Threads
+
+cgroup v1 allowed threads to be in any cgroups which created an
+interesting problem where threads belonging to a parent cgroup and its
+children cgroups competed for resources. This was nasty as two
+different types of entities competed and there was no obvious way to
+settle it. Different controllers did different things.
+
+The cpu controller considered threads and cgroups as equivalents and
+mapped nice levels to cgroup weights. This worked for some cases but
+fell flat when children wanted to be allocated specific ratios of CPU
+cycles and the number of internal threads fluctuated - the ratios
+constantly changed as the number of competing entities fluctuated.
+There also were other issues. The mapping from nice level to weight
+wasn't obvious or universal, and there were various other knobs which
+simply weren't available for threads.
+
+The io controller implicitly created a hidden leaf node for each
+cgroup to host the threads. The hidden leaf had its own copies of all
+the knobs with "leaf_" prefixed. While this allowed equivalent
+control over internal threads, it was with serious drawbacks. It
+always added an extra layer of nesting which wouldn't be necessary
+otherwise, made the interface messy and significantly complicated the
+implementation.
+
+The memory controller didn't have a way to control what happened
+between internal tasks and child cgroups and the behavior was not
+clearly defined. There were attempts to add ad-hoc behaviors and
+knobs to tailor the behavior to specific workloads which would have
+led to problems extremely difficult to resolve in the long term.
+
+Multiple controllers struggled with internal tasks and came up with
+different ways to deal with it; unfortunately, all the approaches were
+severely flawed and, furthermore, the widely different behaviors
+made cgroup as a whole highly inconsistent.
+
+This clearly is a problem which needs to be addressed from cgroup core
+in a uniform way.
+
+
+R-4. Other Interface Issues
+
+cgroup v1 grew without oversight and developed a large number of
+idiosyncrasies and inconsistencies. One issue on the cgroup core side
+was how an empty cgroup was notified - a userland helper binary was
+forked and executed for each event. The event delivery wasn't
+recursive or delegatable. The limitations of the mechanism also led
+to in-kernel event delivery filtering mechanism further complicating
+the interface.
+
+Controller interfaces were problematic too. An extreme example is
+controllers completely ignoring hierarchical organization and treating
+all cgroups as if they were all located directly under the root
+cgroup. Some controllers exposed a large amount of inconsistent
+implementation details to userland.
+
+There also was no consistency across controllers. When a new cgroup
+was created, some controllers defaulted to not imposing extra
+restrictions while others disallowed any resource usage until
+explicitly configured. Configuration knobs for the same type of
+control used widely differing naming schemes and formats. Statistics
+and information knobs were named arbitrarily and used different
+formats and units even in the same controller.
+
+cgroup v2 establishes common conventions where appropriate and updates
+controllers so that they expose minimal and consistent interfaces.
+
+
+R-5. Controller Issues and Remedies
+
+R-5-1. Memory
+
+The original lower boundary, the soft limit, is defined as a limit
+that is per default unset. As a result, the set of cgroups that
+global reclaim prefers is opt-in, rather than opt-out. The costs for
+optimizing these mostly negative lookups are so high that the
+implementation, despite its enormous size, does not even provide the
+basic desirable behavior. First off, the soft limit has no
+hierarchical meaning. All configured groups are organized in a global
+rbtree and treated like equal peers, regardless where they are located
+in the hierarchy. This makes subtree delegation impossible. Second,
+the soft limit reclaim pass is so aggressive that it not just
+introduces high allocation latencies into the system, but also impacts
+system performance due to overreclaim, to the point where the feature
+becomes self-defeating.
+
+The memory.low boundary on the other hand is a top-down allocated
+reserve. A cgroup enjoys reclaim protection when it and all its
+ancestors are below their low boundaries, which makes delegation of
+subtrees possible. Secondly, new cgroups have no reserve per default
+and in the common case most cgroups are eligible for the preferred
+reclaim pass. This allows the new low boundary to be efficiently
+implemented with just a minor addition to the generic reclaim code,
+without the need for out-of-band data structures and reclaim passes.
+Because the generic reclaim code considers all cgroups except for the
+ones running low in the preferred first reclaim pass, overreclaim of
+individual groups is eliminated as well, resulting in much better
+overall workload performance.
+
+The original high boundary, the hard limit, is defined as a strict
+limit that can not budge, even if the OOM killer has to be called.
+But this generally goes against the goal of making the most out of the
+available memory. The memory consumption of workloads varies during
+runtime, and that requires users to overcommit. But doing that with a
+strict upper limit requires either a fairly accurate prediction of the
+working set size or adding slack to the limit. Since working set size
+estimation is hard and error prone, and getting it wrong results in
+OOM kills, most users tend to err on the side of a looser limit and
+end up wasting precious resources.
+
+The memory.high boundary on the other hand can be set much more
+conservatively. When hit, it throttles allocations by forcing them
+into direct reclaim to work off the excess, but it never invokes the
+OOM killer. As a result, a high boundary that is chosen too
+aggressively will not terminate the processes, but instead it will
+lead to gradual performance degradation. The user can monitor this
+and make corrections until the minimal memory footprint that still
+gives acceptable performance is found.
+
+In extreme cases, with many concurrent allocations and a complete
+breakdown of reclaim progress within the group, the high boundary can
+be exceeded. But even then it's mostly better to satisfy the
+allocation from the slack available in other groups or the rest of the
+system than killing the group. Otherwise, memory.max is there to
+limit this type of spillover and ultimately contain buggy or even
+malicious applications.
diff --git a/Documentation/cgroups/unified-hierarchy.txt b/Documentation/cgroups/unified-hierarchy.txt
deleted file mode 100644
index 781b1d475bcf..000000000000
--- a/Documentation/cgroups/unified-hierarchy.txt
+++ /dev/null
@@ -1,647 +0,0 @@
-
-Cgroup unified hierarchy
-
-April, 2014 Tejun Heo <tj@kernel.org>
-
-This document describes the changes made by unified hierarchy and
-their rationales. It will eventually be merged into the main cgroup
-documentation.
-
-CONTENTS
-
-1. Background
-2. Basic Operation
- 2-1. Mounting
- 2-2. cgroup.subtree_control
- 2-3. cgroup.controllers
-3. Structural Constraints
- 3-1. Top-down
- 3-2. No internal tasks
-4. Delegation
- 4-1. Model of delegation
- 4-2. Common ancestor rule
-5. Other Changes
- 5-1. [Un]populated Notification
- 5-2. Other Core Changes
- 5-3. Controller File Conventions
- 5-3-1. Format
- 5-3-2. Control Knobs
- 5-4. Per-Controller Changes
- 5-4-1. io
- 5-4-2. cpuset
- 5-4-3. memory
-6. Planned Changes
- 6-1. CAP for resource control
-
-
-1. Background
-
-cgroup allows an arbitrary number of hierarchies and each hierarchy
-can host any number of controllers. While this seems to provide a
-high level of flexibility, it isn't quite useful in practice.
-
-For example, as there is only one instance of each controller, utility
-type controllers such as freezer which can be useful in all
-hierarchies can only be used in one. The issue is exacerbated by the
-fact that controllers can't be moved around once hierarchies are
-populated. Another issue is that all controllers bound to a hierarchy
-are forced to have exactly the same view of the hierarchy. It isn't
-possible to vary the granularity depending on the specific controller.
-
-In practice, these issues heavily limit which controllers can be put
-on the same hierarchy and most configurations resort to putting each
-controller on its own hierarchy. Only closely related ones, such as
-the cpu and cpuacct controllers, make sense to put on the same
-hierarchy. This often means that userland ends up managing multiple
-similar hierarchies repeating the same steps on each hierarchy
-whenever a hierarchy management operation is necessary.
-
-Unfortunately, support for multiple hierarchies comes at a steep cost.
-Internal implementation in cgroup core proper is dazzlingly
-complicated but more importantly the support for multiple hierarchies
-restricts how cgroup is used in general and what controllers can do.
-
-There's no limit on how many hierarchies there may be, which means
-that a task's cgroup membership can't be described in finite length.
-The key may contain any varying number of entries and is unlimited in
-length, which makes it highly awkward to handle and leads to addition
-of controllers which exist only to identify membership, which in turn
-exacerbates the original problem.
-
-Also, as a controller can't have any expectation regarding what shape
-of hierarchies other controllers would be on, each controller has to
-assume that all other controllers are operating on completely
-orthogonal hierarchies. This makes it impossible, or at least very
-cumbersome, for controllers to cooperate with each other.
-
-In most use cases, putting controllers on hierarchies which are
-completely orthogonal to each other isn't necessary. What usually is
-called for is the ability to have differing levels of granularity
-depending on the specific controller. In other words, hierarchy may
-be collapsed from leaf towards root when viewed from specific
-controllers. For example, a given configuration might not care about
-how memory is distributed beyond a certain level while still wanting
-to control how CPU cycles are distributed.
-
-Unified hierarchy is the next version of cgroup interface. It aims to
-address the aforementioned issues by having more structure while
-retaining enough flexibility for most use cases. Various other
-general and controller-specific interface issues are also addressed in
-the process.
-
-
-2. Basic Operation
-
-2-1. Mounting
-
-Currently, unified hierarchy can be mounted with the following mount
-command. Note that this is still under development and scheduled to
-change soon.
-
- mount -t cgroup -o __DEVEL__sane_behavior cgroup $MOUNT_POINT
-
-All controllers which support the unified hierarchy and are not bound
-to other hierarchies are automatically bound to unified hierarchy and
-show up at the root of it. Controllers which are enabled only in the
-root of unified hierarchy can be bound to other hierarchies. This
-allows mixing unified hierarchy with the traditional multiple
-hierarchies in a fully backward compatible way.
-
-A controller can be moved across hierarchies only after the controller
-is no longer referenced in its current hierarchy. Because per-cgroup
-controller states are destroyed asynchronously and controllers may
-have lingering references, a controller may not show up immediately on
-the unified hierarchy after the final umount of the previous
-hierarchy. Similarly, a controller should be fully disabled to be
-moved out of the unified hierarchy and it may take some time for the
-disabled controller to become available for other hierarchies;
-furthermore, due to dependencies among controllers, other controllers
-may need to be disabled too.
-
-While useful for development and manual configurations, dynamically
-moving controllers between the unified and other hierarchies is
-strongly discouraged for production use. It is recommended to decide
-the hierarchies and controller associations before starting using the
-controllers.
-
-
-2-2. cgroup.subtree_control
-
-All cgroups on unified hierarchy have a "cgroup.subtree_control" file
-which governs which controllers are enabled on the children of the
-cgroup. Let's assume a hierarchy like the following.
-
- root - A - B - C
- \ D
-
-root's "cgroup.subtree_control" file determines which controllers are
-enabled on A. A's on B. B's on C and D. This coincides with the
-fact that controllers on the immediate sub-level are used to
-distribute the resources of the parent. In fact, it's natural to
-assume that resource control knobs of a child belong to its parent.
-Enabling a controller in a "cgroup.subtree_control" file declares that
-distribution of the respective resources of the cgroup will be
-controlled. Note that this means that controller enable states are
-shared among siblings.
-
-When read, the file contains a space-separated list of currently
-enabled controllers. A write to the file should contain a
-space-separated list of controllers with '+' or '-' prefixed (without
-the quotes). Controllers prefixed with '+' are enabled and '-'
-disabled. If a controller is listed multiple times, the last entry
-wins. The specific operations are executed atomically - either all
-succeed or fail.
-
-
-2-3. cgroup.controllers
-
-Read-only "cgroup.controllers" file contains a space-separated list of
-controllers which can be enabled in the cgroup's
-"cgroup.subtree_control" file.
-
-In the root cgroup, this lists controllers which are not bound to
-other hierarchies and the content changes as controllers are bound to
-and unbound from other hierarchies.
-
-In non-root cgroups, the content of this file equals that of the
-parent's "cgroup.subtree_control" file as only controllers enabled
-from the parent can be used in its children.
-
-
-3. Structural Constraints
-
-3-1. Top-down
-
-As it doesn't make sense to nest control of an uncontrolled resource,
-all non-root "cgroup.subtree_control" files can only contain
-controllers which are enabled in the parent's "cgroup.subtree_control"
-file. A controller can be enabled only if the parent has the
-controller enabled and a controller can't be disabled if one or more
-children have it enabled.
-
-
-3-2. No internal tasks
-
-One long-standing issue that cgroup faces is the competition between
-tasks belonging to the parent cgroup and its children cgroups. This
-is inherently nasty as two different types of entities compete and
-there is no agreed-upon obvious way to handle it. Different
-controllers are doing different things.
-
-The cpu controller considers tasks and cgroups as equivalents and maps
-nice levels to cgroup weights. This works for some cases but falls
-flat when children should be allocated specific ratios of CPU cycles
-and the number of internal tasks fluctuates - the ratios constantly
-change as the number of competing entities fluctuates. There also are
-other issues. The mapping from nice level to weight isn't obvious or
-universal, and there are various other knobs which simply aren't
-available for tasks.
-
-The io controller implicitly creates a hidden leaf node for each
-cgroup to host the tasks. The hidden leaf has its own copies of all
-the knobs with "leaf_" prefixed. While this allows equivalent control
-over internal tasks, it's with serious drawbacks. It always adds an
-extra layer of nesting which may not be necessary, makes the interface
-messy and significantly complicates the implementation.
-
-The memory controller currently doesn't have a way to control what
-happens between internal tasks and child cgroups and the behavior is
-not clearly defined. There have been attempts to add ad-hoc behaviors
-and knobs to tailor the behavior to specific workloads. Continuing
-this direction will lead to problems which will be extremely difficult
-to resolve in the long term.
-
-Multiple controllers struggle with internal tasks and came up with
-different ways to deal with it; unfortunately, all the approaches in
-use now are severely flawed and, furthermore, the widely different
-behaviors make cgroup as whole highly inconsistent.
-
-It is clear that this is something which needs to be addressed from
-cgroup core proper in a uniform way so that controllers don't need to
-worry about it and cgroup as a whole shows a consistent and logical
-behavior. To achieve that, unified hierarchy enforces the following
-structural constraint:
-
- Except for the root, only cgroups which don't contain any task may
- have controllers enabled in their "cgroup.subtree_control" files.
-
-Combined with other properties, this guarantees that, when a
-controller is looking at the part of the hierarchy which has it
-enabled, tasks are always only on the leaves. This rules out
-situations where child cgroups compete against internal tasks of the
-parent.
-
-There are two things to note. Firstly, the root cgroup is exempt from
-the restriction. Root contains tasks and anonymous resource
-consumption which can't be associated with any other cgroup and
-requires special treatment from most controllers. How resource
-consumption in the root cgroup is governed is up to each controller.
-
-Secondly, the restriction doesn't take effect if there is no enabled
-controller in the cgroup's "cgroup.subtree_control" file. This is
-important as otherwise it wouldn't be possible to create children of a
-populated cgroup. To control resource distribution of a cgroup, the
-cgroup must create children and transfer all its tasks to the children
-before enabling controllers in its "cgroup.subtree_control" file.
-
-
-4. Delegation
-
-4-1. Model of delegation
-
-A cgroup can be delegated to a less privileged user by granting write
-access of the directory and its "cgroup.procs" file to the user. Note
-that the resource control knobs in a given directory concern the
-resources of the parent and thus must not be delegated along with the
-directory.
-
-Once delegated, the user can build sub-hierarchy under the directory,
-organize processes as it sees fit and further distribute the resources
-it got from the parent. The limits and other settings of all resource
-controllers are hierarchical and regardless of what happens in the
-delegated sub-hierarchy, nothing can escape the resource restrictions
-imposed by the parent.
-
-Currently, cgroup doesn't impose any restrictions on the number of
-cgroups in or nesting depth of a delegated sub-hierarchy; however,
-this may in the future be limited explicitly.
-
-
-4-2. Common ancestor rule
-
-On the unified hierarchy, to write to a "cgroup.procs" file, in
-addition to the usual write permission to the file and uid match, the
-writer must also have write access to the "cgroup.procs" file of the
-common ancestor of the source and destination cgroups. This prevents
-delegatees from smuggling processes across disjoint sub-hierarchies.
-
-Let's say cgroups C0 and C1 have been delegated to user U0 who created
-C00, C01 under C0 and C10 under C1 as follows.
-
- ~~~~~~~~~~~~~ - C0 - C00
- ~ cgroup ~ \ C01
- ~ hierarchy ~
- ~~~~~~~~~~~~~ - C1 - C10
-
-C0 and C1 are separate entities in terms of resource distribution
-regardless of their relative positions in the hierarchy. The
-resources the processes under C0 are entitled to are controlled by
-C0's ancestors and may be completely different from C1. It's clear
-that the intention of delegating C0 to U0 is allowing U0 to organize
-the processes under C0 and further control the distribution of C0's
-resources.
-
-On traditional hierarchies, if a task has write access to "tasks" or
-"cgroup.procs" file of a cgroup and its uid agrees with the target, it
-can move the target to the cgroup. In the above example, U0 will not
-only be able to move processes in each sub-hierarchy but also across
-the two sub-hierarchies, effectively allowing it to violate the
-organizational and resource restrictions implied by the hierarchical
-structure above C0 and C1.
-
-On the unified hierarchy, let's say U0 wants to write the pid of a
-process which has a matching uid and is currently in C10 into
-"C00/cgroup.procs". U0 obviously has write access to the file and
-migration permission on the process; however, the common ancestor of
-the source cgroup C10 and the destination cgroup C00 is above the
-points of delegation and U0 would not have write access to its
-"cgroup.procs" and thus be denied with -EACCES.
-
-
-5. Other Changes
-
-5-1. [Un]populated Notification
-
-cgroup users often need a way to determine when a cgroup's
-subhierarchy becomes empty so that it can be cleaned up. cgroup
-currently provides release_agent for it; unfortunately, this mechanism
-is riddled with issues.
-
-- It delivers events by forking and execing a userland binary
- specified as the release_agent. This is a long deprecated method of
- notification delivery. It's extremely heavy, slow and cumbersome to
- integrate with larger infrastructure.
-
-- There is single monitoring point at the root. There's no way to
- delegate management of a subtree.
-
-- The event isn't recursive. It triggers when a cgroup doesn't have
- any tasks or child cgroups. Events for internal nodes trigger only
- after all children are removed. This again makes it impossible to
- delegate management of a subtree.
-
-- Events are filtered from the kernel side. A "notify_on_release"
- file is used to subscribe to or suppress release events. This is
- unnecessarily complicated and probably done this way because event
- delivery itself was expensive.
-
-Unified hierarchy implements "populated" field in "cgroup.events"
-interface file which can be used to monitor whether the cgroup's
-subhierarchy has tasks in it or not. Its value is 0 if there is no
-task in the cgroup and its descendants; otherwise, 1. poll and
-[id]notify events are triggered when the value changes.
-
-This is significantly lighter and simpler and trivially allows
-delegating management of subhierarchy - subhierarchy monitoring can
-block further propagation simply by putting itself or another process
-in the subhierarchy and monitor events that it's interested in from
-there without interfering with monitoring higher in the tree.
-
-In unified hierarchy, the release_agent mechanism is no longer
-supported and the interface files "release_agent" and
-"notify_on_release" do not exist.
-
-
-5-2. Other Core Changes
-
-- None of the mount options is allowed.
-
-- remount is disallowed.
-
-- rename(2) is disallowed.
-
-- The "tasks" file is removed. Everything should at process
- granularity. Use the "cgroup.procs" file instead.
-
-- The "cgroup.procs" file is not sorted. pids will be unique unless
- they got recycled in-between reads.
-
-- The "cgroup.clone_children" file is removed.
-
-- /proc/PID/cgroup keeps reporting the cgroup that a zombie belonged
- to before exiting. If the cgroup is removed before the zombie is
- reaped, " (deleted)" is appeneded to the path.
-
-
-5-3. Controller File Conventions
-
-5-3-1. Format
-
-In general, all controller files should be in one of the following
-formats whenever possible.
-
-- Values only files
-
- VAL0 VAL1...\n
-
-- Flat keyed files
-
- KEY0 VAL0\n
- KEY1 VAL1\n
- ...
-
-- Nested keyed files
-
- KEY0 SUB_KEY0=VAL00 SUB_KEY1=VAL01...
- KEY1 SUB_KEY0=VAL10 SUB_KEY1=VAL11...
- ...
-
-For a writeable file, the format for writing should generally match
-reading; however, controllers may allow omitting later fields or
-implement restricted shortcuts for most common use cases.
-
-For both flat and nested keyed files, only the values for a single key
-can be written at a time. For nested keyed files, the sub key pairs
-may be specified in any order and not all pairs have to be specified.
-
-
-5-3-2. Control Knobs
-
-- Settings for a single feature should generally be implemented in a
- single file.
-
-- In general, the root cgroup should be exempt from resource control
- and thus shouldn't have resource control knobs.
-
-- If a controller implements ratio based resource distribution, the
- control knob should be named "weight" and have the range [1, 10000]
- and 100 should be the default value. The values are chosen to allow
- enough and symmetric bias in both directions while keeping it
- intuitive (the default is 100%).
-
-- If a controller implements an absolute resource guarantee and/or
- limit, the control knobs should be named "min" and "max"
- respectively. If a controller implements best effort resource
- gurantee and/or limit, the control knobs should be named "low" and
- "high" respectively.
-
- In the above four control files, the special token "max" should be
- used to represent upward infinity for both reading and writing.
-
-- If a setting has configurable default value and specific overrides,
- the default settings should be keyed with "default" and appear as
- the first entry in the file. Specific entries can use "default" as
- its value to indicate inheritance of the default value.
-
-- For events which are not very high frequency, an interface file
- "events" should be created which lists event key value pairs.
- Whenever a notifiable event happens, file modified event should be
- generated on the file.
-
-
-5-4. Per-Controller Changes
-
-5-4-1. io
-
-- blkio is renamed to io. The interface is overhauled anyway. The
- new name is more in line with the other two major controllers, cpu
- and memory, and better suited given that it may be used for cgroup
- writeback without involving block layer.
-
-- Everything including stat is always hierarchical making separate
- recursive stat files pointless and, as no internal node can have
- tasks, leaf weights are meaningless. The operation model is
- simplified and the interface is overhauled accordingly.
-
- io.stat
-
- The stat file. The reported stats are from the point where
- bio's are issued to request_queue. The stats are counted
- independent of which policies are enabled. Each line in the
- file follows the following format. More fields may later be
- added at the end.
-
- $MAJ:$MIN rbytes=$RBYTES wbytes=$WBYTES rios=$RIOS wrios=$WIOS
-
- io.weight
-
- The weight setting, currently only available and effective if
- cfq-iosched is in use for the target device. The weight is
- between 1 and 10000 and defaults to 100. The first line
- always contains the default weight in the following format to
- use when per-device setting is missing.
-
- default $WEIGHT
-
- Subsequent lines list per-device weights of the following
- format.
-
- $MAJ:$MIN $WEIGHT
-
- Writing "$WEIGHT" or "default $WEIGHT" changes the default
- setting. Writing "$MAJ:$MIN $WEIGHT" sets per-device weight
- while "$MAJ:$MIN default" clears it.
-
- This file is available only on non-root cgroups.
-
- io.max
-
- The maximum bandwidth and/or iops setting, only available if
- blk-throttle is enabled. The file is of the following format.
-
- $MAJ:$MIN rbps=$RBPS wbps=$WBPS riops=$RIOPS wiops=$WIOPS
-
- ${R|W}BPS are read/write bytes per second and ${R|W}IOPS are
- read/write IOs per second. "max" indicates no limit. Writing
- to the file follows the same format but the individual
- settings may be omitted or specified in any order.
-
- This file is available only on non-root cgroups.
-
-
-5-4-2. cpuset
-
-- Tasks are kept in empty cpusets after hotplug and take on the masks
- of the nearest non-empty ancestor, instead of being moved to it.
-
-- A task can be moved into an empty cpuset, and again it takes on the
- masks of the nearest non-empty ancestor.
-
-
-5-4-3. memory
-
-- use_hierarchy is on by default and the cgroup file for the flag is
- not created.
-
-- The original lower boundary, the soft limit, is defined as a limit
- that is per default unset. As a result, the set of cgroups that
- global reclaim prefers is opt-in, rather than opt-out. The costs
- for optimizing these mostly negative lookups are so high that the
- implementation, despite its enormous size, does not even provide the
- basic desirable behavior. First off, the soft limit has no
- hierarchical meaning. All configured groups are organized in a
- global rbtree and treated like equal peers, regardless where they
- are located in the hierarchy. This makes subtree delegation
- impossible. Second, the soft limit reclaim pass is so aggressive
- that it not just introduces high allocation latencies into the
- system, but also impacts system performance due to overreclaim, to
- the point where the feature becomes self-defeating.
-
- The memory.low boundary on the other hand is a top-down allocated
- reserve. A cgroup enjoys reclaim protection when it and all its
- ancestors are below their low boundaries, which makes delegation of
- subtrees possible. Secondly, new cgroups have no reserve per
- default and in the common case most cgroups are eligible for the
- preferred reclaim pass. This allows the new low boundary to be
- efficiently implemented with just a minor addition to the generic
- reclaim code, without the need for out-of-band data structures and
- reclaim passes. Because the generic reclaim code considers all
- cgroups except for the ones running low in the preferred first
- reclaim pass, overreclaim of individual groups is eliminated as
- well, resulting in much better overall workload performance.
-
-- The original high boundary, the hard limit, is defined as a strict
- limit that can not budge, even if the OOM killer has to be called.
- But this generally goes against the goal of making the most out of
- the available memory. The memory consumption of workloads varies
- during runtime, and that requires users to overcommit. But doing
- that with a strict upper limit requires either a fairly accurate
- prediction of the working set size or adding slack to the limit.
- Since working set size estimation is hard and error prone, and
- getting it wrong results in OOM kills, most users tend to err on the
- side of a looser limit and end up wasting precious resources.
-
- The memory.high boundary on the other hand can be set much more
- conservatively. When hit, it throttles allocations by forcing them
- into direct reclaim to work off the excess, but it never invokes the
- OOM killer. As a result, a high boundary that is chosen too
- aggressively will not terminate the processes, but instead it will
- lead to gradual performance degradation. The user can monitor this
- and make corrections until the minimal memory footprint that still
- gives acceptable performance is found.
-
- In extreme cases, with many concurrent allocations and a complete
- breakdown of reclaim progress within the group, the high boundary
- can be exceeded. But even then it's mostly better to satisfy the
- allocation from the slack available in other groups or the rest of
- the system than killing the group. Otherwise, memory.max is there
- to limit this type of spillover and ultimately contain buggy or even
- malicious applications.
-
-- The original control file names are unwieldy and inconsistent in
- many different ways. For example, the upper boundary hit count is
- exported in the memory.failcnt file, but an OOM event count has to
- be manually counted by listening to memory.oom_control events, and
- lower boundary / soft limit events have to be counted by first
- setting a threshold for that value and then counting those events.
- Also, usage and limit files encode their units in the filename.
- That makes the filenames very long, even though this is not
- information that a user needs to be reminded of every time they type
- out those names.
-
- To address these naming issues, as well as to signal clearly that
- the new interface carries a new configuration model, the naming
- conventions in it necessarily differ from the old interface.
-
-- The original limit files indicate the state of an unset limit with a
- Very High Number, and a configured limit can be unset by echoing -1
- into those files. But that very high number is implementation and
- architecture dependent and not very descriptive. And while -1 can
- be understood as an underflow into the highest possible value, -2 or
- -10M etc. do not work, so it's not consistent.
-
- memory.low, memory.high, and memory.max will use the string "max" to
- indicate and set the highest possible value.
-
-6. Planned Changes
-
-6-1. CAP for resource control
-
-Unified hierarchy will require one of the capabilities(7), which is
-yet to be decided, for all resource control related knobs. Process
-organization operations - creation of sub-cgroups and migration of
-processes in sub-hierarchies may be delegated by changing the
-ownership and/or permissions on the cgroup directory and
-"cgroup.procs" interface file; however, all operations which affect
-resource control - writes to a "cgroup.subtree_control" file or any
-controller-specific knobs - will require an explicit CAP privilege.
-
-This, in part, is to prevent the cgroup interface from being
-inadvertently promoted to programmable API used by non-privileged
-binaries. cgroup exposes various aspects of the system in ways which
-aren't properly abstracted for direct consumption by regular programs.
-This is an administration interface much closer to sysctl knobs than
-system calls. Even the basic access model, being filesystem path
-based, isn't suitable for direct consumption. There's no way to
-access "my cgroup" in a race-free way or make multiple operations
-atomic against migration to another cgroup.
-
-Another aspect is that, for better or for worse, the cgroup interface
-goes through far less scrutiny than regular interfaces for
-unprivileged userland. The upside is that cgroup is able to expose
-useful features which may not be suitable for general consumption in a
-reasonable time frame. It provides a relatively short path between
-internal details and userland-visible interface. Of course, this
-shortcut comes with high risk. We go through what we go through for
-general kernel APIs for good reasons. It may end up leaking internal
-details in a way which can exert significant pain by locking the
-kernel into a contract that can't be maintained in a reasonable
-manner.
-
-Also, due to the specific nature, cgroup and its controllers don't
-tend to attract attention from a wide scope of developers. cgroup's
-short history is already fraught with severely mis-designed
-interfaces, unnecessary commitments to and exposing of internal
-details, broken and dangerous implementations of various features.
-
-Keeping cgroup as an administration interface is both advantageous for
-its role and imperative given its nature. Some of the cgroup features
-may make sense for unprivileged access. If deemed justified, those
-must be further abstracted and implemented as a different interface,
-be it a system call or process-private filesystem, and survive through
-the scrutiny that any interface for general consumption is required to
-go through.
-
-Requiring CAP is not a complete solution but should serve as a
-significant deterrent against spraying cgroup usages in non-privileged
-programs.
diff --git a/Documentation/cpu-freq/intel-pstate.txt b/Documentation/cpu-freq/intel-pstate.txt
index be8d4006bf76..f7b12c071d53 100644
--- a/Documentation/cpu-freq/intel-pstate.txt
+++ b/Documentation/cpu-freq/intel-pstate.txt
@@ -1,61 +1,131 @@
-Intel P-state driver
+Intel P-State driver
--------------------
-This driver provides an interface to control the P state selection for
-SandyBridge+ Intel processors. The driver can operate two different
-modes based on the processor model, legacy mode and Hardware P state (HWP)
-mode.
-
-In legacy mode, the Intel P-state implements two internal governors,
-performance and powersave, that differ from the general cpufreq governors of
-the same name (the general cpufreq governors implement target(), whereas the
-internal Intel P-state governors implement setpolicy()). The internal
-performance governor sets the max_perf_pct and min_perf_pct to 100; that is,
-the governor selects the highest available P state to maximize the performance
-of the core. The internal powersave governor selects the appropriate P state
-based on the current load on the CPU.
-
-In HWP mode P state selection is implemented in the processor
-itself. The driver provides the interfaces between the cpufreq core and
-the processor to control P state selection based on user preferences
-and reporting frequency to the cpufreq core. In this mode the
-internal Intel P-state governor code is disabled.
-
-In addition to the interfaces provided by the cpufreq core for
-controlling frequency the driver provides sysfs files for
-controlling P state selection. These files have been added to
-/sys/devices/system/cpu/intel_pstate/
-
- max_perf_pct: limits the maximum P state that will be requested by
- the driver stated as a percentage of the available performance. The
- available (P states) performance may be reduced by the no_turbo
+This driver provides an interface to control the P-State selection for the
+SandyBridge+ Intel processors.
+
+The following document explains P-States:
+http://events.linuxfoundation.org/sites/events/files/slides/LinuxConEurope_2015.pdf
+As stated in the document, P-State doesn’t exactly mean a frequency. However, for
+the sake of the relationship with cpufreq, P-State and frequency are used
+interchangeably.
+
+Understanding the cpufreq core governors and policies are important before
+discussing more details about the Intel P-State driver. Based on what callbacks
+a cpufreq driver provides to the cpufreq core, it can support two types of
+drivers:
+- with target_index() callback: In this mode, the drivers using cpufreq core
+simply provide the minimum and maximum frequency limits and an additional
+interface target_index() to set the current frequency. The cpufreq subsystem
+has a number of scaling governors ("performance", "powersave", "ondemand",
+etc.). Depending on which governor is in use, cpufreq core will call for
+transitions to a specific frequency using target_index() callback.
+- setpolicy() callback: In this mode, drivers do not provide target_index()
+callback, so cpufreq core can't request a transition to a specific frequency.
+The driver provides minimum and maximum frequency limits and callbacks to set a
+policy. The policy in cpufreq sysfs is referred to as the "scaling governor".
+The cpufreq core can request the driver to operate in any of the two policies:
+"performance: and "powersave". The driver decides which frequency to use based
+on the above policy selection considering minimum and maximum frequency limits.
+
+The Intel P-State driver falls under the latter category, which implements the
+setpolicy() callback. This driver decides what P-State to use based on the
+requested policy from the cpufreq core. If the processor is capable of
+selecting its next P-State internally, then the driver will offload this
+responsibility to the processor (aka HWP: Hardware P-States). If not, the
+driver implements algorithms to select the next P-State.
+
+Since these policies are implemented in the driver, they are not same as the
+cpufreq scaling governors implementation, even if they have the same name in
+the cpufreq sysfs (scaling_governors). For example the "performance" policy is
+similar to cpufreq’s "performance" governor, but "powersave" is completely
+different than the cpufreq "powersave" governor. The strategy here is similar
+to cpufreq "ondemand", where the requested P-State is related to the system load.
+
+Sysfs Interface
+
+In addition to the frequency-controlling interfaces provided by the cpufreq
+core, the driver provides its own sysfs files to control the P-State selection.
+These files have been added to /sys/devices/system/cpu/intel_pstate/.
+Any changes made to these files are applicable to all CPUs (even in a
+multi-package system).
+
+ max_perf_pct: Limits the maximum P-State that will be requested by
+ the driver. It states it as a percentage of the available performance. The
+ available (P-State) performance may be reduced by the no_turbo
setting described below.
- min_perf_pct: limits the minimum P state that will be requested by
- the driver stated as a percentage of the max (non-turbo)
+ min_perf_pct: Limits the minimum P-State that will be requested by
+ the driver. It states it as a percentage of the max (non-turbo)
performance level.
- no_turbo: limits the driver to selecting P states below the turbo
+ no_turbo: Limits the driver to selecting P-State below the turbo
frequency range.
- turbo_pct: displays the percentage of the total performance that
- is supported by hardware that is in the turbo range. This number
+ turbo_pct: Displays the percentage of the total performance that
+ is supported by hardware that is in the turbo range. This number
is independent of whether turbo has been disabled or not.
- num_pstates: displays the number of pstates that are supported
- by hardware. This number is independent of whether turbo has
+ num_pstates: Displays the number of P-States that are supported
+ by hardware. This number is independent of whether turbo has
been disabled or not.
+For example, if a system has these parameters:
+ Max 1 core turbo ratio: 0x21 (Max 1 core ratio is the maximum P-State)
+ Max non turbo ratio: 0x17
+ Minimum ratio : 0x08 (Here the ratio is called max efficiency ratio)
+
+Sysfs will show :
+ max_perf_pct:100, which corresponds to 1 core ratio
+ min_perf_pct:24, max_efficiency_ratio / max 1 Core ratio
+ no_turbo:0, turbo is not disabled
+ num_pstates:26 = (max 1 Core ratio - Max Efficiency Ratio + 1)
+ turbo_pct:39 = (max 1 core ratio - max non turbo ratio) / num_pstates
+
+Refer to "Intel® 64 and IA-32 Architectures Software Developer’s Manual
+Volume 3: System Programming Guide" to understand ratios.
+
+cpufreq sysfs for Intel P-State
+
+Since this driver registers with cpufreq, cpufreq sysfs is also presented.
+There are some important differences, which need to be considered.
+
+scaling_cur_freq: This displays the real frequency which was used during
+the last sample period instead of what is requested. Some other cpufreq driver,
+like acpi-cpufreq, displays what is requested (Some changes are on the
+way to fix this for acpi-cpufreq driver). The same is true for frequencies
+displayed at /proc/cpuinfo.
+
+scaling_governor: This displays current active policy. Since each CPU has a
+cpufreq sysfs, it is possible to set a scaling governor to each CPU. But this
+is not possible with Intel P-States, as there is one common policy for all
+CPUs. Here, the last requested policy will be applicable to all CPUs. It is
+suggested that one use the cpupower utility to change policy to all CPUs at the
+same time.
+
+scaling_setspeed: This attribute can never be used with Intel P-State.
+
+scaling_max_freq/scaling_min_freq: This interface can be used similarly to
+the max_perf_pct/min_perf_pct of Intel P-State sysfs. However since frequencies
+are converted to nearest possible P-State, this is prone to rounding errors.
+This method is not preferred to limit performance.
+
+affected_cpus: Not used
+related_cpus: Not used
+
For contemporary Intel processors, the frequency is controlled by the
-processor itself and the P-states exposed to software are related to
+processor itself and the P-State exposed to software is related to
performance levels. The idea that frequency can be set to a single
-frequency is fiction for Intel Core processors. Even if the scaling
-driver selects a single P state the actual frequency the processor
+frequency is fictional for Intel Core processors. Even if the scaling
+driver selects a single P-State, the actual frequency the processor
will run at is selected by the processor itself.
-For legacy mode debugfs files have also been added to allow tuning of
-the internal governor algorythm. These files are located at
-/sys/kernel/debug/pstate_snb/ These files are NOT present in HWP mode.
+Tuning Intel P-State driver
+
+When HWP mode is not used, debugfs files have also been added to allow the
+tuning of the internal governor algorithm. These files are located at
+/sys/kernel/debug/pstate_snb/. The algorithm uses a PID (Proportional
+Integral Derivative) controller. The PID tunable parameters are:
deadband
d_gain_pct
@@ -63,3 +133,90 @@ the internal governor algorythm. These files are located at
p_gain_pct
sample_rate_ms
setpoint
+
+To adjust these parameters, some understanding of driver implementation is
+necessary. There are some tweeks described here, but be very careful. Adjusting
+them requires expert level understanding of power and performance relationship.
+These limits are only useful when the "powersave" policy is active.
+
+-To make the system more responsive to load changes, sample_rate_ms can
+be adjusted (current default is 10ms).
+-To make the system use higher performance, even if the load is lower, setpoint
+can be adjusted to a lower number. This will also lead to faster ramp up time
+to reach the maximum P-State.
+If there are no derivative and integral coefficients, The next P-State will be
+equal to:
+ current P-State - ((setpoint - current cpu load) * p_gain_pct)
+
+For example, if the current PID parameters are (Which are defaults for the core
+processors like SandyBridge):
+ deadband = 0
+ d_gain_pct = 0
+ i_gain_pct = 0
+ p_gain_pct = 20
+ sample_rate_ms = 10
+ setpoint = 97
+
+If the current P-State = 0x08 and current load = 100, this will result in the
+next P-State = 0x08 - ((97 - 100) * 0.2) = 8.6 (rounded to 9). Here the P-State
+goes up by only 1. If during next sample interval the current load doesn't
+change and still 100, then P-State goes up by one again. This process will
+continue as long as the load is more than the setpoint until the maximum P-State
+is reached.
+
+For the same load at setpoint = 60, this will result in the next P-State
+= 0x08 - ((60 - 100) * 0.2) = 16
+So by changing the setpoint from 97 to 60, there is an increase of the
+next P-State from 9 to 16. So this will make processor execute at higher
+P-State for the same CPU load. If the load continues to be more than the
+setpoint during next sample intervals, then P-State will go up again till the
+maximum P-State is reached. But the ramp up time to reach the maximum P-State
+will be much faster when the setpoint is 60 compared to 97.
+
+Debugging Intel P-State driver
+
+Event tracing
+To debug P-State transition, the Linux event tracing interface can be used.
+There are two specific events, which can be enabled (Provided the kernel
+configs related to event tracing are enabled).
+
+# cd /sys/kernel/debug/tracing/
+# echo 1 > events/power/pstate_sample/enable
+# echo 1 > events/power/cpu_frequency/enable
+# cat trace
+gnome-terminal--4510 [001] ..s. 1177.680733: pstate_sample: core_busy=107
+ scaled=94 from=26 to=26 mperf=1143818 aperf=1230607 tsc=29838618
+ freq=2474476
+cat-5235 [002] ..s. 1177.681723: cpu_frequency: state=2900000 cpu_id=2
+
+
+Using ftrace
+
+If function level tracing is required, the Linux ftrace interface can be used.
+For example if we want to check how often a function to set a P-State is
+called, we can set ftrace filter to intel_pstate_set_pstate.
+
+# cd /sys/kernel/debug/tracing/
+# cat available_filter_functions | grep -i pstate
+intel_pstate_set_pstate
+intel_pstate_cpu_init
+...
+
+# echo intel_pstate_set_pstate > set_ftrace_filter
+# echo function > current_tracer
+# cat trace | head -15
+# tracer: function
+#
+# entries-in-buffer/entries-written: 80/80 #P:4
+#
+# _-----=> irqs-off
+# / _----=> need-resched
+# | / _---=> hardirq/softirq
+# || / _--=> preempt-depth
+# ||| / delay
+# TASK-PID CPU# |||| TIMESTAMP FUNCTION
+# | | | |||| | |
+ Xorg-3129 [000] ..s. 2537.644844: intel_pstate_set_pstate <-intel_pstate_timer_func
+ gnome-terminal--4510 [002] ..s. 2537.649844: intel_pstate_set_pstate <-intel_pstate_timer_func
+ gnome-shell-3409 [001] ..s. 2537.650850: intel_pstate_set_pstate <-intel_pstate_timer_func
+ <idle>-0 [000] ..s. 2537.654843: intel_pstate_set_pstate <-intel_pstate_timer_func
diff --git a/Documentation/cpu-freq/pcc-cpufreq.txt b/Documentation/cpu-freq/pcc-cpufreq.txt
index 9e3c3b33514c..0a94224ad296 100644
--- a/Documentation/cpu-freq/pcc-cpufreq.txt
+++ b/Documentation/cpu-freq/pcc-cpufreq.txt
@@ -159,8 +159,8 @@ to be strictly associated with a P-state.
2.2 cpuinfo_transition_latency:
-------------------------------
-The cpuinfo_transition_latency field is 0. The PCC specification does
-not include a field to expose this value currently.
+The cpuinfo_transition_latency field is CPUFREQ_ETERNAL. The PCC specification
+does not include a field to expose this value currently.
2.3 cpuinfo_cur_freq:
---------------------
diff --git a/Documentation/device-mapper/verity.txt b/Documentation/device-mapper/verity.txt
index e15bc1a0fb98..89fd8f9a259f 100644
--- a/Documentation/device-mapper/verity.txt
+++ b/Documentation/device-mapper/verity.txt
@@ -18,11 +18,11 @@ Construction Parameters
0 is the original format used in the Chromium OS.
The salt is appended when hashing, digests are stored continuously and
- the rest of the block is padded with zeros.
+ the rest of the block is padded with zeroes.
1 is the current format that should be used for new devices.
The salt is prepended when hashing and each digest is
- padded with zeros to the power of two.
+ padded with zeroes to the power of two.
<dev>
This is the device containing data, the integrity of which needs to be
@@ -79,6 +79,37 @@ restart_on_corruption
not compatible with ignore_corruption and requires user space support to
avoid restart loops.
+ignore_zero_blocks
+ Do not verify blocks that are expected to contain zeroes and always return
+ zeroes instead. This may be useful if the partition contains unused blocks
+ that are not guaranteed to contain zeroes.
+
+use_fec_from_device <fec_dev>
+ Use forward error correction (FEC) to recover from corruption if hash
+ verification fails. Use encoding data from the specified device. This
+ may be the same device where data and hash blocks reside, in which case
+ fec_start must be outside data and hash areas.
+
+ If the encoding data covers additional metadata, it must be accessible
+ on the hash device after the hash blocks.
+
+ Note: block sizes for data and hash devices must match. Also, if the
+ verity <dev> is encrypted the <fec_dev> should be too.
+
+fec_roots <num>
+ Number of generator roots. This equals to the number of parity bytes in
+ the encoding data. For example, in RS(M, N) encoding, the number of roots
+ is M-N.
+
+fec_blocks <num>
+ The number of encoding data blocks on the FEC device. The block size for
+ the FEC device is <data_block_size>.
+
+fec_start <offset>
+ This is the offset, in <data_block_size> blocks, from the start of the
+ FEC device to the beginning of the encoding data.
+
+
Theory of operation
===================
@@ -98,6 +129,11 @@ per-block basis. This allows for a lightweight hash computation on first read
into the page cache. Block hashes are stored linearly, aligned to the nearest
block size.
+If forward error correction (FEC) support is enabled any recovery of
+corrupted data will be verified using the cryptographic hash of the
+corresponding data. This is why combining error correction with
+integrity checking is essential.
+
Hash Tree
---------
diff --git a/Documentation/devicetree/bindings/arm/arm,scpi.txt b/Documentation/devicetree/bindings/arm/arm,scpi.txt
index 86302de67c2c..313dabdc14f9 100644
--- a/Documentation/devicetree/bindings/arm/arm,scpi.txt
+++ b/Documentation/devicetree/bindings/arm/arm,scpi.txt
@@ -63,7 +63,7 @@ Required properties:
- compatible : should be "arm,juno-sram-ns" for Non-secure SRAM on Juno
The rest of the properties should follow the generic mmio-sram description
-found in ../../misc/sysram.txt
+found in ../../sram/sram.txt
Each sub-node represents the reserved area for SCPI.
diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt
index 3a07a87fef20..c352c11bd641 100644
--- a/Documentation/devicetree/bindings/arm/cpus.txt
+++ b/Documentation/devicetree/bindings/arm/cpus.txt
@@ -157,6 +157,7 @@ nodes to be present and contain the properties described below.
"arm,cortex-a17"
"arm,cortex-a53"
"arm,cortex-a57"
+ "arm,cortex-a72"
"arm,cortex-m0"
"arm,cortex-m0+"
"arm,cortex-m1"
@@ -242,6 +243,23 @@ nodes to be present and contain the properties described below.
Definition: Specifies the syscon node controlling the cpu core
power domains.
+ - dynamic-power-coefficient
+ Usage: optional
+ Value type: <prop-encoded-array>
+ Definition: A u32 value that represents the running time dynamic
+ power coefficient in units of mW/MHz/uVolt^2. The
+ coefficient can either be calculated from power
+ measurements or derived by analysis.
+
+ The dynamic power consumption of the CPU is
+ proportional to the square of the Voltage (V) and
+ the clock frequency (f). The coefficient is used to
+ calculate the dynamic power as below -
+
+ Pdyn = dynamic-power-coefficient * V^2 * f
+
+ where voltage is in uV, frequency is in MHz.
+
Example 1 (dual-cluster big.LITTLE system 32-bit):
cpus {
diff --git a/Documentation/devicetree/bindings/arm/l2cc.txt b/Documentation/devicetree/bindings/arm/l2c2x0.txt
index 06c88a4d28ac..fe0398c5c77b 100644
--- a/Documentation/devicetree/bindings/arm/l2cc.txt
+++ b/Documentation/devicetree/bindings/arm/l2c2x0.txt
@@ -1,7 +1,8 @@
* ARM L2 Cache Controller
-ARM cores often have a separate level 2 cache controller. There are various
-implementations of the L2 cache controller with compatible programming models.
+ARM cores often have a separate L2C210/L2C220/L2C310 (also known as PL210/PL220/
+PL310 and variants) based level 2 cache controller. All these various implementations
+of the L2 cache controller have compatible programming models (Note 1).
Some of the properties that are just prefixed "cache-*" are taken from section
3.7.3 of the ePAPR v1.1 specification which can be found at:
https://www.power.org/wp-content/uploads/2012/06/Power_ePAPR_APPROVED_v1.1.pdf
@@ -67,12 +68,17 @@ Optional properties:
disable if zero.
- arm,prefetch-offset : Override prefetch offset value. Valid values are
0-7, 15, 23, and 31.
-- arm,shared-override : The default behavior of the pl310 cache controller with
- respect to the shareable attribute is to transform "normal memory
- non-cacheable transactions" into "cacheable no allocate" (for reads) or
- "write through no write allocate" (for writes).
+- arm,shared-override : The default behavior of the L220 or PL310 cache
+ controllers with respect to the shareable attribute is to transform "normal
+ memory non-cacheable transactions" into "cacheable no allocate" (for reads)
+ or "write through no write allocate" (for writes).
On systems where this may cause DMA buffer corruption, this property must be
specified to indicate that such transforms are precluded.
+- arm,parity-enable : enable parity checking on the L2 cache (L220 or PL310).
+- arm,parity-disable : disable parity checking on the L2 cache (L220 or PL310).
+- arm,outer-sync-disable : disable the outer sync operation on the L2 cache.
+ Some core tiles, especially ARM PB11MPCore have a faulty L220 cache that
+ will randomly hang unless outer sync operations are disabled.
- prefetch-data : Data prefetch. Value: <0> (forcibly disable), <1>
(forcibly enable), property absent (retain settings set by firmware)
- prefetch-instr : Instruction prefetch. Value: <0> (forcibly disable),
@@ -91,3 +97,9 @@ L2: cache-controller {
cache-level = <2>;
interrupts = <45>;
};
+
+Note 1: The description in this document doesn't apply to integrated L2
+ cache controllers as found in e.g. Cortex-A15/A7/A57/A53. These
+ integrated L2 controllers are assumed to be all preconfigured by
+ early secure boot code. Thus no need to deal with their configuration
+ in the kernel at all.
diff --git a/Documentation/devicetree/bindings/arm/pmu.txt b/Documentation/devicetree/bindings/arm/pmu.txt
index 97ba45af04fc..56518839f52a 100644
--- a/Documentation/devicetree/bindings/arm/pmu.txt
+++ b/Documentation/devicetree/bindings/arm/pmu.txt
@@ -9,8 +9,9 @@ Required properties:
- compatible : should be one of
"apm,potenza-pmu"
"arm,armv8-pmuv3"
- "arm.cortex-a57-pmu"
- "arm.cortex-a53-pmu"
+ "arm,cortex-a72-pmu"
+ "arm,cortex-a57-pmu"
+ "arm,cortex-a53-pmu"
"arm,cortex-a17-pmu"
"arm,cortex-a15-pmu"
"arm,cortex-a12-pmu"
diff --git a/Documentation/devicetree/bindings/arm/psci.txt b/Documentation/devicetree/bindings/arm/psci.txt
index a9adab84e2fe..a2c4f1d52492 100644
--- a/Documentation/devicetree/bindings/arm/psci.txt
+++ b/Documentation/devicetree/bindings/arm/psci.txt
@@ -23,17 +23,20 @@ Main node required properties:
- compatible : should contain at least one of:
- * "arm,psci" : for implementations complying to PSCI versions prior to
- 0.2. For these cases function IDs must be provided.
-
- * "arm,psci-0.2" : for implementations complying to PSCI 0.2. Function
- IDs are not required and should be ignored by an OS with PSCI 0.2
- support, but are permitted to be present for compatibility with
- existing software when "arm,psci" is later in the compatible list.
-
- * "arm,psci-1.0" : for implementations complying to PSCI 1.0. PSCI 1.0 is
- backward compatible with PSCI 0.2 with minor specification updates,
- as defined in the PSCI specification[2].
+ * "arm,psci" : For implementations complying to PSCI versions prior
+ to 0.2.
+ For these cases function IDs must be provided.
+
+ * "arm,psci-0.2" : For implementations complying to PSCI 0.2.
+ Function IDs are not required and should be ignored by
+ an OS with PSCI 0.2 support, but are permitted to be
+ present for compatibility with existing software when
+ "arm,psci" is later in the compatible list.
+
+ * "arm,psci-1.0" : For implementations complying to PSCI 1.0.
+ PSCI 1.0 is backward compatible with PSCI 0.2 with
+ minor specification updates, as defined in the PSCI
+ specification[2].
- method : The method of calling the PSCI firmware. Permitted
values are:
diff --git a/Documentation/devicetree/bindings/arm/secure.txt b/Documentation/devicetree/bindings/arm/secure.txt
new file mode 100644
index 000000000000..e31303fb233a
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/secure.txt
@@ -0,0 +1,53 @@
+* ARM Secure world bindings
+
+ARM CPUs with TrustZone support have two distinct address spaces,
+"Normal" and "Secure". Most devicetree consumers (including the Linux
+kernel) are not TrustZone aware and run entirely in either the Normal
+world or the Secure world. However some devicetree consumers are
+TrustZone aware and need to be able to determine whether devices are
+visible only in the Secure address space, only in the Normal address
+space, or visible in both. (One example of that situation would be a
+virtual machine which boots Secure firmware and wants to tell the
+firmware about the layout of the machine via devicetree.)
+
+The general principle of the naming scheme for Secure world bindings
+is that any property that needs a different value in the Secure world
+can be supported by prefixing the property name with "secure-". So for
+instance "secure-foo" would override "foo". For property names with
+a vendor prefix, the Secure variant of "vendor,foo" would be
+"vendor,secure-foo". If there is no "secure-" property then the Secure
+world value is the same as specified for the Normal world by the
+non-prefixed property. However, only the properties listed below may
+validly have "secure-" versions; this list will be enlarged on a
+case-by-case basis.
+
+Defining the bindings in this way means that a device tree which has
+been annotated to indicate the presence of Secure-only devices can
+still be processed unmodified by existing Non-secure software (and in
+particular by the kernel).
+
+Note that it is still valid for bindings intended for purely Secure
+world consumers (like kernels that run entirely in Secure) to simply
+describe the view of Secure world using the standard bindings. These
+secure- bindings only need to be used where both the Secure and Normal
+world views need to be described in a single device tree.
+
+Valid Secure world properties:
+
+- secure-status : specifies whether the device is present and usable
+ in the secure world. The combination of this with "status" allows
+ the various possible combinations of device visibility to be
+ specified. If "secure-status" is not specified it defaults to the
+ same value as "status"; if "status" is not specified either then
+ both default to "okay". This means the following combinations are
+ possible:
+
+ /* Neither specified: default to visible in both S and NS */
+ secure-status = "okay"; /* visible in both */
+ status = "okay"; /* visible in both */
+ status = "okay"; secure-status = "okay"; /* visible in both */
+ secure-status = "disabled"; /* NS-only */
+ status = "okay"; secure-status = "disabled"; /* NS-only */
+ status = "disabled"; secure-status = "okay"; /* S-only */
+ status = "disabled"; /* disabled in both */
+ status = "disabled"; secure-status = "disabled"; /* disabled in both */
diff --git a/Documentation/devicetree/bindings/ata/brcm,sata-brcmstb.txt b/Documentation/devicetree/bindings/ata/brcm,sata-brcmstb.txt
index 20ac9bbfa1fd..60872838f1ad 100644
--- a/Documentation/devicetree/bindings/ata/brcm,sata-brcmstb.txt
+++ b/Documentation/devicetree/bindings/ata/brcm,sata-brcmstb.txt
@@ -4,7 +4,9 @@ SATA nodes are defined to describe on-chip Serial ATA controllers.
Each SATA controller should have its own node.
Required properties:
-- compatible : compatible list, may contain "brcm,bcm7445-ahci" and/or
+- compatible : should be one or more of
+ "brcm,bcm7425-ahci"
+ "brcm,bcm7445-ahci"
"brcm,sata3-ahci"
- reg : register mappings for AHCI and SATA_TOP_CTRL
- reg-names : "ahci" and "top-ctrl"
diff --git a/Documentation/devicetree/bindings/ata/sata_rcar.txt b/Documentation/devicetree/bindings/ata/sata_rcar.txt
index 2493a5a31655..0764f9ab63dc 100644
--- a/Documentation/devicetree/bindings/ata/sata_rcar.txt
+++ b/Documentation/devicetree/bindings/ata/sata_rcar.txt
@@ -8,6 +8,7 @@ Required properties:
- "renesas,sata-r8a7790" for R-Car H2 other than ES1
- "renesas,sata-r8a7791" for R-Car M2-W
- "renesas,sata-r8a7793" for R-Car M2-N
+ - "renesas,sata-r8a7795" for R-Car H3
- reg : address and length of the SATA registers;
- interrupts : must consist of one interrupt specifier.
- clocks : must contain a reference to the functional clock.
diff --git a/Documentation/devicetree/bindings/clock/samsung,s2mps11.txt b/Documentation/devicetree/bindings/clock/samsung,s2mps11.txt
new file mode 100644
index 000000000000..2726c1d58a79
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/samsung,s2mps11.txt
@@ -0,0 +1,49 @@
+Binding for Samsung S2M and S5M family clock generator block
+============================================================
+
+This is a part of device tree bindings for S2M and S5M family multi-function
+devices.
+More information can be found in bindings/mfd/sec-core.txt file.
+
+The S2MPS11/13/15 and S5M8767 provide three(AP/CP/BT) buffered 32.768 kHz
+outputs. The S2MPS14 provides two (AP/BT) buffered 32.768 KHz outputs.
+
+To register these as clocks with common clock framework instantiate under
+main device node a sub-node named "clocks".
+
+It uses the common clock binding documented in:
+ - Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+
+Required properties of the "clocks" sub-node:
+ - #clock-cells: should be 1.
+ - compatible: Should be one of: "samsung,s2mps11-clk", "samsung,s2mps13-clk",
+ "samsung,s2mps14-clk", "samsung,s5m8767-clk"
+ The S2MPS15 uses the same compatible as S2MPS13, as both provides similar
+ clocks.
+
+
+Each clock is assigned an identifier and client nodes use this identifier
+to specify the clock which they consume.
+ Clock ID Devices
+ ----------------------------------------------------------
+ 32KhzAP 0 S2MPS11/13/14/15, S5M8767
+ 32KhzCP 1 S2MPS11/13/15, S5M8767
+ 32KhzBT 2 S2MPS11/13/14/15, S5M8767
+
+Include dt-bindings/clock/samsung,s2mps11.h file to use preprocessor defines
+in device tree sources.
+
+
+Example:
+
+ s2mps11_pmic@66 {
+ compatible = "samsung,s2mps11-pmic";
+ reg = <0x66>;
+
+ s2m_osc: clocks {
+ compatible = "samsung,s2mps11-clk";
+ #clock-cells = <1>;
+ clock-output-names = "xx", "yy", "zz";
+ };
+ };
diff --git a/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt b/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt
index 0715695e94a9..2aa06ac0fac5 100644
--- a/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt
+++ b/Documentation/devicetree/bindings/cpufreq/arm_big_little_dt.txt
@@ -12,7 +12,7 @@ must be present contiguously. Generic DT driver will check only node 'x' for
cpu:x.
Required properties:
-- operating-points: Refer to Documentation/devicetree/bindings/power/opp.txt
+- operating-points: Refer to Documentation/devicetree/bindings/opp/opp.txt
for details
Optional properties:
diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt
index e41c98ffbccb..dd3929e85dec 100644
--- a/Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt
+++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-dt.txt
@@ -11,7 +11,7 @@ Required properties:
- None
Optional properties:
-- operating-points: Refer to Documentation/devicetree/bindings/power/opp.txt for
+- operating-points: Refer to Documentation/devicetree/bindings/opp/opp.txt for
details. OPPs *must* be supplied either via DT, i.e. this property, or
populated at runtime.
- clock-latency: Specify the possible maximum transition latency for clock,
diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-st.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-st.txt
new file mode 100644
index 000000000000..d91a02a3b6b0
--- /dev/null
+++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-st.txt
@@ -0,0 +1,91 @@
+Binding for ST's CPUFreq driver
+===============================
+
+ST's CPUFreq driver attempts to read 'process' and 'version' attributes
+from the SoC, then supplies the OPP framework with 'prop' and 'supported
+hardware' information respectively. The framework is then able to read
+the DT and operate in the usual way.
+
+For more information about the expected DT format [See: ../opp/opp.txt].
+
+Frequency Scaling only
+----------------------
+
+No vendor specific driver required for this.
+
+Located in CPU's node:
+
+- operating-points : [See: ../power/opp.txt]
+
+Example [safe]
+--------------
+
+cpus {
+ cpu@0 {
+ /* kHz uV */
+ operating-points = <1500000 0
+ 1200000 0
+ 800000 0
+ 500000 0>;
+ };
+};
+
+Dynamic Voltage and Frequency Scaling (DVFS)
+--------------------------------------------
+
+This requires the ST CPUFreq driver to supply 'process' and 'version' info.
+
+Located in CPU's node:
+
+- operating-points-v2 : [See ../power/opp.txt]
+
+Example [unsafe]
+----------------
+
+cpus {
+ cpu@0 {
+ operating-points-v2 = <&cpu0_opp_table>;
+ };
+};
+
+cpu0_opp_table: opp_table {
+ compatible = "operating-points-v2";
+
+ /* ############################################################### */
+ /* # WARNING: Do not attempt to copy/replicate these nodes, # */
+ /* # they are only to be supplied by the bootloader !!! # */
+ /* ############################################################### */
+ opp0 {
+ /* Major Minor Substrate */
+ /* 2 all all */
+ opp-supported-hw = <0x00000004 0xffffffff 0xffffffff>;
+ opp-hz = /bits/ 64 <1500000000>;
+ clock-latency-ns = <10000000>;
+
+ opp-microvolt-pcode0 = <1200000>;
+ opp-microvolt-pcode1 = <1200000>;
+ opp-microvolt-pcode2 = <1200000>;
+ opp-microvolt-pcode3 = <1200000>;
+ opp-microvolt-pcode4 = <1170000>;
+ opp-microvolt-pcode5 = <1140000>;
+ opp-microvolt-pcode6 = <1100000>;
+ opp-microvolt-pcode7 = <1070000>;
+ };
+
+ opp1 {
+ /* Major Minor Substrate */
+ /* all all all */
+ opp-supported-hw = <0xffffffff 0xffffffff 0xffffffff>;
+ opp-hz = /bits/ 64 <1200000000>;
+ clock-latency-ns = <10000000>;
+
+ opp-microvolt-pcode0 = <1110000>;
+ opp-microvolt-pcode1 = <1150000>;
+ opp-microvolt-pcode2 = <1100000>;
+ opp-microvolt-pcode3 = <1080000>;
+ opp-microvolt-pcode4 = <1040000>;
+ opp-microvolt-pcode5 = <1020000>;
+ opp-microvolt-pcode6 = <980000>;
+ opp-microvolt-pcode7 = <930000>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/crypto/rockchip-crypto.txt b/Documentation/devicetree/bindings/crypto/rockchip-crypto.txt
new file mode 100644
index 000000000000..096df34b11c1
--- /dev/null
+++ b/Documentation/devicetree/bindings/crypto/rockchip-crypto.txt
@@ -0,0 +1,29 @@
+Rockchip Electronics And Security Accelerator
+
+Required properties:
+- compatible: Should be "rockchip,rk3288-crypto"
+- reg: Base physical address of the engine and length of memory mapped
+ region
+- interrupts: Interrupt number
+- clocks: Reference to the clocks about crypto
+- clock-names: "aclk" used to clock data
+ "hclk" used to clock data
+ "sclk" used to clock crypto accelerator
+ "apb_pclk" used to clock dma
+- resets: Must contain an entry for each entry in reset-names.
+ See ../reset/reset.txt for details.
+- reset-names: Must include the name "crypto-rst".
+
+Examples:
+
+ crypto: cypto-controller@ff8a0000 {
+ compatible = "rockchip,rk3288-crypto";
+ reg = <0xff8a0000 0x4000>;
+ interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru ACLK_CRYPTO>, <&cru HCLK_CRYPTO>,
+ <&cru SCLK_CRYPTO>, <&cru ACLK_DMAC1>;
+ clock-names = "aclk", "hclk", "sclk", "apb_pclk";
+ resets = <&cru SRST_CRYPTO>;
+ reset-names = "crypto-rst";
+ status = "okay";
+ };
diff --git a/Documentation/devicetree/bindings/display/bridge/tda998x.txt b/Documentation/devicetree/bindings/display/bridge/tda998x.txt
index e9e4bce40760..e178e6b9f9ee 100644
--- a/Documentation/devicetree/bindings/display/bridge/tda998x.txt
+++ b/Documentation/devicetree/bindings/display/bridge/tda998x.txt
@@ -5,6 +5,10 @@ Required properties;
- reg: I2C address
+Required node:
+ - port: Input port node with endpoint definition, as described
+ in Documentation/devicetree/bindings/graph.txt
+
Optional properties:
- interrupts: interrupt number and trigger type
default: polling
diff --git a/Documentation/devicetree/bindings/dma/renesas,usb-dmac.txt b/Documentation/devicetree/bindings/dma/renesas,usb-dmac.txt
index 040f365954cc..e7780a186a36 100644
--- a/Documentation/devicetree/bindings/dma/renesas,usb-dmac.txt
+++ b/Documentation/devicetree/bindings/dma/renesas,usb-dmac.txt
@@ -1,7 +1,13 @@
* Renesas USB DMA Controller Device Tree bindings
Required Properties:
-- compatible: must contain "renesas,usb-dmac"
+-compatible: "renesas,<soctype>-usb-dmac", "renesas,usb-dmac" as fallback.
+ Examples with soctypes are:
+ - "renesas,r8a7790-usb-dmac" (R-Car H2)
+ - "renesas,r8a7791-usb-dmac" (R-Car M2-W)
+ - "renesas,r8a7793-usb-dmac" (R-Car M2-N)
+ - "renesas,r8a7794-usb-dmac" (R-Car E2)
+ - "renesas,r8a7795-usb-dmac" (R-Car H3)
- reg: base address and length of the registers block for the DMAC
- interrupts: interrupt specifiers for the DMAC, one for each entry in
interrupt-names.
@@ -15,7 +21,7 @@ Required Properties:
Example: R8A7790 (R-Car H2) USB-DMACs
usb_dmac0: dma-controller@e65a0000 {
- compatible = "renesas,usb-dmac";
+ compatible = "renesas,r8a7790-usb-dmac", "renesas,usb-dmac";
reg = <0 0xe65a0000 0 0x100>;
interrupts = <0 109 IRQ_TYPE_LEVEL_HIGH
0 109 IRQ_TYPE_LEVEL_HIGH>;
diff --git a/Documentation/devicetree/bindings/dma/stm32-dma.txt b/Documentation/devicetree/bindings/dma/stm32-dma.txt
new file mode 100644
index 000000000000..70cd13f1588a
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/stm32-dma.txt
@@ -0,0 +1,82 @@
+* STMicroelectronics STM32 DMA controller
+
+The STM32 DMA is a general-purpose direct memory access controller capable of
+supporting 8 independent DMA channels. Each channel can have up to 8 requests.
+
+Required properties:
+- compatible: Should be "st,stm32-dma"
+- reg: Should contain DMA registers location and length. This should include
+ all of the per-channel registers.
+- interrupts: Should contain all of the per-channel DMA interrupts in
+ ascending order with respect to the DMA channel index.
+- clocks: Should contain the input clock of the DMA instance.
+- #dma-cells : Must be <4>. See DMA client paragraph for more details.
+
+Optional properties:
+- resets: Reference to a reset controller asserting the DMA controller
+- st,mem2mem: boolean; if defined, it indicates that the controller supports
+ memory-to-memory transfer
+
+Example:
+
+ dma2: dma-controller@40026400 {
+ compatible = "st,stm32-dma";
+ reg = <0x40026400 0x400>;
+ interrupts = <56>,
+ <57>,
+ <58>,
+ <59>,
+ <60>,
+ <68>,
+ <69>,
+ <70>;
+ clocks = <&clk_hclk>;
+ #dma-cells = <4>;
+ st,mem2mem;
+ resets = <&rcc 150>;
+ };
+
+* DMA client
+
+DMA clients connected to the STM32 DMA controller must use the format
+described in the dma.txt file, using a five-cell specifier for each
+channel: a phandle plus four integer cells.
+The four cells in order are:
+
+1. The channel id
+2. The request line number
+3. A 32bit mask specifying the DMA channel configuration which are device
+ dependent:
+ -bit 9: Peripheral Increment Address
+ 0x0: no address increment between transfers
+ 0x1: increment address between transfers
+ -bit 10: Memory Increment Address
+ 0x0: no address increment between transfers
+ 0x1: increment address between transfers
+ -bit 15: Peripheral Increment Offset Size
+ 0x0: offset size is linked to the peripheral bus width
+ 0x1: offset size is fixed to 4 (32-bit alignment)
+ -bit 16-17: Priority level
+ 0x0: low
+ 0x1: medium
+ 0x2: high
+ 0x3: very high
+5. A 32bit mask specifying the DMA FIFO threshold configuration which are device
+ dependent:
+ -bit 0-1: Fifo threshold
+ 0x0: 1/4 full FIFO
+ 0x1: 1/2 full FIFO
+ 0x2: 3/4 full FIFO
+ 0x3: full FIFO
+
+Example:
+
+ usart1: serial@40011000 {
+ compatible = "st,stm32-usart", "st,stm32-uart";
+ reg = <0x40011000 0x400>;
+ interrupts = <37>;
+ clocks = <&clk_pclk2>;
+ dmas = <&dma2 2 4 0x10400 0x3>,
+ <&dma2 7 5 0x10200 0x3>;
+ dma-names = "rx", "tx";
+ };
diff --git a/Documentation/devicetree/bindings/dma/ti-dma-crossbar.txt b/Documentation/devicetree/bindings/dma/ti-dma-crossbar.txt
index b152a75dceae..aead5869a28d 100644
--- a/Documentation/devicetree/bindings/dma/ti-dma-crossbar.txt
+++ b/Documentation/devicetree/bindings/dma/ti-dma-crossbar.txt
@@ -14,6 +14,10 @@ The DMA controller node need to have the following poroperties:
Optional properties:
- ti,dma-safe-map: Safe routing value for unused request lines
+- ti,reserved-dma-request-ranges: DMA request ranges which should not be used
+ when mapping xbar input to DMA request, they are either
+ allocated to be used by for example the DSP or they are used as
+ memcpy channels in eDMA.
Notes:
When requesting channel via ti,dra7-dma-crossbar, the DMA clinet must request
@@ -46,6 +50,8 @@ sdma_xbar: dma-router@4a002b78 {
#dma-cells = <1>;
dma-requests = <205>;
ti,dma-safe-map = <0>;
+ /* Protect the sDMA request ranges: 10-14 and 100-126 */
+ ti,reserved-dma-request-ranges = <10 5>, <100 27>;
dma-masters = <&sdma>;
};
diff --git a/Documentation/devicetree/bindings/dma/ti-edma.txt b/Documentation/devicetree/bindings/dma/ti-edma.txt
index d3d0a4fb1c73..079b42a81d7c 100644
--- a/Documentation/devicetree/bindings/dma/ti-edma.txt
+++ b/Documentation/devicetree/bindings/dma/ti-edma.txt
@@ -22,8 +22,7 @@ Required properties:
Optional properties:
- ti,hwmods: Name of the hwmods associated to the eDMA CC
- ti,edma-memcpy-channels: List of channels allocated to be used for memcpy, iow
- these channels will be SW triggered channels. The list must
- contain 16 bits numbers, see example.
+ these channels will be SW triggered channels. See example.
- ti,edma-reserved-slot-ranges: PaRAM slot ranges which should not be used by
the driver, they are allocated to be used by for example the
DSP. See example.
@@ -56,10 +55,9 @@ edma: edma@49000000 {
ti,tptcs = <&edma_tptc0 7>, <&edma_tptc1 7>, <&edma_tptc2 0>;
/* Channel 20 and 21 is allocated for memcpy */
- ti,edma-memcpy-channels = /bits/ 16 <20 21>;
- /* The following PaRAM slots are reserved: 35-45 and 100-110 */
- ti,edma-reserved-slot-ranges = /bits/ 16 <35 10>,
- /bits/ 16 <100 10>;
+ ti,edma-memcpy-channels = <20 21>;
+ /* The following PaRAM slots are reserved: 35-44 and 100-109 */
+ ti,edma-reserved-slot-ranges = <35 10>, <100 10>;
};
edma_tptc0: tptc@49800000 {
diff --git a/Documentation/devicetree/bindings/eeprom/eeprom.txt b/Documentation/devicetree/bindings/eeprom/eeprom.txt
index 4342c10de1bf..735bc94444bb 100644
--- a/Documentation/devicetree/bindings/eeprom/eeprom.txt
+++ b/Documentation/devicetree/bindings/eeprom/eeprom.txt
@@ -2,11 +2,22 @@ EEPROMs (I2C)
Required properties:
- - compatible : should be "<manufacturer>,<type>"
- If there is no specific driver for <manufacturer>, a generic
- driver based on <type> is selected. Possible types are:
- 24c00, 24c01, 24c02, 24c04, 24c08, 24c16, 24c32, 24c64,
- 24c128, 24c256, 24c512, 24c1024, spd
+ - compatible : should be "<manufacturer>,<type>", like these:
+
+ "atmel,24c00", "atmel,24c01", "atmel,24c02", "atmel,24c04",
+ "atmel,24c08", "atmel,24c16", "atmel,24c32", "atmel,24c64",
+ "atmel,24c128", "atmel,24c256", "atmel,24c512", "atmel,24c1024"
+
+ "catalyst,24c32"
+
+ "ramtron,24c64"
+
+ "renesas,r1ex24002"
+
+ If there is no specific driver for <manufacturer>, a generic
+ driver based on <type> is selected. Possible types are:
+ "24c00", "24c01", "24c02", "24c04", "24c08", "24c16", "24c32", "24c64",
+ "24c128", "24c256", "24c512", "24c1024", "spd"
- reg : the I2C address of the EEPROM
diff --git a/Documentation/devicetree/bindings/extcon/extcon-arizona.txt b/Documentation/devicetree/bindings/extcon/extcon-arizona.txt
index e1705fae63a8..e27341f8a4c7 100644
--- a/Documentation/devicetree/bindings/extcon/extcon-arizona.txt
+++ b/Documentation/devicetree/bindings/extcon/extcon-arizona.txt
@@ -13,3 +13,63 @@ Optional properties:
ARIZONA_ACCDET_MODE_HPR or 2 - Headphone detect mode is set to HPDETR
If this node is not mentioned or if the value is unknown, then
headphone detection mode is set to HPDETL.
+
+ - wlf,use-jd2 : Use the additional JD input along with JD1 for dual pin jack
+ detection.
+ - wlf,use-jd2-nopull : Internal pull on JD2 is disabled when used for
+ jack detection.
+ - wlf,jd-invert : Invert the polarity of the jack detection switch
+
+ - wlf,micd-software-compare : Use a software comparison to determine mic
+ presence
+ - wlf,micd-detect-debounce : Additional software microphone detection
+ debounce specified in milliseconds.
+ - wlf,micd-pol-gpio : GPIO specifier for the GPIO controlling the headset
+ polarity if one exists.
+ - wlf,micd-bias-start-time : Time allowed for MICBIAS to startup prior to
+ performing microphone detection, specified as per the ARIZONA_MICD_TIME_XXX
+ defines.
+ - wlf,micd-rate : Delay between successive microphone detection measurements,
+ specified as per the ARIZONA_MICD_TIME_XXX defines.
+ - wlf,micd-dbtime : Microphone detection hardware debounces specified as the
+ number of measurements to take, valid values being 2 and 4.
+ - wlf,micd-timeout-ms : Timeout for microphone detection, specified in
+ milliseconds.
+ - wlf,micd-force-micbias : Force MICBIAS continuously on during microphone
+ detection.
+ - wlf,micd-configs : Headset polarity configurations (generally used for
+ detection of CTIA / OMTP headsets), the field can be of variable length
+ but should always be a multiple of 3 cells long, each three cell group
+ represents one polarity configuration.
+ The first cell defines the accessory detection pin, zero will use MICDET1
+ and all other values will use MICDET2.
+ The second cell represents the MICBIAS to be used.
+ The third cell represents the value of the micd-pol-gpio pin.
+
+ - wlf,gpsw : Settings for the general purpose switch
+
+Example:
+
+codec: wm8280@0 {
+ compatible = "wlf,wm8280";
+ reg = <0>;
+ ...
+
+ wlf,use-jd2;
+ wlf,use-jd2-nopull;
+ wlf,jd-invert;
+
+ wlf,micd-software-compare;
+ wlf,micd-detect-debounce = <0>;
+ wlf,micd-pol-gpio = <&codec 2 0>;
+ wlf,micd-rate = <ARIZONA_MICD_TIME_8MS>;
+ wlf,micd-dbtime = <4>;
+ wlf,micd-timeout-ms = <100>;
+ wlf,micd-force-micbias;
+ wlf,micd-configs = <
+ 0 1 0 /* MICDET1 MICBIAS1 GPIO=low */
+ 1 2 1 /* MICDET2 MICBIAS2 GPIO=high */
+ >;
+
+ wlf,gpsw = <0>;
+};
diff --git a/Documentation/devicetree/bindings/extcon/extcon-max3355.txt b/Documentation/devicetree/bindings/extcon/extcon-max3355.txt
new file mode 100644
index 000000000000..f2288ea9eb82
--- /dev/null
+++ b/Documentation/devicetree/bindings/extcon/extcon-max3355.txt
@@ -0,0 +1,21 @@
+Maxim Integrated MAX3355 USB OTG chip
+-------------------------------------
+
+MAX3355 integrates a charge pump and comparators to enable a system with an
+integrated USB OTG dual-role transceiver to function as a USB OTG dual-role
+device.
+
+Required properties:
+- compatible: should be "maxim,max3355";
+- maxim,shdn-gpios: should contain a phandle and GPIO specifier for the GPIO pin
+ connected to the MAX3355's SHDN# pin;
+- id-gpios: should contain a phandle and GPIO specifier for the GPIO pin
+ connected to the MAX3355's ID_OUT pin.
+
+Example:
+
+ usb-otg {
+ compatible = "maxim,max3355";
+ maxim,shdn-gpios = <&gpio2 4 GPIO_ACTIVE_LOW>;
+ id-gpios = <&gpio5 31 GPIO_ACTIVE_HIGH>;
+ };
diff --git a/Documentation/devicetree/bindings/gpio/gpio-mpc8xxx.txt b/Documentation/devicetree/bindings/gpio/gpio-mpc8xxx.txt
index f2455c50533d..120bc4971cf3 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-mpc8xxx.txt
+++ b/Documentation/devicetree/bindings/gpio/gpio-mpc8xxx.txt
@@ -11,6 +11,10 @@ Required properties:
0 = active high
1 = active low
+Optional properties:
+- little-endian : GPIO registers are used as little endian. If not
+ present registers are used as big endian by default.
+
Example:
gpio0: gpio@1100 {
diff --git a/Documentation/devicetree/bindings/gpio/snps-dwapb-gpio.txt b/Documentation/devicetree/bindings/gpio/snps-dwapb-gpio.txt
index dd5d2c0394b1..4d6c8cdc8586 100644
--- a/Documentation/devicetree/bindings/gpio/snps-dwapb-gpio.txt
+++ b/Documentation/devicetree/bindings/gpio/snps-dwapb-gpio.txt
@@ -24,7 +24,7 @@ controller.
- #interrupt-cells : Specifies the number of cells needed to encode an
interrupt. Shall be set to 2. The first cell defines the interrupt number,
the second encodes the triger flags encoded as described in
- Documentation/devicetree/bindings/interrupts.txt
+ Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
- interrupt-parent : The parent interrupt controller.
- interrupts : The interrupt to the parent controller raised when GPIOs
generate the interrupts.
diff --git a/Documentation/devicetree/bindings/i2c/i2c-at91.txt b/Documentation/devicetree/bindings/i2c/i2c-at91.txt
index 6e81dc153f3b..ef973a0343c7 100644
--- a/Documentation/devicetree/bindings/i2c/i2c-at91.txt
+++ b/Documentation/devicetree/bindings/i2c/i2c-at91.txt
@@ -3,7 +3,7 @@ I2C for Atmel platforms
Required properties :
- compatible : Must be "atmel,at91rm9200-i2c", "atmel,at91sam9261-i2c",
"atmel,at91sam9260-i2c", "atmel,at91sam9g20-i2c", "atmel,at91sam9g10-i2c",
- "atmel,at91sam9x5-i2c" or "atmel,sama5d2-i2c"
+ "atmel,at91sam9x5-i2c", "atmel,sama5d4-i2c" or "atmel,sama5d2-i2c"
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: interrupt number to the cpu.
@@ -17,6 +17,8 @@ Optional properties:
- dma-names: should contain "tx" and "rx".
- atmel,fifo-size: maximum number of data the RX and TX FIFOs can store for FIFO
capable I2C controllers.
+- i2c-sda-hold-time-ns: TWD hold time, only available for "atmel,sama5d4-i2c"
+ and "atmel,sama5d2-i2c".
- Child nodes conforming to i2c bus binding
Examples :
@@ -52,6 +54,7 @@ i2c0: i2c@f8034600 {
#size-cells = <0>;
clocks = <&flx0>;
atmel,fifo-size = <16>;
+ i2c-sda-hold-time-ns = <336>;
wm8731: wm8731@1a {
compatible = "wm8731";
diff --git a/Documentation/devicetree/bindings/i2c/i2c-brcmstb.txt b/Documentation/devicetree/bindings/i2c/i2c-brcmstb.txt
index d6f724efdcf2..aeceaceba3c5 100644
--- a/Documentation/devicetree/bindings/i2c/i2c-brcmstb.txt
+++ b/Documentation/devicetree/bindings/i2c/i2c-brcmstb.txt
@@ -2,7 +2,7 @@ Broadcom stb bsc iic master controller
Required properties:
-- compatible: should be "brcm,brcmstb-i2c"
+- compatible: should be "brcm,brcmstb-i2c" or "brcm,brcmper-i2c"
- clock-frequency: 32-bit decimal value of iic master clock freqency in Hz
valid values are 375000, 390000, 187500, 200000
93750, 97500, 46875 and 50000
diff --git a/Documentation/devicetree/bindings/i2c/i2c-rcar.txt b/Documentation/devicetree/bindings/i2c/i2c-rcar.txt
index ea406eb20fa5..95e97223a71c 100644
--- a/Documentation/devicetree/bindings/i2c/i2c-rcar.txt
+++ b/Documentation/devicetree/bindings/i2c/i2c-rcar.txt
@@ -20,6 +20,10 @@ Optional properties:
propoerty indicates the default frequency 100 kHz.
- clocks: clock specifier.
+- i2c-scl-falling-time-ns: see i2c.txt
+- i2c-scl-internal-delay-ns: see i2c.txt
+- i2c-scl-rising-time-ns: see i2c.txt
+
Examples :
i2c0: i2c@e6508000 {
diff --git a/Documentation/devicetree/bindings/i2c/i2c.txt b/Documentation/devicetree/bindings/i2c/i2c.txt
index 8a99150ac3a7..c8d977ed847f 100644
--- a/Documentation/devicetree/bindings/i2c/i2c.txt
+++ b/Documentation/devicetree/bindings/i2c/i2c.txt
@@ -29,12 +29,38 @@ Optional properties
These properties may not be supported by all drivers. However, if a driver
wants to support one of the below features, it should adapt the bindings below.
-- clock-frequency - frequency of bus clock in Hz.
-- wakeup-source - device can be used as a wakeup source.
+- clock-frequency
+ frequency of bus clock in Hz.
-- interrupts - interrupts used by the device.
-- interrupt-names - "irq" and "wakeup" names are recognized by I2C core,
- other names are left to individual drivers.
+- i2c-scl-falling-time-ns
+ Number of nanoseconds the SCL signal takes to fall; t(f) in the I2C
+ specification.
+
+- i2c-scl-internal-delay-ns
+ Number of nanoseconds the IP core additionally needs to setup SCL.
+
+- i2c-scl-rising-time-ns
+ Number of nanoseconds the SCL signal takes to rise; t(r) in the I2C
+ specification.
+
+- i2c-sda-falling-time-ns
+ Number of nanoseconds the SDA signal takes to fall; t(f) in the I2C
+ specification.
+
+- interrupts
+ interrupts used by the device.
+
+- interrupt-names
+ "irq" and "wakeup" names are recognized by I2C core, other names are
+ left to individual drivers.
+
+- multi-master
+ states that there is another master active on this bus. The OS can use
+ this information to adapt power management to keep the arbitration awake
+ all the time, for example.
+
+- wakeup-source
+ device can be used as a wakeup source.
Binding may contain optional "interrupts" property, describing interrupts
used by the device. I2C core will assign "irq" interrupt (or the very first
diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
index c50cf13c852e..539874490492 100644
--- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt
+++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
@@ -20,22 +20,11 @@ adi,adt7476 +/-1C TDM Extended Temp Range I.C
adi,adt7490 +/-1C TDM Extended Temp Range I.C
adi,adxl345 Three-Axis Digital Accelerometer
adi,adxl346 Three-Axis Digital Accelerometer (backward-compatibility value "adi,adxl345" must be listed too)
+ams,iaq-core AMS iAQ-Core VOC Sensor
at,24c08 i2c serial eeprom (24cxx)
-atmel,24c00 i2c serial eeprom (24cxx)
-atmel,24c01 i2c serial eeprom (24cxx)
-atmel,24c02 i2c serial eeprom (24cxx)
-atmel,24c04 i2c serial eeprom (24cxx)
-atmel,24c16 i2c serial eeprom (24cxx)
-atmel,24c32 i2c serial eeprom (24cxx)
-atmel,24c64 i2c serial eeprom (24cxx)
-atmel,24c128 i2c serial eeprom (24cxx)
-atmel,24c256 i2c serial eeprom (24cxx)
-atmel,24c512 i2c serial eeprom (24cxx)
-atmel,24c1024 i2c serial eeprom (24cxx)
atmel,at97sc3204t i2c trusted platform module (TPM)
capella,cm32181 CM32181: Ambient Light Sensor
capella,cm3232 CM3232: Ambient Light Sensor
-catalyst,24c32 i2c serial eeprom
cirrus,cs42l51 Cirrus Logic CS42L51 audio codec
dallas,ds1307 64 x 8, Serial, I2C Real-Time Clock
dallas,ds1338 I2C RTC with 56-Byte NV RAM
@@ -49,11 +38,13 @@ dallas,ds4510 CPU Supervisor with Nonvolatile Memory and Programmable I/O
dallas,ds75 Digital Thermometer and Thermostat
dlg,da9053 DA9053: flexible system level PMIC with multicore support
dlg,da9063 DA9063: system PMIC for quad-core application processors
+epson,rx8010 I2C-BUS INTERFACE REAL TIME CLOCK MODULE
epson,rx8025 High-Stability. I2C-Bus INTERFACE REAL TIME CLOCK MODULE
epson,rx8581 I2C-BUS INTERFACE REAL TIME CLOCK MODULE
fsl,mag3110 MAG3110: Xtrinsic High Accuracy, 3D Magnetometer
fsl,mc13892 MC13892: Power Management Integrated Circuit (PMIC) for i.MX35/51
fsl,mma8450 MMA8450Q: Xtrinsic Low-power, 3-axis Xtrinsic Accelerometer
+fsl,mpl3115 MPL3115: Absolute Digital Pressure Sensor
fsl,mpr121 MPR121: Proximity Capacitive Touch Sensor Controller
fsl,sgtl5000 SGTL5000: Ultra Low-Power Audio Codec
gmt,g751 G751: Digital Temperature Sensor and Thermal Watchdog with Two-Wire Interface
@@ -80,7 +71,6 @@ ovti,ov5642 OV5642: Color CMOS QSXGA (5-megapixel) Image Sensor with OmniBSI an
pericom,pt7c4338 Real-time Clock Module
plx,pex8648 48-Lane, 12-Port PCI Express Gen 2 (5.0 GT/s) Switch
pulsedlight,lidar-lite-v2 Pulsedlight LIDAR range-finding sensor
-ramtron,24c64 i2c serial eeprom (24cxx)
ricoh,r2025sd I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
ricoh,r2221tl I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
ricoh,rs5c372a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
diff --git a/Documentation/devicetree/bindings/iio/accel/mma8452.txt b/Documentation/devicetree/bindings/iio/accel/mma8452.txt
index e3c37467d7da..3c10e8581144 100644
--- a/Documentation/devicetree/bindings/iio/accel/mma8452.txt
+++ b/Documentation/devicetree/bindings/iio/accel/mma8452.txt
@@ -7,13 +7,18 @@ Required properties:
* "fsl,mma8453"
* "fsl,mma8652"
* "fsl,mma8653"
+
- reg: the I2C address of the chip
Optional properties:
- interrupt-parent: should be the phandle for the interrupt controller
+
- interrupts: interrupt mapping for GPIO IRQ
+ - interrupt-names: should contain "INT1" and/or "INT2", the accelerometer's
+ interrupt line in use.
+
Example:
mma8453fc@1d {
@@ -21,4 +26,5 @@ Example:
reg = <0x1d>;
interrupt-parent = <&gpio1>;
interrupts = <5 0>;
+ interrupt-names = "INT2";
};
diff --git a/Documentation/devicetree/bindings/iio/adc/imx7d-adc.txt b/Documentation/devicetree/bindings/iio/adc/imx7d-adc.txt
new file mode 100644
index 000000000000..5c184b940669
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/imx7d-adc.txt
@@ -0,0 +1,22 @@
+Freescale imx7d ADC bindings
+
+The devicetree bindings are for the ADC driver written for
+imx7d SoC.
+
+Required properties:
+- compatible: Should be "fsl,imx7d-adc"
+- reg: Offset and length of the register set for the ADC device
+- interrupts: The interrupt number for the ADC device
+- clocks: The root clock of the ADC controller
+- clock-names: Must contain "adc", matching entry in the clocks property
+- vref-supply: The regulator supply ADC reference voltage
+
+Example:
+adc1: adc@30610000 {
+ compatible = "fsl,imx7d-adc";
+ reg = <0x30610000 0x10000>;
+ interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clks IMX7D_ADC_ROOT_CLK>;
+ clock-names = "adc";
+ vref-supply = <&reg_vcc_3v3_mcu>;
+};
diff --git a/Documentation/devicetree/bindings/iio/adc/mcp320x.txt b/Documentation/devicetree/bindings/iio/adc/mcp320x.txt
index 2a1f3af30155..bcd3ac8e6e0c 100644
--- a/Documentation/devicetree/bindings/iio/adc/mcp320x.txt
+++ b/Documentation/devicetree/bindings/iio/adc/mcp320x.txt
@@ -10,16 +10,28 @@ must be specified.
Required properties:
- compatible: Must be one of the following, depending on the
model:
- "mcp3001"
- "mcp3002"
- "mcp3004"
- "mcp3008"
- "mcp3201"
- "mcp3202"
- "mcp3204"
- "mcp3208"
- "mcp3301"
+ "mcp3001" (DEPRECATED)
+ "mcp3002" (DEPRECATED)
+ "mcp3004" (DEPRECATED)
+ "mcp3008" (DEPRECATED)
+ "mcp3201" (DEPRECATED)
+ "mcp3202" (DEPRECATED)
+ "mcp3204" (DEPRECATED)
+ "mcp3208" (DEPRECATED)
+ "mcp3301" (DEPRECATED)
+ "microchip,mcp3001"
+ "microchip,mcp3002"
+ "microchip,mcp3004"
+ "microchip,mcp3008"
+ "microchip,mcp3201"
+ "microchip,mcp3202"
+ "microchip,mcp3204"
+ "microchip,mcp3208"
+ "microchip,mcp3301"
+
+ NOTE: The use of the compatibles with no vendor prefix
+ is deprecated and only listed because old DT use them.
Examples:
spi_controller {
diff --git a/Documentation/devicetree/bindings/iio/adc/mcp3422.txt b/Documentation/devicetree/bindings/iio/adc/mcp3422.txt
index 333139cc0bfb..dcae4ccfcc52 100644
--- a/Documentation/devicetree/bindings/iio/adc/mcp3422.txt
+++ b/Documentation/devicetree/bindings/iio/adc/mcp3422.txt
@@ -1,7 +1,8 @@
-* Microchip mcp3422/3/4/6/7/8 chip family (ADC)
+* Microchip mcp3421/2/3/4/6/7/8 chip family (ADC)
Required properties:
- compatible: Should be
+ "microchip,mcp3421" or
"microchip,mcp3422" or
"microchip,mcp3423" or
"microchip,mcp3424" or
diff --git a/Documentation/devicetree/bindings/iio/adc/palmas-gpadc.txt b/Documentation/devicetree/bindings/iio/adc/palmas-gpadc.txt
new file mode 100644
index 000000000000..4bb9a86065d1
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/palmas-gpadc.txt
@@ -0,0 +1,48 @@
+* Palmas general purpose ADC IP block devicetree bindings
+
+Channels list:
+ 0 battery type
+ 1 battery temp NTC (optional current source)
+ 2 GP
+ 3 temp (with ext. diode, optional current source)
+ 4 GP
+ 5 GP
+ 6 VBAT_SENSE
+ 7 VCC_SENSE
+ 8 Backup Battery voltage
+ 9 external charger (VCHG)
+ 10 VBUS
+ 11 DC-DC current probe (how does this work?)
+ 12 internal die temp
+ 13 internal die temp
+ 14 USB ID pin voltage
+ 15 test network
+
+Required properties:
+- compatible : Must be "ti,palmas-gpadc".
+- #io-channel-cells: Should be set to <1>.
+
+Optional sub-nodes:
+ti,channel0-current-microamp: Channel 0 current in uA.
+ Values are rounded to derive 0uA, 5uA, 15uA, 20uA.
+ti,channel3-current-microamp: Channel 3 current in uA.
+ Values are rounded to derive 0uA, 10uA, 400uA, 800uA.
+ti,enable-extended-delay: Enable extended delay.
+
+Example:
+
+pmic {
+ compatible = "ti,twl6035-pmic", "ti,palmas-pmic";
+ ...
+ gpadc {
+ compatible = "ti,palmas-gpadc";
+ interrupts = <18 0
+ 16 0
+ 17 0>;
+ #io-channel-cells = <1>;
+ ti,channel0-current-microamp = <5>;
+ ti,channel3-current-microamp = <10>;
+ };
+ };
+ ...
+};
diff --git a/Documentation/devicetree/bindings/iio/adc/ti-adc128s052.txt b/Documentation/devicetree/bindings/iio/adc/ti-adc128s052.txt
index 15ca6b47958e..daa2b2c29428 100644
--- a/Documentation/devicetree/bindings/iio/adc/ti-adc128s052.txt
+++ b/Documentation/devicetree/bindings/iio/adc/ti-adc128s052.txt
@@ -1,7 +1,7 @@
-* Texas Instruments' ADC128S052 and ADC122S021 ADC chip
+* Texas Instruments' ADC128S052, ADC122S021 and ADC124S021 ADC chip
Required properties:
- - compatible: Should be "ti,adc128s052" or "ti,adc122s021"
+ - compatible: Should be "ti,adc128s052", "ti,adc122s021" or "ti,adc124s021"
- reg: spi chip select number for the device
- vref-supply: The regulator supply for ADC reference voltage
diff --git a/Documentation/devicetree/bindings/iio/adc/ti-ads8688.txt b/Documentation/devicetree/bindings/iio/adc/ti-ads8688.txt
new file mode 100644
index 000000000000..a02337d7efa4
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/ti-ads8688.txt
@@ -0,0 +1,20 @@
+* Texas Instruments' ADS8684 and ADS8688 ADC chip
+
+Required properties:
+ - compatible: Should be "ti,ads8684" or "ti,ads8688"
+ - reg: spi chip select number for the device
+
+Recommended properties:
+ - spi-max-frequency: Definition as per
+ Documentation/devicetree/bindings/spi/spi-bus.txt
+
+Optional properties:
+ - vref-supply: The regulator supply for ADC reference voltage
+
+Example:
+adc@0 {
+ compatible = "ti,ads8688";
+ reg = <0>;
+ vref-supply = <&vdd_supply>;
+ spi-max-frequency = <1000000>;
+};
diff --git a/Documentation/devicetree/bindings/iio/health/max30100.txt b/Documentation/devicetree/bindings/iio/health/max30100.txt
new file mode 100644
index 000000000000..f6fbac66ad06
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/health/max30100.txt
@@ -0,0 +1,21 @@
+Maxim MAX30100 heart rate and pulse oximeter sensor
+
+* https://datasheets.maximintegrated.com/en/ds/MAX30100.pdf
+
+Required properties:
+ - compatible: must be "maxim,max30100"
+ - reg: the I2C address of the sensor
+ - interrupt-parent: should be the phandle for the interrupt controller
+ - interrupts: the sole interrupt generated by the device
+
+ Refer to interrupt-controller/interrupts.txt for generic
+ interrupt client node bindings.
+
+Example:
+
+max30100@057 {
+ compatible = "maxim,max30100";
+ reg = <57>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <16 2>;
+};
diff --git a/Documentation/devicetree/bindings/iio/light/us5182d.txt b/Documentation/devicetree/bindings/iio/light/us5182d.txt
index 6f0a530144fd..a61979997f37 100644
--- a/Documentation/devicetree/bindings/iio/light/us5182d.txt
+++ b/Documentation/devicetree/bindings/iio/light/us5182d.txt
@@ -7,13 +7,24 @@ Required properties:
Optional properties:
- upisemi,glass-coef: glass attenuation factor - compensation factor of
resolution 1000 for material transmittance.
+
- upisemi,dark-ths: array of 8 elements containing 16-bit thresholds (adc
counts) corresponding to every scale.
+
- upisemi,upper-dark-gain: 8-bit dark gain compensation factor(4 int and 4
fractional bits - Q4.4) applied when light > threshold
+
- upisemi,lower-dark-gain: 8-bit dark gain compensation factor(4 int and 4
fractional bits - Q4.4) applied when light < threshold
+- upisemi,continuous: This chip has two power modes: one-shot (chip takes one
+ measurement and then shuts itself down) and continuous (
+ chip takes continuous measurements). The one-shot mode is
+ more power-friendly but the continuous mode may be more
+ reliable. If this property is specified the continuous
+ mode will be used instead of the default one-shot one for
+ raw reads.
+
If the optional properties are not specified these factors will default to the
values in the below example.
The glass-coef defaults to no compensation for the covering material.
diff --git a/Documentation/devicetree/bindings/iio/st-sensors.txt b/Documentation/devicetree/bindings/iio/st-sensors.txt
index d3ccdb190c53..d4b87cc1e446 100644
--- a/Documentation/devicetree/bindings/iio/st-sensors.txt
+++ b/Documentation/devicetree/bindings/iio/st-sensors.txt
@@ -36,6 +36,7 @@ Accelerometers:
- st,lsm303dlm-accel
- st,lsm330-accel
- st,lsm303agr-accel
+- st,lis2dh12-accel
Gyroscopes:
- st,l3g4200d-gyro
diff --git a/Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt b/Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt
index b9c32f6fd687..4357e498ef04 100644
--- a/Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt
+++ b/Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt
@@ -12,7 +12,7 @@ Each key is represented as a sub-node of "allwinner,sun4i-a10-lradc-keys":
Required subnode-properties:
- label: Descriptive name of the key.
- linux,code: Keycode to emit.
- - channel: Channel this key is attached to, mut be 0 or 1.
+ - channel: Channel this key is attached to, must be 0 or 1.
- voltage: Voltage in µV at lradc input when this key is pressed.
Example:
diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
index 8ba98eec765b..c98757a69110 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
@@ -13,6 +13,17 @@ Required properties:
- interrupt-parent : Interrupt controller to which the chip is connected
- interrupts : Interrupt to which the chip is connected
+Optional properties:
+
+ - irq-gpios : GPIO pin used for IRQ. The driver uses the
+ interrupt gpio pin as output to reset the device.
+ - reset-gpios : GPIO pin used for reset
+
+ - touchscreen-inverted-x : X axis is inverted (boolean)
+ - touchscreen-inverted-y : Y axis is inverted (boolean)
+ - touchscreen-swapped-x-y : X and Y axis are swapped (boolean)
+ (swapping is done after inverting the axis)
+
Example:
i2c@00000000 {
@@ -23,6 +34,9 @@ Example:
reg = <0x5d>;
interrupt-parent = <&gpio>;
interrupts = <0 0>;
+
+ irq-gpios = <&gpio1 0 0>;
+ reset-gpios = <&gpio1 1 0>;
};
/* ... */
diff --git a/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt b/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt
index 8eb240a287c8..697a3e7831e7 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt
@@ -9,7 +9,9 @@ Required properties:
- touchscreen-size-y: vertical resolution of touchscreen (in pixels)
Optional properties:
-- reset-gpio: GPIO connected to the RESET line of the chip
+- reset-gpios: GPIO connected to the RESET line of the chip
+- enable-gpios: GPIO connected to the ENABLE line of the chip
+- wake-gpios: GPIO connected to the WAKE line of the chip
Example:
diff --git a/Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt
new file mode 100644
index 000000000000..4c1c092c276b
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt
@@ -0,0 +1,11 @@
+* TS-4800 Touchscreen bindings
+
+Required properties:
+- compatible: must be "technologic,ts4800-ts"
+- reg: physical base address of the controller and length of memory mapped
+ region.
+- syscon: phandle / integers array that points to the syscon node which
+ describes the FPGA's syscon registers.
+ - phandle to FPGA's syscon
+ - offset to the touchscreen register
+ - offset to the touchscreen enable bit
diff --git a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun67i-sc-nmi.txt b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sunxi-nmi.txt
index d1c5cdabc3e0..81cd3692405e 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun67i-sc-nmi.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sunxi-nmi.txt
@@ -4,7 +4,7 @@ Allwinner Sunxi NMI Controller
Required properties:
- compatible : should be "allwinner,sun7i-a20-sc-nmi" or
- "allwinner,sun6i-a31-sc-nmi"
+ "allwinner,sun6i-a31-sc-nmi" or "allwinner,sun9i-a80-nmi"
- reg : Specifies base physical address and size of the registers.
- interrupt-controller : Identifies the node as an interrupt controller
- #interrupt-cells : Specifies the number of cells needed to encode an
diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt b/Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt
index cc56021eb60b..5a1cb4bc3dfe 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt
@@ -18,6 +18,7 @@ Main node required properties:
"arm,cortex-a9-gic"
"arm,gic-400"
"arm,pl390"
+ "arm,tc11mp-gic"
"brcm,brahma-b15-gic"
"qcom,msm-8660-qgic"
"qcom,msm-qgic2"
diff --git a/Documentation/devicetree/bindings/interrupt-controller/hisilicon,mbigen-v2.txt b/Documentation/devicetree/bindings/interrupt-controller/hisilicon,mbigen-v2.txt
new file mode 100644
index 000000000000..720f7c92e9a1
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/hisilicon,mbigen-v2.txt
@@ -0,0 +1,74 @@
+Hisilicon mbigen device tree bindings.
+=======================================
+
+Mbigen means: message based interrupt generator.
+
+MBI is kind of msi interrupt only used on Non-PCI devices.
+
+To reduce the wired interrupt number connected to GIC,
+Hisilicon designed mbigen to collect and generate interrupt.
+
+
+Non-pci devices can connect to mbigen and generate the
+interrupt by writing ITS register.
+
+The mbigen chip and devices connect to mbigen have the following properties:
+
+Mbigen main node required properties:
+-------------------------------------------
+- compatible: Should be "hisilicon,mbigen-v2"
+
+- reg: Specifies the base physical address and size of the Mbigen
+ registers.
+
+- interrupt controller: Identifies the node as an interrupt controller
+
+- msi-parent: Specifies the MSI controller this mbigen use.
+ For more detail information,please refer to the generic msi-parent binding in
+ Documentation/devicetree/bindings/interrupt-controller/msi.txt.
+
+- num-pins: the total number of pins implemented in this Mbigen
+ instance.
+
+- #interrupt-cells : Specifies the number of cells needed to encode an
+ interrupt source. The value must be 2.
+
+ The 1st cell is hardware pin number of the interrupt.This number is local to
+ each mbigen chip and in the range from 0 to the maximum interrupts number
+ of the mbigen.
+
+ The 2nd cell is the interrupt trigger type.
+ The value of this cell should be:
+ 1: rising edge triggered
+ or
+ 4: high level triggered
+
+Examples:
+
+ mbigen_device_gmac:intc {
+ compatible = "hisilicon,mbigen-v2";
+ reg = <0x0 0xc0080000 0x0 0x10000>;
+ interrupt-controller;
+ msi-parent = <&its_dsa 0x40b1c>;
+ num-pins = <9>;
+ #interrupt-cells = <2>;
+ };
+
+Devices connect to mbigen required properties:
+----------------------------------------------------
+-interrupt-parent: Specifies the mbigen device node which device connected.
+
+-interrupts:Specifies the interrupt source.
+ For the specific information of each cell in this property,please refer to
+ the "interrupt-cells" description mentioned above.
+
+Examples:
+ gmac0: ethernet@c2080000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0 0xc2080000 0 0x20000>,
+ <0 0xc0000000 0 0x1000>;
+ interrupt-parent = <&mbigen_device_gmac>;
+ interrupts = <656 1>,
+ <657 1>;
+ };
diff --git a/Documentation/devicetree/bindings/interrupt-controller/qca,ath79-misc-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/qca,ath79-misc-intc.txt
index ec96b1f01478..475ae9bd562b 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/qca,ath79-misc-intc.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/qca,ath79-misc-intc.txt
@@ -22,7 +22,7 @@ Interrupt Controllers bindings used by client devices.
Example:
interrupt-controller@18060010 {
- compatible = "qca,ar9132-misc-intc", qca,ar7100-misc-intc";
+ compatible = "qca,ar9132-misc-intc", "qca,ar7100-misc-intc";
reg = <0x18060010 0x4>;
interrupt-parent = <&cpuintc>;
diff --git a/Documentation/devicetree/bindings/interrupt-controller/technologic,ts4800.txt b/Documentation/devicetree/bindings/interrupt-controller/technologic,ts4800.txt
new file mode 100644
index 000000000000..7f15f1b0325b
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/technologic,ts4800.txt
@@ -0,0 +1,16 @@
+TS-4800 FPGA interrupt controller
+
+TS-4800 FPGA has an internal interrupt controller. When one of the
+interrupts is triggered, the SoC is notified, usually using a GPIO as
+parent interrupt source.
+
+Required properties:
+- compatible: should be "technologic,ts4800-irqc"
+- interrupt-controller: identifies the node as an interrupt controller
+- reg: physical base address of the controller and length of memory mapped
+ region
+- #interrupt-cells: specifies the number of cells needed to encode an interrupt
+ source, should be 1.
+- interrupt-parent: phandle to the parent interrupt controller this one is
+ cascaded from
+- interrupts: specifies the interrupt line in the interrupt-parent controller
diff --git a/Documentation/devicetree/bindings/media/i2c/adp1653.txt b/Documentation/devicetree/bindings/media/i2c/adp1653.txt
index 5ce66f2104e3..4cce0de40ee9 100644
--- a/Documentation/devicetree/bindings/media/i2c/adp1653.txt
+++ b/Documentation/devicetree/bindings/media/i2c/adp1653.txt
@@ -12,12 +12,13 @@ There are two LED outputs available - flash and indicator. One LED is
represented by one child node, nodes need to be named "flash" and "indicator".
Required properties of the LED child node:
-- max-microamp : see Documentation/devicetree/bindings/leds/common.txt
+- led-max-microamp : see Documentation/devicetree/bindings/leds/common.txt
Required properties of the flash LED child node:
- flash-max-microamp : see Documentation/devicetree/bindings/leds/common.txt
- flash-timeout-us : see Documentation/devicetree/bindings/leds/common.txt
+- led-max-microamp : see Documentation/devicetree/bindings/leds/common.txt
Example:
@@ -29,9 +30,9 @@ Example:
flash {
flash-timeout-us = <500000>;
flash-max-microamp = <320000>;
- max-microamp = <50000>;
+ led-max-microamp = <50000>;
};
indicator {
- max-microamp = <17500>;
+ led-max-microamp = <17500>;
};
};
diff --git a/Documentation/devicetree/bindings/media/stih407-c8sectpfe.txt b/Documentation/devicetree/bindings/media/stih407-c8sectpfe.txt
index d4def767bdfe..cc51b1fd6e0c 100644
--- a/Documentation/devicetree/bindings/media/stih407-c8sectpfe.txt
+++ b/Documentation/devicetree/bindings/media/stih407-c8sectpfe.txt
@@ -35,7 +35,7 @@ Required properties (tsin (child) node):
- tsin-num : tsin id of the InputBlock (must be between 0 to 6)
- i2c-bus : phandle to the I2C bus DT node which the demodulators & tuners on this tsin channel are connected.
-- rst-gpio : reset gpio for this tsin channel.
+- reset-gpios : reset gpio for this tsin channel.
Optional properties (tsin (child) node):
@@ -55,27 +55,27 @@ Example:
status = "okay";
reg = <0x08a20000 0x10000>, <0x08a00000 0x4000>;
reg-names = "stfe", "stfe-ram";
- interrupts = <0 34 0>, <0 35 0>;
+ interrupts = <GIC_SPI 34 IRQ_TYPE_NONE>, <GIC_SPI 35 IRQ_TYPE_NONE>;
interrupt-names = "stfe-error-irq", "stfe-idle-irq";
-
- pinctrl-names = "tsin0-serial", "tsin0-parallel", "tsin3-serial",
- "tsin4-serial", "tsin5-serial";
-
pinctrl-0 = <&pinctrl_tsin0_serial>;
pinctrl-1 = <&pinctrl_tsin0_parallel>;
pinctrl-2 = <&pinctrl_tsin3_serial>;
pinctrl-3 = <&pinctrl_tsin4_serial_alt3>;
pinctrl-4 = <&pinctrl_tsin5_serial_alt1>;
-
+ pinctrl-names = "tsin0-serial",
+ "tsin0-parallel",
+ "tsin3-serial",
+ "tsin4-serial",
+ "tsin5-serial";
clocks = <&clk_s_c0_flexgen CLK_PROC_STFE>;
- clock-names = "stfe";
+ clock-names = "c8sectpfe";
/* tsin0 is TSA on NIMA */
tsin0: port@0 {
tsin-num = <0>;
serial-not-parallel;
i2c-bus = <&ssc2>;
- rst-gpio = <&pio15 4 0>;
+ reset-gpios = <&pio15 4 GPIO_ACTIVE_HIGH>;
dvb-card = <STV0367_TDA18212_NIMA_1>;
};
@@ -83,7 +83,7 @@ Example:
tsin-num = <3>;
serial-not-parallel;
i2c-bus = <&ssc3>;
- rst-gpio = <&pio15 7 0>;
+ reset-gpios = <&pio15 7 GPIO_ACTIVE_HIGH>;
dvb-card = <STV0367_TDA18212_NIMB_1>;
};
};
diff --git a/Documentation/devicetree/bindings/memory-controllers/ath79-ddr-controller.txt b/Documentation/devicetree/bindings/memory-controllers/ath79-ddr-controller.txt
index efe35a065714..c81af75bcd88 100644
--- a/Documentation/devicetree/bindings/memory-controllers/ath79-ddr-controller.txt
+++ b/Documentation/devicetree/bindings/memory-controllers/ath79-ddr-controller.txt
@@ -1,6 +1,6 @@
Binding for Qualcomm Atheros AR7xxx/AR9xxx DDR controller
-The DDR controller of the ARxxx and AR9xxx families provides an interface
+The DDR controller of the AR7xxx and AR9xxx families provides an interface
to flush the FIFO between various devices and the DDR. This is mainly used
by the IRQ controller to flush the FIFO before running the interrupt handler
of such devices.
@@ -11,9 +11,9 @@ Required properties:
"qca,[ar7100|ar7240]-ddr-controller" as fallback.
On SoC with PCI support "qca,ar7100-ddr-controller" should be used as
fallback, otherwise "qca,ar7240-ddr-controller" should be used.
-- reg: Base address and size of the controllers memory area
-- #qca,ddr-wb-channel-cells: has to be 1, the index of the write buffer
- channel
+- reg: Base address and size of the controller's memory area
+- #qca,ddr-wb-channel-cells: Specifies the number of cells needed to encode
+ the write buffer channel index, should be 1.
Example:
diff --git a/Documentation/devicetree/bindings/mfd/arizona.txt b/Documentation/devicetree/bindings/mfd/arizona.txt
index 18be0cbfb456..9b30011ecabe 100644
--- a/Documentation/devicetree/bindings/mfd/arizona.txt
+++ b/Documentation/devicetree/bindings/mfd/arizona.txt
@@ -1,4 +1,4 @@
-Wolfson Arizona class audio SoCs
+Cirrus Logic/Wolfson Microelectronics Arizona class audio SoCs
These devices are audio SoCs with extensive digital capabilites and a range
of analogue I/O.
@@ -6,12 +6,14 @@ of analogue I/O.
Required properties:
- compatible : One of the following chip-specific strings:
+ "cirrus,cs47l24"
"wlf,wm5102"
"wlf,wm5110"
"wlf,wm8280"
"wlf,wm8997"
"wlf,wm8998"
"wlf,wm1814"
+ "wlf,wm1831"
- reg : I2C slave address when connected using I2C, chip select number when
using SPI.
@@ -24,7 +26,7 @@ Required properties:
- #interrupt-cells: the number of cells to describe an IRQ, this should be 2.
The first cell is the IRQ number.
The second cell is the flags, encoded as the trigger masks from
- Documentation/devicetree/bindings/interrupts.txt
+ Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
- gpio-controller : Indicates this device is a GPIO controller.
- #gpio-cells : Must be 2. The first cell is the pin number and the
@@ -41,10 +43,21 @@ Required properties:
- SPKVDD-supply : Speaker driver power supply (wm8997)
+ - DCVDD-supply : Main power supply (cs47l24, wm1831)
+
+ - MICVDD-supply : Microphone power supply (cs47l24, wm1831)
+
Optional properties:
- wlf,reset : GPIO specifier for the GPIO controlling /RESET
+ - clocks: Should reference the clocks supplied on MCLK1 and MCLK2
+ - clock-names: Should contains two strings:
+ "mclk1" for the clock supplied on MCLK1, recommended to be a high
+ quality audio reference clock
+ "mclk2" for the clock supplied on MCLK2, recommended to be an always on
+ 32k clock
+
- wlf,gpio-defaults : A list of GPIO configuration register values. Defines
for the appropriate values can found in <dt-bindings/mfd/arizona.txt>. If
absent, no configuration of these registers is performed. If any entry has
@@ -59,6 +72,12 @@ Optional properties:
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,out-mono : A list of boolean values indicating whether each output is
+ mono or stereo. Position within the list indicates the output affected
+ (eg. First entry in the list corresponds to output 1). A non-zero value
+ indicates a mono output. If present, the number of values should be less
+ than or equal to the number of outputs, if less values are supplied the
+ additional outputs will be treated as stereo.
- wlf,dmic-ref : DMIC reference voltage source for each input, can be
selected from either MICVDD or one of the MICBIAS's, defines
@@ -69,6 +88,7 @@ Optional properties:
- DCVDD-supply, MICVDD-supply : Power supplies, only need to be specified if
they are being externally supplied. As covered in
Documentation/devicetree/bindings/regulator/regulator.txt
+ (wm5102, wm5110, wm8280, wm8997, wm8998, wm1814)
Also see child specific device properties:
Regulator - ../regulator/arizona-regulator.txt
diff --git a/Documentation/devicetree/bindings/mfd/palmas.txt b/Documentation/devicetree/bindings/mfd/palmas.txt
index eda898978d33..8ae1a32bfb7e 100644
--- a/Documentation/devicetree/bindings/mfd/palmas.txt
+++ b/Documentation/devicetree/bindings/mfd/palmas.txt
@@ -24,7 +24,7 @@ and also the generic series names
- #interrupt-cells : should be set to 2 for IRQ number and flags
The first cell is the IRQ number.
The second cell is the flags, encoded as the trigger masks from
- Documentation/devicetree/bindings/interrupts.txt
+ Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
- interrupt-parent : The parent interrupt controller.
Optional properties:
diff --git a/Documentation/devicetree/bindings/mfd/s2mpa01.txt b/Documentation/devicetree/bindings/mfd/s2mpa01.txt
deleted file mode 100644
index c13d3d8c3947..000000000000
--- a/Documentation/devicetree/bindings/mfd/s2mpa01.txt
+++ /dev/null
@@ -1,90 +0,0 @@
-
-* Samsung S2MPA01 Voltage and Current Regulator
-
-The Samsung S2MPA01 is a multi-function device which includes high
-efficiency buck converters including Dual-Phase buck converter, various LDOs,
-and an RTC. It is interfaced to the host controller using an I2C interface.
-Each sub-block is addressed by the host system using different I2C slave
-addresses.
-
-Required properties:
-- compatible: Should be "samsung,s2mpa01-pmic".
-- reg: Specifies the I2C slave address of the PMIC block. It should be 0x66.
-
-Optional properties:
-- interrupt-parent: Specifies the phandle of the interrupt controller to which
- the interrupts from s2mpa01 are delivered to.
-- interrupts: An interrupt specifier for the sole interrupt generated by the
- device.
-
-Optional nodes:
-- regulators: The regulators of s2mpa01 that have to be instantiated should be
- included in a sub-node named 'regulators'. Regulator nodes and constraints
- included in this sub-node use the standard regulator bindings which are
- documented elsewhere.
-
-Properties for BUCK regulator nodes:
-- regulator-ramp-delay: ramp delay in uV/us. May be 6250, 12500
- (default), 25000, or 50000. May be 0 for disabling the ramp delay on
- BUCK{1,2,3,4}.
-
- In the absence of the regulator-ramp-delay property, the default ramp
- delay will be used.
-
- NOTE: Some BUCKs share the ramp rate setting i.e. same ramp value will be set
- for a particular group of BUCKs. So provide same regulator-ramp-delay=<value>.
-
- The following BUCKs share ramp settings:
- * 1 and 6
- * 2 and 4
- * 8, 9, and 10
-
-The following are the names of the regulators that the s2mpa01 PMIC block
-supports. Note: The 'n' in LDOn and BUCKn represents the LDO or BUCK number
-as per the datasheet of s2mpa01.
-
- - LDOn
- - valid values for n are 1 to 26
- - Example: LDO1, LD02, LDO26
- - BUCKn
- - valid values for n are 1 to 10.
- - Example: BUCK1, BUCK2, BUCK9
-
-Example:
-
- s2mpa01_pmic@66 {
- compatible = "samsung,s2mpa01-pmic";
- reg = <0x66>;
-
- regulators {
- ldo1_reg: LDO1 {
- regulator-name = "VDD_ALIVE";
- regulator-min-microvolt = <1000000>;
- regulator-max-microvolt = <1000000>;
- };
-
- ldo2_reg: LDO2 {
- regulator-name = "VDDQ_MMC2";
- regulator-min-microvolt = <2800000>;
- regulator-max-microvolt = <2800000>;
- regulator-always-on;
- };
-
- buck1_reg: BUCK1 {
- regulator-name = "vdd_mif";
- regulator-min-microvolt = <950000>;
- regulator-max-microvolt = <1350000>;
- regulator-always-on;
- regulator-boot-on;
- };
-
- buck2_reg: BUCK2 {
- regulator-name = "vdd_arm";
- regulator-min-microvolt = <950000>;
- regulator-max-microvolt = <1350000>;
- regulator-always-on;
- regulator-boot-on;
- regulator-ramp-delay = <50000>;
- };
- };
- };
diff --git a/Documentation/devicetree/bindings/mfd/s2mps11.txt b/Documentation/devicetree/bindings/mfd/s2mps11.txt
deleted file mode 100644
index 09b94c97faac..000000000000
--- a/Documentation/devicetree/bindings/mfd/s2mps11.txt
+++ /dev/null
@@ -1,153 +0,0 @@
-
-* Samsung S2MPS11/13/14/15 and S2MPU02 Voltage and Current Regulator
-
-The Samsung S2MPS11 is a multi-function device which includes voltage and
-current regulators, RTC, charger controller and other sub-blocks. It is
-interfaced to the host controller using an I2C interface. Each sub-block is
-addressed by the host system using different I2C slave addresses.
-
-Required properties:
-- compatible: Should be one of the following
- - "samsung,s2mps11-pmic"
- - "samsung,s2mps13-pmic"
- - "samsung,s2mps14-pmic"
- - "samsung,s2mps15-pmic"
- - "samsung,s2mpu02-pmic".
-- reg: Specifies the I2C slave address of the pmic block. It should be 0x66.
-
-Optional properties:
-- interrupt-parent: Specifies the phandle of the interrupt controller to which
- the interrupts from s2mps11 are delivered to.
-- interrupts: Interrupt specifiers for interrupt sources.
-- samsung,s2mps11-wrstbi-ground: Indicates that WRSTBI pin of PMIC is pulled
- down. When the system is suspended it will always go down thus triggerring
- unwanted buck warm reset (setting buck voltages to default values).
-- samsung,s2mps11-acokb-ground: Indicates that ACOKB pin of S2MPS11 PMIC is
- connected to the ground so the PMIC must manually set PWRHOLD bit in CTRL1
- register to turn off the power. Usually the ACOKB is pulled up to VBATT so
- when PWRHOLD pin goes low, the rising ACOKB will trigger power off.
-
-Optional nodes:
-- clocks: s2mps11, s2mps13, s2mps15 and s5m8767 provide three(AP/CP/BT) buffered 32.768
- KHz outputs, so to register these as clocks with common clock framework
- instantiate a sub-node named "clocks". It uses the common clock binding
- documented in :
- [Documentation/devicetree/bindings/clock/clock-bindings.txt]
- The s2mps14 provides two (AP/BT) buffered 32.768 KHz outputs.
- - #clock-cells: should be 1.
-
- - The following is the list of clocks generated by the controller. Each clock
- is assigned an identifier and client nodes use this identifier to specify
- the clock which they consume.
- Clock ID Devices
- ----------------------------------------------------------
- 32KhzAP 0 S2MPS11, S2MPS13, S2MPS14, S2MPS15, S5M8767
- 32KhzCP 1 S2MPS11, S2MPS13, S2MPS15, S5M8767
- 32KhzBT 2 S2MPS11, S2MPS13, S2MPS14, S2MPS15, S5M8767
-
- - compatible: Should be one of: "samsung,s2mps11-clk", "samsung,s2mps13-clk",
- "samsung,s2mps14-clk", "samsung,s5m8767-clk"
- The s2msp15 uses the same compatible as s2mps13, as both provides similar clocks.
-
-- regulators: The regulators of s2mps11 that have to be instantiated should be
-included in a sub-node named 'regulators'. Regulator nodes included in this
-sub-node should be of the format as listed below.
-
- regulator_name {
- [standard regulator constraints....];
- };
-
- regulator-ramp-delay for BUCKs = [6250/12500/25000(default)/50000] uV/us
-
- BUCK[2/3/4/6] supports disabling ramp delay on hardware, so explicitly
- regulator-ramp-delay = <0> can be used for them to disable ramp delay.
- In the absence of the regulator-ramp-delay property, the default ramp
- delay will be used.
-
-NOTE: Some BUCKs share the ramp rate setting i.e. same ramp value will be set
-for a particular group of BUCKs. So provide same regulator-ramp-delay<value>.
-Grouping of BUCKs sharing ramp rate setting is as follow : BUCK[1, 6],
-BUCK[3, 4], and BUCK[7, 8, 10]
-
-On S2MPS14 the LDO10, LDO11 and LDO12 can be configured to external control
-over GPIO. To turn this feature on this property must be added to the regulator
-sub-node:
- - samsung,ext-control-gpios: GPIO specifier for one GPIO
- controlling this regulator (enable/disable);
-Example:
- LDO12 {
- regulator-name = "V_EMMC_2.8V";
- regulator-min-microvolt = <2800000>;
- regulator-max-microvolt = <2800000>;
- samsung,ext-control-gpios = <&gpk0 2 0>;
- };
-
-
-The regulator constraints inside the regulator nodes use the standard regulator
-bindings which are documented elsewhere.
-
-The following are the names of the regulators that the s2mps11 pmic block
-supports. Note: The 'n' in LDOn and BUCKn represents the LDO or BUCK number
-as per the datasheet of s2mps11.
-
- - LDOn
- - valid values for n are:
- - S2MPS11: 1 to 38
- - S2MPS13: 1 to 40
- - S2MPS14: 1 to 25
- - S2MPS15: 1 to 27
- - S2MPU02: 1 to 28
- - Example: LDO1, LDO2, LDO28
- - BUCKn
- - valid values for n are:
- - S2MPS11: 1 to 10
- - S2MPS13: 1 to 10
- - S2MPS14: 1 to 5
- - S2MPS15: 1 to 10
- - S2MPU02: 1 to 7
- - Example: BUCK1, BUCK2, BUCK9
-
-Example:
-
- s2mps11_pmic@66 {
- compatible = "samsung,s2mps11-pmic";
- reg = <0x66>;
-
- s2m_osc: clocks {
- compatible = "samsung,s2mps11-clk";
- #clock-cells = <1>;
- clock-output-names = "xx", "yy", "zz";
- };
-
- regulators {
- ldo1_reg: LDO1 {
- regulator-name = "VDD_ABB_3.3V";
- regulator-min-microvolt = <3300000>;
- regulator-max-microvolt = <3300000>;
- };
-
- ldo2_reg: LDO2 {
- regulator-name = "VDD_ALIVE_1.1V";
- regulator-min-microvolt = <1100000>;
- regulator-max-microvolt = <1100000>;
- regulator-always-on;
- };
-
- buck1_reg: BUCK1 {
- regulator-name = "vdd_mif";
- regulator-min-microvolt = <950000>;
- regulator-max-microvolt = <1350000>;
- regulator-always-on;
- regulator-boot-on;
- };
-
- buck2_reg: BUCK2 {
- regulator-name = "vdd_arm";
- regulator-min-microvolt = <950000>;
- regulator-max-microvolt = <1350000>;
- regulator-always-on;
- regulator-boot-on;
- regulator-ramp-delay = <50000>;
- };
- };
- };
diff --git a/Documentation/devicetree/bindings/mfd/samsung,sec-core.txt b/Documentation/devicetree/bindings/mfd/samsung,sec-core.txt
new file mode 100644
index 000000000000..cdd079bfc287
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/samsung,sec-core.txt
@@ -0,0 +1,88 @@
+Binding for Samsung S2M and S5M family multi-function device
+============================================================
+
+This is a part of device tree bindings for S2M and S5M family multi-function
+devices.
+
+The Samsung S2MPA01, S2MPS11/13/14/15, S2MPU02 and S5M8767 is a family
+of multi-function devices which include voltage and current regulators, RTC,
+charger controller, clock outputs and other sub-blocks. It is interfaced
+to the host controller using an I2C interface. Each sub-block is usually
+addressed by the host system using different I2C slave addresses.
+
+
+This document describes bindings for main device node. Optional sub-blocks
+must be a sub-nodes to it. Bindings for them can be found in:
+ - bindings/regulator/samsung,s2mpa01.txt
+ - bindings/regulator/samsung,s2mps11.txt
+ - bindings/regulator/samsung,s5m8767.txt
+ - bindings/clock/samsung,s2mps11.txt
+
+
+Required properties:
+ - compatible: Should be one of the following
+ - "samsung,s2mpa01-pmic",
+ - "samsung,s2mps11-pmic",
+ - "samsung,s2mps13-pmic",
+ - "samsung,s2mps14-pmic",
+ - "samsung,s2mps15-pmic",
+ - "samsung,s2mpu02-pmic",
+ - "samsung,s5m8767-pmic".
+ - reg: Specifies the I2C slave address of the pmic block. It should be 0x66.
+
+Optional properties:
+ - interrupt-parent: Specifies the phandle of the interrupt controller to which
+ the interrupts from s2mps11 are delivered to.
+ - interrupts: Interrupt specifiers for interrupt sources.
+ - samsung,s2mps11-wrstbi-ground: Indicates that WRSTBI pin of PMIC is pulled
+ down. When the system is suspended it will always go down thus triggerring
+ unwanted buck warm reset (setting buck voltages to default values).
+ - samsung,s2mps11-acokb-ground: Indicates that ACOKB pin of S2MPS11 PMIC is
+ connected to the ground so the PMIC must manually set PWRHOLD bit in CTRL1
+ register to turn off the power. Usually the ACOKB is pulled up to VBATT so
+ when PWRHOLD pin goes low, the rising ACOKB will trigger power off.
+
+Example:
+
+ s2mps11_pmic@66 {
+ compatible = "samsung,s2mps11-pmic";
+ reg = <0x66>;
+
+ s2m_osc: clocks {
+ compatible = "samsung,s2mps11-clk";
+ #clock-cells = <1>;
+ clock-output-names = "xx", "yy", "zz";
+ };
+
+ regulators {
+ ldo1_reg: LDO1 {
+ regulator-name = "VDD_ABB_3.3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ ldo2_reg: LDO2 {
+ regulator-name = "VDD_ALIVE_1.1V";
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ regulator-always-on;
+ };
+
+ buck1_reg: BUCK1 {
+ regulator-name = "vdd_mif";
+ regulator-min-microvolt = <950000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ buck2_reg: BUCK2 {
+ regulator-name = "vdd_arm";
+ regulator-min-microvolt = <950000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-ramp-delay = <50000>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/mfd/syscon.txt b/Documentation/devicetree/bindings/mfd/syscon.txt
index fe8150bb3248..408f768686f1 100644
--- a/Documentation/devicetree/bindings/mfd/syscon.txt
+++ b/Documentation/devicetree/bindings/mfd/syscon.txt
@@ -13,6 +13,10 @@ Required properties:
- compatible: Should contain "syscon".
- reg: the register region can be accessed from syscon
+Optional property:
+- reg-io-width: the size (in bytes) of the IO accesses that should be
+ performed on the device.
+
Examples:
gpr: iomuxc-gpr@020e0000 {
compatible = "fsl,imx6q-iomuxc-gpr", "syscon";
diff --git a/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt b/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt
index cae29eb5733d..ff611fa66871 100644
--- a/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt
+++ b/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt
@@ -11,6 +11,7 @@ Required properties:
- "renesas,mmcif-r8a7740" for the MMCIF found in r8a7740 SoCs
- "renesas,mmcif-r8a7790" for the MMCIF found in r8a7790 SoCs
- "renesas,mmcif-r8a7791" for the MMCIF found in r8a7791 SoCs
+ - "renesas,mmcif-r8a7793" for the MMCIF found in r8a7793 SoCs
- "renesas,mmcif-r8a7794" for the MMCIF found in r8a7794 SoCs
- clocks: reference to the functional clock
diff --git a/Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt b/Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt
index 4ff7128ee3b2..c2546ced9c02 100644
--- a/Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt
+++ b/Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt
@@ -45,6 +45,8 @@ Required properties:
- #size-cells : <0>
Optional properties:
+- clock : reference to the clock for the NAND controller
+- clock-names : "nand" (required for the above clock)
- 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
@@ -72,6 +74,12 @@ we define additional 'compatible' properties and associated register resources w
and enable registers
- reg-names: (required) "nand-int-base"
+ * "brcm,nand-bcm6368"
+ - compatible: should contain "brcm,nand-bcm<soc>", "brcm,nand-bcm6368"
+ - reg: (required) the 'NAND_INTR_BASE' register range, with combined status
+ and enable registers, and boot address 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,
@@ -148,3 +156,27 @@ nand@f0442800 {
};
};
};
+
+nand@10000200 {
+ compatible = "brcm,nand-bcm63168", "brcm,nand-bcm6368",
+ "brcm,brcmnand-v4.0", "brcm,brcmnand";
+ reg = <0x10000200 0x180>,
+ <0x10000600 0x200>,
+ <0x100000b0 0x10>;
+ reg-names = "nand", "nand-cache", "nand-int-base";
+ interrupt-parent = <&periph_intc>;
+ interrupts = <50>;
+ clocks = <&periph_clk 20>;
+ clock-names = "nand";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ nand0: nandcs@0 {
+ compatible = "brcm,nandcs";
+ reg = <0>;
+ nand-on-flash-bbt;
+ nand-ecc-strength = <1>;
+ nand-ecc-step-size = <512>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
index 862aa2f8837a..00c587b3d3ae 100644
--- a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
+++ b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
@@ -2,7 +2,8 @@
Required properties:
- compatible : Should be "fsl,vf610-qspi", "fsl,imx6sx-qspi",
- "fsl,imx7d-qspi", "fsl,imx6ul-qspi"
+ "fsl,imx7d-qspi", "fsl,imx6ul-qspi",
+ "fsl,ls1021-qspi"
- reg : the first contains the register location and length,
the second contains the memory mapping address and length
- reg-names: Should contain the reg names "QuadSPI" and "QuadSPI-memory"
diff --git a/Documentation/devicetree/bindings/mtd/ingenic,jz4780-nand.txt b/Documentation/devicetree/bindings/mtd/ingenic,jz4780-nand.txt
new file mode 100644
index 000000000000..29ea5853ca91
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/ingenic,jz4780-nand.txt
@@ -0,0 +1,86 @@
+* Ingenic JZ4780 NAND/BCH
+
+This file documents the device tree bindings for NAND flash devices on the
+JZ4780. NAND devices are connected to the NEMC controller (described in
+memory-controllers/ingenic,jz4780-nemc.txt), and thus NAND device nodes must
+be children of the NEMC node.
+
+Required NAND controller device properties:
+- compatible: Should be set to "ingenic,jz4780-nand".
+- reg: For each bank with a NAND chip attached, should specify a bank number,
+ an offset of 0 and a size of 0x1000000 (i.e. the whole NEMC bank).
+
+Optional NAND controller device properties:
+- ingenic,bch-controller: To make use of the hardware BCH controller, this
+ property must contain a phandle for the BCH controller node. The required
+ properties for this node are described below. If this is not specified,
+ software BCH will be used instead.
+
+Optional children nodes:
+- Individual NAND chips are children of the NAND controller node.
+
+Required children node properties:
+- reg: An integer ranging from 1 to 6 representing the CS line to use.
+
+Optional children node properties:
+- nand-ecc-step-size: ECC block size in bytes.
+- nand-ecc-strength: ECC strength (max number of correctable bits).
+- nand-ecc-mode: String, operation mode of the NAND ecc mode. "hw" by default
+- nand-on-flash-bbt: boolean to enable on flash bbt option, if not present false
+- rb-gpios: GPIO specifier for the busy pin.
+- wp-gpios: GPIO specifier for the write protect pin.
+
+Optional child node of NAND chip nodes:
+- partitions: see Documentation/devicetree/bindings/mtd/partition.txt
+
+Example:
+
+nemc: nemc@13410000 {
+ ...
+
+ nandc: nand-controller@1 {
+ compatible = "ingenic,jz4780-nand";
+ reg = <1 0 0x1000000>; /* Bank 1 */
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ingenic,bch-controller = <&bch>;
+
+ nand@1 {
+ reg = <1>;
+
+ nand-ecc-step-size = <1024>;
+ nand-ecc-strength = <24>;
+ nand-ecc-mode = "hw";
+ nand-on-flash-bbt;
+
+ rb-gpios = <&gpa 20 GPIO_ACTIVE_LOW>;
+ wp-gpios = <&gpf 22 GPIO_ACTIVE_LOW>;
+
+ partitions {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ...
+ }
+ };
+ };
+};
+
+The BCH controller is a separate SoC component used for error correction on
+NAND devices. The following is a description of the device properties for a
+BCH controller.
+
+Required BCH properties:
+- compatible: Should be set to "ingenic,jz4780-bch".
+- reg: Should specify the BCH controller registers location and length.
+- clocks: Clock for the BCH controller.
+
+Example:
+
+bch: bch@134d0000 {
+ compatible = "ingenic,jz4780-bch";
+ reg = <0x134d0000 0x10000>;
+
+ clocks = <&cgu JZ4780_CLK_BCH>;
+};
diff --git a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt
index 2bee68103b01..2c91c03e7eb0 100644
--- a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt
+++ b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt
@@ -1,15 +1,61 @@
-* MTD SPI driver for ST M25Pxx (and similar) serial flash chips
+* SPI NOR flash: ST M25Pxx (and similar) serial flash chips
Required properties:
- #address-cells, #size-cells : Must be present if the device has sub-nodes
representing partitions.
- compatible : May include a device-specific string consisting of the
- manufacturer and name of the chip. Bear in mind the DT binding
- is not Linux-only, but in case of Linux, see the "m25p_ids"
- table in drivers/mtd/devices/m25p80.c for the list of supported
- chips.
+ manufacturer and name of the chip. A list of supported chip
+ names follows.
Must also include "jedec,spi-nor" for any SPI NOR flash that can
be identified by the JEDEC READ ID opcode (0x9F).
+
+ Supported chip names:
+ at25df321a
+ at25df641
+ at26df081a
+ mr25h256
+ mx25l4005a
+ mx25l1606e
+ mx25l6405d
+ mx25l12805d
+ mx25l25635e
+ n25q064
+ n25q128a11
+ n25q128a13
+ n25q512a
+ s25fl256s1
+ s25fl512s
+ s25sl12801
+ s25fl008k
+ s25fl064k
+ sst25vf040b
+ m25p40
+ m25p80
+ m25p16
+ m25p32
+ m25p64
+ m25p128
+ w25x80
+ w25x32
+ w25q32
+ w25q32dw
+ w25q80bl
+ w25q128
+ w25q256
+
+ The following chip names have been used historically to
+ designate quirky versions of flash chips that do not support the
+ JEDEC READ ID opcode (0x9F):
+ m25p05-nonjedec
+ m25p10-nonjedec
+ m25p20-nonjedec
+ m25p40-nonjedec
+ m25p80-nonjedec
+ m25p16-nonjedec
+ m25p32-nonjedec
+ m25p64-nonjedec
+ m25p128-nonjedec
+
- reg : Chip-Select number
- spi-max-frequency : Maximum frequency of the SPI bus the chip can operate at
diff --git a/Documentation/devicetree/bindings/mtd/mtk-quadspi.txt b/Documentation/devicetree/bindings/mtd/mtk-quadspi.txt
new file mode 100644
index 000000000000..fb314f09861b
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/mtk-quadspi.txt
@@ -0,0 +1,41 @@
+* Serial NOR flash controller for MTK MT81xx (and similar)
+
+Required properties:
+- compatible: should be "mediatek,mt8173-nor";
+- reg: physical base address and length of the controller's register
+- clocks: the phandle of the clocks needed by the nor controller
+- clock-names: the names of the clocks
+ the clocks should be named "spi" and "sf". "spi" is used for spi bus,
+ and "sf" is used for controller, these are the clocks witch
+ hardware needs to enabling nor flash and nor flash controller.
+ See Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
+- #address-cells: should be <1>
+- #size-cells: should be <0>
+
+The SPI flash must be a child of the nor_flash node and must have a
+compatible property. Also see jedec,spi-nor.txt.
+
+Required properties:
+- compatible: May include a device-specific string consisting of the manufacturer
+ and name of the chip. Must also include "jedec,spi-nor" for any
+ SPI NOR flash that can be identified by the JEDEC READ ID opcode (0x9F).
+- reg : Chip-Select number
+
+Example:
+
+nor_flash: spi@1100d000 {
+ compatible = "mediatek,mt8173-nor";
+ reg = <0 0x1100d000 0 0xe0>;
+ clocks = <&pericfg CLK_PERI_SPI>,
+ <&topckgen CLK_TOP_SPINFI_IFR_SEL>;
+ clock-names = "spi", "sf";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+
+ flash@0 {
+ compatible = "jedec,spi-nor";
+ reg = <0>;
+ };
+};
+
diff --git a/Documentation/devicetree/bindings/mtd/partition.txt b/Documentation/devicetree/bindings/mtd/partition.txt
index f1e2a02381a4..81a224da63be 100644
--- a/Documentation/devicetree/bindings/mtd/partition.txt
+++ b/Documentation/devicetree/bindings/mtd/partition.txt
@@ -6,7 +6,9 @@ used for what purposes, but which don't use an on-flash partition table such
as RedBoot.
The partition table should be a subnode of the mtd node and should be named
-'partitions'. Partitions are defined in subnodes of the partitions node.
+'partitions'. This node should have the following property:
+- compatible : (required) must be "fixed-partitions"
+Partitions are then defined in subnodes of the partitions node.
For backwards compatibility partitions as direct subnodes of the mtd device are
supported. This use is discouraged.
@@ -30,12 +32,15 @@ Optional properties:
partition should only be mounted read-only. This is usually used for flash
partitions containing early-boot firmware images or data which should not be
clobbered.
+- lock : Do not unlock the partition at initialization time (not supported on
+ all devices)
Examples:
flash@0 {
partitions {
+ compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
@@ -53,6 +58,7 @@ flash@0 {
flash@1 {
partitions {
+ compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <2>;
@@ -66,6 +72,7 @@ flash@1 {
flash@2 {
partitions {
+ compatible = "fixed-partitions";
#address-cells = <2>;
#size-cells = <2>;
diff --git a/Documentation/devicetree/bindings/net/cdns-emac.txt b/Documentation/devicetree/bindings/net/cdns-emac.txt
deleted file mode 100644
index 4451ee973223..000000000000
--- a/Documentation/devicetree/bindings/net/cdns-emac.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-* Cadence EMAC Ethernet controller
-
-Required properties:
-- compatible: Should be "cdns,[<chip>-]{emac}"
- Use "cdns,at91rm9200-emac" Atmel at91rm9200 SoC.
- Use "cdns,zynq-gem" Xilinx Zynq-7xxx SoC.
- Or the generic form: "cdns,emac".
-- reg: Address and length of the register set for the device
-- interrupts: Should contain macb interrupt
-- phy-mode: see ethernet.txt file in the same directory.
-
-Examples:
-
- macb0: ethernet@fffc4000 {
- compatible = "cdns,at91rm9200-emac";
- reg = <0xfffc4000 0x4000>;
- interrupts = <21>;
- phy-mode = "rmii";
- local-mac-address = [3a 0e 03 04 05 06];
- };
diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt
index 9853f8e70966..28a4781ab6d7 100644
--- a/Documentation/devicetree/bindings/net/cpsw.txt
+++ b/Documentation/devicetree/bindings/net/cpsw.txt
@@ -40,18 +40,18 @@ Optional properties:
Slave Properties:
Required properties:
-- phy_id : Specifies slave phy id
- phy-mode : See ethernet.txt file in the same directory
Optional properties:
- dual_emac_res_vlan : Specifies VID to be used to segregate the ports
- mac-address : See ethernet.txt file in the same directory
+- phy_id : Specifies slave phy id
- phy-handle : See ethernet.txt file in the same directory
Slave sub-nodes:
- fixed-link : See fixed-link.txt file in the same directory
- Either the properties phy_id and phy-mode,
- or the sub-node fixed-link can be specified
+ Either the property phy_id, or the sub-node
+ fixed-link can be specified
Note: "ti,hwmods" field is used to fetch the base address and irq
resources from TI, omap hwmod data base during device registration.
diff --git a/Documentation/devicetree/bindings/net/dsa/dsa.txt b/Documentation/devicetree/bindings/net/dsa/dsa.txt
index 04e6bef3ac3f..5fdbbcdf8c4b 100644
--- a/Documentation/devicetree/bindings/net/dsa/dsa.txt
+++ b/Documentation/devicetree/bindings/net/dsa/dsa.txt
@@ -31,6 +31,8 @@ A switch child node has the following optional property:
switch. Must be set if the switch can not detect
the presence and/or size of a connected EEPROM,
otherwise optional.
+- reset-gpios : phandle and specifier to a gpio line connected to
+ reset pin of the switch chip.
A switch may have multiple "port" children nodes
@@ -114,6 +116,7 @@ Example:
#size-cells = <0>;
reg = <17 1>; /* MDIO address 17, switch 1 in tree */
mii-bus = <&mii_bus1>;
+ reset-gpios = <&gpio5 1 GPIO_ACTIVE_LOW>;
switch1port0: port@0 {
reg = <0>;
diff --git a/Documentation/devicetree/bindings/net/hisilicon-hns-mdio.txt b/Documentation/devicetree/bindings/net/hisilicon-hns-mdio.txt
index 9c23fdf25018..4a7ede9657b0 100644
--- a/Documentation/devicetree/bindings/net/hisilicon-hns-mdio.txt
+++ b/Documentation/devicetree/bindings/net/hisilicon-hns-mdio.txt
@@ -1,7 +1,12 @@
Hisilicon MDIO bus controller
Properties:
-- compatible: "hisilicon,mdio","hisilicon,hns-mdio".
+- compatible: can be one of:
+ "hisilicon,hns-mdio"
+ "hisilicon,mdio"
+ "hisilicon,hns-mdio" is recommended to be used for hip05 and later SOCs,
+ while "hisilicon,mdio" is optional for backwards compatibility only on
+ hip04 Soc.
- reg: The base address of the MDIO bus controller register bank.
- #address-cells: Must be <1>.
- #size-cells: Must be <0>. MDIO addresses have no size component.
diff --git a/Documentation/devicetree/bindings/net/ieee802154/adf7242.txt b/Documentation/devicetree/bindings/net/ieee802154/adf7242.txt
new file mode 100644
index 000000000000..dea5124cdc52
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/ieee802154/adf7242.txt
@@ -0,0 +1,18 @@
+* ADF7242 IEEE 802.15.4 *
+
+Required properties:
+ - compatible: should be "adi,adf7242"
+ - spi-max-frequency: maximal bus speed (12.5 MHz)
+ - reg: the chipselect index
+ - interrupts: the interrupt generated by the device via pin IRQ1.
+ IRQ_TYPE_LEVEL_HIGH (4) or IRQ_TYPE_EDGE_FALLING (1)
+
+Example:
+
+ adf7242@0 {
+ compatible = "adi,adf7242";
+ spi-max-frequency = <10000000>;
+ reg = <0>;
+ interrupts = <98 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-parent = <&gpio3>;
+ };
diff --git a/Documentation/devicetree/bindings/net/macb.txt b/Documentation/devicetree/bindings/net/macb.txt
index b5d79761ac97..d2e243b1ec0e 100644
--- a/Documentation/devicetree/bindings/net/macb.txt
+++ b/Documentation/devicetree/bindings/net/macb.txt
@@ -2,15 +2,19 @@
Required properties:
- compatible: Should be "cdns,[<chip>-]{macb|gem}"
+ Use "cdns,at91rm9200-emac" Atmel at91rm9200 SoC.
Use "cdns,at91sam9260-macb" for Atmel at91sam9 SoCs or the 10/100Mbit IP
available on sama5d3 SoCs.
+ Use "cdns,np4-macb" for NP4 SoC devices.
Use "cdns,at32ap7000-macb" for other 10/100 usage or use the generic form: "cdns,macb".
Use "cdns,pc302-gem" for Picochip picoXcell pc302 and later devices based on
the Cadence GEM, or the generic form: "cdns,gem".
Use "atmel,sama5d2-gem" for the GEM IP (10/100) available on Atmel sama5d2 SoCs.
Use "atmel,sama5d3-gem" for the Gigabit IP available on Atmel sama5d3 SoCs.
Use "atmel,sama5d4-gem" for the GEM IP (10/100) available on Atmel sama5d4 SoCs.
+ Use "cdns,zynq-gem" Xilinx Zynq-7xxx SoC.
Use "cdns,zynqmp-gem" for Zynq Ultrascale+ MPSoC.
+ Or the generic form: "cdns,emac".
- reg: Address and length of the register set for the device
- interrupts: Should contain macb interrupt
- phy-mode: See ethernet.txt file in the same directory.
@@ -19,6 +23,9 @@ Required properties:
Optional elements: 'tx_clk'
- clocks: Phandles to input clocks.
+Optional properties for PHY child node:
+- reset-gpios : Should specify the gpio for phy reset
+
Examples:
macb0: ethernet@fffc4000 {
@@ -29,4 +36,8 @@ Examples:
local-mac-address = [3a 0e 03 04 05 06];
clock-names = "pclk", "hclk", "tx_clk";
clocks = <&clkc 30>, <&clkc 30>, <&clkc 13>;
+ ethernet-phy@1 {
+ reg = <0x1>;
+ reset-gpios = <&pioE 6 1>;
+ };
};
diff --git a/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt
index f5a8ca29aff0..aeea50c84e92 100644
--- a/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt
+++ b/Documentation/devicetree/bindings/net/marvell-armada-370-neta.txt
@@ -8,6 +8,11 @@ Required properties:
- phy-mode: See ethernet.txt file in the same directory
- clocks: a pointer to the reference clock for this device.
+Optional properties:
+- tx-csum-limit: maximum mtu supported by port that allow TX checksum.
+ Value is presented in bytes. If not used, by default 1600B is set for
+ "marvell,armada-370-neta" and 9800B for others.
+
Example:
ethernet@d0070000 {
@@ -15,6 +20,7 @@ ethernet@d0070000 {
reg = <0xd0070000 0x2500>;
interrupts = <8>;
clocks = <&gate_clk 4>;
+ tx-csum-limit = <9800>
status = "okay";
phy = <&phy0>;
phy-mode = "rgmii-id";
diff --git a/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt b/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt
index 692076fda0e5..f9c32adab5c6 100644
--- a/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt
+++ b/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt
@@ -1,8 +1,9 @@
Micrel KSZ9021/KSZ9031 Gigabit Ethernet PHY
-Some boards require special tuning values, particularly when it comes to
-clock delays. You can specify clock delay values by adding
-micrel-specific properties to an Ethernet OF device node.
+Some boards require special tuning values, particularly when it comes
+to clock delays. You can specify clock delay values in the PHY OF
+device node. Deprecated, but still supported, these properties can
+also be added to an Ethernet OF device node.
Note that these settings are applied after any phy-specific fixup from
phy_fixup_list (see phy_init_hw() from drivers/net/phy/phy_device.c),
@@ -57,16 +58,6 @@ KSZ9031:
Examples:
- /* Attach to an Ethernet device with autodetected PHY */
- &enet {
- rxc-skew-ps = <3000>;
- rxdv-skew-ps = <0>;
- txc-skew-ps = <3000>;
- txen-skew-ps = <0>;
- status = "okay";
- };
-
- /* Attach to an explicitly-specified PHY */
mdio {
phy0: ethernet-phy@0 {
rxc-skew-ps = <3000>;
diff --git a/Documentation/devicetree/bindings/net/nfc/st95hf.txt b/Documentation/devicetree/bindings/net/nfc/st95hf.txt
new file mode 100644
index 000000000000..ea3178bc9ddd
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/nfc/st95hf.txt
@@ -0,0 +1,50 @@
+* STMicroelectronics : NFC Transceiver ST95HF
+
+ST NFC Transceiver is required to attach with SPI bus.
+ST95HF node should be defined in DT as SPI slave device of SPI
+master with which ST95HF transceiver is physically connected.
+The properties defined below are required to be the part of DT
+to include ST95HF transceiver into the platform.
+
+Required properties:
+===================
+- reg: Address of SPI slave "ST95HF transceiver" on SPI master bus.
+
+- compatible: should be "st,st95hf" for ST95HF NFC transceiver
+
+- spi-max-frequency: Max. operating SPI frequency for ST95HF
+ transceiver.
+
+- enable-gpio: GPIO line to enable ST95HF transceiver.
+
+- interrupt-parent : Standard way to specify the controller to which
+ ST95HF transceiver's interrupt is routed.
+
+- interrupts : Standard way to define ST95HF transceiver's out
+ interrupt.
+
+Optional property:
+=================
+- st95hfvin-supply : This is an optional property. It contains a
+ phandle to ST95HF transceiver's regulator supply node in DT.
+
+Example:
+=======
+spi@9840000 {
+ reg = <0x9840000 0x110>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cs-gpios = <&pio0 4>;
+ status = "okay";
+
+ st95hf@0{
+ reg = <0>;
+ compatible = "st,st95hf";
+ status = "okay";
+ spi-max-frequency = <1000000>;
+ enable-gpio = <&pio4 0>;
+ interrupt-parent = <&pio0>;
+ interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
+ };
+
+};
diff --git a/Documentation/devicetree/bindings/net/renesas,ravb.txt b/Documentation/devicetree/bindings/net/renesas,ravb.txt
index b486f3f5f6a3..81a9f9e6b45f 100644
--- a/Documentation/devicetree/bindings/net/renesas,ravb.txt
+++ b/Documentation/devicetree/bindings/net/renesas,ravb.txt
@@ -5,8 +5,18 @@ interface contains.
Required properties:
- compatible: "renesas,etheravb-r8a7790" if the device is a part of R8A7790 SoC.
+ "renesas,etheravb-r8a7791" if the device is a part of R8A7791 SoC.
+ "renesas,etheravb-r8a7792" if the device is a part of R8A7792 SoC.
+ "renesas,etheravb-r8a7793" if the device is a part of R8A7793 SoC.
"renesas,etheravb-r8a7794" if the device is a part of R8A7794 SoC.
"renesas,etheravb-r8a7795" if the device is a part of R8A7795 SoC.
+ "renesas,etheravb-rcar-gen2" for generic R-Car Gen 2 compatible interface.
+ "renesas,etheravb-rcar-gen3" for generic R-Car Gen 3 compatible interface.
+
+ When compatible with the generic version, nodes must list the
+ SoC-specific version corresponding to the platform first
+ followed by the generic version.
+
- reg: offset and length of (1) the register block and (2) the stream buffer.
- interrupts: A list of interrupt-specifiers, one for each entry in
interrupt-names.
@@ -37,7 +47,7 @@ Optional properties:
Example:
ethernet@e6800000 {
- compatible = "renesas,etheravb-r8a7795";
+ compatible = "renesas,etheravb-r8a7795", "renesas,etheravb-rcar-gen3";
reg = <0 0xe6800000 0 0x800>, <0 0xe6a00000 0 0x10000>;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>,
diff --git a/Documentation/devicetree/bindings/net/socfpga-dwmac.txt b/Documentation/devicetree/bindings/net/socfpga-dwmac.txt
index 3a9d67951606..72d82d684342 100644
--- a/Documentation/devicetree/bindings/net/socfpga-dwmac.txt
+++ b/Documentation/devicetree/bindings/net/socfpga-dwmac.txt
@@ -11,6 +11,8 @@ Required properties:
designware version numbers documented in stmmac.txt
- altr,sysmgr-syscon : Should be the phandle to the system manager node that
encompasses the glue register, the register offset, and the register shift.
+ - altr,f2h_ptp_ref_clk use f2h_ptp_ref_clk instead of default eosc1 clock
+ for ptp ref clk. This affects all emacs as the clock is common.
Optional properties:
altr,emac-splitter: Should be the phandle to the emac splitter soft IP node if
diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt
index f34fc3c81a75..e862a922bd3f 100644
--- a/Documentation/devicetree/bindings/net/stmmac.txt
+++ b/Documentation/devicetree/bindings/net/stmmac.txt
@@ -35,18 +35,18 @@ Optional properties:
- reset-names: Should contain the reset signal name "stmmaceth", if a
reset phandle is given
- max-frame-size: See ethernet.txt file in the same directory
-- clocks: If present, the first clock should be the GMAC main clock and
- the second clock should be peripheral's register interface clock. Further
- clocks may be specified in derived bindings.
-- clock-names: One name for each entry in the clocks property, the
- first one should be "stmmaceth" and the second one should be "pclk".
-- clk_ptp_ref: this is the PTP reference clock; in case of the PTP is
- available this clock is used for programming the Timestamp Addend Register.
- If not passed then the system clock will be used and this is fine on some
- platforms.
+- clocks: If present, the first clock should be the GMAC main clock
+ The optional second clock should be peripheral's register interface clock.
+ The third optional clock should be the ptp reference clock.
+ Further clocks may be specified in derived bindings.
+- clock-names: One name for each entry in the clocks property.
+ The first one should be "stmmaceth".
+ The optional second one should be "pclk".
+ The optional third one should be "clk_ptp_ref".
- snps,burst_len: The AXI burst lenth value of the AXI BUS MODE register.
- tx-fifo-depth: See ethernet.txt file in the same directory
- rx-fifo-depth: See ethernet.txt file in the same directory
+- mdio: with compatible = "snps,dwmac-mdio", create and register mdio bus.
Examples:
@@ -65,4 +65,11 @@ Examples:
tx-fifo-depth = <16384>;
clocks = <&clock>;
clock-names = "stmmaceth";
+ mdio0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "snps,dwmac-mdio";
+ phy1: ethernet-phy@0 {
+ };
+ };
};
diff --git a/Documentation/devicetree/bindings/opp/opp.txt b/Documentation/devicetree/bindings/opp/opp.txt
index 0cb44dc21f97..601256fe8c0d 100644
--- a/Documentation/devicetree/bindings/opp/opp.txt
+++ b/Documentation/devicetree/bindings/opp/opp.txt
@@ -45,21 +45,10 @@ 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
@@ -100,6 +89,14 @@ Optional properties:
Entries for multiple regulators must be present in the same order as
regulators are specified in device's DT node.
+- opp-microvolt-<name>: Named opp-microvolt property. This is exactly similar to
+ the above opp-microvolt property, but allows multiple voltage ranges to be
+ provided for the same OPP. At runtime, the platform can pick a <name> and
+ matching opp-microvolt-<name> property will be enabled for all OPPs. If the
+ platform doesn't pick a specific <name> or the <name> doesn't match with any
+ opp-microvolt-<name> properties, then opp-microvolt property shall be used, if
+ present.
+
- 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
@@ -112,6 +109,9 @@ Optional properties:
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.
+- opp-microamp-<name>: Named opp-microamp property. Similar to
+ opp-microvolt-<name> property, but for microamp instead.
+
- clock-latency-ns: Specifies the maximum possible transition latency (in
nanoseconds) for switching to this OPP from any other OPP.
@@ -123,6 +123,26 @@ Optional properties:
- opp-suspend: Marks the OPP to be used during device suspend. Only one OPP in
the table should have this.
+- opp-supported-hw: This enables us to select only a subset of OPPs from the
+ larger OPP table, based on what version of the hardware we are running on. We
+ still can't have multiple nodes with the same opp-hz value in OPP table.
+
+ It's an user defined array containing a hierarchy of hardware version numbers,
+ supported by the OPP. For example: a platform with hierarchy of three levels
+ of versions (A, B and C), this field should be like <X Y Z>, where X
+ corresponds to Version hierarchy A, Y corresponds to version hierarchy B and Z
+ corresponds to version hierarchy C.
+
+ Each level of hierarchy is represented by a 32 bit value, and so there can be
+ only 32 different supported version per hierarchy. i.e. 1 bit per version. A
+ value of 0xFFFFFFFF will enable the OPP for all versions for that hierarchy
+ level. And a value of 0x00000000 will disable the OPP completely, and so we
+ never want that to happen.
+
+ If 32 values aren't sufficient for a version hierarchy, than that version
+ hierarchy can be contained in multiple 32 bit values. i.e. <X Y Z1 Z2> in the
+ above example, Z1 & Z2 refer to the version hierarchy Z.
+
- status: Marks the node enabled/disabled.
Example 1: Single cluster Dual-core ARM cortex A9, switch DVFS states together.
@@ -157,20 +177,20 @@ Example 1: Single cluster Dual-core ARM cortex A9, switch DVFS states together.
compatible = "operating-points-v2";
opp-shared;
- opp00 {
+ opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <970000 975000 985000>;
opp-microamp = <70000>;
clock-latency-ns = <300000>;
opp-suspend;
};
- opp01 {
+ opp@1100000000 {
opp-hz = /bits/ 64 <1100000000>;
opp-microvolt = <980000 1000000 1010000>;
opp-microamp = <80000>;
clock-latency-ns = <310000>;
};
- opp02 {
+ opp@1200000000 {
opp-hz = /bits/ 64 <1200000000>;
opp-microvolt = <1025000>;
clock-latency-ns = <290000>;
@@ -236,20 +256,20 @@ independently.
* independently.
*/
- opp00 {
+ opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <970000 975000 985000>;
opp-microamp = <70000>;
clock-latency-ns = <300000>;
opp-suspend;
};
- opp01 {
+ opp@1100000000 {
opp-hz = /bits/ 64 <1100000000>;
opp-microvolt = <980000 1000000 1010000>;
opp-microamp = <80000>;
clock-latency-ns = <310000>;
};
- opp02 {
+ opp@1200000000 {
opp-hz = /bits/ 64 <1200000000>;
opp-microvolt = <1025000>;
opp-microamp = <90000;
@@ -312,20 +332,20 @@ DVFS state together.
compatible = "operating-points-v2";
opp-shared;
- opp00 {
+ opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <970000 975000 985000>;
opp-microamp = <70000>;
clock-latency-ns = <300000>;
opp-suspend;
};
- opp01 {
+ opp@1100000000 {
opp-hz = /bits/ 64 <1100000000>;
opp-microvolt = <980000 1000000 1010000>;
opp-microamp = <80000>;
clock-latency-ns = <310000>;
};
- opp02 {
+ opp@1200000000 {
opp-hz = /bits/ 64 <1200000000>;
opp-microvolt = <1025000>;
opp-microamp = <90000>;
@@ -338,20 +358,20 @@ DVFS state together.
compatible = "operating-points-v2";
opp-shared;
- opp10 {
+ opp@1300000000 {
opp-hz = /bits/ 64 <1300000000>;
opp-microvolt = <1045000 1050000 1055000>;
opp-microamp = <95000>;
clock-latency-ns = <400000>;
opp-suspend;
};
- opp11 {
+ opp@1400000000 {
opp-hz = /bits/ 64 <1400000000>;
opp-microvolt = <1075000>;
opp-microamp = <100000>;
clock-latency-ns = <400000>;
};
- opp12 {
+ opp@1500000000 {
opp-hz = /bits/ 64 <1500000000>;
opp-microvolt = <1010000 1100000 1110000>;
opp-microamp = <95000>;
@@ -378,7 +398,7 @@ Example 4: Handling multiple regulators
compatible = "operating-points-v2";
opp-shared;
- opp00 {
+ opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <970000>, /* Supply 0 */
<960000>, /* Supply 1 */
@@ -391,7 +411,7 @@ Example 4: Handling multiple regulators
/* OR */
- opp00 {
+ opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <970000 975000 985000>, /* Supply 0 */
<960000 965000 975000>, /* Supply 1 */
@@ -404,7 +424,7 @@ Example 4: Handling multiple regulators
/* OR */
- opp00 {
+ opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <970000 975000 985000>, /* Supply 0 */
<960000 965000 975000>, /* Supply 1 */
@@ -417,7 +437,8 @@ Example 4: Handling multiple regulators
};
};
-Example 5: Multiple OPP tables
+Example 5: opp-supported-hw
+(example: three level hierarchy of versions: cuts, substrate and process)
/ {
cpus {
@@ -426,40 +447,73 @@ Example 5: Multiple OPP tables
...
cpu-supply = <&cpu_supply>
- operating-points-v2 = <&cpu0_opp_table_slow>, <&cpu0_opp_table_fast>;
- operating-points-names = "slow", "fast";
+ operating-points-v2 = <&cpu0_opp_table_slow>;
};
};
- cpu0_opp_table_slow: opp_table_slow {
+ opp_table {
compatible = "operating-points-v2";
status = "okay";
opp-shared;
- opp00 {
+ opp@600000000 {
+ /*
+ * Supports all substrate and process versions for 0xF
+ * cuts, i.e. only first four cuts.
+ */
+ opp-supported-hw = <0xF 0xFFFFFFFF 0xFFFFFFFF>
opp-hz = /bits/ 64 <600000000>;
+ opp-microvolt = <900000 915000 925000>;
...
};
- opp01 {
+ opp@800000000 {
+ /*
+ * Supports:
+ * - cuts: only one, 6th cut (represented by 6th bit).
+ * - substrate: supports 16 different substrate versions
+ * - process: supports 9 different process versions
+ */
+ opp-supported-hw = <0x20 0xff0000ff 0x0000f4f0>
opp-hz = /bits/ 64 <800000000>;
+ opp-microvolt = <900000 915000 925000>;
...
};
};
+};
+
+Example 6: opp-microvolt-<name>, opp-microamp-<name>:
+(example: device with two possible microvolt ranges: slow and fast)
- cpu0_opp_table_fast: opp_table_fast {
+/ {
+ cpus {
+ cpu@0 {
+ compatible = "arm,cortex-a7";
+ ...
+
+ operating-points-v2 = <&cpu0_opp_table>;
+ };
+ };
+
+ cpu0_opp_table: opp_table0 {
compatible = "operating-points-v2";
- status = "okay";
opp-shared;
- opp10 {
+ opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
- ...
+ opp-microvolt-slow = <900000 915000 925000>;
+ opp-microvolt-fast = <970000 975000 985000>;
+ opp-microamp-slow = <70000>;
+ opp-microamp-fast = <71000>;
};
- opp11 {
- opp-hz = /bits/ 64 <1100000000>;
- ...
+ opp@1200000000 {
+ opp-hz = /bits/ 64 <1200000000>;
+ opp-microvolt-slow = <900000 915000 925000>, /* Supply vcc0 */
+ <910000 925000 935000>; /* Supply vcc1 */
+ opp-microvolt-fast = <970000 975000 985000>, /* Supply vcc0 */
+ <960000 965000 975000>; /* Supply vcc1 */
+ opp-microamp = <70000>; /* Will be used for both slow/fast */
};
};
};
diff --git a/Documentation/devicetree/bindings/phy/brcm,brcmstb-sata-phy.txt b/Documentation/devicetree/bindings/phy/brcm,brcmstb-sata-phy.txt
index 7f81ef90146a..d87ab7c127b8 100644
--- a/Documentation/devicetree/bindings/phy/brcm,brcmstb-sata-phy.txt
+++ b/Documentation/devicetree/bindings/phy/brcm,brcmstb-sata-phy.txt
@@ -2,6 +2,7 @@
Required properties:
- compatible: should be one or more of
+ "brcm,bcm7425-sata-phy"
"brcm,bcm7445-sata-phy"
"brcm,phy-sata3"
- address-cells: should be 1
diff --git a/Documentation/devicetree/bindings/phy/phy-hi6220-usb.txt b/Documentation/devicetree/bindings/phy/phy-hi6220-usb.txt
new file mode 100644
index 000000000000..f17a56e2152f
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/phy-hi6220-usb.txt
@@ -0,0 +1,16 @@
+Hisilicon hi6220 usb PHY
+-----------------------
+
+Required properties:
+- compatible: should be "hisilicon,hi6220-usb-phy"
+- #phy-cells: must be 0
+- hisilicon,peripheral-syscon: phandle of syscon used to control phy.
+Refer to phy/phy-bindings.txt for the generic PHY binding properties
+
+Example:
+ usb_phy: usbphy {
+ compatible = "hisilicon,hi6220-usb-phy";
+ #phy-cells = <0>;
+ phy-supply = <&fixed_5v_hub>;
+ hisilicon,peripheral-syscon = <&sys_ctrl>;
+ };
diff --git a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt
new file mode 100644
index 000000000000..2390e4e9c84c
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt
@@ -0,0 +1,39 @@
+* Renesas R-Car generation 3 USB 2.0 PHY
+
+This file provides information on what the device node for the R-Car generation
+3 USB 2.0 PHY contains.
+
+Required properties:
+- compatible: "renesas,usb2-phy-r8a7795" if the device is a part of an R8A7795
+ SoC.
+- reg: offset and length of the partial USB 2.0 Host register block.
+- reg-names: must be "usb2_host".
+- clocks: clock phandle and specifier pair(s).
+- #phy-cells: see phy-bindings.txt in the same directory, must be <0>.
+
+Optional properties:
+To use a USB channel where USB 2.0 Host and HSUSB (USB 2.0 Peripheral) are
+combined, the device tree node should set HSUSB properties to reg and reg-names
+properties. This is because HSUSB has registers to select USB 2.0 host or
+peripheral at that channel:
+- reg: offset and length of the partial HSUSB register block.
+- reg-names: must be "hsusb".
+- interrupts: interrupt specifier for the PHY.
+
+Example (R-Car H3):
+
+ usb-phy@ee080200 {
+ compatible = "renesas,usb2-phy-r8a7795";
+ reg = <0 0xee080200 0 0x700>, <0 0xe6590100 0 0x100>;
+ reg-names = "usb2_host", "hsusb";
+ interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&mstp7_clks R8A7795_CLK_EHCI0>,
+ <&mstp7_clks R8A7795_CLK_HSUSB>;
+ };
+
+ usb-phy@ee0a0200 {
+ compatible = "renesas,usb2-phy-r8a7795";
+ reg = <0 0xee0a0200 0 0x700>;
+ reg-names = "usb2_host";
+ clocks = <&mstp7_clks R8A7795_CLK_EHCI0>;
+ };
diff --git a/Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt b/Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt
index 826454ac43bb..68498d560354 100644
--- a/Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt
+++ b/Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt
@@ -1,7 +1,10 @@
ROCKCHIP USB2 PHY
Required properties:
- - compatible: rockchip,rk3288-usb-phy
+ - compatible: matching the soc type, one of
+ "rockchip,rk3066a-usb-phy"
+ "rockchip,rk3188-usb-phy"
+ "rockchip,rk3288-usb-phy"
- rockchip,grf : phandle to the syscon managing the "general
register files"
- #address-cells: should be 1
@@ -21,6 +24,7 @@ required properties:
Optional Properties:
- clocks : phandle + clock specifier for the phy clocks
- clock-names: string, clock name, must be "phyclk"
+- #clock-cells: for users of the phy-pll, should be 0
Example:
diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
index 0cebf7454517..95736d77fbb7 100644
--- a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
+++ b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
@@ -9,6 +9,7 @@ Required properties:
* allwinner,sun7i-a20-usb-phy
* allwinner,sun8i-a23-usb-phy
* allwinner,sun8i-a33-usb-phy
+ * allwinner,sun8i-h3-usb-phy
- reg : a list of offset + length pairs
- reg-names :
* "phy_ctrl"
diff --git a/Documentation/devicetree/bindings/phy/ti-phy.txt b/Documentation/devicetree/bindings/phy/ti-phy.txt
index 9cf9446eaf2e..a3b394587874 100644
--- a/Documentation/devicetree/bindings/phy/ti-phy.txt
+++ b/Documentation/devicetree/bindings/phy/ti-phy.txt
@@ -31,6 +31,8 @@ OMAP USB2 PHY
Required properties:
- compatible: Should be "ti,omap-usb2"
+ Should be "ti,dra7x-usb2-phy2" for the 2nd instance of USB2 PHY
+ in DRA7x
- reg : Address and length of the register set for the device.
- #phy-cells: determine the number of cells that should be given in the
phandle while referencing this phy.
@@ -40,10 +42,14 @@ Required properties:
* "wkupclk" - wakeup clock.
* "refclk" - reference clock (optional).
-Optional properties:
+Deprecated properties:
- ctrl-module : phandle of the control module used by PHY driver to power on
the PHY.
+Recommended properies:
+- syscon-phy-power : phandle/offset pair. Phandle to the system control
+ module and the register offset to power on/off the PHY.
+
This is usually a subnode of ocp2scp to which it is connected.
usb2phy@4a0ad080 {
@@ -77,14 +83,22 @@ Required properties:
* "div-clk" - apll clock
Optional properties:
- - ctrl-module : phandle of the control module used by PHY driver to power on
- the PHY.
- id: If there are multiple instance of the same type, in order to
differentiate between each instance "id" can be used (e.g., multi-lane PCIe
PHY). If "id" is not provided, it is set to default value of '1'.
- syscon-pllreset: Handle to system control region that contains the
CTRL_CORE_SMA_SW_0 register and register offset to the CTRL_CORE_SMA_SW_0
register that contains the SATA_PLL_SOFT_RESET bit. Only valid for sata_phy.
+ - syscon-pcs : phandle/offset pair. Phandle to the system control module and the
+ register offset to write the PCS delay value.
+
+Deprecated properties:
+ - ctrl-module : phandle of the control module used by PHY driver to power on
+ the PHY.
+
+Recommended properies:
+ - syscon-phy-power : phandle/offset pair. Phandle to the system control
+ module and the register offset to power on/off the PHY.
This is usually a subnode of ocp2scp to which it is connected.
diff --git a/Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt
index b321b26780dc..9213b27e1036 100644
--- a/Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/allwinner,sunxi-pinctrl.txt
@@ -17,7 +17,10 @@ Required properties:
"allwinner,sun8i-a23-pinctrl"
"allwinner,sun8i-a23-r-pinctrl"
"allwinner,sun8i-a33-pinctrl"
+ "allwinner,sun9i-a80-pinctrl"
+ "allwinner,sun9i-a80-r-pinctrl"
"allwinner,sun8i-a83t-pinctrl"
+ "allwinner,sun8i-h3-pinctrl"
- reg: Should contain the register physical address and length for the
pin controller.
diff --git a/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-gpio.txt b/Documentation/devicetree/bindings/pinctrl/brcm,iproc-gpio.txt
index 16589fb6f420..e4277921f3e3 100644
--- a/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-gpio.txt
+++ b/Documentation/devicetree/bindings/pinctrl/brcm,iproc-gpio.txt
@@ -1,4 +1,4 @@
-Broadcom Cygnus GPIO/PINCONF Controller
+Broadcom iProc GPIO/PINCONF Controller
Required properties:
@@ -7,9 +7,12 @@ Required properties:
"brcm,cygnus-crmu-gpio" or "brcm,iproc-gpio"
- reg:
- Define the base and range of the I/O address space that contains the Cygnus
+ Define the base and range of the I/O address space that contains SoC
GPIO/PINCONF controller registers
+- ngpios:
+ Total number of in-use slots in GPIO controller
+
- #gpio-cells:
Must be two. The first cell is the GPIO pin number (within the
controller's pin space) and the second cell is used for the following:
@@ -57,6 +60,7 @@ Example:
compatible = "brcm,cygnus-ccm-gpio";
reg = <0x1800a000 0x50>,
<0x0301d164 0x20>;
+ ngpios = <24>;
#gpio-cells = <2>;
gpio-controller;
interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
@@ -78,6 +82,7 @@ Example:
gpio_asiu: gpio@180a5000 {
compatible = "brcm,cygnus-asiu-gpio";
reg = <0x180a5000 0x668>;
+ ngpios = <146>;
#gpio-cells = <2>;
gpio-controller;
interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
diff --git a/Documentation/devicetree/bindings/pinctrl/brcm,nsp-gpio.txt b/Documentation/devicetree/bindings/pinctrl/brcm,nsp-gpio.txt
new file mode 100644
index 000000000000..0844168a6dd4
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/brcm,nsp-gpio.txt
@@ -0,0 +1,80 @@
+Broadcom Northstar plus (NSP) GPIO/PINCONF Controller
+
+Required properties:
+- compatible:
+ Must be "brcm,nsp-gpio-a"
+
+- reg:
+ Should contain the register physical address and length for each of
+ GPIO base, IO control registers
+
+- #gpio-cells:
+ Must be two. The first cell is the GPIO pin number (within the
+ controller's pin space) and the second cell is used for the following:
+ bit[0]: polarity (0 for active high and 1 for active low)
+
+- gpio-controller:
+ Specifies that the node is a GPIO controller
+
+- ngpios:
+ Number of gpios supported (58x25 supports 32 and 58x23 supports 24)
+
+Optional properties:
+- interrupts:
+ Interrupt ID
+
+- interrupt-controller:
+ Specifies that the node is an interrupt controller
+
+- gpio-ranges:
+ Specifies the mapping between gpio controller and pin-controllers pins.
+ This requires 4 fields in cells defined as -
+ 1. Phandle of pin-controller.
+ 2. GPIO base pin offset.
+ 3 Pin-control base pin offset.
+ 4. number of gpio pins which are linearly mapped from pin base.
+
+Supported generic PINCONF properties in child nodes:
+- pins:
+ The list of pins (within the controller's own pin space) that properties
+ in the node apply to. Pin names are "gpio-<pin>"
+
+- bias-disable:
+ Disable pin bias
+
+- bias-pull-up:
+ Enable internal pull up resistor
+
+- bias-pull-down:
+ Enable internal pull down resistor
+
+- drive-strength:
+ Valid drive strength values include 2, 4, 6, 8, 10, 12, 14, 16 (mA)
+
+Example:
+
+ gpioa: gpio@18000020 {
+ compatible = "brcm,nsp-gpio-a";
+ reg = <0x18000020 0x100>,
+ <0x1803f1c4 0x1c>;
+ #gpio-cells = <2>;
+ gpio-controller;
+ ngpios = <32>;
+ gpio-ranges = <&pinctrl 0 0 31>;
+ interrupt-controller;
+ interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
+
+ /* Hog a few default settings */
+ pinctrl-names = "default";
+ pinctrl-0 = <&led>;
+ led: led {
+ pins = "gpio-1";
+ bias-pull-up;
+ };
+
+ pwr: pwr {
+ gpio-hog;
+ gpios = <3 1>;
+ output-high;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pinctrl/lantiq,pinctrl-xway.txt b/Documentation/devicetree/bindings/pinctrl/lantiq,pinctrl-xway.txt
index e89b4677567d..8e5216bcd748 100644
--- a/Documentation/devicetree/bindings/pinctrl/lantiq,pinctrl-xway.txt
+++ b/Documentation/devicetree/bindings/pinctrl/lantiq,pinctrl-xway.txt
@@ -1,7 +1,16 @@
Lantiq XWAY pinmux controller
Required properties:
-- compatible: "lantiq,pinctrl-xway" or "lantiq,pinctrl-xr9"
+- compatible: "lantiq,pinctrl-xway", (DEPRECATED: Use "lantiq,pinctrl-danube")
+ "lantiq,pinctrl-xr9", (DEPRECATED: Use "lantiq,xrx100-pinctrl" or
+ "lantiq,xrx200-pinctrl")
+ "lantiq,pinctrl-ase", (DEPRECATED: Use "lantiq,ase-pinctrl")
+ "lantiq,<chip>-pinctrl", where <chip> is:
+ "ase" (XWAY AMAZON Family)
+ "danube" (XWAY DANUBE Family)
+ "xrx100" (XWAY xRX100 Family)
+ "xrx200" (XWAY xRX200 Family)
+ "xrx300" (XWAY xRX300 Family)
- reg: Should contain the physical address and length of the gpio/pinmux
register range
@@ -36,19 +45,87 @@ Required subnode-properties:
Valid values for group and function names:
+XWAY: (DEPRECATED: Use DANUBE)
mux groups:
exin0, exin1, exin2, jtag, ebu a23, ebu a24, ebu a25, ebu clk, ebu cs1,
ebu wait, nand ale, nand cs1, nand cle, spi, spi_cs1, spi_cs2, spi_cs3,
- spi_cs4, spi_cs5, spi_cs6, asc0, asc0 cts rts, stp, nmi , gpt1, gpt2,
+ spi_cs4, spi_cs5, spi_cs6, asc0, asc0 cts rts, stp, nmi, gpt1, gpt2,
gpt3, clkout0, clkout1, clkout2, clkout3, gnt1, gnt2, gnt3, req1, req2,
req3
- additional mux groups (XR9 only):
- mdio, nand rdy, nand rd, exin3, exin4, gnt4, req4
+ functions:
+ spi, asc, cgu, jtag, exin, stp, gpt, nmi, pci, ebu
+
+XR9: ( DEPRECATED: Use xRX100/xRX200)
+ mux groups:
+ exin0, exin1, exin2, exin3, exin4, jtag, ebu a23, ebu a24, ebu a25,
+ ebu clk, ebu cs1, ebu wait, nand ale, nand cs1, nand cle, nand rdy,
+ nand rd, spi, spi_cs1, spi_cs2, spi_cs3, spi_cs4, spi_cs5, spi_cs6,
+ asc0, asc0 cts rts, stp, nmi, gpt1, gpt2, gpt3, clkout0, clkout1,
+ clkout2, clkout3, gnt1, gnt2, gnt3, gnt4, req1, req2, req3, req4, mdio,
+ gphy0 led0, gphy0 led1, gphy0 led2, gphy1 led0, gphy1 led1, gphy1 led2
+
+ functions:
+ spi, asc, cgu, jtag, exin, stp, gpt, nmi, pci, ebu, mdio, gphy
+
+AMAZON:
+ mux groups:
+ exin0, exin1, exin2, jtag, spi_di, spi_do, spi_clk, spi_cs1, spi_cs2,
+ spi_cs3, spi_cs4, spi_cs5, spi_cs6, asc, stp, gpt1, gpt2, gpt3, clkout0,
+ clkout1, clkout2, mdio, dfe led0, dfe led1, ephy led0, ephy led1, ephy led2
+
+ functions:
+ spi, asc, cgu, jtag, exin, stp, gpt, mdio, ephy, dfe
+
+DANUBE:
+ mux groups:
+ exin0, exin1, exin2, jtag, ebu a23, ebu a24, ebu a25, ebu clk, ebu cs1,
+ ebu wait, nand ale, nand cs1, nand cle, spi_di, spi_do, spi_clk, spi_cs1,
+ spi_cs2, spi_cs3, spi_cs4, spi_cs5, spi_cs6, asc0, asc0 cts rts, stp, nmi,
+ gpt1, gpt2, gpt3, clkout0, clkout1, clkout2, clkout3, gnt1, gnt2, gnt3,
+ req1, req2, req3, dfe led0, dfe led1
functions:
- spi, asc, cgu, jtag, exin, stp, gpt, nmi, pci, ebu, mdio
+ spi, asc, cgu, jtag, exin, stp, gpt, nmi, pci, ebu, dfe
+xRX100:
+ mux groups:
+ exin0, exin1, exin2, exin3, exin4, ebu a23, ebu a24, ebu a25, ebu clk,
+ ebu cs1, ebu wait, nand ale, nand cs1, nand cle, nand rdy, nand rd,
+ spi_di, spi_do, spi_clk, spi_cs1, spi_cs2, spi_cs3, spi_cs4, spi_cs5,
+ spi_cs6, asc0, asc0 cts rts, stp, nmi, gpt1, gpt2, gpt3, clkout0, clkout1,
+ clkout2, clkout3, gnt1, gnt2, gnt3, gnt4, req1, req2, req3, req4, mdio,
+ dfe led0, dfe led1
+
+ functions:
+ spi, asc, cgu, exin, stp, gpt, nmi, pci, ebu, mdio, dfe
+
+xRX200:
+ mux groups:
+ exin0, exin1, exin2, exin3, exin4, ebu a23, ebu a24, ebu a25, ebu clk,
+ ebu cs1, ebu wait, nand ale, nand cs1, nand cle, nand rdy, nand rd,
+ spi_di, spi_do, spi_clk, spi_cs1, spi_cs2, spi_cs3, spi_cs4, spi_cs5,
+ spi_cs6, usif uart_rx, usif uart_tx, usif uart_rts, usif uart_cts,
+ usif uart_dtr, usif uart_dsr, usif uart_dcd, usif uart_ri, usif spi_di,
+ usif spi_do, usif spi_clk, usif spi_cs0, usif spi_cs1, usif spi_cs2,
+ stp, nmi, gpt1, gpt2, gpt3, clkout0, clkout1, clkout2, clkout3, gnt1,
+ gnt2, gnt3, gnt4, req1, req2, req3, req4, mdio, dfe led0, dfe led1,
+ gphy0 led0, gphy0 led1, gphy0 led2, gphy1 led0, gphy1 led1, gphy1 led2
+
+ functions:
+ spi, usif, cgu, exin, stp, gpt, nmi, pci, ebu, mdio, dfe, gphy
+
+xRX300:
+ mux groups:
+ exin0, exin1, exin2, exin4, nand ale, nand cs0, nand cs1, nand cle,
+ nand rdy, nand rd, nand_d0, nand_d1, nand_d2, nand_d3, nand_d4, nand_d5,
+ nand_d6, nand_d7, nand_d1, nand wr, nand wp, nand se, spi_di, spi_do,
+ spi_clk, spi_cs1, spi_cs4, spi_cs6, usif uart_rx, usif uart_tx,
+ usif spi_di, usif spi_do, usif spi_clk, usif spi_cs0, stp, clkout2,
+ mdio, dfe led0, dfe led1, ephy0 led0, ephy0 led1, ephy1 led0, ephy1 led1
+
+ functions:
+ spi, usif, cgu, exin, stp, ebu, mdio, dfe, ephy
Definition of pin configurations:
@@ -62,15 +139,32 @@ Optional subnode-properties:
0: none, 1: down, 2: up.
- lantiq,open-drain: Boolean, enables open-drain on the defined pin.
-Valid values for XWAY pin names:
+Valid values for XWAY pin names: (DEPRECATED: Use DANUBE)
Pinconf pins can be referenced via the names io0-io31.
-Valid values for XR9 pin names:
+Valid values for XR9 pin names: (DEPRECATED: Use xrX100/xRX200)
Pinconf pins can be referenced via the names io0-io55.
+Valid values for AMAZON pin names:
+ Pinconf pins can be referenced via the names io0-io31.
+
+Valid values for DANUBE pin names:
+ Pinconf pins can be referenced via the names io0-io31.
+
+Valid values for xRX100 pin names:
+ Pinconf pins can be referenced via the names io0-io55.
+
+Valid values for xRX200 pin names:
+ Pinconf pins can be referenced via the names io0-io49.
+
+Valid values for xRX300 pin names:
+ Pinconf pins can be referenced via the names io0-io1,io3-io6,io8-io11,
+ io13-io19,io23-io27,io34-io36,
+ io42-io43,io48-io61.
+
Example:
gpio: pinmux@E100B10 {
- compatible = "lantiq,pinctrl-xway";
+ compatible = "lantiq,danube-pinctrl";
pinctrl-names = "default";
pinctrl-0 = <&state_default>;
diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-mt65xx.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-mt65xx.txt
index 0480bc31bfd7..9ffb0b276bb4 100644
--- a/Documentation/devicetree/bindings/pinctrl/pinctrl-mt65xx.txt
+++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-mt65xx.txt
@@ -4,10 +4,11 @@ The Mediatek's Pin controller is used to control SoC pins.
Required properties:
- compatible: value should be one of the following.
- (a) "mediatek,mt8135-pinctrl", compatible with mt8135 pinctrl.
- (b) "mediatek,mt8173-pinctrl", compatible with mt8173 pinctrl.
- (c) "mediatek,mt6397-pinctrl", compatible with mt6397 pinctrl.
- (d) "mediatek,mt8127-pinctrl", compatible with mt8127 pinctrl.
+ "mediatek,mt2701-pinctrl", compatible with mt2701 pinctrl.
+ "mediatek,mt6397-pinctrl", compatible with mt6397 pinctrl.
+ "mediatek,mt8127-pinctrl", compatible with mt8127 pinctrl.
+ "mediatek,mt8135-pinctrl", compatible with mt8135 pinctrl.
+ "mediatek,mt8173-pinctrl", compatible with mt8173 pinctrl.
- pins-are-numbered: Specify the subnodes are using numbered pinmux to
specify pins.
- gpio-controller : Marks the device node as a gpio controller.
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,msm8996-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/qcom,msm8996-pinctrl.txt
new file mode 100644
index 000000000000..e312a71b2f94
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,msm8996-pinctrl.txt
@@ -0,0 +1,199 @@
+Qualcomm MSM8996 TLMM block
+
+This binding describes the Top Level Mode Multiplexer block found in the
+MSM8996 platform.
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be "qcom,msm8996-pinctrl"
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: the base address and size of the TLMM register space.
+
+- interrupts:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: should specify the TLMM summary IRQ.
+
+- interrupt-controller:
+ Usage: required
+ Value type: <none>
+ Definition: identifies this node as an interrupt controller
+
+- #interrupt-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: must be 2. Specifying the pin number and flags, as defined
+ in <dt-bindings/interrupt-controller/irq.h>
+
+- gpio-controller:
+ Usage: required
+ Value type: <none>
+ Definition: identifies this node as a gpio controller
+
+- #gpio-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: must be 2. Specifying the pin number and flags, as defined
+ in <dt-bindings/gpio/gpio.h>
+
+Please refer to ../gpio/gpio.txt and ../interrupt-controller/interrupts.txt for
+a general description of GPIO and interrupt bindings.
+
+Please refer to pinctrl-bindings.txt in this directory for details of the
+common pinctrl bindings used by client devices, including the meaning of the
+phrase "pin configuration node".
+
+The pin configuration nodes act as a container for an arbitrary number of
+subnodes. Each of these subnodes represents some desired configuration for a
+pin, a group, or a list of pins or groups. This configuration can include the
+mux function to select on those pin(s)/group(s), and various pin configuration
+parameters, such as pull-up, drive strength, etc.
+
+
+PIN CONFIGURATION NODES:
+
+The name of each subnode is not important; all subnodes should be enumerated
+and processed purely based on their content.
+
+Each subnode only affects those parameters that are explicitly listed. In
+other words, a subnode that lists a mux function but no pin configuration
+parameters implies no information about any pin configuration parameters.
+Similarly, a pin subnode that describes a pullup parameter implies no
+information about e.g. the mux function.
+
+
+The following generic properties as defined in pinctrl-bindings.txt are valid
+to specify in a pin configuration subnode:
+
+- pins:
+ Usage: required
+ Value type: <string-array>
+ Definition: List of gpio pins affected by the properties specified in
+ this subnode.
+
+ Valid pins are:
+ gpio0-gpio149
+ Supports mux, bias and drive-strength
+
+ sdc1_clk, sdc1_cmd, sdc1_data sdc2_clk, sdc2_cmd,
+ sdc2_data sdc1_rclk
+ Supports bias and drive-strength
+
+- function:
+ Usage: required
+ Value type: <string>
+ Definition: Specify the alternative function to be configured for the
+ specified pins. Functions are only valid for gpio pins.
+ Valid values are:
+
+ blsp_uart1, blsp_spi1, blsp_i2c1, blsp_uim1, atest_tsens,
+ bimc_dte1, dac_calib0, blsp_spi8, blsp_uart8, blsp_uim8,
+ qdss_cti_trig_out_b, bimc_dte0, dac_calib1, qdss_cti_trig_in_b,
+ dac_calib2, atest_tsens2, atest_usb1, blsp_spi10, blsp_uart10,
+ blsp_uim10, atest_bbrx1, atest_usb13, atest_bbrx0, atest_usb12,
+ mdp_vsync, edp_lcd, blsp_i2c10, atest_gpsadc1, atest_usb11,
+ atest_gpsadc0, edp_hot, atest_usb10, m_voc, dac_gpio, atest_char,
+ cam_mclk, pll_bypassnl, qdss_stm7, blsp_i2c8, qdss_tracedata_b,
+ pll_reset, qdss_stm6, qdss_stm5, qdss_stm4, atest_usb2, cci_i2c,
+ qdss_stm3, dac_calib3, atest_usb23, atest_char3, dac_calib4,
+ qdss_stm2, atest_usb22, atest_char2, qdss_stm1, dac_calib5,
+ atest_usb21, atest_char1, dbg_out, qdss_stm0, dac_calib6,
+ atest_usb20, atest_char0, dac_calib10, qdss_stm10,
+ qdss_cti_trig_in_a, cci_timer4, blsp_spi6, blsp_uart6, blsp_uim6,
+ blsp2_spi, qdss_stm9, qdss_cti_trig_out_a, dac_calib11,
+ qdss_stm8, cci_timer0, qdss_stm13, dac_calib7, cci_timer1,
+ qdss_stm12, dac_calib8, cci_timer2, blsp1_spi, qdss_stm11,
+ dac_calib9, cci_timer3, cci_async, dac_calib12, blsp_i2c6,
+ qdss_tracectl_a, dac_calib13, qdss_traceclk_a, dac_calib14,
+ dac_calib15, hdmi_rcv, dac_calib16, hdmi_cec, pwr_modem,
+ dac_calib17, hdmi_ddc, pwr_nav, dac_calib18, pwr_crypto,
+ dac_calib19, hdmi_hot, dac_calib20, dac_calib21, pci_e0,
+ dac_calib22, dac_calib23, dac_calib24, tsif1_sync, dac_calib25,
+ sd_write, tsif1_error, blsp_spi2, blsp_uart2, blsp_uim2,
+ qdss_cti, blsp_i2c2, blsp_spi3, blsp_uart3, blsp_uim3, blsp_i2c3,
+ uim3, blsp_spi9, blsp_uart9, blsp_uim9, blsp10_spi, blsp_i2c9,
+ blsp_spi7, blsp_uart7, blsp_uim7, qdss_tracedata_a, blsp_i2c7,
+ qua_mi2s, gcc_gp1_clk_a, ssc_irq, uim4, blsp_spi11, blsp_uart11,
+ blsp_uim11, gcc_gp2_clk_a, gcc_gp3_clk_a, blsp_i2c11, cri_trng0,
+ cri_trng1, cri_trng, qdss_stm18, pri_mi2s, qdss_stm17, blsp_spi4,
+ blsp_uart4, blsp_uim4, qdss_stm16, qdss_stm15, blsp_i2c4,
+ qdss_stm14, dac_calib26, spkr_i2s, audio_ref, lpass_slimbus,
+ isense_dbg, tsense_pwm1, tsense_pwm2, btfm_slimbus, ter_mi2s,
+ qdss_stm22, qdss_stm21, qdss_stm20, qdss_stm19, gcc_gp1_clk_b,
+ sec_mi2s, blsp_spi5, blsp_uart5, blsp_uim5, gcc_gp2_clk_b,
+ gcc_gp3_clk_b, blsp_i2c5, blsp_spi12, blsp_uart12, blsp_uim12,
+ qdss_stm25, qdss_stm31, blsp_i2c12, qdss_stm30, qdss_stm29,
+ tsif1_clk, qdss_stm28, tsif1_en, tsif1_data, sdc4_cmd, qdss_stm27,
+ qdss_traceclk_b, tsif2_error, sdc43, vfr_1, qdss_stm26, tsif2_clk,
+ sdc4_clk, qdss_stm24, tsif2_en, sdc42, qdss_stm23, qdss_tracectl_b,
+ sd_card, tsif2_data, sdc41, tsif2_sync, sdc40, mdp_vsync_p_b,
+ ldo_en, mdp_vsync_s_b, ldo_update, blsp11_uart_tx_b, blsp11_uart_rx_b,
+ blsp11_i2c_sda_b, prng_rosc, blsp11_i2c_scl_b, uim2, uim1, uim_batt,
+ pci_e2, pa_indicator, adsp_ext, ddr_bist, qdss_tracedata_11,
+ qdss_tracedata_12, modem_tsync, nav_dr, nav_pps, pci_e1, gsm_tx,
+ qspi_cs, ssbi2, ssbi1, mss_lte, qspi_clk, qspi0, qspi1, qspi2, qspi3,
+ gpio
+
+- bias-disable:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins should be configued as no pull.
+
+- bias-pull-down:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins should be configued as pull down.
+
+- bias-pull-up:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins should be configued as pull up.
+
+- output-high:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins are configured in output mode, driven
+ high.
+ Not valid for sdc pins.
+
+- output-low:
+ Usage: optional
+ Value type: <none>
+ Definition: The specified pins are configured in output mode, driven
+ low.
+ Not valid for sdc pins.
+
+- drive-strength:
+ Usage: optional
+ Value type: <u32>
+ Definition: Selects the drive strength for the specified pins, in mA.
+ Valid values are: 2, 4, 6, 8, 10, 12, 14 and 16
+
+Example:
+
+ tlmm: pinctrl@01010000 {
+ compatible = "qcom,msm8996-pinctrl";
+ reg = <0x01010000 0x300000>;
+ interrupts = <0 208 0>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ uart_console_active: uart_console_active {
+ mux {
+ pins = "gpio4", "gpio5";
+ function = "blsp_uart8";
+ };
+
+ config {
+ pins = "gpio4", "gpio5";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
index 1ae63c0acd40..a90c812ad642 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt
@@ -14,6 +14,7 @@ PMIC's from Qualcomm.
"qcom,pm8917-gpio"
"qcom,pm8921-gpio"
"qcom,pm8941-gpio"
+ "qcom,pm8994-gpio"
"qcom,pma8084-gpio"
- reg:
@@ -79,6 +80,7 @@ to specify in a pin configuration subnode:
gpio1-gpio38 for pm8917
gpio1-gpio44 for pm8921
gpio1-gpio36 for pm8941
+ gpio1-gpio22 for pm8994
gpio1-gpio22 for pma8084
- function:
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt
index d7803a2a94e9..d74e631e10da 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt
@@ -15,6 +15,7 @@ of PMIC's from Qualcomm.
"qcom,pm8917-mpp",
"qcom,pm8921-mpp",
"qcom,pm8941-mpp",
+ "qcom,pm8994-mpp",
"qcom,pma8084-mpp",
- reg:
diff --git a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
index 391ef4be8d50..0cd701b1947f 100644
--- a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt
@@ -21,7 +21,8 @@ defined as gpio sub-nodes of the pinmux controller.
Required properties for iomux controller:
- compatible: one of "rockchip,rk2928-pinctrl", "rockchip,rk3066a-pinctrl"
"rockchip,rk3066b-pinctrl", "rockchip,rk3188-pinctrl"
- "rockchip,rk3288-pinctrl", "rockchip,rk3368-pinctrl"
+ "rockchip,rk3228-pinctrl", "rockchip,rk3288-pinctrl"
+ "rockchip,rk3368-pinctrl"
- rockchip,grf: phandle referencing a syscon providing the
"general register files"
diff --git a/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt
index 9d2a995293e6..6db16b90873a 100644
--- a/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt
@@ -17,6 +17,7 @@ Required Properties:
- "samsung,exynos4x12-pinctrl": for Exynos4x12 compatible pin-controller.
- "samsung,exynos5250-pinctrl": for Exynos5250 compatible pin-controller.
- "samsung,exynos5260-pinctrl": for Exynos5260 compatible pin-controller.
+ - "samsung,exynos5410-pinctrl": for Exynos5410 compatible pin-controller.
- "samsung,exynos5420-pinctrl": for Exynos5420 compatible pin-controller.
- "samsung,exynos7-pinctrl": for Exynos7 compatible pin-controller.
diff --git a/Documentation/devicetree/bindings/regulator/lm363x-regulator.txt b/Documentation/devicetree/bindings/regulator/lm363x-regulator.txt
new file mode 100644
index 000000000000..8f14df9d1205
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/lm363x-regulator.txt
@@ -0,0 +1,34 @@
+TI LMU LM363x regulator device tree bindings
+
+LM363x regulator driver supports LM3631 and LM3632.
+LM3631 has five regulators and LM3632 supports three regulators.
+
+Required property:
+ - compatible: "ti,lm363x-regulator"
+
+Optional properties:
+ LM3632 has external enable pins for two LDOs.
+ - ti,lcm-en1-gpio: A GPIO specifier for Vpos control pin.
+ - ti,lcm-en2-gpio: A GPIO specifier for Vneg control pin.
+
+Child nodes:
+ LM3631
+ - vboost
+ - vcont
+ - voref
+ - vpos
+ - vneg
+
+ LM3632
+ - vboost
+ - vpos
+ - vneg
+
+ Optional properties of a child node:
+ Each sub-node should contain the constraints and initialization.
+ Please refer to [1].
+
+Examples: Please refer to ti-lmu dt-bindings [2].
+
+[1] ../regulator/regulator.txt
+[2] ../mfd/ti-lmu.txt
diff --git a/Documentation/devicetree/bindings/regulator/pv88060.txt b/Documentation/devicetree/bindings/regulator/pv88060.txt
new file mode 100644
index 000000000000..10a6dadc008e
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/pv88060.txt
@@ -0,0 +1,124 @@
+* Powerventure Semiconductor PV88060 Voltage Regulator
+
+Required properties:
+- compatible: "pvs,pv88060".
+- reg: I2C slave address, usually 0x49.
+- interrupts: the interrupt outputs of the controller
+- regulators: A node that houses a sub-node for each regulator within the
+ device. Each sub-node is identified using the node's name, with valid
+ values listed below. The content of each sub-node is defined by the
+ standard binding for regulators; see regulator.txt.
+ BUCK1, LDO1, LDO2, LDO3, LDO4, LDO5, LDO6, LDO7, SW1, SW2, SW3, SW4,
+ SW5, and SW6.
+
+Optional properties:
+- Any optional property defined in regulator.txt
+
+Example
+
+ pmic: pv88060@49 {
+ compatible = "pvs,pv88060";
+ reg = <0x49>;
+ interrupt-parent = <&gpio>;
+ interrupts = <24 24>;
+
+ regulators {
+ BUCK1 {
+ regulator-name = "buck1";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <4387500>;
+ regulator-min-microamp = <1496000>;
+ regulator-max-microamp = <4189000>;
+ regulator-boot-on;
+ };
+
+ LDO1 {
+ regulator-name = "ldo1";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <3350000>;
+ regulator-boot-on;
+ };
+
+ LDO2 {
+ regulator-name = "ldo2";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <3350000>;
+ regulator-boot-on;
+ };
+
+ LDO3 {
+ regulator-name = "ldo3";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <3350000>;
+ regulator-boot-on;
+ };
+
+ LDO4 {
+ regulator-name = "ldo4";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <3350000>;
+ regulator-boot-on;
+ };
+
+ LDO5 {
+ regulator-name = "ldo5";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <3350000>;
+ regulator-boot-on;
+ };
+
+ LDO6 {
+ regulator-name = "ldo6";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <3350000>;
+ regulator-boot-on;
+ };
+
+ LDO7 {
+ regulator-name = "ldo7";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <3350000>;
+ regulator-boot-on;
+ };
+
+ SW1 {
+ regulator-name = "sw1";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ };
+
+ SW2 {
+ regulator-name = "sw2";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-boot-on;
+ };
+
+ SW3 {
+ regulator-name = "sw3";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-boot-on;
+ };
+
+ SW4 {
+ regulator-name = "sw4";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-boot-on;
+ };
+
+ SW5 {
+ regulator-name = "sw5";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-boot-on;
+ };
+
+ SW6 {
+ regulator-name = "sw6";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ };
+ };
+ }; \ No newline at end of file
diff --git a/Documentation/devicetree/bindings/regulator/pv88090.txt b/Documentation/devicetree/bindings/regulator/pv88090.txt
new file mode 100644
index 000000000000..e52b2a95cdde
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/pv88090.txt
@@ -0,0 +1,65 @@
+* Powerventure Semiconductor PV88090 Voltage Regulator
+
+Required properties:
+- compatible: "pvs,pv88090".
+- reg: I2C slave address, usually 0x48.
+- interrupts: the interrupt outputs of the controller
+- regulators: A node that houses a sub-node for each regulator within the
+ device. Each sub-node is identified using the node's name, with valid
+ values listed below. The content of each sub-node is defined by the
+ standard binding for regulators; see regulator.txt.
+ BUCK1, BUCK2, BUCK3, LDO1, and LDO2.
+
+Optional properties:
+- Any optional property defined in regulator.txt
+
+Example
+
+ pmic: pv88090@48 {
+ compatible = "pvs,pv88090";
+ reg = <0x48>;
+ interrupt-parent = <&gpio>;
+ interrupts = <24 24>;
+
+ regulators {
+ BUCK1 {
+ regulator-name = "buck1";
+ regulator-min-microvolt = < 600000>;
+ regulator-max-microvolt = <1393750>;
+ regulator-min-microamp = < 220000>;
+ regulator-max-microamp = <7040000>;
+ regulator-boot-on;
+ };
+
+ BUCK2 {
+ regulator-name = "buck2";
+ regulator-min-microvolt = < 600000>;
+ regulator-max-microvolt = <1393750>;
+ regulator-min-microamp = <1496000>;
+ regulator-max-microamp = <4189000>;
+ };
+
+ BUCK3 {
+ regulator-name = "buck3";
+ regulator-min-microvolt = <600000>;
+ regulator-max-microvolt = <1393750>;
+ regulator-min-microamp = <1496000>;
+ regulator-max-microamp = <4189000>;
+ regulator-boot-on;
+ };
+
+ LDO1 {
+ regulator-name = "ldo1";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <4350000>;
+ regulator-boot-on;
+ };
+
+ LDO2 {
+ regulator-name = "ldo2";
+ regulator-min-microvolt = < 650000>;
+ regulator-max-microvolt = <2225000>;
+ regulator-boot-on;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/soc/qcom,smd-rpm.txt b/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.txt
index e27f5c4c54fd..1f8d6f84b657 100644
--- a/Documentation/devicetree/bindings/soc/qcom,smd-rpm.txt
+++ b/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.txt
@@ -1,27 +1,17 @@
-Qualcomm Resource Power Manager (RPM) over SMD
+QCOM SMD RPM REGULATOR
-This driver is used to interface with the Resource Power Manager (RPM) found in
-various Qualcomm platforms. The RPM allows each component in the system to vote
-for state of the system resources, such as clocks, regulators and bus
-frequencies.
+The Qualcomm RPM over SMD regulator is modelled as a subdevice of the RPM.
+Because SMD is used as the communication transport mechanism, the RPM resides as
+a subnode of the SMD. As such, the SMD-RPM regulator requires that the SMD and
+RPM nodes be present.
-- compatible:
- Usage: required
- Value type: <string>
- Definition: must be one of:
- "qcom,rpm-msm8974"
+Please refer to Documentation/devicetree/bindings/soc/qcom/qcom,smd.txt for
+information pertaining to the SMD node.
-- qcom,smd-channels:
- Usage: required
- Value type: <stringlist>
- Definition: Shared Memory channel used for communication with the RPM
+Please refer to Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.txt for
+information regarding the RPM node.
-= SUBDEVICES
-
-The RPM exposes resources to its subnodes. The below bindings specify the set
-of valid subnodes that can operate on these resources.
-
-== Regulators
+== Regulator
Regulator nodes are identified by their compatible:
@@ -30,7 +20,9 @@ Regulator nodes are identified by their compatible:
Value type: <string>
Definition: must be one of:
"qcom,rpm-pm8841-regulators"
+ "qcom,rpm-pm8916-regulators"
"qcom,rpm-pm8941-regulators"
+ "qcom,rpm-pma8084-regulators"
- vdd_s1-supply:
- vdd_s2-supply:
@@ -48,6 +40,19 @@ Regulator nodes are identified by their compatible:
- vdd_s1-supply:
- vdd_s2-supply:
- vdd_s3-supply:
+- vdd_s4-supply:
+- vdd_l1_l2_l3-supply:
+- vdd_l4_l5_l6-supply:
+- vdd_l7-supply:
+- vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_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_lvs1_2_3-supply:
- vdd_l4_l11-supply:
@@ -63,6 +68,35 @@ Regulator nodes are identified by their compatible:
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_s5-supply:
+- vdd_s6-supply:
+- vdd_s7-supply:
+- vdd_s8-supply:
+- vdd_s9-supply:
+- vdd_s10-supply:
+- vdd_s11-supply:
+- vdd_s12-supply:
+- vdd_l1_l11-supply:
+- vdd_l2_l3_l4_l27-supply:
+- vdd_l5_l7-supply:
+- vdd_l6_l12_l14_l15_l26-supply:
+- vdd_l8-supply:
+- vdd_l9_l10_l13_l20_l23_l24-supply:
+- vdd_l16_l25-supply:
+- vdd_l17-supply:
+- vdd_l18-supply:
+- vdd_l19-supply:
+- vdd_l21-supply:
+- vdd_l22-supply:
+ Usage: optional (pma8084 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.
@@ -70,11 +104,20 @@ 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, s4, 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, 5vs1, 5vs2
+pma8084:
+ s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, l1, l2, l3, l4, l5,
+ l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20,
+ l21, l22, l23, l24, l25, l26, l27, lvs1, lvs2, lvs3, lvs4, 5vs1
+
The content of each sub-node is defined by the standard binding for regulators -
see regulator.txt.
@@ -114,4 +157,3 @@ see regulator.txt.
};
};
};
-
diff --git a/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt b/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt
deleted file mode 100644
index 20191315e444..000000000000
--- a/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt
+++ /dev/null
@@ -1,163 +0,0 @@
-* Samsung S5M8767 Voltage and Current Regulator
-
-The Samsung S5M8767 is a multi-function device which includes voltage and
-current regulators, rtc, charger controller and other sub-blocks. It is
-interfaced to the host controller using a i2c interface. Each sub-block is
-addressed by the host system using different i2c slave address. This document
-describes the bindings for 'pmic' sub-block of s5m8767.
-
-Required properties:
-- compatible: Should be "samsung,s5m8767-pmic".
-- reg: Specifies the i2c slave address of the pmic block. It should be 0x66.
-
-- s5m8767,pmic-buck2-dvs-voltage: A set of 8 voltage values in micro-volt (uV)
- units for buck2 when changing voltage using gpio dvs. Refer to [1] below
- for additional information.
-
-- s5m8767,pmic-buck3-dvs-voltage: A set of 8 voltage values in micro-volt (uV)
- units for buck3 when changing voltage using gpio dvs. Refer to [1] below
- for additional information.
-
-- s5m8767,pmic-buck4-dvs-voltage: A set of 8 voltage values in micro-volt (uV)
- units for buck4 when changing voltage using gpio dvs. Refer to [1] below
- for additional information.
-
-- s5m8767,pmic-buck-ds-gpios: GPIO specifiers for three host gpio's used
- for selecting GPIO DVS lines. It is one-to-one mapped to dvs gpio lines.
-
-[1] If none of the 's5m8767,pmic-buck[2/3/4]-uses-gpio-dvs' optional
- property is specified, the 's5m8767,pmic-buck[2/3/4]-dvs-voltage'
- property should specify atleast one voltage level (which would be a
- safe operating voltage).
-
- If either of the 's5m8767,pmic-buck[2/3/4]-uses-gpio-dvs' optional
- property is specified, then all the eight voltage values for the
- 's5m8767,pmic-buck[2/3/4]-dvs-voltage' should be specified.
-
-Optional properties:
-- interrupt-parent: Specifies the phandle of the interrupt controller to which
- the interrupts from s5m8767 are delivered to.
-- interrupts: Interrupt specifiers for two interrupt sources.
- - First interrupt specifier is for 'irq1' interrupt.
- - Second interrupt specifier is for 'alert' interrupt.
-- s5m8767,pmic-buck2-uses-gpio-dvs: 'buck2' can be controlled by gpio dvs.
-- s5m8767,pmic-buck3-uses-gpio-dvs: 'buck3' can be controlled by gpio dvs.
-- s5m8767,pmic-buck4-uses-gpio-dvs: 'buck4' can be controlled by gpio dvs.
-
-Additional properties required if either of the optional properties are used:
-
-- s5m8767,pmic-buck234-default-dvs-idx: Default voltage setting selected from
- the possible 8 options selectable by the dvs gpios. The value of this
- property should be between 0 and 7. If not specified or if out of range, the
- default value of this property is set to 0.
-
-- s5m8767,pmic-buck-dvs-gpios: GPIO specifiers for three host gpio's used
- for dvs. The format of the gpio specifier depends in the gpio controller.
-
-Regulators: The regulators of s5m8767 that have to be instantiated should be
-included in a sub-node named 'regulators'. Regulator nodes included in this
-sub-node should be of the format as listed below.
-
- regulator_name {
- ldo1_reg: LDO1 {
- regulator-name = "VDD_ALIVE_1.0V";
- regulator-min-microvolt = <1100000>;
- regulator-max-microvolt = <1100000>;
- regulator-always-on;
- regulator-boot-on;
- op_mode = <1>; /* Normal Mode */
- };
- };
-The above regulator entries are defined in regulator bindings documentation
-except these properties:
- - op_mode: describes the different operating modes of the LDO's with
- power mode change in SOC. The different possible values are,
- 0 - always off mode
- 1 - on in normal mode
- 2 - low power mode
- 3 - suspend mode
- - s5m8767,pmic-ext-control-gpios: (optional) GPIO specifier for one
- GPIO controlling this regulator (enable/disable); This is
- valid only for buck9.
-
-The following are the names of the regulators that the s5m8767 pmic block
-supports. Note: The 'n' in LDOn and BUCKn represents the LDO or BUCK number
-as per the datasheet of s5m8767.
-
- - LDOn
- - valid values for n are 1 to 28
- - Example: LDO1, LDO2, LDO28
- - BUCKn
- - valid values for n are 1 to 9.
- - Example: BUCK1, BUCK2, BUCK9
-
-The bindings inside the regulator nodes use the standard regulator bindings
-which are documented elsewhere.
-
-Example:
-
- s5m8767_pmic@66 {
- compatible = "samsung,s5m8767-pmic";
- reg = <0x66>;
-
- s5m8767,pmic-buck2-uses-gpio-dvs;
- s5m8767,pmic-buck3-uses-gpio-dvs;
- s5m8767,pmic-buck4-uses-gpio-dvs;
-
- s5m8767,pmic-buck-default-dvs-idx = <0>;
-
- s5m8767,pmic-buck-dvs-gpios = <&gpx0 0 0>, /* DVS1 */
- <&gpx0 1 0>, /* DVS2 */
- <&gpx0 2 0>; /* DVS3 */
-
- s5m8767,pmic-buck-ds-gpios = <&gpx2 3 0>, /* SET1 */
- <&gpx2 4 0>, /* SET2 */
- <&gpx2 5 0>; /* SET3 */
-
- s5m8767,pmic-buck2-dvs-voltage = <1350000>, <1300000>,
- <1250000>, <1200000>,
- <1150000>, <1100000>,
- <1000000>, <950000>;
-
- s5m8767,pmic-buck3-dvs-voltage = <1100000>, <1100000>,
- <1100000>, <1100000>,
- <1000000>, <1000000>,
- <1000000>, <1000000>;
-
- s5m8767,pmic-buck4-dvs-voltage = <1200000>, <1200000>,
- <1200000>, <1200000>,
- <1200000>, <1200000>,
- <1200000>, <1200000>;
-
- regulators {
- ldo1_reg: LDO1 {
- regulator-name = "VDD_ABB_3.3V";
- regulator-min-microvolt = <3300000>;
- regulator-max-microvolt = <3300000>;
- op_mode = <1>; /* Normal Mode */
- };
-
- ldo2_reg: LDO2 {
- regulator-name = "VDD_ALIVE_1.1V";
- regulator-min-microvolt = <1100000>;
- regulator-max-microvolt = <1100000>;
- regulator-always-on;
- };
-
- buck1_reg: BUCK1 {
- regulator-name = "VDD_MIF_1.2V";
- regulator-min-microvolt = <950000>;
- regulator-max-microvolt = <1350000>;
- regulator-always-on;
- regulator-boot-on;
- };
-
- vemmc_reg: BUCK9 {
- regulator-name = "VMEM_VDD_2.8V";
- regulator-min-microvolt = <2800000>;
- regulator-max-microvolt = <2800000>;
- op_mode = <3>; /* Standby Mode */
- s5m8767,pmic-ext-control-gpios = <&gpk0 2 0>;
- };
- };
- };
diff --git a/Documentation/devicetree/bindings/regulator/samsung,s2mpa01.txt b/Documentation/devicetree/bindings/regulator/samsung,s2mpa01.txt
new file mode 100644
index 000000000000..bae3c7f838cf
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/samsung,s2mpa01.txt
@@ -0,0 +1,79 @@
+Binding for Samsung S2MPA01 regulator block
+===========================================
+
+This is a part of device tree bindings for S2M family multi-function devices.
+More information can be found in bindings/mfd/sec-core.txt file.
+
+The S2MPA01 device provide buck and LDO regulators.
+
+To register these with regulator framework instantiate under main device node
+a sub-node named "regulators" with more sub-nodes for each regulator using the
+common regulator binding documented in:
+ - Documentation/devicetree/bindings/regulator/regulator.txt
+
+
+Names of regulators supported by S2MPA01 device:
+ - LDOn
+ - valid values for n are 1 to 26
+ - Example: LDO1, LD02, LDO26
+ - BUCKn
+ - valid values for n are 1 to 10.
+ - Example: BUCK1, BUCK2, BUCK9
+Note: The 'n' in LDOn and BUCKn represents the LDO or BUCK number
+as per the datasheet of device.
+
+
+Optional properties of buck regulator nodes under "regulators" sub-node:
+ - regulator-ramp-delay: ramp delay in uV/us. May be 6250, 12500
+ (default), 25000, or 50000. May be 0 for disabling the ramp delay on
+ BUCK{1,2,3,4}.
+
+ In the absence of the regulator-ramp-delay property, the default ramp
+ delay will be used.
+
+ Note: Some bucks share the ramp rate setting i.e. same ramp value
+ will be set for a particular group of bucks so provide the same
+ regulator-ramp-delay value for them.
+ Groups sharing ramp rate:
+ - buck{1,6},
+ - buck{2,4},
+ - buck{8,9,10}.
+
+Example:
+
+ s2mpa01_pmic@66 {
+ compatible = "samsung,s2mpa01-pmic";
+ reg = <0x66>;
+
+ regulators {
+ ldo1_reg: LDO1 {
+ regulator-name = "VDD_ALIVE";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ };
+
+ ldo2_reg: LDO2 {
+ regulator-name = "VDDQ_MMC2";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ regulator-always-on;
+ };
+
+ buck1_reg: BUCK1 {
+ regulator-name = "vdd_mif";
+ regulator-min-microvolt = <950000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ buck2_reg: BUCK2 {
+ regulator-name = "vdd_arm";
+ regulator-min-microvolt = <950000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-ramp-delay = <50000>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/regulator/samsung,s2mps11.txt b/Documentation/devicetree/bindings/regulator/samsung,s2mps11.txt
new file mode 100644
index 000000000000..27a48bf1b185
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/samsung,s2mps11.txt
@@ -0,0 +1,102 @@
+Binding for Samsung S2M family regulator block
+==============================================
+
+This is a part of device tree bindings for S2M family multi-function devices.
+More information can be found in bindings/mfd/sec-core.txt file.
+
+The S2MPS11/13/14/15 and S2MPU02 devices provide buck and LDO regulators.
+
+To register these with regulator framework instantiate under main device node
+a sub-node named "regulators" with more sub-nodes for each regulator using the
+common regulator binding documented in:
+ - Documentation/devicetree/bindings/regulator/regulator.txt
+
+
+Names of regulators supported by different devices:
+ - LDOn
+ - valid values for n are:
+ - S2MPS11: 1 to 38
+ - S2MPS13: 1 to 40
+ - S2MPS14: 1 to 25
+ - S2MPS15: 1 to 27
+ - S2MPU02: 1 to 28
+ - Example: LDO1, LDO2, LDO28
+ - BUCKn
+ - valid values for n are:
+ - S2MPS11: 1 to 10
+ - S2MPS13: 1 to 10
+ - S2MPS14: 1 to 5
+ - S2MPS15: 1 to 10
+ - S2MPU02: 1 to 7
+ - Example: BUCK1, BUCK2, BUCK9
+Note: The 'n' in LDOn and BUCKn represents the LDO or BUCK number
+as per the datasheet of device.
+
+
+Optional properties of the nodes under "regulators" sub-node:
+ - regulator-ramp-delay: ramp delay in uV/us. May be 6250, 12500,
+ 25000 (default) or 50000.
+
+ Additionally S2MPS11 supports disabling ramp delay for BUCK{2,3,4,6}
+ by setting it to <0>.
+
+ Note: On S2MPS11 some bucks share the ramp rate setting i.e. same ramp value
+ will be set for a particular group of bucks so provide the same
+ regulator-ramp-delay value for them.
+ Groups sharing ramp rate:
+ - buck{1,6},
+ - buck{3,4},
+ - buck{7,8,10}.
+
+ - samsung,ext-control-gpios: On S2MPS14 the LDO10, LDO11 and LDO12 can be
+ configured to external control over GPIO. To turn this feature on this
+ property must be added to the regulator sub-node:
+ - samsung,ext-control-gpios: GPIO specifier for one GPIO
+ controlling this regulator (enable/disable)
+ Example:
+ LDO12 {
+ regulator-name = "V_EMMC_2.8V";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ samsung,ext-control-gpios = <&gpk0 2 0>;
+ };
+
+
+Example:
+
+ s2mps11_pmic@66 {
+ compatible = "samsung,s2mps11-pmic";
+ reg = <0x66>;
+
+ regulators {
+ ldo1_reg: LDO1 {
+ regulator-name = "VDD_ABB_3.3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ ldo2_reg: LDO2 {
+ regulator-name = "VDD_ALIVE_1.1V";
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ regulator-always-on;
+ };
+
+ buck1_reg: BUCK1 {
+ regulator-name = "vdd_mif";
+ regulator-min-microvolt = <950000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ buck2_reg: BUCK2 {
+ regulator-name = "vdd_arm";
+ regulator-min-microvolt = <950000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-ramp-delay = <50000>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/regulator/samsung,s5m8767.txt b/Documentation/devicetree/bindings/regulator/samsung,s5m8767.txt
new file mode 100644
index 000000000000..093edda0c8df
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/samsung,s5m8767.txt
@@ -0,0 +1,145 @@
+Binding for Samsung S5M8767 regulator block
+===========================================
+
+This is a part of device tree bindings for S5M family multi-function devices.
+More information can be found in bindings/mfd/sec-core.txt file.
+
+The S5M8767 device provide buck and LDO regulators.
+
+To register these with regulator framework instantiate under main device node
+a sub-node named "regulators" with more sub-nodes for each regulator using the
+common regulator binding documented in:
+ - Documentation/devicetree/bindings/regulator/regulator.txt
+
+
+Required properties of the main device node (the parent!):
+ - s5m8767,pmic-buck2-dvs-voltage: A set of 8 voltage values in micro-volt (uV)
+ units for buck2 when changing voltage using gpio dvs. Refer to [1] below
+ for additional information.
+
+ - s5m8767,pmic-buck3-dvs-voltage: A set of 8 voltage values in micro-volt (uV)
+ units for buck3 when changing voltage using gpio dvs. Refer to [1] below
+ for additional information.
+
+ - s5m8767,pmic-buck4-dvs-voltage: A set of 8 voltage values in micro-volt (uV)
+ units for buck4 when changing voltage using gpio dvs. Refer to [1] below
+ for additional information.
+
+ - s5m8767,pmic-buck-ds-gpios: GPIO specifiers for three host gpio's used
+ for selecting GPIO DVS lines. It is one-to-one mapped to dvs gpio lines.
+
+ [1] If none of the 's5m8767,pmic-buck[2/3/4]-uses-gpio-dvs' optional
+ property is specified, the 's5m8767,pmic-buck[2/3/4]-dvs-voltage'
+ property should specify atleast one voltage level (which would be a
+ safe operating voltage).
+
+ If either of the 's5m8767,pmic-buck[2/3/4]-uses-gpio-dvs' optional
+ property is specified, then all the eight voltage values for the
+ 's5m8767,pmic-buck[2/3/4]-dvs-voltage' should be specified.
+
+Optional properties of the main device node (the parent!):
+ - s5m8767,pmic-buck2-uses-gpio-dvs: 'buck2' can be controlled by gpio dvs.
+ - s5m8767,pmic-buck3-uses-gpio-dvs: 'buck3' can be controlled by gpio dvs.
+ - s5m8767,pmic-buck4-uses-gpio-dvs: 'buck4' can be controlled by gpio dvs.
+
+Additional properties required if either of the optional properties are used:
+
+ - s5m8767,pmic-buck234-default-dvs-idx: Default voltage setting selected from
+ the possible 8 options selectable by the dvs gpios. The value of this
+ property should be between 0 and 7. If not specified or if out of range, the
+ default value of this property is set to 0.
+
+ - s5m8767,pmic-buck-dvs-gpios: GPIO specifiers for three host gpio's used
+ for dvs. The format of the gpio specifier depends in the gpio controller.
+
+
+Names of regulators supported by S5M8767 device:
+ - LDOn
+ - valid values for n are 1 to 28
+ - Example: LDO1, LDO2, LDO28
+ - BUCKn
+ - valid values for n are 1 to 9.
+ - Example: BUCK1, BUCK2, BUCK9
+Note: The 'n' in LDOn and BUCKn represents the LDO or BUCK number
+as per the datasheet of device.
+
+
+Optional properties of the nodes under "regulators" sub-node:
+ - op_mode: describes the different operating modes of the LDO's with
+ power mode change in SOC. The different possible values are,
+ 0 - always off mode
+ 1 - on in normal mode
+ 2 - low power mode
+ 3 - suspend mode
+ - s5m8767,pmic-ext-control-gpios: (optional) GPIO specifier for one
+ GPIO controlling this regulator
+ (enable/disable); This is valid only
+ for buck9.
+
+Example:
+
+ s5m8767_pmic@66 {
+ compatible = "samsung,s5m8767-pmic";
+ reg = <0x66>;
+
+ s5m8767,pmic-buck2-uses-gpio-dvs;
+ s5m8767,pmic-buck3-uses-gpio-dvs;
+ s5m8767,pmic-buck4-uses-gpio-dvs;
+
+ s5m8767,pmic-buck-default-dvs-idx = <0>;
+
+ s5m8767,pmic-buck-dvs-gpios = <&gpx0 0 0>, /* DVS1 */
+ <&gpx0 1 0>, /* DVS2 */
+ <&gpx0 2 0>; /* DVS3 */
+
+ s5m8767,pmic-buck-ds-gpios = <&gpx2 3 0>, /* SET1 */
+ <&gpx2 4 0>, /* SET2 */
+ <&gpx2 5 0>; /* SET3 */
+
+ s5m8767,pmic-buck2-dvs-voltage = <1350000>, <1300000>,
+ <1250000>, <1200000>,
+ <1150000>, <1100000>,
+ <1000000>, <950000>;
+
+ s5m8767,pmic-buck3-dvs-voltage = <1100000>, <1100000>,
+ <1100000>, <1100000>,
+ <1000000>, <1000000>,
+ <1000000>, <1000000>;
+
+ s5m8767,pmic-buck4-dvs-voltage = <1200000>, <1200000>,
+ <1200000>, <1200000>,
+ <1200000>, <1200000>,
+ <1200000>, <1200000>;
+
+ regulators {
+ ldo1_reg: LDO1 {
+ regulator-name = "VDD_ABB_3.3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ op_mode = <1>; /* Normal Mode */
+ };
+
+ ldo2_reg: LDO2 {
+ regulator-name = "VDD_ALIVE_1.1V";
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ regulator-always-on;
+ };
+
+ buck1_reg: BUCK1 {
+ regulator-name = "VDD_MIF_1.2V";
+ regulator-min-microvolt = <950000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ vemmc_reg: BUCK9 {
+ regulator-name = "VMEM_VDD_2.8V";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ op_mode = <3>; /* Standby Mode */
+ s5m8767,pmic-ext-control-gpios = <&gpk0 2 0>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/scsi/hisilicon-sas.txt b/Documentation/devicetree/bindings/scsi/hisilicon-sas.txt
new file mode 100644
index 000000000000..f67e761bcc18
--- /dev/null
+++ b/Documentation/devicetree/bindings/scsi/hisilicon-sas.txt
@@ -0,0 +1,69 @@
+* HiSilicon SAS controller
+
+The HiSilicon SAS controller supports SAS/SATA.
+
+Main node required properties:
+ - compatible : value should be as follows:
+ (a) "hisilicon,hip05-sas-v1" for v1 hw in hip05 chipset
+ - sas-addr : array of 8 bytes for host SAS address
+ - reg : Address and length of the SAS register
+ - hisilicon,sas-syscon: phandle of syscon used for sas control
+ - ctrl-reset-reg : offset to controller reset register in ctrl reg
+ - ctrl-reset-sts-reg : offset to controller reset status register in ctrl reg
+ - ctrl-clock-ena-reg : offset to controller clock enable register in ctrl reg
+ - queue-count : number of delivery and completion queues in the controller
+ - phy-count : number of phys accessible by the controller
+ - interrupts : Interrupts for phys, completion queues, and fatal
+ sources; the interrupts are ordered in 3 groups, as follows:
+ - Phy interrupts
+ - Completion queue interrupts
+ - Fatal interrupts
+ Phy interrupts : Each phy has 3 interrupt sources:
+ - broadcast
+ - phyup
+ - abnormal
+ The phy interrupts are ordered into groups of 3 per phy
+ (broadcast, phyup, and abnormal) in increasing order.
+ Completion queue interrupts : each completion queue has 1
+ interrupt source.
+ The interrupts are ordered in increasing order.
+ Fatal interrupts : the fatal interrupts are ordered as follows:
+ - ECC
+ - AXI bus
+
+Example:
+ sas0: sas@c1000000 {
+ compatible = "hisilicon,hip05-sas-v1";
+ sas-addr = [50 01 88 20 16 00 00 0a];
+ reg = <0x0 0xc1000000 0x0 0x10000>;
+ hisilicon,sas-syscon = <&pcie_sas>;
+ ctrl-reset-reg = <0xa60>;
+ ctrl-reset-sts-reg = <0x5a30>;
+ ctrl-clock-ena-reg = <0x338>;
+ queue-count = <32>;
+ phy-count = <8>;
+ dma-coherent;
+ interrupt-parent = <&mbigen_dsa>;
+ interrupts = <259 4>,<263 4>,<264 4>,/* phy0 */
+ <269 4>,<273 4>,<274 4>,/* phy1 */
+ <279 4>,<283 4>,<284 4>,/* phy2 */
+ <289 4>,<293 4>,<294 4>,/* phy3 */
+ <299 4>,<303 4>,<304 4>,/* phy4 */
+ <309 4>,<313 4>,<314 4>,/* phy5 */
+ <319 4>,<323 4>,<324 4>,/* phy6 */
+ <329 4>,<333 4>,<334 4>,/* phy7 */
+ <336 1>,<337 1>,<338 1>,/* cq0-2 */
+ <339 1>,<340 1>,<341 1>,/* cq3-5 */
+ <342 1>,<343 1>,<344 1>,/* cq6-8 */
+ <345 1>,<346 1>,<347 1>,/* cq9-11 */
+ <348 1>,<349 1>,<350 1>,/* cq12-14 */
+ <351 1>,<352 1>,<353 1>,/* cq15-17 */
+ <354 1>,<355 1>,<356 1>,/* cq18-20 */
+ <357 1>,<358 1>,<359 1>,/* cq21-23 */
+ <360 1>,<361 1>,<362 1>,/* cq24-26 */
+ <363 1>,<364 1>,<365 1>,/* cq27-29 */
+ <366 1>,<367 1>/* cq30-31 */
+ <376 4>,/* fatal ecc */
+ <381 4>;/* fatal axi */
+ status = "disabled";
+ };
diff --git a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt
index 73f825e5e644..401b1b33c2c4 100644
--- a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt
+++ b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt
@@ -2,7 +2,7 @@
Required properties:
- - compatible: Must contain one of the following:
+ - compatible: Must contain one or more of the following:
- "renesas,scif-r7s72100" for R7S72100 (RZ/A1H) SCIF compatible UART.
- "renesas,scifa-r8a73a4" for R8A73A4 (R-Mobile APE6) SCIFA compatible UART.
@@ -15,10 +15,14 @@ Required properties:
- "renesas,scifa-r8a7790" for R8A7790 (R-Car H2) SCIFA compatible UART.
- "renesas,scifb-r8a7790" for R8A7790 (R-Car H2) SCIFB compatible UART.
- "renesas,hscif-r8a7790" for R8A7790 (R-Car H2) HSCIF compatible UART.
- - "renesas,scif-r8a7791" for R8A7791 (R-Car M2) SCIF compatible UART.
- - "renesas,scifa-r8a7791" for R8A7791 (R-Car M2) SCIFA compatible UART.
- - "renesas,scifb-r8a7791" for R8A7791 (R-Car M2) SCIFB compatible UART.
- - "renesas,hscif-r8a7791" for R8A7791 (R-Car M2) HSCIF compatible UART.
+ - "renesas,scif-r8a7791" for R8A7791 (R-Car M2-W) SCIF compatible UART.
+ - "renesas,scifa-r8a7791" for R8A7791 (R-Car M2-W) SCIFA compatible UART.
+ - "renesas,scifb-r8a7791" for R8A7791 (R-Car M2-W) SCIFB compatible UART.
+ - "renesas,hscif-r8a7791" for R8A7791 (R-Car M2-W) HSCIF compatible UART.
+ - "renesas,scif-r8a7793" for R8A7793 (R-Car M2-N) SCIF compatible UART.
+ - "renesas,scifa-r8a7793" for R8A7793 (R-Car M2-N) SCIFA compatible UART.
+ - "renesas,scifb-r8a7793" for R8A7793 (R-Car M2-N) SCIFB compatible UART.
+ - "renesas,hscif-r8a7793" for R8A7793 (R-Car M2-N) HSCIF compatible UART.
- "renesas,scif-r8a7794" for R8A7794 (R-Car E2) SCIF compatible UART.
- "renesas,scifa-r8a7794" for R8A7794 (R-Car E2) SCIFA compatible UART.
- "renesas,scifb-r8a7794" for R8A7794 (R-Car E2) SCIFB compatible UART.
@@ -27,6 +31,14 @@ Required properties:
- "renesas,hscif-r8a7795" for R8A7795 (R-Car H3) HSCIF compatible UART.
- "renesas,scifa-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFA compatible UART.
- "renesas,scifb-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFB compatible UART.
+ - "renesas,rcar-gen1-scif" for R-Car Gen1 SCIF compatible UART,
+ - "renesas,rcar-gen2-scif" for R-Car Gen2 SCIF compatible UART,
+ - "renesas,rcar-gen3-scif" for R-Car Gen3 SCIF compatible UART,
+ - "renesas,rcar-gen2-scifa" for R-Car Gen2 SCIFA compatible UART,
+ - "renesas,rcar-gen2-scifb" for R-Car Gen2 SCIFB compatible UART,
+ - "renesas,rcar-gen1-hscif" for R-Car Gen1 HSCIF compatible UART,
+ - "renesas,rcar-gen2-hscif" for R-Car Gen2 HSCIF compatible UART,
+ - "renesas,rcar-gen3-hscif" for R-Car Gen3 HSCIF compatible UART,
- "renesas,scif" for generic SCIF compatible UART.
- "renesas,scifa" for generic SCIFA compatible UART.
- "renesas,scifb" for generic SCIFB compatible UART.
@@ -34,15 +46,26 @@ Required properties:
- "renesas,sci" for generic SCI compatible UART.
When compatible with the generic version, nodes must list the
- SoC-specific version corresponding to the platform first followed by the
- generic version.
+ SoC-specific version corresponding to the platform first, followed by the
+ family-specific and/or generic versions.
- reg: Base address and length of the I/O registers used by the UART.
- interrupts: Must contain an interrupt-specifier for the SCIx interrupt.
- clocks: Must contain a phandle and clock-specifier pair for each entry
in clock-names.
- - clock-names: Must contain "sci_ick" for the SCIx UART interface clock.
+ - clock-names: Must contain "fck" for the SCIx UART functional clock.
+ Apart from the divided functional clock, there may be other possible
+ sources for the sampling clock, depending on SCIx variant.
+ On (H)SCI(F) and some SCIFA, an additional clock may be specified:
+ - "hsck" for the optional external clock input (on HSCIF),
+ - "sck" for the optional external clock input (on other variants).
+ On UARTs equipped with a Baud Rate Generator for External Clock (BRG)
+ (some SCIF and HSCIF), additional clocks may be specified:
+ - "brg_int" for the optional internal clock source for the frequency
+ divider (typically the (AXI or SHwy) bus clock),
+ - "scif_clk" for the optional external clock source for the frequency
+ divider (SCIF_CLK).
Note: Each enabled SCIx UART should have an alias correctly numbered in the
"aliases" node.
@@ -58,12 +81,13 @@ Example:
};
scifa0: serial@e6c40000 {
- compatible = "renesas,scifa-r8a7790", "renesas,scifa";
+ compatible = "renesas,scifa-r8a7790",
+ "renesas,rcar-gen2-scifa", "renesas,scifa";
reg = <0 0xe6c40000 0 64>;
interrupt-parent = <&gic>;
interrupts = <0 144 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp2_clks R8A7790_CLK_SCIFA0>;
- clock-names = "sci_ick";
+ clock-names = "fck";
dmas = <&dmac0 0x21>, <&dmac0 0x22>;
dma-names = "tx", "rx";
};
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.txt
new file mode 100644
index 000000000000..a48049ccf6d0
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.txt
@@ -0,0 +1,58 @@
+Qualcomm Resource Power Manager (RPM) over SMD
+
+This driver is used to interface with the Resource Power Manager (RPM) found in
+various Qualcomm platforms. The RPM allows each component in the system to vote
+for state of the system resources, such as clocks, regulators and bus
+frequencies.
+
+The SMD information for the RPM edge should be filled out. See qcom,smd.txt for
+the required edge properties. All SMD related properties will reside within the
+RPM node itself.
+
+= SUBDEVICES
+
+The RPM exposes resources to its subnodes. The rpm_requests node must be
+present and this subnode may contain children that designate regulator
+resources.
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be one of:
+ "qcom,rpm-apq8084"
+ "qcom,rpm-msm8916"
+ "qcom,rpm-msm8974"
+
+- qcom,smd-channels:
+ Usage: required
+ Value type: <string>
+ Definition: must be "rpm_requests"
+
+Refer to Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.txt
+for information on the regulator subnodes that can exist under the rpm_requests.
+
+Example:
+
+ soc {
+ apcs: syscon@f9011000 {
+ compatible = "syscon";
+ reg = <0xf9011000 0x1000>;
+ };
+ };
+
+ smd {
+ compatible = "qcom,smd";
+
+ rpm {
+ interrupts = <0 168 1>;
+ qcom,ipc = <&apcs 8 0>;
+ qcom,smd-edge = <15>;
+
+ rpm_requests {
+ compatible = "qcom,rpm-msm8974";
+ qcom,smd-channels = "rpm_requests";
+
+ ...
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/atmel-classd.txt b/Documentation/devicetree/bindings/sound/atmel-classd.txt
index 0018451c4351..549e701cb7a1 100644
--- a/Documentation/devicetree/bindings/sound/atmel-classd.txt
+++ b/Documentation/devicetree/bindings/sound/atmel-classd.txt
@@ -16,6 +16,10 @@ Required properties:
Required elements: "pclk", "gclk" and "aclk".
- clocks
Please refer to clock-bindings.txt.
+- assigned-clocks
+ Should be <&classd_gclk>.
+- assigned-clock-parents
+ Should be <&audio_pll_pmc>.
Optional properties:
- pinctrl-names, pinctrl-0
@@ -43,6 +47,8 @@ classd: classd@fc048000 {
dma-names = "tx";
clocks = <&classd_clk>, <&classd_gclk>, <&audio_pll_pmc>;
clock-names = "pclk", "gclk", "aclk";
+ assigned-clocks = <&classd_gclk>;
+ assigned-clock-parents = <&audio_pll_pmc>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_classd_default>;
diff --git a/Documentation/devicetree/bindings/sound/wm8994.txt b/Documentation/devicetree/bindings/sound/wm8994.txt
index e045e90a0924..68c4e8d96bed 100644
--- a/Documentation/devicetree/bindings/sound/wm8994.txt
+++ b/Documentation/devicetree/bindings/sound/wm8994.txt
@@ -30,7 +30,7 @@ Optional properties:
- #interrupt-cells: the number of cells to describe an IRQ, this should be 2.
The first cell is the IRQ number.
The second cell is the flags, encoded as the trigger masks from
- Documentation/devicetree/bindings/interrupts.txt
+ Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
- clocks : A list of up to two phandle and clock specifier pairs
- clock-names : A list of clock names sorted in the same order as clocks.
diff --git a/Documentation/devicetree/bindings/spi/sh-msiof.txt b/Documentation/devicetree/bindings/spi/sh-msiof.txt
index 705075da2f10..aa005c1d10d9 100644
--- a/Documentation/devicetree/bindings/spi/sh-msiof.txt
+++ b/Documentation/devicetree/bindings/spi/sh-msiof.txt
@@ -10,6 +10,7 @@ Required properties:
"renesas,msiof-r8a7792" (R-Car V2H)
"renesas,msiof-r8a7793" (R-Car M2-N)
"renesas,msiof-r8a7794" (R-Car E2)
+ "renesas,msiof-sh73a0" (SH-Mobile AG5)
- reg : A list of offsets and lengths of the register sets for
the device.
If only one register set is present, it is to be used
diff --git a/Documentation/devicetree/bindings/spi/spi-mt65xx.txt b/Documentation/devicetree/bindings/spi/spi-mt65xx.txt
index ce363c923f44..e43f4cf4cf35 100644
--- a/Documentation/devicetree/bindings/spi/spi-mt65xx.txt
+++ b/Documentation/devicetree/bindings/spi/spi-mt65xx.txt
@@ -2,9 +2,10 @@ Binding for MTK SPI controller
Required properties:
- compatible: should be one of the following.
- - mediatek,mt8173-spi: for mt8173 platforms
- - mediatek,mt8135-spi: for mt8135 platforms
+ - mediatek,mt2701-spi: for mt2701 platforms
- mediatek,mt6589-spi: for mt6589 platforms
+ - mediatek,mt8135-spi: for mt8135 platforms
+ - mediatek,mt8173-spi: for mt8173 platforms
- #address-cells: should be 1.
@@ -29,10 +30,10 @@ Required properties:
muxes clock, and "spi-clk" for the clock gate.
Optional properties:
--cs-gpios: see spi-bus.txt, only required for MT8173.
+-cs-gpios: see spi-bus.txt.
- mediatek,pad-select: specify which pins group(ck/mi/mo/cs) spi
- controller used. This is a array, the element value should be 0~3,
+ controller used. This is an array, the element value should be 0~3,
only required for MT8173.
0: specify GPIO69,70,71,72 for spi pins.
1: specify GPIO102,103,104,105 for spi pins.
diff --git a/Documentation/devicetree/bindings/arm/rockchip/pmu-sram.txt b/Documentation/devicetree/bindings/sram/rockchip-pmu-sram.txt
index 6b42fda306ff..6b42fda306ff 100644
--- a/Documentation/devicetree/bindings/arm/rockchip/pmu-sram.txt
+++ b/Documentation/devicetree/bindings/sram/rockchip-pmu-sram.txt
diff --git a/Documentation/devicetree/bindings/arm/rockchip/smp-sram.txt b/Documentation/devicetree/bindings/sram/rockchip-smp-sram.txt
index d9416fb8db6f..800701ecffca 100644
--- a/Documentation/devicetree/bindings/arm/rockchip/smp-sram.txt
+++ b/Documentation/devicetree/bindings/sram/rockchip-smp-sram.txt
@@ -12,7 +12,7 @@ Required sub-node properties:
- compatible : should be "rockchip,rk3066-smp-sram"
The rest of the properties should follow the generic mmio-sram discription
-found in ../../misc/sram.txt
+found in Documentation/devicetree/bindings/sram/sram.txt
Example:
diff --git a/Documentation/devicetree/bindings/arm/exynos/smp-sysram.txt b/Documentation/devicetree/bindings/sram/samsung-sram.txt
index 4a0a4f70a0ce..6bc474b2b885 100644
--- a/Documentation/devicetree/bindings/arm/exynos/smp-sysram.txt
+++ b/Documentation/devicetree/bindings/sram/samsung-sram.txt
@@ -15,7 +15,7 @@ Required sub-node properties:
"samsung,exynos4210-sysram-ns" : for Non-secure SYSRAM
The rest of the properties should follow the generic mmio-sram discription
-found in ../../misc/sysram.txt
+found in Documentation/devicetree/bindings/sram/sram.txt
Example:
diff --git a/Documentation/devicetree/bindings/misc/sram.txt b/Documentation/devicetree/bindings/sram/sram.txt
index 42ee9438b771..42ee9438b771 100644
--- a/Documentation/devicetree/bindings/misc/sram.txt
+++ b/Documentation/devicetree/bindings/sram/sram.txt
diff --git a/Documentation/devicetree/bindings/soc/sunxi/sram.txt b/Documentation/devicetree/bindings/sram/sunxi-sram.txt
index 067698112f5f..8d5665468fe7 100644
--- a/Documentation/devicetree/bindings/soc/sunxi/sram.txt
+++ b/Documentation/devicetree/bindings/sram/sunxi-sram.txt
@@ -16,7 +16,7 @@ SRAM nodes
----------
Each SRAM is described using the mmio-sram bindings documented in
-Documentation/devicetree/bindings/misc/sram.txt
+Documentation/devicetree/bindings/sram/sram.txt
Each SRAM will have SRAM sections that are going to be handled by the
SRAM controller as subnodes. These sections are represented following
diff --git a/Documentation/devicetree/bindings/staging/ion/hi6220-ion.txt b/Documentation/devicetree/bindings/staging/ion/hi6220-ion.txt
new file mode 100644
index 000000000000..c59e27c632c1
--- /dev/null
+++ b/Documentation/devicetree/bindings/staging/ion/hi6220-ion.txt
@@ -0,0 +1,31 @@
+Hi6220 SoC ION
+===================================================================
+Required properties:
+- compatible : "hisilicon,hi6220-ion"
+- list of the ION heaps
+ - heap name : maybe heap_sys_user@0
+ - heap id : id should be unique in the system.
+ - heap base : base ddr address of the heap,0 means that
+ it is dynamic.
+ - heap size : memory size and 0 means it is dynamic.
+ - heap type : the heap type of the heap, please also
+ see the define in ion.h(drivers/staging/android/uapi/ion.h)
+-------------------------------------------------------------------
+Example:
+ hi6220-ion {
+ compatible = "hisilicon,hi6220-ion";
+ heap_sys_user@0 {
+ heap-name = "sys_user";
+ heap-id = <0x0>;
+ heap-base = <0x0>;
+ heap-size = <0x0>;
+ heap-type = "ion_system";
+ };
+ heap_sys_contig@0 {
+ heap-name = "sys_contig";
+ heap-id = <0x1>;
+ heap-base = <0x0>;
+ heap-size = <0x0>;
+ heap-type = "ion_system_contig";
+ };
+ };
diff --git a/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt b/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt
index b38200d2583a..0dfa60d88dd3 100644
--- a/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt
+++ b/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt
@@ -1,7 +1,9 @@
* Temperature Sensor ADC (TSADC) on rockchip SoCs
Required properties:
-- compatible : "rockchip,rk3288-tsadc"
+- compatible : should be "rockchip,<name>-tsadc"
+ "rockchip,rk3288-tsadc": found on RK3288 SoCs
+ "rockchip,rk3368-tsadc": found on RK3368 SoCs
- reg : physical base address of the controller and length of memory mapped
region.
- interrupts : The interrupt number to the cpu. The interrupt specifier format
diff --git a/Documentation/devicetree/bindings/usb/dwc2.txt b/Documentation/devicetree/bindings/usb/dwc2.txt
index fd132cbee70e..221368207ca4 100644
--- a/Documentation/devicetree/bindings/usb/dwc2.txt
+++ b/Documentation/devicetree/bindings/usb/dwc2.txt
@@ -4,6 +4,7 @@ Platform DesignWare HS OTG USB 2.0 controller
Required properties:
- compatible : One of:
- brcm,bcm2835-usb: The DWC2 USB controller instance in the BCM2835 SoC.
+ - hisilicon,hi6220-usb: The DWC2 USB controller instance in the hi6220 SoC.
- rockchip,rk3066-usb: The DWC2 USB controller instance in the rk3066 Soc;
- "rockchip,rk3188-usb", "rockchip,rk3066-usb", "snps,dwc2": for rk3188 Soc;
- "rockchip,rk3288-usb", "rockchip,rk3066-usb", "snps,dwc2": for rk3288 Soc;
diff --git a/Documentation/devicetree/bindings/usb/dwc3-xilinx.txt b/Documentation/devicetree/bindings/usb/dwc3-xilinx.txt
new file mode 100644
index 000000000000..30361b32a460
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/dwc3-xilinx.txt
@@ -0,0 +1,33 @@
+Xilinx SuperSpeed DWC3 USB SoC controller
+
+Required properties:
+- compatible: Should contain "xlnx,zynqmp-dwc3"
+- clocks: A list of phandles for the clocks listed in clock-names
+- clock-names: Should contain the following:
+ "bus_clk" Master/Core clock, have to be >= 125 MHz for SS
+ operation and >= 60MHz for HS operation
+
+ "ref_clk" Clock source to core during PHY power down
+
+Required child node:
+A child node must exist to represent the core DWC3 IP block. The name of
+the node is not important. The content of the node is defined in dwc3.txt.
+
+Example device node:
+
+ usb@0 {
+ #address-cells = <0x2>;
+ #size-cells = <0x1>;
+ status = "okay";
+ compatible = "xlnx,zynqmp-dwc3";
+ clock-names = "bus_clk" "ref_clk";
+ clocks = <&clk125>, <&clk125>;
+ ranges;
+
+ dwc3@fe200000 {
+ compatible = "snps,dwc3";
+ reg = <0x0 0xfe200000 0x40000>;
+ interrupts = <0x0 0x41 0x4>;
+ dr_mode = "host";
+ };
+ };
diff --git a/Documentation/devicetree/bindings/usb/mt8173-xhci.txt b/Documentation/devicetree/bindings/usb/mt8173-xhci.txt
new file mode 100644
index 000000000000..b3a7ffa48852
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/mt8173-xhci.txt
@@ -0,0 +1,51 @@
+MT8173 xHCI
+
+The device node for Mediatek SOC USB3.0 host controller
+
+Required properties:
+ - compatible : should contain "mediatek,mt8173-xhci"
+ - reg : specifies physical base address and size of the registers,
+ the first one for MAC, the second for IPPC
+ - interrupts : interrupt used by the controller
+ - power-domains : a phandle to USB power domain node to control USB's
+ mtcmos
+ - vusb33-supply : regulator of USB avdd3.3v
+
+ - clocks : a list of phandle + clock-specifier pairs, one for each
+ entry in clock-names
+ - clock-names : must contain
+ "sys_ck": for clock of xHCI MAC
+ "wakeup_deb_p0": for USB wakeup debounce clock of port0
+ "wakeup_deb_p1": for USB wakeup debounce clock of port1
+
+ - phys : a list of phandle + phy specifier pairs
+
+Optional properties:
+ - mediatek,wakeup-src : 1: ip sleep wakeup mode; 2: line state wakeup
+ mode;
+ - mediatek,syscon-wakeup : phandle to syscon used to access USB wakeup
+ control register, it depends on "mediatek,wakeup-src".
+ - vbus-supply : reference to the VBUS regulator;
+ - usb3-lpm-capable : supports USB3.0 LPM
+
+Example:
+usb30: usb@11270000 {
+ compatible = "mediatek,mt8173-xhci";
+ reg = <0 0x11270000 0 0x1000>,
+ <0 0x11280700 0 0x0100>;
+ interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_LOW>;
+ power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
+ clocks = <&topckgen CLK_TOP_USB30_SEL>,
+ <&pericfg CLK_PERI_USB0>,
+ <&pericfg CLK_PERI_USB1>;
+ clock-names = "sys_ck",
+ "wakeup_deb_p0",
+ "wakeup_deb_p1";
+ phys = <&phy_port0 PHY_TYPE_USB3>,
+ <&phy_port1 PHY_TYPE_USB2>;
+ vusb33-supply = <&mt6397_vusb_reg>;
+ vbus-supply = <&usb_p1_vbus>;
+ usb3-lpm-capable;
+ mediatek,syscon-wakeup = <&pericfg>;
+ mediatek,wakeup-src = <1>;
+};
diff --git a/Documentation/devicetree/bindings/usb/octeon-usb.txt b/Documentation/devicetree/bindings/usb/octeon-usb.txt
new file mode 100644
index 000000000000..205c8d24d6e3
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/octeon-usb.txt
@@ -0,0 +1,62 @@
+OCTEON/OCTEON+ USB BLOCK
+
+1) Main node
+
+ Required properties:
+
+ - compatible: must be "cavium,octeon-5750-usbn"
+
+ - reg: specifies the physical base address of the USBN block and
+ the length of the memory mapped region.
+
+ - #address-cells: specifies the number of cells needed to encode an
+ address. The value must be 2.
+
+ - #size-cells: specifies the number of cells used to represent the size
+ of an address. The value must be 2.
+
+ - ranges: specifies the translation between child address space and parent
+ address space.
+
+ - clock-frequency: speed of the USB reference clock. Allowed values are
+ 12000000, 24000000 or 48000000.
+
+ - cavium,refclk-type: type of the USB reference clock. Allowed values are
+ "crystal" or "external".
+
+ - refclk-frequency: deprecated, use "clock-frequency".
+
+ - refclk-type: deprecated, use "cavium,refclk-type".
+
+2) Child node
+
+ The main node must have one child node which describes the built-in
+ USB controller.
+
+ Required properties:
+
+ - compatible: must be "cavium,octeon-5750-usbc"
+
+ - reg: specifies the physical base address of the USBC block and
+ the length of the memory mapped region.
+
+ - interrupts: specifies the interrupt number for the USB controller.
+
+3) Example:
+
+ usbn: usbn@1180068000000 {
+ compatible = "cavium,octeon-5750-usbn";
+ reg = <0x11800 0x68000000 0x0 0x1000>;
+ ranges; /* Direct mapping */
+ #address-cells = <2>;
+ #size-cells = <2>;
+ clock-frequency = <12000000>;
+ cavium,refclk-type = "crystal";
+
+ usbc@16f0010000000 {
+ compatible = "cavium,octeon-5750-usbc";
+ reg = <0x16f00 0x10000000 0x0 0x80000>;
+ interrupts = <0 56>;
+ };
+ };
+
diff --git a/Documentation/devicetree/bindings/usb/renesas_usb3.txt b/Documentation/devicetree/bindings/usb/renesas_usb3.txt
new file mode 100644
index 000000000000..8d52766f07b9
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/renesas_usb3.txt
@@ -0,0 +1,23 @@
+Renesas Electronics USB3.0 Peripheral driver
+
+Required properties:
+ - compatible: Must contain one of the following:
+ - "renesas,r8a7795-usb3-peri"
+ - reg: Base address and length of the register for the USB3.0 Peripheral
+ - interrupts: Interrupt specifier for the USB3.0 Peripheral
+ - clocks: clock phandle and specifier pair
+
+Example:
+ usb3_peri0: usb@ee020000 {
+ compatible = "renesas,r8a7795-usb3-peri";
+ reg = <0 0xee020000 0 0x400>;
+ interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cpg CPG_MOD 328>;
+ };
+
+ usb3_peri1: usb@ee060000 {
+ compatible = "renesas,r8a7795-usb3-peri";
+ reg = <0 0xee060000 0 0x400>;
+ interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cpg CPG_MOD 327>;
+ };
diff --git a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt
index 7d48f63db44e..b6040563e51a 100644
--- a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt
+++ b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt
@@ -1,11 +1,21 @@
Renesas Electronics USBHS driver
Required properties:
- - compatible: Must contain one of the following:
- - "renesas,usbhs-r8a7790"
- - "renesas,usbhs-r8a7791"
- - "renesas,usbhs-r8a7794"
- - "renesas,usbhs-r8a7795"
+ - compatible: Must contain one or more of the following:
+
+ - "renesas,usbhs-r8a7790" for r8a7790 (R-Car H2) compatible device
+ - "renesas,usbhs-r8a7791" for r8a7791 (R-Car M2-W) compatible device
+ - "renesas,usbhs-r8a7792" for r8a7792 (R-Car V2H) compatible device
+ - "renesas,usbhs-r8a7793" for r8a7793 (R-Car M2-N) compatible device
+ - "renesas,usbhs-r8a7794" for r8a7794 (R-Car E2) compatible device
+ - "renesas,usbhs-r8a7795" for r8a7795 (R-Car H3) compatible device
+ - "renesas,rcar-gen2-usbhs" for R-Car Gen2 compatible device
+ - "renesas,rcar-gen3-usbhs" for R-Car Gen3 compatible device
+
+ When compatible with the generic version, nodes must list the
+ SoC-specific version corresponding to the platform first followed
+ by the generic version.
+
- reg: Base address and length of the register for the USBHS
- interrupts: Interrupt specifier for the USBHS
- clocks: A list of phandle + clock specifier pairs
@@ -22,7 +32,7 @@ Optional properties:
Example:
usbhs: usb@e6590000 {
- compatible = "renesas,usbhs-r8a7790";
+ compatible = "renesas,usbhs-r8a7790", "renesas,rcar-gen2-usbhs";
reg = <0 0xe6590000 0 0x100>;
interrupts = <0 107 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mstp7_clks R8A7790_CLK_HSUSB>;
diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt
index 86f67f0886bc..082573289f1e 100644
--- a/Documentation/devicetree/bindings/usb/usb-xhci.txt
+++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt
@@ -3,8 +3,8 @@ USB xHCI controllers
Required properties:
- compatible: should be one of "generic-xhci",
"marvell,armada-375-xhci", "marvell,armada-380-xhci",
- "renesas,xhci-r8a7790", "renesas,xhci-r8a7791" (deprecated:
- "xhci-platform").
+ "renesas,xhci-r8a7790", "renesas,xhci-r8a7791", "renesas,xhci-r8a7793",
+ "renesas,xhci-r8a7795" (deprecated: "xhci-platform").
- reg: should contain address and length of the standard XHCI
register set for the device.
- interrupts: one XHCI interrupt should be described here.
diff --git a/Documentation/devicetree/bindings/usb/usb3503.txt b/Documentation/devicetree/bindings/usb/usb3503.txt
index 52493b1480e2..c1a0a9191d26 100644
--- a/Documentation/devicetree/bindings/usb/usb3503.txt
+++ b/Documentation/devicetree/bindings/usb/usb3503.txt
@@ -18,7 +18,8 @@ Optional properties:
- refclk: Clock used for driving REFCLK signal (optional, if not provided
the driver assumes that clock signal is always available, its
rate is specified by REF_SEL pins and a value from the primary
- reference clock frequencies table is used)
+ reference clock frequencies table is used). Use clocks and
+ clock-names in order to assign it
- refclk-frequency: Frequency of the REFCLK signal as defined by REF_SEL
pins (optional, if not provided, driver will not set rate of the
REFCLK signal and assume that a value from the primary reference
@@ -33,4 +34,6 @@ Examples:
intn-gpios = <&gpx3 4 1>;
reset-gpios = <&gpx3 5 1>;
initial-mode = <1>;
+ clocks = <&clks 80>;
+ clock-names = "refclk";
};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 55df1d444e9f..a4f2035569ce 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -161,6 +161,7 @@ nuvoton Nuvoton Technology Corporation
nvidia NVIDIA
nxp NXP Semiconductors
okaya Okaya Electric America, Inc.
+olimex OLIMEX Ltd.
onnn ON Semiconductor Corp.
opencores OpenCores.org
option Option NV
diff --git a/Documentation/dmaengine/client.txt b/Documentation/dmaengine/client.txt
index 11fb87ff6cd0..9e33189745f0 100644
--- a/Documentation/dmaengine/client.txt
+++ b/Documentation/dmaengine/client.txt
@@ -22,25 +22,14 @@ The slave DMA usage consists of following steps:
Channel allocation is slightly different in the slave DMA context,
client drivers typically need a channel from a particular DMA
controller only and even in some cases a specific channel is desired.
- To request a channel dma_request_channel() API is used.
+ To request a channel dma_request_chan() API is used.
Interface:
- struct dma_chan *dma_request_channel(dma_cap_mask_t mask,
- dma_filter_fn filter_fn,
- void *filter_param);
- where dma_filter_fn is defined as:
- typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param);
+ struct dma_chan *dma_request_chan(struct device *dev, const char *name);
- The 'filter_fn' parameter is optional, but highly recommended for
- slave and cyclic channels as they typically need to obtain a specific
- DMA channel.
-
- When the optional 'filter_fn' parameter is NULL, dma_request_channel()
- simply returns the first channel that satisfies the capability mask.
-
- Otherwise, the 'filter_fn' routine will be called once for each free
- channel which has a capability in 'mask'. 'filter_fn' is expected to
- return 'true' when the desired DMA channel is found.
+ Which will find and return the 'name' DMA channel associated with the 'dev'
+ device. The association is done via DT, ACPI or board file based
+ dma_slave_map matching table.
A channel allocated via this interface is exclusive to the caller,
until dma_release_channel() is called.
@@ -128,7 +117,7 @@ The slave DMA usage consists of following steps:
transaction.
For cyclic DMA, a callback function may wish to terminate the
- DMA via dmaengine_terminate_all().
+ DMA via dmaengine_terminate_async().
Therefore, it is important that DMA engine drivers drop any
locks before calling the callback function which may cause a
@@ -166,12 +155,29 @@ The slave DMA usage consists of following steps:
Further APIs:
-1. int dmaengine_terminate_all(struct dma_chan *chan)
+1. int dmaengine_terminate_sync(struct dma_chan *chan)
+ int dmaengine_terminate_async(struct dma_chan *chan)
+ int dmaengine_terminate_all(struct dma_chan *chan) /* DEPRECATED */
This causes all activity for the DMA channel to be stopped, and may
discard data in the DMA FIFO which hasn't been fully transferred.
No callback functions will be called for any incomplete transfers.
+ Two variants of this function are available.
+
+ dmaengine_terminate_async() might not wait until the DMA has been fully
+ stopped or until any running complete callbacks have finished. But it is
+ possible to call dmaengine_terminate_async() from atomic context or from
+ within a complete callback. dmaengine_synchronize() must be called before it
+ is safe to free the memory accessed by the DMA transfer or free resources
+ accessed from within the complete callback.
+
+ dmaengine_terminate_sync() will wait for the transfer and any running
+ complete callbacks to finish before it returns. But the function must not be
+ called from atomic context or from within a complete callback.
+
+ dmaengine_terminate_all() is deprecated and should not be used in new code.
+
2. int dmaengine_pause(struct dma_chan *chan)
This pauses activity on the DMA channel without data loss.
@@ -197,3 +203,20 @@ Further APIs:
a running DMA channel. It is recommended that DMA engine users
pause or stop (via dmaengine_terminate_all()) the channel before
using this API.
+
+5. void dmaengine_synchronize(struct dma_chan *chan)
+
+ Synchronize the termination of the DMA channel to the current context.
+
+ This function should be used after dmaengine_terminate_async() to synchronize
+ the termination of the DMA channel to the current context. The function will
+ wait for the transfer and any running complete callbacks to finish before it
+ returns.
+
+ If dmaengine_terminate_async() is used to stop the DMA channel this function
+ must be called before it is safe to free memory accessed by previously
+ submitted descriptors or to free any resources accessed within the complete
+ callback of previously submitted descriptors.
+
+ The behavior of this function is undefined if dma_async_issue_pending() has
+ been called between dmaengine_terminate_async() and this function.
diff --git a/Documentation/dmaengine/provider.txt b/Documentation/dmaengine/provider.txt
index 67d4ce4df109..122b7f4876bb 100644
--- a/Documentation/dmaengine/provider.txt
+++ b/Documentation/dmaengine/provider.txt
@@ -327,8 +327,24 @@ supported.
* device_terminate_all
- Aborts all the pending and ongoing transfers on the channel
- - This command should operate synchronously on the channel,
- terminating right away all the channels
+ - For aborted transfers the complete callback should not be called
+ - Can be called from atomic context or from within a complete
+ callback of a descriptor. Must not sleep. Drivers must be able
+ to handle this correctly.
+ - Termination may be asynchronous. The driver does not have to
+ wait until the currently active transfer has completely stopped.
+ See device_synchronize.
+
+ * device_synchronize
+ - Must synchronize the termination of a channel to the current
+ context.
+ - Must make sure that memory for previously submitted
+ descriptors is no longer accessed by the DMA controller.
+ - Must make sure that all complete callbacks for previously
+ submitted descriptors have finished running and none are
+ scheduled to run.
+ - May sleep.
+
Misc notes (stuff that should be documented, but don't really know
where to put them)
diff --git a/Documentation/dvb/README.dvb-usb b/Documentation/dvb/README.dvb-usb
index 8eb92264ee04..669dc6ce4330 100644
--- a/Documentation/dvb/README.dvb-usb
+++ b/Documentation/dvb/README.dvb-usb
@@ -45,7 +45,7 @@ Supported devices
See the LinuxTV DVB Wiki at www.linuxtv.org for a complete list of
cards/drivers/firmwares:
-http://www.linuxtv.org/wiki/index.php/DVB_USB
+https://linuxtv.org/wiki/index.php/DVB_USB
0. History & News:
2005-06-30 - added support for WideView WT-220U (Thanks to Steve Chang)
@@ -121,7 +121,7 @@ working.
Have a look at the Wikipage for the DVB-USB-drivers to find out, which firmware
you need for your device:
-http://www.linuxtv.org/wiki/index.php/DVB_USB
+https://linuxtv.org/wiki/index.php/DVB_USB
1.2. Compiling
diff --git a/Documentation/dvb/faq.txt b/Documentation/dvb/faq.txt
index 97b1373f2428..a0be92012877 100644
--- a/Documentation/dvb/faq.txt
+++ b/Documentation/dvb/faq.txt
@@ -76,7 +76,7 @@ Some very frequently asked questions about linuxtv-dvb
the TuxBox CVS many interesting DVB applications and the dBox2
DVB source
- http://www.linuxtv.org/downloads/
+ https://linuxtv.org/downloads
DVB Swiss Army Knife library and utilities
http://www.nenie.org/misc/mpsys/
diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware
index 91b43d2738c7..1a0a04125f71 100755
--- a/Documentation/dvb/get_dvb_firmware
+++ b/Documentation/dvb/get_dvb_firmware
@@ -152,7 +152,7 @@ sub tda10046lifeview {
sub av7110 {
my $sourcefile = "dvb-ttpci-01.fw-261d";
- my $url = "http://www.linuxtv.org/downloads/firmware/$sourcefile";
+ my $url = "https://linuxtv.org/downloads/firmware/$sourcefile";
my $hash = "603431b6259715a8e88f376a53b64e2f";
my $outfile = "dvb-ttpci-01.fw";
@@ -303,7 +303,7 @@ sub vp7049 {
}
sub dibusb {
- my $url = "http://www.linuxtv.org/downloads/firmware/dvb-usb-dibusb-5.0.0.11.fw";
+ my $url = "https://linuxtv.org/downloads/firmware/dvb-usb-dibusb-5.0.0.11.fw";
my $outfile = "dvb-dibusb-5.0.0.11.fw";
my $hash = "fa490295a527360ca16dcdf3224ca243";
@@ -351,7 +351,7 @@ sub nxt2004 {
sub or51211 {
my $fwfile = "dvb-fe-or51211.fw";
- my $url = "http://linuxtv.org/downloads/firmware/$fwfile";
+ my $url = "https://linuxtv.org/downloads/firmware/$fwfile";
my $hash = "d830949c771a289505bf9eafc225d491";
checkstandard();
@@ -364,7 +364,7 @@ sub or51211 {
sub cx231xx {
my $fwfile = "v4l-cx231xx-avcore-01.fw";
- my $url = "http://linuxtv.org/downloads/firmware/$fwfile";
+ my $url = "https://linuxtv.org/downloads/firmware/$fwfile";
my $hash = "7d3bb956dc9df0eafded2b56ba57cc42";
checkstandard();
@@ -376,7 +376,7 @@ sub cx231xx {
}
sub cx18 {
- my $url = "http://linuxtv.org/downloads/firmware/";
+ my $url = "https://linuxtv.org/downloads/firmware/";
my %files = (
'v4l-cx23418-apu.fw' => '588f081b562f5c653a3db1ad8f65939a',
@@ -450,7 +450,7 @@ sub mpc718 {
}
sub cx23885 {
- my $url = "http://linuxtv.org/downloads/firmware/";
+ my $url = "https://linuxtv.org/downloads/firmware/";
my %files = (
'v4l-cx23885-avcore-01.fw' => 'a9f8f5d901a7fb42f552e1ee6384f3bb',
@@ -472,7 +472,7 @@ sub cx23885 {
}
sub pvrusb2 {
- my $url = "http://linuxtv.org/downloads/firmware/";
+ my $url = "https://linuxtv.org/downloads/firmware/";
my %files = (
'v4l-cx25840.fw' => 'dadb79e9904fc8af96e8111d9cb59320',
@@ -494,7 +494,7 @@ sub pvrusb2 {
sub or51132_qam {
my $fwfile = "dvb-fe-or51132-qam.fw";
- my $url = "http://linuxtv.org/downloads/firmware/$fwfile";
+ my $url = "https://linuxtv.org/downloads/firmware/$fwfile";
my $hash = "7702e8938612de46ccadfe9b413cb3b5";
checkstandard();
@@ -507,7 +507,7 @@ sub or51132_qam {
sub or51132_vsb {
my $fwfile = "dvb-fe-or51132-vsb.fw";
- my $url = "http://linuxtv.org/downloads/firmware/$fwfile";
+ my $url = "https://linuxtv.org/downloads/firmware/$fwfile";
my $hash = "c16208e02f36fc439a557ad4c613364a";
checkstandard();
@@ -519,7 +519,7 @@ sub or51132_vsb {
}
sub bluebird {
- my $url = "http://www.linuxtv.org/download/dvb/firmware/dvb-usb-bluebird-01.fw";
+ my $url = "https://linuxtv.org/download/dvb/firmware/dvb-usb-bluebird-01.fw";
my $outfile = "dvb-usb-bluebird-01.fw";
my $hash = "658397cb9eba9101af9031302671f49d";
@@ -677,7 +677,7 @@ sub drxk_hauppauge_hvr930c {
}
sub drxk_terratec_h5 {
- my $url = "http://www.linuxtv.org/downloads/firmware/";
+ my $url = "https://linuxtv.org/downloads/firmware/";
my $hash = "19000dada8e2741162ccc50cc91fa7f1";
my $fwfile = "dvb-usb-terratec-h5-drxk.fw";
diff --git a/Documentation/dvb/readme.txt b/Documentation/dvb/readme.txt
index 0b0380c91990..89965041a266 100644
--- a/Documentation/dvb/readme.txt
+++ b/Documentation/dvb/readme.txt
@@ -2,12 +2,12 @@ Linux Digital Video Broadcast (DVB) subsystem
=============================================
The main development site and CVS repository for these
-drivers is http://linuxtv.org/.
+drivers is https://linuxtv.org.
The developer mailing list linux-dvb is also hosted there,
-see http://linuxtv.org/lists.php. Please check
-the archive http://linuxtv.org/pipermail/linux-dvb/
-and the Wiki http://linuxtv.org/wiki/
+see https://linuxtv.org/lists.php. Please check
+the archive https://linuxtv.org/pipermail/linux-dvb/
+and the Wiki https://linuxtv.org/wiki/
before asking newbie questions on the list.
API documentation, utilities and test/example programs
@@ -16,7 +16,7 @@ are available as part of the old driver package for Linux 2.4
We plan to split this into separate packages, but it's not
been done yet.
-http://linuxtv.org/downloads/
+https://linuxtv.org/downloads/
What's inside this directory:
diff --git a/Documentation/edac.txt b/Documentation/edac.txt
index 80841a2d640c..f89cfd85ae13 100644
--- a/Documentation/edac.txt
+++ b/Documentation/edac.txt
@@ -1,9 +1,13 @@
EDAC - Error Detection And Correction
=====================================
-"bluesmoke" was the name for this device driver when it was "out-of-tree"
-and maintained at sourceforge.net. When it was pushed into 2.6.16 for the
-first time, it was renamed to 'EDAC'.
+"bluesmoke" was the name for this device driver when it
+was "out-of-tree" and maintained at sourceforge.net -
+bluesmoke.sourceforge.net. That site is mostly archaic now and can be
+used only for historical purposes.
+
+When the subsystem was pushed into 2.6.16 for the first time, it was
+renamed to 'EDAC'.
PURPOSE
-------
diff --git a/Documentation/fault-injection/notifier-error-inject.txt b/Documentation/fault-injection/notifier-error-inject.txt
index 09adabef513f..83d3f4e43e91 100644
--- a/Documentation/fault-injection/notifier-error-inject.txt
+++ b/Documentation/fault-injection/notifier-error-inject.txt
@@ -10,6 +10,7 @@ modules that can be used to test the following notifiers.
* PM notifier
* Memory hotplug notifier
* powerpc pSeries reconfig notifier
+ * Netdevice notifier
CPU notifier error injection module
-----------------------------------
@@ -87,6 +88,30 @@ Possible pSeries reconfig notifier events to be failed are:
* PSERIES_DRCONF_MEM_ADD
* PSERIES_DRCONF_MEM_REMOVE
+Netdevice notifier error injection module
+----------------------------------------------
+This feature is controlled through debugfs interface
+/sys/kernel/debug/notifier-error-inject/netdev/actions/<notifier event>/error
+
+Netdevice notifier events which can be failed are:
+
+ * NETDEV_REGISTER
+ * NETDEV_CHANGEMTU
+ * NETDEV_CHANGENAME
+ * NETDEV_PRE_UP
+ * NETDEV_PRE_TYPE_CHANGE
+ * NETDEV_POST_INIT
+ * NETDEV_PRECHANGEMTU
+ * NETDEV_PRECHANGEUPPER
+ * NETDEV_CHANGEUPPER
+
+Example: Inject netdevice mtu change error (-22 == -EINVAL)
+
+ # cd /sys/kernel/debug/notifier-error-inject/netdev
+ # echo -22 > actions/NETDEV_CHANGEMTU/error
+ # ip link set eth0 mtu 1024
+ RTNETLINK answers: Invalid argument
+
For more usage examples
-----------------------
There are tools/testing/selftests using the notifier error injection features
diff --git a/Documentation/features/seccomp/seccomp-filter/arch-support.txt b/Documentation/features/seccomp/seccomp-filter/arch-support.txt
index 76d39d66a5d7..4f66ec133951 100644
--- a/Documentation/features/seccomp/seccomp-filter/arch-support.txt
+++ b/Documentation/features/seccomp/seccomp-filter/arch-support.txt
@@ -33,7 +33,7 @@
| sh: | TODO |
| sparc: | TODO |
| tile: | ok |
- | um: | TODO |
+ | um: | ok |
| unicore32: | TODO |
| x86: | ok |
| xtensa: | TODO |
diff --git a/Documentation/features/time/irq-time-acct/arch-support.txt b/Documentation/features/time/irq-time-acct/arch-support.txt
index e63316239938..4199ffecc0ff 100644
--- a/Documentation/features/time/irq-time-acct/arch-support.txt
+++ b/Documentation/features/time/irq-time-acct/arch-support.txt
@@ -9,7 +9,7 @@
| alpha: | .. |
| arc: | TODO |
| arm: | ok |
- | arm64: | .. |
+ | arm64: | ok |
| avr32: | TODO |
| blackfin: | TODO |
| c6x: | TODO |
diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking
index 06d443450f21..619af9bfdcb3 100644
--- a/Documentation/filesystems/Locking
+++ b/Documentation/filesystems/Locking
@@ -50,8 +50,7 @@ prototypes:
int (*rename2) (struct inode *, struct dentry *,
struct inode *, struct dentry *, unsigned int);
int (*readlink) (struct dentry *, char __user *,int);
- const char *(*follow_link) (struct dentry *, void **);
- void (*put_link) (struct inode *, void *);
+ const char *(*get_link) (struct dentry *, struct inode *, void **);
void (*truncate) (struct inode *);
int (*permission) (struct inode *, int, unsigned int);
int (*get_acl)(struct inode *, int);
@@ -83,8 +82,7 @@ rmdir: yes (both) (see below)
rename: yes (all) (see below)
rename2: yes (all) (see below)
readlink: no
-follow_link: no
-put_link: no
+get_link: no
setattr: yes
permission: no (may not block if called in rcu-walk mode)
get_acl: no
diff --git a/Documentation/filesystems/configfs/configfs.txt b/Documentation/filesystems/configfs/configfs.txt
index af68efdbbfad..e5fe521eea1d 100644
--- a/Documentation/filesystems/configfs/configfs.txt
+++ b/Documentation/filesystems/configfs/configfs.txt
@@ -51,15 +51,27 @@ configfs tree is always there, whether mounted on /config or not.
An item is created via mkdir(2). The item's attributes will also
appear at this time. readdir(3) can determine what the attributes are,
read(2) can query their default values, and write(2) can store new
-values. Like sysfs, attributes should be ASCII text files, preferably
-with only one value per file. The same efficiency caveats from sysfs
-apply. Don't mix more than one attribute in one attribute file.
-
-Like sysfs, configfs expects write(2) to store the entire buffer at
-once. When writing to configfs attributes, userspace processes should
-first read the entire file, modify the portions they wish to change, and
-then write the entire buffer back. Attribute files have a maximum size
-of one page (PAGE_SIZE, 4096 on i386).
+values. Don't mix more than one attribute in one attribute file.
+
+There are two types of configfs attributes:
+
+* Normal attributes, which similar to sysfs attributes, are small ASCII text
+files, with a maximum size of one page (PAGE_SIZE, 4096 on i386). Preferably
+only one value per file should be used, and the same caveats from sysfs apply.
+Configfs expects write(2) to store the entire buffer at once. When writing to
+normal configfs attributes, userspace processes should first read the entire
+file, modify the portions they wish to change, and then write the entire
+buffer back.
+
+* Binary attributes, which are somewhat similar to sysfs binary attributes,
+but with a few slight changes to semantics. The PAGE_SIZE limitation does not
+apply, but the whole binary item must fit in single kernel vmalloc'ed buffer.
+The write(2) calls from user space are buffered, and the attributes'
+write_bin_attribute method will be invoked on the final close, therefore it is
+imperative for user-space to check the return code of close(2) in order to
+verify that the operation finished successfully.
+To avoid a malicious user OOMing the kernel, there's a per-binary attribute
+maximum buffer value.
When an item needs to be destroyed, remove it with rmdir(2). An
item cannot be destroyed if any other item has a link to it (via
@@ -171,6 +183,7 @@ among other things. For that, it needs a type.
struct configfs_item_operations *ct_item_ops;
struct configfs_group_operations *ct_group_ops;
struct configfs_attribute **ct_attrs;
+ struct configfs_bin_attribute **ct_bin_attrs;
};
The most basic function of a config_item_type is to define what
@@ -201,6 +214,32 @@ be called whenever userspace asks for a read(2) on the attribute. If an
attribute is writable and provides a ->store method, that method will be
be called whenever userspace asks for a write(2) on the attribute.
+[struct configfs_bin_attribute]
+
+ struct configfs_attribute {
+ struct configfs_attribute cb_attr;
+ void *cb_private;
+ size_t cb_max_size;
+ };
+
+The binary attribute is used when the one needs to use binary blob to
+appear as the contents of a file in the item's configfs directory.
+To do so add the binary attribute to the NULL-terminated array
+config_item_type->ct_bin_attrs, and the item appears in configfs, the
+attribute file will appear with the configfs_bin_attribute->cb_attr.ca_name
+filename. configfs_bin_attribute->cb_attr.ca_mode specifies the file
+permissions.
+The cb_private member is provided for use by the driver, while the
+cb_max_size member specifies the maximum amount of vmalloc buffer
+to be used.
+
+If binary attribute is readable and the config_item provides a
+ct_item_ops->read_bin_attribute() method, that method will be called
+whenever userspace asks for a read(2) on the attribute. The converse
+will happen for write(2). The reads/writes are bufferred so only a
+single read/write will occur; the attributes' need not concern itself
+with it.
+
[struct config_group]
A config_item cannot live in a vacuum. The only way one can be created
diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt
index b102b436563e..e1c9f0849da6 100644
--- a/Documentation/filesystems/f2fs.txt
+++ b/Documentation/filesystems/f2fs.txt
@@ -102,7 +102,7 @@ background_gc=%s Turn on/off cleaning operations, namely garbage
collection, triggered in background when I/O subsystem is
idle. If background_gc=on, it will turn on the garbage
collection and if background_gc=off, garbage collection
- will be truned off. If background_gc=sync, it will turn
+ will be turned off. If background_gc=sync, it will turn
on synchronous garbage collection running in background.
Default value for this option is on. So garbage
collection is on by default.
@@ -145,10 +145,12 @@ extent_cache Enable an extent cache based on rb-tree, it can cache
as many as extent which map between contiguous logical
address and physical address per inode, resulting in
increasing the cache hit ratio. Set by default.
-noextent_cache Diable an extent cache based on rb-tree explicitly, see
+noextent_cache Disable an extent cache based on rb-tree explicitly, see
the above extent_cache mount option.
noinline_data Disable the inline data feature, inline data feature is
enabled by default.
+data_flush Enable data flushing before checkpoint in order to
+ persist data of regular and symlink.
================================================================================
DEBUGFS ENTRIES
@@ -192,7 +194,7 @@ Files in /sys/fs/f2fs/<devname>
policy for garbage collection. Setting gc_idle = 0
(default) will disable this option. Setting
gc_idle = 1 will select the Cost Benefit approach
- & setting gc_idle = 2 will select the greedy aproach.
+ & setting gc_idle = 2 will select the greedy approach.
reclaim_segments This parameter controls the number of prefree
segments to be reclaimed. If the number of prefree
@@ -298,7 +300,7 @@ The dump.f2fs shows the information of specific inode and dumps SSA and SIT to
file. Each file is dump_ssa and dump_sit.
The dump.f2fs is used to debug on-disk data structures of the f2fs filesystem.
-It shows on-disk inode information reconized by a given inode number, and is
+It shows on-disk inode information recognized by a given inode number, and is
able to dump all the SSA and SIT entries into predefined files, ./dump_ssa and
./dump_sit respectively.
diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting
index f24d1b833957..f1b87d8aa2da 100644
--- a/Documentation/filesystems/porting
+++ b/Documentation/filesystems/porting
@@ -504,3 +504,24 @@ in your dentry operations instead.
[mandatory]
__fd_install() & fd_install() can now sleep. Callers should not
hold a spinlock or other resources that do not allow a schedule.
+--
+[mandatory]
+ any symlink that might use page_follow_link_light/page_put_link() must
+ have inode_nohighmem(inode) called before anything might start playing with
+ its pagecache. No highmem pages should end up in the pagecache of such
+ symlinks. That includes any preseeding that might be done during symlink
+ creation. __page_symlink() will honour the mapping gfp flags, so once
+ you've done inode_nohighmem() it's safe to use, but if you allocate and
+ insert the page manually, make sure to use the right gfp flags.
+--
+[mandatory]
+ ->follow_link() is replaced with ->get_link(); same API, except that
+ * ->get_link() gets inode as a separate argument
+ * ->get_link() may be called in RCU mode - in that case NULL
+ dentry is passed
+--
+[mandatory]
+ ->get_link() gets struct delayed_call *done now, and should do
+ set_delayed_call() where it used to set *cookie.
+ ->put_link() is gone - just give the destructor to set_delayed_call()
+ in ->get_link().
diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt
index 402ab99e409f..e95aa1c6eadf 100644
--- a/Documentation/filesystems/proc.txt
+++ b/Documentation/filesystems/proc.txt
@@ -169,6 +169,9 @@ read the file /proc/PID/status:
VmLck: 0 kB
VmHWM: 476 kB
VmRSS: 476 kB
+ RssAnon: 352 kB
+ RssFile: 120 kB
+ RssShmem: 4 kB
VmData: 156 kB
VmStk: 88 kB
VmExe: 68 kB
@@ -231,14 +234,20 @@ Table 1-2: Contents of the status files (as of 4.1)
VmSize total program size
VmLck locked memory size
VmHWM peak resident set size ("high water mark")
- VmRSS size of memory portions
+ VmRSS size of memory portions. It contains the three
+ following parts (VmRSS = RssAnon + RssFile + RssShmem)
+ RssAnon size of resident anonymous memory
+ RssFile size of resident file mappings
+ RssShmem size of resident shmem memory (includes SysV shm,
+ mapping of tmpfs and shared anonymous mappings)
VmData size of data, stack, and text segments
VmStk size of data, stack, and text segments
VmExe size of text segment
VmLib size of shared library code
VmPTE size of page table entries
VmPMD size of second level page tables
- VmSwap size of swap usage (the number of referred swapents)
+ VmSwap amount of swap used by anonymous private data
+ (shmem swap usage is not included)
HugetlbPages size of hugetlb memory portions
Threads number of threads
SigQ number of signals queued/max. number for queue
@@ -265,7 +274,8 @@ Table 1-3: Contents of the statm files (as of 2.6.8-rc3)
Field Content
size total program size (pages) (same as VmSize in status)
resident size of memory portions (pages) (same as VmRSS in status)
- shared number of pages that are shared (i.e. backed by a file)
+ shared number of pages that are shared (i.e. backed by a file, same
+ as RssFile+RssShmem in status)
trs number of pages that are 'code' (not including libs; broken,
includes data segment)
lrs number of pages of library (always 0 on 2.6)
@@ -459,7 +469,10 @@ and a page is modified, the file page is replaced by a private anonymous copy.
hugetlbfs page which is *not* counted in "RSS" or "PSS" field for historical
reasons. And these are not included in {Shared,Private}_{Clean,Dirty} field.
"Swap" shows how much would-be-anonymous memory is also used, but out on swap.
-"SwapPss" shows proportional swap share of this mapping.
+For shmem mappings, "Swap" includes also the size of the mapped (and not
+replaced by copy-on-write) part of the underlying shmem object out on swap.
+"SwapPss" shows proportional swap share of this mapping. Unlike "Swap", this
+does not take into account swapped out page of underlying shmem objects.
"Locked" indicates whether the mapping is locked in memory or not.
"VmFlags" field deserves a separate description. This member represents the kernel
@@ -842,6 +855,7 @@ Dirty: 968 kB
Writeback: 0 kB
AnonPages: 861800 kB
Mapped: 280372 kB
+Shmem: 644 kB
Slab: 284364 kB
SReclaimable: 159856 kB
SUnreclaim: 124508 kB
@@ -898,6 +912,7 @@ MemAvailable: An estimate of how much memory is available for starting new
AnonPages: Non-file backed pages mapped into userspace page tables
AnonHugePages: Non-file backed huge pages mapped into userspace page tables
Mapped: files which have been mmaped, such as libraries
+ Shmem: Total memory used by shared memory (shmem) and tmpfs
Slab: in-kernel data structures cache
SReclaimable: Part of Slab, that might be reclaimed, such as caches
SUnreclaim: Part of Slab, that cannot be reclaimed on memory pressure
diff --git a/Documentation/filesystems/tmpfs.txt b/Documentation/filesystems/tmpfs.txt
index 98ef55124158..d392e1505f17 100644
--- a/Documentation/filesystems/tmpfs.txt
+++ b/Documentation/filesystems/tmpfs.txt
@@ -17,10 +17,10 @@ RAM, where you have to create an ordinary filesystem on top. Ramdisks
cannot swap and you do not have the possibility to resize them.
Since tmpfs lives completely in the page cache and on swap, all tmpfs
-pages currently in memory will show up as cached. It will not show up
-as shared or something like that. Further on you can check the actual
-RAM+swap use of a tmpfs instance with df(1) and du(1).
-
+pages will be shown as "Shmem" in /proc/meminfo and "Shared" in
+free(1). Notice that these counters also include shared memory
+(shmem, see ipcs(1)). The most reliable way to get the count is
+using df(1) and du(1).
tmpfs has the following uses:
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt
index 8c6f07ad373a..b02a7d598258 100644
--- a/Documentation/filesystems/vfs.txt
+++ b/Documentation/filesystems/vfs.txt
@@ -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);
- const char *(*follow_link) (struct dentry *, void **);
- void (*put_link) (struct inode *, void *);
+ const char *(*get_link) (struct dentry *, struct inode *,
+ struct delayed_call *);
int (*permission) (struct inode *, int);
int (*get_acl)(struct inode *, int);
int (*setattr) (struct dentry *, struct iattr *);
@@ -434,20 +434,19 @@ otherwise noted.
readlink: called by the readlink(2) system call. Only required if
you want to support reading symbolic links
- follow_link: called by the VFS to follow a symbolic link to the
+ get_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 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 stored by follow_link() is passed
- to this method as the last parameter; only called when
- cookie isn't NULL.
+ pinned, arrange for its release by having get_link(..., ..., done)
+ do set_delayed_call(done, destructor, argument).
+ In that case destructor(argument) will be called once VFS is
+ done with the body you've returned.
+ May be called in RCU mode; that is indicated by NULL dentry
+ argument. If request can't be handled without leaving RCU mode,
+ have it return ERR_PTR(-ECHILD).
permission: called by the VFS to check for access rights on a POSIX-like
filesystem.
diff --git a/Documentation/hwmon/htu21 b/Documentation/hwmon/htu21
deleted file mode 100644
index f39a215fb6ae..000000000000
--- a/Documentation/hwmon/htu21
+++ /dev/null
@@ -1,46 +0,0 @@
-Kernel driver htu21
-===================
-
-Supported chips:
- * Measurement Specialties HTU21D
- Prefix: 'htu21'
- Addresses scanned: none
- Datasheet: Publicly available at the Measurement Specialties website
- http://www.meas-spec.com/downloads/HTU21D.pdf
-
-
-Author:
- William Markezana <william.markezana@meas-spec.com>
-
-Description
------------
-
-The HTU21D is a humidity and temperature sensor in a DFN package of
-only 3 x 3 mm footprint and 0.9 mm height.
-
-The devices communicate with the I2C protocol. All sensors are set to the
-same I2C address 0x40, so an entry with I2C_BOARD_INFO("htu21", 0x40) can
-be used in the board setup code.
-
-This driver does not auto-detect devices. You will have to instantiate the
-devices explicitly. Please see Documentation/i2c/instantiating-devices
-for details.
-
-sysfs-Interface
----------------
-
-temp1_input - temperature input
-humidity1_input - humidity input
-
-Notes
------
-
-The driver uses the default resolution settings of 12 bit for humidity and 14
-bit for temperature, which results in typical measurement times of 11 ms for
-humidity and 44 ms for temperature. To keep self heating below 0.1 degree
-Celsius, the device should not be active for more than 10% of the time. For
-this reason, the driver performs no more than two measurements per second and
-reports cached information if polled more frequently.
-
-Different resolutions, the on-chip heater, using the CRC checksum and reading
-the serial number are not supported yet.
diff --git a/Documentation/hwmon/ltc3815 b/Documentation/hwmon/ltc3815
new file mode 100644
index 000000000000..eb7db2d13587
--- /dev/null
+++ b/Documentation/hwmon/ltc3815
@@ -0,0 +1,61 @@
+Kernel driver ltc3815
+=====================
+
+Supported chips:
+ * Linear Technology LTC3815
+ Prefix: 'ltc3815'
+ Addresses scanned: -
+ Datasheet: http://www.linear.com/product/ltc3815
+
+Author: Guenter Roeck <linux@roeck-us.net>
+
+
+Description
+-----------
+
+LTC3815 is a Monolithic Synchronous DC/DC Step-Down Converter.
+
+
+Usage Notes
+-----------
+
+This driver does not probe for PMBus devices. You will have to instantiate
+devices explicitly.
+
+Example: the following commands will load the driver for an LTC3815
+at address 0x20 on I2C bus #1:
+
+# modprobe ltc3815
+# echo ltc3815 0x20 > /sys/bus/i2c/devices/i2c-1/new_device
+
+
+Sysfs attributes
+----------------
+
+in1_label "vin"
+in1_input Measured input voltage.
+in1_alarm Input voltage alarm.
+in1_highest Highest input voltage.
+in1_reset_history Reset input voltage history.
+
+in2_label "vout1".
+in2_input Measured output voltage.
+in2_alarm Output voltage alarm.
+in2_highest Highest output voltage.
+in2_reset_history Reset output voltage history.
+
+temp1_input Measured chip temperature.
+temp1_alarm Temperature alarm.
+temp1_highest Highest measured temperature.
+temp1_reset_history Reset temperature history.
+
+curr1_label "iin".
+curr1_input Measured input current.
+curr1_highest Highest input current.
+curr1_reset_history Reset input current history.
+
+curr2_label "iout1".
+curr2_input Measured output current.
+curr2_alarm Output current alarm.
+curr2_highest Highest output current.
+curr2_reset_history Reset output current history.
diff --git a/Documentation/iio/iio_configfs.txt b/Documentation/iio/iio_configfs.txt
new file mode 100644
index 000000000000..f0add35cd52e
--- /dev/null
+++ b/Documentation/iio/iio_configfs.txt
@@ -0,0 +1,93 @@
+Industrial IIO configfs support
+
+1. Overview
+
+Configfs is a filesystem-based manager of kernel objects. IIO uses some
+objects that could be easily configured using configfs (e.g.: devices,
+triggers).
+
+See Documentation/filesystems/configfs/configfs.txt for more information
+about how configfs works.
+
+2. Usage
+
+In order to use configfs support in IIO we need to select it at compile
+time via CONFIG_IIO_CONFIGFS config option.
+
+Then, mount the configfs filesystem (usually under /config directory):
+
+$ mkdir /config
+$ mount -t configfs none /config
+
+At this point, all default IIO groups will be created and can be accessed
+under /config/iio. Next chapters will describe available IIO configuration
+objects.
+
+3. Software triggers
+
+One of the IIO default configfs groups is the "triggers" group. It is
+automagically accessible when the configfs is mounted and can be found
+under /config/iio/triggers.
+
+IIO software triggers implementation offers support for creating multiple
+trigger types. A new trigger type is usually implemented as a separate
+kernel module following the interface in include/linux/iio/sw_trigger.h:
+
+/*
+ * drivers/iio/trigger/iio-trig-sample.c
+ * sample kernel module implementing a new trigger type
+ */
+#include <linux/iio/sw_trigger.h>
+
+
+static struct iio_sw_trigger *iio_trig_sample_probe(const char *name)
+{
+ /*
+ * This allocates and registers an IIO trigger plus other
+ * trigger type specific initialization.
+ */
+}
+
+static int iio_trig_hrtimer_remove(struct iio_sw_trigger *swt)
+{
+ /*
+ * This undoes the actions in iio_trig_sample_probe
+ */
+}
+
+static const struct iio_sw_trigger_ops iio_trig_sample_ops = {
+ .probe = iio_trig_sample_probe,
+ .remove = iio_trig_sample_remove,
+};
+
+static struct iio_sw_trigger_type iio_trig_sample = {
+ .name = "trig-sample",
+ .owner = THIS_MODULE,
+ .ops = &iio_trig_sample_ops,
+};
+
+module_iio_sw_trigger_driver(iio_trig_sample);
+
+Each trigger type has its own directory under /config/iio/triggers. Loading
+iio-trig-sample module will create 'trig-sample' trigger type directory
+/config/iio/triggers/trig-sample.
+
+We support the following interrupt sources (trigger types):
+ * hrtimer, uses high resolution timers as interrupt source
+
+3.1 Hrtimer triggers creation and destruction
+
+Loading iio-trig-hrtimer module will register hrtimer trigger types allowing
+users to create hrtimer triggers under /config/iio/triggers/hrtimer.
+
+e.g:
+
+$ mkdir /config/triggers/hrtimer/instance1
+$ rmdir /config/triggers/hrtimer/instance1
+
+Each trigger can have one or more attributes specific to the trigger type.
+
+3.2 "hrtimer" trigger types attributes
+
+"hrtimer" trigger type doesn't have any configurable attribute from /config dir.
+It does introduce the sampling_frequency attribute to trigger directory.
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 8978c26cacdd..5a6235ed3663 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -472,6 +472,15 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
Change the amount of debugging information output
when initialising the APIC and IO-APIC components.
+ apic_extnmi= [APIC,X86] External NMI delivery setting
+ Format: { bsp (default) | all | none }
+ bsp: External NMI is delivered only to CPU 0
+ all: External NMIs are broadcast to all CPUs as a
+ backup of CPU 0
+ none: External NMI is masked for all CPUs. This is
+ useful so that a dump capture kernel won't be
+ shot down by NMI
+
autoconf= [IPV6]
See Documentation/networking/ipv6.txt.
@@ -599,6 +608,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
cut the overhead, others just disable the usage. So
only cgroup_disable=memory is actually worthy}
+ cgroup.memory= [KNL] Pass options to the cgroup memory controller.
+ Format: <string>
+ nosocket -- Disable socket memory accounting.
+
checkreqprot [SELINUX] Set initial checkreqprot flag value.
Format: { "0" | "1" }
See security/selinux/Kconfig help text.
@@ -721,16 +734,17 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
uart[8250],io,<addr>[,options]
uart[8250],mmio,<addr>[,options]
+ uart[8250],mmio16,<addr>[,options]
uart[8250],mmio32,<addr>[,options]
uart[8250],0x<addr>[,options]
Start an early, polled-mode console on the 8250/16550
UART at the specified I/O port or MMIO address,
switching to the matching ttyS device later.
MMIO inter-register address stride is either 8-bit
- (mmio) or 32-bit (mmio32).
- If none of [io|mmio|mmio32], <addr> is assumed to be
- equivalent to 'mmio'. 'options' are specified in the
- same format described for ttyS above; if unspecified,
+ (mmio), 16-bit (mmio16), or 32-bit (mmio32).
+ If none of [io|mmio|mmio16|mmio32], <addr> is assumed
+ to be equivalent to 'mmio'. 'options' are specified in
+ the same format described for ttyS above; if unspecified,
the h/w is not re-initialized.
hvc<n> Use the hypervisor console device <n>. This is for
@@ -1002,10 +1016,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
unspecified, the h/w is not initialized.
pl011,<addr>
+ pl011,mmio32,<addr>
Start an early, polled-mode console on a pl011 serial
port at the specified address. The pl011 serial port
must already be setup and configured. Options are not
- yet supported.
+ yet supported. If 'mmio32' is specified, then only
+ the driver will use only 32-bit accessors to read/write
+ the device registers.
msm_serial,<addr>
Start an early, polled-mode console on an msm serial
@@ -2575,8 +2592,6 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
notsc [BUGS=X86-32] Disable Time Stamp Counter
- nousb [USB] Disable the USB subsystem
-
nowatchdog [KNL] Disable both lockup detectors, i.e.
soft-lockup and NMI watchdog (hard-lockup).
@@ -3302,18 +3317,35 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
rcutorture.verbose= [KNL]
Enable additional printk() statements.
+ rcupdate.rcu_cpu_stall_suppress= [KNL]
+ Suppress RCU CPU stall warning messages.
+
+ rcupdate.rcu_cpu_stall_timeout= [KNL]
+ Set timeout for RCU CPU stall warning messages.
+
rcupdate.rcu_expedited= [KNL]
Use expedited grace-period primitives, for
example, synchronize_rcu_expedited() instead
of synchronize_rcu(). This reduces latency,
but can increase CPU utilization, degrade
real-time latency, and degrade energy efficiency.
-
- rcupdate.rcu_cpu_stall_suppress= [KNL]
- Suppress RCU CPU stall warning messages.
-
- rcupdate.rcu_cpu_stall_timeout= [KNL]
- Set timeout for RCU CPU stall warning messages.
+ No effect on CONFIG_TINY_RCU kernels.
+
+ rcupdate.rcu_normal= [KNL]
+ Use only normal grace-period primitives,
+ for example, synchronize_rcu() instead of
+ synchronize_rcu_expedited(). This improves
+ real-time latency, CPU utilization, and
+ energy efficiency, but can expose users to
+ increased grace-period latency. This parameter
+ overrides rcupdate.rcu_expedited. No effect on
+ CONFIG_TINY_RCU kernels.
+
+ rcupdate.rcu_normal_after_boot= [KNL]
+ Once boot has completed (that is, after
+ rcu_end_inkernel_boot() has been invoked), use
+ only normal grace-period primitives. No effect
+ on CONFIG_TINY_RCU kernels.
rcupdate.rcu_task_stall_timeout= [KNL]
Set timeout in jiffies for RCU task stall warning
@@ -3880,6 +3912,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
usbcore.usbfs_snoop=
[USB] Set to log all usbfs traffic (default 0 = off).
+ usbcore.usbfs_snoop_max=
+ [USB] Maximum number of bytes to snoop in each URB
+ (default = 65536).
+
usbcore.blinkenlights=
[USB] Set to cycle leds on hubs (default 0 = off).
@@ -3900,6 +3936,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
USB_REQ_GET_DESCRIPTOR request in milliseconds
(default 5000 = 5.0 seconds).
+ usbcore.nousb [USB] Disable the USB subsystem
+
usbhid.mousepoll=
[USBHID] The interval which mice are to be polled at.
@@ -4120,6 +4158,15 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
or other driver-specific files in the
Documentation/watchdog/ directory.
+ workqueue.watchdog_thresh=
+ If CONFIG_WQ_WATCHDOG is configured, workqueue can
+ warn stall conditions and dump internal state to
+ help debugging. 0 disables workqueue stall
+ detection; otherwise, it's the stall threshold
+ duration in seconds. The default value is 30 and
+ it can be updated at runtime by writing to the
+ corresponding sysfs file.
+
workqueue.disable_numa
By default, all work items queued to unbound
workqueues are affine to the NUMA nodes they're
diff --git a/Documentation/leds/leds-class.txt b/Documentation/leds/leds-class.txt
index 62261c04060a..d406d98339b2 100644
--- a/Documentation/leds/leds-class.txt
+++ b/Documentation/leds/leds-class.txt
@@ -52,6 +52,19 @@ above leaves scope for further attributes should they be needed. If sections
of the name don't apply, just leave that section blank.
+Brightness setting API
+======================
+
+LED subsystem core exposes following API for setting brightness:
+
+ - led_set_brightness : it is guaranteed not to sleep, passing LED_OFF stops
+ blinking,
+ - led_set_brightness_sync : for use cases when immediate effect is desired -
+ it can block the caller for the time required for accessing
+ device registers and can sleep, passing LED_OFF stops hardware
+ blinking, returns -EBUSY if software blink fallback is enabled.
+
+
Hardware accelerated blink of LEDs
==================================
diff --git a/Documentation/md-cluster.txt b/Documentation/md-cluster.txt
index 1b794369e03a..c100c7163507 100644
--- a/Documentation/md-cluster.txt
+++ b/Documentation/md-cluster.txt
@@ -3,7 +3,7 @@ The cluster MD is a shared-device RAID for a cluster.
1. On-disk format
-Separate write-intent-bitmap are used for each cluster node.
+Separate write-intent-bitmaps are used for each cluster node.
The bitmaps record all writes that may have been started on that node,
and may not yet have finished. The on-disk layout is:
@@ -14,117 +14,161 @@ and may not yet have finished. The on-disk layout is:
| bm super[2] + bits | bm bits [2, contd] | bm super[3] + bits |
| bm bits [3, contd] | | |
-During "normal" functioning we assume the filesystem ensures that only one
-node writes to any given block at a time, so a write
-request will
+During "normal" functioning we assume the filesystem ensures that only
+one node writes to any given block at a time, so a write request will
+
- set the appropriate bit (if not already set)
- commit the write to all mirrors
- schedule the bit to be cleared after a timeout.
-Reads are just handled normally. It is up to the filesystem to
-ensure one node doesn't read from a location where another node (or the same
+Reads are just handled normally. It is up to the filesystem to ensure
+one node doesn't read from a location where another node (or the same
node) is writing.
2. DLM Locks for management
-There are two locks for managing the device:
+There are three groups of locks for managing the device:
2.1 Bitmap lock resource (bm_lockres)
- The bm_lockres protects individual node bitmaps. They are named in the
- form bitmap001 for node 1, bitmap002 for node and so on. When a node
- joins the cluster, it acquires the lock in PW mode and it stays so
- during the lifetime the node is part of the cluster. The lock resource
- number is based on the slot number returned by the DLM subsystem. Since
- DLM starts node count from one and bitmap slots start from zero, one is
- subtracted from the DLM slot number to arrive at the bitmap slot number.
+ The bm_lockres protects individual node bitmaps. They are named in
+ the form bitmap000 for node 1, bitmap001 for node 2 and so on. When a
+ node joins the cluster, it acquires the lock in PW mode and it stays
+ so during the lifetime the node is part of the cluster. The lock
+ resource number is based on the slot number returned by the DLM
+ subsystem. Since DLM starts node count from one and bitmap slots
+ start from zero, one is subtracted from the DLM slot number to arrive
+ at the bitmap slot number.
+
+ The LVB of the bitmap lock for a particular node records the range
+ of sectors that are being re-synced by that node. No other
+ node may write to those sectors. This is used when a new nodes
+ joins the cluster.
+
+2.2 Message passing locks
+
+ Each node has to communicate with other nodes when starting or ending
+ resync, and for metadata superblock updates. This communication is
+ managed through three locks: "token", "message", and "ack", together
+ with the Lock Value Block (LVB) of one of the "message" lock.
+
+2.3 new-device management
+
+ A single lock: "no-new-dev" is used to co-ordinate the addition of
+ new devices - this must be synchronized across the array.
+ Normally all nodes hold a concurrent-read lock on this device.
3. Communication
-Each node has to communicate with other nodes when starting or ending
-resync, and metadata superblock updates.
+ Messages can be broadcast to all nodes, and the sender waits for all
+ other nodes to acknowledge the message before proceeding. Only one
+ message can be processed at a time.
3.1 Message Types
- There are 3 types, of messages which are passed
+ There are six types of messages which are passed:
- 3.1.1 METADATA_UPDATED: informs other nodes that the metadata has been
- updated, and the node must re-read the md superblock. This is performed
- synchronously.
+ 3.1.1 METADATA_UPDATED: informs other nodes that the metadata has
+ been updated, and the node must re-read the md superblock. This is
+ performed synchronously. It is primarily used to signal device
+ failure.
- 3.1.2 RESYNC: informs other nodes that a resync is initiated or ended
- so that each node may suspend or resume the region.
+ 3.1.2 RESYNCING: informs other nodes that a resync is initiated or
+ ended so that each node may suspend or resume the region. Each
+ RESYNCING message identifies a range of the devices that the
+ sending node is about to resync. This over-rides any pervious
+ notification from that node: only one ranged can be resynced at a
+ time per-node.
+
+ 3.1.3 NEWDISK: informs other nodes that a device is being added to
+ the array. Message contains an identifier for that device. See
+ below for further details.
+
+ 3.1.4 REMOVE: A failed or spare device is being removed from the
+ array. The slot-number of the device is included in the message.
+
+ 3.1.5 RE_ADD: A failed device is being re-activated - the assumption
+ is that it has been determined to be working again.
+
+ 3.1.6 BITMAP_NEEDS_SYNC: if a node is stopped locally but the bitmap
+ isn't clean, then another node is informed to take the ownership of
+ resync.
3.2 Communication mechanism
The DLM LVB is used to communicate within nodes of the cluster. There
are three resources used for the purpose:
- 3.2.1 Token: The resource which protects the entire communication
+ 3.2.1 token: The resource which protects the entire communication
system. The node having the token resource is allowed to
communicate.
- 3.2.2 Message: The lock resource which carries the data to
+ 3.2.2 message: The lock resource which carries the data to
communicate.
- 3.2.3 Ack: The resource, acquiring which means the message has been
+ 3.2.3 ack: The resource, acquiring which means the message has been
acknowledged by all nodes in the cluster. The BAST of the resource
- is used to inform the receive node that a node wants to communicate.
+ is used to inform the receiving node that a node wants to
+ communicate.
The algorithm is:
- 1. receive status
+ 1. receive status - all nodes have concurrent-reader lock on "ack".
- sender receiver receiver
- ACK:CR ACK:CR ACK:CR
+ sender receiver receiver
+ "ack":CR "ack":CR "ack":CR
- 2. sender get EX of TOKEN
- sender get EX of MESSAGE
+ 2. sender get EX on "token"
+ sender get EX on "message"
sender receiver receiver
- TOKEN:EX ACK:CR ACK:CR
- MESSAGE:EX
- ACK:CR
+ "token":EX "ack":CR "ack":CR
+ "message":EX
+ "ack":CR
- Sender checks that it still needs to send a message. Messages received
- or other events that happened while waiting for the TOKEN may have made
- this message inappropriate or redundant.
+ Sender checks that it still needs to send a message. Messages
+ received or other events that happened while waiting for the
+ "token" may have made this message inappropriate or redundant.
- 3. sender write LVB.
- sender down-convert MESSAGE from EX to CW
- sender try to get EX of ACK
- [ wait until all receiver has *processed* the MESSAGE ]
+ 3. sender writes LVB.
+ sender down-convert "message" from EX to CW
+ sender try to get EX of "ack"
+ [ wait until all receivers have *processed* the "message" ]
- [ triggered by bast of ACK ]
- receiver get CR of MESSAGE
+ [ triggered by bast of "ack" ]
+ receiver get CR on "message"
receiver read LVB
receiver processes the message
[ wait finish ]
- receiver release ACK
-
- sender receiver receiver
- TOKEN:EX MESSAGE:CR MESSAGE:CR
- MESSAGE:CR
- ACK:EX
-
- 4. triggered by grant of EX on ACK (indicating all receivers have processed
- message)
- sender down-convert ACK from EX to CR
- sender release MESSAGE
- sender release TOKEN
- receiver upconvert to PR of MESSAGE
- receiver get CR of ACK
- receiver release MESSAGE
+ receiver releases "ack"
+ receiver tries to get PR on "message"
+
+ sender receiver receiver
+ "token":EX "message":CR "message":CR
+ "message":CW
+ "ack":EX
+
+ 4. triggered by grant of EX on "ack" (indicating all receivers
+ have processed message)
+ sender down-converts "ack" from EX to CR
+ sender releases "message"
+ sender releases "token"
+ receiver upconvert to PR on "message"
+ receiver get CR of "ack"
+ receiver release "message"
sender receiver receiver
- ACK:CR ACK:CR ACK:CR
+ "ack":CR "ack":CR "ack":CR
4. Handling Failures
4.1 Node Failure
- When a node fails, the DLM informs the cluster with the slot. The node
- starts a cluster recovery thread. The cluster recovery thread:
+
+ When a node fails, the DLM informs the cluster with the slot
+ number. The node starts a cluster recovery thread. The cluster
+ recovery thread:
+
- acquires the bitmap<number> lock of the failed node
- opens the bitmap
- reads the bitmap of the failed node
@@ -132,45 +176,143 @@ The algorithm is:
- cleans the bitmap of the failed node
- releases bitmap<number> lock of the failed node
- initiates resync of the bitmap on the current node
+ md_check_recovery is invoked within recover_bitmaps,
+ then md_check_recovery -> metadata_update_start/finish,
+ it will lock the communication by lock_comm.
+ Which means when one node is resyncing it blocks all
+ other nodes from writing anywhere on the array.
- The resync process, is the regular md resync. However, in a clustered
+ The resync process is the regular md resync. However, in a clustered
environment when a resync is performed, it needs to tell other nodes
of the areas which are suspended. Before a resync starts, the node
- send out RESYNC_START with the (lo,hi) range of the area which needs
- to be suspended. Each node maintains a suspend_list, which contains
- the list of ranges which are currently suspended. On receiving
- RESYNC_START, the node adds the range to the suspend_list. Similarly,
- when the node performing resync finishes, it send RESYNC_FINISHED
- to other nodes and other nodes remove the corresponding entry from
- the suspend_list.
+ send out RESYNCING with the (lo,hi) range of the area which needs to
+ be suspended. Each node maintains a suspend_list, which contains the
+ list of ranges which are currently suspended. On receiving RESYNCING,
+ the node adds the range to the suspend_list. Similarly, when the node
+ performing resync finishes, it sends RESYNCING with an empty range to
+ other nodes and other nodes remove the corresponding entry from the
+ suspend_list.
- A helper function, should_suspend() can be used to check if a particular
- I/O range should be suspended or not.
+ A helper function, ->area_resyncing() can be used to check if a
+ particular I/O range should be suspended or not.
4.2 Device Failure
+
Device failures are handled and communicated with the metadata update
- routine.
+ routine. When a node detects a device failure it does not allow
+ any further writes to that device until the failure has been
+ acknowledged by all other nodes.
5. Adding a new Device
-For adding a new device, it is necessary that all nodes "see" the new device
-to be added. For this, the following algorithm is used:
+
+ For adding a new device, it is necessary that all nodes "see" the new
+ device to be added. For this, the following algorithm is used:
1. Node 1 issues mdadm --manage /dev/mdX --add /dev/sdYY which issues
- ioctl(ADD_NEW_DISC with disc.state set to MD_DISK_CLUSTER_ADD)
- 2. Node 1 sends NEWDISK with uuid and slot number
+ ioctl(ADD_NEW_DISK with disc.state set to MD_DISK_CLUSTER_ADD)
+ 2. Node 1 sends a NEWDISK message with uuid and slot number
3. Other nodes issue kobject_uevent_env with uuid and slot number
(Steps 4,5 could be a udev rule)
4. In userspace, the node searches for the disk, perhaps
using blkid -t SUB_UUID=""
- 5. Other nodes issue either of the following depending on whether the disk
- was found:
+ 5. Other nodes issue either of the following depending on whether
+ the disk was found:
ioctl(ADD_NEW_DISK with disc.state set to MD_DISK_CANDIDATE and
- disc.number set to slot number)
+ disc.number set to slot number)
ioctl(CLUSTERED_DISK_NACK)
- 6. Other nodes drop lock on no-new-devs (CR) if device is found
- 7. Node 1 attempts EX lock on no-new-devs
- 8. If node 1 gets the lock, it sends METADATA_UPDATED after unmarking the disk
- as SpareLocal
- 9. If not (get no-new-dev lock), it fails the operation and sends METADATA_UPDATED
- 10. Other nodes get the information whether a disk is added or not
- by the following METADATA_UPDATED.
+ 6. Other nodes drop lock on "no-new-devs" (CR) if device is found
+ 7. Node 1 attempts EX lock on "no-new-dev"
+ 8. If node 1 gets the lock, it sends METADATA_UPDATED after
+ unmarking the disk as SpareLocal
+ 9. If not (get "no-new-dev" lock), it fails the operation and sends
+ METADATA_UPDATED.
+ 10. Other nodes get the information whether a disk is added or not
+ by the following METADATA_UPDATED.
+
+6. Module interface.
+
+ There are 17 call-backs which the md core can make to the cluster
+ module. Understanding these can give a good overview of the whole
+ process.
+
+6.1 join(nodes) and leave()
+
+ These are called when an array is started with a clustered bitmap,
+ and when the array is stopped. join() ensures the cluster is
+ available and initializes the various resources.
+ Only the first 'nodes' nodes in the cluster can use the array.
+
+6.2 slot_number()
+
+ Reports the slot number advised by the cluster infrastructure.
+ Range is from 0 to nodes-1.
+
+6.3 resync_info_update()
+
+ This updates the resync range that is stored in the bitmap lock.
+ The starting point is updated as the resync progresses. The
+ end point is always the end of the array.
+ It does *not* send a RESYNCING message.
+
+6.4 resync_start(), resync_finish()
+
+ These are called when resync/recovery/reshape starts or stops.
+ They update the resyncing range in the bitmap lock and also
+ send a RESYNCING message. resync_start reports the whole
+ array as resyncing, resync_finish reports none of it.
+
+ resync_finish() also sends a BITMAP_NEEDS_SYNC message which
+ allows some other node to take over.
+
+6.5 metadata_update_start(), metadata_update_finish(),
+ metadata_update_cancel().
+
+ metadata_update_start is used to get exclusive access to
+ the metadata. If a change is still needed once that access is
+ gained, metadata_update_finish() will send a METADATA_UPDATE
+ message to all other nodes, otherwise metadata_update_cancel()
+ can be used to release the lock.
+
+6.6 area_resyncing()
+
+ This combines two elements of functionality.
+
+ Firstly, it will check if any node is currently resyncing
+ anything in a given range of sectors. If any resync is found,
+ then the caller will avoid writing or read-balancing in that
+ range.
+
+ Secondly, while node recovery is happening it reports that
+ all areas are resyncing for READ requests. This avoids races
+ between the cluster-filesystem and the cluster-RAID handling
+ a node failure.
+
+6.7 add_new_disk_start(), add_new_disk_finish(), new_disk_ack()
+
+ These are used to manage the new-disk protocol described above.
+ When a new device is added, add_new_disk_start() is called before
+ it is bound to the array and, if that succeeds, add_new_disk_finish()
+ is called the device is fully added.
+
+ When a device is added in acknowledgement to a previous
+ request, or when the device is declared "unavailable",
+ new_disk_ack() is called.
+
+6.8 remove_disk()
+
+ This is called when a spare or failed device is removed from
+ the array. It causes a REMOVE message to be send to other nodes.
+
+6.9 gather_bitmaps()
+
+ This sends a RE_ADD message to all other nodes and then
+ gathers bitmap information from all bitmaps. This combined
+ bitmap is then used to recovery the re-added device.
+
+6.10 lock_all_bitmaps() and unlock_all_bitmaps()
+
+ These are called when change bitmap to none. If a node plans
+ to clear the cluster raid's bitmap, it need to make sure no other
+ nodes are using the raid which is achieved by lock all bitmap
+ locks within the cluster, and also those locks are unlocked
+ accordingly.
diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt
deleted file mode 100644
index f552a75c0e70..000000000000
--- a/Documentation/media-framework.txt
+++ /dev/null
@@ -1,372 +0,0 @@
-Linux kernel media framework
-============================
-
-This document describes the Linux kernel media framework, its data structures,
-functions and their usage.
-
-
-Introduction
-------------
-
-The media controller API is documented in DocBook format in
-Documentation/DocBook/media/v4l/media-controller.xml. This document will focus
-on the kernel-side implementation of the media framework.
-
-
-Abstract media device model
----------------------------
-
-Discovering a device internal topology, and configuring it at runtime, is one
-of the goals of the media framework. To achieve this, hardware devices are
-modelled as an oriented graph of building blocks called entities connected
-through pads.
-
-An entity is a basic media hardware building block. It can correspond to
-a large variety of logical blocks such as physical hardware devices
-(CMOS sensor for instance), logical hardware devices (a building block
-in a System-on-Chip image processing pipeline), DMA channels or physical
-connectors.
-
-A pad is a connection endpoint through which an entity can interact with
-other entities. Data (not restricted to video) produced by an entity
-flows from the entity's output to one or more entity inputs. Pads should
-not be confused with physical pins at chip boundaries.
-
-A link is a point-to-point oriented connection between two pads, either
-on the same entity or on different entities. Data flows from a source
-pad to a sink pad.
-
-
-Media device
-------------
-
-A media device is represented by a struct media_device instance, defined in
-include/media/media-device.h. Allocation of the structure is handled by the
-media device driver, usually by embedding the media_device instance in a
-larger driver-specific structure.
-
-Drivers register media device instances by calling
-
- media_device_register(struct media_device *mdev);
-
-The caller is responsible for initializing the media_device structure before
-registration. The following fields must be set:
-
- - dev must point to the parent device (usually a pci_dev, usb_interface or
- platform_device instance).
-
- - model must be filled with the device model name as a NUL-terminated UTF-8
- string. The device/model revision must not be stored in this field.
-
-The following fields are optional:
-
- - serial is a unique serial number stored as a NUL-terminated ASCII string.
- The field is big enough to store a GUID in text form. If the hardware
- doesn't provide a unique serial number this field must be left empty.
-
- - bus_info represents the location of the device in the system as a
- NUL-terminated ASCII string. For PCI/PCIe devices bus_info must be set to
- "PCI:" (or "PCIe:") followed by the value of pci_name(). For USB devices,
- the usb_make_path() function must be used. This field is used by
- applications to distinguish between otherwise identical devices that don't
- provide a serial number.
-
- - hw_revision is the hardware device revision in a driver-specific format.
- When possible the revision should be formatted with the KERNEL_VERSION
- macro.
-
- - driver_version is formatted with the KERNEL_VERSION macro. The version
- minor must be incremented when new features are added to the userspace API
- without breaking binary compatibility. The version major must be
- incremented when binary compatibility is broken.
-
-Upon successful registration a character device named media[0-9]+ is created.
-The device major and minor numbers are dynamic. The model name is exported as
-a sysfs attribute.
-
-Drivers unregister media device instances by calling
-
- media_device_unregister(struct media_device *mdev);
-
-Unregistering a media device that hasn't been registered is *NOT* safe.
-
-
-Entities, pads and links
-------------------------
-
-- Entities
-
-Entities are represented by a struct media_entity instance, defined in
-include/media/media-entity.h. The structure is usually embedded into a
-higher-level structure, such as a v4l2_subdev or video_device instance,
-although drivers can allocate entities directly.
-
-Drivers initialize entities by calling
-
- media_entity_init(struct media_entity *entity, u16 num_pads,
- struct media_pad *pads, u16 extra_links);
-
-The media_entity name, type, flags, revision and group_id fields can be
-initialized before or after calling media_entity_init. Entities embedded in
-higher-level standard structures can have some of those fields set by the
-higher-level framework.
-
-As the number of pads is known in advance, the pads array is not allocated
-dynamically but is managed by the entity driver. Most drivers will embed the
-pads array in a driver-specific structure, avoiding dynamic allocation.
-
-Drivers must set the direction of every pad in the pads array before calling
-media_entity_init. The function will initialize the other pads fields.
-
-Unlike the number of pads, the total number of links isn't always known in
-advance by the entity driver. As an initial estimate, media_entity_init
-pre-allocates a number of links equal to the number of pads plus an optional
-number of extra links. The links array will be reallocated if it grows beyond
-the initial estimate.
-
-Drivers register entities with a media device by calling
-
- media_device_register_entity(struct media_device *mdev,
- struct media_entity *entity);
-
-Entities are identified by a unique positive integer ID. Drivers can provide an
-ID by filling the media_entity id field prior to registration, or request the
-media controller framework to assign an ID automatically. Drivers that provide
-IDs manually must ensure that all IDs are unique. IDs are not guaranteed to be
-contiguous even when they are all assigned automatically by the framework.
-
-Drivers unregister entities by calling
-
- media_device_unregister_entity(struct media_entity *entity);
-
-Unregistering an entity will not change the IDs of the other entities, and the
-ID will never be reused for a newly registered entity.
-
-When a media device is unregistered, all its entities are unregistered
-automatically. No manual entities unregistration is then required.
-
-Drivers free resources associated with an entity by calling
-
- media_entity_cleanup(struct media_entity *entity);
-
-This function must be called during the cleanup phase after unregistering the
-entity. Note that the media_entity instance itself must be freed explicitly by
-the driver if required.
-
-Entities have flags that describe the entity capabilities and state.
-
- MEDIA_ENT_FL_DEFAULT indicates the default entity for a given type.
- This can be used to report the default audio and video devices or the
- default camera sensor.
-
-Logical entity groups can be defined by setting the group ID of all member
-entities to the same non-zero value. An entity group serves no purpose in the
-kernel, but is reported to userspace during entities enumeration. The group_id
-field belongs to the media device driver and must not by touched by entity
-drivers.
-
-Media device drivers should define groups if several entities are logically
-bound together. Example usages include reporting
-
- - ALSA, VBI and video nodes that carry the same media stream
- - lens and flash controllers associated with a sensor
-
-- Pads
-
-Pads are represented by a struct media_pad instance, defined in
-include/media/media-entity.h. Each entity stores its pads in a pads array
-managed by the entity driver. Drivers usually embed the array in a
-driver-specific structure.
-
-Pads are identified by their entity and their 0-based index in the pads array.
-Both information are stored in the media_pad structure, making the media_pad
-pointer the canonical way to store and pass link references.
-
-Pads have flags that describe the pad capabilities and state.
-
- MEDIA_PAD_FL_SINK indicates that the pad supports sinking data.
- MEDIA_PAD_FL_SOURCE indicates that the pad supports sourcing data.
-
-One and only one of MEDIA_PAD_FL_SINK and MEDIA_PAD_FL_SOURCE must be set for
-each pad.
-
-- Links
-
-Links are represented by a struct media_link instance, defined in
-include/media/media-entity.h. Each entity stores all links originating at or
-targeting any of its pads in a links array. A given link is thus stored
-twice, once in the source entity and once in the target entity. The array is
-pre-allocated and grows dynamically as needed.
-
-Drivers create links by calling
-
- media_entity_create_link(struct media_entity *source, u16 source_pad,
- struct media_entity *sink, u16 sink_pad,
- u32 flags);
-
-An entry in the link array of each entity is allocated and stores pointers
-to source and sink pads.
-
-Links have flags that describe the link capabilities and state.
-
- MEDIA_LNK_FL_ENABLED indicates that the link is enabled and can be used
- to transfer media data. When two or more links target a sink pad, only
- one of them can be enabled at a time.
- MEDIA_LNK_FL_IMMUTABLE indicates that the link enabled state can't be
- modified at runtime. If MEDIA_LNK_FL_IMMUTABLE is set, then
- MEDIA_LNK_FL_ENABLED must also be set since an immutable link is always
- enabled.
-
-
-Graph traversal
----------------
-
-The media framework provides APIs to iterate over entities in a graph.
-
-To iterate over all entities belonging to a media device, drivers can use the
-media_device_for_each_entity macro, defined in include/media/media-device.h.
-
- struct media_entity *entity;
-
- media_device_for_each_entity(entity, mdev) {
- /* entity will point to each entity in turn */
- ...
- }
-
-Drivers might also need to iterate over all entities in a graph that can be
-reached only through enabled links starting at a given entity. The media
-framework provides a depth-first graph traversal API for that purpose.
-
-Note that graphs with cycles (whether directed or undirected) are *NOT*
-supported by the graph traversal API. To prevent infinite loops, the graph
-traversal code limits the maximum depth to MEDIA_ENTITY_ENUM_MAX_DEPTH,
-currently defined as 16.
-
-Drivers initiate a graph traversal by calling
-
- media_entity_graph_walk_start(struct media_entity_graph *graph,
- struct media_entity *entity);
-
-The graph structure, provided by the caller, is initialized to start graph
-traversal at the given entity.
-
-Drivers can then retrieve the next entity by calling
-
- media_entity_graph_walk_next(struct media_entity_graph *graph);
-
-When the graph traversal is complete the function will return NULL.
-
-Graph traversal can be interrupted at any moment. No cleanup function call is
-required and the graph structure can be freed normally.
-
-Helper functions can be used to find a link between two given pads, or a pad
-connected to another pad through an enabled link
-
- media_entity_find_link(struct media_pad *source,
- struct media_pad *sink);
-
- media_entity_remote_pad(struct media_pad *pad);
-
-Refer to the kerneldoc documentation for more information.
-
-
-Use count and power handling
-----------------------------
-
-Due to the wide differences between drivers regarding power management needs,
-the media controller does not implement power management. However, the
-media_entity structure includes a use_count field that media drivers can use to
-track the number of users of every entity for power management needs.
-
-The use_count field is owned by media drivers and must not be touched by entity
-drivers. Access to the field must be protected by the media device graph_mutex
-lock.
-
-
-Links setup
------------
-
-Link properties can be modified at runtime by calling
-
- media_entity_setup_link(struct media_link *link, u32 flags);
-
-The flags argument contains the requested new link flags.
-
-The only configurable property is the ENABLED link flag to enable/disable a
-link. Links marked with the IMMUTABLE link flag can not be enabled or disabled.
-
-When a link is enabled or disabled, the media framework calls the
-link_setup operation for the two entities at the source and sink of the link,
-in that order. If the second link_setup call fails, another link_setup call is
-made on the first entity to restore the original link flags.
-
-Media device drivers can be notified of link setup operations by setting the
-media_device::link_notify pointer to a callback function. If provided, the
-notification callback will be called before enabling and after disabling
-links.
-
-Entity drivers must implement the link_setup operation if any of their links
-is non-immutable. The operation must either configure the hardware or store
-the configuration information to be applied later.
-
-Link configuration must not have any side effect on other links. If an enabled
-link at a sink pad prevents another link at the same pad from being enabled,
-the link_setup operation must return -EBUSY and can't implicitly disable the
-first enabled link.
-
-
-Pipelines and media streams
----------------------------
-
-When starting streaming, drivers must notify all entities in the pipeline to
-prevent link states from being modified during streaming by calling
-
- media_entity_pipeline_start(struct media_entity *entity,
- struct media_pipeline *pipe);
-
-The function will mark all entities connected to the given entity through
-enabled links, either directly or indirectly, as streaming.
-
-The media_pipeline instance pointed to by the pipe argument will be stored in
-every entity in the pipeline. Drivers should embed the media_pipeline structure
-in higher-level pipeline structures and can then access the pipeline through
-the media_entity pipe field.
-
-Calls to media_entity_pipeline_start() can be nested. The pipeline pointer must
-be identical for all nested calls to the function.
-
-media_entity_pipeline_start() may return an error. In that case, it will
-clean up any of the changes it did by itself.
-
-When stopping the stream, drivers must notify the entities with
-
- media_entity_pipeline_stop(struct media_entity *entity);
-
-If multiple calls to media_entity_pipeline_start() have been made the same
-number of media_entity_pipeline_stop() calls are required to stop streaming. The
-media_entity pipe field is reset to NULL on the last nested stop call.
-
-Link configuration will fail with -EBUSY by default if either end of the link is
-a streaming entity. Links that can be modified while streaming must be marked
-with the MEDIA_LNK_FL_DYNAMIC flag.
-
-If other operations need to be disallowed on streaming entities (such as
-changing entities configuration parameters) drivers can explicitly check the
-media_entity stream_count field to find out if an entity is streaming. This
-operation must be done with the media_device graph_mutex held.
-
-
-Link validation
----------------
-
-Link validation is performed by media_entity_pipeline_start() for any
-entity which has sink pads in the pipeline. The
-media_entity::link_validate() callback is used for that purpose. In
-link_validate() callback, entity driver should check that the properties of
-the source pad of the connected entity and its own sink pad match. It is up
-to the type of the entity (and in the end, the properties of the hardware)
-what matching actually means.
-
-Subsystems should facilitate link validation by providing subsystem specific
-helper functions to provide easy access for commonly needed information, and
-in the end provide a way to use driver-specific callbacks.
diff --git a/Documentation/memory-barriers.txt b/Documentation/memory-barriers.txt
index aef9487303d0..a61be39c7b51 100644
--- a/Documentation/memory-barriers.txt
+++ b/Documentation/memory-barriers.txt
@@ -194,7 +194,7 @@ There are some minimal guarantees that may be expected of a CPU:
(*) On any given CPU, dependent memory accesses will be issued in order, with
respect to itself. This means that for:
- WRITE_ONCE(Q, P); smp_read_barrier_depends(); D = READ_ONCE(*Q);
+ Q = READ_ONCE(P); smp_read_barrier_depends(); D = READ_ONCE(*Q);
the CPU will issue the following memory operations:
@@ -202,9 +202,9 @@ There are some minimal guarantees that may be expected of a CPU:
and always in that order. On most systems, smp_read_barrier_depends()
does nothing, but it is required for DEC Alpha. The READ_ONCE()
- and WRITE_ONCE() are required to prevent compiler mischief. Please
- note that you should normally use something like rcu_dereference()
- instead of open-coding smp_read_barrier_depends().
+ is required to prevent compiler mischief. Please note that you
+ should normally use something like rcu_dereference() instead of
+ open-coding smp_read_barrier_depends().
(*) Overlapping loads and stores within a particular CPU will appear to be
ordered within that CPU. This means that for:
@@ -1673,8 +1673,8 @@ There are some more advanced barrier functions:
(*) 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
- insert anything more than a compiler barrier in a UP compilation.
+ barrier after it. It isn't guaranteed to insert anything more than a
+ compiler barrier in a UP compilation.
(*) smp_mb__before_atomic();
diff --git a/Documentation/mtd/nand_ecc.txt b/Documentation/mtd/nand_ecc.txt
index e129b2479ea8..f8c3284bf6a7 100644
--- a/Documentation/mtd/nand_ecc.txt
+++ b/Documentation/mtd/nand_ecc.txt
@@ -107,7 +107,7 @@ for (i = 0; i < 256; i++)
if (i & 0x01)
rp1 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp1;
else
- rp0 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp1;
+ rp0 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp0;
if (i & 0x02)
rp3 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp3;
else
@@ -127,7 +127,7 @@ for (i = 0; i < 256; i++)
if (i & 0x20)
rp11 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp11;
else
- rp10 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp10;
+ rp10 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp10;
if (i & 0x40)
rp13 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ^ rp13;
else
@@ -158,7 +158,7 @@ the values in any order. So instead of calculating all the bits
individually, let us try to rearrange things.
For the column parity this is easy. We can just xor the bytes and in the
end filter out the relevant bits. This is pretty nice as it will bring
-all cp calculation out of the if loop.
+all cp calculation out of the for loop.
Similarly we can first xor the bytes for the various rows.
This leads to:
@@ -271,11 +271,11 @@ to write our code in such a way that we process data in 32 bit chunks.
Of course this means some modification as the row parity is byte by
byte. A quick analysis:
for the column parity we use the par variable. When extending to 32 bits
-we can in the end easily calculate p0 and p1 from it.
+we can in the end easily calculate rp0 and rp1 from it.
(because par now consists of 4 bytes, contributing to rp1, rp0, rp1, rp0
-respectively)
+respectively, from MSB to LSB)
also rp2 and rp3 can be easily retrieved from par as rp3 covers the
-first two bytes and rp2 the last two bytes.
+first two MSBs and rp2 covers the last two LSBs.
Note that of course now the loop is executed only 64 times (256/4).
And note that care must taken wrt byte ordering. The way bytes are
@@ -387,11 +387,11 @@ Analysis 2
The code (of course) works, and hurray: we are a little bit faster than
the linux driver code (about 15%). But wait, don't cheer too quickly.
-THere is more to be gained.
+There is more to be gained.
If we look at e.g. rp14 and rp15 we see that we either xor our data with
rp14 or with rp15. However we also have par which goes over all data.
This means there is no need to calculate rp14 as it can be calculated from
-rp15 through rp14 = par ^ rp15;
+rp15 through rp14 = par ^ rp15, because par = rp14 ^ rp15;
(or if desired we can avoid calculating rp15 and calculate it from
rp14). That is why some places refer to inverse parity.
Of course the same thing holds for rp4/5, rp6/7, rp8/9, rp10/11 and rp12/13.
@@ -419,12 +419,12 @@ with
if (i & 0x20) rp15 ^= cur;
and outside the loop added:
- rp4 = par ^ rp5;
- rp6 = par ^ rp7;
- rp8 = par ^ rp9;
- rp10 = par ^ rp11;
- rp12 = par ^ rp13;
- rp14 = par ^ rp15;
+ rp4 = par ^ rp5;
+ rp6 = par ^ rp7;
+ rp8 = par ^ rp9;
+ rp10 = par ^ rp11;
+ rp12 = par ^ rp13;
+ rp14 = par ^ rp15;
And after that the code takes about 30% more time, although the number of
statements is reduced. This is also reflected in the assembly code.
@@ -524,12 +524,12 @@ THe code within the for loop was changed to:
cur = *bp++; tmppar ^= cur; rp4 ^= cur; rp6 ^= cur;
cur = *bp++; tmppar ^= cur; rp6 ^= cur;
- cur = *bp++; tmppar ^= cur; rp4 ^= cur;
- cur = *bp++; tmppar ^= cur; rp10 ^= tmppar;
+ cur = *bp++; tmppar ^= cur; rp4 ^= cur;
+ cur = *bp++; tmppar ^= cur; rp10 ^= tmppar;
- cur = *bp++; tmppar ^= cur; rp4 ^= cur; rp6 ^= cur; rp8 ^= cur;
+ cur = *bp++; tmppar ^= cur; rp4 ^= cur; rp6 ^= cur; rp8 ^= cur;
cur = *bp++; tmppar ^= cur; rp6 ^= cur; rp8 ^= cur;
- cur = *bp++; tmppar ^= cur; rp4 ^= cur; rp8 ^= cur;
+ cur = *bp++; tmppar ^= cur; rp4 ^= cur; rp8 ^= cur;
cur = *bp++; tmppar ^= cur; rp8 ^= cur;
cur = *bp++; tmppar ^= cur; rp4 ^= cur; rp6 ^= cur;
@@ -537,7 +537,7 @@ THe code within the for loop was changed to:
cur = *bp++; tmppar ^= cur; rp4 ^= cur;
cur = *bp++; tmppar ^= cur;
- par ^= tmppar;
+ par ^= tmppar;
if ((i & 0x1) == 0) rp12 ^= tmppar;
if ((i & 0x2) == 0) rp14 ^= tmppar;
}
@@ -548,8 +548,8 @@ to rp12 and rp14.
While making the changes I also found that I could exploit that tmppar
contains the running parity for this iteration. So instead of having:
-rp4 ^= cur; rp6 = cur;
-I removed the rp6 = cur; statement and did rp6 ^= tmppar; on next
+rp4 ^= cur; rp6 ^= cur;
+I removed the rp6 ^= cur; statement and did rp6 ^= tmppar; on next
statement. A similar change was done for rp8 and rp10
@@ -593,22 +593,22 @@ The new code now looks like:
cur = *bp++; tmppar ^= cur; rp4_6 ^= cur;
cur = *bp++; tmppar ^= cur; rp6 ^= cur;
- cur = *bp++; tmppar ^= cur; rp4 ^= cur;
- cur = *bp++; tmppar ^= cur; rp10 ^= tmppar;
+ cur = *bp++; tmppar ^= cur; rp4 ^= cur;
+ cur = *bp++; tmppar ^= cur; rp10 ^= tmppar;
- notrp8 = tmppar;
- cur = *bp++; tmppar ^= cur; rp4_6 ^= cur;
+ notrp8 = tmppar;
+ cur = *bp++; tmppar ^= cur; rp4_6 ^= cur;
cur = *bp++; tmppar ^= cur; rp6 ^= cur;
- cur = *bp++; tmppar ^= cur; rp4 ^= cur;
+ cur = *bp++; tmppar ^= cur; rp4 ^= cur;
cur = *bp++; tmppar ^= cur;
- rp8 = rp8 ^ tmppar ^ notrp8;
+ rp8 = rp8 ^ tmppar ^ notrp8;
cur = *bp++; tmppar ^= cur; rp4_6 ^= cur;
cur = *bp++; tmppar ^= cur; rp6 ^= cur;
cur = *bp++; tmppar ^= cur; rp4 ^= cur;
cur = *bp++; tmppar ^= cur;
- par ^= tmppar;
+ par ^= tmppar;
if ((i & 0x1) == 0) rp12 ^= tmppar;
if ((i & 0x2) == 0) rp14 ^= tmppar;
}
@@ -700,7 +700,7 @@ Conclusion
The gain when calculating the ecc is tremendous. Om my development hardware
a speedup of a factor of 18 for ecc calculation was achieved. On a test on an
embedded system with a MIPS core a factor 7 was obtained.
-On a test with a Linksys NSLU2 (ARMv5TE processor) the speedup was a factor
+On a test with a Linksys NSLU2 (ARMv5TE processor) the speedup was a factor
5 (big endian mode, gcc 4.1.2, -O3)
For correction not much gain could be obtained (as bitflips are rare). Then
again there are also much less cycles spent there.
diff --git a/Documentation/networking/batman-adv.txt b/Documentation/networking/batman-adv.txt
index 58e49042fc20..ff23b755f5e4 100644
--- a/Documentation/networking/batman-adv.txt
+++ b/Documentation/networking/batman-adv.txt
@@ -115,14 +115,17 @@ The "bat0" interface can be used like any other regular inter-
face. It needs an IP address which can be either statically con-
figured or dynamically (by using DHCP or similar services):
-# NodeA: ifconfig bat0 192.168.0.1
-# NodeB: ifconfig bat0 192.168.0.2
+# NodeA: ip link set up dev bat0
+# NodeA: ip addr add 192.168.0.1/24 dev bat0
+
+# NodeB: ip link set up dev bat0
+# NodeB: ip addr add 192.168.0.2/24 dev bat0
# NodeB: ping 192.168.0.1
Note: In order to avoid problems remove all IP addresses previ-
ously assigned to interfaces now used by batman advanced, e.g.
-# ifconfig eth0 0.0.0.0
+# ip addr flush dev eth0
LOGGING/DEBUGGING
diff --git a/Documentation/networking/e100.txt b/Documentation/networking/e100.txt
index f862cf3aff34..42ddbd4b52a9 100644
--- a/Documentation/networking/e100.txt
+++ b/Documentation/networking/e100.txt
@@ -181,17 +181,3 @@ For general information, go to the Intel support website at:
If an issue is identified with the released source code on the supported
kernel with a supported adapter, email the specific information related to the
issue to e1000-devel@lists.sourceforge.net.
-
-
-License
-=======
-
-This software program is released under the terms of a license agreement
-between you ('Licensee') and Intel. Do not use or load this software or any
-associated materials (collectively, the 'Software') until you have carefully
-read the full terms and conditions of the file COPYING located in this software
-package. By loading or using the Software, you agree to the terms of this
-Agreement. If you do not agree with the terms of this Agreement, do not install
-or use the Software.
-
-* Other names and brands may be claimed as the property of others.
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index 2ea4c45cf1c8..ceb44a095a27 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -335,6 +335,14 @@ tcp_keepalive_intvl - INTEGER
after probes started. Default value: 75sec i.e. connection
will be aborted after ~11 minutes of retries.
+tcp_l3mdev_accept - BOOLEAN
+ Enables child sockets to inherit the L3 master device index.
+ Enabling this option allows a "global" listen socket to work
+ across L3 master domains (e.g., VRFs) with connected sockets
+ derived from the listen socket to be bound to the L3 domain in
+ which the packets originated. Only valid when the kernel was
+ compiled with CONFIG_NET_L3_MASTER_DEV.
+
tcp_low_latency - BOOLEAN
If set, the TCP stack makes decisions that prefer lower
latency as opposed to higher throughput. By default, this
@@ -1723,6 +1731,25 @@ addip_enable - BOOLEAN
Default: 0
+pf_enable - INTEGER
+ Enable or disable pf (pf is short for potentially failed) state. A value
+ of pf_retrans > path_max_retrans also disables pf state. That is, one of
+ both pf_enable and pf_retrans > path_max_retrans can disable pf state.
+ Since pf_retrans and path_max_retrans can be changed by userspace
+ application, sometimes user expects to disable pf state by the value of
+ pf_retrans > path_max_retrans, but occasionally the value of pf_retrans
+ or path_max_retrans is changed by the user application, this pf state is
+ enabled. As such, it is necessary to add this to dynamically enable
+ and disable pf state. See:
+ https://datatracker.ietf.org/doc/draft-ietf-tsvwg-sctp-failover for
+ details.
+
+ 1: Enable pf.
+
+ 0: Disable pf.
+
+ Default: 1
+
addip_noauth_enable - BOOLEAN
Dynamic Address Reconfiguration (ADD-IP) requires the use of
authentication to protect the operations of adding or removing new
@@ -1799,7 +1826,9 @@ pf_retrans - INTEGER
having to reduce path_max_retrans to a very low value. See:
http://www.ietf.org/id/draft-nishida-tsvwg-sctp-failover-05.txt
for details. Note also that a value of pf_retrans > path_max_retrans
- disables this feature
+ disables this feature. Since both pf_retrans and path_max_retrans can
+ be changed by userspace application, a variable pf_enable is used to
+ disable pf state.
Default: 0
diff --git a/Documentation/networking/switchdev.txt b/Documentation/networking/switchdev.txt
index 91994134efca..fad63136ee3e 100644
--- a/Documentation/networking/switchdev.txt
+++ b/Documentation/networking/switchdev.txt
@@ -304,8 +304,12 @@ certain netdevs from flooding unicast traffic for which there is no FDB entry.
IGMP Snooping
^^^^^^^^^^^^^
-XXX: complete this section
-
+In order to support IGMP snooping, the port netdevs should trap to the bridge
+driver all IGMP join and leave messages.
+The bridge multicast module will notify port netdevs on every multicast group
+changed whether it is static configured or dynamically joined/leave.
+The hardware implementation should be forwarding all registered multicast
+traffic groups only to the configured ports.
L3 Routing Offload
------------------
diff --git a/Documentation/power/pci.txt b/Documentation/power/pci.txt
index b0e911e0e8f5..44558882aa60 100644
--- a/Documentation/power/pci.txt
+++ b/Documentation/power/pci.txt
@@ -999,7 +999,7 @@ from its probe routine to make runtime PM work for the device.
It is important to remember that the driver's runtime_suspend() callback
may be executed right after the usage counter has been decremented, because
-user space may already have cuased the pm_runtime_allow() helper function
+user space may already have caused the pm_runtime_allow() helper function
unblocking the runtime PM of the device to run via sysfs, so the driver must
be prepared to cope with that.
diff --git a/Documentation/power/runtime_pm.txt b/Documentation/power/runtime_pm.txt
index 0784bc3a2ab5..7328cf85236c 100644
--- a/Documentation/power/runtime_pm.txt
+++ b/Documentation/power/runtime_pm.txt
@@ -371,6 +371,12 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h:
- increment the device's usage counter, run pm_runtime_resume(dev) and
return its result
+ int pm_runtime_get_if_in_use(struct device *dev);
+ - return -EINVAL if 'power.disable_depth' is nonzero; otherwise, if the
+ runtime PM status is RPM_ACTIVE and the runtime PM usage counter is
+ nonzero, increment the counter and return 1; otherwise return 0 without
+ changing the counter
+
void pm_runtime_put_noidle(struct device *dev);
- decrement the device's usage counter
diff --git a/Documentation/printk-formats.txt b/Documentation/printk-formats.txt
index b784c270105f..6389551bbad6 100644
--- a/Documentation/printk-formats.txt
+++ b/Documentation/printk-formats.txt
@@ -250,6 +250,12 @@ dentry names:
Passed by reference.
+block_device names:
+
+ %pg sda, sda1 or loop0p1
+
+ For printing name of block_device pointers.
+
struct va_format:
%pV
diff --git a/Documentation/s390/zfcpdump.txt b/Documentation/s390/zfcpdump.txt
index dc929be96016..b064aa59714d 100644
--- a/Documentation/s390/zfcpdump.txt
+++ b/Documentation/s390/zfcpdump.txt
@@ -15,19 +15,15 @@ the s390-tools package) to make the device bootable. The operator of a Linux
system can then trigger a SCSI dump by booting the SCSI disk, where zfcpdump
resides on.
-The kernel part of zfcpdump is implemented as a debugfs file under "zcore/mem",
-which exports memory and registers of the crashed Linux in an s390
-standalone dump format. It can be used in the same way as e.g. /dev/mem. The
-dump format defines a 4K header followed by plain uncompressed memory. The
-register sets are stored in the prefix pages of the respective CPUs. To build a
-dump enabled kernel with the zcore driver, the kernel config option
-CONFIG_CRASH_DUMP has to be set. When reading from "zcore/mem", the part of
-memory, which has been saved by hardware is read by the driver via the SCLP
-hardware interface. The second part is just copied from the non overwritten real
-memory.
-
-Since kernel version 3.12 also the /proc/vmcore file can also be used to access
-the dump.
+The user space dump tool accesses the memory of the crashed system by means
+of the /proc/vmcore interface. This interface exports the crashed system's
+memory and registers in ELF core dump format. To access the memory which has
+been saved by the hardware SCLP requests will be created at the time the data
+is needed by /proc/vmcore. The tail part of the crashed systems memory which
+has not been stashed by hardware can just be copied from real memory.
+
+To build a dump enabled kernel the kernel config option CONFIG_CRASH_DUMP
+has to be set.
To get a valid zfcpdump kernel configuration use "make zfcpdump_defconfig".
diff --git a/Documentation/spi/00-INDEX b/Documentation/spi/00-INDEX
index a128fa835512..4644bf0d9832 100644
--- a/Documentation/spi/00-INDEX
+++ b/Documentation/spi/00-INDEX
@@ -10,13 +10,9 @@ pxa2xx
- PXA2xx SPI master controller build by spi_message fifo wq
spidev
- Intro to the userspace API for spi devices
-spidev_fdx.c
- - spidev example file
spi-lm70llp
- Connecting an LM70-LLP sensor to the kernel via the SPI subsys.
spi-sc18is602
- NXP SC18IS602/603 I2C-bus to SPI bridge
spi-summary
- (Linux) SPI overview. If unsure about SPI or SPI in Linux, start here.
-spidev_test.c
- - SPI testing utility.
diff --git a/Documentation/spi/Makefile b/Documentation/spi/Makefile
deleted file mode 100644
index efa255813e9d..000000000000
--- a/Documentation/spi/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-# List of programs to build
-hostprogs-y := spidev_test spidev_fdx
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS_spidev_test.o += -I$(objtree)/usr/include
-HOSTCFLAGS_spidev_fdx.o += -I$(objtree)/usr/include
diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt
index af70d1541d3a..73c6b1ef0e84 100644
--- a/Documentation/sysctl/kernel.txt
+++ b/Documentation/sysctl/kernel.txt
@@ -551,6 +551,21 @@ the recommended setting is 60.
==============================================================
+panic_on_io_nmi:
+
+Controls the kernel's behavior when a CPU receives an NMI caused by
+an IO error.
+
+0: try to continue operation (default)
+
+1: panic immediately. The IO error triggered an NMI. This indicates a
+ serious system condition which could result in IO data corruption.
+ Rather than continuing, panicking might be a better choice. Some
+ servers issue this sort of NMI when the dump button is pushed,
+ and you can use this option to take a crash dump.
+
+==============================================================
+
panic_on_oops:
Controls the kernel's behaviour when an oops or BUG is encountered.
diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt
index f72370b440b1..89a887c76629 100644
--- a/Documentation/sysctl/vm.txt
+++ b/Documentation/sysctl/vm.txt
@@ -42,6 +42,8 @@ Currently, these files are in /proc/sys/vm:
- min_slab_ratio
- min_unmapped_ratio
- mmap_min_addr
+- mmap_rnd_bits
+- mmap_rnd_compat_bits
- nr_hugepages
- nr_overcommit_hugepages
- nr_trim_pages (only if CONFIG_MMU=n)
@@ -135,7 +137,7 @@ Contains, as a percentage of total available memory that contains free pages
and reclaimable pages, the number of pages at which the background kernel
flusher threads will start writing out dirty data.
-The total avaiable memory is not equal to total system memory.
+The total available memory is not equal to total system memory.
==============================================================
@@ -170,7 +172,7 @@ Contains, as a percentage of total available memory that contains free pages
and reclaimable pages, the number of pages at which a process which is
generating disk writes will itself start writing out dirty data.
-The total avaiable memory is not equal to total system memory.
+The total available memory is not equal to total system memory.
==============================================================
@@ -485,6 +487,33 @@ against future potential kernel bugs.
==============================================================
+mmap_rnd_bits:
+
+This value can be used to select the number of bits to use to
+determine the random offset to the base address of vma regions
+resulting from mmap allocations on architectures which support
+tuning address space randomization. This value will be bounded
+by the architecture's minimum and maximum supported values.
+
+This value can be changed after boot using the
+/proc/sys/vm/mmap_rnd_bits tunable
+
+==============================================================
+
+mmap_rnd_compat_bits:
+
+This value can be used to select the number of bits to use to
+determine the random offset to the base address of vma regions
+resulting from mmap allocations for applications run in
+compatibility mode on architectures which support tuning address
+space randomization. This value will be bounded by the
+architecture's minimum and maximum supported values.
+
+This value can be changed after boot using the
+/proc/sys/vm/mmap_rnd_compat_bits tunable
+
+==============================================================
+
nr_hugepages
Change the minimum size of the hugepage pool.
diff --git a/Documentation/trace/events-msr.txt b/Documentation/trace/events-msr.txt
new file mode 100644
index 000000000000..78c383bf06aa
--- /dev/null
+++ b/Documentation/trace/events-msr.txt
@@ -0,0 +1,37 @@
+
+The x86 kernel supports tracing most MSR (Model Specific Register) accesses.
+To see the definition of the MSRs on Intel systems please see the SDM
+at http://www.intel.com/sdm (Volume 3)
+
+Available trace points:
+
+/sys/kernel/debug/tracing/events/msr/
+
+Trace MSR reads
+
+read_msr
+
+msr: MSR number
+val: Value written
+failed: 1 if the access failed, otherwise 0
+
+
+Trace MSR writes
+
+write_msr
+
+msr: MSR number
+val: Value written
+failed: 1 if the access failed, otherwise 0
+
+
+Trace RDPMC in kernel
+
+rdpmc
+
+The trace data can be post processed with the postprocess/decode_msr.py script
+
+cat /sys/kernel/debug/tracing/trace | decode_msr.py /usr/src/linux/include/asm/msr-index.h
+
+to add symbolic MSR names.
+
diff --git a/Documentation/trace/postprocess/decode_msr.py b/Documentation/trace/postprocess/decode_msr.py
new file mode 100644
index 000000000000..0ab40e0db580
--- /dev/null
+++ b/Documentation/trace/postprocess/decode_msr.py
@@ -0,0 +1,37 @@
+#!/usr/bin/python
+# add symbolic names to read_msr / write_msr in trace
+# decode_msr msr-index.h < trace
+import sys
+import re
+
+msrs = dict()
+
+with open(sys.argv[1] if len(sys.argv) > 1 else "msr-index.h", "r") as f:
+ for j in f:
+ m = re.match(r'#define (MSR_\w+)\s+(0x[0-9a-fA-F]+)', j)
+ if m:
+ msrs[int(m.group(2), 16)] = m.group(1)
+
+extra_ranges = (
+ ( "MSR_LASTBRANCH_%d_FROM_IP", 0x680, 0x69F ),
+ ( "MSR_LASTBRANCH_%d_TO_IP", 0x6C0, 0x6DF ),
+ ( "LBR_INFO_%d", 0xdc0, 0xddf ),
+)
+
+for j in sys.stdin:
+ m = re.search(r'(read|write)_msr:\s+([0-9a-f]+)', j)
+ if m:
+ r = None
+ num = int(m.group(2), 16)
+ if num in msrs:
+ r = msrs[num]
+ else:
+ for er in extra_ranges:
+ if er[1] <= num <= er[2]:
+ r = er[0] % (num - er[1],)
+ break
+ if r:
+ j = j.replace(" " + m.group(2), " " + r + "(" + m.group(2) + ")")
+ print j,
+
+
diff --git a/Documentation/usb/chipidea.txt b/Documentation/usb/chipidea.txt
index 3f848c1f2940..05f735a1b5a5 100644
--- a/Documentation/usb/chipidea.txt
+++ b/Documentation/usb/chipidea.txt
@@ -7,8 +7,8 @@ with 2 Freescale i.MX6Q sabre SD boards.
---------------------------------------
Select CONFIG_USB_OTG_FSM, rebuild kernel Image and modules.
If you want to check some internal variables for otg fsm,
-select CONFIG_USB_CHIPIDEA_DEBUG, there are 2 files which
-can show otg fsm variables and some controller registers value:
+mount debugfs, there are 2 files which can show otg fsm
+variables and some controller registers value:
cat /sys/kernel/debug/ci_hdrc.0/otg
cat /sys/kernel/debug/ci_hdrc.0/registers
diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt
index b24d3ef89166..581960574889 100644
--- a/Documentation/usb/gadget-testing.txt
+++ b/Documentation/usb/gadget-testing.txt
@@ -434,7 +434,7 @@ On host: serialc -v <vendorID> -p <productID> -i<interface#> -a1 -s1024 \
where seriald and serialc are Felipe's utilities found here:
-https://git.gitorious.org/usb/usb-tools.git master
+https://github.com/felipebalbi/usb-tools.git master
12. PHONET function
===================
@@ -579,6 +579,8 @@ The SOURCESINK function provides these attributes in its function directory:
isoc_mult - 0..2 (hs/ss only)
isoc_maxburst - 0..15 (ss only)
bulk_buflen - buffer length
+ bulk_qlen - depth of queue for bulk
+ iso_qlen - depth of queue for iso
Testing the SOURCESINK function
-------------------------------
diff --git a/Documentation/usb/power-management.txt b/Documentation/usb/power-management.txt
index 4a15c90bc11d..0a94ffe17ab6 100644
--- a/Documentation/usb/power-management.txt
+++ b/Documentation/usb/power-management.txt
@@ -537,17 +537,18 @@ relevant attribute files are usb2_hardware_lpm and usb3_hardware_lpm.
can write y/Y/1 or n/N/0 to the file to enable/disable
USB2 hardware LPM manually. This is for test purpose mainly.
- power/usb3_hardware_lpm
+ power/usb3_hardware_lpm_u1
+ power/usb3_hardware_lpm_u2
When a USB 3.0 lpm-capable device is plugged in to a
xHCI host which supports link PM, it will check if U1
and U2 exit latencies have been set in the BOS
descriptor; if the check is is passed and the host
supports USB3 hardware LPM, USB3 hardware LPM will be
- enabled for the device and this file will be created.
- The file holds a string value (enable or disable)
- indicating whether or not USB3 hardware LPM is
- enabled for the device.
+ enabled for the device and these files will be created.
+ The files hold a string value (enable or disable)
+ indicating whether or not USB3 hardware LPM U1 or U2
+ is enabled for the device.
USB Port Power Control
----------------------
diff --git a/Documentation/video4linux/API.html b/Documentation/video4linux/API.html
index 256f8efa992c..eaf948cf1ae7 100644
--- a/Documentation/video4linux/API.html
+++ b/Documentation/video4linux/API.html
@@ -9,7 +9,7 @@
<table border="0">
<tr>
<td>
- <a href="http://linuxtv.org/downloads/legacy/video4linux/API/V4L1_API.html">V4L original API</a>
+ <a href="https://linuxtv.org/downloads/legacy/video4linux/API/V4L1_API.html">V4L original API</a>
</td>
<td>
Obsoleted by V4L2 API
diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx
index 9e57ce43c4f4..67209998a439 100644
--- a/Documentation/video4linux/CARDLIST.em28xx
+++ b/Documentation/video4linux/CARDLIST.em28xx
@@ -41,8 +41,8 @@
40 -> Plextor ConvertX PX-TV100U (em2861) [093b:a005]
41 -> Kworld 350 U DVB-T (em2870) [eb1a:e350]
42 -> Kworld 355 U DVB-T (em2870) [eb1a:e355,eb1a:e357,eb1a:e359]
- 43 -> Terratec Cinergy T XS (em2870) [0ccd:0043]
- 44 -> Terratec Cinergy T XS (MT2060) (em2870)
+ 43 -> Terratec Cinergy T XS (em2870)
+ 44 -> Terratec Cinergy T XS (MT2060) (em2870) [0ccd:0043]
45 -> Pinnacle PCTV DVB-T (em2870)
46 -> Compro, VideoMate U3 (em2870) [185b:2870]
47 -> KWorld DVB-T 305U (em2880) [eb1a:e305]
diff --git a/Documentation/video4linux/fimc.txt b/Documentation/video4linux/fimc.txt
index e0c6b8bc4743..4fab231be52e 100644
--- a/Documentation/video4linux/fimc.txt
+++ b/Documentation/video4linux/fimc.txt
@@ -58,7 +58,7 @@ Not currently supported:
4.1. Media device interface
The driver supports Media Controller API as defined at
-http://linuxtv.org/downloads/v4l-dvb-apis/media_common.html
+https://linuxtv.org/downloads/v4l-dvb-apis/media_common.html
The media device driver name is "SAMSUNG S5P FIMC".
The purpose of this interface is to allow changing assignment of FIMC instances
@@ -83,11 +83,11 @@ undefined behaviour.
4.3. Capture video node
The driver supports V4L2 Video Capture Interface as defined at:
-http://linuxtv.org/downloads/v4l-dvb-apis/devices.html
+https://linuxtv.org/downloads/v4l-dvb-apis/devices.html
At the capture and mem-to-mem video nodes only the multi-planar API is
supported. For more details see:
-http://linuxtv.org/downloads/v4l-dvb-apis/planar-apis.html
+https://linuxtv.org/downloads/v4l-dvb-apis/planar-apis.html
4.4. Camera capture subdevs
diff --git a/Documentation/video4linux/omap4_camera.txt b/Documentation/video4linux/omap4_camera.txt
index 25d9b40a4651..a6734aa77242 100644
--- a/Documentation/video4linux/omap4_camera.txt
+++ b/Documentation/video4linux/omap4_camera.txt
@@ -47,7 +47,7 @@ Tested platforms
File list
---------
drivers/staging/media/omap4iss/
-include/media/omap4iss.h
+include/linux/platform_data/media/omap4iss.h
References
----------
diff --git a/Documentation/video4linux/si4713.txt b/Documentation/video4linux/si4713.txt
index 2e7392a4fee1..2ddc6b095a76 100644
--- a/Documentation/video4linux/si4713.txt
+++ b/Documentation/video4linux/si4713.txt
@@ -157,7 +157,7 @@ int main (int argc, char *argv[])
}
The struct si4713_rnl and SI4713_IOC_MEASURE_RNL are defined under
-include/media/si4713.h.
+include/linux/platform_data/media/si4713.h.
Stereo/Mono and RDS subchannels
===============================
diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt
index 75d5c18d689a..fa41608ab2b4 100644
--- a/Documentation/video4linux/v4l2-framework.txt
+++ b/Documentation/video4linux/v4l2-framework.txt
@@ -295,16 +295,16 @@ module owner. This is done for you if you use the i2c helper functions.
If integration with the media framework is needed, you must initialize the
media_entity struct embedded in the v4l2_subdev struct (entity field) by
-calling media_entity_init():
+calling media_entity_pads_init(), if the entity has pads:
struct media_pad *pads = &my_sd->pads;
int err;
- err = media_entity_init(&sd->entity, npads, pads, 0);
+ err = media_entity_pads_init(&sd->entity, npads, pads);
The pads array must have been previously initialized. There is no need to
-manually set the struct media_entity type and name fields, but the revision
-field must be initialized if needed.
+manually set the struct media_entity function and name fields, but the
+revision field must be initialized if needed.
A reference to the entity will be automatically acquired/released when the
subdev device node (if any) is opened/closed.
@@ -695,12 +695,12 @@ difference is that the inode argument is omitted since it is never used.
If integration with the media framework is needed, you must initialize the
media_entity struct embedded in the video_device struct (entity field) by
-calling media_entity_init():
+calling media_entity_pads_init():
struct media_pad *pad = &my_vdev->pad;
int err;
- err = media_entity_init(&vdev->entity, 1, pad, 0);
+ err = media_entity_pads_init(&vdev->entity, 1, pad);
The pads array must have been previously initialized. There is no need to
manually set the struct media_entity type and name fields.
diff --git a/Documentation/video4linux/v4l2-pci-skeleton.c b/Documentation/video4linux/v4l2-pci-skeleton.c
index 95ae82860092..79af0c041056 100644
--- a/Documentation/video4linux/v4l2-pci-skeleton.c
+++ b/Documentation/video4linux/v4l2-pci-skeleton.c
@@ -163,11 +163,10 @@ static irqreturn_t skeleton_irq(int irq, void *dev_id)
* minimum number: many DMA engines need a minimum of 2 buffers in the
* queue and you need to have another available for userspace processing.
*/
-static int queue_setup(struct vb2_queue *vq, const void *parg,
+static int queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct skeleton *skel = vb2_get_drv_priv(vq);
skel->field = skel->format.field;
@@ -183,12 +182,12 @@ static int queue_setup(struct vb2_queue *vq, const void *parg,
if (vq->num_buffers + *nbuffers < 3)
*nbuffers = 3 - vq->num_buffers;
+ alloc_ctxs[0] = skel->alloc_ctx;
- if (fmt && fmt->fmt.pix.sizeimage < skel->format.sizeimage)
- return -EINVAL;
+ if (*nplanes)
+ return sizes[0] < skel->format.sizeimage ? -EINVAL : 0;
*nplanes = 1;
- sizes[0] = fmt ? fmt->fmt.pix.sizeimage : skel->format.sizeimage;
- alloc_ctxs[0] = skel->alloc_ctx;
+ sizes[0] = skel->format.sizeimage;
return 0;
}
@@ -509,7 +508,7 @@ static int skeleton_s_dv_timings(struct file *file, void *_fh,
return -EINVAL;
/* Return 0 if the new timings are the same as the current timings. */
- if (v4l2_match_dv_timings(timings, &skel->timings, 0))
+ if (v4l2_match_dv_timings(timings, &skel->timings, 0, false))
return 0;
/*
diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index 092ee9fbaf2b..053f613fc9a9 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -1451,6 +1451,7 @@ struct kvm_irq_routing_entry {
struct kvm_irq_routing_irqchip irqchip;
struct kvm_irq_routing_msi msi;
struct kvm_irq_routing_s390_adapter adapter;
+ struct kvm_irq_routing_hv_sint hv_sint;
__u32 pad[8];
} u;
};
@@ -1459,6 +1460,7 @@ struct kvm_irq_routing_entry {
#define KVM_IRQ_ROUTING_IRQCHIP 1
#define KVM_IRQ_ROUTING_MSI 2
#define KVM_IRQ_ROUTING_S390_ADAPTER 3
+#define KVM_IRQ_ROUTING_HV_SINT 4
No flags are specified so far, the corresponding field must be set to zero.
@@ -1482,6 +1484,10 @@ struct kvm_irq_routing_s390_adapter {
__u32 adapter_id;
};
+struct kvm_irq_routing_hv_sint {
+ __u32 vcpu;
+ __u32 sint;
+};
4.53 KVM_ASSIGN_SET_MSIX_NR (deprecated)
@@ -3331,6 +3337,28 @@ the userspace IOAPIC should process the EOI and retrigger the interrupt if
it is still asserted. Vector is the LAPIC interrupt vector for which the
EOI was received.
+ struct kvm_hyperv_exit {
+#define KVM_EXIT_HYPERV_SYNIC 1
+ __u32 type;
+ union {
+ struct {
+ __u32 msr;
+ __u64 control;
+ __u64 evt_page;
+ __u64 msg_page;
+ } synic;
+ } u;
+ };
+ /* KVM_EXIT_HYPERV */
+ struct kvm_hyperv_exit hyperv;
+Indicates that the VCPU exits into userspace to process some tasks
+related to Hyper-V emulation.
+Valid values for 'type' are:
+ KVM_EXIT_HYPERV_SYNIC -- synchronously notify user-space about
+Hyper-V SynIC state change. Notification is used to remap SynIC
+event/message pages and to enable/disable SynIC messages/events processing
+in userspace.
+
/* Fix the size of the union. */
char padding[256];
};
@@ -3685,3 +3713,16 @@ available, means that that the kernel has an implementation of the
H_RANDOM hypercall backed by a hardware random-number generator.
If present, the kernel H_RANDOM handler can be enabled for guest use
with the KVM_CAP_PPC_ENABLE_HCALL capability.
+
+8.2 KVM_CAP_HYPERV_SYNIC
+
+Architectures: x86
+This capability, if KVM_CHECK_EXTENSION indicates that it is
+available, means that that the kernel has an implementation of the
+Hyper-V Synthetic interrupt controller(SynIC). Hyper-V SynIC is
+used to support Windows Hyper-V based guest paravirt drivers(VMBus).
+
+In order to use SynIC, it has to be activated by setting this
+capability via KVM_ENABLE_CAP ioctl on the vcpu fd. Note that this
+will disable the use of APIC hardware virtualization even if supported
+by the CPU, as it's incompatible with SynIC auto-EOI behavior.
diff --git a/Documentation/virtual/kvm/devices/vm.txt b/Documentation/virtual/kvm/devices/vm.txt
index 2d09d1ed86d0..f083a168eb35 100644
--- a/Documentation/virtual/kvm/devices/vm.txt
+++ b/Documentation/virtual/kvm/devices/vm.txt
@@ -37,7 +37,8 @@ Returns: -EFAULT if the given address is not accessible
Allows userspace to query the actual limit and set a new limit for
the maximum guest memory size. The limit will be rounded up to
2048 MB, 4096 GB, 8192 TB respectively, as this limit is governed by
-the number of page table levels.
+the number of page table levels. In the case that there is no limit we will set
+the limit to KVM_S390_NO_MEM_LIMIT (U64_MAX).
2. GROUP: KVM_S390_VM_CPU_MODEL
Architectures: s390
diff --git a/Documentation/virtual/kvm/mmu.txt b/Documentation/virtual/kvm/mmu.txt
index 3a4d681c3e98..daf9c0f742d2 100644
--- a/Documentation/virtual/kvm/mmu.txt
+++ b/Documentation/virtual/kvm/mmu.txt
@@ -203,10 +203,10 @@ Shadow pages contain the following information:
page cannot be destroyed. See role.invalid.
parent_ptes:
The reverse mapping for the pte/ptes pointing at this page's spt. If
- parent_ptes bit 0 is zero, only one spte points at this pages and
+ parent_ptes bit 0 is zero, only one spte points at this page and
parent_ptes points at this single spte, otherwise, there exists multiple
sptes pointing at this page and (parent_ptes & ~0x1) points at a data
- structure with a list of parent_ptes.
+ structure with a list of parent sptes.
unsync:
If true, then the translations in this page may not match the guest's
translation. This is equivalent to the state of the tlb when a pte is
diff --git a/Documentation/zh_CN/video4linux/v4l2-framework.txt b/Documentation/zh_CN/video4linux/v4l2-framework.txt
index 2b828e631e31..698660b7f21f 100644
--- a/Documentation/zh_CN/video4linux/v4l2-framework.txt
+++ b/Documentation/zh_CN/video4linux/v4l2-framework.txt
@@ -289,13 +289,13 @@ struct v4l2_subdev_ops {
然åŽï¼Œä½ å¿…须用一个唯一的åå­—åˆå§‹åŒ– subdev->name,并åˆå§‹åŒ–模å—çš„
owner 域。若使用 i2c 辅助函数,这些都会帮你处ç†å¥½ã€‚
-若需åŒåª’体框架整åˆï¼Œä½ å¿…须调用 media_entity_init() åˆå§‹åŒ– v4l2_subdev
+若需åŒåª’体框架整åˆï¼Œä½ å¿…须调用 media_entity_pads_init() åˆå§‹åŒ– v4l2_subdev
结构体中的 media_entity 结构体(entity 域):
struct media_pad *pads = &my_sd->pads;
int err;
- err = media_entity_init(&sd->entity, npads, pads, 0);
+ err = media_entity_pads_init(&sd->entity, npads, pads);
pads 数组必须预先åˆå§‹åŒ–。无须手动设置 media_entity çš„ type å’Œ
name 域,但如有必è¦ï¼Œrevision 域必须åˆå§‹åŒ–。
@@ -596,13 +596,13 @@ void v4l2_disable_ioctl(struct video_device *vdev, unsigned int cmd);
v4l2_file_operations 结构体是 file_operations 的一个å­é›†ã€‚其主è¦
区别在于:因 inode å‚数从未被使用,它将被忽略。
-如果需è¦ä¸Žåª’体框架整åˆï¼Œä½ å¿…须通过调用 media_entity_init() åˆå§‹åŒ–
+如果需è¦ä¸Žåª’体框架整åˆï¼Œä½ å¿…须通过调用 media_entity_pads_init() åˆå§‹åŒ–
嵌入在 video_device 结构体中的 media_entity(entity 域)结构体:
struct media_pad *pad = &my_vdev->pad;
int err;
- err = media_entity_init(&vdev->entity, 1, pad, 0);
+ err = media_entity_pads_init(&vdev->entity, 1, pad);
pads 数组必须预先åˆå§‹åŒ–。没有必è¦æ‰‹åŠ¨è®¾ç½® media_entity çš„ type å’Œ
name 域。
diff --git a/MAINTAINERS b/MAINTAINERS
index 124dd3795475..04d62b1e8b17 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -206,7 +206,7 @@ F: include/trace/events/9p.h
A8293 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -318,7 +318,7 @@ M: Zhang Rui <rui.zhang@intel.com>
L: linux-acpi@vger.kernel.org
W: https://01.org/linux-acpi
S: Supported
-F: drivers/acpi/video.c
+F: drivers/acpi/acpi_video.c
ACPI WMI DRIVER
L: platform-driver-x86@vger.kernel.org
@@ -371,6 +371,15 @@ ADDRESS SPACE LAYOUT RANDOMIZATION (ASLR)
M: Jiri Kosina <jikos@kernel.org>
S: Maintained
+ADF7242 IEEE 802.15.4 RADIO DRIVER
+M: Michael Hennerich <michael.hennerich@analog.com>
+W: https://wiki.analog.com/ADF7242
+W: http://ez.analog.com/community/linux-device-drivers
+L: linux-wpan@vger.kernel.org
+S: Supported
+F: drivers/net/ieee802154/adf7242.c
+F: Documentation/devicetree/bindings/net/ieee802154/adf7242.txt
+
ADM1025 HARDWARE MONITOR DRIVER
M: Jean Delvare <jdelvare@suse.com>
L: lm-sensors@lm-sensors.org
@@ -388,14 +397,14 @@ ADM8211 WIRELESS DRIVER
L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/
S: Orphan
-F: drivers/net/wireless/adm8211.*
+F: drivers/net/wireless/admtek/adm8211.*
ADP1653 FLASH CONTROLLER DRIVER
M: Sakari Ailus <sakari.ailus@iki.fi>
L: linux-media@vger.kernel.org
S: Maintained
F: drivers/media/i2c/adp1653.c
-F: include/media/adp1653.h
+F: include/media/i2c/adp1653.h
ADP5520 BACKLIGHT DRIVER WITH IO EXPANDER (ADP5520/ADP5501)
M: Michael Hennerich <michael.hennerich@analog.com>
@@ -466,7 +475,7 @@ F: sound/oss/aedsp16.c
AF9013 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -476,7 +485,7 @@ F: drivers/media/dvb-frontends/af9013*
AF9033 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -522,7 +531,7 @@ AIMSLAB FM RADIO RECEIVER DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Maintained
F: drivers/media/radio/radio-aimslab*
@@ -536,7 +545,7 @@ F: include/linux/*aio*.h
AIRSPY MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -614,9 +623,9 @@ F: drivers/crypto/ccp/
F: include/linux/ccp.h
AMD FAM15H PROCESSOR POWER MONITORING DRIVER
-M: Andreas Herrmann <herrmann.der.user@googlemail.com>
+M: Huang Rui <ray.huang@amd.com>
L: lm-sensors@lm-sensors.org
-S: Maintained
+S: Supported
F: Documentation/hwmon/fam15h_power
F: drivers/hwmon/fam15h_power.c
@@ -1773,7 +1782,7 @@ L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
S: Maintained
F: drivers/media/i2c/as3645a.c
-F: include/media/as3645a.h
+F: include/media/i2c/as3645a.h
ASC7621 HARDWARE MONITOR DRIVER
M: George Joseph <george.joseph@fairview5.com>
@@ -1847,7 +1856,7 @@ S: Supported
F: drivers/net/wireless/ath/ath6kl/
WILOCITY WIL6210 WIRELESS DRIVER
-M: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
+M: Maya Erez <qca_merez@qca.qualcomm.com>
L: linux-wireless@vger.kernel.org
L: wil6210@qca.qualcomm.com
S: Supported
@@ -1896,7 +1905,6 @@ ATMEL AT91 / AT32 MCI DRIVER
M: Ludovic Desroches <ludovic.desroches@atmel.com>
S: Maintained
F: drivers/mmc/host/atmel-mci.c
-F: drivers/mmc/host/atmel-mci-regs.h
ATMEL AT91 / AT32 SERIAL DRIVER
M: Nicolas Ferre <nicolas.ferre@atmel.com>
@@ -1931,7 +1939,7 @@ S: Supported
F: drivers/i2c/busses/i2c-at91.c
ATMEL ISI DRIVER
-M: Josh Wu <josh.wu@atmel.com>
+M: Ludovic Desroches <ludovic.desroches@atmel.com>
L: linux-media@vger.kernel.org
S: Supported
F: drivers/media/platform/soc_camera/atmel-isi.c
@@ -1950,7 +1958,8 @@ S: Supported
F: drivers/net/ethernet/cadence/
ATMEL NAND DRIVER
-M: Josh Wu <josh.wu@atmel.com>
+M: Wenyou Yang <wenyou.yang@atmel.com>
+M: Josh Wu <rainyfeeling@outlook.com>
L: linux-mtd@lists.infradead.org
S: Supported
F: drivers/mtd/nand/atmel_nand*
@@ -1992,7 +2001,7 @@ L: linux-wireless@vger.kernel.org
W: http://www.thekelleys.org.uk/atmel
W: http://atmelwlandriver.sourceforge.net/
S: Maintained
-F: drivers/net/wireless/atmel*
+F: drivers/net/wireless/atmel/atmel*
ATMEL MAXTOUCH DRIVER
M: Nick Dyer <nick.dyer@itdev.co.uk>
@@ -2063,7 +2072,7 @@ F: net/ax25/
AZ6007 DVB DRIVER
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org
+W: https://linuxtv.org
T: git git://linuxtv.org/media_tree.git
S: Maintained
F: drivers/media/usb/dvb-usb-v2/az6007.c
@@ -2072,7 +2081,7 @@ AZTECH FM RADIO RECEIVER DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Maintained
F: drivers/media/radio/radio-aztech*
@@ -2081,7 +2090,7 @@ L: linux-wireless@vger.kernel.org
L: b43-dev@lists.infradead.org
W: http://wireless.kernel.org/en/users/Drivers/b43
S: Odd Fixes
-F: drivers/net/wireless/b43/
+F: drivers/net/wireless/broadcom/b43/
B43LEGACY WIRELESS DRIVER
M: Larry Finger <Larry.Finger@lwfinger.net>
@@ -2089,7 +2098,7 @@ L: linux-wireless@vger.kernel.org
L: b43-dev@lists.infradead.org
W: http://wireless.kernel.org/en/users/Drivers/b43
S: Maintained
-F: drivers/net/wireless/b43legacy/
+F: drivers/net/wireless/broadcom/b43legacy/
BACKLIGHT CLASS/SUBSYSTEM
M: Jingoo Han <jingoohan1@gmail.com>
@@ -2101,7 +2110,7 @@ F: include/linux/backlight.h
BATMAN ADVANCED
M: Marek Lindner <mareklindner@neomailbox.ch>
M: Simon Wunderlich <sw@simonwunderlich.de>
-M: Antonio Quartulli <antonio@meshcoding.com>
+M: Antonio Quartulli <a@unstable.cc>
L: b.a.t.m.a.n@lists.open-mesh.org
W: http://www.open-mesh.org/
S: Maintained
@@ -2125,7 +2134,7 @@ BDISP ST MEDIA DRIVER
M: Fabien Dessenne <fabien.dessenne@st.com>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Supported
F: drivers/media/platform/sti/bdisp
@@ -2391,7 +2400,7 @@ M: Hante Meuleman <meuleman@broadcom.com>
L: linux-wireless@vger.kernel.org
L: brcm80211-dev-list@broadcom.com
S: Supported
-F: drivers/net/wireless/brcm80211/
+F: drivers/net/wireless/broadcom/brcm80211/
BROADCOM BNX2FC 10 GIGABIT FCOE DRIVER
M: QLogic-Storage-Upstream@qlogic.com
@@ -2518,7 +2527,7 @@ F: fs/btrfs/
BTTV VIDEO4LINUX DRIVER
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org
+W: https://linuxtv.org
T: git git://linuxtv.org/media_tree.git
S: Odd fixes
F: Documentation/video4linux/bttv/
@@ -2557,7 +2566,7 @@ CADET FM/AM RADIO RECEIVER DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Maintained
F: drivers/media/radio/radio-cadet*
@@ -2758,7 +2767,7 @@ S: Maintained
F: Documentation/zh_CN/
CHIPIDEA USB HIGH SPEED DUAL ROLE CONTROLLER
-M: Peter Chen <Peter.Chen@freescale.com>
+M: Peter Chen <Peter.Chen@nxp.com>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
L: linux-usb@vger.kernel.org
S: Maintained
@@ -2850,7 +2859,7 @@ COBALT MEDIA DRIVER
M: Hans Verkuil <hans.verkuil@cisco.com>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Supported
F: drivers/media/pci/cobalt/
@@ -2938,7 +2947,8 @@ F: drivers/usb/atm/cxacru.c
CONFIGFS
M: Joel Becker <jlbec@evilplan.org>
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/jlbec/configfs.git
+M: Christoph Hellwig <hch@lst.de>
+T: git git://git.infradead.org/users/hch/configfs.git
S: Supported
F: fs/configfs/
F: include/linux/configfs.h
@@ -2974,6 +2984,7 @@ F: kernel/cpuset.c
CONTROL GROUP - MEMORY RESOURCE CONTROLLER (MEMCG)
M: Johannes Weiner <hannes@cmpxchg.org>
M: Michal Hocko <mhocko@kernel.org>
+M: Vladimir Davydov <vdavydov@virtuozzo.com>
L: cgroups@vger.kernel.org
L: linux-mm@kvack.org
S: Maintained
@@ -3094,6 +3105,15 @@ S: Maintained
F: crypto/ansi_cprng.c
F: crypto/rng.c
+CS3308 MEDIA DRIVER
+M: Hans Verkuil <hverkuil@xs4all.nl>
+L: linux-media@vger.kernel.org
+T: git git://linuxtv.org/media_tree.git
+W: http://linuxtv.org
+S: Odd Fixes
+F: drivers/media/i2c/cs3308.c
+F: drivers/media/i2c/cs3308.h
+
CS5535 Audio ALSA driver
M: Jaya Kumar <jayakumar.alsa@gmail.com>
S: Maintained
@@ -3102,14 +3122,14 @@ F: sound/pci/cs5535audio/
CW1200 WLAN driver
M: Solomon Peachy <pizza@shaftnet.org>
S: Maintained
-F: drivers/net/wireless/cw1200/
+F: drivers/net/wireless/st/cw1200/
CX18 VIDEO4LINUX DRIVER
M: Andy Walls <awalls@md.metrocast.net>
L: ivtv-devel@ivtvdriver.org (subscribers-only)
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
W: http://www.ivtvdriver.org/index.php/Cx18
S: Maintained
F: Documentation/video4linux/cx18.txt
@@ -3120,7 +3140,7 @@ CX2341X MPEG ENCODER HELPER MODULE
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Maintained
F: drivers/media/common/cx2341x*
F: include/media/cx2341x*
@@ -3129,7 +3149,7 @@ CX24120 MEDIA DRIVER
M: Jemma Denson <jdenson@gmail.com>
M: Patrick Boettcher <patrick.boettcher@posteo.de>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
Q: http://patchwork.linuxtv.org/project/linux-media/list/
S: Maintained
F: drivers/media/dvb-frontends/cx24120*
@@ -3137,7 +3157,7 @@ F: drivers/media/dvb-frontends/cx24120*
CX88 VIDEO4LINUX DRIVER
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org
+W: https://linuxtv.org
T: git git://linuxtv.org/media_tree.git
S: Odd fixes
F: Documentation/video4linux/cx88/
@@ -3146,7 +3166,7 @@ F: drivers/media/pci/cx88/
CXD2820R MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -3252,7 +3272,7 @@ F: drivers/net/wan/pc300*
CYPRESS_FIRMWARE MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -3759,14 +3779,14 @@ DT3155 MEDIA DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Odd Fixes
F: drivers/media/pci/dt3155/
DVB_USB_AF9015 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -3776,7 +3796,7 @@ F: drivers/media/usb/dvb-usb-v2/af9015*
DVB_USB_AF9035 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -3786,7 +3806,7 @@ F: drivers/media/usb/dvb-usb-v2/af9035*
DVB_USB_ANYSEE MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -3796,7 +3816,7 @@ F: drivers/media/usb/dvb-usb-v2/anysee*
DVB_USB_AU6610 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -3806,7 +3826,7 @@ F: drivers/media/usb/dvb-usb-v2/au6610*
DVB_USB_CE6230 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -3816,7 +3836,7 @@ F: drivers/media/usb/dvb-usb-v2/ce6230*
DVB_USB_CXUSB MEDIA DRIVER
M: Michael Krufky <mkrufky@linuxtv.org>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://github.com/mkrufky
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/media_tree.git
@@ -3826,7 +3846,7 @@ F: drivers/media/usb/dvb-usb/cxusb*
DVB_USB_EC168 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -3836,7 +3856,7 @@ F: drivers/media/usb/dvb-usb-v2/ec168*
DVB_USB_GL861 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
S: Maintained
@@ -3845,7 +3865,7 @@ F: drivers/media/usb/dvb-usb-v2/gl861*
DVB_USB_MXL111SF MEDIA DRIVER
M: Michael Krufky <mkrufky@linuxtv.org>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://github.com/mkrufky
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/mkrufky/mxl111sf.git
@@ -3855,7 +3875,7 @@ F: drivers/media/usb/dvb-usb-v2/mxl111sf*
DVB_USB_RTL28XXU MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -3865,7 +3885,7 @@ F: drivers/media/usb/dvb-usb-v2/rtl28xxu*
DVB_USB_V2 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -3895,7 +3915,7 @@ F: Documentation/devicetree/bindings/input/e3x0-button.txt
E4000 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -3911,7 +3931,7 @@ F: drivers/scsi/eata.c
EC100 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -3932,9 +3952,8 @@ M: Doug Thompson <dougthompson@xmission.com>
M: Borislav Petkov <bp@alien8.de>
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org
-W: bluesmoke.sourceforge.net
-T: git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp.git#for-next
-T: git://git.kernel.org/pub/linux/kernel/git/mchehab/linux-edac.git#linux_next
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp.git for-next
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-edac.git linux_next
S: Supported
F: Documentation/edac.txt
F: drivers/edac/
@@ -3944,7 +3963,6 @@ EDAC-AMD64
M: Doug Thompson <dougthompson@xmission.com>
M: Borislav Petkov <bp@alien8.de>
L: linux-edac@vger.kernel.org
-W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/amd64_edac*
@@ -3952,7 +3970,6 @@ EDAC-CALXEDA
M: Doug Thompson <dougthompson@xmission.com>
M: Robert Richter <rric@kernel.org>
L: linux-edac@vger.kernel.org
-W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/highbank*
@@ -3961,7 +3978,6 @@ M: Ralf Baechle <ralf@linux-mips.org>
M: David Daney <david.daney@cavium.com>
L: linux-edac@vger.kernel.org
L: linux-mips@linux-mips.org
-W: bluesmoke.sourceforge.net
S: Supported
F: drivers/edac/octeon_edac*
@@ -3969,63 +3985,54 @@ EDAC-E752X
M: Mark Gross <mark.gross@intel.com>
M: Doug Thompson <dougthompson@xmission.com>
L: linux-edac@vger.kernel.org
-W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/e752x_edac.c
EDAC-E7XXX
M: Doug Thompson <dougthompson@xmission.com>
L: linux-edac@vger.kernel.org
-W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/e7xxx_edac.c
EDAC-GHES
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org
-W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/ghes_edac.c
EDAC-I82443BXGX
M: Tim Small <tim@buttersideup.com>
L: linux-edac@vger.kernel.org
-W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/i82443bxgx_edac.c
EDAC-I3000
M: Jason Uhlenkott <juhlenko@akamai.com>
L: linux-edac@vger.kernel.org
-W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/i3000_edac.c
EDAC-I5000
M: Doug Thompson <dougthompson@xmission.com>
L: linux-edac@vger.kernel.org
-W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/i5000_edac.c
EDAC-I5400
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org
-W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/i5400_edac.c
EDAC-I7300
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org
-W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/i7300_edac.c
EDAC-I7CORE
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org
-W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/i7core_edac.c
@@ -4033,42 +4040,36 @@ EDAC-I82975X
M: Ranganathan Desikan <ravi@jetztechnologies.com>
M: "Arvind R." <arvino55@gmail.com>
L: linux-edac@vger.kernel.org
-W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/i82975x_edac.c
EDAC-IE31200
M: Jason Baron <jbaron@akamai.com>
L: linux-edac@vger.kernel.org
-W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/ie31200_edac.c
EDAC-MPC85XX
M: Johannes Thumshirn <morbidrsa@gmail.com>
L: linux-edac@vger.kernel.org
-W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/mpc85xx_edac.[ch]
EDAC-PASEMI
M: Egor Martovetsky <egor@pasemi.com>
L: linux-edac@vger.kernel.org
-W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/pasemi_edac.c
EDAC-R82600
M: Tim Small <tim@buttersideup.com>
L: linux-edac@vger.kernel.org
-W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/r82600_edac.c
EDAC-SBRIDGE
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org
-W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/sb_edac.c
@@ -4135,7 +4136,7 @@ F: drivers/net/ethernet/ibm/ehea/
EM28XX VIDEO4LINUX DRIVER
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org
+W: https://linuxtv.org
T: git git://linuxtv.org/media_tree.git
S: Maintained
F: drivers/media/usb/em28xx/
@@ -4275,7 +4276,7 @@ F: drivers/media/tuners/fc0011.c
FC2580 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -4599,7 +4600,7 @@ M: Heungjun Kim <riverful.kim@samsung.com>
L: linux-media@vger.kernel.org
S: Maintained
F: drivers/media/i2c/m5mols/
-F: include/media/m5mols.h
+F: include/media/i2c/m5mols.h
FUJITSU TABLET EXTRAS
M: Robert Gerlach <khnz@gmx.de>
@@ -4645,7 +4646,7 @@ GEMTEK FM RADIO RECEIVER DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Maintained
F: drivers/media/radio/radio-gemtek*
@@ -4853,7 +4854,7 @@ HDPVR USB VIDEO ENCODER DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Odd Fixes
F: drivers/media/usb/hdpvr/
@@ -4872,7 +4873,7 @@ F: drivers/tty/hvc/
HACKRF MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -4915,7 +4916,7 @@ F: sound/parisc/harmony.*
HD29L2 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -4984,6 +4985,7 @@ F: arch/*/include/asm/suspend*.h
HID CORE LAYER
M: Jiri Kosina <jikos@kernel.org>
+R: Benjamin Tissoires <benjamin.tissoires@redhat.com>
L: linux-input@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid.git
S: Maintained
@@ -5038,13 +5040,20 @@ F: include/uapi/linux/if_hippi.h
F: net/802/hippi.c
F: drivers/net/hippi/
+HISILICON SAS Controller
+M: John Garry <john.garry@huawei.com>
+W: http://www.hisilicon.com
+S: Supported
+F: drivers/scsi/hisi_sas/
+F: Documentation/devicetree/bindings/scsi/hisilicon-sas.txt
+
HOST AP DRIVER
M: Jouni Malinen <j@w1.fi>
L: hostap@shmoo.com (subscribers-only)
L: linux-wireless@vger.kernel.org
W: http://hostap.epitest.fi/
S: Maintained
-F: drivers/net/wireless/hostap/
+F: drivers/net/wireless/intersil/hostap/
HP COMPAQ TC1100 TABLET WMI EXTRAS DRIVER
L: platform-driver-x86@vger.kernel.org
@@ -5282,6 +5291,13 @@ L: netdev@vger.kernel.org
S: Supported
F: drivers/net/ethernet/ibm/ibmveth.*
+IBM Power SRIOV Virtual NIC Device Driver
+M: Thomas Falcon <tlfalcon@linux.vnet.ibm.com>
+M: John Allen <jallen@linux.vnet.ibm.com>
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/ibm/ibmvnic.*
+
IBM Power Virtual SCSI Device Drivers
M: Tyrel Datwyler <tyreld@linux.vnet.ibm.com>
L: linux-scsi@vger.kernel.org
@@ -5577,7 +5593,7 @@ R: Jesse Brandeburg <jesse.brandeburg@intel.com>
R: Shannon Nelson <shannon.nelson@intel.com>
R: Carolyn Wyborny <carolyn.wyborny@intel.com>
R: Don Skidmore <donald.c.skidmore@intel.com>
-R: Matthew Vick <matthew.vick@intel.com>
+R: Bruce Allan <bruce.w.allan@intel.com>
R: John Ronciak <john.ronciak@intel.com>
R: Mitch Williams <mitch.a.williams@intel.com>
L: intel-wired-lan@lists.osuosl.org
@@ -5612,7 +5628,7 @@ L: linux-wireless@vger.kernel.org
S: Maintained
F: Documentation/networking/README.ipw2100
F: Documentation/networking/README.ipw2200
-F: drivers/net/wireless/ipw2x00/
+F: drivers/net/wireless/intel/ipw2x00/
INTEL(R) TRACE HUB
M: Alexander Shishkin <alexander.shishkin@linux.intel.com>
@@ -5621,9 +5637,7 @@ F: Documentation/trace/intel_th.txt
F: drivers/hwtracing/intel_th/
INTEL(R) TRUSTED EXECUTION TECHNOLOGY (TXT)
-M: Richard L Maliszewski <richard.l.maliszewski@intel.com>
-M: Gang Wei <gang.wei@intel.com>
-M: Shane Wang <shane.wang@intel.com>
+M: Ning Sun <ning.sun@intel.com>
L: tboot-devel@lists.sourceforge.net
W: http://tboot.sourceforge.net
T: hg http://tboot.hg.sourceforge.net:8000/hgroot/tboot/tboot
@@ -5646,7 +5660,7 @@ INTEL WIRELESS 3945ABG/BG, 4965AGN (iwlegacy)
M: Stanislaw Gruszka <sgruszka@redhat.com>
L: linux-wireless@vger.kernel.org
S: Supported
-F: drivers/net/wireless/iwlegacy/
+F: drivers/net/wireless/intel/iwlegacy/
INTEL WIRELESS WIFI LINK (iwlwifi)
M: Johannes Berg <johannes.berg@intel.com>
@@ -5656,7 +5670,7 @@ L: linux-wireless@vger.kernel.org
W: http://intellinuxwireless.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi.git
S: Supported
-F: drivers/net/wireless/iwlwifi/
+F: drivers/net/wireless/intel/iwlwifi/
INTEL MANAGEMENT ENGINE (mei)
M: Tomas Winkler <tomas.winkler@intel.com>
@@ -5821,7 +5835,7 @@ ISA RADIO MODULE
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Maintained
F: drivers/media/radio/radio-isa*
@@ -5891,7 +5905,7 @@ F: drivers/hwmon/it87.c
IT913X MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -5912,7 +5926,7 @@ F: include/uapi/linux/ivtv*
IX2505V MEDIA DRIVER
M: Malcolm Priestley <tvboxspy@gmail.com>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
Q: http://patchwork.linuxtv.org/project/linux-media/list/
S: Maintained
F: drivers/media/dvb-frontends/ix2505v*
@@ -6001,7 +6015,7 @@ KEENE FM RADIO TRANSMITTER DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Maintained
F: drivers/media/radio/radio-keene*
@@ -6101,6 +6115,7 @@ M: Marc Zyngier <marc.zyngier@arm.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
L: kvmarm@lists.cs.columbia.edu
W: http://systems.cs.columbia.edu/projects/kvm-arm
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm.git
S: Supported
F: arch/arm/include/uapi/asm/kvm*
F: arch/arm/include/asm/kvm*
@@ -6253,7 +6268,7 @@ F: drivers/usb/misc/legousbtower.c
LG2160 MEDIA DRIVER
M: Michael Krufky <mkrufky@linuxtv.org>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://github.com/mkrufky
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/mkrufky/tuners.git
@@ -6263,7 +6278,7 @@ F: drivers/media/dvb-frontends/lg2160.*
LGDT3305 MEDIA DRIVER
M: Michael Krufky <mkrufky@linuxtv.org>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://github.com/mkrufky
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/mkrufky/tuners.git
@@ -6367,6 +6382,7 @@ F: arch/*/include/asm/pmem.h
LIGHTNVM PLATFORM SUPPORT
M: Matias Bjorling <mb@lightnvm.io>
W: http://github/OpenChannelSSD
+L: linux-block@vger.kernel.org
S: Maintained
F: drivers/lightnvm/
F: include/linux/lightnvm.h
@@ -6519,7 +6535,7 @@ F: drivers/hwmon/lm95234.c
LME2510 MEDIA DRIVER
M: Malcolm Priestley <tvboxspy@gmail.com>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
Q: http://patchwork.linuxtv.org/project/linux-media/list/
S: Maintained
F: drivers/media/usb/dvb-usb-v2/lmedm04*
@@ -6625,7 +6641,7 @@ F: arch/m68k/hp300/
M88DS3103 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -6635,7 +6651,7 @@ F: drivers/media/dvb-frontends/m88ds3103*
M88RS2000 MEDIA DRIVER
M: Malcolm Priestley <tvboxspy@gmail.com>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
Q: http://patchwork.linuxtv.org/project/linux-media/list/
S: Maintained
F: drivers/media/dvb-frontends/m88rs2000*
@@ -6706,7 +6722,7 @@ F: drivers/net/ethernet/marvell/sk*
MARVELL LIBERTAS WIRELESS DRIVER
L: libertas-dev@lists.infradead.org
S: Orphan
-F: drivers/net/wireless/libertas/
+F: drivers/net/wireless/marvell/libertas/
MARVELL MV643XX ETHERNET DRIVER
M: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
@@ -6726,13 +6742,13 @@ M: Amitkumar Karwar <akarwar@marvell.com>
M: Nishant Sarmukadam <nishants@marvell.com>
L: linux-wireless@vger.kernel.org
S: Maintained
-F: drivers/net/wireless/mwifiex/
+F: drivers/net/wireless/marvell/mwifiex/
MARVELL MWL8K WIRELESS DRIVER
M: Lennert Buytenhek <buytenh@wantstofly.org>
L: linux-wireless@vger.kernel.org
S: Odd Fixes
-F: drivers/net/wireless/mwl8k.c
+F: drivers/net/wireless/marvell/mwl8k.c
MARVELL SOC MMC/SD/SDIO CONTROLLER DRIVER
M: Nicolas Pitre <nico@fluxnic.net>
@@ -6814,7 +6830,7 @@ MAXIRADIO FM RADIO RECEIVER DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Maintained
F: drivers/media/radio/radio-maxiradio*
@@ -6836,7 +6852,7 @@ F: drivers/media/platform/vsp1/
MEDIA DRIVERS FOR ASCOT2E
M: Sergey Kozlov <serjk@netup.ru>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org
+W: https://linuxtv.org
W: http://netup.tv/
T: git git://linuxtv.org/media_tree.git
S: Supported
@@ -6845,7 +6861,7 @@ F: drivers/media/dvb-frontends/ascot2e*
MEDIA DRIVERS FOR CXD2841ER
M: Sergey Kozlov <serjk@netup.ru>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://netup.tv/
T: git git://linuxtv.org/media_tree.git
S: Supported
@@ -6854,7 +6870,7 @@ F: drivers/media/dvb-frontends/cxd2841er*
MEDIA DRIVERS FOR HORUS3A
M: Sergey Kozlov <serjk@netup.ru>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://netup.tv/
T: git git://linuxtv.org/media_tree.git
S: Supported
@@ -6863,7 +6879,7 @@ F: drivers/media/dvb-frontends/horus3a*
MEDIA DRIVERS FOR LNBH25
M: Sergey Kozlov <serjk@netup.ru>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://netup.tv/
T: git git://linuxtv.org/media_tree.git
S: Supported
@@ -6872,7 +6888,7 @@ F: drivers/media/dvb-frontends/lnbh25*
MEDIA DRIVERS FOR NETUP PCI UNIVERSAL DVB devices
M: Sergey Kozlov <serjk@netup.ru>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://netup.tv/
T: git git://linuxtv.org/media_tree.git
S: Supported
@@ -6882,7 +6898,7 @@ MEDIA INPUT INFRASTRUCTURE (V4L/DVB)
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
P: LinuxTV.org Project
L: linux-media@vger.kernel.org
-W: http://linuxtv.org
+W: https://linuxtv.org
Q: http://patchwork.kernel.org/project/linux-media/list/
T: git git://linuxtv.org/media_tree.git
S: Maintained
@@ -6891,6 +6907,7 @@ F: Documentation/video4linux/
F: Documentation/DocBook/media/
F: drivers/media/
F: drivers/staging/media/
+F: include/linux/platform_data/media/
F: include/media/
F: include/uapi/linux/dvb/
F: include/uapi/linux/videodev2.h
@@ -7042,7 +7059,7 @@ MIROSOUND PCM20 FM RADIO RECEIVER DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Odd Fixes
F: drivers/media/radio/radio-miropcm20*
@@ -7078,7 +7095,7 @@ F: drivers/iio/temperature/mlx90614.c
MN88472 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -7089,7 +7106,7 @@ F: drivers/media/dvb-frontends/mn88472.h
MN88473 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -7144,7 +7161,7 @@ F: drivers/platform/x86/msi-wmi.c
MSI001 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -7154,7 +7171,7 @@ F: drivers/media/tuners/msi001*
MSI2500 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -7173,7 +7190,7 @@ L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
S: Maintained
F: drivers/media/i2c/mt9m032.c
-F: include/media/mt9m032.h
+F: include/media/i2c/mt9m032.h
MT9P031 APTINA CAMERA SENSOR
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
@@ -7181,7 +7198,7 @@ L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
S: Maintained
F: drivers/media/i2c/mt9p031.c
-F: include/media/mt9p031.h
+F: include/media/i2c/mt9p031.h
MT9T001 APTINA CAMERA SENSOR
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
@@ -7189,7 +7206,7 @@ L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
S: Maintained
F: drivers/media/i2c/mt9t001.c
-F: include/media/mt9t001.h
+F: include/media/i2c/mt9t001.h
MT9V032 APTINA CAMERA SENSOR
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
@@ -7198,7 +7215,7 @@ T: git git://linuxtv.org/media_tree.git
S: Maintained
F: Documentation/devicetree/bindings/media/i2c/mt9v032.txt
F: drivers/media/i2c/mt9v032.c
-F: include/media/mt9v032.h
+F: include/media/i2c/mt9v032.h
MULTIFUNCTION DEVICES (MFD)
M: Lee Jones <lee.jones@linaro.org>
@@ -7242,7 +7259,7 @@ F: drivers/usb/musb/
MXL5007T MEDIA DRIVER
M: Michael Krufky <mkrufky@linuxtv.org>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://github.com/mkrufky
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/mkrufky/tuners.git
@@ -7367,6 +7384,13 @@ F: include/net/netrom.h
F: include/uapi/linux/netrom.h
F: net/netrom/
+NETRONOME ETHERNET DRIVERS
+M: Jakub Kicinski <jakub.kicinski@netronome.com>
+M: Rolf Neugebauer <rolf.neugebauer@netronome.com>
+L: oss-drivers@netronome.com
+S: Maintained
+F: drivers/net/ethernet/netronome/
+
NETWORK BLOCK DEVICE (NBD)
M: Markus Pargmann <mpa@pengutronix.de>
S: Maintained
@@ -7498,7 +7522,12 @@ F: net/nfc/
F: include/net/nfc/
F: include/uapi/linux/nfc.h
F: drivers/nfc/
+F: include/linux/platform_data/microread.h
+F: include/linux/platform_data/nfcmrvl.h
+F: include/linux/platform_data/nxp-nci.h
F: include/linux/platform_data/pn544.h
+F: include/linux/platform_data/st21nfca.h
+F: include/linux/platform_data/st-nci.h
F: Documentation/devicetree/bindings/net/nfc/
NFS, SUNRPC, AND LOCKD CLIENTS
@@ -7948,7 +7977,7 @@ L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/en/users/Drivers/orinoco
W: http://www.nongnu.org/orinoco/
S: Orphan
-F: drivers/net/wireless/orinoco/
+F: drivers/net/wireless/intersil/orinoco/
OSD LIBRARY and FILESYSTEM
M: Boaz Harrosh <ooo@electrozaur.com>
@@ -7974,7 +8003,7 @@ M: Christian Lamparter <chunkeey@googlemail.com>
L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/en/users/Drivers/p54
S: Maintained
-F: drivers/net/wireless/p54/
+F: drivers/net/wireless/intersil/p54/
PA SEMI ETHERNET DRIVER
M: Olof Johansson <olof@lixom.net>
@@ -8285,7 +8314,7 @@ F: include/linux/delayacct.h
F: kernel/delayacct.c
PERFORMANCE EVENTS SUBSYSTEM
-M: Peter Zijlstra <a.p.zijlstra@chello.nl>
+M: Peter Zijlstra <peterz@infradead.org>
M: Ingo Molnar <mingo@redhat.com>
M: Arnaldo Carvalho de Melo <acme@kernel.org>
L: linux-kernel@vger.kernel.org
@@ -8367,6 +8396,7 @@ F: drivers/pinctrl/intel/
PIN CONTROLLER - RENESAS
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+M: Geert Uytterhoeven <geert+renesas@glider.be>
L: linux-sh@vger.kernel.org
S: Maintained
F: drivers/pinctrl/sh-pfc/
@@ -8378,6 +8408,14 @@ L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
S: Maintained
F: drivers/pinctrl/samsung/
+PIN CONTROLLER - SINGLE
+M: Tony Lindgren <tony@atomide.com>
+M: Haojian Zhuang <haojian.zhuang@linaro.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L: linux-omap@vger.kernel.org
+S: Maintained
+F: drivers/pinctrl/pinctrl-single.c
+
PIN CONTROLLER - ST SPEAR
M: Viresh Kumar <vireshk@kernel.org>
L: spear-devel@list.st.com
@@ -8437,6 +8475,17 @@ F: fs/timerfd.c
F: include/linux/timer*
F: kernel/time/*timer*
+POWER MANAGEMENT CORE
+M: "Rafael J. Wysocki" <rjw@rjwysocki.net>
+L: linux-pm@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
+S: Supported
+F: drivers/base/power/
+F: include/linux/pm.h
+F: include/linux/pm_*
+F: include/linux/powercap.h
+F: drivers/powercap/
+
POWER SUPPLY CLASS/SUBSYSTEM and DRIVERS
M: Sebastian Reichel <sre@kernel.org>
M: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
@@ -8516,7 +8565,7 @@ M: "Luis R. Rodriguez" <mcgrof@gmail.com>
L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/en/users/Drivers/p54
S: Obsolete
-F: drivers/net/wireless/prism54/
+F: drivers/net/wireless/intersil/prism54/
PS3 NETWORK SUPPORT
M: Geoff Levand <geoff@infradead.org>
@@ -8629,6 +8678,7 @@ S: Maintained
F: arch/arm/mach-pxa/
F: drivers/dma/pxa*
F: drivers/pcmcia/pxa2xx*
+F: drivers/pinctrl/pxa/
F: drivers/spi/spi-pxa2xx*
F: drivers/usb/gadget/udc/pxa2*
F: include/sound/pxa2xx-lib.h
@@ -8735,7 +8785,7 @@ F: include/uapi/linux/qnxtypes.h
QT1010 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -8819,7 +8869,7 @@ M: Stanislaw Gruszka <sgruszka@redhat.com>
M: Helmut Schaa <helmut.schaa@googlemail.com>
L: linux-wireless@vger.kernel.org
S: Maintained
-F: drivers/net/wireless/rt2x00/
+F: drivers/net/wireless/ralink/rt2x00/
RAMDISK RAM BLOCK DEVICE DRIVER
M: Jens Axboe <axboe@kernel.dk>
@@ -8944,6 +8994,19 @@ F: drivers/rpmsg/
F: Documentation/rpmsg.txt
F: include/linux/rpmsg.h
+RENESAS ETHERNET DRIVERS
+R: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
+L: netdev@vger.kernel.org
+L: linux-sh@vger.kernel.org
+F: drivers/net/ethernet/renesas/
+F: include/linux/sh_eth.h
+
+RENESAS USB2 PHY DRIVER
+M: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
+L: linux-sh@vger.kernel.org
+S: Maintained
+F: drivers/phy/phy-rcar-gen3-usb2.c
+
RESET CONTROLLER FRAMEWORK
M: Philipp Zabel <p.zabel@pengutronix.de>
S: Maintained
@@ -9021,7 +9084,7 @@ F: net/rose/
RTL2830 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -9031,7 +9094,7 @@ F: drivers/media/dvb-frontends/rtl2830*
RTL2832 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -9041,7 +9104,7 @@ F: drivers/media/dvb-frontends/rtl2832*
RTL2832_SDR MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -9053,7 +9116,7 @@ L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
S: Orphan
-F: drivers/net/wireless/rtl818x/rtl8180/
+F: drivers/net/wireless/realtek/rtl818x/rtl8180/
RTL8187 WIRELESS DRIVER
M: Herton Ronaldo Krzesinski <herton@canonical.com>
@@ -9063,7 +9126,7 @@ L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
S: Maintained
-F: drivers/net/wireless/rtl818x/rtl8187/
+F: drivers/net/wireless/realtek/rtl818x/rtl8187/
RTL8192CE WIRELESS DRIVER
M: Larry Finger <Larry.Finger@lwfinger.net>
@@ -9072,8 +9135,8 @@ L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
S: Maintained
-F: drivers/net/wireless/rtlwifi/
-F: drivers/net/wireless/rtlwifi/rtl8192ce/
+F: drivers/net/wireless/realtek/rtlwifi/
+F: drivers/net/wireless/realtek/rtlwifi/rtl8192ce/
RTL8XXXU WIRELESS DRIVER (rtl8xxxu)
M: Jes Sorensen <Jes.Sorensen@redhat.com>
@@ -9117,7 +9180,7 @@ F: drivers/s390/block/dasd*
F: block/partitions/ibm.c
S390 NETWORK DRIVERS
-M: Ursula Braun <ursula.braun@de.ibm.com>
+M: Ursula Braun <ubraun@linux.vnet.ibm.com>
L: linux-s390@vger.kernel.org
W: http://www.ibm.com/developerworks/linux/linux390/
S: Supported
@@ -9147,7 +9210,7 @@ S: Supported
F: drivers/s390/scsi/zfcp_*
S390 IUCV NETWORK LAYER
-M: Ursula Braun <ursula.braun@de.ibm.com>
+M: Ursula Braun <ubraun@linux.vnet.ibm.com>
L: linux-s390@vger.kernel.org
W: http://www.ibm.com/developerworks/linux/linux390/
S: Supported
@@ -9172,14 +9235,14 @@ SAA6588 RDS RECEIVER DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Odd Fixes
F: drivers/media/i2c/saa6588*
SAA7134 VIDEO4LINUX DRIVER
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org
+W: https://linuxtv.org
T: git git://linuxtv.org/media_tree.git
S: Odd fixes
F: Documentation/video4linux/*.saa7134
@@ -9224,8 +9287,10 @@ 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
+F: Documentation/devicetree/bindings/mfd/samsung,sec-core.txt
+F: Documentation/devicetree/bindings/regulator/samsung,s2m*.txt
+F: Documentation/devicetree/bindings/regulator/samsung,s5m*.txt
+F: Documentation/devicetree/bindings/clock/samsung,s2mps11.txt
SAMSUNG S5P/EXYNOS4 SOC SERIES CAMERA SUBSYSTEM DRIVERS
M: Kyungmin Park <kyungmin.park@samsung.com>
@@ -9241,7 +9306,7 @@ L: linux-media@vger.kernel.org
L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
S: Maintained
F: drivers/media/platform/s3c-camif/
-F: include/media/s3c_camif.h
+F: include/media/drv-intf/s3c_camif.h
SAMSUNG S5C73M3 CAMERA DRIVER
M: Kyungmin Park <kyungmin.park@samsung.com>
@@ -9349,7 +9414,7 @@ M: Andreas Noever <andreas.noever@gmail.com>
S: Maintained
F: drivers/thunderbolt/
-TIMEKEEPING, CLOCKSOURCE CORE, NTP
+TIMEKEEPING, CLOCKSOURCE CORE, NTP, ALARMTIMER
M: John Stultz <john.stultz@linaro.org>
M: Thomas Gleixner <tglx@linutronix.de>
L: linux-kernel@vger.kernel.org
@@ -9362,6 +9427,7 @@ F: include/uapi/linux/time.h
F: include/uapi/linux/timex.h
F: kernel/time/clocksource.c
F: kernel/time/time*.c
+F: kernel/time/alarmtimer.c
F: kernel/time/ntp.c
F: tools/testing/selftests/timers/
@@ -9426,8 +9492,10 @@ F: include/scsi/sg.h
SCSI SUBSYSTEM
M: "James E.J. Bottomley" <JBottomley@odin.com>
-L: linux-scsi@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi.git
+M: "Martin K. Petersen" <martin.petersen@oracle.com>
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mkp/scsi.git
+L: linux-scsi@vger.kernel.org
S: Maintained
F: drivers/scsi/
F: include/scsi/
@@ -9614,7 +9682,7 @@ F: drivers/misc/sgi-xp/
SI2157 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -9624,7 +9692,7 @@ F: drivers/media/tuners/si2157*
SI2168 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -9635,7 +9703,7 @@ SI470X FM RADIO RECEIVER I2C DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Odd Fixes
F: drivers/media/radio/si470x/radio-si470x-i2c.c
@@ -9643,7 +9711,7 @@ SI470X FM RADIO RECEIVER USB DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Maintained
F: drivers/media/radio/si470x/radio-si470x-common.c
F: drivers/media/radio/si470x/radio-si470x.h
@@ -9653,7 +9721,7 @@ SI4713 FM RADIO TRANSMITTER I2C DRIVER
M: Eduardo Valentin <edubezval@gmail.com>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Odd Fixes
F: drivers/media/radio/si4713/si4713.?
@@ -9661,7 +9729,7 @@ SI4713 FM RADIO TRANSMITTER PLATFORM DRIVER
M: Eduardo Valentin <edubezval@gmail.com>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Odd Fixes
F: drivers/media/radio/si4713/radio-platform-si4713.c
@@ -9669,14 +9737,14 @@ SI4713 FM RADIO TRANSMITTER USB DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Maintained
F: drivers/media/radio/si4713/radio-usb-si4713.c
SIANO DVB DRIVER
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org
+W: https://linuxtv.org
T: git git://linuxtv.org/media_tree.git
S: Odd fixes
F: drivers/media/common/siano/
@@ -9701,7 +9769,7 @@ SH_VOU V4L2 OUTPUT DRIVER
L: linux-media@vger.kernel.org
S: Orphan
F: drivers/media/platform/sh_vou.c
-F: include/media/sh_vou.h
+F: include/media/drv-intf/sh_vou.h
SIMPLE FIRMWARE INTERFACE (SFI)
M: Len Brown <lenb@kernel.org>
@@ -9742,7 +9810,7 @@ F: drivers/i2c/busses/i2c-davinci.c
TI DAVINCI SERIES MEDIA DRIVER
M: "Lad, Prabhakar" <prabhakar.csengg@gmail.com>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git
S: Maintained
@@ -9752,7 +9820,7 @@ F: include/media/davinci/
TI AM437X VPFE DRIVER
M: "Lad, Prabhakar" <prabhakar.csengg@gmail.com>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git
S: Maintained
@@ -9761,12 +9829,12 @@ F: drivers/media/platform/am437x/
OV2659 OMNIVISION SENSOR DRIVER
M: "Lad, Prabhakar" <prabhakar.csengg@gmail.com>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git
S: Maintained
F: drivers/media/i2c/ov2659.c
-F: include/media/ov2659.h
+F: include/media/i2c/ov2659.h
SILICON MOTION SM712 FRAME BUFFER DRIVER
M: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
@@ -9855,7 +9923,7 @@ M: Sakari Ailus <sakari.ailus@iki.fi>
L: linux-media@vger.kernel.org
S: Maintained
F: drivers/media/i2c/smiapp/
-F: include/media/smiapp.h
+F: include/media/i2c/smiapp.h
F: drivers/media/i2c/smiapp-pll.c
F: drivers/media/i2c/smiapp-pll.h
F: include/uapi/linux/smiapp.h
@@ -9932,7 +10000,6 @@ S: Supported
F: drivers/media/pci/solo6x10/
SOFTWARE RAID (Multiple Disks) SUPPORT
-M: Neil Brown <neilb@suse.com>
L: linux-raid@vger.kernel.org
S: Supported
F: drivers/md/
@@ -10019,7 +10086,7 @@ F: sound/soc/soc-generic-dmaengine-pcm.c
SP2 MEDIA DRIVER
M: Olli Salonen <olli.salonen@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
Q: http://patchwork.linuxtv.org/project/linux-media/list/
S: Maintained
F: drivers/media/dvb-frontends/sp2*
@@ -10381,7 +10448,7 @@ F: net/ipv4/tcp_lp.c
TDA10071 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -10391,7 +10458,7 @@ F: drivers/media/dvb-frontends/tda10071*
TDA18212 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -10401,7 +10468,7 @@ F: drivers/media/tuners/tda18212*
TDA18218 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -10411,7 +10478,7 @@ F: drivers/media/tuners/tda18218*
TDA18271 MEDIA DRIVER
M: Michael Krufky <mkrufky@linuxtv.org>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://github.com/mkrufky
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/mkrufky/tuners.git
@@ -10421,7 +10488,7 @@ F: drivers/media/tuners/tda18271*
TDA827x MEDIA DRIVER
M: Michael Krufky <mkrufky@linuxtv.org>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://github.com/mkrufky
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/mkrufky/tuners.git
@@ -10431,7 +10498,7 @@ F: drivers/media/tuners/tda8290.*
TDA8290 MEDIA DRIVER
M: Michael Krufky <mkrufky@linuxtv.org>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://github.com/mkrufky
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/mkrufky/tuners.git
@@ -10442,14 +10509,14 @@ TDA9840 MEDIA DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Maintained
F: drivers/media/i2c/tda9840*
TEA5761 TUNER DRIVER
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org
+W: https://linuxtv.org
T: git git://linuxtv.org/media_tree.git
S: Odd fixes
F: drivers/media/tuners/tea5761.*
@@ -10457,7 +10524,7 @@ F: drivers/media/tuners/tea5761.*
TEA5767 TUNER DRIVER
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org
+W: https://linuxtv.org
T: git git://linuxtv.org/media_tree.git
S: Maintained
F: drivers/media/tuners/tea5767.*
@@ -10466,7 +10533,7 @@ TEA6415C MEDIA DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Maintained
F: drivers/media/i2c/tea6415c*
@@ -10474,7 +10541,7 @@ TEA6420 MEDIA DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Maintained
F: drivers/media/i2c/tea6420*
@@ -10572,7 +10639,7 @@ THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Maintained
F: drivers/media/radio/radio-raremono.c
@@ -10796,7 +10863,7 @@ M: Mats Randgaard <matrandg@cisco.com>
L: linux-media@vger.kernel.org
S: Maintained
F: drivers/media/i2c/tc358743*
-F: include/media/tc358743.h
+F: include/media/i2c/tc358743.h
TMIO MMC DRIVER
M: Ian Molton <ian@mnementh.co.uk>
@@ -10824,7 +10891,7 @@ F: mm/shmem.c
TM6000 VIDEO4LINUX DRIVER
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org
+W: https://linuxtv.org
T: git git://linuxtv.org/media_tree.git
S: Odd fixes
F: drivers/media/usb/tm6000/
@@ -10833,7 +10900,7 @@ TW68 VIDEO4LINUX DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Odd Fixes
F: drivers/media/pci/tw68/
@@ -10894,7 +10961,7 @@ F: include/uapi/linux/tty.h
TUA9001 MEDIA DRIVER
M: Antti Palosaari <crope@iki.fi>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org/
+W: https://linuxtv.org
W: http://palosaari.fi/linux/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git
@@ -10902,9 +10969,9 @@ S: Maintained
F: drivers/media/tuners/tua9001*
TULIP NETWORK DRIVERS
-M: Grant Grundler <grundler@parisc-linux.org>
L: netdev@vger.kernel.org
-S: Maintained
+L: linux-parisc@vger.kernel.org
+S: Orphan
F: drivers/net/ethernet/dec/tulip/
TUN/TAP driver
@@ -11096,6 +11163,7 @@ F: include/linux/usb/gadget*
USB HID/HIDBP DRIVERS (USB KEYBOARDS, MICE, REMOTE CONTROLS, ...)
M: Jiri Kosina <jikos@kernel.org>
+R: Benjamin Tissoires <benjamin.tissoires@redhat.com>
L: linux-usb@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid.git
S: Maintained
@@ -11137,7 +11205,7 @@ F: Documentation/usb/ohci.txt
F: drivers/usb/host/ohci*
USB OTG FSM (Finite State Machine)
-M: Peter Chen <Peter.Chen@freescale.com>
+M: Peter Chen <Peter.Chen@nxp.com>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
L: linux-usb@vger.kernel.org
S: Maintained
@@ -11173,6 +11241,13 @@ L: linux-usb@vger.kernel.org
S: Supported
F: drivers/usb/class/usblp.c
+USB QMI WWAN NETWORK DRIVER
+M: Bjørn Mork <bjorn@mork.no>
+L: netdev@vger.kernel.org
+S: Maintained
+F: Documentation/ABI/testing/sysfs-class-net-qmi
+F: drivers/net/usb/qmi_wwan.c
+
USB RTL8150 DRIVER
M: Petko Manolov <petkan@nucleusys.com>
L: linux-usb@vger.kernel.org
@@ -11241,7 +11316,7 @@ USB VISION DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Odd Fixes
F: drivers/media/usb/usbvision/
@@ -11269,7 +11344,7 @@ USB ZD1201 DRIVER
L: linux-wireless@vger.kernel.org
W: http://linux-lc100020.sourceforge.net
S: Orphan
-F: drivers/net/wireless/zd1201.*
+F: drivers/net/wireless/zydas/zd1201.*
USB ZR364XX DRIVER
M: Antoine Jacquet <royale@zerezo.com>
@@ -11455,7 +11530,7 @@ VIVID VIRTUAL VIDEO DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
-W: http://linuxtv.org
+W: https://linuxtv.org
S: Maintained
F: drivers/media/platform/vivid/*
@@ -11672,6 +11747,7 @@ F: drivers/input/touchscreen/wm831x-ts.c
F: drivers/input/touchscreen/wm97*.c
F: drivers/mfd/arizona*
F: drivers/mfd/wm*.c
+F: drivers/mfd/cs47l24*
F: drivers/power/wm83*.c
F: drivers/rtc/rtc-wm83*.c
F: drivers/regulator/wm8*.c
@@ -11685,6 +11761,7 @@ F: include/linux/wm97xx.h
F: include/sound/wm????.h
F: sound/soc/codecs/arizona.?
F: sound/soc/codecs/wm*
+F: sound/soc/codecs/cs47l24*
WORKQUEUE
M: Tejun Heo <tj@kernel.org>
@@ -11744,7 +11821,7 @@ F: arch/x86/entry/vdso/
XC2028/3028 TUNER DRIVER
M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
-W: http://linuxtv.org
+W: https://linuxtv.org
T: git git://linuxtv.org/media_tree.git
S: Maintained
F: drivers/media/tuners/tuner-xc2028.*
@@ -11900,7 +11977,7 @@ W: http://zd1211.ath.cx/wiki/DriverRewrite
L: linux-wireless@vger.kernel.org
L: zd1211-devs@lists.sourceforge.net (subscribers-only)
S: Maintained
-F: drivers/net/wireless/zd1211rw/
+F: drivers/net/wireless/zydas/zd1211rw/
ZPOOL COMPRESSED PAGE STORAGE API
M: Dan Streetman <ddstreet@ieee.org>
@@ -11913,7 +11990,7 @@ ZR36067 VIDEO FOR LINUX DRIVER
L: mjpeg-users@lists.sourceforge.net
L: linux-media@vger.kernel.org
W: http://mjpeg.sourceforge.net/driver-zoran/
-T: hg http://linuxtv.org/hg/v4l-dvb
+T: hg https://linuxtv.org/hg/v4l-dvb
S: Odd Fixes
F: drivers/media/pci/zoran/
diff --git a/Makefile b/Makefile
index 2ffdf9d6f339..70dea02f1346 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
VERSION = 4
PATCHLEVEL = 4
SUBLEVEL = 0
-EXTRAVERSION = -rc2
+EXTRAVERSION =
NAME = Blurry Fish Butt
# *DOCUMENTATION*
diff --git a/arch/Kconfig b/arch/Kconfig
index 4e949e58b192..ba1b626bca00 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -511,6 +511,74 @@ config ARCH_HAS_ELF_RANDOMIZE
- arch_mmap_rnd()
- arch_randomize_brk()
+config HAVE_ARCH_MMAP_RND_BITS
+ bool
+ help
+ An arch should select this symbol if it supports setting a variable
+ number of bits for use in establishing the base address for mmap
+ allocations, has MMU enabled and provides values for both:
+ - ARCH_MMAP_RND_BITS_MIN
+ - ARCH_MMAP_RND_BITS_MAX
+
+config ARCH_MMAP_RND_BITS_MIN
+ int
+
+config ARCH_MMAP_RND_BITS_MAX
+ int
+
+config ARCH_MMAP_RND_BITS_DEFAULT
+ int
+
+config ARCH_MMAP_RND_BITS
+ int "Number of bits to use for ASLR of mmap base address" if EXPERT
+ range ARCH_MMAP_RND_BITS_MIN ARCH_MMAP_RND_BITS_MAX
+ default ARCH_MMAP_RND_BITS_DEFAULT if ARCH_MMAP_RND_BITS_DEFAULT
+ default ARCH_MMAP_RND_BITS_MIN
+ depends on HAVE_ARCH_MMAP_RND_BITS
+ help
+ This value can be used to select the number of bits to use to
+ determine the random offset to the base address of vma regions
+ resulting from mmap allocations. This value will be bounded
+ by the architecture's minimum and maximum supported values.
+
+ This value can be changed after boot using the
+ /proc/sys/vm/mmap_rnd_bits tunable
+
+config HAVE_ARCH_MMAP_RND_COMPAT_BITS
+ bool
+ help
+ An arch should select this symbol if it supports running applications
+ in compatibility mode, supports setting a variable number of bits for
+ use in establishing the base address for mmap allocations, has MMU
+ enabled and provides values for both:
+ - ARCH_MMAP_RND_COMPAT_BITS_MIN
+ - ARCH_MMAP_RND_COMPAT_BITS_MAX
+
+config ARCH_MMAP_RND_COMPAT_BITS_MIN
+ int
+
+config ARCH_MMAP_RND_COMPAT_BITS_MAX
+ int
+
+config ARCH_MMAP_RND_COMPAT_BITS_DEFAULT
+ int
+
+config ARCH_MMAP_RND_COMPAT_BITS
+ int "Number of bits to use for ASLR of mmap base address for compatible applications" if EXPERT
+ range ARCH_MMAP_RND_COMPAT_BITS_MIN ARCH_MMAP_RND_COMPAT_BITS_MAX
+ default ARCH_MMAP_RND_COMPAT_BITS_DEFAULT if ARCH_MMAP_RND_COMPAT_BITS_DEFAULT
+ default ARCH_MMAP_RND_COMPAT_BITS_MIN
+ depends on HAVE_ARCH_MMAP_RND_COMPAT_BITS
+ help
+ This value can be used to select the number of bits to use to
+ determine the random offset to the base address of vma regions
+ resulting from mmap allocations for compatible applications This
+ value will be bounded by the architecture's minimum and maximum
+ supported values.
+
+ This value can be changed after boot using the
+ /proc/sys/vm/mmap_rnd_compat_bits tunable
+
config HAVE_COPY_THREAD_TLS
bool
help
diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h
index 9a20821b111c..c5fb9e6bc3a5 100644
--- a/arch/alpha/include/uapi/asm/socket.h
+++ b/arch/alpha/include/uapi/asm/socket.h
@@ -92,4 +92,7 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* _UAPI_ASM_SOCKET_H */
diff --git a/arch/alpha/kernel/module.c b/arch/alpha/kernel/module.c
index 2fd00b7077e4..936bc8f89a67 100644
--- a/arch/alpha/kernel/module.c
+++ b/arch/alpha/kernel/module.c
@@ -160,7 +160,7 @@ apply_relocate_add(Elf64_Shdr *sechdrs, const char *strtab,
/* The small sections were sorted to the end of the segment.
The following should definitely cover them. */
- gp = (u64)me->module_core + me->core_size - 0x8000;
+ gp = (u64)me->core_layout.base + me->core_layout.size - 0x8000;
got = sechdrs[me->arch.gotsecindex].sh_addr;
for (i = 0; i < n; i++) {
diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index 2c2ac3f3ff80..6312f607932f 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -445,6 +445,7 @@ config LINUX_LINK_BASE
However some customers have peripherals mapped at this addr, so
Linux needs to be scooted a bit.
If you don't know what the above means, leave this setting alone.
+ This needs to match memory start address specified in Device Tree
config HIGHMEM
bool "High Memory Support"
diff --git a/arch/arc/Makefile b/arch/arc/Makefile
index cf0cf34eeb24..aeb19021099e 100644
--- a/arch/arc/Makefile
+++ b/arch/arc/Makefile
@@ -81,7 +81,7 @@ endif
LIBGCC := $(shell $(CC) $(cflags-y) --print-libgcc-file-name)
# Modules with short calls might break for calls into builtin-kernel
-KBUILD_CFLAGS_MODULE += -mlong-calls
+KBUILD_CFLAGS_MODULE += -mlong-calls -mno-millicode
# Finally dump eveything into kernel build system
KBUILD_CFLAGS += $(cflags-y)
diff --git a/arch/arc/boot/dts/axs10x_mb.dtsi b/arch/arc/boot/dts/axs10x_mb.dtsi
index f3db32154973..44a578c10732 100644
--- a/arch/arc/boot/dts/axs10x_mb.dtsi
+++ b/arch/arc/boot/dts/axs10x_mb.dtsi
@@ -46,6 +46,7 @@
snps,pbl = < 32 >;
clocks = <&apbclk>;
clock-names = "stmmaceth";
+ max-speed = <100>;
};
ehci@0x40000 {
diff --git a/arch/arc/boot/dts/nsim_hs.dts b/arch/arc/boot/dts/nsim_hs.dts
index b0eb0e7fe21d..fc81879bc1f5 100644
--- a/arch/arc/boot/dts/nsim_hs.dts
+++ b/arch/arc/boot/dts/nsim_hs.dts
@@ -17,7 +17,8 @@
memory {
device_type = "memory";
- reg = <0x0 0x80000000 0x0 0x40000000 /* 1 GB low mem */
+ /* CONFIG_LINUX_LINK_BASE needs to match low mem start */
+ reg = <0x0 0x80000000 0x0 0x20000000 /* 512 MB low mem */
0x1 0x00000000 0x0 0x40000000>; /* 1 GB highmem */
};
diff --git a/arch/arc/configs/axs101_defconfig b/arch/arc/configs/axs101_defconfig
index c92c0ef1e9d2..f1ac9818b751 100644
--- a/arch/arc/configs/axs101_defconfig
+++ b/arch/arc/configs/axs101_defconfig
@@ -1,4 +1,4 @@
-CONFIG_CROSS_COMPILE="arc-linux-uclibc-"
+CONFIG_CROSS_COMPILE="arc-linux-"
CONFIG_DEFAULT_HOSTNAME="ARCLinux"
# CONFIG_SWAP is not set
CONFIG_SYSVIPC=y
diff --git a/arch/arc/configs/axs103_defconfig b/arch/arc/configs/axs103_defconfig
index cfac24e0e7b6..323486d6ee83 100644
--- a/arch/arc/configs/axs103_defconfig
+++ b/arch/arc/configs/axs103_defconfig
@@ -1,4 +1,4 @@
-CONFIG_CROSS_COMPILE="arc-linux-uclibc-"
+CONFIG_CROSS_COMPILE="arc-linux-"
CONFIG_DEFAULT_HOSTNAME="ARCLinux"
# CONFIG_SWAP is not set
CONFIG_SYSVIPC=y
diff --git a/arch/arc/configs/axs103_smp_defconfig b/arch/arc/configs/axs103_smp_defconfig
index 9922a118a15a..66191cd0447e 100644
--- a/arch/arc/configs/axs103_smp_defconfig
+++ b/arch/arc/configs/axs103_smp_defconfig
@@ -1,4 +1,4 @@
-CONFIG_CROSS_COMPILE="arc-linux-uclibc-"
+CONFIG_CROSS_COMPILE="arc-linux-"
CONFIG_DEFAULT_HOSTNAME="ARCLinux"
# CONFIG_SWAP is not set
CONFIG_SYSVIPC=y
diff --git a/arch/arc/configs/nsim_hs_defconfig b/arch/arc/configs/nsim_hs_defconfig
index f761a7c70761..f68838e8068a 100644
--- a/arch/arc/configs/nsim_hs_defconfig
+++ b/arch/arc/configs/nsim_hs_defconfig
@@ -1,4 +1,4 @@
-CONFIG_CROSS_COMPILE="arc-linux-uclibc-"
+CONFIG_CROSS_COMPILE="arc-linux-"
# CONFIG_LOCALVERSION_AUTO is not set
CONFIG_DEFAULT_HOSTNAME="ARCLinux"
# CONFIG_SWAP is not set
diff --git a/arch/arc/configs/nsim_hs_smp_defconfig b/arch/arc/configs/nsim_hs_smp_defconfig
index dc6f74f41283..96bd1c20fb0b 100644
--- a/arch/arc/configs/nsim_hs_smp_defconfig
+++ b/arch/arc/configs/nsim_hs_smp_defconfig
@@ -1,4 +1,4 @@
-CONFIG_CROSS_COMPILE="arc-linux-uclibc-"
+CONFIG_CROSS_COMPILE="arc-linux-"
# CONFIG_LOCALVERSION_AUTO is not set
CONFIG_DEFAULT_HOSTNAME="ARCLinux"
# CONFIG_SWAP is not set
diff --git a/arch/arc/configs/nsimosci_hs_defconfig b/arch/arc/configs/nsimosci_hs_defconfig
index 3fef0a210c56..fcae66683ca0 100644
--- a/arch/arc/configs/nsimosci_hs_defconfig
+++ b/arch/arc/configs/nsimosci_hs_defconfig
@@ -1,4 +1,4 @@
-CONFIG_CROSS_COMPILE="arc-linux-uclibc-"
+CONFIG_CROSS_COMPILE="arc-linux-"
# CONFIG_LOCALVERSION_AUTO is not set
CONFIG_DEFAULT_HOSTNAME="ARCLinux"
# CONFIG_SWAP is not set
diff --git a/arch/arc/configs/nsimosci_hs_smp_defconfig b/arch/arc/configs/nsimosci_hs_smp_defconfig
index 51784837daae..b01b659168ea 100644
--- a/arch/arc/configs/nsimosci_hs_smp_defconfig
+++ b/arch/arc/configs/nsimosci_hs_smp_defconfig
@@ -1,4 +1,4 @@
-CONFIG_CROSS_COMPILE="arc-linux-uclibc-"
+CONFIG_CROSS_COMPILE="arc-linux-"
CONFIG_DEFAULT_HOSTNAME="ARCLinux"
# CONFIG_SWAP is not set
CONFIG_SYSVIPC=y
diff --git a/arch/arc/configs/vdk_hs38_defconfig b/arch/arc/configs/vdk_hs38_defconfig
index ef35ef3923dd..a07f20de221b 100644
--- a/arch/arc/configs/vdk_hs38_defconfig
+++ b/arch/arc/configs/vdk_hs38_defconfig
@@ -1,4 +1,4 @@
-CONFIG_CROSS_COMPILE="arc-linux-uclibc-"
+CONFIG_CROSS_COMPILE="arc-linux-"
# CONFIG_LOCALVERSION_AUTO is not set
CONFIG_DEFAULT_HOSTNAME="ARCLinux"
# CONFIG_CROSS_MEMORY_ATTACH is not set
diff --git a/arch/arc/configs/vdk_hs38_smp_defconfig b/arch/arc/configs/vdk_hs38_smp_defconfig
index 634509e5e572..f36c047b33ca 100644
--- a/arch/arc/configs/vdk_hs38_smp_defconfig
+++ b/arch/arc/configs/vdk_hs38_smp_defconfig
@@ -1,4 +1,4 @@
-CONFIG_CROSS_COMPILE="arc-linux-uclibc-"
+CONFIG_CROSS_COMPILE="arc-linux-"
# CONFIG_LOCALVERSION_AUTO is not set
CONFIG_DEFAULT_HOSTNAME="ARCLinux"
# CONFIG_CROSS_MEMORY_ATTACH is not set
diff --git a/arch/arc/include/asm/cache.h b/arch/arc/include/asm/cache.h
index abf06e81c929..210ef3e72332 100644
--- a/arch/arc/include/asm/cache.h
+++ b/arch/arc/include/asm/cache.h
@@ -62,9 +62,7 @@ extern int ioc_exists;
#define ARC_REG_IC_IVIC 0x10
#define ARC_REG_IC_CTRL 0x11
#define ARC_REG_IC_IVIL 0x19
-#if defined(CONFIG_ARC_MMU_V3) || defined(CONFIG_ARC_MMU_V4)
#define ARC_REG_IC_PTAG 0x1E
-#endif
#define ARC_REG_IC_PTAG_HI 0x1F
/* Bit val in IC_CTRL */
diff --git a/arch/arc/include/asm/irqflags-arcv2.h b/arch/arc/include/asm/irqflags-arcv2.h
index ad481c24070d..258b0e5ad332 100644
--- a/arch/arc/include/asm/irqflags-arcv2.h
+++ b/arch/arc/include/asm/irqflags-arcv2.h
@@ -37,6 +37,9 @@
#define ISA_INIT_STATUS_BITS (STATUS_IE_MASK | STATUS_AD_MASK | \
(ARCV2_IRQ_DEF_PRIO << 1))
+/* SLEEP needs default irq priority (<=) which can interrupt the doze */
+#define ISA_SLEEP_ARG (0x10 | ARCV2_IRQ_DEF_PRIO)
+
#ifndef __ASSEMBLY__
/*
diff --git a/arch/arc/include/asm/irqflags-compact.h b/arch/arc/include/asm/irqflags-compact.h
index d8c608174617..c1d36458bfb7 100644
--- a/arch/arc/include/asm/irqflags-compact.h
+++ b/arch/arc/include/asm/irqflags-compact.h
@@ -43,6 +43,8 @@
#define ISA_INIT_STATUS_BITS STATUS_IE_MASK
+#define ISA_SLEEP_ARG 0x3
+
#ifndef __ASSEMBLY__
/******************************************************************
diff --git a/arch/arc/include/asm/mach_desc.h b/arch/arc/include/asm/mach_desc.h
index 6ff657a904b6..c28e6c347b49 100644
--- a/arch/arc/include/asm/mach_desc.h
+++ b/arch/arc/include/asm/mach_desc.h
@@ -23,7 +23,7 @@
* @dt_compat: Array of device tree 'compatible' strings
* (XXX: although only 1st entry is looked at)
* @init_early: Very early callback [called from setup_arch()]
- * @init_cpu_smp: for each CPU as it is coming up (SMP as well as UP)
+ * @init_per_cpu: for each CPU as it is coming up (SMP as well as UP)
* [(M):init_IRQ(), (o):start_kernel_secondary()]
* @init_machine: arch initcall level callback (e.g. populate static
* platform devices or parse Devicetree)
@@ -35,7 +35,7 @@ struct machine_desc {
const char **dt_compat;
void (*init_early)(void);
#ifdef CONFIG_SMP
- void (*init_cpu_smp)(unsigned int);
+ void (*init_per_cpu)(unsigned int);
#endif
void (*init_machine)(void);
void (*init_late)(void);
diff --git a/arch/arc/include/asm/smp.h b/arch/arc/include/asm/smp.h
index 133c867d15af..991380438d6b 100644
--- a/arch/arc/include/asm/smp.h
+++ b/arch/arc/include/asm/smp.h
@@ -48,7 +48,7 @@ extern int smp_ipi_irq_setup(int cpu, int irq);
* @init_early_smp: A SMP specific h/w block can init itself
* Could be common across platforms so not covered by
* mach_desc->init_early()
- * @init_irq_cpu: Called for each core so SMP h/w block driver can do
+ * @init_per_cpu: Called for each core so SMP h/w block driver can do
* any needed setup per cpu (e.g. IPI request)
* @cpu_kick: For Master to kickstart a cpu (optionally at a PC)
* @ipi_send: To send IPI to a @cpu
@@ -57,7 +57,7 @@ extern int smp_ipi_irq_setup(int cpu, int irq);
struct plat_smp_ops {
const char *info;
void (*init_early_smp)(void);
- void (*init_irq_cpu)(int cpu);
+ void (*init_per_cpu)(int cpu);
void (*cpu_kick)(int cpu, unsigned long pc);
void (*ipi_send)(int cpu);
void (*ipi_clear)(int irq);
diff --git a/arch/arc/include/asm/unwind.h b/arch/arc/include/asm/unwind.h
index 7ca628b6ee2a..c11a25bb8158 100644
--- a/arch/arc/include/asm/unwind.h
+++ b/arch/arc/include/asm/unwind.h
@@ -112,7 +112,6 @@ struct unwind_frame_info {
extern int arc_unwind(struct unwind_frame_info *frame);
extern void arc_unwind_init(void);
-extern void arc_unwind_setup(void);
extern void *unwind_add_table(struct module *module, const void *table_start,
unsigned long table_size);
extern void unwind_remove_table(void *handle, int init_only);
@@ -152,9 +151,6 @@ static inline void arc_unwind_init(void)
{
}
-static inline void arc_unwind_setup(void)
-{
-}
#define unwind_add_table(a, b, c)
#define unwind_remove_table(a, b)
diff --git a/arch/arc/kernel/ctx_sw.c b/arch/arc/kernel/ctx_sw.c
index c14a5bea0c76..5d446df2c413 100644
--- a/arch/arc/kernel/ctx_sw.c
+++ b/arch/arc/kernel/ctx_sw.c
@@ -58,8 +58,6 @@ __switch_to(struct task_struct *prev_task, struct task_struct *next_task)
"st sp, [r24] \n\t"
#endif
- "sync \n\t"
-
/*
* setup _current_task with incoming tsk.
* optionally, set r25 to that as well
diff --git a/arch/arc/kernel/ctx_sw_asm.S b/arch/arc/kernel/ctx_sw_asm.S
index e248594097e7..e6890b1f8650 100644
--- a/arch/arc/kernel/ctx_sw_asm.S
+++ b/arch/arc/kernel/ctx_sw_asm.S
@@ -44,9 +44,6 @@ __switch_to:
* don't need to do anything special to return it
*/
- /* hardware memory barrier */
- sync
-
/*
* switch to new task, contained in r1
* Temp reg r3 is required to get the ptr to store val
diff --git a/arch/arc/kernel/intc-arcv2.c b/arch/arc/kernel/intc-arcv2.c
index 26c156827479..0394f9f61b46 100644
--- a/arch/arc/kernel/intc-arcv2.c
+++ b/arch/arc/kernel/intc-arcv2.c
@@ -106,10 +106,21 @@ static struct irq_chip arcv2_irq_chip = {
static int arcv2_irq_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{
- if (irq == TIMER0_IRQ || irq == IPI_IRQ)
+ /*
+ * core intc IRQs [16, 23]:
+ * Statically assigned always private-per-core (Timers, WDT, IPI, PCT)
+ */
+ if (hw < 24) {
+ /*
+ * A subsequent request_percpu_irq() fails if percpu_devid is
+ * not set. That in turns sets NOAUTOEN, meaning each core needs
+ * to call enable_percpu_irq()
+ */
+ irq_set_percpu_devid(irq);
irq_set_chip_and_handler(irq, &arcv2_irq_chip, handle_percpu_irq);
- else
+ } else {
irq_set_chip_and_handler(irq, &arcv2_irq_chip, handle_level_irq);
+ }
return 0;
}
diff --git a/arch/arc/kernel/irq.c b/arch/arc/kernel/irq.c
index 2ee226546c6a..ba17f85285cf 100644
--- a/arch/arc/kernel/irq.c
+++ b/arch/arc/kernel/irq.c
@@ -29,11 +29,11 @@ void __init init_IRQ(void)
#ifdef CONFIG_SMP
/* a SMP H/w block could do IPI IRQ request here */
- if (plat_smp_ops.init_irq_cpu)
- plat_smp_ops.init_irq_cpu(smp_processor_id());
+ if (plat_smp_ops.init_per_cpu)
+ plat_smp_ops.init_per_cpu(smp_processor_id());
- if (machine_desc->init_cpu_smp)
- machine_desc->init_cpu_smp(smp_processor_id());
+ if (machine_desc->init_per_cpu)
+ machine_desc->init_per_cpu(smp_processor_id());
#endif
}
@@ -51,6 +51,18 @@ void arch_do_IRQ(unsigned int irq, struct pt_regs *regs)
set_irq_regs(old_regs);
}
+/*
+ * API called for requesting percpu interrupts - called by each CPU
+ * - For boot CPU, actually request the IRQ with genirq core + enables
+ * - For subsequent callers only enable called locally
+ *
+ * Relies on being called by boot cpu first (i.e. request called ahead) of
+ * any enable as expected by genirq. Hence Suitable only for TIMER, IPI
+ * which are guaranteed to be setup on boot core first.
+ * Late probed peripherals such as perf can't use this as there no guarantee
+ * of being called on boot CPU first.
+ */
+
void arc_request_percpu_irq(int irq, int cpu,
irqreturn_t (*isr)(int irq, void *dev),
const char *irq_nm,
@@ -60,14 +72,17 @@ void arc_request_percpu_irq(int irq, int cpu,
if (!cpu) {
int rc;
+#ifdef CONFIG_ISA_ARCOMPACT
/*
- * These 2 calls are essential to making percpu IRQ APIs work
- * Ideally these details could be hidden in irq chip map function
- * but the issue is IPIs IRQs being static (non-DT) and platform
- * specific, so we can't identify them there.
+ * A subsequent request_percpu_irq() fails if percpu_devid is
+ * not set. That in turns sets NOAUTOEN, meaning each core needs
+ * to call enable_percpu_irq()
+ *
+ * For ARCv2, this is done in irq map function since we know
+ * which irqs are strictly per cpu
*/
irq_set_percpu_devid(irq);
- irq_modify_status(irq, IRQ_NOAUTOEN, 0); /* @irq, @clr, @set */
+#endif
rc = request_percpu_irq(irq, isr, irq_nm, percpu_dev);
if (rc)
diff --git a/arch/arc/kernel/mcip.c b/arch/arc/kernel/mcip.c
index 74a9b074ac3e..bd237acdf4f2 100644
--- a/arch/arc/kernel/mcip.c
+++ b/arch/arc/kernel/mcip.c
@@ -132,7 +132,7 @@ static void mcip_probe_n_setup(void)
struct plat_smp_ops plat_smp_ops = {
.info = smp_cpuinfo_buf,
.init_early_smp = mcip_probe_n_setup,
- .init_irq_cpu = mcip_setup_per_cpu,
+ .init_per_cpu = mcip_setup_per_cpu,
.ipi_send = mcip_ipi_send,
.ipi_clear = mcip_ipi_clear,
};
diff --git a/arch/arc/kernel/perf_event.c b/arch/arc/kernel/perf_event.c
index 0c08bb1ce15a..8b134cfe5e1f 100644
--- a/arch/arc/kernel/perf_event.c
+++ b/arch/arc/kernel/perf_event.c
@@ -428,12 +428,11 @@ static irqreturn_t arc_pmu_intr(int irq, void *dev)
#endif /* CONFIG_ISA_ARCV2 */
-void arc_cpu_pmu_irq_init(void)
+static void arc_cpu_pmu_irq_init(void *data)
{
- struct arc_pmu_cpu *pmu_cpu = this_cpu_ptr(&arc_pmu_cpu);
+ int irq = *(int *)data;
- arc_request_percpu_irq(arc_pmu->irq, smp_processor_id(), arc_pmu_intr,
- "ARC perf counters", pmu_cpu);
+ enable_percpu_irq(irq, IRQ_TYPE_NONE);
/* Clear all pending interrupt flags */
write_aux_reg(ARC_REG_PCT_INT_ACT, 0xffffffff);
@@ -515,7 +514,6 @@ static int arc_pmu_device_probe(struct platform_device *pdev)
if (has_interrupts) {
int irq = platform_get_irq(pdev, 0);
- unsigned long flags;
if (irq < 0) {
pr_err("Cannot get IRQ number for the platform\n");
@@ -524,24 +522,12 @@ static int arc_pmu_device_probe(struct platform_device *pdev)
arc_pmu->irq = irq;
- /*
- * arc_cpu_pmu_irq_init() needs to be called on all cores for
- * their respective local PMU.
- * However we use opencoded on_each_cpu() to ensure it is called
- * on core0 first, so that arc_request_percpu_irq() sets up
- * AUTOEN etc. Otherwise enable_percpu_irq() fails to enable
- * perf IRQ on non master cores.
- * see arc_request_percpu_irq()
- */
- preempt_disable();
- local_irq_save(flags);
- arc_cpu_pmu_irq_init();
- local_irq_restore(flags);
- smp_call_function((smp_call_func_t)arc_cpu_pmu_irq_init, 0, 1);
- preempt_enable();
-
- /* Clean all pending interrupt flags */
- write_aux_reg(ARC_REG_PCT_INT_ACT, 0xffffffff);
+ /* intc map function ensures irq_set_percpu_devid() called */
+ request_percpu_irq(irq, arc_pmu_intr, "ARC perf counters",
+ this_cpu_ptr(&arc_pmu_cpu));
+
+ on_each_cpu(arc_cpu_pmu_irq_init, &irq, 1);
+
} else
arc_pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
diff --git a/arch/arc/kernel/process.c b/arch/arc/kernel/process.c
index 91d5a0f1f3f7..a3f750e76b68 100644
--- a/arch/arc/kernel/process.c
+++ b/arch/arc/kernel/process.c
@@ -44,11 +44,10 @@ SYSCALL_DEFINE0(arc_gettls)
void arch_cpu_idle(void)
{
/* sleep, but enable all interrupts before committing */
- if (is_isa_arcompact()) {
- __asm__("sleep 0x3");
- } else {
- __asm__("sleep 0x10");
- }
+ __asm__ __volatile__(
+ "sleep %0 \n"
+ :
+ :"I"(ISA_SLEEP_ARG)); /* can't be "r" has to be embedded const */
}
asmlinkage void ret_from_fork(void);
diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c
index c33e77c0ad3e..e1b87444ea9a 100644
--- a/arch/arc/kernel/setup.c
+++ b/arch/arc/kernel/setup.c
@@ -429,7 +429,6 @@ void __init setup_arch(char **cmdline_p)
#endif
arc_unwind_init();
- arc_unwind_setup();
}
static int __init customize_machine(void)
diff --git a/arch/arc/kernel/smp.c b/arch/arc/kernel/smp.c
index 580587805fa3..ef6e9e15b82a 100644
--- a/arch/arc/kernel/smp.c
+++ b/arch/arc/kernel/smp.c
@@ -132,11 +132,11 @@ void start_kernel_secondary(void)
pr_info("## CPU%u LIVE ##: Executing Code...\n", cpu);
/* Some SMP H/w setup - for each cpu */
- if (plat_smp_ops.init_irq_cpu)
- plat_smp_ops.init_irq_cpu(cpu);
+ if (plat_smp_ops.init_per_cpu)
+ plat_smp_ops.init_per_cpu(cpu);
- if (machine_desc->init_cpu_smp)
- machine_desc->init_cpu_smp(cpu);
+ if (machine_desc->init_per_cpu)
+ machine_desc->init_per_cpu(cpu);
arc_local_timer_setup();
diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c
index 93c6ea52b671..0587bf121d11 100644
--- a/arch/arc/kernel/unwind.c
+++ b/arch/arc/kernel/unwind.c
@@ -170,6 +170,23 @@ static struct unwind_table *find_table(unsigned long pc)
static unsigned long read_pointer(const u8 **pLoc,
const void *end, signed ptrType);
+static void init_unwind_hdr(struct unwind_table *table,
+ void *(*alloc) (unsigned long));
+
+/*
+ * wrappers for header alloc (vs. calling one vs. other at call site)
+ * to elide section mismatches warnings
+ */
+static void *__init unw_hdr_alloc_early(unsigned long sz)
+{
+ return __alloc_bootmem_nopanic(sz, sizeof(unsigned int),
+ MAX_DMA_ADDRESS);
+}
+
+static void *unw_hdr_alloc(unsigned long sz)
+{
+ return kmalloc(sz, GFP_KERNEL);
+}
static void init_unwind_table(struct unwind_table *table, const char *name,
const void *core_start, unsigned long core_size,
@@ -209,6 +226,8 @@ void __init arc_unwind_init(void)
__start_unwind, __end_unwind - __start_unwind,
NULL, 0);
/*__start_unwind_hdr, __end_unwind_hdr - __start_unwind_hdr);*/
+
+ init_unwind_hdr(&root_table, unw_hdr_alloc_early);
}
static const u32 bad_cie, not_fde;
@@ -241,8 +260,8 @@ static void swap_eh_frame_hdr_table_entries(void *p1, void *p2, int size)
e2->fde = v;
}
-static void __init setup_unwind_table(struct unwind_table *table,
- void *(*alloc) (unsigned long))
+static void init_unwind_hdr(struct unwind_table *table,
+ void *(*alloc) (unsigned long))
{
const u8 *ptr;
unsigned long tableSize = table->size, hdrSize;
@@ -277,10 +296,10 @@ static void __init setup_unwind_table(struct unwind_table *table,
if (cie == &not_fde)
continue;
if (cie == NULL || cie == &bad_cie)
- return;
+ goto ret_err;
ptrType = fde_pointer_type(cie);
if (ptrType < 0)
- return;
+ goto ret_err;
ptr = (const u8 *)(fde + 2);
if (!read_pointer(&ptr, (const u8 *)(fde + 1) + *fde,
@@ -296,13 +315,15 @@ static void __init setup_unwind_table(struct unwind_table *table,
}
if (tableSize || !n)
- return;
+ goto ret_err;
hdrSize = 4 + sizeof(unsigned long) + sizeof(unsigned int)
+ 2 * n * sizeof(unsigned long);
+
header = alloc(hdrSize);
if (!header)
- return;
+ goto ret_err;
+
header->version = 1;
header->eh_frame_ptr_enc = DW_EH_PE_abs | DW_EH_PE_native;
header->fde_count_enc = DW_EH_PE_abs | DW_EH_PE_data4;
@@ -340,18 +361,10 @@ static void __init setup_unwind_table(struct unwind_table *table,
table->hdrsz = hdrSize;
smp_wmb();
table->header = (const void *)header;
-}
-
-static void *__init balloc(unsigned long sz)
-{
- return __alloc_bootmem_nopanic(sz,
- sizeof(unsigned int),
- __pa(MAX_DMA_ADDRESS));
-}
+ return;
-void __init arc_unwind_setup(void)
-{
- setup_unwind_table(&root_table, balloc);
+ret_err:
+ panic("Attention !!! Dwarf FDE parsing errors\n");;
}
#ifdef CONFIG_MODULES
@@ -372,11 +385,13 @@ void *unwind_add_table(struct module *module, const void *table_start,
return NULL;
init_unwind_table(table, module->name,
- module->module_core, module->core_size,
- module->module_init, module->init_size,
+ module->core_layout.base, module->core_layout.size,
+ module->init_layout.base, module->init_layout.size,
table_start, table_size,
NULL, 0);
+ init_unwind_hdr(table, unw_hdr_alloc);
+
#ifdef UNWIND_DEBUG
unw_debug("Table added for [%s] %lx %lx\n",
module->name, table->core.pc, table->core.range);
@@ -439,6 +454,7 @@ void unwind_remove_table(void *handle, int init_only)
info.init_only = init_only;
unlink_table(&info); /* XXX: SMP */
+ kfree(table->header);
kfree(table);
}
@@ -588,9 +604,6 @@ static signed fde_pointer_type(const u32 *cie)
const u8 *ptr = (const u8 *)(cie + 2);
unsigned version = *ptr;
- if (version != 1)
- return -1; /* unsupported */
-
if (*++ptr) {
const char *aug;
const u8 *end = (const u8 *)(cie + 1) + *cie;
@@ -986,42 +999,13 @@ int arc_unwind(struct unwind_frame_info *frame)
(const u8 *)(fde +
1) +
*fde, ptrType);
- if (pc >= endLoc)
+ if (pc >= endLoc) {
fde = NULL;
- } else
- fde = NULL;
- }
- if (fde == NULL) {
- for (fde = table->address, tableSize = table->size;
- cie = NULL, tableSize > sizeof(*fde)
- && tableSize - sizeof(*fde) >= *fde;
- tableSize -= sizeof(*fde) + *fde,
- fde += 1 + *fde / sizeof(*fde)) {
- cie = cie_for_fde(fde, table);
- if (cie == &bad_cie) {
cie = NULL;
- break;
}
- if (cie == NULL
- || cie == &not_fde
- || (ptrType = fde_pointer_type(cie)) < 0)
- continue;
- ptr = (const u8 *)(fde + 2);
- startLoc = read_pointer(&ptr,
- (const u8 *)(fde + 1) +
- *fde, ptrType);
- if (!startLoc)
- continue;
- if (!(ptrType & DW_EH_PE_indirect))
- ptrType &=
- DW_EH_PE_FORM | DW_EH_PE_signed;
- endLoc =
- startLoc + read_pointer(&ptr,
- (const u8 *)(fde +
- 1) +
- *fde, ptrType);
- if (pc >= startLoc && pc < endLoc)
- break;
+ } else {
+ fde = NULL;
+ cie = NULL;
}
}
}
@@ -1031,9 +1015,7 @@ int arc_unwind(struct unwind_frame_info *frame)
ptr = (const u8 *)(cie + 2);
end = (const u8 *)(cie + 1) + *cie;
frame->call_frame = 1;
- if ((state.version = *ptr) != 1)
- cie = NULL; /* unsupported version */
- else if (*++ptr) {
+ if (*++ptr) {
/* check if augmentation size is first (thus present) */
if (*ptr == 'z') {
while (++ptr < end && *ptr) {
diff --git a/arch/arc/mm/highmem.c b/arch/arc/mm/highmem.c
index 065ee6bfa82a..92dd92cad7f9 100644
--- a/arch/arc/mm/highmem.c
+++ b/arch/arc/mm/highmem.c
@@ -111,7 +111,7 @@ void __kunmap_atomic(void *kv)
}
EXPORT_SYMBOL(__kunmap_atomic);
-noinline pte_t *alloc_kmap_pgtable(unsigned long kvaddr)
+static noinline pte_t * __init alloc_kmap_pgtable(unsigned long kvaddr)
{
pgd_t *pgd_k;
pud_t *pud_k;
@@ -127,7 +127,7 @@ noinline pte_t *alloc_kmap_pgtable(unsigned long kvaddr)
return pte_k;
}
-void kmap_init(void)
+void __init kmap_init(void)
{
/* Due to recursive include hell, we can't do this in processor.h */
BUILD_BUG_ON(PAGE_OFFSET < (VMALLOC_END + FIXMAP_SIZE + PKMAP_SIZE));
diff --git a/arch/arc/mm/init.c b/arch/arc/mm/init.c
index a9305b5a2cd4..7d2c4fbf4f22 100644
--- a/arch/arc/mm/init.c
+++ b/arch/arc/mm/init.c
@@ -51,7 +51,9 @@ void __init early_init_dt_add_memory_arch(u64 base, u64 size)
int in_use = 0;
if (!low_mem_sz) {
- BUG_ON(base != low_mem_start);
+ if (base != low_mem_start)
+ panic("CONFIG_LINUX_LINK_BASE != DT memory { }");
+
low_mem_sz = size;
in_use = 1;
} else {
diff --git a/arch/arc/mm/tlb.c b/arch/arc/mm/tlb.c
index 0ee739846847..daf2bf52b984 100644
--- a/arch/arc/mm/tlb.c
+++ b/arch/arc/mm/tlb.c
@@ -619,10 +619,10 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long vaddr_unaligned,
int dirty = !test_and_set_bit(PG_dc_clean, &page->flags);
if (dirty) {
- /* wback + inv dcache lines */
+ /* wback + inv dcache lines (K-mapping) */
__flush_dcache_page(paddr, paddr);
- /* invalidate any existing icache lines */
+ /* invalidate any existing icache lines (U-mapping) */
if (vma->vm_flags & VM_EXEC)
__inv_icache_page(paddr, vaddr);
}
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 0365cbbc9179..4e489cc5c45e 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -2,6 +2,7 @@ config ARM
bool
default y
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
+ select ARCH_HAS_DEVMEM_IS_ALLOWED
select ARCH_HAS_ELF_RANDOMIZE
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
select ARCH_HAVE_CUSTOM_GPIO_H
@@ -20,6 +21,7 @@ config ARM
select GENERIC_ALLOCATOR
select GENERIC_ATOMIC64 if (CPU_V7M || CPU_V6 || !CPU_32v6K || !AEABI)
select GENERIC_CLOCKEVENTS_BROADCAST if SMP
+ select GENERIC_EARLY_IOREMAP
select GENERIC_IDLE_POLL_SETUP
select GENERIC_IRQ_PROBE
select GENERIC_IRQ_SHOW
@@ -33,10 +35,12 @@ config ARM
select HARDIRQS_SW_RESEND
select HAVE_ARCH_AUDITSYSCALL if (AEABI && !OABI_COMPAT)
select HAVE_ARCH_BITREVERSE if (CPU_32v7M || CPU_32v7) && !CPU_32v6
- select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL && !CPU_ENDIAN_BE32
- select HAVE_ARCH_KGDB if !CPU_ENDIAN_BE32
+ select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL && !CPU_ENDIAN_BE32 && MMU
+ select HAVE_ARCH_KGDB if !CPU_ENDIAN_BE32 && MMU
+ select HAVE_ARCH_MMAP_RND_BITS if MMU
select HAVE_ARCH_SECCOMP_FILTER if (AEABI && !OABI_COMPAT)
select HAVE_ARCH_TRACEHOOK
+ select HAVE_ARM_SMCCC if CPU_V7
select HAVE_BPF_JIT
select HAVE_CC_STACKPROTECTOR
select HAVE_CONTEXT_TRACKING
@@ -45,7 +49,7 @@ config ARM
select HAVE_DMA_API_DEBUG
select HAVE_DMA_ATTRS
select HAVE_DMA_CONTIGUOUS if MMU
- select HAVE_DYNAMIC_FTRACE if (!XIP_KERNEL) && !CPU_ENDIAN_BE32
+ select HAVE_DYNAMIC_FTRACE if (!XIP_KERNEL) && !CPU_ENDIAN_BE32 && MMU
select HAVE_EFFICIENT_UNALIGNED_ACCESS if (CPU_V6 || CPU_V6K || CPU_V7) && MMU
select HAVE_FTRACE_MCOUNT_RECORD if (!XIP_KERNEL)
select HAVE_FUNCTION_GRAPH_TRACER if (!THUMB2_KERNEL)
@@ -76,6 +80,8 @@ config ARM
select IRQ_FORCED_THREADING
select MODULES_USE_ELF_REL
select NO_BOOTMEM
+ select OF_EARLY_FLATTREE if OF
+ select OF_RESERVED_MEM if OF
select OLD_SIGACTION
select OLD_SIGSUSPEND3
select PERF_USE_VMALLOC
@@ -306,6 +312,14 @@ config MMU
Select if you want MMU-based virtualised addressing space
support by paged memory management. If unsure, say 'Y'.
+config ARCH_MMAP_RND_BITS_MIN
+ default 8
+
+config ARCH_MMAP_RND_BITS_MAX
+ default 14 if PAGE_OFFSET=0x40000000
+ default 15 if PAGE_OFFSET=0x80000000
+ default 16
+
#
# The "ARM system type" choice list is ordered alphabetically by option
# text. Please add new entries in the option alphabetic order.
@@ -609,6 +623,7 @@ config ARCH_PXA
select AUTO_ZRELADDR
select COMMON_CLK
select CLKDEV_LOOKUP
+ select CLKSRC_PXA
select CLKSRC_MMIO
select CLKSRC_OF
select GENERIC_CLOCKEVENTS
@@ -648,6 +663,8 @@ config ARCH_SA1100
select ARCH_SPARSEMEM_ENABLE
select CLKDEV_LOOKUP
select CLKSRC_MMIO
+ select CLKSRC_PXA
+ select CLKSRC_OF if OF
select CPU_FREQ
select CPU_SA1100
select GENERIC_CLOCKEVENTS
@@ -797,6 +814,7 @@ config ARCH_VIRT
bool "Dummy Virtual Machine" if ARCH_MULTI_V7
select ARM_AMBA
select ARM_GIC
+ select ARM_GIC_V2M if PCI_MSI
select ARM_GIC_V3
select ARM_PSCI
select HAVE_ARM_ARCH_TIMER
@@ -1420,7 +1438,7 @@ config BIG_LITTLE
config BL_SWITCHER
bool "big.LITTLE switcher support"
- depends on BIG_LITTLE && MCPM && HOTPLUG_CPU
+ depends on BIG_LITTLE && MCPM && HOTPLUG_CPU && ARM_GIC
select ARM_CPU_SUSPEND
select CPU_PM
help
@@ -1479,7 +1497,7 @@ config HOTPLUG_CPU
config ARM_PSCI
bool "Support for the ARM Power State Coordination Interface (PSCI)"
- depends on CPU_V7
+ depends on HAVE_ARM_SMCCC
select ARM_PSCI_FW
help
Say Y here if you want Linux to communicate with system firmware
@@ -1602,6 +1620,24 @@ config THUMB2_AVOID_R_ARM_THM_JUMP11
config ARM_ASM_UNIFIED
bool
+config ARM_PATCH_IDIV
+ bool "Runtime patch udiv/sdiv instructions into __aeabi_{u}idiv()"
+ depends on CPU_32v7 && !XIP_KERNEL
+ default y
+ help
+ The ARM compiler inserts calls to __aeabi_idiv() and
+ __aeabi_uidiv() when it needs to perform division on signed
+ and unsigned integers. Some v7 CPUs have support for the sdiv
+ and udiv instructions that can be used to implement those
+ functions.
+
+ Enabling this option allows the kernel to modify itself to
+ replace the first two instructions of these library functions
+ with the sdiv or udiv plus "bx lr" instructions when the CPU
+ it is running on supports them. Typically this will be faster
+ and less power intensive than running the original library
+ code to do integer division.
+
config AEABI
bool "Use the ARM EABI to compile the kernel"
help
@@ -1798,6 +1834,25 @@ config SWIOTLB
config IOMMU_HELPER
def_bool SWIOTLB
+config PARAVIRT
+ bool "Enable paravirtualization code"
+ help
+ This changes the kernel so it can modify itself when it is run
+ under a hypervisor, potentially improving performance significantly
+ over full virtualization.
+
+config PARAVIRT_TIME_ACCOUNTING
+ bool "Paravirtual steal time accounting"
+ select PARAVIRT
+ default n
+ help
+ Select this option to enable fine granularity task steal time
+ accounting. Time spent executing other tasks in parallel with
+ the current vCPU is discounted from the vCPU power. To account for
+ that, there can be a small performance impact.
+
+ If in doubt, say N here.
+
config XEN_DOM0
def_bool y
depends on XEN
@@ -1811,6 +1866,7 @@ config XEN
select ARCH_DMA_ADDR_T_64BIT
select ARM_PSCI
select SWIOTLB_XEN
+ select PARAVIRT
help
Say Y if you want to run Linux in a Virtual Machine on Xen on ARM.
@@ -1822,8 +1878,6 @@ config USE_OF
bool "Flattened Device Tree support"
select IRQ_DOMAIN
select OF
- select OF_EARLY_FLATTREE
- select OF_RESERVED_MEM
help
Include support for flattened device tree machine descriptions.
@@ -2040,6 +2094,25 @@ config AUTO_ZRELADDR
0xf8000000. This assumes the zImage being placed in the first 128MB
from start of memory.
+config EFI_STUB
+ bool
+
+config EFI
+ bool "UEFI runtime support"
+ depends on OF && !CPU_BIG_ENDIAN && MMU && AUTO_ZRELADDR && !XIP_KERNEL
+ select UCS2_STRING
+ select EFI_PARAMS_FROM_FDT
+ select EFI_STUB
+ select EFI_ARMSTUB
+ select EFI_RUNTIME_WRAPPERS
+ ---help---
+ This option provides support for runtime services provided
+ by UEFI firmware (such as non-volatile variables, realtime
+ clock, and platform reset). A UEFI stub is also provided to
+ allow the kernel to be booted as an EFI application. This
+ is only useful for kernels that may run on systems that have
+ UEFI firmware.
+
endmenu
menu "CPU Power Management"
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 259c0ca9c99a..e356357d86bb 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -15,20 +15,6 @@ config ARM_PTDUMP
kernel.
If in doubt, say "N"
-config STRICT_DEVMEM
- bool "Filter access to /dev/mem"
- depends on MMU
- ---help---
- If this option is disabled, you allow userspace (root) access to all
- of memory, including kernel and userspace memory. Accidental
- access to this is obviously disastrous, but specific access can
- be used by people debugging the kernel.
-
- If this option is switched on, the /dev/mem file only allows
- userspace access to memory mapped peripherals.
-
- If in doubt, say Y.
-
# RMK wants arm kernels compiled with frame pointers or stack unwinding.
# If you know what you are doing and are willing to live without stack
# traces, you can get a slightly smaller kernel by setting this option to
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index 3f9a9ebc77c3..4c23a68a0917 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -167,9 +167,11 @@ if [ $(words $(ZRELADDR)) -gt 1 -a "$(CONFIG_AUTO_ZRELADDR)" = "" ]; then \
false; \
fi
+efi-obj-$(CONFIG_EFI_STUB) := $(objtree)/drivers/firmware/efi/libstub/lib.a
+
$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.$(suffix_y).o \
$(addprefix $(obj)/, $(OBJS)) $(lib1funcs) $(ashldi3) \
- $(bswapsdi2) FORCE
+ $(bswapsdi2) $(efi-obj-y) FORCE
@$(check_for_multiple_zreladdr)
$(call if_changed,ld)
@$(check_for_bad_syms)
diff --git a/arch/arm/boot/compressed/efi-header.S b/arch/arm/boot/compressed/efi-header.S
new file mode 100644
index 000000000000..9d5dc4fda3c1
--- /dev/null
+++ b/arch/arm/boot/compressed/efi-header.S
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2013-2015 Linaro Ltd
+ * Authors: Roy Franz <roy.franz@linaro.org>
+ * Ard Biesheuvel <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.
+ */
+
+ .macro __nop
+#ifdef CONFIG_EFI_STUB
+ @ This is almost but not quite a NOP, since it does clobber the
+ @ condition flags. But it is the best we can do for EFI, since
+ @ PE/COFF expects the magic string "MZ" at offset 0, while the
+ @ ARM/Linux boot protocol expects an executable instruction
+ @ there.
+ .inst 'M' | ('Z' << 8) | (0x1310 << 16) @ tstne r0, #0x4d000
+#else
+ mov r0, r0
+#endif
+ .endm
+
+ .macro __EFI_HEADER
+#ifdef CONFIG_EFI_STUB
+ b __efi_start
+
+ .set start_offset, __efi_start - start
+ .org start + 0x3c
+ @
+ @ The PE header can be anywhere in the file, but for
+ @ simplicity we keep it together with the MSDOS header
+ @ The offset to the PE/COFF header needs to be at offset
+ @ 0x3C in the MSDOS header.
+ @ The only 2 fields of the MSDOS header that are used are this
+ @ PE/COFF offset, and the "MZ" bytes at offset 0x0.
+ @
+ .long pe_header - start @ Offset to the PE header.
+
+pe_header:
+ .ascii "PE\0\0"
+
+coff_header:
+ .short 0x01c2 @ ARM or Thumb
+ .short 2 @ nr_sections
+ .long 0 @ TimeDateStamp
+ .long 0 @ PointerToSymbolTable
+ .long 1 @ NumberOfSymbols
+ .short section_table - optional_header
+ @ SizeOfOptionalHeader
+ .short 0x306 @ Characteristics.
+ @ IMAGE_FILE_32BIT_MACHINE |
+ @ IMAGE_FILE_DEBUG_STRIPPED |
+ @ IMAGE_FILE_EXECUTABLE_IMAGE |
+ @ IMAGE_FILE_LINE_NUMS_STRIPPED
+
+optional_header:
+ .short 0x10b @ PE32 format
+ .byte 0x02 @ MajorLinkerVersion
+ .byte 0x14 @ MinorLinkerVersion
+ .long _end - __efi_start @ SizeOfCode
+ .long 0 @ SizeOfInitializedData
+ .long 0 @ SizeOfUninitializedData
+ .long efi_stub_entry - start @ AddressOfEntryPoint
+ .long start_offset @ BaseOfCode
+ .long 0 @ data
+
+extra_header_fields:
+ .long 0 @ ImageBase
+ .long 0x200 @ SectionAlignment
+ .long 0x200 @ FileAlignment
+ .short 0 @ MajorOperatingSystemVersion
+ .short 0 @ MinorOperatingSystemVersion
+ .short 0 @ MajorImageVersion
+ .short 0 @ MinorImageVersion
+ .short 0 @ MajorSubsystemVersion
+ .short 0 @ MinorSubsystemVersion
+ .long 0 @ Win32VersionValue
+
+ .long _end - start @ SizeOfImage
+ .long start_offset @ SizeOfHeaders
+ .long 0 @ CheckSum
+ .short 0xa @ Subsystem (EFI application)
+ .short 0 @ DllCharacteristics
+ .long 0 @ SizeOfStackReserve
+ .long 0 @ SizeOfStackCommit
+ .long 0 @ SizeOfHeapReserve
+ .long 0 @ SizeOfHeapCommit
+ .long 0 @ LoaderFlags
+ .long 0x6 @ NumberOfRvaAndSizes
+
+ .quad 0 @ ExportTable
+ .quad 0 @ ImportTable
+ .quad 0 @ ResourceTable
+ .quad 0 @ ExceptionTable
+ .quad 0 @ CertificationTable
+ .quad 0 @ BaseRelocationTable
+
+section_table:
+ @
+ @ The EFI application loader requires a relocation section
+ @ because EFI applications must be relocatable. This is a
+ @ dummy section as far as we are concerned.
+ @
+ .ascii ".reloc\0\0"
+ .long 0 @ VirtualSize
+ .long 0 @ VirtualAddress
+ .long 0 @ SizeOfRawData
+ .long 0 @ PointerToRawData
+ .long 0 @ PointerToRelocations
+ .long 0 @ PointerToLineNumbers
+ .short 0 @ NumberOfRelocations
+ .short 0 @ NumberOfLineNumbers
+ .long 0x42100040 @ Characteristics
+
+ .ascii ".text\0\0\0"
+ .long _end - __efi_start @ VirtualSize
+ .long __efi_start @ VirtualAddress
+ .long _edata - __efi_start @ SizeOfRawData
+ .long __efi_start @ PointerToRawData
+ .long 0 @ PointerToRelocations
+ .long 0 @ PointerToLineNumbers
+ .short 0 @ NumberOfRelocations
+ .short 0 @ NumberOfLineNumbers
+ .long 0xe0500020 @ Characteristics
+
+ .align 9
+__efi_start:
+#endif
+ .endm
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
index 06e983f59980..af11c2f8f3b7 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -12,6 +12,8 @@
#include <asm/assembler.h>
#include <asm/v7m.h>
+#include "efi-header.S"
+
AR_CLASS( .arch armv7-a )
M_CLASS( .arch armv7-m )
@@ -126,7 +128,7 @@
start:
.type start,#function
.rept 7
- mov r0, r0
+ __nop
.endr
ARM( mov r0, r0 )
ARM( b 1f )
@@ -139,7 +141,8 @@ start:
.word 0x04030201 @ endianness flag
THUMB( .thumb )
-1:
+1: __EFI_HEADER
+
ARM_BE8( setend be ) @ go BE8 if compiled for BE8
AR_CLASS( mrs r9, cpsr )
#ifdef CONFIG_ARM_VIRT_EXT
@@ -1353,6 +1356,53 @@ __enter_kernel:
reloc_code_end:
+#ifdef CONFIG_EFI_STUB
+ .align 2
+_start: .long start - .
+
+ENTRY(efi_stub_entry)
+ @ allocate space on stack for passing current zImage address
+ @ and for the EFI stub to return of new entry point of
+ @ zImage, as EFI stub may copy the kernel. Pointer address
+ @ is passed in r2. r0 and r1 are passed through from the
+ @ EFI firmware to efi_entry
+ adr ip, _start
+ ldr r3, [ip]
+ add r3, r3, ip
+ stmfd sp!, {r3, lr}
+ mov r2, sp @ pass zImage address in r2
+ bl efi_entry
+
+ @ Check for error return from EFI stub. r0 has FDT address
+ @ or error code.
+ cmn r0, #1
+ beq efi_load_fail
+
+ @ Preserve return value of efi_entry() in r4
+ mov r4, r0
+ bl cache_clean_flush
+ bl cache_off
+
+ @ Set parameters for booting zImage according to boot protocol
+ @ put FDT address in r2, it was returned by efi_entry()
+ @ r1 is the machine type, and r0 needs to be 0
+ mov r0, #0
+ mov r1, #0xFFFFFFFF
+ mov r2, r4
+
+ @ Branch to (possibly) relocated zImage that is in [sp]
+ ldr lr, [sp]
+ ldr ip, =start_offset
+ add lr, lr, ip
+ mov pc, lr @ no mode switch
+
+efi_load_fail:
+ @ Return EFI_LOAD_ERROR to EFI firmware on error.
+ ldr r0, =0x80000001
+ ldmfd sp!, {ip, pc}
+ENDPROC(efi_stub_entry)
+#endif
+
.align
.section ".stack", "aw", %nobits
.L_user_stack: .space 4096
diff --git a/arch/arm/boot/compressed/vmlinux.lds.S b/arch/arm/boot/compressed/vmlinux.lds.S
index 2b60b843ac5e..81c493156ce8 100644
--- a/arch/arm/boot/compressed/vmlinux.lds.S
+++ b/arch/arm/boot/compressed/vmlinux.lds.S
@@ -48,6 +48,13 @@ SECTIONS
*(.rodata)
*(.rodata.*)
}
+ .data : {
+ /*
+ * The EFI stub always executes from RAM, and runs strictly before the
+ * decompressor, so we can make an exception for its r/w data, and keep it
+ */
+ *(.data.efistub)
+ }
.piggydata : {
*(.piggydata)
}
diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi
index d83ff9c9701e..de8791a4d131 100644
--- a/arch/arm/boot/dts/am4372.dtsi
+++ b/arch/arm/boot/dts/am4372.dtsi
@@ -74,7 +74,7 @@
reg = <0x48240200 0x100>;
interrupts = <GIC_PPI 11 IRQ_TYPE_LEVEL_HIGH>;
interrupt-parent = <&gic>;
- clocks = <&dpll_mpu_m2_ck>;
+ clocks = <&mpu_periphclk>;
};
local_timer: timer@48240600 {
@@ -82,7 +82,7 @@
reg = <0x48240600 0x100>;
interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_HIGH>;
interrupt-parent = <&gic>;
- clocks = <&dpll_mpu_m2_ck>;
+ clocks = <&mpu_periphclk>;
};
l2-cache-controller@48242000 {
diff --git a/arch/arm/boot/dts/am43xx-clocks.dtsi b/arch/arm/boot/dts/am43xx-clocks.dtsi
index cc88728d751d..a38af2bfbfcf 100644
--- a/arch/arm/boot/dts/am43xx-clocks.dtsi
+++ b/arch/arm/boot/dts/am43xx-clocks.dtsi
@@ -259,6 +259,14 @@
ti,invert-autoidle-bit;
};
+ mpu_periphclk: mpu_periphclk {
+ #clock-cells = <0>;
+ compatible = "fixed-factor-clock";
+ clocks = <&dpll_mpu_m2_ck>;
+ clock-mult = <1>;
+ clock-div = <2>;
+ };
+
dpll_ddr_ck: dpll_ddr_ck {
#clock-cells = <0>;
compatible = "ti,am3-dpll-clock";
diff --git a/arch/arm/boot/dts/am57xx-beagle-x15.dts b/arch/arm/boot/dts/am57xx-beagle-x15.dts
index d9ba6b879fc1..00352e761b8c 100644
--- a/arch/arm/boot/dts/am57xx-beagle-x15.dts
+++ b/arch/arm/boot/dts/am57xx-beagle-x15.dts
@@ -604,6 +604,7 @@
reg = <0x6f>;
interrupts-extended = <&crossbar_mpu GIC_SPI 2 IRQ_TYPE_EDGE_RISING>,
<&dra7_pmx_core 0x424>;
+ interrupt-names = "irq", "wakeup";
pinctrl-names = "default";
pinctrl-0 = <&mcp79410_pins_default>;
diff --git a/arch/arm/boot/dts/animeo_ip.dts b/arch/arm/boot/dts/animeo_ip.dts
index 4e0ad3b82796..0962f2fa3f6e 100644
--- a/arch/arm/boot/dts/animeo_ip.dts
+++ b/arch/arm/boot/dts/animeo_ip.dts
@@ -155,21 +155,21 @@
label = "keyswitch_in";
gpios = <&pioB 1 GPIO_ACTIVE_HIGH>;
linux,code = <28>;
- gpio-key,wakeup;
+ wakeup-source;
};
error_in {
label = "error_in";
gpios = <&pioB 2 GPIO_ACTIVE_HIGH>;
linux,code = <29>;
- gpio-key,wakeup;
+ wakeup-source;
};
btn {
label = "btn";
gpios = <&pioC 23 GPIO_ACTIVE_HIGH>;
linux,code = <31>;
- gpio-key,wakeup;
+ wakeup-source;
};
};
};
diff --git a/arch/arm/boot/dts/armada-38x.dtsi b/arch/arm/boot/dts/armada-38x.dtsi
index c6a0e9d7f1a9..e8b7f6726772 100644
--- a/arch/arm/boot/dts/armada-38x.dtsi
+++ b/arch/arm/boot/dts/armada-38x.dtsi
@@ -498,6 +498,7 @@
reg = <0x70000 0x4000>;
interrupts-extended = <&mpic 8>;
clocks = <&gateclk 4>;
+ tx-csum-limit = <9800>;
status = "disabled";
};
diff --git a/arch/arm/boot/dts/at91-foxg20.dts b/arch/arm/boot/dts/at91-foxg20.dts
index f89598af4c2b..6bf873e7d96c 100644
--- a/arch/arm/boot/dts/at91-foxg20.dts
+++ b/arch/arm/boot/dts/at91-foxg20.dts
@@ -159,7 +159,7 @@
label = "Button";
gpios = <&pioC 4 GPIO_ACTIVE_LOW>;
linux,code = <0x103>;
- gpio-key,wakeup;
+ wakeup-source;
};
};
};
diff --git a/arch/arm/boot/dts/at91-kizbox.dts b/arch/arm/boot/dts/at91-kizbox.dts
index bf18ece0c027..229e989eb60d 100644
--- a/arch/arm/boot/dts/at91-kizbox.dts
+++ b/arch/arm/boot/dts/at91-kizbox.dts
@@ -24,15 +24,6 @@
};
clocks {
- #address-cells = <1>;
- #size-cells = <1>;
- ranges;
-
- main_clock: clock@0 {
- compatible = "atmel,osc", "fixed-clock";
- clock-frequency = <18432000>;
- };
-
main_xtal {
clock-frequency = <18432000>;
};
@@ -94,14 +85,14 @@
label = "PB_RST";
gpios = <&pioB 30 GPIO_ACTIVE_HIGH>;
linux,code = <0x100>;
- gpio-key,wakeup;
+ wakeup-source;
};
user {
label = "PB_USER";
gpios = <&pioB 31 GPIO_ACTIVE_HIGH>;
linux,code = <0x101>;
- gpio-key,wakeup;
+ wakeup-source;
};
};
diff --git a/arch/arm/boot/dts/at91-kizbox2.dts b/arch/arm/boot/dts/at91-kizbox2.dts
index f0b1563cb3f1..50a14568f094 100644
--- a/arch/arm/boot/dts/at91-kizbox2.dts
+++ b/arch/arm/boot/dts/at91-kizbox2.dts
@@ -171,21 +171,21 @@
label = "PB_PROG";
gpios = <&pioE 27 GPIO_ACTIVE_LOW>;
linux,code = <0x102>;
- gpio-key,wakeup;
+ wakeup-source;
};
reset {
label = "PB_RST";
gpios = <&pioE 29 GPIO_ACTIVE_LOW>;
linux,code = <0x100>;
- gpio-key,wakeup;
+ wakeup-source;
};
user {
label = "PB_USER";
gpios = <&pioE 31 GPIO_ACTIVE_HIGH>;
linux,code = <0x101>;
- gpio-key,wakeup;
+ wakeup-source;
};
};
diff --git a/arch/arm/boot/dts/at91-kizboxmini.dts b/arch/arm/boot/dts/at91-kizboxmini.dts
index 9f72b4932634..9682d105d4d8 100644
--- a/arch/arm/boot/dts/at91-kizboxmini.dts
+++ b/arch/arm/boot/dts/at91-kizboxmini.dts
@@ -98,14 +98,14 @@
label = "PB_PROG";
gpios = <&pioC 17 GPIO_ACTIVE_LOW>;
linux,code = <0x102>;
- gpio-key,wakeup;
+ wakeup-source;
};
reset {
label = "PB_RST";
gpios = <&pioC 16 GPIO_ACTIVE_LOW>;
linux,code = <0x100>;
- gpio-key,wakeup;
+ wakeup-source;
};
};
diff --git a/arch/arm/boot/dts/at91-qil_a9260.dts b/arch/arm/boot/dts/at91-qil_a9260.dts
index a9aef53ab764..4f2eebf4a560 100644
--- a/arch/arm/boot/dts/at91-qil_a9260.dts
+++ b/arch/arm/boot/dts/at91-qil_a9260.dts
@@ -183,7 +183,7 @@
label = "user_pb";
gpios = <&pioB 10 GPIO_ACTIVE_LOW>;
linux,code = <28>;
- gpio-key,wakeup;
+ wakeup-source;
};
};
diff --git a/arch/arm/boot/dts/at91-sama5d2_xplained.dts b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
index e07c2b206beb..e74df327cdd3 100644
--- a/arch/arm/boot/dts/at91-sama5d2_xplained.dts
+++ b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
@@ -45,6 +45,7 @@
/dts-v1/;
#include "sama5d2.dtsi"
#include "sama5d2-pinfunc.h"
+#include <dt-bindings/mfd/atmel-flexcom.h>
/ {
model = "Atmel SAMA5D2 Xplained";
@@ -59,15 +60,6 @@
};
clocks {
- #address-cells = <1>;
- #size-cells = <1>;
- ranges;
-
- main_clock: clock@0 {
- compatible = "atmel,osc", "fixed-clock";
- clock-frequency = <12000000>;
- };
-
slow_xtal {
clock-frequency = <32768>;
};
@@ -91,6 +83,22 @@
status = "okay";
};
+ sdmmc0: sdio-host@a0000000 {
+ bus-width = <8>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_sdmmc0_default>;
+ non-removable;
+ mmc-ddr-1_8v;
+ status = "okay";
+ };
+
+ sdmmc1: sdio-host@b0000000 {
+ bus-width = <4>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_sdmmc1_default>;
+ status = "okay"; /* conflict with qspi0 */
+ };
+
apb {
spi0: spi@f8000000 {
pinctrl-names = "default";
@@ -176,17 +184,55 @@
regulator-name = "VDD_SDHC_1V8";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
+ regulator-always-on;
};
};
};
};
+ flx0: flexcom@f8034000 {
+ atmel,flexcom-mode = <ATMEL_FLEXCOM_MODE_USART>;
+ status = "disabled"; /* conflict with ISC_D2 & ISC_D3 data pins */
+
+ uart5: serial@200 {
+ compatible = "atmel,at91sam9260-usart";
+ reg = <0x200 0x200>;
+ interrupts = <19 IRQ_TYPE_LEVEL_HIGH 7>;
+ clocks = <&flx0_clk>;
+ clock-names = "usart";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_flx0_default>;
+ atmel,fifo-size = <32>;
+ status = "okay";
+ };
+ };
+
uart3: serial@fc008000 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart3_default>;
status = "okay";
};
+ flx4: flexcom@fc018000 {
+ atmel,flexcom-mode = <ATMEL_FLEXCOM_MODE_TWI>;
+ status = "okay";
+
+ i2c2: i2c@600 {
+ compatible = "atmel,sama5d2-i2c";
+ reg = <0x600 0x200>;
+ interrupts = <23 IRQ_TYPE_LEVEL_HIGH 7>;
+ dmas = <0>, <0>;
+ dma-names = "tx", "rx";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&flx4_clk>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_flx4_default>;
+ atmel,fifo-size = <16>;
+ status = "okay";
+ };
+ };
+
i2c1: i2c@fc028000 {
dmas = <0>, <0>;
pinctrl-names = "default";
@@ -201,6 +247,18 @@
};
pinctrl@fc038000 {
+ pinctrl_flx0_default: flx0_default {
+ pinmux = <PIN_PB28__FLEXCOM0_IO0>,
+ <PIN_PB29__FLEXCOM0_IO1>;
+ bias-disable;
+ };
+
+ pinctrl_flx4_default: flx4_default {
+ pinmux = <PIN_PD12__FLEXCOM4_IO0>,
+ <PIN_PD13__FLEXCOM4_IO1>;
+ bias-disable;
+ };
+
pinctrl_i2c0_default: i2c0_default {
pinmux = <PIN_PD21__TWD0>,
<PIN_PD22__TWCK0>;
@@ -227,6 +285,46 @@
bias-disable;
};
+ pinctrl_sdmmc0_default: sdmmc0_default {
+ cmd_data {
+ pinmux = <PIN_PA1__SDMMC0_CMD>,
+ <PIN_PA2__SDMMC0_DAT0>,
+ <PIN_PA3__SDMMC0_DAT1>,
+ <PIN_PA4__SDMMC0_DAT2>,
+ <PIN_PA5__SDMMC0_DAT3>,
+ <PIN_PA6__SDMMC0_DAT4>,
+ <PIN_PA7__SDMMC0_DAT5>,
+ <PIN_PA8__SDMMC0_DAT6>,
+ <PIN_PA9__SDMMC0_DAT7>;
+ bias-pull-up;
+ };
+
+ ck_cd_rstn_vddsel {
+ pinmux = <PIN_PA0__SDMMC0_CK>,
+ <PIN_PA10__SDMMC0_RSTN>,
+ <PIN_PA11__SDMMC0_VDDSEL>,
+ <PIN_PA13__SDMMC0_CD>;
+ bias-disable;
+ };
+ };
+
+ pinctrl_sdmmc1_default: sdmmc1_default {
+ cmd_data {
+ pinmux = <PIN_PA28__SDMMC1_CMD>,
+ <PIN_PA18__SDMMC1_DAT0>,
+ <PIN_PA19__SDMMC1_DAT1>,
+ <PIN_PA20__SDMMC1_DAT2>,
+ <PIN_PA21__SDMMC1_DAT3>;
+ bias-pull-up;
+ };
+
+ conf-ck_cd {
+ pinmux = <PIN_PA22__SDMMC1_CK>,
+ <PIN_PA30__SDMMC1_CD>;
+ bias-disable;
+ };
+ };
+
pinctrl_spi0_default: spi0_default {
pinmux = <PIN_PA14__SPI0_SPCK>,
<PIN_PA15__SPI0_MOSI>,
diff --git a/arch/arm/boot/dts/at91-sama5d3_xplained.dts b/arch/arm/boot/dts/at91-sama5d3_xplained.dts
index 8488ac53d22d..ff888d21c786 100644
--- a/arch/arm/boot/dts/at91-sama5d3_xplained.dts
+++ b/arch/arm/boot/dts/at91-sama5d3_xplained.dts
@@ -315,7 +315,7 @@
label = "PB_USER";
gpios = <&pioE 29 GPIO_ACTIVE_LOW>;
linux,code = <0x104>;
- gpio-key,wakeup;
+ wakeup-source;
};
};
diff --git a/arch/arm/boot/dts/at91-sama5d4_xplained.dts b/arch/arm/boot/dts/at91-sama5d4_xplained.dts
index 45371a1b61b3..131614f28e75 100644
--- a/arch/arm/boot/dts/at91-sama5d4_xplained.dts
+++ b/arch/arm/boot/dts/at91-sama5d4_xplained.dts
@@ -50,7 +50,6 @@
compatible = "atmel,sama5d4-xplained", "atmel,sama5d4", "atmel,sama5";
chosen {
- bootargs = "ignore_loglevel earlyprintk";
stdout-path = "serial0:115200n8";
};
@@ -59,15 +58,6 @@
};
clocks {
- #address-cells = <1>;
- #size-cells = <1>;
- ranges;
-
- main_clock: clock@0 {
- compatible = "atmel,osc", "fixed-clock";
- clock-frequency = <12000000>;
- };
-
slow_xtal {
clock-frequency = <32768>;
};
@@ -235,7 +225,7 @@
label = "pb_user1";
gpios = <&pioE 8 GPIO_ACTIVE_HIGH>;
linux,code = <0x100>;
- gpio-key,wakeup;
+ wakeup-source;
};
};
diff --git a/arch/arm/boot/dts/at91-sama5d4ek.dts b/arch/arm/boot/dts/at91-sama5d4ek.dts
index 6d272c0125e3..2d4a33100af6 100644
--- a/arch/arm/boot/dts/at91-sama5d4ek.dts
+++ b/arch/arm/boot/dts/at91-sama5d4ek.dts
@@ -50,7 +50,6 @@
compatible = "atmel,sama5d4ek", "atmel,sama5d4", "atmel,sama5";
chosen {
- bootargs = "ignore_loglevel earlyprintk";
stdout-path = "serial0:115200n8";
};
@@ -59,15 +58,6 @@
};
clocks {
- #address-cells = <1>;
- #size-cells = <1>;
- ranges;
-
- main_clock: clock@0 {
- compatible = "atmel,osc", "fixed-clock";
- clock-frequency = <12000000>;
- };
-
slow_xtal {
clock-frequency = <32768>;
};
@@ -304,7 +294,7 @@
label = "pb_user1";
gpios = <&pioE 13 GPIO_ACTIVE_HIGH>;
linux,code = <0x100>;
- gpio-key,wakeup;
+ wakeup-source;
};
};
diff --git a/arch/arm/boot/dts/at91rm9200ek.dts b/arch/arm/boot/dts/at91rm9200ek.dts
index 8dab4b75ca97..f90e1c2d3caa 100644
--- a/arch/arm/boot/dts/at91rm9200ek.dts
+++ b/arch/arm/boot/dts/at91rm9200ek.dts
@@ -21,15 +21,6 @@
};
clocks {
- #address-cells = <1>;
- #size-cells = <1>;
- ranges;
-
- main_clock: clock@0 {
- compatible = "atmel,osc", "fixed-clock";
- clock-frequency = <18432000>;
- };
-
slow_xtal {
clock-frequency = <32768>;
};
diff --git a/arch/arm/boot/dts/at91sam9261ek.dts b/arch/arm/boot/dts/at91sam9261ek.dts
index 2e92ac020f23..55bd51f07fa6 100644
--- a/arch/arm/boot/dts/at91sam9261ek.dts
+++ b/arch/arm/boot/dts/at91sam9261ek.dts
@@ -22,15 +22,6 @@
};
clocks {
- #address-cells = <1>;
- #size-cells = <1>;
- ranges;
-
- main_clock: clock@0 {
- compatible = "atmel,osc", "fixed-clock";
- clock-frequency = <18432000>;
- };
-
slow_xtal {
clock-frequency = <32768>;
};
@@ -149,7 +140,7 @@
ti,debounce-tol = /bits/ 16 <65535>;
ti,debounce-max = /bits/ 16 <1>;
- linux,wakeup;
+ wakeup-source;
};
};
@@ -193,28 +184,28 @@
label = "button_0";
gpios = <&pioA 27 GPIO_ACTIVE_LOW>;
linux,code = <256>;
- gpio-key,wakeup;
+ wakeup-source;
};
button_1 {
label = "button_1";
gpios = <&pioA 26 GPIO_ACTIVE_LOW>;
linux,code = <257>;
- gpio-key,wakeup;
+ wakeup-source;
};
button_2 {
label = "button_2";
gpios = <&pioA 25 GPIO_ACTIVE_LOW>;
linux,code = <258>;
- gpio-key,wakeup;
+ wakeup-source;
};
button_3 {
label = "button_3";
gpios = <&pioA 24 GPIO_ACTIVE_LOW>;
linux,code = <259>;
- gpio-key,wakeup;
+ wakeup-source;
};
};
};
diff --git a/arch/arm/boot/dts/at91sam9263ek.dts b/arch/arm/boot/dts/at91sam9263ek.dts
index 23381276ffb8..59df9d73d276 100644
--- a/arch/arm/boot/dts/at91sam9263ek.dts
+++ b/arch/arm/boot/dts/at91sam9263ek.dts
@@ -22,15 +22,6 @@
};
clocks {
- #address-cells = <1>;
- #size-cells = <1>;
- ranges;
-
- main_clock: clock@0 {
- compatible = "atmel,osc", "fixed-clock";
- clock-frequency = <16367660>;
- };
-
slow_xtal {
clock-frequency = <32768>;
};
@@ -213,14 +204,14 @@
label = "left_click";
gpios = <&pioC 5 GPIO_ACTIVE_LOW>;
linux,code = <272>;
- gpio-key,wakeup;
+ wakeup-source;
};
right_click {
label = "right_click";
gpios = <&pioC 4 GPIO_ACTIVE_LOW>;
linux,code = <273>;
- gpio-key,wakeup;
+ wakeup-source;
};
};
diff --git a/arch/arm/boot/dts/at91sam9g20ek_common.dtsi b/arch/arm/boot/dts/at91sam9g20ek_common.dtsi
index 57548a2c5a1e..e9cc99b6353a 100644
--- a/arch/arm/boot/dts/at91sam9g20ek_common.dtsi
+++ b/arch/arm/boot/dts/at91sam9g20ek_common.dtsi
@@ -19,15 +19,6 @@
};
clocks {
- #address-cells = <1>;
- #size-cells = <1>;
- ranges;
-
- main_clock: clock@0 {
- compatible = "atmel,osc", "fixed-clock";
- clock-frequency = <18432000>;
- };
-
slow_xtal {
clock-frequency = <32768>;
};
@@ -206,14 +197,14 @@
label = "Button 3";
gpios = <&pioA 30 GPIO_ACTIVE_LOW>;
linux,code = <0x103>;
- gpio-key,wakeup;
+ wakeup-source;
};
btn4 {
label = "Button 4";
gpios = <&pioA 31 GPIO_ACTIVE_LOW>;
linux,code = <0x104>;
- gpio-key,wakeup;
+ wakeup-source;
};
};
diff --git a/arch/arm/boot/dts/at91sam9m10g45ek.dts b/arch/arm/boot/dts/at91sam9m10g45ek.dts
index 9d16ef8453c5..2400c99134f7 100644
--- a/arch/arm/boot/dts/at91sam9m10g45ek.dts
+++ b/arch/arm/boot/dts/at91sam9m10g45ek.dts
@@ -24,15 +24,6 @@
};
clocks {
- #address-cells = <1>;
- #size-cells = <1>;
- ranges;
-
- main_clock: clock@0 {
- compatible = "atmel,osc", "fixed-clock";
- clock-frequency = <12000000>;
- };
-
slow_xtal {
clock-frequency = <32768>;
};
@@ -323,14 +314,14 @@
label = "left_click";
gpios = <&pioB 6 GPIO_ACTIVE_LOW>;
linux,code = <272>;
- gpio-key,wakeup;
+ wakeup-source;
};
right_click {
label = "right_click";
gpios = <&pioB 7 GPIO_ACTIVE_LOW>;
linux,code = <273>;
- gpio-key,wakeup;
+ wakeup-source;
};
left {
diff --git a/arch/arm/boot/dts/at91sam9n12ek.dts b/arch/arm/boot/dts/at91sam9n12ek.dts
index acf3451a332d..ca4ddf86817a 100644
--- a/arch/arm/boot/dts/at91sam9n12ek.dts
+++ b/arch/arm/boot/dts/at91sam9n12ek.dts
@@ -23,15 +23,6 @@
};
clocks {
- #address-cells = <1>;
- #size-cells = <1>;
- ranges;
-
- main_clock: clock@0 {
- compatible = "atmel,osc", "fixed-clock";
- clock-frequency = <16000000>;
- };
-
slow_xtal {
clock-frequency = <32768>;
};
@@ -219,7 +210,7 @@
label = "Enter";
gpios = <&pioB 3 GPIO_ACTIVE_LOW>;
linux,code = <28>;
- gpio-key,wakeup;
+ wakeup-source;
};
};
diff --git a/arch/arm/boot/dts/at91sam9rlek.dts b/arch/arm/boot/dts/at91sam9rlek.dts
index 558c9f220bed..f10566f759cd 100644
--- a/arch/arm/boot/dts/at91sam9rlek.dts
+++ b/arch/arm/boot/dts/at91sam9rlek.dts
@@ -22,15 +22,6 @@
};
clocks {
- #address-cells = <1>;
- #size-cells = <1>;
- ranges;
-
- main_clock: clock {
- compatible = "atmel,osc", "fixed-clock";
- clock-frequency = <12000000>;
- };
-
slow_xtal {
clock-frequency = <32768>;
};
@@ -225,14 +216,14 @@
label = "right_click";
gpios = <&pioB 0 GPIO_ACTIVE_LOW>;
linux,code = <273>;
- gpio-key,wakeup;
+ wakeup-source;
};
left_click {
label = "left_click";
gpios = <&pioB 1 GPIO_ACTIVE_LOW>;
linux,code = <272>;
- gpio-key,wakeup;
+ wakeup-source;
};
};
diff --git a/arch/arm/boot/dts/at91sam9x5cm.dtsi b/arch/arm/boot/dts/at91sam9x5cm.dtsi
index 26112ebd15fc..b098ad8cd93a 100644
--- a/arch/arm/boot/dts/at91sam9x5cm.dtsi
+++ b/arch/arm/boot/dts/at91sam9x5cm.dtsi
@@ -13,17 +13,6 @@
};
clocks {
- #address-cells = <1>;
- #size-cells = <1>;
- ranges;
-
- main_clock: clock@0 {
- compatible = "atmel,osc", "fixed-clock";
- clock-frequency = <12000000>;
- };
- };
-
- clocks {
slow_xtal {
clock-frequency = <32768>;
};
diff --git a/arch/arm/boot/dts/berlin2q.dtsi b/arch/arm/boot/dts/berlin2q.dtsi
index 8ea177f375dd..fb1da99996ea 100644
--- a/arch/arm/boot/dts/berlin2q.dtsi
+++ b/arch/arm/boot/dts/berlin2q.dtsi
@@ -118,7 +118,8 @@
sdhci0: sdhci@ab0000 {
compatible = "mrvl,pxav3-mmc";
reg = <0xab0000 0x200>;
- clocks = <&chip_clk CLKID_SDIO1XIN>;
+ clocks = <&chip_clk CLKID_SDIO1XIN>, <&chip_clk CLKID_SDIO>;
+ clock-names = "io", "core";
interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
};
@@ -126,7 +127,8 @@
sdhci1: sdhci@ab0800 {
compatible = "mrvl,pxav3-mmc";
reg = <0xab0800 0x200>;
- clocks = <&chip_clk CLKID_SDIO1XIN>;
+ clocks = <&chip_clk CLKID_SDIO1XIN>, <&chip_clk CLKID_SDIO>;
+ clock-names = "io", "core";
interrupts = <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
};
@@ -135,7 +137,7 @@
compatible = "mrvl,pxav3-mmc";
reg = <0xab1000 0x200>;
interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&chip_clk CLKID_NFC_ECC>, <&chip_clk CLKID_NFC>;
+ clocks = <&chip_clk CLKID_NFC_ECC>, <&chip_clk CLKID_SDIO>;
clock-names = "io", "core";
status = "disabled";
};
diff --git a/arch/arm/boot/dts/dm816x.dtsi b/arch/arm/boot/dts/dm816x.dtsi
index 3c99cfa1a876..eee636de4cd8 100644
--- a/arch/arm/boot/dts/dm816x.dtsi
+++ b/arch/arm/boot/dts/dm816x.dtsi
@@ -218,6 +218,7 @@
reg = <0x480c8000 0x2000>;
interrupts = <77>;
ti,hwmods = "mailbox";
+ #mbox-cells = <1>;
ti,mbox-num-users = <4>;
ti,mbox-num-fifos = <12>;
mbox_dsp: mbox_dsp {
@@ -279,8 +280,11 @@
ti,spi-num-cs = <4>;
ti,hwmods = "mcspi1";
dmas = <&edma 16 &edma 17
- &edma 18 &edma 19>;
- dma-names = "tx0", "rx0", "tx1", "rx1";
+ &edma 18 &edma 19
+ &edma 20 &edma 21
+ &edma 22 &edma 23>;
+ dma-names = "tx0", "rx0", "tx1", "rx1",
+ "tx2", "rx2", "tx3", "rx3";
};
mmc1: mmc@48060000 {
diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi
index bc672fb91466..fe99231cbde5 100644
--- a/arch/arm/boot/dts/dra7.dtsi
+++ b/arch/arm/boot/dts/dra7.dtsi
@@ -1459,8 +1459,8 @@
interrupt-names = "tx", "rx";
dmas = <&sdma_xbar 133>, <&sdma_xbar 132>;
dma-names = "tx", "rx";
- clocks = <&mcasp3_ahclkx_mux>;
- clock-names = "fck";
+ clocks = <&mcasp3_aux_gfclk_mux>, <&mcasp3_ahclkx_mux>;
+ clock-names = "fck", "ahclkx";
status = "disabled";
};
diff --git a/arch/arm/boot/dts/exynos4412.dtsi b/arch/arm/boot/dts/exynos4412.dtsi
index 294cfe40388d..40beede46e55 100644
--- a/arch/arm/boot/dts/exynos4412.dtsi
+++ b/arch/arm/boot/dts/exynos4412.dtsi
@@ -64,73 +64,73 @@
compatible = "operating-points-v2";
opp-shared;
- opp00 {
+ opp@200000000 {
opp-hz = /bits/ 64 <200000000>;
opp-microvolt = <900000>;
clock-latency-ns = <200000>;
};
- opp01 {
+ opp@300000000 {
opp-hz = /bits/ 64 <300000000>;
opp-microvolt = <900000>;
clock-latency-ns = <200000>;
};
- opp02 {
+ opp@400000000 {
opp-hz = /bits/ 64 <400000000>;
opp-microvolt = <925000>;
clock-latency-ns = <200000>;
};
- opp03 {
+ opp@500000000 {
opp-hz = /bits/ 64 <500000000>;
opp-microvolt = <950000>;
clock-latency-ns = <200000>;
};
- opp04 {
+ opp@600000000 {
opp-hz = /bits/ 64 <600000000>;
opp-microvolt = <975000>;
clock-latency-ns = <200000>;
};
- opp05 {
+ opp@700000000 {
opp-hz = /bits/ 64 <700000000>;
opp-microvolt = <987500>;
clock-latency-ns = <200000>;
};
- opp06 {
+ opp@800000000 {
opp-hz = /bits/ 64 <800000000>;
opp-microvolt = <1000000>;
clock-latency-ns = <200000>;
opp-suspend;
};
- opp07 {
+ opp@900000000 {
opp-hz = /bits/ 64 <900000000>;
opp-microvolt = <1037500>;
clock-latency-ns = <200000>;
};
- opp08 {
+ opp@1000000000 {
opp-hz = /bits/ 64 <1000000000>;
opp-microvolt = <1087500>;
clock-latency-ns = <200000>;
};
- opp09 {
+ opp@1100000000 {
opp-hz = /bits/ 64 <1100000000>;
opp-microvolt = <1137500>;
clock-latency-ns = <200000>;
};
- opp10 {
+ opp@1200000000 {
opp-hz = /bits/ 64 <1200000000>;
opp-microvolt = <1187500>;
clock-latency-ns = <200000>;
};
- opp11 {
+ opp@1300000000 {
opp-hz = /bits/ 64 <1300000000>;
opp-microvolt = <1250000>;
clock-latency-ns = <200000>;
};
- opp12 {
+ opp@1400000000 {
opp-hz = /bits/ 64 <1400000000>;
opp-microvolt = <1287500>;
clock-latency-ns = <200000>;
};
- opp13 {
+ opp@1500000000 {
opp-hz = /bits/ 64 <1500000000>;
opp-microvolt = <1350000>;
clock-latency-ns = <200000>;
diff --git a/arch/arm/boot/dts/imx6q-gw5400-a.dts b/arch/arm/boot/dts/imx6q-gw5400-a.dts
index 58adf176425a..a51834e1dd27 100644
--- a/arch/arm/boot/dts/imx6q-gw5400-a.dts
+++ b/arch/arm/boot/dts/imx6q-gw5400-a.dts
@@ -154,7 +154,7 @@
&fec {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet>;
- phy-mode = "rgmii";
+ phy-mode = "rgmii-id";
phy-reset-gpios = <&gpio1 30 GPIO_ACTIVE_HIGH>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx6qdl-gw51xx.dtsi b/arch/arm/boot/dts/imx6qdl-gw51xx.dtsi
index 7b31fdb79ced..dc0cebfe22d7 100644
--- a/arch/arm/boot/dts/imx6qdl-gw51xx.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-gw51xx.dtsi
@@ -94,7 +94,7 @@
&fec {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet>;
- phy-mode = "rgmii";
+ phy-mode = "rgmii-id";
phy-reset-gpios = <&gpio1 30 GPIO_ACTIVE_LOW>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx6qdl-gw52xx.dtsi b/arch/arm/boot/dts/imx6qdl-gw52xx.dtsi
index 1b66328a8498..18cd4114a23e 100644
--- a/arch/arm/boot/dts/imx6qdl-gw52xx.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-gw52xx.dtsi
@@ -154,7 +154,7 @@
&fec {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet>;
- phy-mode = "rgmii";
+ phy-mode = "rgmii-id";
phy-reset-gpios = <&gpio1 30 GPIO_ACTIVE_LOW>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx6qdl-gw53xx.dtsi b/arch/arm/boot/dts/imx6qdl-gw53xx.dtsi
index 7c51839ff934..eea90f37bbb8 100644
--- a/arch/arm/boot/dts/imx6qdl-gw53xx.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-gw53xx.dtsi
@@ -155,7 +155,7 @@
&fec {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet>;
- phy-mode = "rgmii";
+ phy-mode = "rgmii-id";
phy-reset-gpios = <&gpio1 30 GPIO_ACTIVE_LOW>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx6qdl-gw54xx.dtsi b/arch/arm/boot/dts/imx6qdl-gw54xx.dtsi
index 929e0b37bd9e..6c11a2ae35ef 100644
--- a/arch/arm/boot/dts/imx6qdl-gw54xx.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-gw54xx.dtsi
@@ -145,7 +145,7 @@
&fec {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet>;
- phy-mode = "rgmii";
+ phy-mode = "rgmii-id";
phy-reset-gpios = <&gpio1 30 GPIO_ACTIVE_LOW>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
index 8263fc18a7d9..d354d406954d 100644
--- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
@@ -113,14 +113,14 @@
&clks {
assigned-clocks = <&clks IMX6QDL_PLL4_BYPASS_SRC>,
<&clks IMX6QDL_PLL4_BYPASS>,
- <&clks IMX6QDL_CLK_PLL4_POST_DIV>,
<&clks IMX6QDL_CLK_LDB_DI0_SEL>,
- <&clks IMX6QDL_CLK_LDB_DI1_SEL>;
+ <&clks IMX6QDL_CLK_LDB_DI1_SEL>,
+ <&clks IMX6QDL_CLK_PLL4_POST_DIV>;
assigned-clock-parents = <&clks IMX6QDL_CLK_LVDS2_IN>,
<&clks IMX6QDL_PLL4_BYPASS_SRC>,
<&clks IMX6QDL_CLK_PLL3_USB_OTG>,
<&clks IMX6QDL_CLK_PLL3_USB_OTG>;
- assigned-clock-rates = <0>, <0>, <24576000>;
+ assigned-clock-rates = <0>, <0>, <0>, <0>, <24576000>;
};
&ecspi1 {
diff --git a/arch/arm/boot/dts/k2l-netcp.dtsi b/arch/arm/boot/dts/k2l-netcp.dtsi
index 01aef230773d..5acbd0dcc2ab 100644
--- a/arch/arm/boot/dts/k2l-netcp.dtsi
+++ b/arch/arm/boot/dts/k2l-netcp.dtsi
@@ -137,7 +137,7 @@ netcp: netcp@26000000 {
/* NetCP address range */
ranges = <0 0x26000000 0x1000000>;
- clocks = <&papllclk>, <&clkcpgmac>, <&chipclk12>;
+ clocks = <&clkosr>, <&papllclk>, <&clkcpgmac>, <&chipclk12>;
dma-coherent;
ti,navigator-dmas = <&dma_gbe 0>,
diff --git a/arch/arm/boot/dts/kirkwood-ts219.dtsi b/arch/arm/boot/dts/kirkwood-ts219.dtsi
index c56ab6bbfe3c..0e46560551f4 100644
--- a/arch/arm/boot/dts/kirkwood-ts219.dtsi
+++ b/arch/arm/boot/dts/kirkwood-ts219.dtsi
@@ -40,7 +40,7 @@
};
poweroff@12100 {
compatible = "qnap,power-off";
- reg = <0x12000 0x100>;
+ reg = <0x12100 0x100>;
clocks = <&gate_clk 7>;
};
spi@10600 {
diff --git a/arch/arm/boot/dts/omap4-duovero-parlor.dts b/arch/arm/boot/dts/omap4-duovero-parlor.dts
index 1a78f013f37a..b75f7b2b7c4a 100644
--- a/arch/arm/boot/dts/omap4-duovero-parlor.dts
+++ b/arch/arm/boot/dts/omap4-duovero-parlor.dts
@@ -189,3 +189,7 @@
};
};
+&uart3 {
+ interrupts-extended = <&wakeupgen GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH
+ &omap4_pmx_core OMAP4_UART3_RX>;
+};
diff --git a/arch/arm/boot/dts/rk3288-veyron-minnie.dts b/arch/arm/boot/dts/rk3288-veyron-minnie.dts
index 8fd8ef2c72da..85f0373df498 100644
--- a/arch/arm/boot/dts/rk3288-veyron-minnie.dts
+++ b/arch/arm/boot/dts/rk3288-veyron-minnie.dts
@@ -86,6 +86,10 @@
};
};
+&emmc {
+ /delete-property/mmc-hs200-1_8v;
+};
+
&gpio_keys {
pinctrl-0 = <&pwr_key_l &ap_lid_int_l &volum_down_l &volum_up_l>;
diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index 6a79c9c526b8..04ea209f1737 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -452,8 +452,10 @@
clock-names = "tsadc", "apb_pclk";
resets = <&cru SRST_TSADC>;
reset-names = "tsadc-apb";
- pinctrl-names = "default";
- pinctrl-0 = <&otp_out>;
+ pinctrl-names = "init", "default", "sleep";
+ pinctrl-0 = <&otp_gpio>;
+ pinctrl-1 = <&otp_out>;
+ pinctrl-2 = <&otp_gpio>;
#thermal-sensor-cells = <1>;
rockchip,hw-tshut-temp = <95000>;
status = "disabled";
@@ -1395,6 +1397,10 @@
};
tsadc {
+ otp_gpio: otp-gpio {
+ rockchip,pins = <0 10 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
otp_out: otp-out {
rockchip,pins = <0 10 RK_FUNC_1 &pcfg_pull_none>;
};
diff --git a/arch/arm/boot/dts/sama5d35ek.dts b/arch/arm/boot/dts/sama5d35ek.dts
index d9a9aca1ccfd..e812f5c1bf70 100644
--- a/arch/arm/boot/dts/sama5d35ek.dts
+++ b/arch/arm/boot/dts/sama5d35ek.dts
@@ -49,7 +49,7 @@
label = "pb_user1";
gpios = <&pioE 27 GPIO_ACTIVE_HIGH>;
linux,code = <0x100>;
- gpio-key,wakeup;
+ wakeup-source;
};
};
};
diff --git a/arch/arm/boot/dts/sama5d4.dtsi b/arch/arm/boot/dts/sama5d4.dtsi
index 15bbaf690047..2193637b9cd2 100644
--- a/arch/arm/boot/dts/sama5d4.dtsi
+++ b/arch/arm/boot/dts/sama5d4.dtsi
@@ -1300,7 +1300,7 @@
};
watchdog@fc068640 {
- compatible = "atmel,at91sam9260-wdt";
+ compatible = "atmel,sama5d4-wdt";
reg = <0xfc068640 0x10>;
clocks = <&clk32k>;
status = "disabled";
diff --git a/arch/arm/boot/dts/ste-nomadik-stn8815.dtsi b/arch/arm/boot/dts/ste-nomadik-stn8815.dtsi
index 314f59c12162..d0c743853318 100644
--- a/arch/arm/boot/dts/ste-nomadik-stn8815.dtsi
+++ b/arch/arm/boot/dts/ste-nomadik-stn8815.dtsi
@@ -25,9 +25,9 @@
cache-sets = <512>;
cache-line-size = <32>;
/* At full speed latency must be >=2 */
- arm,tag-latency = <2>;
- arm,data-latency = <2 2>;
- arm,dirty-latency = <2>;
+ arm,tag-latency = <8>;
+ arm,data-latency = <8 8>;
+ arm,dirty-latency = <8>;
};
mtu0: mtu@101e2000 {
diff --git a/arch/arm/boot/dts/stihxxx-b2120.dtsi b/arch/arm/boot/dts/stihxxx-b2120.dtsi
index ad21a4293a33..133375bc8aa5 100644
--- a/arch/arm/boot/dts/stihxxx-b2120.dtsi
+++ b/arch/arm/boot/dts/stihxxx-b2120.dtsi
@@ -6,6 +6,9 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include <dt-bindings/clock/stih407-clks.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/media/c8sectpfe.h>
/ {
soc {
sbc_serial0: serial@9530000 {
@@ -35,12 +38,18 @@
status = "okay";
};
- i2c@9842000 {
+ ssc2: i2c@9842000 {
status = "okay";
+ clock-frequency = <100000>;
+ st,i2c-min-scl-pulse-width-us = <0>;
+ st,i2c-min-sda-pulse-width-us = <5>;
};
- i2c@9843000 {
+ ssc3: i2c@9843000 {
status = "okay";
+ clock-frequency = <100000>;
+ st,i2c-min-scl-pulse-width-us = <0>;
+ st,i2c-min-sda-pulse-width-us = <5>;
};
i2c@9844000 {
@@ -93,5 +102,38 @@
phy-mode = "rgmii";
fixed-link = <0 1 1000 0 0>;
};
+
+ demux@08a20000 {
+ compatible = "st,stih407-c8sectpfe";
+ status = "okay";
+ reg = <0x08a20000 0x10000>,
+ <0x08a00000 0x4000>;
+ reg-names = "c8sectpfe", "c8sectpfe-ram";
+ interrupts = <GIC_SPI 34 IRQ_TYPE_NONE>,
+ <GIC_SPI 35 IRQ_TYPE_NONE>;
+ interrupt-names = "c8sectpfe-error-irq",
+ "c8sectpfe-idle-irq";
+ pinctrl-0 = <&pinctrl_tsin0_serial>;
+ pinctrl-1 = <&pinctrl_tsin0_parallel>;
+ pinctrl-2 = <&pinctrl_tsin3_serial>;
+ pinctrl-3 = <&pinctrl_tsin4_serial_alt3>;
+ pinctrl-4 = <&pinctrl_tsin5_serial_alt1>;
+ pinctrl-names = "tsin0-serial",
+ "tsin0-parallel",
+ "tsin3-serial",
+ "tsin4-serial",
+ "tsin5-serial";
+ clocks = <&clk_s_c0_flexgen CLK_PROC_STFE>;
+ clock-names = "c8sectpfe";
+
+ /* tsin0 is TSA on NIMA */
+ tsin0: port@0 {
+ tsin-num = <0>;
+ serial-not-parallel;
+ i2c-bus = <&ssc2>;
+ reset-gpios = <&pio15 4 GPIO_ACTIVE_HIGH>;
+ dvb-card = <STV0367_TDA18212_NIMA_1>;
+ };
+ };
};
};
diff --git a/arch/arm/boot/dts/sun6i-a31s-primo81.dts b/arch/arm/boot/dts/sun6i-a31s-primo81.dts
index 2d4250b1faf8..68b479b8772c 100644
--- a/arch/arm/boot/dts/sun6i-a31s-primo81.dts
+++ b/arch/arm/boot/dts/sun6i-a31s-primo81.dts
@@ -83,6 +83,7 @@
reg = <0x5d>;
interrupt-parent = <&pio>;
interrupts = <0 3 IRQ_TYPE_LEVEL_HIGH>; /* PA3 */
+ touchscreen-swapped-x-y;
};
};
diff --git a/arch/arm/boot/dts/tegra124-nyan.dtsi b/arch/arm/boot/dts/tegra124-nyan.dtsi
index 40c23a0b7cfc..ec1aa64ded68 100644
--- a/arch/arm/boot/dts/tegra124-nyan.dtsi
+++ b/arch/arm/boot/dts/tegra124-nyan.dtsi
@@ -399,7 +399,7 @@
/* CPU DFLL clock */
clock@0,70110000 {
- status = "okay";
+ status = "disabled";
vdd-cpu-supply = <&vdd_cpu>;
nvidia,i2c-fs-rate = <400000>;
};
diff --git a/arch/arm/boot/dts/usb_a9260_common.dtsi b/arch/arm/boot/dts/usb_a9260_common.dtsi
index 12edafefd44a..9beea8976584 100644
--- a/arch/arm/boot/dts/usb_a9260_common.dtsi
+++ b/arch/arm/boot/dts/usb_a9260_common.dtsi
@@ -115,7 +115,7 @@
label = "user_pb";
gpios = <&pioB 10 GPIO_ACTIVE_LOW>;
linux,code = <28>;
- gpio-key,wakeup;
+ wakeup-source;
};
};
diff --git a/arch/arm/boot/dts/usb_a9263.dts b/arch/arm/boot/dts/usb_a9263.dts
index 68c0de36c339..8cc6edb29694 100644
--- a/arch/arm/boot/dts/usb_a9263.dts
+++ b/arch/arm/boot/dts/usb_a9263.dts
@@ -143,7 +143,7 @@
label = "user_pb";
gpios = <&pioB 10 GPIO_ACTIVE_LOW>;
linux,code = <28>;
- gpio-key,wakeup;
+ wakeup-source;
};
};
diff --git a/arch/arm/boot/dts/versatile-ab.dts b/arch/arm/boot/dts/versatile-ab.dts
index 01f40197ea13..3279bf1a17a1 100644
--- a/arch/arm/boot/dts/versatile-ab.dts
+++ b/arch/arm/boot/dts/versatile-ab.dts
@@ -110,7 +110,11 @@
interrupt-parent = <&vic>;
interrupts = <31>; /* Cascaded to vic */
clear-mask = <0xffffffff>;
- valid-mask = <0xffc203f8>;
+ /*
+ * Valid interrupt lines mask according to
+ * table 4-36 page 4-50 of ARM DUI 0225D
+ */
+ valid-mask = <0x0760031b>;
};
dma@10130000 {
@@ -266,8 +270,8 @@
};
mmc@5000 {
compatible = "arm,pl180", "arm,primecell";
- reg = < 0x5000 0x1000>;
- interrupts-extended = <&vic 22 &sic 2>;
+ reg = <0x5000 0x1000>;
+ interrupts-extended = <&vic 22 &sic 1>;
clocks = <&xtal24mhz>, <&pclk>;
clock-names = "mclk", "apb_pclk";
};
diff --git a/arch/arm/boot/dts/versatile-pb.dts b/arch/arm/boot/dts/versatile-pb.dts
index b83137f66034..33a8eb28374e 100644
--- a/arch/arm/boot/dts/versatile-pb.dts
+++ b/arch/arm/boot/dts/versatile-pb.dts
@@ -5,6 +5,16 @@
compatible = "arm,versatile-pb";
amba {
+ /* The Versatile PB is using more SIC IRQ lines than the AB */
+ sic: intc@10003000 {
+ clear-mask = <0xffffffff>;
+ /*
+ * Valid interrupt lines mask according to
+ * figure 3-30 page 3-74 of ARM DUI 0224B
+ */
+ valid-mask = <0x7fe003ff>;
+ };
+
gpio2: gpio@101e6000 {
compatible = "arm,pl061", "arm,primecell";
reg = <0x101e6000 0x1000>;
@@ -67,6 +77,13 @@
};
fpga {
+ mmc@5000 {
+ /*
+ * Overrides the interrupt assignment from
+ * the Versatile AB board file.
+ */
+ interrupts-extended = <&sic 22 &sic 23>;
+ };
uart@9000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x9000 0x1000>;
@@ -86,7 +103,8 @@
mmc@b000 {
compatible = "arm,pl180", "arm,primecell";
reg = <0xb000 0x1000>;
- interrupts-extended = <&vic 23 &sic 2>;
+ interrupt-parent = <&sic>;
+ interrupts = <1>, <2>;
clocks = <&xtal24mhz>, <&pclk>;
clock-names = "mclk", "apb_pclk";
};
diff --git a/arch/arm/boot/dts/vf610-colibri.dtsi b/arch/arm/boot/dts/vf610-colibri.dtsi
index 19fe045b8334..2d7eab755210 100644
--- a/arch/arm/boot/dts/vf610-colibri.dtsi
+++ b/arch/arm/boot/dts/vf610-colibri.dtsi
@@ -18,8 +18,3 @@
reg = <0x80000000 0x10000000>;
};
};
-
-&L2 {
- arm,data-latency = <2 1 2>;
- arm,tag-latency = <3 2 3>;
-};
diff --git a/arch/arm/boot/dts/vf610.dtsi b/arch/arm/boot/dts/vf610.dtsi
index 5f8eb1bd782b..58bc6e448be5 100644
--- a/arch/arm/boot/dts/vf610.dtsi
+++ b/arch/arm/boot/dts/vf610.dtsi
@@ -19,7 +19,7 @@
reg = <0x40006000 0x1000>;
cache-unified;
cache-level = <2>;
- arm,data-latency = <1 1 1>;
+ arm,data-latency = <3 3 3>;
arm,tag-latency = <2 2 2>;
};
};
diff --git a/arch/arm/boot/dts/vfxxx.dtsi b/arch/arm/boot/dts/vfxxx.dtsi
index 6736bae43a5b..3cd1b27f2697 100644
--- a/arch/arm/boot/dts/vfxxx.dtsi
+++ b/arch/arm/boot/dts/vfxxx.dtsi
@@ -158,7 +158,7 @@
interrupts = <67 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks VF610_CLK_DSPI0>;
clock-names = "dspi";
- spi-num-chipselects = <5>;
+ spi-num-chipselects = <6>;
status = "disabled";
};
@@ -170,7 +170,7 @@
interrupts = <68 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks VF610_CLK_DSPI1>;
clock-names = "dspi";
- spi-num-chipselects = <5>;
+ spi-num-chipselects = <4>;
status = "disabled";
};
@@ -178,8 +178,10 @@
compatible = "fsl,vf610-sai";
reg = <0x40031000 0x1000>;
interrupts = <86 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&clks VF610_CLK_SAI2>;
- clock-names = "sai";
+ clocks = <&clks VF610_CLK_SAI2>,
+ <&clks VF610_CLK_SAI2_DIV>,
+ <&clks 0>, <&clks 0>;
+ clock-names = "bus", "mclk1", "mclk2", "mclk3";
dma-names = "tx", "rx";
dmas = <&edma0 0 21>,
<&edma0 0 20>;
@@ -461,6 +463,8 @@
clock-names = "adc";
#io-channel-cells = <1>;
status = "disabled";
+ fsl,adck-max-frequency = <30000000>, <40000000>,
+ <20000000>;
};
esdhc0: esdhc@400b1000 {
@@ -472,8 +476,6 @@
<&clks VF610_CLK_ESDHC0>;
clock-names = "ipg", "ahb", "per";
status = "disabled";
- fsl,adck-max-frequency = <30000000>, <40000000>,
- <20000000>;
};
esdhc1: esdhc@400b2000 {
diff --git a/arch/arm/boot/dts/wm8650.dtsi b/arch/arm/boot/dts/wm8650.dtsi
index b1c59a766a13..e12213d16693 100644
--- a/arch/arm/boot/dts/wm8650.dtsi
+++ b/arch/arm/boot/dts/wm8650.dtsi
@@ -187,6 +187,15 @@
interrupts = <43>;
};
+ sdhc@d800a000 {
+ compatible = "wm,wm8505-sdhc";
+ reg = <0xd800a000 0x400>;
+ interrupts = <20>, <21>;
+ clocks = <&clksdhc>;
+ bus-width = <4>;
+ sdon-inverted;
+ };
+
fb: fb@d8050800 {
compatible = "wm,wm8505-fb";
reg = <0xd8050800 0x200>;
diff --git a/arch/arm/configs/at91_dt_defconfig b/arch/arm/configs/at91_dt_defconfig
index 1b1e5acd76e2..e4b1be66b3f5 100644
--- a/arch/arm/configs/at91_dt_defconfig
+++ b/arch/arm/configs/at91_dt_defconfig
@@ -125,7 +125,6 @@ CONFIG_POWER_RESET=y
# CONFIG_HWMON is not set
CONFIG_WATCHDOG=y
CONFIG_AT91SAM9X_WATCHDOG=y
-CONFIG_SSB=m
CONFIG_MFD_ATMEL_HLCDC=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 69a22fdb52a5..cd7b198fc79e 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -366,6 +366,7 @@ CONFIG_BATTERY_MAX17042=m
CONFIG_CHARGER_MAX14577=m
CONFIG_CHARGER_MAX77693=m
CONFIG_CHARGER_TPS65090=y
+CONFIG_AXP20X_POWER=m
CONFIG_POWER_RESET_AS3722=y
CONFIG_POWER_RESET_GPIO=y
CONFIG_POWER_RESET_GPIO_RESTART=y
diff --git a/arch/arm/configs/sama5_defconfig b/arch/arm/configs/sama5_defconfig
index a0c57ac88b27..63f7e6ce649a 100644
--- a/arch/arm/configs/sama5_defconfig
+++ b/arch/arm/configs/sama5_defconfig
@@ -129,7 +129,6 @@ CONFIG_GPIO_SYSFS=y
CONFIG_POWER_SUPPLY=y
CONFIG_POWER_RESET=y
# CONFIG_HWMON is not set
-CONFIG_SSB=m
CONFIG_MFD_ATMEL_FLEXCOM=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
diff --git a/arch/arm/configs/stm32_defconfig b/arch/arm/configs/stm32_defconfig
index 4725fab562cb..ec5250547d14 100644
--- a/arch/arm/configs/stm32_defconfig
+++ b/arch/arm/configs/stm32_defconfig
@@ -54,6 +54,8 @@ CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_TRIGGERS=y
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_DMADEVICES=y
+CONFIG_STM32_DMA=y
# CONFIG_FILE_LOCKING is not set
# CONFIG_DNOTIFY is not set
# CONFIG_INOTIFY_USER is not set
diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig
index 3c36e16fcacf..b503a89441bf 100644
--- a/arch/arm/configs/sunxi_defconfig
+++ b/arch/arm/configs/sunxi_defconfig
@@ -84,6 +84,7 @@ CONFIG_SPI_SUN4I=y
CONFIG_SPI_SUN6I=y
CONFIG_GPIO_SYSFS=y
CONFIG_POWER_SUPPLY=y
+CONFIG_AXP20X_POWER=y
CONFIG_THERMAL=y
CONFIG_CPU_THERMAL=y
CONFIG_WATCHDOG=y
diff --git a/arch/arm/include/asm/Kbuild b/arch/arm/include/asm/Kbuild
index bd425302c97a..16da6380eb85 100644
--- a/arch/arm/include/asm/Kbuild
+++ b/arch/arm/include/asm/Kbuild
@@ -3,6 +3,7 @@
generic-y += bitsperlong.h
generic-y += cputime.h
generic-y += current.h
+generic-y += early_ioremap.h
generic-y += emergency-restart.h
generic-y += errno.h
generic-y += exec.h
diff --git a/arch/arm/include/asm/arch_gicv3.h b/arch/arm/include/asm/arch_gicv3.h
index 6607d976e07d..7da5503c0591 100644
--- a/arch/arm/include/asm/arch_gicv3.h
+++ b/arch/arm/include/asm/arch_gicv3.h
@@ -21,6 +21,7 @@
#ifndef __ASSEMBLY__
#include <linux/io.h>
+#include <asm/barrier.h>
#define __ACCESS_CP15(CRn, Op1, CRm, Op2) p15, Op1, %0, CRn, CRm, Op2
#define __ACCESS_CP15_64(Op1, CRm) p15, Op1, %Q0, %R0, CRm
diff --git a/arch/arm/include/asm/bug.h b/arch/arm/include/asm/bug.h
index e7335a92144e..4e6e88a6b2f4 100644
--- a/arch/arm/include/asm/bug.h
+++ b/arch/arm/include/asm/bug.h
@@ -5,8 +5,6 @@
#include <linux/types.h>
#include <asm/opcodes.h>
-#ifdef CONFIG_BUG
-
/*
* Use a suitable undefined instruction to use for ARM/Thumb2 bug handling.
* We need to be careful not to conflict with those used by other modules and
@@ -47,7 +45,7 @@ do { \
unreachable(); \
} while (0)
-#else /* not CONFIG_DEBUG_BUGVERBOSE */
+#else
#define __BUG(__file, __line, __value) \
do { \
@@ -57,7 +55,6 @@ do { \
#endif /* CONFIG_DEBUG_BUGVERBOSE */
#define HAVE_ARCH_BUG
-#endif /* CONFIG_BUG */
#include <asm-generic/bug.h>
diff --git a/arch/arm/include/asm/cpuidle.h b/arch/arm/include/asm/cpuidle.h
index 0f8424924902..3848259bebf8 100644
--- a/arch/arm/include/asm/cpuidle.h
+++ b/arch/arm/include/asm/cpuidle.h
@@ -30,7 +30,7 @@ static inline int arm_cpuidle_simple_enter(struct cpuidle_device *dev,
struct device_node;
struct cpuidle_ops {
- int (*suspend)(int cpu, unsigned long arg);
+ int (*suspend)(unsigned long arg);
int (*init)(struct device_node *, int cpu);
};
diff --git a/arch/arm/include/asm/efi.h b/arch/arm/include/asm/efi.h
new file mode 100644
index 000000000000..e0eea72deb87
--- /dev/null
+++ b/arch/arm/include/asm/efi.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#ifndef __ASM_ARM_EFI_H
+#define __ASM_ARM_EFI_H
+
+#include <asm/cacheflush.h>
+#include <asm/cachetype.h>
+#include <asm/early_ioremap.h>
+#include <asm/fixmap.h>
+#include <asm/highmem.h>
+#include <asm/mach/map.h>
+#include <asm/mmu_context.h>
+#include <asm/pgtable.h>
+
+#ifdef CONFIG_EFI
+void efi_init(void);
+
+int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md);
+
+#define efi_call_virt(f, ...) \
+({ \
+ efi_##f##_t *__f; \
+ efi_status_t __s; \
+ \
+ efi_virtmap_load(); \
+ __f = efi.systab->runtime->f; \
+ __s = __f(__VA_ARGS__); \
+ efi_virtmap_unload(); \
+ __s; \
+})
+
+#define __efi_call_virt(f, ...) \
+({ \
+ efi_##f##_t *__f; \
+ \
+ efi_virtmap_load(); \
+ __f = efi.systab->runtime->f; \
+ __f(__VA_ARGS__); \
+ efi_virtmap_unload(); \
+})
+
+static inline void efi_set_pgd(struct mm_struct *mm)
+{
+ check_and_switch_context(mm, NULL);
+}
+
+void efi_virtmap_load(void);
+void efi_virtmap_unload(void);
+
+#else
+#define efi_init()
+#endif /* CONFIG_EFI */
+
+/* arch specific definitions used by the stub code */
+
+#define efi_call_early(f, ...) sys_table_arg->boottime->f(__VA_ARGS__)
+
+/*
+ * A reasonable upper bound for the uncompressed kernel size is 32 MBytes,
+ * so we will reserve that amount of memory. We have no easy way to tell what
+ * the actuall size of code + data the uncompressed kernel will use.
+ * If this is insufficient, the decompressor will relocate itself out of the
+ * way before performing the decompression.
+ */
+#define MAX_UNCOMP_KERNEL_SIZE SZ_32M
+
+/*
+ * The kernel zImage should preferably be located between 32 MB and 128 MB
+ * from the base of DRAM. The min address leaves space for a maximal size
+ * uncompressed image, and the max address is due to how the zImage decompressor
+ * picks a destination address.
+ */
+#define ZIMAGE_OFFSET_LIMIT SZ_128M
+#define MIN_ZIMAGE_OFFSET MAX_UNCOMP_KERNEL_SIZE
+#define MAX_FDT_OFFSET ZIMAGE_OFFSET_LIMIT
+
+#endif /* _ASM_ARM_EFI_H */
diff --git a/arch/arm/include/asm/fixmap.h b/arch/arm/include/asm/fixmap.h
index 58cfe9f1a687..5c17d2dec777 100644
--- a/arch/arm/include/asm/fixmap.h
+++ b/arch/arm/include/asm/fixmap.h
@@ -19,20 +19,47 @@ enum fixed_addresses {
FIX_TEXT_POKE0,
FIX_TEXT_POKE1,
- __end_of_fixed_addresses
+ __end_of_fixmap_region,
+
+ /*
+ * Share the kmap() region with early_ioremap(): this is guaranteed
+ * not to clash since early_ioremap() is only available before
+ * paging_init(), and kmap() only after.
+ */
+#define NR_FIX_BTMAPS 32
+#define FIX_BTMAPS_SLOTS 7
+#define TOTAL_FIX_BTMAPS (NR_FIX_BTMAPS * FIX_BTMAPS_SLOTS)
+
+ FIX_BTMAP_END = __end_of_permanent_fixed_addresses,
+ FIX_BTMAP_BEGIN = FIX_BTMAP_END + TOTAL_FIX_BTMAPS - 1,
+ __end_of_early_ioremap_region
};
+static const enum fixed_addresses __end_of_fixed_addresses =
+ __end_of_fixmap_region > __end_of_early_ioremap_region ?
+ __end_of_fixmap_region : __end_of_early_ioremap_region;
+
#define FIXMAP_PAGE_COMMON (L_PTE_YOUNG | L_PTE_PRESENT | L_PTE_XN | L_PTE_DIRTY)
#define FIXMAP_PAGE_NORMAL (FIXMAP_PAGE_COMMON | L_PTE_MT_WRITEBACK)
+#define FIXMAP_PAGE_RO (FIXMAP_PAGE_NORMAL | L_PTE_RDONLY)
/* Used by set_fixmap_(io|nocache), both meant for mapping a device */
#define FIXMAP_PAGE_IO (FIXMAP_PAGE_COMMON | L_PTE_MT_DEV_SHARED | L_PTE_SHARED)
#define FIXMAP_PAGE_NOCACHE FIXMAP_PAGE_IO
+#define __early_set_fixmap __set_fixmap
+
+#ifdef CONFIG_MMU
+
void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot);
void __init early_fixmap_init(void);
#include <asm-generic/fixmap.h>
+#else
+
+static inline void early_fixmap_init(void) { }
+
+#endif
#endif
diff --git a/arch/arm/include/asm/hardirq.h b/arch/arm/include/asm/hardirq.h
index fe3ea776dc34..3d7351c844aa 100644
--- a/arch/arm/include/asm/hardirq.h
+++ b/arch/arm/include/asm/hardirq.h
@@ -5,7 +5,7 @@
#include <linux/threads.h>
#include <asm/irq.h>
-#define NR_IPI 8
+#define NR_IPI 7
typedef struct {
unsigned int __softirq_pending;
diff --git a/arch/arm/include/asm/irq.h b/arch/arm/include/asm/irq.h
index be1d07d59ee9..1bd9510de1b9 100644
--- a/arch/arm/include/asm/irq.h
+++ b/arch/arm/include/asm/irq.h
@@ -40,6 +40,11 @@ extern void arch_trigger_all_cpu_backtrace(bool);
#define arch_trigger_all_cpu_backtrace(x) arch_trigger_all_cpu_backtrace(x)
#endif
+static inline int nr_legacy_irqs(void)
+{
+ return NR_IRQS_LEGACY;
+}
+
#endif
#endif
diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h
index dc641ddf0784..e22089fb44dc 100644
--- a/arch/arm/include/asm/kvm_arm.h
+++ b/arch/arm/include/asm/kvm_arm.h
@@ -19,6 +19,7 @@
#ifndef __ARM_KVM_ARM_H__
#define __ARM_KVM_ARM_H__
+#include <linux/const.h>
#include <linux/types.h>
/* Hyp Configuration Register (HCR) bits */
@@ -132,10 +133,9 @@
* space.
*/
#define KVM_PHYS_SHIFT (40)
-#define KVM_PHYS_SIZE (1ULL << KVM_PHYS_SHIFT)
-#define KVM_PHYS_MASK (KVM_PHYS_SIZE - 1ULL)
-#define PTRS_PER_S2_PGD (1ULL << (KVM_PHYS_SHIFT - 30))
-#define S2_PGD_ORDER get_order(PTRS_PER_S2_PGD * sizeof(pgd_t))
+#define KVM_PHYS_SIZE (_AC(1, ULL) << KVM_PHYS_SHIFT)
+#define KVM_PHYS_MASK (KVM_PHYS_SIZE - _AC(1, ULL))
+#define PTRS_PER_S2_PGD (_AC(1, ULL) << (KVM_PHYS_SHIFT - 30))
/* Virtualization Translation Control Register (VTCR) bits */
#define VTCR_SH0 (3 << 12)
@@ -162,17 +162,17 @@
#define VTTBR_X (5 - KVM_T0SZ)
#endif
#define VTTBR_BADDR_SHIFT (VTTBR_X - 1)
-#define VTTBR_BADDR_MASK (((1LLU << (40 - VTTBR_X)) - 1) << VTTBR_BADDR_SHIFT)
-#define VTTBR_VMID_SHIFT (48LLU)
-#define VTTBR_VMID_MASK (0xffLLU << VTTBR_VMID_SHIFT)
+#define VTTBR_BADDR_MASK (((_AC(1, ULL) << (40 - VTTBR_X)) - 1) << VTTBR_BADDR_SHIFT)
+#define VTTBR_VMID_SHIFT _AC(48, ULL)
+#define VTTBR_VMID_MASK(size) (_AT(u64, (1 << size) - 1) << VTTBR_VMID_SHIFT)
/* Hyp Syndrome Register (HSR) bits */
#define HSR_EC_SHIFT (26)
-#define HSR_EC (0x3fU << HSR_EC_SHIFT)
-#define HSR_IL (1U << 25)
+#define HSR_EC (_AC(0x3f, UL) << HSR_EC_SHIFT)
+#define HSR_IL (_AC(1, UL) << 25)
#define HSR_ISS (HSR_IL - 1)
#define HSR_ISV_SHIFT (24)
-#define HSR_ISV (1U << HSR_ISV_SHIFT)
+#define HSR_ISV (_AC(1, UL) << HSR_ISV_SHIFT)
#define HSR_SRT_SHIFT (16)
#define HSR_SRT_MASK (0xf << HSR_SRT_SHIFT)
#define HSR_FSC (0x3f)
@@ -180,9 +180,9 @@
#define HSR_SSE (1 << 21)
#define HSR_WNR (1 << 6)
#define HSR_CV_SHIFT (24)
-#define HSR_CV (1U << HSR_CV_SHIFT)
+#define HSR_CV (_AC(1, UL) << HSR_CV_SHIFT)
#define HSR_COND_SHIFT (20)
-#define HSR_COND (0xfU << HSR_COND_SHIFT)
+#define HSR_COND (_AC(0xf, UL) << HSR_COND_SHIFT)
#define FSC_FAULT (0x04)
#define FSC_ACCESS (0x08)
@@ -210,13 +210,13 @@
#define HSR_EC_DABT (0x24)
#define HSR_EC_DABT_HYP (0x25)
-#define HSR_WFI_IS_WFE (1U << 0)
+#define HSR_WFI_IS_WFE (_AC(1, UL) << 0)
-#define HSR_HVC_IMM_MASK ((1UL << 16) - 1)
+#define HSR_HVC_IMM_MASK ((_AC(1, UL) << 16) - 1)
-#define HSR_DABT_S1PTW (1U << 7)
-#define HSR_DABT_CM (1U << 8)
-#define HSR_DABT_EA (1U << 9)
+#define HSR_DABT_S1PTW (_AC(1, UL) << 7)
+#define HSR_DABT_CM (_AC(1, UL) << 8)
+#define HSR_DABT_EA (_AC(1, UL) << 9)
#define kvm_arm_exception_type \
{0, "RESET" }, \
diff --git a/arch/arm/include/asm/kvm_emulate.h b/arch/arm/include/asm/kvm_emulate.h
index a9c80a2ea1a7..3095df091ff8 100644
--- a/arch/arm/include/asm/kvm_emulate.h
+++ b/arch/arm/include/asm/kvm_emulate.h
@@ -28,6 +28,18 @@
unsigned long *vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num);
unsigned long *vcpu_spsr(struct kvm_vcpu *vcpu);
+static inline unsigned long vcpu_get_reg(struct kvm_vcpu *vcpu,
+ u8 reg_num)
+{
+ return *vcpu_reg(vcpu, reg_num);
+}
+
+static inline void vcpu_set_reg(struct kvm_vcpu *vcpu, u8 reg_num,
+ unsigned long val)
+{
+ *vcpu_reg(vcpu, reg_num) = val;
+}
+
bool kvm_condition_valid(struct kvm_vcpu *vcpu);
void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr);
void kvm_inject_undefined(struct kvm_vcpu *vcpu);
diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
index 6692982c9b57..f9f27792d8ed 100644
--- a/arch/arm/include/asm/kvm_host.h
+++ b/arch/arm/include/asm/kvm_host.h
@@ -150,6 +150,12 @@ struct kvm_vcpu_stat {
u32 halt_successful_poll;
u32 halt_attempted_poll;
u32 halt_wakeup;
+ u32 hvc_exit_stat;
+ u64 wfe_exit_stat;
+ u64 wfi_exit_stat;
+ u64 mmio_exit_user;
+ u64 mmio_exit_kernel;
+ u64 exits;
};
int kvm_vcpu_preferred_target(struct kvm_vcpu_init *init);
diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h
index 405aa1883307..9203c21b4673 100644
--- a/arch/arm/include/asm/kvm_mmu.h
+++ b/arch/arm/include/asm/kvm_mmu.h
@@ -279,6 +279,11 @@ static inline void __kvm_extend_hypmap(pgd_t *boot_hyp_pgd,
pgd_t *merged_hyp_pgd,
unsigned long hyp_idmap_start) { }
+static inline unsigned int kvm_get_vmid_bits(void)
+{
+ return 8;
+}
+
#endif /* !__ASSEMBLY__ */
#endif /* __ARM_KVM_MMU_H__ */
diff --git a/arch/arm/include/asm/mach/map.h b/arch/arm/include/asm/mach/map.h
index f98c7f32c9c8..9b7c328fb207 100644
--- a/arch/arm/include/asm/mach/map.h
+++ b/arch/arm/include/asm/mach/map.h
@@ -42,6 +42,8 @@ enum {
extern void iotable_init(struct map_desc *, int);
extern void vm_reserve_area_early(unsigned long addr, unsigned long size,
void *caller);
+extern void create_mapping_late(struct mm_struct *mm, struct map_desc *md,
+ bool ng);
#ifdef CONFIG_DEBUG_LL
extern void debug_ll_addr(unsigned long *paddr, unsigned long *vaddr);
diff --git a/arch/arm/include/asm/mmu_context.h b/arch/arm/include/asm/mmu_context.h
index 9b32f76bb0dd..432ce8176498 100644
--- a/arch/arm/include/asm/mmu_context.h
+++ b/arch/arm/include/asm/mmu_context.h
@@ -26,7 +26,7 @@ void __check_vmalloc_seq(struct mm_struct *mm);
#ifdef CONFIG_CPU_HAS_ASID
void check_and_switch_context(struct mm_struct *mm, struct task_struct *tsk);
-#define init_new_context(tsk,mm) ({ atomic64_set(&mm->context.id, 0); 0; })
+#define init_new_context(tsk,mm) ({ atomic64_set(&(mm)->context.id, 0); 0; })
#ifdef CONFIG_ARM_ERRATA_798181
void a15_erratum_get_cpumask(int this_cpu, struct mm_struct *mm,
diff --git a/arch/arm/include/asm/paravirt.h b/arch/arm/include/asm/paravirt.h
new file mode 100644
index 000000000000..8435ff591386
--- /dev/null
+++ b/arch/arm/include/asm/paravirt.h
@@ -0,0 +1,20 @@
+#ifndef _ASM_ARM_PARAVIRT_H
+#define _ASM_ARM_PARAVIRT_H
+
+#ifdef CONFIG_PARAVIRT
+struct static_key;
+extern struct static_key paravirt_steal_enabled;
+extern struct static_key paravirt_steal_rq_enabled;
+
+struct pv_time_ops {
+ unsigned long long (*steal_clock)(int cpu);
+};
+extern struct pv_time_ops pv_time_ops;
+
+static inline u64 paravirt_steal_clock(int cpu)
+{
+ return pv_time_ops.steal_clock(cpu);
+}
+#endif
+
+#endif
diff --git a/arch/arm/include/asm/psci.h b/arch/arm/include/asm/psci.h
index 68ee3ce17b82..b4c6d99364f1 100644
--- a/arch/arm/include/asm/psci.h
+++ b/arch/arm/include/asm/psci.h
@@ -16,7 +16,7 @@
extern struct smp_operations psci_smp_ops;
-#ifdef CONFIG_ARM_PSCI
+#if defined(CONFIG_SMP) && defined(CONFIG_ARM_PSCI)
bool psci_smp_available(void);
#else
static inline bool psci_smp_available(void) { return false; }
diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h
index e0adb9f1bf94..3613d7e9fc40 100644
--- a/arch/arm/include/asm/setup.h
+++ b/arch/arm/include/asm/setup.h
@@ -25,4 +25,10 @@ extern int arm_add_memory(u64 start, u64 size);
extern void early_print(const char *str, ...);
extern void dump_machine_table(void);
+#ifdef CONFIG_ATAGS_PROC
+extern void save_atags(const struct tag *tags);
+#else
+static inline void save_atags(const struct tag *tags) { }
+#endif
+
#endif
diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h
index 8cc85a4ebec2..35c9db857ebe 100644
--- a/arch/arm/include/asm/uaccess.h
+++ b/arch/arm/include/asm/uaccess.h
@@ -510,10 +510,14 @@ __copy_to_user_std(void __user *to, const void *from, unsigned long n);
static inline unsigned long __must_check
__copy_to_user(void __user *to, const void *from, unsigned long n)
{
+#ifndef CONFIG_UACCESS_WITH_MEMCPY
unsigned int __ua_flags = uaccess_save_and_enable();
n = arm_copy_to_user(to, from, n);
uaccess_restore(__ua_flags);
return n;
+#else
+ return arm_copy_to_user(to, from, n);
+#endif
}
extern unsigned long __must_check
diff --git a/arch/arm/include/asm/xen/hypercall.h b/arch/arm/include/asm/xen/hypercall.h
index 712b50e0a6dc..d769972db8cb 100644
--- a/arch/arm/include/asm/xen/hypercall.h
+++ b/arch/arm/include/asm/xen/hypercall.h
@@ -35,6 +35,7 @@
#include <xen/interface/xen.h>
#include <xen/interface/sched.h>
+#include <xen/interface/platform.h>
long privcmd_call(unsigned call, unsigned long a1,
unsigned long a2, unsigned long a3,
@@ -49,6 +50,12 @@ int HYPERVISOR_memory_op(unsigned int cmd, void *arg);
int HYPERVISOR_physdev_op(int cmd, void *arg);
int HYPERVISOR_vcpu_op(int cmd, int vcpuid, void *extra_args);
int HYPERVISOR_tmem_op(void *arg);
+int HYPERVISOR_platform_op_raw(void *arg);
+static inline int HYPERVISOR_platform_op(struct xen_platform_op *op)
+{
+ op->interface_version = XENPF_INTERFACE_VERSION;
+ return HYPERVISOR_platform_op_raw(op);
+}
int HYPERVISOR_multicall(struct multicall_entry *calls, uint32_t nr);
static inline int
diff --git a/arch/arm/include/asm/xen/interface.h b/arch/arm/include/asm/xen/interface.h
index 50066006e6bd..75d596862892 100644
--- a/arch/arm/include/asm/xen/interface.h
+++ b/arch/arm/include/asm/xen/interface.h
@@ -27,6 +27,8 @@
(hnd).p = val; \
} while (0)
+#define __HYPERVISOR_platform_op_raw __HYPERVISOR_platform_op
+
#ifndef __ASSEMBLY__
/* Explicitly size integers that represent pfns in the interface with
* Xen so that we can have one ABI that works for 32 and 64 bit guests.
@@ -76,6 +78,7 @@ struct pvclock_wall_clock {
u32 version;
u32 sec;
u32 nsec;
+ u32 sec_hi;
} __attribute__((__packed__));
#endif
diff --git a/arch/arm/include/uapi/asm/unistd.h b/arch/arm/include/uapi/asm/unistd.h
index 7a2a32a1d5a8..ede692ffa32e 100644
--- a/arch/arm/include/uapi/asm/unistd.h
+++ b/arch/arm/include/uapi/asm/unistd.h
@@ -416,6 +416,7 @@
#define __NR_execveat (__NR_SYSCALL_BASE+387)
#define __NR_userfaultfd (__NR_SYSCALL_BASE+388)
#define __NR_membarrier (__NR_SYSCALL_BASE+389)
+#define __NR_mlock2 (__NR_SYSCALL_BASE+390)
/*
* The following SWIs are ARM private.
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index af9e59bf3831..2c5f160be65e 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -73,14 +73,15 @@ obj-$(CONFIG_IWMMXT) += iwmmxt.o
obj-$(CONFIG_PERF_EVENTS) += perf_regs.o perf_callchain.o
obj-$(CONFIG_HW_PERF_EVENTS) += perf_event_xscale.o perf_event_v6.o \
perf_event_v7.o
-CFLAGS_pj4-cp0.o := -marm
AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o
obj-$(CONFIG_VDSO) += vdso.o
+obj-$(CONFIG_EFI) += efi.o
ifneq ($(CONFIG_ARCH_EBSA110),y)
obj-y += io.o
endif
+obj-$(CONFIG_PARAVIRT) += paravirt.o
head-y := head$(MMUEXT).o
obj-$(CONFIG_DEBUG_LL) += debug.o
@@ -88,8 +89,9 @@ obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-$(CONFIG_ARM_VIRT_EXT) += hyp-stub.o
ifeq ($(CONFIG_ARM_PSCI),y)
-obj-y += psci-call.o
obj-$(CONFIG_SMP) += psci_smp.o
endif
+obj-$(CONFIG_HAVE_ARM_SMCCC) += smccc-call.o
+
extra-y := $(head-y) vmlinux.lds
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
index f89811fb9a55..7e45f69a0ddc 100644
--- a/arch/arm/kernel/armksyms.c
+++ b/arch/arm/kernel/armksyms.c
@@ -16,6 +16,7 @@
#include <linux/syscalls.h>
#include <linux/uaccess.h>
#include <linux/io.h>
+#include <linux/arm-smccc.h>
#include <asm/checksum.h>
#include <asm/ftrace.h>
@@ -175,3 +176,8 @@ EXPORT_SYMBOL(__gnu_mcount_nc);
EXPORT_SYMBOL(__pv_phys_pfn_offset);
EXPORT_SYMBOL(__pv_offset);
#endif
+
+#ifdef CONFIG_HAVE_ARM_SMCCC
+EXPORT_SYMBOL(arm_smccc_smc);
+EXPORT_SYMBOL(arm_smccc_hvc);
+#endif
diff --git a/arch/arm/kernel/atags.h b/arch/arm/kernel/atags.h
index ec4164da6e30..edfa2268c127 100644
--- a/arch/arm/kernel/atags.h
+++ b/arch/arm/kernel/atags.h
@@ -1,9 +1,3 @@
-#ifdef CONFIG_ATAGS_PROC
-extern void save_atags(struct tag *tags);
-#else
-static inline void save_atags(struct tag *tags) { }
-#endif
-
void convert_to_tag_list(struct tag *tags);
#ifdef CONFIG_ATAGS
diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c
index 6551d28c27e6..066f7f9ba411 100644
--- a/arch/arm/kernel/bios32.c
+++ b/arch/arm/kernel/bios32.c
@@ -17,11 +17,6 @@
#include <asm/mach/pci.h>
static int debug_pci;
-static resource_size_t (*align_resource)(struct pci_dev *dev,
- const struct resource *res,
- resource_size_t start,
- resource_size_t size,
- resource_size_t align) = NULL;
/*
* We can't use pci_get_device() here since we are
@@ -461,7 +456,6 @@ static void pcibios_init_hw(struct device *parent, struct hw_pci *hw,
sys->busnr = busnr;
sys->swizzle = hw->swizzle;
sys->map_irq = hw->map_irq;
- align_resource = hw->align_resource;
INIT_LIST_HEAD(&sys->resources);
if (hw->private_data)
@@ -470,6 +464,8 @@ static void pcibios_init_hw(struct device *parent, struct hw_pci *hw,
ret = hw->setup(nr, sys);
if (ret > 0) {
+ struct pci_host_bridge *host_bridge;
+
ret = pcibios_init_resources(nr, sys);
if (ret) {
kfree(sys);
@@ -491,6 +487,9 @@ static void pcibios_init_hw(struct device *parent, struct hw_pci *hw,
busnr = sys->bus->busn_res.end + 1;
list_add(&sys->node, head);
+
+ host_bridge = pci_find_host_bridge(sys->bus);
+ host_bridge->align_resource = hw->align_resource;
} else {
kfree(sys);
if (ret < 0)
@@ -578,14 +577,18 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res,
{
struct pci_dev *dev = data;
resource_size_t start = res->start;
+ struct pci_host_bridge *host_bridge;
if (res->flags & IORESOURCE_IO && start & 0x300)
start = (start + 0x3ff) & ~0x3ff;
start = (start + align - 1) & ~(align - 1);
- if (align_resource)
- return align_resource(dev, res, start, size, align);
+ host_bridge = pci_find_host_bridge(dev->bus);
+
+ if (host_bridge->align_resource)
+ return host_bridge->align_resource(dev, res,
+ start, size, align);
return start;
}
diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S
index fde6c88d560c..ac368bb068d1 100644
--- a/arch/arm/kernel/calls.S
+++ b/arch/arm/kernel/calls.S
@@ -399,6 +399,7 @@
CALL(sys_execveat)
CALL(sys_userfaultfd)
CALL(sys_membarrier)
+ CALL(sys_mlock2)
#ifndef syscalls_counted
.equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls
#define syscalls_counted
diff --git a/arch/arm/kernel/cpuidle.c b/arch/arm/kernel/cpuidle.c
index 318da33465f4..703926e7007b 100644
--- a/arch/arm/kernel/cpuidle.c
+++ b/arch/arm/kernel/cpuidle.c
@@ -56,7 +56,7 @@ int arm_cpuidle_suspend(int index)
int cpu = smp_processor_id();
if (cpuidle_ops[cpu].suspend)
- ret = cpuidle_ops[cpu].suspend(cpu, index);
+ ret = cpuidle_ops[cpu].suspend(index);
return ret;
}
diff --git a/arch/arm/kernel/efi.c b/arch/arm/kernel/efi.c
new file mode 100644
index 000000000000..ff8a9d8acfac
--- /dev/null
+++ b/arch/arm/kernel/efi.c
@@ -0,0 +1,38 @@
+/*
+ * 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 <linux/efi.h>
+#include <asm/efi.h>
+#include <asm/mach/map.h>
+#include <asm/mmu_context.h>
+
+int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
+{
+ struct map_desc desc = {
+ .virtual = md->virt_addr,
+ .pfn = __phys_to_pfn(md->phys_addr),
+ .length = md->num_pages * EFI_PAGE_SIZE,
+ };
+
+ /*
+ * Order is important here: memory regions may have all of the
+ * bits below set (and usually do), so we check them in order of
+ * preference.
+ */
+ if (md->attribute & EFI_MEMORY_WB)
+ desc.type = MT_MEMORY_RWX;
+ else if (md->attribute & EFI_MEMORY_WT)
+ desc.type = MT_MEMORY_RWX_NONCACHED;
+ else if (md->attribute & EFI_MEMORY_WC)
+ desc.type = MT_DEVICE_WC;
+ else
+ desc.type = MT_DEVICE;
+
+ create_mapping_late(mm, &desc, true);
+ return 0;
+}
diff --git a/arch/arm/kernel/entry-v7m.S b/arch/arm/kernel/entry-v7m.S
index b6c8bb9315e7..907534f97053 100644
--- a/arch/arm/kernel/entry-v7m.S
+++ b/arch/arm/kernel/entry-v7m.S
@@ -88,7 +88,7 @@ __pendsv_entry:
@ execute the pending work, including reschedule
get_thread_info tsk
mov why, #0
- b ret_to_user
+ b ret_to_user_from_irq
ENDPROC(__pendsv_entry)
/*
diff --git a/arch/arm/kernel/module-plts.c b/arch/arm/kernel/module-plts.c
index 097e2e201b9f..0c7efc3446c0 100644
--- a/arch/arm/kernel/module-plts.c
+++ b/arch/arm/kernel/module-plts.c
@@ -32,7 +32,7 @@ struct plt_entries {
static bool in_init(const struct module *mod, u32 addr)
{
- return addr - (u32)mod->module_init < mod->init_size;
+ return addr - (u32)mod->init_layout.base < mod->init_layout.size;
}
u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val)
diff --git a/arch/arm64/kernel/psci-call.S b/arch/arm/kernel/paravirt.c
index cf83e61cd3b5..53f371ed4568 100644
--- a/arch/arm64/kernel/psci-call.S
+++ b/arch/arm/kernel/paravirt.c
@@ -8,21 +8,18 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * Copyright (C) 2015 ARM Limited
+ * Copyright (C) 2013 Citrix Systems
*
- * Author: Will Deacon <will.deacon@arm.com>
+ * Author: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
*/
-#include <linux/linkage.h>
+#include <linux/export.h>
+#include <linux/jump_label.h>
+#include <linux/types.h>
+#include <asm/paravirt.h>
-/* int __invoke_psci_fn_hvc(u64 function_id, u64 arg0, u64 arg1, u64 arg2) */
-ENTRY(__invoke_psci_fn_hvc)
- hvc #0
- ret
-ENDPROC(__invoke_psci_fn_hvc)
+struct static_key paravirt_steal_enabled;
+struct static_key paravirt_steal_rq_enabled;
-/* int __invoke_psci_fn_smc(u64 function_id, u64 arg0, u64 arg1, u64 arg2) */
-ENTRY(__invoke_psci_fn_smc)
- smc #0
- ret
-ENDPROC(__invoke_psci_fn_smc)
+struct pv_time_ops pv_time_ops;
+EXPORT_SYMBOL_GPL(pv_time_ops);
diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c
index 126dc679b230..4152158f6e6a 100644
--- a/arch/arm/kernel/perf_event_v7.c
+++ b/arch/arm/kernel/perf_event_v7.c
@@ -35,133 +35,117 @@
* but the encodings are considered to be `reserved' in the case that
* they are not available.
*/
-enum armv7_perf_types {
- ARMV7_PERFCTR_PMNC_SW_INCR = 0x00,
- ARMV7_PERFCTR_L1_ICACHE_REFILL = 0x01,
- ARMV7_PERFCTR_ITLB_REFILL = 0x02,
- ARMV7_PERFCTR_L1_DCACHE_REFILL = 0x03,
- ARMV7_PERFCTR_L1_DCACHE_ACCESS = 0x04,
- ARMV7_PERFCTR_DTLB_REFILL = 0x05,
- ARMV7_PERFCTR_MEM_READ = 0x06,
- ARMV7_PERFCTR_MEM_WRITE = 0x07,
- ARMV7_PERFCTR_INSTR_EXECUTED = 0x08,
- ARMV7_PERFCTR_EXC_TAKEN = 0x09,
- ARMV7_PERFCTR_EXC_EXECUTED = 0x0A,
- ARMV7_PERFCTR_CID_WRITE = 0x0B,
+#define ARMV7_PERFCTR_PMNC_SW_INCR 0x00
+#define ARMV7_PERFCTR_L1_ICACHE_REFILL 0x01
+#define ARMV7_PERFCTR_ITLB_REFILL 0x02
+#define ARMV7_PERFCTR_L1_DCACHE_REFILL 0x03
+#define ARMV7_PERFCTR_L1_DCACHE_ACCESS 0x04
+#define ARMV7_PERFCTR_DTLB_REFILL 0x05
+#define ARMV7_PERFCTR_MEM_READ 0x06
+#define ARMV7_PERFCTR_MEM_WRITE 0x07
+#define ARMV7_PERFCTR_INSTR_EXECUTED 0x08
+#define ARMV7_PERFCTR_EXC_TAKEN 0x09
+#define ARMV7_PERFCTR_EXC_EXECUTED 0x0A
+#define ARMV7_PERFCTR_CID_WRITE 0x0B
- /*
- * ARMV7_PERFCTR_PC_WRITE is equivalent to HW_BRANCH_INSTRUCTIONS.
- * It counts:
- * - all (taken) branch instructions,
- * - instructions that explicitly write the PC,
- * - exception generating instructions.
- */
- ARMV7_PERFCTR_PC_WRITE = 0x0C,
- ARMV7_PERFCTR_PC_IMM_BRANCH = 0x0D,
- ARMV7_PERFCTR_PC_PROC_RETURN = 0x0E,
- ARMV7_PERFCTR_MEM_UNALIGNED_ACCESS = 0x0F,
- ARMV7_PERFCTR_PC_BRANCH_MIS_PRED = 0x10,
- ARMV7_PERFCTR_CLOCK_CYCLES = 0x11,
- ARMV7_PERFCTR_PC_BRANCH_PRED = 0x12,
-
- /* These events are defined by the PMUv2 supplement (ARM DDI 0457A). */
- ARMV7_PERFCTR_MEM_ACCESS = 0x13,
- ARMV7_PERFCTR_L1_ICACHE_ACCESS = 0x14,
- ARMV7_PERFCTR_L1_DCACHE_WB = 0x15,
- ARMV7_PERFCTR_L2_CACHE_ACCESS = 0x16,
- ARMV7_PERFCTR_L2_CACHE_REFILL = 0x17,
- ARMV7_PERFCTR_L2_CACHE_WB = 0x18,
- ARMV7_PERFCTR_BUS_ACCESS = 0x19,
- ARMV7_PERFCTR_MEM_ERROR = 0x1A,
- ARMV7_PERFCTR_INSTR_SPEC = 0x1B,
- ARMV7_PERFCTR_TTBR_WRITE = 0x1C,
- ARMV7_PERFCTR_BUS_CYCLES = 0x1D,
-
- ARMV7_PERFCTR_CPU_CYCLES = 0xFF
-};
+/*
+ * ARMV7_PERFCTR_PC_WRITE is equivalent to HW_BRANCH_INSTRUCTIONS.
+ * It counts:
+ * - all (taken) branch instructions,
+ * - instructions that explicitly write the PC,
+ * - exception generating instructions.
+ */
+#define ARMV7_PERFCTR_PC_WRITE 0x0C
+#define ARMV7_PERFCTR_PC_IMM_BRANCH 0x0D
+#define ARMV7_PERFCTR_PC_PROC_RETURN 0x0E
+#define ARMV7_PERFCTR_MEM_UNALIGNED_ACCESS 0x0F
+#define ARMV7_PERFCTR_PC_BRANCH_MIS_PRED 0x10
+#define ARMV7_PERFCTR_CLOCK_CYCLES 0x11
+#define ARMV7_PERFCTR_PC_BRANCH_PRED 0x12
+
+/* These events are defined by the PMUv2 supplement (ARM DDI 0457A). */
+#define ARMV7_PERFCTR_MEM_ACCESS 0x13
+#define ARMV7_PERFCTR_L1_ICACHE_ACCESS 0x14
+#define ARMV7_PERFCTR_L1_DCACHE_WB 0x15
+#define ARMV7_PERFCTR_L2_CACHE_ACCESS 0x16
+#define ARMV7_PERFCTR_L2_CACHE_REFILL 0x17
+#define ARMV7_PERFCTR_L2_CACHE_WB 0x18
+#define ARMV7_PERFCTR_BUS_ACCESS 0x19
+#define ARMV7_PERFCTR_MEM_ERROR 0x1A
+#define ARMV7_PERFCTR_INSTR_SPEC 0x1B
+#define ARMV7_PERFCTR_TTBR_WRITE 0x1C
+#define ARMV7_PERFCTR_BUS_CYCLES 0x1D
+
+#define ARMV7_PERFCTR_CPU_CYCLES 0xFF
/* ARMv7 Cortex-A8 specific event types */
-enum armv7_a8_perf_types {
- ARMV7_A8_PERFCTR_L2_CACHE_ACCESS = 0x43,
- ARMV7_A8_PERFCTR_L2_CACHE_REFILL = 0x44,
- ARMV7_A8_PERFCTR_L1_ICACHE_ACCESS = 0x50,
- ARMV7_A8_PERFCTR_STALL_ISIDE = 0x56,
-};
+#define ARMV7_A8_PERFCTR_L2_CACHE_ACCESS 0x43
+#define ARMV7_A8_PERFCTR_L2_CACHE_REFILL 0x44
+#define ARMV7_A8_PERFCTR_L1_ICACHE_ACCESS 0x50
+#define ARMV7_A8_PERFCTR_STALL_ISIDE 0x56
/* ARMv7 Cortex-A9 specific event types */
-enum armv7_a9_perf_types {
- ARMV7_A9_PERFCTR_INSTR_CORE_RENAME = 0x68,
- ARMV7_A9_PERFCTR_STALL_ICACHE = 0x60,
- ARMV7_A9_PERFCTR_STALL_DISPATCH = 0x66,
-};
+#define ARMV7_A9_PERFCTR_INSTR_CORE_RENAME 0x68
+#define ARMV7_A9_PERFCTR_STALL_ICACHE 0x60
+#define ARMV7_A9_PERFCTR_STALL_DISPATCH 0x66
/* ARMv7 Cortex-A5 specific event types */
-enum armv7_a5_perf_types {
- ARMV7_A5_PERFCTR_PREFETCH_LINEFILL = 0xc2,
- ARMV7_A5_PERFCTR_PREFETCH_LINEFILL_DROP = 0xc3,
-};
+#define ARMV7_A5_PERFCTR_PREFETCH_LINEFILL 0xc2
+#define ARMV7_A5_PERFCTR_PREFETCH_LINEFILL_DROP 0xc3
/* ARMv7 Cortex-A15 specific event types */
-enum armv7_a15_perf_types {
- ARMV7_A15_PERFCTR_L1_DCACHE_ACCESS_READ = 0x40,
- ARMV7_A15_PERFCTR_L1_DCACHE_ACCESS_WRITE = 0x41,
- ARMV7_A15_PERFCTR_L1_DCACHE_REFILL_READ = 0x42,
- ARMV7_A15_PERFCTR_L1_DCACHE_REFILL_WRITE = 0x43,
+#define ARMV7_A15_PERFCTR_L1_DCACHE_ACCESS_READ 0x40
+#define ARMV7_A15_PERFCTR_L1_DCACHE_ACCESS_WRITE 0x41
+#define ARMV7_A15_PERFCTR_L1_DCACHE_REFILL_READ 0x42
+#define ARMV7_A15_PERFCTR_L1_DCACHE_REFILL_WRITE 0x43
- ARMV7_A15_PERFCTR_DTLB_REFILL_L1_READ = 0x4C,
- ARMV7_A15_PERFCTR_DTLB_REFILL_L1_WRITE = 0x4D,
+#define ARMV7_A15_PERFCTR_DTLB_REFILL_L1_READ 0x4C
+#define ARMV7_A15_PERFCTR_DTLB_REFILL_L1_WRITE 0x4D
- ARMV7_A15_PERFCTR_L2_CACHE_ACCESS_READ = 0x50,
- ARMV7_A15_PERFCTR_L2_CACHE_ACCESS_WRITE = 0x51,
- ARMV7_A15_PERFCTR_L2_CACHE_REFILL_READ = 0x52,
- ARMV7_A15_PERFCTR_L2_CACHE_REFILL_WRITE = 0x53,
+#define ARMV7_A15_PERFCTR_L2_CACHE_ACCESS_READ 0x50
+#define ARMV7_A15_PERFCTR_L2_CACHE_ACCESS_WRITE 0x51
+#define ARMV7_A15_PERFCTR_L2_CACHE_REFILL_READ 0x52
+#define ARMV7_A15_PERFCTR_L2_CACHE_REFILL_WRITE 0x53
- ARMV7_A15_PERFCTR_PC_WRITE_SPEC = 0x76,
-};
+#define ARMV7_A15_PERFCTR_PC_WRITE_SPEC 0x76
/* ARMv7 Cortex-A12 specific event types */
-enum armv7_a12_perf_types {
- ARMV7_A12_PERFCTR_L1_DCACHE_ACCESS_READ = 0x40,
- ARMV7_A12_PERFCTR_L1_DCACHE_ACCESS_WRITE = 0x41,
+#define ARMV7_A12_PERFCTR_L1_DCACHE_ACCESS_READ 0x40
+#define ARMV7_A12_PERFCTR_L1_DCACHE_ACCESS_WRITE 0x41
- ARMV7_A12_PERFCTR_L2_CACHE_ACCESS_READ = 0x50,
- ARMV7_A12_PERFCTR_L2_CACHE_ACCESS_WRITE = 0x51,
+#define ARMV7_A12_PERFCTR_L2_CACHE_ACCESS_READ 0x50
+#define ARMV7_A12_PERFCTR_L2_CACHE_ACCESS_WRITE 0x51
- ARMV7_A12_PERFCTR_PC_WRITE_SPEC = 0x76,
+#define ARMV7_A12_PERFCTR_PC_WRITE_SPEC 0x76
- ARMV7_A12_PERFCTR_PF_TLB_REFILL = 0xe7,
-};
+#define ARMV7_A12_PERFCTR_PF_TLB_REFILL 0xe7
/* ARMv7 Krait specific event types */
-enum krait_perf_types {
- KRAIT_PMRESR0_GROUP0 = 0xcc,
- KRAIT_PMRESR1_GROUP0 = 0xd0,
- KRAIT_PMRESR2_GROUP0 = 0xd4,
- KRAIT_VPMRESR0_GROUP0 = 0xd8,
+#define KRAIT_PMRESR0_GROUP0 0xcc
+#define KRAIT_PMRESR1_GROUP0 0xd0
+#define KRAIT_PMRESR2_GROUP0 0xd4
+#define KRAIT_VPMRESR0_GROUP0 0xd8
- KRAIT_PERFCTR_L1_ICACHE_ACCESS = 0x10011,
- KRAIT_PERFCTR_L1_ICACHE_MISS = 0x10010,
+#define KRAIT_PERFCTR_L1_ICACHE_ACCESS 0x10011
+#define KRAIT_PERFCTR_L1_ICACHE_MISS 0x10010
- KRAIT_PERFCTR_L1_ITLB_ACCESS = 0x12222,
- KRAIT_PERFCTR_L1_DTLB_ACCESS = 0x12210,
-};
+#define KRAIT_PERFCTR_L1_ITLB_ACCESS 0x12222
+#define KRAIT_PERFCTR_L1_DTLB_ACCESS 0x12210
/* ARMv7 Scorpion specific event types */
-enum scorpion_perf_types {
- SCORPION_LPM0_GROUP0 = 0x4c,
- SCORPION_LPM1_GROUP0 = 0x50,
- SCORPION_LPM2_GROUP0 = 0x54,
- SCORPION_L2LPM_GROUP0 = 0x58,
- SCORPION_VLPM_GROUP0 = 0x5c,
+#define SCORPION_LPM0_GROUP0 0x4c
+#define SCORPION_LPM1_GROUP0 0x50
+#define SCORPION_LPM2_GROUP0 0x54
+#define SCORPION_L2LPM_GROUP0 0x58
+#define SCORPION_VLPM_GROUP0 0x5c
- SCORPION_ICACHE_ACCESS = 0x10053,
- SCORPION_ICACHE_MISS = 0x10052,
+#define SCORPION_ICACHE_ACCESS 0x10053
+#define SCORPION_ICACHE_MISS 0x10052
- SCORPION_DTLB_ACCESS = 0x12013,
- SCORPION_DTLB_MISS = 0x12012,
+#define SCORPION_DTLB_ACCESS 0x12013
+#define SCORPION_DTLB_MISS 0x12012
- SCORPION_ITLB_MISS = 0x12021,
-};
+#define SCORPION_ITLB_MISS 0x12021
/*
* Cortex-A8 HW events mapping
@@ -547,6 +531,134 @@ static const unsigned scorpion_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
[C(BPU)][C(OP_WRITE)][C(RESULT_MISS)] = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
};
+PMU_FORMAT_ATTR(event, "config:0-7");
+
+static struct attribute *armv7_pmu_format_attrs[] = {
+ &format_attr_event.attr,
+ NULL,
+};
+
+static struct attribute_group armv7_pmu_format_attr_group = {
+ .name = "format",
+ .attrs = armv7_pmu_format_attrs,
+};
+
+#define ARMV7_EVENT_ATTR_RESOLVE(m) #m
+#define ARMV7_EVENT_ATTR(name, config) \
+ PMU_EVENT_ATTR_STRING(name, armv7_event_attr_##name, \
+ "event=" ARMV7_EVENT_ATTR_RESOLVE(config))
+
+ARMV7_EVENT_ATTR(sw_incr, ARMV7_PERFCTR_PMNC_SW_INCR);
+ARMV7_EVENT_ATTR(l1i_cache_refill, ARMV7_PERFCTR_L1_ICACHE_REFILL);
+ARMV7_EVENT_ATTR(l1i_tlb_refill, ARMV7_PERFCTR_ITLB_REFILL);
+ARMV7_EVENT_ATTR(l1d_cache_refill, ARMV7_PERFCTR_L1_DCACHE_REFILL);
+ARMV7_EVENT_ATTR(l1d_cache, ARMV7_PERFCTR_L1_DCACHE_ACCESS);
+ARMV7_EVENT_ATTR(l1d_tlb_refill, ARMV7_PERFCTR_DTLB_REFILL);
+ARMV7_EVENT_ATTR(ld_retired, ARMV7_PERFCTR_MEM_READ);
+ARMV7_EVENT_ATTR(st_retired, ARMV7_PERFCTR_MEM_WRITE);
+ARMV7_EVENT_ATTR(inst_retired, ARMV7_PERFCTR_INSTR_EXECUTED);
+ARMV7_EVENT_ATTR(exc_taken, ARMV7_PERFCTR_EXC_TAKEN);
+ARMV7_EVENT_ATTR(exc_return, ARMV7_PERFCTR_EXC_EXECUTED);
+ARMV7_EVENT_ATTR(cid_write_retired, ARMV7_PERFCTR_CID_WRITE);
+ARMV7_EVENT_ATTR(pc_write_retired, ARMV7_PERFCTR_PC_WRITE);
+ARMV7_EVENT_ATTR(br_immed_retired, ARMV7_PERFCTR_PC_IMM_BRANCH);
+ARMV7_EVENT_ATTR(br_return_retired, ARMV7_PERFCTR_PC_PROC_RETURN);
+ARMV7_EVENT_ATTR(unaligned_ldst_retired, ARMV7_PERFCTR_MEM_UNALIGNED_ACCESS);
+ARMV7_EVENT_ATTR(br_mis_pred, ARMV7_PERFCTR_PC_BRANCH_MIS_PRED);
+ARMV7_EVENT_ATTR(cpu_cycles, ARMV7_PERFCTR_CLOCK_CYCLES);
+ARMV7_EVENT_ATTR(br_pred, ARMV7_PERFCTR_PC_BRANCH_PRED);
+
+static struct attribute *armv7_pmuv1_event_attrs[] = {
+ &armv7_event_attr_sw_incr.attr.attr,
+ &armv7_event_attr_l1i_cache_refill.attr.attr,
+ &armv7_event_attr_l1i_tlb_refill.attr.attr,
+ &armv7_event_attr_l1d_cache_refill.attr.attr,
+ &armv7_event_attr_l1d_cache.attr.attr,
+ &armv7_event_attr_l1d_tlb_refill.attr.attr,
+ &armv7_event_attr_ld_retired.attr.attr,
+ &armv7_event_attr_st_retired.attr.attr,
+ &armv7_event_attr_inst_retired.attr.attr,
+ &armv7_event_attr_exc_taken.attr.attr,
+ &armv7_event_attr_exc_return.attr.attr,
+ &armv7_event_attr_cid_write_retired.attr.attr,
+ &armv7_event_attr_pc_write_retired.attr.attr,
+ &armv7_event_attr_br_immed_retired.attr.attr,
+ &armv7_event_attr_br_return_retired.attr.attr,
+ &armv7_event_attr_unaligned_ldst_retired.attr.attr,
+ &armv7_event_attr_br_mis_pred.attr.attr,
+ &armv7_event_attr_cpu_cycles.attr.attr,
+ &armv7_event_attr_br_pred.attr.attr,
+ NULL,
+};
+
+static struct attribute_group armv7_pmuv1_events_attr_group = {
+ .name = "events",
+ .attrs = armv7_pmuv1_event_attrs,
+};
+
+static const struct attribute_group *armv7_pmuv1_attr_groups[] = {
+ &armv7_pmuv1_events_attr_group,
+ &armv7_pmu_format_attr_group,
+ NULL,
+};
+
+ARMV7_EVENT_ATTR(mem_access, ARMV7_PERFCTR_MEM_ACCESS);
+ARMV7_EVENT_ATTR(l1i_cache, ARMV7_PERFCTR_L1_ICACHE_ACCESS);
+ARMV7_EVENT_ATTR(l1d_cache_wb, ARMV7_PERFCTR_L1_DCACHE_WB);
+ARMV7_EVENT_ATTR(l2d_cache, ARMV7_PERFCTR_L2_CACHE_ACCESS);
+ARMV7_EVENT_ATTR(l2d_cache_refill, ARMV7_PERFCTR_L2_CACHE_REFILL);
+ARMV7_EVENT_ATTR(l2d_cache_wb, ARMV7_PERFCTR_L2_CACHE_WB);
+ARMV7_EVENT_ATTR(bus_access, ARMV7_PERFCTR_BUS_ACCESS);
+ARMV7_EVENT_ATTR(memory_error, ARMV7_PERFCTR_MEM_ERROR);
+ARMV7_EVENT_ATTR(inst_spec, ARMV7_PERFCTR_INSTR_SPEC);
+ARMV7_EVENT_ATTR(ttbr_write_retired, ARMV7_PERFCTR_TTBR_WRITE);
+ARMV7_EVENT_ATTR(bus_cycles, ARMV7_PERFCTR_BUS_CYCLES);
+
+static struct attribute *armv7_pmuv2_event_attrs[] = {
+ &armv7_event_attr_sw_incr.attr.attr,
+ &armv7_event_attr_l1i_cache_refill.attr.attr,
+ &armv7_event_attr_l1i_tlb_refill.attr.attr,
+ &armv7_event_attr_l1d_cache_refill.attr.attr,
+ &armv7_event_attr_l1d_cache.attr.attr,
+ &armv7_event_attr_l1d_tlb_refill.attr.attr,
+ &armv7_event_attr_ld_retired.attr.attr,
+ &armv7_event_attr_st_retired.attr.attr,
+ &armv7_event_attr_inst_retired.attr.attr,
+ &armv7_event_attr_exc_taken.attr.attr,
+ &armv7_event_attr_exc_return.attr.attr,
+ &armv7_event_attr_cid_write_retired.attr.attr,
+ &armv7_event_attr_pc_write_retired.attr.attr,
+ &armv7_event_attr_br_immed_retired.attr.attr,
+ &armv7_event_attr_br_return_retired.attr.attr,
+ &armv7_event_attr_unaligned_ldst_retired.attr.attr,
+ &armv7_event_attr_br_mis_pred.attr.attr,
+ &armv7_event_attr_cpu_cycles.attr.attr,
+ &armv7_event_attr_br_pred.attr.attr,
+ &armv7_event_attr_mem_access.attr.attr,
+ &armv7_event_attr_l1i_cache.attr.attr,
+ &armv7_event_attr_l1d_cache_wb.attr.attr,
+ &armv7_event_attr_l2d_cache.attr.attr,
+ &armv7_event_attr_l2d_cache_refill.attr.attr,
+ &armv7_event_attr_l2d_cache_wb.attr.attr,
+ &armv7_event_attr_bus_access.attr.attr,
+ &armv7_event_attr_memory_error.attr.attr,
+ &armv7_event_attr_inst_spec.attr.attr,
+ &armv7_event_attr_ttbr_write_retired.attr.attr,
+ &armv7_event_attr_bus_cycles.attr.attr,
+ NULL,
+};
+
+static struct attribute_group armv7_pmuv2_events_attr_group = {
+ .name = "events",
+ .attrs = armv7_pmuv2_event_attrs,
+};
+
+static const struct attribute_group *armv7_pmuv2_attr_groups[] = {
+ &armv7_pmuv2_events_attr_group,
+ &armv7_pmu_format_attr_group,
+ NULL,
+};
+
/*
* Perf Events' indices
*/
@@ -1085,6 +1197,7 @@ static int armv7_a8_pmu_init(struct arm_pmu *cpu_pmu)
armv7pmu_init(cpu_pmu);
cpu_pmu->name = "armv7_cortex_a8";
cpu_pmu->map_event = armv7_a8_map_event;
+ cpu_pmu->pmu.attr_groups = armv7_pmuv1_attr_groups;
return armv7_probe_num_events(cpu_pmu);
}
@@ -1093,6 +1206,7 @@ static int armv7_a9_pmu_init(struct arm_pmu *cpu_pmu)
armv7pmu_init(cpu_pmu);
cpu_pmu->name = "armv7_cortex_a9";
cpu_pmu->map_event = armv7_a9_map_event;
+ cpu_pmu->pmu.attr_groups = armv7_pmuv1_attr_groups;
return armv7_probe_num_events(cpu_pmu);
}
@@ -1101,6 +1215,7 @@ static int armv7_a5_pmu_init(struct arm_pmu *cpu_pmu)
armv7pmu_init(cpu_pmu);
cpu_pmu->name = "armv7_cortex_a5";
cpu_pmu->map_event = armv7_a5_map_event;
+ cpu_pmu->pmu.attr_groups = armv7_pmuv1_attr_groups;
return armv7_probe_num_events(cpu_pmu);
}
@@ -1110,6 +1225,7 @@ static int armv7_a15_pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->name = "armv7_cortex_a15";
cpu_pmu->map_event = armv7_a15_map_event;
cpu_pmu->set_event_filter = armv7pmu_set_event_filter;
+ cpu_pmu->pmu.attr_groups = armv7_pmuv2_attr_groups;
return armv7_probe_num_events(cpu_pmu);
}
@@ -1119,6 +1235,7 @@ static int armv7_a7_pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->name = "armv7_cortex_a7";
cpu_pmu->map_event = armv7_a7_map_event;
cpu_pmu->set_event_filter = armv7pmu_set_event_filter;
+ cpu_pmu->pmu.attr_groups = armv7_pmuv2_attr_groups;
return armv7_probe_num_events(cpu_pmu);
}
@@ -1128,6 +1245,7 @@ static int armv7_a12_pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->name = "armv7_cortex_a12";
cpu_pmu->map_event = armv7_a12_map_event;
cpu_pmu->set_event_filter = armv7pmu_set_event_filter;
+ cpu_pmu->pmu.attr_groups = armv7_pmuv2_attr_groups;
return armv7_probe_num_events(cpu_pmu);
}
@@ -1135,6 +1253,7 @@ static int armv7_a17_pmu_init(struct arm_pmu *cpu_pmu)
{
int ret = armv7_a12_pmu_init(cpu_pmu);
cpu_pmu->name = "armv7_cortex_a17";
+ cpu_pmu->pmu.attr_groups = armv7_pmuv2_attr_groups;
return ret;
}
diff --git a/arch/arm/kernel/pj4-cp0.c b/arch/arm/kernel/pj4-cp0.c
index 8153e36b2491..7c9248b74d3f 100644
--- a/arch/arm/kernel/pj4-cp0.c
+++ b/arch/arm/kernel/pj4-cp0.c
@@ -66,9 +66,13 @@ static void __init pj4_cp_access_write(u32 value)
__asm__ __volatile__ (
"mcr p15, 0, %1, c1, c0, 2\n\t"
+#ifdef CONFIG_THUMB2_KERNEL
+ "isb\n\t"
+#else
"mrc p15, 0, %0, c1, c0, 2\n\t"
"mov %0, %0\n\t"
"sub pc, pc, #4\n\t"
+#endif
: "=r" (temp) : "r" (value));
}
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index 7a7c4cea5523..4adfb46e3ee9 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -95,6 +95,22 @@ void __show_regs(struct pt_regs *regs)
{
unsigned long flags;
char buf[64];
+#ifndef CONFIG_CPU_V7M
+ unsigned int domain;
+#ifdef CONFIG_CPU_SW_DOMAIN_PAN
+ /*
+ * Get the domain register for the parent context. In user
+ * mode, we don't save the DACR, so lets use what it should
+ * be. For other modes, we place it after the pt_regs struct.
+ */
+ if (user_mode(regs))
+ domain = DACR_UACCESS_ENABLE;
+ else
+ domain = *(unsigned int *)(regs + 1);
+#else
+ domain = get_domain();
+#endif
+#endif
show_regs_print_info(KERN_DEFAULT);
@@ -123,21 +139,8 @@ void __show_regs(struct pt_regs *regs)
#ifndef CONFIG_CPU_V7M
{
- unsigned int domain = get_domain();
const char *segment;
-#ifdef CONFIG_CPU_SW_DOMAIN_PAN
- /*
- * Get the domain register for the parent context. In user
- * mode, we don't save the DACR, so lets use what it should
- * be. For other modes, we place it after the pt_regs struct.
- */
- if (user_mode(regs))
- domain = DACR_UACCESS_ENABLE;
- else
- domain = *(unsigned int *)(regs + 1);
-#endif
-
if ((domain & domain_mask(DOMAIN_USER)) ==
domain_val(DOMAIN_USER, DOMAIN_NOACCESS))
segment = "none";
@@ -163,11 +166,11 @@ void __show_regs(struct pt_regs *regs)
buf[0] = '\0';
#ifdef CONFIG_CPU_CP15_MMU
{
- unsigned int transbase, dac = get_domain();
+ unsigned int transbase;
asm("mrc p15, 0, %0, c2, c0\n\t"
: "=r" (transbase));
snprintf(buf, sizeof(buf), " Table: %08x DAC: %08x",
- transbase, dac);
+ transbase, domain);
}
#endif
asm("mrc p15, 0, %0, c1, c0\n" : "=r" (ctrl));
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 20edd349d379..7d0cba6f1cc5 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -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/efi.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
@@ -37,7 +38,9 @@
#include <asm/cp15.h>
#include <asm/cpu.h>
#include <asm/cputype.h>
+#include <asm/efi.h>
#include <asm/elf.h>
+#include <asm/early_ioremap.h>
#include <asm/fixmap.h>
#include <asm/procinfo.h>
#include <asm/psci.h>
@@ -375,6 +378,72 @@ void __init early_print(const char *str, ...)
printk("%s", buf);
}
+#ifdef CONFIG_ARM_PATCH_IDIV
+
+static inline u32 __attribute_const__ sdiv_instruction(void)
+{
+ if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) {
+ /* "sdiv r0, r0, r1" */
+ u32 insn = __opcode_thumb32_compose(0xfb90, 0xf0f1);
+ return __opcode_to_mem_thumb32(insn);
+ }
+
+ /* "sdiv r0, r0, r1" */
+ return __opcode_to_mem_arm(0xe710f110);
+}
+
+static inline u32 __attribute_const__ udiv_instruction(void)
+{
+ if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) {
+ /* "udiv r0, r0, r1" */
+ u32 insn = __opcode_thumb32_compose(0xfbb0, 0xf0f1);
+ return __opcode_to_mem_thumb32(insn);
+ }
+
+ /* "udiv r0, r0, r1" */
+ return __opcode_to_mem_arm(0xe730f110);
+}
+
+static inline u32 __attribute_const__ bx_lr_instruction(void)
+{
+ if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) {
+ /* "bx lr; nop" */
+ u32 insn = __opcode_thumb32_compose(0x4770, 0x46c0);
+ return __opcode_to_mem_thumb32(insn);
+ }
+
+ /* "bx lr" */
+ return __opcode_to_mem_arm(0xe12fff1e);
+}
+
+static void __init patch_aeabi_idiv(void)
+{
+ extern void __aeabi_uidiv(void);
+ extern void __aeabi_idiv(void);
+ uintptr_t fn_addr;
+ unsigned int mask;
+
+ mask = IS_ENABLED(CONFIG_THUMB2_KERNEL) ? HWCAP_IDIVT : HWCAP_IDIVA;
+ if (!(elf_hwcap & mask))
+ return;
+
+ pr_info("CPU: div instructions available: patching division code\n");
+
+ fn_addr = ((uintptr_t)&__aeabi_uidiv) & ~1;
+ ((u32 *)fn_addr)[0] = udiv_instruction();
+ ((u32 *)fn_addr)[1] = bx_lr_instruction();
+ flush_icache_range(fn_addr, fn_addr + 8);
+
+ fn_addr = ((uintptr_t)&__aeabi_idiv) & ~1;
+ ((u32 *)fn_addr)[0] = sdiv_instruction();
+ ((u32 *)fn_addr)[1] = bx_lr_instruction();
+ flush_icache_range(fn_addr, fn_addr + 8);
+}
+
+#else
+static inline void patch_aeabi_idiv(void) { }
+#endif
+
static void __init cpuid_init_hwcaps(void)
{
int block;
@@ -642,6 +711,7 @@ static void __init setup_processor(void)
elf_hwcap = list->elf_hwcap;
cpuid_init_hwcaps();
+ patch_aeabi_idiv();
#ifndef CONFIG_ARM_THUMB
elf_hwcap &= ~(HWCAP_THUMB | HWCAP_IDIVT);
@@ -956,8 +1026,8 @@ void __init setup_arch(char **cmdline_p)
strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
*cmdline_p = cmd_line;
- if (IS_ENABLED(CONFIG_FIX_EARLYCON_MEM))
- early_fixmap_init();
+ early_fixmap_init();
+ early_ioremap_init();
parse_early_param();
@@ -965,9 +1035,12 @@ void __init setup_arch(char **cmdline_p)
early_paging_init(mdesc);
#endif
setup_dma_zone(mdesc);
+ efi_init();
sanity_check_meminfo();
arm_memblock_init(mdesc);
+ early_ioremap_reset();
+
paging_init(mdesc);
request_standard_resources(mdesc);
diff --git a/arch/arm/kernel/smccc-call.S b/arch/arm/kernel/smccc-call.S
new file mode 100644
index 000000000000..2e48b674aab1
--- /dev/null
+++ b/arch/arm/kernel/smccc-call.S
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ *
+ * 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/linkage.h>
+
+#include <asm/opcodes-sec.h>
+#include <asm/opcodes-virt.h>
+#include <asm/unwind.h>
+
+ /*
+ * Wrap c macros in asm macros to delay expansion until after the
+ * SMCCC asm macro is expanded.
+ */
+ .macro SMCCC_SMC
+ __SMC(0)
+ .endm
+
+ .macro SMCCC_HVC
+ __HVC(0)
+ .endm
+
+ .macro SMCCC instr
+UNWIND( .fnstart)
+ mov r12, sp
+ push {r4-r7}
+UNWIND( .save {r4-r7})
+ ldm r12, {r4-r7}
+ \instr
+ pop {r4-r7}
+ ldr r12, [sp, #(4 * 4)]
+ stm r12, {r0-r3}
+ bx lr
+UNWIND( .fnend)
+ .endm
+
+/*
+ * void smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2,
+ * unsigned long a3, unsigned long a4, unsigned long a5,
+ * unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
+ */
+ENTRY(arm_smccc_smc)
+ SMCCC SMCCC_SMC
+ENDPROC(arm_smccc_smc)
+
+/*
+ * void smccc_hvc(unsigned long a0, unsigned long a1, unsigned long a2,
+ * unsigned long a3, unsigned long a4, unsigned long a5,
+ * unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
+ */
+ENTRY(arm_smccc_hvc)
+ SMCCC SMCCC_HVC
+ENDPROC(arm_smccc_hvc)
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index b26361355dae..37312f6749f3 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -69,11 +69,15 @@ enum ipi_msg_type {
IPI_TIMER,
IPI_RESCHEDULE,
IPI_CALL_FUNC,
- IPI_CALL_FUNC_SINGLE,
IPI_CPU_STOP,
IPI_IRQ_WORK,
IPI_COMPLETION,
- IPI_CPU_BACKTRACE = 15,
+ IPI_CPU_BACKTRACE,
+ /*
+ * SGI8-15 can be reserved by secure firmware, and thus may
+ * not be usable by the kernel. Please keep the above limited
+ * to at most 8 entries.
+ */
};
static DECLARE_COMPLETION(cpu_running);
@@ -475,7 +479,6 @@ static const char *ipi_types[NR_IPI] __tracepoint_string = {
S(IPI_TIMER, "Timer broadcast interrupts"),
S(IPI_RESCHEDULE, "Rescheduling interrupts"),
S(IPI_CALL_FUNC, "Function call interrupts"),
- S(IPI_CALL_FUNC_SINGLE, "Single function call interrupts"),
S(IPI_CPU_STOP, "CPU stop interrupts"),
S(IPI_IRQ_WORK, "IRQ work interrupts"),
S(IPI_COMPLETION, "completion interrupts"),
@@ -525,7 +528,7 @@ void arch_send_wakeup_ipi_mask(const struct cpumask *mask)
void arch_send_call_function_single_ipi(int cpu)
{
- smp_cross_call(cpumask_of(cpu), IPI_CALL_FUNC_SINGLE);
+ smp_cross_call(cpumask_of(cpu), IPI_CALL_FUNC);
}
#ifdef CONFIG_IRQ_WORK
@@ -620,12 +623,6 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
irq_exit();
break;
- case IPI_CALL_FUNC_SINGLE:
- irq_enter();
- generic_smp_call_function_single_interrupt();
- irq_exit();
- break;
-
case IPI_CPU_STOP:
irq_enter();
ipi_cpu_stop(cpu);
diff --git a/arch/arm/kernel/swp_emulate.c b/arch/arm/kernel/swp_emulate.c
index 5b26e7efa9ea..c3fe769d7558 100644
--- a/arch/arm/kernel/swp_emulate.c
+++ b/arch/arm/kernel/swp_emulate.c
@@ -36,10 +36,10 @@
*/
#define __user_swpX_asm(data, addr, res, temp, B) \
__asm__ __volatile__( \
- " mov %2, %1\n" \
- "0: ldrex"B" %1, [%3]\n" \
- "1: strex"B" %0, %2, [%3]\n" \
+ "0: ldrex"B" %2, [%3]\n" \
+ "1: strex"B" %0, %1, [%3]\n" \
" cmp %0, #0\n" \
+ " moveq %1, %2\n" \
" movne %0, %4\n" \
"2:\n" \
" .section .text.fixup,\"ax\"\n" \
diff --git a/arch/arm/kernel/sys_oabi-compat.c b/arch/arm/kernel/sys_oabi-compat.c
index b83f3b7737fb..087acb569b63 100644
--- a/arch/arm/kernel/sys_oabi-compat.c
+++ b/arch/arm/kernel/sys_oabi-compat.c
@@ -193,15 +193,44 @@ struct oabi_flock64 {
pid_t l_pid;
} __attribute__ ((packed,aligned(4)));
-asmlinkage long sys_oabi_fcntl64(unsigned int fd, unsigned int cmd,
+static long do_locks(unsigned int fd, unsigned int cmd,
unsigned long arg)
{
- struct oabi_flock64 user;
struct flock64 kernel;
- mm_segment_t fs = USER_DS; /* initialized to kill a warning */
- unsigned long local_arg = arg;
- int ret;
+ struct oabi_flock64 user;
+ mm_segment_t fs;
+ long ret;
+
+ if (copy_from_user(&user, (struct oabi_flock64 __user *)arg,
+ sizeof(user)))
+ return -EFAULT;
+ kernel.l_type = user.l_type;
+ kernel.l_whence = user.l_whence;
+ kernel.l_start = user.l_start;
+ kernel.l_len = user.l_len;
+ kernel.l_pid = user.l_pid;
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ ret = sys_fcntl64(fd, cmd, (unsigned long)&kernel);
+ set_fs(fs);
+
+ if (!ret && (cmd == F_GETLK64 || cmd == F_OFD_GETLK)) {
+ user.l_type = kernel.l_type;
+ user.l_whence = kernel.l_whence;
+ user.l_start = kernel.l_start;
+ user.l_len = kernel.l_len;
+ user.l_pid = kernel.l_pid;
+ if (copy_to_user((struct oabi_flock64 __user *)arg,
+ &user, sizeof(user)))
+ ret = -EFAULT;
+ }
+ return ret;
+}
+asmlinkage long sys_oabi_fcntl64(unsigned int fd, unsigned int cmd,
+ unsigned long arg)
+{
switch (cmd) {
case F_OFD_GETLK:
case F_OFD_SETLK:
@@ -209,39 +238,11 @@ asmlinkage long sys_oabi_fcntl64(unsigned int fd, unsigned int cmd,
case F_GETLK64:
case F_SETLK64:
case F_SETLKW64:
- if (copy_from_user(&user, (struct oabi_flock64 __user *)arg,
- sizeof(user)))
- return -EFAULT;
- kernel.l_type = user.l_type;
- kernel.l_whence = user.l_whence;
- kernel.l_start = user.l_start;
- kernel.l_len = user.l_len;
- kernel.l_pid = user.l_pid;
- local_arg = (unsigned long)&kernel;
- fs = get_fs();
- set_fs(KERNEL_DS);
- }
-
- ret = sys_fcntl64(fd, cmd, local_arg);
+ return do_locks(fd, cmd, arg);
- switch (cmd) {
- case F_GETLK64:
- if (!ret) {
- user.l_type = kernel.l_type;
- user.l_whence = kernel.l_whence;
- user.l_start = kernel.l_start;
- user.l_len = kernel.l_len;
- user.l_pid = kernel.l_pid;
- if (copy_to_user((struct oabi_flock64 __user *)arg,
- &user, sizeof(user)))
- ret = -EFAULT;
- }
- case F_SETLK64:
- case F_SETLKW64:
- set_fs(fs);
+ default:
+ return sys_fcntl64(fd, cmd, arg);
}
-
- return ret;
}
struct oabi_epoll_event {
diff --git a/arch/arm/kernel/vdso.c b/arch/arm/kernel/vdso.c
index 54a5aeab988d..994e971a8538 100644
--- a/arch/arm/kernel/vdso.c
+++ b/arch/arm/kernel/vdso.c
@@ -224,7 +224,7 @@ static int install_vvar(struct mm_struct *mm, unsigned long addr)
VM_READ | VM_MAYREAD,
&vdso_data_mapping);
- return IS_ERR(vma) ? PTR_ERR(vma) : 0;
+ return PTR_ERR_OR_ZERO(vma);
}
/* assumes mmap_sem is write-locked */
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index eab83b2435b8..dda1959f0dde 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -44,6 +44,7 @@
#include <asm/kvm_emulate.h>
#include <asm/kvm_coproc.h>
#include <asm/kvm_psci.h>
+#include <asm/sections.h>
#ifdef REQUIRES_VIRT
__asm__(".arch_extension virt");
@@ -58,9 +59,12 @@ static DEFINE_PER_CPU(struct kvm_vcpu *, kvm_arm_running_vcpu);
/* The VMID used in the VTTBR */
static atomic64_t kvm_vmid_gen = ATOMIC64_INIT(1);
-static u8 kvm_next_vmid;
+static u32 kvm_next_vmid;
+static unsigned int kvm_vmid_bits __read_mostly;
static DEFINE_SPINLOCK(kvm_vmid_lock);
+static bool vgic_present;
+
static void kvm_arm_set_running_vcpu(struct kvm_vcpu *vcpu)
{
BUG_ON(preemptible());
@@ -132,7 +136,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
kvm->arch.vmid_gen = 0;
/* The maximum number of VCPUs is limited by the host's GIC model */
- kvm->arch.max_vcpus = kvm_vgic_get_max_vcpus();
+ kvm->arch.max_vcpus = vgic_present ?
+ kvm_vgic_get_max_vcpus() : KVM_MAX_VCPUS;
return ret;
out_free_stage2_pgd:
@@ -172,6 +177,8 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
int r;
switch (ext) {
case KVM_CAP_IRQCHIP:
+ r = vgic_present;
+ break;
case KVM_CAP_IOEVENTFD:
case KVM_CAP_DEVICE_CTRL:
case KVM_CAP_USER_MEMORY:
@@ -433,11 +440,12 @@ static void update_vttbr(struct kvm *kvm)
kvm->arch.vmid_gen = atomic64_read(&kvm_vmid_gen);
kvm->arch.vmid = kvm_next_vmid;
kvm_next_vmid++;
+ kvm_next_vmid &= (1 << kvm_vmid_bits) - 1;
/* update vttbr to be used with the new vmid */
pgd_phys = virt_to_phys(kvm_get_hwpgd(kvm));
BUG_ON(pgd_phys & ~VTTBR_BADDR_MASK);
- vmid = ((u64)(kvm->arch.vmid) << VTTBR_VMID_SHIFT) & VTTBR_VMID_MASK;
+ vmid = ((u64)(kvm->arch.vmid) << VTTBR_VMID_SHIFT) & VTTBR_VMID_MASK(kvm_vmid_bits);
kvm->arch.vttbr = pgd_phys | vmid;
spin_unlock(&kvm_vmid_lock);
@@ -564,17 +572,12 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
vcpu_sleep(vcpu);
/*
- * Disarming the background timer must be done in a
- * preemptible context, as this call may sleep.
- */
- kvm_timer_flush_hwstate(vcpu);
-
- /*
* Preparing the interrupts to be injected also
* involves poking the GIC, which must be done in a
* non-preemptible context.
*/
preempt_disable();
+ kvm_timer_flush_hwstate(vcpu);
kvm_vgic_flush_hwstate(vcpu);
local_irq_disable();
@@ -608,6 +611,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
ret = kvm_call_hyp(__kvm_vcpu_run, vcpu);
vcpu->mode = OUTSIDE_GUEST_MODE;
+ vcpu->stat.exits++;
/*
* Back from guest
*************************************************************/
@@ -918,6 +922,8 @@ static int kvm_vm_ioctl_set_device_addr(struct kvm *kvm,
switch (dev_id) {
case KVM_ARM_DEVICE_VGIC_V2:
+ if (!vgic_present)
+ return -ENXIO;
return kvm_vgic_addr(kvm, type, &dev_addr->addr, true);
default:
return -ENODEV;
@@ -932,6 +938,8 @@ long kvm_arch_vm_ioctl(struct file *filp,
switch (ioctl) {
case KVM_CREATE_IRQCHIP: {
+ if (!vgic_present)
+ return -ENXIO;
return kvm_vgic_create(kvm, KVM_DEV_TYPE_ARM_VGIC_V2);
}
case KVM_ARM_SET_DEVICE_ADDR: {
@@ -1072,6 +1080,12 @@ static int init_hyp_mode(void)
goto out_free_mappings;
}
+ err = create_hyp_mappings(__start_rodata, __end_rodata);
+ if (err) {
+ kvm_err("Cannot map rodata section\n");
+ goto out_free_mappings;
+ }
+
/*
* Map the Hyp stack pages
*/
@@ -1116,8 +1130,17 @@ static int init_hyp_mode(void)
* Init HYP view of VGIC
*/
err = kvm_vgic_hyp_init();
- if (err)
+ switch (err) {
+ case 0:
+ vgic_present = true;
+ break;
+ case -ENODEV:
+ case -ENXIO:
+ vgic_present = false;
+ break;
+ default:
goto out_free_context;
+ }
/*
* Init HYP architected timer support
@@ -1132,6 +1155,10 @@ static int init_hyp_mode(void)
kvm_perf_init();
+ /* set size of VMID supported by CPU */
+ kvm_vmid_bits = kvm_get_vmid_bits();
+ kvm_info("%d-bit VMID\n", kvm_vmid_bits);
+
kvm_info("Hyp mode initialized successfully\n");
return 0;
diff --git a/arch/arm/kvm/emulate.c b/arch/arm/kvm/emulate.c
index d6c005283678..dc99159857b4 100644
--- a/arch/arm/kvm/emulate.c
+++ b/arch/arm/kvm/emulate.c
@@ -275,6 +275,40 @@ static u32 exc_vector_base(struct kvm_vcpu *vcpu)
return vbar;
}
+/*
+ * Switch to an exception mode, updating both CPSR and SPSR. Follow
+ * the logic described in AArch32.EnterMode() from the ARMv8 ARM.
+ */
+static void kvm_update_psr(struct kvm_vcpu *vcpu, unsigned long mode)
+{
+ unsigned long cpsr = *vcpu_cpsr(vcpu);
+ u32 sctlr = vcpu->arch.cp15[c1_SCTLR];
+
+ *vcpu_cpsr(vcpu) = (cpsr & ~MODE_MASK) | mode;
+
+ switch (mode) {
+ case FIQ_MODE:
+ *vcpu_cpsr(vcpu) |= PSR_F_BIT;
+ /* Fall through */
+ case ABT_MODE:
+ case IRQ_MODE:
+ *vcpu_cpsr(vcpu) |= PSR_A_BIT;
+ /* Fall through */
+ default:
+ *vcpu_cpsr(vcpu) |= PSR_I_BIT;
+ }
+
+ *vcpu_cpsr(vcpu) &= ~(PSR_IT_MASK | PSR_J_BIT | PSR_E_BIT | PSR_T_BIT);
+
+ if (sctlr & SCTLR_TE)
+ *vcpu_cpsr(vcpu) |= PSR_T_BIT;
+ if (sctlr & SCTLR_EE)
+ *vcpu_cpsr(vcpu) |= PSR_E_BIT;
+
+ /* Note: These now point to the mode banked copies */
+ *vcpu_spsr(vcpu) = cpsr;
+}
+
/**
* kvm_inject_undefined - inject an undefined exception into the guest
* @vcpu: The VCPU to receive the undefined exception
@@ -286,29 +320,13 @@ static u32 exc_vector_base(struct kvm_vcpu *vcpu)
*/
void kvm_inject_undefined(struct kvm_vcpu *vcpu)
{
- unsigned long new_lr_value;
- unsigned long new_spsr_value;
unsigned long cpsr = *vcpu_cpsr(vcpu);
- u32 sctlr = vcpu->arch.cp15[c1_SCTLR];
bool is_thumb = (cpsr & PSR_T_BIT);
u32 vect_offset = 4;
u32 return_offset = (is_thumb) ? 2 : 4;
- new_spsr_value = cpsr;
- new_lr_value = *vcpu_pc(vcpu) - return_offset;
-
- *vcpu_cpsr(vcpu) = (cpsr & ~MODE_MASK) | UND_MODE;
- *vcpu_cpsr(vcpu) |= PSR_I_BIT;
- *vcpu_cpsr(vcpu) &= ~(PSR_IT_MASK | PSR_J_BIT | PSR_E_BIT | PSR_T_BIT);
-
- if (sctlr & SCTLR_TE)
- *vcpu_cpsr(vcpu) |= PSR_T_BIT;
- if (sctlr & SCTLR_EE)
- *vcpu_cpsr(vcpu) |= PSR_E_BIT;
-
- /* Note: These now point to UND banked copies */
- *vcpu_spsr(vcpu) = cpsr;
- *vcpu_reg(vcpu, 14) = new_lr_value;
+ kvm_update_psr(vcpu, UND_MODE);
+ *vcpu_reg(vcpu, 14) = *vcpu_pc(vcpu) - return_offset;
/* Branch to exception vector */
*vcpu_pc(vcpu) = exc_vector_base(vcpu) + vect_offset;
@@ -320,30 +338,14 @@ void kvm_inject_undefined(struct kvm_vcpu *vcpu)
*/
static void inject_abt(struct kvm_vcpu *vcpu, bool is_pabt, unsigned long addr)
{
- unsigned long new_lr_value;
- unsigned long new_spsr_value;
unsigned long cpsr = *vcpu_cpsr(vcpu);
- u32 sctlr = vcpu->arch.cp15[c1_SCTLR];
bool is_thumb = (cpsr & PSR_T_BIT);
u32 vect_offset;
u32 return_offset = (is_thumb) ? 4 : 0;
bool is_lpae;
- new_spsr_value = cpsr;
- new_lr_value = *vcpu_pc(vcpu) + return_offset;
-
- *vcpu_cpsr(vcpu) = (cpsr & ~MODE_MASK) | ABT_MODE;
- *vcpu_cpsr(vcpu) |= PSR_I_BIT | PSR_A_BIT;
- *vcpu_cpsr(vcpu) &= ~(PSR_IT_MASK | PSR_J_BIT | PSR_E_BIT | PSR_T_BIT);
-
- if (sctlr & SCTLR_TE)
- *vcpu_cpsr(vcpu) |= PSR_T_BIT;
- if (sctlr & SCTLR_EE)
- *vcpu_cpsr(vcpu) |= PSR_E_BIT;
-
- /* Note: These now point to ABT banked copies */
- *vcpu_spsr(vcpu) = cpsr;
- *vcpu_reg(vcpu, 14) = new_lr_value;
+ kvm_update_psr(vcpu, ABT_MODE);
+ *vcpu_reg(vcpu, 14) = *vcpu_pc(vcpu) + return_offset;
if (is_pabt)
vect_offset = 12;
diff --git a/arch/arm/kvm/guest.c b/arch/arm/kvm/guest.c
index 96e935bbc38c..5fa69d7bae58 100644
--- a/arch/arm/kvm/guest.c
+++ b/arch/arm/kvm/guest.c
@@ -33,6 +33,12 @@
#define VCPU_STAT(x) { #x, offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU }
struct kvm_stats_debugfs_item debugfs_entries[] = {
+ VCPU_STAT(hvc_exit_stat),
+ VCPU_STAT(wfe_exit_stat),
+ VCPU_STAT(wfi_exit_stat),
+ VCPU_STAT(mmio_exit_user),
+ VCPU_STAT(mmio_exit_kernel),
+ VCPU_STAT(exits),
{ NULL }
};
diff --git a/arch/arm/kvm/handle_exit.c b/arch/arm/kvm/handle_exit.c
index 95f12b2ccdcb..3ede90d8b20b 100644
--- a/arch/arm/kvm/handle_exit.c
+++ b/arch/arm/kvm/handle_exit.c
@@ -42,6 +42,7 @@ static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run)
trace_kvm_hvc(*vcpu_pc(vcpu), *vcpu_reg(vcpu, 0),
kvm_vcpu_hvc_get_imm(vcpu));
+ vcpu->stat.hvc_exit_stat++;
ret = kvm_psci_call(vcpu);
if (ret < 0) {
@@ -89,9 +90,11 @@ static int kvm_handle_wfx(struct kvm_vcpu *vcpu, struct kvm_run *run)
{
if (kvm_vcpu_get_hsr(vcpu) & HSR_WFI_IS_WFE) {
trace_kvm_wfx(*vcpu_pc(vcpu), true);
+ vcpu->stat.wfe_exit_stat++;
kvm_vcpu_on_spin(vcpu);
} else {
trace_kvm_wfx(*vcpu_pc(vcpu), false);
+ vcpu->stat.wfi_exit_stat++;
kvm_vcpu_block(vcpu);
}
diff --git a/arch/arm/kvm/mmio.c b/arch/arm/kvm/mmio.c
index 974b1c606d04..7f33b2056ae6 100644
--- a/arch/arm/kvm/mmio.c
+++ b/arch/arm/kvm/mmio.c
@@ -115,7 +115,7 @@ int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr,
data);
data = vcpu_data_host_to_guest(vcpu, data, len);
- *vcpu_reg(vcpu, vcpu->arch.mmio_decode.rt) = data;
+ vcpu_set_reg(vcpu, vcpu->arch.mmio_decode.rt, data);
}
return 0;
@@ -186,7 +186,8 @@ int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
rt = vcpu->arch.mmio_decode.rt;
if (is_write) {
- data = vcpu_data_guest_to_host(vcpu, *vcpu_reg(vcpu, rt), len);
+ data = vcpu_data_guest_to_host(vcpu, vcpu_get_reg(vcpu, rt),
+ len);
trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, len, fault_ipa, data);
mmio_write_buf(data_buf, len, data);
@@ -209,8 +210,11 @@ int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
if (!ret) {
/* We handled the access successfully in the kernel. */
+ vcpu->stat.mmio_exit_kernel++;
kvm_handle_mmio_return(vcpu, run);
return 1;
+ } else {
+ vcpu->stat.mmio_exit_user++;
}
run->exit_reason = KVM_EXIT_MMIO;
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
index 6984342da13d..22f7fa0124ec 100644
--- a/arch/arm/kvm/mmu.c
+++ b/arch/arm/kvm/mmu.c
@@ -98,6 +98,11 @@ static void kvm_flush_dcache_pud(pud_t pud)
__kvm_flush_dcache_pud(pud);
}
+static bool kvm_is_device_pfn(unsigned long pfn)
+{
+ return !pfn_valid(pfn);
+}
+
/**
* stage2_dissolve_pmd() - clear and flush huge PMD entry
* @kvm: pointer to kvm structure.
@@ -213,7 +218,7 @@ static void unmap_ptes(struct kvm *kvm, pmd_t *pmd,
kvm_tlb_flush_vmid_ipa(kvm, addr);
/* No need to invalidate the cache for device mappings */
- if ((pte_val(old_pte) & PAGE_S2_DEVICE) != PAGE_S2_DEVICE)
+ if (!kvm_is_device_pfn(pte_pfn(old_pte)))
kvm_flush_dcache_pte(old_pte);
put_page(virt_to_page(pte));
@@ -305,8 +310,7 @@ static void stage2_flush_ptes(struct kvm *kvm, pmd_t *pmd,
pte = pte_offset_kernel(pmd, addr);
do {
- if (!pte_none(*pte) &&
- (pte_val(*pte) & PAGE_S2_DEVICE) != PAGE_S2_DEVICE)
+ if (!pte_none(*pte) && !kvm_is_device_pfn(pte_pfn(*pte)))
kvm_flush_dcache_pte(*pte);
} while (pte++, addr += PAGE_SIZE, addr != end);
}
@@ -652,9 +656,9 @@ static void *kvm_alloc_hwpgd(void)
* kvm_alloc_stage2_pgd - allocate level-1 table for stage-2 translation.
* @kvm: The KVM struct pointer for the VM.
*
- * Allocates the 1st level table only of size defined by S2_PGD_ORDER (can
- * support either full 40-bit input addresses or limited to 32-bit input
- * addresses). Clears the allocated pages.
+ * Allocates only the stage-2 HW PGD level table(s) (can support either full
+ * 40-bit input addresses or limited to 32-bit input addresses). Clears the
+ * allocated pages.
*
* Note we don't need locking here as this is only called when the VM is
* created, which can only be done once.
@@ -1037,11 +1041,6 @@ static bool kvm_is_write_fault(struct kvm_vcpu *vcpu)
return kvm_vcpu_dabt_iswrite(vcpu);
}
-static bool kvm_is_device_pfn(unsigned long pfn)
-{
- return !pfn_valid(pfn);
-}
-
/**
* stage2_wp_ptes - write protect PMD range
* @pmd: pointer to pmd entry
diff --git a/arch/arm/kvm/psci.c b/arch/arm/kvm/psci.c
index 0b556968a6da..a9b3b905e661 100644
--- a/arch/arm/kvm/psci.c
+++ b/arch/arm/kvm/psci.c
@@ -75,7 +75,7 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
unsigned long context_id;
phys_addr_t target_pc;
- cpu_id = *vcpu_reg(source_vcpu, 1) & MPIDR_HWID_BITMASK;
+ cpu_id = vcpu_get_reg(source_vcpu, 1) & MPIDR_HWID_BITMASK;
if (vcpu_mode_is_32bit(source_vcpu))
cpu_id &= ~((u32) 0);
@@ -94,8 +94,8 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
return PSCI_RET_INVALID_PARAMS;
}
- target_pc = *vcpu_reg(source_vcpu, 2);
- context_id = *vcpu_reg(source_vcpu, 3);
+ target_pc = vcpu_get_reg(source_vcpu, 2);
+ context_id = vcpu_get_reg(source_vcpu, 3);
kvm_reset_vcpu(vcpu);
@@ -114,7 +114,7 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
* NOTE: We always update r0 (or x0) because for PSCI v0.1
* the general puspose registers are undefined upon CPU_ON.
*/
- *vcpu_reg(vcpu, 0) = context_id;
+ vcpu_set_reg(vcpu, 0, context_id);
vcpu->arch.power_off = false;
smp_mb(); /* Make sure the above is visible */
@@ -134,8 +134,8 @@ static unsigned long kvm_psci_vcpu_affinity_info(struct kvm_vcpu *vcpu)
struct kvm *kvm = vcpu->kvm;
struct kvm_vcpu *tmp;
- target_affinity = *vcpu_reg(vcpu, 1);
- lowest_affinity_level = *vcpu_reg(vcpu, 2);
+ target_affinity = vcpu_get_reg(vcpu, 1);
+ lowest_affinity_level = vcpu_get_reg(vcpu, 2);
/* Determine target affinity mask */
target_affinity_mask = psci_affinity_mask(lowest_affinity_level);
@@ -209,7 +209,7 @@ int kvm_psci_version(struct kvm_vcpu *vcpu)
static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu)
{
int ret = 1;
- unsigned long psci_fn = *vcpu_reg(vcpu, 0) & ~((u32) 0);
+ unsigned long psci_fn = vcpu_get_reg(vcpu, 0) & ~((u32) 0);
unsigned long val;
switch (psci_fn) {
@@ -273,13 +273,13 @@ static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu)
break;
}
- *vcpu_reg(vcpu, 0) = val;
+ vcpu_set_reg(vcpu, 0, val);
return ret;
}
static int kvm_psci_0_1_call(struct kvm_vcpu *vcpu)
{
- unsigned long psci_fn = *vcpu_reg(vcpu, 0) & ~((u32) 0);
+ unsigned long psci_fn = vcpu_get_reg(vcpu, 0) & ~((u32) 0);
unsigned long val;
switch (psci_fn) {
@@ -295,7 +295,7 @@ static int kvm_psci_0_1_call(struct kvm_vcpu *vcpu)
break;
}
- *vcpu_reg(vcpu, 0) = val;
+ vcpu_set_reg(vcpu, 0, val);
return 1;
}
diff --git a/arch/arm/lib/lib1funcs.S b/arch/arm/lib/lib1funcs.S
index af2267f6a529..9397b2e532af 100644
--- a/arch/arm/lib/lib1funcs.S
+++ b/arch/arm/lib/lib1funcs.S
@@ -205,6 +205,10 @@ Boston, MA 02111-1307, USA. */
.endm
+#ifdef CONFIG_ARM_PATCH_IDIV
+ .align 3
+#endif
+
ENTRY(__udivsi3)
ENTRY(__aeabi_uidiv)
UNWIND(.fnstart)
@@ -253,6 +257,10 @@ UNWIND(.fnstart)
UNWIND(.fnend)
ENDPROC(__umodsi3)
+#ifdef CONFIG_ARM_PATCH_IDIV
+ .align 3
+#endif
+
ENTRY(__divsi3)
ENTRY(__aeabi_idiv)
UNWIND(.fnstart)
diff --git a/arch/arm/lib/uaccess_with_memcpy.c b/arch/arm/lib/uaccess_with_memcpy.c
index d72b90905132..588bbc288396 100644
--- a/arch/arm/lib/uaccess_with_memcpy.c
+++ b/arch/arm/lib/uaccess_with_memcpy.c
@@ -88,6 +88,7 @@ pin_page_for_write(const void __user *_addr, pte_t **ptep, spinlock_t **ptlp)
static unsigned long noinline
__copy_to_user_memcpy(void __user *to, const void *from, unsigned long n)
{
+ unsigned long ua_flags;
int atomic;
if (unlikely(segment_eq(get_fs(), KERNEL_DS))) {
@@ -118,7 +119,9 @@ __copy_to_user_memcpy(void __user *to, const void *from, unsigned long n)
if (tocopy > n)
tocopy = n;
+ ua_flags = uaccess_save_and_enable();
memcpy((void *)to, from, tocopy);
+ uaccess_restore(ua_flags);
to += tocopy;
from += tocopy;
n -= tocopy;
@@ -145,14 +148,21 @@ arm_copy_to_user(void __user *to, const void *from, unsigned long n)
* With frame pointer disabled, tail call optimization kicks in
* as well making this test almost invisible.
*/
- if (n < 64)
- return __copy_to_user_std(to, from, n);
- return __copy_to_user_memcpy(to, from, n);
+ if (n < 64) {
+ unsigned long ua_flags = uaccess_save_and_enable();
+ n = __copy_to_user_std(to, from, n);
+ uaccess_restore(ua_flags);
+ } else {
+ n = __copy_to_user_memcpy(to, from, n);
+ }
+ return n;
}
static unsigned long noinline
__clear_user_memset(void __user *addr, unsigned long n)
{
+ unsigned long ua_flags;
+
if (unlikely(segment_eq(get_fs(), KERNEL_DS))) {
memset((void *)addr, 0, n);
return 0;
@@ -175,7 +185,9 @@ __clear_user_memset(void __user *addr, unsigned long n)
if (tocopy > n)
tocopy = n;
+ ua_flags = uaccess_save_and_enable();
memset((void *)addr, 0, tocopy);
+ uaccess_restore(ua_flags);
addr += tocopy;
n -= tocopy;
@@ -193,9 +205,14 @@ out:
unsigned long arm_clear_user(void __user *addr, unsigned long n)
{
/* See rational for this in __copy_to_user() above. */
- if (n < 64)
- return __clear_user_std(addr, n);
- return __clear_user_memset(addr, n);
+ if (n < 64) {
+ unsigned long ua_flags = uaccess_save_and_enable();
+ n = __clear_user_std(addr, n);
+ uaccess_restore(ua_flags);
+ } else {
+ n = __clear_user_memset(addr, n);
+ }
+ return n;
}
#if 0
diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
index 92673006e55c..28656c2b54a0 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -4,7 +4,6 @@ menuconfig ARCH_AT91
select ARCH_REQUIRE_GPIOLIB
select COMMON_CLK_AT91
select PINCTRL
- select PINCTRL_AT91
select SOC_BUS
if ARCH_AT91
@@ -17,6 +16,7 @@ config SOC_SAMA5D2
select HAVE_AT91_USB_CLK
select HAVE_AT91_H32MX
select HAVE_AT91_GENERATED_CLK
+ select PINCTRL_AT91PIO4
help
Select this if ou are using one of Atmel's SAMA5D2 family SoC.
@@ -27,6 +27,7 @@ config SOC_SAMA5D3
select HAVE_AT91_UTMI
select HAVE_AT91_SMD
select HAVE_AT91_USB_CLK
+ select PINCTRL_AT91
help
Select this if you are using one of Atmel's SAMA5D3 family SoC.
This support covers SAMA5D31, SAMA5D33, SAMA5D34, SAMA5D35, SAMA5D36.
@@ -40,6 +41,7 @@ config SOC_SAMA5D4
select HAVE_AT91_SMD
select HAVE_AT91_USB_CLK
select HAVE_AT91_H32MX
+ select PINCTRL_AT91
help
Select this if you are using one of Atmel's SAMA5D4 family SoC.
@@ -50,6 +52,7 @@ config SOC_AT91RM9200
select CPU_ARM920T
select HAVE_AT91_USB_CLK
select MIGHT_HAVE_PCI
+ select PINCTRL_AT91
select SOC_SAM_V4_V5
select SRAM if PM
help
@@ -65,6 +68,7 @@ config SOC_AT91SAM9
select HAVE_AT91_UTMI
select HAVE_FB_ATMEL
select MEMORY
+ select PINCTRL_AT91
select SOC_SAM_V4_V5
select SRAM if PM
help
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index 80e277cfcc8b..23726fb31741 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -41,8 +41,10 @@
* implementation should be moved down into the pinctrl driver and get
* called as part of the generic suspend/resume path.
*/
+#ifdef CONFIG_PINCTRL_AT91
extern void at91_pinctrl_gpio_suspend(void);
extern void at91_pinctrl_gpio_resume(void);
+#endif
static struct {
unsigned long uhp_udp_mask;
@@ -151,8 +153,9 @@ static void at91_pm_suspend(suspend_state_t state)
static int at91_pm_enter(suspend_state_t state)
{
+#ifdef CONFIG_PINCTRL_AT91
at91_pinctrl_gpio_suspend();
-
+#endif
switch (state) {
/*
* Suspend-to-RAM is like STANDBY plus slow clock mode, so
@@ -192,7 +195,9 @@ static int at91_pm_enter(suspend_state_t state)
error:
target_state = PM_SUSPEND_ON;
+#ifdef CONFIG_PINCTRL_AT91
at91_pinctrl_gpio_resume();
+#endif
return 0;
}
diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c
index 1ed545cc2b83..9cc7b818fbf6 100644
--- a/arch/arm/mach-davinci/board-da850-evm.c
+++ b/arch/arm/mach-davinci/board-da850-evm.c
@@ -49,8 +49,8 @@
#include <asm/mach/arch.h>
#include <asm/system_info.h>
-#include <media/tvp514x.h>
-#include <media/adv7343.h>
+#include <media/i2c/tvp514x.h>
+#include <media/i2c/adv7343.h>
#define DA850_EVM_PHY_ID "davinci_mdio-0:00"
#define DA850_LCD_PWR_PIN GPIO_TO_PIN(2, 8)
diff --git a/arch/arm/mach-davinci/board-dm355-evm.c b/arch/arm/mach-davinci/board-dm355-evm.c
index b46b4d25f93e..c71dd9982f03 100644
--- a/arch/arm/mach-davinci/board-dm355-evm.c
+++ b/arch/arm/mach-davinci/board-dm355-evm.c
@@ -19,7 +19,7 @@
#include <linux/gpio.h>
#include <linux/clk.h>
#include <linux/videodev2.h>
-#include <media/tvp514x.h>
+#include <media/i2c/tvp514x.h>
#include <linux/spi/spi.h>
#include <linux/spi/eeprom.h>
#include <linux/platform_data/gpio-davinci.h>
diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c
index a756003595e9..f073518f621a 100644
--- a/arch/arm/mach-davinci/board-dm365-evm.c
+++ b/arch/arm/mach-davinci/board-dm365-evm.c
@@ -40,8 +40,8 @@
#include <linux/platform_data/mtd-davinci.h>
#include <linux/platform_data/keyscan-davinci.h>
-#include <media/ths7303.h>
-#include <media/tvp514x.h>
+#include <media/i2c/ths7303.h>
+#include <media/i2c/tvp514x.h>
#include "davinci.h"
diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c
index bbdd2d614b49..7a20507a3eef 100644
--- a/arch/arm/mach-davinci/board-dm644x-evm.c
+++ b/arch/arm/mach-davinci/board-dm644x-evm.c
@@ -26,7 +26,7 @@
#include <linux/v4l2-dv-timings.h>
#include <linux/export.h>
-#include <media/tvp514x.h>
+#include <media/i2c/tvp514x.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c
index 846a84ddc28e..ee6ab7e8d3b0 100644
--- a/arch/arm/mach-davinci/board-dm646x-evm.c
+++ b/arch/arm/mach-davinci/board-dm646x-evm.c
@@ -25,8 +25,8 @@
#include <linux/platform_data/at24.h>
#include <linux/i2c/pcf857x.h>
-#include <media/tvp514x.h>
-#include <media/adv7343.h>
+#include <media/i2c/tvp514x.h>
+#include <media/i2c/adv7343.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
diff --git a/arch/arm/mach-dove/include/mach/entry-macro.S b/arch/arm/mach-dove/include/mach/entry-macro.S
index 72d622baaad3..df1d44bdc375 100644
--- a/arch/arm/mach-dove/include/mach/entry-macro.S
+++ b/arch/arm/mach-dove/include/mach/entry-macro.S
@@ -18,13 +18,13 @@
@ check low interrupts
ldr \irqstat, [\base, #IRQ_CAUSE_LOW_OFF]
ldr \tmp, [\base, #IRQ_MASK_LOW_OFF]
- mov \irqnr, #31
+ mov \irqnr, #32
ands \irqstat, \irqstat, \tmp
@ if no low interrupts set, check high interrupts
ldreq \irqstat, [\base, #IRQ_CAUSE_HIGH_OFF]
ldreq \tmp, [\base, #IRQ_MASK_HIGH_OFF]
- moveq \irqnr, #63
+ moveq \irqnr, #64
andeqs \irqstat, \irqstat, \tmp
@ find first active interrupt source
diff --git a/arch/arm/mach-ep93xx/snappercl15.c b/arch/arm/mach-ep93xx/snappercl15.c
index c4904264256a..b2db791b3b38 100644
--- a/arch/arm/mach-ep93xx/snappercl15.c
+++ b/arch/arm/mach-ep93xx/snappercl15.c
@@ -49,7 +49,7 @@
static void snappercl15_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
static u16 nand_state = SNAPPERCL15_NAND_WPN;
u16 set;
@@ -76,7 +76,7 @@ static void snappercl15_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
static int snappercl15_nand_dev_ready(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
return !!(__raw_readw(NAND_CTRL_ADDR(chip)) & SNAPPERCL15_NAND_RDY);
}
diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c
index 61f4b5dc4d7d..45b81a2bcd4b 100644
--- a/arch/arm/mach-ep93xx/ts72xx.c
+++ b/arch/arm/mach-ep93xx/ts72xx.c
@@ -74,7 +74,7 @@ static void __init ts72xx_map_io(void)
static void ts72xx_nand_hwcontrol(struct mtd_info *mtd,
int cmd, unsigned int ctrl)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
if (ctrl & NAND_CTRL_CHANGE) {
void __iomem *addr = chip->IO_ADDR_R;
@@ -96,7 +96,7 @@ static void ts72xx_nand_hwcontrol(struct mtd_info *mtd,
static int ts72xx_nand_device_ready(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
void __iomem *addr = chip->IO_ADDR_R;
addr += (1 << TS72XX_NAND_BUSY_ADDR_LINE);
diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
index 3a10f1a8317a..ff105399aae4 100644
--- a/arch/arm/mach-exynos/Kconfig
+++ b/arch/arm/mach-exynos/Kconfig
@@ -27,6 +27,7 @@ menuconfig ARCH_EXYNOS
select SRAM
select THERMAL
select MFD_SYSCON
+ select CLKSRC_EXYNOS_MCT
help
Support for SAMSUNG EXYNOS SoCs (EXYNOS4/5)
diff --git a/arch/arm/mach-exynos/pmu.c b/arch/arm/mach-exynos/pmu.c
index de68938ee6aa..c21e41dad19c 100644
--- a/arch/arm/mach-exynos/pmu.c
+++ b/arch/arm/mach-exynos/pmu.c
@@ -748,8 +748,12 @@ static void exynos5_powerdown_conf(enum sys_powerdown mode)
void exynos_sys_powerdown_conf(enum sys_powerdown mode)
{
unsigned int i;
+ const struct exynos_pmu_data *pmu_data;
+
+ if (!pmu_context)
+ return;
- const struct exynos_pmu_data *pmu_data = pmu_context->pmu_data;
+ pmu_data = pmu_context->pmu_data;
if (pmu_data->powerdown_conf)
pmu_data->powerdown_conf(mode);
diff --git a/arch/arm/mach-imx/devices/devices-common.h b/arch/arm/mach-imx/devices/devices-common.h
index 67f7fb13050d..09cebd8cef2b 100644
--- a/arch/arm/mach-imx/devices/devices-common.h
+++ b/arch/arm/mach-imx/devices/devices-common.h
@@ -177,7 +177,7 @@ struct platform_device *__init imx_add_imx_uart_1irq(
const struct imxuart_platform_data *pdata);
#include <linux/platform_data/video-mx3fb.h>
-#include <linux/platform_data/camera-mx3.h>
+#include <linux/platform_data/media/camera-mx3.h>
struct imx_ipu_core_data {
resource_size_t iobase;
resource_size_t synirq;
@@ -192,7 +192,7 @@ struct platform_device *__init imx_add_mx3_sdc_fb(
const struct imx_ipu_core_data *data,
struct mx3fb_platform_data *pdata);
-#include <linux/platform_data/camera-mx2.h>
+#include <linux/platform_data/media/camera-mx2.h>
struct imx_mx2_camera_data {
const char *devid;
resource_size_t iobasecsi;
diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c
index 8e7976a4c3e7..cfc696b972f3 100644
--- a/arch/arm/mach-imx/gpc.c
+++ b/arch/arm/mach-imx/gpc.c
@@ -177,6 +177,7 @@ static struct irq_chip imx_gpc_chip = {
.irq_unmask = imx_gpc_irq_unmask,
.irq_retrigger = irq_chip_retrigger_hierarchy,
.irq_set_wake = imx_gpc_irq_set_wake,
+ .irq_set_type = irq_chip_set_type_parent,
#ifdef CONFIG_SMP
.irq_set_affinity = irq_chip_set_affinity_parent,
#endif
diff --git a/arch/arm/mach-imx/mach-qong.c b/arch/arm/mach-imx/mach-qong.c
index a213e7b9cb1c..5c2764604727 100644
--- a/arch/arm/mach-imx/mach-qong.c
+++ b/arch/arm/mach-imx/mach-qong.c
@@ -131,7 +131,7 @@ static void qong_init_nor_mtd(void)
*/
static void qong_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
- struct nand_chip *nand_chip = mtd->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
if (cmd == NAND_CMD_NONE)
return;
diff --git a/arch/arm/mach-ixp4xx/include/mach/io.h b/arch/arm/mach-ixp4xx/include/mach/io.h
index b02439019963..7a0c13bf4269 100644
--- a/arch/arm/mach-ixp4xx/include/mach/io.h
+++ b/arch/arm/mach-ixp4xx/include/mach/io.h
@@ -143,7 +143,7 @@ static inline void __indirect_writesl(volatile void __iomem *bus_addr,
writel(*vaddr++, bus_addr);
}
-static inline unsigned char __indirect_readb(const volatile void __iomem *p)
+static inline u8 __indirect_readb(const volatile void __iomem *p)
{
u32 addr = (u32)p;
u32 n, byte_enables, data;
@@ -166,7 +166,7 @@ static inline void __indirect_readsb(const volatile void __iomem *bus_addr,
*vaddr++ = readb(bus_addr);
}
-static inline unsigned short __indirect_readw(const volatile void __iomem *p)
+static inline u16 __indirect_readw(const volatile void __iomem *p)
{
u32 addr = (u32)p;
u32 n, byte_enables, data;
@@ -189,7 +189,7 @@ static inline void __indirect_readsw(const volatile void __iomem *bus_addr,
*vaddr++ = readw(bus_addr);
}
-static inline unsigned long __indirect_readl(const volatile void __iomem *p)
+static inline u32 __indirect_readl(const volatile void __iomem *p)
{
u32 addr = (__force u32)p;
u32 data;
@@ -350,7 +350,7 @@ static inline void insl(u32 io_addr, void *p, u32 count)
((unsigned long)p <= (PIO_MASK + PIO_OFFSET)))
#define ioread8(p) ioread8(p)
-static inline unsigned int ioread8(const void __iomem *addr)
+static inline u8 ioread8(const void __iomem *addr)
{
unsigned long port = (unsigned long __force)addr;
if (__is_io_address(port))
@@ -378,7 +378,7 @@ static inline void ioread8_rep(const void __iomem *addr, void *vaddr, u32 count)
}
#define ioread16(p) ioread16(p)
-static inline unsigned int ioread16(const void __iomem *addr)
+static inline u16 ioread16(const void __iomem *addr)
{
unsigned long port = (unsigned long __force)addr;
if (__is_io_address(port))
@@ -407,7 +407,7 @@ static inline void ioread16_rep(const void __iomem *addr, void *vaddr,
}
#define ioread32(p) ioread32(p)
-static inline unsigned int ioread32(const void __iomem *addr)
+static inline u32 ioread32(const void __iomem *addr)
{
unsigned long port = (unsigned long __force)addr;
if (__is_io_address(port))
diff --git a/arch/arm/mach-ixp4xx/ixdp425-setup.c b/arch/arm/mach-ixp4xx/ixdp425-setup.c
index e7b8befa8729..508c2d7786e2 100644
--- a/arch/arm/mach-ixp4xx/ixdp425-setup.c
+++ b/arch/arm/mach-ixp4xx/ixdp425-setup.c
@@ -76,8 +76,8 @@ static struct mtd_partition ixdp425_partitions[] = {
static void
ixdp425_flash_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
- struct nand_chip *this = mtd->priv;
- int offset = (int)this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ int offset = (int)nand_get_controller_data(this);
if (ctrl & NAND_CTRL_CHANGE) {
if (ctrl & NAND_NCE) {
@@ -88,7 +88,7 @@ ixdp425_flash_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
offset = (ctrl & NAND_CLE) ? IXDP425_NAND_CMD_BYTE : 0;
offset |= (ctrl & NAND_ALE) ? IXDP425_NAND_ADDR_BYTE : 0;
- this->priv = (void *)offset;
+ nand_set_controller_data(this, (void *)offset);
}
if (cmd != NAND_CMD_NONE)
diff --git a/arch/arm/mach-omap1/board-nand.c b/arch/arm/mach-omap1/board-nand.c
index 4d0835327d20..7684f9203474 100644
--- a/arch/arm/mach-omap1/board-nand.c
+++ b/arch/arm/mach-omap1/board-nand.c
@@ -22,7 +22,7 @@
void omap1_nand_cmd_ctl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
unsigned long mask;
if (cmd == NAND_CMD_NONE)
diff --git a/arch/arm/mach-omap1/include/mach/camera.h b/arch/arm/mach-omap1/include/mach/camera.h
index 847d00f0bb0a..caa6c0d6f0ac 100644
--- a/arch/arm/mach-omap1/include/mach/camera.h
+++ b/arch/arm/mach-omap1/include/mach/camera.h
@@ -1,7 +1,7 @@
#ifndef __ASM_ARCH_CAMERA_H_
#define __ASM_ARCH_CAMERA_H_
-#include <media/omap1_camera.h>
+#include <linux/platform_data/media/omap1_camera.h>
void omap1_camera_init(void *);
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 5076d3f334d2..0517f0c1581a 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -65,6 +65,8 @@ config SOC_AM43XX
select MACH_OMAP_GENERIC
select MIGHT_HAVE_CACHE_L2X0
select HAVE_ARM_SCU
+ select GENERIC_CLOCKEVENTS_BROADCAST
+ select HAVE_ARM_TWD
config SOC_DRA7XX
bool "TI DRA7XX"
@@ -121,6 +123,7 @@ config ARCH_OMAP2PLUS_TYPICAL
select NEON if CPU_V7
select PM
select REGULATOR
+ select REGULATOR_FIXED_VOLTAGE
select TWL4030_CORE if ARCH_OMAP3 || ARCH_OMAP4
select TWL4030_POWER if ARCH_OMAP3 || ARCH_OMAP4
select VFP
@@ -201,7 +204,6 @@ config MACH_OMAP3_PANDORA
depends on ARCH_OMAP3
default y
select OMAP_PACKAGE_CBB
- select REGULATOR_FIXED_VOLTAGE if REGULATOR
config MACH_NOKIA_N810
bool
diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c
index 04a56cc04dfa..809827265fb3 100644
--- a/arch/arm/mach-omap2/board-generic.c
+++ b/arch/arm/mach-omap2/board-generic.c
@@ -16,6 +16,7 @@
#include <linux/of_platform.h>
#include <linux/irqdomain.h>
+#include <asm/setup.h>
#include <asm/mach/arch.h>
#include "common.h"
@@ -76,8 +77,17 @@ static const char *const n900_boards_compat[] __initconst = {
NULL,
};
+/* Legacy userspace on Nokia N900 needs ATAGS exported in /proc/atags,
+ * save them while the data is still not overwritten
+ */
+static void __init rx51_reserve(void)
+{
+ save_atags((const struct tag *)(PAGE_OFFSET + 0x100));
+ omap_reserve();
+}
+
DT_MACHINE_START(OMAP3_N900_DT, "Nokia RX-51 board")
- .reserve = omap_reserve,
+ .reserve = rx51_reserve,
.map_io = omap3_map_io,
.init_early = omap3430_init_early,
.init_machine = omap_generic_init,
diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c
index 14edcd7a2a1d..0a0567f8e8a0 100644
--- a/arch/arm/mach-omap2/board-rx51-peripherals.c
+++ b/arch/arm/mach-omap2/board-rx51-peripherals.c
@@ -39,7 +39,7 @@
#include <sound/tlv320aic3x.h>
#include <sound/tpa6130a2-plat.h>
-#include <media/si4713.h>
+#include <linux/platform_data/media/si4713.h>
#include <linux/platform_data/leds-lp55xx.h>
#include <linux/platform_data/tsl2563.h>
@@ -48,7 +48,7 @@
#include <video/omap-panel-data.h>
#if defined(CONFIG_IR_RX51) || defined(CONFIG_IR_RX51_MODULE)
-#include <media/ir-rx51.h>
+#include <linux/platform_data/media/ir-rx51.h>
#endif
#include "mux.h"
diff --git a/arch/arm/mach-omap2/gpmc-onenand.c b/arch/arm/mach-omap2/gpmc-onenand.c
index 17a6f752a436..7b76ce01c21d 100644
--- a/arch/arm/mach-omap2/gpmc-onenand.c
+++ b/arch/arm/mach-omap2/gpmc-onenand.c
@@ -149,8 +149,8 @@ static int omap2_onenand_get_freq(struct omap_onenand_platform_data *cfg,
freq = 104;
break;
default:
- freq = 54;
- break;
+ pr_err("onenand rate not detected, bad GPMC async timings?\n");
+ freq = 0;
}
return freq;
@@ -271,6 +271,11 @@ static int omap2_onenand_setup_async(void __iomem *onenand_base)
struct gpmc_timings t;
int ret;
+ /*
+ * Note that we need to keep sync_write set for the call to
+ * omap2_onenand_set_async_mode() to work to detect the onenand
+ * supported clock rate for the sync timings.
+ */
if (gpmc_onenand_data->of_node) {
gpmc_read_settings_dt(gpmc_onenand_data->of_node,
&onenand_async);
@@ -281,12 +286,9 @@ static int omap2_onenand_setup_async(void __iomem *onenand_base)
else
gpmc_onenand_data->flags |= ONENAND_SYNC_READ;
onenand_async.sync_read = false;
- onenand_async.sync_write = false;
}
}
- omap2_onenand_set_async_mode(onenand_base);
-
omap2_onenand_calc_async_timings(&t);
ret = gpmc_cs_program_settings(gpmc_onenand_data->cs, &onenand_async);
@@ -310,6 +312,8 @@ static int omap2_onenand_setup_sync(void __iomem *onenand_base, int *freq_ptr)
if (!freq) {
/* Very first call freq is not known */
freq = omap2_onenand_get_freq(gpmc_onenand_data, onenand_base);
+ if (!freq)
+ return -ENODEV;
set_onenand_cfg(onenand_base);
}
diff --git a/arch/arm/mach-omap2/omap-smp.c b/arch/arm/mach-omap2/omap-smp.c
index 5305ec7341ec..79e1f876d1c9 100644
--- a/arch/arm/mach-omap2/omap-smp.c
+++ b/arch/arm/mach-omap2/omap-smp.c
@@ -143,9 +143,9 @@ static int omap4_boot_secondary(unsigned int cpu, struct task_struct *idle)
* Ensure that CPU power state is set to ON to avoid CPU
* powerdomain transition on wfi
*/
- clkdm_wakeup(cpu1_clkdm);
- omap_set_pwrdm_state(cpu1_pwrdm, PWRDM_POWER_ON);
- clkdm_allow_idle(cpu1_clkdm);
+ clkdm_wakeup_nolock(cpu1_clkdm);
+ pwrdm_set_next_pwrst(cpu1_pwrdm, PWRDM_POWER_ON);
+ clkdm_allow_idle_nolock(cpu1_clkdm);
if (IS_PM44XX_ERRATUM(PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD)) {
while (gic_dist_disabled()) {
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index cc8a987149e2..48495ad82aba 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -890,6 +890,36 @@ static int _init_opt_clks(struct omap_hwmod *oh)
return ret;
}
+static void _enable_optional_clocks(struct omap_hwmod *oh)
+{
+ struct omap_hwmod_opt_clk *oc;
+ int i;
+
+ pr_debug("omap_hwmod: %s: enabling optional clocks\n", oh->name);
+
+ for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++)
+ if (oc->_clk) {
+ pr_debug("omap_hwmod: enable %s:%s\n", oc->role,
+ __clk_get_name(oc->_clk));
+ clk_enable(oc->_clk);
+ }
+}
+
+static void _disable_optional_clocks(struct omap_hwmod *oh)
+{
+ struct omap_hwmod_opt_clk *oc;
+ int i;
+
+ pr_debug("omap_hwmod: %s: disabling optional clocks\n", oh->name);
+
+ for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++)
+ if (oc->_clk) {
+ pr_debug("omap_hwmod: disable %s:%s\n", oc->role,
+ __clk_get_name(oc->_clk));
+ clk_disable(oc->_clk);
+ }
+}
+
/**
* _enable_clocks - enable hwmod main clock and interface clocks
* @oh: struct omap_hwmod *
@@ -917,6 +947,9 @@ static int _enable_clocks(struct omap_hwmod *oh)
clk_enable(os->_clk);
}
+ if (oh->flags & HWMOD_OPT_CLKS_NEEDED)
+ _enable_optional_clocks(oh);
+
/* The opt clocks are controlled by the device driver. */
return 0;
@@ -948,41 +981,14 @@ static int _disable_clocks(struct omap_hwmod *oh)
clk_disable(os->_clk);
}
+ if (oh->flags & HWMOD_OPT_CLKS_NEEDED)
+ _disable_optional_clocks(oh);
+
/* The opt clocks are controlled by the device driver. */
return 0;
}
-static void _enable_optional_clocks(struct omap_hwmod *oh)
-{
- struct omap_hwmod_opt_clk *oc;
- int i;
-
- pr_debug("omap_hwmod: %s: enabling optional clocks\n", oh->name);
-
- for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++)
- if (oc->_clk) {
- pr_debug("omap_hwmod: enable %s:%s\n", oc->role,
- __clk_get_name(oc->_clk));
- clk_enable(oc->_clk);
- }
-}
-
-static void _disable_optional_clocks(struct omap_hwmod *oh)
-{
- struct omap_hwmod_opt_clk *oc;
- int i;
-
- pr_debug("omap_hwmod: %s: disabling optional clocks\n", oh->name);
-
- for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++)
- if (oc->_clk) {
- pr_debug("omap_hwmod: disable %s:%s\n", oc->role,
- __clk_get_name(oc->_clk));
- clk_disable(oc->_clk);
- }
-}
-
/**
* _omap4_enable_module - enable CLKCTRL modulemode on OMAP4
* @oh: struct omap_hwmod *
diff --git a/arch/arm/mach-omap2/omap_hwmod.h b/arch/arm/mach-omap2/omap_hwmod.h
index ca6df1a73475..76bce11c85a4 100644
--- a/arch/arm/mach-omap2/omap_hwmod.h
+++ b/arch/arm/mach-omap2/omap_hwmod.h
@@ -523,6 +523,8 @@ struct omap_hwmod_omap4_prcm {
* HWMOD_RECONFIG_IO_CHAIN: omap_hwmod code needs to reconfigure wake-up
* events by calling _reconfigure_io_chain() when a device is enabled
* or idled.
+ * HWMOD_OPT_CLKS_NEEDED: The optional clocks are needed for the module to
+ * operate and they need to be handled at the same time as the main_clk.
*/
#define HWMOD_SWSUP_SIDLE (1 << 0)
#define HWMOD_SWSUP_MSTANDBY (1 << 1)
@@ -538,6 +540,7 @@ struct omap_hwmod_omap4_prcm {
#define HWMOD_FORCE_MSTANDBY (1 << 11)
#define HWMOD_SWSUP_SIDLE_ACT (1 << 12)
#define HWMOD_RECONFIG_IO_CHAIN (1 << 13)
+#define HWMOD_OPT_CLKS_NEEDED (1 << 14)
/*
* omap_hwmod._int_flags definitions
diff --git a/arch/arm/mach-omap2/omap_hwmod_7xx_data.c b/arch/arm/mach-omap2/omap_hwmod_7xx_data.c
index 51d1ecb384bd..ee4e04434a94 100644
--- a/arch/arm/mach-omap2/omap_hwmod_7xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_7xx_data.c
@@ -1298,6 +1298,44 @@ static struct omap_hwmod dra7xx_mcspi4_hwmod = {
};
/*
+ * 'mcasp' class
+ *
+ */
+static struct omap_hwmod_class_sysconfig dra7xx_mcasp_sysc = {
+ .sysc_offs = 0x0004,
+ .sysc_flags = SYSC_HAS_SIDLEMODE,
+ .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+ .sysc_fields = &omap_hwmod_sysc_type3,
+};
+
+static struct omap_hwmod_class dra7xx_mcasp_hwmod_class = {
+ .name = "mcasp",
+ .sysc = &dra7xx_mcasp_sysc,
+};
+
+/* mcasp3 */
+static struct omap_hwmod_opt_clk mcasp3_opt_clks[] = {
+ { .role = "ahclkx", .clk = "mcasp3_ahclkx_mux" },
+};
+
+static struct omap_hwmod dra7xx_mcasp3_hwmod = {
+ .name = "mcasp3",
+ .class = &dra7xx_mcasp_hwmod_class,
+ .clkdm_name = "l4per2_clkdm",
+ .main_clk = "mcasp3_aux_gfclk_mux",
+ .flags = HWMOD_OPT_CLKS_NEEDED,
+ .prcm = {
+ .omap4 = {
+ .clkctrl_offs = DRA7XX_CM_L4PER2_MCASP3_CLKCTRL_OFFSET,
+ .context_offs = DRA7XX_RM_L4PER2_MCASP3_CONTEXT_OFFSET,
+ .modulemode = MODULEMODE_SWCTRL,
+ },
+ },
+ .opt_clks = mcasp3_opt_clks,
+ .opt_clks_cnt = ARRAY_SIZE(mcasp3_opt_clks),
+};
+
+/*
* 'mmc' class
*
*/
@@ -2566,6 +2604,22 @@ static struct omap_hwmod_ocp_if dra7xx_l3_main_1__hdmi = {
.user = OCP_USER_MPU | OCP_USER_SDMA,
};
+/* l4_per2 -> mcasp3 */
+static struct omap_hwmod_ocp_if dra7xx_l4_per2__mcasp3 = {
+ .master = &dra7xx_l4_per2_hwmod,
+ .slave = &dra7xx_mcasp3_hwmod,
+ .clk = "l4_root_clk_div",
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* l3_main_1 -> mcasp3 */
+static struct omap_hwmod_ocp_if dra7xx_l3_main_1__mcasp3 = {
+ .master = &dra7xx_l3_main_1_hwmod,
+ .slave = &dra7xx_mcasp3_hwmod,
+ .clk = "l3_iclk_div",
+ .user = OCP_USER_MPU | OCP_USER_SDMA,
+};
+
/* l4_per1 -> elm */
static struct omap_hwmod_ocp_if dra7xx_l4_per1__elm = {
.master = &dra7xx_l4_per1_hwmod,
@@ -3308,6 +3362,8 @@ static struct omap_hwmod_ocp_if *dra7xx_hwmod_ocp_ifs[] __initdata = {
&dra7xx_l4_wkup__dcan1,
&dra7xx_l4_per2__dcan2,
&dra7xx_l4_per2__cpgmac0,
+ &dra7xx_l4_per2__mcasp3,
+ &dra7xx_l3_main_1__mcasp3,
&dra7xx_gmac__mdio,
&dra7xx_l4_cfg__dma_system,
&dra7xx_l3_main_1__dss,
diff --git a/arch/arm/mach-omap2/omap_hwmod_81xx_data.c b/arch/arm/mach-omap2/omap_hwmod_81xx_data.c
index b1288f56d509..6256052893ec 100644
--- a/arch/arm/mach-omap2/omap_hwmod_81xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_81xx_data.c
@@ -144,6 +144,7 @@ static struct omap_hwmod dm81xx_l4_ls_hwmod = {
.name = "l4_ls",
.clkdm_name = "alwon_l3s_clkdm",
.class = &l4_hwmod_class,
+ .flags = HWMOD_NO_IDLEST,
};
/*
@@ -155,6 +156,7 @@ static struct omap_hwmod dm81xx_l4_hs_hwmod = {
.name = "l4_hs",
.clkdm_name = "alwon_l3_med_clkdm",
.class = &l4_hwmod_class,
+ .flags = HWMOD_NO_IDLEST,
};
/* L3 slow -> L4 ls peripheral interface running at 125MHz */
@@ -850,6 +852,7 @@ static struct omap_hwmod dm816x_emac0_hwmod = {
.name = "emac0",
.clkdm_name = "alwon_ethernet_clkdm",
.class = &dm816x_emac_hwmod_class,
+ .flags = HWMOD_NO_IDLEST,
};
static struct omap_hwmod_ocp_if dm81xx_l4_hs__emac0 = {
diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c
index 1dfe34654c43..58144779dec4 100644
--- a/arch/arm/mach-omap2/pdata-quirks.c
+++ b/arch/arm/mach-omap2/pdata-quirks.c
@@ -24,9 +24,6 @@
#include <linux/platform_data/iommu-omap.h>
#include <linux/platform_data/wkup_m3.h>
-#include <asm/siginfo.h>
-#include <asm/signal.h>
-
#include "common.h"
#include "common-board-devices.h"
#include "dss-common.h"
@@ -385,29 +382,6 @@ static void __init omap3_pandora_legacy_init(void)
}
#endif /* CONFIG_ARCH_OMAP3 */
-#ifdef CONFIG_SOC_TI81XX
-static int fault_fixed_up;
-
-static int t410_abort_handler(unsigned long addr, unsigned int fsr,
- struct pt_regs *regs)
-{
- if ((fsr == 0x406 || fsr == 0xc06) && !fault_fixed_up) {
- pr_warn("External imprecise Data abort at addr=%#lx, fsr=%#x ignored.\n",
- addr, fsr);
- fault_fixed_up = 1;
- return 0;
- }
-
- return 1;
-}
-
-static void __init t410_abort_init(void)
-{
- hook_fault_code(16 + 6, t410_abort_handler, SIGBUS, BUS_OBJERR,
- "imprecise external abort");
-}
-#endif
-
#if defined(CONFIG_ARCH_OMAP4) || defined(CONFIG_SOC_OMAP5)
static struct iommu_platform_data omap4_iommu_pdata = {
.reset_name = "mmu_cache",
@@ -536,9 +510,6 @@ static struct pdata_init pdata_quirks[] __initdata = {
{ "openpandora,omap3-pandora-600mhz", omap3_pandora_legacy_init, },
{ "openpandora,omap3-pandora-1ghz", omap3_pandora_legacy_init, },
#endif
-#ifdef CONFIG_SOC_TI81XX
- { "hp,t410", t410_abort_init, },
-#endif
#ifdef CONFIG_SOC_OMAP5
{ "ti,omap5-uevm", omap5_uevm_legacy_init, },
#endif
diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
index 87b98bf92366..2dbd3785ee6f 100644
--- a/arch/arm/mach-omap2/pm34xx.c
+++ b/arch/arm/mach-omap2/pm34xx.c
@@ -301,11 +301,11 @@ static void omap3_pm_idle(void)
if (omap_irq_pending())
return;
- trace_cpu_idle(1, smp_processor_id());
+ trace_cpu_idle_rcuidle(1, smp_processor_id());
omap_sram_idle();
- trace_cpu_idle(PWR_EVENT_EXIT, smp_processor_id());
+ trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, smp_processor_id());
}
#ifdef CONFIG_SUSPEND
diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
index b18ebbefae09..f86692dbcfd5 100644
--- a/arch/arm/mach-omap2/timer.c
+++ b/arch/arm/mach-omap2/timer.c
@@ -320,6 +320,12 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer,
return r;
}
+#if !defined(CONFIG_SMP) && defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST)
+void tick_broadcast(const struct cpumask *mask)
+{
+}
+#endif
+
static void __init omap2_gp_clockevent_init(int gptimer_id,
const char *fck_source,
const char *property)
diff --git a/arch/arm/mach-orion5x/include/mach/entry-macro.S b/arch/arm/mach-orion5x/include/mach/entry-macro.S
index 79eb502a1e64..73919a36b577 100644
--- a/arch/arm/mach-orion5x/include/mach/entry-macro.S
+++ b/arch/arm/mach-orion5x/include/mach/entry-macro.S
@@ -21,5 +21,5 @@
@ find cause bits that are unmasked
ands \irqstat, \irqstat, \tmp @ clear Z flag if any
clzne \irqnr, \irqstat @ calc irqnr
- rsbne \irqnr, \irqnr, #31
+ rsbne \irqnr, \irqnr, #32
.endm
diff --git a/arch/arm/mach-orion5x/ts78xx-setup.c b/arch/arm/mach-orion5x/ts78xx-setup.c
index 1b704d35cf5b..96cf6b51eddc 100644
--- a/arch/arm/mach-orion5x/ts78xx-setup.c
+++ b/arch/arm/mach-orion5x/ts78xx-setup.c
@@ -176,7 +176,7 @@ static void ts78xx_ts_rtc_unload(void)
static void ts78xx_ts_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
if (ctrl & NAND_CTRL_CHANGE) {
unsigned char bits;
@@ -200,7 +200,7 @@ static int ts78xx_ts_nand_dev_ready(struct mtd_info *mtd)
static void ts78xx_ts_nand_write_buf(struct mtd_info *mtd,
const uint8_t *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
void __iomem *io_base = chip->IO_ADDR_W;
unsigned long off = ((unsigned long)buf & 3);
int sz;
@@ -227,7 +227,7 @@ static void ts78xx_ts_nand_write_buf(struct mtd_info *mtd,
static void ts78xx_ts_nand_read_buf(struct mtd_info *mtd,
uint8_t *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
void __iomem *io_base = chip->IO_ADDR_R;
unsigned long off = ((unsigned long)buf & 3);
int sz;
diff --git a/arch/arm/mach-pxa/balloon3.c b/arch/arm/mach-pxa/balloon3.c
index a727282bfa99..7734ec4f1385 100644
--- a/arch/arm/mach-pxa/balloon3.c
+++ b/arch/arm/mach-pxa/balloon3.c
@@ -572,7 +572,7 @@ static inline void balloon3_i2c_init(void) {}
#if defined(CONFIG_MTD_NAND_PLATFORM)||defined(CONFIG_MTD_NAND_PLATFORM_MODULE)
static void balloon3_nand_cmd_ctl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
uint8_t balloon3_ctl_set = 0, balloon3_ctl_clr = 0;
if (ctrl & NAND_CTRL_CHANGE) {
diff --git a/arch/arm/mach-pxa/devices.c b/arch/arm/mach-pxa/devices.c
index 2a6e0ae2b920..d1211a40f400 100644
--- a/arch/arm/mach-pxa/devices.c
+++ b/arch/arm/mach-pxa/devices.c
@@ -14,7 +14,7 @@
#include <mach/irqs.h>
#include <linux/platform_data/usb-ohci-pxa27x.h>
#include <linux/platform_data/keypad-pxa27x.h>
-#include <linux/platform_data/camera-pxa.h>
+#include <linux/platform_data/media/camera-pxa.h>
#include <mach/audio.h>
#include <mach/hardware.h>
#include <linux/platform_data/mmp_dma.h>
diff --git a/arch/arm/mach-pxa/em-x270.c b/arch/arm/mach-pxa/em-x270.c
index 9d7072b04045..2a76c4ef8d03 100644
--- a/arch/arm/mach-pxa/em-x270.c
+++ b/arch/arm/mach-pxa/em-x270.c
@@ -46,7 +46,7 @@
#include <linux/platform_data/usb-ohci-pxa27x.h>
#include <linux/platform_data/mmc-pxamci.h>
#include <linux/platform_data/keypad-pxa27x.h>
-#include <linux/platform_data/camera-pxa.h>
+#include <linux/platform_data/media/camera-pxa.h>
#include "generic.h"
#include "devices.h"
@@ -289,7 +289,7 @@ static void nand_cs_off(void)
static void em_x270_nand_cmd_ctl(struct mtd_info *mtd, int dat,
unsigned int ctrl)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
unsigned long nandaddr = (unsigned long)this->IO_ADDR_W;
dsb();
diff --git a/arch/arm/mach-pxa/ezx.c b/arch/arm/mach-pxa/ezx.c
index 9a9c15bfcd34..cd6224032109 100644
--- a/arch/arm/mach-pxa/ezx.c
+++ b/arch/arm/mach-pxa/ezx.c
@@ -34,7 +34,7 @@
#include <linux/platform_data/usb-ohci-pxa27x.h>
#include <mach/hardware.h>
#include <linux/platform_data/keypad-pxa27x.h>
-#include <linux/platform_data/camera-pxa.h>
+#include <linux/platform_data/media/camera-pxa.h>
#include "devices.h"
#include "generic.h"
@@ -889,6 +889,7 @@ static void __init e680_init(void)
pxa_set_keypad_info(&e680_keypad_platform_data);
+ pwm_add_table(ezx_pwm_lookup, ARRAY_SIZE(ezx_pwm_lookup));
platform_add_devices(ARRAY_AND_SIZE(ezx_devices));
platform_add_devices(ARRAY_AND_SIZE(e680_devices));
}
@@ -956,6 +957,7 @@ static void __init a1200_init(void)
pxa_set_keypad_info(&a1200_keypad_platform_data);
+ pwm_add_table(ezx_pwm_lookup, ARRAY_SIZE(ezx_pwm_lookup));
platform_add_devices(ARRAY_AND_SIZE(ezx_devices));
platform_add_devices(ARRAY_AND_SIZE(a1200_devices));
}
@@ -1148,6 +1150,7 @@ static void __init a910_init(void)
platform_device_register(&a910_camera);
}
+ pwm_add_table(ezx_pwm_lookup, ARRAY_SIZE(ezx_pwm_lookup));
platform_add_devices(ARRAY_AND_SIZE(ezx_devices));
platform_add_devices(ARRAY_AND_SIZE(a910_devices));
}
@@ -1215,6 +1218,7 @@ static void __init e6_init(void)
pxa_set_keypad_info(&e6_keypad_platform_data);
+ pwm_add_table(ezx_pwm_lookup, ARRAY_SIZE(ezx_pwm_lookup));
platform_add_devices(ARRAY_AND_SIZE(ezx_devices));
platform_add_devices(ARRAY_AND_SIZE(e6_devices));
}
@@ -1256,6 +1260,7 @@ static void __init e2_init(void)
pxa_set_keypad_info(&e2_keypad_platform_data);
+ pwm_add_table(ezx_pwm_lookup, ARRAY_SIZE(ezx_pwm_lookup));
platform_add_devices(ARRAY_AND_SIZE(ezx_devices));
platform_add_devices(ARRAY_AND_SIZE(e2_devices));
}
diff --git a/arch/arm/mach-pxa/mioa701.c b/arch/arm/mach-pxa/mioa701.c
index 3b52b1aa0659..ccfd2b63c6a4 100644
--- a/arch/arm/mach-pxa/mioa701.c
+++ b/arch/arm/mach-pxa/mioa701.c
@@ -54,7 +54,7 @@
#include <linux/platform_data/mmc-pxamci.h>
#include <mach/udc.h>
#include <mach/pxa27x-udc.h>
-#include <linux/platform_data/camera-pxa.h>
+#include <linux/platform_data/media/camera-pxa.h>
#include <mach/audio.h>
#include <mach/smemc.h>
#include <media/soc_camera.h>
diff --git a/arch/arm/mach-pxa/palm27x.c b/arch/arm/mach-pxa/palm27x.c
index 13eba2b26e0a..8fbfb10047ec 100644
--- a/arch/arm/mach-pxa/palm27x.c
+++ b/arch/arm/mach-pxa/palm27x.c
@@ -344,7 +344,7 @@ void __init palm27x_pwm_init(int bl, int lcd)
{
palm_bl_power = bl;
palm_lcd_power = lcd;
- pwm_add_lookup(palm27x_pwm_lookup, ARRAY_SIZE(palm27x_pwm_lookup));
+ pwm_add_table(palm27x_pwm_lookup, ARRAY_SIZE(palm27x_pwm_lookup));
platform_device_register(&palm27x_backlight);
}
#endif
diff --git a/arch/arm/mach-pxa/palmtc.c b/arch/arm/mach-pxa/palmtc.c
index aebf6de62468..0b5c3876720c 100644
--- a/arch/arm/mach-pxa/palmtc.c
+++ b/arch/arm/mach-pxa/palmtc.c
@@ -169,7 +169,7 @@ static inline void palmtc_keys_init(void) {}
#if defined(CONFIG_BACKLIGHT_PWM) || defined(CONFIG_BACKLIGHT_PWM_MODULE)
static struct pwm_lookup palmtc_pwm_lookup[] = {
PWM_LOOKUP("pxa25x-pwm.1", 0, "pwm-backlight.0", NULL, PALMTC_PERIOD_NS,
- PWM_PERIOD_NORMAL),
+ PWM_POLARITY_NORMAL),
};
static struct platform_pwm_backlight_data palmtc_backlight_data = {
diff --git a/arch/arm/mach-pxa/palmtreo.c b/arch/arm/mach-pxa/palmtreo.c
index d8b937c870de..2dc56062fb7e 100644
--- a/arch/arm/mach-pxa/palmtreo.c
+++ b/arch/arm/mach-pxa/palmtreo.c
@@ -43,7 +43,7 @@
#include <linux/platform_data/usb-ohci-pxa27x.h>
#include <mach/pxa2xx-regs.h>
#include <linux/platform_data/asoc-palm27x.h>
-#include <linux/platform_data/camera-pxa.h>
+#include <linux/platform_data/media/camera-pxa.h>
#include <mach/palm27x.h>
#include <sound/pxa2xx-lib.h>
diff --git a/arch/arm/mach-pxa/palmtx.c b/arch/arm/mach-pxa/palmtx.c
index 83f830dd8ad8..d787dd17f6b2 100644
--- a/arch/arm/mach-pxa/palmtx.c
+++ b/arch/arm/mach-pxa/palmtx.c
@@ -250,7 +250,7 @@ static inline void palmtx_keys_init(void) {}
static void palmtx_nand_cmd_ctl(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
char __iomem *nandaddr = this->IO_ADDR_W;
if (cmd == NAND_CMD_NONE)
diff --git a/arch/arm/mach-pxa/palmz72.c b/arch/arm/mach-pxa/palmz72.c
index 1a35ddf218da..e3df17a7e8d4 100644
--- a/arch/arm/mach-pxa/palmz72.c
+++ b/arch/arm/mach-pxa/palmz72.c
@@ -49,7 +49,7 @@
#include <mach/palm27x.h>
#include <mach/pm.h>
-#include <linux/platform_data/camera-pxa.h>
+#include <linux/platform_data/media/camera-pxa.h>
#include <media/soc_camera.h>
diff --git a/arch/arm/mach-pxa/pcm990-baseboard.c b/arch/arm/mach-pxa/pcm990-baseboard.c
index b71c96f614f9..8459239a093c 100644
--- a/arch/arm/mach-pxa/pcm990-baseboard.c
+++ b/arch/arm/mach-pxa/pcm990-baseboard.c
@@ -27,10 +27,10 @@
#include <linux/pwm.h>
#include <linux/pwm_backlight.h>
-#include <media/mt9v022.h>
+#include <media/i2c/mt9v022.h>
#include <media/soc_camera.h>
-#include <linux/platform_data/camera-pxa.h>
+#include <linux/platform_data/media/camera-pxa.h>
#include <asm/mach/map.h>
#include <mach/pxa27x.h>
#include <mach/audio.h>
diff --git a/arch/arm/mach-s3c24xx/pll-s3c2440-12000000.c b/arch/arm/mach-s3c24xx/pll-s3c2440-12000000.c
index a19460e6e7b0..b355fca6cc2e 100644
--- a/arch/arm/mach-s3c24xx/pll-s3c2440-12000000.c
+++ b/arch/arm/mach-s3c24xx/pll-s3c2440-12000000.c
@@ -20,7 +20,7 @@
#include <plat/cpu.h>
#include <plat/cpu-freq-core.h>
-static struct cpufreq_frequency_table s3c2440_plls_12[] __initdata = {
+static struct cpufreq_frequency_table s3c2440_plls_12[] = {
{ .frequency = 75000000, .driver_data = PLLVAL(0x75, 3, 3), }, /* FVco 600.000000 */
{ .frequency = 80000000, .driver_data = PLLVAL(0x98, 4, 3), }, /* FVco 640.000000 */
{ .frequency = 90000000, .driver_data = PLLVAL(0x70, 2, 3), }, /* FVco 720.000000 */
diff --git a/arch/arm/mach-s3c24xx/pll-s3c2440-16934400.c b/arch/arm/mach-s3c24xx/pll-s3c2440-16934400.c
index 1191b2905625..be9a248b5ce9 100644
--- a/arch/arm/mach-s3c24xx/pll-s3c2440-16934400.c
+++ b/arch/arm/mach-s3c24xx/pll-s3c2440-16934400.c
@@ -20,7 +20,7 @@
#include <plat/cpu.h>
#include <plat/cpu-freq-core.h>
-static struct cpufreq_frequency_table s3c2440_plls_169344[] __initdata = {
+static struct cpufreq_frequency_table s3c2440_plls_169344[] = {
{ .frequency = 78019200, .driver_data = PLLVAL(121, 5, 3), }, /* FVco 624.153600 */
{ .frequency = 84067200, .driver_data = PLLVAL(131, 5, 3), }, /* FVco 672.537600 */
{ .frequency = 90115200, .driver_data = PLLVAL(141, 5, 3), }, /* FVco 720.921600 */
diff --git a/arch/arm/mach-shmobile/setup-r8a7793.c b/arch/arm/mach-shmobile/setup-r8a7793.c
index 1d2825cb7a65..5fce87f7f254 100644
--- a/arch/arm/mach-shmobile/setup-r8a7793.c
+++ b/arch/arm/mach-shmobile/setup-r8a7793.c
@@ -19,7 +19,7 @@
#include "common.h"
#include "rcar-gen2.h"
-static const char *r8a7793_boards_compat_dt[] __initconst = {
+static const char * const r8a7793_boards_compat_dt[] __initconst = {
"renesas,r8a7793",
NULL,
};
diff --git a/arch/arm/mach-sti/Kconfig b/arch/arm/mach-sti/Kconfig
index 125865daaf17..12dd1dc0a041 100644
--- a/arch/arm/mach-sti/Kconfig
+++ b/arch/arm/mach-sti/Kconfig
@@ -3,6 +3,7 @@ menuconfig ARCH_STI
select ARM_GIC
select ST_IRQCHIP
select ARM_GLOBAL_TIMER
+ select CLKSRC_ST_LPC
select PINCTRL
select PINCTRL_ST
select MFD_SYSCON
diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig
index c9ac19b24e5a..5eacdd61e61c 100644
--- a/arch/arm/mach-ux500/Kconfig
+++ b/arch/arm/mach-ux500/Kconfig
@@ -32,6 +32,7 @@ config UX500_SOC_DB8500
select PINCTRL_AB8540
select REGULATOR
select REGULATOR_DB8500_PRCMU
+ select CLKSRC_DBX500_PRCMU
select PM_GENERIC_DOMAINS if PM
config MACH_MOP500
diff --git a/arch/arm/mach-zx/Kconfig b/arch/arm/mach-zx/Kconfig
index 7fdc5bf24f9b..446334a25cf5 100644
--- a/arch/arm/mach-zx/Kconfig
+++ b/arch/arm/mach-zx/Kconfig
@@ -13,7 +13,7 @@ config SOC_ZX296702
select ARM_GLOBAL_TIMER
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
- select PM_GENERIC_DOMAINS
+ select PM_GENERIC_DOMAINS if PM
help
Support for ZTE ZX296702 SoC which is a dual core CortexA9MP
endif
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
index 493692d838c6..9f9d54271aad 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -790,7 +790,7 @@ static const struct l2c_init_data l2c310_init_fns __initconst = {
};
static int __init __l2c_init(const struct l2c_init_data *data,
- u32 aux_val, u32 aux_mask, u32 cache_id)
+ u32 aux_val, u32 aux_mask, u32 cache_id, bool nosync)
{
struct outer_cache_fns fns;
unsigned way_size_bits, ways;
@@ -866,6 +866,10 @@ static int __init __l2c_init(const struct l2c_init_data *data,
fns.configure = outer_cache.configure;
if (data->fixup)
data->fixup(l2x0_base, cache_id, &fns);
+ if (nosync) {
+ pr_info("L2C: disabling outer sync\n");
+ fns.sync = NULL;
+ }
/*
* Check if l2x0 controller is already enabled. If we are booting
@@ -925,7 +929,7 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
if (data->save)
data->save(l2x0_base);
- __l2c_init(data, aux_val, aux_mask, cache_id);
+ __l2c_init(data, aux_val, aux_mask, cache_id, false);
}
#ifdef CONFIG_OF
@@ -1060,6 +1064,18 @@ static void __init l2x0_of_parse(const struct device_node *np,
val |= (dirty - 1) << L2X0_AUX_CTRL_DIRTY_LATENCY_SHIFT;
}
+ if (of_property_read_bool(np, "arm,parity-enable")) {
+ mask &= ~L2C_AUX_CTRL_PARITY_ENABLE;
+ val |= L2C_AUX_CTRL_PARITY_ENABLE;
+ } else if (of_property_read_bool(np, "arm,parity-disable")) {
+ mask &= ~L2C_AUX_CTRL_PARITY_ENABLE;
+ }
+
+ if (of_property_read_bool(np, "arm,shared-override")) {
+ mask &= ~L2C_AUX_CTRL_SHARED_OVERRIDE;
+ val |= L2C_AUX_CTRL_SHARED_OVERRIDE;
+ }
+
ret = l2x0_cache_size_of_parse(np, aux_val, aux_mask, &assoc, SZ_256K);
if (ret)
return;
@@ -1176,6 +1192,14 @@ static void __init l2c310_of_parse(const struct device_node *np,
*aux_mask &= ~L2C_AUX_CTRL_SHARED_OVERRIDE;
}
+ if (of_property_read_bool(np, "arm,parity-enable")) {
+ *aux_val |= L2C_AUX_CTRL_PARITY_ENABLE;
+ *aux_mask &= ~L2C_AUX_CTRL_PARITY_ENABLE;
+ } else if (of_property_read_bool(np, "arm,parity-disable")) {
+ *aux_val &= ~L2C_AUX_CTRL_PARITY_ENABLE;
+ *aux_mask &= ~L2C_AUX_CTRL_PARITY_ENABLE;
+ }
+
prefetch = l2x0_saved_regs.prefetch_ctrl;
ret = of_property_read_u32(np, "arm,double-linefill", &val);
@@ -1704,6 +1728,7 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask)
struct resource res;
u32 cache_id, old_aux;
u32 cache_level = 2;
+ bool nosync = false;
np = of_find_matching_node(NULL, l2x0_ids);
if (!np)
@@ -1742,6 +1767,8 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask)
if (cache_level != 2)
pr_err("L2C: device tree specifies invalid cache level\n");
+ nosync = of_property_read_bool(np, "arm,outer-sync-disable");
+
/* Read back current (default) hardware configuration */
if (data->save)
data->save(l2x0_base);
@@ -1756,6 +1783,6 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask)
else
cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID);
- return __l2c_init(data, aux_val, aux_mask, cache_id);
+ return __l2c_init(data, aux_val, aux_mask, cache_id, nosync);
}
#endif
diff --git a/arch/arm/mm/cache-uniphier.c b/arch/arm/mm/cache-uniphier.c
index 0502ba17a3ab..a6fa7b73fbe0 100644
--- a/arch/arm/mm/cache-uniphier.c
+++ b/arch/arm/mm/cache-uniphier.c
@@ -377,17 +377,6 @@ static const struct of_device_id uniphier_cache_match[] __initconst = {
{ /* sentinel */ }
};
-static struct device_node * __init uniphier_cache_get_next_level_node(
- struct device_node *np)
-{
- u32 phandle;
-
- if (of_property_read_u32(np, "next-level-cache", &phandle))
- return NULL;
-
- return of_find_node_by_phandle(phandle);
-}
-
static int __init __uniphier_cache_init(struct device_node *np,
unsigned int *cache_level)
{
@@ -491,7 +480,7 @@ static int __init __uniphier_cache_init(struct device_node *np,
* next level cache fails because we want to continue with available
* cache levels.
*/
- next_np = uniphier_cache_get_next_level_node(np);
+ next_np = of_find_next_cache_node(np);
if (next_np) {
(*cache_level)++;
ret = __uniphier_cache_init(next_np, cache_level);
diff --git a/arch/arm/mm/context.c b/arch/arm/mm/context.c
index 845769e41332..c8c8b9ed02e0 100644
--- a/arch/arm/mm/context.c
+++ b/arch/arm/mm/context.c
@@ -165,13 +165,28 @@ static void flush_context(unsigned int cpu)
__flush_icache_all();
}
-static int is_reserved_asid(u64 asid)
+static bool check_update_reserved_asid(u64 asid, u64 newasid)
{
int cpu;
- for_each_possible_cpu(cpu)
- if (per_cpu(reserved_asids, cpu) == asid)
- return 1;
- return 0;
+ bool hit = false;
+
+ /*
+ * Iterate over the set of reserved ASIDs looking for a match.
+ * If we find one, then we can update our mm to use newasid
+ * (i.e. the same ASID in the current generation) but we can't
+ * exit the loop early, since we need to ensure that all copies
+ * of the old ASID are updated to reflect the mm. Failure to do
+ * so could result in us missing the reserved ASID in a future
+ * generation.
+ */
+ for_each_possible_cpu(cpu) {
+ if (per_cpu(reserved_asids, cpu) == asid) {
+ hit = true;
+ per_cpu(reserved_asids, cpu) = newasid;
+ }
+ }
+
+ return hit;
}
static u64 new_context(struct mm_struct *mm, unsigned int cpu)
@@ -181,12 +196,14 @@ static u64 new_context(struct mm_struct *mm, unsigned int cpu)
u64 generation = atomic64_read(&asid_generation);
if (asid != 0) {
+ u64 newasid = generation | (asid & ~ASID_MASK);
+
/*
* If our current ASID was active during a rollover, we
* can continue to use it and this was just a false alarm.
*/
- if (is_reserved_asid(asid))
- return generation | (asid & ~ASID_MASK);
+ if (check_update_reserved_asid(asid, newasid))
+ return newasid;
/*
* We had a valid ASID in a previous life, so try to re-use
@@ -194,7 +211,7 @@ static u64 new_context(struct mm_struct *mm, unsigned int cpu)
*/
asid &= ~ASID_MASK;
if (!__test_and_set_bit(asid, asid_map))
- goto bump_gen;
+ return newasid;
}
/*
@@ -216,11 +233,8 @@ static u64 new_context(struct mm_struct *mm, unsigned int cpu)
__set_bit(asid, asid_map);
cur_idx = asid;
-
-bump_gen:
- asid |= generation;
cpumask_clear(mm_cpumask(mm));
- return asid;
+ return asid | generation;
}
void check_and_switch_context(struct mm_struct *mm, struct task_struct *tsk)
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index e62400e5fb99..534a60ae282e 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -1521,7 +1521,7 @@ static int __map_sg_chunk(struct device *dev, struct scatterlist *sg,
return -ENOMEM;
for (count = 0, s = sg; count < (size >> PAGE_SHIFT); s = sg_next(s)) {
- phys_addr_t phys = sg_phys(s) & PAGE_MASK;
+ phys_addr_t phys = page_to_phys(sg_page(s));
unsigned int len = PAGE_ALIGN(s->offset + s->length);
if (!is_coherent &&
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index 8a63b4cdc0f2..49bd08178008 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -22,6 +22,7 @@
#include <linux/memblock.h>
#include <linux/dma-contiguous.h>
#include <linux/sizes.h>
+#include <linux/stop_machine.h>
#include <asm/cp15.h>
#include <asm/mach-types.h>
@@ -191,7 +192,7 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max_low,
#ifdef CONFIG_HAVE_ARCH_PFN_VALID
int pfn_valid(unsigned long pfn)
{
- return memblock_is_memory(__pfn_to_phys(pfn));
+ return memblock_is_map_memory(__pfn_to_phys(pfn));
}
EXPORT_SYMBOL(pfn_valid);
#endif
@@ -432,6 +433,9 @@ static void __init free_highpages(void)
if (end <= max_low)
continue;
+ if (memblock_is_nomap(mem))
+ continue;
+
/* Truncate partial highmem entries */
if (start < max_low)
start = max_low;
@@ -627,12 +631,10 @@ static struct section_perm ro_perms[] = {
* safe to be called with preemption disabled, as under stop_machine().
*/
static inline void section_update(unsigned long addr, pmdval_t mask,
- pmdval_t prot)
+ pmdval_t prot, struct mm_struct *mm)
{
- struct mm_struct *mm;
pmd_t *pmd;
- mm = current->active_mm;
pmd = pmd_offset(pud_offset(pgd_offset(mm, addr), addr), addr);
#ifdef CONFIG_ARM_LPAE
@@ -656,49 +658,82 @@ static inline bool arch_has_strict_perms(void)
return !!(get_cr() & CR_XP);
}
-#define set_section_perms(perms, field) { \
- size_t i; \
- unsigned long addr; \
- \
- if (!arch_has_strict_perms()) \
- return; \
- \
- for (i = 0; i < ARRAY_SIZE(perms); i++) { \
- if (!IS_ALIGNED(perms[i].start, SECTION_SIZE) || \
- !IS_ALIGNED(perms[i].end, SECTION_SIZE)) { \
- pr_err("BUG: section %lx-%lx not aligned to %lx\n", \
- perms[i].start, perms[i].end, \
- SECTION_SIZE); \
- continue; \
- } \
- \
- for (addr = perms[i].start; \
- addr < perms[i].end; \
- addr += SECTION_SIZE) \
- section_update(addr, perms[i].mask, \
- perms[i].field); \
- } \
+void set_section_perms(struct section_perm *perms, int n, bool set,
+ struct mm_struct *mm)
+{
+ size_t i;
+ unsigned long addr;
+
+ if (!arch_has_strict_perms())
+ return;
+
+ for (i = 0; i < n; i++) {
+ if (!IS_ALIGNED(perms[i].start, SECTION_SIZE) ||
+ !IS_ALIGNED(perms[i].end, SECTION_SIZE)) {
+ pr_err("BUG: section %lx-%lx not aligned to %lx\n",
+ perms[i].start, perms[i].end,
+ SECTION_SIZE);
+ continue;
+ }
+
+ for (addr = perms[i].start;
+ addr < perms[i].end;
+ addr += SECTION_SIZE)
+ section_update(addr, perms[i].mask,
+ set ? perms[i].prot : perms[i].clear, mm);
+ }
+
}
-static inline void fix_kernmem_perms(void)
+static void update_sections_early(struct section_perm perms[], int n)
{
- set_section_perms(nx_perms, prot);
+ struct task_struct *t, *s;
+
+ read_lock(&tasklist_lock);
+ for_each_process(t) {
+ if (t->flags & PF_KTHREAD)
+ continue;
+ for_each_thread(t, s)
+ set_section_perms(perms, n, true, s->mm);
+ }
+ read_unlock(&tasklist_lock);
+ set_section_perms(perms, n, true, current->active_mm);
+ set_section_perms(perms, n, true, &init_mm);
+}
+
+int __fix_kernmem_perms(void *unused)
+{
+ update_sections_early(nx_perms, ARRAY_SIZE(nx_perms));
+ return 0;
+}
+
+void fix_kernmem_perms(void)
+{
+ stop_machine(__fix_kernmem_perms, NULL, NULL);
}
#ifdef CONFIG_DEBUG_RODATA
+int __mark_rodata_ro(void *unused)
+{
+ update_sections_early(ro_perms, ARRAY_SIZE(ro_perms));
+ return 0;
+}
+
void mark_rodata_ro(void)
{
- set_section_perms(ro_perms, prot);
+ stop_machine(__mark_rodata_ro, NULL, NULL);
}
void set_kernel_text_rw(void)
{
- set_section_perms(ro_perms, clear);
+ set_section_perms(ro_perms, ARRAY_SIZE(ro_perms), false,
+ current->active_mm);
}
void set_kernel_text_ro(void)
{
- set_section_perms(ro_perms, prot);
+ set_section_perms(ro_perms, ARRAY_SIZE(ro_perms), true,
+ current->active_mm);
}
#endif /* CONFIG_DEBUG_RODATA */
diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c
index 0c81056c1dd7..66a978d05958 100644
--- a/arch/arm/mm/ioremap.c
+++ b/arch/arm/mm/ioremap.c
@@ -30,6 +30,7 @@
#include <asm/cp15.h>
#include <asm/cputype.h>
#include <asm/cacheflush.h>
+#include <asm/early_ioremap.h>
#include <asm/mmu_context.h>
#include <asm/pgalloc.h>
#include <asm/tlbflush.h>
@@ -469,3 +470,11 @@ int pci_ioremap_io(unsigned int offset, phys_addr_t phys_addr)
}
EXPORT_SYMBOL_GPL(pci_ioremap_io);
#endif
+
+/*
+ * Must be called after early_fixmap_init
+ */
+void __init early_ioremap_init(void)
+{
+ early_ioremap_setup();
+}
diff --git a/arch/arm/mm/mmap.c b/arch/arm/mm/mmap.c
index 407dc786583a..4b4058db0781 100644
--- a/arch/arm/mm/mmap.c
+++ b/arch/arm/mm/mmap.c
@@ -173,8 +173,7 @@ unsigned long arch_mmap_rnd(void)
{
unsigned long rnd;
- /* 8 bits of randomness in 20 address space bits */
- rnd = (unsigned long)get_random_int() % (1 << 8);
+ rnd = (unsigned long)get_random_int() & ((1 << mmap_rnd_bits) - 1);
return rnd << PAGE_SHIFT;
}
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index 4867f5daf82c..a87f6cc3fa2b 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -390,7 +390,7 @@ void __init early_fixmap_init(void)
* The early fixmap range spans multiple pmds, for which
* we are not prepared:
*/
- BUILD_BUG_ON((__fix_to_virt(__end_of_permanent_fixed_addresses) >> PMD_SHIFT)
+ BUILD_BUG_ON((__fix_to_virt(__end_of_early_ioremap_region) >> PMD_SHIFT)
!= FIXADDR_TOP >> PMD_SHIFT);
pmd = fixmap_pmd(FIXADDR_TOP);
@@ -572,7 +572,7 @@ static void __init build_mem_type_table(void)
* in the Short-descriptor translation table format descriptors.
*/
if (cpu_arch == CPU_ARCH_ARMv7 &&
- (read_cpuid_ext(CPUID_EXT_MMFR0) & 0xF) == 4) {
+ (read_cpuid_ext(CPUID_EXT_MMFR0) & 0xF) >= 4) {
user_pmd_table |= PMD_PXNTABLE;
}
#endif
@@ -724,30 +724,49 @@ static void __init *early_alloc(unsigned long sz)
return early_alloc_aligned(sz, sz);
}
-static pte_t * __init early_pte_alloc(pmd_t *pmd, unsigned long addr, unsigned long prot)
+static void *__init late_alloc(unsigned long sz)
+{
+ void *ptr = (void *)__get_free_pages(PGALLOC_GFP, get_order(sz));
+
+ BUG_ON(!ptr);
+ return ptr;
+}
+
+static pte_t * __init pte_alloc(pmd_t *pmd, unsigned long addr,
+ unsigned long prot,
+ void *(*alloc)(unsigned long sz))
{
if (pmd_none(*pmd)) {
- pte_t *pte = early_alloc(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE);
+ pte_t *pte = alloc(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE);
__pmd_populate(pmd, __pa(pte), prot);
}
BUG_ON(pmd_bad(*pmd));
return pte_offset_kernel(pmd, addr);
}
+static pte_t * __init early_pte_alloc(pmd_t *pmd, unsigned long addr,
+ unsigned long prot)
+{
+ return pte_alloc(pmd, addr, prot, early_alloc);
+}
+
static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr,
unsigned long end, unsigned long pfn,
- const struct mem_type *type)
+ const struct mem_type *type,
+ void *(*alloc)(unsigned long sz),
+ bool ng)
{
- pte_t *pte = early_pte_alloc(pmd, addr, type->prot_l1);
+ pte_t *pte = pte_alloc(pmd, addr, type->prot_l1, alloc);
do {
- set_pte_ext(pte, pfn_pte(pfn, __pgprot(type->prot_pte)), 0);
+ set_pte_ext(pte, pfn_pte(pfn, __pgprot(type->prot_pte)),
+ ng ? PTE_EXT_NG : 0);
pfn++;
} while (pte++, addr += PAGE_SIZE, addr != end);
}
static void __init __map_init_section(pmd_t *pmd, unsigned long addr,
unsigned long end, phys_addr_t phys,
- const struct mem_type *type)
+ const struct mem_type *type, bool ng)
{
pmd_t *p = pmd;
@@ -765,7 +784,7 @@ static void __init __map_init_section(pmd_t *pmd, unsigned long addr,
pmd++;
#endif
do {
- *pmd = __pmd(phys | type->prot_sect);
+ *pmd = __pmd(phys | type->prot_sect | (ng ? PMD_SECT_nG : 0));
phys += SECTION_SIZE;
} while (pmd++, addr += SECTION_SIZE, addr != end);
@@ -774,7 +793,8 @@ static void __init __map_init_section(pmd_t *pmd, unsigned long addr,
static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
unsigned long end, phys_addr_t phys,
- const struct mem_type *type)
+ const struct mem_type *type,
+ void *(*alloc)(unsigned long sz), bool ng)
{
pmd_t *pmd = pmd_offset(pud, addr);
unsigned long next;
@@ -792,10 +812,10 @@ static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
*/
if (type->prot_sect &&
((addr | next | phys) & ~SECTION_MASK) == 0) {
- __map_init_section(pmd, addr, next, phys, type);
+ __map_init_section(pmd, addr, next, phys, type, ng);
} else {
alloc_init_pte(pmd, addr, next,
- __phys_to_pfn(phys), type);
+ __phys_to_pfn(phys), type, alloc, ng);
}
phys += next - addr;
@@ -805,21 +825,24 @@ static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,
unsigned long end, phys_addr_t phys,
- const struct mem_type *type)
+ const struct mem_type *type,
+ void *(*alloc)(unsigned long sz), bool ng)
{
pud_t *pud = pud_offset(pgd, addr);
unsigned long next;
do {
next = pud_addr_end(addr, end);
- alloc_init_pmd(pud, addr, next, phys, type);
+ alloc_init_pmd(pud, addr, next, phys, type, alloc, ng);
phys += next - addr;
} while (pud++, addr = next, addr != end);
}
#ifndef CONFIG_ARM_LPAE
-static void __init create_36bit_mapping(struct map_desc *md,
- const struct mem_type *type)
+static void __init create_36bit_mapping(struct mm_struct *mm,
+ struct map_desc *md,
+ const struct mem_type *type,
+ bool ng)
{
unsigned long addr, length, end;
phys_addr_t phys;
@@ -859,7 +882,7 @@ static void __init create_36bit_mapping(struct map_desc *md,
*/
phys |= (((md->pfn >> (32 - PAGE_SHIFT)) & 0xF) << 20);
- pgd = pgd_offset_k(addr);
+ pgd = pgd_offset(mm, addr);
end = addr + length;
do {
pud_t *pud = pud_offset(pgd, addr);
@@ -867,7 +890,8 @@ static void __init create_36bit_mapping(struct map_desc *md,
int i;
for (i = 0; i < 16; i++)
- *pmd++ = __pmd(phys | type->prot_sect | PMD_SECT_SUPER);
+ *pmd++ = __pmd(phys | type->prot_sect | PMD_SECT_SUPER |
+ (ng ? PMD_SECT_nG : 0));
addr += SUPERSECTION_SIZE;
phys += SUPERSECTION_SIZE;
@@ -876,33 +900,15 @@ static void __init create_36bit_mapping(struct map_desc *md,
}
#endif /* !CONFIG_ARM_LPAE */
-/*
- * Create the page directory entries and any necessary
- * page tables for the mapping specified by `md'. We
- * are able to cope here with varying sizes and address
- * offsets, and we take full advantage of sections and
- * supersections.
- */
-static void __init create_mapping(struct map_desc *md)
+static void __init __create_mapping(struct mm_struct *mm, struct map_desc *md,
+ void *(*alloc)(unsigned long sz),
+ bool ng)
{
unsigned long addr, length, end;
phys_addr_t phys;
const struct mem_type *type;
pgd_t *pgd;
- if (md->virtual != vectors_base() && md->virtual < TASK_SIZE) {
- pr_warn("BUG: not creating mapping for 0x%08llx at 0x%08lx in user region\n",
- (long long)__pfn_to_phys((u64)md->pfn), md->virtual);
- return;
- }
-
- if ((md->type == MT_DEVICE || md->type == MT_ROM) &&
- md->virtual >= PAGE_OFFSET && md->virtual < FIXADDR_START &&
- (md->virtual < VMALLOC_START || md->virtual >= VMALLOC_END)) {
- pr_warn("BUG: mapping for 0x%08llx at 0x%08lx out of vmalloc space\n",
- (long long)__pfn_to_phys((u64)md->pfn), md->virtual);
- }
-
type = &mem_types[md->type];
#ifndef CONFIG_ARM_LPAE
@@ -910,7 +916,7 @@ static void __init create_mapping(struct map_desc *md)
* Catch 36-bit addresses
*/
if (md->pfn >= 0x100000) {
- create_36bit_mapping(md, type);
+ create_36bit_mapping(mm, md, type, ng);
return;
}
#endif
@@ -925,12 +931,12 @@ static void __init create_mapping(struct map_desc *md)
return;
}
- pgd = pgd_offset_k(addr);
+ pgd = pgd_offset(mm, addr);
end = addr + length;
do {
unsigned long next = pgd_addr_end(addr, end);
- alloc_init_pud(pgd, addr, next, phys, type);
+ alloc_init_pud(pgd, addr, next, phys, type, alloc, ng);
phys += next - addr;
addr = next;
@@ -938,6 +944,43 @@ static void __init create_mapping(struct map_desc *md)
}
/*
+ * Create the page directory entries and any necessary
+ * page tables for the mapping specified by `md'. We
+ * are able to cope here with varying sizes and address
+ * offsets, and we take full advantage of sections and
+ * supersections.
+ */
+static void __init create_mapping(struct map_desc *md)
+{
+ if (md->virtual != vectors_base() && md->virtual < TASK_SIZE) {
+ pr_warn("BUG: not creating mapping for 0x%08llx at 0x%08lx in user region\n",
+ (long long)__pfn_to_phys((u64)md->pfn), md->virtual);
+ return;
+ }
+
+ if ((md->type == MT_DEVICE || md->type == MT_ROM) &&
+ md->virtual >= PAGE_OFFSET && md->virtual < FIXADDR_START &&
+ (md->virtual < VMALLOC_START || md->virtual >= VMALLOC_END)) {
+ pr_warn("BUG: mapping for 0x%08llx at 0x%08lx out of vmalloc space\n",
+ (long long)__pfn_to_phys((u64)md->pfn), md->virtual);
+ }
+
+ __create_mapping(&init_mm, md, early_alloc, false);
+}
+
+void __init create_mapping_late(struct mm_struct *mm, struct map_desc *md,
+ bool ng)
+{
+#ifdef CONFIG_ARM_LPAE
+ pud_t *pud = pud_alloc(mm, pgd_offset(mm, md->virtual), md->virtual);
+ if (WARN_ON(!pud))
+ return;
+ pmd_alloc(mm, pud, 0);
+#endif
+ __create_mapping(mm, md, late_alloc, ng);
+}
+
+/*
* Create the architecture specific mappings
*/
void __init iotable_init(struct map_desc *io_desc, int nr)
@@ -1392,6 +1435,9 @@ static void __init map_lowmem(void)
phys_addr_t end = start + reg->size;
struct map_desc map;
+ if (memblock_is_nomap(reg))
+ continue;
+
if (end > arm_lowmem_limit)
end = arm_lowmem_limit;
if (start >= end)
diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S
index de2b246fed38..0f92d575a304 100644
--- a/arch/arm/mm/proc-v7.S
+++ b/arch/arm/mm/proc-v7.S
@@ -95,7 +95,7 @@ ENDPROC(cpu_v7_dcache_clean_area)
.equ cpu_v7_suspend_size, 4 * 9
#ifdef CONFIG_ARM_CPU_SUSPEND
ENTRY(cpu_v7_do_suspend)
- stmfd sp!, {r4 - r10, lr}
+ stmfd sp!, {r4 - r11, lr}
mrc p15, 0, r4, c13, c0, 0 @ FCSE/PID
mrc p15, 0, r5, c13, c0, 3 @ User r/o thread ID
stmia r0!, {r4 - r5}
@@ -112,7 +112,7 @@ ENTRY(cpu_v7_do_suspend)
mrc p15, 0, r9, c1, c0, 1 @ Auxiliary control register
mrc p15, 0, r10, c1, c0, 2 @ Co-processor access control
stmia r0, {r5 - r11}
- ldmfd sp!, {r4 - r10, pc}
+ ldmfd sp!, {r4 - r11, pc}
ENDPROC(cpu_v7_do_suspend)
ENTRY(cpu_v7_do_resume)
@@ -274,10 +274,12 @@ __v7_ca15mp_setup:
__v7_b15mp_setup:
__v7_ca17mp_setup:
mov r10, #0
-1: adr r12, __v7_setup_stack @ the local stack
- stmia r12, {r0-r5, lr} @ v7_invalidate_l1 touches r0-r6
+1: adr r0, __v7_setup_stack_ptr
+ ldr r12, [r0]
+ add r12, r12, r0 @ the local stack
+ stmia r12, {r1-r6, lr} @ v7_invalidate_l1 touches r0-r6
bl v7_invalidate_l1
- ldmia r12, {r0-r5, lr}
+ ldmia r12, {r1-r6, lr}
#ifdef CONFIG_SMP
ALT_SMP(mrc p15, 0, r0, c1, c0, 1)
ALT_UP(mov r0, #(1 << 6)) @ fake it for UP
@@ -415,10 +417,12 @@ __v7_pj4b_setup:
#endif /* CONFIG_CPU_PJ4B */
__v7_setup:
- adr r12, __v7_setup_stack @ the local stack
- stmia r12, {r0-r5, lr} @ v7_invalidate_l1 touches r0-r6
+ adr r0, __v7_setup_stack_ptr
+ ldr r12, [r0]
+ add r12, r12, r0 @ the local stack
+ stmia r12, {r1-r6, lr} @ v7_invalidate_l1 touches r0-r6
bl v7_invalidate_l1
- ldmia r12, {r0-r5, lr}
+ ldmia r12, {r1-r6, lr}
__v7_setup_cont:
and r0, r9, #0xff000000 @ ARM?
@@ -480,11 +484,16 @@ __errata_finish:
orr r0, r0, r6 @ set them
THUMB( orr r0, r0, #1 << 30 ) @ Thumb exceptions
ret lr @ return to head.S:__ret
+
+ .align 2
+__v7_setup_stack_ptr:
+ .word __v7_setup_stack - .
ENDPROC(__v7_setup)
+ .bss
.align 2
__v7_setup_stack:
- .space 4 * 7 @ 12 registers
+ .space 4 * 7 @ 7 registers
__INITDATA
diff --git a/arch/arm/mm/proc-v7m.S b/arch/arm/mm/proc-v7m.S
index 67d9209077c6..7229d8d0be1a 100644
--- a/arch/arm/mm/proc-v7m.S
+++ b/arch/arm/mm/proc-v7m.S
@@ -12,6 +12,7 @@
*/
#include <linux/linkage.h>
#include <asm/assembler.h>
+#include <asm/memory.h>
#include <asm/v7m.h>
#include "proc-macros.S"
@@ -97,19 +98,19 @@ __v7m_setup:
mov r5, #0x00800000
str r5, [r0, V7M_SCB_SHPR3] @ set PendSV priority
- @ SVC to run the kernel in this mode
+ @ SVC to switch to handler mode. Notice that this requires sp to
+ @ point to writeable memory because the processor saves
+ @ some registers to the stack.
badr r1, 1f
ldr r5, [r12, #11 * 4] @ read the SVC vector entry
str r1, [r12, #11 * 4] @ write the temporary SVC vector entry
mov r6, lr @ save LR
- mov r7, sp @ save SP
- ldr sp, =__v7m_setup_stack_top
+ ldr sp, =init_thread_union + THREAD_START_SP
cpsie i
svc #0
1: cpsid i
str r5, [r12, #11 * 4] @ restore the original SVC vector entry
mov lr, r6 @ restore LR
- mov sp, r7 @ restore SP
@ Special-purpose control register
mov r1, #1
@@ -123,11 +124,6 @@ __v7m_setup:
ret lr
ENDPROC(__v7m_setup)
- .align 2
-__v7m_setup_stack:
- .space 4 * 8 @ 8 registers
-__v7m_setup_stack_top:
-
define_processor_functions v7m, dabort=nommu_early_abort, pabort=legacy_pabort, nommu=1
.section ".rodata"
diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c
index 591f9db3bf40..93d0b6d0b63e 100644
--- a/arch/arm/net/bpf_jit_32.c
+++ b/arch/arm/net/bpf_jit_32.c
@@ -187,19 +187,6 @@ static inline int mem_words_used(struct jit_ctx *ctx)
return fls(ctx->seen & SEEN_MEM);
}
-static inline bool is_load_to_a(u16 inst)
-{
- switch (inst) {
- case BPF_LD | BPF_W | BPF_LEN:
- case BPF_LD | BPF_W | BPF_ABS:
- case BPF_LD | BPF_H | BPF_ABS:
- case BPF_LD | BPF_B | BPF_ABS:
- return true;
- default:
- return false;
- }
-}
-
static void jit_fill_hole(void *area, unsigned int size)
{
u32 *ptr;
@@ -211,7 +198,6 @@ static void jit_fill_hole(void *area, unsigned int size)
static void build_prologue(struct jit_ctx *ctx)
{
u16 reg_set = saved_regs(ctx);
- u16 first_inst = ctx->skf->insns[0].code;
u16 off;
#ifdef CONFIG_FRAME_POINTER
@@ -241,7 +227,7 @@ static void build_prologue(struct jit_ctx *ctx)
emit(ARM_MOV_I(r_X, 0), ctx);
/* do not leak kernel data to userspace */
- if ((first_inst != (BPF_RET | BPF_K)) && !(is_load_to_a(first_inst)))
+ if (bpf_needs_clear_a(&ctx->skf->insns[0]))
emit(ARM_MOV_I(r_A, 0), ctx);
/* stack space for the BPF_MEM words */
@@ -770,7 +756,8 @@ load_ind:
case BPF_ALU | BPF_RSH | BPF_K:
if (unlikely(k > 31))
return -1;
- emit(ARM_LSR_I(r_A, r_A, k), ctx);
+ if (k)
+ emit(ARM_LSR_I(r_A, r_A, k), ctx);
break;
case BPF_ALU | BPF_RSH | BPF_X:
update_on_xread(ctx);
diff --git a/arch/arm/plat-samsung/devs.c b/arch/arm/plat-samsung/devs.c
index 82074625de5c..42f6f9cd844c 100644
--- a/arch/arm/plat-samsung/devs.c
+++ b/arch/arm/plat-samsung/devs.c
@@ -36,7 +36,7 @@
#include <linux/platform_data/s3c-hsotg.h>
#include <linux/platform_data/dma-s3c24xx.h>
-#include <media/s5p_hdmi.h>
+#include <linux/platform_data/media/s5p_hdmi.h>
#include <asm/irq.h>
#include <asm/mach/arch.h>
@@ -1100,9 +1100,7 @@ struct platform_device s3c_device_wdt = {
#ifdef CONFIG_S3C64XX_DEV_SPI0
static struct resource s3c64xx_spi0_resource[] = {
[0] = DEFINE_RES_MEM(S3C_PA_SPI0, SZ_256),
- [1] = DEFINE_RES_DMA(DMACH_SPI0_TX),
- [2] = DEFINE_RES_DMA(DMACH_SPI0_RX),
- [3] = DEFINE_RES_IRQ(IRQ_SPI0),
+ [1] = DEFINE_RES_IRQ(IRQ_SPI0),
};
struct platform_device s3c64xx_device_spi0 = {
@@ -1130,6 +1128,8 @@ void __init s3c64xx_spi0_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
pd.num_cs = num_cs;
pd.src_clk_nr = src_clk_nr;
pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi0_cfg_gpio;
+ pd.dma_tx = (void *)DMACH_SPI0_TX;
+ pd.dma_rx = (void *)DMACH_SPI0_RX;
#if defined(CONFIG_PL330_DMA)
pd.filter = pl330_filter;
#elif defined(CONFIG_S3C64XX_PL080)
@@ -1145,9 +1145,7 @@ void __init s3c64xx_spi0_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
#ifdef CONFIG_S3C64XX_DEV_SPI1
static struct resource s3c64xx_spi1_resource[] = {
[0] = DEFINE_RES_MEM(S3C_PA_SPI1, SZ_256),
- [1] = DEFINE_RES_DMA(DMACH_SPI1_TX),
- [2] = DEFINE_RES_DMA(DMACH_SPI1_RX),
- [3] = DEFINE_RES_IRQ(IRQ_SPI1),
+ [1] = DEFINE_RES_IRQ(IRQ_SPI1),
};
struct platform_device s3c64xx_device_spi1 = {
@@ -1175,12 +1173,15 @@ void __init s3c64xx_spi1_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
pd.num_cs = num_cs;
pd.src_clk_nr = src_clk_nr;
pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi1_cfg_gpio;
+ pd.dma_tx = (void *)DMACH_SPI1_TX;
+ pd.dma_rx = (void *)DMACH_SPI1_RX;
#if defined(CONFIG_PL330_DMA)
pd.filter = pl330_filter;
#elif defined(CONFIG_S3C64XX_PL080)
pd.filter = pl08x_filter_id;
#endif
+
s3c_set_platdata(&pd, sizeof(pd), &s3c64xx_device_spi1);
}
#endif /* CONFIG_S3C64XX_DEV_SPI1 */
@@ -1188,9 +1189,7 @@ void __init s3c64xx_spi1_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
#ifdef CONFIG_S3C64XX_DEV_SPI2
static struct resource s3c64xx_spi2_resource[] = {
[0] = DEFINE_RES_MEM(S3C_PA_SPI2, SZ_256),
- [1] = DEFINE_RES_DMA(DMACH_SPI2_TX),
- [2] = DEFINE_RES_DMA(DMACH_SPI2_RX),
- [3] = DEFINE_RES_IRQ(IRQ_SPI2),
+ [1] = DEFINE_RES_IRQ(IRQ_SPI2),
};
struct platform_device s3c64xx_device_spi2 = {
@@ -1218,6 +1217,8 @@ void __init s3c64xx_spi2_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
pd.num_cs = num_cs;
pd.src_clk_nr = src_clk_nr;
pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi2_cfg_gpio;
+ pd.dma_tx = (void *)DMACH_SPI2_TX;
+ pd.dma_rx = (void *)DMACH_SPI2_RX;
#if defined(CONFIG_PL330_DMA)
pd.filter = pl330_filter;
#elif defined(CONFIG_S3C64XX_PL080)
diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c
index fc7ea529f462..75cd7345c654 100644
--- a/arch/arm/xen/enlighten.c
+++ b/arch/arm/xen/enlighten.c
@@ -12,6 +12,7 @@
#include <xen/page.h>
#include <xen/interface/sched.h>
#include <xen/xen-ops.h>
+#include <asm/paravirt.h>
#include <asm/xen/hypervisor.h>
#include <asm/xen/hypercall.h>
#include <asm/system_misc.h>
@@ -25,6 +26,10 @@
#include <linux/cpufreq.h>
#include <linux/cpu.h>
#include <linux/console.h>
+#include <linux/pvclock_gtod.h>
+#include <linux/time64.h>
+#include <linux/timekeeping.h>
+#include <linux/timekeeper_internal.h>
#include <linux/mm.h>
@@ -79,6 +84,83 @@ int xen_unmap_domain_gfn_range(struct vm_area_struct *vma,
}
EXPORT_SYMBOL_GPL(xen_unmap_domain_gfn_range);
+static unsigned long long xen_stolen_accounting(int cpu)
+{
+ struct vcpu_runstate_info state;
+
+ BUG_ON(cpu != smp_processor_id());
+
+ xen_get_runstate_snapshot(&state);
+
+ WARN_ON(state.state != RUNSTATE_running);
+
+ return state.time[RUNSTATE_runnable] + state.time[RUNSTATE_offline];
+}
+
+static void xen_read_wallclock(struct timespec64 *ts)
+{
+ u32 version;
+ struct timespec64 now, ts_monotonic;
+ struct shared_info *s = HYPERVISOR_shared_info;
+ struct pvclock_wall_clock *wall_clock = &(s->wc);
+
+ /* get wallclock at system boot */
+ do {
+ version = wall_clock->version;
+ rmb(); /* fetch version before time */
+ now.tv_sec = ((uint64_t)wall_clock->sec_hi << 32) | wall_clock->sec;
+ now.tv_nsec = wall_clock->nsec;
+ rmb(); /* fetch time before checking version */
+ } while ((wall_clock->version & 1) || (version != wall_clock->version));
+
+ /* time since system boot */
+ ktime_get_ts64(&ts_monotonic);
+ *ts = timespec64_add(now, ts_monotonic);
+}
+
+static int xen_pvclock_gtod_notify(struct notifier_block *nb,
+ unsigned long was_set, void *priv)
+{
+ /* Protected by the calling core code serialization */
+ static struct timespec64 next_sync;
+
+ struct xen_platform_op op;
+ struct timespec64 now, system_time;
+ struct timekeeper *tk = priv;
+
+ now.tv_sec = tk->xtime_sec;
+ now.tv_nsec = (long)(tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift);
+ system_time = timespec64_add(now, tk->wall_to_monotonic);
+
+ /*
+ * We only take the expensive HV call when the clock was set
+ * or when the 11 minutes RTC synchronization time elapsed.
+ */
+ if (!was_set && timespec64_compare(&now, &next_sync) < 0)
+ return NOTIFY_OK;
+
+ op.cmd = XENPF_settime64;
+ op.u.settime64.mbz = 0;
+ op.u.settime64.secs = now.tv_sec;
+ op.u.settime64.nsecs = now.tv_nsec;
+ op.u.settime64.system_time = timespec64_to_ns(&system_time);
+ (void)HYPERVISOR_platform_op(&op);
+
+ /*
+ * Move the next drift compensation time 11 minutes
+ * ahead. That's emulating the sync_cmos_clock() update for
+ * the hardware RTC.
+ */
+ next_sync = now;
+ next_sync.tv_sec += 11 * 60;
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block xen_pvclock_gtod_notifier = {
+ .notifier_call = xen_pvclock_gtod_notify,
+};
+
static void xen_percpu_init(void)
{
struct vcpu_register_vcpu_info info;
@@ -104,6 +186,8 @@ static void xen_percpu_init(void)
BUG_ON(err);
per_cpu(xen_vcpu, cpu) = vcpup;
+ xen_setup_runstate_info(cpu);
+
after_register_vcpu_info:
enable_percpu_irq(xen_events_irq, 0);
put_cpu();
@@ -271,6 +355,11 @@ static int __init xen_guest_init(void)
register_cpu_notifier(&xen_cpu_notifier);
+ pv_time_ops.steal_clock = xen_stolen_accounting;
+ static_key_slow_inc(&paravirt_steal_enabled);
+ if (xen_initial_domain())
+ pvclock_gtod_register_notifier(&xen_pvclock_gtod_notifier);
+
return 0;
}
early_initcall(xen_guest_init);
@@ -282,6 +371,11 @@ static int __init xen_pm_init(void)
pm_power_off = xen_power_off;
arm_pm_restart = xen_restart;
+ if (!xen_initial_domain()) {
+ struct timespec64 ts;
+ xen_read_wallclock(&ts);
+ do_settimeofday64(&ts);
+ }
return 0;
}
@@ -307,5 +401,6 @@ EXPORT_SYMBOL_GPL(HYPERVISOR_memory_op);
EXPORT_SYMBOL_GPL(HYPERVISOR_physdev_op);
EXPORT_SYMBOL_GPL(HYPERVISOR_vcpu_op);
EXPORT_SYMBOL_GPL(HYPERVISOR_tmem_op);
+EXPORT_SYMBOL_GPL(HYPERVISOR_platform_op);
EXPORT_SYMBOL_GPL(HYPERVISOR_multicall);
EXPORT_SYMBOL_GPL(privcmd_call);
diff --git a/arch/arm/xen/hypercall.S b/arch/arm/xen/hypercall.S
index 10fd99c568c6..9a36f4f49c10 100644
--- a/arch/arm/xen/hypercall.S
+++ b/arch/arm/xen/hypercall.S
@@ -89,6 +89,7 @@ HYPERCALL2(memory_op);
HYPERCALL2(physdev_op);
HYPERCALL3(vcpu_op);
HYPERCALL1(tmem_op);
+HYPERCALL1(platform_op_raw);
HYPERCALL2(multicall);
ENTRY(privcmd_call)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 9ac16a482ff1..6be3fa2310ee 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -3,6 +3,7 @@ config ARM64
select ACPI_CCA_REQUIRED if ACPI
select ACPI_GENERIC_GSI if ACPI
select ACPI_REDUCED_HARDWARE_ONLY if ACPI
+ select ARCH_HAS_DEVMEM_IS_ALLOWED
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
select ARCH_HAS_ELF_RANDOMIZE
select ARCH_HAS_GCOV_PROFILE_ALL
@@ -49,8 +50,10 @@ config ARM64
select HAVE_ARCH_AUDITSYSCALL
select HAVE_ARCH_BITREVERSE
select HAVE_ARCH_JUMP_LABEL
- select HAVE_ARCH_KASAN if SPARSEMEM_VMEMMAP
+ select HAVE_ARCH_KASAN if SPARSEMEM_VMEMMAP && !(ARM64_16K_PAGES && ARM64_VA_BITS_48)
select HAVE_ARCH_KGDB
+ select HAVE_ARCH_MMAP_RND_BITS
+ select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT
select HAVE_ARCH_SECCOMP_FILTER
select HAVE_ARCH_TRACEHOOK
select HAVE_BPF_JIT
@@ -70,6 +73,7 @@ config ARM64
select HAVE_FUNCTION_GRAPH_TRACER
select HAVE_GENERIC_DMA_COHERENT
select HAVE_HW_BREAKPOINT if PERF_EVENTS
+ select HAVE_IRQ_TIME_ACCOUNTING
select HAVE_MEMBLOCK
select HAVE_PATA_PLATFORM
select HAVE_PERF_EVENTS
@@ -92,6 +96,7 @@ config ARM64
select SPARSE_IRQ
select SYSCTL_EXCEPTION_TRACE
select HAVE_CONTEXT_TRACKING
+ select HAVE_ARM_SMCCC
help
ARM 64-bit (AArch64) Linux support.
@@ -104,6 +109,33 @@ config ARCH_PHYS_ADDR_T_64BIT
config MMU
def_bool y
+config ARCH_MMAP_RND_BITS_MIN
+ default 14 if ARM64_64K_PAGES
+ default 16 if ARM64_16K_PAGES
+ default 18
+
+# max bits determined by the following formula:
+# VA_BITS - PAGE_SHIFT - 3
+config ARCH_MMAP_RND_BITS_MAX
+ default 19 if ARM64_VA_BITS=36
+ default 24 if ARM64_VA_BITS=39
+ default 27 if ARM64_VA_BITS=42
+ default 30 if ARM64_VA_BITS=47
+ default 29 if ARM64_VA_BITS=48 && ARM64_64K_PAGES
+ default 31 if ARM64_VA_BITS=48 && ARM64_16K_PAGES
+ default 33 if ARM64_VA_BITS=48
+ default 14 if ARM64_64K_PAGES
+ default 16 if ARM64_16K_PAGES
+ default 18
+
+config ARCH_MMAP_RND_COMPAT_BITS_MIN
+ default 7 if ARM64_64K_PAGES
+ default 9 if ARM64_16K_PAGES
+ default 11
+
+config ARCH_MMAP_RND_COMPAT_BITS_MAX
+ default 16
+
config NO_IOPORT_MAP
def_bool y if !PCI
@@ -316,6 +348,27 @@ config ARM64_ERRATUM_832075
If unsure, say Y.
+config ARM64_ERRATUM_834220
+ bool "Cortex-A57: 834220: Stage 2 translation fault might be incorrectly reported in presence of a Stage 1 fault"
+ depends on KVM
+ default y
+ help
+ This option adds an alternative code sequence to work around ARM
+ erratum 834220 on Cortex-A57 parts up to r1p2.
+
+ Affected Cortex-A57 parts might report a Stage 2 translation
+ fault as the result of a Stage 1 fault for load crossing a
+ page boundary when there is a permission or device memory
+ alignment fault at Stage 1 and a translation fault at Stage 2.
+
+ The workaround is to verify that the Stage 1 translation
+ doesn't generate a fault before handling the Stage 2 fault.
+ Please note that this does not necessarily enable the workaround,
+ as it depends on the alternative framework, which will only patch
+ the kernel if an affected CPU is detected.
+
+ If unsure, say Y.
+
config ARM64_ERRATUM_845719
bool "Cortex-A53: 845719: a load might read incorrect data"
depends on COMPAT
@@ -508,9 +561,6 @@ config HW_PERF_EVENTS
config SYS_SUPPORTS_HUGETLBFS
def_bool y
-config ARCH_WANT_GENERAL_HUGETLB
- def_bool y
-
config ARCH_WANT_HUGE_PMD_SHARE
def_bool y if ARM64_4K_PAGES || (ARM64_16K_PAGES && !ARM64_VA_BITS_36)
@@ -535,6 +585,25 @@ config SECCOMP
and the task is only allowed to execute a few safe syscalls
defined by each seccomp mode.
+config PARAVIRT
+ bool "Enable paravirtualization code"
+ help
+ This changes the kernel so it can modify itself when it is run
+ under a hypervisor, potentially improving performance significantly
+ over full virtualization.
+
+config PARAVIRT_TIME_ACCOUNTING
+ bool "Paravirtual steal time accounting"
+ select PARAVIRT
+ default n
+ help
+ Select this option to enable fine granularity task steal time
+ accounting. Time spent executing other tasks in parallel with
+ the current vCPU is discounted from the vCPU power. To account for
+ that, there can be a small performance impact.
+
+ If in doubt, say N here.
+
config XEN_DOM0
def_bool y
depends on XEN
@@ -543,6 +612,7 @@ config XEN
bool "Xen guest support on ARM64"
depends on ARM64 && OF
select SWIOTLB_XEN
+ select PARAVIRT
help
Say Y if you want to run Linux in a Virtual Machine on Xen on ARM64.
diff --git a/arch/arm64/Kconfig.debug b/arch/arm64/Kconfig.debug
index 04fb73b973f1..e13c4bf84d9e 100644
--- a/arch/arm64/Kconfig.debug
+++ b/arch/arm64/Kconfig.debug
@@ -14,20 +14,6 @@ config ARM64_PTDUMP
kernel.
If in doubt, say "N"
-config STRICT_DEVMEM
- bool "Filter access to /dev/mem"
- depends on MMU
- help
- If this option is disabled, you allow userspace (root) access to all
- of memory, including kernel and userspace memory. Accidental
- access to this is obviously disastrous, but specific access can
- be used by people debugging the kernel.
-
- If this option is switched on, the /dev/mem file only allows
- userspace access to memory mapped peripherals.
-
- If in doubt, say Y.
-
config PID_IN_CONTEXTIDR
bool "Write the current PID to the CONTEXTIDR register"
help
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls2080a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls2080a.dtsi
index e81cd48d6245..925552e7b4f3 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls2080a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls2080a.dtsi
@@ -269,6 +269,7 @@
clock-frequency = <0>; /* Updated by bootloader */
voltage-ranges = <1800 1800 3300 3300>;
sdhci,auto-cmd12;
+ little-endian;
bus-width = <4>;
};
@@ -277,6 +278,7 @@
reg = <0x0 0x2300000 0x0 0x10000>;
interrupts = <0 36 0x4>; /* Level high type */
gpio-controller;
+ little-endian;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
@@ -287,6 +289,7 @@
reg = <0x0 0x2310000 0x0 0x10000>;
interrupts = <0 36 0x4>; /* Level high type */
gpio-controller;
+ little-endian;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
@@ -297,6 +300,7 @@
reg = <0x0 0x2320000 0x0 0x10000>;
interrupts = <0 37 0x4>; /* Level high type */
gpio-controller;
+ little-endian;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
@@ -307,6 +311,7 @@
reg = <0x0 0x2330000 0x0 0x10000>;
interrupts = <0 37 0x4>; /* Level high type */
gpio-controller;
+ little-endian;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
diff --git a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
index 811cb760ba49..9b1482a1d007 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
+++ b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
@@ -13,6 +13,7 @@
*/
/dts-v1/;
+#include <dt-bindings/gpio/gpio.h>
#include "mt8173.dtsi"
/ {
@@ -32,6 +33,15 @@
};
chosen { };
+
+ usb_p1_vbus: regulator@0 {
+ compatible = "regulator-fixed";
+ regulator-name = "usb_vbus";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&pio 130 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ };
};
&i2c1 {
@@ -408,3 +418,9 @@
&uart0 {
status = "okay";
};
+
+&usb30 {
+ vusb33-supply = <&mt6397_vusb_reg>;
+ vbus-supply = <&usb_p1_vbus>;
+ mediatek,wakeup-src = <1>;
+};
diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
index 4dd5f93d0303..c1fd2757f8e4 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
@@ -14,6 +14,7 @@
#include <dt-bindings/clock/mt8173-clk.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/phy/phy.h>
#include <dt-bindings/power/mt8173-power.h>
#include <dt-bindings/reset-controller/mt8173-resets.h>
#include "mt8173-pinfunc.h"
@@ -510,6 +511,47 @@
status = "disabled";
};
+ usb30: usb@11270000 {
+ compatible = "mediatek,mt8173-xhci";
+ reg = <0 0x11270000 0 0x1000>,
+ <0 0x11280700 0 0x0100>;
+ interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_LOW>;
+ power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
+ clocks = <&topckgen CLK_TOP_USB30_SEL>,
+ <&pericfg CLK_PERI_USB0>,
+ <&pericfg CLK_PERI_USB1>;
+ clock-names = "sys_ck",
+ "wakeup_deb_p0",
+ "wakeup_deb_p1";
+ phys = <&phy_port0 PHY_TYPE_USB3>,
+ <&phy_port1 PHY_TYPE_USB2>;
+ mediatek,syscon-wakeup = <&pericfg>;
+ status = "okay";
+ };
+
+ u3phy: usb-phy@11290000 {
+ compatible = "mediatek,mt8173-u3phy";
+ reg = <0 0x11290000 0 0x800>;
+ clocks = <&apmixedsys CLK_APMIXED_REF2USB_TX>;
+ clock-names = "u3phya_ref";
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+ status = "okay";
+
+ phy_port0: port@11290800 {
+ reg = <0 0x11290800 0 0x800>;
+ #phy-cells = <1>;
+ status = "okay";
+ };
+
+ phy_port1: port@11291000 {
+ reg = <0 0x11291000 0 0x800>;
+ #phy-cells = <1>;
+ status = "okay";
+ };
+ };
+
mmsys: clock-controller@14000000 {
compatible = "mediatek,mt8173-mmsys", "syscon";
reg = <0 0x14000000 0 0x1000>;
diff --git a/arch/arm64/include/asm/alternative.h b/arch/arm64/include/asm/alternative.h
index d56ec0715157..e4962f04201e 100644
--- a/arch/arm64/include/asm/alternative.h
+++ b/arch/arm64/include/asm/alternative.h
@@ -19,7 +19,6 @@ struct alt_instr {
void __init apply_alternatives_all(void);
void apply_alternatives(void *start, size_t length);
-void free_alternatives_memory(void);
#define ALTINSTR_ENTRY(feature) \
" .word 661b - .\n" /* label */ \
diff --git a/arch/arm64/include/asm/arch_gicv3.h b/arch/arm64/include/asm/arch_gicv3.h
index 030cdcb46c6b..2731d3b25ed2 100644
--- a/arch/arm64/include/asm/arch_gicv3.h
+++ b/arch/arm64/include/asm/arch_gicv3.h
@@ -77,6 +77,7 @@
#ifndef __ASSEMBLY__
#include <linux/stringify.h>
+#include <asm/barrier.h>
/*
* Low-level accessors
diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h
index 12eff928ef8b..bb7b72734c24 100644
--- a/arch/arm64/include/asm/assembler.h
+++ b/arch/arm64/include/asm/assembler.h
@@ -193,6 +193,17 @@ lr .req x30 // link register
str \src, [\tmp, :lo12:\sym]
.endm
+ /*
+ * @sym: The name of the per-cpu variable
+ * @reg: Result of per_cpu(sym, smp_processor_id())
+ * @tmp: scratch register
+ */
+ .macro this_cpu_ptr, sym, reg, tmp
+ adr_l \reg, \sym
+ mrs \tmp, tpidr_el1
+ add \reg, \reg, \tmp
+ .endm
+
/*
* Annotate a function as position independent, i.e., safe to be called before
* the kernel virtual mapping is activated.
diff --git a/arch/arm64/include/asm/cacheflush.h b/arch/arm64/include/asm/cacheflush.h
index 54efedaf331f..7fc294c3bc5b 100644
--- a/arch/arm64/include/asm/cacheflush.h
+++ b/arch/arm64/include/asm/cacheflush.h
@@ -68,6 +68,7 @@
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);
+extern void __clean_dcache_area_pou(void *addr, size_t len);
extern long __flush_cache_user_range(unsigned long start, unsigned long end);
static inline void flush_cache_mm(struct mm_struct *mm)
diff --git a/arch/arm64/include/asm/cmpxchg.h b/arch/arm64/include/asm/cmpxchg.h
index 9ea611ea69df..510c7b404454 100644
--- a/arch/arm64/include/asm/cmpxchg.h
+++ b/arch/arm64/include/asm/cmpxchg.h
@@ -19,7 +19,6 @@
#define __ASM_CMPXCHG_H
#include <linux/bug.h>
-#include <linux/mmdebug.h>
#include <asm/atomic.h>
#include <asm/barrier.h>
diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index 11d5bb0fdd54..8f271b83f910 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -29,8 +29,9 @@
#define ARM64_HAS_PAN 4
#define ARM64_HAS_LSE_ATOMICS 5
#define ARM64_WORKAROUND_CAVIUM_23154 6
+#define ARM64_WORKAROUND_834220 7
-#define ARM64_NCAPS 7
+#define ARM64_NCAPS 8
#ifndef __ASSEMBLY__
@@ -46,8 +47,12 @@ enum ftr_type {
#define FTR_STRICT true /* SANITY check strict matching required */
#define FTR_NONSTRICT false /* SANITY check ignored */
+#define FTR_SIGNED true /* Value should be treated as signed */
+#define FTR_UNSIGNED false /* Value should be treated as unsigned */
+
struct arm64_ftr_bits {
- bool strict; /* CPU Sanity check: strict matching required ? */
+ bool sign; /* Value is signed ? */
+ bool strict; /* CPU Sanity check: strict matching required ? */
enum ftr_type type;
u8 shift;
u8 width;
@@ -123,6 +128,18 @@ cpuid_feature_extract_field(u64 features, int field)
return cpuid_feature_extract_field_width(features, field, 4);
}
+static inline unsigned int __attribute_const__
+cpuid_feature_extract_unsigned_field_width(u64 features, int field, int width)
+{
+ return (u64)(features << (64 - width - field)) >> (64 - width);
+}
+
+static inline unsigned int __attribute_const__
+cpuid_feature_extract_unsigned_field(u64 features, int field)
+{
+ return cpuid_feature_extract_unsigned_field_width(features, field, 4);
+}
+
static inline u64 arm64_ftr_mask(struct arm64_ftr_bits *ftrp)
{
return (u64)GENMASK(ftrp->shift + ftrp->width - 1, ftrp->shift);
@@ -130,7 +147,9 @@ static inline u64 arm64_ftr_mask(struct arm64_ftr_bits *ftrp)
static inline s64 arm64_ftr_value(struct arm64_ftr_bits *ftrp, u64 val)
{
- return cpuid_feature_extract_field_width(val, ftrp->shift, ftrp->width);
+ return ftrp->sign ?
+ cpuid_feature_extract_field_width(val, ftrp->shift, ftrp->width) :
+ cpuid_feature_extract_unsigned_field_width(val, ftrp->shift, ftrp->width);
}
static inline bool id_aa64mmfr0_mixed_endian_el0(u64 mmfr0)
diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h
index ef572206f1c3..8e88a696c9cb 100644
--- a/arch/arm64/include/asm/efi.h
+++ b/arch/arm64/include/asm/efi.h
@@ -2,7 +2,9 @@
#define _ASM_EFI_H
#include <asm/io.h>
+#include <asm/mmu_context.h>
#include <asm/neon.h>
+#include <asm/tlbflush.h>
#ifdef CONFIG_EFI
extern void efi_init(void);
@@ -10,6 +12,8 @@ extern void efi_init(void);
#define efi_init()
#endif
+int efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md);
+
#define efi_call_virt(f, ...) \
({ \
efi_##f##_t *__f; \
@@ -63,6 +67,11 @@ extern void efi_init(void);
* Services are enabled and the EFI_RUNTIME_SERVICES bit set.
*/
+static inline void efi_set_pgd(struct mm_struct *mm)
+{
+ switch_mm(NULL, mm, NULL);
+}
+
void efi_virtmap_load(void);
void efi_virtmap_unload(void);
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h
index c5534facf941..3c60f37e48ab 100644
--- a/arch/arm64/include/asm/ftrace.h
+++ b/arch/arm64/include/asm/ftrace.h
@@ -28,6 +28,8 @@ struct dyn_arch_ftrace {
extern unsigned long ftrace_graph_call;
+extern void return_to_handler(void);
+
static inline unsigned long ftrace_call_adjust(unsigned long addr)
{
/*
diff --git a/arch/arm64/include/asm/hugetlb.h b/arch/arm64/include/asm/hugetlb.h
index bb4052e85dba..bbc1e35aa601 100644
--- a/arch/arm64/include/asm/hugetlb.h
+++ b/arch/arm64/include/asm/hugetlb.h
@@ -26,36 +26,7 @@ static inline pte_t huge_ptep_get(pte_t *ptep)
return *ptep;
}
-static inline void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, pte_t pte)
-{
- set_pte_at(mm, addr, ptep, pte);
-}
-
-static inline void huge_ptep_clear_flush(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep)
-{
- ptep_clear_flush(vma, addr, ptep);
-}
-
-static inline void huge_ptep_set_wrprotect(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep)
-{
- ptep_set_wrprotect(mm, addr, ptep);
-}
-static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep)
-{
- return ptep_get_and_clear(mm, addr, ptep);
-}
-
-static inline int huge_ptep_set_access_flags(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep,
- pte_t pte, int dirty)
-{
- return ptep_set_access_flags(vma, addr, ptep, pte, dirty);
-}
static inline void hugetlb_free_pgd_range(struct mmu_gather *tlb,
unsigned long addr, unsigned long end,
@@ -97,4 +68,19 @@ static inline void arch_clear_hugepage_flags(struct page *page)
clear_bit(PG_dcache_clean, &page->flags);
}
+extern pte_t arch_make_huge_pte(pte_t entry, struct vm_area_struct *vma,
+ struct page *page, int writable);
+#define arch_make_huge_pte arch_make_huge_pte
+extern void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
+ pte_t *ptep, pte_t pte);
+extern int huge_ptep_set_access_flags(struct vm_area_struct *vma,
+ unsigned long addr, pte_t *ptep,
+ pte_t pte, int dirty);
+extern pte_t huge_ptep_get_and_clear(struct mm_struct *mm,
+ unsigned long addr, pte_t *ptep);
+extern void huge_ptep_set_wrprotect(struct mm_struct *mm,
+ unsigned long addr, pte_t *ptep);
+extern void huge_ptep_clear_flush(struct vm_area_struct *vma,
+ unsigned long addr, pte_t *ptep);
+
#endif /* __ASM_HUGETLB_H */
diff --git a/arch/arm64/include/asm/hw_breakpoint.h b/arch/arm64/include/asm/hw_breakpoint.h
index e54415ec6935..9732908bfc8a 100644
--- a/arch/arm64/include/asm/hw_breakpoint.h
+++ b/arch/arm64/include/asm/hw_breakpoint.h
@@ -138,16 +138,18 @@ extern struct pmu perf_ops_bp;
/* Determine number of BRP registers available. */
static inline int get_num_brps(void)
{
+ u64 dfr0 = read_system_reg(SYS_ID_AA64DFR0_EL1);
return 1 +
- cpuid_feature_extract_field(read_system_reg(SYS_ID_AA64DFR0_EL1),
+ cpuid_feature_extract_unsigned_field(dfr0,
ID_AA64DFR0_BRPS_SHIFT);
}
/* Determine number of WRP registers available. */
static inline int get_num_wrps(void)
{
+ u64 dfr0 = read_system_reg(SYS_ID_AA64DFR0_EL1);
return 1 +
- cpuid_feature_extract_field(read_system_reg(SYS_ID_AA64DFR0_EL1),
+ cpuid_feature_extract_unsigned_field(dfr0,
ID_AA64DFR0_WRPS_SHIFT);
}
diff --git a/arch/arm64/include/asm/irq.h b/arch/arm64/include/asm/irq.h
index 23eb450b820b..b77197d941fc 100644
--- a/arch/arm64/include/asm/irq.h
+++ b/arch/arm64/include/asm/irq.h
@@ -1,10 +1,60 @@
#ifndef __ASM_IRQ_H
#define __ASM_IRQ_H
+#define IRQ_STACK_SIZE THREAD_SIZE
+#define IRQ_STACK_START_SP THREAD_START_SP
+
+#ifndef __ASSEMBLER__
+
+#include <linux/percpu.h>
+
#include <asm-generic/irq.h>
+#include <asm/thread_info.h>
struct pt_regs;
+DECLARE_PER_CPU(unsigned long [IRQ_STACK_SIZE/sizeof(long)], irq_stack);
+
+/*
+ * The highest address on the stack, and the first to be used. Used to
+ * find the dummy-stack frame put down by el?_irq() in entry.S, which
+ * is structured as follows:
+ *
+ * ------------
+ * | | <- irq_stack_ptr
+ * top ------------
+ * | x19 | <- irq_stack_ptr - 0x08
+ * ------------
+ * | x29 | <- irq_stack_ptr - 0x10
+ * ------------
+ *
+ * where x19 holds a copy of the task stack pointer where the struct pt_regs
+ * from kernel_entry can be found.
+ *
+ */
+#define IRQ_STACK_PTR(cpu) ((unsigned long)per_cpu(irq_stack, cpu) + IRQ_STACK_START_SP)
+
+/*
+ * The offset from irq_stack_ptr where entry.S will store the original
+ * stack pointer. Used by unwind_frame() and dump_backtrace().
+ */
+#define IRQ_STACK_TO_TASK_STACK(ptr) (*((unsigned long *)((ptr) - 0x08)))
+
extern void set_handle_irq(void (*handle_irq)(struct pt_regs *));
+static inline int nr_legacy_irqs(void)
+{
+ return 0;
+}
+
+static inline bool on_irq_stack(unsigned long sp, int cpu)
+{
+ /* variable names the same as kernel/stacktrace.c */
+ unsigned long low = (unsigned long)per_cpu(irq_stack, cpu);
+ unsigned long high = low + IRQ_STACK_START_SP;
+
+ return (low <= sp && sp <= high);
+}
+
+#endif /* !__ASSEMBLER__ */
#endif
diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
index 5e6857b6bdc4..738a95f93e49 100644
--- a/arch/arm64/include/asm/kvm_arm.h
+++ b/arch/arm64/include/asm/kvm_arm.h
@@ -125,6 +125,7 @@
#define VTCR_EL2_SL0_LVL1 (1 << 6)
#define VTCR_EL2_T0SZ_MASK 0x3f
#define VTCR_EL2_T0SZ_40B 24
+#define VTCR_EL2_VS 19
/*
* We configure the Stage-2 page tables to always restrict the IPA space to be
@@ -169,7 +170,7 @@
#define VTTBR_BADDR_SHIFT (VTTBR_X - 1)
#define VTTBR_BADDR_MASK (((UL(1) << (PHYS_MASK_SHIFT - VTTBR_X)) - 1) << VTTBR_BADDR_SHIFT)
#define VTTBR_VMID_SHIFT (UL(48))
-#define VTTBR_VMID_MASK (UL(0xFF) << VTTBR_VMID_SHIFT)
+#define VTTBR_VMID_MASK(size) (_AT(u64, (1 << size) - 1) << VTTBR_VMID_SHIFT)
/* Hyp System Trap Register */
#define HSTR_EL2_T(x) (1 << x)
diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
index 5e377101f919..52b777b7d407 100644
--- a/arch/arm64/include/asm/kvm_asm.h
+++ b/arch/arm64/include/asm/kvm_asm.h
@@ -20,82 +20,6 @@
#include <asm/virt.h>
-/*
- * 0 is reserved as an invalid value.
- * Order *must* be kept in sync with the hyp switch code.
- */
-#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 /* 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 */
-#define TCR_EL1 8 /* Translation Control Register */
-#define ESR_EL1 9 /* Exception Syndrome Register */
-#define AFSR0_EL1 10 /* Auxilary Fault Status Register 0 */
-#define AFSR1_EL1 11 /* Auxilary Fault Status Register 1 */
-#define FAR_EL1 12 /* Fault Address Register */
-#define MAIR_EL1 13 /* Memory Attribute Indirection Register */
-#define VBAR_EL1 14 /* Vector Base Address Register */
-#define CONTEXTIDR_EL1 15 /* Context ID Register */
-#define TPIDR_EL0 16 /* Thread ID, User R/W */
-#define TPIDRRO_EL0 17 /* Thread ID, User R/O */
-#define TPIDR_EL1 18 /* Thread ID, Privileged */
-#define AMAIR_EL1 19 /* Aux Memory Attribute Indirection Register */
-#define CNTKCTL_EL1 20 /* Timer Control Register (EL1) */
-#define PAR_EL1 21 /* Physical Address Register */
-#define MDSCR_EL1 22 /* Monitor Debug System Control Register */
-#define MDCCINT_EL1 23 /* Monitor Debug Comms Channel Interrupt Enable Reg */
-
-/* 32bit specific registers. Keep them at the end of the range */
-#define DACR32_EL2 24 /* Domain Access Control Register */
-#define IFSR32_EL2 25 /* Instruction Fault Status Register */
-#define FPEXC32_EL2 26 /* Floating-Point Exception Control Register */
-#define DBGVCR32_EL2 27 /* Debug Vector Catch Register */
-#define NR_SYS_REGS 28
-
-/* 32bit mapping */
-#define c0_MPIDR (MPIDR_EL1 * 2) /* MultiProcessor ID Register */
-#define c0_CSSELR (CSSELR_EL1 * 2)/* Cache Size Selection Register */
-#define c1_SCTLR (SCTLR_EL1 * 2) /* System Control Register */
-#define c1_ACTLR (ACTLR_EL1 * 2) /* Auxiliary Control Register */
-#define c1_CPACR (CPACR_EL1 * 2) /* Coprocessor Access Control */
-#define c2_TTBR0 (TTBR0_EL1 * 2) /* Translation Table Base Register 0 */
-#define c2_TTBR0_high (c2_TTBR0 + 1) /* TTBR0 top 32 bits */
-#define c2_TTBR1 (TTBR1_EL1 * 2) /* Translation Table Base Register 1 */
-#define c2_TTBR1_high (c2_TTBR1 + 1) /* TTBR1 top 32 bits */
-#define c2_TTBCR (TCR_EL1 * 2) /* Translation Table Base Control R. */
-#define c3_DACR (DACR32_EL2 * 2)/* Domain Access Control Register */
-#define c5_DFSR (ESR_EL1 * 2) /* Data Fault Status Register */
-#define c5_IFSR (IFSR32_EL2 * 2)/* Instruction Fault Status Register */
-#define c5_ADFSR (AFSR0_EL1 * 2) /* Auxiliary Data Fault Status R */
-#define c5_AIFSR (AFSR1_EL1 * 2) /* Auxiliary Instr Fault Status R */
-#define c6_DFAR (FAR_EL1 * 2) /* Data Fault Address Register */
-#define c6_IFAR (c6_DFAR + 1) /* Instruction Fault Address Register */
-#define c7_PAR (PAR_EL1 * 2) /* Physical Address Register */
-#define c7_PAR_high (c7_PAR + 1) /* PAR top 32 bits */
-#define c10_PRRR (MAIR_EL1 * 2) /* Primary Region Remap Register */
-#define c10_NMRR (c10_PRRR + 1) /* Normal Memory Remap Register */
-#define c12_VBAR (VBAR_EL1 * 2) /* Vector Base Address Register */
-#define c13_CID (CONTEXTIDR_EL1 * 2) /* Context ID Register */
-#define c13_TID_URW (TPIDR_EL0 * 2) /* Thread ID, User R/W */
-#define c13_TID_URO (TPIDRRO_EL0 * 2)/* Thread ID, User R/O */
-#define c13_TID_PRIV (TPIDR_EL1 * 2) /* Thread ID, Privileged */
-#define c10_AMAIR0 (AMAIR_EL1 * 2) /* Aux Memory Attr Indirection Reg */
-#define c10_AMAIR1 (c10_AMAIR0 + 1)/* Aux Memory Attr Indirection Reg */
-#define c14_CNTKCTL (CNTKCTL_EL1 * 2) /* Timer Control Register (PL1) */
-
-#define cp14_DBGDSCRext (MDSCR_EL1 * 2)
-#define cp14_DBGBCR0 (DBGBCR0_EL1 * 2)
-#define cp14_DBGBVR0 (DBGBVR0_EL1 * 2)
-#define cp14_DBGBXVR0 (cp14_DBGBVR0 + 1)
-#define cp14_DBGWCR0 (DBGWCR0_EL1 * 2)
-#define cp14_DBGWVR0 (DBGWVR0_EL1 * 2)
-#define cp14_DBGDCCINT (MDCCINT_EL1 * 2)
-
-#define NR_COPRO_REGS (NR_SYS_REGS * 2)
-
#define ARM_EXCEPTION_IRQ 0
#define ARM_EXCEPTION_TRAP 1
diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
index 17e92f05b1fe..3066328cd86b 100644
--- a/arch/arm64/include/asm/kvm_emulate.h
+++ b/arch/arm64/include/asm/kvm_emulate.h
@@ -26,7 +26,6 @@
#include <asm/esr.h>
#include <asm/kvm_arm.h>
-#include <asm/kvm_asm.h>
#include <asm/kvm_mmio.h>
#include <asm/ptrace.h>
#include <asm/cputype.h>
@@ -99,12 +98,22 @@ static inline void vcpu_set_thumb(struct kvm_vcpu *vcpu)
*vcpu_cpsr(vcpu) |= COMPAT_PSR_T_BIT;
}
-static inline unsigned long *vcpu_reg(const struct kvm_vcpu *vcpu, u8 reg_num)
+/*
+ * vcpu_get_reg and vcpu_set_reg should always be passed a register number
+ * coming from a read of ESR_EL2. Otherwise, it may give the wrong result on
+ * AArch32 with banked registers.
+ */
+static inline unsigned long vcpu_get_reg(const struct kvm_vcpu *vcpu,
+ u8 reg_num)
{
- if (vcpu_mode_is_32bit(vcpu))
- return vcpu_reg32(vcpu, reg_num);
+ return (reg_num == 31) ? 0 : vcpu_gp_regs(vcpu)->regs.regs[reg_num];
+}
- return (unsigned long *)&vcpu_gp_regs(vcpu)->regs.regs[reg_num];
+static inline void vcpu_set_reg(struct kvm_vcpu *vcpu, u8 reg_num,
+ unsigned long val)
+{
+ if (reg_num != 31)
+ vcpu_gp_regs(vcpu)->regs.regs[reg_num] = val;
}
/* Get vcpu SPSR for current mode */
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index a35ce7266aac..689d4c95e12f 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -25,7 +25,6 @@
#include <linux/types.h>
#include <linux/kvm_types.h>
#include <asm/kvm.h>
-#include <asm/kvm_asm.h>
#include <asm/kvm_mmio.h>
#define __KVM_HAVE_ARCH_INTC_INITIALIZED
@@ -85,6 +84,86 @@ struct kvm_vcpu_fault_info {
u64 hpfar_el2; /* Hyp IPA Fault Address Register */
};
+/*
+ * 0 is reserved as an invalid value.
+ * Order should be kept in sync with the save/restore code.
+ */
+enum vcpu_sysreg {
+ __INVALID_SYSREG__,
+ MPIDR_EL1, /* MultiProcessor Affinity Register */
+ CSSELR_EL1, /* Cache Size Selection Register */
+ SCTLR_EL1, /* System Control Register */
+ ACTLR_EL1, /* Auxiliary Control Register */
+ CPACR_EL1, /* Coprocessor Access Control */
+ TTBR0_EL1, /* Translation Table Base Register 0 */
+ TTBR1_EL1, /* Translation Table Base Register 1 */
+ TCR_EL1, /* Translation Control Register */
+ ESR_EL1, /* Exception Syndrome Register */
+ AFSR0_EL1, /* Auxilary Fault Status Register 0 */
+ AFSR1_EL1, /* Auxilary Fault Status Register 1 */
+ FAR_EL1, /* Fault Address Register */
+ MAIR_EL1, /* Memory Attribute Indirection Register */
+ VBAR_EL1, /* Vector Base Address Register */
+ CONTEXTIDR_EL1, /* Context ID Register */
+ TPIDR_EL0, /* Thread ID, User R/W */
+ TPIDRRO_EL0, /* Thread ID, User R/O */
+ TPIDR_EL1, /* Thread ID, Privileged */
+ AMAIR_EL1, /* Aux Memory Attribute Indirection Register */
+ CNTKCTL_EL1, /* Timer Control Register (EL1) */
+ PAR_EL1, /* Physical Address Register */
+ MDSCR_EL1, /* Monitor Debug System Control Register */
+ MDCCINT_EL1, /* Monitor Debug Comms Channel Interrupt Enable Reg */
+
+ /* 32bit specific registers. Keep them at the end of the range */
+ DACR32_EL2, /* Domain Access Control Register */
+ IFSR32_EL2, /* Instruction Fault Status Register */
+ FPEXC32_EL2, /* Floating-Point Exception Control Register */
+ DBGVCR32_EL2, /* Debug Vector Catch Register */
+
+ NR_SYS_REGS /* Nothing after this line! */
+};
+
+/* 32bit mapping */
+#define c0_MPIDR (MPIDR_EL1 * 2) /* MultiProcessor ID Register */
+#define c0_CSSELR (CSSELR_EL1 * 2)/* Cache Size Selection Register */
+#define c1_SCTLR (SCTLR_EL1 * 2) /* System Control Register */
+#define c1_ACTLR (ACTLR_EL1 * 2) /* Auxiliary Control Register */
+#define c1_CPACR (CPACR_EL1 * 2) /* Coprocessor Access Control */
+#define c2_TTBR0 (TTBR0_EL1 * 2) /* Translation Table Base Register 0 */
+#define c2_TTBR0_high (c2_TTBR0 + 1) /* TTBR0 top 32 bits */
+#define c2_TTBR1 (TTBR1_EL1 * 2) /* Translation Table Base Register 1 */
+#define c2_TTBR1_high (c2_TTBR1 + 1) /* TTBR1 top 32 bits */
+#define c2_TTBCR (TCR_EL1 * 2) /* Translation Table Base Control R. */
+#define c3_DACR (DACR32_EL2 * 2)/* Domain Access Control Register */
+#define c5_DFSR (ESR_EL1 * 2) /* Data Fault Status Register */
+#define c5_IFSR (IFSR32_EL2 * 2)/* Instruction Fault Status Register */
+#define c5_ADFSR (AFSR0_EL1 * 2) /* Auxiliary Data Fault Status R */
+#define c5_AIFSR (AFSR1_EL1 * 2) /* Auxiliary Instr Fault Status R */
+#define c6_DFAR (FAR_EL1 * 2) /* Data Fault Address Register */
+#define c6_IFAR (c6_DFAR + 1) /* Instruction Fault Address Register */
+#define c7_PAR (PAR_EL1 * 2) /* Physical Address Register */
+#define c7_PAR_high (c7_PAR + 1) /* PAR top 32 bits */
+#define c10_PRRR (MAIR_EL1 * 2) /* Primary Region Remap Register */
+#define c10_NMRR (c10_PRRR + 1) /* Normal Memory Remap Register */
+#define c12_VBAR (VBAR_EL1 * 2) /* Vector Base Address Register */
+#define c13_CID (CONTEXTIDR_EL1 * 2) /* Context ID Register */
+#define c13_TID_URW (TPIDR_EL0 * 2) /* Thread ID, User R/W */
+#define c13_TID_URO (TPIDRRO_EL0 * 2)/* Thread ID, User R/O */
+#define c13_TID_PRIV (TPIDR_EL1 * 2) /* Thread ID, Privileged */
+#define c10_AMAIR0 (AMAIR_EL1 * 2) /* Aux Memory Attr Indirection Reg */
+#define c10_AMAIR1 (c10_AMAIR0 + 1)/* Aux Memory Attr Indirection Reg */
+#define c14_CNTKCTL (CNTKCTL_EL1 * 2) /* Timer Control Register (PL1) */
+
+#define cp14_DBGDSCRext (MDSCR_EL1 * 2)
+#define cp14_DBGBCR0 (DBGBCR0_EL1 * 2)
+#define cp14_DBGBVR0 (DBGBVR0_EL1 * 2)
+#define cp14_DBGBXVR0 (cp14_DBGBVR0 + 1)
+#define cp14_DBGWCR0 (DBGWCR0_EL1 * 2)
+#define cp14_DBGWVR0 (DBGWVR0_EL1 * 2)
+#define cp14_DBGDCCINT (MDCCINT_EL1 * 2)
+
+#define NR_COPRO_REGS (NR_SYS_REGS * 2)
+
struct kvm_cpu_context {
struct kvm_regs gp_regs;
union {
@@ -197,6 +276,12 @@ struct kvm_vcpu_stat {
u32 halt_successful_poll;
u32 halt_attempted_poll;
u32 halt_wakeup;
+ u32 hvc_exit_stat;
+ u64 wfe_exit_stat;
+ u64 wfi_exit_stat;
+ u64 mmio_exit_user;
+ u64 mmio_exit_kernel;
+ u64 exits;
};
int kvm_vcpu_preferred_target(struct kvm_vcpu_init *init);
diff --git a/arch/arm64/include/asm/kvm_mmio.h b/arch/arm64/include/asm/kvm_mmio.h
index 889c908ee631..fe612a962576 100644
--- a/arch/arm64/include/asm/kvm_mmio.h
+++ b/arch/arm64/include/asm/kvm_mmio.h
@@ -19,7 +19,6 @@
#define __ARM64_KVM_MMIO_H__
#include <linux/kvm_host.h>
-#include <asm/kvm_asm.h>
#include <asm/kvm_arm.h>
/*
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
index 61505676d085..0bf8b4320a91 100644
--- a/arch/arm64/include/asm/kvm_mmu.h
+++ b/arch/arm64/include/asm/kvm_mmu.h
@@ -20,6 +20,7 @@
#include <asm/page.h>
#include <asm/memory.h>
+#include <asm/cpufeature.h>
/*
* As we only have the TTBR0_EL2 register, we cannot express
@@ -158,7 +159,6 @@ static inline bool kvm_s2pmd_readonly(pmd_t *pmd)
#define PTRS_PER_S2_PGD_SHIFT (KVM_PHYS_SHIFT - PGDIR_SHIFT)
#endif
#define PTRS_PER_S2_PGD (1 << PTRS_PER_S2_PGD_SHIFT)
-#define S2_PGD_ORDER get_order(PTRS_PER_S2_PGD * sizeof(pgd_t))
#define kvm_pgd_index(addr) (((addr) >> PGDIR_SHIFT) & (PTRS_PER_S2_PGD - 1))
@@ -302,5 +302,12 @@ static inline void __kvm_extend_hypmap(pgd_t *boot_hyp_pgd,
merged_hyp_pgd[idmap_idx] = __pgd(__pa(boot_hyp_pgd) | PMD_TYPE_TABLE);
}
+static inline unsigned int kvm_get_vmid_bits(void)
+{
+ int reg = read_system_reg(SYS_ID_AA64MMFR1_EL1);
+
+ return (cpuid_feature_extract_field(reg, ID_AA64MMFR1_VMIDBITS_SHIFT) == 2) ? 16 : 8;
+}
+
#endif /* __ASSEMBLY__ */
#endif /* __ARM64_KVM_MMU_H__ */
diff --git a/arch/arm64/include/asm/paravirt.h b/arch/arm64/include/asm/paravirt.h
new file mode 100644
index 000000000000..fd5f42886251
--- /dev/null
+++ b/arch/arm64/include/asm/paravirt.h
@@ -0,0 +1,20 @@
+#ifndef _ASM_ARM64_PARAVIRT_H
+#define _ASM_ARM64_PARAVIRT_H
+
+#ifdef CONFIG_PARAVIRT
+struct static_key;
+extern struct static_key paravirt_steal_enabled;
+extern struct static_key paravirt_steal_rq_enabled;
+
+struct pv_time_ops {
+ unsigned long long (*steal_clock)(int cpu);
+};
+extern struct pv_time_ops pv_time_ops;
+
+static inline u64 paravirt_steal_clock(int cpu)
+{
+ return pv_time_ops.steal_clock(cpu);
+}
+#endif
+
+#endif
diff --git a/arch/arm64/include/asm/pgtable-hwdef.h b/arch/arm64/include/asm/pgtable-hwdef.h
index d6739e836f7b..5c25b831273d 100644
--- a/arch/arm64/include/asm/pgtable-hwdef.h
+++ b/arch/arm64/include/asm/pgtable-hwdef.h
@@ -90,7 +90,23 @@
/*
* Contiguous page definitions.
*/
-#define CONT_PTES (_AC(1, UL) << CONT_SHIFT)
+#ifdef CONFIG_ARM64_64K_PAGES
+#define CONT_PTE_SHIFT 5
+#define CONT_PMD_SHIFT 5
+#elif defined(CONFIG_ARM64_16K_PAGES)
+#define CONT_PTE_SHIFT 7
+#define CONT_PMD_SHIFT 5
+#else
+#define CONT_PTE_SHIFT 4
+#define CONT_PMD_SHIFT 4
+#endif
+
+#define CONT_PTES (1 << CONT_PTE_SHIFT)
+#define CONT_PTE_SIZE (CONT_PTES * PAGE_SIZE)
+#define CONT_PTE_MASK (~(CONT_PTE_SIZE - 1))
+#define CONT_PMDS (1 << CONT_PMD_SHIFT)
+#define CONT_PMD_SIZE (CONT_PMDS * PMD_SIZE)
+#define CONT_PMD_MASK (~(CONT_PMD_SIZE - 1))
/* the the numerical offset of the PTE within a range of CONT_PTES */
#define CONT_RANGE_OFFSET(addr) (((addr)>>PAGE_SHIFT)&(CONT_PTES-1))
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index 7e074f93f383..69d2e2f86bce 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -167,6 +167,16 @@ extern struct page *empty_zero_page;
((pte_val(pte) & (PTE_VALID | PTE_USER)) == (PTE_VALID | PTE_USER))
#define pte_valid_not_user(pte) \
((pte_val(pte) & (PTE_VALID | PTE_USER)) == PTE_VALID)
+#define pte_valid_young(pte) \
+ ((pte_val(pte) & (PTE_VALID | PTE_AF)) == (PTE_VALID | PTE_AF))
+
+/*
+ * Could the pte be present in the TLB? We must check mm_tlb_flush_pending
+ * so that we don't erroneously return false for pages that have been
+ * remapped as PROT_NONE but are yet to be flushed from the TLB.
+ */
+#define pte_accessible(mm, pte) \
+ (mm_tlb_flush_pending(mm) ? pte_present(pte) : pte_valid_young(pte))
static inline pte_t clear_pte_bit(pte_t pte, pgprot_t prot)
{
@@ -217,7 +227,8 @@ static inline pte_t pte_mkspecial(pte_t pte)
static inline pte_t pte_mkcont(pte_t pte)
{
- return set_pte_bit(pte, __pgprot(PTE_CONT));
+ pte = set_pte_bit(pte, __pgprot(PTE_CONT));
+ return set_pte_bit(pte, __pgprot(PTE_TYPE_PAGE));
}
static inline pte_t pte_mknoncont(pte_t pte)
@@ -225,6 +236,11 @@ static inline pte_t pte_mknoncont(pte_t pte)
return clear_pte_bit(pte, __pgprot(PTE_CONT));
}
+static inline pmd_t pmd_mkcont(pmd_t pmd)
+{
+ return __pmd(pmd_val(pmd) | PMD_SECT_CONT);
+}
+
static inline void set_pte(pte_t *ptep, pte_t pte)
{
*ptep = pte;
@@ -276,10 +292,14 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
* hardware updates of the pte (ptep_set_access_flags safely changes
* valid ptes without going through an invalid entry).
*/
- if (IS_ENABLED(CONFIG_DEBUG_VM) && IS_ENABLED(CONFIG_ARM64_HW_AFDBM) &&
- pte_valid(*ptep)) {
- BUG_ON(!pte_young(pte));
- BUG_ON(pte_write(*ptep) && !pte_dirty(pte));
+ if (IS_ENABLED(CONFIG_ARM64_HW_AFDBM) &&
+ pte_valid(*ptep) && pte_valid(pte)) {
+ VM_WARN_ONCE(!pte_young(pte),
+ "%s: racy access flag clearing: 0x%016llx -> 0x%016llx",
+ __func__, pte_val(*ptep), pte_val(pte));
+ VM_WARN_ONCE(pte_write(*ptep) && !pte_dirty(pte),
+ "%s: racy dirty state clearing: 0x%016llx -> 0x%016llx",
+ __func__, pte_val(*ptep), pte_val(pte));
}
set_pte(ptep, pte);
@@ -294,7 +314,7 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
/*
* Hugetlb definitions.
*/
-#define HUGE_MAX_HSTATE 2
+#define HUGE_MAX_HSTATE 4
#define HPAGE_SHIFT PMD_SHIFT
#define HPAGE_SIZE (_AC(1, UL) << HPAGE_SHIFT)
#define HPAGE_MASK (~(HPAGE_SIZE - 1))
@@ -660,7 +680,8 @@ extern int kern_addr_valid(unsigned long addr);
#include <asm-generic/pgtable.h>
-#define pgtable_cache_init() do { } while (0)
+void pgd_cache_init(void);
+#define pgtable_cache_init pgd_cache_init
/*
* On AArch64, the cache coherency is handled via the set_pte_at() function.
diff --git a/arch/arm64/include/asm/shmparam.h b/arch/arm64/include/asm/shmparam.h
index 4df608a8459e..e368a55ebd22 100644
--- a/arch/arm64/include/asm/shmparam.h
+++ b/arch/arm64/include/asm/shmparam.h
@@ -21,7 +21,7 @@
* alignment value. Since we don't have aliasing D-caches, the rest of
* the time we can safely use PAGE_SIZE.
*/
-#define COMPAT_SHMLBA 0x4000
+#define COMPAT_SHMLBA (4 * PAGE_SIZE)
#include <asm-generic/shmparam.h>
diff --git a/arch/arm64/include/asm/spinlock.h b/arch/arm64/include/asm/spinlock.h
index c85e96d174a5..fc9682bfe002 100644
--- a/arch/arm64/include/asm/spinlock.h
+++ b/arch/arm64/include/asm/spinlock.h
@@ -26,9 +26,28 @@
* The memory barriers are implicit with the load-acquire and store-release
* instructions.
*/
+static inline void arch_spin_unlock_wait(arch_spinlock_t *lock)
+{
+ unsigned int tmp;
+ arch_spinlock_t lockval;
-#define arch_spin_unlock_wait(lock) \
- do { while (arch_spin_is_locked(lock)) cpu_relax(); } while (0)
+ asm volatile(
+" sevl\n"
+"1: wfe\n"
+"2: ldaxr %w0, %2\n"
+" eor %w1, %w0, %w0, ror #16\n"
+" cbnz %w1, 1b\n"
+ ARM64_LSE_ATOMIC_INSN(
+ /* LL/SC */
+" stxr %w1, %w0, %2\n"
+" cbnz %w1, 2b\n", /* Serialise against any concurrent lockers */
+ /* LSE atomics */
+" nop\n"
+" nop\n")
+ : "=&r" (lockval), "=&r" (tmp), "+Q" (*lock)
+ :
+ : "memory");
+}
#define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)
diff --git a/arch/arm64/include/asm/stacktrace.h b/arch/arm64/include/asm/stacktrace.h
index 7318f6d54aa9..801a16dbbdf6 100644
--- a/arch/arm64/include/asm/stacktrace.h
+++ b/arch/arm64/include/asm/stacktrace.h
@@ -16,14 +16,19 @@
#ifndef __ASM_STACKTRACE_H
#define __ASM_STACKTRACE_H
+struct task_struct;
+
struct stackframe {
unsigned long fp;
unsigned long sp;
unsigned long pc;
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ unsigned int graph;
+#endif
};
-extern int unwind_frame(struct stackframe *frame);
-extern void walk_stackframe(struct stackframe *frame,
+extern int unwind_frame(struct task_struct *tsk, struct stackframe *frame);
+extern void walk_stackframe(struct task_struct *tsk, struct stackframe *frame,
int (*fn)(struct stackframe *, void *), void *data);
#endif /* __ASM_STACKTRACE_H */
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index d48ab5b41f52..4aeebec3d882 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -20,6 +20,8 @@
#ifndef __ASM_SYSREG_H
#define __ASM_SYSREG_H
+#include <linux/stringify.h>
+
#include <asm/opcodes.h>
/*
@@ -208,6 +210,8 @@
#else
+#include <linux/types.h>
+
asm(
" .irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30\n"
" .equ __reg_num_x\\num, \\num\n"
@@ -232,6 +236,23 @@ static inline void config_sctlr_el1(u32 clear, u32 set)
val |= set;
asm volatile("msr sctlr_el1, %0" : : "r" (val));
}
+
+/*
+ * Unlike read_cpuid, calls to read_sysreg are never expected to be
+ * optimized away or replaced with synthetic values.
+ */
+#define read_sysreg(r) ({ \
+ u64 __val; \
+ asm volatile("mrs %0, " __stringify(r) : "=r" (__val)); \
+ __val; \
+})
+
+#define write_sysreg(v, r) do { \
+ u64 __val = (u64)v; \
+ asm volatile("msr " __stringify(r) ", %0" \
+ : : "r" (__val)); \
+} while (0)
+
#endif
#endif /* __ASM_SYSREG_H */
diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h
index 90c7ff233735..abd64bd1f6d9 100644
--- a/arch/arm64/include/asm/thread_info.h
+++ b/arch/arm64/include/asm/thread_info.h
@@ -73,10 +73,16 @@ register unsigned long current_stack_pointer asm ("sp");
*/
static inline struct thread_info *current_thread_info(void) __attribute_const__;
+/*
+ * struct thread_info can be accessed directly via sp_el0.
+ */
static inline struct thread_info *current_thread_info(void)
{
- return (struct thread_info *)
- (current_stack_pointer & ~(THREAD_SIZE - 1));
+ unsigned long sp_el0;
+
+ asm ("mrs %0, sp_el0" : "=r" (sp_el0));
+
+ return (struct thread_info *)sp_el0;
}
#define thread_saved_pc(tsk) \
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 474691f8b13a..83cd7e68e83b 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -14,10 +14,10 @@ CFLAGS_REMOVE_return_address.o = -pg
arm64-obj-y := debug-monitors.o entry.o irq.o fpsimd.o \
entry-fpsimd.o process.o ptrace.o setup.o signal.o \
sys.o stacktrace.o time.o traps.o io.o vdso.o \
- hyp-stub.o psci.o psci-call.o cpu_ops.o insn.o \
+ hyp-stub.o psci.o cpu_ops.o insn.o \
return_address.o cpuinfo.o cpu_errata.o \
cpufeature.o alternative.o cacheinfo.o \
- smp.o smp_spin_table.o topology.o
+ smp.o smp_spin_table.o topology.o smccc-call.o
extra-$(CONFIG_EFI) := efi-entry.o
@@ -41,6 +41,7 @@ arm64-obj-$(CONFIG_EFI) += efi.o efi-entry.stub.o
arm64-obj-$(CONFIG_PCI) += pci.o
arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o
arm64-obj-$(CONFIG_ACPI) += acpi.o
+arm64-obj-$(CONFIG_PARAVIRT) += paravirt.o
obj-y += $(arm64-obj-y) vdso/
obj-m += $(arm64-obj-m)
diff --git a/arch/arm64/kernel/alternative.c b/arch/arm64/kernel/alternative.c
index ab9db0e9818c..d2ee1b21a10d 100644
--- a/arch/arm64/kernel/alternative.c
+++ b/arch/arm64/kernel/alternative.c
@@ -158,9 +158,3 @@ void apply_alternatives(void *start, size_t length)
__apply_alternatives(&region);
}
-
-void free_alternatives_memory(void)
-{
- free_reserved_area(__alt_instructions, __alt_instructions_end,
- 0, "alternatives");
-}
diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c
index 3b6d8cc9dfe0..678f30b05a45 100644
--- a/arch/arm64/kernel/arm64ksyms.c
+++ b/arch/arm64/kernel/arm64ksyms.c
@@ -26,6 +26,7 @@
#include <linux/syscalls.h>
#include <linux/uaccess.h>
#include <linux/io.h>
+#include <linux/arm-smccc.h>
#include <asm/checksum.h>
@@ -68,3 +69,7 @@ EXPORT_SYMBOL(test_and_change_bit);
#ifdef CONFIG_FUNCTION_TRACER
EXPORT_SYMBOL(_mcount);
#endif
+
+ /* arm-smccc */
+EXPORT_SYMBOL(arm_smccc_smc);
+EXPORT_SYMBOL(arm_smccc_hvc);
diff --git a/arch/arm64/kernel/armv8_deprecated.c b/arch/arm64/kernel/armv8_deprecated.c
index 937f5e58a4d3..3e01207917b1 100644
--- a/arch/arm64/kernel/armv8_deprecated.c
+++ b/arch/arm64/kernel/armv8_deprecated.c
@@ -62,7 +62,7 @@ struct insn_emulation {
};
static LIST_HEAD(insn_emulation);
-static int nr_insn_emulated;
+static int nr_insn_emulated __initdata;
static DEFINE_RAW_SPINLOCK(insn_emulation_lock);
static void register_emulation_hooks(struct insn_emulation_ops *ops)
@@ -173,7 +173,7 @@ static int update_insn_emulation_mode(struct insn_emulation *insn,
return ret;
}
-static void register_insn_emulation(struct insn_emulation_ops *ops)
+static void __init register_insn_emulation(struct insn_emulation_ops *ops)
{
unsigned long flags;
struct insn_emulation *insn;
@@ -237,7 +237,7 @@ static struct ctl_table ctl_abi[] = {
{ }
};
-static void register_insn_emulation_sysctl(struct ctl_table *table)
+static void __init register_insn_emulation_sysctl(struct ctl_table *table)
{
unsigned long flags;
int i = 0;
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index 25de8b244961..fffa4ac6c25a 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -28,6 +28,7 @@
#include <asm/suspend.h>
#include <asm/vdso_datapage.h>
#include <linux/kbuild.h>
+#include <linux/arm-smccc.h>
int main(void)
{
@@ -108,49 +109,11 @@ int main(void)
DEFINE(CPU_GP_REGS, offsetof(struct kvm_cpu_context, gp_regs));
DEFINE(CPU_USER_PT_REGS, offsetof(struct kvm_regs, regs));
DEFINE(CPU_FP_REGS, offsetof(struct kvm_regs, fp_regs));
- DEFINE(CPU_SP_EL1, offsetof(struct kvm_regs, sp_el1));
- DEFINE(CPU_ELR_EL1, offsetof(struct kvm_regs, elr_el1));
- DEFINE(CPU_SPSR, offsetof(struct kvm_regs, spsr));
- DEFINE(CPU_SYSREGS, offsetof(struct kvm_cpu_context, sys_regs));
+ DEFINE(VCPU_FPEXC32_EL2, offsetof(struct kvm_vcpu, arch.ctxt.sys_regs[FPEXC32_EL2]));
DEFINE(VCPU_ESR_EL2, offsetof(struct kvm_vcpu, arch.fault.esr_el2));
DEFINE(VCPU_FAR_EL2, offsetof(struct kvm_vcpu, arch.fault.far_el2));
DEFINE(VCPU_HPFAR_EL2, offsetof(struct kvm_vcpu, arch.fault.hpfar_el2));
- DEFINE(VCPU_DEBUG_FLAGS, offsetof(struct kvm_vcpu, arch.debug_flags));
- DEFINE(VCPU_DEBUG_PTR, offsetof(struct kvm_vcpu, arch.debug_ptr));
- DEFINE(DEBUG_BCR, offsetof(struct kvm_guest_debug_arch, dbg_bcr));
- DEFINE(DEBUG_BVR, offsetof(struct kvm_guest_debug_arch, dbg_bvr));
- DEFINE(DEBUG_WCR, offsetof(struct kvm_guest_debug_arch, dbg_wcr));
- DEFINE(DEBUG_WVR, offsetof(struct kvm_guest_debug_arch, dbg_wvr));
- DEFINE(VCPU_HCR_EL2, offsetof(struct kvm_vcpu, arch.hcr_el2));
- DEFINE(VCPU_MDCR_EL2, offsetof(struct kvm_vcpu, arch.mdcr_el2));
- DEFINE(VCPU_IRQ_LINES, offsetof(struct kvm_vcpu, arch.irq_lines));
DEFINE(VCPU_HOST_CONTEXT, offsetof(struct kvm_vcpu, arch.host_cpu_context));
- DEFINE(VCPU_HOST_DEBUG_STATE, offsetof(struct kvm_vcpu, arch.host_debug_state));
- DEFINE(VCPU_TIMER_CNTV_CTL, offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_ctl));
- DEFINE(VCPU_TIMER_CNTV_CVAL, offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_cval));
- DEFINE(KVM_TIMER_CNTVOFF, offsetof(struct kvm, arch.timer.cntvoff));
- DEFINE(KVM_TIMER_ENABLED, offsetof(struct kvm, arch.timer.enabled));
- DEFINE(VCPU_KVM, offsetof(struct kvm_vcpu, kvm));
- DEFINE(VCPU_VGIC_CPU, offsetof(struct kvm_vcpu, arch.vgic_cpu));
- 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));
- DEFINE(VGIC_V2_CPU_EISR, offsetof(struct vgic_cpu, vgic_v2.vgic_eisr));
- DEFINE(VGIC_V2_CPU_ELRSR, offsetof(struct vgic_cpu, vgic_v2.vgic_elrsr));
- DEFINE(VGIC_V2_CPU_APR, offsetof(struct vgic_cpu, vgic_v2.vgic_apr));
- DEFINE(VGIC_V2_CPU_LR, offsetof(struct vgic_cpu, vgic_v2.vgic_lr));
- DEFINE(VGIC_V3_CPU_SRE, offsetof(struct vgic_cpu, vgic_v3.vgic_sre));
- DEFINE(VGIC_V3_CPU_HCR, offsetof(struct vgic_cpu, vgic_v3.vgic_hcr));
- DEFINE(VGIC_V3_CPU_VMCR, offsetof(struct vgic_cpu, vgic_v3.vgic_vmcr));
- DEFINE(VGIC_V3_CPU_MISR, offsetof(struct vgic_cpu, vgic_v3.vgic_misr));
- DEFINE(VGIC_V3_CPU_EISR, offsetof(struct vgic_cpu, vgic_v3.vgic_eisr));
- DEFINE(VGIC_V3_CPU_ELRSR, offsetof(struct vgic_cpu, vgic_v3.vgic_elrsr));
- DEFINE(VGIC_V3_CPU_AP0R, offsetof(struct vgic_cpu, vgic_v3.vgic_ap0r));
- DEFINE(VGIC_V3_CPU_AP1R, offsetof(struct vgic_cpu, vgic_v3.vgic_ap1r));
- DEFINE(VGIC_V3_CPU_LR, offsetof(struct vgic_cpu, vgic_v3.vgic_lr));
- DEFINE(VGIC_CPU_NR_LR, offsetof(struct vgic_cpu, nr_lr));
- DEFINE(KVM_VTTBR, offsetof(struct kvm, arch.vttbr));
- DEFINE(KVM_VGIC_VCTRL, offsetof(struct kvm, arch.vgic.vctrl_base));
#endif
#ifdef CONFIG_CPU_PM
DEFINE(CPU_SUSPEND_SZ, sizeof(struct cpu_suspend_ctx));
@@ -161,5 +124,7 @@ int main(void)
DEFINE(SLEEP_SAVE_SP_PHYS, offsetof(struct sleep_save_sp, save_ptr_stash_phys));
DEFINE(SLEEP_SAVE_SP_VIRT, offsetof(struct sleep_save_sp, save_ptr_stash));
#endif
+ DEFINE(ARM_SMCCC_RES_X0_OFFS, offsetof(struct arm_smccc_res, a0));
+ DEFINE(ARM_SMCCC_RES_X2_OFFS, offsetof(struct arm_smccc_res, a2));
return 0;
}
diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c
index 24926f2504f7..feb6b4efa641 100644
--- a/arch/arm64/kernel/cpu_errata.c
+++ b/arch/arm64/kernel/cpu_errata.c
@@ -75,6 +75,15 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
(1 << MIDR_VARIANT_SHIFT) | 2),
},
#endif
+#ifdef CONFIG_ARM64_ERRATUM_834220
+ {
+ /* Cortex-A57 r0p0 - r1p2 */
+ .desc = "ARM erratum 834220",
+ .capability = ARM64_WORKAROUND_834220,
+ MIDR_RANGE(MIDR_CORTEX_A57, 0x00,
+ (1 << MIDR_VARIANT_SHIFT) | 2),
+ },
+#endif
#ifdef CONFIG_ARM64_ERRATUM_845719
{
/* Cortex-A53 r0p[01234] */
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index c8cf89223b5a..5c90aa490a2b 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -44,8 +44,9 @@ unsigned int compat_elf_hwcap2 __read_mostly;
DECLARE_BITMAP(cpu_hwcaps, ARM64_NCAPS);
-#define ARM64_FTR_BITS(STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL) \
+#define __ARM64_FTR_BITS(SIGNED, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL) \
{ \
+ .sign = SIGNED, \
.strict = STRICT, \
.type = TYPE, \
.shift = SHIFT, \
@@ -53,6 +54,14 @@ DECLARE_BITMAP(cpu_hwcaps, ARM64_NCAPS);
.safe_val = SAFE_VAL, \
}
+/* Define a feature with signed values */
+#define ARM64_FTR_BITS(STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL) \
+ __ARM64_FTR_BITS(FTR_SIGNED, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL)
+
+/* Define a feature with unsigned value */
+#define U_ARM64_FTR_BITS(STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL) \
+ __ARM64_FTR_BITS(FTR_UNSIGNED, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL)
+
#define ARM64_FTR_END \
{ \
.width = 0, \
@@ -99,7 +108,7 @@ static struct arm64_ftr_bits ftr_id_aa64mmfr0[] = {
* Differing PARange is fine as long as all peripherals and memory are mapped
* within the minimum PARange of all CPUs
*/
- ARM64_FTR_BITS(FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64MMFR0_PARANGE_SHIFT, 4, 0),
+ U_ARM64_FTR_BITS(FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64MMFR0_PARANGE_SHIFT, 4, 0),
ARM64_FTR_END,
};
@@ -115,18 +124,18 @@ static struct arm64_ftr_bits ftr_id_aa64mmfr1[] = {
};
static struct arm64_ftr_bits ftr_ctr[] = {
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 31, 1, 1), /* RAO */
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 31, 1, 1), /* RAO */
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 28, 3, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_HIGHER_SAFE, 24, 4, 0), /* CWG */
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 20, 4, 0), /* ERG */
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 16, 4, 1), /* DminLine */
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_HIGHER_SAFE, 24, 4, 0), /* CWG */
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 20, 4, 0), /* ERG */
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 16, 4, 1), /* DminLine */
/*
* Linux can handle differing I-cache policies. Userspace JITs will
* make use of *minLine
*/
- ARM64_FTR_BITS(FTR_NONSTRICT, FTR_EXACT, 14, 2, 0), /* L1Ip */
+ U_ARM64_FTR_BITS(FTR_NONSTRICT, FTR_EXACT, 14, 2, 0), /* L1Ip */
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 4, 10, 0), /* RAZ */
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 0, 4, 0), /* IminLine */
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 0, 4, 0), /* IminLine */
ARM64_FTR_END,
};
@@ -144,12 +153,12 @@ static struct arm64_ftr_bits ftr_id_mmfr0[] = {
static struct arm64_ftr_bits ftr_id_aa64dfr0[] = {
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 32, 32, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_CTX_CMPS_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_WRPS_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_BRPS_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64DFR0_TRACEVER_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_CTX_CMPS_SHIFT, 4, 0),
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_WRPS_SHIFT, 4, 0),
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_BRPS_SHIFT, 4, 0),
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64DFR0_TRACEVER_SHIFT, 4, 0),
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
ARM64_FTR_END,
};
@@ -675,7 +684,7 @@ static const struct arm64_cpu_capabilities arm64_hwcaps[] = {
{},
};
-static void cap_set_hwcap(const struct arm64_cpu_capabilities *cap)
+static void __init cap_set_hwcap(const struct arm64_cpu_capabilities *cap)
{
switch (cap->hwcap_type) {
case CAP_HWCAP:
@@ -720,7 +729,7 @@ static bool __maybe_unused cpus_have_hwcap(const struct arm64_cpu_capabilities *
return rc;
}
-static void setup_cpu_hwcaps(void)
+static void __init setup_cpu_hwcaps(void)
{
int i;
const struct arm64_cpu_capabilities *hwcaps = arm64_hwcaps;
@@ -749,7 +758,8 @@ void update_cpu_capabilities(const struct arm64_cpu_capabilities *caps,
* Run through the enabled capabilities and enable() it on all active
* CPUs
*/
-static void enable_cpu_capabilities(const struct arm64_cpu_capabilities *caps)
+static void __init
+enable_cpu_capabilities(const struct arm64_cpu_capabilities *caps)
{
int i;
@@ -888,7 +898,7 @@ static inline void set_sys_caps_initialised(void)
#endif /* CONFIG_HOTPLUG_CPU */
-static void setup_feature_capabilities(void)
+static void __init setup_feature_capabilities(void)
{
update_cpu_capabilities(arm64_features, "detected feature:");
enable_cpu_capabilities(arm64_features);
diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
index fc5508e0df57..b6abc852f2a1 100644
--- a/arch/arm64/kernel/efi.c
+++ b/arch/arm64/kernel/efi.c
@@ -11,310 +11,34 @@
*
*/
-#include <linux/atomic.h>
#include <linux/dmi.h>
#include <linux/efi.h>
-#include <linux/export.h>
-#include <linux/memblock.h>
-#include <linux/mm_types.h>
-#include <linux/bootmem.h>
-#include <linux/of.h>
-#include <linux/of_fdt.h>
-#include <linux/preempt.h>
-#include <linux/rbtree.h>
-#include <linux/rwsem.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
+#include <linux/init.h>
-#include <asm/cacheflush.h>
#include <asm/efi.h>
-#include <asm/tlbflush.h>
-#include <asm/mmu_context.h>
-#include <asm/mmu.h>
-#include <asm/pgtable.h>
-struct efi_memory_map memmap;
-
-static u64 efi_system_table;
-
-static pgd_t efi_pgd[PTRS_PER_PGD] __page_aligned_bss;
-
-static struct mm_struct efi_mm = {
- .mm_rb = RB_ROOT,
- .pgd = efi_pgd,
- .mm_users = ATOMIC_INIT(2),
- .mm_count = ATOMIC_INIT(1),
- .mmap_sem = __RWSEM_INITIALIZER(efi_mm.mmap_sem),
- .page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock),
- .mmlist = LIST_HEAD_INIT(efi_mm.mmlist),
-};
-
-static int __init is_normal_ram(efi_memory_desc_t *md)
-{
- if (md->attribute & EFI_MEMORY_WB)
- return 1;
- return 0;
-}
-
-/*
- * Translate a EFI virtual address into a physical address: this is necessary,
- * as some data members of the EFI system table are virtually remapped after
- * SetVirtualAddressMap() has been called.
- */
-static phys_addr_t efi_to_phys(unsigned long addr)
+int __init efi_create_mapping(struct mm_struct *mm, efi_memory_desc_t *md)
{
- efi_memory_desc_t *md;
-
- for_each_efi_memory_desc(&memmap, md) {
- if (!(md->attribute & EFI_MEMORY_RUNTIME))
- continue;
- if (md->virt_addr == 0)
- /* no virtual mapping has been installed by the stub */
- break;
- if (md->virt_addr <= addr &&
- (addr - md->virt_addr) < (md->num_pages << EFI_PAGE_SHIFT))
- return md->phys_addr + addr - md->virt_addr;
- }
- return addr;
-}
-
-static int __init uefi_init(void)
-{
- efi_char16_t *c16;
- void *config_tables;
- u64 table_size;
- char vendor[100] = "unknown";
- int i, retval;
-
- efi.systab = early_memremap(efi_system_table,
- sizeof(efi_system_table_t));
- if (efi.systab == NULL) {
- pr_warn("Unable to map EFI system table.\n");
- return -ENOMEM;
- }
-
- set_bit(EFI_BOOT, &efi.flags);
- set_bit(EFI_64BIT, &efi.flags);
+ pteval_t prot_val;
/*
- * Verify the EFI Table
+ * Only regions of type EFI_RUNTIME_SERVICES_CODE need to be
+ * executable, everything else can be mapped with the XN bits
+ * set.
*/
- if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) {
- pr_err("System table signature incorrect\n");
- retval = -EINVAL;
- goto out;
- }
- if ((efi.systab->hdr.revision >> 16) < 2)
- pr_warn("Warning: EFI system table version %d.%02d, expected 2.00 or greater\n",
- efi.systab->hdr.revision >> 16,
- efi.systab->hdr.revision & 0xffff);
-
- /* Show what we know for posterity */
- c16 = early_memremap(efi_to_phys(efi.systab->fw_vendor),
- sizeof(vendor) * sizeof(efi_char16_t));
- if (c16) {
- for (i = 0; i < (int) sizeof(vendor) - 1 && *c16; ++i)
- vendor[i] = c16[i];
- vendor[i] = '\0';
- early_memunmap(c16, sizeof(vendor) * sizeof(efi_char16_t));
- }
-
- pr_info("EFI v%u.%.02u by %s\n",
- efi.systab->hdr.revision >> 16,
- efi.systab->hdr.revision & 0xffff, vendor);
-
- table_size = sizeof(efi_config_table_64_t) * efi.systab->nr_tables;
- config_tables = early_memremap(efi_to_phys(efi.systab->tables),
- table_size);
-
- retval = efi_config_parse_tables(config_tables, efi.systab->nr_tables,
- sizeof(efi_config_table_64_t), NULL);
-
- early_memunmap(config_tables, table_size);
-out:
- early_memunmap(efi.systab, sizeof(efi_system_table_t));
- return retval;
-}
-
-/*
- * Return true for RAM regions we want to permanently reserve.
- */
-static __init int is_reserve_region(efi_memory_desc_t *md)
-{
- switch (md->type) {
- case EFI_LOADER_CODE:
- case EFI_LOADER_DATA:
- case EFI_BOOT_SERVICES_CODE:
- case EFI_BOOT_SERVICES_DATA:
- case EFI_CONVENTIONAL_MEMORY:
- case EFI_PERSISTENT_MEMORY:
- return 0;
- default:
- break;
- }
- return is_normal_ram(md);
-}
-
-static __init void reserve_regions(void)
-{
- efi_memory_desc_t *md;
- u64 paddr, npages, size;
-
- if (efi_enabled(EFI_DBG))
- pr_info("Processing EFI memory map:\n");
-
- for_each_efi_memory_desc(&memmap, md) {
- paddr = md->phys_addr;
- npages = md->num_pages;
-
- if (efi_enabled(EFI_DBG)) {
- char buf[64];
-
- pr_info(" 0x%012llx-0x%012llx %s",
- paddr, paddr + (npages << EFI_PAGE_SHIFT) - 1,
- efi_md_typeattr_format(buf, sizeof(buf), md));
- }
-
- memrange_efi_to_native(&paddr, &npages);
- size = npages << PAGE_SHIFT;
-
- if (is_normal_ram(md))
- early_init_dt_add_memory_arch(paddr, size);
-
- if (is_reserve_region(md)) {
- memblock_reserve(paddr, size);
- if (efi_enabled(EFI_DBG))
- pr_cont("*");
- }
-
- if (efi_enabled(EFI_DBG))
- pr_cont("\n");
- }
-
- set_bit(EFI_MEMMAP, &efi.flags);
-}
-
-void __init efi_init(void)
-{
- struct efi_fdt_params params;
-
- /* Grab UEFI information placed in FDT by stub */
- if (!efi_get_fdt_params(&params))
- return;
-
- efi_system_table = params.system_table;
-
- memblock_reserve(params.mmap & PAGE_MASK,
- PAGE_ALIGN(params.mmap_size + (params.mmap & ~PAGE_MASK)));
- memmap.phys_map = params.mmap;
- memmap.map = early_memremap(params.mmap, params.mmap_size);
- memmap.map_end = memmap.map + params.mmap_size;
- memmap.desc_size = params.desc_size;
- memmap.desc_version = params.desc_ver;
-
- if (uefi_init() < 0)
- return;
-
- reserve_regions();
- early_memunmap(memmap.map, params.mmap_size);
-}
-
-static bool __init efi_virtmap_init(void)
-{
- efi_memory_desc_t *md;
-
- init_new_context(NULL, &efi_mm);
-
- for_each_efi_memory_desc(&memmap, md) {
- u64 paddr, npages, size;
- pgprot_t prot;
-
- if (!(md->attribute & EFI_MEMORY_RUNTIME))
- continue;
- if (md->virt_addr == 0)
- return false;
-
- paddr = md->phys_addr;
- npages = md->num_pages;
- memrange_efi_to_native(&paddr, &npages);
- size = npages << PAGE_SHIFT;
-
- pr_info(" EFI remap 0x%016llx => %p\n",
- md->phys_addr, (void *)md->virt_addr);
-
- /*
- * Only regions of type EFI_RUNTIME_SERVICES_CODE need to be
- * executable, everything else can be mapped with the XN bits
- * set.
- */
- if (!is_normal_ram(md))
- prot = __pgprot(PROT_DEVICE_nGnRE);
- else if (md->type == EFI_RUNTIME_SERVICES_CODE ||
- !PAGE_ALIGNED(md->phys_addr))
- prot = PAGE_KERNEL_EXEC;
- else
- prot = PAGE_KERNEL;
-
- create_pgd_mapping(&efi_mm, paddr, md->virt_addr, size,
- __pgprot(pgprot_val(prot) | PTE_NG));
- }
- return true;
-}
-
-/*
- * Enable the UEFI Runtime Services if all prerequisites are in place, i.e.,
- * non-early mapping of the UEFI system table and virtual mappings for all
- * EFI_MEMORY_RUNTIME regions.
- */
-static int __init arm64_enable_runtime_services(void)
-{
- u64 mapsize;
-
- if (!efi_enabled(EFI_BOOT)) {
- pr_info("EFI services will not be available.\n");
- return -1;
- }
-
- if (efi_runtime_disabled()) {
- pr_info("EFI runtime services will be disabled.\n");
- return -1;
- }
-
- pr_info("Remapping and enabling EFI services.\n");
-
- mapsize = memmap.map_end - memmap.map;
- memmap.map = (__force void *)ioremap_cache(memmap.phys_map,
- mapsize);
- if (!memmap.map) {
- pr_err("Failed to remap EFI memory map\n");
- return -1;
- }
- memmap.map_end = memmap.map + mapsize;
- efi.memmap = &memmap;
-
- efi.systab = (__force void *)ioremap_cache(efi_system_table,
- sizeof(efi_system_table_t));
- if (!efi.systab) {
- pr_err("Failed to remap EFI System Table\n");
- return -1;
- }
- set_bit(EFI_SYSTEM_TABLES, &efi.flags);
-
- if (!efi_virtmap_init()) {
- pr_err("No UEFI virtual mapping was installed -- runtime services will not be available\n");
- return -1;
- }
-
- /* Set up runtime services function pointers */
- efi_native_runtime_setup();
- set_bit(EFI_RUNTIME_SERVICES, &efi.flags);
-
- efi.runtime_version = efi.systab->hdr.revision;
-
+ if ((md->attribute & EFI_MEMORY_WB) == 0)
+ prot_val = PROT_DEVICE_nGnRE;
+ else if (md->type == EFI_RUNTIME_SERVICES_CODE ||
+ !PAGE_ALIGNED(md->phys_addr))
+ prot_val = pgprot_val(PAGE_KERNEL_EXEC);
+ else
+ prot_val = pgprot_val(PAGE_KERNEL);
+
+ create_pgd_mapping(mm, md->phys_addr, md->virt_addr,
+ md->num_pages << EFI_PAGE_SHIFT,
+ __pgprot(prot_val | PTE_NG));
return 0;
}
-early_initcall(arm64_enable_runtime_services);
static int __init arm64_dmi_init(void)
{
@@ -330,23 +54,6 @@ static int __init arm64_dmi_init(void)
}
core_initcall(arm64_dmi_init);
-static void efi_set_pgd(struct mm_struct *mm)
-{
- switch_mm(NULL, mm, NULL);
-}
-
-void efi_virtmap_load(void)
-{
- preempt_disable();
- efi_set_pgd(&efi_mm);
-}
-
-void efi_virtmap_unload(void)
-{
- efi_set_pgd(current->active_mm);
- preempt_enable();
-}
-
/*
* UpdateCapsule() depends on the system being shutdown via
* ResetSystem().
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index 7ed3d75f6304..1f7f5a2b61bf 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -27,6 +27,7 @@
#include <asm/cpufeature.h>
#include <asm/errno.h>
#include <asm/esr.h>
+#include <asm/irq.h>
#include <asm/thread_info.h>
#include <asm/unistd.h>
@@ -88,9 +89,12 @@
.if \el == 0
mrs x21, sp_el0
- get_thread_info tsk // Ensure MDSCR_EL1.SS is clear,
+ mov tsk, sp
+ and tsk, tsk, #~(THREAD_SIZE - 1) // Ensure MDSCR_EL1.SS is clear,
ldr x19, [tsk, #TI_FLAGS] // since we can unmask debug
disable_step_tsk x19, x20 // exceptions when scheduling.
+
+ mov x29, xzr // fp pointed to user-space
.else
add x21, sp, #S_FRAME_SIZE
.endif
@@ -108,6 +112,13 @@
.endif
/*
+ * Set sp_el0 to current thread_info.
+ */
+ .if \el == 0
+ msr sp_el0, tsk
+ .endif
+
+ /*
* Registers that may be useful after this macro is invoked:
*
* x21 - aborted SP
@@ -164,8 +175,44 @@ alternative_endif
.endm
.macro get_thread_info, rd
- mov \rd, sp
- and \rd, \rd, #~(THREAD_SIZE - 1) // top of stack
+ mrs \rd, sp_el0
+ .endm
+
+ .macro irq_stack_entry
+ mov x19, sp // preserve the original sp
+
+ /*
+ * Compare sp with the current thread_info, if the top
+ * ~(THREAD_SIZE - 1) bits match, we are on a task stack, and
+ * should switch to the irq stack.
+ */
+ and x25, x19, #~(THREAD_SIZE - 1)
+ cmp x25, tsk
+ b.ne 9998f
+
+ this_cpu_ptr irq_stack, x25, x26
+ mov x26, #IRQ_STACK_START_SP
+ add x26, x25, x26
+
+ /* switch to the irq stack */
+ mov sp, x26
+
+ /*
+ * Add a dummy stack frame, this non-standard format is fixed up
+ * by unwind_frame()
+ */
+ stp x29, x19, [sp, #-16]!
+ mov x29, sp
+
+9998:
+ .endm
+
+ /*
+ * x19 should be preserved between irq_stack_entry and
+ * irq_stack_exit.
+ */
+ .macro irq_stack_exit
+ mov sp, x19
.endm
/*
@@ -183,10 +230,11 @@ tsk .req x28 // current thread_info
* Interrupt handling.
*/
.macro irq_handler
- adrp x1, handle_arch_irq
- ldr x1, [x1, #:lo12:handle_arch_irq]
+ ldr_l x1, handle_arch_irq
mov x0, sp
+ irq_stack_entry
blr x1
+ irq_stack_exit
.endm
.text
@@ -358,10 +406,10 @@ el1_irq:
bl trace_hardirqs_off
#endif
+ get_thread_info tsk
irq_handler
#ifdef CONFIG_PREEMPT
- get_thread_info tsk
ldr w24, [tsk, #TI_PREEMPT] // get preempt count
cbnz w24, 1f // preempt count != 0
ldr x0, [tsk, #TI_FLAGS] // get flags
@@ -599,6 +647,8 @@ ENTRY(cpu_switch_to)
ldp x29, x9, [x8], #16
ldr lr, [x8]
mov sp, x9
+ and x9, x9, #~(THREAD_SIZE - 1)
+ msr sp_el0, x9
ret
ENDPROC(cpu_switch_to)
@@ -626,14 +676,14 @@ ret_fast_syscall_trace:
work_pending:
tbnz x1, #TIF_NEED_RESCHED, work_resched
/* TIF_SIGPENDING, TIF_NOTIFY_RESUME or TIF_FOREIGN_FPSTATE case */
- ldr x2, [sp, #S_PSTATE]
mov x0, sp // 'regs'
- tst x2, #PSR_MODE_MASK // user mode regs?
- b.ne no_work_pending // returning to kernel
enable_irq // enable interrupts for do_notify_resume()
bl do_notify_resume
b ret_to_user
work_resched:
+#ifdef CONFIG_TRACE_IRQFLAGS
+ bl trace_hardirqs_off // the IRQs are off here, inform the tracing code
+#endif
bl schedule
/*
@@ -645,7 +695,6 @@ ret_to_user:
and x2, x1, #_TIF_WORK_MASK
cbnz x2, work_pending
enable_step_tsk x1, x2
-no_work_pending:
kernel_exit 0
ENDPROC(ret_to_user)
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index 4c46c54a3ad7..acc1afd5c749 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -289,7 +289,7 @@ static struct notifier_block fpsimd_cpu_pm_notifier_block = {
.notifier_call = fpsimd_cpu_pm_notifier,
};
-static void fpsimd_pm_init(void)
+static void __init fpsimd_pm_init(void)
{
cpu_pm_register_notifier(&fpsimd_cpu_pm_notifier_block);
}
diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c
index c851be795080..ebecf9aa33d1 100644
--- a/arch/arm64/kernel/ftrace.c
+++ b/arch/arm64/kernel/ftrace.c
@@ -29,12 +29,11 @@ static int ftrace_modify_code(unsigned long pc, u32 old, u32 new,
/*
* Note:
- * Due to modules and __init, code can disappear and change,
- * we need to protect against faulting as well as code changing.
- * We do this by aarch64_insn_*() which use the probe_kernel_*().
- *
- * No lock is held here because all the modifications are run
- * through stop_machine().
+ * We are paranoid about modifying text, as if a bug were to happen, it
+ * could cause us to read or write to someplace that could cause harm.
+ * Carefully read and modify the code with aarch64_insn_*() which uses
+ * probe_kernel_*(), and make sure what we read is what we expected it
+ * to be before modifying it.
*/
if (validate) {
if (aarch64_insn_read((void *)pc, &replaced))
@@ -93,6 +92,11 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
return ftrace_modify_code(pc, old, new, true);
}
+void arch_ftrace_update_code(int command)
+{
+ ftrace_modify_all_code(command);
+}
+
int __init ftrace_dyn_arch_init(void)
{
return 0;
@@ -125,23 +129,20 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
* on other archs. It's unlikely on AArch64.
*/
old = *parent;
- *parent = return_hooker;
trace.func = self_addr;
trace.depth = current->curr_ret_stack + 1;
/* Only trace if the calling function expects to */
- if (!ftrace_graph_entry(&trace)) {
- *parent = old;
+ if (!ftrace_graph_entry(&trace))
return;
- }
err = ftrace_push_return_trace(old, self_addr, &trace.depth,
frame_pointer);
- if (err == -EBUSY) {
- *parent = old;
+ if (err == -EBUSY)
return;
- }
+ else
+ *parent = return_hooker;
}
#ifdef CONFIG_DYNAMIC_FTRACE
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 23cfc08fc8ba..ffe9c2b6431b 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -415,15 +415,17 @@ ENDPROC(__create_page_tables)
*/
.set initial_sp, init_thread_union + THREAD_START_SP
__mmap_switched:
- adr_l x6, __bss_start
- adr_l x7, __bss_stop
-
-1: cmp x6, x7
- b.hs 2f
- str xzr, [x6], #8 // Clear BSS
- b 1b
-2:
+ // Clear BSS
+ adr_l x0, __bss_start
+ mov x1, xzr
+ adr_l x2, __bss_stop
+ sub x2, x2, x0
+ bl __pi_memset
+
adr_l sp, initial_sp, x4
+ mov x4, sp
+ and x4, x4, #~(THREAD_SIZE - 1)
+ msr sp_el0, x4 // Save thread_info
str_l x21, __fdt_pointer, x5 // Save FDT pointer
str_l x24, memstart_addr, x6 // Save PHYS_OFFSET
mov x29, #0
@@ -606,6 +608,8 @@ ENDPROC(secondary_startup)
ENTRY(__secondary_switched)
ldr x0, [x21] // get secondary_data.stack
mov sp, x0
+ and x0, x0, #~(THREAD_SIZE - 1)
+ msr sp_el0, x0 // save thread_info
mov x29, #0
b secondary_start_kernel
ENDPROC(__secondary_switched)
diff --git a/arch/arm64/kernel/irq.c b/arch/arm64/kernel/irq.c
index 9f17ec071ee0..2386b26c0712 100644
--- a/arch/arm64/kernel/irq.c
+++ b/arch/arm64/kernel/irq.c
@@ -30,6 +30,9 @@
unsigned long irq_err_count;
+/* irq stack only needs to be 16 byte aligned - not IRQ_STACK_SIZE aligned. */
+DEFINE_PER_CPU(unsigned long [IRQ_STACK_SIZE/sizeof(long)], irq_stack) __aligned(16);
+
int arch_show_interrupts(struct seq_file *p, int prec)
{
show_ipi_list(p, prec);
diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c
index f4bc779e62e8..93e970231ca9 100644
--- a/arch/arm64/kernel/module.c
+++ b/arch/arm64/kernel/module.c
@@ -30,9 +30,6 @@
#include <asm/insn.h>
#include <asm/sections.h>
-#define AARCH64_INSN_IMM_MOVNZ AARCH64_INSN_IMM_MAX
-#define AARCH64_INSN_IMM_MOVK AARCH64_INSN_IMM_16
-
void *module_alloc(unsigned long size)
{
void *p;
@@ -75,15 +72,18 @@ static u64 do_reloc(enum aarch64_reloc_op reloc_op, void *place, u64 val)
static int reloc_data(enum aarch64_reloc_op op, void *place, u64 val, int len)
{
- u64 imm_mask = (1 << len) - 1;
s64 sval = do_reloc(op, place, val);
switch (len) {
case 16:
*(s16 *)place = sval;
+ if (sval < S16_MIN || sval > U16_MAX)
+ return -ERANGE;
break;
case 32:
*(s32 *)place = sval;
+ if (sval < S32_MIN || sval > U32_MAX)
+ return -ERANGE;
break;
case 64:
*(s64 *)place = sval;
@@ -92,34 +92,23 @@ static int reloc_data(enum aarch64_reloc_op op, void *place, u64 val, int len)
pr_err("Invalid length (%d) for data relocation\n", len);
return 0;
}
-
- /*
- * Extract the upper value bits (including the sign bit) and
- * shift them to bit 0.
- */
- sval = (s64)(sval & ~(imm_mask >> 1)) >> (len - 1);
-
- /*
- * Overflow has occurred if the value is not representable in
- * len bits (i.e the bottom len bits are not sign-extended and
- * the top bits are not all zero).
- */
- if ((u64)(sval + 1) > 2)
- return -ERANGE;
-
return 0;
}
+enum aarch64_insn_movw_imm_type {
+ AARCH64_INSN_IMM_MOVNZ,
+ AARCH64_INSN_IMM_MOVKZ,
+};
+
static int reloc_insn_movw(enum aarch64_reloc_op op, void *place, u64 val,
- int lsb, enum aarch64_insn_imm_type imm_type)
+ int lsb, enum aarch64_insn_movw_imm_type imm_type)
{
- u64 imm, limit = 0;
+ u64 imm;
s64 sval;
u32 insn = le32_to_cpu(*(u32 *)place);
sval = do_reloc(op, place, val);
- sval >>= lsb;
- imm = sval & 0xffff;
+ imm = sval >> lsb;
if (imm_type == AARCH64_INSN_IMM_MOVNZ) {
/*
@@ -128,7 +117,7 @@ static int reloc_insn_movw(enum aarch64_reloc_op op, void *place, u64 val,
* immediate is less than zero.
*/
insn &= ~(3 << 29);
- if ((s64)imm >= 0) {
+ if (sval >= 0) {
/* >=0: Set the instruction to MOVZ (opcode 10b). */
insn |= 2 << 29;
} else {
@@ -140,29 +129,13 @@ static int reloc_insn_movw(enum aarch64_reloc_op op, void *place, u64 val,
*/
imm = ~imm;
}
- imm_type = AARCH64_INSN_IMM_MOVK;
}
/* Update the instruction with the new encoding. */
- insn = aarch64_insn_encode_immediate(imm_type, insn, imm);
+ insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_16, insn, imm);
*(u32 *)place = cpu_to_le32(insn);
- /* Shift out the immediate field. */
- sval >>= 16;
-
- /*
- * For unsigned immediates, the overflow check is straightforward.
- * For signed immediates, the sign bit is actually the bit past the
- * most significant bit of the field.
- * The AARCH64_INSN_IMM_16 immediate type is unsigned.
- */
- if (imm_type != AARCH64_INSN_IMM_16) {
- sval++;
- limit++;
- }
-
- /* Check the upper bits depending on the sign of the immediate. */
- if ((u64)sval > limit)
+ if (imm > U16_MAX)
return -ERANGE;
return 0;
@@ -267,25 +240,25 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
overflow_check = false;
case R_AARCH64_MOVW_UABS_G0:
ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0,
- AARCH64_INSN_IMM_16);
+ AARCH64_INSN_IMM_MOVKZ);
break;
case R_AARCH64_MOVW_UABS_G1_NC:
overflow_check = false;
case R_AARCH64_MOVW_UABS_G1:
ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16,
- AARCH64_INSN_IMM_16);
+ AARCH64_INSN_IMM_MOVKZ);
break;
case R_AARCH64_MOVW_UABS_G2_NC:
overflow_check = false;
case R_AARCH64_MOVW_UABS_G2:
ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32,
- AARCH64_INSN_IMM_16);
+ AARCH64_INSN_IMM_MOVKZ);
break;
case R_AARCH64_MOVW_UABS_G3:
/* We're using the top bits so we can't overflow. */
overflow_check = false;
ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 48,
- AARCH64_INSN_IMM_16);
+ AARCH64_INSN_IMM_MOVKZ);
break;
case R_AARCH64_MOVW_SABS_G0:
ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0,
@@ -302,7 +275,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
case R_AARCH64_MOVW_PREL_G0_NC:
overflow_check = false;
ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0,
- AARCH64_INSN_IMM_MOVK);
+ AARCH64_INSN_IMM_MOVKZ);
break;
case R_AARCH64_MOVW_PREL_G0:
ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0,
@@ -311,7 +284,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
case R_AARCH64_MOVW_PREL_G1_NC:
overflow_check = false;
ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16,
- AARCH64_INSN_IMM_MOVK);
+ AARCH64_INSN_IMM_MOVKZ);
break;
case R_AARCH64_MOVW_PREL_G1:
ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16,
@@ -320,7 +293,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
case R_AARCH64_MOVW_PREL_G2_NC:
overflow_check = false;
ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32,
- AARCH64_INSN_IMM_MOVK);
+ AARCH64_INSN_IMM_MOVKZ);
break;
case R_AARCH64_MOVW_PREL_G2:
ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32,
diff --git a/arch/arm64/kernel/paravirt.c b/arch/arm64/kernel/paravirt.c
new file mode 100644
index 000000000000..53f371ed4568
--- /dev/null
+++ b/arch/arm64/kernel/paravirt.c
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2013 Citrix Systems
+ *
+ * Author: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
+ */
+
+#include <linux/export.h>
+#include <linux/jump_label.h>
+#include <linux/types.h>
+#include <asm/paravirt.h>
+
+struct static_key paravirt_steal_enabled;
+struct static_key paravirt_steal_rq_enabled;
+
+struct pv_time_ops pv_time_ops;
+EXPORT_SYMBOL_GPL(pv_time_ops);
diff --git a/arch/arm64/kernel/perf_callchain.c b/arch/arm64/kernel/perf_callchain.c
index 3aa74830cc69..ff4665462a02 100644
--- a/arch/arm64/kernel/perf_callchain.c
+++ b/arch/arm64/kernel/perf_callchain.c
@@ -164,8 +164,11 @@ void perf_callchain_kernel(struct perf_callchain_entry *entry,
frame.fp = regs->regs[29];
frame.sp = regs->sp;
frame.pc = regs->pc;
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ frame.graph = current->curr_ret_stack;
+#endif
- walk_stackframe(&frame, callchain_trace, entry);
+ walk_stackframe(current, &frame, callchain_trace, entry);
}
unsigned long perf_instruction_pointer(struct pt_regs *regs)
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 5b1897e8ca24..f7ab14c4d5df 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -29,60 +29,74 @@
* ARMv8 PMUv3 Performance Events handling code.
* Common event types.
*/
-enum armv8_pmuv3_perf_types {
- /* Required events. */
- ARMV8_PMUV3_PERFCTR_PMNC_SW_INCR = 0x00,
- ARMV8_PMUV3_PERFCTR_L1_DCACHE_REFILL = 0x03,
- ARMV8_PMUV3_PERFCTR_L1_DCACHE_ACCESS = 0x04,
- ARMV8_PMUV3_PERFCTR_PC_BRANCH_MIS_PRED = 0x10,
- ARMV8_PMUV3_PERFCTR_CLOCK_CYCLES = 0x11,
- ARMV8_PMUV3_PERFCTR_PC_BRANCH_PRED = 0x12,
-
- /* At least one of the following is required. */
- ARMV8_PMUV3_PERFCTR_INSTR_EXECUTED = 0x08,
- ARMV8_PMUV3_PERFCTR_OP_SPEC = 0x1B,
-
- /* Common architectural events. */
- ARMV8_PMUV3_PERFCTR_MEM_READ = 0x06,
- ARMV8_PMUV3_PERFCTR_MEM_WRITE = 0x07,
- ARMV8_PMUV3_PERFCTR_EXC_TAKEN = 0x09,
- ARMV8_PMUV3_PERFCTR_EXC_EXECUTED = 0x0A,
- ARMV8_PMUV3_PERFCTR_CID_WRITE = 0x0B,
- ARMV8_PMUV3_PERFCTR_PC_WRITE = 0x0C,
- ARMV8_PMUV3_PERFCTR_PC_IMM_BRANCH = 0x0D,
- ARMV8_PMUV3_PERFCTR_PC_PROC_RETURN = 0x0E,
- ARMV8_PMUV3_PERFCTR_MEM_UNALIGNED_ACCESS = 0x0F,
- ARMV8_PMUV3_PERFCTR_TTBR_WRITE = 0x1C,
-
- /* Common microarchitectural events. */
- ARMV8_PMUV3_PERFCTR_L1_ICACHE_REFILL = 0x01,
- ARMV8_PMUV3_PERFCTR_ITLB_REFILL = 0x02,
- ARMV8_PMUV3_PERFCTR_DTLB_REFILL = 0x05,
- ARMV8_PMUV3_PERFCTR_MEM_ACCESS = 0x13,
- ARMV8_PMUV3_PERFCTR_L1_ICACHE_ACCESS = 0x14,
- ARMV8_PMUV3_PERFCTR_L1_DCACHE_WB = 0x15,
- ARMV8_PMUV3_PERFCTR_L2_CACHE_ACCESS = 0x16,
- ARMV8_PMUV3_PERFCTR_L2_CACHE_REFILL = 0x17,
- ARMV8_PMUV3_PERFCTR_L2_CACHE_WB = 0x18,
- ARMV8_PMUV3_PERFCTR_BUS_ACCESS = 0x19,
- ARMV8_PMUV3_PERFCTR_MEM_ERROR = 0x1A,
- ARMV8_PMUV3_PERFCTR_BUS_CYCLES = 0x1D,
-};
+
+/* Required events. */
+#define ARMV8_PMUV3_PERFCTR_PMNC_SW_INCR 0x00
+#define ARMV8_PMUV3_PERFCTR_L1_DCACHE_REFILL 0x03
+#define ARMV8_PMUV3_PERFCTR_L1_DCACHE_ACCESS 0x04
+#define ARMV8_PMUV3_PERFCTR_PC_BRANCH_MIS_PRED 0x10
+#define ARMV8_PMUV3_PERFCTR_CLOCK_CYCLES 0x11
+#define ARMV8_PMUV3_PERFCTR_PC_BRANCH_PRED 0x12
+
+/* At least one of the following is required. */
+#define ARMV8_PMUV3_PERFCTR_INSTR_EXECUTED 0x08
+#define ARMV8_PMUV3_PERFCTR_OP_SPEC 0x1B
+
+/* Common architectural events. */
+#define ARMV8_PMUV3_PERFCTR_MEM_READ 0x06
+#define ARMV8_PMUV3_PERFCTR_MEM_WRITE 0x07
+#define ARMV8_PMUV3_PERFCTR_EXC_TAKEN 0x09
+#define ARMV8_PMUV3_PERFCTR_EXC_EXECUTED 0x0A
+#define ARMV8_PMUV3_PERFCTR_CID_WRITE 0x0B
+#define ARMV8_PMUV3_PERFCTR_PC_WRITE 0x0C
+#define ARMV8_PMUV3_PERFCTR_PC_IMM_BRANCH 0x0D
+#define ARMV8_PMUV3_PERFCTR_PC_PROC_RETURN 0x0E
+#define ARMV8_PMUV3_PERFCTR_MEM_UNALIGNED_ACCESS 0x0F
+#define ARMV8_PMUV3_PERFCTR_TTBR_WRITE 0x1C
+#define ARMV8_PMUV3_PERFCTR_CHAIN 0x1E
+#define ARMV8_PMUV3_PERFCTR_BR_RETIRED 0x21
+
+/* Common microarchitectural events. */
+#define ARMV8_PMUV3_PERFCTR_L1_ICACHE_REFILL 0x01
+#define ARMV8_PMUV3_PERFCTR_ITLB_REFILL 0x02
+#define ARMV8_PMUV3_PERFCTR_DTLB_REFILL 0x05
+#define ARMV8_PMUV3_PERFCTR_MEM_ACCESS 0x13
+#define ARMV8_PMUV3_PERFCTR_L1_ICACHE_ACCESS 0x14
+#define ARMV8_PMUV3_PERFCTR_L1_DCACHE_WB 0x15
+#define ARMV8_PMUV3_PERFCTR_L2_CACHE_ACCESS 0x16
+#define ARMV8_PMUV3_PERFCTR_L2_CACHE_REFILL 0x17
+#define ARMV8_PMUV3_PERFCTR_L2_CACHE_WB 0x18
+#define ARMV8_PMUV3_PERFCTR_BUS_ACCESS 0x19
+#define ARMV8_PMUV3_PERFCTR_MEM_ERROR 0x1A
+#define ARMV8_PMUV3_PERFCTR_BUS_CYCLES 0x1D
+#define ARMV8_PMUV3_PERFCTR_L1D_CACHE_ALLOCATE 0x1F
+#define ARMV8_PMUV3_PERFCTR_L2D_CACHE_ALLOCATE 0x20
+#define ARMV8_PMUV3_PERFCTR_BR_MIS_PRED_RETIRED 0x22
+#define ARMV8_PMUV3_PERFCTR_STALL_FRONTEND 0x23
+#define ARMV8_PMUV3_PERFCTR_STALL_BACKEND 0x24
+#define ARMV8_PMUV3_PERFCTR_L1D_TLB 0x25
+#define ARMV8_PMUV3_PERFCTR_L1I_TLB 0x26
+#define ARMV8_PMUV3_PERFCTR_L2I_CACHE 0x27
+#define ARMV8_PMUV3_PERFCTR_L2I_CACHE_REFILL 0x28
+#define ARMV8_PMUV3_PERFCTR_L3D_CACHE_ALLOCATE 0x29
+#define ARMV8_PMUV3_PERFCTR_L3D_CACHE_REFILL 0x2A
+#define ARMV8_PMUV3_PERFCTR_L3D_CACHE 0x2B
+#define ARMV8_PMUV3_PERFCTR_L3D_CACHE_WB 0x2C
+#define ARMV8_PMUV3_PERFCTR_L2D_TLB_REFILL 0x2D
+#define ARMV8_PMUV3_PERFCTR_L21_TLB_REFILL 0x2E
+#define ARMV8_PMUV3_PERFCTR_L2D_TLB 0x2F
+#define ARMV8_PMUV3_PERFCTR_L21_TLB 0x30
/* ARMv8 Cortex-A53 specific event types. */
-enum armv8_a53_pmu_perf_types {
- ARMV8_A53_PERFCTR_PREFETCH_LINEFILL = 0xC2,
-};
+#define ARMV8_A53_PERFCTR_PREFETCH_LINEFILL 0xC2
-/* ARMv8 Cortex-A57 specific event types. */
-enum armv8_a57_perf_types {
- ARMV8_A57_PERFCTR_L1_DCACHE_ACCESS_LD = 0x40,
- ARMV8_A57_PERFCTR_L1_DCACHE_ACCESS_ST = 0x41,
- ARMV8_A57_PERFCTR_L1_DCACHE_REFILL_LD = 0x42,
- ARMV8_A57_PERFCTR_L1_DCACHE_REFILL_ST = 0x43,
- ARMV8_A57_PERFCTR_DTLB_REFILL_LD = 0x4c,
- ARMV8_A57_PERFCTR_DTLB_REFILL_ST = 0x4d,
-};
+/* ARMv8 Cortex-A57 and Cortex-A72 specific event types. */
+#define ARMV8_A57_PERFCTR_L1_DCACHE_ACCESS_LD 0x40
+#define ARMV8_A57_PERFCTR_L1_DCACHE_ACCESS_ST 0x41
+#define ARMV8_A57_PERFCTR_L1_DCACHE_REFILL_LD 0x42
+#define ARMV8_A57_PERFCTR_L1_DCACHE_REFILL_ST 0x43
+#define ARMV8_A57_PERFCTR_DTLB_REFILL_LD 0x4c
+#define ARMV8_A57_PERFCTR_DTLB_REFILL_ST 0x4d
/* PMUv3 HW events mapping. */
static const unsigned armv8_pmuv3_perf_map[PERF_COUNT_HW_MAX] = {
@@ -106,6 +120,7 @@ static const unsigned armv8_a53_perf_map[PERF_COUNT_HW_MAX] = {
[PERF_COUNT_HW_BUS_CYCLES] = ARMV8_PMUV3_PERFCTR_BUS_CYCLES,
};
+/* ARM Cortex-A57 and Cortex-A72 events mapping. */
static const unsigned armv8_a57_perf_map[PERF_COUNT_HW_MAX] = {
PERF_MAP_ALL_UNSUPPORTED,
[PERF_COUNT_HW_CPU_CYCLES] = ARMV8_PMUV3_PERFCTR_CLOCK_CYCLES,
@@ -178,6 +193,137 @@ static const unsigned armv8_a57_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
[C(BPU)][C(OP_WRITE)][C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_PC_BRANCH_MIS_PRED,
};
+#define ARMV8_EVENT_ATTR_RESOLVE(m) #m
+#define ARMV8_EVENT_ATTR(name, config) \
+ PMU_EVENT_ATTR_STRING(name, armv8_event_attr_##name, \
+ "event=" ARMV8_EVENT_ATTR_RESOLVE(config))
+
+ARMV8_EVENT_ATTR(sw_incr, ARMV8_PMUV3_PERFCTR_PMNC_SW_INCR);
+ARMV8_EVENT_ATTR(l1i_cache_refill, ARMV8_PMUV3_PERFCTR_L1_ICACHE_REFILL);
+ARMV8_EVENT_ATTR(l1i_tlb_refill, ARMV8_PMUV3_PERFCTR_ITLB_REFILL);
+ARMV8_EVENT_ATTR(l1d_cache_refill, ARMV8_PMUV3_PERFCTR_L1_DCACHE_REFILL);
+ARMV8_EVENT_ATTR(l1d_cache, ARMV8_PMUV3_PERFCTR_L1_DCACHE_ACCESS);
+ARMV8_EVENT_ATTR(l1d_tlb_refill, ARMV8_PMUV3_PERFCTR_DTLB_REFILL);
+ARMV8_EVENT_ATTR(ld_retired, ARMV8_PMUV3_PERFCTR_MEM_READ);
+ARMV8_EVENT_ATTR(st_retired, ARMV8_PMUV3_PERFCTR_MEM_WRITE);
+ARMV8_EVENT_ATTR(inst_retired, ARMV8_PMUV3_PERFCTR_INSTR_EXECUTED);
+ARMV8_EVENT_ATTR(exc_taken, ARMV8_PMUV3_PERFCTR_EXC_TAKEN);
+ARMV8_EVENT_ATTR(exc_return, ARMV8_PMUV3_PERFCTR_EXC_EXECUTED);
+ARMV8_EVENT_ATTR(cid_write_retired, ARMV8_PMUV3_PERFCTR_CID_WRITE);
+ARMV8_EVENT_ATTR(pc_write_retired, ARMV8_PMUV3_PERFCTR_PC_WRITE);
+ARMV8_EVENT_ATTR(br_immed_retired, ARMV8_PMUV3_PERFCTR_PC_IMM_BRANCH);
+ARMV8_EVENT_ATTR(br_return_retired, ARMV8_PMUV3_PERFCTR_PC_PROC_RETURN);
+ARMV8_EVENT_ATTR(unaligned_ldst_retired, ARMV8_PMUV3_PERFCTR_MEM_UNALIGNED_ACCESS);
+ARMV8_EVENT_ATTR(br_mis_pred, ARMV8_PMUV3_PERFCTR_PC_BRANCH_MIS_PRED);
+ARMV8_EVENT_ATTR(cpu_cycles, ARMV8_PMUV3_PERFCTR_CLOCK_CYCLES);
+ARMV8_EVENT_ATTR(br_pred, ARMV8_PMUV3_PERFCTR_PC_BRANCH_PRED);
+ARMV8_EVENT_ATTR(mem_access, ARMV8_PMUV3_PERFCTR_MEM_ACCESS);
+ARMV8_EVENT_ATTR(l1i_cache, ARMV8_PMUV3_PERFCTR_L1_ICACHE_ACCESS);
+ARMV8_EVENT_ATTR(l1d_cache_wb, ARMV8_PMUV3_PERFCTR_L1_DCACHE_WB);
+ARMV8_EVENT_ATTR(l2d_cache, ARMV8_PMUV3_PERFCTR_L2_CACHE_ACCESS);
+ARMV8_EVENT_ATTR(l2d_cache_refill, ARMV8_PMUV3_PERFCTR_L2_CACHE_REFILL);
+ARMV8_EVENT_ATTR(l2d_cache_wb, ARMV8_PMUV3_PERFCTR_L2_CACHE_WB);
+ARMV8_EVENT_ATTR(bus_access, ARMV8_PMUV3_PERFCTR_BUS_ACCESS);
+ARMV8_EVENT_ATTR(memory_error, ARMV8_PMUV3_PERFCTR_MEM_ERROR);
+ARMV8_EVENT_ATTR(inst_spec, ARMV8_PMUV3_PERFCTR_OP_SPEC);
+ARMV8_EVENT_ATTR(ttbr_write_retired, ARMV8_PMUV3_PERFCTR_TTBR_WRITE);
+ARMV8_EVENT_ATTR(bus_cycles, ARMV8_PMUV3_PERFCTR_BUS_CYCLES);
+ARMV8_EVENT_ATTR(chain, ARMV8_PMUV3_PERFCTR_CHAIN);
+ARMV8_EVENT_ATTR(l1d_cache_allocate, ARMV8_PMUV3_PERFCTR_L1D_CACHE_ALLOCATE);
+ARMV8_EVENT_ATTR(l2d_cache_allocate, ARMV8_PMUV3_PERFCTR_L2D_CACHE_ALLOCATE);
+ARMV8_EVENT_ATTR(br_retired, ARMV8_PMUV3_PERFCTR_BR_RETIRED);
+ARMV8_EVENT_ATTR(br_mis_pred_retired, ARMV8_PMUV3_PERFCTR_BR_MIS_PRED_RETIRED);
+ARMV8_EVENT_ATTR(stall_frontend, ARMV8_PMUV3_PERFCTR_STALL_FRONTEND);
+ARMV8_EVENT_ATTR(stall_backend, ARMV8_PMUV3_PERFCTR_STALL_BACKEND);
+ARMV8_EVENT_ATTR(l1d_tlb, ARMV8_PMUV3_PERFCTR_L1D_TLB);
+ARMV8_EVENT_ATTR(l1i_tlb, ARMV8_PMUV3_PERFCTR_L1I_TLB);
+ARMV8_EVENT_ATTR(l2i_cache, ARMV8_PMUV3_PERFCTR_L2I_CACHE);
+ARMV8_EVENT_ATTR(l2i_cache_refill, ARMV8_PMUV3_PERFCTR_L2I_CACHE_REFILL);
+ARMV8_EVENT_ATTR(l3d_cache_allocate, ARMV8_PMUV3_PERFCTR_L3D_CACHE_ALLOCATE);
+ARMV8_EVENT_ATTR(l3d_cache_refill, ARMV8_PMUV3_PERFCTR_L3D_CACHE_REFILL);
+ARMV8_EVENT_ATTR(l3d_cache, ARMV8_PMUV3_PERFCTR_L3D_CACHE);
+ARMV8_EVENT_ATTR(l3d_cache_wb, ARMV8_PMUV3_PERFCTR_L3D_CACHE_WB);
+ARMV8_EVENT_ATTR(l2d_tlb_refill, ARMV8_PMUV3_PERFCTR_L2D_TLB_REFILL);
+ARMV8_EVENT_ATTR(l21_tlb_refill, ARMV8_PMUV3_PERFCTR_L21_TLB_REFILL);
+ARMV8_EVENT_ATTR(l2d_tlb, ARMV8_PMUV3_PERFCTR_L2D_TLB);
+ARMV8_EVENT_ATTR(l21_tlb, ARMV8_PMUV3_PERFCTR_L21_TLB);
+
+static struct attribute *armv8_pmuv3_event_attrs[] = {
+ &armv8_event_attr_sw_incr.attr.attr,
+ &armv8_event_attr_l1i_cache_refill.attr.attr,
+ &armv8_event_attr_l1i_tlb_refill.attr.attr,
+ &armv8_event_attr_l1d_cache_refill.attr.attr,
+ &armv8_event_attr_l1d_cache.attr.attr,
+ &armv8_event_attr_l1d_tlb_refill.attr.attr,
+ &armv8_event_attr_ld_retired.attr.attr,
+ &armv8_event_attr_st_retired.attr.attr,
+ &armv8_event_attr_inst_retired.attr.attr,
+ &armv8_event_attr_exc_taken.attr.attr,
+ &armv8_event_attr_exc_return.attr.attr,
+ &armv8_event_attr_cid_write_retired.attr.attr,
+ &armv8_event_attr_pc_write_retired.attr.attr,
+ &armv8_event_attr_br_immed_retired.attr.attr,
+ &armv8_event_attr_br_return_retired.attr.attr,
+ &armv8_event_attr_unaligned_ldst_retired.attr.attr,
+ &armv8_event_attr_br_mis_pred.attr.attr,
+ &armv8_event_attr_cpu_cycles.attr.attr,
+ &armv8_event_attr_br_pred.attr.attr,
+ &armv8_event_attr_mem_access.attr.attr,
+ &armv8_event_attr_l1i_cache.attr.attr,
+ &armv8_event_attr_l1d_cache_wb.attr.attr,
+ &armv8_event_attr_l2d_cache.attr.attr,
+ &armv8_event_attr_l2d_cache_refill.attr.attr,
+ &armv8_event_attr_l2d_cache_wb.attr.attr,
+ &armv8_event_attr_bus_access.attr.attr,
+ &armv8_event_attr_memory_error.attr.attr,
+ &armv8_event_attr_inst_spec.attr.attr,
+ &armv8_event_attr_ttbr_write_retired.attr.attr,
+ &armv8_event_attr_bus_cycles.attr.attr,
+ &armv8_event_attr_chain.attr.attr,
+ &armv8_event_attr_l1d_cache_allocate.attr.attr,
+ &armv8_event_attr_l2d_cache_allocate.attr.attr,
+ &armv8_event_attr_br_retired.attr.attr,
+ &armv8_event_attr_br_mis_pred_retired.attr.attr,
+ &armv8_event_attr_stall_frontend.attr.attr,
+ &armv8_event_attr_stall_backend.attr.attr,
+ &armv8_event_attr_l1d_tlb.attr.attr,
+ &armv8_event_attr_l1i_tlb.attr.attr,
+ &armv8_event_attr_l2i_cache.attr.attr,
+ &armv8_event_attr_l2i_cache_refill.attr.attr,
+ &armv8_event_attr_l3d_cache_allocate.attr.attr,
+ &armv8_event_attr_l3d_cache_refill.attr.attr,
+ &armv8_event_attr_l3d_cache.attr.attr,
+ &armv8_event_attr_l3d_cache_wb.attr.attr,
+ &armv8_event_attr_l2d_tlb_refill.attr.attr,
+ &armv8_event_attr_l21_tlb_refill.attr.attr,
+ &armv8_event_attr_l2d_tlb.attr.attr,
+ &armv8_event_attr_l21_tlb.attr.attr,
+ NULL,
+};
+
+static struct attribute_group armv8_pmuv3_events_attr_group = {
+ .name = "events",
+ .attrs = armv8_pmuv3_event_attrs,
+};
+
+PMU_FORMAT_ATTR(event, "config:0-9");
+
+static struct attribute *armv8_pmuv3_format_attrs[] = {
+ &format_attr_event.attr,
+ NULL,
+};
+
+static struct attribute_group armv8_pmuv3_format_attr_group = {
+ .name = "format",
+ .attrs = armv8_pmuv3_format_attrs,
+};
+
+static const struct attribute_group *armv8_pmuv3_attr_groups[] = {
+ &armv8_pmuv3_events_attr_group,
+ &armv8_pmuv3_format_attr_group,
+ NULL,
+};
+
/*
* Perf Events' indices
@@ -574,9 +720,6 @@ static void armv8pmu_reset(void *info)
/* Initialize & Reset PMNC: C and P bits. */
armv8pmu_pmcr_write(ARMV8_PMCR_P | ARMV8_PMCR_C);
-
- /* Disable access from userspace. */
- asm volatile("msr pmuserenr_el0, %0" :: "r" (0));
}
static int armv8_pmuv3_map_event(struct perf_event *event)
@@ -646,6 +789,7 @@ static int armv8_a53_pmu_init(struct arm_pmu *cpu_pmu)
armv8_pmu_init(cpu_pmu);
cpu_pmu->name = "armv8_cortex_a53";
cpu_pmu->map_event = armv8_a53_map_event;
+ cpu_pmu->pmu.attr_groups = armv8_pmuv3_attr_groups;
return armv8pmu_probe_num_events(cpu_pmu);
}
@@ -654,6 +798,16 @@ static int armv8_a57_pmu_init(struct arm_pmu *cpu_pmu)
armv8_pmu_init(cpu_pmu);
cpu_pmu->name = "armv8_cortex_a57";
cpu_pmu->map_event = armv8_a57_map_event;
+ cpu_pmu->pmu.attr_groups = armv8_pmuv3_attr_groups;
+ return armv8pmu_probe_num_events(cpu_pmu);
+}
+
+static int armv8_a72_pmu_init(struct arm_pmu *cpu_pmu)
+{
+ armv8_pmu_init(cpu_pmu);
+ cpu_pmu->name = "armv8_cortex_a72";
+ cpu_pmu->map_event = armv8_a57_map_event;
+ cpu_pmu->pmu.attr_groups = armv8_pmuv3_attr_groups;
return armv8pmu_probe_num_events(cpu_pmu);
}
@@ -661,6 +815,7 @@ static const struct of_device_id armv8_pmu_of_device_ids[] = {
{.compatible = "arm,armv8-pmuv3", .data = armv8_pmuv3_init},
{.compatible = "arm,cortex-a53-pmu", .data = armv8_a53_pmu_init},
{.compatible = "arm,cortex-a57-pmu", .data = armv8_a57_pmu_init},
+ {.compatible = "arm,cortex-a72-pmu", .data = armv8_a72_pmu_init},
{},
};
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index f75b540bc3b4..88d742ba19d5 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -344,11 +344,14 @@ unsigned long get_wchan(struct task_struct *p)
frame.fp = thread_saved_fp(p);
frame.sp = thread_saved_sp(p);
frame.pc = thread_saved_pc(p);
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ frame.graph = p->curr_ret_stack;
+#endif
stack_page = (unsigned long)task_stack_page(p);
do {
if (frame.sp < stack_page ||
frame.sp >= stack_page + THREAD_SIZE ||
- unwind_frame(&frame))
+ unwind_frame(p, &frame))
return 0;
if (!in_sched_functions(frame.pc))
return frame.pc;
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index 1971f491bb90..ff7f13239515 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -58,6 +58,12 @@
*/
void ptrace_disable(struct task_struct *child)
{
+ /*
+ * This would be better off in core code, but PTRACE_DETACH has
+ * grown its fair share of arch-specific worts and changing it
+ * is likely to cause regressions on obscure architectures.
+ */
+ user_disable_single_step(child);
}
#ifdef CONFIG_HAVE_HW_BREAKPOINT
diff --git a/arch/arm64/kernel/return_address.c b/arch/arm64/kernel/return_address.c
index 6c4fd2810ecb..1718706fde83 100644
--- a/arch/arm64/kernel/return_address.c
+++ b/arch/arm64/kernel/return_address.c
@@ -43,8 +43,11 @@ void *return_address(unsigned int level)
frame.fp = (unsigned long)__builtin_frame_address(0);
frame.sp = current_stack_pointer;
frame.pc = (unsigned long)return_address; /* dummy */
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ frame.graph = current->curr_ret_stack;
+#endif
- walk_stackframe(&frame, save_return_addr, &data);
+ walk_stackframe(current, &frame, save_return_addr, &data);
if (!data.level)
return data.addr;
diff --git a/arch/arm64/kernel/sleep.S b/arch/arm64/kernel/sleep.S
index f586f7c875e2..e33fe33876ab 100644
--- a/arch/arm64/kernel/sleep.S
+++ b/arch/arm64/kernel/sleep.S
@@ -173,6 +173,9 @@ ENTRY(cpu_resume)
/* load physical address of identity map page table in x1 */
adrp x1, idmap_pg_dir
mov sp, x2
+ /* save thread_info */
+ and x2, x2, #~(THREAD_SIZE - 1)
+ msr sp_el0, x2
/*
* cpu_do_resume expects x0 to contain context physical address
* pointer and x1 to contain physical address of 1:1 page tables
diff --git a/arch/arm64/kernel/smccc-call.S b/arch/arm64/kernel/smccc-call.S
new file mode 100644
index 000000000000..ae0496fa4235
--- /dev/null
+++ b/arch/arm64/kernel/smccc-call.S
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/linkage.h>
+#include <asm/asm-offsets.h>
+
+ .macro SMCCC instr
+ .cfi_startproc
+ \instr #0
+ ldr x4, [sp]
+ stp x0, x1, [x4, #ARM_SMCCC_RES_X0_OFFS]
+ stp x2, x3, [x4, #ARM_SMCCC_RES_X2_OFFS]
+ ret
+ .cfi_endproc
+ .endm
+
+/*
+ * void arm_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2,
+ * unsigned long a3, unsigned long a4, unsigned long a5,
+ * unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
+ */
+ENTRY(arm_smccc_smc)
+ SMCCC smc
+ENDPROC(arm_smccc_smc)
+
+/*
+ * void arm_smccc_hvc(unsigned long a0, unsigned long a1, unsigned long a2,
+ * unsigned long a3, unsigned long a4, unsigned long a5,
+ * unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
+ */
+ENTRY(arm_smccc_hvc)
+ SMCCC hvc
+ENDPROC(arm_smccc_hvc)
diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c
index ccb6078ed9f2..4fad9787ab46 100644
--- a/arch/arm64/kernel/stacktrace.c
+++ b/arch/arm64/kernel/stacktrace.c
@@ -17,9 +17,11 @@
*/
#include <linux/kernel.h>
#include <linux/export.h>
+#include <linux/ftrace.h>
#include <linux/sched.h>
#include <linux/stacktrace.h>
+#include <asm/irq.h>
#include <asm/stacktrace.h>
/*
@@ -35,25 +37,83 @@
* ldp x29, x30, [sp]
* add sp, sp, #0x10
*/
-int notrace unwind_frame(struct stackframe *frame)
+int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
{
unsigned long high, low;
unsigned long fp = frame->fp;
+ unsigned long irq_stack_ptr;
+
+ /*
+ * Use raw_smp_processor_id() to avoid false-positives from
+ * CONFIG_DEBUG_PREEMPT. get_wchan() calls unwind_frame() on sleeping
+ * task stacks, we can be pre-empted in this case, so
+ * {raw_,}smp_processor_id() may give us the wrong value. Sleeping
+ * tasks can't ever be on an interrupt stack, so regardless of cpu,
+ * the checks will always fail.
+ */
+ irq_stack_ptr = IRQ_STACK_PTR(raw_smp_processor_id());
low = frame->sp;
- high = ALIGN(low, THREAD_SIZE);
+ /* irq stacks are not THREAD_SIZE aligned */
+ if (on_irq_stack(frame->sp, raw_smp_processor_id()))
+ high = irq_stack_ptr;
+ else
+ high = ALIGN(low, THREAD_SIZE) - 0x20;
- if (fp < low || fp > high - 0x18 || fp & 0xf)
+ if (fp < low || fp > high || fp & 0xf)
return -EINVAL;
frame->sp = fp + 0x10;
frame->fp = *(unsigned long *)(fp);
frame->pc = *(unsigned long *)(fp + 8);
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ if (tsk && tsk->ret_stack &&
+ (frame->pc == (unsigned long)return_to_handler)) {
+ /*
+ * This is a case where function graph tracer has
+ * modified a return address (LR) in a stack frame
+ * to hook a function return.
+ * So replace it to an original value.
+ */
+ frame->pc = tsk->ret_stack[frame->graph--].ret;
+ }
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
+
+ /*
+ * Check whether we are going to walk through from interrupt stack
+ * to task stack.
+ * If we reach the end of the stack - and its an interrupt stack,
+ * unpack the dummy frame to find the original elr.
+ *
+ * Check the frame->fp we read from the bottom of the irq_stack,
+ * and the original task stack pointer are both in current->stack.
+ */
+ if (frame->sp == irq_stack_ptr) {
+ struct pt_regs *irq_args;
+ unsigned long orig_sp = IRQ_STACK_TO_TASK_STACK(irq_stack_ptr);
+
+ if (object_is_on_stack((void *)orig_sp) &&
+ object_is_on_stack((void *)frame->fp)) {
+ frame->sp = orig_sp;
+
+ /* orig_sp is the saved pt_regs, find the elr */
+ irq_args = (struct pt_regs *)orig_sp;
+ frame->pc = irq_args->pc;
+ } else {
+ /*
+ * This frame has a non-standard format, and we
+ * didn't fix it, because the data looked wrong.
+ * Refuse to output this frame.
+ */
+ return -EINVAL;
+ }
+ }
+
return 0;
}
-void notrace walk_stackframe(struct stackframe *frame,
+void notrace walk_stackframe(struct task_struct *tsk, struct stackframe *frame,
int (*fn)(struct stackframe *, void *), void *data)
{
while (1) {
@@ -61,7 +121,7 @@ void notrace walk_stackframe(struct stackframe *frame,
if (fn(frame, data))
break;
- ret = unwind_frame(frame);
+ ret = unwind_frame(tsk, frame);
if (ret < 0)
break;
}
@@ -112,8 +172,11 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
frame.sp = current_stack_pointer;
frame.pc = (unsigned long)save_stack_trace_tsk;
}
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ frame.graph = tsk->curr_ret_stack;
+#endif
- walk_stackframe(&frame, save_trace, &data);
+ walk_stackframe(tsk, &frame, save_trace, &data);
if (trace->nr_entries < trace->max_entries)
trace->entries[trace->nr_entries++] = ULONG_MAX;
}
diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c
index 13339b6ffc1a..59779699a1a4 100644
--- a/arch/arm64/kernel/time.c
+++ b/arch/arm64/kernel/time.c
@@ -52,8 +52,11 @@ unsigned long profile_pc(struct pt_regs *regs)
frame.fp = regs->regs[29];
frame.sp = regs->sp;
frame.pc = regs->pc;
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ frame.graph = -1; /* no task info */
+#endif
do {
- int ret = unwind_frame(&frame);
+ int ret = unwind_frame(NULL, &frame);
if (ret < 0)
return 0;
} while (in_lock_functions(frame.pc));
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index e9b9b5364393..cbedd724f48e 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -146,17 +146,15 @@ static void dump_instr(const char *lvl, struct pt_regs *regs)
static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
{
struct stackframe frame;
+ unsigned long irq_stack_ptr = IRQ_STACK_PTR(smp_processor_id());
+ int skip;
pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk);
if (!tsk)
tsk = current;
- if (regs) {
- frame.fp = regs->regs[29];
- frame.sp = regs->sp;
- frame.pc = regs->pc;
- } else if (tsk == current) {
+ if (tsk == current) {
frame.fp = (unsigned long)__builtin_frame_address(0);
frame.sp = current_stack_pointer;
frame.pc = (unsigned long)dump_backtrace;
@@ -168,21 +166,49 @@ static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
frame.sp = thread_saved_sp(tsk);
frame.pc = thread_saved_pc(tsk);
}
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ frame.graph = tsk->curr_ret_stack;
+#endif
- pr_emerg("Call trace:\n");
+ skip = !!regs;
+ printk("Call trace:\n");
while (1) {
unsigned long where = frame.pc;
unsigned long stack;
int ret;
- dump_backtrace_entry(where);
- ret = unwind_frame(&frame);
+ /* skip until specified stack frame */
+ if (!skip) {
+ dump_backtrace_entry(where);
+ } else if (frame.fp == regs->regs[29]) {
+ skip = 0;
+ /*
+ * Mostly, this is the case where this function is
+ * called in panic/abort. As exception handler's
+ * stack frame does not contain the corresponding pc
+ * at which an exception has taken place, use regs->pc
+ * instead.
+ */
+ dump_backtrace_entry(regs->pc);
+ }
+ ret = unwind_frame(tsk, &frame);
if (ret < 0)
break;
stack = frame.sp;
- if (in_exception_text(where))
+ if (in_exception_text(where)) {
+ /*
+ * If we switched to the irq_stack before calling this
+ * exception handler, then the pt_regs will be on the
+ * task stack. The easiest way to tell is if the large
+ * pt_regs would overlap with the end of the irq_stack.
+ */
+ if (stack < irq_stack_ptr &&
+ (stack + sizeof(struct pt_regs)) > irq_stack_ptr)
+ stack = IRQ_STACK_TO_TASK_STACK(irq_stack_ptr);
+
dump_mem("", "Exception stack", stack,
stack + sizeof(struct pt_regs), false);
+ }
}
}
@@ -456,22 +482,22 @@ asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr)
void __pte_error(const char *file, int line, unsigned long val)
{
- pr_crit("%s:%d: bad pte %016lx.\n", file, line, val);
+ pr_err("%s:%d: bad pte %016lx.\n", file, line, val);
}
void __pmd_error(const char *file, int line, unsigned long val)
{
- pr_crit("%s:%d: bad pmd %016lx.\n", file, line, val);
+ pr_err("%s:%d: bad pmd %016lx.\n", file, line, val);
}
void __pud_error(const char *file, int line, unsigned long val)
{
- pr_crit("%s:%d: bad pud %016lx.\n", file, line, val);
+ pr_err("%s:%d: bad pud %016lx.\n", file, line, val);
}
void __pgd_error(const char *file, int line, unsigned long val)
{
- pr_crit("%s:%d: bad pgd %016lx.\n", file, line, val);
+ pr_err("%s:%d: bad pgd %016lx.\n", file, line, val);
}
/* GENERIC_BUG traps */
diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
index 1ee2c3937d4e..e3928f578891 100644
--- a/arch/arm64/kernel/vmlinux.lds.S
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -5,6 +5,7 @@
*/
#include <asm-generic/vmlinux.lds.h>
+#include <asm/cache.h>
#include <asm/kernel-pgtable.h>
#include <asm/thread_info.h>
#include <asm/memory.h>
@@ -112,7 +113,6 @@ SECTIONS
*(.got) /* Global offset table */
}
- ALIGN_DEBUG_RO
RO_DATA(PAGE_SIZE)
EXCEPTION_TABLE(8)
NOTES
@@ -127,7 +127,6 @@ SECTIONS
ARM_EXIT_KEEP(EXIT_TEXT)
}
- ALIGN_DEBUG_RO_MIN(16)
.init.data : {
INIT_DATA
INIT_SETUP(16)
@@ -140,10 +139,7 @@ SECTIONS
ARM_EXIT_KEEP(EXIT_DATA)
}
- PERCPU_SECTION(64)
-
- . = ALIGN(PAGE_SIZE);
- __init_end = .;
+ PERCPU_SECTION(L1_CACHE_BYTES)
. = ALIGN(4);
.altinstructions : {
@@ -156,9 +152,11 @@ SECTIONS
}
. = ALIGN(PAGE_SIZE);
+ __init_end = .;
+
_data = .;
_sdata = .;
- RW_DATA_SECTION(64, PAGE_SIZE, THREAD_SIZE)
+ RW_DATA_SECTION(L1_CACHE_BYTES, PAGE_SIZE, THREAD_SIZE)
PECOFF_EDATA_PADDING
_edata = .;
diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile
index 1949fe5f5424..caee9ee8e12a 100644
--- a/arch/arm64/kvm/Makefile
+++ b/arch/arm64/kvm/Makefile
@@ -10,6 +10,7 @@ KVM=../../../virt/kvm
ARM=../../../arch/arm/kvm
obj-$(CONFIG_KVM_ARM_HOST) += kvm.o
+obj-$(CONFIG_KVM_ARM_HOST) += hyp/
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
@@ -22,8 +23,6 @@ kvm-$(CONFIG_KVM_ARM_HOST) += guest.o debug.o reset.o sys_regs.o sys_regs_generi
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic.o
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic-v2.o
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic-v2-emul.o
-kvm-$(CONFIG_KVM_ARM_HOST) += vgic-v2-switch.o
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic-v3.o
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/vgic-v3-emul.o
-kvm-$(CONFIG_KVM_ARM_HOST) += vgic-v3-switch.o
kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/arch_timer.o
diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index d250160d32bc..fcb778899a38 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -28,13 +28,21 @@
#include <asm/cputype.h>
#include <asm/uaccess.h>
#include <asm/kvm.h>
-#include <asm/kvm_asm.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_coproc.h>
#include "trace.h"
+#define VM_STAT(x) { #x, offsetof(struct kvm, stat.x), KVM_STAT_VM }
+#define VCPU_STAT(x) { #x, offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU }
+
struct kvm_stats_debugfs_item debugfs_entries[] = {
+ VCPU_STAT(hvc_exit_stat),
+ VCPU_STAT(wfe_exit_stat),
+ VCPU_STAT(wfi_exit_stat),
+ VCPU_STAT(mmio_exit_user),
+ VCPU_STAT(mmio_exit_kernel),
+ VCPU_STAT(exits),
{ NULL }
};
diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c
index 68a0759b1375..eba89e42f0ed 100644
--- a/arch/arm64/kvm/handle_exit.c
+++ b/arch/arm64/kvm/handle_exit.c
@@ -23,6 +23,7 @@
#include <linux/kvm_host.h>
#include <asm/esr.h>
+#include <asm/kvm_asm.h>
#include <asm/kvm_coproc.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_mmu.h>
@@ -37,8 +38,9 @@ static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run)
{
int ret;
- trace_kvm_hvc_arm64(*vcpu_pc(vcpu), *vcpu_reg(vcpu, 0),
+ trace_kvm_hvc_arm64(*vcpu_pc(vcpu), vcpu_get_reg(vcpu, 0),
kvm_vcpu_hvc_get_imm(vcpu));
+ vcpu->stat.hvc_exit_stat++;
ret = kvm_psci_call(vcpu);
if (ret < 0) {
@@ -71,9 +73,11 @@ static int kvm_handle_wfx(struct kvm_vcpu *vcpu, struct kvm_run *run)
{
if (kvm_vcpu_get_hsr(vcpu) & ESR_ELx_WFx_ISS_WFE) {
trace_kvm_wfx_arm64(*vcpu_pc(vcpu), true);
+ vcpu->stat.wfe_exit_stat++;
kvm_vcpu_on_spin(vcpu);
} else {
trace_kvm_wfx_arm64(*vcpu_pc(vcpu), false);
+ vcpu->stat.wfi_exit_stat++;
kvm_vcpu_block(vcpu);
}
diff --git a/arch/arm64/kvm/hyp-init.S b/arch/arm64/kvm/hyp-init.S
index 178ba2248a98..3e568dcd907b 100644
--- a/arch/arm64/kvm/hyp-init.S
+++ b/arch/arm64/kvm/hyp-init.S
@@ -94,6 +94,15 @@ __do_hyp_init:
*/
mrs x5, ID_AA64MMFR0_EL1
bfi x4, x5, #16, #3
+ /*
+ * Read the VMIDBits bits from ID_AA64MMFR1_EL1 and set the VS bit in
+ * VTCR_EL2.
+ */
+ mrs x5, ID_AA64MMFR1_EL1
+ ubfx x5, x5, #5, #1
+ lsl x5, x5, #VTCR_EL2_VS
+ orr x4, x4, x5
+
msr vtcr_el2, x4
mrs x4, mair_el1
diff --git a/arch/arm64/kvm/hyp.S b/arch/arm64/kvm/hyp.S
index 1599701ef044..0ccdcbbef3c2 100644
--- a/arch/arm64/kvm/hyp.S
+++ b/arch/arm64/kvm/hyp.S
@@ -17,906 +17,7 @@
#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>
-#include <asm/kvm.h>
-#include <asm/kvm_arm.h>
-#include <asm/kvm_asm.h>
-#include <asm/kvm_mmu.h>
-#include <asm/memory.h>
-
-#define CPU_GP_REG_OFFSET(x) (CPU_GP_REGS + x)
-#define CPU_XREG_OFFSET(x) CPU_GP_REG_OFFSET(CPU_USER_PT_REGS + 8*x)
-#define CPU_SPSR_OFFSET(x) CPU_GP_REG_OFFSET(CPU_SPSR + 8*x)
-#define CPU_SYSREG_OFFSET(x) (CPU_SYSREGS + 8*x)
-
- .text
- .pushsection .hyp.text, "ax"
- .align PAGE_SHIFT
-
-.macro save_common_regs
- // x2: base address for cpu context
- // x3: tmp register
-
- add x3, x2, #CPU_XREG_OFFSET(19)
- stp x19, x20, [x3]
- stp x21, x22, [x3, #16]
- stp x23, x24, [x3, #32]
- stp x25, x26, [x3, #48]
- stp x27, x28, [x3, #64]
- stp x29, lr, [x3, #80]
-
- mrs x19, sp_el0
- 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]
-
- mrs x22, sp_el1
- mrs x23, elr_el1
- mrs x24, spsr_el1
-
- str x22, [x2, #CPU_GP_REG_OFFSET(CPU_SP_EL1)]
- str x23, [x2, #CPU_GP_REG_OFFSET(CPU_ELR_EL1)]
- str x24, [x2, #CPU_SPSR_OFFSET(KVM_SPSR_EL1)]
-.endm
-
-.macro restore_common_regs
- // x2: base address for cpu context
- // x3: tmp register
-
- ldr x22, [x2, #CPU_GP_REG_OFFSET(CPU_SP_EL1)]
- ldr x23, [x2, #CPU_GP_REG_OFFSET(CPU_ELR_EL1)]
- ldr x24, [x2, #CPU_SPSR_OFFSET(KVM_SPSR_EL1)]
-
- msr sp_el1, x22
- msr elr_el1, x23
- msr spsr_el1, x24
-
- add x3, x2, #CPU_XREG_OFFSET(31) // SP_EL0
- ldp x19, x20, [x3]
- ldr x21, [x3, #16]
-
- msr sp_el0, x19
- 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]
- ldp x21, x22, [x3, #16]
- ldp x23, x24, [x3, #32]
- ldp x25, x26, [x3, #48]
- ldp x27, x28, [x3, #64]
- ldp x29, lr, [x3, #80]
-.endm
-
-.macro save_host_regs
- save_common_regs
-.endm
-
-.macro restore_host_regs
- restore_common_regs
-.endm
-
-.macro save_fpsimd
- // x2: cpu context address
- // x3, x4: tmp regs
- add x3, x2, #CPU_GP_REG_OFFSET(CPU_FP_REGS)
- fpsimd_save x3, 4
-.endm
-
-.macro restore_fpsimd
- // x2: cpu context address
- // x3, x4: tmp regs
- add x3, x2, #CPU_GP_REG_OFFSET(CPU_FP_REGS)
- fpsimd_restore x3, 4
-.endm
-
-.macro save_guest_regs
- // x0 is the vcpu address
- // x1 is the return code, do not corrupt!
- // x2 is the cpu context
- // x3 is a tmp register
- // Guest's x0-x3 are on the stack
-
- // Compute base to save registers
- add x3, x2, #CPU_XREG_OFFSET(4)
- stp x4, x5, [x3]
- stp x6, x7, [x3, #16]
- stp x8, x9, [x3, #32]
- stp x10, x11, [x3, #48]
- stp x12, x13, [x3, #64]
- stp x14, x15, [x3, #80]
- stp x16, x17, [x3, #96]
- str x18, [x3, #112]
-
- pop x6, x7 // x2, x3
- pop x4, x5 // x0, x1
-
- add x3, x2, #CPU_XREG_OFFSET(0)
- stp x4, x5, [x3]
- stp x6, x7, [x3, #16]
-
- save_common_regs
-.endm
-
-.macro restore_guest_regs
- // x0 is the vcpu address.
- // x2 is the cpu context
- // x3 is a tmp register
-
- // Prepare x0-x3 for later restore
- add x3, x2, #CPU_XREG_OFFSET(0)
- ldp x4, x5, [x3]
- ldp x6, x7, [x3, #16]
- push x4, x5 // Push x0-x3 on the stack
- push x6, x7
-
- // x4-x18
- ldp x4, x5, [x3, #32]
- ldp x6, x7, [x3, #48]
- ldp x8, x9, [x3, #64]
- ldp x10, x11, [x3, #80]
- ldp x12, x13, [x3, #96]
- ldp x14, x15, [x3, #112]
- ldp x16, x17, [x3, #128]
- ldr x18, [x3, #144]
-
- // x19-x29, lr, sp*, elr*, spsr*
- restore_common_regs
-
- // Last bits of the 64bit state
- pop x2, x3
- pop x0, x1
-
- // Do not touch any register after this!
-.endm
-
-/*
- * Macros to perform system register save/restore.
- *
- * Ordering here is absolutely critical, and must be kept consistent
- * in {save,restore}_sysregs, {save,restore}_guest_32bit_state,
- * and in kvm_asm.h.
- *
- * In other words, don't touch any of these unless you know what
- * you are doing.
- */
-.macro save_sysregs
- // x2: base address for cpu context
- // x3: tmp register
-
- add x3, x2, #CPU_SYSREG_OFFSET(MPIDR_EL1)
-
- mrs x4, vmpidr_el2
- mrs x5, csselr_el1
- mrs x6, sctlr_el1
- mrs x7, actlr_el1
- mrs x8, cpacr_el1
- mrs x9, ttbr0_el1
- mrs x10, ttbr1_el1
- mrs x11, tcr_el1
- mrs x12, esr_el1
- mrs x13, afsr0_el1
- mrs x14, afsr1_el1
- mrs x15, far_el1
- mrs x16, mair_el1
- mrs x17, vbar_el1
- mrs x18, contextidr_el1
- mrs x19, tpidr_el0
- mrs x20, tpidrro_el0
- mrs x21, tpidr_el1
- mrs x22, amair_el1
- mrs x23, cntkctl_el1
- mrs x24, par_el1
- mrs x25, mdscr_el1
-
- stp x4, x5, [x3]
- stp x6, x7, [x3, #16]
- stp x8, x9, [x3, #32]
- stp x10, x11, [x3, #48]
- stp x12, x13, [x3, #64]
- stp x14, x15, [x3, #80]
- stp x16, x17, [x3, #96]
- stp x18, x19, [x3, #112]
- stp x20, x21, [x3, #128]
- stp x22, x23, [x3, #144]
- stp x24, x25, [x3, #160]
-.endm
-
-.macro save_debug type
- // x4: pointer to register set
- // x5: number of registers to skip
- // x6..x22 trashed
-
- adr x22, 1f
- add x22, x22, x5, lsl #2
- br x22
-1:
- mrs x21, \type\()15_el1
- mrs x20, \type\()14_el1
- mrs x19, \type\()13_el1
- mrs x18, \type\()12_el1
- mrs x17, \type\()11_el1
- mrs x16, \type\()10_el1
- mrs x15, \type\()9_el1
- mrs x14, \type\()8_el1
- mrs x13, \type\()7_el1
- mrs x12, \type\()6_el1
- mrs x11, \type\()5_el1
- mrs x10, \type\()4_el1
- mrs x9, \type\()3_el1
- mrs x8, \type\()2_el1
- mrs x7, \type\()1_el1
- mrs x6, \type\()0_el1
-
- adr x22, 1f
- add x22, x22, x5, lsl #2
- br x22
-1:
- str x21, [x4, #(15 * 8)]
- str x20, [x4, #(14 * 8)]
- str x19, [x4, #(13 * 8)]
- str x18, [x4, #(12 * 8)]
- str x17, [x4, #(11 * 8)]
- str x16, [x4, #(10 * 8)]
- str x15, [x4, #(9 * 8)]
- str x14, [x4, #(8 * 8)]
- str x13, [x4, #(7 * 8)]
- str x12, [x4, #(6 * 8)]
- str x11, [x4, #(5 * 8)]
- str x10, [x4, #(4 * 8)]
- str x9, [x4, #(3 * 8)]
- str x8, [x4, #(2 * 8)]
- str x7, [x4, #(1 * 8)]
- str x6, [x4, #(0 * 8)]
-.endm
-
-.macro restore_sysregs
- // x2: base address for cpu context
- // x3: tmp register
-
- add x3, x2, #CPU_SYSREG_OFFSET(MPIDR_EL1)
-
- ldp x4, x5, [x3]
- ldp x6, x7, [x3, #16]
- ldp x8, x9, [x3, #32]
- ldp x10, x11, [x3, #48]
- ldp x12, x13, [x3, #64]
- ldp x14, x15, [x3, #80]
- ldp x16, x17, [x3, #96]
- ldp x18, x19, [x3, #112]
- ldp x20, x21, [x3, #128]
- ldp x22, x23, [x3, #144]
- ldp x24, x25, [x3, #160]
-
- msr vmpidr_el2, x4
- msr csselr_el1, x5
- msr sctlr_el1, x6
- msr actlr_el1, x7
- msr cpacr_el1, x8
- msr ttbr0_el1, x9
- msr ttbr1_el1, x10
- msr tcr_el1, x11
- msr esr_el1, x12
- msr afsr0_el1, x13
- msr afsr1_el1, x14
- msr far_el1, x15
- msr mair_el1, x16
- msr vbar_el1, x17
- msr contextidr_el1, x18
- msr tpidr_el0, x19
- msr tpidrro_el0, x20
- msr tpidr_el1, x21
- msr amair_el1, x22
- msr cntkctl_el1, x23
- msr par_el1, x24
- msr mdscr_el1, x25
-.endm
-
-.macro restore_debug type
- // x4: pointer to register set
- // x5: number of registers to skip
- // x6..x22 trashed
-
- adr x22, 1f
- add x22, x22, x5, lsl #2
- br x22
-1:
- ldr x21, [x4, #(15 * 8)]
- ldr x20, [x4, #(14 * 8)]
- ldr x19, [x4, #(13 * 8)]
- ldr x18, [x4, #(12 * 8)]
- ldr x17, [x4, #(11 * 8)]
- ldr x16, [x4, #(10 * 8)]
- ldr x15, [x4, #(9 * 8)]
- ldr x14, [x4, #(8 * 8)]
- ldr x13, [x4, #(7 * 8)]
- ldr x12, [x4, #(6 * 8)]
- ldr x11, [x4, #(5 * 8)]
- ldr x10, [x4, #(4 * 8)]
- ldr x9, [x4, #(3 * 8)]
- ldr x8, [x4, #(2 * 8)]
- ldr x7, [x4, #(1 * 8)]
- ldr x6, [x4, #(0 * 8)]
-
- adr x22, 1f
- add x22, x22, x5, lsl #2
- br x22
-1:
- msr \type\()15_el1, x21
- msr \type\()14_el1, x20
- msr \type\()13_el1, x19
- msr \type\()12_el1, x18
- msr \type\()11_el1, x17
- msr \type\()10_el1, x16
- msr \type\()9_el1, x15
- msr \type\()8_el1, x14
- msr \type\()7_el1, x13
- msr \type\()6_el1, x12
- msr \type\()5_el1, x11
- msr \type\()4_el1, x10
- msr \type\()3_el1, x9
- msr \type\()2_el1, x8
- msr \type\()1_el1, x7
- msr \type\()0_el1, x6
-.endm
-
-.macro skip_32bit_state tmp, target
- // Skip 32bit state if not needed
- mrs \tmp, hcr_el2
- tbnz \tmp, #HCR_RW_SHIFT, \target
-.endm
-
-.macro skip_tee_state tmp, target
- // Skip ThumbEE state if not needed
- mrs \tmp, id_pfr0_el1
- tbz \tmp, #12, \target
-.endm
-
-.macro skip_debug_state tmp, target
- ldr \tmp, [x0, #VCPU_DEBUG_FLAGS]
- tbz \tmp, #KVM_ARM64_DEBUG_DIRTY_SHIFT, \target
-.endm
-
-/*
- * Branch to target if CPTR_EL2.TFP bit is set (VFP/SIMD trapping enabled)
- */
-.macro skip_fpsimd_state tmp, target
- mrs \tmp, cptr_el2
- tbnz \tmp, #CPTR_EL2_TFP_SHIFT, \target
-.endm
-
-.macro compute_debug_state target
- // Compute debug state: If any of KDE, MDE or KVM_ARM64_DEBUG_DIRTY
- // is set, we do a full save/restore cycle and disable trapping.
- add x25, x0, #VCPU_CONTEXT
-
- // Check the state of MDSCR_EL1
- ldr x25, [x25, #CPU_SYSREG_OFFSET(MDSCR_EL1)]
- and x26, x25, #DBG_MDSCR_KDE
- and x25, x25, #DBG_MDSCR_MDE
- adds xzr, x25, x26
- b.eq 9998f // Nothing to see there
-
- // If any interesting bits was set, we must set the flag
- mov x26, #KVM_ARM64_DEBUG_DIRTY
- str x26, [x0, #VCPU_DEBUG_FLAGS]
- b 9999f // Don't skip restore
-
-9998:
- // Otherwise load the flags from memory in case we recently
- // trapped
- skip_debug_state x25, \target
-9999:
-.endm
-
-.macro save_guest_32bit_state
- skip_32bit_state x3, 1f
-
- add x3, x2, #CPU_SPSR_OFFSET(KVM_SPSR_ABT)
- mrs x4, spsr_abt
- mrs x5, spsr_und
- mrs x6, spsr_irq
- mrs x7, spsr_fiq
- stp x4, x5, [x3]
- stp x6, x7, [x3, #16]
-
- add x3, x2, #CPU_SYSREG_OFFSET(DACR32_EL2)
- mrs x4, dacr32_el2
- mrs x5, ifsr32_el2
- stp x4, x5, [x3]
-
- skip_fpsimd_state x8, 2f
- mrs x6, fpexc32_el2
- str x6, [x3, #16]
-2:
- skip_debug_state x8, 1f
- mrs x7, dbgvcr32_el2
- str x7, [x3, #24]
-1:
-.endm
-
-.macro restore_guest_32bit_state
- skip_32bit_state x3, 1f
-
- add x3, x2, #CPU_SPSR_OFFSET(KVM_SPSR_ABT)
- ldp x4, x5, [x3]
- ldp x6, x7, [x3, #16]
- msr spsr_abt, x4
- msr spsr_und, x5
- msr spsr_irq, x6
- msr spsr_fiq, x7
-
- add x3, x2, #CPU_SYSREG_OFFSET(DACR32_EL2)
- ldp x4, x5, [x3]
- msr dacr32_el2, x4
- msr ifsr32_el2, x5
-
- skip_debug_state x8, 1f
- ldr x7, [x3, #24]
- msr dbgvcr32_el2, x7
-1:
-.endm
-
-.macro activate_traps
- ldr x2, [x0, #VCPU_HCR_EL2]
-
- /*
- * We are about to set CPTR_EL2.TFP to trap all floating point
- * register accesses to EL2, however, the ARM ARM clearly states that
- * traps are only taken to EL2 if the operation would not otherwise
- * trap to EL1. Therefore, always make sure that for 32-bit guests,
- * we set FPEXC.EN to prevent traps to EL1, when setting the TFP bit.
- */
- tbnz x2, #HCR_RW_SHIFT, 99f // open code skip_32bit_state
- mov x3, #(1 << 30)
- msr fpexc32_el2, x3
- isb
-99:
- msr hcr_el2, x2
- mov x2, #CPTR_EL2_TTA
- orr x2, x2, #CPTR_EL2_TFP
- msr cptr_el2, x2
-
- mov x2, #(1 << 15) // Trap CP15 Cr=15
- msr hstr_el2, x2
-
- // Monitor Debug Config - see kvm_arm_setup_debug()
- ldr x2, [x0, #VCPU_MDCR_EL2]
- msr mdcr_el2, x2
-.endm
-
-.macro deactivate_traps
- mov x2, #HCR_RW
- msr hcr_el2, x2
- msr hstr_el2, xzr
-
- mrs x2, mdcr_el2
- and x2, x2, #MDCR_EL2_HPMN_MASK
- msr mdcr_el2, x2
-.endm
-
-.macro activate_vm
- ldr x1, [x0, #VCPU_KVM]
- kern_hyp_va x1
- ldr x2, [x1, #KVM_VTTBR]
- msr vttbr_el2, x2
-.endm
-
-.macro deactivate_vm
- msr vttbr_el2, xzr
-.endm
-
-/*
- * Call into the vgic backend for state saving
- */
-.macro save_vgic_state
-alternative_if_not ARM64_HAS_SYSREG_GIC_CPUIF
- bl __save_vgic_v2_state
-alternative_else
- bl __save_vgic_v3_state
-alternative_endif
- mrs x24, hcr_el2
- mov x25, #HCR_INT_OVERRIDE
- neg x25, x25
- and x24, x24, x25
- msr hcr_el2, x24
-.endm
-
-/*
- * Call into the vgic backend for state restoring
- */
-.macro restore_vgic_state
- mrs x24, hcr_el2
- ldr x25, [x0, #VCPU_IRQ_LINES]
- orr x24, x24, #HCR_INT_OVERRIDE
- orr x24, x24, x25
- msr hcr_el2, x24
-alternative_if_not ARM64_HAS_SYSREG_GIC_CPUIF
- bl __restore_vgic_v2_state
-alternative_else
- bl __restore_vgic_v3_state
-alternative_endif
-.endm
-
-.macro save_timer_state
- // x0: vcpu pointer
- ldr x2, [x0, #VCPU_KVM]
- kern_hyp_va x2
- ldr w3, [x2, #KVM_TIMER_ENABLED]
- cbz w3, 1f
-
- mrs x3, cntv_ctl_el0
- and x3, x3, #3
- str w3, [x0, #VCPU_TIMER_CNTV_CTL]
-
- isb
-
- mrs x3, cntv_cval_el0
- str x3, [x0, #VCPU_TIMER_CNTV_CVAL]
-
-1:
- // Disable the virtual timer
- msr cntv_ctl_el0, xzr
-
- // Allow physical timer/counter access for the host
- mrs x2, cnthctl_el2
- orr x2, x2, #3
- msr cnthctl_el2, x2
-
- // Clear cntvoff for the host
- msr cntvoff_el2, xzr
-.endm
-
-.macro restore_timer_state
- // x0: vcpu pointer
- // Disallow physical timer access for the guest
- // Physical counter access is allowed
- mrs x2, cnthctl_el2
- orr x2, x2, #1
- bic x2, x2, #2
- msr cnthctl_el2, x2
-
- ldr x2, [x0, #VCPU_KVM]
- kern_hyp_va x2
- ldr w3, [x2, #KVM_TIMER_ENABLED]
- cbz w3, 1f
-
- ldr x3, [x2, #KVM_TIMER_CNTVOFF]
- msr cntvoff_el2, x3
- ldr x2, [x0, #VCPU_TIMER_CNTV_CVAL]
- msr cntv_cval_el0, x2
- isb
-
- ldr w2, [x0, #VCPU_TIMER_CNTV_CTL]
- and x2, x2, #3
- msr cntv_ctl_el0, x2
-1:
-.endm
-
-__save_sysregs:
- save_sysregs
- ret
-
-__restore_sysregs:
- restore_sysregs
- ret
-
-/* Save debug state */
-__save_debug:
- // x2: ptr to CPU context
- // x3: ptr to debug reg struct
- // x4/x5/x6-22/x24-26: trashed
-
- mrs x26, id_aa64dfr0_el1
- ubfx x24, x26, #12, #4 // Extract BRPs
- ubfx x25, x26, #20, #4 // Extract WRPs
- mov w26, #15
- sub w24, w26, w24 // How many BPs to skip
- sub w25, w26, w25 // How many WPs to skip
-
- mov x5, x24
- add x4, x3, #DEBUG_BCR
- save_debug dbgbcr
- add x4, x3, #DEBUG_BVR
- save_debug dbgbvr
-
- mov x5, x25
- add x4, x3, #DEBUG_WCR
- save_debug dbgwcr
- add x4, x3, #DEBUG_WVR
- save_debug dbgwvr
-
- mrs x21, mdccint_el1
- str x21, [x2, #CPU_SYSREG_OFFSET(MDCCINT_EL1)]
- ret
-
-/* Restore debug state */
-__restore_debug:
- // x2: ptr to CPU context
- // x3: ptr to debug reg struct
- // x4/x5/x6-22/x24-26: trashed
-
- mrs x26, id_aa64dfr0_el1
- ubfx x24, x26, #12, #4 // Extract BRPs
- ubfx x25, x26, #20, #4 // Extract WRPs
- mov w26, #15
- sub w24, w26, w24 // How many BPs to skip
- sub w25, w26, w25 // How many WPs to skip
-
- mov x5, x24
- add x4, x3, #DEBUG_BCR
- restore_debug dbgbcr
- add x4, x3, #DEBUG_BVR
- restore_debug dbgbvr
-
- mov x5, x25
- add x4, x3, #DEBUG_WCR
- restore_debug dbgwcr
- add x4, x3, #DEBUG_WVR
- restore_debug dbgwvr
-
- ldr x21, [x2, #CPU_SYSREG_OFFSET(MDCCINT_EL1)]
- msr mdccint_el1, x21
-
- ret
-
-__save_fpsimd:
- skip_fpsimd_state x3, 1f
- save_fpsimd
-1: ret
-
-__restore_fpsimd:
- skip_fpsimd_state x3, 1f
- restore_fpsimd
-1: ret
-
-switch_to_guest_fpsimd:
- push x4, lr
-
- mrs x2, cptr_el2
- bic x2, x2, #CPTR_EL2_TFP
- msr cptr_el2, x2
- isb
-
- mrs x0, tpidr_el2
-
- ldr x2, [x0, #VCPU_HOST_CONTEXT]
- kern_hyp_va x2
- bl __save_fpsimd
-
- add x2, x0, #VCPU_CONTEXT
- bl __restore_fpsimd
-
- skip_32bit_state x3, 1f
- ldr x4, [x2, #CPU_SYSREG_OFFSET(FPEXC32_EL2)]
- msr fpexc32_el2, x4
-1:
- pop x4, lr
- pop x2, x3
- pop x0, x1
-
- eret
-
-/*
- * u64 __kvm_vcpu_run(struct kvm_vcpu *vcpu);
- *
- * This is the world switch. The first half of the function
- * deals with entering the guest, and anything from __kvm_vcpu_return
- * to the end of the function deals with reentering the host.
- * On the enter path, only x0 (vcpu pointer) must be preserved until
- * the last moment. On the exit path, x0 (vcpu pointer) and x1 (exception
- * code) must both be preserved until the epilogue.
- * In both cases, x2 points to the CPU context we're saving/restoring from/to.
- */
-ENTRY(__kvm_vcpu_run)
- kern_hyp_va x0
- msr tpidr_el2, x0 // Save the vcpu register
-
- // Host context
- ldr x2, [x0, #VCPU_HOST_CONTEXT]
- kern_hyp_va x2
-
- save_host_regs
- bl __save_sysregs
-
- compute_debug_state 1f
- add x3, x0, #VCPU_HOST_DEBUG_STATE
- bl __save_debug
-1:
- activate_traps
- activate_vm
-
- restore_vgic_state
- restore_timer_state
-
- // Guest context
- add x2, x0, #VCPU_CONTEXT
-
- // We must restore the 32-bit state before the sysregs, thanks
- // to Cortex-A57 erratum #852523.
- restore_guest_32bit_state
- bl __restore_sysregs
-
- skip_debug_state x3, 1f
- ldr x3, [x0, #VCPU_DEBUG_PTR]
- kern_hyp_va x3
- bl __restore_debug
-1:
- restore_guest_regs
-
- // That's it, no more messing around.
- eret
-
-__kvm_vcpu_return:
- // Assume x0 is the vcpu pointer, x1 the return code
- // Guest's x0-x3 are on the stack
-
- // Guest context
- add x2, x0, #VCPU_CONTEXT
-
- save_guest_regs
- bl __save_fpsimd
- bl __save_sysregs
-
- skip_debug_state x3, 1f
- ldr x3, [x0, #VCPU_DEBUG_PTR]
- kern_hyp_va x3
- bl __save_debug
-1:
- save_guest_32bit_state
-
- save_timer_state
- save_vgic_state
-
- deactivate_traps
- deactivate_vm
-
- // Host context
- ldr x2, [x0, #VCPU_HOST_CONTEXT]
- kern_hyp_va x2
-
- bl __restore_sysregs
- bl __restore_fpsimd
- /* Clear FPSIMD and Trace trapping */
- msr cptr_el2, xzr
-
- skip_debug_state x3, 1f
- // Clear the dirty flag for the next run, as all the state has
- // already been saved. Note that we nuke the whole 64bit word.
- // If we ever add more flags, we'll have to be more careful...
- str xzr, [x0, #VCPU_DEBUG_FLAGS]
- add x3, x0, #VCPU_HOST_DEBUG_STATE
- bl __restore_debug
-1:
- restore_host_regs
-
- mov x0, x1
- ret
-END(__kvm_vcpu_run)
-
-// void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
-ENTRY(__kvm_tlb_flush_vmid_ipa)
- dsb ishst
-
- kern_hyp_va x0
- ldr x2, [x0, #KVM_VTTBR]
- msr vttbr_el2, x2
- isb
-
- /*
- * We could do so much better if we had the VA as well.
- * Instead, we invalidate Stage-2 for this IPA, and the
- * whole of Stage-1. Weep...
- */
- lsr x1, x1, #12
- tlbi ipas2e1is, x1
- /*
- * We have to ensure completion of the invalidation at Stage-2,
- * since a table walk on another CPU could refill a TLB with a
- * complete (S1 + S2) walk based on the old Stage-2 mapping if
- * the Stage-1 invalidation happened first.
- */
- dsb ish
- tlbi vmalle1is
- dsb ish
- isb
-
- msr vttbr_el2, xzr
- ret
-ENDPROC(__kvm_tlb_flush_vmid_ipa)
-
-/**
- * void __kvm_tlb_flush_vmid(struct kvm *kvm) - Flush per-VMID TLBs
- * @struct kvm *kvm - pointer to kvm structure
- *
- * Invalidates all Stage 1 and 2 TLB entries for current VMID.
- */
-ENTRY(__kvm_tlb_flush_vmid)
- dsb ishst
-
- kern_hyp_va x0
- ldr x2, [x0, #KVM_VTTBR]
- msr vttbr_el2, x2
- isb
-
- tlbi vmalls12e1is
- dsb ish
- isb
-
- msr vttbr_el2, xzr
- ret
-ENDPROC(__kvm_tlb_flush_vmid)
-
-ENTRY(__kvm_flush_vm_context)
- dsb ishst
- tlbi alle1is
- ic ialluis
- dsb ish
- ret
-ENDPROC(__kvm_flush_vm_context)
-
-__kvm_hyp_panic:
- // Guess the context by looking at VTTBR:
- // If zero, then we're already a host.
- // Otherwise restore a minimal host context before panicing.
- mrs x0, vttbr_el2
- cbz x0, 1f
-
- mrs x0, tpidr_el2
-
- deactivate_traps
- deactivate_vm
-
- ldr x2, [x0, #VCPU_HOST_CONTEXT]
- kern_hyp_va x2
-
- bl __restore_sysregs
-
- /*
- * Make sure we have a valid host stack, and don't leave junk in the
- * frame pointer that will give us a misleading host stack unwinding.
- */
- ldr x22, [x2, #CPU_GP_REG_OFFSET(CPU_SP_EL1)]
- msr sp_el1, x22
- mov x29, xzr
-
-1: adr x0, __hyp_panic_str
- adr x1, 2f
- ldp x2, x3, [x1]
- sub x0, x0, x2
- add x0, x0, x3
- mrs x1, spsr_el2
- mrs x2, elr_el2
- mrs x3, esr_el2
- mrs x4, far_el2
- mrs x5, hpfar_el2
- mrs x6, par_el1
- mrs x7, tpidr_el2
-
- mov lr, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT |\
- PSR_MODE_EL1h)
- msr spsr_el2, lr
- ldr lr, =panic
- msr elr_el2, lr
- eret
-
- .align 3
-2: .quad HYP_PAGE_OFFSET
- .quad PAGE_OFFSET
-ENDPROC(__kvm_hyp_panic)
-
-__hyp_panic_str:
- .ascii "HYP panic:\nPS:%08x PC:%p ESR:%p\nFAR:%p HPFAR:%p PAR:%p\nVCPU:%p\n\0"
-
- .align 2
/*
* u64 kvm_call_hyp(void *hypfn, ...);
@@ -930,7 +31,7 @@ __hyp_panic_str:
* passed as x0, x1, and x2 (a maximum of 3 arguments in addition to the
* function pointer can be passed). The function being called must be mapped
* in Hyp mode (see init_hyp_mode in arch/arm/kvm/arm.c). Return values are
- * passed in r0 and r1.
+ * passed in x0.
*
* A function pointer with a value of 0 has a special meaning, and is
* used to implement __hyp_get_vectors in the same way as in
@@ -940,173 +41,3 @@ ENTRY(kvm_call_hyp)
hvc #0
ret
ENDPROC(kvm_call_hyp)
-
-.macro invalid_vector label, target
- .align 2
-\label:
- b \target
-ENDPROC(\label)
-.endm
-
- /* None of these should ever happen */
- invalid_vector el2t_sync_invalid, __kvm_hyp_panic
- invalid_vector el2t_irq_invalid, __kvm_hyp_panic
- invalid_vector el2t_fiq_invalid, __kvm_hyp_panic
- invalid_vector el2t_error_invalid, __kvm_hyp_panic
- invalid_vector el2h_sync_invalid, __kvm_hyp_panic
- invalid_vector el2h_irq_invalid, __kvm_hyp_panic
- invalid_vector el2h_fiq_invalid, __kvm_hyp_panic
- invalid_vector el2h_error_invalid, __kvm_hyp_panic
- invalid_vector el1_sync_invalid, __kvm_hyp_panic
- invalid_vector el1_irq_invalid, __kvm_hyp_panic
- invalid_vector el1_fiq_invalid, __kvm_hyp_panic
- invalid_vector el1_error_invalid, __kvm_hyp_panic
-
-el1_sync: // Guest trapped into EL2
- push x0, x1
- push x2, x3
-
- mrs x1, esr_el2
- lsr x2, x1, #ESR_ELx_EC_SHIFT
-
- cmp x2, #ESR_ELx_EC_HVC64
- b.ne el1_trap
-
- mrs x3, vttbr_el2 // If vttbr is valid, the 64bit guest
- cbnz x3, el1_trap // called HVC
-
- /* Here, we're pretty sure the host called HVC. */
- pop x2, x3
- pop x0, x1
-
- /* Check for __hyp_get_vectors */
- cbnz x0, 1f
- mrs x0, vbar_el2
- b 2f
-
-1: push lr, xzr
-
- /*
- * Compute the function address in EL2, and shuffle the parameters.
- */
- kern_hyp_va x0
- mov lr, x0
- mov x0, x1
- mov x1, x2
- mov x2, x3
- blr lr
-
- pop lr, xzr
-2: eret
-
-el1_trap:
- /*
- * x1: ESR
- * x2: ESR_EC
- */
-
- /* Guest accessed VFP/SIMD registers, save host, restore Guest */
- cmp x2, #ESR_ELx_EC_FP_ASIMD
- b.eq switch_to_guest_fpsimd
-
- cmp x2, #ESR_ELx_EC_DABT_LOW
- mov x0, #ESR_ELx_EC_IABT_LOW
- ccmp x2, x0, #4, ne
- b.ne 1f // Not an abort we care about
-
- /* This is an abort. Check for permission fault */
- and x2, x1, #ESR_ELx_FSC_TYPE
- cmp x2, #FSC_PERM
- b.ne 1f // Not a permission fault
-
- /*
- * Check for Stage-1 page table walk, which is guaranteed
- * to give a valid HPFAR_EL2.
- */
- tbnz x1, #7, 1f // S1PTW is set
-
- /* Preserve PAR_EL1 */
- mrs x3, par_el1
- push x3, xzr
-
- /*
- * Permission fault, HPFAR_EL2 is invalid.
- * Resolve the IPA the hard way using the guest VA.
- * Stage-1 translation already validated the memory access rights.
- * As such, we can use the EL1 translation regime, and don't have
- * to distinguish between EL0 and EL1 access.
- */
- mrs x2, far_el2
- at s1e1r, x2
- isb
-
- /* Read result */
- mrs x3, par_el1
- pop x0, xzr // Restore PAR_EL1 from the stack
- msr par_el1, x0
- tbnz x3, #0, 3f // Bail out if we failed the translation
- ubfx x3, x3, #12, #36 // Extract IPA
- lsl x3, x3, #4 // and present it like HPFAR
- b 2f
-
-1: mrs x3, hpfar_el2
- mrs x2, far_el2
-
-2: mrs x0, tpidr_el2
- str w1, [x0, #VCPU_ESR_EL2]
- str x2, [x0, #VCPU_FAR_EL2]
- str x3, [x0, #VCPU_HPFAR_EL2]
-
- mov x1, #ARM_EXCEPTION_TRAP
- b __kvm_vcpu_return
-
- /*
- * Translation failed. Just return to the guest and
- * let it fault again. Another CPU is probably playing
- * behind our back.
- */
-3: pop x2, x3
- pop x0, x1
-
- eret
-
-el1_irq:
- push x0, x1
- push x2, x3
- mrs x0, tpidr_el2
- mov x1, #ARM_EXCEPTION_IRQ
- b __kvm_vcpu_return
-
- .ltorg
-
- .align 11
-
-ENTRY(__kvm_hyp_vector)
- ventry el2t_sync_invalid // Synchronous EL2t
- ventry el2t_irq_invalid // IRQ EL2t
- ventry el2t_fiq_invalid // FIQ EL2t
- ventry el2t_error_invalid // Error EL2t
-
- ventry el2h_sync_invalid // Synchronous EL2h
- ventry el2h_irq_invalid // IRQ EL2h
- ventry el2h_fiq_invalid // FIQ EL2h
- ventry el2h_error_invalid // Error EL2h
-
- ventry el1_sync // Synchronous 64-bit EL1
- ventry el1_irq // IRQ 64-bit EL1
- ventry el1_fiq_invalid // FIQ 64-bit EL1
- ventry el1_error_invalid // Error 64-bit EL1
-
- ventry el1_sync // Synchronous 32-bit EL1
- ventry el1_irq // IRQ 32-bit EL1
- ventry el1_fiq_invalid // FIQ 32-bit EL1
- ventry el1_error_invalid // Error 32-bit EL1
-ENDPROC(__kvm_hyp_vector)
-
-
-ENTRY(__kvm_get_mdcr_el2)
- mrs x0, mdcr_el2
- ret
-ENDPROC(__kvm_get_mdcr_el2)
-
- .popsection
diff --git a/arch/arm64/kvm/hyp/Makefile b/arch/arm64/kvm/hyp/Makefile
new file mode 100644
index 000000000000..826032bc3945
--- /dev/null
+++ b/arch/arm64/kvm/hyp/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for Kernel-based Virtual Machine module, HYP part
+#
+
+obj-$(CONFIG_KVM_ARM_HOST) += vgic-v2-sr.o
+obj-$(CONFIG_KVM_ARM_HOST) += vgic-v3-sr.o
+obj-$(CONFIG_KVM_ARM_HOST) += timer-sr.o
+obj-$(CONFIG_KVM_ARM_HOST) += sysreg-sr.o
+obj-$(CONFIG_KVM_ARM_HOST) += debug-sr.o
+obj-$(CONFIG_KVM_ARM_HOST) += entry.o
+obj-$(CONFIG_KVM_ARM_HOST) += switch.o
+obj-$(CONFIG_KVM_ARM_HOST) += fpsimd.o
+obj-$(CONFIG_KVM_ARM_HOST) += tlb.o
+obj-$(CONFIG_KVM_ARM_HOST) += hyp-entry.o
diff --git a/arch/arm64/kvm/hyp/debug-sr.c b/arch/arm64/kvm/hyp/debug-sr.c
new file mode 100644
index 000000000000..c9c1e97501a9
--- /dev/null
+++ b/arch/arm64/kvm/hyp/debug-sr.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/compiler.h>
+#include <linux/kvm_host.h>
+
+#include <asm/kvm_asm.h>
+#include <asm/kvm_mmu.h>
+
+#include "hyp.h"
+
+#define read_debug(r,n) read_sysreg(r##n##_el1)
+#define write_debug(v,r,n) write_sysreg(v, r##n##_el1)
+
+#define save_debug(ptr,reg,nr) \
+ switch (nr) { \
+ case 15: ptr[15] = read_debug(reg, 15); \
+ case 14: ptr[14] = read_debug(reg, 14); \
+ case 13: ptr[13] = read_debug(reg, 13); \
+ case 12: ptr[12] = read_debug(reg, 12); \
+ case 11: ptr[11] = read_debug(reg, 11); \
+ case 10: ptr[10] = read_debug(reg, 10); \
+ case 9: ptr[9] = read_debug(reg, 9); \
+ case 8: ptr[8] = read_debug(reg, 8); \
+ case 7: ptr[7] = read_debug(reg, 7); \
+ case 6: ptr[6] = read_debug(reg, 6); \
+ case 5: ptr[5] = read_debug(reg, 5); \
+ case 4: ptr[4] = read_debug(reg, 4); \
+ case 3: ptr[3] = read_debug(reg, 3); \
+ case 2: ptr[2] = read_debug(reg, 2); \
+ case 1: ptr[1] = read_debug(reg, 1); \
+ default: ptr[0] = read_debug(reg, 0); \
+ }
+
+#define restore_debug(ptr,reg,nr) \
+ switch (nr) { \
+ case 15: write_debug(ptr[15], reg, 15); \
+ case 14: write_debug(ptr[14], reg, 14); \
+ case 13: write_debug(ptr[13], reg, 13); \
+ case 12: write_debug(ptr[12], reg, 12); \
+ case 11: write_debug(ptr[11], reg, 11); \
+ case 10: write_debug(ptr[10], reg, 10); \
+ case 9: write_debug(ptr[9], reg, 9); \
+ case 8: write_debug(ptr[8], reg, 8); \
+ case 7: write_debug(ptr[7], reg, 7); \
+ case 6: write_debug(ptr[6], reg, 6); \
+ case 5: write_debug(ptr[5], reg, 5); \
+ case 4: write_debug(ptr[4], reg, 4); \
+ case 3: write_debug(ptr[3], reg, 3); \
+ case 2: write_debug(ptr[2], reg, 2); \
+ case 1: write_debug(ptr[1], reg, 1); \
+ default: write_debug(ptr[0], reg, 0); \
+ }
+
+void __hyp_text __debug_save_state(struct kvm_vcpu *vcpu,
+ struct kvm_guest_debug_arch *dbg,
+ struct kvm_cpu_context *ctxt)
+{
+ u64 aa64dfr0;
+ int brps, wrps;
+
+ if (!(vcpu->arch.debug_flags & KVM_ARM64_DEBUG_DIRTY))
+ return;
+
+ aa64dfr0 = read_sysreg(id_aa64dfr0_el1);
+ brps = (aa64dfr0 >> 12) & 0xf;
+ wrps = (aa64dfr0 >> 20) & 0xf;
+
+ save_debug(dbg->dbg_bcr, dbgbcr, brps);
+ save_debug(dbg->dbg_bvr, dbgbvr, brps);
+ save_debug(dbg->dbg_wcr, dbgwcr, wrps);
+ save_debug(dbg->dbg_wvr, dbgwvr, wrps);
+
+ ctxt->sys_regs[MDCCINT_EL1] = read_sysreg(mdccint_el1);
+}
+
+void __hyp_text __debug_restore_state(struct kvm_vcpu *vcpu,
+ struct kvm_guest_debug_arch *dbg,
+ struct kvm_cpu_context *ctxt)
+{
+ u64 aa64dfr0;
+ int brps, wrps;
+
+ if (!(vcpu->arch.debug_flags & KVM_ARM64_DEBUG_DIRTY))
+ return;
+
+ aa64dfr0 = read_sysreg(id_aa64dfr0_el1);
+
+ brps = (aa64dfr0 >> 12) & 0xf;
+ wrps = (aa64dfr0 >> 20) & 0xf;
+
+ restore_debug(dbg->dbg_bcr, dbgbcr, brps);
+ restore_debug(dbg->dbg_bvr, dbgbvr, brps);
+ restore_debug(dbg->dbg_wcr, dbgwcr, wrps);
+ restore_debug(dbg->dbg_wvr, dbgwvr, wrps);
+
+ write_sysreg(ctxt->sys_regs[MDCCINT_EL1], mdccint_el1);
+}
+
+void __hyp_text __debug_cond_save_host_state(struct kvm_vcpu *vcpu)
+{
+ /* If any of KDE, MDE or KVM_ARM64_DEBUG_DIRTY is set, perform
+ * a full save/restore cycle. */
+ if ((vcpu->arch.ctxt.sys_regs[MDSCR_EL1] & DBG_MDSCR_KDE) ||
+ (vcpu->arch.ctxt.sys_regs[MDSCR_EL1] & DBG_MDSCR_MDE))
+ vcpu->arch.debug_flags |= KVM_ARM64_DEBUG_DIRTY;
+
+ __debug_save_state(vcpu, &vcpu->arch.host_debug_state,
+ kern_hyp_va(vcpu->arch.host_cpu_context));
+}
+
+void __hyp_text __debug_cond_restore_host_state(struct kvm_vcpu *vcpu)
+{
+ __debug_restore_state(vcpu, &vcpu->arch.host_debug_state,
+ kern_hyp_va(vcpu->arch.host_cpu_context));
+
+ if (vcpu->arch.debug_flags & KVM_ARM64_DEBUG_DIRTY)
+ vcpu->arch.debug_flags &= ~KVM_ARM64_DEBUG_DIRTY;
+}
+
+static u32 __hyp_text __debug_read_mdcr_el2(void)
+{
+ return read_sysreg(mdcr_el2);
+}
+
+__alias(__debug_read_mdcr_el2) u32 __kvm_get_mdcr_el2(void);
diff --git a/arch/arm64/kvm/hyp/entry.S b/arch/arm64/kvm/hyp/entry.S
new file mode 100644
index 000000000000..fd0fbe9b7e6a
--- /dev/null
+++ b/arch/arm64/kvm/hyp/entry.S
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/linkage.h>
+
+#include <asm/asm-offsets.h>
+#include <asm/assembler.h>
+#include <asm/fpsimdmacros.h>
+#include <asm/kvm.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_asm.h>
+#include <asm/kvm_mmu.h>
+
+#define CPU_GP_REG_OFFSET(x) (CPU_GP_REGS + x)
+#define CPU_XREG_OFFSET(x) CPU_GP_REG_OFFSET(CPU_USER_PT_REGS + 8*x)
+
+ .text
+ .pushsection .hyp.text, "ax"
+
+.macro save_callee_saved_regs ctxt
+ stp x19, x20, [\ctxt, #CPU_XREG_OFFSET(19)]
+ stp x21, x22, [\ctxt, #CPU_XREG_OFFSET(21)]
+ stp x23, x24, [\ctxt, #CPU_XREG_OFFSET(23)]
+ stp x25, x26, [\ctxt, #CPU_XREG_OFFSET(25)]
+ stp x27, x28, [\ctxt, #CPU_XREG_OFFSET(27)]
+ stp x29, lr, [\ctxt, #CPU_XREG_OFFSET(29)]
+.endm
+
+.macro restore_callee_saved_regs ctxt
+ ldp x19, x20, [\ctxt, #CPU_XREG_OFFSET(19)]
+ ldp x21, x22, [\ctxt, #CPU_XREG_OFFSET(21)]
+ ldp x23, x24, [\ctxt, #CPU_XREG_OFFSET(23)]
+ ldp x25, x26, [\ctxt, #CPU_XREG_OFFSET(25)]
+ ldp x27, x28, [\ctxt, #CPU_XREG_OFFSET(27)]
+ ldp x29, lr, [\ctxt, #CPU_XREG_OFFSET(29)]
+.endm
+
+/*
+ * u64 __guest_enter(struct kvm_vcpu *vcpu,
+ * struct kvm_cpu_context *host_ctxt);
+ */
+ENTRY(__guest_enter)
+ // x0: vcpu
+ // x1: host/guest context
+ // x2-x18: clobbered by macros
+
+ // Store the host regs
+ save_callee_saved_regs x1
+
+ // Preserve vcpu & host_ctxt for use at exit time
+ stp x0, x1, [sp, #-16]!
+
+ add x1, x0, #VCPU_CONTEXT
+
+ // Prepare x0-x1 for later restore by pushing them onto the stack
+ ldp x2, x3, [x1, #CPU_XREG_OFFSET(0)]
+ stp x2, x3, [sp, #-16]!
+
+ // x2-x18
+ ldp x2, x3, [x1, #CPU_XREG_OFFSET(2)]
+ ldp x4, x5, [x1, #CPU_XREG_OFFSET(4)]
+ ldp x6, x7, [x1, #CPU_XREG_OFFSET(6)]
+ ldp x8, x9, [x1, #CPU_XREG_OFFSET(8)]
+ ldp x10, x11, [x1, #CPU_XREG_OFFSET(10)]
+ ldp x12, x13, [x1, #CPU_XREG_OFFSET(12)]
+ ldp x14, x15, [x1, #CPU_XREG_OFFSET(14)]
+ ldp x16, x17, [x1, #CPU_XREG_OFFSET(16)]
+ ldr x18, [x1, #CPU_XREG_OFFSET(18)]
+
+ // x19-x29, lr
+ restore_callee_saved_regs x1
+
+ // Last bits of the 64bit state
+ ldp x0, x1, [sp], #16
+
+ // Do not touch any register after this!
+ eret
+ENDPROC(__guest_enter)
+
+ENTRY(__guest_exit)
+ // x0: vcpu
+ // x1: return code
+ // x2-x3: free
+ // x4-x29,lr: vcpu regs
+ // vcpu x0-x3 on the stack
+
+ add x2, x0, #VCPU_CONTEXT
+
+ stp x4, x5, [x2, #CPU_XREG_OFFSET(4)]
+ stp x6, x7, [x2, #CPU_XREG_OFFSET(6)]
+ stp x8, x9, [x2, #CPU_XREG_OFFSET(8)]
+ stp x10, x11, [x2, #CPU_XREG_OFFSET(10)]
+ stp x12, x13, [x2, #CPU_XREG_OFFSET(12)]
+ stp x14, x15, [x2, #CPU_XREG_OFFSET(14)]
+ stp x16, x17, [x2, #CPU_XREG_OFFSET(16)]
+ str x18, [x2, #CPU_XREG_OFFSET(18)]
+
+ ldp x6, x7, [sp], #16 // x2, x3
+ ldp x4, x5, [sp], #16 // x0, x1
+
+ stp x4, x5, [x2, #CPU_XREG_OFFSET(0)]
+ stp x6, x7, [x2, #CPU_XREG_OFFSET(2)]
+
+ save_callee_saved_regs x2
+
+ // Restore vcpu & host_ctxt from the stack
+ // (preserving return code in x1)
+ ldp x0, x2, [sp], #16
+ // Now restore the host regs
+ restore_callee_saved_regs x2
+
+ mov x0, x1
+ ret
+ENDPROC(__guest_exit)
+
+ENTRY(__fpsimd_guest_restore)
+ stp x4, lr, [sp, #-16]!
+
+ mrs x2, cptr_el2
+ bic x2, x2, #CPTR_EL2_TFP
+ msr cptr_el2, x2
+ isb
+
+ mrs x3, tpidr_el2
+
+ ldr x0, [x3, #VCPU_HOST_CONTEXT]
+ kern_hyp_va x0
+ add x0, x0, #CPU_GP_REG_OFFSET(CPU_FP_REGS)
+ bl __fpsimd_save_state
+
+ add x2, x3, #VCPU_CONTEXT
+ add x0, x2, #CPU_GP_REG_OFFSET(CPU_FP_REGS)
+ bl __fpsimd_restore_state
+
+ // Skip restoring fpexc32 for AArch64 guests
+ mrs x1, hcr_el2
+ tbnz x1, #HCR_RW_SHIFT, 1f
+ ldr x4, [x3, #VCPU_FPEXC32_EL2]
+ msr fpexc32_el2, x4
+1:
+ ldp x4, lr, [sp], #16
+ ldp x2, x3, [sp], #16
+ ldp x0, x1, [sp], #16
+
+ eret
+ENDPROC(__fpsimd_guest_restore)
diff --git a/arch/arm/kernel/psci-call.S b/arch/arm64/kvm/hyp/fpsimd.S
index a78e9e1e206d..da3f22c7f14a 100644
--- a/arch/arm/kernel/psci-call.S
+++ b/arch/arm64/kvm/hyp/fpsimd.S
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.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.
@@ -8,24 +11,23 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * Copyright (C) 2015 ARM Limited
- *
- * Author: Mark Rutland <mark.rutland@arm.com>
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/linkage.h>
-#include <asm/opcodes-sec.h>
-#include <asm/opcodes-virt.h>
+#include <asm/fpsimdmacros.h>
+
+ .text
+ .pushsection .hyp.text, "ax"
-/* int __invoke_psci_fn_hvc(u32 function_id, u32 arg0, u32 arg1, u32 arg2) */
-ENTRY(__invoke_psci_fn_hvc)
- __HVC(0)
- bx lr
-ENDPROC(__invoke_psci_fn_hvc)
+ENTRY(__fpsimd_save_state)
+ fpsimd_save x0, 1
+ ret
+ENDPROC(__fpsimd_save_state)
-/* int __invoke_psci_fn_smc(u32 function_id, u32 arg0, u32 arg1, u32 arg2) */
-ENTRY(__invoke_psci_fn_smc)
- __SMC(0)
- bx lr
-ENDPROC(__invoke_psci_fn_smc)
+ENTRY(__fpsimd_restore_state)
+ fpsimd_restore x0, 1
+ ret
+ENDPROC(__fpsimd_restore_state)
diff --git a/arch/arm64/kvm/hyp/hyp-entry.S b/arch/arm64/kvm/hyp/hyp-entry.S
new file mode 100644
index 000000000000..93e8d983c0bd
--- /dev/null
+++ b/arch/arm64/kvm/hyp/hyp-entry.S
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/linkage.h>
+
+#include <asm/alternative.h>
+#include <asm/assembler.h>
+#include <asm/asm-offsets.h>
+#include <asm/cpufeature.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_asm.h>
+#include <asm/kvm_mmu.h>
+
+ .text
+ .pushsection .hyp.text, "ax"
+
+.macro save_x0_to_x3
+ stp x0, x1, [sp, #-16]!
+ stp x2, x3, [sp, #-16]!
+.endm
+
+.macro restore_x0_to_x3
+ ldp x2, x3, [sp], #16
+ ldp x0, x1, [sp], #16
+.endm
+
+el1_sync: // Guest trapped into EL2
+ save_x0_to_x3
+
+ mrs x1, esr_el2
+ lsr x2, x1, #ESR_ELx_EC_SHIFT
+
+ cmp x2, #ESR_ELx_EC_HVC64
+ b.ne el1_trap
+
+ mrs x3, vttbr_el2 // If vttbr is valid, the 64bit guest
+ cbnz x3, el1_trap // called HVC
+
+ /* Here, we're pretty sure the host called HVC. */
+ restore_x0_to_x3
+
+ /* Check for __hyp_get_vectors */
+ cbnz x0, 1f
+ mrs x0, vbar_el2
+ b 2f
+
+1: stp lr, xzr, [sp, #-16]!
+
+ /*
+ * Compute the function address in EL2, and shuffle the parameters.
+ */
+ kern_hyp_va x0
+ mov lr, x0
+ mov x0, x1
+ mov x1, x2
+ mov x2, x3
+ blr lr
+
+ ldp lr, xzr, [sp], #16
+2: eret
+
+el1_trap:
+ /*
+ * x1: ESR
+ * x2: ESR_EC
+ */
+
+ /* Guest accessed VFP/SIMD registers, save host, restore Guest */
+ cmp x2, #ESR_ELx_EC_FP_ASIMD
+ b.eq __fpsimd_guest_restore
+
+ cmp x2, #ESR_ELx_EC_DABT_LOW
+ mov x0, #ESR_ELx_EC_IABT_LOW
+ ccmp x2, x0, #4, ne
+ b.ne 1f // Not an abort we care about
+
+ /* This is an abort. Check for permission fault */
+alternative_if_not ARM64_WORKAROUND_834220
+ and x2, x1, #ESR_ELx_FSC_TYPE
+ cmp x2, #FSC_PERM
+ b.ne 1f // Not a permission fault
+alternative_else
+ nop // Use the permission fault path to
+ nop // check for a valid S1 translation,
+ nop // regardless of the ESR value.
+alternative_endif
+
+ /*
+ * Check for Stage-1 page table walk, which is guaranteed
+ * to give a valid HPFAR_EL2.
+ */
+ tbnz x1, #7, 1f // S1PTW is set
+
+ /* Preserve PAR_EL1 */
+ mrs x3, par_el1
+ stp x3, xzr, [sp, #-16]!
+
+ /*
+ * Permission fault, HPFAR_EL2 is invalid.
+ * Resolve the IPA the hard way using the guest VA.
+ * Stage-1 translation already validated the memory access rights.
+ * As such, we can use the EL1 translation regime, and don't have
+ * to distinguish between EL0 and EL1 access.
+ */
+ mrs x2, far_el2
+ at s1e1r, x2
+ isb
+
+ /* Read result */
+ mrs x3, par_el1
+ ldp x0, xzr, [sp], #16 // Restore PAR_EL1 from the stack
+ msr par_el1, x0
+ tbnz x3, #0, 3f // Bail out if we failed the translation
+ ubfx x3, x3, #12, #36 // Extract IPA
+ lsl x3, x3, #4 // and present it like HPFAR
+ b 2f
+
+1: mrs x3, hpfar_el2
+ mrs x2, far_el2
+
+2: mrs x0, tpidr_el2
+ str w1, [x0, #VCPU_ESR_EL2]
+ str x2, [x0, #VCPU_FAR_EL2]
+ str x3, [x0, #VCPU_HPFAR_EL2]
+
+ mov x1, #ARM_EXCEPTION_TRAP
+ b __guest_exit
+
+ /*
+ * Translation failed. Just return to the guest and
+ * let it fault again. Another CPU is probably playing
+ * behind our back.
+ */
+3: restore_x0_to_x3
+
+ eret
+
+el1_irq:
+ save_x0_to_x3
+ mrs x0, tpidr_el2
+ mov x1, #ARM_EXCEPTION_IRQ
+ b __guest_exit
+
+ENTRY(__hyp_do_panic)
+ mov lr, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT |\
+ PSR_MODE_EL1h)
+ msr spsr_el2, lr
+ ldr lr, =panic
+ msr elr_el2, lr
+ eret
+ENDPROC(__hyp_do_panic)
+
+.macro invalid_vector label, target = __hyp_panic
+ .align 2
+\label:
+ b \target
+ENDPROC(\label)
+.endm
+
+ /* None of these should ever happen */
+ invalid_vector el2t_sync_invalid
+ invalid_vector el2t_irq_invalid
+ invalid_vector el2t_fiq_invalid
+ invalid_vector el2t_error_invalid
+ invalid_vector el2h_sync_invalid
+ invalid_vector el2h_irq_invalid
+ invalid_vector el2h_fiq_invalid
+ invalid_vector el2h_error_invalid
+ invalid_vector el1_sync_invalid
+ invalid_vector el1_irq_invalid
+ invalid_vector el1_fiq_invalid
+ invalid_vector el1_error_invalid
+
+ .ltorg
+
+ .align 11
+
+ENTRY(__kvm_hyp_vector)
+ ventry el2t_sync_invalid // Synchronous EL2t
+ ventry el2t_irq_invalid // IRQ EL2t
+ ventry el2t_fiq_invalid // FIQ EL2t
+ ventry el2t_error_invalid // Error EL2t
+
+ ventry el2h_sync_invalid // Synchronous EL2h
+ ventry el2h_irq_invalid // IRQ EL2h
+ ventry el2h_fiq_invalid // FIQ EL2h
+ ventry el2h_error_invalid // Error EL2h
+
+ ventry el1_sync // Synchronous 64-bit EL1
+ ventry el1_irq // IRQ 64-bit EL1
+ ventry el1_fiq_invalid // FIQ 64-bit EL1
+ ventry el1_error_invalid // Error 64-bit EL1
+
+ ventry el1_sync // Synchronous 32-bit EL1
+ ventry el1_irq // IRQ 32-bit EL1
+ ventry el1_fiq_invalid // FIQ 32-bit EL1
+ ventry el1_error_invalid // Error 32-bit EL1
+ENDPROC(__kvm_hyp_vector)
diff --git a/arch/arm64/kvm/hyp/hyp.h b/arch/arm64/kvm/hyp/hyp.h
new file mode 100644
index 000000000000..fb275178b6af
--- /dev/null
+++ b/arch/arm64/kvm/hyp/hyp.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ARM64_KVM_HYP_H__
+#define __ARM64_KVM_HYP_H__
+
+#include <linux/compiler.h>
+#include <linux/kvm_host.h>
+#include <asm/kvm_mmu.h>
+#include <asm/sysreg.h>
+
+#define __hyp_text __section(.hyp.text) notrace
+
+#define kern_hyp_va(v) (typeof(v))((unsigned long)(v) & HYP_PAGE_OFFSET_MASK)
+#define hyp_kern_va(v) (typeof(v))((unsigned long)(v) - HYP_PAGE_OFFSET \
+ + PAGE_OFFSET)
+
+/**
+ * hyp_alternate_select - Generates patchable code sequences that are
+ * used to switch between two implementations of a function, depending
+ * on the availability of a feature.
+ *
+ * @fname: a symbol name that will be defined as a function returning a
+ * function pointer whose type will match @orig and @alt
+ * @orig: A pointer to the default function, as returned by @fname when
+ * @cond doesn't hold
+ * @alt: A pointer to the alternate function, as returned by @fname
+ * when @cond holds
+ * @cond: a CPU feature (as described in asm/cpufeature.h)
+ */
+#define hyp_alternate_select(fname, orig, alt, cond) \
+typeof(orig) * __hyp_text fname(void) \
+{ \
+ typeof(alt) *val = orig; \
+ asm volatile(ALTERNATIVE("nop \n", \
+ "mov %0, %1 \n", \
+ cond) \
+ : "+r" (val) : "r" (alt)); \
+ return val; \
+}
+
+void __vgic_v2_save_state(struct kvm_vcpu *vcpu);
+void __vgic_v2_restore_state(struct kvm_vcpu *vcpu);
+
+void __vgic_v3_save_state(struct kvm_vcpu *vcpu);
+void __vgic_v3_restore_state(struct kvm_vcpu *vcpu);
+
+void __timer_save_state(struct kvm_vcpu *vcpu);
+void __timer_restore_state(struct kvm_vcpu *vcpu);
+
+void __sysreg_save_state(struct kvm_cpu_context *ctxt);
+void __sysreg_restore_state(struct kvm_cpu_context *ctxt);
+void __sysreg32_save_state(struct kvm_vcpu *vcpu);
+void __sysreg32_restore_state(struct kvm_vcpu *vcpu);
+
+void __debug_save_state(struct kvm_vcpu *vcpu,
+ struct kvm_guest_debug_arch *dbg,
+ struct kvm_cpu_context *ctxt);
+void __debug_restore_state(struct kvm_vcpu *vcpu,
+ struct kvm_guest_debug_arch *dbg,
+ struct kvm_cpu_context *ctxt);
+void __debug_cond_save_host_state(struct kvm_vcpu *vcpu);
+void __debug_cond_restore_host_state(struct kvm_vcpu *vcpu);
+
+void __fpsimd_save_state(struct user_fpsimd_state *fp_regs);
+void __fpsimd_restore_state(struct user_fpsimd_state *fp_regs);
+static inline bool __fpsimd_enabled(void)
+{
+ return !(read_sysreg(cptr_el2) & CPTR_EL2_TFP);
+}
+
+u64 __guest_enter(struct kvm_vcpu *vcpu, struct kvm_cpu_context *host_ctxt);
+void __noreturn __hyp_do_panic(unsigned long, ...);
+
+#endif /* __ARM64_KVM_HYP_H__ */
+
diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c
new file mode 100644
index 000000000000..ca8f5a5e2f96
--- /dev/null
+++ b/arch/arm64/kvm/hyp/switch.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hyp.h"
+
+static void __hyp_text __activate_traps(struct kvm_vcpu *vcpu)
+{
+ u64 val;
+
+ /*
+ * We are about to set CPTR_EL2.TFP to trap all floating point
+ * register accesses to EL2, however, the ARM ARM clearly states that
+ * traps are only taken to EL2 if the operation would not otherwise
+ * trap to EL1. Therefore, always make sure that for 32-bit guests,
+ * we set FPEXC.EN to prevent traps to EL1, when setting the TFP bit.
+ */
+ val = vcpu->arch.hcr_el2;
+ if (!(val & HCR_RW)) {
+ write_sysreg(1 << 30, fpexc32_el2);
+ isb();
+ }
+ write_sysreg(val, hcr_el2);
+ /* Trap on AArch32 cp15 c15 accesses (EL1 or EL0) */
+ write_sysreg(1 << 15, hstr_el2);
+ write_sysreg(CPTR_EL2_TTA | CPTR_EL2_TFP, cptr_el2);
+ write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2);
+}
+
+static void __hyp_text __deactivate_traps(struct kvm_vcpu *vcpu)
+{
+ write_sysreg(HCR_RW, hcr_el2);
+ write_sysreg(0, hstr_el2);
+ write_sysreg(read_sysreg(mdcr_el2) & MDCR_EL2_HPMN_MASK, mdcr_el2);
+ write_sysreg(0, cptr_el2);
+}
+
+static void __hyp_text __activate_vm(struct kvm_vcpu *vcpu)
+{
+ struct kvm *kvm = kern_hyp_va(vcpu->kvm);
+ write_sysreg(kvm->arch.vttbr, vttbr_el2);
+}
+
+static void __hyp_text __deactivate_vm(struct kvm_vcpu *vcpu)
+{
+ write_sysreg(0, vttbr_el2);
+}
+
+static hyp_alternate_select(__vgic_call_save_state,
+ __vgic_v2_save_state, __vgic_v3_save_state,
+ ARM64_HAS_SYSREG_GIC_CPUIF);
+
+static hyp_alternate_select(__vgic_call_restore_state,
+ __vgic_v2_restore_state, __vgic_v3_restore_state,
+ ARM64_HAS_SYSREG_GIC_CPUIF);
+
+static void __hyp_text __vgic_save_state(struct kvm_vcpu *vcpu)
+{
+ __vgic_call_save_state()(vcpu);
+ write_sysreg(read_sysreg(hcr_el2) & ~HCR_INT_OVERRIDE, hcr_el2);
+}
+
+static void __hyp_text __vgic_restore_state(struct kvm_vcpu *vcpu)
+{
+ u64 val;
+
+ val = read_sysreg(hcr_el2);
+ val |= HCR_INT_OVERRIDE;
+ val |= vcpu->arch.irq_lines;
+ write_sysreg(val, hcr_el2);
+
+ __vgic_call_restore_state()(vcpu);
+}
+
+static int __hyp_text __guest_run(struct kvm_vcpu *vcpu)
+{
+ struct kvm_cpu_context *host_ctxt;
+ struct kvm_cpu_context *guest_ctxt;
+ bool fp_enabled;
+ u64 exit_code;
+
+ vcpu = kern_hyp_va(vcpu);
+ write_sysreg(vcpu, tpidr_el2);
+
+ host_ctxt = kern_hyp_va(vcpu->arch.host_cpu_context);
+ guest_ctxt = &vcpu->arch.ctxt;
+
+ __sysreg_save_state(host_ctxt);
+ __debug_cond_save_host_state(vcpu);
+
+ __activate_traps(vcpu);
+ __activate_vm(vcpu);
+
+ __vgic_restore_state(vcpu);
+ __timer_restore_state(vcpu);
+
+ /*
+ * We must restore the 32-bit state before the sysregs, thanks
+ * to Cortex-A57 erratum #852523.
+ */
+ __sysreg32_restore_state(vcpu);
+ __sysreg_restore_state(guest_ctxt);
+ __debug_restore_state(vcpu, kern_hyp_va(vcpu->arch.debug_ptr), guest_ctxt);
+
+ /* Jump in the fire! */
+ exit_code = __guest_enter(vcpu, host_ctxt);
+ /* And we're baaack! */
+
+ fp_enabled = __fpsimd_enabled();
+
+ __sysreg_save_state(guest_ctxt);
+ __sysreg32_save_state(vcpu);
+ __timer_save_state(vcpu);
+ __vgic_save_state(vcpu);
+
+ __deactivate_traps(vcpu);
+ __deactivate_vm(vcpu);
+
+ __sysreg_restore_state(host_ctxt);
+
+ if (fp_enabled) {
+ __fpsimd_save_state(&guest_ctxt->gp_regs.fp_regs);
+ __fpsimd_restore_state(&host_ctxt->gp_regs.fp_regs);
+ }
+
+ __debug_save_state(vcpu, kern_hyp_va(vcpu->arch.debug_ptr), guest_ctxt);
+ __debug_cond_restore_host_state(vcpu);
+
+ return exit_code;
+}
+
+__alias(__guest_run) int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
+
+static const char __hyp_panic_string[] = "HYP panic:\nPS:%08llx PC:%016llx ESR:%08llx\nFAR:%016llx HPFAR:%016llx PAR:%016llx\nVCPU:%p\n";
+
+void __hyp_text __noreturn __hyp_panic(void)
+{
+ unsigned long str_va = (unsigned long)__hyp_panic_string;
+ u64 spsr = read_sysreg(spsr_el2);
+ u64 elr = read_sysreg(elr_el2);
+ u64 par = read_sysreg(par_el1);
+
+ if (read_sysreg(vttbr_el2)) {
+ struct kvm_vcpu *vcpu;
+ struct kvm_cpu_context *host_ctxt;
+
+ vcpu = (struct kvm_vcpu *)read_sysreg(tpidr_el2);
+ host_ctxt = kern_hyp_va(vcpu->arch.host_cpu_context);
+ __deactivate_traps(vcpu);
+ __deactivate_vm(vcpu);
+ __sysreg_restore_state(host_ctxt);
+ }
+
+ /* Call panic for real */
+ __hyp_do_panic(hyp_kern_va(str_va),
+ spsr, elr,
+ read_sysreg(esr_el2), read_sysreg(far_el2),
+ read_sysreg(hpfar_el2), par,
+ (void *)read_sysreg(tpidr_el2));
+
+ unreachable();
+}
diff --git a/arch/arm64/kvm/hyp/sysreg-sr.c b/arch/arm64/kvm/hyp/sysreg-sr.c
new file mode 100644
index 000000000000..425630980229
--- /dev/null
+++ b/arch/arm64/kvm/hyp/sysreg-sr.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2012-2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/compiler.h>
+#include <linux/kvm_host.h>
+
+#include <asm/kvm_asm.h>
+#include <asm/kvm_mmu.h>
+
+#include "hyp.h"
+
+/* ctxt is already in the HYP VA space */
+void __hyp_text __sysreg_save_state(struct kvm_cpu_context *ctxt)
+{
+ ctxt->sys_regs[MPIDR_EL1] = read_sysreg(vmpidr_el2);
+ ctxt->sys_regs[CSSELR_EL1] = read_sysreg(csselr_el1);
+ ctxt->sys_regs[SCTLR_EL1] = read_sysreg(sctlr_el1);
+ ctxt->sys_regs[ACTLR_EL1] = read_sysreg(actlr_el1);
+ ctxt->sys_regs[CPACR_EL1] = read_sysreg(cpacr_el1);
+ ctxt->sys_regs[TTBR0_EL1] = read_sysreg(ttbr0_el1);
+ ctxt->sys_regs[TTBR1_EL1] = read_sysreg(ttbr1_el1);
+ ctxt->sys_regs[TCR_EL1] = read_sysreg(tcr_el1);
+ ctxt->sys_regs[ESR_EL1] = read_sysreg(esr_el1);
+ ctxt->sys_regs[AFSR0_EL1] = read_sysreg(afsr0_el1);
+ ctxt->sys_regs[AFSR1_EL1] = read_sysreg(afsr1_el1);
+ ctxt->sys_regs[FAR_EL1] = read_sysreg(far_el1);
+ ctxt->sys_regs[MAIR_EL1] = read_sysreg(mair_el1);
+ ctxt->sys_regs[VBAR_EL1] = read_sysreg(vbar_el1);
+ ctxt->sys_regs[CONTEXTIDR_EL1] = read_sysreg(contextidr_el1);
+ ctxt->sys_regs[TPIDR_EL0] = read_sysreg(tpidr_el0);
+ ctxt->sys_regs[TPIDRRO_EL0] = read_sysreg(tpidrro_el0);
+ ctxt->sys_regs[TPIDR_EL1] = read_sysreg(tpidr_el1);
+ ctxt->sys_regs[AMAIR_EL1] = read_sysreg(amair_el1);
+ ctxt->sys_regs[CNTKCTL_EL1] = read_sysreg(cntkctl_el1);
+ ctxt->sys_regs[PAR_EL1] = read_sysreg(par_el1);
+ ctxt->sys_regs[MDSCR_EL1] = read_sysreg(mdscr_el1);
+
+ ctxt->gp_regs.regs.sp = read_sysreg(sp_el0);
+ ctxt->gp_regs.regs.pc = read_sysreg(elr_el2);
+ ctxt->gp_regs.regs.pstate = read_sysreg(spsr_el2);
+ ctxt->gp_regs.sp_el1 = read_sysreg(sp_el1);
+ ctxt->gp_regs.elr_el1 = read_sysreg(elr_el1);
+ ctxt->gp_regs.spsr[KVM_SPSR_EL1]= read_sysreg(spsr_el1);
+}
+
+void __hyp_text __sysreg_restore_state(struct kvm_cpu_context *ctxt)
+{
+ write_sysreg(ctxt->sys_regs[MPIDR_EL1], vmpidr_el2);
+ write_sysreg(ctxt->sys_regs[CSSELR_EL1], csselr_el1);
+ write_sysreg(ctxt->sys_regs[SCTLR_EL1], sctlr_el1);
+ write_sysreg(ctxt->sys_regs[ACTLR_EL1], actlr_el1);
+ write_sysreg(ctxt->sys_regs[CPACR_EL1], cpacr_el1);
+ write_sysreg(ctxt->sys_regs[TTBR0_EL1], ttbr0_el1);
+ write_sysreg(ctxt->sys_regs[TTBR1_EL1], ttbr1_el1);
+ write_sysreg(ctxt->sys_regs[TCR_EL1], tcr_el1);
+ write_sysreg(ctxt->sys_regs[ESR_EL1], esr_el1);
+ write_sysreg(ctxt->sys_regs[AFSR0_EL1], afsr0_el1);
+ write_sysreg(ctxt->sys_regs[AFSR1_EL1], afsr1_el1);
+ write_sysreg(ctxt->sys_regs[FAR_EL1], far_el1);
+ write_sysreg(ctxt->sys_regs[MAIR_EL1], mair_el1);
+ write_sysreg(ctxt->sys_regs[VBAR_EL1], vbar_el1);
+ write_sysreg(ctxt->sys_regs[CONTEXTIDR_EL1], contextidr_el1);
+ write_sysreg(ctxt->sys_regs[TPIDR_EL0], tpidr_el0);
+ write_sysreg(ctxt->sys_regs[TPIDRRO_EL0], tpidrro_el0);
+ write_sysreg(ctxt->sys_regs[TPIDR_EL1], tpidr_el1);
+ write_sysreg(ctxt->sys_regs[AMAIR_EL1], amair_el1);
+ write_sysreg(ctxt->sys_regs[CNTKCTL_EL1], cntkctl_el1);
+ write_sysreg(ctxt->sys_regs[PAR_EL1], par_el1);
+ write_sysreg(ctxt->sys_regs[MDSCR_EL1], mdscr_el1);
+
+ write_sysreg(ctxt->gp_regs.regs.sp, sp_el0);
+ write_sysreg(ctxt->gp_regs.regs.pc, elr_el2);
+ write_sysreg(ctxt->gp_regs.regs.pstate, spsr_el2);
+ write_sysreg(ctxt->gp_regs.sp_el1, sp_el1);
+ write_sysreg(ctxt->gp_regs.elr_el1, elr_el1);
+ write_sysreg(ctxt->gp_regs.spsr[KVM_SPSR_EL1], spsr_el1);
+}
+
+void __hyp_text __sysreg32_save_state(struct kvm_vcpu *vcpu)
+{
+ u64 *spsr, *sysreg;
+
+ if (read_sysreg(hcr_el2) & HCR_RW)
+ return;
+
+ spsr = vcpu->arch.ctxt.gp_regs.spsr;
+ sysreg = vcpu->arch.ctxt.sys_regs;
+
+ spsr[KVM_SPSR_ABT] = read_sysreg(spsr_abt);
+ spsr[KVM_SPSR_UND] = read_sysreg(spsr_und);
+ spsr[KVM_SPSR_IRQ] = read_sysreg(spsr_irq);
+ spsr[KVM_SPSR_FIQ] = read_sysreg(spsr_fiq);
+
+ sysreg[DACR32_EL2] = read_sysreg(dacr32_el2);
+ sysreg[IFSR32_EL2] = read_sysreg(ifsr32_el2);
+
+ if (__fpsimd_enabled())
+ sysreg[FPEXC32_EL2] = read_sysreg(fpexc32_el2);
+
+ if (vcpu->arch.debug_flags & KVM_ARM64_DEBUG_DIRTY)
+ sysreg[DBGVCR32_EL2] = read_sysreg(dbgvcr32_el2);
+}
+
+void __hyp_text __sysreg32_restore_state(struct kvm_vcpu *vcpu)
+{
+ u64 *spsr, *sysreg;
+
+ if (read_sysreg(hcr_el2) & HCR_RW)
+ return;
+
+ spsr = vcpu->arch.ctxt.gp_regs.spsr;
+ sysreg = vcpu->arch.ctxt.sys_regs;
+
+ write_sysreg(spsr[KVM_SPSR_ABT], spsr_abt);
+ write_sysreg(spsr[KVM_SPSR_UND], spsr_und);
+ write_sysreg(spsr[KVM_SPSR_IRQ], spsr_irq);
+ write_sysreg(spsr[KVM_SPSR_FIQ], spsr_fiq);
+
+ write_sysreg(sysreg[DACR32_EL2], dacr32_el2);
+ write_sysreg(sysreg[IFSR32_EL2], ifsr32_el2);
+
+ if (vcpu->arch.debug_flags & KVM_ARM64_DEBUG_DIRTY)
+ write_sysreg(sysreg[DBGVCR32_EL2], dbgvcr32_el2);
+}
diff --git a/arch/arm64/kvm/hyp/timer-sr.c b/arch/arm64/kvm/hyp/timer-sr.c
new file mode 100644
index 000000000000..1051e5d7320f
--- /dev/null
+++ b/arch/arm64/kvm/hyp/timer-sr.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2012-2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <clocksource/arm_arch_timer.h>
+#include <linux/compiler.h>
+#include <linux/kvm_host.h>
+
+#include <asm/kvm_mmu.h>
+
+#include "hyp.h"
+
+/* vcpu is already in the HYP VA space */
+void __hyp_text __timer_save_state(struct kvm_vcpu *vcpu)
+{
+ struct kvm *kvm = kern_hyp_va(vcpu->kvm);
+ struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+ u64 val;
+
+ if (kvm->arch.timer.enabled) {
+ timer->cntv_ctl = read_sysreg(cntv_ctl_el0);
+ timer->cntv_cval = read_sysreg(cntv_cval_el0);
+ }
+
+ /* Disable the virtual timer */
+ write_sysreg(0, cntv_ctl_el0);
+
+ /* Allow physical timer/counter access for the host */
+ val = read_sysreg(cnthctl_el2);
+ val |= CNTHCTL_EL1PCTEN | CNTHCTL_EL1PCEN;
+ write_sysreg(val, cnthctl_el2);
+
+ /* Clear cntvoff for the host */
+ write_sysreg(0, cntvoff_el2);
+}
+
+void __hyp_text __timer_restore_state(struct kvm_vcpu *vcpu)
+{
+ struct kvm *kvm = kern_hyp_va(vcpu->kvm);
+ struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+ u64 val;
+
+ /*
+ * Disallow physical timer access for the guest
+ * Physical counter access is allowed
+ */
+ val = read_sysreg(cnthctl_el2);
+ val &= ~CNTHCTL_EL1PCEN;
+ val |= CNTHCTL_EL1PCTEN;
+ write_sysreg(val, cnthctl_el2);
+
+ if (kvm->arch.timer.enabled) {
+ write_sysreg(kvm->arch.timer.cntvoff, cntvoff_el2);
+ write_sysreg(timer->cntv_cval, cntv_cval_el0);
+ isb();
+ write_sysreg(timer->cntv_ctl, cntv_ctl_el0);
+ }
+}
diff --git a/arch/arm64/kvm/hyp/tlb.c b/arch/arm64/kvm/hyp/tlb.c
new file mode 100644
index 000000000000..2a7e0d838698
--- /dev/null
+++ b/arch/arm64/kvm/hyp/tlb.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hyp.h"
+
+static void __hyp_text __tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa)
+{
+ dsb(ishst);
+
+ /* Switch to requested VMID */
+ kvm = kern_hyp_va(kvm);
+ write_sysreg(kvm->arch.vttbr, vttbr_el2);
+ isb();
+
+ /*
+ * We could do so much better if we had the VA as well.
+ * Instead, we invalidate Stage-2 for this IPA, and the
+ * whole of Stage-1. Weep...
+ */
+ ipa >>= 12;
+ asm volatile("tlbi ipas2e1is, %0" : : "r" (ipa));
+
+ /*
+ * We have to ensure completion of the invalidation at Stage-2,
+ * since a table walk on another CPU could refill a TLB with a
+ * complete (S1 + S2) walk based on the old Stage-2 mapping if
+ * the Stage-1 invalidation happened first.
+ */
+ dsb(ish);
+ asm volatile("tlbi vmalle1is" : : );
+ dsb(ish);
+ isb();
+
+ write_sysreg(0, vttbr_el2);
+}
+
+__alias(__tlb_flush_vmid_ipa) void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm,
+ phys_addr_t ipa);
+
+static void __hyp_text __tlb_flush_vmid(struct kvm *kvm)
+{
+ dsb(ishst);
+
+ /* Switch to requested VMID */
+ kvm = kern_hyp_va(kvm);
+ write_sysreg(kvm->arch.vttbr, vttbr_el2);
+ isb();
+
+ asm volatile("tlbi vmalls12e1is" : : );
+ dsb(ish);
+ isb();
+
+ write_sysreg(0, vttbr_el2);
+}
+
+__alias(__tlb_flush_vmid) void __kvm_tlb_flush_vmid(struct kvm *kvm);
+
+static void __hyp_text __tlb_flush_vm_context(void)
+{
+ dsb(ishst);
+ asm volatile("tlbi alle1is \n"
+ "ic ialluis ": : );
+ dsb(ish);
+}
+
+__alias(__tlb_flush_vm_context) void __kvm_flush_vm_context(void);
diff --git a/arch/arm64/kvm/hyp/vgic-v2-sr.c b/arch/arm64/kvm/hyp/vgic-v2-sr.c
new file mode 100644
index 000000000000..e71761238cfc
--- /dev/null
+++ b/arch/arm64/kvm/hyp/vgic-v2-sr.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2012-2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/compiler.h>
+#include <linux/irqchip/arm-gic.h>
+#include <linux/kvm_host.h>
+
+#include <asm/kvm_mmu.h>
+
+#include "hyp.h"
+
+/* vcpu is already in the HYP VA space */
+void __hyp_text __vgic_v2_save_state(struct kvm_vcpu *vcpu)
+{
+ struct kvm *kvm = kern_hyp_va(vcpu->kvm);
+ struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
+ struct vgic_dist *vgic = &kvm->arch.vgic;
+ void __iomem *base = kern_hyp_va(vgic->vctrl_base);
+ u32 eisr0, eisr1, elrsr0, elrsr1;
+ int i, nr_lr;
+
+ if (!base)
+ return;
+
+ nr_lr = vcpu->arch.vgic_cpu.nr_lr;
+ cpu_if->vgic_vmcr = readl_relaxed(base + GICH_VMCR);
+ cpu_if->vgic_misr = readl_relaxed(base + GICH_MISR);
+ eisr0 = readl_relaxed(base + GICH_EISR0);
+ elrsr0 = readl_relaxed(base + GICH_ELRSR0);
+ if (unlikely(nr_lr > 32)) {
+ eisr1 = readl_relaxed(base + GICH_EISR1);
+ elrsr1 = readl_relaxed(base + GICH_ELRSR1);
+ } else {
+ eisr1 = elrsr1 = 0;
+ }
+#ifdef CONFIG_CPU_BIG_ENDIAN
+ cpu_if->vgic_eisr = ((u64)eisr0 << 32) | eisr1;
+ cpu_if->vgic_elrsr = ((u64)elrsr0 << 32) | elrsr1;
+#else
+ cpu_if->vgic_eisr = ((u64)eisr1 << 32) | eisr0;
+ cpu_if->vgic_elrsr = ((u64)elrsr1 << 32) | elrsr0;
+#endif
+ cpu_if->vgic_apr = readl_relaxed(base + GICH_APR);
+
+ writel_relaxed(0, base + GICH_HCR);
+
+ for (i = 0; i < nr_lr; i++)
+ cpu_if->vgic_lr[i] = readl_relaxed(base + GICH_LR0 + (i * 4));
+}
+
+/* vcpu is already in the HYP VA space */
+void __hyp_text __vgic_v2_restore_state(struct kvm_vcpu *vcpu)
+{
+ struct kvm *kvm = kern_hyp_va(vcpu->kvm);
+ struct vgic_v2_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v2;
+ struct vgic_dist *vgic = &kvm->arch.vgic;
+ void __iomem *base = kern_hyp_va(vgic->vctrl_base);
+ int i, nr_lr;
+
+ if (!base)
+ return;
+
+ writel_relaxed(cpu_if->vgic_hcr, base + GICH_HCR);
+ writel_relaxed(cpu_if->vgic_vmcr, base + GICH_VMCR);
+ writel_relaxed(cpu_if->vgic_apr, base + GICH_APR);
+
+ nr_lr = vcpu->arch.vgic_cpu.nr_lr;
+ for (i = 0; i < nr_lr; i++)
+ writel_relaxed(cpu_if->vgic_lr[i], base + GICH_LR0 + (i * 4));
+}
diff --git a/arch/arm64/kvm/hyp/vgic-v3-sr.c b/arch/arm64/kvm/hyp/vgic-v3-sr.c
new file mode 100644
index 000000000000..9142e082f5f3
--- /dev/null
+++ b/arch/arm64/kvm/hyp/vgic-v3-sr.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2012-2015 - ARM Ltd
+ * Author: Marc Zyngier <marc.zyngier@arm.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/compiler.h>
+#include <linux/irqchip/arm-gic-v3.h>
+#include <linux/kvm_host.h>
+
+#include <asm/kvm_mmu.h>
+
+#include "hyp.h"
+
+#define vtr_to_max_lr_idx(v) ((v) & 0xf)
+#define vtr_to_nr_pri_bits(v) (((u32)(v) >> 29) + 1)
+
+#define read_gicreg(r) \
+ ({ \
+ u64 reg; \
+ asm volatile("mrs_s %0, " __stringify(r) : "=r" (reg)); \
+ reg; \
+ })
+
+#define write_gicreg(v,r) \
+ do { \
+ u64 __val = (v); \
+ asm volatile("msr_s " __stringify(r) ", %0" : : "r" (__val));\
+ } while (0)
+
+/* vcpu is already in the HYP VA space */
+void __hyp_text __vgic_v3_save_state(struct kvm_vcpu *vcpu)
+{
+ struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3;
+ u64 val;
+ u32 max_lr_idx, nr_pri_bits;
+
+ /*
+ * Make sure stores to the GIC via the memory mapped interface
+ * are now visible to the system register interface.
+ */
+ dsb(st);
+
+ cpu_if->vgic_vmcr = read_gicreg(ICH_VMCR_EL2);
+ cpu_if->vgic_misr = read_gicreg(ICH_MISR_EL2);
+ cpu_if->vgic_eisr = read_gicreg(ICH_EISR_EL2);
+ cpu_if->vgic_elrsr = read_gicreg(ICH_ELSR_EL2);
+
+ write_gicreg(0, ICH_HCR_EL2);
+ val = read_gicreg(ICH_VTR_EL2);
+ max_lr_idx = vtr_to_max_lr_idx(val);
+ nr_pri_bits = vtr_to_nr_pri_bits(val);
+
+ switch (max_lr_idx) {
+ case 15:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(15)] = read_gicreg(ICH_LR15_EL2);
+ case 14:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(14)] = read_gicreg(ICH_LR14_EL2);
+ case 13:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(13)] = read_gicreg(ICH_LR13_EL2);
+ case 12:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(12)] = read_gicreg(ICH_LR12_EL2);
+ case 11:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(11)] = read_gicreg(ICH_LR11_EL2);
+ case 10:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(10)] = read_gicreg(ICH_LR10_EL2);
+ case 9:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(9)] = read_gicreg(ICH_LR9_EL2);
+ case 8:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(8)] = read_gicreg(ICH_LR8_EL2);
+ case 7:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(7)] = read_gicreg(ICH_LR7_EL2);
+ case 6:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(6)] = read_gicreg(ICH_LR6_EL2);
+ case 5:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(5)] = read_gicreg(ICH_LR5_EL2);
+ case 4:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(4)] = read_gicreg(ICH_LR4_EL2);
+ case 3:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(3)] = read_gicreg(ICH_LR3_EL2);
+ case 2:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(2)] = read_gicreg(ICH_LR2_EL2);
+ case 1:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(1)] = read_gicreg(ICH_LR1_EL2);
+ case 0:
+ cpu_if->vgic_lr[VGIC_V3_LR_INDEX(0)] = read_gicreg(ICH_LR0_EL2);
+ }
+
+ switch (nr_pri_bits) {
+ case 7:
+ cpu_if->vgic_ap0r[3] = read_gicreg(ICH_AP0R3_EL2);
+ cpu_if->vgic_ap0r[2] = read_gicreg(ICH_AP0R2_EL2);
+ case 6:
+ cpu_if->vgic_ap0r[1] = read_gicreg(ICH_AP0R1_EL2);
+ default:
+ cpu_if->vgic_ap0r[0] = read_gicreg(ICH_AP0R0_EL2);
+ }
+
+ switch (nr_pri_bits) {
+ case 7:
+ cpu_if->vgic_ap1r[3] = read_gicreg(ICH_AP1R3_EL2);
+ cpu_if->vgic_ap1r[2] = read_gicreg(ICH_AP1R2_EL2);
+ case 6:
+ cpu_if->vgic_ap1r[1] = read_gicreg(ICH_AP1R1_EL2);
+ default:
+ cpu_if->vgic_ap1r[0] = read_gicreg(ICH_AP1R0_EL2);
+ }
+
+ val = read_gicreg(ICC_SRE_EL2);
+ write_gicreg(val | ICC_SRE_EL2_ENABLE, ICC_SRE_EL2);
+ isb(); /* Make sure ENABLE is set at EL2 before setting SRE at EL1 */
+ write_gicreg(1, ICC_SRE_EL1);
+}
+
+void __hyp_text __vgic_v3_restore_state(struct kvm_vcpu *vcpu)
+{
+ struct vgic_v3_cpu_if *cpu_if = &vcpu->arch.vgic_cpu.vgic_v3;
+ u64 val;
+ u32 max_lr_idx, nr_pri_bits;
+
+ /*
+ * VFIQEn is RES1 if ICC_SRE_EL1.SRE is 1. This causes a
+ * Group0 interrupt (as generated in GICv2 mode) to be
+ * delivered as a FIQ to the guest, with potentially fatal
+ * consequences. So we must make sure that ICC_SRE_EL1 has
+ * been actually programmed with the value we want before
+ * starting to mess with the rest of the GIC.
+ */
+ write_gicreg(cpu_if->vgic_sre, ICC_SRE_EL1);
+ isb();
+
+ write_gicreg(cpu_if->vgic_hcr, ICH_HCR_EL2);
+ write_gicreg(cpu_if->vgic_vmcr, ICH_VMCR_EL2);
+
+ val = read_gicreg(ICH_VTR_EL2);
+ max_lr_idx = vtr_to_max_lr_idx(val);
+ nr_pri_bits = vtr_to_nr_pri_bits(val);
+
+ switch (nr_pri_bits) {
+ case 7:
+ write_gicreg(cpu_if->vgic_ap1r[3], ICH_AP1R3_EL2);
+ write_gicreg(cpu_if->vgic_ap1r[2], ICH_AP1R2_EL2);
+ case 6:
+ write_gicreg(cpu_if->vgic_ap1r[1], ICH_AP1R1_EL2);
+ default:
+ write_gicreg(cpu_if->vgic_ap1r[0], ICH_AP1R0_EL2);
+ }
+
+ switch (nr_pri_bits) {
+ case 7:
+ write_gicreg(cpu_if->vgic_ap0r[3], ICH_AP0R3_EL2);
+ write_gicreg(cpu_if->vgic_ap0r[2], ICH_AP0R2_EL2);
+ case 6:
+ write_gicreg(cpu_if->vgic_ap0r[1], ICH_AP0R1_EL2);
+ default:
+ write_gicreg(cpu_if->vgic_ap0r[0], ICH_AP0R0_EL2);
+ }
+
+ switch (max_lr_idx) {
+ case 15:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(15)], ICH_LR15_EL2);
+ case 14:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(14)], ICH_LR14_EL2);
+ case 13:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(13)], ICH_LR13_EL2);
+ case 12:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(12)], ICH_LR12_EL2);
+ case 11:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(11)], ICH_LR11_EL2);
+ case 10:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(10)], ICH_LR10_EL2);
+ case 9:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(9)], ICH_LR9_EL2);
+ case 8:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(8)], ICH_LR8_EL2);
+ case 7:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(7)], ICH_LR7_EL2);
+ case 6:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(6)], ICH_LR6_EL2);
+ case 5:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(5)], ICH_LR5_EL2);
+ case 4:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(4)], ICH_LR4_EL2);
+ case 3:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(3)], ICH_LR3_EL2);
+ case 2:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(2)], ICH_LR2_EL2);
+ case 1:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(1)], ICH_LR1_EL2);
+ case 0:
+ write_gicreg(cpu_if->vgic_lr[VGIC_V3_LR_INDEX(0)], ICH_LR0_EL2);
+ }
+
+ /*
+ * Ensures that the above will have reached the
+ * (re)distributors. This ensure the guest will read the
+ * correct values from the memory-mapped interface.
+ */
+ isb();
+ dsb(sy);
+
+ /*
+ * Prevent the guest from touching the GIC system registers if
+ * SRE isn't enabled for GICv3 emulation.
+ */
+ if (!cpu_if->vgic_sre) {
+ write_gicreg(read_gicreg(ICC_SRE_EL2) & ~ICC_SRE_EL2_ENABLE,
+ ICC_SRE_EL2);
+ }
+}
+
+static u64 __hyp_text __vgic_v3_read_ich_vtr_el2(void)
+{
+ return read_gicreg(ICH_VTR_EL2);
+}
+
+__alias(__vgic_v3_read_ich_vtr_el2) u64 __vgic_v3_get_ich_vtr_el2(void);
diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c
index 85c57158dcd9..648112e90ed5 100644
--- a/arch/arm64/kvm/inject_fault.c
+++ b/arch/arm64/kvm/inject_fault.c
@@ -48,7 +48,7 @@ static void prepare_fault32(struct kvm_vcpu *vcpu, u32 mode, u32 vect_offset)
/* Note: These now point to the banked copies */
*vcpu_spsr(vcpu) = new_spsr_value;
- *vcpu_reg(vcpu, 14) = *vcpu_pc(vcpu) + return_offset;
+ *vcpu_reg32(vcpu, 14) = *vcpu_pc(vcpu) + return_offset;
/* Branch to exception vector */
if (sctlr & (1 << 13))
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 87a64e8db04c..eec3598b4184 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -29,6 +29,7 @@
#include <asm/debug-monitors.h>
#include <asm/esr.h>
#include <asm/kvm_arm.h>
+#include <asm/kvm_asm.h>
#include <asm/kvm_coproc.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_host.h>
@@ -78,7 +79,7 @@ static u32 get_ccsidr(u32 csselr)
* See note at ARMv7 ARM B1.14.4 (TL;DR: S/W ops are not easily virtualized).
*/
static bool access_dcsw(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *p,
+ struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
if (!p->is_write)
@@ -94,21 +95,19 @@ static bool access_dcsw(struct kvm_vcpu *vcpu,
* sys_regs and leave it in complete control of the caches.
*/
static bool access_vm_reg(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *p,
+ struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
- unsigned long val;
bool was_enabled = vcpu_has_cache_enabled(vcpu);
BUG_ON(!p->is_write);
- val = *vcpu_reg(vcpu, p->Rt);
if (!p->is_aarch32) {
- vcpu_sys_reg(vcpu, r->reg) = val;
+ vcpu_sys_reg(vcpu, r->reg) = p->regval;
} else {
if (!p->is_32bit)
- vcpu_cp15_64_high(vcpu, r->reg) = val >> 32;
- vcpu_cp15_64_low(vcpu, r->reg) = val & 0xffffffffUL;
+ vcpu_cp15_64_high(vcpu, r->reg) = upper_32_bits(p->regval);
+ vcpu_cp15_64_low(vcpu, r->reg) = lower_32_bits(p->regval);
}
kvm_toggle_cache(vcpu, was_enabled);
@@ -122,22 +121,19 @@ static bool access_vm_reg(struct kvm_vcpu *vcpu,
* for both AArch64 and AArch32 accesses.
*/
static bool access_gic_sgi(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *p,
+ struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
- u64 val;
-
if (!p->is_write)
return read_from_write_only(vcpu, p);
- val = *vcpu_reg(vcpu, p->Rt);
- vgic_v3_dispatch_sgi(vcpu, val);
+ vgic_v3_dispatch_sgi(vcpu, p->regval);
return true;
}
static bool trap_raz_wi(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *p,
+ struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
if (p->is_write)
@@ -147,19 +143,19 @@ static bool trap_raz_wi(struct kvm_vcpu *vcpu,
}
static bool trap_oslsr_el1(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *p,
+ struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
if (p->is_write) {
return ignore_write(vcpu, p);
} else {
- *vcpu_reg(vcpu, p->Rt) = (1 << 3);
+ p->regval = (1 << 3);
return true;
}
}
static bool trap_dbgauthstatus_el1(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *p,
+ struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
if (p->is_write) {
@@ -167,7 +163,7 @@ static bool trap_dbgauthstatus_el1(struct kvm_vcpu *vcpu,
} else {
u32 val;
asm volatile("mrs %0, dbgauthstatus_el1" : "=r" (val));
- *vcpu_reg(vcpu, p->Rt) = val;
+ p->regval = val;
return true;
}
}
@@ -200,17 +196,17 @@ static bool trap_dbgauthstatus_el1(struct kvm_vcpu *vcpu,
* now use the debug registers.
*/
static bool trap_debug_regs(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *p,
+ struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
if (p->is_write) {
- vcpu_sys_reg(vcpu, r->reg) = *vcpu_reg(vcpu, p->Rt);
+ vcpu_sys_reg(vcpu, r->reg) = p->regval;
vcpu->arch.debug_flags |= KVM_ARM64_DEBUG_DIRTY;
} else {
- *vcpu_reg(vcpu, p->Rt) = vcpu_sys_reg(vcpu, r->reg);
+ p->regval = vcpu_sys_reg(vcpu, r->reg);
}
- trace_trap_reg(__func__, r->reg, p->is_write, *vcpu_reg(vcpu, p->Rt));
+ trace_trap_reg(__func__, r->reg, p->is_write, p->regval);
return true;
}
@@ -224,11 +220,11 @@ static bool trap_debug_regs(struct kvm_vcpu *vcpu,
* All writes will set the KVM_ARM64_DEBUG_DIRTY flag to ensure the
* hyp.S code switches between host and guest values in future.
*/
-static inline void reg_to_dbg(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *p,
- u64 *dbg_reg)
+static void reg_to_dbg(struct kvm_vcpu *vcpu,
+ struct sys_reg_params *p,
+ u64 *dbg_reg)
{
- u64 val = *vcpu_reg(vcpu, p->Rt);
+ u64 val = p->regval;
if (p->is_32bit) {
val &= 0xffffffffUL;
@@ -239,21 +235,18 @@ static inline void reg_to_dbg(struct kvm_vcpu *vcpu,
vcpu->arch.debug_flags |= KVM_ARM64_DEBUG_DIRTY;
}
-static inline void dbg_to_reg(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *p,
- u64 *dbg_reg)
+static void dbg_to_reg(struct kvm_vcpu *vcpu,
+ struct sys_reg_params *p,
+ u64 *dbg_reg)
{
- u64 val = *dbg_reg;
-
+ p->regval = *dbg_reg;
if (p->is_32bit)
- val &= 0xffffffffUL;
-
- *vcpu_reg(vcpu, p->Rt) = val;
+ p->regval &= 0xffffffffUL;
}
-static inline bool trap_bvr(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *p,
- const struct sys_reg_desc *rd)
+static bool trap_bvr(struct kvm_vcpu *vcpu,
+ struct sys_reg_params *p,
+ const struct sys_reg_desc *rd)
{
u64 *dbg_reg = &vcpu->arch.vcpu_debug_state.dbg_bvr[rd->reg];
@@ -287,15 +280,15 @@ static int get_bvr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
return 0;
}
-static inline void reset_bvr(struct kvm_vcpu *vcpu,
- const struct sys_reg_desc *rd)
+static void reset_bvr(struct kvm_vcpu *vcpu,
+ const struct sys_reg_desc *rd)
{
vcpu->arch.vcpu_debug_state.dbg_bvr[rd->reg] = rd->val;
}
-static inline bool trap_bcr(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *p,
- const struct sys_reg_desc *rd)
+static bool trap_bcr(struct kvm_vcpu *vcpu,
+ struct sys_reg_params *p,
+ const struct sys_reg_desc *rd)
{
u64 *dbg_reg = &vcpu->arch.vcpu_debug_state.dbg_bcr[rd->reg];
@@ -330,15 +323,15 @@ static int get_bcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
return 0;
}
-static inline void reset_bcr(struct kvm_vcpu *vcpu,
- const struct sys_reg_desc *rd)
+static void reset_bcr(struct kvm_vcpu *vcpu,
+ const struct sys_reg_desc *rd)
{
vcpu->arch.vcpu_debug_state.dbg_bcr[rd->reg] = rd->val;
}
-static inline bool trap_wvr(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *p,
- const struct sys_reg_desc *rd)
+static bool trap_wvr(struct kvm_vcpu *vcpu,
+ struct sys_reg_params *p,
+ const struct sys_reg_desc *rd)
{
u64 *dbg_reg = &vcpu->arch.vcpu_debug_state.dbg_wvr[rd->reg];
@@ -373,15 +366,15 @@ static int get_wvr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
return 0;
}
-static inline void reset_wvr(struct kvm_vcpu *vcpu,
- const struct sys_reg_desc *rd)
+static void reset_wvr(struct kvm_vcpu *vcpu,
+ const struct sys_reg_desc *rd)
{
vcpu->arch.vcpu_debug_state.dbg_wvr[rd->reg] = rd->val;
}
-static inline bool trap_wcr(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *p,
- const struct sys_reg_desc *rd)
+static bool trap_wcr(struct kvm_vcpu *vcpu,
+ struct sys_reg_params *p,
+ const struct sys_reg_desc *rd)
{
u64 *dbg_reg = &vcpu->arch.vcpu_debug_state.dbg_wcr[rd->reg];
@@ -415,8 +408,8 @@ static int get_wcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
return 0;
}
-static inline void reset_wcr(struct kvm_vcpu *vcpu,
- const struct sys_reg_desc *rd)
+static void reset_wcr(struct kvm_vcpu *vcpu,
+ const struct sys_reg_desc *rd)
{
vcpu->arch.vcpu_debug_state.dbg_wcr[rd->reg] = rd->val;
}
@@ -687,7 +680,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
};
static bool trap_dbgidr(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *p,
+ struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
if (p->is_write) {
@@ -697,23 +690,23 @@ static bool trap_dbgidr(struct kvm_vcpu *vcpu,
u64 pfr = read_system_reg(SYS_ID_AA64PFR0_EL1);
u32 el3 = !!cpuid_feature_extract_field(pfr, ID_AA64PFR0_EL3_SHIFT);
- *vcpu_reg(vcpu, p->Rt) = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
- (((dfr >> ID_AA64DFR0_BRPS_SHIFT) & 0xf) << 24) |
- (((dfr >> ID_AA64DFR0_CTX_CMPS_SHIFT) & 0xf) << 20) |
- (6 << 16) | (el3 << 14) | (el3 << 12));
+ p->regval = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
+ (((dfr >> ID_AA64DFR0_BRPS_SHIFT) & 0xf) << 24) |
+ (((dfr >> ID_AA64DFR0_CTX_CMPS_SHIFT) & 0xf) << 20)
+ | (6 << 16) | (el3 << 14) | (el3 << 12));
return true;
}
}
static bool trap_debug32(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *p,
+ struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
if (p->is_write) {
- vcpu_cp14(vcpu, r->reg) = *vcpu_reg(vcpu, p->Rt);
+ vcpu_cp14(vcpu, r->reg) = p->regval;
vcpu->arch.debug_flags |= KVM_ARM64_DEBUG_DIRTY;
} else {
- *vcpu_reg(vcpu, p->Rt) = vcpu_cp14(vcpu, r->reg);
+ p->regval = vcpu_cp14(vcpu, r->reg);
}
return true;
@@ -730,9 +723,9 @@ static bool trap_debug32(struct kvm_vcpu *vcpu,
* system is in.
*/
-static inline bool trap_xvr(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *p,
- const struct sys_reg_desc *rd)
+static bool trap_xvr(struct kvm_vcpu *vcpu,
+ struct sys_reg_params *p,
+ const struct sys_reg_desc *rd)
{
u64 *dbg_reg = &vcpu->arch.vcpu_debug_state.dbg_bvr[rd->reg];
@@ -740,12 +733,12 @@ static inline bool trap_xvr(struct kvm_vcpu *vcpu,
u64 val = *dbg_reg;
val &= 0xffffffffUL;
- val |= *vcpu_reg(vcpu, p->Rt) << 32;
+ val |= p->regval << 32;
*dbg_reg = val;
vcpu->arch.debug_flags |= KVM_ARM64_DEBUG_DIRTY;
} else {
- *vcpu_reg(vcpu, p->Rt) = *dbg_reg >> 32;
+ p->regval = *dbg_reg >> 32;
}
trace_trap_reg(__func__, rd->reg, p->is_write, *dbg_reg);
@@ -991,7 +984,7 @@ int kvm_handle_cp14_load_store(struct kvm_vcpu *vcpu, struct kvm_run *run)
* Return 0 if the access has been handled, and -1 if not.
*/
static int emulate_cp(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *params,
+ struct sys_reg_params *params,
const struct sys_reg_desc *table,
size_t num)
{
@@ -1062,12 +1055,12 @@ static int kvm_handle_cp_64(struct kvm_vcpu *vcpu,
{
struct sys_reg_params params;
u32 hsr = kvm_vcpu_get_hsr(vcpu);
+ int Rt = (hsr >> 5) & 0xf;
int Rt2 = (hsr >> 10) & 0xf;
params.is_aarch32 = true;
params.is_32bit = false;
params.CRm = (hsr >> 1) & 0xf;
- params.Rt = (hsr >> 5) & 0xf;
params.is_write = ((hsr & 1) == 0);
params.Op0 = 0;
@@ -1076,15 +1069,12 @@ static int kvm_handle_cp_64(struct kvm_vcpu *vcpu,
params.CRn = 0;
/*
- * Massive hack here. Store Rt2 in the top 32bits so we only
- * have one register to deal with. As we use the same trap
+ * Make a 64-bit value out of Rt and Rt2. As we use the same trap
* backends between AArch32 and AArch64, we get away with it.
*/
if (params.is_write) {
- u64 val = *vcpu_reg(vcpu, params.Rt);
- val &= 0xffffffff;
- val |= *vcpu_reg(vcpu, Rt2) << 32;
- *vcpu_reg(vcpu, params.Rt) = val;
+ params.regval = vcpu_get_reg(vcpu, Rt) & 0xffffffff;
+ params.regval |= vcpu_get_reg(vcpu, Rt2) << 32;
}
if (!emulate_cp(vcpu, &params, target_specific, nr_specific))
@@ -1095,11 +1085,10 @@ static int kvm_handle_cp_64(struct kvm_vcpu *vcpu,
unhandled_cp_access(vcpu, &params);
out:
- /* Do the opposite hack for the read side */
+ /* Split up the value between registers for the read side */
if (!params.is_write) {
- u64 val = *vcpu_reg(vcpu, params.Rt);
- val >>= 32;
- *vcpu_reg(vcpu, Rt2) = val;
+ vcpu_set_reg(vcpu, Rt, lower_32_bits(params.regval));
+ vcpu_set_reg(vcpu, Rt2, upper_32_bits(params.regval));
}
return 1;
@@ -1118,21 +1107,24 @@ static int kvm_handle_cp_32(struct kvm_vcpu *vcpu,
{
struct sys_reg_params params;
u32 hsr = kvm_vcpu_get_hsr(vcpu);
+ int Rt = (hsr >> 5) & 0xf;
params.is_aarch32 = true;
params.is_32bit = true;
params.CRm = (hsr >> 1) & 0xf;
- params.Rt = (hsr >> 5) & 0xf;
+ params.regval = vcpu_get_reg(vcpu, Rt);
params.is_write = ((hsr & 1) == 0);
params.CRn = (hsr >> 10) & 0xf;
params.Op0 = 0;
params.Op1 = (hsr >> 14) & 0x7;
params.Op2 = (hsr >> 17) & 0x7;
- if (!emulate_cp(vcpu, &params, target_specific, nr_specific))
- return 1;
- if (!emulate_cp(vcpu, &params, global, nr_global))
+ if (!emulate_cp(vcpu, &params, target_specific, nr_specific) ||
+ !emulate_cp(vcpu, &params, global, nr_global)) {
+ if (!params.is_write)
+ vcpu_set_reg(vcpu, Rt, params.regval);
return 1;
+ }
unhandled_cp_access(vcpu, &params);
return 1;
@@ -1175,7 +1167,7 @@ int kvm_handle_cp14_32(struct kvm_vcpu *vcpu, struct kvm_run *run)
}
static int emulate_sys_reg(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *params)
+ struct sys_reg_params *params)
{
size_t num;
const struct sys_reg_desc *table, *r;
@@ -1230,6 +1222,8 @@ int kvm_handle_sys_reg(struct kvm_vcpu *vcpu, struct kvm_run *run)
{
struct sys_reg_params params;
unsigned long esr = kvm_vcpu_get_hsr(vcpu);
+ int Rt = (esr >> 5) & 0x1f;
+ int ret;
trace_kvm_handle_sys_reg(esr);
@@ -1240,10 +1234,14 @@ int kvm_handle_sys_reg(struct kvm_vcpu *vcpu, struct kvm_run *run)
params.CRn = (esr >> 10) & 0xf;
params.CRm = (esr >> 1) & 0xf;
params.Op2 = (esr >> 17) & 0x7;
- params.Rt = (esr >> 5) & 0x1f;
+ params.regval = vcpu_get_reg(vcpu, Rt);
params.is_write = !(esr & 1);
- return emulate_sys_reg(vcpu, &params);
+ ret = emulate_sys_reg(vcpu, &params);
+
+ if (!params.is_write)
+ vcpu_set_reg(vcpu, Rt, params.regval);
+ return ret;
}
/******************************************************************************
diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h
index eaa324e4db4d..dbbb01cfbee9 100644
--- a/arch/arm64/kvm/sys_regs.h
+++ b/arch/arm64/kvm/sys_regs.h
@@ -28,7 +28,7 @@ struct sys_reg_params {
u8 CRn;
u8 CRm;
u8 Op2;
- u8 Rt;
+ u64 regval;
bool is_write;
bool is_aarch32;
bool is_32bit; /* Only valid if is_aarch32 is true */
@@ -44,7 +44,7 @@ struct sys_reg_desc {
/* Trapped access from guest, if non-NULL. */
bool (*access)(struct kvm_vcpu *,
- const struct sys_reg_params *,
+ struct sys_reg_params *,
const struct sys_reg_desc *);
/* Initialization for vcpu. */
@@ -77,9 +77,9 @@ static inline bool ignore_write(struct kvm_vcpu *vcpu,
}
static inline bool read_zero(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *p)
+ struct sys_reg_params *p)
{
- *vcpu_reg(vcpu, p->Rt) = 0;
+ p->regval = 0;
return true;
}
diff --git a/arch/arm64/kvm/sys_regs_generic_v8.c b/arch/arm64/kvm/sys_regs_generic_v8.c
index 1e4576824165..ed90578fa120 100644
--- a/arch/arm64/kvm/sys_regs_generic_v8.c
+++ b/arch/arm64/kvm/sys_regs_generic_v8.c
@@ -31,13 +31,13 @@
#include "sys_regs.h"
static bool access_actlr(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *p,
+ struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
if (p->is_write)
return ignore_write(vcpu, p);
- *vcpu_reg(vcpu, p->Rt) = vcpu_sys_reg(vcpu, ACTLR_EL1);
+ p->regval = vcpu_sys_reg(vcpu, ACTLR_EL1);
return true;
}
diff --git a/arch/arm64/kvm/vgic-v2-switch.S b/arch/arm64/kvm/vgic-v2-switch.S
deleted file mode 100644
index 3f000712a85d..000000000000
--- a/arch/arm64/kvm/vgic-v2-switch.S
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2012,2013 - ARM Ltd
- * Author: Marc Zyngier <marc.zyngier@arm.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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <linux/linkage.h>
-#include <linux/irqchip/arm-gic.h>
-
-#include <asm/assembler.h>
-#include <asm/memory.h>
-#include <asm/asm-offsets.h>
-#include <asm/kvm.h>
-#include <asm/kvm_asm.h>
-#include <asm/kvm_arm.h>
-#include <asm/kvm_mmu.h>
-
- .text
- .pushsection .hyp.text, "ax"
-
-/*
- * Save the VGIC CPU state into memory
- * x0: Register pointing to VCPU struct
- * Do not corrupt x1!!!
- */
-ENTRY(__save_vgic_v2_state)
-__save_vgic_v2_state:
- /* Get VGIC VCTRL base into x2 */
- ldr x2, [x0, #VCPU_KVM]
- kern_hyp_va x2
- ldr x2, [x2, #KVM_VGIC_VCTRL]
- kern_hyp_va x2
- cbz x2, 2f // disabled
-
- /* Compute the address of struct vgic_cpu */
- add x3, x0, #VCPU_VGIC_CPU
-
- /* Save all interesting registers */
- ldr w5, [x2, #GICH_VMCR]
- ldr w6, [x2, #GICH_MISR]
- ldr w7, [x2, #GICH_EISR0]
- ldr w8, [x2, #GICH_EISR1]
- ldr w9, [x2, #GICH_ELRSR0]
- ldr w10, [x2, #GICH_ELRSR1]
- ldr w11, [x2, #GICH_APR]
-CPU_BE( rev w5, w5 )
-CPU_BE( rev w6, w6 )
-CPU_BE( rev w7, w7 )
-CPU_BE( rev w8, w8 )
-CPU_BE( rev w9, w9 )
-CPU_BE( rev w10, w10 )
-CPU_BE( rev w11, w11 )
-
- str w5, [x3, #VGIC_V2_CPU_VMCR]
- str w6, [x3, #VGIC_V2_CPU_MISR]
-CPU_LE( str w7, [x3, #VGIC_V2_CPU_EISR] )
-CPU_LE( str w8, [x3, #(VGIC_V2_CPU_EISR + 4)] )
-CPU_LE( str w9, [x3, #VGIC_V2_CPU_ELRSR] )
-CPU_LE( str w10, [x3, #(VGIC_V2_CPU_ELRSR + 4)] )
-CPU_BE( str w7, [x3, #(VGIC_V2_CPU_EISR + 4)] )
-CPU_BE( str w8, [x3, #VGIC_V2_CPU_EISR] )
-CPU_BE( str w9, [x3, #(VGIC_V2_CPU_ELRSR + 4)] )
-CPU_BE( str w10, [x3, #VGIC_V2_CPU_ELRSR] )
- str w11, [x3, #VGIC_V2_CPU_APR]
-
- /* Clear GICH_HCR */
- str wzr, [x2, #GICH_HCR]
-
- /* Save list registers */
- add x2, x2, #GICH_LR0
- ldr w4, [x3, #VGIC_CPU_NR_LR]
- add x3, x3, #VGIC_V2_CPU_LR
-1: ldr w5, [x2], #4
-CPU_BE( rev w5, w5 )
- str w5, [x3], #4
- sub w4, w4, #1
- cbnz w4, 1b
-2:
- ret
-ENDPROC(__save_vgic_v2_state)
-
-/*
- * Restore the VGIC CPU state from memory
- * x0: Register pointing to VCPU struct
- */
-ENTRY(__restore_vgic_v2_state)
-__restore_vgic_v2_state:
- /* Get VGIC VCTRL base into x2 */
- ldr x2, [x0, #VCPU_KVM]
- kern_hyp_va x2
- ldr x2, [x2, #KVM_VGIC_VCTRL]
- kern_hyp_va x2
- cbz x2, 2f // disabled
-
- /* Compute the address of struct vgic_cpu */
- add x3, x0, #VCPU_VGIC_CPU
-
- /* We only restore a minimal set of registers */
- ldr w4, [x3, #VGIC_V2_CPU_HCR]
- ldr w5, [x3, #VGIC_V2_CPU_VMCR]
- ldr w6, [x3, #VGIC_V2_CPU_APR]
-CPU_BE( rev w4, w4 )
-CPU_BE( rev w5, w5 )
-CPU_BE( rev w6, w6 )
-
- str w4, [x2, #GICH_HCR]
- str w5, [x2, #GICH_VMCR]
- str w6, [x2, #GICH_APR]
-
- /* Restore list registers */
- add x2, x2, #GICH_LR0
- ldr w4, [x3, #VGIC_CPU_NR_LR]
- add x3, x3, #VGIC_V2_CPU_LR
-1: ldr w5, [x3], #4
-CPU_BE( rev w5, w5 )
- str w5, [x2], #4
- sub w4, w4, #1
- cbnz w4, 1b
-2:
- ret
-ENDPROC(__restore_vgic_v2_state)
-
- .popsection
diff --git a/arch/arm64/kvm/vgic-v3-switch.S b/arch/arm64/kvm/vgic-v3-switch.S
deleted file mode 100644
index 3c20730ddff5..000000000000
--- a/arch/arm64/kvm/vgic-v3-switch.S
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Copyright (C) 2012,2013 - ARM Ltd
- * Author: Marc Zyngier <marc.zyngier@arm.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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <linux/linkage.h>
-#include <linux/irqchip/arm-gic-v3.h>
-
-#include <asm/assembler.h>
-#include <asm/memory.h>
-#include <asm/asm-offsets.h>
-#include <asm/kvm.h>
-#include <asm/kvm_asm.h>
-#include <asm/kvm_arm.h>
-
- .text
- .pushsection .hyp.text, "ax"
-
-/*
- * We store LRs in reverse order to let the CPU deal with streaming
- * access. Use this macro to make it look saner...
- */
-#define LR_OFFSET(n) (VGIC_V3_CPU_LR + (15 - n) * 8)
-
-/*
- * Save the VGIC CPU state into memory
- * x0: Register pointing to VCPU struct
- * Do not corrupt x1!!!
- */
-.macro save_vgic_v3_state
- // Compute the address of struct vgic_cpu
- add x3, x0, #VCPU_VGIC_CPU
-
- // Make sure stores to the GIC via the memory mapped interface
- // are now visible to the system register interface
- dsb st
-
- // Save all interesting registers
- 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 w5, [x3, #VGIC_V3_CPU_VMCR]
- str w6, [x3, #VGIC_V3_CPU_MISR]
- str w7, [x3, #VGIC_V3_CPU_EISR]
- str w8, [x3, #VGIC_V3_CPU_ELRSR]
-
- msr_s ICH_HCR_EL2, xzr
-
- mrs_s x21, ICH_VTR_EL2
- mvn w22, w21
- ubfiz w23, w22, 2, 4 // w23 = (15 - ListRegs) * 4
-
- adr x24, 1f
- add x24, x24, x23
- br x24
-
-1:
- mrs_s x20, ICH_LR15_EL2
- mrs_s x19, ICH_LR14_EL2
- mrs_s x18, ICH_LR13_EL2
- mrs_s x17, ICH_LR12_EL2
- mrs_s x16, ICH_LR11_EL2
- mrs_s x15, ICH_LR10_EL2
- mrs_s x14, ICH_LR9_EL2
- mrs_s x13, ICH_LR8_EL2
- mrs_s x12, ICH_LR7_EL2
- mrs_s x11, ICH_LR6_EL2
- mrs_s x10, ICH_LR5_EL2
- mrs_s x9, ICH_LR4_EL2
- mrs_s x8, ICH_LR3_EL2
- mrs_s x7, ICH_LR2_EL2
- mrs_s x6, ICH_LR1_EL2
- mrs_s x5, ICH_LR0_EL2
-
- adr x24, 1f
- add x24, x24, x23
- br x24
-
-1:
- str x20, [x3, #LR_OFFSET(15)]
- str x19, [x3, #LR_OFFSET(14)]
- str x18, [x3, #LR_OFFSET(13)]
- str x17, [x3, #LR_OFFSET(12)]
- str x16, [x3, #LR_OFFSET(11)]
- str x15, [x3, #LR_OFFSET(10)]
- str x14, [x3, #LR_OFFSET(9)]
- str x13, [x3, #LR_OFFSET(8)]
- str x12, [x3, #LR_OFFSET(7)]
- str x11, [x3, #LR_OFFSET(6)]
- str x10, [x3, #LR_OFFSET(5)]
- str x9, [x3, #LR_OFFSET(4)]
- str x8, [x3, #LR_OFFSET(3)]
- str x7, [x3, #LR_OFFSET(2)]
- str x6, [x3, #LR_OFFSET(1)]
- str x5, [x3, #LR_OFFSET(0)]
-
- tbnz w21, #29, 6f // 6 bits
- tbz w21, #30, 5f // 5 bits
- // 7 bits
- mrs_s x20, ICH_AP0R3_EL2
- str w20, [x3, #(VGIC_V3_CPU_AP0R + 3*4)]
- mrs_s x19, ICH_AP0R2_EL2
- str w19, [x3, #(VGIC_V3_CPU_AP0R + 2*4)]
-6: mrs_s x18, ICH_AP0R1_EL2
- str w18, [x3, #(VGIC_V3_CPU_AP0R + 1*4)]
-5: mrs_s x17, ICH_AP0R0_EL2
- str w17, [x3, #VGIC_V3_CPU_AP0R]
-
- tbnz w21, #29, 6f // 6 bits
- tbz w21, #30, 5f // 5 bits
- // 7 bits
- mrs_s x20, ICH_AP1R3_EL2
- str w20, [x3, #(VGIC_V3_CPU_AP1R + 3*4)]
- mrs_s x19, ICH_AP1R2_EL2
- str w19, [x3, #(VGIC_V3_CPU_AP1R + 2*4)]
-6: mrs_s x18, ICH_AP1R1_EL2
- str w18, [x3, #(VGIC_V3_CPU_AP1R + 1*4)]
-5: mrs_s x17, ICH_AP1R0_EL2
- str w17, [x3, #VGIC_V3_CPU_AP1R]
-
- // Restore SRE_EL1 access and re-enable SRE at EL1.
- mrs_s x5, ICC_SRE_EL2
- orr x5, x5, #ICC_SRE_EL2_ENABLE
- msr_s ICC_SRE_EL2, x5
- isb
- mov x5, #1
- msr_s ICC_SRE_EL1, x5
-.endm
-
-/*
- * Restore the VGIC CPU state from memory
- * x0: Register pointing to VCPU struct
- */
-.macro restore_vgic_v3_state
- // Compute the address of struct vgic_cpu
- add x3, x0, #VCPU_VGIC_CPU
-
- // Restore all interesting registers
- ldr w4, [x3, #VGIC_V3_CPU_HCR]
- ldr w5, [x3, #VGIC_V3_CPU_VMCR]
- ldr w25, [x3, #VGIC_V3_CPU_SRE]
-
- msr_s ICC_SRE_EL1, x25
-
- // make sure SRE is valid before writing the other registers
- isb
-
- msr_s ICH_HCR_EL2, x4
- msr_s ICH_VMCR_EL2, x5
-
- mrs_s x21, ICH_VTR_EL2
-
- tbnz w21, #29, 6f // 6 bits
- tbz w21, #30, 5f // 5 bits
- // 7 bits
- ldr w20, [x3, #(VGIC_V3_CPU_AP1R + 3*4)]
- msr_s ICH_AP1R3_EL2, x20
- ldr w19, [x3, #(VGIC_V3_CPU_AP1R + 2*4)]
- msr_s ICH_AP1R2_EL2, x19
-6: ldr w18, [x3, #(VGIC_V3_CPU_AP1R + 1*4)]
- msr_s ICH_AP1R1_EL2, x18
-5: ldr w17, [x3, #VGIC_V3_CPU_AP1R]
- msr_s ICH_AP1R0_EL2, x17
-
- tbnz w21, #29, 6f // 6 bits
- tbz w21, #30, 5f // 5 bits
- // 7 bits
- ldr w20, [x3, #(VGIC_V3_CPU_AP0R + 3*4)]
- msr_s ICH_AP0R3_EL2, x20
- ldr w19, [x3, #(VGIC_V3_CPU_AP0R + 2*4)]
- msr_s ICH_AP0R2_EL2, x19
-6: ldr w18, [x3, #(VGIC_V3_CPU_AP0R + 1*4)]
- msr_s ICH_AP0R1_EL2, x18
-5: ldr w17, [x3, #VGIC_V3_CPU_AP0R]
- msr_s ICH_AP0R0_EL2, x17
-
- and w22, w21, #0xf
- mvn w22, w21
- ubfiz w23, w22, 2, 4 // w23 = (15 - ListRegs) * 4
-
- adr x24, 1f
- add x24, x24, x23
- br x24
-
-1:
- ldr x20, [x3, #LR_OFFSET(15)]
- ldr x19, [x3, #LR_OFFSET(14)]
- ldr x18, [x3, #LR_OFFSET(13)]
- ldr x17, [x3, #LR_OFFSET(12)]
- ldr x16, [x3, #LR_OFFSET(11)]
- ldr x15, [x3, #LR_OFFSET(10)]
- ldr x14, [x3, #LR_OFFSET(9)]
- ldr x13, [x3, #LR_OFFSET(8)]
- ldr x12, [x3, #LR_OFFSET(7)]
- ldr x11, [x3, #LR_OFFSET(6)]
- ldr x10, [x3, #LR_OFFSET(5)]
- ldr x9, [x3, #LR_OFFSET(4)]
- ldr x8, [x3, #LR_OFFSET(3)]
- ldr x7, [x3, #LR_OFFSET(2)]
- ldr x6, [x3, #LR_OFFSET(1)]
- ldr x5, [x3, #LR_OFFSET(0)]
-
- adr x24, 1f
- add x24, x24, x23
- br x24
-
-1:
- msr_s ICH_LR15_EL2, x20
- msr_s ICH_LR14_EL2, x19
- msr_s ICH_LR13_EL2, x18
- msr_s ICH_LR12_EL2, x17
- msr_s ICH_LR11_EL2, x16
- msr_s ICH_LR10_EL2, x15
- msr_s ICH_LR9_EL2, x14
- msr_s ICH_LR8_EL2, x13
- msr_s ICH_LR7_EL2, x12
- msr_s ICH_LR6_EL2, x11
- msr_s ICH_LR5_EL2, x10
- msr_s ICH_LR4_EL2, x9
- msr_s ICH_LR3_EL2, x8
- msr_s ICH_LR2_EL2, x7
- msr_s ICH_LR1_EL2, x6
- msr_s ICH_LR0_EL2, x5
-
- // Ensure that the above will have reached the
- // (re)distributors. This ensure the guest will read
- // the correct values from the memory-mapped interface.
- isb
- dsb sy
-
- // Prevent the guest from touching the GIC system registers
- // if SRE isn't enabled for GICv3 emulation
- cbnz x25, 1f
- mrs_s x5, ICC_SRE_EL2
- and x5, x5, #~ICC_SRE_EL2_ENABLE
- msr_s ICC_SRE_EL2, x5
-1:
-.endm
-
-ENTRY(__save_vgic_v3_state)
- save_vgic_v3_state
- ret
-ENDPROC(__save_vgic_v3_state)
-
-ENTRY(__restore_vgic_v3_state)
- restore_vgic_v3_state
- ret
-ENDPROC(__restore_vgic_v3_state)
-
-ENTRY(__vgic_v3_get_ich_vtr_el2)
- mrs_s x0, ICH_VTR_EL2
- ret
-ENDPROC(__vgic_v3_get_ich_vtr_el2)
-
- .popsection
diff --git a/arch/arm64/mm/cache.S b/arch/arm64/mm/cache.S
index cfa44a6adc0a..6df07069a025 100644
--- a/arch/arm64/mm/cache.S
+++ b/arch/arm64/mm/cache.S
@@ -81,26 +81,32 @@ ENDPROC(__flush_cache_user_range)
/*
* __flush_dcache_area(kaddr, size)
*
- * Ensure that the data held in the page kaddr is written back to the
- * page in question.
+ * Ensure that any D-cache lines for the interval [kaddr, kaddr+size)
+ * are cleaned and invalidated to the PoC.
*
* - kaddr - kernel address
* - size - size in question
*/
ENTRY(__flush_dcache_area)
- dcache_line_size x2, x3
- add x1, x0, x1
- sub x3, x2, #1
- bic x0, x0, x3
-1: dc civac, x0 // clean & invalidate D line / unified line
- add x0, x0, x2
- cmp x0, x1
- b.lo 1b
- dsb sy
+ dcache_by_line_op civac, sy, x0, x1, x2, x3
ret
ENDPIPROC(__flush_dcache_area)
/*
+ * __clean_dcache_area_pou(kaddr, size)
+ *
+ * Ensure that any D-cache lines for the interval [kaddr, kaddr+size)
+ * are cleaned to the PoU.
+ *
+ * - kaddr - kernel address
+ * - size - size in question
+ */
+ENTRY(__clean_dcache_area_pou)
+ dcache_by_line_op cvau, ish, x0, x1, x2, x3
+ ret
+ENDPROC(__clean_dcache_area_pou)
+
+/*
* __inval_cache_range(start, end)
* - start - start address of region
* - end - end address of region
diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c
index f636a2639f03..e87f53ff5f58 100644
--- a/arch/arm64/mm/context.c
+++ b/arch/arm64/mm/context.c
@@ -76,13 +76,28 @@ static void flush_context(unsigned int cpu)
__flush_icache_all();
}
-static int is_reserved_asid(u64 asid)
+static bool check_update_reserved_asid(u64 asid, u64 newasid)
{
int cpu;
- for_each_possible_cpu(cpu)
- if (per_cpu(reserved_asids, cpu) == asid)
- return 1;
- return 0;
+ bool hit = false;
+
+ /*
+ * Iterate over the set of reserved ASIDs looking for a match.
+ * If we find one, then we can update our mm to use newasid
+ * (i.e. the same ASID in the current generation) but we can't
+ * exit the loop early, since we need to ensure that all copies
+ * of the old ASID are updated to reflect the mm. Failure to do
+ * so could result in us missing the reserved ASID in a future
+ * generation.
+ */
+ for_each_possible_cpu(cpu) {
+ if (per_cpu(reserved_asids, cpu) == asid) {
+ hit = true;
+ per_cpu(reserved_asids, cpu) = newasid;
+ }
+ }
+
+ return hit;
}
static u64 new_context(struct mm_struct *mm, unsigned int cpu)
@@ -92,12 +107,14 @@ static u64 new_context(struct mm_struct *mm, unsigned int cpu)
u64 generation = atomic64_read(&asid_generation);
if (asid != 0) {
+ u64 newasid = generation | (asid & ~ASID_MASK);
+
/*
* If our current ASID was active during a rollover, we
* can continue to use it and this was just a false alarm.
*/
- if (is_reserved_asid(asid))
- return generation | (asid & ~ASID_MASK);
+ if (check_update_reserved_asid(asid, newasid))
+ return newasid;
/*
* We had a valid ASID in a previous life, so try to re-use
@@ -105,7 +122,7 @@ static u64 new_context(struct mm_struct *mm, unsigned int cpu)
*/
asid &= ~ASID_MASK;
if (!__test_and_set_bit(asid, asid_map))
- goto bump_gen;
+ return newasid;
}
/*
@@ -129,10 +146,7 @@ static u64 new_context(struct mm_struct *mm, unsigned int cpu)
set_asid:
__set_bit(asid, asid_map);
cur_idx = asid;
-
-bump_gen:
- asid |= generation;
- return asid;
+ return asid | generation;
}
void check_and_switch_context(struct mm_struct *mm, unsigned int cpu)
diff --git a/arch/arm64/mm/copypage.c b/arch/arm64/mm/copypage.c
index 13bbc3be6f5a..22e4cb4d6f53 100644
--- a/arch/arm64/mm/copypage.c
+++ b/arch/arm64/mm/copypage.c
@@ -24,8 +24,9 @@
void __cpu_copy_user_page(void *kto, const void *kfrom, unsigned long vaddr)
{
+ struct page *page = virt_to_page(kto);
copy_page(kto, kfrom);
- __flush_dcache_area(kto, PAGE_SIZE);
+ flush_dcache_page(page);
}
EXPORT_SYMBOL_GPL(__cpu_copy_user_page);
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index 7963aa4b5d28..331c4ca6205c 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -40,7 +40,7 @@ static pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot,
static struct gen_pool *atomic_pool;
#define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_256K
-static size_t atomic_pool_size = DEFAULT_DMA_COHERENT_POOL_SIZE;
+static size_t atomic_pool_size __initdata = DEFAULT_DMA_COHERENT_POOL_SIZE;
static int __init early_coherent_pool(char *p)
{
@@ -896,7 +896,7 @@ static int __iommu_attach_notifier(struct notifier_block *nb,
return 0;
}
-static int register_iommu_dma_ops_notifier(struct bus_type *bus)
+static int __init register_iommu_dma_ops_notifier(struct bus_type *bus)
{
struct notifier_block *nb = kzalloc(sizeof(*nb), GFP_KERNEL);
int ret;
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 19211c4a8911..92ddac1e8ca2 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -393,16 +393,16 @@ static struct fault_info {
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 1 translation fault" },
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 2 translation fault" },
{ do_page_fault, SIGSEGV, SEGV_MAPERR, "level 3 translation fault" },
- { do_bad, SIGBUS, 0, "reserved access flag fault" },
+ { do_bad, SIGBUS, 0, "unknown 8" },
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 1 access flag fault" },
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 access flag fault" },
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 access flag fault" },
- { do_bad, SIGBUS, 0, "reserved permission fault" },
+ { do_bad, SIGBUS, 0, "unknown 12" },
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 1 permission fault" },
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 permission fault" },
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 permission fault" },
{ do_bad, SIGBUS, 0, "synchronous external abort" },
- { do_bad, SIGBUS, 0, "asynchronous external abort" },
+ { do_bad, SIGBUS, 0, "unknown 17" },
{ do_bad, SIGBUS, 0, "unknown 18" },
{ do_bad, SIGBUS, 0, "unknown 19" },
{ do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" },
@@ -410,16 +410,16 @@ static struct fault_info {
{ do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" },
{ do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" },
{ do_bad, SIGBUS, 0, "synchronous parity error" },
- { do_bad, SIGBUS, 0, "asynchronous parity error" },
+ { do_bad, SIGBUS, 0, "unknown 25" },
{ do_bad, SIGBUS, 0, "unknown 26" },
{ do_bad, SIGBUS, 0, "unknown 27" },
- { do_bad, SIGBUS, 0, "synchronous parity error (translation table walk" },
- { do_bad, SIGBUS, 0, "synchronous parity error (translation table walk" },
- { do_bad, SIGBUS, 0, "synchronous parity error (translation table walk" },
- { do_bad, SIGBUS, 0, "synchronous parity error (translation table walk" },
+ { do_bad, SIGBUS, 0, "synchronous parity error (translation table walk)" },
+ { do_bad, SIGBUS, 0, "synchronous parity error (translation table walk)" },
+ { do_bad, SIGBUS, 0, "synchronous parity error (translation table walk)" },
+ { do_bad, SIGBUS, 0, "synchronous parity error (translation table walk)" },
{ do_bad, SIGBUS, 0, "unknown 32" },
{ do_bad, SIGBUS, BUS_ADRALN, "alignment fault" },
- { do_bad, SIGBUS, 0, "debug event" },
+ { do_bad, SIGBUS, 0, "unknown 34" },
{ do_bad, SIGBUS, 0, "unknown 35" },
{ do_bad, SIGBUS, 0, "unknown 36" },
{ do_bad, SIGBUS, 0, "unknown 37" },
@@ -433,21 +433,21 @@ static struct fault_info {
{ do_bad, SIGBUS, 0, "unknown 45" },
{ do_bad, SIGBUS, 0, "unknown 46" },
{ do_bad, SIGBUS, 0, "unknown 47" },
- { do_bad, SIGBUS, 0, "unknown 48" },
+ { do_bad, SIGBUS, 0, "TLB conflict abort" },
{ do_bad, SIGBUS, 0, "unknown 49" },
{ do_bad, SIGBUS, 0, "unknown 50" },
{ do_bad, SIGBUS, 0, "unknown 51" },
{ do_bad, SIGBUS, 0, "implementation fault (lockdown abort)" },
- { do_bad, SIGBUS, 0, "unknown 53" },
+ { do_bad, SIGBUS, 0, "implementation fault (unsupported exclusive)" },
{ do_bad, SIGBUS, 0, "unknown 54" },
{ do_bad, SIGBUS, 0, "unknown 55" },
{ do_bad, SIGBUS, 0, "unknown 56" },
{ do_bad, SIGBUS, 0, "unknown 57" },
- { do_bad, SIGBUS, 0, "implementation fault (coprocessor abort)" },
+ { do_bad, SIGBUS, 0, "unknown 58" },
{ do_bad, SIGBUS, 0, "unknown 59" },
{ do_bad, SIGBUS, 0, "unknown 60" },
- { do_bad, SIGBUS, 0, "unknown 61" },
- { do_bad, SIGBUS, 0, "unknown 62" },
+ { do_bad, SIGBUS, 0, "section domain fault" },
+ { do_bad, SIGBUS, 0, "page domain fault" },
{ do_bad, SIGBUS, 0, "unknown 63" },
};
diff --git a/arch/arm64/mm/flush.c b/arch/arm64/mm/flush.c
index c26b804015e8..46649d6e6c5a 100644
--- a/arch/arm64/mm/flush.c
+++ b/arch/arm64/mm/flush.c
@@ -34,19 +34,24 @@ void flush_cache_range(struct vm_area_struct *vma, unsigned long start,
__flush_icache_all();
}
+static void sync_icache_aliases(void *kaddr, unsigned long len)
+{
+ unsigned long addr = (unsigned long)kaddr;
+
+ if (icache_is_aliasing()) {
+ __clean_dcache_area_pou(kaddr, len);
+ __flush_icache_all();
+ } else {
+ flush_icache_range(addr, addr + len);
+ }
+}
+
static void flush_ptrace_access(struct vm_area_struct *vma, struct page *page,
unsigned long uaddr, void *kaddr,
unsigned long len)
{
- if (vma->vm_flags & VM_EXEC) {
- unsigned long addr = (unsigned long)kaddr;
- if (icache_is_aliasing()) {
- __flush_dcache_area(kaddr, len);
- __flush_icache_all();
- } else {
- flush_icache_range(addr, addr + len);
- }
- }
+ if (vma->vm_flags & VM_EXEC)
+ sync_icache_aliases(kaddr, len);
}
/*
@@ -74,13 +79,11 @@ void __sync_icache_dcache(pte_t pte, unsigned long addr)
if (!page_mapping(page))
return;
- if (!test_and_set_bit(PG_dcache_clean, &page->flags)) {
- __flush_dcache_area(page_address(page),
- PAGE_SIZE << compound_order(page));
+ if (!test_and_set_bit(PG_dcache_clean, &page->flags))
+ sync_icache_aliases(page_address(page),
+ PAGE_SIZE << compound_order(page));
+ else if (icache_is_aivivt())
__flush_icache_all();
- } else if (icache_is_aivivt()) {
- __flush_icache_all();
- }
}
/*
diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c
index 383b03ff38f8..82d607c3614e 100644
--- a/arch/arm64/mm/hugetlbpage.c
+++ b/arch/arm64/mm/hugetlbpage.c
@@ -41,17 +41,289 @@ int pud_huge(pud_t pud)
#endif
}
+static int find_num_contig(struct mm_struct *mm, unsigned long addr,
+ pte_t *ptep, pte_t pte, size_t *pgsize)
+{
+ pgd_t *pgd = pgd_offset(mm, addr);
+ pud_t *pud;
+ pmd_t *pmd;
+
+ *pgsize = PAGE_SIZE;
+ if (!pte_cont(pte))
+ return 1;
+ if (!pgd_present(*pgd)) {
+ VM_BUG_ON(!pgd_present(*pgd));
+ return 1;
+ }
+ pud = pud_offset(pgd, addr);
+ if (!pud_present(*pud)) {
+ VM_BUG_ON(!pud_present(*pud));
+ return 1;
+ }
+ pmd = pmd_offset(pud, addr);
+ if (!pmd_present(*pmd)) {
+ VM_BUG_ON(!pmd_present(*pmd));
+ return 1;
+ }
+ if ((pte_t *)pmd == ptep) {
+ *pgsize = PMD_SIZE;
+ return CONT_PMDS;
+ }
+ return CONT_PTES;
+}
+
+void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
+ pte_t *ptep, pte_t pte)
+{
+ size_t pgsize;
+ int i;
+ int ncontig = find_num_contig(mm, addr, ptep, pte, &pgsize);
+ unsigned long pfn;
+ pgprot_t hugeprot;
+
+ if (ncontig == 1) {
+ set_pte_at(mm, addr, ptep, pte);
+ return;
+ }
+
+ pfn = pte_pfn(pte);
+ hugeprot = __pgprot(pte_val(pfn_pte(pfn, __pgprot(0))) ^ pte_val(pte));
+ for (i = 0; i < ncontig; i++) {
+ pr_debug("%s: set pte %p to 0x%llx\n", __func__, ptep,
+ pte_val(pfn_pte(pfn, hugeprot)));
+ set_pte_at(mm, addr, ptep, pfn_pte(pfn, hugeprot));
+ ptep++;
+ pfn += pgsize >> PAGE_SHIFT;
+ addr += pgsize;
+ }
+}
+
+pte_t *huge_pte_alloc(struct mm_struct *mm,
+ unsigned long addr, unsigned long sz)
+{
+ pgd_t *pgd;
+ pud_t *pud;
+ pte_t *pte = NULL;
+
+ pr_debug("%s: addr:0x%lx sz:0x%lx\n", __func__, addr, sz);
+ pgd = pgd_offset(mm, addr);
+ pud = pud_alloc(mm, pgd, addr);
+ if (!pud)
+ return NULL;
+
+ if (sz == PUD_SIZE) {
+ pte = (pte_t *)pud;
+ } else if (sz == (PAGE_SIZE * CONT_PTES)) {
+ pmd_t *pmd = pmd_alloc(mm, pud, addr);
+
+ WARN_ON(addr & (sz - 1));
+ /*
+ * Note that if this code were ever ported to the
+ * 32-bit arm platform then it will cause trouble in
+ * the case where CONFIG_HIGHPTE is set, since there
+ * will be no pte_unmap() to correspond with this
+ * pte_alloc_map().
+ */
+ pte = pte_alloc_map(mm, NULL, pmd, addr);
+ } else if (sz == PMD_SIZE) {
+ if (IS_ENABLED(CONFIG_ARCH_WANT_HUGE_PMD_SHARE) &&
+ pud_none(*pud))
+ pte = huge_pmd_share(mm, addr, pud);
+ else
+ pte = (pte_t *)pmd_alloc(mm, pud, addr);
+ } else if (sz == (PMD_SIZE * CONT_PMDS)) {
+ pmd_t *pmd;
+
+ pmd = pmd_alloc(mm, pud, addr);
+ WARN_ON(addr & (sz - 1));
+ return (pte_t *)pmd;
+ }
+
+ pr_debug("%s: addr:0x%lx sz:0x%lx ret pte=%p/0x%llx\n", __func__, addr,
+ sz, pte, pte_val(*pte));
+ return pte;
+}
+
+pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr)
+{
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd = NULL;
+ pte_t *pte = NULL;
+
+ pgd = pgd_offset(mm, addr);
+ pr_debug("%s: addr:0x%lx pgd:%p\n", __func__, addr, pgd);
+ if (!pgd_present(*pgd))
+ return NULL;
+ pud = pud_offset(pgd, addr);
+ if (!pud_present(*pud))
+ return NULL;
+
+ if (pud_huge(*pud))
+ return (pte_t *)pud;
+ pmd = pmd_offset(pud, addr);
+ if (!pmd_present(*pmd))
+ return NULL;
+
+ if (pte_cont(pmd_pte(*pmd))) {
+ pmd = pmd_offset(
+ pud, (addr & CONT_PMD_MASK));
+ return (pte_t *)pmd;
+ }
+ if (pmd_huge(*pmd))
+ return (pte_t *)pmd;
+ pte = pte_offset_kernel(pmd, addr);
+ if (pte_present(*pte) && pte_cont(*pte)) {
+ pte = pte_offset_kernel(
+ pmd, (addr & CONT_PTE_MASK));
+ return pte;
+ }
+ return NULL;
+}
+
+pte_t arch_make_huge_pte(pte_t entry, struct vm_area_struct *vma,
+ struct page *page, int writable)
+{
+ size_t pagesize = huge_page_size(hstate_vma(vma));
+
+ if (pagesize == CONT_PTE_SIZE) {
+ entry = pte_mkcont(entry);
+ } else if (pagesize == CONT_PMD_SIZE) {
+ entry = pmd_pte(pmd_mkcont(pte_pmd(entry)));
+ } else if (pagesize != PUD_SIZE && pagesize != PMD_SIZE) {
+ pr_warn("%s: unrecognized huge page size 0x%lx\n",
+ __func__, pagesize);
+ }
+ return entry;
+}
+
+pte_t huge_ptep_get_and_clear(struct mm_struct *mm,
+ unsigned long addr, pte_t *ptep)
+{
+ pte_t pte;
+
+ if (pte_cont(*ptep)) {
+ int ncontig, i;
+ size_t pgsize;
+ pte_t *cpte;
+ bool is_dirty = false;
+
+ cpte = huge_pte_offset(mm, addr);
+ ncontig = find_num_contig(mm, addr, cpte, *cpte, &pgsize);
+ /* save the 1st pte to return */
+ pte = ptep_get_and_clear(mm, addr, cpte);
+ for (i = 1; i < ncontig; ++i) {
+ /*
+ * If HW_AFDBM is enabled, then the HW could
+ * turn on the dirty bit for any of the page
+ * in the set, so check them all.
+ */
+ ++cpte;
+ if (pte_dirty(ptep_get_and_clear(mm, addr, cpte)))
+ is_dirty = true;
+ }
+ if (is_dirty)
+ return pte_mkdirty(pte);
+ else
+ return pte;
+ } else {
+ return ptep_get_and_clear(mm, addr, ptep);
+ }
+}
+
+int huge_ptep_set_access_flags(struct vm_area_struct *vma,
+ unsigned long addr, pte_t *ptep,
+ pte_t pte, int dirty)
+{
+ pte_t *cpte;
+
+ if (pte_cont(pte)) {
+ int ncontig, i, changed = 0;
+ size_t pgsize = 0;
+ unsigned long pfn = pte_pfn(pte);
+ /* Select all bits except the pfn */
+ pgprot_t hugeprot =
+ __pgprot(pte_val(pfn_pte(pfn, __pgprot(0))) ^
+ pte_val(pte));
+
+ cpte = huge_pte_offset(vma->vm_mm, addr);
+ pfn = pte_pfn(*cpte);
+ ncontig = find_num_contig(vma->vm_mm, addr, cpte,
+ *cpte, &pgsize);
+ for (i = 0; i < ncontig; ++i, ++cpte) {
+ changed = ptep_set_access_flags(vma, addr, cpte,
+ pfn_pte(pfn,
+ hugeprot),
+ dirty);
+ pfn += pgsize >> PAGE_SHIFT;
+ }
+ return changed;
+ } else {
+ return ptep_set_access_flags(vma, addr, ptep, pte, dirty);
+ }
+}
+
+void huge_ptep_set_wrprotect(struct mm_struct *mm,
+ unsigned long addr, pte_t *ptep)
+{
+ if (pte_cont(*ptep)) {
+ int ncontig, i;
+ pte_t *cpte;
+ size_t pgsize = 0;
+
+ cpte = huge_pte_offset(mm, addr);
+ ncontig = find_num_contig(mm, addr, cpte, *cpte, &pgsize);
+ for (i = 0; i < ncontig; ++i, ++cpte)
+ ptep_set_wrprotect(mm, addr, cpte);
+ } else {
+ ptep_set_wrprotect(mm, addr, ptep);
+ }
+}
+
+void huge_ptep_clear_flush(struct vm_area_struct *vma,
+ unsigned long addr, pte_t *ptep)
+{
+ if (pte_cont(*ptep)) {
+ int ncontig, i;
+ pte_t *cpte;
+ size_t pgsize = 0;
+
+ cpte = huge_pte_offset(vma->vm_mm, addr);
+ ncontig = find_num_contig(vma->vm_mm, addr, cpte,
+ *cpte, &pgsize);
+ for (i = 0; i < ncontig; ++i, ++cpte)
+ ptep_clear_flush(vma, addr, cpte);
+ } else {
+ ptep_clear_flush(vma, addr, ptep);
+ }
+}
+
static __init int setup_hugepagesz(char *opt)
{
unsigned long ps = memparse(opt, &opt);
+
if (ps == PMD_SIZE) {
hugetlb_add_hstate(PMD_SHIFT - PAGE_SHIFT);
} else if (ps == PUD_SIZE) {
hugetlb_add_hstate(PUD_SHIFT - PAGE_SHIFT);
+ } else if (ps == (PAGE_SIZE * CONT_PTES)) {
+ hugetlb_add_hstate(CONT_PTE_SHIFT);
+ } else if (ps == (PMD_SIZE * CONT_PMDS)) {
+ hugetlb_add_hstate((PMD_SHIFT + CONT_PMD_SHIFT) - PAGE_SHIFT);
} else {
- pr_err("hugepagesz: Unsupported page size %lu M\n", ps >> 20);
+ pr_err("hugepagesz: Unsupported page size %lu K\n", ps >> 10);
return 0;
}
return 1;
}
__setup("hugepagesz=", setup_hugepagesz);
+
+#ifdef CONFIG_ARM64_64K_PAGES
+static __init int add_default_hugepagesz(void)
+{
+ if (size_to_hstate(CONT_PTES * PAGE_SIZE) == NULL)
+ hugetlb_add_hstate(CONT_PMD_SHIFT);
+ return 0;
+}
+arch_initcall(add_default_hugepagesz);
+#endif
diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
index 17bf39ac83ba..f3b061e67bfe 100644
--- a/arch/arm64/mm/init.c
+++ b/arch/arm64/mm/init.c
@@ -71,7 +71,7 @@ early_param("initrd", early_initrd);
* currently assumes that for memory starting above 4G, 32-bit devices will
* use a DMA offset.
*/
-static phys_addr_t max_zone_dma_phys(void)
+static phys_addr_t __init max_zone_dma_phys(void)
{
phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, 32);
return min(offset + (1ULL << 32), memblock_end_of_DRAM());
@@ -120,17 +120,17 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max)
#ifdef CONFIG_HAVE_ARCH_PFN_VALID
int pfn_valid(unsigned long pfn)
{
- return memblock_is_memory(pfn << PAGE_SHIFT);
+ return memblock_is_map_memory(pfn << PAGE_SHIFT);
}
EXPORT_SYMBOL(pfn_valid);
#endif
#ifndef CONFIG_SPARSEMEM
-static void arm64_memory_present(void)
+static void __init arm64_memory_present(void)
{
}
#else
-static void arm64_memory_present(void)
+static void __init arm64_memory_present(void)
{
struct memblock_region *reg;
@@ -360,7 +360,6 @@ void free_initmem(void)
{
fixup_init();
free_initmem_default(0);
- free_alternatives_memory();
}
#ifdef CONFIG_BLK_DEV_INITRD
diff --git a/arch/arm64/mm/mmap.c b/arch/arm64/mm/mmap.c
index ed177475dd8c..4c893b5189dd 100644
--- a/arch/arm64/mm/mmap.c
+++ b/arch/arm64/mm/mmap.c
@@ -51,8 +51,12 @@ unsigned long arch_mmap_rnd(void)
{
unsigned long rnd;
- rnd = (unsigned long)get_random_int() & STACK_RND_MASK;
-
+#ifdef CONFIG_COMPAT
+ if (test_thread_flag(TIF_32BIT))
+ rnd = (unsigned long)get_random_int() & ((1 << mmap_rnd_compat_bits) - 1);
+ else
+#endif
+ rnd = (unsigned long)get_random_int() & ((1 << mmap_rnd_bits) - 1);
return rnd << PAGE_SHIFT;
}
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index abb66f84d4ac..58faeaa7fbdc 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -64,8 +64,12 @@ EXPORT_SYMBOL(phys_mem_access_prot);
static void __init *early_alloc(unsigned long sz)
{
- void *ptr = __va(memblock_alloc(sz, sz));
- BUG_ON(!ptr);
+ phys_addr_t phys;
+ void *ptr;
+
+ phys = memblock_alloc(sz, sz);
+ BUG_ON(!phys);
+ ptr = __va(phys);
memset(ptr, 0, sz);
return ptr;
}
@@ -81,55 +85,19 @@ static void split_pmd(pmd_t *pmd, pte_t *pte)
do {
/*
* Need to have the least restrictive permissions available
- * permissions will be fixed up later. Default the new page
- * range as contiguous ptes.
+ * permissions will be fixed up later
*/
- set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC_CONT));
+ set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC));
pfn++;
} while (pte++, i++, i < PTRS_PER_PTE);
}
-/*
- * Given a PTE with the CONT bit set, determine where the CONT range
- * starts, and clear the entire range of PTE CONT bits.
- */
-static void clear_cont_pte_range(pte_t *pte, unsigned long addr)
-{
- int i;
-
- pte -= CONT_RANGE_OFFSET(addr);
- for (i = 0; i < CONT_PTES; i++) {
- set_pte(pte, pte_mknoncont(*pte));
- pte++;
- }
- flush_tlb_all();
-}
-
-/*
- * Given a range of PTEs set the pfn and provided page protection flags
- */
-static void __populate_init_pte(pte_t *pte, unsigned long addr,
- unsigned long end, phys_addr_t phys,
- pgprot_t prot)
-{
- unsigned long pfn = __phys_to_pfn(phys);
-
- do {
- /* clear all the bits except the pfn, then apply the prot */
- set_pte(pte, pfn_pte(pfn, prot));
- pte++;
- pfn++;
- addr += PAGE_SIZE;
- } while (addr != end);
-}
-
static void alloc_init_pte(pmd_t *pmd, unsigned long addr,
- unsigned long end, phys_addr_t phys,
+ unsigned long end, unsigned long pfn,
pgprot_t prot,
void *(*alloc)(unsigned long size))
{
pte_t *pte;
- unsigned long next;
if (pmd_none(*pmd) || pmd_sect(*pmd)) {
pte = alloc(PTRS_PER_PTE * sizeof(pte_t));
@@ -142,27 +110,9 @@ static void alloc_init_pte(pmd_t *pmd, unsigned long addr,
pte = pte_offset_kernel(pmd, addr);
do {
- next = min(end, (addr + CONT_SIZE) & CONT_MASK);
- if (((addr | next | phys) & ~CONT_MASK) == 0) {
- /* a block of CONT_PTES */
- __populate_init_pte(pte, addr, next, phys,
- __pgprot(pgprot_val(prot) | PTE_CONT));
- } else {
- /*
- * If the range being split is already inside of a
- * contiguous range but this PTE isn't going to be
- * contiguous, then we want to unmark the adjacent
- * ranges, then update the portion of the range we
- * are interrested in.
- */
- clear_cont_pte_range(pte, addr);
- __populate_init_pte(pte, addr, next, phys, prot);
- }
-
- pte += (next - addr) >> PAGE_SHIFT;
- phys += next - addr;
- addr = next;
- } while (addr != end);
+ set_pte(pte, pfn_pte(pfn, prot));
+ pfn++;
+ } while (pte++, addr += PAGE_SIZE, addr != end);
}
static void split_pud(pud_t *old_pud, pmd_t *pmd)
@@ -223,7 +173,8 @@ static void alloc_init_pmd(struct mm_struct *mm, pud_t *pud,
}
}
} else {
- alloc_init_pte(pmd, addr, next, phys, prot, alloc);
+ alloc_init_pte(pmd, addr, next, __phys_to_pfn(phys),
+ prot, alloc);
}
phys += next - addr;
} while (pmd++, addr = next, addr != end);
@@ -300,6 +251,14 @@ static void __create_mapping(struct mm_struct *mm, pgd_t *pgd,
{
unsigned long addr, length, end, next;
+ /*
+ * If the virtual and physical address don't have the same offset
+ * within a page, we cannot map the region as the caller expects.
+ */
+ if (WARN_ON((phys ^ virt) & ~PAGE_MASK))
+ return;
+
+ phys &= PAGE_MASK;
addr = virt & PAGE_MASK;
length = PAGE_ALIGN(size + (virt & ~PAGE_MASK));
@@ -329,7 +288,7 @@ static void __init create_mapping(phys_addr_t phys, unsigned long virt,
&phys, virt);
return;
}
- __create_mapping(&init_mm, pgd_offset_k(virt & PAGE_MASK), phys, virt,
+ __create_mapping(&init_mm, pgd_offset_k(virt), phys, virt,
size, prot, early_alloc);
}
@@ -350,7 +309,7 @@ static void create_mapping_late(phys_addr_t phys, unsigned long virt,
return;
}
- return __create_mapping(&init_mm, pgd_offset_k(virt & PAGE_MASK),
+ return __create_mapping(&init_mm, pgd_offset_k(virt),
phys, virt, size, prot, late_alloc);
}
@@ -421,6 +380,8 @@ static void __init map_mem(void)
if (start >= end)
break;
+ if (memblock_is_nomap(reg))
+ continue;
if (ARM64_SWAPPER_USES_SECTION_MAPS) {
/*
@@ -505,6 +466,9 @@ void __init paging_init(void)
empty_zero_page = virt_to_page(zero_page);
+ /* Ensure the zero page is visible to the page table walker */
+ dsb(ishst);
+
/*
* TTBR0 is only used for the identity mapping at this stage. Make it
* point to zero page to avoid speculatively fetching new entries.
diff --git a/arch/arm64/mm/pgd.c b/arch/arm64/mm/pgd.c
index cb3ba1b812e7..ae11d4e03d0e 100644
--- a/arch/arm64/mm/pgd.c
+++ b/arch/arm64/mm/pgd.c
@@ -46,14 +46,14 @@ void pgd_free(struct mm_struct *mm, pgd_t *pgd)
kmem_cache_free(pgd_cache, pgd);
}
-static int __init pgd_cache_init(void)
+void __init pgd_cache_init(void)
{
+ if (PGD_SIZE == PAGE_SIZE)
+ return;
+
/*
* Naturally aligned pgds required by the architecture.
*/
- if (PGD_SIZE != PAGE_SIZE)
- pgd_cache = kmem_cache_create("pgd_cache", PGD_SIZE, PGD_SIZE,
- SLAB_PANIC, NULL);
- return 0;
+ pgd_cache = kmem_cache_create("pgd_cache", PGD_SIZE, PGD_SIZE,
+ SLAB_PANIC, NULL);
}
-core_initcall(pgd_cache_init);
diff --git a/arch/arm64/mm/proc-macros.S b/arch/arm64/mm/proc-macros.S
index 4c4d93c4bf65..146bd99a7532 100644
--- a/arch/arm64/mm/proc-macros.S
+++ b/arch/arm64/mm/proc-macros.S
@@ -62,3 +62,25 @@
bfi \valreg, \tmpreg, #TCR_T0SZ_OFFSET, #TCR_TxSZ_WIDTH
#endif
.endm
+
+/*
+ * Macro to perform a data cache maintenance for the interval
+ * [kaddr, kaddr + size)
+ *
+ * op: operation passed to dc instruction
+ * domain: domain used in dsb instruciton
+ * kaddr: starting virtual address of the region
+ * size: size of the region
+ * Corrupts: kaddr, size, tmp1, tmp2
+ */
+ .macro dcache_by_line_op op, domain, kaddr, size, tmp1, tmp2
+ dcache_line_size \tmp1, \tmp2
+ add \size, \kaddr, \size
+ sub \tmp2, \tmp1, #1
+ bic \kaddr, \kaddr, \tmp2
+9998: dc \op, \kaddr
+ add \kaddr, \kaddr, \tmp1
+ cmp \kaddr, \size
+ b.lo 9998b
+ dsb \domain
+ .endm
diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
index cacecc4ad3e5..a3d867e723b4 100644
--- a/arch/arm64/mm/proc.S
+++ b/arch/arm64/mm/proc.S
@@ -117,6 +117,7 @@ ENTRY(cpu_do_resume)
*/
ubfx x11, x11, #1, #1
msr oslar_el1, x11
+ msr pmuserenr_el0, xzr // Disable PMU access from EL0
mov x0, x12
dsb nsh // Make sure local tlb invalidation completed
isb
@@ -139,8 +140,6 @@ ENTRY(cpu_do_switch_mm)
ret
ENDPROC(cpu_do_switch_mm)
- .section ".text.init", #alloc, #execinstr
-
/*
* __cpu_setup
*
@@ -155,6 +154,7 @@ ENTRY(__cpu_setup)
msr cpacr_el1, x0 // Enable FP/ASIMD
mov x0, #1 << 12 // Reset mdscr_el1 and disable
msr mdscr_el1, x0 // access to the DCC from EL0
+ msr pmuserenr_el0, xzr // Disable PMU access from EL0
/*
* Memory region attributes for LPAE:
*
diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
index d6a53ef2350b..7658612d915c 100644
--- a/arch/arm64/net/bpf_jit_comp.c
+++ b/arch/arm64/net/bpf_jit_comp.c
@@ -139,6 +139,12 @@ static inline int epilogue_offset(const struct jit_ctx *ctx)
/* Stack must be multiples of 16B */
#define STACK_ALIGN(sz) (((sz) + 15) & ~15)
+#define _STACK_SIZE \
+ (MAX_BPF_STACK \
+ + 4 /* extra for skb_copy_bits buffer */)
+
+#define STACK_SIZE STACK_ALIGN(_STACK_SIZE)
+
static void build_prologue(struct jit_ctx *ctx)
{
const u8 r6 = bpf2a64[BPF_REG_6];
@@ -146,14 +152,8 @@ static void build_prologue(struct jit_ctx *ctx)
const u8 r8 = bpf2a64[BPF_REG_8];
const u8 r9 = bpf2a64[BPF_REG_9];
const u8 fp = bpf2a64[BPF_REG_FP];
- const u8 ra = bpf2a64[BPF_REG_A];
- const u8 rx = bpf2a64[BPF_REG_X];
const u8 tmp1 = bpf2a64[TMP_REG_1];
const u8 tmp2 = bpf2a64[TMP_REG_2];
- int stack_size = MAX_BPF_STACK;
-
- stack_size += 4; /* extra for skb_copy_bits buffer */
- stack_size = STACK_ALIGN(stack_size);
/*
* BPF prog stack layout
@@ -165,12 +165,13 @@ static void build_prologue(struct jit_ctx *ctx)
* | ... | callee saved registers
* +-----+
* | | x25/x26
- * BPF fp register => -80:+-----+
+ * BPF fp register => -80:+-----+ <= (BPF_FP)
* | |
* | ... | BPF prog stack
* | |
- * | |
- * current A64_SP => +-----+
+ * +-----+ <= (BPF_FP - MAX_BPF_STACK)
+ * |RSVD | JIT scratchpad
+ * current A64_SP => +-----+ <= (BPF_FP - STACK_SIZE)
* | |
* | ... | Function call stack
* | |
@@ -196,11 +197,7 @@ static void build_prologue(struct jit_ctx *ctx)
emit(A64_MOV(1, fp, A64_SP), ctx);
/* Set up function call stack */
- emit(A64_SUB_I(1, A64_SP, A64_SP, stack_size), ctx);
-
- /* Clear registers A and X */
- emit_a64_mov_i64(ra, 0, ctx);
- emit_a64_mov_i64(rx, 0, ctx);
+ emit(A64_SUB_I(1, A64_SP, A64_SP, STACK_SIZE), ctx);
}
static void build_epilogue(struct jit_ctx *ctx)
@@ -213,13 +210,9 @@ static void build_epilogue(struct jit_ctx *ctx)
const u8 fp = bpf2a64[BPF_REG_FP];
const u8 tmp1 = bpf2a64[TMP_REG_1];
const u8 tmp2 = bpf2a64[TMP_REG_2];
- int stack_size = MAX_BPF_STACK;
-
- stack_size += 4; /* extra for skb_copy_bits buffer */
- stack_size = STACK_ALIGN(stack_size);
/* We're done with BPF stack */
- emit(A64_ADD_I(1, A64_SP, A64_SP, stack_size), ctx);
+ emit(A64_ADD_I(1, A64_SP, A64_SP, STACK_SIZE), ctx);
/* Restore fs (x25) and x26 */
emit(A64_POP(fp, A64_R(26), A64_SP), ctx);
@@ -591,7 +584,25 @@ emit_cond_jmp:
case BPF_ST | BPF_MEM | BPF_H:
case BPF_ST | BPF_MEM | BPF_B:
case BPF_ST | BPF_MEM | BPF_DW:
- goto notyet;
+ /* Load imm to a register then store it */
+ ctx->tmp_used = 1;
+ emit_a64_mov_i(1, tmp2, off, ctx);
+ emit_a64_mov_i(1, tmp, imm, ctx);
+ switch (BPF_SIZE(code)) {
+ case BPF_W:
+ emit(A64_STR32(tmp, dst, tmp2), ctx);
+ break;
+ case BPF_H:
+ emit(A64_STRH(tmp, dst, tmp2), ctx);
+ break;
+ case BPF_B:
+ emit(A64_STRB(tmp, dst, tmp2), ctx);
+ break;
+ case BPF_DW:
+ emit(A64_STR64(tmp, dst, tmp2), ctx);
+ break;
+ }
+ break;
/* STX: *(size *)(dst + off) = src */
case BPF_STX | BPF_MEM | BPF_W:
@@ -658,7 +669,7 @@ emit_cond_jmp:
return -EINVAL;
}
emit_a64_mov_i64(r3, size, ctx);
- emit(A64_ADD_I(1, r4, fp, MAX_BPF_STACK), ctx);
+ emit(A64_SUB_I(1, r4, fp, STACK_SIZE), ctx);
emit_a64_mov_i64(r5, (unsigned long)bpf_load_pointer, ctx);
emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx);
emit(A64_MOV(1, A64_FP, A64_SP), ctx);
diff --git a/arch/arm64/xen/hypercall.S b/arch/arm64/xen/hypercall.S
index 8bbe9401f4f0..70df80e8da2c 100644
--- a/arch/arm64/xen/hypercall.S
+++ b/arch/arm64/xen/hypercall.S
@@ -80,6 +80,7 @@ HYPERCALL2(memory_op);
HYPERCALL2(physdev_op);
HYPERCALL3(vcpu_op);
HYPERCALL1(tmem_op);
+HYPERCALL1(platform_op_raw);
HYPERCALL2(multicall);
ENTRY(privcmd_call)
diff --git a/arch/avr32/include/uapi/asm/socket.h b/arch/avr32/include/uapi/asm/socket.h
index 2b65ed6b277c..9de0796240a0 100644
--- a/arch/avr32/include/uapi/asm/socket.h
+++ b/arch/avr32/include/uapi/asm/socket.h
@@ -85,4 +85,7 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* _UAPI__ASM_AVR32_SOCKET_H */
diff --git a/arch/avr32/kernel/module.c b/arch/avr32/kernel/module.c
index 164efa009e5b..2b4c54c04cb6 100644
--- a/arch/avr32/kernel/module.c
+++ b/arch/avr32/kernel/module.c
@@ -118,9 +118,9 @@ int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
* Increase core size to make room for GOT and set start
* offset for GOT.
*/
- module->core_size = ALIGN(module->core_size, 4);
- module->arch.got_offset = module->core_size;
- module->core_size += module->arch.got_size;
+ module->core_layout.size = ALIGN(module->core_layout.size, 4);
+ module->arch.got_offset = module->core_layout.size;
+ module->core_layout.size += module->arch.got_size;
return 0;
@@ -177,7 +177,7 @@ int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab,
if (!info->got_initialized) {
Elf32_Addr *gotent;
- gotent = (module->module_core
+ gotent = (module->core_layout.base
+ module->arch.got_offset
+ info->got_offset);
*gotent = relocation;
@@ -255,8 +255,8 @@ int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab,
*/
pr_debug("GOTPC: PC=0x%x, got_offset=0x%lx, core=0x%p\n",
relocation, module->arch.got_offset,
- module->module_core);
- relocation -= ((unsigned long)module->module_core
+ module->core_layout.base);
+ relocation -= ((unsigned long)module->core_layout.base
+ module->arch.got_offset);
*location = relocation;
break;
diff --git a/arch/blackfin/include/asm/cmpxchg.h b/arch/blackfin/include/asm/cmpxchg.h
index c05868cc61c1..253928854299 100644
--- a/arch/blackfin/include/asm/cmpxchg.h
+++ b/arch/blackfin/include/asm/cmpxchg.h
@@ -128,6 +128,5 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr,
#endif /* !CONFIG_SMP */
#define xchg(ptr, x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x), (ptr), sizeof(*(ptr))))
-#define tas(ptr) ((void)xchg((ptr), 1))
#endif /* __ARCH_BLACKFIN_CMPXCHG__ */
diff --git a/arch/blackfin/include/asm/uaccess.h b/arch/blackfin/include/asm/uaccess.h
index 90612a7f2cf3..12f5d6851bbc 100644
--- a/arch/blackfin/include/asm/uaccess.h
+++ b/arch/blackfin/include/asm/uaccess.h
@@ -168,12 +168,6 @@ static inline int bad_user_access_length(void)
#define __copy_to_user_inatomic __copy_to_user
#define __copy_from_user_inatomic __copy_from_user
-#define copy_to_user_ret(to, from, n, retval) ({ if (copy_to_user(to, from, n))\
- return retval; })
-
-#define copy_from_user_ret(to, from, n, retval) ({ if (copy_from_user(to, from, n))\
- return retval; })
-
static inline unsigned long __must_check
copy_from_user(void *to, const void __user *from, unsigned long n)
{
diff --git a/arch/blackfin/kernel/perf_event.c b/arch/blackfin/kernel/perf_event.c
index 1e9c8b0bf486..170d786807c4 100644
--- a/arch/blackfin/kernel/perf_event.c
+++ b/arch/blackfin/kernel/perf_event.c
@@ -14,7 +14,7 @@
* Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar
* Copyright (C) 2009 Jaswinder Singh Rajput
* Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter
- * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra
* Copyright (C) 2009 Intel Corporation, <markus.t.metzger@intel.com>
*
* ppc:
diff --git a/arch/blackfin/mach-bf537/boards/stamp.c b/arch/blackfin/mach-bf537/boards/stamp.c
index 88a19fc9844d..c181543a399a 100644
--- a/arch/blackfin/mach-bf537/boards/stamp.c
+++ b/arch/blackfin/mach-bf537/boards/stamp.c
@@ -404,7 +404,7 @@ static struct mtd_partition bfin_plat_nand_partitions[] = {
#define BFIN_NAND_PLAT_ALE 1
static void bfin_plat_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
if (cmd == NAND_CMD_NONE)
return;
diff --git a/arch/blackfin/mach-bf561/boards/acvilon.c b/arch/blackfin/mach-bf561/boards/acvilon.c
index 6ab951534d79..37f8f25a1347 100644
--- a/arch/blackfin/mach-bf561/boards/acvilon.c
+++ b/arch/blackfin/mach-bf561/boards/acvilon.c
@@ -267,7 +267,7 @@ static struct mtd_partition bfin_plat_nand_partitions[] = {
static void bfin_plat_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
if (cmd == NAND_CMD_NONE)
return;
diff --git a/arch/blackfin/mach-bf561/boards/ezkit.c b/arch/blackfin/mach-bf561/boards/ezkit.c
index 2de71e8c104b..f35525b55819 100644
--- a/arch/blackfin/mach-bf561/boards/ezkit.c
+++ b/arch/blackfin/mach-bf561/boards/ezkit.c
@@ -443,7 +443,7 @@ static const struct ppi_info ppi_info = {
};
#if IS_ENABLED(CONFIG_VIDEO_ADV7183)
-#include <media/adv7183.h>
+#include <media/i2c/adv7183.h>
static struct v4l2_input adv7183_inputs[] = {
{
.index = 0,
diff --git a/arch/blackfin/mach-bf609/boards/ezkit.c b/arch/blackfin/mach-bf609/boards/ezkit.c
index 2c61fc0c98f9..c7928d8ebb82 100644
--- a/arch/blackfin/mach-bf609/boards/ezkit.c
+++ b/arch/blackfin/mach-bf609/boards/ezkit.c
@@ -933,7 +933,7 @@ static struct bfin_capture_config bfin_capture_data = {
#endif
#if IS_ENABLED(CONFIG_VIDEO_ADV7842)
-#include <media/adv7842.h>
+#include <media/i2c/adv7842.h>
static struct v4l2_input adv7842_inputs[] = {
{
@@ -1084,7 +1084,7 @@ static const struct ppi_info ppi_info = {
};
#if IS_ENABLED(CONFIG_VIDEO_ADV7511)
-#include <media/adv7511.h>
+#include <media/i2c/adv7511.h>
static struct v4l2_output adv7511_outputs[] = {
{
@@ -1125,7 +1125,7 @@ static struct bfin_display_config bfin_display_data = {
#endif
#if IS_ENABLED(CONFIG_VIDEO_ADV7343)
-#include <media/adv7343.h>
+#include <media/i2c/adv7343.h>
static struct v4l2_output adv7343_outputs[] = {
{
diff --git a/arch/c6x/include/asm/Kbuild b/arch/c6x/include/asm/Kbuild
index 945544ec603e..64465e7e2245 100644
--- a/arch/c6x/include/asm/Kbuild
+++ b/arch/c6x/include/asm/Kbuild
@@ -4,6 +4,7 @@ generic-y += auxvec.h
generic-y += barrier.h
generic-y += bitsperlong.h
generic-y += bugs.h
+generic-y += clkdev.h
generic-y += cputime.h
generic-y += current.h
generic-y += device.h
diff --git a/arch/c6x/include/asm/clkdev.h b/arch/c6x/include/asm/clkdev.h
deleted file mode 100644
index 76a070b1c2e5..000000000000
--- a/arch/c6x/include/asm/clkdev.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef _ASM_CLKDEV_H
-#define _ASM_CLKDEV_H
-
-#include <linux/slab.h>
-
-struct clk;
-
-static inline int __clk_get(struct clk *clk)
-{
- return 1;
-}
-
-static inline void __clk_put(struct clk *clk)
-{
-}
-
-static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size)
-{
- return kzalloc(size, GFP_KERNEL);
-}
-
-#endif /* _ASM_CLKDEV_H */
diff --git a/arch/c6x/include/asm/cmpxchg.h b/arch/c6x/include/asm/cmpxchg.h
index b27c8cefb8c3..93d0a5a047a2 100644
--- a/arch/c6x/include/asm/cmpxchg.h
+++ b/arch/c6x/include/asm/cmpxchg.h
@@ -47,8 +47,6 @@ static inline unsigned int __xchg(unsigned int x, volatile void *ptr, int size)
#define xchg(ptr, x) \
((__typeof__(*(ptr)))__xchg((unsigned int)(x), (void *) (ptr), \
sizeof(*(ptr))))
-#define tas(ptr) xchg((ptr), 1)
-
#include <asm-generic/cmpxchg-local.h>
diff --git a/arch/cris/arch-v32/drivers/mach-a3/nandflash.c b/arch/cris/arch-v32/drivers/mach-a3/nandflash.c
index 7fb52128ddc9..5aa3f5162310 100644
--- a/arch/cris/arch-v32/drivers/mach-a3/nandflash.c
+++ b/arch/cris/arch-v32/drivers/mach-a3/nandflash.c
@@ -36,7 +36,6 @@
#define CE_BIT 12
struct mtd_info_wrapper {
- struct mtd_info info;
struct nand_chip chip;
};
@@ -52,7 +51,7 @@ static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd,
{
unsigned long flags;
reg_pio_rw_dout dout;
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
local_irq_save(flags);
@@ -148,10 +147,7 @@ struct mtd_info *__init crisv32_nand_flash_probe(void)
/* Get pointer to private data */
this = &wrapper->chip;
- crisv32_mtd = &wrapper->info;
-
- /* Link the private data with the MTD structure */
- crisv32_mtd->priv = this;
+ crisv32_mtd = nand_to_mtd(this);
/* Set address of NAND IO lines */
this->IO_ADDR_R = read_cs;
diff --git a/arch/cris/arch-v32/drivers/mach-fs/nandflash.c b/arch/cris/arch-v32/drivers/mach-fs/nandflash.c
index e03238454b0e..a7c17b0f172a 100644
--- a/arch/cris/arch-v32/drivers/mach-fs/nandflash.c
+++ b/arch/cris/arch-v32/drivers/mach-fs/nandflash.c
@@ -31,7 +31,6 @@
#define BY_BIT 7
struct mtd_info_wrapper {
- struct mtd_info info;
struct nand_chip chip;
};
@@ -51,7 +50,7 @@ static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd,
{
unsigned long flags;
reg_gio_rw_pa_dout dout;
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
local_irq_save(flags);
@@ -129,7 +128,7 @@ struct mtd_info *__init crisv32_nand_flash_probe(void)
/* Get pointer to private data */
this = &wrapper->chip;
- crisv32_mtd = &wrapper->info;
+ crisv32_mtd = nand_to_mtd(this);
pa_oe.oe |= 1 << CE_BIT;
pa_oe.oe |= 1 << ALE_BIT;
@@ -141,9 +140,6 @@ struct mtd_info *__init crisv32_nand_flash_probe(void)
bif_cfg.gated_csp1 = regk_bif_core_wr;
REG_WR(bif_core, regi_bif_core, rw_grp3_cfg, bif_cfg);
- /* Link the private data with the MTD structure */
- crisv32_mtd->priv = this;
-
/* Set address of NAND IO lines */
this->IO_ADDR_R = read_cs;
this->IO_ADDR_W = write_cs;
diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig
index 34aa19352dc1..03bfd6bf03e7 100644
--- a/arch/frv/Kconfig
+++ b/arch/frv/Kconfig
@@ -10,6 +10,7 @@ config FRV
select HAVE_DEBUG_BUGVERBOSE
select ARCH_HAVE_NMI_SAFE_CMPXCHG
select GENERIC_CPU_DEVICES
+ select ARCH_HAS_DEVMEM_IS_ALLOWED
select ARCH_WANT_IPC_PARSE_VERSION
select OLD_SIGSUSPEND3
select OLD_SIGACTION
diff --git a/arch/frv/include/asm/cmpxchg.h b/arch/frv/include/asm/cmpxchg.h
index 5b04dd0aecab..a899765102ea 100644
--- a/arch/frv/include/asm/cmpxchg.h
+++ b/arch/frv/include/asm/cmpxchg.h
@@ -69,8 +69,6 @@ extern uint32_t __xchg_32(uint32_t i, volatile void *v);
#endif
-#define tas(ptr) (xchg((ptr), 1))
-
/*****************************************************************************/
/*
* compare and conditionally exchange value with memory
diff --git a/arch/frv/include/uapi/asm/socket.h b/arch/frv/include/uapi/asm/socket.h
index 4823ad125578..f02e4849ae83 100644
--- a/arch/frv/include/uapi/asm/socket.h
+++ b/arch/frv/include/uapi/asm/socket.h
@@ -85,5 +85,8 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* _ASM_SOCKET_H */
diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig
index dd3ac75776ad..2e20333cbce9 100644
--- a/arch/h8300/Kconfig
+++ b/arch/h8300/Kconfig
@@ -17,6 +17,7 @@ config H8300
select HAVE_MEMBLOCK
select HAVE_DMA_ATTRS
select CLKSRC_OF
+ select H8300_TMR8
config RWSEM_GENERIC_SPINLOCK
def_bool y
diff --git a/arch/h8300/include/asm/io.h b/arch/h8300/include/asm/io.h
index bb837cded268..f0e14f3a800d 100644
--- a/arch/h8300/include/asm/io.h
+++ b/arch/h8300/include/asm/io.h
@@ -3,40 +3,45 @@
#ifdef __KERNEL__
-#include <asm-generic/io.h>
-
/* H8/300 internal I/O functions */
-static inline unsigned char ctrl_inb(unsigned long addr)
+
+#define __raw_readb __raw_readb
+static inline u8 __raw_readb(const volatile void __iomem *addr)
{
- return *(volatile unsigned char *)addr;
+ return *(volatile u8 *)addr;
}
-static inline unsigned short ctrl_inw(unsigned long addr)
+#define __raw_readw __raw_readw
+static inline u16 __raw_readw(const volatile void __iomem *addr)
{
- return *(volatile unsigned short *)addr;
+ return *(volatile u16 *)addr;
}
-static inline unsigned long ctrl_inl(unsigned long addr)
+#define __raw_readl __raw_readl
+static inline u32 __raw_readl(const volatile void __iomem *addr)
{
- return *(volatile unsigned long *)addr;
+ return *(volatile u32 *)addr;
}
-static inline void ctrl_outb(unsigned char b, unsigned long addr)
+#define __raw_writeb __raw_writeb
+static inline void __raw_writeb(u8 b, const volatile void __iomem *addr)
{
- *(volatile unsigned char *)addr = b;
+ *(volatile u8 *)addr = b;
}
-static inline void ctrl_outw(unsigned short b, unsigned long addr)
+#define __raw_writew __raw_writew
+static inline void __raw_writew(u16 b, const volatile void __iomem *addr)
{
- *(volatile unsigned short *)addr = b;
+ *(volatile u16 *)addr = b;
}
-static inline void ctrl_outl(unsigned long b, unsigned long addr)
+#define __raw_writel __raw_writel
+static inline void __raw_writel(u32 b, const volatile void __iomem *addr)
{
- *(volatile unsigned long *)addr = b;
+ *(volatile u32 *)addr = b;
}
-static inline void ctrl_bclr(int b, unsigned char *addr)
+static inline void ctrl_bclr(int b, void __iomem *addr)
{
if (__builtin_constant_p(b))
__asm__("bclr %1,%0" : "+WU"(*addr): "i"(b));
@@ -44,7 +49,7 @@ static inline void ctrl_bclr(int b, unsigned char *addr)
__asm__("bclr %w1,%0" : "+WU"(*addr): "r"(b));
}
-static inline void ctrl_bset(int b, unsigned char *addr)
+static inline void ctrl_bset(int b, void __iomem *addr)
{
if (__builtin_constant_p(b))
__asm__("bset %1,%0" : "+WU"(*addr): "i"(b));
@@ -52,6 +57,8 @@ static inline void ctrl_bset(int b, unsigned char *addr)
__asm__("bset %w1,%0" : "+WU"(*addr): "r"(b));
}
+#include <asm-generic/io.h>
+
#endif /* __KERNEL__ */
#endif /* _H8300_IO_H */
diff --git a/arch/h8300/kernel/setup.c b/arch/h8300/kernel/setup.c
index c772abe6d19c..e4985dfa91dc 100644
--- a/arch/h8300/kernel/setup.c
+++ b/arch/h8300/kernel/setup.c
@@ -207,14 +207,14 @@ device_initcall(device_probe);
#define get_wait(base, addr) ({ \
int baddr; \
baddr = ((addr) / 0x200000 * 2); \
- w *= (ctrl_inw((unsigned long)(base) + 2) & (3 << baddr)) + 1; \
+ w *= (readw((base) + 2) & (3 << baddr)) + 1; \
})
#endif
#if defined(CONFIG_CPU_H8S)
#define get_wait(base, addr) ({ \
int baddr; \
baddr = ((addr) / 0x200000 * 16); \
- w *= (ctrl_inl((unsigned long)(base) + 2) & (7 << baddr)) + 1; \
+ w *= (readl((base) + 2) & (7 << baddr)) + 1; \
})
#endif
@@ -228,8 +228,8 @@ static __init int access_timing(void)
bsc = of_find_compatible_node(NULL, NULL, "renesas,h8300-bsc");
base = of_iomap(bsc, 0);
- w = (ctrl_inb((unsigned long)base + 0) & bit)?2:1;
- if (ctrl_inb((unsigned long)base + 1) & bit)
+ w = (readb(base + 0) & bit)?2:1;
+ if (readb(base + 1) & bit)
w *= get_wait(base, addr);
else
w *= 2;
diff --git a/arch/ia64/include/asm/barrier.h b/arch/ia64/include/asm/barrier.h
index df896a1c41d3..209c4b817c95 100644
--- a/arch/ia64/include/asm/barrier.h
+++ b/arch/ia64/include/asm/barrier.h
@@ -77,7 +77,7 @@ do { \
___p1; \
})
-#define smp_store_mb(var, value) do { WRITE_ONCE(var, value); mb(); } while (0)
+#define smp_store_mb(var, value) do { WRITE_ONCE(var, value); smp_mb(); } while (0)
/*
* The group barrier in front of the rsm & ssm are necessary to ensure
diff --git a/arch/ia64/include/asm/early_ioremap.h b/arch/ia64/include/asm/early_ioremap.h
new file mode 100644
index 000000000000..eec9e1d1b833
--- /dev/null
+++ b/arch/ia64/include/asm/early_ioremap.h
@@ -0,0 +1,10 @@
+#ifndef _ASM_IA64_EARLY_IOREMAP_H
+#define _ASM_IA64_EARLY_IOREMAP_H
+
+extern void __iomem * early_ioremap (unsigned long phys_addr, unsigned long size);
+#define early_memremap(phys_addr, size) early_ioremap(phys_addr, size)
+
+extern void early_iounmap (volatile void __iomem *addr, unsigned long size);
+#define early_memunmap(addr, size) early_iounmap(addr, size)
+
+#endif
diff --git a/arch/ia64/include/asm/io.h b/arch/ia64/include/asm/io.h
index 9041bbe2b7b4..a865d2a04f75 100644
--- a/arch/ia64/include/asm/io.h
+++ b/arch/ia64/include/asm/io.h
@@ -20,6 +20,7 @@
*/
#include <asm/unaligned.h>
+#include <asm/early_ioremap.h>
/* We don't use IO slowdowns on the ia64, but.. */
#define __SLOW_DOWN_IO do { } while (0)
@@ -427,10 +428,6 @@ __writeq (unsigned long val, volatile void __iomem *addr)
extern void __iomem * ioremap(unsigned long offset, unsigned long size);
extern void __iomem * ioremap_nocache (unsigned long offset, unsigned long size);
extern void iounmap (volatile void __iomem *addr);
-extern void __iomem * early_ioremap (unsigned long phys_addr, unsigned long size);
-#define early_memremap(phys_addr, size) early_ioremap(phys_addr, size)
-extern void early_iounmap (volatile void __iomem *addr, unsigned long size);
-#define early_memunmap(addr, size) early_iounmap(addr, size)
static inline void __iomem * ioremap_cache (unsigned long phys_addr, unsigned long size)
{
return ioremap(phys_addr, size);
diff --git a/arch/ia64/include/asm/percpu.h b/arch/ia64/include/asm/percpu.h
index 0ec484d2dcbc..b9295793a5e2 100644
--- a/arch/ia64/include/asm/percpu.h
+++ b/arch/ia64/include/asm/percpu.h
@@ -6,8 +6,6 @@
* David Mosberger-Tang <davidm@hpl.hp.com>
*/
-#define PERCPU_ENOUGH_ROOM PERCPU_PAGE_SIZE
-
#ifdef __ASSEMBLY__
# define THIS_CPU(var) (var) /* use this to mark accesses to per-CPU variables... */
#else /* !__ASSEMBLY__ */
diff --git a/arch/ia64/include/asm/unistd.h b/arch/ia64/include/asm/unistd.h
index db73390568c8..74c132d901bd 100644
--- a/arch/ia64/include/asm/unistd.h
+++ b/arch/ia64/include/asm/unistd.h
@@ -11,7 +11,7 @@
-#define NR_syscalls 322 /* length of syscall table */
+#define NR_syscalls 323 /* length of syscall table */
/*
* The following defines stop scripts/checksyscalls.sh from complaining about
diff --git a/arch/ia64/include/uapi/asm/socket.h b/arch/ia64/include/uapi/asm/socket.h
index 59be3d87f86d..bce29166de1b 100644
--- a/arch/ia64/include/uapi/asm/socket.h
+++ b/arch/ia64/include/uapi/asm/socket.h
@@ -94,4 +94,7 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* _ASM_IA64_SOCKET_H */
diff --git a/arch/ia64/include/uapi/asm/unistd.h b/arch/ia64/include/uapi/asm/unistd.h
index 9038726e7d26..762edce7572e 100644
--- a/arch/ia64/include/uapi/asm/unistd.h
+++ b/arch/ia64/include/uapi/asm/unistd.h
@@ -335,5 +335,6 @@
#define __NR_userfaultfd 1343
#define __NR_membarrier 1344
#define __NR_kcmp 1345
+#define __NR_mlock2 1346
#endif /* _UAPI_ASM_IA64_UNISTD_H */
diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S
index dcd97f84d065..534a74acb849 100644
--- a/arch/ia64/kernel/entry.S
+++ b/arch/ia64/kernel/entry.S
@@ -1771,5 +1771,6 @@ sys_call_table:
data8 sys_userfaultfd
data8 sys_membarrier
data8 sys_kcmp // 1345
+ data8 sys_mlock2
.org sys_call_table + 8*NR_syscalls // guard against failures to increase NR_syscalls
diff --git a/arch/ia64/kernel/ftrace.c b/arch/ia64/kernel/ftrace.c
index 3b0c2aa07857..cee411e647ca 100644
--- a/arch/ia64/kernel/ftrace.c
+++ b/arch/ia64/kernel/ftrace.c
@@ -97,13 +97,11 @@ ftrace_modify_code(unsigned long ip, unsigned char *old_code,
unsigned char replaced[MCOUNT_INSN_SIZE];
/*
- * Note: Due to modules and __init, code can
- * disappear and change, we need to protect against faulting
- * as well as code changing. We do this by using the
- * probe_kernel_* functions.
- *
- * No real locking needed, this code is run through
- * kstop_machine, or before SMP starts.
+ * Note:
+ * We are paranoid about modifying text, as if a bug was to happen, it
+ * could cause us to read or write to someplace that could cause harm.
+ * Carefully read and modify the code with probe_kernel_*(), and make
+ * sure what we read is what we expected it to be before modifying it.
*/
if (!do_check)
diff --git a/arch/ia64/kernel/module.c b/arch/ia64/kernel/module.c
index b15933c31b2f..6ab0ae7d6535 100644
--- a/arch/ia64/kernel/module.c
+++ b/arch/ia64/kernel/module.c
@@ -486,13 +486,13 @@ module_frob_arch_sections (Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, char *secstrings,
static inline int
in_init (const struct module *mod, uint64_t addr)
{
- return addr - (uint64_t) mod->module_init < mod->init_size;
+ return addr - (uint64_t) mod->init_layout.base < mod->init_layout.size;
}
static inline int
in_core (const struct module *mod, uint64_t addr)
{
- return addr - (uint64_t) mod->module_core < mod->core_size;
+ return addr - (uint64_t) mod->core_layout.base < mod->core_layout.size;
}
static inline int
@@ -675,7 +675,7 @@ do_reloc (struct module *mod, uint8_t r_type, Elf64_Sym *sym, uint64_t addend,
break;
case RV_BDREL:
- val -= (uint64_t) (in_init(mod, val) ? mod->module_init : mod->module_core);
+ val -= (uint64_t) (in_init(mod, val) ? mod->init_layout.base : mod->core_layout.base);
break;
case RV_LTV:
@@ -810,15 +810,15 @@ apply_relocate_add (Elf64_Shdr *sechdrs, const char *strtab, unsigned int symind
* addresses have been selected...
*/
uint64_t gp;
- if (mod->core_size > MAX_LTOFF)
+ if (mod->core_layout.size > MAX_LTOFF)
/*
* This takes advantage of fact that SHF_ARCH_SMALL gets allocated
* at the end of the module.
*/
- gp = mod->core_size - MAX_LTOFF / 2;
+ gp = mod->core_layout.size - MAX_LTOFF / 2;
else
- gp = mod->core_size / 2;
- gp = (uint64_t) mod->module_core + ((gp + 7) & -8);
+ gp = mod->core_layout.size / 2;
+ gp = (uint64_t) mod->core_layout.base + ((gp + 7) & -8);
mod->arch.gp = gp;
DEBUGP("%s: placing gp at 0x%lx\n", __func__, gp);
}
diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c
index 60e02f7747ff..9cd607b06964 100644
--- a/arch/ia64/kernel/perfmon.c
+++ b/arch/ia64/kernel/perfmon.c
@@ -2332,8 +2332,7 @@ pfm_smpl_buffer_alloc(struct task_struct *task, struct file *filp, pfm_context_t
*/
insert_vm_struct(mm, vma);
- vm_stat_account(vma->vm_mm, vma->vm_flags, vma->vm_file,
- vma_pages(vma));
+ vm_stat_account(vma->vm_mm, vma->vm_flags, vma_pages(vma));
up_write(&task->mm->mmap_sem);
/*
diff --git a/arch/m32r/Kconfig b/arch/m32r/Kconfig
index 9e44bbd8051e..836ac5a963c8 100644
--- a/arch/m32r/Kconfig
+++ b/arch/m32r/Kconfig
@@ -13,6 +13,7 @@ config M32R
select GENERIC_IRQ_PROBE
select GENERIC_IRQ_SHOW
select GENERIC_ATOMIC64
+ select ARCH_HAS_DEVMEM_IS_ALLOWED
select ARCH_USES_GETTIMEOFFSET
select MODULES_USE_ELF_RELA
select HAVE_DEBUG_STACKOVERFLOW
diff --git a/arch/m32r/include/asm/Kbuild b/arch/m32r/include/asm/Kbuild
index fd104bd221ce..860e440611c9 100644
--- a/arch/m32r/include/asm/Kbuild
+++ b/arch/m32r/include/asm/Kbuild
@@ -3,6 +3,7 @@ generic-y += clkdev.h
generic-y += cputime.h
generic-y += exec.h
generic-y += irq_work.h
+generic-y += kvm_para.h
generic-y += mcs_spinlock.h
generic-y += mm-arch-hooks.h
generic-y += module.h
diff --git a/arch/m32r/include/asm/io.h b/arch/m32r/include/asm/io.h
index 61b8931bc192..4b0f5e001d4d 100644
--- a/arch/m32r/include/asm/io.h
+++ b/arch/m32r/include/asm/io.h
@@ -168,13 +168,21 @@ static inline void _writel(unsigned long l, unsigned long addr)
#define writew_relaxed writew
#define writel_relaxed writel
-#define ioread8 read
+#define ioread8 readb
#define ioread16 readw
#define ioread32 readl
#define iowrite8 writeb
#define iowrite16 writew
#define iowrite32 writel
+#define ioread8_rep(p, dst, count) insb((unsigned long)(p), (dst), (count))
+#define ioread16_rep(p, dst, count) insw((unsigned long)(p), (dst), (count))
+#define ioread32_rep(p, dst, count) insl((unsigned long)(p), (dst), (count))
+
+#define iowrite8_rep(p, src, count) outsb((unsigned long)(p), (src), (count))
+#define iowrite16_rep(p, src, count) outsw((unsigned long)(p), (src), (count))
+#define iowrite32_rep(p, src, count) outsl((unsigned long)(p), (src), (count))
+
#define ioread16be(addr) be16_to_cpu(readw(addr))
#define ioread32be(addr) be32_to_cpu(readl(addr))
#define iowrite16be(v, addr) writew(cpu_to_be16(v), (addr))
diff --git a/arch/m32r/include/uapi/asm/socket.h b/arch/m32r/include/uapi/asm/socket.h
index 7bc4cb273856..14aa4a6bccf1 100644
--- a/arch/m32r/include/uapi/asm/socket.h
+++ b/arch/m32r/include/uapi/asm/socket.h
@@ -85,4 +85,7 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* _ASM_M32R_SOCKET_H */
diff --git a/arch/m32r/kernel/setup.c b/arch/m32r/kernel/setup.c
index 0392112a5d70..a5ecef7188ba 100644
--- a/arch/m32r/kernel/setup.c
+++ b/arch/m32r/kernel/setup.c
@@ -81,7 +81,10 @@ static struct resource code_resource = {
};
unsigned long memory_start;
+EXPORT_SYMBOL(memory_start);
+
unsigned long memory_end;
+EXPORT_SYMBOL(memory_end);
void __init setup_arch(char **);
int get_cpuinfo(char *);
diff --git a/arch/m68k/atari/config.c b/arch/m68k/atari/config.c
index 192b00f098f4..cbd5991fd49a 100644
--- a/arch/m68k/atari/config.c
+++ b/arch/m68k/atari/config.c
@@ -858,7 +858,7 @@ static struct platform_device *atari_netusbee_devices[] __initdata = {
};
#endif /* CONFIG_ATARI_ETHERNEC */
-#ifdef CONFIG_ATARI_SCSI
+#if IS_ENABLED(CONFIG_ATARI_SCSI)
static const struct resource atari_scsi_st_rsrc[] __initconst = {
{
.flags = IORESOURCE_IRQ,
@@ -910,7 +910,7 @@ int __init atari_platform_init(void)
}
#endif
-#ifdef CONFIG_ATARI_SCSI
+#if IS_ENABLED(CONFIG_ATARI_SCSI)
if (ATARIHW_PRESENT(ST_SCSI))
platform_device_register_simple("atari_scsi", -1,
atari_scsi_st_rsrc, ARRAY_SIZE(atari_scsi_st_rsrc));
diff --git a/arch/m68k/coldfire/gpio.c b/arch/m68k/coldfire/gpio.c
index e7e428681ec5..37a83e27c7a6 100644
--- a/arch/m68k/coldfire/gpio.c
+++ b/arch/m68k/coldfire/gpio.c
@@ -121,7 +121,7 @@ static int mcfgpio_direction_input(struct gpio_chip *chip, unsigned offset)
static int mcfgpio_get_value(struct gpio_chip *chip, unsigned offset)
{
- return __mcfgpio_get_value(offset);
+ return !!__mcfgpio_get_value(offset);
}
static int mcfgpio_direction_output(struct gpio_chip *chip, unsigned offset,
diff --git a/arch/m68k/coldfire/m54xx.c b/arch/m68k/coldfire/m54xx.c
index f7836c6a6b60..c32f76791f48 100644
--- a/arch/m68k/coldfire/m54xx.c
+++ b/arch/m68k/coldfire/m54xx.c
@@ -98,7 +98,7 @@ static void __init mcf54xx_bootmem_alloc(void)
memstart = PAGE_ALIGN(_ramstart);
min_low_pfn = PFN_DOWN(_rambase);
start_pfn = PFN_DOWN(memstart);
- max_low_pfn = PFN_DOWN(_ramend);
+ max_pfn = max_low_pfn = PFN_DOWN(_ramend);
high_memory = (void *)_ramend;
m68k_virt_to_node_shift = fls(_ramend - _rambase - 1) - 6;
diff --git a/arch/m68k/configs/amiga_defconfig b/arch/m68k/configs/amiga_defconfig
index 5b4ec541ba7c..fc96e814188e 100644
--- a/arch/m68k/configs/amiga_defconfig
+++ b/arch/m68k/configs/amiga_defconfig
@@ -276,6 +276,7 @@ CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
CONFIG_MPLS_ROUTING=m
CONFIG_MPLS_IPTUNNEL=m
+CONFIG_NET_L3_MASTER_DEV=y
# CONFIG_WIRELESS is not set
# CONFIG_UEVENT_HELPER is not set
CONFIG_DEVTMPFS=y
@@ -532,11 +533,13 @@ CONFIG_NLS_MAC_INUIT=m
CONFIG_NLS_MAC_ROMANIAN=m
CONFIG_NLS_MAC_TURKISH=m
CONFIG_DLM=m
+# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
CONFIG_MAGIC_SYSRQ=y
CONFIG_ASYNC_RAID6_TEST=m
CONFIG_TEST_HEXDUMP=m
CONFIG_TEST_STRING_HELPERS=m
CONFIG_TEST_KSTRTOX=m
+CONFIG_TEST_PRINTF=m
CONFIG_TEST_RHASHTABLE=m
CONFIG_TEST_LKM=m
CONFIG_TEST_USER_COPY=m
@@ -559,6 +562,7 @@ CONFIG_CRYPTO_CTS=m
CONFIG_CRYPTO_LRW=m
CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_XTS=m
+CONFIG_CRYPTO_KEYWRAP=m
CONFIG_CRYPTO_XCBC=m
CONFIG_CRYPTO_VMAC=m
CONFIG_CRYPTO_MICHAEL_MIC=m
diff --git a/arch/m68k/configs/apollo_defconfig b/arch/m68k/configs/apollo_defconfig
index 6e5198e2c124..05c904f08d9d 100644
--- a/arch/m68k/configs/apollo_defconfig
+++ b/arch/m68k/configs/apollo_defconfig
@@ -274,6 +274,7 @@ CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
CONFIG_MPLS_ROUTING=m
CONFIG_MPLS_IPTUNNEL=m
+CONFIG_NET_L3_MASTER_DEV=y
# CONFIG_WIRELESS is not set
# CONFIG_UEVENT_HELPER is not set
CONFIG_DEVTMPFS=y
@@ -372,6 +373,7 @@ CONFIG_INPUT_EVDEV=m
# CONFIG_MOUSE_PS2 is not set
CONFIG_MOUSE_SERIAL=m
CONFIG_SERIO=m
+CONFIG_USERIO=m
# CONFIG_LEGACY_PTYS is not set
# CONFIG_DEVKMEM is not set
# CONFIG_HW_RANDOM is not set
@@ -490,11 +492,13 @@ CONFIG_NLS_MAC_INUIT=m
CONFIG_NLS_MAC_ROMANIAN=m
CONFIG_NLS_MAC_TURKISH=m
CONFIG_DLM=m
+# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
CONFIG_MAGIC_SYSRQ=y
CONFIG_ASYNC_RAID6_TEST=m
CONFIG_TEST_HEXDUMP=m
CONFIG_TEST_STRING_HELPERS=m
CONFIG_TEST_KSTRTOX=m
+CONFIG_TEST_PRINTF=m
CONFIG_TEST_RHASHTABLE=m
CONFIG_TEST_LKM=m
CONFIG_TEST_USER_COPY=m
@@ -517,6 +521,7 @@ CONFIG_CRYPTO_CTS=m
CONFIG_CRYPTO_LRW=m
CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_XTS=m
+CONFIG_CRYPTO_KEYWRAP=m
CONFIG_CRYPTO_XCBC=m
CONFIG_CRYPTO_VMAC=m
CONFIG_CRYPTO_MICHAEL_MIC=m
diff --git a/arch/m68k/configs/atari_defconfig b/arch/m68k/configs/atari_defconfig
index f75600b0ca23..d572b731c510 100644
--- a/arch/m68k/configs/atari_defconfig
+++ b/arch/m68k/configs/atari_defconfig
@@ -274,6 +274,7 @@ CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
CONFIG_MPLS_ROUTING=m
CONFIG_MPLS_IPTUNNEL=m
+CONFIG_NET_L3_MASTER_DEV=y
# CONFIG_WIRELESS is not set
# CONFIG_UEVENT_HELPER is not set
CONFIG_DEVTMPFS=y
@@ -512,11 +513,13 @@ CONFIG_NLS_MAC_INUIT=m
CONFIG_NLS_MAC_ROMANIAN=m
CONFIG_NLS_MAC_TURKISH=m
CONFIG_DLM=m
+# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
CONFIG_MAGIC_SYSRQ=y
CONFIG_ASYNC_RAID6_TEST=m
CONFIG_TEST_HEXDUMP=m
CONFIG_TEST_STRING_HELPERS=m
CONFIG_TEST_KSTRTOX=m
+CONFIG_TEST_PRINTF=m
CONFIG_TEST_RHASHTABLE=m
CONFIG_TEST_LKM=m
CONFIG_TEST_USER_COPY=m
@@ -539,6 +542,7 @@ CONFIG_CRYPTO_CTS=m
CONFIG_CRYPTO_LRW=m
CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_XTS=m
+CONFIG_CRYPTO_KEYWRAP=m
CONFIG_CRYPTO_XCBC=m
CONFIG_CRYPTO_VMAC=m
CONFIG_CRYPTO_MICHAEL_MIC=m
diff --git a/arch/m68k/configs/bvme6000_defconfig b/arch/m68k/configs/bvme6000_defconfig
index a42d91c389a6..11a30c65ad44 100644
--- a/arch/m68k/configs/bvme6000_defconfig
+++ b/arch/m68k/configs/bvme6000_defconfig
@@ -272,6 +272,7 @@ CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
CONFIG_MPLS_ROUTING=m
CONFIG_MPLS_IPTUNNEL=m
+CONFIG_NET_L3_MASTER_DEV=y
# CONFIG_WIRELESS is not set
# CONFIG_UEVENT_HELPER is not set
CONFIG_DEVTMPFS=y
@@ -483,11 +484,13 @@ CONFIG_NLS_MAC_INUIT=m
CONFIG_NLS_MAC_ROMANIAN=m
CONFIG_NLS_MAC_TURKISH=m
CONFIG_DLM=m
+# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
CONFIG_MAGIC_SYSRQ=y
CONFIG_ASYNC_RAID6_TEST=m
CONFIG_TEST_HEXDUMP=m
CONFIG_TEST_STRING_HELPERS=m
CONFIG_TEST_KSTRTOX=m
+CONFIG_TEST_PRINTF=m
CONFIG_TEST_RHASHTABLE=m
CONFIG_TEST_LKM=m
CONFIG_TEST_USER_COPY=m
@@ -510,6 +513,7 @@ CONFIG_CRYPTO_CTS=m
CONFIG_CRYPTO_LRW=m
CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_XTS=m
+CONFIG_CRYPTO_KEYWRAP=m
CONFIG_CRYPTO_XCBC=m
CONFIG_CRYPTO_VMAC=m
CONFIG_CRYPTO_MICHAEL_MIC=m
diff --git a/arch/m68k/configs/hp300_defconfig b/arch/m68k/configs/hp300_defconfig
index 77f4a11083e9..6630a5154b9d 100644
--- a/arch/m68k/configs/hp300_defconfig
+++ b/arch/m68k/configs/hp300_defconfig
@@ -274,6 +274,7 @@ CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
CONFIG_MPLS_ROUTING=m
CONFIG_MPLS_IPTUNNEL=m
+CONFIG_NET_L3_MASTER_DEV=y
# CONFIG_WIRELESS is not set
# CONFIG_UEVENT_HELPER is not set
CONFIG_DEVTMPFS=y
@@ -375,6 +376,7 @@ CONFIG_MOUSE_SERIAL=m
CONFIG_INPUT_MISC=y
CONFIG_HP_SDC_RTC=m
CONFIG_SERIO_SERPORT=m
+CONFIG_USERIO=m
# CONFIG_LEGACY_PTYS is not set
# CONFIG_DEVKMEM is not set
# CONFIG_HW_RANDOM is not set
@@ -492,11 +494,13 @@ CONFIG_NLS_MAC_INUIT=m
CONFIG_NLS_MAC_ROMANIAN=m
CONFIG_NLS_MAC_TURKISH=m
CONFIG_DLM=m
+# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
CONFIG_MAGIC_SYSRQ=y
CONFIG_ASYNC_RAID6_TEST=m
CONFIG_TEST_HEXDUMP=m
CONFIG_TEST_STRING_HELPERS=m
CONFIG_TEST_KSTRTOX=m
+CONFIG_TEST_PRINTF=m
CONFIG_TEST_RHASHTABLE=m
CONFIG_TEST_LKM=m
CONFIG_TEST_USER_COPY=m
@@ -519,6 +523,7 @@ CONFIG_CRYPTO_CTS=m
CONFIG_CRYPTO_LRW=m
CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_XTS=m
+CONFIG_CRYPTO_KEYWRAP=m
CONFIG_CRYPTO_XCBC=m
CONFIG_CRYPTO_VMAC=m
CONFIG_CRYPTO_MICHAEL_MIC=m
diff --git a/arch/m68k/configs/mac_defconfig b/arch/m68k/configs/mac_defconfig
index 5a329f77329b..1d90b71d0903 100644
--- a/arch/m68k/configs/mac_defconfig
+++ b/arch/m68k/configs/mac_defconfig
@@ -276,6 +276,7 @@ CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
CONFIG_MPLS_ROUTING=m
CONFIG_MPLS_IPTUNNEL=m
+CONFIG_NET_L3_MASTER_DEV=y
# CONFIG_WIRELESS is not set
# CONFIG_UEVENT_HELPER is not set
CONFIG_DEVTMPFS=y
@@ -394,6 +395,7 @@ CONFIG_MOUSE_SERIAL=m
CONFIG_INPUT_MISC=y
CONFIG_INPUT_M68K_BEEP=m
CONFIG_SERIO=m
+CONFIG_USERIO=m
# CONFIG_LEGACY_PTYS is not set
# CONFIG_DEVKMEM is not set
CONFIG_SERIAL_PMACZILOG=y
@@ -514,11 +516,13 @@ CONFIG_NLS_MAC_INUIT=m
CONFIG_NLS_MAC_ROMANIAN=m
CONFIG_NLS_MAC_TURKISH=m
CONFIG_DLM=m
+# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
CONFIG_MAGIC_SYSRQ=y
CONFIG_ASYNC_RAID6_TEST=m
CONFIG_TEST_HEXDUMP=m
CONFIG_TEST_STRING_HELPERS=m
CONFIG_TEST_KSTRTOX=m
+CONFIG_TEST_PRINTF=m
CONFIG_TEST_RHASHTABLE=m
CONFIG_TEST_LKM=m
CONFIG_TEST_USER_COPY=m
@@ -541,6 +545,7 @@ CONFIG_CRYPTO_CTS=m
CONFIG_CRYPTO_LRW=m
CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_XTS=m
+CONFIG_CRYPTO_KEYWRAP=m
CONFIG_CRYPTO_XCBC=m
CONFIG_CRYPTO_VMAC=m
CONFIG_CRYPTO_MICHAEL_MIC=m
diff --git a/arch/m68k/configs/multi_defconfig b/arch/m68k/configs/multi_defconfig
index 83c80d2030ec..1fd21c1ca87f 100644
--- a/arch/m68k/configs/multi_defconfig
+++ b/arch/m68k/configs/multi_defconfig
@@ -286,6 +286,7 @@ CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
CONFIG_MPLS_ROUTING=m
CONFIG_MPLS_IPTUNNEL=m
+CONFIG_NET_L3_MASTER_DEV=y
# CONFIG_WIRELESS is not set
# CONFIG_UEVENT_HELPER is not set
CONFIG_DEVTMPFS=y
@@ -449,6 +450,7 @@ CONFIG_INPUT_MISC=y
CONFIG_INPUT_M68K_BEEP=m
CONFIG_HP_SDC_RTC=m
CONFIG_SERIO_Q40KBD=y
+CONFIG_USERIO=m
# CONFIG_LEGACY_PTYS is not set
# CONFIG_DEVKMEM is not set
CONFIG_SERIAL_PMACZILOG=y
@@ -594,11 +596,13 @@ CONFIG_NLS_MAC_INUIT=m
CONFIG_NLS_MAC_ROMANIAN=m
CONFIG_NLS_MAC_TURKISH=m
CONFIG_DLM=m
+# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
CONFIG_MAGIC_SYSRQ=y
CONFIG_ASYNC_RAID6_TEST=m
CONFIG_TEST_HEXDUMP=m
CONFIG_TEST_STRING_HELPERS=m
CONFIG_TEST_KSTRTOX=m
+CONFIG_TEST_PRINTF=m
CONFIG_TEST_RHASHTABLE=m
CONFIG_TEST_LKM=m
CONFIG_TEST_USER_COPY=m
@@ -621,6 +625,7 @@ CONFIG_CRYPTO_CTS=m
CONFIG_CRYPTO_LRW=m
CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_XTS=m
+CONFIG_CRYPTO_KEYWRAP=m
CONFIG_CRYPTO_XCBC=m
CONFIG_CRYPTO_VMAC=m
CONFIG_CRYPTO_MICHAEL_MIC=m
diff --git a/arch/m68k/configs/mvme147_defconfig b/arch/m68k/configs/mvme147_defconfig
index 6cb42c3bf5a2..74e10f79d7b1 100644
--- a/arch/m68k/configs/mvme147_defconfig
+++ b/arch/m68k/configs/mvme147_defconfig
@@ -271,6 +271,7 @@ CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
CONFIG_MPLS_ROUTING=m
CONFIG_MPLS_IPTUNNEL=m
+CONFIG_NET_L3_MASTER_DEV=y
# CONFIG_WIRELESS is not set
# CONFIG_UEVENT_HELPER is not set
CONFIG_DEVTMPFS=y
@@ -483,11 +484,13 @@ CONFIG_NLS_MAC_INUIT=m
CONFIG_NLS_MAC_ROMANIAN=m
CONFIG_NLS_MAC_TURKISH=m
CONFIG_DLM=m
+# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
CONFIG_MAGIC_SYSRQ=y
CONFIG_ASYNC_RAID6_TEST=m
CONFIG_TEST_HEXDUMP=m
CONFIG_TEST_STRING_HELPERS=m
CONFIG_TEST_KSTRTOX=m
+CONFIG_TEST_PRINTF=m
CONFIG_TEST_RHASHTABLE=m
CONFIG_TEST_LKM=m
CONFIG_TEST_USER_COPY=m
@@ -510,6 +513,7 @@ CONFIG_CRYPTO_CTS=m
CONFIG_CRYPTO_LRW=m
CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_XTS=m
+CONFIG_CRYPTO_KEYWRAP=m
CONFIG_CRYPTO_XCBC=m
CONFIG_CRYPTO_VMAC=m
CONFIG_CRYPTO_MICHAEL_MIC=m
diff --git a/arch/m68k/configs/mvme16x_defconfig b/arch/m68k/configs/mvme16x_defconfig
index c7508c30330c..7034e716f166 100644
--- a/arch/m68k/configs/mvme16x_defconfig
+++ b/arch/m68k/configs/mvme16x_defconfig
@@ -272,6 +272,7 @@ CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
CONFIG_MPLS_ROUTING=m
CONFIG_MPLS_IPTUNNEL=m
+CONFIG_NET_L3_MASTER_DEV=y
# CONFIG_WIRELESS is not set
# CONFIG_UEVENT_HELPER is not set
CONFIG_DEVTMPFS=y
@@ -483,11 +484,13 @@ CONFIG_NLS_MAC_INUIT=m
CONFIG_NLS_MAC_ROMANIAN=m
CONFIG_NLS_MAC_TURKISH=m
CONFIG_DLM=m
+# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
CONFIG_MAGIC_SYSRQ=y
CONFIG_ASYNC_RAID6_TEST=m
CONFIG_TEST_HEXDUMP=m
CONFIG_TEST_STRING_HELPERS=m
CONFIG_TEST_KSTRTOX=m
+CONFIG_TEST_PRINTF=m
CONFIG_TEST_RHASHTABLE=m
CONFIG_TEST_LKM=m
CONFIG_TEST_USER_COPY=m
@@ -510,6 +513,7 @@ CONFIG_CRYPTO_CTS=m
CONFIG_CRYPTO_LRW=m
CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_XTS=m
+CONFIG_CRYPTO_KEYWRAP=m
CONFIG_CRYPTO_XCBC=m
CONFIG_CRYPTO_VMAC=m
CONFIG_CRYPTO_MICHAEL_MIC=m
diff --git a/arch/m68k/configs/q40_defconfig b/arch/m68k/configs/q40_defconfig
index 64b71664a303..f7deb5f702a6 100644
--- a/arch/m68k/configs/q40_defconfig
+++ b/arch/m68k/configs/q40_defconfig
@@ -272,6 +272,7 @@ CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
CONFIG_MPLS_ROUTING=m
CONFIG_MPLS_IPTUNNEL=m
+CONFIG_NET_L3_MASTER_DEV=y
# CONFIG_WIRELESS is not set
# CONFIG_UEVENT_HELPER is not set
CONFIG_DEVTMPFS=y
@@ -385,6 +386,7 @@ CONFIG_MOUSE_SERIAL=m
CONFIG_INPUT_MISC=y
CONFIG_INPUT_M68K_BEEP=m
CONFIG_SERIO_Q40KBD=y
+CONFIG_USERIO=m
# CONFIG_LEGACY_PTYS is not set
# CONFIG_DEVKMEM is not set
CONFIG_PRINTER=m
@@ -505,11 +507,13 @@ CONFIG_NLS_MAC_INUIT=m
CONFIG_NLS_MAC_ROMANIAN=m
CONFIG_NLS_MAC_TURKISH=m
CONFIG_DLM=m
+# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
CONFIG_MAGIC_SYSRQ=y
CONFIG_ASYNC_RAID6_TEST=m
CONFIG_TEST_HEXDUMP=m
CONFIG_TEST_STRING_HELPERS=m
CONFIG_TEST_KSTRTOX=m
+CONFIG_TEST_PRINTF=m
CONFIG_TEST_RHASHTABLE=m
CONFIG_TEST_LKM=m
CONFIG_TEST_USER_COPY=m
@@ -532,6 +536,7 @@ CONFIG_CRYPTO_CTS=m
CONFIG_CRYPTO_LRW=m
CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_XTS=m
+CONFIG_CRYPTO_KEYWRAP=m
CONFIG_CRYPTO_XCBC=m
CONFIG_CRYPTO_VMAC=m
CONFIG_CRYPTO_MICHAEL_MIC=m
diff --git a/arch/m68k/configs/sun3_defconfig b/arch/m68k/configs/sun3_defconfig
index 9a4cab78a2ea..0ce79eb0d805 100644
--- a/arch/m68k/configs/sun3_defconfig
+++ b/arch/m68k/configs/sun3_defconfig
@@ -269,6 +269,7 @@ CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
CONFIG_MPLS_ROUTING=m
CONFIG_MPLS_IPTUNNEL=m
+CONFIG_NET_L3_MASTER_DEV=y
# CONFIG_WIRELESS is not set
# CONFIG_UEVENT_HELPER is not set
CONFIG_DEVTMPFS=y
@@ -369,6 +370,7 @@ CONFIG_INPUT_EVDEV=m
CONFIG_KEYBOARD_SUNKBD=y
# CONFIG_MOUSE_PS2 is not set
CONFIG_MOUSE_SERIAL=m
+CONFIG_USERIO=m
# CONFIG_LEGACY_PTYS is not set
# CONFIG_DEVKMEM is not set
# CONFIG_HW_RANDOM is not set
@@ -484,11 +486,13 @@ CONFIG_NLS_MAC_INUIT=m
CONFIG_NLS_MAC_ROMANIAN=m
CONFIG_NLS_MAC_TURKISH=m
CONFIG_DLM=m
+# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
CONFIG_MAGIC_SYSRQ=y
CONFIG_ASYNC_RAID6_TEST=m
CONFIG_TEST_HEXDUMP=m
CONFIG_TEST_STRING_HELPERS=m
CONFIG_TEST_KSTRTOX=m
+CONFIG_TEST_PRINTF=m
CONFIG_TEST_RHASHTABLE=m
CONFIG_TEST_LKM=m
CONFIG_TEST_USER_COPY=m
@@ -510,6 +514,7 @@ CONFIG_CRYPTO_CTS=m
CONFIG_CRYPTO_LRW=m
CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_XTS=m
+CONFIG_CRYPTO_KEYWRAP=m
CONFIG_CRYPTO_XCBC=m
CONFIG_CRYPTO_VMAC=m
CONFIG_CRYPTO_MICHAEL_MIC=m
diff --git a/arch/m68k/configs/sun3x_defconfig b/arch/m68k/configs/sun3x_defconfig
index 1a2eaac13dbd..4cb787e4991f 100644
--- a/arch/m68k/configs/sun3x_defconfig
+++ b/arch/m68k/configs/sun3x_defconfig
@@ -269,6 +269,7 @@ CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
CONFIG_MPLS_ROUTING=m
CONFIG_MPLS_IPTUNNEL=m
+CONFIG_NET_L3_MASTER_DEV=y
# CONFIG_WIRELESS is not set
# CONFIG_UEVENT_HELPER is not set
CONFIG_DEVTMPFS=y
@@ -369,6 +370,7 @@ CONFIG_INPUT_EVDEV=m
CONFIG_KEYBOARD_SUNKBD=y
# CONFIG_MOUSE_PS2 is not set
CONFIG_MOUSE_SERIAL=m
+CONFIG_USERIO=m
# CONFIG_LEGACY_PTYS is not set
# CONFIG_DEVKMEM is not set
# CONFIG_HW_RANDOM is not set
@@ -484,11 +486,13 @@ CONFIG_NLS_MAC_INUIT=m
CONFIG_NLS_MAC_ROMANIAN=m
CONFIG_NLS_MAC_TURKISH=m
CONFIG_DLM=m
+# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set
CONFIG_MAGIC_SYSRQ=y
CONFIG_ASYNC_RAID6_TEST=m
CONFIG_TEST_HEXDUMP=m
CONFIG_TEST_STRING_HELPERS=m
CONFIG_TEST_KSTRTOX=m
+CONFIG_TEST_PRINTF=m
CONFIG_TEST_RHASHTABLE=m
CONFIG_TEST_LKM=m
CONFIG_TEST_USER_COPY=m
@@ -511,6 +515,7 @@ CONFIG_CRYPTO_CTS=m
CONFIG_CRYPTO_LRW=m
CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_XTS=m
+CONFIG_CRYPTO_KEYWRAP=m
CONFIG_CRYPTO_XCBC=m
CONFIG_CRYPTO_VMAC=m
CONFIG_CRYPTO_MICHAEL_MIC=m
diff --git a/arch/m68k/include/asm/mac_psc.h b/arch/m68k/include/asm/mac_psc.h
index e5c0d71d1543..923305117a69 100644
--- a/arch/m68k/include/asm/mac_psc.h
+++ b/arch/m68k/include/asm/mac_psc.h
@@ -209,7 +209,6 @@
#ifndef __ASSEMBLY__
extern volatile __u8 *psc;
-extern int psc_present;
extern void psc_register_interrupts(void);
extern void psc_irq_enable(int);
diff --git a/arch/m68k/include/asm/page.h b/arch/m68k/include/asm/page.h
index 38b024a0b045..430d4d54c883 100644
--- a/arch/m68k/include/asm/page.h
+++ b/arch/m68k/include/asm/page.h
@@ -48,6 +48,9 @@ extern unsigned long _ramend;
#include <asm/page_no.h>
#endif
+#define __phys_to_pfn(paddr) ((unsigned long)((paddr) >> PAGE_SHIFT))
+#define __pfn_to_phys(pfn) PFN_PHYS(pfn)
+
#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \
VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
diff --git a/arch/m68k/include/asm/uaccess_no.h b/arch/m68k/include/asm/uaccess_no.h
index 68bbe9b312f1..1bdf15263754 100644
--- a/arch/m68k/include/asm/uaccess_no.h
+++ b/arch/m68k/include/asm/uaccess_no.h
@@ -135,10 +135,6 @@ extern int __get_user_bad(void);
#define __copy_to_user_inatomic __copy_to_user
#define __copy_from_user_inatomic __copy_from_user
-#define copy_to_user_ret(to,from,n,retval) ({ if (copy_to_user(to,from,n)) return retval; })
-
-#define copy_from_user_ret(to,from,n,retval) ({ if (copy_from_user(to,from,n)) return retval; })
-
/*
* Copy a null terminated string from userspace.
*/
diff --git a/arch/m68k/include/asm/unistd.h b/arch/m68k/include/asm/unistd.h
index 0793a7f17417..f9d96bf86910 100644
--- a/arch/m68k/include/asm/unistd.h
+++ b/arch/m68k/include/asm/unistd.h
@@ -4,7 +4,7 @@
#include <uapi/asm/unistd.h>
-#define NR_syscalls 375
+#define NR_syscalls 376
#define __ARCH_WANT_OLD_READDIR
#define __ARCH_WANT_OLD_STAT
diff --git a/arch/m68k/include/uapi/asm/unistd.h b/arch/m68k/include/uapi/asm/unistd.h
index 5e6fae6c275f..36cf129de663 100644
--- a/arch/m68k/include/uapi/asm/unistd.h
+++ b/arch/m68k/include/uapi/asm/unistd.h
@@ -380,5 +380,6 @@
#define __NR_sendmmsg 372
#define __NR_userfaultfd 373
#define __NR_membarrier 374
+#define __NR_mlock2 375
#endif /* _UAPI_ASM_M68K_UNISTD_H_ */
diff --git a/arch/m68k/kernel/setup_no.c b/arch/m68k/kernel/setup_no.c
index 88c27d94a721..76b9113f3092 100644
--- a/arch/m68k/kernel/setup_no.c
+++ b/arch/m68k/kernel/setup_no.c
@@ -238,11 +238,14 @@ void __init setup_arch(char **cmdline_p)
* Give all the memory to the bootmap allocator, tell it to put the
* boot mem_map at the start of memory.
*/
+ min_low_pfn = PFN_DOWN(memory_start);
+ max_pfn = max_low_pfn = PFN_DOWN(memory_end);
+
bootmap_size = init_bootmem_node(
NODE_DATA(0),
- memory_start >> PAGE_SHIFT, /* map goes here */
- PAGE_OFFSET >> PAGE_SHIFT, /* 0 on coldfire */
- memory_end >> PAGE_SHIFT);
+ min_low_pfn, /* map goes here */
+ PFN_DOWN(PAGE_OFFSET),
+ max_pfn);
/*
* Free the usable memory, we have to make sure we do not free
* the bootmem bitmap so we then reserve it after freeing it :-)
diff --git a/arch/m68k/kernel/syscalltable.S b/arch/m68k/kernel/syscalltable.S
index 5dd0e80042f5..282cd903f4c4 100644
--- a/arch/m68k/kernel/syscalltable.S
+++ b/arch/m68k/kernel/syscalltable.S
@@ -395,3 +395,4 @@ ENTRY(sys_call_table)
.long sys_sendmmsg
.long sys_userfaultfd
.long sys_membarrier
+ .long sys_mlock2 /* 375 */
diff --git a/arch/m68k/mac/macints.c b/arch/m68k/mac/macints.c
index 5c1a6b2ff0af..9f98c0871901 100644
--- a/arch/m68k/mac/macints.c
+++ b/arch/m68k/mac/macints.c
@@ -174,7 +174,7 @@ void __init mac_init_IRQ(void)
oss_register_interrupts();
else
via_register_interrupts();
- if (psc_present)
+ if (psc)
psc_register_interrupts();
if (baboon_present)
baboon_register_interrupts();
@@ -212,7 +212,7 @@ void mac_irq_enable(struct irq_data *data)
case 4:
case 5:
case 6:
- if (psc_present)
+ if (psc)
psc_irq_enable(irq);
else if (oss_present)
oss_irq_enable(irq);
@@ -242,7 +242,7 @@ void mac_irq_disable(struct irq_data *data)
case 4:
case 5:
case 6:
- if (psc_present)
+ if (psc)
psc_irq_disable(irq);
else if (oss_present)
oss_irq_disable(irq);
diff --git a/arch/m68k/mac/psc.c b/arch/m68k/mac/psc.c
index 2290c0cae48b..cb2b1a3a2b62 100644
--- a/arch/m68k/mac/psc.c
+++ b/arch/m68k/mac/psc.c
@@ -27,7 +27,6 @@
#define DEBUG_PSC
-int psc_present;
volatile __u8 *psc;
EXPORT_SYMBOL_GPL(psc);
@@ -39,7 +38,9 @@ static void psc_debug_dump(void)
{
int i;
- if (!psc_present) return;
+ if (!psc)
+ return;
+
for (i = 0x30 ; i < 0x70 ; i += 0x10) {
printk("PSC #%d: IFR = 0x%02X IER = 0x%02X\n",
i >> 4,
@@ -81,7 +82,6 @@ void __init psc_init(void)
&& macintosh_config->ident != MAC_MODEL_Q840)
{
psc = NULL;
- psc_present = 0;
return;
}
@@ -91,7 +91,6 @@ void __init psc_init(void)
*/
psc = (void *) PSC_BASE;
- psc_present = 1;
printk("PSC detected at %p\n", psc);
diff --git a/arch/m68k/mm/motorola.c b/arch/m68k/mm/motorola.c
index b958916e5eac..8f37fdd80be9 100644
--- a/arch/m68k/mm/motorola.c
+++ b/arch/m68k/mm/motorola.c
@@ -250,7 +250,7 @@ void __init paging_init(void)
high_memory = phys_to_virt(max_addr);
min_low_pfn = availmem >> PAGE_SHIFT;
- max_low_pfn = max_addr >> PAGE_SHIFT;
+ max_pfn = max_low_pfn = max_addr >> PAGE_SHIFT;
for (i = 0; i < m68k_num_memory; i++) {
addr = m68k_memory[i].addr;
diff --git a/arch/m68k/sun3/config.c b/arch/m68k/sun3/config.c
index a8b942bf7163..71884bf01d72 100644
--- a/arch/m68k/sun3/config.c
+++ b/arch/m68k/sun3/config.c
@@ -118,13 +118,13 @@ static void __init sun3_bootmem_alloc(unsigned long memory_start,
memory_end = memory_end & PAGE_MASK;
start_page = __pa(memory_start) >> PAGE_SHIFT;
- num_pages = __pa(memory_end) >> PAGE_SHIFT;
+ max_pfn = num_pages = __pa(memory_end) >> PAGE_SHIFT;
high_memory = (void *)memory_end;
availmem = memory_start;
m68k_setup_node(0);
- availmem += init_bootmem_node(NODE_DATA(0), start_page, 0, num_pages);
+ availmem += init_bootmem(start_page, num_pages);
availmem = (availmem + (PAGE_SIZE-1)) & PAGE_MASK;
free_bootmem(__pa(availmem), memory_end - (availmem));
@@ -171,7 +171,7 @@ static void __init sun3_sched_init(irq_handler_t timer_routine)
intersil_clear();
}
-#ifdef CONFIG_SUN3_SCSI
+#if IS_ENABLED(CONFIG_SUN3_SCSI)
static const struct resource sun3_scsi_vme_rsrc[] __initconst = {
{
diff --git a/arch/metag/kernel/ftrace.c b/arch/metag/kernel/ftrace.c
index ed1d685157c2..ac8c039b0318 100644
--- a/arch/metag/kernel/ftrace.c
+++ b/arch/metag/kernel/ftrace.c
@@ -54,12 +54,11 @@ static int ftrace_modify_code(unsigned long pc, unsigned char *old_code,
unsigned char replaced[MCOUNT_INSN_SIZE];
/*
- * Note: Due to modules and __init, code can
- * disappear and change, we need to protect against faulting
- * as well as code changing.
- *
- * No real locking needed, this code is run through
- * kstop_machine.
+ * Note:
+ * We are paranoid about modifying text, as if a bug was to happen, it
+ * could cause us to read or write to someplace that could cause harm.
+ * Carefully read and modify the code with probe_kernel_*(), and make
+ * sure what we read is what we expected it to be before modifying it.
*/
/* read the text we want to modify */
diff --git a/arch/metag/kernel/module.c b/arch/metag/kernel/module.c
index 986331cd0a52..bb8dfba9a763 100644
--- a/arch/metag/kernel/module.c
+++ b/arch/metag/kernel/module.c
@@ -176,8 +176,8 @@ static uint32_t do_plt_call(void *location, Elf32_Addr val,
tramp[1] = 0xac000001 | ((val & 0x0000ffff) << 3);
/* Init, or core PLT? */
- if (location >= mod->module_core
- && location < mod->module_core + mod->core_size)
+ if (location >= mod->core_layout.base
+ && location < mod->core_layout.base + mod->core_layout.size)
entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr;
else
entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr;
diff --git a/arch/microblaze/kernel/dma.c b/arch/microblaze/kernel/dma.c
index c89da6312954..bf4dec229437 100644
--- a/arch/microblaze/kernel/dma.c
+++ b/arch/microblaze/kernel/dma.c
@@ -61,7 +61,8 @@ static int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl,
/* FIXME this part of code is untested */
for_each_sg(sgl, sg, nents, i) {
sg->dma_address = sg_phys(sg);
- __dma_sync(sg_phys(sg), sg->length, direction);
+ __dma_sync(page_to_phys(sg_page(sg)) + sg->offset,
+ sg->length, direction);
}
return nents;
diff --git a/arch/mips/alchemy/devboards/db1200.c b/arch/mips/alchemy/devboards/db1200.c
index 8c13675a12e7..992442a03d8b 100644
--- a/arch/mips/alchemy/devboards/db1200.c
+++ b/arch/mips/alchemy/devboards/db1200.c
@@ -200,7 +200,7 @@ static struct i2c_board_info db1200_i2c_devs[] __initdata = {
static void au1200_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
unsigned long ioaddr = (unsigned long)this->IO_ADDR_W;
ioaddr &= 0xffffff00;
diff --git a/arch/mips/alchemy/devboards/db1300.c b/arch/mips/alchemy/devboards/db1300.c
index b58077008a53..d3c087f59f1a 100644
--- a/arch/mips/alchemy/devboards/db1300.c
+++ b/arch/mips/alchemy/devboards/db1300.c
@@ -150,7 +150,7 @@ static void __init db1300_gpio_config(void)
static void au1300_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
unsigned long ioaddr = (unsigned long)this->IO_ADDR_W;
ioaddr &= 0xffffff00;
diff --git a/arch/mips/alchemy/devboards/db1550.c b/arch/mips/alchemy/devboards/db1550.c
index 5740bcfdfc7f..b518f029f5e7 100644
--- a/arch/mips/alchemy/devboards/db1550.c
+++ b/arch/mips/alchemy/devboards/db1550.c
@@ -128,7 +128,7 @@ static struct i2c_board_info db1550_i2c_devs[] __initdata = {
static void au1550_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
unsigned long ioaddr = (unsigned long)this->IO_ADDR_W;
ioaddr &= 0xffffff00;
diff --git a/arch/mips/bcm47xx/setup.c b/arch/mips/bcm47xx/setup.c
index 6d38948f0f1e..c807e32d6d81 100644
--- a/arch/mips/bcm47xx/setup.c
+++ b/arch/mips/bcm47xx/setup.c
@@ -101,50 +101,13 @@ static void bcm47xx_machine_halt(void)
}
#ifdef CONFIG_BCM47XX_SSB
-static int bcm47xx_get_invariants(struct ssb_bus *bus,
- struct ssb_init_invariants *iv)
-{
- char buf[20];
- int len, err;
-
- /* Fill boardinfo structure */
- memset(&iv->boardinfo, 0 , sizeof(struct ssb_boardinfo));
-
- len = bcm47xx_nvram_getenv("boardvendor", buf, sizeof(buf));
- if (len > 0) {
- err = kstrtou16(strim(buf), 0, &iv->boardinfo.vendor);
- if (err)
- pr_warn("Couldn't parse nvram board vendor entry with value \"%s\"\n",
- buf);
- }
- if (!iv->boardinfo.vendor)
- iv->boardinfo.vendor = SSB_BOARDVENDOR_BCM;
-
- len = bcm47xx_nvram_getenv("boardtype", buf, sizeof(buf));
- if (len > 0) {
- err = kstrtou16(strim(buf), 0, &iv->boardinfo.type);
- if (err)
- pr_warn("Couldn't parse nvram board type entry with value \"%s\"\n",
- buf);
- }
-
- memset(&iv->sprom, 0, sizeof(struct ssb_sprom));
- bcm47xx_fill_sprom(&iv->sprom, NULL, false);
-
- if (bcm47xx_nvram_getenv("cardbus", buf, sizeof(buf)) >= 0)
- iv->has_cardbus_slot = !!simple_strtoul(buf, NULL, 10);
-
- return 0;
-}
-
static void __init bcm47xx_register_ssb(void)
{
int err;
char buf[100];
struct ssb_mipscore *mcore;
- err = ssb_bus_ssbbus_register(&bcm47xx_bus.ssb, SSB_ENUM_BASE,
- bcm47xx_get_invariants);
+ err = ssb_bus_host_soc_register(&bcm47xx_bus.ssb, SSB_ENUM_BASE);
if (err)
panic("Failed to initialize SSB bus (err %d)", err);
diff --git a/arch/mips/boot/dts/brcm/bcm6328.dtsi b/arch/mips/boot/dts/brcm/bcm6328.dtsi
index 41891c1e58bd..d52ce3d07f16 100644
--- a/arch/mips/boot/dts/brcm/bcm6328.dtsi
+++ b/arch/mips/boot/dts/brcm/bcm6328.dtsi
@@ -73,7 +73,6 @@
timer: timer@10000040 {
compatible = "syscon";
reg = <0x10000040 0x2c>;
- little-endian;
};
reboot {
diff --git a/arch/mips/boot/dts/brcm/bcm7125.dtsi b/arch/mips/boot/dts/brcm/bcm7125.dtsi
index 1a7efa883c5e..4fc7ecee273c 100644
--- a/arch/mips/boot/dts/brcm/bcm7125.dtsi
+++ b/arch/mips/boot/dts/brcm/bcm7125.dtsi
@@ -98,7 +98,6 @@
sun_top_ctrl: syscon@404000 {
compatible = "brcm,bcm7125-sun-top-ctrl", "syscon";
reg = <0x404000 0x60c>;
- little-endian;
};
reboot {
diff --git a/arch/mips/boot/dts/brcm/bcm7346.dtsi b/arch/mips/boot/dts/brcm/bcm7346.dtsi
index d4bf52cfcf17..a3039bb53477 100644
--- a/arch/mips/boot/dts/brcm/bcm7346.dtsi
+++ b/arch/mips/boot/dts/brcm/bcm7346.dtsi
@@ -118,7 +118,6 @@
sun_top_ctrl: syscon@404000 {
compatible = "brcm,bcm7346-sun-top-ctrl", "syscon";
reg = <0x404000 0x51c>;
- little-endian;
};
reboot {
diff --git a/arch/mips/boot/dts/brcm/bcm7358.dtsi b/arch/mips/boot/dts/brcm/bcm7358.dtsi
index 8e2501694d03..4274ff41ec21 100644
--- a/arch/mips/boot/dts/brcm/bcm7358.dtsi
+++ b/arch/mips/boot/dts/brcm/bcm7358.dtsi
@@ -112,7 +112,6 @@
sun_top_ctrl: syscon@404000 {
compatible = "brcm,bcm7358-sun-top-ctrl", "syscon";
reg = <0x404000 0x51c>;
- little-endian;
};
reboot {
diff --git a/arch/mips/boot/dts/brcm/bcm7360.dtsi b/arch/mips/boot/dts/brcm/bcm7360.dtsi
index 7e5f76040fb8..0dcc9163c27b 100644
--- a/arch/mips/boot/dts/brcm/bcm7360.dtsi
+++ b/arch/mips/boot/dts/brcm/bcm7360.dtsi
@@ -112,7 +112,6 @@
sun_top_ctrl: syscon@404000 {
compatible = "brcm,bcm7360-sun-top-ctrl", "syscon";
reg = <0x404000 0x51c>;
- little-endian;
};
reboot {
diff --git a/arch/mips/boot/dts/brcm/bcm7362.dtsi b/arch/mips/boot/dts/brcm/bcm7362.dtsi
index c739ea77acb0..2f3f9fc2c478 100644
--- a/arch/mips/boot/dts/brcm/bcm7362.dtsi
+++ b/arch/mips/boot/dts/brcm/bcm7362.dtsi
@@ -118,7 +118,6 @@
sun_top_ctrl: syscon@404000 {
compatible = "brcm,bcm7362-sun-top-ctrl", "syscon";
reg = <0x404000 0x51c>;
- little-endian;
};
reboot {
diff --git a/arch/mips/boot/dts/brcm/bcm7420.dtsi b/arch/mips/boot/dts/brcm/bcm7420.dtsi
index 5f55d0a50a28..bee221b3b568 100644
--- a/arch/mips/boot/dts/brcm/bcm7420.dtsi
+++ b/arch/mips/boot/dts/brcm/bcm7420.dtsi
@@ -99,7 +99,6 @@
sun_top_ctrl: syscon@404000 {
compatible = "brcm,bcm7420-sun-top-ctrl", "syscon";
reg = <0x404000 0x60c>;
- little-endian;
};
reboot {
diff --git a/arch/mips/boot/dts/brcm/bcm7425.dtsi b/arch/mips/boot/dts/brcm/bcm7425.dtsi
index e24d41ab4e30..571f30f52e3f 100644
--- a/arch/mips/boot/dts/brcm/bcm7425.dtsi
+++ b/arch/mips/boot/dts/brcm/bcm7425.dtsi
@@ -100,7 +100,6 @@
sun_top_ctrl: syscon@404000 {
compatible = "brcm,bcm7425-sun-top-ctrl", "syscon";
reg = <0x404000 0x51c>;
- little-endian;
};
reboot {
diff --git a/arch/mips/boot/dts/brcm/bcm7435.dtsi b/arch/mips/boot/dts/brcm/bcm7435.dtsi
index 8b9432cc062b..614ee211f71a 100644
--- a/arch/mips/boot/dts/brcm/bcm7435.dtsi
+++ b/arch/mips/boot/dts/brcm/bcm7435.dtsi
@@ -114,7 +114,6 @@
sun_top_ctrl: syscon@404000 {
compatible = "brcm,bcm7425-sun-top-ctrl", "syscon";
reg = <0x404000 0x51c>;
- little-endian;
};
reboot {
diff --git a/arch/mips/include/asm/uaccess.h b/arch/mips/include/asm/uaccess.h
index 5305d694ffe5..095ecafe6bd3 100644
--- a/arch/mips/include/asm/uaccess.h
+++ b/arch/mips/include/asm/uaccess.h
@@ -599,7 +599,7 @@ extern void __put_user_unknown(void);
* On error, the variable @x is set to zero.
*/
#define __get_user_unaligned(x,ptr) \
- __get_user__unalignednocheck((x),(ptr),sizeof(*(ptr)))
+ __get_user_unaligned_nocheck((x),(ptr),sizeof(*(ptr)))
/*
* Yuck. We need two variants, one for 64bit operation and one
@@ -620,8 +620,8 @@ extern void __get_user_unaligned_unknown(void);
do { \
switch (size) { \
case 1: __get_data_asm(val, "lb", ptr); break; \
- case 2: __get_user_unaligned_asm(val, "ulh", ptr); break; \
- case 4: __get_user_unaligned_asm(val, "ulw", ptr); break; \
+ case 2: __get_data_unaligned_asm(val, "ulh", ptr); break; \
+ case 4: __get_data_unaligned_asm(val, "ulw", ptr); break; \
case 8: __GET_USER_UNALIGNED_DW(val, ptr); break; \
default: __get_user_unaligned_unknown(); break; \
} \
@@ -1122,9 +1122,15 @@ extern size_t __copy_in_user_eva(void *__to, const void *__from, size_t __n);
__cu_to = (to); \
__cu_from = (from); \
__cu_len = (n); \
- might_fault(); \
- __cu_len = __invoke_copy_from_user(__cu_to, __cu_from, \
- __cu_len); \
+ if (eva_kernel_access()) { \
+ __cu_len = __invoke_copy_from_kernel(__cu_to, \
+ __cu_from, \
+ __cu_len); \
+ } else { \
+ might_fault(); \
+ __cu_len = __invoke_copy_from_user(__cu_to, __cu_from, \
+ __cu_len); \
+ } \
__cu_len; \
})
@@ -1229,16 +1235,28 @@ __clear_user(void __user *addr, __kernel_size_t size)
{
__kernel_size_t res;
- might_fault();
- __asm__ __volatile__(
- "move\t$4, %1\n\t"
- "move\t$5, $0\n\t"
- "move\t$6, %2\n\t"
- __MODULE_JAL(__bzero)
- "move\t%0, $6"
- : "=r" (res)
- : "r" (addr), "r" (size)
- : "$4", "$5", "$6", __UA_t0, __UA_t1, "$31");
+ if (eva_kernel_access()) {
+ __asm__ __volatile__(
+ "move\t$4, %1\n\t"
+ "move\t$5, $0\n\t"
+ "move\t$6, %2\n\t"
+ __MODULE_JAL(__bzero_kernel)
+ "move\t%0, $6"
+ : "=r" (res)
+ : "r" (addr), "r" (size)
+ : "$4", "$5", "$6", __UA_t0, __UA_t1, "$31");
+ } else {
+ might_fault();
+ __asm__ __volatile__(
+ "move\t$4, %1\n\t"
+ "move\t$5, $0\n\t"
+ "move\t$6, %2\n\t"
+ __MODULE_JAL(__bzero)
+ "move\t%0, $6"
+ : "=r" (res)
+ : "r" (addr), "r" (size)
+ : "$4", "$5", "$6", __UA_t0, __UA_t1, "$31");
+ }
return res;
}
@@ -1384,7 +1402,7 @@ static inline long strlen_user(const char __user *s)
might_fault();
__asm__ __volatile__(
"move\t$4, %1\n\t"
- __MODULE_JAL(__strlen_kernel_asm)
+ __MODULE_JAL(__strlen_user_asm)
"move\t%0, $2"
: "=r" (res)
: "r" (s)
diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h
index dec3c850f36b..5910fe294e93 100644
--- a/arch/mips/include/uapi/asm/socket.h
+++ b/arch/mips/include/uapi/asm/socket.h
@@ -103,4 +103,7 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* _UAPI_ASM_SOCKET_H */
diff --git a/arch/mips/kernel/cps-vec.S b/arch/mips/kernel/cps-vec.S
index 8fd5a276cad2..ac81edd44563 100644
--- a/arch/mips/kernel/cps-vec.S
+++ b/arch/mips/kernel/cps-vec.S
@@ -257,7 +257,6 @@ LEAF(mips_cps_core_init)
has_mt t0, 3f
.set push
- .set mips64r2
.set mt
/* Only allow 1 TC per VPE to execute... */
@@ -376,7 +375,6 @@ LEAF(mips_cps_boot_vpes)
nop
.set push
- .set mips64r2
.set mt
1: /* Enter VPE configuration state */
diff --git a/arch/mips/kernel/mips_ksyms.c b/arch/mips/kernel/mips_ksyms.c
index 291af0b5c482..e2b6ab74643d 100644
--- a/arch/mips/kernel/mips_ksyms.c
+++ b/arch/mips/kernel/mips_ksyms.c
@@ -17,6 +17,7 @@
#include <asm/fpu.h>
#include <asm/msa.h>
+extern void *__bzero_kernel(void *__s, size_t __count);
extern void *__bzero(void *__s, size_t __count);
extern long __strncpy_from_kernel_nocheck_asm(char *__to,
const char *__from, long __len);
@@ -64,6 +65,7 @@ EXPORT_SYMBOL(__copy_from_user_eva);
EXPORT_SYMBOL(__copy_in_user_eva);
EXPORT_SYMBOL(__copy_to_user_eva);
EXPORT_SYMBOL(__copy_user_inatomic_eva);
+EXPORT_SYMBOL(__bzero_kernel);
#endif
EXPORT_SYMBOL(__bzero);
EXPORT_SYMBOL(__strncpy_from_kernel_nocheck_asm);
diff --git a/arch/mips/kernel/vpe.c b/arch/mips/kernel/vpe.c
index 9067b651c7a2..544ea21bfef9 100644
--- a/arch/mips/kernel/vpe.c
+++ b/arch/mips/kernel/vpe.c
@@ -205,11 +205,11 @@ static void layout_sections(struct module *mod, const Elf_Ehdr *hdr,
|| s->sh_entsize != ~0UL)
continue;
s->sh_entsize =
- get_offset((unsigned long *)&mod->core_size, s);
+ get_offset((unsigned long *)&mod->core_layout.size, s);
}
if (m == 0)
- mod->core_text_size = mod->core_size;
+ mod->core_layout.text_size = mod->core_layout.size;
}
}
@@ -641,7 +641,7 @@ static int vpe_elfload(struct vpe *v)
layout_sections(&mod, hdr, sechdrs, secstrings);
}
- v->load_addr = alloc_progmem(mod.core_size);
+ v->load_addr = alloc_progmem(mod.core_layout.size);
if (!v->load_addr)
return -ENOMEM;
diff --git a/arch/mips/kvm/emulate.c b/arch/mips/kvm/emulate.c
index d5fa3eaf39a1..41b1b090f56f 100644
--- a/arch/mips/kvm/emulate.c
+++ b/arch/mips/kvm/emulate.c
@@ -1581,7 +1581,7 @@ enum emulation_result kvm_mips_emulate_cache(uint32_t inst, uint32_t *opc,
base = (inst >> 21) & 0x1f;
op_inst = (inst >> 16) & 0x1f;
- offset = inst & 0xffff;
+ offset = (int16_t)inst;
cache = (inst >> 16) & 0x3;
op = (inst >> 18) & 0x7;
diff --git a/arch/mips/kvm/locore.S b/arch/mips/kvm/locore.S
index 7bab3a4e8f7d..7e2210846b8b 100644
--- a/arch/mips/kvm/locore.S
+++ b/arch/mips/kvm/locore.S
@@ -157,9 +157,11 @@ FEXPORT(__kvm_mips_vcpu_run)
FEXPORT(__kvm_mips_load_asid)
/* Set the ASID for the Guest Kernel */
- INT_SLL t0, t0, 1 /* with kseg0 @ 0x40000000, kernel */
- /* addresses shift to 0x80000000 */
- bltz t0, 1f /* If kernel */
+ PTR_L t0, VCPU_COP0(k1)
+ LONG_L t0, COP0_STATUS(t0)
+ andi t0, KSU_USER | ST0_ERL | ST0_EXL
+ xori t0, KSU_USER
+ bnez t0, 1f /* If kernel */
INT_ADDIU t1, k1, VCPU_GUEST_KERNEL_ASID /* (BD) */
INT_ADDIU t1, k1, VCPU_GUEST_USER_ASID /* else user */
1:
@@ -474,9 +476,11 @@ __kvm_mips_return_to_guest:
mtc0 t0, CP0_EPC
/* Set the ASID for the Guest Kernel */
- INT_SLL t0, t0, 1 /* with kseg0 @ 0x40000000, kernel */
- /* addresses shift to 0x80000000 */
- bltz t0, 1f /* If kernel */
+ PTR_L t0, VCPU_COP0(k1)
+ LONG_L t0, COP0_STATUS(t0)
+ andi t0, KSU_USER | ST0_ERL | ST0_EXL
+ xori t0, KSU_USER
+ bnez t0, 1f /* If kernel */
INT_ADDIU t1, k1, VCPU_GUEST_KERNEL_ASID /* (BD) */
INT_ADDIU t1, k1, VCPU_GUEST_USER_ASID /* else user */
1:
diff --git a/arch/mips/kvm/mips.c b/arch/mips/kvm/mips.c
index 49ff3bfc007e..b9b803facdbf 100644
--- a/arch/mips/kvm/mips.c
+++ b/arch/mips/kvm/mips.c
@@ -279,7 +279,7 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
if (!gebase) {
err = -ENOMEM;
- goto out_free_cpu;
+ goto out_uninit_cpu;
}
kvm_debug("Allocated %d bytes for KVM Exception Handlers @ %p\n",
ALIGN(size, PAGE_SIZE), gebase);
@@ -343,6 +343,9 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
out_free_gebase:
kfree(gebase);
+out_uninit_cpu:
+ kvm_vcpu_uninit(vcpu);
+
out_free_cpu:
kfree(vcpu);
diff --git a/arch/mips/lasat/picvue_proc.c b/arch/mips/lasat/picvue_proc.c
index 2bcd8391bc93..b42095880667 100644
--- a/arch/mips/lasat/picvue_proc.c
+++ b/arch/mips/lasat/picvue_proc.c
@@ -22,7 +22,6 @@
static DEFINE_MUTEX(pvc_mutex);
static char pvc_lines[PVC_NLINES][PVC_LINELEN+1];
static int pvc_linedata[PVC_NLINES];
-static struct proc_dir_entry *pvc_display_dir;
static char *pvc_linename[PVC_NLINES] = {"line1", "line2"};
#define DISPLAY_DIR_NAME "display"
static int scroll_dir, scroll_interval;
@@ -169,22 +168,17 @@ void pvc_proc_timerfunc(unsigned long data)
static void pvc_proc_cleanup(void)
{
- int i;
- for (i = 0; i < PVC_NLINES; i++)
- remove_proc_entry(pvc_linename[i], pvc_display_dir);
- remove_proc_entry("scroll", pvc_display_dir);
- remove_proc_entry(DISPLAY_DIR_NAME, NULL);
-
+ remove_proc_subtree(DISPLAY_DIR_NAME, NULL);
del_timer_sync(&timer);
}
static int __init pvc_proc_init(void)
{
- struct proc_dir_entry *proc_entry;
+ struct proc_dir_entry *dir, *proc_entry;
int i;
- pvc_display_dir = proc_mkdir(DISPLAY_DIR_NAME, NULL);
- if (pvc_display_dir == NULL)
+ dir = proc_mkdir(DISPLAY_DIR_NAME, NULL);
+ if (dir == NULL)
goto error;
for (i = 0; i < PVC_NLINES; i++) {
@@ -192,12 +186,12 @@ static int __init pvc_proc_init(void)
pvc_linedata[i] = i;
}
for (i = 0; i < PVC_NLINES; i++) {
- proc_entry = proc_create_data(pvc_linename[i], 0644, pvc_display_dir,
+ proc_entry = proc_create_data(pvc_linename[i], 0644, dir,
&pvc_line_proc_fops, &pvc_linedata[i]);
if (proc_entry == NULL)
goto error;
}
- proc_entry = proc_create("scroll", 0644, pvc_display_dir,
+ proc_entry = proc_create("scroll", 0644, dir,
&pvc_scroll_proc_fops);
if (proc_entry == NULL)
goto error;
diff --git a/arch/mips/lib/memset.S b/arch/mips/lib/memset.S
index b8e63fd00375..8f0019a2e5c8 100644
--- a/arch/mips/lib/memset.S
+++ b/arch/mips/lib/memset.S
@@ -283,6 +283,8 @@ LEAF(memset)
1:
#ifndef CONFIG_EVA
FEXPORT(__bzero)
+#else
+FEXPORT(__bzero_kernel)
#endif
__BUILD_BZERO LEGACY_MODE
diff --git a/arch/mips/mm/dma-default.c b/arch/mips/mm/dma-default.c
index d8117be729a2..730d394ce5f0 100644
--- a/arch/mips/mm/dma-default.c
+++ b/arch/mips/mm/dma-default.c
@@ -145,7 +145,7 @@ static void *mips_dma_alloc_coherent(struct device *dev, size_t size,
gfp = massage_gfp_flags(dev, gfp);
- if (IS_ENABLED(CONFIG_DMA_CMA) && !(gfp & GFP_ATOMIC))
+ if (IS_ENABLED(CONFIG_DMA_CMA) && gfpflags_allow_blocking(gfp))
page = dma_alloc_from_contiguous(dev,
count, get_order(size));
if (!page)
diff --git a/arch/mips/net/bpf_jit.c b/arch/mips/net/bpf_jit.c
index 77cb27309db2..1a8c96035716 100644
--- a/arch/mips/net/bpf_jit.c
+++ b/arch/mips/net/bpf_jit.c
@@ -521,19 +521,6 @@ static inline u16 align_sp(unsigned int num)
return num;
}
-static bool is_load_to_a(u16 inst)
-{
- switch (inst) {
- case BPF_LD | BPF_W | BPF_LEN:
- case BPF_LD | BPF_W | BPF_ABS:
- case BPF_LD | BPF_H | BPF_ABS:
- case BPF_LD | BPF_B | BPF_ABS:
- return true;
- default:
- return false;
- }
-}
-
static void save_bpf_jit_regs(struct jit_ctx *ctx, unsigned offset)
{
int i = 0, real_off = 0;
@@ -614,7 +601,6 @@ static unsigned int get_stack_depth(struct jit_ctx *ctx)
static void build_prologue(struct jit_ctx *ctx)
{
- u16 first_inst = ctx->skf->insns[0].code;
int sp_off;
/* Calculate the total offset for the stack pointer */
@@ -641,7 +627,7 @@ static void build_prologue(struct jit_ctx *ctx)
emit_jit_reg_move(r_X, r_zero, ctx);
/* Do not leak kernel data to userspace */
- if ((first_inst != (BPF_RET | BPF_K)) && !(is_load_to_a(first_inst)))
+ if (bpf_needs_clear_a(&ctx->skf->insns[0]))
emit_jit_reg_move(r_A, r_zero, ctx);
}
diff --git a/arch/mips/pci/pci-rt2880.c b/arch/mips/pci/pci-rt2880.c
index 8a978022630b..a245cad4372a 100644
--- a/arch/mips/pci/pci-rt2880.c
+++ b/arch/mips/pci/pci-rt2880.c
@@ -11,6 +11,7 @@
* by the Free Software Foundation.
*/
+#include <linux/delay.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/io.h>
@@ -220,7 +221,6 @@ int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
static int rt288x_pci_probe(struct platform_device *pdev)
{
void __iomem *io_map_base;
- int i;
rt2880_pci_base = ioremap_nocache(RT2880_PCI_BASE, PAGE_SIZE);
@@ -232,8 +232,7 @@ static int rt288x_pci_probe(struct platform_device *pdev)
ioport_resource.end = RT2880_PCI_IO_BASE + RT2880_PCI_IO_SIZE - 1;
rt2880_pci_reg_write(0, RT2880_PCI_REG_PCICFG_ADDR);
- for (i = 0; i < 0xfffff; i++)
- ;
+ udelay(1);
rt2880_pci_reg_write(0x79, RT2880_PCI_REG_ARBCTL);
rt2880_pci_reg_write(0x07FF0001, RT2880_PCI_REG_BAR0SETUP_ADDR);
diff --git a/arch/mips/pmcs-msp71xx/msp_setup.c b/arch/mips/pmcs-msp71xx/msp_setup.c
index 4f925e06c414..9d293b3e9130 100644
--- a/arch/mips/pmcs-msp71xx/msp_setup.c
+++ b/arch/mips/pmcs-msp71xx/msp_setup.c
@@ -10,6 +10,8 @@
* option) any later version.
*/
+#include <linux/delay.h>
+
#include <asm/bootinfo.h>
#include <asm/cacheflush.h>
#include <asm/idle.h>
@@ -37,7 +39,6 @@ extern void msp_serial_setup(void);
void msp7120_reset(void)
{
void *start, *end, *iptr;
- register int i;
/* Diasble all interrupts */
local_irq_disable();
@@ -77,7 +78,7 @@ void msp7120_reset(void)
*/
/* Wait a bit for the DDRC to settle */
- for (i = 0; i < 100000000; i++);
+ mdelay(125);
#if defined(CONFIG_PMC_MSP7120_GW)
/*
diff --git a/arch/mips/pnx833x/common/platform.c b/arch/mips/pnx833x/common/platform.c
index b4b774bc3178..3cd357737a26 100644
--- a/arch/mips/pnx833x/common/platform.c
+++ b/arch/mips/pnx833x/common/platform.c
@@ -180,7 +180,7 @@ static struct platform_device pnx833x_sata_device = {
static void
pnx833x_flash_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
unsigned long nandaddr = (unsigned long)this->IO_ADDR_W;
if (cmd == NAND_CMD_NONE)
diff --git a/arch/mips/rb532/devices.c b/arch/mips/rb532/devices.c
index 9bd7a2de0765..0966adccf520 100644
--- a/arch/mips/rb532/devices.c
+++ b/arch/mips/rb532/devices.c
@@ -148,7 +148,7 @@ static int rb532_dev_ready(struct mtd_info *mtd)
static void rb532_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
unsigned char orbits, nandbits;
if (ctrl & NAND_CTRL_CHANGE) {
diff --git a/arch/mips/sni/reset.c b/arch/mips/sni/reset.c
index 244f9427625b..6afa34346b81 100644
--- a/arch/mips/sni/reset.c
+++ b/arch/mips/sni/reset.c
@@ -3,6 +3,8 @@
*
* Reset a SNI machine.
*/
+#include <linux/delay.h>
+
#include <asm/io.h>
#include <asm/reboot.h>
#include <asm/sni.h>
@@ -24,7 +26,7 @@ static inline void kb_wait(void)
/* XXX This ends up at the ARC firmware prompt ... */
void sni_machine_restart(char *command)
{
- int i, j;
+ int i;
/* This does a normal via the keyboard controller like a PC.
We can do that easier ... */
@@ -32,9 +34,9 @@ void sni_machine_restart(char *command)
for (;;) {
for (i = 0; i < 100; i++) {
kb_wait();
- for (j = 0; j < 100000 ; j++)
- /* nothing */;
+ udelay(50);
outb_p(0xfe, 0x64); /* pulse reset low */
+ udelay(50);
}
}
}
diff --git a/arch/mips/vdso/Makefile b/arch/mips/vdso/Makefile
index ef5f348f386a..ee3617c0c5e2 100644
--- a/arch/mips/vdso/Makefile
+++ b/arch/mips/vdso/Makefile
@@ -26,8 +26,8 @@ aflags-vdso := $(ccflags-vdso) \
# the comments on that file.
#
ifndef CONFIG_CPU_MIPSR6
- ifeq ($(call ld-ifversion, -gt, 22400000, y),)
- $(warning MIPS VDSO requires binutils > 2.24)
+ ifeq ($(call ld-ifversion, -lt, 225000000, y),y)
+ $(warning MIPS VDSO requires binutils >= 2.25)
obj-vdso-y := $(filter-out gettimeofday.o, $(obj-vdso-y))
ccflags-vdso += -DDISABLE_MIPS_VDSO
endif
diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig
index 4434b54e1d87..78ae5552fdb8 100644
--- a/arch/mn10300/Kconfig
+++ b/arch/mn10300/Kconfig
@@ -1,6 +1,7 @@
config MN10300
def_bool y
select HAVE_OPROFILE
+ select HAVE_UID16
select GENERIC_IRQ_SHOW
select ARCH_WANT_IPC_PARSE_VERSION
select HAVE_ARCH_TRACEHOOK
@@ -37,9 +38,6 @@ config HIGHMEM
config NUMA
def_bool n
-config UID16
- def_bool y
-
config RWSEM_GENERIC_SPINLOCK
def_bool y
diff --git a/arch/mn10300/include/asm/uaccess.h b/arch/mn10300/include/asm/uaccess.h
index 537278746a15..20f7bf6de384 100644
--- a/arch/mn10300/include/asm/uaccess.h
+++ b/arch/mn10300/include/asm/uaccess.h
@@ -110,21 +110,6 @@ extern int fixup_exception(struct pt_regs *regs);
#define __put_user(x, ptr) __put_user_nocheck((x), (ptr), sizeof(*(ptr)))
#define __get_user(x, ptr) __get_user_nocheck((x), (ptr), sizeof(*(ptr)))
-/*
- * The "xxx_ret" versions return constant specified in third argument, if
- * something bad happens. These macros can be optimized for the
- * case of just returning from the function xxx_ret is used.
- */
-
-#define put_user_ret(x, ptr, ret) \
- ({ if (put_user((x), (ptr))) return (ret); })
-#define get_user_ret(x, ptr, ret) \
- ({ if (get_user((x), (ptr))) return (ret); })
-#define __put_user_ret(x, ptr, ret) \
- ({ if (__put_user((x), (ptr))) return (ret); })
-#define __get_user_ret(x, ptr, ret) \
- ({ if (__get_user((x), (ptr))) return (ret); })
-
struct __large_struct { unsigned long buf[100]; };
#define __m(x) (*(struct __large_struct *)(x))
diff --git a/arch/mn10300/include/uapi/asm/socket.h b/arch/mn10300/include/uapi/asm/socket.h
index cab7d6d50051..58b1aa01ab9f 100644
--- a/arch/mn10300/include/uapi/asm/socket.h
+++ b/arch/mn10300/include/uapi/asm/socket.h
@@ -85,4 +85,7 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* _ASM_SOCKET_H */
diff --git a/arch/nios2/mm/cacheflush.c b/arch/nios2/mm/cacheflush.c
index 223cdcc8203f..87bf88ed04c6 100644
--- a/arch/nios2/mm/cacheflush.c
+++ b/arch/nios2/mm/cacheflush.c
@@ -23,22 +23,6 @@ static void __flush_dcache(unsigned long start, unsigned long end)
end += (cpuinfo.dcache_line_size - 1);
end &= ~(cpuinfo.dcache_line_size - 1);
- for (addr = start; addr < end; addr += cpuinfo.dcache_line_size) {
- __asm__ __volatile__ (" flushda 0(%0)\n"
- : /* Outputs */
- : /* Inputs */ "r"(addr)
- /* : No clobber */);
- }
-}
-
-static void __flush_dcache_all(unsigned long start, unsigned long end)
-{
- unsigned long addr;
-
- start &= ~(cpuinfo.dcache_line_size - 1);
- end += (cpuinfo.dcache_line_size - 1);
- end &= ~(cpuinfo.dcache_line_size - 1);
-
if (end > start + cpuinfo.dcache_size)
end = start + cpuinfo.dcache_size;
@@ -112,7 +96,7 @@ static void flush_aliases(struct address_space *mapping, struct page *page)
void flush_cache_all(void)
{
- __flush_dcache_all(0, cpuinfo.dcache_size);
+ __flush_dcache(0, cpuinfo.dcache_size);
__flush_icache(0, cpuinfo.icache_size);
}
@@ -182,7 +166,7 @@ void __flush_dcache_page(struct address_space *mapping, struct page *page)
*/
unsigned long start = (unsigned long)page_address(page);
- __flush_dcache_all(start, start + PAGE_SIZE);
+ __flush_dcache(start, start + PAGE_SIZE);
}
void flush_dcache_page(struct page *page)
@@ -268,7 +252,7 @@ void copy_from_user_page(struct vm_area_struct *vma, struct page *page,
{
flush_cache_page(vma, user_vaddr, page_to_pfn(page));
memcpy(dst, src, len);
- __flush_dcache_all((unsigned long)src, (unsigned long)src + len);
+ __flush_dcache((unsigned long)src, (unsigned long)src + len);
if (vma->vm_flags & VM_EXEC)
__flush_icache((unsigned long)src, (unsigned long)src + len);
}
@@ -279,7 +263,7 @@ void copy_to_user_page(struct vm_area_struct *vma, struct page *page,
{
flush_cache_page(vma, user_vaddr, page_to_pfn(page));
memcpy(dst, src, len);
- __flush_dcache_all((unsigned long)dst, (unsigned long)dst + len);
+ __flush_dcache((unsigned long)dst, (unsigned long)dst + len);
if (vma->vm_flags & VM_EXEC)
__flush_icache((unsigned long)dst, (unsigned long)dst + len);
}
diff --git a/arch/parisc/include/asm/pgtable.h b/arch/parisc/include/asm/pgtable.h
index d8534f95915a..291cee28ccb6 100644
--- a/arch/parisc/include/asm/pgtable.h
+++ b/arch/parisc/include/asm/pgtable.h
@@ -372,7 +372,8 @@ static inline pte_t pte_mkspecial(pte_t pte) { return pte; }
*/
#ifdef CONFIG_HUGETLB_PAGE
#define pte_huge(pte) (pte_val(pte) & _PAGE_HUGE)
-#define pte_mkhuge(pte) (__pte(pte_val(pte) | _PAGE_HUGE))
+#define pte_mkhuge(pte) (__pte(pte_val(pte) | \
+ (parisc_requires_coherency() ? 0 : _PAGE_HUGE)))
#else
#define pte_huge(pte) (0)
#define pte_mkhuge(pte) (pte)
diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h
index a5cd40cd8ee1..f9cf1223422c 100644
--- a/arch/parisc/include/uapi/asm/socket.h
+++ b/arch/parisc/include/uapi/asm/socket.h
@@ -84,4 +84,7 @@
#define SO_ATTACH_BPF 0x402B
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 0x402C
+#define SO_ATTACH_REUSEPORT_EBPF 0x402D
+
#endif /* _UAPI_ASM_SOCKET_H */
diff --git a/arch/parisc/include/uapi/asm/unistd.h b/arch/parisc/include/uapi/asm/unistd.h
index 33170384d3ac..35bdccbb2036 100644
--- a/arch/parisc/include/uapi/asm/unistd.h
+++ b/arch/parisc/include/uapi/asm/unistd.h
@@ -360,8 +360,9 @@
#define __NR_execveat (__NR_Linux + 342)
#define __NR_membarrier (__NR_Linux + 343)
#define __NR_userfaultfd (__NR_Linux + 344)
+#define __NR_mlock2 (__NR_Linux + 345)
-#define __NR_Linux_syscalls (__NR_userfaultfd + 1)
+#define __NR_Linux_syscalls (__NR_mlock2 + 1)
#define __IGNORE_select /* newselect */
diff --git a/arch/parisc/kernel/module.c b/arch/parisc/kernel/module.c
index 3c63a820fcda..b9d75d9fa9ac 100644
--- a/arch/parisc/kernel/module.c
+++ b/arch/parisc/kernel/module.c
@@ -42,9 +42,9 @@
* We are not doing SEGREL32 handling correctly. According to the ABI, we
* should do a value offset, like this:
* if (in_init(me, (void *)val))
- * val -= (uint32_t)me->module_init;
+ * val -= (uint32_t)me->init_layout.base;
* else
- * val -= (uint32_t)me->module_core;
+ * val -= (uint32_t)me->core_layout.base;
* However, SEGREL32 is used only for PARISC unwind entries, and we want
* those entries to have an absolute address, and not just an offset.
*
@@ -100,14 +100,14 @@
* or init pieces the location is */
static inline int in_init(struct module *me, void *loc)
{
- return (loc >= me->module_init &&
- loc <= (me->module_init + me->init_size));
+ return (loc >= me->init_layout.base &&
+ loc <= (me->init_layout.base + me->init_layout.size));
}
static inline int in_core(struct module *me, void *loc)
{
- return (loc >= me->module_core &&
- loc <= (me->module_core + me->core_size));
+ return (loc >= me->core_layout.base &&
+ loc <= (me->core_layout.base + me->core_layout.size));
}
static inline int in_local(struct module *me, void *loc)
@@ -367,13 +367,13 @@ int module_frob_arch_sections(CONST Elf_Ehdr *hdr,
}
/* align things a bit */
- me->core_size = ALIGN(me->core_size, 16);
- me->arch.got_offset = me->core_size;
- me->core_size += gots * sizeof(struct got_entry);
+ me->core_layout.size = ALIGN(me->core_layout.size, 16);
+ me->arch.got_offset = me->core_layout.size;
+ me->core_layout.size += gots * sizeof(struct got_entry);
- me->core_size = ALIGN(me->core_size, 16);
- me->arch.fdesc_offset = me->core_size;
- me->core_size += fdescs * sizeof(Elf_Fdesc);
+ me->core_layout.size = ALIGN(me->core_layout.size, 16);
+ me->arch.fdesc_offset = me->core_layout.size;
+ me->core_layout.size += fdescs * sizeof(Elf_Fdesc);
me->arch.got_max = gots;
me->arch.fdesc_max = fdescs;
@@ -391,7 +391,7 @@ static Elf64_Word get_got(struct module *me, unsigned long value, long addend)
BUG_ON(value == 0);
- got = me->module_core + me->arch.got_offset;
+ got = me->core_layout.base + me->arch.got_offset;
for (i = 0; got[i].addr; i++)
if (got[i].addr == value)
goto out;
@@ -409,7 +409,7 @@ static Elf64_Word get_got(struct module *me, unsigned long value, long addend)
#ifdef CONFIG_64BIT
static Elf_Addr get_fdesc(struct module *me, unsigned long value)
{
- Elf_Fdesc *fdesc = me->module_core + me->arch.fdesc_offset;
+ Elf_Fdesc *fdesc = me->core_layout.base + me->arch.fdesc_offset;
if (!value) {
printk(KERN_ERR "%s: zero OPD requested!\n", me->name);
@@ -427,7 +427,7 @@ static Elf_Addr get_fdesc(struct module *me, unsigned long value)
/* Create new one */
fdesc->addr = value;
- fdesc->gp = (Elf_Addr)me->module_core + me->arch.got_offset;
+ fdesc->gp = (Elf_Addr)me->core_layout.base + me->arch.got_offset;
return (Elf_Addr)fdesc;
}
#endif /* CONFIG_64BIT */
@@ -839,7 +839,7 @@ register_unwind_table(struct module *me,
table = (unsigned char *)sechdrs[me->arch.unwind_section].sh_addr;
end = table + sechdrs[me->arch.unwind_section].sh_size;
- gp = (Elf_Addr)me->module_core + me->arch.got_offset;
+ gp = (Elf_Addr)me->core_layout.base + me->arch.got_offset;
DEBUGP("register_unwind_table(), sect = %d at 0x%p - 0x%p (gp=0x%lx)\n",
me->arch.unwind_section, table, end, gp);
diff --git a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c
index 64f2764a8cef..c99f3dde455c 100644
--- a/arch/parisc/kernel/pci.c
+++ b/arch/parisc/kernel/pci.c
@@ -171,24 +171,6 @@ void pcibios_set_master(struct pci_dev *dev)
}
-void __init pcibios_init_bus(struct pci_bus *bus)
-{
- struct pci_dev *dev = bus->self;
- unsigned short bridge_ctl;
-
- /* We deal only with pci controllers and pci-pci bridges. */
- if (!dev || (dev->class >> 8) != PCI_CLASS_BRIDGE_PCI)
- return;
-
- /* PCI-PCI bridge - set the cache line and default latency
- (32) for primary and secondary buses. */
- pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, 32);
-
- pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &bridge_ctl);
- bridge_ctl |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR;
- pci_write_config_word(dev, PCI_BRIDGE_CONTROL, bridge_ctl);
-}
-
/*
* pcibios align resources() is called every time generic PCI code
* wants to generate a new address. The process of looking for
diff --git a/arch/parisc/kernel/signal.c b/arch/parisc/kernel/signal.c
index dc1ea796fd60..2264f68f3c2f 100644
--- a/arch/parisc/kernel/signal.c
+++ b/arch/parisc/kernel/signal.c
@@ -435,6 +435,55 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs, int in_syscall)
regs->gr[28]);
}
+/*
+ * Check how the syscall number gets loaded into %r20 within
+ * the delay branch in userspace and adjust as needed.
+ */
+
+static void check_syscallno_in_delay_branch(struct pt_regs *regs)
+{
+ u32 opcode, source_reg;
+ u32 __user *uaddr;
+ int err;
+
+ /* Usually we don't have to restore %r20 (the system call number)
+ * because it gets loaded in the delay slot of the branch external
+ * instruction via the ldi instruction.
+ * In some cases a register-to-register copy instruction might have
+ * been used instead, in which case we need to copy the syscall
+ * number into the source register before returning to userspace.
+ */
+
+ /* A syscall is just a branch, so all we have to do is fiddle the
+ * return pointer so that the ble instruction gets executed again.
+ */
+ regs->gr[31] -= 8; /* delayed branching */
+
+ /* Get assembler opcode of code in delay branch */
+ uaddr = (unsigned int *) ((regs->gr[31] & ~3) + 4);
+ err = get_user(opcode, uaddr);
+ if (err)
+ return;
+
+ /* Check if delay branch uses "ldi int,%r20" */
+ if ((opcode & 0xffff0000) == 0x34140000)
+ return; /* everything ok, just return */
+
+ /* Check if delay branch uses "nop" */
+ if (opcode == INSN_NOP)
+ return;
+
+ /* Check if delay branch uses "copy %rX,%r20" */
+ if ((opcode & 0xffe0ffff) == 0x08000254) {
+ source_reg = (opcode >> 16) & 31;
+ regs->gr[source_reg] = regs->gr[20];
+ return;
+ }
+
+ pr_warn("syscall restart: %s (pid %d): unexpected opcode 0x%08x\n",
+ current->comm, task_pid_nr(current), opcode);
+}
+
static inline void
syscall_restart(struct pt_regs *regs, struct k_sigaction *ka)
{
@@ -457,10 +506,7 @@ syscall_restart(struct pt_regs *regs, struct k_sigaction *ka)
}
/* fallthrough */
case -ERESTARTNOINTR:
- /* A syscall is just a branch, so all
- * we have to do is fiddle the return pointer.
- */
- regs->gr[31] -= 8; /* delayed branching */
+ check_syscallno_in_delay_branch(regs);
break;
}
}
@@ -510,15 +556,9 @@ insert_restart_trampoline(struct pt_regs *regs)
}
case -ERESTARTNOHAND:
case -ERESTARTSYS:
- case -ERESTARTNOINTR: {
- /* Hooray for delayed branching. We don't
- * have to restore %r20 (the system call
- * number) because it gets loaded in the delay
- * slot of the branch external instruction.
- */
- regs->gr[31] -= 8;
+ case -ERESTARTNOINTR:
+ check_syscallno_in_delay_branch(regs);
return;
- }
default:
break;
}
diff --git a/arch/parisc/kernel/syscall_table.S b/arch/parisc/kernel/syscall_table.S
index 78c3ef8c348d..d4ffcfbc9885 100644
--- a/arch/parisc/kernel/syscall_table.S
+++ b/arch/parisc/kernel/syscall_table.S
@@ -440,6 +440,7 @@
ENTRY_COMP(execveat)
ENTRY_SAME(membarrier)
ENTRY_SAME(userfaultfd)
+ ENTRY_SAME(mlock2) /* 345 */
.ifne (. - 90b) - (__NR_Linux_syscalls * (91b - 90b))
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 405ce42c8ff7..7d5a8350f913 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -159,6 +159,7 @@ config PPC
select EDAC_SUPPORT
select EDAC_ATOMIC_SCRUB
select ARCH_HAS_DMA_SET_COHERENT_MASK
+ select ARCH_HAS_DEVMEM_IS_ALLOWED
select HAVE_ARCH_SECCOMP_FILTER
config GENERIC_CSUM
diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug
index 77e2cefe47eb..638f9ce740f5 100644
--- a/arch/powerpc/Kconfig.debug
+++ b/arch/powerpc/Kconfig.debug
@@ -335,18 +335,6 @@ config PPC_EARLY_DEBUG_CPM_ADDR
platform probing is done, all platforms selected must
share the same address.
-config STRICT_DEVMEM
- def_bool y
- prompt "Filter access to /dev/mem"
- help
- This option restricts access to /dev/mem. If this option is
- disabled, you allow userspace access to all memory, including
- kernel and userspace memory. Accidental memory access is likely
- to be disastrous.
- Memory access is required for experts who want to debug the kernel.
-
- If you are unsure, say Y.
-
config FAIL_IOMMU
bool "Fault-injection capability for IOMMU"
depends on FAULT_INJECTION
diff --git a/arch/powerpc/boot/dts/sbc8641d.dts b/arch/powerpc/boot/dts/sbc8641d.dts
index 631ede72e226..68f0ed7626bd 100644
--- a/arch/powerpc/boot/dts/sbc8641d.dts
+++ b/arch/powerpc/boot/dts/sbc8641d.dts
@@ -227,23 +227,15 @@
reg = <0x520 0x20>;
phy0: ethernet-phy@1f {
- interrupt-parent = <&mpic>;
- interrupts = <10 1>;
reg = <0x1f>;
};
phy1: ethernet-phy@0 {
- interrupt-parent = <&mpic>;
- interrupts = <10 1>;
reg = <0>;
};
phy2: ethernet-phy@1 {
- interrupt-parent = <&mpic>;
- interrupts = <10 1>;
reg = <1>;
};
phy3: ethernet-phy@2 {
- interrupt-parent = <&mpic>;
- interrupts = <10 1>;
reg = <2>;
};
tbi0: tbi-phy@11 {
diff --git a/arch/powerpc/include/asm/barrier.h b/arch/powerpc/include/asm/barrier.h
index 0eca6efc0631..a7af5fb7b914 100644
--- a/arch/powerpc/include/asm/barrier.h
+++ b/arch/powerpc/include/asm/barrier.h
@@ -34,7 +34,7 @@
#define rmb() __asm__ __volatile__ ("sync" : : : "memory")
#define wmb() __asm__ __volatile__ ("sync" : : : "memory")
-#define smp_store_mb(var, value) do { WRITE_ONCE(var, value); mb(); } while (0)
+#define smp_store_mb(var, value) do { WRITE_ONCE(var, value); smp_mb(); } while (0)
#ifdef __SUBARCH_HAS_LWSYNC
# define SMPWMB LWSYNC
diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h
index 85bc8c0d257b..e3b54dd4f730 100644
--- a/arch/powerpc/include/asm/hvcall.h
+++ b/arch/powerpc/include/asm/hvcall.h
@@ -258,11 +258,16 @@
#define H_DEL_CONN 0x288
#define H_JOIN 0x298
#define H_VASI_STATE 0x2A4
+#define H_VIOCTL 0x2A8
#define H_ENABLE_CRQ 0x2B0
#define H_GET_EM_PARMS 0x2B8
#define H_SET_MPP 0x2D0
#define H_GET_MPP 0x2D4
+#define H_REG_SUB_CRQ 0x2DC
#define H_HOME_NODE_ASSOCIATIVITY 0x2EC
+#define H_FREE_SUB_CRQ 0x2E0
+#define H_SEND_SUB_CRQ 0x2E4
+#define H_SEND_SUB_CRQ_INDIRECT 0x2E8
#define H_BEST_ENERGY 0x2F4
#define H_XIRR_X 0x2FC
#define H_RANDOM 0x300
@@ -271,6 +276,21 @@
#define H_SET_MODE 0x31C
#define MAX_HCALL_OPCODE H_SET_MODE
+/* H_VIOCTL functions */
+#define H_GET_VIOA_DUMP_SIZE 0x01
+#define H_GET_VIOA_DUMP 0x02
+#define H_GET_ILLAN_NUM_VLAN_IDS 0x03
+#define H_GET_ILLAN_VLAN_ID_LIST 0x04
+#define H_GET_ILLAN_SWITCH_ID 0x05
+#define H_DISABLE_MIGRATION 0x06
+#define H_ENABLE_MIGRATION 0x07
+#define H_GET_PARTNER_INFO 0x08
+#define H_GET_PARTNER_WWPN_LIST 0x09
+#define H_DISABLE_ALL_VIO_INTS 0x0A
+#define H_DISABLE_VIO_INTERRUPT 0x0B
+#define H_ENABLE_VIO_INTERRUPT 0x0C
+
+
/* Platform specific hcalls, used by KVM */
#define H_RTAS 0xf000
diff --git a/arch/powerpc/include/asm/icswx.h b/arch/powerpc/include/asm/icswx.h
index 9f8402b35115..27e588f6c72e 100644
--- a/arch/powerpc/include/asm/icswx.h
+++ b/arch/powerpc/include/asm/icswx.h
@@ -164,6 +164,7 @@ struct coprocessor_request_block {
#define ICSWX_INITIATED (0x8)
#define ICSWX_BUSY (0x4)
#define ICSWX_REJECTED (0x2)
+#define ICSWX_XERS0 (0x1) /* undefined or set from XERSO. */
static inline int icswx(__be32 ccw, struct coprocessor_request_block *crb)
{
diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
index cfa758c6b4f6..271fefbbe521 100644
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -50,6 +50,10 @@
#define KVM_NR_IRQCHIPS 1
#define KVM_IRQCHIP_NUM_PINS 256
+/* PPC-specific vcpu->requests bit members */
+#define KVM_REQ_WATCHDOG 8
+#define KVM_REQ_EPR_EXIT 9
+
#include <linux/mmu_notifier.h>
#define KVM_ARCH_WANT_MMU_NOTIFIER
diff --git a/arch/powerpc/include/asm/systbl.h b/arch/powerpc/include/asm/systbl.h
index f2b0b1b0c72a..5654ece02c0d 100644
--- a/arch/powerpc/include/asm/systbl.h
+++ b/arch/powerpc/include/asm/systbl.h
@@ -370,16 +370,16 @@ COMPAT_SYS(execveat)
PPC64ONLY(switch_endian)
SYSCALL_SPU(userfaultfd)
SYSCALL_SPU(membarrier)
-SYSCALL(semop)
-SYSCALL(semget)
-COMPAT_SYS(semctl)
-COMPAT_SYS(semtimedop)
-COMPAT_SYS(msgsnd)
-COMPAT_SYS(msgrcv)
-SYSCALL(msgget)
-COMPAT_SYS(msgctl)
-COMPAT_SYS(shmat)
-SYSCALL(shmdt)
-SYSCALL(shmget)
-COMPAT_SYS(shmctl)
+SYSCALL(ni_syscall)
+SYSCALL(ni_syscall)
+SYSCALL(ni_syscall)
+SYSCALL(ni_syscall)
+SYSCALL(ni_syscall)
+SYSCALL(ni_syscall)
+SYSCALL(ni_syscall)
+SYSCALL(ni_syscall)
+SYSCALL(ni_syscall)
+SYSCALL(ni_syscall)
+SYSCALL(ni_syscall)
+SYSCALL(ni_syscall)
SYSCALL(mlock2)
diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h
index 2a8ebae0936b..b7c20f0b8fbe 100644
--- a/arch/powerpc/include/asm/uaccess.h
+++ b/arch/powerpc/include/asm/uaccess.h
@@ -274,21 +274,6 @@ do { \
__gu_err; \
})
-#ifndef __powerpc64__
-#define __get_user64_nocheck(x, ptr, size) \
-({ \
- long __gu_err; \
- long long __gu_val; \
- __typeof__(*(ptr)) __user *__gu_addr = (ptr); \
- __chk_user_ptr(ptr); \
- if (!is_kernel_addr((unsigned long)__gu_addr)) \
- might_fault(); \
- __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \
- (x) = (__force __typeof__(*(ptr)))__gu_val; \
- __gu_err; \
-})
-#endif /* __powerpc64__ */
-
#define __get_user_check(x, ptr, size) \
({ \
long __gu_err = -EFAULT; \
diff --git a/arch/powerpc/include/uapi/asm/socket.h b/arch/powerpc/include/uapi/asm/socket.h
index c046666038f8..dd54f28ecdec 100644
--- a/arch/powerpc/include/uapi/asm/socket.h
+++ b/arch/powerpc/include/uapi/asm/socket.h
@@ -92,4 +92,7 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* _ASM_POWERPC_SOCKET_H */
diff --git a/arch/powerpc/include/uapi/asm/unistd.h b/arch/powerpc/include/uapi/asm/unistd.h
index 1effea5193d6..12a05652377a 100644
--- a/arch/powerpc/include/uapi/asm/unistd.h
+++ b/arch/powerpc/include/uapi/asm/unistd.h
@@ -388,18 +388,6 @@
#define __NR_switch_endian 363
#define __NR_userfaultfd 364
#define __NR_membarrier 365
-#define __NR_semop 366
-#define __NR_semget 367
-#define __NR_semctl 368
-#define __NR_semtimedop 369
-#define __NR_msgsnd 370
-#define __NR_msgrcv 371
-#define __NR_msgget 372
-#define __NR_msgctl 373
-#define __NR_shmat 374
-#define __NR_shmdt 375
-#define __NR_shmget 376
-#define __NR_shmctl 377
#define __NR_mlock2 378
#endif /* _UAPI_ASM_POWERPC_UNISTD_H_ */
diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
index 80dfe8965df9..8d14feb40f12 100644
--- a/arch/powerpc/kernel/eeh_driver.c
+++ b/arch/powerpc/kernel/eeh_driver.c
@@ -590,16 +590,10 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus)
eeh_ops->configure_bridge(pe);
eeh_pe_restore_bars(pe);
- /*
- * If it's PHB PE, the frozen state on all available PEs should have
- * been cleared by the PHB reset. Otherwise, we unfreeze the PE and its
- * child PEs because they might be in frozen state.
- */
- if (!(pe->type & EEH_PE_PHB)) {
- rc = eeh_clear_pe_frozen_state(pe, false);
- if (rc)
- return rc;
- }
+ /* Clear frozen state */
+ rc = eeh_clear_pe_frozen_state(pe, false);
+ if (rc)
+ return rc;
/* Give the system 5 seconds to finish running the user-space
* hotplug shutdown scripts, e.g. ifdown for ethernet. Yes,
diff --git a/arch/powerpc/kernel/module_32.c b/arch/powerpc/kernel/module_32.c
index c94d2e018d84..2c01665eb410 100644
--- a/arch/powerpc/kernel/module_32.c
+++ b/arch/powerpc/kernel/module_32.c
@@ -188,8 +188,8 @@ static uint32_t do_plt_call(void *location,
pr_debug("Doing plt for call to 0x%x at 0x%x\n", val, (unsigned int)location);
/* Init, or core PLT? */
- if (location >= mod->module_core
- && location < mod->module_core + mod->core_size)
+ if (location >= mod->core_layout.base
+ && location < mod->core_layout.base + mod->core_layout.size)
entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr;
else
entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr;
@@ -296,7 +296,7 @@ int apply_relocate_add(Elf32_Shdr *sechdrs,
}
#ifdef CONFIG_DYNAMIC_FTRACE
module->arch.tramp =
- do_plt_call(module->module_core,
+ do_plt_call(module->core_layout.base,
(unsigned long)ftrace_caller,
sechdrs, module);
#endif
diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c
index 32e26526f7e4..0cab9e8c3794 100644
--- a/arch/powerpc/kernel/nvram_64.c
+++ b/arch/powerpc/kernel/nvram_64.c
@@ -27,6 +27,7 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/kmsg_dump.h>
+#include <linux/pagemap.h>
#include <linux/pstore.h>
#include <linux/zlib.h>
#include <asm/uaccess.h>
@@ -733,24 +734,10 @@ static void oops_to_nvram(struct kmsg_dumper *dumper,
static loff_t dev_nvram_llseek(struct file *file, loff_t offset, int origin)
{
- int size;
-
if (ppc_md.nvram_size == NULL)
return -ENODEV;
- size = ppc_md.nvram_size();
-
- switch (origin) {
- case 1:
- offset += file->f_pos;
- break;
- case 2:
- offset += size;
- break;
- }
- if (offset < 0)
- return -EINVAL;
- file->f_pos = offset;
- return file->f_pos;
+ return generic_file_llseek_size(file, offset, origin, MAX_LFS_FILESIZE,
+ ppc_md.nvram_size());
}
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index 8e694707bc56..cff207b72c46 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -224,6 +224,12 @@ static void kvmppc_core_vcpu_put_hv(struct kvm_vcpu *vcpu)
static void kvmppc_set_msr_hv(struct kvm_vcpu *vcpu, u64 msr)
{
+ /*
+ * Check for illegal transactional state bit combination
+ * and if we find it, force the TS field to a safe state.
+ */
+ if ((msr & MSR_TS_MASK) == MSR_TS_MASK)
+ msr &= ~MSR_TS_MASK;
vcpu->arch.shregs.msr = msr;
kvmppc_end_cede(vcpu);
}
@@ -308,16 +314,10 @@ static void kvmppc_dump_regs(struct kvm_vcpu *vcpu)
static struct kvm_vcpu *kvmppc_find_vcpu(struct kvm *kvm, int id)
{
- int r;
- struct kvm_vcpu *v, *ret = NULL;
+ struct kvm_vcpu *ret;
mutex_lock(&kvm->lock);
- kvm_for_each_vcpu(r, v, kvm) {
- if (v->vcpu_id == id) {
- ret = v;
- break;
- }
- }
+ ret = kvm_get_vcpu_by_id(kvm, id);
mutex_unlock(&kvm->lock);
return ret;
}
diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c
index a78e0e6bd932..95bceca8f40e 100644
--- a/arch/powerpc/kvm/book3s_pr.c
+++ b/arch/powerpc/kvm/book3s_pr.c
@@ -512,7 +512,7 @@ static void kvmppc_patch_dcbz(struct kvm_vcpu *vcpu, struct kvmppc_pte *pte)
put_page(hpage);
}
-static int kvmppc_visible_gpa(struct kvm_vcpu *vcpu, gpa_t gpa)
+static bool kvmppc_visible_gpa(struct kvm_vcpu *vcpu, gpa_t gpa)
{
ulong mp_pa = vcpu->arch.magic_page_pa;
@@ -521,7 +521,7 @@ static int kvmppc_visible_gpa(struct kvm_vcpu *vcpu, gpa_t gpa)
gpa &= ~0xFFFULL;
if (unlikely(mp_pa) && unlikely((mp_pa & KVM_PAM) == (gpa & KVM_PAM))) {
- return 1;
+ return true;
}
return kvm_is_visible_gfn(vcpu->kvm, gpa >> PAGE_SHIFT);
diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c
index 04782164ee67..2d66a8446198 100644
--- a/arch/powerpc/net/bpf_jit_comp.c
+++ b/arch/powerpc/net/bpf_jit_comp.c
@@ -78,18 +78,9 @@ static void bpf_jit_build_prologue(struct bpf_prog *fp, u32 *image,
PPC_LI(r_X, 0);
}
- switch (filter[0].code) {
- case BPF_RET | BPF_K:
- case BPF_LD | BPF_W | BPF_LEN:
- case BPF_LD | BPF_W | BPF_ABS:
- case BPF_LD | BPF_H | BPF_ABS:
- case BPF_LD | BPF_B | BPF_ABS:
- /* first instruction sets A register (or is RET 'constant') */
- break;
- default:
- /* make sure we dont leak kernel information to user */
+ /* make sure we dont leak kernel information to user */
+ if (bpf_needs_clear_a(&filter[0]))
PPC_LI(r_A, 0);
- }
}
static void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx)
diff --git a/arch/powerpc/platforms/82xx/ep8248e.c b/arch/powerpc/platforms/82xx/ep8248e.c
index a0cb8bd41958..6781bda117be 100644
--- a/arch/powerpc/platforms/82xx/ep8248e.c
+++ b/arch/powerpc/platforms/82xx/ep8248e.c
@@ -131,23 +131,15 @@ static int ep8248e_mdio_probe(struct platform_device *ofdev)
if (!bus)
return -ENOMEM;
- bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (bus->irq == NULL) {
- ret = -ENOMEM;
- goto err_free_bus;
- }
-
bus->name = "ep8248e-mdio-bitbang";
bus->parent = &ofdev->dev;
snprintf(bus->id, MII_BUS_ID_SIZE, "%x", res.start);
ret = of_mdiobus_register(bus, ofdev->dev.of_node);
if (ret)
- goto err_free_irq;
+ goto err_free_bus;
return 0;
-err_free_irq:
- kfree(bus->irq);
err_free_bus:
free_mdio_bitbang(bus);
return ret;
diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c
index 11634fa7ab3c..ad4840f86be1 100644
--- a/arch/powerpc/platforms/cell/spufs/inode.c
+++ b/arch/powerpc/platforms/cell/spufs/inode.c
@@ -767,7 +767,7 @@ static int __init spufs_init(void)
ret = -ENOMEM;
spufs_inode_cache = kmem_cache_create("spufs_inode_cache",
sizeof(struct spufs_inode_info), 0,
- SLAB_HWCACHE_ALIGN, spufs_init_once);
+ SLAB_HWCACHE_ALIGN|SLAB_ACCOUNT, spufs_init_once);
if (!spufs_inode_cache)
goto out;
diff --git a/arch/powerpc/platforms/pasemi/gpio_mdio.c b/arch/powerpc/platforms/pasemi/gpio_mdio.c
index ae3f47b25b18..ddf635000c6b 100644
--- a/arch/powerpc/platforms/pasemi/gpio_mdio.c
+++ b/arch/powerpc/platforms/pasemi/gpio_mdio.c
@@ -41,7 +41,6 @@ static void __iomem *gpio_regs;
struct gpio_priv {
int mdc_pin;
int mdio_pin;
- int mdio_irqs[PHY_MAX_ADDR];
};
#define MDC_PIN(bus) (((struct gpio_priv *)bus->priv)->mdc_pin)
@@ -245,8 +244,6 @@ static int gpio_mdio_probe(struct platform_device *ofdev)
snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", *prop);
new_bus->priv = priv;
- new_bus->irq = priv->mdio_irqs;
-
prop = of_get_property(np, "mdc-pin", NULL);
priv->mdc_pin = *prop;
diff --git a/arch/powerpc/platforms/powernv/opal-irqchip.c b/arch/powerpc/platforms/powernv/opal-irqchip.c
index 6ccfb6c1c707..e505223b4ec5 100644
--- a/arch/powerpc/platforms/powernv/opal-irqchip.c
+++ b/arch/powerpc/platforms/powernv/opal-irqchip.c
@@ -43,11 +43,34 @@ 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 u64 last_outstanding_events;
static struct irq_work opal_event_irq_work = {
.func = opal_handle_irq_work,
};
+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 void opal_event_mask(struct irq_data *d)
{
clear_bit(d->hwirq, &opal_event_irqchip.mask);
@@ -55,9 +78,21 @@ static void opal_event_mask(struct irq_data *d)
static void opal_event_unmask(struct irq_data *d)
{
+ __be64 events;
+
set_bit(d->hwirq, &opal_event_irqchip.mask);
- opal_poll_events(&last_outstanding_events);
+ opal_poll_events(&events);
+ last_outstanding_events = be64_to_cpu(events);
+
+ /*
+ * We can't just handle the events now with opal_handle_events().
+ * If we did we would deadlock when opal_event_unmask() is called from
+ * handle_level_irq() with the irq descriptor lock held, because
+ * calling opal_handle_events() would call generic_handle_irq() and
+ * then handle_level_irq() which would try to take the descriptor lock
+ * again. Instead queue the events for later.
+ */
if (last_outstanding_events & opal_event_irqchip.mask)
/* Need to retrigger the interrupt */
irq_work_queue(&opal_event_irq_work);
@@ -96,29 +131,6 @@ static int opal_event_map(struct irq_domain *d, unsigned int 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;
@@ -131,7 +143,7 @@ static irqreturn_t opal_interrupt(int irq, void *data)
static void opal_handle_irq_work(struct irq_work *work)
{
- opal_handle_events(be64_to_cpu(last_outstanding_events));
+ opal_handle_events(last_outstanding_events);
}
static int opal_event_match(struct irq_domain *h, struct device_node *node,
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 81f4a3ab8743..4e0da5af94a1 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -273,7 +273,7 @@ static void opal_handle_message(void)
/* Sanity check */
if (type >= OPAL_MSG_TYPE_MAX) {
- pr_warning("%s: Unknown message type: %u\n", __func__, type);
+ pr_warn_once("%s: Unknown message type: %u\n", __func__, type);
return;
}
opal_message_do_notify(type, (void *)&msg);
diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c
index 79976e51b8ad..c69e88e91459 100644
--- a/arch/powerpc/sysdev/fsl_pci.c
+++ b/arch/powerpc/sysdev/fsl_pci.c
@@ -21,10 +21,12 @@
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/string.h>
+#include <linux/fsl/edac.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/memblock.h>
#include <linux/log2.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/suspend.h>
#include <linux/syscore_ops.h>
@@ -1268,6 +1270,25 @@ void fsl_pcibios_fixup_phb(struct pci_controller *phb)
#endif
}
+static int add_err_dev(struct platform_device *pdev)
+{
+ struct platform_device *errdev;
+ struct mpc85xx_edac_pci_plat_data pd = {
+ .of_node = pdev->dev.of_node
+ };
+
+ errdev = platform_device_register_resndata(&pdev->dev,
+ "mpc85xx-pci-edac",
+ PLATFORM_DEVID_AUTO,
+ pdev->resource,
+ pdev->num_resources,
+ &pd, sizeof(pd));
+ if (IS_ERR(errdev))
+ return PTR_ERR(errdev);
+
+ return 0;
+}
+
static int fsl_pci_probe(struct platform_device *pdev)
{
struct device_node *node;
@@ -1275,8 +1296,13 @@ static int fsl_pci_probe(struct platform_device *pdev)
node = pdev->dev.of_node;
ret = fsl_add_bridge(pdev, fsl_pci_primary == node);
+ if (ret)
+ return ret;
- mpc85xx_pci_err_probe(pdev);
+ ret = add_err_dev(pdev);
+ if (ret)
+ dev_err(&pdev->dev, "couldn't register error device: %d\n",
+ ret);
return 0;
}
diff --git a/arch/powerpc/sysdev/fsl_pci.h b/arch/powerpc/sysdev/fsl_pci.h
index c1cec771d5ea..151588530b06 100644
--- a/arch/powerpc/sysdev/fsl_pci.h
+++ b/arch/powerpc/sysdev/fsl_pci.h
@@ -130,15 +130,6 @@ void fsl_pci_assign_primary(void);
static inline void fsl_pci_assign_primary(void) {}
#endif
-#ifdef CONFIG_EDAC_MPC85XX
-int mpc85xx_pci_err_probe(struct platform_device *op);
-#else
-static inline int mpc85xx_pci_err_probe(struct platform_device *op)
-{
- return -ENOTSUPP;
-}
-#endif
-
#ifdef CONFIG_FSL_PCI
extern int fsl_pci_mcheck_exception(struct pt_regs *);
#else
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 3a55f493c7da..24490344c30f 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -66,6 +66,7 @@ config S390
def_bool y
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
select ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS
+ select ARCH_HAS_DEVMEM_IS_ALLOWED
select ARCH_HAS_ELF_RANDOMIZE
select ARCH_HAS_GCOV_PROFILE_ALL
select ARCH_HAS_SG_CHAIN
@@ -166,8 +167,7 @@ config SCHED_OMIT_FRAME_POINTER
config PGTABLE_LEVELS
int
- default 4 if 64BIT
- default 2
+ default 4
source "init/Kconfig"
@@ -390,9 +390,6 @@ config HOTPLUG_CPU
can be controlled through /sys/devices/system/cpu/cpu#.
Say N if you want to disable CPU hotplug.
-config SCHED_SMT
- def_bool n
-
# Some NUMA nodes have memory ranges that span
# other nodes. Even though a pfn is valid and
# between a node's start and end pfns, it may not
@@ -403,7 +400,7 @@ config NODES_SPAN_OTHER_NODES
config NUMA
bool "NUMA support"
- depends on SMP && 64BIT && SCHED_TOPOLOGY
+ depends on SMP && SCHED_TOPOLOGY
default n
help
Enable NUMA support
@@ -463,6 +460,9 @@ config EMU_SIZE
endmenu
+config SCHED_SMT
+ def_bool n
+
config SCHED_MC
def_bool n
diff --git a/arch/s390/Kconfig.debug b/arch/s390/Kconfig.debug
index c56878e1245f..26c5d5beb4be 100644
--- a/arch/s390/Kconfig.debug
+++ b/arch/s390/Kconfig.debug
@@ -5,18 +5,6 @@ config TRACE_IRQFLAGS_SUPPORT
source "lib/Kconfig.debug"
-config STRICT_DEVMEM
- def_bool y
- prompt "Filter access to /dev/mem"
- ---help---
- This option restricts access to /dev/mem. If this option is
- disabled, you allow userspace access to all memory, including
- kernel and userspace memory. Accidental memory access is likely
- to be disastrous.
- Memory access is required for experts who want to debug the kernel.
-
- If you are unsure, say Y.
-
config S390_PTDUMP
bool "Export kernel pagetable layout to userspace via debugfs"
depends on DEBUG_KERNEL
diff --git a/arch/s390/Makefile b/arch/s390/Makefile
index e8d4423e4f85..224b42734f0d 100644
--- a/arch/s390/Makefile
+++ b/arch/s390/Makefile
@@ -106,6 +106,7 @@ drivers-y += drivers/s390/
drivers-$(CONFIG_OPROFILE) += arch/s390/oprofile/
boot := arch/s390/boot
+tools := arch/s390/tools
all: image bzImage
@@ -124,9 +125,17 @@ vdso_install:
archclean:
$(Q)$(MAKE) $(clean)=$(boot)
+ $(Q)$(MAKE) $(clean)=$(tools)
+
+archprepare:
+ $(Q)$(MAKE) $(build)=$(tools) include/generated/facilities.h
# Don't use tabs in echo arguments
define archhelp
echo '* image - Kernel image for IPL ($(boot)/image)'
echo '* bzImage - Compressed kernel image for IPL ($(boot)/bzImage)'
+ echo ' install - Install kernel using'
+ echo ' (your) ~/bin/$(INSTALLKERNEL) or'
+ echo ' (distribution) /sbin/$(INSTALLKERNEL) or'
+ echo ' install to $$(INSTALL_PATH)'
endef
diff --git a/arch/s390/configs/default_defconfig b/arch/s390/configs/default_defconfig
index ed7da281df66..0ac42cc4f880 100644
--- a/arch/s390/configs/default_defconfig
+++ b/arch/s390/configs/default_defconfig
@@ -10,28 +10,35 @@ CONFIG_TASKSTATS=y
CONFIG_TASK_DELAY_ACCT=y
CONFIG_TASK_XACCT=y
CONFIG_TASK_IO_ACCOUNTING=y
-CONFIG_RCU_FAST_NO_HZ=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_NUMA_BALANCING=y
CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_PIDS=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CPUSETS=y
CONFIG_CGROUP_CPUACCT=y
+CONFIG_MEMCG=y
+CONFIG_MEMCG_SWAP=y
+CONFIG_MEMCG_KMEM=y
+CONFIG_CGROUP_HUGETLB=y
CONFIG_CGROUP_PERF=y
CONFIG_CFS_BANDWIDTH=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_BLK_CGROUP=y
CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
CONFIG_SCHED_AUTOGROUP=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_EXPERT=y
CONFIG_BPF_SYSCALL=y
+CONFIG_USERFAULTFD=y
# CONFIG_COMPAT_BRK is not set
CONFIG_PROFILING=y
CONFIG_OPROFILE=m
CONFIG_KPROBES=y
CONFIG_JUMP_LABEL=y
+CONFIG_STATIC_KEYS_SELFTEST=y
CONFIG_MODULES=y
CONFIG_MODULE_FORCE_LOAD=y
CONFIG_MODULE_UNLOAD=y
@@ -64,7 +71,6 @@ CONFIG_HOTPLUG_PCI=y
CONFIG_HOTPLUG_PCI_S390=y
CONFIG_CHSC_SCH=y
CONFIG_CRASH_DUMP=y
-# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
CONFIG_BINFMT_MISC=m
CONFIG_HIBERNATION=y
CONFIG_NET=y
@@ -106,7 +112,6 @@ CONFIG_TCP_CONG_LP=m
CONFIG_TCP_CONG_VENO=m
CONFIG_TCP_CONG_YEAH=m
CONFIG_TCP_CONG_ILLINOIS=m
-CONFIG_IPV6=y
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_INET6_AH=m
CONFIG_INET6_ESP=m
@@ -457,19 +462,9 @@ CONFIG_INFINIBAND=m
CONFIG_INFINIBAND_USER_ACCESS=m
CONFIG_MLX4_INFINIBAND=m
CONFIG_VIRTIO_BALLOON=m
-# CONFIG_IOMMU_SUPPORT is not set
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT2_FS_POSIX_ACL=y
-CONFIG_EXT2_FS_SECURITY=y
-CONFIG_EXT3_FS=y
-# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
-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_JBD_DEBUG=y
CONFIG_JBD2_DEBUG=y
CONFIG_JFS_FS=m
CONFIG_JFS_POSIX_ACL=y
@@ -490,7 +485,7 @@ CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_QFMT_V1=m
CONFIG_QFMT_V2=m
CONFIG_AUTOFS4_FS=m
-CONFIG_FUSE_FS=m
+CONFIG_FUSE_FS=y
CONFIG_CUSE=m
CONFIG_FSCACHE=m
CONFIG_CACHEFILES=m
@@ -542,10 +537,11 @@ CONFIG_DLM=m
CONFIG_PRINTK_TIME=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_INFO=y
-# CONFIG_ENABLE_MUST_CHECK is not set
CONFIG_FRAME_WARN=1024
CONFIG_READABLE_ASM=y
CONFIG_UNUSED_SYMBOLS=y
+CONFIG_HEADERS_CHECK=y
+CONFIG_DEBUG_SECTION_MISMATCH=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_PAGEALLOC=y
CONFIG_DEBUG_OBJECTS=y
@@ -588,6 +584,7 @@ CONFIG_FAILSLAB=y
CONFIG_FAIL_PAGE_ALLOC=y
CONFIG_FAIL_MAKE_REQUEST=y
CONFIG_FAIL_IO_TIMEOUT=y
+CONFIG_FAIL_FUTEX=y
CONFIG_FAULT_INJECTION_DEBUG_FS=y
CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
CONFIG_LATENCYTOP=y
diff --git a/arch/s390/configs/gcov_defconfig b/arch/s390/configs/gcov_defconfig
index 9858b14cde1e..a31dcd56f7c0 100644
--- a/arch/s390/configs/gcov_defconfig
+++ b/arch/s390/configs/gcov_defconfig
@@ -10,21 +10,27 @@ CONFIG_TASKSTATS=y
CONFIG_TASK_DELAY_ACCT=y
CONFIG_TASK_XACCT=y
CONFIG_TASK_IO_ACCOUNTING=y
-CONFIG_RCU_FAST_NO_HZ=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_NUMA_BALANCING=y
CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_PIDS=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CPUSETS=y
CONFIG_CGROUP_CPUACCT=y
+CONFIG_MEMCG=y
+CONFIG_MEMCG_SWAP=y
+CONFIG_MEMCG_KMEM=y
+CONFIG_CGROUP_HUGETLB=y
CONFIG_CGROUP_PERF=y
CONFIG_BLK_CGROUP=y
CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
CONFIG_SCHED_AUTOGROUP=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_EXPERT=y
CONFIG_BPF_SYSCALL=y
+CONFIG_USERFAULTFD=y
# CONFIG_COMPAT_BRK is not set
CONFIG_PROFILING=y
CONFIG_OPROFILE=m
@@ -61,7 +67,6 @@ CONFIG_HOTPLUG_PCI=y
CONFIG_HOTPLUG_PCI_S390=y
CONFIG_CHSC_SCH=y
CONFIG_CRASH_DUMP=y
-# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
CONFIG_BINFMT_MISC=m
CONFIG_HIBERNATION=y
CONFIG_NET=y
@@ -103,7 +108,6 @@ CONFIG_TCP_CONG_LP=m
CONFIG_TCP_CONG_VENO=m
CONFIG_TCP_CONG_YEAH=m
CONFIG_TCP_CONG_ILLINOIS=m
-CONFIG_IPV6=y
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_INET6_AH=m
CONFIG_INET6_ESP=m
@@ -453,19 +457,9 @@ CONFIG_INFINIBAND=m
CONFIG_INFINIBAND_USER_ACCESS=m
CONFIG_MLX4_INFINIBAND=m
CONFIG_VIRTIO_BALLOON=m
-# CONFIG_IOMMU_SUPPORT is not set
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT2_FS_POSIX_ACL=y
-CONFIG_EXT2_FS_SECURITY=y
-CONFIG_EXT3_FS=y
-# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
-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_JBD_DEBUG=y
CONFIG_JBD2_DEBUG=y
CONFIG_JFS_FS=m
CONFIG_JFS_POSIX_ACL=y
@@ -485,7 +479,7 @@ CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_QFMT_V1=m
CONFIG_QFMT_V2=m
CONFIG_AUTOFS4_FS=m
-CONFIG_FUSE_FS=m
+CONFIG_FUSE_FS=y
CONFIG_CUSE=m
CONFIG_FSCACHE=m
CONFIG_CACHEFILES=m
@@ -550,6 +544,7 @@ CONFIG_NOTIFIER_ERROR_INJECTION=m
CONFIG_CPU_NOTIFIER_ERROR_INJECT=m
CONFIG_PM_NOTIFIER_ERROR_INJECT=m
CONFIG_LATENCYTOP=y
+CONFIG_DEBUG_STRICT_USER_COPY_CHECKS=y
CONFIG_BLK_DEV_IO_TRACE=y
# CONFIG_KPROBE_EVENT is not set
CONFIG_LKDTM=m
@@ -557,6 +552,7 @@ CONFIG_RBTREE_TEST=m
CONFIG_INTERVAL_TREE_TEST=m
CONFIG_PERCPU_TEST=m
CONFIG_ATOMIC64_SELFTEST=y
+CONFIG_TEST_BPF=m
# CONFIG_STRICT_DEVMEM is not set
CONFIG_S390_PTDUMP=y
CONFIG_ENCRYPTED_KEYS=m
diff --git a/arch/s390/configs/performance_defconfig b/arch/s390/configs/performance_defconfig
index 7f14f80717d4..7b73bf353345 100644
--- a/arch/s390/configs/performance_defconfig
+++ b/arch/s390/configs/performance_defconfig
@@ -10,22 +10,28 @@ CONFIG_TASKSTATS=y
CONFIG_TASK_DELAY_ACCT=y
CONFIG_TASK_XACCT=y
CONFIG_TASK_IO_ACCOUNTING=y
-CONFIG_RCU_FAST_NO_HZ=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_NUMA_BALANCING=y
# CONFIG_NUMA_BALANCING_DEFAULT_ENABLED is not set
CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_PIDS=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CPUSETS=y
CONFIG_CGROUP_CPUACCT=y
+CONFIG_MEMCG=y
+CONFIG_MEMCG_SWAP=y
+CONFIG_MEMCG_KMEM=y
+CONFIG_CGROUP_HUGETLB=y
CONFIG_CGROUP_PERF=y
CONFIG_BLK_CGROUP=y
CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
CONFIG_SCHED_AUTOGROUP=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_EXPERT=y
CONFIG_BPF_SYSCALL=y
+CONFIG_USERFAULTFD=y
# CONFIG_COMPAT_BRK is not set
CONFIG_PROFILING=y
CONFIG_OPROFILE=m
@@ -61,7 +67,6 @@ CONFIG_HOTPLUG_PCI=y
CONFIG_HOTPLUG_PCI_S390=y
CONFIG_CHSC_SCH=y
CONFIG_CRASH_DUMP=y
-# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
CONFIG_BINFMT_MISC=m
CONFIG_HIBERNATION=y
CONFIG_NET=y
@@ -103,7 +108,6 @@ CONFIG_TCP_CONG_LP=m
CONFIG_TCP_CONG_VENO=m
CONFIG_TCP_CONG_YEAH=m
CONFIG_TCP_CONG_ILLINOIS=m
-CONFIG_IPV6=y
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_INET6_AH=m
CONFIG_INET6_ESP=m
@@ -453,19 +457,9 @@ CONFIG_INFINIBAND=m
CONFIG_INFINIBAND_USER_ACCESS=m
CONFIG_MLX4_INFINIBAND=m
CONFIG_VIRTIO_BALLOON=m
-# CONFIG_IOMMU_SUPPORT is not set
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT2_FS_POSIX_ACL=y
-CONFIG_EXT2_FS_SECURITY=y
-CONFIG_EXT3_FS=y
-# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
-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_JBD_DEBUG=y
CONFIG_JBD2_DEBUG=y
CONFIG_JFS_FS=m
CONFIG_JFS_POSIX_ACL=y
@@ -485,7 +479,7 @@ CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_QFMT_V1=m
CONFIG_QFMT_V2=m
CONFIG_AUTOFS4_FS=m
-CONFIG_FUSE_FS=m
+CONFIG_FUSE_FS=y
CONFIG_CUSE=m
CONFIG_FSCACHE=m
CONFIG_CACHEFILES=m
@@ -546,6 +540,7 @@ CONFIG_TIMER_STATS=y
CONFIG_RCU_TORTURE_TEST=m
CONFIG_RCU_CPU_STALL_TIMEOUT=60
CONFIG_LATENCYTOP=y
+CONFIG_DEBUG_STRICT_USER_COPY_CHECKS=y
CONFIG_SCHED_TRACER=y
CONFIG_FTRACE_SYSCALLS=y
CONFIG_STACK_TRACER=y
@@ -554,6 +549,7 @@ CONFIG_UPROBE_EVENT=y
CONFIG_LKDTM=m
CONFIG_PERCPU_TEST=m
CONFIG_ATOMIC64_SELFTEST=y
+CONFIG_TEST_BPF=m
# CONFIG_STRICT_DEVMEM is not set
CONFIG_S390_PTDUMP=y
CONFIG_ENCRYPTED_KEYS=m
diff --git a/arch/s390/configs/zfcpdump_defconfig b/arch/s390/configs/zfcpdump_defconfig
index 92805d604173..1719843a55a2 100644
--- a/arch/s390/configs/zfcpdump_defconfig
+++ b/arch/s390/configs/zfcpdump_defconfig
@@ -23,8 +23,6 @@ CONFIG_CRASH_DUMP=y
# CONFIG_SECCOMP is not set
CONFIG_NET=y
# CONFIG_IUCV is not set
-CONFIG_ATM=y
-CONFIG_ATM_LANE=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_DEVTMPFS=y
# CONFIG_FIRMWARE_IN_KERNEL is not set
@@ -54,14 +52,10 @@ CONFIG_RAW_DRIVER=y
# CONFIG_S390_VMUR is not set
# CONFIG_HID is not set
# CONFIG_IOMMU_SUPPORT is not set
-CONFIG_EXT2_FS=y
-CONFIG_EXT3_FS=y
-# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
-CONFIG_EXT4_FS=y
-CONFIG_EXT4_FS_POSIX_ACL=y
-CONFIG_EXT4_FS_SECURITY=y
+# CONFIG_DNOTIFY is not set
# CONFIG_INOTIFY_USER is not set
CONFIG_CONFIGFS_FS=y
+# CONFIG_MISC_FILESYSTEMS is not set
CONFIG_PRINTK_TIME=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_FS=y
diff --git a/arch/s390/defconfig b/arch/s390/defconfig
index 9256b48e7e43..e24f2af4c73b 100644
--- a/arch/s390/defconfig
+++ b/arch/s390/defconfig
@@ -11,22 +11,31 @@ CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_CGROUPS=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_PIDS=y
+CONFIG_CGROUP_DEVICE=y
CONFIG_CPUSETS=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y
+CONFIG_MEMCG_KMEM=y
+CONFIG_CGROUP_HUGETLB=y
+CONFIG_CGROUP_PERF=y
CONFIG_CGROUP_SCHED=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_BLK_CGROUP=y
CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_EXPERT=y
CONFIG_BPF_SYSCALL=y
+CONFIG_USERFAULTFD=y
# CONFIG_COMPAT_BRK is not set
CONFIG_PROFILING=y
CONFIG_OPROFILE=y
CONFIG_KPROBES=y
CONFIG_JUMP_LABEL=y
+CONFIG_STATIC_KEYS_SELFTEST=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODVERSIONS=y
@@ -37,6 +46,7 @@ CONFIG_DEFAULT_DEADLINE=y
CONFIG_LIVEPATCH=y
CONFIG_MARCH_Z196=y
CONFIG_NR_CPUS=256
+CONFIG_NUMA=y
CONFIG_HZ_100=y
CONFIG_MEMORY_HOTPLUG=y
CONFIG_MEMORY_HOTREMOVE=y
@@ -52,7 +62,6 @@ CONFIG_NET_KEY=y
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
# CONFIG_INET_LRO is not set
-CONFIG_IPV6=y
CONFIG_L2TP=m
CONFIG_L2TP_DEBUGFS=m
CONFIG_VLAN_8021Q=y
@@ -89,10 +98,26 @@ CONFIG_BLK_DEV_SR_VENDOR=y
CONFIG_CHR_DEV_SG=y
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
-CONFIG_SCSI_SCAN_ASYNC=y
CONFIG_SCSI_FC_ATTRS=y
CONFIG_ZFCP=y
CONFIG_SCSI_VIRTIO=y
+CONFIG_MD=y
+CONFIG_MD_LINEAR=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_MULTIPATH=m
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_LOG_USERSPACE=m
+CONFIG_DM_RAID=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_MULTIPATH=m
+CONFIG_DM_MULTIPATH_QL=m
+CONFIG_DM_MULTIPATH_ST=m
+CONFIG_DM_UEVENT=y
+CONFIG_DM_VERITY=m
+CONFIG_DM_SWITCH=m
CONFIG_NETDEVICES=y
CONFIG_BONDING=m
CONFIG_DUMMY=m
@@ -137,7 +162,6 @@ CONFIG_DEBUG_PI_LIST=y
CONFIG_DEBUG_SG=y
CONFIG_DEBUG_NOTIFIERS=y
CONFIG_RCU_CPU_STALL_TIMEOUT=60
-# CONFIG_RCU_CPU_STALL_INFO is not set
CONFIG_RCU_TRACE=y
CONFIG_LATENCYTOP=y
CONFIG_DEBUG_STRICT_USER_COPY_CHECKS=y
diff --git a/arch/s390/include/asm/barrier.h b/arch/s390/include/asm/barrier.h
index d68e11e0df5e..7ffd0b19135c 100644
--- a/arch/s390/include/asm/barrier.h
+++ b/arch/s390/include/asm/barrier.h
@@ -36,7 +36,7 @@
#define smp_mb__before_atomic() smp_mb()
#define smp_mb__after_atomic() smp_mb()
-#define smp_store_mb(var, value) do { WRITE_ONCE(var, value); 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 { \
diff --git a/arch/s390/include/asm/compat.h b/arch/s390/include/asm/compat.h
index d350ed9d0fbb..352f7bdaf11f 100644
--- a/arch/s390/include/asm/compat.h
+++ b/arch/s390/include/asm/compat.h
@@ -284,7 +284,7 @@ static inline compat_uptr_t ptr_to_compat(void __user *uptr)
static inline int is_compat_task(void)
{
- return is_32bit_task();
+ return test_thread_flag(TIF_31BIT);
}
static inline void __user *arch_compat_alloc_user_space(long len)
diff --git a/arch/s390/include/asm/crw.h b/arch/s390/include/asm/crw.h
index 7c31d3e25cd1..bcb9cd2a730a 100644
--- a/arch/s390/include/asm/crw.h
+++ b/arch/s390/include/asm/crw.h
@@ -52,18 +52,4 @@ void crw_wait_for_channel_report(void);
#define CRW_ERC_PERRI 0x07 /* perm. error, facility init */
#define CRW_ERC_PMOD 0x08 /* installed parameters modified */
-static inline int stcrw(struct crw *pcrw)
-{
- int ccode;
-
- asm volatile(
- " stcrw 0(%2)\n"
- " ipm %0\n"
- " srl %0,28\n"
- : "=d" (ccode), "=m" (*pcrw)
- : "a" (pcrw)
- : "cc" );
- return ccode;
-}
-
#endif /* _ASM_S390_CRW_H */
diff --git a/arch/s390/include/asm/elf.h b/arch/s390/include/asm/elf.h
index bab6739a1154..563ab9f44874 100644
--- a/arch/s390/include/asm/elf.h
+++ b/arch/s390/include/asm/elf.h
@@ -104,6 +104,9 @@
#define HWCAP_S390_TE 1024
#define HWCAP_S390_VXRS 2048
+/* Internal bits, not exposed via elf */
+#define HWCAP_INT_SIE 1UL
+
/*
* These are used to set parameters in the core dumps.
*/
@@ -126,6 +129,7 @@ typedef s390_regs elf_gregset_t;
typedef s390_fp_regs compat_elf_fpregset_t;
typedef s390_compat_regs compat_elf_gregset_t;
+#include <linux/compat.h>
#include <linux/sched.h> /* for task_struct */
#include <asm/mmu_context.h>
@@ -159,7 +163,7 @@ extern unsigned int vdso_enabled;
the loader. We need to make sure that it is out of the way of the program
that it will "exec", and that there is sufficient room for the brk. 64-bit
tasks are aligned to 4GB. */
-#define ELF_ET_DYN_BASE (is_32bit_task() ? \
+#define ELF_ET_DYN_BASE (is_compat_task() ? \
(STACK_TOP / 3 * 2) : \
(STACK_TOP / 3 * 2) & ~((1UL << 32) - 1))
@@ -169,6 +173,10 @@ extern unsigned int vdso_enabled;
extern unsigned long elf_hwcap;
#define ELF_HWCAP (elf_hwcap)
+/* Internal hardware capabilities, not exposed via elf */
+
+extern unsigned long int_hwcap;
+
/* This yields a string that ld.so will use to load implementation
specific libraries for optimization. This is more specific in
intent than poking at uname or /proc/cpuinfo.
@@ -212,9 +220,9 @@ do { \
* of up to 1GB. For 31-bit processes the virtual address space is limited,
* use no alignment and limit the randomization to 8MB.
*/
-#define BRK_RND_MASK (is_32bit_task() ? 0x7ffUL : 0x3ffffUL)
-#define MMAP_RND_MASK (is_32bit_task() ? 0x7ffUL : 0x3ff80UL)
-#define MMAP_ALIGN_MASK (is_32bit_task() ? 0 : 0x7fUL)
+#define BRK_RND_MASK (is_compat_task() ? 0x7ffUL : 0x3ffffUL)
+#define MMAP_RND_MASK (is_compat_task() ? 0x7ffUL : 0x3ff80UL)
+#define MMAP_ALIGN_MASK (is_compat_task() ? 0 : 0x7fUL)
#define STACK_RND_MASK MMAP_RND_MASK
#define ARCH_DLINFO \
@@ -229,6 +237,4 @@ struct linux_binprm;
#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
int arch_setup_additional_pages(struct linux_binprm *, int);
-void *fill_cpu_elf_notes(void *ptr, struct save_area *sa, __vector128 *vxrs);
-
#endif
diff --git a/arch/s390/include/asm/facilities_src.h b/arch/s390/include/asm/facilities_src.h
new file mode 100644
index 000000000000..4917728e5828
--- /dev/null
+++ b/arch/s390/include/asm/facilities_src.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright IBM Corp. 2015
+ */
+
+#ifndef S390_GEN_FACILITIES_C
+#error "This file can only be included by gen_facilities.c"
+#endif
+
+#include <linux/kconfig.h>
+
+struct facility_def {
+ char *name;
+ int *bits;
+};
+
+static struct facility_def facility_defs[] = {
+ {
+ /*
+ * FACILITIES_ALS contains the list of facilities that are
+ * required to run a kernel that is compiled e.g. with
+ * -march=<machine>.
+ */
+ .name = "FACILITIES_ALS",
+ .bits = (int[]){
+#ifdef CONFIG_HAVE_MARCH_Z900_FEATURES
+ 0, /* N3 instructions */
+ 1, /* z/Arch mode installed */
+#endif
+#ifdef CONFIG_HAVE_MARCH_Z990_FEATURES
+ 18, /* long displacement facility */
+#endif
+#ifdef CONFIG_HAVE_MARCH_Z9_109_FEATURES
+ 7, /* stfle */
+ 17, /* message security assist */
+ 21, /* extended-immediate facility */
+ 25, /* store clock fast */
+#endif
+#ifdef CONFIG_HAVE_MARCH_Z10_FEATURES
+ 27, /* mvcos */
+ 32, /* compare and swap and store */
+ 33, /* compare and swap and store 2 */
+ 34, /* general extension facility */
+ 35, /* execute extensions */
+#endif
+#ifdef CONFIG_HAVE_MARCH_Z196_FEATURES
+ 45, /* fast-BCR, etc. */
+#endif
+#ifdef CONFIG_HAVE_MARCH_ZEC12_FEATURES
+ 49, /* misc-instruction-extensions */
+ 52, /* interlocked facility 2 */
+#endif
+#ifdef CONFIG_HAVE_MARCH_Z13_FEATURES
+ 53, /* load-and-zero-rightmost-byte, etc. */
+#endif
+ -1 /* END */
+ }
+ },
+};
diff --git a/arch/s390/include/asm/facility.h b/arch/s390/include/asm/facility.h
index 0aa6a7ed95a3..09b406db7529 100644
--- a/arch/s390/include/asm/facility.h
+++ b/arch/s390/include/asm/facility.h
@@ -7,6 +7,10 @@
#ifndef __ASM_FACILITY_H
#define __ASM_FACILITY_H
+#include <generated/facilities.h>
+
+#ifndef __ASSEMBLY__
+
#include <linux/string.h>
#include <linux/preempt.h>
#include <asm/lowcore.h>
@@ -30,6 +34,12 @@ static inline int __test_facility(unsigned long nr, void *facilities)
*/
static inline int test_facility(unsigned long nr)
{
+ unsigned long facilities_als[] = { FACILITIES_ALS };
+
+ if (__builtin_constant_p(nr) && nr < sizeof(facilities_als) * 8) {
+ if (__test_facility(nr, &facilities_als))
+ return 1;
+ }
return __test_facility(nr, &S390_lowcore.stfle_fac_list);
}
@@ -44,10 +54,8 @@ static inline void stfle(u64 *stfle_fac_list, int size)
preempt_disable();
asm volatile(
- " .insn s,0xb2b10000,0(0)\n" /* stfl */
- "0:\n"
- EX_TABLE(0b, 0b)
- : "+m" (S390_lowcore.stfl_fac_list));
+ " stfl 0(0)\n"
+ : "=m" (S390_lowcore.stfl_fac_list));
nr = 4; /* bytes stored by stfl */
memcpy(stfle_fac_list, &S390_lowcore.stfl_fac_list, 4);
if (S390_lowcore.stfl_fac_list & 0x01000000) {
@@ -64,4 +72,5 @@ static inline void stfle(u64 *stfle_fac_list, int size)
preempt_enable();
}
+#endif /* __ASSEMBLY__ */
#endif /* __ASM_FACILITY_H */
diff --git a/arch/s390/include/asm/fpu/internal.h b/arch/s390/include/asm/fpu/internal.h
index 2559b16da525..ea91ddfe54eb 100644
--- a/arch/s390/include/asm/fpu/internal.h
+++ b/arch/s390/include/asm/fpu/internal.h
@@ -12,21 +12,13 @@
#include <asm/ctl_reg.h>
#include <asm/fpu/types.h>
-static inline void save_vx_regs_safe(__vector128 *vxrs)
+static inline void save_vx_regs(__vector128 *vxrs)
{
- unsigned long cr0, flags;
-
- flags = arch_local_irq_save();
- __ctl_store(cr0, 0, 0);
- __ctl_set_bit(0, 17);
- __ctl_set_bit(0, 18);
asm volatile(
" la 1,%0\n"
" .word 0xe70f,0x1000,0x003e\n" /* vstm 0,15,0(1) */
" .word 0xe70f,0x1100,0x0c3e\n" /* vstm 16,31,256(1) */
: "=Q" (*(struct vx_array *) vxrs) : : "1");
- __ctl_load(cr0, 0, 0);
- arch_local_irq_restore(flags);
}
static inline void convert_vx_to_fp(freg_t *fprs, __vector128 *vxrs)
diff --git a/arch/s390/include/asm/ipl.h b/arch/s390/include/asm/ipl.h
index 86634e71b69f..6fc44dca193e 100644
--- a/arch/s390/include/asm/ipl.h
+++ b/arch/s390/include/asm/ipl.h
@@ -87,14 +87,12 @@ struct ipl_parameter_block {
* IPL validity flags
*/
extern u32 ipl_flags;
-extern u32 dump_prefix_page;
-struct dump_save_areas {
- struct save_area_ext **areas;
- int count;
-};
-
-extern struct dump_save_areas dump_save_areas;
+struct save_area;
+struct save_area * __init save_area_alloc(bool is_boot_cpu);
+struct save_area * __init save_area_boot_cpu(void);
+void __init save_area_add_regs(struct save_area *, void *regs);
+void __init save_area_add_vxrs(struct save_area *, __vector128 *vxrs);
extern void do_reipl(void);
extern void do_halt(void);
@@ -176,7 +174,7 @@ enum diag308_rc {
extern int diag308(unsigned long subcode, void *addr);
extern void diag308_reset(void);
-extern void store_status(void);
+extern void store_status(void (*fn)(void *), void *data);
extern void lgr_info_log(void);
#endif /* _ASM_S390_IPL_H */
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
index efaac2c3bb77..6742414dbd6f 100644
--- a/arch/s390/include/asm/kvm_host.h
+++ b/arch/s390/include/asm/kvm_host.h
@@ -25,7 +25,9 @@
#include <asm/fpu/api.h>
#include <asm/isc.h>
-#define KVM_MAX_VCPUS 64
+#define KVM_S390_BSCA_CPU_SLOTS 64
+#define KVM_S390_ESCA_CPU_SLOTS 248
+#define KVM_MAX_VCPUS KVM_S390_ESCA_CPU_SLOTS
#define KVM_USER_MEM_SLOTS 32
/*
@@ -37,12 +39,41 @@
#define KVM_IRQCHIP_NUM_PINS 4096
#define KVM_HALT_POLL_NS_DEFAULT 0
+/* s390-specific vcpu->requests bit members */
+#define KVM_REQ_ENABLE_IBS 8
+#define KVM_REQ_DISABLE_IBS 9
+
#define SIGP_CTRL_C 0x80
#define SIGP_CTRL_SCN_MASK 0x3f
-struct sca_entry {
+union bsca_sigp_ctrl {
+ __u8 value;
+ struct {
+ __u8 c : 1;
+ __u8 r : 1;
+ __u8 scn : 6;
+ };
+} __packed;
+
+union esca_sigp_ctrl {
+ __u16 value;
+ struct {
+ __u8 c : 1;
+ __u8 reserved: 7;
+ __u8 scn;
+ };
+} __packed;
+
+struct esca_entry {
+ union esca_sigp_ctrl sigp_ctrl;
+ __u16 reserved1[3];
+ __u64 sda;
+ __u64 reserved2[6];
+} __packed;
+
+struct bsca_entry {
__u8 reserved0;
- __u8 sigp_ctrl;
+ union bsca_sigp_ctrl sigp_ctrl;
__u16 reserved[3];
__u64 sda;
__u64 reserved2[2];
@@ -57,14 +88,22 @@ union ipte_control {
};
};
-struct sca_block {
+struct bsca_block {
union ipte_control ipte_control;
__u64 reserved[5];
__u64 mcn;
__u64 reserved2;
- struct sca_entry cpu[64];
+ struct bsca_entry cpu[KVM_S390_BSCA_CPU_SLOTS];
} __attribute__((packed));
+struct esca_block {
+ union ipte_control ipte_control;
+ __u64 reserved1[7];
+ __u64 mcn[4];
+ __u64 reserved2[20];
+ struct esca_entry cpu[KVM_S390_ESCA_CPU_SLOTS];
+} __packed;
+
#define CPUSTAT_STOPPED 0x80000000
#define CPUSTAT_WAIT 0x10000000
#define CPUSTAT_ECALL_PEND 0x08000000
@@ -182,7 +221,8 @@ struct kvm_s390_sie_block {
__u64 pp; /* 0x01de */
__u8 reserved1e6[2]; /* 0x01e6 */
__u64 itdba; /* 0x01e8 */
- __u8 reserved1f0[16]; /* 0x01f0 */
+ __u64 riccbd; /* 0x01f0 */
+ __u8 reserved1f8[8]; /* 0x01f8 */
} __attribute__((packed));
struct kvm_s390_itdb {
@@ -585,11 +625,14 @@ struct kvm_s390_crypto_cb {
};
struct kvm_arch{
- struct sca_block *sca;
+ void *sca;
+ int use_esca;
+ rwlock_t sca_lock;
debug_info_t *dbf;
struct kvm_s390_float_interrupt float_int;
struct kvm_device *flic;
struct gmap *gmap;
+ unsigned long mem_limit;
int css_support;
int use_irqchip;
int use_cmma;
diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h
index afe1cfebf1a4..d79ba7cf75b0 100644
--- a/arch/s390/include/asm/lowcore.h
+++ b/arch/s390/include/asm/lowcore.h
@@ -16,28 +16,7 @@
#define LC_ORDER 1
#define LC_PAGES 2
-struct save_area {
- u64 fp_regs[16];
- u64 gp_regs[16];
- u8 psw[16];
- u8 pad1[8];
- u32 pref_reg;
- u32 fp_ctrl_reg;
- u8 pad2[4];
- u32 tod_reg;
- u64 timer;
- u64 clk_cmp;
- u8 pad3[8];
- u32 acc_regs[16];
- u64 ctrl_regs[16];
-} __packed;
-
-struct save_area_ext {
- struct save_area sa;
- __vector128 vx_regs[32];
-};
-
-struct _lowcore {
+struct lowcore {
__u8 pad_0x0000[0x0014-0x0000]; /* 0x0000 */
__u32 ipl_parmblock_ptr; /* 0x0014 */
__u8 pad_0x0018[0x0080-0x0018]; /* 0x0018 */
@@ -204,9 +183,9 @@ struct _lowcore {
__u8 vector_save_area[1024]; /* 0x1c00 */
} __packed;
-#define S390_lowcore (*((struct _lowcore *) 0))
+#define S390_lowcore (*((struct lowcore *) 0))
-extern struct _lowcore *lowcore_ptr[];
+extern struct lowcore *lowcore_ptr[];
static inline void set_prefix(__u32 address)
{
diff --git a/arch/s390/include/asm/os_info.h b/arch/s390/include/asm/os_info.h
index 295f2c4f1c96..943475382d51 100644
--- a/arch/s390/include/asm/os_info.h
+++ b/arch/s390/include/asm/os_info.h
@@ -38,7 +38,7 @@ u32 os_info_csum(struct os_info *os_info);
#ifdef CONFIG_CRASH_DUMP
void *os_info_old_entry(int nr, unsigned long *size);
-int copy_from_oldmem(void *dest, void *src, size_t count);
+int copy_oldmem_kernel(void *dst, void *src, size_t count);
#else
static inline void *os_info_old_entry(int nr, unsigned long *size)
{
diff --git a/arch/s390/include/asm/pci_dma.h b/arch/s390/include/asm/pci_dma.h
index 1aac41e83ea1..92df3eb8d14e 100644
--- a/arch/s390/include/asm/pci_dma.h
+++ b/arch/s390/include/asm/pci_dma.h
@@ -23,6 +23,8 @@ enum zpci_ioat_dtype {
#define ZPCI_IOTA_FS_2G 2
#define ZPCI_KEY (PAGE_DEFAULT_KEY << 5)
+#define ZPCI_TABLE_SIZE_RT (1UL << 42)
+
#define ZPCI_IOTA_STO_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_ST)
#define ZPCI_IOTA_RTTO_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_RT)
#define ZPCI_IOTA_RSTO_FLAG (ZPCI_IOTA_IOT_ENABLED | ZPCI_KEY | ZPCI_IOTA_DT_RS)
diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h
index b16c3d0a1b9f..f16debf6a612 100644
--- a/arch/s390/include/asm/processor.h
+++ b/arch/s390/include/asm/processor.h
@@ -18,12 +18,14 @@
#define CIF_NOHZ_DELAY 2 /* delay HZ disable for a tick */
#define CIF_FPU 3 /* restore FPU registers */
#define CIF_IGNORE_IRQ 4 /* ignore interrupt (for udelay) */
+#define CIF_ENABLED_WAIT 5 /* in enabled wait state */
#define _CIF_MCCK_PENDING _BITUL(CIF_MCCK_PENDING)
#define _CIF_ASCE _BITUL(CIF_ASCE)
#define _CIF_NOHZ_DELAY _BITUL(CIF_NOHZ_DELAY)
#define _CIF_FPU _BITUL(CIF_FPU)
#define _CIF_IGNORE_IRQ _BITUL(CIF_IGNORE_IRQ)
+#define _CIF_ENABLED_WAIT _BITUL(CIF_ENABLED_WAIT)
#ifndef __ASSEMBLY__
@@ -52,6 +54,16 @@ static inline int test_cpu_flag(int flag)
return !!(S390_lowcore.cpu_flags & (1UL << flag));
}
+/*
+ * Test CIF flag of another CPU. The caller needs to ensure that
+ * CPU hotplug can not happen, e.g. by disabling preemption.
+ */
+static inline int test_cpu_flag_of(int flag, int cpu)
+{
+ struct lowcore *lc = lowcore_ptr[cpu];
+ return !!(lc->cpu_flags & (1UL << flag));
+}
+
#define arch_needs_cpu() test_cpu_flag(CIF_NOHZ_DELAY)
/*
diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h
index 37cbc50947f2..f00cd35c8ac4 100644
--- a/arch/s390/include/asm/ptrace.h
+++ b/arch/s390/include/asm/ptrace.h
@@ -24,25 +24,25 @@
PSW_MASK_PSTATE | PSW_ASC_PRIMARY)
struct psw_bits {
- unsigned long long : 1;
- unsigned long long r : 1; /* PER-Mask */
- unsigned long long : 3;
- unsigned long long t : 1; /* DAT Mode */
- unsigned long long i : 1; /* Input/Output Mask */
- unsigned long long e : 1; /* External Mask */
- unsigned long long key : 4; /* PSW Key */
- unsigned long long : 1;
- unsigned long long m : 1; /* Machine-Check Mask */
- unsigned long long w : 1; /* Wait State */
- unsigned long long p : 1; /* Problem State */
- unsigned long long as : 2; /* Address Space Control */
- unsigned long long cc : 2; /* Condition Code */
- unsigned long long pm : 4; /* Program Mask */
- unsigned long long ri : 1; /* Runtime Instrumentation */
- unsigned long long : 6;
- unsigned long long eaba : 2; /* Addressing Mode */
- unsigned long long : 31;
- unsigned long long ia : 64;/* Instruction Address */
+ unsigned long : 1;
+ unsigned long r : 1; /* PER-Mask */
+ unsigned long : 3;
+ unsigned long t : 1; /* DAT Mode */
+ unsigned long i : 1; /* Input/Output Mask */
+ unsigned long e : 1; /* External Mask */
+ unsigned long key : 4; /* PSW Key */
+ unsigned long : 1;
+ unsigned long m : 1; /* Machine-Check Mask */
+ unsigned long w : 1; /* Wait State */
+ unsigned long p : 1; /* Problem State */
+ unsigned long as : 2; /* Address Space Control */
+ unsigned long cc : 2; /* Condition Code */
+ unsigned long pm : 4; /* Program Mask */
+ unsigned long ri : 1; /* Runtime Instrumentation */
+ unsigned long : 6;
+ unsigned long eaba : 2; /* Addressing Mode */
+ unsigned long : 31;
+ unsigned long ia : 64; /* Instruction Address */
};
enum {
diff --git a/arch/s390/include/asm/reset.h b/arch/s390/include/asm/reset.h
index 72786067b300..fe11fa88a0e0 100644
--- a/arch/s390/include/asm/reset.h
+++ b/arch/s390/include/asm/reset.h
@@ -15,6 +15,5 @@ struct reset_call {
extern void register_reset_call(struct reset_call *reset);
extern void unregister_reset_call(struct reset_call *reset);
-extern void s390_reset_system(void (*fn_pre)(void),
- void (*fn_post)(void *), void *data);
+extern void s390_reset_system(void);
#endif /* _ASM_S390_RESET_H */
diff --git a/arch/s390/include/asm/sclp.h b/arch/s390/include/asm/sclp.h
index 821dde5f425d..bab456be9a4f 100644
--- a/arch/s390/include/asm/sclp.h
+++ b/arch/s390/include/asm/sclp.h
@@ -29,7 +29,10 @@ struct sclp_ipl_info {
struct sclp_core_entry {
u8 core_id;
- u8 reserved0[2];
+ u8 reserved0;
+ u8 : 4;
+ u8 sief2 : 1;
+ u8 : 3;
u8 : 3;
u8 siif : 1;
u8 sigpif : 1;
@@ -53,16 +56,19 @@ struct sclp_info {
unsigned char has_sigpif : 1;
unsigned char has_core_type : 1;
unsigned char has_sprp : 1;
+ unsigned char has_hvs : 1;
+ unsigned char has_esca : 1;
+ unsigned char has_sief2 : 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 long rzm;
+ unsigned long rnmax;
+ unsigned long hamax;
unsigned int max_cores;
unsigned long hsa_size;
- unsigned long long facilities;
+ unsigned long facilities;
};
extern struct sclp_info sclp;
@@ -77,8 +83,9 @@ int sclp_chp_read_info(struct sclp_chp_info *info);
void sclp_get_ipl_info(struct sclp_ipl_info *info);
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);
+int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count);
+int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count);
void sclp_early_detect(void);
-int _sclp_print_early(const char *);
+void _sclp_print_early(const char *);
#endif /* _ASM_S390_SCLP_H */
diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h
index 23537661da0e..69837225119e 100644
--- a/arch/s390/include/asm/setup.h
+++ b/arch/s390/include/asm/setup.h
@@ -12,27 +12,24 @@
#define PARMAREA 0x10400
/*
- * Machine features detected in head.S
+ * Machine features detected in early.c
*/
#define MACHINE_FLAG_VM _BITUL(0)
-#define MACHINE_FLAG_IEEE _BITUL(1)
-#define MACHINE_FLAG_CSP _BITUL(2)
-#define MACHINE_FLAG_MVPG _BITUL(3)
-#define MACHINE_FLAG_DIAG44 _BITUL(4)
+#define MACHINE_FLAG_KVM _BITUL(1)
+#define MACHINE_FLAG_LPAR _BITUL(2)
+#define MACHINE_FLAG_DIAG9C _BITUL(3)
+#define MACHINE_FLAG_ESOP _BITUL(4)
#define MACHINE_FLAG_IDTE _BITUL(5)
-#define MACHINE_FLAG_DIAG9C _BITUL(6)
-#define MACHINE_FLAG_KVM _BITUL(8)
-#define MACHINE_FLAG_ESOP _BITUL(9)
-#define MACHINE_FLAG_EDAT1 _BITUL(10)
-#define MACHINE_FLAG_EDAT2 _BITUL(11)
-#define MACHINE_FLAG_LPAR _BITUL(12)
-#define MACHINE_FLAG_LPP _BITUL(13)
-#define MACHINE_FLAG_TOPOLOGY _BITUL(14)
-#define MACHINE_FLAG_TE _BITUL(15)
-#define MACHINE_FLAG_TLB_LC _BITUL(17)
-#define MACHINE_FLAG_VX _BITUL(18)
-#define MACHINE_FLAG_CAD _BITUL(19)
+#define MACHINE_FLAG_DIAG44 _BITUL(6)
+#define MACHINE_FLAG_EDAT1 _BITUL(7)
+#define MACHINE_FLAG_EDAT2 _BITUL(8)
+#define MACHINE_FLAG_LPP _BITUL(9)
+#define MACHINE_FLAG_TOPOLOGY _BITUL(10)
+#define MACHINE_FLAG_TE _BITUL(11)
+#define MACHINE_FLAG_TLB_LC _BITUL(12)
+#define MACHINE_FLAG_VX _BITUL(13)
+#define MACHINE_FLAG_CAD _BITUL(14)
#define LPP_MAGIC _BITUL(31)
#define LPP_PFAULT_PID_MASK _AC(0xffffffff, UL)
diff --git a/arch/s390/include/asm/smp.h b/arch/s390/include/asm/smp.h
index 5df26b11cf47..0cc383b9be7f 100644
--- a/arch/s390/include/asm/smp.h
+++ b/arch/s390/include/asm/smp.h
@@ -18,6 +18,7 @@
extern struct mutex smp_cpu_state_mutex;
extern unsigned int smp_cpu_mt_shift;
extern unsigned int smp_cpu_mtid;
+extern __vector128 __initdata boot_cpu_vector_save_area[__NUM_VXRS];
extern int __cpu_up(unsigned int cpu, struct task_struct *tidle);
@@ -55,7 +56,6 @@ static inline int smp_store_status(int cpu) { return 0; }
static inline int smp_vcpu_scheduled(int cpu) { return 1; }
static inline void smp_yield_cpu(int cpu) { }
static inline void smp_fill_possible_mask(void) { }
-static inline void smp_save_dump_cpus(void) { }
#endif /* CONFIG_SMP */
diff --git a/arch/s390/include/asm/sysinfo.h b/arch/s390/include/asm/sysinfo.h
index f7054a892d9e..2728114d5484 100644
--- a/arch/s390/include/asm/sysinfo.h
+++ b/arch/s390/include/asm/sysinfo.h
@@ -56,7 +56,12 @@ struct sysinfo_1_2_2 {
char format;
char reserved_0[1];
unsigned short acc_offset;
- char reserved_1[20];
+ unsigned char mt_installed :1;
+ unsigned char :2;
+ unsigned char mt_stid :5;
+ unsigned char :3;
+ unsigned char mt_gtid :5;
+ char reserved_1[18];
unsigned int nominal_cap;
unsigned int secondary_cap;
unsigned int capability;
@@ -92,9 +97,13 @@ struct sysinfo_2_2_2 {
char name[8];
unsigned int caf;
char reserved_2[8];
- unsigned char mt_installed;
- unsigned char mt_general;
- unsigned char mt_psmtid;
+ unsigned char mt_installed :1;
+ unsigned char :2;
+ unsigned char mt_stid :5;
+ unsigned char :3;
+ unsigned char mt_gtid :5;
+ unsigned char :3;
+ unsigned char mt_psmtid :5;
char reserved_3[5];
unsigned short cpus_dedicated;
unsigned short cpus_shared;
diff --git a/arch/s390/include/asm/thread_info.h b/arch/s390/include/asm/thread_info.h
index 692b9247c019..2fffc2c27581 100644
--- a/arch/s390/include/asm/thread_info.h
+++ b/arch/s390/include/asm/thread_info.h
@@ -96,6 +96,4 @@ void arch_release_task_struct(struct task_struct *tsk);
#define _TIF_31BIT _BITUL(TIF_31BIT)
#define _TIF_SINGLE_STEP _BITUL(TIF_SINGLE_STEP)
-#define is_32bit_task() (test_thread_flag(TIF_31BIT))
-
#endif /* _ASM_THREAD_INFO_H */
diff --git a/arch/s390/include/asm/topology.h b/arch/s390/include/asm/topology.h
index 94fc55fc72ce..6b53962e807e 100644
--- a/arch/s390/include/asm/topology.h
+++ b/arch/s390/include/asm/topology.h
@@ -7,7 +7,7 @@
struct sysinfo_15_1_x;
struct cpu;
-#ifdef CONFIG_SCHED_BOOK
+#ifdef CONFIG_SCHED_TOPOLOGY
struct cpu_topology_s390 {
unsigned short thread_id;
@@ -40,13 +40,13 @@ void store_topology(struct sysinfo_15_1_x *info);
void topology_expect_change(void);
const struct cpumask *cpu_coregroup_mask(int cpu);
-#else /* CONFIG_SCHED_BOOK */
+#else /* CONFIG_SCHED_TOPOLOGY */
static inline void topology_schedule_update(void) { }
static inline int topology_cpu_init(struct cpu *cpu) { return 0; }
static inline void topology_expect_change(void) { }
-#endif /* CONFIG_SCHED_BOOK */
+#endif /* CONFIG_SCHED_TOPOLOGY */
#define POLARIZATION_UNKNOWN (-1)
#define POLARIZATION_HRZ (0)
diff --git a/arch/s390/include/asm/vdso.h b/arch/s390/include/asm/vdso.h
index 787acd4f9668..d0a2dbf2433d 100644
--- a/arch/s390/include/asm/vdso.h
+++ b/arch/s390/include/asm/vdso.h
@@ -38,12 +38,14 @@ struct vdso_data {
struct vdso_per_cpu_data {
__u64 ectg_timer_base;
__u64 ectg_user_time;
+ __u32 cpu_nr;
+ __u32 node_id;
};
extern struct vdso_data *vdso_data;
-int vdso_alloc_per_cpu(struct _lowcore *lowcore);
-void vdso_free_per_cpu(struct _lowcore *lowcore);
+int vdso_alloc_per_cpu(struct lowcore *lowcore);
+void vdso_free_per_cpu(struct lowcore *lowcore);
#endif /* __ASSEMBLY__ */
#endif /* __S390_VDSO_H__ */
diff --git a/arch/s390/include/uapi/asm/kvm.h b/arch/s390/include/uapi/asm/kvm.h
index ef1a5fcc6c66..fe84bd5fe7ce 100644
--- a/arch/s390/include/uapi/asm/kvm.h
+++ b/arch/s390/include/uapi/asm/kvm.h
@@ -66,6 +66,8 @@ struct kvm_s390_io_adapter_req {
#define KVM_S390_VM_MEM_CLR_CMMA 1
#define KVM_S390_VM_MEM_LIMIT_SIZE 2
+#define KVM_S390_NO_MEM_LIMIT U64_MAX
+
/* kvm attributes for KVM_S390_VM_TOD */
#define KVM_S390_VM_TOD_LOW 0
#define KVM_S390_VM_TOD_HIGH 1
@@ -151,6 +153,7 @@ struct kvm_guest_debug_arch {
#define KVM_SYNC_ARCH0 (1UL << 4)
#define KVM_SYNC_PFAULT (1UL << 5)
#define KVM_SYNC_VRS (1UL << 6)
+#define KVM_SYNC_RICCB (1UL << 7)
/* definition of registers in kvm_run */
struct kvm_sync_regs {
__u64 prefix; /* prefix register */
@@ -168,6 +171,8 @@ struct kvm_sync_regs {
__u64 vrs[32][2]; /* vector registers */
__u8 reserved[512]; /* for future vector expansion */
__u32 fpc; /* only valid with vector registers */
+ __u8 padding[52]; /* riccb needs to be 64byte aligned */
+ __u8 riccb[64]; /* runtime instrumentation controls block */
};
#define KVM_REG_S390_TODPR (KVM_REG_S390 | KVM_REG_SIZE_U32 | 0x1)
diff --git a/arch/s390/include/uapi/asm/socket.h b/arch/s390/include/uapi/asm/socket.h
index 296942d56e6a..d02e89d14fef 100644
--- a/arch/s390/include/uapi/asm/socket.h
+++ b/arch/s390/include/uapi/asm/socket.h
@@ -91,4 +91,7 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* _ASM_SOCKET_H */
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile
index dc167a23b920..2f5586ab8a6a 100644
--- a/arch/s390/kernel/Makefile
+++ b/arch/s390/kernel/Makefile
@@ -34,8 +34,10 @@ CFLAGS_sysinfo.o += -w
#
CFLAGS_REMOVE_sclp.o = $(CC_FLAGS_FTRACE)
ifneq ($(CC_FLAGS_MARCH),-march=z900)
-CFLAGS_REMOVE_sclp.o += $(CC_FLAGS_MARCH)
-CFLAGS_sclp.o += -march=z900
+CFLAGS_REMOVE_sclp.o += $(CC_FLAGS_MARCH)
+CFLAGS_sclp.o += -march=z900
+AFLAGS_REMOVE_head.o += $(CC_FLAGS_MARCH)
+AFLAGS_head.o += -march=z900
endif
GCOV_PROFILE_sclp.o := n
@@ -50,7 +52,7 @@ extra-y += head.o head64.o vmlinux.lds
obj-$(CONFIG_MODULES) += s390_ksyms.o module.o
obj-$(CONFIG_SMP) += smp.o
-obj-$(CONFIG_SCHED_BOOK) += topology.o
+obj-$(CONFIG_SCHED_TOPOLOGY) += topology.o
obj-$(CONFIG_HIBERNATION) += suspend.o swsusp.o
obj-$(CONFIG_AUDIT) += audit.o
compat-obj-$(CONFIG_AUDIT) += compat_audit.o
diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c
index 9cd248f637c7..53bbc9e8b281 100644
--- a/arch/s390/kernel/asm-offsets.c
+++ b/arch/s390/kernel/asm-offsets.c
@@ -80,6 +80,8 @@ int main(void)
OFFSET(__VDSO_TK_SHIFT, vdso_data, tk_shift);
OFFSET(__VDSO_ECTG_BASE, vdso_per_cpu_data, ectg_timer_base);
OFFSET(__VDSO_ECTG_USER, vdso_per_cpu_data, ectg_user_time);
+ OFFSET(__VDSO_CPU_NR, vdso_per_cpu_data, cpu_nr);
+ OFFSET(__VDSO_NODE_ID, vdso_per_cpu_data, node_id);
BLANK();
/* constants used by the vdso */
DEFINE(__CLOCK_REALTIME, CLOCK_REALTIME);
@@ -97,95 +99,96 @@ int main(void)
OFFSET(__TIMER_IDLE_EXIT, s390_idle_data, timer_idle_exit);
BLANK();
/* hardware defined lowcore locations 0x000 - 0x1ff */
- OFFSET(__LC_EXT_PARAMS, _lowcore, ext_params);
- OFFSET(__LC_EXT_CPU_ADDR, _lowcore, ext_cpu_addr);
- OFFSET(__LC_EXT_INT_CODE, _lowcore, ext_int_code);
- OFFSET(__LC_SVC_ILC, _lowcore, svc_ilc);
- OFFSET(__LC_SVC_INT_CODE, _lowcore, svc_code);
- OFFSET(__LC_PGM_ILC, _lowcore, pgm_ilc);
- OFFSET(__LC_PGM_INT_CODE, _lowcore, pgm_code);
- OFFSET(__LC_DATA_EXC_CODE, _lowcore, data_exc_code);
- OFFSET(__LC_MON_CLASS_NR, _lowcore, mon_class_num);
- OFFSET(__LC_PER_CODE, _lowcore, per_code);
- OFFSET(__LC_PER_ATMID, _lowcore, per_atmid);
- OFFSET(__LC_PER_ADDRESS, _lowcore, per_address);
- OFFSET(__LC_EXC_ACCESS_ID, _lowcore, exc_access_id);
- OFFSET(__LC_PER_ACCESS_ID, _lowcore, per_access_id);
- OFFSET(__LC_OP_ACCESS_ID, _lowcore, op_access_id);
- OFFSET(__LC_AR_MODE_ID, _lowcore, ar_mode_id);
- OFFSET(__LC_TRANS_EXC_CODE, _lowcore, trans_exc_code);
- OFFSET(__LC_MON_CODE, _lowcore, monitor_code);
- OFFSET(__LC_SUBCHANNEL_ID, _lowcore, subchannel_id);
- OFFSET(__LC_SUBCHANNEL_NR, _lowcore, subchannel_nr);
- OFFSET(__LC_IO_INT_PARM, _lowcore, io_int_parm);
- OFFSET(__LC_IO_INT_WORD, _lowcore, io_int_word);
- OFFSET(__LC_STFL_FAC_LIST, _lowcore, stfl_fac_list);
- OFFSET(__LC_MCCK_CODE, _lowcore, mcck_interruption_code);
- OFFSET(__LC_MCCK_FAIL_STOR_ADDR, _lowcore, failing_storage_address);
- OFFSET(__LC_LAST_BREAK, _lowcore, breaking_event_addr);
- OFFSET(__LC_RST_OLD_PSW, _lowcore, restart_old_psw);
- OFFSET(__LC_EXT_OLD_PSW, _lowcore, external_old_psw);
- OFFSET(__LC_SVC_OLD_PSW, _lowcore, svc_old_psw);
- OFFSET(__LC_PGM_OLD_PSW, _lowcore, program_old_psw);
- OFFSET(__LC_MCK_OLD_PSW, _lowcore, mcck_old_psw);
- OFFSET(__LC_IO_OLD_PSW, _lowcore, io_old_psw);
- OFFSET(__LC_RST_NEW_PSW, _lowcore, restart_psw);
- OFFSET(__LC_EXT_NEW_PSW, _lowcore, external_new_psw);
- OFFSET(__LC_SVC_NEW_PSW, _lowcore, svc_new_psw);
- OFFSET(__LC_PGM_NEW_PSW, _lowcore, program_new_psw);
- OFFSET(__LC_MCK_NEW_PSW, _lowcore, mcck_new_psw);
- OFFSET(__LC_IO_NEW_PSW, _lowcore, io_new_psw);
+ OFFSET(__LC_EXT_PARAMS, lowcore, ext_params);
+ OFFSET(__LC_EXT_CPU_ADDR, lowcore, ext_cpu_addr);
+ OFFSET(__LC_EXT_INT_CODE, lowcore, ext_int_code);
+ OFFSET(__LC_SVC_ILC, lowcore, svc_ilc);
+ OFFSET(__LC_SVC_INT_CODE, lowcore, svc_code);
+ OFFSET(__LC_PGM_ILC, lowcore, pgm_ilc);
+ OFFSET(__LC_PGM_INT_CODE, lowcore, pgm_code);
+ OFFSET(__LC_DATA_EXC_CODE, lowcore, data_exc_code);
+ OFFSET(__LC_MON_CLASS_NR, lowcore, mon_class_num);
+ OFFSET(__LC_PER_CODE, lowcore, per_code);
+ OFFSET(__LC_PER_ATMID, lowcore, per_atmid);
+ OFFSET(__LC_PER_ADDRESS, lowcore, per_address);
+ OFFSET(__LC_EXC_ACCESS_ID, lowcore, exc_access_id);
+ OFFSET(__LC_PER_ACCESS_ID, lowcore, per_access_id);
+ OFFSET(__LC_OP_ACCESS_ID, lowcore, op_access_id);
+ OFFSET(__LC_AR_MODE_ID, lowcore, ar_mode_id);
+ OFFSET(__LC_TRANS_EXC_CODE, lowcore, trans_exc_code);
+ OFFSET(__LC_MON_CODE, lowcore, monitor_code);
+ OFFSET(__LC_SUBCHANNEL_ID, lowcore, subchannel_id);
+ OFFSET(__LC_SUBCHANNEL_NR, lowcore, subchannel_nr);
+ OFFSET(__LC_IO_INT_PARM, lowcore, io_int_parm);
+ OFFSET(__LC_IO_INT_WORD, lowcore, io_int_word);
+ OFFSET(__LC_STFL_FAC_LIST, lowcore, stfl_fac_list);
+ OFFSET(__LC_STFLE_FAC_LIST, lowcore, stfle_fac_list);
+ OFFSET(__LC_MCCK_CODE, lowcore, mcck_interruption_code);
+ OFFSET(__LC_MCCK_FAIL_STOR_ADDR, lowcore, failing_storage_address);
+ OFFSET(__LC_LAST_BREAK, lowcore, breaking_event_addr);
+ OFFSET(__LC_RST_OLD_PSW, lowcore, restart_old_psw);
+ OFFSET(__LC_EXT_OLD_PSW, lowcore, external_old_psw);
+ OFFSET(__LC_SVC_OLD_PSW, lowcore, svc_old_psw);
+ OFFSET(__LC_PGM_OLD_PSW, lowcore, program_old_psw);
+ OFFSET(__LC_MCK_OLD_PSW, lowcore, mcck_old_psw);
+ OFFSET(__LC_IO_OLD_PSW, lowcore, io_old_psw);
+ OFFSET(__LC_RST_NEW_PSW, lowcore, restart_psw);
+ OFFSET(__LC_EXT_NEW_PSW, lowcore, external_new_psw);
+ OFFSET(__LC_SVC_NEW_PSW, lowcore, svc_new_psw);
+ OFFSET(__LC_PGM_NEW_PSW, lowcore, program_new_psw);
+ OFFSET(__LC_MCK_NEW_PSW, lowcore, mcck_new_psw);
+ OFFSET(__LC_IO_NEW_PSW, lowcore, io_new_psw);
/* software defined lowcore locations 0x200 - 0xdff*/
- OFFSET(__LC_SAVE_AREA_SYNC, _lowcore, save_area_sync);
- OFFSET(__LC_SAVE_AREA_ASYNC, _lowcore, save_area_async);
- OFFSET(__LC_SAVE_AREA_RESTART, _lowcore, save_area_restart);
- OFFSET(__LC_CPU_FLAGS, _lowcore, cpu_flags);
- OFFSET(__LC_RETURN_PSW, _lowcore, return_psw);
- OFFSET(__LC_RETURN_MCCK_PSW, _lowcore, return_mcck_psw);
- OFFSET(__LC_SYNC_ENTER_TIMER, _lowcore, sync_enter_timer);
- OFFSET(__LC_ASYNC_ENTER_TIMER, _lowcore, async_enter_timer);
- OFFSET(__LC_MCCK_ENTER_TIMER, _lowcore, mcck_enter_timer);
- OFFSET(__LC_EXIT_TIMER, _lowcore, exit_timer);
- OFFSET(__LC_USER_TIMER, _lowcore, user_timer);
- OFFSET(__LC_SYSTEM_TIMER, _lowcore, system_timer);
- OFFSET(__LC_STEAL_TIMER, _lowcore, steal_timer);
- OFFSET(__LC_LAST_UPDATE_TIMER, _lowcore, last_update_timer);
- OFFSET(__LC_LAST_UPDATE_CLOCK, _lowcore, last_update_clock);
- OFFSET(__LC_INT_CLOCK, _lowcore, int_clock);
- OFFSET(__LC_MCCK_CLOCK, _lowcore, mcck_clock);
- OFFSET(__LC_CURRENT, _lowcore, current_task);
- OFFSET(__LC_THREAD_INFO, _lowcore, thread_info);
- OFFSET(__LC_KERNEL_STACK, _lowcore, kernel_stack);
- OFFSET(__LC_ASYNC_STACK, _lowcore, async_stack);
- OFFSET(__LC_PANIC_STACK, _lowcore, panic_stack);
- OFFSET(__LC_RESTART_STACK, _lowcore, restart_stack);
- OFFSET(__LC_RESTART_FN, _lowcore, restart_fn);
- OFFSET(__LC_RESTART_DATA, _lowcore, restart_data);
- OFFSET(__LC_RESTART_SOURCE, _lowcore, restart_source);
- OFFSET(__LC_USER_ASCE, _lowcore, user_asce);
- OFFSET(__LC_LPP, _lowcore, lpp);
- OFFSET(__LC_CURRENT_PID, _lowcore, current_pid);
- OFFSET(__LC_PERCPU_OFFSET, _lowcore, percpu_offset);
- OFFSET(__LC_VDSO_PER_CPU, _lowcore, vdso_per_cpu_data);
- OFFSET(__LC_MACHINE_FLAGS, _lowcore, machine_flags);
- OFFSET(__LC_GMAP, _lowcore, gmap);
- OFFSET(__LC_PASTE, _lowcore, paste);
+ OFFSET(__LC_SAVE_AREA_SYNC, lowcore, save_area_sync);
+ OFFSET(__LC_SAVE_AREA_ASYNC, lowcore, save_area_async);
+ OFFSET(__LC_SAVE_AREA_RESTART, lowcore, save_area_restart);
+ OFFSET(__LC_CPU_FLAGS, lowcore, cpu_flags);
+ OFFSET(__LC_RETURN_PSW, lowcore, return_psw);
+ OFFSET(__LC_RETURN_MCCK_PSW, lowcore, return_mcck_psw);
+ OFFSET(__LC_SYNC_ENTER_TIMER, lowcore, sync_enter_timer);
+ OFFSET(__LC_ASYNC_ENTER_TIMER, lowcore, async_enter_timer);
+ OFFSET(__LC_MCCK_ENTER_TIMER, lowcore, mcck_enter_timer);
+ OFFSET(__LC_EXIT_TIMER, lowcore, exit_timer);
+ OFFSET(__LC_USER_TIMER, lowcore, user_timer);
+ OFFSET(__LC_SYSTEM_TIMER, lowcore, system_timer);
+ OFFSET(__LC_STEAL_TIMER, lowcore, steal_timer);
+ OFFSET(__LC_LAST_UPDATE_TIMER, lowcore, last_update_timer);
+ OFFSET(__LC_LAST_UPDATE_CLOCK, lowcore, last_update_clock);
+ OFFSET(__LC_INT_CLOCK, lowcore, int_clock);
+ OFFSET(__LC_MCCK_CLOCK, lowcore, mcck_clock);
+ OFFSET(__LC_CURRENT, lowcore, current_task);
+ OFFSET(__LC_THREAD_INFO, lowcore, thread_info);
+ OFFSET(__LC_KERNEL_STACK, lowcore, kernel_stack);
+ OFFSET(__LC_ASYNC_STACK, lowcore, async_stack);
+ OFFSET(__LC_PANIC_STACK, lowcore, panic_stack);
+ OFFSET(__LC_RESTART_STACK, lowcore, restart_stack);
+ OFFSET(__LC_RESTART_FN, lowcore, restart_fn);
+ OFFSET(__LC_RESTART_DATA, lowcore, restart_data);
+ OFFSET(__LC_RESTART_SOURCE, lowcore, restart_source);
+ OFFSET(__LC_USER_ASCE, lowcore, user_asce);
+ OFFSET(__LC_LPP, lowcore, lpp);
+ OFFSET(__LC_CURRENT_PID, lowcore, current_pid);
+ OFFSET(__LC_PERCPU_OFFSET, lowcore, percpu_offset);
+ OFFSET(__LC_VDSO_PER_CPU, lowcore, vdso_per_cpu_data);
+ OFFSET(__LC_MACHINE_FLAGS, lowcore, machine_flags);
+ OFFSET(__LC_GMAP, lowcore, gmap);
+ OFFSET(__LC_PASTE, lowcore, paste);
/* software defined ABI-relevant lowcore locations 0xe00 - 0xe20 */
- OFFSET(__LC_DUMP_REIPL, _lowcore, ipib);
+ OFFSET(__LC_DUMP_REIPL, lowcore, ipib);
/* hardware defined lowcore locations 0x1000 - 0x18ff */
- OFFSET(__LC_VX_SAVE_AREA_ADDR, _lowcore, vector_save_area_addr);
- OFFSET(__LC_EXT_PARAMS2, _lowcore, ext_params2);
- OFFSET(SAVE_AREA_BASE, _lowcore, floating_pt_save_area);
- OFFSET(__LC_FPREGS_SAVE_AREA, _lowcore, floating_pt_save_area);
- OFFSET(__LC_GPREGS_SAVE_AREA, _lowcore, gpregs_save_area);
- OFFSET(__LC_PSW_SAVE_AREA, _lowcore, psw_save_area);
- OFFSET(__LC_PREFIX_SAVE_AREA, _lowcore, prefixreg_save_area);
- OFFSET(__LC_FP_CREG_SAVE_AREA, _lowcore, fpt_creg_save_area);
- OFFSET(__LC_CPU_TIMER_SAVE_AREA, _lowcore, cpu_timer_save_area);
- OFFSET(__LC_CLOCK_COMP_SAVE_AREA, _lowcore, clock_comp_save_area);
- OFFSET(__LC_AREGS_SAVE_AREA, _lowcore, access_regs_save_area);
- OFFSET(__LC_CREGS_SAVE_AREA, _lowcore, cregs_save_area);
- OFFSET(__LC_PGM_TDB, _lowcore, pgm_tdb);
+ OFFSET(__LC_VX_SAVE_AREA_ADDR, lowcore, vector_save_area_addr);
+ OFFSET(__LC_EXT_PARAMS2, lowcore, ext_params2);
+ OFFSET(__LC_FPREGS_SAVE_AREA, lowcore, floating_pt_save_area);
+ OFFSET(__LC_GPREGS_SAVE_AREA, lowcore, gpregs_save_area);
+ OFFSET(__LC_PSW_SAVE_AREA, lowcore, psw_save_area);
+ OFFSET(__LC_PREFIX_SAVE_AREA, lowcore, prefixreg_save_area);
+ OFFSET(__LC_FP_CREG_SAVE_AREA, lowcore, fpt_creg_save_area);
+ OFFSET(__LC_TOD_PROGREG_SAVE_AREA, lowcore, tod_progreg_save_area);
+ OFFSET(__LC_CPU_TIMER_SAVE_AREA, lowcore, cpu_timer_save_area);
+ OFFSET(__LC_CLOCK_COMP_SAVE_AREA, lowcore, clock_comp_save_area);
+ OFFSET(__LC_AREGS_SAVE_AREA, lowcore, access_regs_save_area);
+ OFFSET(__LC_CREGS_SAVE_AREA, lowcore, cregs_save_area);
+ OFFSET(__LC_PGM_TDB, lowcore, pgm_tdb);
BLANK();
/* gmap/sie offsets */
OFFSET(__GMAP_ASCE, gmap, asce);
diff --git a/arch/s390/kernel/crash_dump.c b/arch/s390/kernel/crash_dump.c
index 171e09bb8ea2..a92b39fd0e63 100644
--- a/arch/s390/kernel/crash_dump.c
+++ b/arch/s390/kernel/crash_dump.c
@@ -13,6 +13,7 @@
#include <linux/slab.h>
#include <linux/bootmem.h>
#include <linux/elf.h>
+#include <asm/asm-offsets.h>
#include <linux/memblock.h>
#include <asm/os_info.h>
#include <asm/elf.h>
@@ -32,7 +33,84 @@ static struct memblock_type oldmem_type = {
.regions = &oldmem_region,
};
-struct dump_save_areas dump_save_areas;
+struct save_area {
+ struct list_head list;
+ u64 psw[2];
+ u64 ctrs[16];
+ u64 gprs[16];
+ u32 acrs[16];
+ u64 fprs[16];
+ u32 fpc;
+ u32 prefix;
+ u64 todpreg;
+ u64 timer;
+ u64 todcmp;
+ u64 vxrs_low[16];
+ __vector128 vxrs_high[16];
+};
+
+static LIST_HEAD(dump_save_areas);
+
+/*
+ * Allocate a save area
+ */
+struct save_area * __init save_area_alloc(bool is_boot_cpu)
+{
+ struct save_area *sa;
+
+ sa = (void *) memblock_alloc(sizeof(*sa), 8);
+ if (!sa)
+ return NULL;
+ if (is_boot_cpu)
+ list_add(&sa->list, &dump_save_areas);
+ else
+ list_add_tail(&sa->list, &dump_save_areas);
+ return sa;
+}
+
+/*
+ * Return the address of the save area for the boot CPU
+ */
+struct save_area * __init save_area_boot_cpu(void)
+{
+ if (list_empty(&dump_save_areas))
+ return NULL;
+ return list_first_entry(&dump_save_areas, struct save_area, list);
+}
+
+/*
+ * Copy CPU registers into the save area
+ */
+void __init save_area_add_regs(struct save_area *sa, void *regs)
+{
+ struct lowcore *lc;
+
+ lc = (struct lowcore *)(regs - __LC_FPREGS_SAVE_AREA);
+ memcpy(&sa->psw, &lc->psw_save_area, sizeof(sa->psw));
+ memcpy(&sa->ctrs, &lc->cregs_save_area, sizeof(sa->ctrs));
+ memcpy(&sa->gprs, &lc->gpregs_save_area, sizeof(sa->gprs));
+ memcpy(&sa->acrs, &lc->access_regs_save_area, sizeof(sa->acrs));
+ memcpy(&sa->fprs, &lc->floating_pt_save_area, sizeof(sa->fprs));
+ memcpy(&sa->fpc, &lc->fpt_creg_save_area, sizeof(sa->fpc));
+ memcpy(&sa->prefix, &lc->prefixreg_save_area, sizeof(sa->prefix));
+ memcpy(&sa->todpreg, &lc->tod_progreg_save_area, sizeof(sa->todpreg));
+ memcpy(&sa->timer, &lc->cpu_timer_save_area, sizeof(sa->timer));
+ memcpy(&sa->todcmp, &lc->clock_comp_save_area, sizeof(sa->todcmp));
+}
+
+/*
+ * Copy vector registers into the save area
+ */
+void __init save_area_add_vxrs(struct save_area *sa, __vector128 *vxrs)
+{
+ int i;
+
+ /* Copy lower halves of vector registers 0-15 */
+ for (i = 0; i < 16; i++)
+ memcpy(&sa->vxrs_low[i], &vxrs[i].u[2], 8);
+ /* Copy vector registers 16-31 */
+ memcpy(sa->vxrs_high, vxrs + 16, 16 * sizeof(__vector128));
+}
/*
* Return physical address for virtual address
@@ -51,79 +129,85 @@ static inline void *load_real_addr(void *addr)
}
/*
- * Copy real to virtual or real memory
- */
-static int copy_from_realmem(void *dest, void *src, size_t count)
-{
- unsigned long size;
-
- if (!count)
- return 0;
- if (!is_vmalloc_or_module_addr(dest))
- return memcpy_real(dest, src, count);
- do {
- size = min(count, PAGE_SIZE - (__pa(dest) & ~PAGE_MASK));
- if (memcpy_real(load_real_addr(dest), src, size))
- return -EFAULT;
- count -= size;
- dest += size;
- src += size;
- } while (count);
- return 0;
-}
-
-/*
- * Pointer to ELF header in new kernel
- */
-static void *elfcorehdr_newmem;
-
-/*
- * Copy one page from zfcpdump "oldmem"
- *
- * For pages below HSA size memory from the HSA is copied. Otherwise
- * real memory copy is used.
+ * Copy memory of the old, dumped system to a kernel space virtual address
*/
-static ssize_t copy_oldmem_page_zfcpdump(char *buf, size_t csize,
- unsigned long src, int userbuf)
+int copy_oldmem_kernel(void *dst, void *src, size_t count)
{
+ unsigned long from, len;
+ void *ra;
int rc;
- if (src < sclp.hsa_size) {
- rc = memcpy_hsa(buf, src, csize, userbuf);
- } else {
- if (userbuf)
- rc = copy_to_user_real((void __force __user *) buf,
- (void *) src, csize);
- else
- rc = memcpy_real(buf, (void *) src, csize);
+ while (count) {
+ from = __pa(src);
+ if (!OLDMEM_BASE && from < sclp.hsa_size) {
+ /* Copy from zfcpdump HSA area */
+ len = min(count, sclp.hsa_size - from);
+ rc = memcpy_hsa_kernel(dst, from, len);
+ if (rc)
+ return rc;
+ } else {
+ /* Check for swapped kdump oldmem areas */
+ if (OLDMEM_BASE && from - OLDMEM_BASE < OLDMEM_SIZE) {
+ from -= OLDMEM_BASE;
+ len = min(count, OLDMEM_SIZE - from);
+ } else if (OLDMEM_BASE && from < OLDMEM_SIZE) {
+ len = min(count, OLDMEM_SIZE - from);
+ from += OLDMEM_BASE;
+ } else {
+ len = count;
+ }
+ if (is_vmalloc_or_module_addr(dst)) {
+ ra = load_real_addr(dst);
+ len = min(PAGE_SIZE - offset_in_page(ra), len);
+ } else {
+ ra = dst;
+ }
+ if (memcpy_real(ra, (void *) from, len))
+ return -EFAULT;
+ }
+ dst += len;
+ src += len;
+ count -= len;
}
- return rc ? rc : csize;
+ return 0;
}
/*
- * Copy one page from kdump "oldmem"
- *
- * For the kdump reserved memory this functions performs a swap operation:
- * - [OLDMEM_BASE - OLDMEM_BASE + OLDMEM_SIZE] is mapped to [0 - OLDMEM_SIZE].
- * - [0 - OLDMEM_SIZE] is mapped to [OLDMEM_BASE - OLDMEM_BASE + OLDMEM_SIZE]
+ * Copy memory of the old, dumped system to a user space virtual address
*/
-static ssize_t copy_oldmem_page_kdump(char *buf, size_t csize,
- unsigned long src, int userbuf)
-
+int copy_oldmem_user(void __user *dst, void *src, size_t count)
{
+ unsigned long from, len;
int rc;
- if (src < OLDMEM_SIZE)
- src += OLDMEM_BASE;
- else if (src > OLDMEM_BASE &&
- src < OLDMEM_BASE + OLDMEM_SIZE)
- src -= OLDMEM_BASE;
- if (userbuf)
- rc = copy_to_user_real((void __force __user *) buf,
- (void *) src, csize);
- else
- rc = copy_from_realmem(buf, (void *) src, csize);
- return (rc == 0) ? rc : csize;
+ while (count) {
+ from = __pa(src);
+ if (!OLDMEM_BASE && from < sclp.hsa_size) {
+ /* Copy from zfcpdump HSA area */
+ len = min(count, sclp.hsa_size - from);
+ rc = memcpy_hsa_user(dst, from, len);
+ if (rc)
+ return rc;
+ } else {
+ /* Check for swapped kdump oldmem areas */
+ if (OLDMEM_BASE && from - OLDMEM_BASE < OLDMEM_SIZE) {
+ from -= OLDMEM_BASE;
+ len = min(count, OLDMEM_SIZE - from);
+ } else if (OLDMEM_BASE && from < OLDMEM_SIZE) {
+ len = min(count, OLDMEM_SIZE - from);
+ from += OLDMEM_BASE;
+ } else {
+ len = count;
+ }
+ rc = copy_to_user_real(dst, (void *) from, count);
+ if (rc)
+ return rc;
+ }
+ dst += len;
+ src += len;
+ count -= len;
+ }
+ return 0;
}
/*
@@ -132,15 +216,17 @@ static ssize_t copy_oldmem_page_kdump(char *buf, size_t csize,
ssize_t copy_oldmem_page(unsigned long pfn, char *buf, size_t csize,
unsigned long offset, int userbuf)
{
- unsigned long src;
+ void *src;
+ int rc;
if (!csize)
return 0;
- src = (pfn << PAGE_SHIFT) + offset;
- if (OLDMEM_BASE)
- return copy_oldmem_page_kdump(buf, csize, src, userbuf);
+ src = (void *) (pfn << PAGE_SHIFT) + offset;
+ if (userbuf)
+ rc = copy_oldmem_user((void __force __user *) buf, src, csize);
else
- return copy_oldmem_page_zfcpdump(buf, csize, src, userbuf);
+ rc = copy_oldmem_kernel((void *) buf, src, csize);
+ return rc;
}
/*
@@ -209,33 +295,6 @@ int remap_oldmem_pfn_range(struct vm_area_struct *vma, unsigned long from,
}
/*
- * Copy memory from old kernel
- */
-int copy_from_oldmem(void *dest, void *src, size_t count)
-{
- unsigned long copied = 0;
- int rc;
-
- if (OLDMEM_BASE) {
- if ((unsigned long) src < OLDMEM_SIZE) {
- copied = min(count, OLDMEM_SIZE - (unsigned long) src);
- rc = copy_from_realmem(dest, src + OLDMEM_BASE, copied);
- if (rc)
- return rc;
- }
- } else {
- 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);
- if (rc)
- return rc;
- }
- }
- return copy_from_realmem(dest + copied, src + copied, count - copied);
-}
-
-/*
* Alloc memory and panic in case of ENOMEM
*/
static void *kzalloc_panic(int len)
@@ -251,8 +310,8 @@ static void *kzalloc_panic(int len)
/*
* Initialize ELF note
*/
-static void *nt_init(void *buf, Elf64_Word type, void *desc, int d_len,
- const char *name)
+static void *nt_init_name(void *buf, Elf64_Word type, void *desc, int d_len,
+ const char *name)
{
Elf64_Nhdr *note;
u64 len;
@@ -272,136 +331,42 @@ static void *nt_init(void *buf, Elf64_Word type, void *desc, int d_len,
return PTR_ADD(buf, len);
}
-/*
- * Initialize prstatus note
- */
-static void *nt_prstatus(void *ptr, struct save_area *sa)
+static inline void *nt_init(void *buf, Elf64_Word type, void *desc, int d_len)
{
- struct elf_prstatus nt_prstatus;
- static int cpu_nr = 1;
-
- memset(&nt_prstatus, 0, sizeof(nt_prstatus));
- memcpy(&nt_prstatus.pr_reg.gprs, sa->gp_regs, sizeof(sa->gp_regs));
- memcpy(&nt_prstatus.pr_reg.psw, sa->psw, sizeof(sa->psw));
- memcpy(&nt_prstatus.pr_reg.acrs, sa->acc_regs, sizeof(sa->acc_regs));
- nt_prstatus.pr_pid = cpu_nr;
- cpu_nr++;
-
- return nt_init(ptr, NT_PRSTATUS, &nt_prstatus, sizeof(nt_prstatus),
- "CORE");
+ return nt_init_name(buf, type, desc, d_len, KEXEC_CORE_NOTE_NAME);
}
/*
- * Initialize fpregset (floating point) note
+ * Fill ELF notes for one CPU with save area registers
*/
-static void *nt_fpregset(void *ptr, struct save_area *sa)
+static void *fill_cpu_elf_notes(void *ptr, int cpu, struct save_area *sa)
{
+ struct elf_prstatus nt_prstatus;
elf_fpregset_t nt_fpregset;
+ /* Prepare prstatus note */
+ memset(&nt_prstatus, 0, sizeof(nt_prstatus));
+ memcpy(&nt_prstatus.pr_reg.gprs, sa->gprs, sizeof(sa->gprs));
+ memcpy(&nt_prstatus.pr_reg.psw, sa->psw, sizeof(sa->psw));
+ memcpy(&nt_prstatus.pr_reg.acrs, sa->acrs, sizeof(sa->acrs));
+ nt_prstatus.pr_pid = cpu;
+ /* Prepare fpregset (floating point) note */
memset(&nt_fpregset, 0, sizeof(nt_fpregset));
- memcpy(&nt_fpregset.fpc, &sa->fp_ctrl_reg, sizeof(sa->fp_ctrl_reg));
- memcpy(&nt_fpregset.fprs, &sa->fp_regs, sizeof(sa->fp_regs));
-
- return nt_init(ptr, NT_PRFPREG, &nt_fpregset, sizeof(nt_fpregset),
- "CORE");
-}
-
-/*
- * Initialize timer note
- */
-static void *nt_s390_timer(void *ptr, struct save_area *sa)
-{
- return nt_init(ptr, NT_S390_TIMER, &sa->timer, sizeof(sa->timer),
- KEXEC_CORE_NOTE_NAME);
-}
-
-/*
- * Initialize TOD clock comparator note
- */
-static void *nt_s390_tod_cmp(void *ptr, struct save_area *sa)
-{
- return nt_init(ptr, NT_S390_TODCMP, &sa->clk_cmp,
- sizeof(sa->clk_cmp), KEXEC_CORE_NOTE_NAME);
-}
-
-/*
- * Initialize TOD programmable register note
- */
-static void *nt_s390_tod_preg(void *ptr, struct save_area *sa)
-{
- return nt_init(ptr, NT_S390_TODPREG, &sa->tod_reg,
- sizeof(sa->tod_reg), KEXEC_CORE_NOTE_NAME);
-}
-
-/*
- * Initialize control register note
- */
-static void *nt_s390_ctrs(void *ptr, struct save_area *sa)
-{
- return nt_init(ptr, NT_S390_CTRS, &sa->ctrl_regs,
- sizeof(sa->ctrl_regs), KEXEC_CORE_NOTE_NAME);
-}
-
-/*
- * Initialize prefix register note
- */
-static void *nt_s390_prefix(void *ptr, struct save_area *sa)
-{
- return nt_init(ptr, NT_S390_PREFIX, &sa->pref_reg,
- sizeof(sa->pref_reg), KEXEC_CORE_NOTE_NAME);
-}
-
-/*
- * Initialize vxrs high note (full 128 bit VX registers 16-31)
- */
-static void *nt_s390_vx_high(void *ptr, __vector128 *vx_regs)
-{
- return nt_init(ptr, NT_S390_VXRS_HIGH, &vx_regs[16],
- 16 * sizeof(__vector128), KEXEC_CORE_NOTE_NAME);
-}
-
-/*
- * Initialize vxrs low note (lower halves of VX registers 0-15)
- */
-static void *nt_s390_vx_low(void *ptr, __vector128 *vx_regs)
-{
- Elf64_Nhdr *note;
- u64 len;
- int i;
-
- note = (Elf64_Nhdr *)ptr;
- note->n_namesz = strlen(KEXEC_CORE_NOTE_NAME) + 1;
- note->n_descsz = 16 * 8;
- note->n_type = NT_S390_VXRS_LOW;
- len = sizeof(Elf64_Nhdr);
-
- memcpy(ptr + len, KEXEC_CORE_NOTE_NAME, note->n_namesz);
- len = roundup(len + note->n_namesz, 4);
-
- ptr += len;
- /* Copy lower halves of SIMD registers 0-15 */
- for (i = 0; i < 16; i++) {
- memcpy(ptr, &vx_regs[i].u[2], 8);
- ptr += 8;
- }
- return ptr;
-}
-
-/*
- * Fill ELF notes for one CPU with save area registers
- */
-void *fill_cpu_elf_notes(void *ptr, struct save_area *sa, __vector128 *vx_regs)
-{
- ptr = nt_prstatus(ptr, sa);
- ptr = nt_fpregset(ptr, sa);
- ptr = nt_s390_timer(ptr, sa);
- ptr = nt_s390_tod_cmp(ptr, sa);
- ptr = nt_s390_tod_preg(ptr, sa);
- ptr = nt_s390_ctrs(ptr, sa);
- ptr = nt_s390_prefix(ptr, sa);
- if (MACHINE_HAS_VX && vx_regs) {
- ptr = nt_s390_vx_low(ptr, vx_regs);
- ptr = nt_s390_vx_high(ptr, vx_regs);
+ memcpy(&nt_fpregset.fpc, &sa->fpc, sizeof(sa->fpc));
+ memcpy(&nt_fpregset.fprs, &sa->fprs, sizeof(sa->fprs));
+ /* Create ELF notes for the CPU */
+ ptr = nt_init(ptr, NT_PRSTATUS, &nt_prstatus, sizeof(nt_prstatus));
+ ptr = nt_init(ptr, NT_PRFPREG, &nt_fpregset, sizeof(nt_fpregset));
+ ptr = nt_init(ptr, NT_S390_TIMER, &sa->timer, sizeof(sa->timer));
+ ptr = nt_init(ptr, NT_S390_TODCMP, &sa->todcmp, sizeof(sa->todcmp));
+ ptr = nt_init(ptr, NT_S390_TODPREG, &sa->todpreg, sizeof(sa->todpreg));
+ ptr = nt_init(ptr, NT_S390_CTRS, &sa->ctrs, sizeof(sa->ctrs));
+ ptr = nt_init(ptr, NT_S390_PREFIX, &sa->prefix, sizeof(sa->prefix));
+ if (MACHINE_HAS_VX) {
+ ptr = nt_init(ptr, NT_S390_VXRS_HIGH,
+ &sa->vxrs_high, sizeof(sa->vxrs_high));
+ ptr = nt_init(ptr, NT_S390_VXRS_LOW,
+ &sa->vxrs_low, sizeof(sa->vxrs_low));
}
return ptr;
}
@@ -416,8 +381,7 @@ static void *nt_prpsinfo(void *ptr)
memset(&prpsinfo, 0, sizeof(prpsinfo));
prpsinfo.pr_sname = 'R';
strcpy(prpsinfo.pr_fname, "vmlinux");
- return nt_init(ptr, NT_PRPSINFO, &prpsinfo, sizeof(prpsinfo),
- KEXEC_CORE_NOTE_NAME);
+ return nt_init(ptr, NT_PRPSINFO, &prpsinfo, sizeof(prpsinfo));
}
/*
@@ -429,17 +393,18 @@ static void *get_vmcoreinfo_old(unsigned long *size)
Elf64_Nhdr note;
void *addr;
- if (copy_from_oldmem(&addr, &S390_lowcore.vmcore_info, sizeof(addr)))
+ if (copy_oldmem_kernel(&addr, &S390_lowcore.vmcore_info, sizeof(addr)))
return NULL;
memset(nt_name, 0, sizeof(nt_name));
- if (copy_from_oldmem(&note, addr, sizeof(note)))
+ if (copy_oldmem_kernel(&note, addr, sizeof(note)))
return NULL;
- if (copy_from_oldmem(nt_name, addr + sizeof(note), sizeof(nt_name) - 1))
+ if (copy_oldmem_kernel(nt_name, addr + sizeof(note),
+ sizeof(nt_name) - 1))
return NULL;
if (strcmp(nt_name, "VMCOREINFO") != 0)
return NULL;
vmcoreinfo = kzalloc_panic(note.n_descsz);
- if (copy_from_oldmem(vmcoreinfo, addr + 24, note.n_descsz))
+ if (copy_oldmem_kernel(vmcoreinfo, addr + 24, note.n_descsz))
return NULL;
*size = note.n_descsz;
return vmcoreinfo;
@@ -458,7 +423,7 @@ static void *nt_vmcoreinfo(void *ptr)
vmcoreinfo = get_vmcoreinfo_old(&size);
if (!vmcoreinfo)
return ptr;
- return nt_init(ptr, 0, vmcoreinfo, size, "VMCOREINFO");
+ return nt_init_name(ptr, 0, vmcoreinfo, size, "VMCOREINFO");
}
/*
@@ -487,13 +452,12 @@ static void *ehdr_init(Elf64_Ehdr *ehdr, int mem_chunk_cnt)
*/
static int get_cpu_cnt(void)
{
- int i, cpus = 0;
+ struct save_area *sa;
+ int cpus = 0;
- for (i = 0; i < dump_save_areas.count; i++) {
- if (dump_save_areas.areas[i]->sa.pref_reg == 0)
- continue;
- cpus++;
- }
+ list_for_each_entry(sa, &dump_save_areas, list)
+ if (sa->prefix != 0)
+ cpus++;
return cpus;
}
@@ -538,18 +502,16 @@ static void loads_init(Elf64_Phdr *phdr, u64 loads_offset)
*/
static void *notes_init(Elf64_Phdr *phdr, void *ptr, u64 notes_offset)
{
- struct save_area_ext *sa_ext;
+ struct save_area *sa;
void *ptr_start = ptr;
- int i;
+ int cpu;
ptr = nt_prpsinfo(ptr);
- for (i = 0; i < dump_save_areas.count; i++) {
- sa_ext = dump_save_areas.areas[i];
- if (sa_ext->sa.pref_reg == 0)
- continue;
- ptr = fill_cpu_elf_notes(ptr, &sa_ext->sa, sa_ext->vx_regs);
- }
+ cpu = 1;
+ list_for_each_entry(sa, &dump_save_areas, list)
+ if (sa->prefix != 0)
+ ptr = fill_cpu_elf_notes(ptr, cpu++, sa);
ptr = nt_vmcoreinfo(ptr);
memset(phdr, 0, sizeof(*phdr));
phdr->p_type = PT_NOTE;
@@ -573,9 +535,6 @@ int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size)
/* If we are not in kdump or zfcpdump mode return */
if (!OLDMEM_BASE && ipl_info.type != IPL_TYPE_FCP_DUMP)
return 0;
- /* If elfcorehdr= has been passed via cmdline, we use that one */
- 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.hsa_size)
return -ENODEV;
@@ -606,7 +565,6 @@ int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size)
hdr_off = PTR_DIFF(ptr, hdr);
loads_init(phdr_loads, hdr_off);
*addr = (unsigned long long) hdr;
- elfcorehdr_newmem = hdr;
*size = (unsigned long long) hdr_off;
BUG_ON(elfcorehdr_size > alloc_size);
return 0;
@@ -617,8 +575,6 @@ int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size)
*/
void elfcorehdr_free(unsigned long long addr)
{
- if (!elfcorehdr_newmem)
- return;
kfree((void *)(unsigned long)addr);
}
@@ -629,7 +585,6 @@ ssize_t elfcorehdr_read(char *buf, size_t count, u64 *ppos)
{
void *src = (void *)(unsigned long)*ppos;
- src = elfcorehdr_newmem ? src : src - OLDMEM_BASE;
memcpy(buf, src, count);
*ppos += count;
return count;
@@ -641,15 +596,8 @@ ssize_t elfcorehdr_read(char *buf, size_t count, u64 *ppos)
ssize_t elfcorehdr_read_notes(char *buf, size_t count, u64 *ppos)
{
void *src = (void *)(unsigned long)*ppos;
- int rc;
- if (elfcorehdr_newmem) {
- memcpy(buf, src, count);
- } else {
- rc = copy_from_oldmem(buf, src, count);
- if (rc)
- return rc;
- }
+ memcpy(buf, src, count);
*ppos += count;
return count;
}
diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c
index 8140d10c6785..62973efd214a 100644
--- a/arch/s390/kernel/dis.c
+++ b/arch/s390/kernel/dis.c
@@ -1920,16 +1920,23 @@ static int print_insn(char *buffer, unsigned char *code, unsigned long addr)
}
if (separator)
ptr += sprintf(ptr, "%c", separator);
+ /*
+ * Use four '%' characters below because of the
+ * following two conversions:
+ *
+ * 1) sprintf: %%%%r -> %%r
+ * 2) printk : %%r -> %r
+ */
if (operand->flags & OPERAND_GPR)
- ptr += sprintf(ptr, "%%r%i", value);
+ ptr += sprintf(ptr, "%%%%r%i", value);
else if (operand->flags & OPERAND_FPR)
- ptr += sprintf(ptr, "%%f%i", value);
+ ptr += sprintf(ptr, "%%%%f%i", value);
else if (operand->flags & OPERAND_AR)
- ptr += sprintf(ptr, "%%a%i", value);
+ ptr += sprintf(ptr, "%%%%a%i", value);
else if (operand->flags & OPERAND_CR)
- ptr += sprintf(ptr, "%%c%i", value);
+ ptr += sprintf(ptr, "%%%%c%i", value);
else if (operand->flags & OPERAND_VR)
- ptr += sprintf(ptr, "%%v%i", value);
+ ptr += sprintf(ptr, "%%%%v%i", value);
else if (operand->flags & OPERAND_PCREL)
ptr += sprintf(ptr, "%lx", (signed int) value
+ addr);
@@ -2015,7 +2022,7 @@ void show_code(struct pt_regs *regs)
*ptr++ = '\t';
ptr += print_insn(ptr, code + start, addr);
start += opsize;
- printk(buffer);
+ printk("%s", buffer);
ptr = buffer;
ptr += sprintf(ptr, "\n ");
hops++;
@@ -2042,7 +2049,7 @@ void print_fn_code(unsigned char *code, unsigned long len)
ptr += print_insn(ptr, code, (unsigned long) code);
*ptr++ = '\n';
*ptr++ = 0;
- printk(buffer);
+ printk("%s", buffer);
code += opsize;
len -= opsize;
}
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index 3c31609df959..20a5caf6d981 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -335,6 +335,14 @@ static __init void detect_machine_facilities(void)
}
}
+static inline void save_vector_registers(void)
+{
+#ifdef CONFIG_CRASH_DUMP
+ if (test_facility(129))
+ save_vx_regs(boot_cpu_vector_save_area);
+#endif
+}
+
static int __init disable_vector_extension(char *str)
{
S390_lowcore.machine_flags &= ~MACHINE_FLAG_VX;
@@ -451,6 +459,7 @@ void __init startup_init(void)
detect_diag9c();
detect_diag44();
detect_machine_facilities();
+ save_vector_registers();
setup_topology();
sclp_early_detect();
lockdep_on();
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S
index 857b6526d298..cd5a191381b9 100644
--- a/arch/s390/kernel/entry.S
+++ b/arch/s390/kernel/entry.S
@@ -764,6 +764,7 @@ ENTRY(psw_idle)
.insn rsy,0xeb0000000017,%r1,5,__SF_EMPTY+16(%r15)
.Lpsw_idle_stcctm:
#endif
+ oi __LC_CPU_FLAGS+7,_CIF_ENABLED_WAIT
STCK __CLOCK_IDLE_ENTER(%r2)
stpt __TIMER_IDLE_ENTER(%r2)
.Lpsw_idle_lpsw:
@@ -1146,6 +1147,7 @@ cleanup_critical:
.quad .Lio_done - 4
.Lcleanup_idle:
+ ni __LC_CPU_FLAGS+7,255-_CIF_ENABLED_WAIT
# copy interrupt clock & cpu timer
mvc __CLOCK_IDLE_EXIT(8,%r2),__LC_INT_CLOCK
mvc __TIMER_IDLE_EXIT(8,%r2),__LC_ASYNC_ENTER_TIMER
diff --git a/arch/s390/kernel/head.S b/arch/s390/kernel/head.S
index 301ee9c70688..fcaefb041364 100644
--- a/arch/s390/kernel/head.S
+++ b/arch/s390/kernel/head.S
@@ -25,6 +25,7 @@
#include <linux/linkage.h>
#include <asm/asm-offsets.h>
#include <asm/thread_info.h>
+#include <asm/facility.h>
#include <asm/page.h>
#include <asm/ptrace.h>
@@ -300,27 +301,27 @@ ENTRY(startup_kdump)
xc 0x200(256),0x200 # partially clear lowcore
xc 0x300(256),0x300
xc 0xe00(256),0xe00
+ xc 0xf00(256),0xf00
lctlg %c0,%c15,0x200(%r0) # initialize control registers
stck __LC_LAST_UPDATE_CLOCK
spt 6f-.LPG0(%r13)
mvc __LC_LAST_UPDATE_TIMER(8),6f-.LPG0(%r13)
- xc __LC_STFL_FAC_LIST(8),__LC_STFL_FAC_LIST
- # check capabilities against MARCH_{G5,Z900,Z990,Z9_109,Z10}
- .insn s,0xb2b10000,0 # store facilities @ __LC_STFL_FAC_LIST
- tm __LC_STFL_FAC_LIST,0x01 # stfle available ?
+ stfl 0(%r0) # store facilities @ __LC_STFL_FAC_LIST
+ mvc __LC_STFLE_FAC_LIST(4),__LC_STFL_FAC_LIST
+ tm __LC_STFLE_FAC_LIST,0x01 # stfle available ?
jz 0f
- la %r0,1
- .insn s,0xb2b00000,__LC_STFL_FAC_LIST # store facility list extended
+ lghi %r0,FACILITIES_ALS_DWORDS-1
+ .insn s,0xb2b00000,__LC_STFLE_FAC_LIST # store facility list extended
# verify if all required facilities are supported by the machine
-0: la %r1,__LC_STFL_FAC_LIST
+0: la %r1,__LC_STFLE_FAC_LIST
la %r2,3f+8-.LPG0(%r13)
- l %r3,0(%r2)
-1: l %r0,0(%r1)
- n %r0,4(%r2)
- cl %r0,4(%r2)
+ lhi %r3,FACILITIES_ALS_DWORDS
+1: lg %r0,0(%r1)
+ ng %r0,0(%r2)
+ clg %r0,0(%r2)
jne 2f
- la %r1,4(%r1)
- la %r2,4(%r2)
+ la %r1,8(%r1)
+ la %r2,8(%r2)
ahi %r3,-1
jnz 1b
j 4f
@@ -340,24 +341,10 @@ ENTRY(startup_kdump)
3: .long 0x000a0000,0x8badcccc
# List of facilities that are required. If not all facilities are present
-# the kernel will crash. Format is number of facility words with bits set,
-# followed by the facility words.
+# the kernel will crash.
+
+ .quad FACILITIES_ALS
-#if defined(CONFIG_MARCH_Z13)
- .long 2, 0xc100eff2, 0xf46cc800
-#elif defined(CONFIG_MARCH_ZEC12)
- .long 2, 0xc100eff2, 0xf46cc800
-#elif defined(CONFIG_MARCH_Z196)
- .long 2, 0xc100eff2, 0xf46c0000
-#elif defined(CONFIG_MARCH_Z10)
- .long 2, 0xc100eff2, 0xf0680000
-#elif defined(CONFIG_MARCH_Z9_109)
- .long 1, 0xc100efc2
-#elif defined(CONFIG_MARCH_Z990)
- .long 1, 0xc0002000
-#elif defined(CONFIG_MARCH_Z900)
- .long 1, 0xc0000000
-#endif
4:
/* Continue with startup code in head64.S */
jg startup_continue
diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S
index 58b719fa8067..c5febe84eba6 100644
--- a/arch/s390/kernel/head64.S
+++ b/arch/s390/kernel/head64.S
@@ -16,7 +16,7 @@
__HEAD
ENTRY(startup_continue)
- tm __LC_STFL_FAC_LIST+6,0x80 # LPP available ?
+ tm __LC_STFLE_FAC_LIST+6,0x80 # LPP available ?
jz 0f
xc __LC_LPP+1(7,0),__LC_LPP+1 # clear lpp and current_pid
mvi __LC_LPP,0x80 # and set LPP_MAGIC
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c
index b1f0a90f933b..0a5a6b661b93 100644
--- a/arch/s390/kernel/ipl.c
+++ b/arch/s390/kernel/ipl.c
@@ -2039,21 +2039,15 @@ static void do_reset_calls(void)
reset->fn();
}
-u32 dump_prefix_page;
-
-void s390_reset_system(void (*fn_pre)(void),
- void (*fn_post)(void *), void *data)
+void s390_reset_system(void)
{
- struct _lowcore *lc;
+ struct lowcore *lc;
- lc = (struct _lowcore *)(unsigned long) store_prefix();
+ lc = (struct lowcore *)(unsigned long) store_prefix();
/* Stack for interrupt/machine check handler */
lc->panic_stack = S390_lowcore.panic_stack;
- /* Save prefix page address for dump case */
- dump_prefix_page = (u32)(unsigned long) lc;
-
/* Disable prefixing */
set_prefix(0);
@@ -2077,14 +2071,5 @@ void s390_reset_system(void (*fn_pre)(void),
S390_lowcore.subchannel_id = 0;
S390_lowcore.subchannel_nr = 0;
- /* Store status at absolute zero */
- store_status();
-
- /* Call function before reset */
- if (fn_pre)
- fn_pre();
do_reset_calls();
- /* Call function after reset */
- if (fn_post)
- fn_post(data);
}
diff --git a/arch/s390/kernel/machine_kexec.c b/arch/s390/kernel/machine_kexec.c
index fb0901ec4306..2f1b7217c25c 100644
--- a/arch/s390/kernel/machine_kexec.c
+++ b/arch/s390/kernel/machine_kexec.c
@@ -35,46 +35,6 @@ extern const unsigned long long relocate_kernel_len;
#ifdef CONFIG_CRASH_DUMP
/*
- * Create ELF notes for one CPU
- */
-static void add_elf_notes(int cpu)
-{
- struct save_area *sa = (void *) 4608 + store_prefix();
- void *ptr;
-
- memcpy((void *) (4608UL + sa->pref_reg), sa, sizeof(*sa));
- ptr = (u64 *) per_cpu_ptr(crash_notes, cpu);
- ptr = fill_cpu_elf_notes(ptr, sa, NULL);
- memset(ptr, 0, sizeof(struct elf_note));
-}
-
-/*
- * Initialize CPU ELF notes
- */
-static void setup_regs(void)
-{
- unsigned long sa = S390_lowcore.prefixreg_save_area + SAVE_AREA_BASE;
- struct _lowcore *lc;
- int cpu, this_cpu;
-
- /* Get lowcore pointer from store status of this CPU (absolute zero) */
- lc = (struct _lowcore *)(unsigned long)S390_lowcore.prefixreg_save_area;
- this_cpu = smp_find_processor_id(stap());
- add_elf_notes(this_cpu);
- for_each_online_cpu(cpu) {
- if (cpu == this_cpu)
- continue;
- if (smp_store_status(cpu))
- continue;
- add_elf_notes(cpu);
- }
- if (MACHINE_HAS_VX)
- save_vx_regs_safe((void *) lc->vector_save_area_addr);
- /* Copy dump CPU store status info to absolute zero */
- memcpy((void *) SAVE_AREA_BASE, (void *) sa, sizeof(struct save_area));
-}
-
-/*
* PM notifier callback for kdump
*/
static int machine_kdump_pm_cb(struct notifier_block *nb, unsigned long action,
@@ -105,14 +65,66 @@ static int __init machine_kdump_pm_init(void)
arch_initcall(machine_kdump_pm_init);
/*
- * Start kdump: We expect here that a store status has been done on our CPU
+ * Reset the system, copy boot CPU registers to absolute zero,
+ * and jump to the kdump image
*/
static void __do_machine_kdump(void *image)
{
- int (*start_kdump)(int) = (void *)((struct kimage *) image)->start;
+ int (*start_kdump)(int);
+ unsigned long prefix;
+
+ /* store_status() saved the prefix register to lowcore */
+ prefix = (unsigned long) S390_lowcore.prefixreg_save_area;
+
+ /* Now do the reset */
+ s390_reset_system();
+
+ /*
+ * Copy dump CPU store status info to absolute zero.
+ * This need to be done *after* s390_reset_system set the
+ * prefix register of this CPU to zero
+ */
+ memcpy((void *) __LC_FPREGS_SAVE_AREA,
+ (void *)(prefix + __LC_FPREGS_SAVE_AREA), 512);
__load_psw_mask(PSW_MASK_BASE | PSW_DEFAULT_KEY | PSW_MASK_EA | PSW_MASK_BA);
+ start_kdump = (void *)((struct kimage *) image)->start;
start_kdump(1);
+
+ /* Die if start_kdump returns */
+ disabled_wait((unsigned long) __builtin_return_address(0));
+}
+
+/*
+ * Start kdump: create a LGR log entry, store status of all CPUs and
+ * branch to __do_machine_kdump.
+ */
+static noinline void __machine_kdump(void *image)
+{
+ int this_cpu, cpu;
+
+ lgr_info_log();
+ /* Get status of the other CPUs */
+ this_cpu = smp_find_processor_id(stap());
+ for_each_online_cpu(cpu) {
+ if (cpu == this_cpu)
+ continue;
+ if (smp_store_status(cpu))
+ continue;
+ }
+ /* Store status of the boot CPU */
+ if (MACHINE_HAS_VX)
+ save_vx_regs((void *) &S390_lowcore.vector_save_area);
+ /*
+ * To create a good backchain for this CPU in the dump store_status
+ * is passed the address of a function. The address is saved into
+ * the PSW save area of the boot CPU and the function is invoked as
+ * a tail call of store_status. The backchain in the dump will look
+ * like this:
+ * restart_int_handler -> __machine_kexec -> __do_machine_kdump
+ * The call to store_status() will not return.
+ */
+ store_status(__do_machine_kdump, image);
}
#endif
@@ -235,10 +247,14 @@ static void __do_machine_kexec(void *data)
relocate_kernel_t data_mover;
struct kimage *image = data;
+ s390_reset_system();
data_mover = (relocate_kernel_t) page_to_phys(image->control_code_page);
/* Call the moving routine */
(*data_mover)(&image->head, image->start);
+
+ /* Die if kexec returns */
+ disabled_wait((unsigned long) __builtin_return_address(0));
}
/*
@@ -251,14 +267,10 @@ static void __machine_kexec(void *data)
tracing_off();
debug_locks_off();
#ifdef CONFIG_CRASH_DUMP
- if (((struct kimage *) data)->type == KEXEC_TYPE_CRASH) {
-
- lgr_info_log();
- s390_reset_system(setup_regs, __do_machine_kdump, data);
- } else
+ if (((struct kimage *) data)->type == KEXEC_TYPE_CRASH)
+ __machine_kdump(data);
#endif
- s390_reset_system(NULL, __do_machine_kexec, data);
- disabled_wait((unsigned long) __builtin_return_address(0));
+ __do_machine_kexec(data);
}
/*
diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c
index 0c1a679314dd..7873e171457c 100644
--- a/arch/s390/kernel/module.c
+++ b/arch/s390/kernel/module.c
@@ -159,11 +159,11 @@ int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
/* Increase core size by size of got & plt and set start
offsets for got and plt. */
- me->core_size = ALIGN(me->core_size, 4);
- me->arch.got_offset = me->core_size;
- me->core_size += me->arch.got_size;
- me->arch.plt_offset = me->core_size;
- me->core_size += me->arch.plt_size;
+ me->core_layout.size = ALIGN(me->core_layout.size, 4);
+ me->arch.got_offset = me->core_layout.size;
+ me->core_layout.size += me->arch.got_size;
+ me->arch.plt_offset = me->core_layout.size;
+ me->core_layout.size += me->arch.plt_size;
return 0;
}
@@ -279,7 +279,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
if (info->got_initialized == 0) {
Elf_Addr *gotent;
- gotent = me->module_core + me->arch.got_offset +
+ gotent = me->core_layout.base + me->arch.got_offset +
info->got_offset;
*gotent = val;
info->got_initialized = 1;
@@ -302,7 +302,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
rc = apply_rela_bits(loc, val, 0, 64, 0);
else if (r_type == R_390_GOTENT ||
r_type == R_390_GOTPLTENT) {
- val += (Elf_Addr) me->module_core - loc;
+ val += (Elf_Addr) me->core_layout.base - loc;
rc = apply_rela_bits(loc, val, 1, 32, 1);
}
break;
@@ -315,7 +315,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
case R_390_PLTOFF64: /* 16 bit offset from GOT to PLT. */
if (info->plt_initialized == 0) {
unsigned int *ip;
- ip = me->module_core + me->arch.plt_offset +
+ ip = me->core_layout.base + me->arch.plt_offset +
info->plt_offset;
ip[0] = 0x0d10e310; /* basr 1,0; lg 1,10(1); br 1 */
ip[1] = 0x100a0004;
@@ -334,7 +334,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
val - loc + 0xffffUL < 0x1ffffeUL) ||
(r_type == R_390_PLT32DBL &&
val - loc + 0xffffffffULL < 0x1fffffffeULL)))
- val = (Elf_Addr) me->module_core +
+ val = (Elf_Addr) me->core_layout.base +
me->arch.plt_offset +
info->plt_offset;
val += rela->r_addend - loc;
@@ -356,7 +356,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
case R_390_GOTOFF32: /* 32 bit offset to GOT. */
case R_390_GOTOFF64: /* 64 bit offset to GOT. */
val = val + rela->r_addend -
- ((Elf_Addr) me->module_core + me->arch.got_offset);
+ ((Elf_Addr) me->core_layout.base + me->arch.got_offset);
if (r_type == R_390_GOTOFF16)
rc = apply_rela_bits(loc, val, 0, 16, 0);
else if (r_type == R_390_GOTOFF32)
@@ -366,7 +366,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
break;
case R_390_GOTPC: /* 32 bit PC relative offset to GOT. */
case R_390_GOTPCDBL: /* 32 bit PC rel. off. to GOT shifted by 1. */
- val = (Elf_Addr) me->module_core + me->arch.got_offset +
+ val = (Elf_Addr) me->core_layout.base + me->arch.got_offset +
rela->r_addend - loc;
if (r_type == R_390_GOTPC)
rc = apply_rela_bits(loc, val, 1, 32, 0);
diff --git a/arch/s390/kernel/os_info.c b/arch/s390/kernel/os_info.c
index d112fc66f993..87f05e475ae8 100644
--- a/arch/s390/kernel/os_info.c
+++ b/arch/s390/kernel/os_info.c
@@ -89,7 +89,7 @@ static void os_info_old_alloc(int nr, int align)
goto fail;
}
buf_align = PTR_ALIGN(buf, align);
- if (copy_from_oldmem(buf_align, (void *) addr, size)) {
+ if (copy_oldmem_kernel(buf_align, (void *) addr, size)) {
msg = "copy failed";
goto fail_free;
}
@@ -122,14 +122,15 @@ static void os_info_old_init(void)
return;
if (!OLDMEM_BASE)
goto fail;
- if (copy_from_oldmem(&addr, &S390_lowcore.os_info, sizeof(addr)))
+ if (copy_oldmem_kernel(&addr, &S390_lowcore.os_info, sizeof(addr)))
goto fail;
if (addr == 0 || addr % PAGE_SIZE)
goto fail;
os_info_old = kzalloc(sizeof(*os_info_old), GFP_KERNEL);
if (!os_info_old)
goto fail;
- if (copy_from_oldmem(os_info_old, (void *) addr, sizeof(*os_info_old)))
+ if (copy_oldmem_kernel(os_info_old, (void *) addr,
+ sizeof(*os_info_old)))
goto fail_free;
if (os_info_old->magic != OS_INFO_MAGIC)
goto fail_free;
diff --git a/arch/s390/kernel/processor.c b/arch/s390/kernel/processor.c
index 7ce00e7a709a..647128d5b983 100644
--- a/arch/s390/kernel/processor.c
+++ b/arch/s390/kernel/processor.c
@@ -61,6 +61,9 @@ static int show_cpuinfo(struct seq_file *m, void *v)
"esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp",
"edat", "etf3eh", "highgprs", "te", "vx"
};
+ static const char * const int_hwcap_str[] = {
+ "sie"
+ };
unsigned long n = (unsigned long) v - 1;
int i;
@@ -75,6 +78,9 @@ static int show_cpuinfo(struct seq_file *m, void *v)
for (i = 0; i < ARRAY_SIZE(hwcap_str); i++)
if (hwcap_str[i] && (elf_hwcap & (1UL << i)))
seq_printf(m, "%s ", hwcap_str[i]);
+ for (i = 0; i < ARRAY_SIZE(int_hwcap_str); i++)
+ if (int_hwcap_str[i] && (int_hwcap & (1UL << i)))
+ seq_printf(m, "%s ", int_hwcap_str[i]);
seq_puts(m, "\n");
show_cacheinfo(m);
}
diff --git a/arch/s390/kernel/reipl.S b/arch/s390/kernel/reipl.S
index 52aab0bd84f8..89ea8c213d82 100644
--- a/arch/s390/kernel/reipl.S
+++ b/arch/s390/kernel/reipl.S
@@ -9,60 +9,66 @@
#include <asm/sigp.h>
#
-# store_status
+# Issue "store status" for the current CPU to its prefix page
+# and call passed function afterwards
#
-# Prerequisites to run this function:
-# - Prefix register is set to zero
-# - Original prefix register is stored in "dump_prefix_page"
-# - Lowcore protection is off
+# r2 = Function to be called after store status
+# r3 = Parameter for function
#
ENTRY(store_status)
/* Save register one and load save area base */
stg %r1,__LC_SAVE_AREA_RESTART
- lghi %r1,SAVE_AREA_BASE
/* General purpose registers */
- stmg %r0,%r15,__LC_GPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
- lg %r2,__LC_SAVE_AREA_RESTART
- stg %r2,__LC_GPREGS_SAVE_AREA-SAVE_AREA_BASE+8(%r1)
+ lghi %r1,__LC_GPREGS_SAVE_AREA
+ stmg %r0,%r15,0(%r1)
+ mvc 8(8,%r1),__LC_SAVE_AREA_RESTART
/* Control registers */
- stctg %c0,%c15,__LC_CREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ lghi %r1,__LC_CREGS_SAVE_AREA
+ stctg %c0,%c15,0(%r1)
/* Access registers */
- stam %a0,%a15,__LC_AREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ lghi %r1,__LC_AREGS_SAVE_AREA
+ stam %a0,%a15,0(%r1)
/* Floating point registers */
- std %f0, 0x00 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
- std %f1, 0x08 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
- std %f2, 0x10 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
- std %f3, 0x18 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
- std %f4, 0x20 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
- std %f5, 0x28 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
- std %f6, 0x30 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
- std %f7, 0x38 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
- std %f8, 0x40 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
- std %f9, 0x48 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
- std %f10,0x50 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
- std %f11,0x58 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
- std %f12,0x60 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
- std %f13,0x68 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
- std %f14,0x70 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
- std %f15,0x78 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ lghi %r1,__LC_FPREGS_SAVE_AREA
+ std %f0, 0x00(%r1)
+ std %f1, 0x08(%r1)
+ std %f2, 0x10(%r1)
+ std %f3, 0x18(%r1)
+ std %f4, 0x20(%r1)
+ std %f5, 0x28(%r1)
+ std %f6, 0x30(%r1)
+ std %f7, 0x38(%r1)
+ std %f8, 0x40(%r1)
+ std %f9, 0x48(%r1)
+ std %f10,0x50(%r1)
+ std %f11,0x58(%r1)
+ std %f12,0x60(%r1)
+ std %f13,0x68(%r1)
+ std %f14,0x70(%r1)
+ std %f15,0x78(%r1)
/* Floating point control register */
- stfpc __LC_FP_CREG_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ lghi %r1,__LC_FP_CREG_SAVE_AREA
+ stfpc 0(%r1)
/* CPU timer */
- stpt __LC_CPU_TIMER_SAVE_AREA-SAVE_AREA_BASE(%r1)
- /* Saved prefix register */
- larl %r2,dump_prefix_page
- mvc __LC_PREFIX_SAVE_AREA-SAVE_AREA_BASE(4,%r1),0(%r2)
+ lghi %r1,__LC_CPU_TIMER_SAVE_AREA
+ stpt 0(%r1)
+ /* Store prefix register */
+ lghi %r1,__LC_PREFIX_SAVE_AREA
+ stpx 0(%r1)
/* Clock comparator - seven bytes */
- larl %r2,.Lclkcmp
- stckc 0(%r2)
- mvc __LC_CLOCK_COMP_SAVE_AREA-SAVE_AREA_BASE + 1(7,%r1),1(%r2)
+ lghi %r1,__LC_CLOCK_COMP_SAVE_AREA
+ larl %r4,.Lclkcmp
+ stckc 0(%r4)
+ mvc 1(7,%r1),1(%r4)
/* Program status word */
- epsw %r2,%r3
- st %r2,__LC_PSW_SAVE_AREA-SAVE_AREA_BASE + 0(%r1)
- st %r3,__LC_PSW_SAVE_AREA-SAVE_AREA_BASE + 4(%r1)
- larl %r2,store_status
- stg %r2,__LC_PSW_SAVE_AREA-SAVE_AREA_BASE + 8(%r1)
- br %r14
+ lghi %r1,__LC_PSW_SAVE_AREA
+ epsw %r4,%r5
+ st %r4,0(%r1)
+ st %r5,4(%r1)
+ stg %r2,8(%r1)
+ lgr %r1,%r2
+ lgr %r2,%r3
+ br %r1
.section .bss
.align 8
@@ -77,9 +83,11 @@ ENTRY(store_status)
ENTRY(do_reipl_asm)
basr %r13,0
.Lpg0: lpswe .Lnewpsw-.Lpg0(%r13)
-.Lpg1: brasl %r14,store_status
+.Lpg1: lgr %r3,%r2
+ larl %r2,.Lstatus
+ brasl %r14,store_status
- lctlg %c6,%c6,.Lall-.Lpg0(%r13)
+.Lstatus: lctlg %c6,%c6,.Lall-.Lpg0(%r13)
lgr %r1,%r2
mvc __LC_PGM_NEW_PSW(16),.Lpcnew-.Lpg0(%r13)
stsch .Lschib-.Lpg0(%r13)
diff --git a/arch/s390/kernel/sclp.c b/arch/s390/kernel/sclp.c
index 9fe7781a45cd..d88db40bdf15 100644
--- a/arch/s390/kernel/sclp.c
+++ b/arch/s390/kernel/sclp.c
@@ -9,7 +9,11 @@
#include <asm/processor.h>
#include <asm/sclp.h>
+#define EVTYP_VT220MSG_MASK 0x00000040
+#define EVTYP_MSG_MASK 0x40000000
+
static char _sclp_work_area[4096] __aligned(PAGE_SIZE);
+static bool have_vt220, have_linemode;
static void _sclp_wait_int(void)
{
@@ -68,7 +72,7 @@ static int _sclp_setup(int disable)
0x00, 0x1c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x04,
- 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
unsigned int *masks;
@@ -82,13 +86,13 @@ static int _sclp_setup(int disable)
rc = _sclp_servc(0x00780005, _sclp_work_area);
if (rc)
return rc;
- if ((masks[0] & masks[3]) != masks[0] ||
- (masks[1] & masks[2]) != masks[1])
- return -EIO;
+ have_vt220 = masks[2] & EVTYP_VT220MSG_MASK;
+ have_linemode = masks[2] & EVTYP_MSG_MASK;
return 0;
}
-static int _sclp_print(const char *str)
+/* Output multi-line text using SCLP Message interface. */
+static void _sclp_print_lm(const char *str)
{
static unsigned char write_head[] = {
/* sccb header */
@@ -143,18 +147,49 @@ static int _sclp_print(const char *str)
} while (ch != 0);
/* SCLP write data */
- return _sclp_servc(0x00760005, _sclp_work_area);
+ _sclp_servc(0x00760005, _sclp_work_area);
}
-int _sclp_print_early(const char *str)
+/* Output multi-line text (plus a newline) using SCLP VT220
+ * interface.
+ */
+static void _sclp_print_vt220(const char *str)
{
- int rc;
+ static unsigned char const write_head[] = {
+ /* sccb header */
+ 0x00, 0x0e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* evbuf header */
+ 0x00, 0x06,
+ 0x1a, 0x00, 0x00, 0x00,
+ };
+ size_t len = strlen(str);
- rc = _sclp_setup(0);
- if (rc)
- return rc;
- rc = _sclp_print(str);
- if (rc)
- return rc;
- return _sclp_setup(1);
+ if (sizeof(write_head) + len >= sizeof(_sclp_work_area))
+ len = sizeof(_sclp_work_area) - sizeof(write_head) - 1;
+
+ memcpy(_sclp_work_area, write_head, sizeof(write_head));
+ memcpy(_sclp_work_area + sizeof(write_head), str, len);
+ _sclp_work_area[sizeof(write_head) + len] = '\n';
+
+ /* Update length fields in evbuf and sccb headers */
+ *(unsigned short *)(_sclp_work_area + 8) += len + 1;
+ *(unsigned short *)(_sclp_work_area + 0) += len + 1;
+
+ /* SCLP write data */
+ (void)_sclp_servc(0x00760005, _sclp_work_area);
+}
+
+/* Output one or more lines of text on the SCLP console (VT220 and /
+ * or line-mode). All lines get terminated; no need for a trailing LF.
+ */
+void _sclp_print_early(const char *str)
+{
+ if (_sclp_setup(0) != 0)
+ return;
+ if (have_linemode)
+ _sclp_print_lm(str);
+ if (have_vt220)
+ _sclp_print_vt220(str);
+ _sclp_setup(1);
}
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index c837bcacf218..c6878fbbcf13 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -80,6 +80,8 @@ EXPORT_SYMBOL(console_irq);
unsigned long elf_hwcap __read_mostly = 0;
char elf_platform[ELF_PLATFORM_SIZE];
+unsigned long int_hwcap = 0;
+
int __initdata memory_end_set;
unsigned long __initdata memory_end;
unsigned long __initdata max_physmem_end;
@@ -97,7 +99,7 @@ unsigned long MODULES_VADDR;
unsigned long MODULES_END;
/* An array with a pointer to the lowcore of every CPU. */
-struct _lowcore *lowcore_ptr[NR_CPUS];
+struct lowcore *lowcore_ptr[NR_CPUS];
EXPORT_SYMBOL(lowcore_ptr);
/*
@@ -291,12 +293,12 @@ void *restart_stack __attribute__((__section__(".data")));
static void __init setup_lowcore(void)
{
- struct _lowcore *lc;
+ struct lowcore *lc;
/*
* Setup lowcore for boot cpu
*/
- BUILD_BUG_ON(sizeof(struct _lowcore) != LC_PAGES * 4096);
+ BUILD_BUG_ON(sizeof(struct lowcore) != LC_PAGES * 4096);
lc = __alloc_bootmem_low(LC_PAGES * PAGE_SIZE, LC_PAGES * PAGE_SIZE, 0);
lc->restart_psw.mask = PSW_KERNEL_BITS;
lc->restart_psw.addr =
@@ -661,15 +663,6 @@ static void __init reserve_kernel(void)
#endif
}
-static void __init reserve_elfcorehdr(void)
-{
-#ifdef CONFIG_CRASH_DUMP
- if (is_kdump_kernel())
- memblock_reserve(elfcorehdr_addr - OLDMEM_BASE,
- PAGE_ALIGN(elfcorehdr_size));
-#endif
-}
-
static void __init setup_memory(void)
{
struct memblock_region *reg;
@@ -793,6 +786,13 @@ static int __init setup_hwcaps(void)
strcpy(elf_platform, "z13");
break;
}
+
+ /*
+ * Virtualization support HWCAP_INT_SIE is bit 0.
+ */
+ if (sclp.has_sief2)
+ int_hwcap |= HWCAP_INT_SIE;
+
return 0;
}
arch_initcall(setup_hwcaps);
@@ -841,6 +841,11 @@ void __init setup_arch(char **cmdline_p)
init_mm.brk = (unsigned long) &_end;
parse_early_param();
+#ifdef CONFIG_CRASH_DUMP
+ /* Deactivate elfcorehdr= kernel parameter */
+ elfcorehdr_addr = ELFCORE_ADDR_MAX;
+#endif
+
os_info_init();
setup_ipl();
@@ -849,7 +854,6 @@ void __init setup_arch(char **cmdline_p)
reserve_oldmem();
reserve_kernel();
reserve_initrd();
- reserve_elfcorehdr();
memblock_allow_resize();
/* Get information about *all* installed memory */
@@ -870,11 +874,13 @@ void __init setup_arch(char **cmdline_p)
check_initrd();
reserve_crashkernel();
+#ifdef CONFIG_CRASH_DUMP
/*
* Be aware that smp_save_dump_cpus() triggers a system reset.
* Therefore CPU and device initialization should be done afterwards.
*/
smp_save_dump_cpus();
+#endif
setup_resources();
setup_vmcoreinfo();
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index 9062df575afe..a13468b9a913 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -64,8 +64,9 @@ enum {
static DEFINE_PER_CPU(struct cpu *, cpu_device);
struct pcpu {
- struct _lowcore *lowcore; /* lowcore page(s) for the cpu */
+ struct lowcore *lowcore; /* lowcore page(s) for the cpu */
unsigned long ec_mask; /* bit mask for ec_xxx functions */
+ unsigned long ec_clk; /* sigp timestamp for ec_xxx */
signed char state; /* physical cpu state */
signed char polarization; /* physical polarization */
u16 address; /* physical cpu address */
@@ -80,6 +81,10 @@ EXPORT_SYMBOL(smp_cpu_mt_shift);
unsigned int smp_cpu_mtid;
EXPORT_SYMBOL(smp_cpu_mtid);
+#ifdef CONFIG_CRASH_DUMP
+__vector128 __initdata boot_cpu_vector_save_area[__NUM_VXRS];
+#endif
+
static unsigned int smp_max_threads __initdata = -1U;
static int __init early_nosmt(char *s)
@@ -105,8 +110,7 @@ DEFINE_MUTEX(smp_cpu_state_mutex);
/*
* Signal processor helper functions.
*/
-static inline int __pcpu_sigp_relax(u16 addr, u8 order, unsigned long parm,
- u32 *status)
+static inline int __pcpu_sigp_relax(u16 addr, u8 order, unsigned long parm)
{
int cc;
@@ -171,6 +175,7 @@ static void pcpu_ec_call(struct pcpu *pcpu, int ec_bit)
if (test_and_set_bit(ec_bit, &pcpu->ec_mask))
return;
order = pcpu_running(pcpu) ? SIGP_EXTERNAL_CALL : SIGP_EMERGENCY_SIGNAL;
+ pcpu->ec_clk = get_tod_clock_fast();
pcpu_sigp_retry(pcpu, order, 0);
}
@@ -180,10 +185,10 @@ static void pcpu_ec_call(struct pcpu *pcpu, int ec_bit)
static int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu)
{
unsigned long async_stack, panic_stack;
- struct _lowcore *lc;
+ struct lowcore *lc;
if (pcpu != &pcpu_devices[0]) {
- pcpu->lowcore = (struct _lowcore *)
+ pcpu->lowcore = (struct lowcore *)
__get_free_pages(GFP_KERNEL | GFP_DMA, LC_ORDER);
async_stack = __get_free_pages(GFP_KERNEL, ASYNC_ORDER);
panic_stack = __get_free_page(GFP_KERNEL);
@@ -235,7 +240,7 @@ static void pcpu_free_lowcore(struct pcpu *pcpu)
static void pcpu_prepare_secondary(struct pcpu *pcpu, int cpu)
{
- struct _lowcore *lc = pcpu->lowcore;
+ struct lowcore *lc = pcpu->lowcore;
if (MACHINE_HAS_TLB_LC)
cpumask_set_cpu(cpu, &init_mm.context.cpu_attach_mask);
@@ -255,7 +260,7 @@ static void pcpu_prepare_secondary(struct pcpu *pcpu, int cpu)
static void pcpu_attach_task(struct pcpu *pcpu, struct task_struct *tsk)
{
- struct _lowcore *lc = pcpu->lowcore;
+ struct lowcore *lc = pcpu->lowcore;
struct thread_info *ti = task_thread_info(tsk);
lc->kernel_stack = (unsigned long) task_stack_page(tsk)
@@ -271,7 +276,7 @@ static void pcpu_attach_task(struct pcpu *pcpu, struct task_struct *tsk)
static void pcpu_start_fn(struct pcpu *pcpu, void (*func)(void *), void *data)
{
- struct _lowcore *lc = pcpu->lowcore;
+ struct lowcore *lc = pcpu->lowcore;
lc->restart_stack = lc->kernel_stack;
lc->restart_fn = (unsigned long) func;
@@ -286,7 +291,7 @@ static void pcpu_start_fn(struct pcpu *pcpu, void (*func)(void *), void *data)
static void pcpu_delegate(struct pcpu *pcpu, void (*func)(void *),
void *data, unsigned long stack)
{
- struct _lowcore *lc = lowcore_ptr[pcpu - pcpu_devices];
+ struct lowcore *lc = lowcore_ptr[pcpu - pcpu_devices];
unsigned long source_cpu = stap();
__load_psw_mask(PSW_KERNEL_BITS);
@@ -538,53 +543,24 @@ EXPORT_SYMBOL(smp_ctl_clear_bit);
#ifdef CONFIG_CRASH_DUMP
-static void __init __smp_store_cpu_state(struct save_area_ext *sa_ext,
- u16 address, int is_boot_cpu)
-{
- void *lc = (void *)(unsigned long) store_prefix();
- unsigned long vx_sa;
-
- if (is_boot_cpu) {
- /* Copy the registers of the boot CPU. */
- copy_oldmem_page(1, (void *) &sa_ext->sa, sizeof(sa_ext->sa),
- SAVE_AREA_BASE - PAGE_SIZE, 0);
- if (MACHINE_HAS_VX)
- save_vx_regs_safe(sa_ext->vx_regs);
- return;
- }
- /* Get the registers of a non-boot cpu. */
- __pcpu_sigp_relax(address, SIGP_STOP_AND_STORE_STATUS, 0, NULL);
- memcpy_real(&sa_ext->sa, lc + SAVE_AREA_BASE, sizeof(sa_ext->sa));
- if (!MACHINE_HAS_VX)
- return;
- /* Get the VX registers */
- vx_sa = memblock_alloc(PAGE_SIZE, PAGE_SIZE);
- if (!vx_sa)
- panic("could not allocate memory for VX save area\n");
- __pcpu_sigp_relax(address, SIGP_STORE_ADDITIONAL_STATUS, vx_sa, NULL);
- memcpy(sa_ext->vx_regs, (void *) vx_sa, sizeof(sa_ext->vx_regs));
- memblock_free(vx_sa, PAGE_SIZE);
-}
-
int smp_store_status(int cpu)
{
- unsigned long vx_sa;
- struct pcpu *pcpu;
+ struct pcpu *pcpu = pcpu_devices + cpu;
+ unsigned long pa;
- pcpu = pcpu_devices + cpu;
- if (__pcpu_sigp_relax(pcpu->address, SIGP_STOP_AND_STORE_STATUS,
- 0, NULL) != SIGP_CC_ORDER_CODE_ACCEPTED)
+ pa = __pa(&pcpu->lowcore->floating_pt_save_area);
+ if (__pcpu_sigp_relax(pcpu->address, SIGP_STORE_STATUS_AT_ADDRESS,
+ pa) != SIGP_CC_ORDER_CODE_ACCEPTED)
return -EIO;
if (!MACHINE_HAS_VX)
return 0;
- vx_sa = __pa(pcpu->lowcore->vector_save_area_addr);
- __pcpu_sigp_relax(pcpu->address, SIGP_STORE_ADDITIONAL_STATUS,
- vx_sa, NULL);
+ pa = __pa(pcpu->lowcore->vector_save_area_addr);
+ if (__pcpu_sigp_relax(pcpu->address, SIGP_STORE_ADDITIONAL_STATUS,
+ pa) != SIGP_CC_ORDER_CODE_ACCEPTED)
+ return -EIO;
return 0;
}
-#endif /* CONFIG_CRASH_DUMP */
-
/*
* Collect CPU state of the previous, crashed system.
* There are four cases:
@@ -593,7 +569,7 @@ int smp_store_status(int cpu)
* The state for all CPUs except the boot CPU needs to be collected
* with sigp stop-and-store-status. The boot CPU state is located in
* the absolute lowcore of the memory stored in the HSA. The zcore code
- * will allocate the save area and copy the boot CPU state from the HSA.
+ * will copy the boot CPU state from the HSA.
* 2) stand-alone kdump for SCSI (zfcp dump with swapped memory)
* condition: OLDMEM_BASE != NULL && ipl_info.type == IPL_TYPE_FCP_DUMP
* The state for all CPUs except the boot CPU needs to be collected
@@ -608,55 +584,78 @@ int smp_store_status(int cpu)
* stored the registers of the boot CPU in the memory of the old system.
* 4) kdump and the old kernel stored the CPU state
* condition: OLDMEM_BASE != NULL && is_kdump_kernel()
- * The state of all CPUs is stored in ELF sections in the memory of the
- * old system. The ELF sections are picked up by the crash_dump code
- * via elfcorehdr_addr.
+ * This case does not exist for s390 anymore, setup_arch explicitly
+ * deactivates the elfcorehdr= kernel parameter
*/
+static __init void smp_save_cpu_vxrs(struct save_area *sa, u16 addr,
+ bool is_boot_cpu, unsigned long page)
+{
+ __vector128 *vxrs = (__vector128 *) page;
+
+ if (is_boot_cpu)
+ vxrs = boot_cpu_vector_save_area;
+ else
+ __pcpu_sigp_relax(addr, SIGP_STORE_ADDITIONAL_STATUS, page);
+ save_area_add_vxrs(sa, vxrs);
+}
+
+static __init void smp_save_cpu_regs(struct save_area *sa, u16 addr,
+ bool is_boot_cpu, unsigned long page)
+{
+ void *regs = (void *) page;
+
+ if (is_boot_cpu)
+ copy_oldmem_kernel(regs, (void *) __LC_FPREGS_SAVE_AREA, 512);
+ else
+ __pcpu_sigp_relax(addr, SIGP_STORE_STATUS_AT_ADDRESS, page);
+ save_area_add_regs(sa, regs);
+}
+
void __init smp_save_dump_cpus(void)
{
-#ifdef CONFIG_CRASH_DUMP
- int addr, cpu, boot_cpu_addr, max_cpu_addr;
- struct save_area_ext *sa_ext;
+ int addr, boot_cpu_addr, max_cpu_addr;
+ struct save_area *sa;
+ unsigned long page;
bool is_boot_cpu;
- if (is_kdump_kernel())
- /* Previous system stored the CPU states. Nothing to do. */
- return;
if (!(OLDMEM_BASE || ipl_info.type == IPL_TYPE_FCP_DUMP))
/* No previous system present, normal boot. */
return;
+ /* Allocate a page as dumping area for the store status sigps */
+ page = memblock_alloc_base(PAGE_SIZE, PAGE_SIZE, 1UL << 31);
+ if (!page)
+ panic("could not allocate memory for save area\n");
/* Set multi-threading state to the previous system. */
pcpu_set_smt(sclp.mtid_prev);
- max_cpu_addr = SCLP_MAX_CORES << sclp.mtid_prev;
- for (cpu = 0, addr = 0; addr <= max_cpu_addr; addr++) {
- if (__pcpu_sigp_relax(addr, SIGP_SENSE, 0, NULL) ==
- SIGP_CC_NOT_OPERATIONAL)
- continue;
- cpu += 1;
- }
- dump_save_areas.areas = (void *)memblock_alloc(sizeof(void *) * cpu, 8);
- dump_save_areas.count = cpu;
boot_cpu_addr = stap();
- for (cpu = 0, addr = 0; addr <= max_cpu_addr; addr++) {
- if (__pcpu_sigp_relax(addr, SIGP_SENSE, 0, NULL) ==
+ max_cpu_addr = SCLP_MAX_CORES << sclp.mtid_prev;
+ for (addr = 0; addr <= max_cpu_addr; addr++) {
+ if (__pcpu_sigp_relax(addr, SIGP_SENSE, 0) ==
SIGP_CC_NOT_OPERATIONAL)
continue;
- sa_ext = (void *) memblock_alloc(sizeof(*sa_ext), 8);
- dump_save_areas.areas[cpu] = sa_ext;
- if (!sa_ext)
- panic("could not allocate memory for save area\n");
is_boot_cpu = (addr == boot_cpu_addr);
- cpu += 1;
- if (is_boot_cpu && !OLDMEM_BASE)
- /* Skip boot CPU for standard zfcp dump. */
- continue;
- /* Get state for this CPU. */
- __smp_store_cpu_state(sa_ext, addr, is_boot_cpu);
+ /* Allocate save area */
+ sa = save_area_alloc(is_boot_cpu);
+ if (!sa)
+ panic("could not allocate memory for save area\n");
+ if (MACHINE_HAS_VX)
+ /* Get the vector registers */
+ smp_save_cpu_vxrs(sa, addr, is_boot_cpu, page);
+ /*
+ * For a zfcp dump OLDMEM_BASE == NULL and the registers
+ * of the boot CPU are stored in the HSA. To retrieve
+ * these registers an SCLP request is required which is
+ * done by drivers/s390/char/zcore.c:init_cpu_info()
+ */
+ if (!is_boot_cpu || OLDMEM_BASE)
+ /* Get the CPU registers */
+ smp_save_cpu_regs(sa, addr, is_boot_cpu, page);
}
+ memblock_free(page, PAGE_SIZE);
diag308_reset();
pcpu_set_smt(0);
-#endif /* CONFIG_CRASH_DUMP */
}
+#endif /* CONFIG_CRASH_DUMP */
void smp_cpu_set_polarization(int cpu, int val)
{
@@ -680,7 +679,7 @@ static struct sclp_core_info *smp_get_core_info(void)
for (address = 0;
address < (SCLP_MAX_CORES << smp_cpu_mt_shift);
address += (1U << smp_cpu_mt_shift)) {
- if (__pcpu_sigp_relax(address, SIGP_SENSE, 0, NULL) ==
+ if (__pcpu_sigp_relax(address, SIGP_SENSE, 0) ==
SIGP_CC_NOT_OPERATIONAL)
continue;
info->core[info->configured].core_id =
@@ -924,7 +923,7 @@ void __init smp_prepare_boot_cpu(void)
pcpu->state = CPU_STATE_CONFIGURED;
pcpu->address = stap();
- pcpu->lowcore = (struct _lowcore *)(unsigned long) store_prefix();
+ pcpu->lowcore = (struct lowcore *)(unsigned long) store_prefix();
S390_lowcore.percpu_offset = __per_cpu_offset[0];
smp_cpu_set_polarization(0, POLARIZATION_UNKNOWN);
set_cpu_present(0, true);
diff --git a/arch/s390/kernel/sysinfo.c b/arch/s390/kernel/sysinfo.c
index 99babea026ca..f7dba3887a54 100644
--- a/arch/s390/kernel/sysinfo.c
+++ b/arch/s390/kernel/sysinfo.c
@@ -111,8 +111,7 @@ static void stsi_1_1_1(struct seq_file *m, struct sysinfo_1_1_1 *info)
static void stsi_15_1_x(struct seq_file *m, struct sysinfo_15_1_x *info)
{
- static int max_mnest;
- int i, rc;
+ int i;
seq_putc(m, '\n');
if (!MACHINE_HAS_TOPOLOGY)
@@ -123,7 +122,7 @@ static void stsi_15_1_x(struct seq_file *m, struct sysinfo_15_1_x *info)
for (i = 0; i < TOPOLOGY_NR_MAG; i++)
seq_printf(m, " %d", info->mag[i]);
seq_putc(m, '\n');
-#ifdef CONFIG_SCHED_MC
+#ifdef CONFIG_SCHED_TOPOLOGY
store_topology(info);
seq_printf(m, "CPU Topology SW: ");
for (i = 0; i < TOPOLOGY_NR_MAG; i++)
@@ -145,6 +144,10 @@ static void stsi_1_2_2(struct seq_file *m, struct sysinfo_1_2_2 *info)
seq_printf(m, "CPUs Configured: %d\n", info->cpus_configured);
seq_printf(m, "CPUs Standby: %d\n", info->cpus_standby);
seq_printf(m, "CPUs Reserved: %d\n", info->cpus_reserved);
+ if (info->mt_installed) {
+ seq_printf(m, "CPUs G-MTID: %d\n", info->mt_gtid);
+ seq_printf(m, "CPUs S-MTID: %d\n", info->mt_stid);
+ }
/*
* Sigh 2. According to the specification the alternate
* capability field is a 32 bit floating point number
@@ -194,13 +197,10 @@ static void stsi_2_2_2(struct seq_file *m, struct sysinfo_2_2_2 *info)
seq_printf(m, "LPAR CPUs Reserved: %d\n", info->cpus_reserved);
seq_printf(m, "LPAR CPUs Dedicated: %d\n", info->cpus_dedicated);
seq_printf(m, "LPAR CPUs Shared: %d\n", info->cpus_shared);
- if (info->mt_installed & 0x80) {
- seq_printf(m, "LPAR CPUs G-MTID: %d\n",
- info->mt_general & 0x1f);
- seq_printf(m, "LPAR CPUs S-MTID: %d\n",
- info->mt_installed & 0x1f);
- seq_printf(m, "LPAR CPUs PS-MTID: %d\n",
- info->mt_psmtid & 0x1f);
+ if (info->mt_installed) {
+ seq_printf(m, "LPAR CPUs G-MTID: %d\n", info->mt_gtid);
+ seq_printf(m, "LPAR CPUs S-MTID: %d\n", info->mt_stid);
+ seq_printf(m, "LPAR CPUs PS-MTID: %d\n", info->mt_psmtid);
}
}
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c
index 1b18118bbc06..d69d648759c9 100644
--- a/arch/s390/kernel/traps.c
+++ b/arch/s390/kernel/traps.c
@@ -260,11 +260,8 @@ void vector_exception(struct pt_regs *regs)
void data_exception(struct pt_regs *regs)
{
- __u16 __user *location;
int signal = 0;
- location = get_trap_ip(regs);
-
save_fpu_regs();
if (current->thread.fpu.fpc & FPC_DXC_MASK)
signal = SIGFPE;
diff --git a/arch/s390/kernel/vdso.c b/arch/s390/kernel/vdso.c
index 59eddb0e1a3e..94495cac8be3 100644
--- a/arch/s390/kernel/vdso.c
+++ b/arch/s390/kernel/vdso.c
@@ -80,7 +80,7 @@ struct vdso_data *vdso_data = &vdso_data_store.data;
/*
* Setup vdso data page.
*/
-static void vdso_init_data(struct vdso_data *vd)
+static void __init vdso_init_data(struct vdso_data *vd)
{
vd->ectg_available = test_facility(31);
}
@@ -90,9 +90,10 @@ static void vdso_init_data(struct vdso_data *vd)
*/
#define SEGMENT_ORDER 2
-int vdso_alloc_per_cpu(struct _lowcore *lowcore)
+int vdso_alloc_per_cpu(struct lowcore *lowcore)
{
unsigned long segment_table, page_table, page_frame;
+ struct vdso_per_cpu_data *vd;
u32 *psal, *aste;
int i;
@@ -107,6 +108,12 @@ int vdso_alloc_per_cpu(struct _lowcore *lowcore)
if (!segment_table || !page_table || !page_frame)
goto out;
+ /* Initialize per-cpu vdso data page */
+ vd = (struct vdso_per_cpu_data *) page_frame;
+ vd->cpu_nr = lowcore->cpu_nr;
+ vd->node_id = cpu_to_node(vd->cpu_nr);
+
+ /* Set up access register mode page table */
clear_table((unsigned long *) segment_table, _SEGMENT_ENTRY_EMPTY,
PAGE_SIZE << SEGMENT_ORDER);
clear_table((unsigned long *) page_table, _PAGE_INVALID,
@@ -138,7 +145,7 @@ out:
return -ENOMEM;
}
-void vdso_free_per_cpu(struct _lowcore *lowcore)
+void vdso_free_per_cpu(struct lowcore *lowcore)
{
unsigned long segment_table, page_table, page_frame;
u32 *psal, *aste;
@@ -163,7 +170,7 @@ static void vdso_init_cr5(void)
if (!vdso_enabled)
return;
- cr5 = offsetof(struct _lowcore, paste);
+ cr5 = offsetof(struct lowcore, paste);
__ctl_load(cr5, 5, 5);
}
@@ -299,8 +306,6 @@ static int __init vdso_init(void)
get_page(virt_to_page(vdso_data));
- smp_mb();
-
return 0;
}
early_initcall(vdso_init);
diff --git a/arch/s390/kernel/vdso32/Makefile b/arch/s390/kernel/vdso32/Makefile
index ee8a18e50a25..f9c459586649 100644
--- a/arch/s390/kernel/vdso32/Makefile
+++ b/arch/s390/kernel/vdso32/Makefile
@@ -1,6 +1,6 @@
# List of files in the vdso, has to be asm only for now
-obj-vdso32 = gettimeofday.o clock_getres.o clock_gettime.o note.o
+obj-vdso32 = gettimeofday.o clock_getres.o clock_gettime.o note.o getcpu.o
# Build rules
diff --git a/arch/s390/kernel/vdso32/getcpu.S b/arch/s390/kernel/vdso32/getcpu.S
new file mode 100644
index 000000000000..c1ed0b72030f
--- /dev/null
+++ b/arch/s390/kernel/vdso32/getcpu.S
@@ -0,0 +1,43 @@
+/*
+ * Userland implementation of getcpu() for 32 bits processes in a
+ * s390 kernel for use in the vDSO
+ *
+ * Copyright IBM Corp. 2016
+ * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
+ */
+#include <asm/vdso.h>
+#include <asm/asm-offsets.h>
+
+ .text
+ .align 4
+ .globl __kernel_getcpu
+ .type __kernel_getcpu,@function
+__kernel_getcpu:
+ .cfi_startproc
+ ear %r1,%a4
+ lhi %r4,1
+ sll %r4,24
+ sar %a4,%r4
+ la %r4,0
+ epsw %r0,0
+ sacf 512
+ l %r5,__VDSO_CPU_NR(%r4)
+ l %r4,__VDSO_NODE_ID(%r4)
+ tml %r0,0x4000
+ jo 1f
+ tml %r0,0x8000
+ jno 0f
+ sacf 256
+ j 1f
+0: sacf 0
+1: sar %a4,%r1
+ ltr %r2,%r2
+ jz 2f
+ st %r5,0(%r2)
+2: ltr %r3,%r3
+ jz 3f
+ st %r4,0(%r3)
+3: lhi %r2,0
+ br %r14
+ .cfi_endproc
+ .size __kernel_getcpu,.-__kernel_getcpu
diff --git a/arch/s390/kernel/vdso32/vdso32.lds.S b/arch/s390/kernel/vdso32/vdso32.lds.S
index a8c379fa1247..8f048c2d6d13 100644
--- a/arch/s390/kernel/vdso32/vdso32.lds.S
+++ b/arch/s390/kernel/vdso32/vdso32.lds.S
@@ -132,6 +132,7 @@ VERSION
__kernel_gettimeofday;
__kernel_clock_gettime;
__kernel_clock_getres;
+ __kernel_getcpu;
local: *;
};
diff --git a/arch/s390/kernel/vdso64/Makefile b/arch/s390/kernel/vdso64/Makefile
index c4b03f9ed228..058659c1b8cf 100644
--- a/arch/s390/kernel/vdso64/Makefile
+++ b/arch/s390/kernel/vdso64/Makefile
@@ -1,6 +1,6 @@
# List of files in the vdso, has to be asm only for now
-obj-vdso64 = gettimeofday.o clock_getres.o clock_gettime.o note.o
+obj-vdso64 = gettimeofday.o clock_getres.o clock_gettime.o note.o getcpu.o
# Build rules
diff --git a/arch/s390/kernel/vdso64/getcpu.S b/arch/s390/kernel/vdso64/getcpu.S
new file mode 100644
index 000000000000..4cbe98291931
--- /dev/null
+++ b/arch/s390/kernel/vdso64/getcpu.S
@@ -0,0 +1,42 @@
+/*
+ * Userland implementation of getcpu() for 64 bits processes in a
+ * s390 kernel for use in the vDSO
+ *
+ * Copyright IBM Corp. 2016
+ * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
+ */
+#include <asm/vdso.h>
+#include <asm/asm-offsets.h>
+
+ .text
+ .align 4
+ .globl __kernel_getcpu
+ .type __kernel_getcpu,@function
+__kernel_getcpu:
+ .cfi_startproc
+ ear %r1,%a4
+ llilh %r4,0x0100
+ sar %a4,%r4
+ la %r4,0
+ epsw %r0,0
+ sacf 512
+ l %r5,__VDSO_CPU_NR(%r4)
+ l %r4,__VDSO_NODE_ID(%r4)
+ tml %r0,0x4000
+ jo 1f
+ tml %r0,0x8000
+ jno 0f
+ sacf 256
+ j 1f
+0: sacf 0
+1: sar %a4,%r1
+ ltgr %r2,%r2
+ jz 2f
+ st %r5,0(%r2)
+2: ltgr %r3,%r3
+ jz 3f
+ st %r4,0(%r3)
+3: lghi %r2,0
+ br %r14
+ .cfi_endproc
+ .size __kernel_getcpu,.-__kernel_getcpu
diff --git a/arch/s390/kernel/vdso64/vdso64.lds.S b/arch/s390/kernel/vdso64/vdso64.lds.S
index 9f5979d102a9..f35455d497fe 100644
--- a/arch/s390/kernel/vdso64/vdso64.lds.S
+++ b/arch/s390/kernel/vdso64/vdso64.lds.S
@@ -132,6 +132,7 @@ VERSION
__kernel_gettimeofday;
__kernel_clock_gettime;
__kernel_clock_getres;
+ __kernel_getcpu;
local: *;
};
diff --git a/arch/s390/kvm/diag.c b/arch/s390/kvm/diag.c
index 5fbfb88f8477..05f7de9869a9 100644
--- a/arch/s390/kvm/diag.c
+++ b/arch/s390/kvm/diag.c
@@ -155,10 +155,8 @@ static int __diag_time_slice_end(struct kvm_vcpu *vcpu)
static int __diag_time_slice_end_directed(struct kvm_vcpu *vcpu)
{
- struct kvm *kvm = vcpu->kvm;
struct kvm_vcpu *tcpu;
int tid;
- int i;
tid = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4];
vcpu->stat.diagnose_9c++;
@@ -167,12 +165,9 @@ static int __diag_time_slice_end_directed(struct kvm_vcpu *vcpu)
if (tid == vcpu->vcpu_id)
return 0;
- kvm_for_each_vcpu(i, tcpu, kvm)
- if (tcpu->vcpu_id == tid) {
- kvm_vcpu_yield_to(tcpu);
- break;
- }
-
+ tcpu = kvm_get_vcpu_by_id(vcpu->kvm, tid);
+ if (tcpu)
+ kvm_vcpu_yield_to(tcpu);
return 0;
}
diff --git a/arch/s390/kvm/gaccess.c b/arch/s390/kvm/gaccess.c
index a7559f7207df..d30db40437dc 100644
--- a/arch/s390/kvm/gaccess.c
+++ b/arch/s390/kvm/gaccess.c
@@ -259,10 +259,14 @@ struct aste {
int ipte_lock_held(struct kvm_vcpu *vcpu)
{
- union ipte_control *ic = &vcpu->kvm->arch.sca->ipte_control;
+ if (vcpu->arch.sie_block->eca & 1) {
+ int rc;
- if (vcpu->arch.sie_block->eca & 1)
- return ic->kh != 0;
+ read_lock(&vcpu->kvm->arch.sca_lock);
+ rc = kvm_s390_get_ipte_control(vcpu->kvm)->kh != 0;
+ read_unlock(&vcpu->kvm->arch.sca_lock);
+ return rc;
+ }
return vcpu->kvm->arch.ipte_lock_count != 0;
}
@@ -274,16 +278,20 @@ static void ipte_lock_simple(struct kvm_vcpu *vcpu)
vcpu->kvm->arch.ipte_lock_count++;
if (vcpu->kvm->arch.ipte_lock_count > 1)
goto out;
- ic = &vcpu->kvm->arch.sca->ipte_control;
+retry:
+ read_lock(&vcpu->kvm->arch.sca_lock);
+ ic = kvm_s390_get_ipte_control(vcpu->kvm);
do {
old = READ_ONCE(*ic);
- while (old.k) {
+ if (old.k) {
+ read_unlock(&vcpu->kvm->arch.sca_lock);
cond_resched();
- old = READ_ONCE(*ic);
+ goto retry;
}
new = old;
new.k = 1;
} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
+ read_unlock(&vcpu->kvm->arch.sca_lock);
out:
mutex_unlock(&vcpu->kvm->arch.ipte_mutex);
}
@@ -296,12 +304,14 @@ static void ipte_unlock_simple(struct kvm_vcpu *vcpu)
vcpu->kvm->arch.ipte_lock_count--;
if (vcpu->kvm->arch.ipte_lock_count)
goto out;
- ic = &vcpu->kvm->arch.sca->ipte_control;
+ read_lock(&vcpu->kvm->arch.sca_lock);
+ ic = kvm_s390_get_ipte_control(vcpu->kvm);
do {
old = READ_ONCE(*ic);
new = old;
new.k = 0;
} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
+ read_unlock(&vcpu->kvm->arch.sca_lock);
wake_up(&vcpu->kvm->arch.ipte_wq);
out:
mutex_unlock(&vcpu->kvm->arch.ipte_mutex);
@@ -311,24 +321,29 @@ static void ipte_lock_siif(struct kvm_vcpu *vcpu)
{
union ipte_control old, new, *ic;
- ic = &vcpu->kvm->arch.sca->ipte_control;
+retry:
+ read_lock(&vcpu->kvm->arch.sca_lock);
+ ic = kvm_s390_get_ipte_control(vcpu->kvm);
do {
old = READ_ONCE(*ic);
- while (old.kg) {
+ if (old.kg) {
+ read_unlock(&vcpu->kvm->arch.sca_lock);
cond_resched();
- old = READ_ONCE(*ic);
+ goto retry;
}
new = old;
new.k = 1;
new.kh++;
} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
+ read_unlock(&vcpu->kvm->arch.sca_lock);
}
static void ipte_unlock_siif(struct kvm_vcpu *vcpu)
{
union ipte_control old, new, *ic;
- ic = &vcpu->kvm->arch.sca->ipte_control;
+ read_lock(&vcpu->kvm->arch.sca_lock);
+ ic = kvm_s390_get_ipte_control(vcpu->kvm);
do {
old = READ_ONCE(*ic);
new = old;
@@ -336,6 +351,7 @@ static void ipte_unlock_siif(struct kvm_vcpu *vcpu)
if (!new.kh)
new.k = 0;
} while (cmpxchg(&ic->val, old.val, new.val) != old.val);
+ read_unlock(&vcpu->kvm->arch.sca_lock);
if (!new.kh)
wake_up(&vcpu->kvm->arch.ipte_wq);
}
diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c
index b4a5aa110cec..d53c10753c46 100644
--- a/arch/s390/kvm/intercept.c
+++ b/arch/s390/kvm/intercept.c
@@ -54,9 +54,6 @@ void kvm_s390_rewind_psw(struct kvm_vcpu *vcpu, int ilc)
static int handle_noop(struct kvm_vcpu *vcpu)
{
switch (vcpu->arch.sie_block->icptcode) {
- case 0x0:
- vcpu->stat.exit_null++;
- break;
case 0x10:
vcpu->stat.exit_external_request++;
break;
@@ -338,8 +335,10 @@ static int handle_partial_execution(struct kvm_vcpu *vcpu)
int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu)
{
+ if (kvm_is_ucontrol(vcpu->kvm))
+ return -EOPNOTSUPP;
+
switch (vcpu->arch.sie_block->icptcode) {
- case 0x00:
case 0x10:
case 0x18:
return handle_noop(vcpu);
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index 373e32346d68..f88ca72c3a77 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -34,6 +34,106 @@
#define PFAULT_DONE 0x0680
#define VIRTIO_PARAM 0x0d00
+/* handle external calls via sigp interpretation facility */
+static int sca_ext_call_pending(struct kvm_vcpu *vcpu, int *src_id)
+{
+ int c, scn;
+
+ if (!(atomic_read(&vcpu->arch.sie_block->cpuflags) & CPUSTAT_ECALL_PEND))
+ return 0;
+
+ read_lock(&vcpu->kvm->arch.sca_lock);
+ if (vcpu->kvm->arch.use_esca) {
+ struct esca_block *sca = vcpu->kvm->arch.sca;
+ union esca_sigp_ctrl sigp_ctrl =
+ sca->cpu[vcpu->vcpu_id].sigp_ctrl;
+
+ c = sigp_ctrl.c;
+ scn = sigp_ctrl.scn;
+ } else {
+ struct bsca_block *sca = vcpu->kvm->arch.sca;
+ union bsca_sigp_ctrl sigp_ctrl =
+ sca->cpu[vcpu->vcpu_id].sigp_ctrl;
+
+ c = sigp_ctrl.c;
+ scn = sigp_ctrl.scn;
+ }
+ read_unlock(&vcpu->kvm->arch.sca_lock);
+
+ if (src_id)
+ *src_id = scn;
+
+ return c;
+}
+
+static int sca_inject_ext_call(struct kvm_vcpu *vcpu, int src_id)
+{
+ int expect, rc;
+
+ read_lock(&vcpu->kvm->arch.sca_lock);
+ if (vcpu->kvm->arch.use_esca) {
+ struct esca_block *sca = vcpu->kvm->arch.sca;
+ union esca_sigp_ctrl *sigp_ctrl =
+ &(sca->cpu[vcpu->vcpu_id].sigp_ctrl);
+ union esca_sigp_ctrl new_val = {0}, old_val = *sigp_ctrl;
+
+ new_val.scn = src_id;
+ new_val.c = 1;
+ old_val.c = 0;
+
+ expect = old_val.value;
+ rc = cmpxchg(&sigp_ctrl->value, old_val.value, new_val.value);
+ } else {
+ struct bsca_block *sca = vcpu->kvm->arch.sca;
+ union bsca_sigp_ctrl *sigp_ctrl =
+ &(sca->cpu[vcpu->vcpu_id].sigp_ctrl);
+ union bsca_sigp_ctrl new_val = {0}, old_val = *sigp_ctrl;
+
+ new_val.scn = src_id;
+ new_val.c = 1;
+ old_val.c = 0;
+
+ expect = old_val.value;
+ rc = cmpxchg(&sigp_ctrl->value, old_val.value, new_val.value);
+ }
+ read_unlock(&vcpu->kvm->arch.sca_lock);
+
+ if (rc != expect) {
+ /* another external call is pending */
+ return -EBUSY;
+ }
+ atomic_or(CPUSTAT_ECALL_PEND, &vcpu->arch.sie_block->cpuflags);
+ return 0;
+}
+
+static void sca_clear_ext_call(struct kvm_vcpu *vcpu)
+{
+ struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
+ int rc, expect;
+
+ atomic_andnot(CPUSTAT_ECALL_PEND, li->cpuflags);
+ read_lock(&vcpu->kvm->arch.sca_lock);
+ if (vcpu->kvm->arch.use_esca) {
+ struct esca_block *sca = vcpu->kvm->arch.sca;
+ union esca_sigp_ctrl *sigp_ctrl =
+ &(sca->cpu[vcpu->vcpu_id].sigp_ctrl);
+ union esca_sigp_ctrl old = *sigp_ctrl;
+
+ expect = old.value;
+ rc = cmpxchg(&sigp_ctrl->value, old.value, 0);
+ } else {
+ struct bsca_block *sca = vcpu->kvm->arch.sca;
+ union bsca_sigp_ctrl *sigp_ctrl =
+ &(sca->cpu[vcpu->vcpu_id].sigp_ctrl);
+ union bsca_sigp_ctrl old = *sigp_ctrl;
+
+ expect = old.value;
+ rc = cmpxchg(&sigp_ctrl->value, old.value, 0);
+ }
+ read_unlock(&vcpu->kvm->arch.sca_lock);
+ WARN_ON(rc != expect); /* cannot clear? */
+}
+
int psw_extint_disabled(struct kvm_vcpu *vcpu)
{
return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_EXT);
@@ -399,9 +499,9 @@ static int __must_check __deliver_restart(struct kvm_vcpu *vcpu)
trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, KVM_S390_RESTART, 0, 0);
rc = write_guest_lc(vcpu,
- offsetof(struct _lowcore, restart_old_psw),
+ offsetof(struct lowcore, restart_old_psw),
&vcpu->arch.sie_block->gpsw, sizeof(psw_t));
- rc |= read_guest_lc(vcpu, offsetof(struct _lowcore, restart_psw),
+ rc |= read_guest_lc(vcpu, offsetof(struct lowcore, restart_psw),
&vcpu->arch.sie_block->gpsw, sizeof(psw_t));
clear_bit(IRQ_PEND_RESTART, &li->pending_irqs);
return rc ? -EFAULT : 0;
@@ -792,13 +892,11 @@ static const deliver_irq_t deliver_irq_funcs[] = {
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)
return test_bit(IRQ_PEND_EXT_EXTERNAL, &li->pending_irqs);
- return (sigp_ctrl & SIGP_CTRL_C) &&
- (atomic_read(&vcpu->arch.sie_block->cpuflags) & CPUSTAT_ECALL_PEND);
+ return sca_ext_call_pending(vcpu, NULL);
}
int kvm_s390_vcpu_has_irq(struct kvm_vcpu *vcpu, int exclude_stop)
@@ -909,9 +1007,7 @@ void kvm_s390_clear_local_irqs(struct kvm_vcpu *vcpu)
memset(&li->irq, 0, sizeof(li->irq));
spin_unlock(&li->lock);
- /* clear pending external calls set by sigp interpretation facility */
- atomic_andnot(CPUSTAT_ECALL_PEND, li->cpuflags);
- vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].sigp_ctrl = 0;
+ sca_clear_ext_call(vcpu);
}
int __must_check kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
@@ -1003,21 +1099,6 @@ static int __inject_pfault_init(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
return 0;
}
-static int __inject_extcall_sigpif(struct kvm_vcpu *vcpu, uint16_t src_id)
-{
- unsigned char new_val, old_val;
- uint8_t *sigp_ctrl = &vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].sigp_ctrl;
-
- new_val = SIGP_CTRL_C | (src_id & SIGP_CTRL_SCN_MASK);
- old_val = *sigp_ctrl & ~SIGP_CTRL_C;
- if (cmpxchg(sigp_ctrl, old_val, new_val) != old_val) {
- /* another external call is pending */
- return -EBUSY;
- }
- atomic_or(CPUSTAT_ECALL_PEND, &vcpu->arch.sie_block->cpuflags);
- return 0;
-}
-
static int __inject_extcall(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
{
struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
@@ -1030,12 +1111,11 @@ static int __inject_extcall(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
src_id, 0);
/* sending vcpu invalid */
- if (src_id >= KVM_MAX_VCPUS ||
- kvm_get_vcpu(vcpu->kvm, src_id) == NULL)
+ if (kvm_get_vcpu_by_id(vcpu->kvm, src_id) == NULL)
return -EINVAL;
if (sclp.has_sigpif)
- return __inject_extcall_sigpif(vcpu, src_id);
+ return sca_inject_ext_call(vcpu, src_id);
if (test_and_set_bit(IRQ_PEND_EXT_EXTERNAL, &li->pending_irqs))
return -EBUSY;
@@ -1110,6 +1190,10 @@ static int __inject_sigp_emergency(struct kvm_vcpu *vcpu,
trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_INT_EMERGENCY,
irq->u.emerg.code, 0);
+ /* sending vcpu invalid */
+ if (kvm_get_vcpu_by_id(vcpu->kvm, irq->u.emerg.code) == NULL)
+ return -EINVAL;
+
set_bit(irq->u.emerg.code, li->sigp_emerg_pending);
set_bit(IRQ_PEND_EXT_EMERGENCY, &li->pending_irqs);
atomic_or(CPUSTAT_EXT_INT, li->cpuflags);
@@ -2200,7 +2284,7 @@ static void store_local_irq(struct kvm_s390_local_interrupt *li,
int kvm_s390_get_irq_state(struct kvm_vcpu *vcpu, __u8 __user *buf, int len)
{
- uint8_t sigp_ctrl = vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].sigp_ctrl;
+ int scn;
unsigned long sigp_emerg_pending[BITS_TO_LONGS(KVM_MAX_VCPUS)];
struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
unsigned long pending_irqs;
@@ -2240,14 +2324,12 @@ int kvm_s390_get_irq_state(struct kvm_vcpu *vcpu, __u8 __user *buf, int len)
}
}
- if ((sigp_ctrl & SIGP_CTRL_C) &&
- (atomic_read(&vcpu->arch.sie_block->cpuflags) &
- CPUSTAT_ECALL_PEND)) {
+ if (sca_ext_call_pending(vcpu, &scn)) {
if (n + sizeof(irq) > len)
return -ENOBUFS;
memset(&irq, 0, sizeof(irq));
irq.type = KVM_S390_INT_EXTERNAL_CALL;
- irq.u.extcall.code = sigp_ctrl & SIGP_CTRL_SCN_MASK;
+ irq.u.extcall.code = scn;
if (copy_to_user(&buf[n], &irq, sizeof(irq)))
return -EFAULT;
n += sizeof(irq);
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 8fe2f1c722dc..835d60bedb54 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -246,7 +246,8 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
break;
case KVM_CAP_NR_VCPUS:
case KVM_CAP_MAX_VCPUS:
- r = KVM_MAX_VCPUS;
+ r = sclp.has_esca ? KVM_S390_ESCA_CPU_SLOTS
+ : KVM_S390_BSCA_CPU_SLOTS;
break;
case KVM_CAP_NR_MEMSLOTS:
r = KVM_USER_MEM_SLOTS;
@@ -257,6 +258,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
case KVM_CAP_S390_VECTOR_REGISTERS:
r = MACHINE_HAS_VX;
break;
+ case KVM_CAP_S390_RI:
+ r = test_facility(64);
+ break;
default:
r = 0;
}
@@ -283,6 +287,8 @@ static void kvm_s390_sync_dirty_log(struct kvm *kvm,
}
/* Section: vm related */
+static void sca_del_vcpu(struct kvm_vcpu *vcpu);
+
/*
* Get (and clear) the dirty memory log for a memory slot.
*/
@@ -342,15 +348,33 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap)
r = 0;
break;
case KVM_CAP_S390_VECTOR_REGISTERS:
- if (MACHINE_HAS_VX) {
+ mutex_lock(&kvm->lock);
+ if (atomic_read(&kvm->online_vcpus)) {
+ r = -EBUSY;
+ } else if (MACHINE_HAS_VX) {
set_kvm_facility(kvm->arch.model.fac->mask, 129);
set_kvm_facility(kvm->arch.model.fac->list, 129);
r = 0;
} else
r = -EINVAL;
+ mutex_unlock(&kvm->lock);
VM_EVENT(kvm, 3, "ENABLE: CAP_S390_VECTOR_REGISTERS %s",
r ? "(not available)" : "(success)");
break;
+ case KVM_CAP_S390_RI:
+ r = -EINVAL;
+ mutex_lock(&kvm->lock);
+ if (atomic_read(&kvm->online_vcpus)) {
+ r = -EBUSY;
+ } else if (test_facility(64)) {
+ set_kvm_facility(kvm->arch.model.fac->mask, 64);
+ set_kvm_facility(kvm->arch.model.fac->list, 64);
+ r = 0;
+ }
+ mutex_unlock(&kvm->lock);
+ VM_EVENT(kvm, 3, "ENABLE: CAP_S390_RI %s",
+ r ? "(not available)" : "(success)");
+ break;
case KVM_CAP_S390_USER_STSI:
VM_EVENT(kvm, 3, "%s", "ENABLE: CAP_S390_USER_STSI");
kvm->arch.user_stsi = 1;
@@ -371,8 +395,8 @@ static int kvm_s390_get_mem_control(struct kvm *kvm, struct kvm_device_attr *att
case KVM_S390_VM_MEM_LIMIT_SIZE:
ret = 0;
VM_EVENT(kvm, 3, "QUERY: max guest memory: %lu bytes",
- kvm->arch.gmap->asce_end);
- if (put_user(kvm->arch.gmap->asce_end, (u64 __user *)attr->addr))
+ kvm->arch.mem_limit);
+ if (put_user(kvm->arch.mem_limit, (u64 __user *)attr->addr))
ret = -EFAULT;
break;
default:
@@ -424,9 +448,17 @@ static int kvm_s390_set_mem_control(struct kvm *kvm, struct kvm_device_attr *att
if (get_user(new_limit, (u64 __user *)attr->addr))
return -EFAULT;
- if (new_limit > kvm->arch.gmap->asce_end)
+ if (kvm->arch.mem_limit != KVM_S390_NO_MEM_LIMIT &&
+ new_limit > kvm->arch.mem_limit)
return -E2BIG;
+ if (!new_limit)
+ return -EINVAL;
+
+ /* gmap_alloc takes last usable address */
+ if (new_limit != KVM_S390_NO_MEM_LIMIT)
+ new_limit -= 1;
+
ret = -EBUSY;
mutex_lock(&kvm->lock);
if (atomic_read(&kvm->online_vcpus) == 0) {
@@ -443,7 +475,9 @@ static int kvm_s390_set_mem_control(struct kvm *kvm, struct kvm_device_attr *att
}
}
mutex_unlock(&kvm->lock);
- VM_EVENT(kvm, 3, "SET: max guest memory: %lu bytes", new_limit);
+ VM_EVENT(kvm, 3, "SET: max guest address: %lu", new_limit);
+ VM_EVENT(kvm, 3, "New guest asce: 0x%pK",
+ (void *) kvm->arch.gmap->asce);
break;
}
default:
@@ -1020,7 +1054,7 @@ static int kvm_s390_apxa_installed(void)
u8 config[128];
int cc;
- if (test_facility(2) && test_facility(12)) {
+ if (test_facility(12)) {
cc = kvm_s390_query_ap_config(config);
if (cc)
@@ -1071,6 +1105,15 @@ static int kvm_s390_crypto_init(struct kvm *kvm)
return 0;
}
+static void sca_dispose(struct kvm *kvm)
+{
+ if (kvm->arch.use_esca)
+ free_pages_exact(kvm->arch.sca, sizeof(struct esca_block));
+ else
+ free_page((unsigned long)(kvm->arch.sca));
+ kvm->arch.sca = NULL;
+}
+
int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
{
int i, rc;
@@ -1094,14 +1137,17 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
rc = -ENOMEM;
- kvm->arch.sca = (struct sca_block *) get_zeroed_page(GFP_KERNEL);
+ kvm->arch.use_esca = 0; /* start with basic SCA */
+ rwlock_init(&kvm->arch.sca_lock);
+ kvm->arch.sca = (struct bsca_block *) get_zeroed_page(GFP_KERNEL);
if (!kvm->arch.sca)
goto out_err;
spin_lock(&kvm_lock);
sca_offset += 16;
- if (sca_offset + sizeof(struct sca_block) > PAGE_SIZE)
+ if (sca_offset + sizeof(struct bsca_block) > PAGE_SIZE)
sca_offset = 0;
- kvm->arch.sca = (struct sca_block *) ((char *) kvm->arch.sca + sca_offset);
+ kvm->arch.sca = (struct bsca_block *)
+ ((char *) kvm->arch.sca + sca_offset);
spin_unlock(&kvm_lock);
sprintf(debug_name, "kvm-%u", current->pid);
@@ -1153,8 +1199,14 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
if (type & KVM_VM_S390_UCONTROL) {
kvm->arch.gmap = NULL;
+ kvm->arch.mem_limit = KVM_S390_NO_MEM_LIMIT;
} else {
- kvm->arch.gmap = gmap_alloc(current->mm, (1UL << 44) - 1);
+ if (sclp.hamax == U64_MAX)
+ kvm->arch.mem_limit = TASK_MAX_SIZE;
+ else
+ kvm->arch.mem_limit = min_t(unsigned long, TASK_MAX_SIZE,
+ sclp.hamax + 1);
+ kvm->arch.gmap = gmap_alloc(current->mm, kvm->arch.mem_limit - 1);
if (!kvm->arch.gmap)
goto out_err;
kvm->arch.gmap->private = kvm;
@@ -1166,14 +1218,14 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
kvm->arch.epoch = 0;
spin_lock_init(&kvm->arch.start_stop_lock);
- KVM_EVENT(3, "vm 0x%p created by pid %u", kvm, current->pid);
+ KVM_EVENT(3, "vm 0x%pK created by pid %u", kvm, current->pid);
return 0;
out_err:
kfree(kvm->arch.crypto.crycb);
free_page((unsigned long)kvm->arch.model.fac);
debug_unregister(kvm->arch.dbf);
- free_page((unsigned long)(kvm->arch.sca));
+ sca_dispose(kvm);
KVM_EVENT(3, "creation of vm failed: %d", rc);
return rc;
}
@@ -1184,14 +1236,8 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
trace_kvm_s390_destroy_vcpu(vcpu->vcpu_id);
kvm_s390_clear_local_irqs(vcpu);
kvm_clear_async_pf_completion_queue(vcpu);
- if (!kvm_is_ucontrol(vcpu->kvm)) {
- clear_bit(63 - vcpu->vcpu_id,
- (unsigned long *) &vcpu->kvm->arch.sca->mcn);
- if (vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].sda ==
- (__u64) vcpu->arch.sie_block)
- vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].sda = 0;
- }
- smp_mb();
+ if (!kvm_is_ucontrol(vcpu->kvm))
+ sca_del_vcpu(vcpu);
if (kvm_is_ucontrol(vcpu->kvm))
gmap_free(vcpu->arch.gmap);
@@ -1224,14 +1270,14 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
{
kvm_free_vcpus(kvm);
free_page((unsigned long)kvm->arch.model.fac);
- free_page((unsigned long)(kvm->arch.sca));
+ sca_dispose(kvm);
debug_unregister(kvm->arch.dbf);
kfree(kvm->arch.crypto.crycb);
if (!kvm_is_ucontrol(kvm))
gmap_free(kvm->arch.gmap);
kvm_s390_destroy_adapters(kvm);
kvm_s390_clear_float_irqs(kvm);
- KVM_EVENT(3, "vm 0x%p destroyed", kvm);
+ KVM_EVENT(3, "vm 0x%pK destroyed", kvm);
}
/* Section: vcpu related */
@@ -1245,6 +1291,117 @@ static int __kvm_ucontrol_vcpu_init(struct kvm_vcpu *vcpu)
return 0;
}
+static void sca_del_vcpu(struct kvm_vcpu *vcpu)
+{
+ read_lock(&vcpu->kvm->arch.sca_lock);
+ if (vcpu->kvm->arch.use_esca) {
+ struct esca_block *sca = vcpu->kvm->arch.sca;
+
+ clear_bit_inv(vcpu->vcpu_id, (unsigned long *) sca->mcn);
+ sca->cpu[vcpu->vcpu_id].sda = 0;
+ } else {
+ struct bsca_block *sca = vcpu->kvm->arch.sca;
+
+ clear_bit_inv(vcpu->vcpu_id, (unsigned long *) &sca->mcn);
+ sca->cpu[vcpu->vcpu_id].sda = 0;
+ }
+ read_unlock(&vcpu->kvm->arch.sca_lock);
+}
+
+static void sca_add_vcpu(struct kvm_vcpu *vcpu)
+{
+ read_lock(&vcpu->kvm->arch.sca_lock);
+ if (vcpu->kvm->arch.use_esca) {
+ struct esca_block *sca = vcpu->kvm->arch.sca;
+
+ sca->cpu[vcpu->vcpu_id].sda = (__u64) vcpu->arch.sie_block;
+ vcpu->arch.sie_block->scaoh = (__u32)(((__u64)sca) >> 32);
+ vcpu->arch.sie_block->scaol = (__u32)(__u64)sca & ~0x3fU;
+ vcpu->arch.sie_block->ecb2 |= 0x04U;
+ set_bit_inv(vcpu->vcpu_id, (unsigned long *) sca->mcn);
+ } else {
+ struct bsca_block *sca = vcpu->kvm->arch.sca;
+
+ sca->cpu[vcpu->vcpu_id].sda = (__u64) vcpu->arch.sie_block;
+ vcpu->arch.sie_block->scaoh = (__u32)(((__u64)sca) >> 32);
+ vcpu->arch.sie_block->scaol = (__u32)(__u64)sca;
+ set_bit_inv(vcpu->vcpu_id, (unsigned long *) &sca->mcn);
+ }
+ read_unlock(&vcpu->kvm->arch.sca_lock);
+}
+
+/* Basic SCA to Extended SCA data copy routines */
+static inline void sca_copy_entry(struct esca_entry *d, struct bsca_entry *s)
+{
+ d->sda = s->sda;
+ d->sigp_ctrl.c = s->sigp_ctrl.c;
+ d->sigp_ctrl.scn = s->sigp_ctrl.scn;
+}
+
+static void sca_copy_b_to_e(struct esca_block *d, struct bsca_block *s)
+{
+ int i;
+
+ d->ipte_control = s->ipte_control;
+ d->mcn[0] = s->mcn;
+ for (i = 0; i < KVM_S390_BSCA_CPU_SLOTS; i++)
+ sca_copy_entry(&d->cpu[i], &s->cpu[i]);
+}
+
+static int sca_switch_to_extended(struct kvm *kvm)
+{
+ struct bsca_block *old_sca = kvm->arch.sca;
+ struct esca_block *new_sca;
+ struct kvm_vcpu *vcpu;
+ unsigned int vcpu_idx;
+ u32 scaol, scaoh;
+
+ new_sca = alloc_pages_exact(sizeof(*new_sca), GFP_KERNEL|__GFP_ZERO);
+ if (!new_sca)
+ return -ENOMEM;
+
+ scaoh = (u32)((u64)(new_sca) >> 32);
+ scaol = (u32)(u64)(new_sca) & ~0x3fU;
+
+ kvm_s390_vcpu_block_all(kvm);
+ write_lock(&kvm->arch.sca_lock);
+
+ sca_copy_b_to_e(new_sca, old_sca);
+
+ kvm_for_each_vcpu(vcpu_idx, vcpu, kvm) {
+ vcpu->arch.sie_block->scaoh = scaoh;
+ vcpu->arch.sie_block->scaol = scaol;
+ vcpu->arch.sie_block->ecb2 |= 0x04U;
+ }
+ kvm->arch.sca = new_sca;
+ kvm->arch.use_esca = 1;
+
+ write_unlock(&kvm->arch.sca_lock);
+ kvm_s390_vcpu_unblock_all(kvm);
+
+ free_page((unsigned long)old_sca);
+
+ VM_EVENT(kvm, 2, "Switched to ESCA (0x%pK -> 0x%pK)",
+ old_sca, kvm->arch.sca);
+ return 0;
+}
+
+static int sca_can_add_vcpu(struct kvm *kvm, unsigned int id)
+{
+ int rc;
+
+ if (id < KVM_S390_BSCA_CPU_SLOTS)
+ return true;
+ if (!sclp.has_esca)
+ return false;
+
+ mutex_lock(&kvm->lock);
+ rc = kvm->arch.use_esca ? 0 : sca_switch_to_extended(kvm);
+ mutex_unlock(&kvm->lock);
+
+ return rc == 0 && id < KVM_S390_ESCA_CPU_SLOTS;
+}
+
int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
{
vcpu->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID;
@@ -1255,6 +1412,8 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
KVM_SYNC_CRS |
KVM_SYNC_ARCH0 |
KVM_SYNC_PFAULT;
+ if (test_kvm_facility(vcpu->kvm, 64))
+ vcpu->run->kvm_valid_regs |= KVM_SYNC_RICCB;
if (test_kvm_facility(vcpu->kvm, 129))
vcpu->run->kvm_valid_regs |= KVM_SYNC_VRS;
@@ -1365,8 +1524,11 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
vcpu->arch.sie_block->epoch = vcpu->kvm->arch.epoch;
preempt_enable();
mutex_unlock(&vcpu->kvm->lock);
- if (!kvm_is_ucontrol(vcpu->kvm))
+ if (!kvm_is_ucontrol(vcpu->kvm)) {
vcpu->arch.gmap = vcpu->kvm->arch.gmap;
+ sca_add_vcpu(vcpu);
+ }
+
}
static void kvm_s390_vcpu_crypto_setup(struct kvm_vcpu *vcpu)
@@ -1435,10 +1597,13 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
vcpu->arch.sie_block->eca |= 1;
if (sclp.has_sigpif)
vcpu->arch.sie_block->eca |= 0x10000000U;
+ if (test_kvm_facility(vcpu->kvm, 64))
+ vcpu->arch.sie_block->ecb3 |= 0x01;
if (test_kvm_facility(vcpu->kvm, 129)) {
vcpu->arch.sie_block->eca |= 0x00020000;
vcpu->arch.sie_block->ecd |= 0x20000000;
}
+ vcpu->arch.sie_block->riccbd = (unsigned long) &vcpu->run->s.regs.riccb;
vcpu->arch.sie_block->ictl |= ICTL_ISKE | ICTL_SSKE | ICTL_RRBE;
if (vcpu->kvm->arch.use_cmma) {
@@ -1461,7 +1626,7 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
struct sie_page *sie_page;
int rc = -EINVAL;
- if (id >= KVM_MAX_VCPUS)
+ if (!kvm_is_ucontrol(kvm) && !sca_can_add_vcpu(kvm, id))
goto out;
rc = -ENOMEM;
@@ -1478,20 +1643,6 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
vcpu->arch.sie_block->itdba = (unsigned long) &sie_page->itdb;
vcpu->arch.sie_block->icpua = id;
- if (!kvm_is_ucontrol(kvm)) {
- if (!kvm->arch.sca) {
- WARN_ON_ONCE(1);
- goto out_free_cpu;
- }
- if (!kvm->arch.sca->cpu[id].sda)
- kvm->arch.sca->cpu[id].sda =
- (__u64) vcpu->arch.sie_block;
- vcpu->arch.sie_block->scaoh =
- (__u32)(((__u64)kvm->arch.sca) >> 32);
- vcpu->arch.sie_block->scaol = (__u32)(__u64)kvm->arch.sca;
- set_bit(63 - id, (unsigned long *) &kvm->arch.sca->mcn);
- }
-
spin_lock_init(&vcpu->arch.local_int.lock);
vcpu->arch.local_int.float_int = &kvm->arch.float_int;
vcpu->arch.local_int.wq = &vcpu->wq;
@@ -1505,15 +1656,13 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
*/
vcpu->arch.guest_fpregs.fprs = kzalloc(sizeof(freg_t) * __NUM_FPRS,
GFP_KERNEL);
- if (!vcpu->arch.guest_fpregs.fprs) {
- rc = -ENOMEM;
+ if (!vcpu->arch.guest_fpregs.fprs)
goto out_free_sie_block;
- }
rc = kvm_vcpu_init(vcpu, kvm, id);
if (rc)
goto out_free_sie_block;
- VM_EVENT(kvm, 3, "create cpu %d at %p, sie block at %p", id, vcpu,
+ VM_EVENT(kvm, 3, "create cpu %d at 0x%pK, sie block at 0x%pK", id, vcpu,
vcpu->arch.sie_block);
trace_kvm_s390_create_vcpu(id, vcpu, vcpu->arch.sie_block);
@@ -2009,7 +2158,8 @@ static int vcpu_pre_run(struct kvm_vcpu *vcpu)
*/
kvm_check_async_pf_completion(vcpu);
- memcpy(&vcpu->arch.sie_block->gg14, &vcpu->run->s.regs.gprs[14], 16);
+ vcpu->arch.sie_block->gg14 = vcpu->run->s.regs.gprs[14];
+ vcpu->arch.sie_block->gg15 = vcpu->run->s.regs.gprs[15];
if (need_resched())
schedule();
@@ -2067,8 +2217,6 @@ static int vcpu_post_run_fault_in_sie(struct kvm_vcpu *vcpu)
static int vcpu_post_run(struct kvm_vcpu *vcpu, int exit_reason)
{
- int rc = -1;
-
VCPU_EVENT(vcpu, 6, "exit sie icptcode %d",
vcpu->arch.sie_block->icptcode);
trace_kvm_s390_sie_exit(vcpu, vcpu->arch.sie_block->icptcode);
@@ -2076,40 +2224,36 @@ static int vcpu_post_run(struct kvm_vcpu *vcpu, int exit_reason)
if (guestdbg_enabled(vcpu))
kvm_s390_restore_guest_per_regs(vcpu);
- if (exit_reason >= 0) {
- rc = 0;
+ vcpu->run->s.regs.gprs[14] = vcpu->arch.sie_block->gg14;
+ vcpu->run->s.regs.gprs[15] = vcpu->arch.sie_block->gg15;
+
+ if (vcpu->arch.sie_block->icptcode > 0) {
+ int rc = kvm_handle_sie_intercept(vcpu);
+
+ if (rc != -EOPNOTSUPP)
+ return rc;
+ vcpu->run->exit_reason = KVM_EXIT_S390_SIEIC;
+ vcpu->run->s390_sieic.icptcode = vcpu->arch.sie_block->icptcode;
+ vcpu->run->s390_sieic.ipa = vcpu->arch.sie_block->ipa;
+ vcpu->run->s390_sieic.ipb = vcpu->arch.sie_block->ipb;
+ return -EREMOTE;
+ } else if (exit_reason != -EFAULT) {
+ vcpu->stat.exit_null++;
+ return 0;
} else if (kvm_is_ucontrol(vcpu->kvm)) {
vcpu->run->exit_reason = KVM_EXIT_S390_UCONTROL;
vcpu->run->s390_ucontrol.trans_exc_code =
current->thread.gmap_addr;
vcpu->run->s390_ucontrol.pgm_code = 0x10;
- rc = -EREMOTE;
-
+ return -EREMOTE;
} else if (current->thread.gmap_pfault) {
trace_kvm_s390_major_guest_pfault(vcpu);
current->thread.gmap_pfault = 0;
- if (kvm_arch_setup_async_pf(vcpu)) {
- rc = 0;
- } else {
- gpa_t gpa = current->thread.gmap_addr;
- rc = kvm_arch_fault_in_page(vcpu, gpa, 1);
- }
- }
-
- if (rc == -1)
- rc = vcpu_post_run_fault_in_sie(vcpu);
-
- memcpy(&vcpu->run->s.regs.gprs[14], &vcpu->arch.sie_block->gg14, 16);
-
- if (rc == 0) {
- if (kvm_is_ucontrol(vcpu->kvm))
- /* Don't exit for host interrupts. */
- rc = vcpu->arch.sie_block->icptcode ? -EOPNOTSUPP : 0;
- else
- rc = kvm_handle_sie_intercept(vcpu);
+ if (kvm_arch_setup_async_pf(vcpu))
+ return 0;
+ return kvm_arch_fault_in_page(vcpu, current->thread.gmap_addr, 1);
}
-
- return rc;
+ return vcpu_post_run_fault_in_sie(vcpu);
}
static int __vcpu_run(struct kvm_vcpu *vcpu)
@@ -2229,18 +2373,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
rc = 0;
}
- if (rc == -EOPNOTSUPP) {
- /* intercept cannot be handled in-kernel, prepare kvm-run */
- kvm_run->exit_reason = KVM_EXIT_S390_SIEIC;
- kvm_run->s390_sieic.icptcode = vcpu->arch.sie_block->icptcode;
- kvm_run->s390_sieic.ipa = vcpu->arch.sie_block->ipa;
- kvm_run->s390_sieic.ipb = vcpu->arch.sie_block->ipb;
- rc = 0;
- }
-
if (rc == -EREMOTE) {
- /* intercept was handled, but userspace support is needed
- * kvm_run has been prepared by the handler */
+ /* userspace support is needed, kvm_run has been prepared */
rc = 0;
}
@@ -2266,37 +2400,37 @@ int kvm_s390_store_status_unloaded(struct kvm_vcpu *vcpu, unsigned long gpa)
u64 clkcomp;
int rc;
+ px = kvm_s390_get_prefix(vcpu);
if (gpa == KVM_S390_STORE_STATUS_NOADDR) {
if (write_guest_abs(vcpu, 163, &archmode, 1))
return -EFAULT;
- gpa = SAVE_AREA_BASE;
+ gpa = 0;
} else if (gpa == KVM_S390_STORE_STATUS_PREFIXED) {
if (write_guest_real(vcpu, 163, &archmode, 1))
return -EFAULT;
- gpa = kvm_s390_real_to_abs(vcpu, SAVE_AREA_BASE);
- }
- rc = write_guest_abs(vcpu, gpa + offsetof(struct save_area, fp_regs),
+ gpa = px;
+ } else
+ gpa -= __LC_FPREGS_SAVE_AREA;
+ rc = write_guest_abs(vcpu, gpa + __LC_FPREGS_SAVE_AREA,
vcpu->arch.guest_fpregs.fprs, 128);
- rc |= write_guest_abs(vcpu, gpa + offsetof(struct save_area, gp_regs),
+ rc |= write_guest_abs(vcpu, gpa + __LC_GPREGS_SAVE_AREA,
vcpu->run->s.regs.gprs, 128);
- rc |= write_guest_abs(vcpu, gpa + offsetof(struct save_area, psw),
+ rc |= write_guest_abs(vcpu, gpa + __LC_PSW_SAVE_AREA,
&vcpu->arch.sie_block->gpsw, 16);
- px = kvm_s390_get_prefix(vcpu);
- rc |= write_guest_abs(vcpu, gpa + offsetof(struct save_area, pref_reg),
+ rc |= write_guest_abs(vcpu, gpa + __LC_PREFIX_SAVE_AREA,
&px, 4);
- rc |= write_guest_abs(vcpu,
- gpa + offsetof(struct save_area, fp_ctrl_reg),
+ rc |= write_guest_abs(vcpu, gpa + __LC_FP_CREG_SAVE_AREA,
&vcpu->arch.guest_fpregs.fpc, 4);
- rc |= write_guest_abs(vcpu, gpa + offsetof(struct save_area, tod_reg),
+ rc |= write_guest_abs(vcpu, gpa + __LC_TOD_PROGREG_SAVE_AREA,
&vcpu->arch.sie_block->todpr, 4);
- rc |= write_guest_abs(vcpu, gpa + offsetof(struct save_area, timer),
+ rc |= write_guest_abs(vcpu, gpa + __LC_CPU_TIMER_SAVE_AREA,
&vcpu->arch.sie_block->cputm, 8);
clkcomp = vcpu->arch.sie_block->ckc >> 8;
- rc |= write_guest_abs(vcpu, gpa + offsetof(struct save_area, clk_cmp),
+ rc |= write_guest_abs(vcpu, gpa + __LC_CLOCK_COMP_SAVE_AREA,
&clkcomp, 8);
- rc |= write_guest_abs(vcpu, gpa + offsetof(struct save_area, acc_regs),
+ rc |= write_guest_abs(vcpu, gpa + __LC_AREGS_SAVE_AREA,
&vcpu->run->s.regs.acrs, 64);
- rc |= write_guest_abs(vcpu, gpa + offsetof(struct save_area, ctrl_regs),
+ rc |= write_guest_abs(vcpu, gpa + __LC_CREGS_SAVE_AREA,
&vcpu->arch.sie_block->gcr, 128);
return rc ? -EFAULT : 0;
}
@@ -2732,6 +2866,9 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
if (mem->memory_size & 0xffffful)
return -EINVAL;
+ if (mem->guest_phys_addr + mem->memory_size > kvm->arch.mem_limit)
+ return -EINVAL;
+
return 0;
}
@@ -2763,6 +2900,11 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
static int __init kvm_s390_init(void)
{
+ if (!sclp.has_sief2) {
+ pr_info("SIE not available\n");
+ return -ENODEV;
+ }
+
return kvm_init(NULL, sizeof(struct kvm_vcpu), 0, THIS_MODULE);
}
diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h
index 1e70e00d3c5e..df1abada1f36 100644
--- a/arch/s390/kvm/kvm-s390.h
+++ b/arch/s390/kvm/kvm-s390.h
@@ -340,4 +340,11 @@ void kvm_s390_clear_bp_data(struct kvm_vcpu *vcpu);
void kvm_s390_prepare_debug_exit(struct kvm_vcpu *vcpu);
void kvm_s390_handle_per_event(struct kvm_vcpu *vcpu);
+/* support for Basic/Extended SCA handling */
+static inline union ipte_control *kvm_s390_get_ipte_control(struct kvm *kvm)
+{
+ struct bsca_block *sca = kvm->arch.sca; /* SCA version doesn't matter */
+
+ return &sca->ipte_control;
+}
#endif
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c
index 77191b85ea7a..ed74e86d9b9e 100644
--- a/arch/s390/kvm/priv.c
+++ b/arch/s390/kvm/priv.c
@@ -355,7 +355,7 @@ static int handle_stfl(struct kvm_vcpu *vcpu)
* into a u32 memory representation. They will remain bits 0-31.
*/
fac = *vcpu->kvm->arch.model.fac->list >> 32;
- rc = write_guest_lc(vcpu, offsetof(struct _lowcore, stfl_fac_list),
+ rc = write_guest_lc(vcpu, offsetof(struct lowcore, stfl_fac_list),
&fac, sizeof(fac));
if (rc)
return rc;
@@ -660,7 +660,7 @@ static int handle_pfmf(struct kvm_vcpu *vcpu)
kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
- if (!MACHINE_HAS_PFMF)
+ if (!test_kvm_facility(vcpu->kvm, 8))
return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
diff --git a/arch/s390/kvm/sigp.c b/arch/s390/kvm/sigp.c
index da690b69f9fe..77c22d685c7a 100644
--- a/arch/s390/kvm/sigp.c
+++ b/arch/s390/kvm/sigp.c
@@ -291,12 +291,8 @@ static int handle_sigp_dst(struct kvm_vcpu *vcpu, u8 order_code,
u16 cpu_addr, u32 parameter, u64 *status_reg)
{
int rc;
- struct kvm_vcpu *dst_vcpu;
+ struct kvm_vcpu *dst_vcpu = kvm_get_vcpu_by_id(vcpu->kvm, cpu_addr);
- if (cpu_addr >= KVM_MAX_VCPUS)
- return SIGP_CC_NOT_OPERATIONAL;
-
- dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr);
if (!dst_vcpu)
return SIGP_CC_NOT_OPERATIONAL;
@@ -478,7 +474,7 @@ int kvm_s390_handle_sigp_pei(struct kvm_vcpu *vcpu)
trace_kvm_s390_handle_sigp_pei(vcpu, order_code, cpu_addr);
if (order_code == SIGP_EXTERNAL_CALL) {
- dest_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr);
+ dest_vcpu = kvm_get_vcpu_by_id(vcpu->kvm, cpu_addr);
BUG_ON(dest_vcpu == NULL);
kvm_s390_vcpu_wakeup(dest_vcpu);
diff --git a/arch/s390/kvm/trace-s390.h b/arch/s390/kvm/trace-s390.h
index cc1d6c68356f..396485bca191 100644
--- a/arch/s390/kvm/trace-s390.h
+++ b/arch/s390/kvm/trace-s390.h
@@ -55,8 +55,8 @@ TRACE_EVENT(kvm_s390_create_vcpu,
__entry->sie_block = sie_block;
),
- TP_printk("create cpu %d at %p, sie block at %p", __entry->id,
- __entry->vcpu, __entry->sie_block)
+ TP_printk("create cpu %d at 0x%pK, sie block at 0x%pK",
+ __entry->id, __entry->vcpu, __entry->sie_block)
);
TRACE_EVENT(kvm_s390_destroy_vcpu,
@@ -254,7 +254,7 @@ TRACE_EVENT(kvm_s390_enable_css,
__entry->kvm = kvm;
),
- TP_printk("enabling channel I/O support (kvm @ %p)\n",
+ TP_printk("enabling channel I/O support (kvm @ %pK)\n",
__entry->kvm)
);
diff --git a/arch/s390/lib/spinlock.c b/arch/s390/lib/spinlock.c
index 427aa44b3505..d4549c964589 100644
--- a/arch/s390/lib/spinlock.c
+++ b/arch/s390/lib/spinlock.c
@@ -37,12 +37,22 @@ static inline void _raw_compare_and_delay(unsigned int *lock, unsigned int old)
asm(".insn rsy,0xeb0000000022,%0,0,%1" : : "d" (old), "Q" (*lock));
}
+static inline int cpu_is_preempted(int cpu)
+{
+ if (test_cpu_flag_of(CIF_ENABLED_WAIT, cpu))
+ return 0;
+ if (smp_vcpu_scheduled(cpu))
+ return 0;
+ return 1;
+}
+
void arch_spin_lock_wait(arch_spinlock_t *lp)
{
unsigned int cpu = SPINLOCK_LOCKVAL;
unsigned int owner;
- int count;
+ int count, first_diag;
+ first_diag = 1;
while (1) {
owner = ACCESS_ONCE(lp->lock);
/* Try to get the lock if it is free. */
@@ -51,9 +61,10 @@ void arch_spin_lock_wait(arch_spinlock_t *lp)
return;
continue;
}
- /* Check if the lock owner is running. */
- if (!smp_vcpu_scheduled(~owner)) {
+ /* First iteration: check if the lock owner is running. */
+ if (first_diag && cpu_is_preempted(~owner)) {
smp_yield_cpu(~owner);
+ first_diag = 0;
continue;
}
/* Loop for a while on the lock value. */
@@ -67,10 +78,13 @@ void arch_spin_lock_wait(arch_spinlock_t *lp)
continue;
/*
* For multiple layers of hypervisors, e.g. z/VM + LPAR
- * yield the CPU if the lock is still unavailable.
+ * yield the CPU unconditionally. For LPAR rely on the
+ * sense running status.
*/
- if (!MACHINE_IS_LPAR)
+ if (!MACHINE_IS_LPAR || cpu_is_preempted(~owner)) {
smp_yield_cpu(~owner);
+ first_diag = 0;
+ }
}
}
EXPORT_SYMBOL(arch_spin_lock_wait);
@@ -79,9 +93,10 @@ void arch_spin_lock_wait_flags(arch_spinlock_t *lp, unsigned long flags)
{
unsigned int cpu = SPINLOCK_LOCKVAL;
unsigned int owner;
- int count;
+ int count, first_diag;
local_irq_restore(flags);
+ first_diag = 1;
while (1) {
owner = ACCESS_ONCE(lp->lock);
/* Try to get the lock if it is free. */
@@ -92,8 +107,9 @@ void arch_spin_lock_wait_flags(arch_spinlock_t *lp, unsigned long flags)
local_irq_restore(flags);
}
/* Check if the lock owner is running. */
- if (!smp_vcpu_scheduled(~owner)) {
+ if (first_diag && cpu_is_preempted(~owner)) {
smp_yield_cpu(~owner);
+ first_diag = 0;
continue;
}
/* Loop for a while on the lock value. */
@@ -107,10 +123,13 @@ void arch_spin_lock_wait_flags(arch_spinlock_t *lp, unsigned long flags)
continue;
/*
* For multiple layers of hypervisors, e.g. z/VM + LPAR
- * yield the CPU if the lock is still unavailable.
+ * yield the CPU unconditionally. For LPAR rely on the
+ * sense running status.
*/
- if (!MACHINE_IS_LPAR)
+ if (!MACHINE_IS_LPAR || cpu_is_preempted(~owner)) {
smp_yield_cpu(~owner);
+ first_diag = 0;
+ }
}
}
EXPORT_SYMBOL(arch_spin_lock_wait_flags);
@@ -145,7 +164,7 @@ void _raw_read_lock_wait(arch_rwlock_t *rw)
owner = 0;
while (1) {
if (count-- <= 0) {
- if (owner && !smp_vcpu_scheduled(~owner))
+ if (owner && cpu_is_preempted(~owner))
smp_yield_cpu(~owner);
count = spin_retry;
}
@@ -191,7 +210,7 @@ void _raw_write_lock_wait(arch_rwlock_t *rw, unsigned int prev)
owner = 0;
while (1) {
if (count-- <= 0) {
- if (owner && !smp_vcpu_scheduled(~owner))
+ if (owner && cpu_is_preempted(~owner))
smp_yield_cpu(~owner);
count = spin_retry;
}
@@ -221,7 +240,7 @@ void _raw_write_lock_wait(arch_rwlock_t *rw)
owner = 0;
while (1) {
if (count-- <= 0) {
- if (owner && !smp_vcpu_scheduled(~owner))
+ if (owner && cpu_is_preempted(~owner))
smp_yield_cpu(~owner);
count = spin_retry;
}
@@ -265,7 +284,7 @@ void arch_lock_relax(unsigned int cpu)
{
if (!cpu)
return;
- if (MACHINE_IS_LPAR && smp_vcpu_scheduled(~cpu))
+ if (MACHINE_IS_LPAR && !cpu_is_preempted(~cpu))
return;
smp_yield_cpu(~cpu);
}
diff --git a/arch/s390/mm/extable.c b/arch/s390/mm/extable.c
index 4d1ee88864e8..18c8b819b0aa 100644
--- a/arch/s390/mm/extable.c
+++ b/arch/s390/mm/extable.c
@@ -52,12 +52,16 @@ void sort_extable(struct exception_table_entry *start,
int i;
/* Normalize entries to being relative to the start of the section */
- for (p = start, i = 0; p < finish; p++, i += 8)
+ for (p = start, i = 0; p < finish; p++, i += 8) {
p->insn += i;
+ p->fixup += i + 4;
+ }
sort(start, finish - start, sizeof(*start), cmp_ex, NULL);
/* Denormalize all entries */
- for (p = start, i = 0; p < finish; p++, i += 8)
+ for (p = start, i = 0; p < finish; p++, i += 8) {
p->insn -= i;
+ p->fixup -= i + 4;
+ }
}
#ifdef CONFIG_MODULES
diff --git a/arch/s390/mm/extmem.c b/arch/s390/mm/extmem.c
index 18fccc303db7..a1bf4ad8925d 100644
--- a/arch/s390/mm/extmem.c
+++ b/arch/s390/mm/extmem.c
@@ -94,7 +94,7 @@ static DEFINE_MUTEX(dcss_lock);
static LIST_HEAD(dcss_list);
static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC",
"EW/EN-MIXED" };
-static int loadshr_scode, loadnsr_scode, findseg_scode;
+static int loadshr_scode, loadnsr_scode;
static int segext_scode, purgeseg_scode;
static int scode_set;
@@ -130,7 +130,6 @@ dcss_set_subcodes(void)
loadshr_scode = DCSS_LOADSHRX;
loadnsr_scode = DCSS_LOADNSRX;
purgeseg_scode = DCSS_PURGESEG;
- findseg_scode = DCSS_FINDSEGX;
segext_scode = DCSS_SEGEXTX;
return 0;
}
@@ -138,7 +137,6 @@ dcss_set_subcodes(void)
loadshr_scode = DCSS_LOADNOLY;
loadnsr_scode = DCSS_LOADNSR;
purgeseg_scode = DCSS_PURGESEG;
- findseg_scode = DCSS_FINDSEG;
segext_scode = DCSS_SEGEXT;
return 0;
}
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c
index ec1a30d0d11a..1b903f6ad54a 100644
--- a/arch/s390/mm/fault.c
+++ b/arch/s390/mm/fault.c
@@ -254,7 +254,6 @@ static noinline void do_sigsegv(struct pt_regs *regs, int si_code)
static noinline void do_no_context(struct pt_regs *regs)
{
const struct exception_table_entry *fixup;
- unsigned long address;
/* Are we prepared to handle this kernel fault? */
fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN);
@@ -267,7 +266,6 @@ static noinline void do_no_context(struct pt_regs *regs)
* Oops. The kernel tried to access some bad page. We'll have to
* terminate things with extreme prejudice.
*/
- address = regs->int_parm_long & __FAIL_ADDR_MASK;
if (!user_space_fault(regs))
printk(KERN_ALERT "Unable to handle kernel pointer dereference"
" in virtual kernel address space\n");
diff --git a/arch/s390/mm/gup.c b/arch/s390/mm/gup.c
index 12bbf0e8478f..21c74a71e2ab 100644
--- a/arch/s390/mm/gup.c
+++ b/arch/s390/mm/gup.c
@@ -233,6 +233,7 @@ int get_user_pages_fast(unsigned long start, int nr_pages, int write,
struct mm_struct *mm = current->mm;
int nr, ret;
+ might_sleep();
start &= PAGE_MASK;
nr = __get_user_pages_fast(start, nr_pages, write, pages);
if (nr == nr_pages)
diff --git a/arch/s390/mm/maccess.c b/arch/s390/mm/maccess.c
index 8a993a53fcd6..fec59c067d0d 100644
--- a/arch/s390/mm/maccess.c
+++ b/arch/s390/mm/maccess.c
@@ -163,11 +163,11 @@ static int is_swapped(unsigned long addr)
unsigned long lc;
int cpu;
- if (addr < sizeof(struct _lowcore))
+ if (addr < sizeof(struct lowcore))
return 1;
for_each_online_cpu(cpu) {
lc = (unsigned long) lowcore_ptr[cpu];
- if (addr > lc + sizeof(struct _lowcore) - 1 || addr < lc)
+ if (addr > lc + sizeof(struct lowcore) - 1 || addr < lc)
continue;
return 1;
}
diff --git a/arch/s390/mm/mem_detect.c b/arch/s390/mm/mem_detect.c
index e00f0d5d296d..d612cc3eec6a 100644
--- a/arch/s390/mm/mem_detect.c
+++ b/arch/s390/mm/mem_detect.c
@@ -14,8 +14,6 @@
#include <asm/sclp.h>
#include <asm/setup.h>
-#define ADDR2G (1ULL << 31)
-
#define CHUNK_READ_WRITE 0
#define CHUNK_READ_ONLY 1
@@ -27,15 +25,14 @@ static inline void memblock_physmem_add(phys_addr_t start, phys_addr_t size)
void __init detect_memory_memblock(void)
{
- unsigned long long memsize, rnmax, rzm;
- unsigned long addr, size;
+ unsigned long memsize, rnmax, rzm, addr, size;
int type;
rzm = sclp.rzm;
rnmax = sclp.rnmax;
memsize = rzm * rnmax;
if (!rzm)
- rzm = 1ULL << 17;
+ rzm = 1UL << 17;
max_physmem_end = memsize;
addr = 0;
/* keep memblock lists close to the kernel */
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c
index 54ef3bc01b43..aa34af0a0b26 100644
--- a/arch/s390/mm/pgtable.c
+++ b/arch/s390/mm/pgtable.c
@@ -133,7 +133,7 @@ void crst_table_downgrade(struct mm_struct *mm, unsigned long limit)
/**
* gmap_alloc - allocate a guest address space
* @mm: pointer to the parent mm_struct
- * @limit: maximum size of the gmap address space
+ * @limit: maximum address of the gmap address space
*
* Returns a guest address space structure.
*/
@@ -402,7 +402,7 @@ int gmap_map_segment(struct gmap *gmap, unsigned long from,
if ((from | to | len) & (PMD_SIZE - 1))
return -EINVAL;
if (len == 0 || from + len < from || to + len < to ||
- from + len > TASK_MAX_SIZE || to + len > gmap->asce_end)
+ from + len - 1 > TASK_MAX_SIZE || to + len - 1 > gmap->asce_end)
return -EINVAL;
flush = 0;
@@ -603,10 +603,7 @@ static void gmap_zap_swap_entry(swp_entry_t entry, struct mm_struct *mm)
else if (is_migration_entry(entry)) {
struct page *page = migration_entry_to_page(entry);
- if (PageAnon(page))
- dec_mm_counter(mm, MM_ANONPAGES);
- else
- dec_mm_counter(mm, MM_FILEPAGES);
+ dec_mm_counter(mm, mm_counter(page));
}
free_swap_and_cache(entry);
}
diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c
index 9a0c4c22e536..3c0bfc1f2694 100644
--- a/arch/s390/net/bpf_jit_comp.c
+++ b/arch/s390/net/bpf_jit_comp.c
@@ -408,7 +408,7 @@ static void emit_load_skb_data_hlen(struct bpf_jit *jit)
* Save registers and create stack frame if necessary.
* See stack frame layout desription in "bpf_jit.h"!
*/
-static void bpf_jit_prologue(struct bpf_jit *jit, bool is_classic)
+static void bpf_jit_prologue(struct bpf_jit *jit)
{
if (jit->seen & SEEN_TAIL_CALL) {
/* xc STK_OFF_TCCNT(4,%r15),STK_OFF_TCCNT(%r15) */
@@ -448,15 +448,6 @@ static void bpf_jit_prologue(struct bpf_jit *jit, bool is_classic)
/* stg %b1,ST_OFF_SKBP(%r0,%r15) */
EMIT6_DISP_LH(0xe3000000, 0x0024, REG_W1, REG_0, REG_15,
STK_OFF_SKBP);
- /* Clear A (%b0) and X (%b7) registers for converted BPF programs */
- if (is_classic) {
- if (REG_SEEN(BPF_REG_A))
- /* lghi %ba,0 */
- EMIT4_IMM(0xa7090000, BPF_REG_A, 0);
- if (REG_SEEN(BPF_REG_X))
- /* lghi %bx,0 */
- EMIT4_IMM(0xa7090000, BPF_REG_X, 0);
- }
}
/*
@@ -1245,7 +1236,7 @@ static int bpf_jit_prog(struct bpf_jit *jit, struct bpf_prog *fp)
jit->lit = jit->lit_start;
jit->prg = 0;
- bpf_jit_prologue(jit, bpf_prog_was_classic(fp));
+ bpf_jit_prologue(jit);
for (i = 0; i < fp->len; i += insn_count) {
insn_count = bpf_jit_insn(jit, fp, i);
if (insn_count < 0)
diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c
index 7ef12a3ace3a..11d4f277e9f6 100644
--- a/arch/s390/pci/pci.c
+++ b/arch/s390/pci/pci.c
@@ -701,8 +701,7 @@ static int zpci_restore(struct device *dev)
goto out;
zpci_map_resources(pdev);
- zpci_register_ioat(zdev, 0, zdev->start_dma + PAGE_OFFSET,
- zdev->start_dma + zdev->iommu_size - 1,
+ zpci_register_ioat(zdev, 0, zdev->start_dma, zdev->end_dma,
(u64) zdev->dma_table);
out:
diff --git a/arch/s390/pci/pci_dma.c b/arch/s390/pci/pci_dma.c
index d348f2c09a1e..4638b93c7632 100644
--- a/arch/s390/pci/pci_dma.c
+++ b/arch/s390/pci/pci_dma.c
@@ -366,8 +366,7 @@ static void *s390_dma_alloc(struct device *dev, size_t size,
pa = page_to_phys(page);
memset((void *) pa, 0, size);
- map = s390_dma_map_pages(dev, page, pa % PAGE_SIZE,
- size, DMA_BIDIRECTIONAL, NULL);
+ map = s390_dma_map_pages(dev, page, 0, size, DMA_BIDIRECTIONAL, NULL);
if (dma_mapping_error(dev, map)) {
free_pages(pa, get_order(size));
return NULL;
@@ -458,7 +457,19 @@ int zpci_dma_init_device(struct zpci_dev *zdev)
goto out_clean;
}
- zdev->iommu_size = (unsigned long) high_memory - PAGE_OFFSET;
+ /*
+ * Restrict the iommu bitmap size to the minimum of the following:
+ * - main memory size
+ * - 3-level pagetable address limit minus start_dma offset
+ * - DMA address range allowed by the hardware (clp query pci fn)
+ *
+ * Also set zdev->end_dma to the actual end address of the usable
+ * range, instead of the theoretical maximum as reported by hardware.
+ */
+ zdev->iommu_size = min3((u64) high_memory,
+ ZPCI_TABLE_SIZE_RT - zdev->start_dma,
+ zdev->end_dma - zdev->start_dma + 1);
+ zdev->end_dma = zdev->start_dma + zdev->iommu_size - 1;
zdev->iommu_pages = zdev->iommu_size >> PAGE_SHIFT;
zdev->iommu_bitmap = vzalloc(zdev->iommu_pages / 8);
if (!zdev->iommu_bitmap) {
@@ -466,10 +477,7 @@ int zpci_dma_init_device(struct zpci_dev *zdev)
goto out_reg;
}
- rc = zpci_register_ioat(zdev,
- 0,
- zdev->start_dma + PAGE_OFFSET,
- zdev->start_dma + zdev->iommu_size - 1,
+ rc = zpci_register_ioat(zdev, 0, zdev->start_dma, zdev->end_dma,
(u64) zdev->dma_table);
if (rc)
goto out_reg;
diff --git a/arch/s390/tools/.gitignore b/arch/s390/tools/.gitignore
new file mode 100644
index 000000000000..72a4b2cf1365
--- /dev/null
+++ b/arch/s390/tools/.gitignore
@@ -0,0 +1 @@
+gen_facilities
diff --git a/arch/s390/tools/Makefile b/arch/s390/tools/Makefile
new file mode 100644
index 000000000000..6d9814c9df2b
--- /dev/null
+++ b/arch/s390/tools/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for s390 specific build tools
+#
+
+hostprogs-y += gen_facilities
+HOSTCFLAGS_gen_facilities.o += -Wall $(LINUXINCLUDE)
+
+define filechk_facilities.h
+ $(obj)/gen_facilities
+endef
+
+$(obj)/gen_facilities.o: $(srctree)/arch/s390/tools/gen_facilities.c
+
+include/generated/facilities.h: $(obj)/gen_facilities FORCE
+ $(call filechk,facilities.h)
diff --git a/arch/s390/tools/gen_facilities.c b/arch/s390/tools/gen_facilities.c
new file mode 100644
index 000000000000..e2660d27889b
--- /dev/null
+++ b/arch/s390/tools/gen_facilities.c
@@ -0,0 +1,67 @@
+/*
+ * Simple program to generate defines out of facility lists that use the bit
+ * numbering scheme from the Princples of Operations: most significant bit
+ * has bit number 0.
+ *
+ * Copyright IBM Corp. 2015
+ *
+ */
+
+#define S390_GEN_FACILITIES_C
+
+#include <strings.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <asm/facilities_src.h>
+
+static void print_facility_list(struct facility_def *def)
+{
+ unsigned int high, bit, dword, i;
+ unsigned long long *array;
+
+ array = calloc(1, 8);
+ if (!array)
+ exit(EXIT_FAILURE);
+ high = 0;
+ for (i = 0; def->bits[i] != -1; i++) {
+ bit = 63 - (def->bits[i] & 63);
+ dword = def->bits[i] / 64;
+ if (dword > high) {
+ array = realloc(array, (dword + 1) * 8);
+ if (!array)
+ exit(EXIT_FAILURE);
+ memset(array + high + 1, 0, (dword - high) * 8);
+ high = dword;
+ }
+ array[dword] |= 1ULL << bit;
+ }
+ printf("#define %s ", def->name);
+ for (i = 0; i <= high; i++)
+ printf("_AC(0x%016llx,UL)%c", array[i], i < high ? ',' : '\n');
+ printf("#define %s_DWORDS %d\n", def->name, high + 1);
+ free(array);
+}
+
+static void print_facility_lists(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(facility_defs) / sizeof(facility_defs[0]); i++)
+ print_facility_list(&facility_defs[i]);
+}
+
+int main(int argc, char **argv)
+{
+ printf("#ifndef __ASM_S390_FACILITIES__\n");
+ printf("#define __ASM_S390_FACILITIES__\n");
+ printf("/*\n");
+ printf(" * DO NOT MODIFY.\n");
+ printf(" *\n");
+ printf(" * This file was generated by %s\n", __FILE__);
+ printf(" */\n\n");
+ printf("#include <linux/const.h>\n\n");
+ print_facility_lists();
+ printf("\n#endif\n");
+ return 0;
+}
diff --git a/arch/sh/boards/mach-ap325rxa/setup.c b/arch/sh/boards/mach-ap325rxa/setup.c
index cbd2a9f02a91..62c3b81300ed 100644
--- a/arch/sh/boards/mach-ap325rxa/setup.c
+++ b/arch/sh/boards/mach-ap325rxa/setup.c
@@ -27,10 +27,10 @@
#include <linux/gpio.h>
#include <linux/videodev2.h>
#include <linux/sh_intc.h>
-#include <media/ov772x.h>
+#include <media/i2c/ov772x.h>
#include <media/soc_camera.h>
-#include <media/soc_camera_platform.h>
-#include <media/sh_mobile_ceu.h>
+#include <linux/platform_data/media/soc_camera_platform.h>
+#include <media/drv-intf/sh_mobile_ceu.h>
#include <video/sh_mobile_lcdc.h>
#include <asm/io.h>
#include <asm/clock.h>
diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c
index d531791f06ff..a9c0c07386fd 100644
--- a/arch/sh/boards/mach-ecovec24/setup.c
+++ b/arch/sh/boards/mach-ecovec24/setup.c
@@ -38,10 +38,10 @@
#include <video/sh_mobile_lcdc.h>
#include <sound/sh_fsi.h>
#include <sound/simple_card.h>
-#include <media/sh_mobile_ceu.h>
+#include <media/drv-intf/sh_mobile_ceu.h>
#include <media/soc_camera.h>
-#include <media/tw9910.h>
-#include <media/mt9t112.h>
+#include <media/i2c/tw9910.h>
+#include <media/i2c/mt9t112.h>
#include <asm/heartbeat.h>
#include <asm/clock.h>
#include <asm/suspend.h>
@@ -900,8 +900,8 @@ static struct platform_device irda_device = {
.resource = irda_resources,
};
-#include <media/ak881x.h>
-#include <media/sh_vou.h>
+#include <media/i2c/ak881x.h>
+#include <media/drv-intf/sh_vou.h>
static struct ak881x_pdata ak881x_pdata = {
.flags = AK881X_IF_MODE_SLAVE,
diff --git a/arch/sh/boards/mach-kfr2r09/setup.c b/arch/sh/boards/mach-kfr2r09/setup.c
index 7d997cec09c5..6bd9230e64e3 100644
--- a/arch/sh/boards/mach-kfr2r09/setup.c
+++ b/arch/sh/boards/mach-kfr2r09/setup.c
@@ -27,9 +27,9 @@
#include <linux/usb/r8a66597.h>
#include <linux/videodev2.h>
#include <linux/sh_intc.h>
-#include <media/rj54n1cb0c.h>
+#include <media/i2c/rj54n1cb0c.h>
#include <media/soc_camera.h>
-#include <media/sh_mobile_ceu.h>
+#include <media/drv-intf/sh_mobile_ceu.h>
#include <video/sh_mobile_lcdc.h>
#include <asm/suspend.h>
#include <asm/clock.h>
diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c
index 29b7c0dcfc51..7a04da3efce4 100644
--- a/arch/sh/boards/mach-migor/setup.c
+++ b/arch/sh/boards/mach-migor/setup.c
@@ -27,10 +27,10 @@
#include <linux/videodev2.h>
#include <linux/sh_intc.h>
#include <video/sh_mobile_lcdc.h>
-#include <media/sh_mobile_ceu.h>
-#include <media/ov772x.h>
+#include <media/drv-intf/sh_mobile_ceu.h>
+#include <media/i2c/ov772x.h>
#include <media/soc_camera.h>
-#include <media/tw9910.h>
+#include <media/i2c/tw9910.h>
#include <asm/clock.h>
#include <asm/machvec.h>
#include <asm/io.h>
@@ -167,7 +167,7 @@ static struct mtd_partition migor_nand_flash_partitions[] = {
static void migor_nand_flash_cmd_ctl(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
if (cmd == NAND_CMD_NONE)
return;
diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c
index 4f6635a075f2..e0e1df136642 100644
--- a/arch/sh/boards/mach-se/7724/setup.c
+++ b/arch/sh/boards/mach-se/7724/setup.c
@@ -30,7 +30,7 @@
#include <linux/sh_intc.h>
#include <linux/videodev2.h>
#include <video/sh_mobile_lcdc.h>
-#include <media/sh_mobile_ceu.h>
+#include <media/drv-intf/sh_mobile_ceu.h>
#include <sound/sh_fsi.h>
#include <sound/simple_card.h>
#include <asm/io.h>
@@ -534,8 +534,8 @@ static struct platform_device irda_device = {
.resource = irda_resources,
};
-#include <media/ak881x.h>
-#include <media/sh_vou.h>
+#include <media/i2c/ak881x.h>
+#include <media/drv-intf/sh_vou.h>
static struct ak881x_pdata ak881x_pdata = {
.flags = AK881X_IF_MODE_SLAVE,
diff --git a/arch/sh/include/uapi/asm/unistd_64.h b/arch/sh/include/uapi/asm/unistd_64.h
index e6820c86e8c7..47ebd5b5ed55 100644
--- a/arch/sh/include/uapi/asm/unistd_64.h
+++ b/arch/sh/include/uapi/asm/unistd_64.h
@@ -278,7 +278,7 @@
#define __NR_fsetxattr 256
#define __NR_getxattr 257
#define __NR_lgetxattr 258
-#define __NR_fgetxattr 269
+#define __NR_fgetxattr 259
#define __NR_listxattr 260
#define __NR_llistxattr 261
#define __NR_flistxattr 262
diff --git a/arch/sh/kernel/cpu/clock-cpg.c b/arch/sh/kernel/cpu/clock-cpg.c
index 8525a671266f..786c0769b4c3 100644
--- a/arch/sh/kernel/cpu/clock-cpg.c
+++ b/arch/sh/kernel/cpu/clock-cpg.c
@@ -63,7 +63,6 @@ int __init __deprecated cpg_clk_init(void)
clk_add_alias("fck", "sh-mtu2", "peripheral_clk", NULL);
clk_add_alias("fck", "sh-cmt-16.0", "peripheral_clk", NULL);
clk_add_alias("fck", "sh-cmt-32.0", "peripheral_clk", NULL);
- clk_add_alias("sci_ick", NULL, "peripheral_clk", NULL);
return ret;
}
diff --git a/arch/sh/kernel/cpu/sh2a/clock-sh7264.c b/arch/sh/kernel/cpu/sh2a/clock-sh7264.c
index 8638fba6cd7f..7e06e39b0958 100644
--- a/arch/sh/kernel/cpu/sh2a/clock-sh7264.c
+++ b/arch/sh/kernel/cpu/sh2a/clock-sh7264.c
@@ -115,7 +115,14 @@ static struct clk_lookup lookups[] = {
CLKDEV_CON_ID("peripheral_clk", &div4_clks[DIV4_P]),
/* MSTP clocks */
- CLKDEV_CON_ID("sci_ick", &mstp_clks[MSTP77]),
+ CLKDEV_ICK_ID("fck", "sh-sci.0", &mstp_clks[MSTP77]),
+ CLKDEV_ICK_ID("fck", "sh-sci.1", &mstp_clks[MSTP77]),
+ CLKDEV_ICK_ID("fck", "sh-sci.2", &mstp_clks[MSTP77]),
+ CLKDEV_ICK_ID("fck", "sh-sci.3", &mstp_clks[MSTP77]),
+ CLKDEV_ICK_ID("fck", "sh-sci.4", &mstp_clks[MSTP77]),
+ CLKDEV_ICK_ID("fck", "sh-sci.5", &mstp_clks[MSTP77]),
+ CLKDEV_ICK_ID("fck", "sh-sci.6", &mstp_clks[MSTP77]),
+ CLKDEV_ICK_ID("fck", "sh-sci.7", &mstp_clks[MSTP77]),
CLKDEV_CON_ID("vdc3", &mstp_clks[MSTP74]),
CLKDEV_ICK_ID("fck", "sh-cmt-16.0", &mstp_clks[MSTP72]),
CLKDEV_CON_ID("usb0", &mstp_clks[MSTP60]),
diff --git a/arch/sh/kernel/cpu/sh2a/clock-sh7269.c b/arch/sh/kernel/cpu/sh2a/clock-sh7269.c
index f8a5c2abdfb3..663a97bed554 100644
--- a/arch/sh/kernel/cpu/sh2a/clock-sh7269.c
+++ b/arch/sh/kernel/cpu/sh2a/clock-sh7269.c
@@ -150,14 +150,14 @@ static struct clk_lookup lookups[] = {
CLKDEV_CON_ID("bus_clk", &div4_clks[DIV4_B]),
/* MSTP clocks */
- CLKDEV_ICK_ID("sci_fck", "sh-sci.0", &mstp_clks[MSTP47]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.1", &mstp_clks[MSTP46]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.2", &mstp_clks[MSTP45]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.3", &mstp_clks[MSTP44]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.4", &mstp_clks[MSTP43]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.5", &mstp_clks[MSTP42]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.6", &mstp_clks[MSTP41]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.7", &mstp_clks[MSTP40]),
+ CLKDEV_ICK_ID("fck", "sh-sci.0", &mstp_clks[MSTP47]),
+ CLKDEV_ICK_ID("fck", "sh-sci.1", &mstp_clks[MSTP46]),
+ CLKDEV_ICK_ID("fck", "sh-sci.2", &mstp_clks[MSTP45]),
+ CLKDEV_ICK_ID("fck", "sh-sci.3", &mstp_clks[MSTP44]),
+ CLKDEV_ICK_ID("fck", "sh-sci.4", &mstp_clks[MSTP43]),
+ CLKDEV_ICK_ID("fck", "sh-sci.5", &mstp_clks[MSTP42]),
+ CLKDEV_ICK_ID("fck", "sh-sci.6", &mstp_clks[MSTP41]),
+ CLKDEV_ICK_ID("fck", "sh-sci.7", &mstp_clks[MSTP40]),
CLKDEV_ICK_ID("fck", "sh-cmt-16.0", &mstp_clks[MSTP72]),
CLKDEV_CON_ID("usb0", &mstp_clks[MSTP60]),
CLKDEV_ICK_ID("fck", "sh-mtu2", &mstp_clks[MSTP35]),
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7343.c b/arch/sh/kernel/cpu/sh4a/clock-sh7343.c
index 9edc06c02dcf..a907ee2388bf 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7343.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7343.c
@@ -232,10 +232,10 @@ static struct clk_lookup lookups[] = {
CLKDEV_CON_ID("mfi0", &mstp_clks[MSTP011]),
CLKDEV_CON_ID("flctl0", &mstp_clks[MSTP010]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.0", &mstp_clks[MSTP007]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.1", &mstp_clks[MSTP006]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.2", &mstp_clks[MSTP005]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.3", &mstp_clks[MSTP004]),
+ CLKDEV_ICK_ID("fck", "sh-sci.0", &mstp_clks[MSTP007]),
+ CLKDEV_ICK_ID("fck", "sh-sci.1", &mstp_clks[MSTP006]),
+ CLKDEV_ICK_ID("fck", "sh-sci.2", &mstp_clks[MSTP005]),
+ CLKDEV_ICK_ID("fck", "sh-sci.3", &mstp_clks[MSTP004]),
CLKDEV_CON_ID("sio0", &mstp_clks[MSTP003]),
CLKDEV_CON_ID("siof0", &mstp_clks[MSTP002]),
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7366.c b/arch/sh/kernel/cpu/sh4a/clock-sh7366.c
index 955b9add7810..ac9854179dee 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7366.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7366.c
@@ -230,9 +230,9 @@ static struct clk_lookup lookups[] = {
CLKDEV_CON_ID("mfi0", &mstp_clks[MSTP011]),
CLKDEV_CON_ID("flctl0", &mstp_clks[MSTP010]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.0", &mstp_clks[MSTP007]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.1", &mstp_clks[MSTP006]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.2", &mstp_clks[MSTP005]),
+ CLKDEV_ICK_ID("fck", "sh-sci.0", &mstp_clks[MSTP007]),
+ CLKDEV_ICK_ID("fck", "sh-sci.1", &mstp_clks[MSTP006]),
+ CLKDEV_ICK_ID("fck", "sh-sci.2", &mstp_clks[MSTP005]),
CLKDEV_CON_ID("msiof0", &mstp_clks[MSTP002]),
CLKDEV_CON_ID("sbr0", &mstp_clks[MSTP001]),
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7723.c b/arch/sh/kernel/cpu/sh4a/clock-sh7723.c
index ccbcab550df2..fe844222f3f6 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7723.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7723.c
@@ -267,12 +267,12 @@ static struct clk_lookup lookups[] = {
CLKDEV_ICK_ID("fck", "sh-tmu.0", &mstp_clks[HWBLK_TMU0]),
CLKDEV_ICK_ID("fck", "sh-tmu.1", &mstp_clks[HWBLK_TMU1]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.0", &mstp_clks[HWBLK_SCIF0]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.1", &mstp_clks[HWBLK_SCIF1]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.2", &mstp_clks[HWBLK_SCIF2]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.3", &mstp_clks[HWBLK_SCIF3]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.4", &mstp_clks[HWBLK_SCIF4]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.5", &mstp_clks[HWBLK_SCIF5]),
+ CLKDEV_ICK_ID("fck", "sh-sci.0", &mstp_clks[HWBLK_SCIF0]),
+ CLKDEV_ICK_ID("fck", "sh-sci.1", &mstp_clks[HWBLK_SCIF1]),
+ CLKDEV_ICK_ID("fck", "sh-sci.2", &mstp_clks[HWBLK_SCIF2]),
+ CLKDEV_ICK_ID("fck", "sh-sci.3", &mstp_clks[HWBLK_SCIF3]),
+ CLKDEV_ICK_ID("fck", "sh-sci.4", &mstp_clks[HWBLK_SCIF4]),
+ CLKDEV_ICK_ID("fck", "sh-sci.5", &mstp_clks[HWBLK_SCIF5]),
CLKDEV_DEV_ID("sh_mobile_lcdc_fb.0", &mstp_clks[HWBLK_LCDC]),
};
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7734.c b/arch/sh/kernel/cpu/sh4a/clock-sh7734.c
index 7f54bf2f453d..354dcac5e4cd 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7734.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7734.c
@@ -194,12 +194,12 @@ static struct clk_lookup lookups[] = {
/* MSTP32 clocks */
CLKDEV_DEV_ID("i2c-sh7734.0", &mstp_clks[MSTP030]),
CLKDEV_DEV_ID("i2c-sh7734.1", &mstp_clks[MSTP029]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.0", &mstp_clks[MSTP026]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.1", &mstp_clks[MSTP025]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.2", &mstp_clks[MSTP024]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.3", &mstp_clks[MSTP023]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.4", &mstp_clks[MSTP022]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.5", &mstp_clks[MSTP021]),
+ CLKDEV_ICK_ID("fck", "sh-sci.0", &mstp_clks[MSTP026]),
+ CLKDEV_ICK_ID("fck", "sh-sci.1", &mstp_clks[MSTP025]),
+ CLKDEV_ICK_ID("fck", "sh-sci.2", &mstp_clks[MSTP024]),
+ CLKDEV_ICK_ID("fck", "sh-sci.3", &mstp_clks[MSTP023]),
+ CLKDEV_ICK_ID("fck", "sh-sci.4", &mstp_clks[MSTP022]),
+ CLKDEV_ICK_ID("fck", "sh-sci.5", &mstp_clks[MSTP021]),
CLKDEV_CON_ID("hscif", &mstp_clks[MSTP019]),
CLKDEV_ICK_ID("fck", "sh-tmu.0", &mstp_clks[MSTP016]),
CLKDEV_ICK_ID("fck", "sh-tmu.1", &mstp_clks[MSTP015]),
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7757.c b/arch/sh/kernel/cpu/sh4a/clock-sh7757.c
index e40ec2c97ad1..b10af2ae9f35 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7757.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7757.c
@@ -125,9 +125,9 @@ static struct clk_lookup lookups[] = {
CLKDEV_ICK_ID("fck", "sh-tmu.0", &mstp_clks[MSTP113]),
CLKDEV_ICK_ID("fck", "sh-tmu.1", &mstp_clks[MSTP114]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.2", &mstp_clks[MSTP112]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.1", &mstp_clks[MSTP111]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.0", &mstp_clks[MSTP110]),
+ CLKDEV_ICK_ID("fck", "sh-sci.2", &mstp_clks[MSTP112]),
+ CLKDEV_ICK_ID("fck", "sh-sci.1", &mstp_clks[MSTP111]),
+ CLKDEV_ICK_ID("fck", "sh-sci.0", &mstp_clks[MSTP110]),
CLKDEV_CON_ID("usb_fck", &mstp_clks[MSTP103]),
CLKDEV_DEV_ID("renesas_usbhs.0", &mstp_clks[MSTP102]),
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7785.c b/arch/sh/kernel/cpu/sh4a/clock-sh7785.c
index 8eb6e62340c9..1aafd5496752 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7785.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7785.c
@@ -132,12 +132,12 @@ static struct clk_lookup lookups[] = {
CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]),
/* MSTP32 clocks */
- CLKDEV_ICK_ID("sci_fck", "sh-sci.5", &mstp_clks[MSTP029]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.4", &mstp_clks[MSTP028]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.3", &mstp_clks[MSTP027]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.2", &mstp_clks[MSTP026]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.1", &mstp_clks[MSTP025]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.0", &mstp_clks[MSTP024]),
+ CLKDEV_ICK_ID("fck", "sh-sci.5", &mstp_clks[MSTP029]),
+ CLKDEV_ICK_ID("fck", "sh-sci.4", &mstp_clks[MSTP028]),
+ CLKDEV_ICK_ID("fck", "sh-sci.3", &mstp_clks[MSTP027]),
+ CLKDEV_ICK_ID("fck", "sh-sci.2", &mstp_clks[MSTP026]),
+ CLKDEV_ICK_ID("fck", "sh-sci.1", &mstp_clks[MSTP025]),
+ CLKDEV_ICK_ID("fck", "sh-sci.0", &mstp_clks[MSTP024]),
CLKDEV_CON_ID("ssi1_fck", &mstp_clks[MSTP021]),
CLKDEV_CON_ID("ssi0_fck", &mstp_clks[MSTP020]),
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7786.c b/arch/sh/kernel/cpu/sh4a/clock-sh7786.c
index 5e50e7ebeff0..ac3dcfe5d303 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7786.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7786.c
@@ -139,12 +139,12 @@ static struct clk_lookup lookups[] = {
CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]),
/* MSTP32 clocks */
- CLKDEV_ICK_ID("sci_fck", "sh-sci.5", &mstp_clks[MSTP029]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.4", &mstp_clks[MSTP028]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.3", &mstp_clks[MSTP027]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.2", &mstp_clks[MSTP026]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.1", &mstp_clks[MSTP025]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.0", &mstp_clks[MSTP024]),
+ CLKDEV_ICK_ID("fck", "sh-sci.5", &mstp_clks[MSTP029]),
+ CLKDEV_ICK_ID("fck", "sh-sci.4", &mstp_clks[MSTP028]),
+ CLKDEV_ICK_ID("fck", "sh-sci.3", &mstp_clks[MSTP027]),
+ CLKDEV_ICK_ID("fck", "sh-sci.2", &mstp_clks[MSTP026]),
+ CLKDEV_ICK_ID("fck", "sh-sci.1", &mstp_clks[MSTP025]),
+ CLKDEV_ICK_ID("fck", "sh-sci.0", &mstp_clks[MSTP024]),
CLKDEV_CON_ID("ssi3_fck", &mstp_clks[MSTP023]),
CLKDEV_CON_ID("ssi2_fck", &mstp_clks[MSTP022]),
diff --git a/arch/sh/kernel/cpu/sh4a/clock-shx3.c b/arch/sh/kernel/cpu/sh4a/clock-shx3.c
index 605221d1448a..b1bdbc3cbc21 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-shx3.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-shx3.c
@@ -114,10 +114,10 @@ static struct clk_lookup lookups[] = {
CLKDEV_CON_ID("cpu_clk", &div4_clks[DIV4_I]),
/* MSTP32 clocks */
- CLKDEV_ICK_ID("sci_fck", "sh-sci.3", &mstp_clks[MSTP027]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.2", &mstp_clks[MSTP026]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.1", &mstp_clks[MSTP025]),
- CLKDEV_ICK_ID("sci_fck", "sh-sci.0", &mstp_clks[MSTP024]),
+ CLKDEV_ICK_ID("fck", "sh-sci.3", &mstp_clks[MSTP027]),
+ CLKDEV_ICK_ID("fck", "sh-sci.2", &mstp_clks[MSTP026]),
+ CLKDEV_ICK_ID("fck", "sh-sci.1", &mstp_clks[MSTP025]),
+ CLKDEV_ICK_ID("fck", "sh-sci.0", &mstp_clks[MSTP024]),
CLKDEV_CON_ID("h8ex_fck", &mstp_clks[MSTP003]),
CLKDEV_CON_ID("csm_fck", &mstp_clks[MSTP002]),
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7734.c b/arch/sh/kernel/cpu/sh4a/setup-sh7734.c
index f617bcb734df..69b8a50310d9 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7734.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7734.c
@@ -28,7 +28,7 @@ static struct plat_sci_port scif0_platform_data = {
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
.type = PORT_SCIF,
- .regtype = SCIx_SH4_SCIF_REGTYPE,
+ .regtype = SCIx_SH4_SCIF_BRG_REGTYPE,
};
static struct resource scif0_resources[] = {
@@ -50,7 +50,7 @@ static struct plat_sci_port scif1_platform_data = {
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
.type = PORT_SCIF,
- .regtype = SCIx_SH4_SCIF_REGTYPE,
+ .regtype = SCIx_SH4_SCIF_BRG_REGTYPE,
};
static struct resource scif1_resources[] = {
@@ -72,7 +72,7 @@ static struct plat_sci_port scif2_platform_data = {
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
.type = PORT_SCIF,
- .regtype = SCIx_SH4_SCIF_REGTYPE,
+ .regtype = SCIx_SH4_SCIF_BRG_REGTYPE,
};
static struct resource scif2_resources[] = {
@@ -94,7 +94,7 @@ static struct plat_sci_port scif3_platform_data = {
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE | SCSCR_TOIE,
.type = PORT_SCIF,
- .regtype = SCIx_SH4_SCIF_REGTYPE,
+ .regtype = SCIx_SH4_SCIF_BRG_REGTYPE,
};
static struct resource scif3_resources[] = {
@@ -116,7 +116,7 @@ static struct plat_sci_port scif4_platform_data = {
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
.type = PORT_SCIF,
- .regtype = SCIx_SH4_SCIF_REGTYPE,
+ .regtype = SCIx_SH4_SCIF_BRG_REGTYPE,
};
static struct resource scif4_resources[] = {
@@ -138,7 +138,7 @@ static struct plat_sci_port scif5_platform_data = {
.flags = UPF_BOOT_AUTOCONF,
.scscr = SCSCR_RE | SCSCR_TE | SCSCR_REIE,
.type = PORT_SCIF,
- .regtype = SCIx_SH4_SCIF_REGTYPE,
+ .regtype = SCIx_SH4_SCIF_BRG_REGTYPE,
};
static struct resource scif5_resources[] = {
diff --git a/arch/sh/kernel/ftrace.c b/arch/sh/kernel/ftrace.c
index 079d70e6d74b..38993e09ef03 100644
--- a/arch/sh/kernel/ftrace.c
+++ b/arch/sh/kernel/ftrace.c
@@ -212,13 +212,11 @@ static int ftrace_modify_code(unsigned long ip, unsigned char *old_code,
unsigned char replaced[MCOUNT_INSN_SIZE];
/*
- * Note: Due to modules and __init, code can
- * disappear and change, we need to protect against faulting
- * as well as code changing. We do this by using the
- * probe_kernel_* functions.
- *
- * No real locking needed, this code is run through
- * kstop_machine, or before SMP starts.
+ * Note:
+ * We are paranoid about modifying text, as if a bug was to happen, it
+ * could cause us to read or write to someplace that could cause harm.
+ * Carefully read and modify the code with probe_kernel_*(), and make
+ * sure what we read is what we expected it to be before modifying it.
*/
/* read the text we want to modify */
diff --git a/arch/sh/kernel/perf_event.c b/arch/sh/kernel/perf_event.c
index 7cfd7f153966..4dca18347ee9 100644
--- a/arch/sh/kernel/perf_event.c
+++ b/arch/sh/kernel/perf_event.c
@@ -10,7 +10,7 @@
* Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar
* Copyright (C) 2009 Jaswinder Singh Rajput
* Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter
- * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra
* Copyright (C) 2009 Intel Corporation, <markus.t.metzger@intel.com>
*
* ppc:
diff --git a/arch/sparc/include/asm/elf_64.h b/arch/sparc/include/asm/elf_64.h
index 370ca1e71ffb..93310837c2df 100644
--- a/arch/sparc/include/asm/elf_64.h
+++ b/arch/sparc/include/asm/elf_64.h
@@ -95,6 +95,7 @@
* really available. So we simply advertise only "crypto" support.
*/
#define HWCAP_SPARC_CRYPTO 0x04000000 /* CRYPTO insns available */
+#define HWCAP_SPARC_ADI 0x08000000 /* ADI available */
#define CORE_DUMP_USE_REGSET
diff --git a/arch/sparc/include/asm/uaccess_32.h b/arch/sparc/include/asm/uaccess_32.h
index 64ee103dc29d..57aca2792d29 100644
--- a/arch/sparc/include/asm/uaccess_32.h
+++ b/arch/sparc/include/asm/uaccess_32.h
@@ -205,31 +205,6 @@ int __put_user_bad(void);
__gu_ret; \
})
-#define __get_user_check_ret(x, addr, size, type, retval) ({ \
- register unsigned long __gu_val __asm__ ("l1"); \
- if (__access_ok(addr, size)) { \
- switch (size) { \
- case 1: \
- __get_user_asm_ret(__gu_val, ub, addr, retval); \
- break; \
- case 2: \
- __get_user_asm_ret(__gu_val, uh, addr, retval); \
- break; \
- case 4: \
- __get_user_asm_ret(__gu_val, , addr, retval); \
- break; \
- case 8: \
- __get_user_asm_ret(__gu_val, d, addr, retval); \
- break; \
- default: \
- if (__get_user_bad()) \
- return retval; \
- } \
- x = (__force type) __gu_val; \
- } else \
- return retval; \
-})
-
#define __get_user_nocheck(x, addr, size, type) ({ \
register int __gu_ret; \
register unsigned long __gu_val; \
@@ -247,20 +222,6 @@ int __put_user_bad(void);
__gu_ret; \
})
-#define __get_user_nocheck_ret(x, addr, size, type, retval) ({ \
- register unsigned long __gu_val __asm__ ("l1"); \
- switch (size) { \
- case 1: __get_user_asm_ret(__gu_val, ub, addr, retval); break; \
- case 2: __get_user_asm_ret(__gu_val, uh, addr, retval); break; \
- case 4: __get_user_asm_ret(__gu_val, , addr, retval); break; \
- case 8: __get_user_asm_ret(__gu_val, d, addr, retval); break; \
- default: \
- if (__get_user_bad()) \
- return retval; \
- } \
- x = (__force type) __gu_val; \
-})
-
#define __get_user_asm(x, size, addr, ret) \
__asm__ __volatile__( \
"/* Get user asm, inline. */\n" \
@@ -281,32 +242,6 @@ __asm__ __volatile__( \
: "=&r" (ret), "=&r" (x) : "m" (*__m(addr)), \
"i" (-EFAULT))
-#define __get_user_asm_ret(x, size, addr, retval) \
-if (__builtin_constant_p(retval) && retval == -EFAULT) \
- __asm__ __volatile__( \
- "/* Get user asm ret, inline. */\n" \
- "1:\t" "ld"#size " %1, %0\n\n\t" \
- ".section __ex_table,#alloc\n\t" \
- ".align 4\n\t" \
- ".word 1b,__ret_efault\n\n\t" \
- ".previous\n\t" \
- : "=&r" (x) : "m" (*__m(addr))); \
-else \
- __asm__ __volatile__( \
- "/* Get user asm ret, inline. */\n" \
- "1:\t" "ld"#size " %1, %0\n\n\t" \
- ".section .fixup,#alloc,#execinstr\n\t" \
- ".align 4\n" \
- "3:\n\t" \
- "ret\n\t" \
- " restore %%g0, %2, %%o0\n\n\t" \
- ".previous\n\t" \
- ".section __ex_table,#alloc\n\t" \
- ".align 4\n\t" \
- ".word 1b, 3b\n\n\t" \
- ".previous\n\t" \
- : "=&r" (x) : "m" (*__m(addr)), "i" (retval))
-
int __get_user_bad(void);
unsigned long __copy_user(void __user *to, const void __user *from, unsigned long size);
diff --git a/arch/sparc/include/asm/uaccess_64.h b/arch/sparc/include/asm/uaccess_64.h
index ea6e9a20f3ff..e9a51d64974d 100644
--- a/arch/sparc/include/asm/uaccess_64.h
+++ b/arch/sparc/include/asm/uaccess_64.h
@@ -179,20 +179,6 @@ int __put_user_bad(void);
__gu_ret; \
})
-#define __get_user_nocheck_ret(data, addr, size, type, retval) ({ \
- register unsigned long __gu_val __asm__ ("l1"); \
- switch (size) { \
- case 1: __get_user_asm_ret(__gu_val, ub, addr, retval); break; \
- case 2: __get_user_asm_ret(__gu_val, uh, addr, retval); break; \
- case 4: __get_user_asm_ret(__gu_val, uw, addr, retval); break; \
- case 8: __get_user_asm_ret(__gu_val, x, addr, retval); break; \
- default: \
- if (__get_user_bad()) \
- return retval; \
- } \
- data = (__force type) __gu_val; \
-})
-
#define __get_user_asm(x, size, addr, ret) \
__asm__ __volatile__( \
"/* Get user asm, inline. */\n" \
@@ -214,32 +200,6 @@ __asm__ __volatile__( \
: "=r" (ret), "=r" (x) : "r" (__m(addr)), \
"i" (-EFAULT))
-#define __get_user_asm_ret(x, size, addr, retval) \
-if (__builtin_constant_p(retval) && retval == -EFAULT) \
- __asm__ __volatile__( \
- "/* Get user asm ret, inline. */\n" \
- "1:\t" "ld"#size "a [%1] %%asi, %0\n\n\t" \
- ".section __ex_table,\"a\"\n\t" \
- ".align 4\n\t" \
- ".word 1b,__ret_efault\n\n\t" \
- ".previous\n\t" \
- : "=r" (x) : "r" (__m(addr))); \
-else \
- __asm__ __volatile__( \
- "/* Get user asm ret, inline. */\n" \
- "1:\t" "ld"#size "a [%1] %%asi, %0\n\n\t" \
- ".section .fixup,#alloc,#execinstr\n\t" \
- ".align 4\n" \
- "3:\n\t" \
- "ret\n\t" \
- " restore %%g0, %2, %%o0\n\n\t" \
- ".previous\n\t" \
- ".section __ex_table,\"a\"\n\t" \
- ".align 4\n\t" \
- ".word 1b, 3b\n\n\t" \
- ".previous\n\t" \
- : "=r" (x) : "r" (__m(addr)), "i" (retval))
-
int __get_user_bad(void);
unsigned long __must_check ___copy_from_user(void *to,
diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h
index e6a16c40be5f..d270ee91968e 100644
--- a/arch/sparc/include/uapi/asm/socket.h
+++ b/arch/sparc/include/uapi/asm/socket.h
@@ -81,6 +81,9 @@
#define SO_ATTACH_BPF 0x0034
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 0x0035
+#define SO_ATTACH_REUSEPORT_EBPF 0x0036
+
/* Security levels - as per NRL IPv6 - don't actually do anything */
#define SO_SECURITY_AUTHENTICATION 0x5001
#define SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002
diff --git a/arch/sparc/include/uapi/asm/unistd.h b/arch/sparc/include/uapi/asm/unistd.h
index efe9479f837b..1c26d440d288 100644
--- a/arch/sparc/include/uapi/asm/unistd.h
+++ b/arch/sparc/include/uapi/asm/unistd.h
@@ -417,8 +417,13 @@
#define __NR_bpf 349
#define __NR_execveat 350
#define __NR_membarrier 351
+#define __NR_userfaultfd 352
+#define __NR_bind 353
+#define __NR_listen 354
+#define __NR_setsockopt 355
+#define __NR_mlock2 356
-#define NR_syscalls 352
+#define NR_syscalls 357
/* Bitmask values returned from kern_features system call. */
#define KERN_FEATURE_MIXED_MODE_STACK 0x00000001
diff --git a/arch/sparc/kernel/head_64.S b/arch/sparc/kernel/head_64.S
index 3d61fcae7ee3..f2d30cab5b3f 100644
--- a/arch/sparc/kernel/head_64.S
+++ b/arch/sparc/kernel/head_64.S
@@ -946,6 +946,12 @@ ENTRY(__retl_one)
mov 1, %o0
ENDPROC(__retl_one)
+ENTRY(__retl_one_fp)
+ VISExitHalf
+ retl
+ mov 1, %o0
+ENDPROC(__retl_one_fp)
+
ENTRY(__ret_one_asi)
wr %g0, ASI_AIUS, %asi
ret
@@ -958,6 +964,13 @@ ENTRY(__retl_one_asi)
mov 1, %o0
ENDPROC(__retl_one_asi)
+ENTRY(__retl_one_asi_fp)
+ wr %g0, ASI_AIUS, %asi
+ VISExitHalf
+ retl
+ mov 1, %o0
+ENDPROC(__retl_one_asi_fp)
+
ENTRY(__retl_o1)
retl
mov %o1, %o0
diff --git a/arch/sparc/kernel/idprom.c b/arch/sparc/kernel/idprom.c
index 6bd75012109d..f95dd11b75ea 100644
--- a/arch/sparc/kernel/idprom.c
+++ b/arch/sparc/kernel/idprom.c
@@ -9,6 +9,7 @@
#include <linux/types.h>
#include <linux/init.h>
#include <linux/export.h>
+#include <linux/etherdevice.h>
#include <asm/oplib.h>
#include <asm/idprom.h>
@@ -60,6 +61,12 @@ static void __init display_system_type(unsigned char machtype)
{
}
#endif
+
+unsigned char *arch_get_platform_mac_address(void)
+{
+ return idprom->id_ethaddr;
+}
+
/* Calculate the IDPROM checksum (xor of the data bytes). */
static unsigned char __init calc_idprom_cksum(struct idprom *idprom)
{
diff --git a/arch/sparc/kernel/mdesc.c b/arch/sparc/kernel/mdesc.c
index 6f80936e0eea..11228861d9b4 100644
--- a/arch/sparc/kernel/mdesc.c
+++ b/arch/sparc/kernel/mdesc.c
@@ -1033,25 +1033,9 @@ static ssize_t mdesc_read(struct file *file, char __user *buf,
static loff_t mdesc_llseek(struct file *file, loff_t offset, int whence)
{
- struct mdesc_handle *hp;
-
- switch (whence) {
- case SEEK_CUR:
- offset += file->f_pos;
- break;
- case SEEK_SET:
- break;
- default:
- return -EINVAL;
- }
-
- hp = file->private_data;
- if (offset > hp->handle_size)
- return -EINVAL;
- else
- file->f_pos = offset;
+ struct mdesc_handle *hp = file->private_data;
- return offset;
+ return no_seek_end_llseek_size(file, offset, whence, hp->handle_size);
}
/* mdesc_close() - /dev/mdesc is being closed, release the reference to
diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c
index b0da5aedb336..6596f66ce112 100644
--- a/arch/sparc/kernel/perf_event.c
+++ b/arch/sparc/kernel/perf_event.c
@@ -9,7 +9,7 @@
* Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar
* Copyright (C) 2009 Jaswinder Singh Rajput
* Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter
- * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra
*/
#include <linux/perf_event.h>
@@ -1828,11 +1828,18 @@ static void perf_callchain_user_32(struct perf_callchain_entry *entry,
void
perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs)
{
+ u64 saved_fault_address = current_thread_info()->fault_address;
+ u8 saved_fault_code = get_thread_fault_code();
+ mm_segment_t old_fs;
+
perf_callchain_store(entry, regs->tpc);
if (!current->mm)
return;
+ old_fs = get_fs();
+ set_fs(USER_DS);
+
flushw_user();
pagefault_disable();
@@ -1843,4 +1850,8 @@ perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs)
perf_callchain_user_64(entry, regs);
pagefault_enable();
+
+ set_fs(old_fs);
+ set_thread_fault_code(saved_fault_code);
+ current_thread_info()->fault_address = saved_fault_address;
}
diff --git a/arch/sparc/kernel/rtrap_64.S b/arch/sparc/kernel/rtrap_64.S
index 39f0c662f4c8..d08bdaffdbfc 100644
--- a/arch/sparc/kernel/rtrap_64.S
+++ b/arch/sparc/kernel/rtrap_64.S
@@ -73,7 +73,13 @@ rtrap_nmi: ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
andn %l1, %l4, %l1
srl %l4, 20, %l4
ba,pt %xcc, rtrap_no_irq_enable
- wrpr %l4, %pil
+ nop
+ /* Do not actually set the %pil here. We will do that
+ * below after we clear PSTATE_IE in the %pstate register.
+ * If we re-enable interrupts here, we can recurse down
+ * the hardirq stack potentially endlessly, causing a
+ * stack overflow.
+ */
.align 64
.globl rtrap_irq, rtrap, irqsz_patchme, rtrap_xcall
diff --git a/arch/sparc/kernel/setup_64.c b/arch/sparc/kernel/setup_64.c
index f7b261749383..f3185e2b028b 100644
--- a/arch/sparc/kernel/setup_64.c
+++ b/arch/sparc/kernel/setup_64.c
@@ -380,7 +380,8 @@ static const char *hwcaps[] = {
*/
"mul32", "div32", "fsmuld", "v8plus", "popc", "vis", "vis2",
"ASIBlkInit", "fmaf", "vis3", "hpc", "random", "trans", "fjfmau",
- "ima", "cspare", "pause", "cbcond",
+ "ima", "cspare", "pause", "cbcond", NULL /*reserved for crypto */,
+ "adp",
};
static const char *crypto_hwcaps[] = {
@@ -396,7 +397,7 @@ void cpucap_info(struct seq_file *m)
seq_puts(m, "cpucaps\t\t: ");
for (i = 0; i < ARRAY_SIZE(hwcaps); i++) {
unsigned long bit = 1UL << i;
- if (caps & bit) {
+ if (hwcaps[i] && (caps & bit)) {
seq_printf(m, "%s%s",
printed ? "," : "", hwcaps[i]);
printed++;
@@ -450,7 +451,7 @@ static void __init report_hwcaps(unsigned long caps)
for (i = 0; i < ARRAY_SIZE(hwcaps); i++) {
unsigned long bit = 1UL << i;
- if (caps & bit)
+ if (hwcaps[i] && (caps & bit))
report_one_hwcap(&printed, hwcaps[i]);
}
if (caps & HWCAP_SPARC_CRYPTO)
@@ -485,7 +486,7 @@ static unsigned long __init mdesc_cpu_hwcap_list(void)
for (i = 0; i < ARRAY_SIZE(hwcaps); i++) {
unsigned long bit = 1UL << i;
- if (!strcmp(prop, hwcaps[i])) {
+ if (hwcaps[i] && !strcmp(prop, hwcaps[i])) {
caps |= bit;
break;
}
diff --git a/arch/sparc/kernel/systbls_32.S b/arch/sparc/kernel/systbls_32.S
index cc23b62b6e38..e663b6c78de2 100644
--- a/arch/sparc/kernel/systbls_32.S
+++ b/arch/sparc/kernel/systbls_32.S
@@ -35,18 +35,18 @@ sys_call_table:
/*80*/ .long sys_setgroups16, sys_getpgrp, sys_setgroups, sys_setitimer, sys_ftruncate64
/*85*/ .long sys_swapon, sys_getitimer, sys_setuid, sys_sethostname, sys_setgid
/*90*/ .long sys_dup2, sys_setfsuid, sys_fcntl, sys_select, sys_setfsgid
-/*95*/ .long sys_fsync, sys_setpriority, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+/*95*/ .long sys_fsync, sys_setpriority, sys_socket, sys_connect, sys_accept
/*100*/ .long sys_getpriority, sys_rt_sigreturn, sys_rt_sigaction, sys_rt_sigprocmask, sys_rt_sigpending
/*105*/ .long sys_rt_sigtimedwait, sys_rt_sigqueueinfo, sys_rt_sigsuspend, sys_setresuid, sys_getresuid
-/*110*/ .long sys_setresgid, sys_getresgid, sys_setregid, sys_nis_syscall, sys_nis_syscall
-/*115*/ .long sys_getgroups, sys_gettimeofday, sys_getrusage, sys_nis_syscall, sys_getcwd
+/*110*/ .long sys_setresgid, sys_getresgid, sys_setregid, sys_recvmsg, sys_sendmsg
+/*115*/ .long sys_getgroups, sys_gettimeofday, sys_getrusage, sys_getsockopt, sys_getcwd
/*120*/ .long sys_readv, sys_writev, sys_settimeofday, sys_fchown16, sys_fchmod
-/*125*/ .long sys_nis_syscall, sys_setreuid16, sys_setregid16, sys_rename, sys_truncate
-/*130*/ .long sys_ftruncate, sys_flock, sys_lstat64, sys_nis_syscall, sys_nis_syscall
-/*135*/ .long sys_nis_syscall, sys_mkdir, sys_rmdir, sys_utimes, sys_stat64
-/*140*/ .long sys_sendfile64, sys_nis_syscall, sys_futex, sys_gettid, sys_getrlimit
+/*125*/ .long sys_recvfrom, sys_setreuid16, sys_setregid16, sys_rename, sys_truncate
+/*130*/ .long sys_ftruncate, sys_flock, sys_lstat64, sys_sendto, sys_shutdown
+/*135*/ .long sys_socketpair, sys_mkdir, sys_rmdir, sys_utimes, sys_stat64
+/*140*/ .long sys_sendfile64, sys_getpeername, sys_futex, sys_gettid, sys_getrlimit
/*145*/ .long sys_setrlimit, sys_pivot_root, sys_prctl, sys_pciconfig_read, sys_pciconfig_write
-/*150*/ .long sys_nis_syscall, sys_inotify_init, sys_inotify_add_watch, sys_poll, sys_getdents64
+/*150*/ .long sys_getsockname, sys_inotify_init, sys_inotify_add_watch, sys_poll, sys_getdents64
/*155*/ .long sys_fcntl64, sys_inotify_rm_watch, sys_statfs, sys_fstatfs, sys_oldumount
/*160*/ .long sys_sched_setaffinity, sys_sched_getaffinity, sys_getdomainname, sys_setdomainname, sys_nis_syscall
/*165*/ .long sys_quotactl, sys_set_tid_address, sys_mount, sys_ustat, sys_setxattr
@@ -87,4 +87,5 @@ sys_call_table:
/*335*/ .long sys_syncfs, sys_sendmmsg, sys_setns, sys_process_vm_readv, sys_process_vm_writev
/*340*/ .long sys_ni_syscall, sys_kcmp, sys_finit_module, sys_sched_setattr, sys_sched_getattr
/*345*/ .long sys_renameat2, sys_seccomp, sys_getrandom, sys_memfd_create, sys_bpf
-/*350*/ .long sys_execveat, sys_membarrier
+/*350*/ .long sys_execveat, sys_membarrier, sys_userfaultfd, sys_bind, sys_listen
+/*355*/ .long sys_setsockopt, sys_mlock2
diff --git a/arch/sparc/kernel/systbls_64.S b/arch/sparc/kernel/systbls_64.S
index f229468a7479..1557121f4cdc 100644
--- a/arch/sparc/kernel/systbls_64.S
+++ b/arch/sparc/kernel/systbls_64.S
@@ -37,15 +37,15 @@ sys_call_table32:
/*80*/ .word sys_setgroups16, sys_getpgrp, sys_setgroups, compat_sys_setitimer, sys32_ftruncate64
.word sys_swapon, compat_sys_getitimer, sys_setuid, sys_sethostname, sys_setgid
/*90*/ .word sys_dup2, sys_setfsuid, compat_sys_fcntl, sys32_select, sys_setfsgid
- .word sys_fsync, sys_setpriority, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
+ .word sys_fsync, sys_setpriority, sys_socket, sys_connect, sys_accept
/*100*/ .word sys_getpriority, sys32_rt_sigreturn, compat_sys_rt_sigaction, compat_sys_rt_sigprocmask, compat_sys_rt_sigpending
.word compat_sys_rt_sigtimedwait, compat_sys_rt_sigqueueinfo, compat_sys_rt_sigsuspend, sys_setresuid, sys_getresuid
-/*110*/ .word sys_setresgid, sys_getresgid, sys_setregid, sys_nis_syscall, sys_nis_syscall
- .word sys_getgroups, compat_sys_gettimeofday, compat_sys_getrusage, sys_nis_syscall, sys_getcwd
+/*110*/ .word sys_setresgid, sys_getresgid, sys_setregid, compat_sys_recvmsg, compat_sys_sendmsg
+ .word sys_getgroups, compat_sys_gettimeofday, compat_sys_getrusage, compat_sys_getsockopt, sys_getcwd
/*120*/ .word compat_sys_readv, compat_sys_writev, compat_sys_settimeofday, sys_fchown16, sys_fchmod
- .word sys_nis_syscall, sys_setreuid16, sys_setregid16, sys_rename, compat_sys_truncate
-/*130*/ .word compat_sys_ftruncate, sys_flock, compat_sys_lstat64, sys_nis_syscall, sys_nis_syscall
- .word sys_nis_syscall, sys_mkdir, sys_rmdir, compat_sys_utimes, compat_sys_stat64
+ .word sys_recvfrom, sys_setreuid16, sys_setregid16, sys_rename, compat_sys_truncate
+/*130*/ .word compat_sys_ftruncate, sys_flock, compat_sys_lstat64, sys_sendto, sys_shutdown
+ .word sys_socketpair, sys_mkdir, sys_rmdir, compat_sys_utimes, compat_sys_stat64
/*140*/ .word sys_sendfile64, sys_nis_syscall, sys32_futex, sys_gettid, compat_sys_getrlimit
.word compat_sys_setrlimit, sys_pivot_root, sys_prctl, sys_pciconfig_read, sys_pciconfig_write
/*150*/ .word sys_nis_syscall, sys_inotify_init, sys_inotify_add_watch, sys_poll, sys_getdents64
@@ -88,7 +88,8 @@ sys_call_table32:
.word sys_syncfs, compat_sys_sendmmsg, sys_setns, compat_sys_process_vm_readv, compat_sys_process_vm_writev
/*340*/ .word sys_kern_features, sys_kcmp, sys_finit_module, sys_sched_setattr, sys_sched_getattr
.word sys32_renameat2, sys_seccomp, sys_getrandom, sys_memfd_create, sys_bpf
-/*350*/ .word sys32_execveat, sys_membarrier
+/*350*/ .word sys32_execveat, sys_membarrier, sys_userfaultfd, sys_bind, sys_listen
+ .word compat_sys_setsockopt, sys_mlock2
#endif /* CONFIG_COMPAT */
@@ -168,4 +169,5 @@ sys_call_table:
.word sys_syncfs, sys_sendmmsg, sys_setns, sys_process_vm_readv, sys_process_vm_writev
/*340*/ .word sys_kern_features, sys_kcmp, sys_finit_module, sys_sched_setattr, sys_sched_getattr
.word sys_renameat2, sys_seccomp, sys_getrandom, sys_memfd_create, sys_bpf
-/*350*/ .word sys64_execveat, sys_membarrier
+/*350*/ .word sys64_execveat, sys_membarrier, sys_userfaultfd, sys_bind, sys_listen
+ .word sys_setsockopt, sys_mlock2
diff --git a/arch/sparc/lib/NG2copy_from_user.S b/arch/sparc/lib/NG2copy_from_user.S
index 119ccb9a54f4..d5242b8c4f94 100644
--- a/arch/sparc/lib/NG2copy_from_user.S
+++ b/arch/sparc/lib/NG2copy_from_user.S
@@ -11,6 +11,14 @@
.text; \
.align 4;
+#define EX_LD_FP(x) \
+98: x; \
+ .section __ex_table,"a";\
+ .align 4; \
+ .word 98b, __retl_one_asi_fp;\
+ .text; \
+ .align 4;
+
#ifndef ASI_AIUS
#define ASI_AIUS 0x11
#endif
diff --git a/arch/sparc/lib/NG2copy_to_user.S b/arch/sparc/lib/NG2copy_to_user.S
index 7fe1ccefd9d0..4e962d993b10 100644
--- a/arch/sparc/lib/NG2copy_to_user.S
+++ b/arch/sparc/lib/NG2copy_to_user.S
@@ -11,6 +11,14 @@
.text; \
.align 4;
+#define EX_ST_FP(x) \
+98: x; \
+ .section __ex_table,"a";\
+ .align 4; \
+ .word 98b, __retl_one_asi_fp;\
+ .text; \
+ .align 4;
+
#ifndef ASI_AIUS
#define ASI_AIUS 0x11
#endif
diff --git a/arch/sparc/lib/NG2memcpy.S b/arch/sparc/lib/NG2memcpy.S
index 30eee6e8a81b..d5f585df2f3f 100644
--- a/arch/sparc/lib/NG2memcpy.S
+++ b/arch/sparc/lib/NG2memcpy.S
@@ -34,10 +34,16 @@
#ifndef EX_LD
#define EX_LD(x) x
#endif
+#ifndef EX_LD_FP
+#define EX_LD_FP(x) x
+#endif
#ifndef EX_ST
#define EX_ST(x) x
#endif
+#ifndef EX_ST_FP
+#define EX_ST_FP(x) x
+#endif
#ifndef EX_RETVAL
#define EX_RETVAL(x) x
@@ -134,40 +140,40 @@
fsrc2 %x6, %f12; \
fsrc2 %x7, %f14;
#define FREG_LOAD_1(base, x0) \
- EX_LD(LOAD(ldd, base + 0x00, %x0))
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0))
#define FREG_LOAD_2(base, x0, x1) \
- EX_LD(LOAD(ldd, base + 0x00, %x0)); \
- EX_LD(LOAD(ldd, base + 0x08, %x1));
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0)); \
+ EX_LD_FP(LOAD(ldd, base + 0x08, %x1));
#define FREG_LOAD_3(base, x0, x1, x2) \
- EX_LD(LOAD(ldd, base + 0x00, %x0)); \
- EX_LD(LOAD(ldd, base + 0x08, %x1)); \
- EX_LD(LOAD(ldd, base + 0x10, %x2));
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0)); \
+ EX_LD_FP(LOAD(ldd, base + 0x08, %x1)); \
+ EX_LD_FP(LOAD(ldd, base + 0x10, %x2));
#define FREG_LOAD_4(base, x0, x1, x2, x3) \
- EX_LD(LOAD(ldd, base + 0x00, %x0)); \
- EX_LD(LOAD(ldd, base + 0x08, %x1)); \
- EX_LD(LOAD(ldd, base + 0x10, %x2)); \
- EX_LD(LOAD(ldd, base + 0x18, %x3));
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0)); \
+ EX_LD_FP(LOAD(ldd, base + 0x08, %x1)); \
+ EX_LD_FP(LOAD(ldd, base + 0x10, %x2)); \
+ EX_LD_FP(LOAD(ldd, base + 0x18, %x3));
#define FREG_LOAD_5(base, x0, x1, x2, x3, x4) \
- EX_LD(LOAD(ldd, base + 0x00, %x0)); \
- EX_LD(LOAD(ldd, base + 0x08, %x1)); \
- EX_LD(LOAD(ldd, base + 0x10, %x2)); \
- EX_LD(LOAD(ldd, base + 0x18, %x3)); \
- EX_LD(LOAD(ldd, base + 0x20, %x4));
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0)); \
+ EX_LD_FP(LOAD(ldd, base + 0x08, %x1)); \
+ EX_LD_FP(LOAD(ldd, base + 0x10, %x2)); \
+ EX_LD_FP(LOAD(ldd, base + 0x18, %x3)); \
+ EX_LD_FP(LOAD(ldd, base + 0x20, %x4));
#define FREG_LOAD_6(base, x0, x1, x2, x3, x4, x5) \
- EX_LD(LOAD(ldd, base + 0x00, %x0)); \
- EX_LD(LOAD(ldd, base + 0x08, %x1)); \
- EX_LD(LOAD(ldd, base + 0x10, %x2)); \
- EX_LD(LOAD(ldd, base + 0x18, %x3)); \
- EX_LD(LOAD(ldd, base + 0x20, %x4)); \
- EX_LD(LOAD(ldd, base + 0x28, %x5));
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0)); \
+ EX_LD_FP(LOAD(ldd, base + 0x08, %x1)); \
+ EX_LD_FP(LOAD(ldd, base + 0x10, %x2)); \
+ EX_LD_FP(LOAD(ldd, base + 0x18, %x3)); \
+ EX_LD_FP(LOAD(ldd, base + 0x20, %x4)); \
+ EX_LD_FP(LOAD(ldd, base + 0x28, %x5));
#define FREG_LOAD_7(base, x0, x1, x2, x3, x4, x5, x6) \
- EX_LD(LOAD(ldd, base + 0x00, %x0)); \
- EX_LD(LOAD(ldd, base + 0x08, %x1)); \
- EX_LD(LOAD(ldd, base + 0x10, %x2)); \
- EX_LD(LOAD(ldd, base + 0x18, %x3)); \
- EX_LD(LOAD(ldd, base + 0x20, %x4)); \
- EX_LD(LOAD(ldd, base + 0x28, %x5)); \
- EX_LD(LOAD(ldd, base + 0x30, %x6));
+ EX_LD_FP(LOAD(ldd, base + 0x00, %x0)); \
+ EX_LD_FP(LOAD(ldd, base + 0x08, %x1)); \
+ EX_LD_FP(LOAD(ldd, base + 0x10, %x2)); \
+ EX_LD_FP(LOAD(ldd, base + 0x18, %x3)); \
+ EX_LD_FP(LOAD(ldd, base + 0x20, %x4)); \
+ EX_LD_FP(LOAD(ldd, base + 0x28, %x5)); \
+ EX_LD_FP(LOAD(ldd, base + 0x30, %x6));
.register %g2,#scratch
.register %g3,#scratch
@@ -275,11 +281,11 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
nop
/* fall through for 0 < low bits < 8 */
110: sub %o4, 64, %g2
- EX_LD(LOAD_BLK(%g2, %f0))
-1: EX_ST(STORE_INIT(%g0, %o4 + %g3))
- EX_LD(LOAD_BLK(%o4, %f16))
+ EX_LD_FP(LOAD_BLK(%g2, %f0))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
+ EX_LD_FP(LOAD_BLK(%o4, %f16))
FREG_FROB(f0, f2, f4, f6, f8, f10, f12, f14, f16)
- EX_ST(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
FREG_MOVE_8(f16, f18, f20, f22, f24, f26, f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
@@ -290,10 +296,10 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
120: sub %o4, 56, %g2
FREG_LOAD_7(%g2, f0, f2, f4, f6, f8, f10, f12)
-1: EX_ST(STORE_INIT(%g0, %o4 + %g3))
- EX_LD(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
+ EX_LD_FP(LOAD_BLK(%o4, %f16))
FREG_FROB(f0, f2, f4, f6, f8, f10, f12, f16, f18)
- EX_ST(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
FREG_MOVE_7(f18, f20, f22, f24, f26, f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
@@ -304,10 +310,10 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
130: sub %o4, 48, %g2
FREG_LOAD_6(%g2, f0, f2, f4, f6, f8, f10)
-1: EX_ST(STORE_INIT(%g0, %o4 + %g3))
- EX_LD(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
+ EX_LD_FP(LOAD_BLK(%o4, %f16))
FREG_FROB(f0, f2, f4, f6, f8, f10, f16, f18, f20)
- EX_ST(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
FREG_MOVE_6(f20, f22, f24, f26, f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
@@ -318,10 +324,10 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
140: sub %o4, 40, %g2
FREG_LOAD_5(%g2, f0, f2, f4, f6, f8)
-1: EX_ST(STORE_INIT(%g0, %o4 + %g3))
- EX_LD(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
+ EX_LD_FP(LOAD_BLK(%o4, %f16))
FREG_FROB(f0, f2, f4, f6, f8, f16, f18, f20, f22)
- EX_ST(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
FREG_MOVE_5(f22, f24, f26, f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
@@ -332,10 +338,10 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
150: sub %o4, 32, %g2
FREG_LOAD_4(%g2, f0, f2, f4, f6)
-1: EX_ST(STORE_INIT(%g0, %o4 + %g3))
- EX_LD(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
+ EX_LD_FP(LOAD_BLK(%o4, %f16))
FREG_FROB(f0, f2, f4, f6, f16, f18, f20, f22, f24)
- EX_ST(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
FREG_MOVE_4(f24, f26, f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
@@ -346,10 +352,10 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
160: sub %o4, 24, %g2
FREG_LOAD_3(%g2, f0, f2, f4)
-1: EX_ST(STORE_INIT(%g0, %o4 + %g3))
- EX_LD(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
+ EX_LD_FP(LOAD_BLK(%o4, %f16))
FREG_FROB(f0, f2, f4, f16, f18, f20, f22, f24, f26)
- EX_ST(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
FREG_MOVE_3(f26, f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
@@ -360,10 +366,10 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
170: sub %o4, 16, %g2
FREG_LOAD_2(%g2, f0, f2)
-1: EX_ST(STORE_INIT(%g0, %o4 + %g3))
- EX_LD(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
+ EX_LD_FP(LOAD_BLK(%o4, %f16))
FREG_FROB(f0, f2, f16, f18, f20, f22, f24, f26, f28)
- EX_ST(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
FREG_MOVE_2(f28, f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
@@ -374,10 +380,10 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
180: sub %o4, 8, %g2
FREG_LOAD_1(%g2, f0)
-1: EX_ST(STORE_INIT(%g0, %o4 + %g3))
- EX_LD(LOAD_BLK(%o4, %f16))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
+ EX_LD_FP(LOAD_BLK(%o4, %f16))
FREG_FROB(f0, f16, f18, f20, f22, f24, f26, f28, f30)
- EX_ST(STORE_BLK(%f0, %o4 + %g3))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
FREG_MOVE_1(f30)
subcc %g1, 64, %g1
add %o4, 64, %o4
@@ -387,10 +393,10 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
nop
190:
-1: EX_ST(STORE_INIT(%g0, %o4 + %g3))
+1: EX_ST_FP(STORE_INIT(%g0, %o4 + %g3))
subcc %g1, 64, %g1
- EX_LD(LOAD_BLK(%o4, %f0))
- EX_ST(STORE_BLK(%f0, %o4 + %g3))
+ EX_LD_FP(LOAD_BLK(%o4, %f0))
+ EX_ST_FP(STORE_BLK(%f0, %o4 + %g3))
add %o4, 64, %o4
bne,pt %xcc, 1b
LOAD(prefetch, %o4 + 64, #one_read)
diff --git a/arch/sparc/lib/NG4copy_from_user.S b/arch/sparc/lib/NG4copy_from_user.S
index fd9f903ffa32..2e8ee7ad07a9 100644
--- a/arch/sparc/lib/NG4copy_from_user.S
+++ b/arch/sparc/lib/NG4copy_from_user.S
@@ -11,6 +11,14 @@
.text; \
.align 4;
+#define EX_LD_FP(x) \
+98: x; \
+ .section __ex_table,"a";\
+ .align 4; \
+ .word 98b, __retl_one_asi_fp;\
+ .text; \
+ .align 4;
+
#ifndef ASI_AIUS
#define ASI_AIUS 0x11
#endif
diff --git a/arch/sparc/lib/NG4copy_to_user.S b/arch/sparc/lib/NG4copy_to_user.S
index 9744c4540a8d..be0bf4590df8 100644
--- a/arch/sparc/lib/NG4copy_to_user.S
+++ b/arch/sparc/lib/NG4copy_to_user.S
@@ -11,6 +11,14 @@
.text; \
.align 4;
+#define EX_ST_FP(x) \
+98: x; \
+ .section __ex_table,"a";\
+ .align 4; \
+ .word 98b, __retl_one_asi_fp;\
+ .text; \
+ .align 4;
+
#ifndef ASI_AIUS
#define ASI_AIUS 0x11
#endif
diff --git a/arch/sparc/lib/NG4memcpy.S b/arch/sparc/lib/NG4memcpy.S
index 83aeeb1dffdb..8e13ee1f4454 100644
--- a/arch/sparc/lib/NG4memcpy.S
+++ b/arch/sparc/lib/NG4memcpy.S
@@ -48,10 +48,16 @@
#ifndef EX_LD
#define EX_LD(x) x
#endif
+#ifndef EX_LD_FP
+#define EX_LD_FP(x) x
+#endif
#ifndef EX_ST
#define EX_ST(x) x
#endif
+#ifndef EX_ST_FP
+#define EX_ST_FP(x) x
+#endif
#ifndef EX_RETVAL
#define EX_RETVAL(x) x
@@ -210,17 +216,17 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
sub %o2, %o4, %o2
alignaddr %o1, %g0, %g1
add %o1, %o4, %o1
- EX_LD(LOAD(ldd, %g1 + 0x00, %f0))
-1: EX_LD(LOAD(ldd, %g1 + 0x08, %f2))
+ EX_LD_FP(LOAD(ldd, %g1 + 0x00, %f0))
+1: EX_LD_FP(LOAD(ldd, %g1 + 0x08, %f2))
subcc %o4, 0x40, %o4
- EX_LD(LOAD(ldd, %g1 + 0x10, %f4))
- EX_LD(LOAD(ldd, %g1 + 0x18, %f6))
- EX_LD(LOAD(ldd, %g1 + 0x20, %f8))
- EX_LD(LOAD(ldd, %g1 + 0x28, %f10))
- EX_LD(LOAD(ldd, %g1 + 0x30, %f12))
- EX_LD(LOAD(ldd, %g1 + 0x38, %f14))
+ EX_LD_FP(LOAD(ldd, %g1 + 0x10, %f4))
+ EX_LD_FP(LOAD(ldd, %g1 + 0x18, %f6))
+ EX_LD_FP(LOAD(ldd, %g1 + 0x20, %f8))
+ EX_LD_FP(LOAD(ldd, %g1 + 0x28, %f10))
+ EX_LD_FP(LOAD(ldd, %g1 + 0x30, %f12))
+ EX_LD_FP(LOAD(ldd, %g1 + 0x38, %f14))
faligndata %f0, %f2, %f16
- EX_LD(LOAD(ldd, %g1 + 0x40, %f0))
+ EX_LD_FP(LOAD(ldd, %g1 + 0x40, %f0))
faligndata %f2, %f4, %f18
add %g1, 0x40, %g1
faligndata %f4, %f6, %f20
@@ -229,14 +235,14 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
faligndata %f10, %f12, %f26
faligndata %f12, %f14, %f28
faligndata %f14, %f0, %f30
- EX_ST(STORE(std, %f16, %o0 + 0x00))
- EX_ST(STORE(std, %f18, %o0 + 0x08))
- EX_ST(STORE(std, %f20, %o0 + 0x10))
- EX_ST(STORE(std, %f22, %o0 + 0x18))
- EX_ST(STORE(std, %f24, %o0 + 0x20))
- EX_ST(STORE(std, %f26, %o0 + 0x28))
- EX_ST(STORE(std, %f28, %o0 + 0x30))
- EX_ST(STORE(std, %f30, %o0 + 0x38))
+ EX_ST_FP(STORE(std, %f16, %o0 + 0x00))
+ EX_ST_FP(STORE(std, %f18, %o0 + 0x08))
+ EX_ST_FP(STORE(std, %f20, %o0 + 0x10))
+ EX_ST_FP(STORE(std, %f22, %o0 + 0x18))
+ EX_ST_FP(STORE(std, %f24, %o0 + 0x20))
+ EX_ST_FP(STORE(std, %f26, %o0 + 0x28))
+ EX_ST_FP(STORE(std, %f28, %o0 + 0x30))
+ EX_ST_FP(STORE(std, %f30, %o0 + 0x38))
add %o0, 0x40, %o0
bne,pt %icc, 1b
LOAD(prefetch, %g1 + 0x200, #n_reads_strong)
diff --git a/arch/sparc/lib/U1copy_from_user.S b/arch/sparc/lib/U1copy_from_user.S
index a6ae2ea04bf5..ecc5692fa2b4 100644
--- a/arch/sparc/lib/U1copy_from_user.S
+++ b/arch/sparc/lib/U1copy_from_user.S
@@ -11,6 +11,14 @@
.text; \
.align 4;
+#define EX_LD_FP(x) \
+98: x; \
+ .section __ex_table,"a";\
+ .align 4; \
+ .word 98b, __retl_one_fp;\
+ .text; \
+ .align 4;
+
#define FUNC_NAME ___copy_from_user
#define LOAD(type,addr,dest) type##a [addr] %asi, dest
#define LOAD_BLK(addr,dest) ldda [addr] ASI_BLK_AIUS, dest
diff --git a/arch/sparc/lib/U1copy_to_user.S b/arch/sparc/lib/U1copy_to_user.S
index f4b970eeb485..9eea392e44d4 100644
--- a/arch/sparc/lib/U1copy_to_user.S
+++ b/arch/sparc/lib/U1copy_to_user.S
@@ -11,6 +11,14 @@
.text; \
.align 4;
+#define EX_ST_FP(x) \
+98: x; \
+ .section __ex_table,"a";\
+ .align 4; \
+ .word 98b, __retl_one_fp;\
+ .text; \
+ .align 4;
+
#define FUNC_NAME ___copy_to_user
#define STORE(type,src,addr) type##a src, [addr] ASI_AIUS
#define STORE_BLK(src,addr) stda src, [addr] ASI_BLK_AIUS
diff --git a/arch/sparc/lib/U1memcpy.S b/arch/sparc/lib/U1memcpy.S
index b67142b7768e..3e6209ebb7d7 100644
--- a/arch/sparc/lib/U1memcpy.S
+++ b/arch/sparc/lib/U1memcpy.S
@@ -25,10 +25,16 @@
#ifndef EX_LD
#define EX_LD(x) x
#endif
+#ifndef EX_LD_FP
+#define EX_LD_FP(x) x
+#endif
#ifndef EX_ST
#define EX_ST(x) x
#endif
+#ifndef EX_ST_FP
+#define EX_ST_FP(x) x
+#endif
#ifndef EX_RETVAL
#define EX_RETVAL(x) x
@@ -73,8 +79,8 @@
faligndata %f8, %f9, %f62;
#define MAIN_LOOP_CHUNK(src, dest, fdest, fsrc, len, jmptgt) \
- EX_LD(LOAD_BLK(%src, %fdest)); \
- EX_ST(STORE_BLK(%fsrc, %dest)); \
+ EX_LD_FP(LOAD_BLK(%src, %fdest)); \
+ EX_ST_FP(STORE_BLK(%fsrc, %dest)); \
add %src, 0x40, %src; \
subcc %len, 0x40, %len; \
be,pn %xcc, jmptgt; \
@@ -89,12 +95,12 @@
#define DO_SYNC membar #Sync;
#define STORE_SYNC(dest, fsrc) \
- EX_ST(STORE_BLK(%fsrc, %dest)); \
+ EX_ST_FP(STORE_BLK(%fsrc, %dest)); \
add %dest, 0x40, %dest; \
DO_SYNC
#define STORE_JUMP(dest, fsrc, target) \
- EX_ST(STORE_BLK(%fsrc, %dest)); \
+ EX_ST_FP(STORE_BLK(%fsrc, %dest)); \
add %dest, 0x40, %dest; \
ba,pt %xcc, target; \
nop;
@@ -103,7 +109,7 @@
subcc %left, 8, %left;\
bl,pn %xcc, 95f; \
faligndata %f0, %f1, %f48; \
- EX_ST(STORE(std, %f48, %dest)); \
+ EX_ST_FP(STORE(std, %f48, %dest)); \
add %dest, 8, %dest;
#define UNEVEN_VISCHUNK_LAST(dest, f0, f1, left) \
@@ -160,8 +166,8 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
and %g2, 0x38, %g2
1: subcc %g1, 0x1, %g1
- EX_LD(LOAD(ldub, %o1 + 0x00, %o3))
- EX_ST(STORE(stb, %o3, %o1 + %GLOBAL_SPARE))
+ EX_LD_FP(LOAD(ldub, %o1 + 0x00, %o3))
+ EX_ST_FP(STORE(stb, %o3, %o1 + %GLOBAL_SPARE))
bgu,pt %XCC, 1b
add %o1, 0x1, %o1
@@ -172,20 +178,20 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
be,pt %icc, 3f
alignaddr %o1, %g0, %o1
- EX_LD(LOAD(ldd, %o1, %f4))
-1: EX_LD(LOAD(ldd, %o1 + 0x8, %f6))
+ EX_LD_FP(LOAD(ldd, %o1, %f4))
+1: EX_LD_FP(LOAD(ldd, %o1 + 0x8, %f6))
add %o1, 0x8, %o1
subcc %g2, 0x8, %g2
faligndata %f4, %f6, %f0
- EX_ST(STORE(std, %f0, %o0))
+ EX_ST_FP(STORE(std, %f0, %o0))
be,pn %icc, 3f
add %o0, 0x8, %o0
- EX_LD(LOAD(ldd, %o1 + 0x8, %f4))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x8, %f4))
add %o1, 0x8, %o1
subcc %g2, 0x8, %g2
faligndata %f6, %f4, %f0
- EX_ST(STORE(std, %f0, %o0))
+ EX_ST_FP(STORE(std, %f0, %o0))
bne,pt %icc, 1b
add %o0, 0x8, %o0
@@ -208,13 +214,13 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
add %g1, %GLOBAL_SPARE, %g1
subcc %o2, %g3, %o2
- EX_LD(LOAD_BLK(%o1, %f0))
+ EX_LD_FP(LOAD_BLK(%o1, %f0))
add %o1, 0x40, %o1
add %g1, %g3, %g1
- EX_LD(LOAD_BLK(%o1, %f16))
+ EX_LD_FP(LOAD_BLK(%o1, %f16))
add %o1, 0x40, %o1
sub %GLOBAL_SPARE, 0x80, %GLOBAL_SPARE
- EX_LD(LOAD_BLK(%o1, %f32))
+ EX_LD_FP(LOAD_BLK(%o1, %f32))
add %o1, 0x40, %o1
/* There are 8 instances of the unrolled loop,
@@ -426,28 +432,28 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
62: FINISH_VISCHUNK(o0, f44, f46, g3)
63: UNEVEN_VISCHUNK_LAST(o0, f46, f0, g3)
-93: EX_LD(LOAD(ldd, %o1, %f2))
+93: EX_LD_FP(LOAD(ldd, %o1, %f2))
add %o1, 8, %o1
subcc %g3, 8, %g3
faligndata %f0, %f2, %f8
- EX_ST(STORE(std, %f8, %o0))
+ EX_ST_FP(STORE(std, %f8, %o0))
bl,pn %xcc, 95f
add %o0, 8, %o0
- EX_LD(LOAD(ldd, %o1, %f0))
+ EX_LD_FP(LOAD(ldd, %o1, %f0))
add %o1, 8, %o1
subcc %g3, 8, %g3
faligndata %f2, %f0, %f8
- EX_ST(STORE(std, %f8, %o0))
+ EX_ST_FP(STORE(std, %f8, %o0))
bge,pt %xcc, 93b
add %o0, 8, %o0
95: brz,pt %o2, 2f
mov %g1, %o1
-1: EX_LD(LOAD(ldub, %o1, %o3))
+1: EX_LD_FP(LOAD(ldub, %o1, %o3))
add %o1, 1, %o1
subcc %o2, 1, %o2
- EX_ST(STORE(stb, %o3, %o0))
+ EX_ST_FP(STORE(stb, %o3, %o0))
bne,pt %xcc, 1b
add %o0, 1, %o0
diff --git a/arch/sparc/lib/U3copy_from_user.S b/arch/sparc/lib/U3copy_from_user.S
index b1acd1331c33..88ad73d86fe4 100644
--- a/arch/sparc/lib/U3copy_from_user.S
+++ b/arch/sparc/lib/U3copy_from_user.S
@@ -11,6 +11,14 @@
.text; \
.align 4;
+#define EX_LD_FP(x) \
+98: x; \
+ .section __ex_table,"a";\
+ .align 4; \
+ .word 98b, __retl_one_fp;\
+ .text; \
+ .align 4;
+
#define FUNC_NAME U3copy_from_user
#define LOAD(type,addr,dest) type##a [addr] %asi, dest
#define EX_RETVAL(x) 0
diff --git a/arch/sparc/lib/U3copy_to_user.S b/arch/sparc/lib/U3copy_to_user.S
index ef1e493afdfa..845139d75537 100644
--- a/arch/sparc/lib/U3copy_to_user.S
+++ b/arch/sparc/lib/U3copy_to_user.S
@@ -11,6 +11,14 @@
.text; \
.align 4;
+#define EX_ST_FP(x) \
+98: x; \
+ .section __ex_table,"a";\
+ .align 4; \
+ .word 98b, __retl_one_fp;\
+ .text; \
+ .align 4;
+
#define FUNC_NAME U3copy_to_user
#define STORE(type,src,addr) type##a src, [addr] ASI_AIUS
#define STORE_BLK(src,addr) stda src, [addr] ASI_BLK_AIUS
diff --git a/arch/sparc/lib/U3memcpy.S b/arch/sparc/lib/U3memcpy.S
index 7cae9cc6a204..491ee69e4995 100644
--- a/arch/sparc/lib/U3memcpy.S
+++ b/arch/sparc/lib/U3memcpy.S
@@ -24,10 +24,16 @@
#ifndef EX_LD
#define EX_LD(x) x
#endif
+#ifndef EX_LD_FP
+#define EX_LD_FP(x) x
+#endif
#ifndef EX_ST
#define EX_ST(x) x
#endif
+#ifndef EX_ST_FP
+#define EX_ST_FP(x) x
+#endif
#ifndef EX_RETVAL
#define EX_RETVAL(x) x
@@ -120,8 +126,8 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
and %g2, 0x38, %g2
1: subcc %g1, 0x1, %g1
- EX_LD(LOAD(ldub, %o1 + 0x00, %o3))
- EX_ST(STORE(stb, %o3, %o1 + GLOBAL_SPARE))
+ EX_LD_FP(LOAD(ldub, %o1 + 0x00, %o3))
+ EX_ST_FP(STORE(stb, %o3, %o1 + GLOBAL_SPARE))
bgu,pt %XCC, 1b
add %o1, 0x1, %o1
@@ -132,20 +138,20 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
be,pt %icc, 3f
alignaddr %o1, %g0, %o1
- EX_LD(LOAD(ldd, %o1, %f4))
-1: EX_LD(LOAD(ldd, %o1 + 0x8, %f6))
+ EX_LD_FP(LOAD(ldd, %o1, %f4))
+1: EX_LD_FP(LOAD(ldd, %o1 + 0x8, %f6))
add %o1, 0x8, %o1
subcc %g2, 0x8, %g2
faligndata %f4, %f6, %f0
- EX_ST(STORE(std, %f0, %o0))
+ EX_ST_FP(STORE(std, %f0, %o0))
be,pn %icc, 3f
add %o0, 0x8, %o0
- EX_LD(LOAD(ldd, %o1 + 0x8, %f4))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x8, %f4))
add %o1, 0x8, %o1
subcc %g2, 0x8, %g2
faligndata %f6, %f4, %f2
- EX_ST(STORE(std, %f2, %o0))
+ EX_ST_FP(STORE(std, %f2, %o0))
bne,pt %icc, 1b
add %o0, 0x8, %o0
@@ -155,25 +161,25 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
LOAD(prefetch, %o1 + 0x080, #one_read)
LOAD(prefetch, %o1 + 0x0c0, #one_read)
LOAD(prefetch, %o1 + 0x100, #one_read)
- EX_LD(LOAD(ldd, %o1 + 0x000, %f0))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x000, %f0))
LOAD(prefetch, %o1 + 0x140, #one_read)
- EX_LD(LOAD(ldd, %o1 + 0x008, %f2))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x008, %f2))
LOAD(prefetch, %o1 + 0x180, #one_read)
- EX_LD(LOAD(ldd, %o1 + 0x010, %f4))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x010, %f4))
LOAD(prefetch, %o1 + 0x1c0, #one_read)
faligndata %f0, %f2, %f16
- EX_LD(LOAD(ldd, %o1 + 0x018, %f6))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x018, %f6))
faligndata %f2, %f4, %f18
- EX_LD(LOAD(ldd, %o1 + 0x020, %f8))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x020, %f8))
faligndata %f4, %f6, %f20
- EX_LD(LOAD(ldd, %o1 + 0x028, %f10))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x028, %f10))
faligndata %f6, %f8, %f22
- EX_LD(LOAD(ldd, %o1 + 0x030, %f12))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x030, %f12))
faligndata %f8, %f10, %f24
- EX_LD(LOAD(ldd, %o1 + 0x038, %f14))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x038, %f14))
faligndata %f10, %f12, %f26
- EX_LD(LOAD(ldd, %o1 + 0x040, %f0))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x040, %f0))
subcc GLOBAL_SPARE, 0x80, GLOBAL_SPARE
add %o1, 0x40, %o1
@@ -184,26 +190,26 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
.align 64
1:
- EX_LD(LOAD(ldd, %o1 + 0x008, %f2))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x008, %f2))
faligndata %f12, %f14, %f28
- EX_LD(LOAD(ldd, %o1 + 0x010, %f4))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x010, %f4))
faligndata %f14, %f0, %f30
- EX_ST(STORE_BLK(%f16, %o0))
- EX_LD(LOAD(ldd, %o1 + 0x018, %f6))
+ EX_ST_FP(STORE_BLK(%f16, %o0))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x018, %f6))
faligndata %f0, %f2, %f16
add %o0, 0x40, %o0
- EX_LD(LOAD(ldd, %o1 + 0x020, %f8))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x020, %f8))
faligndata %f2, %f4, %f18
- EX_LD(LOAD(ldd, %o1 + 0x028, %f10))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x028, %f10))
faligndata %f4, %f6, %f20
- EX_LD(LOAD(ldd, %o1 + 0x030, %f12))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x030, %f12))
subcc %o3, 0x01, %o3
faligndata %f6, %f8, %f22
- EX_LD(LOAD(ldd, %o1 + 0x038, %f14))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x038, %f14))
faligndata %f8, %f10, %f24
- EX_LD(LOAD(ldd, %o1 + 0x040, %f0))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x040, %f0))
LOAD(prefetch, %o1 + 0x1c0, #one_read)
faligndata %f10, %f12, %f26
bg,pt %XCC, 1b
@@ -211,29 +217,29 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
/* Finally we copy the last full 64-byte block. */
2:
- EX_LD(LOAD(ldd, %o1 + 0x008, %f2))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x008, %f2))
faligndata %f12, %f14, %f28
- EX_LD(LOAD(ldd, %o1 + 0x010, %f4))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x010, %f4))
faligndata %f14, %f0, %f30
- EX_ST(STORE_BLK(%f16, %o0))
- EX_LD(LOAD(ldd, %o1 + 0x018, %f6))
+ EX_ST_FP(STORE_BLK(%f16, %o0))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x018, %f6))
faligndata %f0, %f2, %f16
- EX_LD(LOAD(ldd, %o1 + 0x020, %f8))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x020, %f8))
faligndata %f2, %f4, %f18
- EX_LD(LOAD(ldd, %o1 + 0x028, %f10))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x028, %f10))
faligndata %f4, %f6, %f20
- EX_LD(LOAD(ldd, %o1 + 0x030, %f12))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x030, %f12))
faligndata %f6, %f8, %f22
- EX_LD(LOAD(ldd, %o1 + 0x038, %f14))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x038, %f14))
faligndata %f8, %f10, %f24
cmp %g1, 0
be,pt %XCC, 1f
add %o0, 0x40, %o0
- EX_LD(LOAD(ldd, %o1 + 0x040, %f0))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x040, %f0))
1: faligndata %f10, %f12, %f26
faligndata %f12, %f14, %f28
faligndata %f14, %f0, %f30
- EX_ST(STORE_BLK(%f16, %o0))
+ EX_ST_FP(STORE_BLK(%f16, %o0))
add %o0, 0x40, %o0
add %o1, 0x40, %o1
membar #Sync
@@ -253,20 +259,20 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */
sub %o2, %g2, %o2
be,a,pt %XCC, 1f
- EX_LD(LOAD(ldd, %o1 + 0x00, %f0))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x00, %f0))
-1: EX_LD(LOAD(ldd, %o1 + 0x08, %f2))
+1: EX_LD_FP(LOAD(ldd, %o1 + 0x08, %f2))
add %o1, 0x8, %o1
subcc %g2, 0x8, %g2
faligndata %f0, %f2, %f8
- EX_ST(STORE(std, %f8, %o0))
+ EX_ST_FP(STORE(std, %f8, %o0))
be,pn %XCC, 2f
add %o0, 0x8, %o0
- EX_LD(LOAD(ldd, %o1 + 0x08, %f0))
+ EX_LD_FP(LOAD(ldd, %o1 + 0x08, %f0))
add %o1, 0x8, %o1
subcc %g2, 0x8, %g2
faligndata %f2, %f0, %f8
- EX_ST(STORE(std, %f8, %o0))
+ EX_ST_FP(STORE(std, %f8, %o0))
bne,pn %XCC, 1b
add %o0, 0x8, %o0
diff --git a/arch/sparc/net/bpf_jit_comp.c b/arch/sparc/net/bpf_jit_comp.c
index 22564f5f2364..3e6e05a7c4c2 100644
--- a/arch/sparc/net/bpf_jit_comp.c
+++ b/arch/sparc/net/bpf_jit_comp.c
@@ -420,22 +420,9 @@ void bpf_jit_compile(struct bpf_prog *fp)
}
emit_reg_move(O7, r_saved_O7);
- switch (filter[0].code) {
- case BPF_RET | BPF_K:
- case BPF_LD | BPF_W | BPF_LEN:
- case BPF_LD | BPF_W | BPF_ABS:
- case BPF_LD | BPF_H | BPF_ABS:
- case BPF_LD | BPF_B | BPF_ABS:
- /* The first instruction sets the A register (or is
- * a "RET 'constant'")
- */
- break;
- default:
- /* Make sure we dont leak kernel information to the
- * user.
- */
+ /* Make sure we dont leak kernel information to the user. */
+ if (bpf_needs_clear_a(&filter[0]))
emit_clear(r_A); /* A = 0 */
- }
for (i = 0; i < flen; i++) {
unsigned int K = filter[i].k;
diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig
index 106c21bd7f44..be05b4ae02b6 100644
--- a/arch/tile/Kconfig
+++ b/arch/tile/Kconfig
@@ -19,6 +19,7 @@ config TILE
select VIRT_TO_BUS
select SYS_HYPERVISOR
select ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS
+ select ARCH_HAS_DEVMEM_IS_ALLOWED
select ARCH_HAVE_NMI_SAFE_CMPXCHG
select GENERIC_CLOCKEVENTS
select MODULES_USE_ELF_RELA
@@ -116,9 +117,6 @@ config ARCH_DISCONTIGMEM_DEFAULT
config TRACE_IRQFLAGS_SUPPORT
def_bool y
-config STRICT_DEVMEM
- def_bool y
-
# SMP is required for Tilera Linux.
config SMP
def_bool y
@@ -176,8 +174,6 @@ config NR_CPUS
smaller kernel memory footprint results from using a smaller
value on chips with fewer tiles.
-if TILEGX
-
choice
prompt "Kernel page size"
default PAGE_SIZE_64KB
@@ -188,8 +184,11 @@ choice
connections, etc., it may be better to select 16KB, which uses
memory more efficiently at some cost in TLB performance.
- Note that this option is TILE-Gx specific; currently
- TILEPro page size is set by rebuilding the hypervisor.
+ Note that for TILEPro, you must also rebuild the hypervisor
+ with a matching page size.
+
+config PAGE_SIZE_4KB
+ bool "4KB" if TILEPRO
config PAGE_SIZE_16KB
bool "16KB"
@@ -199,8 +198,6 @@ config PAGE_SIZE_64KB
endchoice
-endif
-
source "kernel/Kconfig.hz"
config KEXEC
diff --git a/arch/tile/include/asm/cmpxchg.h b/arch/tile/include/asm/cmpxchg.h
index 0ccda3c425be..25d5899497be 100644
--- a/arch/tile/include/asm/cmpxchg.h
+++ b/arch/tile/include/asm/cmpxchg.h
@@ -127,8 +127,6 @@ long long _atomic64_cmpxchg(long long *v, long long o, long long n);
#endif
-#define tas(ptr) xchg((ptr), 1)
-
#endif /* __ASSEMBLY__ */
#endif /* _ASM_TILE_CMPXCHG_H */
diff --git a/arch/tile/include/asm/page.h b/arch/tile/include/asm/page.h
index a213a8d84a95..8eca6a0e1762 100644
--- a/arch/tile/include/asm/page.h
+++ b/arch/tile/include/asm/page.h
@@ -20,15 +20,17 @@
#include <arch/chip.h>
/* PAGE_SHIFT and HPAGE_SHIFT determine the page sizes. */
-#if defined(CONFIG_PAGE_SIZE_16KB)
+#if defined(CONFIG_PAGE_SIZE_4KB) /* tilepro only */
+#define PAGE_SHIFT 12
+#define CTX_PAGE_FLAG HV_CTX_PG_SM_4K
+#elif defined(CONFIG_PAGE_SIZE_16KB)
#define PAGE_SHIFT 14
#define CTX_PAGE_FLAG HV_CTX_PG_SM_16K
#elif defined(CONFIG_PAGE_SIZE_64KB)
#define PAGE_SHIFT 16
#define CTX_PAGE_FLAG HV_CTX_PG_SM_64K
#else
-#define PAGE_SHIFT HV_LOG2_DEFAULT_PAGE_SIZE_SMALL
-#define CTX_PAGE_FLAG 0
+#error Page size not specified in Kconfig
#endif
#define HPAGE_SHIFT HV_LOG2_DEFAULT_PAGE_SIZE_LARGE
diff --git a/arch/tile/kernel/perf_event.c b/arch/tile/kernel/perf_event.c
index bb509cee3b59..8767060d70fb 100644
--- a/arch/tile/kernel/perf_event.c
+++ b/arch/tile/kernel/perf_event.c
@@ -21,7 +21,7 @@
* Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar
* Copyright (C) 2009 Jaswinder Singh Rajput
* Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter
- * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra
* Copyright (C) 2009 Intel Corporation, <markus.t.metzger@intel.com>
* Copyright (C) 2009 Google, Inc., Stephane Eranian
*/
diff --git a/arch/um/Kconfig.common b/arch/um/Kconfig.common
index d195a87ca542..cc0013475444 100644
--- a/arch/um/Kconfig.common
+++ b/arch/um/Kconfig.common
@@ -2,6 +2,7 @@ config UML
bool
default y
select HAVE_ARCH_AUDITSYSCALL
+ select HAVE_ARCH_SECCOMP_FILTER
select HAVE_UID16
select HAVE_FUTEX_CMPXCHG if FUTEX
select GENERIC_IRQ_SHOW
diff --git a/arch/um/Kconfig.um b/arch/um/Kconfig.um
index 28a9885e3a37..4b2ed5858b2e 100644
--- a/arch/um/Kconfig.um
+++ b/arch/um/Kconfig.um
@@ -104,3 +104,19 @@ config PGTABLE_LEVELS
int
default 3 if 3_LEVEL_PGTABLES
default 2
+
+config SECCOMP
+ def_bool y
+ prompt "Enable seccomp to safely compute untrusted bytecode"
+ ---help---
+ This kernel feature is useful for number crunching applications
+ that may need to compute untrusted bytecode during their
+ execution. By using pipes or other transports made available to
+ the process as file descriptors supporting the read/write
+ syscalls, it's possible to isolate those applications in
+ their own address space using seccomp. Once seccomp is
+ enabled via prctl(PR_SET_SECCOMP), it cannot be disabled
+ and the task is only allowed to execute a few safe syscalls
+ defined by each seccomp mode.
+
+ If unsure, say Y.
diff --git a/arch/um/Makefile b/arch/um/Makefile
index 25ed4098640e..e3abe6f3156d 100644
--- a/arch/um/Makefile
+++ b/arch/um/Makefile
@@ -131,7 +131,7 @@ export LDS_ELF_FORMAT := $(ELF_FORMAT)
# The wrappers will select whether using "malloc" or the kernel allocator.
LINK_WRAPS = -Wl,--wrap,malloc -Wl,--wrap,free -Wl,--wrap,calloc
-LD_FLAGS_CMDLINE = $(foreach opt,$(LDFLAGS),-Wl,$(opt)) -lrt
+LD_FLAGS_CMDLINE = $(foreach opt,$(LDFLAGS),-Wl,$(opt))
# Used by link-vmlinux.sh which has special support for um link
export CFLAGS_vmlinux := $(LINK-y) $(LINK_WRAPS) $(LD_FLAGS_CMDLINE)
diff --git a/arch/um/drivers/hostaudio_kern.c b/arch/um/drivers/hostaudio_kern.c
index f6b911cc3923..3a4b58730f5f 100644
--- a/arch/um/drivers/hostaudio_kern.c
+++ b/arch/um/drivers/hostaudio_kern.c
@@ -105,13 +105,9 @@ static ssize_t hostaudio_write(struct file *file, const char __user *buffer,
printk(KERN_DEBUG "hostaudio: write called, count = %d\n", count);
#endif
- kbuf = kmalloc(count, GFP_KERNEL);
- if (kbuf == NULL)
- return -ENOMEM;
-
- err = -EFAULT;
- if (copy_from_user(kbuf, buffer, count))
- goto out;
+ kbuf = memdup_user(buffer, count);
+ if (IS_ERR(kbuf))
+ return PTR_ERR(kbuf);
err = os_write_file(state->fd, kbuf, count);
if (err < 0)
diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c
index 29880c9b324e..b821b13d343a 100644
--- a/arch/um/drivers/mconsole_kern.c
+++ b/arch/um/drivers/mconsole_kern.c
@@ -748,19 +748,11 @@ static ssize_t mconsole_proc_write(struct file *file,
{
char *buf;
- buf = kmalloc(count + 1, GFP_KERNEL);
- if (buf == NULL)
- return -ENOMEM;
-
- if (copy_from_user(buf, buffer, count)) {
- count = -EFAULT;
- goto out;
- }
-
- buf[count] = '\0';
+ buf = memdup_user_nul(buffer, count);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
- out:
kfree(buf);
return count;
}
diff --git a/arch/um/drivers/net_user.c b/arch/um/drivers/net_user.c
index e697a4136707..e9f8445861dc 100644
--- a/arch/um/drivers/net_user.c
+++ b/arch/um/drivers/net_user.c
@@ -249,21 +249,23 @@ void close_addr(unsigned char *addr, unsigned char *netmask, void *arg)
char *split_if_spec(char *str, ...)
{
- char **arg, *end;
+ char **arg, *end, *ret = NULL;
va_list ap;
va_start(ap, str);
while ((arg = va_arg(ap, char **)) != NULL) {
if (*str == '\0')
- return NULL;
+ goto out;
end = strchr(str, ',');
if (end != str)
*arg = str;
if (end == NULL)
- return NULL;
+ goto out;
*end++ = '\0';
str = end;
}
+ ret = str;
+out:
va_end(ap);
- return str;
+ return ret;
}
diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c
index e8ab93c3e638..39ba20755e03 100644
--- a/arch/um/drivers/ubd_kern.c
+++ b/arch/um/drivers/ubd_kern.c
@@ -535,11 +535,7 @@ static int read_cow_bitmap(int fd, void *buf, int offset, int len)
{
int err;
- err = os_seek_file(fd, offset);
- if (err < 0)
- return err;
-
- err = os_read_file(fd, buf, len);
+ err = os_pread_file(fd, buf, len, offset);
if (err < 0)
return err;
@@ -1377,14 +1373,8 @@ static int update_bitmap(struct io_thread_req *req)
if(req->cow_offset == -1)
return 0;
- n = os_seek_file(req->fds[1], req->cow_offset);
- if(n < 0){
- printk("do_io - bitmap lseek failed : err = %d\n", -n);
- return 1;
- }
-
- n = os_write_file(req->fds[1], &req->bitmap_words,
- sizeof(req->bitmap_words));
+ n = os_pwrite_file(req->fds[1], &req->bitmap_words,
+ sizeof(req->bitmap_words), req->cow_offset);
if(n != sizeof(req->bitmap_words)){
printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
req->fds[1]);
@@ -1399,7 +1389,6 @@ static void do_io(struct io_thread_req *req)
char *buf;
unsigned long len;
int n, nsectors, start, end, bit;
- int err;
__u64 off;
if (req->op == UBD_FLUSH) {
@@ -1428,18 +1417,12 @@ static void do_io(struct io_thread_req *req)
len = (end - start) * req->sectorsize;
buf = &req->buffer[start * req->sectorsize];
- err = os_seek_file(req->fds[bit], off);
- if(err < 0){
- printk("do_io - lseek failed : err = %d\n", -err);
- req->error = 1;
- return;
- }
if(req->op == UBD_READ){
n = 0;
do {
buf = &buf[n];
len -= n;
- n = os_read_file(req->fds[bit], buf, len);
+ n = os_pread_file(req->fds[bit], buf, len, off);
if (n < 0) {
printk("do_io - read failed, err = %d "
"fd = %d\n", -n, req->fds[bit]);
@@ -1449,7 +1432,7 @@ static void do_io(struct io_thread_req *req)
} while((n < len) && (n != 0));
if (n < len) memset(&buf[n], 0, len - n);
} else {
- n = os_write_file(req->fds[bit], buf, len);
+ n = os_pwrite_file(req->fds[bit], buf, len, off);
if(n != len){
printk("do_io - write failed err = %d "
"fd = %d\n", -n, req->fds[bit]);
diff --git a/arch/um/include/asm/hardirq.h b/arch/um/include/asm/hardirq.h
new file mode 100644
index 000000000000..756f0778e327
--- /dev/null
+++ b/arch/um/include/asm/hardirq.h
@@ -0,0 +1,23 @@
+#ifndef __ASM_UM_HARDIRQ_H
+#define __ASM_UM_HARDIRQ_H
+
+#include <linux/cache.h>
+#include <linux/threads.h>
+
+typedef struct {
+ unsigned int __softirq_pending;
+} ____cacheline_aligned irq_cpustat_t;
+
+#include <linux/irq_cpustat.h> /* Standard mappings for irq_cpustat_t above */
+#include <linux/irq.h>
+
+#ifndef ack_bad_irq
+static inline void ack_bad_irq(unsigned int irq)
+{
+ printk(KERN_CRIT "unexpected IRQ trap at vector %02x\n", irq);
+}
+#endif
+
+#define __ARCH_IRQ_EXIT_IRQS_DISABLED 1
+
+#endif /* __ASM_UM_HARDIRQ_H */
diff --git a/arch/um/include/asm/syscall-generic.h b/arch/um/include/asm/syscall-generic.h
new file mode 100644
index 000000000000..9fb9cf8cd39a
--- /dev/null
+++ b/arch/um/include/asm/syscall-generic.h
@@ -0,0 +1,138 @@
+/*
+ * Access to user system call parameters and results
+ *
+ * See asm-generic/syscall.h for function descriptions.
+ *
+ * Copyright (C) 2015 Mickaël Salaün <mic@digikod.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __UM_SYSCALL_GENERIC_H
+#define __UM_SYSCALL_GENERIC_H
+
+#include <asm/ptrace.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <sysdep/ptrace.h>
+
+static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs)
+{
+
+ return PT_REGS_SYSCALL_NR(regs);
+}
+
+static inline void syscall_rollback(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ /* do nothing */
+}
+
+static inline long syscall_get_error(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ const long error = regs_return_value(regs);
+
+ return IS_ERR_VALUE(error) ? error : 0;
+}
+
+static inline long syscall_get_return_value(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ return regs_return_value(regs);
+}
+
+static inline void syscall_set_return_value(struct task_struct *task,
+ struct pt_regs *regs,
+ int error, long val)
+{
+ PT_REGS_SET_SYSCALL_RETURN(regs, (long) error ?: val);
+}
+
+static inline void syscall_get_arguments(struct task_struct *task,
+ struct pt_regs *regs,
+ unsigned int i, unsigned int n,
+ unsigned long *args)
+{
+ const struct uml_pt_regs *r = &regs->regs;
+
+ switch (i) {
+ case 0:
+ if (!n--)
+ break;
+ *args++ = UPT_SYSCALL_ARG1(r);
+ case 1:
+ if (!n--)
+ break;
+ *args++ = UPT_SYSCALL_ARG2(r);
+ case 2:
+ if (!n--)
+ break;
+ *args++ = UPT_SYSCALL_ARG3(r);
+ case 3:
+ if (!n--)
+ break;
+ *args++ = UPT_SYSCALL_ARG4(r);
+ case 4:
+ if (!n--)
+ break;
+ *args++ = UPT_SYSCALL_ARG5(r);
+ case 5:
+ if (!n--)
+ break;
+ *args++ = UPT_SYSCALL_ARG6(r);
+ case 6:
+ if (!n--)
+ break;
+ default:
+ BUG();
+ break;
+ }
+}
+
+static inline void syscall_set_arguments(struct task_struct *task,
+ struct pt_regs *regs,
+ unsigned int i, unsigned int n,
+ const unsigned long *args)
+{
+ struct uml_pt_regs *r = &regs->regs;
+
+ switch (i) {
+ case 0:
+ if (!n--)
+ break;
+ UPT_SYSCALL_ARG1(r) = *args++;
+ case 1:
+ if (!n--)
+ break;
+ UPT_SYSCALL_ARG2(r) = *args++;
+ case 2:
+ if (!n--)
+ break;
+ UPT_SYSCALL_ARG3(r) = *args++;
+ case 3:
+ if (!n--)
+ break;
+ UPT_SYSCALL_ARG4(r) = *args++;
+ case 4:
+ if (!n--)
+ break;
+ UPT_SYSCALL_ARG5(r) = *args++;
+ case 5:
+ if (!n--)
+ break;
+ UPT_SYSCALL_ARG6(r) = *args++;
+ case 6:
+ if (!n--)
+ break;
+ default:
+ BUG();
+ break;
+ }
+}
+
+/* See arch/x86/um/asm/syscall.h for syscall_get_arch() definition. */
+
+#endif /* __UM_SYSCALL_GENERIC_H */
diff --git a/arch/um/include/asm/thread_info.h b/arch/um/include/asm/thread_info.h
index 53968aaf76f9..053baff03674 100644
--- a/arch/um/include/asm/thread_info.h
+++ b/arch/um/include/asm/thread_info.h
@@ -62,11 +62,13 @@ static inline struct thread_info *current_thread_info(void)
#define TIF_SYSCALL_AUDIT 6
#define TIF_RESTORE_SIGMASK 7
#define TIF_NOTIFY_RESUME 8
+#define TIF_SECCOMP 9 /* secure computing */
#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
#define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED)
#define _TIF_MEMDIE (1 << TIF_MEMDIE)
#define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT)
+#define _TIF_SECCOMP (1 << TIF_SECCOMP)
#endif
diff --git a/arch/um/include/shared/os.h b/arch/um/include/shared/os.h
index 868e6c3f83dd..de5d572225f3 100644
--- a/arch/um/include/shared/os.h
+++ b/arch/um/include/shared/os.h
@@ -146,6 +146,8 @@ extern int os_read_file(int fd, void *buf, int len);
extern int os_write_file(int fd, const void *buf, int count);
extern int os_sync_file(int fd);
extern int os_file_size(const char *file, unsigned long long *size_out);
+extern int os_pread_file(int fd, void *buf, int len, unsigned long long offset);
+extern int os_pwrite_file(int fd, const void *buf, int count, unsigned long long offset);
extern int os_file_modtime(const char *file, unsigned long *modtime);
extern int os_pipe(int *fd, int stream, int close_on_exec);
extern int os_set_fd_async(int fd);
@@ -282,7 +284,6 @@ extern void initial_thread_cb_skas(void (*proc)(void *),
void *arg);
extern void halt_skas(void);
extern void reboot_skas(void);
-extern int get_syscall(struct uml_pt_regs *regs);
/* irq.c */
extern int os_waiting_for_events(struct irq_fd *active_fds);
diff --git a/arch/um/kernel/signal.c b/arch/um/kernel/signal.c
index 57acbd67d85d..fc8be0e3a4ff 100644
--- a/arch/um/kernel/signal.c
+++ b/arch/um/kernel/signal.c
@@ -69,7 +69,7 @@ void do_signal(struct pt_regs *regs)
struct ksignal ksig;
int handled_sig = 0;
- while (get_signal(&ksig)) {
+ if (get_signal(&ksig)) {
handled_sig = 1;
/* Whee! Actually deliver the signal. */
handle_signal(&ksig, regs);
diff --git a/arch/um/kernel/skas/syscall.c b/arch/um/kernel/skas/syscall.c
index 1683b8efdfda..48b0dcbd87be 100644
--- a/arch/um/kernel/skas/syscall.c
+++ b/arch/um/kernel/skas/syscall.c
@@ -5,31 +5,38 @@
#include <linux/kernel.h>
#include <linux/ptrace.h>
+#include <linux/seccomp.h>
#include <kern_util.h>
#include <sysdep/ptrace.h>
+#include <sysdep/ptrace_user.h>
#include <sysdep/syscalls.h>
-#include <os.h>
void handle_syscall(struct uml_pt_regs *r)
{
struct pt_regs *regs = container_of(r, struct pt_regs, regs);
- long result;
int syscall;
- if (syscall_trace_enter(regs)) {
- result = -ENOSYS;
+ /* Initialize the syscall number and default return value. */
+ UPT_SYSCALL_NR(r) = PT_SYSCALL_NR(r->gp);
+ PT_REGS_SET_SYSCALL_RETURN(regs, -ENOSYS);
+
+ /* Do the secure computing check first; failures should be fast. */
+ if (secure_computing() == -1)
+ return;
+
+ if (syscall_trace_enter(regs))
goto out;
- }
- syscall = get_syscall(r);
+ /* Update the syscall number after orig_ax has potentially been updated
+ * with ptrace.
+ */
+ UPT_SYSCALL_NR(r) = PT_SYSCALL_NR(r->gp);
+ syscall = UPT_SYSCALL_NR(r);
- if ((syscall > __NR_syscall_max) || syscall < 0)
- result = -ENOSYS;
- else
- result = EXECUTE_SYSCALL(syscall, regs);
+ if (syscall >= 0 && syscall <= __NR_syscall_max)
+ PT_REGS_SET_SYSCALL_RETURN(regs,
+ EXECUTE_SYSCALL(syscall, regs));
out:
- PT_REGS_SET_SYSCALL_RETURN(regs, result);
-
syscall_trace_leave(regs);
}
diff --git a/arch/um/os-Linux/file.c b/arch/um/os-Linux/file.c
index 26e0164895e4..2db18cbbb0ea 100644
--- a/arch/um/os-Linux/file.c
+++ b/arch/um/os-Linux/file.c
@@ -264,6 +264,15 @@ int os_read_file(int fd, void *buf, int len)
return n;
}
+int os_pread_file(int fd, void *buf, int len, unsigned long long offset)
+{
+ int n = pread(fd, buf, len, offset);
+
+ if (n < 0)
+ return -errno;
+ return n;
+}
+
int os_write_file(int fd, const void *buf, int len)
{
int n = write(fd, (void *) buf, len);
@@ -282,6 +291,16 @@ int os_sync_file(int fd)
return n;
}
+int os_pwrite_file(int fd, const void *buf, int len, unsigned long long offset)
+{
+ int n = pwrite(fd, (void *) buf, len, offset);
+
+ if (n < 0)
+ return -errno;
+ return n;
+}
+
+
int os_file_size(const char *file, unsigned long long *size_out)
{
struct uml_stat buf;
diff --git a/arch/um/os-Linux/mem.c b/arch/um/os-Linux/mem.c
index 897e9ad0c108..8b1767668515 100644
--- a/arch/um/os-Linux/mem.c
+++ b/arch/um/os-Linux/mem.c
@@ -106,6 +106,17 @@ static int __init make_tempfile(const char *template)
}
}
+#ifdef O_TMPFILE
+ fd = open(tempdir, O_CLOEXEC | O_RDWR | O_EXCL | O_TMPFILE, 0700);
+ /*
+ * If the running system does not support O_TMPFILE flag then retry
+ * without it.
+ */
+ if (fd != -1 || (errno != EINVAL && errno != EISDIR &&
+ errno != EOPNOTSUPP))
+ return fd;
+#endif
+
tempname = malloc(strlen(tempdir) + strlen(template) + 1);
if (tempname == NULL)
return -1;
@@ -142,12 +153,6 @@ static int __init create_tmp_file(unsigned long long len)
if (fd < 0)
exit(1);
- err = fchmod(fd, 0777);
- if (err < 0) {
- perror("fchmod");
- exit(1);
- }
-
/*
* Seek to len - 1 because writing a character there will
* increase the file size by one byte, to the desired length.
diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c
index c211153ca69a..7801666514ed 100644
--- a/arch/um/os-Linux/signal.c
+++ b/arch/um/os-Linux/signal.c
@@ -62,6 +62,7 @@ static void sig_handler_common(int sig, struct siginfo *si, mcontext_t *mc)
static int signals_enabled;
static unsigned int signals_pending;
+static unsigned int signals_active = 0;
void sig_handler(int sig, struct siginfo *si, mcontext_t *mc)
{
@@ -101,7 +102,12 @@ void timer_alarm_handler(int sig, struct siginfo *unused_si, mcontext_t *mc)
block_signals();
+ signals_active |= SIGALRM_MASK;
+
timer_real_alarm_handler(mc);
+
+ signals_active &= ~SIGALRM_MASK;
+
set_signals(enabled);
}
@@ -286,8 +292,16 @@ void unblock_signals(void)
if (save_pending & SIGIO_MASK)
sig_handler_common(SIGIO, NULL, NULL);
- if (save_pending & SIGALRM_MASK)
+ /* Do not reenter the handler */
+
+ if ((save_pending & SIGALRM_MASK) && (!(signals_active & SIGALRM_MASK)))
timer_real_alarm_handler(NULL);
+
+ /* Rerun the loop only if there is still pending SIGIO and not in TIMER handler */
+
+ if (!(signals_pending & SIGIO_MASK) && (signals_active & SIGALRM_MASK))
+ return;
+
}
}
diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c
index b856c66ebd3a..23025d645160 100644
--- a/arch/um/os-Linux/skas/process.c
+++ b/arch/um/os-Linux/skas/process.c
@@ -172,13 +172,6 @@ static void handle_trap(int pid, struct uml_pt_regs *regs,
handle_syscall(regs);
}
-int get_syscall(struct uml_pt_regs *regs)
-{
- UPT_SYSCALL_NR(regs) = PT_SYSCALL_NR(regs->gp);
-
- return UPT_SYSCALL_NR(regs);
-}
-
extern char __syscall_stub_start[];
static int userspace_tramp(void *stack)
diff --git a/arch/um/os-Linux/start_up.c b/arch/um/os-Linux/start_up.c
index 47f1ff056a54..22a358ef1b0c 100644
--- a/arch/um/os-Linux/start_up.c
+++ b/arch/um/os-Linux/start_up.c
@@ -94,6 +94,8 @@ static int start_ptraced_child(void)
{
int pid, n, status;
+ fflush(stdout);
+
pid = fork();
if (pid == 0)
ptrace_child();
diff --git a/arch/unicore32/Kconfig b/arch/unicore32/Kconfig
index c9faddc61100..5dc4c0a43ccd 100644
--- a/arch/unicore32/Kconfig
+++ b/arch/unicore32/Kconfig
@@ -1,5 +1,6 @@
config UNICORE32
def_bool y
+ select ARCH_HAS_DEVMEM_IS_ALLOWED
select ARCH_MIGHT_HAVE_PC_PARPORT
select ARCH_MIGHT_HAVE_PC_SERIO
select HAVE_MEMBLOCK
diff --git a/arch/unicore32/Kconfig.debug b/arch/unicore32/Kconfig.debug
index 1a3626239843..f075bbe1d46f 100644
--- a/arch/unicore32/Kconfig.debug
+++ b/arch/unicore32/Kconfig.debug
@@ -2,20 +2,6 @@ menu "Kernel hacking"
source "lib/Kconfig.debug"
-config STRICT_DEVMEM
- bool "Filter access to /dev/mem"
- depends on MMU
- ---help---
- If this option is disabled, you allow userspace (root) access to all
- of memory, including kernel and userspace memory. Accidental
- access to this is obviously disastrous, but specific access can
- be used by people debugging the kernel.
-
- If this option is switched on, the /dev/mem file only allows
- userspace access to memory mapped peripherals.
-
- If in doubt, say Y.
-
config EARLY_PRINTK
def_bool DEBUG_OCD
help
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index db3622f22b61..24f362bf3ec6 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -24,6 +24,7 @@ config X86
select ARCH_DISCARD_MEMBLOCK
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
select ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS
+ select ARCH_HAS_DEVMEM_IS_ALLOWED
select ARCH_HAS_ELF_RANDOMIZE
select ARCH_HAS_FAST_MULTIPLIER
select ARCH_HAS_GCOV_PROFILE_ALL
@@ -82,6 +83,8 @@ config X86
select HAVE_ARCH_KASAN if X86_64 && SPARSEMEM_VMEMMAP
select HAVE_ARCH_KGDB
select HAVE_ARCH_KMEMCHECK
+ select HAVE_ARCH_MMAP_RND_BITS if MMU
+ select HAVE_ARCH_MMAP_RND_COMPAT_BITS if MMU && COMPAT
select HAVE_ARCH_SECCOMP_FILTER
select HAVE_ARCH_SOFT_DIRTY if X86_64
select HAVE_ARCH_TRACEHOOK
@@ -183,6 +186,20 @@ config HAVE_LATENCYTOP_SUPPORT
config MMU
def_bool y
+config ARCH_MMAP_RND_BITS_MIN
+ default 28 if 64BIT
+ default 8
+
+config ARCH_MMAP_RND_BITS_MAX
+ default 32 if 64BIT
+ default 16
+
+config ARCH_MMAP_RND_COMPAT_BITS_MIN
+ default 8
+
+config ARCH_MMAP_RND_COMPAT_BITS_MAX
+ default 16
+
config SBUS
bool
@@ -349,6 +366,17 @@ config X86_FEATURE_NAMES
If in doubt, say Y.
+config X86_FAST_FEATURE_TESTS
+ bool "Fast CPU feature tests" if EMBEDDED
+ default y
+ ---help---
+ Some fast-paths in the kernel depend on the capabilities of the CPU.
+ Say Y here for the kernel to patch in the appropriate code at runtime
+ based on the capabilities of the CPU. The infrastructure for patching
+ code at runtime takes up some additional space; space-constrained
+ embedded systems may wish to say N here to produce smaller, slightly
+ slower code.
+
config X86_X2APIC
bool "Support x2apic"
depends on X86_LOCAL_APIC && X86_64 && (IRQ_REMAP || HYPERVISOR_GUEST)
@@ -523,9 +551,10 @@ config X86_INTEL_QUARK
config X86_INTEL_LPSS
bool "Intel Low Power Subsystem Support"
- depends on ACPI
+ depends on X86 && ACPI
select COMMON_CLK
select PINCTRL
+ select IOSF_MBI
---help---
Select to build support for Intel Low Power Subsystem such as
found on Intel Lynxpoint PCH. Selecting this option enables
@@ -687,6 +716,14 @@ config PARAVIRT_SPINLOCKS
If you are unsure how to answer this question, answer Y.
+config QUEUED_LOCK_STAT
+ bool "Paravirt queued spinlock statistics"
+ depends on PARAVIRT_SPINLOCKS && DEBUG_FS && QUEUED_SPINLOCKS
+ ---help---
+ Enable the collection of statistical data on the slowpath
+ behavior of paravirtualized queued spinlocks and report
+ them on debugfs.
+
source "arch/x86/xen/Kconfig"
config KVM_GUEST
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
index 137dfa96aa14..9b18ed97a8a2 100644
--- a/arch/x86/Kconfig.debug
+++ b/arch/x86/Kconfig.debug
@@ -5,23 +5,6 @@ config TRACE_IRQFLAGS_SUPPORT
source "lib/Kconfig.debug"
-config STRICT_DEVMEM
- bool "Filter access to /dev/mem"
- ---help---
- If this option is disabled, you allow userspace (root) access to all
- of memory, including kernel and userspace memory. Accidental
- access to this is obviously disastrous, but specific access can
- be used by people debugging the kernel. Note that with PAT support
- enabled, even in this case there are restrictions on /dev/mem
- use due to the cache aliasing requirements.
-
- If this option is switched on, the /dev/mem file only allows
- userspace access to PCI space and the BIOS code and data regions.
- This is sufficient for dosemu and X and all common users of
- /dev/mem.
-
- If in doubt, say Y.
-
config X86_VERBOSE_BOOTUP
bool "Enable verbose x86 bootup info messages"
default y
@@ -69,7 +52,7 @@ config X86_PTDUMP_CORE
def_bool n
config X86_PTDUMP
- bool "Export kernel pagetable layout to userspace via debugfs"
+ tristate "Export kernel pagetable layout to userspace via debugfs"
depends on DEBUG_KERNEL
select DEBUG_FS
select X86_PTDUMP_CORE
diff --git a/arch/x86/boot/boot.h b/arch/x86/boot/boot.h
index 0033e96c3f09..9011a88353de 100644
--- a/arch/x86/boot/boot.h
+++ b/arch/x86/boot/boot.h
@@ -23,7 +23,6 @@
#include <stdarg.h>
#include <linux/types.h>
#include <linux/edd.h>
-#include <asm/boot.h>
#include <asm/setup.h>
#include "bitops.h"
#include "ctype.h"
diff --git a/arch/x86/boot/video-mode.c b/arch/x86/boot/video-mode.c
index aa8a96b052e3..95c7a818c0ed 100644
--- a/arch/x86/boot/video-mode.c
+++ b/arch/x86/boot/video-mode.c
@@ -19,6 +19,8 @@
#include "video.h"
#include "vesa.h"
+#include <uapi/asm/boot.h>
+
/*
* Common variables
*/
diff --git a/arch/x86/boot/video.c b/arch/x86/boot/video.c
index 05111bb8d018..77780e386e9b 100644
--- a/arch/x86/boot/video.c
+++ b/arch/x86/boot/video.c
@@ -13,6 +13,8 @@
* Select video mode
*/
+#include <uapi/asm/boot.h>
+
#include "boot.h"
#include "video.h"
#include "vesa.h"
diff --git a/arch/x86/crypto/chacha20_glue.c b/arch/x86/crypto/chacha20_glue.c
index 722bacea040e..8baaff5af0b5 100644
--- a/arch/x86/crypto/chacha20_glue.c
+++ b/arch/x86/crypto/chacha20_glue.c
@@ -125,7 +125,7 @@ static struct crypto_alg alg = {
static int __init chacha20_simd_mod_init(void)
{
- if (!cpu_has_ssse3)
+ if (!boot_cpu_has(X86_FEATURE_SSSE3))
return -ENODEV;
#ifdef CONFIG_AS_AVX2
diff --git a/arch/x86/crypto/crc32c-intel_glue.c b/arch/x86/crypto/crc32c-intel_glue.c
index 81a595d75cf5..0e9871693f24 100644
--- a/arch/x86/crypto/crc32c-intel_glue.c
+++ b/arch/x86/crypto/crc32c-intel_glue.c
@@ -257,7 +257,7 @@ static int __init crc32c_intel_mod_init(void)
if (!x86_match_cpu(crc32c_cpu_id))
return -ENODEV;
#ifdef CONFIG_X86_64
- if (cpu_has_pclmulqdq) {
+ if (boot_cpu_has(X86_FEATURE_PCLMULQDQ)) {
alg.update = crc32c_pcl_intel_update;
alg.finup = crc32c_pcl_intel_finup;
alg.digest = crc32c_pcl_intel_digest;
diff --git a/arch/x86/crypto/ghash-clmulni-intel_glue.c b/arch/x86/crypto/ghash-clmulni-intel_glue.c
index 440df0c7a2ee..a69321a77783 100644
--- a/arch/x86/crypto/ghash-clmulni-intel_glue.c
+++ b/arch/x86/crypto/ghash-clmulni-intel_glue.c
@@ -219,6 +219,29 @@ static int ghash_async_final(struct ahash_request *req)
}
}
+static int ghash_async_import(struct ahash_request *req, const void *in)
+{
+ struct ahash_request *cryptd_req = ahash_request_ctx(req);
+ struct shash_desc *desc = cryptd_shash_desc(cryptd_req);
+ struct ghash_desc_ctx *dctx = shash_desc_ctx(desc);
+
+ ghash_async_init(req);
+ memcpy(dctx, in, sizeof(*dctx));
+ return 0;
+
+}
+
+static int ghash_async_export(struct ahash_request *req, void *out)
+{
+ struct ahash_request *cryptd_req = ahash_request_ctx(req);
+ struct shash_desc *desc = cryptd_shash_desc(cryptd_req);
+ struct ghash_desc_ctx *dctx = shash_desc_ctx(desc);
+
+ memcpy(out, dctx, sizeof(*dctx));
+ return 0;
+
+}
+
static int ghash_async_digest(struct ahash_request *req)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
@@ -288,8 +311,11 @@ static struct ahash_alg ghash_async_alg = {
.final = ghash_async_final,
.setkey = ghash_async_setkey,
.digest = ghash_async_digest,
+ .export = ghash_async_export,
+ .import = ghash_async_import,
.halg = {
.digestsize = GHASH_DIGEST_SIZE,
+ .statesize = sizeof(struct ghash_desc_ctx),
.base = {
.cra_name = "ghash",
.cra_driver_name = "ghash-clmulni",
diff --git a/arch/x86/entry/calling.h b/arch/x86/entry/calling.h
index 3c71dd947c7b..e32206e09868 100644
--- a/arch/x86/entry/calling.h
+++ b/arch/x86/entry/calling.h
@@ -1,3 +1,5 @@
+#include <linux/jump_label.h>
+
/*
x86 function call convention, 64-bit:
@@ -232,3 +234,16 @@ For 32-bit we have the following conventions - kernel is built with
#endif /* CONFIG_X86_64 */
+/*
+ * This does 'call enter_from_user_mode' unless we can avoid it based on
+ * kernel config or using the static jump infrastructure.
+ */
+.macro CALL_enter_from_user_mode
+#ifdef CONFIG_CONTEXT_TRACKING
+#ifdef HAVE_JUMP_LABEL
+ STATIC_JUMP_IF_FALSE .Lafter_call_\@, context_tracking_enabled, def=0
+#endif
+ call enter_from_user_mode
+.Lafter_call_\@:
+#endif
+.endm
diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c
index a89fdbc1f0be..03663740c866 100644
--- a/arch/x86/entry/common.c
+++ b/arch/x86/entry/common.c
@@ -421,7 +421,7 @@ __visible long do_fast_syscall_32(struct pt_regs *regs)
regs->ip = landing_pad;
/*
- * Fetch ECX from where the vDSO stashed it.
+ * Fetch EBP from where the vDSO stashed it.
*
* WARNING: We are in CONTEXT_USER and RCU isn't paying attention!
*/
@@ -432,10 +432,10 @@ __visible long do_fast_syscall_32(struct pt_regs *regs)
* Micro-optimization: the pointer we're following is explicitly
* 32 bits, so it can't be out of range.
*/
- __get_user(*(u32 *)&regs->cx,
+ __get_user(*(u32 *)&regs->bp,
(u32 __user __force *)(unsigned long)(u32)regs->sp)
#else
- get_user(*(u32 *)&regs->cx,
+ get_user(*(u32 *)&regs->bp,
(u32 __user __force *)(unsigned long)(u32)regs->sp)
#endif
) {
diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S
index 3eb572ed3d7a..77d8c5112900 100644
--- a/arch/x86/entry/entry_32.S
+++ b/arch/x86/entry/entry_32.S
@@ -292,7 +292,7 @@ ENTRY(entry_SYSENTER_32)
movl TSS_sysenter_sp0(%esp), %esp
sysenter_past_esp:
pushl $__USER_DS /* pt_regs->ss */
- pushl %ecx /* pt_regs->cx */
+ pushl %ebp /* pt_regs->sp (stashed in bp) */
pushfl /* pt_regs->flags (except IF = 0) */
orl $X86_EFLAGS_IF, (%esp) /* Fix IF */
pushl $__USER_CS /* pt_regs->cs */
@@ -308,8 +308,9 @@ sysenter_past_esp:
movl %esp, %eax
call do_fast_syscall_32
- testl %eax, %eax
- jz .Lsyscall_32_done
+ /* XEN PV guests always use IRET path */
+ ALTERNATIVE "testl %eax, %eax; jz .Lsyscall_32_done", \
+ "jmp .Lsyscall_32_done", X86_FEATURE_XENPV
/* Opportunistic SYSEXIT */
TRACE_IRQS_ON /* User mode traces as IRQs on. */
@@ -328,7 +329,8 @@ sysenter_past_esp:
* Return back to the vDSO, which will pop ecx and edx.
* Don't bother with DS and ES (they already contain __USER_DS).
*/
- ENABLE_INTERRUPTS_SYSEXIT
+ sti
+ sysexit
.pushsection .fixup, "ax"
2: movl $0, PT_FS(%esp)
@@ -551,11 +553,6 @@ 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)
diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S
index 53616ca03244..9d34d3cfceb6 100644
--- a/arch/x86/entry/entry_64.S
+++ b/arch/x86/entry/entry_64.S
@@ -509,9 +509,18 @@ END(irq_entries_start)
* tracking that we're in kernel mode.
*/
SWAPGS
-#ifdef CONFIG_CONTEXT_TRACKING
- call enter_from_user_mode
-#endif
+
+ /*
+ * We need to tell lockdep that IRQs are off. We can't do this until
+ * we fix gsbase, and we should do it before enter_from_user_mode
+ * (which can take locks). Since TRACE_IRQS_OFF idempotent,
+ * the simplest way to handle it is to just call it twice if
+ * we enter from user mode. There's no reason to optimize this since
+ * TRACE_IRQS_OFF is a no-op if lockdep is off.
+ */
+ TRACE_IRQS_OFF
+
+ CALL_enter_from_user_mode
1:
/*
@@ -1049,12 +1058,16 @@ ENTRY(error_entry)
SWAPGS
.Lerror_entry_from_usermode_after_swapgs:
-#ifdef CONFIG_CONTEXT_TRACKING
- call enter_from_user_mode
-#endif
+ /*
+ * We need to tell lockdep that IRQs are off. We can't do this until
+ * we fix gsbase, and we should do it before enter_from_user_mode
+ * (which can take locks).
+ */
+ TRACE_IRQS_OFF
+ CALL_enter_from_user_mode
+ ret
.Lerror_entry_done:
-
TRACE_IRQS_OFF
ret
diff --git a/arch/x86/entry/entry_64_compat.S b/arch/x86/entry/entry_64_compat.S
index c3201830a85e..ff1c6d61f332 100644
--- a/arch/x86/entry/entry_64_compat.S
+++ b/arch/x86/entry/entry_64_compat.S
@@ -18,13 +18,6 @@
.section .entry.text, "ax"
-#ifdef CONFIG_PARAVIRT
-ENTRY(native_usergs_sysret32)
- swapgs
- sysretl
-ENDPROC(native_usergs_sysret32)
-#endif
-
/*
* 32-bit SYSENTER instruction entry.
*
@@ -63,7 +56,7 @@ ENTRY(entry_SYSENTER_compat)
/* Construct struct pt_regs on stack */
pushq $__USER32_DS /* pt_regs->ss */
- pushq %rcx /* pt_regs->sp */
+ pushq %rbp /* pt_regs->sp (stashed in bp) */
/*
* Push flags. This is nasty. First, interrupts are currently
@@ -82,14 +75,14 @@ ENTRY(entry_SYSENTER_compat)
pushq %rdi /* pt_regs->di */
pushq %rsi /* pt_regs->si */
pushq %rdx /* pt_regs->dx */
- pushq %rcx /* pt_regs->cx (will be overwritten) */
+ pushq %rcx /* pt_regs->cx */
pushq $-ENOSYS /* pt_regs->ax */
pushq %r8 /* pt_regs->r8 = 0 */
pushq %r8 /* pt_regs->r9 = 0 */
pushq %r8 /* pt_regs->r10 = 0 */
pushq %r8 /* pt_regs->r11 = 0 */
pushq %rbx /* pt_regs->rbx */
- pushq %rbp /* pt_regs->rbp */
+ pushq %rbp /* pt_regs->rbp (will be overwritten) */
pushq %r8 /* pt_regs->r12 = 0 */
pushq %r8 /* pt_regs->r13 = 0 */
pushq %r8 /* pt_regs->r14 = 0 */
@@ -103,15 +96,15 @@ ENTRY(entry_SYSENTER_compat)
* This needs to happen before enabling interrupts so that
* we don't get preempted with NT set.
*
- * NB.: sysenter_fix_flags is a label with the code under it moved
+ * NB.: .Lsysenter_fix_flags is a label with the code under it moved
* out-of-line as an optimization: NT is unlikely to be set in the
* majority of the cases and instead of polluting the I$ unnecessarily,
* we're keeping that code behind a branch which will predict as
* not-taken and therefore its instructions won't be fetched.
*/
testl $X86_EFLAGS_NT, EFLAGS(%rsp)
- jnz sysenter_fix_flags
-sysenter_flags_fixed:
+ jnz .Lsysenter_fix_flags
+.Lsysenter_flags_fixed:
/*
* User mode is traced as though IRQs are on, and SYSENTER
@@ -121,14 +114,15 @@ sysenter_flags_fixed:
movq %rsp, %rdi
call do_fast_syscall_32
- testl %eax, %eax
- jz .Lsyscall_32_done
+ /* XEN PV guests always use IRET path */
+ ALTERNATIVE "testl %eax, %eax; jz .Lsyscall_32_done", \
+ "jmp .Lsyscall_32_done", X86_FEATURE_XENPV
jmp sysret32_from_system_call
-sysenter_fix_flags:
+.Lsysenter_fix_flags:
pushq $X86_EFLAGS_FIXED
popfq
- jmp sysenter_flags_fixed
+ jmp .Lsysenter_flags_fixed
ENDPROC(entry_SYSENTER_compat)
/*
@@ -178,7 +172,7 @@ ENTRY(entry_SYSCALL_compat)
pushq %rdi /* pt_regs->di */
pushq %rsi /* pt_regs->si */
pushq %rdx /* pt_regs->dx */
- pushq %rcx /* pt_regs->cx (will be overwritten) */
+ pushq %rbp /* pt_regs->cx (stashed in bp) */
pushq $-ENOSYS /* pt_regs->ax */
xorq %r8,%r8
pushq %r8 /* pt_regs->r8 = 0 */
@@ -186,7 +180,7 @@ ENTRY(entry_SYSCALL_compat)
pushq %r8 /* pt_regs->r10 = 0 */
pushq %r8 /* pt_regs->r11 = 0 */
pushq %rbx /* pt_regs->rbx */
- pushq %rbp /* pt_regs->rbp */
+ pushq %rbp /* pt_regs->rbp (will be overwritten) */
pushq %r8 /* pt_regs->r12 = 0 */
pushq %r8 /* pt_regs->r13 = 0 */
pushq %r8 /* pt_regs->r14 = 0 */
@@ -200,8 +194,9 @@ ENTRY(entry_SYSCALL_compat)
movq %rsp, %rdi
call do_fast_syscall_32
- testl %eax, %eax
- jz .Lsyscall_32_done
+ /* XEN PV guests always use IRET path */
+ ALTERNATIVE "testl %eax, %eax; jz .Lsyscall_32_done", \
+ "jmp .Lsyscall_32_done", X86_FEATURE_XENPV
/* Opportunistic SYSRET */
sysret32_from_system_call:
@@ -236,7 +231,8 @@ sysret32_from_system_call:
xorq %r9, %r9
xorq %r10, %r10
movq RSP-ORIG_RAX(%rsp), %rsp
- USERGS_SYSRET32
+ swapgs
+ sysretl
END(entry_SYSCALL_compat)
/*
diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index f17705e1332c..cb713df81180 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -383,3 +383,4 @@
374 i386 userfaultfd sys_userfaultfd
375 i386 membarrier sys_membarrier
376 i386 mlock2 sys_mlock2
+377 i386 copy_file_range sys_copy_file_range
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index 314a90bfc09c..dc1040a50bdc 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -332,6 +332,7 @@
323 common userfaultfd sys_userfaultfd
324 common membarrier sys_membarrier
325 common mlock2 sys_mlock2
+326 common copy_file_range sys_copy_file_range
#
# x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/arch/x86/entry/vdso/vclock_gettime.c b/arch/x86/entry/vdso/vclock_gettime.c
index ca94fa649251..1a50e09c945b 100644
--- a/arch/x86/entry/vdso/vclock_gettime.c
+++ b/arch/x86/entry/vdso/vclock_gettime.c
@@ -17,8 +17,10 @@
#include <asm/vvar.h>
#include <asm/unistd.h>
#include <asm/msr.h>
+#include <asm/pvclock.h>
#include <linux/math64.h>
#include <linux/time.h>
+#include <linux/kernel.h>
#define gtod (&VVAR(vsyscall_gtod_data))
@@ -36,12 +38,12 @@ static notrace cycle_t vread_hpet(void)
}
#endif
-#ifndef BUILD_VDSO32
+#ifdef CONFIG_PARAVIRT_CLOCK
+extern u8 pvclock_page
+ __attribute__((visibility("hidden")));
+#endif
-#include <linux/kernel.h>
-#include <asm/vsyscall.h>
-#include <asm/fixmap.h>
-#include <asm/pvclock.h>
+#ifndef BUILD_VDSO32
notrace static long vdso_fallback_gettime(long clock, struct timespec *ts)
{
@@ -60,75 +62,6 @@ notrace static long vdso_fallback_gtod(struct timeval *tv, struct timezone *tz)
return ret;
}
-#ifdef CONFIG_PARAVIRT_CLOCK
-
-static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu)
-{
- const struct pvclock_vsyscall_time_info *pvti_base;
- int idx = cpu / (PAGE_SIZE/PVTI_SIZE);
- int offset = cpu % (PAGE_SIZE/PVTI_SIZE);
-
- BUG_ON(PVCLOCK_FIXMAP_BEGIN + idx > PVCLOCK_FIXMAP_END);
-
- pvti_base = (struct pvclock_vsyscall_time_info *)
- __fix_to_virt(PVCLOCK_FIXMAP_BEGIN+idx);
-
- return &pvti_base[offset];
-}
-
-static notrace cycle_t vread_pvclock(int *mode)
-{
- const struct pvclock_vsyscall_time_info *pvti;
- cycle_t ret;
- u64 last;
- u32 version;
- u8 flags;
- unsigned cpu, cpu1;
-
-
- /*
- * Note: hypervisor must guarantee that:
- * 1. cpu ID number maps 1:1 to per-CPU pvclock time info.
- * 2. that per-CPU pvclock time info is updated if the
- * underlying CPU changes.
- * 3. that version is increased whenever underlying CPU
- * changes.
- *
- */
- do {
- cpu = __getcpu() & VGETCPU_CPU_MASK;
- /* TODO: We can put vcpu id into higher bits of pvti.version.
- * This will save a couple of cycles by getting rid of
- * __getcpu() calls (Gleb).
- */
-
- pvti = get_pvti(cpu);
-
- version = __pvclock_read_cycles(&pvti->pvti, &ret, &flags);
-
- /*
- * Test we're still on the cpu as well as the version.
- * We could have been migrated just after the first
- * vgetcpu but before fetching the version, so we
- * wouldn't notice a version change.
- */
- cpu1 = __getcpu() & VGETCPU_CPU_MASK;
- } while (unlikely(cpu != cpu1 ||
- (pvti->pvti.version & 1) ||
- pvti->pvti.version != version));
-
- if (unlikely(!(flags & PVCLOCK_TSC_STABLE_BIT)))
- *mode = VCLOCK_NONE;
-
- /* refer to tsc.c read_tsc() comment for rationale */
- last = gtod->cycle_last;
-
- if (likely(ret >= last))
- return ret;
-
- return last;
-}
-#endif
#else
@@ -162,15 +95,77 @@ notrace static long vdso_fallback_gtod(struct timeval *tv, struct timezone *tz)
return ret;
}
+#endif
+
#ifdef CONFIG_PARAVIRT_CLOCK
+static notrace const struct pvclock_vsyscall_time_info *get_pvti0(void)
+{
+ return (const struct pvclock_vsyscall_time_info *)&pvclock_page;
+}
static notrace cycle_t vread_pvclock(int *mode)
{
- *mode = VCLOCK_NONE;
- return 0;
-}
-#endif
+ const struct pvclock_vcpu_time_info *pvti = &get_pvti0()->pvti;
+ cycle_t ret;
+ u64 tsc, pvti_tsc;
+ u64 last, delta, pvti_system_time;
+ u32 version, pvti_tsc_to_system_mul, pvti_tsc_shift;
+ /*
+ * Note: The kernel and hypervisor must guarantee that cpu ID
+ * number maps 1:1 to per-CPU pvclock time info.
+ *
+ * Because the hypervisor is entirely unaware of guest userspace
+ * preemption, it cannot guarantee that per-CPU pvclock time
+ * info is updated if the underlying CPU changes or that that
+ * version is increased whenever underlying CPU changes.
+ *
+ * On KVM, we are guaranteed that pvti updates for any vCPU are
+ * atomic as seen by *all* vCPUs. This is an even stronger
+ * guarantee than we get with a normal seqlock.
+ *
+ * On Xen, we don't appear to have that guarantee, but Xen still
+ * supplies a valid seqlock using the version field.
+ *
+ * We only do pvclock vdso timing at all if
+ * PVCLOCK_TSC_STABLE_BIT is set, and we interpret that bit to
+ * mean that all vCPUs have matching pvti and that the TSC is
+ * synced, so we can just look at vCPU 0's pvti.
+ */
+
+ do {
+ version = pvti->version;
+
+ smp_rmb();
+
+ if (unlikely(!(pvti->flags & PVCLOCK_TSC_STABLE_BIT))) {
+ *mode = VCLOCK_NONE;
+ return 0;
+ }
+
+ tsc = rdtsc_ordered();
+ pvti_tsc_to_system_mul = pvti->tsc_to_system_mul;
+ pvti_tsc_shift = pvti->tsc_shift;
+ pvti_system_time = pvti->system_time;
+ pvti_tsc = pvti->tsc_timestamp;
+
+ /* Make sure that the version double-check is last. */
+ smp_rmb();
+ } while (unlikely((version & 1) || version != pvti->version));
+
+ delta = tsc - pvti_tsc;
+ ret = pvti_system_time +
+ pvclock_scale_delta(delta, pvti_tsc_to_system_mul,
+ pvti_tsc_shift);
+
+ /* refer to vread_tsc() comment for rationale */
+ last = gtod->cycle_last;
+
+ if (likely(ret >= last))
+ return ret;
+
+ return last;
+}
#endif
notrace static cycle_t vread_tsc(void)
diff --git a/arch/x86/entry/vdso/vdso-layout.lds.S b/arch/x86/entry/vdso/vdso-layout.lds.S
index de2c921025f5..4158acc17df0 100644
--- a/arch/x86/entry/vdso/vdso-layout.lds.S
+++ b/arch/x86/entry/vdso/vdso-layout.lds.S
@@ -25,7 +25,7 @@ SECTIONS
* segment.
*/
- vvar_start = . - 2 * PAGE_SIZE;
+ vvar_start = . - 3 * PAGE_SIZE;
vvar_page = vvar_start;
/* Place all vvars at the offsets in asm/vvar.h. */
@@ -36,6 +36,7 @@ SECTIONS
#undef EMIT_VVAR
hpet_page = vvar_start + PAGE_SIZE;
+ pvclock_page = vvar_start + 2 * PAGE_SIZE;
. = SIZEOF_HEADERS;
diff --git a/arch/x86/entry/vdso/vdso2c.c b/arch/x86/entry/vdso/vdso2c.c
index 785d9922b106..491020b2826d 100644
--- a/arch/x86/entry/vdso/vdso2c.c
+++ b/arch/x86/entry/vdso/vdso2c.c
@@ -73,6 +73,7 @@ enum {
sym_vvar_start,
sym_vvar_page,
sym_hpet_page,
+ sym_pvclock_page,
sym_VDSO_FAKE_SECTION_TABLE_START,
sym_VDSO_FAKE_SECTION_TABLE_END,
};
@@ -80,6 +81,7 @@ enum {
const int special_pages[] = {
sym_vvar_page,
sym_hpet_page,
+ sym_pvclock_page,
};
struct vdso_sym {
@@ -91,6 +93,7 @@ struct vdso_sym required_syms[] = {
[sym_vvar_start] = {"vvar_start", true},
[sym_vvar_page] = {"vvar_page", true},
[sym_hpet_page] = {"hpet_page", true},
+ [sym_pvclock_page] = {"pvclock_page", true},
[sym_VDSO_FAKE_SECTION_TABLE_START] = {
"VDSO_FAKE_SECTION_TABLE_START", false
},
diff --git a/arch/x86/entry/vdso/vdso32/system_call.S b/arch/x86/entry/vdso/vdso32/system_call.S
index 93bd8452383f..3a1d9297074b 100644
--- a/arch/x86/entry/vdso/vdso32/system_call.S
+++ b/arch/x86/entry/vdso/vdso32/system_call.S
@@ -1,5 +1,5 @@
/*
- * Code for the vDSO. This version uses the old int $0x80 method.
+ * AT_SYSINFO entry point
*/
#include <asm/dwarf2.h>
@@ -21,35 +21,67 @@ __kernel_vsyscall:
/*
* Reshuffle regs so that all of any of the entry instructions
* will preserve enough state.
+ *
+ * A really nice entry sequence would be:
+ * pushl %edx
+ * pushl %ecx
+ * movl %esp, %ecx
+ *
+ * Unfortunately, naughty Android versions between July and December
+ * 2015 actually hardcode the traditional Linux SYSENTER entry
+ * sequence. That is severely broken for a number of reasons (ask
+ * anyone with an AMD CPU, for example). Nonetheless, we try to keep
+ * it working approximately as well as it ever worked.
+ *
+ * This link may eludicate some of the history:
+ * https://android-review.googlesource.com/#/q/Iac3295376d61ef83e713ac9b528f3b50aa780cd7
+ * personally, I find it hard to understand what's going on there.
+ *
+ * Note to future user developers: DO NOT USE SYSENTER IN YOUR CODE.
+ * Execute an indirect call to the address in the AT_SYSINFO auxv
+ * entry. That is the ONLY correct way to make a fast 32-bit system
+ * call on Linux. (Open-coding int $0x80 is also fine, but it's
+ * slow.)
*/
+ pushl %ecx
+ CFI_ADJUST_CFA_OFFSET 4
+ CFI_REL_OFFSET ecx, 0
pushl %edx
CFI_ADJUST_CFA_OFFSET 4
CFI_REL_OFFSET edx, 0
- pushl %ecx
+ pushl %ebp
CFI_ADJUST_CFA_OFFSET 4
- CFI_REL_OFFSET ecx, 0
- movl %esp, %ecx
+ CFI_REL_OFFSET ebp, 0
+
+ #define SYSENTER_SEQUENCE "movl %esp, %ebp; sysenter"
+ #define SYSCALL_SEQUENCE "movl %ecx, %ebp; syscall"
#ifdef CONFIG_X86_64
/* If SYSENTER (Intel) or SYSCALL32 (AMD) is available, use it. */
- ALTERNATIVE_2 "", "sysenter", X86_FEATURE_SYSENTER32, \
- "syscall", X86_FEATURE_SYSCALL32
+ ALTERNATIVE_2 "", SYSENTER_SEQUENCE, X86_FEATURE_SYSENTER32, \
+ SYSCALL_SEQUENCE, X86_FEATURE_SYSCALL32
#else
- ALTERNATIVE "", "sysenter", X86_FEATURE_SEP
+ ALTERNATIVE "", SYSENTER_SEQUENCE, X86_FEATURE_SEP
#endif
/* Enter using int $0x80 */
- movl (%esp), %ecx
int $0x80
GLOBAL(int80_landing_pad)
- /* Restore ECX and EDX in case they were clobbered. */
- popl %ecx
- CFI_RESTORE ecx
+ /*
+ * Restore EDX and ECX in case they were clobbered. EBP is not
+ * clobbered (the kernel restores it), but it's cleaner and
+ * probably faster to pop it than to adjust ESP using addl.
+ */
+ popl %ebp
+ CFI_RESTORE ebp
CFI_ADJUST_CFA_OFFSET -4
popl %edx
CFI_RESTORE edx
CFI_ADJUST_CFA_OFFSET -4
+ popl %ecx
+ CFI_RESTORE ecx
+ CFI_ADJUST_CFA_OFFSET -4
ret
CFI_ENDPROC
diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c
index 64df47148160..b8f69e264ac4 100644
--- a/arch/x86/entry/vdso/vma.c
+++ b/arch/x86/entry/vdso/vma.c
@@ -12,6 +12,7 @@
#include <linux/random.h>
#include <linux/elf.h>
#include <linux/cpu.h>
+#include <asm/pvclock.h>
#include <asm/vgtod.h>
#include <asm/proto.h>
#include <asm/vdso.h>
@@ -100,6 +101,7 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
.name = "[vvar]",
.pages = no_pages,
};
+ struct pvclock_vsyscall_time_info *pvti;
if (calculate_addr) {
addr = vdso_addr(current->mm->start_stack,
@@ -169,6 +171,18 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
}
#endif
+ pvti = pvclock_pvti_cpu0_va();
+ if (pvti && image->sym_pvclock_page) {
+ ret = remap_pfn_range(vma,
+ text_start + image->sym_pvclock_page,
+ __pa(pvti) >> PAGE_SHIFT,
+ PAGE_SIZE,
+ PAGE_READONLY);
+
+ if (ret)
+ goto up_fail;
+ }
+
up_fail:
if (ret)
current->mm->context.vdso = NULL;
diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h
index a30316bf801a..c80f6b6f3da2 100644
--- a/arch/x86/include/asm/apic.h
+++ b/arch/x86/include/asm/apic.h
@@ -23,6 +23,11 @@
#define APIC_VERBOSE 1
#define APIC_DEBUG 2
+/* Macros for apic_extnmi which controls external NMI masking */
+#define APIC_EXTNMI_BSP 0 /* Default */
+#define APIC_EXTNMI_ALL 1
+#define APIC_EXTNMI_NONE 2
+
/*
* Define the default level of output to be very little
* This can be turned up by using apic=verbose for more
@@ -303,6 +308,7 @@ struct apic {
unsigned int *apicid);
/* ipi */
+ void (*send_IPI)(int cpu, int vector);
void (*send_IPI_mask)(const struct cpumask *mask, int vector);
void (*send_IPI_mask_allbutself)(const struct cpumask *mask,
int vector);
diff --git a/arch/x86/include/asm/atomic.h b/arch/x86/include/asm/atomic.h
index ae5fb83e6d91..3e8674288198 100644
--- a/arch/x86/include/asm/atomic.h
+++ b/arch/x86/include/asm/atomic.h
@@ -3,7 +3,6 @@
#include <linux/compiler.h>
#include <linux/types.h>
-#include <asm/processor.h>
#include <asm/alternative.h>
#include <asm/cmpxchg.h>
#include <asm/rmwcc.h>
diff --git a/arch/x86/include/asm/atomic64_32.h b/arch/x86/include/asm/atomic64_32.h
index a11c30b77fb5..a984111135b1 100644
--- a/arch/x86/include/asm/atomic64_32.h
+++ b/arch/x86/include/asm/atomic64_32.h
@@ -3,7 +3,6 @@
#include <linux/compiler.h>
#include <linux/types.h>
-#include <asm/processor.h>
//#include <asm/cmpxchg.h>
/* An 64bit atomic type */
diff --git a/arch/x86/include/asm/boot.h b/arch/x86/include/asm/boot.h
index 4fa687a47a62..6b8d6e8cd449 100644
--- a/arch/x86/include/asm/boot.h
+++ b/arch/x86/include/asm/boot.h
@@ -27,7 +27,7 @@
#define BOOT_HEAP_SIZE 0x400000
#else /* !CONFIG_KERNEL_BZIP2 */
-#define BOOT_HEAP_SIZE 0x8000
+#define BOOT_HEAP_SIZE 0x10000
#endif /* !CONFIG_KERNEL_BZIP2 */
diff --git a/arch/x86/include/asm/calgary.h b/arch/x86/include/asm/calgary.h
index 0d467b338835..a8303ebe089f 100644
--- a/arch/x86/include/asm/calgary.h
+++ b/arch/x86/include/asm/calgary.h
@@ -31,7 +31,7 @@
#include <asm/types.h>
struct iommu_table {
- struct cal_chipset_ops *chip_ops; /* chipset specific funcs */
+ const struct cal_chipset_ops *chip_ops; /* chipset specific funcs */
unsigned long it_base; /* mapped address of tce table */
unsigned long it_hint; /* Hint for next alloc */
unsigned long *it_map; /* A simple allocation bitmap for now */
diff --git a/arch/x86/include/asm/cmpxchg_32.h b/arch/x86/include/asm/cmpxchg_32.h
index f7e142926481..e4959d023af8 100644
--- a/arch/x86/include/asm/cmpxchg_32.h
+++ b/arch/x86/include/asm/cmpxchg_32.h
@@ -109,6 +109,6 @@ static inline u64 __cmpxchg64_local(volatile u64 *ptr, u64 old, u64 new)
#endif
-#define system_has_cmpxchg_double() cpu_has_cx8
+#define system_has_cmpxchg_double() boot_cpu_has(X86_FEATURE_CX8)
#endif /* _ASM_X86_CMPXCHG_32_H */
diff --git a/arch/x86/include/asm/cmpxchg_64.h b/arch/x86/include/asm/cmpxchg_64.h
index 1af94697aae5..caa23a34c963 100644
--- a/arch/x86/include/asm/cmpxchg_64.h
+++ b/arch/x86/include/asm/cmpxchg_64.h
@@ -18,6 +18,6 @@ static inline void set_64bit(volatile u64 *ptr, u64 val)
cmpxchg_local((ptr), (o), (n)); \
})
-#define system_has_cmpxchg_double() cpu_has_cx16
+#define system_has_cmpxchg_double() boot_cpu_has(X86_FEATURE_CX16)
#endif /* _ASM_X86_CMPXCHG_64_H */
diff --git a/arch/x86/include/asm/cpu.h b/arch/x86/include/asm/cpu.h
index bf2caa1dedc5..678637ad7476 100644
--- a/arch/x86/include/asm/cpu.h
+++ b/arch/x86/include/asm/cpu.h
@@ -36,4 +36,7 @@ extern int _debug_hotplug_cpu(int cpu, int action);
int mwait_usable(const struct cpuinfo_x86 *);
+unsigned int x86_family(unsigned int sig);
+unsigned int x86_model(unsigned int sig);
+unsigned int x86_stepping(unsigned int sig);
#endif /* _ASM_X86_CPU_H */
diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h
index e4f8010f22e0..7ad8c9464297 100644
--- a/arch/x86/include/asm/cpufeature.h
+++ b/arch/x86/include/asm/cpufeature.h
@@ -12,7 +12,7 @@
#include <asm/disabled-features.h>
#endif
-#define NCAPINTS 14 /* N 32-bit words worth of info */
+#define NCAPINTS 16 /* N 32-bit words worth of info */
#define NBUGINTS 1 /* N 32-bit bug flags */
/*
@@ -181,22 +181,17 @@
/*
* Auxiliary flags: Linux defined - For features scattered in various
- * CPUID levels like 0x6, 0xA etc, word 7
+ * CPUID levels like 0x6, 0xA etc, word 7.
+ *
+ * Reuse free bits when adding new feature flags!
*/
-#define X86_FEATURE_IDA ( 7*32+ 0) /* Intel Dynamic Acceleration */
-#define X86_FEATURE_ARAT ( 7*32+ 1) /* Always Running APIC Timer */
+
#define X86_FEATURE_CPB ( 7*32+ 2) /* AMD Core Performance Boost */
#define X86_FEATURE_EPB ( 7*32+ 3) /* IA32_ENERGY_PERF_BIAS support */
-#define X86_FEATURE_PLN ( 7*32+ 5) /* Intel Power Limit Notification */
-#define X86_FEATURE_PTS ( 7*32+ 6) /* Intel Package Thermal Status */
-#define X86_FEATURE_DTHERM ( 7*32+ 7) /* Digital Thermal Sensor */
+
#define X86_FEATURE_HW_PSTATE ( 7*32+ 8) /* AMD HW-PState */
#define X86_FEATURE_PROC_FEEDBACK ( 7*32+ 9) /* AMD ProcFeedbackInterface */
-#define X86_FEATURE_HWP ( 7*32+ 10) /* "hwp" Intel HWP */
-#define X86_FEATURE_HWP_NOTIFY ( 7*32+ 11) /* Intel HWP_NOTIFY */
-#define X86_FEATURE_HWP_ACT_WINDOW ( 7*32+ 12) /* Intel HWP_ACT_WINDOW */
-#define X86_FEATURE_HWP_EPP ( 7*32+13) /* Intel HWP_EPP */
-#define X86_FEATURE_HWP_PKG_REQ ( 7*32+14) /* Intel HWP_PKG_REQ */
+
#define X86_FEATURE_INTEL_PT ( 7*32+15) /* Intel Processor Trace */
/* Virtualization flags: Linux defined, word 8 */
@@ -205,17 +200,9 @@
#define X86_FEATURE_FLEXPRIORITY ( 8*32+ 2) /* Intel FlexPriority */
#define X86_FEATURE_EPT ( 8*32+ 3) /* Intel Extended Page Table */
#define X86_FEATURE_VPID ( 8*32+ 4) /* Intel Virtual Processor ID */
-#define X86_FEATURE_NPT ( 8*32+ 5) /* AMD Nested Page Table support */
-#define X86_FEATURE_LBRV ( 8*32+ 6) /* AMD LBR Virtualization support */
-#define X86_FEATURE_SVML ( 8*32+ 7) /* "svm_lock" AMD SVM locking MSR */
-#define X86_FEATURE_NRIPS ( 8*32+ 8) /* "nrip_save" AMD SVM next_rip save */
-#define X86_FEATURE_TSCRATEMSR ( 8*32+ 9) /* "tsc_scale" AMD TSC scaling support */
-#define X86_FEATURE_VMCBCLEAN ( 8*32+10) /* "vmcb_clean" AMD VMCB clean bits support */
-#define X86_FEATURE_FLUSHBYASID ( 8*32+11) /* AMD flush-by-ASID support */
-#define X86_FEATURE_DECODEASSISTS ( 8*32+12) /* AMD Decode Assists support */
-#define X86_FEATURE_PAUSEFILTER ( 8*32+13) /* AMD filtered pause intercept */
-#define X86_FEATURE_PFTHRESHOLD ( 8*32+14) /* AMD pause filter threshold */
+
#define X86_FEATURE_VMMCALL ( 8*32+15) /* Prefer vmmcall to vmcall */
+#define X86_FEATURE_XENPV ( 8*32+16) /* "" Xen paravirtual guest */
/* Intel-defined CPU features, CPUID level 0x00000007:0 (ebx), word 9 */
@@ -258,6 +245,30 @@
/* AMD-defined CPU features, CPUID level 0x80000008 (ebx), word 13 */
#define X86_FEATURE_CLZERO (13*32+0) /* CLZERO instruction */
+/* Thermal and Power Management Leaf, CPUID level 0x00000006 (eax), word 14 */
+#define X86_FEATURE_DTHERM (14*32+ 0) /* Digital Thermal Sensor */
+#define X86_FEATURE_IDA (14*32+ 1) /* Intel Dynamic Acceleration */
+#define X86_FEATURE_ARAT (14*32+ 2) /* Always Running APIC Timer */
+#define X86_FEATURE_PLN (14*32+ 4) /* Intel Power Limit Notification */
+#define X86_FEATURE_PTS (14*32+ 6) /* Intel Package Thermal Status */
+#define X86_FEATURE_HWP (14*32+ 7) /* Intel Hardware P-states */
+#define X86_FEATURE_HWP_NOTIFY (14*32+ 8) /* HWP Notification */
+#define X86_FEATURE_HWP_ACT_WINDOW (14*32+ 9) /* HWP Activity Window */
+#define X86_FEATURE_HWP_EPP (14*32+10) /* HWP Energy Perf. Preference */
+#define X86_FEATURE_HWP_PKG_REQ (14*32+11) /* HWP Package Level Request */
+
+/* AMD SVM Feature Identification, CPUID level 0x8000000a (edx), word 15 */
+#define X86_FEATURE_NPT (15*32+ 0) /* Nested Page Table support */
+#define X86_FEATURE_LBRV (15*32+ 1) /* LBR Virtualization support */
+#define X86_FEATURE_SVML (15*32+ 2) /* "svm_lock" SVM locking MSR */
+#define X86_FEATURE_NRIPS (15*32+ 3) /* "nrip_save" SVM next_rip save */
+#define X86_FEATURE_TSCRATEMSR (15*32+ 4) /* "tsc_scale" TSC scaling support */
+#define X86_FEATURE_VMCBCLEAN (15*32+ 5) /* "vmcb_clean" VMCB clean bits support */
+#define X86_FEATURE_FLUSHBYASID (15*32+ 6) /* flush-by-ASID support */
+#define X86_FEATURE_DECODEASSISTS (15*32+ 7) /* Decode Assists support */
+#define X86_FEATURE_PAUSEFILTER (15*32+10) /* filtered pause intercept */
+#define X86_FEATURE_PFTHRESHOLD (15*32+12) /* pause filter threshold */
+
/*
* BUG word(s)
*/
@@ -278,6 +289,26 @@
#include <asm/asm.h>
#include <linux/bitops.h>
+enum cpuid_leafs
+{
+ CPUID_1_EDX = 0,
+ CPUID_8000_0001_EDX,
+ CPUID_8086_0001_EDX,
+ CPUID_LNX_1,
+ CPUID_1_ECX,
+ CPUID_C000_0001_EDX,
+ CPUID_8000_0001_ECX,
+ CPUID_LNX_2,
+ CPUID_LNX_3,
+ CPUID_7_0_EBX,
+ CPUID_D_1_EAX,
+ CPUID_F_0_EDX,
+ CPUID_F_1_EDX,
+ CPUID_8000_0008_EBX,
+ CPUID_6_EAX,
+ CPUID_8000_000A_EDX,
+};
+
#ifdef CONFIG_X86_FEATURE_NAMES
extern const char * const x86_cap_flags[NCAPINTS*32];
extern const char * const x86_power_flags[32];
@@ -355,60 +386,31 @@ extern const char * const x86_bug_flags[NBUGINTS*32];
} while (0)
#define cpu_has_fpu boot_cpu_has(X86_FEATURE_FPU)
-#define cpu_has_de boot_cpu_has(X86_FEATURE_DE)
#define cpu_has_pse boot_cpu_has(X86_FEATURE_PSE)
#define cpu_has_tsc boot_cpu_has(X86_FEATURE_TSC)
#define cpu_has_pge boot_cpu_has(X86_FEATURE_PGE)
#define cpu_has_apic boot_cpu_has(X86_FEATURE_APIC)
-#define cpu_has_sep boot_cpu_has(X86_FEATURE_SEP)
-#define cpu_has_mtrr boot_cpu_has(X86_FEATURE_MTRR)
-#define cpu_has_mmx boot_cpu_has(X86_FEATURE_MMX)
#define cpu_has_fxsr boot_cpu_has(X86_FEATURE_FXSR)
#define cpu_has_xmm boot_cpu_has(X86_FEATURE_XMM)
#define cpu_has_xmm2 boot_cpu_has(X86_FEATURE_XMM2)
-#define cpu_has_xmm3 boot_cpu_has(X86_FEATURE_XMM3)
-#define cpu_has_ssse3 boot_cpu_has(X86_FEATURE_SSSE3)
#define cpu_has_aes boot_cpu_has(X86_FEATURE_AES)
#define cpu_has_avx boot_cpu_has(X86_FEATURE_AVX)
#define cpu_has_avx2 boot_cpu_has(X86_FEATURE_AVX2)
-#define cpu_has_ht boot_cpu_has(X86_FEATURE_HT)
-#define cpu_has_nx boot_cpu_has(X86_FEATURE_NX)
-#define cpu_has_xstore boot_cpu_has(X86_FEATURE_XSTORE)
-#define cpu_has_xstore_enabled boot_cpu_has(X86_FEATURE_XSTORE_EN)
-#define cpu_has_xcrypt boot_cpu_has(X86_FEATURE_XCRYPT)
-#define cpu_has_xcrypt_enabled boot_cpu_has(X86_FEATURE_XCRYPT_EN)
-#define cpu_has_ace2 boot_cpu_has(X86_FEATURE_ACE2)
-#define cpu_has_ace2_enabled boot_cpu_has(X86_FEATURE_ACE2_EN)
-#define cpu_has_phe boot_cpu_has(X86_FEATURE_PHE)
-#define cpu_has_phe_enabled boot_cpu_has(X86_FEATURE_PHE_EN)
-#define cpu_has_pmm boot_cpu_has(X86_FEATURE_PMM)
-#define cpu_has_pmm_enabled boot_cpu_has(X86_FEATURE_PMM_EN)
-#define cpu_has_ds boot_cpu_has(X86_FEATURE_DS)
-#define cpu_has_pebs boot_cpu_has(X86_FEATURE_PEBS)
#define cpu_has_clflush boot_cpu_has(X86_FEATURE_CLFLUSH)
-#define cpu_has_bts boot_cpu_has(X86_FEATURE_BTS)
#define cpu_has_gbpages boot_cpu_has(X86_FEATURE_GBPAGES)
#define cpu_has_arch_perfmon boot_cpu_has(X86_FEATURE_ARCH_PERFMON)
#define cpu_has_pat boot_cpu_has(X86_FEATURE_PAT)
-#define cpu_has_xmm4_1 boot_cpu_has(X86_FEATURE_XMM4_1)
-#define cpu_has_xmm4_2 boot_cpu_has(X86_FEATURE_XMM4_2)
#define cpu_has_x2apic boot_cpu_has(X86_FEATURE_X2APIC)
#define cpu_has_xsave boot_cpu_has(X86_FEATURE_XSAVE)
-#define cpu_has_xsaveopt boot_cpu_has(X86_FEATURE_XSAVEOPT)
#define cpu_has_xsaves boot_cpu_has(X86_FEATURE_XSAVES)
#define cpu_has_osxsave boot_cpu_has(X86_FEATURE_OSXSAVE)
#define cpu_has_hypervisor boot_cpu_has(X86_FEATURE_HYPERVISOR)
-#define cpu_has_pclmulqdq boot_cpu_has(X86_FEATURE_PCLMULQDQ)
-#define cpu_has_perfctr_core boot_cpu_has(X86_FEATURE_PERFCTR_CORE)
-#define cpu_has_perfctr_nb boot_cpu_has(X86_FEATURE_PERFCTR_NB)
-#define cpu_has_perfctr_l2 boot_cpu_has(X86_FEATURE_PERFCTR_L2)
-#define cpu_has_cx8 boot_cpu_has(X86_FEATURE_CX8)
-#define cpu_has_cx16 boot_cpu_has(X86_FEATURE_CX16)
-#define cpu_has_eager_fpu boot_cpu_has(X86_FEATURE_EAGER_FPU)
-#define cpu_has_topoext boot_cpu_has(X86_FEATURE_TOPOEXT)
-#define cpu_has_bpext boot_cpu_has(X86_FEATURE_BPEXT)
-
-#if __GNUC__ >= 4
+/*
+ * Do not add any more of those clumsy macros - use static_cpu_has_safe() for
+ * fast paths and boot_cpu_has() otherwise!
+ */
+
+#if __GNUC__ >= 4 && defined(CONFIG_X86_FAST_FEATURE_TESTS)
extern void warn_pre_alternatives(void);
extern bool __static_cpu_has_safe(u16 bit);
diff --git a/arch/x86/include/asm/fixmap.h b/arch/x86/include/asm/fixmap.h
index f80d70009ff8..6d7d0e52ed5a 100644
--- a/arch/x86/include/asm/fixmap.h
+++ b/arch/x86/include/asm/fixmap.h
@@ -19,7 +19,6 @@
#include <asm/acpi.h>
#include <asm/apicdef.h>
#include <asm/page.h>
-#include <asm/pvclock.h>
#ifdef CONFIG_X86_32
#include <linux/threads.h>
#include <asm/kmap_types.h>
@@ -72,10 +71,6 @@ enum fixed_addresses {
#ifdef CONFIG_X86_VSYSCALL_EMULATION
VSYSCALL_PAGE = (FIXADDR_TOP - VSYSCALL_ADDR) >> PAGE_SHIFT,
#endif
-#ifdef CONFIG_PARAVIRT_CLOCK
- PVCLOCK_FIXMAP_BEGIN,
- PVCLOCK_FIXMAP_END = PVCLOCK_FIXMAP_BEGIN+PVCLOCK_VSYSCALL_NR_PAGES-1,
-#endif
#endif
FIX_DBGP_BASE,
FIX_EARLYCON_MEM_BASE,
diff --git a/arch/x86/include/asm/fpu/internal.h b/arch/x86/include/asm/fpu/internal.h
index 3c3550c3a4a3..0fd440df63f1 100644
--- a/arch/x86/include/asm/fpu/internal.h
+++ b/arch/x86/include/asm/fpu/internal.h
@@ -42,6 +42,7 @@ 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);
+extern u64 fpu__get_supported_xfeatures_mask(void);
/*
* Debugging facility:
@@ -224,18 +225,67 @@ static inline void copy_fxregs_to_kernel(struct fpu *fpu)
#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)
+#define XSTATE_OP(op, st, lmask, hmask, err) \
+ asm volatile("1:" op "\n\t" \
+ "xor %[err], %[err]\n" \
+ "2:\n\t" \
+ ".pushsection .fixup,\"ax\"\n\t" \
+ "3: movl $-2,%[err]\n\t" \
+ "jmp 2b\n\t" \
+ ".popsection\n\t" \
+ _ASM_EXTABLE(1b, 3b) \
+ : [err] "=r" (err) \
+ : "D" (st), "m" (*st), "a" (lmask), "d" (hmask) \
+ : "memory")
+
+/*
+ * If XSAVES is enabled, it replaces XSAVEOPT because it supports a 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.
+ *
+ * We use XSAVE as a fallback.
+ *
+ * The 661 label is defined in the ALTERNATIVE* macros as the address of the
+ * original instruction which gets replaced. We need to use it here as the
+ * address of the instruction where we might get an exception at.
+ */
+#define XSTATE_XSAVE(st, lmask, hmask, err) \
+ asm volatile(ALTERNATIVE_2(XSAVE, \
+ XSAVEOPT, X86_FEATURE_XSAVEOPT, \
+ XSAVES, X86_FEATURE_XSAVES) \
+ "\n" \
+ "xor %[err], %[err]\n" \
+ "3:\n" \
+ ".pushsection .fixup,\"ax\"\n" \
+ "4: movl $-2, %[err]\n" \
+ "jmp 3b\n" \
+ ".popsection\n" \
+ _ASM_EXTABLE(661b, 4b) \
+ : [err] "=r" (err) \
+ : "D" (st), "m" (*st), "a" (lmask), "d" (hmask) \
+ : "memory")
+
+/*
+ * Use XRSTORS to restore context if it is enabled. XRSTORS supports compact
+ * XSAVE area format.
+ */
+#define XSTATE_XRESTORE(st, lmask, hmask, err) \
+ asm volatile(ALTERNATIVE(XRSTOR, \
+ XRSTORS, X86_FEATURE_XSAVES) \
+ "\n" \
+ "xor %[err], %[err]\n" \
+ "3:\n" \
+ ".pushsection .fixup,\"ax\"\n" \
+ "4: movl $-2, %[err]\n" \
+ "jmp 3b\n" \
+ ".popsection\n" \
+ _ASM_EXTABLE(661b, 4b) \
+ : [err] "=r" (err) \
+ : "D" (st), "m" (*st), "a" (lmask), "d" (hmask) \
+ : "memory")
/*
* This function is called only during boot time when x86 caps are not set
@@ -246,22 +296,14 @@ 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;
+ int err;
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");
+ if (static_cpu_has_safe(X86_FEATURE_XSAVES))
+ XSTATE_OP(XSAVES, xstate, lmask, hmask, err);
else
- asm volatile("1:"XSAVE"\n\t"
- "2:\n\t"
- xstate_fault(err)
- : "D" (xstate), "m" (*xstate), "a" (lmask), "d" (hmask), "0" (err)
- : "memory");
+ XSTATE_OP(XSAVE, xstate, lmask, hmask, err);
/* We should never fault when copying to a kernel buffer: */
WARN_ON_FPU(err);
@@ -276,22 +318,14 @@ 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;
+ int err;
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");
+ if (static_cpu_has_safe(X86_FEATURE_XSAVES))
+ XSTATE_OP(XRSTORS, xstate, lmask, hmask, err);
else
- asm volatile("1:"XRSTOR"\n\t"
- "2:\n\t"
- xstate_fault(err)
- : "D" (xstate), "m" (*xstate), "a" (lmask), "d" (hmask), "0" (err)
- : "memory");
+ XSTATE_OP(XRSTOR, xstate, lmask, hmask, err);
/* We should never fault when copying from a kernel buffer: */
WARN_ON_FPU(err);
@@ -305,33 +339,11 @@ static inline void copy_xregs_to_kernel(struct xregs_state *xstate)
u64 mask = -1;
u32 lmask = mask;
u32 hmask = mask >> 32;
- int err = 0;
+ int err;
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");
+ XSTATE_XSAVE(xstate, lmask, hmask, err);
/* We should never fault when copying to a kernel buffer: */
WARN_ON_FPU(err);
@@ -344,23 +356,9 @@ static inline void copy_kernel_to_xregs(struct xregs_state *xstate, u64 mask)
{
u32 lmask = mask;
u32 hmask = mask >> 32;
- int err = 0;
+ int err;
- /*
- * 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");
+ XSTATE_XRESTORE(xstate, lmask, hmask, err);
/* We should never fault when copying from a kernel buffer: */
WARN_ON_FPU(err);
@@ -388,12 +386,10 @@ static inline int copy_xregs_to_user(struct xregs_state __user *buf)
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");
+ stac();
+ XSTATE_OP(XSAVE, buf, -1, -1, err);
+ clac();
+
return err;
}
@@ -405,14 +401,12 @@ 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? */
+ int err;
+
+ stac();
+ XSTATE_OP(XRSTOR, xstate, lmask, hmask, err);
+ clac();
+
return err;
}
diff --git a/arch/x86/include/asm/fpu/xstate.h b/arch/x86/include/asm/fpu/xstate.h
index 3a6c89b70307..af30fdeb140d 100644
--- a/arch/x86/include/asm/fpu/xstate.h
+++ b/arch/x86/include/asm/fpu/xstate.h
@@ -20,15 +20,16 @@
/* Supported features which support lazy state saving */
#define XFEATURE_MASK_LAZY (XFEATURE_MASK_FP | \
- XFEATURE_MASK_SSE | \
+ XFEATURE_MASK_SSE)
+
+/* Supported features which require eager state saving */
+#define XFEATURE_MASK_EAGER (XFEATURE_MASK_BNDREGS | \
+ XFEATURE_MASK_BNDCSR | \
XFEATURE_MASK_YMM | \
- XFEATURE_MASK_OPMASK | \
+ XFEATURE_MASK_OPMASK | \
XFEATURE_MASK_ZMM_Hi256 | \
XFEATURE_MASK_Hi16_ZMM)
-/* Supported features which require eager state saving */
-#define XFEATURE_MASK_EAGER (XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR)
-
/* All currently supported features */
#define XCNTXT_MASK (XFEATURE_MASK_LAZY | XFEATURE_MASK_EAGER)
diff --git a/arch/x86/include/asm/intel_pt.h b/arch/x86/include/asm/intel_pt.h
new file mode 100644
index 000000000000..e1a411786bf5
--- /dev/null
+++ b/arch/x86/include/asm/intel_pt.h
@@ -0,0 +1,10 @@
+#ifndef _ASM_X86_INTEL_PT_H
+#define _ASM_X86_INTEL_PT_H
+
+#if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_INTEL)
+void cpu_emergency_stop_pt(void);
+#else
+static inline void cpu_emergency_stop_pt(void) {}
+#endif
+
+#endif /* _ASM_X86_INTEL_PT_H */
diff --git a/arch/x86/include/asm/iosf_mbi.h b/arch/x86/include/asm/iosf_mbi.h
index b72ad0faa6c5..b41ee164930a 100644
--- a/arch/x86/include/asm/iosf_mbi.h
+++ b/arch/x86/include/asm/iosf_mbi.h
@@ -1,5 +1,5 @@
/*
- * iosf_mbi.h: Intel OnChip System Fabric MailBox access support
+ * Intel OnChip System Fabric MailBox access support
*/
#ifndef IOSF_MBI_SYMS_H
@@ -16,6 +16,18 @@
#define MBI_MASK_LO 0x000000FF
#define MBI_ENABLE 0xF0
+/* IOSF SB read/write opcodes */
+#define MBI_MMIO_READ 0x00
+#define MBI_MMIO_WRITE 0x01
+#define MBI_CFG_READ 0x04
+#define MBI_CFG_WRITE 0x05
+#define MBI_CR_READ 0x06
+#define MBI_CR_WRITE 0x07
+#define MBI_REG_READ 0x10
+#define MBI_REG_WRITE 0x11
+#define MBI_ESRAM_READ 0x12
+#define MBI_ESRAM_WRITE 0x13
+
/* Baytrail available units */
#define BT_MBI_UNIT_AUNIT 0x00
#define BT_MBI_UNIT_SMC 0x01
@@ -28,50 +40,13 @@
#define BT_MBI_UNIT_SATA 0xA3
#define BT_MBI_UNIT_PCIE 0xA6
-/* Baytrail read/write opcodes */
-#define BT_MBI_AUNIT_READ 0x10
-#define BT_MBI_AUNIT_WRITE 0x11
-#define BT_MBI_SMC_READ 0x10
-#define BT_MBI_SMC_WRITE 0x11
-#define BT_MBI_CPU_READ 0x10
-#define BT_MBI_CPU_WRITE 0x11
-#define BT_MBI_BUNIT_READ 0x10
-#define BT_MBI_BUNIT_WRITE 0x11
-#define BT_MBI_PMC_READ 0x06
-#define BT_MBI_PMC_WRITE 0x07
-#define BT_MBI_GFX_READ 0x00
-#define BT_MBI_GFX_WRITE 0x01
-#define BT_MBI_SMIO_READ 0x06
-#define BT_MBI_SMIO_WRITE 0x07
-#define BT_MBI_USB_READ 0x06
-#define BT_MBI_USB_WRITE 0x07
-#define BT_MBI_SATA_READ 0x00
-#define BT_MBI_SATA_WRITE 0x01
-#define BT_MBI_PCIE_READ 0x00
-#define BT_MBI_PCIE_WRITE 0x01
-
/* Quark available units */
#define QRK_MBI_UNIT_HBA 0x00
#define QRK_MBI_UNIT_HB 0x03
#define QRK_MBI_UNIT_RMU 0x04
#define QRK_MBI_UNIT_MM 0x05
-#define QRK_MBI_UNIT_MMESRAM 0x05
#define QRK_MBI_UNIT_SOC 0x31
-/* Quark read/write opcodes */
-#define QRK_MBI_HBA_READ 0x10
-#define QRK_MBI_HBA_WRITE 0x11
-#define QRK_MBI_HB_READ 0x10
-#define QRK_MBI_HB_WRITE 0x11
-#define QRK_MBI_RMU_READ 0x10
-#define QRK_MBI_RMU_WRITE 0x11
-#define QRK_MBI_MM_READ 0x10
-#define QRK_MBI_MM_WRITE 0x11
-#define QRK_MBI_MMESRAM_READ 0x12
-#define QRK_MBI_MMESRAM_WRITE 0x13
-#define QRK_MBI_SOC_READ 0x06
-#define QRK_MBI_SOC_WRITE 0x07
-
#if IS_ENABLED(CONFIG_IOSF_MBI)
bool iosf_mbi_available(void);
diff --git a/arch/x86/include/asm/ipi.h b/arch/x86/include/asm/ipi.h
index 615fa9061b57..cfc9a0d2d07c 100644
--- a/arch/x86/include/asm/ipi.h
+++ b/arch/x86/include/asm/ipi.h
@@ -119,6 +119,8 @@ static inline void
native_apic_mem_write(APIC_ICR, cfg);
}
+extern void default_send_IPI_single(int cpu, int vector);
+extern void default_send_IPI_single_phys(int cpu, int vector);
extern void default_send_IPI_mask_sequence_phys(const struct cpumask *mask,
int vector);
extern void default_send_IPI_mask_allbutself_phys(const struct cpumask *mask,
diff --git a/arch/x86/include/asm/jump_label.h b/arch/x86/include/asm/jump_label.h
index 5daeca3d0f9e..adc54c12cbd1 100644
--- a/arch/x86/include/asm/jump_label.h
+++ b/arch/x86/include/asm/jump_label.h
@@ -1,12 +1,18 @@
#ifndef _ASM_X86_JUMP_LABEL_H
#define _ASM_X86_JUMP_LABEL_H
-#ifndef __ASSEMBLY__
-
-#include <linux/stringify.h>
-#include <linux/types.h>
-#include <asm/nops.h>
-#include <asm/asm.h>
+#ifndef HAVE_JUMP_LABEL
+/*
+ * For better or for worse, if jump labels (the gcc extension) are missing,
+ * then the entire static branch patching infrastructure is compiled out.
+ * If that happens, the code in here will malfunction. Raise a compiler
+ * error instead.
+ *
+ * In theory, jump labels and the static branch patching infrastructure
+ * could be decoupled to fix this.
+ */
+#error asm/jump_label.h included on a non-jump-label kernel
+#endif
#define JUMP_LABEL_NOP_SIZE 5
@@ -16,6 +22,14 @@
# define STATIC_KEY_INIT_NOP GENERIC_NOP5_ATOMIC
#endif
+#include <asm/asm.h>
+#include <asm/nops.h>
+
+#ifndef __ASSEMBLY__
+
+#include <linux/stringify.h>
+#include <linux/types.h>
+
static __always_inline bool arch_static_branch(struct static_key *key, bool branch)
{
asm_volatile_goto("1:"
@@ -59,5 +73,40 @@ struct jump_entry {
jump_label_t key;
};
-#endif /* __ASSEMBLY__ */
+#else /* __ASSEMBLY__ */
+
+.macro STATIC_JUMP_IF_TRUE target, key, def
+.Lstatic_jump_\@:
+ .if \def
+ /* Equivalent to "jmp.d32 \target" */
+ .byte 0xe9
+ .long \target - .Lstatic_jump_after_\@
+.Lstatic_jump_after_\@:
+ .else
+ .byte STATIC_KEY_INIT_NOP
+ .endif
+ .pushsection __jump_table, "aw"
+ _ASM_ALIGN
+ _ASM_PTR .Lstatic_jump_\@, \target, \key
+ .popsection
+.endm
+
+.macro STATIC_JUMP_IF_FALSE target, key, def
+.Lstatic_jump_\@:
+ .if \def
+ .byte STATIC_KEY_INIT_NOP
+ .else
+ /* Equivalent to "jmp.d32 \target" */
+ .byte 0xe9
+ .long \target - .Lstatic_jump_after_\@
+.Lstatic_jump_after_\@:
+ .endif
+ .pushsection __jump_table, "aw"
+ _ASM_ALIGN
+ _ASM_PTR .Lstatic_jump_\@, \target, \key + 1
+ .popsection
+.endm
+
+#endif /* __ASSEMBLY__ */
+
#endif
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 30cfd64295a0..44adbb819041 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -25,6 +25,7 @@
#include <linux/pvclock_gtod.h>
#include <linux/clocksource.h>
#include <linux/irqbypass.h>
+#include <linux/hyperv.h>
#include <asm/pvclock-abi.h>
#include <asm/desc.h>
@@ -45,6 +46,31 @@
#define KVM_IRQCHIP_NUM_PINS KVM_IOAPIC_NUM_PINS
+/* x86-specific vcpu->requests bit members */
+#define KVM_REQ_MIGRATE_TIMER 8
+#define KVM_REQ_REPORT_TPR_ACCESS 9
+#define KVM_REQ_TRIPLE_FAULT 10
+#define KVM_REQ_MMU_SYNC 11
+#define KVM_REQ_CLOCK_UPDATE 12
+#define KVM_REQ_DEACTIVATE_FPU 13
+#define KVM_REQ_EVENT 14
+#define KVM_REQ_APF_HALT 15
+#define KVM_REQ_STEAL_UPDATE 16
+#define KVM_REQ_NMI 17
+#define KVM_REQ_PMU 18
+#define KVM_REQ_PMI 19
+#define KVM_REQ_SMI 20
+#define KVM_REQ_MASTERCLOCK_UPDATE 21
+#define KVM_REQ_MCLOCK_INPROGRESS 22
+#define KVM_REQ_SCAN_IOAPIC 23
+#define KVM_REQ_GLOBAL_CLOCK_UPDATE 24
+#define KVM_REQ_APIC_PAGE_RELOAD 25
+#define KVM_REQ_HV_CRASH 26
+#define KVM_REQ_IOAPIC_EOI_EXIT 27
+#define KVM_REQ_HV_RESET 28
+#define KVM_REQ_HV_EXIT 29
+#define KVM_REQ_HV_STIMER 30
+
#define CR0_RESERVED_BITS \
(~(unsigned long)(X86_CR0_PE | X86_CR0_MP | X86_CR0_EM | X86_CR0_TS \
| X86_CR0_ET | X86_CR0_NE | X86_CR0_WP | X86_CR0_AM \
@@ -213,6 +239,10 @@ union kvm_mmu_page_role {
};
};
+struct kvm_rmap_head {
+ unsigned long val;
+};
+
struct kvm_mmu_page {
struct list_head link;
struct hlist_node hash_link;
@@ -230,7 +260,7 @@ struct kvm_mmu_page {
bool unsync;
int root_count; /* Currently serving as active root */
unsigned int unsync_children;
- unsigned long parent_ptes; /* Reverse mapping for parent_pte */
+ struct kvm_rmap_head parent_ptes; /* rmap pointers to parent sptes */
/* The page is obsolete if mmu_valid_gen != kvm->arch.mmu_valid_gen. */
unsigned long mmu_valid_gen;
@@ -374,10 +404,38 @@ struct kvm_mtrr {
struct list_head head;
};
+/* Hyper-V SynIC timer */
+struct kvm_vcpu_hv_stimer {
+ struct hrtimer timer;
+ int index;
+ u64 config;
+ u64 count;
+ u64 exp_time;
+ struct hv_message msg;
+ bool msg_pending;
+};
+
+/* Hyper-V synthetic interrupt controller (SynIC)*/
+struct kvm_vcpu_hv_synic {
+ u64 version;
+ u64 control;
+ u64 msg_page;
+ u64 evt_page;
+ atomic64_t sint[HV_SYNIC_SINT_COUNT];
+ atomic_t sint_to_gsi[HV_SYNIC_SINT_COUNT];
+ DECLARE_BITMAP(auto_eoi_bitmap, 256);
+ DECLARE_BITMAP(vec_bitmap, 256);
+ bool active;
+};
+
/* Hyper-V per vcpu emulation context */
struct kvm_vcpu_hv {
u64 hv_vapic;
s64 runtime_offset;
+ struct kvm_vcpu_hv_synic synic;
+ struct kvm_hyperv_exit exit;
+ struct kvm_vcpu_hv_stimer stimer[HV_SYNIC_STIMER_COUNT];
+ DECLARE_BITMAP(stimer_pending_bitmap, HV_SYNIC_STIMER_COUNT);
};
struct kvm_vcpu_arch {
@@ -400,7 +458,8 @@ struct kvm_vcpu_arch {
u64 efer;
u64 apic_base;
struct kvm_lapic *apic; /* kernel irqchip context */
- u64 eoi_exit_bitmap[4];
+ bool apicv_active;
+ DECLARE_BITMAP(ioapic_handled_vectors, 256);
unsigned long apic_attention;
int32_t apic_arb_prio;
int mp_state;
@@ -589,7 +648,7 @@ struct kvm_lpage_info {
};
struct kvm_arch_memory_slot {
- unsigned long *rmap[KVM_NR_PAGE_SIZES];
+ struct kvm_rmap_head *rmap[KVM_NR_PAGE_SIZES];
struct kvm_lpage_info *lpage_info[KVM_NR_PAGE_SIZES - 1];
};
@@ -831,10 +890,11 @@ struct kvm_x86_ops {
void (*enable_nmi_window)(struct kvm_vcpu *vcpu);
void (*enable_irq_window)(struct kvm_vcpu *vcpu);
void (*update_cr8_intercept)(struct kvm_vcpu *vcpu, int tpr, int irr);
- int (*cpu_uses_apicv)(struct kvm_vcpu *vcpu);
+ bool (*get_enable_apicv)(void);
+ void (*refresh_apicv_exec_ctrl)(struct kvm_vcpu *vcpu);
void (*hwapic_irr_update)(struct kvm_vcpu *vcpu, int max_irr);
void (*hwapic_isr_update)(struct kvm *kvm, int isr);
- void (*load_eoi_exitmap)(struct kvm_vcpu *vcpu);
+ void (*load_eoi_exitmap)(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap);
void (*set_virtual_x2apic_mode)(struct kvm_vcpu *vcpu, bool set);
void (*set_apic_access_page_addr)(struct kvm_vcpu *vcpu, hpa_t hpa);
void (*deliver_posted_interrupt)(struct kvm_vcpu *vcpu, int vector);
@@ -1086,6 +1146,8 @@ gpa_t kvm_mmu_gva_to_gpa_write(struct kvm_vcpu *vcpu, gva_t gva,
gpa_t kvm_mmu_gva_to_gpa_system(struct kvm_vcpu *vcpu, gva_t gva,
struct x86_exception *exception);
+void kvm_vcpu_deactivate_apicv(struct kvm_vcpu *vcpu);
+
int kvm_emulate_hypercall(struct kvm_vcpu *vcpu);
int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t gva, u32 error_code,
@@ -1231,6 +1293,9 @@ u64 kvm_read_l1_tsc(struct kvm_vcpu *vcpu, u64 host_tsc);
unsigned long kvm_get_linear_rip(struct kvm_vcpu *vcpu);
bool kvm_is_linear_rip(struct kvm_vcpu *vcpu, unsigned long linear_rip);
+void kvm_make_mclock_inprogress_request(struct kvm *kvm);
+void kvm_make_scan_ioapic_request(struct kvm *kvm);
+
void kvm_arch_async_page_not_present(struct kvm_vcpu *vcpu,
struct kvm_async_pf *work);
void kvm_arch_async_page_present(struct kvm_vcpu *vcpu,
diff --git a/arch/x86/include/asm/lguest.h b/arch/x86/include/asm/lguest.h
index 3bbc07a57a31..73d0c9b92087 100644
--- a/arch/x86/include/asm/lguest.h
+++ b/arch/x86/include/asm/lguest.h
@@ -12,7 +12,9 @@
#define GUEST_PL 1
/* Page for Switcher text itself, then two pages per cpu */
-#define TOTAL_SWITCHER_PAGES (1 + 2 * nr_cpu_ids)
+#define SWITCHER_TEXT_PAGES (1)
+#define SWITCHER_STACK_PAGES (2 * nr_cpu_ids)
+#define TOTAL_SWITCHER_PAGES (SWITCHER_TEXT_PAGES + SWITCHER_STACK_PAGES)
/* Where we map the Switcher, in both Host and Guest. */
extern unsigned long switcher_addr;
diff --git a/arch/x86/include/asm/microcode.h b/arch/x86/include/asm/microcode.h
index 34e62b1dcfce..1e1b07a5a738 100644
--- a/arch/x86/include/asm/microcode.h
+++ b/arch/x86/include/asm/microcode.h
@@ -1,6 +1,7 @@
#ifndef _ASM_X86_MICROCODE_H
#define _ASM_X86_MICROCODE_H
+#include <asm/cpu.h>
#include <linux/earlycpio.h>
#define native_rdmsr(msr, val1, val2) \
@@ -95,14 +96,14 @@ static inline void __exit exit_amd_microcode(void) {}
/*
* In early loading microcode phase on BSP, boot_cpu_data is not set up yet.
- * x86_vendor() gets vendor id for BSP.
+ * x86_cpuid_vendor() gets vendor id for BSP.
*
* In 32 bit AP case, accessing boot_cpu_data needs linear address. To simplify
- * coding, we still use x86_vendor() to get vendor id for AP.
+ * coding, we still use x86_cpuid_vendor() to get vendor id for AP.
*
- * x86_vendor() gets vendor information directly from CPUID.
+ * x86_cpuid_vendor() gets vendor information directly from CPUID.
*/
-static inline int x86_vendor(void)
+static inline int x86_cpuid_vendor(void)
{
u32 eax = 0x00000000;
u32 ebx, ecx = 0, edx;
@@ -118,40 +119,14 @@ static inline int x86_vendor(void)
return X86_VENDOR_UNKNOWN;
}
-static inline unsigned int __x86_family(unsigned int sig)
-{
- unsigned int x86;
-
- x86 = (sig >> 8) & 0xf;
-
- if (x86 == 0xf)
- x86 += (sig >> 20) & 0xff;
-
- return x86;
-}
-
-static inline unsigned int x86_family(void)
+static inline unsigned int x86_cpuid_family(void)
{
u32 eax = 0x00000001;
u32 ebx, ecx = 0, edx;
native_cpuid(&eax, &ebx, &ecx, &edx);
- return __x86_family(eax);
-}
-
-static inline unsigned int x86_model(unsigned int sig)
-{
- unsigned int x86, model;
-
- x86 = __x86_family(sig);
-
- model = (sig >> 4) & 0xf;
-
- if (x86 == 0x6 || x86 == 0xf)
- model += ((sig >> 16) & 0xf) << 4;
-
- return model;
+ return x86_family(eax);
}
#ifdef CONFIG_MICROCODE
diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h
index 379cd3658799..bfd9b2a35a0b 100644
--- a/arch/x86/include/asm/mmu_context.h
+++ b/arch/x86/include/asm/mmu_context.h
@@ -116,8 +116,36 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
#endif
cpumask_set_cpu(cpu, mm_cpumask(next));
- /* Re-load page tables */
+ /*
+ * Re-load page tables.
+ *
+ * This logic has an ordering constraint:
+ *
+ * CPU 0: Write to a PTE for 'next'
+ * CPU 0: load bit 1 in mm_cpumask. if nonzero, send IPI.
+ * CPU 1: set bit 1 in next's mm_cpumask
+ * CPU 1: load from the PTE that CPU 0 writes (implicit)
+ *
+ * We need to prevent an outcome in which CPU 1 observes
+ * the new PTE value and CPU 0 observes bit 1 clear in
+ * mm_cpumask. (If that occurs, then the IPI will never
+ * be sent, and CPU 0's TLB will contain a stale entry.)
+ *
+ * The bad outcome can occur if either CPU's load is
+ * reordered before that CPU's store, so both CPUs must
+ * execute full barriers to prevent this from happening.
+ *
+ * Thus, switch_mm needs a full barrier between the
+ * store to mm_cpumask and any operation that could load
+ * from next->pgd. TLB fills are special and can happen
+ * due to instruction fetches or for no reason at all,
+ * and neither LOCK nor MFENCE orders them.
+ * Fortunately, load_cr3() is serializing and gives the
+ * ordering guarantee we need.
+ *
+ */
load_cr3(next->pgd);
+
trace_tlb_flush(TLB_FLUSH_ON_TASK_SWITCH, TLB_FLUSH_ALL);
/* Stop flush ipis for the previous mm */
@@ -156,10 +184,14 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
* schedule, protecting us from simultaneous changes.
*/
cpumask_set_cpu(cpu, mm_cpumask(next));
+
/*
* We were in lazy tlb mode and leave_mm disabled
* tlb flush IPI delivery. We must reload CR3
* to make sure to use no freed page tables.
+ *
+ * As above, load_cr3() is serializing and orders TLB
+ * fills with respect to the mm_cpumask write.
*/
load_cr3(next->pgd);
trace_tlb_flush(TLB_FLUSH_ON_TASK_SWITCH, TLB_FLUSH_ALL);
diff --git a/arch/x86/include/asm/msi.h b/arch/x86/include/asm/msi.h
index 93724cc62177..eb4b09b41df5 100644
--- a/arch/x86/include/asm/msi.h
+++ b/arch/x86/include/asm/msi.h
@@ -1,7 +1,13 @@
#ifndef _ASM_X86_MSI_H
#define _ASM_X86_MSI_H
#include <asm/hw_irq.h>
+#include <asm/irqdomain.h>
typedef struct irq_alloc_info msi_alloc_info_t;
+int pci_msi_prepare(struct irq_domain *domain, struct device *dev, int nvec,
+ msi_alloc_info_t *arg);
+
+void pci_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc);
+
#endif /* _ASM_X86_MSI_H */
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 690b4027e17c..b05402ef3b84 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -321,6 +321,7 @@
#define MSR_F15H_PERF_CTR 0xc0010201
#define MSR_F15H_NB_PERF_CTL 0xc0010240
#define MSR_F15H_NB_PERF_CTR 0xc0010241
+#define MSR_F15H_IC_CFG 0xc0011021
/* Fam 10h MSRs */
#define MSR_FAM10H_MMIO_CONF_BASE 0xc0010058
diff --git a/arch/x86/include/asm/msr-trace.h b/arch/x86/include/asm/msr-trace.h
new file mode 100644
index 000000000000..7567225747d8
--- /dev/null
+++ b/arch/x86/include/asm/msr-trace.h
@@ -0,0 +1,57 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM msr
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE msr-trace
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH asm/
+
+#if !defined(_TRACE_MSR_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_MSR_H
+
+#include <linux/tracepoint.h>
+
+/*
+ * Tracing for x86 model specific registers. Directly maps to the
+ * RDMSR/WRMSR instructions.
+ */
+
+DECLARE_EVENT_CLASS(msr_trace_class,
+ TP_PROTO(unsigned msr, u64 val, int failed),
+ TP_ARGS(msr, val, failed),
+ TP_STRUCT__entry(
+ __field( unsigned, msr )
+ __field( u64, val )
+ __field( int, failed )
+ ),
+ TP_fast_assign(
+ __entry->msr = msr;
+ __entry->val = val;
+ __entry->failed = failed;
+ ),
+ TP_printk("%x, value %llx%s",
+ __entry->msr,
+ __entry->val,
+ __entry->failed ? " #GP" : "")
+);
+
+DEFINE_EVENT(msr_trace_class, read_msr,
+ TP_PROTO(unsigned msr, u64 val, int failed),
+ TP_ARGS(msr, val, failed)
+);
+
+DEFINE_EVENT(msr_trace_class, write_msr,
+ TP_PROTO(unsigned msr, u64 val, int failed),
+ TP_ARGS(msr, val, failed)
+);
+
+DEFINE_EVENT(msr_trace_class, rdpmc,
+ TP_PROTO(unsigned msr, u64 val, int failed),
+ TP_ARGS(msr, val, failed)
+);
+
+#endif /* _TRACE_MSR_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/arch/x86/include/asm/msr.h b/arch/x86/include/asm/msr.h
index 77d8b284e4a7..93fb7c1cffda 100644
--- a/arch/x86/include/asm/msr.h
+++ b/arch/x86/include/asm/msr.h
@@ -32,6 +32,16 @@ struct msr_regs_info {
int err;
};
+struct saved_msr {
+ bool valid;
+ struct msr_info info;
+};
+
+struct saved_msrs {
+ unsigned int num;
+ struct saved_msr *array;
+};
+
static inline unsigned long long native_read_tscp(unsigned int *aux)
{
unsigned long low, high;
@@ -57,11 +67,34 @@ static inline unsigned long long native_read_tscp(unsigned int *aux)
#define EAX_EDX_RET(val, low, high) "=A" (val)
#endif
+#ifdef CONFIG_TRACEPOINTS
+/*
+ * Be very careful with includes. This header is prone to include loops.
+ */
+#include <asm/atomic.h>
+#include <linux/tracepoint-defs.h>
+
+extern struct tracepoint __tracepoint_read_msr;
+extern struct tracepoint __tracepoint_write_msr;
+extern struct tracepoint __tracepoint_rdpmc;
+#define msr_tracepoint_active(t) static_key_false(&(t).key)
+extern void do_trace_write_msr(unsigned msr, u64 val, int failed);
+extern void do_trace_read_msr(unsigned msr, u64 val, int failed);
+extern void do_trace_rdpmc(unsigned msr, u64 val, int failed);
+#else
+#define msr_tracepoint_active(t) false
+static inline void do_trace_write_msr(unsigned msr, u64 val, int failed) {}
+static inline void do_trace_read_msr(unsigned msr, u64 val, int failed) {}
+static inline void do_trace_rdpmc(unsigned msr, u64 val, int failed) {}
+#endif
+
static inline unsigned long long native_read_msr(unsigned int msr)
{
DECLARE_ARGS(val, low, high);
asm volatile("rdmsr" : EAX_EDX_RET(val, low, high) : "c" (msr));
+ if (msr_tracepoint_active(__tracepoint_read_msr))
+ do_trace_read_msr(msr, EAX_EDX_VAL(val, low, high), 0);
return EAX_EDX_VAL(val, low, high);
}
@@ -78,6 +111,8 @@ static inline unsigned long long native_read_msr_safe(unsigned int msr,
_ASM_EXTABLE(2b, 3b)
: [err] "=r" (*err), EAX_EDX_RET(val, low, high)
: "c" (msr), [fault] "i" (-EIO));
+ if (msr_tracepoint_active(__tracepoint_read_msr))
+ do_trace_read_msr(msr, EAX_EDX_VAL(val, low, high), *err);
return EAX_EDX_VAL(val, low, high);
}
@@ -85,6 +120,8 @@ static inline void native_write_msr(unsigned int msr,
unsigned low, unsigned high)
{
asm volatile("wrmsr" : : "c" (msr), "a"(low), "d" (high) : "memory");
+ if (msr_tracepoint_active(__tracepoint_read_msr))
+ do_trace_write_msr(msr, ((u64)high << 32 | low), 0);
}
/* Can be uninlined because referenced by paravirt */
@@ -102,6 +139,8 @@ notrace static inline int native_write_msr_safe(unsigned int msr,
: "c" (msr), "0" (low), "d" (high),
[fault] "i" (-EIO)
: "memory");
+ if (msr_tracepoint_active(__tracepoint_read_msr))
+ do_trace_write_msr(msr, ((u64)high << 32 | low), err);
return err;
}
@@ -160,6 +199,8 @@ static inline unsigned long long native_read_pmc(int counter)
DECLARE_ARGS(val, low, high);
asm volatile("rdpmc" : EAX_EDX_RET(val, low, high) : "c" (counter));
+ if (msr_tracepoint_active(__tracepoint_rdpmc))
+ do_trace_rdpmc(counter, EAX_EDX_VAL(val, low, high), 0);
return EAX_EDX_VAL(val, low, high);
}
@@ -190,7 +231,7 @@ static inline void wrmsr(unsigned msr, unsigned low, unsigned high)
static inline void wrmsrl(unsigned msr, u64 val)
{
- native_write_msr(msr, (u32)val, (u32)(val >> 32));
+ native_write_msr(msr, (u32)(val & 0xffffffffULL), (u32)(val >> 32));
}
/* wrmsr with exception handling */
diff --git a/arch/x86/include/asm/page_types.h b/arch/x86/include/asm/page_types.h
index c5b7fb2774d0..7bd0099384ca 100644
--- a/arch/x86/include/asm/page_types.h
+++ b/arch/x86/include/asm/page_types.h
@@ -5,23 +5,25 @@
#include <linux/types.h>
/* PAGE_SHIFT determines the page size */
-#define PAGE_SHIFT 12
-#define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT)
-#define PAGE_MASK (~(PAGE_SIZE-1))
+#define PAGE_SHIFT 12
+#define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT)
+#define PAGE_MASK (~(PAGE_SIZE-1))
+
+#define PMD_PAGE_SIZE (_AC(1, UL) << PMD_SHIFT)
+#define PMD_PAGE_MASK (~(PMD_PAGE_SIZE-1))
+
+#define PUD_PAGE_SIZE (_AC(1, UL) << PUD_SHIFT)
+#define PUD_PAGE_MASK (~(PUD_PAGE_SIZE-1))
#define __PHYSICAL_MASK ((phys_addr_t)((1ULL << __PHYSICAL_MASK_SHIFT) - 1))
#define __VIRTUAL_MASK ((1UL << __VIRTUAL_MASK_SHIFT) - 1)
-/* Cast PAGE_MASK to a signed type so that it is sign-extended if
+/* Cast *PAGE_MASK to a signed type so that it is sign-extended if
virtual addresses are 32-bits but physical addresses are larger
(ie, 32-bit PAE). */
#define PHYSICAL_PAGE_MASK (((signed long)PAGE_MASK) & __PHYSICAL_MASK)
-
-#define PMD_PAGE_SIZE (_AC(1, UL) << PMD_SHIFT)
-#define PMD_PAGE_MASK (~(PMD_PAGE_SIZE-1))
-
-#define PUD_PAGE_SIZE (_AC(1, UL) << PUD_SHIFT)
-#define PUD_PAGE_MASK (~(PUD_PAGE_SIZE-1))
+#define PHYSICAL_PMD_PAGE_MASK (((signed long)PMD_PAGE_MASK) & __PHYSICAL_MASK)
+#define PHYSICAL_PUD_PAGE_MASK (((signed long)PUD_PAGE_MASK) & __PHYSICAL_MASK)
#define HPAGE_SHIFT PMD_SHIFT
#define HPAGE_SIZE (_AC(1,UL) << HPAGE_SHIFT)
diff --git a/arch/x86/include/asm/paravirt.h b/arch/x86/include/asm/paravirt.h
index 10d0596433f8..f6192502149e 100644
--- a/arch/x86/include/asm/paravirt.h
+++ b/arch/x86/include/asm/paravirt.h
@@ -19,6 +19,12 @@ static inline int paravirt_enabled(void)
return pv_info.paravirt_enabled;
}
+static inline int paravirt_has_feature(unsigned int feature)
+{
+ WARN_ON_ONCE(!pv_info.paravirt_enabled);
+ return (pv_info.features & feature);
+}
+
static inline void load_sp0(struct tss_struct *tss,
struct thread_struct *thread)
{
@@ -285,15 +291,6 @@ static inline void slow_down_io(void)
#endif
}
-#ifdef CONFIG_SMP
-static inline void startup_ipi_hook(int phys_apicid, unsigned long start_eip,
- unsigned long start_esp)
-{
- PVOP_VCALL3(pv_apic_ops.startup_ipi_hook,
- phys_apicid, start_eip, start_esp);
-}
-#endif
-
static inline void paravirt_activate_mm(struct mm_struct *prev,
struct mm_struct *next)
{
@@ -375,23 +372,6 @@ static inline void pte_update(struct mm_struct *mm, unsigned long addr,
{
PVOP_VCALL3(pv_mmu_ops.pte_update, mm, addr, ptep);
}
-static inline void pmd_update(struct mm_struct *mm, unsigned long addr,
- pmd_t *pmdp)
-{
- PVOP_VCALL3(pv_mmu_ops.pmd_update, mm, addr, pmdp);
-}
-
-static inline void pte_update_defer(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep)
-{
- PVOP_VCALL3(pv_mmu_ops.pte_update_defer, mm, addr, ptep);
-}
-
-static inline void pmd_update_defer(struct mm_struct *mm, unsigned long addr,
- pmd_t *pmdp)
-{
- PVOP_VCALL3(pv_mmu_ops.pmd_update_defer, mm, addr, pmdp);
-}
static inline pte_t __pte(pteval_t val)
{
@@ -922,23 +902,11 @@ extern void default_banner(void);
call PARA_INDIRECT(pv_irq_ops+PV_IRQ_irq_enable); \
PV_RESTORE_REGS(clobbers | CLBR_CALLEE_SAVE);)
-#define USERGS_SYSRET32 \
- PARA_SITE(PARA_PATCH(pv_cpu_ops, PV_CPU_usergs_sysret32), \
- CLBR_NONE, \
- jmp PARA_INDIRECT(pv_cpu_ops+PV_CPU_usergs_sysret32))
-
#ifdef CONFIG_X86_32
#define GET_CR0_INTO_EAX \
push %ecx; push %edx; \
call PARA_INDIRECT(pv_cpu_ops+PV_CPU_read_cr0); \
pop %edx; pop %ecx
-
-#define ENABLE_INTERRUPTS_SYSEXIT \
- PARA_SITE(PARA_PATCH(pv_cpu_ops, PV_CPU_irq_enable_sysexit), \
- CLBR_NONE, \
- jmp PARA_INDIRECT(pv_cpu_ops+PV_CPU_irq_enable_sysexit))
-
-
#else /* !CONFIG_X86_32 */
/*
diff --git a/arch/x86/include/asm/paravirt_types.h b/arch/x86/include/asm/paravirt_types.h
index 31247b5bff7c..77db5616a473 100644
--- a/arch/x86/include/asm/paravirt_types.h
+++ b/arch/x86/include/asm/paravirt_types.h
@@ -70,9 +70,14 @@ struct pv_info {
#endif
int paravirt_enabled;
+ unsigned int features; /* valid only if paravirt_enabled is set */
const char *name;
};
+#define paravirt_has(x) paravirt_has_feature(PV_SUPPORTED_##x)
+/* Supported features */
+#define PV_SUPPORTED_RTC (1<<0)
+
struct pv_init_ops {
/*
* Patch may replace one of the defined code sequences with
@@ -157,15 +162,6 @@ struct pv_cpu_ops {
u64 (*read_pmc)(int counter);
-#ifdef CONFIG_X86_32
- /*
- * Atomically enable interrupts and return to userspace. This
- * 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
* sysret. Only used in 64-bit kernels to return to 64-bit
@@ -174,14 +170,6 @@ struct pv_cpu_ops {
*/
void (*usergs_sysret64)(void);
- /*
- * Switch to usermode gs and return to 32-bit usermode using
- * sysret. Used to return to 32-on-64 compat processes.
- * Other usermode register state, including %esp, must already
- * be restored.
- */
- void (*usergs_sysret32)(void);
-
/* Normal iret. Jump to this with the standard iret stack
frame set up. */
void (*iret)(void);
@@ -215,14 +203,6 @@ struct pv_irq_ops {
#endif
};
-struct pv_apic_ops {
-#ifdef CONFIG_X86_LOCAL_APIC
- void (*startup_ipi_hook)(int phys_apicid,
- unsigned long start_eip,
- unsigned long start_esp);
-#endif
-};
-
struct pv_mmu_ops {
unsigned long (*read_cr2)(void);
void (*write_cr2)(unsigned long);
@@ -274,12 +254,6 @@ struct pv_mmu_ops {
pmd_t *pmdp, pmd_t pmdval);
void (*pte_update)(struct mm_struct *mm, unsigned long addr,
pte_t *ptep);
- void (*pte_update_defer)(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep);
- void (*pmd_update)(struct mm_struct *mm, unsigned long addr,
- pmd_t *pmdp);
- void (*pmd_update_defer)(struct mm_struct *mm,
- unsigned long addr, pmd_t *pmdp);
pte_t (*ptep_modify_prot_start)(struct mm_struct *mm, unsigned long addr,
pte_t *ptep);
@@ -354,7 +328,6 @@ struct paravirt_patch_template {
struct pv_time_ops pv_time_ops;
struct pv_cpu_ops pv_cpu_ops;
struct pv_irq_ops pv_irq_ops;
- struct pv_apic_ops pv_apic_ops;
struct pv_mmu_ops pv_mmu_ops;
struct pv_lock_ops pv_lock_ops;
};
@@ -364,7 +337,6 @@ extern struct pv_init_ops pv_init_ops;
extern struct pv_time_ops pv_time_ops;
extern struct pv_cpu_ops pv_cpu_ops;
extern struct pv_irq_ops pv_irq_ops;
-extern struct pv_apic_ops pv_apic_ops;
extern struct pv_mmu_ops pv_mmu_ops;
extern struct pv_lock_ops pv_lock_ops;
@@ -402,10 +374,8 @@ extern struct pv_lock_ops pv_lock_ops;
__visible extern const char start_##ops##_##name[], end_##ops##_##name[]; \
asm(NATIVE_LABEL("start_", ops, name) code NATIVE_LABEL("end_", ops, name))
-unsigned paravirt_patch_nop(void);
unsigned paravirt_patch_ident_32(void *insnbuf, unsigned len);
unsigned paravirt_patch_ident_64(void *insnbuf, unsigned len);
-unsigned paravirt_patch_ignore(unsigned len);
unsigned paravirt_patch_call(void *insnbuf,
const void *target, u16 tgt_clobbers,
unsigned long addr, u16 site_clobbers,
diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
index 6ec0c8b2e9df..d3eee663c41f 100644
--- a/arch/x86/include/asm/pgtable.h
+++ b/arch/x86/include/asm/pgtable.h
@@ -69,9 +69,6 @@ extern struct mm_struct *pgd_page_get_mm(struct page *page);
#define pmd_clear(pmd) native_pmd_clear(pmd)
#define pte_update(mm, addr, ptep) do { } while (0)
-#define pte_update_defer(mm, addr, ptep) do { } while (0)
-#define pmd_update(mm, addr, ptep) do { } while (0)
-#define pmd_update_defer(mm, addr, ptep) do { } while (0)
#define pgd_val(x) native_pgd_val(x)
#define __pgd(x) native_make_pgd(x)
@@ -731,14 +728,9 @@ static inline void native_set_pmd_at(struct mm_struct *mm, unsigned long addr,
* updates should either be sets, clears, or set_pte_atomic for P->P
* transitions, which means this hook should only be called for user PTEs.
* This hook implies a P->P protection or access change has taken place, which
- * requires a subsequent TLB flush. The notification can optionally be delayed
- * until the TLB flush event by using the pte_update_defer form of the
- * interface, but care must be taken to assure that the flush happens while
- * still holding the same page table lock so that the shadow and primary pages
- * do not become out of sync on SMP.
+ * requires a subsequent TLB flush.
*/
#define pte_update(mm, addr, ptep) do { } while (0)
-#define pte_update_defer(mm, addr, ptep) do { } while (0)
#endif
/*
@@ -830,9 +822,7 @@ static inline int pmd_write(pmd_t pmd)
static inline pmd_t pmdp_huge_get_and_clear(struct mm_struct *mm, unsigned long addr,
pmd_t *pmdp)
{
- pmd_t pmd = native_pmdp_get_and_clear(pmdp);
- pmd_update(mm, addr, pmdp);
- return pmd;
+ return native_pmdp_get_and_clear(pmdp);
}
#define __HAVE_ARCH_PMDP_SET_WRPROTECT
@@ -840,7 +830,6 @@ static inline void pmdp_set_wrprotect(struct mm_struct *mm,
unsigned long addr, pmd_t *pmdp)
{
clear_bit(_PAGE_BIT_RW, (unsigned long *)pmdp);
- pmd_update(mm, addr, pmdp);
}
/*
diff --git a/arch/x86/include/asm/pgtable_types.h b/arch/x86/include/asm/pgtable_types.h
index dd5b0aa9dd2f..a471cadb9630 100644
--- a/arch/x86/include/asm/pgtable_types.h
+++ b/arch/x86/include/asm/pgtable_types.h
@@ -279,17 +279,14 @@ static inline pmdval_t native_pmd_val(pmd_t pmd)
static inline pudval_t pud_pfn_mask(pud_t pud)
{
if (native_pud_val(pud) & _PAGE_PSE)
- return PUD_PAGE_MASK & PHYSICAL_PAGE_MASK;
+ return PHYSICAL_PUD_PAGE_MASK;
else
return PTE_PFN_MASK;
}
static inline pudval_t pud_flags_mask(pud_t pud)
{
- if (native_pud_val(pud) & _PAGE_PSE)
- return ~(PUD_PAGE_MASK & (pudval_t)PHYSICAL_PAGE_MASK);
- else
- return ~PTE_PFN_MASK;
+ return ~pud_pfn_mask(pud);
}
static inline pudval_t pud_flags(pud_t pud)
@@ -300,17 +297,14 @@ static inline pudval_t pud_flags(pud_t pud)
static inline pmdval_t pmd_pfn_mask(pmd_t pmd)
{
if (native_pmd_val(pmd) & _PAGE_PSE)
- return PMD_PAGE_MASK & PHYSICAL_PAGE_MASK;
+ return PHYSICAL_PMD_PAGE_MASK;
else
return PTE_PFN_MASK;
}
static inline pmdval_t pmd_flags_mask(pmd_t pmd)
{
- if (native_pmd_val(pmd) & _PAGE_PSE)
- return ~(PMD_PAGE_MASK & (pmdval_t)PHYSICAL_PAGE_MASK);
- else
- return ~PTE_PFN_MASK;
+ return ~pmd_pfn_mask(pmd);
}
static inline pmdval_t pmd_flags(pmd_t pmd)
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index 67522256c7ff..2d5a50cb61a2 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -472,6 +472,7 @@ static inline unsigned long current_top_of_stack(void)
#else
#define __cpuid native_cpuid
#define paravirt_enabled() 0
+#define paravirt_has(x) 0
static inline void load_sp0(struct tss_struct *tss,
struct thread_struct *thread)
diff --git a/arch/x86/include/asm/pvclock.h b/arch/x86/include/asm/pvclock.h
index 7a6bed5c08bc..fdcc04020636 100644
--- a/arch/x86/include/asm/pvclock.h
+++ b/arch/x86/include/asm/pvclock.h
@@ -4,6 +4,15 @@
#include <linux/clocksource.h>
#include <asm/pvclock-abi.h>
+#ifdef CONFIG_KVM_GUEST
+extern struct pvclock_vsyscall_time_info *pvclock_pvti_cpu0_va(void);
+#else
+static inline struct pvclock_vsyscall_time_info *pvclock_pvti_cpu0_va(void)
+{
+ return NULL;
+}
+#endif
+
/* some helper functions for xen and kvm pv clock sources */
cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src);
u8 pvclock_read_flags(struct pvclock_vcpu_time_info *src);
@@ -91,10 +100,5 @@ struct pvclock_vsyscall_time_info {
} __attribute__((__aligned__(SMP_CACHE_BYTES)));
#define PVTI_SIZE sizeof(struct pvclock_vsyscall_time_info)
-#define PVCLOCK_VSYSCALL_NR_PAGES (((NR_CPUS-1)/(PAGE_SIZE/PVTI_SIZE))+1)
-
-int __init pvclock_init_vsyscall(struct pvclock_vsyscall_time_info *i,
- int size);
-struct pvclock_vcpu_time_info *pvclock_get_vsyscall_time_info(int cpu);
#endif /* _ASM_X86_PVCLOCK_H */
diff --git a/arch/x86/include/asm/qspinlock_paravirt.h b/arch/x86/include/asm/qspinlock_paravirt.h
index b002e711ba88..9f92c180ed2f 100644
--- a/arch/x86/include/asm/qspinlock_paravirt.h
+++ b/arch/x86/include/asm/qspinlock_paravirt.h
@@ -1,6 +1,65 @@
#ifndef __ASM_QSPINLOCK_PARAVIRT_H
#define __ASM_QSPINLOCK_PARAVIRT_H
+/*
+ * For x86-64, PV_CALLEE_SAVE_REGS_THUNK() saves and restores 8 64-bit
+ * registers. For i386, however, only 1 32-bit register needs to be saved
+ * and restored. So an optimized version of __pv_queued_spin_unlock() is
+ * hand-coded for 64-bit, but it isn't worthwhile to do it for 32-bit.
+ */
+#ifdef CONFIG_64BIT
+
+PV_CALLEE_SAVE_REGS_THUNK(__pv_queued_spin_unlock_slowpath);
+#define __pv_queued_spin_unlock __pv_queued_spin_unlock
+#define PV_UNLOCK "__raw_callee_save___pv_queued_spin_unlock"
+#define PV_UNLOCK_SLOWPATH "__raw_callee_save___pv_queued_spin_unlock_slowpath"
+
+/*
+ * Optimized assembly version of __raw_callee_save___pv_queued_spin_unlock
+ * which combines the registers saving trunk and the body of the following
+ * C code:
+ *
+ * void __pv_queued_spin_unlock(struct qspinlock *lock)
+ * {
+ * struct __qspinlock *l = (void *)lock;
+ * u8 lockval = cmpxchg(&l->locked, _Q_LOCKED_VAL, 0);
+ *
+ * if (likely(lockval == _Q_LOCKED_VAL))
+ * return;
+ * pv_queued_spin_unlock_slowpath(lock, lockval);
+ * }
+ *
+ * For x86-64,
+ * rdi = lock (first argument)
+ * rsi = lockval (second argument)
+ * rdx = internal variable (set to 0)
+ */
+asm (".pushsection .text;"
+ ".globl " PV_UNLOCK ";"
+ ".align 4,0x90;"
+ PV_UNLOCK ": "
+ "push %rdx;"
+ "mov $0x1,%eax;"
+ "xor %edx,%edx;"
+ "lock cmpxchg %dl,(%rdi);"
+ "cmp $0x1,%al;"
+ "jne .slowpath;"
+ "pop %rdx;"
+ "ret;"
+ ".slowpath: "
+ "push %rsi;"
+ "movzbl %al,%esi;"
+ "call " PV_UNLOCK_SLOWPATH ";"
+ "pop %rsi;"
+ "pop %rdx;"
+ "ret;"
+ ".size " PV_UNLOCK ", .-" PV_UNLOCK ";"
+ ".popsection");
+
+#else /* CONFIG_64BIT */
+
+extern void __pv_queued_spin_unlock(struct qspinlock *lock);
PV_CALLEE_SAVE_REGS_THUNK(__pv_queued_spin_unlock);
+#endif /* CONFIG_64BIT */
#endif
diff --git a/arch/x86/include/asm/reboot.h b/arch/x86/include/asm/reboot.h
index a82c4f1b4d83..2cb1cc253d51 100644
--- a/arch/x86/include/asm/reboot.h
+++ b/arch/x86/include/asm/reboot.h
@@ -25,5 +25,6 @@ void __noreturn machine_real_restart(unsigned int type);
typedef void (*nmi_shootdown_cb)(int, struct pt_regs*);
void nmi_shootdown_cpus(nmi_shootdown_cb callback);
+void run_crash_ipi_callback(struct pt_regs *regs);
#endif /* _ASM_X86_REBOOT_H */
diff --git a/arch/x86/include/asm/smp.h b/arch/x86/include/asm/smp.h
index 222a6a3ca2b5..dfcf0727623b 100644
--- a/arch/x86/include/asm/smp.h
+++ b/arch/x86/include/asm/smp.h
@@ -21,15 +21,6 @@
extern int smp_num_siblings;
extern unsigned int num_processors;
-static inline bool cpu_has_ht_siblings(void)
-{
- bool has_siblings = false;
-#ifdef CONFIG_SMP
- has_siblings = cpu_has_ht && smp_num_siblings > 1;
-#endif
- return has_siblings;
-}
-
DECLARE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_sibling_map);
DECLARE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_core_map);
/* cpus sharing the last level cache: */
@@ -74,9 +65,6 @@ struct smp_ops {
extern void set_cpu_sibling_map(int cpu);
#ifdef CONFIG_SMP
-#ifndef CONFIG_PARAVIRT
-#define startup_ipi_hook(phys_apicid, start_eip, start_esp) do { } while (0)
-#endif
extern struct smp_ops smp_ops;
static inline void smp_send_stop(void)
diff --git a/arch/x86/include/asm/suspend_32.h b/arch/x86/include/asm/suspend_32.h
index d1793f06854d..8e9dbe7b73a1 100644
--- a/arch/x86/include/asm/suspend_32.h
+++ b/arch/x86/include/asm/suspend_32.h
@@ -15,6 +15,7 @@ struct saved_context {
unsigned long cr0, cr2, cr3, cr4;
u64 misc_enable;
bool misc_enable_saved;
+ struct saved_msrs saved_msrs;
struct desc_ptr gdt_desc;
struct desc_ptr idt;
u16 ldt;
diff --git a/arch/x86/include/asm/suspend_64.h b/arch/x86/include/asm/suspend_64.h
index 7ebf0ebe4e68..6136a18152af 100644
--- a/arch/x86/include/asm/suspend_64.h
+++ b/arch/x86/include/asm/suspend_64.h
@@ -24,6 +24,7 @@ struct saved_context {
unsigned long cr0, cr2, cr3, cr4, cr8;
u64 misc_enable;
bool misc_enable_saved;
+ struct saved_msrs saved_msrs;
unsigned long efer;
u16 gdt_pad; /* Unused */
struct desc_ptr gdt_desc;
diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index 09b1b0ab94b7..660458af425d 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -745,5 +745,14 @@ copy_to_user(void __user *to, const void *from, unsigned long n)
#undef __copy_from_user_overflow
#undef __copy_to_user_overflow
+/*
+ * We rely on the nested NMI work to allow atomic faults from the NMI path; the
+ * nested NMI paths are careful to preserve CR2.
+ *
+ * Caller must use pagefault_enable/disable, or run in interrupt context,
+ * and also do a uaccess_ok() check
+ */
+#define __copy_from_user_nmi __copy_from_user_inatomic
+
#endif /* _ASM_X86_UACCESS_H */
diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h
index 756de9190aec..deabaf9759b6 100644
--- a/arch/x86/include/asm/vdso.h
+++ b/arch/x86/include/asm/vdso.h
@@ -22,6 +22,7 @@ struct vdso_image {
long sym_vvar_page;
long sym_hpet_page;
+ long sym_pvclock_page;
long sym_VDSO32_NOTE_MASK;
long sym___kernel_sigreturn;
long sym___kernel_rt_sigreturn;
diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h
index 48d34d28f5a6..1ae89a2721d6 100644
--- a/arch/x86/include/asm/x86_init.h
+++ b/arch/x86/include/asm/x86_init.h
@@ -1,7 +1,6 @@
#ifndef _ASM_X86_PLATFORM_H
#define _ASM_X86_PLATFORM_H
-#include <asm/pgtable_types.h>
#include <asm/bootparam.h>
struct mpc_bus;
@@ -83,13 +82,11 @@ struct x86_init_paging {
* struct x86_init_timers - platform specific timer setup
* @setup_perpcu_clockev: set up the per cpu clock event device for the
* boot cpu
- * @tsc_pre_init: platform function called before TSC init
* @timer_init: initialize the platform timer (default PIT/HPET)
* @wallclock_init: init the wallclock device
*/
struct x86_init_timers {
void (*setup_percpu_clockev)(void);
- void (*tsc_pre_init)(void);
void (*timer_init)(void);
void (*wallclock_init)(void);
};
diff --git a/arch/x86/include/asm/xen/hypercall.h b/arch/x86/include/asm/xen/hypercall.h
index 4c20dd333412..3bcdcc84259d 100644
--- a/arch/x86/include/asm/xen/hypercall.h
+++ b/arch/x86/include/asm/xen/hypercall.h
@@ -310,10 +310,10 @@ HYPERVISOR_mca(struct xen_mc *mc_op)
}
static inline int
-HYPERVISOR_dom0_op(struct xen_platform_op *platform_op)
+HYPERVISOR_platform_op(struct xen_platform_op *op)
{
- platform_op->interface_version = XENPF_INTERFACE_VERSION;
- return _hypercall1(int, dom0_op, platform_op);
+ op->interface_version = XENPF_INTERFACE_VERSION;
+ return _hypercall1(int, platform_op, op);
}
static inline int
diff --git a/arch/x86/include/asm/xor_32.h b/arch/x86/include/asm/xor_32.h
index 5a08bc8bff33..c54beb44c4c1 100644
--- a/arch/x86/include/asm/xor_32.h
+++ b/arch/x86/include/asm/xor_32.h
@@ -553,7 +553,7 @@ do { \
if (cpu_has_xmm) { \
xor_speed(&xor_block_pIII_sse); \
xor_speed(&xor_block_sse_pf64); \
- } else if (cpu_has_mmx) { \
+ } else if (boot_cpu_has(X86_FEATURE_MMX)) { \
xor_speed(&xor_block_pII_mmx); \
xor_speed(&xor_block_p5_mmx); \
} else { \
diff --git a/arch/x86/include/uapi/asm/hyperv.h b/arch/x86/include/uapi/asm/hyperv.h
index 040d4083c24f..7956412d09bd 100644
--- a/arch/x86/include/uapi/asm/hyperv.h
+++ b/arch/x86/include/uapi/asm/hyperv.h
@@ -269,4 +269,96 @@ typedef struct _HV_REFERENCE_TSC_PAGE {
#define HV_SYNIC_SINT_AUTO_EOI (1ULL << 17)
#define HV_SYNIC_SINT_VECTOR_MASK (0xFF)
+#define HV_SYNIC_STIMER_COUNT (4)
+
+/* Define synthetic interrupt controller message constants. */
+#define HV_MESSAGE_SIZE (256)
+#define HV_MESSAGE_PAYLOAD_BYTE_COUNT (240)
+#define HV_MESSAGE_PAYLOAD_QWORD_COUNT (30)
+
+/* Define hypervisor message types. */
+enum hv_message_type {
+ HVMSG_NONE = 0x00000000,
+
+ /* Memory access messages. */
+ HVMSG_UNMAPPED_GPA = 0x80000000,
+ HVMSG_GPA_INTERCEPT = 0x80000001,
+
+ /* Timer notification messages. */
+ HVMSG_TIMER_EXPIRED = 0x80000010,
+
+ /* Error messages. */
+ HVMSG_INVALID_VP_REGISTER_VALUE = 0x80000020,
+ HVMSG_UNRECOVERABLE_EXCEPTION = 0x80000021,
+ HVMSG_UNSUPPORTED_FEATURE = 0x80000022,
+
+ /* Trace buffer complete messages. */
+ HVMSG_EVENTLOG_BUFFERCOMPLETE = 0x80000040,
+
+ /* Platform-specific processor intercept messages. */
+ HVMSG_X64_IOPORT_INTERCEPT = 0x80010000,
+ HVMSG_X64_MSR_INTERCEPT = 0x80010001,
+ HVMSG_X64_CPUID_INTERCEPT = 0x80010002,
+ HVMSG_X64_EXCEPTION_INTERCEPT = 0x80010003,
+ HVMSG_X64_APIC_EOI = 0x80010004,
+ HVMSG_X64_LEGACY_FP_ERROR = 0x80010005
+};
+
+/* Define synthetic interrupt controller message flags. */
+union hv_message_flags {
+ __u8 asu8;
+ struct {
+ __u8 msg_pending:1;
+ __u8 reserved:7;
+ };
+};
+
+/* Define port identifier type. */
+union hv_port_id {
+ __u32 asu32;
+ struct {
+ __u32 id:24;
+ __u32 reserved:8;
+ } u;
+};
+
+/* Define synthetic interrupt controller message header. */
+struct hv_message_header {
+ __u32 message_type;
+ __u8 payload_size;
+ union hv_message_flags message_flags;
+ __u8 reserved[2];
+ union {
+ __u64 sender;
+ union hv_port_id port;
+ };
+};
+
+/* Define synthetic interrupt controller message format. */
+struct hv_message {
+ struct hv_message_header header;
+ union {
+ __u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT];
+ } u;
+};
+
+/* Define the synthetic interrupt message page layout. */
+struct hv_message_page {
+ struct hv_message sint_message[HV_SYNIC_SINT_COUNT];
+};
+
+/* Define timer message payload structure. */
+struct hv_timer_message_payload {
+ __u32 timer_index;
+ __u32 reserved;
+ __u64 expiration_time; /* When the timer expired */
+ __u64 delivery_time; /* When the message was delivered */
+};
+
+#define HV_STIMER_ENABLE (1ULL << 0)
+#define HV_STIMER_PERIODIC (1ULL << 1)
+#define HV_STIMER_LAZY (1ULL << 2)
+#define HV_STIMER_AUTOENABLE (1ULL << 3)
+#define HV_STIMER_SINT(config) (__u8)(((config) >> 16) & 0x0F)
+
#endif
diff --git a/arch/x86/include/uapi/asm/mce.h b/arch/x86/include/uapi/asm/mce.h
index 03429da2fa80..2184943341bf 100644
--- a/arch/x86/include/uapi/asm/mce.h
+++ b/arch/x86/include/uapi/asm/mce.h
@@ -16,7 +16,7 @@ struct mce {
__u8 cpuvendor; /* cpu vendor as encoded in system.h */
__u8 inject_flags; /* software inject flags */
__u8 severity;
- __u8 usable_addr;
+ __u8 pad;
__u32 cpuid; /* CPUID 1 EAX */
__u8 cs; /* code segment */
__u8 bank; /* machine check bank */
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 2f69e3b184f6..8a5cddac7d44 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -82,6 +82,12 @@ physid_mask_t phys_cpu_present_map;
static unsigned int disabled_cpu_apicid __read_mostly = BAD_APICID;
/*
+ * This variable controls which CPUs receive external NMIs. By default,
+ * external NMIs are delivered only to the BSP.
+ */
+static int apic_extnmi = APIC_EXTNMI_BSP;
+
+/*
* Map cpu index to physical APIC ID
*/
DEFINE_EARLY_PER_CPU_READ_MOSTLY(u16, x86_cpu_to_apicid, BAD_APICID);
@@ -1161,6 +1167,8 @@ void __init init_bsp_APIC(void)
value = APIC_DM_NMI;
if (!lapic_is_integrated()) /* 82489DX */
value |= APIC_LVT_LEVEL_TRIGGER;
+ if (apic_extnmi == APIC_EXTNMI_NONE)
+ value |= APIC_LVT_MASKED;
apic_write(APIC_LVT1, value);
}
@@ -1378,9 +1386,11 @@ void setup_local_APIC(void)
apic_write(APIC_LVT0, value);
/*
- * only the BP should see the LINT1 NMI signal, obviously.
+ * Only the BSP sees the LINT1 NMI signal by default. This can be
+ * modified by apic_extnmi= boot option.
*/
- if (!cpu)
+ if ((!cpu && apic_extnmi != APIC_EXTNMI_NONE) ||
+ apic_extnmi == APIC_EXTNMI_ALL)
value = APIC_DM_NMI;
else
value = APIC_DM_NMI | APIC_LVT_MASKED;
@@ -2270,6 +2280,7 @@ static struct {
unsigned int apic_tmict;
unsigned int apic_tdcr;
unsigned int apic_thmr;
+ unsigned int apic_cmci;
} apic_pm_state;
static int lapic_suspend(void)
@@ -2299,6 +2310,10 @@ static int lapic_suspend(void)
if (maxlvt >= 5)
apic_pm_state.apic_thmr = apic_read(APIC_LVTTHMR);
#endif
+#ifdef CONFIG_X86_MCE_INTEL
+ if (maxlvt >= 6)
+ apic_pm_state.apic_cmci = apic_read(APIC_LVTCMCI);
+#endif
local_irq_save(flags);
disable_local_APIC();
@@ -2355,10 +2370,14 @@ static void lapic_resume(void)
apic_write(APIC_SPIV, apic_pm_state.apic_spiv);
apic_write(APIC_LVT0, apic_pm_state.apic_lvt0);
apic_write(APIC_LVT1, apic_pm_state.apic_lvt1);
-#if defined(CONFIG_X86_MCE_INTEL)
+#ifdef CONFIG_X86_THERMAL_VECTOR
if (maxlvt >= 5)
apic_write(APIC_LVTTHMR, apic_pm_state.apic_thmr);
#endif
+#ifdef CONFIG_X86_MCE_INTEL
+ if (maxlvt >= 6)
+ apic_write(APIC_LVTCMCI, apic_pm_state.apic_cmci);
+#endif
if (maxlvt >= 4)
apic_write(APIC_LVTPC, apic_pm_state.apic_lvtpc);
apic_write(APIC_LVTT, apic_pm_state.apic_lvtt);
@@ -2548,3 +2567,23 @@ static int __init apic_set_disabled_cpu_apicid(char *arg)
return 0;
}
early_param("disable_cpu_apicid", apic_set_disabled_cpu_apicid);
+
+static int __init apic_set_extnmi(char *arg)
+{
+ if (!arg)
+ return -EINVAL;
+
+ if (!strncmp("all", arg, 3))
+ apic_extnmi = APIC_EXTNMI_ALL;
+ else if (!strncmp("none", arg, 4))
+ apic_extnmi = APIC_EXTNMI_NONE;
+ else if (!strncmp("bsp", arg, 3))
+ apic_extnmi = APIC_EXTNMI_BSP;
+ else {
+ pr_warn("Unknown external NMI delivery mode `%s' ignored\n", arg);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+early_param("apic_extnmi", apic_set_extnmi);
diff --git a/arch/x86/kernel/apic/apic_flat_64.c b/arch/x86/kernel/apic/apic_flat_64.c
index f92ab36979a2..9968f30cca3e 100644
--- a/arch/x86/kernel/apic/apic_flat_64.c
+++ b/arch/x86/kernel/apic/apic_flat_64.c
@@ -185,6 +185,7 @@ static struct apic apic_flat = {
.cpu_mask_to_apicid_and = flat_cpu_mask_to_apicid_and,
+ .send_IPI = default_send_IPI_single,
.send_IPI_mask = flat_send_IPI_mask,
.send_IPI_mask_allbutself = flat_send_IPI_mask_allbutself,
.send_IPI_allbutself = flat_send_IPI_allbutself,
@@ -230,17 +231,6 @@ static int physflat_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
return 0;
}
-static void physflat_send_IPI_mask(const struct cpumask *cpumask, int vector)
-{
- default_send_IPI_mask_sequence_phys(cpumask, vector);
-}
-
-static void physflat_send_IPI_mask_allbutself(const struct cpumask *cpumask,
- int vector)
-{
- default_send_IPI_mask_allbutself_phys(cpumask, vector);
-}
-
static void physflat_send_IPI_allbutself(int vector)
{
default_send_IPI_mask_allbutself_phys(cpu_online_mask, vector);
@@ -248,7 +238,7 @@ static void physflat_send_IPI_allbutself(int vector)
static void physflat_send_IPI_all(int vector)
{
- physflat_send_IPI_mask(cpu_online_mask, vector);
+ default_send_IPI_mask_sequence_phys(cpu_online_mask, vector);
}
static int physflat_probe(void)
@@ -292,8 +282,9 @@ static struct apic apic_physflat = {
.cpu_mask_to_apicid_and = default_cpu_mask_to_apicid_and,
- .send_IPI_mask = physflat_send_IPI_mask,
- .send_IPI_mask_allbutself = physflat_send_IPI_mask_allbutself,
+ .send_IPI = default_send_IPI_single_phys,
+ .send_IPI_mask = default_send_IPI_mask_sequence_phys,
+ .send_IPI_mask_allbutself = default_send_IPI_mask_allbutself_phys,
.send_IPI_allbutself = physflat_send_IPI_allbutself,
.send_IPI_all = physflat_send_IPI_all,
.send_IPI_self = apic_send_IPI_self,
diff --git a/arch/x86/kernel/apic/apic_noop.c b/arch/x86/kernel/apic/apic_noop.c
index 0d96749cfcac..331a7a07c48f 100644
--- a/arch/x86/kernel/apic/apic_noop.c
+++ b/arch/x86/kernel/apic/apic_noop.c
@@ -30,6 +30,7 @@
#include <asm/e820.h>
static void noop_init_apic_ldr(void) { }
+static void noop_send_IPI(int cpu, int vector) { }
static void noop_send_IPI_mask(const struct cpumask *cpumask, int vector) { }
static void noop_send_IPI_mask_allbutself(const struct cpumask *cpumask, int vector) { }
static void noop_send_IPI_allbutself(int vector) { }
@@ -144,6 +145,7 @@ struct apic apic_noop = {
.cpu_mask_to_apicid_and = flat_cpu_mask_to_apicid_and,
+ .send_IPI = noop_send_IPI,
.send_IPI_mask = noop_send_IPI_mask,
.send_IPI_mask_allbutself = noop_send_IPI_mask_allbutself,
.send_IPI_allbutself = noop_send_IPI_allbutself,
diff --git a/arch/x86/kernel/apic/apic_numachip.c b/arch/x86/kernel/apic/apic_numachip.c
index 38dd5efdd04c..c80c02c6ec49 100644
--- a/arch/x86/kernel/apic/apic_numachip.c
+++ b/arch/x86/kernel/apic/apic_numachip.c
@@ -193,20 +193,17 @@ static int __init numachip_system_init(void)
case 1:
init_extra_mapping_uc(NUMACHIP_LCSR_BASE, NUMACHIP_LCSR_SIZE);
numachip_apic_icr_write = numachip1_apic_icr_write;
- x86_init.pci.arch_init = pci_numachip_init;
break;
case 2:
init_extra_mapping_uc(NUMACHIP2_LCSR_BASE, NUMACHIP2_LCSR_SIZE);
numachip_apic_icr_write = numachip2_apic_icr_write;
-
- /* Use MCFG config cycles rather than locked CF8 cycles */
- raw_pci_ops = &pci_mmcfg;
break;
default:
return 0;
}
x86_cpuinit.fixup_cpu_id = fixup_cpu_id;
+ x86_init.pci.arch_init = pci_numachip_init;
return 0;
}
@@ -276,6 +273,7 @@ static const struct apic apic_numachip1 __refconst = {
.cpu_mask_to_apicid_and = default_cpu_mask_to_apicid_and,
+ .send_IPI = numachip_send_IPI_one,
.send_IPI_mask = numachip_send_IPI_mask,
.send_IPI_mask_allbutself = numachip_send_IPI_mask_allbutself,
.send_IPI_allbutself = numachip_send_IPI_allbutself,
@@ -327,6 +325,7 @@ static const struct apic apic_numachip2 __refconst = {
.cpu_mask_to_apicid_and = default_cpu_mask_to_apicid_and,
+ .send_IPI = numachip_send_IPI_one,
.send_IPI_mask = numachip_send_IPI_mask,
.send_IPI_mask_allbutself = numachip_send_IPI_mask_allbutself,
.send_IPI_allbutself = numachip_send_IPI_allbutself,
diff --git a/arch/x86/kernel/apic/bigsmp_32.c b/arch/x86/kernel/apic/bigsmp_32.c
index 971cf8875939..cf9bd896c12d 100644
--- a/arch/x86/kernel/apic/bigsmp_32.c
+++ b/arch/x86/kernel/apic/bigsmp_32.c
@@ -96,11 +96,6 @@ static int bigsmp_phys_pkg_id(int cpuid_apic, int index_msb)
return cpuid_apic >> index_msb;
}
-static inline void bigsmp_send_IPI_mask(const struct cpumask *mask, int vector)
-{
- default_send_IPI_mask_sequence_phys(mask, vector);
-}
-
static void bigsmp_send_IPI_allbutself(int vector)
{
default_send_IPI_mask_allbutself_phys(cpu_online_mask, vector);
@@ -108,7 +103,7 @@ static void bigsmp_send_IPI_allbutself(int vector)
static void bigsmp_send_IPI_all(int vector)
{
- bigsmp_send_IPI_mask(cpu_online_mask, vector);
+ default_send_IPI_mask_sequence_phys(cpu_online_mask, vector);
}
static int dmi_bigsmp; /* can be set by dmi scanners */
@@ -180,7 +175,8 @@ static struct apic apic_bigsmp = {
.cpu_mask_to_apicid_and = default_cpu_mask_to_apicid_and,
- .send_IPI_mask = bigsmp_send_IPI_mask,
+ .send_IPI = default_send_IPI_single_phys,
+ .send_IPI_mask = default_send_IPI_mask_sequence_phys,
.send_IPI_mask_allbutself = NULL,
.send_IPI_allbutself = bigsmp_send_IPI_allbutself,
.send_IPI_all = bigsmp_send_IPI_all,
diff --git a/arch/x86/kernel/apic/ipi.c b/arch/x86/kernel/apic/ipi.c
index 62071569bd50..eb45fc9b6124 100644
--- a/arch/x86/kernel/apic/ipi.c
+++ b/arch/x86/kernel/apic/ipi.c
@@ -18,6 +18,16 @@
#include <asm/proto.h>
#include <asm/ipi.h>
+void default_send_IPI_single_phys(int cpu, int vector)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ __default_send_IPI_dest_field(per_cpu(x86_cpu_to_apicid, cpu),
+ vector, APIC_DEST_PHYSICAL);
+ local_irq_restore(flags);
+}
+
void default_send_IPI_mask_sequence_phys(const struct cpumask *mask, int vector)
{
unsigned long query_cpu;
@@ -55,6 +65,14 @@ void default_send_IPI_mask_allbutself_phys(const struct cpumask *mask,
local_irq_restore(flags);
}
+/*
+ * Helper function for APICs which insist on cpumasks
+ */
+void default_send_IPI_single(int cpu, int vector)
+{
+ apic->send_IPI_mask(cpumask_of(cpu), vector);
+}
+
#ifdef CONFIG_X86_32
void default_send_IPI_mask_sequence_logical(const struct cpumask *mask,
diff --git a/arch/x86/kernel/apic/msi.c b/arch/x86/kernel/apic/msi.c
index 5f1feb6854af..ade25320df96 100644
--- a/arch/x86/kernel/apic/msi.c
+++ b/arch/x86/kernel/apic/msi.c
@@ -96,8 +96,8 @@ static irq_hw_number_t pci_msi_get_hwirq(struct msi_domain_info *info,
return arg->msi_hwirq;
}
-static int pci_msi_prepare(struct irq_domain *domain, struct device *dev,
- int nvec, msi_alloc_info_t *arg)
+int pci_msi_prepare(struct irq_domain *domain, struct device *dev, int nvec,
+ msi_alloc_info_t *arg)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct msi_desc *desc = first_pci_msi_entry(pdev);
@@ -113,11 +113,13 @@ static int pci_msi_prepare(struct irq_domain *domain, struct device *dev,
return 0;
}
+EXPORT_SYMBOL_GPL(pci_msi_prepare);
-static void pci_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
+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);
}
+EXPORT_SYMBOL_GPL(pci_msi_set_desc);
static struct msi_domain_ops pci_msi_domain_ops = {
.get_hwirq = pci_msi_get_hwirq,
diff --git a/arch/x86/kernel/apic/probe_32.c b/arch/x86/kernel/apic/probe_32.c
index 7694ae6c1199..f316e34abb42 100644
--- a/arch/x86/kernel/apic/probe_32.c
+++ b/arch/x86/kernel/apic/probe_32.c
@@ -105,6 +105,7 @@ static struct apic apic_default = {
.cpu_mask_to_apicid_and = flat_cpu_mask_to_apicid_and,
+ .send_IPI = default_send_IPI_single,
.send_IPI_mask = default_send_IPI_mask_logical,
.send_IPI_mask_allbutself = default_send_IPI_mask_allbutself_logical,
.send_IPI_allbutself = default_send_IPI_allbutself,
diff --git a/arch/x86/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c
index 861bc59c8f25..908cb37da171 100644
--- a/arch/x86/kernel/apic/vector.c
+++ b/arch/x86/kernel/apic/vector.c
@@ -29,6 +29,7 @@ struct apic_chip_data {
};
struct irq_domain *x86_vector_domain;
+EXPORT_SYMBOL_GPL(x86_vector_domain);
static DEFINE_RAW_SPINLOCK(vector_lock);
static cpumask_var_t vector_cpumask;
static struct irq_chip lapic_controller;
@@ -66,6 +67,7 @@ struct irq_cfg *irqd_cfg(struct irq_data *irq_data)
return data ? &data->cfg : NULL;
}
+EXPORT_SYMBOL_GPL(irqd_cfg);
struct irq_cfg *irq_cfg(unsigned int irq)
{
diff --git a/arch/x86/kernel/apic/x2apic_cluster.c b/arch/x86/kernel/apic/x2apic_cluster.c
index cc8311c4d298..aca8b75c1552 100644
--- a/arch/x86/kernel/apic/x2apic_cluster.c
+++ b/arch/x86/kernel/apic/x2apic_cluster.c
@@ -23,6 +23,14 @@ static inline u32 x2apic_cluster(int cpu)
return per_cpu(x86_cpu_to_logical_apicid, cpu) >> 16;
}
+static void x2apic_send_IPI(int cpu, int vector)
+{
+ u32 dest = per_cpu(x86_cpu_to_logical_apicid, cpu);
+
+ x2apic_wrmsr_fence();
+ __x2apic_send_IPI_dest(dest, vector, APIC_DEST_LOGICAL);
+}
+
static void
__x2apic_send_IPI_mask(const struct cpumask *mask, int vector, int apic_dest)
{
@@ -266,6 +274,7 @@ static struct apic apic_x2apic_cluster = {
.cpu_mask_to_apicid_and = x2apic_cpu_mask_to_apicid_and,
+ .send_IPI = x2apic_send_IPI,
.send_IPI_mask = x2apic_send_IPI_mask,
.send_IPI_mask_allbutself = x2apic_send_IPI_mask_allbutself,
.send_IPI_allbutself = x2apic_send_IPI_allbutself,
diff --git a/arch/x86/kernel/apic/x2apic_phys.c b/arch/x86/kernel/apic/x2apic_phys.c
index 662e9150ea6f..a1242e2c12e6 100644
--- a/arch/x86/kernel/apic/x2apic_phys.c
+++ b/arch/x86/kernel/apic/x2apic_phys.c
@@ -36,6 +36,14 @@ static int x2apic_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
return x2apic_enabled() && (x2apic_phys || x2apic_fadt_phys());
}
+static void x2apic_send_IPI(int cpu, int vector)
+{
+ u32 dest = per_cpu(x86_cpu_to_apicid, cpu);
+
+ x2apic_wrmsr_fence();
+ __x2apic_send_IPI_dest(dest, vector, APIC_DEST_PHYSICAL);
+}
+
static void
__x2apic_send_IPI_mask(const struct cpumask *mask, int vector, int apic_dest)
{
@@ -122,6 +130,7 @@ static struct apic apic_x2apic_phys = {
.cpu_mask_to_apicid_and = default_cpu_mask_to_apicid_and,
+ .send_IPI = x2apic_send_IPI,
.send_IPI_mask = x2apic_send_IPI_mask,
.send_IPI_mask_allbutself = x2apic_send_IPI_mask_allbutself,
.send_IPI_allbutself = x2apic_send_IPI_allbutself,
diff --git a/arch/x86/kernel/apic/x2apic_uv_x.c b/arch/x86/kernel/apic/x2apic_uv_x.c
index 4a139465f1d4..d760c6bb37b5 100644
--- a/arch/x86/kernel/apic/x2apic_uv_x.c
+++ b/arch/x86/kernel/apic/x2apic_uv_x.c
@@ -406,6 +406,7 @@ static struct apic __refdata apic_x2apic_uv_x = {
.cpu_mask_to_apicid_and = uv_cpu_mask_to_apicid_and,
+ .send_IPI = uv_send_IPI_one,
.send_IPI_mask = uv_send_IPI_mask,
.send_IPI_mask_allbutself = uv_send_IPI_mask_allbutself,
.send_IPI_allbutself = uv_send_IPI_allbutself,
diff --git a/arch/x86/kernel/asm-offsets.c b/arch/x86/kernel/asm-offsets.c
index 439df975bc7a..84a7524b202c 100644
--- a/arch/x86/kernel/asm-offsets.c
+++ b/arch/x86/kernel/asm-offsets.c
@@ -65,9 +65,6 @@ 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
diff --git a/arch/x86/kernel/asm-offsets_64.c b/arch/x86/kernel/asm-offsets_64.c
index d8f42f902a0f..f2edafb5f24e 100644
--- a/arch/x86/kernel/asm-offsets_64.c
+++ b/arch/x86/kernel/asm-offsets_64.c
@@ -23,7 +23,6 @@ int main(void)
{
#ifdef CONFIG_PARAVIRT
OFFSET(PV_IRQ_adjust_exception_frame, pv_irq_ops, adjust_exception_frame);
- OFFSET(PV_CPU_usergs_sysret32, pv_cpu_ops, usergs_sysret32);
OFFSET(PV_CPU_usergs_sysret64, pv_cpu_ops, usergs_sysret64);
OFFSET(PV_CPU_swapgs, pv_cpu_ops, swapgs);
BLANK();
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index a8816b325162..a07956a08936 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -304,7 +304,7 @@ static void amd_get_topology(struct cpuinfo_x86 *c)
int cpu = smp_processor_id();
/* get information required for multi-node processors */
- if (cpu_has_topoext) {
+ if (boot_cpu_has(X86_FEATURE_TOPOEXT)) {
u32 eax, ebx, ecx, edx;
cpuid(0x8000001e, &eax, &ebx, &ecx, &edx);
@@ -434,8 +434,7 @@ static void srat_detect_node(struct cpuinfo_x86 *c)
*/
int ht_nodeid = c->initial_apicid;
- if (ht_nodeid >= 0 &&
- __apicid_to_node[ht_nodeid] != NUMA_NO_NODE)
+ if (__apicid_to_node[ht_nodeid] != NUMA_NO_NODE)
node = __apicid_to_node[ht_nodeid];
/* Pick a nearby node */
if (!node_online(node))
@@ -678,9 +677,9 @@ static void init_amd_bd(struct cpuinfo_x86 *c)
* Disable it on the affected CPUs.
*/
if ((c->x86_model >= 0x02) && (c->x86_model < 0x20)) {
- if (!rdmsrl_safe(0xc0011021, &value) && !(value & 0x1E)) {
+ if (!rdmsrl_safe(MSR_F15H_IC_CFG, &value) && !(value & 0x1E)) {
value |= 0x1E;
- wrmsrl_safe(0xc0011021, value);
+ wrmsrl_safe(MSR_F15H_IC_CFG, value);
}
}
}
@@ -922,7 +921,7 @@ static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum)
void set_dr_addr_mask(unsigned long mask, int dr)
{
- if (!cpu_has_bpext)
+ if (!boot_cpu_has(X86_FEATURE_BPEXT))
return;
switch (dr) {
diff --git a/arch/x86/kernel/cpu/centaur.c b/arch/x86/kernel/cpu/centaur.c
index d8fba5c15fbd..ae20be6e483c 100644
--- a/arch/x86/kernel/cpu/centaur.c
+++ b/arch/x86/kernel/cpu/centaur.c
@@ -43,7 +43,7 @@ static void init_c3(struct cpuinfo_x86 *c)
/* store Centaur Extended Feature Flags as
* word 5 of the CPU capability bit array
*/
- c->x86_capability[5] = cpuid_edx(0xC0000001);
+ c->x86_capability[CPUID_C000_0001_EDX] = cpuid_edx(0xC0000001);
}
#ifdef CONFIG_X86_32
/* Cyrix III family needs CX8 & PGE explicitly enabled. */
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index c2b7522cbf35..37830de8f60a 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -581,14 +581,9 @@ void cpu_detect(struct cpuinfo_x86 *c)
u32 junk, tfms, cap0, misc;
cpuid(0x00000001, &tfms, &misc, &junk, &cap0);
- c->x86 = (tfms >> 8) & 0xf;
- c->x86_model = (tfms >> 4) & 0xf;
- c->x86_mask = tfms & 0xf;
-
- if (c->x86 == 0xf)
- c->x86 += (tfms >> 20) & 0xff;
- if (c->x86 >= 0x6)
- c->x86_model += ((tfms >> 16) & 0xf) << 4;
+ c->x86 = x86_family(tfms);
+ c->x86_model = x86_model(tfms);
+ c->x86_mask = x86_stepping(tfms);
if (cap0 & (1<<19)) {
c->x86_clflush_size = ((misc >> 8) & 0xff) * 8;
@@ -599,50 +594,47 @@ void cpu_detect(struct cpuinfo_x86 *c)
void get_cpu_cap(struct cpuinfo_x86 *c)
{
- u32 tfms, xlvl;
- u32 ebx;
+ u32 eax, ebx, ecx, edx;
/* Intel-defined flags: level 0x00000001 */
if (c->cpuid_level >= 0x00000001) {
- u32 capability, excap;
+ cpuid(0x00000001, &eax, &ebx, &ecx, &edx);
- cpuid(0x00000001, &tfms, &ebx, &excap, &capability);
- c->x86_capability[0] = capability;
- c->x86_capability[4] = excap;
+ c->x86_capability[CPUID_1_ECX] = ecx;
+ c->x86_capability[CPUID_1_EDX] = edx;
}
/* Additional Intel-defined flags: level 0x00000007 */
if (c->cpuid_level >= 0x00000007) {
- u32 eax, ebx, ecx, edx;
-
cpuid_count(0x00000007, 0, &eax, &ebx, &ecx, &edx);
- c->x86_capability[9] = ebx;
+ c->x86_capability[CPUID_7_0_EBX] = ebx;
+
+ c->x86_capability[CPUID_6_EAX] = cpuid_eax(0x00000006);
}
/* Extended state features: level 0x0000000d */
if (c->cpuid_level >= 0x0000000d) {
- u32 eax, ebx, ecx, edx;
-
cpuid_count(0x0000000d, 1, &eax, &ebx, &ecx, &edx);
- c->x86_capability[10] = eax;
+ c->x86_capability[CPUID_D_1_EAX] = eax;
}
/* Additional Intel-defined flags: level 0x0000000F */
if (c->cpuid_level >= 0x0000000F) {
- u32 eax, ebx, ecx, edx;
/* QoS sub-leaf, EAX=0Fh, ECX=0 */
cpuid_count(0x0000000F, 0, &eax, &ebx, &ecx, &edx);
- c->x86_capability[11] = edx;
+ c->x86_capability[CPUID_F_0_EDX] = edx;
+
if (cpu_has(c, X86_FEATURE_CQM_LLC)) {
/* will be overridden if occupancy monitoring exists */
c->x86_cache_max_rmid = ebx;
/* QoS sub-leaf, EAX=0Fh, ECX=1 */
cpuid_count(0x0000000F, 1, &eax, &ebx, &ecx, &edx);
- c->x86_capability[12] = edx;
+ c->x86_capability[CPUID_F_1_EDX] = edx;
+
if (cpu_has(c, X86_FEATURE_CQM_OCCUP_LLC)) {
c->x86_cache_max_rmid = ecx;
c->x86_cache_occ_scale = ebx;
@@ -654,22 +646,24 @@ void get_cpu_cap(struct cpuinfo_x86 *c)
}
/* AMD-defined flags: level 0x80000001 */
- xlvl = cpuid_eax(0x80000000);
- c->extended_cpuid_level = xlvl;
+ eax = cpuid_eax(0x80000000);
+ c->extended_cpuid_level = eax;
- if ((xlvl & 0xffff0000) == 0x80000000) {
- if (xlvl >= 0x80000001) {
- c->x86_capability[1] = cpuid_edx(0x80000001);
- c->x86_capability[6] = cpuid_ecx(0x80000001);
+ if ((eax & 0xffff0000) == 0x80000000) {
+ if (eax >= 0x80000001) {
+ cpuid(0x80000001, &eax, &ebx, &ecx, &edx);
+
+ c->x86_capability[CPUID_8000_0001_ECX] = ecx;
+ c->x86_capability[CPUID_8000_0001_EDX] = edx;
}
}
if (c->extended_cpuid_level >= 0x80000008) {
- u32 eax = cpuid_eax(0x80000008);
+ cpuid(0x80000008, &eax, &ebx, &ecx, &edx);
c->x86_virt_bits = (eax >> 8) & 0xff;
c->x86_phys_bits = eax & 0xff;
- c->x86_capability[13] = cpuid_ebx(0x80000008);
+ c->x86_capability[CPUID_8000_0008_EBX] = ebx;
}
#ifdef CONFIG_X86_32
else if (cpu_has(c, X86_FEATURE_PAE) || cpu_has(c, X86_FEATURE_PSE36))
@@ -679,6 +673,9 @@ void get_cpu_cap(struct cpuinfo_x86 *c)
if (c->extended_cpuid_level >= 0x80000007)
c->x86_power = cpuid_edx(0x80000007);
+ if (c->extended_cpuid_level >= 0x8000000a)
+ c->x86_capability[CPUID_8000_000A_EDX] = cpuid_edx(0x8000000a);
+
init_scattered_cpuid_features(c);
}
@@ -1185,7 +1182,7 @@ void syscall_init(void)
* They both write to the same internal register. STAR allows to
* set CS/DS but only a 32bit target. LSTAR sets the 64bit rip.
*/
- wrmsrl(MSR_STAR, ((u64)__USER32_CS)<<48 | ((u64)__KERNEL_CS)<<32);
+ wrmsr(MSR_STAR, 0, (__USER32_CS << 16) | __KERNEL_CS);
wrmsrl(MSR_LSTAR, (unsigned long)entry_SYSCALL_64);
#ifdef CONFIG_IA32_EMULATION
@@ -1443,7 +1440,9 @@ void cpu_init(void)
printk(KERN_INFO "Initializing CPU#%d\n", cpu);
- if (cpu_feature_enabled(X86_FEATURE_VME) || cpu_has_tsc || cpu_has_de)
+ if (cpu_feature_enabled(X86_FEATURE_VME) ||
+ cpu_has_tsc ||
+ boot_cpu_has(X86_FEATURE_DE))
cr4_clear_bits(X86_CR4_VME|X86_CR4_PVI|X86_CR4_TSD|X86_CR4_DE);
load_current_idt();
diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index 209ac1e7d1f0..565648bc1a0a 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -445,7 +445,8 @@ static void init_intel(struct cpuinfo_x86 *c)
if (cpu_has_xmm2)
set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC);
- if (cpu_has_ds) {
+
+ if (boot_cpu_has(X86_FEATURE_DS)) {
unsigned int l1;
rdmsr(MSR_IA32_MISC_ENABLE, l1, l2);
if (!(l1 & (1<<11)))
diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c
index e38d338a6447..0b6c52388cf4 100644
--- a/arch/x86/kernel/cpu/intel_cacheinfo.c
+++ b/arch/x86/kernel/cpu/intel_cacheinfo.c
@@ -591,7 +591,7 @@ cpuid4_cache_lookup_regs(int index, struct _cpuid4_info_regs *this_leaf)
unsigned edx;
if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) {
- if (cpu_has_topoext)
+ if (boot_cpu_has(X86_FEATURE_TOPOEXT))
cpuid_count(0x8000001d, index, &eax.full,
&ebx.full, &ecx.full, &edx);
else
@@ -637,7 +637,7 @@ static int find_num_cache_leaves(struct cpuinfo_x86 *c)
void init_amd_cacheinfo(struct cpuinfo_x86 *c)
{
- if (cpu_has_topoext) {
+ if (boot_cpu_has(X86_FEATURE_TOPOEXT)) {
num_cache_leaves = find_num_cache_leaves(c);
} else if (c->extended_cpuid_level >= 0x80000006) {
if (cpuid_edx(0x80000006) & 0xf000)
@@ -809,7 +809,7 @@ static int __cache_amd_cpumap_setup(unsigned int cpu, int index,
struct cacheinfo *this_leaf;
int i, sibling;
- if (cpu_has_topoext) {
+ if (boot_cpu_has(X86_FEATURE_TOPOEXT)) {
unsigned int apicid, nshared, first, last;
this_leaf = this_cpu_ci->info_list + index;
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c
index c5b0d562dbf5..a006f4cd792b 100644
--- a/arch/x86/kernel/cpu/mcheck/mce.c
+++ b/arch/x86/kernel/cpu/mcheck/mce.c
@@ -114,7 +114,6 @@ static struct work_struct mce_work;
static struct irq_work mce_irq_work;
static void (*quirk_no_way_out)(int bank, struct mce *m, struct pt_regs *regs);
-static int mce_usable_address(struct mce *m);
/*
* CPU/chipset specific EDAC code can register a notifier call here to print
@@ -475,6 +474,28 @@ static void mce_report_event(struct pt_regs *regs)
irq_work_queue(&mce_irq_work);
}
+/*
+ * Check if the address reported by the CPU is in a format we can parse.
+ * It would be possible to add code for most other cases, but all would
+ * be somewhat complicated (e.g. segment offset would require an instruction
+ * parser). So only support physical addresses up to page granuality for now.
+ */
+static int mce_usable_address(struct mce *m)
+{
+ if (!(m->status & MCI_STATUS_MISCV) || !(m->status & MCI_STATUS_ADDRV))
+ return 0;
+
+ /* Checks after this one are Intel-specific: */
+ if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL)
+ return 1;
+
+ if (MCI_MISC_ADDR_LSB(m->misc) > PAGE_SHIFT)
+ return 0;
+ if (MCI_MISC_ADDR_MODE(m->misc) != MCI_MISC_ADDR_PHYS)
+ return 0;
+ return 1;
+}
+
static int srao_decode_notifier(struct notifier_block *nb, unsigned long val,
void *data)
{
@@ -484,7 +505,7 @@ static int srao_decode_notifier(struct notifier_block *nb, unsigned long val,
if (!mce)
return NOTIFY_DONE;
- if (mce->usable_addr && (mce->severity == MCE_AO_SEVERITY)) {
+ if (mce_usable_address(mce) && (mce->severity == MCE_AO_SEVERITY)) {
pfn = mce->addr >> PAGE_SHIFT;
memory_failure(pfn, MCE_VECTOR, 0);
}
@@ -522,10 +543,10 @@ static bool memory_error(struct mce *m)
struct cpuinfo_x86 *c = &boot_cpu_data;
if (c->x86_vendor == X86_VENDOR_AMD) {
- /*
- * coming soon
- */
- return false;
+ /* ErrCodeExt[20:16] */
+ u8 xec = (m->status >> 16) & 0x1f;
+
+ return (xec == 0x0 || xec == 0x8);
} else if (c->x86_vendor == X86_VENDOR_INTEL) {
/*
* Intel SDM Volume 3B - 15.9.2 Compound Error Codes
@@ -567,7 +588,7 @@ DEFINE_PER_CPU(unsigned, mce_poll_count);
*/
bool machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
{
- bool error_logged = false;
+ bool error_seen = false;
struct mce m;
int severity;
int i;
@@ -601,6 +622,8 @@ bool machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
(m.status & (mca_cfg.ser ? MCI_STATUS_S : MCI_STATUS_UC)))
continue;
+ error_seen = true;
+
mce_read_aux(&m, i);
if (!(flags & MCP_TIMESTAMP))
@@ -608,27 +631,24 @@ bool machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
severity = mce_severity(&m, mca_cfg.tolerant, NULL, false);
- /*
- * In the cases where we don't have a valid address after all,
- * do not add it into the ring buffer.
- */
- if (severity == MCE_DEFERRED_SEVERITY && memory_error(&m)) {
- if (m.status & MCI_STATUS_ADDRV) {
+ if (severity == MCE_DEFERRED_SEVERITY && memory_error(&m))
+ if (m.status & MCI_STATUS_ADDRV)
m.severity = severity;
- m.usable_addr = mce_usable_address(&m);
-
- if (!mce_gen_pool_add(&m))
- mce_schedule_work();
- }
- }
/*
* Don't get the IP here because it's unlikely to
* have anything to do with the actual error location.
*/
- if (!(flags & MCP_DONTLOG) && !mca_cfg.dont_log_ce) {
- error_logged = true;
+ if (!(flags & MCP_DONTLOG) && !mca_cfg.dont_log_ce)
mce_log(&m);
+ else if (mce_usable_address(&m)) {
+ /*
+ * Although we skipped logging this, we still want
+ * to take action. Add to the pool so the registered
+ * notifiers will see it.
+ */
+ if (!mce_gen_pool_add(&m))
+ mce_schedule_work();
}
/*
@@ -644,7 +664,7 @@ bool machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
sync_core();
- return error_logged;
+ return error_seen;
}
EXPORT_SYMBOL_GPL(machine_check_poll);
@@ -931,23 +951,6 @@ reset:
return ret;
}
-/*
- * Check if the address reported by the CPU is in a format we can parse.
- * It would be possible to add code for most other cases, but all would
- * be somewhat complicated (e.g. segment offset would require an instruction
- * parser). So only support physical addresses up to page granuality for now.
- */
-static int mce_usable_address(struct mce *m)
-{
- if (!(m->status & MCI_STATUS_MISCV) || !(m->status & MCI_STATUS_ADDRV))
- return 0;
- if (MCI_MISC_ADDR_LSB(m->misc) > PAGE_SHIFT)
- return 0;
- if (MCI_MISC_ADDR_MODE(m->misc) != MCI_MISC_ADDR_PHYS)
- return 0;
- return 1;
-}
-
static void mce_clear_state(unsigned long *toclear)
{
int i;
@@ -999,6 +1002,17 @@ void do_machine_check(struct pt_regs *regs, long error_code)
int flags = MF_ACTION_REQUIRED;
int lmce = 0;
+ /* If this CPU is offline, just bail out. */
+ if (cpu_is_offline(smp_processor_id())) {
+ u64 mcgstatus;
+
+ mcgstatus = mce_rdmsrl(MSR_IA32_MCG_STATUS);
+ if (mcgstatus & MCG_STATUS_RIPV) {
+ mce_wrmsrl(MSR_IA32_MCG_STATUS, 0);
+ return;
+ }
+ }
+
ist_enter(regs);
this_cpu_inc(mce_exception_count);
@@ -1089,7 +1103,6 @@ void do_machine_check(struct pt_regs *regs, long error_code)
/* assuming valid severity level != 0 */
m.severity = severity;
- m.usable_addr = mce_usable_address(&m);
mce_log(&m);
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
index 7fc27f1cca58..faec7120c508 100644
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -129,8 +129,8 @@ void __init load_ucode_bsp(void)
if (!have_cpuid_p())
return;
- vendor = x86_vendor();
- family = x86_family();
+ vendor = x86_cpuid_vendor();
+ family = x86_cpuid_family();
switch (vendor) {
case X86_VENDOR_INTEL:
@@ -165,8 +165,8 @@ void load_ucode_ap(void)
if (!have_cpuid_p())
return;
- vendor = x86_vendor();
- family = x86_family();
+ vendor = x86_cpuid_vendor();
+ family = x86_cpuid_family();
switch (vendor) {
case X86_VENDOR_INTEL:
@@ -206,8 +206,8 @@ void reload_early_microcode(void)
{
int vendor, family;
- vendor = x86_vendor();
- family = x86_family();
+ vendor = x86_cpuid_vendor();
+ family = x86_cpuid_family();
switch (vendor) {
case X86_VENDOR_INTEL:
@@ -698,3 +698,4 @@ int __init microcode_init(void)
return error;
}
+late_initcall(microcode_init);
diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c
index ce47402eb2f9..ee81c544ee0d 100644
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -145,10 +145,10 @@ matching_model_microcode(struct microcode_header_intel *mc_header,
int ext_sigcount, i;
struct extended_signature *ext_sig;
- fam = __x86_family(sig);
+ fam = x86_family(sig);
model = x86_model(sig);
- fam_ucode = __x86_family(mc_header->sig);
+ fam_ucode = x86_family(mc_header->sig);
model_ucode = x86_model(mc_header->sig);
if (fam == fam_ucode && model == model_ucode)
@@ -163,7 +163,7 @@ matching_model_microcode(struct microcode_header_intel *mc_header,
ext_sigcount = ext_header->count;
for (i = 0; i < ext_sigcount; i++) {
- fam_ucode = __x86_family(ext_sig->sig);
+ fam_ucode = x86_family(ext_sig->sig);
model_ucode = x86_model(ext_sig->sig);
if (fam == fam_ucode && model == model_ucode)
@@ -365,7 +365,7 @@ static int collect_cpu_info_early(struct ucode_cpu_info *uci)
native_cpuid(&eax, &ebx, &ecx, &edx);
csig.sig = eax;
- family = __x86_family(csig.sig);
+ family = x86_family(csig.sig);
model = x86_model(csig.sig);
if ((model >= 5) || (family > 6)) {
@@ -521,16 +521,12 @@ 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);
+ sprintf(name, "intel-ucode/%02x-%02x-%02x",
+ x86_family(eax), x86_model(eax), x86_stepping(eax));
return get_builtin_firmware(cp, name);
#else
diff --git a/arch/x86/kernel/cpu/mtrr/cleanup.c b/arch/x86/kernel/cpu/mtrr/cleanup.c
index 70d7c93f4550..0d98503c2245 100644
--- a/arch/x86/kernel/cpu/mtrr/cleanup.c
+++ b/arch/x86/kernel/cpu/mtrr/cleanup.c
@@ -593,9 +593,16 @@ mtrr_calc_range_state(u64 chunk_size, u64 gran_size,
unsigned long x_remove_base,
unsigned long x_remove_size, int i)
{
- static struct range range_new[RANGE_NUM];
+ /*
+ * range_new should really be an automatic variable, but
+ * putting 4096 bytes on the stack is frowned upon, to put it
+ * mildly. It is safe to make it a static __initdata variable,
+ * since mtrr_calc_range_state is only called during init and
+ * there's no way it will call itself recursively.
+ */
+ static struct range range_new[RANGE_NUM] __initdata;
unsigned long range_sums_new;
- static int nr_range_new;
+ int nr_range_new;
int num_reg;
/* Convert ranges to var ranges state: */
diff --git a/arch/x86/kernel/cpu/mtrr/generic.c b/arch/x86/kernel/cpu/mtrr/generic.c
index 3b533cf37c74..c870af161008 100644
--- a/arch/x86/kernel/cpu/mtrr/generic.c
+++ b/arch/x86/kernel/cpu/mtrr/generic.c
@@ -349,7 +349,7 @@ static void get_fixed_ranges(mtrr_type *frs)
void mtrr_save_fixed_ranges(void *info)
{
- if (cpu_has_mtrr)
+ if (boot_cpu_has(X86_FEATURE_MTRR))
get_fixed_ranges(mtrr_state.fixed_ranges);
}
diff --git a/arch/x86/kernel/cpu/mtrr/main.c b/arch/x86/kernel/cpu/mtrr/main.c
index f891b4750f04..5c3d149ee91c 100644
--- a/arch/x86/kernel/cpu/mtrr/main.c
+++ b/arch/x86/kernel/cpu/mtrr/main.c
@@ -682,7 +682,7 @@ void __init mtrr_bp_init(void)
phys_addr = 32;
- if (cpu_has_mtrr) {
+ if (boot_cpu_has(X86_FEATURE_MTRR)) {
mtrr_if = &generic_mtrr_ops;
size_or_mask = SIZE_OR_MASK_BITS(36);
size_and_mask = 0x00f00000;
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c
index 4562cf070c27..1b443db2db50 100644
--- a/arch/x86/kernel/cpu/perf_event.c
+++ b/arch/x86/kernel/cpu/perf_event.c
@@ -5,7 +5,7 @@
* Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar
* Copyright (C) 2009 Jaswinder Singh Rajput
* Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter
- * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra
* Copyright (C) 2009 Intel Corporation, <markus.t.metzger@intel.com>
* Copyright (C) 2009 Google, Inc., Stephane Eranian
*
@@ -482,6 +482,9 @@ int x86_pmu_hw_config(struct perf_event *event)
/* Support for IP fixup */
if (x86_pmu.lbr_nr || x86_pmu.intel_cap.pebs_format >= 2)
precise++;
+
+ if (x86_pmu.pebs_prec_dist)
+ precise++;
}
if (event->attr.precise_ip > precise)
@@ -1531,6 +1534,7 @@ static void __init filter_events(struct attribute **attrs)
{
struct device_attribute *d;
struct perf_pmu_events_attr *pmu_attr;
+ int offset = 0;
int i, j;
for (i = 0; attrs[i]; i++) {
@@ -1539,7 +1543,7 @@ static void __init filter_events(struct attribute **attrs)
/* str trumps id */
if (pmu_attr->event_str)
continue;
- if (x86_pmu.event_map(i))
+ if (x86_pmu.event_map(i + offset))
continue;
for (j = i; attrs[j]; j++)
@@ -1547,6 +1551,14 @@ static void __init filter_events(struct attribute **attrs)
/* Check the shifted attr. */
i--;
+
+ /*
+ * event_map() is index based, the attrs array is organized
+ * by increasing event index. If we shift the events, then
+ * we need to compensate for the event_map(), otherwise
+ * we are looking up the wrong event in the map
+ */
+ offset++;
}
}
@@ -2250,12 +2262,19 @@ perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry *entry)
ss_base = get_segment_base(regs->ss);
fp = compat_ptr(ss_base + regs->bp);
+ pagefault_disable();
while (entry->nr < PERF_MAX_STACK_DEPTH) {
unsigned long bytes;
frame.next_frame = 0;
frame.return_address = 0;
- bytes = copy_from_user_nmi(&frame, fp, sizeof(frame));
+ if (!access_ok(VERIFY_READ, fp, 8))
+ break;
+
+ bytes = __copy_from_user_nmi(&frame.next_frame, fp, 4);
+ if (bytes != 0)
+ break;
+ bytes = __copy_from_user_nmi(&frame.return_address, fp+4, 4);
if (bytes != 0)
break;
@@ -2265,6 +2284,7 @@ perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry *entry)
perf_callchain_store(entry, cs_base + frame.return_address);
fp = compat_ptr(ss_base + frame.next_frame);
}
+ pagefault_enable();
return 1;
}
#else
@@ -2302,12 +2322,19 @@ perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs)
if (perf_callchain_user32(regs, entry))
return;
+ pagefault_disable();
while (entry->nr < PERF_MAX_STACK_DEPTH) {
unsigned long bytes;
frame.next_frame = NULL;
frame.return_address = 0;
- bytes = copy_from_user_nmi(&frame, fp, sizeof(frame));
+ if (!access_ok(VERIFY_READ, fp, 16))
+ break;
+
+ bytes = __copy_from_user_nmi(&frame.next_frame, fp, 8);
+ if (bytes != 0)
+ break;
+ bytes = __copy_from_user_nmi(&frame.return_address, fp+8, 8);
if (bytes != 0)
break;
@@ -2315,8 +2342,9 @@ perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs)
break;
perf_callchain_store(entry, frame.return_address);
- fp = frame.next_frame;
+ fp = (void __user *)frame.next_frame;
}
+ pagefault_enable();
}
/*
diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h
index 499f533dd3cc..7bb61e32fb29 100644
--- a/arch/x86/kernel/cpu/perf_event.h
+++ b/arch/x86/kernel/cpu/perf_event.h
@@ -5,7 +5,7 @@
* Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar
* Copyright (C) 2009 Jaswinder Singh Rajput
* Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter
- * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra
* Copyright (C) 2009 Intel Corporation, <markus.t.metzger@intel.com>
* Copyright (C) 2009 Google, Inc., Stephane Eranian
*
@@ -14,17 +14,7 @@
#include <linux/perf_event.h>
-#if 0
-#undef wrmsrl
-#define wrmsrl(msr, val) \
-do { \
- unsigned int _msr = (msr); \
- u64 _val = (val); \
- trace_printk("wrmsrl(%x, %Lx)\n", (unsigned int)(_msr), \
- (unsigned long long)(_val)); \
- native_write_msr((_msr), (u32)(_val), (u32)(_val >> 32)); \
-} while (0)
-#endif
+/* To enable MSR tracing please use the generic trace points. */
/*
* | NHM/WSM | SNB |
@@ -318,6 +308,10 @@ struct cpu_hw_events {
#define INTEL_UEVENT_CONSTRAINT(c, n) \
EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK)
+/* Constraint on specific umask bit only + event */
+#define INTEL_UBIT_EVENT_CONSTRAINT(c, n) \
+ EVENT_CONSTRAINT(c, n, ARCH_PERFMON_EVENTSEL_EVENT|(c))
+
/* Like UEVENT_CONSTRAINT, but match flags too */
#define INTEL_FLAGS_UEVENT_CONSTRAINT(c, n) \
EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS)
@@ -387,7 +381,7 @@ struct cpu_hw_events {
/* Check flags and event code/umask, and set the HSW N/A flag */
#define INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_NA(code, n) \
__EVENT_CONSTRAINT(code, n, \
- INTEL_ARCH_EVENT_MASK|INTEL_ARCH_EVENT_MASK, \
+ INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \
HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_NA_HSW)
@@ -589,7 +583,8 @@ struct x86_pmu {
bts_active :1,
pebs :1,
pebs_active :1,
- pebs_broken :1;
+ pebs_broken :1,
+ pebs_prec_dist :1;
int pebs_record_size;
void (*drain_pebs)(struct pt_regs *regs);
struct event_constraint *pebs_constraints;
@@ -627,6 +622,7 @@ struct x86_perf_task_context {
u64 lbr_from[MAX_LBR_ENTRIES];
u64 lbr_to[MAX_LBR_ENTRIES];
u64 lbr_info[MAX_LBR_ENTRIES];
+ int tos;
int lbr_callstack_users;
int lbr_stack_state;
};
@@ -906,6 +902,8 @@ void intel_pmu_lbr_init_hsw(void);
void intel_pmu_lbr_init_skl(void);
+void intel_pmu_lbr_init_knl(void);
+
int intel_pmu_setup_lbr_filter(struct perf_event *event);
void intel_pt_interrupt(void);
diff --git a/arch/x86/kernel/cpu/perf_event_amd.c b/arch/x86/kernel/cpu/perf_event_amd.c
index 1cee5d2d7ece..58610539b048 100644
--- a/arch/x86/kernel/cpu/perf_event_amd.c
+++ b/arch/x86/kernel/cpu/perf_event_amd.c
@@ -18,7 +18,7 @@ static __initconst const u64 amd_hw_cache_event_ids
[ C(RESULT_MISS) ] = 0x0141, /* Data Cache Misses */
},
[ C(OP_WRITE) ] = {
- [ C(RESULT_ACCESS) ] = 0x0142, /* Data Cache Refills :system */
+ [ C(RESULT_ACCESS) ] = 0,
[ C(RESULT_MISS) ] = 0,
},
[ C(OP_PREFETCH) ] = {
@@ -160,7 +160,7 @@ static inline int amd_pmu_addr_offset(int index, bool eventsel)
if (offset)
return offset;
- if (!cpu_has_perfctr_core)
+ if (!boot_cpu_has(X86_FEATURE_PERFCTR_CORE))
offset = index;
else
offset = index << 1;
@@ -652,7 +652,7 @@ static __initconst const struct x86_pmu amd_pmu = {
static int __init amd_core_pmu_init(void)
{
- if (!cpu_has_perfctr_core)
+ if (!boot_cpu_has(X86_FEATURE_PERFCTR_CORE))
return 0;
switch (boot_cpu_data.x86) {
diff --git a/arch/x86/kernel/cpu/perf_event_amd_uncore.c b/arch/x86/kernel/cpu/perf_event_amd_uncore.c
index cc6cedb8f25d..49742746a6c9 100644
--- a/arch/x86/kernel/cpu/perf_event_amd_uncore.c
+++ b/arch/x86/kernel/cpu/perf_event_amd_uncore.c
@@ -523,10 +523,10 @@ static int __init amd_uncore_init(void)
if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
goto fail_nodev;
- if (!cpu_has_topoext)
+ if (!boot_cpu_has(X86_FEATURE_TOPOEXT))
goto fail_nodev;
- if (cpu_has_perfctr_nb) {
+ if (boot_cpu_has(X86_FEATURE_PERFCTR_NB)) {
amd_uncore_nb = alloc_percpu(struct amd_uncore *);
if (!amd_uncore_nb) {
ret = -ENOMEM;
@@ -540,7 +540,7 @@ static int __init amd_uncore_init(void)
ret = 0;
}
- if (cpu_has_perfctr_l2) {
+ if (boot_cpu_has(X86_FEATURE_PERFCTR_L2)) {
amd_uncore_l2 = alloc_percpu(struct amd_uncore *);
if (!amd_uncore_l2) {
ret = -ENOMEM;
@@ -583,10 +583,11 @@ fail_online:
/* amd_uncore_nb/l2 should have been freed by cleanup_cpu_online */
amd_uncore_nb = amd_uncore_l2 = NULL;
- if (cpu_has_perfctr_l2)
+
+ if (boot_cpu_has(X86_FEATURE_PERFCTR_L2))
perf_pmu_unregister(&amd_l2_pmu);
fail_l2:
- if (cpu_has_perfctr_nb)
+ if (boot_cpu_has(X86_FEATURE_PERFCTR_NB))
perf_pmu_unregister(&amd_nb_pmu);
if (amd_uncore_l2)
free_percpu(amd_uncore_l2);
diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c
index f63360be2238..a667078a5180 100644
--- a/arch/x86/kernel/cpu/perf_event_intel.c
+++ b/arch/x86/kernel/cpu/perf_event_intel.c
@@ -185,6 +185,14 @@ struct event_constraint intel_skl_event_constraints[] = {
EVENT_CONSTRAINT_END
};
+static struct extra_reg intel_knl_extra_regs[] __read_mostly = {
+ INTEL_UEVENT_EXTRA_REG(0x01b7,
+ MSR_OFFCORE_RSP_0, 0x7f9ffbffffull, RSP_0),
+ INTEL_UEVENT_EXTRA_REG(0x02b7,
+ MSR_OFFCORE_RSP_1, 0x3f9ffbffffull, RSP_1),
+ EVENT_EXTRA_END
+};
+
static struct extra_reg intel_snb_extra_regs[] __read_mostly = {
/* must define OFFCORE_RSP_X first, see intel_fixup_er() */
INTEL_UEVENT_EXTRA_REG(0x01b7, MSR_OFFCORE_RSP_0, 0x3f807f8fffull, RSP_0),
@@ -232,7 +240,7 @@ static struct event_constraint intel_hsw_event_constraints[] = {
FIXED_EVENT_CONSTRAINT(0x00c0, 0), /* INST_RETIRED.ANY */
FIXED_EVENT_CONSTRAINT(0x003c, 1), /* CPU_CLK_UNHALTED.CORE */
FIXED_EVENT_CONSTRAINT(0x0300, 2), /* CPU_CLK_UNHALTED.REF */
- INTEL_EVENT_CONSTRAINT(0x48, 0x4), /* L1D_PEND_MISS.* */
+ INTEL_UEVENT_CONSTRAINT(0x148, 0x4), /* L1D_PEND_MISS.PENDING */
INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PREC_DIST */
INTEL_EVENT_CONSTRAINT(0xcd, 0x8), /* MEM_TRANS_RETIRED.LOAD_LATENCY */
/* CYCLE_ACTIVITY.CYCLES_L1D_PENDING */
@@ -255,7 +263,7 @@ struct event_constraint intel_bdw_event_constraints[] = {
FIXED_EVENT_CONSTRAINT(0x003c, 1), /* CPU_CLK_UNHALTED.CORE */
FIXED_EVENT_CONSTRAINT(0x0300, 2), /* CPU_CLK_UNHALTED.REF */
INTEL_UEVENT_CONSTRAINT(0x148, 0x4), /* L1D_PEND_MISS.PENDING */
- INTEL_UEVENT_CONSTRAINT(0x8a3, 0x4), /* CYCLE_ACTIVITY.CYCLES_L1D_MISS */
+ INTEL_UBIT_EVENT_CONSTRAINT(0x8a3, 0x4), /* CYCLE_ACTIVITY.CYCLES_L1D_MISS */
EVENT_CONSTRAINT_END
};
@@ -1457,6 +1465,42 @@ static __initconst const u64 slm_hw_cache_event_ids
},
};
+#define KNL_OT_L2_HITE BIT_ULL(19) /* Other Tile L2 Hit */
+#define KNL_OT_L2_HITF BIT_ULL(20) /* Other Tile L2 Hit */
+#define KNL_MCDRAM_LOCAL BIT_ULL(21)
+#define KNL_MCDRAM_FAR BIT_ULL(22)
+#define KNL_DDR_LOCAL BIT_ULL(23)
+#define KNL_DDR_FAR BIT_ULL(24)
+#define KNL_DRAM_ANY (KNL_MCDRAM_LOCAL | KNL_MCDRAM_FAR | \
+ KNL_DDR_LOCAL | KNL_DDR_FAR)
+#define KNL_L2_READ SLM_DMND_READ
+#define KNL_L2_WRITE SLM_DMND_WRITE
+#define KNL_L2_PREFETCH SLM_DMND_PREFETCH
+#define KNL_L2_ACCESS SLM_LLC_ACCESS
+#define KNL_L2_MISS (KNL_OT_L2_HITE | KNL_OT_L2_HITF | \
+ KNL_DRAM_ANY | SNB_SNP_ANY | \
+ SNB_NON_DRAM)
+
+static __initconst const u64 knl_hw_cache_extra_regs
+ [PERF_COUNT_HW_CACHE_MAX]
+ [PERF_COUNT_HW_CACHE_OP_MAX]
+ [PERF_COUNT_HW_CACHE_RESULT_MAX] = {
+ [C(LL)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = KNL_L2_READ | KNL_L2_ACCESS,
+ [C(RESULT_MISS)] = 0,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = KNL_L2_WRITE | KNL_L2_ACCESS,
+ [C(RESULT_MISS)] = KNL_L2_WRITE | KNL_L2_MISS,
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = KNL_L2_PREFETCH | KNL_L2_ACCESS,
+ [C(RESULT_MISS)] = KNL_L2_PREFETCH | KNL_L2_MISS,
+ },
+ },
+};
+
/*
* Use from PMIs where the LBRs are already disabled.
*/
@@ -2475,6 +2519,44 @@ static void intel_pebs_aliases_snb(struct perf_event *event)
}
}
+static void intel_pebs_aliases_precdist(struct perf_event *event)
+{
+ if ((event->hw.config & X86_RAW_EVENT_MASK) == 0x003c) {
+ /*
+ * Use an alternative encoding for CPU_CLK_UNHALTED.THREAD_P
+ * (0x003c) so that we can use it with PEBS.
+ *
+ * The regular CPU_CLK_UNHALTED.THREAD_P event (0x003c) isn't
+ * PEBS capable. However we can use INST_RETIRED.PREC_DIST
+ * (0x01c0), which is a PEBS capable event, to get the same
+ * count.
+ *
+ * The PREC_DIST event has special support to minimize sample
+ * shadowing effects. One drawback is that it can be
+ * only programmed on counter 1, but that seems like an
+ * acceptable trade off.
+ */
+ u64 alt_config = X86_CONFIG(.event=0xc0, .umask=0x01, .inv=1, .cmask=16);
+
+ alt_config |= (event->hw.config & ~X86_RAW_EVENT_MASK);
+ event->hw.config = alt_config;
+ }
+}
+
+static void intel_pebs_aliases_ivb(struct perf_event *event)
+{
+ if (event->attr.precise_ip < 3)
+ return intel_pebs_aliases_snb(event);
+ return intel_pebs_aliases_precdist(event);
+}
+
+static void intel_pebs_aliases_skl(struct perf_event *event)
+{
+ if (event->attr.precise_ip < 3)
+ return intel_pebs_aliases_core2(event);
+ return intel_pebs_aliases_precdist(event);
+}
+
static unsigned long intel_pmu_free_running_flags(struct perf_event *event)
{
unsigned long flags = x86_pmu.free_running_flags;
@@ -3332,6 +3414,7 @@ __init int intel_pmu_init(void)
x86_pmu.event_constraints = intel_gen_event_constraints;
x86_pmu.pebs_constraints = intel_atom_pebs_event_constraints;
+ x86_pmu.pebs_aliases = intel_pebs_aliases_core2;
pr_cont("Atom events, ");
break;
@@ -3431,7 +3514,8 @@ __init int intel_pmu_init(void)
x86_pmu.event_constraints = intel_ivb_event_constraints;
x86_pmu.pebs_constraints = intel_ivb_pebs_event_constraints;
- x86_pmu.pebs_aliases = intel_pebs_aliases_snb;
+ x86_pmu.pebs_aliases = intel_pebs_aliases_ivb;
+ x86_pmu.pebs_prec_dist = true;
if (boot_cpu_data.x86_model == 62)
x86_pmu.extra_regs = intel_snbep_extra_regs;
else
@@ -3464,7 +3548,8 @@ __init int intel_pmu_init(void)
x86_pmu.event_constraints = intel_hsw_event_constraints;
x86_pmu.pebs_constraints = intel_hsw_pebs_event_constraints;
x86_pmu.extra_regs = intel_snbep_extra_regs;
- x86_pmu.pebs_aliases = intel_pebs_aliases_snb;
+ x86_pmu.pebs_aliases = intel_pebs_aliases_ivb;
+ x86_pmu.pebs_prec_dist = true;
/* all extra regs are per-cpu when HT is on */
x86_pmu.flags |= PMU_FL_HAS_RSP_1;
x86_pmu.flags |= PMU_FL_NO_HT_SHARING;
@@ -3499,7 +3584,8 @@ __init int intel_pmu_init(void)
x86_pmu.event_constraints = intel_bdw_event_constraints;
x86_pmu.pebs_constraints = intel_hsw_pebs_event_constraints;
x86_pmu.extra_regs = intel_snbep_extra_regs;
- x86_pmu.pebs_aliases = intel_pebs_aliases_snb;
+ x86_pmu.pebs_aliases = intel_pebs_aliases_ivb;
+ x86_pmu.pebs_prec_dist = true;
/* all extra regs are per-cpu when HT is on */
x86_pmu.flags |= PMU_FL_HAS_RSP_1;
x86_pmu.flags |= PMU_FL_NO_HT_SHARING;
@@ -3511,6 +3597,24 @@ __init int intel_pmu_init(void)
pr_cont("Broadwell events, ");
break;
+ case 87: /* Knights Landing Xeon Phi */
+ memcpy(hw_cache_event_ids,
+ slm_hw_cache_event_ids, sizeof(hw_cache_event_ids));
+ memcpy(hw_cache_extra_regs,
+ knl_hw_cache_extra_regs, sizeof(hw_cache_extra_regs));
+ intel_pmu_lbr_init_knl();
+
+ x86_pmu.event_constraints = intel_slm_event_constraints;
+ x86_pmu.pebs_constraints = intel_slm_pebs_event_constraints;
+ x86_pmu.extra_regs = intel_knl_extra_regs;
+
+ /* all extra regs are per-cpu when HT is on */
+ x86_pmu.flags |= PMU_FL_HAS_RSP_1;
+ x86_pmu.flags |= PMU_FL_NO_HT_SHARING;
+
+ pr_cont("Knights Landing events, ");
+ break;
+
case 78: /* 14nm Skylake Mobile */
case 94: /* 14nm Skylake Desktop */
x86_pmu.late_ack = true;
@@ -3521,7 +3625,8 @@ __init int intel_pmu_init(void)
x86_pmu.event_constraints = intel_skl_event_constraints;
x86_pmu.pebs_constraints = intel_skl_pebs_event_constraints;
x86_pmu.extra_regs = intel_skl_extra_regs;
- x86_pmu.pebs_aliases = intel_pebs_aliases_snb;
+ x86_pmu.pebs_aliases = intel_pebs_aliases_skl;
+ x86_pmu.pebs_prec_dist = true;
/* all extra regs are per-cpu when HT is on */
x86_pmu.flags |= PMU_FL_HAS_RSP_1;
x86_pmu.flags |= PMU_FL_NO_HT_SHARING;
diff --git a/arch/x86/kernel/cpu/perf_event_intel_cqm.c b/arch/x86/kernel/cpu/perf_event_intel_cqm.c
index 377e8f8ed391..a316ca96f1b6 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_cqm.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_cqm.c
@@ -298,7 +298,7 @@ static bool __match_event(struct perf_event *a, struct perf_event *b)
static inline struct perf_cgroup *event_to_cgroup(struct perf_event *event)
{
if (event->attach_state & PERF_ATTACH_TASK)
- return perf_cgroup_from_task(event->hw.target);
+ return perf_cgroup_from_task(event->hw.target, event->ctx);
return event->cgrp;
}
diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c
index 5db1c7755548..10602f0a438f 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_ds.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c
@@ -620,6 +620,8 @@ struct event_constraint intel_atom_pebs_event_constraints[] = {
INTEL_FLAGS_EVENT_CONSTRAINT(0xcb, 0x1), /* MEM_LOAD_RETIRED.* */
/* INST_RETIRED.ANY_P, inv=1, cmask=16 (cycles:p). */
INTEL_FLAGS_EVENT_CONSTRAINT(0x108000c0, 0x01),
+ /* Allow all events as PEBS with no flags */
+ INTEL_ALL_EVENT_CONSTRAINT(0, 0x1),
EVENT_CONSTRAINT_END
};
@@ -686,6 +688,8 @@ struct event_constraint intel_ivb_pebs_event_constraints[] = {
INTEL_PST_CONSTRAINT(0x02cd, 0x8), /* MEM_TRANS_RETIRED.PRECISE_STORES */
/* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */
INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c2, 0xf),
+ /* INST_RETIRED.PREC_DIST, inv=1, cmask=16 (cycles:ppp). */
+ INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c0, 0x2),
INTEL_EXCLEVT_CONSTRAINT(0xd0, 0xf), /* MEM_UOP_RETIRED.* */
INTEL_EXCLEVT_CONSTRAINT(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */
INTEL_EXCLEVT_CONSTRAINT(0xd2, 0xf), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */
@@ -700,6 +704,8 @@ struct event_constraint intel_hsw_pebs_event_constraints[] = {
INTEL_PLD_CONSTRAINT(0x01cd, 0xf), /* MEM_TRANS_RETIRED.* */
/* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */
INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c2, 0xf),
+ /* INST_RETIRED.PREC_DIST, inv=1, cmask=16 (cycles:ppp). */
+ INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c0, 0x2),
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_NA(0x01c2, 0xf), /* UOPS_RETIRED.ALL */
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XLD(0x11d0, 0xf), /* MEM_UOPS_RETIRED.STLB_MISS_LOADS */
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XLD(0x21d0, 0xf), /* MEM_UOPS_RETIRED.LOCK_LOADS */
@@ -718,9 +724,10 @@ struct event_constraint intel_hsw_pebs_event_constraints[] = {
struct event_constraint intel_skl_pebs_event_constraints[] = {
INTEL_FLAGS_UEVENT_CONSTRAINT(0x1c0, 0x2), /* INST_RETIRED.PREC_DIST */
- INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_NA(0x01c2, 0xf), /* UOPS_RETIRED.ALL */
- /* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */
- INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c2, 0xf),
+ /* INST_RETIRED.PREC_DIST, inv=1, cmask=16 (cycles:ppp). */
+ INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c0, 0x2),
+ /* INST_RETIRED.TOTAL_CYCLES_PS (inv=1, cmask=16) (cycles:p). */
+ INTEL_FLAGS_EVENT_CONSTRAINT(0x108000c0, 0x0f),
INTEL_PLD_CONSTRAINT(0x1cd, 0xf), /* MEM_TRANS_RETIRED.* */
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x11d0, 0xf), /* MEM_INST_RETIRED.STLB_MISS_LOADS */
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x12d0, 0xf), /* MEM_INST_RETIRED.STLB_MISS_STORES */
@@ -1101,6 +1108,13 @@ get_next_pebs_record_by_bit(void *base, void *top, int bit)
void *at;
u64 pebs_status;
+ /*
+ * fmt0 does not have a status bitfield (does not use
+ * perf_record_nhm format)
+ */
+ if (x86_pmu.intel_cap.pebs_format < 1)
+ return base;
+
if (base == NULL)
return NULL;
@@ -1186,7 +1200,7 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs)
if (!event->attr.precise_ip)
return;
- n = (top - at) / x86_pmu.pebs_record_size;
+ n = top - at;
if (n <= 0)
return;
@@ -1230,12 +1244,21 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs)
pebs_status = p->status & cpuc->pebs_enabled;
pebs_status &= (1ULL << x86_pmu.max_pebs_events) - 1;
+ /*
+ * On some CPUs the PEBS status can be zero when PEBS is
+ * racing with clearing of GLOBAL_STATUS.
+ *
+ * Normally we would drop that record, but in the
+ * case when there is only a single active PEBS event
+ * we can assume it's for that event.
+ */
+ if (!pebs_status && cpuc->pebs_enabled &&
+ !(cpuc->pebs_enabled & (cpuc->pebs_enabled-1)))
+ pebs_status = cpuc->pebs_enabled;
+
bit = find_first_bit((unsigned long *)&pebs_status,
x86_pmu.max_pebs_events);
- if (WARN(bit >= x86_pmu.max_pebs_events,
- "PEBS record without PEBS event! status=%Lx pebs_enabled=%Lx active_mask=%Lx",
- (unsigned long long)p->status, (unsigned long long)cpuc->pebs_enabled,
- *(unsigned long long *)cpuc->active_mask))
+ if (bit >= x86_pmu.max_pebs_events)
continue;
/*
diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c
index bfd0b717e944..653f88d25987 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c
@@ -42,6 +42,13 @@ static enum {
#define LBR_FAR_BIT 8 /* do not capture far branches */
#define LBR_CALL_STACK_BIT 9 /* enable call stack */
+/*
+ * Following bit only exists in Linux; we mask it out before writing it to
+ * the actual MSR. But it helps the constraint perf code to understand
+ * that this is a separate configuration.
+ */
+#define LBR_NO_INFO_BIT 63 /* don't read LBR_INFO. */
+
#define LBR_KERNEL (1 << LBR_KERNEL_BIT)
#define LBR_USER (1 << LBR_USER_BIT)
#define LBR_JCC (1 << LBR_JCC_BIT)
@@ -52,6 +59,7 @@ static enum {
#define LBR_IND_JMP (1 << LBR_IND_JMP_BIT)
#define LBR_FAR (1 << LBR_FAR_BIT)
#define LBR_CALL_STACK (1 << LBR_CALL_STACK_BIT)
+#define LBR_NO_INFO (1ULL << LBR_NO_INFO_BIT)
#define LBR_PLM (LBR_KERNEL | LBR_USER)
@@ -152,8 +160,8 @@ static void __intel_pmu_lbr_enable(bool pmi)
* did not change.
*/
if (cpuc->lbr_sel)
- lbr_select = cpuc->lbr_sel->config;
- if (!pmi)
+ lbr_select = cpuc->lbr_sel->config & x86_pmu.lbr_sel_mask;
+ if (!pmi && cpuc->lbr_sel)
wrmsrl(MSR_LBR_SELECT, lbr_select);
rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
@@ -239,7 +247,7 @@ static void __intel_pmu_lbr_restore(struct x86_perf_task_context *task_ctx)
}
mask = x86_pmu.lbr_nr - 1;
- tos = intel_pmu_lbr_tos();
+ tos = task_ctx->tos;
for (i = 0; i < tos; i++) {
lbr_idx = (tos - i) & mask;
wrmsrl(x86_pmu.lbr_from + lbr_idx, task_ctx->lbr_from[i]);
@@ -247,6 +255,7 @@ static void __intel_pmu_lbr_restore(struct x86_perf_task_context *task_ctx)
if (x86_pmu.intel_cap.lbr_format == LBR_FORMAT_INFO)
wrmsrl(MSR_LBR_INFO_0 + lbr_idx, task_ctx->lbr_info[i]);
}
+ wrmsrl(x86_pmu.lbr_tos, tos);
task_ctx->lbr_stack_state = LBR_NONE;
}
@@ -270,6 +279,7 @@ static void __intel_pmu_lbr_save(struct x86_perf_task_context *task_ctx)
if (x86_pmu.intel_cap.lbr_format == LBR_FORMAT_INFO)
rdmsrl(MSR_LBR_INFO_0 + lbr_idx, task_ctx->lbr_info[i]);
}
+ task_ctx->tos = tos;
task_ctx->lbr_stack_state = LBR_VALID;
}
@@ -420,6 +430,7 @@ static void intel_pmu_lbr_read_32(struct cpu_hw_events *cpuc)
*/
static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc)
{
+ bool need_info = false;
unsigned long mask = x86_pmu.lbr_nr - 1;
int lbr_format = x86_pmu.intel_cap.lbr_format;
u64 tos = intel_pmu_lbr_tos();
@@ -427,8 +438,11 @@ static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc)
int out = 0;
int num = x86_pmu.lbr_nr;
- if (cpuc->lbr_sel->config & LBR_CALL_STACK)
- num = tos;
+ if (cpuc->lbr_sel) {
+ need_info = !(cpuc->lbr_sel->config & LBR_NO_INFO);
+ if (cpuc->lbr_sel->config & LBR_CALL_STACK)
+ num = tos;
+ }
for (i = 0; i < num; i++) {
unsigned long lbr_idx = (tos - i) & mask;
@@ -440,7 +454,7 @@ static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc)
rdmsrl(x86_pmu.lbr_from + lbr_idx, from);
rdmsrl(x86_pmu.lbr_to + lbr_idx, to);
- if (lbr_format == LBR_FORMAT_INFO) {
+ if (lbr_format == LBR_FORMAT_INFO && need_info) {
u64 info;
rdmsrl(MSR_LBR_INFO_0 + lbr_idx, info);
@@ -588,6 +602,7 @@ static int intel_pmu_setup_hw_lbr_filter(struct perf_event *event)
if (v != LBR_IGN)
mask |= v;
}
+
reg = &event->hw.branch_reg;
reg->idx = EXTRA_REG_LBR;
@@ -598,6 +613,11 @@ static int intel_pmu_setup_hw_lbr_filter(struct perf_event *event)
*/
reg->config = mask ^ x86_pmu.lbr_sel_mask;
+ if ((br_type & PERF_SAMPLE_BRANCH_NO_CYCLES) &&
+ (br_type & PERF_SAMPLE_BRANCH_NO_FLAGS) &&
+ (x86_pmu.intel_cap.lbr_format == LBR_FORMAT_INFO))
+ reg->config |= LBR_NO_INFO;
+
return 0;
}
@@ -1026,3 +1046,17 @@ void __init intel_pmu_lbr_init_atom(void)
*/
pr_cont("8-deep LBR, ");
}
+
+/* Knights Landing */
+void intel_pmu_lbr_init_knl(void)
+{
+ x86_pmu.lbr_nr = 8;
+ x86_pmu.lbr_tos = MSR_LBR_TOS;
+ x86_pmu.lbr_from = MSR_LBR_NHM_FROM;
+ x86_pmu.lbr_to = MSR_LBR_NHM_TO;
+
+ x86_pmu.lbr_sel_mask = LBR_SEL_MASK;
+ x86_pmu.lbr_sel_map = snb_lbr_sel_map;
+
+ pr_cont("8-deep LBR, ");
+}
diff --git a/arch/x86/kernel/cpu/perf_event_intel_pt.c b/arch/x86/kernel/cpu/perf_event_intel_pt.c
index 868e1194337f..c0bbd1033b7c 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_pt.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_pt.c
@@ -27,6 +27,7 @@
#include <asm/perf_event.h>
#include <asm/insn.h>
#include <asm/io.h>
+#include <asm/intel_pt.h>
#include "perf_event.h"
#include "intel_pt.h"
@@ -1122,6 +1123,14 @@ static int pt_event_init(struct perf_event *event)
return 0;
}
+void cpu_emergency_stop_pt(void)
+{
+ struct pt *pt = this_cpu_ptr(&pt_ctx);
+
+ if (pt->handle.event)
+ pt_event_stop(pt->handle.event, PERF_EF_UPDATE);
+}
+
static __init int pt_init(void)
{
int ret, cpu, prior_warn = 0;
diff --git a/arch/x86/kernel/cpu/perf_event_intel_rapl.c b/arch/x86/kernel/cpu/perf_event_intel_rapl.c
index ed446bdcbf31..24a351ad628d 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_rapl.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_rapl.c
@@ -63,7 +63,7 @@
#define INTEL_RAPL_PP1 0x4 /* pseudo-encoding */
#define NR_RAPL_DOMAINS 0x4
-static const char *rapl_domain_names[NR_RAPL_DOMAINS] __initconst = {
+static const char *const rapl_domain_names[NR_RAPL_DOMAINS] __initconst = {
"pp0-core",
"package",
"dram",
@@ -109,11 +109,11 @@ static struct kobj_attribute format_attr_##_var = \
#define RAPL_CNTR_WIDTH 32 /* 32-bit rapl counters */
-#define RAPL_EVENT_ATTR_STR(_name, v, str) \
-static struct perf_pmu_events_attr event_attr_##v = { \
- .attr = __ATTR(_name, 0444, rapl_sysfs_show, NULL), \
- .id = 0, \
- .event_str = str, \
+#define RAPL_EVENT_ATTR_STR(_name, v, str) \
+static struct perf_pmu_events_attr event_attr_##v = { \
+ .attr = __ATTR(_name, 0444, perf_event_sysfs_show, NULL), \
+ .id = 0, \
+ .event_str = str, \
};
struct rapl_pmu {
@@ -405,19 +405,6 @@ static struct attribute_group rapl_pmu_attr_group = {
.attrs = rapl_pmu_attrs,
};
-static ssize_t rapl_sysfs_show(struct device *dev,
- struct device_attribute *attr,
- char *page)
-{
- struct perf_pmu_events_attr *pmu_attr = \
- container_of(attr, struct perf_pmu_events_attr, attr);
-
- if (pmu_attr->event_str)
- return sprintf(page, "%s", pmu_attr->event_str);
-
- return 0;
-}
-
RAPL_EVENT_ATTR_STR(energy-cores, rapl_cores, "event=0x01");
RAPL_EVENT_ATTR_STR(energy-pkg , rapl_pkg, "event=0x02");
RAPL_EVENT_ATTR_STR(energy-ram , rapl_ram, "event=0x03");
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.c b/arch/x86/kernel/cpu/perf_event_intel_uncore.c
index 61215a69b03d..f97f8075bf04 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_uncore.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.c
@@ -884,6 +884,15 @@ static int uncore_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id
* each box has a different function id.
*/
pmu = &type->pmus[UNCORE_PCI_DEV_IDX(id->driver_data)];
+ /* Knights Landing uses a common PCI device ID for multiple instances of
+ * an uncore PMU device type. There is only one entry per device type in
+ * the knl_uncore_pci_ids table inspite of multiple devices present for
+ * some device types. Hence PCI device idx would be 0 for all devices.
+ * So increment pmu pointer to point to an unused array element.
+ */
+ if (boot_cpu_data.x86_model == 87)
+ while (pmu->func_id >= 0)
+ pmu++;
if (pmu->func_id < 0)
pmu->func_id = pdev->devfn;
else
@@ -966,6 +975,7 @@ static int __init uncore_pci_init(void)
case 63: /* Haswell-EP */
ret = hswep_uncore_pci_init();
break;
+ case 79: /* BDX-EP */
case 86: /* BDX-DE */
ret = bdx_uncore_pci_init();
break;
@@ -982,6 +992,9 @@ static int __init uncore_pci_init(void)
case 61: /* Broadwell */
ret = bdw_uncore_pci_init();
break;
+ case 87: /* Knights Landing */
+ ret = knl_uncore_pci_init();
+ break;
default:
return 0;
}
@@ -1287,9 +1300,13 @@ static int __init uncore_cpu_init(void)
case 63: /* Haswell-EP */
hswep_uncore_cpu_init();
break;
+ case 79: /* BDX-EP */
case 86: /* BDX-DE */
bdx_uncore_cpu_init();
break;
+ case 87: /* Knights Landing */
+ knl_uncore_cpu_init();
+ break;
default:
return 0;
}
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.h b/arch/x86/kernel/cpu/perf_event_intel_uncore.h
index 2f0a4a98e16b..07aa2d6bd710 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_uncore.h
+++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.h
@@ -338,6 +338,7 @@ int hsw_uncore_pci_init(void);
int bdw_uncore_pci_init(void);
void snb_uncore_cpu_init(void);
void nhm_uncore_cpu_init(void);
+int snb_pci2phy_map_init(int devid);
/* perf_event_intel_uncore_snbep.c */
int snbep_uncore_pci_init(void);
@@ -348,6 +349,8 @@ int hswep_uncore_pci_init(void);
void hswep_uncore_cpu_init(void);
int bdx_uncore_pci_init(void);
void bdx_uncore_cpu_init(void);
+int knl_uncore_pci_init(void);
+void knl_uncore_cpu_init(void);
/* perf_event_intel_uncore_nhmex.c */
void nhmex_uncore_cpu_init(void);
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore_snb.c b/arch/x86/kernel/cpu/perf_event_intel_uncore_snb.c
index 845256158a10..0b934820fafd 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_uncore_snb.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_uncore_snb.c
@@ -417,7 +417,7 @@ static void snb_uncore_imc_event_del(struct perf_event *event, int flags)
}
}
-static int snb_pci2phy_map_init(int devid)
+int snb_pci2phy_map_init(int devid)
{
struct pci_dev *dev = NULL;
struct pci2phy_map *map;
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c b/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c
index f0f4fcba252e..33acb884ccf1 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c
@@ -209,31 +209,98 @@
#define HSWEP_PCU_MSR_PMON_BOX_CTL 0x710
#define HSWEP_PCU_MSR_PMON_BOX_FILTER 0x715
+/* KNL Ubox */
+#define KNL_U_MSR_PMON_RAW_EVENT_MASK \
+ (SNBEP_U_MSR_PMON_RAW_EVENT_MASK | \
+ SNBEP_CBO_PMON_CTL_TID_EN)
+/* KNL CHA */
+#define KNL_CHA_MSR_OFFSET 0xc
+#define KNL_CHA_MSR_PMON_CTL_QOR (1 << 16)
+#define KNL_CHA_MSR_PMON_RAW_EVENT_MASK \
+ (SNBEP_CBO_MSR_PMON_RAW_EVENT_MASK | \
+ KNL_CHA_MSR_PMON_CTL_QOR)
+#define KNL_CHA_MSR_PMON_BOX_FILTER_TID 0x1ff
+#define KNL_CHA_MSR_PMON_BOX_FILTER_STATE (7 << 18)
+#define KNL_CHA_MSR_PMON_BOX_FILTER_OP (0xfffffe2aULL << 32)
+
+/* KNL EDC/MC UCLK */
+#define KNL_UCLK_MSR_PMON_CTR0_LOW 0x400
+#define KNL_UCLK_MSR_PMON_CTL0 0x420
+#define KNL_UCLK_MSR_PMON_BOX_CTL 0x430
+#define KNL_UCLK_MSR_PMON_UCLK_FIXED_LOW 0x44c
+#define KNL_UCLK_MSR_PMON_UCLK_FIXED_CTL 0x454
+#define KNL_PMON_FIXED_CTL_EN 0x1
+
+/* KNL EDC */
+#define KNL_EDC0_ECLK_MSR_PMON_CTR0_LOW 0xa00
+#define KNL_EDC0_ECLK_MSR_PMON_CTL0 0xa20
+#define KNL_EDC0_ECLK_MSR_PMON_BOX_CTL 0xa30
+#define KNL_EDC0_ECLK_MSR_PMON_ECLK_FIXED_LOW 0xa3c
+#define KNL_EDC0_ECLK_MSR_PMON_ECLK_FIXED_CTL 0xa44
+
+/* KNL MC */
+#define KNL_MC0_CH0_MSR_PMON_CTR0_LOW 0xb00
+#define KNL_MC0_CH0_MSR_PMON_CTL0 0xb20
+#define KNL_MC0_CH0_MSR_PMON_BOX_CTL 0xb30
+#define KNL_MC0_CH0_MSR_PMON_FIXED_LOW 0xb3c
+#define KNL_MC0_CH0_MSR_PMON_FIXED_CTL 0xb44
+
+/* KNL IRP */
+#define KNL_IRP_PCI_PMON_BOX_CTL 0xf0
+#define KNL_IRP_PCI_PMON_RAW_EVENT_MASK (SNBEP_PMON_RAW_EVENT_MASK | \
+ KNL_CHA_MSR_PMON_CTL_QOR)
+/* KNL PCU */
+#define KNL_PCU_PMON_CTL_EV_SEL_MASK 0x0000007f
+#define KNL_PCU_PMON_CTL_USE_OCC_CTR (1 << 7)
+#define KNL_PCU_MSR_PMON_CTL_TRESH_MASK 0x3f000000
+#define KNL_PCU_MSR_PMON_RAW_EVENT_MASK \
+ (KNL_PCU_PMON_CTL_EV_SEL_MASK | \
+ KNL_PCU_PMON_CTL_USE_OCC_CTR | \
+ SNBEP_PCU_MSR_PMON_CTL_OCC_SEL_MASK | \
+ SNBEP_PMON_CTL_EDGE_DET | \
+ SNBEP_CBO_PMON_CTL_TID_EN | \
+ SNBEP_PMON_CTL_EV_SEL_EXT | \
+ SNBEP_PMON_CTL_INVERT | \
+ KNL_PCU_MSR_PMON_CTL_TRESH_MASK | \
+ SNBEP_PCU_MSR_PMON_CTL_OCC_INVERT | \
+ SNBEP_PCU_MSR_PMON_CTL_OCC_EDGE_DET)
DEFINE_UNCORE_FORMAT_ATTR(event, event, "config:0-7");
+DEFINE_UNCORE_FORMAT_ATTR(event2, event, "config:0-6");
DEFINE_UNCORE_FORMAT_ATTR(event_ext, event, "config:0-7,21");
+DEFINE_UNCORE_FORMAT_ATTR(use_occ_ctr, use_occ_ctr, "config:7");
DEFINE_UNCORE_FORMAT_ATTR(umask, umask, "config:8-15");
+DEFINE_UNCORE_FORMAT_ATTR(qor, qor, "config:16");
DEFINE_UNCORE_FORMAT_ATTR(edge, edge, "config:18");
DEFINE_UNCORE_FORMAT_ATTR(tid_en, tid_en, "config:19");
DEFINE_UNCORE_FORMAT_ATTR(inv, inv, "config:23");
DEFINE_UNCORE_FORMAT_ATTR(thresh8, thresh, "config:24-31");
+DEFINE_UNCORE_FORMAT_ATTR(thresh6, thresh, "config:24-29");
DEFINE_UNCORE_FORMAT_ATTR(thresh5, thresh, "config:24-28");
DEFINE_UNCORE_FORMAT_ATTR(occ_sel, occ_sel, "config:14-15");
DEFINE_UNCORE_FORMAT_ATTR(occ_invert, occ_invert, "config:30");
DEFINE_UNCORE_FORMAT_ATTR(occ_edge, occ_edge, "config:14-51");
+DEFINE_UNCORE_FORMAT_ATTR(occ_edge_det, occ_edge_det, "config:31");
DEFINE_UNCORE_FORMAT_ATTR(filter_tid, filter_tid, "config1:0-4");
DEFINE_UNCORE_FORMAT_ATTR(filter_tid2, filter_tid, "config1:0");
DEFINE_UNCORE_FORMAT_ATTR(filter_tid3, filter_tid, "config1:0-5");
+DEFINE_UNCORE_FORMAT_ATTR(filter_tid4, filter_tid, "config1:0-8");
DEFINE_UNCORE_FORMAT_ATTR(filter_cid, filter_cid, "config1:5");
DEFINE_UNCORE_FORMAT_ATTR(filter_link, filter_link, "config1:5-8");
DEFINE_UNCORE_FORMAT_ATTR(filter_link2, filter_link, "config1:6-8");
+DEFINE_UNCORE_FORMAT_ATTR(filter_link3, filter_link, "config1:12");
DEFINE_UNCORE_FORMAT_ATTR(filter_nid, filter_nid, "config1:10-17");
DEFINE_UNCORE_FORMAT_ATTR(filter_nid2, filter_nid, "config1:32-47");
DEFINE_UNCORE_FORMAT_ATTR(filter_state, filter_state, "config1:18-22");
DEFINE_UNCORE_FORMAT_ATTR(filter_state2, filter_state, "config1:17-22");
DEFINE_UNCORE_FORMAT_ATTR(filter_state3, filter_state, "config1:17-23");
+DEFINE_UNCORE_FORMAT_ATTR(filter_state4, filter_state, "config1:18-20");
+DEFINE_UNCORE_FORMAT_ATTR(filter_local, filter_local, "config1:33");
+DEFINE_UNCORE_FORMAT_ATTR(filter_all_op, filter_all_op, "config1:35");
+DEFINE_UNCORE_FORMAT_ATTR(filter_nnm, filter_nnm, "config1:37");
DEFINE_UNCORE_FORMAT_ATTR(filter_opc, filter_opc, "config1:23-31");
DEFINE_UNCORE_FORMAT_ATTR(filter_opc2, filter_opc, "config1:52-60");
+DEFINE_UNCORE_FORMAT_ATTR(filter_opc3, filter_opc, "config1:41-60");
DEFINE_UNCORE_FORMAT_ATTR(filter_nc, filter_nc, "config1:62");
DEFINE_UNCORE_FORMAT_ATTR(filter_c6, filter_c6, "config1:61");
DEFINE_UNCORE_FORMAT_ATTR(filter_isoc, filter_isoc, "config1:63");
@@ -315,8 +382,9 @@ static u64 snbep_uncore_pci_read_counter(struct intel_uncore_box *box, struct pe
static void snbep_uncore_pci_init_box(struct intel_uncore_box *box)
{
struct pci_dev *pdev = box->pci_dev;
+ int box_ctl = uncore_pci_box_ctl(box);
- pci_write_config_dword(pdev, SNBEP_PCI_PMON_BOX_CTL, SNBEP_PMON_BOX_CTL_INT);
+ pci_write_config_dword(pdev, box_ctl, SNBEP_PMON_BOX_CTL_INT);
}
static void snbep_uncore_msr_disable_box(struct intel_uncore_box *box)
@@ -1728,6 +1796,419 @@ int ivbep_uncore_pci_init(void)
}
/* end of IvyTown uncore support */
+/* KNL uncore support */
+static struct attribute *knl_uncore_ubox_formats_attr[] = {
+ &format_attr_event.attr,
+ &format_attr_umask.attr,
+ &format_attr_edge.attr,
+ &format_attr_tid_en.attr,
+ &format_attr_inv.attr,
+ &format_attr_thresh5.attr,
+ NULL,
+};
+
+static struct attribute_group knl_uncore_ubox_format_group = {
+ .name = "format",
+ .attrs = knl_uncore_ubox_formats_attr,
+};
+
+static struct intel_uncore_type knl_uncore_ubox = {
+ .name = "ubox",
+ .num_counters = 2,
+ .num_boxes = 1,
+ .perf_ctr_bits = 48,
+ .fixed_ctr_bits = 48,
+ .perf_ctr = HSWEP_U_MSR_PMON_CTR0,
+ .event_ctl = HSWEP_U_MSR_PMON_CTL0,
+ .event_mask = KNL_U_MSR_PMON_RAW_EVENT_MASK,
+ .fixed_ctr = HSWEP_U_MSR_PMON_UCLK_FIXED_CTR,
+ .fixed_ctl = HSWEP_U_MSR_PMON_UCLK_FIXED_CTL,
+ .ops = &snbep_uncore_msr_ops,
+ .format_group = &knl_uncore_ubox_format_group,
+};
+
+static struct attribute *knl_uncore_cha_formats_attr[] = {
+ &format_attr_event.attr,
+ &format_attr_umask.attr,
+ &format_attr_qor.attr,
+ &format_attr_edge.attr,
+ &format_attr_tid_en.attr,
+ &format_attr_inv.attr,
+ &format_attr_thresh8.attr,
+ &format_attr_filter_tid4.attr,
+ &format_attr_filter_link3.attr,
+ &format_attr_filter_state4.attr,
+ &format_attr_filter_local.attr,
+ &format_attr_filter_all_op.attr,
+ &format_attr_filter_nnm.attr,
+ &format_attr_filter_opc3.attr,
+ &format_attr_filter_nc.attr,
+ &format_attr_filter_isoc.attr,
+ NULL,
+};
+
+static struct attribute_group knl_uncore_cha_format_group = {
+ .name = "format",
+ .attrs = knl_uncore_cha_formats_attr,
+};
+
+static struct event_constraint knl_uncore_cha_constraints[] = {
+ UNCORE_EVENT_CONSTRAINT(0x11, 0x1),
+ UNCORE_EVENT_CONSTRAINT(0x1f, 0x1),
+ UNCORE_EVENT_CONSTRAINT(0x36, 0x1),
+ EVENT_CONSTRAINT_END
+};
+
+static struct extra_reg knl_uncore_cha_extra_regs[] = {
+ SNBEP_CBO_EVENT_EXTRA_REG(SNBEP_CBO_PMON_CTL_TID_EN,
+ SNBEP_CBO_PMON_CTL_TID_EN, 0x1),
+ SNBEP_CBO_EVENT_EXTRA_REG(0x3d, 0xff, 0x2),
+ SNBEP_CBO_EVENT_EXTRA_REG(0x35, 0xff, 0x4),
+ SNBEP_CBO_EVENT_EXTRA_REG(0x36, 0xff, 0x4),
+ EVENT_EXTRA_END
+};
+
+static u64 knl_cha_filter_mask(int fields)
+{
+ u64 mask = 0;
+
+ if (fields & 0x1)
+ mask |= KNL_CHA_MSR_PMON_BOX_FILTER_TID;
+ if (fields & 0x2)
+ mask |= KNL_CHA_MSR_PMON_BOX_FILTER_STATE;
+ if (fields & 0x4)
+ mask |= KNL_CHA_MSR_PMON_BOX_FILTER_OP;
+ return mask;
+}
+
+static struct event_constraint *
+knl_cha_get_constraint(struct intel_uncore_box *box, struct perf_event *event)
+{
+ return __snbep_cbox_get_constraint(box, event, knl_cha_filter_mask);
+}
+
+static int knl_cha_hw_config(struct intel_uncore_box *box,
+ struct perf_event *event)
+{
+ struct hw_perf_event_extra *reg1 = &event->hw.extra_reg;
+ struct extra_reg *er;
+ int idx = 0;
+
+ for (er = knl_uncore_cha_extra_regs; er->msr; er++) {
+ if (er->event != (event->hw.config & er->config_mask))
+ continue;
+ idx |= er->idx;
+ }
+
+ if (idx) {
+ reg1->reg = HSWEP_C0_MSR_PMON_BOX_FILTER0 +
+ KNL_CHA_MSR_OFFSET * box->pmu->pmu_idx;
+ reg1->config = event->attr.config1 & knl_cha_filter_mask(idx);
+ reg1->idx = idx;
+ }
+ return 0;
+}
+
+static void hswep_cbox_enable_event(struct intel_uncore_box *box,
+ struct perf_event *event);
+
+static struct intel_uncore_ops knl_uncore_cha_ops = {
+ .init_box = snbep_uncore_msr_init_box,
+ .disable_box = snbep_uncore_msr_disable_box,
+ .enable_box = snbep_uncore_msr_enable_box,
+ .disable_event = snbep_uncore_msr_disable_event,
+ .enable_event = hswep_cbox_enable_event,
+ .read_counter = uncore_msr_read_counter,
+ .hw_config = knl_cha_hw_config,
+ .get_constraint = knl_cha_get_constraint,
+ .put_constraint = snbep_cbox_put_constraint,
+};
+
+static struct intel_uncore_type knl_uncore_cha = {
+ .name = "cha",
+ .num_counters = 4,
+ .num_boxes = 38,
+ .perf_ctr_bits = 48,
+ .event_ctl = HSWEP_C0_MSR_PMON_CTL0,
+ .perf_ctr = HSWEP_C0_MSR_PMON_CTR0,
+ .event_mask = KNL_CHA_MSR_PMON_RAW_EVENT_MASK,
+ .box_ctl = HSWEP_C0_MSR_PMON_BOX_CTL,
+ .msr_offset = KNL_CHA_MSR_OFFSET,
+ .num_shared_regs = 1,
+ .constraints = knl_uncore_cha_constraints,
+ .ops = &knl_uncore_cha_ops,
+ .format_group = &knl_uncore_cha_format_group,
+};
+
+static struct attribute *knl_uncore_pcu_formats_attr[] = {
+ &format_attr_event2.attr,
+ &format_attr_use_occ_ctr.attr,
+ &format_attr_occ_sel.attr,
+ &format_attr_edge.attr,
+ &format_attr_tid_en.attr,
+ &format_attr_inv.attr,
+ &format_attr_thresh6.attr,
+ &format_attr_occ_invert.attr,
+ &format_attr_occ_edge_det.attr,
+ NULL,
+};
+
+static struct attribute_group knl_uncore_pcu_format_group = {
+ .name = "format",
+ .attrs = knl_uncore_pcu_formats_attr,
+};
+
+static struct intel_uncore_type knl_uncore_pcu = {
+ .name = "pcu",
+ .num_counters = 4,
+ .num_boxes = 1,
+ .perf_ctr_bits = 48,
+ .perf_ctr = HSWEP_PCU_MSR_PMON_CTR0,
+ .event_ctl = HSWEP_PCU_MSR_PMON_CTL0,
+ .event_mask = KNL_PCU_MSR_PMON_RAW_EVENT_MASK,
+ .box_ctl = HSWEP_PCU_MSR_PMON_BOX_CTL,
+ .ops = &snbep_uncore_msr_ops,
+ .format_group = &knl_uncore_pcu_format_group,
+};
+
+static struct intel_uncore_type *knl_msr_uncores[] = {
+ &knl_uncore_ubox,
+ &knl_uncore_cha,
+ &knl_uncore_pcu,
+ NULL,
+};
+
+void knl_uncore_cpu_init(void)
+{
+ uncore_msr_uncores = knl_msr_uncores;
+}
+
+static void knl_uncore_imc_enable_box(struct intel_uncore_box *box)
+{
+ struct pci_dev *pdev = box->pci_dev;
+ int box_ctl = uncore_pci_box_ctl(box);
+
+ pci_write_config_dword(pdev, box_ctl, 0);
+}
+
+static void knl_uncore_imc_enable_event(struct intel_uncore_box *box,
+ struct perf_event *event)
+{
+ struct pci_dev *pdev = box->pci_dev;
+ struct hw_perf_event *hwc = &event->hw;
+
+ if ((event->attr.config & SNBEP_PMON_CTL_EV_SEL_MASK)
+ == UNCORE_FIXED_EVENT)
+ pci_write_config_dword(pdev, hwc->config_base,
+ hwc->config | KNL_PMON_FIXED_CTL_EN);
+ else
+ pci_write_config_dword(pdev, hwc->config_base,
+ hwc->config | SNBEP_PMON_CTL_EN);
+}
+
+static struct intel_uncore_ops knl_uncore_imc_ops = {
+ .init_box = snbep_uncore_pci_init_box,
+ .disable_box = snbep_uncore_pci_disable_box,
+ .enable_box = knl_uncore_imc_enable_box,
+ .read_counter = snbep_uncore_pci_read_counter,
+ .enable_event = knl_uncore_imc_enable_event,
+ .disable_event = snbep_uncore_pci_disable_event,
+};
+
+static struct intel_uncore_type knl_uncore_imc_uclk = {
+ .name = "imc_uclk",
+ .num_counters = 4,
+ .num_boxes = 2,
+ .perf_ctr_bits = 48,
+ .fixed_ctr_bits = 48,
+ .perf_ctr = KNL_UCLK_MSR_PMON_CTR0_LOW,
+ .event_ctl = KNL_UCLK_MSR_PMON_CTL0,
+ .event_mask = SNBEP_PMON_RAW_EVENT_MASK,
+ .fixed_ctr = KNL_UCLK_MSR_PMON_UCLK_FIXED_LOW,
+ .fixed_ctl = KNL_UCLK_MSR_PMON_UCLK_FIXED_CTL,
+ .box_ctl = KNL_UCLK_MSR_PMON_BOX_CTL,
+ .ops = &knl_uncore_imc_ops,
+ .format_group = &snbep_uncore_format_group,
+};
+
+static struct intel_uncore_type knl_uncore_imc_dclk = {
+ .name = "imc",
+ .num_counters = 4,
+ .num_boxes = 6,
+ .perf_ctr_bits = 48,
+ .fixed_ctr_bits = 48,
+ .perf_ctr = KNL_MC0_CH0_MSR_PMON_CTR0_LOW,
+ .event_ctl = KNL_MC0_CH0_MSR_PMON_CTL0,
+ .event_mask = SNBEP_PMON_RAW_EVENT_MASK,
+ .fixed_ctr = KNL_MC0_CH0_MSR_PMON_FIXED_LOW,
+ .fixed_ctl = KNL_MC0_CH0_MSR_PMON_FIXED_CTL,
+ .box_ctl = KNL_MC0_CH0_MSR_PMON_BOX_CTL,
+ .ops = &knl_uncore_imc_ops,
+ .format_group = &snbep_uncore_format_group,
+};
+
+static struct intel_uncore_type knl_uncore_edc_uclk = {
+ .name = "edc_uclk",
+ .num_counters = 4,
+ .num_boxes = 8,
+ .perf_ctr_bits = 48,
+ .fixed_ctr_bits = 48,
+ .perf_ctr = KNL_UCLK_MSR_PMON_CTR0_LOW,
+ .event_ctl = KNL_UCLK_MSR_PMON_CTL0,
+ .event_mask = SNBEP_PMON_RAW_EVENT_MASK,
+ .fixed_ctr = KNL_UCLK_MSR_PMON_UCLK_FIXED_LOW,
+ .fixed_ctl = KNL_UCLK_MSR_PMON_UCLK_FIXED_CTL,
+ .box_ctl = KNL_UCLK_MSR_PMON_BOX_CTL,
+ .ops = &knl_uncore_imc_ops,
+ .format_group = &snbep_uncore_format_group,
+};
+
+static struct intel_uncore_type knl_uncore_edc_eclk = {
+ .name = "edc_eclk",
+ .num_counters = 4,
+ .num_boxes = 8,
+ .perf_ctr_bits = 48,
+ .fixed_ctr_bits = 48,
+ .perf_ctr = KNL_EDC0_ECLK_MSR_PMON_CTR0_LOW,
+ .event_ctl = KNL_EDC0_ECLK_MSR_PMON_CTL0,
+ .event_mask = SNBEP_PMON_RAW_EVENT_MASK,
+ .fixed_ctr = KNL_EDC0_ECLK_MSR_PMON_ECLK_FIXED_LOW,
+ .fixed_ctl = KNL_EDC0_ECLK_MSR_PMON_ECLK_FIXED_CTL,
+ .box_ctl = KNL_EDC0_ECLK_MSR_PMON_BOX_CTL,
+ .ops = &knl_uncore_imc_ops,
+ .format_group = &snbep_uncore_format_group,
+};
+
+static struct event_constraint knl_uncore_m2pcie_constraints[] = {
+ UNCORE_EVENT_CONSTRAINT(0x23, 0x3),
+ EVENT_CONSTRAINT_END
+};
+
+static struct intel_uncore_type knl_uncore_m2pcie = {
+ .name = "m2pcie",
+ .num_counters = 4,
+ .num_boxes = 1,
+ .perf_ctr_bits = 48,
+ .constraints = knl_uncore_m2pcie_constraints,
+ SNBEP_UNCORE_PCI_COMMON_INIT(),
+};
+
+static struct attribute *knl_uncore_irp_formats_attr[] = {
+ &format_attr_event.attr,
+ &format_attr_umask.attr,
+ &format_attr_qor.attr,
+ &format_attr_edge.attr,
+ &format_attr_inv.attr,
+ &format_attr_thresh8.attr,
+ NULL,
+};
+
+static struct attribute_group knl_uncore_irp_format_group = {
+ .name = "format",
+ .attrs = knl_uncore_irp_formats_attr,
+};
+
+static struct intel_uncore_type knl_uncore_irp = {
+ .name = "irp",
+ .num_counters = 2,
+ .num_boxes = 1,
+ .perf_ctr_bits = 48,
+ .perf_ctr = SNBEP_PCI_PMON_CTR0,
+ .event_ctl = SNBEP_PCI_PMON_CTL0,
+ .event_mask = KNL_IRP_PCI_PMON_RAW_EVENT_MASK,
+ .box_ctl = KNL_IRP_PCI_PMON_BOX_CTL,
+ .ops = &snbep_uncore_pci_ops,
+ .format_group = &knl_uncore_irp_format_group,
+};
+
+enum {
+ KNL_PCI_UNCORE_MC_UCLK,
+ KNL_PCI_UNCORE_MC_DCLK,
+ KNL_PCI_UNCORE_EDC_UCLK,
+ KNL_PCI_UNCORE_EDC_ECLK,
+ KNL_PCI_UNCORE_M2PCIE,
+ KNL_PCI_UNCORE_IRP,
+};
+
+static struct intel_uncore_type *knl_pci_uncores[] = {
+ [KNL_PCI_UNCORE_MC_UCLK] = &knl_uncore_imc_uclk,
+ [KNL_PCI_UNCORE_MC_DCLK] = &knl_uncore_imc_dclk,
+ [KNL_PCI_UNCORE_EDC_UCLK] = &knl_uncore_edc_uclk,
+ [KNL_PCI_UNCORE_EDC_ECLK] = &knl_uncore_edc_eclk,
+ [KNL_PCI_UNCORE_M2PCIE] = &knl_uncore_m2pcie,
+ [KNL_PCI_UNCORE_IRP] = &knl_uncore_irp,
+ NULL,
+};
+
+/*
+ * KNL uses a common PCI device ID for multiple instances of an Uncore PMU
+ * device type. prior to KNL, each instance of a PMU device type had a unique
+ * device ID.
+ *
+ * PCI Device ID Uncore PMU Devices
+ * ----------------------------------
+ * 0x7841 MC0 UClk, MC1 UClk
+ * 0x7843 MC0 DClk CH 0, MC0 DClk CH 1, MC0 DClk CH 2,
+ * MC1 DClk CH 0, MC1 DClk CH 1, MC1 DClk CH 2
+ * 0x7833 EDC0 UClk, EDC1 UClk, EDC2 UClk, EDC3 UClk,
+ * EDC4 UClk, EDC5 UClk, EDC6 UClk, EDC7 UClk
+ * 0x7835 EDC0 EClk, EDC1 EClk, EDC2 EClk, EDC3 EClk,
+ * EDC4 EClk, EDC5 EClk, EDC6 EClk, EDC7 EClk
+ * 0x7817 M2PCIe
+ * 0x7814 IRP
+*/
+
+static const struct pci_device_id knl_uncore_pci_ids[] = {
+ { /* MC UClk */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x7841),
+ .driver_data = UNCORE_PCI_DEV_DATA(KNL_PCI_UNCORE_MC_UCLK, 0),
+ },
+ { /* MC DClk Channel */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x7843),
+ .driver_data = UNCORE_PCI_DEV_DATA(KNL_PCI_UNCORE_MC_DCLK, 0),
+ },
+ { /* EDC UClk */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x7833),
+ .driver_data = UNCORE_PCI_DEV_DATA(KNL_PCI_UNCORE_EDC_UCLK, 0),
+ },
+ { /* EDC EClk */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x7835),
+ .driver_data = UNCORE_PCI_DEV_DATA(KNL_PCI_UNCORE_EDC_ECLK, 0),
+ },
+ { /* M2PCIe */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x7817),
+ .driver_data = UNCORE_PCI_DEV_DATA(KNL_PCI_UNCORE_M2PCIE, 0),
+ },
+ { /* IRP */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x7814),
+ .driver_data = UNCORE_PCI_DEV_DATA(KNL_PCI_UNCORE_IRP, 0),
+ },
+ { /* end: all zeroes */ }
+};
+
+static struct pci_driver knl_uncore_pci_driver = {
+ .name = "knl_uncore",
+ .id_table = knl_uncore_pci_ids,
+};
+
+int knl_uncore_pci_init(void)
+{
+ int ret;
+
+ /* All KNL PCI based PMON units are on the same PCI bus except IRP */
+ ret = snb_pci2phy_map_init(0x7814); /* IRP */
+ if (ret)
+ return ret;
+ ret = snb_pci2phy_map_init(0x7817); /* M2PCIe */
+ if (ret)
+ return ret;
+ uncore_pci_uncores = knl_pci_uncores;
+ uncore_pci_driver = &knl_uncore_pci_driver;
+ return 0;
+}
+
+/* end of KNL uncore support */
+
/* Haswell-EP uncore support */
static struct attribute *hswep_uncore_ubox_formats_attr[] = {
&format_attr_event.attr,
@@ -2338,7 +2819,7 @@ int hswep_uncore_pci_init(void)
}
/* end of Haswell-EP uncore support */
-/* BDX-DE uncore support */
+/* BDX uncore support */
static struct intel_uncore_type bdx_uncore_ubox = {
.name = "ubox",
@@ -2360,13 +2841,14 @@ static struct event_constraint bdx_uncore_cbox_constraints[] = {
UNCORE_EVENT_CONSTRAINT(0x09, 0x3),
UNCORE_EVENT_CONSTRAINT(0x11, 0x1),
UNCORE_EVENT_CONSTRAINT(0x36, 0x1),
+ UNCORE_EVENT_CONSTRAINT(0x3e, 0x1),
EVENT_CONSTRAINT_END
};
static struct intel_uncore_type bdx_uncore_cbox = {
.name = "cbox",
.num_counters = 4,
- .num_boxes = 8,
+ .num_boxes = 24,
.perf_ctr_bits = 48,
.event_ctl = HSWEP_C0_MSR_PMON_CTL0,
.perf_ctr = HSWEP_C0_MSR_PMON_CTR0,
@@ -2379,9 +2861,24 @@ static struct intel_uncore_type bdx_uncore_cbox = {
.format_group = &hswep_uncore_cbox_format_group,
};
+static struct intel_uncore_type bdx_uncore_sbox = {
+ .name = "sbox",
+ .num_counters = 4,
+ .num_boxes = 4,
+ .perf_ctr_bits = 48,
+ .event_ctl = HSWEP_S0_MSR_PMON_CTL0,
+ .perf_ctr = HSWEP_S0_MSR_PMON_CTR0,
+ .event_mask = HSWEP_S_MSR_PMON_RAW_EVENT_MASK,
+ .box_ctl = HSWEP_S0_MSR_PMON_BOX_CTL,
+ .msr_offset = HSWEP_SBOX_MSR_OFFSET,
+ .ops = &hswep_uncore_sbox_msr_ops,
+ .format_group = &hswep_uncore_sbox_format_group,
+};
+
static struct intel_uncore_type *bdx_msr_uncores[] = {
&bdx_uncore_ubox,
&bdx_uncore_cbox,
+ &bdx_uncore_sbox,
&hswep_uncore_pcu,
NULL,
};
@@ -2396,7 +2893,7 @@ void bdx_uncore_cpu_init(void)
static struct intel_uncore_type bdx_uncore_ha = {
.name = "ha",
.num_counters = 4,
- .num_boxes = 1,
+ .num_boxes = 2,
.perf_ctr_bits = 48,
SNBEP_UNCORE_PCI_COMMON_INIT(),
};
@@ -2404,7 +2901,7 @@ static struct intel_uncore_type bdx_uncore_ha = {
static struct intel_uncore_type bdx_uncore_imc = {
.name = "imc",
.num_counters = 5,
- .num_boxes = 2,
+ .num_boxes = 8,
.perf_ctr_bits = 48,
.fixed_ctr_bits = 48,
.fixed_ctr = SNBEP_MC_CHy_PCI_PMON_FIXED_CTR,
@@ -2424,6 +2921,19 @@ static struct intel_uncore_type bdx_uncore_irp = {
.format_group = &snbep_uncore_format_group,
};
+static struct intel_uncore_type bdx_uncore_qpi = {
+ .name = "qpi",
+ .num_counters = 4,
+ .num_boxes = 3,
+ .perf_ctr_bits = 48,
+ .perf_ctr = SNBEP_PCI_PMON_CTR0,
+ .event_ctl = SNBEP_PCI_PMON_CTL0,
+ .event_mask = SNBEP_QPI_PCI_PMON_RAW_EVENT_MASK,
+ .box_ctl = SNBEP_PCI_PMON_BOX_CTL,
+ .num_shared_regs = 1,
+ .ops = &snbep_uncore_qpi_ops,
+ .format_group = &snbep_uncore_qpi_format_group,
+};
static struct event_constraint bdx_uncore_r2pcie_constraints[] = {
UNCORE_EVENT_CONSTRAINT(0x10, 0x3),
@@ -2432,6 +2942,8 @@ static struct event_constraint bdx_uncore_r2pcie_constraints[] = {
UNCORE_EVENT_CONSTRAINT(0x23, 0x1),
UNCORE_EVENT_CONSTRAINT(0x25, 0x1),
UNCORE_EVENT_CONSTRAINT(0x26, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x28, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x2c, 0x3),
UNCORE_EVENT_CONSTRAINT(0x2d, 0x3),
EVENT_CONSTRAINT_END
};
@@ -2445,18 +2957,65 @@ static struct intel_uncore_type bdx_uncore_r2pcie = {
SNBEP_UNCORE_PCI_COMMON_INIT(),
};
+static struct event_constraint bdx_uncore_r3qpi_constraints[] = {
+ UNCORE_EVENT_CONSTRAINT(0x01, 0x7),
+ UNCORE_EVENT_CONSTRAINT(0x07, 0x7),
+ UNCORE_EVENT_CONSTRAINT(0x08, 0x7),
+ UNCORE_EVENT_CONSTRAINT(0x09, 0x7),
+ UNCORE_EVENT_CONSTRAINT(0x0a, 0x7),
+ UNCORE_EVENT_CONSTRAINT(0x0e, 0x7),
+ UNCORE_EVENT_CONSTRAINT(0x10, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x11, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x13, 0x1),
+ UNCORE_EVENT_CONSTRAINT(0x14, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x15, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x1f, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x20, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x21, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x22, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x23, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x25, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x26, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x28, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x29, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x2c, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x2d, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x2e, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x2f, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x33, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x34, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x36, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x37, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x38, 0x3),
+ UNCORE_EVENT_CONSTRAINT(0x39, 0x3),
+ EVENT_CONSTRAINT_END
+};
+
+static struct intel_uncore_type bdx_uncore_r3qpi = {
+ .name = "r3qpi",
+ .num_counters = 3,
+ .num_boxes = 3,
+ .perf_ctr_bits = 48,
+ .constraints = bdx_uncore_r3qpi_constraints,
+ SNBEP_UNCORE_PCI_COMMON_INIT(),
+};
+
enum {
BDX_PCI_UNCORE_HA,
BDX_PCI_UNCORE_IMC,
BDX_PCI_UNCORE_IRP,
+ BDX_PCI_UNCORE_QPI,
BDX_PCI_UNCORE_R2PCIE,
+ BDX_PCI_UNCORE_R3QPI,
};
static struct intel_uncore_type *bdx_pci_uncores[] = {
[BDX_PCI_UNCORE_HA] = &bdx_uncore_ha,
[BDX_PCI_UNCORE_IMC] = &bdx_uncore_imc,
[BDX_PCI_UNCORE_IRP] = &bdx_uncore_irp,
+ [BDX_PCI_UNCORE_QPI] = &bdx_uncore_qpi,
[BDX_PCI_UNCORE_R2PCIE] = &bdx_uncore_r2pcie,
+ [BDX_PCI_UNCORE_R3QPI] = &bdx_uncore_r3qpi,
NULL,
};
@@ -2465,6 +3024,10 @@ static const struct pci_device_id bdx_uncore_pci_ids[] = {
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f30),
.driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_HA, 0),
},
+ { /* Home Agent 1 */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f38),
+ .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_HA, 1),
+ },
{ /* MC0 Channel 0 */
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6fb0),
.driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_IMC, 0),
@@ -2473,14 +3036,74 @@ static const struct pci_device_id bdx_uncore_pci_ids[] = {
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6fb1),
.driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_IMC, 1),
},
+ { /* MC0 Channel 2 */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6fb4),
+ .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_IMC, 2),
+ },
+ { /* MC0 Channel 3 */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6fb5),
+ .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_IMC, 3),
+ },
+ { /* MC1 Channel 0 */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6fd0),
+ .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_IMC, 4),
+ },
+ { /* MC1 Channel 1 */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6fd1),
+ .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_IMC, 5),
+ },
+ { /* MC1 Channel 2 */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6fd4),
+ .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_IMC, 6),
+ },
+ { /* MC1 Channel 3 */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6fd5),
+ .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_IMC, 7),
+ },
{ /* IRP */
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f39),
.driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_IRP, 0),
},
+ { /* QPI0 Port 0 */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f32),
+ .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_QPI, 0),
+ },
+ { /* QPI0 Port 1 */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f33),
+ .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_QPI, 1),
+ },
+ { /* QPI1 Port 2 */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f3a),
+ .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_QPI, 2),
+ },
{ /* R2PCIe */
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f34),
.driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_R2PCIE, 0),
},
+ { /* R3QPI0 Link 0 */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f36),
+ .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_R3QPI, 0),
+ },
+ { /* R3QPI0 Link 1 */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f37),
+ .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_R3QPI, 1),
+ },
+ { /* R3QPI1 Link 2 */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f3e),
+ .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_R3QPI, 2),
+ },
+ { /* QPI Port 0 filter */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f86),
+ .driver_data = UNCORE_PCI_DEV_DATA(UNCORE_EXTRA_PCI_DEV, 0),
+ },
+ { /* QPI Port 1 filter */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f96),
+ .driver_data = UNCORE_PCI_DEV_DATA(UNCORE_EXTRA_PCI_DEV, 1),
+ },
+ { /* QPI Port 2 filter */
+ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f46),
+ .driver_data = UNCORE_PCI_DEV_DATA(UNCORE_EXTRA_PCI_DEV, 2),
+ },
{ /* end: all zeroes */ }
};
@@ -2500,4 +3123,4 @@ int bdx_uncore_pci_init(void)
return 0;
}
-/* end of BDX-DE uncore support */
+/* end of BDX uncore support */
diff --git a/arch/x86/kernel/cpu/rdrand.c b/arch/x86/kernel/cpu/rdrand.c
index 136ac74dee82..819d94982e07 100644
--- a/arch/x86/kernel/cpu/rdrand.c
+++ b/arch/x86/kernel/cpu/rdrand.c
@@ -33,28 +33,27 @@ static int __init x86_rdrand_setup(char *s)
__setup("nordrand", x86_rdrand_setup);
/*
- * Force a reseed cycle; we are architecturally guaranteed a reseed
- * after no more than 512 128-bit chunks of random data. This also
- * acts as a test of the CPU capability.
+ * RDRAND has Built-In-Self-Test (BIST) that runs on every invocation.
+ * Run the instruction a few times as a sanity check.
+ * If it fails, it is simple to disable RDRAND here.
*/
-#define RESEED_LOOP ((512*128)/sizeof(unsigned long))
+#define SANITY_CHECK_LOOPS 8
void x86_init_rdrand(struct cpuinfo_x86 *c)
{
#ifdef CONFIG_ARCH_RANDOM
unsigned long tmp;
- int i, count, ok;
+ int i;
if (!cpu_has(c, X86_FEATURE_RDRAND))
- return; /* Nothing to do */
+ return;
- for (count = i = 0; i < RESEED_LOOP; i++) {
- ok = rdrand_long(&tmp);
- if (ok)
- count++;
+ for (i = 0; i < SANITY_CHECK_LOOPS; i++) {
+ if (!rdrand_long(&tmp)) {
+ clear_cpu_cap(c, X86_FEATURE_RDRAND);
+ printk_once(KERN_WARNING "rdrand: disabled\n");
+ return;
+ }
}
-
- if (count != RESEED_LOOP)
- clear_cpu_cap(c, X86_FEATURE_RDRAND);
#endif
}
diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c
index 608fb26c7254..8cb57df9398d 100644
--- a/arch/x86/kernel/cpu/scattered.c
+++ b/arch/x86/kernel/cpu/scattered.c
@@ -31,32 +31,12 @@ void init_scattered_cpuid_features(struct cpuinfo_x86 *c)
const struct cpuid_bit *cb;
static const struct cpuid_bit cpuid_bits[] = {
- { X86_FEATURE_DTHERM, CR_EAX, 0, 0x00000006, 0 },
- { X86_FEATURE_IDA, CR_EAX, 1, 0x00000006, 0 },
- { X86_FEATURE_ARAT, CR_EAX, 2, 0x00000006, 0 },
- { X86_FEATURE_PLN, CR_EAX, 4, 0x00000006, 0 },
- { X86_FEATURE_PTS, CR_EAX, 6, 0x00000006, 0 },
- { X86_FEATURE_HWP, CR_EAX, 7, 0x00000006, 0 },
- { X86_FEATURE_HWP_NOTIFY, CR_EAX, 8, 0x00000006, 0 },
- { X86_FEATURE_HWP_ACT_WINDOW, CR_EAX, 9, 0x00000006, 0 },
- { X86_FEATURE_HWP_EPP, CR_EAX,10, 0x00000006, 0 },
- { X86_FEATURE_HWP_PKG_REQ, CR_EAX,11, 0x00000006, 0 },
{ X86_FEATURE_INTEL_PT, CR_EBX,25, 0x00000007, 0 },
{ X86_FEATURE_APERFMPERF, CR_ECX, 0, 0x00000006, 0 },
{ X86_FEATURE_EPB, CR_ECX, 3, 0x00000006, 0 },
{ X86_FEATURE_HW_PSTATE, CR_EDX, 7, 0x80000007, 0 },
{ X86_FEATURE_CPB, CR_EDX, 9, 0x80000007, 0 },
{ X86_FEATURE_PROC_FEEDBACK, CR_EDX,11, 0x80000007, 0 },
- { X86_FEATURE_NPT, CR_EDX, 0, 0x8000000a, 0 },
- { X86_FEATURE_LBRV, CR_EDX, 1, 0x8000000a, 0 },
- { X86_FEATURE_SVML, CR_EDX, 2, 0x8000000a, 0 },
- { X86_FEATURE_NRIPS, CR_EDX, 3, 0x8000000a, 0 },
- { X86_FEATURE_TSCRATEMSR, CR_EDX, 4, 0x8000000a, 0 },
- { X86_FEATURE_VMCBCLEAN, CR_EDX, 5, 0x8000000a, 0 },
- { X86_FEATURE_FLUSHBYASID, CR_EDX, 6, 0x8000000a, 0 },
- { X86_FEATURE_DECODEASSISTS, CR_EDX, 7, 0x8000000a, 0 },
- { X86_FEATURE_PAUSEFILTER, CR_EDX,10, 0x8000000a, 0 },
- { X86_FEATURE_PFTHRESHOLD, CR_EDX,12, 0x8000000a, 0 },
{ 0, 0, 0, 0, 0 }
};
diff --git a/arch/x86/kernel/cpu/transmeta.c b/arch/x86/kernel/cpu/transmeta.c
index 3fa0e5ad86b4..252da7aceca6 100644
--- a/arch/x86/kernel/cpu/transmeta.c
+++ b/arch/x86/kernel/cpu/transmeta.c
@@ -12,7 +12,7 @@ static void early_init_transmeta(struct cpuinfo_x86 *c)
xlvl = cpuid_eax(0x80860000);
if ((xlvl & 0xffff0000) == 0x80860000) {
if (xlvl >= 0x80860001)
- c->x86_capability[2] = cpuid_edx(0x80860001);
+ c->x86_capability[CPUID_8086_0001_EDX] = cpuid_edx(0x80860001);
}
}
@@ -82,7 +82,7 @@ static void init_transmeta(struct cpuinfo_x86 *c)
/* Unhide possibly hidden capability flags */
rdmsr(0x80860004, cap_mask, uk);
wrmsr(0x80860004, ~0, uk);
- c->x86_capability[0] = cpuid_edx(0x00000001);
+ c->x86_capability[CPUID_1_EDX] = cpuid_edx(0x00000001);
wrmsr(0x80860004, cap_mask, uk);
/* All Transmeta CPUs have a constant TSC */
diff --git a/arch/x86/kernel/cpuid.c b/arch/x86/kernel/cpuid.c
index bd3507da39f0..2836de390f95 100644
--- a/arch/x86/kernel/cpuid.c
+++ b/arch/x86/kernel/cpuid.c
@@ -58,28 +58,6 @@ static void cpuid_smp_cpuid(void *cmd_block)
&cmd->eax, &cmd->ebx, &cmd->ecx, &cmd->edx);
}
-static loff_t cpuid_seek(struct file *file, loff_t offset, int orig)
-{
- loff_t ret;
- struct inode *inode = file->f_mapping->host;
-
- mutex_lock(&inode->i_mutex);
- switch (orig) {
- case 0:
- file->f_pos = offset;
- ret = file->f_pos;
- break;
- case 1:
- file->f_pos += offset;
- ret = file->f_pos;
- break;
- default:
- ret = -EINVAL;
- }
- mutex_unlock(&inode->i_mutex);
- return ret;
-}
-
static ssize_t cpuid_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
@@ -132,7 +110,7 @@ static int cpuid_open(struct inode *inode, struct file *file)
*/
static const struct file_operations cpuid_fops = {
.owner = THIS_MODULE,
- .llseek = cpuid_seek,
+ .llseek = no_seek_end_llseek,
.read = cpuid_read,
.open = cpuid_open,
};
diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c
index 2c1910f6717e..58f34319b29a 100644
--- a/arch/x86/kernel/crash.c
+++ b/arch/x86/kernel/crash.c
@@ -35,6 +35,7 @@
#include <asm/cpu.h>
#include <asm/reboot.h>
#include <asm/virtext.h>
+#include <asm/intel_pt.h>
/* Alignment required for elf header segment */
#define ELF_CORE_HEADER_ALIGN 4096
@@ -125,6 +126,11 @@ static void kdump_nmi_callback(int cpu, struct pt_regs *regs)
cpu_emergency_vmxoff();
cpu_emergency_svm_disable();
+ /*
+ * Disable Intel PT to stop its logging
+ */
+ cpu_emergency_stop_pt();
+
disable_local_APIC();
}
@@ -169,6 +175,11 @@ void native_machine_crash_shutdown(struct pt_regs *regs)
cpu_emergency_vmxoff();
cpu_emergency_svm_disable();
+ /*
+ * Disable Intel PT to stop its logging
+ */
+ cpu_emergency_stop_pt();
+
#ifdef CONFIG_X86_IO_APIC
/* Prevent crash_kexec() from deadlocking on ioapic_lock. */
ioapic_zap_locks();
diff --git a/arch/x86/kernel/fpu/init.c b/arch/x86/kernel/fpu/init.c
index be39b5fde4b9..6d9f0a7ef4c8 100644
--- a/arch/x86/kernel/fpu/init.c
+++ b/arch/x86/kernel/fpu/init.c
@@ -3,8 +3,11 @@
*/
#include <asm/fpu/internal.h>
#include <asm/tlbflush.h>
+#include <asm/setup.h>
+#include <asm/cmdline.h>
#include <linux/sched.h>
+#include <linux/init.h>
/*
* Initialize the TS bit in CR0 according to the style of context-switches
@@ -12,7 +15,7 @@
*/
static void fpu__init_cpu_ctx_switch(void)
{
- if (!cpu_has_eager_fpu)
+ if (!boot_cpu_has(X86_FEATURE_EAGER_FPU))
stts();
else
clts();
@@ -143,9 +146,18 @@ static void __init fpu__init_system_generic(void)
unsigned int xstate_size;
EXPORT_SYMBOL_GPL(xstate_size);
-/* Enforce that 'MEMBER' is the last field of 'TYPE': */
+/* Get alignment of the TYPE. */
+#define TYPE_ALIGN(TYPE) offsetof(struct { char x; TYPE test; }, test)
+
+/*
+ * Enforce that 'MEMBER' is the last field of 'TYPE'.
+ *
+ * Align the computed size with alignment of the TYPE,
+ * because that's how C aligns structs.
+ */
#define CHECK_MEMBER_AT_END_OF(TYPE, MEMBER) \
- BUILD_BUG_ON(sizeof(TYPE) != offsetofend(TYPE, MEMBER))
+ BUILD_BUG_ON(sizeof(TYPE) != ALIGN(offsetofend(TYPE, MEMBER), \
+ TYPE_ALIGN(TYPE)))
/*
* We append the 'struct fpu' to the task_struct:
@@ -188,7 +200,7 @@ static void __init fpu__init_task_struct_size(void)
*/
static void __init fpu__init_system_xstate_size_legacy(void)
{
- static int on_boot_cpu = 1;
+ static int on_boot_cpu __initdata = 1;
WARN_ON_FPU(!on_boot_cpu);
on_boot_cpu = 0;
@@ -261,24 +273,56 @@ static void __init fpu__init_system_xstate_size_legacy(void)
*/
static enum { AUTO, ENABLE, DISABLE } eagerfpu = AUTO;
-static int __init eager_fpu_setup(char *s)
+/*
+ * Find supported xfeatures based on cpu features and command-line input.
+ * This must be called after fpu__init_parse_early_param() is called and
+ * xfeatures_mask is enumerated.
+ */
+u64 __init fpu__get_supported_xfeatures_mask(void)
{
- if (!strcmp(s, "on"))
- eagerfpu = ENABLE;
- else if (!strcmp(s, "off"))
- eagerfpu = DISABLE;
- else if (!strcmp(s, "auto"))
- eagerfpu = AUTO;
- return 1;
+ /* Support all xfeatures known to us */
+ if (eagerfpu != DISABLE)
+ return XCNTXT_MASK;
+
+ /* Warning of xfeatures being disabled for no eagerfpu mode */
+ if (xfeatures_mask & XFEATURE_MASK_EAGER) {
+ pr_err("x86/fpu: eagerfpu switching disabled, disabling the following xstate features: 0x%llx.\n",
+ xfeatures_mask & XFEATURE_MASK_EAGER);
+ }
+
+ /* Return a mask that masks out all features requiring eagerfpu mode */
+ return ~XFEATURE_MASK_EAGER;
+}
+
+/*
+ * Disable features dependent on eagerfpu.
+ */
+static void __init fpu__clear_eager_fpu_features(void)
+{
+ setup_clear_cpu_cap(X86_FEATURE_MPX);
+ setup_clear_cpu_cap(X86_FEATURE_AVX);
+ setup_clear_cpu_cap(X86_FEATURE_AVX2);
+ setup_clear_cpu_cap(X86_FEATURE_AVX512F);
+ setup_clear_cpu_cap(X86_FEATURE_AVX512PF);
+ setup_clear_cpu_cap(X86_FEATURE_AVX512ER);
+ setup_clear_cpu_cap(X86_FEATURE_AVX512CD);
}
-__setup("eagerfpu=", eager_fpu_setup);
/*
* Pick the FPU context switching strategy:
+ *
+ * When eagerfpu is AUTO or ENABLE, we ensure it is ENABLE if either of
+ * the following is true:
+ *
+ * (1) the cpu has xsaveopt, as it has the optimization and doing eager
+ * FPU switching has a relatively low cost compared to a plain xsave;
+ * (2) the cpu has xsave features (e.g. MPX) that depend on eager FPU
+ * switching. Should the kernel boot with noxsaveopt, we support MPX
+ * with eager FPU switching at a higher cost.
*/
static void __init fpu__init_system_ctx_switch(void)
{
- static bool on_boot_cpu = 1;
+ static bool on_boot_cpu __initdata = 1;
WARN_ON_FPU(!on_boot_cpu);
on_boot_cpu = 0;
@@ -286,19 +330,11 @@ static void __init fpu__init_system_ctx_switch(void)
WARN_ON_FPU(current->thread.fpu.fpstate_active);
current_thread_info()->status = 0;
- /* Auto enable eagerfpu for xsaveopt */
- if (cpu_has_xsaveopt && eagerfpu != DISABLE)
+ if (boot_cpu_has(X86_FEATURE_XSAVEOPT) && eagerfpu != DISABLE)
eagerfpu = ENABLE;
- if (xfeatures_mask & XFEATURE_MASK_EAGER) {
- if (eagerfpu == DISABLE) {
- pr_err("x86/fpu: eagerfpu switching disabled, disabling the following xstate features: 0x%llx.\n",
- xfeatures_mask & XFEATURE_MASK_EAGER);
- xfeatures_mask &= ~XFEATURE_MASK_EAGER;
- } else {
- eagerfpu = ENABLE;
- }
- }
+ if (xfeatures_mask & XFEATURE_MASK_EAGER)
+ eagerfpu = ENABLE;
if (eagerfpu == ENABLE)
setup_force_cpu_cap(X86_FEATURE_EAGER_FPU);
@@ -307,11 +343,48 @@ static void __init fpu__init_system_ctx_switch(void)
}
/*
+ * We parse fpu parameters early because fpu__init_system() is executed
+ * before parse_early_param().
+ */
+static void __init fpu__init_parse_early_param(void)
+{
+ /*
+ * No need to check "eagerfpu=auto" again, since it is the
+ * initial default.
+ */
+ if (cmdline_find_option_bool(boot_command_line, "eagerfpu=off")) {
+ eagerfpu = DISABLE;
+ fpu__clear_eager_fpu_features();
+ } else if (cmdline_find_option_bool(boot_command_line, "eagerfpu=on")) {
+ eagerfpu = ENABLE;
+ }
+
+ if (cmdline_find_option_bool(boot_command_line, "no387"))
+ setup_clear_cpu_cap(X86_FEATURE_FPU);
+
+ if (cmdline_find_option_bool(boot_command_line, "nofxsr")) {
+ setup_clear_cpu_cap(X86_FEATURE_FXSR);
+ setup_clear_cpu_cap(X86_FEATURE_FXSR_OPT);
+ setup_clear_cpu_cap(X86_FEATURE_XMM);
+ }
+
+ if (cmdline_find_option_bool(boot_command_line, "noxsave"))
+ fpu__xstate_clear_all_cpu_caps();
+
+ if (cmdline_find_option_bool(boot_command_line, "noxsaveopt"))
+ setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT);
+
+ if (cmdline_find_option_bool(boot_command_line, "noxsaves"))
+ setup_clear_cpu_cap(X86_FEATURE_XSAVES);
+}
+
+/*
* 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_parse_early_param();
fpu__init_system_early_generic(c);
/*
@@ -335,62 +408,3 @@ void __init fpu__init_system(struct cpuinfo_x86 *c)
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;
-
- fpu__xstate_clear_all_cpu_caps();
-
- 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/xstate.c b/arch/x86/kernel/fpu/xstate.c
index 70fc312221fc..d425cda5ae6d 100644
--- a/arch/x86/kernel/fpu/xstate.c
+++ b/arch/x86/kernel/fpu/xstate.c
@@ -52,6 +52,7 @@ void fpu__xstate_clear_all_cpu_caps(void)
setup_clear_cpu_cap(X86_FEATURE_AVX512ER);
setup_clear_cpu_cap(X86_FEATURE_AVX512CD);
setup_clear_cpu_cap(X86_FEATURE_MPX);
+ setup_clear_cpu_cap(X86_FEATURE_XGETBV1);
}
/*
@@ -297,7 +298,7 @@ static void __init setup_xstate_comp(void)
*/
static void __init setup_init_fpu_buf(void)
{
- static int on_boot_cpu = 1;
+ static int on_boot_cpu __initdata = 1;
WARN_ON_FPU(!on_boot_cpu);
on_boot_cpu = 0;
@@ -608,7 +609,7 @@ static void fpu__init_disable_system_xstate(void)
void __init fpu__init_system_xstate(void)
{
unsigned int eax, ebx, ecx, edx;
- static int on_boot_cpu = 1;
+ static int on_boot_cpu __initdata = 1;
int err;
WARN_ON_FPU(!on_boot_cpu);
@@ -632,8 +633,7 @@ void __init fpu__init_system_xstate(void)
BUG();
}
- /* Support only the state known to the OS: */
- xfeatures_mask = xfeatures_mask & XCNTXT_MASK;
+ xfeatures_mask &= fpu__get_supported_xfeatures_mask();
/* Enable xstate instructions to be able to continue with initialization: */
fpu__init_cpu_xstate();
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
index 311bcf338f07..29408d6d6626 100644
--- a/arch/x86/kernel/ftrace.c
+++ b/arch/x86/kernel/ftrace.c
@@ -105,14 +105,14 @@ ftrace_modify_code_direct(unsigned long ip, unsigned const char *old_code,
{
unsigned char replaced[MCOUNT_INSN_SIZE];
+ ftrace_expected = old_code;
+
/*
- * Note: Due to modules and __init, code can
- * disappear and change, we need to protect against faulting
- * as well as code changing. We do this by using the
- * probe_kernel_* functions.
- *
- * No real locking needed, this code is run through
- * kstop_machine, or before SMP starts.
+ * Note:
+ * We are paranoid about modifying text, as if a bug was to happen, it
+ * could cause us to read or write to someplace that could cause harm.
+ * Carefully read and modify the code with probe_kernel_*(), and make
+ * sure what we read is what we expected it to be before modifying it.
*/
/* read the text we want to modify */
@@ -154,6 +154,8 @@ int ftrace_make_nop(struct module *mod,
if (addr == MCOUNT_ADDR)
return ftrace_modify_code_direct(rec->ip, old, new);
+ ftrace_expected = NULL;
+
/* Normal cases use add_brk_on_nop */
WARN_ONCE(1, "invalid use of ftrace_make_nop");
return -EINVAL;
@@ -220,6 +222,7 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
unsigned long addr)
{
WARN_ON(1);
+ ftrace_expected = NULL;
return -EINVAL;
}
@@ -314,6 +317,8 @@ static int add_break(unsigned long ip, const char *old)
if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE))
return -EFAULT;
+ ftrace_expected = old;
+
/* Make sure it is what we expect it to be */
if (memcmp(replaced, old, MCOUNT_INSN_SIZE) != 0)
return -EINVAL;
@@ -413,6 +418,8 @@ static int remove_breakpoint(struct dyn_ftrace *rec)
ftrace_addr = ftrace_get_addr_curr(rec);
nop = ftrace_call_replace(ip, ftrace_addr);
+ ftrace_expected = nop;
+
if (memcmp(&ins[1], &nop[1], MCOUNT_INSN_SIZE - 1) != 0)
return -EINVAL;
}
diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoint.c
index 50a3fad5b89f..2bcfb5f2bc44 100644
--- a/arch/x86/kernel/hw_breakpoint.c
+++ b/arch/x86/kernel/hw_breakpoint.c
@@ -300,6 +300,10 @@ static int arch_build_bp_info(struct perf_event *bp)
return -EINVAL;
if (bp->attr.bp_addr & (bp->attr.bp_len - 1))
return -EINVAL;
+
+ if (!boot_cpu_has(X86_FEATURE_BPEXT))
+ return -EOPNOTSUPP;
+
/*
* It's impossible to use a range breakpoint to fake out
* user vs kernel detection because bp_len - 1 can't
@@ -307,8 +311,6 @@ static int arch_build_bp_info(struct perf_event *bp)
* breakpoints, then we'll have to check for kprobe-blacklisted
* addresses anywhere in the range.
*/
- if (!cpu_has_bpext)
- return -EOPNOTSUPP;
info->mask = bp->attr.bp_len - 1;
info->len = X86_BREAKPOINT_LEN_1;
}
diff --git a/arch/x86/kernel/irq_work.c b/arch/x86/kernel/irq_work.c
index dc5fa6a1e8d6..3512ba607361 100644
--- a/arch/x86/kernel/irq_work.c
+++ b/arch/x86/kernel/irq_work.c
@@ -1,7 +1,7 @@
/*
* x86 specific code for irq_work
*
- * Copyright (C) 2010 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2010 Red Hat, Inc., Peter Zijlstra
*/
#include <linux/kernel.h>
diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c
index 2bd81e302427..72cef58693c7 100644
--- a/arch/x86/kernel/kvmclock.c
+++ b/arch/x86/kernel/kvmclock.c
@@ -45,6 +45,11 @@ early_param("no-kvmclock", parse_no_kvmclock);
static struct pvclock_vsyscall_time_info *hv_clock;
static struct pvclock_wall_clock wall_clock;
+struct pvclock_vsyscall_time_info *pvclock_pvti_cpu0_va(void)
+{
+ return hv_clock;
+}
+
/*
* The wallclock is the time of day when we booted. Since then, some time may
* have elapsed since the hypervisor wrote the data. So we try to account for
@@ -305,7 +310,6 @@ int __init kvm_setup_vsyscall_timeinfo(void)
{
#ifdef CONFIG_X86_64
int cpu;
- int ret;
u8 flags;
struct pvclock_vcpu_time_info *vcpu_time;
unsigned int size;
@@ -325,11 +329,6 @@ int __init kvm_setup_vsyscall_timeinfo(void)
return 1;
}
- if ((ret = pvclock_init_vsyscall(hv_clock, size))) {
- put_cpu();
- return ret;
- }
-
put_cpu();
kvm_clock.archdata.vclock_mode = VCLOCK_PVCLOCK;
diff --git a/arch/x86/kernel/livepatch.c b/arch/x86/kernel/livepatch.c
index d1d35ccffed3..92fc1a51f994 100644
--- a/arch/x86/kernel/livepatch.c
+++ b/arch/x86/kernel/livepatch.c
@@ -20,8 +20,6 @@
#include <linux/module.h>
#include <linux/uaccess.h>
-#include <asm/cacheflush.h>
-#include <asm/page_types.h>
#include <asm/elf.h>
#include <asm/livepatch.h>
@@ -38,11 +36,10 @@
int klp_write_module_reloc(struct module *mod, unsigned long type,
unsigned long loc, unsigned long value)
{
- int ret, numpages, size = 4;
- bool readonly;
+ size_t size = 4;
unsigned long val;
- unsigned long core = (unsigned long)mod->module_core;
- unsigned long core_size = mod->core_size;
+ unsigned long core = (unsigned long)mod->core_layout.base;
+ unsigned long core_size = mod->core_layout.size;
switch (type) {
case R_X86_64_NONE:
@@ -69,23 +66,5 @@ int klp_write_module_reloc(struct module *mod, unsigned long type,
/* loc does not point to any symbol inside the module */
return -EINVAL;
- readonly = false;
-
-#ifdef CONFIG_DEBUG_SET_MODULE_RONX
- if (loc < core + mod->core_ro_size)
- readonly = true;
-#endif
-
- /* determine if the relocation spans a page boundary */
- numpages = ((loc & PAGE_MASK) == ((loc + size) & PAGE_MASK)) ? 1 : 2;
-
- if (readonly)
- set_memory_rw(loc & PAGE_MASK, numpages);
-
- ret = probe_kernel_write((void *)loc, &val, size);
-
- if (readonly)
- set_memory_ro(loc & PAGE_MASK, numpages);
-
- return ret;
+ return probe_kernel_write((void *)loc, &val, size);
}
diff --git a/arch/x86/kernel/msr.c b/arch/x86/kernel/msr.c
index 113e70784854..64f9616f93f1 100644
--- a/arch/x86/kernel/msr.c
+++ b/arch/x86/kernel/msr.c
@@ -45,28 +45,6 @@
static struct class *msr_class;
-static loff_t msr_seek(struct file *file, loff_t offset, int orig)
-{
- loff_t ret;
- struct inode *inode = file_inode(file);
-
- mutex_lock(&inode->i_mutex);
- switch (orig) {
- case SEEK_SET:
- file->f_pos = offset;
- ret = file->f_pos;
- break;
- case SEEK_CUR:
- file->f_pos += offset;
- ret = file->f_pos;
- break;
- default:
- ret = -EINVAL;
- }
- mutex_unlock(&inode->i_mutex);
- return ret;
-}
-
static ssize_t msr_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
@@ -194,7 +172,7 @@ static int msr_open(struct inode *inode, struct file *file)
*/
static const struct file_operations msr_fops = {
.owner = THIS_MODULE,
- .llseek = msr_seek,
+ .llseek = no_seek_end_llseek,
.read = msr_read,
.write = msr_write,
.open = msr_open,
diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c
index 697f90db0e37..8a2cdd736fa4 100644
--- a/arch/x86/kernel/nmi.c
+++ b/arch/x86/kernel/nmi.c
@@ -29,6 +29,7 @@
#include <asm/mach_traps.h>
#include <asm/nmi.h>
#include <asm/x86_init.h>
+#include <asm/reboot.h>
#define CREATE_TRACE_POINTS
#include <trace/events/nmi.h>
@@ -231,7 +232,7 @@ pci_serr_error(unsigned char reason, struct pt_regs *regs)
#endif
if (panic_on_unrecovered_nmi)
- panic("NMI: Not continuing");
+ nmi_panic(regs, "NMI: Not continuing");
pr_emerg("Dazed and confused, but trying to continue\n");
@@ -255,8 +256,16 @@ io_check_error(unsigned char reason, struct pt_regs *regs)
reason, smp_processor_id());
show_regs(regs);
- if (panic_on_io_nmi)
- panic("NMI IOCK error: Not continuing");
+ if (panic_on_io_nmi) {
+ nmi_panic(regs, "NMI IOCK error: Not continuing");
+
+ /*
+ * If we end up here, it means we have received an NMI while
+ * processing panic(). Simply return without delaying and
+ * re-enabling NMIs.
+ */
+ return;
+ }
/* Re-enable the IOCK line, wait for a few seconds */
reason = (reason & NMI_REASON_CLEAR_MASK) | NMI_REASON_CLEAR_IOCHK;
@@ -297,7 +306,7 @@ unknown_nmi_error(unsigned char reason, struct pt_regs *regs)
pr_emerg("Do you have a strange power saving mode enabled?\n");
if (unknown_nmi_panic || panic_on_unrecovered_nmi)
- panic("NMI: Not continuing");
+ nmi_panic(regs, "NMI: Not continuing");
pr_emerg("Dazed and confused, but trying to continue\n");
}
@@ -348,8 +357,19 @@ static void default_do_nmi(struct pt_regs *regs)
return;
}
- /* Non-CPU-specific NMI: NMI sources can be processed on any CPU */
- raw_spin_lock(&nmi_reason_lock);
+ /*
+ * Non-CPU-specific NMI: NMI sources can be processed on any CPU.
+ *
+ * Another CPU may be processing panic routines while holding
+ * nmi_reason_lock. Check if the CPU issued the IPI for crash dumping,
+ * and if so, call its callback directly. If there is no CPU preparing
+ * crash dump, we simply loop here.
+ */
+ while (!raw_spin_trylock(&nmi_reason_lock)) {
+ run_crash_ipi_callback(regs);
+ cpu_relax();
+ }
+
reason = x86_platform.get_nmi_reason();
if (reason & NMI_REASON_MASK) {
diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c
index c2130aef3f9d..f08ac28b8136 100644
--- a/arch/x86/kernel/paravirt.c
+++ b/arch/x86/kernel/paravirt.c
@@ -74,16 +74,6 @@ void __init default_banner(void)
/* Undefined instruction for dealing with missing ops pointers. */
static const unsigned char ud2a[] = { 0x0f, 0x0b };
-unsigned paravirt_patch_nop(void)
-{
- return 0;
-}
-
-unsigned paravirt_patch_ignore(unsigned len)
-{
- return len;
-}
-
struct branch {
unsigned char opcode;
u32 delta;
@@ -133,7 +123,6 @@ static void *get_call_destination(u8 type)
.pv_time_ops = pv_time_ops,
.pv_cpu_ops = pv_cpu_ops,
.pv_irq_ops = pv_irq_ops,
- .pv_apic_ops = pv_apic_ops,
.pv_mmu_ops = pv_mmu_ops,
#ifdef CONFIG_PARAVIRT_SPINLOCKS
.pv_lock_ops = pv_lock_ops,
@@ -152,8 +141,7 @@ unsigned paravirt_patch_default(u8 type, u16 clobbers, void *insnbuf,
/* If there's no function, patch it with a ud2a (BUG) */
ret = paravirt_patch_insns(insnbuf, len, ud2a, ud2a+sizeof(ud2a));
else if (opfunc == _paravirt_nop)
- /* If the operation is a nop, then nop the callsite */
- ret = paravirt_patch_nop();
+ ret = 0;
/* identity functions just return their single argument */
else if (opfunc == _paravirt_ident_32)
@@ -162,10 +150,6 @@ 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 */
ret = paravirt_patch_jmp(insnbuf, opfunc, addr, len);
@@ -220,8 +204,6 @@ static u64 native_steal_clock(int cpu)
/* These are in entry.S */
extern void native_iret(void);
-extern void native_irq_enable_sysexit(void);
-extern void native_usergs_sysret32(void);
extern void native_usergs_sysret64(void);
static struct resource reserve_ioports = {
@@ -379,13 +361,7 @@ __visible struct pv_cpu_ops pv_cpu_ops = {
.load_sp0 = native_load_sp0,
-#if defined(CONFIG_X86_32)
- .irq_enable_sysexit = native_irq_enable_sysexit,
-#endif
#ifdef CONFIG_X86_64
-#ifdef CONFIG_IA32_EMULATION
- .usergs_sysret32 = native_usergs_sysret32,
-#endif
.usergs_sysret64 = native_usergs_sysret64,
#endif
.iret = native_iret,
@@ -403,12 +379,6 @@ NOKPROBE_SYMBOL(native_get_debugreg);
NOKPROBE_SYMBOL(native_set_debugreg);
NOKPROBE_SYMBOL(native_load_idt);
-struct pv_apic_ops pv_apic_ops = {
-#ifdef CONFIG_X86_LOCAL_APIC
- .startup_ipi_hook = paravirt_nop,
-#endif
-};
-
#if defined(CONFIG_X86_32) && !defined(CONFIG_X86_PAE)
/* 32-bit pagetable entries */
#define PTE_IDENT __PV_IS_CALLEE_SAVE(_paravirt_ident_32)
@@ -444,9 +414,6 @@ struct pv_mmu_ops pv_mmu_ops = {
.set_pmd = native_set_pmd,
.set_pmd_at = native_set_pmd_at,
.pte_update = paravirt_nop,
- .pte_update_defer = paravirt_nop,
- .pmd_update = paravirt_nop,
- .pmd_update_defer = paravirt_nop,
.ptep_modify_prot_start = __ptep_modify_prot_start,
.ptep_modify_prot_commit = __ptep_modify_prot_commit,
@@ -492,6 +459,5 @@ struct pv_mmu_ops pv_mmu_ops = {
EXPORT_SYMBOL_GPL(pv_time_ops);
EXPORT_SYMBOL (pv_cpu_ops);
EXPORT_SYMBOL (pv_mmu_ops);
-EXPORT_SYMBOL_GPL(pv_apic_ops);
EXPORT_SYMBOL_GPL(pv_info);
EXPORT_SYMBOL (pv_irq_ops);
diff --git a/arch/x86/kernel/paravirt_patch_32.c b/arch/x86/kernel/paravirt_patch_32.c
index c89f50a76e97..158dc0650d5d 100644
--- a/arch/x86/kernel/paravirt_patch_32.c
+++ b/arch/x86/kernel/paravirt_patch_32.c
@@ -5,7 +5,6 @@ DEF_NATIVE(pv_irq_ops, irq_enable, "sti");
DEF_NATIVE(pv_irq_ops, restore_fl, "push %eax; popf");
DEF_NATIVE(pv_irq_ops, save_fl, "pushf; pop %eax");
DEF_NATIVE(pv_cpu_ops, iret, "iret");
-DEF_NATIVE(pv_cpu_ops, irq_enable_sysexit, "sti; sysexit");
DEF_NATIVE(pv_mmu_ops, read_cr2, "mov %cr2, %eax");
DEF_NATIVE(pv_mmu_ops, write_cr3, "mov %eax, %cr3");
DEF_NATIVE(pv_mmu_ops, read_cr3, "mov %cr3, %eax");
@@ -46,7 +45,6 @@ unsigned native_patch(u8 type, u16 clobbers, void *ibuf,
PATCH_SITE(pv_irq_ops, restore_fl);
PATCH_SITE(pv_irq_ops, save_fl);
PATCH_SITE(pv_cpu_ops, iret);
- PATCH_SITE(pv_cpu_ops, irq_enable_sysexit);
PATCH_SITE(pv_mmu_ops, read_cr2);
PATCH_SITE(pv_mmu_ops, read_cr3);
PATCH_SITE(pv_mmu_ops, write_cr3);
diff --git a/arch/x86/kernel/paravirt_patch_64.c b/arch/x86/kernel/paravirt_patch_64.c
index 8aa05583bc42..e70087a04cc8 100644
--- a/arch/x86/kernel/paravirt_patch_64.c
+++ b/arch/x86/kernel/paravirt_patch_64.c
@@ -13,9 +13,7 @@ DEF_NATIVE(pv_mmu_ops, flush_tlb_single, "invlpg (%rdi)");
DEF_NATIVE(pv_cpu_ops, clts, "clts");
DEF_NATIVE(pv_cpu_ops, wbinvd, "wbinvd");
-DEF_NATIVE(pv_cpu_ops, irq_enable_sysexit, "swapgs; sti; sysexit");
DEF_NATIVE(pv_cpu_ops, usergs_sysret64, "swapgs; sysretq");
-DEF_NATIVE(pv_cpu_ops, usergs_sysret32, "swapgs; sysretl");
DEF_NATIVE(pv_cpu_ops, swapgs, "swapgs");
DEF_NATIVE(, mov32, "mov %edi, %eax");
@@ -55,7 +53,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, usergs_sysret32);
PATCH_SITE(pv_cpu_ops, usergs_sysret64);
PATCH_SITE(pv_cpu_ops, swapgs);
PATCH_SITE(pv_mmu_ops, read_cr2);
diff --git a/arch/x86/kernel/pci-calgary_64.c b/arch/x86/kernel/pci-calgary_64.c
index 0497f719977d..833b1d329c47 100644
--- a/arch/x86/kernel/pci-calgary_64.c
+++ b/arch/x86/kernel/pci-calgary_64.c
@@ -180,13 +180,13 @@ static void calioc2_dump_error_regs(struct iommu_table *tbl);
static void calgary_init_bitmap_from_tce_table(struct iommu_table *tbl);
static void get_tce_space_from_tar(void);
-static struct cal_chipset_ops calgary_chip_ops = {
+static const struct cal_chipset_ops calgary_chip_ops = {
.handle_quirks = calgary_handle_quirks,
.tce_cache_blast = calgary_tce_cache_blast,
.dump_error_regs = calgary_dump_error_regs
};
-static struct cal_chipset_ops calioc2_chip_ops = {
+static const struct cal_chipset_ops calioc2_chip_ops = {
.handle_quirks = calioc2_handle_quirks,
.tce_cache_blast = calioc2_tce_cache_blast,
.dump_error_regs = calioc2_dump_error_regs
diff --git a/arch/x86/kernel/pci-swiotlb.c b/arch/x86/kernel/pci-swiotlb.c
index adf0392d549a..7c577a178859 100644
--- a/arch/x86/kernel/pci-swiotlb.c
+++ b/arch/x86/kernel/pci-swiotlb.c
@@ -88,7 +88,7 @@ int __init pci_swiotlb_detect_4gb(void)
{
/* don't initialize swiotlb if iommu=off (no_iommu=1) */
#ifdef CONFIG_X86_64
- if (!no_iommu && max_pfn > MAX_DMA32_PFN)
+ if (!no_iommu && max_possible_pfn > MAX_DMA32_PFN)
swiotlb = 1;
#endif
return swiotlb;
diff --git a/arch/x86/kernel/pmem.c b/arch/x86/kernel/pmem.c
index 4f00b63d7ff3..14415aff1813 100644
--- a/arch/x86/kernel/pmem.c
+++ b/arch/x86/kernel/pmem.c
@@ -4,10 +4,22 @@
*/
#include <linux/platform_device.h>
#include <linux/module.h>
+#include <linux/ioport.h>
+
+static int found(u64 start, u64 end, void *data)
+{
+ return 1;
+}
static __init int register_e820_pmem(void)
{
+ char *pmem = "Persistent Memory (legacy)";
struct platform_device *pdev;
+ int rc;
+
+ rc = walk_iomem_res(pmem, IORESOURCE_MEM, 0, -1, NULL, found);
+ if (rc <= 0)
+ return 0;
/*
* See drivers/nvdimm/e820.c for the implementation, this is
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index e835d263a33b..b9d99e0f82c4 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -125,7 +125,7 @@ void release_thread(struct task_struct *dead_task)
if (dead_task->mm->context.ldt) {
pr_warn("WARNING: dead process %s still has LDT? <%p/%d>\n",
dead_task->comm,
- dead_task->mm->context.ldt,
+ dead_task->mm->context.ldt->entries,
dead_task->mm->context.ldt->size);
BUG();
}
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 558f50edebca..32e9d9cbb884 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -124,21 +124,6 @@ const char *regs_query_register_name(unsigned int offset)
return NULL;
}
-static const int arg_offs_table[] = {
-#ifdef CONFIG_X86_32
- [0] = offsetof(struct pt_regs, ax),
- [1] = offsetof(struct pt_regs, dx),
- [2] = offsetof(struct pt_regs, cx)
-#else /* CONFIG_X86_64 */
- [0] = offsetof(struct pt_regs, di),
- [1] = offsetof(struct pt_regs, si),
- [2] = offsetof(struct pt_regs, dx),
- [3] = offsetof(struct pt_regs, cx),
- [4] = offsetof(struct pt_regs, r8),
- [5] = offsetof(struct pt_regs, r9)
-#endif
-};
-
/*
* does not yet catch signals sent when the child dies.
* in exit.c or in signal.c.
diff --git a/arch/x86/kernel/pvclock.c b/arch/x86/kernel/pvclock.c
index 2f355d229a58..99bfc025111d 100644
--- a/arch/x86/kernel/pvclock.c
+++ b/arch/x86/kernel/pvclock.c
@@ -140,27 +140,3 @@ void pvclock_read_wallclock(struct pvclock_wall_clock *wall_clock,
set_normalized_timespec(ts, now.tv_sec, now.tv_nsec);
}
-
-#ifdef CONFIG_X86_64
-/*
- * Initialize the generic pvclock vsyscall state. This will allocate
- * a/some page(s) for the per-vcpu pvclock information, set up a
- * fixmap mapping for the page(s)
- */
-
-int __init pvclock_init_vsyscall(struct pvclock_vsyscall_time_info *i,
- int size)
-{
- int idx;
-
- WARN_ON (size != PVCLOCK_VSYSCALL_NR_PAGES*PAGE_SIZE);
-
- for (idx = 0; idx <= (PVCLOCK_FIXMAP_END-PVCLOCK_FIXMAP_BEGIN); idx++) {
- __set_fixmap(PVCLOCK_FIXMAP_BEGIN + idx,
- __pa(i) + (idx*PAGE_SIZE),
- PAGE_KERNEL_VVAR);
- }
-
- return 0;
-}
-#endif
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index 02693dd9a079..ab0adc0fa5db 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -182,6 +182,14 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "iMac9,1"),
},
},
+ { /* Handle problems with rebooting on the iMac10,1. */
+ .callback = set_pci_reboot,
+ .ident = "Apple iMac10,1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "iMac10,1"),
+ },
+ },
/* ASRock */
{ /* Handle problems with rebooting on ASRock Q1900DC-ITX */
@@ -718,6 +726,7 @@ static int crashing_cpu;
static nmi_shootdown_cb shootdown_callback;
static atomic_t waiting_for_crash_ipi;
+static int crash_ipi_issued;
static int crash_nmi_callback(unsigned int val, struct pt_regs *regs)
{
@@ -780,6 +789,9 @@ void nmi_shootdown_cpus(nmi_shootdown_cb callback)
smp_send_nmi_allbutself();
+ /* Kick CPUs looping in NMI context. */
+ WRITE_ONCE(crash_ipi_issued, 1);
+
msecs = 1000; /* Wait at most a second for the other cpus to stop */
while ((atomic_read(&waiting_for_crash_ipi) > 0) && msecs) {
mdelay(1);
@@ -788,9 +800,35 @@ void nmi_shootdown_cpus(nmi_shootdown_cb callback)
/* Leave the nmi callback set */
}
+
+/*
+ * Check if the crash dumping IPI got issued and if so, call its callback
+ * directly. This function is used when we have already been in NMI handler.
+ * It doesn't return.
+ */
+void run_crash_ipi_callback(struct pt_regs *regs)
+{
+ if (crash_ipi_issued)
+ crash_nmi_callback(0, regs);
+}
+
+/* Override the weak function in kernel/panic.c */
+void nmi_panic_self_stop(struct pt_regs *regs)
+{
+ while (1) {
+ /* If no CPU is preparing crash dump, we simply loop here. */
+ run_crash_ipi_callback(regs);
+ cpu_relax();
+ }
+}
+
#else /* !CONFIG_SMP */
void nmi_shootdown_cpus(nmi_shootdown_cb callback)
{
/* No other CPUs to shoot down */
}
+
+void run_crash_ipi_callback(struct pt_regs *regs)
+{
+}
#endif
diff --git a/arch/x86/kernel/rtc.c b/arch/x86/kernel/rtc.c
index cd9685235df9..4af8d063fb36 100644
--- a/arch/x86/kernel/rtc.c
+++ b/arch/x86/kernel/rtc.c
@@ -200,6 +200,9 @@ static __init int add_rtc_cmos(void)
}
#endif
+ if (paravirt_enabled() && !paravirt_has(RTC))
+ return -ENODEV;
+
platform_device_register(&rtc_device);
dev_info(&rtc_device.dev,
"registered platform RTC device (no PNP device found)\n");
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 29db25f9a745..d3d80e6d42a2 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -1048,6 +1048,8 @@ void __init setup_arch(char **cmdline_p)
if (mtrr_trim_uncached_memory(max_pfn))
max_pfn = e820_end_of_ram_pfn();
+ max_possible_pfn = max_pfn;
+
#ifdef CONFIG_X86_32
/* max_low_pfn get updated here */
find_low_pfn_range();
@@ -1250,8 +1252,6 @@ void __init setup_arch(char **cmdline_p)
if (efi_enabled(EFI_BOOT))
efi_apply_memmap_quirks();
#endif
-
- microcode_init();
}
#ifdef CONFIG_X86_32
diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
index b7ffb7c00075..cb6282c3638f 100644
--- a/arch/x86/kernel/signal.c
+++ b/arch/x86/kernel/signal.c
@@ -690,12 +690,15 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)
signal_setup_done(failed, ksig, stepping);
}
-#ifdef CONFIG_X86_32
-#define NR_restart_syscall __NR_restart_syscall
-#else /* !CONFIG_X86_32 */
-#define NR_restart_syscall \
- test_thread_flag(TIF_IA32) ? __NR_ia32_restart_syscall : __NR_restart_syscall
-#endif /* CONFIG_X86_32 */
+static inline unsigned long get_nr_restart_syscall(const struct pt_regs *regs)
+{
+#if defined(CONFIG_X86_32) || !defined(CONFIG_X86_64)
+ return __NR_restart_syscall;
+#else /* !CONFIG_X86_32 && CONFIG_X86_64 */
+ return test_thread_flag(TIF_IA32) ? __NR_ia32_restart_syscall :
+ __NR_restart_syscall | (regs->orig_ax & __X32_SYSCALL_BIT);
+#endif /* CONFIG_X86_32 || !CONFIG_X86_64 */
+}
/*
* Note that 'init' is a special process: it doesn't get signals it doesn't
@@ -724,7 +727,7 @@ void do_signal(struct pt_regs *regs)
break;
case -ERESTART_RESTARTBLOCK:
- regs->ax = NR_restart_syscall;
+ regs->ax = get_nr_restart_syscall(regs);
regs->ip -= 2;
break;
}
diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c
index 12c8286206ce..658777cf3851 100644
--- a/arch/x86/kernel/smp.c
+++ b/arch/x86/kernel/smp.c
@@ -125,12 +125,12 @@ static void native_smp_send_reschedule(int cpu)
WARN_ON(1);
return;
}
- apic->send_IPI_mask(cpumask_of(cpu), RESCHEDULE_VECTOR);
+ apic->send_IPI(cpu, RESCHEDULE_VECTOR);
}
void native_send_call_func_single_ipi(int cpu)
{
- apic->send_IPI_mask(cpumask_of(cpu), CALL_FUNCTION_SINGLE_VECTOR);
+ apic->send_IPI(cpu, CALL_FUNCTION_SINGLE_VECTOR);
}
void native_send_call_func_ipi(const struct cpumask *mask)
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 892ee2e5ecbc..24d57f77b3c1 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -304,7 +304,7 @@ do { \
static bool match_smt(struct cpuinfo_x86 *c, struct cpuinfo_x86 *o)
{
- if (cpu_has_topoext) {
+ if (boot_cpu_has(X86_FEATURE_TOPOEXT)) {
int cpu1 = c->cpu_index, cpu2 = o->cpu_index;
if (c->phys_proc_id == o->phys_proc_id &&
@@ -509,7 +509,7 @@ void __inquire_remote_apic(int apicid)
*/
#define UDELAY_10MS_DEFAULT 10000
-static unsigned int init_udelay = INT_MAX;
+static unsigned int init_udelay = UINT_MAX;
static int __init cpu_init_udelay(char *str)
{
@@ -522,14 +522,15 @@ 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 != INT_MAX)
+ if (init_udelay != UINT_MAX)
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)))
+ ((boot_cpu_data.x86_vendor == X86_VENDOR_AMD) && (boot_cpu_data.x86 >= 0xF))) {
init_udelay = 0;
-
+ return;
+ }
/* else, use legacy delay */
init_udelay = UDELAY_10MS_DEFAULT;
}
@@ -629,13 +630,6 @@ wakeup_secondary_cpu_via_init(int phys_apicid, unsigned long start_eip)
num_starts = 0;
/*
- * Paravirt / VMI wants a startup IPI hook here to set up the
- * target processor state.
- */
- startup_ipi_hook(phys_apicid, (unsigned long) start_secondary,
- stack_start);
-
- /*
* Run STARTUP IPI loop.
*/
pr_debug("#startup loops: %d\n", num_starts);
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
index c7c4d9c51e99..3d743da828d3 100644
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c
@@ -1185,8 +1185,6 @@ void __init tsc_init(void)
u64 lpj;
int cpu;
- x86_init.timers.tsc_pre_init();
-
if (!cpu_has_tsc) {
setup_clear_cpu_cap(X86_FEATURE_TSC_DEADLINE_TIMER);
return;
diff --git a/arch/x86/kernel/verify_cpu.S b/arch/x86/kernel/verify_cpu.S
index 4cf401f581e7..07efb35ee4bc 100644
--- a/arch/x86/kernel/verify_cpu.S
+++ b/arch/x86/kernel/verify_cpu.S
@@ -48,31 +48,31 @@ verify_cpu:
pushfl
popl %eax
cmpl %eax,%ebx
- jz verify_cpu_no_longmode # cpu has no cpuid
+ jz .Lverify_cpu_no_longmode # cpu has no cpuid
#endif
movl $0x0,%eax # See if cpuid 1 is implemented
cpuid
cmpl $0x1,%eax
- jb verify_cpu_no_longmode # no cpuid 1
+ jb .Lverify_cpu_no_longmode # no cpuid 1
xor %di,%di
cmpl $0x68747541,%ebx # AuthenticAMD
- jnz verify_cpu_noamd
+ jnz .Lverify_cpu_noamd
cmpl $0x69746e65,%edx
- jnz verify_cpu_noamd
+ jnz .Lverify_cpu_noamd
cmpl $0x444d4163,%ecx
- jnz verify_cpu_noamd
+ jnz .Lverify_cpu_noamd
mov $1,%di # cpu is from AMD
- jmp verify_cpu_check
+ jmp .Lverify_cpu_check
-verify_cpu_noamd:
+.Lverify_cpu_noamd:
cmpl $0x756e6547,%ebx # GenuineIntel?
- jnz verify_cpu_check
+ jnz .Lverify_cpu_check
cmpl $0x49656e69,%edx
- jnz verify_cpu_check
+ jnz .Lverify_cpu_check
cmpl $0x6c65746e,%ecx
- jnz verify_cpu_check
+ jnz .Lverify_cpu_check
# only call IA32_MISC_ENABLE when:
# family > 6 || (family == 6 && model >= 0xd)
@@ -83,59 +83,59 @@ verify_cpu_noamd:
andl $0x0ff00f00, %eax # mask family and extended family
shrl $8, %eax
cmpl $6, %eax
- ja verify_cpu_clear_xd # family > 6, ok
- jb verify_cpu_check # family < 6, skip
+ ja .Lverify_cpu_clear_xd # family > 6, ok
+ jb .Lverify_cpu_check # family < 6, skip
andl $0x000f00f0, %ecx # mask model and extended model
shrl $4, %ecx
cmpl $0xd, %ecx
- jb verify_cpu_check # family == 6, model < 0xd, skip
+ jb .Lverify_cpu_check # family == 6, model < 0xd, skip
-verify_cpu_clear_xd:
+.Lverify_cpu_clear_xd:
movl $MSR_IA32_MISC_ENABLE, %ecx
rdmsr
btrl $2, %edx # clear MSR_IA32_MISC_ENABLE_XD_DISABLE
- jnc verify_cpu_check # only write MSR if bit was changed
+ jnc .Lverify_cpu_check # only write MSR if bit was changed
wrmsr
-verify_cpu_check:
+.Lverify_cpu_check:
movl $0x1,%eax # Does the cpu have what it takes
cpuid
andl $REQUIRED_MASK0,%edx
xorl $REQUIRED_MASK0,%edx
- jnz verify_cpu_no_longmode
+ jnz .Lverify_cpu_no_longmode
movl $0x80000000,%eax # See if extended cpuid is implemented
cpuid
cmpl $0x80000001,%eax
- jb verify_cpu_no_longmode # no extended cpuid
+ jb .Lverify_cpu_no_longmode # no extended cpuid
movl $0x80000001,%eax # Does the cpu have what it takes
cpuid
andl $REQUIRED_MASK1,%edx
xorl $REQUIRED_MASK1,%edx
- jnz verify_cpu_no_longmode
+ jnz .Lverify_cpu_no_longmode
-verify_cpu_sse_test:
+.Lverify_cpu_sse_test:
movl $1,%eax
cpuid
andl $SSE_MASK,%edx
cmpl $SSE_MASK,%edx
- je verify_cpu_sse_ok
+ je .Lverify_cpu_sse_ok
test %di,%di
- jz verify_cpu_no_longmode # only try to force SSE on AMD
+ jz .Lverify_cpu_no_longmode # only try to force SSE on AMD
movl $MSR_K7_HWCR,%ecx
rdmsr
btr $15,%eax # enable SSE
wrmsr
xor %di,%di # don't loop
- jmp verify_cpu_sse_test # try again
+ jmp .Lverify_cpu_sse_test # try again
-verify_cpu_no_longmode:
+.Lverify_cpu_no_longmode:
popf # Restore caller passed flags
movl $1,%eax
ret
-verify_cpu_sse_ok:
+.Lverify_cpu_sse_ok:
popf # Restore caller passed flags
xorl %eax, %eax
ret
diff --git a/arch/x86/kernel/vm86_32.c b/arch/x86/kernel/vm86_32.c
index 524619351961..483231ebbb0b 100644
--- a/arch/x86/kernel/vm86_32.c
+++ b/arch/x86/kernel/vm86_32.c
@@ -357,8 +357,10 @@ static long do_sys_vm86(struct vm86plus_struct __user *user_vm86, bool plus)
tss = &per_cpu(cpu_tss, get_cpu());
/* make room for real-mode segments */
tsk->thread.sp0 += 16;
- if (cpu_has_sep)
+
+ if (static_cpu_has_safe(X86_FEATURE_SEP))
tsk->thread.sysenter_cs = 0;
+
load_sp0(tss, &tsk->thread);
put_cpu();
diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c
index 3839628d962e..dad5fe9633a3 100644
--- a/arch/x86/kernel/x86_init.c
+++ b/arch/x86/kernel/x86_init.c
@@ -68,7 +68,6 @@ struct x86_init_ops x86_init __initdata = {
.timers = {
.setup_percpu_clockev = setup_boot_APIC_clock,
- .tsc_pre_init = x86_init_noop,
.timer_init = hpet_time_init,
.wallclock_init = x86_init_noop,
},
diff --git a/arch/x86/kvm/cpuid.h b/arch/x86/kvm/cpuid.h
index 06332cb7e7d1..c8eda1498121 100644
--- a/arch/x86/kvm/cpuid.h
+++ b/arch/x86/kvm/cpuid.h
@@ -2,6 +2,7 @@
#define ARCH_X86_KVM_CPUID_H
#include "x86.h"
+#include <asm/cpu.h>
int kvm_update_cpuid(struct kvm_vcpu *vcpu);
struct kvm_cpuid_entry2 *kvm_find_cpuid_entry(struct kvm_vcpu *vcpu,
@@ -38,6 +39,14 @@ static inline bool guest_cpuid_has_xsave(struct kvm_vcpu *vcpu)
return best && (best->ecx & bit(X86_FEATURE_XSAVE));
}
+static inline bool guest_cpuid_has_mtrr(struct kvm_vcpu *vcpu)
+{
+ struct kvm_cpuid_entry2 *best;
+
+ best = kvm_find_cpuid_entry(vcpu, 1, 0);
+ return best && (best->edx & bit(X86_FEATURE_MTRR));
+}
+
static inline bool guest_cpuid_has_tsc_adjust(struct kvm_vcpu *vcpu)
{
struct kvm_cpuid_entry2 *best;
@@ -170,4 +179,37 @@ static inline bool guest_cpuid_has_nrips(struct kvm_vcpu *vcpu)
}
#undef BIT_NRIPS
+static inline int guest_cpuid_family(struct kvm_vcpu *vcpu)
+{
+ struct kvm_cpuid_entry2 *best;
+
+ best = kvm_find_cpuid_entry(vcpu, 0x1, 0);
+ if (!best)
+ return -1;
+
+ return x86_family(best->eax);
+}
+
+static inline int guest_cpuid_model(struct kvm_vcpu *vcpu)
+{
+ struct kvm_cpuid_entry2 *best;
+
+ best = kvm_find_cpuid_entry(vcpu, 0x1, 0);
+ if (!best)
+ return -1;
+
+ return x86_model(best->eax);
+}
+
+static inline int guest_cpuid_stepping(struct kvm_vcpu *vcpu)
+{
+ struct kvm_cpuid_entry2 *best;
+
+ best = kvm_find_cpuid_entry(vcpu, 0x1, 0);
+ if (!best)
+ return -1;
+
+ return x86_stepping(best->eax);
+}
+
#endif
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index 62cf8c915e95..c58ba67175ac 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -23,13 +23,665 @@
#include "x86.h"
#include "lapic.h"
+#include "ioapic.h"
#include "hyperv.h"
#include <linux/kvm_host.h>
+#include <linux/highmem.h>
+#include <asm/apicdef.h>
#include <trace/events/kvm.h>
#include "trace.h"
+static inline u64 synic_read_sint(struct kvm_vcpu_hv_synic *synic, int sint)
+{
+ return atomic64_read(&synic->sint[sint]);
+}
+
+static inline int synic_get_sint_vector(u64 sint_value)
+{
+ if (sint_value & HV_SYNIC_SINT_MASKED)
+ return -1;
+ return sint_value & HV_SYNIC_SINT_VECTOR_MASK;
+}
+
+static bool synic_has_vector_connected(struct kvm_vcpu_hv_synic *synic,
+ int vector)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(synic->sint); i++) {
+ if (synic_get_sint_vector(synic_read_sint(synic, i)) == vector)
+ return true;
+ }
+ return false;
+}
+
+static bool synic_has_vector_auto_eoi(struct kvm_vcpu_hv_synic *synic,
+ int vector)
+{
+ int i;
+ u64 sint_value;
+
+ for (i = 0; i < ARRAY_SIZE(synic->sint); i++) {
+ sint_value = synic_read_sint(synic, i);
+ if (synic_get_sint_vector(sint_value) == vector &&
+ sint_value & HV_SYNIC_SINT_AUTO_EOI)
+ return true;
+ }
+ return false;
+}
+
+static int synic_set_sint(struct kvm_vcpu_hv_synic *synic, int sint,
+ u64 data, bool host)
+{
+ int vector;
+
+ vector = data & HV_SYNIC_SINT_VECTOR_MASK;
+ if (vector < 16 && !host)
+ return 1;
+ /*
+ * Guest may configure multiple SINTs to use the same vector, so
+ * we maintain a bitmap of vectors handled by synic, and a
+ * bitmap of vectors with auto-eoi behavior. The bitmaps are
+ * updated here, and atomically queried on fast paths.
+ */
+
+ atomic64_set(&synic->sint[sint], data);
+
+ if (synic_has_vector_connected(synic, vector))
+ __set_bit(vector, synic->vec_bitmap);
+ else
+ __clear_bit(vector, synic->vec_bitmap);
+
+ if (synic_has_vector_auto_eoi(synic, vector))
+ __set_bit(vector, synic->auto_eoi_bitmap);
+ else
+ __clear_bit(vector, synic->auto_eoi_bitmap);
+
+ /* Load SynIC vectors into EOI exit bitmap */
+ kvm_make_request(KVM_REQ_SCAN_IOAPIC, synic_to_vcpu(synic));
+ return 0;
+}
+
+static struct kvm_vcpu_hv_synic *synic_get(struct kvm *kvm, u32 vcpu_id)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vcpu_hv_synic *synic;
+
+ if (vcpu_id >= atomic_read(&kvm->online_vcpus))
+ return NULL;
+ vcpu = kvm_get_vcpu(kvm, vcpu_id);
+ if (!vcpu)
+ return NULL;
+ synic = vcpu_to_synic(vcpu);
+ return (synic->active) ? synic : NULL;
+}
+
+static void synic_clear_sint_msg_pending(struct kvm_vcpu_hv_synic *synic,
+ u32 sint)
+{
+ struct kvm_vcpu *vcpu = synic_to_vcpu(synic);
+ struct page *page;
+ gpa_t gpa;
+ struct hv_message *msg;
+ struct hv_message_page *msg_page;
+
+ gpa = synic->msg_page & PAGE_MASK;
+ page = kvm_vcpu_gfn_to_page(vcpu, gpa >> PAGE_SHIFT);
+ if (is_error_page(page)) {
+ vcpu_err(vcpu, "Hyper-V SynIC can't get msg page, gpa 0x%llx\n",
+ gpa);
+ return;
+ }
+ msg_page = kmap_atomic(page);
+
+ msg = &msg_page->sint_message[sint];
+ msg->header.message_flags.msg_pending = 0;
+
+ kunmap_atomic(msg_page);
+ kvm_release_page_dirty(page);
+ kvm_vcpu_mark_page_dirty(vcpu, gpa >> PAGE_SHIFT);
+}
+
+static void kvm_hv_notify_acked_sint(struct kvm_vcpu *vcpu, u32 sint)
+{
+ struct kvm *kvm = vcpu->kvm;
+ struct kvm_vcpu_hv_synic *synic = vcpu_to_synic(vcpu);
+ struct kvm_vcpu_hv *hv_vcpu = vcpu_to_hv_vcpu(vcpu);
+ struct kvm_vcpu_hv_stimer *stimer;
+ int gsi, idx, stimers_pending;
+
+ trace_kvm_hv_notify_acked_sint(vcpu->vcpu_id, sint);
+
+ if (synic->msg_page & HV_SYNIC_SIMP_ENABLE)
+ synic_clear_sint_msg_pending(synic, sint);
+
+ /* Try to deliver pending Hyper-V SynIC timers messages */
+ stimers_pending = 0;
+ for (idx = 0; idx < ARRAY_SIZE(hv_vcpu->stimer); idx++) {
+ stimer = &hv_vcpu->stimer[idx];
+ if (stimer->msg_pending &&
+ (stimer->config & HV_STIMER_ENABLE) &&
+ HV_STIMER_SINT(stimer->config) == sint) {
+ set_bit(stimer->index,
+ hv_vcpu->stimer_pending_bitmap);
+ stimers_pending++;
+ }
+ }
+ if (stimers_pending)
+ kvm_make_request(KVM_REQ_HV_STIMER, vcpu);
+
+ idx = srcu_read_lock(&kvm->irq_srcu);
+ gsi = atomic_read(&synic->sint_to_gsi[sint]);
+ if (gsi != -1)
+ kvm_notify_acked_gsi(kvm, gsi);
+ srcu_read_unlock(&kvm->irq_srcu, idx);
+}
+
+static void synic_exit(struct kvm_vcpu_hv_synic *synic, u32 msr)
+{
+ struct kvm_vcpu *vcpu = synic_to_vcpu(synic);
+ struct kvm_vcpu_hv *hv_vcpu = &vcpu->arch.hyperv;
+
+ hv_vcpu->exit.type = KVM_EXIT_HYPERV_SYNIC;
+ hv_vcpu->exit.u.synic.msr = msr;
+ hv_vcpu->exit.u.synic.control = synic->control;
+ hv_vcpu->exit.u.synic.evt_page = synic->evt_page;
+ hv_vcpu->exit.u.synic.msg_page = synic->msg_page;
+
+ kvm_make_request(KVM_REQ_HV_EXIT, vcpu);
+}
+
+static int synic_set_msr(struct kvm_vcpu_hv_synic *synic,
+ u32 msr, u64 data, bool host)
+{
+ struct kvm_vcpu *vcpu = synic_to_vcpu(synic);
+ int ret;
+
+ if (!synic->active)
+ return 1;
+
+ trace_kvm_hv_synic_set_msr(vcpu->vcpu_id, msr, data, host);
+
+ ret = 0;
+ switch (msr) {
+ case HV_X64_MSR_SCONTROL:
+ synic->control = data;
+ if (!host)
+ synic_exit(synic, msr);
+ break;
+ case HV_X64_MSR_SVERSION:
+ if (!host) {
+ ret = 1;
+ break;
+ }
+ synic->version = data;
+ break;
+ case HV_X64_MSR_SIEFP:
+ if (data & HV_SYNIC_SIEFP_ENABLE)
+ if (kvm_clear_guest(vcpu->kvm,
+ data & PAGE_MASK, PAGE_SIZE)) {
+ ret = 1;
+ break;
+ }
+ synic->evt_page = data;
+ if (!host)
+ synic_exit(synic, msr);
+ break;
+ case HV_X64_MSR_SIMP:
+ if (data & HV_SYNIC_SIMP_ENABLE)
+ if (kvm_clear_guest(vcpu->kvm,
+ data & PAGE_MASK, PAGE_SIZE)) {
+ ret = 1;
+ break;
+ }
+ synic->msg_page = data;
+ if (!host)
+ synic_exit(synic, msr);
+ break;
+ case HV_X64_MSR_EOM: {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(synic->sint); i++)
+ kvm_hv_notify_acked_sint(vcpu, i);
+ break;
+ }
+ case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
+ ret = synic_set_sint(synic, msr - HV_X64_MSR_SINT0, data, host);
+ break;
+ default:
+ ret = 1;
+ break;
+ }
+ return ret;
+}
+
+static int synic_get_msr(struct kvm_vcpu_hv_synic *synic, u32 msr, u64 *pdata)
+{
+ int ret;
+
+ if (!synic->active)
+ return 1;
+
+ ret = 0;
+ switch (msr) {
+ case HV_X64_MSR_SCONTROL:
+ *pdata = synic->control;
+ break;
+ case HV_X64_MSR_SVERSION:
+ *pdata = synic->version;
+ break;
+ case HV_X64_MSR_SIEFP:
+ *pdata = synic->evt_page;
+ break;
+ case HV_X64_MSR_SIMP:
+ *pdata = synic->msg_page;
+ break;
+ case HV_X64_MSR_EOM:
+ *pdata = 0;
+ break;
+ case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
+ *pdata = atomic64_read(&synic->sint[msr - HV_X64_MSR_SINT0]);
+ break;
+ default:
+ ret = 1;
+ break;
+ }
+ return ret;
+}
+
+int synic_set_irq(struct kvm_vcpu_hv_synic *synic, u32 sint)
+{
+ struct kvm_vcpu *vcpu = synic_to_vcpu(synic);
+ struct kvm_lapic_irq irq;
+ int ret, vector;
+
+ if (sint >= ARRAY_SIZE(synic->sint))
+ return -EINVAL;
+
+ vector = synic_get_sint_vector(synic_read_sint(synic, sint));
+ if (vector < 0)
+ return -ENOENT;
+
+ memset(&irq, 0, sizeof(irq));
+ irq.dest_id = kvm_apic_id(vcpu->arch.apic);
+ irq.dest_mode = APIC_DEST_PHYSICAL;
+ irq.delivery_mode = APIC_DM_FIXED;
+ irq.vector = vector;
+ irq.level = 1;
+
+ ret = kvm_irq_delivery_to_apic(vcpu->kvm, NULL, &irq, NULL);
+ trace_kvm_hv_synic_set_irq(vcpu->vcpu_id, sint, irq.vector, ret);
+ return ret;
+}
+
+int kvm_hv_synic_set_irq(struct kvm *kvm, u32 vcpu_id, u32 sint)
+{
+ struct kvm_vcpu_hv_synic *synic;
+
+ synic = synic_get(kvm, vcpu_id);
+ if (!synic)
+ return -EINVAL;
+
+ return synic_set_irq(synic, sint);
+}
+
+void kvm_hv_synic_send_eoi(struct kvm_vcpu *vcpu, int vector)
+{
+ struct kvm_vcpu_hv_synic *synic = vcpu_to_synic(vcpu);
+ int i;
+
+ trace_kvm_hv_synic_send_eoi(vcpu->vcpu_id, vector);
+
+ for (i = 0; i < ARRAY_SIZE(synic->sint); i++)
+ if (synic_get_sint_vector(synic_read_sint(synic, i)) == vector)
+ kvm_hv_notify_acked_sint(vcpu, i);
+}
+
+static int kvm_hv_set_sint_gsi(struct kvm *kvm, u32 vcpu_id, u32 sint, int gsi)
+{
+ struct kvm_vcpu_hv_synic *synic;
+
+ synic = synic_get(kvm, vcpu_id);
+ if (!synic)
+ return -EINVAL;
+
+ if (sint >= ARRAY_SIZE(synic->sint_to_gsi))
+ return -EINVAL;
+
+ atomic_set(&synic->sint_to_gsi[sint], gsi);
+ return 0;
+}
+
+void kvm_hv_irq_routing_update(struct kvm *kvm)
+{
+ struct kvm_irq_routing_table *irq_rt;
+ struct kvm_kernel_irq_routing_entry *e;
+ u32 gsi;
+
+ irq_rt = srcu_dereference_check(kvm->irq_routing, &kvm->irq_srcu,
+ lockdep_is_held(&kvm->irq_lock));
+
+ for (gsi = 0; gsi < irq_rt->nr_rt_entries; gsi++) {
+ hlist_for_each_entry(e, &irq_rt->map[gsi], link) {
+ if (e->type == KVM_IRQ_ROUTING_HV_SINT)
+ kvm_hv_set_sint_gsi(kvm, e->hv_sint.vcpu,
+ e->hv_sint.sint, gsi);
+ }
+ }
+}
+
+static void synic_init(struct kvm_vcpu_hv_synic *synic)
+{
+ int i;
+
+ memset(synic, 0, sizeof(*synic));
+ synic->version = HV_SYNIC_VERSION_1;
+ for (i = 0; i < ARRAY_SIZE(synic->sint); i++) {
+ atomic64_set(&synic->sint[i], HV_SYNIC_SINT_MASKED);
+ atomic_set(&synic->sint_to_gsi[i], -1);
+ }
+}
+
+static u64 get_time_ref_counter(struct kvm *kvm)
+{
+ return div_u64(get_kernel_ns() + kvm->arch.kvmclock_offset, 100);
+}
+
+static void stimer_mark_pending(struct kvm_vcpu_hv_stimer *stimer,
+ bool vcpu_kick)
+{
+ struct kvm_vcpu *vcpu = stimer_to_vcpu(stimer);
+
+ set_bit(stimer->index,
+ vcpu_to_hv_vcpu(vcpu)->stimer_pending_bitmap);
+ kvm_make_request(KVM_REQ_HV_STIMER, vcpu);
+ if (vcpu_kick)
+ kvm_vcpu_kick(vcpu);
+}
+
+static void stimer_cleanup(struct kvm_vcpu_hv_stimer *stimer)
+{
+ struct kvm_vcpu *vcpu = stimer_to_vcpu(stimer);
+
+ trace_kvm_hv_stimer_cleanup(stimer_to_vcpu(stimer)->vcpu_id,
+ stimer->index);
+
+ hrtimer_cancel(&stimer->timer);
+ clear_bit(stimer->index,
+ vcpu_to_hv_vcpu(vcpu)->stimer_pending_bitmap);
+ stimer->msg_pending = false;
+ stimer->exp_time = 0;
+}
+
+static enum hrtimer_restart stimer_timer_callback(struct hrtimer *timer)
+{
+ struct kvm_vcpu_hv_stimer *stimer;
+
+ stimer = container_of(timer, struct kvm_vcpu_hv_stimer, timer);
+ trace_kvm_hv_stimer_callback(stimer_to_vcpu(stimer)->vcpu_id,
+ stimer->index);
+ stimer_mark_pending(stimer, true);
+
+ return HRTIMER_NORESTART;
+}
+
+/*
+ * stimer_start() assumptions:
+ * a) stimer->count is not equal to 0
+ * b) stimer->config has HV_STIMER_ENABLE flag
+ */
+static int stimer_start(struct kvm_vcpu_hv_stimer *stimer)
+{
+ u64 time_now;
+ ktime_t ktime_now;
+
+ time_now = get_time_ref_counter(stimer_to_vcpu(stimer)->kvm);
+ ktime_now = ktime_get();
+
+ if (stimer->config & HV_STIMER_PERIODIC) {
+ if (stimer->exp_time) {
+ if (time_now >= stimer->exp_time) {
+ u64 remainder;
+
+ div64_u64_rem(time_now - stimer->exp_time,
+ stimer->count, &remainder);
+ stimer->exp_time =
+ time_now + (stimer->count - remainder);
+ }
+ } else
+ stimer->exp_time = time_now + stimer->count;
+
+ trace_kvm_hv_stimer_start_periodic(
+ stimer_to_vcpu(stimer)->vcpu_id,
+ stimer->index,
+ time_now, stimer->exp_time);
+
+ hrtimer_start(&stimer->timer,
+ ktime_add_ns(ktime_now,
+ 100 * (stimer->exp_time - time_now)),
+ HRTIMER_MODE_ABS);
+ return 0;
+ }
+ stimer->exp_time = stimer->count;
+ if (time_now >= stimer->count) {
+ /*
+ * Expire timer according to Hypervisor Top-Level Functional
+ * specification v4(15.3.1):
+ * "If a one shot is enabled and the specified count is in
+ * the past, it will expire immediately."
+ */
+ stimer_mark_pending(stimer, false);
+ return 0;
+ }
+
+ trace_kvm_hv_stimer_start_one_shot(stimer_to_vcpu(stimer)->vcpu_id,
+ stimer->index,
+ time_now, stimer->count);
+
+ hrtimer_start(&stimer->timer,
+ ktime_add_ns(ktime_now, 100 * (stimer->count - time_now)),
+ HRTIMER_MODE_ABS);
+ return 0;
+}
+
+static int stimer_set_config(struct kvm_vcpu_hv_stimer *stimer, u64 config,
+ bool host)
+{
+ trace_kvm_hv_stimer_set_config(stimer_to_vcpu(stimer)->vcpu_id,
+ stimer->index, config, host);
+
+ stimer_cleanup(stimer);
+ if ((stimer->config & HV_STIMER_ENABLE) && HV_STIMER_SINT(config) == 0)
+ config &= ~HV_STIMER_ENABLE;
+ stimer->config = config;
+ stimer_mark_pending(stimer, false);
+ return 0;
+}
+
+static int stimer_set_count(struct kvm_vcpu_hv_stimer *stimer, u64 count,
+ bool host)
+{
+ trace_kvm_hv_stimer_set_count(stimer_to_vcpu(stimer)->vcpu_id,
+ stimer->index, count, host);
+
+ stimer_cleanup(stimer);
+ stimer->count = count;
+ if (stimer->count == 0)
+ stimer->config &= ~HV_STIMER_ENABLE;
+ else if (stimer->config & HV_STIMER_AUTOENABLE)
+ stimer->config |= HV_STIMER_ENABLE;
+ stimer_mark_pending(stimer, false);
+ return 0;
+}
+
+static int stimer_get_config(struct kvm_vcpu_hv_stimer *stimer, u64 *pconfig)
+{
+ *pconfig = stimer->config;
+ return 0;
+}
+
+static int stimer_get_count(struct kvm_vcpu_hv_stimer *stimer, u64 *pcount)
+{
+ *pcount = stimer->count;
+ return 0;
+}
+
+static int synic_deliver_msg(struct kvm_vcpu_hv_synic *synic, u32 sint,
+ struct hv_message *src_msg)
+{
+ struct kvm_vcpu *vcpu = synic_to_vcpu(synic);
+ struct page *page;
+ gpa_t gpa;
+ struct hv_message *dst_msg;
+ int r;
+ struct hv_message_page *msg_page;
+
+ if (!(synic->msg_page & HV_SYNIC_SIMP_ENABLE))
+ return -ENOENT;
+
+ gpa = synic->msg_page & PAGE_MASK;
+ page = kvm_vcpu_gfn_to_page(vcpu, gpa >> PAGE_SHIFT);
+ if (is_error_page(page))
+ return -EFAULT;
+
+ msg_page = kmap_atomic(page);
+ dst_msg = &msg_page->sint_message[sint];
+ if (sync_cmpxchg(&dst_msg->header.message_type, HVMSG_NONE,
+ src_msg->header.message_type) != HVMSG_NONE) {
+ dst_msg->header.message_flags.msg_pending = 1;
+ r = -EAGAIN;
+ } else {
+ memcpy(&dst_msg->u.payload, &src_msg->u.payload,
+ src_msg->header.payload_size);
+ dst_msg->header.message_type = src_msg->header.message_type;
+ dst_msg->header.payload_size = src_msg->header.payload_size;
+ r = synic_set_irq(synic, sint);
+ if (r >= 1)
+ r = 0;
+ else if (r == 0)
+ r = -EFAULT;
+ }
+ kunmap_atomic(msg_page);
+ kvm_release_page_dirty(page);
+ kvm_vcpu_mark_page_dirty(vcpu, gpa >> PAGE_SHIFT);
+ return r;
+}
+
+static int stimer_send_msg(struct kvm_vcpu_hv_stimer *stimer)
+{
+ struct kvm_vcpu *vcpu = stimer_to_vcpu(stimer);
+ struct hv_message *msg = &stimer->msg;
+ struct hv_timer_message_payload *payload =
+ (struct hv_timer_message_payload *)&msg->u.payload;
+
+ payload->expiration_time = stimer->exp_time;
+ payload->delivery_time = get_time_ref_counter(vcpu->kvm);
+ return synic_deliver_msg(vcpu_to_synic(vcpu),
+ HV_STIMER_SINT(stimer->config), msg);
+}
+
+static void stimer_expiration(struct kvm_vcpu_hv_stimer *stimer)
+{
+ int r;
+
+ stimer->msg_pending = true;
+ r = stimer_send_msg(stimer);
+ trace_kvm_hv_stimer_expiration(stimer_to_vcpu(stimer)->vcpu_id,
+ stimer->index, r);
+ if (!r) {
+ stimer->msg_pending = false;
+ if (!(stimer->config & HV_STIMER_PERIODIC))
+ stimer->config &= ~HV_STIMER_ENABLE;
+ }
+}
+
+void kvm_hv_process_stimers(struct kvm_vcpu *vcpu)
+{
+ struct kvm_vcpu_hv *hv_vcpu = vcpu_to_hv_vcpu(vcpu);
+ struct kvm_vcpu_hv_stimer *stimer;
+ u64 time_now, exp_time;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hv_vcpu->stimer); i++)
+ if (test_and_clear_bit(i, hv_vcpu->stimer_pending_bitmap)) {
+ stimer = &hv_vcpu->stimer[i];
+ if (stimer->config & HV_STIMER_ENABLE) {
+ exp_time = stimer->exp_time;
+
+ if (exp_time) {
+ time_now =
+ get_time_ref_counter(vcpu->kvm);
+ if (time_now >= exp_time)
+ stimer_expiration(stimer);
+ }
+
+ if ((stimer->config & HV_STIMER_ENABLE) &&
+ stimer->count)
+ stimer_start(stimer);
+ else
+ stimer_cleanup(stimer);
+ }
+ }
+}
+
+void kvm_hv_vcpu_uninit(struct kvm_vcpu *vcpu)
+{
+ struct kvm_vcpu_hv *hv_vcpu = vcpu_to_hv_vcpu(vcpu);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hv_vcpu->stimer); i++)
+ stimer_cleanup(&hv_vcpu->stimer[i]);
+}
+
+static void stimer_prepare_msg(struct kvm_vcpu_hv_stimer *stimer)
+{
+ struct hv_message *msg = &stimer->msg;
+ struct hv_timer_message_payload *payload =
+ (struct hv_timer_message_payload *)&msg->u.payload;
+
+ memset(&msg->header, 0, sizeof(msg->header));
+ msg->header.message_type = HVMSG_TIMER_EXPIRED;
+ msg->header.payload_size = sizeof(*payload);
+
+ payload->timer_index = stimer->index;
+ payload->expiration_time = 0;
+ payload->delivery_time = 0;
+}
+
+static void stimer_init(struct kvm_vcpu_hv_stimer *stimer, int timer_index)
+{
+ memset(stimer, 0, sizeof(*stimer));
+ stimer->index = timer_index;
+ hrtimer_init(&stimer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ stimer->timer.function = stimer_timer_callback;
+ stimer_prepare_msg(stimer);
+}
+
+void kvm_hv_vcpu_init(struct kvm_vcpu *vcpu)
+{
+ struct kvm_vcpu_hv *hv_vcpu = vcpu_to_hv_vcpu(vcpu);
+ int i;
+
+ synic_init(&hv_vcpu->synic);
+
+ bitmap_zero(hv_vcpu->stimer_pending_bitmap, HV_SYNIC_STIMER_COUNT);
+ for (i = 0; i < ARRAY_SIZE(hv_vcpu->stimer); i++)
+ stimer_init(&hv_vcpu->stimer[i], i);
+}
+
+int kvm_hv_activate_synic(struct kvm_vcpu *vcpu)
+{
+ /*
+ * Hyper-V SynIC auto EOI SINT's are
+ * not compatible with APICV, so deactivate APICV
+ */
+ kvm_vcpu_deactivate_apicv(vcpu);
+ vcpu_to_synic(vcpu)->active = true;
+ return 0;
+}
+
static bool kvm_hv_msr_partition_wide(u32 msr)
{
bool r = false;
@@ -226,6 +878,31 @@ static int kvm_hv_set_msr(struct kvm_vcpu *vcpu, u32 msr, u64 data, bool host)
return 1;
hv->runtime_offset = data - current_task_runtime_100ns();
break;
+ case HV_X64_MSR_SCONTROL:
+ case HV_X64_MSR_SVERSION:
+ case HV_X64_MSR_SIEFP:
+ case HV_X64_MSR_SIMP:
+ case HV_X64_MSR_EOM:
+ case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
+ return synic_set_msr(vcpu_to_synic(vcpu), msr, data, host);
+ case HV_X64_MSR_STIMER0_CONFIG:
+ case HV_X64_MSR_STIMER1_CONFIG:
+ case HV_X64_MSR_STIMER2_CONFIG:
+ case HV_X64_MSR_STIMER3_CONFIG: {
+ int timer_index = (msr - HV_X64_MSR_STIMER0_CONFIG)/2;
+
+ return stimer_set_config(vcpu_to_stimer(vcpu, timer_index),
+ data, host);
+ }
+ case HV_X64_MSR_STIMER0_COUNT:
+ case HV_X64_MSR_STIMER1_COUNT:
+ case HV_X64_MSR_STIMER2_COUNT:
+ case HV_X64_MSR_STIMER3_COUNT: {
+ int timer_index = (msr - HV_X64_MSR_STIMER0_COUNT)/2;
+
+ return stimer_set_count(vcpu_to_stimer(vcpu, timer_index),
+ data, host);
+ }
default:
vcpu_unimpl(vcpu, "Hyper-V uhandled wrmsr: 0x%x data 0x%llx\n",
msr, data);
@@ -248,11 +925,9 @@ static int kvm_hv_get_msr_pw(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
case HV_X64_MSR_HYPERCALL:
data = hv->hv_hypercall;
break;
- case HV_X64_MSR_TIME_REF_COUNT: {
- data =
- div_u64(get_kernel_ns() + kvm->arch.kvmclock_offset, 100);
+ case HV_X64_MSR_TIME_REF_COUNT:
+ data = get_time_ref_counter(kvm);
break;
- }
case HV_X64_MSR_REFERENCE_TSC:
data = hv->hv_tsc_page;
break;
@@ -304,6 +979,31 @@ static int kvm_hv_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
case HV_X64_MSR_VP_RUNTIME:
data = current_task_runtime_100ns() + hv->runtime_offset;
break;
+ case HV_X64_MSR_SCONTROL:
+ case HV_X64_MSR_SVERSION:
+ case HV_X64_MSR_SIEFP:
+ case HV_X64_MSR_SIMP:
+ case HV_X64_MSR_EOM:
+ case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
+ return synic_get_msr(vcpu_to_synic(vcpu), msr, pdata);
+ case HV_X64_MSR_STIMER0_CONFIG:
+ case HV_X64_MSR_STIMER1_CONFIG:
+ case HV_X64_MSR_STIMER2_CONFIG:
+ case HV_X64_MSR_STIMER3_CONFIG: {
+ int timer_index = (msr - HV_X64_MSR_STIMER0_CONFIG)/2;
+
+ return stimer_get_config(vcpu_to_stimer(vcpu, timer_index),
+ pdata);
+ }
+ case HV_X64_MSR_STIMER0_COUNT:
+ case HV_X64_MSR_STIMER1_COUNT:
+ case HV_X64_MSR_STIMER2_COUNT:
+ case HV_X64_MSR_STIMER3_COUNT: {
+ int timer_index = (msr - HV_X64_MSR_STIMER0_COUNT)/2;
+
+ return stimer_get_count(vcpu_to_stimer(vcpu, timer_index),
+ pdata);
+ }
default:
vcpu_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr);
return 1;
diff --git a/arch/x86/kvm/hyperv.h b/arch/x86/kvm/hyperv.h
index c7bce559f67b..60eccd4bd1d3 100644
--- a/arch/x86/kvm/hyperv.h
+++ b/arch/x86/kvm/hyperv.h
@@ -24,9 +24,64 @@
#ifndef __ARCH_X86_KVM_HYPERV_H__
#define __ARCH_X86_KVM_HYPERV_H__
+static inline struct kvm_vcpu_hv *vcpu_to_hv_vcpu(struct kvm_vcpu *vcpu)
+{
+ return &vcpu->arch.hyperv;
+}
+
+static inline struct kvm_vcpu *hv_vcpu_to_vcpu(struct kvm_vcpu_hv *hv_vcpu)
+{
+ struct kvm_vcpu_arch *arch;
+
+ arch = container_of(hv_vcpu, struct kvm_vcpu_arch, hyperv);
+ return container_of(arch, struct kvm_vcpu, arch);
+}
+
+static inline struct kvm_vcpu_hv_synic *vcpu_to_synic(struct kvm_vcpu *vcpu)
+{
+ return &vcpu->arch.hyperv.synic;
+}
+
+static inline struct kvm_vcpu *synic_to_vcpu(struct kvm_vcpu_hv_synic *synic)
+{
+ return hv_vcpu_to_vcpu(container_of(synic, struct kvm_vcpu_hv, synic));
+}
+
int kvm_hv_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data, bool host);
int kvm_hv_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata);
+
bool kvm_hv_hypercall_enabled(struct kvm *kvm);
int kvm_hv_hypercall(struct kvm_vcpu *vcpu);
+void kvm_hv_irq_routing_update(struct kvm *kvm);
+int kvm_hv_synic_set_irq(struct kvm *kvm, u32 vcpu_id, u32 sint);
+void kvm_hv_synic_send_eoi(struct kvm_vcpu *vcpu, int vector);
+int kvm_hv_activate_synic(struct kvm_vcpu *vcpu);
+
+void kvm_hv_vcpu_init(struct kvm_vcpu *vcpu);
+void kvm_hv_vcpu_uninit(struct kvm_vcpu *vcpu);
+
+static inline struct kvm_vcpu_hv_stimer *vcpu_to_stimer(struct kvm_vcpu *vcpu,
+ int timer_index)
+{
+ return &vcpu_to_hv_vcpu(vcpu)->stimer[timer_index];
+}
+
+static inline struct kvm_vcpu *stimer_to_vcpu(struct kvm_vcpu_hv_stimer *stimer)
+{
+ struct kvm_vcpu_hv *hv_vcpu;
+
+ hv_vcpu = container_of(stimer - stimer->index, struct kvm_vcpu_hv,
+ stimer[0]);
+ return hv_vcpu_to_vcpu(hv_vcpu);
+}
+
+static inline bool kvm_hv_has_stimer_pending(struct kvm_vcpu *vcpu)
+{
+ return !bitmap_empty(vcpu->arch.hyperv.stimer_pending_bitmap,
+ HV_SYNIC_STIMER_COUNT);
+}
+
+void kvm_hv_process_stimers(struct kvm_vcpu *vcpu);
+
#endif
diff --git a/arch/x86/kvm/i8254.c b/arch/x86/kvm/i8254.c
index 08116ff227cc..b0ea42b78ccd 100644
--- a/arch/x86/kvm/i8254.c
+++ b/arch/x86/kvm/i8254.c
@@ -420,6 +420,7 @@ void kvm_pit_load_count(struct kvm *kvm, int channel, u32 val, int hpet_legacy_s
u8 saved_mode;
if (hpet_legacy_start) {
/* save existing mode for later reenablement */
+ WARN_ON(channel != 0);
saved_mode = kvm->arch.vpit->pit_state.channels[0].mode;
kvm->arch.vpit->pit_state.channels[0].mode = 0xff; /* disable timer */
pit_load_count(kvm, channel, val);
diff --git a/arch/x86/kvm/ioapic.c b/arch/x86/kvm/ioapic.c
index 88d0a92d3f94..1facfd60b04a 100644
--- a/arch/x86/kvm/ioapic.c
+++ b/arch/x86/kvm/ioapic.c
@@ -233,7 +233,7 @@ static void kvm_ioapic_inject_all(struct kvm_ioapic *ioapic, unsigned long irr)
}
-void kvm_ioapic_scan_entry(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap)
+void kvm_ioapic_scan_entry(struct kvm_vcpu *vcpu, ulong *ioapic_handled_vectors)
{
struct kvm_ioapic *ioapic = vcpu->kvm->arch.vioapic;
union kvm_ioapic_redirect_entry *e;
@@ -250,7 +250,7 @@ void kvm_ioapic_scan_entry(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap)
(e->fields.trig_mode == IOAPIC_EDGE_TRIG &&
kvm_apic_pending_eoi(vcpu, e->fields.vector)))
__set_bit(e->fields.vector,
- (unsigned long *)eoi_exit_bitmap);
+ ioapic_handled_vectors);
}
}
spin_unlock(&ioapic->lock);
diff --git a/arch/x86/kvm/ioapic.h b/arch/x86/kvm/ioapic.h
index 084617d37c74..2d16dc251d81 100644
--- a/arch/x86/kvm/ioapic.h
+++ b/arch/x86/kvm/ioapic.h
@@ -121,7 +121,8 @@ int kvm_irq_delivery_to_apic(struct kvm *kvm, struct kvm_lapic *src,
struct kvm_lapic_irq *irq, unsigned long *dest_map);
int kvm_get_ioapic(struct kvm *kvm, struct kvm_ioapic_state *state);
int kvm_set_ioapic(struct kvm *kvm, struct kvm_ioapic_state *state);
-void kvm_ioapic_scan_entry(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap);
-void kvm_scan_ioapic_routes(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap);
-
+void kvm_ioapic_scan_entry(struct kvm_vcpu *vcpu,
+ ulong *ioapic_handled_vectors);
+void kvm_scan_ioapic_routes(struct kvm_vcpu *vcpu,
+ ulong *ioapic_handled_vectors);
#endif
diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c
index 097060e33bd6..3982b479bb5f 100644
--- a/arch/x86/kvm/irq.c
+++ b/arch/x86/kvm/irq.c
@@ -76,7 +76,7 @@ int kvm_cpu_has_injectable_intr(struct kvm_vcpu *v)
if (kvm_cpu_has_extint(v))
return 1;
- if (kvm_vcpu_apic_vid_enabled(v))
+ if (kvm_vcpu_apicv_active(v))
return 0;
return kvm_apic_has_interrupt(v) != -1; /* LAPIC */
diff --git a/arch/x86/kvm/irq_comm.c b/arch/x86/kvm/irq_comm.c
index 84b96d319909..8fc89efb5250 100644
--- a/arch/x86/kvm/irq_comm.c
+++ b/arch/x86/kvm/irq_comm.c
@@ -33,6 +33,8 @@
#include "lapic.h"
+#include "hyperv.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)
@@ -219,6 +221,16 @@ void kvm_fire_mask_notifiers(struct kvm *kvm, unsigned irqchip, unsigned pin,
srcu_read_unlock(&kvm->irq_srcu, idx);
}
+static int kvm_hv_set_sint(struct kvm_kernel_irq_routing_entry *e,
+ struct kvm *kvm, int irq_source_id, int level,
+ bool line_status)
+{
+ if (!level)
+ return -1;
+
+ return kvm_hv_synic_set_irq(kvm, e->hv_sint.vcpu, e->hv_sint.sint);
+}
+
int kvm_set_routing_entry(struct kvm_kernel_irq_routing_entry *e,
const struct kvm_irq_routing_entry *ue)
{
@@ -257,6 +269,11 @@ int kvm_set_routing_entry(struct kvm_kernel_irq_routing_entry *e,
e->msi.address_hi = ue->u.msi.address_hi;
e->msi.data = ue->u.msi.data;
break;
+ case KVM_IRQ_ROUTING_HV_SINT:
+ e->set = kvm_hv_set_sint;
+ e->hv_sint.vcpu = ue->u.hv_sint.vcpu;
+ e->hv_sint.sint = ue->u.hv_sint.sint;
+ break;
default:
goto out;
}
@@ -332,14 +349,15 @@ int kvm_setup_empty_irq_routing(struct kvm *kvm)
return kvm_set_irq_routing(kvm, empty_routing, 0, 0);
}
-void kvm_arch_irq_routing_update(struct kvm *kvm)
+void kvm_arch_post_irq_routing_update(struct kvm *kvm)
{
if (ioapic_in_kernel(kvm) || !irqchip_in_kernel(kvm))
return;
kvm_make_scan_ioapic_request(kvm);
}
-void kvm_scan_ioapic_routes(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap)
+void kvm_scan_ioapic_routes(struct kvm_vcpu *vcpu,
+ ulong *ioapic_handled_vectors)
{
struct kvm *kvm = vcpu->kvm;
struct kvm_kernel_irq_routing_entry *entry;
@@ -369,9 +387,26 @@ void kvm_scan_ioapic_routes(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap)
u32 vector = entry->msi.data & 0xff;
__set_bit(vector,
- (unsigned long *) eoi_exit_bitmap);
+ ioapic_handled_vectors);
}
}
}
srcu_read_unlock(&kvm->irq_srcu, idx);
}
+
+int kvm_arch_set_irq(struct kvm_kernel_irq_routing_entry *irq, struct kvm *kvm,
+ int irq_source_id, int level, bool line_status)
+{
+ switch (irq->type) {
+ case KVM_IRQ_ROUTING_HV_SINT:
+ return kvm_hv_set_sint(irq, kvm, irq_source_id, level,
+ line_status);
+ default:
+ return -EWOULDBLOCK;
+ }
+}
+
+void kvm_arch_irq_routing_update(struct kvm *kvm)
+{
+ kvm_hv_irq_routing_update(kvm);
+}
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 4d30b865be30..36591faed13b 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -41,6 +41,7 @@
#include "trace.h"
#include "x86.h"
#include "cpuid.h"
+#include "hyperv.h"
#ifndef CONFIG_X86_64
#define mod_64(x, y) ((x) - (y) * div64_u64(x, y))
@@ -128,11 +129,6 @@ static inline int apic_enabled(struct kvm_lapic *apic)
(LVT_MASK | APIC_MODE_MASK | APIC_INPUT_POLARITY | \
APIC_LVT_REMOTE_IRR | APIC_LVT_LEVEL_TRIGGER)
-static inline int kvm_apic_id(struct kvm_lapic *apic)
-{
- return (kvm_apic_get_reg(apic, APIC_ID) >> 24) & 0xff;
-}
-
/* The logical map is definitely wrong if we have multiple
* modes at the same time. (Physical map is always right.)
*/
@@ -379,7 +375,8 @@ static inline int apic_find_highest_irr(struct kvm_lapic *apic)
if (!apic->irr_pending)
return -1;
- kvm_x86_ops->sync_pir_to_irr(apic->vcpu);
+ if (apic->vcpu->arch.apicv_active)
+ kvm_x86_ops->sync_pir_to_irr(apic->vcpu);
result = apic_search_irr(apic);
ASSERT(result == -1 || result >= 16);
@@ -392,7 +389,7 @@ static inline void apic_clear_irr(int vec, struct kvm_lapic *apic)
vcpu = apic->vcpu;
- if (unlikely(kvm_vcpu_apic_vid_enabled(vcpu))) {
+ if (unlikely(vcpu->arch.apicv_active)) {
/* try to update RVI */
apic_clear_vector(vec, apic->regs + APIC_IRR);
kvm_make_request(KVM_REQ_EVENT, vcpu);
@@ -418,7 +415,7 @@ static inline void apic_set_isr(int vec, struct kvm_lapic *apic)
* because the processor can modify ISR under the hood. Instead
* just set SVI.
*/
- if (unlikely(kvm_x86_ops->hwapic_isr_update))
+ if (unlikely(vcpu->arch.apicv_active))
kvm_x86_ops->hwapic_isr_update(vcpu->kvm, vec);
else {
++apic->isr_count;
@@ -466,7 +463,7 @@ static inline void apic_clear_isr(int vec, struct kvm_lapic *apic)
* on the other hand isr_count and highest_isr_cache are unused
* and must be left alone.
*/
- if (unlikely(kvm_x86_ops->hwapic_isr_update))
+ if (unlikely(vcpu->arch.apicv_active))
kvm_x86_ops->hwapic_isr_update(vcpu->kvm,
apic_find_highest_isr(apic));
else {
@@ -852,7 +849,7 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode,
apic_clear_vector(vector, apic->regs + APIC_TMR);
}
- if (kvm_x86_ops->deliver_posted_interrupt)
+ if (vcpu->arch.apicv_active)
kvm_x86_ops->deliver_posted_interrupt(vcpu, vector);
else {
apic_set_irr(vector, apic);
@@ -932,7 +929,7 @@ int kvm_apic_compare_prio(struct kvm_vcpu *vcpu1, struct kvm_vcpu *vcpu2)
static bool kvm_ioapic_handles_vector(struct kvm_lapic *apic, int vector)
{
- return test_bit(vector, (ulong *)apic->vcpu->arch.eoi_exit_bitmap);
+ return test_bit(vector, apic->vcpu->arch.ioapic_handled_vectors);
}
static void kvm_ioapic_send_eoi(struct kvm_lapic *apic, int vector)
@@ -974,6 +971,9 @@ static int apic_set_eoi(struct kvm_lapic *apic)
apic_clear_isr(vector, apic);
apic_update_ppr(apic);
+ if (test_bit(vector, vcpu_to_synic(apic->vcpu)->vec_bitmap))
+ kvm_hv_synic_send_eoi(apic->vcpu, vector);
+
kvm_ioapic_send_eoi(apic, vector);
kvm_make_request(KVM_REQ_EVENT, apic->vcpu);
return vector;
@@ -1225,7 +1225,7 @@ static bool lapic_timer_int_injected(struct kvm_vcpu *vcpu)
int vec = reg & APIC_VECTOR_MASK;
void *bitmap = apic->regs + APIC_ISR;
- if (kvm_x86_ops->deliver_posted_interrupt)
+ if (vcpu->arch.apicv_active)
bitmap = apic->regs + APIC_IRR;
if (apic_test_vector(vec, bitmap))
@@ -1693,8 +1693,8 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event)
apic_set_reg(apic, APIC_ISR + 0x10 * i, 0);
apic_set_reg(apic, APIC_TMR + 0x10 * i, 0);
}
- apic->irr_pending = kvm_vcpu_apic_vid_enabled(vcpu);
- apic->isr_count = kvm_x86_ops->hwapic_isr_update ? 1 : 0;
+ apic->irr_pending = vcpu->arch.apicv_active;
+ apic->isr_count = vcpu->arch.apicv_active ? 1 : 0;
apic->highest_isr_cache = -1;
update_divide_count(apic);
atomic_set(&apic->lapic_timer.pending, 0);
@@ -1883,6 +1883,12 @@ int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu)
apic_set_isr(vector, apic);
apic_update_ppr(apic);
apic_clear_irr(vector, apic);
+
+ if (test_bit(vector, vcpu_to_synic(vcpu)->auto_eoi_bitmap)) {
+ apic_clear_isr(vector, apic);
+ apic_update_ppr(apic);
+ }
+
return vector;
}
@@ -1906,15 +1912,15 @@ void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu,
update_divide_count(apic);
start_apic_timer(apic);
apic->irr_pending = true;
- apic->isr_count = kvm_x86_ops->hwapic_isr_update ?
+ apic->isr_count = vcpu->arch.apicv_active ?
1 : count_vectors(apic->regs + APIC_ISR);
apic->highest_isr_cache = -1;
- if (kvm_x86_ops->hwapic_irr_update)
+ if (vcpu->arch.apicv_active) {
kvm_x86_ops->hwapic_irr_update(vcpu,
apic_find_highest_irr(apic));
- if (unlikely(kvm_x86_ops->hwapic_isr_update))
kvm_x86_ops->hwapic_isr_update(vcpu->kvm,
apic_find_highest_isr(apic));
+ }
kvm_make_request(KVM_REQ_EVENT, vcpu);
if (ioapic_in_kernel(vcpu->kvm))
kvm_rtc_eoi_tracking_restore_one(vcpu);
diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h
index fde8e35d5850..41bdb35b4b67 100644
--- a/arch/x86/kvm/lapic.h
+++ b/arch/x86/kvm/lapic.h
@@ -143,9 +143,9 @@ static inline int apic_x2apic_mode(struct kvm_lapic *apic)
return apic->vcpu->arch.apic_base & X2APIC_ENABLE;
}
-static inline bool kvm_vcpu_apic_vid_enabled(struct kvm_vcpu *vcpu)
+static inline bool kvm_vcpu_apicv_active(struct kvm_vcpu *vcpu)
{
- return kvm_x86_ops->cpu_uses_apicv(vcpu);
+ return vcpu->arch.apic && vcpu->arch.apicv_active;
}
static inline bool kvm_apic_has_events(struct kvm_vcpu *vcpu)
@@ -164,6 +164,11 @@ 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);
}
+static inline int kvm_apic_id(struct kvm_lapic *apic)
+{
+ return (kvm_apic_get_reg(apic, APIC_ID) >> 24) & 0xff;
+}
+
bool kvm_apic_pending_eoi(struct kvm_vcpu *vcpu, int vector);
void wait_lapic_expire(struct kvm_vcpu *vcpu);
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index e7c2c1428a69..420a5ca3c0ee 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -311,11 +311,6 @@ static int is_large_pte(u64 pte)
return pte & PT_PAGE_SIZE_MASK;
}
-static int is_rmap_spte(u64 pte)
-{
- return is_shadow_present_pte(pte);
-}
-
static int is_last_spte(u64 pte, int level)
{
if (level == PT_PAGE_TABLE_LEVEL)
@@ -540,7 +535,7 @@ static bool mmu_spte_update(u64 *sptep, u64 new_spte)
u64 old_spte = *sptep;
bool ret = false;
- WARN_ON(!is_rmap_spte(new_spte));
+ WARN_ON(!is_shadow_present_pte(new_spte));
if (!is_shadow_present_pte(old_spte)) {
mmu_spte_set(sptep, new_spte);
@@ -595,7 +590,7 @@ static int mmu_spte_clear_track_bits(u64 *sptep)
else
old_spte = __update_clear_spte_slow(sptep, 0ull);
- if (!is_rmap_spte(old_spte))
+ if (!is_shadow_present_pte(old_spte))
return 0;
pfn = spte_to_pfn(old_spte);
@@ -909,36 +904,35 @@ static int mapping_level(struct kvm_vcpu *vcpu, gfn_t large_gfn,
}
/*
- * Pte mapping structures:
+ * About rmap_head encoding:
*
- * If pte_list bit zero is zero, then pte_list point to the spte.
- *
- * If pte_list bit zero is one, (then pte_list & ~1) points to a struct
+ * If the bit zero of rmap_head->val is clear, then it points to the only spte
+ * in this rmap chain. Otherwise, (rmap_head->val & ~1) points to a struct
* pte_list_desc containing more mappings.
- *
- * Returns the number of pte entries before the spte was added or zero if
- * the spte was not added.
- *
+ */
+
+/*
+ * Returns the number of pointers in the rmap chain, not counting the new one.
*/
static int pte_list_add(struct kvm_vcpu *vcpu, u64 *spte,
- unsigned long *pte_list)
+ struct kvm_rmap_head *rmap_head)
{
struct pte_list_desc *desc;
int i, count = 0;
- if (!*pte_list) {
+ if (!rmap_head->val) {
rmap_printk("pte_list_add: %p %llx 0->1\n", spte, *spte);
- *pte_list = (unsigned long)spte;
- } else if (!(*pte_list & 1)) {
+ rmap_head->val = (unsigned long)spte;
+ } else if (!(rmap_head->val & 1)) {
rmap_printk("pte_list_add: %p %llx 1->many\n", spte, *spte);
desc = mmu_alloc_pte_list_desc(vcpu);
- desc->sptes[0] = (u64 *)*pte_list;
+ desc->sptes[0] = (u64 *)rmap_head->val;
desc->sptes[1] = spte;
- *pte_list = (unsigned long)desc | 1;
+ rmap_head->val = (unsigned long)desc | 1;
++count;
} else {
rmap_printk("pte_list_add: %p %llx many->many\n", spte, *spte);
- desc = (struct pte_list_desc *)(*pte_list & ~1ul);
+ desc = (struct pte_list_desc *)(rmap_head->val & ~1ul);
while (desc->sptes[PTE_LIST_EXT-1] && desc->more) {
desc = desc->more;
count += PTE_LIST_EXT;
@@ -955,8 +949,9 @@ static int pte_list_add(struct kvm_vcpu *vcpu, u64 *spte,
}
static void
-pte_list_desc_remove_entry(unsigned long *pte_list, struct pte_list_desc *desc,
- int i, struct pte_list_desc *prev_desc)
+pte_list_desc_remove_entry(struct kvm_rmap_head *rmap_head,
+ struct pte_list_desc *desc, int i,
+ struct pte_list_desc *prev_desc)
{
int j;
@@ -967,43 +962,43 @@ pte_list_desc_remove_entry(unsigned long *pte_list, struct pte_list_desc *desc,
if (j != 0)
return;
if (!prev_desc && !desc->more)
- *pte_list = (unsigned long)desc->sptes[0];
+ rmap_head->val = (unsigned long)desc->sptes[0];
else
if (prev_desc)
prev_desc->more = desc->more;
else
- *pte_list = (unsigned long)desc->more | 1;
+ rmap_head->val = (unsigned long)desc->more | 1;
mmu_free_pte_list_desc(desc);
}
-static void pte_list_remove(u64 *spte, unsigned long *pte_list)
+static void pte_list_remove(u64 *spte, struct kvm_rmap_head *rmap_head)
{
struct pte_list_desc *desc;
struct pte_list_desc *prev_desc;
int i;
- if (!*pte_list) {
+ if (!rmap_head->val) {
printk(KERN_ERR "pte_list_remove: %p 0->BUG\n", spte);
BUG();
- } else if (!(*pte_list & 1)) {
+ } else if (!(rmap_head->val & 1)) {
rmap_printk("pte_list_remove: %p 1->0\n", spte);
- if ((u64 *)*pte_list != spte) {
+ if ((u64 *)rmap_head->val != spte) {
printk(KERN_ERR "pte_list_remove: %p 1->BUG\n", spte);
BUG();
}
- *pte_list = 0;
+ rmap_head->val = 0;
} else {
rmap_printk("pte_list_remove: %p many->many\n", spte);
- desc = (struct pte_list_desc *)(*pte_list & ~1ul);
+ desc = (struct pte_list_desc *)(rmap_head->val & ~1ul);
prev_desc = NULL;
while (desc) {
- for (i = 0; i < PTE_LIST_EXT && desc->sptes[i]; ++i)
+ for (i = 0; i < PTE_LIST_EXT && desc->sptes[i]; ++i) {
if (desc->sptes[i] == spte) {
- pte_list_desc_remove_entry(pte_list,
- desc, i,
- prev_desc);
+ pte_list_desc_remove_entry(rmap_head,
+ desc, i, prev_desc);
return;
}
+ }
prev_desc = desc;
desc = desc->more;
}
@@ -1012,28 +1007,8 @@ static void pte_list_remove(u64 *spte, unsigned long *pte_list)
}
}
-typedef void (*pte_list_walk_fn) (u64 *spte);
-static void pte_list_walk(unsigned long *pte_list, pte_list_walk_fn fn)
-{
- struct pte_list_desc *desc;
- int i;
-
- if (!*pte_list)
- return;
-
- if (!(*pte_list & 1))
- return fn((u64 *)*pte_list);
-
- desc = (struct pte_list_desc *)(*pte_list & ~1ul);
- while (desc) {
- for (i = 0; i < PTE_LIST_EXT && desc->sptes[i]; ++i)
- fn(desc->sptes[i]);
- desc = desc->more;
- }
-}
-
-static unsigned long *__gfn_to_rmap(gfn_t gfn, int level,
- struct kvm_memory_slot *slot)
+static struct kvm_rmap_head *__gfn_to_rmap(gfn_t gfn, int level,
+ struct kvm_memory_slot *slot)
{
unsigned long idx;
@@ -1041,10 +1016,8 @@ static unsigned long *__gfn_to_rmap(gfn_t gfn, int level,
return &slot->arch.rmap[level - PT_PAGE_TABLE_LEVEL][idx];
}
-/*
- * Take gfn and return the reverse mapping to it.
- */
-static unsigned long *gfn_to_rmap(struct kvm *kvm, gfn_t gfn, struct kvm_mmu_page *sp)
+static struct kvm_rmap_head *gfn_to_rmap(struct kvm *kvm, gfn_t gfn,
+ struct kvm_mmu_page *sp)
{
struct kvm_memslots *slots;
struct kvm_memory_slot *slot;
@@ -1065,24 +1038,24 @@ static bool rmap_can_add(struct kvm_vcpu *vcpu)
static int rmap_add(struct kvm_vcpu *vcpu, u64 *spte, gfn_t gfn)
{
struct kvm_mmu_page *sp;
- unsigned long *rmapp;
+ struct kvm_rmap_head *rmap_head;
sp = page_header(__pa(spte));
kvm_mmu_page_set_gfn(sp, spte - sp->spt, gfn);
- rmapp = gfn_to_rmap(vcpu->kvm, gfn, sp);
- return pte_list_add(vcpu, spte, rmapp);
+ rmap_head = gfn_to_rmap(vcpu->kvm, gfn, sp);
+ return pte_list_add(vcpu, spte, rmap_head);
}
static void rmap_remove(struct kvm *kvm, u64 *spte)
{
struct kvm_mmu_page *sp;
gfn_t gfn;
- unsigned long *rmapp;
+ struct kvm_rmap_head *rmap_head;
sp = page_header(__pa(spte));
gfn = kvm_mmu_page_get_gfn(sp, spte - sp->spt);
- rmapp = gfn_to_rmap(kvm, gfn, sp);
- pte_list_remove(spte, rmapp);
+ rmap_head = gfn_to_rmap(kvm, gfn, sp);
+ pte_list_remove(spte, rmap_head);
}
/*
@@ -1102,19 +1075,26 @@ struct rmap_iterator {
*
* Returns sptep if found, NULL otherwise.
*/
-static u64 *rmap_get_first(unsigned long rmap, struct rmap_iterator *iter)
+static u64 *rmap_get_first(struct kvm_rmap_head *rmap_head,
+ struct rmap_iterator *iter)
{
- if (!rmap)
+ u64 *sptep;
+
+ if (!rmap_head->val)
return NULL;
- if (!(rmap & 1)) {
+ if (!(rmap_head->val & 1)) {
iter->desc = NULL;
- return (u64 *)rmap;
+ sptep = (u64 *)rmap_head->val;
+ goto out;
}
- iter->desc = (struct pte_list_desc *)(rmap & ~1ul);
+ iter->desc = (struct pte_list_desc *)(rmap_head->val & ~1ul);
iter->pos = 0;
- return iter->desc->sptes[iter->pos];
+ sptep = iter->desc->sptes[iter->pos];
+out:
+ BUG_ON(!is_shadow_present_pte(*sptep));
+ return sptep;
}
/*
@@ -1124,14 +1104,14 @@ static u64 *rmap_get_first(unsigned long rmap, struct rmap_iterator *iter)
*/
static u64 *rmap_get_next(struct rmap_iterator *iter)
{
+ u64 *sptep;
+
if (iter->desc) {
if (iter->pos < PTE_LIST_EXT - 1) {
- u64 *sptep;
-
++iter->pos;
sptep = iter->desc->sptes[iter->pos];
if (sptep)
- return sptep;
+ goto out;
}
iter->desc = iter->desc->more;
@@ -1139,17 +1119,20 @@ static u64 *rmap_get_next(struct rmap_iterator *iter)
if (iter->desc) {
iter->pos = 0;
/* desc->sptes[0] cannot be NULL */
- return iter->desc->sptes[iter->pos];
+ sptep = iter->desc->sptes[iter->pos];
+ goto out;
}
}
return NULL;
+out:
+ BUG_ON(!is_shadow_present_pte(*sptep));
+ return sptep;
}
-#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_))
+#define for_each_rmap_spte(_rmap_head_, _iter_, _spte_) \
+ for (_spte_ = rmap_get_first(_rmap_head_, _iter_); \
+ _spte_; _spte_ = rmap_get_next(_iter_))
static void drop_spte(struct kvm *kvm, u64 *sptep)
{
@@ -1207,14 +1190,15 @@ static bool spte_write_protect(struct kvm *kvm, u64 *sptep, bool pt_protect)
return mmu_spte_update(sptep, spte);
}
-static bool __rmap_write_protect(struct kvm *kvm, unsigned long *rmapp,
+static bool __rmap_write_protect(struct kvm *kvm,
+ struct kvm_rmap_head *rmap_head,
bool pt_protect)
{
u64 *sptep;
struct rmap_iterator iter;
bool flush = false;
- for_each_rmap_spte(rmapp, &iter, sptep)
+ for_each_rmap_spte(rmap_head, &iter, sptep)
flush |= spte_write_protect(kvm, sptep, pt_protect);
return flush;
@@ -1231,13 +1215,13 @@ static bool spte_clear_dirty(struct kvm *kvm, u64 *sptep)
return mmu_spte_update(sptep, spte);
}
-static bool __rmap_clear_dirty(struct kvm *kvm, unsigned long *rmapp)
+static bool __rmap_clear_dirty(struct kvm *kvm, struct kvm_rmap_head *rmap_head)
{
u64 *sptep;
struct rmap_iterator iter;
bool flush = false;
- for_each_rmap_spte(rmapp, &iter, sptep)
+ for_each_rmap_spte(rmap_head, &iter, sptep)
flush |= spte_clear_dirty(kvm, sptep);
return flush;
@@ -1254,13 +1238,13 @@ static bool spte_set_dirty(struct kvm *kvm, u64 *sptep)
return mmu_spte_update(sptep, spte);
}
-static bool __rmap_set_dirty(struct kvm *kvm, unsigned long *rmapp)
+static bool __rmap_set_dirty(struct kvm *kvm, struct kvm_rmap_head *rmap_head)
{
u64 *sptep;
struct rmap_iterator iter;
bool flush = false;
- for_each_rmap_spte(rmapp, &iter, sptep)
+ for_each_rmap_spte(rmap_head, &iter, sptep)
flush |= spte_set_dirty(kvm, sptep);
return flush;
@@ -1280,12 +1264,12 @@ static void kvm_mmu_write_protect_pt_masked(struct kvm *kvm,
struct kvm_memory_slot *slot,
gfn_t gfn_offset, unsigned long mask)
{
- unsigned long *rmapp;
+ struct kvm_rmap_head *rmap_head;
while (mask) {
- rmapp = __gfn_to_rmap(slot->base_gfn + gfn_offset + __ffs(mask),
- PT_PAGE_TABLE_LEVEL, slot);
- __rmap_write_protect(kvm, rmapp, false);
+ rmap_head = __gfn_to_rmap(slot->base_gfn + gfn_offset + __ffs(mask),
+ PT_PAGE_TABLE_LEVEL, slot);
+ __rmap_write_protect(kvm, rmap_head, false);
/* clear the first set bit */
mask &= mask - 1;
@@ -1305,12 +1289,12 @@ void kvm_mmu_clear_dirty_pt_masked(struct kvm *kvm,
struct kvm_memory_slot *slot,
gfn_t gfn_offset, unsigned long mask)
{
- unsigned long *rmapp;
+ struct kvm_rmap_head *rmap_head;
while (mask) {
- rmapp = __gfn_to_rmap(slot->base_gfn + gfn_offset + __ffs(mask),
- PT_PAGE_TABLE_LEVEL, slot);
- __rmap_clear_dirty(kvm, rmapp);
+ rmap_head = __gfn_to_rmap(slot->base_gfn + gfn_offset + __ffs(mask),
+ PT_PAGE_TABLE_LEVEL, slot);
+ __rmap_clear_dirty(kvm, rmap_head);
/* clear the first set bit */
mask &= mask - 1;
@@ -1342,28 +1326,27 @@ void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,
static bool rmap_write_protect(struct kvm_vcpu *vcpu, u64 gfn)
{
struct kvm_memory_slot *slot;
- unsigned long *rmapp;
+ struct kvm_rmap_head *rmap_head;
int i;
bool write_protected = false;
slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);
for (i = PT_PAGE_TABLE_LEVEL; i <= PT_MAX_HUGEPAGE_LEVEL; ++i) {
- rmapp = __gfn_to_rmap(gfn, i, slot);
- write_protected |= __rmap_write_protect(vcpu->kvm, rmapp, true);
+ rmap_head = __gfn_to_rmap(gfn, i, slot);
+ write_protected |= __rmap_write_protect(vcpu->kvm, rmap_head, true);
}
return write_protected;
}
-static bool kvm_zap_rmapp(struct kvm *kvm, unsigned long *rmapp)
+static bool kvm_zap_rmapp(struct kvm *kvm, struct kvm_rmap_head *rmap_head)
{
u64 *sptep;
struct rmap_iterator iter;
bool flush = false;
- while ((sptep = rmap_get_first(*rmapp, &iter))) {
- BUG_ON(!(*sptep & PT_PRESENT_MASK));
+ while ((sptep = rmap_get_first(rmap_head, &iter))) {
rmap_printk("%s: spte %p %llx.\n", __func__, sptep, *sptep);
drop_spte(kvm, sptep);
@@ -1373,14 +1356,14 @@ static bool kvm_zap_rmapp(struct kvm *kvm, unsigned long *rmapp)
return flush;
}
-static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp,
+static int kvm_unmap_rmapp(struct kvm *kvm, struct kvm_rmap_head *rmap_head,
struct kvm_memory_slot *slot, gfn_t gfn, int level,
unsigned long data)
{
- return kvm_zap_rmapp(kvm, rmapp);
+ return kvm_zap_rmapp(kvm, rmap_head);
}
-static int kvm_set_pte_rmapp(struct kvm *kvm, unsigned long *rmapp,
+static int kvm_set_pte_rmapp(struct kvm *kvm, struct kvm_rmap_head *rmap_head,
struct kvm_memory_slot *slot, gfn_t gfn, int level,
unsigned long data)
{
@@ -1395,7 +1378,7 @@ static int kvm_set_pte_rmapp(struct kvm *kvm, unsigned long *rmapp,
new_pfn = pte_pfn(*ptep);
restart:
- for_each_rmap_spte(rmapp, &iter, sptep) {
+ for_each_rmap_spte(rmap_head, &iter, sptep) {
rmap_printk("kvm_set_pte_rmapp: spte %p %llx gfn %llx (%d)\n",
sptep, *sptep, gfn, level);
@@ -1433,11 +1416,11 @@ struct slot_rmap_walk_iterator {
/* output fields. */
gfn_t gfn;
- unsigned long *rmap;
+ struct kvm_rmap_head *rmap;
int level;
/* private field. */
- unsigned long *end_rmap;
+ struct kvm_rmap_head *end_rmap;
};
static void
@@ -1496,7 +1479,7 @@ static int kvm_handle_hva_range(struct kvm *kvm,
unsigned long end,
unsigned long data,
int (*handler)(struct kvm *kvm,
- unsigned long *rmapp,
+ struct kvm_rmap_head *rmap_head,
struct kvm_memory_slot *slot,
gfn_t gfn,
int level,
@@ -1540,7 +1523,8 @@ static int kvm_handle_hva_range(struct kvm *kvm,
static int kvm_handle_hva(struct kvm *kvm, unsigned long hva,
unsigned long data,
- int (*handler)(struct kvm *kvm, unsigned long *rmapp,
+ int (*handler)(struct kvm *kvm,
+ struct kvm_rmap_head *rmap_head,
struct kvm_memory_slot *slot,
gfn_t gfn, int level,
unsigned long data))
@@ -1563,7 +1547,7 @@ void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte)
kvm_handle_hva(kvm, hva, (unsigned long)&pte, kvm_set_pte_rmapp);
}
-static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
+static int kvm_age_rmapp(struct kvm *kvm, struct kvm_rmap_head *rmap_head,
struct kvm_memory_slot *slot, gfn_t gfn, int level,
unsigned long data)
{
@@ -1573,18 +1557,19 @@ static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
BUG_ON(!shadow_accessed_mask);
- for_each_rmap_spte(rmapp, &iter, sptep)
+ for_each_rmap_spte(rmap_head, &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;
}
-static int kvm_test_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
+static int kvm_test_age_rmapp(struct kvm *kvm, struct kvm_rmap_head *rmap_head,
struct kvm_memory_slot *slot, gfn_t gfn,
int level, unsigned long data)
{
@@ -1600,11 +1585,12 @@ static int kvm_test_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
if (!shadow_accessed_mask)
goto out;
- for_each_rmap_spte(rmapp, &iter, sptep)
+ for_each_rmap_spte(rmap_head, &iter, sptep) {
if (*sptep & shadow_accessed_mask) {
young = 1;
break;
}
+ }
out:
return young;
}
@@ -1613,14 +1599,14 @@ out:
static void rmap_recycle(struct kvm_vcpu *vcpu, u64 *spte, gfn_t gfn)
{
- unsigned long *rmapp;
+ struct kvm_rmap_head *rmap_head;
struct kvm_mmu_page *sp;
sp = page_header(__pa(spte));
- rmapp = gfn_to_rmap(vcpu->kvm, gfn, sp);
+ rmap_head = gfn_to_rmap(vcpu->kvm, gfn, sp);
- kvm_unmap_rmapp(vcpu->kvm, rmapp, NULL, gfn, sp->role.level, 0);
+ kvm_unmap_rmapp(vcpu->kvm, rmap_head, NULL, gfn, sp->role.level, 0);
kvm_flush_remote_tlbs(vcpu->kvm);
}
@@ -1720,8 +1706,7 @@ static void drop_parent_pte(struct kvm_mmu_page *sp,
mmu_spte_clear_no_track(parent_pte);
}
-static struct kvm_mmu_page *kvm_mmu_alloc_page(struct kvm_vcpu *vcpu,
- u64 *parent_pte, int direct)
+static struct kvm_mmu_page *kvm_mmu_alloc_page(struct kvm_vcpu *vcpu, int direct)
{
struct kvm_mmu_page *sp;
@@ -1737,8 +1722,6 @@ static struct kvm_mmu_page *kvm_mmu_alloc_page(struct kvm_vcpu *vcpu,
* this feature. See the comments in kvm_zap_obsolete_pages().
*/
list_add(&sp->link, &vcpu->kvm->arch.active_mmu_pages);
- sp->parent_ptes = 0;
- mmu_page_add_parent_pte(vcpu, sp, parent_pte);
kvm_mod_used_mmu_pages(vcpu->kvm, +1);
return sp;
}
@@ -1746,7 +1729,12 @@ static struct kvm_mmu_page *kvm_mmu_alloc_page(struct kvm_vcpu *vcpu,
static void mark_unsync(u64 *spte);
static void kvm_mmu_mark_parents_unsync(struct kvm_mmu_page *sp)
{
- pte_list_walk(&sp->parent_ptes, mark_unsync);
+ u64 *sptep;
+ struct rmap_iterator iter;
+
+ for_each_rmap_spte(&sp->parent_ptes, &iter, sptep) {
+ mark_unsync(sptep);
+ }
}
static void mark_unsync(u64 *spte)
@@ -1806,6 +1794,13 @@ static int mmu_pages_add(struct kvm_mmu_pages *pvec, struct kvm_mmu_page *sp,
return (pvec->nr == KVM_PAGE_ARRAY_NR);
}
+static inline void clear_unsync_child_bit(struct kvm_mmu_page *sp, int idx)
+{
+ --sp->unsync_children;
+ WARN_ON((int)sp->unsync_children < 0);
+ __clear_bit(idx, sp->unsync_child_bitmap);
+}
+
static int __mmu_unsync_walk(struct kvm_mmu_page *sp,
struct kvm_mmu_pages *pvec)
{
@@ -1815,8 +1810,10 @@ static int __mmu_unsync_walk(struct kvm_mmu_page *sp,
struct kvm_mmu_page *child;
u64 ent = sp->spt[i];
- if (!is_shadow_present_pte(ent) || is_large_pte(ent))
- goto clear_child_bitmap;
+ if (!is_shadow_present_pte(ent) || is_large_pte(ent)) {
+ clear_unsync_child_bit(sp, i);
+ continue;
+ }
child = page_header(ent & PT64_BASE_ADDR_MASK);
@@ -1825,28 +1822,21 @@ static int __mmu_unsync_walk(struct kvm_mmu_page *sp,
return -ENOSPC;
ret = __mmu_unsync_walk(child, pvec);
- if (!ret)
- goto clear_child_bitmap;
- else if (ret > 0)
+ if (!ret) {
+ clear_unsync_child_bit(sp, i);
+ continue;
+ } else if (ret > 0) {
nr_unsync_leaf += ret;
- else
+ } else
return ret;
} else if (child->unsync) {
nr_unsync_leaf++;
if (mmu_pages_add(pvec, child, i))
return -ENOSPC;
} else
- goto clear_child_bitmap;
-
- continue;
-
-clear_child_bitmap:
- __clear_bit(i, sp->unsync_child_bitmap);
- sp->unsync_children--;
- WARN_ON((int)sp->unsync_children < 0);
+ clear_unsync_child_bit(sp, i);
}
-
return nr_unsync_leaf;
}
@@ -2009,9 +1999,7 @@ static void mmu_pages_clear_parents(struct mmu_page_path *parents)
if (!sp)
return;
- --sp->unsync_children;
- WARN_ON((int)sp->unsync_children < 0);
- __clear_bit(idx, sp->unsync_child_bitmap);
+ clear_unsync_child_bit(sp, idx);
level++;
} while (level < PT64_ROOT_LEVEL-1 && !sp->unsync_children);
}
@@ -2053,14 +2041,6 @@ static void mmu_sync_children(struct kvm_vcpu *vcpu,
}
}
-static void init_shadow_page_table(struct kvm_mmu_page *sp)
-{
- int i;
-
- for (i = 0; i < PT64_ENT_PER_PAGE; ++i)
- sp->spt[i] = 0ull;
-}
-
static void __clear_sp_write_flooding_count(struct kvm_mmu_page *sp)
{
sp->write_flooding_count = 0;
@@ -2083,8 +2063,7 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
gva_t gaddr,
unsigned level,
int direct,
- unsigned access,
- u64 *parent_pte)
+ unsigned access)
{
union kvm_mmu_page_role role;
unsigned quadrant;
@@ -2116,21 +2095,18 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
if (sp->unsync && kvm_sync_page_transient(vcpu, sp))
break;
- mmu_page_add_parent_pte(vcpu, sp, parent_pte);
- if (sp->unsync_children) {
+ if (sp->unsync_children)
kvm_make_request(KVM_REQ_MMU_SYNC, vcpu);
- kvm_mmu_mark_parents_unsync(sp);
- } else if (sp->unsync)
- kvm_mmu_mark_parents_unsync(sp);
__clear_sp_write_flooding_count(sp);
trace_kvm_mmu_get_page(sp, false);
return sp;
}
+
++vcpu->kvm->stat.mmu_cache_miss;
- sp = kvm_mmu_alloc_page(vcpu, parent_pte, direct);
- if (!sp)
- return sp;
+
+ sp = kvm_mmu_alloc_page(vcpu, direct);
+
sp->gfn = gfn;
sp->role = role;
hlist_add_head(&sp->hash_link,
@@ -2144,7 +2120,7 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
account_shadowed(vcpu->kvm, sp);
}
sp->mmu_valid_gen = vcpu->kvm->arch.mmu_valid_gen;
- init_shadow_page_table(sp);
+ clear_page(sp->spt);
trace_kvm_mmu_get_page(sp, true);
return sp;
}
@@ -2198,7 +2174,8 @@ static void shadow_walk_next(struct kvm_shadow_walk_iterator *iterator)
return __shadow_walk_next(iterator, *iterator->sptep);
}
-static void link_shadow_page(u64 *sptep, struct kvm_mmu_page *sp, bool accessed)
+static void link_shadow_page(struct kvm_vcpu *vcpu, u64 *sptep,
+ struct kvm_mmu_page *sp)
{
u64 spte;
@@ -2206,12 +2183,14 @@ static void link_shadow_page(u64 *sptep, struct kvm_mmu_page *sp, bool accessed)
VMX_EPT_WRITABLE_MASK != PT_WRITABLE_MASK);
spte = __pa(sp->spt) | PT_PRESENT_MASK | PT_WRITABLE_MASK |
- shadow_user_mask | shadow_x_mask;
-
- if (accessed)
- spte |= shadow_accessed_mask;
+ shadow_user_mask | shadow_x_mask | shadow_accessed_mask;
mmu_spte_set(sptep, spte);
+
+ mmu_page_add_parent_pte(vcpu, sp, sptep);
+
+ if (sp->unsync_children || sp->unsync)
+ mark_unsync(sptep);
}
static void validate_direct_spte(struct kvm_vcpu *vcpu, u64 *sptep,
@@ -2270,17 +2249,12 @@ static void kvm_mmu_page_unlink_children(struct kvm *kvm,
mmu_page_zap_pte(kvm, sp, sp->spt + i);
}
-static void kvm_mmu_put_page(struct kvm_mmu_page *sp, u64 *parent_pte)
-{
- mmu_page_remove_parent_pte(sp, parent_pte);
-}
-
static void kvm_mmu_unlink_parents(struct kvm *kvm, struct kvm_mmu_page *sp)
{
u64 *sptep;
struct rmap_iterator iter;
- while ((sptep = rmap_get_first(sp->parent_ptes, &iter)))
+ while ((sptep = rmap_get_first(&sp->parent_ptes, &iter)))
drop_parent_pte(sp, sptep);
}
@@ -2564,18 +2538,18 @@ done:
return ret;
}
-static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep,
- unsigned pte_access, int write_fault, int *emulate,
- int level, gfn_t gfn, pfn_t pfn, bool speculative,
- bool host_writable)
+static bool mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, unsigned pte_access,
+ int write_fault, int level, gfn_t gfn, pfn_t pfn,
+ bool speculative, bool host_writable)
{
int was_rmapped = 0;
int rmap_count;
+ bool emulate = false;
pgprintk("%s: spte %llx write_fault %d gfn %llx\n", __func__,
*sptep, write_fault, gfn);
- if (is_rmap_spte(*sptep)) {
+ if (is_shadow_present_pte(*sptep)) {
/*
* If we overwrite a PTE page pointer with a 2MB PMD, unlink
* the parent of the now unreachable PTE.
@@ -2600,12 +2574,12 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep,
if (set_spte(vcpu, sptep, pte_access, level, gfn, pfn, speculative,
true, host_writable)) {
if (write_fault)
- *emulate = 1;
+ emulate = true;
kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu);
}
- if (unlikely(is_mmio_spte(*sptep) && emulate))
- *emulate = 1;
+ if (unlikely(is_mmio_spte(*sptep)))
+ emulate = true;
pgprintk("%s: setting spte %llx\n", __func__, *sptep);
pgprintk("instantiating %s PTE (%s) at %llx (%llx) addr %p\n",
@@ -2624,6 +2598,8 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep,
}
kvm_release_pfn_clean(pfn);
+
+ return emulate;
}
static pfn_t pte_prefetch_gfn_to_pfn(struct kvm_vcpu *vcpu, gfn_t gfn,
@@ -2658,9 +2634,8 @@ static int direct_pte_prefetch_many(struct kvm_vcpu *vcpu,
return -1;
for (i = 0; i < ret; i++, gfn++, start++)
- mmu_set_spte(vcpu, start, access, 0, NULL,
- sp->role.level, gfn, page_to_pfn(pages[i]),
- true, true);
+ mmu_set_spte(vcpu, start, access, 0, sp->role.level, gfn,
+ page_to_pfn(pages[i]), true, true);
return 0;
}
@@ -2708,9 +2683,8 @@ static void direct_pte_prefetch(struct kvm_vcpu *vcpu, u64 *sptep)
__direct_pte_prefetch(vcpu, sp, sptep);
}
-static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write,
- int map_writable, int level, gfn_t gfn, pfn_t pfn,
- bool prefault)
+static int __direct_map(struct kvm_vcpu *vcpu, int write, int map_writable,
+ int level, gfn_t gfn, pfn_t pfn, bool prefault)
{
struct kvm_shadow_walk_iterator iterator;
struct kvm_mmu_page *sp;
@@ -2722,9 +2696,9 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write,
for_each_shadow_entry(vcpu, (u64)gfn << PAGE_SHIFT, iterator) {
if (iterator.level == level) {
- mmu_set_spte(vcpu, iterator.sptep, ACC_ALL,
- write, &emulate, level, gfn, pfn,
- prefault, map_writable);
+ emulate = mmu_set_spte(vcpu, iterator.sptep, ACC_ALL,
+ write, level, gfn, pfn, prefault,
+ map_writable);
direct_pte_prefetch(vcpu, iterator.sptep);
++vcpu->stat.pf_fixed;
break;
@@ -2737,10 +2711,9 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write,
base_addr &= PT64_LVL_ADDR_MASK(iterator.level);
pseudo_gfn = base_addr >> PAGE_SHIFT;
sp = kvm_mmu_get_page(vcpu, pseudo_gfn, iterator.addr,
- iterator.level - 1,
- 1, ACC_ALL, iterator.sptep);
+ iterator.level - 1, 1, ACC_ALL);
- link_shadow_page(iterator.sptep, sp, true);
+ link_shadow_page(vcpu, iterator.sptep, sp);
}
}
return emulate;
@@ -2919,7 +2892,7 @@ static bool fast_page_fault(struct kvm_vcpu *vcpu, gva_t gva, int level,
* If the mapping has been changed, let the vcpu fault on the
* same address again.
*/
- if (!is_rmap_spte(spte)) {
+ if (!is_shadow_present_pte(spte)) {
ret = true;
goto exit;
}
@@ -3018,11 +2991,9 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, u32 error_code,
make_mmu_pages_available(vcpu);
if (likely(!force_pt_level))
transparent_hugepage_adjust(vcpu, &gfn, &pfn, &level);
- r = __direct_map(vcpu, v, write, map_writable, level, gfn, pfn,
- prefault);
+ r = __direct_map(vcpu, write, map_writable, level, gfn, pfn, prefault);
spin_unlock(&vcpu->kvm->mmu_lock);
-
return r;
out_unlock:
@@ -3097,8 +3068,7 @@ static int mmu_alloc_direct_roots(struct kvm_vcpu *vcpu)
if (vcpu->arch.mmu.shadow_root_level == PT64_ROOT_LEVEL) {
spin_lock(&vcpu->kvm->mmu_lock);
make_mmu_pages_available(vcpu);
- sp = kvm_mmu_get_page(vcpu, 0, 0, PT64_ROOT_LEVEL,
- 1, ACC_ALL, NULL);
+ sp = kvm_mmu_get_page(vcpu, 0, 0, PT64_ROOT_LEVEL, 1, ACC_ALL);
++sp->root_count;
spin_unlock(&vcpu->kvm->mmu_lock);
vcpu->arch.mmu.root_hpa = __pa(sp->spt);
@@ -3110,9 +3080,7 @@ static int mmu_alloc_direct_roots(struct kvm_vcpu *vcpu)
spin_lock(&vcpu->kvm->mmu_lock);
make_mmu_pages_available(vcpu);
sp = kvm_mmu_get_page(vcpu, i << (30 - PAGE_SHIFT),
- i << 30,
- PT32_ROOT_LEVEL, 1, ACC_ALL,
- NULL);
+ i << 30, PT32_ROOT_LEVEL, 1, ACC_ALL);
root = __pa(sp->spt);
++sp->root_count;
spin_unlock(&vcpu->kvm->mmu_lock);
@@ -3149,7 +3117,7 @@ static int mmu_alloc_shadow_roots(struct kvm_vcpu *vcpu)
spin_lock(&vcpu->kvm->mmu_lock);
make_mmu_pages_available(vcpu);
sp = kvm_mmu_get_page(vcpu, root_gfn, 0, PT64_ROOT_LEVEL,
- 0, ACC_ALL, NULL);
+ 0, ACC_ALL);
root = __pa(sp->spt);
++sp->root_count;
spin_unlock(&vcpu->kvm->mmu_lock);
@@ -3182,9 +3150,8 @@ static int mmu_alloc_shadow_roots(struct kvm_vcpu *vcpu)
}
spin_lock(&vcpu->kvm->mmu_lock);
make_mmu_pages_available(vcpu);
- sp = kvm_mmu_get_page(vcpu, root_gfn, i << 30,
- PT32_ROOT_LEVEL, 0,
- ACC_ALL, NULL);
+ sp = kvm_mmu_get_page(vcpu, root_gfn, i << 30, PT32_ROOT_LEVEL,
+ 0, ACC_ALL);
root = __pa(sp->spt);
++sp->root_count;
spin_unlock(&vcpu->kvm->mmu_lock);
@@ -3531,8 +3498,7 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code,
make_mmu_pages_available(vcpu);
if (likely(!force_pt_level))
transparent_hugepage_adjust(vcpu, &gfn, &pfn, &level);
- r = __direct_map(vcpu, gpa, write, map_writable,
- level, gfn, pfn, prefault);
+ r = __direct_map(vcpu, write, map_writable, level, gfn, pfn, prefault);
spin_unlock(&vcpu->kvm->mmu_lock);
return r;
@@ -4058,10 +4024,12 @@ static void init_kvm_nested_mmu(struct kvm_vcpu *vcpu)
g_context->inject_page_fault = kvm_inject_page_fault;
/*
- * Note that arch.mmu.gva_to_gpa translates l2_gva to l1_gpa. The
- * translation of l2_gpa to l1_gpa addresses is done using the
- * arch.nested_mmu.gva_to_gpa function. Basically the gva_to_gpa
- * functions between mmu and nested_mmu are swapped.
+ * Note that arch.mmu.gva_to_gpa translates l2_gpa to l1_gpa using
+ * L1's nested page tables (e.g. EPT12). The nested translation
+ * of l2_gva to l1_gpa is done by arch.nested_mmu.gva_to_gpa using
+ * L2's page tables as the first level of translation and L1's
+ * nested page tables as the second level of translation. Basically
+ * the gva_to_gpa functions between mmu and nested_mmu are swapped.
*/
if (!is_paging(vcpu)) {
g_context->nx = false;
@@ -4495,7 +4463,7 @@ void kvm_mmu_setup(struct kvm_vcpu *vcpu)
}
/* The return value indicates if tlb flush on all vcpus is needed. */
-typedef bool (*slot_level_handler) (struct kvm *kvm, unsigned long *rmap);
+typedef bool (*slot_level_handler) (struct kvm *kvm, struct kvm_rmap_head *rmap_head);
/* The caller should hold mmu-lock before calling this function. */
static bool
@@ -4589,9 +4557,10 @@ void kvm_zap_gfn_range(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_end)
spin_unlock(&kvm->mmu_lock);
}
-static bool slot_rmap_write_protect(struct kvm *kvm, unsigned long *rmapp)
+static bool slot_rmap_write_protect(struct kvm *kvm,
+ struct kvm_rmap_head *rmap_head)
{
- return __rmap_write_protect(kvm, rmapp, false);
+ return __rmap_write_protect(kvm, rmap_head, false);
}
void kvm_mmu_slot_remove_write_access(struct kvm *kvm,
@@ -4627,7 +4596,7 @@ void kvm_mmu_slot_remove_write_access(struct kvm *kvm,
}
static bool kvm_mmu_zap_collapsible_spte(struct kvm *kvm,
- unsigned long *rmapp)
+ struct kvm_rmap_head *rmap_head)
{
u64 *sptep;
struct rmap_iterator iter;
@@ -4636,7 +4605,7 @@ static bool kvm_mmu_zap_collapsible_spte(struct kvm *kvm,
struct kvm_mmu_page *sp;
restart:
- for_each_rmap_spte(rmapp, &iter, sptep) {
+ for_each_rmap_spte(rmap_head, &iter, sptep) {
sp = page_header(__pa(sptep));
pfn = spte_to_pfn(*sptep);
diff --git a/arch/x86/kvm/mmu_audit.c b/arch/x86/kvm/mmu_audit.c
index 03d518e499a6..1cee3ec20dd2 100644
--- a/arch/x86/kvm/mmu_audit.c
+++ b/arch/x86/kvm/mmu_audit.c
@@ -129,7 +129,7 @@ static void audit_mappings(struct kvm_vcpu *vcpu, u64 *sptep, int level)
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_rmap_head *rmap_head;
struct kvm_mmu_page *rev_sp;
struct kvm_memslots *slots;
struct kvm_memory_slot *slot;
@@ -150,8 +150,8 @@ static void inspect_spte_has_rmap(struct kvm *kvm, u64 *sptep)
return;
}
- rmapp = __gfn_to_rmap(gfn, rev_sp->role.level, slot);
- if (!*rmapp) {
+ rmap_head = __gfn_to_rmap(gfn, rev_sp->role.level, slot);
+ if (!rmap_head->val) {
if (!__ratelimit(&ratelimit_state))
return;
audit_printk(kvm, "no rmap for writable spte %llx\n",
@@ -183,7 +183,7 @@ static void check_mappings_rmap(struct kvm *kvm, struct kvm_mmu_page *sp)
return;
for (i = 0; i < PT64_ENT_PER_PAGE; ++i) {
- if (!is_rmap_spte(sp->spt[i]))
+ if (!is_shadow_present_pte(sp->spt[i]))
continue;
inspect_spte_has_rmap(kvm, sp->spt + i);
@@ -192,7 +192,7 @@ static void check_mappings_rmap(struct kvm *kvm, struct kvm_mmu_page *sp)
static void audit_write_protection(struct kvm *kvm, struct kvm_mmu_page *sp)
{
- unsigned long *rmapp;
+ struct kvm_rmap_head *rmap_head;
u64 *sptep;
struct rmap_iterator iter;
struct kvm_memslots *slots;
@@ -203,13 +203,14 @@ static void audit_write_protection(struct kvm *kvm, struct kvm_mmu_page *sp)
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);
+ rmap_head = __gfn_to_rmap(sp->gfn, PT_PAGE_TABLE_LEVEL, slot);
- for_each_rmap_spte(rmapp, &iter, sptep)
+ for_each_rmap_spte(rmap_head, &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
index 9e8bf13572e6..3f8c732117ec 100644
--- a/arch/x86/kvm/mtrr.c
+++ b/arch/x86/kvm/mtrr.c
@@ -120,14 +120,22 @@ static u8 mtrr_default_type(struct kvm_mtrr *mtrr_state)
return mtrr_state->deftype & IA32_MTRR_DEF_TYPE_TYPE_MASK;
}
-static u8 mtrr_disabled_type(void)
+static u8 mtrr_disabled_type(struct kvm_vcpu *vcpu)
{
/*
* Intel SDM 11.11.2.2: all MTRRs are disabled when
* IA32_MTRR_DEF_TYPE.E bit is cleared, and the UC
* memory type is applied to all of physical memory.
+ *
+ * However, virtual machines can be run with CPUID such that
+ * there are no MTRRs. In that case, the firmware will never
+ * enable MTRRs and it is obviously undesirable to run the
+ * guest entirely with UC memory and we use WB.
*/
- return MTRR_TYPE_UNCACHABLE;
+ if (guest_cpuid_has_mtrr(vcpu))
+ return MTRR_TYPE_UNCACHABLE;
+ else
+ return MTRR_TYPE_WRBACK;
}
/*
@@ -267,7 +275,7 @@ static int fixed_mtrr_addr_to_seg(u64 addr)
for (seg = 0; seg < seg_num; seg++) {
mtrr_seg = &fixed_seg_table[seg];
- if (mtrr_seg->start >= addr && addr < mtrr_seg->end)
+ if (mtrr_seg->start <= addr && addr < mtrr_seg->end)
return seg;
}
@@ -300,7 +308,6 @@ static void var_mtrr_range(struct kvm_mtrr_range *range, u64 *start, u64 *end)
*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.
@@ -356,10 +363,14 @@ static void set_var_mtrr_msr(struct kvm_vcpu *vcpu, u32 msr, u64 data)
if (var_mtrr_range_is_valid(cur))
list_del(&mtrr_state->var_ranges[index].node);
+ /* Extend the mask with all 1 bits to the left, since those
+ * bits must implicitly be 0. The bits are then cleared
+ * when reading them.
+ */
if (!is_mtrr_mask)
cur->base = data;
else
- cur->mask = data;
+ cur->mask = data | (-1LL << cpuid_maxphyaddr(vcpu));
/* add it to the list if it's enabled. */
if (var_mtrr_range_is_valid(cur)) {
@@ -426,6 +437,8 @@ int kvm_mtrr_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
*pdata = vcpu->arch.mtrr_state.var_ranges[index].base;
else
*pdata = vcpu->arch.mtrr_state.var_ranges[index].mask;
+
+ *pdata &= (1ULL << cpuid_maxphyaddr(vcpu)) - 1;
}
return 0;
@@ -670,7 +683,7 @@ u8 kvm_mtrr_get_guest_memory_type(struct kvm_vcpu *vcpu, gfn_t gfn)
}
if (iter.mtrr_disabled)
- return mtrr_disabled_type();
+ return mtrr_disabled_type(vcpu);
/* not contained in any MTRRs. */
if (type == -1)
diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h
index 3058a22a658d..91e939b486d1 100644
--- a/arch/x86/kvm/paging_tmpl.h
+++ b/arch/x86/kvm/paging_tmpl.h
@@ -475,8 +475,8 @@ FNAME(prefetch_gpte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
* we call mmu_set_spte() with host_writable = true because
* pte_prefetch_gfn_to_pfn always gets a writable pfn.
*/
- mmu_set_spte(vcpu, spte, pte_access, 0, NULL, PT_PAGE_TABLE_LEVEL,
- gfn, pfn, true, true);
+ mmu_set_spte(vcpu, spte, pte_access, 0, PT_PAGE_TABLE_LEVEL, gfn, pfn,
+ true, true);
return true;
}
@@ -556,7 +556,7 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,
struct kvm_mmu_page *sp = NULL;
struct kvm_shadow_walk_iterator it;
unsigned direct_access, access = gw->pt_access;
- int top_level, emulate = 0;
+ int top_level, emulate;
direct_access = gw->pte_access;
@@ -587,7 +587,7 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,
if (!is_shadow_present_pte(*it.sptep)) {
table_gfn = gw->table_gfn[it.level - 2];
sp = kvm_mmu_get_page(vcpu, table_gfn, addr, it.level-1,
- false, access, it.sptep);
+ false, access);
}
/*
@@ -598,7 +598,7 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,
goto out_gpte_changed;
if (sp)
- link_shadow_page(it.sptep, sp, PT_GUEST_ACCESSED_MASK);
+ link_shadow_page(vcpu, it.sptep, sp);
}
for (;
@@ -617,20 +617,18 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,
direct_gfn = gw->gfn & ~(KVM_PAGES_PER_HPAGE(it.level) - 1);
sp = kvm_mmu_get_page(vcpu, direct_gfn, addr, it.level-1,
- true, direct_access, it.sptep);
- link_shadow_page(it.sptep, sp, PT_GUEST_ACCESSED_MASK);
+ true, direct_access);
+ link_shadow_page(vcpu, it.sptep, sp);
}
clear_sp_write_flooding_count(it.sptep);
- mmu_set_spte(vcpu, it.sptep, gw->pte_access, write_fault, &emulate,
- it.level, gw->gfn, pfn, prefault, map_writable);
+ emulate = mmu_set_spte(vcpu, it.sptep, gw->pte_access, write_fault,
+ it.level, gw->gfn, pfn, prefault, map_writable);
FNAME(pte_prefetch)(vcpu, gw, it.sptep);
return emulate;
out_gpte_changed:
- if (sp)
- kvm_mmu_put_page(sp, it.sptep);
kvm_release_pfn_clean(pfn);
return 0;
}
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 83a1c643f9a5..c13a64b7d789 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -86,6 +86,7 @@ static const u32 host_save_user_msrs[] = {
MSR_FS_BASE,
#endif
MSR_IA32_SYSENTER_CS, MSR_IA32_SYSENTER_ESP, MSR_IA32_SYSENTER_EIP,
+ MSR_TSC_AUX,
};
#define NR_HOST_SAVE_USER_MSRS ARRAY_SIZE(host_save_user_msrs)
@@ -135,6 +136,7 @@ struct vcpu_svm {
uint64_t asid_generation;
uint64_t sysenter_esp;
uint64_t sysenter_eip;
+ uint64_t tsc_aux;
u64 next_rip;
@@ -1238,6 +1240,9 @@ static void svm_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
wrmsrl(MSR_AMD64_TSC_RATIO, tsc_ratio);
}
}
+ /* This assumes that the kernel never uses MSR_TSC_AUX */
+ if (static_cpu_has(X86_FEATURE_RDTSCP))
+ wrmsrl(MSR_TSC_AUX, svm->tsc_aux);
}
static void svm_vcpu_put(struct kvm_vcpu *vcpu)
@@ -3024,6 +3029,11 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
case MSR_IA32_SYSENTER_ESP:
msr_info->data = svm->sysenter_esp;
break;
+ case MSR_TSC_AUX:
+ if (!boot_cpu_has(X86_FEATURE_RDTSCP))
+ return 1;
+ msr_info->data = svm->tsc_aux;
+ break;
/*
* Nobody will change the following 5 values in the VMCB so we can
* safely return them on rdmsr. They will always be 0 until LBRV is
@@ -3053,6 +3063,23 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
case MSR_IA32_UCODE_REV:
msr_info->data = 0x01000065;
break;
+ case MSR_F15H_IC_CFG: {
+
+ int family, model;
+
+ family = guest_cpuid_family(vcpu);
+ model = guest_cpuid_model(vcpu);
+
+ if (family < 0 || model < 0)
+ return kvm_get_msr_common(vcpu, msr_info);
+
+ msr_info->data = 0;
+
+ if (family == 0x15 &&
+ (model >= 0x2 && model < 0x20))
+ msr_info->data = 0x1E;
+ }
+ break;
default:
return kvm_get_msr_common(vcpu, msr_info);
}
@@ -3145,6 +3172,18 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
svm->sysenter_esp = data;
svm->vmcb->save.sysenter_esp = data;
break;
+ case MSR_TSC_AUX:
+ if (!boot_cpu_has(X86_FEATURE_RDTSCP))
+ return 1;
+
+ /*
+ * This is rare, so we update the MSR here instead of using
+ * direct_access_msrs. Doing that would require a rdmsr in
+ * svm_vcpu_put.
+ */
+ svm->tsc_aux = data;
+ wrmsrl(MSR_TSC_AUX, svm->tsc_aux);
+ break;
case MSR_IA32_DEBUGCTLMSR:
if (!boot_cpu_has(X86_FEATURE_LBRV)) {
vcpu_unimpl(vcpu, "%s: MSR_IA32_DEBUGCTL 0x%llx, nop\n",
@@ -3422,6 +3461,8 @@ static int handle_exit(struct kvm_vcpu *vcpu)
struct kvm_run *kvm_run = vcpu->run;
u32 exit_code = svm->vmcb->control.exit_code;
+ trace_kvm_exit(exit_code, vcpu, KVM_ISA_SVM);
+
if (!is_cr_intercept(svm, INTERCEPT_CR0_WRITE))
vcpu->arch.cr0 = svm->vmcb->save.cr0;
if (npt_enabled)
@@ -3559,12 +3600,16 @@ static void svm_set_virtual_x2apic_mode(struct kvm_vcpu *vcpu, bool set)
return;
}
-static int svm_cpu_uses_apicv(struct kvm_vcpu *vcpu)
+static bool svm_get_enable_apicv(void)
{
- return 0;
+ return false;
}
-static void svm_load_eoi_exitmap(struct kvm_vcpu *vcpu)
+static void svm_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu)
+{
+}
+
+static void svm_load_eoi_exitmap(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap)
{
return;
}
@@ -3892,8 +3937,6 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu)
vcpu->arch.regs[VCPU_REGS_RSP] = svm->vmcb->save.rsp;
vcpu->arch.regs[VCPU_REGS_RIP] = svm->vmcb->save.rip;
- trace_kvm_exit(svm->vmcb->control.exit_code, vcpu, KVM_ISA_SVM);
-
if (unlikely(svm->vmcb->control.exit_code == SVM_EXIT_NMI))
kvm_before_handle_nmi(&svm->vcpu);
@@ -4037,7 +4080,7 @@ static int svm_get_lpage_level(void)
static bool svm_rdtscp_supported(void)
{
- return false;
+ return boot_cpu_has(X86_FEATURE_RDTSCP);
}
static bool svm_invpcid_supported(void)
@@ -4328,7 +4371,8 @@ static struct kvm_x86_ops svm_x86_ops = {
.enable_irq_window = enable_irq_window,
.update_cr8_intercept = update_cr8_intercept,
.set_virtual_x2apic_mode = svm_set_virtual_x2apic_mode,
- .cpu_uses_apicv = svm_cpu_uses_apicv,
+ .get_enable_apicv = svm_get_enable_apicv,
+ .refresh_apicv_exec_ctrl = svm_refresh_apicv_exec_ctrl,
.load_eoi_exitmap = svm_load_eoi_exitmap,
.sync_pir_to_irr = svm_sync_pir_to_irr,
diff --git a/arch/x86/kvm/trace.h b/arch/x86/kvm/trace.h
index 120302511802..ad9f6a23f139 100644
--- a/arch/x86/kvm/trace.h
+++ b/arch/x86/kvm/trace.h
@@ -268,7 +268,7 @@ TRACE_EVENT(kvm_inj_virq,
#define kvm_trace_sym_exc \
EXS(DE), EXS(DB), EXS(BP), EXS(OF), EXS(BR), EXS(UD), EXS(NM), \
EXS(DF), EXS(TS), EXS(NP), EXS(SS), EXS(GP), EXS(PF), \
- EXS(MF), EXS(MC)
+ EXS(MF), EXS(AC), EXS(MC)
/*
* Tracepoint for kvm interrupt injection:
@@ -1025,6 +1025,269 @@ TRACE_EVENT(kvm_pi_irte_update,
__entry->pi_desc_addr)
);
+/*
+ * Tracepoint for kvm_hv_notify_acked_sint.
+ */
+TRACE_EVENT(kvm_hv_notify_acked_sint,
+ TP_PROTO(int vcpu_id, u32 sint),
+ TP_ARGS(vcpu_id, sint),
+
+ TP_STRUCT__entry(
+ __field(int, vcpu_id)
+ __field(u32, sint)
+ ),
+
+ TP_fast_assign(
+ __entry->vcpu_id = vcpu_id;
+ __entry->sint = sint;
+ ),
+
+ TP_printk("vcpu_id %d sint %u", __entry->vcpu_id, __entry->sint)
+);
+
+/*
+ * Tracepoint for synic_set_irq.
+ */
+TRACE_EVENT(kvm_hv_synic_set_irq,
+ TP_PROTO(int vcpu_id, u32 sint, int vector, int ret),
+ TP_ARGS(vcpu_id, sint, vector, ret),
+
+ TP_STRUCT__entry(
+ __field(int, vcpu_id)
+ __field(u32, sint)
+ __field(int, vector)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __entry->vcpu_id = vcpu_id;
+ __entry->sint = sint;
+ __entry->vector = vector;
+ __entry->ret = ret;
+ ),
+
+ TP_printk("vcpu_id %d sint %u vector %d ret %d",
+ __entry->vcpu_id, __entry->sint, __entry->vector,
+ __entry->ret)
+);
+
+/*
+ * Tracepoint for kvm_hv_synic_send_eoi.
+ */
+TRACE_EVENT(kvm_hv_synic_send_eoi,
+ TP_PROTO(int vcpu_id, int vector),
+ TP_ARGS(vcpu_id, vector),
+
+ TP_STRUCT__entry(
+ __field(int, vcpu_id)
+ __field(u32, sint)
+ __field(int, vector)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __entry->vcpu_id = vcpu_id;
+ __entry->vector = vector;
+ ),
+
+ TP_printk("vcpu_id %d vector %d", __entry->vcpu_id, __entry->vector)
+);
+
+/*
+ * Tracepoint for synic_set_msr.
+ */
+TRACE_EVENT(kvm_hv_synic_set_msr,
+ TP_PROTO(int vcpu_id, u32 msr, u64 data, bool host),
+ TP_ARGS(vcpu_id, msr, data, host),
+
+ TP_STRUCT__entry(
+ __field(int, vcpu_id)
+ __field(u32, msr)
+ __field(u64, data)
+ __field(bool, host)
+ ),
+
+ TP_fast_assign(
+ __entry->vcpu_id = vcpu_id;
+ __entry->msr = msr;
+ __entry->data = data;
+ __entry->host = host
+ ),
+
+ TP_printk("vcpu_id %d msr 0x%x data 0x%llx host %d",
+ __entry->vcpu_id, __entry->msr, __entry->data, __entry->host)
+);
+
+/*
+ * Tracepoint for stimer_set_config.
+ */
+TRACE_EVENT(kvm_hv_stimer_set_config,
+ TP_PROTO(int vcpu_id, int timer_index, u64 config, bool host),
+ TP_ARGS(vcpu_id, timer_index, config, host),
+
+ TP_STRUCT__entry(
+ __field(int, vcpu_id)
+ __field(int, timer_index)
+ __field(u64, config)
+ __field(bool, host)
+ ),
+
+ TP_fast_assign(
+ __entry->vcpu_id = vcpu_id;
+ __entry->timer_index = timer_index;
+ __entry->config = config;
+ __entry->host = host;
+ ),
+
+ TP_printk("vcpu_id %d timer %d config 0x%llx host %d",
+ __entry->vcpu_id, __entry->timer_index, __entry->config,
+ __entry->host)
+);
+
+/*
+ * Tracepoint for stimer_set_count.
+ */
+TRACE_EVENT(kvm_hv_stimer_set_count,
+ TP_PROTO(int vcpu_id, int timer_index, u64 count, bool host),
+ TP_ARGS(vcpu_id, timer_index, count, host),
+
+ TP_STRUCT__entry(
+ __field(int, vcpu_id)
+ __field(int, timer_index)
+ __field(u64, count)
+ __field(bool, host)
+ ),
+
+ TP_fast_assign(
+ __entry->vcpu_id = vcpu_id;
+ __entry->timer_index = timer_index;
+ __entry->count = count;
+ __entry->host = host;
+ ),
+
+ TP_printk("vcpu_id %d timer %d count %llu host %d",
+ __entry->vcpu_id, __entry->timer_index, __entry->count,
+ __entry->host)
+);
+
+/*
+ * Tracepoint for stimer_start(periodic timer case).
+ */
+TRACE_EVENT(kvm_hv_stimer_start_periodic,
+ TP_PROTO(int vcpu_id, int timer_index, u64 time_now, u64 exp_time),
+ TP_ARGS(vcpu_id, timer_index, time_now, exp_time),
+
+ TP_STRUCT__entry(
+ __field(int, vcpu_id)
+ __field(int, timer_index)
+ __field(u64, time_now)
+ __field(u64, exp_time)
+ ),
+
+ TP_fast_assign(
+ __entry->vcpu_id = vcpu_id;
+ __entry->timer_index = timer_index;
+ __entry->time_now = time_now;
+ __entry->exp_time = exp_time;
+ ),
+
+ TP_printk("vcpu_id %d timer %d time_now %llu exp_time %llu",
+ __entry->vcpu_id, __entry->timer_index, __entry->time_now,
+ __entry->exp_time)
+);
+
+/*
+ * Tracepoint for stimer_start(one-shot timer case).
+ */
+TRACE_EVENT(kvm_hv_stimer_start_one_shot,
+ TP_PROTO(int vcpu_id, int timer_index, u64 time_now, u64 count),
+ TP_ARGS(vcpu_id, timer_index, time_now, count),
+
+ TP_STRUCT__entry(
+ __field(int, vcpu_id)
+ __field(int, timer_index)
+ __field(u64, time_now)
+ __field(u64, count)
+ ),
+
+ TP_fast_assign(
+ __entry->vcpu_id = vcpu_id;
+ __entry->timer_index = timer_index;
+ __entry->time_now = time_now;
+ __entry->count = count;
+ ),
+
+ TP_printk("vcpu_id %d timer %d time_now %llu count %llu",
+ __entry->vcpu_id, __entry->timer_index, __entry->time_now,
+ __entry->count)
+);
+
+/*
+ * Tracepoint for stimer_timer_callback.
+ */
+TRACE_EVENT(kvm_hv_stimer_callback,
+ TP_PROTO(int vcpu_id, int timer_index),
+ TP_ARGS(vcpu_id, timer_index),
+
+ TP_STRUCT__entry(
+ __field(int, vcpu_id)
+ __field(int, timer_index)
+ ),
+
+ TP_fast_assign(
+ __entry->vcpu_id = vcpu_id;
+ __entry->timer_index = timer_index;
+ ),
+
+ TP_printk("vcpu_id %d timer %d",
+ __entry->vcpu_id, __entry->timer_index)
+);
+
+/*
+ * Tracepoint for stimer_expiration.
+ */
+TRACE_EVENT(kvm_hv_stimer_expiration,
+ TP_PROTO(int vcpu_id, int timer_index, int msg_send_result),
+ TP_ARGS(vcpu_id, timer_index, msg_send_result),
+
+ TP_STRUCT__entry(
+ __field(int, vcpu_id)
+ __field(int, timer_index)
+ __field(int, msg_send_result)
+ ),
+
+ TP_fast_assign(
+ __entry->vcpu_id = vcpu_id;
+ __entry->timer_index = timer_index;
+ __entry->msg_send_result = msg_send_result;
+ ),
+
+ TP_printk("vcpu_id %d timer %d msg send result %d",
+ __entry->vcpu_id, __entry->timer_index,
+ __entry->msg_send_result)
+);
+
+/*
+ * Tracepoint for stimer_cleanup.
+ */
+TRACE_EVENT(kvm_hv_stimer_cleanup,
+ TP_PROTO(int vcpu_id, int timer_index),
+ TP_ARGS(vcpu_id, timer_index),
+
+ TP_STRUCT__entry(
+ __field(int, vcpu_id)
+ __field(int, timer_index)
+ ),
+
+ TP_fast_assign(
+ __entry->vcpu_id = vcpu_id;
+ __entry->timer_index = timer_index;
+ ),
+
+ TP_printk("vcpu_id %d timer %d",
+ __entry->vcpu_id, __entry->timer_index)
+);
+
#endif /* _TRACE_KVM_H */
#undef TRACE_INCLUDE_PATH
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 87acc5221740..04d61d496b14 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -19,6 +19,7 @@
#include "irq.h"
#include "mmu.h"
#include "cpuid.h"
+#include "lapic.h"
#include <linux/kvm_host.h>
#include <linux/module.h>
@@ -862,7 +863,6 @@ static void kvm_cpu_vmxon(u64 addr);
static void kvm_cpu_vmxoff(void);
static bool vmx_mpx_supported(void);
static bool vmx_xsaves_supported(void);
-static int vmx_cpu_uses_apicv(struct kvm_vcpu *vcpu);
static int vmx_set_tss_addr(struct kvm *kvm, unsigned int addr);
static void vmx_set_segment(struct kvm_vcpu *vcpu,
struct kvm_segment *var, int seg);
@@ -870,7 +870,6 @@ static void vmx_get_segment(struct kvm_vcpu *vcpu,
struct kvm_segment *var, int seg);
static bool guest_state_valid(struct kvm_vcpu *vcpu);
static u32 vmx_segment_access_rights(struct kvm_segment *var);
-static void vmx_sync_pir_to_irr_dummy(struct kvm_vcpu *vcpu);
static void copy_vmcs12_to_shadow(struct vcpu_vmx *vmx);
static void copy_shadow_to_vmcs12(struct vcpu_vmx *vmx);
static int alloc_identity_pagetable(struct kvm *kvm);
@@ -1448,7 +1447,51 @@ static inline void ept_sync_context(u64 eptp)
}
}
-static __always_inline unsigned long vmcs_readl(unsigned long field)
+static __always_inline void vmcs_check16(unsigned long field)
+{
+ BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6001) == 0x2000,
+ "16-bit accessor invalid for 64-bit field");
+ BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6001) == 0x2001,
+ "16-bit accessor invalid for 64-bit high field");
+ BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6000) == 0x4000,
+ "16-bit accessor invalid for 32-bit high field");
+ BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6000) == 0x6000,
+ "16-bit accessor invalid for natural width field");
+}
+
+static __always_inline void vmcs_check32(unsigned long field)
+{
+ BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6000) == 0,
+ "32-bit accessor invalid for 16-bit field");
+ BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6000) == 0x6000,
+ "32-bit accessor invalid for natural width field");
+}
+
+static __always_inline void vmcs_check64(unsigned long field)
+{
+ BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6000) == 0,
+ "64-bit accessor invalid for 16-bit field");
+ BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6001) == 0x2001,
+ "64-bit accessor invalid for 64-bit high field");
+ BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6000) == 0x4000,
+ "64-bit accessor invalid for 32-bit field");
+ BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6000) == 0x6000,
+ "64-bit accessor invalid for natural width field");
+}
+
+static __always_inline void vmcs_checkl(unsigned long field)
+{
+ BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6000) == 0,
+ "Natural width accessor invalid for 16-bit field");
+ BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6001) == 0x2000,
+ "Natural width accessor invalid for 64-bit field");
+ BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6001) == 0x2001,
+ "Natural width accessor invalid for 64-bit high field");
+ BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6000) == 0x4000,
+ "Natural width accessor invalid for 32-bit field");
+}
+
+static __always_inline unsigned long __vmcs_readl(unsigned long field)
{
unsigned long value;
@@ -1459,23 +1502,32 @@ static __always_inline unsigned long vmcs_readl(unsigned long field)
static __always_inline u16 vmcs_read16(unsigned long field)
{
- return vmcs_readl(field);
+ vmcs_check16(field);
+ return __vmcs_readl(field);
}
static __always_inline u32 vmcs_read32(unsigned long field)
{
- return vmcs_readl(field);
+ vmcs_check32(field);
+ return __vmcs_readl(field);
}
static __always_inline u64 vmcs_read64(unsigned long field)
{
+ vmcs_check64(field);
#ifdef CONFIG_X86_64
- return vmcs_readl(field);
+ return __vmcs_readl(field);
#else
- return vmcs_readl(field) | ((u64)vmcs_readl(field+1) << 32);
+ return __vmcs_readl(field) | ((u64)__vmcs_readl(field+1) << 32);
#endif
}
+static __always_inline unsigned long vmcs_readl(unsigned long field)
+{
+ vmcs_checkl(field);
+ return __vmcs_readl(field);
+}
+
static noinline void vmwrite_error(unsigned long field, unsigned long value)
{
printk(KERN_ERR "vmwrite error: reg %lx value %lx (err %d)\n",
@@ -1483,7 +1535,7 @@ static noinline void vmwrite_error(unsigned long field, unsigned long value)
dump_stack();
}
-static void vmcs_writel(unsigned long field, unsigned long value)
+static __always_inline void __vmcs_writel(unsigned long field, unsigned long value)
{
u8 error;
@@ -1493,33 +1545,46 @@ static void vmcs_writel(unsigned long field, unsigned long value)
vmwrite_error(field, value);
}
-static void vmcs_write16(unsigned long field, u16 value)
+static __always_inline void vmcs_write16(unsigned long field, u16 value)
{
- vmcs_writel(field, value);
+ vmcs_check16(field);
+ __vmcs_writel(field, value);
}
-static void vmcs_write32(unsigned long field, u32 value)
+static __always_inline void vmcs_write32(unsigned long field, u32 value)
{
- vmcs_writel(field, value);
+ vmcs_check32(field);
+ __vmcs_writel(field, value);
}
-static void vmcs_write64(unsigned long field, u64 value)
+static __always_inline void vmcs_write64(unsigned long field, u64 value)
{
- vmcs_writel(field, value);
+ vmcs_check64(field);
+ __vmcs_writel(field, value);
#ifndef CONFIG_X86_64
asm volatile ("");
- vmcs_writel(field+1, value >> 32);
+ __vmcs_writel(field+1, value >> 32);
#endif
}
-static void vmcs_clear_bits(unsigned long field, u32 mask)
+static __always_inline void vmcs_writel(unsigned long field, unsigned long value)
+{
+ vmcs_checkl(field);
+ __vmcs_writel(field, value);
+}
+
+static __always_inline void vmcs_clear_bits(unsigned long field, u32 mask)
{
- vmcs_writel(field, vmcs_readl(field) & ~mask);
+ BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6000) == 0x2000,
+ "vmcs_clear_bits does not support 64-bit fields");
+ __vmcs_writel(field, __vmcs_readl(field) & ~mask);
}
-static void vmcs_set_bits(unsigned long field, u32 mask)
+static __always_inline void vmcs_set_bits(unsigned long field, u32 mask)
{
- vmcs_writel(field, vmcs_readl(field) | mask);
+ BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6000) == 0x2000,
+ "vmcs_set_bits does not support 64-bit fields");
+ __vmcs_writel(field, __vmcs_readl(field) | mask);
}
static inline void vm_entry_controls_init(struct vcpu_vmx *vmx, u32 val)
@@ -2498,7 +2563,7 @@ static void nested_vmx_setup_ctls_msrs(struct vcpu_vmx *vmx)
vmx->nested.nested_vmx_pinbased_ctls_high |=
PIN_BASED_ALWAYSON_WITHOUT_TRUE_MSR |
PIN_BASED_VMX_PREEMPTION_TIMER;
- if (vmx_cpu_uses_apicv(&vmx->vcpu))
+ if (kvm_vcpu_apicv_active(&vmx->vcpu))
vmx->nested.nested_vmx_pinbased_ctls_high |=
PIN_BASED_POSTED_INTR;
@@ -2803,7 +2868,7 @@ static int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
msr_info->data = vcpu->arch.ia32_xss;
break;
case MSR_TSC_AUX:
- if (!guest_cpuid_has_rdtscp(vcpu))
+ if (!guest_cpuid_has_rdtscp(vcpu) && !msr_info->host_initiated)
return 1;
/* Otherwise falls through */
default:
@@ -2909,7 +2974,7 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
clear_atomic_switch_msr(vmx, MSR_IA32_XSS);
break;
case MSR_TSC_AUX:
- if (!guest_cpuid_has_rdtscp(vcpu))
+ if (!guest_cpuid_has_rdtscp(vcpu) && !msr_info->host_initiated)
return 1;
/* Check reserved bit, higher 32 bits should be zero */
if ((data >> 32) != 0)
@@ -4462,9 +4527,9 @@ static void vmx_disable_intercept_msr_write_x2apic(u32 msr)
msr, MSR_TYPE_W);
}
-static int vmx_cpu_uses_apicv(struct kvm_vcpu *vcpu)
+static bool vmx_get_enable_apicv(void)
{
- return enable_apicv && lapic_in_kernel(vcpu);
+ return enable_apicv;
}
static int vmx_complete_nested_posted_interrupt(struct kvm_vcpu *vcpu)
@@ -4586,11 +4651,6 @@ static void vmx_sync_pir_to_irr(struct kvm_vcpu *vcpu)
kvm_apic_update_irr(vcpu, vmx->pi_desc.pir);
}
-static void vmx_sync_pir_to_irr_dummy(struct kvm_vcpu *vcpu)
-{
- return;
-}
-
/*
* Set up the vmcs's constant host-state fields, i.e., host-state fields that
* will not change in the lifetime of the guest.
@@ -4660,11 +4720,18 @@ static u32 vmx_pin_based_exec_ctrl(struct vcpu_vmx *vmx)
{
u32 pin_based_exec_ctrl = vmcs_config.pin_based_exec_ctrl;
- if (!vmx_cpu_uses_apicv(&vmx->vcpu))
+ if (!kvm_vcpu_apicv_active(&vmx->vcpu))
pin_based_exec_ctrl &= ~PIN_BASED_POSTED_INTR;
return pin_based_exec_ctrl;
}
+static void vmx_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu)
+{
+ struct vcpu_vmx *vmx = to_vmx(vcpu);
+
+ vmcs_write32(PIN_BASED_VM_EXEC_CONTROL, vmx_pin_based_exec_ctrl(vmx));
+}
+
static u32 vmx_exec_control(struct vcpu_vmx *vmx)
{
u32 exec_control = vmcs_config.cpu_based_exec_ctrl;
@@ -4703,7 +4770,7 @@ static u32 vmx_secondary_exec_control(struct vcpu_vmx *vmx)
exec_control &= ~SECONDARY_EXEC_UNRESTRICTED_GUEST;
if (!ple_gap)
exec_control &= ~SECONDARY_EXEC_PAUSE_LOOP_EXITING;
- if (!vmx_cpu_uses_apicv(&vmx->vcpu))
+ if (!kvm_vcpu_apicv_active(&vmx->vcpu))
exec_control &= ~(SECONDARY_EXEC_APIC_REGISTER_VIRT |
SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY);
exec_control &= ~SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE;
@@ -4767,7 +4834,7 @@ static int vmx_vcpu_setup(struct vcpu_vmx *vmx)
vmcs_write32(SECONDARY_VM_EXEC_CONTROL,
vmx_secondary_exec_control(vmx));
- if (vmx_cpu_uses_apicv(&vmx->vcpu)) {
+ if (kvm_vcpu_apicv_active(&vmx->vcpu)) {
vmcs_write64(EOI_EXIT_BITMAP0, 0);
vmcs_write64(EOI_EXIT_BITMAP1, 0);
vmcs_write64(EOI_EXIT_BITMAP2, 0);
@@ -4775,7 +4842,7 @@ static int vmx_vcpu_setup(struct vcpu_vmx *vmx)
vmcs_write16(GUEST_INTR_STATUS, 0);
- vmcs_write64(POSTED_INTR_NV, POSTED_INTR_VECTOR);
+ vmcs_write16(POSTED_INTR_NV, POSTED_INTR_VECTOR);
vmcs_write64(POSTED_INTR_DESC_ADDR, __pa((&vmx->pi_desc)));
}
@@ -4867,7 +4934,7 @@ static void vmx_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)
seg_setup(VCPU_SREG_CS);
vmcs_write16(GUEST_CS_SELECTOR, 0xf000);
- vmcs_write32(GUEST_CS_BASE, 0xffff0000);
+ vmcs_writel(GUEST_CS_BASE, 0xffff0000ul);
seg_setup(VCPU_SREG_DS);
seg_setup(VCPU_SREG_ES);
@@ -4903,7 +4970,7 @@ static void vmx_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)
vmcs_write32(GUEST_ACTIVITY_STATE, GUEST_ACTIVITY_ACTIVE);
vmcs_write32(GUEST_INTERRUPTIBILITY_INFO, 0);
- vmcs_write32(GUEST_PENDING_DBG_EXCEPTIONS, 0);
+ vmcs_writel(GUEST_PENDING_DBG_EXCEPTIONS, 0);
setup_msrs(vmx);
@@ -4919,7 +4986,7 @@ static void vmx_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)
kvm_make_request(KVM_REQ_APIC_PAGE_RELOAD, vcpu);
- if (vmx_cpu_uses_apicv(vcpu))
+ if (kvm_vcpu_apicv_active(vcpu))
memset(&vmx->pi_desc, 0, sizeof(struct pi_desc));
if (vmx->vpid != 0)
@@ -6203,15 +6270,6 @@ static __init int hardware_setup(void)
kvm_tsc_scaling_ratio_frac_bits = 48;
}
- if (enable_apicv)
- kvm_x86_ops->update_cr8_intercept = NULL;
- else {
- kvm_x86_ops->hwapic_irr_update = NULL;
- kvm_x86_ops->hwapic_isr_update = NULL;
- kvm_x86_ops->deliver_posted_interrupt = NULL;
- kvm_x86_ops->sync_pir_to_irr = vmx_sync_pir_to_irr_dummy;
- }
-
vmx_disable_intercept_for_msr(MSR_FS_BASE, false);
vmx_disable_intercept_for_msr(MSR_GS_BASE, false);
vmx_disable_intercept_for_msr(MSR_KERNEL_GS_BASE, true);
@@ -7394,11 +7452,6 @@ static int handle_invvpid(struct kvm_vcpu *vcpu)
switch (type) {
case VMX_VPID_EXTENT_ALL_CONTEXT:
- if (get_vmcs12(vcpu)->virtual_processor_id == 0) {
- nested_vmx_failValid(vcpu,
- VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID);
- return 1;
- }
__vmx_flush_tlb(vcpu, to_vmx(vcpu)->nested.vpid02);
nested_vmx_succeed(vcpu);
break;
@@ -7906,7 +7959,7 @@ static void dump_vmcs(void)
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);
+ u64 efer = vmcs_read64(GUEST_IA32_EFER);
int i, n;
if (cpu_has_secondary_exec_ctrls())
@@ -7922,10 +7975,10 @@ static void dump_vmcs(void)
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("PDPTR0 = 0x%016llx PDPTR1 = 0x%016llx\n",
+ vmcs_read64(GUEST_PDPTR0), vmcs_read64(GUEST_PDPTR1));
+ pr_err("PDPTR2 = 0x%016llx PDPTR3 = 0x%016llx\n",
+ vmcs_read64(GUEST_PDPTR2), vmcs_read64(GUEST_PDPTR3));
}
pr_err("RSP = 0x%016lx RIP = 0x%016lx\n",
vmcs_readl(GUEST_RSP), vmcs_readl(GUEST_RIP));
@@ -7946,16 +7999,16 @@ static void dump_vmcs(void)
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),
+ pr_err("EFER = 0x%016llx PAT = 0x%016llx\n",
+ efer, vmcs_read64(GUEST_IA32_PAT));
+ pr_err("DebugCtl = 0x%016llx DebugExceptions = 0x%016lx\n",
+ vmcs_read64(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));
+ pr_err("PerfGlobCtl = 0x%016llx\n",
+ vmcs_read64(GUEST_IA32_PERF_GLOBAL_CTRL));
if (vmentry_ctl & VM_ENTRY_LOAD_BNDCFGS)
- pr_err("BndCfgS = 0x%016lx\n", vmcs_readl(GUEST_BNDCFGS));
+ pr_err("BndCfgS = 0x%016llx\n", vmcs_read64(GUEST_BNDCFGS));
pr_err("Interruptibility = %08x ActivityState = %08x\n",
vmcs_read32(GUEST_INTERRUPTIBILITY_INFO),
vmcs_read32(GUEST_ACTIVITY_STATE));
@@ -7984,11 +8037,12 @@ static void dump_vmcs(void)
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));
+ pr_err("EFER = 0x%016llx PAT = 0x%016llx\n",
+ vmcs_read64(HOST_IA32_EFER),
+ vmcs_read64(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("PerfGlobCtl = 0x%016llx\n",
+ vmcs_read64(HOST_IA32_PERF_GLOBAL_CTRL));
pr_err("*** Control State ***\n");
pr_err("PinBased=%08x CPUBased=%08x SecondaryExec=%08x\n",
@@ -8011,16 +8065,16 @@ static void dump_vmcs(void)
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));
+ pr_err("TSC Offset = 0x%016llx\n", vmcs_read64(TSC_OFFSET));
if (secondary_exec_control & SECONDARY_EXEC_TSC_SCALING)
- pr_err("TSC Multiplier = 0x%016lx\n",
- vmcs_readl(TSC_MULTIPLIER));
+ pr_err("TSC Multiplier = 0x%016llx\n",
+ vmcs_read64(TSC_MULTIPLIER));
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));
+ pr_err("EPT pointer = 0x%016llx\n", vmcs_read64(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",
@@ -8047,6 +8101,8 @@ static int vmx_handle_exit(struct kvm_vcpu *vcpu)
u32 exit_reason = vmx->exit_reason;
u32 vectoring_info = vmx->idt_vectoring_info;
+ trace_kvm_exit(exit_reason, vcpu, KVM_ISA_VMX);
+
/*
* Flush logged GPAs PML buffer, this will make dirty_bitmap more
* updated. Another good is, in kvm_vm_ioctl_get_dirty_log, before
@@ -8157,7 +8213,7 @@ static void vmx_set_virtual_x2apic_mode(struct kvm_vcpu *vcpu, bool set)
* apicv
*/
if (!cpu_has_vmx_virtualize_x2apic_mode() ||
- !vmx_cpu_uses_apicv(vcpu))
+ !kvm_vcpu_apicv_active(vcpu))
return;
if (!cpu_need_tpr_shadow(vcpu))
@@ -8262,10 +8318,9 @@ static void vmx_hwapic_irr_update(struct kvm_vcpu *vcpu, int max_irr)
}
}
-static void vmx_load_eoi_exitmap(struct kvm_vcpu *vcpu)
+static void vmx_load_eoi_exitmap(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap)
{
- u64 *eoi_exit_bitmap = vcpu->arch.eoi_exit_bitmap;
- if (!vmx_cpu_uses_apicv(vcpu))
+ if (!kvm_vcpu_apicv_active(vcpu))
return;
vmcs_write64(EOI_EXIT_BITMAP0, eoi_exit_bitmap[0]);
@@ -8673,7 +8728,6 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu)
vmx->loaded_vmcs->launched = 1;
vmx->exit_reason = vmcs_read32(VM_EXIT_REASON);
- trace_kvm_exit(vmx->exit_reason, vcpu, KVM_ISA_VMX);
/*
* the KVM_REQ_EVENT optimization bit is only on for one entry, and if
@@ -8936,7 +8990,8 @@ static void vmx_cpuid_update(struct kvm_vcpu *vcpu)
best->ebx &= ~bit(X86_FEATURE_INVPCID);
}
- vmcs_set_secondary_exec_control(secondary_exec_ctl);
+ if (cpu_has_secondary_exec_ctrls())
+ vmcs_set_secondary_exec_control(secondary_exec_ctl);
if (static_cpu_has(X86_FEATURE_PCOMMIT) && nested) {
if (guest_cpuid_has_pcommit(vcpu))
@@ -9512,7 +9567,7 @@ static void prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
*/
vmx->nested.posted_intr_nv = vmcs12->posted_intr_nv;
vmx->nested.pi_pending = false;
- vmcs_write64(POSTED_INTR_NV, POSTED_INTR_VECTOR);
+ vmcs_write16(POSTED_INTR_NV, POSTED_INTR_VECTOR);
vmcs_write64(POSTED_INTR_DESC_ADDR,
page_to_phys(vmx->nested.pi_desc_page) +
(unsigned long)(vmcs12->posted_intr_desc_addr &
@@ -10173,7 +10228,7 @@ static void prepare_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12,
* Additionally, restore L2's PDPTR to vmcs12.
*/
if (enable_ept) {
- vmcs12->guest_cr3 = vmcs_read64(GUEST_CR3);
+ vmcs12->guest_cr3 = vmcs_readl(GUEST_CR3);
vmcs12->guest_pdptr0 = vmcs_read64(GUEST_PDPTR0);
vmcs12->guest_pdptr1 = vmcs_read64(GUEST_PDPTR1);
vmcs12->guest_pdptr2 = vmcs_read64(GUEST_PDPTR2);
@@ -10809,7 +10864,8 @@ static struct kvm_x86_ops vmx_x86_ops = {
.update_cr8_intercept = update_cr8_intercept,
.set_virtual_x2apic_mode = vmx_set_virtual_x2apic_mode,
.set_apic_access_page_addr = vmx_set_apic_access_page_addr,
- .cpu_uses_apicv = vmx_cpu_uses_apicv,
+ .get_enable_apicv = vmx_get_enable_apicv,
+ .refresh_apicv_exec_ctrl = vmx_refresh_apicv_exec_ctrl,
.load_eoi_exitmap = vmx_load_eoi_exitmap,
.hwapic_irr_update = vmx_hwapic_irr_update,
.hwapic_isr_update = vmx_hwapic_isr_update,
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 00462bd63129..f53f5b13c677 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -951,7 +951,7 @@ static u32 msrs_to_save[] = {
MSR_CSTAR, MSR_KERNEL_GS_BASE, MSR_SYSCALL_MASK, MSR_LSTAR,
#endif
MSR_IA32_TSC, MSR_IA32_CR_PAT, MSR_VM_HSAVE_PA,
- MSR_IA32_FEATURE_CONTROL, MSR_IA32_BNDCFGS
+ MSR_IA32_FEATURE_CONTROL, MSR_IA32_BNDCFGS, MSR_TSC_AUX,
};
static unsigned num_msrs_to_save;
@@ -966,6 +966,8 @@ static u32 emulated_msrs[] = {
HV_X64_MSR_RESET,
HV_X64_MSR_VP_INDEX,
HV_X64_MSR_VP_RUNTIME,
+ HV_X64_MSR_SCONTROL,
+ HV_X64_MSR_STIMER0_CONFIG,
HV_X64_MSR_APIC_ASSIST_PAGE, MSR_KVM_ASYNC_PF_EN, MSR_KVM_STEAL_TIME,
MSR_KVM_PV_EOI_EN,
@@ -1167,7 +1169,8 @@ static void kvm_write_wall_clock(struct kvm *kvm, gpa_t wall_clock)
++version;
- kvm_write_guest(kvm, wall_clock, &version, sizeof(version));
+ if (kvm_write_guest(kvm, wall_clock, &version, sizeof(version)))
+ return;
/*
* The guest calculates current wall clock time by adding
@@ -1683,6 +1686,11 @@ static void pvclock_update_vm_gtod_copy(struct kvm *kvm)
#endif
}
+void kvm_make_mclock_inprogress_request(struct kvm *kvm)
+{
+ kvm_make_all_cpus_request(kvm, KVM_REQ_MCLOCK_INPROGRESS);
+}
+
static void kvm_gen_update_masterclock(struct kvm *kvm)
{
#ifdef CONFIG_X86_64
@@ -2198,6 +2206,7 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
case HV_X64_MSR_GUEST_OS_ID ... HV_X64_MSR_SINT15:
case HV_X64_MSR_CRASH_P0 ... HV_X64_MSR_CRASH_P4:
case HV_X64_MSR_CRASH_CTL:
+ case HV_X64_MSR_STIMER0_CONFIG ... HV_X64_MSR_STIMER3_COUNT:
return kvm_hv_set_msr_common(vcpu, msr, data,
msr_info->host_initiated);
case MSR_IA32_BBL_CR_CTL3:
@@ -2402,6 +2411,7 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
case HV_X64_MSR_GUEST_OS_ID ... HV_X64_MSR_SINT15:
case HV_X64_MSR_CRASH_P0 ... HV_X64_MSR_CRASH_P4:
case HV_X64_MSR_CRASH_CTL:
+ case HV_X64_MSR_STIMER0_CONFIG ... HV_X64_MSR_STIMER3_COUNT:
return kvm_hv_get_msr_common(vcpu,
msr_info->index, &msr_info->data);
break;
@@ -2541,6 +2551,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
case KVM_CAP_HYPERV:
case KVM_CAP_HYPERV_VAPIC:
case KVM_CAP_HYPERV_SPIN:
+ case KVM_CAP_HYPERV_SYNIC:
case KVM_CAP_PCI_SEGMENT:
case KVM_CAP_DEBUGREGS:
case KVM_CAP_X86_ROBUST_SINGLESTEP:
@@ -2693,6 +2704,11 @@ static bool need_emulate_wbinvd(struct kvm_vcpu *vcpu)
return kvm_arch_has_noncoherent_dma(vcpu->kvm);
}
+static inline void kvm_migrate_timers(struct kvm_vcpu *vcpu)
+{
+ set_bit(KVM_REQ_MIGRATE_TIMER, &vcpu->requests);
+}
+
void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
{
/* Address WBINVD may be executed by guest */
@@ -2748,7 +2764,9 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
static int kvm_vcpu_ioctl_get_lapic(struct kvm_vcpu *vcpu,
struct kvm_lapic_state *s)
{
- kvm_x86_ops->sync_pir_to_irr(vcpu);
+ if (vcpu->arch.apicv_active)
+ kvm_x86_ops->sync_pir_to_irr(vcpu);
+
memcpy(s->regs, vcpu->arch.apic->regs, sizeof *s);
return 0;
@@ -2763,6 +2781,26 @@ static int kvm_vcpu_ioctl_set_lapic(struct kvm_vcpu *vcpu,
return 0;
}
+static int kvm_cpu_accept_dm_intr(struct kvm_vcpu *vcpu)
+{
+ return (!lapic_in_kernel(vcpu) ||
+ kvm_apic_accept_pic_intr(vcpu));
+}
+
+/*
+ * if userspace requested an interrupt window, check that the
+ * interrupt window is open.
+ *
+ * No need to exit to userspace if we already have an interrupt queued.
+ */
+static int kvm_vcpu_ready_for_interrupt_injection(struct kvm_vcpu *vcpu)
+{
+ return kvm_arch_interrupt_allowed(vcpu) &&
+ !kvm_cpu_has_interrupt(vcpu) &&
+ !kvm_event_needs_reinjection(vcpu) &&
+ kvm_cpu_accept_dm_intr(vcpu);
+}
+
static int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu,
struct kvm_interrupt *irq)
{
@@ -2786,6 +2824,7 @@ static int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu,
return -EEXIST;
vcpu->arch.pending_external_vector = irq->irq;
+ kvm_make_request(KVM_REQ_EVENT, vcpu);
return 0;
}
@@ -3170,6 +3209,20 @@ static int kvm_set_guest_paused(struct kvm_vcpu *vcpu)
return 0;
}
+static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
+ struct kvm_enable_cap *cap)
+{
+ if (cap->flags)
+ return -EINVAL;
+
+ switch (cap->cap) {
+ case KVM_CAP_HYPERV_SYNIC:
+ return kvm_hv_activate_synic(vcpu);
+ default:
+ return -EINVAL;
+ }
+}
+
long kvm_arch_vcpu_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg)
{
@@ -3434,6 +3487,15 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
r = kvm_set_guest_paused(vcpu);
goto out;
}
+ case KVM_ENABLE_CAP: {
+ struct kvm_enable_cap cap;
+
+ r = -EFAULT;
+ if (copy_from_user(&cap, argp, sizeof(cap)))
+ goto out;
+ r = kvm_vcpu_ioctl_enable_cap(vcpu, &cap);
+ break;
+ }
default:
r = -EINVAL;
}
@@ -3551,9 +3613,11 @@ static int kvm_vm_ioctl_get_pit(struct kvm *kvm, struct kvm_pit_state *ps)
static int kvm_vm_ioctl_set_pit(struct kvm *kvm, struct kvm_pit_state *ps)
{
+ int i;
mutex_lock(&kvm->arch.vpit->pit_state.lock);
memcpy(&kvm->arch.vpit->pit_state, ps, sizeof(struct kvm_pit_state));
- kvm_pit_load_count(kvm, 0, ps->channels[0].count, 0);
+ for (i = 0; i < 3; i++)
+ kvm_pit_load_count(kvm, i, ps->channels[i].count, 0);
mutex_unlock(&kvm->arch.vpit->pit_state.lock);
return 0;
}
@@ -3572,6 +3636,7 @@ static int kvm_vm_ioctl_get_pit2(struct kvm *kvm, struct kvm_pit_state2 *ps)
static int kvm_vm_ioctl_set_pit2(struct kvm *kvm, struct kvm_pit_state2 *ps)
{
int start = 0;
+ int i;
u32 prev_legacy, cur_legacy;
mutex_lock(&kvm->arch.vpit->pit_state.lock);
prev_legacy = kvm->arch.vpit->pit_state.flags & KVM_PIT_FLAGS_HPET_LEGACY;
@@ -3581,7 +3646,9 @@ static int kvm_vm_ioctl_set_pit2(struct kvm *kvm, struct kvm_pit_state2 *ps)
memcpy(&kvm->arch.vpit->pit_state.channels, &ps->channels,
sizeof(kvm->arch.vpit->pit_state.channels));
kvm->arch.vpit->pit_state.flags = ps->flags;
- kvm_pit_load_count(kvm, 0, kvm->arch.vpit->pit_state.channels[0].count, start);
+ for (i = 0; i < 3; i++)
+ kvm_pit_load_count(kvm, i, kvm->arch.vpit->pit_state.channels[i].count,
+ start && i == 0);
mutex_unlock(&kvm->arch.vpit->pit_state.lock);
return 0;
}
@@ -3980,16 +4047,17 @@ static void kvm_init_msr_list(void)
/*
* Even MSRs that are valid in the host may not be exposed
- * to the guests in some cases. We could work around this
- * in VMX with the generic MSR save/load machinery, but it
- * is not really worthwhile since it will really only
- * happen with nested virtualization.
+ * to the guests in some cases.
*/
switch (msrs_to_save[i]) {
case MSR_IA32_BNDCFGS:
if (!kvm_x86_ops->mpx_supported())
continue;
break;
+ case MSR_TSC_AUX:
+ if (!kvm_x86_ops->rdtscp_supported())
+ continue;
+ break;
default:
break;
}
@@ -5846,6 +5914,12 @@ static void kvm_pv_kick_cpu_op(struct kvm *kvm, unsigned long flags, int apicid)
kvm_irq_delivery_to_apic(kvm, NULL, &lapic_irq, NULL);
}
+void kvm_vcpu_deactivate_apicv(struct kvm_vcpu *vcpu)
+{
+ vcpu->arch.apicv_active = false;
+ kvm_x86_ops->refresh_apicv_exec_ctrl(vcpu);
+}
+
int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
{
unsigned long nr, a0, a1, a2, a3, ret;
@@ -5910,23 +5984,10 @@ static int emulator_fix_hypercall(struct x86_emulate_ctxt *ctxt)
return emulator_write_emulated(ctxt, rip, instruction, 3, NULL);
}
-/*
- * Check if userspace requested an interrupt window, and that the
- * interrupt window is open.
- *
- * No need to exit to userspace if we already have an interrupt queued.
- */
static int dm_request_for_irq_injection(struct kvm_vcpu *vcpu)
{
- if (!vcpu->run->request_interrupt_window || pic_in_kernel(vcpu->kvm))
- return false;
-
- if (kvm_cpu_has_interrupt(vcpu))
- return false;
-
- return (irqchip_split(vcpu->kvm)
- ? kvm_apic_accept_pic_intr(vcpu)
- : kvm_arch_interrupt_allowed(vcpu));
+ return vcpu->run->request_interrupt_window &&
+ likely(!pic_in_kernel(vcpu->kvm));
}
static void post_kvm_run_save(struct kvm_vcpu *vcpu)
@@ -5937,17 +5998,9 @@ static void post_kvm_run_save(struct kvm_vcpu *vcpu)
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))
- kvm_run->ready_for_interrupt_injection =
- kvm_arch_interrupt_allowed(vcpu) &&
- !kvm_cpu_has_interrupt(vcpu) &&
- !kvm_event_needs_reinjection(vcpu);
- else if (!pic_in_kernel(vcpu->kvm))
- kvm_run->ready_for_interrupt_injection =
- kvm_apic_accept_pic_intr(vcpu) &&
- !kvm_cpu_has_interrupt(vcpu);
- else
- kvm_run->ready_for_interrupt_injection = 1;
+ kvm_run->ready_for_interrupt_injection =
+ pic_in_kernel(vcpu->kvm) ||
+ kvm_vcpu_ready_for_interrupt_injection(vcpu);
}
static void update_cr8_intercept(struct kvm_vcpu *vcpu)
@@ -5960,6 +6013,9 @@ static void update_cr8_intercept(struct kvm_vcpu *vcpu)
if (!vcpu->arch.apic)
return;
+ if (vcpu->arch.apicv_active)
+ return;
+
if (!vcpu->arch.apic->vapic_addr)
max_irr = kvm_lapic_find_highest_irr(vcpu);
else
@@ -6296,20 +6352,30 @@ static void process_smi(struct kvm_vcpu *vcpu)
kvm_mmu_reset_context(vcpu);
}
+void kvm_make_scan_ioapic_request(struct kvm *kvm)
+{
+ kvm_make_all_cpus_request(kvm, KVM_REQ_SCAN_IOAPIC);
+}
+
static void vcpu_scan_ioapic(struct kvm_vcpu *vcpu)
{
+ u64 eoi_exit_bitmap[4];
+
if (!kvm_apic_hw_enabled(vcpu->arch.apic))
return;
- memset(vcpu->arch.eoi_exit_bitmap, 0, 256 / 8);
+ bitmap_zero(vcpu->arch.ioapic_handled_vectors, 256);
if (irqchip_split(vcpu->kvm))
- kvm_scan_ioapic_routes(vcpu, vcpu->arch.eoi_exit_bitmap);
+ kvm_scan_ioapic_routes(vcpu, vcpu->arch.ioapic_handled_vectors);
else {
- kvm_x86_ops->sync_pir_to_irr(vcpu);
- kvm_ioapic_scan_entry(vcpu, vcpu->arch.eoi_exit_bitmap);
+ if (vcpu->arch.apicv_active)
+ kvm_x86_ops->sync_pir_to_irr(vcpu);
+ kvm_ioapic_scan_entry(vcpu, vcpu->arch.ioapic_handled_vectors);
}
- kvm_x86_ops->load_eoi_exitmap(vcpu);
+ bitmap_or((ulong *)eoi_exit_bitmap, vcpu->arch.ioapic_handled_vectors,
+ vcpu_to_synic(vcpu)->vec_bitmap, 256);
+ kvm_x86_ops->load_eoi_exitmap(vcpu, eoi_exit_bitmap);
}
static void kvm_vcpu_flush_tlb(struct kvm_vcpu *vcpu)
@@ -6360,8 +6426,10 @@ void kvm_arch_mmu_notifier_invalidate_page(struct kvm *kvm,
static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
{
int r;
- bool req_int_win = !lapic_in_kernel(vcpu) &&
- vcpu->run->request_interrupt_window;
+ bool req_int_win =
+ dm_request_for_irq_injection(vcpu) &&
+ kvm_cpu_accept_dm_intr(vcpu);
+
bool req_immediate_exit = false;
if (vcpu->requests) {
@@ -6415,7 +6483,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
if (kvm_check_request(KVM_REQ_IOAPIC_EOI_EXIT, vcpu)) {
BUG_ON(vcpu->arch.pending_ioapic_eoi > 255);
if (test_bit(vcpu->arch.pending_ioapic_eoi,
- (void *) vcpu->arch.eoi_exit_bitmap)) {
+ vcpu->arch.ioapic_handled_vectors)) {
vcpu->run->exit_reason = KVM_EXIT_IOAPIC_EOI;
vcpu->run->eoi.vector =
vcpu->arch.pending_ioapic_eoi;
@@ -6439,6 +6507,20 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
r = 0;
goto out;
}
+ if (kvm_check_request(KVM_REQ_HV_EXIT, vcpu)) {
+ vcpu->run->exit_reason = KVM_EXIT_HYPERV;
+ vcpu->run->hyperv = vcpu->arch.hyperv.exit;
+ r = 0;
+ goto out;
+ }
+
+ /*
+ * KVM_REQ_HV_STIMER has to be processed after
+ * KVM_REQ_CLOCK_UPDATE, because Hyper-V SynIC timers
+ * depend on the guest clock being up-to-date
+ */
+ if (kvm_check_request(KVM_REQ_HV_STIMER, vcpu))
+ kvm_hv_process_stimers(vcpu);
}
/*
@@ -6450,7 +6532,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
* Update architecture specific hints for APIC
* virtual interrupt delivery.
*/
- if (kvm_x86_ops->hwapic_irr_update)
+ if (vcpu->arch.apicv_active)
kvm_x86_ops->hwapic_irr_update(vcpu,
kvm_lapic_find_highest_irr(vcpu));
}
@@ -6513,6 +6595,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
if (req_immediate_exit)
smp_send_reschedule(vcpu->cpu);
+ trace_kvm_entry(vcpu->vcpu_id);
+ wait_lapic_expire(vcpu);
__kvm_guest_enter();
if (unlikely(vcpu->arch.switch_db_regs)) {
@@ -6525,8 +6609,6 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
vcpu->arch.switch_db_regs &= ~KVM_DEBUGREG_RELOAD;
}
- trace_kvm_entry(vcpu->vcpu_id);
- wait_lapic_expire(vcpu);
kvm_x86_ops->run(vcpu);
/*
@@ -6663,7 +6745,8 @@ static int vcpu_run(struct kvm_vcpu *vcpu)
if (kvm_cpu_has_pending_timer(vcpu))
kvm_inject_pending_timer_irqs(vcpu);
- if (dm_request_for_irq_injection(vcpu)) {
+ if (dm_request_for_irq_injection(vcpu) &&
+ kvm_vcpu_ready_for_interrupt_injection(vcpu)) {
r = 0;
vcpu->run->exit_reason = KVM_EXIT_IRQ_WINDOW_OPEN;
++vcpu->stat.request_irq_exits;
@@ -7520,6 +7603,7 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
BUG_ON(vcpu->kvm == NULL);
kvm = vcpu->kvm;
+ vcpu->arch.apicv_active = kvm_x86_ops->get_enable_apicv();
vcpu->arch.pv.pv_unhalted = false;
vcpu->arch.emulate_ctxt.ops = &emulate_ops;
if (!irqchip_in_kernel(kvm) || kvm_vcpu_is_reset_bsp(vcpu))
@@ -7577,6 +7661,8 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
vcpu->arch.pending_external_vector = -1;
+ kvm_hv_vcpu_init(vcpu);
+
return 0;
fail_free_mce_banks:
@@ -7595,6 +7681,7 @@ void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu)
{
int idx;
+ kvm_hv_vcpu_uninit(vcpu);
kvm_pmu_destroy(vcpu);
kfree(vcpu->arch.mce_banks);
kvm_free_lapic(vcpu);
@@ -7989,6 +8076,9 @@ static inline bool kvm_vcpu_has_events(struct kvm_vcpu *vcpu)
kvm_cpu_has_interrupt(vcpu))
return true;
+ if (kvm_hv_has_stimer_pending(vcpu))
+ return true;
+
return false;
}
diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c
index a0d09f6c6533..4ba229ac3f4f 100644
--- a/arch/x86/lguest/boot.c
+++ b/arch/x86/lguest/boot.c
@@ -1414,6 +1414,7 @@ __init void lguest_init(void)
pv_info.kernel_rpl = 1;
/* Everyone except Xen runs with this set. */
pv_info.shared_kernel_pmd = 1;
+ pv_info.features = 0;
/*
* We set up all the lguest overrides for sensitive operations. These
@@ -1472,7 +1473,6 @@ __init void lguest_init(void)
pv_mmu_ops.lazy_mode.leave = lguest_leave_lazy_mmu_mode;
pv_mmu_ops.lazy_mode.flush = paravirt_flush_lazy_mmu;
pv_mmu_ops.pte_update = lguest_pte_update;
- pv_mmu_ops.pte_update_defer = lguest_pte_update;
#ifdef CONFIG_X86_LOCAL_APIC
/* APIC read/write intercepts */
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
index f2587888d987..a501fa25da41 100644
--- a/arch/x86/lib/Makefile
+++ b/arch/x86/lib/Makefile
@@ -16,7 +16,7 @@ clean-files := inat-tables.c
obj-$(CONFIG_SMP) += msr-smp.o cache-smp.o
-lib-y := delay.o misc.o cmdline.o
+lib-y := delay.o misc.o cmdline.o cpu.o
lib-y += usercopy_$(BITS).o usercopy.o getuser.o putuser.o
lib-y += memcpy_$(BITS).o
lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
diff --git a/arch/x86/lib/cpu.c b/arch/x86/lib/cpu.c
new file mode 100644
index 000000000000..aa417a97511c
--- /dev/null
+++ b/arch/x86/lib/cpu.c
@@ -0,0 +1,35 @@
+#include <linux/module.h>
+
+unsigned int x86_family(unsigned int sig)
+{
+ unsigned int x86;
+
+ x86 = (sig >> 8) & 0xf;
+
+ if (x86 == 0xf)
+ x86 += (sig >> 20) & 0xff;
+
+ return x86;
+}
+EXPORT_SYMBOL_GPL(x86_family);
+
+unsigned int x86_model(unsigned int sig)
+{
+ unsigned int fam, model;
+
+ fam = x86_family(sig);
+
+ model = (sig >> 4) & 0xf;
+
+ if (fam >= 0x6)
+ model += ((sig >> 16) & 0xf) << 4;
+
+ return model;
+}
+EXPORT_SYMBOL_GPL(x86_model);
+
+unsigned int x86_stepping(unsigned int sig)
+{
+ return sig & 0xf;
+}
+EXPORT_SYMBOL_GPL(x86_stepping);
diff --git a/arch/x86/lib/msr.c b/arch/x86/lib/msr.c
index 43623739c7cf..004c861b1648 100644
--- a/arch/x86/lib/msr.c
+++ b/arch/x86/lib/msr.c
@@ -1,6 +1,8 @@
#include <linux/module.h>
#include <linux/preempt.h>
#include <asm/msr.h>
+#define CREATE_TRACE_POINTS
+#include <asm/msr-trace.h>
struct msr *msrs_alloc(void)
{
@@ -108,3 +110,27 @@ int msr_clear_bit(u32 msr, u8 bit)
{
return __flip_bit(msr, bit, false);
}
+
+#ifdef CONFIG_TRACEPOINTS
+void do_trace_write_msr(unsigned msr, u64 val, int failed)
+{
+ trace_write_msr(msr, val, failed);
+}
+EXPORT_SYMBOL(do_trace_write_msr);
+EXPORT_TRACEPOINT_SYMBOL(write_msr);
+
+void do_trace_read_msr(unsigned msr, u64 val, int failed)
+{
+ trace_read_msr(msr, val, failed);
+}
+EXPORT_SYMBOL(do_trace_read_msr);
+EXPORT_TRACEPOINT_SYMBOL(read_msr);
+
+void do_trace_rdpmc(unsigned counter, u64 val, int failed)
+{
+ trace_rdpmc(counter, val, failed);
+}
+EXPORT_SYMBOL(do_trace_rdpmc);
+EXPORT_TRACEPOINT_SYMBOL(rdpmc);
+
+#endif
diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile
index 65c47fda26fc..f9d38a48e3c8 100644
--- a/arch/x86/mm/Makefile
+++ b/arch/x86/mm/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_X86_32) += pgtable_32.o iomap_32.o
obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
obj-$(CONFIG_X86_PTDUMP_CORE) += dump_pagetables.o
+obj-$(CONFIG_X86_PTDUMP) += debug_pagetables.o
obj-$(CONFIG_HIGHMEM) += highmem_32.o
diff --git a/arch/x86/mm/debug_pagetables.c b/arch/x86/mm/debug_pagetables.c
new file mode 100644
index 000000000000..bfcffdf6c577
--- /dev/null
+++ b/arch/x86/mm/debug_pagetables.c
@@ -0,0 +1,46 @@
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <asm/pgtable.h>
+
+static int ptdump_show(struct seq_file *m, void *v)
+{
+ ptdump_walk_pgd_level(m, NULL);
+ return 0;
+}
+
+static int ptdump_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, ptdump_show, NULL);
+}
+
+static const struct file_operations ptdump_fops = {
+ .owner = THIS_MODULE,
+ .open = ptdump_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct dentry *pe;
+
+static int __init pt_dump_debug_init(void)
+{
+ pe = debugfs_create_file("kernel_page_tables", S_IRUSR, NULL, NULL,
+ &ptdump_fops);
+ if (!pe)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void __exit pt_dump_debug_exit(void)
+{
+ debugfs_remove_recursive(pe);
+}
+
+module_init(pt_dump_debug_init);
+module_exit(pt_dump_debug_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Arjan van de Ven <arjan@linux.intel.com>");
+MODULE_DESCRIPTION("Kernel debugging helper that dumps pagetables");
diff --git a/arch/x86/mm/dump_pagetables.c b/arch/x86/mm/dump_pagetables.c
index a035c2aa7801..4a6f1d9b5106 100644
--- a/arch/x86/mm/dump_pagetables.c
+++ b/arch/x86/mm/dump_pagetables.c
@@ -89,7 +89,7 @@ static struct addr_marker address_markers[] = {
{ 0/* VMALLOC_START */, "vmalloc() Area" },
{ 0/*VMALLOC_END*/, "vmalloc() End" },
# ifdef CONFIG_HIGHMEM
- { 0/*PKMAP_BASE*/, "Persisent kmap() Area" },
+ { 0/*PKMAP_BASE*/, "Persistent kmap() Area" },
# endif
{ 0/*FIXADDR_START*/, "Fixmap Area" },
#endif
@@ -426,38 +426,15 @@ void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd)
{
ptdump_walk_pgd_level_core(m, pgd, false);
}
+EXPORT_SYMBOL_GPL(ptdump_walk_pgd_level);
void ptdump_walk_pgd_level_checkwx(void)
{
ptdump_walk_pgd_level_core(NULL, NULL, true);
}
-#ifdef CONFIG_X86_PTDUMP
-static int ptdump_show(struct seq_file *m, void *v)
+static int __init pt_dump_init(void)
{
- ptdump_walk_pgd_level(m, NULL);
- return 0;
-}
-
-static int ptdump_open(struct inode *inode, struct file *filp)
-{
- return single_open(filp, ptdump_show, NULL);
-}
-
-static const struct file_operations ptdump_fops = {
- .open = ptdump_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-#endif
-
-static int pt_dump_init(void)
-{
-#ifdef CONFIG_X86_PTDUMP
- struct dentry *pe;
-#endif
-
#ifdef CONFIG_X86_32
/* Not a compile-time constant on x86-32 */
address_markers[VMALLOC_START_NR].start_address = VMALLOC_START;
@@ -468,13 +445,6 @@ static int pt_dump_init(void)
address_markers[FIXADDR_START_NR].start_address = FIXADDR_START;
#endif
-#ifdef CONFIG_X86_PTDUMP
- pe = debugfs_create_file("kernel_page_tables", 0600, NULL, NULL,
- &ptdump_fops);
- if (!pe)
- return -ENOMEM;
-#endif
-
return 0;
}
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index ec081fe0ce2c..8829482d69ec 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -814,8 +814,7 @@ remove_pte_table(pte_t *pte_start, unsigned long addr, unsigned long end,
if (phys_addr < (phys_addr_t)0x40000000)
return;
- if (IS_ALIGNED(addr, PAGE_SIZE) &&
- IS_ALIGNED(next, PAGE_SIZE)) {
+ if (PAGE_ALIGNED(addr) && PAGE_ALIGNED(next)) {
/*
* Do not free direct mapping pages since they were
* freed when offlining, or simplely not in use.
diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c
index b9c78f3bcd67..0d8d53d1f5cc 100644
--- a/arch/x86/mm/ioremap.c
+++ b/arch/x86/mm/ioremap.c
@@ -194,8 +194,8 @@ static void __iomem *__ioremap_caller(resource_size_t phys_addr,
* Check if the request spans more than any BAR in the iomem resource
* tree.
*/
- WARN_ONCE(iomem_map_sanity_check(unaligned_phys_addr, unaligned_size),
- KERN_INFO "Info: mapping multiple BARs. Your kernel is fine.");
+ if (iomem_map_sanity_check(unaligned_phys_addr, unaligned_size))
+ pr_warn("caller %pS mapping multiple BARs\n", caller);
return ret_addr;
err_free_area:
diff --git a/arch/x86/mm/mmap.c b/arch/x86/mm/mmap.c
index 844b06d67df4..96bd1e2bffaf 100644
--- a/arch/x86/mm/mmap.c
+++ b/arch/x86/mm/mmap.c
@@ -69,14 +69,14 @@ unsigned long arch_mmap_rnd(void)
{
unsigned long rnd;
- /*
- * 8 bits of randomness in 32bit mmaps, 20 address space bits
- * 28 bits of randomness in 64bit mmaps, 40 address space bits
- */
if (mmap_is_ia32())
- rnd = (unsigned long)get_random_int() % (1<<8);
+#ifdef CONFIG_COMPAT
+ rnd = (unsigned long)get_random_int() & ((1 << mmap_rnd_compat_bits) - 1);
+#else
+ rnd = (unsigned long)get_random_int() & ((1 << mmap_rnd_bits) - 1);
+#endif
else
- rnd = (unsigned long)get_random_int() % (1<<28);
+ rnd = (unsigned long)get_random_int() & ((1 << mmap_rnd_bits) - 1);
return rnd << PAGE_SHIFT;
}
diff --git a/arch/x86/mm/mpx.c b/arch/x86/mm/mpx.c
index 1202d5ca2fb5..b2fd67da1701 100644
--- a/arch/x86/mm/mpx.c
+++ b/arch/x86/mm/mpx.c
@@ -101,19 +101,19 @@ static int get_reg_offset(struct insn *insn, struct pt_regs *regs,
switch (type) {
case REG_TYPE_RM:
regno = X86_MODRM_RM(insn->modrm.value);
- if (X86_REX_B(insn->rex_prefix.value) == 1)
+ if (X86_REX_B(insn->rex_prefix.value))
regno += 8;
break;
case REG_TYPE_INDEX:
regno = X86_SIB_INDEX(insn->sib.value);
- if (X86_REX_X(insn->rex_prefix.value) == 1)
+ if (X86_REX_X(insn->rex_prefix.value))
regno += 8;
break;
case REG_TYPE_BASE:
regno = X86_SIB_BASE(insn->sib.value);
- if (X86_REX_B(insn->rex_prefix.value) == 1)
+ if (X86_REX_B(insn->rex_prefix.value))
regno += 8;
break;
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c
index a3137a4feed1..fc6a4c8f6e2a 100644
--- a/arch/x86/mm/pageattr.c
+++ b/arch/x86/mm/pageattr.c
@@ -66,6 +66,9 @@ void update_page_count(int level, unsigned long pages)
static void split_page_count(int level)
{
+ if (direct_pages_count[level] == 0)
+ return;
+
direct_pages_count[level]--;
direct_pages_count[level - 1] += PTRS_PER_PTE;
}
@@ -129,14 +132,16 @@ within(unsigned long addr, unsigned long start, unsigned long end)
*/
void clflush_cache_range(void *vaddr, unsigned int size)
{
- unsigned long clflush_mask = boot_cpu_data.x86_clflush_size - 1;
+ const unsigned long clflush_size = boot_cpu_data.x86_clflush_size;
+ void *p = (void *)((unsigned long)vaddr & ~(clflush_size - 1));
void *vend = vaddr + size;
- void *p;
+
+ if (p >= vend)
+ return;
mb();
- for (p = (void *)((unsigned long)vaddr & ~clflush_mask);
- p < vend; p += boot_cpu_data.x86_clflush_size)
+ for (; p < vend; p += clflush_size)
clflushopt(p);
mb();
diff --git a/arch/x86/mm/pat.c b/arch/x86/mm/pat.c
index 188e3e07eeeb..031782e74231 100644
--- a/arch/x86/mm/pat.c
+++ b/arch/x86/mm/pat.c
@@ -586,7 +586,7 @@ int free_memtype(u64 start, u64 end)
entry = rbt_memtype_erase(start, end);
spin_unlock(&memtype_lock);
- if (!entry) {
+ if (IS_ERR(entry)) {
pr_info("x86/PAT: %s:%d freeing invalid memtype [mem %#010Lx-%#010Lx]\n",
current->comm, current->pid, start, end - 1);
return -EINVAL;
@@ -992,6 +992,16 @@ void untrack_pfn(struct vm_area_struct *vma, unsigned long pfn,
vma->vm_flags &= ~VM_PAT;
}
+/*
+ * untrack_pfn_moved is called, while mremapping a pfnmap for a new region,
+ * with the old vma after its pfnmap page table has been removed. The new
+ * vma has a new pfnmap to the same pfn & cache type with VM_PAT set.
+ */
+void untrack_pfn_moved(struct vm_area_struct *vma)
+{
+ vma->vm_flags &= ~VM_PAT;
+}
+
pgprot_t pgprot_writecombine(pgprot_t prot)
{
return __pgprot(pgprot_val(prot) |
diff --git a/arch/x86/mm/pat_rbtree.c b/arch/x86/mm/pat_rbtree.c
index 63931080366a..2f7702253ccf 100644
--- a/arch/x86/mm/pat_rbtree.c
+++ b/arch/x86/mm/pat_rbtree.c
@@ -98,8 +98,13 @@ static struct memtype *memtype_rb_lowest_match(struct rb_root *root,
return last_lower; /* Returns NULL if there is no overlap */
}
-static struct memtype *memtype_rb_exact_match(struct rb_root *root,
- u64 start, u64 end)
+enum {
+ MEMTYPE_EXACT_MATCH = 0,
+ MEMTYPE_END_MATCH = 1
+};
+
+static struct memtype *memtype_rb_match(struct rb_root *root,
+ u64 start, u64 end, int match_type)
{
struct memtype *match;
@@ -107,7 +112,12 @@ static struct memtype *memtype_rb_exact_match(struct rb_root *root,
while (match != NULL && match->start < end) {
struct rb_node *node;
- if (match->start == start && match->end == end)
+ if ((match_type == MEMTYPE_EXACT_MATCH) &&
+ (match->start == start) && (match->end == end))
+ return match;
+
+ if ((match_type == MEMTYPE_END_MATCH) &&
+ (match->start < start) && (match->end == end))
return match;
node = rb_next(&match->rb);
@@ -117,7 +127,7 @@ static struct memtype *memtype_rb_exact_match(struct rb_root *root,
match = NULL;
}
- return NULL; /* Returns NULL if there is no exact match */
+ return NULL; /* Returns NULL if there is no match */
}
static int memtype_rb_check_conflict(struct rb_root *root,
@@ -210,12 +220,36 @@ struct memtype *rbt_memtype_erase(u64 start, u64 end)
{
struct memtype *data;
- data = memtype_rb_exact_match(&memtype_rbroot, start, end);
- if (!data)
- goto out;
+ /*
+ * Since the memtype_rbroot tree allows overlapping ranges,
+ * rbt_memtype_erase() checks with EXACT_MATCH first, i.e. free
+ * a whole node for the munmap case. If no such entry is found,
+ * it then checks with END_MATCH, i.e. shrink the size of a node
+ * from the end for the mremap case.
+ */
+ data = memtype_rb_match(&memtype_rbroot, start, end,
+ MEMTYPE_EXACT_MATCH);
+ if (!data) {
+ data = memtype_rb_match(&memtype_rbroot, start, end,
+ MEMTYPE_END_MATCH);
+ if (!data)
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (data->start == start) {
+ /* munmap: erase this node */
+ rb_erase_augmented(&data->rb, &memtype_rbroot,
+ &memtype_rb_augment_cb);
+ } else {
+ /* mremap: update the end value of this node */
+ rb_erase_augmented(&data->rb, &memtype_rbroot,
+ &memtype_rb_augment_cb);
+ data->end = start;
+ data->subtree_max_end = data->end;
+ memtype_rb_insert(&memtype_rbroot, data);
+ return NULL;
+ }
- rb_erase_augmented(&data->rb, &memtype_rbroot, &memtype_rb_augment_cb);
-out:
return data;
}
diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c
index fb0a9dd1d6e4..ee9c2e3a7199 100644
--- a/arch/x86/mm/pgtable.c
+++ b/arch/x86/mm/pgtable.c
@@ -414,7 +414,7 @@ int ptep_set_access_flags(struct vm_area_struct *vma,
if (changed && dirty) {
*ptep = entry;
- pte_update_defer(vma->vm_mm, address, ptep);
+ pte_update(vma->vm_mm, address, ptep);
}
return changed;
@@ -431,7 +431,6 @@ int pmdp_set_access_flags(struct vm_area_struct *vma,
if (changed && dirty) {
*pmdp = entry;
- pmd_update_defer(vma->vm_mm, address, pmdp);
/*
* We had a write-protection fault here and changed the pmd
* to to more permissive. No need to flush the TLB for that,
@@ -469,9 +468,6 @@ int pmdp_test_and_clear_young(struct vm_area_struct *vma,
ret = test_and_clear_bit(_PAGE_BIT_ACCESSED,
(unsigned long *)pmdp);
- if (ret)
- pmd_update(vma->vm_mm, addr, pmdp);
-
return ret;
}
#endif
@@ -518,7 +514,6 @@ void pmdp_splitting_flush(struct vm_area_struct *vma,
set = !test_and_set_bit(_PAGE_BIT_SPLITTING,
(unsigned long *)pmdp);
if (set) {
- pmd_update(vma->vm_mm, address, pmdp);
/* need tlb flush only to serialize against gup-fast */
flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
}
diff --git a/arch/x86/mm/setup_nx.c b/arch/x86/mm/setup_nx.c
index 90555bf60aa4..92e2eacb3321 100644
--- a/arch/x86/mm/setup_nx.c
+++ b/arch/x86/mm/setup_nx.c
@@ -31,7 +31,7 @@ early_param("noexec", noexec_setup);
void x86_configure_nx(void)
{
- if (cpu_has_nx && !disable_nx)
+ if (boot_cpu_has(X86_FEATURE_NX) && !disable_nx)
__supported_pte_mask |= _PAGE_NX;
else
__supported_pte_mask &= ~_PAGE_NX;
@@ -39,7 +39,7 @@ void x86_configure_nx(void)
void __init x86_report_nx(void)
{
- if (!cpu_has_nx) {
+ if (!boot_cpu_has(X86_FEATURE_NX)) {
printk(KERN_NOTICE "Notice: NX (Execute Disable) protection "
"missing in CPU!\n");
} else {
diff --git a/arch/x86/mm/srat.c b/arch/x86/mm/srat.c
index c2aea63bee20..b5f821881465 100644
--- a/arch/x86/mm/srat.c
+++ b/arch/x86/mm/srat.c
@@ -203,6 +203,8 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
pr_warn("SRAT: Failed to mark hotplug range [mem %#010Lx-%#010Lx] in memblock\n",
(unsigned long long)start, (unsigned long long)end - 1);
+ max_possible_pfn = max(max_possible_pfn, PFN_UP(end - 1));
+
return 0;
out_err_bad_srat:
bad_srat();
diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
index 8ddb5d0d66fb..8f4cc3dfac32 100644
--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -161,7 +161,10 @@ void flush_tlb_current_task(void)
preempt_disable();
count_vm_tlb_event(NR_TLB_LOCAL_FLUSH_ALL);
+
+ /* This is an implicit full barrier that synchronizes with switch_mm. */
local_flush_tlb();
+
trace_tlb_flush(TLB_LOCAL_SHOOTDOWN, TLB_FLUSH_ALL);
if (cpumask_any_but(mm_cpumask(mm), smp_processor_id()) < nr_cpu_ids)
flush_tlb_others(mm_cpumask(mm), mm, 0UL, TLB_FLUSH_ALL);
@@ -188,17 +191,29 @@ void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start,
unsigned long base_pages_to_flush = TLB_FLUSH_ALL;
preempt_disable();
- if (current->active_mm != mm)
+ if (current->active_mm != mm) {
+ /* Synchronize with switch_mm. */
+ smp_mb();
+
goto out;
+ }
if (!current->mm) {
leave_mm(smp_processor_id());
+
+ /* Synchronize with switch_mm. */
+ smp_mb();
+
goto out;
}
if ((end != TLB_FLUSH_ALL) && !(vmflag & VM_HUGETLB))
base_pages_to_flush = (end - start) >> PAGE_SHIFT;
+ /*
+ * Both branches below are implicit full barriers (MOV to CR or
+ * INVLPG) that synchronize with switch_mm.
+ */
if (base_pages_to_flush > tlb_single_page_flush_ceiling) {
base_pages_to_flush = TLB_FLUSH_ALL;
count_vm_tlb_event(NR_TLB_LOCAL_FLUSH_ALL);
@@ -228,10 +243,18 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long start)
preempt_disable();
if (current->active_mm == mm) {
- if (current->mm)
+ if (current->mm) {
+ /*
+ * Implicit full barrier (INVLPG) that synchronizes
+ * with switch_mm.
+ */
__flush_tlb_one(start);
- else
+ } else {
leave_mm(smp_processor_id());
+
+ /* Synchronize with switch_mm. */
+ smp_mb();
+ }
}
if (cpumask_any_but(mm_cpumask(mm), smp_processor_id()) < nr_cpu_ids)
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index 75991979f667..4286f3618bd0 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -193,7 +193,7 @@ struct jit_context {
32 /* space for rbx, r13, r14, r15 */ + \
8 /* space for skb_copy_bits() buffer */)
-#define PROLOGUE_SIZE 51
+#define PROLOGUE_SIZE 48
/* emit x64 prologue code for BPF program and check it's size.
* bpf_tail_call helper will skip it while jumping into another program
@@ -229,11 +229,15 @@ static void emit_prologue(u8 **pprog)
/* mov qword ptr [rbp-X],r15 */
EMIT3_off32(0x4C, 0x89, 0xBD, -STACKSIZE + 24);
- /* clear A and X registers */
- EMIT2(0x31, 0xc0); /* xor eax, eax */
- EMIT3(0x4D, 0x31, 0xED); /* xor r13, r13 */
+ /* Clear the tail call counter (tail_call_cnt): for eBPF tail calls
+ * we need to reset the counter to 0. It's done in two instructions,
+ * resetting rax register to 0 (xor on eax gets 0 extended), and
+ * moving it to the counter location.
+ */
- /* clear tail_cnt: mov qword ptr [rbp-X], rax */
+ /* xor eax, eax */
+ EMIT2(0x31, 0xc0);
+ /* mov qword ptr [rbp-X], rax */
EMIT3_off32(0x48, 0x89, 0x85, -STACKSIZE + 32);
BUILD_BUG_ON(cnt != PROLOGUE_SIZE);
@@ -455,6 +459,18 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
}
case BPF_ALU | BPF_MOV | BPF_K:
+ /* optimization: if imm32 is zero, use 'xor <dst>,<dst>'
+ * to save 3 bytes.
+ */
+ if (imm32 == 0) {
+ if (is_ereg(dst_reg))
+ EMIT1(add_2mod(0x40, dst_reg, dst_reg));
+ b2 = 0x31; /* xor */
+ b3 = 0xC0;
+ EMIT2(b2, add_2reg(b3, dst_reg, dst_reg));
+ break;
+ }
+
/* mov %eax, imm32 */
if (is_ereg(dst_reg))
EMIT1(add_1mod(0x40, dst_reg));
@@ -469,6 +485,20 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
return -EINVAL;
}
+ /* optimization: if imm64 is zero, use 'xor <dst>,<dst>'
+ * to save 7 bytes.
+ */
+ if (insn[0].imm == 0 && insn[1].imm == 0) {
+ b1 = add_2mod(0x48, dst_reg, dst_reg);
+ b2 = 0x31; /* xor */
+ b3 = 0xC0;
+ EMIT3(b1, b2, add_2reg(b3, dst_reg, dst_reg));
+
+ insn++;
+ i++;
+ break;
+ }
+
/* movabsq %rax, imm64 */
EMIT2(add_1mod(0x48, dst_reg), add_1reg(0xB8, dst_reg));
EMIT(insn[0].imm, 4);
diff --git a/arch/x86/pci/bus_numa.c b/arch/x86/pci/bus_numa.c
index 7bcf06a7cd12..6eb3c8af96e2 100644
--- a/arch/x86/pci/bus_numa.c
+++ b/arch/x86/pci/bus_numa.c
@@ -50,18 +50,9 @@ void x86_pci_root_bus_resources(int bus, struct list_head *resources)
if (!found)
pci_add_resource(resources, &info->busn);
- list_for_each_entry(root_res, &info->resources, list) {
- struct resource *res;
- struct resource *root;
+ list_for_each_entry(root_res, &info->resources, list)
+ pci_add_resource(resources, &root_res->res);
- res = &root_res->res;
- pci_add_resource(resources, res);
- if (res->flags & IORESOURCE_IO)
- root = &ioport_resource;
- else
- root = &iomem_resource;
- insert_resource(root, res);
- }
return;
default_resources:
diff --git a/arch/x86/platform/atom/punit_atom_debug.c b/arch/x86/platform/atom/punit_atom_debug.c
index 5ca8ead91579..81c769e80614 100644
--- a/arch/x86/platform/atom/punit_atom_debug.c
+++ b/arch/x86/platform/atom/punit_atom_debug.c
@@ -25,8 +25,6 @@
#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 */
@@ -85,9 +83,8 @@ static int punit_dev_state_show(struct seq_file *seq_file, void *unused)
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);
+ status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
+ punit_devp->reg, &punit_pwr_status);
if (status) {
seq_printf(seq_file, "%9s : Read Failed\n",
punit_devp->name);
diff --git a/arch/x86/platform/intel-quark/imr.c b/arch/x86/platform/intel-quark/imr.c
index 0ee619f9fcb7..c1bdafaac3ca 100644
--- a/arch/x86/platform/intel-quark/imr.c
+++ b/arch/x86/platform/intel-quark/imr.c
@@ -111,23 +111,19 @@ static int imr_read(struct imr_device *idev, u32 imr_id, struct imr_regs *imr)
u32 reg = imr_id * IMR_NUM_REGS + idev->reg_base;
int ret;
- ret = iosf_mbi_read(QRK_MBI_UNIT_MM, QRK_MBI_MM_READ,
- reg++, &imr->addr_lo);
+ ret = iosf_mbi_read(QRK_MBI_UNIT_MM, MBI_REG_READ, reg++, &imr->addr_lo);
if (ret)
return ret;
- ret = iosf_mbi_read(QRK_MBI_UNIT_MM, QRK_MBI_MM_READ,
- reg++, &imr->addr_hi);
+ ret = iosf_mbi_read(QRK_MBI_UNIT_MM, MBI_REG_READ, reg++, &imr->addr_hi);
if (ret)
return ret;
- ret = iosf_mbi_read(QRK_MBI_UNIT_MM, QRK_MBI_MM_READ,
- reg++, &imr->rmask);
+ ret = iosf_mbi_read(QRK_MBI_UNIT_MM, MBI_REG_READ, reg++, &imr->rmask);
if (ret)
return ret;
- return iosf_mbi_read(QRK_MBI_UNIT_MM, QRK_MBI_MM_READ,
- reg++, &imr->wmask);
+ return iosf_mbi_read(QRK_MBI_UNIT_MM, MBI_REG_READ, reg++, &imr->wmask);
}
/**
@@ -151,31 +147,27 @@ static int imr_write(struct imr_device *idev, u32 imr_id,
local_irq_save(flags);
- ret = iosf_mbi_write(QRK_MBI_UNIT_MM, QRK_MBI_MM_WRITE, reg++,
- imr->addr_lo);
+ ret = iosf_mbi_write(QRK_MBI_UNIT_MM, MBI_REG_WRITE, reg++, imr->addr_lo);
if (ret)
goto failed;
- ret = iosf_mbi_write(QRK_MBI_UNIT_MM, QRK_MBI_MM_WRITE,
- reg++, imr->addr_hi);
+ ret = iosf_mbi_write(QRK_MBI_UNIT_MM, MBI_REG_WRITE, reg++, imr->addr_hi);
if (ret)
goto failed;
- ret = iosf_mbi_write(QRK_MBI_UNIT_MM, QRK_MBI_MM_WRITE,
- reg++, imr->rmask);
+ ret = iosf_mbi_write(QRK_MBI_UNIT_MM, MBI_REG_WRITE, reg++, imr->rmask);
if (ret)
goto failed;
- ret = iosf_mbi_write(QRK_MBI_UNIT_MM, QRK_MBI_MM_WRITE,
- reg++, imr->wmask);
+ ret = iosf_mbi_write(QRK_MBI_UNIT_MM, MBI_REG_WRITE, reg++, imr->wmask);
if (ret)
goto failed;
/* Lock bit must be set separately to addr_lo address bits. */
if (lock) {
imr->addr_lo |= IMR_LOCK;
- ret = iosf_mbi_write(QRK_MBI_UNIT_MM, QRK_MBI_MM_WRITE,
- reg - IMR_NUM_REGS, imr->addr_lo);
+ ret = iosf_mbi_write(QRK_MBI_UNIT_MM, MBI_REG_WRITE,
+ reg - IMR_NUM_REGS, imr->addr_lo);
if (ret)
goto failed;
}
diff --git a/arch/x86/platform/uv/uv_nmi.c b/arch/x86/platform/uv/uv_nmi.c
index 327f21c3bde1..8dd80050d705 100644
--- a/arch/x86/platform/uv/uv_nmi.c
+++ b/arch/x86/platform/uv/uv_nmi.c
@@ -28,6 +28,7 @@
#include <linux/nmi.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/clocksource.h>
#include <asm/apic.h>
#include <asm/current.h>
diff --git a/arch/x86/power/cpu.c b/arch/x86/power/cpu.c
index 9ab52791fed5..d5f64996394a 100644
--- a/arch/x86/power/cpu.c
+++ b/arch/x86/power/cpu.c
@@ -23,6 +23,7 @@
#include <asm/debugreg.h>
#include <asm/cpu.h>
#include <asm/mmu_context.h>
+#include <linux/dmi.h>
#ifdef CONFIG_X86_32
__visible unsigned long saved_context_ebx;
@@ -32,6 +33,29 @@ __visible unsigned long saved_context_eflags;
#endif
struct saved_context saved_context;
+static void msr_save_context(struct saved_context *ctxt)
+{
+ struct saved_msr *msr = ctxt->saved_msrs.array;
+ struct saved_msr *end = msr + ctxt->saved_msrs.num;
+
+ while (msr < end) {
+ msr->valid = !rdmsrl_safe(msr->info.msr_no, &msr->info.reg.q);
+ msr++;
+ }
+}
+
+static void msr_restore_context(struct saved_context *ctxt)
+{
+ struct saved_msr *msr = ctxt->saved_msrs.array;
+ struct saved_msr *end = msr + ctxt->saved_msrs.num;
+
+ while (msr < end) {
+ if (msr->valid)
+ wrmsrl(msr->info.msr_no, msr->info.reg.q);
+ msr++;
+ }
+}
+
/**
* __save_processor_state - save CPU registers before creating a
* hibernation image and before restoring the memory state from it
@@ -111,6 +135,7 @@ static void __save_processor_state(struct saved_context *ctxt)
#endif
ctxt->misc_enable_saved = !rdmsrl_safe(MSR_IA32_MISC_ENABLE,
&ctxt->misc_enable);
+ msr_save_context(ctxt);
}
/* Needed by apm.c */
@@ -229,6 +254,7 @@ static void notrace __restore_processor_state(struct saved_context *ctxt)
x86_platform.restore_sched_clock_state();
mtrr_bp_restore();
perf_restore_debug_store();
+ msr_restore_context(ctxt);
}
/* Needed by apm.c */
@@ -320,3 +346,69 @@ static int __init bsp_pm_check_init(void)
}
core_initcall(bsp_pm_check_init);
+
+static int msr_init_context(const u32 *msr_id, const int total_num)
+{
+ int i = 0;
+ struct saved_msr *msr_array;
+
+ if (saved_context.saved_msrs.array || saved_context.saved_msrs.num > 0) {
+ pr_err("x86/pm: MSR quirk already applied, please check your DMI match table.\n");
+ return -EINVAL;
+ }
+
+ msr_array = kmalloc_array(total_num, sizeof(struct saved_msr), GFP_KERNEL);
+ if (!msr_array) {
+ pr_err("x86/pm: Can not allocate memory to save/restore MSRs during suspend.\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < total_num; i++) {
+ msr_array[i].info.msr_no = msr_id[i];
+ msr_array[i].valid = false;
+ msr_array[i].info.reg.q = 0;
+ }
+ saved_context.saved_msrs.num = total_num;
+ saved_context.saved_msrs.array = msr_array;
+
+ return 0;
+}
+
+/*
+ * The following section is a quirk framework for problematic BIOSen:
+ * Sometimes MSRs are modified by the BIOSen after suspended to
+ * RAM, this might cause unexpected behavior after wakeup.
+ * Thus we save/restore these specified MSRs across suspend/resume
+ * in order to work around it.
+ *
+ * For any further problematic BIOSen/platforms,
+ * please add your own function similar to msr_initialize_bdw.
+ */
+static int msr_initialize_bdw(const struct dmi_system_id *d)
+{
+ /* Add any extra MSR ids into this array. */
+ u32 bdw_msr_id[] = { MSR_IA32_THERM_CONTROL };
+
+ pr_info("x86/pm: %s detected, MSR saving is needed during suspending.\n", d->ident);
+ return msr_init_context(bdw_msr_id, ARRAY_SIZE(bdw_msr_id));
+}
+
+static struct dmi_system_id msr_save_dmi_table[] = {
+ {
+ .callback = msr_initialize_bdw,
+ .ident = "BROADWELL BDX_EP",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "GRANTLEY"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "E63448-400"),
+ },
+ },
+ {}
+};
+
+static int pm_check_save_msr(void)
+{
+ dmi_check_system(msr_save_dmi_table);
+ return 0;
+}
+
+device_initcall(pm_check_save_msr);
diff --git a/arch/x86/um/Makefile b/arch/x86/um/Makefile
index a8fecc226946..3ee2bb6b440b 100644
--- a/arch/x86/um/Makefile
+++ b/arch/x86/um/Makefile
@@ -17,7 +17,7 @@ obj-y = bug.o bugs_$(BITS).o delay.o fault.o ksyms.o ldt.o \
ifeq ($(CONFIG_X86_32),y)
obj-y += checksum_32.o
-obj-$(CONFIG_BINFMT_ELF) += elfcore.o
+obj-$(CONFIG_ELF_CORE) += elfcore.o
subarch-y = ../lib/string_32.o ../lib/atomic64_32.o ../lib/atomic64_cx8_32.o
subarch-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += ../lib/rwsem.o
diff --git a/arch/x86/um/asm/syscall.h b/arch/x86/um/asm/syscall.h
index 81d6562ce01d..11ab90dc5f14 100644
--- a/arch/x86/um/asm/syscall.h
+++ b/arch/x86/um/asm/syscall.h
@@ -1,6 +1,7 @@
#ifndef __UM_ASM_SYSCALL_H
#define __UM_ASM_SYSCALL_H
+#include <asm/syscall-generic.h>
#include <uapi/linux/audit.h>
typedef asmlinkage long (*sys_call_ptr_t)(unsigned long, unsigned long,
diff --git a/arch/x86/um/ptrace_32.c b/arch/x86/um/ptrace_32.c
index a29756f2d940..47c78d5e5c32 100644
--- a/arch/x86/um/ptrace_32.c
+++ b/arch/x86/um/ptrace_32.c
@@ -68,6 +68,7 @@ static const int reg_offsets[] = {
[EFL] = HOST_EFLAGS,
[UESP] = HOST_SP,
[SS] = HOST_SS,
+ [ORIG_EAX] = HOST_ORIG_AX,
};
int putreg(struct task_struct *child, int regno, unsigned long value)
@@ -83,6 +84,7 @@ int putreg(struct task_struct *child, int regno, unsigned long value)
case EAX:
case EIP:
case UESP:
+ case ORIG_EAX:
break;
case FS:
if (value && (value & 3) != 3)
@@ -108,9 +110,6 @@ int putreg(struct task_struct *child, int regno, unsigned long value)
value &= FLAG_MASK;
child->thread.regs.regs.gp[HOST_EFLAGS] |= value;
return 0;
- case ORIG_EAX:
- child->thread.regs.regs.syscall = value;
- return 0;
default :
panic("Bad register in putreg() : %d\n", regno);
}
@@ -143,8 +142,6 @@ unsigned long getreg(struct task_struct *child, int regno)
regno >>= 2;
switch (regno) {
- case ORIG_EAX:
- return child->thread.regs.regs.syscall;
case FS:
case GS:
case DS:
@@ -163,6 +160,7 @@ unsigned long getreg(struct task_struct *child, int regno)
case EDI:
case EBP:
case EFL:
+ case ORIG_EAX:
break;
default:
panic("Bad register in getreg() : %d\n", regno);
diff --git a/arch/x86/um/signal.c b/arch/x86/um/signal.c
index 06934a8a4872..14fcd01ed992 100644
--- a/arch/x86/um/signal.c
+++ b/arch/x86/um/signal.c
@@ -211,7 +211,7 @@ static int copy_sc_from_user(struct pt_regs *regs,
if (err)
return 1;
- err = convert_fxsr_from_user(&fpx, sc.fpstate);
+ err = convert_fxsr_from_user(&fpx, (void *)sc.fpstate);
if (err)
return 1;
@@ -227,7 +227,7 @@ static int copy_sc_from_user(struct pt_regs *regs,
{
struct user_i387_struct fp;
- err = copy_from_user(&fp, sc.fpstate,
+ err = copy_from_user(&fp, (void *)sc.fpstate,
sizeof(struct user_i387_struct));
if (err)
return 1;
@@ -291,7 +291,7 @@ static int copy_sc_to_user(struct sigcontext __user *to,
#endif
#undef PUTREG
sc.oldmask = mask;
- sc.fpstate = to_fp;
+ sc.fpstate = (unsigned long)to_fp;
err = copy_to_user(to, &sc, sizeof(struct sigcontext));
if (err)
@@ -468,12 +468,10 @@ long sys_sigreturn(void)
struct sigframe __user *frame = (struct sigframe __user *)(sp - 8);
sigset_t set;
struct sigcontext __user *sc = &frame->sc;
- unsigned long __user *oldmask = &sc->oldmask;
- unsigned long __user *extramask = frame->extramask;
int sig_size = (_NSIG_WORDS - 1) * sizeof(unsigned long);
- if (copy_from_user(&set.sig[0], oldmask, sizeof(set.sig[0])) ||
- copy_from_user(&set.sig[1], extramask, sig_size))
+ if (copy_from_user(&set.sig[0], &sc->oldmask, sizeof(set.sig[0])) ||
+ copy_from_user(&set.sig[1], frame->extramask, sig_size))
goto segfault;
set_current_blocked(&set);
@@ -505,6 +503,7 @@ int setup_signal_stack_si(unsigned long stack_top, struct ksignal *ksig,
{
struct rt_sigframe __user *frame;
int err = 0, sig = ksig->sig;
+ unsigned long fp_to;
frame = (struct rt_sigframe __user *)
round_down(stack_top - sizeof(struct rt_sigframe), 16);
@@ -526,7 +525,10 @@ int setup_signal_stack_si(unsigned long stack_top, struct ksignal *ksig,
err |= __save_altstack(&frame->uc.uc_stack, PT_REGS_SP(regs));
err |= copy_sc_to_user(&frame->uc.uc_mcontext, &frame->fpstate, regs,
set->sig[0]);
- err |= __put_user(&frame->fpstate, &frame->uc.uc_mcontext.fpstate);
+
+ fp_to = (unsigned long)&frame->fpstate;
+
+ err |= __put_user(fp_to, &frame->uc.uc_mcontext.fpstate);
if (sizeof(*set) == 16) {
err |= __put_user(set->sig[0], &frame->uc.uc_sigmask.sig[0]);
err |= __put_user(set->sig[1], &frame->uc.uc_sigmask.sig[1]);
diff --git a/arch/x86/xen/apic.c b/arch/x86/xen/apic.c
index acda713ab5be..abf4901c917b 100644
--- a/arch/x86/xen/apic.c
+++ b/arch/x86/xen/apic.c
@@ -64,7 +64,7 @@ static u32 xen_apic_read(u32 reg)
if (reg != APIC_ID)
return 0;
- ret = HYPERVISOR_dom0_op(&op);
+ ret = HYPERVISOR_platform_op(&op);
if (ret)
return 0;
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
index 5774800ff583..d09e4c9d7cc5 100644
--- a/arch/x86/xen/enlighten.c
+++ b/arch/x86/xen/enlighten.c
@@ -415,7 +415,7 @@ static bool __init xen_check_mwait(void)
set_xen_guest_handle(op.u.set_pminfo.pdc, buf);
- if ((HYPERVISOR_dom0_op(&op) == 0) &&
+ if ((HYPERVISOR_platform_op(&op) == 0) &&
(buf[2] & (ACPI_PDC_C_C1_FFH | ACPI_PDC_C_C2C3_FFH))) {
cpuid_leaf5_ecx_val = cx;
cpuid_leaf5_edx_val = dx;
@@ -1192,7 +1192,7 @@ static const struct pv_info xen_info __initconst = {
#ifdef CONFIG_X86_64
.extra_user_64bit_cs = FLAT_USER_CS64,
#endif
-
+ .features = 0,
.name = "Xen",
};
@@ -1229,10 +1229,7 @@ static const struct pv_cpu_ops xen_cpu_ops __initconst = {
.iret = xen_iret,
#ifdef CONFIG_X86_64
- .usergs_sysret32 = xen_sysret32,
.usergs_sysret64 = xen_sysret64,
-#else
- .irq_enable_sysexit = xen_sysexit,
#endif
.load_tr_desc = paravirt_nop,
@@ -1265,12 +1262,6 @@ static const struct pv_cpu_ops xen_cpu_ops __initconst = {
.end_context_switch = xen_end_context_switch,
};
-static const struct pv_apic_ops xen_apic_ops __initconst = {
-#ifdef CONFIG_X86_LOCAL_APIC
- .startup_ipi_hook = paravirt_nop,
-#endif
-};
-
static void xen_reboot(int reason)
{
struct sched_shutdown r = { .reason = reason };
@@ -1374,7 +1365,7 @@ static void __init xen_boot_params_init_edd(void)
info->params.length = sizeof(info->params);
set_xen_guest_handle(op.u.firmware_info.u.disk_info.edd_params,
&info->params);
- ret = HYPERVISOR_dom0_op(&op);
+ ret = HYPERVISOR_platform_op(&op);
if (ret)
break;
@@ -1392,7 +1383,7 @@ static void __init xen_boot_params_init_edd(void)
op.u.firmware_info.type = XEN_FW_DISK_MBR_SIGNATURE;
for (nr = 0; nr < EDD_MBR_SIG_MAX; nr++) {
op.u.firmware_info.index = nr;
- ret = HYPERVISOR_dom0_op(&op);
+ ret = HYPERVISOR_platform_op(&op);
if (ret)
break;
mbr_signature[nr] = op.u.firmware_info.u.disk_mbr_signature.mbr_signature;
@@ -1535,8 +1526,9 @@ asmlinkage __visible void __init xen_start_kernel(void)
/* Install Xen paravirt ops */
pv_info = xen_info;
+ if (xen_initial_domain())
+ pv_info.features |= PV_SUPPORTED_RTC;
pv_init_ops = xen_init_ops;
- pv_apic_ops = xen_apic_ops;
if (!xen_pvh_domain()) {
pv_cpu_ops = xen_cpu_ops;
@@ -1698,7 +1690,7 @@ asmlinkage __visible void __init xen_start_kernel(void)
xen_start_info->console.domU.mfn = 0;
xen_start_info->console.domU.evtchn = 0;
- if (HYPERVISOR_dom0_op(&op) == 0)
+ if (HYPERVISOR_platform_op(&op) == 0)
boot_params.kbd_status = op.u.firmware_info.u.kbd_shift_flags;
/* Make sure ACS will be enabled */
@@ -1886,8 +1878,10 @@ EXPORT_SYMBOL_GPL(xen_hvm_need_lapic);
static void xen_set_cpu_features(struct cpuinfo_x86 *c)
{
- if (xen_pv_domain())
+ if (xen_pv_domain()) {
clear_cpu_bug(c, X86_BUG_SYSRET_SS_ATTRS);
+ set_cpu_cap(c, X86_FEATURE_XENPV);
+ }
}
const struct hypervisor_x86 x86_hyper_xen = {
diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c
index ac161db63388..c913ca4f6958 100644
--- a/arch/x86/xen/mmu.c
+++ b/arch/x86/xen/mmu.c
@@ -2436,7 +2436,6 @@ static const struct pv_mmu_ops xen_mmu_ops __initconst = {
.flush_tlb_others = xen_flush_tlb_others,
.pte_update = paravirt_nop,
- .pte_update_defer = paravirt_nop,
.pgd_alloc = xen_pgd_alloc,
.pgd_free = xen_pgd_free,
@@ -2495,14 +2494,9 @@ void __init xen_init_mmu_ops(void)
{
x86_init.paging.pagetable_init = xen_pagetable_init;
- /* Optimization - we can use the HVM one but it has no idea which
- * VCPUs are descheduled - which means that it will needlessly IPI
- * them. Xen knows so let it do the job.
- */
- if (xen_feature(XENFEAT_auto_translated_physmap)) {
- pv_mmu_ops.flush_tlb_others = xen_flush_tlb_others;
+ if (xen_feature(XENFEAT_auto_translated_physmap))
return;
- }
+
pv_mmu_ops = xen_mmu_ops;
memset(dummy_mapping, 0xff, PAGE_SIZE);
diff --git a/arch/x86/xen/suspend.c b/arch/x86/xen/suspend.c
index feddabdab448..7f664c416faf 100644
--- a/arch/x86/xen/suspend.c
+++ b/arch/x86/xen/suspend.c
@@ -1,6 +1,7 @@
#include <linux/types.h>
#include <linux/tick.h>
+#include <xen/xen.h>
#include <xen/interface/xen.h>
#include <xen/grant_table.h>
#include <xen/events.h>
@@ -33,7 +34,8 @@ static void xen_hvm_post_suspend(int suspend_cancelled)
{
#ifdef CONFIG_XEN_PVHVM
int cpu;
- xen_hvm_init_shared_info();
+ if (!suspend_cancelled)
+ xen_hvm_init_shared_info();
xen_callback_vector();
xen_unplug_emulated_devices();
if (xen_feature(XENFEAT_hvm_safe_pvclock)) {
@@ -68,26 +70,16 @@ static void xen_pv_post_suspend(int suspend_cancelled)
void xen_arch_pre_suspend(void)
{
- int cpu;
-
- for_each_online_cpu(cpu)
- xen_pmu_finish(cpu);
-
if (xen_pv_domain())
xen_pv_pre_suspend();
}
void xen_arch_post_suspend(int cancelled)
{
- int cpu;
-
if (xen_pv_domain())
xen_pv_post_suspend(cancelled);
else
xen_hvm_post_suspend(cancelled);
-
- for_each_online_cpu(cpu)
- xen_pmu_init(cpu);
}
static void xen_vcpu_notify_restore(void *data)
@@ -106,10 +98,20 @@ static void xen_vcpu_notify_suspend(void *data)
void xen_arch_resume(void)
{
+ int cpu;
+
on_each_cpu(xen_vcpu_notify_restore, NULL, 1);
+
+ for_each_online_cpu(cpu)
+ xen_pmu_init(cpu);
}
void xen_arch_suspend(void)
{
+ int cpu;
+
+ for_each_online_cpu(cpu)
+ xen_pmu_finish(cpu);
+
on_each_cpu(xen_vcpu_notify_suspend, NULL, 1);
}
diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c
index f1ba6a092854..a0a4e554c6f1 100644
--- a/arch/x86/xen/time.c
+++ b/arch/x86/xen/time.c
@@ -16,6 +16,7 @@
#include <linux/gfp.h>
#include <linux/slab.h>
#include <linux/pvclock_gtod.h>
+#include <linux/timekeeper_internal.h>
#include <asm/pvclock.h>
#include <asm/xen/hypervisor.h>
@@ -32,86 +33,12 @@
#define TIMER_SLOP 100000
#define NS_PER_TICK (1000000000LL / HZ)
-/* runstate info updated by Xen */
-static DEFINE_PER_CPU(struct vcpu_runstate_info, xen_runstate);
-
/* snapshots of runstate info */
static DEFINE_PER_CPU(struct vcpu_runstate_info, xen_runstate_snapshot);
/* unused ns of stolen time */
static DEFINE_PER_CPU(u64, xen_residual_stolen);
-/* return an consistent snapshot of 64-bit time/counter value */
-static u64 get64(const u64 *p)
-{
- u64 ret;
-
- if (BITS_PER_LONG < 64) {
- u32 *p32 = (u32 *)p;
- u32 h, l;
-
- /*
- * Read high then low, and then make sure high is
- * still the same; this will only loop if low wraps
- * and carries into high.
- * XXX some clean way to make this endian-proof?
- */
- do {
- h = p32[1];
- barrier();
- l = p32[0];
- barrier();
- } while (p32[1] != h);
-
- ret = (((u64)h) << 32) | l;
- } else
- ret = *p;
-
- return ret;
-}
-
-/*
- * Runstate accounting
- */
-static void get_runstate_snapshot(struct vcpu_runstate_info *res)
-{
- u64 state_time;
- struct vcpu_runstate_info *state;
-
- BUG_ON(preemptible());
-
- state = this_cpu_ptr(&xen_runstate);
-
- /*
- * The runstate info is always updated by the hypervisor on
- * the current CPU, so there's no need to use anything
- * stronger than a compiler barrier when fetching it.
- */
- do {
- state_time = get64(&state->state_entry_time);
- barrier();
- *res = *state;
- barrier();
- } while (get64(&state->state_entry_time) != state_time);
-}
-
-/* return true when a vcpu could run but has no real cpu to run on */
-bool xen_vcpu_stolen(int vcpu)
-{
- return per_cpu(xen_runstate, vcpu).state == RUNSTATE_runnable;
-}
-
-void xen_setup_runstate_info(int cpu)
-{
- struct vcpu_register_runstate_memory_area area;
-
- area.addr.v = &per_cpu(xen_runstate, cpu);
-
- if (HYPERVISOR_vcpu_op(VCPUOP_register_runstate_memory_area,
- cpu, &area))
- BUG();
-}
-
static void do_stolen_accounting(void)
{
struct vcpu_runstate_info state;
@@ -119,7 +46,7 @@ static void do_stolen_accounting(void)
s64 runnable, offline, stolen;
cputime_t ticks;
- get_runstate_snapshot(&state);
+ xen_get_runstate_snapshot(&state);
WARN_ON(state.state != RUNSTATE_running);
@@ -194,26 +121,46 @@ static int xen_pvclock_gtod_notify(struct notifier_block *nb,
unsigned long was_set, void *priv)
{
/* Protected by the calling core code serialization */
- static struct timespec next_sync;
+ static struct timespec64 next_sync;
struct xen_platform_op op;
- struct timespec now;
+ struct timespec64 now;
+ struct timekeeper *tk = priv;
+ static bool settime64_supported = true;
+ int ret;
- now = __current_kernel_time();
+ now.tv_sec = tk->xtime_sec;
+ now.tv_nsec = (long)(tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift);
/*
* We only take the expensive HV call when the clock was set
* or when the 11 minutes RTC synchronization time elapsed.
*/
- if (!was_set && timespec_compare(&now, &next_sync) < 0)
+ if (!was_set && timespec64_compare(&now, &next_sync) < 0)
return NOTIFY_OK;
- op.cmd = XENPF_settime;
- op.u.settime.secs = now.tv_sec;
- op.u.settime.nsecs = now.tv_nsec;
- op.u.settime.system_time = xen_clocksource_read();
+again:
+ if (settime64_supported) {
+ op.cmd = XENPF_settime64;
+ op.u.settime64.mbz = 0;
+ op.u.settime64.secs = now.tv_sec;
+ op.u.settime64.nsecs = now.tv_nsec;
+ op.u.settime64.system_time = xen_clocksource_read();
+ } else {
+ op.cmd = XENPF_settime32;
+ op.u.settime32.secs = now.tv_sec;
+ op.u.settime32.nsecs = now.tv_nsec;
+ op.u.settime32.system_time = xen_clocksource_read();
+ }
+
+ ret = HYPERVISOR_platform_op(&op);
- (void)HYPERVISOR_dom0_op(&op);
+ if (ret == -ENOSYS && settime64_supported) {
+ settime64_supported = false;
+ goto again;
+ }
+ if (ret < 0)
+ return NOTIFY_BAD;
/*
* Move the next drift compensation time 11 minutes
diff --git a/arch/x86/xen/xen-asm_32.S b/arch/x86/xen/xen-asm_32.S
index fd92a64d748e..feb6d40a0860 100644
--- a/arch/x86/xen/xen-asm_32.S
+++ b/arch/x86/xen/xen-asm_32.S
@@ -35,20 +35,6 @@ check_events:
ret
/*
- * We can't use sysexit directly, because we're not running in ring0.
- * But we can easily fake it up using iret. Assuming xen_sysexit is
- * jumped to with a standard stack frame, we can just strip it back to
- * a standard iret frame and use iret.
- */
-ENTRY(xen_sysexit)
- movl PT_EAX(%esp), %eax /* Shouldn't be necessary? */
- orl $X86_EFLAGS_IF, PT_EFLAGS(%esp)
- lea PT_EIP(%esp), %esp
-
- jmp xen_iret
-ENDPROC(xen_sysexit)
-
-/*
* This is run where a normal iret would be run, with the same stack setup:
* 8: eflags
* 4: cs
diff --git a/arch/x86/xen/xen-asm_64.S b/arch/x86/xen/xen-asm_64.S
index f22667abf7b9..cc8acc410ddb 100644
--- a/arch/x86/xen/xen-asm_64.S
+++ b/arch/x86/xen/xen-asm_64.S
@@ -68,25 +68,6 @@ ENTRY(xen_sysret64)
ENDPATCH(xen_sysret64)
RELOC(xen_sysret64, 1b+1)
-ENTRY(xen_sysret32)
- /*
- * 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(cpu_current_top_of_stack), %rsp
-
- pushq $__USER32_DS
- pushq PER_CPU_VAR(rsp_scratch)
- pushq %r11
- pushq $__USER32_CS
- pushq %rcx
-
- pushq $0
-1: jmp hypercall_iret
-ENDPATCH(xen_sysret32)
-RELOC(xen_sysret32, 1b+1)
-
/*
* Xen handles syscall callbacks much like ordinary exceptions, which
* means we have:
diff --git a/arch/x86/xen/xen-ops.h b/arch/x86/xen/xen-ops.h
index 1399423f3418..4140b070f2e9 100644
--- a/arch/x86/xen/xen-ops.h
+++ b/arch/x86/xen/xen-ops.h
@@ -139,9 +139,6 @@ 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);
diff --git a/arch/xtensa/include/uapi/asm/socket.h b/arch/xtensa/include/uapi/asm/socket.h
index 4120af086160..fd3b96d1153f 100644
--- a/arch/xtensa/include/uapi/asm/socket.h
+++ b/arch/xtensa/include/uapi/asm/socket.h
@@ -96,4 +96,7 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* _XTENSA_SOCKET_H */
diff --git a/arch/xtensa/platforms/iss/simdisk.c b/arch/xtensa/platforms/iss/simdisk.c
index 3c3ace2c46b6..f58a4e6472cb 100644
--- a/arch/xtensa/platforms/iss/simdisk.c
+++ b/arch/xtensa/platforms/iss/simdisk.c
@@ -227,16 +227,12 @@ static ssize_t proc_read_simdisk(struct file *file, char __user *buf,
static ssize_t proc_write_simdisk(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- char *tmp = kmalloc(count + 1, GFP_KERNEL);
+ char *tmp = memdup_user_nul(buf, count);
struct simdisk *dev = PDE_DATA(file_inode(file));
int err;
- if (tmp == NULL)
- return -ENOMEM;
- if (copy_from_user(tmp, buf, count)) {
- err = -EFAULT;
- goto out_free;
- }
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
err = simdisk_detach(dev);
if (err != 0)
@@ -244,8 +240,6 @@ static ssize_t proc_write_simdisk(struct file *file, const char __user *buf,
if (count > 0 && tmp[count - 1] == '\n')
tmp[count - 1] = 0;
- else
- tmp[count] = 0;
if (tmp[0])
err = simdisk_attach(dev, tmp);
diff --git a/block/Makefile b/block/Makefile
index 00ecc97629db..db5f622c9d67 100644
--- a/block/Makefile
+++ b/block/Makefile
@@ -8,7 +8,7 @@ obj-$(CONFIG_BLOCK) := bio.o elevator.o blk-core.o blk-tag.o blk-sysfs.o \
blk-iopoll.o blk-lib.o blk-mq.o blk-mq-tag.o \
blk-mq-sysfs.o blk-mq-cpu.o blk-mq-cpumap.o ioctl.o \
genhd.o scsi_ioctl.o partition-generic.o ioprio.o \
- partitions/
+ badblocks.o partitions/
obj-$(CONFIG_BOUNCE) += bounce.o
obj-$(CONFIG_BLK_DEV_BSG) += bsg.o
diff --git a/block/badblocks.c b/block/badblocks.c
new file mode 100644
index 000000000000..7be53cb1cc3c
--- /dev/null
+++ b/block/badblocks.c
@@ -0,0 +1,585 @@
+/*
+ * Bad block management
+ *
+ * - Heavily based on MD badblocks code from Neil Brown
+ *
+ * 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/badblocks.h>
+#include <linux/seqlock.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+
+/**
+ * badblocks_check() - check a given range for bad sectors
+ * @bb: the badblocks structure that holds all badblock information
+ * @s: sector (start) at which to check for badblocks
+ * @sectors: number of sectors to check for badblocks
+ * @first_bad: pointer to store location of the first badblock
+ * @bad_sectors: pointer to store number of badblocks after @first_bad
+ *
+ * We can record which blocks on each device are 'bad' and so just
+ * fail those blocks, or that stripe, rather than the whole device.
+ * Entries in the bad-block table are 64bits wide. This comprises:
+ * Length of bad-range, in sectors: 0-511 for lengths 1-512
+ * Start of bad-range, sector offset, 54 bits (allows 8 exbibytes)
+ * A 'shift' can be set so that larger blocks are tracked and
+ * consequently larger devices can be covered.
+ * 'Acknowledged' flag - 1 bit. - the most significant bit.
+ *
+ * Locking of the bad-block table uses a seqlock so badblocks_check
+ * might need to retry if it is very unlucky.
+ * We will sometimes want to check for bad blocks in a bi_end_io function,
+ * so we use the write_seqlock_irq variant.
+ *
+ * When looking for a bad block we specify a range and want to
+ * know if any block in the range is bad. So we binary-search
+ * to the last range that starts at-or-before the given endpoint,
+ * (or "before the sector after the target range")
+ * then see if it ends after the given start.
+ *
+ * Return:
+ * 0: there are no known bad blocks in the range
+ * 1: there are known bad block which are all acknowledged
+ * -1: there are bad blocks which have not yet been acknowledged in metadata.
+ * plus the start/length of the first bad section we overlap.
+ */
+int badblocks_check(struct badblocks *bb, sector_t s, int sectors,
+ sector_t *first_bad, int *bad_sectors)
+{
+ int hi;
+ int lo;
+ u64 *p = bb->page;
+ int rv;
+ sector_t target = s + sectors;
+ unsigned seq;
+
+ if (bb->shift > 0) {
+ /* round the start down, and the end up */
+ s >>= bb->shift;
+ target += (1<<bb->shift) - 1;
+ target >>= bb->shift;
+ sectors = target - s;
+ }
+ /* 'target' is now the first block after the bad range */
+
+retry:
+ seq = read_seqbegin(&bb->lock);
+ lo = 0;
+ rv = 0;
+ hi = bb->count;
+
+ /* Binary search between lo and hi for 'target'
+ * i.e. for the last range that starts before 'target'
+ */
+ /* INVARIANT: ranges before 'lo' and at-or-after 'hi'
+ * are known not to be the last range before target.
+ * VARIANT: hi-lo is the number of possible
+ * ranges, and decreases until it reaches 1
+ */
+ while (hi - lo > 1) {
+ int mid = (lo + hi) / 2;
+ sector_t a = BB_OFFSET(p[mid]);
+
+ if (a < target)
+ /* This could still be the one, earlier ranges
+ * could not.
+ */
+ lo = mid;
+ else
+ /* This and later ranges are definitely out. */
+ hi = mid;
+ }
+ /* 'lo' might be the last that started before target, but 'hi' isn't */
+ if (hi > lo) {
+ /* need to check all range that end after 's' to see if
+ * any are unacknowledged.
+ */
+ while (lo >= 0 &&
+ BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > s) {
+ if (BB_OFFSET(p[lo]) < target) {
+ /* starts before the end, and finishes after
+ * the start, so they must overlap
+ */
+ if (rv != -1 && BB_ACK(p[lo]))
+ rv = 1;
+ else
+ rv = -1;
+ *first_bad = BB_OFFSET(p[lo]);
+ *bad_sectors = BB_LEN(p[lo]);
+ }
+ lo--;
+ }
+ }
+
+ if (read_seqretry(&bb->lock, seq))
+ goto retry;
+
+ return rv;
+}
+EXPORT_SYMBOL_GPL(badblocks_check);
+
+/**
+ * badblocks_set() - Add a range of bad blocks to the table.
+ * @bb: the badblocks structure that holds all badblock information
+ * @s: first sector to mark as bad
+ * @sectors: number of sectors to mark as bad
+ * @acknowledged: weather to mark the bad sectors as acknowledged
+ *
+ * This might extend the table, or might contract it if two adjacent ranges
+ * can be merged. We binary-search to find the 'insertion' point, then
+ * decide how best to handle it.
+ *
+ * Return:
+ * 0: success
+ * 1: failed to set badblocks (out of space)
+ */
+int badblocks_set(struct badblocks *bb, sector_t s, int sectors,
+ int acknowledged)
+{
+ u64 *p;
+ int lo, hi;
+ int rv = 0;
+ unsigned long flags;
+
+ if (bb->shift < 0)
+ /* badblocks are disabled */
+ return 0;
+
+ if (bb->shift) {
+ /* round the start down, and the end up */
+ sector_t next = s + sectors;
+
+ s >>= bb->shift;
+ next += (1<<bb->shift) - 1;
+ next >>= bb->shift;
+ sectors = next - s;
+ }
+
+ write_seqlock_irqsave(&bb->lock, flags);
+
+ p = bb->page;
+ lo = 0;
+ hi = bb->count;
+ /* Find the last range that starts at-or-before 's' */
+ while (hi - lo > 1) {
+ int mid = (lo + hi) / 2;
+ sector_t a = BB_OFFSET(p[mid]);
+
+ if (a <= s)
+ lo = mid;
+ else
+ hi = mid;
+ }
+ if (hi > lo && BB_OFFSET(p[lo]) > s)
+ hi = lo;
+
+ if (hi > lo) {
+ /* we found a range that might merge with the start
+ * of our new range
+ */
+ sector_t a = BB_OFFSET(p[lo]);
+ sector_t e = a + BB_LEN(p[lo]);
+ int ack = BB_ACK(p[lo]);
+
+ if (e >= s) {
+ /* Yes, we can merge with a previous range */
+ if (s == a && s + sectors >= e)
+ /* new range covers old */
+ ack = acknowledged;
+ else
+ ack = ack && acknowledged;
+
+ if (e < s + sectors)
+ e = s + sectors;
+ if (e - a <= BB_MAX_LEN) {
+ p[lo] = BB_MAKE(a, e-a, ack);
+ s = e;
+ } else {
+ /* does not all fit in one range,
+ * make p[lo] maximal
+ */
+ if (BB_LEN(p[lo]) != BB_MAX_LEN)
+ p[lo] = BB_MAKE(a, BB_MAX_LEN, ack);
+ s = a + BB_MAX_LEN;
+ }
+ sectors = e - s;
+ }
+ }
+ if (sectors && hi < bb->count) {
+ /* 'hi' points to the first range that starts after 's'.
+ * Maybe we can merge with the start of that range
+ */
+ sector_t a = BB_OFFSET(p[hi]);
+ sector_t e = a + BB_LEN(p[hi]);
+ int ack = BB_ACK(p[hi]);
+
+ if (a <= s + sectors) {
+ /* merging is possible */
+ if (e <= s + sectors) {
+ /* full overlap */
+ e = s + sectors;
+ ack = acknowledged;
+ } else
+ ack = ack && acknowledged;
+
+ a = s;
+ if (e - a <= BB_MAX_LEN) {
+ p[hi] = BB_MAKE(a, e-a, ack);
+ s = e;
+ } else {
+ p[hi] = BB_MAKE(a, BB_MAX_LEN, ack);
+ s = a + BB_MAX_LEN;
+ }
+ sectors = e - s;
+ lo = hi;
+ hi++;
+ }
+ }
+ if (sectors == 0 && hi < bb->count) {
+ /* we might be able to combine lo and hi */
+ /* Note: 's' is at the end of 'lo' */
+ sector_t a = BB_OFFSET(p[hi]);
+ int lolen = BB_LEN(p[lo]);
+ int hilen = BB_LEN(p[hi]);
+ int newlen = lolen + hilen - (s - a);
+
+ if (s >= a && newlen < BB_MAX_LEN) {
+ /* yes, we can combine them */
+ int ack = BB_ACK(p[lo]) && BB_ACK(p[hi]);
+
+ p[lo] = BB_MAKE(BB_OFFSET(p[lo]), newlen, ack);
+ memmove(p + hi, p + hi + 1,
+ (bb->count - hi - 1) * 8);
+ bb->count--;
+ }
+ }
+ while (sectors) {
+ /* didn't merge (it all).
+ * Need to add a range just before 'hi'
+ */
+ if (bb->count >= MAX_BADBLOCKS) {
+ /* No room for more */
+ rv = 1;
+ break;
+ } else {
+ int this_sectors = sectors;
+
+ memmove(p + hi + 1, p + hi,
+ (bb->count - hi) * 8);
+ bb->count++;
+
+ if (this_sectors > BB_MAX_LEN)
+ this_sectors = BB_MAX_LEN;
+ p[hi] = BB_MAKE(s, this_sectors, acknowledged);
+ sectors -= this_sectors;
+ s += this_sectors;
+ }
+ }
+
+ bb->changed = 1;
+ if (!acknowledged)
+ bb->unacked_exist = 1;
+ write_sequnlock_irqrestore(&bb->lock, flags);
+
+ return rv;
+}
+EXPORT_SYMBOL_GPL(badblocks_set);
+
+/**
+ * badblocks_clear() - Remove a range of bad blocks to the table.
+ * @bb: the badblocks structure that holds all badblock information
+ * @s: first sector to mark as bad
+ * @sectors: number of sectors to mark as bad
+ *
+ * This may involve extending the table if we spilt a region,
+ * but it must not fail. So if the table becomes full, we just
+ * drop the remove request.
+ *
+ * Return:
+ * 0: success
+ * 1: failed to clear badblocks
+ */
+int badblocks_clear(struct badblocks *bb, sector_t s, int sectors)
+{
+ u64 *p;
+ int lo, hi;
+ sector_t target = s + sectors;
+ int rv = 0;
+
+ if (bb->shift > 0) {
+ /* When clearing we round the start up and the end down.
+ * This should not matter as the shift should align with
+ * the block size and no rounding should ever be needed.
+ * However it is better the think a block is bad when it
+ * isn't than to think a block is not bad when it is.
+ */
+ s += (1<<bb->shift) - 1;
+ s >>= bb->shift;
+ target >>= bb->shift;
+ sectors = target - s;
+ }
+
+ write_seqlock_irq(&bb->lock);
+
+ p = bb->page;
+ lo = 0;
+ hi = bb->count;
+ /* Find the last range that starts before 'target' */
+ while (hi - lo > 1) {
+ int mid = (lo + hi) / 2;
+ sector_t a = BB_OFFSET(p[mid]);
+
+ if (a < target)
+ lo = mid;
+ else
+ hi = mid;
+ }
+ if (hi > lo) {
+ /* p[lo] is the last range that could overlap the
+ * current range. Earlier ranges could also overlap,
+ * but only this one can overlap the end of the range.
+ */
+ if (BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > target) {
+ /* Partial overlap, leave the tail of this range */
+ int ack = BB_ACK(p[lo]);
+ sector_t a = BB_OFFSET(p[lo]);
+ sector_t end = a + BB_LEN(p[lo]);
+
+ if (a < s) {
+ /* we need to split this range */
+ if (bb->count >= MAX_BADBLOCKS) {
+ rv = -ENOSPC;
+ goto out;
+ }
+ memmove(p+lo+1, p+lo, (bb->count - lo) * 8);
+ bb->count++;
+ p[lo] = BB_MAKE(a, s-a, ack);
+ lo++;
+ }
+ p[lo] = BB_MAKE(target, end - target, ack);
+ /* there is no longer an overlap */
+ hi = lo;
+ lo--;
+ }
+ while (lo >= 0 &&
+ BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > s) {
+ /* This range does overlap */
+ if (BB_OFFSET(p[lo]) < s) {
+ /* Keep the early parts of this range. */
+ int ack = BB_ACK(p[lo]);
+ sector_t start = BB_OFFSET(p[lo]);
+
+ p[lo] = BB_MAKE(start, s - start, ack);
+ /* now low doesn't overlap, so.. */
+ break;
+ }
+ lo--;
+ }
+ /* 'lo' is strictly before, 'hi' is strictly after,
+ * anything between needs to be discarded
+ */
+ if (hi - lo > 1) {
+ memmove(p+lo+1, p+hi, (bb->count - hi) * 8);
+ bb->count -= (hi - lo - 1);
+ }
+ }
+
+ bb->changed = 1;
+out:
+ write_sequnlock_irq(&bb->lock);
+ return rv;
+}
+EXPORT_SYMBOL_GPL(badblocks_clear);
+
+/**
+ * ack_all_badblocks() - Acknowledge all bad blocks in a list.
+ * @bb: the badblocks structure that holds all badblock information
+ *
+ * This only succeeds if ->changed is clear. It is used by
+ * in-kernel metadata updates
+ */
+void ack_all_badblocks(struct badblocks *bb)
+{
+ if (bb->page == NULL || bb->changed)
+ /* no point even trying */
+ return;
+ write_seqlock_irq(&bb->lock);
+
+ if (bb->changed == 0 && bb->unacked_exist) {
+ u64 *p = bb->page;
+ int i;
+
+ for (i = 0; i < bb->count ; i++) {
+ if (!BB_ACK(p[i])) {
+ sector_t start = BB_OFFSET(p[i]);
+ int len = BB_LEN(p[i]);
+
+ p[i] = BB_MAKE(start, len, 1);
+ }
+ }
+ bb->unacked_exist = 0;
+ }
+ write_sequnlock_irq(&bb->lock);
+}
+EXPORT_SYMBOL_GPL(ack_all_badblocks);
+
+/**
+ * badblocks_show() - sysfs access to bad-blocks list
+ * @bb: the badblocks structure that holds all badblock information
+ * @page: buffer received from sysfs
+ * @unack: weather to show unacknowledged badblocks
+ *
+ * Return:
+ * Length of returned data
+ */
+ssize_t badblocks_show(struct badblocks *bb, char *page, int unack)
+{
+ size_t len;
+ int i;
+ u64 *p = bb->page;
+ unsigned seq;
+
+ if (bb->shift < 0)
+ return 0;
+
+retry:
+ seq = read_seqbegin(&bb->lock);
+
+ len = 0;
+ i = 0;
+
+ while (len < PAGE_SIZE && i < bb->count) {
+ sector_t s = BB_OFFSET(p[i]);
+ unsigned int length = BB_LEN(p[i]);
+ int ack = BB_ACK(p[i]);
+
+ i++;
+
+ if (unack && ack)
+ continue;
+
+ len += snprintf(page+len, PAGE_SIZE-len, "%llu %u\n",
+ (unsigned long long)s << bb->shift,
+ length << bb->shift);
+ }
+ if (unack && len == 0)
+ bb->unacked_exist = 0;
+
+ if (read_seqretry(&bb->lock, seq))
+ goto retry;
+
+ return len;
+}
+EXPORT_SYMBOL_GPL(badblocks_show);
+
+/**
+ * badblocks_store() - sysfs access to bad-blocks list
+ * @bb: the badblocks structure that holds all badblock information
+ * @page: buffer received from sysfs
+ * @len: length of data received from sysfs
+ * @unack: weather to show unacknowledged badblocks
+ *
+ * Return:
+ * Length of the buffer processed or -ve error.
+ */
+ssize_t badblocks_store(struct badblocks *bb, const char *page, size_t len,
+ int unack)
+{
+ unsigned long long sector;
+ int length;
+ char newline;
+
+ switch (sscanf(page, "%llu %d%c", &sector, &length, &newline)) {
+ case 3:
+ if (newline != '\n')
+ return -EINVAL;
+ case 2:
+ if (length <= 0)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (badblocks_set(bb, sector, length, !unack))
+ return -ENOSPC;
+ else
+ return len;
+}
+EXPORT_SYMBOL_GPL(badblocks_store);
+
+static int __badblocks_init(struct device *dev, struct badblocks *bb,
+ int enable)
+{
+ bb->dev = dev;
+ bb->count = 0;
+ if (enable)
+ bb->shift = 0;
+ else
+ bb->shift = -1;
+ if (dev)
+ bb->page = devm_kzalloc(dev, PAGE_SIZE, GFP_KERNEL);
+ else
+ bb->page = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!bb->page) {
+ bb->shift = -1;
+ return -ENOMEM;
+ }
+ seqlock_init(&bb->lock);
+
+ return 0;
+}
+
+/**
+ * badblocks_init() - initialize the badblocks structure
+ * @bb: the badblocks structure that holds all badblock information
+ * @enable: weather to enable badblocks accounting
+ *
+ * Return:
+ * 0: success
+ * -ve errno: on error
+ */
+int badblocks_init(struct badblocks *bb, int enable)
+{
+ return __badblocks_init(NULL, bb, enable);
+}
+EXPORT_SYMBOL_GPL(badblocks_init);
+
+int devm_init_badblocks(struct device *dev, struct badblocks *bb)
+{
+ if (!bb)
+ return -EINVAL;
+ return __badblocks_init(dev, bb, 1);
+}
+EXPORT_SYMBOL_GPL(devm_init_badblocks);
+
+/**
+ * badblocks_exit() - free the badblocks structure
+ * @bb: the badblocks structure that holds all badblock information
+ */
+void badblocks_exit(struct badblocks *bb)
+{
+ if (!bb)
+ return;
+ if (bb->dev)
+ devm_kfree(bb->dev, bb->page);
+ else
+ kfree(bb->page);
+ bb->page = NULL;
+}
+EXPORT_SYMBOL_GPL(badblocks_exit);
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index 5bcdfc10c23a..5a37188b559f 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -1127,15 +1127,15 @@ void blkcg_exit_queue(struct request_queue *q)
* of the main cic data structures. For now we allow a task to change
* its cgroup only if it's the only owner of its ioc.
*/
-static int blkcg_can_attach(struct cgroup_subsys_state *css,
- struct cgroup_taskset *tset)
+static int blkcg_can_attach(struct cgroup_taskset *tset)
{
struct task_struct *task;
+ struct cgroup_subsys_state *dst_css;
struct io_context *ioc;
int ret = 0;
/* task_lock() is needed to avoid races with exit_io_context() */
- cgroup_taskset_for_each(task, tset) {
+ cgroup_taskset_for_each(task, dst_css, tset) {
task_lock(task);
ioc = task->io_context;
if (ioc && atomic_read(&ioc->nr_tasks) > 1)
diff --git a/block/blk-core.c b/block/blk-core.c
index 5131993b23a1..33e2f62d5062 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -207,6 +207,22 @@ void blk_delay_queue(struct request_queue *q, unsigned long msecs)
EXPORT_SYMBOL(blk_delay_queue);
/**
+ * blk_start_queue_async - asynchronously restart a previously stopped queue
+ * @q: The &struct request_queue in question
+ *
+ * Description:
+ * blk_start_queue_async() will clear the stop flag on the queue, and
+ * ensure that the request_fn for the queue is run from an async
+ * context.
+ **/
+void blk_start_queue_async(struct request_queue *q)
+{
+ queue_flag_clear(QUEUE_FLAG_STOPPED, q);
+ blk_run_queue_async(q);
+}
+EXPORT_SYMBOL(blk_start_queue_async);
+
+/**
* blk_start_queue - restart a previously stopped queue
* @q: The &struct request_queue in question
*
@@ -1689,8 +1705,6 @@ static blk_qc_t blk_queue_bio(struct request_queue *q, struct bio *bio)
struct request *req;
unsigned int request_count = 0;
- blk_queue_split(q, &bio, q->bio_split);
-
/*
* low level driver can indicate that it wants pages above a
* certain limit bounced to low memory (ie for highmem, or even
@@ -1698,6 +1712,8 @@ static blk_qc_t blk_queue_bio(struct request_queue *q, struct bio *bio)
*/
blk_queue_bounce(q, &bio);
+ blk_queue_split(q, &bio, q->bio_split);
+
if (bio_integrity_enabled(bio) && bio_integrity_prep(bio)) {
bio->bi_error = -EIO;
bio_endio(bio);
@@ -2114,7 +2130,8 @@ blk_qc_t submit_bio(int rw, struct bio *bio)
EXPORT_SYMBOL(submit_bio);
/**
- * blk_rq_check_limits - Helper function to check a request for the queue limit
+ * blk_cloned_rq_check_limits - Helper function to check a cloned request
+ * for new the queue limits
* @q: the queue
* @rq: the request being checked
*
@@ -2125,20 +2142,13 @@ EXPORT_SYMBOL(submit_bio);
* after it is inserted to @q, it should be checked against @q before
* the insertion using this generic function.
*
- * This function should also be useful for request stacking drivers
- * in some cases below, so export this function.
* Request stacking drivers like request-based dm may change the queue
- * limits while requests are in the queue (e.g. dm's table swapping).
- * Such request stacking drivers should check those requests against
- * the new queue limits again when they dispatch those requests,
- * although such checkings are also done against the old queue limits
- * when submitting requests.
+ * limits when retrying requests on other queues. Those requests need
+ * to be checked against the new queue limits again during dispatch.
*/
-int blk_rq_check_limits(struct request_queue *q, struct request *rq)
+static int blk_cloned_rq_check_limits(struct request_queue *q,
+ struct request *rq)
{
- if (!rq_mergeable(rq))
- return 0;
-
if (blk_rq_sectors(rq) > blk_queue_get_max_sectors(q, rq->cmd_flags)) {
printk(KERN_ERR "%s: over max size limit.\n", __func__);
return -EIO;
@@ -2158,7 +2168,6 @@ int blk_rq_check_limits(struct request_queue *q, struct request *rq)
return 0;
}
-EXPORT_SYMBOL_GPL(blk_rq_check_limits);
/**
* blk_insert_cloned_request - Helper for stacking drivers to submit a request
@@ -2170,7 +2179,7 @@ int blk_insert_cloned_request(struct request_queue *q, struct request *rq)
unsigned long flags;
int where = ELEVATOR_INSERT_BACK;
- if (blk_rq_check_limits(q, rq))
+ if (blk_cloned_rq_check_limits(q, rq))
return -EIO;
if (rq->rq_disk &&
@@ -3412,6 +3421,9 @@ int blk_pre_runtime_suspend(struct request_queue *q)
{
int ret = 0;
+ if (!q->dev)
+ return ret;
+
spin_lock_irq(q->queue_lock);
if (q->nr_pending) {
ret = -EBUSY;
@@ -3439,6 +3451,9 @@ EXPORT_SYMBOL(blk_pre_runtime_suspend);
*/
void blk_post_runtime_suspend(struct request_queue *q, int err)
{
+ if (!q->dev)
+ return;
+
spin_lock_irq(q->queue_lock);
if (!err) {
q->rpm_status = RPM_SUSPENDED;
@@ -3463,6 +3478,9 @@ EXPORT_SYMBOL(blk_post_runtime_suspend);
*/
void blk_pre_runtime_resume(struct request_queue *q)
{
+ if (!q->dev)
+ return;
+
spin_lock_irq(q->queue_lock);
q->rpm_status = RPM_RESUMING;
spin_unlock_irq(q->queue_lock);
@@ -3485,6 +3503,9 @@ EXPORT_SYMBOL(blk_pre_runtime_resume);
*/
void blk_post_runtime_resume(struct request_queue *q, int err)
{
+ if (!q->dev)
+ return;
+
spin_lock_irq(q->queue_lock);
if (!err) {
q->rpm_status = RPM_ACTIVE;
diff --git a/block/blk-merge.c b/block/blk-merge.c
index de5716d8e525..e01405a3e8b3 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -76,6 +76,9 @@ static struct bio *blk_bio_segment_split(struct request_queue *q,
struct bio_vec bv, bvprv, *bvprvp = NULL;
struct bvec_iter iter;
unsigned seg_size = 0, nsegs = 0, sectors = 0;
+ unsigned front_seg_size = bio->bi_seg_front_size;
+ bool do_split = true;
+ struct bio *new = NULL;
bio_for_each_segment(bv, bio, iter) {
if (sectors + (bv.bv_len >> 9) > queue_max_sectors(q))
@@ -98,8 +101,11 @@ static struct bio *blk_bio_segment_split(struct request_queue *q,
seg_size += bv.bv_len;
bvprv = bv;
- bvprvp = &bv;
+ bvprvp = &bvprv;
sectors += bv.bv_len >> 9;
+
+ if (nsegs == 1 && seg_size > front_seg_size)
+ front_seg_size = seg_size;
continue;
}
new_segment:
@@ -108,16 +114,29 @@ new_segment:
nsegs++;
bvprv = bv;
- bvprvp = &bv;
+ bvprvp = &bvprv;
seg_size = bv.bv_len;
sectors += bv.bv_len >> 9;
+
+ if (nsegs == 1 && seg_size > front_seg_size)
+ front_seg_size = seg_size;
}
- *segs = nsegs;
- return NULL;
+ do_split = false;
split:
*segs = nsegs;
- return bio_split(bio, sectors, GFP_NOIO, bs);
+
+ if (do_split) {
+ new = bio_split(bio, sectors, GFP_NOIO, bs);
+ if (new)
+ bio = new;
+ }
+
+ bio->bi_seg_front_size = front_seg_size;
+ if (seg_size > bio->bi_seg_back_size)
+ bio->bi_seg_back_size = seg_size;
+
+ return do_split ? new : NULL;
}
void blk_queue_split(struct request_queue *q, struct bio **bio,
@@ -412,6 +431,12 @@ int blk_rq_map_sg(struct request_queue *q, struct request *rq,
if (sg)
sg_mark_end(sg);
+ /*
+ * Something must have been wrong if the figured number of
+ * segment is bigger than number of req's physical segments
+ */
+ WARN_ON(nsegs > rq->nr_phys_segments);
+
return nsegs;
}
EXPORT_SYMBOL(blk_rq_map_sg);
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 3ae09de62f19..6d6f8feb48c0 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -1291,15 +1291,16 @@ static blk_qc_t blk_mq_make_request(struct request_queue *q, struct bio *bio)
blk_mq_bio_to_request(rq, bio);
/*
- * we do limited pluging. If bio can be merged, do merge.
+ * We do limited pluging. If the bio can be merged, do that.
* Otherwise the existing request in the plug list will be
* issued. So the plug list will have one request at most
*/
if (plug) {
/*
* The plug list might get flushed before this. If that
- * happens, same_queue_rq is invalid and plug list is empty
- **/
+ * happens, same_queue_rq is invalid and plug list is
+ * empty
+ */
if (same_queue_rq && !list_empty(&plug->mq_list)) {
old_rq = same_queue_rq;
list_del_init(&old_rq->queuelist);
@@ -1380,12 +1381,15 @@ static blk_qc_t blk_sq_make_request(struct request_queue *q, struct bio *bio)
blk_mq_bio_to_request(rq, bio);
if (!request_count)
trace_block_plug(q);
- else if (request_count >= BLK_MAX_REQUEST_COUNT) {
+
+ blk_mq_put_ctx(data.ctx);
+
+ if (request_count >= BLK_MAX_REQUEST_COUNT) {
blk_flush_plug_list(plug, false);
trace_block_plug(q);
}
+
list_add_tail(&rq->queuelist, &plug->mq_list);
- blk_mq_put_ctx(data.ctx);
return cookie;
}
diff --git a/block/blk-settings.c b/block/blk-settings.c
index 7d8f129a1516..dd4973583978 100644
--- a/block/blk-settings.c
+++ b/block/blk-settings.c
@@ -91,7 +91,8 @@ void blk_set_default_limits(struct queue_limits *lim)
lim->seg_boundary_mask = BLK_SEG_BOUNDARY_MASK;
lim->virt_boundary_mask = 0;
lim->max_segment_size = BLK_MAX_SEGMENT_SIZE;
- lim->max_sectors = lim->max_hw_sectors = BLK_SAFE_MAX_SECTORS;
+ lim->max_sectors = lim->max_dev_sectors = lim->max_hw_sectors =
+ BLK_SAFE_MAX_SECTORS;
lim->chunk_sectors = 0;
lim->max_write_same_sectors = 0;
lim->max_discard_sectors = 0;
@@ -127,6 +128,7 @@ void blk_set_stacking_limits(struct queue_limits *lim)
lim->max_hw_sectors = UINT_MAX;
lim->max_segment_size = UINT_MAX;
lim->max_sectors = UINT_MAX;
+ lim->max_dev_sectors = UINT_MAX;
lim->max_write_same_sectors = UINT_MAX;
}
EXPORT_SYMBOL(blk_set_stacking_limits);
@@ -214,8 +216,8 @@ void blk_queue_bounce_limit(struct request_queue *q, u64 max_addr)
EXPORT_SYMBOL(blk_queue_bounce_limit);
/**
- * blk_limits_max_hw_sectors - set hard and soft limit of max sectors for request
- * @limits: the queue limits
+ * blk_queue_max_hw_sectors - set max sectors for a request for this queue
+ * @q: the request queue for the device
* @max_hw_sectors: max hardware sectors in the usual 512b unit
*
* Description:
@@ -224,13 +226,19 @@ EXPORT_SYMBOL(blk_queue_bounce_limit);
* the device driver based upon the capabilities of the I/O
* controller.
*
+ * max_dev_sectors is a hard limit imposed by the storage device for
+ * READ/WRITE requests. It is set by the disk driver.
+ *
* max_sectors is a soft limit imposed by the block layer for
* filesystem type requests. This value can be overridden on a
* per-device basis in /sys/block/<device>/queue/max_sectors_kb.
* The soft limit can not exceed max_hw_sectors.
**/
-void blk_limits_max_hw_sectors(struct queue_limits *limits, unsigned int max_hw_sectors)
+void blk_queue_max_hw_sectors(struct request_queue *q, unsigned int max_hw_sectors)
{
+ struct queue_limits *limits = &q->limits;
+ unsigned int max_sectors;
+
if ((max_hw_sectors << 9) < PAGE_CACHE_SIZE) {
max_hw_sectors = 1 << (PAGE_CACHE_SHIFT - 9);
printk(KERN_INFO "%s: set to minimum %d\n",
@@ -238,22 +246,9 @@ void blk_limits_max_hw_sectors(struct queue_limits *limits, unsigned int max_hw_
}
limits->max_hw_sectors = max_hw_sectors;
- limits->max_sectors = min_t(unsigned int, max_hw_sectors,
- BLK_DEF_MAX_SECTORS);
-}
-EXPORT_SYMBOL(blk_limits_max_hw_sectors);
-
-/**
- * blk_queue_max_hw_sectors - set max sectors for a request for this queue
- * @q: the request queue for the device
- * @max_hw_sectors: max hardware sectors in the usual 512b unit
- *
- * Description:
- * See description for blk_limits_max_hw_sectors().
- **/
-void blk_queue_max_hw_sectors(struct request_queue *q, unsigned int max_hw_sectors)
-{
- blk_limits_max_hw_sectors(&q->limits, max_hw_sectors);
+ max_sectors = min_not_zero(max_hw_sectors, limits->max_dev_sectors);
+ max_sectors = min_t(unsigned int, max_sectors, BLK_DEF_MAX_SECTORS);
+ limits->max_sectors = max_sectors;
}
EXPORT_SYMBOL(blk_queue_max_hw_sectors);
@@ -527,6 +522,7 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
t->max_sectors = min_not_zero(t->max_sectors, b->max_sectors);
t->max_hw_sectors = min_not_zero(t->max_hw_sectors, b->max_hw_sectors);
+ t->max_dev_sectors = min_not_zero(t->max_dev_sectors, b->max_dev_sectors);
t->max_write_same_sectors = min(t->max_write_same_sectors,
b->max_write_same_sectors);
t->bounce_pfn = min_not_zero(t->bounce_pfn, b->bounce_pfn);
diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c
index 565b8dac5782..e140cc487ce1 100644
--- a/block/blk-sysfs.c
+++ b/block/blk-sysfs.c
@@ -205,6 +205,9 @@ queue_max_sectors_store(struct request_queue *q, const char *page, size_t count)
if (ret < 0)
return ret;
+ max_hw_sectors_kb = min_not_zero(max_hw_sectors_kb, (unsigned long)
+ q->limits.max_dev_sectors >> 1);
+
if (max_sectors_kb > max_hw_sectors_kb || max_sectors_kb < page_kb)
return -EINVAL;
diff --git a/block/blk-timeout.c b/block/blk-timeout.c
index 246dfb16c3d9..aa40aa93381b 100644
--- a/block/blk-timeout.c
+++ b/block/blk-timeout.c
@@ -158,11 +158,13 @@ void blk_abort_request(struct request *req)
{
if (blk_mark_rq_complete(req))
return;
- blk_delete_timer(req);
- if (req->q->mq_ops)
+
+ if (req->q->mq_ops) {
blk_mq_rq_timed_out(req, false);
- else
+ } else {
+ blk_delete_timer(req);
blk_rq_timed_out(req);
+ }
}
EXPORT_SYMBOL_GPL(blk_abort_request);
diff --git a/block/genhd.c b/block/genhd.c
index e5cafa51567c..5aaeb2ad0fd3 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -20,6 +20,7 @@
#include <linux/idr.h>
#include <linux/log2.h>
#include <linux/pm_runtime.h>
+#include <linux/badblocks.h>
#include "blk.h"
@@ -664,7 +665,6 @@ void del_gendisk(struct gendisk *disk)
kobject_put(disk->part0.holder_dir);
kobject_put(disk->slave_dir);
- disk->driverfs_dev = NULL;
if (!sysfs_deprecated)
sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk)));
pm_runtime_set_memalloc_noio(disk_to_dev(disk), false);
@@ -672,6 +672,31 @@ void del_gendisk(struct gendisk *disk)
}
EXPORT_SYMBOL(del_gendisk);
+/* sysfs access to bad-blocks list. */
+static ssize_t disk_badblocks_show(struct device *dev,
+ struct device_attribute *attr,
+ char *page)
+{
+ struct gendisk *disk = dev_to_disk(dev);
+
+ if (!disk->bb)
+ return sprintf(page, "\n");
+
+ return badblocks_show(disk->bb, page, 0);
+}
+
+static ssize_t disk_badblocks_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *page, size_t len)
+{
+ struct gendisk *disk = dev_to_disk(dev);
+
+ if (!disk->bb)
+ return -ENXIO;
+
+ return badblocks_store(disk->bb, page, len, 0);
+}
+
/**
* get_gendisk - get partitioning information for a given device
* @devt: device to get partitioning information for
@@ -990,6 +1015,8 @@ static DEVICE_ATTR(discard_alignment, S_IRUGO, disk_discard_alignment_show,
static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL);
static DEVICE_ATTR(stat, S_IRUGO, part_stat_show, NULL);
static DEVICE_ATTR(inflight, S_IRUGO, part_inflight_show, NULL);
+static DEVICE_ATTR(badblocks, S_IRUGO | S_IWUSR, disk_badblocks_show,
+ disk_badblocks_store);
#ifdef CONFIG_FAIL_MAKE_REQUEST
static struct device_attribute dev_attr_fail =
__ATTR(make-it-fail, S_IRUGO|S_IWUSR, part_fail_show, part_fail_store);
@@ -1011,6 +1038,7 @@ static struct attribute *disk_attrs[] = {
&dev_attr_capability.attr,
&dev_attr_stat.attr,
&dev_attr_inflight.attr,
+ &dev_attr_badblocks.attr,
#ifdef CONFIG_FAIL_MAKE_REQUEST
&dev_attr_fail.attr,
#endif
diff --git a/block/ioctl.c b/block/ioctl.c
index 0918aed2d847..2c84683aada5 100644
--- a/block/ioctl.c
+++ b/block/ioctl.c
@@ -4,6 +4,7 @@
#include <linux/gfp.h>
#include <linux/blkpg.h>
#include <linux/hdreg.h>
+#include <linux/badblocks.h>
#include <linux/backing-dev.h>
#include <linux/fs.h>
#include <linux/blktrace_api.h>
@@ -406,6 +407,71 @@ static inline int is_unrecognized_ioctl(int ret)
ret == -ENOIOCTLCMD;
}
+#ifdef CONFIG_FS_DAX
+bool blkdev_dax_capable(struct block_device *bdev)
+{
+ struct gendisk *disk = bdev->bd_disk;
+
+ if (!disk->fops->direct_access)
+ return false;
+
+ /*
+ * If the partition is not aligned on a page boundary, we can't
+ * do dax I/O to it.
+ */
+ if ((bdev->bd_part->start_sect % (PAGE_SIZE / 512))
+ || (bdev->bd_part->nr_sects % (PAGE_SIZE / 512)))
+ return false;
+
+ /*
+ * If the device has known bad blocks, force all I/O through the
+ * driver / page cache.
+ *
+ * TODO: support finer grained dax error handling
+ */
+ if (disk->bb && disk->bb->count)
+ return false;
+
+ return true;
+}
+
+static int blkdev_daxset(struct block_device *bdev, unsigned long argp)
+{
+ unsigned long arg;
+ int rc = 0;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ if (get_user(arg, (int __user *)(argp)))
+ return -EFAULT;
+ arg = !!arg;
+ if (arg == !!(bdev->bd_inode->i_flags & S_DAX))
+ return 0;
+
+ if (arg)
+ arg = S_DAX;
+
+ if (arg && !blkdev_dax_capable(bdev))
+ return -ENOTTY;
+
+ mutex_lock(&bdev->bd_inode->i_mutex);
+ if (bdev->bd_map_count == 0)
+ inode_set_flags(bdev->bd_inode, arg, S_DAX);
+ else
+ rc = -EBUSY;
+ mutex_unlock(&bdev->bd_inode->i_mutex);
+ return rc;
+}
+#else
+static int blkdev_daxset(struct block_device *bdev, int arg)
+{
+ if (arg)
+ return -ENOTTY;
+ return 0;
+}
+#endif
+
static int blkdev_flushbuf(struct block_device *bdev, fmode_t mode,
unsigned cmd, unsigned long arg)
{
@@ -568,6 +634,11 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
case BLKTRACESETUP:
case BLKTRACETEARDOWN:
return blk_trace_ioctl(bdev, cmd, argp);
+ case BLKDAXSET:
+ return blkdev_daxset(bdev, arg);
+ case BLKDAXGET:
+ return put_int(arg, !!(bdev->bd_inode->i_flags & S_DAX));
+ break;
case IOC_PR_REGISTER:
return blkdev_pr_register(bdev, argp);
case IOC_PR_RESERVE:
diff --git a/block/noop-iosched.c b/block/noop-iosched.c
index 3de89d4690f3..a163c487cf38 100644
--- a/block/noop-iosched.c
+++ b/block/noop-iosched.c
@@ -21,10 +21,10 @@ static void noop_merged_requests(struct request_queue *q, struct request *rq,
static int noop_dispatch(struct request_queue *q, int force)
{
struct noop_data *nd = q->elevator->elevator_data;
+ struct request *rq;
- if (!list_empty(&nd->queue)) {
- struct request *rq;
- rq = list_entry(nd->queue.next, struct request, queuelist);
+ rq = list_first_entry_or_null(&nd->queue, struct request, queuelist);
+ if (rq) {
list_del_init(&rq->queuelist);
elv_dispatch_sort(q, rq);
return 1;
@@ -46,7 +46,7 @@ noop_former_request(struct request_queue *q, struct request *rq)
if (rq->queuelist.prev == &nd->queue)
return NULL;
- return list_entry(rq->queuelist.prev, struct request, queuelist);
+ return list_prev_entry(rq, queuelist);
}
static struct request *
@@ -56,7 +56,7 @@ noop_latter_request(struct request_queue *q, struct request *rq)
if (rq->queuelist.next == &nd->queue)
return NULL;
- return list_entry(rq->queuelist.next, struct request, queuelist);
+ return list_next_entry(rq, queuelist);
}
static int noop_init_queue(struct request_queue *q, struct elevator_type *e)
diff --git a/block/partition-generic.c b/block/partition-generic.c
index 3b030157ec85..746935a5973c 100644
--- a/block/partition-generic.c
+++ b/block/partition-generic.c
@@ -397,7 +397,7 @@ static int drop_partitions(struct gendisk *disk, struct block_device *bdev)
struct hd_struct *part;
int res;
- if (bdev->bd_part_count)
+ if (bdev->bd_part_count || bdev->bd_super)
return -EBUSY;
res = invalidate_partition(disk, 0);
if (res)
diff --git a/block/partitions/mac.c b/block/partitions/mac.c
index c2c48ec64b27..621317ac4d59 100644
--- a/block/partitions/mac.c
+++ b/block/partitions/mac.c
@@ -32,7 +32,7 @@ int mac_partition(struct parsed_partitions *state)
Sector sect;
unsigned char *data;
int slot, blocks_in_map;
- unsigned secsize;
+ unsigned secsize, datasize, partoffset;
#ifdef CONFIG_PPC_PMAC
int found_root = 0;
int found_root_goodness = 0;
@@ -50,10 +50,14 @@ int mac_partition(struct parsed_partitions *state)
}
secsize = be16_to_cpu(md->block_size);
put_dev_sector(sect);
- data = read_part_sector(state, secsize/512, &sect);
+ datasize = round_down(secsize, 512);
+ data = read_part_sector(state, datasize / 512, &sect);
if (!data)
return -1;
- part = (struct mac_partition *) (data + secsize%512);
+ partoffset = secsize % 512;
+ if (partoffset + sizeof(*part) > datasize)
+ return -1;
+ part = (struct mac_partition *) (data + partoffset);
if (be16_to_cpu(part->signature) != MAC_PARTITION_MAGIC) {
put_dev_sector(sect);
return 0; /* not a MacOS disk */
diff --git a/crypto/Makefile b/crypto/Makefile
index f7aba923458d..2acdbbd30475 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -40,6 +40,7 @@ rsa_generic-y := rsapubkey-asn1.o
rsa_generic-y += rsaprivkey-asn1.o
rsa_generic-y += rsa.o
rsa_generic-y += rsa_helper.o
+rsa_generic-y += rsa-pkcs1pad.o
obj-$(CONFIG_CRYPTO_RSA) += rsa_generic.o
cryptomgr-y := algboss.o testmgr.o
diff --git a/crypto/ablkcipher.c b/crypto/ablkcipher.c
index b4ffc5be1a93..e5b5721809e2 100644
--- a/crypto/ablkcipher.c
+++ b/crypto/ablkcipher.c
@@ -277,12 +277,12 @@ static int ablkcipher_walk_first(struct ablkcipher_request *req,
if (WARN_ON_ONCE(in_irq()))
return -EDEADLK;
+ walk->iv = req->info;
walk->nbytes = walk->total;
if (unlikely(!walk->total))
return 0;
walk->iv_buffer = NULL;
- walk->iv = req->info;
if (unlikely(((unsigned long)walk->iv & alignmask))) {
int err = ablkcipher_copy_iv(walk, tfm, alignmask);
diff --git a/crypto/akcipher.c b/crypto/akcipher.c
index 120ec042ec9e..def301ed1288 100644
--- a/crypto/akcipher.c
+++ b/crypto/akcipher.c
@@ -21,6 +21,7 @@
#include <linux/cryptouser.h>
#include <net/netlink.h>
#include <crypto/akcipher.h>
+#include <crypto/internal/akcipher.h>
#include "internal.h"
#ifdef CONFIG_NET
@@ -75,9 +76,17 @@ static int crypto_akcipher_init_tfm(struct crypto_tfm *tfm)
return 0;
}
+static void crypto_akcipher_free_instance(struct crypto_instance *inst)
+{
+ struct akcipher_instance *akcipher = akcipher_instance(inst);
+
+ akcipher->free(akcipher);
+}
+
static const struct crypto_type crypto_akcipher_type = {
.extsize = crypto_alg_extsize,
.init_tfm = crypto_akcipher_init_tfm,
+ .free = crypto_akcipher_free_instance,
#ifdef CONFIG_PROC_FS
.show = crypto_akcipher_show,
#endif
@@ -88,6 +97,14 @@ static const struct crypto_type crypto_akcipher_type = {
.tfmsize = offsetof(struct crypto_akcipher, base),
};
+int crypto_grab_akcipher(struct crypto_akcipher_spawn *spawn, const char *name,
+ u32 type, u32 mask)
+{
+ spawn->base.frontend = &crypto_akcipher_type;
+ return crypto_grab_spawn(&spawn->base, name, type, mask);
+}
+EXPORT_SYMBOL_GPL(crypto_grab_akcipher);
+
struct crypto_akcipher *crypto_alloc_akcipher(const char *alg_name, u32 type,
u32 mask)
{
@@ -95,13 +112,20 @@ struct crypto_akcipher *crypto_alloc_akcipher(const char *alg_name, u32 type,
}
EXPORT_SYMBOL_GPL(crypto_alloc_akcipher);
-int crypto_register_akcipher(struct akcipher_alg *alg)
+static void akcipher_prepare_alg(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;
+}
+
+int crypto_register_akcipher(struct akcipher_alg *alg)
+{
+ struct crypto_alg *base = &alg->base;
+
+ akcipher_prepare_alg(alg);
return crypto_register_alg(base);
}
EXPORT_SYMBOL_GPL(crypto_register_akcipher);
@@ -112,5 +136,13 @@ void crypto_unregister_akcipher(struct akcipher_alg *alg)
}
EXPORT_SYMBOL_GPL(crypto_unregister_akcipher);
+int akcipher_register_instance(struct crypto_template *tmpl,
+ struct akcipher_instance *inst)
+{
+ akcipher_prepare_alg(&inst->alg);
+ return crypto_register_instance(tmpl, akcipher_crypto_instance(inst));
+}
+EXPORT_SYMBOL_GPL(akcipher_register_instance);
+
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Generic public key cipher type");
diff --git a/crypto/algapi.c b/crypto/algapi.c
index 59bf491fe3d8..7be76aa31579 100644
--- a/crypto/algapi.c
+++ b/crypto/algapi.c
@@ -93,16 +93,15 @@ static struct list_head *crypto_more_spawns(struct crypto_alg *alg,
{
struct crypto_spawn *spawn, *n;
- if (list_empty(stack))
+ spawn = list_first_entry_or_null(stack, struct crypto_spawn, list);
+ if (!spawn)
return NULL;
- spawn = list_first_entry(stack, struct crypto_spawn, list);
- n = list_entry(spawn->list.next, struct crypto_spawn, list);
+ n = list_next_entry(spawn, list);
if (spawn->alg && &n->list != stack && !n->alg)
n->alg = (n->list.next == stack) ? alg :
- &list_entry(n->list.next, struct crypto_spawn,
- list)->inst->alg;
+ &list_next_entry(n, list)->inst->alg;
list_move(&spawn->list, secondary_spawns);
diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c
index 0aa6fdfb448a..147069c9afd0 100644
--- a/crypto/algif_aead.c
+++ b/crypto/algif_aead.c
@@ -106,7 +106,7 @@ static void aead_wmem_wakeup(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait, POLLIN |
POLLRDNORM |
POLLRDBAND);
@@ -125,7 +125,7 @@ static int aead_wait_for_data(struct sock *sk, unsigned flags)
if (flags & MSG_DONTWAIT)
return -EAGAIN;
- set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+ sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);
for (;;) {
if (signal_pending(current))
@@ -139,7 +139,7 @@ static int aead_wait_for_data(struct sock *sk, unsigned flags)
}
finish_wait(sk_sleep(sk), &wait);
- clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+ sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk);
return err;
}
@@ -157,7 +157,7 @@ static void aead_data_wakeup(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait, POLLOUT |
POLLRDNORM |
POLLRDBAND);
@@ -213,7 +213,7 @@ static int aead_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
}
while (size) {
- unsigned long len = size;
+ size_t len = size;
struct scatterlist *sg = NULL;
/* use the existing memory in an allocated page */
@@ -247,7 +247,7 @@ static int aead_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
/* allocate a new page */
len = min_t(unsigned long, size, aead_sndbuf(sk));
while (len) {
- int plen = 0;
+ size_t plen = 0;
if (sgl->cur >= ALG_MAX_PAGES) {
aead_put_sgl(sk);
@@ -256,7 +256,7 @@ static int aead_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
}
sg = sgl->sg + sgl->cur;
- plen = min_t(int, len, PAGE_SIZE);
+ plen = min_t(size_t, len, PAGE_SIZE);
sg_assign_page(sg, alloc_page(GFP_KERNEL));
err = -ENOMEM;
diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c
index af31a0ee4057..eaa9f9be5b87 100644
--- a/crypto/algif_skcipher.c
+++ b/crypto/algif_skcipher.c
@@ -40,14 +40,14 @@ struct skcipher_ctx {
struct af_alg_completion completion;
atomic_t inflight;
- unsigned used;
+ size_t used;
unsigned int len;
bool more;
bool merge;
bool enc;
- struct ablkcipher_request req;
+ struct skcipher_request req;
};
struct skcipher_async_rsgl {
@@ -64,13 +64,13 @@ struct skcipher_async_req {
};
#define GET_SREQ(areq, ctx) (struct skcipher_async_req *)((char *)areq + \
- crypto_ablkcipher_reqsize(crypto_ablkcipher_reqtfm(&ctx->req)))
+ crypto_skcipher_reqsize(crypto_skcipher_reqtfm(&ctx->req)))
#define GET_REQ_SIZE(ctx) \
- crypto_ablkcipher_reqsize(crypto_ablkcipher_reqtfm(&ctx->req))
+ crypto_skcipher_reqsize(crypto_skcipher_reqtfm(&ctx->req))
#define GET_IV_SIZE(ctx) \
- crypto_ablkcipher_ivsize(crypto_ablkcipher_reqtfm(&ctx->req))
+ crypto_skcipher_ivsize(crypto_skcipher_reqtfm(&ctx->req))
#define MAX_SGL_ENTS ((4096 - sizeof(struct skcipher_sg_list)) / \
sizeof(struct scatterlist) - 1)
@@ -153,7 +153,7 @@ static int skcipher_alloc_sgl(struct sock *sk)
return 0;
}
-static void skcipher_pull_sgl(struct sock *sk, int used, int put)
+static void skcipher_pull_sgl(struct sock *sk, size_t used, int put)
{
struct alg_sock *ask = alg_sk(sk);
struct skcipher_ctx *ctx = ask->private;
@@ -167,7 +167,7 @@ static void skcipher_pull_sgl(struct sock *sk, int used, int put)
sg = sgl->sg;
for (i = 0; i < sgl->cur; i++) {
- int plen = min_t(int, used, sg[i].length);
+ size_t plen = min_t(size_t, used, sg[i].length);
if (!sg_page(sg + i))
continue;
@@ -212,7 +212,7 @@ static int skcipher_wait_for_wmem(struct sock *sk, unsigned flags)
if (flags & MSG_DONTWAIT)
return -EAGAIN;
- set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+ sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
for (;;) {
if (signal_pending(current))
@@ -238,7 +238,7 @@ static void skcipher_wmem_wakeup(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait, POLLIN |
POLLRDNORM |
POLLRDBAND);
@@ -258,7 +258,7 @@ static int skcipher_wait_for_data(struct sock *sk, unsigned flags)
return -EAGAIN;
}
- set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+ sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);
for (;;) {
if (signal_pending(current))
@@ -272,7 +272,7 @@ static int skcipher_wait_for_data(struct sock *sk, unsigned flags)
}
finish_wait(sk_sleep(sk), &wait);
- clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+ sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk);
return err;
}
@@ -288,7 +288,7 @@ static void skcipher_data_wakeup(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait, POLLOUT |
POLLRDNORM |
POLLRDBAND);
@@ -302,8 +302,8 @@ static int skcipher_sendmsg(struct socket *sock, struct msghdr *msg,
struct sock *sk = sock->sk;
struct alg_sock *ask = alg_sk(sk);
struct skcipher_ctx *ctx = ask->private;
- struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(&ctx->req);
- unsigned ivsize = crypto_ablkcipher_ivsize(tfm);
+ struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(&ctx->req);
+ unsigned ivsize = crypto_skcipher_ivsize(tfm);
struct skcipher_sg_list *sgl;
struct af_alg_control con = {};
long copied = 0;
@@ -348,7 +348,7 @@ static int skcipher_sendmsg(struct socket *sock, struct msghdr *msg,
while (size) {
struct scatterlist *sg;
unsigned long len = size;
- int plen;
+ size_t plen;
if (ctx->merge) {
sgl = list_entry(ctx->tsgl.prev,
@@ -390,7 +390,7 @@ static int skcipher_sendmsg(struct socket *sock, struct msghdr *msg,
sg_unmark_end(sg + sgl->cur);
do {
i = sgl->cur;
- plen = min_t(int, len, PAGE_SIZE);
+ plen = min_t(size_t, len, PAGE_SIZE);
sg_assign_page(sg + i, alloc_page(GFP_KERNEL));
err = -ENOMEM;
@@ -507,7 +507,7 @@ static int skcipher_recvmsg_async(struct socket *sock, struct msghdr *msg,
struct skcipher_sg_list *sgl;
struct scatterlist *sg;
struct skcipher_async_req *sreq;
- struct ablkcipher_request *req;
+ struct skcipher_request *req;
struct skcipher_async_rsgl *last_rsgl = NULL;
unsigned int txbufs = 0, len = 0, tx_nents = skcipher_all_sg_nents(ctx);
unsigned int reqlen = sizeof(struct skcipher_async_req) +
@@ -531,9 +531,9 @@ static int skcipher_recvmsg_async(struct socket *sock, struct msghdr *msg,
}
sg_init_table(sreq->tsg, tx_nents);
memcpy(sreq->iv, ctx->iv, GET_IV_SIZE(ctx));
- ablkcipher_request_set_tfm(req, crypto_ablkcipher_reqtfm(&ctx->req));
- ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
- skcipher_async_cb, sk);
+ skcipher_request_set_tfm(req, crypto_skcipher_reqtfm(&ctx->req));
+ skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ skcipher_async_cb, sk);
while (iov_iter_count(&msg->msg_iter)) {
struct skcipher_async_rsgl *rsgl;
@@ -608,10 +608,10 @@ static int skcipher_recvmsg_async(struct socket *sock, struct msghdr *msg,
if (mark)
sg_mark_end(sreq->tsg + txbufs - 1);
- ablkcipher_request_set_crypt(req, sreq->tsg, sreq->first_sgl.sgl.sg,
- len, sreq->iv);
- err = ctx->enc ? crypto_ablkcipher_encrypt(req) :
- crypto_ablkcipher_decrypt(req);
+ skcipher_request_set_crypt(req, sreq->tsg, sreq->first_sgl.sgl.sg,
+ len, sreq->iv);
+ err = ctx->enc ? crypto_skcipher_encrypt(req) :
+ crypto_skcipher_decrypt(req);
if (err == -EINPROGRESS) {
atomic_inc(&ctx->inflight);
err = -EIOCBQUEUED;
@@ -632,7 +632,7 @@ static int skcipher_recvmsg_sync(struct socket *sock, struct msghdr *msg,
struct sock *sk = sock->sk;
struct alg_sock *ask = alg_sk(sk);
struct skcipher_ctx *ctx = ask->private;
- unsigned bs = crypto_ablkcipher_blocksize(crypto_ablkcipher_reqtfm(
+ unsigned bs = crypto_skcipher_blocksize(crypto_skcipher_reqtfm(
&ctx->req));
struct skcipher_sg_list *sgl;
struct scatterlist *sg;
@@ -669,14 +669,13 @@ static int skcipher_recvmsg_sync(struct socket *sock, struct msghdr *msg,
if (!used)
goto free;
- ablkcipher_request_set_crypt(&ctx->req, sg,
- ctx->rsgl.sg, used,
- ctx->iv);
+ skcipher_request_set_crypt(&ctx->req, sg, ctx->rsgl.sg, used,
+ ctx->iv);
err = af_alg_wait_for_completion(
ctx->enc ?
- crypto_ablkcipher_encrypt(&ctx->req) :
- crypto_ablkcipher_decrypt(&ctx->req),
+ crypto_skcipher_encrypt(&ctx->req) :
+ crypto_skcipher_decrypt(&ctx->req),
&ctx->completion);
free:
@@ -751,17 +750,17 @@ static struct proto_ops algif_skcipher_ops = {
static void *skcipher_bind(const char *name, u32 type, u32 mask)
{
- return crypto_alloc_ablkcipher(name, type, mask);
+ return crypto_alloc_skcipher(name, type, mask);
}
static void skcipher_release(void *private)
{
- crypto_free_ablkcipher(private);
+ crypto_free_skcipher(private);
}
static int skcipher_setkey(void *private, const u8 *key, unsigned int keylen)
{
- return crypto_ablkcipher_setkey(private, key, keylen);
+ return crypto_skcipher_setkey(private, key, keylen);
}
static void skcipher_wait(struct sock *sk)
@@ -778,13 +777,13 @@ static void skcipher_sock_destruct(struct sock *sk)
{
struct alg_sock *ask = alg_sk(sk);
struct skcipher_ctx *ctx = ask->private;
- struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(&ctx->req);
+ struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(&ctx->req);
if (atomic_read(&ctx->inflight))
skcipher_wait(sk);
skcipher_free_sgl(sk);
- sock_kzfree_s(sk, ctx->iv, crypto_ablkcipher_ivsize(tfm));
+ sock_kzfree_s(sk, ctx->iv, crypto_skcipher_ivsize(tfm));
sock_kfree_s(sk, ctx, ctx->len);
af_alg_release_parent(sk);
}
@@ -793,20 +792,20 @@ static int skcipher_accept_parent(void *private, struct sock *sk)
{
struct skcipher_ctx *ctx;
struct alg_sock *ask = alg_sk(sk);
- unsigned int len = sizeof(*ctx) + crypto_ablkcipher_reqsize(private);
+ unsigned int len = sizeof(*ctx) + crypto_skcipher_reqsize(private);
ctx = sock_kmalloc(sk, len, GFP_KERNEL);
if (!ctx)
return -ENOMEM;
- ctx->iv = sock_kmalloc(sk, crypto_ablkcipher_ivsize(private),
+ ctx->iv = sock_kmalloc(sk, crypto_skcipher_ivsize(private),
GFP_KERNEL);
if (!ctx->iv) {
sock_kfree_s(sk, ctx, len);
return -ENOMEM;
}
- memset(ctx->iv, 0, crypto_ablkcipher_ivsize(private));
+ memset(ctx->iv, 0, crypto_skcipher_ivsize(private));
INIT_LIST_HEAD(&ctx->tsgl);
ctx->len = len;
@@ -819,9 +818,9 @@ static int skcipher_accept_parent(void *private, struct sock *sk)
ask->private = ctx;
- ablkcipher_request_set_tfm(&ctx->req, private);
- ablkcipher_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG,
- af_alg_complete, &ctx->completion);
+ skcipher_request_set_tfm(&ctx->req, private);
+ skcipher_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ af_alg_complete, &ctx->completion);
sk->sk_destruct = skcipher_sock_destruct;
diff --git a/crypto/asymmetric_keys/signature.c b/crypto/asymmetric_keys/signature.c
index 9441240f7d2a..004d5fc8e56b 100644
--- a/crypto/asymmetric_keys/signature.c
+++ b/crypto/asymmetric_keys/signature.c
@@ -13,7 +13,7 @@
#define pr_fmt(fmt) "SIG: "fmt
#include <keys/asymmetric-subtype.h>
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/err.h>
#include <crypto/public_key.h>
#include "asymmetric_keys.h"
diff --git a/crypto/async_tx/async_memcpy.c b/crypto/async_tx/async_memcpy.c
index f8c0b8dbeb75..88bc8e6b2a54 100644
--- a/crypto/async_tx/async_memcpy.c
+++ b/crypto/async_tx/async_memcpy.c
@@ -53,7 +53,7 @@ async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset,
struct dmaengine_unmap_data *unmap = NULL;
if (device)
- unmap = dmaengine_get_unmap_data(device->dev, 2, GFP_NOIO);
+ unmap = dmaengine_get_unmap_data(device->dev, 2, GFP_NOWAIT);
if (unmap && is_dma_copy_aligned(device, src_offset, dest_offset, len)) {
unsigned long dma_prep_flags = 0;
diff --git a/crypto/async_tx/async_pq.c b/crypto/async_tx/async_pq.c
index 5d355e0c2633..c0748bbd4c08 100644
--- a/crypto/async_tx/async_pq.c
+++ b/crypto/async_tx/async_pq.c
@@ -188,7 +188,7 @@ async_gen_syndrome(struct page **blocks, unsigned int offset, int disks,
BUG_ON(disks > 255 || !(P(blocks, disks) || Q(blocks, disks)));
if (device)
- unmap = dmaengine_get_unmap_data(device->dev, disks, GFP_NOIO);
+ unmap = dmaengine_get_unmap_data(device->dev, disks, GFP_NOWAIT);
/* XORing P/Q is only implemented in software */
if (unmap && !(submit->flags & ASYNC_TX_PQ_XOR_DST) &&
@@ -307,7 +307,7 @@ async_syndrome_val(struct page **blocks, unsigned int offset, int disks,
BUG_ON(disks < 4);
if (device)
- unmap = dmaengine_get_unmap_data(device->dev, disks, GFP_NOIO);
+ unmap = dmaengine_get_unmap_data(device->dev, disks, GFP_NOWAIT);
if (unmap && disks <= dma_maxpq(device, 0) &&
is_dma_pq_aligned(device, offset, 0, len)) {
diff --git a/crypto/async_tx/async_raid6_recov.c b/crypto/async_tx/async_raid6_recov.c
index 934a84981495..8fab6275ea1f 100644
--- a/crypto/async_tx/async_raid6_recov.c
+++ b/crypto/async_tx/async_raid6_recov.c
@@ -41,7 +41,7 @@ async_sum_product(struct page *dest, struct page **srcs, unsigned char *coef,
u8 *a, *b, *c;
if (dma)
- unmap = dmaengine_get_unmap_data(dma->dev, 3, GFP_NOIO);
+ unmap = dmaengine_get_unmap_data(dma->dev, 3, GFP_NOWAIT);
if (unmap) {
struct device *dev = dma->dev;
@@ -105,7 +105,7 @@ async_mult(struct page *dest, struct page *src, u8 coef, size_t len,
u8 *d, *s;
if (dma)
- unmap = dmaengine_get_unmap_data(dma->dev, 3, GFP_NOIO);
+ unmap = dmaengine_get_unmap_data(dma->dev, 3, GFP_NOWAIT);
if (unmap) {
dma_addr_t dma_dest[2];
diff --git a/crypto/async_tx/async_xor.c b/crypto/async_tx/async_xor.c
index e1bce26cd4f9..da75777f2b3f 100644
--- a/crypto/async_tx/async_xor.c
+++ b/crypto/async_tx/async_xor.c
@@ -182,7 +182,7 @@ async_xor(struct page *dest, struct page **src_list, unsigned int offset,
BUG_ON(src_cnt <= 1);
if (device)
- unmap = dmaengine_get_unmap_data(device->dev, src_cnt+1, GFP_NOIO);
+ unmap = dmaengine_get_unmap_data(device->dev, src_cnt+1, GFP_NOWAIT);
if (unmap && is_dma_xor_aligned(device, offset, 0, len)) {
struct dma_async_tx_descriptor *tx;
@@ -278,7 +278,7 @@ async_xor_val(struct page *dest, struct page **src_list, unsigned int offset,
BUG_ON(src_cnt <= 1);
if (device)
- unmap = dmaengine_get_unmap_data(device->dev, src_cnt, GFP_NOIO);
+ unmap = dmaengine_get_unmap_data(device->dev, src_cnt, GFP_NOWAIT);
if (unmap && src_cnt <= device->max_xor &&
is_dma_xor_aligned(device, offset, 0, len)) {
diff --git a/crypto/blkcipher.c b/crypto/blkcipher.c
index 11b981492031..8cc1622b2ee0 100644
--- a/crypto/blkcipher.c
+++ b/crypto/blkcipher.c
@@ -326,12 +326,12 @@ static int blkcipher_walk_first(struct blkcipher_desc *desc,
if (WARN_ON_ONCE(in_irq()))
return -EDEADLK;
+ walk->iv = desc->info;
walk->nbytes = walk->total;
if (unlikely(!walk->total))
return 0;
walk->buffer = NULL;
- walk->iv = desc->info;
if (unlikely(((unsigned long)walk->iv & walk->alignmask))) {
int err = blkcipher_copy_iv(walk);
if (err)
diff --git a/crypto/chacha20poly1305.c b/crypto/chacha20poly1305.c
index 99c3cce01290..7b6b935cef23 100644
--- a/crypto/chacha20poly1305.c
+++ b/crypto/chacha20poly1305.c
@@ -130,6 +130,9 @@ static int chacha_decrypt(struct aead_request *req)
struct scatterlist *src, *dst;
int err;
+ if (rctx->cryptlen == 0)
+ goto skip;
+
chacha_iv(creq->iv, req, 1);
sg_init_table(rctx->src, 2);
@@ -150,6 +153,7 @@ static int chacha_decrypt(struct aead_request *req)
if (err)
return err;
+skip:
return poly_verify_tag(req);
}
@@ -415,6 +419,9 @@ static int chacha_encrypt(struct aead_request *req)
struct scatterlist *src, *dst;
int err;
+ if (req->cryptlen == 0)
+ goto skip;
+
chacha_iv(creq->iv, req, 1);
sg_init_table(rctx->src, 2);
@@ -435,6 +442,7 @@ static int chacha_encrypt(struct aead_request *req)
if (err)
return err;
+skip:
return poly_genkey(req);
}
diff --git a/crypto/cryptd.c b/crypto/cryptd.c
index c81861b1350b..7921251cdb13 100644
--- a/crypto/cryptd.c
+++ b/crypto/cryptd.c
@@ -637,6 +637,7 @@ static int cryptd_create_hash(struct crypto_template *tmpl, struct rtattr **tb,
inst->alg.halg.base.cra_flags = type;
inst->alg.halg.digestsize = salg->digestsize;
+ inst->alg.halg.statesize = salg->statesize;
inst->alg.halg.base.cra_ctxsize = sizeof(struct cryptd_hash_ctx);
inst->alg.halg.base.cra_init = cryptd_hash_init_tfm;
@@ -887,8 +888,7 @@ struct cryptd_ablkcipher *cryptd_alloc_ablkcipher(const char *alg_name,
if (snprintf(cryptd_alg_name, CRYPTO_MAX_ALG_NAME,
"cryptd(%s)", alg_name) >= CRYPTO_MAX_ALG_NAME)
return ERR_PTR(-EINVAL);
- type &= ~(CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV);
- type |= CRYPTO_ALG_TYPE_BLKCIPHER;
+ type = crypto_skcipher_type(type);
mask &= ~CRYPTO_ALG_TYPE_MASK;
mask |= (CRYPTO_ALG_GENIV | CRYPTO_ALG_TYPE_BLKCIPHER_MASK);
tfm = crypto_alloc_base(cryptd_alg_name, type, mask);
diff --git a/crypto/drbg.c b/crypto/drbg.c
index a7c23146b87f..ab6ef1d08568 100644
--- a/crypto/drbg.c
+++ b/crypto/drbg.c
@@ -626,7 +626,7 @@ out:
return len;
}
-static struct drbg_state_ops drbg_ctr_ops = {
+static const struct drbg_state_ops drbg_ctr_ops = {
.update = drbg_ctr_update,
.generate = drbg_ctr_generate,
.crypto_init = drbg_init_sym_kernel,
@@ -752,7 +752,7 @@ static int drbg_hmac_generate(struct drbg_state *drbg,
return len;
}
-static struct drbg_state_ops drbg_hmac_ops = {
+static const struct drbg_state_ops drbg_hmac_ops = {
.update = drbg_hmac_update,
.generate = drbg_hmac_generate,
.crypto_init = drbg_init_hash_kernel,
@@ -1032,7 +1032,7 @@ out:
* scratchpad usage: as update and generate are used isolated, both
* can use the scratchpad
*/
-static struct drbg_state_ops drbg_hash_ops = {
+static const struct drbg_state_ops drbg_hash_ops = {
.update = drbg_hash_update,
.generate = drbg_hash_generate,
.crypto_init = drbg_init_hash_kernel,
diff --git a/crypto/mcryptd.c b/crypto/mcryptd.c
index fe5b495a434d..f78d4fc4e38a 100644
--- a/crypto/mcryptd.c
+++ b/crypto/mcryptd.c
@@ -128,13 +128,9 @@ static void mcryptd_opportunistic_flush(void)
flist = per_cpu_ptr(mcryptd_flist, smp_processor_id());
while (single_task_running()) {
mutex_lock(&flist->lock);
- if (list_empty(&flist->list)) {
- mutex_unlock(&flist->lock);
- return;
- }
- cstate = list_entry(flist->list.next,
+ cstate = list_first_entry_or_null(&flist->list,
struct mcryptd_alg_cstate, flush_list);
- if (!cstate->flusher_engaged) {
+ if (!cstate || !cstate->flusher_engaged) {
mutex_unlock(&flist->lock);
return;
}
diff --git a/crypto/md5.c b/crypto/md5.c
index 33d17e9a8702..2355a7c25c45 100644
--- a/crypto/md5.c
+++ b/crypto/md5.c
@@ -24,6 +24,12 @@
#include <linux/cryptohash.h>
#include <asm/byteorder.h>
+const u8 md5_zero_message_hash[MD5_DIGEST_SIZE] = {
+ 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
+ 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e,
+};
+EXPORT_SYMBOL_GPL(md5_zero_message_hash);
+
/* XXX: this stuff can be optimized */
static inline void le32_to_cpu_array(u32 *buf, unsigned int words)
{
diff --git a/crypto/rsa-pkcs1pad.c b/crypto/rsa-pkcs1pad.c
new file mode 100644
index 000000000000..50f5c97e1087
--- /dev/null
+++ b/crypto/rsa-pkcs1pad.c
@@ -0,0 +1,628 @@
+/*
+ * RSA padding templates.
+ *
+ * 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.
+ */
+
+#include <crypto/algapi.h>
+#include <crypto/akcipher.h>
+#include <crypto/internal/akcipher.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/random.h>
+
+struct pkcs1pad_ctx {
+ struct crypto_akcipher *child;
+
+ unsigned int key_size;
+};
+
+struct pkcs1pad_request {
+ struct akcipher_request child_req;
+
+ struct scatterlist in_sg[3], out_sg[2];
+ uint8_t *in_buf, *out_buf;
+};
+
+static int pkcs1pad_set_pub_key(struct crypto_akcipher *tfm, const void *key,
+ unsigned int keylen)
+{
+ struct pkcs1pad_ctx *ctx = akcipher_tfm_ctx(tfm);
+ int err, size;
+
+ err = crypto_akcipher_set_pub_key(ctx->child, key, keylen);
+
+ if (!err) {
+ /* Find out new modulus size from rsa implementation */
+ size = crypto_akcipher_maxsize(ctx->child);
+
+ ctx->key_size = size > 0 ? size : 0;
+ if (size <= 0)
+ err = size;
+ }
+
+ return err;
+}
+
+static int pkcs1pad_set_priv_key(struct crypto_akcipher *tfm, const void *key,
+ unsigned int keylen)
+{
+ struct pkcs1pad_ctx *ctx = akcipher_tfm_ctx(tfm);
+ int err, size;
+
+ err = crypto_akcipher_set_priv_key(ctx->child, key, keylen);
+
+ if (!err) {
+ /* Find out new modulus size from rsa implementation */
+ size = crypto_akcipher_maxsize(ctx->child);
+
+ ctx->key_size = size > 0 ? size : 0;
+ if (size <= 0)
+ err = size;
+ }
+
+ return err;
+}
+
+static int pkcs1pad_get_max_size(struct crypto_akcipher *tfm)
+{
+ struct pkcs1pad_ctx *ctx = akcipher_tfm_ctx(tfm);
+
+ /*
+ * The maximum destination buffer size for the encrypt/sign operations
+ * will be the same as for RSA, even though it's smaller for
+ * decrypt/verify.
+ */
+
+ return ctx->key_size ?: -EINVAL;
+}
+
+static void pkcs1pad_sg_set_buf(struct scatterlist *sg, void *buf, size_t len,
+ struct scatterlist *next)
+{
+ int nsegs = next ? 1 : 0;
+
+ if (offset_in_page(buf) + len <= PAGE_SIZE) {
+ nsegs += 1;
+ sg_init_table(sg, nsegs);
+ sg_set_buf(sg, buf, len);
+ } else {
+ nsegs += 2;
+ sg_init_table(sg, nsegs);
+ sg_set_buf(sg + 0, buf, PAGE_SIZE - offset_in_page(buf));
+ sg_set_buf(sg + 1, buf + PAGE_SIZE - offset_in_page(buf),
+ offset_in_page(buf) + len - PAGE_SIZE);
+ }
+
+ if (next)
+ sg_chain(sg, nsegs, next);
+}
+
+static int pkcs1pad_encrypt_sign_complete(struct akcipher_request *req, int err)
+{
+ struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
+ struct pkcs1pad_ctx *ctx = akcipher_tfm_ctx(tfm);
+ struct pkcs1pad_request *req_ctx = akcipher_request_ctx(req);
+ size_t pad_len = ctx->key_size - req_ctx->child_req.dst_len;
+ size_t chunk_len, pad_left;
+ struct sg_mapping_iter miter;
+
+ if (!err) {
+ if (pad_len) {
+ sg_miter_start(&miter, req->dst,
+ sg_nents_for_len(req->dst, pad_len),
+ SG_MITER_ATOMIC | SG_MITER_TO_SG);
+
+ pad_left = pad_len;
+ while (pad_left) {
+ sg_miter_next(&miter);
+
+ chunk_len = min(miter.length, pad_left);
+ memset(miter.addr, 0, chunk_len);
+ pad_left -= chunk_len;
+ }
+
+ sg_miter_stop(&miter);
+ }
+
+ sg_pcopy_from_buffer(req->dst,
+ sg_nents_for_len(req->dst, ctx->key_size),
+ req_ctx->out_buf, req_ctx->child_req.dst_len,
+ pad_len);
+ }
+ req->dst_len = ctx->key_size;
+
+ kfree(req_ctx->in_buf);
+ kzfree(req_ctx->out_buf);
+
+ return err;
+}
+
+static void pkcs1pad_encrypt_sign_complete_cb(
+ struct crypto_async_request *child_async_req, int err)
+{
+ struct akcipher_request *req = child_async_req->data;
+ struct crypto_async_request async_req;
+
+ if (err == -EINPROGRESS)
+ return;
+
+ async_req.data = req->base.data;
+ async_req.tfm = crypto_akcipher_tfm(crypto_akcipher_reqtfm(req));
+ async_req.flags = child_async_req->flags;
+ req->base.complete(&async_req,
+ pkcs1pad_encrypt_sign_complete(req, err));
+}
+
+static int pkcs1pad_encrypt(struct akcipher_request *req)
+{
+ struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
+ struct pkcs1pad_ctx *ctx = akcipher_tfm_ctx(tfm);
+ struct pkcs1pad_request *req_ctx = akcipher_request_ctx(req);
+ int err;
+ unsigned int i, ps_end;
+
+ if (!ctx->key_size)
+ return -EINVAL;
+
+ if (req->src_len > ctx->key_size - 11)
+ return -EOVERFLOW;
+
+ if (req->dst_len < ctx->key_size) {
+ req->dst_len = ctx->key_size;
+ return -EOVERFLOW;
+ }
+
+ if (ctx->key_size > PAGE_SIZE)
+ return -ENOTSUPP;
+
+ /*
+ * Replace both input and output to add the padding in the input and
+ * the potential missing leading zeros in the output.
+ */
+ req_ctx->child_req.src = req_ctx->in_sg;
+ req_ctx->child_req.src_len = ctx->key_size - 1;
+ req_ctx->child_req.dst = req_ctx->out_sg;
+ req_ctx->child_req.dst_len = ctx->key_size;
+
+ req_ctx->in_buf = kmalloc(ctx->key_size - 1 - req->src_len,
+ (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
+ GFP_KERNEL : GFP_ATOMIC);
+ if (!req_ctx->in_buf)
+ return -ENOMEM;
+
+ ps_end = ctx->key_size - req->src_len - 2;
+ req_ctx->in_buf[0] = 0x02;
+ for (i = 1; i < ps_end; i++)
+ req_ctx->in_buf[i] = 1 + prandom_u32_max(255);
+ req_ctx->in_buf[ps_end] = 0x00;
+
+ pkcs1pad_sg_set_buf(req_ctx->in_sg, req_ctx->in_buf,
+ ctx->key_size - 1 - req->src_len, req->src);
+
+ req_ctx->out_buf = kmalloc(ctx->key_size,
+ (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
+ GFP_KERNEL : GFP_ATOMIC);
+ if (!req_ctx->out_buf) {
+ kfree(req_ctx->in_buf);
+ return -ENOMEM;
+ }
+
+ pkcs1pad_sg_set_buf(req_ctx->out_sg, req_ctx->out_buf,
+ ctx->key_size, NULL);
+
+ akcipher_request_set_tfm(&req_ctx->child_req, ctx->child);
+ akcipher_request_set_callback(&req_ctx->child_req, req->base.flags,
+ pkcs1pad_encrypt_sign_complete_cb, req);
+
+ err = crypto_akcipher_encrypt(&req_ctx->child_req);
+ if (err != -EINPROGRESS &&
+ (err != -EBUSY ||
+ !(req->base.flags & CRYPTO_TFM_REQ_MAY_BACKLOG)))
+ return pkcs1pad_encrypt_sign_complete(req, err);
+
+ return err;
+}
+
+static int pkcs1pad_decrypt_complete(struct akcipher_request *req, int err)
+{
+ struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
+ struct pkcs1pad_ctx *ctx = akcipher_tfm_ctx(tfm);
+ struct pkcs1pad_request *req_ctx = akcipher_request_ctx(req);
+ unsigned int pos;
+
+ if (err == -EOVERFLOW)
+ /* Decrypted value had no leading 0 byte */
+ err = -EINVAL;
+
+ if (err)
+ goto done;
+
+ if (req_ctx->child_req.dst_len != ctx->key_size - 1) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (req_ctx->out_buf[0] != 0x02) {
+ err = -EINVAL;
+ goto done;
+ }
+ for (pos = 1; pos < req_ctx->child_req.dst_len; pos++)
+ if (req_ctx->out_buf[pos] == 0x00)
+ break;
+ if (pos < 9 || pos == req_ctx->child_req.dst_len) {
+ err = -EINVAL;
+ goto done;
+ }
+ pos++;
+
+ if (req->dst_len < req_ctx->child_req.dst_len - pos)
+ err = -EOVERFLOW;
+ req->dst_len = req_ctx->child_req.dst_len - pos;
+
+ if (!err)
+ sg_copy_from_buffer(req->dst,
+ sg_nents_for_len(req->dst, req->dst_len),
+ req_ctx->out_buf + pos, req->dst_len);
+
+done:
+ kzfree(req_ctx->out_buf);
+
+ return err;
+}
+
+static void pkcs1pad_decrypt_complete_cb(
+ struct crypto_async_request *child_async_req, int err)
+{
+ struct akcipher_request *req = child_async_req->data;
+ struct crypto_async_request async_req;
+
+ if (err == -EINPROGRESS)
+ return;
+
+ async_req.data = req->base.data;
+ async_req.tfm = crypto_akcipher_tfm(crypto_akcipher_reqtfm(req));
+ async_req.flags = child_async_req->flags;
+ req->base.complete(&async_req, pkcs1pad_decrypt_complete(req, err));
+}
+
+static int pkcs1pad_decrypt(struct akcipher_request *req)
+{
+ struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
+ struct pkcs1pad_ctx *ctx = akcipher_tfm_ctx(tfm);
+ struct pkcs1pad_request *req_ctx = akcipher_request_ctx(req);
+ int err;
+
+ if (!ctx->key_size || req->src_len != ctx->key_size)
+ return -EINVAL;
+
+ if (ctx->key_size > PAGE_SIZE)
+ return -ENOTSUPP;
+
+ /* Reuse input buffer, output to a new buffer */
+ req_ctx->child_req.src = req->src;
+ req_ctx->child_req.src_len = req->src_len;
+ req_ctx->child_req.dst = req_ctx->out_sg;
+ req_ctx->child_req.dst_len = ctx->key_size - 1;
+
+ req_ctx->out_buf = kmalloc(ctx->key_size - 1,
+ (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
+ GFP_KERNEL : GFP_ATOMIC);
+ if (!req_ctx->out_buf)
+ return -ENOMEM;
+
+ pkcs1pad_sg_set_buf(req_ctx->out_sg, req_ctx->out_buf,
+ ctx->key_size - 1, NULL);
+
+ akcipher_request_set_tfm(&req_ctx->child_req, ctx->child);
+ akcipher_request_set_callback(&req_ctx->child_req, req->base.flags,
+ pkcs1pad_decrypt_complete_cb, req);
+
+ err = crypto_akcipher_decrypt(&req_ctx->child_req);
+ if (err != -EINPROGRESS &&
+ (err != -EBUSY ||
+ !(req->base.flags & CRYPTO_TFM_REQ_MAY_BACKLOG)))
+ return pkcs1pad_decrypt_complete(req, err);
+
+ return err;
+}
+
+static int pkcs1pad_sign(struct akcipher_request *req)
+{
+ struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
+ struct pkcs1pad_ctx *ctx = akcipher_tfm_ctx(tfm);
+ struct pkcs1pad_request *req_ctx = akcipher_request_ctx(req);
+ int err;
+ unsigned int ps_end;
+
+ if (!ctx->key_size)
+ return -EINVAL;
+
+ if (req->src_len > ctx->key_size - 11)
+ return -EOVERFLOW;
+
+ if (req->dst_len < ctx->key_size) {
+ req->dst_len = ctx->key_size;
+ return -EOVERFLOW;
+ }
+
+ if (ctx->key_size > PAGE_SIZE)
+ return -ENOTSUPP;
+
+ /*
+ * Replace both input and output to add the padding in the input and
+ * the potential missing leading zeros in the output.
+ */
+ req_ctx->child_req.src = req_ctx->in_sg;
+ req_ctx->child_req.src_len = ctx->key_size - 1;
+ req_ctx->child_req.dst = req_ctx->out_sg;
+ req_ctx->child_req.dst_len = ctx->key_size;
+
+ req_ctx->in_buf = kmalloc(ctx->key_size - 1 - req->src_len,
+ (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
+ GFP_KERNEL : GFP_ATOMIC);
+ if (!req_ctx->in_buf)
+ return -ENOMEM;
+
+ ps_end = ctx->key_size - req->src_len - 2;
+ req_ctx->in_buf[0] = 0x01;
+ memset(req_ctx->in_buf + 1, 0xff, ps_end - 1);
+ req_ctx->in_buf[ps_end] = 0x00;
+
+ pkcs1pad_sg_set_buf(req_ctx->in_sg, req_ctx->in_buf,
+ ctx->key_size - 1 - req->src_len, req->src);
+
+ req_ctx->out_buf = kmalloc(ctx->key_size,
+ (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
+ GFP_KERNEL : GFP_ATOMIC);
+ if (!req_ctx->out_buf) {
+ kfree(req_ctx->in_buf);
+ return -ENOMEM;
+ }
+
+ pkcs1pad_sg_set_buf(req_ctx->out_sg, req_ctx->out_buf,
+ ctx->key_size, NULL);
+
+ akcipher_request_set_tfm(&req_ctx->child_req, ctx->child);
+ akcipher_request_set_callback(&req_ctx->child_req, req->base.flags,
+ pkcs1pad_encrypt_sign_complete_cb, req);
+
+ err = crypto_akcipher_sign(&req_ctx->child_req);
+ if (err != -EINPROGRESS &&
+ (err != -EBUSY ||
+ !(req->base.flags & CRYPTO_TFM_REQ_MAY_BACKLOG)))
+ return pkcs1pad_encrypt_sign_complete(req, err);
+
+ return err;
+}
+
+static int pkcs1pad_verify_complete(struct akcipher_request *req, int err)
+{
+ struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
+ struct pkcs1pad_ctx *ctx = akcipher_tfm_ctx(tfm);
+ struct pkcs1pad_request *req_ctx = akcipher_request_ctx(req);
+ unsigned int pos;
+
+ if (err == -EOVERFLOW)
+ /* Decrypted value had no leading 0 byte */
+ err = -EINVAL;
+
+ if (err)
+ goto done;
+
+ if (req_ctx->child_req.dst_len != ctx->key_size - 1) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (req_ctx->out_buf[0] != 0x01) {
+ err = -EINVAL;
+ goto done;
+ }
+ for (pos = 1; pos < req_ctx->child_req.dst_len; pos++)
+ if (req_ctx->out_buf[pos] != 0xff)
+ break;
+ if (pos < 9 || pos == req_ctx->child_req.dst_len ||
+ req_ctx->out_buf[pos] != 0x00) {
+ err = -EINVAL;
+ goto done;
+ }
+ pos++;
+
+ if (req->dst_len < req_ctx->child_req.dst_len - pos)
+ err = -EOVERFLOW;
+ req->dst_len = req_ctx->child_req.dst_len - pos;
+
+ if (!err)
+ sg_copy_from_buffer(req->dst,
+ sg_nents_for_len(req->dst, req->dst_len),
+ req_ctx->out_buf + pos, req->dst_len);
+
+done:
+ kzfree(req_ctx->out_buf);
+
+ return err;
+}
+
+static void pkcs1pad_verify_complete_cb(
+ struct crypto_async_request *child_async_req, int err)
+{
+ struct akcipher_request *req = child_async_req->data;
+ struct crypto_async_request async_req;
+
+ if (err == -EINPROGRESS)
+ return;
+
+ async_req.data = req->base.data;
+ async_req.tfm = crypto_akcipher_tfm(crypto_akcipher_reqtfm(req));
+ async_req.flags = child_async_req->flags;
+ req->base.complete(&async_req, pkcs1pad_verify_complete(req, err));
+}
+
+/*
+ * The verify operation is here for completeness similar to the verification
+ * defined in RFC2313 section 10.2 except that block type 0 is not accepted,
+ * as in RFC2437. RFC2437 section 9.2 doesn't define any operation to
+ * retrieve the DigestInfo from a signature, instead the user is expected
+ * to call the sign operation to generate the expected signature and compare
+ * signatures instead of the message-digests.
+ */
+static int pkcs1pad_verify(struct akcipher_request *req)
+{
+ struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
+ struct pkcs1pad_ctx *ctx = akcipher_tfm_ctx(tfm);
+ struct pkcs1pad_request *req_ctx = akcipher_request_ctx(req);
+ int err;
+
+ if (!ctx->key_size || req->src_len != ctx->key_size)
+ return -EINVAL;
+
+ if (ctx->key_size > PAGE_SIZE)
+ return -ENOTSUPP;
+
+ /* Reuse input buffer, output to a new buffer */
+ req_ctx->child_req.src = req->src;
+ req_ctx->child_req.src_len = req->src_len;
+ req_ctx->child_req.dst = req_ctx->out_sg;
+ req_ctx->child_req.dst_len = ctx->key_size - 1;
+
+ req_ctx->out_buf = kmalloc(ctx->key_size - 1,
+ (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
+ GFP_KERNEL : GFP_ATOMIC);
+ if (!req_ctx->out_buf)
+ return -ENOMEM;
+
+ pkcs1pad_sg_set_buf(req_ctx->out_sg, req_ctx->out_buf,
+ ctx->key_size - 1, NULL);
+
+ akcipher_request_set_tfm(&req_ctx->child_req, ctx->child);
+ akcipher_request_set_callback(&req_ctx->child_req, req->base.flags,
+ pkcs1pad_verify_complete_cb, req);
+
+ err = crypto_akcipher_verify(&req_ctx->child_req);
+ if (err != -EINPROGRESS &&
+ (err != -EBUSY ||
+ !(req->base.flags & CRYPTO_TFM_REQ_MAY_BACKLOG)))
+ return pkcs1pad_verify_complete(req, err);
+
+ return err;
+}
+
+static int pkcs1pad_init_tfm(struct crypto_akcipher *tfm)
+{
+ struct akcipher_instance *inst = akcipher_alg_instance(tfm);
+ struct pkcs1pad_ctx *ctx = akcipher_tfm_ctx(tfm);
+ struct crypto_akcipher *child_tfm;
+
+ child_tfm = crypto_spawn_akcipher(akcipher_instance_ctx(inst));
+ if (IS_ERR(child_tfm))
+ return PTR_ERR(child_tfm);
+
+ ctx->child = child_tfm;
+
+ return 0;
+}
+
+static void pkcs1pad_exit_tfm(struct crypto_akcipher *tfm)
+{
+ struct pkcs1pad_ctx *ctx = akcipher_tfm_ctx(tfm);
+
+ crypto_free_akcipher(ctx->child);
+}
+
+static void pkcs1pad_free(struct akcipher_instance *inst)
+{
+ struct crypto_akcipher_spawn *spawn = akcipher_instance_ctx(inst);
+
+ crypto_drop_akcipher(spawn);
+
+ kfree(inst);
+}
+
+static int pkcs1pad_create(struct crypto_template *tmpl, struct rtattr **tb)
+{
+ struct crypto_attr_type *algt;
+ struct akcipher_instance *inst;
+ struct crypto_akcipher_spawn *spawn;
+ struct akcipher_alg *rsa_alg;
+ const char *rsa_alg_name;
+ int err;
+
+ algt = crypto_get_attr_type(tb);
+ if (IS_ERR(algt))
+ return PTR_ERR(algt);
+
+ if ((algt->type ^ CRYPTO_ALG_TYPE_AKCIPHER) & algt->mask)
+ return -EINVAL;
+
+ rsa_alg_name = crypto_attr_alg_name(tb[1]);
+ if (IS_ERR(rsa_alg_name))
+ return PTR_ERR(rsa_alg_name);
+
+ inst = kzalloc(sizeof(*inst) + sizeof(*spawn), GFP_KERNEL);
+ if (!inst)
+ return -ENOMEM;
+
+ spawn = akcipher_instance_ctx(inst);
+ crypto_set_spawn(&spawn->base, akcipher_crypto_instance(inst));
+ err = crypto_grab_akcipher(spawn, rsa_alg_name, 0,
+ crypto_requires_sync(algt->type, algt->mask));
+ if (err)
+ goto out_free_inst;
+
+ rsa_alg = crypto_spawn_akcipher_alg(spawn);
+
+ err = -ENAMETOOLONG;
+ if (snprintf(inst->alg.base.cra_name,
+ CRYPTO_MAX_ALG_NAME, "pkcs1pad(%s)",
+ rsa_alg->base.cra_name) >=
+ CRYPTO_MAX_ALG_NAME ||
+ snprintf(inst->alg.base.cra_driver_name,
+ CRYPTO_MAX_ALG_NAME, "pkcs1pad(%s)",
+ rsa_alg->base.cra_driver_name) >=
+ CRYPTO_MAX_ALG_NAME)
+ goto out_drop_alg;
+
+ inst->alg.base.cra_flags = rsa_alg->base.cra_flags & CRYPTO_ALG_ASYNC;
+ inst->alg.base.cra_priority = rsa_alg->base.cra_priority;
+ inst->alg.base.cra_ctxsize = sizeof(struct pkcs1pad_ctx);
+
+ inst->alg.init = pkcs1pad_init_tfm;
+ inst->alg.exit = pkcs1pad_exit_tfm;
+
+ inst->alg.encrypt = pkcs1pad_encrypt;
+ inst->alg.decrypt = pkcs1pad_decrypt;
+ inst->alg.sign = pkcs1pad_sign;
+ inst->alg.verify = pkcs1pad_verify;
+ inst->alg.set_pub_key = pkcs1pad_set_pub_key;
+ inst->alg.set_priv_key = pkcs1pad_set_priv_key;
+ inst->alg.max_size = pkcs1pad_get_max_size;
+ inst->alg.reqsize = sizeof(struct pkcs1pad_request) + rsa_alg->reqsize;
+
+ inst->free = pkcs1pad_free;
+
+ err = akcipher_register_instance(tmpl, inst);
+ if (err)
+ goto out_drop_alg;
+
+ return 0;
+
+out_drop_alg:
+ crypto_drop_akcipher(spawn);
+out_free_inst:
+ kfree(inst);
+ return err;
+}
+
+struct crypto_template rsa_pkcs1pad_tmpl = {
+ .name = "pkcs1pad",
+ .create = pkcs1pad_create,
+ .module = THIS_MODULE,
+};
diff --git a/crypto/rsa.c b/crypto/rsa.c
index 1093e041db03..77d737f52147 100644
--- a/crypto/rsa.c
+++ b/crypto/rsa.c
@@ -13,6 +13,7 @@
#include <crypto/internal/rsa.h>
#include <crypto/internal/akcipher.h>
#include <crypto/akcipher.h>
+#include <crypto/algapi.h>
/*
* RSAEP function [RFC3447 sec 5.1.1]
@@ -91,12 +92,6 @@ static int rsa_enc(struct akcipher_request *req)
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;
- }
-
ret = -ENOMEM;
m = mpi_read_raw_from_sgl(req->src, req->src_len);
if (!m)
@@ -136,12 +131,6 @@ static int rsa_dec(struct akcipher_request *req)
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;
- }
-
ret = -ENOMEM;
c = mpi_read_raw_from_sgl(req->src, req->src_len);
if (!c)
@@ -180,12 +169,6 @@ static int rsa_sign(struct akcipher_request *req)
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;
- }
-
ret = -ENOMEM;
m = mpi_read_raw_from_sgl(req->src, req->src_len);
if (!m)
@@ -225,12 +208,6 @@ static int rsa_verify(struct akcipher_request *req)
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;
- }
-
ret = -ENOMEM;
s = mpi_read_raw_from_sgl(req->src, req->src_len);
if (!s) {
@@ -339,11 +316,24 @@ static struct akcipher_alg rsa = {
static int rsa_init(void)
{
- return crypto_register_akcipher(&rsa);
+ int err;
+
+ err = crypto_register_akcipher(&rsa);
+ if (err)
+ return err;
+
+ err = crypto_register_template(&rsa_pkcs1pad_tmpl);
+ if (err) {
+ crypto_unregister_akcipher(&rsa);
+ return err;
+ }
+
+ return 0;
}
static void rsa_exit(void)
{
+ crypto_unregister_template(&rsa_pkcs1pad_tmpl);
crypto_unregister_akcipher(&rsa);
}
diff --git a/crypto/sha1_generic.c b/crypto/sha1_generic.c
index 39e3acc438d9..6877cbb9105f 100644
--- a/crypto/sha1_generic.c
+++ b/crypto/sha1_generic.c
@@ -26,6 +26,13 @@
#include <crypto/sha1_base.h>
#include <asm/byteorder.h>
+const u8 sha1_zero_message_hash[SHA1_DIGEST_SIZE] = {
+ 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d,
+ 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90,
+ 0xaf, 0xd8, 0x07, 0x09
+};
+EXPORT_SYMBOL_GPL(sha1_zero_message_hash);
+
static void sha1_generic_block_fn(struct sha1_state *sst, u8 const *src,
int blocks)
{
diff --git a/crypto/sha256_generic.c b/crypto/sha256_generic.c
index 78431163ed3c..8f9c47e1a96e 100644
--- a/crypto/sha256_generic.c
+++ b/crypto/sha256_generic.c
@@ -27,6 +27,22 @@
#include <asm/byteorder.h>
#include <asm/unaligned.h>
+const u8 sha224_zero_message_hash[SHA224_DIGEST_SIZE] = {
+ 0xd1, 0x4a, 0x02, 0x8c, 0x2a, 0x3a, 0x2b, 0xc9, 0x47,
+ 0x61, 0x02, 0xbb, 0x28, 0x82, 0x34, 0xc4, 0x15, 0xa2,
+ 0xb0, 0x1f, 0x82, 0x8e, 0xa6, 0x2a, 0xc5, 0xb3, 0xe4,
+ 0x2f
+};
+EXPORT_SYMBOL_GPL(sha224_zero_message_hash);
+
+const u8 sha256_zero_message_hash[SHA256_DIGEST_SIZE] = {
+ 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
+ 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
+ 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
+ 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55
+};
+EXPORT_SYMBOL_GPL(sha256_zero_message_hash);
+
static inline u32 Ch(u32 x, u32 y, u32 z)
{
return z ^ (x & (y ^ z));
diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c
index 46a4a757d478..270bc4b82bd9 100644
--- a/crypto/tcrypt.c
+++ b/crypto/tcrypt.c
@@ -1789,7 +1789,7 @@ static int do_test(const char *alg, u32 type, u32 mask, int m)
test_aead_speed("rfc4106(gcm(aes))", ENCRYPT, sec,
NULL, 0, 16, 16, aead_speed_template_20);
test_aead_speed("gcm(aes)", ENCRYPT, sec,
- NULL, 0, 16, 8, aead_speed_template_20);
+ NULL, 0, 16, 8, speed_template_16_24_32);
break;
case 212:
diff --git a/drivers/Makefile b/drivers/Makefile
index 73d039156ea7..8f5d076baeb0 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_FB_I810) += video/fbdev/i810/
obj-$(CONFIG_FB_INTEL) += video/fbdev/intelfb/
obj-$(CONFIG_PARPORT) += parport/
+obj-$(CONFIG_NVM) += lightnvm/
obj-y += base/ block/ misc/ mfd/ nfc/
obj-$(CONFIG_LIBNVDIMM) += nvdimm/
obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf/
@@ -70,7 +71,6 @@ obj-$(CONFIG_NUBUS) += nubus/
obj-y += macintosh/
obj-$(CONFIG_IDE) += ide/
obj-$(CONFIG_SCSI) += scsi/
-obj-$(CONFIG_NVM) += lightnvm/
obj-y += nvme/
obj-$(CONFIG_ATA) += ata/
obj-$(CONFIG_TARGET_CORE) += target/
@@ -106,7 +106,7 @@ obj-y += i2c/ media/
obj-$(CONFIG_PPS) += pps/
obj-$(CONFIG_PTP_1588_CLOCK) += ptp/
obj-$(CONFIG_W1) += w1/
-obj-$(CONFIG_POWER_SUPPLY) += power/
+obj-y += power/
obj-$(CONFIG_HWMON) += hwmon/
obj-$(CONFIG_THERMAL) += thermal/
obj-$(CONFIG_WATCHDOG) += watchdog/
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 25dbb76c02cc..82b96ee8624c 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -58,14 +58,25 @@ config ACPI_CCA_REQUIRED
bool
config ACPI_DEBUGGER
- bool "In-kernel debugger (EXPERIMENTAL)"
+ bool "AML debugger interface"
select ACPI_DEBUG
help
- Enable in-kernel debugging facilities: statistics, internal
- object dump, single step control method execution.
+ Enable in-kernel debugging of AML facilities: statistics,
+ internal object dump, single step control method execution.
This is still under development, currently enabling this only
results in the compilation of the ACPICA debugger files.
+if ACPI_DEBUGGER
+
+config ACPI_DEBUGGER_USER
+ tristate "Userspace debugger accessiblity"
+ depends on DEBUG_FS
+ help
+ Export /sys/kernel/debug/acpi/acpidbg for userspace utilities
+ to access the debugger functionalities.
+
+endif
+
config ACPI_SLEEP
bool
depends on SUSPEND || HIBERNATION
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 675eaf337178..cb648a49543a 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -8,13 +8,13 @@ ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT
#
# ACPI Boot-Time Table Parsing
#
-obj-y += tables.o
+obj-$(CONFIG_ACPI) += tables.o
obj-$(CONFIG_X86) += blacklist.o
#
# ACPI Core Subsystem (Interpreter)
#
-obj-y += acpi.o \
+obj-$(CONFIG_ACPI) += acpi.o \
acpica/
# All the builtin files are in the "acpi." module_param namespace.
@@ -66,10 +66,10 @@ obj-$(CONFIG_ACPI_FAN) += fan.o
obj-$(CONFIG_ACPI_VIDEO) += video.o
obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o
obj-$(CONFIG_ACPI_PROCESSOR) += processor.o
-obj-y += container.o
+obj-$(CONFIG_ACPI) += container.o
obj-$(CONFIG_ACPI_THERMAL) += thermal.o
obj-$(CONFIG_ACPI_NFIT) += nfit.o
-obj-y += acpi_memhotplug.o
+obj-$(CONFIG_ACPI) += acpi_memhotplug.o
obj-$(CONFIG_ACPI_HOTPLUG_IOAPIC) += ioapic.o
obj-$(CONFIG_ACPI_BATTERY) += battery.o
obj-$(CONFIG_ACPI_SBS) += sbshc.o
@@ -79,6 +79,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o
obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
obj-$(CONFIG_ACPI_BGRT) += bgrt.o
obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o
+obj-$(CONFIG_ACPI_DEBUGGER_USER) += acpi_dbg.o
# processor has its own "processor." module_param namespace
processor-y := processor_driver.o
diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c
index a450e7af877c..d507cf6deda0 100644
--- a/drivers/acpi/acpi_apd.c
+++ b/drivers/acpi/acpi_apd.c
@@ -51,7 +51,7 @@ struct apd_private_data {
const struct apd_device_desc *dev_desc;
};
-#ifdef CONFIG_X86_AMD_PLATFORM_DEVICE
+#if defined(CONFIG_X86_AMD_PLATFORM_DEVICE) || defined(CONFIG_ARM64)
#define APD_ADDR(desc) ((unsigned long)&desc)
static int acpi_apd_setup(struct apd_private_data *pdata)
@@ -71,6 +71,7 @@ static int acpi_apd_setup(struct apd_private_data *pdata)
return 0;
}
+#ifdef CONFIG_X86_AMD_PLATFORM_DEVICE
static struct apd_device_desc cz_i2c_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 133000000,
@@ -80,6 +81,14 @@ static struct apd_device_desc cz_uart_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 48000000,
};
+#endif
+
+#ifdef CONFIG_ARM64
+static struct apd_device_desc xgene_i2c_desc = {
+ .setup = acpi_apd_setup,
+ .fixed_clk_rate = 100000000,
+};
+#endif
#else
@@ -132,9 +141,14 @@ static int acpi_apd_create_device(struct acpi_device *adev,
static const struct acpi_device_id acpi_apd_device_ids[] = {
/* Generic apd devices */
+#ifdef CONFIG_X86_AMD_PLATFORM_DEVICE
{ "AMD0010", APD_ADDR(cz_i2c_desc) },
{ "AMD0020", APD_ADDR(cz_uart_desc) },
{ "AMD0030", },
+#endif
+#ifdef CONFIG_ARM64
+ { "APMC0D0F", APD_ADDR(xgene_i2c_desc) },
+#endif
{ }
};
diff --git a/drivers/acpi/acpi_dbg.c b/drivers/acpi/acpi_dbg.c
new file mode 100644
index 000000000000..15e4604efba7
--- /dev/null
+++ b/drivers/acpi/acpi_dbg.c
@@ -0,0 +1,804 @@
+/*
+ * ACPI AML interfacing support
+ *
+ * Copyright (C) 2015, Intel Corporation
+ * Authors: Lv Zheng <lv.zheng@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.
+ */
+
+/* #define DEBUG */
+#define pr_fmt(fmt) "ACPI : AML: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/proc_fs.h>
+#include <linux/debugfs.h>
+#include <linux/circ_buf.h>
+#include <linux/acpi.h>
+#include "internal.h"
+
+#define ACPI_AML_BUF_ALIGN (sizeof (acpi_size))
+#define ACPI_AML_BUF_SIZE PAGE_SIZE
+
+#define circ_count(circ) \
+ (CIRC_CNT((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_count_to_end(circ) \
+ (CIRC_CNT_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space(circ) \
+ (CIRC_SPACE((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space_to_end(circ) \
+ (CIRC_SPACE_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+
+#define ACPI_AML_OPENED 0x0001
+#define ACPI_AML_CLOSED 0x0002
+#define ACPI_AML_IN_USER 0x0004 /* user space is writing cmd */
+#define ACPI_AML_IN_KERN 0x0008 /* kernel space is reading cmd */
+#define ACPI_AML_OUT_USER 0x0010 /* user space is reading log */
+#define ACPI_AML_OUT_KERN 0x0020 /* kernel space is writing log */
+#define ACPI_AML_USER (ACPI_AML_IN_USER | ACPI_AML_OUT_USER)
+#define ACPI_AML_KERN (ACPI_AML_IN_KERN | ACPI_AML_OUT_KERN)
+#define ACPI_AML_BUSY (ACPI_AML_USER | ACPI_AML_KERN)
+#define ACPI_AML_OPEN (ACPI_AML_OPENED | ACPI_AML_CLOSED)
+
+struct acpi_aml_io {
+ wait_queue_head_t wait;
+ unsigned long flags;
+ unsigned long users;
+ struct mutex lock;
+ struct task_struct *thread;
+ char out_buf[ACPI_AML_BUF_SIZE] __aligned(ACPI_AML_BUF_ALIGN);
+ struct circ_buf out_crc;
+ char in_buf[ACPI_AML_BUF_SIZE] __aligned(ACPI_AML_BUF_ALIGN);
+ struct circ_buf in_crc;
+ acpi_osd_exec_callback function;
+ void *context;
+ unsigned long usages;
+};
+
+static struct acpi_aml_io acpi_aml_io;
+static bool acpi_aml_initialized;
+static struct file *acpi_aml_active_reader;
+static struct dentry *acpi_aml_dentry;
+
+static inline bool __acpi_aml_running(void)
+{
+ return acpi_aml_io.thread ? true : false;
+}
+
+static inline bool __acpi_aml_access_ok(unsigned long flag)
+{
+ /*
+ * The debugger interface is in opened state (OPENED && !CLOSED),
+ * then it is allowed to access the debugger buffers from either
+ * user space or the kernel space.
+ * In addition, for the kernel space, only the debugger thread
+ * (thread ID matched) is allowed to access.
+ */
+ if (!(acpi_aml_io.flags & ACPI_AML_OPENED) ||
+ (acpi_aml_io.flags & ACPI_AML_CLOSED) ||
+ !__acpi_aml_running())
+ return false;
+ if ((flag & ACPI_AML_KERN) &&
+ current != acpi_aml_io.thread)
+ return false;
+ return true;
+}
+
+static inline bool __acpi_aml_readable(struct circ_buf *circ, unsigned long flag)
+{
+ /*
+ * Another read is not in progress and there is data in buffer
+ * available for read.
+ */
+ if (!(acpi_aml_io.flags & flag) && circ_count(circ))
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_writable(struct circ_buf *circ, unsigned long flag)
+{
+ /*
+ * Another write is not in progress and there is buffer space
+ * available for write.
+ */
+ if (!(acpi_aml_io.flags & flag) && circ_space(circ))
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_busy(void)
+{
+ if (acpi_aml_io.flags & ACPI_AML_BUSY)
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_opened(void)
+{
+ if (acpi_aml_io.flags & ACPI_AML_OPEN)
+ return true;
+ return false;
+}
+
+static inline bool __acpi_aml_used(void)
+{
+ return acpi_aml_io.usages ? true : false;
+}
+
+static inline bool acpi_aml_running(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = __acpi_aml_running();
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_busy(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = __acpi_aml_busy();
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_used(void)
+{
+ bool ret;
+
+ /*
+ * The usage count is prepared to avoid race conditions between the
+ * starts and the stops of the debugger thread.
+ */
+ mutex_lock(&acpi_aml_io.lock);
+ ret = __acpi_aml_used();
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_kern_readable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_IN_KERN) ||
+ __acpi_aml_readable(&acpi_aml_io.in_crc, ACPI_AML_IN_KERN);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_kern_writable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_OUT_KERN) ||
+ __acpi_aml_writable(&acpi_aml_io.out_crc, ACPI_AML_OUT_KERN);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_user_readable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_OUT_USER) ||
+ __acpi_aml_readable(&acpi_aml_io.out_crc, ACPI_AML_OUT_USER);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static bool acpi_aml_user_writable(void)
+{
+ bool ret;
+
+ mutex_lock(&acpi_aml_io.lock);
+ ret = !__acpi_aml_access_ok(ACPI_AML_IN_USER) ||
+ __acpi_aml_writable(&acpi_aml_io.in_crc, ACPI_AML_IN_USER);
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static int acpi_aml_lock_write(struct circ_buf *circ, unsigned long flag)
+{
+ int ret = 0;
+
+ mutex_lock(&acpi_aml_io.lock);
+ if (!__acpi_aml_access_ok(flag)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (!__acpi_aml_writable(circ, flag)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ acpi_aml_io.flags |= flag;
+out:
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static int acpi_aml_lock_read(struct circ_buf *circ, unsigned long flag)
+{
+ int ret = 0;
+
+ mutex_lock(&acpi_aml_io.lock);
+ if (!__acpi_aml_access_ok(flag)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (!__acpi_aml_readable(circ, flag)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ acpi_aml_io.flags |= flag;
+out:
+ mutex_unlock(&acpi_aml_io.lock);
+ return ret;
+}
+
+static void acpi_aml_unlock_fifo(unsigned long flag, bool wakeup)
+{
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.flags &= ~flag;
+ if (wakeup)
+ wake_up_interruptible(&acpi_aml_io.wait);
+ mutex_unlock(&acpi_aml_io.lock);
+}
+
+static int acpi_aml_write_kern(const char *buf, int len)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.out_crc;
+ int n;
+ char *p;
+
+ ret = acpi_aml_lock_write(crc, ACPI_AML_OUT_KERN);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ /* sync tail before inserting logs */
+ smp_mb();
+ p = &crc->buf[crc->head];
+ n = min(len, circ_space_to_end(crc));
+ memcpy(p, buf, n);
+ /* sync head after inserting logs */
+ smp_wmb();
+ crc->head = (crc->head + n) & (ACPI_AML_BUF_SIZE - 1);
+ acpi_aml_unlock_fifo(ACPI_AML_OUT_KERN, true);
+ return n;
+}
+
+static int acpi_aml_readb_kern(void)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.in_crc;
+ char *p;
+
+ ret = acpi_aml_lock_read(crc, ACPI_AML_IN_KERN);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ /* sync head before removing cmds */
+ smp_rmb();
+ p = &crc->buf[crc->tail];
+ ret = (int)*p;
+ /* sync tail before inserting cmds */
+ smp_mb();
+ crc->tail = (crc->tail + 1) & (ACPI_AML_BUF_SIZE - 1);
+ acpi_aml_unlock_fifo(ACPI_AML_IN_KERN, true);
+ return ret;
+}
+
+/*
+ * acpi_aml_write_log() - Capture debugger output
+ * @msg: the debugger output
+ *
+ * This function should be used to implement acpi_os_printf() to filter out
+ * the debugger output and store the output into the debugger interface
+ * buffer. Return the size of stored logs or errno.
+ */
+static ssize_t acpi_aml_write_log(const char *msg)
+{
+ int ret = 0;
+ int count = 0, size = 0;
+
+ if (!acpi_aml_initialized)
+ return -ENODEV;
+ if (msg)
+ count = strlen(msg);
+ while (count > 0) {
+again:
+ ret = acpi_aml_write_kern(msg + size, count);
+ if (ret == -EAGAIN) {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_kern_writable());
+ /*
+ * We need to retry when the condition
+ * becomes true.
+ */
+ if (ret == 0)
+ goto again;
+ break;
+ }
+ if (IS_ERR_VALUE(ret))
+ break;
+ size += ret;
+ count -= ret;
+ }
+ return size > 0 ? size : ret;
+}
+
+/*
+ * acpi_aml_read_cmd() - Capture debugger input
+ * @msg: the debugger input
+ * @size: the size of the debugger input
+ *
+ * This function should be used to implement acpi_os_get_line() to capture
+ * the debugger input commands and store the input commands into the
+ * debugger interface buffer. Return the size of stored commands or errno.
+ */
+static ssize_t acpi_aml_read_cmd(char *msg, size_t count)
+{
+ int ret = 0;
+ int size = 0;
+
+ /*
+ * This is ensured by the running fact of the debugger thread
+ * unless a bug is introduced.
+ */
+ BUG_ON(!acpi_aml_initialized);
+ while (count > 0) {
+again:
+ /*
+ * Check each input byte to find the end of the command.
+ */
+ ret = acpi_aml_readb_kern();
+ if (ret == -EAGAIN) {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_kern_readable());
+ /*
+ * We need to retry when the condition becomes
+ * true.
+ */
+ if (ret == 0)
+ goto again;
+ }
+ if (IS_ERR_VALUE(ret))
+ break;
+ *(msg + size) = (char)ret;
+ size++;
+ count--;
+ if (ret == '\n') {
+ /*
+ * acpi_os_get_line() requires a zero terminated command
+ * string.
+ */
+ *(msg + size - 1) = '\0';
+ break;
+ }
+ }
+ return size > 0 ? size : ret;
+}
+
+static int acpi_aml_thread(void *unsed)
+{
+ acpi_osd_exec_callback function = NULL;
+ void *context;
+
+ mutex_lock(&acpi_aml_io.lock);
+ if (acpi_aml_io.function) {
+ acpi_aml_io.usages++;
+ function = acpi_aml_io.function;
+ context = acpi_aml_io.context;
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+
+ if (function)
+ function(context);
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.usages--;
+ if (!__acpi_aml_used()) {
+ acpi_aml_io.thread = NULL;
+ wake_up(&acpi_aml_io.wait);
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+
+ return 0;
+}
+
+/*
+ * acpi_aml_create_thread() - Create AML debugger thread
+ * @function: the debugger thread callback
+ * @context: the context to be passed to the debugger thread
+ *
+ * This function should be used to implement acpi_os_execute() which is
+ * used by the ACPICA debugger to create the debugger thread.
+ */
+static int acpi_aml_create_thread(acpi_osd_exec_callback function, void *context)
+{
+ struct task_struct *t;
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.function = function;
+ acpi_aml_io.context = context;
+ mutex_unlock(&acpi_aml_io.lock);
+
+ t = kthread_create(acpi_aml_thread, NULL, "aml");
+ if (IS_ERR(t)) {
+ pr_err("Failed to create AML debugger thread.\n");
+ return PTR_ERR(t);
+ }
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.thread = t;
+ acpi_set_debugger_thread_id((acpi_thread_id)(unsigned long)t);
+ wake_up_process(t);
+ mutex_unlock(&acpi_aml_io.lock);
+ return 0;
+}
+
+static int acpi_aml_wait_command_ready(bool single_step,
+ char *buffer, size_t length)
+{
+ acpi_status status;
+
+ if (single_step)
+ acpi_os_printf("\n%1c ", ACPI_DEBUGGER_EXECUTE_PROMPT);
+ else
+ acpi_os_printf("\n%1c ", ACPI_DEBUGGER_COMMAND_PROMPT);
+
+ status = acpi_os_get_line(buffer, length, NULL);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+ return 0;
+}
+
+static int acpi_aml_notify_command_complete(void)
+{
+ return 0;
+}
+
+static int acpi_aml_open(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+ acpi_status status;
+
+ mutex_lock(&acpi_aml_io.lock);
+ /*
+ * The debugger interface is being closed, no new user is allowed
+ * during this period.
+ */
+ if (acpi_aml_io.flags & ACPI_AML_CLOSED) {
+ ret = -EBUSY;
+ goto err_lock;
+ }
+ if ((file->f_flags & O_ACCMODE) != O_WRONLY) {
+ /*
+ * Only one reader is allowed to initiate the debugger
+ * thread.
+ */
+ if (acpi_aml_active_reader) {
+ ret = -EBUSY;
+ goto err_lock;
+ } else {
+ pr_debug("Opening debugger reader.\n");
+ acpi_aml_active_reader = file;
+ }
+ } else {
+ /*
+ * No writer is allowed unless the debugger thread is
+ * ready.
+ */
+ if (!(acpi_aml_io.flags & ACPI_AML_OPENED)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ }
+ if (acpi_aml_active_reader == file) {
+ pr_debug("Opening debugger interface.\n");
+ mutex_unlock(&acpi_aml_io.lock);
+
+ pr_debug("Initializing debugger thread.\n");
+ status = acpi_initialize_debugger();
+ if (ACPI_FAILURE(status)) {
+ pr_err("Failed to initialize debugger.\n");
+ ret = -EINVAL;
+ goto err_exit;
+ }
+ pr_debug("Debugger thread initialized.\n");
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.flags |= ACPI_AML_OPENED;
+ acpi_aml_io.out_crc.head = acpi_aml_io.out_crc.tail = 0;
+ acpi_aml_io.in_crc.head = acpi_aml_io.in_crc.tail = 0;
+ pr_debug("Debugger interface opened.\n");
+ }
+ acpi_aml_io.users++;
+err_lock:
+ if (IS_ERR_VALUE(ret)) {
+ if (acpi_aml_active_reader == file)
+ acpi_aml_active_reader = NULL;
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+err_exit:
+ return ret;
+}
+
+static int acpi_aml_release(struct inode *inode, struct file *file)
+{
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.users--;
+ if (file == acpi_aml_active_reader) {
+ pr_debug("Closing debugger reader.\n");
+ acpi_aml_active_reader = NULL;
+
+ pr_debug("Closing debugger interface.\n");
+ acpi_aml_io.flags |= ACPI_AML_CLOSED;
+
+ /*
+ * Wake up all user space/kernel space blocked
+ * readers/writers.
+ */
+ wake_up_interruptible(&acpi_aml_io.wait);
+ mutex_unlock(&acpi_aml_io.lock);
+ /*
+ * Wait all user space/kernel space readers/writers to
+ * stop so that ACPICA command loop of the debugger thread
+ * should fail all its command line reads after this point.
+ */
+ wait_event(acpi_aml_io.wait, !acpi_aml_busy());
+
+ /*
+ * Then we try to terminate the debugger thread if it is
+ * not terminated.
+ */
+ pr_debug("Terminating debugger thread.\n");
+ acpi_terminate_debugger();
+ wait_event(acpi_aml_io.wait, !acpi_aml_used());
+ pr_debug("Debugger thread terminated.\n");
+
+ mutex_lock(&acpi_aml_io.lock);
+ acpi_aml_io.flags &= ~ACPI_AML_OPENED;
+ }
+ if (acpi_aml_io.users == 0) {
+ pr_debug("Debugger interface closed.\n");
+ acpi_aml_io.flags &= ~ACPI_AML_CLOSED;
+ }
+ mutex_unlock(&acpi_aml_io.lock);
+ return 0;
+}
+
+static int acpi_aml_read_user(char __user *buf, int len)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.out_crc;
+ int n;
+ char *p;
+
+ ret = acpi_aml_lock_read(crc, ACPI_AML_OUT_USER);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ /* sync head before removing logs */
+ smp_rmb();
+ p = &crc->buf[crc->tail];
+ n = min(len, circ_count_to_end(crc));
+ if (copy_to_user(buf, p, n)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ /* sync tail after removing logs */
+ smp_mb();
+ crc->tail = (crc->tail + n) & (ACPI_AML_BUF_SIZE - 1);
+ ret = n;
+out:
+ acpi_aml_unlock_fifo(ACPI_AML_OUT_USER, !IS_ERR_VALUE(ret));
+ return ret;
+}
+
+static ssize_t acpi_aml_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret = 0;
+ int size = 0;
+
+ if (!count)
+ return 0;
+ if (!access_ok(VERIFY_WRITE, buf, count))
+ return -EFAULT;
+
+ while (count > 0) {
+again:
+ ret = acpi_aml_read_user(buf + size, count);
+ if (ret == -EAGAIN) {
+ if (file->f_flags & O_NONBLOCK)
+ break;
+ else {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_user_readable());
+ /*
+ * We need to retry when the condition
+ * becomes true.
+ */
+ if (ret == 0)
+ goto again;
+ }
+ }
+ if (IS_ERR_VALUE(ret)) {
+ if (!acpi_aml_running())
+ ret = 0;
+ break;
+ }
+ if (ret) {
+ size += ret;
+ count -= ret;
+ *ppos += ret;
+ break;
+ }
+ }
+ return size > 0 ? size : ret;
+}
+
+static int acpi_aml_write_user(const char __user *buf, int len)
+{
+ int ret;
+ struct circ_buf *crc = &acpi_aml_io.in_crc;
+ int n;
+ char *p;
+
+ ret = acpi_aml_lock_write(crc, ACPI_AML_IN_USER);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ /* sync tail before inserting cmds */
+ smp_mb();
+ p = &crc->buf[crc->head];
+ n = min(len, circ_space_to_end(crc));
+ if (copy_from_user(p, buf, n)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ /* sync head after inserting cmds */
+ smp_wmb();
+ crc->head = (crc->head + n) & (ACPI_AML_BUF_SIZE - 1);
+ ret = n;
+out:
+ acpi_aml_unlock_fifo(ACPI_AML_IN_USER, !IS_ERR_VALUE(ret));
+ return n;
+}
+
+static ssize_t acpi_aml_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret = 0;
+ int size = 0;
+
+ if (!count)
+ return 0;
+ if (!access_ok(VERIFY_READ, buf, count))
+ return -EFAULT;
+
+ while (count > 0) {
+again:
+ ret = acpi_aml_write_user(buf + size, count);
+ if (ret == -EAGAIN) {
+ if (file->f_flags & O_NONBLOCK)
+ break;
+ else {
+ ret = wait_event_interruptible(acpi_aml_io.wait,
+ acpi_aml_user_writable());
+ /*
+ * We need to retry when the condition
+ * becomes true.
+ */
+ if (ret == 0)
+ goto again;
+ }
+ }
+ if (IS_ERR_VALUE(ret)) {
+ if (!acpi_aml_running())
+ ret = 0;
+ break;
+ }
+ if (ret) {
+ size += ret;
+ count -= ret;
+ *ppos += ret;
+ }
+ }
+ return size > 0 ? size : ret;
+}
+
+static unsigned int acpi_aml_poll(struct file *file, poll_table *wait)
+{
+ int masks = 0;
+
+ poll_wait(file, &acpi_aml_io.wait, wait);
+ if (acpi_aml_user_readable())
+ masks |= POLLIN | POLLRDNORM;
+ if (acpi_aml_user_writable())
+ masks |= POLLOUT | POLLWRNORM;
+
+ return masks;
+}
+
+static const struct file_operations acpi_aml_operations = {
+ .read = acpi_aml_read,
+ .write = acpi_aml_write,
+ .poll = acpi_aml_poll,
+ .open = acpi_aml_open,
+ .release = acpi_aml_release,
+ .llseek = generic_file_llseek,
+};
+
+static const struct acpi_debugger_ops acpi_aml_debugger = {
+ .create_thread = acpi_aml_create_thread,
+ .read_cmd = acpi_aml_read_cmd,
+ .write_log = acpi_aml_write_log,
+ .wait_command_ready = acpi_aml_wait_command_ready,
+ .notify_command_complete = acpi_aml_notify_command_complete,
+};
+
+int __init acpi_aml_init(void)
+{
+ int ret = 0;
+
+ if (!acpi_debugfs_dir) {
+ ret = -ENOENT;
+ goto err_exit;
+ }
+
+ /* Initialize AML IO interface */
+ mutex_init(&acpi_aml_io.lock);
+ init_waitqueue_head(&acpi_aml_io.wait);
+ acpi_aml_io.out_crc.buf = acpi_aml_io.out_buf;
+ acpi_aml_io.in_crc.buf = acpi_aml_io.in_buf;
+ acpi_aml_dentry = debugfs_create_file("acpidbg",
+ S_IFREG | S_IRUGO | S_IWUSR,
+ acpi_debugfs_dir, NULL,
+ &acpi_aml_operations);
+ if (acpi_aml_dentry == NULL) {
+ ret = -ENODEV;
+ goto err_exit;
+ }
+ ret = acpi_register_debugger(THIS_MODULE, &acpi_aml_debugger);
+ if (ret)
+ goto err_fs;
+ acpi_aml_initialized = true;
+
+err_fs:
+ if (ret) {
+ debugfs_remove(acpi_aml_dentry);
+ acpi_aml_dentry = NULL;
+ }
+err_exit:
+ return ret;
+}
+
+void __exit acpi_aml_exit(void)
+{
+ if (acpi_aml_initialized) {
+ acpi_unregister_debugger(&acpi_aml_debugger);
+ if (acpi_aml_dentry) {
+ debugfs_remove(acpi_aml_dentry);
+ acpi_aml_dentry = NULL;
+ }
+ acpi_aml_initialized = false;
+ }
+}
+
+module_init(acpi_aml_init);
+module_exit(acpi_aml_exit);
+
+MODULE_AUTHOR("Lv Zheng");
+MODULE_DESCRIPTION("ACPI debugger userspace IO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
index f9e0d09f7c66..047281a6ae11 100644
--- a/drivers/acpi/acpi_lpss.c
+++ b/drivers/acpi/acpi_lpss.c
@@ -15,6 +15,7 @@
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/io.h>
+#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/platform_data/clk-lpss.h>
#include <linux/pm_runtime.h>
@@ -26,6 +27,10 @@ ACPI_MODULE_NAME("acpi_lpss");
#ifdef CONFIG_X86_INTEL_LPSS
+#include <asm/cpu_device_id.h>
+#include <asm/iosf_mbi.h>
+#include <asm/pmc_atom.h>
+
#define LPSS_ADDR(desc) ((unsigned long)&desc)
#define LPSS_CLK_SIZE 0x04
@@ -71,7 +76,7 @@ struct lpss_device_desc {
void (*setup)(struct lpss_private_data *pdata);
};
-static struct lpss_device_desc lpss_dma_desc = {
+static const struct lpss_device_desc lpss_dma_desc = {
.flags = LPSS_CLK,
};
@@ -84,6 +89,23 @@ struct lpss_private_data {
u32 prv_reg_ctx[LPSS_PRV_REG_COUNT];
};
+/* LPSS run time quirks */
+static unsigned int lpss_quirks;
+
+/*
+ * LPSS_QUIRK_ALWAYS_POWER_ON: override power state for LPSS DMA device.
+ *
+ * The LPSS DMA controller has neither _PS0 nor _PS3 method. Moreover
+ * it can be powered off automatically whenever the last LPSS device goes down.
+ * In case of no power any access to the DMA controller will hang the system.
+ * The behaviour is reproduced on some HP laptops based on Intel BayTrail as
+ * well as on ASuS T100TA transformer.
+ *
+ * This quirk overrides power state of entire LPSS island to keep DMA powered
+ * on whenever we have at least one other device in use.
+ */
+#define LPSS_QUIRK_ALWAYS_POWER_ON BIT(0)
+
/* UART Component Parameter Register */
#define LPSS_UART_CPR 0xF4
#define LPSS_UART_CPR_AFCE BIT(4)
@@ -196,13 +218,21 @@ static const struct lpss_device_desc bsw_i2c_dev_desc = {
.setup = byt_i2c_setup,
};
-static struct lpss_device_desc bsw_spi_dev_desc = {
+static const struct lpss_device_desc bsw_spi_dev_desc = {
.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX
| LPSS_NO_D3_DELAY,
.prv_offset = 0x400,
.setup = lpss_deassert_reset,
};
+#define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, }
+
+static const struct x86_cpu_id lpss_cpu_ids[] = {
+ ICPU(0x37), /* Valleyview, Bay Trail */
+ ICPU(0x4c), /* Braswell, Cherry Trail */
+ {}
+};
+
#else
#define LPSS_ADDR(desc) (0UL)
@@ -574,6 +604,17 @@ static void acpi_lpss_restore_ctx(struct device *dev,
{
unsigned int i;
+ for (i = 0; i < LPSS_PRV_REG_COUNT; i++) {
+ unsigned long offset = i * sizeof(u32);
+
+ __lpss_reg_write(pdata->prv_reg_ctx[i], pdata, offset);
+ dev_dbg(dev, "restoring 0x%08x to LPSS reg at offset 0x%02lx\n",
+ pdata->prv_reg_ctx[i], offset);
+ }
+}
+
+static void acpi_lpss_d3_to_d0_delay(struct lpss_private_data *pdata)
+{
/*
* The following delay is needed or the subsequent write operations may
* fail. The LPSS devices are actually PCI devices and the PCI spec
@@ -586,14 +627,34 @@ static void acpi_lpss_restore_ctx(struct device *dev,
delay = 0;
msleep(delay);
+}
- for (i = 0; i < LPSS_PRV_REG_COUNT; i++) {
- unsigned long offset = i * sizeof(u32);
+static int acpi_lpss_activate(struct device *dev)
+{
+ struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
+ int ret;
- __lpss_reg_write(pdata->prv_reg_ctx[i], pdata, offset);
- dev_dbg(dev, "restoring 0x%08x to LPSS reg at offset 0x%02lx\n",
- pdata->prv_reg_ctx[i], offset);
- }
+ ret = acpi_dev_runtime_resume(dev);
+ if (ret)
+ return ret;
+
+ acpi_lpss_d3_to_d0_delay(pdata);
+
+ /*
+ * This is called only on ->probe() stage where a device is either in
+ * known state defined by BIOS or most likely powered off. Due to this
+ * we have to deassert reset line to be sure that ->probe() will
+ * recognize the device.
+ */
+ if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
+ lpss_deassert_reset(pdata);
+
+ return 0;
+}
+
+static void acpi_lpss_dismiss(struct device *dev)
+{
+ acpi_dev_runtime_suspend(dev);
}
#ifdef CONFIG_PM_SLEEP
@@ -621,6 +682,8 @@ static int acpi_lpss_resume_early(struct device *dev)
if (ret)
return ret;
+ acpi_lpss_d3_to_d0_delay(pdata);
+
if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
acpi_lpss_restore_ctx(dev, pdata);
@@ -628,6 +691,89 @@ static int acpi_lpss_resume_early(struct device *dev)
}
#endif /* CONFIG_PM_SLEEP */
+/* IOSF SB for LPSS island */
+#define LPSS_IOSF_UNIT_LPIOEP 0xA0
+#define LPSS_IOSF_UNIT_LPIO1 0xAB
+#define LPSS_IOSF_UNIT_LPIO2 0xAC
+
+#define LPSS_IOSF_PMCSR 0x84
+#define LPSS_PMCSR_D0 0
+#define LPSS_PMCSR_D3hot 3
+#define LPSS_PMCSR_Dx_MASK GENMASK(1, 0)
+
+#define LPSS_IOSF_GPIODEF0 0x154
+#define LPSS_GPIODEF0_DMA1_D3 BIT(2)
+#define LPSS_GPIODEF0_DMA2_D3 BIT(3)
+#define LPSS_GPIODEF0_DMA_D3_MASK GENMASK(3, 2)
+
+static DEFINE_MUTEX(lpss_iosf_mutex);
+
+static void lpss_iosf_enter_d3_state(void)
+{
+ u32 value1 = 0;
+ u32 mask1 = LPSS_GPIODEF0_DMA_D3_MASK;
+ u32 value2 = LPSS_PMCSR_D3hot;
+ u32 mask2 = LPSS_PMCSR_Dx_MASK;
+ /*
+ * PMC provides an information about actual status of the LPSS devices.
+ * Here we read the values related to LPSS power island, i.e. LPSS
+ * devices, excluding both LPSS DMA controllers, along with SCC domain.
+ */
+ u32 func_dis, d3_sts_0, pmc_status, pmc_mask = 0xfe000ffe;
+ int ret;
+
+ ret = pmc_atom_read(PMC_FUNC_DIS, &func_dis);
+ if (ret)
+ return;
+
+ mutex_lock(&lpss_iosf_mutex);
+
+ ret = pmc_atom_read(PMC_D3_STS_0, &d3_sts_0);
+ if (ret)
+ goto exit;
+
+ /*
+ * Get the status of entire LPSS power island per device basis.
+ * Shutdown both LPSS DMA controllers if and only if all other devices
+ * are already in D3hot.
+ */
+ pmc_status = (~(d3_sts_0 | func_dis)) & pmc_mask;
+ if (pmc_status)
+ goto exit;
+
+ iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO1, MBI_CFG_WRITE,
+ LPSS_IOSF_PMCSR, value2, mask2);
+
+ iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO2, MBI_CFG_WRITE,
+ LPSS_IOSF_PMCSR, value2, mask2);
+
+ iosf_mbi_modify(LPSS_IOSF_UNIT_LPIOEP, MBI_CR_WRITE,
+ LPSS_IOSF_GPIODEF0, value1, mask1);
+exit:
+ mutex_unlock(&lpss_iosf_mutex);
+}
+
+static void lpss_iosf_exit_d3_state(void)
+{
+ u32 value1 = LPSS_GPIODEF0_DMA1_D3 | LPSS_GPIODEF0_DMA2_D3;
+ u32 mask1 = LPSS_GPIODEF0_DMA_D3_MASK;
+ u32 value2 = LPSS_PMCSR_D0;
+ u32 mask2 = LPSS_PMCSR_Dx_MASK;
+
+ mutex_lock(&lpss_iosf_mutex);
+
+ iosf_mbi_modify(LPSS_IOSF_UNIT_LPIOEP, MBI_CR_WRITE,
+ LPSS_IOSF_GPIODEF0, value1, mask1);
+
+ iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO2, MBI_CFG_WRITE,
+ LPSS_IOSF_PMCSR, value2, mask2);
+
+ iosf_mbi_modify(LPSS_IOSF_UNIT_LPIO1, MBI_CFG_WRITE,
+ LPSS_IOSF_PMCSR, value2, mask2);
+
+ mutex_unlock(&lpss_iosf_mutex);
+}
+
static int acpi_lpss_runtime_suspend(struct device *dev)
{
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
@@ -640,7 +786,17 @@ static int acpi_lpss_runtime_suspend(struct device *dev)
if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
acpi_lpss_save_ctx(dev, pdata);
- return acpi_dev_runtime_suspend(dev);
+ ret = acpi_dev_runtime_suspend(dev);
+
+ /*
+ * This call must be last in the sequence, otherwise PMC will return
+ * wrong status for devices being about to be powered off. See
+ * lpss_iosf_enter_d3_state() for further information.
+ */
+ if (lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available())
+ lpss_iosf_enter_d3_state();
+
+ return ret;
}
static int acpi_lpss_runtime_resume(struct device *dev)
@@ -648,10 +804,19 @@ static int acpi_lpss_runtime_resume(struct device *dev)
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
int ret;
+ /*
+ * This call is kept first to be in symmetry with
+ * acpi_lpss_runtime_suspend() one.
+ */
+ if (lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available())
+ lpss_iosf_exit_d3_state();
+
ret = acpi_dev_runtime_resume(dev);
if (ret)
return ret;
+ acpi_lpss_d3_to_d0_delay(pdata);
+
if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
acpi_lpss_restore_ctx(dev, pdata);
@@ -660,6 +825,10 @@ static int acpi_lpss_runtime_resume(struct device *dev)
#endif /* CONFIG_PM */
static struct dev_pm_domain acpi_lpss_pm_domain = {
+#ifdef CONFIG_PM
+ .activate = acpi_lpss_activate,
+ .dismiss = acpi_lpss_dismiss,
+#endif
.ops = {
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
@@ -705,8 +874,14 @@ static int acpi_lpss_platform_notify(struct notifier_block *nb,
}
switch (action) {
- case BUS_NOTIFY_ADD_DEVICE:
+ case BUS_NOTIFY_BIND_DRIVER:
pdev->dev.pm_domain = &acpi_lpss_pm_domain;
+ break;
+ case BUS_NOTIFY_DRIVER_NOT_BOUND:
+ case BUS_NOTIFY_UNBOUND_DRIVER:
+ pdev->dev.pm_domain = NULL;
+ break;
+ case BUS_NOTIFY_ADD_DEVICE:
if (pdata->dev_desc->flags & LPSS_LTR)
return sysfs_create_group(&pdev->dev.kobj,
&lpss_attr_group);
@@ -714,7 +889,6 @@ static int acpi_lpss_platform_notify(struct notifier_block *nb,
case BUS_NOTIFY_DEL_DEVICE:
if (pdata->dev_desc->flags & LPSS_LTR)
sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group);
- pdev->dev.pm_domain = NULL;
break;
default:
break;
@@ -754,10 +928,19 @@ static struct acpi_scan_handler lpss_handler = {
void __init acpi_lpss_init(void)
{
- if (!lpt_clk_init()) {
- bus_register_notifier(&platform_bus_type, &acpi_lpss_nb);
- acpi_scan_add_handler(&lpss_handler);
- }
+ const struct x86_cpu_id *id;
+ int ret;
+
+ ret = lpt_clk_init();
+ if (ret)
+ return;
+
+ id = x86_match_cpu(lpss_cpu_ids);
+ if (id)
+ lpss_quirks |= LPSS_QUIRK_ALWAYS_POWER_ON;
+
+ bus_register_notifier(&platform_bus_type, &acpi_lpss_nb);
+ acpi_scan_add_handler(&lpss_handler);
}
#else
diff --git a/drivers/acpi/acpi_pnp.c b/drivers/acpi/acpi_pnp.c
index 48fc3ad13a4b..67d97c0090a2 100644
--- a/drivers/acpi/acpi_pnp.c
+++ b/drivers/acpi/acpi_pnp.c
@@ -367,7 +367,7 @@ static struct acpi_scan_handler acpi_pnp_handler = {
*/
static int is_cmos_rtc_device(struct acpi_device *adev)
{
- struct acpi_device_id ids[] = {
+ static const struct acpi_device_id ids[] = {
{ "PNP0B00" },
{ "PNP0B01" },
{ "PNP0B02" },
diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c
index 3405f7a41e25..06a006ff89b0 100644
--- a/drivers/acpi/acpi_video.c
+++ b/drivers/acpi/acpi_video.c
@@ -77,14 +77,21 @@ module_param(allow_duplicates, bool, 0644);
static int disable_backlight_sysfs_if = -1;
module_param(disable_backlight_sysfs_if, int, 0444);
+#define REPORT_OUTPUT_KEY_EVENTS 0x01
+#define REPORT_BRIGHTNESS_KEY_EVENTS 0x02
+static int report_key_events = -1;
+module_param(report_key_events, int, 0644);
+MODULE_PARM_DESC(report_key_events,
+ "0: none, 1: output changes, 2: brightness changes, 3: all");
+
static bool device_id_scheme = false;
module_param(device_id_scheme, bool, 0444);
static bool only_lcd = false;
module_param(only_lcd, bool, 0444);
-static int register_count;
-static DEFINE_MUTEX(register_count_mutex);
+static DECLARE_COMPLETION(register_done);
+static DEFINE_MUTEX(register_done_mutex);
static struct mutex video_list_lock;
static struct list_head video_bus_head;
static int acpi_video_bus_add(struct acpi_device *device);
@@ -412,6 +419,13 @@ static int video_enable_only_lcd(const struct dmi_system_id *d)
return 0;
}
+static int video_set_report_key_events(const struct dmi_system_id *id)
+{
+ if (report_key_events == -1)
+ report_key_events = (uintptr_t)id->driver_data;
+ return 0;
+}
+
static struct dmi_system_id video_dmi_table[] = {
/*
* Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121
@@ -500,6 +514,24 @@ static struct dmi_system_id video_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile M9410"),
},
},
+ /*
+ * Some machines report wrong key events on the acpi-bus, suppress
+ * key event reporting on these. Note this is only intended to work
+ * around events which are plain wrong. In some cases we get double
+ * events, in this case acpi-video is considered the canonical source
+ * and the events from the other source should be filtered. E.g.
+ * by calling acpi_video_handles_brightness_key_presses() from the
+ * vendor acpi/wmi driver or by using /lib/udev/hwdb.d/60-keyboard.hwdb
+ */
+ {
+ .callback = video_set_report_key_events,
+ .driver_data = (void *)((uintptr_t)REPORT_OUTPUT_KEY_EVENTS),
+ .ident = "Dell Vostro V131",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"),
+ },
+ },
{}
};
@@ -1480,7 +1512,7 @@ static void acpi_video_bus_notify(struct acpi_device *device, u32 event)
/* Something vetoed the keypress. */
keycode = 0;
- if (keycode) {
+ if (keycode && (report_key_events & REPORT_OUTPUT_KEY_EVENTS)) {
input_report_key(input, keycode, 1);
input_sync(input);
input_report_key(input, keycode, 0);
@@ -1544,7 +1576,7 @@ static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data)
acpi_notifier_call_chain(device, event, 0);
- if (keycode) {
+ if (keycode && (report_key_events & REPORT_BRIGHTNESS_KEY_EVENTS)) {
input_report_key(input, keycode, 1);
input_sync(input);
input_report_key(input, keycode, 0);
@@ -2017,8 +2049,8 @@ int acpi_video_register(void)
{
int ret = 0;
- mutex_lock(&register_count_mutex);
- if (register_count) {
+ mutex_lock(&register_done_mutex);
+ if (completion_done(&register_done)) {
/*
* if the function of acpi_video_register is already called,
* don't register the acpi_vide_bus again and return no error.
@@ -2039,22 +2071,22 @@ int acpi_video_register(void)
* When the acpi_video_bus is loaded successfully, increase
* the counter reference.
*/
- register_count = 1;
+ complete(&register_done);
leave:
- mutex_unlock(&register_count_mutex);
+ mutex_unlock(&register_done_mutex);
return ret;
}
EXPORT_SYMBOL(acpi_video_register);
void acpi_video_unregister(void)
{
- mutex_lock(&register_count_mutex);
- if (register_count) {
+ mutex_lock(&register_done_mutex);
+ if (completion_done(&register_done)) {
acpi_bus_unregister_driver(&acpi_video_bus);
- register_count = 0;
+ reinit_completion(&register_done);
}
- mutex_unlock(&register_count_mutex);
+ mutex_unlock(&register_done_mutex);
}
EXPORT_SYMBOL(acpi_video_unregister);
@@ -2062,15 +2094,29 @@ void acpi_video_unregister_backlight(void)
{
struct acpi_video_bus *video;
- mutex_lock(&register_count_mutex);
- if (register_count) {
+ mutex_lock(&register_done_mutex);
+ if (completion_done(&register_done)) {
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);
+ mutex_unlock(&register_done_mutex);
+}
+
+bool acpi_video_handles_brightness_key_presses(void)
+{
+ bool have_video_busses;
+
+ wait_for_completion(&register_done);
+ mutex_lock(&video_list_lock);
+ have_video_busses = !list_empty(&video_bus_head);
+ mutex_unlock(&video_list_lock);
+
+ return have_video_busses &&
+ (report_key_events & REPORT_BRIGHTNESS_KEY_EVENTS);
}
+EXPORT_SYMBOL(acpi_video_handles_brightness_key_presses);
/*
* This is kind of nasty. Hardware using Intel chipsets may require
diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile
index 885936f79542..f682374c19f4 100644
--- a/drivers/acpi/acpica/Makefile
+++ b/drivers/acpi/acpica/Makefile
@@ -50,6 +50,7 @@ acpi-y += \
exdump.o \
exfield.o \
exfldio.o \
+ exmisc.o \
exmutex.o \
exnames.o \
exoparg1.o \
@@ -57,7 +58,6 @@ acpi-y += \
exoparg3.o \
exoparg6.o \
exprep.o \
- exmisc.o \
exregion.o \
exresnte.o \
exresolv.o \
@@ -66,6 +66,7 @@ acpi-y += \
exstoren.o \
exstorob.o \
exsystem.o \
+ extrace.o \
exutils.o
acpi-y += \
@@ -196,7 +197,6 @@ acpi-$(ACPI_FUTURE_USAGE) += \
dbfileio.o \
dbtest.o \
utcache.o \
- utfileio.o \
utprint.o \
uttrack.o \
utuuid.o
diff --git a/drivers/acpi/acpica/acapps.h b/drivers/acpi/acpica/acapps.h
index e4cc48fbf4ee..8b4ff40a294c 100644
--- a/drivers/acpi/acpica/acapps.h
+++ b/drivers/acpi/acpica/acapps.h
@@ -44,6 +44,8 @@
#ifndef _ACAPPS
#define _ACAPPS
+#include <stdio.h>
+
/* Common info for tool signons */
#define ACPICA_NAME "Intel ACPI Component Architecture"
@@ -85,11 +87,40 @@
acpi_os_printf (description);
#define ACPI_OPTION(name, description) \
- acpi_os_printf (" %-18s%s\n", name, description);
+ acpi_os_printf (" %-20s%s\n", name, description);
+
+/* Check for unexpected exceptions */
+
+#define ACPI_CHECK_STATUS(name, status, expected) \
+ if (status != expected) \
+ { \
+ acpi_os_printf ("Unexpected %s from %s (%s-%d)\n", \
+ acpi_format_exception (status), #name, _acpi_module_name, __LINE__); \
+ }
+
+/* Check for unexpected non-AE_OK errors */
+
+#define ACPI_CHECK_OK(name, status) ACPI_CHECK_STATUS (name, status, AE_OK);
#define FILE_SUFFIX_DISASSEMBLY "dsl"
#define FILE_SUFFIX_BINARY_TABLE ".dat" /* Needs the dot */
+/* acfileio */
+
+acpi_status
+ac_get_all_tables_from_file(char *filename,
+ u8 get_only_aml_tables,
+ struct acpi_new_table_desc **return_list_head);
+
+u8 ac_is_file_binary(FILE * file);
+
+acpi_status ac_validate_table_header(FILE * file, long table_offset);
+
+/* Values for get_only_aml_tables */
+
+#define ACPI_GET_ONLY_AML_TABLES TRUE
+#define ACPI_GET_ALL_TABLES FALSE
+
/*
* getopt
*/
@@ -107,30 +138,6 @@ extern char *acpi_gbl_optarg;
*/
u32 cm_get_file_size(ACPI_FILE file);
-#ifndef ACPI_DUMP_APP
-/*
- * adisasm
- */
-acpi_status
-ad_aml_disassemble(u8 out_to_file,
- char *filename, char *prefix, char **out_filename);
-
-void ad_print_statistics(void);
-
-acpi_status ad_find_dsdt(u8 **dsdt_ptr, u32 *dsdt_length);
-
-void ad_dump_tables(void);
-
-acpi_status ad_get_local_tables(void);
-
-acpi_status
-ad_parse_table(struct acpi_table_header *table,
- acpi_owner_id * owner_id, u8 load_table, u8 external);
-
-acpi_status ad_display_tables(char *filename, struct acpi_table_header *table);
-
-acpi_status ad_display_statistics(void);
-
/*
* adwalk
*/
@@ -168,6 +175,5 @@ char *ad_generate_filename(char *prefix, char *table_id);
void
ad_write_table(struct acpi_table_header *table,
u32 length, char *table_name, char *oem_table_id);
-#endif
#endif /* _ACAPPS */
diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h
index c928ba494c40..ecb05f1c1d5c 100644
--- a/drivers/acpi/acpica/acdebug.h
+++ b/drivers/acpi/acpica/acdebug.h
@@ -80,9 +80,15 @@ struct acpi_db_execute_walk {
/*
* dbxface - external debugger interfaces
*/
-acpi_status
-acpi_db_single_step(struct acpi_walk_state *walk_state,
- union acpi_parse_object *op, u32 op_type);
+ACPI_DBR_DEPENDENT_RETURN_OK(acpi_status
+ acpi_db_single_step(struct acpi_walk_state
+ *walk_state,
+ union acpi_parse_object *op,
+ u32 op_type))
+ ACPI_DBR_DEPENDENT_RETURN_VOID(void
+ acpi_db_signal_break_point(struct
+ acpi_walk_state
+ *walk_state))
/*
* dbcmds - debug commands and output routines
@@ -182,11 +188,15 @@ void acpi_db_display_method_info(union acpi_parse_object *op);
void acpi_db_decode_and_display_object(char *target, char *output_type);
-void
-acpi_db_display_result_object(union acpi_operand_object *obj_desc,
- struct acpi_walk_state *walk_state);
+ACPI_DBR_DEPENDENT_RETURN_VOID(void
+ acpi_db_display_result_object(union
+ acpi_operand_object
+ *obj_desc,
+ struct
+ acpi_walk_state
+ *walk_state))
-acpi_status acpi_db_display_all_methods(char *display_count_arg);
+ acpi_status acpi_db_display_all_methods(char *display_count_arg);
void acpi_db_display_arguments(void);
@@ -198,9 +208,13 @@ void acpi_db_display_calling_tree(void);
void acpi_db_display_object_type(char *object_arg);
-void
-acpi_db_display_argument_object(union acpi_operand_object *obj_desc,
- struct acpi_walk_state *walk_state);
+ACPI_DBR_DEPENDENT_RETURN_VOID(void
+ acpi_db_display_argument_object(union
+ acpi_operand_object
+ *obj_desc,
+ struct
+ acpi_walk_state
+ *walk_state))
/*
* dbexec - debugger control method execution
@@ -231,10 +245,7 @@ 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,
- u8 must_be_aml_table);
+acpi_status acpi_db_load_tables(struct acpi_new_table_desc *list_head);
/*
* dbhistry - debugger HISTORY command
@@ -257,7 +268,7 @@ acpi_db_command_dispatch(char *input_buffer,
void ACPI_SYSTEM_XFACE acpi_db_execute_thread(void *context);
-acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op);
+acpi_status acpi_db_user_commands(void);
char *acpi_db_get_next_token(char *string,
char **next, acpi_object_type * return_type);
diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h
index 228704b78657..d18f18409071 100644
--- a/drivers/acpi/acpica/acevents.h
+++ b/drivers/acpi/acpica/acevents.h
@@ -161,6 +161,11 @@ acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
/*
* evhandler - Address space handling
*/
+union acpi_operand_object *acpi_ev_find_region_handler(acpi_adr_space_type
+ space_id,
+ union acpi_operand_object
+ *handler_obj);
+
u8
acpi_ev_has_default_handler(struct acpi_namespace_node *node,
acpi_adr_space_type space_id);
@@ -193,9 +198,11 @@ void
acpi_ev_detach_region(union acpi_operand_object *region_obj,
u8 acpi_ns_is_locked);
-acpi_status
+void acpi_ev_associate_reg_method(union acpi_operand_object *region_obj);
+
+void
acpi_ev_execute_reg_methods(struct acpi_namespace_node *node,
- acpi_adr_space_type space_id);
+ acpi_adr_space_type space_id, u32 function);
acpi_status
acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function);
diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h
index faa97604d878..73462cac41d2 100644
--- a/drivers/acpi/acpica/acglobal.h
+++ b/drivers/acpi/acpica/acglobal.h
@@ -145,6 +145,7 @@ ACPI_GLOBAL(acpi_cache_t *, acpi_gbl_operand_cache);
ACPI_INIT_GLOBAL(u32, acpi_gbl_startup_flags, 0);
ACPI_INIT_GLOBAL(u8, acpi_gbl_shutdown, TRUE);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_early_initialization, TRUE);
/* Global handlers */
@@ -164,7 +165,7 @@ ACPI_GLOBAL(u8, acpi_gbl_next_owner_id_offset);
/* Initialization sequencing */
-ACPI_GLOBAL(u8, acpi_gbl_reg_methods_executed);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_reg_methods_enabled, FALSE);
/* Misc */
@@ -326,7 +327,6 @@ ACPI_GLOBAL(struct acpi_external_file *, acpi_gbl_external_file_list);
#ifdef ACPI_DEBUGGER
ACPI_INIT_GLOBAL(u8, acpi_gbl_abort_method, FALSE);
-ACPI_INIT_GLOBAL(u8, acpi_gbl_method_executing, FALSE);
ACPI_INIT_GLOBAL(acpi_thread_id, acpi_gbl_db_thread_id, ACPI_INVALID_THREAD_ID);
ACPI_GLOBAL(u8, acpi_gbl_db_opt_no_ini_methods);
@@ -345,7 +345,6 @@ ACPI_GLOBAL(acpi_object_type, acpi_gbl_db_arg_types[ACPI_DEBUGGER_MAX_ARGS]);
/* These buffers should all be the same size */
-ACPI_GLOBAL(char, acpi_gbl_db_line_buf[ACPI_DB_LINE_BUFFER_SIZE]);
ACPI_GLOBAL(char, acpi_gbl_db_parsed_buf[ACPI_DB_LINE_BUFFER_SIZE]);
ACPI_GLOBAL(char, acpi_gbl_db_scope_buf[ACPI_DB_LINE_BUFFER_SIZE]);
ACPI_GLOBAL(char, acpi_gbl_db_debug_filename[ACPI_DB_LINE_BUFFER_SIZE]);
@@ -360,9 +359,6 @@ ACPI_GLOBAL(u16, acpi_gbl_node_type_count_misc);
ACPI_GLOBAL(u32, acpi_gbl_num_nodes);
ACPI_GLOBAL(u32, acpi_gbl_num_objects);
-ACPI_GLOBAL(acpi_mutex, acpi_gbl_db_command_ready);
-ACPI_GLOBAL(acpi_mutex, acpi_gbl_db_command_complete);
-
#endif /* ACPI_DEBUGGER */
/*****************************************************************************
diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h
index e1dd784d8515..24928ec444de 100644
--- a/drivers/acpi/acpica/aclocal.h
+++ b/drivers/acpi/acpica/aclocal.h
@@ -219,6 +219,13 @@ struct acpi_table_list {
#define ACPI_ROOT_ORIGIN_ALLOCATED (1)
#define ACPI_ROOT_ALLOW_RESIZE (2)
+/* List to manage incoming ACPI tables */
+
+struct acpi_new_table_desc {
+ struct acpi_table_header *table;
+ struct acpi_new_table_desc *next;
+};
+
/* Predefined table indexes */
#define ACPI_INVALID_TABLE_INDEX (0xFFFFFFFF)
@@ -388,7 +395,8 @@ union acpi_predefined_info {
/* Return object auto-repair info */
-typedef acpi_status(*acpi_object_converter) (union acpi_operand_object
+typedef acpi_status(*acpi_object_converter) (struct acpi_namespace_node * scope,
+ union acpi_operand_object
*original_object,
union acpi_operand_object
**converted_object);
@@ -420,6 +428,7 @@ struct acpi_simple_repair_info {
struct acpi_reg_walk_info {
acpi_adr_space_type space_id;
+ u32 function;
u32 reg_run_count;
};
@@ -861,6 +870,7 @@ struct acpi_parse_state {
#define ACPI_PARSEOP_CLOSING_PAREN 0x10
#define ACPI_PARSEOP_COMPOUND 0x20
#define ACPI_PARSEOP_ASSIGNMENT 0x40
+#define ACPI_PARSEOP_ELSEIF 0x80
/*****************************************************************************
*
diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h
index e85366ceb15a..bad5bca03acc 100644
--- a/drivers/acpi/acpica/acmacros.h
+++ b/drivers/acpi/acpica/acmacros.h
@@ -401,17 +401,6 @@
#endif
/*
- * Some code only gets executed when the debugger is built in.
- * Note that this is entirely independent of whether the
- * DEBUG_PRINT stuff (set by ACPI_DEBUG_OUTPUT) is on, or not.
- */
-#ifdef ACPI_DEBUGGER
-#define ACPI_DEBUGGER_EXEC(a) a
-#else
-#define ACPI_DEBUGGER_EXEC(a)
-#endif
-
-/*
* Macros used for ACPICA utilities only
*/
diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h
index 5d261c942a0d..d082e62d7308 100644
--- a/drivers/acpi/acpica/acnamesp.h
+++ b/drivers/acpi/acpica/acnamesp.h
@@ -77,6 +77,7 @@
/* Object is not a package element */
#define ACPI_NOT_PACKAGE_ELEMENT ACPI_UINT32_MAX
+#define ACPI_ALL_PACKAGE_ELEMENTS (ACPI_UINT32_MAX-1)
/* Always emit warning message, not dependent on node flags */
@@ -183,13 +184,20 @@ acpi_ns_convert_to_buffer(union acpi_operand_object *original_object,
union acpi_operand_object **return_object);
acpi_status
-acpi_ns_convert_to_unicode(union acpi_operand_object *original_object,
+acpi_ns_convert_to_unicode(struct acpi_namespace_node *scope,
+ union acpi_operand_object *original_object,
union acpi_operand_object **return_object);
acpi_status
-acpi_ns_convert_to_resource(union acpi_operand_object *original_object,
+acpi_ns_convert_to_resource(struct acpi_namespace_node *scope,
+ union acpi_operand_object *original_object,
union acpi_operand_object **return_object);
+acpi_status
+acpi_ns_convert_to_reference(struct acpi_namespace_node *scope,
+ union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object);
+
/*
* nsdump - Namespace dump/print utilities
*/
diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h
index 0bd02c4a5f75..2b154cfbe136 100644
--- a/drivers/acpi/acpica/acobject.h
+++ b/drivers/acpi/acpica/acobject.h
@@ -93,9 +93,10 @@
#define AOPOBJ_AML_CONSTANT 0x01 /* Integer is an AML constant */
#define AOPOBJ_STATIC_POINTER 0x02 /* Data is part of an ACPI table, don't delete */
#define AOPOBJ_DATA_VALID 0x04 /* Object is initialized and data is valid */
-#define AOPOBJ_OBJECT_INITIALIZED 0x08 /* Region is initialized, _REG was run */
-#define AOPOBJ_SETUP_COMPLETE 0x10 /* Region setup is complete */
-#define AOPOBJ_INVALID 0x20 /* Host OS won't allow a Region address */
+#define AOPOBJ_OBJECT_INITIALIZED 0x08 /* Region is initialized */
+#define AOPOBJ_REG_CONNECTED 0x10 /* _REG was run */
+#define AOPOBJ_SETUP_COMPLETE 0x20 /* Region setup is complete */
+#define AOPOBJ_INVALID 0x40 /* Host OS won't allow a Region address */
/******************************************************************************
*
diff --git a/drivers/acpi/acpica/acopcode.h b/drivers/acpi/acpica/acopcode.h
index f9acf92fa0bc..324512db62bf 100644
--- a/drivers/acpi/acpica/acopcode.h
+++ b/drivers/acpi/acpica/acopcode.h
@@ -92,7 +92,7 @@
#define ARGP_BYTELIST_OP ARGP_LIST1 (ARGP_NAMESTRING)
#define ARGP_CONCAT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
#define ARGP_CONCAT_RES_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
-#define ARGP_COND_REF_OF_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_SUPERNAME)
+#define ARGP_COND_REF_OF_OP ARGP_LIST2 (ARGP_NAME_OR_REF,ARGP_TARGET)
#define ARGP_CONNECTFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING)
#define ARGP_CONTINUE_OP ARG_NONE
#define ARGP_COPY_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_SIMPLENAME)
@@ -152,13 +152,14 @@
#define ARGP_NAMEPATH_OP ARGP_LIST1 (ARGP_NAMESTRING)
#define ARGP_NOOP_OP ARG_NONE
#define ARGP_NOTIFY_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_TERMARG)
+#define ARGP_OBJECT_TYPE_OP ARGP_LIST1 (ARGP_NAME_OR_REF)
#define ARGP_ONE_OP ARG_NONE
#define ARGP_ONES_OP ARG_NONE
#define ARGP_PACKAGE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_BYTEDATA, ARGP_DATAOBJLIST)
#define ARGP_POWER_RES_OP ARGP_LIST5 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_BYTEDATA, ARGP_WORDDATA, ARGP_OBJLIST)
#define ARGP_PROCESSOR_OP ARGP_LIST6 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_BYTEDATA, ARGP_DWORDDATA, ARGP_BYTEDATA, ARGP_OBJLIST)
#define ARGP_QWORD_OP ARGP_LIST1 (ARGP_QWORDDATA)
-#define ARGP_REF_OF_OP ARGP_LIST1 (ARGP_SUPERNAME)
+#define ARGP_REF_OF_OP ARGP_LIST1 (ARGP_NAME_OR_REF)
#define ARGP_REGION_OP ARGP_LIST4 (ARGP_NAME, ARGP_BYTEDATA, ARGP_TERMARG, ARGP_TERMARG)
#define ARGP_RELEASE_OP ARGP_LIST1 (ARGP_SUPERNAME)
#define ARGP_RESERVEDFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING)
@@ -185,7 +186,6 @@
#define ARGP_TO_HEX_STR_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET)
#define ARGP_TO_INTEGER_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET)
#define ARGP_TO_STRING_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET)
-#define ARGP_TYPE_OP ARGP_LIST1 (ARGP_SUPERNAME)
#define ARGP_UNLOAD_OP ARGP_LIST1 (ARGP_SUPERNAME)
#define ARGP_VAR_PACKAGE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_DATAOBJLIST)
#define ARGP_WAIT_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_TERMARG)
@@ -223,7 +223,7 @@
#define ARGI_BUFFER_OP ARGI_LIST1 (ARGI_INTEGER)
#define ARGI_BYTE_OP ARGI_INVALID_OPCODE
#define ARGI_BYTELIST_OP ARGI_INVALID_OPCODE
-#define ARGI_CONCAT_OP ARGI_LIST3 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA, ARGI_TARGETREF)
+#define ARGI_CONCAT_OP ARGI_LIST3 (ARGI_ANYTYPE, ARGI_ANYTYPE, ARGI_TARGETREF)
#define ARGI_CONCAT_RES_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_BUFFER, ARGI_TARGETREF)
#define ARGI_COND_REF_OF_OP ARGI_LIST2 (ARGI_OBJECT_REF, ARGI_TARGETREF)
#define ARGI_CONNECTFIELD_OP ARGI_INVALID_OPCODE
@@ -285,6 +285,7 @@
#define ARGI_NAMEPATH_OP ARGI_INVALID_OPCODE
#define ARGI_NOOP_OP ARG_NONE
#define ARGI_NOTIFY_OP ARGI_LIST2 (ARGI_DEVICE_REF, ARGI_INTEGER)
+#define ARGI_OBJECT_TYPE_OP ARGI_LIST1 (ARGI_ANYTYPE)
#define ARGI_ONE_OP ARG_NONE
#define ARGI_ONES_OP ARG_NONE
#define ARGI_PACKAGE_OP ARGI_LIST1 (ARGI_INTEGER)
@@ -318,7 +319,6 @@
#define ARGI_TO_HEX_STR_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET)
#define ARGI_TO_INTEGER_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET)
#define ARGI_TO_STRING_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_FIXED_TARGET)
-#define ARGI_TYPE_OP ARGI_LIST1 (ARGI_ANYTYPE)
#define ARGI_UNLOAD_OP ARGI_LIST1 (ARGI_DDBHANDLE)
#define ARGI_VAR_PACKAGE_OP ARGI_LIST1 (ARGI_INTEGER)
#define ARGI_WAIT_OP ARGI_LIST2 (ARGI_EVENT, ARGI_INTEGER)
diff --git a/drivers/acpi/acpica/acparser.h b/drivers/acpi/acpica/acparser.h
index 8fc8c7cea879..96d510a7feba 100644
--- a/drivers/acpi/acpica/acparser.h
+++ b/drivers/acpi/acpica/acparser.h
@@ -92,7 +92,13 @@ acpi_ps_get_next_simple_arg(struct acpi_parse_state *parser_state,
acpi_status
acpi_ps_get_next_namepath(struct acpi_walk_state *walk_state,
struct acpi_parse_state *parser_state,
- union acpi_parse_object *arg, u8 method_call);
+ union acpi_parse_object *arg,
+ u8 possible_method_call);
+
+/* Values for u8 above */
+
+#define ACPI_NOT_METHOD_CALL FALSE
+#define ACPI_POSSIBLE_METHOD_CALL TRUE
acpi_status
acpi_ps_get_next_arg(struct acpi_walk_state *walk_state,
diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h
index 8b8fef6cc32d..9e84c05c0b91 100644
--- a/drivers/acpi/acpica/acutils.h
+++ b/drivers/acpi/acpica/acutils.h
@@ -184,24 +184,24 @@ acpi_status acpi_ut_init_globals(void);
#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER)
-char *acpi_ut_get_mutex_name(u32 mutex_id);
+const char *acpi_ut_get_mutex_name(u32 mutex_id);
const char *acpi_ut_get_notify_name(u32 notify_value, acpi_object_type type);
#endif
-char *acpi_ut_get_type_name(acpi_object_type type);
+const char *acpi_ut_get_type_name(acpi_object_type type);
-char *acpi_ut_get_node_name(void *object);
+const char *acpi_ut_get_node_name(void *object);
-char *acpi_ut_get_descriptor_name(void *object);
+const char *acpi_ut_get_descriptor_name(void *object);
const char *acpi_ut_get_reference_name(union acpi_operand_object *object);
-char *acpi_ut_get_object_type_name(union acpi_operand_object *obj_desc);
+const char *acpi_ut_get_object_type_name(union acpi_operand_object *obj_desc);
-char *acpi_ut_get_region_name(u8 space_id);
+const char *acpi_ut_get_region_name(u8 space_id);
-char *acpi_ut_get_event_name(u32 event_id);
+const char *acpi_ut_get_event_name(u32 event_id);
char acpi_ut_hex_to_ascii_char(u64 integer, u32 position);
@@ -353,14 +353,6 @@ acpi_ut_execute_power_methods(struct acpi_namespace_node *device_node,
u8 method_count, u8 *out_values);
/*
- * utfileio - file operations
- */
-#ifdef ACPI_APPLICATION
-acpi_status
-acpi_ut_read_table_from_file(char *filename, struct acpi_table_header **table);
-#endif
-
-/*
* utids - device ID support
*/
acpi_status
@@ -372,10 +364,6 @@ acpi_ut_execute_UID(struct acpi_namespace_node *device_node,
struct acpi_pnp_device_id ** return_id);
acpi_status
-acpi_ut_execute_SUB(struct acpi_namespace_node *device_node,
- struct acpi_pnp_device_id **return_id);
-
-acpi_status
acpi_ut_execute_CID(struct acpi_namespace_node *device_node,
struct acpi_pnp_device_id_list ** return_cid_list);
diff --git a/drivers/acpi/acpica/amlcode.h b/drivers/acpi/acpica/amlcode.h
index 883f20cfa698..ab9f3f1fbb0f 100644
--- a/drivers/acpi/acpica/amlcode.h
+++ b/drivers/acpi/acpica/amlcode.h
@@ -120,7 +120,7 @@
#define AML_CREATE_WORD_FIELD_OP (u16) 0x8b
#define AML_CREATE_BYTE_FIELD_OP (u16) 0x8c
#define AML_CREATE_BIT_FIELD_OP (u16) 0x8d
-#define AML_TYPE_OP (u16) 0x8e
+#define AML_OBJECT_TYPE_OP (u16) 0x8e
#define AML_CREATE_QWORD_FIELD_OP (u16) 0x8f /* ACPI 2.0 */
#define AML_LAND_OP (u16) 0x90
#define AML_LOR_OP (u16) 0x91
@@ -238,7 +238,8 @@
#define ARGP_TERMLIST 0x0F
#define ARGP_WORDDATA 0x10
#define ARGP_QWORDDATA 0x11
-#define ARGP_SIMPLENAME 0x12
+#define ARGP_SIMPLENAME 0x12 /* name_string | local_term | arg_term */
+#define ARGP_NAME_OR_REF 0x13 /* For object_type only */
/*
* Resolved argument types for the AML Interpreter
diff --git a/drivers/acpi/acpica/dbcmds.c b/drivers/acpi/acpica/dbcmds.c
index 30414b3d7fdd..328c35b323d5 100644
--- a/drivers/acpi/acpica/dbcmds.c
+++ b/drivers/acpi/acpica/dbcmds.c
@@ -798,7 +798,7 @@ acpi_db_device_resources(acpi_handle obj_handle,
acpi_status status;
node = ACPI_CAST_PTR(struct acpi_namespace_node, obj_handle);
- parent_path = acpi_ns_get_external_pathname(node);
+ parent_path = acpi_ns_get_normalized_pathname(node, TRUE);
if (!parent_path) {
return (AE_NO_MEMORY);
}
@@ -1131,13 +1131,8 @@ void acpi_db_trace(char *enable_arg, char *method_arg, char *once_arg)
u32 debug_layer = 0;
u32 flags = 0;
- if (enable_arg) {
- acpi_ut_strupr(enable_arg);
- }
-
- if (once_arg) {
- acpi_ut_strupr(once_arg);
- }
+ acpi_ut_strupr(enable_arg);
+ acpi_ut_strupr(once_arg);
if (method_arg) {
if (acpi_db_trace_method_name) {
diff --git a/drivers/acpi/acpica/dbdisply.c b/drivers/acpi/acpica/dbdisply.c
index 672977ec7c7d..1965b48d8e83 100644
--- a/drivers/acpi/acpica/dbdisply.c
+++ b/drivers/acpi/acpica/dbdisply.c
@@ -48,6 +48,7 @@
#include "acnamesp.h"
#include "acparser.h"
#include "acinterp.h"
+#include "acevents.h"
#include "acdebug.h"
#define _COMPONENT ACPI_CA_DEBUGGER
@@ -588,7 +589,7 @@ void acpi_db_display_calling_tree(void)
*
* FUNCTION: acpi_db_display_object_type
*
- * PARAMETERS: name - User entered NS node handle or name
+ * PARAMETERS: object_arg - User entered NS node handle
*
* RETURN: None
*
@@ -596,44 +597,34 @@ void acpi_db_display_calling_tree(void)
*
******************************************************************************/
-void acpi_db_display_object_type(char *name)
+void acpi_db_display_object_type(char *object_arg)
{
- struct acpi_namespace_node *node;
+ acpi_handle handle;
struct acpi_device_info *info;
acpi_status status;
u32 i;
- node = acpi_db_convert_to_node(name);
- if (!node) {
- return;
- }
+ handle = ACPI_TO_POINTER(strtoul(object_arg, NULL, 16));
- status = acpi_get_object_info(ACPI_CAST_PTR(acpi_handle, node), &info);
+ status = acpi_get_object_info(handle, &info);
if (ACPI_FAILURE(status)) {
acpi_os_printf("Could not get object info, %s\n",
acpi_format_exception(status));
return;
}
- if (info->valid & ACPI_VALID_ADR) {
- acpi_os_printf("ADR: %8.8X%8.8X, STA: %8.8X, Flags: %X\n",
- ACPI_FORMAT_UINT64(info->address),
- info->current_status, info->flags);
- }
- if (info->valid & ACPI_VALID_SXDS) {
- acpi_os_printf("S1D-%2.2X S2D-%2.2X S3D-%2.2X S4D-%2.2X\n",
- info->highest_dstates[0],
- info->highest_dstates[1],
- info->highest_dstates[2],
- info->highest_dstates[3]);
- }
- if (info->valid & ACPI_VALID_SXWS) {
- acpi_os_printf
- ("S0W-%2.2X S1W-%2.2X S2W-%2.2X S3W-%2.2X S4W-%2.2X\n",
- info->lowest_dstates[0], info->lowest_dstates[1],
- info->lowest_dstates[2], info->lowest_dstates[3],
- info->lowest_dstates[4]);
- }
+ acpi_os_printf("ADR: %8.8X%8.8X, STA: %8.8X, Flags: %X\n",
+ ACPI_FORMAT_UINT64(info->address),
+ info->current_status, info->flags);
+
+ acpi_os_printf("S1D-%2.2X S2D-%2.2X S3D-%2.2X S4D-%2.2X\n",
+ info->highest_dstates[0], info->highest_dstates[1],
+ info->highest_dstates[2], info->highest_dstates[3]);
+
+ acpi_os_printf("S0W-%2.2X S1W-%2.2X S2W-%2.2X S3W-%2.2X S4W-%2.2X\n",
+ info->lowest_dstates[0], info->lowest_dstates[1],
+ info->lowest_dstates[2], info->lowest_dstates[3],
+ info->lowest_dstates[4]);
if (info->valid & ACPI_VALID_HID) {
acpi_os_printf("HID: %s\n", info->hardware_id.string);
@@ -643,10 +634,6 @@ void acpi_db_display_object_type(char *name)
acpi_os_printf("UID: %s\n", info->unique_id.string);
}
- if (info->valid & ACPI_VALID_SUB) {
- acpi_os_printf("SUB: %s\n", info->subsystem_id.string);
- }
-
if (info->valid & ACPI_VALID_CID) {
for (i = 0; i < info->compatible_id_list.count; i++) {
acpi_os_printf("CID %u: %s\n", i,
@@ -679,6 +666,12 @@ acpi_db_display_result_object(union acpi_operand_object *obj_desc,
struct acpi_walk_state *walk_state)
{
+#ifndef ACPI_APPLICATION
+ if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {
+ return;
+ }
+#endif
+
/* Only display if single stepping */
if (!acpi_gbl_cm_single_step) {
@@ -708,6 +701,12 @@ acpi_db_display_argument_object(union acpi_operand_object *obj_desc,
struct acpi_walk_state *walk_state)
{
+#ifndef ACPI_APPLICATION
+ if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {
+ return;
+ }
+#endif
+
if (!acpi_gbl_cm_single_step) {
return;
}
@@ -951,28 +950,25 @@ void acpi_db_display_handlers(void)
if (obj_desc) {
for (i = 0; i < ACPI_ARRAY_LENGTH(acpi_gbl_space_id_list); i++) {
space_id = acpi_gbl_space_id_list[i];
- handler_obj = obj_desc->device.handler;
acpi_os_printf(ACPI_PREDEFINED_PREFIX,
acpi_ut_get_region_name((u8)space_id),
space_id);
- while (handler_obj) {
- if (acpi_gbl_space_id_list[i] ==
- handler_obj->address_space.space_id) {
- acpi_os_printf
- (ACPI_HANDLER_PRESENT_STRING,
- (handler_obj->address_space.
- handler_flags &
- ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)
- ? "Default" : "User",
- handler_obj->address_space.
- handler);
-
- goto found_handler;
- }
+ handler_obj =
+ acpi_ev_find_region_handler(space_id,
+ obj_desc->common_notify.
+ handler);
+ if (handler_obj) {
+ acpi_os_printf(ACPI_HANDLER_PRESENT_STRING,
+ (handler_obj->address_space.
+ handler_flags &
+ ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)
+ ? "Default" : "User",
+ handler_obj->address_space.
+ handler);
- handler_obj = handler_obj->address_space.next;
+ goto found_handler;
}
/* There is no handler for this space_id */
@@ -984,7 +980,7 @@ found_handler: ;
/* Find all handlers for user-defined space_IDs */
- handler_obj = obj_desc->device.handler;
+ handler_obj = obj_desc->common_notify.handler;
while (handler_obj) {
if (handler_obj->address_space.space_id >=
ACPI_USER_REGION_BEGIN) {
@@ -1079,14 +1075,14 @@ acpi_db_display_non_root_handlers(acpi_handle obj_handle,
return (AE_OK);
}
- pathname = acpi_ns_get_external_pathname(node);
+ pathname = acpi_ns_get_normalized_pathname(node, TRUE);
if (!pathname) {
return (AE_OK);
}
/* Display all handlers associated with this device */
- handler_obj = obj_desc->device.handler;
+ handler_obj = obj_desc->common_notify.handler;
while (handler_obj) {
acpi_os_printf(ACPI_PREDEFINED_PREFIX,
acpi_ut_get_region_name((u8)handler_obj->
diff --git a/drivers/acpi/acpica/dbfileio.c b/drivers/acpi/acpica/dbfileio.c
index d0e6b20ce82a..31f54d71c51a 100644
--- a/drivers/acpi/acpica/dbfileio.c
+++ b/drivers/acpi/acpica/dbfileio.c
@@ -46,6 +46,10 @@
#include "accommon.h"
#include "acdebug.h"
#include "actables.h"
+#include <stdio.h>
+#ifdef ACPI_APPLICATION
+#include "acapps.h"
+#endif
#define _COMPONENT ACPI_CA_DEBUGGER
ACPI_MODULE_NAME("dbfileio")
@@ -110,122 +114,31 @@ void acpi_db_open_debug_file(char *name)
}
#endif
-#ifdef ACPI_APPLICATION
-#include "acapps.h"
-
-/*******************************************************************************
- *
- * FUNCTION: ae_local_load_table
- *
- * PARAMETERS: table - pointer to a buffer containing the entire
- * table to be loaded
- *
- * RETURN: Status
- *
- * DESCRIPTION: This function is called to load a table from the caller's
- * buffer. The buffer must contain an entire ACPI Table including
- * a valid header. The header fields will be verified, and if it
- * is determined that the table is invalid, the call will fail.
- *
- ******************************************************************************/
-
-static acpi_status ae_local_load_table(struct acpi_table_header *table)
-{
- acpi_status status = AE_OK;
-
- ACPI_FUNCTION_TRACE(ae_local_load_table);
-
-#if 0
-/* struct acpi_table_desc table_info; */
-
- if (!table) {
- return_ACPI_STATUS(AE_BAD_PARAMETER);
- }
-
- table_info.pointer = table;
- status = acpi_tb_recognize_table(&table_info, ACPI_TABLE_ALL);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
-
- /* Install the new table into the local data structures */
-
- status = acpi_tb_init_table_descriptor(&table_info);
- if (ACPI_FAILURE(status)) {
- if (status == AE_ALREADY_EXISTS) {
-
- /* Table already exists, no error */
-
- status = AE_OK;
- }
-
- /* Free table allocated by acpi_tb_get_table */
-
- acpi_tb_delete_single_table(&table_info);
- return_ACPI_STATUS(status);
- }
-#if (!defined (ACPI_NO_METHOD_EXECUTION) && !defined (ACPI_CONSTANT_EVAL_ONLY))
-
- status =
- acpi_ns_load_table(table_info.installed_desc, acpi_gbl_root_node);
- if (ACPI_FAILURE(status)) {
-
- /* Uninstall table and free the buffer */
-
- acpi_tb_delete_tables_by_type(ACPI_TABLE_ID_DSDT);
- return_ACPI_STATUS(status);
- }
-#endif
-#endif
-
- return_ACPI_STATUS(status);
-}
-#endif
-
/*******************************************************************************
*
- * FUNCTION: acpi_db_get_table_from_file
+ * FUNCTION: acpi_db_load_tables
*
- * PARAMETERS: filename - File where table is located
- * return_table - Where a pointer to the table is returned
+ * PARAMETERS: list_head - List of ACPI tables to load
*
* RETURN: Status
*
- * DESCRIPTION: Load an ACPI table from a file
+ * DESCRIPTION: Load ACPI tables from a previously constructed table list.
*
******************************************************************************/
-acpi_status
-acpi_db_get_table_from_file(char *filename,
- struct acpi_table_header **return_table,
- u8 must_be_aml_file)
+acpi_status acpi_db_load_tables(struct acpi_new_table_desc *list_head)
{
-#ifdef ACPI_APPLICATION
acpi_status status;
+ struct acpi_new_table_desc *table_list_head;
struct acpi_table_header *table;
- u8 is_aml_table = TRUE;
-
- status = acpi_ut_read_table_from_file(filename, &table);
- if (ACPI_FAILURE(status)) {
- return (status);
- }
-
- if (must_be_aml_file) {
- is_aml_table = acpi_ut_is_aml_table(table);
- if (!is_aml_table) {
- ACPI_EXCEPTION((AE_INFO, AE_OK,
- "Input for -e is not an AML table: "
- "\"%4.4s\" (must be DSDT/SSDT)",
- table->signature));
- return (AE_TYPE);
- }
- }
- if (is_aml_table) {
+ /* Load all ACPI tables in the list */
- /* Attempt to recognize and install the table */
+ table_list_head = list_head;
+ while (table_list_head) {
+ table = table_list_head->table;
- status = ae_local_load_table(table);
+ status = acpi_load_table(table);
if (ACPI_FAILURE(status)) {
if (status == AE_ALREADY_EXISTS) {
acpi_os_printf
@@ -239,18 +152,12 @@ acpi_db_get_table_from_file(char *filename,
return (status);
}
- acpi_tb_print_table_header(0, table);
-
fprintf(stderr,
"Acpi table [%4.4s] successfully installed and loaded\n",
table->signature);
- }
- acpi_gbl_acpi_hardware_present = FALSE;
- if (return_table) {
- *return_table = table;
+ table_list_head = table_list_head->next;
}
-#endif /* ACPI_APPLICATION */
return (AE_OK);
}
diff --git a/drivers/acpi/acpica/dbinput.c b/drivers/acpi/acpica/dbinput.c
index 0480254437f1..6203001baa30 100644
--- a/drivers/acpi/acpica/dbinput.c
+++ b/drivers/acpi/acpica/dbinput.c
@@ -45,6 +45,10 @@
#include "accommon.h"
#include "acdebug.h"
+#ifdef ACPI_APPLICATION
+#include "acapps.h"
+#endif
+
#define _COMPONENT ACPI_CA_DEBUGGER
ACPI_MODULE_NAME("dbinput")
@@ -53,8 +57,6 @@ static u32 acpi_db_get_line(char *input_buffer);
static u32 acpi_db_match_command(char *user_command);
-static void acpi_db_single_thread(void);
-
static void acpi_db_display_command_info(char *command, u8 display_all);
static void acpi_db_display_help(char *command);
@@ -623,9 +625,7 @@ static u32 acpi_db_get_line(char *input_buffer)
/* Uppercase the actual command */
- if (acpi_gbl_db_args[0]) {
- acpi_ut_strupr(acpi_gbl_db_args[0]);
- }
+ acpi_ut_strupr(acpi_gbl_db_args[0]);
count = i;
if (count) {
@@ -1050,11 +1050,17 @@ acpi_db_command_dispatch(char *input_buffer,
acpi_db_close_debug_file();
break;
- case CMD_LOAD:
+ case CMD_LOAD:{
+ struct acpi_new_table_desc *list_head = NULL;
- status =
- acpi_db_get_table_from_file(acpi_gbl_db_args[1], NULL,
- FALSE);
+ status =
+ ac_get_all_tables_from_file(acpi_gbl_db_args[1],
+ ACPI_GET_ALL_TABLES,
+ &list_head);
+ if (ACPI_SUCCESS(status)) {
+ acpi_db_load_tables(list_head);
+ }
+ }
break;
case CMD_OPEN:
@@ -1149,55 +1155,16 @@ acpi_db_command_dispatch(char *input_buffer,
void ACPI_SYSTEM_XFACE acpi_db_execute_thread(void *context)
{
- acpi_status status = AE_OK;
- acpi_status Mstatus;
-
- while (status != AE_CTRL_TERMINATE && !acpi_gbl_db_terminate_loop) {
- acpi_gbl_method_executing = FALSE;
- acpi_gbl_step_to_next_call = FALSE;
-
- Mstatus = acpi_os_acquire_mutex(acpi_gbl_db_command_ready,
- ACPI_WAIT_FOREVER);
- if (ACPI_FAILURE(Mstatus)) {
- return;
- }
-
- status =
- acpi_db_command_dispatch(acpi_gbl_db_line_buf, NULL, NULL);
- acpi_os_release_mutex(acpi_gbl_db_command_complete);
- }
+ (void)acpi_db_user_commands();
acpi_gbl_db_threads_terminated = TRUE;
}
/*******************************************************************************
*
- * FUNCTION: acpi_db_single_thread
- *
- * PARAMETERS: None
- *
- * RETURN: None
- *
- * DESCRIPTION: Debugger execute thread. Waits for a command line, then
- * simply dispatches it.
- *
- ******************************************************************************/
-
-static void acpi_db_single_thread(void)
-{
-
- acpi_gbl_method_executing = FALSE;
- acpi_gbl_step_to_next_call = FALSE;
-
- (void)acpi_db_command_dispatch(acpi_gbl_db_line_buf, NULL, NULL);
-}
-
-/*******************************************************************************
- *
* FUNCTION: acpi_db_user_commands
*
- * PARAMETERS: prompt - User prompt (depends on mode)
- * op - Current executing parse op
+ * PARAMETERS: None
*
* RETURN: None
*
@@ -1206,7 +1173,7 @@ static void acpi_db_single_thread(void)
*
******************************************************************************/
-acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op)
+acpi_status acpi_db_user_commands(void)
{
acpi_status status = AE_OK;
@@ -1216,52 +1183,31 @@ acpi_status acpi_db_user_commands(char prompt, union acpi_parse_object *op)
while (!acpi_gbl_db_terminate_loop) {
- /* Force output to console until a command is entered */
-
- acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
-
- /* Different prompt if method is executing */
-
- if (!acpi_gbl_method_executing) {
- acpi_os_printf("%1c ", ACPI_DEBUGGER_COMMAND_PROMPT);
- } else {
- acpi_os_printf("%1c ", ACPI_DEBUGGER_EXECUTE_PROMPT);
- }
+ /* Wait the readiness of the command */
- /* Get the user input line */
-
- status = acpi_os_get_line(acpi_gbl_db_line_buf,
- ACPI_DB_LINE_BUFFER_SIZE, NULL);
+ status = acpi_os_wait_command_ready();
if (ACPI_FAILURE(status)) {
- ACPI_EXCEPTION((AE_INFO, status,
- "While parsing command line"));
- return (status);
+ break;
}
- /* Check for single or multithreaded debug */
+ /* Just call to the command line interpreter */
- if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) {
- /*
- * Signal the debug thread that we have a command to execute,
- * and wait for the command to complete.
- */
- acpi_os_release_mutex(acpi_gbl_db_command_ready);
- if (ACPI_FAILURE(status)) {
- return (status);
- }
+ acpi_gbl_method_executing = FALSE;
+ acpi_gbl_step_to_next_call = FALSE;
- status =
- acpi_os_acquire_mutex(acpi_gbl_db_command_complete,
- ACPI_WAIT_FOREVER);
- if (ACPI_FAILURE(status)) {
- return (status);
- }
- } else {
- /* Just call to the command line interpreter */
+ (void)acpi_db_command_dispatch(acpi_gbl_db_line_buf, NULL,
+ NULL);
+
+ /* Notify the completion of the command */
- acpi_db_single_thread();
+ status = acpi_os_notify_command_complete();
+ if (ACPI_FAILURE(status)) {
+ break;
}
}
+ if (ACPI_FAILURE(status) && status != AE_CTRL_TERMINATE) {
+ ACPI_EXCEPTION((AE_INFO, status, "While parsing command line"));
+ }
return (status);
}
diff --git a/drivers/acpi/acpica/dbnames.c b/drivers/acpi/acpica/dbnames.c
index 04ff1ebfda58..4f68dfc6ea55 100644
--- a/drivers/acpi/acpica/dbnames.c
+++ b/drivers/acpi/acpica/dbnames.c
@@ -438,7 +438,7 @@ acpi_db_walk_for_predefined_names(acpi_handle obj_handle,
return (AE_OK);
}
- pathname = acpi_ns_get_external_pathname(node);
+ pathname = acpi_ns_get_normalized_pathname(node, TRUE);
if (!pathname) {
return (AE_OK);
}
diff --git a/drivers/acpi/acpica/dbstats.c b/drivers/acpi/acpica/dbstats.c
index 4ba0a20811eb..de255d975941 100644
--- a/drivers/acpi/acpica/dbstats.c
+++ b/drivers/acpi/acpica/dbstats.c
@@ -382,6 +382,7 @@ acpi_status acpi_db_display_statistics(char *type_arg)
acpi_gbl_node_type_count[i],
acpi_gbl_obj_type_count[i]);
}
+
acpi_os_printf("%16.16s % 10ld% 10ld\n", "Misc/Unknown",
acpi_gbl_node_type_count_misc,
acpi_gbl_obj_type_count_misc);
diff --git a/drivers/acpi/acpica/dbtest.c b/drivers/acpi/acpica/dbtest.c
index 10ea8bf9b810..68b4e8d9e1d6 100644
--- a/drivers/acpi/acpica/dbtest.c
+++ b/drivers/acpi/acpica/dbtest.c
@@ -953,7 +953,7 @@ acpi_db_evaluate_one_predefined_name(acpi_handle obj_handle,
return (AE_OK);
}
- pathname = acpi_ns_get_external_pathname(node);
+ pathname = acpi_ns_get_normalized_pathname(node, TRUE);
if (!pathname) {
return (AE_OK);
}
diff --git a/drivers/acpi/acpica/dbutils.c b/drivers/acpi/acpica/dbutils.c
index 86790e080139..8c85d85a9cb2 100644
--- a/drivers/acpi/acpica/dbutils.c
+++ b/drivers/acpi/acpica/dbutils.c
@@ -173,6 +173,7 @@ void acpi_db_dump_external_object(union acpi_object *obj_desc, u32 level)
if (obj_desc->buffer.length > 16) {
acpi_os_printf("\n");
}
+
acpi_ut_debug_dump_buffer(ACPI_CAST_PTR
(u8,
obj_desc->buffer.pointer),
diff --git a/drivers/acpi/acpica/dbxface.c b/drivers/acpi/acpica/dbxface.c
index 342298a6e10f..d7ff58e8c233 100644
--- a/drivers/acpi/acpica/dbxface.c
+++ b/drivers/acpi/acpica/dbxface.c
@@ -85,46 +85,21 @@ acpi_db_start_command(struct acpi_walk_state *walk_state,
acpi_gbl_method_executing = TRUE;
status = AE_CTRL_TRUE;
- while (status == AE_CTRL_TRUE) {
- if (acpi_gbl_debugger_configuration == DEBUGGER_MULTI_THREADED) {
-
- /* Handshake with the front-end that gets user command lines */
-
- acpi_os_release_mutex(acpi_gbl_db_command_complete);
-
- status =
- acpi_os_acquire_mutex(acpi_gbl_db_command_ready,
- ACPI_WAIT_FOREVER);
- if (ACPI_FAILURE(status)) {
- return (status);
- }
- } else {
- /* Single threaded, we must get a command line ourselves */
-
- /* Force output to console until a command is entered */
- acpi_db_set_output_destination(ACPI_DB_CONSOLE_OUTPUT);
+ while (status == AE_CTRL_TRUE) {
- /* Different prompt if method is executing */
+ /* Notify the completion of the command */
- if (!acpi_gbl_method_executing) {
- acpi_os_printf("%1c ",
- ACPI_DEBUGGER_COMMAND_PROMPT);
- } else {
- acpi_os_printf("%1c ",
- ACPI_DEBUGGER_EXECUTE_PROMPT);
- }
+ status = acpi_os_notify_command_complete();
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
+ }
- /* Get the user input line */
+ /* Wait the readiness of the command */
- status = acpi_os_get_line(acpi_gbl_db_line_buf,
- ACPI_DB_LINE_BUFFER_SIZE,
- NULL);
- if (ACPI_FAILURE(status)) {
- ACPI_EXCEPTION((AE_INFO, status,
- "While parsing command line"));
- return (status);
- }
+ status = acpi_os_wait_command_ready();
+ if (ACPI_FAILURE(status)) {
+ goto error_exit;
}
status =
@@ -134,11 +109,46 @@ acpi_db_start_command(struct acpi_walk_state *walk_state,
/* acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); */
+error_exit:
+ if (ACPI_FAILURE(status) && status != AE_CTRL_TERMINATE) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "While parsing/handling command line"));
+ }
return (status);
}
/*******************************************************************************
*
+ * FUNCTION: acpi_db_signal_break_point
+ *
+ * PARAMETERS: walk_state - Current walk
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Called for AML_BREAK_POINT_OP
+ *
+ ******************************************************************************/
+
+void acpi_db_signal_break_point(struct acpi_walk_state *walk_state)
+{
+
+#ifndef ACPI_APPLICATION
+ if (acpi_gbl_db_thread_id != acpi_os_get_thread_id()) {
+ return;
+ }
+#endif
+
+ /*
+ * Set the single-step flag. This will cause the debugger (if present)
+ * to break to the console within the AML debugger at the start of the
+ * next AML instruction.
+ */
+ acpi_gbl_cm_single_step = TRUE;
+ acpi_os_printf("**break** Executed AML BreakPoint opcode\n");
+}
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_db_single_step
*
* PARAMETERS: walk_state - Current walk
@@ -420,15 +430,7 @@ acpi_status acpi_initialize_debugger(void)
/* These were created with one unit, grab it */
- status = acpi_os_acquire_mutex(acpi_gbl_db_command_complete,
- ACPI_WAIT_FOREVER);
- if (ACPI_FAILURE(status)) {
- acpi_os_printf("Could not get debugger mutex\n");
- return_ACPI_STATUS(status);
- }
-
- status = acpi_os_acquire_mutex(acpi_gbl_db_command_ready,
- ACPI_WAIT_FOREVER);
+ status = acpi_os_initialize_command_signals();
if (ACPI_FAILURE(status)) {
acpi_os_printf("Could not get debugger mutex\n");
return_ACPI_STATUS(status);
@@ -473,13 +475,14 @@ void acpi_terminate_debugger(void)
acpi_gbl_db_terminate_loop = TRUE;
if (acpi_gbl_debugger_configuration & DEBUGGER_MULTI_THREADED) {
- acpi_os_release_mutex(acpi_gbl_db_command_ready);
/* Wait the AML Debugger threads */
while (!acpi_gbl_db_threads_terminated) {
acpi_os_sleep(100);
}
+
+ acpi_os_terminate_command_signals();
}
if (acpi_gbl_db_buffer) {
diff --git a/drivers/acpi/acpica/dsargs.c b/drivers/acpi/acpica/dsargs.c
index e2ab59e39162..76cfced31f9f 100644
--- a/drivers/acpi/acpica/dsargs.c
+++ b/drivers/acpi/acpica/dsargs.c
@@ -194,8 +194,8 @@ acpi_ds_get_buffer_field_arguments(union acpi_operand_object *obj_desc)
extra_desc = acpi_ns_get_secondary_object(obj_desc);
node = obj_desc->buffer_field.node;
- ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname(ACPI_TYPE_BUFFER_FIELD,
- node, NULL));
+ ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname
+ (ACPI_TYPE_BUFFER_FIELD, node, NULL));
ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "[%4.4s] BufferField Arg Init\n",
acpi_ut_get_node_name(node)));
@@ -385,7 +385,8 @@ acpi_status acpi_ds_get_region_arguments(union acpi_operand_object *obj_desc)
ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname
(ACPI_TYPE_REGION, node, NULL));
- ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "[%4.4s] OpRegion Arg Init at AML %p\n",
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "[%4.4s] OpRegion Arg Init at AML %p\n",
acpi_ut_get_node_name(node),
extra_desc->extra.aml_start));
diff --git a/drivers/acpi/acpica/dscontrol.c b/drivers/acpi/acpica/dscontrol.c
index 435fc16e2f83..06a6f7f3af52 100644
--- a/drivers/acpi/acpica/dscontrol.c
+++ b/drivers/acpi/acpica/dscontrol.c
@@ -47,6 +47,7 @@
#include "amlcode.h"
#include "acdispat.h"
#include "acinterp.h"
+#include "acdebug.h"
#define _COMPONENT ACPI_DISPATCHER
ACPI_MODULE_NAME("dscontrol")
@@ -348,14 +349,7 @@ acpi_ds_exec_end_control_op(struct acpi_walk_state * walk_state,
case AML_BREAK_POINT_OP:
- /*
- * Set the single-step flag. This will cause the debugger (if present)
- * to break to the console within the AML debugger at the start of the
- * next AML instruction.
- */
- ACPI_DEBUGGER_EXEC(acpi_gbl_cm_single_step = TRUE);
- ACPI_DEBUGGER_EXEC(acpi_os_printf
- ("**break** Executed AML BreakPoint opcode\n"));
+ acpi_db_signal_break_point(walk_state);
/* Call to the OSL in case OS wants a piece of the action */
diff --git a/drivers/acpi/acpica/dsdebug.c b/drivers/acpi/acpica/dsdebug.c
index 309556efc553..1eb82bd7ee16 100644
--- a/drivers/acpi/acpica/dsdebug.c
+++ b/drivers/acpi/acpica/dsdebug.c
@@ -161,6 +161,7 @@ acpi_ds_dump_method_stack(acpi_status status,
ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
"\n**** Exception %s during execution of method ",
acpi_format_exception(status)));
+
acpi_ds_print_node_pathname(walk_state->method_node, NULL);
/* Display stack of executing methods */
@@ -203,8 +204,8 @@ acpi_ds_dump_method_stack(acpi_status status,
} else {
/*
* This method has called another method
- * NOTE: the method call parse subtree is already deleted at this
- * point, so we cannot disassemble the method invocation.
+ * NOTE: the method call parse subtree is already deleted at
+ * this point, so we cannot disassemble the method invocation.
*/
ACPI_DEBUG_PRINT_RAW((ACPI_DB_DISPATCH,
"Call to method "));
diff --git a/drivers/acpi/acpica/dsfield.c b/drivers/acpi/acpica/dsfield.c
index 20de148594fd..6bca0ec42dbd 100644
--- a/drivers/acpi/acpica/dsfield.c
+++ b/drivers/acpi/acpica/dsfield.c
@@ -106,6 +106,7 @@ acpi_ds_create_external_region(acpi_status lookup_status,
* insert the name into the namespace.
*/
acpi_dm_add_op_to_external_list(op, path, ACPI_TYPE_REGION, 0, 0);
+
status = acpi_ns_lookup(walk_state->scope_info, path, ACPI_TYPE_REGION,
ACPI_IMODE_LOAD_PASS1, ACPI_NS_SEARCH_PARENT,
walk_state, node);
@@ -202,11 +203,10 @@ acpi_ds_create_buffer_field(union acpi_parse_object *op,
/* Enter the name_string into the namespace */
- status =
- acpi_ns_lookup(walk_state->scope_info,
- arg->common.value.string, ACPI_TYPE_ANY,
- ACPI_IMODE_LOAD_PASS1, flags, walk_state,
- &node);
+ status = acpi_ns_lookup(walk_state->scope_info,
+ arg->common.value.string, ACPI_TYPE_ANY,
+ ACPI_IMODE_LOAD_PASS1, flags,
+ walk_state, &node);
if (ACPI_FAILURE(status)) {
ACPI_ERROR_NAMESPACE(arg->common.value.string, status);
return_ACPI_STATUS(status);
@@ -244,8 +244,8 @@ acpi_ds_create_buffer_field(union acpi_parse_object *op,
}
/*
- * Remember location in AML stream of the field unit opcode and operands --
- * since the buffer and index operands must be evaluated.
+ * Remember location in AML stream of the field unit opcode and operands
+ * -- since the buffer and index operands must be evaluated.
*/
second_desc = obj_desc->common.next_object;
second_desc->extra.aml_start = op->named.data;
@@ -310,8 +310,8 @@ acpi_ds_get_field_names(struct acpi_create_field_info *info,
switch (arg->common.aml_opcode) {
case AML_INT_RESERVEDFIELD_OP:
- position = (u64) info->field_bit_position
- + (u64) arg->common.value.size;
+ position = (u64)info->field_bit_position +
+ (u64)arg->common.value.size;
if (position > ACPI_UINT32_MAX) {
ACPI_ERROR((AE_INFO,
@@ -344,13 +344,13 @@ acpi_ds_get_field_names(struct acpi_create_field_info *info,
/* access_attribute (attrib_quick, attrib_byte, etc.) */
- info->attribute =
- (u8)((arg->common.value.integer >> 8) & 0xFF);
+ info->attribute = (u8)
+ ((arg->common.value.integer >> 8) & 0xFF);
/* access_length (for serial/buffer protocols) */
- info->access_length =
- (u8)((arg->common.value.integer >> 16) & 0xFF);
+ info->access_length = (u8)
+ ((arg->common.value.integer >> 16) & 0xFF);
break;
case AML_INT_CONNECTION_OP:
@@ -425,8 +425,8 @@ acpi_ds_get_field_names(struct acpi_create_field_info *info,
/* Keep track of bit position for the next field */
- position = (u64) info->field_bit_position
- + (u64) arg->common.value.size;
+ position = (u64)info->field_bit_position +
+ (u64)arg->common.value.size;
if (position > ACPI_UINT32_MAX) {
ACPI_ERROR((AE_INFO,
@@ -716,11 +716,12 @@ acpi_ds_create_bank_field(union acpi_parse_object *op,
/*
* Use Info.data_register_node to store bank_field Op
- * It's safe because data_register_node will never be used when create bank field
- * We store aml_start and aml_length in the bank_field Op for late evaluation
- * Used in acpi_ex_prep_field_value(Info)
+ * It's safe because data_register_node will never be used when create
+ * bank field \we store aml_start and aml_length in the bank_field Op for
+ * late evaluation. Used in acpi_ex_prep_field_value(Info)
*
- * TBD: Or, should we add a field in struct acpi_create_field_info, like "void *ParentOp"?
+ * TBD: Or, should we add a field in struct acpi_create_field_info, like
+ * "void *ParentOp"?
*/
info.data_register_node = (struct acpi_namespace_node *)op;
diff --git a/drivers/acpi/acpica/dsinit.c b/drivers/acpi/acpica/dsinit.c
index 920f1b199bc6..c1d8af8a8aaf 100644
--- a/drivers/acpi/acpica/dsinit.c
+++ b/drivers/acpi/acpica/dsinit.c
@@ -247,7 +247,7 @@ acpi_ds_initialize_objects(u32 table_index,
/* Summary of objects initialized */
ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT,
- "Table [%4.4s:%8.8s] (id %.2X) - %4u Objects with %3u Devices, "
+ "Table [%4.4s: %-8.8s] (id %.2X) - %4u Objects with %3u Devices, "
"%3u Regions, %4u Methods (%u/%u/%u Serial/Non/Cvt)\n",
table->signature, table->oem_table_id, owner_id,
info.object_count, info.device_count,
diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c
index bc32f3194afe..6585e8e37c8e 100644
--- a/drivers/acpi/acpica/dsmethod.c
+++ b/drivers/acpi/acpica/dsmethod.c
@@ -118,10 +118,9 @@ acpi_ds_auto_serialize_method(struct acpi_namespace_node *node,
return_ACPI_STATUS(AE_NO_MEMORY);
}
- status =
- acpi_ds_init_aml_walk(walk_state, op, node,
- obj_desc->method.aml_start,
- obj_desc->method.aml_length, NULL, 0);
+ status = acpi_ds_init_aml_walk(walk_state, op, node,
+ obj_desc->method.aml_start,
+ obj_desc->method.aml_length, NULL, 0);
if (ACPI_FAILURE(status)) {
acpi_ds_delete_walk_state(walk_state);
acpi_ps_free_op(op);
@@ -375,7 +374,8 @@ acpi_ds_begin_method_execution(struct acpi_namespace_node *method_node,
&& (walk_state->thread->current_sync_level >
obj_desc->method.mutex->mutex.sync_level)) {
ACPI_ERROR((AE_INFO,
- "Cannot acquire Mutex for method [%4.4s], current SyncLevel is too large (%u)",
+ "Cannot acquire Mutex for method [%4.4s]"
+ ", current SyncLevel is too large (%u)",
acpi_ut_get_node_name(method_node),
walk_state->thread->current_sync_level));
@@ -411,8 +411,19 @@ acpi_ds_begin_method_execution(struct acpi_namespace_node *method_node,
obj_desc->method.mutex->mutex.thread_id =
walk_state->thread->thread_id;
- walk_state->thread->current_sync_level =
- obj_desc->method.sync_level;
+
+ /*
+ * Update the current sync_level only if this is not an auto-
+ * serialized method. In the auto case, we have to ignore
+ * the sync level for the method mutex (created for the
+ * auto-serialization) because we have no idea of what the
+ * sync level should be. Therefore, just ignore it.
+ */
+ if (!(obj_desc->method.info_flags &
+ ACPI_METHOD_IGNORE_SYNC_LEVEL)) {
+ walk_state->thread->current_sync_level =
+ obj_desc->method.sync_level;
+ }
} else {
obj_desc->method.mutex->mutex.
original_sync_level =
@@ -501,16 +512,18 @@ acpi_ds_call_control_method(struct acpi_thread_state *thread,
/* Init for new method, possibly wait on method mutex */
- status = acpi_ds_begin_method_execution(method_node, obj_desc,
- this_walk_state);
+ status =
+ acpi_ds_begin_method_execution(method_node, obj_desc,
+ this_walk_state);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/* Begin method parse/execution. Create a new walk state */
- next_walk_state = acpi_ds_create_walk_state(obj_desc->method.owner_id,
- NULL, obj_desc, thread);
+ next_walk_state =
+ acpi_ds_create_walk_state(obj_desc->method.owner_id, NULL, obj_desc,
+ thread);
if (!next_walk_state) {
status = AE_NO_MEMORY;
goto cleanup;
@@ -797,7 +810,8 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc,
info_flags & ACPI_METHOD_SERIALIZED_PENDING) {
if (walk_state) {
ACPI_INFO((AE_INFO,
- "Marking method %4.4s as Serialized because of AE_ALREADY_EXISTS error",
+ "Marking method %4.4s as Serialized "
+ "because of AE_ALREADY_EXISTS error",
walk_state->method_node->name.
ascii));
}
@@ -815,6 +829,7 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc,
*/
method_desc->method.info_flags &=
~ACPI_METHOD_SERIALIZED_PENDING;
+
method_desc->method.info_flags |=
(ACPI_METHOD_SERIALIZED |
ACPI_METHOD_IGNORE_SYNC_LEVEL);
diff --git a/drivers/acpi/acpica/dsmthdat.c b/drivers/acpi/acpica/dsmthdat.c
index 2e4c42b377ec..03c44f2ac7b7 100644
--- a/drivers/acpi/acpica/dsmthdat.c
+++ b/drivers/acpi/acpica/dsmthdat.c
@@ -99,6 +99,7 @@ void acpi_ds_method_data_init(struct acpi_walk_state *walk_state)
for (i = 0; i < ACPI_METHOD_NUM_ARGS; i++) {
ACPI_MOVE_32_TO_32(&walk_state->arguments[i].name,
NAMEOF_ARG_NTE);
+
walk_state->arguments[i].name.integer |= (i << 24);
walk_state->arguments[i].descriptor_type = ACPI_DESC_TYPE_NAMED;
walk_state->arguments[i].type = ACPI_TYPE_ANY;
@@ -201,7 +202,7 @@ acpi_ds_method_data_init_args(union acpi_operand_object **params,
if (!params) {
ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
- "No param list passed to method\n"));
+ "No parameter list passed to method\n"));
return_ACPI_STATUS(AE_OK);
}
@@ -214,9 +215,9 @@ acpi_ds_method_data_init_args(union acpi_operand_object **params,
* Store the argument in the method/walk descriptor.
* Do not copy the arg in order to implement call by reference
*/
- status = acpi_ds_method_data_set_value(ACPI_REFCLASS_ARG, index,
- params[index],
- walk_state);
+ status =
+ acpi_ds_method_data_set_value(ACPI_REFCLASS_ARG, index,
+ params[index], walk_state);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
@@ -610,11 +611,11 @@ acpi_ds_store_object_to_local(u8 type,
* do the indirect store
*/
if ((ACPI_GET_DESCRIPTOR_TYPE(current_obj_desc) ==
- ACPI_DESC_TYPE_OPERAND)
- && (current_obj_desc->common.type ==
- ACPI_TYPE_LOCAL_REFERENCE)
- && (current_obj_desc->reference.class ==
- ACPI_REFCLASS_REFOF)) {
+ ACPI_DESC_TYPE_OPERAND) &&
+ (current_obj_desc->common.type ==
+ ACPI_TYPE_LOCAL_REFERENCE) &&
+ (current_obj_desc->reference.class ==
+ ACPI_REFCLASS_REFOF)) {
ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
"Arg (%p) is an ObjRef(Node), storing in node %p\n",
new_obj_desc,
@@ -638,6 +639,7 @@ acpi_ds_store_object_to_local(u8 type,
if (new_obj_desc != obj_desc) {
acpi_ut_remove_reference(new_obj_desc);
}
+
return_ACPI_STATUS(status);
}
}
diff --git a/drivers/acpi/acpica/dsobject.c b/drivers/acpi/acpica/dsobject.c
index 2beb7fd674ae..302c91f5377b 100644
--- a/drivers/acpi/acpica/dsobject.c
+++ b/drivers/acpi/acpica/dsobject.c
@@ -463,10 +463,10 @@ acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state,
arg->common.node);
}
} else {
- status = acpi_ds_build_internal_object(walk_state, arg,
- &obj_desc->
- package.
- elements[i]);
+ status =
+ acpi_ds_build_internal_object(walk_state, arg,
+ &obj_desc->package.
+ elements[i]);
}
if (*obj_desc_ptr) {
@@ -525,7 +525,8 @@ acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state,
}
ACPI_INFO((AE_INFO,
- "Actual Package length (%u) is larger than NumElements field (%u), truncated",
+ "Actual Package length (%u) is larger than "
+ "NumElements field (%u), truncated",
i, element_count));
} else if (i < element_count) {
/*
@@ -533,7 +534,8 @@ acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state,
* Note: this is not an error, the package is padded out with NULLs.
*/
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "Package List length (%u) smaller than NumElements count (%u), padded with null elements\n",
+ "Package List length (%u) smaller than NumElements "
+ "count (%u), padded with null elements\n",
i, element_count));
}
@@ -584,8 +586,9 @@ acpi_ds_create_node(struct acpi_walk_state *walk_state,
/* Build an internal object for the argument(s) */
- status = acpi_ds_build_internal_object(walk_state, op->common.value.arg,
- &obj_desc);
+ status =
+ acpi_ds_build_internal_object(walk_state, op->common.value.arg,
+ &obj_desc);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c
index 81d7b9863e32..1edd66f18907 100644
--- a/drivers/acpi/acpica/dsopcode.c
+++ b/drivers/acpi/acpica/dsopcode.c
@@ -243,8 +243,9 @@ acpi_ds_init_buffer_field(u16 aml_opcode,
* For field_flags, use LOCK_RULE = 0 (NO_LOCK),
* UPDATE_RULE = 0 (UPDATE_PRESERVE)
*/
- status = acpi_ex_prep_common_field_object(obj_desc, field_flags, 0,
- bit_offset, bit_count);
+ status =
+ acpi_ex_prep_common_field_object(obj_desc, field_flags, 0,
+ bit_offset, bit_count);
if (ACPI_FAILURE(status)) {
goto cleanup;
}
@@ -330,8 +331,9 @@ acpi_ds_eval_buffer_field_operands(struct acpi_walk_state *walk_state,
/* Resolve the operands */
- status = acpi_ex_resolve_operands(op->common.aml_opcode,
- ACPI_WALK_OPERANDS, walk_state);
+ status =
+ acpi_ex_resolve_operands(op->common.aml_opcode, ACPI_WALK_OPERANDS,
+ walk_state);
if (ACPI_FAILURE(status)) {
ACPI_ERROR((AE_INFO, "(%s) bad operand(s), status 0x%X",
acpi_ps_get_opcode_name(op->common.aml_opcode),
@@ -414,8 +416,9 @@ acpi_ds_eval_region_operands(struct acpi_walk_state *walk_state,
/* Resolve the length and address operands to numbers */
- status = acpi_ex_resolve_operands(op->common.aml_opcode,
- ACPI_WALK_OPERANDS, walk_state);
+ status =
+ acpi_ex_resolve_operands(op->common.aml_opcode, ACPI_WALK_OPERANDS,
+ walk_state);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
@@ -452,7 +455,6 @@ acpi_ds_eval_region_operands(struct acpi_walk_state *walk_state,
/* Now the address and length are valid for this opregion */
obj_desc->region.flags |= AOPOBJ_DATA_VALID;
-
return_ACPI_STATUS(status);
}
@@ -510,8 +512,9 @@ acpi_ds_eval_table_region_operands(struct acpi_walk_state *walk_state,
* Resolve the Signature string, oem_id string,
* and oem_table_id string operands
*/
- status = acpi_ex_resolve_operands(op->common.aml_opcode,
- ACPI_WALK_OPERANDS, walk_state);
+ status =
+ acpi_ex_resolve_operands(op->common.aml_opcode, ACPI_WALK_OPERANDS,
+ walk_state);
if (ACPI_FAILURE(status)) {
goto cleanup;
}
diff --git a/drivers/acpi/acpica/dsutils.c b/drivers/acpi/acpica/dsutils.c
index ebc577baeaf9..fa8e2920a3ef 100644
--- a/drivers/acpi/acpica/dsutils.c
+++ b/drivers/acpi/acpica/dsutils.c
@@ -245,9 +245,9 @@ acpi_ds_is_result_used(union acpi_parse_object * op,
* we will use the return value
*/
if ((walk_state->control_state->common.state ==
- ACPI_CONTROL_PREDICATE_EXECUTING)
- && (walk_state->control_state->control.
- predicate_op == op)) {
+ ACPI_CONTROL_PREDICATE_EXECUTING) &&
+ (walk_state->control_state->control.predicate_op ==
+ op)) {
goto result_used;
}
break;
@@ -481,10 +481,9 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
/* Get the entire name string from the AML stream */
- status =
- acpi_ex_get_name_string(ACPI_TYPE_ANY,
- arg->common.value.buffer,
- &name_string, &name_length);
+ status = acpi_ex_get_name_string(ACPI_TYPE_ANY,
+ arg->common.value.buffer,
+ &name_string, &name_length);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
@@ -503,9 +502,8 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
*/
if ((walk_state->deferred_node) &&
(walk_state->deferred_node->type == ACPI_TYPE_BUFFER_FIELD)
- && (arg_index ==
- (u32) ((walk_state->opcode ==
- AML_CREATE_FIELD_OP) ? 3 : 2))) {
+ && (arg_index == (u32)
+ ((walk_state->opcode == AML_CREATE_FIELD_OP) ? 3 : 2))) {
obj_desc =
ACPI_CAST_PTR(union acpi_operand_object,
walk_state->deferred_node);
@@ -522,9 +520,10 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
op_info =
acpi_ps_get_opcode_info(parent_op->common.
aml_opcode);
- if ((op_info->flags & AML_NSNODE)
- && (parent_op->common.aml_opcode !=
- AML_INT_METHODCALL_OP)
+
+ if ((op_info->flags & AML_NSNODE) &&
+ (parent_op->common.aml_opcode !=
+ AML_INT_METHODCALL_OP)
&& (parent_op->common.aml_opcode != AML_REGION_OP)
&& (parent_op->common.aml_opcode !=
AML_INT_NAMEPATH_OP)) {
@@ -605,8 +604,8 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
- ACPI_DEBUGGER_EXEC(acpi_db_display_argument_object
- (obj_desc, walk_state));
+
+ acpi_db_display_argument_object(obj_desc, walk_state);
} else {
/* Check for null name case */
@@ -633,15 +632,16 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
return_ACPI_STATUS(AE_NOT_IMPLEMENTED);
}
- if ((op_info->flags & AML_HAS_RETVAL)
- || (arg->common.flags & ACPI_PARSEOP_IN_STACK)) {
+ if ((op_info->flags & AML_HAS_RETVAL) ||
+ (arg->common.flags & ACPI_PARSEOP_IN_STACK)) {
ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
"Argument previously created, already stacked\n"));
- ACPI_DEBUGGER_EXEC(acpi_db_display_argument_object
- (walk_state->
- operands[walk_state->num_operands -
- 1], walk_state));
+ acpi_db_display_argument_object(walk_state->
+ operands[walk_state->
+ num_operands -
+ 1],
+ walk_state);
/*
* Use value that was already previously returned
@@ -685,8 +685,7 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
return_ACPI_STATUS(status);
}
- ACPI_DEBUGGER_EXEC(acpi_db_display_argument_object
- (obj_desc, walk_state));
+ acpi_db_display_argument_object(obj_desc, walk_state);
}
return_ACPI_STATUS(AE_OK);
diff --git a/drivers/acpi/acpica/dswexec.c b/drivers/acpi/acpica/dswexec.c
index df54d46225cd..ed2f1d362092 100644
--- a/drivers/acpi/acpica/dswexec.c
+++ b/drivers/acpi/acpica/dswexec.c
@@ -172,14 +172,14 @@ acpi_ds_get_predicate_value(struct acpi_walk_state *walk_state,
cleanup:
- ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Completed a predicate eval=%X Op=%p\n",
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Completed a predicate eval=%X Op=%p\n",
walk_state->control_state->common.value,
walk_state->op));
/* Break to debugger to display result */
- ACPI_DEBUGGER_EXEC(acpi_db_display_result_object
- (local_obj_desc, walk_state));
+ acpi_db_display_result_object(local_obj_desc, walk_state);
/*
* Delete the predicate result object (we know that
@@ -264,8 +264,8 @@ acpi_ds_exec_begin_op(struct acpi_walk_state *walk_state,
(walk_state->control_state->common.state ==
ACPI_CONTROL_CONDITIONAL_EXECUTING)) {
ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
- "Exec predicate Op=%p State=%p\n", op,
- walk_state));
+ "Exec predicate Op=%p State=%p\n",
+ op, walk_state));
walk_state->control_state->common.state =
ACPI_CONTROL_PREDICATE_EXECUTING;
@@ -386,11 +386,10 @@ acpi_status acpi_ds_exec_end_op(struct acpi_walk_state *walk_state)
/* Call debugger for single step support (DEBUG build only) */
- ACPI_DEBUGGER_EXEC(status =
- acpi_db_single_step(walk_state, op, op_class));
- ACPI_DEBUGGER_EXEC(if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);}
- ) ;
+ status = acpi_db_single_step(walk_state, op, op_class);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
/* Decode the Opcode Class */
@@ -502,9 +501,8 @@ acpi_status acpi_ds_exec_end_op(struct acpi_walk_state *walk_state)
"Method Reference in a Package, Op=%p\n",
op));
- op->common.node =
- (struct acpi_namespace_node *)op->asl.value.
- arg->asl.node;
+ op->common.node = (struct acpi_namespace_node *)
+ op->asl.value.arg->asl.node;
acpi_ut_add_reference(op->asl.value.arg->asl.
node->object);
return_ACPI_STATUS(AE_OK);
@@ -586,8 +584,8 @@ acpi_status acpi_ds_exec_end_op(struct acpi_walk_state *walk_state)
* Put the Node on the object stack (Contains the ACPI Name
* of this object)
*/
- walk_state->operands[0] =
- (void *)op->common.parent->common.node;
+ walk_state->operands[0] = (void *)
+ op->common.parent->common.node;
walk_state->num_operands = 1;
status = acpi_ds_create_node(walk_state,
@@ -692,7 +690,8 @@ acpi_status acpi_ds_exec_end_op(struct acpi_walk_state *walk_state)
default:
ACPI_ERROR((AE_INFO,
- "Unimplemented opcode, class=0x%X type=0x%X Opcode=0x%X Op=%p",
+ "Unimplemented opcode, class=0x%X "
+ "type=0x%X Opcode=0x%X Op=%p",
op_class, op_type, op->common.aml_opcode,
op));
@@ -728,8 +727,8 @@ cleanup:
/* Break to debugger to display result */
- ACPI_DEBUGGER_EXEC(acpi_db_display_result_object
- (walk_state->result_obj, walk_state));
+ acpi_db_display_result_object(walk_state->result_obj,
+ walk_state);
/*
* Delete the result op if and only if:
diff --git a/drivers/acpi/acpica/dswload.c b/drivers/acpi/acpica/dswload.c
index 097188a6b1c1..b3254742aaf6 100644
--- a/drivers/acpi/acpica/dswload.c
+++ b/drivers/acpi/acpica/dswload.c
@@ -476,13 +476,9 @@ acpi_status acpi_ds_load1_end_op(struct acpi_walk_state *walk_state)
status =
acpi_ex_create_region(op->named.data,
op->named.length,
- (acpi_adr_space_type) ((op->
- common.
- value.
- arg)->
- common.
- value.
- integer),
+ (acpi_adr_space_type)
+ ((op->common.value.arg)->
+ common.value.integer),
walk_state);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
diff --git a/drivers/acpi/acpica/dswload2.c b/drivers/acpi/acpica/dswload2.c
index e2c08cd79aca..8a32153a111b 100644
--- a/drivers/acpi/acpica/dswload2.c
+++ b/drivers/acpi/acpica/dswload2.c
@@ -598,11 +598,10 @@ acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state)
* Executing a method: initialize the region and unlock
* the interpreter
*/
- status =
- acpi_ex_create_region(op->named.data,
- op->named.length,
- region_space,
- walk_state);
+ status = acpi_ex_create_region(op->named.data,
+ op->named.length,
+ region_space,
+ walk_state);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
@@ -664,6 +663,7 @@ acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state)
length,
walk_state);
}
+
walk_state->operands[0] = NULL;
walk_state->num_operands = 0;
diff --git a/drivers/acpi/acpica/dswscope.c b/drivers/acpi/acpica/dswscope.c
index 43b3ea40c0b6..2d7a04493469 100644
--- a/drivers/acpi/acpica/dswscope.c
+++ b/drivers/acpi/acpica/dswscope.c
@@ -77,6 +77,7 @@ void acpi_ds_scope_stack_clear(struct acpi_walk_state *walk_state)
"Popped object type (%s)\n",
acpi_ut_get_type_name(scope_info->common.
value)));
+
acpi_ut_delete_generic_state(scope_info);
}
}
diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c
index ccf793247447..112e821a1cec 100644
--- a/drivers/acpi/acpica/evgpe.c
+++ b/drivers/acpi/acpica/evgpe.c
@@ -92,8 +92,8 @@ acpi_ev_update_gpe_enable_mask(struct acpi_gpe_event_info *gpe_event_info)
ACPI_SET_BIT(gpe_register_info->enable_for_run,
(u8)register_bit);
}
- gpe_register_info->enable_mask = gpe_register_info->enable_for_run;
+ gpe_register_info->enable_mask = gpe_register_info->enable_for_run;
return_ACPI_STATUS(AE_OK);
}
diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c
index e0f24c504513..c00a9f2f82d5 100644
--- a/drivers/acpi/acpica/evgpeblk.c
+++ b/drivers/acpi/acpica/evgpeblk.c
@@ -167,6 +167,7 @@ acpi_status acpi_ev_delete_gpe_block(struct acpi_gpe_block_info *gpe_block)
if (gpe_block->next) {
gpe_block->next->previous = gpe_block->previous;
}
+
acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
}
diff --git a/drivers/acpi/acpica/evgpeutil.c b/drivers/acpi/acpica/evgpeutil.c
index 3a958f3612fe..fd5ab9012238 100644
--- a/drivers/acpi/acpica/evgpeutil.c
+++ b/drivers/acpi/acpica/evgpeutil.c
@@ -346,6 +346,7 @@ acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
ACPI_FREE(notify);
notify = next;
}
+
gpe_event_info->dispatch.notify_list = NULL;
gpe_event_info->flags &=
~ACPI_GPE_DISPATCH_MASK;
diff --git a/drivers/acpi/acpica/evhandler.c b/drivers/acpi/acpica/evhandler.c
index 74e8595f5a2b..709419c7cde4 100644
--- a/drivers/acpi/acpica/evhandler.c
+++ b/drivers/acpi/acpica/evhandler.c
@@ -159,7 +159,7 @@ acpi_ev_has_default_handler(struct acpi_namespace_node *node,
obj_desc = acpi_ns_get_attached_object(node);
if (obj_desc) {
- handler_obj = obj_desc->device.handler;
+ handler_obj = obj_desc->common_notify.handler;
/* Walk the linked list of handlers for this object */
@@ -247,35 +247,31 @@ acpi_ev_install_handler(acpi_handle obj_handle,
/* Check if this Device already has a handler for this address space */
- next_handler_obj = obj_desc->device.handler;
- while (next_handler_obj) {
+ next_handler_obj =
+ acpi_ev_find_region_handler(handler_obj->address_space.
+ space_id,
+ obj_desc->common_notify.
+ handler);
+ if (next_handler_obj) {
/* Found a handler, is it for the same address space? */
- if (next_handler_obj->address_space.space_id ==
- handler_obj->address_space.space_id) {
- ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
- "Found handler for region [%s] in device %p(%p) "
- "handler %p\n",
- acpi_ut_get_region_name
- (handler_obj->address_space.
- space_id), obj_desc,
- next_handler_obj,
- handler_obj));
-
- /*
- * Since the object we found it on was a device, then it
- * means that someone has already installed a handler for
- * the branch of the namespace from this device on. Just
- * bail out telling the walk routine to not traverse this
- * branch. This preserves the scoping rule for handlers.
- */
- return (AE_CTRL_DEPTH);
- }
-
- /* Walk the linked list of handlers attached to this device */
-
- next_handler_obj = next_handler_obj->address_space.next;
+ ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
+ "Found handler for region [%s] in device %p(%p) handler %p\n",
+ acpi_ut_get_region_name(handler_obj->
+ address_space.
+ space_id),
+ obj_desc, next_handler_obj,
+ handler_obj));
+
+ /*
+ * Since the object we found it on was a device, then it means
+ * that someone has already installed a handler for the branch
+ * of the namespace from this device on. Just bail out telling
+ * the walk routine to not traverse this branch. This preserves
+ * the scoping rule for handlers.
+ */
+ return (AE_CTRL_DEPTH);
}
/*
@@ -309,6 +305,44 @@ acpi_ev_install_handler(acpi_handle obj_handle,
/*******************************************************************************
*
+ * FUNCTION: acpi_ev_find_region_handler
+ *
+ * PARAMETERS: space_id - The address space ID
+ * handler_obj - Head of the handler object list
+ *
+ * RETURN: Matching handler object. NULL if space ID not matched
+ *
+ * DESCRIPTION: Search a handler object list for a match on the address
+ * space ID.
+ *
+ ******************************************************************************/
+
+union acpi_operand_object *acpi_ev_find_region_handler(acpi_adr_space_type
+ space_id,
+ union acpi_operand_object
+ *handler_obj)
+{
+
+ /* Walk the handler list for this device */
+
+ while (handler_obj) {
+
+ /* Same space_id indicates a handler is installed */
+
+ if (handler_obj->address_space.space_id == space_id) {
+ return (handler_obj);
+ }
+
+ /* Next handler object */
+
+ handler_obj = handler_obj->address_space.next;
+ }
+
+ return (NULL);
+}
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_ev_install_space_handler
*
* PARAMETERS: node - Namespace node for the device
@@ -332,15 +366,15 @@ acpi_ev_install_space_handler(struct acpi_namespace_node * node,
{
union acpi_operand_object *obj_desc;
union acpi_operand_object *handler_obj;
- acpi_status status;
+ acpi_status status = AE_OK;
acpi_object_type type;
u8 flags = 0;
ACPI_FUNCTION_TRACE(ev_install_space_handler);
/*
- * This registration is valid for only the types below and the root. This
- * is where the default handlers get placed.
+ * This registration is valid for only the types below and the root.
+ * The root node is where the default handlers get installed.
*/
if ((node->type != ACPI_TYPE_DEVICE) &&
(node->type != ACPI_TYPE_PROCESSOR) &&
@@ -407,38 +441,30 @@ acpi_ev_install_space_handler(struct acpi_namespace_node * node,
obj_desc = acpi_ns_get_attached_object(node);
if (obj_desc) {
/*
- * The attached device object already exists. Make sure the handler
- * is not already installed.
+ * The attached device object already exists. Now make sure
+ * the handler is not already installed.
*/
- handler_obj = obj_desc->device.handler;
-
- /* Walk the handler list for this device */
-
- while (handler_obj) {
-
- /* Same space_id indicates a handler already installed */
+ handler_obj = acpi_ev_find_region_handler(space_id,
+ obj_desc->
+ common_notify.
+ handler);
- if (handler_obj->address_space.space_id == space_id) {
- if (handler_obj->address_space.handler ==
- handler) {
- /*
- * It is (relatively) OK to attempt to install the SAME
- * handler twice. This can easily happen with the
- * PCI_Config space.
- */
- status = AE_SAME_HANDLER;
- goto unlock_and_exit;
- } else {
- /* A handler is already installed */
-
- status = AE_ALREADY_EXISTS;
- }
+ if (handler_obj) {
+ if (handler_obj->address_space.handler == handler) {
+ /*
+ * It is (relatively) OK to attempt to install the SAME
+ * handler twice. This can easily happen with the
+ * PCI_Config space.
+ */
+ status = AE_SAME_HANDLER;
goto unlock_and_exit;
- }
+ } else {
+ /* A handler is already installed */
- /* Walk the linked list of handlers */
+ status = AE_ALREADY_EXISTS;
+ }
- handler_obj = handler_obj->address_space.next;
+ goto unlock_and_exit;
}
} else {
ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
@@ -477,7 +503,8 @@ acpi_ev_install_space_handler(struct acpi_namespace_node * node,
}
ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
- "Installing address handler for region %s(%X) on Device %4.4s %p(%p)\n",
+ "Installing address handler for region %s(%X) "
+ "on Device %4.4s %p(%p)\n",
acpi_ut_get_region_name(space_id), space_id,
acpi_ut_get_node_name(node), node, obj_desc));
@@ -506,28 +533,26 @@ acpi_ev_install_space_handler(struct acpi_namespace_node * node,
/* Install at head of Device.address_space list */
- handler_obj->address_space.next = obj_desc->device.handler;
+ handler_obj->address_space.next = obj_desc->common_notify.handler;
/*
* The Device object is the first reference on the handler_obj.
* Each region that uses the handler adds a reference.
*/
- obj_desc->device.handler = handler_obj;
+ obj_desc->common_notify.handler = handler_obj;
/*
- * Walk the namespace finding all of the regions this
- * handler will manage.
+ * Walk the namespace finding all of the regions this handler will
+ * manage.
*
- * Start at the device and search the branch toward
- * the leaf nodes until either the leaf is encountered or
- * a device is detected that has an address handler of the
- * same type.
+ * Start at the device and search the branch toward the leaf nodes
+ * until either the leaf is encountered or a device is detected that
+ * has an address handler of the same type.
*
- * In either case, back up and search down the remainder
- * of the branch
+ * In either case, back up and search down the remainder of the branch
*/
- status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, node, ACPI_UINT32_MAX,
- ACPI_NS_WALK_UNLOCK,
+ status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, node,
+ ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK,
acpi_ev_install_handler, NULL,
handler_obj, NULL);
diff --git a/drivers/acpi/acpica/evmisc.c b/drivers/acpi/acpica/evmisc.c
index f7c9dfe7b990..8866f50d38f7 100644
--- a/drivers/acpi/acpica/evmisc.c
+++ b/drivers/acpi/acpica/evmisc.c
@@ -68,6 +68,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_notify_dispatch(void *context);
u8 acpi_ev_is_notify_object(struct acpi_namespace_node *node)
{
+
switch (node->type) {
case ACPI_TYPE_DEVICE:
case ACPI_TYPE_PROCESSOR:
@@ -170,8 +171,8 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node * node,
acpi_ut_get_notify_name(notify_value, ACPI_TYPE_ANY),
node));
- status = acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_ev_notify_dispatch,
- info);
+ status = acpi_os_execute(OSL_NOTIFY_HANDLER,
+ acpi_ev_notify_dispatch, info);
if (ACPI_FAILURE(status)) {
acpi_ut_delete_generic_state(info);
}
diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c
index 5ee79a16fe33..a43178f20c59 100644
--- a/drivers/acpi/acpica/evregion.c
+++ b/drivers/acpi/acpica/evregion.c
@@ -97,15 +97,12 @@ acpi_status acpi_ev_initialize_op_regions(void)
if (acpi_ev_has_default_handler(acpi_gbl_root_node,
acpi_gbl_default_address_spaces
[i])) {
- status =
- acpi_ev_execute_reg_methods(acpi_gbl_root_node,
- acpi_gbl_default_address_spaces
- [i]);
+ acpi_ev_execute_reg_methods(acpi_gbl_root_node,
+ acpi_gbl_default_address_spaces
+ [i], ACPI_REG_CONNECT);
}
}
- acpi_gbl_reg_methods_executed = TRUE;
-
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
return_ACPI_STATUS(status);
}
@@ -127,6 +124,12 @@ acpi_status acpi_ev_initialize_op_regions(void)
* DESCRIPTION: Dispatch an address space or operation region access to
* a previously installed handler.
*
+ * NOTE: During early initialization, we always install the default region
+ * handlers for Memory, I/O and PCI_Config. This ensures that these operation
+ * region address spaces are always available as per the ACPI specification.
+ * This is especially needed in order to support the execution of
+ * module-level AML code during loading of the ACPI tables.
+ *
******************************************************************************/
acpi_status
@@ -498,6 +501,12 @@ acpi_ev_attach_region(union acpi_operand_object *handler_obj,
ACPI_FUNCTION_TRACE(ev_attach_region);
+ /* Install the region's handler */
+
+ if (region_obj->region.handler) {
+ return_ACPI_STATUS(AE_ALREADY_EXISTS);
+ }
+
ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
"Adding Region [%4.4s] %p to address handler %p [%s]\n",
acpi_ut_get_node_name(region_obj->region.node),
@@ -509,17 +518,56 @@ acpi_ev_attach_region(union acpi_operand_object *handler_obj,
region_obj->region.next = handler_obj->address_space.region_list;
handler_obj->address_space.region_list = region_obj;
+ region_obj->region.handler = handler_obj;
+ acpi_ut_add_reference(handler_obj);
- /* Install the region's handler */
+ return_ACPI_STATUS(AE_OK);
+}
- if (region_obj->region.handler) {
- return_ACPI_STATUS(AE_ALREADY_EXISTS);
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ev_associate_reg_method
+ *
+ * PARAMETERS: region_obj - Region object
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Find and associate _REG method to a region
+ *
+ ******************************************************************************/
+
+void acpi_ev_associate_reg_method(union acpi_operand_object *region_obj)
+{
+ acpi_name *reg_name_ptr = (acpi_name *) METHOD_NAME__REG;
+ struct acpi_namespace_node *method_node;
+ struct acpi_namespace_node *node;
+ union acpi_operand_object *region_obj2;
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE(ev_associate_reg_method);
+
+ region_obj2 = acpi_ns_get_secondary_object(region_obj);
+ if (!region_obj2) {
+ return_VOID;
}
- region_obj->region.handler = handler_obj;
- acpi_ut_add_reference(handler_obj);
+ node = region_obj->region.node->parent;
- return_ACPI_STATUS(AE_OK);
+ /* Find any "_REG" method associated with this region definition */
+
+ status =
+ acpi_ns_search_one_scope(*reg_name_ptr, node, ACPI_TYPE_METHOD,
+ &method_node);
+ if (ACPI_SUCCESS(status)) {
+ /*
+ * The _REG method is optional and there can be only one per region
+ * definition. This will be executed when the handler is attached
+ * or removed
+ */
+ region_obj2->extra.method_REG = method_node;
+ }
+
+ return_VOID;
}
/*******************************************************************************
@@ -550,7 +598,18 @@ acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function)
return_ACPI_STATUS(AE_NOT_EXIST);
}
- if (region_obj2->extra.method_REG == NULL) {
+ if (region_obj2->extra.method_REG == NULL ||
+ region_obj->region.handler == NULL ||
+ !acpi_gbl_reg_methods_enabled) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* _REG(DISCONNECT) should be paired with _REG(CONNECT) */
+
+ if ((function == ACPI_REG_CONNECT &&
+ region_obj->common.flags & AOPOBJ_REG_CONNECTED) ||
+ (function == ACPI_REG_DISCONNECT &&
+ !(region_obj->common.flags & AOPOBJ_REG_CONNECTED))) {
return_ACPI_STATUS(AE_OK);
}
@@ -599,6 +658,16 @@ acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function)
status = acpi_ns_evaluate(info);
acpi_ut_remove_reference(args[1]);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup2;
+ }
+
+ if (function == ACPI_REG_CONNECT) {
+ region_obj->common.flags |= AOPOBJ_REG_CONNECTED;
+ } else {
+ region_obj->common.flags &= ~AOPOBJ_REG_CONNECTED;
+ }
+
cleanup2:
acpi_ut_remove_reference(args[0]);
@@ -613,24 +682,25 @@ cleanup1:
*
* PARAMETERS: node - Namespace node for the device
* space_id - The address space ID
+ * function - Passed to _REG: On (1) or Off (0)
*
- * RETURN: Status
+ * RETURN: None
*
* DESCRIPTION: Run all _REG methods for the input Space ID;
* Note: assumes namespace is locked, or system init time.
*
******************************************************************************/
-acpi_status
+void
acpi_ev_execute_reg_methods(struct acpi_namespace_node *node,
- acpi_adr_space_type space_id)
+ acpi_adr_space_type space_id, u32 function)
{
- acpi_status status;
struct acpi_reg_walk_info info;
ACPI_FUNCTION_TRACE(ev_execute_reg_methods);
info.space_id = space_id;
+ info.function = function;
info.reg_run_count = 0;
ACPI_DEBUG_PRINT_RAW((ACPI_DB_NAMES,
@@ -643,9 +713,9 @@ acpi_ev_execute_reg_methods(struct acpi_namespace_node *node,
* regions and _REG methods. (i.e. handlers must be installed for all
* regions of this Space ID before we can run any _REG methods)
*/
- status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, node, ACPI_UINT32_MAX,
- ACPI_NS_WALK_UNLOCK, acpi_ev_reg_run,
- NULL, &info, NULL);
+ (void)acpi_ns_walk_namespace(ACPI_TYPE_ANY, node, ACPI_UINT32_MAX,
+ ACPI_NS_WALK_UNLOCK, acpi_ev_reg_run, NULL,
+ &info, NULL);
/* Special case for EC: handle "orphan" _REG methods with no region */
@@ -658,7 +728,7 @@ acpi_ev_execute_reg_methods(struct acpi_namespace_node *node,
info.reg_run_count,
acpi_ut_get_region_name(info.space_id)));
- return_ACPI_STATUS(status);
+ return_VOID;
}
/*******************************************************************************
@@ -717,7 +787,7 @@ acpi_ev_reg_run(acpi_handle obj_handle,
}
info->reg_run_count++;
- status = acpi_ev_execute_reg_method(obj_desc, ACPI_REG_CONNECT);
+ status = acpi_ev_execute_reg_method(obj_desc, info->function);
return (status);
}
diff --git a/drivers/acpi/acpica/evrgnini.c b/drivers/acpi/acpica/evrgnini.c
index da323390bb70..bb2e529249c7 100644
--- a/drivers/acpi/acpica/evrgnini.c
+++ b/drivers/acpi/acpica/evrgnini.c
@@ -507,9 +507,6 @@ acpi_ev_initialize_region(union acpi_operand_object *region_obj,
acpi_adr_space_type space_id;
struct acpi_namespace_node *node;
acpi_status status;
- struct acpi_namespace_node *method_node;
- acpi_name *reg_name_ptr = (acpi_name *) METHOD_NAME__REG;
- union acpi_operand_object *region_obj2;
ACPI_FUNCTION_TRACE_U32(ev_initialize_region, acpi_ns_locked);
@@ -521,38 +518,15 @@ acpi_ev_initialize_region(union acpi_operand_object *region_obj,
return_ACPI_STATUS(AE_OK);
}
- region_obj2 = acpi_ns_get_secondary_object(region_obj);
- if (!region_obj2) {
- return_ACPI_STATUS(AE_NOT_EXIST);
- }
+ acpi_ev_associate_reg_method(region_obj);
+ region_obj->common.flags |= AOPOBJ_OBJECT_INITIALIZED;
node = region_obj->region.node->parent;
space_id = region_obj->region.space_id;
- /* Setup defaults */
-
- region_obj->region.handler = NULL;
- region_obj2->extra.method_REG = NULL;
- region_obj->common.flags &= ~(AOPOBJ_SETUP_COMPLETE);
- region_obj->common.flags |= AOPOBJ_OBJECT_INITIALIZED;
-
- /* Find any "_REG" method associated with this region definition */
-
- status =
- acpi_ns_search_one_scope(*reg_name_ptr, node, ACPI_TYPE_METHOD,
- &method_node);
- if (ACPI_SUCCESS(status)) {
- /*
- * The _REG method is optional and there can be only one per region
- * definition. This will be executed when the handler is attached
- * or removed
- */
- region_obj2->extra.method_REG = method_node;
- }
-
/*
* The following loop depends upon the root Node having no parent
- * ie: acpi_gbl_root_node->parent_entry being set to NULL
+ * ie: acpi_gbl_root_node->Parent being set to NULL
*/
while (node) {
@@ -566,18 +540,10 @@ acpi_ev_initialize_region(union acpi_operand_object *region_obj,
switch (node->type) {
case ACPI_TYPE_DEVICE:
-
- handler_obj = obj_desc->device.handler;
- break;
-
case ACPI_TYPE_PROCESSOR:
-
- handler_obj = obj_desc->processor.handler;
- break;
-
case ACPI_TYPE_THERMAL:
- handler_obj = obj_desc->thermal_zone.handler;
+ handler_obj = obj_desc->common_notify.handler;
break;
case ACPI_TYPE_METHOD:
@@ -602,60 +568,49 @@ acpi_ev_initialize_region(union acpi_operand_object *region_obj,
break;
}
- while (handler_obj) {
-
- /* Is this handler of the correct type? */
+ handler_obj =
+ acpi_ev_find_region_handler(space_id, handler_obj);
+ if (handler_obj) {
- if (handler_obj->address_space.space_id ==
- space_id) {
+ /* Found correct handler */
- /* Found correct handler */
+ ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
+ "Found handler %p for region %p in obj %p\n",
+ handler_obj, region_obj,
+ obj_desc));
- ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
- "Found handler %p for region %p in obj %p\n",
- handler_obj,
+ status =
+ acpi_ev_attach_region(handler_obj,
region_obj,
- obj_desc));
+ acpi_ns_locked);
+ /*
+ * Tell all users that this region is usable by
+ * running the _REG method
+ */
+ if (acpi_ns_locked) {
status =
- acpi_ev_attach_region(handler_obj,
- region_obj,
- acpi_ns_locked);
-
- /*
- * Tell all users that this region is usable by
- * running the _REG method
- */
- if (acpi_ns_locked) {
- status =
- acpi_ut_release_mutex
- (ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS
- (status);
- }
+ acpi_ut_release_mutex
+ (ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
}
+ }
+ status =
+ acpi_ev_execute_reg_method(region_obj,
+ ACPI_REG_CONNECT);
+
+ if (acpi_ns_locked) {
status =
- acpi_ev_execute_reg_method
- (region_obj, ACPI_REG_CONNECT);
-
- if (acpi_ns_locked) {
- status =
- acpi_ut_acquire_mutex
- (ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS
- (status);
- }
+ acpi_ut_acquire_mutex
+ (ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
}
-
- return_ACPI_STATUS(AE_OK);
}
- /* Try next handler in the list */
-
- handler_obj = handler_obj->address_space.next;
+ return_ACPI_STATUS(AE_OK);
}
}
diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c
index 07d22bfbaa00..012b9dedfa79 100644
--- a/drivers/acpi/acpica/evxface.c
+++ b/drivers/acpi/acpica/evxface.c
@@ -879,9 +879,8 @@ acpi_install_gpe_handler(acpi_handle gpe_device,
ACPI_FUNCTION_TRACE(acpi_install_gpe_handler);
- status =
- acpi_ev_install_gpe_handler(gpe_device, gpe_number, type, FALSE,
- address, context);
+ status = acpi_ev_install_gpe_handler(gpe_device, gpe_number, type,
+ FALSE, address, context);
return_ACPI_STATUS(status);
}
@@ -914,8 +913,8 @@ acpi_install_gpe_raw_handler(acpi_handle gpe_device,
ACPI_FUNCTION_TRACE(acpi_install_gpe_raw_handler);
- status = acpi_ev_install_gpe_handler(gpe_device, gpe_number, type, TRUE,
- address, context);
+ status = acpi_ev_install_gpe_handler(gpe_device, gpe_number, type,
+ TRUE, address, context);
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/evxfregn.c b/drivers/acpi/acpica/evxfregn.c
index f21afbab03f7..35f9e60ce2b7 100644
--- a/drivers/acpi/acpica/evxfregn.c
+++ b/drivers/acpi/acpica/evxfregn.c
@@ -112,41 +112,9 @@ acpi_install_address_space_handler(acpi_handle device,
goto unlock_and_exit;
}
- /*
- * For the default space_IDs, (the IDs for which there are default region handlers
- * installed) Only execute the _REG methods if the global initialization _REG
- * methods have already been run (via acpi_initialize_objects). In other words,
- * we will defer the execution of the _REG methods for these space_IDs until
- * execution of acpi_initialize_objects. This is done because we need the handlers
- * for the default spaces (mem/io/pci/table) to be installed before we can run
- * any control methods (or _REG methods). There is known BIOS code that depends
- * on this.
- *
- * For all other space_IDs, we can safely execute the _REG methods immediately.
- * This means that for IDs like embedded_controller, this function should be called
- * only after acpi_enable_subsystem has been called.
- */
- switch (space_id) {
- case ACPI_ADR_SPACE_SYSTEM_MEMORY:
- case ACPI_ADR_SPACE_SYSTEM_IO:
- case ACPI_ADR_SPACE_PCI_CONFIG:
- case ACPI_ADR_SPACE_DATA_TABLE:
-
- if (!acpi_gbl_reg_methods_executed) {
-
- /* We will defer execution of the _REG methods for this space */
- goto unlock_and_exit;
- }
- break;
-
- default:
-
- break;
- }
-
/* Run all _REG methods for this address space */
- status = acpi_ev_execute_reg_methods(node, space_id);
+ acpi_ev_execute_reg_methods(node, space_id, ACPI_REG_CONNECT);
unlock_and_exit:
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
@@ -215,8 +183,8 @@ acpi_remove_address_space_handler(acpi_handle device,
/* Find the address handler the user requested */
- handler_obj = obj_desc->device.handler;
- last_obj_ptr = &obj_desc->device.handler;
+ handler_obj = obj_desc->common_notify.handler;
+ last_obj_ptr = &obj_desc->common_notify.handler;
while (handler_obj) {
/* We have a handler, see if user requested this one */
diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c
index b540913c11ac..adcb9c7029c4 100644
--- a/drivers/acpi/acpica/exconfig.c
+++ b/drivers/acpi/acpica/exconfig.c
@@ -358,8 +358,8 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
}
/*
- * If the Region Address and Length have not been previously evaluated,
- * evaluate them now and save the results.
+ * If the Region Address and Length have not been previously
+ * evaluated, evaluate them now and save the results.
*/
if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
status = acpi_ds_get_region_arguments(obj_desc);
@@ -454,8 +454,8 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
}
/*
- * Copy the table from the buffer because the buffer could be modified
- * or even deleted in the future
+ * Copy the table from the buffer because the buffer could be
+ * modified or even deleted in the future
*/
table = ACPI_ALLOCATE(length);
if (!table) {
diff --git a/drivers/acpi/acpica/exconvrt.c b/drivers/acpi/acpica/exconvrt.c
index 1e4c5b6dc0b0..73c2e823488d 100644
--- a/drivers/acpi/acpica/exconvrt.c
+++ b/drivers/acpi/acpica/exconvrt.c
@@ -227,8 +227,8 @@ acpi_ex_convert_to_buffer(union acpi_operand_object *obj_desc,
/* Copy the integer to the buffer, LSB first */
new_buf = return_desc->buffer.pointer;
- memcpy(new_buf,
- &obj_desc->integer.value, acpi_gbl_integer_byte_width);
+ memcpy(new_buf, &obj_desc->integer.value,
+ acpi_gbl_integer_byte_width);
break;
case ACPI_TYPE_STRING:
@@ -354,9 +354,8 @@ acpi_ex_convert_to_ascii(u64 integer, u16 base, u8 *string, u8 data_width)
/* Get one hex digit, most significant digits first */
- string[k] =
- (u8) acpi_ut_hex_to_ascii_char(integer,
- ACPI_MUL_4(j));
+ string[k] = (u8)
+ acpi_ut_hex_to_ascii_char(integer, ACPI_MUL_4(j));
k++;
}
break;
diff --git a/drivers/acpi/acpica/excreate.c b/drivers/acpi/acpica/excreate.c
index ccb7219bdcee..46be5a276863 100644
--- a/drivers/acpi/acpica/excreate.c
+++ b/drivers/acpi/acpica/excreate.c
@@ -189,9 +189,9 @@ acpi_status acpi_ex_create_event(struct acpi_walk_state *walk_state)
/* Attach object to the Node */
- status =
- acpi_ns_attach_object((struct acpi_namespace_node *)walk_state->
- operands[0], obj_desc, ACPI_TYPE_EVENT);
+ status = acpi_ns_attach_object((struct acpi_namespace_node *)
+ walk_state->operands[0], obj_desc,
+ ACPI_TYPE_EVENT);
cleanup:
/*
@@ -326,9 +326,10 @@ acpi_ex_create_region(u8 * aml_start,
* Remember location in AML stream of address & length
* operands since they need to be evaluated at run time.
*/
- region_obj2 = obj_desc->common.next_object;
+ region_obj2 = acpi_ns_get_secondary_object(obj_desc);
region_obj2->extra.aml_start = aml_start;
region_obj2->extra.aml_length = aml_length;
+ region_obj2->extra.method_REG = NULL;
if (walk_state->scope_info) {
region_obj2->extra.scope_node =
walk_state->scope_info->scope.node;
@@ -342,6 +343,10 @@ acpi_ex_create_region(u8 * aml_start,
obj_desc->region.address = 0;
obj_desc->region.length = 0;
obj_desc->region.node = node;
+ obj_desc->region.handler = NULL;
+ obj_desc->common.flags &=
+ ~(AOPOBJ_SETUP_COMPLETE | AOPOBJ_REG_CONNECTED |
+ AOPOBJ_OBJECT_INITIALIZED);
/* Install the new region object in the parent Node */
@@ -492,10 +497,9 @@ acpi_ex_create_method(u8 * aml_start,
* Disassemble the method flags. Split off the arg_count, Serialized
* flag, and sync_level for efficiency.
*/
- method_flags = (u8) operand[1]->integer.value;
-
- obj_desc->method.param_count =
- (u8) (method_flags & AML_METHOD_ARG_COUNT);
+ method_flags = (u8)operand[1]->integer.value;
+ obj_desc->method.param_count = (u8)
+ (method_flags & AML_METHOD_ARG_COUNT);
/*
* Get the sync_level. If method is serialized, a mutex will be
diff --git a/drivers/acpi/acpica/exdebug.c b/drivers/acpi/acpica/exdebug.c
index de92458236f5..b22309094c5f 100644
--- a/drivers/acpi/acpica/exdebug.c
+++ b/drivers/acpi/acpica/exdebug.c
@@ -43,21 +43,11 @@
#include <acpi/acpi.h>
#include "accommon.h"
-#include "acnamesp.h"
#include "acinterp.h"
-#include "acparser.h"
#define _COMPONENT ACPI_EXECUTER
ACPI_MODULE_NAME("exdebug")
-static union acpi_operand_object *acpi_gbl_trace_method_object = NULL;
-
-/* Local prototypes */
-
-#ifdef ACPI_DEBUG_OUTPUT
-static const char *acpi_ex_get_trace_event_name(acpi_trace_event_type type);
-#endif
-
#ifndef ACPI_NO_ERROR_MESSAGES
/*******************************************************************************
*
@@ -80,7 +70,6 @@ static const char *acpi_ex_get_trace_event_name(acpi_trace_event_type type);
* enabled if necessary.
*
******************************************************************************/
-
void
acpi_ex_do_debug_object(union acpi_operand_object *source_desc,
u32 level, u32 index)
@@ -99,20 +88,40 @@ acpi_ex_do_debug_object(union acpi_operand_object *source_desc,
return_VOID;
}
- /*
- * We will emit the current timer value (in microseconds) with each
- * debug output. Only need the lower 26 bits. This allows for 67
- * million microseconds or 67 seconds before rollover.
- */
- timer = ((u32)acpi_os_get_timer() / 10); /* (100 nanoseconds to microseconds) */
- timer &= 0x03FFFFFF;
+ /* Null string or newline -- don't emit the line header */
+
+ if (source_desc &&
+ (ACPI_GET_DESCRIPTOR_TYPE(source_desc) == ACPI_DESC_TYPE_OPERAND) &&
+ (source_desc->common.type == ACPI_TYPE_STRING)) {
+ if ((source_desc->string.length == 0) ||
+ ((source_desc->string.length == 1) &&
+ (*source_desc->string.pointer == '\n'))) {
+ acpi_os_printf("\n");
+ return_VOID;
+ }
+ }
/*
* Print line header as long as we are not in the middle of an
* object display
*/
if (!((level > 0) && index == 0)) {
- acpi_os_printf("[ACPI Debug %.8u] %*s", timer, level, " ");
+ if (acpi_gbl_display_debug_timer) {
+ /*
+ * We will emit the current timer value (in microseconds) with each
+ * debug output. Only need the lower 26 bits. This allows for 67
+ * million microseconds or 67 seconds before rollover.
+ *
+ * Convert 100 nanosecond units to microseconds
+ */
+ timer = ((u32)acpi_os_get_timer() / 10);
+ timer &= 0x03FFFFFF;
+
+ acpi_os_printf("[ACPI Debug T=0x%8.8X] %*s", timer,
+ level, " ");
+ } else {
+ acpi_os_printf("[ACPI Debug] %*s", level, " ");
+ }
}
/* Display the index for package output only */
@@ -127,8 +136,15 @@ acpi_ex_do_debug_object(union acpi_operand_object *source_desc,
}
if (ACPI_GET_DESCRIPTOR_TYPE(source_desc) == ACPI_DESC_TYPE_OPERAND) {
- acpi_os_printf("%s ",
- acpi_ut_get_object_type_name(source_desc));
+
+ /* No object type prefix needed for integers and strings */
+
+ if ((source_desc->common.type != ACPI_TYPE_INTEGER) &&
+ (source_desc->common.type != ACPI_TYPE_STRING)) {
+ acpi_os_printf("%s ",
+ acpi_ut_get_object_type_name
+ (source_desc));
+ }
if (!acpi_ut_valid_internal_object(source_desc)) {
acpi_os_printf("%p, Invalid Internal Object!\n",
@@ -137,7 +153,7 @@ acpi_ex_do_debug_object(union acpi_operand_object *source_desc,
}
} else if (ACPI_GET_DESCRIPTOR_TYPE(source_desc) ==
ACPI_DESC_TYPE_NAMED) {
- acpi_os_printf("%s: %p\n",
+ acpi_os_printf("%s (Node %p)\n",
acpi_ut_get_type_name(((struct
acpi_namespace_node *)
source_desc)->type),
@@ -175,14 +191,12 @@ acpi_ex_do_debug_object(union acpi_operand_object *source_desc,
case ACPI_TYPE_STRING:
- acpi_os_printf("[0x%.2X] \"%s\"\n",
- source_desc->string.length,
- source_desc->string.pointer);
+ acpi_os_printf("\"%s\"\n", source_desc->string.pointer);
break;
case ACPI_TYPE_PACKAGE:
- acpi_os_printf("[Contains 0x%.2X Elements]\n",
+ acpi_os_printf("(Contains 0x%.2X Elements):\n",
source_desc->package.count);
/* Output the entire contents of the package */
@@ -261,11 +275,14 @@ acpi_ex_do_debug_object(union acpi_operand_object *source_desc,
if (ACPI_GET_DESCRIPTOR_TYPE
(source_desc->reference.object) ==
ACPI_DESC_TYPE_NAMED) {
- acpi_ex_do_debug_object(((struct
- acpi_namespace_node *)
+
+ /* Reference object is a namespace node */
+
+ acpi_ex_do_debug_object(ACPI_CAST_PTR
+ (union
+ acpi_operand_object,
source_desc->reference.
- object)->object,
- level + 4, 0);
+ object), level + 4, 0);
} else {
object_desc = source_desc->reference.object;
value = source_desc->reference.value;
@@ -293,9 +310,14 @@ acpi_ex_do_debug_object(union acpi_operand_object *source_desc,
case ACPI_TYPE_PACKAGE:
acpi_os_printf("Package[%u] = ", value);
- acpi_ex_do_debug_object(*source_desc->
- reference.where,
- level + 4, 0);
+ if (!(*source_desc->reference.where)) {
+ acpi_os_printf
+ ("[Uninitialized Package Element]\n");
+ } else {
+ acpi_ex_do_debug_object
+ (*source_desc->reference.
+ where, level + 4, 0);
+ }
break;
default:
@@ -311,7 +333,7 @@ acpi_ex_do_debug_object(union acpi_operand_object *source_desc,
default:
- acpi_os_printf("%p\n", source_desc);
+ acpi_os_printf("(Descriptor %p)\n", source_desc);
break;
}
@@ -319,316 +341,3 @@ acpi_ex_do_debug_object(union acpi_operand_object *source_desc,
return_VOID;
}
#endif
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_ex_interpreter_trace_enabled
- *
- * PARAMETERS: name - Whether method name should be matched,
- * this should be checked before starting
- * the tracer
- *
- * RETURN: TRUE if interpreter trace is enabled.
- *
- * DESCRIPTION: Check whether interpreter trace is enabled
- *
- ******************************************************************************/
-
-static u8 acpi_ex_interpreter_trace_enabled(char *name)
-{
-
- /* Check if tracing is enabled */
-
- if (!(acpi_gbl_trace_flags & ACPI_TRACE_ENABLED)) {
- return (FALSE);
- }
-
- /*
- * Check if tracing is filtered:
- *
- * 1. If the tracer is started, acpi_gbl_trace_method_object should have
- * been filled by the trace starter
- * 2. If the tracer is not started, acpi_gbl_trace_method_name should be
- * matched if it is specified
- * 3. If the tracer is oneshot style, acpi_gbl_trace_method_name should
- * not be cleared by the trace stopper during the first match
- */
- if (acpi_gbl_trace_method_object) {
- return (TRUE);
- }
- if (name &&
- (acpi_gbl_trace_method_name &&
- strcmp(acpi_gbl_trace_method_name, name))) {
- return (FALSE);
- }
- if ((acpi_gbl_trace_flags & ACPI_TRACE_ONESHOT) &&
- !acpi_gbl_trace_method_name) {
- return (FALSE);
- }
-
- return (TRUE);
-}
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_ex_get_trace_event_name
- *
- * PARAMETERS: type - Trace event type
- *
- * RETURN: Trace event name.
- *
- * DESCRIPTION: Used to obtain the full trace event name.
- *
- ******************************************************************************/
-
-#ifdef ACPI_DEBUG_OUTPUT
-
-static const char *acpi_ex_get_trace_event_name(acpi_trace_event_type type)
-{
- switch (type) {
- case ACPI_TRACE_AML_METHOD:
-
- return "Method";
-
- case ACPI_TRACE_AML_OPCODE:
-
- return "Opcode";
-
- case ACPI_TRACE_AML_REGION:
-
- return "Region";
-
- default:
-
- return "";
- }
-}
-
-#endif
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_ex_trace_point
- *
- * PARAMETERS: type - Trace event type
- * begin - TRUE if before execution
- * aml - Executed AML address
- * pathname - Object path
- *
- * RETURN: None
- *
- * DESCRIPTION: Internal interpreter execution trace.
- *
- ******************************************************************************/
-
-void
-acpi_ex_trace_point(acpi_trace_event_type type,
- u8 begin, u8 *aml, char *pathname)
-{
-
- ACPI_FUNCTION_NAME(ex_trace_point);
-
- if (pathname) {
- ACPI_DEBUG_PRINT((ACPI_DB_TRACE_POINT,
- "%s %s [0x%p:%s] execution.\n",
- acpi_ex_get_trace_event_name(type),
- begin ? "Begin" : "End", aml, pathname));
- } else {
- ACPI_DEBUG_PRINT((ACPI_DB_TRACE_POINT,
- "%s %s [0x%p] execution.\n",
- acpi_ex_get_trace_event_name(type),
- begin ? "Begin" : "End", aml));
- }
-}
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_ex_start_trace_method
- *
- * PARAMETERS: method_node - Node of the method
- * obj_desc - The method object
- * walk_state - current state, NULL if not yet executing
- * a method.
- *
- * RETURN: None
- *
- * DESCRIPTION: Start control method execution trace
- *
- ******************************************************************************/
-
-void
-acpi_ex_start_trace_method(struct acpi_namespace_node *method_node,
- union acpi_operand_object *obj_desc,
- struct acpi_walk_state *walk_state)
-{
- acpi_status status;
- char *pathname = NULL;
- u8 enabled = FALSE;
-
- ACPI_FUNCTION_NAME(ex_start_trace_method);
-
- if (method_node) {
- pathname = acpi_ns_get_normalized_pathname(method_node, TRUE);
- }
-
- status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE(status)) {
- goto exit;
- }
-
- enabled = acpi_ex_interpreter_trace_enabled(pathname);
- if (enabled && !acpi_gbl_trace_method_object) {
- acpi_gbl_trace_method_object = obj_desc;
- acpi_gbl_original_dbg_level = acpi_dbg_level;
- acpi_gbl_original_dbg_layer = acpi_dbg_layer;
- acpi_dbg_level = ACPI_TRACE_LEVEL_ALL;
- acpi_dbg_layer = ACPI_TRACE_LAYER_ALL;
-
- if (acpi_gbl_trace_dbg_level) {
- acpi_dbg_level = acpi_gbl_trace_dbg_level;
- }
- if (acpi_gbl_trace_dbg_layer) {
- acpi_dbg_layer = acpi_gbl_trace_dbg_layer;
- }
- }
- (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
-
-exit:
- if (enabled) {
- ACPI_TRACE_POINT(ACPI_TRACE_AML_METHOD, TRUE,
- obj_desc ? obj_desc->method.aml_start : NULL,
- pathname);
- }
- if (pathname) {
- ACPI_FREE(pathname);
- }
-}
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_ex_stop_trace_method
- *
- * PARAMETERS: method_node - Node of the method
- * obj_desc - The method object
- * walk_state - current state, NULL if not yet executing
- * a method.
- *
- * RETURN: None
- *
- * DESCRIPTION: Stop control method execution trace
- *
- ******************************************************************************/
-
-void
-acpi_ex_stop_trace_method(struct acpi_namespace_node *method_node,
- union acpi_operand_object *obj_desc,
- struct acpi_walk_state *walk_state)
-{
- acpi_status status;
- char *pathname = NULL;
- u8 enabled;
-
- ACPI_FUNCTION_NAME(ex_stop_trace_method);
-
- if (method_node) {
- pathname = acpi_ns_get_normalized_pathname(method_node, TRUE);
- }
-
- status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE(status)) {
- goto exit_path;
- }
-
- enabled = acpi_ex_interpreter_trace_enabled(NULL);
-
- (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
-
- if (enabled) {
- ACPI_TRACE_POINT(ACPI_TRACE_AML_METHOD, FALSE,
- obj_desc ? obj_desc->method.aml_start : NULL,
- pathname);
- }
-
- status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE(status)) {
- goto exit_path;
- }
-
- /* Check whether the tracer should be stopped */
-
- if (acpi_gbl_trace_method_object == obj_desc) {
-
- /* Disable further tracing if type is one-shot */
-
- if (acpi_gbl_trace_flags & ACPI_TRACE_ONESHOT) {
- acpi_gbl_trace_method_name = NULL;
- }
-
- acpi_dbg_level = acpi_gbl_original_dbg_level;
- acpi_dbg_layer = acpi_gbl_original_dbg_layer;
- acpi_gbl_trace_method_object = NULL;
- }
-
- (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
-
-exit_path:
- if (pathname) {
- ACPI_FREE(pathname);
- }
-}
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_ex_start_trace_opcode
- *
- * PARAMETERS: op - The parser opcode object
- * walk_state - current state, NULL if not yet executing
- * a method.
- *
- * RETURN: None
- *
- * DESCRIPTION: Start opcode execution trace
- *
- ******************************************************************************/
-
-void
-acpi_ex_start_trace_opcode(union acpi_parse_object *op,
- struct acpi_walk_state *walk_state)
-{
-
- ACPI_FUNCTION_NAME(ex_start_trace_opcode);
-
- if (acpi_ex_interpreter_trace_enabled(NULL) &&
- (acpi_gbl_trace_flags & ACPI_TRACE_OPCODE)) {
- ACPI_TRACE_POINT(ACPI_TRACE_AML_OPCODE, TRUE,
- op->common.aml, op->common.aml_op_name);
- }
-}
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_ex_stop_trace_opcode
- *
- * PARAMETERS: op - The parser opcode object
- * walk_state - current state, NULL if not yet executing
- * a method.
- *
- * RETURN: None
- *
- * DESCRIPTION: Stop opcode execution trace
- *
- ******************************************************************************/
-
-void
-acpi_ex_stop_trace_opcode(union acpi_parse_object *op,
- struct acpi_walk_state *walk_state)
-{
-
- ACPI_FUNCTION_NAME(ex_stop_trace_opcode);
-
- if (acpi_ex_interpreter_trace_enabled(NULL) &&
- (acpi_gbl_trace_flags & ACPI_TRACE_OPCODE)) {
- ACPI_TRACE_POINT(ACPI_TRACE_AML_OPCODE, FALSE,
- op->common.aml, op->common.aml_op_name);
- }
-}
diff --git a/drivers/acpi/acpica/exdump.c b/drivers/acpi/acpica/exdump.c
index d836f888bb16..ff976c43b992 100644
--- a/drivers/acpi/acpica/exdump.c
+++ b/drivers/acpi/acpica/exdump.c
@@ -508,7 +508,8 @@ acpi_ex_dump_object(union acpi_operand_object *obj_desc,
if (next) {
acpi_os_printf("(%s %2.2X)",
acpi_ut_get_object_type_name
- (next), next->common.type);
+ (next),
+ next->address_space.space_id);
while (next->address_space.next) {
if ((next->common.type ==
@@ -520,7 +521,8 @@ acpi_ex_dump_object(union acpi_operand_object *obj_desc,
acpi_os_printf("->%p(%s %2.2X)", next,
acpi_ut_get_object_type_name
(next),
- next->common.type);
+ next->address_space.
+ space_id);
if ((next == start) || (next == data)) {
acpi_os_printf
diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c
index 61fd9c7b88bc..ad7080ba65e2 100644
--- a/drivers/acpi/acpica/exfield.c
+++ b/drivers/acpi/acpica/exfield.c
@@ -167,10 +167,11 @@ acpi_ex_read_data_from_field(struct acpi_walk_state * walk_state,
|| obj_desc->field.region_obj->region.space_id ==
ACPI_ADR_SPACE_IPMI)) {
/*
- * This is an SMBus, GSBus or IPMI read. We must create a buffer to hold
- * the data and then directly access the region handler.
+ * This is an SMBus, GSBus or IPMI read. We must create a buffer to
+ * hold the data and then directly access the region handler.
*
- * Note: SMBus and GSBus protocol value is passed in upper 16-bits of Function
+ * Note: SMBus and GSBus protocol value is passed in upper 16-bits
+ * of Function
*/
if (obj_desc->field.region_obj->region.space_id ==
ACPI_ADR_SPACE_SMBUS) {
@@ -180,17 +181,17 @@ acpi_ex_read_data_from_field(struct acpi_walk_state * walk_state,
} else if (obj_desc->field.region_obj->region.space_id ==
ACPI_ADR_SPACE_GSBUS) {
accessor_type = obj_desc->field.attribute;
- length = acpi_ex_get_serial_access_length(accessor_type,
- obj_desc->
- field.
- access_length);
+ length =
+ acpi_ex_get_serial_access_length(accessor_type,
+ obj_desc->field.
+ access_length);
/*
* Add additional 2 bytes for the generic_serial_bus data buffer:
*
- * Status; (Byte 0 of the data buffer)
- * Length; (Byte 1 of the data buffer)
- * Data[x-1]; (Bytes 2-x of the arbitrary length data buffer)
+ * Status; (Byte 0 of the data buffer)
+ * Length; (Byte 1 of the data buffer)
+ * Data[x-1]: (Bytes 2-x of the arbitrary length data buffer)
*/
length += 2;
function = ACPI_READ | (accessor_type << 16);
@@ -216,6 +217,7 @@ acpi_ex_read_data_from_field(struct acpi_walk_state * walk_state,
buffer_desc->
buffer.pointer),
function);
+
acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
goto exit;
}
@@ -232,6 +234,7 @@ acpi_ex_read_data_from_field(struct acpi_walk_state * walk_state,
*/
length =
(acpi_size) ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->field.bit_length);
+
if (length > acpi_gbl_integer_byte_width) {
/* Field is too large for an Integer, create a Buffer instead */
@@ -273,8 +276,10 @@ acpi_ex_read_data_from_field(struct acpi_walk_state * walk_state,
/* Perform the write */
- status = acpi_ex_access_region(obj_desc, 0,
- (u64 *)buffer, ACPI_READ);
+ status =
+ acpi_ex_access_region(obj_desc, 0, (u64 *)buffer,
+ ACPI_READ);
+
acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
if (ACPI_FAILURE(status)) {
acpi_ut_remove_reference(buffer_desc);
@@ -366,19 +371,22 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc,
|| obj_desc->field.region_obj->region.space_id ==
ACPI_ADR_SPACE_IPMI)) {
/*
- * This is an SMBus, GSBus or IPMI write. We will bypass the entire field
- * mechanism and handoff the buffer directly to the handler. For
- * these address spaces, the buffer is bi-directional; on a write,
- * return data is returned in the same buffer.
+ * This is an SMBus, GSBus or IPMI write. We will bypass the entire
+ * field mechanism and handoff the buffer directly to the handler.
+ * For these address spaces, the buffer is bi-directional; on a
+ * write, return data is returned in the same buffer.
*
* Source must be a buffer of sufficient size:
- * ACPI_SMBUS_BUFFER_SIZE, ACPI_GSBUS_BUFFER_SIZE, or ACPI_IPMI_BUFFER_SIZE.
+ * ACPI_SMBUS_BUFFER_SIZE, ACPI_GSBUS_BUFFER_SIZE, or
+ * ACPI_IPMI_BUFFER_SIZE.
*
- * Note: SMBus and GSBus protocol type is passed in upper 16-bits of Function
+ * Note: SMBus and GSBus protocol type is passed in upper 16-bits
+ * of Function
*/
if (source_desc->common.type != ACPI_TYPE_BUFFER) {
ACPI_ERROR((AE_INFO,
- "SMBus/IPMI/GenericSerialBus write requires Buffer, found type %s",
+ "SMBus/IPMI/GenericSerialBus write requires "
+ "Buffer, found type %s",
acpi_ut_get_object_type_name(source_desc)));
return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
@@ -392,17 +400,17 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc,
} else if (obj_desc->field.region_obj->region.space_id ==
ACPI_ADR_SPACE_GSBUS) {
accessor_type = obj_desc->field.attribute;
- length = acpi_ex_get_serial_access_length(accessor_type,
- obj_desc->
- field.
- access_length);
+ length =
+ acpi_ex_get_serial_access_length(accessor_type,
+ obj_desc->field.
+ access_length);
/*
* Add additional 2 bytes for the generic_serial_bus data buffer:
*
- * Status; (Byte 0 of the data buffer)
- * Length; (Byte 1 of the data buffer)
- * Data[x-1]; (Bytes 2-x of the arbitrary length data buffer)
+ * Status; (Byte 0 of the data buffer)
+ * Length; (Byte 1 of the data buffer)
+ * Data[x-1]: (Bytes 2-x of the arbitrary length data buffer)
*/
length += 2;
function = ACPI_WRITE | (accessor_type << 16);
@@ -414,7 +422,8 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc,
if (source_desc->buffer.length < length) {
ACPI_ERROR((AE_INFO,
- "SMBus/IPMI/GenericSerialBus write requires Buffer of length %u, found length %u",
+ "SMBus/IPMI/GenericSerialBus write requires "
+ "Buffer of length %u, found length %u",
length, source_desc->buffer.length));
return_ACPI_STATUS(AE_AML_BUFFER_LIMIT);
@@ -438,8 +447,8 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc,
* Perform the write (returns status and perhaps data in the
* same buffer)
*/
- status = acpi_ex_access_region(obj_desc, 0,
- (u64 *) buffer, function);
+ status =
+ acpi_ex_access_region(obj_desc, 0, (u64 *)buffer, function);
acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
*result_desc = buffer_desc;
@@ -460,7 +469,7 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc,
}
ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
- "GPIO FieldWrite [FROM]: (%s:%X), Val %.8X [TO]: Pin %u Bits %u\n",
+ "GPIO FieldWrite [FROM]: (%s:%X), Val %.8X [TO]: Pin %u Bits %u\n",
acpi_ut_get_type_name(source_desc->common.
type),
source_desc->common.type,
@@ -476,8 +485,9 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc,
/* Perform the write */
- status = acpi_ex_access_region(obj_desc, 0,
- (u64 *)buffer, ACPI_WRITE);
+ status =
+ acpi_ex_access_region(obj_desc, 0, (u64 *)buffer,
+ ACPI_WRITE);
acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c
index 70b7bbbb860b..0337191dbf3d 100644
--- a/drivers/acpi/acpica/exfldio.c
+++ b/drivers/acpi/acpica/exfldio.c
@@ -180,7 +180,8 @@ acpi_ex_setup_region(union acpi_operand_object *obj_desc,
* byte, and a field with Dword access specified.
*/
ACPI_ERROR((AE_INFO,
- "Field [%4.4s] access width (%u bytes) too large for region [%4.4s] (length %u)",
+ "Field [%4.4s] access width (%u bytes) "
+ "too large for region [%4.4s] (length %u)",
acpi_ut_get_node_name(obj_desc->
common_field.node),
obj_desc->common_field.access_byte_width,
@@ -194,7 +195,8 @@ acpi_ex_setup_region(union acpi_operand_object *obj_desc,
* exceeds region length, indicate an error
*/
ACPI_ERROR((AE_INFO,
- "Field [%4.4s] Base+Offset+Width %u+%u+%u is beyond end of region [%4.4s] (length %u)",
+ "Field [%4.4s] Base+Offset+Width %u+%u+%u "
+ "is beyond end of region [%4.4s] (length %u)",
acpi_ut_get_node_name(obj_desc->common_field.node),
obj_desc->common_field.base_byte_offset,
field_datum_byte_offset,
@@ -638,15 +640,15 @@ acpi_ex_write_with_update_rule(union acpi_operand_object *obj_desc,
ACPI_ERROR((AE_INFO,
"Unknown UpdateRule value: 0x%X",
- (obj_desc->common_field.
- field_flags &
+ (obj_desc->common_field.field_flags &
AML_FIELD_UPDATE_RULE_MASK)));
return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
}
}
ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
- "Mask %8.8X%8.8X, DatumOffset %X, Width %X, Value %8.8X%8.8X, MergedValue %8.8X%8.8X\n",
+ "Mask %8.8X%8.8X, DatumOffset %X, Width %X, "
+ "Value %8.8X%8.8X, MergedValue %8.8X%8.8X\n",
ACPI_FORMAT_UINT64(mask),
field_datum_byte_offset,
obj_desc->common_field.access_byte_width,
@@ -655,8 +657,9 @@ acpi_ex_write_with_update_rule(union acpi_operand_object *obj_desc,
/* Write the merged value */
- status = acpi_ex_field_datum_io(obj_desc, field_datum_byte_offset,
- &merged_value, ACPI_WRITE);
+ status =
+ acpi_ex_field_datum_io(obj_desc, field_datum_byte_offset,
+ &merged_value, ACPI_WRITE);
return_ACPI_STATUS(status);
}
@@ -764,8 +767,9 @@ acpi_ex_extract_from_field(union acpi_operand_object *obj_desc,
/* Get next input datum from the field */
field_offset += obj_desc->common_field.access_byte_width;
- status = acpi_ex_field_datum_io(obj_desc, field_offset,
- &raw_datum, ACPI_READ);
+ status =
+ acpi_ex_field_datum_io(obj_desc, field_offset, &raw_datum,
+ ACPI_READ);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
@@ -858,6 +862,7 @@ acpi_ex_insert_into_field(union acpi_operand_object *obj_desc,
new_buffer = NULL;
required_length =
ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.bit_length);
+
/*
* We must have a buffer that is at least as long as the field
* we are writing to. This is because individual fields are
@@ -932,9 +937,9 @@ acpi_ex_insert_into_field(union acpi_operand_object *obj_desc,
/* Write merged datum to the target field */
merged_datum &= mask;
- status = acpi_ex_write_with_update_rule(obj_desc, mask,
- merged_datum,
- field_offset);
+ status =
+ acpi_ex_write_with_update_rule(obj_desc, mask, merged_datum,
+ field_offset);
if (ACPI_FAILURE(status)) {
goto exit;
}
@@ -990,9 +995,9 @@ acpi_ex_insert_into_field(union acpi_operand_object *obj_desc,
/* Write the last datum to the field */
merged_datum &= mask;
- status = acpi_ex_write_with_update_rule(obj_desc,
- mask, merged_datum,
- field_offset);
+ status =
+ acpi_ex_write_with_update_rule(obj_desc, mask, merged_datum,
+ field_offset);
exit:
/* Free temporary buffer if we used one */
diff --git a/drivers/acpi/acpica/exmisc.c b/drivers/acpi/acpica/exmisc.c
index d02afece0f10..f598b3948c17 100644
--- a/drivers/acpi/acpica/exmisc.c
+++ b/drivers/acpi/acpica/exmisc.c
@@ -98,9 +98,9 @@ acpi_ex_get_object_reference(union acpi_operand_object *obj_desc,
default:
- ACPI_ERROR((AE_INFO, "Unknown Reference Class 0x%2.2X",
+ ACPI_ERROR((AE_INFO, "Invalid Reference Class 0x%2.2X",
obj_desc->reference.class));
- return_ACPI_STATUS(AE_AML_INTERNAL);
+ return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
}
break;
@@ -247,6 +247,7 @@ acpi_ex_do_concatenate(union acpi_operand_object *operand0,
union acpi_operand_object *local_operand1 = operand1;
union acpi_operand_object *return_desc;
char *new_buf;
+ const char *type_string;
acpi_status status;
ACPI_FUNCTION_TRACE(ex_do_concatenate);
@@ -266,9 +267,41 @@ acpi_ex_do_concatenate(union acpi_operand_object *operand0,
break;
case ACPI_TYPE_STRING:
+ /*
+ * Per the ACPI spec, Concatenate only supports int/str/buf.
+ * However, we support all objects here as an extension.
+ * This improves the usefulness of the Printf() macro.
+ * 12/2015.
+ */
+ switch (operand1->common.type) {
+ case ACPI_TYPE_INTEGER:
+ case ACPI_TYPE_STRING:
+ case ACPI_TYPE_BUFFER:
+
+ status =
+ acpi_ex_convert_to_string(operand1, &local_operand1,
+ ACPI_IMPLICIT_CONVERT_HEX);
+ break;
+
+ default:
+ /*
+ * Just emit a string containing the object type.
+ */
+ type_string =
+ acpi_ut_get_type_name(operand1->common.type);
+
+ local_operand1 = acpi_ut_create_string_object(((acpi_size) strlen(type_string) + 9)); /* 9 For "[Object]" */
+ if (!local_operand1) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
- status = acpi_ex_convert_to_string(operand1, &local_operand1,
- ACPI_IMPLICIT_CONVERT_HEX);
+ strcpy(local_operand1->string.pointer, "[");
+ strcat(local_operand1->string.pointer, type_string);
+ strcat(local_operand1->string.pointer, " Object]");
+ status = AE_OK;
+ break;
+ }
break;
case ACPI_TYPE_BUFFER:
@@ -347,8 +380,7 @@ acpi_ex_do_concatenate(union acpi_operand_object *operand0,
/* Concatenate the strings */
strcpy(new_buf, operand0->string.pointer);
- strcpy(new_buf + operand0->string.length,
- local_operand1->string.pointer);
+ strcat(new_buf, local_operand1->string.pointer);
break;
case ACPI_TYPE_BUFFER:
@@ -591,8 +623,9 @@ acpi_ex_do_logical_op(u16 opcode,
case ACPI_TYPE_STRING:
- status = acpi_ex_convert_to_string(operand1, &local_operand1,
- ACPI_IMPLICIT_CONVERT_HEX);
+ status =
+ acpi_ex_convert_to_string(operand1, &local_operand1,
+ ACPI_IMPLICIT_CONVERT_HEX);
break;
case ACPI_TYPE_BUFFER:
diff --git a/drivers/acpi/acpica/exmutex.c b/drivers/acpi/acpica/exmutex.c
index 472030f2b5bb..843c60ae91f6 100644
--- a/drivers/acpi/acpica/exmutex.c
+++ b/drivers/acpi/acpica/exmutex.c
@@ -185,8 +185,9 @@ acpi_ex_acquire_mutex_object(u16 timeout,
if (obj_desc == acpi_gbl_global_lock_mutex) {
status = acpi_ev_acquire_global_lock(timeout);
} else {
- status = acpi_ex_system_wait_mutex(obj_desc->mutex.os_mutex,
- timeout);
+ status =
+ acpi_ex_system_wait_mutex(obj_desc->mutex.os_mutex,
+ timeout);
}
if (ACPI_FAILURE(status)) {
@@ -243,20 +244,30 @@ acpi_ex_acquire_mutex(union acpi_operand_object *time_desc,
}
/*
- * Current sync level must be less than or equal to the sync level of the
- * mutex. This mechanism provides some deadlock prevention
+ * Current sync level must be less than or equal to the sync level
+ * of the mutex. This mechanism provides some deadlock prevention.
*/
if (walk_state->thread->current_sync_level > obj_desc->mutex.sync_level) {
ACPI_ERROR((AE_INFO,
- "Cannot acquire Mutex [%4.4s], current SyncLevel is too large (%u)",
+ "Cannot acquire Mutex [%4.4s], "
+ "current SyncLevel is too large (%u)",
acpi_ut_get_node_name(obj_desc->mutex.node),
walk_state->thread->current_sync_level));
return_ACPI_STATUS(AE_AML_MUTEX_ORDER);
}
- status = acpi_ex_acquire_mutex_object((u16) time_desc->integer.value,
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Acquiring: Mutex SyncLevel %u, Thread SyncLevel %u, "
+ "Depth %u TID %p\n",
+ obj_desc->mutex.sync_level,
+ walk_state->thread->current_sync_level,
+ obj_desc->mutex.acquisition_depth,
+ walk_state->thread));
+
+ status = acpi_ex_acquire_mutex_object((u16)time_desc->integer.value,
obj_desc,
walk_state->thread->thread_id);
+
if (ACPI_SUCCESS(status) && obj_desc->mutex.acquisition_depth == 1) {
/* Save Thread object, original/current sync levels */
@@ -272,6 +283,12 @@ acpi_ex_acquire_mutex(union acpi_operand_object *time_desc,
acpi_ex_link_mutex(obj_desc, walk_state->thread);
}
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Acquired: Mutex SyncLevel %u, Thread SyncLevel %u, Depth %u\n",
+ obj_desc->mutex.sync_level,
+ walk_state->thread->current_sync_level,
+ obj_desc->mutex.acquisition_depth));
+
return_ACPI_STATUS(status);
}
@@ -356,9 +373,9 @@ acpi_status
acpi_ex_release_mutex(union acpi_operand_object *obj_desc,
struct acpi_walk_state *walk_state)
{
- acpi_status status = AE_OK;
u8 previous_sync_level;
struct acpi_thread_state *owner_thread;
+ acpi_status status = AE_OK;
ACPI_FUNCTION_TRACE(ex_release_mutex);
@@ -409,7 +426,8 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc,
*/
if (obj_desc->mutex.sync_level != owner_thread->current_sync_level) {
ACPI_ERROR((AE_INFO,
- "Cannot release Mutex [%4.4s], SyncLevel mismatch: mutex %u current %u",
+ "Cannot release Mutex [%4.4s], SyncLevel mismatch: "
+ "mutex %u current %u",
acpi_ut_get_node_name(obj_desc->mutex.node),
obj_desc->mutex.sync_level,
walk_state->thread->current_sync_level));
@@ -424,6 +442,15 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc,
previous_sync_level =
owner_thread->acquired_mutex_list->mutex.original_sync_level;
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Releasing: Object SyncLevel %u, Thread SyncLevel %u, "
+ "Prev SyncLevel %u, Depth %u TID %p\n",
+ obj_desc->mutex.sync_level,
+ walk_state->thread->current_sync_level,
+ previous_sync_level,
+ obj_desc->mutex.acquisition_depth,
+ walk_state->thread));
+
status = acpi_ex_release_mutex_object(obj_desc);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
@@ -436,6 +463,14 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc,
owner_thread->current_sync_level = previous_sync_level;
}
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
+ "Released: Object SyncLevel %u, Thread SyncLevel, %u, "
+ "Prev SyncLevel %u, Depth %u\n",
+ obj_desc->mutex.sync_level,
+ walk_state->thread->current_sync_level,
+ previous_sync_level,
+ obj_desc->mutex.acquisition_depth));
+
return_ACPI_STATUS(status);
}
@@ -462,21 +497,17 @@ void acpi_ex_release_all_mutexes(struct acpi_thread_state *thread)
union acpi_operand_object *next = thread->acquired_mutex_list;
union acpi_operand_object *obj_desc;
- ACPI_FUNCTION_NAME(ex_release_all_mutexes);
+ ACPI_FUNCTION_TRACE(ex_release_all_mutexes);
/* Traverse the list of owned mutexes, releasing each one */
while (next) {
obj_desc = next;
- next = obj_desc->mutex.next;
-
- obj_desc->mutex.prev = NULL;
- obj_desc->mutex.next = NULL;
- obj_desc->mutex.acquisition_depth = 0;
-
ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
- "Force-releasing held mutex: %p\n",
- obj_desc));
+ "Mutex [%4.4s] force-release, SyncLevel %u Depth %u\n",
+ obj_desc->mutex.node->name.ascii,
+ obj_desc->mutex.sync_level,
+ obj_desc->mutex.acquisition_depth));
/* Release the mutex, special case for Global Lock */
@@ -489,14 +520,21 @@ void acpi_ex_release_all_mutexes(struct acpi_thread_state *thread)
acpi_os_release_mutex(obj_desc->mutex.os_mutex);
}
- /* Mark mutex unowned */
-
- obj_desc->mutex.owner_thread = NULL;
- obj_desc->mutex.thread_id = 0;
-
/* Update Thread sync_level (Last mutex is the important one) */
thread->current_sync_level =
obj_desc->mutex.original_sync_level;
+
+ /* Mark mutex unowned */
+
+ next = obj_desc->mutex.next;
+
+ obj_desc->mutex.prev = NULL;
+ obj_desc->mutex.next = NULL;
+ obj_desc->mutex.acquisition_depth = 0;
+ obj_desc->mutex.owner_thread = NULL;
+ obj_desc->mutex.thread_id = 0;
}
+
+ return_VOID;
}
diff --git a/drivers/acpi/acpica/exnames.c b/drivers/acpi/acpica/exnames.c
index 20e87813c7d7..b2e911a35866 100644
--- a/drivers/acpi/acpica/exnames.c
+++ b/drivers/acpi/acpica/exnames.c
@@ -164,8 +164,8 @@ static acpi_status acpi_ex_name_segment(u8 ** in_aml_address, char *name_string)
ACPI_FUNCTION_TRACE(ex_name_segment);
/*
- * If first character is a digit, then we know that we aren't looking at a
- * valid name segment
+ * If first character is a digit, then we know that we aren't looking
+ * at a valid name segment
*/
char_buf[0] = *aml_address;
diff --git a/drivers/acpi/acpica/exoparg1.c b/drivers/acpi/acpica/exoparg1.c
index 77930683ab7d..efe7ac319f65 100644
--- a/drivers/acpi/acpica/exoparg1.c
+++ b/drivers/acpi/acpica/exoparg1.c
@@ -484,22 +484,26 @@ acpi_status acpi_ex_opcode_1A_1T_1R(struct acpi_walk_state *walk_state)
case AML_TO_DECSTRING_OP: /* to_decimal_string (Data, Result) */
- status = acpi_ex_convert_to_string(operand[0], &return_desc,
- ACPI_EXPLICIT_CONVERT_DECIMAL);
+ status =
+ acpi_ex_convert_to_string(operand[0], &return_desc,
+ ACPI_EXPLICIT_CONVERT_DECIMAL);
if (return_desc == operand[0]) {
/* No conversion performed, add ref to handle return value */
+
acpi_ut_add_reference(return_desc);
}
break;
case AML_TO_HEXSTRING_OP: /* to_hex_string (Data, Result) */
- status = acpi_ex_convert_to_string(operand[0], &return_desc,
- ACPI_EXPLICIT_CONVERT_HEX);
+ status =
+ acpi_ex_convert_to_string(operand[0], &return_desc,
+ ACPI_EXPLICIT_CONVERT_HEX);
if (return_desc == operand[0]) {
/* No conversion performed, add ref to handle return value */
+
acpi_ut_add_reference(return_desc);
}
break;
@@ -510,17 +514,20 @@ acpi_status acpi_ex_opcode_1A_1T_1R(struct acpi_walk_state *walk_state)
if (return_desc == operand[0]) {
/* No conversion performed, add ref to handle return value */
+
acpi_ut_add_reference(return_desc);
}
break;
case AML_TO_INTEGER_OP: /* to_integer (Data, Result) */
- status = acpi_ex_convert_to_integer(operand[0], &return_desc,
- ACPI_ANY_BASE);
+ status =
+ acpi_ex_convert_to_integer(operand[0], &return_desc,
+ ACPI_ANY_BASE);
if (return_desc == operand[0]) {
/* No conversion performed, add ref to handle return value */
+
acpi_ut_add_reference(return_desc);
}
break;
@@ -679,7 +686,7 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state)
status = acpi_ex_store(return_desc, operand[0], walk_state);
break;
- case AML_TYPE_OP: /* object_type (source_object) */
+ case AML_OBJECT_TYPE_OP: /* object_type (source_object) */
/*
* Note: The operand is not resolved at this point because we want to
* get the associated object, not its value. For example, we don't
@@ -713,9 +720,9 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state)
/* Get the base object */
- status = acpi_ex_resolve_multiple(walk_state,
- operand[0], &type,
- &temp_desc);
+ status =
+ acpi_ex_resolve_multiple(walk_state, operand[0], &type,
+ &temp_desc);
if (ACPI_FAILURE(status)) {
goto cleanup;
}
@@ -759,8 +766,10 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state)
default:
ACPI_ERROR((AE_INFO,
- "Operand must be Buffer/Integer/String/Package - found type %s",
+ "Operand must be Buffer/Integer/String/Package"
+ " - found type %s",
acpi_ut_get_type_name(type)));
+
status = AE_AML_OPERAND_TYPE;
goto cleanup;
}
@@ -981,6 +990,7 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state)
"Unknown Index TargetType 0x%X in reference object %p",
operand[0]->reference.
target_type, operand[0]));
+
status = AE_AML_OPERAND_TYPE;
goto cleanup;
}
@@ -1050,6 +1060,7 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state)
ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X",
walk_state->opcode));
+
status = AE_AML_BAD_OPCODE;
goto cleanup;
}
diff --git a/drivers/acpi/acpica/exoparg2.c b/drivers/acpi/acpica/exoparg2.c
index b8944ebb1081..6dad2ca1c8c9 100644
--- a/drivers/acpi/acpica/exoparg2.c
+++ b/drivers/acpi/acpica/exoparg2.c
@@ -199,6 +199,7 @@ acpi_status acpi_ex_opcode_2A_2T_1R(struct acpi_walk_state *walk_state)
ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X",
walk_state->opcode));
+
status = AE_AML_BAD_OPCODE;
goto cleanup;
}
@@ -299,8 +300,9 @@ acpi_status acpi_ex_opcode_2A_1T_1R(struct acpi_walk_state *walk_state)
case AML_CONCAT_OP: /* Concatenate (Data1, Data2, Result) */
- status = acpi_ex_do_concatenate(operand[0], operand[1],
- &return_desc, walk_state);
+ status =
+ acpi_ex_do_concatenate(operand[0], operand[1], &return_desc,
+ walk_state);
break;
case AML_TO_STRING_OP: /* to_string (Buffer, Length, Result) (ACPI 2.0) */
@@ -345,8 +347,9 @@ acpi_status acpi_ex_opcode_2A_1T_1R(struct acpi_walk_state *walk_state)
/* concatenate_res_template (Buffer, Buffer, Result) (ACPI 2.0) */
- status = acpi_ex_concat_template(operand[0], operand[1],
- &return_desc, walk_state);
+ status =
+ acpi_ex_concat_template(operand[0], operand[1],
+ &return_desc, walk_state);
break;
case AML_INDEX_OP: /* Index (Source Index Result) */
@@ -553,6 +556,7 @@ acpi_status acpi_ex_opcode_2A_0T_1R(struct acpi_walk_state *walk_state)
ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X",
walk_state->opcode));
+
status = AE_AML_BAD_OPCODE;
goto cleanup;
}
diff --git a/drivers/acpi/acpica/exoparg3.c b/drivers/acpi/acpica/exoparg3.c
index fa100b3b92ee..27fb0172fca2 100644
--- a/drivers/acpi/acpica/exoparg3.c
+++ b/drivers/acpi/acpica/exoparg3.c
@@ -95,10 +95,11 @@ acpi_status acpi_ex_opcode_3A_0T_0R(struct acpi_walk_state *walk_state)
case AML_FATAL_OP: /* Fatal (fatal_type fatal_code fatal_arg) */
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "FatalOp: Type %X Code %X Arg %X <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n",
- (u32) operand[0]->integer.value,
- (u32) operand[1]->integer.value,
- (u32) operand[2]->integer.value));
+ "FatalOp: Type %X Code %X Arg %X "
+ "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n",
+ (u32)operand[0]->integer.value,
+ (u32)operand[1]->integer.value,
+ (u32)operand[2]->integer.value));
fatal = ACPI_ALLOCATE(sizeof(struct acpi_signal_fatal_info));
if (fatal) {
@@ -131,6 +132,7 @@ acpi_status acpi_ex_opcode_3A_0T_0R(struct acpi_walk_state *walk_state)
ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X",
walk_state->opcode));
+
status = AE_AML_BAD_OPCODE;
goto cleanup;
}
@@ -193,7 +195,8 @@ acpi_status acpi_ex_opcode_3A_1T_1R(struct acpi_walk_state *walk_state)
/* Truncate request if larger than the actual String/Buffer */
else if ((index + length) > operand[0]->string.length) {
- length = (acpi_size) operand[0]->string.length -
+ length =
+ (acpi_size) operand[0]->string.length -
(acpi_size) index;
}
@@ -237,8 +240,8 @@ acpi_status acpi_ex_opcode_3A_1T_1R(struct acpi_walk_state *walk_state)
/* We have a buffer, copy the portion requested */
- memcpy(buffer, operand[0]->string.pointer + index,
- length);
+ memcpy(buffer,
+ operand[0]->string.pointer + index, length);
}
/* Set the length of the new String/Buffer */
@@ -255,6 +258,7 @@ acpi_status acpi_ex_opcode_3A_1T_1R(struct acpi_walk_state *walk_state)
ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X",
walk_state->opcode));
+
status = AE_AML_BAD_OPCODE;
goto cleanup;
}
@@ -270,12 +274,11 @@ cleanup:
if (ACPI_FAILURE(status) || walk_state->result_obj) {
acpi_ut_remove_reference(return_desc);
walk_state->result_obj = NULL;
- }
+ } else {
+ /* Set the return object and exit */
- /* Set the return object and exit */
-
- else {
walk_state->result_obj = return_desc;
}
+
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/exoparg6.c b/drivers/acpi/acpica/exoparg6.c
index c930edda3f65..7efc9f47ffb9 100644
--- a/drivers/acpi/acpica/exoparg6.c
+++ b/drivers/acpi/acpica/exoparg6.c
@@ -310,6 +310,7 @@ acpi_status acpi_ex_opcode_6A_0T_1R(struct acpi_walk_state * walk_state)
ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X",
walk_state->opcode));
+
status = AE_AML_BAD_OPCODE;
goto cleanup;
}
diff --git a/drivers/acpi/acpica/exprep.c b/drivers/acpi/acpica/exprep.c
index 4c2836dc825b..1f111cc94c00 100644
--- a/drivers/acpi/acpica/exprep.c
+++ b/drivers/acpi/acpica/exprep.c
@@ -1,6 +1,6 @@
/******************************************************************************
*
- * Module Name: exprep - ACPI AML (p-code) execution - field prep utilities
+ * Module Name: exprep - ACPI AML field prep utilities
*
*****************************************************************************/
@@ -103,8 +103,10 @@ acpi_ex_generate_access(u32 field_bit_offset,
/* Round Field start offset and length to "minimal" byte boundaries */
field_byte_offset = ACPI_DIV_8(ACPI_ROUND_DOWN(field_bit_offset, 8));
- field_byte_end_offset = ACPI_DIV_8(ACPI_ROUND_UP(field_bit_length +
- field_bit_offset, 8));
+
+ field_byte_end_offset =
+ ACPI_DIV_8(ACPI_ROUND_UP(field_bit_length + field_bit_offset, 8));
+
field_byte_length = field_byte_end_offset - field_byte_offset;
ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
@@ -159,7 +161,8 @@ acpi_ex_generate_access(u32 field_bit_offset,
if (accesses <= 1) {
ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
- "Entire field can be accessed with one operation of size %u\n",
+ "Entire field can be accessed "
+ "with one operation of size %u\n",
access_byte_width));
return_VALUE(access_byte_width);
}
@@ -202,6 +205,7 @@ acpi_ex_generate_access(u32 field_bit_offset,
*/
ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
"Cannot access field in one operation, using width 8\n"));
+
return_VALUE(8);
}
#endif /* ACPI_UNDER_DEVELOPMENT */
@@ -281,6 +285,7 @@ acpi_ex_decode_field_access(union acpi_operand_object *obj_desc,
/* Invalid field access type */
ACPI_ERROR((AE_INFO, "Unknown field access type 0x%X", access));
+
return_UINT32(0);
}
@@ -354,8 +359,8 @@ acpi_ex_prep_common_field_object(union acpi_operand_object *obj_desc,
* For all other access types (Byte, Word, Dword, Qword), the Bitwidth is
* the same (equivalent) as the byte_alignment.
*/
- access_bit_width = acpi_ex_decode_field_access(obj_desc, field_flags,
- &byte_alignment);
+ access_bit_width =
+ acpi_ex_decode_field_access(obj_desc, field_flags, &byte_alignment);
if (!access_bit_width) {
return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
}
@@ -595,7 +600,8 @@ acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info)
access_byte_width);
ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
- "IndexField: BitOff %X, Off %X, Value %X, Gran %X, Index %p, Data %p\n",
+ "IndexField: BitOff %X, Off %X, Value %X, "
+ "Gran %X, Index %p, Data %p\n",
obj_desc->index_field.start_field_bit_offset,
obj_desc->index_field.base_byte_offset,
obj_desc->index_field.value,
@@ -615,8 +621,9 @@ acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info)
* Store the constructed descriptor (obj_desc) into the parent Node,
* preserving the current type of that named_obj.
*/
- status = acpi_ns_attach_object(info->field_node, obj_desc,
- acpi_ns_get_type(info->field_node));
+ status =
+ acpi_ns_attach_object(info->field_node, obj_desc,
+ acpi_ns_get_type(info->field_node));
ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
"Set NamedObj %p [%4.4s], ObjDesc %p\n",
diff --git a/drivers/acpi/acpica/exregion.c b/drivers/acpi/acpica/exregion.c
index b4a5e44c00dd..1851a307544a 100644
--- a/drivers/acpi/acpica/exregion.c
+++ b/drivers/acpi/acpica/exregion.c
@@ -392,7 +392,8 @@ acpi_ex_pci_config_space_handler(u32 function,
pci_register = (u16) (u32) address;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "Pci-Config %u (%u) Seg(%04x) Bus(%04x) Dev(%04x) Func(%04x) Reg(%04x)\n",
+ "Pci-Config %u (%u) Seg(%04x) Bus(%04x) "
+ "Dev(%04x) Func(%04x) Reg(%04x)\n",
function, bit_width, pci_id->segment, pci_id->bus,
pci_id->device, pci_id->function, pci_register));
@@ -400,14 +401,16 @@ acpi_ex_pci_config_space_handler(u32 function,
case ACPI_READ:
*value = 0;
- status = acpi_os_read_pci_configuration(pci_id, pci_register,
- value, bit_width);
+ status =
+ acpi_os_read_pci_configuration(pci_id, pci_register, value,
+ bit_width);
break;
case ACPI_WRITE:
- status = acpi_os_write_pci_configuration(pci_id, pci_register,
- *value, bit_width);
+ status =
+ acpi_os_write_pci_configuration(pci_id, pci_register,
+ *value, bit_width);
break;
default:
diff --git a/drivers/acpi/acpica/exresnte.c b/drivers/acpi/acpica/exresnte.c
index 1b372ef69308..6793dcc8a946 100644
--- a/drivers/acpi/acpica/exresnte.c
+++ b/drivers/acpi/acpica/exresnte.c
@@ -112,7 +112,7 @@ acpi_ex_resolve_node_to_value(struct acpi_namespace_node **object_ptr,
/*
* Several object types require no further processing:
- * 1) Device/Thermal objects don't have a "real" subobject, return the Node
+ * 1) Device/Thermal objects don't have a "real" subobject, return Node
* 2) Method locals and arguments have a pseudo-Node
* 3) 10/2007: Added method type to assist with Package construction.
*/
diff --git a/drivers/acpi/acpica/exresolv.c b/drivers/acpi/acpica/exresolv.c
index a1afe1a1e7c2..7f9260b129fc 100644
--- a/drivers/acpi/acpica/exresolv.c
+++ b/drivers/acpi/acpica/exresolv.c
@@ -217,7 +217,8 @@ acpi_ex_resolve_object_to_value(union acpi_operand_object **stack_ptr,
* the package, can't dereference it
*/
ACPI_ERROR((AE_INFO,
- "Attempt to dereference an Index to NULL package element Idx=%p",
+ "Attempt to dereference an Index to "
+ "NULL package element Idx=%p",
stack_desc));
status = AE_AML_UNINITIALIZED_ELEMENT;
}
@@ -361,10 +362,9 @@ acpi_ex_resolve_multiple(struct acpi_walk_state *walk_state,
if (type == ACPI_TYPE_LOCAL_ALIAS) {
type = ((struct acpi_namespace_node *)obj_desc)->type;
- obj_desc =
- acpi_ns_get_attached_object((struct
- acpi_namespace_node *)
- obj_desc);
+ obj_desc = acpi_ns_get_attached_object((struct
+ acpi_namespace_node
+ *)obj_desc);
}
if (!obj_desc) {
diff --git a/drivers/acpi/acpica/exresop.c b/drivers/acpi/acpica/exresop.c
index 424442d50b5e..861453e58555 100644
--- a/drivers/acpi/acpica/exresop.c
+++ b/drivers/acpi/acpica/exresop.c
@@ -90,8 +90,8 @@ acpi_ex_check_object_type(acpi_object_type type_needed,
* specification, a store to a constant is a noop.)
*/
if ((this_type == ACPI_TYPE_INTEGER) &&
- (((union acpi_operand_object *)object)->common.
- flags & AOPOBJ_AML_CONSTANT)) {
+ (((union acpi_operand_object *)object)->common.flags &
+ AOPOBJ_AML_CONSTANT)) {
return (AE_OK);
}
}
@@ -196,10 +196,10 @@ acpi_ex_resolve_operands(u16 opcode,
* thus, the attached object is always the aliased namespace node
*/
if (object_type == ACPI_TYPE_LOCAL_ALIAS) {
- obj_desc =
- acpi_ns_get_attached_object((struct
- acpi_namespace_node
- *)obj_desc);
+ obj_desc = acpi_ns_get_attached_object((struct
+ acpi_namespace_node
+ *)
+ obj_desc);
*stack_ptr = obj_desc;
object_type =
((struct acpi_namespace_node *)obj_desc)->
@@ -285,8 +285,8 @@ acpi_ex_resolve_operands(u16 opcode,
case ARGI_REF_OR_STRING: /* Can be a String or Reference */
if ((ACPI_GET_DESCRIPTOR_TYPE(obj_desc) ==
- ACPI_DESC_TYPE_OPERAND)
- && (obj_desc->common.type == ACPI_TYPE_STRING)) {
+ ACPI_DESC_TYPE_OPERAND) &&
+ (obj_desc->common.type == ACPI_TYPE_STRING)) {
/*
* String found - the string references a named object and
* must be resolved to a node
@@ -465,8 +465,9 @@ acpi_ex_resolve_operands(u16 opcode,
* But we can implicitly convert from a BUFFER or INTEGER
* aka - "Implicit Source Operand Conversion"
*/
- status = acpi_ex_convert_to_string(obj_desc, stack_ptr,
- ACPI_IMPLICIT_CONVERT_HEX);
+ status =
+ acpi_ex_convert_to_string(obj_desc, stack_ptr,
+ ACPI_IMPLICIT_CONVERT_HEX);
if (ACPI_FAILURE(status)) {
if (status == AE_TYPE) {
ACPI_ERROR((AE_INFO,
@@ -597,8 +598,10 @@ acpi_ex_resolve_operands(u16 opcode,
case ARGI_REGION_OR_BUFFER: /* Used by Load() only */
- /* Need an operand of type REGION or a BUFFER (which could be a resolved region field) */
-
+ /*
+ * Need an operand of type REGION or a BUFFER
+ * (which could be a resolved region field)
+ */
switch (obj_desc->common.type) {
case ACPI_TYPE_BUFFER:
case ACPI_TYPE_REGION:
@@ -640,9 +643,9 @@ acpi_ex_resolve_operands(u16 opcode,
if (acpi_gbl_enable_interpreter_slack) {
/*
- * Enable original behavior of Store(), allowing any and all
- * objects as the source operand. The ACPI spec does not
- * allow this, however.
+ * Enable original behavior of Store(), allowing any
+ * and all objects as the source operand. The ACPI
+ * spec does not allow this, however.
*/
break;
}
@@ -655,7 +658,8 @@ acpi_ex_resolve_operands(u16 opcode,
}
ACPI_ERROR((AE_INFO,
- "Needed Integer/Buffer/String/Package/Ref/Ddb], found [%s] %p",
+ "Needed Integer/Buffer/String/Package/Ref/Ddb]"
+ ", found [%s] %p",
acpi_ut_get_object_type_name
(obj_desc), obj_desc));
@@ -678,9 +682,10 @@ acpi_ex_resolve_operands(u16 opcode,
* Make sure that the original object was resolved to the
* required object type (Simple cases only).
*/
- status = acpi_ex_check_object_type(type_needed,
- (*stack_ptr)->common.type,
- *stack_ptr);
+ status =
+ acpi_ex_check_object_type(type_needed,
+ (*stack_ptr)->common.type,
+ *stack_ptr);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/exstore.c b/drivers/acpi/acpica/exstore.c
index c076e9100d66..d3afbcbe7886 100644
--- a/drivers/acpi/acpica/exstore.c
+++ b/drivers/acpi/acpica/exstore.c
@@ -467,7 +467,8 @@ acpi_ex_store_object_to_node(union acpi_operand_object *source_desc,
case ACPI_TYPE_THERMAL:
ACPI_ERROR((AE_INFO,
- "Target must be [Buffer/Integer/String/Reference], found [%s] (%4.4s)",
+ "Target must be [Buffer/Integer/String/Reference]"
+ ", found [%s] (%4.4s)",
acpi_ut_get_type_name(node->type),
node->name.ascii));
@@ -504,8 +505,9 @@ acpi_ex_store_object_to_node(union acpi_operand_object *source_desc,
* an implicit conversion, as per the ACPI specification.
* A direct store is performed instead.
*/
- status = acpi_ex_store_direct_to_node(source_desc, node,
- walk_state);
+ status =
+ acpi_ex_store_direct_to_node(source_desc, node,
+ walk_state);
break;
}
@@ -528,8 +530,9 @@ acpi_ex_store_object_to_node(union acpi_operand_object *source_desc,
* store has been performed such that the node/object type
* has been changed.
*/
- status = acpi_ns_attach_object(node, new_desc,
- new_desc->common.type);
+ status =
+ acpi_ns_attach_object(node, new_desc,
+ new_desc->common.type);
ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
"Store type [%s] into [%s] via Convert/Attach\n",
@@ -563,8 +566,8 @@ acpi_ex_store_object_to_node(union acpi_operand_object *source_desc,
* operator. (Note, for this default case, all normal
* Store/Target operations exited above with an error).
*/
- status = acpi_ex_store_direct_to_node(source_desc, node,
- walk_state);
+ status =
+ acpi_ex_store_direct_to_node(source_desc, node, walk_state);
break;
}
diff --git a/drivers/acpi/acpica/exstorob.c b/drivers/acpi/acpica/exstorob.c
index e1d4f4d51b97..ad3bc92af2e6 100644
--- a/drivers/acpi/acpica/exstorob.c
+++ b/drivers/acpi/acpica/exstorob.c
@@ -1,6 +1,6 @@
/******************************************************************************
*
- * Module Name: exstorob - AML Interpreter object store support, store to object
+ * Module Name: exstorob - AML object store support, store to object
*
*****************************************************************************/
@@ -203,8 +203,9 @@ acpi_ex_store_string_to_string(union acpi_operand_object *source_desc,
ACPI_FREE(target_desc->string.pointer);
}
- target_desc->string.pointer = ACPI_ALLOCATE_ZEROED((acpi_size)
- length + 1);
+ target_desc->string.pointer =
+ ACPI_ALLOCATE_ZEROED((acpi_size) length + 1);
+
if (!target_desc->string.pointer) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
diff --git a/drivers/acpi/acpica/exsystem.c b/drivers/acpi/acpica/exsystem.c
index 05450656fe3d..7c91c1f799a5 100644
--- a/drivers/acpi/acpica/exsystem.c
+++ b/drivers/acpi/acpica/exsystem.c
@@ -78,7 +78,6 @@ acpi_status acpi_ex_system_wait_semaphore(acpi_semaphore semaphore, u16 timeout)
/* We must wait, so unlock the interpreter */
acpi_ex_exit_interpreter();
-
status = acpi_os_wait_semaphore(semaphore, 1, timeout);
ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
@@ -124,7 +123,6 @@ acpi_status acpi_ex_system_wait_mutex(acpi_mutex mutex, u16 timeout)
/* We must wait, so unlock the interpreter */
acpi_ex_exit_interpreter();
-
status = acpi_os_acquire_mutex(mutex, timeout);
ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
@@ -169,8 +167,8 @@ acpi_status acpi_ex_system_do_stall(u32 how_long)
* (ACPI specifies 100 usec as max, but this gives some slack in
* order to support existing BIOSs)
*/
- ACPI_ERROR((AE_INFO, "Time parameter is too large (%u)",
- how_long));
+ ACPI_ERROR((AE_INFO,
+ "Time parameter is too large (%u)", how_long));
status = AE_AML_OPERAND_VALUE;
} else {
acpi_os_stall(how_long);
diff --git a/drivers/acpi/acpica/extrace.c b/drivers/acpi/acpica/extrace.c
new file mode 100644
index 000000000000..e4a185eece8a
--- /dev/null
+++ b/drivers/acpi/acpica/extrace.c
@@ -0,0 +1,377 @@
+/******************************************************************************
+ *
+ * Module Name: extrace - Support for interpreter execution tracing
+ *
+ *****************************************************************************/
+
+/*
+ * 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.
+ */
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+#include "acinterp.h"
+
+#define _COMPONENT ACPI_EXECUTER
+ACPI_MODULE_NAME("extrace")
+
+static union acpi_operand_object *acpi_gbl_trace_method_object = NULL;
+
+/* Local prototypes */
+
+#ifdef ACPI_DEBUG_OUTPUT
+static const char *acpi_ex_get_trace_event_name(acpi_trace_event_type type);
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_interpreter_trace_enabled
+ *
+ * PARAMETERS: name - Whether method name should be matched,
+ * this should be checked before starting
+ * the tracer
+ *
+ * RETURN: TRUE if interpreter trace is enabled.
+ *
+ * DESCRIPTION: Check whether interpreter trace is enabled
+ *
+ ******************************************************************************/
+
+static u8 acpi_ex_interpreter_trace_enabled(char *name)
+{
+
+ /* Check if tracing is enabled */
+
+ if (!(acpi_gbl_trace_flags & ACPI_TRACE_ENABLED)) {
+ return (FALSE);
+ }
+
+ /*
+ * Check if tracing is filtered:
+ *
+ * 1. If the tracer is started, acpi_gbl_trace_method_object should have
+ * been filled by the trace starter
+ * 2. If the tracer is not started, acpi_gbl_trace_method_name should be
+ * matched if it is specified
+ * 3. If the tracer is oneshot style, acpi_gbl_trace_method_name should
+ * not be cleared by the trace stopper during the first match
+ */
+ if (acpi_gbl_trace_method_object) {
+ return (TRUE);
+ }
+
+ if (name &&
+ (acpi_gbl_trace_method_name &&
+ strcmp(acpi_gbl_trace_method_name, name))) {
+ return (FALSE);
+ }
+
+ if ((acpi_gbl_trace_flags & ACPI_TRACE_ONESHOT) &&
+ !acpi_gbl_trace_method_name) {
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_get_trace_event_name
+ *
+ * PARAMETERS: type - Trace event type
+ *
+ * RETURN: Trace event name.
+ *
+ * DESCRIPTION: Used to obtain the full trace event name.
+ *
+ ******************************************************************************/
+
+#ifdef ACPI_DEBUG_OUTPUT
+
+static const char *acpi_ex_get_trace_event_name(acpi_trace_event_type type)
+{
+
+ switch (type) {
+ case ACPI_TRACE_AML_METHOD:
+
+ return "Method";
+
+ case ACPI_TRACE_AML_OPCODE:
+
+ return "Opcode";
+
+ case ACPI_TRACE_AML_REGION:
+
+ return "Region";
+
+ default:
+
+ return "";
+ }
+}
+
+#endif
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_trace_point
+ *
+ * PARAMETERS: type - Trace event type
+ * begin - TRUE if before execution
+ * aml - Executed AML address
+ * pathname - Object path
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Internal interpreter execution trace.
+ *
+ ******************************************************************************/
+
+void
+acpi_ex_trace_point(acpi_trace_event_type type,
+ u8 begin, u8 *aml, char *pathname)
+{
+
+ ACPI_FUNCTION_NAME(ex_trace_point);
+
+ if (pathname) {
+ ACPI_DEBUG_PRINT((ACPI_DB_TRACE_POINT,
+ "%s %s [0x%p:%s] execution.\n",
+ acpi_ex_get_trace_event_name(type),
+ begin ? "Begin" : "End", aml, pathname));
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_TRACE_POINT,
+ "%s %s [0x%p] execution.\n",
+ acpi_ex_get_trace_event_name(type),
+ begin ? "Begin" : "End", aml));
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_start_trace_method
+ *
+ * PARAMETERS: method_node - Node of the method
+ * obj_desc - The method object
+ * walk_state - current state, NULL if not yet executing
+ * a method.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Start control method execution trace
+ *
+ ******************************************************************************/
+
+void
+acpi_ex_start_trace_method(struct acpi_namespace_node *method_node,
+ union acpi_operand_object *obj_desc,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status;
+ char *pathname = NULL;
+ u8 enabled = FALSE;
+
+ ACPI_FUNCTION_NAME(ex_start_trace_method);
+
+ if (method_node) {
+ pathname = acpi_ns_get_normalized_pathname(method_node, TRUE);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
+
+ enabled = acpi_ex_interpreter_trace_enabled(pathname);
+ if (enabled && !acpi_gbl_trace_method_object) {
+ acpi_gbl_trace_method_object = obj_desc;
+ acpi_gbl_original_dbg_level = acpi_dbg_level;
+ acpi_gbl_original_dbg_layer = acpi_dbg_layer;
+ acpi_dbg_level = ACPI_TRACE_LEVEL_ALL;
+ acpi_dbg_layer = ACPI_TRACE_LAYER_ALL;
+
+ if (acpi_gbl_trace_dbg_level) {
+ acpi_dbg_level = acpi_gbl_trace_dbg_level;
+ }
+
+ if (acpi_gbl_trace_dbg_layer) {
+ acpi_dbg_layer = acpi_gbl_trace_dbg_layer;
+ }
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+
+exit:
+ if (enabled) {
+ ACPI_TRACE_POINT(ACPI_TRACE_AML_METHOD, TRUE,
+ obj_desc ? obj_desc->method.aml_start : NULL,
+ pathname);
+ }
+
+ if (pathname) {
+ ACPI_FREE(pathname);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_stop_trace_method
+ *
+ * PARAMETERS: method_node - Node of the method
+ * obj_desc - The method object
+ * walk_state - current state, NULL if not yet executing
+ * a method.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Stop control method execution trace
+ *
+ ******************************************************************************/
+
+void
+acpi_ex_stop_trace_method(struct acpi_namespace_node *method_node,
+ union acpi_operand_object *obj_desc,
+ struct acpi_walk_state *walk_state)
+{
+ acpi_status status;
+ char *pathname = NULL;
+ u8 enabled;
+
+ ACPI_FUNCTION_NAME(ex_stop_trace_method);
+
+ if (method_node) {
+ pathname = acpi_ns_get_normalized_pathname(method_node, TRUE);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ goto exit_path;
+ }
+
+ enabled = acpi_ex_interpreter_trace_enabled(NULL);
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+
+ if (enabled) {
+ ACPI_TRACE_POINT(ACPI_TRACE_AML_METHOD, FALSE,
+ obj_desc ? obj_desc->method.aml_start : NULL,
+ pathname);
+ }
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ goto exit_path;
+ }
+
+ /* Check whether the tracer should be stopped */
+
+ if (acpi_gbl_trace_method_object == obj_desc) {
+
+ /* Disable further tracing if type is one-shot */
+
+ if (acpi_gbl_trace_flags & ACPI_TRACE_ONESHOT) {
+ acpi_gbl_trace_method_name = NULL;
+ }
+
+ acpi_dbg_level = acpi_gbl_original_dbg_level;
+ acpi_dbg_layer = acpi_gbl_original_dbg_layer;
+ acpi_gbl_trace_method_object = NULL;
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+
+exit_path:
+ if (pathname) {
+ ACPI_FREE(pathname);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_start_trace_opcode
+ *
+ * PARAMETERS: op - The parser opcode object
+ * walk_state - current state, NULL if not yet executing
+ * a method.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Start opcode execution trace
+ *
+ ******************************************************************************/
+
+void
+acpi_ex_start_trace_opcode(union acpi_parse_object *op,
+ struct acpi_walk_state *walk_state)
+{
+
+ ACPI_FUNCTION_NAME(ex_start_trace_opcode);
+
+ if (acpi_ex_interpreter_trace_enabled(NULL) &&
+ (acpi_gbl_trace_flags & ACPI_TRACE_OPCODE)) {
+ ACPI_TRACE_POINT(ACPI_TRACE_AML_OPCODE, TRUE,
+ op->common.aml, op->common.aml_op_name);
+ }
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ex_stop_trace_opcode
+ *
+ * PARAMETERS: op - The parser opcode object
+ * walk_state - current state, NULL if not yet executing
+ * a method.
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Stop opcode execution trace
+ *
+ ******************************************************************************/
+
+void
+acpi_ex_stop_trace_opcode(union acpi_parse_object *op,
+ struct acpi_walk_state *walk_state)
+{
+
+ ACPI_FUNCTION_NAME(ex_stop_trace_opcode);
+
+ if (acpi_ex_interpreter_trace_enabled(NULL) &&
+ (acpi_gbl_trace_flags & ACPI_TRACE_OPCODE)) {
+ ACPI_TRACE_POINT(ACPI_TRACE_AML_OPCODE, FALSE,
+ op->common.aml, op->common.aml_op_name);
+ }
+}
diff --git a/drivers/acpi/acpica/exutils.c b/drivers/acpi/acpica/exutils.c
index 30c3f464fda5..8ae7634bd7d2 100644
--- a/drivers/acpi/acpica/exutils.c
+++ b/drivers/acpi/acpica/exutils.c
@@ -167,8 +167,8 @@ u8 acpi_ex_truncate_for32bit_table(union acpi_operand_object *obj_desc)
if ((acpi_gbl_integer_byte_width == 4) &&
(obj_desc->integer.value > (u64)ACPI_UINT32_MAX)) {
/*
- * We are executing in a 32-bit ACPI table.
- * Truncate the value to 32 bits by zeroing out the upper 32-bit field
+ * We are executing in a 32-bit ACPI table. Truncate
+ * the value to 32 bits by zeroing out the upper 32-bit field
*/
obj_desc->integer.value &= (u64)ACPI_UINT32_MAX;
return (TRUE);
@@ -323,7 +323,8 @@ void acpi_ex_eisa_id_to_string(char *out_string, u64 compressed_id)
if (compressed_id > ACPI_UINT32_MAX) {
ACPI_WARNING((AE_INFO,
- "Expected EISAID is larger than 32 bits: 0x%8.8X%8.8X, truncating",
+ "Expected EISAID is larger than 32 bits: "
+ "0x%8.8X%8.8X, truncating",
ACPI_FORMAT_UINT64(compressed_id)));
}
diff --git a/drivers/acpi/acpica/hwesleep.c b/drivers/acpi/acpica/hwesleep.c
index e5599f610808..d0319a228ef7 100644
--- a/drivers/acpi/acpica/hwesleep.c
+++ b/drivers/acpi/acpica/hwesleep.c
@@ -117,8 +117,8 @@ acpi_status acpi_hw_extended_sleep(u8 sleep_state)
/* Clear wake status (WAK_STS) */
- status =
- acpi_write((u64)ACPI_X_WAKE_STATUS, &acpi_gbl_FADT.sleep_status);
+ status = acpi_write((u64)ACPI_X_WAKE_STATUS,
+ &acpi_gbl_FADT.sleep_status);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c
index 73cfa5947ff3..8272f966382a 100644
--- a/drivers/acpi/acpica/hwgpe.c
+++ b/drivers/acpi/acpica/hwgpe.c
@@ -187,9 +187,8 @@ acpi_status acpi_hw_clear_gpe(struct acpi_gpe_event_info * gpe_event_info)
*/
register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info);
- status = acpi_hw_write(register_bit,
- &gpe_register_info->status_address);
-
+ status =
+ acpi_hw_write(register_bit, &gpe_register_info->status_address);
return (status);
}
@@ -297,6 +296,7 @@ acpi_hw_gpe_enable_write(u8 enable_mask,
acpi_status status;
gpe_register_info->enable_mask = enable_mask;
+
status = acpi_hw_write(enable_mask, &gpe_register_info->enable_address);
return (status);
}
diff --git a/drivers/acpi/acpica/hwsleep.c b/drivers/acpi/acpica/hwsleep.c
index 7d21cae6d602..ac5b7f768d4b 100644
--- a/drivers/acpi/acpica/hwsleep.c
+++ b/drivers/acpi/acpica/hwsleep.c
@@ -80,8 +80,8 @@ acpi_status acpi_hw_legacy_sleep(u8 sleep_state)
/* Clear wake status */
- status =
- acpi_write_bit_register(ACPI_BITREG_WAKE_STATUS, ACPI_CLEAR_STATUS);
+ status = acpi_write_bit_register(ACPI_BITREG_WAKE_STATUS,
+ ACPI_CLEAR_STATUS);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/hwxface.c b/drivers/acpi/acpica/hwxface.c
index 5f97468df8ff..b2e50d8007fe 100644
--- a/drivers/acpi/acpica/hwxface.c
+++ b/drivers/acpi/acpica/hwxface.c
@@ -504,11 +504,20 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b)
* Evaluate the \_Sx namespace object containing the register values
* for this state
*/
- info->relative_pathname =
- ACPI_CAST_PTR(char, acpi_gbl_sleep_state_names[sleep_state]);
+ info->relative_pathname = ACPI_CAST_PTR(char,
+ acpi_gbl_sleep_state_names
+ [sleep_state]);
+
status = acpi_ns_evaluate(info);
if (ACPI_FAILURE(status)) {
- goto cleanup;
+ if (status == AE_NOT_FOUND) {
+
+ /* The _Sx states are optional, ignore NOT_FOUND */
+
+ goto final_cleanup;
+ }
+
+ goto warning_cleanup;
}
/* Must have a return object */
@@ -517,7 +526,7 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b)
ACPI_ERROR((AE_INFO, "No Sleep State object returned from [%s]",
info->relative_pathname));
status = AE_AML_NO_RETURN_VALUE;
- goto cleanup;
+ goto warning_cleanup;
}
/* Return object must be of type Package */
@@ -526,7 +535,7 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b)
ACPI_ERROR((AE_INFO,
"Sleep State return object is not a Package"));
status = AE_AML_OPERAND_TYPE;
- goto cleanup1;
+ goto return_value_cleanup;
}
/*
@@ -570,16 +579,17 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b)
break;
}
-cleanup1:
+return_value_cleanup:
acpi_ut_remove_reference(info->return_object);
-cleanup:
+warning_cleanup:
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status,
"While evaluating Sleep State [%s]",
info->relative_pathname));
}
+final_cleanup:
ACPI_FREE(info);
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/hwxfsleep.c b/drivers/acpi/acpica/hwxfsleep.c
index d62a61612b3f..1ce4efa1a2bd 100644
--- a/drivers/acpi/acpica/hwxfsleep.c
+++ b/drivers/acpi/acpica/hwxfsleep.c
@@ -52,9 +52,9 @@ ACPI_MODULE_NAME("hwxfsleep")
/* Local prototypes */
#if (!ACPI_REDUCED_HARDWARE)
static acpi_status
-acpi_hw_set_firmware_waking_vectors(struct acpi_table_facs *facs,
- acpi_physical_address physical_address,
- acpi_physical_address physical_address64);
+acpi_hw_set_firmware_waking_vector(struct acpi_table_facs *facs,
+ acpi_physical_address physical_address,
+ acpi_physical_address physical_address64);
#endif
static acpi_status acpi_hw_sleep_dispatch(u8 sleep_state, u32 function_id);
@@ -79,22 +79,20 @@ static struct acpi_sleep_functions acpi_sleep_dispatch[] = {
/*
* These functions are removed for the ACPI_REDUCED_HARDWARE case:
- * acpi_set_firmware_waking_vectors
* acpi_set_firmware_waking_vector
- * acpi_set_firmware_waking_vector64
* acpi_enter_sleep_state_s4bios
*/
#if (!ACPI_REDUCED_HARDWARE)
/*******************************************************************************
*
- * FUNCTION: acpi_hw_set_firmware_waking_vectors
+ * FUNCTION: acpi_hw_set_firmware_waking_vector
*
* PARAMETERS: facs - Pointer to FACS table
* physical_address - 32-bit physical address of ACPI real mode
- * entry point.
+ * entry point
* physical_address64 - 64-bit physical address of ACPI protected
- * mode entry point.
+ * mode entry point
*
* RETURN: Status
*
@@ -103,11 +101,11 @@ static struct acpi_sleep_functions acpi_sleep_dispatch[] = {
******************************************************************************/
static acpi_status
-acpi_hw_set_firmware_waking_vectors(struct acpi_table_facs *facs,
- acpi_physical_address physical_address,
- acpi_physical_address physical_address64)
+acpi_hw_set_firmware_waking_vector(struct acpi_table_facs *facs,
+ acpi_physical_address physical_address,
+ acpi_physical_address physical_address64)
{
- ACPI_FUNCTION_TRACE(acpi_hw_set_firmware_waking_vectors);
+ ACPI_FUNCTION_TRACE(acpi_hw_set_firmware_waking_vector);
/*
@@ -140,12 +138,12 @@ acpi_hw_set_firmware_waking_vectors(struct acpi_table_facs *facs,
/*******************************************************************************
*
- * FUNCTION: acpi_set_firmware_waking_vectors
+ * FUNCTION: acpi_set_firmware_waking_vector
*
* PARAMETERS: physical_address - 32-bit physical address of ACPI real mode
- * entry point.
+ * entry point
* physical_address64 - 64-bit physical address of ACPI protected
- * mode entry point.
+ * mode entry point
*
* RETURN: Status
*
@@ -154,79 +152,23 @@ acpi_hw_set_firmware_waking_vectors(struct acpi_table_facs *facs,
******************************************************************************/
acpi_status
-acpi_set_firmware_waking_vectors(acpi_physical_address physical_address,
- acpi_physical_address physical_address64)
+acpi_set_firmware_waking_vector(acpi_physical_address physical_address,
+ acpi_physical_address physical_address64)
{
- ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vectors);
+ ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector);
if (acpi_gbl_FACS) {
- (void)acpi_hw_set_firmware_waking_vectors(acpi_gbl_FACS,
- physical_address,
- physical_address64);
+ (void)acpi_hw_set_firmware_waking_vector(acpi_gbl_FACS,
+ physical_address,
+ physical_address64);
}
return_ACPI_STATUS(AE_OK);
}
-ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vectors)
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_set_firmware_waking_vector
- *
- * PARAMETERS: physical_address - 32-bit physical address of ACPI real mode
- * entry point.
- *
- * RETURN: Status
- *
- * DESCRIPTION: Sets the 32-bit firmware_waking_vector field of the FACS
- *
- ******************************************************************************/
-acpi_status acpi_set_firmware_waking_vector(u32 physical_address)
-{
- acpi_status status;
-
- ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector);
-
- status = acpi_set_firmware_waking_vectors((acpi_physical_address)
- physical_address, 0);
-
- return_ACPI_STATUS(status);
-}
-
ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector)
-#if ACPI_MACHINE_WIDTH == 64
-/*******************************************************************************
- *
- * FUNCTION: acpi_set_firmware_waking_vector64
- *
- * PARAMETERS: physical_address - 64-bit physical address of ACPI protected
- * mode entry point.
- *
- * RETURN: Status
- *
- * DESCRIPTION: Sets the 64-bit X_firmware_waking_vector field of the FACS, if
- * it exists in the table. This function is intended for use with
- * 64-bit host operating systems.
- *
- ******************************************************************************/
-acpi_status acpi_set_firmware_waking_vector64(u64 physical_address)
-{
- acpi_status status;
-
- ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector64);
-
- status = acpi_set_firmware_waking_vectors(0,
- (acpi_physical_address)
- physical_address);
-
- return_ACPI_STATUS(status);
-}
-
-ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector64)
-#endif
/*******************************************************************************
*
* FUNCTION: acpi_enter_sleep_state_s4bios
@@ -286,6 +228,7 @@ acpi_status acpi_enter_sleep_state_s4bios(void)
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
+
} while (!in_value);
return_ACPI_STATUS(AE_OK);
diff --git a/drivers/acpi/acpica/nsconvert.c b/drivers/acpi/acpica/nsconvert.c
index da55a1c60da1..f21568ba325b 100644
--- a/drivers/acpi/acpica/nsconvert.c
+++ b/drivers/acpi/acpica/nsconvert.c
@@ -96,9 +96,9 @@ acpi_ns_convert_to_integer(union acpi_operand_object *original_object,
/* Extract each buffer byte to create the integer */
for (i = 0; i < original_object->buffer.length; i++) {
- value |=
- ((u64)original_object->buffer.
- pointer[i] << (i * 8));
+ value |= ((u64)
+ original_object->buffer.pointer[i] << (i *
+ 8));
}
break;
@@ -153,10 +153,9 @@ acpi_ns_convert_to_string(union acpi_operand_object *original_object,
return (AE_NO_MEMORY);
}
} else {
- status =
- acpi_ex_convert_to_string(original_object,
- &new_object,
- ACPI_IMPLICIT_CONVERT_HEX);
+ status = acpi_ex_convert_to_string(original_object,
+ &new_object,
+ ACPI_IMPLICIT_CONVERT_HEX);
if (ACPI_FAILURE(status)) {
return (status);
}
@@ -244,9 +243,8 @@ acpi_ns_convert_to_buffer(union acpi_operand_object *original_object,
/* String-to-Buffer conversion. Simple data copy */
- new_object =
- acpi_ut_create_buffer_object(original_object->string.
- length);
+ new_object = acpi_ut_create_buffer_object
+ (original_object->string.length);
if (!new_object) {
return (AE_NO_MEMORY);
}
@@ -308,7 +306,8 @@ acpi_ns_convert_to_buffer(union acpi_operand_object *original_object,
*
* FUNCTION: acpi_ns_convert_to_unicode
*
- * PARAMETERS: original_object - ASCII String Object to be converted
+ * PARAMETERS: scope - Namespace node for the method/object
+ * original_object - ASCII String Object to be converted
* return_object - Where the new converted object is returned
*
* RETURN: Status. AE_OK if conversion was successful.
@@ -318,7 +317,8 @@ acpi_ns_convert_to_buffer(union acpi_operand_object *original_object,
******************************************************************************/
acpi_status
-acpi_ns_convert_to_unicode(union acpi_operand_object *original_object,
+acpi_ns_convert_to_unicode(struct acpi_namespace_node * scope,
+ union acpi_operand_object *original_object,
union acpi_operand_object **return_object)
{
union acpi_operand_object *new_object;
@@ -372,7 +372,8 @@ acpi_ns_convert_to_unicode(union acpi_operand_object *original_object,
*
* FUNCTION: acpi_ns_convert_to_resource
*
- * PARAMETERS: original_object - Object to be converted
+ * PARAMETERS: scope - Namespace node for the method/object
+ * original_object - Object to be converted
* return_object - Where the new converted object is returned
*
* RETURN: Status. AE_OK if conversion was successful
@@ -383,7 +384,8 @@ acpi_ns_convert_to_unicode(union acpi_operand_object *original_object,
******************************************************************************/
acpi_status
-acpi_ns_convert_to_resource(union acpi_operand_object *original_object,
+acpi_ns_convert_to_resource(struct acpi_namespace_node * scope,
+ union acpi_operand_object *original_object,
union acpi_operand_object **return_object)
{
union acpi_operand_object *new_object;
@@ -444,3 +446,78 @@ acpi_ns_convert_to_resource(union acpi_operand_object *original_object,
*return_object = new_object;
return (AE_OK);
}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_convert_to_reference
+ *
+ * PARAMETERS: scope - Namespace node for the method/object
+ * original_object - Object to be converted
+ * return_object - Where the new converted object is returned
+ *
+ * RETURN: Status. AE_OK if conversion was successful
+ *
+ * DESCRIPTION: Attempt to convert a Integer object to a object_reference.
+ * Buffer.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_convert_to_reference(struct acpi_namespace_node * scope,
+ union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object)
+{
+ union acpi_operand_object *new_object = NULL;
+ acpi_status status;
+ struct acpi_namespace_node *node;
+ union acpi_generic_state scope_info;
+ char *name;
+
+ ACPI_FUNCTION_NAME(ns_convert_to_reference);
+
+ /* Convert path into internal presentation */
+
+ status =
+ acpi_ns_internalize_name(original_object->string.pointer, &name);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Find the namespace node */
+
+ scope_info.scope.node =
+ ACPI_CAST_PTR(struct acpi_namespace_node, scope);
+ status =
+ acpi_ns_lookup(&scope_info, name, ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE,
+ ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE,
+ NULL, &node);
+ if (ACPI_FAILURE(status)) {
+
+ /* Check if we are resolving a named reference within a package */
+
+ ACPI_ERROR_NAMESPACE(original_object->string.pointer, status);
+ goto error_exit;
+ }
+
+ /* Create and init a new internal ACPI object */
+
+ new_object = acpi_ut_create_internal_object(ACPI_TYPE_LOCAL_REFERENCE);
+ if (!new_object) {
+ status = AE_NO_MEMORY;
+ goto error_exit;
+ }
+ new_object->reference.node = node;
+ new_object->reference.object = node->object;
+ new_object->reference.class = ACPI_REFCLASS_NAME;
+
+ /*
+ * Increase reference of the object if needed (the object is likely a
+ * null for device nodes).
+ */
+ acpi_ut_add_reference(node->object);
+
+error_exit:
+ ACPI_FREE(name);
+ *return_object = new_object;
+ return (AE_OK);
+}
diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c
index 37aa5c45ca4b..bc5ff358b2a7 100644
--- a/drivers/acpi/acpica/nsdump.c
+++ b/drivers/acpi/acpica/nsdump.c
@@ -539,11 +539,13 @@ acpi_ns_dump_one_object(acpi_handle obj_handle,
acpi_os_printf
("(Pointer to ACPI Object type %.2X [UNKNOWN])\n",
obj_type);
+
bytes_to_dump = 32;
} else {
acpi_os_printf
("(Pointer to ACPI Object type %.2X [%s])\n",
obj_type, acpi_ut_get_type_name(obj_type));
+
bytes_to_dump =
sizeof(union acpi_operand_object);
}
@@ -573,6 +575,7 @@ acpi_ns_dump_one_object(acpi_handle obj_handle,
*/
bytes_to_dump = obj_desc->string.length;
obj_desc = (void *)obj_desc->string.pointer;
+
acpi_os_printf("(Buffer/String pointer %p length %X)\n",
obj_desc, bytes_to_dump);
ACPI_DUMP_BUFFER(obj_desc, bytes_to_dump);
@@ -717,7 +720,7 @@ acpi_ns_dump_one_object_path(acpi_handle obj_handle,
return (AE_OK);
}
- pathname = acpi_ns_get_external_pathname(node);
+ pathname = acpi_ns_get_normalized_pathname(node, TRUE);
path_indent = 1;
if (level <= max_level) {
diff --git a/drivers/acpi/acpica/nseval.c b/drivers/acpi/acpica/nseval.c
index 7eba578d36f3..15e0b2ec5d65 100644
--- a/drivers/acpi/acpica/nseval.c
+++ b/drivers/acpi/acpica/nseval.c
@@ -135,7 +135,7 @@ acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info)
/* Get the full pathname to the object, for use in warning messages */
- info->full_pathname = acpi_ns_get_external_pathname(info->node);
+ info->full_pathname = acpi_ns_get_normalized_pathname(info->node, TRUE);
if (!info->full_pathname) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
diff --git a/drivers/acpi/acpica/nsinit.c b/drivers/acpi/acpica/nsinit.c
index b744a53618eb..ac59929c3ee9 100644
--- a/drivers/acpi/acpica/nsinit.c
+++ b/drivers/acpi/acpica/nsinit.c
@@ -582,7 +582,8 @@ acpi_ns_init_one_device(acpi_handle obj_handle,
/* Ignore error and move on to next device */
- char *scope_name = acpi_ns_get_external_pathname(info->node);
+ char *scope_name =
+ acpi_ns_get_normalized_pathname(device_node, TRUE);
ACPI_EXCEPTION((AE_INFO, status, "during %s._INI execution",
scope_name));
diff --git a/drivers/acpi/acpica/nsload.c b/drivers/acpi/acpica/nsload.c
index 14ab83668207..14c953e6fe9e 100644
--- a/drivers/acpi/acpica/nsload.c
+++ b/drivers/acpi/acpica/nsload.c
@@ -149,6 +149,23 @@ unlock:
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"**** Completed Table Object Initialization\n"));
+ /*
+ * Execute any module-level code that was detected during the table load
+ * phase. Although illegal since ACPI 2.0, there are many machines that
+ * contain this type of code. Each block of detected executable AML code
+ * outside of any control method is wrapped with a temporary control
+ * method object and placed on a global list. The methods on this list
+ * are executed below.
+ *
+ * This case executes the module-level code for each table immediately
+ * after the table has been loaded. This provides compatibility with
+ * other ACPI implementations. Optionally, the execution can be deferred
+ * until later, see acpi_initialize_objects.
+ */
+ if (!acpi_gbl_group_module_level_code) {
+ acpi_ns_exec_module_code_list();
+ }
+
return_ACPI_STATUS(status);
}
@@ -321,7 +338,6 @@ acpi_status acpi_ns_unload_namespace(acpi_handle handle)
/* This function does the real work */
status = acpi_ns_delete_subtree(handle);
-
return_ACPI_STATUS(status);
}
#endif
diff --git a/drivers/acpi/acpica/nsnames.c b/drivers/acpi/acpica/nsnames.c
index 8934b4eddb73..521031f9b6c6 100644
--- a/drivers/acpi/acpica/nsnames.c
+++ b/drivers/acpi/acpica/nsnames.c
@@ -70,7 +70,6 @@ char *acpi_ns_get_external_pathname(struct acpi_namespace_node *node)
ACPI_FUNCTION_TRACE_PTR(ns_get_external_pathname, node);
name_buffer = acpi_ns_get_normalized_pathname(node, FALSE);
-
return_PTR(name_buffer);
}
@@ -93,7 +92,6 @@ acpi_size acpi_ns_get_pathname_length(struct acpi_namespace_node *node)
ACPI_FUNCTION_ENTRY();
size = acpi_ns_build_normalized_path(node, NULL, 0, FALSE);
-
return (size);
}
@@ -217,6 +215,7 @@ acpi_ns_build_normalized_path(struct acpi_namespace_node *node,
ACPI_PATH_PUT8(full_path, path_size,
AML_DUAL_NAME_PREFIX, length);
}
+
ACPI_MOVE_32_TO_32(name, &next_node->name);
do_no_trailing = no_trailing;
for (i = 0; i < 4; i++) {
@@ -228,8 +227,10 @@ acpi_ns_build_normalized_path(struct acpi_namespace_node *node,
ACPI_PATH_PUT8(full_path, path_size, c, length);
}
}
+
next_node = next_node->parent;
}
+
ACPI_PATH_PUT8(full_path, path_size, AML_ROOT_PREFIX, length);
/* Reverse the path string */
@@ -237,6 +238,7 @@ acpi_ns_build_normalized_path(struct acpi_namespace_node *node,
if (length <= path_size) {
left = full_path;
right = full_path + length - 1;
+
while (left < right) {
c = *left;
*left++ = *right;
diff --git a/drivers/acpi/acpica/nsparse.c b/drivers/acpi/acpica/nsparse.c
index 3736d43b18b9..43b45a8c2fe4 100644
--- a/drivers/acpi/acpica/nsparse.c
+++ b/drivers/acpi/acpica/nsparse.c
@@ -141,8 +141,8 @@ acpi_ns_one_complete_parse(u32 pass_number,
/* Parse the AML */
- ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "*PARSE* pass %u parse\n",
- pass_number));
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
+ "*PARSE* pass %u parse\n", pass_number));
status = acpi_ps_parse_aml(walk_state);
cleanup:
@@ -181,6 +181,7 @@ acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node)
* performs another complete parse of the AML.
*/
ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "**** Start pass 1\n"));
+
status = acpi_ns_one_complete_parse(ACPI_IMODE_LOAD_PASS1,
table_index, start_node);
if (ACPI_FAILURE(status)) {
diff --git a/drivers/acpi/acpica/nsprepkg.c b/drivers/acpi/acpica/nsprepkg.c
index 9bb251932b45..c05a83be5c11 100644
--- a/drivers/acpi/acpica/nsprepkg.c
+++ b/drivers/acpi/acpica/nsprepkg.c
@@ -233,8 +233,9 @@ acpi_ns_check_package(struct acpi_evaluate_info *info,
/* First element is the (Integer) revision */
- status = acpi_ns_check_object_type(info, elements,
- ACPI_RTYPE_INTEGER, 0);
+ status =
+ acpi_ns_check_object_type(info, elements,
+ ACPI_RTYPE_INTEGER, 0);
if (ACPI_FAILURE(status)) {
return (status);
}
@@ -252,8 +253,9 @@ acpi_ns_check_package(struct acpi_evaluate_info *info,
/* First element is the (Integer) count of subpackages to follow */
- status = acpi_ns_check_object_type(info, elements,
- ACPI_RTYPE_INTEGER, 0);
+ status =
+ acpi_ns_check_object_type(info, elements,
+ ACPI_RTYPE_INTEGER, 0);
if (ACPI_FAILURE(status)) {
return (status);
}
diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c
index 77d8103d0094..6418863f93d5 100644
--- a/drivers/acpi/acpica/nsrepair.c
+++ b/drivers/acpi/acpica/nsrepair.c
@@ -116,6 +116,11 @@ static const struct acpi_simple_repair_info acpi_object_repair_info[] = {
ACPI_NOT_PACKAGE_ELEMENT,
acpi_ns_convert_to_resource},
+ /* Object reference conversions */
+
+ {"_DEP", ACPI_RTYPE_STRING, ACPI_ALL_PACKAGE_ELEMENTS,
+ acpi_ns_convert_to_reference},
+
/* Unicode conversions */
{"_MLS", ACPI_RTYPE_STRING, 1,
@@ -172,8 +177,8 @@ acpi_ns_simple_repair(struct acpi_evaluate_info *info,
"Missing expected return value"));
}
- status =
- predefined->object_converter(return_object, &new_object);
+ status = predefined->object_converter(info->node, return_object,
+ &new_object);
if (ACPI_FAILURE(status)) {
/* A fatal error occurred during a conversion */
@@ -360,12 +365,15 @@ static const struct acpi_simple_repair_info *acpi_ns_match_simple_repair(struct
/* Check if we can actually repair this name/type combination */
if ((return_btype & this_name->unexpected_btypes) &&
- (package_index == this_name->package_index)) {
+ (this_name->package_index ==
+ ACPI_ALL_PACKAGE_ELEMENTS
+ || package_index == this_name->package_index)) {
return (this_name);
}
return (NULL);
}
+
this_name++;
}
@@ -521,6 +529,7 @@ acpi_ns_remove_null_elements(struct acpi_evaluate_info *info,
*dest = *source;
dest++;
}
+
source++;
}
@@ -572,8 +581,8 @@ acpi_ns_wrap_with_package(struct acpi_evaluate_info *info,
ACPI_FUNCTION_NAME(ns_wrap_with_package);
/*
- * Create the new outer package and populate it. The new package will
- * have a single element, the lone sub-object.
+ * Create the new outer package and populate it. The new
+ * package will have a single element, the lone sub-object.
*/
pkg_obj_desc = acpi_ut_create_package_object(1);
if (!pkg_obj_desc) {
diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c
index 0515a70f42a4..f6dd2a83ea63 100644
--- a/drivers/acpi/acpica/nsrepair2.c
+++ b/drivers/acpi/acpica/nsrepair2.c
@@ -225,6 +225,7 @@ static const struct acpi_repair_info *acpi_ns_match_complex_repair(struct
if (ACPI_COMPARE_NAME(node->name.ascii, this_name->name)) {
return (this_name);
}
+
this_name++;
}
@@ -301,7 +302,8 @@ acpi_ns_repair_FDE(struct acpi_evaluate_info *info,
/* We can only repair if we have exactly 5 BYTEs */
if (return_object->buffer.length != ACPI_FDE_BYTE_BUFFER_SIZE) {
- ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname,
+ ACPI_WARN_PREDEFINED((AE_INFO,
+ info->full_pathname,
info->node_flags,
"Incorrect return buffer length %u, expected %u",
return_object->buffer.length,
@@ -321,8 +323,8 @@ acpi_ns_repair_FDE(struct acpi_evaluate_info *info,
/* Expand each byte to a DWORD */
byte_buffer = return_object->buffer.pointer;
- dword_buffer =
- ACPI_CAST_PTR(u32, buffer_object->buffer.pointer);
+ dword_buffer = ACPI_CAST_PTR(u32,
+ buffer_object->buffer.pointer);
for (i = 0; i < ACPI_FDE_FIELD_COUNT; i++) {
*dword_buffer = (u32) *byte_buffer;
@@ -461,7 +463,8 @@ acpi_ns_repair_CST(struct acpi_evaluate_info *info,
removing = FALSE;
if ((*outer_elements)->package.count == 0) {
- ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname,
+ ACPI_WARN_PREDEFINED((AE_INFO,
+ info->full_pathname,
info->node_flags,
"SubPackage[%u] - removing entry due to zero count",
i));
@@ -471,7 +474,8 @@ acpi_ns_repair_CST(struct acpi_evaluate_info *info,
obj_desc = (*outer_elements)->package.elements[1]; /* Index1 = Type */
if ((u32)obj_desc->integer.value == 0) {
- ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname,
+ ACPI_WARN_PREDEFINED((AE_INFO,
+ info->full_pathname,
info->node_flags,
"SubPackage[%u] - removing entry due to invalid Type(0)",
i));
@@ -538,8 +542,8 @@ acpi_ns_repair_HID(struct acpi_evaluate_info *info,
}
if (return_object->string.length == 0) {
- ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname,
- info->node_flags,
+ ACPI_WARN_PREDEFINED((AE_INFO,
+ info->full_pathname, info->node_flags,
"Invalid zero-length _HID or _CID string"));
/* Return AE_OK anyway, let driver handle it */
@@ -710,8 +714,9 @@ acpi_ns_repair_PSS(struct acpi_evaluate_info *info,
elements = (*outer_elements)->package.elements;
obj_desc = elements[1]; /* Index1 = power_dissipation */
- if ((u32) obj_desc->integer.value > previous_value) {
- ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname,
+ if ((u32)obj_desc->integer.value > previous_value) {
+ ACPI_WARN_PREDEFINED((AE_INFO,
+ info->full_pathname,
info->node_flags,
"SubPackage[%u,%u] - suspicious power dissipation values",
i - 1, i));
@@ -969,6 +974,7 @@ acpi_ns_remove_element(union acpi_operand_object *obj_desc, u32 index)
*dest = *source;
dest++;
}
+
source++;
}
diff --git a/drivers/acpi/acpica/nssearch.c b/drivers/acpi/acpica/nssearch.c
index d73904013830..9cc3564de37e 100644
--- a/drivers/acpi/acpica/nssearch.c
+++ b/drivers/acpi/acpica/nssearch.c
@@ -105,7 +105,7 @@ acpi_ns_search_one_scope(u32 target_name,
if (ACPI_LV_NAMES & acpi_dbg_level) {
char *scope_name;
- scope_name = acpi_ns_get_external_pathname(parent_node);
+ scope_name = acpi_ns_get_normalized_pathname(parent_node, TRUE);
if (scope_name) {
ACPI_DEBUG_PRINT((ACPI_DB_NAMES,
"Searching %s (%p) For [%4.4s] (%s)\n",
diff --git a/drivers/acpi/acpica/nsutils.c b/drivers/acpi/acpica/nsutils.c
index de325ae04ce1..32f1d956eb7f 100644
--- a/drivers/acpi/acpica/nsutils.c
+++ b/drivers/acpi/acpica/nsutils.c
@@ -173,9 +173,10 @@ void acpi_ns_get_internal_name_length(struct acpi_namestring_info *info)
info->fully_qualified = FALSE;
/*
- * For the internal name, the required length is 4 bytes per segment, plus
- * 1 each for root_prefix, multi_name_prefix_op, segment count, trailing null
- * (which is not really needed, but no there's harm in putting it there)
+ * For the internal name, the required length is 4 bytes per segment,
+ * plus 1 each for root_prefix, multi_name_prefix_op, segment count,
+ * trailing null (which is not really needed, but no there's harm in
+ * putting it there)
*
* strlen() + 1 covers the first name_seg, which has no path separator
*/
@@ -699,6 +700,7 @@ acpi_ns_get_node(struct acpi_namespace_node *prefix_node,
if (!prefix_node) {
*return_node = acpi_gbl_root_node;
}
+
return_ACPI_STATUS(AE_OK);
}
diff --git a/drivers/acpi/acpica/nsxfeval.c b/drivers/acpi/acpica/nsxfeval.c
index 6ee1e52b903d..429f0d27bef0 100644
--- a/drivers/acpi/acpica/nsxfeval.c
+++ b/drivers/acpi/acpica/nsxfeval.c
@@ -750,8 +750,8 @@ acpi_ns_get_device_callback(acpi_handle obj_handle,
/* We have a valid device, invoke the user function */
- status = info->user_function(obj_handle, nesting_level, info->context,
- return_value);
+ status = info->user_function(obj_handle, nesting_level,
+ info->context, return_value);
return (status);
}
diff --git a/drivers/acpi/acpica/nsxfname.c b/drivers/acpi/acpica/nsxfname.c
index 4b4d2f43d406..669e0f1b0967 100644
--- a/drivers/acpi/acpica/nsxfname.c
+++ b/drivers/acpi/acpica/nsxfname.c
@@ -159,7 +159,7 @@ acpi_get_name(acpi_handle handle, u32 name_type, struct acpi_buffer * buffer)
{
acpi_status status;
struct acpi_namespace_node *node;
- char *node_name;
+ const char *node_name;
/* Parameter validation */
@@ -238,7 +238,6 @@ static char *acpi_ns_copy_device_id(struct acpi_pnp_device_id *dest,
struct acpi_pnp_device_id *source,
char *string_area)
{
-
/* Create the destination PNP_DEVICE_ID */
dest->string = string_area;
@@ -263,11 +262,18 @@ static char *acpi_ns_copy_device_id(struct acpi_pnp_device_id *dest,
* namespace node and possibly by running several standard
* control methods (Such as in the case of a device.)
*
- * For Device and Processor objects, run the Device _HID, _UID, _CID, _SUB,
- * _CLS, _STA, _ADR, _sx_w, and _sx_d methods.
+ * For Device and Processor objects, run the Device _HID, _UID, _CID, _STA,
+ * _CLS, _ADR, _sx_w, and _sx_d methods.
*
* Note: Allocates the return buffer, must be freed by the caller.
*
+ * Note: This interface is intended to be used during the initial device
+ * discovery namespace traversal. Therefore, no complex methods can be
+ * executed, especially those that access operation regions. Therefore, do
+ * not add any additional methods that could cause problems in this area.
+ * this was the fate of the _SUB method which was found to cause such
+ * problems and was removed (11/2015).
+ *
******************************************************************************/
acpi_status
@@ -279,7 +285,6 @@ acpi_get_object_info(acpi_handle handle,
struct acpi_pnp_device_id_list *cid_list = NULL;
struct acpi_pnp_device_id *hid = NULL;
struct acpi_pnp_device_id *uid = NULL;
- struct acpi_pnp_device_id *sub = NULL;
struct acpi_pnp_device_id *cls = NULL;
char *next_id_string;
acpi_object_type type;
@@ -325,7 +330,7 @@ acpi_get_object_info(acpi_handle handle,
if ((type == ACPI_TYPE_DEVICE) || (type == ACPI_TYPE_PROCESSOR)) {
/*
* Get extra info for ACPI Device/Processor objects only:
- * Run the Device _HID, _UID, _SUB, _CID, and _CLS methods.
+ * Run the Device _HID, _UID, _CLS, and _CID methods.
*
* Note: none of these methods are required, so they may or may
* not be present for this device. The Info->Valid bitfield is used
@@ -348,14 +353,6 @@ acpi_get_object_info(acpi_handle handle,
valid |= ACPI_VALID_UID;
}
- /* Execute the Device._SUB method */
-
- status = acpi_ut_execute_SUB(node, &sub);
- if (ACPI_SUCCESS(status)) {
- info_size += sub->length;
- valid |= ACPI_VALID_SUB;
- }
-
/* Execute the Device._CID method */
status = acpi_ut_execute_CID(node, &cid_list);
@@ -456,9 +453,8 @@ acpi_get_object_info(acpi_handle handle,
}
/*
- * Copy the HID, UID, SUB, and CIDs to the return buffer.
- * The variable-length strings are copied to the reserved area
- * at the end of the buffer.
+ * Copy the HID, UID, and CIDs to the return buffer. The variable-length
+ * strings are copied to the reserved area at the end of the buffer.
*
* For HID and CID, check if the ID is a PCI Root Bridge.
*/
@@ -476,11 +472,6 @@ acpi_get_object_info(acpi_handle handle,
uid, next_id_string);
}
- if (sub) {
- next_id_string = acpi_ns_copy_device_id(&info->subsystem_id,
- sub, next_id_string);
- }
-
if (cid_list) {
info->compatible_id_list.count = cid_list->count;
info->compatible_id_list.list_size = cid_list->list_size;
@@ -522,9 +513,6 @@ cleanup:
if (uid) {
ACPI_FREE(uid);
}
- if (sub) {
- ACPI_FREE(sub);
- }
if (cid_list) {
ACPI_FREE(cid_list);
}
@@ -591,6 +579,7 @@ acpi_status acpi_install_method(u8 *buffer)
parser_state.aml += acpi_ps_get_opcode_size(opcode);
parser_state.pkg_end = acpi_ps_get_next_package_end(&parser_state);
path = acpi_ps_get_next_namestring(&parser_state);
+
method_flags = *parser_state.aml++;
aml_start = parser_state.aml;
aml_length = ACPI_PTR_DIFF(parser_state.pkg_end, aml_start);
diff --git a/drivers/acpi/acpica/nsxfobj.c b/drivers/acpi/acpica/nsxfobj.c
index 793383501f81..6e1389babb47 100644
--- a/drivers/acpi/acpica/nsxfobj.c
+++ b/drivers/acpi/acpica/nsxfobj.c
@@ -74,10 +74,8 @@ acpi_status acpi_get_type(acpi_handle handle, acpi_object_type * ret_type)
return (AE_BAD_PARAMETER);
}
- /*
- * Special case for the predefined Root Node
- * (return type ANY)
- */
+ /* Special case for the predefined Root Node (return type ANY) */
+
if (handle == ACPI_ROOT_OBJECT) {
*ret_type = ACPI_TYPE_ANY;
return (AE_OK);
diff --git a/drivers/acpi/acpica/psargs.c b/drivers/acpi/acpica/psargs.c
index 29d8b7b01dca..f3bcfa20b0ae 100644
--- a/drivers/acpi/acpica/psargs.c
+++ b/drivers/acpi/acpica/psargs.c
@@ -269,7 +269,8 @@ acpi_ps_get_next_namepath(struct acpi_walk_state *walk_state,
*/
if (ACPI_SUCCESS(status) &&
possible_method_call && (node->type == ACPI_TYPE_METHOD)) {
- if (walk_state->opcode == AML_UNLOAD_OP) {
+ if (GET_CURRENT_ARG_TYPE(walk_state->arg_types) ==
+ ARGP_SUPERNAME) {
/*
* acpi_ps_get_next_namestring has increased the AML pointer,
* so we need to restore the saved AML pointer for method call.
@@ -696,7 +697,7 @@ static union acpi_parse_object *acpi_ps_get_next_field(struct acpi_parse_state
*
* PARAMETERS: walk_state - Current state
* parser_state - Current parser state object
- * arg_type - The argument type (AML_*_ARG)
+ * arg_type - The parser argument type (ARGP_*)
* return_arg - Where the next arg is returned
*
* RETURN: Status, and an op object containing the next argument.
@@ -733,6 +734,7 @@ acpi_ps_get_next_arg(struct acpi_walk_state *walk_state,
if (!arg) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
+
acpi_ps_get_next_simple_arg(parser_state, arg_type, arg);
break;
@@ -798,6 +800,7 @@ acpi_ps_get_next_arg(struct acpi_walk_state *walk_state,
case ARGP_TARGET:
case ARGP_SUPERNAME:
case ARGP_SIMPLENAME:
+ case ARGP_NAME_OR_REF:
subop = acpi_ps_peek_opcode(parser_state);
if (subop == 0 ||
@@ -814,17 +817,17 @@ acpi_ps_get_next_arg(struct acpi_walk_state *walk_state,
return_ACPI_STATUS(AE_NO_MEMORY);
}
- /* To support super_name arg of Unload */
+ /* super_name allows argument to be a method call */
- if (walk_state->opcode == AML_UNLOAD_OP) {
+ if (arg_type == ARGP_SUPERNAME) {
status =
acpi_ps_get_next_namepath(walk_state,
parser_state, arg,
- 1);
+ ACPI_POSSIBLE_METHOD_CALL);
/*
- * If the super_name arg of Unload is a method call,
- * we have restored the AML pointer, just free this Arg
+ * If the super_name argument is a method call, we have
+ * already restored the AML pointer, just free this Arg
*/
if (arg->common.aml_opcode ==
AML_INT_METHODCALL_OP) {
@@ -835,7 +838,7 @@ acpi_ps_get_next_arg(struct acpi_walk_state *walk_state,
status =
acpi_ps_get_next_namepath(walk_state,
parser_state, arg,
- 0);
+ ACPI_NOT_METHOD_CALL);
}
} else {
/* Single complex argument, nothing returned */
diff --git a/drivers/acpi/acpica/psloop.c b/drivers/acpi/acpica/psloop.c
index 03ac8c9a67ab..a57f473bac83 100644
--- a/drivers/acpi/acpica/psloop.c
+++ b/drivers/acpi/acpica/psloop.c
@@ -109,10 +109,10 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state,
case AML_INT_NAMEPATH_OP: /* AML_NAMESTRING_ARG */
- status =
- acpi_ps_get_next_namepath(walk_state,
- &(walk_state->parser_state), op,
- 1);
+ status = acpi_ps_get_next_namepath(walk_state,
+ &(walk_state->parser_state),
+ op,
+ ACPI_POSSIBLE_METHOD_CALL);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
@@ -124,8 +124,8 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state,
/*
* Op is not a constant or string, append each argument to the Op
*/
- while (GET_CURRENT_ARG_TYPE(walk_state->arg_types)
- && !walk_state->arg_count) {
+ while (GET_CURRENT_ARG_TYPE(walk_state->arg_types) &&
+ !walk_state->arg_count) {
walk_state->aml = walk_state->parser_state.aml;
status =
diff --git a/drivers/acpi/acpica/psopcode.c b/drivers/acpi/acpica/psopcode.c
index ed90fddf2487..40909ddeebb3 100644
--- a/drivers/acpi/acpica/psopcode.c
+++ b/drivers/acpi/acpica/psopcode.c
@@ -185,458 +185,458 @@ const struct acpi_opcode_info acpi_gbl_aml_op_info[AML_NUM_OPCODES] = {
/* Index Name Parser Args Interpreter Args ObjectType Class Type Flags */
/* 00 */ ACPI_OP("Zero", ARGP_ZERO_OP, ARGI_ZERO_OP, ACPI_TYPE_INTEGER,
- AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT),
+ AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT),
/* 01 */ ACPI_OP("One", ARGP_ONE_OP, ARGI_ONE_OP, ACPI_TYPE_INTEGER,
- AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT),
+ AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT),
/* 02 */ ACPI_OP("Alias", ARGP_ALIAS_OP, ARGI_ALIAS_OP,
- ACPI_TYPE_LOCAL_ALIAS, AML_CLASS_NAMED_OBJECT,
- AML_TYPE_NAMED_SIMPLE,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
- AML_NSNODE | AML_NAMED),
+ ACPI_TYPE_LOCAL_ALIAS, AML_CLASS_NAMED_OBJECT,
+ AML_TYPE_NAMED_SIMPLE,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED),
/* 03 */ ACPI_OP("Name", ARGP_NAME_OP, ARGI_NAME_OP, ACPI_TYPE_ANY,
- AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_COMPLEX,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
- AML_NSNODE | AML_NAMED),
+ AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_COMPLEX,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED),
/* 04 */ ACPI_OP("ByteConst", ARGP_BYTE_OP, ARGI_BYTE_OP,
- ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT,
- AML_TYPE_LITERAL, AML_CONSTANT),
+ ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT,
+ AML_TYPE_LITERAL, AML_CONSTANT),
/* 05 */ ACPI_OP("WordConst", ARGP_WORD_OP, ARGI_WORD_OP,
- ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT,
- AML_TYPE_LITERAL, AML_CONSTANT),
+ ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT,
+ AML_TYPE_LITERAL, AML_CONSTANT),
/* 06 */ ACPI_OP("DwordConst", ARGP_DWORD_OP, ARGI_DWORD_OP,
- ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT,
- AML_TYPE_LITERAL, AML_CONSTANT),
+ ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT,
+ AML_TYPE_LITERAL, AML_CONSTANT),
/* 07 */ ACPI_OP("String", ARGP_STRING_OP, ARGI_STRING_OP,
- ACPI_TYPE_STRING, AML_CLASS_ARGUMENT,
- AML_TYPE_LITERAL, AML_CONSTANT),
+ ACPI_TYPE_STRING, AML_CLASS_ARGUMENT,
+ AML_TYPE_LITERAL, AML_CONSTANT),
/* 08 */ ACPI_OP("Scope", ARGP_SCOPE_OP, ARGI_SCOPE_OP,
- ACPI_TYPE_LOCAL_SCOPE, AML_CLASS_NAMED_OBJECT,
- AML_TYPE_NAMED_NO_OBJ,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
- AML_NSNODE | AML_NAMED),
+ ACPI_TYPE_LOCAL_SCOPE, AML_CLASS_NAMED_OBJECT,
+ AML_TYPE_NAMED_NO_OBJ,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED),
/* 09 */ ACPI_OP("Buffer", ARGP_BUFFER_OP, ARGI_BUFFER_OP,
- ACPI_TYPE_BUFFER, AML_CLASS_CREATE,
- AML_TYPE_CREATE_OBJECT,
- AML_HAS_ARGS | AML_DEFER | AML_CONSTANT),
+ ACPI_TYPE_BUFFER, AML_CLASS_CREATE,
+ AML_TYPE_CREATE_OBJECT,
+ AML_HAS_ARGS | AML_DEFER | AML_CONSTANT),
/* 0A */ ACPI_OP("Package", ARGP_PACKAGE_OP, ARGI_PACKAGE_OP,
- ACPI_TYPE_PACKAGE, AML_CLASS_CREATE,
- AML_TYPE_CREATE_OBJECT,
- AML_HAS_ARGS | AML_DEFER | AML_CONSTANT),
+ ACPI_TYPE_PACKAGE, AML_CLASS_CREATE,
+ AML_TYPE_CREATE_OBJECT,
+ AML_HAS_ARGS | AML_DEFER | AML_CONSTANT),
/* 0B */ ACPI_OP("Method", ARGP_METHOD_OP, ARGI_METHOD_OP,
- ACPI_TYPE_METHOD, AML_CLASS_NAMED_OBJECT,
- AML_TYPE_NAMED_COMPLEX,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
- AML_NSNODE | AML_NAMED | AML_DEFER),
+ ACPI_TYPE_METHOD, AML_CLASS_NAMED_OBJECT,
+ AML_TYPE_NAMED_COMPLEX,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED | AML_DEFER),
/* 0C */ ACPI_OP("Local0", ARGP_LOCAL0, ARGI_LOCAL0,
- ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
- AML_TYPE_LOCAL_VARIABLE, 0),
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_LOCAL_VARIABLE, 0),
/* 0D */ ACPI_OP("Local1", ARGP_LOCAL1, ARGI_LOCAL1,
- ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
- AML_TYPE_LOCAL_VARIABLE, 0),
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_LOCAL_VARIABLE, 0),
/* 0E */ ACPI_OP("Local2", ARGP_LOCAL2, ARGI_LOCAL2,
- ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
- AML_TYPE_LOCAL_VARIABLE, 0),
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_LOCAL_VARIABLE, 0),
/* 0F */ ACPI_OP("Local3", ARGP_LOCAL3, ARGI_LOCAL3,
- ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
- AML_TYPE_LOCAL_VARIABLE, 0),
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_LOCAL_VARIABLE, 0),
/* 10 */ ACPI_OP("Local4", ARGP_LOCAL4, ARGI_LOCAL4,
- ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
- AML_TYPE_LOCAL_VARIABLE, 0),
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_LOCAL_VARIABLE, 0),
/* 11 */ ACPI_OP("Local5", ARGP_LOCAL5, ARGI_LOCAL5,
- ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
- AML_TYPE_LOCAL_VARIABLE, 0),
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_LOCAL_VARIABLE, 0),
/* 12 */ ACPI_OP("Local6", ARGP_LOCAL6, ARGI_LOCAL6,
- ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
- AML_TYPE_LOCAL_VARIABLE, 0),
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_LOCAL_VARIABLE, 0),
/* 13 */ ACPI_OP("Local7", ARGP_LOCAL7, ARGI_LOCAL7,
- ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
- AML_TYPE_LOCAL_VARIABLE, 0),
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_LOCAL_VARIABLE, 0),
/* 14 */ ACPI_OP("Arg0", ARGP_ARG0, ARGI_ARG0,
- ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
- AML_TYPE_METHOD_ARGUMENT, 0),
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_METHOD_ARGUMENT, 0),
/* 15 */ ACPI_OP("Arg1", ARGP_ARG1, ARGI_ARG1,
- ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
- AML_TYPE_METHOD_ARGUMENT, 0),
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_METHOD_ARGUMENT, 0),
/* 16 */ ACPI_OP("Arg2", ARGP_ARG2, ARGI_ARG2,
- ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
- AML_TYPE_METHOD_ARGUMENT, 0),
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_METHOD_ARGUMENT, 0),
/* 17 */ ACPI_OP("Arg3", ARGP_ARG3, ARGI_ARG3,
- ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
- AML_TYPE_METHOD_ARGUMENT, 0),
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_METHOD_ARGUMENT, 0),
/* 18 */ ACPI_OP("Arg4", ARGP_ARG4, ARGI_ARG4,
- ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
- AML_TYPE_METHOD_ARGUMENT, 0),
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_METHOD_ARGUMENT, 0),
/* 19 */ ACPI_OP("Arg5", ARGP_ARG5, ARGI_ARG5,
- ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
- AML_TYPE_METHOD_ARGUMENT, 0),
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_METHOD_ARGUMENT, 0),
/* 1A */ ACPI_OP("Arg6", ARGP_ARG6, ARGI_ARG6,
- ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
- AML_TYPE_METHOD_ARGUMENT, 0),
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_METHOD_ARGUMENT, 0),
/* 1B */ ACPI_OP("Store", ARGP_STORE_OP, ARGI_STORE_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R,
- AML_FLAGS_EXEC_1A_1T_1R),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R),
/* 1C */ ACPI_OP("RefOf", ARGP_REF_OF_OP, ARGI_REF_OF_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R,
- AML_FLAGS_EXEC_1A_0T_1R),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R,
+ AML_FLAGS_EXEC_1A_0T_1R),
/* 1D */ ACPI_OP("Add", ARGP_ADD_OP, ARGI_ADD_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
- AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
/* 1E */ ACPI_OP("Concatenate", ARGP_CONCAT_OP, ARGI_CONCAT_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_2A_1T_1R,
- AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT),
/* 1F */ ACPI_OP("Subtract", ARGP_SUBTRACT_OP, ARGI_SUBTRACT_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_2A_1T_1R,
- AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
/* 20 */ ACPI_OP("Increment", ARGP_INCREMENT_OP, ARGI_INCREMENT_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_1A_0T_1R,
- AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_0T_1R,
+ AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT),
/* 21 */ ACPI_OP("Decrement", ARGP_DECREMENT_OP, ARGI_DECREMENT_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_1A_0T_1R,
- AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_0T_1R,
+ AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT),
/* 22 */ ACPI_OP("Multiply", ARGP_MULTIPLY_OP, ARGI_MULTIPLY_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_2A_1T_1R,
- AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
/* 23 */ ACPI_OP("Divide", ARGP_DIVIDE_OP, ARGI_DIVIDE_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_2A_2T_1R,
- AML_FLAGS_EXEC_2A_2T_1R | AML_CONSTANT),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_2T_1R,
+ AML_FLAGS_EXEC_2A_2T_1R | AML_CONSTANT),
/* 24 */ ACPI_OP("ShiftLeft", ARGP_SHIFT_LEFT_OP, ARGI_SHIFT_LEFT_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_2A_1T_1R,
- AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
/* 25 */ ACPI_OP("ShiftRight", ARGP_SHIFT_RIGHT_OP, ARGI_SHIFT_RIGHT_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_2A_1T_1R,
- AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
/* 26 */ ACPI_OP("And", ARGP_BIT_AND_OP, ARGI_BIT_AND_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
- AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
/* 27 */ ACPI_OP("NAnd", ARGP_BIT_NAND_OP, ARGI_BIT_NAND_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_2A_1T_1R,
- AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
/* 28 */ ACPI_OP("Or", ARGP_BIT_OR_OP, ARGI_BIT_OR_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
- AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
/* 29 */ ACPI_OP("NOr", ARGP_BIT_NOR_OP, ARGI_BIT_NOR_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
- AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
/* 2A */ ACPI_OP("XOr", ARGP_BIT_XOR_OP, ARGI_BIT_XOR_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
- AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT),
/* 2B */ ACPI_OP("Not", ARGP_BIT_NOT_OP, ARGI_BIT_NOT_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R,
- AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
/* 2C */ ACPI_OP("FindSetLeftBit", ARGP_FIND_SET_LEFT_BIT_OP,
- ARGI_FIND_SET_LEFT_BIT_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R,
- AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
+ ARGI_FIND_SET_LEFT_BIT_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
/* 2D */ ACPI_OP("FindSetRightBit", ARGP_FIND_SET_RIGHT_BIT_OP,
- ARGI_FIND_SET_RIGHT_BIT_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R,
- AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
+ ARGI_FIND_SET_RIGHT_BIT_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
/* 2E */ ACPI_OP("DerefOf", ARGP_DEREF_OF_OP, ARGI_DEREF_OF_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_1A_0T_1R, AML_FLAGS_EXEC_1A_0T_1R),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_0T_1R, AML_FLAGS_EXEC_1A_0T_1R),
/* 2F */ ACPI_OP("Notify", ARGP_NOTIFY_OP, ARGI_NOTIFY_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_2A_0T_0R, AML_FLAGS_EXEC_2A_0T_0R),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_0T_0R, AML_FLAGS_EXEC_2A_0T_0R),
/* 30 */ ACPI_OP("SizeOf", ARGP_SIZE_OF_OP, ARGI_SIZE_OF_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_1A_0T_1R,
- AML_FLAGS_EXEC_1A_0T_1R | AML_NO_OPERAND_RESOLVE),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_0T_1R,
+ AML_FLAGS_EXEC_1A_0T_1R | AML_NO_OPERAND_RESOLVE),
/* 31 */ ACPI_OP("Index", ARGP_INDEX_OP, ARGI_INDEX_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
- AML_FLAGS_EXEC_2A_1T_1R),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R),
/* 32 */ ACPI_OP("Match", ARGP_MATCH_OP, ARGI_MATCH_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_6A_0T_1R,
- AML_FLAGS_EXEC_6A_0T_1R | AML_CONSTANT),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_6A_0T_1R,
+ AML_FLAGS_EXEC_6A_0T_1R | AML_CONSTANT),
/* 33 */ ACPI_OP("CreateDWordField", ARGP_CREATE_DWORD_FIELD_OP,
- ARGI_CREATE_DWORD_FIELD_OP,
- ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE,
- AML_TYPE_CREATE_FIELD,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE |
- AML_DEFER | AML_CREATE),
+ ARGI_CREATE_DWORD_FIELD_OP,
+ ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE,
+ AML_TYPE_CREATE_FIELD,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE |
+ AML_DEFER | AML_CREATE),
/* 34 */ ACPI_OP("CreateWordField", ARGP_CREATE_WORD_FIELD_OP,
- ARGI_CREATE_WORD_FIELD_OP,
- ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE,
- AML_TYPE_CREATE_FIELD,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE |
- AML_DEFER | AML_CREATE),
+ ARGI_CREATE_WORD_FIELD_OP,
+ ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE,
+ AML_TYPE_CREATE_FIELD,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE |
+ AML_DEFER | AML_CREATE),
/* 35 */ ACPI_OP("CreateByteField", ARGP_CREATE_BYTE_FIELD_OP,
- ARGI_CREATE_BYTE_FIELD_OP,
- ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE,
- AML_TYPE_CREATE_FIELD,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE |
- AML_DEFER | AML_CREATE),
+ ARGI_CREATE_BYTE_FIELD_OP,
+ ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE,
+ AML_TYPE_CREATE_FIELD,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE |
+ AML_DEFER | AML_CREATE),
/* 36 */ ACPI_OP("CreateBitField", ARGP_CREATE_BIT_FIELD_OP,
- ARGI_CREATE_BIT_FIELD_OP,
- ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE,
- AML_TYPE_CREATE_FIELD,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE |
- AML_DEFER | AML_CREATE),
-/* 37 */ ACPI_OP("ObjectType", ARGP_TYPE_OP, ARGI_TYPE_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_1A_0T_1R,
- AML_FLAGS_EXEC_1A_0T_1R | AML_NO_OPERAND_RESOLVE),
+ ARGI_CREATE_BIT_FIELD_OP,
+ ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE,
+ AML_TYPE_CREATE_FIELD,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE |
+ AML_DEFER | AML_CREATE),
+/* 37 */ ACPI_OP("ObjectType", ARGP_OBJECT_TYPE_OP, ARGI_OBJECT_TYPE_OP,
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_0T_1R,
+ AML_FLAGS_EXEC_1A_0T_1R | AML_NO_OPERAND_RESOLVE),
/* 38 */ ACPI_OP("LAnd", ARGP_LAND_OP, ARGI_LAND_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R,
AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL_NUMERIC |
AML_CONSTANT),
/* 39 */ ACPI_OP("LOr", ARGP_LOR_OP, ARGI_LOR_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R,
- AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL_NUMERIC |
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R,
+ AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL_NUMERIC |
AML_CONSTANT),
/* 3A */ ACPI_OP("LNot", ARGP_LNOT_OP, ARGI_LNOT_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R,
- AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R,
+ AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT),
/* 3B */ ACPI_OP("LEqual", ARGP_LEQUAL_OP, ARGI_LEQUAL_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_2A_0T_1R,
- AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_0T_1R,
+ AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT),
/* 3C */ ACPI_OP("LGreater", ARGP_LGREATER_OP, ARGI_LGREATER_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_2A_0T_1R,
- AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_0T_1R,
+ AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT),
/* 3D */ ACPI_OP("LLess", ARGP_LLESS_OP, ARGI_LLESS_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R,
- AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R,
+ AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT),
/* 3E */ ACPI_OP("If", ARGP_IF_OP, ARGI_IF_OP, ACPI_TYPE_ANY,
- AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS),
+ AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS),
/* 3F */ ACPI_OP("Else", ARGP_ELSE_OP, ARGI_ELSE_OP, ACPI_TYPE_ANY,
- AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS),
+ AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS),
/* 40 */ ACPI_OP("While", ARGP_WHILE_OP, ARGI_WHILE_OP, ACPI_TYPE_ANY,
- AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS),
+ AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS),
/* 41 */ ACPI_OP("Noop", ARGP_NOOP_OP, ARGI_NOOP_OP, ACPI_TYPE_ANY,
- AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0),
+ AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0),
/* 42 */ ACPI_OP("Return", ARGP_RETURN_OP, ARGI_RETURN_OP,
- ACPI_TYPE_ANY, AML_CLASS_CONTROL,
- AML_TYPE_CONTROL, AML_HAS_ARGS),
+ ACPI_TYPE_ANY, AML_CLASS_CONTROL,
+ AML_TYPE_CONTROL, AML_HAS_ARGS),
/* 43 */ ACPI_OP("Break", ARGP_BREAK_OP, ARGI_BREAK_OP, ACPI_TYPE_ANY,
- AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0),
+ AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0),
/* 44 */ ACPI_OP("BreakPoint", ARGP_BREAK_POINT_OP, ARGI_BREAK_POINT_OP,
- ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0),
+ ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0),
/* 45 */ ACPI_OP("Ones", ARGP_ONES_OP, ARGI_ONES_OP, ACPI_TYPE_INTEGER,
- AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT),
+ AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT),
/* Prefixed opcodes (Two-byte opcodes with a prefix op) */
/* 46 */ ACPI_OP("Mutex", ARGP_MUTEX_OP, ARGI_MUTEX_OP, ACPI_TYPE_MUTEX,
- AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_SIMPLE,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
- AML_NSNODE | AML_NAMED),
+ AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_SIMPLE,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED),
/* 47 */ ACPI_OP("Event", ARGP_EVENT_OP, ARGI_EVENT_OP, ACPI_TYPE_EVENT,
- AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_SIMPLE,
- AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED),
+ AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_SIMPLE,
+ AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED),
/* 48 */ ACPI_OP("CondRefOf", ARGP_COND_REF_OF_OP, ARGI_COND_REF_OF_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R),
/* 49 */ ACPI_OP("CreateField", ARGP_CREATE_FIELD_OP,
- ARGI_CREATE_FIELD_OP, ACPI_TYPE_BUFFER_FIELD,
- AML_CLASS_CREATE, AML_TYPE_CREATE_FIELD,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE |
- AML_DEFER | AML_FIELD | AML_CREATE),
+ ARGI_CREATE_FIELD_OP, ACPI_TYPE_BUFFER_FIELD,
+ AML_CLASS_CREATE, AML_TYPE_CREATE_FIELD,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE |
+ AML_DEFER | AML_FIELD | AML_CREATE),
/* 4A */ ACPI_OP("Load", ARGP_LOAD_OP, ARGI_LOAD_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_0R,
- AML_FLAGS_EXEC_1A_1T_0R),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_0R,
+ AML_FLAGS_EXEC_1A_1T_0R),
/* 4B */ ACPI_OP("Stall", ARGP_STALL_OP, ARGI_STALL_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R,
- AML_FLAGS_EXEC_1A_0T_0R),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R,
+ AML_FLAGS_EXEC_1A_0T_0R),
/* 4C */ ACPI_OP("Sleep", ARGP_SLEEP_OP, ARGI_SLEEP_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R,
- AML_FLAGS_EXEC_1A_0T_0R),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R,
+ AML_FLAGS_EXEC_1A_0T_0R),
/* 4D */ ACPI_OP("Acquire", ARGP_ACQUIRE_OP, ARGI_ACQUIRE_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_2A_0T_1R, AML_FLAGS_EXEC_2A_0T_1R),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_0T_1R, AML_FLAGS_EXEC_2A_0T_1R),
/* 4E */ ACPI_OP("Signal", ARGP_SIGNAL_OP, ARGI_SIGNAL_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R),
/* 4F */ ACPI_OP("Wait", ARGP_WAIT_OP, ARGI_WAIT_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R,
- AML_FLAGS_EXEC_2A_0T_1R),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R,
+ AML_FLAGS_EXEC_2A_0T_1R),
/* 50 */ ACPI_OP("Reset", ARGP_RESET_OP, ARGI_RESET_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R,
- AML_FLAGS_EXEC_1A_0T_0R),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R,
+ AML_FLAGS_EXEC_1A_0T_0R),
/* 51 */ ACPI_OP("Release", ARGP_RELEASE_OP, ARGI_RELEASE_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R),
/* 52 */ ACPI_OP("FromBCD", ARGP_FROM_BCD_OP, ARGI_FROM_BCD_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_1A_1T_1R,
- AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
/* 53 */ ACPI_OP("ToBCD", ARGP_TO_BCD_OP, ARGI_TO_BCD_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R,
- AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
/* 54 */ ACPI_OP("Unload", ARGP_UNLOAD_OP, ARGI_UNLOAD_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R),
/* 55 */ ACPI_OP("Revision", ARGP_REVISION_OP, ARGI_REVISION_OP,
- ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT,
- AML_TYPE_CONSTANT, 0),
+ ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT,
+ AML_TYPE_CONSTANT, 0),
/* 56 */ ACPI_OP("Debug", ARGP_DEBUG_OP, ARGI_DEBUG_OP,
- ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
- AML_TYPE_CONSTANT, 0),
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_CONSTANT, 0),
/* 57 */ ACPI_OP("Fatal", ARGP_FATAL_OP, ARGI_FATAL_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_3A_0T_0R,
- AML_FLAGS_EXEC_3A_0T_0R),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_3A_0T_0R,
+ AML_FLAGS_EXEC_3A_0T_0R),
/* 58 */ ACPI_OP("OperationRegion", ARGP_REGION_OP, ARGI_REGION_OP,
- ACPI_TYPE_REGION, AML_CLASS_NAMED_OBJECT,
- AML_TYPE_NAMED_COMPLEX,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
- AML_NSNODE | AML_NAMED | AML_DEFER),
+ ACPI_TYPE_REGION, AML_CLASS_NAMED_OBJECT,
+ AML_TYPE_NAMED_COMPLEX,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED | AML_DEFER),
/* 59 */ ACPI_OP("Field", ARGP_FIELD_OP, ARGI_FIELD_OP, ACPI_TYPE_ANY,
- AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_FIELD,
+ AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_FIELD,
AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
AML_FIELD),
/* 5A */ ACPI_OP("Device", ARGP_DEVICE_OP, ARGI_DEVICE_OP,
- ACPI_TYPE_DEVICE, AML_CLASS_NAMED_OBJECT,
- AML_TYPE_NAMED_NO_OBJ,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
- AML_NSNODE | AML_NAMED),
+ ACPI_TYPE_DEVICE, AML_CLASS_NAMED_OBJECT,
+ AML_TYPE_NAMED_NO_OBJ,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED),
/* 5B */ ACPI_OP("Processor", ARGP_PROCESSOR_OP, ARGI_PROCESSOR_OP,
- ACPI_TYPE_PROCESSOR, AML_CLASS_NAMED_OBJECT,
- AML_TYPE_NAMED_SIMPLE,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
- AML_NSNODE | AML_NAMED),
+ ACPI_TYPE_PROCESSOR, AML_CLASS_NAMED_OBJECT,
+ AML_TYPE_NAMED_SIMPLE,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED),
/* 5C */ ACPI_OP("PowerResource", ARGP_POWER_RES_OP, ARGI_POWER_RES_OP,
- ACPI_TYPE_POWER, AML_CLASS_NAMED_OBJECT,
- AML_TYPE_NAMED_SIMPLE,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
- AML_NSNODE | AML_NAMED),
+ ACPI_TYPE_POWER, AML_CLASS_NAMED_OBJECT,
+ AML_TYPE_NAMED_SIMPLE,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED),
/* 5D */ ACPI_OP("ThermalZone", ARGP_THERMAL_ZONE_OP,
- ARGI_THERMAL_ZONE_OP, ACPI_TYPE_THERMAL,
- AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_NO_OBJ,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
- AML_NSNODE | AML_NAMED),
+ ARGI_THERMAL_ZONE_OP, ACPI_TYPE_THERMAL,
+ AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_NO_OBJ,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED),
/* 5E */ ACPI_OP("IndexField", ARGP_INDEX_FIELD_OP, ARGI_INDEX_FIELD_OP,
- ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT,
- AML_TYPE_NAMED_FIELD,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT,
+ AML_TYPE_NAMED_FIELD,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
AML_FIELD),
/* 5F */ ACPI_OP("BankField", ARGP_BANK_FIELD_OP, ARGI_BANK_FIELD_OP,
- ACPI_TYPE_LOCAL_BANK_FIELD,
+ ACPI_TYPE_LOCAL_BANK_FIELD,
AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_FIELD,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
AML_FIELD | AML_DEFER),
/* Internal opcodes that map to invalid AML opcodes */
/* 60 */ ACPI_OP("LNotEqual", ARGP_LNOTEQUAL_OP, ARGI_LNOTEQUAL_OP,
- ACPI_TYPE_ANY, AML_CLASS_INTERNAL,
- AML_TYPE_BOGUS, AML_HAS_ARGS | AML_CONSTANT),
+ ACPI_TYPE_ANY, AML_CLASS_INTERNAL,
+ AML_TYPE_BOGUS, AML_HAS_ARGS | AML_CONSTANT),
/* 61 */ ACPI_OP("LLessEqual", ARGP_LLESSEQUAL_OP, ARGI_LLESSEQUAL_OP,
- ACPI_TYPE_ANY, AML_CLASS_INTERNAL,
- AML_TYPE_BOGUS, AML_HAS_ARGS | AML_CONSTANT),
+ ACPI_TYPE_ANY, AML_CLASS_INTERNAL,
+ AML_TYPE_BOGUS, AML_HAS_ARGS | AML_CONSTANT),
/* 62 */ ACPI_OP("LGreaterEqual", ARGP_LGREATEREQUAL_OP,
- ARGI_LGREATEREQUAL_OP, ACPI_TYPE_ANY,
- AML_CLASS_INTERNAL, AML_TYPE_BOGUS,
- AML_HAS_ARGS | AML_CONSTANT),
+ ARGI_LGREATEREQUAL_OP, ACPI_TYPE_ANY,
+ AML_CLASS_INTERNAL, AML_TYPE_BOGUS,
+ AML_HAS_ARGS | AML_CONSTANT),
/* 63 */ ACPI_OP("-NamePath-", ARGP_NAMEPATH_OP, ARGI_NAMEPATH_OP,
- ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
- AML_TYPE_LITERAL, AML_NSOBJECT | AML_NSNODE),
+ ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT,
+ AML_TYPE_LITERAL, AML_NSOBJECT | AML_NSNODE),
/* 64 */ ACPI_OP("-MethodCall-", ARGP_METHODCALL_OP, ARGI_METHODCALL_OP,
- ACPI_TYPE_METHOD, AML_CLASS_METHOD_CALL,
- AML_TYPE_METHOD_CALL,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE),
+ ACPI_TYPE_METHOD, AML_CLASS_METHOD_CALL,
+ AML_TYPE_METHOD_CALL,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE),
/* 65 */ ACPI_OP("-ByteList-", ARGP_BYTELIST_OP, ARGI_BYTELIST_OP,
- ACPI_TYPE_ANY, AML_CLASS_ARGUMENT,
- AML_TYPE_LITERAL, 0),
+ ACPI_TYPE_ANY, AML_CLASS_ARGUMENT,
+ AML_TYPE_LITERAL, 0),
/* 66 */ ACPI_OP("-ReservedField-", ARGP_RESERVEDFIELD_OP,
- ARGI_RESERVEDFIELD_OP, ACPI_TYPE_ANY,
- AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0),
+ ARGI_RESERVEDFIELD_OP, ACPI_TYPE_ANY,
+ AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0),
/* 67 */ ACPI_OP("-NamedField-", ARGP_NAMEDFIELD_OP, ARGI_NAMEDFIELD_OP,
- ACPI_TYPE_ANY, AML_CLASS_INTERNAL,
- AML_TYPE_BOGUS,
- AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED),
+ ACPI_TYPE_ANY, AML_CLASS_INTERNAL,
+ AML_TYPE_BOGUS,
+ AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED),
/* 68 */ ACPI_OP("-AccessField-", ARGP_ACCESSFIELD_OP,
- ARGI_ACCESSFIELD_OP, ACPI_TYPE_ANY,
- AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0),
+ ARGI_ACCESSFIELD_OP, ACPI_TYPE_ANY,
+ AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0),
/* 69 */ ACPI_OP("-StaticString", ARGP_STATICSTRING_OP,
- ARGI_STATICSTRING_OP, ACPI_TYPE_ANY,
- AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0),
+ ARGI_STATICSTRING_OP, ACPI_TYPE_ANY,
+ AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0),
/* 6A */ ACPI_OP("-Return Value-", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY,
- AML_CLASS_RETURN_VALUE, AML_TYPE_RETURN,
- AML_HAS_ARGS | AML_HAS_RETVAL),
+ AML_CLASS_RETURN_VALUE, AML_TYPE_RETURN,
+ AML_HAS_ARGS | AML_HAS_RETVAL),
/* 6B */ ACPI_OP("-UNKNOWN_OP-", ARG_NONE, ARG_NONE, ACPI_TYPE_INVALID,
- AML_CLASS_UNKNOWN, AML_TYPE_BOGUS, AML_HAS_ARGS),
+ AML_CLASS_UNKNOWN, AML_TYPE_BOGUS, AML_HAS_ARGS),
/* 6C */ ACPI_OP("-ASCII_ONLY-", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY,
- AML_CLASS_ASCII, AML_TYPE_BOGUS, AML_HAS_ARGS),
+ AML_CLASS_ASCII, AML_TYPE_BOGUS, AML_HAS_ARGS),
/* 6D */ ACPI_OP("-PREFIX_ONLY-", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY,
- AML_CLASS_PREFIX, AML_TYPE_BOGUS, AML_HAS_ARGS),
+ AML_CLASS_PREFIX, AML_TYPE_BOGUS, AML_HAS_ARGS),
/* ACPI 2.0 opcodes */
/* 6E */ ACPI_OP("QwordConst", ARGP_QWORD_OP, ARGI_QWORD_OP,
- ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT,
- AML_TYPE_LITERAL, AML_CONSTANT),
+ ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT,
+ AML_TYPE_LITERAL, AML_CONSTANT),
/* 6F */ ACPI_OP("Package", /* Var */ ARGP_VAR_PACKAGE_OP,
ARGI_VAR_PACKAGE_OP, ACPI_TYPE_PACKAGE,
AML_CLASS_CREATE, AML_TYPE_CREATE_OBJECT,
AML_HAS_ARGS | AML_DEFER),
/* 70 */ ACPI_OP("ConcatenateResTemplate", ARGP_CONCAT_RES_OP,
- ARGI_CONCAT_RES_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
- AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT),
+ ARGI_CONCAT_RES_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT),
/* 71 */ ACPI_OP("Mod", ARGP_MOD_OP, ARGI_MOD_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
- AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT),
/* 72 */ ACPI_OP("CreateQWordField", ARGP_CREATE_QWORD_FIELD_OP,
- ARGI_CREATE_QWORD_FIELD_OP,
- ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE,
- AML_TYPE_CREATE_FIELD,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE |
- AML_DEFER | AML_CREATE),
+ ARGI_CREATE_QWORD_FIELD_OP,
+ ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE,
+ AML_TYPE_CREATE_FIELD,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE |
+ AML_DEFER | AML_CREATE),
/* 73 */ ACPI_OP("ToBuffer", ARGP_TO_BUFFER_OP, ARGI_TO_BUFFER_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_1A_1T_1R,
- AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
/* 74 */ ACPI_OP("ToDecimalString", ARGP_TO_DEC_STR_OP,
- ARGI_TO_DEC_STR_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R,
- AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
+ ARGI_TO_DEC_STR_OP, ACPI_TYPE_ANY,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
/* 75 */ ACPI_OP("ToHexString", ARGP_TO_HEX_STR_OP, ARGI_TO_HEX_STR_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_1A_1T_1R,
- AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
/* 76 */ ACPI_OP("ToInteger", ARGP_TO_INTEGER_OP, ARGI_TO_INTEGER_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_1A_1T_1R,
- AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_1T_1R,
+ AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT),
/* 77 */ ACPI_OP("ToString", ARGP_TO_STRING_OP, ARGI_TO_STRING_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_2A_1T_1R,
- AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_2A_1T_1R,
+ AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT),
/* 78 */ ACPI_OP("CopyObject", ARGP_COPY_OP, ARGI_COPY_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R),
/* 79 */ ACPI_OP("Mid", ARGP_MID_OP, ARGI_MID_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_3A_1T_1R,
- AML_FLAGS_EXEC_3A_1T_1R | AML_CONSTANT),
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_3A_1T_1R,
+ AML_FLAGS_EXEC_3A_1T_1R | AML_CONSTANT),
/* 7A */ ACPI_OP("Continue", ARGP_CONTINUE_OP, ARGI_CONTINUE_OP,
- ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0),
+ ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0),
/* 7B */ ACPI_OP("LoadTable", ARGP_LOAD_TABLE_OP, ARGI_LOAD_TABLE_OP,
- ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
- AML_TYPE_EXEC_6A_0T_1R, AML_FLAGS_EXEC_6A_0T_1R),
+ ACPI_TYPE_ANY, AML_CLASS_EXECUTE,
+ AML_TYPE_EXEC_6A_0T_1R, AML_FLAGS_EXEC_6A_0T_1R),
/* 7C */ ACPI_OP("DataTableRegion", ARGP_DATA_REGION_OP,
- ARGI_DATA_REGION_OP, ACPI_TYPE_REGION,
- AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_COMPLEX,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
- AML_NSNODE | AML_NAMED | AML_DEFER),
+ ARGI_DATA_REGION_OP, ACPI_TYPE_REGION,
+ AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_COMPLEX,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ AML_NSNODE | AML_NAMED | AML_DEFER),
/* 7D */ ACPI_OP("[EvalSubTree]", ARGP_SCOPE_OP, ARGI_SCOPE_OP,
- ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT,
- AML_TYPE_NAMED_NO_OBJ,
- AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
+ ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT,
+ AML_TYPE_NAMED_NO_OBJ,
+ AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE |
AML_NSNODE),
/* ACPI 3.0 opcodes */
/* 7E */ ACPI_OP("Timer", ARGP_TIMER_OP, ARGI_TIMER_OP, ACPI_TYPE_ANY,
- AML_CLASS_EXECUTE, AML_TYPE_EXEC_0A_0T_1R,
+ AML_CLASS_EXECUTE, AML_TYPE_EXEC_0A_0T_1R,
AML_FLAGS_EXEC_0A_0T_1R),
/* ACPI 5.0 opcodes */
diff --git a/drivers/acpi/acpica/psparse.c b/drivers/acpi/acpica/psparse.c
index 98001d7f6f80..b729d9b291d0 100644
--- a/drivers/acpi/acpica/psparse.c
+++ b/drivers/acpi/acpica/psparse.c
@@ -526,8 +526,8 @@ acpi_status acpi_ps_parse_aml(struct acpi_walk_state *walk_state)
}
/*
- * If the transfer to the new method method call worked, a new walk
- * state was created -- get it
+ * If the transfer to the new method method call worked
+ *, a new walk state was created -- get it
*/
walk_state = acpi_ds_get_current_walk_state(thread);
continue;
@@ -544,8 +544,8 @@ acpi_status acpi_ps_parse_aml(struct acpi_walk_state *walk_state)
/* Check for possible multi-thread reentrancy problem */
if ((status == AE_ALREADY_EXISTS) &&
- (!(walk_state->method_desc->method.
- info_flags & ACPI_METHOD_SERIALIZED))) {
+ (!(walk_state->method_desc->method.info_flags &
+ ACPI_METHOD_SERIALIZED))) {
/*
* Method is not serialized and tried to create an object
* twice. The probable cause is that the method cannot
diff --git a/drivers/acpi/acpica/psutils.c b/drivers/acpi/acpica/psutils.c
index 71d2877cd2ce..6cb02a2a1468 100644
--- a/drivers/acpi/acpica/psutils.c
+++ b/drivers/acpi/acpica/psutils.c
@@ -175,8 +175,8 @@ void acpi_ps_free_op(union acpi_parse_object *op)
ACPI_FUNCTION_NAME(ps_free_op);
if (op->common.aml_opcode == AML_INT_RETURN_VALUE_OP) {
- ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "Free retval op: %p\n",
- op));
+ ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS,
+ "Free retval op: %p\n", op));
}
if (op->common.flags & ACPI_PARSEOP_GENERIC) {
diff --git a/drivers/acpi/acpica/pswalk.c b/drivers/acpi/acpica/pswalk.c
index ba5f69171288..f620d4395b66 100644
--- a/drivers/acpi/acpica/pswalk.c
+++ b/drivers/acpi/acpica/pswalk.c
@@ -99,6 +99,7 @@ void acpi_ps_delete_parse_tree(union acpi_parse_object *subtree_root)
if (op == subtree_root) {
return_VOID;
}
+
if (next) {
op = next;
} else {
diff --git a/drivers/acpi/acpica/rsaddr.c b/drivers/acpi/acpica/rsaddr.c
index 66d406e8fe36..bdb7e73cdf4a 100644
--- a/drivers/acpi/acpica/rsaddr.c
+++ b/drivers/acpi/acpica/rsaddr.c
@@ -312,8 +312,8 @@ acpi_rs_get_address_common(struct acpi_resource *resource,
/* Validate the Resource Type */
- if ((aml->address.resource_type > 2)
- && (aml->address.resource_type < 0xC0)) {
+ if ((aml->address.resource_type > 2) &&
+ (aml->address.resource_type < 0xC0)) {
return (FALSE);
}
diff --git a/drivers/acpi/acpica/rscalc.c b/drivers/acpi/acpica/rscalc.c
index cb739a694931..88fce58cc545 100644
--- a/drivers/acpi/acpica/rscalc.c
+++ b/drivers/acpi/acpica/rscalc.c
@@ -143,16 +143,17 @@ acpi_rs_stream_option_length(u32 resource_length,
ACPI_FUNCTION_ENTRY();
/*
- * The resource_source_index and resource_source are optional elements of some
- * Large-type resource descriptors.
+ * The resource_source_index and resource_source are optional elements of
+ * some Large-type resource descriptors.
*/
/*
- * If the length of the actual resource descriptor is greater than the ACPI
- * spec-defined minimum length, it means that a resource_source_index exists
- * and is followed by a (required) null terminated string. The string length
- * (including the null terminator) is the resource length minus the minimum
- * length, minus one byte for the resource_source_index itself.
+ * If the length of the actual resource descriptor is greater than the
+ * ACPI spec-defined minimum length, it means that a resource_source_index
+ * exists and is followed by a (required) null terminated string. The
+ * string length (including the null terminator) is the resource length
+ * minus the minimum length, minus one byte for the resource_source_index
+ * itself.
*/
if (resource_length > minimum_aml_resource_length) {
@@ -277,11 +278,11 @@ acpi_rs_get_aml_length(struct acpi_resource *resource,
* 16-Bit Address Resource:
* Add the size of the optional resource_source info
*/
- total_size = (acpi_rs_length)
- (total_size +
- acpi_rs_struct_option_length(&resource->data.
- address16.
- resource_source));
+ total_size = (acpi_rs_length) (total_size +
+ acpi_rs_struct_option_length
+ (&resource->data.
+ address16.
+ resource_source));
break;
case ACPI_RESOURCE_TYPE_ADDRESS32:
@@ -289,11 +290,11 @@ acpi_rs_get_aml_length(struct acpi_resource *resource,
* 32-Bit Address Resource:
* Add the size of the optional resource_source info
*/
- total_size = (acpi_rs_length)
- (total_size +
- acpi_rs_struct_option_length(&resource->data.
- address32.
- resource_source));
+ total_size = (acpi_rs_length) (total_size +
+ acpi_rs_struct_option_length
+ (&resource->data.
+ address32.
+ resource_source));
break;
case ACPI_RESOURCE_TYPE_ADDRESS64:
@@ -301,11 +302,11 @@ acpi_rs_get_aml_length(struct acpi_resource *resource,
* 64-Bit Address Resource:
* Add the size of the optional resource_source info
*/
- total_size = (acpi_rs_length)
- (total_size +
- acpi_rs_struct_option_length(&resource->data.
- address64.
- resource_source));
+ total_size = (acpi_rs_length) (total_size +
+ acpi_rs_struct_option_length
+ (&resource->data.
+ address64.
+ resource_source));
break;
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
@@ -314,26 +315,28 @@ acpi_rs_get_aml_length(struct acpi_resource *resource,
* Add the size of each additional optional interrupt beyond the
* required 1 (4 bytes for each u32 interrupt number)
*/
- total_size = (acpi_rs_length)
- (total_size +
- ((resource->data.extended_irq.interrupt_count -
- 1) * 4) +
- /* Add the size of the optional resource_source info */
- acpi_rs_struct_option_length(&resource->data.
- extended_irq.
- resource_source));
+ total_size = (acpi_rs_length) (total_size +
+ ((resource->data.
+ extended_irq.
+ interrupt_count -
+ 1) * 4) +
+ /* Add the size of the optional resource_source info */
+ acpi_rs_struct_option_length
+ (&resource->data.
+ extended_irq.
+ resource_source));
break;
case ACPI_RESOURCE_TYPE_GPIO:
- total_size =
- (acpi_rs_length) (total_size +
- (resource->data.gpio.
- pin_table_length * 2) +
- resource->data.gpio.
- resource_source.string_length +
- resource->data.gpio.
- vendor_length);
+ total_size = (acpi_rs_length) (total_size +
+ (resource->data.gpio.
+ pin_table_length * 2) +
+ resource->data.gpio.
+ resource_source.
+ string_length +
+ resource->data.gpio.
+ vendor_length);
break;
@@ -566,8 +569,8 @@ acpi_rs_get_list_length(u8 * aml_buffer,
acpi_gbl_resource_struct_sizes[resource_index] +
extra_struct_bytes;
}
- buffer_size = (u32)ACPI_ROUND_UP_TO_NATIVE_WORD(buffer_size);
+ buffer_size = (u32)ACPI_ROUND_UP_TO_NATIVE_WORD(buffer_size);
*size_needed += buffer_size;
ACPI_DEBUG_PRINT((ACPI_DB_RESOURCES,
diff --git a/drivers/acpi/acpica/rscreate.c b/drivers/acpi/acpica/rscreate.c
index a5344428f3ae..603e544e3f64 100644
--- a/drivers/acpi/acpica/rscreate.c
+++ b/drivers/acpi/acpica/rscreate.c
@@ -81,8 +81,9 @@ acpi_buffer_to_resource(u8 *aml_buffer,
/* Get the required length for the converted resource */
- status = acpi_rs_get_list_length(aml_buffer, aml_buffer_length,
- &list_size_needed);
+ status =
+ acpi_rs_get_list_length(aml_buffer, aml_buffer_length,
+ &list_size_needed);
if (status == AE_AML_NO_RESOURCE_END_TAG) {
status = AE_OK;
}
@@ -232,8 +233,9 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object,
/* Get the required buffer length */
- status = acpi_rs_get_pci_routing_table_length(package_object,
- &buffer_size_needed);
+ status =
+ acpi_rs_get_pci_routing_table_length(package_object,
+ &buffer_size_needed);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
@@ -270,9 +272,9 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object,
user_prt = ACPI_CAST_PTR(struct acpi_pci_routing_table, buffer);
/*
- * Fill in the Length field with the information we have at this point.
- * The minus four is to subtract the size of the u8 Source[4] member
- * because it is added below.
+ * Fill in the Length field with the information we have at this
+ * point. The minus four is to subtract the size of the u8
+ * Source[4] member because it is added below.
*/
user_prt->length = (sizeof(struct acpi_pci_routing_table) - 4);
@@ -345,11 +347,7 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object,
(u8 *) output_buffer->pointer);
path_buffer.pointer = user_prt->source;
- status =
- acpi_ns_handle_to_pathname((acpi_handle)
- node,
- &path_buffer,
- FALSE);
+ status = acpi_ns_handle_to_pathname((acpi_handle) node, &path_buffer, FALSE);
/* +1 to include null terminator */
@@ -371,8 +369,8 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object,
case ACPI_TYPE_INTEGER:
/*
- * If this is a number, then the Source Name is NULL, since the
- * entire buffer was zeroed out, we can leave this alone.
+ * If this is a number, then the Source Name is NULL, since
+ * the entire buffer was zeroed out, we can leave this alone.
*
* Add to the Length field the length of the u32 NULL
*/
@@ -451,9 +449,9 @@ acpi_rs_create_aml_resources(struct acpi_buffer *resource_list,
/* Get the buffer size needed for the AML byte stream */
- status = acpi_rs_get_aml_length(resource_list->pointer,
- resource_list->length,
- &aml_size_needed);
+ status =
+ acpi_rs_get_aml_length(resource_list->pointer,
+ resource_list->length, &aml_size_needed);
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "AmlSizeNeeded=%X, %s\n",
(u32)aml_size_needed, acpi_format_exception(status)));
diff --git a/drivers/acpi/acpica/rsdump.c b/drivers/acpi/acpica/rsdump.c
index 2a09288e7c57..05cc560699e1 100644
--- a/drivers/acpi/acpica/rsdump.c
+++ b/drivers/acpi/acpica/rsdump.c
@@ -483,6 +483,7 @@ static void acpi_rs_dump_address_common(union acpi_resource_data *resource)
static void acpi_rs_out_string(char *title, char *value)
{
+
acpi_os_printf("%27s : %s", title, value);
if (!*value) {
acpi_os_printf("[NULL NAMESTRING]");
@@ -497,21 +498,25 @@ static void acpi_rs_out_integer8(char *title, u8 value)
static void acpi_rs_out_integer16(char *title, u16 value)
{
+
acpi_os_printf("%27s : %4.4X\n", title, value);
}
static void acpi_rs_out_integer32(char *title, u32 value)
{
+
acpi_os_printf("%27s : %8.8X\n", title, value);
}
static void acpi_rs_out_integer64(char *title, u64 value)
{
+
acpi_os_printf("%27s : %8.8X%8.8X\n", title, ACPI_FORMAT_UINT64(value));
}
static void acpi_rs_out_title(char *title)
{
+
acpi_os_printf("%27s : ", title);
}
@@ -544,6 +549,7 @@ static void acpi_rs_dump_short_byte_list(u8 length, u8 * data)
for (i = 0; i < length; i++) {
acpi_os_printf("%X ", data[i]);
}
+
acpi_os_printf("\n");
}
diff --git a/drivers/acpi/acpica/rslist.c b/drivers/acpi/acpica/rslist.c
index 50d5be2ee062..286ccb461a20 100644
--- a/drivers/acpi/acpica/rslist.c
+++ b/drivers/acpi/acpica/rslist.c
@@ -89,6 +89,7 @@ acpi_rs_convert_aml_to_resources(u8 * aml,
/* Get the appropriate conversion info table */
aml_resource = ACPI_CAST_PTR(union aml_resource, aml);
+
if (acpi_ut_get_resource_type(aml) == ACPI_RESOURCE_NAME_SERIAL_BUS) {
if (aml_resource->common_serial_bus.type >
AML_RESOURCE_MAX_SERIALBUSTYPE) {
@@ -225,10 +226,10 @@ acpi_rs_convert_resources_to_aml(struct acpi_resource *resource,
/* Perform final sanity check on the new AML resource descriptor */
- status = acpi_ut_validate_resource(NULL,
- ACPI_CAST_PTR(union
- aml_resource,
- aml), NULL);
+ status =
+ acpi_ut_validate_resource(NULL,
+ ACPI_CAST_PTR(union aml_resource,
+ aml), NULL);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/rsmisc.c b/drivers/acpi/acpica/rsmisc.c
index ac37852e0821..b112c7b1abbf 100644
--- a/drivers/acpi/acpica/rsmisc.c
+++ b/drivers/acpi/acpica/rsmisc.c
@@ -189,8 +189,8 @@ acpi_rs_convert_aml_to_resource(struct acpi_resource *resource,
item_count = ACPI_GET8(source);
ACPI_SET8(destination, item_count);
- resource->length = resource->length +
- (info->value * item_count);
+ resource->length =
+ resource->length + (info->value * item_count);
break;
case ACPI_RSC_COUNT_GPIO_RES:
@@ -445,8 +445,8 @@ exit:
/* Round the resource struct length up to the next boundary (32 or 64) */
- resource->length =
- (u32) ACPI_ROUND_UP_TO_NATIVE_WORD(resource->length);
+ resource->length = (u32)
+ ACPI_ROUND_UP_TO_NATIVE_WORD(resource->length);
}
return_ACPI_STATUS(AE_OK);
}
@@ -550,9 +550,8 @@ acpi_rs_convert_resource_to_aml(struct acpi_resource *resource,
item_count = ACPI_GET8(source);
ACPI_SET8(destination, item_count);
- aml_length =
- (u16) (aml_length +
- (info->value * (item_count - 1)));
+ aml_length = (u16)
+ (aml_length + (info->value * (item_count - 1)));
break;
case ACPI_RSC_COUNT16:
@@ -723,11 +722,10 @@ acpi_rs_convert_resource_to_aml(struct acpi_resource *resource,
/*
* 16-bit encoded bitmask (IRQ macro)
*/
- temp16 = acpi_rs_encode_bitmask(source,
- *ACPI_ADD_PTR(u8,
- resource,
- info->
- value));
+ temp16 =
+ acpi_rs_encode_bitmask(source,
+ *ACPI_ADD_PTR(u8, resource,
+ info->value));
ACPI_MOVE_16_TO_16(destination, &temp16);
break;
diff --git a/drivers/acpi/acpica/rsutils.c b/drivers/acpi/acpica/rsutils.c
index 9486992edbb8..33e558c9434f 100644
--- a/drivers/acpi/acpica/rsutils.c
+++ b/drivers/acpi/acpica/rsutils.c
@@ -221,14 +221,13 @@ acpi_rs_set_resource_length(acpi_rsdesc_size total_length,
ACPI_MOVE_16_TO_16(&aml->large_header.resource_length,
&resource_length);
} else {
- /* Small descriptor -- bits 2:0 of byte 0 contain the length */
-
+ /*
+ * Small descriptor -- bits 2:0 of byte 0 contain the length
+ * Clear any existing length, preserving descriptor type bits
+ */
aml->small_header.descriptor_type = (u8)
-
- /* Clear any existing length, preserving descriptor type bits */
- ((aml->small_header.
- descriptor_type & ~ACPI_RESOURCE_NAME_SMALL_LENGTH_MASK)
-
+ ((aml->small_header.descriptor_type &
+ ~ACPI_RESOURCE_NAME_SMALL_LENGTH_MASK)
| resource_length);
}
}
@@ -333,8 +332,8 @@ acpi_rs_get_resource_source(acpi_rs_length resource_length,
aml_resource_source = ACPI_ADD_PTR(u8, aml, minimum_length);
/*
- * resource_source is present if the length of the descriptor is longer than
- * the minimum length.
+ * resource_source is present if the length of the descriptor is longer
+ * than the minimum length.
*
* Note: Some resource descriptors will have an additional null, so
* we add 1 to the minimum length.
@@ -366,6 +365,7 @@ acpi_rs_get_resource_source(acpi_rs_length resource_length,
total_length =
(u32)strlen(ACPI_CAST_PTR(char, &aml_resource_source[1])) +
1;
+
total_length = (u32)ACPI_ROUND_UP_TO_NATIVE_WORD(total_length);
memset(resource_source->string_ptr, 0, total_length);
@@ -438,8 +438,8 @@ acpi_rs_set_resource_source(union aml_resource * aml,
* Add the length of the string (+ 1 for null terminator) to the
* final descriptor length
*/
- descriptor_length +=
- ((acpi_rsdesc_size) resource_source->string_length + 1);
+ descriptor_length += ((acpi_rsdesc_size)
+ resource_source->string_length + 1);
}
/* Return the new total length of the AML descriptor */
@@ -478,8 +478,9 @@ acpi_rs_get_prt_method_data(struct acpi_namespace_node * node,
/* Execute the method, no parameters */
- status = acpi_ut_evaluate_object(node, METHOD_NAME__PRT,
- ACPI_BTYPE_PACKAGE, &obj_desc);
+ status =
+ acpi_ut_evaluate_object(node, METHOD_NAME__PRT, ACPI_BTYPE_PACKAGE,
+ &obj_desc);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
@@ -527,8 +528,9 @@ acpi_rs_get_crs_method_data(struct acpi_namespace_node *node,
/* Execute the method, no parameters */
- status = acpi_ut_evaluate_object(node, METHOD_NAME__CRS,
- ACPI_BTYPE_BUFFER, &obj_desc);
+ status =
+ acpi_ut_evaluate_object(node, METHOD_NAME__CRS, ACPI_BTYPE_BUFFER,
+ &obj_desc);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
@@ -577,8 +579,9 @@ acpi_rs_get_prs_method_data(struct acpi_namespace_node *node,
/* Execute the method, no parameters */
- status = acpi_ut_evaluate_object(node, METHOD_NAME__PRS,
- ACPI_BTYPE_BUFFER, &obj_desc);
+ status =
+ acpi_ut_evaluate_object(node, METHOD_NAME__PRS, ACPI_BTYPE_BUFFER,
+ &obj_desc);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
@@ -627,8 +630,9 @@ acpi_rs_get_aei_method_data(struct acpi_namespace_node *node,
/* Execute the method, no parameters */
- status = acpi_ut_evaluate_object(node, METHOD_NAME__AEI,
- ACPI_BTYPE_BUFFER, &obj_desc);
+ status =
+ acpi_ut_evaluate_object(node, METHOD_NAME__AEI, ACPI_BTYPE_BUFFER,
+ &obj_desc);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/rsxface.c b/drivers/acpi/acpica/rsxface.c
index 1e8cd5723326..308bfd6bff5f 100644
--- a/drivers/acpi/acpica/rsxface.c
+++ b/drivers/acpi/acpica/rsxface.c
@@ -53,7 +53,7 @@ ACPI_MODULE_NAME("rsxface")
/* Local macros for 16,32-bit to 64-bit conversion */
#define ACPI_COPY_FIELD(out, in, field) ((out)->field = (in)->field)
-#define ACPI_COPY_ADDRESS(out, in) \
+#define ACPI_COPY_ADDRESS(out, in) \
ACPI_COPY_FIELD(out, in, resource_type); \
ACPI_COPY_FIELD(out, in, producer_consumer); \
ACPI_COPY_FIELD(out, in, decode); \
diff --git a/drivers/acpi/acpica/tbdata.c b/drivers/acpi/acpica/tbdata.c
index 5c9d5abf1588..4a8152777767 100644
--- a/drivers/acpi/acpica/tbdata.c
+++ b/drivers/acpi/acpica/tbdata.c
@@ -407,6 +407,7 @@ acpi_tb_verify_temp_table(struct acpi_table_desc * table_desc, char *signature)
table_desc->signature.ascii : "????",
ACPI_FORMAT_UINT64(table_desc->
address)));
+
goto invalidate_and_exit;
}
}
diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c
index 6319b42420c6..bd87801acedf 100644
--- a/drivers/acpi/acpica/tbinstal.c
+++ b/drivers/acpi/acpica/tbinstal.c
@@ -337,8 +337,8 @@ acpi_tb_install_standard_table(acpi_physical_address address,
* need to be unregistered when they are unloaded, and slots in the
* root table list should be reused when empty.
*/
- if (acpi_gbl_root_table_list.tables[i].
- flags & ACPI_TABLE_IS_LOADED) {
+ if (acpi_gbl_root_table_list.tables[i].flags &
+ ACPI_TABLE_IS_LOADED) {
/* Table is still loaded, this is an error */
diff --git a/drivers/acpi/acpica/tbprint.c b/drivers/acpi/acpica/tbprint.c
index 709d5112fc16..d0d12596cfc9 100644
--- a/drivers/acpi/acpica/tbprint.c
+++ b/drivers/acpi/acpica/tbprint.c
@@ -76,6 +76,7 @@ static void acpi_tb_fix_string(char *string, acpi_size length)
if (!isprint((int)*string)) {
*string = '?';
}
+
string++;
length--;
}
diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c
index d8ddef38c947..7c1b5f8a5cbf 100644
--- a/drivers/acpi/acpica/tbutils.c
+++ b/drivers/acpi/acpica/tbutils.c
@@ -121,6 +121,7 @@ void acpi_tb_check_dsdt_header(void)
ACPI_BIOS_ERROR((AE_INFO,
"The DSDT has been corrupted or replaced - "
"old, new headers below"));
+
acpi_tb_print_table_header(0, &acpi_gbl_original_dsdt_header);
acpi_tb_print_table_header(0, acpi_gbl_DSDT);
@@ -379,7 +380,6 @@ next_table:
}
acpi_os_unmap_memory(table, length);
-
return_ACPI_STATUS(AE_OK);
}
@@ -389,7 +389,7 @@ next_table:
*
* PARAMETERS: signature - Sig string to be validated
*
- * RETURN: TRUE if signature is correct length and has valid characters
+ * RETURN: TRUE if signature is has 4 valid ACPI characters
*
* DESCRIPTION: Validate an ACPI table signature.
*
@@ -399,12 +399,6 @@ u8 acpi_is_valid_signature(char *signature)
{
u32 i;
- /* Validate the signature length */
-
- if (strlen(signature) != ACPI_NAME_SIZE) {
- return (FALSE);
- }
-
/* Validate each character in the signature */
for (i = 0; i < ACPI_NAME_SIZE; i++) {
diff --git a/drivers/acpi/acpica/tbxfload.c b/drivers/acpi/acpica/tbxfload.c
index 55ee14ca9418..ca2f1366b498 100644
--- a/drivers/acpi/acpica/tbxfload.c
+++ b/drivers/acpi/acpica/tbxfload.c
@@ -191,6 +191,7 @@ acpi_status acpi_tb_load_namespace(void)
"(%4.4s:%8.8s) while loading table",
table->signature.ascii,
table->pointer->oem_table_id));
+
tables_failed++;
ACPI_DEBUG_PRINT_RAW((ACPI_DB_INIT,
@@ -206,7 +207,7 @@ acpi_status acpi_tb_load_namespace(void)
if (!tables_failed) {
ACPI_INFO((AE_INFO,
- "%u ACPI AML tables successfully acquired and loaded",
+ "%u ACPI AML tables successfully acquired and loaded\n",
tables_loaded));
} else {
ACPI_ERROR((AE_INFO,
diff --git a/drivers/acpi/acpica/utaddress.c b/drivers/acpi/acpica/utaddress.c
index 911ea8e7fe87..38a29e235b74 100644
--- a/drivers/acpi/acpica/utaddress.c
+++ b/drivers/acpi/acpica/utaddress.c
@@ -239,8 +239,9 @@ acpi_ut_check_address_range(acpi_adr_space_type space_id,
overlap_count++;
if (warn) { /* Optional warning message */
pathname =
- acpi_ns_get_external_pathname(range_info->
- region_node);
+ acpi_ns_get_normalized_pathname(range_info->
+ region_node,
+ TRUE);
ACPI_WARNING((AE_INFO,
"%s range 0x%8.8X%8.8X-0x%8.8X%8.8X conflicts with OpRegion 0x%8.8X%8.8X-0x%8.8X%8.8X (%s)",
diff --git a/drivers/acpi/acpica/utcopy.c b/drivers/acpi/acpica/utcopy.c
index 257221d452c8..ade8acf3f3a5 100644
--- a/drivers/acpi/acpica/utcopy.c
+++ b/drivers/acpi/acpica/utcopy.c
@@ -257,9 +257,9 @@ acpi_ut_copy_ielement_to_eelement(u8 object_type,
ACPI_FUNCTION_ENTRY();
this_index = state->pkg.index;
- target_object = (union acpi_object *)
- &((union acpi_object *)(state->pkg.dest_object))->package.
- elements[this_index];
+ target_object = (union acpi_object *)&((union acpi_object *)
+ (state->pkg.dest_object))->
+ package.elements[this_index];
switch (object_type) {
case ACPI_COPY_TYPE_SIMPLE:
@@ -348,15 +348,15 @@ acpi_ut_copy_ipackage_to_epackage(union acpi_operand_object *internal_object,
* Free space begins right after the first package
*/
info.length = ACPI_ROUND_UP_TO_NATIVE_WORD(sizeof(union acpi_object));
- info.free_space =
- buffer + ACPI_ROUND_UP_TO_NATIVE_WORD(sizeof(union acpi_object));
+ info.free_space = buffer +
+ ACPI_ROUND_UP_TO_NATIVE_WORD(sizeof(union acpi_object));
info.object_space = 0;
info.num_packages = 1;
external_object->type = internal_object->common.type;
external_object->package.count = internal_object->package.count;
- external_object->package.elements = ACPI_CAST_PTR(union acpi_object,
- info.free_space);
+ external_object->package.elements =
+ ACPI_CAST_PTR(union acpi_object, info.free_space);
/*
* Leave room for an array of ACPI_OBJECTS in the buffer
@@ -593,8 +593,8 @@ acpi_ut_copy_epackage_to_ipackage(union acpi_object *external_object,
package_elements = package_object->package.elements;
/*
- * Recursive implementation. Probably ok, since nested external packages
- * as parameters should be very rare.
+ * Recursive implementation. Probably ok, since nested external
+ * packages as parameters should be very rare.
*/
for (i = 0; i < external_object->package.count; i++) {
status =
@@ -649,9 +649,8 @@ acpi_ut_copy_eobject_to_iobject(union acpi_object *external_object,
/*
* Build a simple object (no nested objects)
*/
- status =
- acpi_ut_copy_esimple_to_isimple(external_object,
- internal_object);
+ status = acpi_ut_copy_esimple_to_isimple(external_object,
+ internal_object);
}
return_ACPI_STATUS(status);
diff --git a/drivers/acpi/acpica/utdecode.c b/drivers/acpi/acpica/utdecode.c
index ecaaaffc0788..3533135dbd4d 100644
--- a/drivers/acpi/acpica/utdecode.c
+++ b/drivers/acpi/acpica/utdecode.c
@@ -114,7 +114,7 @@ const char *acpi_gbl_region_types[ACPI_NUM_PREDEFINED_REGIONS] = {
"PCC" /* 0x0A */
};
-char *acpi_ut_get_region_name(u8 space_id)
+const char *acpi_ut_get_region_name(u8 space_id)
{
if (space_id >= ACPI_USER_REGION_BEGIN) {
@@ -127,7 +127,7 @@ char *acpi_ut_get_region_name(u8 space_id)
return ("InvalidSpaceId");
}
- return (ACPI_CAST_PTR(char, acpi_gbl_region_types[space_id]));
+ return (acpi_gbl_region_types[space_id]);
}
/*******************************************************************************
@@ -152,14 +152,14 @@ static const char *acpi_gbl_event_types[ACPI_NUM_FIXED_EVENTS] = {
"RealTimeClock",
};
-char *acpi_ut_get_event_name(u32 event_id)
+const char *acpi_ut_get_event_name(u32 event_id)
{
if (event_id > ACPI_EVENT_MAX) {
return ("InvalidEventID");
}
- return (ACPI_CAST_PTR(char, acpi_gbl_event_types[event_id]));
+ return (acpi_gbl_event_types[event_id]);
}
/*******************************************************************************
@@ -180,7 +180,8 @@ char *acpi_ut_get_event_name(u32 event_id)
*
* The type ACPI_TYPE_ANY (Untyped) is used as a "don't care" when searching;
* when stored in a table it really means that we have thus far seen no
- * evidence to indicate what type is actually going to be stored for this entry.
+ * evidence to indicate what type is actually going to be stored for this
+ & entry.
*/
static const char acpi_gbl_bad_type[] = "UNDEFINED";
@@ -220,17 +221,17 @@ static const char *acpi_gbl_ns_type_names[] = {
/* 30 */ "Invalid"
};
-char *acpi_ut_get_type_name(acpi_object_type type)
+const char *acpi_ut_get_type_name(acpi_object_type type)
{
if (type > ACPI_TYPE_INVALID) {
- return (ACPI_CAST_PTR(char, acpi_gbl_bad_type));
+ return (acpi_gbl_bad_type);
}
- return (ACPI_CAST_PTR(char, acpi_gbl_ns_type_names[type]));
+ return (acpi_gbl_ns_type_names[type]);
}
-char *acpi_ut_get_object_type_name(union acpi_operand_object *obj_desc)
+const char *acpi_ut_get_object_type_name(union acpi_operand_object *obj_desc)
{
ACPI_FUNCTION_TRACE(ut_get_object_type_name);
@@ -267,7 +268,7 @@ char *acpi_ut_get_object_type_name(union acpi_operand_object *obj_desc)
*
******************************************************************************/
-char *acpi_ut_get_node_name(void *object)
+const char *acpi_ut_get_node_name(void *object)
{
struct acpi_namespace_node *node = (struct acpi_namespace_node *)object;
@@ -333,7 +334,7 @@ static const char *acpi_gbl_desc_type_names[] = {
/* 15 */ "Node"
};
-char *acpi_ut_get_descriptor_name(void *object)
+const char *acpi_ut_get_descriptor_name(void *object)
{
if (!object) {
@@ -344,10 +345,7 @@ char *acpi_ut_get_descriptor_name(void *object)
return ("Not a Descriptor");
}
- return (ACPI_CAST_PTR(char,
- acpi_gbl_desc_type_names[ACPI_GET_DESCRIPTOR_TYPE
- (object)]));
-
+ return (acpi_gbl_desc_type_names[ACPI_GET_DESCRIPTOR_TYPE(object)]);
}
/*******************************************************************************
@@ -415,7 +413,7 @@ const char *acpi_ut_get_reference_name(union acpi_operand_object *object)
/* Names for internal mutex objects, used for debug output */
-static char *acpi_gbl_mutex_names[ACPI_NUM_MUTEX] = {
+static const char *acpi_gbl_mutex_names[ACPI_NUM_MUTEX] = {
"ACPI_MTX_Interpreter",
"ACPI_MTX_Namespace",
"ACPI_MTX_Tables",
@@ -424,7 +422,7 @@ static char *acpi_gbl_mutex_names[ACPI_NUM_MUTEX] = {
"ACPI_MTX_Memory",
};
-char *acpi_ut_get_mutex_name(u32 mutex_id)
+const char *acpi_ut_get_mutex_name(u32 mutex_id)
{
if (mutex_id > ACPI_MAX_MUTEX) {
diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c
index 1638312e3d8f..1afd7427a90c 100644
--- a/drivers/acpi/acpica/utdelete.c
+++ b/drivers/acpi/acpica/utdelete.c
@@ -209,6 +209,7 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object)
acpi_ut_delete_object_desc(object->method.mutex);
object->method.mutex = NULL;
}
+
if (object->method.node) {
object->method.node = NULL;
}
@@ -515,8 +516,8 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action)
}
/*
- * All sub-objects must have their reference count incremented also.
- * Different object types have different subobjects.
+ * All sub-objects must have their reference count incremented
+ * also. Different object types have different subobjects.
*/
switch (object->common.type) {
case ACPI_TYPE_DEVICE:
diff --git a/drivers/acpi/acpica/uterror.c b/drivers/acpi/acpica/uterror.c
index 9ef80f2828e3..f93bb90ea72a 100644
--- a/drivers/acpi/acpica/uterror.c
+++ b/drivers/acpi/acpica/uterror.c
@@ -217,8 +217,9 @@ acpi_ut_namespace_error(const char *module_name,
} else {
/* Convert path to external format */
- status = acpi_ns_externalize_name(ACPI_UINT32_MAX,
- internal_name, NULL, &name);
+ status =
+ acpi_ns_externalize_name(ACPI_UINT32_MAX, internal_name,
+ NULL, &name);
/* Print target name */
@@ -271,9 +272,8 @@ acpi_ut_method_error(const char *module_name,
acpi_os_printf(ACPI_MSG_ERROR);
if (path) {
- status =
- acpi_ns_get_node(prefix_node, path, ACPI_NS_NO_UPSEARCH,
- &node);
+ status = acpi_ns_get_node(prefix_node, path,
+ ACPI_NS_NO_UPSEARCH, &node);
if (ACPI_FAILURE(status)) {
acpi_os_printf("[Could not get node by pathname]");
}
diff --git a/drivers/acpi/acpica/utfileio.c b/drivers/acpi/acpica/utfileio.c
deleted file mode 100644
index d435b7b7eb94..000000000000
--- a/drivers/acpi/acpica/utfileio.c
+++ /dev/null
@@ -1,334 +0,0 @@
-/*******************************************************************************
- *
- * Module Name: utfileio - simple file I/O routines
- *
- ******************************************************************************/
-
-/*
- * 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.
- */
-
-#include <acpi/acpi.h>
-#include "accommon.h"
-#include "actables.h"
-#include "acapps.h"
-#include "errno.h"
-
-#ifdef ACPI_ASL_COMPILER
-#include "aslcompiler.h"
-#endif
-
-#define _COMPONENT ACPI_CA_DEBUGGER
-ACPI_MODULE_NAME("utfileio")
-
-#ifdef ACPI_APPLICATION
-/* Local prototypes */
-static acpi_status
-acpi_ut_check_text_mode_corruption(u8 *table,
- u32 table_length, u32 file_length);
-
-static acpi_status
-acpi_ut_read_table(FILE * fp,
- struct acpi_table_header **table, u32 *table_length);
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_ut_check_text_mode_corruption
- *
- * PARAMETERS: table - Table buffer
- * table_length - Length of table from the table header
- * file_length - Length of the file that contains the table
- *
- * RETURN: Status
- *
- * DESCRIPTION: Check table for text mode file corruption where all linefeed
- * characters (LF) have been replaced by carriage return linefeed
- * pairs (CR/LF).
- *
- ******************************************************************************/
-
-static acpi_status
-acpi_ut_check_text_mode_corruption(u8 *table, u32 table_length, u32 file_length)
-{
- u32 i;
- u32 pairs = 0;
-
- if (table_length != file_length) {
- ACPI_WARNING((AE_INFO,
- "File length (0x%X) is not the same as the table length (0x%X)",
- file_length, table_length));
- }
-
- /* Scan entire table to determine if each LF has been prefixed with a CR */
-
- for (i = 1; i < file_length; i++) {
- if (table[i] == 0x0A) {
- if (table[i - 1] != 0x0D) {
-
- /* The LF does not have a preceding CR, table not corrupted */
-
- return (AE_OK);
- } else {
- /* Found a CR/LF pair */
-
- pairs++;
- }
- i++;
- }
- }
-
- if (!pairs) {
- return (AE_OK);
- }
-
- /*
- * Entire table scanned, each CR is part of a CR/LF pair --
- * meaning that the table was treated as a text file somewhere.
- *
- * NOTE: We can't "fix" the table, because any existing CR/LF pairs in the
- * original table are left untouched by the text conversion process --
- * meaning that we cannot simply replace CR/LF pairs with LFs.
- */
- acpi_os_printf("Table has been corrupted by text mode conversion\n");
- acpi_os_printf("All LFs (%u) were changed to CR/LF pairs\n", pairs);
- acpi_os_printf("Table cannot be repaired!\n");
- return (AE_BAD_VALUE);
-}
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_ut_read_table
- *
- * PARAMETERS: fp - File that contains table
- * table - Return value, buffer with table
- * table_length - Return value, length of table
- *
- * RETURN: Status
- *
- * DESCRIPTION: Load the DSDT from the file pointer
- *
- ******************************************************************************/
-
-static acpi_status
-acpi_ut_read_table(FILE * fp,
- struct acpi_table_header **table, u32 *table_length)
-{
- struct acpi_table_header table_header;
- u32 actual;
- acpi_status status;
- u32 file_size;
- u8 standard_header = TRUE;
- s32 count;
-
- /* Get the file size */
-
- file_size = cm_get_file_size(fp);
- if (file_size == ACPI_UINT32_MAX) {
- return (AE_ERROR);
- }
-
- if (file_size < 4) {
- return (AE_BAD_HEADER);
- }
-
- /* Read the signature */
-
- fseek(fp, 0, SEEK_SET);
-
- count = fread(&table_header, 1, sizeof(struct acpi_table_header), fp);
- if (count != sizeof(struct acpi_table_header)) {
- acpi_os_printf("Could not read the table header\n");
- return (AE_BAD_HEADER);
- }
-
- /* The RSDP table does not have standard ACPI header */
-
- if (ACPI_VALIDATE_RSDP_SIG(table_header.signature)) {
- *table_length = file_size;
- standard_header = FALSE;
- } else {
-
-#if 0
- /* Validate the table header/length */
-
- status = acpi_tb_validate_table_header(&table_header);
- if (ACPI_FAILURE(status)) {
- acpi_os_printf("Table header is invalid!\n");
- return (status);
- }
-#endif
-
- /* File size must be at least as long as the Header-specified length */
-
- if (table_header.length > file_size) {
- acpi_os_printf
- ("TableHeader length [0x%X] greater than the input file size [0x%X]\n",
- table_header.length, file_size);
-
-#ifdef ACPI_ASL_COMPILER
- acpi_os_printf("File is corrupt or is ASCII text -- "
- "it must be a binary file\n");
-#endif
- return (AE_BAD_HEADER);
- }
-#ifdef ACPI_OBSOLETE_CODE
- /* We only support a limited number of table types */
-
- if (!ACPI_COMPARE_NAME
- ((char *)table_header.signature, ACPI_SIG_DSDT)
- && !ACPI_COMPARE_NAME((char *)table_header.signature,
- ACPI_SIG_PSDT)
- && !ACPI_COMPARE_NAME((char *)table_header.signature,
- ACPI_SIG_SSDT)) {
- acpi_os_printf
- ("Table signature [%4.4s] is invalid or not supported\n",
- (char *)table_header.signature);
- ACPI_DUMP_BUFFER(&table_header,
- sizeof(struct acpi_table_header));
- return (AE_ERROR);
- }
-#endif
-
- *table_length = table_header.length;
- }
-
- /* Allocate a buffer for the table */
-
- *table = acpi_os_allocate((size_t) file_size);
- if (!*table) {
- acpi_os_printf
- ("Could not allocate memory for ACPI table %4.4s (size=0x%X)\n",
- table_header.signature, *table_length);
- return (AE_NO_MEMORY);
- }
-
- /* Get the rest of the table */
-
- fseek(fp, 0, SEEK_SET);
- actual = fread(*table, 1, (size_t) file_size, fp);
- if (actual == file_size) {
- if (standard_header) {
-
- /* Now validate the checksum */
-
- status = acpi_tb_verify_checksum((void *)*table,
- ACPI_CAST_PTR(struct
- acpi_table_header,
- *table)->
- length);
-
- if (status == AE_BAD_CHECKSUM) {
- status =
- acpi_ut_check_text_mode_corruption((u8 *)
- *table,
- file_size,
- (*table)->
- length);
- return (status);
- }
- }
- return (AE_OK);
- }
-
- if (actual > 0) {
- acpi_os_printf("Warning - reading table, asked for %X got %X\n",
- file_size, actual);
- return (AE_OK);
- }
-
- acpi_os_printf("Error - could not read the table file\n");
- acpi_os_free(*table);
- *table = NULL;
- *table_length = 0;
- return (AE_ERROR);
-}
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_ut_read_table_from_file
- *
- * PARAMETERS: filename - File where table is located
- * table - Where a pointer to the table is returned
- *
- * RETURN: Status
- *
- * DESCRIPTION: Get an ACPI table from a file
- *
- ******************************************************************************/
-
-acpi_status
-acpi_ut_read_table_from_file(char *filename, struct acpi_table_header ** table)
-{
- FILE *file;
- u32 file_size;
- u32 table_length;
- acpi_status status = AE_ERROR;
-
- /* Open the file, get current size */
-
- file = fopen(filename, "rb");
- if (!file) {
- perror("Could not open input file");
-
- if (errno == ENOENT) {
- return (AE_NOT_EXIST);
- }
-
- return (status);
- }
-
- file_size = cm_get_file_size(file);
- if (file_size == ACPI_UINT32_MAX) {
- goto exit;
- }
-
- /* Get the entire file */
-
- fprintf(stderr,
- "Reading ACPI table from file %12s - Length %.8u (0x%06X)\n",
- filename, file_size, file_size);
-
- status = acpi_ut_read_table(file, table, &table_length);
- if (ACPI_FAILURE(status)) {
- acpi_os_printf("Could not get table from the file\n");
- }
-
-exit:
- fclose(file);
- return (status);
-}
-
-#endif
diff --git a/drivers/acpi/acpica/uthex.c b/drivers/acpi/acpica/uthex.c
index fda8b3def81c..8ad086ed1a06 100644
--- a/drivers/acpi/acpica/uthex.c
+++ b/drivers/acpi/acpica/uthex.c
@@ -48,7 +48,7 @@
ACPI_MODULE_NAME("uthex")
/* Hex to ASCII conversion table */
-static char acpi_gbl_hex_to_ascii[] = {
+static const char acpi_gbl_hex_to_ascii[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
'E', 'F'
};
diff --git a/drivers/acpi/acpica/utids.c b/drivers/acpi/acpica/utids.c
index 7956df1e263c..05ee76eec314 100644
--- a/drivers/acpi/acpica/utids.c
+++ b/drivers/acpi/acpica/utids.c
@@ -127,73 +127,6 @@ cleanup:
/*******************************************************************************
*
- * FUNCTION: acpi_ut_execute_SUB
- *
- * PARAMETERS: device_node - Node for the device
- * return_id - Where the _SUB is returned
- *
- * RETURN: Status
- *
- * DESCRIPTION: Executes the _SUB control method that returns the subsystem
- * ID of the device. The _SUB value is always a string containing
- * either a valid PNP or ACPI ID.
- *
- * NOTE: Internal function, no parameter validation
- *
- ******************************************************************************/
-
-acpi_status
-acpi_ut_execute_SUB(struct acpi_namespace_node *device_node,
- struct acpi_pnp_device_id **return_id)
-{
- union acpi_operand_object *obj_desc;
- struct acpi_pnp_device_id *sub;
- u32 length;
- acpi_status status;
-
- ACPI_FUNCTION_TRACE(ut_execute_SUB);
-
- status = acpi_ut_evaluate_object(device_node, METHOD_NAME__SUB,
- ACPI_BTYPE_STRING, &obj_desc);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
-
- /* Get the size of the String to be returned, includes null terminator */
-
- length = obj_desc->string.length + 1;
-
- /* Allocate a buffer for the SUB */
-
- sub =
- ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_pnp_device_id) +
- (acpi_size) length);
- if (!sub) {
- status = AE_NO_MEMORY;
- goto cleanup;
- }
-
- /* Area for the string starts after PNP_DEVICE_ID struct */
-
- sub->string =
- ACPI_ADD_PTR(char, sub, sizeof(struct acpi_pnp_device_id));
-
- /* Simply copy existing string */
-
- strcpy(sub->string, obj_desc->string.pointer);
- sub->length = length;
- *return_id = sub;
-
-cleanup:
-
- /* On exit, we must delete the return object */
-
- acpi_ut_remove_reference(obj_desc);
- return_ACPI_STATUS(status);
-}
-
-/*******************************************************************************
- *
* FUNCTION: acpi_ut_execute_UID
*
* PARAMETERS: device_node - Node for the device
diff --git a/drivers/acpi/acpica/utinit.c b/drivers/acpi/acpica/utinit.c
index ccd0745f011e..fd82a122785e 100644
--- a/drivers/acpi/acpica/utinit.c
+++ b/drivers/acpi/acpica/utinit.c
@@ -206,7 +206,6 @@ acpi_status acpi_ut_init_globals(void)
acpi_gbl_next_owner_id_offset = 0;
acpi_gbl_debugger_configuration = DEBUGGER_THREADING;
acpi_gbl_osi_mutex = NULL;
- acpi_gbl_reg_methods_executed = FALSE;
acpi_gbl_max_loop_iterations = 0xFFFF;
/* Hardware oriented */
diff --git a/drivers/acpi/acpica/utmath.c b/drivers/acpi/acpica/utmath.c
index f9ff100f0159..58b5d4236429 100644
--- a/drivers/acpi/acpica/utmath.c
+++ b/drivers/acpi/acpica/utmath.c
@@ -111,6 +111,7 @@ acpi_ut_short_divide(u64 dividend,
*/
ACPI_DIV_64_BY_32(0, dividend_ovl.part.hi, divisor,
quotient.part.hi, remainder32);
+
ACPI_DIV_64_BY_32(remainder32, dividend_ovl.part.lo, divisor,
quotient.part.lo, remainder32);
@@ -179,6 +180,7 @@ acpi_ut_divide(u64 in_dividend,
*/
ACPI_DIV_64_BY_32(0, dividend.part.hi, divisor.part.lo,
quotient.part.hi, partial1);
+
ACPI_DIV_64_BY_32(partial1, dividend.part.lo, divisor.part.lo,
quotient.part.lo, remainder.part.lo);
}
@@ -206,12 +208,12 @@ acpi_ut_divide(u64 in_dividend,
ACPI_DIV_64_BY_32(normalized_dividend.part.hi,
normalized_dividend.part.lo,
- normalized_divisor.part.lo,
- quotient.part.lo, partial1);
+ normalized_divisor.part.lo, quotient.part.lo,
+ partial1);
/*
- * The quotient is always 32 bits, and simply requires adjustment.
- * The 64-bit remainder must be generated.
+ * The quotient is always 32 bits, and simply requires
+ * adjustment. The 64-bit remainder must be generated.
*/
partial1 = quotient.part.lo * divisor.part.hi;
partial2.full = (u64) quotient.part.lo * divisor.part.lo;
diff --git a/drivers/acpi/acpica/utmisc.c b/drivers/acpi/acpica/utmisc.c
index bd4443bdcbad..eab1cfeb52cc 100644
--- a/drivers/acpi/acpica/utmisc.c
+++ b/drivers/acpi/acpica/utmisc.c
@@ -264,8 +264,8 @@ acpi_ut_walk_package_tree(union acpi_operand_object *source_object,
*/
if ((!this_source_obj) ||
(ACPI_GET_DESCRIPTOR_TYPE(this_source_obj) !=
- ACPI_DESC_TYPE_OPERAND)
- || (this_source_obj->common.type != ACPI_TYPE_PACKAGE)) {
+ ACPI_DESC_TYPE_OPERAND) ||
+ (this_source_obj->common.type != ACPI_TYPE_PACKAGE)) {
status =
walk_callback(ACPI_COPY_TYPE_SIMPLE,
this_source_obj, state, context);
@@ -318,9 +318,10 @@ acpi_ut_walk_package_tree(union acpi_operand_object *source_object,
* The callback above returned a new target package object.
*/
acpi_ut_push_generic_state(&state_list, state);
- state = acpi_ut_create_pkg_state(this_source_obj,
- state->pkg.
- this_target_obj, 0);
+ state =
+ acpi_ut_create_pkg_state(this_source_obj,
+ state->pkg.this_target_obj,
+ 0);
if (!state) {
/* Free any stacked Update State objects */
diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c
index ce406e39b669..038ff849ad20 100644
--- a/drivers/acpi/acpica/utmutex.c
+++ b/drivers/acpi/acpica/utmutex.c
@@ -111,17 +111,6 @@ acpi_status acpi_ut_mutex_initialize(void)
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
-#ifdef ACPI_DEBUGGER
-
- /* Debugger Support */
-
- status = acpi_os_create_mutex(&acpi_gbl_db_command_ready);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
-
- status = acpi_os_create_mutex(&acpi_gbl_db_command_complete);
-#endif
return_ACPI_STATUS(status);
}
@@ -162,12 +151,6 @@ void acpi_ut_mutex_terminate(void)
/* Delete the reader/writer lock */
acpi_ut_delete_rw_lock(&acpi_gbl_namespace_rw_lock);
-
-#ifdef ACPI_DEBUGGER
- acpi_os_delete_mutex(acpi_gbl_db_command_ready);
- acpi_os_delete_mutex(acpi_gbl_db_command_complete);
-#endif
-
return_VOID;
}
@@ -290,8 +273,9 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id)
(u32)this_thread_id,
acpi_ut_get_mutex_name(mutex_id)));
- status = acpi_os_acquire_mutex(acpi_gbl_mutex_info[mutex_id].mutex,
- ACPI_WAIT_FOREVER);
+ status =
+ acpi_os_acquire_mutex(acpi_gbl_mutex_info[mutex_id].mutex,
+ ACPI_WAIT_FOREVER);
if (ACPI_SUCCESS(status)) {
ACPI_DEBUG_PRINT((ACPI_DB_MUTEX,
"Thread %u acquired Mutex [%s]\n",
diff --git a/drivers/acpi/acpica/utnonansi.c b/drivers/acpi/acpica/utnonansi.c
index 1d5f6b17b766..9c3cadc27fb8 100644
--- a/drivers/acpi/acpica/utnonansi.c
+++ b/drivers/acpi/acpica/utnonansi.c
@@ -282,8 +282,8 @@ acpi_status acpi_ut_strtoul64(char *string, u32 base, u64 *ret_integer)
/* Divide the digit into the correct position */
- (void)acpi_ut_short_divide((dividend - (u64)this_digit),
- base, &quotient, NULL);
+ (void)acpi_ut_short_divide((dividend - (u64)this_digit), base,
+ &quotient, NULL);
if (return_value > quotient) {
if (to_integer_op) {
diff --git a/drivers/acpi/acpica/utobject.c b/drivers/acpi/acpica/utobject.c
index 7d83efe1ea29..787eccf6a1d5 100644
--- a/drivers/acpi/acpica/utobject.c
+++ b/drivers/acpi/acpica/utobject.c
@@ -112,9 +112,9 @@ union acpi_operand_object *acpi_ut_create_internal_object_dbg(const char
/* These types require a secondary object */
- second_object = acpi_ut_allocate_object_desc_dbg(module_name,
- line_number,
- component_id);
+ second_object =
+ acpi_ut_allocate_object_desc_dbg(module_name, line_number,
+ component_id);
if (!second_object) {
acpi_ut_delete_object_desc(object);
return_PTR(NULL);
@@ -253,7 +253,8 @@ union acpi_operand_object *acpi_ut_create_buffer_object(acpi_size buffer_size)
buffer = ACPI_ALLOCATE_ZEROED(buffer_size);
if (!buffer) {
ACPI_ERROR((AE_INFO, "Could not allocate size %u",
- (u32) buffer_size));
+ (u32)buffer_size));
+
acpi_ut_remove_reference(buffer_desc);
return_PTR(NULL);
}
@@ -305,7 +306,8 @@ union acpi_operand_object *acpi_ut_create_string_object(acpi_size string_size)
string = ACPI_ALLOCATE_ZEROED(string_size + 1);
if (!string) {
ACPI_ERROR((AE_INFO, "Could not allocate size %u",
- (u32) string_size));
+ (u32)string_size));
+
acpi_ut_remove_reference(string_desc);
return_PTR(NULL);
}
@@ -649,8 +651,9 @@ acpi_ut_get_package_object_size(union acpi_operand_object *internal_object,
info.object_space = 0;
info.num_packages = 1;
- status = acpi_ut_walk_package_tree(internal_object, NULL,
- acpi_ut_get_element_length, &info);
+ status =
+ acpi_ut_walk_package_tree(internal_object, NULL,
+ acpi_ut_get_element_length, &info);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
@@ -660,7 +663,8 @@ acpi_ut_get_package_object_size(union acpi_operand_object *internal_object,
* just add the length of the package objects themselves.
* Round up to the next machine word.
*/
- info.length += ACPI_ROUND_UP_TO_NATIVE_WORD(sizeof(union acpi_object)) *
+ info.length +=
+ ACPI_ROUND_UP_TO_NATIVE_WORD(sizeof(union acpi_object)) *
(acpi_size) info.num_packages;
/* Return the total package length */
@@ -692,8 +696,8 @@ acpi_ut_get_object_size(union acpi_operand_object *internal_object,
ACPI_FUNCTION_ENTRY();
if ((ACPI_GET_DESCRIPTOR_TYPE(internal_object) ==
- ACPI_DESC_TYPE_OPERAND)
- && (internal_object->common.type == ACPI_TYPE_PACKAGE)) {
+ ACPI_DESC_TYPE_OPERAND) &&
+ (internal_object->common.type == ACPI_TYPE_PACKAGE)) {
status =
acpi_ut_get_package_object_size(internal_object,
obj_length);
diff --git a/drivers/acpi/acpica/utosi.c b/drivers/acpi/acpica/utosi.c
index 8f3d203aed79..0809d73193e1 100644
--- a/drivers/acpi/acpica/utosi.c
+++ b/drivers/acpi/acpica/utosi.c
@@ -269,9 +269,10 @@ acpi_status acpi_ut_remove_interface(acpi_string interface_name)
previous_interface = next_interface = acpi_gbl_supported_interfaces;
while (next_interface) {
if (!strcmp(interface_name, next_interface->name)) {
-
- /* Found: name is in either the static list or was added at runtime */
-
+ /*
+ * Found: name is in either the static list
+ * or was added at runtime
+ */
if (next_interface->flags & ACPI_OSI_DYNAMIC) {
/* Interface was added dynamically, remove and free it */
@@ -288,8 +289,8 @@ acpi_status acpi_ut_remove_interface(acpi_string interface_name)
ACPI_FREE(next_interface);
} else {
/*
- * Interface is in static list. If marked invalid, then it
- * does not actually exist. Else, mark it invalid.
+ * Interface is in static list. If marked invalid, then
+ * it does not actually exist. Else, mark it invalid.
*/
if (next_interface->flags & ACPI_OSI_INVALID) {
return (AE_NOT_EXIST);
diff --git a/drivers/acpi/acpica/utownerid.c b/drivers/acpi/acpica/utownerid.c
index 2959217067cb..ebb811c43c89 100644
--- a/drivers/acpi/acpica/utownerid.c
+++ b/drivers/acpi/acpica/utownerid.c
@@ -73,8 +73,8 @@ acpi_status acpi_ut_allocate_owner_id(acpi_owner_id * owner_id)
/* Guard against multiple allocations of ID to the same location */
if (*owner_id) {
- ACPI_ERROR((AE_INFO, "Owner ID [0x%2.2X] already exists",
- *owner_id));
+ ACPI_ERROR((AE_INFO,
+ "Owner ID [0x%2.2X] already exists", *owner_id));
return_ACPI_STATUS(AE_ALREADY_EXISTS);
}
@@ -87,8 +87,8 @@ acpi_status acpi_ut_allocate_owner_id(acpi_owner_id * owner_id)
/*
* Find a free owner ID, cycle through all possible IDs on repeated
- * allocations. (ACPI_NUM_OWNERID_MASKS + 1) because first index may have
- * to be scanned twice.
+ * allocations. (ACPI_NUM_OWNERID_MASKS + 1) because first index
+ * may have to be scanned twice.
*/
for (i = 0, j = acpi_gbl_last_owner_id_index;
i < (ACPI_NUM_OWNERID_MASKS + 1); i++, j++) {
@@ -141,8 +141,8 @@ acpi_status acpi_ut_allocate_owner_id(acpi_owner_id * owner_id)
* they are released when a table is unloaded or a method completes
* execution.
*
- * If this error happens, there may be very deep nesting of invoked control
- * methods, or there may be a bug where the IDs are not released.
+ * If this error happens, there may be very deep nesting of invoked
+ * control methods, or there may be a bug where the IDs are not released.
*/
status = AE_OWNER_ID_LIMIT;
ACPI_ERROR((AE_INFO,
diff --git a/drivers/acpi/acpica/utpredef.c b/drivers/acpi/acpica/utpredef.c
index 97898ed71b4b..9f8e415bf0af 100644
--- a/drivers/acpi/acpica/utpredef.c
+++ b/drivers/acpi/acpica/utpredef.c
@@ -225,8 +225,10 @@ const union acpi_predefined_info *acpi_ut_match_resource_name(char *name)
{
const union acpi_predefined_info *this_name;
- /* Quick check for a predefined name, first character must be underscore */
-
+ /*
+ * Quick check for a predefined name, first character must
+ * be underscore
+ */
if (name[0] != '_') {
return (NULL);
}
diff --git a/drivers/acpi/acpica/utprint.c b/drivers/acpi/acpica/utprint.c
index b26297c5de49..01f04da779c5 100644
--- a/drivers/acpi/acpica/utprint.c
+++ b/drivers/acpi/acpica/utprint.c
@@ -314,8 +314,9 @@ static char *acpi_ut_format_number(char *string,
if (need_prefix) {
string = acpi_ut_bound_string_output(string, end, '0');
if (base == 16) {
- string = acpi_ut_bound_string_output(string, end,
- upper ? 'X' : 'x');
+ string =
+ acpi_ut_bound_string_output(string, end,
+ upper ? 'X' : 'x');
}
}
if (!(type & ACPI_FORMAT_LEFT)) {
@@ -400,6 +401,7 @@ acpi_ut_vsnprintf(char *string,
} else {
break;
}
+
} while (1);
/* Process width */
@@ -429,6 +431,7 @@ acpi_ut_vsnprintf(char *string,
++format;
precision = va_arg(args, int);
}
+
if (precision < 0) {
precision = 0;
}
@@ -488,10 +491,12 @@ acpi_ut_vsnprintf(char *string,
' ');
}
}
+
for (i = 0; i < length; ++i) {
pos = acpi_ut_bound_string_output(pos, end, *s);
++s;
}
+
while (length < width--) {
pos =
acpi_ut_bound_string_output(pos, end, ' ');
@@ -529,9 +534,9 @@ acpi_ut_vsnprintf(char *string,
}
p = va_arg(args, void *);
- pos = acpi_ut_format_number(pos, end,
- ACPI_TO_INTEGER(p), 16,
- width, precision, type);
+ pos =
+ acpi_ut_format_number(pos, end, ACPI_TO_INTEGER(p),
+ 16, width, precision, type);
continue;
default:
diff --git a/drivers/acpi/acpica/utresrc.c b/drivers/acpi/acpica/utresrc.c
index b3505dbc715e..d50b41c4daa7 100644
--- a/drivers/acpi/acpica/utresrc.c
+++ b/drivers/acpi/acpica/utresrc.c
@@ -441,8 +441,8 @@ acpi_ut_walk_aml_resources(struct acpi_walk_state *walk_state,
acpi_ut_validate_resource(walk_state, aml, &resource_index);
if (ACPI_FAILURE(status)) {
/*
- * Exit on failure. Cannot continue because the descriptor length
- * may be bogus also.
+ * Exit on failure. Cannot continue because the descriptor
+ * length may be bogus also.
*/
return_ACPI_STATUS(status);
}
@@ -568,8 +568,8 @@ acpi_ut_validate_resource(struct acpi_walk_state *walk_state,
}
/*
- * Check validity of the resource type, via acpi_gbl_resource_types. Zero
- * indicates an invalid resource.
+ * Check validity of the resource type, via acpi_gbl_resource_types.
+ * Zero indicates an invalid resource.
*/
if (!acpi_gbl_resource_types[resource_index]) {
goto invalid_resource;
diff --git a/drivers/acpi/acpica/utstate.c b/drivers/acpi/acpica/utstate.c
index f201171c5dda..0050e00997ed 100644
--- a/drivers/acpi/acpica/utstate.c
+++ b/drivers/acpi/acpica/utstate.c
@@ -246,6 +246,7 @@ union acpi_generic_state *acpi_ut_create_pkg_state(void *internal_object,
state->pkg.dest_object = external_object;
state->pkg.index = index;
state->pkg.num_packages = 1;
+
return (state);
}
@@ -279,6 +280,7 @@ union acpi_generic_state *acpi_ut_create_control_state(void)
state->common.descriptor_type = ACPI_DESC_TYPE_STATE_CONTROL;
state->common.state = ACPI_CONTROL_CONDITIONAL_EXECUTING;
+
return (state);
}
@@ -304,5 +306,6 @@ void acpi_ut_delete_generic_state(union acpi_generic_state *state)
if (state) {
(void)acpi_os_release_object(acpi_gbl_state_cache, state);
}
+
return;
}
diff --git a/drivers/acpi/acpica/utstring.c b/drivers/acpi/acpica/utstring.c
index 4ddd105d9741..958b2f7b552d 100644
--- a/drivers/acpi/acpica/utstring.c
+++ b/drivers/acpi/acpica/utstring.c
@@ -135,6 +135,7 @@ void acpi_ut_print_string(char *string, u16 max_length)
break;
}
}
+
acpi_os_printf("\"");
if (i == max_length && string[i]) {
@@ -239,6 +240,14 @@ void acpi_ut_repair_name(char *name)
ACPI_FUNCTION_NAME(ut_repair_name);
+ /*
+ * Special case for the root node. This can happen if we get an
+ * error during the execution of module-level code.
+ */
+ if (ACPI_COMPARE_NAME(name, "\\___")) {
+ return;
+ }
+
ACPI_MOVE_NAME(&original_name, name);
/* Check each character in the name */
diff --git a/drivers/acpi/acpica/uttrack.c b/drivers/acpi/acpica/uttrack.c
index 9a7dc8196a5d..ea698e98442e 100644
--- a/drivers/acpi/acpica/uttrack.c
+++ b/drivers/acpi/acpica/uttrack.c
@@ -150,9 +150,9 @@ void *acpi_ut_allocate_and_track(acpi_size size,
return (NULL);
}
- status = acpi_ut_track_allocation(allocation, size,
- ACPI_MEM_MALLOC, component, module,
- line);
+ status =
+ acpi_ut_track_allocation(allocation, size, ACPI_MEM_MALLOC,
+ component, module, line);
if (ACPI_FAILURE(status)) {
acpi_os_free(allocation);
return (NULL);
@@ -161,6 +161,7 @@ void *acpi_ut_allocate_and_track(acpi_size size,
acpi_gbl_global_list->total_allocated++;
acpi_gbl_global_list->total_size += (u32)size;
acpi_gbl_global_list->current_total_size += (u32)size;
+
if (acpi_gbl_global_list->current_total_size >
acpi_gbl_global_list->max_occupied) {
acpi_gbl_global_list->max_occupied =
@@ -223,6 +224,7 @@ void *acpi_ut_allocate_zeroed_and_track(acpi_size size,
acpi_gbl_global_list->total_allocated++;
acpi_gbl_global_list->total_size += (u32)size;
acpi_gbl_global_list->current_total_size += (u32)size;
+
if (acpi_gbl_global_list->current_total_size >
acpi_gbl_global_list->max_occupied) {
acpi_gbl_global_list->max_occupied =
@@ -269,8 +271,8 @@ acpi_ut_free_and_track(void *allocation,
acpi_gbl_global_list->total_freed++;
acpi_gbl_global_list->current_total_size -= debug_block->size;
- status = acpi_ut_remove_allocation(debug_block,
- component, module, line);
+ status =
+ acpi_ut_remove_allocation(debug_block, component, module, line);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, "Could not free memory"));
}
@@ -525,35 +527,35 @@ void acpi_ut_dump_allocation_info(void)
/*
ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,
- ("%30s: %4d (%3d Kb)\n", "Current allocations",
- mem_list->current_count,
- ROUND_UP_TO_1K (mem_list->current_size)));
+ ("%30s: %4d (%3d Kb)\n", "Current allocations",
+ mem_list->current_count,
+ ROUND_UP_TO_1K (mem_list->current_size)));
ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,
- ("%30s: %4d (%3d Kb)\n", "Max concurrent allocations",
- mem_list->max_concurrent_count,
- ROUND_UP_TO_1K (mem_list->max_concurrent_size)));
+ ("%30s: %4d (%3d Kb)\n", "Max concurrent allocations",
+ mem_list->max_concurrent_count,
+ ROUND_UP_TO_1K (mem_list->max_concurrent_size)));
ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,
- ("%30s: %4d (%3d Kb)\n", "Total (all) internal objects",
- running_object_count,
- ROUND_UP_TO_1K (running_object_size)));
+ ("%30s: %4d (%3d Kb)\n", "Total (all) internal objects",
+ running_object_count,
+ ROUND_UP_TO_1K (running_object_size)));
ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,
- ("%30s: %4d (%3d Kb)\n", "Total (all) allocations",
- running_alloc_count,
- ROUND_UP_TO_1K (running_alloc_size)));
+ ("%30s: %4d (%3d Kb)\n", "Total (all) allocations",
+ running_alloc_count,
+ ROUND_UP_TO_1K (running_alloc_size)));
ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,
- ("%30s: %4d (%3d Kb)\n", "Current Nodes",
- acpi_gbl_current_node_count,
- ROUND_UP_TO_1K (acpi_gbl_current_node_size)));
+ ("%30s: %4d (%3d Kb)\n", "Current Nodes",
+ acpi_gbl_current_node_count,
+ ROUND_UP_TO_1K (acpi_gbl_current_node_size)));
ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES,
- ("%30s: %4d (%3d Kb)\n", "Max Nodes",
- acpi_gbl_max_concurrent_node_count,
- ROUND_UP_TO_1K ((acpi_gbl_max_concurrent_node_count *
- sizeof (struct acpi_namespace_node)))));
+ ("%30s: %4d (%3d Kb)\n", "Max Nodes",
+ acpi_gbl_max_concurrent_node_count,
+ ROUND_UP_TO_1K ((acpi_gbl_max_concurrent_node_count *
+ sizeof (struct acpi_namespace_node)))));
*/
return_VOID;
}
diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c
index f9c8f9ce1f0f..9f3f0a1591f6 100644
--- a/drivers/acpi/acpica/utxface.c
+++ b/drivers/acpi/acpica/utxface.c
@@ -154,7 +154,6 @@ acpi_status acpi_get_system_info(struct acpi_buffer * out_buffer)
* Populate the return buffer
*/
info_ptr = (struct acpi_system_info *)out_buffer->pointer;
-
info_ptr->acpi_ca_version = ACPI_CA_VERSION;
/* System flags (ACPI capabilities) */
@@ -216,7 +215,6 @@ acpi_status acpi_get_statistics(struct acpi_statistics *stats)
/* Other counters */
stats->method_count = acpi_method_count;
-
return_ACPI_STATUS(AE_OK);
}
diff --git a/drivers/acpi/acpica/utxferror.c b/drivers/acpi/acpica/utxferror.c
index 98d578753101..f6cbaf451dbf 100644
--- a/drivers/acpi/acpica/utxferror.c
+++ b/drivers/acpi/acpica/utxferror.c
@@ -117,6 +117,7 @@ acpi_exception(const char *module_name,
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;
diff --git a/drivers/acpi/acpica/utxfinit.c b/drivers/acpi/acpica/utxfinit.c
index a7137ec28447..e38facd3e32f 100644
--- a/drivers/acpi/acpica/utxfinit.c
+++ b/drivers/acpi/acpica/utxfinit.c
@@ -147,6 +147,28 @@ acpi_status __init acpi_enable_subsystem(u32 flags)
ACPI_FUNCTION_TRACE(acpi_enable_subsystem);
+ /*
+ * The early initialization phase is complete. The namespace is loaded,
+ * and we can now support address spaces other than Memory, I/O, and
+ * PCI_Config.
+ */
+ acpi_gbl_early_initialization = FALSE;
+
+ /*
+ * Install the default operation region handlers. These are the
+ * handlers that are defined by the ACPI specification to be
+ * "always accessible" -- namely, system_memory, system_IO, and
+ * PCI_Config. This also means that no _REG methods need to be
+ * run for these address spaces. We need to have these handlers
+ * installed before any AML code can be executed, especially any
+ * module-level code (11/2015).
+ */
+ status = acpi_ev_install_region_handlers();
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status,
+ "During Region initialization"));
+ return_ACPI_STATUS(status);
+ }
#if (!ACPI_REDUCED_HARDWARE)
/* Enable ACPI mode */
@@ -175,23 +197,7 @@ acpi_status __init acpi_enable_subsystem(u32 flags)
return_ACPI_STATUS(status);
}
}
-#endif /* !ACPI_REDUCED_HARDWARE */
-
- /*
- * Install the default op_region handlers. These are installed unless
- * other handlers have already been installed via the
- * install_address_space_handler interface.
- */
- if (!(flags & ACPI_NO_ADDRESS_SPACE_INIT)) {
- ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
- "[Init] Installing default address space handlers\n"));
- status = acpi_ev_install_region_handlers();
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
- }
-#if (!ACPI_REDUCED_HARDWARE)
/*
* Initialize ACPI Event handling (Fixed and General Purpose)
*
@@ -261,6 +267,7 @@ acpi_status __init acpi_initialize_objects(u32 flags)
* initialized, even if they contain executable AML (see the call to
* acpi_ns_initialize_objects below).
*/
+ acpi_gbl_reg_methods_enabled = TRUE;
if (!(flags & ACPI_NO_ADDRESS_SPACE_INIT)) {
ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
"[Init] Executing _REG OpRegion methods\n"));
@@ -285,8 +292,14 @@ acpi_status __init acpi_initialize_objects(u32 flags)
* outside of any control method is wrapped with a temporary control
* method object and placed on a global list. The methods on this list
* are executed below.
+ *
+ * This case executes the module-level code for all tables only after
+ * all of the tables have been loaded. It is a legacy option and is
+ * not compatible with other ACPI implementations. See acpi_ns_load_table.
*/
- acpi_ns_exec_module_code_list();
+ if (acpi_gbl_group_module_level_code) {
+ acpi_ns_exec_module_code_list();
+ }
/*
* Initialize the objects that remain uninitialized. This runs the
diff --git a/drivers/acpi/acpica/utxfmutex.c b/drivers/acpi/acpica/utxfmutex.c
index f2606af3364c..95d6123a7010 100644
--- a/drivers/acpi/acpica/utxfmutex.c
+++ b/drivers/acpi/acpica/utxfmutex.c
@@ -89,9 +89,9 @@ acpi_ut_get_mutex_object(acpi_handle handle,
mutex_node = handle;
if (pathname != NULL) {
- status = acpi_get_handle(handle, pathname,
- ACPI_CAST_PTR(acpi_handle,
- &mutex_node));
+ status =
+ acpi_get_handle(handle, pathname,
+ ACPI_CAST_PTR(acpi_handle, &mutex_node));
if (ACPI_FAILURE(status)) {
return (status);
}
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index a212cefae524..891c42d1cd65 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -180,14 +180,15 @@ static void acpi_print_osc_error(acpi_handle handle,
int i;
if (ACPI_FAILURE(acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer)))
- printk(KERN_DEBUG "%s\n", error);
+ printk(KERN_DEBUG "%s: %s\n", context->uuid_str, error);
else {
- printk(KERN_DEBUG "%s:%s\n", (char *)buffer.pointer, error);
+ printk(KERN_DEBUG "%s (%s): %s\n",
+ (char *)buffer.pointer, context->uuid_str, error);
kfree(buffer.pointer);
}
- printk(KERN_DEBUG"_OSC request data:");
+ printk(KERN_DEBUG "_OSC request data:");
for (i = 0; i < context->cap.length; i += sizeof(u32))
- printk("%x ", *((u32 *)(context->cap.pointer + i)));
+ printk(" %x", *((u32 *)(context->cap.pointer + i)));
printk("\n");
}
@@ -1094,6 +1095,7 @@ static int __init acpi_init(void)
acpi_debugfs_init();
acpi_sleep_proc_init();
acpi_wakeup_device_init();
+ acpi_debugger_init();
return 0;
}
diff --git a/drivers/acpi/device_sysfs.c b/drivers/acpi/device_sysfs.c
index 707cf6213bc2..b9afb47db7ed 100644
--- a/drivers/acpi/device_sysfs.c
+++ b/drivers/acpi/device_sysfs.c
@@ -104,7 +104,7 @@ static void acpi_expose_nondev_subnodes(struct kobject *kobj,
init_completion(&dn->kobj_done);
ret = kobject_init_and_add(&dn->kobj, &acpi_data_node_ktype,
- kobj, dn->name);
+ kobj, "%s", dn->name);
if (ret)
acpi_handle_err(dn->handle, "Failed to expose (%d)\n", ret);
else
diff --git a/drivers/acpi/gsi.c b/drivers/acpi/gsi.c
index fa4585a6914e..ee9e0f27b2bf 100644
--- a/drivers/acpi/gsi.c
+++ b/drivers/acpi/gsi.c
@@ -17,25 +17,6 @@ enum acpi_irq_model_id acpi_irq_model;
static struct fwnode_handle *acpi_gsi_domain_id;
-static unsigned int acpi_gsi_get_irq_type(int trigger, int polarity)
-{
- switch (polarity) {
- case ACPI_ACTIVE_LOW:
- return trigger == ACPI_EDGE_SENSITIVE ?
- IRQ_TYPE_EDGE_FALLING :
- IRQ_TYPE_LEVEL_LOW;
- case ACPI_ACTIVE_HIGH:
- return trigger == ACPI_EDGE_SENSITIVE ?
- IRQ_TYPE_EDGE_RISING :
- IRQ_TYPE_LEVEL_HIGH;
- case ACPI_ACTIVE_BOTH:
- if (trigger == ACPI_EDGE_SENSITIVE)
- return IRQ_TYPE_EDGE_BOTH;
- default:
- return IRQ_TYPE_NONE;
- }
-}
-
/**
* acpi_gsi_to_irq() - Retrieve the linux irq number for a given GSI
* @gsi: GSI IRQ number to map
@@ -82,7 +63,7 @@ int acpi_register_gsi(struct device *dev, u32 gsi, int trigger,
fwspec.fwnode = acpi_gsi_domain_id;
fwspec.param[0] = gsi;
- fwspec.param[1] = acpi_gsi_get_irq_type(trigger, polarity);
+ fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
fwspec.param_count = 2;
return irq_create_fwspec_mapping(&fwspec);
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 11d87bf67e73..1e6833a5cd44 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -86,6 +86,14 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent);
#define ACPI_STA_DEFAULT (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | \
ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING)
+extern struct list_head acpi_bus_id_list;
+
+struct acpi_device_bus_id {
+ char bus_id[15];
+ unsigned int instance_no;
+ struct list_head node;
+};
+
int acpi_device_add(struct acpi_device *device,
void (*release)(struct device *));
void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c
index f7dab53b352a..ad6d8c6b777e 100644
--- a/drivers/acpi/nfit.c
+++ b/drivers/acpi/nfit.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/ndctl.h>
+#include <linux/delay.h>
#include <linux/list.h>
#include <linux/acpi.h>
#include <linux/sort.h>
@@ -233,11 +234,12 @@ static bool add_spa(struct acpi_nfit_desc *acpi_desc,
struct nfit_table_prev *prev,
struct acpi_nfit_system_address *spa)
{
+ size_t length = min_t(size_t, sizeof(*spa), spa->header.length);
struct device *dev = acpi_desc->dev;
struct nfit_spa *nfit_spa;
list_for_each_entry(nfit_spa, &prev->spas, list) {
- if (memcmp(nfit_spa->spa, spa, sizeof(*spa)) == 0) {
+ if (memcmp(nfit_spa->spa, spa, length) == 0) {
list_move_tail(&nfit_spa->list, &acpi_desc->spas);
return true;
}
@@ -259,11 +261,12 @@ static bool add_memdev(struct acpi_nfit_desc *acpi_desc,
struct nfit_table_prev *prev,
struct acpi_nfit_memory_map *memdev)
{
+ size_t length = min_t(size_t, sizeof(*memdev), memdev->header.length);
struct device *dev = acpi_desc->dev;
struct nfit_memdev *nfit_memdev;
list_for_each_entry(nfit_memdev, &prev->memdevs, list)
- if (memcmp(nfit_memdev->memdev, memdev, sizeof(*memdev)) == 0) {
+ if (memcmp(nfit_memdev->memdev, memdev, length) == 0) {
list_move_tail(&nfit_memdev->list, &acpi_desc->memdevs);
return true;
}
@@ -284,11 +287,12 @@ static bool add_dcr(struct acpi_nfit_desc *acpi_desc,
struct nfit_table_prev *prev,
struct acpi_nfit_control_region *dcr)
{
+ size_t length = min_t(size_t, sizeof(*dcr), dcr->header.length);
struct device *dev = acpi_desc->dev;
struct nfit_dcr *nfit_dcr;
list_for_each_entry(nfit_dcr, &prev->dcrs, list)
- if (memcmp(nfit_dcr->dcr, dcr, sizeof(*dcr)) == 0) {
+ if (memcmp(nfit_dcr->dcr, dcr, length) == 0) {
list_move_tail(&nfit_dcr->list, &acpi_desc->dcrs);
return true;
}
@@ -308,11 +312,12 @@ static bool add_bdw(struct acpi_nfit_desc *acpi_desc,
struct nfit_table_prev *prev,
struct acpi_nfit_data_region *bdw)
{
+ size_t length = min_t(size_t, sizeof(*bdw), bdw->header.length);
struct device *dev = acpi_desc->dev;
struct nfit_bdw *nfit_bdw;
list_for_each_entry(nfit_bdw, &prev->bdws, list)
- if (memcmp(nfit_bdw->bdw, bdw, sizeof(*bdw)) == 0) {
+ if (memcmp(nfit_bdw->bdw, bdw, length) == 0) {
list_move_tail(&nfit_bdw->list, &acpi_desc->bdws);
return true;
}
@@ -332,11 +337,12 @@ static bool add_idt(struct acpi_nfit_desc *acpi_desc,
struct nfit_table_prev *prev,
struct acpi_nfit_interleave *idt)
{
+ size_t length = min_t(size_t, sizeof(*idt), idt->header.length);
struct device *dev = acpi_desc->dev;
struct nfit_idt *nfit_idt;
list_for_each_entry(nfit_idt, &prev->idts, list)
- if (memcmp(nfit_idt->idt, idt, sizeof(*idt)) == 0) {
+ if (memcmp(nfit_idt->idt, idt, length) == 0) {
list_move_tail(&nfit_idt->list, &acpi_desc->idts);
return true;
}
@@ -356,11 +362,12 @@ static bool add_flush(struct acpi_nfit_desc *acpi_desc,
struct nfit_table_prev *prev,
struct acpi_nfit_flush_address *flush)
{
+ size_t length = min_t(size_t, sizeof(*flush), flush->header.length);
struct device *dev = acpi_desc->dev;
struct nfit_flush *nfit_flush;
list_for_each_entry(nfit_flush, &prev->flushes, list)
- if (memcmp(nfit_flush->flush, flush, sizeof(*flush)) == 0) {
+ if (memcmp(nfit_flush->flush, flush, length) == 0) {
list_move_tail(&nfit_flush->list, &acpi_desc->flushes);
return true;
}
@@ -655,7 +662,7 @@ static ssize_t revision_show(struct device *dev,
struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
- return sprintf(buf, "%d\n", acpi_desc->nfit->header.revision);
+ return sprintf(buf, "%d\n", acpi_desc->acpi_header.revision);
}
static DEVICE_ATTR_RO(revision);
@@ -1467,6 +1474,201 @@ static void acpi_nfit_blk_region_disable(struct nvdimm_bus *nvdimm_bus,
/* devm will free nfit_blk */
}
+static int ars_get_cap(struct nvdimm_bus_descriptor *nd_desc,
+ struct nd_cmd_ars_cap *cmd, u64 addr, u64 length)
+{
+ cmd->address = addr;
+ cmd->length = length;
+
+ return nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_CAP, cmd,
+ sizeof(*cmd));
+}
+
+static int ars_do_start(struct nvdimm_bus_descriptor *nd_desc,
+ struct nd_cmd_ars_start *cmd, u64 addr, u64 length)
+{
+ int rc;
+
+ cmd->address = addr;
+ cmd->length = length;
+ cmd->type = ND_ARS_PERSISTENT;
+
+ while (1) {
+ rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_START, cmd,
+ sizeof(*cmd));
+ if (rc)
+ return rc;
+ switch (cmd->status) {
+ case 0:
+ return 0;
+ case 1:
+ /* ARS unsupported, but we should never get here */
+ return 0;
+ case 2:
+ return -EINVAL;
+ case 3:
+ /* ARS is in progress */
+ msleep(1000);
+ break;
+ default:
+ return -ENXIO;
+ }
+ }
+}
+
+static int ars_get_status(struct nvdimm_bus_descriptor *nd_desc,
+ struct nd_cmd_ars_status *cmd)
+{
+ int rc;
+
+ while (1) {
+ rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_STATUS, cmd,
+ sizeof(*cmd));
+ if (rc || cmd->status & 0xffff)
+ return -ENXIO;
+
+ /* Check extended status (Upper two bytes) */
+ switch (cmd->status >> 16) {
+ case 0:
+ return 0;
+ case 1:
+ /* ARS is in progress */
+ msleep(1000);
+ break;
+ case 2:
+ /* No ARS performed for the current boot */
+ return 0;
+ default:
+ return -ENXIO;
+ }
+ }
+}
+
+static int ars_status_process_records(struct nvdimm_bus *nvdimm_bus,
+ struct nd_cmd_ars_status *ars_status, u64 start)
+{
+ int rc;
+ u32 i;
+
+ /*
+ * The address field returned by ars_status should be either
+ * less than or equal to the address we last started ARS for.
+ * The (start, length) returned by ars_status should also have
+ * non-zero overlap with the range we started ARS for.
+ * If this is not the case, bail.
+ */
+ if (ars_status->address > start ||
+ (ars_status->address + ars_status->length < start))
+ return -ENXIO;
+
+ for (i = 0; i < ars_status->num_records; i++) {
+ rc = nvdimm_bus_add_poison(nvdimm_bus,
+ ars_status->records[i].err_address,
+ ars_status->records[i].length);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static int acpi_nfit_find_poison(struct acpi_nfit_desc *acpi_desc,
+ struct nd_region_desc *ndr_desc)
+{
+ struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
+ struct nvdimm_bus *nvdimm_bus = acpi_desc->nvdimm_bus;
+ struct nd_cmd_ars_status *ars_status = NULL;
+ struct nd_cmd_ars_start *ars_start = NULL;
+ struct nd_cmd_ars_cap *ars_cap = NULL;
+ u64 start, len, cur, remaining;
+ int rc;
+
+ ars_cap = kzalloc(sizeof(*ars_cap), GFP_KERNEL);
+ if (!ars_cap)
+ return -ENOMEM;
+
+ start = ndr_desc->res->start;
+ len = ndr_desc->res->end - ndr_desc->res->start + 1;
+
+ rc = ars_get_cap(nd_desc, ars_cap, start, len);
+ if (rc)
+ goto out;
+
+ /*
+ * If ARS is unsupported, or if the 'Persistent Memory Scrub' flag in
+ * extended status is not set, skip this but continue initialization
+ */
+ if ((ars_cap->status & 0xffff) ||
+ !(ars_cap->status >> 16 & ND_ARS_PERSISTENT)) {
+ dev_warn(acpi_desc->dev,
+ "ARS unsupported (status: 0x%x), won't create an error list\n",
+ ars_cap->status);
+ goto out;
+ }
+
+ /*
+ * Check if a full-range ARS has been run. If so, use those results
+ * without having to start a new ARS.
+ */
+ ars_status = kzalloc(ars_cap->max_ars_out + sizeof(*ars_status),
+ GFP_KERNEL);
+ if (!ars_status) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = ars_get_status(nd_desc, ars_status);
+ if (rc)
+ goto out;
+
+ if (ars_status->address <= start &&
+ (ars_status->address + ars_status->length >= start + len)) {
+ rc = ars_status_process_records(nvdimm_bus, ars_status, start);
+ goto out;
+ }
+
+ /*
+ * ARS_STATUS can overflow if the number of poison entries found is
+ * greater than the maximum buffer size (ars_cap->max_ars_out)
+ * To detect overflow, check if the length field of ars_status
+ * is less than the length we supplied. If so, process the
+ * error entries we got, adjust the start point, and start again
+ */
+ ars_start = kzalloc(sizeof(*ars_start), GFP_KERNEL);
+ if (!ars_start)
+ return -ENOMEM;
+
+ cur = start;
+ remaining = len;
+ do {
+ u64 done, end;
+
+ rc = ars_do_start(nd_desc, ars_start, cur, remaining);
+ if (rc)
+ goto out;
+
+ rc = ars_get_status(nd_desc, ars_status);
+ if (rc)
+ goto out;
+
+ rc = ars_status_process_records(nvdimm_bus, ars_status, cur);
+ if (rc)
+ goto out;
+
+ end = min(cur + remaining,
+ ars_status->address + ars_status->length);
+ done = end - cur;
+ cur += done;
+ remaining -= done;
+ } while (remaining);
+
+ out:
+ kfree(ars_cap);
+ kfree(ars_start);
+ kfree(ars_status);
+ return rc;
+}
+
static int acpi_nfit_init_mapping(struct acpi_nfit_desc *acpi_desc,
struct nd_mapping *nd_mapping, struct nd_region_desc *ndr_desc,
struct acpi_nfit_memory_map *memdev,
@@ -1579,6 +1781,13 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc,
nvdimm_bus = acpi_desc->nvdimm_bus;
if (nfit_spa_type(spa) == NFIT_SPA_PM) {
+ rc = acpi_nfit_find_poison(acpi_desc, ndr_desc);
+ if (rc) {
+ dev_err(acpi_desc->dev,
+ "error while performing ARS to find poison: %d\n",
+ rc);
+ return rc;
+ }
if (!nvdimm_pmem_region_create(nvdimm_bus, ndr_desc))
return -ENOMEM;
} else if (nfit_spa_type(spa) == NFIT_SPA_VOLATILE) {
@@ -1652,7 +1861,6 @@ int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, acpi_size sz)
data = (u8 *) acpi_desc->nfit;
end = data + sz;
- data += sizeof(struct acpi_table_nfit);
while (!IS_ERR_OR_NULL(data))
data = add_table(acpi_desc, &prev, data, end);
@@ -1748,13 +1956,29 @@ static int acpi_nfit_add(struct acpi_device *adev)
return PTR_ERR(acpi_desc);
}
- acpi_desc->nfit = (struct acpi_table_nfit *) tbl;
+ /*
+ * Save the acpi header for later and then skip it,
+ * making nfit point to the first nfit table header.
+ */
+ acpi_desc->acpi_header = *tbl;
+ acpi_desc->nfit = (void *) tbl + sizeof(struct acpi_table_nfit);
+ sz -= sizeof(struct acpi_table_nfit);
/* Evaluate _FIT and override with that if present */
status = acpi_evaluate_object(adev->handle, "_FIT", NULL, &buf);
if (ACPI_SUCCESS(status) && buf.length > 0) {
- acpi_desc->nfit = (struct acpi_table_nfit *)buf.pointer;
- sz = buf.length;
+ union acpi_object *obj;
+ /*
+ * Adjust for the acpi_object header of the _FIT
+ */
+ obj = buf.pointer;
+ if (obj->type == ACPI_TYPE_BUFFER) {
+ acpi_desc->nfit =
+ (struct acpi_nfit_header *)obj->buffer.pointer;
+ sz = obj->buffer.length;
+ } else
+ dev_dbg(dev, "%s invalid type %d, ignoring _FIT\n",
+ __func__, (int) obj->type);
}
rc = acpi_nfit_init(acpi_desc, sz);
@@ -1777,7 +2001,8 @@ static void acpi_nfit_notify(struct acpi_device *adev, u32 event)
{
struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(&adev->dev);
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
- struct acpi_table_nfit *nfit_saved;
+ struct acpi_nfit_header *nfit_saved;
+ union acpi_object *obj;
struct device *dev = &adev->dev;
acpi_status status;
int ret;
@@ -1788,7 +2013,7 @@ static void acpi_nfit_notify(struct acpi_device *adev, u32 event)
if (!dev->driver) {
/* dev->driver may be null if we're being removed */
dev_dbg(dev, "%s: no driver found for dev\n", __func__);
- return;
+ goto out_unlock;
}
if (!acpi_desc) {
@@ -1808,12 +2033,19 @@ static void acpi_nfit_notify(struct acpi_device *adev, u32 event)
}
nfit_saved = acpi_desc->nfit;
- acpi_desc->nfit = (struct acpi_table_nfit *)buf.pointer;
- ret = acpi_nfit_init(acpi_desc, buf.length);
- if (!ret) {
- /* Merge failed, restore old nfit, and exit */
- acpi_desc->nfit = nfit_saved;
- dev_err(dev, "failed to merge updated NFIT\n");
+ obj = buf.pointer;
+ if (obj->type == ACPI_TYPE_BUFFER) {
+ acpi_desc->nfit =
+ (struct acpi_nfit_header *)obj->buffer.pointer;
+ ret = acpi_nfit_init(acpi_desc, obj->buffer.length);
+ if (ret) {
+ /* Merge failed, restore old nfit, and exit */
+ acpi_desc->nfit = nfit_saved;
+ dev_err(dev, "failed to merge updated NFIT\n");
+ }
+ } else {
+ /* Bad _FIT, restore old nfit */
+ dev_err(dev, "Invalid _FIT\n");
}
kfree(buf.pointer);
diff --git a/drivers/acpi/nfit.h b/drivers/acpi/nfit.h
index 2ea5c0797c8f..3d549a383659 100644
--- a/drivers/acpi/nfit.h
+++ b/drivers/acpi/nfit.h
@@ -96,7 +96,8 @@ struct nfit_mem {
struct acpi_nfit_desc {
struct nvdimm_bus_descriptor nd_desc;
- struct acpi_table_nfit *nfit;
+ struct acpi_table_header acpi_header;
+ struct acpi_nfit_header *nfit;
struct mutex spa_map_mutex;
struct mutex init_mutex;
struct list_head spa_maps;
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 32d684af0ec7..67da6fb72274 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -220,6 +220,7 @@ void acpi_os_printf(const char *fmt, ...)
acpi_os_vprintf(fmt, args);
va_end(args);
}
+EXPORT_SYMBOL(acpi_os_printf);
void acpi_os_vprintf(const char *fmt, va_list args)
{
@@ -234,7 +235,8 @@ void acpi_os_vprintf(const char *fmt, va_list args)
printk(KERN_CONT "%s", buffer);
}
#else
- printk(KERN_CONT "%s", buffer);
+ if (acpi_debugger_write_log(buffer) < 0)
+ printk(KERN_CONT "%s", buffer);
#endif
}
@@ -364,6 +366,19 @@ static void acpi_unmap(acpi_physical_address pg_off, void __iomem *vaddr)
iounmap(vaddr);
}
+/**
+ * acpi_os_map_iomem - Get a virtual address for a given physical address range.
+ * @phys: Start of the physical address range to map.
+ * @size: Size of the physical address range to map.
+ *
+ * Look up the given physical address range in the list of existing ACPI memory
+ * mappings. If found, get a reference to it and return a pointer to it (its
+ * virtual address). If not found, map it, add it to that list and return a
+ * pointer to it.
+ *
+ * During early init (when acpi_gbl_permanent_mmap has not been set yet) this
+ * routine simply calls __acpi_map_table() to get the job done.
+ */
void __iomem *__init_refok
acpi_os_map_iomem(acpi_physical_address phys, acpi_size size)
{
@@ -439,6 +454,20 @@ static void acpi_os_map_cleanup(struct acpi_ioremap *map)
}
}
+/**
+ * acpi_os_unmap_iomem - Drop a memory mapping reference.
+ * @virt: Start of the address range to drop a reference to.
+ * @size: Size of the address range to drop a reference to.
+ *
+ * Look up the given virtual address range in the list of existing ACPI memory
+ * mappings, drop a reference to it and unmap it if there are no more active
+ * references to it.
+ *
+ * During early init (when acpi_gbl_permanent_mmap has not been set yet) this
+ * routine simply calls __acpi_unmap_table() to get the job done. Since
+ * __acpi_unmap_table() is an __init function, the __ref annotation is needed
+ * here.
+ */
void __ref acpi_os_unmap_iomem(void __iomem *virt, acpi_size size)
{
struct acpi_ioremap *map;
@@ -1101,6 +1130,200 @@ static void acpi_os_execute_deferred(struct work_struct *work)
kfree(dpc);
}
+#ifdef CONFIG_ACPI_DEBUGGER
+static struct acpi_debugger acpi_debugger;
+static bool acpi_debugger_initialized;
+
+int acpi_register_debugger(struct module *owner,
+ const struct acpi_debugger_ops *ops)
+{
+ int ret = 0;
+
+ mutex_lock(&acpi_debugger.lock);
+ if (acpi_debugger.ops) {
+ ret = -EBUSY;
+ goto err_lock;
+ }
+
+ acpi_debugger.owner = owner;
+ acpi_debugger.ops = ops;
+
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+EXPORT_SYMBOL(acpi_register_debugger);
+
+void acpi_unregister_debugger(const struct acpi_debugger_ops *ops)
+{
+ mutex_lock(&acpi_debugger.lock);
+ if (ops == acpi_debugger.ops) {
+ acpi_debugger.ops = NULL;
+ acpi_debugger.owner = NULL;
+ }
+ mutex_unlock(&acpi_debugger.lock);
+}
+EXPORT_SYMBOL(acpi_unregister_debugger);
+
+int acpi_debugger_create_thread(acpi_osd_exec_callback function, void *context)
+{
+ int ret;
+ int (*func)(acpi_osd_exec_callback, void *);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->create_thread;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(function, context);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+ssize_t acpi_debugger_write_log(const char *msg)
+{
+ ssize_t ret;
+ ssize_t (*func)(const char *);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->write_log;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(msg);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+ssize_t acpi_debugger_read_cmd(char *buffer, size_t buffer_length)
+{
+ ssize_t ret;
+ ssize_t (*func)(char *, size_t);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->read_cmd;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(buffer, buffer_length);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+int acpi_debugger_wait_command_ready(void)
+{
+ int ret;
+ int (*func)(bool, char *, size_t);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->wait_command_ready;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func(acpi_gbl_method_executing,
+ acpi_gbl_db_line_buf, ACPI_DB_LINE_BUFFER_SIZE);
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+int acpi_debugger_notify_command_complete(void)
+{
+ int ret;
+ int (*func)(void);
+ struct module *owner;
+
+ if (!acpi_debugger_initialized)
+ return -ENODEV;
+ mutex_lock(&acpi_debugger.lock);
+ if (!acpi_debugger.ops) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ if (!try_module_get(acpi_debugger.owner)) {
+ ret = -ENODEV;
+ goto err_lock;
+ }
+ func = acpi_debugger.ops->notify_command_complete;
+ owner = acpi_debugger.owner;
+ mutex_unlock(&acpi_debugger.lock);
+
+ ret = func();
+
+ mutex_lock(&acpi_debugger.lock);
+ module_put(owner);
+err_lock:
+ mutex_unlock(&acpi_debugger.lock);
+ return ret;
+}
+
+int __init acpi_debugger_init(void)
+{
+ mutex_init(&acpi_debugger.lock);
+ acpi_debugger_initialized = true;
+ return 0;
+}
+#endif
+
/*******************************************************************************
*
* FUNCTION: acpi_os_execute
@@ -1127,6 +1350,15 @@ acpi_status acpi_os_execute(acpi_execute_type type,
"Scheduling function [%p(%p)] for deferred execution.\n",
function, context));
+ if (type == OSL_DEBUGGER_MAIN_THREAD) {
+ ret = acpi_debugger_create_thread(function, context);
+ if (ret) {
+ pr_err("Call to kthread_create() failed.\n");
+ status = AE_ERROR;
+ }
+ goto out_thread;
+ }
+
/*
* Allocate/initialize DPC structure. Note that this memory will be
* freed by the callee. The kernel handles the work_struct list in a
@@ -1151,11 +1383,17 @@ acpi_status acpi_os_execute(acpi_execute_type type,
if (type == OSL_NOTIFY_HANDLER) {
queue = kacpi_notify_wq;
INIT_WORK(&dpc->work, acpi_os_execute_deferred);
- } else {
+ } else if (type == OSL_GPE_HANDLER) {
queue = kacpid_wq;
INIT_WORK(&dpc->work, acpi_os_execute_deferred);
+ } else {
+ pr_err("Unsupported os_execute type %d.\n", type);
+ status = AE_ERROR;
}
+ if (ACPI_FAILURE(status))
+ goto err_workqueue;
+
/*
* On some machines, a software-initiated SMI causes corruption unless
* the SMI runs on CPU 0. An SMI can be initiated by any AML, but
@@ -1164,13 +1402,15 @@ acpi_status acpi_os_execute(acpi_execute_type type,
* queueing on CPU 0.
*/
ret = queue_work_on(0, queue, &dpc->work);
-
if (!ret) {
printk(KERN_ERR PREFIX
"Call to queue_work() failed.\n");
status = AE_ERROR;
- kfree(dpc);
}
+err_workqueue:
+ if (ACPI_FAILURE(status))
+ kfree(dpc);
+out_thread:
return status;
}
EXPORT_SYMBOL(acpi_os_execute);
@@ -1358,10 +1598,39 @@ acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read)
chars = strlen(buffer) - 1;
buffer[chars] = '\0';
}
+#else
+ int ret;
+
+ ret = acpi_debugger_read_cmd(buffer, buffer_length);
+ if (ret < 0)
+ return AE_ERROR;
+ if (bytes_read)
+ *bytes_read = ret;
#endif
return AE_OK;
}
+EXPORT_SYMBOL(acpi_os_get_line);
+
+acpi_status acpi_os_wait_command_ready(void)
+{
+ int ret;
+
+ ret = acpi_debugger_wait_command_ready();
+ if (ret < 0)
+ return AE_ERROR;
+ return AE_OK;
+}
+
+acpi_status acpi_os_notify_command_complete(void)
+{
+ int ret;
+
+ ret = acpi_debugger_notify_command_complete();
+ if (ret < 0)
+ return AE_ERROR;
+ return AE_OK;
+}
acpi_status acpi_os_signal(u32 function, void *info)
{
diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c
index c9336751e5e3..d30184c7f3bc 100644
--- a/drivers/acpi/pci_irq.c
+++ b/drivers/acpi/pci_irq.c
@@ -131,9 +131,6 @@ static void do_prt_fixups(struct acpi_prt_entry *entry,
quirk = &prt_quirks[i];
/* All current quirks involve link devices, not GSIs */
- if (!prt->source)
- continue;
-
if (dmi_check_system(quirk->system) &&
entry->id.segment == quirk->segment &&
entry->id.bus == quirk->bus &&
diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c
index 7c8408b946ca..fa2863567eed 100644
--- a/drivers/acpi/pci_link.c
+++ b/drivers/acpi/pci_link.c
@@ -4,6 +4,7 @@
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
* Copyright (C) 2002 Dominik Brodowski <devel@brodo.de>
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
@@ -67,12 +68,12 @@ static struct acpi_scan_handler pci_link_handler = {
* later even the link is disable. Instead, we just repick the active irq
*/
struct acpi_pci_link_irq {
- u8 active; /* Current IRQ */
+ u32 active; /* Current IRQ */
u8 triggering; /* All IRQs */
u8 polarity; /* All IRQs */
u8 resource_type;
u8 possible_count;
- u8 possible[ACPI_PCI_LINK_MAX_POSSIBLE];
+ u32 possible[ACPI_PCI_LINK_MAX_POSSIBLE];
u8 initialized:1;
u8 reserved:7;
};
@@ -437,7 +438,6 @@ static int acpi_pci_link_set(struct acpi_pci_link *link, int irq)
* enabled system.
*/
-#define ACPI_MAX_IRQS 256
#define ACPI_MAX_ISA_IRQ 16
#define PIRQ_PENALTY_PCI_AVAILABLE (0)
@@ -447,7 +447,7 @@ static int acpi_pci_link_set(struct acpi_pci_link *link, int irq)
#define PIRQ_PENALTY_ISA_USED (16*16*16*16*16)
#define PIRQ_PENALTY_ISA_ALWAYS (16*16*16*16*16*16)
-static int acpi_irq_penalty[ACPI_MAX_IRQS] = {
+static int acpi_irq_isa_penalty[ACPI_MAX_ISA_IRQ] = {
PIRQ_PENALTY_ISA_ALWAYS, /* IRQ0 timer */
PIRQ_PENALTY_ISA_ALWAYS, /* IRQ1 keyboard */
PIRQ_PENALTY_ISA_ALWAYS, /* IRQ2 cascade */
@@ -464,9 +464,68 @@ static int acpi_irq_penalty[ACPI_MAX_IRQS] = {
PIRQ_PENALTY_ISA_USED, /* IRQ13 fpe, sometimes */
PIRQ_PENALTY_ISA_USED, /* IRQ14 ide0 */
PIRQ_PENALTY_ISA_USED, /* IRQ15 ide1 */
- /* >IRQ15 */
};
+struct irq_penalty_info {
+ int irq;
+ int penalty;
+ struct list_head node;
+};
+
+static LIST_HEAD(acpi_irq_penalty_list);
+
+static int acpi_irq_get_penalty(int irq)
+{
+ struct irq_penalty_info *irq_info;
+
+ if (irq < ACPI_MAX_ISA_IRQ)
+ return acpi_irq_isa_penalty[irq];
+
+ list_for_each_entry(irq_info, &acpi_irq_penalty_list, node) {
+ if (irq_info->irq == irq)
+ return irq_info->penalty;
+ }
+
+ return 0;
+}
+
+static int acpi_irq_set_penalty(int irq, int new_penalty)
+{
+ struct irq_penalty_info *irq_info;
+
+ /* see if this is a ISA IRQ */
+ if (irq < ACPI_MAX_ISA_IRQ) {
+ acpi_irq_isa_penalty[irq] = new_penalty;
+ return 0;
+ }
+
+ /* next, try to locate from the dynamic list */
+ list_for_each_entry(irq_info, &acpi_irq_penalty_list, node) {
+ if (irq_info->irq == irq) {
+ irq_info->penalty = new_penalty;
+ return 0;
+ }
+ }
+
+ /* nope, let's allocate a slot for this IRQ */
+ irq_info = kzalloc(sizeof(*irq_info), GFP_KERNEL);
+ if (!irq_info)
+ return -ENOMEM;
+
+ irq_info->irq = irq;
+ irq_info->penalty = new_penalty;
+ list_add_tail(&irq_info->node, &acpi_irq_penalty_list);
+
+ return 0;
+}
+
+static void acpi_irq_add_penalty(int irq, int penalty)
+{
+ int curpen = acpi_irq_get_penalty(irq);
+
+ acpi_irq_set_penalty(irq, curpen + penalty);
+}
+
int __init acpi_irq_penalty_init(void)
{
struct acpi_pci_link *link;
@@ -487,15 +546,16 @@ int __init acpi_irq_penalty_init(void)
link->irq.possible_count;
for (i = 0; i < link->irq.possible_count; i++) {
- if (link->irq.possible[i] < ACPI_MAX_ISA_IRQ)
- acpi_irq_penalty[link->irq.
- possible[i]] +=
- penalty;
+ if (link->irq.possible[i] < ACPI_MAX_ISA_IRQ) {
+ int irqpos = link->irq.possible[i];
+
+ acpi_irq_add_penalty(irqpos, penalty);
+ }
}
} else if (link->irq.active) {
- acpi_irq_penalty[link->irq.active] +=
- PIRQ_PENALTY_PCI_POSSIBLE;
+ acpi_irq_add_penalty(link->irq.active,
+ PIRQ_PENALTY_PCI_POSSIBLE);
}
}
@@ -547,12 +607,12 @@ static int acpi_pci_link_allocate(struct acpi_pci_link *link)
* the use of IRQs 9, 10, 11, and >15.
*/
for (i = (link->irq.possible_count - 1); i >= 0; i--) {
- if (acpi_irq_penalty[irq] >
- acpi_irq_penalty[link->irq.possible[i]])
+ if (acpi_irq_get_penalty(irq) >
+ acpi_irq_get_penalty(link->irq.possible[i]))
irq = link->irq.possible[i];
}
}
- if (acpi_irq_penalty[irq] >= PIRQ_PENALTY_ISA_ALWAYS) {
+ if (acpi_irq_get_penalty(irq) >= PIRQ_PENALTY_ISA_ALWAYS) {
printk(KERN_ERR PREFIX "No IRQ available for %s [%s]. "
"Try pci=noacpi or acpi=off\n",
acpi_device_name(link->device),
@@ -568,7 +628,8 @@ static int acpi_pci_link_allocate(struct acpi_pci_link *link)
acpi_device_bid(link->device));
return -ENODEV;
} else {
- acpi_irq_penalty[link->irq.active] += PIRQ_PENALTY_PCI_USING;
+ acpi_irq_add_penalty(link->irq.active, PIRQ_PENALTY_PCI_USING);
+
printk(KERN_WARNING PREFIX "%s [%s] enabled at IRQ %d\n",
acpi_device_name(link->device),
acpi_device_bid(link->device), link->irq.active);
@@ -778,7 +839,7 @@ static void acpi_pci_link_remove(struct acpi_device *device)
}
/*
- * modify acpi_irq_penalty[] from cmdline
+ * modify penalty from cmdline
*/
static int __init acpi_irq_penalty_update(char *str, int used)
{
@@ -796,13 +857,10 @@ static int __init acpi_irq_penalty_update(char *str, int used)
if (irq < 0)
continue;
- if (irq >= ARRAY_SIZE(acpi_irq_penalty))
- continue;
-
if (used)
- acpi_irq_penalty[irq] += PIRQ_PENALTY_ISA_USED;
+ acpi_irq_add_penalty(irq, PIRQ_PENALTY_ISA_USED);
else
- acpi_irq_penalty[irq] = PIRQ_PENALTY_PCI_AVAILABLE;
+ acpi_irq_set_penalty(irq, PIRQ_PENALTY_PCI_AVAILABLE);
if (retval != 2) /* no next number */
break;
@@ -819,18 +877,15 @@ static int __init acpi_irq_penalty_update(char *str, int used)
*/
void acpi_penalize_isa_irq(int irq, int active)
{
- if (irq >= 0 && irq < ARRAY_SIZE(acpi_irq_penalty)) {
- if (active)
- acpi_irq_penalty[irq] += PIRQ_PENALTY_ISA_USED;
- else
- acpi_irq_penalty[irq] += PIRQ_PENALTY_PCI_USING;
- }
+ if (irq >= 0)
+ acpi_irq_add_penalty(irq, active ?
+ PIRQ_PENALTY_ISA_USED : PIRQ_PENALTY_PCI_USING);
}
bool acpi_isa_irq_available(int irq)
{
- return irq >= 0 && (irq >= ARRAY_SIZE(acpi_irq_penalty) ||
- acpi_irq_penalty[irq] < PIRQ_PENALTY_ISA_ALWAYS);
+ return irq >= 0 &&
+ (acpi_irq_get_penalty(irq) < PIRQ_PENALTY_ISA_ALWAYS);
}
/*
@@ -840,13 +895,18 @@ bool acpi_isa_irq_available(int irq)
*/
void acpi_penalize_sci_irq(int irq, int trigger, int polarity)
{
- if (irq >= 0 && irq < ARRAY_SIZE(acpi_irq_penalty)) {
- if (trigger != ACPI_MADT_TRIGGER_LEVEL ||
- polarity != ACPI_MADT_POLARITY_ACTIVE_LOW)
- acpi_irq_penalty[irq] += PIRQ_PENALTY_ISA_ALWAYS;
- else
- acpi_irq_penalty[irq] += PIRQ_PENALTY_PCI_USING;
- }
+ int penalty;
+
+ if (irq < 0)
+ return;
+
+ if (trigger != ACPI_MADT_TRIGGER_LEVEL ||
+ polarity != ACPI_MADT_POLARITY_ACTIVE_LOW)
+ penalty = PIRQ_PENALTY_ISA_ALWAYS;
+ else
+ penalty = PIRQ_PENALTY_PCI_USING;
+
+ acpi_irq_add_penalty(irq, penalty);
}
/*
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index 850d7bf0c873..ae3fe4e64203 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -768,6 +768,13 @@ static void pci_acpi_root_add_resources(struct acpi_pci_root_info *info)
else
continue;
+ /*
+ * Some legacy x86 host bridge drivers use iomem_resource and
+ * ioport_resource as default resource pool, skip it.
+ */
+ if (res == root)
+ continue;
+
conflict = insert_resource_conflict(root, res);
if (conflict) {
dev_info(&info->bridge->dev,
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index f4e02ae93f58..11154a330f07 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -200,7 +200,8 @@ static int acpi_pss_perf_init(struct acpi_processor *pr,
goto err_remove_sysfs_thermal;
}
- sysfs_remove_link(&pr->cdev->device.kobj, "device");
+ return 0;
+
err_remove_sysfs_thermal:
sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
err_thermal_unregister:
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c
index 88f4306744c0..2aee41655ce9 100644
--- a/drivers/acpi/property.c
+++ b/drivers/acpi/property.c
@@ -346,7 +346,7 @@ void acpi_free_properties(struct acpi_device *adev)
*
* Return: %0 if property with @name has been found (success),
* %-EINVAL if the arguments are invalid,
- * %-ENODATA if the property doesn't exist,
+ * %-EINVAL if the property doesn't exist,
* %-EPROTO if the property value type doesn't match @type.
*/
static int acpi_data_get_property(struct acpi_device_data *data,
@@ -360,7 +360,7 @@ static int acpi_data_get_property(struct acpi_device_data *data,
return -EINVAL;
if (!data->pointer || !data->properties)
- return -ENODATA;
+ return -EINVAL;
properties = data->properties;
for (i = 0; i < properties->package.count; i++) {
@@ -375,13 +375,13 @@ static int acpi_data_get_property(struct acpi_device_data *data,
if (!strcmp(name, propname->string.pointer)) {
if (type != ACPI_TYPE_ANY && propvalue->type != type)
return -EPROTO;
- else if (obj)
+ if (obj)
*obj = propvalue;
return 0;
}
}
- return -ENODATA;
+ return -EINVAL;
}
/**
@@ -439,7 +439,7 @@ int acpi_node_prop_get(struct fwnode_handle *fwnode, const char *propname,
*
* Return: %0 if array property (package) with @name has been found (success),
* %-EINVAL if the arguments are invalid,
- * %-ENODATA if the property doesn't exist,
+ * %-EINVAL if the property doesn't exist,
* %-EPROTO if the property is not a package or the type of its elements
* doesn't match @type.
*/
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c
index cdc5c2599beb..d02fd53042a5 100644
--- a/drivers/acpi/resource.c
+++ b/drivers/acpi/resource.c
@@ -23,6 +23,7 @@
#include <linux/export.h>
#include <linux/ioport.h>
#include <linux/slab.h>
+#include <linux/irq.h>
#ifdef CONFIG_X86
#define valid_IRQ(i) (((i) != 0) && ((i) != 2))
@@ -336,6 +337,31 @@ unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable)
}
EXPORT_SYMBOL_GPL(acpi_dev_irq_flags);
+/**
+ * acpi_dev_get_irq_type - Determine irq type.
+ * @triggering: Triggering type as provided by ACPI.
+ * @polarity: Interrupt polarity as provided by ACPI.
+ */
+unsigned int acpi_dev_get_irq_type(int triggering, int polarity)
+{
+ switch (polarity) {
+ case ACPI_ACTIVE_LOW:
+ return triggering == ACPI_EDGE_SENSITIVE ?
+ IRQ_TYPE_EDGE_FALLING :
+ IRQ_TYPE_LEVEL_LOW;
+ case ACPI_ACTIVE_HIGH:
+ return triggering == ACPI_EDGE_SENSITIVE ?
+ IRQ_TYPE_EDGE_RISING :
+ IRQ_TYPE_LEVEL_HIGH;
+ case ACPI_ACTIVE_BOTH:
+ if (triggering == ACPI_EDGE_SENSITIVE)
+ return IRQ_TYPE_EDGE_BOTH;
+ default:
+ return IRQ_TYPE_NONE;
+ }
+}
+EXPORT_SYMBOL_GPL(acpi_dev_get_irq_type);
+
static void acpi_dev_irqresource_disabled(struct resource *res, u32 gsi)
{
res->start = gsi;
diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c
index cb3dedb1beae..ad0b13ad4bbb 100644
--- a/drivers/acpi/sbs.c
+++ b/drivers/acpi/sbs.c
@@ -417,11 +417,11 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery)
if ((value & 0xf000) != sel) {
value &= 0x0fff;
value |= sel;
- ret = acpi_smbus_write(sbs->hc, SMBUS_WRITE_WORD,
+ ret = acpi_smbus_write(sbs->hc, SMBUS_WRITE_WORD,
ACPI_SBS_MANAGER,
0x01, (u8 *)&value, 2);
- if (ret)
- goto end;
+ if (ret)
+ goto end;
}
}
ret = acpi_smbus_write(sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY,
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 78d5f02a073b..407a3760e8de 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -39,7 +39,7 @@ static const char *dummy_hid = "device";
static LIST_HEAD(acpi_dep_list);
static DEFINE_MUTEX(acpi_dep_list_lock);
-static LIST_HEAD(acpi_bus_id_list);
+LIST_HEAD(acpi_bus_id_list);
static DEFINE_MUTEX(acpi_scan_lock);
static LIST_HEAD(acpi_scan_handlers_list);
DEFINE_MUTEX(acpi_device_lock);
@@ -52,12 +52,6 @@ struct acpi_dep_data {
acpi_handle slave;
};
-struct acpi_device_bus_id{
- char bus_id[15];
- unsigned int instance_no;
- struct list_head node;
-};
-
void acpi_scan_lock_acquire(void)
{
mutex_lock(&acpi_scan_lock);
@@ -471,10 +465,24 @@ static void acpi_device_release(struct device *dev)
static void acpi_device_del(struct acpi_device *device)
{
+ struct acpi_device_bus_id *acpi_device_bus_id;
+
mutex_lock(&acpi_device_lock);
if (device->parent)
list_del(&device->node);
+ list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node)
+ if (!strcmp(acpi_device_bus_id->bus_id,
+ acpi_device_hid(device))) {
+ if (acpi_device_bus_id->instance_no > 0)
+ acpi_device_bus_id->instance_no--;
+ else {
+ list_del(&acpi_device_bus_id->node);
+ kfree(acpi_device_bus_id);
+ }
+ break;
+ }
+
list_del(&device->wakeup_list);
mutex_unlock(&acpi_device_lock);
@@ -1461,7 +1469,7 @@ static int acpi_bus_type_and_status(acpi_handle handle, int *type,
*type = ACPI_BUS_TYPE_DEVICE;
status = acpi_bus_get_status_handle(handle, sta);
if (ACPI_FAILURE(status))
- return -ENODEV;
+ *sta = 0;
break;
case ACPI_TYPE_PROCESSOR:
*type = ACPI_BUS_TYPE_PROCESSOR;
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 0d94621dc856..9cb975200cac 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -61,7 +61,7 @@ static int acpi_sleep_prepare(u32 acpi_state)
if (acpi_state == ACPI_STATE_S3) {
if (!acpi_wakeup_address)
return -EFAULT;
- acpi_set_firmware_waking_vector(acpi_wakeup_address);
+ acpi_set_waking_vector(acpi_wakeup_address);
}
ACPI_FLUSH_CPU_CACHE();
@@ -410,7 +410,7 @@ static void acpi_pm_finish(void)
acpi_leave_sleep_state(acpi_state);
/* reset firmware waking vector */
- acpi_set_firmware_waking_vector((acpi_physical_address) 0);
+ acpi_set_waking_vector(0);
acpi_target_sleep_state = ACPI_STATE_S0;
diff --git a/drivers/acpi/sleep.h b/drivers/acpi/sleep.h
index c797ffa568d5..a9cc34e663f9 100644
--- a/drivers/acpi/sleep.h
+++ b/drivers/acpi/sleep.h
@@ -6,3 +6,9 @@ extern struct list_head acpi_wakeup_device_list;
extern struct mutex acpi_device_lock;
extern void acpi_resume_power_resources(void);
+
+static inline acpi_status acpi_set_waking_vector(u32 wakeup_address)
+{
+ return acpi_set_firmware_waking_vector(
+ (acpi_physical_address)wakeup_address, 0);
+}
diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c
index 475c9079bf85..f2f9873bb5c3 100644
--- a/drivers/acpi/utils.c
+++ b/drivers/acpi/utils.c
@@ -29,6 +29,7 @@
#include <linux/dynamic_debug.h>
#include "internal.h"
+#include "sleep.h"
#define _COMPONENT ACPI_BUS_COMPONENT
ACPI_MODULE_NAME("utils");
@@ -709,6 +710,36 @@ bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, int rev, u64 funcs)
}
EXPORT_SYMBOL(acpi_check_dsm);
+/**
+ * acpi_dev_present - Detect presence of a given ACPI device in the system.
+ * @hid: Hardware ID of the device.
+ *
+ * Return %true if the device was present at the moment of invocation.
+ * Note that if the device is pluggable, it may since have disappeared.
+ *
+ * For this function to work, acpi_bus_scan() must have been executed
+ * which happens in the subsys_initcall() subsection. Hence, do not
+ * call from a subsys_initcall() or earlier (use acpi_get_devices()
+ * instead). Calling from module_init() is fine (which is synonymous
+ * with device_initcall()).
+ */
+bool acpi_dev_present(const char *hid)
+{
+ struct acpi_device_bus_id *acpi_device_bus_id;
+ bool found = false;
+
+ mutex_lock(&acpi_device_lock);
+ list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node)
+ if (!strcmp(acpi_device_bus_id->bus_id, hid)) {
+ found = true;
+ break;
+ }
+ mutex_unlock(&acpi_device_lock);
+
+ return found;
+}
+EXPORT_SYMBOL(acpi_dev_present);
+
/*
* acpi_backlight= handling, this is done here rather then in video_detect.c
* because __setup cannot be used in modules.
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
index daaf1c4e1e0f..90e2d54be526 100644
--- a/drivers/acpi/video_detect.c
+++ b/drivers/acpi/video_detect.c
@@ -250,6 +250,15 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "XPS L521X"),
},
},
+ {
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=108971 */
+ .callback = video_detect_force_video,
+ .ident = "SAMSUNG 530U4E/540U4E",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "530U4E/540U4E"),
+ },
+ },
/* Non win8 machines which need native backlight nevertheless */
{
@@ -279,6 +288,14 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro12,1"),
},
},
+ {
+ .callback = video_detect_force_native,
+ .ident = "Dell Vostro V131",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"),
+ },
+ },
{ },
};
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 6aaa3f81755b..861643ea91b5 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -100,7 +100,7 @@ config SATA_AHCI_PLATFORM
config AHCI_BRCMSTB
tristate "Broadcom STB AHCI SATA support"
- depends on ARCH_BRCMSTB
+ depends on ARCH_BRCMSTB || BMIPS_GENERIC
help
This option enables support for the AHCI SATA3 controller found on
STB SoC's.
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index ff02bb4218fc..594fcabd22cd 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -314,16 +314,6 @@ static const struct pci_device_id ahci_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, 0x1f37), board_ahci_avn }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f3e), board_ahci_avn }, /* Avoton RAID */
{ PCI_VDEVICE(INTEL, 0x1f3f), board_ahci_avn }, /* Avoton RAID */
- { PCI_VDEVICE(INTEL, 0xa182), board_ahci }, /* Lewisburg AHCI*/
- { PCI_VDEVICE(INTEL, 0xa202), board_ahci }, /* Lewisburg AHCI*/
- { PCI_VDEVICE(INTEL, 0xa184), board_ahci }, /* Lewisburg RAID*/
- { PCI_VDEVICE(INTEL, 0xa204), board_ahci }, /* Lewisburg RAID*/
- { PCI_VDEVICE(INTEL, 0xa186), board_ahci }, /* Lewisburg RAID*/
- { PCI_VDEVICE(INTEL, 0xa206), board_ahci }, /* Lewisburg RAID*/
- { PCI_VDEVICE(INTEL, 0x2822), board_ahci }, /* Lewisburg RAID*/
- { PCI_VDEVICE(INTEL, 0x2826), board_ahci }, /* Lewisburg RAID*/
- { PCI_VDEVICE(INTEL, 0xa18e), board_ahci }, /* Lewisburg RAID*/
- { PCI_VDEVICE(INTEL, 0xa20e), board_ahci }, /* Lewisburg RAID*/
{ PCI_VDEVICE(INTEL, 0x2823), board_ahci }, /* Wellsburg RAID */
{ PCI_VDEVICE(INTEL, 0x2827), board_ahci }, /* Wellsburg RAID */
{ PCI_VDEVICE(INTEL, 0x8d02), board_ahci }, /* Wellsburg AHCI */
@@ -350,10 +340,22 @@ static const struct pci_device_id ahci_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, 0x9d03), board_ahci }, /* Sunrise Point-LP AHCI */
{ PCI_VDEVICE(INTEL, 0x9d05), board_ahci }, /* Sunrise Point-LP RAID */
{ PCI_VDEVICE(INTEL, 0x9d07), board_ahci }, /* Sunrise Point-LP RAID */
+ { PCI_VDEVICE(INTEL, 0xa102), board_ahci }, /* Sunrise Point-H AHCI */
{ PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H AHCI */
{ PCI_VDEVICE(INTEL, 0xa105), board_ahci }, /* Sunrise Point-H RAID */
+ { PCI_VDEVICE(INTEL, 0xa106), board_ahci }, /* Sunrise Point-H RAID */
{ PCI_VDEVICE(INTEL, 0xa107), board_ahci }, /* Sunrise Point-H RAID */
{ PCI_VDEVICE(INTEL, 0xa10f), board_ahci }, /* Sunrise Point-H RAID */
+ { PCI_VDEVICE(INTEL, 0x2822), board_ahci }, /* Lewisburg RAID*/
+ { PCI_VDEVICE(INTEL, 0x2826), board_ahci }, /* Lewisburg RAID*/
+ { PCI_VDEVICE(INTEL, 0xa182), board_ahci }, /* Lewisburg AHCI*/
+ { PCI_VDEVICE(INTEL, 0xa184), board_ahci }, /* Lewisburg RAID*/
+ { PCI_VDEVICE(INTEL, 0xa186), board_ahci }, /* Lewisburg RAID*/
+ { PCI_VDEVICE(INTEL, 0xa18e), board_ahci }, /* Lewisburg RAID*/
+ { PCI_VDEVICE(INTEL, 0xa202), board_ahci }, /* Lewisburg AHCI*/
+ { PCI_VDEVICE(INTEL, 0xa204), board_ahci }, /* Lewisburg RAID*/
+ { PCI_VDEVICE(INTEL, 0xa206), board_ahci }, /* Lewisburg RAID*/
+ { PCI_VDEVICE(INTEL, 0xa20e), board_ahci }, /* Lewisburg RAID*/
/* JMicron 360/1/3/5/6, match class to avoid IDE function */
{ PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
@@ -1304,15 +1306,13 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host)
#endif
/*
- * ahci_init_msix() only implements single MSI-X support, not multiple
- * MSI-X per-port interrupts. This is needed for host controllers that only
- * have MSI-X support implemented, but no MSI or intx.
+ * ahci_init_msix() - optionally enable per-port MSI-X otherwise defer
+ * to single msi.
*/
static int ahci_init_msix(struct pci_dev *pdev, unsigned int n_ports,
- struct ahci_host_priv *hpriv)
+ struct ahci_host_priv *hpriv, unsigned long flags)
{
- int rc, nvec;
- struct msix_entry entry = {};
+ int nvec, i, rc;
/* Do not init MSI-X if MSI is disabled for the device */
if (hpriv->flags & AHCI_HFLAG_NO_MSI)
@@ -1322,22 +1322,39 @@ static int ahci_init_msix(struct pci_dev *pdev, unsigned int n_ports,
if (nvec < 0)
return nvec;
- if (!nvec) {
+ /*
+ * Proper MSI-X implementations will have a vector per-port.
+ * Barring that, we prefer single-MSI over single-MSIX. If this
+ * check fails (not enough MSI-X vectors for all ports) we will
+ * be called again with the flag clear iff ahci_init_msi()
+ * fails.
+ */
+ if (flags & AHCI_HFLAG_MULTI_MSIX) {
+ if (nvec < n_ports)
+ return -ENODEV;
+ nvec = n_ports;
+ } else if (nvec) {
+ nvec = 1;
+ } else {
+ /*
+ * Emit dev_err() since this was the non-legacy irq
+ * method of last resort.
+ */
rc = -ENODEV;
goto fail;
}
- /*
- * There can be more than one vector (e.g. for error detection or
- * hdd hotplug). Only the first vector (entry.entry = 0) is used.
- */
- rc = pci_enable_msix_exact(pdev, &entry, 1);
+ for (i = 0; i < nvec; i++)
+ hpriv->msix[i].entry = i;
+ rc = pci_enable_msix_exact(pdev, hpriv->msix, nvec);
if (rc < 0)
goto fail;
- hpriv->irq = entry.vector;
+ if (nvec > 1)
+ hpriv->flags |= AHCI_HFLAG_MULTI_MSIX;
+ hpriv->irq = hpriv->msix[0].vector; /* for single msi-x */
- return 1;
+ return nvec;
fail:
dev_err(&pdev->dev,
"failed to enable MSI-X with error %d, # of vectors: %d\n",
@@ -1401,20 +1418,25 @@ static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
{
int nvec;
+ /*
+ * Try to enable per-port MSI-X. If the host is not capable
+ * fall back to single MSI before finally attempting single
+ * MSI-X.
+ */
+ nvec = ahci_init_msix(pdev, n_ports, hpriv, AHCI_HFLAG_MULTI_MSIX);
+ if (nvec >= 0)
+ return nvec;
+
nvec = ahci_init_msi(pdev, n_ports, hpriv);
if (nvec >= 0)
return nvec;
- /*
- * Currently, MSI-X support only implements single IRQ mode and
- * exists for controllers which can't do other types of IRQ. Only
- * set it up if MSI fails.
- */
- nvec = ahci_init_msix(pdev, n_ports, hpriv);
+ /* try single-msix */
+ nvec = ahci_init_msix(pdev, n_ports, hpriv, 0);
if (nvec >= 0)
return nvec;
- /* lagacy intx interrupts */
+ /* legacy intx interrupts */
pci_intx(pdev, 1);
hpriv->irq = pdev->irq;
@@ -1576,7 +1598,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (!host)
return -ENOMEM;
host->private_data = hpriv;
-
+ hpriv->msix = devm_kzalloc(&pdev->dev,
+ sizeof(struct msix_entry) * n_ports, GFP_KERNEL);
+ if (!hpriv->msix)
+ return -ENOMEM;
ahci_init_interrupts(pdev, n_ports, hpriv);
if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index 45586c1dbbdc..a4faa438889c 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -35,6 +35,7 @@
#ifndef _AHCI_H
#define _AHCI_H
+#include <linux/pci.h>
#include <linux/clk.h>
#include <linux/libata.h>
#include <linux/phy/phy.h>
@@ -237,11 +238,18 @@ enum {
AHCI_HFLAG_DELAY_ENGINE = (1 << 15), /* do not start engine on
port start (wait until
error-handling stage) */
- AHCI_HFLAG_MULTI_MSI = (1 << 16), /* multiple PCI MSIs */
AHCI_HFLAG_NO_DEVSLP = (1 << 17), /* no device sleep */
AHCI_HFLAG_NO_FBS = (1 << 18), /* no FBS */
AHCI_HFLAG_EDGE_IRQ = (1 << 19), /* HOST_IRQ_STAT behaves as
Edge Triggered */
+#ifdef CONFIG_PCI_MSI
+ AHCI_HFLAG_MULTI_MSI = (1 << 20), /* multiple PCI MSIs */
+ AHCI_HFLAG_MULTI_MSIX = (1 << 21), /* per-port MSI-X */
+#else
+ /* compile out MSI infrastructure */
+ AHCI_HFLAG_MULTI_MSI = 0,
+ AHCI_HFLAG_MULTI_MSIX = 0,
+#endif
/* ap->flags bits */
@@ -308,7 +316,6 @@ struct ahci_port_priv {
unsigned int ncq_saw_d2h:1;
unsigned int ncq_saw_dmas:1;
unsigned int ncq_saw_sdb:1;
- atomic_t intr_status; /* interrupts to handle */
spinlock_t lock; /* protects parent ata_port */
u32 intr_mask; /* interrupts to enable */
bool fbs_supported; /* set iff FBS is supported */
@@ -343,6 +350,7 @@ struct ahci_host_priv {
* the PHY position in this array.
*/
struct phy **phys;
+ struct msix_entry *msix; /* Optional MSI-X support */
unsigned nports; /* Number of ports */
void *plat_data; /* Other platform data */
unsigned int irq; /* interrupt line */
@@ -354,6 +362,21 @@ struct ahci_host_priv {
void (*start_engine)(struct ata_port *ap);
};
+#ifdef CONFIG_PCI_MSI
+static inline int ahci_irq_vector(struct ahci_host_priv *hpriv, int port)
+{
+ if (hpriv->flags & AHCI_HFLAG_MULTI_MSIX)
+ return hpriv->msix[port].vector;
+ else
+ return hpriv->irq + port;
+}
+#else
+static inline int ahci_irq_vector(struct ahci_host_priv *hpriv, int port)
+{
+ return hpriv->irq;
+}
+#endif
+
extern int ahci_ignore_sss;
extern struct device_attribute *ahci_shost_attrs[];
diff --git a/drivers/ata/ahci_brcmstb.c b/drivers/ata/ahci_brcmstb.c
index 14b7305d2ba0..b36cae2fd04b 100644
--- a/drivers/ata/ahci_brcmstb.c
+++ b/drivers/ata/ahci_brcmstb.c
@@ -52,8 +52,10 @@
#define SATA_TOP_CTRL_2_PHY_GLOBAL_RESET BIT(14)
#define SATA_TOP_CTRL_PHY_OFFS 0x8
#define SATA_TOP_MAX_PHYS 2
-#define SATA_TOP_CTRL_SATA_TP_OUT 0x1c
-#define SATA_TOP_CTRL_CLIENT_INIT_CTRL 0x20
+
+#define SATA_FIRST_PORT_CTRL 0x700
+#define SATA_NEXT_PORT_CTRL_OFFSET 0x80
+#define SATA_PORT_PCTRL6(reg_base) (reg_base + 0x18)
/* On big-endian MIPS, buses are reversed to big endian, so switch them back */
#if defined(CONFIG_MIPS) && defined(__BIG_ENDIAN)
@@ -69,14 +71,21 @@
(DATA_ENDIAN << DMADESC_ENDIAN_SHIFT) | \
(MMIO_ENDIAN << MMIO_ENDIAN_SHIFT))
+enum brcm_ahci_quirks {
+ BRCM_AHCI_QUIRK_NO_NCQ = BIT(0),
+ BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE = BIT(1),
+};
+
struct brcm_ahci_priv {
struct device *dev;
void __iomem *top_ctrl;
u32 port_mask;
+ u32 quirks;
};
static const struct ata_port_info ahci_brcm_port_info = {
- .flags = AHCI_FLAG_COMMON,
+ .flags = AHCI_FLAG_COMMON | ATA_FLAG_NO_DIPM,
+ .link_flags = ATA_LFLAG_NO_DB_DELAY,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_platform_ops,
@@ -107,6 +116,34 @@ static inline void brcm_sata_writereg(u32 val, void __iomem *addr)
writel_relaxed(val, addr);
}
+static void brcm_sata_alpm_init(struct ahci_host_priv *hpriv)
+{
+ struct brcm_ahci_priv *priv = hpriv->plat_data;
+ u32 bus_ctrl, port_ctrl, host_caps;
+ int i;
+
+ /* Enable support for ALPM */
+ bus_ctrl = brcm_sata_readreg(priv->top_ctrl +
+ SATA_TOP_CTRL_BUS_CTRL);
+ brcm_sata_writereg(bus_ctrl | OVERRIDE_HWINIT,
+ priv->top_ctrl + SATA_TOP_CTRL_BUS_CTRL);
+ host_caps = readl(hpriv->mmio + HOST_CAP);
+ writel(host_caps | HOST_CAP_ALPM, hpriv->mmio);
+ brcm_sata_writereg(bus_ctrl, priv->top_ctrl + SATA_TOP_CTRL_BUS_CTRL);
+
+ /*
+ * Adjust timeout to allow PLL sufficient time to lock while waking
+ * up from slumber mode.
+ */
+ for (i = 0, port_ctrl = SATA_FIRST_PORT_CTRL;
+ i < SATA_TOP_MAX_PHYS;
+ i++, port_ctrl += SATA_NEXT_PORT_CTRL_OFFSET) {
+ if (priv->port_mask & BIT(i))
+ writel(0xff1003fc,
+ hpriv->mmio + SATA_PORT_PCTRL6(port_ctrl));
+ }
+}
+
static void brcm_sata_phy_enable(struct brcm_ahci_priv *priv, int port)
{
void __iomem *phyctrl = priv->top_ctrl + SATA_TOP_CTRL_PHY_CTRL +
@@ -114,6 +151,9 @@ static void brcm_sata_phy_enable(struct brcm_ahci_priv *priv, int port)
void __iomem *p;
u32 reg;
+ if (priv->quirks & BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE)
+ return;
+
/* clear PHY_DEFAULT_POWER_STATE */
p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_1;
reg = brcm_sata_readreg(p);
@@ -143,6 +183,9 @@ static void brcm_sata_phy_disable(struct brcm_ahci_priv *priv, int port)
void __iomem *p;
u32 reg;
+ if (priv->quirks & BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE)
+ return;
+
/* power-off the PHY digital logic */
p = phyctrl + SATA_TOP_CTRL_PHY_CTRL_2;
reg = brcm_sata_readreg(p);
@@ -230,6 +273,7 @@ static int brcm_ahci_resume(struct device *dev)
brcm_sata_init(priv);
brcm_sata_phys_enable(priv);
+ brcm_sata_alpm_init(hpriv);
return ahci_platform_resume(dev);
}
#endif
@@ -256,6 +300,11 @@ static int brcm_ahci_probe(struct platform_device *pdev)
if (IS_ERR(priv->top_ctrl))
return PTR_ERR(priv->top_ctrl);
+ if (of_device_is_compatible(dev->of_node, "brcm,bcm7425-ahci")) {
+ priv->quirks |= BRCM_AHCI_QUIRK_NO_NCQ;
+ priv->quirks |= BRCM_AHCI_QUIRK_SKIP_PHY_ENABLE;
+ }
+
brcm_sata_init(priv);
priv->port_mask = brcm_ahci_get_portmask(pdev, priv);
@@ -269,10 +318,15 @@ static int brcm_ahci_probe(struct platform_device *pdev)
return PTR_ERR(hpriv);
hpriv->plat_data = priv;
+ brcm_sata_alpm_init(hpriv);
+
ret = ahci_platform_enable_resources(hpriv);
if (ret)
return ret;
+ if (priv->quirks & BRCM_AHCI_QUIRK_NO_NCQ)
+ hpriv->flags |= AHCI_HFLAG_NO_NCQ;
+
ret = ahci_platform_init_host(pdev, hpriv, &ahci_brcm_port_info,
&ahci_platform_sht);
if (ret)
@@ -300,6 +354,7 @@ static int brcm_ahci_remove(struct platform_device *pdev)
}
static const struct of_device_id ahci_of_match[] = {
+ {.compatible = "brcm,bcm7425-ahci"},
{.compatible = "brcm,bcm7445-ahci"},
{},
};
diff --git a/drivers/ata/ahci_mvebu.c b/drivers/ata/ahci_mvebu.c
index 8490d37aee2a..f7a7fa81740e 100644
--- a/drivers/ata/ahci_mvebu.c
+++ b/drivers/ata/ahci_mvebu.c
@@ -62,6 +62,7 @@ static void ahci_mvebu_regret_option(struct ahci_host_priv *hpriv)
writel(0x80, hpriv->mmio + AHCI_VENDOR_SPECIFIC_0_DATA);
}
+#ifdef CONFIG_PM_SLEEP
static int ahci_mvebu_suspend(struct platform_device *pdev, pm_message_t state)
{
return ahci_platform_suspend_host(&pdev->dev);
@@ -81,6 +82,10 @@ static int ahci_mvebu_resume(struct platform_device *pdev)
return ahci_platform_resume_host(&pdev->dev);
}
+#else
+#define ahci_mvebu_suspend NULL
+#define ahci_mvebu_resume NULL
+#endif
static const struct ata_port_info ahci_mvebu_port_info = {
.flags = AHCI_FLAG_COMMON,
diff --git a/drivers/ata/ahci_qoriq.c b/drivers/ata/ahci_qoriq.c
index d0f9de96e4ea..7bdee9bd8786 100644
--- a/drivers/ata/ahci_qoriq.c
+++ b/drivers/ata/ahci_qoriq.c
@@ -34,14 +34,20 @@
/* port register default value */
#define AHCI_PORT_PHY_1_CFG 0xa003fffe
-#define AHCI_PORT_PHY_2_CFG 0x28183411
-#define AHCI_PORT_PHY_3_CFG 0x0e081004
-#define AHCI_PORT_PHY_4_CFG 0x00480811
-#define AHCI_PORT_PHY_5_CFG 0x192c96a4
-#define AHCI_PORT_TRANS_CFG 0x08000025
+#define AHCI_PORT_TRANS_CFG 0x08000029
+
+/* for ls1021a */
+#define LS1021A_PORT_PHY2 0x28183414
+#define LS1021A_PORT_PHY3 0x0e080e06
+#define LS1021A_PORT_PHY4 0x064a080b
+#define LS1021A_PORT_PHY5 0x2aa86470
#define SATA_ECC_DISABLE 0x00020000
+/* for ls1043a */
+#define LS1043A_PORT_PHY2 0x28184d1f
+#define LS1043A_PORT_PHY3 0x0e081509
+
enum ahci_qoriq_type {
AHCI_LS1021A,
AHCI_LS1043A,
@@ -151,16 +157,23 @@ static int ahci_qoriq_phy_init(struct ahci_host_priv *hpriv)
case AHCI_LS1021A:
writel(SATA_ECC_DISABLE, qpriv->ecc_addr);
writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
- writel(AHCI_PORT_PHY_2_CFG, reg_base + PORT_PHY2);
- writel(AHCI_PORT_PHY_3_CFG, reg_base + PORT_PHY3);
- writel(AHCI_PORT_PHY_4_CFG, reg_base + PORT_PHY4);
- writel(AHCI_PORT_PHY_5_CFG, reg_base + PORT_PHY5);
+ writel(LS1021A_PORT_PHY2, reg_base + PORT_PHY2);
+ writel(LS1021A_PORT_PHY3, reg_base + PORT_PHY3);
+ writel(LS1021A_PORT_PHY4, reg_base + PORT_PHY4);
+ writel(LS1021A_PORT_PHY5, reg_base + PORT_PHY5);
writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
break;
case AHCI_LS1043A:
+ writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
+ writel(LS1043A_PORT_PHY2, reg_base + PORT_PHY2);
+ writel(LS1043A_PORT_PHY3, reg_base + PORT_PHY3);
+ writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
+ break;
+
case AHCI_LS2080A:
writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
+ writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
break;
}
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 096064cd6c52..d61740e78d6d 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -43,6 +43,7 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_cmnd.h>
#include <linux/libata.h>
+#include <linux/pci.h>
#include "ahci.h"
#include "libata.h"
@@ -1273,6 +1274,15 @@ static int ahci_exec_polled_cmd(struct ata_port *ap, int pmp,
ata_tf_to_fis(tf, pmp, is_cmd, fis);
ahci_fill_cmd_slot(pp, 0, cmd_fis_len | flags | (pmp << 12));
+ /* set port value for softreset of Port Multiplier */
+ if (pp->fbs_enabled && pp->fbs_last_dev != pmp) {
+ tmp = readl(port_mmio + PORT_FBS);
+ tmp &= ~(PORT_FBS_DEV_MASK | PORT_FBS_DEC);
+ tmp |= pmp << PORT_FBS_DEV_OFFSET;
+ writel(tmp, port_mmio + PORT_FBS);
+ pp->fbs_last_dev = pmp;
+ }
+
/* issue & wait */
writel(1, port_mmio + PORT_CMD_ISSUE);
@@ -1795,41 +1805,24 @@ static void ahci_port_intr(struct ata_port *ap)
ahci_handle_port_interrupt(ap, port_mmio, status);
}
-static irqreturn_t ahci_port_thread_fn(int irq, void *dev_instance)
+static irqreturn_t ahci_multi_irqs_intr_hard(int irq, void *dev_instance)
{
struct ata_port *ap = dev_instance;
- struct ahci_port_priv *pp = ap->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
u32 status;
- status = atomic_xchg(&pp->intr_status, 0);
- if (!status)
- return IRQ_NONE;
-
- spin_lock_bh(ap->lock);
- ahci_handle_port_interrupt(ap, port_mmio, status);
- spin_unlock_bh(ap->lock);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance)
-{
- struct ata_port *ap = dev_instance;
- void __iomem *port_mmio = ahci_port_base(ap);
- struct ahci_port_priv *pp = ap->private_data;
- u32 status;
-
VPRINTK("ENTER\n");
status = readl(port_mmio + PORT_IRQ_STAT);
writel(status, port_mmio + PORT_IRQ_STAT);
- atomic_or(status, &pp->intr_status);
+ spin_lock(ap->lock);
+ ahci_handle_port_interrupt(ap, port_mmio, status);
+ spin_unlock(ap->lock);
VPRINTK("EXIT\n");
- return IRQ_WAKE_THREAD;
+ return IRQ_HANDLED;
}
static u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked)
@@ -2470,9 +2463,10 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv,
}
EXPORT_SYMBOL_GPL(ahci_set_em_messages);
-static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq,
+static int ahci_host_activate_multi_irqs(struct ata_host *host,
struct scsi_host_template *sht)
{
+ struct ahci_host_priv *hpriv = host->private_data;
int i, rc;
rc = ata_host_start(host);
@@ -2484,6 +2478,7 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq,
*/
for (i = 0; i < host->n_ports; i++) {
struct ahci_port_priv *pp = host->ports[i]->private_data;
+ int irq = ahci_irq_vector(hpriv, i);
/* Do not receive interrupts sent by dummy ports */
if (!pp) {
@@ -2491,14 +2486,14 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host, int irq,
continue;
}
- rc = devm_request_threaded_irq(host->dev, irq + i,
- ahci_multi_irqs_intr,
- ahci_port_thread_fn, 0,
- pp->irq_desc, host->ports[i]);
+ rc = devm_request_irq(host->dev, irq, ahci_multi_irqs_intr_hard,
+ 0, pp->irq_desc, host->ports[i]);
+
if (rc)
return rc;
- ata_port_desc(host->ports[i], "irq %d", irq + i);
+ ata_port_desc(host->ports[i], "irq %d", irq);
}
+
return ata_host_register(host, sht);
}
@@ -2519,8 +2514,8 @@ int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht)
int irq = hpriv->irq;
int rc;
- if (hpriv->flags & AHCI_HFLAG_MULTI_MSI)
- rc = ahci_host_activate_multi_irqs(host, irq, sht);
+ if (hpriv->flags & (AHCI_HFLAG_MULTI_MSI | AHCI_HFLAG_MULTI_MSIX))
+ rc = ahci_host_activate_multi_irqs(host, sht);
else if (hpriv->flags & AHCI_HFLAG_EDGE_IRQ)
rc = ata_host_activate(host, irq, ahci_single_edge_irq_intr,
IRQF_SHARED, sht);
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index b79cb10e289e..cbb74719d2c1 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -50,6 +50,7 @@
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/timer.h>
+#include <linux/time.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/suspend.h>
@@ -3597,7 +3598,8 @@ int sata_link_resume(struct ata_link *link, const unsigned long *params,
* immediately after resuming. Delay 200ms before
* debouncing.
*/
- ata_msleep(link->ap, 200);
+ if (!(link->flags & ATA_LFLAG_NO_DB_DELAY))
+ ata_msleep(link->ap, 200);
/* is SControl restored correctly? */
if ((rc = sata_scr_read(link, SCR_CONTROL, &scontrol)))
@@ -6223,6 +6225,7 @@ int ata_host_activate(struct ata_host *host, int irq,
struct scsi_host_template *sht)
{
int i, rc;
+ char *irq_desc;
rc = ata_host_start(host);
if (rc)
@@ -6234,8 +6237,14 @@ int ata_host_activate(struct ata_host *host, int irq,
return ata_host_register(host, sht);
}
+ irq_desc = devm_kasprintf(host->dev, GFP_KERNEL, "%s[%s]",
+ dev_driver_string(host->dev),
+ dev_name(host->dev));
+ if (!irq_desc)
+ return -ENOMEM;
+
rc = devm_request_irq(host->dev, irq, irq_handler, irq_flags,
- dev_name(host->dev), host);
+ irq_desc, host);
if (rc)
return rc;
@@ -6697,7 +6706,12 @@ void ata_msleep(struct ata_port *ap, unsigned int msecs)
if (owns_eh)
ata_eh_release(ap);
- msleep(msecs);
+ if (msecs < 20) {
+ unsigned long usecs = msecs * USEC_PER_MSEC;
+ usleep_range(usecs, usecs + 50);
+ } else {
+ msleep(msecs);
+ }
if (owns_eh)
ata_eh_acquire(ap);
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index cb0508af1459..961acc788f44 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -1505,12 +1505,20 @@ static const char *ata_err_string(unsigned int err_mask)
unsigned int ata_read_log_page(struct ata_device *dev, u8 log,
u8 page, void *buf, unsigned int sectors)
{
+ unsigned long ap_flags = dev->link->ap->flags;
struct ata_taskfile tf;
unsigned int err_mask;
bool dma = false;
DPRINTK("read log page - log 0x%x, page 0x%x\n", log, page);
+ /*
+ * Return error without actually issuing the command on controllers
+ * which e.g. lockup on a read log page.
+ */
+ if (ap_flags & ATA_FLAG_NO_LOG_PAGE)
+ return AC_ERR_DEV;
+
retry:
ata_tf_init(dev, &tf);
if (dev->dma_mode && ata_id_has_read_log_dma_ext(dev->id) &&
diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c
index 5389579c5120..a723ae929783 100644
--- a/drivers/ata/sata_fsl.c
+++ b/drivers/ata/sata_fsl.c
@@ -45,7 +45,8 @@ enum {
SATA_FSL_MAX_PRD_DIRECT = 16, /* Direct PRDT entries */
SATA_FSL_HOST_FLAGS = (ATA_FLAG_SATA | ATA_FLAG_PIO_DMA |
- ATA_FLAG_PMP | ATA_FLAG_NCQ | ATA_FLAG_AN),
+ ATA_FLAG_PMP | ATA_FLAG_NCQ |
+ ATA_FLAG_AN | ATA_FLAG_NO_LOG_PAGE),
SATA_FSL_MAX_CMDS = SATA_FSL_QUEUE_DEPTH,
SATA_FSL_CMD_HDR_SIZE = 16, /* 4 DWORDS */
diff --git a/drivers/ata/sata_rcar.c b/drivers/ata/sata_rcar.c
index 8804127b108c..f72d601e300a 100644
--- a/drivers/ata/sata_rcar.c
+++ b/drivers/ata/sata_rcar.c
@@ -854,17 +854,14 @@ static struct of_device_id sata_rcar_match[] = {
.compatible = "renesas,sata-r8a7793",
.data = (void *)RCAR_GEN2_SATA
},
+ {
+ .compatible = "renesas,sata-r8a7795",
+ .data = (void *)RCAR_GEN2_SATA
+ },
{ },
};
MODULE_DEVICE_TABLE(of, sata_rcar_match);
-static const struct platform_device_id sata_rcar_id_table[] = {
- { "sata_rcar", RCAR_GEN1_SATA }, /* Deprecated by "sata-r8a7779" */
- { "sata-r8a7779", RCAR_GEN1_SATA },
- { },
-};
-MODULE_DEVICE_TABLE(platform, sata_rcar_id_table);
-
static int sata_rcar_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id;
@@ -884,11 +881,10 @@ static int sata_rcar_probe(struct platform_device *pdev)
return -ENOMEM;
of_id = of_match_device(sata_rcar_match, &pdev->dev);
- if (of_id)
- priv->type = (enum sata_rcar_type)of_id->data;
- else
- priv->type = platform_get_device_id(pdev)->driver_data;
+ if (!of_id)
+ return -ENODEV;
+ priv->type = (enum sata_rcar_type)of_id->data;
priv->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(priv->clk)) {
dev_err(&pdev->dev, "failed to get access to sata clock\n");
@@ -1018,7 +1014,6 @@ static const struct dev_pm_ops sata_rcar_pm_ops = {
static struct platform_driver sata_rcar_driver = {
.probe = sata_rcar_probe,
.remove = sata_rcar_remove,
- .id_table = sata_rcar_id_table,
.driver = {
.name = DRV_NAME,
.of_match_table = sata_rcar_match,
diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c
index dea6edcbf145..29bcff086bce 100644
--- a/drivers/ata/sata_sil.c
+++ b/drivers/ata/sata_sil.c
@@ -630,6 +630,9 @@ static void sil_dev_config(struct ata_device *dev)
unsigned int n, quirks = 0;
unsigned char model_num[ATA_ID_PROD_LEN + 1];
+ /* This controller doesn't support trim */
+ dev->horkage |= ATA_HORKAGE_NOTRIM;
+
ata_id_c_string(dev->id, model_num, ATA_ID_PROD, sizeof(model_num));
for (n = 0; sil_blacklist[n].product; n++)
diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c
index fab504fd9cfd..48301cb3a316 100644
--- a/drivers/ata/sata_sx4.c
+++ b/drivers/ata/sata_sx4.c
@@ -1396,6 +1396,8 @@ static unsigned int pdc20621_dimm_init(struct ata_host *host)
addr = 0;
length = size * 1024 * 1024;
buf = kzalloc(ECC_ERASE_BUF_SZ, GFP_KERNEL);
+ if (!buf)
+ return 1;
while (addr < length) {
pdc20621_put_to_dimm(host, buf, addr,
ECC_ERASE_BUF_SZ);
diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c
index 3d7fb6516f74..6ac2b2b1e8de 100644
--- a/drivers/atm/solos-pci.c
+++ b/drivers/atm/solos-pci.c
@@ -347,8 +347,8 @@ static char *next_string(struct sk_buff *skb)
*/
static int process_status(struct solos_card *card, int port, struct sk_buff *skb)
{
- char *str, *end, *state_str, *snr, *attn;
- int ver, rate_up, rate_down;
+ char *str, *state_str, *snr, *attn;
+ int ver, rate_up, rate_down, err;
if (!card->atmdev[port])
return -ENODEV;
@@ -357,7 +357,11 @@ static int process_status(struct solos_card *card, int port, struct sk_buff *skb
if (!str)
return -EIO;
- ver = simple_strtol(str, NULL, 10);
+ err = kstrtoint(str, 10, &ver);
+ if (err) {
+ dev_warn(&card->dev->dev, "Unexpected status interrupt version\n");
+ return err;
+ }
if (ver < 1) {
dev_warn(&card->dev->dev, "Unexpected status interrupt version %d\n",
ver);
@@ -373,16 +377,16 @@ static int process_status(struct solos_card *card, int port, struct sk_buff *skb
return 0;
}
- rate_down = simple_strtol(str, &end, 10);
- if (*end)
- return -EIO;
+ err = kstrtoint(str, 10, &rate_down);
+ if (err)
+ return err;
str = next_string(skb);
if (!str)
return -EIO;
- rate_up = simple_strtol(str, &end, 10);
- if (*end)
- return -EIO;
+ err = kstrtoint(str, 10, &rate_up);
+ if (err)
+ return err;
state_str = next_string(skb);
if (!state_str)
@@ -417,7 +421,7 @@ static int process_command(struct solos_card *card, int port, struct sk_buff *sk
struct solos_param *prm;
unsigned long flags;
int cmdpid;
- int found = 0;
+ int found = 0, err;
if (skb->len < 7)
return 0;
@@ -428,7 +432,9 @@ static int process_command(struct solos_card *card, int port, struct sk_buff *sk
skb->data[6] != '\n')
return 0;
- cmdpid = simple_strtol(&skb->data[1], NULL, 10);
+ err = kstrtoint(&skb->data[1], 10, &cmdpid);
+ if (err)
+ return err;
spin_lock_irqsave(&card->param_queue_lock, flags);
list_for_each_entry(prm, &card->param_queue, list) {
@@ -519,7 +525,7 @@ struct geos_gpio_attr {
static ssize_t geos_gpio_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr);
struct solos_card *card = pci_get_drvdata(pdev);
uint32_t data32;
@@ -545,7 +551,7 @@ static ssize_t geos_gpio_store(struct device *dev, struct device_attribute *attr
static ssize_t geos_gpio_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr);
struct solos_card *card = pci_get_drvdata(pdev);
uint32_t data32;
@@ -559,7 +565,7 @@ static ssize_t geos_gpio_show(struct device *dev, struct device_attribute *attr,
static ssize_t hardware_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
struct geos_gpio_attr *gattr = container_of(attr, struct geos_gpio_attr, attr);
struct solos_card *card = pci_get_drvdata(pdev);
uint32_t data32;
diff --git a/drivers/base/base.h b/drivers/base/base.h
index 1782f3aa386e..e05db388bd1c 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -131,6 +131,8 @@ extern void device_remove_groups(struct device *dev,
extern char *make_class_name(const char *name, struct kobject *kobj);
extern int devres_release_all(struct device *dev);
+extern void device_block_probing(void);
+extern void device_unblock_probing(void);
/* /sys/devices directory */
extern struct kset *devices_kset;
diff --git a/drivers/base/component.c b/drivers/base/component.c
index f748430bb654..89f5cf68d80a 100644
--- a/drivers/base/component.c
+++ b/drivers/base/component.c
@@ -18,18 +18,24 @@
#include <linux/mutex.h>
#include <linux/slab.h>
+struct component;
+
+struct component_match_array {
+ void *data;
+ int (*compare)(struct device *, void *);
+ void (*release)(struct device *, void *);
+ struct component *component;
+ bool duplicate;
+};
+
struct component_match {
size_t alloc;
size_t num;
- struct {
- void *data;
- int (*fn)(struct device *, void *);
- } compare[0];
+ struct component_match_array *compare;
};
struct master {
struct list_head node;
- struct list_head components;
bool bound;
const struct component_master_ops *ops;
@@ -39,7 +45,6 @@ struct master {
struct component {
struct list_head node;
- struct list_head master_node;
struct master *master;
bool bound;
@@ -63,48 +68,21 @@ static struct master *__master_find(struct device *dev,
return NULL;
}
-/* Attach an unattached component to a master. */
-static void component_attach_master(struct master *master, struct component *c)
-{
- c->master = master;
-
- list_add_tail(&c->master_node, &master->components);
-}
-
-/* Detach a component from a master. */
-static void component_detach_master(struct master *master, struct component *c)
-{
- list_del(&c->master_node);
-
- c->master = NULL;
-}
-
-/*
- * Add a component to a master, finding the component via the compare
- * function and compare data. This is safe to call for duplicate matches
- * and will not result in the same component being added multiple times.
- */
-int component_master_add_child(struct master *master,
+static struct component *find_component(struct master *master,
int (*compare)(struct device *, void *), void *compare_data)
{
struct component *c;
- int ret = -ENXIO;
list_for_each_entry(c, &component_list, node) {
if (c->master && c->master != master)
continue;
- if (compare(c->dev, compare_data)) {
- if (!c->master)
- component_attach_master(master, c);
- ret = 0;
- break;
- }
+ if (compare(c->dev, compare_data))
+ return c;
}
- return ret;
+ return NULL;
}
-EXPORT_SYMBOL_GPL(component_master_add_child);
static int find_components(struct master *master)
{
@@ -112,39 +90,44 @@ static int find_components(struct master *master)
size_t i;
int ret = 0;
- if (!match) {
- /*
- * Search the list of components, looking for components that
- * belong to this master, and attach them to the master.
- */
- return master->ops->add_components(master->dev, master);
- }
-
/*
* Scan the array of match functions and attach
* any components which are found to this master.
*/
for (i = 0; i < match->num; i++) {
- ret = component_master_add_child(master,
- match->compare[i].fn,
- match->compare[i].data);
- if (ret)
+ struct component_match_array *mc = &match->compare[i];
+ struct component *c;
+
+ dev_dbg(master->dev, "Looking for component %zu\n", i);
+
+ if (match->compare[i].component)
+ continue;
+
+ c = find_component(master, mc->compare, mc->data);
+ if (!c) {
+ ret = -ENXIO;
break;
+ }
+
+ dev_dbg(master->dev, "found component %s, duplicate %u\n", dev_name(c->dev), !!c->master);
+
+ /* Attach this component to the master */
+ match->compare[i].duplicate = !!c->master;
+ match->compare[i].component = c;
+ c->master = master;
}
return ret;
}
-/* Detach all attached components from this master */
-static void master_remove_components(struct master *master)
+/* Detach component from associated master */
+static void remove_component(struct master *master, struct component *c)
{
- while (!list_empty(&master->components)) {
- struct component *c = list_first_entry(&master->components,
- struct component, master_node);
-
- WARN_ON(c->master != master);
+ size_t i;
- component_detach_master(master, c);
- }
+ /* Detach the component from this master. */
+ for (i = 0; i < master->match->num; i++)
+ if (master->match->compare[i].component == c)
+ master->match->compare[i].component = NULL;
}
/*
@@ -159,44 +142,32 @@ static int try_to_bring_up_master(struct master *master,
{
int ret;
- if (master->bound)
- return 0;
+ dev_dbg(master->dev, "trying to bring up master\n");
- /*
- * Search the list of components, looking for components that
- * belong to this master, and attach them to the master.
- */
if (find_components(master)) {
- /* Failed to find all components */
- ret = 0;
- goto out;
+ dev_dbg(master->dev, "master has incomplete components\n");
+ return 0;
}
if (component && component->master != master) {
- ret = 0;
- goto out;
+ dev_dbg(master->dev, "master is not for this component (%s)\n",
+ dev_name(component->dev));
+ return 0;
}
- if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) {
- ret = -ENOMEM;
- goto out;
- }
+ if (!devres_open_group(master->dev, NULL, GFP_KERNEL))
+ return -ENOMEM;
/* Found all components */
ret = master->ops->bind(master->dev);
if (ret < 0) {
devres_release_group(master->dev, NULL);
dev_info(master->dev, "master bind failed: %d\n", ret);
- goto out;
+ return ret;
}
master->bound = true;
return 1;
-
-out:
- master_remove_components(master);
-
- return ret;
}
static int try_to_bring_up_masters(struct component *component)
@@ -205,9 +176,11 @@ static int try_to_bring_up_masters(struct component *component)
int ret = 0;
list_for_each_entry(m, &masters, node) {
- ret = try_to_bring_up_master(m, component);
- if (ret != 0)
- break;
+ if (!m->bound) {
+ ret = try_to_bring_up_master(m, component);
+ if (ret != 0)
+ break;
+ }
}
return ret;
@@ -220,45 +193,57 @@ static void take_down_master(struct master *master)
devres_release_group(master->dev, NULL);
master->bound = false;
}
+}
- master_remove_components(master);
+static void component_match_release(struct device *master,
+ struct component_match *match)
+{
+ unsigned int i;
+
+ for (i = 0; i < match->num; i++) {
+ struct component_match_array *mc = &match->compare[i];
+
+ if (mc->release)
+ mc->release(master, mc->data);
+ }
}
-static size_t component_match_size(size_t num)
+static void devm_component_match_release(struct device *dev, void *res)
{
- return offsetof(struct component_match, compare[num]);
+ component_match_release(dev, res);
}
-static struct component_match *component_match_realloc(struct device *dev,
+static int component_match_realloc(struct device *dev,
struct component_match *match, size_t num)
{
- struct component_match *new;
+ struct component_match_array *new;
- if (match && match->alloc == num)
- return match;
+ if (match->alloc == num)
+ return 0;
- new = devm_kmalloc(dev, component_match_size(num), GFP_KERNEL);
+ new = devm_kmalloc_array(dev, num, sizeof(*new), GFP_KERNEL);
if (!new)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
- if (match) {
- memcpy(new, match, component_match_size(min(match->num, num)));
- devm_kfree(dev, match);
- } else {
- new->num = 0;
+ if (match->compare) {
+ memcpy(new, match->compare, sizeof(*new) *
+ min(match->num, num));
+ devm_kfree(dev, match->compare);
}
+ match->compare = new;
+ match->alloc = num;
- new->alloc = num;
-
- return new;
+ return 0;
}
/*
- * Add a component to be matched.
+ * Add a component to be matched, with a release function.
*
* The match array is first created or extended if necessary.
*/
-void component_match_add(struct device *dev, struct component_match **matchptr,
+void component_match_add_release(struct device *master,
+ struct component_match **matchptr,
+ void (*release)(struct device *, void *),
int (*compare)(struct device *, void *), void *compare_data)
{
struct component_match *match = *matchptr;
@@ -266,22 +251,37 @@ void component_match_add(struct device *dev, struct component_match **matchptr,
if (IS_ERR(match))
return;
- if (!match || match->num == match->alloc) {
- size_t new_size = match ? match->alloc + 16 : 15;
+ if (!match) {
+ match = devres_alloc(devm_component_match_release,
+ sizeof(*match), GFP_KERNEL);
+ if (!match) {
+ *matchptr = ERR_PTR(-ENOMEM);
+ return;
+ }
- match = component_match_realloc(dev, match, new_size);
+ devres_add(master, match);
*matchptr = match;
+ }
+
+ if (match->num == match->alloc) {
+ size_t new_size = match ? match->alloc + 16 : 15;
+ int ret;
- if (IS_ERR(match))
+ ret = component_match_realloc(master, match, new_size);
+ if (ret) {
+ *matchptr = ERR_PTR(ret);
return;
+ }
}
- match->compare[match->num].fn = compare;
+ match->compare[match->num].compare = compare;
+ match->compare[match->num].release = release;
match->compare[match->num].data = compare_data;
+ match->compare[match->num].component = NULL;
match->num++;
}
-EXPORT_SYMBOL(component_match_add);
+EXPORT_SYMBOL(component_match_add_release);
int component_master_add_with_match(struct device *dev,
const struct component_master_ops *ops,
@@ -290,15 +290,10 @@ int component_master_add_with_match(struct device *dev,
struct master *master;
int ret;
- if (ops->add_components && match)
- return -EINVAL;
-
- if (match) {
- /* Reallocate the match array for its true size */
- match = component_match_realloc(dev, match, match->num);
- if (IS_ERR(match))
- return PTR_ERR(match);
- }
+ /* Reallocate the match array for its true size */
+ ret = component_match_realloc(dev, match, match->num);
+ if (ret)
+ return ret;
master = kzalloc(sizeof(*master), GFP_KERNEL);
if (!master)
@@ -307,7 +302,6 @@ int component_master_add_with_match(struct device *dev,
master->dev = dev;
master->ops = ops;
master->match = match;
- INIT_LIST_HEAD(&master->components);
/* Add to the list of available masters. */
mutex_lock(&component_mutex);
@@ -326,24 +320,28 @@ int component_master_add_with_match(struct device *dev,
}
EXPORT_SYMBOL_GPL(component_master_add_with_match);
-int component_master_add(struct device *dev,
- const struct component_master_ops *ops)
-{
- return component_master_add_with_match(dev, ops, NULL);
-}
-EXPORT_SYMBOL_GPL(component_master_add);
-
void component_master_del(struct device *dev,
const struct component_master_ops *ops)
{
struct master *master;
+ int i;
mutex_lock(&component_mutex);
master = __master_find(dev, ops);
if (master) {
+ struct component_match *match = master->match;
+
take_down_master(master);
list_del(&master->node);
+
+ if (match) {
+ for (i = 0; i < match->num; i++) {
+ struct component *c = match->compare[i].component;
+ if (c)
+ c->master = NULL;
+ }
+ }
kfree(master);
}
mutex_unlock(&component_mutex);
@@ -366,6 +364,7 @@ void component_unbind_all(struct device *master_dev, void *data)
{
struct master *master;
struct component *c;
+ size_t i;
WARN_ON(!mutex_is_locked(&component_mutex));
@@ -373,8 +372,12 @@ void component_unbind_all(struct device *master_dev, void *data)
if (!master)
return;
- list_for_each_entry_reverse(c, &master->components, master_node)
- component_unbind(c, master, data);
+ /* Unbind components in reverse order */
+ for (i = master->match->num; i--; )
+ if (!master->match->compare[i].duplicate) {
+ c = master->match->compare[i].component;
+ component_unbind(c, master, data);
+ }
}
EXPORT_SYMBOL_GPL(component_unbind_all);
@@ -434,6 +437,7 @@ int component_bind_all(struct device *master_dev, void *data)
{
struct master *master;
struct component *c;
+ size_t i;
int ret = 0;
WARN_ON(!mutex_is_locked(&component_mutex));
@@ -442,16 +446,21 @@ int component_bind_all(struct device *master_dev, void *data)
if (!master)
return -EINVAL;
- list_for_each_entry(c, &master->components, master_node) {
- ret = component_bind(c, master, data);
- if (ret)
- break;
- }
+ /* Bind components in match order */
+ for (i = 0; i < master->match->num; i++)
+ if (!master->match->compare[i].duplicate) {
+ c = master->match->compare[i].component;
+ ret = component_bind(c, master, data);
+ if (ret)
+ break;
+ }
if (ret != 0) {
- list_for_each_entry_continue_reverse(c, &master->components,
- master_node)
- component_unbind(c, master, data);
+ for (; i--; )
+ if (!master->match->compare[i].duplicate) {
+ c = master->match->compare[i].component;
+ component_unbind(c, master, data);
+ }
}
return ret;
@@ -499,8 +508,10 @@ void component_del(struct device *dev, const struct component_ops *ops)
break;
}
- if (component && component->master)
+ if (component && component->master) {
take_down_master(component->master);
+ remove_component(component->master, component);
+ }
mutex_unlock(&component_mutex);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index b7d56c5ea3c6..0a8bdade53f2 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -2261,7 +2261,10 @@ void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode)
if (fwnode_is_primary(fn))
fn = fn->secondary;
- fwnode->secondary = fn;
+ if (fn) {
+ WARN_ON(fwnode->secondary);
+ fwnode->secondary = fn;
+ }
dev->fwnode = fwnode;
} else {
dev->fwnode = fwnode_is_primary(dev->fwnode) ?
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index a641cf3ccad6..7399be790b5d 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -55,6 +55,13 @@ static struct workqueue_struct *deferred_wq;
static atomic_t deferred_trigger_count = ATOMIC_INIT(0);
/*
+ * In some cases, like suspend to RAM or hibernation, It might be reasonable
+ * to prohibit probing of devices as it could be unsafe.
+ * Once defer_all_probes is true all drivers probes will be forcibly deferred.
+ */
+static bool defer_all_probes;
+
+/*
* deferred_probe_work_func() - Retry probing devices in the active list.
*/
static void deferred_probe_work_func(struct work_struct *work)
@@ -172,6 +179,30 @@ static void driver_deferred_probe_trigger(void)
}
/**
+ * device_block_probing() - Block/defere device's probes
+ *
+ * It will disable probing of devices and defer their probes instead.
+ */
+void device_block_probing(void)
+{
+ defer_all_probes = true;
+ /* sync with probes to avoid races. */
+ wait_for_device_probe();
+}
+
+/**
+ * device_unblock_probing() - Unblock/enable device's probes
+ *
+ * It will restore normal behavior and trigger re-probing of deferred
+ * devices.
+ */
+void device_unblock_probing(void)
+{
+ defer_all_probes = false;
+ driver_deferred_probe_trigger();
+}
+
+/**
* deferred_probe_initcall() - Enable probing of deferred devices
*
* We don't want to get in the way when the bulk of drivers are getting probed.
@@ -268,6 +299,9 @@ int device_bind_driver(struct device *dev)
ret = driver_sysfs_add(dev);
if (!ret)
driver_bound(dev);
+ else if (dev->bus)
+ blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
+ BUS_NOTIFY_DRIVER_NOT_BOUND, dev);
return ret;
}
EXPORT_SYMBOL_GPL(device_bind_driver);
@@ -277,9 +311,20 @@ static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue);
static int really_probe(struct device *dev, struct device_driver *drv)
{
- int ret = 0;
+ int ret = -EPROBE_DEFER;
int local_trigger_count = atomic_read(&deferred_trigger_count);
+ if (defer_all_probes) {
+ /*
+ * Value of defer_all_probes can be set only by
+ * device_defer_all_probes_enable() which, in turn, will call
+ * wait_for_device_probe() right after that to avoid any races.
+ */
+ dev_dbg(dev, "Driver %s force probe deferral\n", drv->name);
+ driver_deferred_probe_add(dev);
+ return ret;
+ }
+
atomic_inc(&probe_count);
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
drv->bus->name, __func__, drv->name, dev_name(dev));
@@ -290,7 +335,7 @@ static int really_probe(struct device *dev, struct device_driver *drv)
/* If using pinctrl, bind pins now before probing */
ret = pinctrl_bind_pins(dev);
if (ret)
- goto probe_failed;
+ goto pinctrl_bind_failed;
if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
@@ -334,12 +379,17 @@ static int really_probe(struct device *dev, struct device_driver *drv)
goto done;
probe_failed:
+ if (dev->bus)
+ blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
+ BUS_NOTIFY_DRIVER_NOT_BOUND, dev);
+pinctrl_bind_failed:
devres_release_all(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
dev_set_drvdata(dev, NULL);
if (dev->pm_domain && dev->pm_domain->dismiss)
dev->pm_domain->dismiss(dev);
+ pm_runtime_reinit(dev);
switch (ret) {
case -EPROBE_DEFER:
@@ -393,6 +443,10 @@ int driver_probe_done(void)
*/
void wait_for_device_probe(void)
{
+ /* wait for the deferred probe workqueue to finish */
+ if (driver_deferred_probe_enable)
+ flush_workqueue(deferred_wq);
+
/* wait for the known devices to complete their probing */
wait_event(probe_waitqueue, atomic_read(&probe_count) == 0);
async_synchronize_full();
@@ -695,13 +749,13 @@ static void __device_release_driver(struct device *dev)
dev_set_drvdata(dev, NULL);
if (dev->pm_domain && dev->pm_domain->dismiss)
dev->pm_domain->dismiss(dev);
+ pm_runtime_reinit(dev);
klist_remove(&dev->p->knode_driver);
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_UNBOUND_DRIVER,
dev);
-
}
}
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 2804aed3f416..619fe584a44c 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -303,6 +303,10 @@ static int memory_subsys_offline(struct device *dev)
if (mem->state == MEM_OFFLINE)
return 0;
+ /* Can't offline block with non-present sections */
+ if (mem->section_count != sections_per_block)
+ return -EINVAL;
+
return memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE);
}
@@ -446,8 +450,7 @@ memory_probe_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
u64 phys_addr;
- int nid;
- int i, ret;
+ int nid, ret;
unsigned long pages_per_block = PAGES_PER_SECTION * sections_per_block;
ret = kstrtoull(buf, 0, &phys_addr);
@@ -457,15 +460,12 @@ memory_probe_store(struct device *dev, struct device_attribute *attr,
if (phys_addr & ((pages_per_block << PAGE_SHIFT) - 1))
return -EINVAL;
- for (i = 0; i < sections_per_block; i++) {
- nid = memory_add_physaddr_to_nid(phys_addr);
- ret = add_memory(nid, phys_addr,
- PAGES_PER_SECTION << PAGE_SHIFT);
- if (ret)
- goto out;
+ nid = memory_add_physaddr_to_nid(phys_addr);
+ ret = add_memory(nid, phys_addr,
+ MIN_MEMORY_BLOCK_SIZE * sections_per_block);
- phys_addr += MIN_MEMORY_BLOCK_SIZE;
- }
+ if (ret)
+ goto out;
ret = count;
out:
@@ -614,7 +614,6 @@ static int init_memory_block(struct memory_block **memory,
base_memory_block_id(scn_nr) * sections_per_block;
mem->end_section_nr = mem->start_section_nr + sections_per_block - 1;
mem->state = state;
- mem->section_count++;
start_pfn = section_nr_to_pfn(mem->start_section_nr);
mem->phys_device = arch_get_memory_phys_device(start_pfn);
@@ -668,6 +667,7 @@ int register_new_memory(int nid, struct mem_section *section)
ret = init_memory_block(&mem, section, MEM_OFFLINE);
if (ret)
goto out;
+ mem->section_count++;
}
if (mem->section_count == sections_per_block)
@@ -688,7 +688,7 @@ unregister_memory(struct memory_block *memory)
device_unregister(&memory->dev);
}
-static int remove_memory_block(unsigned long node_id,
+static int remove_memory_section(unsigned long node_id,
struct mem_section *section, int phys_device)
{
struct memory_block *mem;
@@ -712,7 +712,7 @@ int unregister_memory_section(struct mem_section *section)
if (!present_section(section))
return -EINVAL;
- return remove_memory_block(0, section, 0);
+ return remove_memory_section(0, section, 0);
}
#endif /* CONFIG_MEMORY_HOTREMOVE */
diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c
index 5df4575b5ba7..47c43386786b 100644
--- a/drivers/base/platform-msi.c
+++ b/drivers/base/platform-msi.c
@@ -24,13 +24,17 @@
#include <linux/msi.h>
#include <linux/slab.h>
-#define DEV_ID_SHIFT 24
+#define DEV_ID_SHIFT 21
+#define MAX_DEV_MSIS (1 << (32 - DEV_ID_SHIFT))
/*
* Internal data structure containing a (made up, but unique) devid
* and the callback to write the MSI message.
*/
struct platform_msi_priv_data {
+ struct device *dev;
+ void *host_data;
+ msi_alloc_info_t arg;
irq_write_msi_msg_t write_msg;
int devid;
};
@@ -110,39 +114,49 @@ static void platform_msi_update_chip_ops(struct msi_domain_info *info)
chip->irq_write_msi_msg = platform_msi_write_msg;
}
-static void platform_msi_free_descs(struct device *dev)
+static void platform_msi_free_descs(struct device *dev, int base, int nvec)
{
struct msi_desc *desc, *tmp;
list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) {
- list_del(&desc->list);
- free_msi_entry(desc);
+ if (desc->platform.msi_index >= base &&
+ desc->platform.msi_index < (base + nvec)) {
+ list_del(&desc->list);
+ free_msi_entry(desc);
+ }
}
}
-static int platform_msi_alloc_descs(struct device *dev, int nvec,
- struct platform_msi_priv_data *data)
+static int platform_msi_alloc_descs_with_irq(struct device *dev, int virq,
+ int nvec,
+ struct platform_msi_priv_data *data)
{
- int i;
+ struct msi_desc *desc;
+ int i, base = 0;
- for (i = 0; i < nvec; i++) {
- struct msi_desc *desc;
+ if (!list_empty(dev_to_msi_list(dev))) {
+ desc = list_last_entry(dev_to_msi_list(dev),
+ struct msi_desc, list);
+ base = desc->platform.msi_index + 1;
+ }
+ for (i = 0; i < nvec; i++) {
desc = alloc_msi_entry(dev);
if (!desc)
break;
desc->platform.msi_priv_data = data;
- desc->platform.msi_index = i;
+ desc->platform.msi_index = base + i;
desc->nvec_used = 1;
+ desc->irq = virq ? virq + i : 0;
list_add_tail(&desc->list, dev_to_msi_list(dev));
}
if (i != nvec) {
/* Clean up the mess */
- platform_msi_free_descs(dev);
+ platform_msi_free_descs(dev, base, nvec);
return -ENOMEM;
}
@@ -150,6 +164,13 @@ static int platform_msi_alloc_descs(struct device *dev, int nvec,
return 0;
}
+static int platform_msi_alloc_descs(struct device *dev, int nvec,
+ struct platform_msi_priv_data *data)
+
+{
+ return platform_msi_alloc_descs_with_irq(dev, 0, nvec, data);
+}
+
/**
* platform_msi_create_irq_domain - Create a platform MSI interrupt domain
* @fwnode: Optional fwnode of the interrupt controller
@@ -180,56 +201,75 @@ struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
return domain;
}
-/**
- * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev
- * @dev: The device for which to allocate interrupts
- * @nvec: The number of interrupts to allocate
- * @write_msi_msg: Callback to write an interrupt message for @dev
- *
- * Returns:
- * Zero for success, or an error code in case of failure
- */
-int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
- irq_write_msi_msg_t write_msi_msg)
+static struct platform_msi_priv_data *
+platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec,
+ irq_write_msi_msg_t write_msi_msg)
{
- struct platform_msi_priv_data *priv_data;
- int err;
-
+ struct platform_msi_priv_data *datap;
/*
* Limit the number of interrupts to 256 per device. Should we
* need to bump this up, DEV_ID_SHIFT should be adjusted
* accordingly (which would impact the max number of MSI
* capable devices).
*/
- if (!dev->msi_domain || !write_msi_msg || !nvec ||
- nvec > (1 << (32 - DEV_ID_SHIFT)))
- return -EINVAL;
+ if (!dev->msi_domain || !write_msi_msg || !nvec || nvec > MAX_DEV_MSIS)
+ return ERR_PTR(-EINVAL);
if (dev->msi_domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) {
dev_err(dev, "Incompatible msi_domain, giving up\n");
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
}
/* Already had a helping of MSI? Greed... */
if (!list_empty(dev_to_msi_list(dev)))
- return -EBUSY;
+ return ERR_PTR(-EBUSY);
+
+ datap = kzalloc(sizeof(*datap), GFP_KERNEL);
+ if (!datap)
+ return ERR_PTR(-ENOMEM);
+
+ datap->devid = ida_simple_get(&platform_msi_devid_ida,
+ 0, 1 << DEV_ID_SHIFT, GFP_KERNEL);
+ if (datap->devid < 0) {
+ int err = datap->devid;
+ kfree(datap);
+ return ERR_PTR(err);
+ }
- priv_data = kzalloc(sizeof(*priv_data), GFP_KERNEL);
- if (!priv_data)
- return -ENOMEM;
+ datap->write_msg = write_msi_msg;
+ datap->dev = dev;
- priv_data->devid = ida_simple_get(&platform_msi_devid_ida,
- 0, 1 << DEV_ID_SHIFT, GFP_KERNEL);
- if (priv_data->devid < 0) {
- err = priv_data->devid;
- goto out_free_data;
- }
+ return datap;
+}
+
+static void platform_msi_free_priv_data(struct platform_msi_priv_data *data)
+{
+ ida_simple_remove(&platform_msi_devid_ida, data->devid);
+ kfree(data);
+}
+
+/**
+ * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev
+ * @dev: The device for which to allocate interrupts
+ * @nvec: The number of interrupts to allocate
+ * @write_msi_msg: Callback to write an interrupt message for @dev
+ *
+ * Returns:
+ * Zero for success, or an error code in case of failure
+ */
+int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
+ irq_write_msi_msg_t write_msi_msg)
+{
+ struct platform_msi_priv_data *priv_data;
+ int err;
- priv_data->write_msg = write_msi_msg;
+ priv_data = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg);
+ if (IS_ERR(priv_data))
+ return PTR_ERR(priv_data);
err = platform_msi_alloc_descs(dev, nvec, priv_data);
if (err)
- goto out_free_id;
+ goto out_free_priv_data;
err = msi_domain_alloc_irqs(dev->msi_domain, dev, nvec);
if (err)
@@ -238,11 +278,9 @@ int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
return 0;
out_free_desc:
- platform_msi_free_descs(dev);
-out_free_id:
- ida_simple_remove(&platform_msi_devid_ida, priv_data->devid);
-out_free_data:
- kfree(priv_data);
+ platform_msi_free_descs(dev, 0, nvec);
+out_free_priv_data:
+ platform_msi_free_priv_data(priv_data);
return err;
}
@@ -253,18 +291,126 @@ out_free_data:
*/
void platform_msi_domain_free_irqs(struct device *dev)
{
- struct msi_desc *desc;
+ if (!list_empty(dev_to_msi_list(dev))) {
+ struct msi_desc *desc;
+
+ desc = first_msi_entry(dev);
+ platform_msi_free_priv_data(desc->platform.msi_priv_data);
+ }
+
+ msi_domain_free_irqs(dev->msi_domain, dev);
+ platform_msi_free_descs(dev, 0, MAX_DEV_MSIS);
+}
+
+/**
+ * platform_msi_get_host_data - Query the private data associated with
+ * a platform-msi domain
+ * @domain: The platform-msi domain
+ *
+ * Returns the private data provided when calling
+ * platform_msi_create_device_domain.
+ */
+void *platform_msi_get_host_data(struct irq_domain *domain)
+{
+ struct platform_msi_priv_data *data = domain->host_data;
+ return data->host_data;
+}
+
+/**
+ * platform_msi_create_device_domain - Create a platform-msi domain
+ *
+ * @dev: The device generating the MSIs
+ * @nvec: The number of MSIs that need to be allocated
+ * @write_msi_msg: Callback to write an interrupt message for @dev
+ * @ops: The hierarchy domain operations to use
+ * @host_data: Private data associated to this domain
+ *
+ * Returns an irqdomain for @nvec interrupts
+ */
+struct irq_domain *
+platform_msi_create_device_domain(struct device *dev,
+ unsigned int nvec,
+ irq_write_msi_msg_t write_msi_msg,
+ const struct irq_domain_ops *ops,
+ void *host_data)
+{
+ struct platform_msi_priv_data *data;
+ struct irq_domain *domain;
+ int err;
+
+ data = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg);
+ if (IS_ERR(data))
+ return NULL;
+
+ data->host_data = host_data;
+ domain = irq_domain_create_hierarchy(dev->msi_domain, 0, nvec,
+ of_node_to_fwnode(dev->of_node),
+ ops, data);
+ if (!domain)
+ goto free_priv;
- desc = first_msi_entry(dev);
- if (desc) {
- struct platform_msi_priv_data *data;
+ err = msi_domain_prepare_irqs(domain->parent, dev, nvec, &data->arg);
+ if (err)
+ goto free_domain;
+
+ return domain;
- data = desc->platform.msi_priv_data;
+free_domain:
+ irq_domain_remove(domain);
+free_priv:
+ platform_msi_free_priv_data(data);
+ return NULL;
+}
+
+/**
+ * platform_msi_domain_free - Free interrupts associated with a platform-msi
+ * domain
+ *
+ * @domain: The platform-msi domain
+ * @virq: The base irq from which to perform the free operation
+ * @nvec: How many interrupts to free from @virq
+ */
+void platform_msi_domain_free(struct irq_domain *domain, unsigned int virq,
+ unsigned int nvec)
+{
+ struct platform_msi_priv_data *data = domain->host_data;
+ struct msi_desc *desc;
+ for_each_msi_entry(desc, data->dev) {
+ if (WARN_ON(!desc->irq || desc->nvec_used != 1))
+ return;
+ if (!(desc->irq >= virq && desc->irq < (virq + nvec)))
+ continue;
- ida_simple_remove(&platform_msi_devid_ida, data->devid);
- kfree(data);
+ irq_domain_free_irqs_common(domain, desc->irq, 1);
}
+}
- msi_domain_free_irqs(dev->msi_domain, dev);
- platform_msi_free_descs(dev);
+/**
+ * platform_msi_domain_alloc - Allocate interrupts associated with
+ * a platform-msi domain
+ *
+ * @domain: The platform-msi domain
+ * @virq: The base irq from which to perform the allocate operation
+ * @nvec: How many interrupts to free from @virq
+ *
+ * Return 0 on success, or an error code on failure. Must be called
+ * with irq_domain_mutex held (which can only be done as part of a
+ * top-level interrupt allocation).
+ */
+int platform_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs)
+{
+ struct platform_msi_priv_data *data = domain->host_data;
+ int err;
+
+ err = platform_msi_alloc_descs_with_irq(data->dev, virq, nr_irqs, data);
+ if (err)
+ return err;
+
+ err = msi_domain_populate_irqs(domain->parent, data->dev,
+ virq, nr_irqs, &data->arg);
+ if (err)
+ platform_msi_domain_free(domain, virq, nr_irqs);
+
+ return err;
}
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 1dd6d3bf1098..8dcbb266643b 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -26,6 +26,7 @@
#include <linux/acpi.h>
#include <linux/clk/clk-conf.h>
#include <linux/limits.h>
+#include <linux/property.h>
#include "base.h"
#include "power/power.h"
@@ -117,6 +118,26 @@ int platform_get_irq(struct platform_device *dev, unsigned int num)
EXPORT_SYMBOL_GPL(platform_get_irq);
/**
+ * platform_irq_count - Count the number of IRQs a platform device uses
+ * @dev: platform device
+ *
+ * Return: Number of IRQs a platform device uses or EPROBE_DEFER
+ */
+int platform_irq_count(struct platform_device *dev)
+{
+ int ret, nr = 0;
+
+ while ((ret = platform_get_irq(dev, nr)) >= 0)
+ nr++;
+
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
+ return nr;
+}
+EXPORT_SYMBOL_GPL(platform_irq_count);
+
+/**
* platform_get_resource_byname - get a resource for a device by name
* @dev: platform device
* @type: resource type
@@ -299,6 +320,22 @@ int platform_device_add_data(struct platform_device *pdev, const void *data,
EXPORT_SYMBOL_GPL(platform_device_add_data);
/**
+ * platform_device_add_properties - add built-in properties to a platform device
+ * @pdev: platform device to add properties to
+ * @pset: properties to add
+ *
+ * The function will take deep copy of the properties in @pset and attach
+ * the copy to the platform device. The memory associated with properties
+ * will be freed when the platform device is released.
+ */
+int platform_device_add_properties(struct platform_device *pdev,
+ const struct property_set *pset)
+{
+ return device_add_property_set(&pdev->dev, pset);
+}
+EXPORT_SYMBOL_GPL(platform_device_add_properties);
+
+/**
* platform_device_add - add a platform device to device hierarchy
* @pdev: platform device we're adding
*
@@ -409,6 +446,8 @@ void platform_device_del(struct platform_device *pdev)
if (r->parent)
release_resource(r);
}
+
+ device_remove_property_set(&pdev->dev);
}
}
EXPORT_SYMBOL_GPL(platform_device_del);
@@ -487,6 +526,12 @@ struct platform_device *platform_device_register_full(
if (ret)
goto err;
+ if (pdevinfo->pset) {
+ ret = platform_device_add_properties(pdev, pdevinfo->pset);
+ if (ret)
+ goto err;
+ }
+
ret = platform_device_add(pdev);
if (ret) {
err:
diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c
index 60ee5591ee8f..c39b8617280f 100644
--- a/drivers/base/power/clock_ops.c
+++ b/drivers/base/power/clock_ops.c
@@ -473,6 +473,7 @@ static int pm_clk_notify(struct notifier_block *nb,
enable_clock(dev, NULL);
}
break;
+ case BUS_NOTIFY_DRIVER_NOT_BOUND:
case BUS_NOTIFY_UNBOUND_DRIVER:
if (clknb->con_ids[0]) {
for (con_id = clknb->con_ids; *con_id; con_id++)
diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c
index f32b802b98f4..f48e33385b3e 100644
--- a/drivers/base/power/common.c
+++ b/drivers/base/power/common.c
@@ -112,7 +112,7 @@ EXPORT_SYMBOL_GPL(dev_pm_domain_attach);
/**
* dev_pm_domain_detach - Detach a device from its PM domain.
- * @dev: Device to attach.
+ * @dev: Device to detach.
* @power_off: Used to indicate whether we should power off the device.
*
* This functions will reverse the actions from dev_pm_domain_attach() and thus
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index e03b1ad25a90..b80379012840 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -390,6 +390,7 @@ static int pm_genpd_runtime_suspend(struct device *dev)
struct generic_pm_domain *genpd;
bool (*stop_ok)(struct device *__dev);
struct gpd_timing_data *td = &dev_gpd_data(dev)->td;
+ bool runtime_pm = pm_runtime_enabled(dev);
ktime_t time_start;
s64 elapsed_ns;
int ret;
@@ -400,12 +401,19 @@ static int pm_genpd_runtime_suspend(struct device *dev)
if (IS_ERR(genpd))
return -EINVAL;
+ /*
+ * A runtime PM centric subsystem/driver may re-use the runtime PM
+ * callbacks for other purposes than runtime PM. In those scenarios
+ * runtime PM is disabled. Under these circumstances, we shall skip
+ * validating/measuring the PM QoS latency.
+ */
stop_ok = genpd->gov ? genpd->gov->stop_ok : NULL;
- if (stop_ok && !stop_ok(dev))
+ if (runtime_pm && stop_ok && !stop_ok(dev))
return -EBUSY;
/* Measure suspend latency. */
- time_start = ktime_get();
+ if (runtime_pm)
+ time_start = ktime_get();
ret = genpd_save_dev(genpd, dev);
if (ret)
@@ -418,13 +426,15 @@ static int pm_genpd_runtime_suspend(struct device *dev)
}
/* Update suspend latency value if the measured time exceeds it. */
- elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
- if (elapsed_ns > td->suspend_latency_ns) {
- td->suspend_latency_ns = elapsed_ns;
- dev_dbg(dev, "suspend latency exceeded, %lld ns\n",
- elapsed_ns);
- genpd->max_off_time_changed = true;
- td->constraint_changed = true;
+ if (runtime_pm) {
+ elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
+ if (elapsed_ns > td->suspend_latency_ns) {
+ td->suspend_latency_ns = elapsed_ns;
+ dev_dbg(dev, "suspend latency exceeded, %lld ns\n",
+ elapsed_ns);
+ genpd->max_off_time_changed = true;
+ td->constraint_changed = true;
+ }
}
/*
@@ -453,6 +463,7 @@ static int pm_genpd_runtime_resume(struct device *dev)
{
struct generic_pm_domain *genpd;
struct gpd_timing_data *td = &dev_gpd_data(dev)->td;
+ bool runtime_pm = pm_runtime_enabled(dev);
ktime_t time_start;
s64 elapsed_ns;
int ret;
@@ -479,14 +490,14 @@ static int pm_genpd_runtime_resume(struct device *dev)
out:
/* Measure resume latency. */
- if (timed)
+ if (timed && runtime_pm)
time_start = ktime_get();
genpd_start_dev(genpd, dev);
genpd_restore_dev(genpd, dev);
/* Update resume latency value if the measured time exceeds it. */
- if (timed) {
+ if (timed && runtime_pm) {
elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
if (elapsed_ns > td->resume_latency_ns) {
td->resume_latency_ns = elapsed_ns;
@@ -1252,6 +1263,7 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
return ret;
}
+EXPORT_SYMBOL_GPL(__pm_genpd_add_device);
/**
* pm_genpd_remove_device - Remove a device from an I/O PM domain.
@@ -1302,6 +1314,7 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
return ret;
}
+EXPORT_SYMBOL_GPL(pm_genpd_remove_device);
/**
* pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
@@ -1775,10 +1788,10 @@ int genpd_dev_pm_attach(struct device *dev)
}
pd = of_genpd_get_from_provider(&pd_args);
+ of_node_put(pd_args.np);
if (IS_ERR(pd)) {
dev_dbg(dev, "%s() failed to find PM domain: %ld\n",
__func__, PTR_ERR(pd));
- of_node_put(dev->of_node);
return -EPROBE_DEFER;
}
@@ -1796,7 +1809,6 @@ int genpd_dev_pm_attach(struct device *dev)
if (ret < 0) {
dev_err(dev, "failed to add to PM domain %s: %d",
pd->name, ret);
- of_node_put(dev->of_node);
goto out;
}
diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c
index e60dd12e23aa..1e937ac5f456 100644
--- a/drivers/base/power/domain_governor.c
+++ b/drivers/base/power/domain_governor.c
@@ -160,9 +160,6 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)
struct gpd_timing_data *td;
s64 constraint_ns;
- if (!pdd->dev->driver)
- continue;
-
/*
* Check if the device is allowed to be off long enough for the
* domain to turn off and on (that's how much time it will
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 1710c26ba097..9d626ac08d9c 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -963,6 +963,9 @@ void dpm_complete(pm_message_t state)
}
list_splice(&list, &dpm_list);
mutex_unlock(&dpm_list_mtx);
+
+ /* Allow device probing and trigger re-probing of deferred devices */
+ device_unblock_probing();
trace_suspend_resume(TPS("dpm_complete"), state.event, false);
}
@@ -1624,6 +1627,20 @@ int dpm_prepare(pm_message_t state)
trace_suspend_resume(TPS("dpm_prepare"), state.event, true);
might_sleep();
+ /*
+ * Give a chance for the known devices to complete their probes, before
+ * disable probing of devices. This sync point is important at least
+ * at boot time + hibernation restore.
+ */
+ wait_for_device_probe();
+ /*
+ * It is unsafe if probing of devices will happen during suspend or
+ * hibernation and system behavior will be unpredictable in this case.
+ * So, let's prohibit device's probing here and defer their probes
+ * instead. The normal behavior will be restored in dpm_complete().
+ */
+ device_block_probing();
+
mutex_lock(&dpm_list_mtx);
while (!list_empty(&dpm_list)) {
struct device *dev = to_device(dpm_list.next);
diff --git a/drivers/base/power/opp/Makefile b/drivers/base/power/opp/Makefile
index 33c1e18c41a4..19837ef04d8e 100644
--- a/drivers/base/power/opp/Makefile
+++ b/drivers/base/power/opp/Makefile
@@ -1,2 +1,3 @@
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
obj-y += core.o cpu.o
+obj-$(CONFIG_DEBUG_FS) += debugfs.o
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c
index b8e76f75073b..cf351d3dab1c 100644
--- a/drivers/base/power/opp/core.c
+++ b/drivers/base/power/opp/core.c
@@ -463,6 +463,7 @@ static void _kfree_list_dev_rcu(struct rcu_head *head)
static void _remove_list_dev(struct device_list_opp *list_dev,
struct device_opp *dev_opp)
{
+ opp_debug_unregister(list_dev, dev_opp);
list_del(&list_dev->node);
call_srcu(&dev_opp->srcu_head.srcu, &list_dev->rcu_head,
_kfree_list_dev_rcu);
@@ -472,6 +473,7 @@ struct device_list_opp *_add_list_dev(const struct device *dev,
struct device_opp *dev_opp)
{
struct device_list_opp *list_dev;
+ int ret;
list_dev = kzalloc(sizeof(*list_dev), GFP_KERNEL);
if (!list_dev)
@@ -481,6 +483,12 @@ struct device_list_opp *_add_list_dev(const struct device *dev,
list_dev->dev = dev;
list_add_rcu(&list_dev->node, &dev_opp->dev_list);
+ /* Create debugfs entries for the dev_opp */
+ ret = opp_debug_register(list_dev, dev_opp);
+ if (ret)
+ dev_err(dev, "%s: Failed to register opp debugfs (%d)\n",
+ __func__, ret);
+
return list_dev;
}
@@ -551,6 +559,12 @@ static void _remove_device_opp(struct device_opp *dev_opp)
if (!list_empty(&dev_opp->opp_list))
return;
+ if (dev_opp->supported_hw)
+ return;
+
+ if (dev_opp->prop_name)
+ return;
+
list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp,
node);
@@ -596,6 +610,7 @@ static void _opp_remove(struct device_opp *dev_opp,
*/
if (notify)
srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_REMOVE, opp);
+ opp_debug_remove_one(opp);
list_del_rcu(&opp->node);
call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu);
@@ -673,6 +688,7 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
{
struct dev_pm_opp *opp;
struct list_head *head = &dev_opp->opp_list;
+ int ret;
/*
* Insert new OPP in order of increasing frequency and discard if
@@ -703,6 +719,11 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
new_opp->dev_opp = dev_opp;
list_add_rcu(&new_opp->node, head);
+ ret = opp_debug_create_one(new_opp, dev_opp);
+ if (ret)
+ dev_err(dev, "%s: Failed to register opp to debugfs (%d)\n",
+ __func__, ret);
+
return 0;
}
@@ -776,35 +797,49 @@ unlock:
}
/* TODO: Support multiple regulators */
-static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev)
+static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
+ struct device_opp *dev_opp)
{
u32 microvolt[3] = {0};
u32 val;
int count, ret;
+ struct property *prop = NULL;
+ char name[NAME_MAX];
+
+ /* Search for "opp-microvolt-<name>" */
+ if (dev_opp->prop_name) {
+ snprintf(name, sizeof(name), "opp-microvolt-%s",
+ dev_opp->prop_name);
+ prop = of_find_property(opp->np, name, NULL);
+ }
- /* Missing property isn't a problem, but an invalid entry is */
- if (!of_find_property(opp->np, "opp-microvolt", NULL))
- return 0;
+ if (!prop) {
+ /* Search for "opp-microvolt" */
+ sprintf(name, "opp-microvolt");
+ prop = of_find_property(opp->np, name, NULL);
- count = of_property_count_u32_elems(opp->np, "opp-microvolt");
+ /* Missing property isn't a problem, but an invalid entry is */
+ if (!prop)
+ return 0;
+ }
+
+ count = of_property_count_u32_elems(opp->np, name);
if (count < 0) {
- dev_err(dev, "%s: Invalid opp-microvolt property (%d)\n",
- __func__, count);
+ dev_err(dev, "%s: Invalid %s property (%d)\n",
+ __func__, name, count);
return count;
}
/* There can be one or three elements here */
if (count != 1 && count != 3) {
- dev_err(dev, "%s: Invalid number of elements in opp-microvolt property (%d)\n",
- __func__, count);
+ dev_err(dev, "%s: Invalid number of elements in %s property (%d)\n",
+ __func__, name, count);
return -EINVAL;
}
- ret = of_property_read_u32_array(opp->np, "opp-microvolt", microvolt,
- count);
+ ret = of_property_read_u32_array(opp->np, name, microvolt, count);
if (ret) {
- dev_err(dev, "%s: error parsing opp-microvolt: %d\n", __func__,
- ret);
+ dev_err(dev, "%s: error parsing %s: %d\n", __func__, name, ret);
return -EINVAL;
}
@@ -812,13 +847,272 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev)
opp->u_volt_min = microvolt[1];
opp->u_volt_max = microvolt[2];
- if (!of_property_read_u32(opp->np, "opp-microamp", &val))
+ /* Search for "opp-microamp-<name>" */
+ prop = NULL;
+ if (dev_opp->prop_name) {
+ snprintf(name, sizeof(name), "opp-microamp-%s",
+ dev_opp->prop_name);
+ prop = of_find_property(opp->np, name, NULL);
+ }
+
+ if (!prop) {
+ /* Search for "opp-microamp" */
+ sprintf(name, "opp-microamp");
+ prop = of_find_property(opp->np, name, NULL);
+ }
+
+ if (prop && !of_property_read_u32(opp->np, name, &val))
opp->u_amp = val;
return 0;
}
/**
+ * dev_pm_opp_set_supported_hw() - Set supported platforms
+ * @dev: Device for which supported-hw has to be set.
+ * @versions: Array of hierarchy of versions to match.
+ * @count: Number of elements in the array.
+ *
+ * This is required only for the V2 bindings, and it enables a platform to
+ * specify the hierarchy of versions it supports. OPP layer will then enable
+ * OPPs, which are available for those versions, based on its 'opp-supported-hw'
+ * property.
+ *
+ * Locking: The internal device_opp and opp structures are RCU protected.
+ * Hence this function internally uses RCU updater strategy with mutex locks
+ * to keep the integrity of the internal data structures. Callers should ensure
+ * that this function is *NOT* called under RCU protection or in contexts where
+ * mutex cannot be locked.
+ */
+int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions,
+ unsigned int count)
+{
+ struct device_opp *dev_opp;
+ int ret = 0;
+
+ /* Hold our list modification lock here */
+ mutex_lock(&dev_opp_list_lock);
+
+ dev_opp = _add_device_opp(dev);
+ if (!dev_opp) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ /* Make sure there are no concurrent readers while updating dev_opp */
+ WARN_ON(!list_empty(&dev_opp->opp_list));
+
+ /* Do we already have a version hierarchy associated with dev_opp? */
+ if (dev_opp->supported_hw) {
+ dev_err(dev, "%s: Already have supported hardware list\n",
+ __func__);
+ ret = -EBUSY;
+ goto err;
+ }
+
+ dev_opp->supported_hw = kmemdup(versions, count * sizeof(*versions),
+ GFP_KERNEL);
+ if (!dev_opp->supported_hw) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ dev_opp->supported_hw_count = count;
+ mutex_unlock(&dev_opp_list_lock);
+ return 0;
+
+err:
+ _remove_device_opp(dev_opp);
+unlock:
+ mutex_unlock(&dev_opp_list_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_set_supported_hw);
+
+/**
+ * dev_pm_opp_put_supported_hw() - Releases resources blocked for supported hw
+ * @dev: Device for which supported-hw has to be set.
+ *
+ * This is required only for the V2 bindings, and is called for a matching
+ * dev_pm_opp_set_supported_hw(). Until this is called, the device_opp structure
+ * will not be freed.
+ *
+ * Locking: The internal device_opp and opp structures are RCU protected.
+ * Hence this function internally uses RCU updater strategy with mutex locks
+ * to keep the integrity of the internal data structures. Callers should ensure
+ * that this function is *NOT* called under RCU protection or in contexts where
+ * mutex cannot be locked.
+ */
+void dev_pm_opp_put_supported_hw(struct device *dev)
+{
+ struct device_opp *dev_opp;
+
+ /* Hold our list modification lock here */
+ mutex_lock(&dev_opp_list_lock);
+
+ /* Check for existing list for 'dev' first */
+ dev_opp = _find_device_opp(dev);
+ if (IS_ERR(dev_opp)) {
+ dev_err(dev, "Failed to find dev_opp: %ld\n", PTR_ERR(dev_opp));
+ goto unlock;
+ }
+
+ /* Make sure there are no concurrent readers while updating dev_opp */
+ WARN_ON(!list_empty(&dev_opp->opp_list));
+
+ if (!dev_opp->supported_hw) {
+ dev_err(dev, "%s: Doesn't have supported hardware list\n",
+ __func__);
+ goto unlock;
+ }
+
+ kfree(dev_opp->supported_hw);
+ dev_opp->supported_hw = NULL;
+ dev_opp->supported_hw_count = 0;
+
+ /* Try freeing device_opp if this was the last blocking resource */
+ _remove_device_opp(dev_opp);
+
+unlock:
+ mutex_unlock(&dev_opp_list_lock);
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_put_supported_hw);
+
+/**
+ * dev_pm_opp_set_prop_name() - Set prop-extn name
+ * @dev: Device for which the regulator has to be set.
+ * @name: name to postfix to properties.
+ *
+ * This is required only for the V2 bindings, and it enables a platform to
+ * specify the extn to be used for certain property names. The properties to
+ * which the extension will apply are opp-microvolt and opp-microamp. OPP core
+ * should postfix the property name with -<name> while looking for them.
+ *
+ * Locking: The internal device_opp and opp structures are RCU protected.
+ * Hence this function internally uses RCU updater strategy with mutex locks
+ * to keep the integrity of the internal data structures. Callers should ensure
+ * that this function is *NOT* called under RCU protection or in contexts where
+ * mutex cannot be locked.
+ */
+int dev_pm_opp_set_prop_name(struct device *dev, const char *name)
+{
+ struct device_opp *dev_opp;
+ int ret = 0;
+
+ /* Hold our list modification lock here */
+ mutex_lock(&dev_opp_list_lock);
+
+ dev_opp = _add_device_opp(dev);
+ if (!dev_opp) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ /* Make sure there are no concurrent readers while updating dev_opp */
+ WARN_ON(!list_empty(&dev_opp->opp_list));
+
+ /* Do we already have a prop-name associated with dev_opp? */
+ if (dev_opp->prop_name) {
+ dev_err(dev, "%s: Already have prop-name %s\n", __func__,
+ dev_opp->prop_name);
+ ret = -EBUSY;
+ goto err;
+ }
+
+ dev_opp->prop_name = kstrdup(name, GFP_KERNEL);
+ if (!dev_opp->prop_name) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ mutex_unlock(&dev_opp_list_lock);
+ return 0;
+
+err:
+ _remove_device_opp(dev_opp);
+unlock:
+ mutex_unlock(&dev_opp_list_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_set_prop_name);
+
+/**
+ * dev_pm_opp_put_prop_name() - Releases resources blocked for prop-name
+ * @dev: Device for which the regulator has to be set.
+ *
+ * This is required only for the V2 bindings, and is called for a matching
+ * dev_pm_opp_set_prop_name(). Until this is called, the device_opp structure
+ * will not be freed.
+ *
+ * Locking: The internal device_opp and opp structures are RCU protected.
+ * Hence this function internally uses RCU updater strategy with mutex locks
+ * to keep the integrity of the internal data structures. Callers should ensure
+ * that this function is *NOT* called under RCU protection or in contexts where
+ * mutex cannot be locked.
+ */
+void dev_pm_opp_put_prop_name(struct device *dev)
+{
+ struct device_opp *dev_opp;
+
+ /* Hold our list modification lock here */
+ mutex_lock(&dev_opp_list_lock);
+
+ /* Check for existing list for 'dev' first */
+ dev_opp = _find_device_opp(dev);
+ if (IS_ERR(dev_opp)) {
+ dev_err(dev, "Failed to find dev_opp: %ld\n", PTR_ERR(dev_opp));
+ goto unlock;
+ }
+
+ /* Make sure there are no concurrent readers while updating dev_opp */
+ WARN_ON(!list_empty(&dev_opp->opp_list));
+
+ if (!dev_opp->prop_name) {
+ dev_err(dev, "%s: Doesn't have a prop-name\n", __func__);
+ goto unlock;
+ }
+
+ kfree(dev_opp->prop_name);
+ dev_opp->prop_name = NULL;
+
+ /* Try freeing device_opp if this was the last blocking resource */
+ _remove_device_opp(dev_opp);
+
+unlock:
+ mutex_unlock(&dev_opp_list_lock);
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name);
+
+static bool _opp_is_supported(struct device *dev, struct device_opp *dev_opp,
+ struct device_node *np)
+{
+ unsigned int count = dev_opp->supported_hw_count;
+ u32 version;
+ int ret;
+
+ if (!dev_opp->supported_hw)
+ return true;
+
+ while (count--) {
+ ret = of_property_read_u32_index(np, "opp-supported-hw", count,
+ &version);
+ if (ret) {
+ dev_warn(dev, "%s: failed to read opp-supported-hw property at index %d: %d\n",
+ __func__, count, ret);
+ return false;
+ }
+
+ /* Both of these are bitwise masks of the versions */
+ if (!(version & dev_opp->supported_hw[count]))
+ return false;
+ }
+
+ return true;
+}
+
+/**
* _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings)
* @dev: device for which we do this operation
* @np: device node
@@ -864,6 +1158,12 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
goto free_opp;
}
+ /* Check if the OPP supports hardware's hierarchy of versions or not */
+ if (!_opp_is_supported(dev, dev_opp, np)) {
+ dev_dbg(dev, "OPP not supported by hardware: %llu\n", rate);
+ goto free_opp;
+ }
+
/*
* Rate is defined as an unsigned long in clk API, and so casting
* explicitly to its type. Must be fixed once rate is 64 bit
@@ -879,7 +1179,7 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
if (!of_property_read_u32(np, "clock-latency-ns", &val))
new_opp->clock_latency_ns = val;
- ret = opp_parse_supplies(new_opp, dev);
+ ret = opp_parse_supplies(new_opp, dev, dev_opp);
if (ret)
goto free_opp;
@@ -889,12 +1189,14 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
/* OPP to select on device suspend */
if (of_property_read_bool(np, "opp-suspend")) {
- if (dev_opp->suspend_opp)
+ if (dev_opp->suspend_opp) {
dev_warn(dev, "%s: Multiple suspend OPPs found (%lu %lu)\n",
__func__, dev_opp->suspend_opp->rate,
new_opp->rate);
- else
+ } else {
+ new_opp->suspend = true;
dev_opp->suspend_opp = new_opp;
+ }
}
if (new_opp->clock_latency_ns > dev_opp->clock_latency_ns_max)
diff --git a/drivers/base/power/opp/cpu.c b/drivers/base/power/opp/cpu.c
index 7b445e88a0d5..9f0c15570f64 100644
--- a/drivers/base/power/opp/cpu.c
+++ b/drivers/base/power/opp/cpu.c
@@ -214,7 +214,6 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table);
/*
* Works only for OPP v2 bindings.
*
- * cpumask should be already set to mask of cpu_dev->id.
* Returns -ENOENT if operating-points-v2 bindings aren't supported.
*/
int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask)
@@ -230,6 +229,8 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask
return -ENOENT;
}
+ cpumask_set_cpu(cpu_dev->id, cpumask);
+
/* OPPs are shared ? */
if (!of_property_read_bool(np, "opp-shared"))
goto put_cpu_node;
diff --git a/drivers/base/power/opp/debugfs.c b/drivers/base/power/opp/debugfs.c
new file mode 100644
index 000000000000..ddfe4773e922
--- /dev/null
+++ b/drivers/base/power/opp/debugfs.c
@@ -0,0 +1,219 @@
+/*
+ * Generic OPP debugfs interface
+ *
+ * Copyright (C) 2015-2016 Viresh Kumar <viresh.kumar@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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/limits.h>
+
+#include "opp.h"
+
+static struct dentry *rootdir;
+
+static void opp_set_dev_name(const struct device *dev, char *name)
+{
+ if (dev->parent)
+ snprintf(name, NAME_MAX, "%s-%s", dev_name(dev->parent),
+ dev_name(dev));
+ else
+ snprintf(name, NAME_MAX, "%s", dev_name(dev));
+}
+
+void opp_debug_remove_one(struct dev_pm_opp *opp)
+{
+ debugfs_remove_recursive(opp->dentry);
+}
+
+int opp_debug_create_one(struct dev_pm_opp *opp, struct device_opp *dev_opp)
+{
+ struct dentry *pdentry = dev_opp->dentry;
+ struct dentry *d;
+ char name[25]; /* 20 chars for 64 bit value + 5 (opp:\0) */
+
+ /* Rate is unique to each OPP, use it to give opp-name */
+ snprintf(name, sizeof(name), "opp:%lu", opp->rate);
+
+ /* Create per-opp directory */
+ d = debugfs_create_dir(name, pdentry);
+ if (!d)
+ return -ENOMEM;
+
+ if (!debugfs_create_bool("available", S_IRUGO, d, &opp->available))
+ return -ENOMEM;
+
+ if (!debugfs_create_bool("dynamic", S_IRUGO, d, &opp->dynamic))
+ return -ENOMEM;
+
+ if (!debugfs_create_bool("turbo", S_IRUGO, d, &opp->turbo))
+ return -ENOMEM;
+
+ if (!debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend))
+ return -ENOMEM;
+
+ if (!debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate))
+ return -ENOMEM;
+
+ if (!debugfs_create_ulong("u_volt_target", S_IRUGO, d, &opp->u_volt))
+ return -ENOMEM;
+
+ if (!debugfs_create_ulong("u_volt_min", S_IRUGO, d, &opp->u_volt_min))
+ return -ENOMEM;
+
+ if (!debugfs_create_ulong("u_volt_max", S_IRUGO, d, &opp->u_volt_max))
+ return -ENOMEM;
+
+ if (!debugfs_create_ulong("u_amp", S_IRUGO, d, &opp->u_amp))
+ return -ENOMEM;
+
+ if (!debugfs_create_ulong("clock_latency_ns", S_IRUGO, d,
+ &opp->clock_latency_ns))
+ return -ENOMEM;
+
+ opp->dentry = d;
+ return 0;
+}
+
+static int device_opp_debug_create_dir(struct device_list_opp *list_dev,
+ struct device_opp *dev_opp)
+{
+ const struct device *dev = list_dev->dev;
+ struct dentry *d;
+
+ opp_set_dev_name(dev, dev_opp->dentry_name);
+
+ /* Create device specific directory */
+ d = debugfs_create_dir(dev_opp->dentry_name, rootdir);
+ if (!d) {
+ dev_err(dev, "%s: Failed to create debugfs dir\n", __func__);
+ return -ENOMEM;
+ }
+
+ list_dev->dentry = d;
+ dev_opp->dentry = d;
+
+ return 0;
+}
+
+static int device_opp_debug_create_link(struct device_list_opp *list_dev,
+ struct device_opp *dev_opp)
+{
+ const struct device *dev = list_dev->dev;
+ char name[NAME_MAX];
+ struct dentry *d;
+
+ opp_set_dev_name(list_dev->dev, name);
+
+ /* Create device specific directory link */
+ d = debugfs_create_symlink(name, rootdir, dev_opp->dentry_name);
+ if (!d) {
+ dev_err(dev, "%s: Failed to create link\n", __func__);
+ return -ENOMEM;
+ }
+
+ list_dev->dentry = d;
+
+ return 0;
+}
+
+/**
+ * opp_debug_register - add a device opp node to the debugfs 'opp' directory
+ * @list_dev: list-dev pointer for device
+ * @dev_opp: the device-opp being added
+ *
+ * Dynamically adds device specific directory in debugfs 'opp' directory. If the
+ * device-opp is shared with other devices, then links will be created for all
+ * devices except the first.
+ *
+ * Return: 0 on success, otherwise negative error.
+ */
+int opp_debug_register(struct device_list_opp *list_dev,
+ struct device_opp *dev_opp)
+{
+ if (!rootdir) {
+ pr_debug("%s: Uninitialized rootdir\n", __func__);
+ return -EINVAL;
+ }
+
+ if (dev_opp->dentry)
+ return device_opp_debug_create_link(list_dev, dev_opp);
+
+ return device_opp_debug_create_dir(list_dev, dev_opp);
+}
+
+static void opp_migrate_dentry(struct device_list_opp *list_dev,
+ struct device_opp *dev_opp)
+{
+ struct device_list_opp *new_dev;
+ const struct device *dev;
+ struct dentry *dentry;
+
+ /* Look for next list-dev */
+ list_for_each_entry(new_dev, &dev_opp->dev_list, node)
+ if (new_dev != list_dev)
+ break;
+
+ /* new_dev is guaranteed to be valid here */
+ dev = new_dev->dev;
+ debugfs_remove_recursive(new_dev->dentry);
+
+ opp_set_dev_name(dev, dev_opp->dentry_name);
+
+ dentry = debugfs_rename(rootdir, list_dev->dentry, rootdir,
+ dev_opp->dentry_name);
+ if (!dentry) {
+ dev_err(dev, "%s: Failed to rename link from: %s to %s\n",
+ __func__, dev_name(list_dev->dev), dev_name(dev));
+ return;
+ }
+
+ new_dev->dentry = dentry;
+ dev_opp->dentry = dentry;
+}
+
+/**
+ * opp_debug_unregister - remove a device opp node from debugfs opp directory
+ * @list_dev: list-dev pointer for device
+ * @dev_opp: the device-opp being removed
+ *
+ * Dynamically removes device specific directory from debugfs 'opp' directory.
+ */
+void opp_debug_unregister(struct device_list_opp *list_dev,
+ struct device_opp *dev_opp)
+{
+ if (list_dev->dentry == dev_opp->dentry) {
+ /* Move the real dentry object under another device */
+ if (!list_is_singular(&dev_opp->dev_list)) {
+ opp_migrate_dentry(list_dev, dev_opp);
+ goto out;
+ }
+ dev_opp->dentry = NULL;
+ }
+
+ debugfs_remove_recursive(list_dev->dentry);
+
+out:
+ list_dev->dentry = NULL;
+}
+
+static int __init opp_debug_init(void)
+{
+ /* Create /sys/kernel/debug/opp directory */
+ rootdir = debugfs_create_dir("opp", NULL);
+ if (!rootdir) {
+ pr_err("%s: Failed to create root directory\n", __func__);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+core_initcall(opp_debug_init);
diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h
index 7366b2aa8997..690638ef36ee 100644
--- a/drivers/base/power/opp/opp.h
+++ b/drivers/base/power/opp/opp.h
@@ -17,6 +17,7 @@
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/limits.h>
#include <linux/pm_opp.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
@@ -50,9 +51,10 @@ extern struct mutex dev_opp_list_lock;
* are protected by the dev_opp_list_lock for integrity.
* IMPORTANT: the opp nodes should be maintained in increasing
* order.
- * @dynamic: not-created from static DT entries.
* @available: true/false - marks if this OPP as available or not
+ * @dynamic: not-created from static DT entries.
* @turbo: true if turbo (boost) OPP
+ * @suspend: true if suspend OPP
* @rate: Frequency in hertz
* @u_volt: Target voltage in microvolts corresponding to this OPP
* @u_volt_min: Minimum voltage in microvolts corresponding to this OPP
@@ -63,6 +65,7 @@ extern struct mutex dev_opp_list_lock;
* @dev_opp: points back to the device_opp struct this opp belongs to
* @rcu_head: RCU callback head used for deferred freeing
* @np: OPP's device node.
+ * @dentry: debugfs dentry pointer (per opp)
*
* This structure stores the OPP information for a given device.
*/
@@ -72,6 +75,7 @@ struct dev_pm_opp {
bool available;
bool dynamic;
bool turbo;
+ bool suspend;
unsigned long rate;
unsigned long u_volt;
@@ -84,6 +88,10 @@ struct dev_pm_opp {
struct rcu_head rcu_head;
struct device_node *np;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dentry;
+#endif
};
/**
@@ -91,6 +99,7 @@ struct dev_pm_opp {
* @node: list node
* @dev: device to which the struct object belongs
* @rcu_head: RCU callback head used for deferred freeing
+ * @dentry: debugfs dentry pointer (per device)
*
* This is an internal data structure maintaining the list of devices that are
* managed by 'struct device_opp'.
@@ -99,6 +108,10 @@ struct device_list_opp {
struct list_head node;
const struct device *dev;
struct rcu_head rcu_head;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dentry;
+#endif
};
/**
@@ -113,7 +126,14 @@ struct device_list_opp {
* @dev_list: list of devices that share these OPPs
* @opp_list: list of opps
* @np: struct device_node pointer for opp's DT node.
+ * @clock_latency_ns_max: Max clock latency in nanoseconds.
* @shared_opp: OPP is shared between multiple devices.
+ * @suspend_opp: Pointer to OPP to be used during device suspend.
+ * @supported_hw: Array of version number to support.
+ * @supported_hw_count: Number of elements in supported_hw array.
+ * @prop_name: A name to postfix to many DT properties, while parsing them.
+ * @dentry: debugfs dentry pointer of the real device directory (not links).
+ * @dentry_name: Name of the real dentry.
*
* This is an internal data structure maintaining the link to opps attached to
* a device. This structure is not meant to be shared to users as it is
@@ -135,6 +155,15 @@ struct device_opp {
unsigned long clock_latency_ns_max;
bool shared_opp;
struct dev_pm_opp *suspend_opp;
+
+ unsigned int *supported_hw;
+ unsigned int supported_hw_count;
+ const char *prop_name;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dentry;
+ char dentry_name[NAME_MAX];
+#endif
};
/* Routines internal to opp core */
@@ -143,4 +172,26 @@ struct device_list_opp *_add_list_dev(const struct device *dev,
struct device_opp *dev_opp);
struct device_node *_of_get_opp_desc_node(struct device *dev);
+#ifdef CONFIG_DEBUG_FS
+void opp_debug_remove_one(struct dev_pm_opp *opp);
+int opp_debug_create_one(struct dev_pm_opp *opp, struct device_opp *dev_opp);
+int opp_debug_register(struct device_list_opp *list_dev,
+ struct device_opp *dev_opp);
+void opp_debug_unregister(struct device_list_opp *list_dev,
+ struct device_opp *dev_opp);
+#else
+static inline void opp_debug_remove_one(struct dev_pm_opp *opp) {}
+
+static inline int opp_debug_create_one(struct dev_pm_opp *opp,
+ struct device_opp *dev_opp)
+{ return 0; }
+static inline int opp_debug_register(struct device_list_opp *list_dev,
+ struct device_opp *dev_opp)
+{ return 0; }
+
+static inline void opp_debug_unregister(struct device_list_opp *list_dev,
+ struct device_opp *dev_opp)
+{ }
+#endif /* DEBUG_FS */
+
#endif /* __DRIVER_OPP_H__ */
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index 998fa6b23084..8b06193d4a5e 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -18,6 +18,7 @@ static inline void pm_runtime_early_init(struct device *dev)
}
extern void pm_runtime_init(struct device *dev);
+extern void pm_runtime_reinit(struct device *dev);
extern void pm_runtime_remove(struct device *dev);
struct wake_irq {
@@ -84,6 +85,7 @@ static inline void pm_runtime_early_init(struct device *dev)
}
static inline void pm_runtime_init(struct device *dev) {}
+static inline void pm_runtime_reinit(struct device *dev) {}
static inline void pm_runtime_remove(struct device *dev) {}
static inline int dpm_sysfs_add(struct device *dev) { return 0; }
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index e1a10a03df8e..4c7055009bd6 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -966,6 +966,30 @@ int __pm_runtime_resume(struct device *dev, int rpmflags)
EXPORT_SYMBOL_GPL(__pm_runtime_resume);
/**
+ * pm_runtime_get_if_in_use - Conditionally bump up the device's usage counter.
+ * @dev: Device to handle.
+ *
+ * Return -EINVAL if runtime PM is disabled for the device.
+ *
+ * If that's not the case and if the device's runtime PM status is RPM_ACTIVE
+ * and the runtime PM usage counter is nonzero, increment the counter and
+ * return 1. Otherwise return 0 without changing the counter.
+ */
+int pm_runtime_get_if_in_use(struct device *dev)
+{
+ unsigned long flags;
+ int retval;
+
+ spin_lock_irqsave(&dev->power.lock, flags);
+ retval = dev->power.disable_depth > 0 ? -EINVAL :
+ dev->power.runtime_status == RPM_ACTIVE
+ && atomic_inc_not_zero(&dev->power.usage_count);
+ spin_unlock_irqrestore(&dev->power.lock, flags);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(pm_runtime_get_if_in_use);
+
+/**
* __pm_runtime_set_status - Set runtime PM status of a device.
* @dev: Device to handle.
* @status: New runtime PM status of the device.
@@ -1390,18 +1414,32 @@ void pm_runtime_init(struct device *dev)
}
/**
+ * pm_runtime_reinit - Re-initialize runtime PM fields in given device object.
+ * @dev: Device object to re-initialize.
+ */
+void pm_runtime_reinit(struct device *dev)
+{
+ if (!pm_runtime_enabled(dev)) {
+ if (dev->power.runtime_status == RPM_ACTIVE)
+ pm_runtime_set_suspended(dev);
+ if (dev->power.irq_safe) {
+ spin_lock_irq(&dev->power.lock);
+ dev->power.irq_safe = 0;
+ spin_unlock_irq(&dev->power.lock);
+ if (dev->parent)
+ pm_runtime_put(dev->parent);
+ }
+ }
+}
+
+/**
* pm_runtime_remove - Prepare for removing a device from device hierarchy.
* @dev: Device object being removed from device hierarchy.
*/
void pm_runtime_remove(struct device *dev)
{
__pm_runtime_disable(dev, false);
-
- /* Change the status back to 'suspended' to match the initial status. */
- if (dev->power.runtime_status == RPM_ACTIVE)
- pm_runtime_set_suspended(dev);
- if (dev->power.irq_safe && dev->parent)
- pm_runtime_put(dev->parent);
+ pm_runtime_reinit(dev);
}
/**
diff --git a/drivers/base/property.c b/drivers/base/property.c
index 1325ff225cc4..c359351d50f1 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -19,32 +19,14 @@
#include <linux/etherdevice.h>
#include <linux/phy.h>
-/**
- * device_add_property_set - Add a collection of properties to a device object.
- * @dev: Device to add properties to.
- * @pset: Collection of properties to add.
- *
- * Associate a collection of device properties represented by @pset with @dev
- * as its secondary firmware node.
- */
-void device_add_property_set(struct device *dev, struct property_set *pset)
-{
- if (!pset)
- return;
-
- pset->fwnode.type = FWNODE_PDATA;
- set_secondary_fwnode(dev, &pset->fwnode);
-}
-EXPORT_SYMBOL_GPL(device_add_property_set);
-
-static inline bool is_pset(struct fwnode_handle *fwnode)
+static inline bool is_pset_node(struct fwnode_handle *fwnode)
{
return fwnode && fwnode->type == FWNODE_PDATA;
}
-static inline struct property_set *to_pset(struct fwnode_handle *fwnode)
+static inline struct property_set *to_pset_node(struct fwnode_handle *fwnode)
{
- return is_pset(fwnode) ?
+ return is_pset_node(fwnode) ?
container_of(fwnode, struct property_set, fwnode) : NULL;
}
@@ -63,45 +45,135 @@ static struct property_entry *pset_prop_get(struct property_set *pset,
return NULL;
}
-static int pset_prop_read_array(struct property_set *pset, const char *name,
- enum dev_prop_type type, void *val, size_t nval)
+static void *pset_prop_find(struct property_set *pset, const char *propname,
+ size_t length)
{
struct property_entry *prop;
- unsigned int item_size;
+ void *pointer;
- prop = pset_prop_get(pset, name);
+ prop = pset_prop_get(pset, propname);
if (!prop)
- return -ENODATA;
+ return ERR_PTR(-EINVAL);
+ if (prop->is_array)
+ pointer = prop->pointer.raw_data;
+ else
+ pointer = &prop->value.raw_data;
+ if (!pointer)
+ return ERR_PTR(-ENODATA);
+ if (length > prop->length)
+ return ERR_PTR(-EOVERFLOW);
+ return pointer;
+}
+
+static int pset_prop_read_u8_array(struct property_set *pset,
+ const char *propname,
+ u8 *values, size_t nval)
+{
+ void *pointer;
+ size_t length = nval * sizeof(*values);
+
+ pointer = pset_prop_find(pset, propname, length);
+ if (IS_ERR(pointer))
+ return PTR_ERR(pointer);
+
+ memcpy(values, pointer, length);
+ return 0;
+}
+
+static int pset_prop_read_u16_array(struct property_set *pset,
+ const char *propname,
+ u16 *values, size_t nval)
+{
+ void *pointer;
+ size_t length = nval * sizeof(*values);
+
+ pointer = pset_prop_find(pset, propname, length);
+ if (IS_ERR(pointer))
+ return PTR_ERR(pointer);
+
+ memcpy(values, pointer, length);
+ return 0;
+}
+
+static int pset_prop_read_u32_array(struct property_set *pset,
+ const char *propname,
+ u32 *values, size_t nval)
+{
+ void *pointer;
+ size_t length = nval * sizeof(*values);
+
+ pointer = pset_prop_find(pset, propname, length);
+ if (IS_ERR(pointer))
+ return PTR_ERR(pointer);
+
+ memcpy(values, pointer, length);
+ return 0;
+}
+
+static int pset_prop_read_u64_array(struct property_set *pset,
+ const char *propname,
+ u64 *values, size_t nval)
+{
+ void *pointer;
+ size_t length = nval * sizeof(*values);
+
+ pointer = pset_prop_find(pset, propname, length);
+ if (IS_ERR(pointer))
+ return PTR_ERR(pointer);
+
+ memcpy(values, pointer, length);
+ return 0;
+}
+
+static int pset_prop_count_elems_of_size(struct property_set *pset,
+ const char *propname, size_t length)
+{
+ struct property_entry *prop;
+
+ prop = pset_prop_get(pset, propname);
+ if (!prop)
+ return -EINVAL;
+
+ return prop->length / length;
+}
+
+static int pset_prop_read_string_array(struct property_set *pset,
+ const char *propname,
+ const char **strings, size_t nval)
+{
+ void *pointer;
+ size_t length = nval * sizeof(*strings);
+
+ pointer = pset_prop_find(pset, propname, length);
+ if (IS_ERR(pointer))
+ return PTR_ERR(pointer);
+
+ memcpy(strings, pointer, length);
+ return 0;
+}
+
+static int pset_prop_read_string(struct property_set *pset,
+ const char *propname, const char **strings)
+{
+ struct property_entry *prop;
+ const char **pointer;
- if (prop->type != type)
- return -EPROTO;
-
- if (!val)
- return prop->nval;
-
- if (prop->nval < nval)
- return -EOVERFLOW;
-
- switch (type) {
- case DEV_PROP_U8:
- item_size = sizeof(u8);
- break;
- case DEV_PROP_U16:
- item_size = sizeof(u16);
- break;
- case DEV_PROP_U32:
- item_size = sizeof(u32);
- break;
- case DEV_PROP_U64:
- item_size = sizeof(u64);
- break;
- case DEV_PROP_STRING:
- item_size = sizeof(const char *);
- break;
- default:
+ prop = pset_prop_get(pset, propname);
+ if (!prop)
return -EINVAL;
+ if (!prop->is_string)
+ return -EILSEQ;
+ if (prop->is_array) {
+ pointer = prop->pointer.str;
+ if (!pointer)
+ return -ENODATA;
+ } else {
+ pointer = &prop->value.str;
+ if (*pointer && strnlen(*pointer, prop->length) >= prop->length)
+ return -EILSEQ;
}
- memcpy(val, prop->value.raw_data, nval * item_size);
+
+ *strings = *pointer;
return 0;
}
@@ -124,6 +196,18 @@ bool device_property_present(struct device *dev, const char *propname)
}
EXPORT_SYMBOL_GPL(device_property_present);
+static bool __fwnode_property_present(struct fwnode_handle *fwnode,
+ const char *propname)
+{
+ if (is_of_node(fwnode))
+ return of_property_read_bool(to_of_node(fwnode), propname);
+ else if (is_acpi_node(fwnode))
+ return !acpi_node_prop_get(fwnode, propname, NULL);
+ else if (is_pset_node(fwnode))
+ return !!pset_prop_get(to_pset_node(fwnode), propname);
+ return false;
+}
+
/**
* fwnode_property_present - check if a property of a firmware node is present
* @fwnode: Firmware node whose property to check
@@ -131,12 +215,12 @@ EXPORT_SYMBOL_GPL(device_property_present);
*/
bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname)
{
- if (is_of_node(fwnode))
- return of_property_read_bool(to_of_node(fwnode), propname);
- else if (is_acpi_node(fwnode))
- return !acpi_node_prop_get(fwnode, propname, NULL);
+ bool ret;
- return !!pset_prop_get(to_pset(fwnode), propname);
+ ret = __fwnode_property_present(fwnode, propname);
+ if (ret == false && fwnode && fwnode->secondary)
+ ret = __fwnode_property_present(fwnode->secondary, propname);
+ return ret;
}
EXPORT_SYMBOL_GPL(fwnode_property_present);
@@ -309,25 +393,40 @@ int device_property_match_string(struct device *dev, const char *propname,
}
EXPORT_SYMBOL_GPL(device_property_match_string);
-#define OF_DEV_PROP_READ_ARRAY(node, propname, type, val, nval) \
- (val) ? of_property_read_##type##_array((node), (propname), (val), (nval)) \
+#define OF_DEV_PROP_READ_ARRAY(node, propname, type, val, nval) \
+ (val) ? of_property_read_##type##_array((node), (propname), (val), (nval)) \
: of_property_count_elems_of_size((node), (propname), sizeof(type))
-#define FWNODE_PROP_READ_ARRAY(_fwnode_, _propname_, _type_, _proptype_, _val_, _nval_) \
-({ \
- int _ret_; \
- if (is_of_node(_fwnode_)) \
- _ret_ = OF_DEV_PROP_READ_ARRAY(to_of_node(_fwnode_), _propname_, \
- _type_, _val_, _nval_); \
- else if (is_acpi_node(_fwnode_)) \
- _ret_ = acpi_node_prop_read(_fwnode_, _propname_, _proptype_, \
- _val_, _nval_); \
- else if (is_pset(_fwnode_)) \
- _ret_ = pset_prop_read_array(to_pset(_fwnode_), _propname_, \
- _proptype_, _val_, _nval_); \
- else \
- _ret_ = -ENXIO; \
- _ret_; \
+#define PSET_PROP_READ_ARRAY(node, propname, type, val, nval) \
+ (val) ? pset_prop_read_##type##_array((node), (propname), (val), (nval)) \
+ : pset_prop_count_elems_of_size((node), (propname), sizeof(type))
+
+#define FWNODE_PROP_READ(_fwnode_, _propname_, _type_, _proptype_, _val_, _nval_) \
+({ \
+ int _ret_; \
+ if (is_of_node(_fwnode_)) \
+ _ret_ = OF_DEV_PROP_READ_ARRAY(to_of_node(_fwnode_), _propname_, \
+ _type_, _val_, _nval_); \
+ else if (is_acpi_node(_fwnode_)) \
+ _ret_ = acpi_node_prop_read(_fwnode_, _propname_, _proptype_, \
+ _val_, _nval_); \
+ else if (is_pset_node(_fwnode_)) \
+ _ret_ = PSET_PROP_READ_ARRAY(to_pset_node(_fwnode_), _propname_, \
+ _type_, _val_, _nval_); \
+ else \
+ _ret_ = -ENXIO; \
+ _ret_; \
+})
+
+#define FWNODE_PROP_READ_ARRAY(_fwnode_, _propname_, _type_, _proptype_, _val_, _nval_) \
+({ \
+ int _ret_; \
+ _ret_ = FWNODE_PROP_READ(_fwnode_, _propname_, _type_, _proptype_, \
+ _val_, _nval_); \
+ if (_ret_ == -EINVAL && _fwnode_ && _fwnode_->secondary) \
+ _ret_ = FWNODE_PROP_READ(_fwnode_->secondary, _propname_, _type_, \
+ _proptype_, _val_, _nval_); \
+ _ret_; \
})
/**
@@ -434,6 +533,41 @@ int fwnode_property_read_u64_array(struct fwnode_handle *fwnode,
}
EXPORT_SYMBOL_GPL(fwnode_property_read_u64_array);
+static int __fwnode_property_read_string_array(struct fwnode_handle *fwnode,
+ const char *propname,
+ const char **val, size_t nval)
+{
+ if (is_of_node(fwnode))
+ return val ?
+ of_property_read_string_array(to_of_node(fwnode),
+ propname, val, nval) :
+ of_property_count_strings(to_of_node(fwnode), propname);
+ else if (is_acpi_node(fwnode))
+ return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING,
+ val, nval);
+ else if (is_pset_node(fwnode))
+ return val ?
+ pset_prop_read_string_array(to_pset_node(fwnode),
+ propname, val, nval) :
+ pset_prop_count_elems_of_size(to_pset_node(fwnode),
+ propname,
+ sizeof(const char *));
+ return -ENXIO;
+}
+
+static int __fwnode_property_read_string(struct fwnode_handle *fwnode,
+ const char *propname, const char **val)
+{
+ if (is_of_node(fwnode))
+ return of_property_read_string(to_of_node(fwnode), propname, val);
+ else if (is_acpi_node(fwnode))
+ return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING,
+ val, 1);
+ else if (is_pset_node(fwnode))
+ return pset_prop_read_string(to_pset_node(fwnode), propname, val);
+ return -ENXIO;
+}
+
/**
* fwnode_property_read_string_array - return string array property of a node
* @fwnode: Firmware node to get the property of
@@ -456,18 +590,13 @@ int fwnode_property_read_string_array(struct fwnode_handle *fwnode,
const char *propname, const char **val,
size_t nval)
{
- if (is_of_node(fwnode))
- return val ?
- of_property_read_string_array(to_of_node(fwnode),
- propname, val, nval) :
- of_property_count_strings(to_of_node(fwnode), propname);
- else if (is_acpi_node(fwnode))
- return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING,
- val, nval);
- else if (is_pset(fwnode))
- return pset_prop_read_array(to_pset(fwnode), propname,
- DEV_PROP_STRING, val, nval);
- return -ENXIO;
+ int ret;
+
+ ret = __fwnode_property_read_string_array(fwnode, propname, val, nval);
+ if (ret == -EINVAL && fwnode && fwnode->secondary)
+ ret = __fwnode_property_read_string_array(fwnode->secondary,
+ propname, val, nval);
+ return ret;
}
EXPORT_SYMBOL_GPL(fwnode_property_read_string_array);
@@ -489,14 +618,13 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_string_array);
int fwnode_property_read_string(struct fwnode_handle *fwnode,
const char *propname, const char **val)
{
- if (is_of_node(fwnode))
- return of_property_read_string(to_of_node(fwnode), propname, val);
- else if (is_acpi_node(fwnode))
- return acpi_node_prop_read(fwnode, propname, DEV_PROP_STRING,
- val, 1);
+ int ret;
- return pset_prop_read_array(to_pset(fwnode), propname,
- DEV_PROP_STRING, val, 1);
+ ret = __fwnode_property_read_string(fwnode, propname, val);
+ if (ret == -EINVAL && fwnode && fwnode->secondary)
+ ret = __fwnode_property_read_string(fwnode->secondary,
+ propname, val);
+ return ret;
}
EXPORT_SYMBOL_GPL(fwnode_property_read_string);
@@ -525,6 +653,9 @@ int fwnode_property_match_string(struct fwnode_handle *fwnode,
if (nval < 0)
return nval;
+ if (nval == 0)
+ return -ENODATA;
+
values = kcalloc(nval, sizeof(*values), GFP_KERNEL);
if (!values)
return -ENOMEM;
@@ -547,6 +678,182 @@ out:
EXPORT_SYMBOL_GPL(fwnode_property_match_string);
/**
+ * pset_free_set - releases memory allocated for copied property set
+ * @pset: Property set to release
+ *
+ * Function takes previously copied property set and releases all the
+ * memory allocated to it.
+ */
+static void pset_free_set(struct property_set *pset)
+{
+ const struct property_entry *prop;
+ size_t i, nval;
+
+ if (!pset)
+ return;
+
+ for (prop = pset->properties; prop->name; prop++) {
+ if (prop->is_array) {
+ if (prop->is_string && prop->pointer.str) {
+ nval = prop->length / sizeof(const char *);
+ for (i = 0; i < nval; i++)
+ kfree(prop->pointer.str[i]);
+ }
+ kfree(prop->pointer.raw_data);
+ } else if (prop->is_string) {
+ kfree(prop->value.str);
+ }
+ kfree(prop->name);
+ }
+
+ kfree(pset->properties);
+ kfree(pset);
+}
+
+static int pset_copy_entry(struct property_entry *dst,
+ const struct property_entry *src)
+{
+ const char **d, **s;
+ size_t i, nval;
+
+ dst->name = kstrdup(src->name, GFP_KERNEL);
+ if (!dst->name)
+ return -ENOMEM;
+
+ if (src->is_array) {
+ if (!src->length)
+ return -ENODATA;
+
+ if (src->is_string) {
+ nval = src->length / sizeof(const char *);
+ dst->pointer.str = kcalloc(nval, sizeof(const char *),
+ GFP_KERNEL);
+ if (!dst->pointer.str)
+ return -ENOMEM;
+
+ d = dst->pointer.str;
+ s = src->pointer.str;
+ for (i = 0; i < nval; i++) {
+ d[i] = kstrdup(s[i], GFP_KERNEL);
+ if (!d[i] && s[i])
+ return -ENOMEM;
+ }
+ } else {
+ dst->pointer.raw_data = kmemdup(src->pointer.raw_data,
+ src->length, GFP_KERNEL);
+ if (!dst->pointer.raw_data)
+ return -ENOMEM;
+ }
+ } else if (src->is_string) {
+ dst->value.str = kstrdup(src->value.str, GFP_KERNEL);
+ if (!dst->value.str && src->value.str)
+ return -ENOMEM;
+ } else {
+ dst->value.raw_data = src->value.raw_data;
+ }
+
+ dst->length = src->length;
+ dst->is_array = src->is_array;
+ dst->is_string = src->is_string;
+
+ return 0;
+}
+
+/**
+ * pset_copy_set - copies property set
+ * @pset: Property set to copy
+ *
+ * This function takes a deep copy of the given property set and returns
+ * pointer to the copy. Call device_free_property_set() to free resources
+ * allocated in this function.
+ *
+ * Return: Pointer to the new property set or error pointer.
+ */
+static struct property_set *pset_copy_set(const struct property_set *pset)
+{
+ const struct property_entry *entry;
+ struct property_set *p;
+ size_t i, n = 0;
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return ERR_PTR(-ENOMEM);
+
+ while (pset->properties[n].name)
+ n++;
+
+ p->properties = kcalloc(n + 1, sizeof(*entry), GFP_KERNEL);
+ if (!p->properties) {
+ kfree(p);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ for (i = 0; i < n; i++) {
+ int ret = pset_copy_entry(&p->properties[i],
+ &pset->properties[i]);
+ if (ret) {
+ pset_free_set(p);
+ return ERR_PTR(ret);
+ }
+ }
+
+ return p;
+}
+
+/**
+ * device_remove_property_set - Remove properties from a device object.
+ * @dev: Device whose properties to remove.
+ *
+ * The function removes properties previously associated to the device
+ * secondary firmware node with device_add_property_set(). Memory allocated
+ * to the properties will also be released.
+ */
+void device_remove_property_set(struct device *dev)
+{
+ struct fwnode_handle *fwnode;
+
+ fwnode = dev_fwnode(dev);
+ if (!fwnode)
+ return;
+ /*
+ * Pick either primary or secondary node depending which one holds
+ * the pset. If there is no real firmware node (ACPI/DT) primary
+ * will hold the pset.
+ */
+ if (!is_pset_node(fwnode))
+ fwnode = fwnode->secondary;
+ if (!IS_ERR(fwnode) && is_pset_node(fwnode))
+ pset_free_set(to_pset_node(fwnode));
+ set_secondary_fwnode(dev, NULL);
+}
+EXPORT_SYMBOL_GPL(device_remove_property_set);
+
+/**
+ * device_add_property_set - Add a collection of properties to a device object.
+ * @dev: Device to add properties to.
+ * @pset: Collection of properties to add.
+ *
+ * Associate a collection of device properties represented by @pset with @dev
+ * as its secondary firmware node. The function takes a copy of @pset.
+ */
+int device_add_property_set(struct device *dev, const struct property_set *pset)
+{
+ struct property_set *p;
+
+ if (!pset)
+ return -EINVAL;
+
+ p = pset_copy_set(pset);
+ if (IS_ERR(p))
+ return PTR_ERR(p);
+
+ p->fwnode.type = FWNODE_PDATA;
+ set_secondary_fwnode(dev, &p->fwnode);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(device_add_property_set);
+
+/**
* device_get_next_child_node - Return the next child node handle for a device
* @dev: Device to find the next child node for.
* @child: Handle to one of the device's child nodes or a null handle.
diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c
index 0246f44ded74..686c9e0b930e 100644
--- a/drivers/base/regmap/regcache-flat.c
+++ b/drivers/base/regmap/regcache-flat.c
@@ -21,7 +21,7 @@ static int regcache_flat_init(struct regmap *map)
int i;
unsigned int *cache;
- map->cache = kzalloc(sizeof(unsigned int) * (map->max_register + 1),
+ map->cache = kcalloc(map->max_register + 1, sizeof(unsigned int),
GFP_KERNEL);
if (!map->cache)
return -ENOMEM;
diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c
index 736e0d378567..6f77d7319fc6 100644
--- a/drivers/base/regmap/regcache-lzo.c
+++ b/drivers/base/regmap/regcache-lzo.c
@@ -139,7 +139,7 @@ static int regcache_lzo_init(struct regmap *map)
ret = 0;
blkcount = regcache_lzo_block_count(map);
- map->cache = kzalloc(blkcount * sizeof *lzo_blocks,
+ map->cache = kcalloc(blkcount, sizeof(*lzo_blocks),
GFP_KERNEL);
if (!map->cache)
return -ENOMEM;
@@ -152,8 +152,8 @@ static int regcache_lzo_init(struct regmap *map)
* that register.
*/
bmp_size = map->num_reg_defaults_raw;
- sync_bmp = kmalloc(BITS_TO_LONGS(bmp_size) * sizeof(long),
- GFP_KERNEL);
+ sync_bmp = kmalloc_array(BITS_TO_LONGS(bmp_size), sizeof(long),
+ GFP_KERNEL);
if (!sync_bmp) {
ret = -ENOMEM;
goto err;
diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c
index 56486d92c4e7..aa56af87d941 100644
--- a/drivers/base/regmap/regcache-rbtree.c
+++ b/drivers/base/regmap/regcache-rbtree.c
@@ -361,13 +361,14 @@ regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg)
rbnode->base_reg = reg;
}
- rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size,
- GFP_KERNEL);
+ rbnode->block = kmalloc_array(rbnode->blklen, map->cache_word_size,
+ GFP_KERNEL);
if (!rbnode->block)
goto err_free;
- rbnode->cache_present = kzalloc(BITS_TO_LONGS(rbnode->blklen) *
- sizeof(*rbnode->cache_present), GFP_KERNEL);
+ rbnode->cache_present = kcalloc(BITS_TO_LONGS(rbnode->blklen),
+ sizeof(*rbnode->cache_present),
+ GFP_KERNEL);
if (!rbnode->cache_present)
goto err_free_block;
@@ -413,8 +414,8 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
max = reg + max_dist;
/* look for an adjacent register to the one we are about to add */
- for (node = rb_first(&rbtree_ctx->root); node;
- node = rb_next(node)) {
+ node = rbtree_ctx->root.rb_node;
+ while (node) {
rbnode_tmp = rb_entry(node, struct regcache_rbtree_node,
node);
@@ -425,6 +426,11 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
new_base_reg = min(reg, base_reg);
new_top_reg = max(reg, top_reg);
} else {
+ if (max < base_reg)
+ node = node->rb_left;
+ else
+ node = node->rb_right;
+
continue;
}
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index 4c07802986b2..348be3a35410 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -100,15 +100,25 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
int i;
void *tmp_buf;
- for (i = 0; i < config->num_reg_defaults; i++)
- if (config->reg_defaults[i].reg % map->reg_stride)
- return -EINVAL;
-
if (map->cache_type == REGCACHE_NONE) {
+ if (config->reg_defaults || config->num_reg_defaults_raw)
+ dev_warn(map->dev,
+ "No cache used with register defaults set!\n");
+
map->cache_bypass = true;
return 0;
}
+ if (config->reg_defaults && !config->num_reg_defaults) {
+ dev_err(map->dev,
+ "Register defaults are set without the number!\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < config->num_reg_defaults; i++)
+ if (config->reg_defaults[i].reg % map->reg_stride)
+ return -EINVAL;
+
for (i = 0; i < ARRAY_SIZE(cache_types); i++)
if (cache_types[i]->type == map->cache_type)
break;
@@ -138,8 +148,6 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
* a copy of it.
*/
if (config->reg_defaults) {
- if (!map->num_reg_defaults)
- return -EINVAL;
tmp_buf = kmemdup(config->reg_defaults, map->num_reg_defaults *
sizeof(struct reg_default), GFP_KERNEL);
if (!tmp_buf)
@@ -535,19 +543,30 @@ bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
switch (map->cache_word_size) {
case 1: {
u8 *cache = base;
+
cache[idx] = val;
break;
}
case 2: {
u16 *cache = base;
+
cache[idx] = val;
break;
}
case 4: {
u32 *cache = base;
+
+ cache[idx] = val;
+ break;
+ }
+#ifdef CONFIG_64BIT
+ case 8: {
+ u64 *cache = base;
+
cache[idx] = val;
break;
}
+#endif
default:
BUG();
}
@@ -568,16 +587,26 @@ unsigned int regcache_get_val(struct regmap *map, const void *base,
switch (map->cache_word_size) {
case 1: {
const u8 *cache = base;
+
return cache[idx];
}
case 2: {
const u16 *cache = base;
+
return cache[idx];
}
case 4: {
const u32 *cache = base;
+
+ return cache[idx];
+ }
+#ifdef CONFIG_64BIT
+ case 8: {
+ const u64 *cache = base;
+
return cache[idx];
}
+#endif
default:
BUG();
}
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c
index 3f0a7e262d69..1ee3d40861c7 100644
--- a/drivers/base/regmap/regmap-debugfs.c
+++ b/drivers/base/regmap/regmap-debugfs.c
@@ -397,72 +397,39 @@ static const struct file_operations regmap_reg_ranges_fops = {
.llseek = default_llseek,
};
-static ssize_t regmap_access_read_file(struct file *file,
- char __user *user_buf, size_t count,
- loff_t *ppos)
+static int regmap_access_show(struct seq_file *s, void *ignored)
{
- int reg_len, tot_len;
- size_t buf_pos = 0;
- loff_t p = 0;
- ssize_t ret;
- int i;
- struct regmap *map = file->private_data;
- char *buf;
-
- if (*ppos < 0 || !count)
- return -EINVAL;
+ struct regmap *map = s->private;
+ int i, reg_len;
- buf = kmalloc(count, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- /* Calculate the length of a fixed format */
reg_len = regmap_calc_reg_len(map->max_register);
- tot_len = reg_len + 10; /* ': R W V P\n' */
for (i = 0; i <= map->max_register; i += map->reg_stride) {
/* Ignore registers which are neither readable nor writable */
if (!regmap_readable(map, i) && !regmap_writeable(map, i))
continue;
- /* If we're in the region the user is trying to read */
- if (p >= *ppos) {
- /* ...but not beyond it */
- if (buf_pos + tot_len + 1 >= count)
- break;
-
- /* Format the register */
- snprintf(buf + buf_pos, count - buf_pos,
- "%.*x: %c %c %c %c\n",
- reg_len, i,
- regmap_readable(map, i) ? 'y' : 'n',
- regmap_writeable(map, i) ? 'y' : 'n',
- regmap_volatile(map, i) ? 'y' : 'n',
- regmap_precious(map, i) ? 'y' : 'n');
-
- buf_pos += tot_len;
- }
- p += tot_len;
- }
-
- ret = buf_pos;
-
- if (copy_to_user(user_buf, buf, buf_pos)) {
- ret = -EFAULT;
- goto out;
+ /* Format the register */
+ seq_printf(s, "%.*x: %c %c %c %c\n", reg_len, i,
+ regmap_readable(map, i) ? 'y' : 'n',
+ regmap_writeable(map, i) ? 'y' : 'n',
+ regmap_volatile(map, i) ? 'y' : 'n',
+ regmap_precious(map, i) ? 'y' : 'n');
}
- *ppos += buf_pos;
+ return 0;
+}
-out:
- kfree(buf);
- return ret;
+static int access_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, regmap_access_show, inode->i_private);
}
static const struct file_operations regmap_access_fops = {
- .open = simple_open,
- .read = regmap_access_read_file,
- .llseek = default_llseek,
+ .open = access_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
};
static ssize_t regmap_cache_only_write_file(struct file *file,
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 8d16db533527..9b0d202414d0 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -39,8 +39,11 @@ struct regmap_irq_chip_data {
unsigned int *mask_buf;
unsigned int *mask_buf_def;
unsigned int *wake_buf;
+ unsigned int *type_buf;
+ unsigned int *type_buf_def;
unsigned int irq_reg_stride;
+ unsigned int type_reg_stride;
};
static inline const
@@ -144,6 +147,22 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
}
}
+ for (i = 0; i < d->chip->num_type_reg; i++) {
+ if (!d->type_buf_def[i])
+ continue;
+ reg = d->chip->type_base +
+ (i * map->reg_stride * d->type_reg_stride);
+ if (d->chip->type_invert)
+ ret = regmap_update_bits(d->map, reg,
+ d->type_buf_def[i], ~d->type_buf[i]);
+ else
+ ret = regmap_update_bits(d->map, reg,
+ d->type_buf_def[i], d->type_buf[i]);
+ if (ret != 0)
+ dev_err(d->map->dev, "Failed to sync type in %x\n",
+ reg);
+ }
+
if (d->chip->runtime_pm)
pm_runtime_put(map->dev);
@@ -178,6 +197,38 @@ static void regmap_irq_disable(struct irq_data *data)
d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask;
}
+static int regmap_irq_set_type(struct irq_data *data, unsigned int type)
+{
+ struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
+ struct regmap *map = d->map;
+ const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
+ int reg = irq_data->type_reg_offset / map->reg_stride;
+
+ if (!(irq_data->type_rising_mask | irq_data->type_falling_mask))
+ return 0;
+
+ d->type_buf[reg] &= ~(irq_data->type_falling_mask |
+ irq_data->type_rising_mask);
+ switch (type) {
+ case IRQ_TYPE_EDGE_FALLING:
+ d->type_buf[reg] |= irq_data->type_falling_mask;
+ break;
+
+ case IRQ_TYPE_EDGE_RISING:
+ d->type_buf[reg] |= irq_data->type_rising_mask;
+ break;
+
+ case IRQ_TYPE_EDGE_BOTH:
+ d->type_buf[reg] |= (irq_data->type_falling_mask |
+ irq_data->type_rising_mask);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
static int regmap_irq_set_wake(struct irq_data *data, unsigned int on)
{
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
@@ -204,6 +255,7 @@ static const struct irq_chip regmap_irq_chip = {
.irq_bus_sync_unlock = regmap_irq_sync_unlock,
.irq_disable = regmap_irq_disable,
.irq_enable = regmap_irq_enable,
+ .irq_set_type = regmap_irq_set_type,
.irq_set_wake = regmap_irq_set_wake,
};
@@ -386,28 +438,40 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
if (!d)
return -ENOMEM;
- d->status_buf = kzalloc(sizeof(unsigned int) * chip->num_regs,
+ d->status_buf = kcalloc(chip->num_regs, sizeof(unsigned int),
GFP_KERNEL);
if (!d->status_buf)
goto err_alloc;
- d->mask_buf = kzalloc(sizeof(unsigned int) * chip->num_regs,
+ d->mask_buf = kcalloc(chip->num_regs, sizeof(unsigned int),
GFP_KERNEL);
if (!d->mask_buf)
goto err_alloc;
- d->mask_buf_def = kzalloc(sizeof(unsigned int) * chip->num_regs,
+ d->mask_buf_def = kcalloc(chip->num_regs, sizeof(unsigned int),
GFP_KERNEL);
if (!d->mask_buf_def)
goto err_alloc;
if (chip->wake_base) {
- d->wake_buf = kzalloc(sizeof(unsigned int) * chip->num_regs,
+ d->wake_buf = kcalloc(chip->num_regs, sizeof(unsigned int),
GFP_KERNEL);
if (!d->wake_buf)
goto err_alloc;
}
+ if (chip->num_type_reg) {
+ d->type_buf_def = kcalloc(chip->num_type_reg,
+ sizeof(unsigned int), GFP_KERNEL);
+ if (!d->type_buf_def)
+ goto err_alloc;
+
+ d->type_buf = kcalloc(chip->num_type_reg, sizeof(unsigned int),
+ GFP_KERNEL);
+ if (!d->type_buf)
+ goto err_alloc;
+ }
+
d->irq_chip = regmap_irq_chip;
d->irq_chip.name = chip->name;
d->irq = irq;
@@ -420,10 +484,16 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
else
d->irq_reg_stride = 1;
+ if (chip->type_reg_stride)
+ d->type_reg_stride = chip->type_reg_stride;
+ else
+ d->type_reg_stride = 1;
+
if (!map->use_single_read && map->reg_stride == 1 &&
d->irq_reg_stride == 1) {
- d->status_reg_buf = kmalloc(map->format.val_bytes *
- chip->num_regs, GFP_KERNEL);
+ d->status_reg_buf = kmalloc_array(chip->num_regs,
+ map->format.val_bytes,
+ GFP_KERNEL);
if (!d->status_reg_buf)
goto err_alloc;
}
@@ -511,6 +581,33 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
}
}
+ if (chip->num_type_reg) {
+ for (i = 0; i < chip->num_irqs; i++) {
+ reg = chip->irqs[i].type_reg_offset / map->reg_stride;
+ d->type_buf_def[reg] |= chip->irqs[i].type_rising_mask |
+ chip->irqs[i].type_falling_mask;
+ }
+ for (i = 0; i < chip->num_type_reg; ++i) {
+ if (!d->type_buf_def[i])
+ continue;
+
+ reg = chip->type_base +
+ (i * map->reg_stride * d->type_reg_stride);
+ if (chip->type_invert)
+ ret = regmap_update_bits(map, reg,
+ d->type_buf_def[i], 0xFF);
+ else
+ ret = regmap_update_bits(map, reg,
+ d->type_buf_def[i], 0x0);
+ if (ret != 0) {
+ dev_err(map->dev,
+ "Failed to set type in 0x%x: %x\n",
+ reg, ret);
+ goto err_alloc;
+ }
+ }
+ }
+
if (irq_base)
d->domain = irq_domain_add_legacy(map->dev->of_node,
chip->num_irqs, irq_base, 0,
@@ -541,6 +638,8 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
err_domain:
/* Should really dispose of the domain but... */
err_alloc:
+ kfree(d->type_buf);
+ kfree(d->type_buf_def);
kfree(d->wake_buf);
kfree(d->mask_buf_def);
kfree(d->mask_buf);
@@ -564,6 +663,8 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
free_irq(irq, d);
irq_domain_remove(d->domain);
+ kfree(d->type_buf);
+ kfree(d->type_buf_def);
kfree(d->wake_buf);
kfree(d->mask_buf_def);
kfree(d->mask_buf);
diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c
index 426a57e41ac7..8812bfb9e3b8 100644
--- a/drivers/base/regmap/regmap-mmio.c
+++ b/drivers/base/regmap/regmap-mmio.c
@@ -61,6 +61,33 @@ static int regmap_mmio_regbits_check(size_t reg_bits)
}
}
+static int regmap_mmio_get_min_stride(size_t val_bits)
+{
+ int min_stride;
+
+ switch (val_bits) {
+ case 8:
+ /* The core treats 0 as 1 */
+ min_stride = 0;
+ return 0;
+ case 16:
+ min_stride = 2;
+ break;
+ case 32:
+ min_stride = 4;
+ break;
+#ifdef CONFIG_64BIT
+ case 64:
+ min_stride = 8;
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+ return min_stride;
+}
+
static inline void regmap_mmio_count_check(size_t count, u32 offset)
{
BUG_ON(count <= offset);
@@ -106,17 +133,17 @@ static int regmap_mmio_gather_write(void *context,
while (val_size) {
switch (ctx->val_bytes) {
case 1:
- writeb(*(u8 *)val, ctx->regs + offset);
+ __raw_writeb(*(u8 *)val, ctx->regs + offset);
break;
case 2:
- writew(*(u16 *)val, ctx->regs + offset);
+ __raw_writew(*(u16 *)val, ctx->regs + offset);
break;
case 4:
- writel(*(u32 *)val, ctx->regs + offset);
+ __raw_writel(*(u32 *)val, ctx->regs + offset);
break;
#ifdef CONFIG_64BIT
case 8:
- writeq(*(u64 *)val, ctx->regs + offset);
+ __raw_writeq(*(u64 *)val, ctx->regs + offset);
break;
#endif
default:
@@ -166,17 +193,17 @@ static int regmap_mmio_read(void *context,
while (val_size) {
switch (ctx->val_bytes) {
case 1:
- *(u8 *)val = readb(ctx->regs + offset);
+ *(u8 *)val = __raw_readb(ctx->regs + offset);
break;
case 2:
- *(u16 *)val = readw(ctx->regs + offset);
+ *(u16 *)val = __raw_readw(ctx->regs + offset);
break;
case 4:
- *(u32 *)val = readl(ctx->regs + offset);
+ *(u32 *)val = __raw_readl(ctx->regs + offset);
break;
#ifdef CONFIG_64BIT
case 8:
- *(u64 *)val = readq(ctx->regs + offset);
+ *(u64 *)val = __raw_readq(ctx->regs + offset);
break;
#endif
default:
@@ -231,26 +258,9 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
if (config->pad_bits)
return ERR_PTR(-EINVAL);
- switch (config->val_bits) {
- case 8:
- /* The core treats 0 as 1 */
- min_stride = 0;
- break;
- case 16:
- min_stride = 2;
- break;
- case 32:
- min_stride = 4;
- break;
-#ifdef CONFIG_64BIT
- case 64:
- min_stride = 8;
- break;
-#endif
- break;
- default:
- return ERR_PTR(-EINVAL);
- }
+ min_stride = regmap_mmio_get_min_stride(config->val_bits);
+ if (min_stride < 0)
+ return ERR_PTR(min_stride);
if (config->reg_stride < min_stride)
return ERR_PTR(-EINVAL);
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 4ac63c0e50c7..ee54e841de4a 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -245,6 +245,28 @@ static void regmap_format_32_native(void *buf, unsigned int val,
*(u32 *)buf = val << shift;
}
+#ifdef CONFIG_64BIT
+static void regmap_format_64_be(void *buf, unsigned int val, unsigned int shift)
+{
+ __be64 *b = buf;
+
+ b[0] = cpu_to_be64((u64)val << shift);
+}
+
+static void regmap_format_64_le(void *buf, unsigned int val, unsigned int shift)
+{
+ __le64 *b = buf;
+
+ b[0] = cpu_to_le64((u64)val << shift);
+}
+
+static void regmap_format_64_native(void *buf, unsigned int val,
+ unsigned int shift)
+{
+ *(u64 *)buf = (u64)val << shift;
+}
+#endif
+
static void regmap_parse_inplace_noop(void *buf)
{
}
@@ -332,6 +354,41 @@ static unsigned int regmap_parse_32_native(const void *buf)
return *(u32 *)buf;
}
+#ifdef CONFIG_64BIT
+static unsigned int regmap_parse_64_be(const void *buf)
+{
+ const __be64 *b = buf;
+
+ return be64_to_cpu(b[0]);
+}
+
+static unsigned int regmap_parse_64_le(const void *buf)
+{
+ const __le64 *b = buf;
+
+ return le64_to_cpu(b[0]);
+}
+
+static void regmap_parse_64_be_inplace(void *buf)
+{
+ __be64 *b = buf;
+
+ b[0] = be64_to_cpu(b[0]);
+}
+
+static void regmap_parse_64_le_inplace(void *buf)
+{
+ __le64 *b = buf;
+
+ b[0] = le64_to_cpu(b[0]);
+}
+
+static unsigned int regmap_parse_64_native(const void *buf)
+{
+ return *(u64 *)buf;
+}
+#endif
+
static void regmap_lock_mutex(void *__map)
{
struct regmap *map = __map;
@@ -712,6 +769,21 @@ struct regmap *__regmap_init(struct device *dev,
}
break;
+#ifdef CONFIG_64BIT
+ case 64:
+ switch (reg_endian) {
+ case REGMAP_ENDIAN_BIG:
+ map->format.format_reg = regmap_format_64_be;
+ break;
+ case REGMAP_ENDIAN_NATIVE:
+ map->format.format_reg = regmap_format_64_native;
+ break;
+ default:
+ goto err_map;
+ }
+ break;
+#endif
+
default:
goto err_map;
}
@@ -771,6 +843,28 @@ struct regmap *__regmap_init(struct device *dev,
goto err_map;
}
break;
+#ifdef CONFIG_64BIT
+ case 64:
+ switch (val_endian) {
+ case REGMAP_ENDIAN_BIG:
+ map->format.format_val = regmap_format_64_be;
+ map->format.parse_val = regmap_parse_64_be;
+ map->format.parse_inplace = regmap_parse_64_be_inplace;
+ break;
+ case REGMAP_ENDIAN_LITTLE:
+ map->format.format_val = regmap_format_64_le;
+ map->format.parse_val = regmap_parse_64_le;
+ map->format.parse_inplace = regmap_parse_64_le_inplace;
+ break;
+ case REGMAP_ENDIAN_NATIVE:
+ map->format.format_val = regmap_format_64_native;
+ map->format.parse_val = regmap_parse_64_native;
+ break;
+ default:
+ goto err_map;
+ }
+ break;
+#endif
}
if (map->format.format_write) {
@@ -1513,7 +1607,7 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
{
int ret;
- if (reg % map->reg_stride)
+ if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
map->lock(map->lock_arg);
@@ -1540,7 +1634,7 @@ int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val)
{
int ret;
- if (reg % map->reg_stride)
+ if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
map->lock(map->lock_arg);
@@ -1714,7 +1808,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
if (map->bus && !map->format.parse_inplace)
return -EINVAL;
- if (reg % map->reg_stride)
+ if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
/*
@@ -1983,7 +2077,7 @@ static int _regmap_multi_reg_write(struct regmap *map,
int reg = regs[i].reg;
if (!map->writeable_reg(map->dev, reg))
return -EINVAL;
- if (reg % map->reg_stride)
+ if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
}
@@ -2133,7 +2227,7 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg,
if (val_len % map->format.val_bytes)
return -EINVAL;
- if (reg % map->reg_stride)
+ if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
map->lock(map->lock_arg);
@@ -2260,7 +2354,7 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
{
int ret;
- if (reg % map->reg_stride)
+ if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
map->lock(map->lock_arg);
@@ -2296,7 +2390,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
return -EINVAL;
if (val_len % map->format.val_bytes)
return -EINVAL;
- if (reg % map->reg_stride)
+ if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
if (val_count == 0)
return -EINVAL;
@@ -2414,7 +2508,7 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
size_t val_bytes = map->format.val_bytes;
bool vol = regmap_volatile_range(map, reg, val_count);
- if (reg % map->reg_stride)
+ if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
if (map->bus && map->format.parse_inplace && (vol || map->cache_type == REGCACHE_NONE)) {
@@ -2488,11 +2582,19 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
* we assume that the values are native
* endian.
*/
+#ifdef CONFIG_64BIT
+ u64 *u64 = val;
+#endif
u32 *u32 = val;
u16 *u16 = val;
u8 *u8 = val;
switch (map->format.val_bytes) {
+#ifdef CONFIG_64BIT
+ case 8:
+ u64[i] = ival;
+ break;
+#endif
case 4:
u32[i] = ival;
break;
diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c
index 59d8d0d14824..c466f752b067 100644
--- a/drivers/bcma/main.c
+++ b/drivers/bcma/main.c
@@ -668,11 +668,36 @@ static int bcma_device_uevent(struct device *dev, struct kobj_uevent_env *env)
core->id.rev, core->id.class);
}
-static int __init bcma_modinit(void)
+static unsigned int bcma_bus_registered;
+
+/*
+ * If built-in, bus has to be registered early, before any driver calls
+ * bcma_driver_register.
+ * Otherwise registering driver would trigger BUG in driver_register.
+ */
+static int __init bcma_init_bus_register(void)
{
int err;
+ if (bcma_bus_registered)
+ return 0;
+
err = bus_register(&bcma_bus_type);
+ if (!err)
+ bcma_bus_registered = 1;
+
+ return err;
+}
+#ifndef MODULE
+fs_initcall(bcma_init_bus_register);
+#endif
+
+/* Main initialization has to be done with SPI/mtd/NAND/SPROM available */
+static int __init bcma_modinit(void)
+{
+ int err;
+
+ err = bcma_init_bus_register();
if (err)
return err;
@@ -691,7 +716,7 @@ static int __init bcma_modinit(void)
return err;
}
-fs_initcall(bcma_modinit);
+module_init(bcma_modinit);
static void __exit bcma_modexit(void)
{
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 0422c47261c3..63c2064689f8 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -514,14 +514,9 @@ cciss_proc_write(struct file *file, const char __user *buf,
if (!buf || length > PAGE_SIZE - 1)
return -EINVAL;
- buffer = (char *)__get_free_page(GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
-
- err = -EFAULT;
- if (copy_from_user(buffer, buf, length))
- goto out;
- buffer[length] = '\0';
+ buffer = memdup_user_nul(buf, length);
+ if (IS_ERR(buffer))
+ return PTR_ERR(buffer);
#ifdef CONFIG_CISS_SCSI_TAPE
if (strncmp(ENGAGE_SCSI, buffer, sizeof ENGAGE_SCSI - 1) == 0) {
@@ -537,8 +532,7 @@ cciss_proc_write(struct file *file, const char __user *buf,
/* might be nice to have "disengage" too, but it's not
safely possible. (only 1 module use count, lock issues.) */
-out:
- free_page((unsigned long)buffer);
+ kfree(buffer);
return err;
}
@@ -3854,7 +3848,7 @@ static void print_cfg_table(ctlr_info_t *h)
readl(&(tb->HostWrite.CoalIntDelay)));
dev_dbg(&h->pdev->dev, " Coalesce Interrupt Count = 0x%x\n",
readl(&(tb->HostWrite.CoalIntCount)));
- dev_dbg(&h->pdev->dev, " Max outstanding commands = 0x%d\n",
+ dev_dbg(&h->pdev->dev, " Max outstanding commands = 0x%x\n",
readl(&(tb->CmdsOutMax)));
dev_dbg(&h->pdev->dev, " Bus Types = 0x%x\n",
readl(&(tb->BusTypes)));
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index 331363e7de0f..9e251201dd48 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -3585,7 +3585,7 @@ static void __init config_types(void)
unsigned int type = UDP->cmos;
struct floppy_drive_params *params;
const char *name = NULL;
- static char temparea[32];
+ char temparea[32];
if (type < ARRAY_SIZE(default_drive_params)) {
params = &default_drive_params[type].params;
@@ -3596,7 +3596,8 @@ static void __init config_types(void)
allowed_drive_mask &= ~(1 << drive);
} else {
params = &default_drive_params[0].params;
- sprintf(temparea, "unknown type %d (usb?)", type);
+ snprintf(temparea, sizeof(temparea),
+ "unknown type %d (usb?)", type);
name = temparea;
}
if (name) {
diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c
index a28a562f7b7f..34997d8ecd64 100644
--- a/drivers/block/mtip32xx/mtip32xx.c
+++ b/drivers/block/mtip32xx/mtip32xx.c
@@ -2029,13 +2029,10 @@ static int exec_drive_taskfile(struct driver_data *dd,
}
if (taskout) {
- outbuf = kzalloc(taskout, GFP_KERNEL);
- if (outbuf == NULL) {
- err = -ENOMEM;
- goto abort;
- }
- if (copy_from_user(outbuf, buf + outtotal, taskout)) {
- err = -EFAULT;
+ outbuf = memdup_user(buf + outtotal, taskout);
+ if (IS_ERR(outbuf)) {
+ err = PTR_ERR(outbuf);
+ outbuf = NULL;
goto abort;
}
outbuf_dma = pci_map_single(dd->pdev,
@@ -2050,14 +2047,10 @@ static int exec_drive_taskfile(struct driver_data *dd,
}
if (taskin) {
- inbuf = kzalloc(taskin, GFP_KERNEL);
- if (inbuf == NULL) {
- err = -ENOMEM;
- goto abort;
- }
-
- if (copy_from_user(inbuf, buf + intotal, taskin)) {
- err = -EFAULT;
+ inbuf = memdup_user(buf + intotal, taskin);
+ if (IS_ERR(inbuf)) {
+ err = PTR_ERR(inbuf);
+ inbuf = NULL;
goto abort;
}
inbuf_dma = pci_map_single(dd->pdev,
@@ -3810,7 +3803,6 @@ static int mtip_block_initialize(struct driver_data *dd)
sector_t capacity;
unsigned int index = 0;
struct kobject *kobj;
- unsigned char thd_name[16];
if (dd->disk)
goto skip_create_disk; /* hw init done, before rebuild */
@@ -3958,10 +3950,9 @@ skip_create_disk:
}
start_service_thread:
- sprintf(thd_name, "mtip_svc_thd_%02d", index);
dd->mtip_svc_handler = kthread_create_on_node(mtip_service_thread,
- dd, dd->numa_node, "%s",
- thd_name);
+ dd, dd->numa_node,
+ "mtip_svc_thd_%02d", index);
if (IS_ERR(dd->mtip_svc_handler)) {
dev_err(&dd->pdev->dev, "service thread failed to start\n");
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 93b3f99b6865..e4c5cc107934 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -827,6 +827,7 @@ static const struct block_device_operations nbd_fops =
{
.owner = THIS_MODULE,
.ioctl = nbd_ioctl,
+ .compat_ioctl = nbd_ioctl,
};
#if IS_ENABLED(CONFIG_DEBUG_FS)
diff --git a/drivers/block/null_blk.c b/drivers/block/null_blk.c
index 6255d1c4bba4..09e3c0d87ecc 100644
--- a/drivers/block/null_blk.c
+++ b/drivers/block/null_blk.c
@@ -8,6 +8,7 @@
#include <linux/slab.h>
#include <linux/blk-mq.h>
#include <linux/hrtimer.h>
+#include <linux/lightnvm.h>
struct nullb_cmd {
struct list_head list;
@@ -17,6 +18,7 @@ struct nullb_cmd {
struct bio *bio;
unsigned int tag;
struct nullb_queue *nq;
+ struct hrtimer timer;
};
struct nullb_queue {
@@ -39,23 +41,14 @@ struct nullb {
struct nullb_queue *queues;
unsigned int nr_queues;
+ char disk_name[DISK_NAME_LEN];
};
static LIST_HEAD(nullb_list);
static struct mutex lock;
static int null_major;
static int nullb_indexes;
-
-struct completion_queue {
- struct llist_head list;
- struct hrtimer timer;
-};
-
-/*
- * These are per-cpu for now, they will need to be configured by the
- * complete_queues parameter and appropriately mapped.
- */
-static DEFINE_PER_CPU(struct completion_queue, completion_queues);
+static struct kmem_cache *ppa_cache;
enum {
NULL_IRQ_NONE = 0,
@@ -119,6 +112,10 @@ static int nr_devices = 2;
module_param(nr_devices, int, S_IRUGO);
MODULE_PARM_DESC(nr_devices, "Number of devices to register");
+static bool use_lightnvm;
+module_param(use_lightnvm, bool, S_IRUGO);
+MODULE_PARM_DESC(use_lightnvm, "Register as a LightNVM device");
+
static int irqmode = NULL_IRQ_SOFTIRQ;
static int null_set_irqmode(const char *str, const struct kernel_param *kp)
@@ -135,8 +132,8 @@ static const struct kernel_param_ops null_irqmode_param_ops = {
device_param_cb(irqmode, &null_irqmode_param_ops, &irqmode, S_IRUGO);
MODULE_PARM_DESC(irqmode, "IRQ completion handler. 0-none, 1-softirq, 2-timer");
-static int completion_nsec = 10000;
-module_param(completion_nsec, int, S_IRUGO);
+static unsigned long completion_nsec = 10000;
+module_param(completion_nsec, ulong, S_IRUGO);
MODULE_PARM_DESC(completion_nsec, "Time in ns to complete a request in hardware. Default: 10,000ns");
static int hw_queue_depth = 64;
@@ -173,6 +170,8 @@ static void free_cmd(struct nullb_cmd *cmd)
put_tag(cmd->nq, cmd->tag);
}
+static enum hrtimer_restart null_cmd_timer_expired(struct hrtimer *timer);
+
static struct nullb_cmd *__alloc_cmd(struct nullb_queue *nq)
{
struct nullb_cmd *cmd;
@@ -183,6 +182,11 @@ static struct nullb_cmd *__alloc_cmd(struct nullb_queue *nq)
cmd = &nq->cmds[tag];
cmd->tag = tag;
cmd->nq = nq;
+ if (irqmode == NULL_IRQ_TIMER) {
+ hrtimer_init(&cmd->timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ cmd->timer.function = null_cmd_timer_expired;
+ }
return cmd;
}
@@ -213,6 +217,11 @@ static struct nullb_cmd *alloc_cmd(struct nullb_queue *nq, int can_wait)
static void end_cmd(struct nullb_cmd *cmd)
{
+ struct request_queue *q = NULL;
+
+ if (cmd->rq)
+ q = cmd->rq->q;
+
switch (queue_mode) {
case NULL_Q_MQ:
blk_mq_end_request(cmd->rq, 0);
@@ -227,51 +236,29 @@ static void end_cmd(struct nullb_cmd *cmd)
}
free_cmd(cmd);
+
+ /* Restart queue if needed, as we are freeing a tag */
+ if (queue_mode == NULL_Q_RQ && blk_queue_stopped(q)) {
+ unsigned long flags;
+
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_start_queue_async(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+ }
}
static enum hrtimer_restart null_cmd_timer_expired(struct hrtimer *timer)
{
- struct completion_queue *cq;
- struct llist_node *entry;
- struct nullb_cmd *cmd;
-
- cq = &per_cpu(completion_queues, smp_processor_id());
-
- while ((entry = llist_del_all(&cq->list)) != NULL) {
- entry = llist_reverse_order(entry);
- do {
- struct request_queue *q = NULL;
-
- cmd = container_of(entry, struct nullb_cmd, ll_list);
- entry = entry->next;
- if (cmd->rq)
- q = cmd->rq->q;
- end_cmd(cmd);
-
- if (q && !q->mq_ops && blk_queue_stopped(q)) {
- spin_lock(q->queue_lock);
- if (blk_queue_stopped(q))
- blk_start_queue(q);
- spin_unlock(q->queue_lock);
- }
- } while (entry);
- }
+ end_cmd(container_of(timer, struct nullb_cmd, timer));
return HRTIMER_NORESTART;
}
static void null_cmd_end_timer(struct nullb_cmd *cmd)
{
- struct completion_queue *cq = &per_cpu(completion_queues, get_cpu());
+ ktime_t kt = ktime_set(0, completion_nsec);
- cmd->ll_list.next = NULL;
- if (llist_add(&cmd->ll_list, &cq->list)) {
- ktime_t kt = ktime_set(0, completion_nsec);
-
- hrtimer_start(&cq->timer, kt, HRTIMER_MODE_REL_PINNED);
- }
-
- put_cpu();
+ hrtimer_start(&cmd->timer, kt, HRTIMER_MODE_REL);
}
static void null_softirq_done_fn(struct request *rq)
@@ -369,6 +356,10 @@ static int null_queue_rq(struct blk_mq_hw_ctx *hctx,
{
struct nullb_cmd *cmd = blk_mq_rq_to_pdu(bd->rq);
+ if (irqmode == NULL_IRQ_TIMER) {
+ hrtimer_init(&cmd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ cmd->timer.function = null_cmd_timer_expired;
+ }
cmd->rq = bd->rq;
cmd->nq = hctx->driver_data;
@@ -427,15 +418,157 @@ static void null_del_dev(struct nullb *nullb)
{
list_del_init(&nullb->list);
- del_gendisk(nullb->disk);
+ if (use_lightnvm)
+ nvm_unregister(nullb->disk_name);
+ else
+ del_gendisk(nullb->disk);
blk_cleanup_queue(nullb->q);
if (queue_mode == NULL_Q_MQ)
blk_mq_free_tag_set(&nullb->tag_set);
- put_disk(nullb->disk);
+ if (!use_lightnvm)
+ put_disk(nullb->disk);
cleanup_queues(nullb);
kfree(nullb);
}
+#ifdef CONFIG_NVM
+
+static void null_lnvm_end_io(struct request *rq, int error)
+{
+ struct nvm_rq *rqd = rq->end_io_data;
+ struct nvm_dev *dev = rqd->dev;
+
+ dev->mt->end_io(rqd, error);
+
+ blk_put_request(rq);
+}
+
+static int null_lnvm_submit_io(struct nvm_dev *dev, struct nvm_rq *rqd)
+{
+ struct request_queue *q = dev->q;
+ struct request *rq;
+ struct bio *bio = rqd->bio;
+
+ rq = blk_mq_alloc_request(q, bio_rw(bio), GFP_KERNEL, 0);
+ if (IS_ERR(rq))
+ return -ENOMEM;
+
+ rq->cmd_type = REQ_TYPE_DRV_PRIV;
+ rq->__sector = bio->bi_iter.bi_sector;
+ rq->ioprio = bio_prio(bio);
+
+ if (bio_has_data(bio))
+ rq->nr_phys_segments = bio_phys_segments(q, bio);
+
+ rq->__data_len = bio->bi_iter.bi_size;
+ rq->bio = rq->biotail = bio;
+
+ rq->end_io_data = rqd;
+
+ blk_execute_rq_nowait(q, NULL, rq, 0, null_lnvm_end_io);
+
+ return 0;
+}
+
+static int null_lnvm_id(struct nvm_dev *dev, struct nvm_id *id)
+{
+ sector_t size = gb * 1024 * 1024 * 1024ULL;
+ sector_t blksize;
+ struct nvm_id_group *grp;
+
+ id->ver_id = 0x1;
+ id->vmnt = 0;
+ id->cgrps = 1;
+ id->cap = 0x3;
+ id->dom = 0x1;
+
+ id->ppaf.blk_offset = 0;
+ id->ppaf.blk_len = 16;
+ id->ppaf.pg_offset = 16;
+ id->ppaf.pg_len = 16;
+ id->ppaf.sect_offset = 32;
+ id->ppaf.sect_len = 8;
+ id->ppaf.pln_offset = 40;
+ id->ppaf.pln_len = 8;
+ id->ppaf.lun_offset = 48;
+ id->ppaf.lun_len = 8;
+ id->ppaf.ch_offset = 56;
+ id->ppaf.ch_len = 8;
+
+ do_div(size, bs); /* convert size to pages */
+ do_div(size, 256); /* concert size to pgs pr blk */
+ grp = &id->groups[0];
+ grp->mtype = 0;
+ grp->fmtype = 0;
+ grp->num_ch = 1;
+ grp->num_pg = 256;
+ blksize = size;
+ do_div(size, (1 << 16));
+ grp->num_lun = size + 1;
+ do_div(blksize, grp->num_lun);
+ grp->num_blk = blksize;
+ grp->num_pln = 1;
+
+ grp->fpg_sz = bs;
+ grp->csecs = bs;
+ grp->trdt = 25000;
+ grp->trdm = 25000;
+ grp->tprt = 500000;
+ grp->tprm = 500000;
+ grp->tbet = 1500000;
+ grp->tbem = 1500000;
+ grp->mpos = 0x010101; /* single plane rwe */
+ grp->cpar = hw_queue_depth;
+
+ return 0;
+}
+
+static void *null_lnvm_create_dma_pool(struct nvm_dev *dev, char *name)
+{
+ mempool_t *virtmem_pool;
+
+ virtmem_pool = mempool_create_slab_pool(64, ppa_cache);
+ if (!virtmem_pool) {
+ pr_err("null_blk: Unable to create virtual memory pool\n");
+ return NULL;
+ }
+
+ return virtmem_pool;
+}
+
+static void null_lnvm_destroy_dma_pool(void *pool)
+{
+ mempool_destroy(pool);
+}
+
+static void *null_lnvm_dev_dma_alloc(struct nvm_dev *dev, void *pool,
+ gfp_t mem_flags, dma_addr_t *dma_handler)
+{
+ return mempool_alloc(pool, mem_flags);
+}
+
+static void null_lnvm_dev_dma_free(void *pool, void *entry,
+ dma_addr_t dma_handler)
+{
+ mempool_free(entry, pool);
+}
+
+static struct nvm_dev_ops null_lnvm_dev_ops = {
+ .identity = null_lnvm_id,
+ .submit_io = null_lnvm_submit_io,
+
+ .create_dma_pool = null_lnvm_create_dma_pool,
+ .destroy_dma_pool = null_lnvm_destroy_dma_pool,
+ .dev_dma_alloc = null_lnvm_dev_dma_alloc,
+ .dev_dma_free = null_lnvm_dev_dma_free,
+
+ /* Simulate nvme protocol restriction */
+ .max_phys_sect = 64,
+};
+#else
+static struct nvm_dev_ops null_lnvm_dev_ops;
+#endif /* CONFIG_NVM */
+
static int null_open(struct block_device *bdev, fmode_t mode)
{
return 0;
@@ -575,11 +708,6 @@ static int null_add_dev(void)
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, nullb->q);
queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, nullb->q);
- disk = nullb->disk = alloc_disk_node(1, home_node);
- if (!disk) {
- rv = -ENOMEM;
- goto out_cleanup_blk_queue;
- }
mutex_lock(&lock);
list_add_tail(&nullb->list, &nullb_list);
@@ -589,6 +717,21 @@ static int null_add_dev(void)
blk_queue_logical_block_size(nullb->q, bs);
blk_queue_physical_block_size(nullb->q, bs);
+ sprintf(nullb->disk_name, "nullb%d", nullb->index);
+
+ if (use_lightnvm) {
+ rv = nvm_register(nullb->q, nullb->disk_name,
+ &null_lnvm_dev_ops);
+ if (rv)
+ goto out_cleanup_blk_queue;
+ goto done;
+ }
+
+ disk = nullb->disk = alloc_disk_node(1, home_node);
+ if (!disk) {
+ rv = -ENOMEM;
+ goto out_cleanup_lightnvm;
+ }
size = gb * 1024 * 1024 * 1024ULL;
set_capacity(disk, size >> 9);
@@ -598,10 +741,15 @@ static int null_add_dev(void)
disk->fops = &null_fops;
disk->private_data = nullb;
disk->queue = nullb->q;
- sprintf(disk->disk_name, "nullb%d", nullb->index);
+ strncpy(disk->disk_name, nullb->disk_name, DISK_NAME_LEN);
+
add_disk(disk);
+done:
return 0;
+out_cleanup_lightnvm:
+ if (use_lightnvm)
+ nvm_unregister(nullb->disk_name);
out_cleanup_blk_queue:
blk_cleanup_queue(nullb->q);
out_cleanup_tags:
@@ -617,7 +765,9 @@ out:
static int __init null_init(void)
{
+ int ret = 0;
unsigned int i;
+ struct nullb *nullb;
if (bs > PAGE_SIZE) {
pr_warn("null_blk: invalid block size\n");
@@ -625,6 +775,18 @@ static int __init null_init(void)
bs = PAGE_SIZE;
}
+ if (use_lightnvm && bs != 4096) {
+ pr_warn("null_blk: LightNVM only supports 4k block size\n");
+ pr_warn("null_blk: defaults block size to 4k\n");
+ bs = 4096;
+ }
+
+ if (use_lightnvm && queue_mode != NULL_Q_MQ) {
+ pr_warn("null_blk: LightNVM only supported for blk-mq\n");
+ pr_warn("null_blk: defaults queue mode to blk-mq\n");
+ queue_mode = NULL_Q_MQ;
+ }
+
if (queue_mode == NULL_Q_MQ && use_per_node_hctx) {
if (submit_queues < nr_online_nodes) {
pr_warn("null_blk: submit_queues param is set to %u.",
@@ -638,32 +800,38 @@ static int __init null_init(void)
mutex_init(&lock);
- /* Initialize a separate list for each CPU for issuing softirqs */
- for_each_possible_cpu(i) {
- struct completion_queue *cq = &per_cpu(completion_queues, i);
-
- init_llist_head(&cq->list);
-
- if (irqmode != NULL_IRQ_TIMER)
- continue;
-
- hrtimer_init(&cq->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- cq->timer.function = null_cmd_timer_expired;
- }
-
null_major = register_blkdev(0, "nullb");
if (null_major < 0)
return null_major;
- for (i = 0; i < nr_devices; i++) {
- if (null_add_dev()) {
- unregister_blkdev(null_major, "nullb");
- return -EINVAL;
+ if (use_lightnvm) {
+ ppa_cache = kmem_cache_create("ppa_cache", 64 * sizeof(u64),
+ 0, 0, NULL);
+ if (!ppa_cache) {
+ pr_err("null_blk: unable to create ppa cache\n");
+ ret = -ENOMEM;
+ goto err_ppa;
}
}
+ for (i = 0; i < nr_devices; i++) {
+ ret = null_add_dev();
+ if (ret)
+ goto err_dev;
+ }
+
pr_info("null: module loaded\n");
return 0;
+
+err_dev:
+ while (!list_empty(&nullb_list)) {
+ nullb = list_entry(nullb_list.next, struct nullb, list);
+ null_del_dev(nullb);
+ }
+ kmem_cache_destroy(ppa_cache);
+err_ppa:
+ unregister_blkdev(null_major, "nullb");
+ return ret;
}
static void __exit null_exit(void)
@@ -678,6 +846,8 @@ static void __exit null_exit(void)
null_del_dev(nullb);
}
mutex_unlock(&lock);
+
+ kmem_cache_destroy(ppa_cache);
}
module_init(null_init);
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 235708c7c46e..81ea69fee7ca 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -3442,6 +3442,7 @@ static void rbd_queue_workfn(struct work_struct *work)
goto err_rq;
}
img_request->rq = rq;
+ snapc = NULL; /* img_request consumes a ref */
if (op_type == OBJ_OP_DISCARD)
result = rbd_img_request_fill(img_request, OBJ_REQUEST_NODATA,
diff --git a/drivers/block/rsxx/core.c b/drivers/block/rsxx/core.c
index d8b2488aaade..34997df132e2 100644
--- a/drivers/block/rsxx/core.c
+++ b/drivers/block/rsxx/core.c
@@ -203,14 +203,11 @@ static ssize_t rsxx_cram_write(struct file *fp, const char __user *ubuf,
char *buf;
ssize_t st;
- buf = kzalloc(cnt, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
+ buf = memdup_user(ubuf, cnt);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
- st = copy_from_user(buf, ubuf, cnt);
- if (!st)
- st = rsxx_creg_write(card, CREG_ADD_CRAM + (u32)*ppos, cnt,
- buf, 1);
+ st = rsxx_creg_write(card, CREG_ADD_CRAM + (u32)*ppos, cnt, buf, 1);
kfree(buf);
if (st)
return st;
diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c
index f9099940c272..41fb1a917b17 100644
--- a/drivers/block/xen-blkback/blkback.c
+++ b/drivers/block/xen-blkback/blkback.c
@@ -950,6 +950,8 @@ static int xen_blkbk_parse_indirect(struct blkif_request *req,
goto unmap;
for (n = 0, i = 0; n < nseg; n++) {
+ uint8_t first_sect, last_sect;
+
if ((n % SEGS_PER_INDIRECT_FRAME) == 0) {
/* Map indirect segments */
if (segments)
@@ -957,15 +959,18 @@ static int xen_blkbk_parse_indirect(struct blkif_request *req,
segments = kmap_atomic(pages[n/SEGS_PER_INDIRECT_FRAME]->page);
}
i = n % SEGS_PER_INDIRECT_FRAME;
+
pending_req->segments[n]->gref = segments[i].gref;
- seg[n].nsec = segments[i].last_sect -
- segments[i].first_sect + 1;
- seg[n].offset = (segments[i].first_sect << 9);
- if ((segments[i].last_sect >= (XEN_PAGE_SIZE >> 9)) ||
- (segments[i].last_sect < segments[i].first_sect)) {
+
+ first_sect = READ_ONCE(segments[i].first_sect);
+ last_sect = READ_ONCE(segments[i].last_sect);
+ if (last_sect >= (XEN_PAGE_SIZE >> 9) || last_sect < first_sect) {
rc = -EINVAL;
goto unmap;
}
+
+ seg[n].nsec = last_sect - first_sect + 1;
+ seg[n].offset = first_sect << 9;
preq->nr_sects += seg[n].nsec;
}
diff --git a/drivers/block/xen-blkback/common.h b/drivers/block/xen-blkback/common.h
index 68e87a037b99..c929ae22764c 100644
--- a/drivers/block/xen-blkback/common.h
+++ b/drivers/block/xen-blkback/common.h
@@ -408,8 +408,8 @@ static inline void blkif_get_x86_32_req(struct blkif_request *dst,
struct blkif_x86_32_request *src)
{
int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST, j;
- dst->operation = src->operation;
- switch (src->operation) {
+ dst->operation = READ_ONCE(src->operation);
+ switch (dst->operation) {
case BLKIF_OP_READ:
case BLKIF_OP_WRITE:
case BLKIF_OP_WRITE_BARRIER:
@@ -456,8 +456,8 @@ static inline void blkif_get_x86_64_req(struct blkif_request *dst,
struct blkif_x86_64_request *src)
{
int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST, j;
- dst->operation = src->operation;
- switch (src->operation) {
+ dst->operation = READ_ONCE(src->operation);
+ switch (dst->operation) {
case BLKIF_OP_READ:
case BLKIF_OP_WRITE:
case BLKIF_OP_WRITE_BARRIER:
diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c
index 5cb13ca3a3ac..3ef42e563bb5 100644
--- a/drivers/block/zram/zcomp.c
+++ b/drivers/block/zram/zcomp.c
@@ -74,18 +74,18 @@ static void zcomp_strm_free(struct zcomp *comp, struct zcomp_strm *zstrm)
* allocate new zcomp_strm structure with ->private initialized by
* backend, return NULL on error
*/
-static struct zcomp_strm *zcomp_strm_alloc(struct zcomp *comp)
+static struct zcomp_strm *zcomp_strm_alloc(struct zcomp *comp, gfp_t flags)
{
- struct zcomp_strm *zstrm = kmalloc(sizeof(*zstrm), GFP_KERNEL);
+ struct zcomp_strm *zstrm = kmalloc(sizeof(*zstrm), flags);
if (!zstrm)
return NULL;
- zstrm->private = comp->backend->create();
+ zstrm->private = comp->backend->create(flags);
/*
* allocate 2 pages. 1 for compressed data, plus 1 extra for the
* case when compressed size is larger than the original one
*/
- zstrm->buffer = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1);
+ zstrm->buffer = (void *)__get_free_pages(flags | __GFP_ZERO, 1);
if (!zstrm->private || !zstrm->buffer) {
zcomp_strm_free(comp, zstrm);
zstrm = NULL;
@@ -120,8 +120,16 @@ static struct zcomp_strm *zcomp_strm_multi_find(struct zcomp *comp)
/* allocate new zstrm stream */
zs->avail_strm++;
spin_unlock(&zs->strm_lock);
-
- zstrm = zcomp_strm_alloc(comp);
+ /*
+ * This function can be called in swapout/fs write path
+ * so we can't use GFP_FS|IO. And it assumes we already
+ * have at least one stream in zram initialization so we
+ * don't do best effort to allocate more stream in here.
+ * A default stream will work well without further multiple
+ * streams. That's why we use NORETRY | NOWARN.
+ */
+ zstrm = zcomp_strm_alloc(comp, GFP_NOIO | __GFP_NORETRY |
+ __GFP_NOWARN);
if (!zstrm) {
spin_lock(&zs->strm_lock);
zs->avail_strm--;
@@ -209,7 +217,7 @@ static int zcomp_strm_multi_create(struct zcomp *comp, int max_strm)
zs->max_strm = max_strm;
zs->avail_strm = 1;
- zstrm = zcomp_strm_alloc(comp);
+ zstrm = zcomp_strm_alloc(comp, GFP_KERNEL);
if (!zstrm) {
kfree(zs);
return -ENOMEM;
@@ -259,7 +267,7 @@ static int zcomp_strm_single_create(struct zcomp *comp)
comp->stream = zs;
mutex_init(&zs->strm_lock);
- zs->zstrm = zcomp_strm_alloc(comp);
+ zs->zstrm = zcomp_strm_alloc(comp, GFP_KERNEL);
if (!zs->zstrm) {
kfree(zs);
return -ENOMEM;
diff --git a/drivers/block/zram/zcomp.h b/drivers/block/zram/zcomp.h
index 46e2b9f8f1f0..b7d2a4bcae54 100644
--- a/drivers/block/zram/zcomp.h
+++ b/drivers/block/zram/zcomp.h
@@ -33,7 +33,7 @@ struct zcomp_backend {
int (*decompress)(const unsigned char *src, size_t src_len,
unsigned char *dst);
- void *(*create)(void);
+ void *(*create)(gfp_t flags);
void (*destroy)(void *private);
const char *name;
diff --git a/drivers/block/zram/zcomp_lz4.c b/drivers/block/zram/zcomp_lz4.c
index f2afb7e988c3..0110086accba 100644
--- a/drivers/block/zram/zcomp_lz4.c
+++ b/drivers/block/zram/zcomp_lz4.c
@@ -10,17 +10,26 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/lz4.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
#include "zcomp_lz4.h"
-static void *zcomp_lz4_create(void)
+static void *zcomp_lz4_create(gfp_t flags)
{
- return kzalloc(LZ4_MEM_COMPRESS, GFP_KERNEL);
+ void *ret;
+
+ ret = kmalloc(LZ4_MEM_COMPRESS, flags);
+ if (!ret)
+ ret = __vmalloc(LZ4_MEM_COMPRESS,
+ flags | __GFP_HIGHMEM,
+ PAGE_KERNEL);
+ return ret;
}
static void zcomp_lz4_destroy(void *private)
{
- kfree(private);
+ kvfree(private);
}
static int zcomp_lz4_compress(const unsigned char *src, unsigned char *dst,
diff --git a/drivers/block/zram/zcomp_lzo.c b/drivers/block/zram/zcomp_lzo.c
index da1bc47d588e..ed7a1f0549ec 100644
--- a/drivers/block/zram/zcomp_lzo.c
+++ b/drivers/block/zram/zcomp_lzo.c
@@ -10,17 +10,26 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/lzo.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
#include "zcomp_lzo.h"
-static void *lzo_create(void)
+static void *lzo_create(gfp_t flags)
{
- return kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
+ void *ret;
+
+ ret = kmalloc(LZO1X_MEM_COMPRESS, flags);
+ if (!ret)
+ ret = __vmalloc(LZO1X_MEM_COMPRESS,
+ flags | __GFP_HIGHMEM,
+ PAGE_KERNEL);
+ return ret;
}
static void lzo_destroy(void *private)
{
- kfree(private);
+ kvfree(private);
}
static int lzo_compress(const unsigned char *src, unsigned char *dst,
diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c
index 364f82b34d03..5b0ef7bbe8ac 100644
--- a/drivers/bluetooth/bcm203x.c
+++ b/drivers/bluetooth/bcm203x.c
@@ -178,10 +178,8 @@ static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id
return -ENODEV;
data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
- if (!data) {
- BT_ERR("Can't allocate memory for data structure");
+ if (!data)
return -ENOMEM;
- }
data->udev = udev;
data->state = BCM203X_LOAD_MINIDRV;
diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c
index 616ec2ac1b22..3bf4ec60e073 100644
--- a/drivers/bluetooth/bfusb.c
+++ b/drivers/bluetooth/bfusb.c
@@ -324,7 +324,7 @@ static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned ch
return -ENOMEM;
}
- bt_cb(skb)->pkt_type = pkt_type;
+ hci_skb_pkt_type(skb) = pkt_type;
data->reassembly = skb;
} else {
@@ -469,9 +469,10 @@ static int bfusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
unsigned char buf[3];
int sent = 0, size, count;
- BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, bt_cb(skb)->pkt_type, skb->len);
+ BT_DBG("hdev %p skb %p type %d len %d", hdev, skb,
+ hci_skb_pkt_type(skb), skb->len);
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
@@ -484,7 +485,7 @@ static int bfusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
}
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
count = skb->len;
@@ -635,10 +636,8 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i
/* Initialize control structure and load firmware */
data = devm_kzalloc(&intf->dev, sizeof(struct bfusb_data), GFP_KERNEL);
- if (!data) {
- BT_ERR("Can't allocate memory for control structure");
- goto done;
- }
+ if (!data)
+ return -ENOMEM;
data->udev = udev;
data->bulk_in_ep = bulk_in_ep->desc.bEndpointAddress;
diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c
index 36fa1c958c74..c0b3b5576992 100644
--- a/drivers/bluetooth/bluecard_cs.c
+++ b/drivers/bluetooth/bluecard_cs.c
@@ -261,7 +261,7 @@ static void bluecard_write_wakeup(struct bluecard_info *info)
if (!skb)
break;
- if (bt_cb(skb)->pkt_type & 0x80) {
+ if (hci_skb_pkt_type(skb) & 0x80) {
/* Disable RTS */
info->ctrl_reg |= REG_CONTROL_RTS;
outb(info->ctrl_reg, iobase + REG_CONTROL);
@@ -279,13 +279,13 @@ static void bluecard_write_wakeup(struct bluecard_info *info)
/* Mark the buffer as dirty */
clear_bit(ready_bit, &(info->tx_state));
- if (bt_cb(skb)->pkt_type & 0x80) {
+ if (hci_skb_pkt_type(skb) & 0x80) {
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
DEFINE_WAIT(wait);
unsigned char baud_reg;
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case PKT_BAUD_RATE_460800:
baud_reg = REG_CONTROL_BAUD_RATE_460800;
break;
@@ -402,9 +402,9 @@ static void bluecard_receive(struct bluecard_info *info,
if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
- bt_cb(info->rx_skb)->pkt_type = buf[i];
+ hci_skb_pkt_type(info->rx_skb) = buf[i];
- switch (bt_cb(info->rx_skb)->pkt_type) {
+ switch (hci_skb_pkt_type(info->rx_skb)) {
case 0x00:
/* init packet */
@@ -436,7 +436,8 @@ static void bluecard_receive(struct bluecard_info *info,
default:
/* unknown packet */
- BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+ BT_ERR("Unknown HCI packet with type 0x%02x received",
+ hci_skb_pkt_type(info->rx_skb));
info->hdev->stat.err_rx++;
kfree_skb(info->rx_skb);
@@ -578,21 +579,21 @@ static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud)
switch (baud) {
case 460800:
cmd[4] = 0x00;
- bt_cb(skb)->pkt_type = PKT_BAUD_RATE_460800;
+ hci_skb_pkt_type(skb) = PKT_BAUD_RATE_460800;
break;
case 230400:
cmd[4] = 0x01;
- bt_cb(skb)->pkt_type = PKT_BAUD_RATE_230400;
+ hci_skb_pkt_type(skb) = PKT_BAUD_RATE_230400;
break;
case 115200:
cmd[4] = 0x02;
- bt_cb(skb)->pkt_type = PKT_BAUD_RATE_115200;
+ hci_skb_pkt_type(skb) = PKT_BAUD_RATE_115200;
break;
case 57600:
/* Fall through... */
default:
cmd[4] = 0x03;
- bt_cb(skb)->pkt_type = PKT_BAUD_RATE_57600;
+ hci_skb_pkt_type(skb) = PKT_BAUD_RATE_57600;
break;
}
@@ -660,7 +661,7 @@ static int bluecard_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct bluecard_info *info = hci_get_drvdata(hdev);
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
@@ -673,7 +674,7 @@ static int bluecard_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
}
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&(info->txq), skb);
bluecard_write_wakeup(info);
diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c
index 49c397e21b39..fd6b53e9bbf2 100644
--- a/drivers/bluetooth/bpa10x.c
+++ b/drivers/bluetooth/bpa10x.c
@@ -295,9 +295,9 @@ static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
return -ENOMEM;
/* Prepend skb with frame type */
- *skb_push(skb, 1) = bt_cb(skb)->pkt_type;
+ *skb_push(skb, 1) = hci_skb_pkt_type(skb);
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
if (!dr) {
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
index 5803aaed958f..8165ef2fe877 100644
--- a/drivers/bluetooth/bt3c_cs.c
+++ b/drivers/bluetooth/bt3c_cs.c
@@ -246,10 +246,10 @@ static void bt3c_receive(struct bt3c_info *info)
if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
- bt_cb(info->rx_skb)->pkt_type = inb(iobase + DATA_L);
+ hci_skb_pkt_type(info->rx_skb) = inb(iobase + DATA_L);
inb(iobase + DATA_H);
- switch (bt_cb(info->rx_skb)->pkt_type) {
+ switch (hci_skb_pkt_type(info->rx_skb)) {
case HCI_EVENT_PKT:
info->rx_state = RECV_WAIT_EVENT_HEADER;
@@ -268,7 +268,8 @@ static void bt3c_receive(struct bt3c_info *info)
default:
/* Unknown packet */
- BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+ BT_ERR("Unknown HCI packet with type 0x%02x received",
+ hci_skb_pkt_type(info->rx_skb));
info->hdev->stat.err_rx++;
kfree_skb(info->rx_skb);
@@ -411,7 +412,7 @@ static int bt3c_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
struct bt3c_info *info = hci_get_drvdata(hdev);
unsigned long flags;
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
@@ -424,7 +425,7 @@ static int bt3c_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
}
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&(info->txq), skb);
spin_lock_irqsave(&(info->lock), flags);
diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
index 1f13e617bf56..fce154855718 100644
--- a/drivers/bluetooth/btintel.c
+++ b/drivers/bluetooth/btintel.c
@@ -73,6 +73,48 @@ int btintel_check_bdaddr(struct hci_dev *hdev)
}
EXPORT_SYMBOL_GPL(btintel_check_bdaddr);
+int btintel_enter_mfg(struct hci_dev *hdev)
+{
+ const u8 param[] = { 0x01, 0x00 };
+ struct sk_buff *skb;
+
+ skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_CMD_TIMEOUT);
+ if (IS_ERR(skb)) {
+ bt_dev_err(hdev, "Entering manufacturer mode failed (%ld)",
+ PTR_ERR(skb));
+ return PTR_ERR(skb);
+ }
+ kfree_skb(skb);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btintel_enter_mfg);
+
+int btintel_exit_mfg(struct hci_dev *hdev, bool reset, bool patched)
+{
+ u8 param[] = { 0x00, 0x00 };
+ struct sk_buff *skb;
+
+ /* The 2nd command parameter specifies the manufacturing exit method:
+ * 0x00: Just disable the manufacturing mode (0x00).
+ * 0x01: Disable manufacturing mode and reset with patches deactivated.
+ * 0x02: Disable manufacturing mode and reset with patches activated.
+ */
+ if (reset)
+ param[1] |= patched ? 0x02 : 0x01;
+
+ skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_CMD_TIMEOUT);
+ if (IS_ERR(skb)) {
+ bt_dev_err(hdev, "Exiting manufacturer mode failed (%ld)",
+ PTR_ERR(skb));
+ return PTR_ERR(skb);
+ }
+ kfree_skb(skb);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btintel_exit_mfg);
+
int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
struct sk_buff *skb;
@@ -126,37 +168,19 @@ EXPORT_SYMBOL_GPL(btintel_set_diag);
int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable)
{
- struct sk_buff *skb;
- u8 param[2];
- int err;
-
- param[0] = 0x01;
- param[1] = 0x00;
-
- skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- err = PTR_ERR(skb);
- BT_ERR("%s: Entering Intel manufacturer mode failed (%d)",
- hdev->name, err);
- return PTR_ERR(skb);
- }
- kfree_skb(skb);
+ int err, ret;
- err = btintel_set_diag(hdev, enable);
+ err = btintel_enter_mfg(hdev);
+ if (err)
+ return err;
- param[0] = 0x00;
- param[1] = 0x00;
+ ret = btintel_set_diag(hdev, enable);
- skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- err = PTR_ERR(skb);
- BT_ERR("%s: Leaving Intel manufacturer mode failed (%d)",
- hdev->name, err);
- return PTR_ERR(skb);
- }
- kfree_skb(skb);
+ err = btintel_exit_mfg(hdev, false, false);
+ if (err)
+ return err;
- return err;
+ return ret;
}
EXPORT_SYMBOL_GPL(btintel_set_diag_mfg);
@@ -309,39 +333,46 @@ EXPORT_SYMBOL_GPL(btintel_set_event_mask);
int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug)
{
- struct sk_buff *skb;
- u8 param[2];
- int err;
+ int err, ret;
- param[0] = 0x01;
- param[1] = 0x00;
+ err = btintel_enter_mfg(hdev);
+ if (err)
+ return err;
- skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- err = PTR_ERR(skb);
- BT_ERR("%s: Entering Intel manufacturer mode failed (%d)",
- hdev->name, err);
- return PTR_ERR(skb);
- }
- kfree_skb(skb);
+ ret = btintel_set_event_mask(hdev, debug);
- err = btintel_set_event_mask(hdev, debug);
+ err = btintel_exit_mfg(hdev, false, false);
+ if (err)
+ return err;
- param[0] = 0x00;
- param[1] = 0x00;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(btintel_set_event_mask_mfg);
- skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_INIT_TIMEOUT);
+int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver)
+{
+ struct sk_buff *skb;
+
+ skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_CMD_TIMEOUT);
if (IS_ERR(skb)) {
- err = PTR_ERR(skb);
- BT_ERR("%s: Leaving Intel manufacturer mode failed (%d)",
- hdev->name, err);
+ bt_dev_err(hdev, "Reading Intel version information failed (%ld)",
+ PTR_ERR(skb));
return PTR_ERR(skb);
}
+
+ if (skb->len != sizeof(*ver)) {
+ bt_dev_err(hdev, "Intel version event size mismatch");
+ kfree_skb(skb);
+ return -EILSEQ;
+ }
+
+ memcpy(ver, skb->data, sizeof(*ver));
+
kfree_skb(skb);
- return err;
+ return 0;
}
-EXPORT_SYMBOL_GPL(btintel_set_event_mask_mfg);
+EXPORT_SYMBOL_GPL(btintel_read_version);
/* ------- REGMAP IBT SUPPORT ------- */
diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h
index 07e58e05a7fa..1e8955aaafed 100644
--- a/drivers/bluetooth/btintel.h
+++ b/drivers/bluetooth/btintel.h
@@ -72,6 +72,8 @@ struct intel_secure_send_result {
#if IS_ENABLED(CONFIG_BT_INTEL)
int btintel_check_bdaddr(struct hci_dev *hdev);
+int btintel_enter_mfg(struct hci_dev *hdev);
+int btintel_exit_mfg(struct hci_dev *hdev, bool reset, bool patched);
int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
int btintel_set_diag(struct hci_dev *hdev, bool enable);
int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable);
@@ -83,6 +85,7 @@ int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen,
int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name);
int btintel_set_event_mask(struct hci_dev *hdev, bool debug);
int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug);
+int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver);
struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read,
u16 opcode_write);
@@ -94,6 +97,16 @@ static inline int btintel_check_bdaddr(struct hci_dev *hdev)
return -EOPNOTSUPP;
}
+static inline int btintel_enter_mfg(struct hci_dev *hdev)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int btintel_exit_mfg(struct hci_dev *hdev, bool reset, bool patched)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
return -EOPNOTSUPP;
@@ -140,6 +153,12 @@ static inline int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug)
return -EOPNOTSUPP;
}
+static inline int btintel_read_version(struct hci_dev *hdev,
+ struct intel_version *ver)
+{
+ return -EOPNOTSUPP;
+}
+
static inline struct regmap *btintel_regmap_init(struct hci_dev *hdev,
u16 opcode_read,
u16 opcode_write)
diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h
index 27a9aac25583..05904732e6f1 100644
--- a/drivers/bluetooth/btmrvl_drv.h
+++ b/drivers/bluetooth/btmrvl_drv.h
@@ -89,6 +89,7 @@ struct btmrvl_adapter {
wait_queue_head_t event_hs_wait_q;
u8 cmd_complete;
bool is_suspended;
+ bool is_suspending;
};
struct btmrvl_private {
diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c
index 6af917331962..f25a825a693f 100644
--- a/drivers/bluetooth/btmrvl_main.c
+++ b/drivers/bluetooth/btmrvl_main.c
@@ -196,7 +196,7 @@ static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode,
if (len)
memcpy(skb_put(skb, len), param, len);
- bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+ hci_skb_pkt_type(skb) = MRVL_VENDOR_PKT;
skb_queue_head(&priv->adapter->tx_queue, skb);
@@ -387,7 +387,7 @@ static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb)
skb->data[0] = (skb->len & 0x0000ff);
skb->data[1] = (skb->len & 0x00ff00) >> 8;
skb->data[2] = (skb->len & 0xff0000) >> 16;
- skb->data[3] = bt_cb(skb)->pkt_type;
+ skb->data[3] = hci_skb_pkt_type(skb);
if (priv->hw_host_to_card)
ret = priv->hw_host_to_card(priv, skb->data, skb->len);
@@ -434,9 +434,14 @@ static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btmrvl_private *priv = hci_get_drvdata(hdev);
- BT_DBG("type=%d, len=%d", skb->pkt_type, skb->len);
+ BT_DBG("type=%d, len=%d", hci_skb_pkt_type(skb), skb->len);
- switch (bt_cb(skb)->pkt_type) {
+ if (priv->adapter->is_suspending || priv->adapter->is_suspended) {
+ BT_ERR("%s: Device is suspending or suspended", __func__);
+ return -EBUSY;
+ }
+
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
@@ -452,7 +457,8 @@ static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
skb_queue_tail(&priv->adapter->tx_queue, skb);
- wake_up_interruptible(&priv->main_thread.wait_q);
+ if (!priv->adapter->is_suspended)
+ wake_up_interruptible(&priv->main_thread.wait_q);
return 0;
}
@@ -543,7 +549,7 @@ static int btmrvl_setup(struct hci_dev *hdev)
if (ret)
return ret;
- priv->btmrvl_dev.gpio_gap = 0xffff;
+ priv->btmrvl_dev.gpio_gap = 0xfffe;
btmrvl_check_device_tree(priv);
@@ -643,7 +649,8 @@ static int btmrvl_service_main_thread(void *data)
if (adapter->ps_state == PS_SLEEP)
continue;
- if (!priv->btmrvl_dev.tx_dnld_rdy)
+ if (!priv->btmrvl_dev.tx_dnld_rdy ||
+ priv->adapter->is_suspended)
continue;
skb = skb_dequeue(&adapter->tx_queue);
diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c
index 71ea2a3af293..6ed8acfcfa9c 100644
--- a/drivers/bluetooth/btmrvl_sdio.c
+++ b/drivers/bluetooth/btmrvl_sdio.c
@@ -698,7 +698,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
case HCI_ACLDATA_PKT:
case HCI_SCODATA_PKT:
case HCI_EVENT_PKT:
- bt_cb(skb)->pkt_type = type;
+ hci_skb_pkt_type(skb) = type;
skb_put(skb, buf_len);
skb_pull(skb, SDIO_HEADER_LEN);
@@ -713,7 +713,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
break;
case MRVL_VENDOR_PKT:
- bt_cb(skb)->pkt_type = HCI_VENDOR_PKT;
+ hci_skb_pkt_type(skb) = HCI_VENDOR_PKT;
skb_put(skb, buf_len);
skb_pull(skb, SDIO_HEADER_LEN);
@@ -1112,7 +1112,8 @@ static int btmrvl_sdio_download_fw(struct btmrvl_sdio_card *card)
*/
if (btmrvl_sdio_verify_fw_download(card, pollnum)) {
BT_ERR("FW failed to be active in time!");
- return -ETIMEDOUT;
+ ret = -ETIMEDOUT;
+ goto done;
}
sdio_release_host(card->func);
@@ -1544,10 +1545,10 @@ static int btmrvl_sdio_suspend(struct device *dev)
}
priv = card->priv;
+ priv->adapter->is_suspending = true;
hcidev = priv->btmrvl_dev.hcidev;
BT_DBG("%s: SDIO suspend", hcidev->name);
hci_suspend_dev(hcidev);
- skb_queue_purge(&priv->adapter->tx_queue);
if (priv->adapter->hs_state != HS_ACTIVATED) {
if (btmrvl_enable_hs(priv)) {
@@ -1556,6 +1557,7 @@ static int btmrvl_sdio_suspend(struct device *dev)
}
}
+ priv->adapter->is_suspending = false;
priv->adapter->is_suspended = true;
/* We will keep the power when hs enabled successfully */
diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c
index 7b624423a7e8..2b05661e3818 100644
--- a/drivers/bluetooth/btsdio.c
+++ b/drivers/bluetooth/btsdio.c
@@ -86,7 +86,7 @@ static int btsdio_tx_packet(struct btsdio_data *data, struct sk_buff *skb)
skb->data[0] = (skb->len & 0x0000ff);
skb->data[1] = (skb->len & 0x00ff00) >> 8;
skb->data[2] = (skb->len & 0xff0000) >> 16;
- skb->data[3] = bt_cb(skb)->pkt_type;
+ skb->data[3] = hci_skb_pkt_type(skb);
err = sdio_writesb(data->func, REG_TDAT, skb->data, skb->len);
if (err < 0) {
@@ -158,7 +158,7 @@ static int btsdio_rx_packet(struct btsdio_data *data)
data->hdev->stat.byte_rx += len;
- bt_cb(skb)->pkt_type = hdr[3];
+ hci_skb_pkt_type(skb) = hdr[3];
err = hci_recv_frame(data->hdev, skb);
if (err < 0)
@@ -252,7 +252,7 @@ static int btsdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s", hdev->name);
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c
index bb8e4025fb9e..9624b29f8349 100644
--- a/drivers/bluetooth/btuart_cs.c
+++ b/drivers/bluetooth/btuart_cs.c
@@ -200,9 +200,9 @@ static void btuart_receive(struct btuart_info *info)
if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
- bt_cb(info->rx_skb)->pkt_type = inb(iobase + UART_RX);
+ hci_skb_pkt_type(info->rx_skb) = inb(iobase + UART_RX);
- switch (bt_cb(info->rx_skb)->pkt_type) {
+ switch (hci_skb_pkt_type(info->rx_skb)) {
case HCI_EVENT_PKT:
info->rx_state = RECV_WAIT_EVENT_HEADER;
@@ -221,7 +221,8 @@ static void btuart_receive(struct btuart_info *info)
default:
/* Unknown packet */
- BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+ BT_ERR("Unknown HCI packet with type 0x%02x received",
+ hci_skb_pkt_type(info->rx_skb));
info->hdev->stat.err_rx++;
kfree_skb(info->rx_skb);
@@ -424,7 +425,7 @@ static int btuart_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btuart_info *info = hci_get_drvdata(hdev);
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
@@ -437,7 +438,7 @@ static int btuart_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
}
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&(info->txq), skb);
btuart_write_wakeup(info);
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 92f0ee388f9e..a191e318fab8 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -153,6 +153,10 @@ static const struct usb_device_id btusb_table[] = {
{ USB_VENDOR_AND_INTERFACE_INFO(0x13d3, 0xff, 0x01, 0x01),
.driver_info = BTUSB_BCM_PATCHRAM },
+ /* Toshiba Corp - Broadcom based */
+ { USB_VENDOR_AND_INTERFACE_INFO(0x0930, 0xff, 0x01, 0x01),
+ .driver_info = BTUSB_BCM_PATCHRAM },
+
/* Intel Bluetooth USB Bootloader (RAM module) */
{ USB_DEVICE(0x8087, 0x0a5a),
.driver_info = BTUSB_INTEL_BOOT | BTUSB_BROKEN_ISOC },
@@ -437,22 +441,22 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
break;
}
- bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
- bt_cb(skb)->expect = HCI_EVENT_HDR_SIZE;
+ hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
+ hci_skb_expect(skb) = HCI_EVENT_HDR_SIZE;
}
- len = min_t(uint, bt_cb(skb)->expect, count);
+ len = min_t(uint, hci_skb_expect(skb), count);
memcpy(skb_put(skb, len), buffer, len);
count -= len;
buffer += len;
- bt_cb(skb)->expect -= len;
+ hci_skb_expect(skb) -= len;
if (skb->len == HCI_EVENT_HDR_SIZE) {
/* Complete event header */
- bt_cb(skb)->expect = hci_event_hdr(skb)->plen;
+ hci_skb_expect(skb) = hci_event_hdr(skb)->plen;
- if (skb_tailroom(skb) < bt_cb(skb)->expect) {
+ if (skb_tailroom(skb) < hci_skb_expect(skb)) {
kfree_skb(skb);
skb = NULL;
@@ -461,7 +465,7 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
}
}
- if (bt_cb(skb)->expect == 0) {
+ if (!hci_skb_expect(skb)) {
/* Complete frame */
data->recv_event(data->hdev, skb);
skb = NULL;
@@ -492,24 +496,24 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
break;
}
- bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
- bt_cb(skb)->expect = HCI_ACL_HDR_SIZE;
+ hci_skb_pkt_type(skb) = HCI_ACLDATA_PKT;
+ hci_skb_expect(skb) = HCI_ACL_HDR_SIZE;
}
- len = min_t(uint, bt_cb(skb)->expect, count);
+ len = min_t(uint, hci_skb_expect(skb), count);
memcpy(skb_put(skb, len), buffer, len);
count -= len;
buffer += len;
- bt_cb(skb)->expect -= len;
+ hci_skb_expect(skb) -= len;
if (skb->len == HCI_ACL_HDR_SIZE) {
__le16 dlen = hci_acl_hdr(skb)->dlen;
/* Complete ACL header */
- bt_cb(skb)->expect = __le16_to_cpu(dlen);
+ hci_skb_expect(skb) = __le16_to_cpu(dlen);
- if (skb_tailroom(skb) < bt_cb(skb)->expect) {
+ if (skb_tailroom(skb) < hci_skb_expect(skb)) {
kfree_skb(skb);
skb = NULL;
@@ -518,7 +522,7 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
}
}
- if (bt_cb(skb)->expect == 0) {
+ if (!hci_skb_expect(skb)) {
/* Complete frame */
hci_recv_frame(data->hdev, skb);
skb = NULL;
@@ -549,22 +553,22 @@ static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count)
break;
}
- bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
- bt_cb(skb)->expect = HCI_SCO_HDR_SIZE;
+ hci_skb_pkt_type(skb) = HCI_SCODATA_PKT;
+ hci_skb_expect(skb) = HCI_SCO_HDR_SIZE;
}
- len = min_t(uint, bt_cb(skb)->expect, count);
+ len = min_t(uint, hci_skb_expect(skb), count);
memcpy(skb_put(skb, len), buffer, len);
count -= len;
buffer += len;
- bt_cb(skb)->expect -= len;
+ hci_skb_expect(skb) -= len;
if (skb->len == HCI_SCO_HDR_SIZE) {
/* Complete SCO header */
- bt_cb(skb)->expect = hci_sco_hdr(skb)->dlen;
+ hci_skb_expect(skb) = hci_sco_hdr(skb)->dlen;
- if (skb_tailroom(skb) < bt_cb(skb)->expect) {
+ if (skb_tailroom(skb) < hci_skb_expect(skb)) {
kfree_skb(skb);
skb = NULL;
@@ -573,7 +577,7 @@ static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count)
}
}
- if (bt_cb(skb)->expect == 0) {
+ if (!hci_skb_expect(skb)) {
/* Complete frame */
hci_recv_frame(data->hdev, skb);
skb = NULL;
@@ -1257,7 +1261,7 @@ static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s", hdev->name);
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
urb = alloc_ctrl_urb(hdev, skb);
if (IS_ERR(urb))
@@ -1642,13 +1646,8 @@ static int btusb_setup_intel(struct hci_dev *hdev)
struct sk_buff *skb;
const struct firmware *fw;
const u8 *fw_ptr;
- int disable_patch;
- struct intel_version *ver;
-
- const u8 mfg_enable[] = { 0x01, 0x00 };
- const u8 mfg_disable[] = { 0x00, 0x00 };
- const u8 mfg_reset_deactivate[] = { 0x00, 0x01 };
- const u8 mfg_reset_activate[] = { 0x00, 0x02 };
+ int disable_patch, err;
+ struct intel_version ver;
BT_DBG("%s", hdev->name);
@@ -1674,35 +1673,22 @@ static int btusb_setup_intel(struct hci_dev *hdev)
* The returned information are hardware variant and revision plus
* firmware variant, revision and build number.
*/
- skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- BT_ERR("%s reading Intel fw version command failed (%ld)",
- hdev->name, PTR_ERR(skb));
- return PTR_ERR(skb);
- }
-
- if (skb->len != sizeof(*ver)) {
- BT_ERR("%s Intel version event length mismatch", hdev->name);
- kfree_skb(skb);
- return -EIO;
- }
-
- ver = (struct intel_version *)skb->data;
+ err = btintel_read_version(hdev, &ver);
+ if (err)
+ return err;
BT_INFO("%s: read Intel version: %02x%02x%02x%02x%02x%02x%02x%02x%02x",
- hdev->name, ver->hw_platform, ver->hw_variant,
- ver->hw_revision, ver->fw_variant, ver->fw_revision,
- ver->fw_build_num, ver->fw_build_ww, ver->fw_build_yy,
- ver->fw_patch_num);
+ hdev->name, ver.hw_platform, ver.hw_variant, ver.hw_revision,
+ ver.fw_variant, ver.fw_revision, ver.fw_build_num,
+ ver.fw_build_ww, ver.fw_build_yy, ver.fw_patch_num);
/* fw_patch_num indicates the version of patch the device currently
* have. If there is no patch data in the device, it is always 0x00.
* So, if it is other than 0x00, no need to patch the device again.
*/
- if (ver->fw_patch_num) {
+ if (ver.fw_patch_num) {
BT_INFO("%s: Intel device is already patched. patch num: %02x",
- hdev->name, ver->fw_patch_num);
- kfree_skb(skb);
+ hdev->name, ver.fw_patch_num);
goto complete;
}
@@ -1712,31 +1698,21 @@ static int btusb_setup_intel(struct hci_dev *hdev)
* If no patch file is found, allow the device to operate without
* a patch.
*/
- fw = btusb_setup_intel_get_fw(hdev, ver);
- if (!fw) {
- kfree_skb(skb);
+ fw = btusb_setup_intel_get_fw(hdev, &ver);
+ if (!fw)
goto complete;
- }
fw_ptr = fw->data;
- kfree_skb(skb);
-
- /* This Intel specific command enables the manufacturer mode of the
- * controller.
- *
+ /* Enable the manufacturer mode of the controller.
* Only while this mode is enabled, the driver can download the
* firmware patch data and configuration parameters.
*/
- skb = __hci_cmd_sync(hdev, 0xfc11, 2, mfg_enable, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- BT_ERR("%s entering Intel manufacturer mode failed (%ld)",
- hdev->name, PTR_ERR(skb));
+ err = btintel_enter_mfg(hdev);
+ if (err) {
release_firmware(fw);
- return PTR_ERR(skb);
+ return err;
}
- kfree_skb(skb);
-
disable_patch = 1;
/* The firmware data file consists of list of Intel specific HCI
@@ -1776,14 +1752,9 @@ static int btusb_setup_intel(struct hci_dev *hdev)
/* Patching completed successfully and disable the manufacturer mode
* with reset and activate the downloaded firmware patches.
*/
- skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_reset_activate),
- mfg_reset_activate, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
- hdev->name, PTR_ERR(skb));
- return PTR_ERR(skb);
- }
- kfree_skb(skb);
+ err = btintel_exit_mfg(hdev, true, true);
+ if (err)
+ return err;
BT_INFO("%s: Intel Bluetooth firmware patch completed and activated",
hdev->name);
@@ -1792,14 +1763,9 @@ static int btusb_setup_intel(struct hci_dev *hdev)
exit_mfg_disable:
/* Disable the manufacturer mode without reset */
- skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_disable), mfg_disable,
- HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
- hdev->name, PTR_ERR(skb));
- return PTR_ERR(skb);
- }
- kfree_skb(skb);
+ err = btintel_exit_mfg(hdev, false, false);
+ if (err)
+ return err;
BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name);
@@ -1811,14 +1777,9 @@ exit_mfg_deactivate:
/* Patching failed. Disable the manufacturer mode with reset and
* deactivate the downloaded firmware patches.
*/
- skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_reset_deactivate),
- mfg_reset_deactivate, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
- hdev->name, PTR_ERR(skb));
- return PTR_ERR(skb);
- }
- kfree_skb(skb);
+ err = btintel_exit_mfg(hdev, true, false);
+ if (err)
+ return err;
BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated",
hdev->name);
@@ -1853,7 +1814,7 @@ static int inject_cmd_complete(struct hci_dev *hdev, __u16 opcode)
*skb_put(skb, 1) = 0x00;
- bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+ hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
return hci_recv_frame(hdev, skb);
}
@@ -1945,7 +1906,7 @@ static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s", hdev->name);
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
if (test_bit(BTUSB_BOOTLOADER, &data->flags)) {
struct hci_command_hdr *cmd = (void *)skb->data;
@@ -2005,7 +1966,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
0x00, 0x08, 0x04, 0x00 };
struct btusb_data *data = hci_get_drvdata(hdev);
struct sk_buff *skb;
- struct intel_version *ver;
+ struct intel_version ver;
struct intel_boot_params *params;
const struct firmware *fw;
const u8 *fw_ptr;
@@ -2023,28 +1984,16 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
* is in bootloader mode or if it already has operational firmware
* loaded.
*/
- skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- BT_ERR("%s: Reading Intel version information failed (%ld)",
- hdev->name, PTR_ERR(skb));
- return PTR_ERR(skb);
- }
-
- if (skb->len != sizeof(*ver)) {
- BT_ERR("%s: Intel version event size mismatch", hdev->name);
- kfree_skb(skb);
- return -EILSEQ;
- }
-
- ver = (struct intel_version *)skb->data;
+ err = btintel_read_version(hdev, &ver);
+ if (err)
+ return err;
/* The hardware platform number has a fixed value of 0x37 and
* for now only accept this single value.
*/
- if (ver->hw_platform != 0x37) {
+ if (ver.hw_platform != 0x37) {
BT_ERR("%s: Unsupported Intel hardware platform (%u)",
- hdev->name, ver->hw_platform);
- kfree_skb(skb);
+ hdev->name, ver.hw_platform);
return -EINVAL;
}
@@ -2053,14 +2002,13 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
* put in place to ensure correct forward compatibility options
* when newer hardware variants come along.
*/
- if (ver->hw_variant != 0x0b) {
+ if (ver.hw_variant != 0x0b) {
BT_ERR("%s: Unsupported Intel hardware variant (%u)",
- hdev->name, ver->hw_variant);
- kfree_skb(skb);
+ hdev->name, ver.hw_variant);
return -EINVAL;
}
- btintel_version_info(hdev, ver);
+ btintel_version_info(hdev, &ver);
/* The firmware variant determines if the device is in bootloader
* mode or is running operational firmware. The value 0x06 identifies
@@ -2075,8 +2023,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
* It is not possible to use the Secure Boot Parameters in this
* case since that command is only available in bootloader mode.
*/
- if (ver->fw_variant == 0x23) {
- kfree_skb(skb);
+ if (ver.fw_variant == 0x23) {
clear_bit(BTUSB_BOOTLOADER, &data->flags);
btintel_check_bdaddr(hdev);
return 0;
@@ -2085,15 +2032,12 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
/* If the device is not in bootloader mode, then the only possible
* choice is to return an error and abort the device initialization.
*/
- if (ver->fw_variant != 0x06) {
+ if (ver.fw_variant != 0x06) {
BT_ERR("%s: Unsupported Intel firmware variant (%u)",
- hdev->name, ver->fw_variant);
- kfree_skb(skb);
+ hdev->name, ver.fw_variant);
return -ENODEV;
}
- kfree_skb(skb);
-
/* Read the secure boot parameters to identify the operating
* details of the bootloader.
*/
diff --git a/drivers/bluetooth/btwilink.c b/drivers/bluetooth/btwilink.c
index 57eb935aedc7..24a652f9252b 100644
--- a/drivers/bluetooth/btwilink.c
+++ b/drivers/bluetooth/btwilink.c
@@ -249,10 +249,10 @@ static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
hst = hci_get_drvdata(hdev);
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
- BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type,
- skb->len);
+ BT_DBG("%s: type %d len %d", hdev->name, hci_skb_pkt_type(skb),
+ skb->len);
/* Insert skb to shared transport layer's transmit queue.
* Freeing skb memory is taken care in shared transport layer,
@@ -268,7 +268,7 @@ static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
/* ST accepted our skb. So, Go ahead and do rest */
hdev->stat.byte_tx += len;
- ti_st_tx_complete(hst, bt_cb(skb)->pkt_type);
+ ti_st_tx_complete(hst, hci_skb_pkt_type(skb));
return 0;
}
diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c
index 5026f66fac88..6317c6f323bf 100644
--- a/drivers/bluetooth/dtl1_cs.c
+++ b/drivers/bluetooth/dtl1_cs.c
@@ -239,7 +239,7 @@ static void dtl1_receive(struct dtl1_info *info)
info->rx_count = nsh->len + (nsh->len & 0x0001);
break;
case RECV_WAIT_DATA:
- bt_cb(info->rx_skb)->pkt_type = nsh->type;
+ hci_skb_pkt_type(info->rx_skb) = nsh->type;
/* remove PAD byte if it exists */
if (nsh->len & 0x0001) {
@@ -250,7 +250,7 @@ static void dtl1_receive(struct dtl1_info *info)
/* remove NSH */
skb_pull(info->rx_skb, NSHL);
- switch (bt_cb(info->rx_skb)->pkt_type) {
+ switch (hci_skb_pkt_type(info->rx_skb)) {
case 0x80:
/* control data for the Nokia Card */
dtl1_control(info, info->rx_skb);
@@ -259,12 +259,13 @@ static void dtl1_receive(struct dtl1_info *info)
case 0x83:
case 0x84:
/* send frame to the HCI layer */
- bt_cb(info->rx_skb)->pkt_type &= 0x0f;
+ hci_skb_pkt_type(info->rx_skb) &= 0x0f;
hci_recv_frame(info->hdev, info->rx_skb);
break;
default:
/* unknown packet */
- BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+ BT_ERR("Unknown HCI packet with type 0x%02x received",
+ hci_skb_pkt_type(info->rx_skb));
kfree_skb(info->rx_skb);
break;
}
@@ -386,7 +387,7 @@ static int dtl1_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
struct sk_buff *s;
struct nsh nsh;
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
nsh.type = 0x81;
diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
index d776dfd51478..0ccf6bf01ed4 100644
--- a/drivers/bluetooth/hci_ath.c
+++ b/drivers/bluetooth/hci_ath.c
@@ -205,7 +205,7 @@ static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{
struct ath_struct *ath = hu->priv;
- if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) {
+ if (hci_skb_pkt_type(skb) == HCI_SCODATA_PKT) {
kfree_skb(skb);
return 0;
}
@@ -213,7 +213,7 @@ static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb)
/* Update power management enable flag with parameters of
* HCI sleep enable vendor specific HCI command.
*/
- if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
+ if (hci_skb_pkt_type(skb) == HCI_COMMAND_PKT) {
struct hci_command_hdr *hdr = (void *)skb->data;
if (__le16_to_cpu(hdr->opcode) == HCI_OP_ATH_SLEEP)
@@ -223,7 +223,7 @@ static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb)
BT_DBG("hu %p skb %p", hu, skb);
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&ath->txq, skb);
set_bit(HCI_UART_SENDING, &hu->tx_state);
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
index cb852cc750b7..5f3de181e744 100644
--- a/drivers/bluetooth/hci_bcm.c
+++ b/drivers/bluetooth/hci_bcm.c
@@ -472,7 +472,7 @@ static int bcm_enqueue(struct hci_uart *hu, struct sk_buff *skb)
bt_dev_dbg(hu->hdev, "hu %p skb %p", hu, skb);
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&bcm->txq, skb);
return 0;
@@ -814,8 +814,16 @@ static const struct hci_uart_proto bcm_proto = {
#ifdef CONFIG_ACPI
static const struct acpi_device_id bcm_acpi_match[] = {
+ { "BCM2E1A", 0 },
{ "BCM2E39", 0 },
+ { "BCM2E3A", 0 },
+ { "BCM2E3D", 0 },
+ { "BCM2E3F", 0 },
+ { "BCM2E40", 0 },
+ { "BCM2E64", 0 },
+ { "BCM2E65", 0 },
{ "BCM2E67", 0 },
+ { "BCM2E7B", 0 },
{ },
};
MODULE_DEVICE_TABLE(acpi, bcm_acpi_match);
diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c
index d0b615a932d1..064f2fefad62 100644
--- a/drivers/bluetooth/hci_bcsp.c
+++ b/drivers/bluetooth/hci_bcsp.c
@@ -155,7 +155,7 @@ static int bcsp_enqueue(struct hci_uart *hu, struct sk_buff *skb)
return 0;
}
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_ACLDATA_PKT:
case HCI_COMMAND_PKT:
skb_queue_tail(&bcsp->rel, skb);
@@ -231,7 +231,7 @@ static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data,
if (!nskb)
return NULL;
- bt_cb(nskb)->pkt_type = pkt_type;
+ hci_skb_pkt_type(nskb) = pkt_type;
bcsp_slip_msgdelim(nskb);
@@ -291,7 +291,10 @@ static struct sk_buff *bcsp_dequeue(struct hci_uart *hu)
skb = skb_dequeue(&bcsp->unrel);
if (skb != NULL) {
- struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, bt_cb(skb)->pkt_type);
+ struct sk_buff *nskb;
+
+ nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len,
+ hci_skb_pkt_type(skb));
if (nskb) {
kfree_skb(skb);
return nskb;
@@ -310,8 +313,10 @@ static struct sk_buff *bcsp_dequeue(struct hci_uart *hu)
if (bcsp->unack.qlen < BCSP_TXWINSIZE) {
skb = skb_dequeue(&bcsp->rel);
if (skb != NULL) {
- struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len,
- bt_cb(skb)->pkt_type);
+ struct sk_buff *nskb;
+
+ nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len,
+ hci_skb_pkt_type(skb));
if (nskb) {
__skb_queue_tail(&bcsp->unack, skb);
mod_timer(&bcsp->tbcsp, jiffies + HZ / 4);
@@ -412,7 +417,7 @@ static void bcsp_handle_le_pkt(struct hci_uart *hu)
if (!nskb)
return;
memcpy(skb_put(nskb, 4), conf_rsp_pkt, 4);
- bt_cb(nskb)->pkt_type = BCSP_LE_PKT;
+ hci_skb_pkt_type(nskb) = BCSP_LE_PKT;
skb_queue_head(&bcsp->unrel, nskb);
hci_uart_tx_wakeup(hu);
@@ -494,14 +499,14 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu)
bcsp_pkt_cull(bcsp);
if ((bcsp->rx_skb->data[1] & 0x0f) == 6 &&
bcsp->rx_skb->data[0] & 0x80) {
- bt_cb(bcsp->rx_skb)->pkt_type = HCI_ACLDATA_PKT;
+ hci_skb_pkt_type(bcsp->rx_skb) = HCI_ACLDATA_PKT;
pass_up = 1;
} else if ((bcsp->rx_skb->data[1] & 0x0f) == 5 &&
bcsp->rx_skb->data[0] & 0x80) {
- bt_cb(bcsp->rx_skb)->pkt_type = HCI_EVENT_PKT;
+ hci_skb_pkt_type(bcsp->rx_skb) = HCI_EVENT_PKT;
pass_up = 1;
} else if ((bcsp->rx_skb->data[1] & 0x0f) == 7) {
- bt_cb(bcsp->rx_skb)->pkt_type = HCI_SCODATA_PKT;
+ hci_skb_pkt_type(bcsp->rx_skb) = HCI_SCODATA_PKT;
pass_up = 1;
} else if ((bcsp->rx_skb->data[1] & 0x0f) == 1 &&
!(bcsp->rx_skb->data[0] & 0x80)) {
@@ -523,7 +528,7 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu)
hdr.evt = 0xff;
hdr.plen = bcsp->rx_skb->len;
memcpy(skb_push(bcsp->rx_skb, HCI_EVENT_HDR_SIZE), &hdr, HCI_EVENT_HDR_SIZE);
- bt_cb(bcsp->rx_skb)->pkt_type = HCI_EVENT_PKT;
+ hci_skb_pkt_type(bcsp->rx_skb) = HCI_EVENT_PKT;
hci_recv_frame(hu->hdev, bcsp->rx_skb);
} else {
diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c
index a6fce48da0fb..635597b6e168 100644
--- a/drivers/bluetooth/hci_h4.c
+++ b/drivers/bluetooth/hci_h4.c
@@ -108,7 +108,7 @@ static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb)
BT_DBG("hu %p skb %p", hu, skb);
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&h4->txq, skb);
return 0;
@@ -184,8 +184,8 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
if (!skb)
return ERR_PTR(-ENOMEM);
- bt_cb(skb)->pkt_type = (&pkts[i])->type;
- bt_cb(skb)->expect = (&pkts[i])->hlen;
+ hci_skb_pkt_type(skb) = (&pkts[i])->type;
+ hci_skb_expect(skb) = (&pkts[i])->hlen;
break;
}
@@ -197,18 +197,18 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
buffer += 1;
}
- len = min_t(uint, bt_cb(skb)->expect - skb->len, count);
+ len = min_t(uint, hci_skb_expect(skb) - skb->len, count);
memcpy(skb_put(skb, len), buffer, len);
count -= len;
buffer += len;
/* Check for partial packet */
- if (skb->len < bt_cb(skb)->expect)
+ if (skb->len < hci_skb_expect(skb))
continue;
for (i = 0; i < pkts_count; i++) {
- if (bt_cb(skb)->pkt_type == (&pkts[i])->type)
+ if (hci_skb_pkt_type(skb) == (&pkts[i])->type)
break;
}
@@ -228,7 +228,7 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
case 1:
/* Single octet variable length */
dlen = skb->data[(&pkts[i])->loff];
- bt_cb(skb)->expect += dlen;
+ hci_skb_expect(skb) += dlen;
if (skb_tailroom(skb) < dlen) {
kfree_skb(skb);
@@ -239,7 +239,7 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
/* Double octet variable length */
dlen = get_unaligned_le16(skb->data +
(&pkts[i])->loff);
- bt_cb(skb)->expect += dlen;
+ hci_skb_expect(skb) += dlen;
if (skb_tailroom(skb) < dlen) {
kfree_skb(skb);
diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
index abee2216fdeb..0879d64b1caf 100644
--- a/drivers/bluetooth/hci_h5.c
+++ b/drivers/bluetooth/hci_h5.c
@@ -51,7 +51,7 @@
#define H5_HDR_CRC(hdr) (((hdr)[0] >> 6) & 0x01)
#define H5_HDR_RELIABLE(hdr) (((hdr)[0] >> 7) & 0x01)
#define H5_HDR_PKT_TYPE(hdr) ((hdr)[1] & 0x0f)
-#define H5_HDR_LEN(hdr) ((((hdr)[1] >> 4) & 0xff) + ((hdr)[2] << 4))
+#define H5_HDR_LEN(hdr) ((((hdr)[1] >> 4) & 0x0f) + ((hdr)[2] << 4))
#define SLIP_DELIMITER 0xc0
#define SLIP_ESC 0xdb
@@ -107,7 +107,7 @@ static void h5_link_control(struct hci_uart *hu, const void *data, size_t len)
if (!nskb)
return;
- bt_cb(nskb)->pkt_type = HCI_3WIRE_LINK_PKT;
+ hci_skb_pkt_type(nskb) = HCI_3WIRE_LINK_PKT;
memcpy(skb_put(nskb, len), data, len);
@@ -116,18 +116,14 @@ static void h5_link_control(struct hci_uart *hu, const void *data, size_t len)
static u8 h5_cfg_field(struct h5 *h5)
{
- u8 field = 0;
-
/* Sliding window size (first 3 bits) */
- field |= (h5->tx_win & 7);
-
- return field;
+ return h5->tx_win & 0x07;
}
static void h5_timed_event(unsigned long arg)
{
const unsigned char sync_req[] = { 0x01, 0x7e };
- unsigned char conf_req[] = { 0x03, 0xfc, 0x01 };
+ unsigned char conf_req[3] = { 0x03, 0xfc };
struct hci_uart *hu = (struct hci_uart *)arg;
struct h5 *h5 = hu->priv;
struct sk_buff *skb;
@@ -285,7 +281,7 @@ static void h5_handle_internal_rx(struct hci_uart *hu)
struct h5 *h5 = hu->priv;
const unsigned char sync_req[] = { 0x01, 0x7e };
const unsigned char sync_rsp[] = { 0x02, 0x7d };
- unsigned char conf_req[] = { 0x03, 0xfc, 0x01 };
+ unsigned char conf_req[3] = { 0x03, 0xfc };
const unsigned char conf_rsp[] = { 0x04, 0x7b };
const unsigned char wakeup_req[] = { 0x05, 0xfa };
const unsigned char woken_req[] = { 0x06, 0xf9 };
@@ -317,7 +313,7 @@ static void h5_handle_internal_rx(struct hci_uart *hu)
h5_link_control(hu, conf_req, 3);
} else if (memcmp(data, conf_rsp, 2) == 0) {
if (H5_HDR_LEN(hdr) > 2)
- h5->tx_win = (data[2] & 7);
+ h5->tx_win = (data[2] & 0x07);
BT_DBG("Three-wire init complete. tx_win %u", h5->tx_win);
h5->state = H5_ACTIVE;
hci_uart_init_ready(hu);
@@ -360,7 +356,7 @@ static void h5_complete_rx_pkt(struct hci_uart *hu)
case HCI_EVENT_PKT:
case HCI_ACLDATA_PKT:
case HCI_SCODATA_PKT:
- bt_cb(h5->rx_skb)->pkt_type = H5_HDR_PKT_TYPE(hdr);
+ hci_skb_pkt_type(h5->rx_skb) = H5_HDR_PKT_TYPE(hdr);
/* Remove Three-wire header */
skb_pull(h5->rx_skb, 4);
@@ -562,7 +558,7 @@ static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb)
return 0;
}
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_ACLDATA_PKT:
case HCI_COMMAND_PKT:
skb_queue_tail(&h5->rel, skb);
@@ -573,7 +569,7 @@ static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb)
break;
default:
- BT_ERR("Unknown packet type %u", bt_cb(skb)->pkt_type);
+ BT_ERR("Unknown packet type %u", hci_skb_pkt_type(skb));
kfree_skb(skb);
break;
}
@@ -642,7 +638,7 @@ static struct sk_buff *h5_prepare_pkt(struct hci_uart *hu, u8 pkt_type,
if (!nskb)
return NULL;
- bt_cb(nskb)->pkt_type = pkt_type;
+ hci_skb_pkt_type(nskb) = pkt_type;
h5_slip_delim(nskb);
@@ -697,7 +693,7 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu)
skb = skb_dequeue(&h5->unrel);
if (skb) {
- nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
+ nskb = h5_prepare_pkt(hu, hci_skb_pkt_type(skb),
skb->data, skb->len);
if (nskb) {
kfree_skb(skb);
@@ -715,7 +711,7 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu)
skb = skb_dequeue(&h5->rel);
if (skb) {
- nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
+ nskb = h5_prepare_pkt(hu, hci_skb_pkt_type(skb),
skb->data, skb->len);
if (nskb) {
__skb_queue_tail(&h5->unack, skb);
diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c
index 4a414a5a3165..3d63ea37bd4c 100644
--- a/drivers/bluetooth/hci_intel.c
+++ b/drivers/bluetooth/hci_intel.c
@@ -186,7 +186,7 @@ static int intel_lpm_suspend(struct hci_uart *hu)
}
memcpy(skb_put(skb, sizeof(suspend)), suspend, sizeof(suspend));
- bt_cb(skb)->pkt_type = HCI_LPM_PKT;
+ hci_skb_pkt_type(skb) = HCI_LPM_PKT;
set_bit(STATE_LPM_TRANSACTION, &intel->flags);
@@ -230,7 +230,7 @@ static int intel_lpm_resume(struct hci_uart *hu)
return -ENOMEM;
}
- bt_cb(skb)->pkt_type = HCI_LPM_WAKE_PKT;
+ hci_skb_pkt_type(skb) = HCI_LPM_WAKE_PKT;
set_bit(STATE_LPM_TRANSACTION, &intel->flags);
@@ -272,7 +272,7 @@ static int intel_lpm_host_wake(struct hci_uart *hu)
memcpy(skb_put(skb, sizeof(lpm_resume_ack)), lpm_resume_ack,
sizeof(lpm_resume_ack));
- bt_cb(skb)->pkt_type = HCI_LPM_PKT;
+ hci_skb_pkt_type(skb) = HCI_LPM_PKT;
/* LPM flow is a priority, enqueue packet at list head */
skb_queue_head(&intel->txq, skb);
@@ -467,7 +467,7 @@ static int inject_cmd_complete(struct hci_dev *hdev, __u16 opcode)
*skb_put(skb, 1) = 0x00;
- bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+ hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
return hci_recv_frame(hdev, skb);
}
@@ -502,7 +502,7 @@ static int intel_set_baudrate(struct hci_uart *hu, unsigned int speed)
/* Device will not accept speed change if Intel version has not been
* previously requested.
*/
- skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
+ skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_CMD_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Reading Intel version information failed (%ld)",
PTR_ERR(skb));
@@ -517,7 +517,7 @@ static int intel_set_baudrate(struct hci_uart *hu, unsigned int speed)
}
memcpy(skb_put(skb, sizeof(speed_cmd)), speed_cmd, sizeof(speed_cmd));
- bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+ hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
hci_uart_set_flow_control(hu, true);
@@ -542,7 +542,7 @@ static int intel_setup(struct hci_uart *hu)
struct intel_device *idev = NULL;
struct hci_dev *hdev = hu->hdev;
struct sk_buff *skb;
- struct intel_version *ver;
+ struct intel_version ver;
struct intel_boot_params *params;
struct list_head *p;
const struct firmware *fw;
@@ -590,35 +590,16 @@ static int intel_setup(struct hci_uart *hu)
* is in bootloader mode or if it already has operational firmware
* loaded.
*/
- skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- bt_dev_err(hdev, "Reading Intel version information failed (%ld)",
- PTR_ERR(skb));
- return PTR_ERR(skb);
- }
-
- if (skb->len != sizeof(*ver)) {
- bt_dev_err(hdev, "Intel version event size mismatch");
- kfree_skb(skb);
- return -EILSEQ;
- }
-
- ver = (struct intel_version *)skb->data;
- if (ver->status) {
- bt_dev_err(hdev, "Intel version command failure (%02x)",
- ver->status);
- err = -bt_to_errno(ver->status);
- kfree_skb(skb);
+ err = btintel_read_version(hdev, &ver);
+ if (err)
return err;
- }
/* The hardware platform number has a fixed value of 0x37 and
* for now only accept this single value.
*/
- if (ver->hw_platform != 0x37) {
+ if (ver.hw_platform != 0x37) {
bt_dev_err(hdev, "Unsupported Intel hardware platform (%u)",
- ver->hw_platform);
- kfree_skb(skb);
+ ver.hw_platform);
return -EINVAL;
}
@@ -627,14 +608,13 @@ static int intel_setup(struct hci_uart *hu)
* put in place to ensure correct forward compatibility options
* when newer hardware variants come along.
*/
- if (ver->hw_variant != 0x0b) {
+ if (ver.hw_variant != 0x0b) {
bt_dev_err(hdev, "Unsupported Intel hardware variant (%u)",
- ver->hw_variant);
- kfree_skb(skb);
+ ver.hw_variant);
return -EINVAL;
}
- btintel_version_info(hdev, ver);
+ btintel_version_info(hdev, &ver);
/* The firmware variant determines if the device is in bootloader
* mode or is running operational firmware. The value 0x06 identifies
@@ -649,8 +629,7 @@ static int intel_setup(struct hci_uart *hu)
* It is not possible to use the Secure Boot Parameters in this
* case since that command is only available in bootloader mode.
*/
- if (ver->fw_variant == 0x23) {
- kfree_skb(skb);
+ if (ver.fw_variant == 0x23) {
clear_bit(STATE_BOOTLOADER, &intel->flags);
btintel_check_bdaddr(hdev);
return 0;
@@ -659,19 +638,16 @@ static int intel_setup(struct hci_uart *hu)
/* If the device is not in bootloader mode, then the only possible
* choice is to return an error and abort the device initialization.
*/
- if (ver->fw_variant != 0x06) {
+ if (ver.fw_variant != 0x06) {
bt_dev_err(hdev, "Unsupported Intel firmware variant (%u)",
- ver->fw_variant);
- kfree_skb(skb);
+ ver.fw_variant);
return -ENODEV;
}
- kfree_skb(skb);
-
/* Read the secure boot parameters to identify the operating
* details of the bootloader.
*/
- skb = __hci_cmd_sync(hdev, 0xfc0d, 0, NULL, HCI_INIT_TIMEOUT);
+ skb = __hci_cmd_sync(hdev, 0xfc0d, 0, NULL, HCI_CMD_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Reading Intel boot parameters failed (%ld)",
PTR_ERR(skb));
@@ -881,7 +857,7 @@ done:
set_bit(STATE_BOOTING, &intel->flags);
skb = __hci_cmd_sync(hdev, 0xfc01, sizeof(reset_param), reset_param,
- HCI_INIT_TIMEOUT);
+ HCI_CMD_TIMEOUT);
if (IS_ERR(skb))
return PTR_ERR(skb);
@@ -1126,7 +1102,7 @@ static struct sk_buff *intel_dequeue(struct hci_uart *hu)
return skb;
if (test_bit(STATE_BOOTLOADER, &intel->flags) &&
- (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT)) {
+ (hci_skb_pkt_type(skb) == HCI_COMMAND_PKT)) {
struct hci_command_hdr *cmd = (void *)skb->data;
__u16 opcode = le16_to_cpu(cmd->opcode);
@@ -1140,7 +1116,7 @@ static struct sk_buff *intel_dequeue(struct hci_uart *hu)
}
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
return skb;
}
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 96bcec5598c2..73202624133b 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -162,7 +162,7 @@ restart:
break;
}
- hci_uart_tx_complete(hu, bt_cb(skb)->pkt_type);
+ hci_uart_tx_complete(hu, hci_skb_pkt_type(skb));
kfree_skb(skb);
}
@@ -248,7 +248,8 @@ static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
- BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
+ BT_DBG("%s: type %d len %d", hdev->name, hci_skb_pkt_type(skb),
+ skb->len);
hu->proto->enqueue(hu, skb);
@@ -461,13 +462,7 @@ static int hci_uart_tty_open(struct tty_struct *tty)
INIT_WORK(&hu->init_ready, hci_uart_init_work);
INIT_WORK(&hu->write_work, hci_uart_write_work);
- /* Flush any pending characters in the driver and line discipline. */
-
- /* FIXME: why is this needed. Note don't use ldisc_ref here as the
- open path is before the ldisc is referencable */
-
- if (tty->ldisc->ops->flush_buffer)
- tty->ldisc->ops->flush_buffer(tty);
+ /* Flush any pending characters in the driver */
tty_driver_flush_buffer(tty);
return 0;
diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c
index 9ee24b075f79..02692fe30279 100644
--- a/drivers/bluetooth/hci_ll.c
+++ b/drivers/bluetooth/hci_ll.c
@@ -307,7 +307,7 @@ static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb)
BT_DBG("hu %p skb %p", hu, skb);
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
/* lock hcill state */
spin_lock_irqsave(&ll->hcill_lock, flags);
@@ -493,7 +493,7 @@ static int ll_recv(struct hci_uart *hu, const void *data, int count)
return -ENOMEM;
}
- bt_cb(ll->rx_skb)->pkt_type = type;
+ hci_skb_pkt_type(ll->rx_skb) = type;
}
return count;
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 71325e443e46..683c2b642057 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -678,7 +678,7 @@ static int qca_enqueue(struct hci_uart *hu, struct sk_buff *skb)
qca->tx_ibs_state);
/* Prepend skb with frame type */
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
/* Don't go to sleep in middle of patch download or
* Out-Of-Band(GPIOs control) sleep is selected.
@@ -873,7 +873,7 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate)
/* Assign commands to change baudrate and packet type. */
memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));
- bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+ hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
skb_queue_tail(&qca->txq, skb);
hci_uart_tx_wakeup(hu);
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
index ed888e302bc3..80783dcb7f57 100644
--- a/drivers/bluetooth/hci_vhci.c
+++ b/drivers/bluetooth/hci_vhci.c
@@ -80,7 +80,7 @@ static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct vhci_data *data = hci_get_drvdata(hdev);
- memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&data->readq, skb);
wake_up_interruptible(&data->read_wait);
@@ -140,7 +140,7 @@ static int vhci_create_device(struct vhci_data *data, __u8 opcode)
return -EBUSY;
}
- bt_cb(skb)->pkt_type = HCI_VENDOR_PKT;
+ hci_skb_pkt_type(skb) = HCI_VENDOR_PKT;
*skb_put(skb, 1) = 0xff;
*skb_put(skb, 1) = opcode;
@@ -183,7 +183,7 @@ static inline ssize_t vhci_get_user(struct vhci_data *data,
return -ENODEV;
}
- bt_cb(skb)->pkt_type = pkt_type;
+ hci_skb_pkt_type(skb) = pkt_type;
ret = hci_recv_frame(data->hdev, skb);
break;
@@ -234,7 +234,7 @@ static inline ssize_t vhci_put_user(struct vhci_data *data,
data->hdev->stat.byte_tx += len;
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
data->hdev->stat.cmd_tx++;
break;
diff --git a/drivers/bus/omap-ocp2scp.c b/drivers/bus/omap-ocp2scp.c
index 9f1856948758..bf500e0e7362 100644
--- a/drivers/bus/omap-ocp2scp.c
+++ b/drivers/bus/omap-ocp2scp.c
@@ -117,7 +117,7 @@ static struct platform_driver omap_ocp2scp_driver = {
module_platform_driver(omap_ocp2scp_driver);
-MODULE_ALIAS("platform: omap-ocp2scp");
+MODULE_ALIAS("platform:omap-ocp2scp");
MODULE_AUTHOR("Texas Instruments Inc.");
MODULE_DESCRIPTION("OMAP OCP2SCP driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c
index 846bc29c157d..25996e256110 100644
--- a/drivers/bus/sunxi-rsb.c
+++ b/drivers/bus/sunxi-rsb.c
@@ -342,13 +342,13 @@ static int sunxi_rsb_read(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr,
ret = _sunxi_rsb_run_xfer(rsb);
if (ret)
- goto out;
+ goto unlock;
*buf = readl(rsb->regs + RSB_DATA);
+unlock:
mutex_unlock(&rsb->lock);
-out:
return ret;
}
@@ -527,9 +527,9 @@ static int sunxi_rsb_init_device_mode(struct sunxi_rsb *rsb)
*/
static const struct sunxi_rsb_addr_map sunxi_rsb_addr_maps[] = {
- { 0x3e3, 0x2d }, /* Primary PMIC: AXP223, AXP809, AXP81X, ... */
+ { 0x3a3, 0x2d }, /* Primary PMIC: AXP223, AXP809, AXP81X, ... */
{ 0x745, 0x3a }, /* Secondary PMIC: AXP806, ... */
- { 0xe89, 0x45 }, /* Peripheral IC: AC100, ... */
+ { 0xe89, 0x4e }, /* Peripheral IC: AC100, ... */
};
static u8 sunxi_rsb_get_rtaddr(u16 hwaddr)
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index c206ccda899b..1b257ea9776a 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -3186,15 +3186,11 @@ static noinline int mmc_ioctl_dvd_read_struct(struct cdrom_device_info *cdi,
if (!CDROM_CAN(CDC_DVD))
return -ENOSYS;
- s = kmalloc(size, GFP_KERNEL);
- if (!s)
- return -ENOMEM;
+ s = memdup_user(arg, size);
+ if (IS_ERR(s))
+ return PTR_ERR(s);
cd_dbg(CD_DO_IOCTL, "entering DVD_READ_STRUCT\n");
- if (copy_from_user(s, arg, size)) {
- kfree(s);
- return -EFAULT;
- }
ret = dvd_read_struct(cdi, s, cgc);
if (ret)
diff --git a/drivers/char/generic_nvram.c b/drivers/char/generic_nvram.c
index 6c4f4b5a9dd3..073db9558379 100644
--- a/drivers/char/generic_nvram.c
+++ b/drivers/char/generic_nvram.c
@@ -20,6 +20,7 @@
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/mutex.h>
+#include <linux/pagemap.h>
#include <asm/uaccess.h>
#include <asm/nvram.h>
#ifdef CONFIG_PPC_PMAC
@@ -33,24 +34,8 @@ static ssize_t nvram_len;
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_len;
- break;
- default:
- offset = -1;
- }
- if (offset < 0)
- return -EINVAL;
-
- file->f_pos = offset;
-
- return file->f_pos;
+ return generic_file_llseek_size(file, offset, origin,
+ MAX_LFS_FILESIZE, nvram_len);
}
static ssize_t read_nvram(struct file *file, char __user *buf,
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
index 6f497aa1b276..9203f2d130c0 100644
--- a/drivers/char/hw_random/core.c
+++ b/drivers/char/hw_random/core.c
@@ -238,7 +238,10 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf,
goto out;
}
- mutex_lock(&reading_mutex);
+ if (mutex_lock_interruptible(&reading_mutex)) {
+ err = -ERESTARTSYS;
+ goto out_put;
+ }
if (!data_avail) {
bytes_read = rng_get_data(rng, rng_buffer,
rng_buffer_size(),
@@ -288,6 +291,7 @@ out:
out_unlock_reading:
mutex_unlock(&reading_mutex);
+out_put:
put_rng(rng);
goto out;
}
diff --git a/drivers/char/hw_random/omap3-rom-rng.c b/drivers/char/hw_random/omap3-rom-rng.c
index a405cdcd8dd2..8da14f1a1f56 100644
--- a/drivers/char/hw_random/omap3-rom-rng.c
+++ b/drivers/char/hw_random/omap3-rom-rng.c
@@ -17,7 +17,7 @@
#include <linux/init.h>
#include <linux/random.h>
#include <linux/hw_random.h>
-#include <linux/timer.h>
+#include <linux/workqueue.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/platform_device.h>
@@ -29,11 +29,11 @@
/* param1: ptr, param2: count, param3: flag */
static u32 (*omap3_rom_rng_call)(u32, u32, u32);
-static struct timer_list idle_timer;
+static struct delayed_work idle_work;
static int rng_idle;
static struct clk *rng_clk;
-static void omap3_rom_rng_idle(unsigned long data)
+static void omap3_rom_rng_idle(struct work_struct *work)
{
int r;
@@ -51,7 +51,7 @@ static int omap3_rom_rng_get_random(void *buf, unsigned int count)
u32 r;
u32 ptr;
- del_timer_sync(&idle_timer);
+ cancel_delayed_work_sync(&idle_work);
if (rng_idle) {
clk_prepare_enable(rng_clk);
r = omap3_rom_rng_call(0, 0, RNG_GEN_PRNG_HW_INIT);
@@ -65,7 +65,7 @@ static int omap3_rom_rng_get_random(void *buf, unsigned int count)
ptr = virt_to_phys(buf);
r = omap3_rom_rng_call(ptr, count, RNG_GEN_HW);
- mod_timer(&idle_timer, jiffies + msecs_to_jiffies(500));
+ schedule_delayed_work(&idle_work, msecs_to_jiffies(500));
if (r != 0)
return -EINVAL;
return 0;
@@ -102,7 +102,7 @@ static int omap3_rom_rng_probe(struct platform_device *pdev)
return -EINVAL;
}
- setup_timer(&idle_timer, omap3_rom_rng_idle, 0);
+ INIT_DELAYED_WORK(&idle_work, omap3_rom_rng_idle);
rng_clk = devm_clk_get(&pdev->dev, "ick");
if (IS_ERR(rng_clk)) {
pr_err("unable to get RNG clock\n");
@@ -118,6 +118,7 @@ static int omap3_rom_rng_probe(struct platform_device *pdev)
static int omap3_rom_rng_remove(struct platform_device *pdev)
{
+ cancel_delayed_work_sync(&idle_work);
hwrng_unregister(&omap3_rom_rng_ops);
clk_disable_unprepare(rng_clk);
return 0;
diff --git a/drivers/char/hw_random/via-rng.c b/drivers/char/hw_random/via-rng.c
index 0c98a9d51a24..44ce80606944 100644
--- a/drivers/char/hw_random/via-rng.c
+++ b/drivers/char/hw_random/via-rng.c
@@ -140,7 +140,7 @@ static int via_rng_init(struct hwrng *rng)
* RNG configuration like it used to be the case in this
* register */
if ((c->x86 == 6) && (c->x86_model >= 0x0f)) {
- if (!cpu_has_xstore_enabled) {
+ if (!boot_cpu_has(X86_FEATURE_XSTORE_EN)) {
pr_err(PFX "can't enable hardware RNG "
"if XSTORE is not enabled\n");
return -ENODEV;
@@ -200,8 +200,9 @@ static int __init mod_init(void)
{
int err;
- if (!cpu_has_xstore)
+ if (!boot_cpu_has(X86_FEATURE_XSTORE))
return -ENODEV;
+
pr_info("VIA RNG detected\n");
err = hwrng_register(&via_rng);
if (err) {
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index e3536da05c88..94fb407d8561 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -472,9 +472,10 @@ static DEFINE_MUTEX(smi_watchers_mutex);
#define ipmi_get_stat(intf, stat) \
((unsigned int) atomic_read(&(intf)->stats[IPMI_STAT_ ## stat]))
-static char *addr_src_to_str[] = { "invalid", "hotmod", "hardcoded", "SPMI",
- "ACPI", "SMBIOS", "PCI",
- "device-tree", "default" };
+static const char * const addr_src_to_str[] = {
+ "invalid", "hotmod", "hardcoded", "SPMI", "ACPI", "SMBIOS", "PCI",
+ "device-tree", "default"
+};
const char *ipmi_addr_src_to_str(enum ipmi_addr_src src)
{
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index 55fe9020459f..9fda22e3387e 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -105,7 +105,8 @@ enum si_intf_state {
enum si_type {
SI_KCS, SI_SMIC, SI_BT
};
-static char *si_to_str[] = { "kcs", "smic", "bt" };
+
+static const char * const si_to_str[] = { "kcs", "smic", "bt" };
#define DEVICE_NAME "ipmi_si"
@@ -1230,14 +1231,14 @@ static int smi_start_processing(void *send_info,
new_smi->intf = intf;
- /* Try to claim any interrupts. */
- if (new_smi->irq_setup)
- new_smi->irq_setup(new_smi);
-
/* Set up the timer that drives the interface. */
setup_timer(&new_smi->si_timer, smi_timeout, (long)new_smi);
smi_mod_timer(new_smi, jiffies + SI_TIMEOUT_JIFFIES);
+ /* Try to claim any interrupts. */
+ if (new_smi->irq_setup)
+ new_smi->irq_setup(new_smi);
+
/*
* Check if the user forcefully enabled the daemon.
*/
@@ -1341,7 +1342,7 @@ static unsigned int num_slave_addrs;
#define IPMI_IO_ADDR_SPACE 0
#define IPMI_MEM_ADDR_SPACE 1
-static char *addr_space_to_str[] = { "i/o", "mem" };
+static const char * const addr_space_to_str[] = { "i/o", "mem" };
static int hotmod_handler(const char *val, struct kernel_param *kp);
@@ -1723,27 +1724,31 @@ static int mem_setup(struct smi_info *info)
*/
enum hotmod_op { HM_ADD, HM_REMOVE };
struct hotmod_vals {
- char *name;
- int val;
+ const char *name;
+ const int val;
};
-static struct hotmod_vals hotmod_ops[] = {
+
+static const struct hotmod_vals hotmod_ops[] = {
{ "add", HM_ADD },
{ "remove", HM_REMOVE },
{ NULL }
};
-static struct hotmod_vals hotmod_si[] = {
+
+static const struct hotmod_vals hotmod_si[] = {
{ "kcs", SI_KCS },
{ "smic", SI_SMIC },
{ "bt", SI_BT },
{ NULL }
};
-static struct hotmod_vals hotmod_as[] = {
+
+static const struct hotmod_vals hotmod_as[] = {
{ "mem", IPMI_MEM_ADDR_SPACE },
{ "i/o", IPMI_IO_ADDR_SPACE },
{ NULL }
};
-static int parse_str(struct hotmod_vals *v, int *val, char *name, char **curr)
+static int parse_str(const struct hotmod_vals *v, int *val, char *name,
+ char **curr)
{
char *s;
int i;
@@ -2554,7 +2559,6 @@ static void ipmi_pci_remove(struct pci_dev *pdev)
{
struct smi_info *info = pci_get_drvdata(pdev);
cleanup_one_si(info);
- pci_disable_device(pdev);
}
static const struct pci_device_id ipmi_pci_devices[] = {
@@ -2870,7 +2874,7 @@ static int ipmi_parisc_remove(struct parisc_device *dev)
return 0;
}
-static struct parisc_device_id ipmi_parisc_tbl[] = {
+static const struct parisc_device_id ipmi_parisc_tbl[] = {
{ HPHW_MC, HVERSION_REV_ANY_ID, 0x004, 0xC0 },
{ 0, }
};
@@ -3444,8 +3448,8 @@ static inline void wait_for_timer_and_thread(struct smi_info *smi_info)
static const struct ipmi_default_vals
{
- int type;
- int port;
+ const int type;
+ const int port;
} ipmi_defaults[] =
{
{ .type = SI_KCS, .port = 0xca2 },
diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
index 90e624662257..5f1c3d08ba65 100644
--- a/drivers/char/ipmi/ipmi_ssif.c
+++ b/drivers/char/ipmi/ipmi_ssif.c
@@ -1959,7 +1959,6 @@ MODULE_DEVICE_TABLE(i2c, ssif_id);
static struct i2c_driver ssif_i2c_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
- .owner = THIS_MODULE,
.name = DEVICE_NAME
},
.probe = ssif_probe,
diff --git a/drivers/char/mbcs.c b/drivers/char/mbcs.c
index e5d3e3f7a49b..67d426470e53 100644
--- a/drivers/char/mbcs.c
+++ b/drivers/char/mbcs.c
@@ -26,6 +26,7 @@
#include <linux/uio.h>
#include <linux/mutex.h>
#include <linux/slab.h>
+#include <linux/pagemap.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
@@ -451,31 +452,8 @@ mbcs_sram_write(struct file * fp, const char __user *buf, size_t len, loff_t * o
static loff_t mbcs_sram_llseek(struct file * filp, loff_t off, int whence)
{
- loff_t newpos;
-
- switch (whence) {
- case SEEK_SET:
- newpos = off;
- break;
-
- case SEEK_CUR:
- newpos = filp->f_pos + off;
- break;
-
- case SEEK_END:
- newpos = MBCS_SRAM_SIZE + off;
- break;
-
- default: /* can't happen */
- return -EINVAL;
- }
-
- if (newpos < 0)
- return -EINVAL;
-
- filp->f_pos = newpos;
-
- return newpos;
+ return generic_file_llseek_size(filp, off, whence, MAX_LFS_FILESIZE,
+ MBCS_SRAM_SIZE);
}
static uint64_t mbcs_pioaddr(struct mbcs_soft *soft, uint64_t offset)
diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c
index 97c2d8d433d6..01292328a456 100644
--- a/drivers/char/nvram.c
+++ b/drivers/char/nvram.c
@@ -110,6 +110,7 @@
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>
+#include <linux/pagemap.h>
static DEFINE_MUTEX(nvram_mutex);
@@ -213,21 +214,8 @@ void nvram_set_checksum(void)
static loff_t nvram_llseek(struct file *file, loff_t offset, int origin)
{
- switch (origin) {
- case 0:
- /* nothing to do */
- break;
- case 1:
- offset += file->f_pos;
- break;
- case 2:
- offset += NVRAM_BYTES;
- break;
- default:
- return -EINVAL;
- }
-
- return (offset >= 0) ? (file->f_pos = offset) : -EINVAL;
+ return generic_file_llseek_size(file, offset, origin, MAX_LFS_FILESIZE,
+ NVRAM_BYTES);
}
static ssize_t nvram_read(struct file *file, char __user *buf,
diff --git a/drivers/char/nwflash.c b/drivers/char/nwflash.c
index e371480d3639..dbe598de9b74 100644
--- a/drivers/char/nwflash.c
+++ b/drivers/char/nwflash.c
@@ -277,36 +277,7 @@ static loff_t flash_llseek(struct file *file, loff_t offset, int orig)
printk(KERN_DEBUG "flash_llseek: offset=0x%X, orig=0x%X.\n",
(unsigned int) offset, orig);
- switch (orig) {
- case 0:
- if (offset < 0) {
- ret = -EINVAL;
- break;
- }
-
- if ((unsigned int) offset > gbFlashSize) {
- ret = -EINVAL;
- break;
- }
-
- file->f_pos = (unsigned int) offset;
- ret = file->f_pos;
- break;
- case 1:
- if ((file->f_pos + offset) > gbFlashSize) {
- ret = -EINVAL;
- break;
- }
- if ((file->f_pos + offset) < 0) {
- ret = -EINVAL;
- break;
- }
- file->f_pos += offset;
- ret = file->f_pos;
- break;
- default:
- ret = -EINVAL;
- }
+ ret = no_seek_end_llseek_size(file, offset, orig, gbFlashSize);
mutex_unlock(&flash_mutex);
return ret;
}
diff --git a/drivers/clk/clk-gpio.c b/drivers/clk/clk-gpio.c
index 10819e248414..335322dc403f 100644
--- a/drivers/clk/clk-gpio.c
+++ b/drivers/clk/clk-gpio.c
@@ -209,6 +209,8 @@ EXPORT_SYMBOL_GPL(clk_register_gpio_mux);
struct clk_gpio_delayed_register_data {
const char *gpio_name;
+ int num_parents;
+ const char **parent_names;
struct device_node *node;
struct mutex lock;
struct clk *clk;
@@ -222,8 +224,6 @@ static struct clk *of_clk_gpio_delayed_register_get(
{
struct clk_gpio_delayed_register_data *data = _data;
struct clk *clk;
- const char **parent_names;
- int i, num_parents;
int gpio;
enum of_gpio_flags of_flags;
@@ -248,26 +248,14 @@ static struct clk *of_clk_gpio_delayed_register_get(
return ERR_PTR(gpio);
}
- num_parents = of_clk_get_parent_count(data->node);
-
- parent_names = kcalloc(num_parents, sizeof(char *), GFP_KERNEL);
- if (!parent_names) {
- clk = ERR_PTR(-ENOMEM);
- goto out;
- }
-
- for (i = 0; i < num_parents; i++)
- parent_names[i] = of_clk_get_parent_name(data->node, i);
-
- clk = data->clk_register_get(data->node->name, parent_names,
- num_parents, gpio, of_flags & OF_GPIO_ACTIVE_LOW);
+ clk = data->clk_register_get(data->node->name, data->parent_names,
+ data->num_parents, gpio, of_flags & OF_GPIO_ACTIVE_LOW);
if (IS_ERR(clk))
goto out;
data->clk = clk;
out:
mutex_unlock(&data->lock);
- kfree(parent_names);
return clk;
}
@@ -296,11 +284,24 @@ static void __init of_gpio_clk_setup(struct device_node *node,
unsigned gpio, bool active_low))
{
struct clk_gpio_delayed_register_data *data;
+ const char **parent_names;
+ int i, num_parents;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return;
+ num_parents = of_clk_get_parent_count(node);
+
+ parent_names = kcalloc(num_parents, sizeof(char *), GFP_KERNEL);
+ if (!parent_names)
+ return;
+
+ for (i = 0; i < num_parents; i++)
+ parent_names[i] = of_clk_get_parent_name(node, i);
+
+ data->num_parents = num_parents;
+ data->parent_names = parent_names;
data->node = node;
data->gpio_name = gpio_name;
data->clk_register_get = clk_register_get;
diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c
index 1ab0fb81c6a0..7bc1c4527ae4 100644
--- a/drivers/clk/clk-qoriq.c
+++ b/drivers/clk/clk-qoriq.c
@@ -778,8 +778,10 @@ static struct clk * __init create_one_cmux(struct clockgen *cg, int idx)
*/
clksel = (cg_in(cg, hwc->reg) & CLKSEL_MASK) >> CLKSEL_SHIFT;
div = get_pll_div(cg, hwc, clksel);
- if (!div)
+ if (!div) {
+ kfree(hwc);
return NULL;
+ }
pct80_rate = clk_get_rate(div->clk);
pct80_rate *= 8;
diff --git a/drivers/clk/clk-scpi.c b/drivers/clk/clk-scpi.c
index 0b501a9fef92..cd0f2726f5e0 100644
--- a/drivers/clk/clk-scpi.c
+++ b/drivers/clk/clk-scpi.c
@@ -292,6 +292,7 @@ static int scpi_clocks_probe(struct platform_device *pdev)
ret = scpi_clk_add(dev, child, match);
if (ret) {
scpi_clocks_remove(pdev);
+ of_node_put(child);
return ret;
}
}
diff --git a/drivers/clk/imx/clk-pllv1.c b/drivers/clk/imx/clk-pllv1.c
index 8564e4342c7d..82fe3662b5f6 100644
--- a/drivers/clk/imx/clk-pllv1.c
+++ b/drivers/clk/imx/clk-pllv1.c
@@ -52,7 +52,7 @@ static unsigned long clk_pllv1_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_pllv1 *pll = to_clk_pllv1(hw);
- long long ll;
+ unsigned long long ull;
int mfn_abs;
unsigned int mfi, mfn, mfd, pd;
u32 reg;
@@ -94,16 +94,16 @@ static unsigned long clk_pllv1_recalc_rate(struct clk_hw *hw,
rate = parent_rate * 2;
rate /= pd + 1;
- ll = (unsigned long long)rate * mfn_abs;
+ ull = (unsigned long long)rate * mfn_abs;
- do_div(ll, mfd + 1);
+ do_div(ull, mfd + 1);
if (mfn_is_negative(pll, mfn))
- ll = -ll;
+ ull = (rate * mfi) - ull;
+ else
+ ull = (rate * mfi) + ull;
- ll = (rate * mfi) + ll;
-
- return ll;
+ return ull;
}
static struct clk_ops clk_pllv1_ops = {
diff --git a/drivers/clk/imx/clk-pllv2.c b/drivers/clk/imx/clk-pllv2.c
index b18f875eac6a..4aeda56ce372 100644
--- a/drivers/clk/imx/clk-pllv2.c
+++ b/drivers/clk/imx/clk-pllv2.c
@@ -79,7 +79,7 @@ static unsigned long __clk_pllv2_recalc_rate(unsigned long parent_rate,
{
long mfi, mfn, mfd, pdf, ref_clk;
unsigned long dbl;
- s64 temp;
+ u64 temp;
dbl = dp_ctl & MXC_PLL_DP_CTL_DPDCK0_2_EN;
@@ -98,8 +98,9 @@ static unsigned long __clk_pllv2_recalc_rate(unsigned long parent_rate,
temp = (u64) ref_clk * abs(mfn);
do_div(temp, mfd + 1);
if (mfn < 0)
- temp = -temp;
- temp = (ref_clk * mfi) + temp;
+ temp = (ref_clk * mfi) - temp;
+ else
+ temp = (ref_clk * mfi) + temp;
return temp;
}
@@ -126,7 +127,7 @@ static int __clk_pllv2_set_rate(unsigned long rate, unsigned long parent_rate,
{
u32 reg;
long mfi, pdf, mfn, mfd = 999999;
- s64 temp64;
+ u64 temp64;
unsigned long quad_parent_rate;
quad_parent_rate = 4 * parent_rate;
diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c
index d1b1c95177bb..0a94d9661d91 100644
--- a/drivers/clk/imx/clk-vf610.c
+++ b/drivers/clk/imx/clk-vf610.c
@@ -335,22 +335,22 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)
clk[VF610_CLK_SAI0_SEL] = imx_clk_mux("sai0_sel", CCM_CSCMR1, 0, 2, sai_sels, 4);
clk[VF610_CLK_SAI0_EN] = imx_clk_gate("sai0_en", "sai0_sel", CCM_CSCDR1, 16);
clk[VF610_CLK_SAI0_DIV] = imx_clk_divider("sai0_div", "sai0_en", CCM_CSCDR1, 0, 4);
- clk[VF610_CLK_SAI0] = imx_clk_gate2("sai0", "sai0_div", CCM_CCGR0, CCM_CCGRx_CGn(15));
+ clk[VF610_CLK_SAI0] = imx_clk_gate2("sai0", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(15));
clk[VF610_CLK_SAI1_SEL] = imx_clk_mux("sai1_sel", CCM_CSCMR1, 2, 2, sai_sels, 4);
clk[VF610_CLK_SAI1_EN] = imx_clk_gate("sai1_en", "sai1_sel", CCM_CSCDR1, 17);
clk[VF610_CLK_SAI1_DIV] = imx_clk_divider("sai1_div", "sai1_en", CCM_CSCDR1, 4, 4);
- clk[VF610_CLK_SAI1] = imx_clk_gate2("sai1", "sai1_div", CCM_CCGR1, CCM_CCGRx_CGn(0));
+ clk[VF610_CLK_SAI1] = imx_clk_gate2("sai1", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(0));
clk[VF610_CLK_SAI2_SEL] = imx_clk_mux("sai2_sel", CCM_CSCMR1, 4, 2, sai_sels, 4);
clk[VF610_CLK_SAI2_EN] = imx_clk_gate("sai2_en", "sai2_sel", CCM_CSCDR1, 18);
clk[VF610_CLK_SAI2_DIV] = imx_clk_divider("sai2_div", "sai2_en", CCM_CSCDR1, 8, 4);
- clk[VF610_CLK_SAI2] = imx_clk_gate2("sai2", "sai2_div", CCM_CCGR1, CCM_CCGRx_CGn(1));
+ clk[VF610_CLK_SAI2] = imx_clk_gate2("sai2", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(1));
clk[VF610_CLK_SAI3_SEL] = imx_clk_mux("sai3_sel", CCM_CSCMR1, 6, 2, sai_sels, 4);
clk[VF610_CLK_SAI3_EN] = imx_clk_gate("sai3_en", "sai3_sel", CCM_CSCDR1, 19);
clk[VF610_CLK_SAI3_DIV] = imx_clk_divider("sai3_div", "sai3_en", CCM_CSCDR1, 12, 4);
- clk[VF610_CLK_SAI3] = imx_clk_gate2("sai3", "sai3_div", CCM_CCGR1, CCM_CCGRx_CGn(2));
+ clk[VF610_CLK_SAI3] = imx_clk_gate2("sai3", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(2));
clk[VF610_CLK_NFC_SEL] = imx_clk_mux("nfc_sel", CCM_CSCMR1, 12, 2, nfc_sels, 4);
clk[VF610_CLK_NFC_EN] = imx_clk_gate("nfc_en", "nfc_sel", CCM_CSCDR2, 9);
diff --git a/drivers/clk/mmp/clk-mmp2.c b/drivers/clk/mmp/clk-mmp2.c
index 09d2832fbd78..71fd29348f28 100644
--- a/drivers/clk/mmp/clk-mmp2.c
+++ b/drivers/clk/mmp/clk-mmp2.c
@@ -9,6 +9,7 @@
* warranty of any kind, whether express or implied.
*/
+#include <linux/clk.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
diff --git a/drivers/clk/mmp/clk-pxa168.c b/drivers/clk/mmp/clk-pxa168.c
index 93e967c0f972..75244915df05 100644
--- a/drivers/clk/mmp/clk-pxa168.c
+++ b/drivers/clk/mmp/clk-pxa168.c
@@ -9,6 +9,7 @@
* warranty of any kind, whether express or implied.
*/
+#include <linux/clk.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
diff --git a/drivers/clk/mmp/clk-pxa910.c b/drivers/clk/mmp/clk-pxa910.c
index 993abcdb32cc..37ba04ba1368 100644
--- a/drivers/clk/mmp/clk-pxa910.c
+++ b/drivers/clk/mmp/clk-pxa910.c
@@ -9,6 +9,7 @@
* warranty of any kind, whether express or implied.
*/
+#include <linux/clk.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
diff --git a/drivers/clk/sunxi/clk-a10-pll2.c b/drivers/clk/sunxi/clk-a10-pll2.c
index 5484c31ec568..0ee1f363e4be 100644
--- a/drivers/clk/sunxi/clk-a10-pll2.c
+++ b/drivers/clk/sunxi/clk-a10-pll2.c
@@ -41,15 +41,10 @@
#define SUN4I_PLL2_OUTPUTS 4
-struct sun4i_pll2_data {
- u32 post_div_offset;
- u32 pre_div_flags;
-};
-
static DEFINE_SPINLOCK(sun4i_a10_pll2_lock);
static void __init sun4i_pll2_setup(struct device_node *node,
- struct sun4i_pll2_data *data)
+ int post_div_offset)
{
const char *clk_name = node->name, *parent;
struct clk **clks, *base_clk, *prediv_clk;
@@ -76,7 +71,7 @@ static void __init sun4i_pll2_setup(struct device_node *node,
parent, 0, reg,
SUN4I_PLL2_PRE_DIV_SHIFT,
SUN4I_PLL2_PRE_DIV_WIDTH,
- data->pre_div_flags,
+ CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
&sun4i_a10_pll2_lock);
if (!prediv_clk) {
pr_err("Couldn't register the prediv clock\n");
@@ -127,7 +122,7 @@ static void __init sun4i_pll2_setup(struct device_node *node,
*/
val = readl(reg);
val &= ~(SUN4I_PLL2_POST_DIV_MASK << SUN4I_PLL2_POST_DIV_SHIFT);
- val |= (SUN4I_PLL2_POST_DIV_VALUE - data->post_div_offset) << SUN4I_PLL2_POST_DIV_SHIFT;
+ val |= (SUN4I_PLL2_POST_DIV_VALUE - post_div_offset) << SUN4I_PLL2_POST_DIV_SHIFT;
writel(val, reg);
of_property_read_string_index(node, "clock-output-names",
@@ -191,25 +186,17 @@ err_unmap:
iounmap(reg);
}
-static struct sun4i_pll2_data sun4i_a10_pll2_data = {
- .pre_div_flags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
-};
-
static void __init sun4i_a10_pll2_setup(struct device_node *node)
{
- sun4i_pll2_setup(node, &sun4i_a10_pll2_data);
+ sun4i_pll2_setup(node, 0);
}
CLK_OF_DECLARE(sun4i_a10_pll2, "allwinner,sun4i-a10-pll2-clk",
sun4i_a10_pll2_setup);
-static struct sun4i_pll2_data sun5i_a13_pll2_data = {
- .post_div_offset = 1,
-};
-
static void __init sun5i_a13_pll2_setup(struct device_node *node)
{
- sun4i_pll2_setup(node, &sun5i_a13_pll2_data);
+ sun4i_pll2_setup(node, 1);
}
CLK_OF_DECLARE(sun5i_a13_pll2, "allwinner,sun5i-a13-pll2-clk",
diff --git a/drivers/clk/ti/clk-816x.c b/drivers/clk/ti/clk-816x.c
index 1dfad0c712cd..2a5d84fdddc5 100644
--- a/drivers/clk/ti/clk-816x.c
+++ b/drivers/clk/ti/clk-816x.c
@@ -20,6 +20,8 @@ static struct ti_dt_clk dm816x_clks[] = {
DT_CLK(NULL, "sys_clkin", "sys_clkin_ck"),
DT_CLK(NULL, "timer_sys_ck", "sys_clkin_ck"),
DT_CLK(NULL, "sys_32k_ck", "sys_32k_ck"),
+ DT_CLK(NULL, "timer_32k_ck", "sysclk18_ck"),
+ DT_CLK(NULL, "timer_ext_ck", "tclkin_ck"),
DT_CLK(NULL, "mpu_ck", "mpu_ck"),
DT_CLK(NULL, "timer1_fck", "timer1_fck"),
DT_CLK(NULL, "timer2_fck", "timer2_fck"),
diff --git a/drivers/clk/ti/clkt_dpll.c b/drivers/clk/ti/clkt_dpll.c
index 9023ca9caf84..b5cc6f66ae5d 100644
--- a/drivers/clk/ti/clkt_dpll.c
+++ b/drivers/clk/ti/clkt_dpll.c
@@ -240,7 +240,7 @@ u8 omap2_init_dpll_parent(struct clk_hw *hw)
*/
unsigned long omap2_get_dpll_rate(struct clk_hw_omap *clk)
{
- long long dpll_clk;
+ u64 dpll_clk;
u32 dpll_mult, dpll_div, v;
struct dpll_data *dd;
@@ -262,7 +262,7 @@ unsigned long omap2_get_dpll_rate(struct clk_hw_omap *clk)
dpll_div = v & dd->div1_mask;
dpll_div >>= __ffs(dd->div1_mask);
- dpll_clk = (long long)clk_get_rate(dd->clk_ref) * dpll_mult;
+ dpll_clk = (u64)clk_get_rate(dd->clk_ref) * dpll_mult;
do_div(dpll_clk, dpll_div + 1);
return dpll_clk;
diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c
index 5b1726829e6d..df2558350fc1 100644
--- a/drivers/clk/ti/divider.c
+++ b/drivers/clk/ti/divider.c
@@ -214,7 +214,6 @@ static int ti_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
{
struct clk_divider *divider;
unsigned int div, value;
- unsigned long flags = 0;
u32 val;
if (!hw || !rate)
@@ -228,9 +227,6 @@ static int ti_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
if (value > div_mask(divider))
value = div_mask(divider);
- if (divider->lock)
- spin_lock_irqsave(divider->lock, flags);
-
if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
val = div_mask(divider) << (divider->shift + 16);
} else {
@@ -240,9 +236,6 @@ static int ti_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
val |= value << divider->shift;
ti_clk_ll_ops->clk_writel(val, divider->reg);
- if (divider->lock)
- spin_unlock_irqrestore(divider->lock, flags);
-
return 0;
}
@@ -256,8 +249,7 @@ static struct clk *_register_divider(struct device *dev, const char *name,
const char *parent_name,
unsigned long flags, void __iomem *reg,
u8 shift, u8 width, u8 clk_divider_flags,
- const struct clk_div_table *table,
- spinlock_t *lock)
+ const struct clk_div_table *table)
{
struct clk_divider *div;
struct clk *clk;
@@ -288,7 +280,6 @@ static struct clk *_register_divider(struct device *dev, const char *name,
div->shift = shift;
div->width = width;
div->flags = clk_divider_flags;
- div->lock = lock;
div->hw.init = &init;
div->table = table;
@@ -421,7 +412,7 @@ struct clk *ti_clk_register_divider(struct ti_clk *setup)
clk = _register_divider(NULL, setup->name, div->parent,
flags, (void __iomem *)reg, div->bit_shift,
- width, div_flags, table, NULL);
+ width, div_flags, table);
if (IS_ERR(clk))
kfree(table);
@@ -584,8 +575,7 @@ static void __init of_ti_divider_clk_setup(struct device_node *node)
goto cleanup;
clk = _register_divider(NULL, node->name, parent_name, flags, reg,
- shift, width, clk_divider_flags, table,
- NULL);
+ shift, width, clk_divider_flags, table);
if (!IS_ERR(clk)) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
diff --git a/drivers/clk/ti/fapll.c b/drivers/clk/ti/fapll.c
index f4b2e9888bdf..66a0d0ed8b55 100644
--- a/drivers/clk/ti/fapll.c
+++ b/drivers/clk/ti/fapll.c
@@ -168,7 +168,7 @@ static unsigned long ti_fapll_recalc_rate(struct clk_hw *hw,
{
struct fapll_data *fd = to_fapll(hw);
u32 fapll_n, fapll_p, v;
- long long rate;
+ u64 rate;
if (ti_fapll_clock_is_bypass(fd))
return parent_rate;
@@ -314,7 +314,7 @@ static unsigned long ti_fapll_synth_recalc_rate(struct clk_hw *hw,
{
struct fapll_synth *synth = to_synth(hw);
u32 synth_div_m;
- long long rate;
+ u64 rate;
/* The audio_pll_clk1 is hardwired to produce 32.768KiHz clock */
if (!synth->div)
diff --git a/drivers/clk/ti/mux.c b/drivers/clk/ti/mux.c
index 69f08a1d047d..dab9ba88b9d6 100644
--- a/drivers/clk/ti/mux.c
+++ b/drivers/clk/ti/mux.c
@@ -69,7 +69,6 @@ static int ti_clk_mux_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_mux *mux = to_clk_mux(hw);
u32 val;
- unsigned long flags = 0;
if (mux->table) {
index = mux->table[index];
@@ -81,9 +80,6 @@ static int ti_clk_mux_set_parent(struct clk_hw *hw, u8 index)
index++;
}
- if (mux->lock)
- spin_lock_irqsave(mux->lock, flags);
-
if (mux->flags & CLK_MUX_HIWORD_MASK) {
val = mux->mask << (mux->shift + 16);
} else {
@@ -93,9 +89,6 @@ static int ti_clk_mux_set_parent(struct clk_hw *hw, u8 index)
val |= index << mux->shift;
ti_clk_ll_ops->clk_writel(val, mux->reg);
- if (mux->lock)
- spin_unlock_irqrestore(mux->lock, flags);
-
return 0;
}
@@ -109,7 +102,7 @@ static struct clk *_register_mux(struct device *dev, const char *name,
const char **parent_names, u8 num_parents,
unsigned long flags, void __iomem *reg,
u8 shift, u32 mask, u8 clk_mux_flags,
- u32 *table, spinlock_t *lock)
+ u32 *table)
{
struct clk_mux *mux;
struct clk *clk;
@@ -133,7 +126,6 @@ static struct clk *_register_mux(struct device *dev, const char *name,
mux->shift = shift;
mux->mask = mask;
mux->flags = clk_mux_flags;
- mux->lock = lock;
mux->table = table;
mux->hw.init = &init;
@@ -175,7 +167,7 @@ struct clk *ti_clk_register_mux(struct ti_clk *setup)
return _register_mux(NULL, setup->name, mux->parents, mux->num_parents,
flags, (void __iomem *)reg, mux->bit_shift, mask,
- mux_flags, NULL, NULL);
+ mux_flags, NULL);
}
/**
@@ -227,8 +219,7 @@ static void of_mux_clk_setup(struct device_node *node)
mask = (1 << fls(mask)) - 1;
clk = _register_mux(NULL, node->name, parent_names, num_parents,
- flags, reg, shift, mask, clk_mux_flags, NULL,
- NULL);
+ flags, reg, shift, mask, clk_mux_flags, NULL);
if (!IS_ERR(clk))
of_clk_add_provider(node, of_clk_src_simple_get, clk);
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 2eb5f0efae90..56777f04d2d9 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -28,10 +28,16 @@ config CLKSRC_MMIO
bool
config DIGICOLOR_TIMER
- bool
+ bool "Digicolor timer driver" if COMPILE_TEST
+ depends on GENERIC_CLOCKEVENTS
+ help
+ Enables the support for the digicolor timer driver.
config DW_APB_TIMER
- bool
+ bool "DW APB timer driver" if COMPILE_TEST
+ depends on GENERIC_CLOCKEVENTS
+ help
+ Enables the support for the dw_apb timer.
config DW_APB_TIMER_OF
bool
@@ -39,47 +45,77 @@ config DW_APB_TIMER_OF
select CLKSRC_OF
config ROCKCHIP_TIMER
- bool
+ bool "Rockchip timer driver" if COMPILE_TEST
+ depends on ARM || ARM64
select CLKSRC_OF
+ help
+ Enables the support for the rockchip timer driver.
config ARMADA_370_XP_TIMER
- bool
+ bool "Armada 370 and XP timer driver" if COMPILE_TEST
+ depends on ARM
select CLKSRC_OF
+ help
+ Enables the support for the Armada 370 and XP timer driver.
config MESON6_TIMER
- bool
+ bool "Meson6 timer driver" if COMPILE_TEST
+ depends on GENERIC_CLOCKEVENTS
select CLKSRC_MMIO
+ help
+ Enables the support for the Meson6 timer driver.
config ORION_TIMER
+ bool "Orion timer driver" if COMPILE_TEST
+ depends on ARM
select CLKSRC_OF
select CLKSRC_MMIO
- bool
+ help
+ Enables the support for the Orion timer driver
config SUN4I_TIMER
+ bool "Sun4i timer driver" if COMPILE_TEST
+ depends on GENERIC_CLOCKEVENTS
select CLKSRC_MMIO
- bool
+ help
+ Enables support for the Sun4i timer.
config SUN5I_HSTIMER
+ bool "Sun5i timer driver" if COMPILE_TEST
select CLKSRC_MMIO
- bool
+ depends on COMMON_CLK
+ help
+ Enables support the Sun5i timer.
config TEGRA_TIMER
- bool
+ bool "Tegra timer driver" if COMPILE_TEST
+ depends on ARM
+ help
+ Enables support for the Tegra driver.
config VT8500_TIMER
- bool
+ bool "VT8500 timer driver" if COMPILE_TEST
+ depends on GENERIC_CLOCKEVENTS
+ help
+ Enables support for the VT8500 driver.
config CADENCE_TTC_TIMER
- bool
+ bool "Cadence TTC timer driver" if COMPILE_TEST
+ depends on COMMON_CLK
+ help
+ Enables support for the cadence ttc driver.
config ASM9260_TIMER
- bool
+ bool "ASM9260 timer driver" if COMPILE_TEST
+ depends on GENERIC_CLOCKEVENTS
select CLKSRC_MMIO
select CLKSRC_OF
+ help
+ Enables support for the ASM9260 timer.
config CLKSRC_NOMADIK_MTU
- bool
- depends on (ARCH_NOMADIK || ARCH_U8500)
+ bool "Nomakdik clocksource driver" if COMPILE_TEST
+ depends on ARM
select CLKSRC_MMIO
help
Support for Multi Timer Unit. MTU provides access
@@ -93,9 +129,8 @@ config CLKSRC_NOMADIK_MTU_SCHED_CLOCK
Use the Multi Timer Unit as the sched_clock.
config CLKSRC_DBX500_PRCMU
- bool "Clocksource PRCMU Timer"
- depends on UX500_SOC_DB8500
- default y
+ bool "Clocksource PRCMU Timer" if COMPILE_TEST
+ depends on GENERIC_CLOCKEVENTS
help
Use the always on PRCMU Timer as clocksource
@@ -116,13 +151,19 @@ config CLKSRC_EFM32
event device.
config CLKSRC_LPC32XX
- bool
+ bool "Clocksource for LPC32XX" if COMPILE_TEST
+ depends on GENERIC_CLOCKEVENTS && HAS_IOMEM
select CLKSRC_MMIO
select CLKSRC_OF
+ help
+ Support for the LPC32XX clocksource.
config CLKSRC_PISTACHIO
- bool
+ bool "Clocksource for Pistachio SoC" if COMPILE_TEST
+ depends on HAS_IOMEM
select CLKSRC_OF
+ help
+ Enables the clocksource for the Pistachio SoC.
config CLKSRC_TI_32K
bool "Texas Instruments 32.768 Hz Clocksource" if COMPILE_TEST
@@ -199,13 +240,14 @@ config CLKSRC_METAG_GENERIC
This option enables support for the Meta per-thread timers.
config CLKSRC_EXYNOS_MCT
- def_bool y if ARCH_EXYNOS
- depends on !ARM64
+ bool "Exynos multi core timer driver" if COMPILE_TEST
+ depends on ARM
help
Support for Multi Core Timer controller on Exynos SoCs.
config CLKSRC_SAMSUNG_PWM
- bool
+ bool "PWM timer drvier for Samsung S3C, S5P" if COMPILE_TEST
+ depends on GENERIC_CLOCKEVENTS
help
This is a new clocksource driver for the PWM timer found in
Samsung S3C, S5P and Exynos SoCs, replacing an earlier driver
@@ -213,7 +255,9 @@ config CLKSRC_SAMSUNG_PWM
needed only on systems that do not have the Exynos MCT available.
config FSL_FTM_TIMER
- bool
+ bool "Freescale FlexTimer Module driver" if COMPILE_TEST
+ depends on GENERIC_CLOCKEVENTS
+ select CLKSRC_MMIO
help
Support for Freescale FlexTimer Module (FTM) timer.
@@ -226,9 +270,12 @@ config SYS_SUPPORTS_SH_CMT
bool
config MTK_TIMER
+ bool "Mediatek timer driver" if COMPILE_TEST
+ depends on GENERIC_CLOCKEVENTS && HAS_IOMEM
select CLKSRC_OF
select CLKSRC_MMIO
- bool
+ help
+ Support for Mediatek timer driver.
config SYS_SUPPORTS_SH_MTU2
bool
@@ -279,7 +326,12 @@ config EM_TIMER_STI
such as EMEV2 from former NEC Electronics.
config CLKSRC_QCOM
- bool
+ bool "Qualcomm MSM timer" if COMPILE_TEST
+ depends on ARM
+ select CLKSRC_OF
+ help
+ This enables the clocksource and the per CPU clockevent driver for the
+ Qualcomm SoCs.
config CLKSRC_VERSATILE
bool "ARM Versatile (Express) reference platforms clock source"
@@ -298,21 +350,40 @@ config CLKSRC_MIPS_GIC
select CLKSRC_OF
config CLKSRC_TANGO_XTAL
- bool
+ bool "Clocksource for Tango SoC" if COMPILE_TEST
+ depends on ARM
select CLKSRC_OF
+ select CLKSRC_MMIO
+ help
+ This enables the clocksource for Tango SoC
config CLKSRC_PXA
- def_bool y if ARCH_PXA || ARCH_SA1100
- select CLKSRC_OF if OF
+ bool "Clocksource for PXA or SA-11x0 platform" if COMPILE_TEST
+ depends on GENERIC_CLOCKEVENTS
+ select CLKSRC_MMIO
help
This enables OST0 support available on PXA and SA-11x0
platforms.
+config H8300_TMR8
+ bool "Clockevent timer for the H8300 platform" if COMPILE_TEST
+ depends on GENERIC_CLOCKEVENTS && HAS_IOMEM
+ help
+ This enables the 8 bits timer for the H8300 platform.
+
config H8300_TMR16
- bool
+ bool "Clockevent timer for the H83069 platform" if COMPILE_TEST
+ depends on GENERIC_CLOCKEVENTS && HAS_IOMEM
+ help
+ This enables the 16 bits timer for the H8300 platform with the
+ H83069 cpu.
config H8300_TPU
- bool
+ bool "Clocksource for the H8300 platform" if COMPILE_TEST
+ depends on GENERIC_CLOCKEVENTS && HAS_IOMEM
+ help
+ This enables the clocksource for the H8300 platform with the
+ H8S2678 cpu.
config CLKSRC_IMX_GPT
bool "Clocksource using i.MX GPT" if COMPILE_TEST
@@ -320,9 +391,9 @@ config CLKSRC_IMX_GPT
select CLKSRC_MMIO
config CLKSRC_ST_LPC
- bool
- depends on ARCH_STI
+ bool "Low power clocksource found in the LPC" if COMPILE_TEST
select CLKSRC_OF if OF
+ depends on HAS_IOMEM
help
Enable this option to use the Low Power controller timer
as clocksource.
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 56bd16e77ae3..dc2b8997f6e6 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -60,7 +60,7 @@ obj-$(CONFIG_CLKSRC_MIPS_GIC) += mips-gic-timer.o
obj-$(CONFIG_CLKSRC_TANGO_XTAL) += tango_xtal.o
obj-$(CONFIG_CLKSRC_IMX_GPT) += timer-imx-gpt.o
obj-$(CONFIG_ASM9260_TIMER) += asm9260_timer.o
-obj-$(CONFIG_H8300) += h8300_timer8.o
+obj-$(CONFIG_H8300_TMR8) += h8300_timer8.o
obj-$(CONFIG_H8300_TMR16) += h8300_timer16.o
obj-$(CONFIG_H8300_TPU) += h8300_tpu.o
obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o
diff --git a/drivers/clocksource/acpi_pm.c b/drivers/clocksource/acpi_pm.c
index 6eab88985670..28037d0b8dcd 100644
--- a/drivers/clocksource/acpi_pm.c
+++ b/drivers/clocksource/acpi_pm.c
@@ -109,10 +109,8 @@ static void acpi_pm_check_blacklist(struct pci_dev *dev)
/* the bug has been fixed in PIIX4M */
if (dev->revision < 3) {
- printk(KERN_WARNING "* Found PM-Timer Bug on the chipset."
- " Due to workarounds for a bug,\n"
- "* this clock source is slow. Consider trying"
- " other clock sources\n");
+ pr_warn("* Found PM-Timer Bug on the chipset. Due to workarounds for a bug,\n"
+ "* this clock source is slow. Consider trying other clock sources\n");
acpi_pm_need_workaround();
}
@@ -125,12 +123,9 @@ static void acpi_pm_check_graylist(struct pci_dev *dev)
if (acpi_pm_good)
return;
- printk(KERN_WARNING "* The chipset may have PM-Timer Bug. Due to"
- " workarounds for a bug,\n"
- "* this clock source is slow. If you are sure your timer"
- " does not have\n"
- "* this bug, please use \"acpi_pm_good\" to disable the"
- " workaround\n");
+ pr_warn("* The chipset may have PM-Timer Bug. Due to workarounds for a bug,\n"
+ "* this clock source is slow. If you are sure your timer does not have\n"
+ "* this bug, please use \"acpi_pm_good\" to disable the workaround\n");
acpi_pm_need_workaround();
}
@@ -162,8 +157,7 @@ static int verify_pmtmr_rate(void)
/* Check that the PMTMR delta is within 5% of what we expect */
if (delta < (PMTMR_EXPECTED_RATE * 19) / 20 ||
delta > (PMTMR_EXPECTED_RATE * 21) / 20) {
- printk(KERN_INFO "PM-Timer running at invalid rate: %lu%% "
- "of normal - aborting.\n",
+ pr_info("PM-Timer running at invalid rate: %lu%% of normal - aborting.\n",
100UL * delta / PMTMR_EXPECTED_RATE);
return -1;
}
@@ -199,15 +193,14 @@ static int __init init_acpi_pm_clocksource(void)
break;
if ((value2 < value1) && ((value2) < 0xFFF))
break;
- printk(KERN_INFO "PM-Timer had inconsistent results:"
- " %#llx, %#llx - aborting.\n",
- value1, value2);
+ pr_info("PM-Timer had inconsistent results: %#llx, %#llx - aborting.\n",
+ value1, value2);
pmtmr_ioport = 0;
return -EINVAL;
}
if (i == ACPI_PM_READ_CHECKS) {
- printk(KERN_INFO "PM-Timer failed consistency check "
- " (%#llx) - aborting.\n", value1);
+ pr_info("PM-Timer failed consistency check (%#llx) - aborting.\n",
+ value1);
pmtmr_ioport = 0;
return -ENODEV;
}
diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c
index a2cb6fae9295..d189d8cb69f7 100644
--- a/drivers/clocksource/arm_global_timer.c
+++ b/drivers/clocksource/arm_global_timer.c
@@ -99,17 +99,17 @@ static void gt_compare_set(unsigned long delta, int periodic)
counter += delta;
ctrl = GT_CONTROL_TIMER_ENABLE;
- writel(ctrl, gt_base + GT_CONTROL);
- writel(lower_32_bits(counter), gt_base + GT_COMP0);
- writel(upper_32_bits(counter), gt_base + GT_COMP1);
+ writel_relaxed(ctrl, gt_base + GT_CONTROL);
+ writel_relaxed(lower_32_bits(counter), gt_base + GT_COMP0);
+ writel_relaxed(upper_32_bits(counter), gt_base + GT_COMP1);
if (periodic) {
- writel(delta, gt_base + GT_AUTO_INC);
+ writel_relaxed(delta, gt_base + GT_AUTO_INC);
ctrl |= GT_CONTROL_AUTO_INC;
}
ctrl |= GT_CONTROL_COMP_ENABLE | GT_CONTROL_IRQ_ENABLE;
- writel(ctrl, gt_base + GT_CONTROL);
+ writel_relaxed(ctrl, gt_base + GT_CONTROL);
}
static int gt_clockevent_shutdown(struct clock_event_device *evt)
@@ -195,12 +195,23 @@ static cycle_t gt_clocksource_read(struct clocksource *cs)
return gt_counter_read();
}
+static void gt_resume(struct clocksource *cs)
+{
+ unsigned long ctrl;
+
+ ctrl = readl(gt_base + GT_CONTROL);
+ if (!(ctrl & GT_CONTROL_TIMER_ENABLE))
+ /* re-enable timer on resume */
+ writel(GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL);
+}
+
static struct clocksource gt_clocksource = {
.name = "arm_global_timer",
.rating = 300,
.read = gt_clocksource_read,
.mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
+ .resume = gt_resume,
};
#ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
diff --git a/drivers/clocksource/dw_apb_timer.c b/drivers/clocksource/dw_apb_timer.c
index c76c75006ea6..63345260244d 100644
--- a/drivers/clocksource/dw_apb_timer.c
+++ b/drivers/clocksource/dw_apb_timer.c
@@ -49,20 +49,31 @@ clocksource_to_dw_apb_clocksource(struct clocksource *cs)
return container_of(cs, struct dw_apb_clocksource, cs);
}
-static unsigned long apbt_readl(struct dw_apb_timer *timer, unsigned long offs)
+static inline u32 apbt_readl(struct dw_apb_timer *timer, unsigned long offs)
{
return readl(timer->base + offs);
}
-static void apbt_writel(struct dw_apb_timer *timer, unsigned long val,
- unsigned long offs)
+static inline void apbt_writel(struct dw_apb_timer *timer, u32 val,
+ unsigned long offs)
{
writel(val, timer->base + offs);
}
+static inline u32 apbt_readl_relaxed(struct dw_apb_timer *timer, unsigned long offs)
+{
+ return readl_relaxed(timer->base + offs);
+}
+
+static inline void apbt_writel_relaxed(struct dw_apb_timer *timer, u32 val,
+ unsigned long offs)
+{
+ writel_relaxed(val, timer->base + offs);
+}
+
static void apbt_disable_int(struct dw_apb_timer *timer)
{
- unsigned long ctrl = apbt_readl(timer, APBTMR_N_CONTROL);
+ u32 ctrl = apbt_readl(timer, APBTMR_N_CONTROL);
ctrl |= APBTMR_CONTROL_INT;
apbt_writel(timer, ctrl, APBTMR_N_CONTROL);
@@ -81,7 +92,7 @@ void dw_apb_clockevent_pause(struct dw_apb_clock_event_device *dw_ced)
static void apbt_eoi(struct dw_apb_timer *timer)
{
- apbt_readl(timer, APBTMR_N_EOI);
+ apbt_readl_relaxed(timer, APBTMR_N_EOI);
}
static irqreturn_t dw_apb_clockevent_irq(int irq, void *data)
@@ -103,7 +114,7 @@ static irqreturn_t dw_apb_clockevent_irq(int irq, void *data)
static void apbt_enable_int(struct dw_apb_timer *timer)
{
- unsigned long ctrl = apbt_readl(timer, APBTMR_N_CONTROL);
+ u32 ctrl = apbt_readl(timer, APBTMR_N_CONTROL);
/* clear pending intr */
apbt_readl(timer, APBTMR_N_EOI);
ctrl &= ~APBTMR_CONTROL_INT;
@@ -113,7 +124,7 @@ static void apbt_enable_int(struct dw_apb_timer *timer)
static int apbt_shutdown(struct clock_event_device *evt)
{
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
- unsigned long ctrl;
+ u32 ctrl;
pr_debug("%s CPU %d state=shutdown\n", __func__,
cpumask_first(evt->cpumask));
@@ -127,7 +138,7 @@ static int apbt_shutdown(struct clock_event_device *evt)
static int apbt_set_oneshot(struct clock_event_device *evt)
{
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
- unsigned long ctrl;
+ u32 ctrl;
pr_debug("%s CPU %d state=oneshot\n", __func__,
cpumask_first(evt->cpumask));
@@ -160,7 +171,7 @@ static int apbt_set_periodic(struct clock_event_device *evt)
{
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
unsigned long period = DIV_ROUND_UP(dw_ced->timer.freq, HZ);
- unsigned long ctrl;
+ u32 ctrl;
pr_debug("%s CPU %d state=periodic\n", __func__,
cpumask_first(evt->cpumask));
@@ -196,17 +207,17 @@ static int apbt_resume(struct clock_event_device *evt)
static int apbt_next_event(unsigned long delta,
struct clock_event_device *evt)
{
- unsigned long ctrl;
+ u32 ctrl;
struct dw_apb_clock_event_device *dw_ced = ced_to_dw_apb_ced(evt);
/* Disable timer */
- ctrl = apbt_readl(&dw_ced->timer, APBTMR_N_CONTROL);
+ ctrl = apbt_readl_relaxed(&dw_ced->timer, APBTMR_N_CONTROL);
ctrl &= ~APBTMR_CONTROL_ENABLE;
- apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
+ apbt_writel_relaxed(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
/* write new count */
- apbt_writel(&dw_ced->timer, delta, APBTMR_N_LOAD_COUNT);
+ apbt_writel_relaxed(&dw_ced->timer, delta, APBTMR_N_LOAD_COUNT);
ctrl |= APBTMR_CONTROL_ENABLE;
- apbt_writel(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
+ apbt_writel_relaxed(&dw_ced->timer, ctrl, APBTMR_N_CONTROL);
return 0;
}
@@ -323,7 +334,7 @@ void dw_apb_clocksource_start(struct dw_apb_clocksource *dw_cs)
* start count down from 0xffff_ffff. this is done by toggling the
* enable bit then load initial load count to ~0.
*/
- unsigned long ctrl = apbt_readl(&dw_cs->timer, APBTMR_N_CONTROL);
+ u32 ctrl = apbt_readl(&dw_cs->timer, APBTMR_N_CONTROL);
ctrl &= ~APBTMR_CONTROL_ENABLE;
apbt_writel(&dw_cs->timer, ctrl, APBTMR_N_CONTROL);
@@ -338,11 +349,12 @@ void dw_apb_clocksource_start(struct dw_apb_clocksource *dw_cs)
static cycle_t __apbt_read_clocksource(struct clocksource *cs)
{
- unsigned long current_count;
+ u32 current_count;
struct dw_apb_clocksource *dw_cs =
clocksource_to_dw_apb_clocksource(cs);
- current_count = apbt_readl(&dw_cs->timer, APBTMR_N_CURRENT_VALUE);
+ current_count = apbt_readl_relaxed(&dw_cs->timer,
+ APBTMR_N_CURRENT_VALUE);
return (cycle_t)~current_count;
}
diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c
index a19a3f619cc7..860843cef572 100644
--- a/drivers/clocksource/dw_apb_timer_of.c
+++ b/drivers/clocksource/dw_apb_timer_of.c
@@ -16,6 +16,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <linux/delay.h>
#include <linux/dw_apb_timer.h>
#include <linux/of.h>
#include <linux/of_address.h>
@@ -130,6 +131,17 @@ static void __init init_sched_clock(void)
sched_clock_register(read_sched_clock, 32, sched_rate);
}
+#ifdef CONFIG_ARM
+static unsigned long dw_apb_delay_timer_read(void)
+{
+ return ~readl_relaxed(sched_io_base);
+}
+
+static struct delay_timer dw_apb_delay_timer = {
+ .read_current_timer = dw_apb_delay_timer_read,
+};
+#endif
+
static int num_called;
static void __init dw_apb_timer_init(struct device_node *timer)
{
@@ -142,6 +154,10 @@ static void __init dw_apb_timer_init(struct device_node *timer)
pr_debug("%s: found clocksource timer\n", __func__);
add_clocksource(timer);
init_sched_clock();
+#ifdef CONFIG_ARM
+ dw_apb_delay_timer.freq = sched_rate;
+ register_current_timer_delay(&dw_apb_delay_timer);
+#endif
break;
default:
break;
diff --git a/drivers/clocksource/h8300_timer16.c b/drivers/clocksource/h8300_timer16.c
index 0e076c6fc006..75c44079b345 100644
--- a/drivers/clocksource/h8300_timer16.c
+++ b/drivers/clocksource/h8300_timer16.c
@@ -4,85 +4,56 @@
* Copyright 2015 Yoshinori Sato <ysato@users.sourcefoge.jp>
*/
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/param.h>
-#include <linux/string.h>
-#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/init.h>
-#include <linux/platform_device.h>
#include <linux/clocksource.h>
-#include <linux/module.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/of.h>
-
-#include <asm/segment.h>
-#include <asm/irq.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#define TSTR 0
-#define TSNC 1
-#define TMDR 2
-#define TOLR 3
-#define TISRA 4
-#define TISRB 5
#define TISRC 6
#define TCR 0
-#define TIOR 1
#define TCNT 2
-#define GRA 4
-#define GRB 6
-
-#define FLAG_REPROGRAM (1 << 0)
-#define FLAG_SKIPEVENT (1 << 1)
-#define FLAG_IRQCONTEXT (1 << 2)
-#define FLAG_STARTED (1 << 3)
-#define ONESHOT 0
-#define PERIODIC 1
-
-#define RELATIVE 0
-#define ABSOLUTE 1
+#define bset(b, a) iowrite8(ioread8(a) | (1 << (b)), (a))
+#define bclr(b, a) iowrite8(ioread8(a) & ~(1 << (b)), (a))
struct timer16_priv {
- struct platform_device *pdev;
struct clocksource cs;
- struct irqaction irqaction;
unsigned long total_cycles;
- unsigned long mapbase;
- unsigned long mapcommon;
- unsigned long flags;
- unsigned short gra;
+ void __iomem *mapbase;
+ void __iomem *mapcommon;
unsigned short cs_enabled;
unsigned char enb;
- unsigned char imfa;
- unsigned char imiea;
unsigned char ovf;
- raw_spinlock_t lock;
- struct clk *clk;
+ unsigned char ovie;
};
static unsigned long timer16_get_counter(struct timer16_priv *p)
{
- unsigned long v1, v2, v3;
- int o1, o2;
+ unsigned short v1, v2, v3;
+ unsigned char o1, o2;
- o1 = ctrl_inb(p->mapcommon + TISRC) & p->ovf;
+ o1 = ioread8(p->mapcommon + TISRC) & p->ovf;
/* Make sure the timer value is stable. Stolen from acpi_pm.c */
do {
o2 = o1;
- v1 = ctrl_inw(p->mapbase + TCNT);
- v2 = ctrl_inw(p->mapbase + TCNT);
- v3 = ctrl_inw(p->mapbase + TCNT);
- o1 = ctrl_inb(p->mapcommon + TISRC) & p->ovf;
+ v1 = ioread16be(p->mapbase + TCNT);
+ v2 = ioread16be(p->mapbase + TCNT);
+ v3 = ioread16be(p->mapbase + TCNT);
+ o1 = ioread8(p->mapcommon + TISRC) & p->ovf;
} while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
|| (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
- v2 |= 0x10000;
- return v2;
+ if (likely(!o1))
+ return v2;
+ else
+ return v2 + 0x10000;
}
@@ -90,8 +61,7 @@ static irqreturn_t timer16_interrupt(int irq, void *dev_id)
{
struct timer16_priv *p = (struct timer16_priv *)dev_id;
- ctrl_outb(ctrl_inb(p->mapcommon + TISRA) & ~p->imfa,
- p->mapcommon + TISRA);
+ bclr(p->ovf, p->mapcommon + TISRC);
p->total_cycles += 0x10000;
return IRQ_HANDLED;
@@ -105,13 +75,10 @@ static inline struct timer16_priv *cs_to_priv(struct clocksource *cs)
static cycle_t timer16_clocksource_read(struct clocksource *cs)
{
struct timer16_priv *p = cs_to_priv(cs);
- unsigned long flags, raw;
- unsigned long value;
+ unsigned long raw, value;
- raw_spin_lock_irqsave(&p->lock, flags);
value = p->total_cycles;
raw = timer16_get_counter(p);
- raw_spin_unlock_irqrestore(&p->lock, flags);
return value + raw;
}
@@ -123,10 +90,10 @@ static int timer16_enable(struct clocksource *cs)
WARN_ON(p->cs_enabled);
p->total_cycles = 0;
- ctrl_outw(0x0000, p->mapbase + TCNT);
- ctrl_outb(0x83, p->mapbase + TCR);
- ctrl_outb(ctrl_inb(p->mapcommon + TSTR) | p->enb,
- p->mapcommon + TSTR);
+ iowrite16be(0x0000, p->mapbase + TCNT);
+ iowrite8(0x83, p->mapbase + TCR);
+ bset(p->ovie, p->mapcommon + TISRC);
+ bset(p->enb, p->mapcommon + TSTR);
p->cs_enabled = true;
return 0;
@@ -138,116 +105,83 @@ static void timer16_disable(struct clocksource *cs)
WARN_ON(!p->cs_enabled);
- ctrl_outb(ctrl_inb(p->mapcommon + TSTR) & ~p->enb,
- p->mapcommon + TSTR);
+ bclr(p->ovie, p->mapcommon + TISRC);
+ bclr(p->enb, p->mapcommon + TSTR);
p->cs_enabled = false;
}
+static struct timer16_priv timer16_priv = {
+ .cs = {
+ .name = "h8300_16timer",
+ .rating = 200,
+ .read = timer16_clocksource_read,
+ .enable = timer16_enable,
+ .disable = timer16_disable,
+ .mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+ },
+};
+
#define REG_CH 0
#define REG_COMM 1
-static int timer16_setup(struct timer16_priv *p, struct platform_device *pdev)
+static void __init h8300_16timer_init(struct device_node *node)
{
- struct resource *res[2];
+ void __iomem *base[2];
int ret, irq;
unsigned int ch;
+ struct clk *clk;
- p->pdev = pdev;
-
- res[REG_CH] = platform_get_resource(p->pdev,
- IORESOURCE_MEM, REG_CH);
- res[REG_COMM] = platform_get_resource(p->pdev,
- IORESOURCE_MEM, REG_COMM);
- if (!res[REG_CH] || !res[REG_COMM]) {
- dev_err(&p->pdev->dev, "failed to get I/O memory\n");
- return -ENXIO;
- }
- irq = platform_get_irq(p->pdev, 0);
- if (irq < 0) {
- dev_err(&p->pdev->dev, "failed to get irq\n");
- return irq;
+ clk = of_clk_get(node, 0);
+ if (IS_ERR(clk)) {
+ pr_err("failed to get clock for clocksource\n");
+ return;
}
- p->clk = clk_get(&p->pdev->dev, "fck");
- if (IS_ERR(p->clk)) {
- dev_err(&p->pdev->dev, "can't get clk\n");
- return PTR_ERR(p->clk);
+ base[REG_CH] = of_iomap(node, 0);
+ if (!base[REG_CH]) {
+ pr_err("failed to map registers for clocksource\n");
+ goto free_clk;
}
- of_property_read_u32(p->pdev->dev.of_node, "renesas,channel", &ch);
-
- p->pdev = pdev;
- p->mapbase = res[REG_CH]->start;
- p->mapcommon = res[REG_COMM]->start;
- p->enb = 1 << ch;
- p->imfa = 1 << ch;
- p->imiea = 1 << (4 + ch);
- p->cs.name = pdev->name;
- p->cs.rating = 200;
- p->cs.read = timer16_clocksource_read;
- p->cs.enable = timer16_enable;
- p->cs.disable = timer16_disable;
- p->cs.mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8);
- p->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
- ret = request_irq(irq, timer16_interrupt,
- IRQF_TIMER, pdev->name, p);
- if (ret < 0) {
- dev_err(&p->pdev->dev, "failed to request irq %d\n", irq);
- return ret;
+ base[REG_COMM] = of_iomap(node, 1);
+ if (!base[REG_COMM]) {
+ pr_err("failed to map registers for clocksource\n");
+ goto unmap_ch;
}
- clocksource_register_hz(&p->cs, clk_get_rate(p->clk) / 8);
-
- return 0;
-}
-
-static int timer16_probe(struct platform_device *pdev)
-{
- struct timer16_priv *p = platform_get_drvdata(pdev);
-
- if (p) {
- dev_info(&pdev->dev, "kept as earlytimer\n");
- return 0;
+ irq = irq_of_parse_and_map(node, 0);
+ if (!irq) {
+ pr_err("failed to get irq for clockevent\n");
+ goto unmap_comm;
}
- p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
- if (!p)
- return -ENOMEM;
+ of_property_read_u32(node, "renesas,channel", &ch);
- return timer16_setup(p, pdev);
-}
-
-static int timer16_remove(struct platform_device *pdev)
-{
- return -EBUSY;
-}
+ timer16_priv.mapbase = base[REG_CH];
+ timer16_priv.mapcommon = base[REG_COMM];
+ timer16_priv.enb = ch;
+ timer16_priv.ovf = ch;
+ timer16_priv.ovie = 4 + ch;
-static const struct of_device_id timer16_of_table[] = {
- { .compatible = "renesas,16bit-timer" },
- { }
-};
-static struct platform_driver timer16_driver = {
- .probe = timer16_probe,
- .remove = timer16_remove,
- .driver = {
- .name = "h8300h-16timer",
- .of_match_table = of_match_ptr(timer16_of_table),
+ ret = request_irq(irq, timer16_interrupt,
+ IRQF_TIMER, timer16_priv.cs.name, &timer16_priv);
+ if (ret < 0) {
+ pr_err("failed to request irq %d of clocksource\n", irq);
+ goto unmap_comm;
}
-};
-static int __init timer16_init(void)
-{
- return platform_driver_register(&timer16_driver);
-}
+ clocksource_register_hz(&timer16_priv.cs,
+ clk_get_rate(clk) / 8);
+ return;
-static void __exit timer16_exit(void)
-{
- platform_driver_unregister(&timer16_driver);
+unmap_comm:
+ iounmap(base[REG_COMM]);
+unmap_ch:
+ iounmap(base[REG_CH]);
+free_clk:
+ clk_put(clk);
}
-subsys_initcall(timer16_init);
-module_exit(timer16_exit);
-MODULE_AUTHOR("Yoshinori Sato");
-MODULE_DESCRIPTION("H8/300H 16bit Timer Driver");
-MODULE_LICENSE("GPL v2");
+CLOCKSOURCE_OF_DECLARE(h8300_16bit, "renesas,16bit-timer", h8300_16timer_init);
diff --git a/drivers/clocksource/h8300_timer8.c b/drivers/clocksource/h8300_timer8.c
index 44375d8b9bc4..c151941e1956 100644
--- a/drivers/clocksource/h8300_timer8.c
+++ b/drivers/clocksource/h8300_timer8.c
@@ -8,19 +8,15 @@
*/
#include <linux/errno.h>
-#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
#include <linux/clockchips.h>
-#include <linux/module.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/of.h>
-
-#include <asm/irq.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#define _8TCR 0
#define _8TCSR 2
@@ -28,126 +24,74 @@
#define TCORB 6
#define _8TCNT 8
-#define FLAG_REPROGRAM (1 << 0)
-#define FLAG_SKIPEVENT (1 << 1)
-#define FLAG_IRQCONTEXT (1 << 2)
+#define CMIEA 6
+#define CMFA 6
+
#define FLAG_STARTED (1 << 3)
-#define ONESHOT 0
-#define PERIODIC 1
+#define SCALE 64
-#define RELATIVE 0
-#define ABSOLUTE 1
+#define bset(b, a) iowrite8(ioread8(a) | (1 << (b)), (a))
+#define bclr(b, a) iowrite8(ioread8(a) & ~(1 << (b)), (a))
struct timer8_priv {
- struct platform_device *pdev;
struct clock_event_device ced;
- struct irqaction irqaction;
- unsigned long mapbase;
- raw_spinlock_t lock;
+ void __iomem *mapbase;
unsigned long flags;
unsigned int rate;
- unsigned int tcora;
- struct clk *pclk;
};
-static unsigned long timer8_get_counter(struct timer8_priv *p)
-{
- unsigned long v1, v2, v3;
- int o1, o2;
-
- o1 = ctrl_inb(p->mapbase + _8TCSR) & 0x20;
-
- /* Make sure the timer value is stable. Stolen from acpi_pm.c */
- do {
- o2 = o1;
- v1 = ctrl_inw(p->mapbase + _8TCNT);
- v2 = ctrl_inw(p->mapbase + _8TCNT);
- v3 = ctrl_inw(p->mapbase + _8TCNT);
- o1 = ctrl_inb(p->mapbase + _8TCSR) & 0x20;
- } while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
- || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
-
- v2 |= o1 << 10;
- return v2;
-}
-
static irqreturn_t timer8_interrupt(int irq, void *dev_id)
{
struct timer8_priv *p = dev_id;
- ctrl_outb(ctrl_inb(p->mapbase + _8TCSR) & ~0x40,
- p->mapbase + _8TCSR);
- p->flags |= FLAG_IRQCONTEXT;
- ctrl_outw(p->tcora, p->mapbase + TCORA);
- if (!(p->flags & FLAG_SKIPEVENT)) {
- if (clockevent_state_oneshot(&p->ced))
- ctrl_outw(0x0000, p->mapbase + _8TCR);
- p->ced.event_handler(&p->ced);
- }
- p->flags &= ~(FLAG_SKIPEVENT | FLAG_IRQCONTEXT);
+ if (clockevent_state_oneshot(&p->ced))
+ iowrite16be(0x0000, p->mapbase + _8TCR);
+
+ p->ced.event_handler(&p->ced);
+
+ bclr(CMFA, p->mapbase + _8TCSR);
return IRQ_HANDLED;
}
static void timer8_set_next(struct timer8_priv *p, unsigned long delta)
{
- unsigned long flags;
- unsigned long now;
-
- raw_spin_lock_irqsave(&p->lock, flags);
if (delta >= 0x10000)
- dev_warn(&p->pdev->dev, "delta out of range\n");
- now = timer8_get_counter(p);
- p->tcora = delta;
- ctrl_outb(ctrl_inb(p->mapbase + _8TCR) | 0x40, p->mapbase + _8TCR);
- if (delta > now)
- ctrl_outw(delta, p->mapbase + TCORA);
- else
- ctrl_outw(now + 1, p->mapbase + TCORA);
-
- raw_spin_unlock_irqrestore(&p->lock, flags);
+ pr_warn("delta out of range\n");
+ bclr(CMIEA, p->mapbase + _8TCR);
+ iowrite16be(delta, p->mapbase + TCORA);
+ iowrite16be(0x0000, p->mapbase + _8TCNT);
+ bclr(CMFA, p->mapbase + _8TCSR);
+ bset(CMIEA, p->mapbase + _8TCR);
}
static int timer8_enable(struct timer8_priv *p)
{
- p->rate = clk_get_rate(p->pclk) / 64;
- ctrl_outw(0xffff, p->mapbase + TCORA);
- ctrl_outw(0x0000, p->mapbase + _8TCNT);
- ctrl_outw(0x0c02, p->mapbase + _8TCR);
+ iowrite16be(0xffff, p->mapbase + TCORA);
+ iowrite16be(0x0000, p->mapbase + _8TCNT);
+ iowrite16be(0x0c02, p->mapbase + _8TCR);
return 0;
}
static int timer8_start(struct timer8_priv *p)
{
- int ret = 0;
- unsigned long flags;
-
- raw_spin_lock_irqsave(&p->lock, flags);
-
- if (!(p->flags & FLAG_STARTED))
- ret = timer8_enable(p);
+ int ret;
- if (ret)
- goto out;
- p->flags |= FLAG_STARTED;
+ if ((p->flags & FLAG_STARTED))
+ return 0;
- out:
- raw_spin_unlock_irqrestore(&p->lock, flags);
+ ret = timer8_enable(p);
+ if (!ret)
+ p->flags |= FLAG_STARTED;
return ret;
}
static void timer8_stop(struct timer8_priv *p)
{
- unsigned long flags;
-
- raw_spin_lock_irqsave(&p->lock, flags);
-
- ctrl_outw(0x0000, p->mapbase + _8TCR);
-
- raw_spin_unlock_irqrestore(&p->lock, flags);
+ iowrite16be(0x0000, p->mapbase + _8TCR);
}
static inline struct timer8_priv *ced_to_priv(struct clock_event_device *ced)
@@ -155,7 +99,7 @@ static inline struct timer8_priv *ced_to_priv(struct clock_event_device *ced)
return container_of(ced, struct timer8_priv, ced);
}
-static void timer8_clock_event_start(struct timer8_priv *p, int periodic)
+static void timer8_clock_event_start(struct timer8_priv *p, unsigned long delta)
{
struct clock_event_device *ced = &p->ced;
@@ -166,7 +110,7 @@ static void timer8_clock_event_start(struct timer8_priv *p, int periodic)
ced->max_delta_ns = clockevent_delta2ns(0xffff, ced);
ced->min_delta_ns = clockevent_delta2ns(0x0001, ced);
- timer8_set_next(p, periodic?(p->rate + HZ/2) / HZ:0x10000);
+ timer8_set_next(p, delta);
}
static int timer8_clock_event_shutdown(struct clock_event_device *ced)
@@ -179,9 +123,9 @@ static int timer8_clock_event_periodic(struct clock_event_device *ced)
{
struct timer8_priv *p = ced_to_priv(ced);
- dev_info(&p->pdev->dev, "used for periodic clock events\n");
+ pr_info("%s: used for periodic clock events\n", ced->name);
timer8_stop(p);
- timer8_clock_event_start(p, PERIODIC);
+ timer8_clock_event_start(p, (p->rate + HZ/2) / HZ);
return 0;
}
@@ -190,9 +134,9 @@ static int timer8_clock_event_oneshot(struct clock_event_device *ced)
{
struct timer8_priv *p = ced_to_priv(ced);
- dev_info(&p->pdev->dev, "used for oneshot clock events\n");
+ pr_info("%s: used for oneshot clock events\n", ced->name);
timer8_stop(p);
- timer8_clock_event_start(p, ONESHOT);
+ timer8_clock_event_start(p, 0x10000);
return 0;
}
@@ -208,110 +152,64 @@ static int timer8_clock_event_next(unsigned long delta,
return 0;
}
-static int timer8_setup(struct timer8_priv *p,
- struct platform_device *pdev)
+static struct timer8_priv timer8_priv = {
+ .ced = {
+ .name = "h8300_8timer",
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .rating = 200,
+ .set_next_event = timer8_clock_event_next,
+ .set_state_shutdown = timer8_clock_event_shutdown,
+ .set_state_periodic = timer8_clock_event_periodic,
+ .set_state_oneshot = timer8_clock_event_oneshot,
+ },
+};
+
+static void __init h8300_8timer_init(struct device_node *node)
{
- struct resource *res;
+ void __iomem *base;
int irq;
- int ret;
+ struct clk *clk;
- p->pdev = pdev;
-
- res = platform_get_resource(p->pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&p->pdev->dev, "failed to get I/O memory\n");
- return -ENXIO;
+ clk = of_clk_get(node, 0);
+ if (IS_ERR(clk)) {
+ pr_err("failed to get clock for clockevent\n");
+ return;
}
- irq = platform_get_irq(p->pdev, 0);
- if (irq < 0) {
- dev_err(&p->pdev->dev, "failed to get irq\n");
- return -ENXIO;
+ base = of_iomap(node, 0);
+ if (!base) {
+ pr_err("failed to map registers for clockevent\n");
+ goto free_clk;
}
- p->mapbase = res->start;
-
- p->irqaction.name = dev_name(&p->pdev->dev);
- p->irqaction.handler = timer8_interrupt;
- p->irqaction.dev_id = p;
- p->irqaction.flags = IRQF_TIMER;
-
- p->pclk = clk_get(&p->pdev->dev, "fck");
- if (IS_ERR(p->pclk)) {
- dev_err(&p->pdev->dev, "can't get clk\n");
- return PTR_ERR(p->pclk);
+ irq = irq_of_parse_and_map(node, 0);
+ if (!irq) {
+ pr_err("failed to get irq for clockevent\n");
+ goto unmap_reg;
}
- p->ced.name = pdev->name;
- p->ced.features = CLOCK_EVT_FEAT_PERIODIC |
- CLOCK_EVT_FEAT_ONESHOT;
- p->ced.rating = 200;
- p->ced.cpumask = cpumask_of(0);
- p->ced.set_next_event = timer8_clock_event_next;
- p->ced.set_state_shutdown = timer8_clock_event_shutdown;
- p->ced.set_state_periodic = timer8_clock_event_periodic;
- p->ced.set_state_oneshot = timer8_clock_event_oneshot;
-
- ret = setup_irq(irq, &p->irqaction);
- if (ret < 0) {
- dev_err(&p->pdev->dev,
- "failed to request irq %d\n", irq);
- return ret;
- }
- clockevents_register_device(&p->ced);
- platform_set_drvdata(pdev, p);
+ timer8_priv.mapbase = base;
- return 0;
-}
-
-static int timer8_probe(struct platform_device *pdev)
-{
- struct timer8_priv *p = platform_get_drvdata(pdev);
-
- if (p) {
- dev_info(&pdev->dev, "kept as earlytimer\n");
- return 0;
+ timer8_priv.rate = clk_get_rate(clk) / SCALE;
+ if (!timer8_priv.rate) {
+ pr_err("Failed to get rate for the clocksource\n");
+ goto unmap_reg;
}
- p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
- if (!p)
- return -ENOMEM;
-
- return timer8_setup(p, pdev);
-}
-
-static int timer8_remove(struct platform_device *pdev)
-{
- return -EBUSY;
-}
-
-static const struct of_device_id timer8_of_table[] __maybe_unused = {
- { .compatible = "renesas,8bit-timer" },
- { }
-};
-
-MODULE_DEVICE_TABLE(of, timer8_of_table);
-static struct platform_driver timer8_driver = {
- .probe = timer8_probe,
- .remove = timer8_remove,
- .driver = {
- .name = "h8300-8timer",
- .of_match_table = of_match_ptr(timer8_of_table),
+ if (request_irq(irq, timer8_interrupt, IRQF_TIMER,
+ timer8_priv.ced.name, &timer8_priv) < 0) {
+ pr_err("failed to request irq %d for clockevent\n", irq);
+ goto unmap_reg;
}
-};
-static int __init timer8_init(void)
-{
- return platform_driver_register(&timer8_driver);
-}
+ clockevents_config_and_register(&timer8_priv.ced,
+ timer8_priv.rate, 1, 0x0000ffff);
-static void __exit timer8_exit(void)
-{
- platform_driver_unregister(&timer8_driver);
+ return;
+unmap_reg:
+ iounmap(base);
+free_clk:
+ clk_put(clk);
}
-subsys_initcall(timer8_init);
-module_exit(timer8_exit);
-MODULE_AUTHOR("Yoshinori Sato");
-MODULE_DESCRIPTION("H8/300 8bit Timer Driver");
-MODULE_LICENSE("GPL v2");
+CLOCKSOURCE_OF_DECLARE(h8300_8bit, "renesas,8bit-timer", h8300_8timer_init);
diff --git a/drivers/clocksource/h8300_tpu.c b/drivers/clocksource/h8300_tpu.c
index 5487410bfabb..d4c1a287c262 100644
--- a/drivers/clocksource/h8300_tpu.c
+++ b/drivers/clocksource/h8300_tpu.c
@@ -1,42 +1,30 @@
/*
- * H8/300 TPU Driver
+ * H8S TPU Driver
*
* Copyright 2015 Yoshinori Sato <ysato@users.sourcefoge.jp>
*
*/
#include <linux/errno.h>
-#include <linux/sched.h>
#include <linux/kernel.h>
-#include <linux/interrupt.h>
#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
#include <linux/clocksource.h>
-#include <linux/module.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
-#include <asm/irq.h>
+#define TCR 0x0
+#define TSR 0x5
+#define TCNT 0x6
-#define TCR 0
-#define TMDR 1
-#define TIOR 2
-#define TER 4
-#define TSR 5
-#define TCNT 6
-#define TGRA 8
-#define TGRB 10
-#define TGRC 12
-#define TGRD 14
+#define TCFV 0x10
struct tpu_priv {
- struct platform_device *pdev;
struct clocksource cs;
- struct clk *clk;
- unsigned long mapbase1;
- unsigned long mapbase2;
+ void __iomem *mapbase1;
+ void __iomem *mapbase2;
raw_spinlock_t lock;
unsigned int cs_enabled;
};
@@ -45,8 +33,8 @@ static inline unsigned long read_tcnt32(struct tpu_priv *p)
{
unsigned long tcnt;
- tcnt = ctrl_inw(p->mapbase1 + TCNT) << 16;
- tcnt |= ctrl_inw(p->mapbase2 + TCNT);
+ tcnt = ioread16be(p->mapbase1 + TCNT) << 16;
+ tcnt |= ioread16be(p->mapbase2 + TCNT);
return tcnt;
}
@@ -55,7 +43,7 @@ static int tpu_get_counter(struct tpu_priv *p, unsigned long long *val)
unsigned long v1, v2, v3;
int o1, o2;
- o1 = ctrl_inb(p->mapbase1 + TSR) & 0x10;
+ o1 = ioread8(p->mapbase1 + TSR) & TCFV;
/* Make sure the timer value is stable. Stolen from acpi_pm.c */
do {
@@ -63,7 +51,7 @@ static int tpu_get_counter(struct tpu_priv *p, unsigned long long *val)
v1 = read_tcnt32(p);
v2 = read_tcnt32(p);
v3 = read_tcnt32(p);
- o1 = ctrl_inb(p->mapbase1 + TSR) & 0x10;
+ o1 = ioread8(p->mapbase1 + TSR) & TCFV;
} while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3)
|| (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2)));
@@ -96,10 +84,10 @@ static int tpu_clocksource_enable(struct clocksource *cs)
WARN_ON(p->cs_enabled);
- ctrl_outw(0, p->mapbase1 + TCNT);
- ctrl_outw(0, p->mapbase2 + TCNT);
- ctrl_outb(0x0f, p->mapbase1 + TCR);
- ctrl_outb(0x03, p->mapbase2 + TCR);
+ iowrite16be(0, p->mapbase1 + TCNT);
+ iowrite16be(0, p->mapbase2 + TCNT);
+ iowrite8(0x0f, p->mapbase1 + TCR);
+ iowrite8(0x03, p->mapbase2 + TCR);
p->cs_enabled = true;
return 0;
@@ -111,96 +99,59 @@ static void tpu_clocksource_disable(struct clocksource *cs)
WARN_ON(!p->cs_enabled);
- ctrl_outb(0, p->mapbase1 + TCR);
- ctrl_outb(0, p->mapbase2 + TCR);
+ iowrite8(0, p->mapbase1 + TCR);
+ iowrite8(0, p->mapbase2 + TCR);
p->cs_enabled = false;
}
+static struct tpu_priv tpu_priv = {
+ .cs = {
+ .name = "H8S_TPU",
+ .rating = 200,
+ .read = tpu_clocksource_read,
+ .enable = tpu_clocksource_enable,
+ .disable = tpu_clocksource_disable,
+ .mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+ },
+};
+
#define CH_L 0
#define CH_H 1
-static int __init tpu_setup(struct tpu_priv *p, struct platform_device *pdev)
+static void __init h8300_tpu_init(struct device_node *node)
{
- struct resource *res[2];
-
- p->pdev = pdev;
+ void __iomem *base[2];
+ struct clk *clk;
- res[CH_L] = platform_get_resource(p->pdev, IORESOURCE_MEM, CH_L);
- res[CH_H] = platform_get_resource(p->pdev, IORESOURCE_MEM, CH_H);
- if (!res[CH_L] || !res[CH_H]) {
- dev_err(&p->pdev->dev, "failed to get I/O memory\n");
- return -ENXIO;
+ clk = of_clk_get(node, 0);
+ if (IS_ERR(clk)) {
+ pr_err("failed to get clock for clocksource\n");
+ return;
}
- p->clk = clk_get(&p->pdev->dev, "fck");
- if (IS_ERR(p->clk)) {
- dev_err(&p->pdev->dev, "can't get clk\n");
- return PTR_ERR(p->clk);
+ base[CH_L] = of_iomap(node, CH_L);
+ if (!base[CH_L]) {
+ pr_err("failed to map registers for clocksource\n");
+ goto free_clk;
}
-
- p->mapbase1 = res[CH_L]->start;
- p->mapbase2 = res[CH_H]->start;
-
- p->cs.name = pdev->name;
- p->cs.rating = 200;
- p->cs.read = tpu_clocksource_read;
- p->cs.enable = tpu_clocksource_enable;
- p->cs.disable = tpu_clocksource_disable;
- p->cs.mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8);
- p->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
- clocksource_register_hz(&p->cs, clk_get_rate(p->clk) / 64);
- platform_set_drvdata(pdev, p);
-
- return 0;
-}
-
-static int tpu_probe(struct platform_device *pdev)
-{
- struct tpu_priv *p = platform_get_drvdata(pdev);
-
- if (p) {
- dev_info(&pdev->dev, "kept as earlytimer\n");
- return 0;
+ base[CH_H] = of_iomap(node, CH_H);
+ if (!base[CH_H]) {
+ pr_err("failed to map registers for clocksource\n");
+ goto unmap_L;
}
- p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
- if (!p)
- return -ENOMEM;
+ tpu_priv.mapbase1 = base[CH_L];
+ tpu_priv.mapbase2 = base[CH_H];
- return tpu_setup(p, pdev);
-}
-
-static int tpu_remove(struct platform_device *pdev)
-{
- return -EBUSY;
-}
-
-static const struct of_device_id tpu_of_table[] = {
- { .compatible = "renesas,tpu" },
- { }
-};
+ clocksource_register_hz(&tpu_priv.cs, clk_get_rate(clk) / 64);
-static struct platform_driver tpu_driver = {
- .probe = tpu_probe,
- .remove = tpu_remove,
- .driver = {
- .name = "h8s-tpu",
- .of_match_table = of_match_ptr(tpu_of_table),
- }
-};
-
-static int __init tpu_init(void)
-{
- return platform_driver_register(&tpu_driver);
-}
+ return;
-static void __exit tpu_exit(void)
-{
- platform_driver_unregister(&tpu_driver);
+unmap_L:
+ iounmap(base[CH_H]);
+free_clk:
+ clk_put(clk);
}
-subsys_initcall(tpu_init);
-module_exit(tpu_exit);
-MODULE_AUTHOR("Yoshinori Sato");
-MODULE_DESCRIPTION("H8S Timer Pulse Unit Driver");
-MODULE_LICENSE("GPL v2");
+CLOCKSOURCE_OF_DECLARE(h8300_tpu, "renesas,tpu", h8300_tpu_init);
diff --git a/drivers/clocksource/mmio.c b/drivers/clocksource/mmio.c
index 1593ade2a815..c4f7d7a9b689 100644
--- a/drivers/clocksource/mmio.c
+++ b/drivers/clocksource/mmio.c
@@ -55,7 +55,7 @@ int __init clocksource_mmio_init(void __iomem *base, const char *name,
{
struct clocksource_mmio *cs;
- if (bits > 32 || bits < 16)
+ if (bits > 64 || bits < 16)
return -EINVAL;
cs = kzalloc(sizeof(struct clocksource_mmio), GFP_KERNEL);
diff --git a/drivers/clocksource/mtk_timer.c b/drivers/clocksource/mtk_timer.c
index fbfc74685e6a..d67bc356488f 100644
--- a/drivers/clocksource/mtk_timer.c
+++ b/drivers/clocksource/mtk_timer.c
@@ -16,6 +16,8 @@
* GNU General Public License for more details.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
@@ -187,10 +189,8 @@ static void __init mtk_timer_init(struct device_node *node)
struct clk *clk;
evt = kzalloc(sizeof(*evt), GFP_KERNEL);
- if (!evt) {
- pr_warn("Can't allocate mtk clock event driver struct");
+ if (!evt)
return;
- }
evt->dev.name = "mtk_tick";
evt->dev.rating = 300;
@@ -204,31 +204,31 @@ static void __init mtk_timer_init(struct device_node *node)
evt->gpt_base = of_io_request_and_map(node, 0, "mtk-timer");
if (IS_ERR(evt->gpt_base)) {
- pr_warn("Can't get resource\n");
- return;
+ pr_err("Can't get resource\n");
+ goto err_kzalloc;
}
evt->dev.irq = irq_of_parse_and_map(node, 0);
if (evt->dev.irq <= 0) {
- pr_warn("Can't parse IRQ");
+ pr_err("Can't parse IRQ\n");
goto err_mem;
}
clk = of_clk_get(node, 0);
if (IS_ERR(clk)) {
- pr_warn("Can't get timer clock");
+ pr_err("Can't get timer clock\n");
goto err_irq;
}
if (clk_prepare_enable(clk)) {
- pr_warn("Can't prepare clock");
+ pr_err("Can't prepare clock\n");
goto err_clk_put;
}
rate = clk_get_rate(clk);
if (request_irq(evt->dev.irq, mtk_timer_interrupt,
IRQF_TIMER | IRQF_IRQPOLL, "mtk_timer", evt)) {
- pr_warn("failed to setup irq %d\n", evt->dev.irq);
+ pr_err("failed to setup irq %d\n", evt->dev.irq);
goto err_clk_disable;
}
@@ -260,5 +260,7 @@ err_mem:
iounmap(evt->gpt_base);
of_address_to_resource(node, 0, &res);
release_mem_region(res.start, resource_size(&res));
+err_kzalloc:
+ kfree(evt);
}
CLOCKSOURCE_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);
diff --git a/drivers/clocksource/rockchip_timer.c b/drivers/clocksource/rockchip_timer.c
index d3c1742ded1a..8c77a529d0d4 100644
--- a/drivers/clocksource/rockchip_timer.c
+++ b/drivers/clocksource/rockchip_timer.c
@@ -17,16 +17,16 @@
#define TIMER_NAME "rk_timer"
-#define TIMER_LOAD_COUNT0 0x00
-#define TIMER_LOAD_COUNT1 0x04
-#define TIMER_CONTROL_REG 0x10
-#define TIMER_INT_STATUS 0x18
+#define TIMER_LOAD_COUNT0 0x00
+#define TIMER_LOAD_COUNT1 0x04
+#define TIMER_CONTROL_REG 0x10
+#define TIMER_INT_STATUS 0x18
-#define TIMER_DISABLE 0x0
-#define TIMER_ENABLE 0x1
-#define TIMER_MODE_FREE_RUNNING (0 << 1)
-#define TIMER_MODE_USER_DEFINED_COUNT (1 << 1)
-#define TIMER_INT_UNMASK (1 << 2)
+#define TIMER_DISABLE 0x0
+#define TIMER_ENABLE 0x1
+#define TIMER_MODE_FREE_RUNNING (0 << 1)
+#define TIMER_MODE_USER_DEFINED_COUNT (1 << 1)
+#define TIMER_INT_UNMASK (1 << 2)
struct bc_timer {
struct clock_event_device ce;
@@ -49,14 +49,12 @@ static inline void __iomem *rk_base(struct clock_event_device *ce)
static inline void rk_timer_disable(struct clock_event_device *ce)
{
writel_relaxed(TIMER_DISABLE, rk_base(ce) + TIMER_CONTROL_REG);
- dsb();
}
static inline void rk_timer_enable(struct clock_event_device *ce, u32 flags)
{
writel_relaxed(TIMER_ENABLE | TIMER_INT_UNMASK | flags,
rk_base(ce) + TIMER_CONTROL_REG);
- dsb();
}
static void rk_timer_update_counter(unsigned long cycles,
@@ -64,13 +62,11 @@ static void rk_timer_update_counter(unsigned long cycles,
{
writel_relaxed(cycles, rk_base(ce) + TIMER_LOAD_COUNT0);
writel_relaxed(0, rk_base(ce) + TIMER_LOAD_COUNT1);
- dsb();
}
static void rk_timer_interrupt_clear(struct clock_event_device *ce)
{
writel_relaxed(1, rk_base(ce) + TIMER_INT_STATUS);
- dsb();
}
static inline int rk_timer_set_next_event(unsigned long cycles,
@@ -173,4 +169,5 @@ static void __init rk_timer_init(struct device_node *np)
clockevents_config_and_register(ce, bc_timer.freq, 1, UINT_MAX);
}
+
CLOCKSOURCE_OF_DECLARE(rk_timer, "rockchip,rk3288-timer", rk_timer_init);
diff --git a/drivers/clocksource/tango_xtal.c b/drivers/clocksource/tango_xtal.c
index d297b30d2bc0..2bcecafdeaea 100644
--- a/drivers/clocksource/tango_xtal.c
+++ b/drivers/clocksource/tango_xtal.c
@@ -19,19 +19,6 @@ static u64 notrace read_sched_clock(void)
return read_xtal_counter();
}
-static cycle_t read_clocksource(struct clocksource *cs)
-{
- return read_xtal_counter();
-}
-
-static struct clocksource tango_xtal = {
- .name = "tango-xtal",
- .rating = 350,
- .read = read_clocksource,
- .mask = CLOCKSOURCE_MASK(32),
- .flags = CLOCK_SOURCE_IS_CONTINUOUS,
-};
-
static void __init tango_clocksource_init(struct device_node *np)
{
struct clk *clk;
@@ -53,8 +40,9 @@ static void __init tango_clocksource_init(struct device_node *np)
delay_timer.freq = xtal_freq;
delay_timer.read_current_timer = read_xtal_counter;
- ret = clocksource_register_hz(&tango_xtal, xtal_freq);
- if (ret != 0) {
+ ret = clocksource_mmio_init(xtal_in_cnt, "tango-xtal", xtal_freq, 350,
+ 32, clocksource_mmio_readl_up);
+ if (!ret) {
pr_err("%s: registration failed\n", np->full_name);
return;
}
diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c
index 6ebda1177e79..38333aba3055 100644
--- a/drivers/clocksource/tegra20_timer.c
+++ b/drivers/clocksource/tegra20_timer.c
@@ -96,7 +96,8 @@ static struct clock_event_device tegra_clockevent = {
.name = "timer0",
.rating = 300,
.features = CLOCK_EVT_FEAT_ONESHOT |
- CLOCK_EVT_FEAT_PERIODIC,
+ CLOCK_EVT_FEAT_PERIODIC |
+ CLOCK_EVT_FEAT_DYNIRQ,
.set_next_event = tegra_timer_set_next_event,
.set_state_shutdown = tegra_timer_shutdown,
.set_state_periodic = tegra_timer_set_periodic,
diff --git a/drivers/clocksource/time-lpc32xx.c b/drivers/clocksource/time-lpc32xx.c
index a1c06a2bc77c..1316876b487a 100644
--- a/drivers/clocksource/time-lpc32xx.c
+++ b/drivers/clocksource/time-lpc32xx.c
@@ -125,7 +125,7 @@ static int __init lpc32xx_clocksource_init(struct device_node *np)
clk = of_clk_get_by_name(np, "timerclk");
if (IS_ERR(clk)) {
- pr_err("clock get failed (%lu)\n", PTR_ERR(clk));
+ pr_err("clock get failed (%ld)\n", PTR_ERR(clk));
return PTR_ERR(clk);
}
@@ -184,7 +184,7 @@ static int __init lpc32xx_clockevent_init(struct device_node *np)
clk = of_clk_get_by_name(np, "timerclk");
if (IS_ERR(clk)) {
- pr_err("clock get failed (%lu)\n", PTR_ERR(clk));
+ pr_err("clock get failed (%ld)\n", PTR_ERR(clk));
return PTR_ERR(clk);
}
diff --git a/drivers/clocksource/time-pistachio.c b/drivers/clocksource/time-pistachio.c
index bba679900054..3269d9ef7a18 100644
--- a/drivers/clocksource/time-pistachio.c
+++ b/drivers/clocksource/time-pistachio.c
@@ -84,7 +84,7 @@ pistachio_clocksource_read_cycles(struct clocksource *cs)
counter = gpt_readl(pcs->base, TIMER_CURRENT_VALUE, 0);
raw_spin_unlock_irqrestore(&pcs->lock, flags);
- return ~(cycle_t)counter;
+ return (cycle_t)~counter;
}
static u64 notrace pistachio_read_sched_clock(void)
diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c
index bca9573e036a..24c83f9efd87 100644
--- a/drivers/clocksource/timer-sun5i.c
+++ b/drivers/clocksource/timer-sun5i.c
@@ -152,13 +152,6 @@ static irqreturn_t sun5i_timer_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static cycle_t sun5i_clksrc_read(struct clocksource *clksrc)
-{
- struct sun5i_timer_clksrc *cs = to_sun5i_timer_clksrc(clksrc);
-
- return ~readl(cs->timer.base + TIMER_CNTVAL_LO_REG(1));
-}
-
static int sun5i_rate_cb_clksrc(struct notifier_block *nb,
unsigned long event, void *data)
{
@@ -217,13 +210,8 @@ static int __init sun5i_setup_clocksource(struct device_node *node,
writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
base + TIMER_CTL_REG(1));
- cs->clksrc.name = node->name;
- cs->clksrc.rating = 340;
- cs->clksrc.read = sun5i_clksrc_read;
- cs->clksrc.mask = CLOCKSOURCE_MASK(32);
- cs->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
-
- ret = clocksource_register_hz(&cs->clksrc, rate);
+ ret = clocksource_mmio_init(base + TIMER_CNTVAL_LO_REG(1), node->name,
+ rate, 340, 32, clocksource_mmio_readl_down);
if (ret) {
pr_err("Couldn't register clock source.\n");
goto err_remove_notifier;
diff --git a/drivers/clocksource/vt8500_timer.c b/drivers/clocksource/vt8500_timer.c
index a92e94b40b5b..ddb409274f45 100644
--- a/drivers/clocksource/vt8500_timer.c
+++ b/drivers/clocksource/vt8500_timer.c
@@ -30,7 +30,6 @@
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/delay.h>
-#include <asm/mach/time.h>
#include <linux/of.h>
#include <linux/of_address.h>
@@ -50,6 +49,8 @@
#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
+#define MIN_OSCR_DELTA 16
+
static void __iomem *regbase;
static cycle_t vt8500_timer_read(struct clocksource *cs)
@@ -80,7 +81,7 @@ static int vt8500_timer_set_next_event(unsigned long cycles,
cpu_relax();
writel((unsigned long)alarm, regbase + TIMER_MATCH_VAL);
- if ((signed)(alarm - clocksource.read(&clocksource)) <= 16)
+ if ((signed)(alarm - clocksource.read(&clocksource)) <= MIN_OSCR_DELTA)
return -ETIME;
writel(1, regbase + TIMER_IER_VAL);
@@ -151,7 +152,7 @@ static void __init vt8500_timer_init(struct device_node *np)
pr_err("%s: setup_irq failed for %s\n", __func__,
clockevent.name);
clockevents_config_and_register(&clockevent, VT8500_TIMER_HZ,
- 4, 0xf0000000);
+ MIN_OSCR_DELTA * 2, 0xf0000000);
}
CLOCKSOURCE_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init);
diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c
index d7373ca69c99..25693b045371 100644
--- a/drivers/connector/connector.c
+++ b/drivers/connector/connector.c
@@ -179,26 +179,21 @@ static int cn_call_callback(struct sk_buff *skb)
*
* It checks skb, netlink header and msg sizes, and calls callback helper.
*/
-static void cn_rx_skb(struct sk_buff *__skb)
+static void cn_rx_skb(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
- struct sk_buff *skb;
int len, err;
- skb = skb_get(__skb);
-
if (skb->len >= NLMSG_HDRLEN) {
nlh = nlmsg_hdr(skb);
len = nlmsg_len(nlh);
if (len < (int)sizeof(struct cn_msg) ||
skb->len < nlh->nlmsg_len ||
- len > CONNECTOR_MAX_MSG_SIZE) {
- kfree_skb(skb);
+ len > CONNECTOR_MAX_MSG_SIZE)
return;
- }
- err = cn_call_callback(skb);
+ err = cn_call_callback(skb_get(skb));
if (err < 0)
kfree_skb(skb);
}
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 8014c2307332..0031069b64c9 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -6,6 +6,8 @@
config ARM_BIG_LITTLE_CPUFREQ
tristate "Generic ARM big LITTLE CPUfreq driver"
depends on (ARM_CPU_TOPOLOGY || ARM64) && HAVE_CLK
+ # if CPU_THERMAL is on and THERMAL=m, ARM_BIT_LITTLE_CPUFREQ cannot be =y
+ depends on !CPU_THERMAL || THERMAL
select PM_OPP
help
This enables the Generic CPUfreq driver for ARM big.LITTLE platforms.
@@ -202,7 +204,7 @@ config ARM_SA1110_CPUFREQ
config ARM_SCPI_CPUFREQ
tristate "SCPI based CPUfreq driver"
- depends on ARM_BIG_LITTLE_CPUFREQ && ARM_SCPI_PROTOCOL
+ depends on ARM_BIG_LITTLE_CPUFREQ && ARM_SCPI_PROTOCOL && COMMON_CLK_SCPI
help
This adds the CPUfreq driver support for ARM big.LITTLE platforms
using SCPI protocol for CPU power management.
@@ -217,6 +219,16 @@ config ARM_SPEAR_CPUFREQ
help
This adds the CPUFreq driver support for SPEAr SOCs.
+config ARM_STI_CPUFREQ
+ tristate "STi CPUFreq support"
+ depends on SOC_STIH407
+ help
+ This driver uses the generic OPP framework to match the running
+ platform with a predefined set of suitable values. If not provided
+ we will fall-back so safe-values contained in Device Tree. Enable
+ this config option if you wish to add CPUFreq support for STi based
+ SoCs.
+
config ARM_TEGRA20_CPUFREQ
bool "Tegra20 CPUFreq support"
depends on ARCH_TEGRA
@@ -226,7 +238,7 @@ config ARM_TEGRA20_CPUFREQ
config ARM_TEGRA124_CPUFREQ
tristate "Tegra124 CPUFreq support"
- depends on ARCH_TEGRA && CPUFREQ_DT
+ depends on ARCH_TEGRA && CPUFREQ_DT && REGULATOR
default y
help
This adds the CPUFreq driver support for Tegra124 SOCs.
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index c0af1a1281c8..9e63fb1b09f8 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -73,6 +73,7 @@ obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa1100-cpufreq.o
obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o
obj-$(CONFIG_ARM_SCPI_CPUFREQ) += scpi-cpufreq.o
obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o
+obj-$(CONFIG_ARM_STI_CPUFREQ) += sti-cpufreq.o
obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o
obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o
obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
index cec1ee2d2f74..51eef87bbc37 100644
--- a/drivers/cpufreq/acpi-cpufreq.c
+++ b/drivers/cpufreq/acpi-cpufreq.c
@@ -135,7 +135,7 @@ static void boost_set_msrs(bool enable, const struct cpumask *cpumask)
wrmsr_on_cpus(cpumask, msr_addr, msrs);
}
-static int _store_boost(int val)
+static int set_boost(int val)
{
get_online_cpus();
boost_set_msrs(val, cpu_online_mask);
@@ -158,29 +158,24 @@ static ssize_t show_freqdomain_cpus(struct cpufreq_policy *policy, char *buf)
cpufreq_freq_attr_ro(freqdomain_cpus);
#ifdef CONFIG_X86_ACPI_CPUFREQ_CPB
-static ssize_t store_boost(const char *buf, size_t count)
+static ssize_t store_cpb(struct cpufreq_policy *policy, const char *buf,
+ size_t count)
{
int ret;
- unsigned long val = 0;
+ unsigned int val = 0;
- if (!acpi_cpufreq_driver.boost_supported)
+ if (!acpi_cpufreq_driver.set_boost)
return -EINVAL;
- ret = kstrtoul(buf, 10, &val);
- if (ret || (val > 1))
+ ret = kstrtouint(buf, 10, &val);
+ if (ret || val > 1)
return -EINVAL;
- _store_boost((int) val);
+ set_boost(val);
return count;
}
-static ssize_t store_cpb(struct cpufreq_policy *policy, const char *buf,
- size_t count)
-{
- return store_boost(buf, count);
-}
-
static ssize_t show_cpb(struct cpufreq_policy *policy, char *buf)
{
return sprintf(buf, "%u\n", acpi_cpufreq_driver.boost_enabled);
@@ -905,7 +900,6 @@ static struct cpufreq_driver acpi_cpufreq_driver = {
.resume = acpi_cpufreq_resume,
.name = "acpi-cpufreq",
.attr = acpi_cpufreq_attr,
- .set_boost = _store_boost,
};
static void __init acpi_cpufreq_boost_init(void)
@@ -916,7 +910,7 @@ static void __init acpi_cpufreq_boost_init(void)
if (!msrs)
return;
- acpi_cpufreq_driver.boost_supported = true;
+ acpi_cpufreq_driver.set_boost = set_boost;
acpi_cpufreq_driver.boost_enabled = boost_state(0);
cpu_notifier_register_begin();
diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c
index c5d256caa664..c251247ae661 100644
--- a/drivers/cpufreq/arm_big_little.c
+++ b/drivers/cpufreq/arm_big_little.c
@@ -23,6 +23,7 @@
#include <linux/cpu.h>
#include <linux/cpufreq.h>
#include <linux/cpumask.h>
+#include <linux/cpu_cooling.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/mutex.h>
@@ -55,6 +56,7 @@ static bool bL_switching_enabled;
#define ACTUAL_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq << 1 : freq)
#define VIRT_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq >> 1 : freq)
+static struct thermal_cooling_device *cdev[MAX_CLUSTERS];
static struct cpufreq_arm_bL_ops *arm_bL_ops;
static struct clk *clk[MAX_CLUSTERS];
static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS + 1];
@@ -493,6 +495,12 @@ static int bL_cpufreq_init(struct cpufreq_policy *policy)
static int bL_cpufreq_exit(struct cpufreq_policy *policy)
{
struct device *cpu_dev;
+ int cur_cluster = cpu_to_cluster(policy->cpu);
+
+ if (cur_cluster < MAX_CLUSTERS) {
+ cpufreq_cooling_unregister(cdev[cur_cluster]);
+ cdev[cur_cluster] = NULL;
+ }
cpu_dev = get_cpu_device(policy->cpu);
if (!cpu_dev) {
@@ -507,6 +515,38 @@ static int bL_cpufreq_exit(struct cpufreq_policy *policy)
return 0;
}
+static void bL_cpufreq_ready(struct cpufreq_policy *policy)
+{
+ struct device *cpu_dev = get_cpu_device(policy->cpu);
+ int cur_cluster = cpu_to_cluster(policy->cpu);
+ struct device_node *np;
+
+ /* Do not register a cpu_cooling device if we are in IKS mode */
+ if (cur_cluster >= MAX_CLUSTERS)
+ return;
+
+ np = of_node_get(cpu_dev->of_node);
+ if (WARN_ON(!np))
+ return;
+
+ if (of_find_property(np, "#cooling-cells", NULL)) {
+ u32 power_coefficient = 0;
+
+ of_property_read_u32(np, "dynamic-power-coefficient",
+ &power_coefficient);
+
+ cdev[cur_cluster] = of_cpufreq_power_cooling_register(np,
+ policy->related_cpus, power_coefficient, NULL);
+ if (IS_ERR(cdev[cur_cluster])) {
+ dev_err(cpu_dev,
+ "running cpufreq without cooling device: %ld\n",
+ PTR_ERR(cdev[cur_cluster]));
+ cdev[cur_cluster] = NULL;
+ }
+ }
+ of_node_put(np);
+}
+
static struct cpufreq_driver bL_cpufreq_driver = {
.name = "arm-big-little",
.flags = CPUFREQ_STICKY |
@@ -517,6 +557,7 @@ static struct cpufreq_driver bL_cpufreq_driver = {
.get = bL_cpufreq_get_rate,
.init = bL_cpufreq_init,
.exit = bL_cpufreq_exit,
+ .ready = bL_cpufreq_ready,
.attr = cpufreq_generic_attr,
};
diff --git a/drivers/cpufreq/blackfin-cpufreq.c b/drivers/cpufreq/blackfin-cpufreq.c
index a9f8e5bd0716..12e97d8a9db0 100644
--- a/drivers/cpufreq/blackfin-cpufreq.c
+++ b/drivers/cpufreq/blackfin-cpufreq.c
@@ -112,7 +112,7 @@ static unsigned int bfin_getfreq_khz(unsigned int cpu)
}
#ifdef CONFIG_BF60x
-unsigned long cpu_set_cclk(int cpu, unsigned long new)
+static int cpu_set_cclk(int cpu, unsigned long new)
{
struct clk *clk;
int ret;
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index e8cb334094b0..7c0bdfb1a2ca 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -98,10 +98,11 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
policy->max = cpu->perf_caps.highest_perf;
policy->cpuinfo.min_freq = policy->min;
policy->cpuinfo.max_freq = policy->max;
+ policy->shared_type = cpu->shared_type;
if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY)
cpumask_copy(policy->cpus, cpu->shared_cpu_map);
- else {
+ else if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL) {
/* Support only SW_ANY for now. */
pr_debug("Unsupported CPU co-ord type\n");
return -EFAULT;
diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c
index 90d64081ddb3..9bc37c437874 100644
--- a/drivers/cpufreq/cpufreq-dt.c
+++ b/drivers/cpufreq/cpufreq-dt.c
@@ -50,7 +50,8 @@ static int set_target(struct cpufreq_policy *policy, unsigned int index)
struct private_data *priv = policy->driver_data;
struct device *cpu_dev = priv->cpu_dev;
struct regulator *cpu_reg = priv->cpu_reg;
- unsigned long volt = 0, volt_old = 0, tol = 0;
+ unsigned long volt = 0, tol = 0;
+ int volt_old = 0;
unsigned int old_freq, new_freq;
long freq_Hz, freq_exact;
int ret;
@@ -83,7 +84,7 @@ static int set_target(struct cpufreq_policy *policy, unsigned int index)
opp_freq / 1000, volt);
}
- dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n",
+ dev_dbg(cpu_dev, "%u MHz, %d mV --> %u MHz, %ld mV\n",
old_freq / 1000, (volt_old > 0) ? volt_old / 1000 : -1,
new_freq / 1000, volt ? volt / 1000 : -1);
@@ -407,8 +408,13 @@ static void cpufreq_ready(struct cpufreq_policy *policy)
* thermal DT code takes care of matching them.
*/
if (of_find_property(np, "#cooling-cells", NULL)) {
- priv->cdev = of_cpufreq_cooling_register(np,
- policy->related_cpus);
+ u32 power_coefficient = 0;
+
+ of_property_read_u32(np, "dynamic-power-coefficient",
+ &power_coefficient);
+
+ priv->cdev = of_cpufreq_power_cooling_register(np,
+ policy->related_cpus, power_coefficient, NULL);
if (IS_ERR(priv->cdev)) {
dev_err(priv->cpu_dev,
"running cpufreq without cooling device: %ld\n",
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 7c48e7316d91..c35e7da1ed7a 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -976,10 +976,14 @@ static int cpufreq_init_policy(struct cpufreq_policy *policy)
new_policy.governor = gov;
- /* Use the default policy if its valid. */
- if (cpufreq_driver->setpolicy)
- cpufreq_parse_governor(gov->name, &new_policy.policy, NULL);
-
+ /* Use the default policy if there is no last_policy. */
+ if (cpufreq_driver->setpolicy) {
+ if (policy->last_policy)
+ new_policy.policy = policy->last_policy;
+ else
+ cpufreq_parse_governor(gov->name, &new_policy.policy,
+ NULL);
+ }
/* set default policy */
return cpufreq_set_policy(policy, &new_policy);
}
@@ -1330,6 +1334,8 @@ static void cpufreq_offline_prepare(unsigned int cpu)
if (has_target())
strncpy(policy->last_governor, policy->governor->name,
CPUFREQ_NAME_LEN);
+ else
+ policy->last_policy = policy->policy;
} else if (cpu == policy->cpu) {
/* Nominate new CPU */
policy->cpu = cpumask_any(policy->cpus);
@@ -1401,13 +1407,10 @@ static void cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif)
}
cpumask_clear_cpu(cpu, policy->real_cpus);
+ remove_cpu_dev_symlink(policy, cpu);
- if (cpumask_empty(policy->real_cpus)) {
+ if (cpumask_empty(policy->real_cpus))
cpufreq_policy_free(policy, true);
- return;
- }
-
- remove_cpu_dev_symlink(policy, cpu);
}
static void handle_update(struct work_struct *work)
@@ -2327,29 +2330,15 @@ int cpufreq_boost_trigger_state(int state)
return ret;
}
-int cpufreq_boost_supported(void)
+static bool cpufreq_boost_supported(void)
{
- if (likely(cpufreq_driver))
- return cpufreq_driver->boost_supported;
-
- return 0;
+ return likely(cpufreq_driver) && cpufreq_driver->set_boost;
}
-EXPORT_SYMBOL_GPL(cpufreq_boost_supported);
static int create_boost_sysfs_file(void)
{
int ret;
- if (!cpufreq_boost_supported())
- return 0;
-
- /*
- * Check if driver provides function to enable boost -
- * if not, use cpufreq_boost_set_sw as default
- */
- if (!cpufreq_driver->set_boost)
- cpufreq_driver->set_boost = cpufreq_boost_set_sw;
-
ret = sysfs_create_file(cpufreq_global_kobject, &boost.attr);
if (ret)
pr_err("%s: cannot register global BOOST sysfs file\n",
@@ -2372,7 +2361,7 @@ int cpufreq_enable_boost_support(void)
if (cpufreq_boost_supported())
return 0;
- cpufreq_driver->boost_supported = true;
+ cpufreq_driver->set_boost = cpufreq_boost_set_sw;
/* This will get removed on driver unregister */
return create_boost_sysfs_file();
@@ -2432,9 +2421,11 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
if (driver_data->setpolicy)
driver_data->flags |= CPUFREQ_CONST_LOOPS;
- ret = create_boost_sysfs_file();
- if (ret)
- goto err_null_driver;
+ if (cpufreq_boost_supported()) {
+ ret = create_boost_sysfs_file();
+ if (ret)
+ goto err_null_driver;
+ }
ret = subsys_interface_register(&cpufreq_interface);
if (ret)
diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c
index 1fa1deb6e91f..606ad74abe6e 100644
--- a/drivers/cpufreq/cpufreq_conservative.c
+++ b/drivers/cpufreq/cpufreq_conservative.c
@@ -115,13 +115,13 @@ static void cs_check_cpu(int cpu, unsigned int load)
}
}
-static unsigned int cs_dbs_timer(struct cpu_dbs_info *cdbs,
- struct dbs_data *dbs_data, bool modify_all)
+static unsigned int cs_dbs_timer(struct cpufreq_policy *policy, bool modify_all)
{
+ struct dbs_data *dbs_data = policy->governor_data;
struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
if (modify_all)
- dbs_check_cpu(dbs_data, cdbs->shared->policy->cpu);
+ dbs_check_cpu(dbs_data, policy->cpu);
return delay_for_sampling_rate(cs_tuners->sampling_rate);
}
diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c
index b260576ddb12..bab3a514ec12 100644
--- a/drivers/cpufreq/cpufreq_governor.c
+++ b/drivers/cpufreq/cpufreq_governor.c
@@ -84,6 +84,9 @@ void dbs_check_cpu(struct dbs_data *dbs_data, int cpu)
(cur_wall_time - j_cdbs->prev_cpu_wall);
j_cdbs->prev_cpu_wall = cur_wall_time;
+ if (cur_idle_time < j_cdbs->prev_cpu_idle)
+ cur_idle_time = j_cdbs->prev_cpu_idle;
+
idle_time = (unsigned int)
(cur_idle_time - j_cdbs->prev_cpu_idle);
j_cdbs->prev_cpu_idle = cur_idle_time;
@@ -158,47 +161,55 @@ void dbs_check_cpu(struct dbs_data *dbs_data, int cpu)
}
EXPORT_SYMBOL_GPL(dbs_check_cpu);
-static inline void __gov_queue_work(int cpu, struct dbs_data *dbs_data,
- unsigned int delay)
+void gov_add_timers(struct cpufreq_policy *policy, unsigned int delay)
{
- struct cpu_dbs_info *cdbs = dbs_data->cdata->get_cpu_cdbs(cpu);
-
- mod_delayed_work_on(cpu, system_wq, &cdbs->dwork, delay);
-}
-
-void gov_queue_work(struct dbs_data *dbs_data, struct cpufreq_policy *policy,
- unsigned int delay, bool all_cpus)
-{
- int i;
+ struct dbs_data *dbs_data = policy->governor_data;
+ struct cpu_dbs_info *cdbs;
+ int cpu;
- if (!all_cpus) {
- /*
- * Use raw_smp_processor_id() to avoid preemptible warnings.
- * We know that this is only called with all_cpus == false from
- * works that have been queued with *_work_on() functions and
- * those works are canceled during CPU_DOWN_PREPARE so they
- * can't possibly run on any other CPU.
- */
- __gov_queue_work(raw_smp_processor_id(), dbs_data, delay);
- } else {
- for_each_cpu(i, policy->cpus)
- __gov_queue_work(i, dbs_data, delay);
+ for_each_cpu(cpu, policy->cpus) {
+ cdbs = dbs_data->cdata->get_cpu_cdbs(cpu);
+ cdbs->timer.expires = jiffies + delay;
+ add_timer_on(&cdbs->timer, cpu);
}
}
-EXPORT_SYMBOL_GPL(gov_queue_work);
+EXPORT_SYMBOL_GPL(gov_add_timers);
-static inline void gov_cancel_work(struct dbs_data *dbs_data,
- struct cpufreq_policy *policy)
+static inline void gov_cancel_timers(struct cpufreq_policy *policy)
{
+ struct dbs_data *dbs_data = policy->governor_data;
struct cpu_dbs_info *cdbs;
int i;
for_each_cpu(i, policy->cpus) {
cdbs = dbs_data->cdata->get_cpu_cdbs(i);
- cancel_delayed_work_sync(&cdbs->dwork);
+ del_timer_sync(&cdbs->timer);
}
}
+void gov_cancel_work(struct cpu_common_dbs_info *shared)
+{
+ /* Tell dbs_timer_handler() to skip queuing up work items. */
+ atomic_inc(&shared->skip_work);
+ /*
+ * If dbs_timer_handler() is already running, it may not notice the
+ * incremented skip_work, so wait for it to complete to prevent its work
+ * item from being queued up after the cancel_work_sync() below.
+ */
+ gov_cancel_timers(shared->policy);
+ /*
+ * In case dbs_timer_handler() managed to run and spawn a work item
+ * before the timers have been canceled, wait for that work item to
+ * complete and then cancel all of the timers set up by it. If
+ * dbs_timer_handler() runs again at that point, it will see the
+ * positive value of skip_work and won't spawn any more work items.
+ */
+ cancel_work_sync(&shared->work);
+ gov_cancel_timers(shared->policy);
+ atomic_set(&shared->skip_work, 0);
+}
+EXPORT_SYMBOL_GPL(gov_cancel_work);
+
/* Will return if we need to evaluate cpu load again or not */
static bool need_load_eval(struct cpu_common_dbs_info *shared,
unsigned int sampling_rate)
@@ -217,29 +228,21 @@ static bool need_load_eval(struct cpu_common_dbs_info *shared,
return true;
}
-static void dbs_timer(struct work_struct *work)
+static void dbs_work_handler(struct work_struct *work)
{
- struct cpu_dbs_info *cdbs = container_of(work, struct cpu_dbs_info,
- dwork.work);
- struct cpu_common_dbs_info *shared = cdbs->shared;
+ struct cpu_common_dbs_info *shared = container_of(work, struct
+ cpu_common_dbs_info, work);
struct cpufreq_policy *policy;
struct dbs_data *dbs_data;
unsigned int sampling_rate, delay;
- bool modify_all = true;
-
- mutex_lock(&shared->timer_mutex);
+ bool eval_load;
policy = shared->policy;
-
- /*
- * Governor might already be disabled and there is no point continuing
- * with the work-handler.
- */
- if (!policy)
- goto unlock;
-
dbs_data = policy->governor_data;
+ /* Kill all timers */
+ gov_cancel_timers(policy);
+
if (dbs_data->cdata->governor == GOV_CONSERVATIVE) {
struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
@@ -250,14 +253,37 @@ static void dbs_timer(struct work_struct *work)
sampling_rate = od_tuners->sampling_rate;
}
- if (!need_load_eval(cdbs->shared, sampling_rate))
- modify_all = false;
-
- delay = dbs_data->cdata->gov_dbs_timer(cdbs, dbs_data, modify_all);
- gov_queue_work(dbs_data, policy, delay, modify_all);
+ eval_load = need_load_eval(shared, sampling_rate);
-unlock:
+ /*
+ * Make sure cpufreq_governor_limits() isn't evaluating load in
+ * parallel.
+ */
+ mutex_lock(&shared->timer_mutex);
+ delay = dbs_data->cdata->gov_dbs_timer(policy, eval_load);
mutex_unlock(&shared->timer_mutex);
+
+ atomic_dec(&shared->skip_work);
+
+ gov_add_timers(policy, delay);
+}
+
+static void dbs_timer_handler(unsigned long data)
+{
+ struct cpu_dbs_info *cdbs = (struct cpu_dbs_info *)data;
+ struct cpu_common_dbs_info *shared = cdbs->shared;
+
+ /*
+ * Timer handler may not be allowed to queue the work at the moment,
+ * because:
+ * - Another timer handler has done that
+ * - We are stopping the governor
+ * - Or we are updating the sampling rate of the ondemand governor
+ */
+ if (atomic_inc_return(&shared->skip_work) > 1)
+ atomic_dec(&shared->skip_work);
+ else
+ queue_work(system_wq, &shared->work);
}
static void set_sampling_rate(struct dbs_data *dbs_data,
@@ -287,6 +313,9 @@ static int alloc_common_dbs_info(struct cpufreq_policy *policy,
for_each_cpu(j, policy->related_cpus)
cdata->get_cpu_cdbs(j)->shared = shared;
+ mutex_init(&shared->timer_mutex);
+ atomic_set(&shared->skip_work, 0);
+ INIT_WORK(&shared->work, dbs_work_handler);
return 0;
}
@@ -297,6 +326,8 @@ static void free_common_dbs_info(struct cpufreq_policy *policy,
struct cpu_common_dbs_info *shared = cdbs->shared;
int j;
+ mutex_destroy(&shared->timer_mutex);
+
for_each_cpu(j, policy->cpus)
cdata->get_cpu_cdbs(j)->shared = NULL;
@@ -433,7 +464,6 @@ static int cpufreq_governor_start(struct cpufreq_policy *policy,
shared->policy = policy;
shared->time_stamp = ktime_get();
- mutex_init(&shared->timer_mutex);
for_each_cpu(j, policy->cpus) {
struct cpu_dbs_info *j_cdbs = cdata->get_cpu_cdbs(j);
@@ -450,7 +480,9 @@ static int cpufreq_governor_start(struct cpufreq_policy *policy,
if (ignore_nice)
j_cdbs->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE];
- INIT_DEFERRABLE_WORK(&j_cdbs->dwork, dbs_timer);
+ __setup_timer(&j_cdbs->timer, dbs_timer_handler,
+ (unsigned long)j_cdbs,
+ TIMER_DEFERRABLE | TIMER_IRQSAFE);
}
if (cdata->governor == GOV_CONSERVATIVE) {
@@ -468,8 +500,7 @@ static int cpufreq_governor_start(struct cpufreq_policy *policy,
od_ops->powersave_bias_init_cpu(cpu);
}
- gov_queue_work(dbs_data, policy, delay_for_sampling_rate(sampling_rate),
- true);
+ gov_add_timers(policy, delay_for_sampling_rate(sampling_rate));
return 0;
}
@@ -483,18 +514,9 @@ static int cpufreq_governor_stop(struct cpufreq_policy *policy,
if (!shared || !shared->policy)
return -EBUSY;
- /*
- * Work-handler must see this updated, as it should not proceed any
- * further after governor is disabled. And so timer_mutex is taken while
- * updating this value.
- */
- mutex_lock(&shared->timer_mutex);
+ gov_cancel_work(shared);
shared->policy = NULL;
- mutex_unlock(&shared->timer_mutex);
-
- gov_cancel_work(dbs_data, policy);
- mutex_destroy(&shared->timer_mutex);
return 0;
}
diff --git a/drivers/cpufreq/cpufreq_governor.h b/drivers/cpufreq/cpufreq_governor.h
index 5621bb03e874..91e767a058a7 100644
--- a/drivers/cpufreq/cpufreq_governor.h
+++ b/drivers/cpufreq/cpufreq_governor.h
@@ -17,6 +17,7 @@
#ifndef _CPUFREQ_GOVERNOR_H
#define _CPUFREQ_GOVERNOR_H
+#include <linux/atomic.h>
#include <linux/cpufreq.h>
#include <linux/kernel_stat.h>
#include <linux/module.h>
@@ -132,12 +133,14 @@ static void *get_cpu_dbs_info_s(int cpu) \
struct cpu_common_dbs_info {
struct cpufreq_policy *policy;
/*
- * percpu mutex that serializes governor limit change with dbs_timer
- * invocation. We do not want dbs_timer to run when user is changing
- * the governor or limits.
+ * Per policy mutex that serializes load evaluation from limit-change
+ * and work-handler.
*/
struct mutex timer_mutex;
+
ktime_t time_stamp;
+ atomic_t skip_work;
+ struct work_struct work;
};
/* Per cpu structures */
@@ -152,7 +155,7 @@ struct cpu_dbs_info {
* wake-up from idle.
*/
unsigned int prev_load;
- struct delayed_work dwork;
+ struct timer_list timer;
struct cpu_common_dbs_info *shared;
};
@@ -209,8 +212,7 @@ struct common_dbs_data {
struct cpu_dbs_info *(*get_cpu_cdbs)(int cpu);
void *(*get_cpu_dbs_info_s)(int cpu);
- unsigned int (*gov_dbs_timer)(struct cpu_dbs_info *cdbs,
- struct dbs_data *dbs_data,
+ unsigned int (*gov_dbs_timer)(struct cpufreq_policy *policy,
bool modify_all);
void (*gov_check_cpu)(int cpu, unsigned int load);
int (*init)(struct dbs_data *dbs_data, bool notify);
@@ -269,11 +271,11 @@ static ssize_t show_sampling_rate_min_gov_pol \
extern struct mutex cpufreq_governor_lock;
+void gov_add_timers(struct cpufreq_policy *policy, unsigned int delay);
+void gov_cancel_work(struct cpu_common_dbs_info *shared);
void dbs_check_cpu(struct dbs_data *dbs_data, int cpu);
int cpufreq_governor_dbs(struct cpufreq_policy *policy,
struct common_dbs_data *cdata, unsigned int event);
-void gov_queue_work(struct dbs_data *dbs_data, struct cpufreq_policy *policy,
- unsigned int delay, bool all_cpus);
void od_register_powersave_bias_handler(unsigned int (*f)
(struct cpufreq_policy *, unsigned int, unsigned int),
unsigned int powersave_bias);
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index 03ac6ce54042..eae51070c034 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -191,10 +191,9 @@ static void od_check_cpu(int cpu, unsigned int load)
}
}
-static unsigned int od_dbs_timer(struct cpu_dbs_info *cdbs,
- struct dbs_data *dbs_data, bool modify_all)
+static unsigned int od_dbs_timer(struct cpufreq_policy *policy, bool modify_all)
{
- struct cpufreq_policy *policy = cdbs->shared->policy;
+ struct dbs_data *dbs_data = policy->governor_data;
unsigned int cpu = policy->cpu;
struct od_cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info,
cpu);
@@ -247,40 +246,66 @@ static void update_sampling_rate(struct dbs_data *dbs_data,
unsigned int new_rate)
{
struct od_dbs_tuners *od_tuners = dbs_data->tuners;
+ struct cpumask cpumask;
int cpu;
od_tuners->sampling_rate = new_rate = max(new_rate,
dbs_data->min_sampling_rate);
- for_each_online_cpu(cpu) {
+ /*
+ * Lock governor so that governor start/stop can't execute in parallel.
+ */
+ mutex_lock(&od_dbs_cdata.mutex);
+
+ cpumask_copy(&cpumask, cpu_online_mask);
+
+ for_each_cpu(cpu, &cpumask) {
struct cpufreq_policy *policy;
struct od_cpu_dbs_info_s *dbs_info;
+ struct cpu_dbs_info *cdbs;
+ struct cpu_common_dbs_info *shared;
unsigned long next_sampling, appointed_at;
- policy = cpufreq_cpu_get(cpu);
- if (!policy)
- continue;
- if (policy->governor != &cpufreq_gov_ondemand) {
- cpufreq_cpu_put(policy);
- continue;
- }
dbs_info = &per_cpu(od_cpu_dbs_info, cpu);
- cpufreq_cpu_put(policy);
+ cdbs = &dbs_info->cdbs;
+ shared = cdbs->shared;
- if (!delayed_work_pending(&dbs_info->cdbs.dwork))
+ /*
+ * A valid shared and shared->policy means governor hasn't
+ * stopped or exited yet.
+ */
+ if (!shared || !shared->policy)
+ continue;
+
+ policy = shared->policy;
+
+ /* clear all CPUs of this policy */
+ cpumask_andnot(&cpumask, &cpumask, policy->cpus);
+
+ /*
+ * Update sampling rate for CPUs whose policy is governed by
+ * dbs_data. In case of governor_per_policy, only a single
+ * policy will be governed by dbs_data, otherwise there can be
+ * multiple policies that are governed by the same dbs_data.
+ */
+ if (dbs_data != policy->governor_data)
continue;
+ /*
+ * Checking this for any CPU should be fine, timers for all of
+ * them are scheduled together.
+ */
next_sampling = jiffies + usecs_to_jiffies(new_rate);
- appointed_at = dbs_info->cdbs.dwork.timer.expires;
+ appointed_at = dbs_info->cdbs.timer.expires;
if (time_before(next_sampling, appointed_at)) {
- cancel_delayed_work_sync(&dbs_info->cdbs.dwork);
-
- gov_queue_work(dbs_data, policy,
- usecs_to_jiffies(new_rate), true);
+ gov_cancel_work(shared);
+ gov_add_timers(policy, usecs_to_jiffies(new_rate));
}
}
+
+ mutex_unlock(&od_dbs_cdata.mutex);
}
static ssize_t store_sampling_rate(struct dbs_data *dbs_data, const char *buf,
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 001a532e342e..cd83d477e32d 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -66,6 +66,7 @@ static inline int ceiling_fp(int32_t x)
struct sample {
int32_t core_pct_busy;
+ int32_t busy_scaled;
u64 aperf;
u64 mperf;
u64 tsc;
@@ -112,6 +113,7 @@ struct cpudata {
u64 prev_aperf;
u64 prev_mperf;
u64 prev_tsc;
+ u64 prev_cummulative_iowait;
struct sample sample;
};
@@ -133,6 +135,7 @@ struct pstate_funcs {
int (*get_scaling)(void);
void (*set)(struct cpudata*, int pstate);
void (*get_vid)(struct cpudata *);
+ int32_t (*get_target_pstate)(struct cpudata *);
};
struct cpu_defaults {
@@ -140,6 +143,9 @@ struct cpu_defaults {
struct pstate_funcs funcs;
};
+static inline int32_t get_target_pstate_use_performance(struct cpudata *cpu);
+static inline int32_t get_target_pstate_use_cpu_load(struct cpudata *cpu);
+
static struct pstate_adjust_policy pid_params;
static struct pstate_funcs pstate_funcs;
static int hwp_active;
@@ -738,6 +744,7 @@ static struct cpu_defaults core_params = {
.get_turbo = core_get_turbo_pstate,
.get_scaling = core_get_scaling,
.set = core_set_pstate,
+ .get_target_pstate = get_target_pstate_use_performance,
},
};
@@ -758,6 +765,7 @@ static struct cpu_defaults silvermont_params = {
.set = atom_set_pstate,
.get_scaling = silvermont_get_scaling,
.get_vid = atom_get_vid,
+ .get_target_pstate = get_target_pstate_use_cpu_load,
},
};
@@ -778,6 +786,7 @@ static struct cpu_defaults airmont_params = {
.set = atom_set_pstate,
.get_scaling = airmont_get_scaling,
.get_vid = atom_get_vid,
+ .get_target_pstate = get_target_pstate_use_cpu_load,
},
};
@@ -797,6 +806,7 @@ static struct cpu_defaults knl_params = {
.get_turbo = knl_get_turbo_pstate,
.get_scaling = core_get_scaling,
.set = core_set_pstate,
+ .get_target_pstate = get_target_pstate_use_performance,
},
};
@@ -882,12 +892,11 @@ static inline void intel_pstate_sample(struct cpudata *cpu)
local_irq_save(flags);
rdmsrl(MSR_IA32_APERF, aperf);
rdmsrl(MSR_IA32_MPERF, mperf);
- if (cpu->prev_mperf == mperf) {
+ tsc = rdtsc();
+ if ((cpu->prev_mperf == mperf) || (cpu->prev_tsc == tsc)) {
local_irq_restore(flags);
return;
}
-
- tsc = rdtsc();
local_irq_restore(flags);
cpu->last_sample_time = cpu->sample.time;
@@ -922,7 +931,43 @@ static inline void intel_pstate_set_sample_time(struct cpudata *cpu)
mod_timer_pinned(&cpu->timer, jiffies + delay);
}
-static inline int32_t intel_pstate_get_scaled_busy(struct cpudata *cpu)
+static inline int32_t get_target_pstate_use_cpu_load(struct cpudata *cpu)
+{
+ struct sample *sample = &cpu->sample;
+ u64 cummulative_iowait, delta_iowait_us;
+ u64 delta_iowait_mperf;
+ u64 mperf, now;
+ int32_t cpu_load;
+
+ cummulative_iowait = get_cpu_iowait_time_us(cpu->cpu, &now);
+
+ /*
+ * Convert iowait time into number of IO cycles spent at max_freq.
+ * IO is considered as busy only for the cpu_load algorithm. For
+ * performance this is not needed since we always try to reach the
+ * maximum P-State, so we are already boosting the IOs.
+ */
+ delta_iowait_us = cummulative_iowait - cpu->prev_cummulative_iowait;
+ delta_iowait_mperf = div64_u64(delta_iowait_us * cpu->pstate.scaling *
+ cpu->pstate.max_pstate, MSEC_PER_SEC);
+
+ mperf = cpu->sample.mperf + delta_iowait_mperf;
+ cpu->prev_cummulative_iowait = cummulative_iowait;
+
+
+ /*
+ * The load can be estimated as the ratio of the mperf counter
+ * running at a constant frequency during active periods
+ * (C0) and the time stamp counter running at the same frequency
+ * also during C-states.
+ */
+ cpu_load = div64_u64(int_tofp(100) * mperf, sample->tsc);
+ cpu->sample.busy_scaled = cpu_load;
+
+ return cpu->pstate.current_pstate - pid_calc(&cpu->pid, cpu_load);
+}
+
+static inline int32_t get_target_pstate_use_performance(struct cpudata *cpu)
{
int32_t core_busy, max_pstate, current_pstate, sample_ratio;
s64 duration_us;
@@ -960,30 +1005,24 @@ static inline int32_t intel_pstate_get_scaled_busy(struct cpudata *cpu)
core_busy = mul_fp(core_busy, sample_ratio);
}
- return core_busy;
+ cpu->sample.busy_scaled = core_busy;
+ return cpu->pstate.current_pstate - pid_calc(&cpu->pid, core_busy);
}
static inline void intel_pstate_adjust_busy_pstate(struct cpudata *cpu)
{
- int32_t busy_scaled;
- struct _pid *pid;
- signed int ctl;
- int from;
+ int from, target_pstate;
struct sample *sample;
from = cpu->pstate.current_pstate;
- pid = &cpu->pid;
- busy_scaled = intel_pstate_get_scaled_busy(cpu);
+ target_pstate = pstate_funcs.get_target_pstate(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, true);
+ intel_pstate_set_pstate(cpu, target_pstate, true);
sample = &cpu->sample;
trace_pstate_sample(fp_toint(sample->core_pct_busy),
- fp_toint(busy_scaled),
+ fp_toint(sample->busy_scaled),
from,
cpu->pstate.current_pstate,
sample->mperf,
@@ -1101,6 +1140,8 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy)
policy->max >= policy->cpuinfo.max_freq) {
pr_debug("intel_pstate: set performance\n");
limits = &performance_limits;
+ if (hwp_active)
+ intel_pstate_hwp_set();
return 0;
}
@@ -1108,7 +1149,8 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy)
limits = &powersave_limits;
limits->min_policy_pct = (policy->min * 100) / policy->cpuinfo.max_freq;
limits->min_policy_pct = clamp_t(int, limits->min_policy_pct, 0 , 100);
- limits->max_policy_pct = (policy->max * 100) / policy->cpuinfo.max_freq;
+ limits->max_policy_pct = DIV_ROUND_UP(policy->max * 100,
+ policy->cpuinfo.max_freq);
limits->max_policy_pct = clamp_t(int, limits->max_policy_pct, 0 , 100);
/* Normalize user input to [min_policy_pct, max_policy_pct] */
@@ -1120,6 +1162,7 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy)
limits->max_sysfs_pct);
limits->max_perf_pct = max(limits->min_policy_pct,
limits->max_perf_pct);
+ limits->max_perf = round_up(limits->max_perf, FRAC_BITS);
/* Make sure min_perf_pct <= max_perf_pct */
limits->min_perf_pct = min(limits->max_perf_pct, limits->min_perf_pct);
@@ -1233,6 +1276,8 @@ static void copy_cpu_funcs(struct pstate_funcs *funcs)
pstate_funcs.get_scaling = funcs->get_scaling;
pstate_funcs.set = funcs->set;
pstate_funcs.get_vid = funcs->get_vid;
+ pstate_funcs.get_target_pstate = funcs->get_target_pstate;
+
}
#if IS_ENABLED(CONFIG_ACPI)
diff --git a/drivers/cpufreq/mt8173-cpufreq.c b/drivers/cpufreq/mt8173-cpufreq.c
index 83001dc5b646..1efba340456d 100644
--- a/drivers/cpufreq/mt8173-cpufreq.c
+++ b/drivers/cpufreq/mt8173-cpufreq.c
@@ -41,16 +41,35 @@
* the original PLL becomes stable at target frequency.
*/
struct mtk_cpu_dvfs_info {
+ struct cpumask cpus;
struct device *cpu_dev;
struct regulator *proc_reg;
struct regulator *sram_reg;
struct clk *cpu_clk;
struct clk *inter_clk;
struct thermal_cooling_device *cdev;
+ struct list_head list_head;
int intermediate_voltage;
bool need_voltage_tracking;
};
+static LIST_HEAD(dvfs_info_list);
+
+static struct mtk_cpu_dvfs_info *mtk_cpu_dvfs_info_lookup(int cpu)
+{
+ struct mtk_cpu_dvfs_info *info;
+ struct list_head *list;
+
+ list_for_each(list, &dvfs_info_list) {
+ info = list_entry(list, struct mtk_cpu_dvfs_info, list_head);
+
+ if (cpumask_test_cpu(cpu, &info->cpus))
+ return info;
+ }
+
+ return NULL;
+}
+
static int mtk_cpufreq_voltage_tracking(struct mtk_cpu_dvfs_info *info,
int new_vproc)
{
@@ -59,7 +78,10 @@ static int mtk_cpufreq_voltage_tracking(struct mtk_cpu_dvfs_info *info,
int old_vproc, old_vsram, new_vsram, vsram, vproc, ret;
old_vproc = regulator_get_voltage(proc_reg);
- old_vsram = regulator_get_voltage(sram_reg);
+ if (old_vproc < 0) {
+ pr_err("%s: invalid Vproc value: %d\n", __func__, old_vproc);
+ return old_vproc;
+ }
/* Vsram should not exceed the maximum allowed voltage of SoC. */
new_vsram = min(new_vproc + MIN_VOLT_SHIFT, MAX_VOLT_LIMIT);
@@ -72,7 +94,17 @@ static int mtk_cpufreq_voltage_tracking(struct mtk_cpu_dvfs_info *info,
*/
do {
old_vsram = regulator_get_voltage(sram_reg);
+ if (old_vsram < 0) {
+ pr_err("%s: invalid Vsram value: %d\n",
+ __func__, old_vsram);
+ return old_vsram;
+ }
old_vproc = regulator_get_voltage(proc_reg);
+ if (old_vproc < 0) {
+ pr_err("%s: invalid Vproc value: %d\n",
+ __func__, old_vproc);
+ return old_vproc;
+ }
vsram = min(new_vsram, old_vproc + MAX_VOLT_SHIFT);
@@ -117,7 +149,17 @@ static int mtk_cpufreq_voltage_tracking(struct mtk_cpu_dvfs_info *info,
*/
do {
old_vproc = regulator_get_voltage(proc_reg);
+ if (old_vproc < 0) {
+ pr_err("%s: invalid Vproc value: %d\n",
+ __func__, old_vproc);
+ return old_vproc;
+ }
old_vsram = regulator_get_voltage(sram_reg);
+ if (old_vsram < 0) {
+ pr_err("%s: invalid Vsram value: %d\n",
+ __func__, old_vsram);
+ return old_vsram;
+ }
vproc = max(new_vproc, old_vsram - MAX_VOLT_SHIFT);
ret = regulator_set_voltage(proc_reg, vproc,
@@ -185,6 +227,10 @@ static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
old_freq_hz = clk_get_rate(cpu_clk);
old_vproc = regulator_get_voltage(info->proc_reg);
+ if (old_vproc < 0) {
+ pr_err("%s: invalid Vproc value: %d\n", __func__, old_vproc);
+ return old_vproc;
+ }
freq_hz = freq_table[index].frequency * 1000;
@@ -344,7 +390,15 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
/* Both presence and absence of sram regulator are valid cases. */
sram_reg = regulator_get_exclusive(cpu_dev, "sram");
- ret = dev_pm_opp_of_add_table(cpu_dev);
+ /* Get OPP-sharing information from "operating-points-v2" bindings */
+ ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, &info->cpus);
+ if (ret) {
+ pr_err("failed to get OPP-sharing information for cpu%d\n",
+ cpu);
+ goto out_free_resources;
+ }
+
+ ret = dev_pm_opp_of_cpumask_add_table(&info->cpus);
if (ret) {
pr_warn("no OPP table for cpu%d\n", cpu);
goto out_free_resources;
@@ -378,7 +432,7 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
return 0;
out_free_opp_table:
- dev_pm_opp_of_remove_table(cpu_dev);
+ dev_pm_opp_of_cpumask_remove_table(&info->cpus);
out_free_resources:
if (!IS_ERR(proc_reg))
@@ -404,7 +458,7 @@ static void mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info *info)
if (!IS_ERR(info->inter_clk))
clk_put(info->inter_clk);
- dev_pm_opp_of_remove_table(info->cpu_dev);
+ dev_pm_opp_of_cpumask_remove_table(&info->cpus);
}
static int mtk_cpufreq_init(struct cpufreq_policy *policy)
@@ -413,22 +467,18 @@ static int mtk_cpufreq_init(struct cpufreq_policy *policy)
struct cpufreq_frequency_table *freq_table;
int ret;
- info = kzalloc(sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- ret = mtk_cpu_dvfs_info_init(info, policy->cpu);
- if (ret) {
- pr_err("%s failed to initialize dvfs info for cpu%d\n",
- __func__, policy->cpu);
- goto out_free_dvfs_info;
+ info = mtk_cpu_dvfs_info_lookup(policy->cpu);
+ if (!info) {
+ pr_err("dvfs info for cpu%d is not initialized.\n",
+ policy->cpu);
+ return -EINVAL;
}
ret = dev_pm_opp_init_cpufreq_table(info->cpu_dev, &freq_table);
if (ret) {
pr_err("failed to init cpufreq table for cpu%d: %d\n",
policy->cpu, ret);
- goto out_release_dvfs_info;
+ return ret;
}
ret = cpufreq_table_validate_and_show(policy, freq_table);
@@ -437,8 +487,7 @@ static int mtk_cpufreq_init(struct cpufreq_policy *policy)
goto out_free_cpufreq_table;
}
- /* CPUs in the same cluster share a clock and power domain. */
- cpumask_copy(policy->cpus, &cpu_topology[policy->cpu].core_sibling);
+ cpumask_copy(policy->cpus, &info->cpus);
policy->driver_data = info;
policy->clk = info->cpu_clk;
@@ -446,13 +495,6 @@ static int mtk_cpufreq_init(struct cpufreq_policy *policy)
out_free_cpufreq_table:
dev_pm_opp_free_cpufreq_table(info->cpu_dev, &freq_table);
-
-out_release_dvfs_info:
- mtk_cpu_dvfs_info_release(info);
-
-out_free_dvfs_info:
- kfree(info);
-
return ret;
}
@@ -462,14 +504,13 @@ static int mtk_cpufreq_exit(struct cpufreq_policy *policy)
cpufreq_cooling_unregister(info->cdev);
dev_pm_opp_free_cpufreq_table(info->cpu_dev, &policy->freq_table);
- mtk_cpu_dvfs_info_release(info);
- kfree(info);
return 0;
}
static struct cpufreq_driver mt8173_cpufreq_driver = {
- .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
+ .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK |
+ CPUFREQ_HAVE_GOVERNOR_PER_POLICY,
.verify = cpufreq_generic_frequency_table_verify,
.target_index = mtk_cpufreq_set_target,
.get = cpufreq_generic_get,
@@ -482,11 +523,47 @@ static struct cpufreq_driver mt8173_cpufreq_driver = {
static int mt8173_cpufreq_probe(struct platform_device *pdev)
{
- int ret;
+ struct mtk_cpu_dvfs_info *info;
+ struct list_head *list, *tmp;
+ int cpu, ret;
+
+ for_each_possible_cpu(cpu) {
+ info = mtk_cpu_dvfs_info_lookup(cpu);
+ if (info)
+ continue;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ ret = -ENOMEM;
+ goto release_dvfs_info_list;
+ }
+
+ ret = mtk_cpu_dvfs_info_init(info, cpu);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to initialize dvfs info for cpu%d\n",
+ cpu);
+ goto release_dvfs_info_list;
+ }
+
+ list_add(&info->list_head, &dvfs_info_list);
+ }
ret = cpufreq_register_driver(&mt8173_cpufreq_driver);
- if (ret)
- pr_err("failed to register mtk cpufreq driver\n");
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register mtk cpufreq driver\n");
+ goto release_dvfs_info_list;
+ }
+
+ return 0;
+
+release_dvfs_info_list:
+ list_for_each_safe(list, tmp, &dvfs_info_list) {
+ info = list_entry(list, struct mtk_cpu_dvfs_info, list_head);
+
+ mtk_cpu_dvfs_info_release(info);
+ list_del(list);
+ }
return ret;
}
diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c
index 2a0d58959acf..808a320e9d5d 100644
--- a/drivers/cpufreq/pcc-cpufreq.c
+++ b/drivers/cpufreq/pcc-cpufreq.c
@@ -555,6 +555,8 @@ static int pcc_cpufreq_cpu_init(struct cpufreq_policy *policy)
policy->min = policy->cpuinfo.min_freq =
ioread32(&pcch_hdr->minimum_frequency) * 1000;
+ policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+
pr_debug("init: policy->max is %d, policy->min is %d\n",
policy->max, policy->min);
out:
diff --git a/drivers/cpufreq/qoriq-cpufreq.c b/drivers/cpufreq/qoriq-cpufreq.c
index 358f0752c31e..b23e525a7af3 100644
--- a/drivers/cpufreq/qoriq-cpufreq.c
+++ b/drivers/cpufreq/qoriq-cpufreq.c
@@ -12,6 +12,7 @@
#include <linux/clk.h>
#include <linux/cpufreq.h>
+#include <linux/cpu_cooling.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -33,6 +34,7 @@
struct cpu_data {
struct clk **pclk;
struct cpufreq_frequency_table *table;
+ struct thermal_cooling_device *cdev;
};
/**
@@ -321,6 +323,27 @@ static int qoriq_cpufreq_target(struct cpufreq_policy *policy,
return clk_set_parent(policy->clk, parent);
}
+
+static void qoriq_cpufreq_ready(struct cpufreq_policy *policy)
+{
+ struct cpu_data *cpud = policy->driver_data;
+ struct device_node *np = of_get_cpu_node(policy->cpu, NULL);
+
+ if (of_find_property(np, "#cooling-cells", NULL)) {
+ cpud->cdev = of_cpufreq_cooling_register(np,
+ policy->related_cpus);
+
+ if (IS_ERR(cpud->cdev)) {
+ pr_err("Failed to register cooling device cpu%d: %ld\n",
+ policy->cpu, PTR_ERR(cpud->cdev));
+
+ cpud->cdev = NULL;
+ }
+ }
+
+ of_node_put(np);
+}
+
static struct cpufreq_driver qoriq_cpufreq_driver = {
.name = "qoriq_cpufreq",
.flags = CPUFREQ_CONST_LOOPS,
@@ -329,6 +352,7 @@ static struct cpufreq_driver qoriq_cpufreq_driver = {
.verify = cpufreq_generic_frequency_table_verify,
.target_index = qoriq_cpufreq_target,
.get = cpufreq_generic_get,
+ .ready = qoriq_cpufreq_ready,
.attr = cpufreq_generic_attr,
};
diff --git a/drivers/cpufreq/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c
index 733aa5153e74..68ef8fd9482f 100644
--- a/drivers/cpufreq/s3c24xx-cpufreq.c
+++ b/drivers/cpufreq/s3c24xx-cpufreq.c
@@ -648,7 +648,7 @@ late_initcall(s3c_cpufreq_initcall);
*
* Register the given set of PLLs with the system.
*/
-int __init s3c_plltab_register(struct cpufreq_frequency_table *plls,
+int s3c_plltab_register(struct cpufreq_frequency_table *plls,
unsigned int plls_no)
{
struct cpufreq_frequency_table *vals;
diff --git a/drivers/cpufreq/scpi-cpufreq.c b/drivers/cpufreq/scpi-cpufreq.c
index 2c3b16fd3a01..de5e89b2eaaa 100644
--- a/drivers/cpufreq/scpi-cpufreq.c
+++ b/drivers/cpufreq/scpi-cpufreq.c
@@ -31,7 +31,7 @@ static struct scpi_ops *scpi_ops;
static struct scpi_dvfs_info *scpi_get_dvfs_info(struct device *cpu_dev)
{
- u8 domain = topology_physical_package_id(cpu_dev->id);
+ int domain = topology_physical_package_id(cpu_dev->id);
if (domain < 0)
return ERR_PTR(-EINVAL);
diff --git a/drivers/cpufreq/sti-cpufreq.c b/drivers/cpufreq/sti-cpufreq.c
new file mode 100644
index 000000000000..a9c659f58974
--- /dev/null
+++ b/drivers/cpufreq/sti-cpufreq.c
@@ -0,0 +1,294 @@
+/*
+ * Match running platform with pre-defined OPP values for CPUFreq
+ *
+ * Author: Ajit Pal Singh <ajitpal.singh@st.com>
+ * Lee Jones <lee.jones@linaro.org>
+ *
+ * Copyright (C) 2015 STMicroelectronics (R&D) Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License as
+ * published by the Free Software Foundation
+ */
+
+#include <linux/cpu.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/pm_opp.h>
+#include <linux/regmap.h>
+
+#define VERSION_ELEMENTS 3
+#define MAX_PCODE_NAME_LEN 7
+
+#define VERSION_SHIFT 28
+#define HW_INFO_INDEX 1
+#define MAJOR_ID_INDEX 1
+#define MINOR_ID_INDEX 2
+
+/*
+ * Only match on "suitable for ALL versions" entries
+ *
+ * This will be used with the BIT() macro. It sets the
+ * top bit of a 32bit value and is equal to 0x80000000.
+ */
+#define DEFAULT_VERSION 31
+
+enum {
+ PCODE = 0,
+ SUBSTRATE,
+ DVFS_MAX_REGFIELDS,
+};
+
+/**
+ * ST CPUFreq Driver Data
+ *
+ * @cpu_node CPU's OF node
+ * @syscfg_eng Engineering Syscon register map
+ * @regmap Syscon register map
+ */
+static struct sti_cpufreq_ddata {
+ struct device *cpu;
+ struct regmap *syscfg_eng;
+ struct regmap *syscfg;
+} ddata;
+
+static int sti_cpufreq_fetch_major(void) {
+ struct device_node *np = ddata.cpu->of_node;
+ struct device *dev = ddata.cpu;
+ unsigned int major_offset;
+ unsigned int socid;
+ int ret;
+
+ ret = of_property_read_u32_index(np, "st,syscfg",
+ MAJOR_ID_INDEX, &major_offset);
+ if (ret) {
+ dev_err(dev, "No major number offset provided in %s [%d]\n",
+ np->full_name, ret);
+ return ret;
+ }
+
+ ret = regmap_read(ddata.syscfg, major_offset, &socid);
+ if (ret) {
+ dev_err(dev, "Failed to read major number from syscon [%d]\n",
+ ret);
+ return ret;
+ }
+
+ return ((socid >> VERSION_SHIFT) & 0xf) + 1;
+}
+
+static int sti_cpufreq_fetch_minor(void)
+{
+ struct device *dev = ddata.cpu;
+ struct device_node *np = dev->of_node;
+ unsigned int minor_offset;
+ unsigned int minid;
+ int ret;
+
+ ret = of_property_read_u32_index(np, "st,syscfg-eng",
+ MINOR_ID_INDEX, &minor_offset);
+ if (ret) {
+ dev_err(dev,
+ "No minor number offset provided %s [%d]\n",
+ np->full_name, ret);
+ return ret;
+ }
+
+ ret = regmap_read(ddata.syscfg_eng, minor_offset, &minid);
+ if (ret) {
+ dev_err(dev,
+ "Failed to read the minor number from syscon [%d]\n",
+ ret);
+ return ret;
+ }
+
+ return minid & 0xf;
+}
+
+static int sti_cpufreq_fetch_regmap_field(const struct reg_field *reg_fields,
+ int hw_info_offset, int field)
+{
+ struct regmap_field *regmap_field;
+ struct reg_field reg_field = reg_fields[field];
+ struct device *dev = ddata.cpu;
+ unsigned int value;
+ int ret;
+
+ reg_field.reg = hw_info_offset;
+ regmap_field = devm_regmap_field_alloc(dev,
+ ddata.syscfg_eng,
+ reg_field);
+ if (IS_ERR(regmap_field)) {
+ dev_err(dev, "Failed to allocate reg field\n");
+ return PTR_ERR(regmap_field);
+ }
+
+ ret = regmap_field_read(regmap_field, &value);
+ if (ret) {
+ dev_err(dev, "Failed to read %s code\n",
+ field ? "SUBSTRATE" : "PCODE");
+ return ret;
+ }
+
+ return value;
+}
+
+static const struct reg_field sti_stih407_dvfs_regfields[DVFS_MAX_REGFIELDS] = {
+ [PCODE] = REG_FIELD(0, 16, 19),
+ [SUBSTRATE] = REG_FIELD(0, 0, 2),
+};
+
+static const struct reg_field *sti_cpufreq_match(void)
+{
+ if (of_machine_is_compatible("st,stih407") ||
+ of_machine_is_compatible("st,stih410"))
+ return sti_stih407_dvfs_regfields;
+
+ return NULL;
+}
+
+static int sti_cpufreq_set_opp_info(void)
+{
+ struct device *dev = ddata.cpu;
+ struct device_node *np = dev->of_node;
+ const struct reg_field *reg_fields;
+ unsigned int hw_info_offset;
+ unsigned int version[VERSION_ELEMENTS];
+ int pcode, substrate, major, minor;
+ int ret;
+ char name[MAX_PCODE_NAME_LEN];
+
+ reg_fields = sti_cpufreq_match();
+ if (!reg_fields) {
+ dev_err(dev, "This SoC doesn't support voltage scaling");
+ return -ENODEV;
+ }
+
+ ret = of_property_read_u32_index(np, "st,syscfg-eng",
+ HW_INFO_INDEX, &hw_info_offset);
+ if (ret) {
+ dev_warn(dev, "Failed to read HW info offset from DT\n");
+ substrate = DEFAULT_VERSION;
+ pcode = 0;
+ goto use_defaults;
+ }
+
+ pcode = sti_cpufreq_fetch_regmap_field(reg_fields,
+ hw_info_offset,
+ PCODE);
+ if (pcode < 0) {
+ dev_warn(dev, "Failed to obtain process code\n");
+ /* Use default pcode */
+ pcode = 0;
+ }
+
+ substrate = sti_cpufreq_fetch_regmap_field(reg_fields,
+ hw_info_offset,
+ SUBSTRATE);
+ if (substrate) {
+ dev_warn(dev, "Failed to obtain substrate code\n");
+ /* Use default substrate */
+ substrate = DEFAULT_VERSION;
+ }
+
+use_defaults:
+ major = sti_cpufreq_fetch_major();
+ if (major < 0) {
+ dev_err(dev, "Failed to obtain major version\n");
+ /* Use default major number */
+ major = DEFAULT_VERSION;
+ }
+
+ minor = sti_cpufreq_fetch_minor();
+ if (minor < 0) {
+ dev_err(dev, "Failed to obtain minor version\n");
+ /* Use default minor number */
+ minor = DEFAULT_VERSION;
+ }
+
+ snprintf(name, MAX_PCODE_NAME_LEN, "pcode%d", pcode);
+
+ ret = dev_pm_opp_set_prop_name(dev, name);
+ if (ret) {
+ dev_err(dev, "Failed to set prop name\n");
+ return ret;
+ }
+
+ version[0] = BIT(major);
+ version[1] = BIT(minor);
+ version[2] = BIT(substrate);
+
+ ret = dev_pm_opp_set_supported_hw(dev, version, VERSION_ELEMENTS);
+ if (ret) {
+ dev_err(dev, "Failed to set supported hardware\n");
+ return ret;
+ }
+
+ dev_dbg(dev, "pcode: %d major: %d minor: %d substrate: %d\n",
+ pcode, major, minor, substrate);
+ dev_dbg(dev, "version[0]: %x version[1]: %x version[2]: %x\n",
+ version[0], version[1], version[2]);
+
+ return 0;
+}
+
+static int sti_cpufreq_fetch_syscon_regsiters(void)
+{
+ struct device *dev = ddata.cpu;
+ struct device_node *np = dev->of_node;
+
+ ddata.syscfg = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+ if (IS_ERR(ddata.syscfg)) {
+ dev_err(dev, "\"st,syscfg\" not supplied\n");
+ return PTR_ERR(ddata.syscfg);
+ }
+
+ ddata.syscfg_eng = syscon_regmap_lookup_by_phandle(np, "st,syscfg-eng");
+ if (IS_ERR(ddata.syscfg_eng)) {
+ dev_err(dev, "\"st,syscfg-eng\" not supplied\n");
+ return PTR_ERR(ddata.syscfg_eng);
+ }
+
+ return 0;
+}
+
+static int sti_cpufreq_init(void)
+{
+ int ret;
+
+ ddata.cpu = get_cpu_device(0);
+ if (!ddata.cpu) {
+ dev_err(ddata.cpu, "Failed to get device for CPU0\n");
+ goto skip_voltage_scaling;
+ }
+
+ if (!of_get_property(ddata.cpu->of_node, "operating-points-v2", NULL)) {
+ dev_err(ddata.cpu, "OPP-v2 not supported\n");
+ goto skip_voltage_scaling;
+ }
+
+ ret = sti_cpufreq_fetch_syscon_regsiters();
+ if (ret)
+ goto skip_voltage_scaling;
+
+ ret = sti_cpufreq_set_opp_info();
+ if (!ret)
+ goto register_cpufreq_dt;
+
+skip_voltage_scaling:
+ dev_err(ddata.cpu, "Not doing voltage scaling\n");
+
+register_cpufreq_dt:
+ platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
+
+ return 0;
+}
+module_init(sti_cpufreq_init);
+
+MODULE_DESCRIPTION("STMicroelectronics CPUFreq/OPP driver");
+MODULE_AUTHOR("Ajitpal Singh <ajitpal.singh@st.com>");
+MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/cpuidle/cpuidle-clps711x.c b/drivers/cpuidle/cpuidle-clps711x.c
index 18a7f7380508..66a9f231ec41 100644
--- a/drivers/cpuidle/cpuidle-clps711x.c
+++ b/drivers/cpuidle/cpuidle-clps711x.c
@@ -12,7 +12,7 @@
#include <linux/cpuidle.h>
#include <linux/err.h>
#include <linux/io.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/platform_device.h>
#define CLPS711X_CPUIDLE_NAME "clps711x-cpuidle"
@@ -56,8 +56,4 @@ static struct platform_driver clps711x_cpuidle_driver = {
.name = CLPS711X_CPUIDLE_NAME,
},
};
-module_platform_driver_probe(clps711x_cpuidle_driver, clps711x_cpuidle_probe);
-
-MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
-MODULE_DESCRIPTION("CLPS711X CPU idle driver");
-MODULE_LICENSE("GPL");
+builtin_platform_driver_probe(clps711x_cpuidle_driver, clps711x_cpuidle_probe);
diff --git a/drivers/cpuidle/cpuidle-exynos.c b/drivers/cpuidle/cpuidle-exynos.c
index b5f0a9cc8185..00cd129b10a4 100644
--- a/drivers/cpuidle/cpuidle-exynos.c
+++ b/drivers/cpuidle/cpuidle-exynos.c
@@ -14,7 +14,7 @@
#include <linux/cpuidle.h>
#include <linux/cpu_pm.h>
#include <linux/export.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/platform_data/cpuidle-exynos.h>
@@ -142,5 +142,4 @@ static struct platform_driver exynos_cpuidle_driver = {
.name = "exynos_cpuidle",
},
};
-
-module_platform_driver(exynos_cpuidle_driver);
+builtin_platform_driver(exynos_cpuidle_driver);
diff --git a/drivers/cpuidle/cpuidle-ux500.c b/drivers/cpuidle/cpuidle-ux500.c
index 8bf895c0017d..7941a090bea6 100644
--- a/drivers/cpuidle/cpuidle-ux500.c
+++ b/drivers/cpuidle/cpuidle-ux500.c
@@ -9,7 +9,7 @@
* published by the Free Software Foundation.
*/
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/cpuidle.h>
#include <linux/spinlock.h>
#include <linux/atomic.h>
@@ -124,5 +124,4 @@ static struct platform_driver dbx500_cpuidle_plat_driver = {
},
.probe = dbx500_cpuidle_probe,
};
-
-module_platform_driver(dbx500_cpuidle_plat_driver);
+builtin_platform_driver(dbx500_cpuidle_plat_driver);
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index 22e4463d1787..7b0971d97cc3 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -330,7 +330,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
* We want to default to C1 (hlt), not to busy polling
* unless the timer is happening really really soon.
*/
- if (data->next_timer_us > 5 &&
+ if (interactivity_req > 20 &&
!drv->states[CPUIDLE_DRIVER_STATE_START].disabled &&
dev->states_usage[CPUIDLE_DRIVER_STATE_START].disable == 0)
data->last_state_idx = CPUIDLE_DRIVER_STATE_START;
@@ -404,8 +404,10 @@ static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
measured_us = cpuidle_get_last_residency(dev);
/* Deduct exit latency */
- if (measured_us > target->exit_latency)
+ if (measured_us > 2 * target->exit_latency)
measured_us -= target->exit_latency;
+ else
+ measured_us /= 2;
/* Make sure our coefficients do not exceed unity */
if (measured_us > data->next_timer_us)
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 2569e043317e..3dd69df9c970 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -194,6 +194,9 @@ config CRYPTO_DEV_NIAGARA2
select CRYPTO_DES
select CRYPTO_BLKCIPHER
select CRYPTO_HASH
+ select CRYPTO_MD5
+ select CRYPTO_SHA1
+ select CRYPTO_SHA256
depends on SPARC64
help
Each core of a Niagara2 processor contains a Stream
@@ -378,10 +381,10 @@ config CRYPTO_DEV_BFIN_CRC
config CRYPTO_DEV_ATMEL_AES
tristate "Support for Atmel AES hw accelerator"
- depends on ARCH_AT91
+ depends on AT_XDMAC || AT_HDMAC || COMPILE_TEST
select CRYPTO_AES
+ select CRYPTO_AEAD
select CRYPTO_BLKCIPHER
- select AT_HDMAC
help
Some Atmel processors have AES hw accelerator.
Select this if you want to use the Atmel module for
@@ -498,4 +501,15 @@ config CRYPTO_DEV_SUN4I_SS
To compile this driver as a module, choose M here: the module
will be called sun4i-ss.
+config CRYPTO_DEV_ROCKCHIP
+ tristate "Rockchip's Cryptographic Engine driver"
+ depends on OF && ARCH_ROCKCHIP
+ select CRYPTO_AES
+ select CRYPTO_DES
+ select CRYPTO_BLKCIPHER
+
+ help
+ This driver interfaces with the hardware crypto accelerator.
+ Supporting cbc/ecb chainmode, and aes/des/des3_ede cipher mode.
+
endif # CRYPTO_HW
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index c3ced6fbd1b8..713de9d11148 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_CRYPTO_DEV_QAT) += qat/
obj-$(CONFIG_CRYPTO_DEV_QCE) += qce/
obj-$(CONFIG_CRYPTO_DEV_VMX) += vmx/
obj-$(CONFIG_CRYPTO_DEV_SUN4I_SS) += sunxi-ss/
+obj-$(CONFIG_CRYPTO_DEV_ROCKCHIP) += rockchip/
diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c
index 58a630e55d5d..62134c8a2260 100644
--- a/drivers/crypto/amcc/crypto4xx_core.c
+++ b/drivers/crypto/amcc/crypto4xx_core.c
@@ -781,6 +781,10 @@ u32 crypto4xx_build_pd(struct crypto_async_request *req,
/* figure how many gd is needed */
num_gd = sg_nents_for_len(src, datalen);
+ if ((int)num_gd < 0) {
+ dev_err(dev->core_dev->device, "Invalid number of src SG.\n");
+ return -EINVAL;
+ }
if (num_gd == 1)
num_gd = 0;
diff --git a/drivers/crypto/atmel-aes-regs.h b/drivers/crypto/atmel-aes-regs.h
index 2786bb1a5aa0..6c2951bb70b1 100644
--- a/drivers/crypto/atmel-aes-regs.h
+++ b/drivers/crypto/atmel-aes-regs.h
@@ -9,6 +9,7 @@
#define AES_MR 0x04
#define AES_MR_CYPHER_DEC (0 << 0)
#define AES_MR_CYPHER_ENC (1 << 0)
+#define AES_MR_GTAGEN (1 << 1)
#define AES_MR_DUALBUFF (1 << 3)
#define AES_MR_PROCDLY_MASK (0xF << 4)
#define AES_MR_PROCDLY_OFFSET 4
@@ -26,6 +27,7 @@
#define AES_MR_OPMOD_OFB (0x2 << 12)
#define AES_MR_OPMOD_CFB (0x3 << 12)
#define AES_MR_OPMOD_CTR (0x4 << 12)
+#define AES_MR_OPMOD_GCM (0x5 << 12)
#define AES_MR_LOD (0x1 << 15)
#define AES_MR_CFBS_MASK (0x7 << 16)
#define AES_MR_CFBS_128b (0x0 << 16)
@@ -44,6 +46,7 @@
#define AES_ISR 0x1C
#define AES_INT_DATARDY (1 << 0)
#define AES_INT_URAD (1 << 8)
+#define AES_INT_TAGRDY (1 << 16)
#define AES_ISR_URAT_MASK (0xF << 12)
#define AES_ISR_URAT_IDR_WR_PROC (0x0 << 12)
#define AES_ISR_URAT_ODR_RD_PROC (0x1 << 12)
@@ -57,6 +60,13 @@
#define AES_ODATAR(x) (0x50 + ((x) * 0x04))
#define AES_IVR(x) (0x60 + ((x) * 0x04))
+#define AES_AADLENR 0x70
+#define AES_CLENR 0x74
+#define AES_GHASHR(x) (0x78 + ((x) * 0x04))
+#define AES_TAGR(x) (0x88 + ((x) * 0x04))
+#define AES_CTRR 0x98
+#define AES_GCMHR(x) (0x9c + ((x) * 0x04))
+
#define AES_HW_VERSION 0xFC
#endif /* __ATMEL_AES_REGS_H__ */
diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c
index fb16d812c8f5..5621612ee921 100644
--- a/drivers/crypto/atmel-aes.c
+++ b/drivers/crypto/atmel-aes.c
@@ -33,68 +33,118 @@
#include <linux/of_device.h>
#include <linux/delay.h>
#include <linux/crypto.h>
-#include <linux/cryptohash.h>
#include <crypto/scatterwalk.h>
#include <crypto/algapi.h>
#include <crypto/aes.h>
-#include <crypto/hash.h>
-#include <crypto/internal/hash.h>
+#include <crypto/internal/aead.h>
#include <linux/platform_data/crypto-atmel.h>
#include <dt-bindings/dma/at91.h>
#include "atmel-aes-regs.h"
+#define ATMEL_AES_PRIORITY 300
+
+#define ATMEL_AES_BUFFER_ORDER 2
+#define ATMEL_AES_BUFFER_SIZE (PAGE_SIZE << ATMEL_AES_BUFFER_ORDER)
+
#define CFB8_BLOCK_SIZE 1
#define CFB16_BLOCK_SIZE 2
#define CFB32_BLOCK_SIZE 4
#define CFB64_BLOCK_SIZE 8
+#define SIZE_IN_WORDS(x) ((x) >> 2)
+
/* AES flags */
-#define AES_FLAGS_MODE_MASK 0x03ff
-#define AES_FLAGS_ENCRYPT BIT(0)
-#define AES_FLAGS_CBC BIT(1)
-#define AES_FLAGS_CFB BIT(2)
-#define AES_FLAGS_CFB8 BIT(3)
-#define AES_FLAGS_CFB16 BIT(4)
-#define AES_FLAGS_CFB32 BIT(5)
-#define AES_FLAGS_CFB64 BIT(6)
-#define AES_FLAGS_CFB128 BIT(7)
-#define AES_FLAGS_OFB BIT(8)
-#define AES_FLAGS_CTR BIT(9)
-
-#define AES_FLAGS_INIT BIT(16)
-#define AES_FLAGS_DMA BIT(17)
-#define AES_FLAGS_BUSY BIT(18)
-#define AES_FLAGS_FAST BIT(19)
+/* Reserve bits [18:16] [14:12] [1:0] for mode (same as for AES_MR) */
+#define AES_FLAGS_ENCRYPT AES_MR_CYPHER_ENC
+#define AES_FLAGS_GTAGEN AES_MR_GTAGEN
+#define AES_FLAGS_OPMODE_MASK (AES_MR_OPMOD_MASK | AES_MR_CFBS_MASK)
+#define AES_FLAGS_ECB AES_MR_OPMOD_ECB
+#define AES_FLAGS_CBC AES_MR_OPMOD_CBC
+#define AES_FLAGS_OFB AES_MR_OPMOD_OFB
+#define AES_FLAGS_CFB128 (AES_MR_OPMOD_CFB | AES_MR_CFBS_128b)
+#define AES_FLAGS_CFB64 (AES_MR_OPMOD_CFB | AES_MR_CFBS_64b)
+#define AES_FLAGS_CFB32 (AES_MR_OPMOD_CFB | AES_MR_CFBS_32b)
+#define AES_FLAGS_CFB16 (AES_MR_OPMOD_CFB | AES_MR_CFBS_16b)
+#define AES_FLAGS_CFB8 (AES_MR_OPMOD_CFB | AES_MR_CFBS_8b)
+#define AES_FLAGS_CTR AES_MR_OPMOD_CTR
+#define AES_FLAGS_GCM AES_MR_OPMOD_GCM
+
+#define AES_FLAGS_MODE_MASK (AES_FLAGS_OPMODE_MASK | \
+ AES_FLAGS_ENCRYPT | \
+ AES_FLAGS_GTAGEN)
+
+#define AES_FLAGS_INIT BIT(2)
+#define AES_FLAGS_BUSY BIT(3)
+#define AES_FLAGS_DUMP_REG BIT(4)
+
+#define AES_FLAGS_PERSISTENT (AES_FLAGS_INIT | AES_FLAGS_BUSY)
#define ATMEL_AES_QUEUE_LENGTH 50
-#define ATMEL_AES_DMA_THRESHOLD 16
+#define ATMEL_AES_DMA_THRESHOLD 256
struct atmel_aes_caps {
- bool has_dualbuff;
- bool has_cfb64;
- u32 max_burst_size;
+ bool has_dualbuff;
+ bool has_cfb64;
+ bool has_ctr32;
+ bool has_gcm;
+ u32 max_burst_size;
};
struct atmel_aes_dev;
+
+typedef int (*atmel_aes_fn_t)(struct atmel_aes_dev *);
+
+
+struct atmel_aes_base_ctx {
+ struct atmel_aes_dev *dd;
+ atmel_aes_fn_t start;
+ int keylen;
+ u32 key[AES_KEYSIZE_256 / sizeof(u32)];
+ u16 block_size;
+};
+
struct atmel_aes_ctx {
- struct atmel_aes_dev *dd;
+ struct atmel_aes_base_ctx base;
+};
+
+struct atmel_aes_ctr_ctx {
+ struct atmel_aes_base_ctx base;
+
+ u32 iv[AES_BLOCK_SIZE / sizeof(u32)];
+ size_t offset;
+ struct scatterlist src[2];
+ struct scatterlist dst[2];
+};
- int keylen;
- u32 key[AES_KEYSIZE_256 / sizeof(u32)];
+struct atmel_aes_gcm_ctx {
+ struct atmel_aes_base_ctx base;
- u16 block_size;
+ struct scatterlist src[2];
+ struct scatterlist dst[2];
+
+ u32 j0[AES_BLOCK_SIZE / sizeof(u32)];
+ u32 tag[AES_BLOCK_SIZE / sizeof(u32)];
+ u32 ghash[AES_BLOCK_SIZE / sizeof(u32)];
+ size_t textlen;
+
+ const u32 *ghash_in;
+ u32 *ghash_out;
+ atmel_aes_fn_t ghash_resume;
};
struct atmel_aes_reqctx {
- unsigned long mode;
+ unsigned long mode;
};
struct atmel_aes_dma {
- struct dma_chan *chan;
- struct dma_slave_config dma_conf;
+ struct dma_chan *chan;
+ struct scatterlist *sg;
+ int nents;
+ unsigned int remainder;
+ unsigned int sg_len;
};
struct atmel_aes_dev {
@@ -102,13 +152,18 @@ struct atmel_aes_dev {
unsigned long phys_base;
void __iomem *io_base;
- struct atmel_aes_ctx *ctx;
+ struct crypto_async_request *areq;
+ struct atmel_aes_base_ctx *ctx;
+
+ bool is_async;
+ atmel_aes_fn_t resume;
+ atmel_aes_fn_t cpu_transfer_complete;
+
struct device *dev;
struct clk *iclk;
- int irq;
+ int irq;
unsigned long flags;
- int err;
spinlock_t lock;
struct crypto_queue queue;
@@ -116,33 +171,21 @@ struct atmel_aes_dev {
struct tasklet_struct done_task;
struct tasklet_struct queue_task;
- struct ablkcipher_request *req;
- size_t total;
+ size_t total;
+ size_t datalen;
+ u32 *data;
- struct scatterlist *in_sg;
- unsigned int nb_in_sg;
- size_t in_offset;
- struct scatterlist *out_sg;
- unsigned int nb_out_sg;
- size_t out_offset;
+ struct atmel_aes_dma src;
+ struct atmel_aes_dma dst;
- size_t bufcnt;
- size_t buflen;
- size_t dma_size;
-
- void *buf_in;
- int dma_in;
- dma_addr_t dma_addr_in;
- struct atmel_aes_dma dma_lch_in;
-
- void *buf_out;
- int dma_out;
- dma_addr_t dma_addr_out;
- struct atmel_aes_dma dma_lch_out;
+ size_t buflen;
+ void *buf;
+ struct scatterlist aligned_sg;
+ struct scatterlist *real_dst;
struct atmel_aes_caps caps;
- u32 hw_version;
+ u32 hw_version;
};
struct atmel_aes_drv {
@@ -155,71 +198,128 @@ static struct atmel_aes_drv atmel_aes = {
.lock = __SPIN_LOCK_UNLOCKED(atmel_aes.lock),
};
-static int atmel_aes_sg_length(struct ablkcipher_request *req,
- struct scatterlist *sg)
+#ifdef VERBOSE_DEBUG
+static const char *atmel_aes_reg_name(u32 offset, char *tmp, size_t sz)
{
- unsigned int total = req->nbytes;
- int sg_nb;
- unsigned int len;
- struct scatterlist *sg_list;
-
- sg_nb = 0;
- sg_list = sg;
- total = req->nbytes;
+ switch (offset) {
+ case AES_CR:
+ return "CR";
+
+ case AES_MR:
+ return "MR";
+
+ case AES_ISR:
+ return "ISR";
+
+ case AES_IMR:
+ return "IMR";
+
+ case AES_IER:
+ return "IER";
+
+ case AES_IDR:
+ return "IDR";
+
+ case AES_KEYWR(0):
+ case AES_KEYWR(1):
+ case AES_KEYWR(2):
+ case AES_KEYWR(3):
+ case AES_KEYWR(4):
+ case AES_KEYWR(5):
+ case AES_KEYWR(6):
+ case AES_KEYWR(7):
+ snprintf(tmp, sz, "KEYWR[%u]", (offset - AES_KEYWR(0)) >> 2);
+ break;
- while (total) {
- len = min(sg_list->length, total);
+ case AES_IDATAR(0):
+ case AES_IDATAR(1):
+ case AES_IDATAR(2):
+ case AES_IDATAR(3):
+ snprintf(tmp, sz, "IDATAR[%u]", (offset - AES_IDATAR(0)) >> 2);
+ break;
- sg_nb++;
- total -= len;
+ case AES_ODATAR(0):
+ case AES_ODATAR(1):
+ case AES_ODATAR(2):
+ case AES_ODATAR(3):
+ snprintf(tmp, sz, "ODATAR[%u]", (offset - AES_ODATAR(0)) >> 2);
+ break;
- sg_list = sg_next(sg_list);
- if (!sg_list)
- total = 0;
- }
+ case AES_IVR(0):
+ case AES_IVR(1):
+ case AES_IVR(2):
+ case AES_IVR(3):
+ snprintf(tmp, sz, "IVR[%u]", (offset - AES_IVR(0)) >> 2);
+ break;
- return sg_nb;
-}
+ case AES_AADLENR:
+ return "AADLENR";
-static int atmel_aes_sg_copy(struct scatterlist **sg, size_t *offset,
- void *buf, size_t buflen, size_t total, int out)
-{
- unsigned int count, off = 0;
+ case AES_CLENR:
+ return "CLENR";
- while (buflen && total) {
- count = min((*sg)->length - *offset, total);
- count = min(count, buflen);
+ case AES_GHASHR(0):
+ case AES_GHASHR(1):
+ case AES_GHASHR(2):
+ case AES_GHASHR(3):
+ snprintf(tmp, sz, "GHASHR[%u]", (offset - AES_GHASHR(0)) >> 2);
+ break;
- if (!count)
- return off;
+ case AES_TAGR(0):
+ case AES_TAGR(1):
+ case AES_TAGR(2):
+ case AES_TAGR(3):
+ snprintf(tmp, sz, "TAGR[%u]", (offset - AES_TAGR(0)) >> 2);
+ break;
- scatterwalk_map_and_copy(buf + off, *sg, *offset, count, out);
+ case AES_CTRR:
+ return "CTRR";
- off += count;
- buflen -= count;
- *offset += count;
- total -= count;
+ case AES_GCMHR(0):
+ case AES_GCMHR(1):
+ case AES_GCMHR(2):
+ case AES_GCMHR(3):
+ snprintf(tmp, sz, "GCMHR[%u]", (offset - AES_GCMHR(0)) >> 2);
- if (*offset == (*sg)->length) {
- *sg = sg_next(*sg);
- if (*sg)
- *offset = 0;
- else
- total = 0;
- }
+ default:
+ snprintf(tmp, sz, "0x%02x", offset);
+ break;
}
- return off;
+ return tmp;
}
+#endif /* VERBOSE_DEBUG */
+
+/* Shared functions */
static inline u32 atmel_aes_read(struct atmel_aes_dev *dd, u32 offset)
{
- return readl_relaxed(dd->io_base + offset);
+ u32 value = readl_relaxed(dd->io_base + offset);
+
+#ifdef VERBOSE_DEBUG
+ if (dd->flags & AES_FLAGS_DUMP_REG) {
+ char tmp[16];
+
+ dev_vdbg(dd->dev, "read 0x%08x from %s\n", value,
+ atmel_aes_reg_name(offset, tmp, sizeof(tmp)));
+ }
+#endif /* VERBOSE_DEBUG */
+
+ return value;
}
static inline void atmel_aes_write(struct atmel_aes_dev *dd,
u32 offset, u32 value)
{
+#ifdef VERBOSE_DEBUG
+ if (dd->flags & AES_FLAGS_DUMP_REG) {
+ char tmp[16];
+
+ dev_vdbg(dd->dev, "write 0x%08x into %s\n", value,
+ atmel_aes_reg_name(offset, tmp));
+ }
+#endif /* VERBOSE_DEBUG */
+
writel_relaxed(value, dd->io_base + offset);
}
@@ -231,13 +331,50 @@ static void atmel_aes_read_n(struct atmel_aes_dev *dd, u32 offset,
}
static void atmel_aes_write_n(struct atmel_aes_dev *dd, u32 offset,
- u32 *value, int count)
+ const u32 *value, int count)
{
for (; count--; value++, offset += 4)
atmel_aes_write(dd, offset, *value);
}
-static struct atmel_aes_dev *atmel_aes_find_dev(struct atmel_aes_ctx *ctx)
+static inline void atmel_aes_read_block(struct atmel_aes_dev *dd, u32 offset,
+ u32 *value)
+{
+ atmel_aes_read_n(dd, offset, value, SIZE_IN_WORDS(AES_BLOCK_SIZE));
+}
+
+static inline void atmel_aes_write_block(struct atmel_aes_dev *dd, u32 offset,
+ const u32 *value)
+{
+ atmel_aes_write_n(dd, offset, value, SIZE_IN_WORDS(AES_BLOCK_SIZE));
+}
+
+static inline int atmel_aes_wait_for_data_ready(struct atmel_aes_dev *dd,
+ atmel_aes_fn_t resume)
+{
+ u32 isr = atmel_aes_read(dd, AES_ISR);
+
+ if (unlikely(isr & AES_INT_DATARDY))
+ return resume(dd);
+
+ dd->resume = resume;
+ atmel_aes_write(dd, AES_IER, AES_INT_DATARDY);
+ return -EINPROGRESS;
+}
+
+static inline size_t atmel_aes_padlen(size_t len, size_t block_size)
+{
+ len &= block_size - 1;
+ return len ? block_size - len : 0;
+}
+
+static inline struct aead_request *
+aead_request_cast(struct crypto_async_request *req)
+{
+ return container_of(req, struct aead_request, base);
+}
+
+static struct atmel_aes_dev *atmel_aes_find_dev(struct atmel_aes_base_ctx *ctx)
{
struct atmel_aes_dev *aes_dd = NULL;
struct atmel_aes_dev *tmp;
@@ -270,7 +407,6 @@ static int atmel_aes_hw_init(struct atmel_aes_dev *dd)
atmel_aes_write(dd, AES_CR, AES_CR_SWRST);
atmel_aes_write(dd, AES_MR, 0xE << AES_MR_CKEY_OFFSET);
dd->flags |= AES_FLAGS_INIT;
- dd->err = 0;
}
return 0;
@@ -281,552 +417,643 @@ static inline unsigned int atmel_aes_get_version(struct atmel_aes_dev *dd)
return atmel_aes_read(dd, AES_HW_VERSION) & 0x00000fff;
}
-static void atmel_aes_hw_version_init(struct atmel_aes_dev *dd)
+static int atmel_aes_hw_version_init(struct atmel_aes_dev *dd)
{
- atmel_aes_hw_init(dd);
+ int err;
+
+ err = atmel_aes_hw_init(dd);
+ if (err)
+ return err;
dd->hw_version = atmel_aes_get_version(dd);
- dev_info(dd->dev,
- "version: 0x%x\n", dd->hw_version);
+ dev_info(dd->dev, "version: 0x%x\n", dd->hw_version);
clk_disable_unprepare(dd->iclk);
+ return 0;
}
-static void atmel_aes_finish_req(struct atmel_aes_dev *dd, int err)
+static inline void atmel_aes_set_mode(struct atmel_aes_dev *dd,
+ const struct atmel_aes_reqctx *rctx)
{
- struct ablkcipher_request *req = dd->req;
+ /* Clear all but persistent flags and set request flags. */
+ dd->flags = (dd->flags & AES_FLAGS_PERSISTENT) | rctx->mode;
+}
+static inline bool atmel_aes_is_encrypt(const struct atmel_aes_dev *dd)
+{
+ return (dd->flags & AES_FLAGS_ENCRYPT);
+}
+
+static inline int atmel_aes_complete(struct atmel_aes_dev *dd, int err)
+{
clk_disable_unprepare(dd->iclk);
dd->flags &= ~AES_FLAGS_BUSY;
- req->base.complete(&req->base, err);
-}
+ if (dd->is_async)
+ dd->areq->complete(dd->areq, err);
-static void atmel_aes_dma_callback(void *data)
-{
- struct atmel_aes_dev *dd = data;
+ tasklet_schedule(&dd->queue_task);
- /* dma_lch_out - completed */
- tasklet_schedule(&dd->done_task);
+ return err;
}
-static int atmel_aes_crypt_dma(struct atmel_aes_dev *dd,
- dma_addr_t dma_addr_in, dma_addr_t dma_addr_out, int length)
+static void atmel_aes_write_ctrl(struct atmel_aes_dev *dd, bool use_dma,
+ const u32 *iv)
{
- struct scatterlist sg[2];
- struct dma_async_tx_descriptor *in_desc, *out_desc;
+ u32 valmr = 0;
- dd->dma_size = length;
+ /* MR register must be set before IV registers */
+ if (dd->ctx->keylen == AES_KEYSIZE_128)
+ valmr |= AES_MR_KEYSIZE_128;
+ else if (dd->ctx->keylen == AES_KEYSIZE_192)
+ valmr |= AES_MR_KEYSIZE_192;
+ else
+ valmr |= AES_MR_KEYSIZE_256;
- dma_sync_single_for_device(dd->dev, dma_addr_in, length,
- DMA_TO_DEVICE);
- dma_sync_single_for_device(dd->dev, dma_addr_out, length,
- DMA_FROM_DEVICE);
+ valmr |= dd->flags & AES_FLAGS_MODE_MASK;
- if (dd->flags & AES_FLAGS_CFB8) {
- dd->dma_lch_in.dma_conf.dst_addr_width =
- DMA_SLAVE_BUSWIDTH_1_BYTE;
- dd->dma_lch_out.dma_conf.src_addr_width =
- DMA_SLAVE_BUSWIDTH_1_BYTE;
- } else if (dd->flags & AES_FLAGS_CFB16) {
- dd->dma_lch_in.dma_conf.dst_addr_width =
- DMA_SLAVE_BUSWIDTH_2_BYTES;
- dd->dma_lch_out.dma_conf.src_addr_width =
- DMA_SLAVE_BUSWIDTH_2_BYTES;
+ if (use_dma) {
+ valmr |= AES_MR_SMOD_IDATAR0;
+ if (dd->caps.has_dualbuff)
+ valmr |= AES_MR_DUALBUFF;
} else {
- dd->dma_lch_in.dma_conf.dst_addr_width =
- DMA_SLAVE_BUSWIDTH_4_BYTES;
- dd->dma_lch_out.dma_conf.src_addr_width =
- DMA_SLAVE_BUSWIDTH_4_BYTES;
+ valmr |= AES_MR_SMOD_AUTO;
}
- if (dd->flags & (AES_FLAGS_CFB8 | AES_FLAGS_CFB16 |
- AES_FLAGS_CFB32 | AES_FLAGS_CFB64)) {
- dd->dma_lch_in.dma_conf.src_maxburst = 1;
- dd->dma_lch_in.dma_conf.dst_maxburst = 1;
- dd->dma_lch_out.dma_conf.src_maxburst = 1;
- dd->dma_lch_out.dma_conf.dst_maxburst = 1;
- } else {
- dd->dma_lch_in.dma_conf.src_maxburst = dd->caps.max_burst_size;
- dd->dma_lch_in.dma_conf.dst_maxburst = dd->caps.max_burst_size;
- dd->dma_lch_out.dma_conf.src_maxburst = dd->caps.max_burst_size;
- dd->dma_lch_out.dma_conf.dst_maxburst = dd->caps.max_burst_size;
- }
+ atmel_aes_write(dd, AES_MR, valmr);
- dmaengine_slave_config(dd->dma_lch_in.chan, &dd->dma_lch_in.dma_conf);
- dmaengine_slave_config(dd->dma_lch_out.chan, &dd->dma_lch_out.dma_conf);
+ atmel_aes_write_n(dd, AES_KEYWR(0), dd->ctx->key,
+ SIZE_IN_WORDS(dd->ctx->keylen));
- dd->flags |= AES_FLAGS_DMA;
+ if (iv && (valmr & AES_MR_OPMOD_MASK) != AES_MR_OPMOD_ECB)
+ atmel_aes_write_block(dd, AES_IVR(0), iv);
+}
- sg_init_table(&sg[0], 1);
- sg_dma_address(&sg[0]) = dma_addr_in;
- sg_dma_len(&sg[0]) = length;
- sg_init_table(&sg[1], 1);
- sg_dma_address(&sg[1]) = dma_addr_out;
- sg_dma_len(&sg[1]) = length;
+/* CPU transfer */
- in_desc = dmaengine_prep_slave_sg(dd->dma_lch_in.chan, &sg[0],
- 1, DMA_MEM_TO_DEV,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!in_desc)
- return -EINVAL;
+static int atmel_aes_cpu_transfer(struct atmel_aes_dev *dd)
+{
+ int err = 0;
+ u32 isr;
- out_desc = dmaengine_prep_slave_sg(dd->dma_lch_out.chan, &sg[1],
- 1, DMA_DEV_TO_MEM,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!out_desc)
- return -EINVAL;
+ for (;;) {
+ atmel_aes_read_block(dd, AES_ODATAR(0), dd->data);
+ dd->data += 4;
+ dd->datalen -= AES_BLOCK_SIZE;
- out_desc->callback = atmel_aes_dma_callback;
- out_desc->callback_param = dd;
+ if (dd->datalen < AES_BLOCK_SIZE)
+ break;
- dmaengine_submit(out_desc);
- dma_async_issue_pending(dd->dma_lch_out.chan);
+ atmel_aes_write_block(dd, AES_IDATAR(0), dd->data);
- dmaengine_submit(in_desc);
- dma_async_issue_pending(dd->dma_lch_in.chan);
+ isr = atmel_aes_read(dd, AES_ISR);
+ if (!(isr & AES_INT_DATARDY)) {
+ dd->resume = atmel_aes_cpu_transfer;
+ atmel_aes_write(dd, AES_IER, AES_INT_DATARDY);
+ return -EINPROGRESS;
+ }
+ }
- return 0;
+ if (!sg_copy_from_buffer(dd->real_dst, sg_nents(dd->real_dst),
+ dd->buf, dd->total))
+ err = -EINVAL;
+
+ if (err)
+ return atmel_aes_complete(dd, err);
+
+ return dd->cpu_transfer_complete(dd);
}
-static int atmel_aes_crypt_cpu_start(struct atmel_aes_dev *dd)
+static int atmel_aes_cpu_start(struct atmel_aes_dev *dd,
+ struct scatterlist *src,
+ struct scatterlist *dst,
+ size_t len,
+ atmel_aes_fn_t resume)
{
- dd->flags &= ~AES_FLAGS_DMA;
+ size_t padlen = atmel_aes_padlen(len, AES_BLOCK_SIZE);
- dma_sync_single_for_cpu(dd->dev, dd->dma_addr_in,
- dd->dma_size, DMA_TO_DEVICE);
- dma_sync_single_for_cpu(dd->dev, dd->dma_addr_out,
- dd->dma_size, DMA_FROM_DEVICE);
-
- /* use cache buffers */
- dd->nb_in_sg = atmel_aes_sg_length(dd->req, dd->in_sg);
- if (!dd->nb_in_sg)
+ if (unlikely(len == 0))
return -EINVAL;
- dd->nb_out_sg = atmel_aes_sg_length(dd->req, dd->out_sg);
- if (!dd->nb_out_sg)
- return -EINVAL;
+ sg_copy_to_buffer(src, sg_nents(src), dd->buf, len);
- dd->bufcnt = sg_copy_to_buffer(dd->in_sg, dd->nb_in_sg,
- dd->buf_in, dd->total);
+ dd->total = len;
+ dd->real_dst = dst;
+ dd->cpu_transfer_complete = resume;
+ dd->datalen = len + padlen;
+ dd->data = (u32 *)dd->buf;
+ atmel_aes_write_block(dd, AES_IDATAR(0), dd->data);
+ return atmel_aes_wait_for_data_ready(dd, atmel_aes_cpu_transfer);
+}
- if (!dd->bufcnt)
- return -EINVAL;
- dd->total -= dd->bufcnt;
+/* DMA transfer */
- atmel_aes_write(dd, AES_IER, AES_INT_DATARDY);
- atmel_aes_write_n(dd, AES_IDATAR(0), (u32 *) dd->buf_in,
- dd->bufcnt >> 2);
+static void atmel_aes_dma_callback(void *data);
- return 0;
+static bool atmel_aes_check_aligned(struct atmel_aes_dev *dd,
+ struct scatterlist *sg,
+ size_t len,
+ struct atmel_aes_dma *dma)
+{
+ int nents;
+
+ if (!IS_ALIGNED(len, dd->ctx->block_size))
+ return false;
+
+ for (nents = 0; sg; sg = sg_next(sg), ++nents) {
+ if (!IS_ALIGNED(sg->offset, sizeof(u32)))
+ return false;
+
+ if (len <= sg->length) {
+ if (!IS_ALIGNED(len, dd->ctx->block_size))
+ return false;
+
+ dma->nents = nents+1;
+ dma->remainder = sg->length - len;
+ sg->length = len;
+ return true;
+ }
+
+ if (!IS_ALIGNED(sg->length, dd->ctx->block_size))
+ return false;
+
+ len -= sg->length;
+ }
+
+ return false;
}
-static int atmel_aes_crypt_dma_start(struct atmel_aes_dev *dd)
+static inline void atmel_aes_restore_sg(const struct atmel_aes_dma *dma)
{
- int err, fast = 0, in, out;
- size_t count;
- dma_addr_t addr_in, addr_out;
+ struct scatterlist *sg = dma->sg;
+ int nents = dma->nents;
- if ((!dd->in_offset) && (!dd->out_offset)) {
- /* check for alignment */
- in = IS_ALIGNED((u32)dd->in_sg->offset, sizeof(u32)) &&
- IS_ALIGNED(dd->in_sg->length, dd->ctx->block_size);
- out = IS_ALIGNED((u32)dd->out_sg->offset, sizeof(u32)) &&
- IS_ALIGNED(dd->out_sg->length, dd->ctx->block_size);
- fast = in && out;
+ if (!dma->remainder)
+ return;
- if (sg_dma_len(dd->in_sg) != sg_dma_len(dd->out_sg))
- fast = 0;
- }
+ while (--nents > 0 && sg)
+ sg = sg_next(sg);
+ if (!sg)
+ return;
- if (fast) {
- count = min(dd->total, sg_dma_len(dd->in_sg));
- count = min(count, sg_dma_len(dd->out_sg));
+ sg->length += dma->remainder;
+}
- err = dma_map_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE);
- if (!err) {
- dev_err(dd->dev, "dma_map_sg() error\n");
- return -EINVAL;
- }
+static int atmel_aes_map(struct atmel_aes_dev *dd,
+ struct scatterlist *src,
+ struct scatterlist *dst,
+ size_t len)
+{
+ bool src_aligned, dst_aligned;
+ size_t padlen;
+
+ dd->total = len;
+ dd->src.sg = src;
+ dd->dst.sg = dst;
+ dd->real_dst = dst;
- err = dma_map_sg(dd->dev, dd->out_sg, 1,
- DMA_FROM_DEVICE);
- if (!err) {
- dev_err(dd->dev, "dma_map_sg() error\n");
- dma_unmap_sg(dd->dev, dd->in_sg, 1,
- DMA_TO_DEVICE);
- return -EINVAL;
+ src_aligned = atmel_aes_check_aligned(dd, src, len, &dd->src);
+ if (src == dst)
+ dst_aligned = src_aligned;
+ else
+ dst_aligned = atmel_aes_check_aligned(dd, dst, len, &dd->dst);
+ if (!src_aligned || !dst_aligned) {
+ padlen = atmel_aes_padlen(len, dd->ctx->block_size);
+
+ if (dd->buflen < len + padlen)
+ return -ENOMEM;
+
+ if (!src_aligned) {
+ sg_copy_to_buffer(src, sg_nents(src), dd->buf, len);
+ dd->src.sg = &dd->aligned_sg;
+ dd->src.nents = 1;
+ dd->src.remainder = 0;
}
- addr_in = sg_dma_address(dd->in_sg);
- addr_out = sg_dma_address(dd->out_sg);
+ if (!dst_aligned) {
+ dd->dst.sg = &dd->aligned_sg;
+ dd->dst.nents = 1;
+ dd->dst.remainder = 0;
+ }
- dd->flags |= AES_FLAGS_FAST;
+ sg_init_table(&dd->aligned_sg, 1);
+ sg_set_buf(&dd->aligned_sg, dd->buf, len + padlen);
+ }
+ if (dd->src.sg == dd->dst.sg) {
+ dd->src.sg_len = dma_map_sg(dd->dev, dd->src.sg, dd->src.nents,
+ DMA_BIDIRECTIONAL);
+ dd->dst.sg_len = dd->src.sg_len;
+ if (!dd->src.sg_len)
+ return -EFAULT;
} else {
- dma_sync_single_for_cpu(dd->dev, dd->dma_addr_in,
- dd->dma_size, DMA_TO_DEVICE);
+ dd->src.sg_len = dma_map_sg(dd->dev, dd->src.sg, dd->src.nents,
+ DMA_TO_DEVICE);
+ if (!dd->src.sg_len)
+ return -EFAULT;
+
+ dd->dst.sg_len = dma_map_sg(dd->dev, dd->dst.sg, dd->dst.nents,
+ DMA_FROM_DEVICE);
+ if (!dd->dst.sg_len) {
+ dma_unmap_sg(dd->dev, dd->src.sg, dd->src.nents,
+ DMA_TO_DEVICE);
+ return -EFAULT;
+ }
+ }
- /* use cache buffers */
- count = atmel_aes_sg_copy(&dd->in_sg, &dd->in_offset,
- dd->buf_in, dd->buflen, dd->total, 0);
+ return 0;
+}
- addr_in = dd->dma_addr_in;
- addr_out = dd->dma_addr_out;
+static void atmel_aes_unmap(struct atmel_aes_dev *dd)
+{
+ if (dd->src.sg == dd->dst.sg) {
+ dma_unmap_sg(dd->dev, dd->src.sg, dd->src.nents,
+ DMA_BIDIRECTIONAL);
- dd->flags &= ~AES_FLAGS_FAST;
- }
+ if (dd->src.sg != &dd->aligned_sg)
+ atmel_aes_restore_sg(&dd->src);
+ } else {
+ dma_unmap_sg(dd->dev, dd->dst.sg, dd->dst.nents,
+ DMA_FROM_DEVICE);
- dd->total -= count;
+ if (dd->dst.sg != &dd->aligned_sg)
+ atmel_aes_restore_sg(&dd->dst);
- err = atmel_aes_crypt_dma(dd, addr_in, addr_out, count);
+ dma_unmap_sg(dd->dev, dd->src.sg, dd->src.nents,
+ DMA_TO_DEVICE);
- if (err && (dd->flags & AES_FLAGS_FAST)) {
- dma_unmap_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE);
- dma_unmap_sg(dd->dev, dd->out_sg, 1, DMA_TO_DEVICE);
+ if (dd->src.sg != &dd->aligned_sg)
+ atmel_aes_restore_sg(&dd->src);
}
- return err;
+ if (dd->dst.sg == &dd->aligned_sg)
+ sg_copy_from_buffer(dd->real_dst, sg_nents(dd->real_dst),
+ dd->buf, dd->total);
}
-static int atmel_aes_write_ctrl(struct atmel_aes_dev *dd)
+static int atmel_aes_dma_transfer_start(struct atmel_aes_dev *dd,
+ enum dma_slave_buswidth addr_width,
+ enum dma_transfer_direction dir,
+ u32 maxburst)
{
+ struct dma_async_tx_descriptor *desc;
+ struct dma_slave_config config;
+ dma_async_tx_callback callback;
+ struct atmel_aes_dma *dma;
int err;
- u32 valcr = 0, valmr = 0;
- err = atmel_aes_hw_init(dd);
+ memset(&config, 0, sizeof(config));
+ config.direction = dir;
+ config.src_addr_width = addr_width;
+ config.dst_addr_width = addr_width;
+ config.src_maxburst = maxburst;
+ config.dst_maxburst = maxburst;
+
+ switch (dir) {
+ case DMA_MEM_TO_DEV:
+ dma = &dd->src;
+ callback = NULL;
+ config.dst_addr = dd->phys_base + AES_IDATAR(0);
+ break;
+
+ case DMA_DEV_TO_MEM:
+ dma = &dd->dst;
+ callback = atmel_aes_dma_callback;
+ config.src_addr = dd->phys_base + AES_ODATAR(0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ err = dmaengine_slave_config(dma->chan, &config);
if (err)
return err;
- /* MR register must be set before IV registers */
- if (dd->ctx->keylen == AES_KEYSIZE_128)
- valmr |= AES_MR_KEYSIZE_128;
- else if (dd->ctx->keylen == AES_KEYSIZE_192)
- valmr |= AES_MR_KEYSIZE_192;
- else
- valmr |= AES_MR_KEYSIZE_256;
+ desc = dmaengine_prep_slave_sg(dma->chan, dma->sg, dma->sg_len, dir,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc)
+ return -ENOMEM;
- if (dd->flags & AES_FLAGS_CBC) {
- valmr |= AES_MR_OPMOD_CBC;
- } else if (dd->flags & AES_FLAGS_CFB) {
- valmr |= AES_MR_OPMOD_CFB;
- if (dd->flags & AES_FLAGS_CFB8)
- valmr |= AES_MR_CFBS_8b;
- else if (dd->flags & AES_FLAGS_CFB16)
- valmr |= AES_MR_CFBS_16b;
- else if (dd->flags & AES_FLAGS_CFB32)
- valmr |= AES_MR_CFBS_32b;
- else if (dd->flags & AES_FLAGS_CFB64)
- valmr |= AES_MR_CFBS_64b;
- else if (dd->flags & AES_FLAGS_CFB128)
- valmr |= AES_MR_CFBS_128b;
- } else if (dd->flags & AES_FLAGS_OFB) {
- valmr |= AES_MR_OPMOD_OFB;
- } else if (dd->flags & AES_FLAGS_CTR) {
- valmr |= AES_MR_OPMOD_CTR;
- } else {
- valmr |= AES_MR_OPMOD_ECB;
- }
+ desc->callback = callback;
+ desc->callback_param = dd;
+ dmaengine_submit(desc);
+ dma_async_issue_pending(dma->chan);
- if (dd->flags & AES_FLAGS_ENCRYPT)
- valmr |= AES_MR_CYPHER_ENC;
+ return 0;
+}
- if (dd->total > ATMEL_AES_DMA_THRESHOLD) {
- valmr |= AES_MR_SMOD_IDATAR0;
- if (dd->caps.has_dualbuff)
- valmr |= AES_MR_DUALBUFF;
- } else {
- valmr |= AES_MR_SMOD_AUTO;
+static void atmel_aes_dma_transfer_stop(struct atmel_aes_dev *dd,
+ enum dma_transfer_direction dir)
+{
+ struct atmel_aes_dma *dma;
+
+ switch (dir) {
+ case DMA_MEM_TO_DEV:
+ dma = &dd->src;
+ break;
+
+ case DMA_DEV_TO_MEM:
+ dma = &dd->dst;
+ break;
+
+ default:
+ return;
}
- atmel_aes_write(dd, AES_CR, valcr);
- atmel_aes_write(dd, AES_MR, valmr);
+ dmaengine_terminate_all(dma->chan);
+}
- atmel_aes_write_n(dd, AES_KEYWR(0), dd->ctx->key,
- dd->ctx->keylen >> 2);
+static int atmel_aes_dma_start(struct atmel_aes_dev *dd,
+ struct scatterlist *src,
+ struct scatterlist *dst,
+ size_t len,
+ atmel_aes_fn_t resume)
+{
+ enum dma_slave_buswidth addr_width;
+ u32 maxburst;
+ int err;
+
+ switch (dd->ctx->block_size) {
+ case CFB8_BLOCK_SIZE:
+ addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ maxburst = 1;
+ break;
+
+ case CFB16_BLOCK_SIZE:
+ addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ maxburst = 1;
+ break;
+
+ case CFB32_BLOCK_SIZE:
+ case CFB64_BLOCK_SIZE:
+ addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ maxburst = 1;
+ break;
+
+ case AES_BLOCK_SIZE:
+ addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ maxburst = dd->caps.max_burst_size;
+ break;
- if (((dd->flags & AES_FLAGS_CBC) || (dd->flags & AES_FLAGS_CFB) ||
- (dd->flags & AES_FLAGS_OFB) || (dd->flags & AES_FLAGS_CTR)) &&
- dd->req->info) {
- atmel_aes_write_n(dd, AES_IVR(0), dd->req->info, 4);
+ default:
+ err = -EINVAL;
+ goto exit;
}
- return 0;
+ err = atmel_aes_map(dd, src, dst, len);
+ if (err)
+ goto exit;
+
+ dd->resume = resume;
+
+ /* Set output DMA transfer first */
+ err = atmel_aes_dma_transfer_start(dd, addr_width, DMA_DEV_TO_MEM,
+ maxburst);
+ if (err)
+ goto unmap;
+
+ /* Then set input DMA transfer */
+ err = atmel_aes_dma_transfer_start(dd, addr_width, DMA_MEM_TO_DEV,
+ maxburst);
+ if (err)
+ goto output_transfer_stop;
+
+ return -EINPROGRESS;
+
+output_transfer_stop:
+ atmel_aes_dma_transfer_stop(dd, DMA_DEV_TO_MEM);
+unmap:
+ atmel_aes_unmap(dd);
+exit:
+ return atmel_aes_complete(dd, err);
+}
+
+static void atmel_aes_dma_stop(struct atmel_aes_dev *dd)
+{
+ atmel_aes_dma_transfer_stop(dd, DMA_MEM_TO_DEV);
+ atmel_aes_dma_transfer_stop(dd, DMA_DEV_TO_MEM);
+ atmel_aes_unmap(dd);
+}
+
+static void atmel_aes_dma_callback(void *data)
+{
+ struct atmel_aes_dev *dd = data;
+
+ atmel_aes_dma_stop(dd);
+ dd->is_async = true;
+ (void)dd->resume(dd);
}
static int atmel_aes_handle_queue(struct atmel_aes_dev *dd,
- struct ablkcipher_request *req)
+ struct crypto_async_request *new_areq)
{
- struct crypto_async_request *async_req, *backlog;
- struct atmel_aes_ctx *ctx;
- struct atmel_aes_reqctx *rctx;
+ struct crypto_async_request *areq, *backlog;
+ struct atmel_aes_base_ctx *ctx;
unsigned long flags;
int err, ret = 0;
spin_lock_irqsave(&dd->lock, flags);
- if (req)
- ret = ablkcipher_enqueue_request(&dd->queue, req);
+ if (new_areq)
+ ret = crypto_enqueue_request(&dd->queue, new_areq);
if (dd->flags & AES_FLAGS_BUSY) {
spin_unlock_irqrestore(&dd->lock, flags);
return ret;
}
backlog = crypto_get_backlog(&dd->queue);
- async_req = crypto_dequeue_request(&dd->queue);
- if (async_req)
+ areq = crypto_dequeue_request(&dd->queue);
+ if (areq)
dd->flags |= AES_FLAGS_BUSY;
spin_unlock_irqrestore(&dd->lock, flags);
- if (!async_req)
+ if (!areq)
return ret;
if (backlog)
backlog->complete(backlog, -EINPROGRESS);
- req = ablkcipher_request_cast(async_req);
+ ctx = crypto_tfm_ctx(areq->tfm);
- /* assign new request to device */
- dd->req = req;
- dd->total = req->nbytes;
- dd->in_offset = 0;
- dd->in_sg = req->src;
- dd->out_offset = 0;
- dd->out_sg = req->dst;
-
- rctx = ablkcipher_request_ctx(req);
- ctx = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req));
- rctx->mode &= AES_FLAGS_MODE_MASK;
- dd->flags = (dd->flags & ~AES_FLAGS_MODE_MASK) | rctx->mode;
+ dd->areq = areq;
dd->ctx = ctx;
- ctx->dd = dd;
+ dd->is_async = (areq != new_areq);
- err = atmel_aes_write_ctrl(dd);
- if (!err) {
- if (dd->total > ATMEL_AES_DMA_THRESHOLD)
- err = atmel_aes_crypt_dma_start(dd);
- else
- err = atmel_aes_crypt_cpu_start(dd);
- }
- if (err) {
- /* aes_task will not finish it, so do it here */
- atmel_aes_finish_req(dd, err);
- tasklet_schedule(&dd->queue_task);
- }
-
- return ret;
+ err = ctx->start(dd);
+ return (dd->is_async) ? ret : err;
}
-static int atmel_aes_crypt_dma_stop(struct atmel_aes_dev *dd)
-{
- int err = -EINVAL;
- size_t count;
- if (dd->flags & AES_FLAGS_DMA) {
- err = 0;
- if (dd->flags & AES_FLAGS_FAST) {
- dma_unmap_sg(dd->dev, dd->out_sg, 1, DMA_FROM_DEVICE);
- dma_unmap_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE);
- } else {
- dma_sync_single_for_cpu(dd->dev, dd->dma_addr_out,
- dd->dma_size, DMA_FROM_DEVICE);
-
- /* copy data */
- count = atmel_aes_sg_copy(&dd->out_sg, &dd->out_offset,
- dd->buf_out, dd->buflen, dd->dma_size, 1);
- if (count != dd->dma_size) {
- err = -EINVAL;
- pr_err("not all data converted: %u\n", count);
- }
- }
- }
+/* AES async block ciphers */
- return err;
+static int atmel_aes_transfer_complete(struct atmel_aes_dev *dd)
+{
+ return atmel_aes_complete(dd, 0);
}
-
-static int atmel_aes_buff_init(struct atmel_aes_dev *dd)
+static int atmel_aes_start(struct atmel_aes_dev *dd)
{
- int err = -ENOMEM;
-
- dd->buf_in = (void *)__get_free_pages(GFP_KERNEL, 0);
- dd->buf_out = (void *)__get_free_pages(GFP_KERNEL, 0);
- dd->buflen = PAGE_SIZE;
- dd->buflen &= ~(AES_BLOCK_SIZE - 1);
-
- if (!dd->buf_in || !dd->buf_out) {
- dev_err(dd->dev, "unable to alloc pages.\n");
- goto err_alloc;
- }
+ struct ablkcipher_request *req = ablkcipher_request_cast(dd->areq);
+ struct atmel_aes_reqctx *rctx = ablkcipher_request_ctx(req);
+ bool use_dma = (req->nbytes >= ATMEL_AES_DMA_THRESHOLD ||
+ dd->ctx->block_size != AES_BLOCK_SIZE);
+ int err;
- /* MAP here */
- dd->dma_addr_in = dma_map_single(dd->dev, dd->buf_in,
- dd->buflen, DMA_TO_DEVICE);
- if (dma_mapping_error(dd->dev, dd->dma_addr_in)) {
- dev_err(dd->dev, "dma %d bytes error\n", dd->buflen);
- err = -EINVAL;
- goto err_map_in;
- }
+ atmel_aes_set_mode(dd, rctx);
- dd->dma_addr_out = dma_map_single(dd->dev, dd->buf_out,
- dd->buflen, DMA_FROM_DEVICE);
- if (dma_mapping_error(dd->dev, dd->dma_addr_out)) {
- dev_err(dd->dev, "dma %d bytes error\n", dd->buflen);
- err = -EINVAL;
- goto err_map_out;
- }
+ err = atmel_aes_hw_init(dd);
+ if (err)
+ return atmel_aes_complete(dd, err);
- return 0;
+ atmel_aes_write_ctrl(dd, use_dma, req->info);
+ if (use_dma)
+ return atmel_aes_dma_start(dd, req->src, req->dst, req->nbytes,
+ atmel_aes_transfer_complete);
-err_map_out:
- dma_unmap_single(dd->dev, dd->dma_addr_in, dd->buflen,
- DMA_TO_DEVICE);
-err_map_in:
-err_alloc:
- free_page((unsigned long)dd->buf_out);
- free_page((unsigned long)dd->buf_in);
- if (err)
- pr_err("error: %d\n", err);
- return err;
+ return atmel_aes_cpu_start(dd, req->src, req->dst, req->nbytes,
+ atmel_aes_transfer_complete);
}
-static void atmel_aes_buff_cleanup(struct atmel_aes_dev *dd)
+static inline struct atmel_aes_ctr_ctx *
+atmel_aes_ctr_ctx_cast(struct atmel_aes_base_ctx *ctx)
{
- dma_unmap_single(dd->dev, dd->dma_addr_out, dd->buflen,
- DMA_FROM_DEVICE);
- dma_unmap_single(dd->dev, dd->dma_addr_in, dd->buflen,
- DMA_TO_DEVICE);
- free_page((unsigned long)dd->buf_out);
- free_page((unsigned long)dd->buf_in);
+ return container_of(ctx, struct atmel_aes_ctr_ctx, base);
}
-static int atmel_aes_crypt(struct ablkcipher_request *req, unsigned long mode)
+static int atmel_aes_ctr_transfer(struct atmel_aes_dev *dd)
{
- struct atmel_aes_ctx *ctx = crypto_ablkcipher_ctx(
- crypto_ablkcipher_reqtfm(req));
- struct atmel_aes_reqctx *rctx = ablkcipher_request_ctx(req);
- struct atmel_aes_dev *dd;
-
- if (mode & AES_FLAGS_CFB8) {
- if (!IS_ALIGNED(req->nbytes, CFB8_BLOCK_SIZE)) {
- pr_err("request size is not exact amount of CFB8 blocks\n");
- return -EINVAL;
- }
- ctx->block_size = CFB8_BLOCK_SIZE;
- } else if (mode & AES_FLAGS_CFB16) {
- if (!IS_ALIGNED(req->nbytes, CFB16_BLOCK_SIZE)) {
- pr_err("request size is not exact amount of CFB16 blocks\n");
- return -EINVAL;
- }
- ctx->block_size = CFB16_BLOCK_SIZE;
- } else if (mode & AES_FLAGS_CFB32) {
- if (!IS_ALIGNED(req->nbytes, CFB32_BLOCK_SIZE)) {
- pr_err("request size is not exact amount of CFB32 blocks\n");
- return -EINVAL;
- }
- ctx->block_size = CFB32_BLOCK_SIZE;
- } else if (mode & AES_FLAGS_CFB64) {
- if (!IS_ALIGNED(req->nbytes, CFB64_BLOCK_SIZE)) {
- pr_err("request size is not exact amount of CFB64 blocks\n");
- return -EINVAL;
+ struct atmel_aes_ctr_ctx *ctx = atmel_aes_ctr_ctx_cast(dd->ctx);
+ struct ablkcipher_request *req = ablkcipher_request_cast(dd->areq);
+ struct scatterlist *src, *dst;
+ u32 ctr, blocks;
+ size_t datalen;
+ bool use_dma, fragmented = false;
+
+ /* Check for transfer completion. */
+ ctx->offset += dd->total;
+ if (ctx->offset >= req->nbytes)
+ return atmel_aes_transfer_complete(dd);
+
+ /* Compute data length. */
+ datalen = req->nbytes - ctx->offset;
+ blocks = DIV_ROUND_UP(datalen, AES_BLOCK_SIZE);
+ ctr = be32_to_cpu(ctx->iv[3]);
+ if (dd->caps.has_ctr32) {
+ /* Check 32bit counter overflow. */
+ u32 start = ctr;
+ u32 end = start + blocks - 1;
+
+ if (end < start) {
+ ctr |= 0xffffffff;
+ datalen = AES_BLOCK_SIZE * -start;
+ fragmented = true;
}
- ctx->block_size = CFB64_BLOCK_SIZE;
} else {
- if (!IS_ALIGNED(req->nbytes, AES_BLOCK_SIZE)) {
- pr_err("request size is not exact amount of AES blocks\n");
- return -EINVAL;
+ /* Check 16bit counter overflow. */
+ u16 start = ctr & 0xffff;
+ u16 end = start + (u16)blocks - 1;
+
+ if (blocks >> 16 || end < start) {
+ ctr |= 0xffff;
+ datalen = AES_BLOCK_SIZE * (0x10000-start);
+ fragmented = true;
}
- ctx->block_size = AES_BLOCK_SIZE;
+ }
+ use_dma = (datalen >= ATMEL_AES_DMA_THRESHOLD);
+
+ /* Jump to offset. */
+ src = scatterwalk_ffwd(ctx->src, req->src, ctx->offset);
+ dst = ((req->src == req->dst) ? src :
+ scatterwalk_ffwd(ctx->dst, req->dst, ctx->offset));
+
+ /* Configure hardware. */
+ atmel_aes_write_ctrl(dd, use_dma, ctx->iv);
+ if (unlikely(fragmented)) {
+ /*
+ * Increment the counter manually to cope with the hardware
+ * counter overflow.
+ */
+ ctx->iv[3] = cpu_to_be32(ctr);
+ crypto_inc((u8 *)ctx->iv, AES_BLOCK_SIZE);
}
- dd = atmel_aes_find_dev(ctx);
- if (!dd)
- return -ENODEV;
-
- rctx->mode = mode;
+ if (use_dma)
+ return atmel_aes_dma_start(dd, src, dst, datalen,
+ atmel_aes_ctr_transfer);
- return atmel_aes_handle_queue(dd, req);
+ return atmel_aes_cpu_start(dd, src, dst, datalen,
+ atmel_aes_ctr_transfer);
}
-static bool atmel_aes_filter(struct dma_chan *chan, void *slave)
+static int atmel_aes_ctr_start(struct atmel_aes_dev *dd)
{
- struct at_dma_slave *sl = slave;
+ struct atmel_aes_ctr_ctx *ctx = atmel_aes_ctr_ctx_cast(dd->ctx);
+ struct ablkcipher_request *req = ablkcipher_request_cast(dd->areq);
+ struct atmel_aes_reqctx *rctx = ablkcipher_request_ctx(req);
+ int err;
- if (sl && sl->dma_dev == chan->device->dev) {
- chan->private = sl;
- return true;
- } else {
- return false;
- }
+ atmel_aes_set_mode(dd, rctx);
+
+ err = atmel_aes_hw_init(dd);
+ if (err)
+ return atmel_aes_complete(dd, err);
+
+ memcpy(ctx->iv, req->info, AES_BLOCK_SIZE);
+ ctx->offset = 0;
+ dd->total = 0;
+ return atmel_aes_ctr_transfer(dd);
}
-static int atmel_aes_dma_init(struct atmel_aes_dev *dd,
- struct crypto_platform_data *pdata)
+static int atmel_aes_crypt(struct ablkcipher_request *req, unsigned long mode)
{
- int err = -ENOMEM;
- dma_cap_mask_t mask;
+ struct atmel_aes_base_ctx *ctx;
+ struct atmel_aes_reqctx *rctx;
+ struct atmel_aes_dev *dd;
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
+ ctx = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req));
+ switch (mode & AES_FLAGS_OPMODE_MASK) {
+ case AES_FLAGS_CFB8:
+ ctx->block_size = CFB8_BLOCK_SIZE;
+ break;
- /* Try to grab 2 DMA channels */
- dd->dma_lch_in.chan = dma_request_slave_channel_compat(mask,
- atmel_aes_filter, &pdata->dma_slave->rxdata, dd->dev, "tx");
- if (!dd->dma_lch_in.chan)
- goto err_dma_in;
+ case AES_FLAGS_CFB16:
+ ctx->block_size = CFB16_BLOCK_SIZE;
+ break;
- dd->dma_lch_in.dma_conf.direction = DMA_MEM_TO_DEV;
- dd->dma_lch_in.dma_conf.dst_addr = dd->phys_base +
- AES_IDATAR(0);
- dd->dma_lch_in.dma_conf.src_maxburst = dd->caps.max_burst_size;
- dd->dma_lch_in.dma_conf.src_addr_width =
- DMA_SLAVE_BUSWIDTH_4_BYTES;
- dd->dma_lch_in.dma_conf.dst_maxburst = dd->caps.max_burst_size;
- dd->dma_lch_in.dma_conf.dst_addr_width =
- DMA_SLAVE_BUSWIDTH_4_BYTES;
- dd->dma_lch_in.dma_conf.device_fc = false;
-
- dd->dma_lch_out.chan = dma_request_slave_channel_compat(mask,
- atmel_aes_filter, &pdata->dma_slave->txdata, dd->dev, "rx");
- if (!dd->dma_lch_out.chan)
- goto err_dma_out;
+ case AES_FLAGS_CFB32:
+ ctx->block_size = CFB32_BLOCK_SIZE;
+ break;
- dd->dma_lch_out.dma_conf.direction = DMA_DEV_TO_MEM;
- dd->dma_lch_out.dma_conf.src_addr = dd->phys_base +
- AES_ODATAR(0);
- dd->dma_lch_out.dma_conf.src_maxburst = dd->caps.max_burst_size;
- dd->dma_lch_out.dma_conf.src_addr_width =
- DMA_SLAVE_BUSWIDTH_4_BYTES;
- dd->dma_lch_out.dma_conf.dst_maxburst = dd->caps.max_burst_size;
- dd->dma_lch_out.dma_conf.dst_addr_width =
- DMA_SLAVE_BUSWIDTH_4_BYTES;
- dd->dma_lch_out.dma_conf.device_fc = false;
+ case AES_FLAGS_CFB64:
+ ctx->block_size = CFB64_BLOCK_SIZE;
+ break;
- return 0;
+ default:
+ ctx->block_size = AES_BLOCK_SIZE;
+ break;
+ }
-err_dma_out:
- dma_release_channel(dd->dma_lch_in.chan);
-err_dma_in:
- dev_warn(dd->dev, "no DMA channel available\n");
- return err;
-}
+ dd = atmel_aes_find_dev(ctx);
+ if (!dd)
+ return -ENODEV;
-static void atmel_aes_dma_cleanup(struct atmel_aes_dev *dd)
-{
- dma_release_channel(dd->dma_lch_in.chan);
- dma_release_channel(dd->dma_lch_out.chan);
+ rctx = ablkcipher_request_ctx(req);
+ rctx->mode = mode;
+
+ return atmel_aes_handle_queue(dd, &req->base);
}
static int atmel_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
unsigned int keylen)
{
- struct atmel_aes_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+ struct atmel_aes_base_ctx *ctx = crypto_ablkcipher_ctx(tfm);
- if (keylen != AES_KEYSIZE_128 && keylen != AES_KEYSIZE_192 &&
- keylen != AES_KEYSIZE_256) {
+ if (keylen != AES_KEYSIZE_128 &&
+ keylen != AES_KEYSIZE_192 &&
+ keylen != AES_KEYSIZE_256) {
crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
return -EINVAL;
}
@@ -839,115 +1066,110 @@ static int atmel_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
static int atmel_aes_ecb_encrypt(struct ablkcipher_request *req)
{
- return atmel_aes_crypt(req,
- AES_FLAGS_ENCRYPT);
+ return atmel_aes_crypt(req, AES_FLAGS_ECB | AES_FLAGS_ENCRYPT);
}
static int atmel_aes_ecb_decrypt(struct ablkcipher_request *req)
{
- return atmel_aes_crypt(req,
- 0);
+ return atmel_aes_crypt(req, AES_FLAGS_ECB);
}
static int atmel_aes_cbc_encrypt(struct ablkcipher_request *req)
{
- return atmel_aes_crypt(req,
- AES_FLAGS_ENCRYPT | AES_FLAGS_CBC);
+ return atmel_aes_crypt(req, AES_FLAGS_CBC | AES_FLAGS_ENCRYPT);
}
static int atmel_aes_cbc_decrypt(struct ablkcipher_request *req)
{
- return atmel_aes_crypt(req,
- AES_FLAGS_CBC);
+ return atmel_aes_crypt(req, AES_FLAGS_CBC);
}
static int atmel_aes_ofb_encrypt(struct ablkcipher_request *req)
{
- return atmel_aes_crypt(req,
- AES_FLAGS_ENCRYPT | AES_FLAGS_OFB);
+ return atmel_aes_crypt(req, AES_FLAGS_OFB | AES_FLAGS_ENCRYPT);
}
static int atmel_aes_ofb_decrypt(struct ablkcipher_request *req)
{
- return atmel_aes_crypt(req,
- AES_FLAGS_OFB);
+ return atmel_aes_crypt(req, AES_FLAGS_OFB);
}
static int atmel_aes_cfb_encrypt(struct ablkcipher_request *req)
{
- return atmel_aes_crypt(req,
- AES_FLAGS_ENCRYPT | AES_FLAGS_CFB | AES_FLAGS_CFB128);
+ return atmel_aes_crypt(req, AES_FLAGS_CFB128 | AES_FLAGS_ENCRYPT);
}
static int atmel_aes_cfb_decrypt(struct ablkcipher_request *req)
{
- return atmel_aes_crypt(req,
- AES_FLAGS_CFB | AES_FLAGS_CFB128);
+ return atmel_aes_crypt(req, AES_FLAGS_CFB128);
}
static int atmel_aes_cfb64_encrypt(struct ablkcipher_request *req)
{
- return atmel_aes_crypt(req,
- AES_FLAGS_ENCRYPT | AES_FLAGS_CFB | AES_FLAGS_CFB64);
+ return atmel_aes_crypt(req, AES_FLAGS_CFB64 | AES_FLAGS_ENCRYPT);
}
static int atmel_aes_cfb64_decrypt(struct ablkcipher_request *req)
{
- return atmel_aes_crypt(req,
- AES_FLAGS_CFB | AES_FLAGS_CFB64);
+ return atmel_aes_crypt(req, AES_FLAGS_CFB64);
}
static int atmel_aes_cfb32_encrypt(struct ablkcipher_request *req)
{
- return atmel_aes_crypt(req,
- AES_FLAGS_ENCRYPT | AES_FLAGS_CFB | AES_FLAGS_CFB32);
+ return atmel_aes_crypt(req, AES_FLAGS_CFB32 | AES_FLAGS_ENCRYPT);
}
static int atmel_aes_cfb32_decrypt(struct ablkcipher_request *req)
{
- return atmel_aes_crypt(req,
- AES_FLAGS_CFB | AES_FLAGS_CFB32);
+ return atmel_aes_crypt(req, AES_FLAGS_CFB32);
}
static int atmel_aes_cfb16_encrypt(struct ablkcipher_request *req)
{
- return atmel_aes_crypt(req,
- AES_FLAGS_ENCRYPT | AES_FLAGS_CFB | AES_FLAGS_CFB16);
+ return atmel_aes_crypt(req, AES_FLAGS_CFB16 | AES_FLAGS_ENCRYPT);
}
static int atmel_aes_cfb16_decrypt(struct ablkcipher_request *req)
{
- return atmel_aes_crypt(req,
- AES_FLAGS_CFB | AES_FLAGS_CFB16);
+ return atmel_aes_crypt(req, AES_FLAGS_CFB16);
}
static int atmel_aes_cfb8_encrypt(struct ablkcipher_request *req)
{
- return atmel_aes_crypt(req,
- AES_FLAGS_ENCRYPT | AES_FLAGS_CFB | AES_FLAGS_CFB8);
+ return atmel_aes_crypt(req, AES_FLAGS_CFB8 | AES_FLAGS_ENCRYPT);
}
static int atmel_aes_cfb8_decrypt(struct ablkcipher_request *req)
{
- return atmel_aes_crypt(req,
- AES_FLAGS_CFB | AES_FLAGS_CFB8);
+ return atmel_aes_crypt(req, AES_FLAGS_CFB8);
}
static int atmel_aes_ctr_encrypt(struct ablkcipher_request *req)
{
- return atmel_aes_crypt(req,
- AES_FLAGS_ENCRYPT | AES_FLAGS_CTR);
+ return atmel_aes_crypt(req, AES_FLAGS_CTR | AES_FLAGS_ENCRYPT);
}
static int atmel_aes_ctr_decrypt(struct ablkcipher_request *req)
{
- return atmel_aes_crypt(req,
- AES_FLAGS_CTR);
+ return atmel_aes_crypt(req, AES_FLAGS_CTR);
}
static int atmel_aes_cra_init(struct crypto_tfm *tfm)
{
+ struct atmel_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ tfm->crt_ablkcipher.reqsize = sizeof(struct atmel_aes_reqctx);
+ ctx->base.start = atmel_aes_start;
+
+ return 0;
+}
+
+static int atmel_aes_ctr_cra_init(struct crypto_tfm *tfm)
+{
+ struct atmel_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+
tfm->crt_ablkcipher.reqsize = sizeof(struct atmel_aes_reqctx);
+ ctx->base.start = atmel_aes_ctr_start;
return 0;
}
@@ -960,7 +1182,7 @@ static struct crypto_alg aes_algs[] = {
{
.cra_name = "ecb(aes)",
.cra_driver_name = "atmel-ecb-aes",
- .cra_priority = 100,
+ .cra_priority = ATMEL_AES_PRIORITY,
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_aes_ctx),
@@ -980,7 +1202,7 @@ static struct crypto_alg aes_algs[] = {
{
.cra_name = "cbc(aes)",
.cra_driver_name = "atmel-cbc-aes",
- .cra_priority = 100,
+ .cra_priority = ATMEL_AES_PRIORITY,
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_aes_ctx),
@@ -1001,7 +1223,7 @@ static struct crypto_alg aes_algs[] = {
{
.cra_name = "ofb(aes)",
.cra_driver_name = "atmel-ofb-aes",
- .cra_priority = 100,
+ .cra_priority = ATMEL_AES_PRIORITY,
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_aes_ctx),
@@ -1022,7 +1244,7 @@ static struct crypto_alg aes_algs[] = {
{
.cra_name = "cfb(aes)",
.cra_driver_name = "atmel-cfb-aes",
- .cra_priority = 100,
+ .cra_priority = ATMEL_AES_PRIORITY,
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_aes_ctx),
@@ -1043,7 +1265,7 @@ static struct crypto_alg aes_algs[] = {
{
.cra_name = "cfb32(aes)",
.cra_driver_name = "atmel-cfb32-aes",
- .cra_priority = 100,
+ .cra_priority = ATMEL_AES_PRIORITY,
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = CFB32_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_aes_ctx),
@@ -1064,7 +1286,7 @@ static struct crypto_alg aes_algs[] = {
{
.cra_name = "cfb16(aes)",
.cra_driver_name = "atmel-cfb16-aes",
- .cra_priority = 100,
+ .cra_priority = ATMEL_AES_PRIORITY,
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = CFB16_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_aes_ctx),
@@ -1085,7 +1307,7 @@ static struct crypto_alg aes_algs[] = {
{
.cra_name = "cfb8(aes)",
.cra_driver_name = "atmel-cfb8-aes",
- .cra_priority = 100,
+ .cra_priority = ATMEL_AES_PRIORITY,
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = CFB8_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_aes_ctx),
@@ -1106,14 +1328,14 @@ static struct crypto_alg aes_algs[] = {
{
.cra_name = "ctr(aes)",
.cra_driver_name = "atmel-ctr-aes",
- .cra_priority = 100,
+ .cra_priority = ATMEL_AES_PRIORITY,
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
- .cra_blocksize = AES_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct atmel_aes_ctx),
+ .cra_blocksize = 1,
+ .cra_ctxsize = sizeof(struct atmel_aes_ctr_ctx),
.cra_alignmask = 0xf,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
- .cra_init = atmel_aes_cra_init,
+ .cra_init = atmel_aes_ctr_cra_init,
.cra_exit = atmel_aes_cra_exit,
.cra_u.ablkcipher = {
.min_keysize = AES_MIN_KEY_SIZE,
@@ -1129,7 +1351,7 @@ static struct crypto_alg aes_algs[] = {
static struct crypto_alg aes_cfb64_alg = {
.cra_name = "cfb64(aes)",
.cra_driver_name = "atmel-cfb64-aes",
- .cra_priority = 100,
+ .cra_priority = ATMEL_AES_PRIORITY,
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = CFB64_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct atmel_aes_ctx),
@@ -1148,53 +1370,496 @@ static struct crypto_alg aes_cfb64_alg = {
}
};
-static void atmel_aes_queue_task(unsigned long data)
+
+/* gcm aead functions */
+
+static int atmel_aes_gcm_ghash(struct atmel_aes_dev *dd,
+ const u32 *data, size_t datalen,
+ const u32 *ghash_in, u32 *ghash_out,
+ atmel_aes_fn_t resume);
+static int atmel_aes_gcm_ghash_init(struct atmel_aes_dev *dd);
+static int atmel_aes_gcm_ghash_finalize(struct atmel_aes_dev *dd);
+
+static int atmel_aes_gcm_start(struct atmel_aes_dev *dd);
+static int atmel_aes_gcm_process(struct atmel_aes_dev *dd);
+static int atmel_aes_gcm_length(struct atmel_aes_dev *dd);
+static int atmel_aes_gcm_data(struct atmel_aes_dev *dd);
+static int atmel_aes_gcm_tag_init(struct atmel_aes_dev *dd);
+static int atmel_aes_gcm_tag(struct atmel_aes_dev *dd);
+static int atmel_aes_gcm_finalize(struct atmel_aes_dev *dd);
+
+static inline struct atmel_aes_gcm_ctx *
+atmel_aes_gcm_ctx_cast(struct atmel_aes_base_ctx *ctx)
{
- struct atmel_aes_dev *dd = (struct atmel_aes_dev *)data;
+ return container_of(ctx, struct atmel_aes_gcm_ctx, base);
+}
- atmel_aes_handle_queue(dd, NULL);
+static int atmel_aes_gcm_ghash(struct atmel_aes_dev *dd,
+ const u32 *data, size_t datalen,
+ const u32 *ghash_in, u32 *ghash_out,
+ atmel_aes_fn_t resume)
+{
+ struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx);
+
+ dd->data = (u32 *)data;
+ dd->datalen = datalen;
+ ctx->ghash_in = ghash_in;
+ ctx->ghash_out = ghash_out;
+ ctx->ghash_resume = resume;
+
+ atmel_aes_write_ctrl(dd, false, NULL);
+ return atmel_aes_wait_for_data_ready(dd, atmel_aes_gcm_ghash_init);
}
-static void atmel_aes_done_task(unsigned long data)
+static int atmel_aes_gcm_ghash_init(struct atmel_aes_dev *dd)
{
- struct atmel_aes_dev *dd = (struct atmel_aes_dev *) data;
+ struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx);
+
+ /* Set the data length. */
+ atmel_aes_write(dd, AES_AADLENR, dd->total);
+ atmel_aes_write(dd, AES_CLENR, 0);
+
+ /* If needed, overwrite the GCM Intermediate Hash Word Registers */
+ if (ctx->ghash_in)
+ atmel_aes_write_block(dd, AES_GHASHR(0), ctx->ghash_in);
+
+ return atmel_aes_gcm_ghash_finalize(dd);
+}
+
+static int atmel_aes_gcm_ghash_finalize(struct atmel_aes_dev *dd)
+{
+ struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx);
+ u32 isr;
+
+ /* Write data into the Input Data Registers. */
+ while (dd->datalen > 0) {
+ atmel_aes_write_block(dd, AES_IDATAR(0), dd->data);
+ dd->data += 4;
+ dd->datalen -= AES_BLOCK_SIZE;
+
+ isr = atmel_aes_read(dd, AES_ISR);
+ if (!(isr & AES_INT_DATARDY)) {
+ dd->resume = atmel_aes_gcm_ghash_finalize;
+ atmel_aes_write(dd, AES_IER, AES_INT_DATARDY);
+ return -EINPROGRESS;
+ }
+ }
+
+ /* Read the computed hash from GHASHRx. */
+ atmel_aes_read_block(dd, AES_GHASHR(0), ctx->ghash_out);
+
+ return ctx->ghash_resume(dd);
+}
+
+
+static int atmel_aes_gcm_start(struct atmel_aes_dev *dd)
+{
+ struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx);
+ struct aead_request *req = aead_request_cast(dd->areq);
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ struct atmel_aes_reqctx *rctx = aead_request_ctx(req);
+ size_t ivsize = crypto_aead_ivsize(tfm);
+ size_t datalen, padlen;
+ const void *iv = req->iv;
+ u8 *data = dd->buf;
int err;
- if (!(dd->flags & AES_FLAGS_DMA)) {
- atmel_aes_read_n(dd, AES_ODATAR(0), (u32 *) dd->buf_out,
- dd->bufcnt >> 2);
+ atmel_aes_set_mode(dd, rctx);
- if (sg_copy_from_buffer(dd->out_sg, dd->nb_out_sg,
- dd->buf_out, dd->bufcnt))
- err = 0;
- else
- err = -EINVAL;
+ err = atmel_aes_hw_init(dd);
+ if (err)
+ return atmel_aes_complete(dd, err);
+
+ if (likely(ivsize == 12)) {
+ memcpy(ctx->j0, iv, ivsize);
+ ctx->j0[3] = cpu_to_be32(1);
+ return atmel_aes_gcm_process(dd);
+ }
+
+ padlen = atmel_aes_padlen(ivsize, AES_BLOCK_SIZE);
+ datalen = ivsize + padlen + AES_BLOCK_SIZE;
+ if (datalen > dd->buflen)
+ return atmel_aes_complete(dd, -EINVAL);
+
+ memcpy(data, iv, ivsize);
+ memset(data + ivsize, 0, padlen + sizeof(u64));
+ ((u64 *)(data + datalen))[-1] = cpu_to_be64(ivsize * 8);
+
+ return atmel_aes_gcm_ghash(dd, (const u32 *)data, datalen,
+ NULL, ctx->j0, atmel_aes_gcm_process);
+}
- goto cpu_end;
+static int atmel_aes_gcm_process(struct atmel_aes_dev *dd)
+{
+ struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx);
+ struct aead_request *req = aead_request_cast(dd->areq);
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ bool enc = atmel_aes_is_encrypt(dd);
+ u32 authsize;
+
+ /* Compute text length. */
+ authsize = crypto_aead_authsize(tfm);
+ ctx->textlen = req->cryptlen - (enc ? 0 : authsize);
+
+ /*
+ * According to tcrypt test suite, the GCM Automatic Tag Generation
+ * fails when both the message and its associated data are empty.
+ */
+ if (likely(req->assoclen != 0 || ctx->textlen != 0))
+ dd->flags |= AES_FLAGS_GTAGEN;
+
+ atmel_aes_write_ctrl(dd, false, NULL);
+ return atmel_aes_wait_for_data_ready(dd, atmel_aes_gcm_length);
+}
+
+static int atmel_aes_gcm_length(struct atmel_aes_dev *dd)
+{
+ struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx);
+ struct aead_request *req = aead_request_cast(dd->areq);
+ u32 j0_lsw, *j0 = ctx->j0;
+ size_t padlen;
+
+ /* Write incr32(J0) into IV. */
+ j0_lsw = j0[3];
+ j0[3] = cpu_to_be32(be32_to_cpu(j0[3]) + 1);
+ atmel_aes_write_block(dd, AES_IVR(0), j0);
+ j0[3] = j0_lsw;
+
+ /* Set aad and text lengths. */
+ atmel_aes_write(dd, AES_AADLENR, req->assoclen);
+ atmel_aes_write(dd, AES_CLENR, ctx->textlen);
+
+ /* Check whether AAD are present. */
+ if (unlikely(req->assoclen == 0)) {
+ dd->datalen = 0;
+ return atmel_aes_gcm_data(dd);
}
- err = atmel_aes_crypt_dma_stop(dd);
+ /* Copy assoc data and add padding. */
+ padlen = atmel_aes_padlen(req->assoclen, AES_BLOCK_SIZE);
+ if (unlikely(req->assoclen + padlen > dd->buflen))
+ return atmel_aes_complete(dd, -EINVAL);
+ sg_copy_to_buffer(req->src, sg_nents(req->src), dd->buf, req->assoclen);
- err = dd->err ? : err;
+ /* Write assoc data into the Input Data register. */
+ dd->data = (u32 *)dd->buf;
+ dd->datalen = req->assoclen + padlen;
+ return atmel_aes_gcm_data(dd);
+}
- if (dd->total && !err) {
- if (dd->flags & AES_FLAGS_FAST) {
- dd->in_sg = sg_next(dd->in_sg);
- dd->out_sg = sg_next(dd->out_sg);
- if (!dd->in_sg || !dd->out_sg)
- err = -EINVAL;
+static int atmel_aes_gcm_data(struct atmel_aes_dev *dd)
+{
+ struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx);
+ struct aead_request *req = aead_request_cast(dd->areq);
+ bool use_dma = (ctx->textlen >= ATMEL_AES_DMA_THRESHOLD);
+ struct scatterlist *src, *dst;
+ u32 isr, mr;
+
+ /* Write AAD first. */
+ while (dd->datalen > 0) {
+ atmel_aes_write_block(dd, AES_IDATAR(0), dd->data);
+ dd->data += 4;
+ dd->datalen -= AES_BLOCK_SIZE;
+
+ isr = atmel_aes_read(dd, AES_ISR);
+ if (!(isr & AES_INT_DATARDY)) {
+ dd->resume = atmel_aes_gcm_data;
+ atmel_aes_write(dd, AES_IER, AES_INT_DATARDY);
+ return -EINPROGRESS;
}
- if (!err)
- err = atmel_aes_crypt_dma_start(dd);
- if (!err)
- return; /* DMA started. Not fininishing. */
}
-cpu_end:
- atmel_aes_finish_req(dd, err);
+ /* GMAC only. */
+ if (unlikely(ctx->textlen == 0))
+ return atmel_aes_gcm_tag_init(dd);
+
+ /* Prepare src and dst scatter lists to transfer cipher/plain texts */
+ src = scatterwalk_ffwd(ctx->src, req->src, req->assoclen);
+ dst = ((req->src == req->dst) ? src :
+ scatterwalk_ffwd(ctx->dst, req->dst, req->assoclen));
+
+ if (use_dma) {
+ /* Update the Mode Register for DMA transfers. */
+ mr = atmel_aes_read(dd, AES_MR);
+ mr &= ~(AES_MR_SMOD_MASK | AES_MR_DUALBUFF);
+ mr |= AES_MR_SMOD_IDATAR0;
+ if (dd->caps.has_dualbuff)
+ mr |= AES_MR_DUALBUFF;
+ atmel_aes_write(dd, AES_MR, mr);
+
+ return atmel_aes_dma_start(dd, src, dst, ctx->textlen,
+ atmel_aes_gcm_tag_init);
+ }
+
+ return atmel_aes_cpu_start(dd, src, dst, ctx->textlen,
+ atmel_aes_gcm_tag_init);
+}
+
+static int atmel_aes_gcm_tag_init(struct atmel_aes_dev *dd)
+{
+ struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx);
+ struct aead_request *req = aead_request_cast(dd->areq);
+ u64 *data = dd->buf;
+
+ if (likely(dd->flags & AES_FLAGS_GTAGEN)) {
+ if (!(atmel_aes_read(dd, AES_ISR) & AES_INT_TAGRDY)) {
+ dd->resume = atmel_aes_gcm_tag_init;
+ atmel_aes_write(dd, AES_IER, AES_INT_TAGRDY);
+ return -EINPROGRESS;
+ }
+
+ return atmel_aes_gcm_finalize(dd);
+ }
+
+ /* Read the GCM Intermediate Hash Word Registers. */
+ atmel_aes_read_block(dd, AES_GHASHR(0), ctx->ghash);
+
+ data[0] = cpu_to_be64(req->assoclen * 8);
+ data[1] = cpu_to_be64(ctx->textlen * 8);
+
+ return atmel_aes_gcm_ghash(dd, (const u32 *)data, AES_BLOCK_SIZE,
+ ctx->ghash, ctx->ghash, atmel_aes_gcm_tag);
+}
+
+static int atmel_aes_gcm_tag(struct atmel_aes_dev *dd)
+{
+ struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx);
+ unsigned long flags;
+
+ /*
+ * Change mode to CTR to complete the tag generation.
+ * Use J0 as Initialization Vector.
+ */
+ flags = dd->flags;
+ dd->flags &= ~(AES_FLAGS_OPMODE_MASK | AES_FLAGS_GTAGEN);
+ dd->flags |= AES_FLAGS_CTR;
+ atmel_aes_write_ctrl(dd, false, ctx->j0);
+ dd->flags = flags;
+
+ atmel_aes_write_block(dd, AES_IDATAR(0), ctx->ghash);
+ return atmel_aes_wait_for_data_ready(dd, atmel_aes_gcm_finalize);
+}
+
+static int atmel_aes_gcm_finalize(struct atmel_aes_dev *dd)
+{
+ struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx);
+ struct aead_request *req = aead_request_cast(dd->areq);
+ struct crypto_aead *tfm = crypto_aead_reqtfm(req);
+ bool enc = atmel_aes_is_encrypt(dd);
+ u32 offset, authsize, itag[4], *otag = ctx->tag;
+ int err;
+
+ /* Read the computed tag. */
+ if (likely(dd->flags & AES_FLAGS_GTAGEN))
+ atmel_aes_read_block(dd, AES_TAGR(0), ctx->tag);
+ else
+ atmel_aes_read_block(dd, AES_ODATAR(0), ctx->tag);
+
+ offset = req->assoclen + ctx->textlen;
+ authsize = crypto_aead_authsize(tfm);
+ if (enc) {
+ scatterwalk_map_and_copy(otag, req->dst, offset, authsize, 1);
+ err = 0;
+ } else {
+ scatterwalk_map_and_copy(itag, req->src, offset, authsize, 0);
+ err = crypto_memneq(itag, otag, authsize) ? -EBADMSG : 0;
+ }
+
+ return atmel_aes_complete(dd, err);
+}
+
+static int atmel_aes_gcm_crypt(struct aead_request *req,
+ unsigned long mode)
+{
+ struct atmel_aes_base_ctx *ctx;
+ struct atmel_aes_reqctx *rctx;
+ struct atmel_aes_dev *dd;
+
+ ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
+ ctx->block_size = AES_BLOCK_SIZE;
+
+ dd = atmel_aes_find_dev(ctx);
+ if (!dd)
+ return -ENODEV;
+
+ rctx = aead_request_ctx(req);
+ rctx->mode = AES_FLAGS_GCM | mode;
+
+ return atmel_aes_handle_queue(dd, &req->base);
+}
+
+static int atmel_aes_gcm_setkey(struct crypto_aead *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct atmel_aes_base_ctx *ctx = crypto_aead_ctx(tfm);
+
+ if (keylen != AES_KEYSIZE_256 &&
+ keylen != AES_KEYSIZE_192 &&
+ keylen != AES_KEYSIZE_128) {
+ crypto_aead_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+ }
+
+ memcpy(ctx->key, key, keylen);
+ ctx->keylen = keylen;
+
+ return 0;
+}
+
+static int atmel_aes_gcm_setauthsize(struct crypto_aead *tfm,
+ unsigned int authsize)
+{
+ /* Same as crypto_gcm_authsize() from crypto/gcm.c */
+ switch (authsize) {
+ case 4:
+ case 8:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int atmel_aes_gcm_encrypt(struct aead_request *req)
+{
+ return atmel_aes_gcm_crypt(req, AES_FLAGS_ENCRYPT);
+}
+
+static int atmel_aes_gcm_decrypt(struct aead_request *req)
+{
+ return atmel_aes_gcm_crypt(req, 0);
+}
+
+static int atmel_aes_gcm_init(struct crypto_aead *tfm)
+{
+ struct atmel_aes_gcm_ctx *ctx = crypto_aead_ctx(tfm);
+
+ crypto_aead_set_reqsize(tfm, sizeof(struct atmel_aes_reqctx));
+ ctx->base.start = atmel_aes_gcm_start;
+
+ return 0;
+}
+
+static void atmel_aes_gcm_exit(struct crypto_aead *tfm)
+{
+
+}
+
+static struct aead_alg aes_gcm_alg = {
+ .setkey = atmel_aes_gcm_setkey,
+ .setauthsize = atmel_aes_gcm_setauthsize,
+ .encrypt = atmel_aes_gcm_encrypt,
+ .decrypt = atmel_aes_gcm_decrypt,
+ .init = atmel_aes_gcm_init,
+ .exit = atmel_aes_gcm_exit,
+ .ivsize = 12,
+ .maxauthsize = AES_BLOCK_SIZE,
+
+ .base = {
+ .cra_name = "gcm(aes)",
+ .cra_driver_name = "atmel-gcm-aes",
+ .cra_priority = ATMEL_AES_PRIORITY,
+ .cra_flags = CRYPTO_ALG_ASYNC,
+ .cra_blocksize = 1,
+ .cra_ctxsize = sizeof(struct atmel_aes_gcm_ctx),
+ .cra_alignmask = 0xf,
+ .cra_module = THIS_MODULE,
+ },
+};
+
+
+/* Probe functions */
+
+static int atmel_aes_buff_init(struct atmel_aes_dev *dd)
+{
+ dd->buf = (void *)__get_free_pages(GFP_KERNEL, ATMEL_AES_BUFFER_ORDER);
+ dd->buflen = ATMEL_AES_BUFFER_SIZE;
+ dd->buflen &= ~(AES_BLOCK_SIZE - 1);
+
+ if (!dd->buf) {
+ dev_err(dd->dev, "unable to alloc pages.\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void atmel_aes_buff_cleanup(struct atmel_aes_dev *dd)
+{
+ free_page((unsigned long)dd->buf);
+}
+
+static bool atmel_aes_filter(struct dma_chan *chan, void *slave)
+{
+ struct at_dma_slave *sl = slave;
+
+ if (sl && sl->dma_dev == chan->device->dev) {
+ chan->private = sl;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static int atmel_aes_dma_init(struct atmel_aes_dev *dd,
+ struct crypto_platform_data *pdata)
+{
+ struct at_dma_slave *slave;
+ int err = -ENOMEM;
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ /* Try to grab 2 DMA channels */
+ slave = &pdata->dma_slave->rxdata;
+ dd->src.chan = dma_request_slave_channel_compat(mask, atmel_aes_filter,
+ slave, dd->dev, "tx");
+ if (!dd->src.chan)
+ goto err_dma_in;
+
+ slave = &pdata->dma_slave->txdata;
+ dd->dst.chan = dma_request_slave_channel_compat(mask, atmel_aes_filter,
+ slave, dd->dev, "rx");
+ if (!dd->dst.chan)
+ goto err_dma_out;
+
+ return 0;
+
+err_dma_out:
+ dma_release_channel(dd->src.chan);
+err_dma_in:
+ dev_warn(dd->dev, "no DMA channel available\n");
+ return err;
+}
+
+static void atmel_aes_dma_cleanup(struct atmel_aes_dev *dd)
+{
+ dma_release_channel(dd->dst.chan);
+ dma_release_channel(dd->src.chan);
+}
+
+static void atmel_aes_queue_task(unsigned long data)
+{
+ struct atmel_aes_dev *dd = (struct atmel_aes_dev *)data;
+
atmel_aes_handle_queue(dd, NULL);
}
+static void atmel_aes_done_task(unsigned long data)
+{
+ struct atmel_aes_dev *dd = (struct atmel_aes_dev *)data;
+
+ dd->is_async = true;
+ (void)dd->resume(dd);
+}
+
static irqreturn_t atmel_aes_irq(int irq, void *dev_id)
{
struct atmel_aes_dev *aes_dd = dev_id;
@@ -1217,10 +1882,14 @@ static void atmel_aes_unregister_algs(struct atmel_aes_dev *dd)
{
int i;
- for (i = 0; i < ARRAY_SIZE(aes_algs); i++)
- crypto_unregister_alg(&aes_algs[i]);
+ if (dd->caps.has_gcm)
+ crypto_unregister_aead(&aes_gcm_alg);
+
if (dd->caps.has_cfb64)
crypto_unregister_alg(&aes_cfb64_alg);
+
+ for (i = 0; i < ARRAY_SIZE(aes_algs); i++)
+ crypto_unregister_alg(&aes_algs[i]);
}
static int atmel_aes_register_algs(struct atmel_aes_dev *dd)
@@ -1239,8 +1908,16 @@ static int atmel_aes_register_algs(struct atmel_aes_dev *dd)
goto err_aes_cfb64_alg;
}
+ if (dd->caps.has_gcm) {
+ err = crypto_register_aead(&aes_gcm_alg);
+ if (err)
+ goto err_aes_gcm_alg;
+ }
+
return 0;
+err_aes_gcm_alg:
+ crypto_unregister_alg(&aes_cfb64_alg);
err_aes_cfb64_alg:
i = ARRAY_SIZE(aes_algs);
err_aes_algs:
@@ -1254,13 +1931,24 @@ static void atmel_aes_get_cap(struct atmel_aes_dev *dd)
{
dd->caps.has_dualbuff = 0;
dd->caps.has_cfb64 = 0;
+ dd->caps.has_ctr32 = 0;
+ dd->caps.has_gcm = 0;
dd->caps.max_burst_size = 1;
/* keep only major version number */
switch (dd->hw_version & 0xff0) {
+ case 0x500:
+ dd->caps.has_dualbuff = 1;
+ dd->caps.has_cfb64 = 1;
+ dd->caps.has_ctr32 = 1;
+ dd->caps.has_gcm = 1;
+ dd->caps.max_burst_size = 4;
+ break;
case 0x200:
dd->caps.has_dualbuff = 1;
dd->caps.has_cfb64 = 1;
+ dd->caps.has_ctr32 = 1;
+ dd->caps.has_gcm = 1;
dd->caps.max_burst_size = 4;
break;
case 0x130:
@@ -1402,7 +2090,9 @@ static int atmel_aes_probe(struct platform_device *pdev)
goto res_err;
}
- atmel_aes_hw_version_init(aes_dd);
+ err = atmel_aes_hw_version_init(aes_dd);
+ if (err)
+ goto res_err;
atmel_aes_get_cap(aes_dd);
@@ -1423,8 +2113,8 @@ static int atmel_aes_probe(struct platform_device *pdev)
goto err_algs;
dev_info(dev, "Atmel AES - Using %s, %s for DMA transfers\n",
- dma_chan_name(aes_dd->dma_lch_in.chan),
- dma_chan_name(aes_dd->dma_lch_out.chan));
+ dma_chan_name(aes_dd->src.chan),
+ dma_chan_name(aes_dd->dst.chan));
return 0;
@@ -1462,6 +2152,7 @@ static int atmel_aes_remove(struct platform_device *pdev)
tasklet_kill(&aes_dd->queue_task);
atmel_aes_dma_cleanup(aes_dd);
+ atmel_aes_buff_cleanup(aes_dd);
return 0;
}
diff --git a/drivers/crypto/atmel-sha.c b/drivers/crypto/atmel-sha.c
index 660d8c06540b..20de861aa0ea 100644
--- a/drivers/crypto/atmel-sha.c
+++ b/drivers/crypto/atmel-sha.c
@@ -755,7 +755,6 @@ static int atmel_sha_finish(struct ahash_request *req)
{
struct atmel_sha_reqctx *ctx = ahash_request_ctx(req);
struct atmel_sha_dev *dd = ctx->dd;
- int err = 0;
if (ctx->digcnt[0] || ctx->digcnt[1])
atmel_sha_copy_ready_hash(req);
@@ -763,7 +762,7 @@ static int atmel_sha_finish(struct ahash_request *req)
dev_dbg(dd->dev, "digcnt: 0x%llx 0x%llx, bufcnt: %d\n", ctx->digcnt[1],
ctx->digcnt[0], ctx->bufcnt);
- return err;
+ return 0;
}
static void atmel_sha_finish_req(struct ahash_request *req, int err)
diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c
index 49106ea42887..5845d4a08797 100644
--- a/drivers/crypto/caam/caamhash.c
+++ b/drivers/crypto/caam/caamhash.c
@@ -803,6 +803,10 @@ static int ahash_update_ctx(struct ahash_request *req)
if (to_hash) {
src_nents = sg_nents_for_len(req->src,
req->nbytes - (*next_buflen));
+ if (src_nents < 0) {
+ dev_err(jrdev, "Invalid number of src SG.\n");
+ return src_nents;
+ }
sec4_sg_src_index = 1 + (*buflen ? 1 : 0);
sec4_sg_bytes = (sec4_sg_src_index + src_nents) *
sizeof(struct sec4_sg_entry);
@@ -1002,6 +1006,10 @@ static int ahash_finup_ctx(struct ahash_request *req)
int sh_len;
src_nents = sg_nents_for_len(req->src, req->nbytes);
+ if (src_nents < 0) {
+ dev_err(jrdev, "Invalid number of src SG.\n");
+ return src_nents;
+ }
sec4_sg_src_index = 1 + (buflen ? 1 : 0);
sec4_sg_bytes = (sec4_sg_src_index + src_nents) *
sizeof(struct sec4_sg_entry);
@@ -1086,6 +1094,10 @@ static int ahash_digest(struct ahash_request *req)
int sh_len;
src_nents = sg_count(req->src, req->nbytes);
+ if (src_nents < 0) {
+ dev_err(jrdev, "Invalid number of src SG.\n");
+ return src_nents;
+ }
dma_map_sg(jrdev, req->src, src_nents ? : 1, DMA_TO_DEVICE);
sec4_sg_bytes = src_nents * sizeof(struct sec4_sg_entry);
@@ -1234,6 +1246,10 @@ static int ahash_update_no_ctx(struct ahash_request *req)
if (to_hash) {
src_nents = sg_nents_for_len(req->src,
req->nbytes - (*next_buflen));
+ if (src_nents < 0) {
+ dev_err(jrdev, "Invalid number of src SG.\n");
+ return src_nents;
+ }
sec4_sg_bytes = (1 + src_nents) *
sizeof(struct sec4_sg_entry);
@@ -1342,6 +1358,10 @@ static int ahash_finup_no_ctx(struct ahash_request *req)
int ret = 0;
src_nents = sg_nents_for_len(req->src, req->nbytes);
+ if (src_nents < 0) {
+ dev_err(jrdev, "Invalid number of src SG.\n");
+ return src_nents;
+ }
sec4_sg_src_index = 2;
sec4_sg_bytes = (sec4_sg_src_index + src_nents) *
sizeof(struct sec4_sg_entry);
@@ -1430,6 +1450,10 @@ static int ahash_update_first(struct ahash_request *req)
if (to_hash) {
src_nents = sg_count(req->src, req->nbytes - (*next_buflen));
+ if (src_nents < 0) {
+ dev_err(jrdev, "Invalid number of src SG.\n");
+ return src_nents;
+ }
dma_map_sg(jrdev, req->src, src_nents ? : 1, DMA_TO_DEVICE);
sec4_sg_bytes = src_nents * sizeof(struct sec4_sg_entry);
@@ -1572,7 +1596,7 @@ static int ahash_export(struct ahash_request *req, void *out)
len = state->buflen_1;
} else {
buf = state->buf_0;
- len = state->buflen_1;
+ len = state->buflen_0;
}
memcpy(export->buf, buf, len);
diff --git a/drivers/crypto/ccp/Kconfig b/drivers/crypto/ccp/Kconfig
index 3cd8481065f8..6e37845abf8f 100644
--- a/drivers/crypto/ccp/Kconfig
+++ b/drivers/crypto/ccp/Kconfig
@@ -3,6 +3,8 @@ config CRYPTO_DEV_CCP_DD
depends on CRYPTO_DEV_CCP
default m
select HW_RANDOM
+ select CRYPTO_SHA1
+ select CRYPTO_SHA256
help
Provides the interface to use the AMD Cryptographic Coprocessor
which can be used to offload encryption operations such as SHA,
diff --git a/drivers/crypto/ccp/ccp-ops.c b/drivers/crypto/ccp/ccp-ops.c
index c6e883b296a9..6613aee79b87 100644
--- a/drivers/crypto/ccp/ccp-ops.c
+++ b/drivers/crypto/ccp/ccp-ops.c
@@ -152,32 +152,6 @@ static const __be32 ccp_sha256_init[CCP_SHA_CTXSIZE / sizeof(__be32)] = {
cpu_to_be32(SHA256_H6), cpu_to_be32(SHA256_H7),
};
-/* The CCP cannot perform zero-length sha operations so the caller
- * is required to buffer data for the final operation. However, a
- * sha operation for a message with a total length of zero is valid
- * so known values are required to supply the result.
- */
-static const u8 ccp_sha1_zero[CCP_SHA_CTXSIZE] = {
- 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d,
- 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90,
- 0xaf, 0xd8, 0x07, 0x09, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-static const u8 ccp_sha224_zero[CCP_SHA_CTXSIZE] = {
- 0xd1, 0x4a, 0x02, 0x8c, 0x2a, 0x3a, 0x2b, 0xc9,
- 0x47, 0x61, 0x02, 0xbb, 0x28, 0x82, 0x34, 0xc4,
- 0x15, 0xa2, 0xb0, 0x1f, 0x82, 0x8e, 0xa6, 0x2a,
- 0xc5, 0xb3, 0xe4, 0x2f, 0x00, 0x00, 0x00, 0x00,
-};
-
-static const u8 ccp_sha256_zero[CCP_SHA_CTXSIZE] = {
- 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
- 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
- 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
- 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55,
-};
-
static u32 ccp_addr_lo(struct ccp_dma_info *info)
{
return lower_32_bits(info->address + info->offset);
@@ -1391,18 +1365,21 @@ static int ccp_run_sha_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd)
if (sha->msg_bits)
return -EINVAL;
- /* A sha operation for a message with a total length of zero,
- * return known result.
+ /* The CCP cannot perform zero-length sha operations so the
+ * caller is required to buffer data for the final operation.
+ * However, a sha operation for a message with a total length
+ * of zero is valid so known values are required to supply
+ * the result.
*/
switch (sha->type) {
case CCP_SHA_TYPE_1:
- sha_zero = ccp_sha1_zero;
+ sha_zero = sha1_zero_message_hash;
break;
case CCP_SHA_TYPE_224:
- sha_zero = ccp_sha224_zero;
+ sha_zero = sha224_zero_message_hash;
break;
case CCP_SHA_TYPE_256:
- sha_zero = ccp_sha256_zero;
+ sha_zero = sha256_zero_message_hash;
break;
default:
return -EINVAL;
diff --git a/drivers/crypto/ccp/ccp-pci.c b/drivers/crypto/ccp/ccp-pci.c
index 6ade02f04f91..7690467c42f8 100644
--- a/drivers/crypto/ccp/ccp-pci.c
+++ b/drivers/crypto/ccp/ccp-pci.c
@@ -44,7 +44,7 @@ static int ccp_get_msix_irqs(struct ccp_device *ccp)
{
struct ccp_pci *ccp_pci = ccp->dev_specific;
struct device *dev = ccp->dev;
- struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
struct msix_entry msix_entry[MSIX_VECTORS];
unsigned int name_len = sizeof(ccp_pci->msix[0].name) - 1;
int v, ret;
@@ -86,7 +86,7 @@ e_irq:
static int ccp_get_msi_irq(struct ccp_device *ccp)
{
struct device *dev = ccp->dev;
- struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
int ret;
ret = pci_enable_msi(pdev);
@@ -133,7 +133,7 @@ static void ccp_free_irqs(struct ccp_device *ccp)
{
struct ccp_pci *ccp_pci = ccp->dev_specific;
struct device *dev = ccp->dev;
- struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
if (ccp_pci->msix_count) {
while (ccp_pci->msix_count--)
@@ -149,7 +149,7 @@ static void ccp_free_irqs(struct ccp_device *ccp)
static int ccp_find_mmio_area(struct ccp_device *ccp)
{
struct device *dev = ccp->dev;
- struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
resource_size_t io_len;
unsigned long io_flags;
diff --git a/drivers/crypto/ccp/ccp-platform.c b/drivers/crypto/ccp/ccp-platform.c
index 01b50cb4c982..66dd7c9d08c3 100644
--- a/drivers/crypto/ccp/ccp-platform.c
+++ b/drivers/crypto/ccp/ccp-platform.c
@@ -35,8 +35,7 @@ struct ccp_platform {
static int ccp_get_irq(struct ccp_device *ccp)
{
struct device *dev = ccp->dev;
- struct platform_device *pdev = container_of(dev,
- struct platform_device, dev);
+ struct platform_device *pdev = to_platform_device(dev);
int ret;
ret = platform_get_irq(pdev, 0);
@@ -78,8 +77,7 @@ static void ccp_free_irqs(struct ccp_device *ccp)
static struct resource *ccp_find_mmio_area(struct ccp_device *ccp)
{
struct device *dev = ccp->dev;
- struct platform_device *pdev = container_of(dev,
- struct platform_device, dev);
+ struct platform_device *pdev = to_platform_device(dev);
struct resource *ior;
ior = platform_get_resource(pdev, IORESOURCE_MEM, 0);
diff --git a/drivers/crypto/hifn_795x.c b/drivers/crypto/hifn_795x.c
index ca5c71ab4b4d..eee2c7e6c299 100644
--- a/drivers/crypto/hifn_795x.c
+++ b/drivers/crypto/hifn_795x.c
@@ -11,10 +11,6 @@
* 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/kernel.h>
@@ -36,14 +32,6 @@
#include <crypto/algapi.h>
#include <crypto/des.h>
-//#define HIFN_DEBUG
-
-#ifdef HIFN_DEBUG
-#define dprintk(f, a...) printk(f, ##a)
-#else
-#define dprintk(f, a...) do {} while (0)
-#endif
-
static char hifn_pll_ref[sizeof("extNNN")] = "ext";
module_param_string(hifn_pll_ref, hifn_pll_ref, sizeof(hifn_pll_ref), 0444);
MODULE_PARM_DESC(hifn_pll_ref,
@@ -79,12 +67,12 @@ static atomic_t hifn_dev_number;
/* DMA registres */
-#define HIFN_DMA_CRA 0x0C /* DMA Command Ring Address */
-#define HIFN_DMA_SDRA 0x1C /* DMA Source Data Ring Address */
+#define HIFN_DMA_CRA 0x0C /* DMA Command Ring Address */
+#define HIFN_DMA_SDRA 0x1C /* DMA Source Data Ring Address */
#define HIFN_DMA_RRA 0x2C /* DMA Result Ring Address */
#define HIFN_DMA_DDRA 0x3C /* DMA Destination Data Ring Address */
#define HIFN_DMA_STCTL 0x40 /* DMA Status and Control */
-#define HIFN_DMA_INTREN 0x44 /* DMA Interrupt Enable */
+#define HIFN_DMA_INTREN 0x44 /* DMA Interrupt Enable */
#define HIFN_DMA_CFG1 0x48 /* DMA Configuration #1 */
#define HIFN_DMA_CFG2 0x6C /* DMA Configuration #2 */
#define HIFN_CHIP_ID 0x98 /* Chip ID */
@@ -358,10 +346,10 @@ static atomic_t hifn_dev_number;
#define HIFN_NAMESIZE 32
#define HIFN_MAX_RESULT_ORDER 5
-#define HIFN_D_CMD_RSIZE 24*1
-#define HIFN_D_SRC_RSIZE 80*1
-#define HIFN_D_DST_RSIZE 80*1
-#define HIFN_D_RES_RSIZE 24*1
+#define HIFN_D_CMD_RSIZE (24 * 1)
+#define HIFN_D_SRC_RSIZE (80 * 1)
+#define HIFN_D_DST_RSIZE (80 * 1)
+#define HIFN_D_RES_RSIZE (24 * 1)
#define HIFN_D_DST_DALIGN 4
@@ -386,17 +374,16 @@ static atomic_t hifn_dev_number;
#define HIFN_MAX_RESULT (8 + 4 + 4 + 20 + 4)
#define HIFN_USED_RESULT 12
-struct hifn_desc
-{
+struct hifn_desc {
volatile __le32 l;
volatile __le32 p;
};
struct hifn_dma {
- struct hifn_desc cmdr[HIFN_D_CMD_RSIZE+1];
- struct hifn_desc srcr[HIFN_D_SRC_RSIZE+1];
- struct hifn_desc dstr[HIFN_D_DST_RSIZE+1];
- struct hifn_desc resr[HIFN_D_RES_RSIZE+1];
+ struct hifn_desc cmdr[HIFN_D_CMD_RSIZE + 1];
+ struct hifn_desc srcr[HIFN_D_SRC_RSIZE + 1];
+ struct hifn_desc dstr[HIFN_D_DST_RSIZE + 1];
+ struct hifn_desc resr[HIFN_D_RES_RSIZE + 1];
u8 command_bufs[HIFN_D_CMD_RSIZE][HIFN_MAX_COMMAND];
u8 result_bufs[HIFN_D_CMD_RSIZE][HIFN_MAX_RESULT];
@@ -410,16 +397,15 @@ struct hifn_dma {
int cmdk, srck, dstk, resk;
};
-#define HIFN_FLAG_CMD_BUSY (1<<0)
-#define HIFN_FLAG_SRC_BUSY (1<<1)
-#define HIFN_FLAG_DST_BUSY (1<<2)
-#define HIFN_FLAG_RES_BUSY (1<<3)
-#define HIFN_FLAG_OLD_KEY (1<<4)
+#define HIFN_FLAG_CMD_BUSY (1 << 0)
+#define HIFN_FLAG_SRC_BUSY (1 << 1)
+#define HIFN_FLAG_DST_BUSY (1 << 2)
+#define HIFN_FLAG_RES_BUSY (1 << 3)
+#define HIFN_FLAG_OLD_KEY (1 << 4)
#define HIFN_DEFAULT_ACTIVE_NUM 5
-struct hifn_device
-{
+struct hifn_device {
char name[HIFN_NAMESIZE];
int irq;
@@ -432,7 +418,7 @@ struct hifn_device
u32 dmareg;
- void *sa[HIFN_D_RES_RSIZE];
+ void *sa[HIFN_D_RES_RSIZE];
spinlock_t lock;
@@ -447,7 +433,7 @@ struct hifn_device
struct tasklet_struct tasklet;
- struct crypto_queue queue;
+ struct crypto_queue queue;
struct list_head alg_list;
unsigned int pk_clk_freq;
@@ -468,8 +454,7 @@ struct hifn_device
#define HIFN_D_JUMP 0x40000000
#define HIFN_D_VALID 0x80000000
-struct hifn_base_command
-{
+struct hifn_base_command {
volatile __le16 masks;
volatile __le16 session_num;
volatile __le16 total_source_count;
@@ -491,12 +476,11 @@ struct hifn_base_command
/*
* Structure to help build up the command data structure.
*/
-struct hifn_crypt_command
-{
- volatile __le16 masks;
- volatile __le16 header_skip;
- volatile __le16 source_count;
- volatile __le16 reserved;
+struct hifn_crypt_command {
+ volatile __le16 masks;
+ volatile __le16 header_skip;
+ volatile __le16 source_count;
+ volatile __le16 reserved;
};
#define HIFN_CRYPT_CMD_ALG_MASK 0x0003 /* algorithm: */
@@ -522,12 +506,11 @@ struct hifn_crypt_command
/*
* Structure to help build up the command data structure.
*/
-struct hifn_mac_command
-{
- volatile __le16 masks;
- volatile __le16 header_skip;
- volatile __le16 source_count;
- volatile __le16 reserved;
+struct hifn_mac_command {
+ volatile __le16 masks;
+ volatile __le16 header_skip;
+ volatile __le16 source_count;
+ volatile __le16 reserved;
};
#define HIFN_MAC_CMD_ALG_MASK 0x0001
@@ -551,12 +534,11 @@ struct hifn_mac_command
#define HIFN_MAC_CMD_POS_IPSEC 0x0200
#define HIFN_MAC_CMD_NEW_KEY 0x0800
-struct hifn_comp_command
-{
- volatile __le16 masks;
- volatile __le16 header_skip;
- volatile __le16 source_count;
- volatile __le16 reserved;
+struct hifn_comp_command {
+ volatile __le16 masks;
+ volatile __le16 header_skip;
+ volatile __le16 source_count;
+ volatile __le16 reserved;
};
#define HIFN_COMP_CMD_SRCLEN_M 0xc000
@@ -570,12 +552,11 @@ struct hifn_comp_command
#define HIFN_COMP_CMD_ALG_MPPC 0x0001 /* MPPC */
#define HIFN_COMP_CMD_ALG_LZS 0x0000 /* LZS */
-struct hifn_base_result
-{
- volatile __le16 flags;
- volatile __le16 session;
- volatile __le16 src_cnt; /* 15:0 of source count */
- volatile __le16 dst_cnt; /* 15:0 of dest count */
+struct hifn_base_result {
+ volatile __le16 flags;
+ volatile __le16 session;
+ volatile __le16 src_cnt; /* 15:0 of source count */
+ volatile __le16 dst_cnt; /* 15:0 of dest count */
};
#define HIFN_BASE_RES_DSTOVERRUN 0x0200 /* destination overrun */
@@ -584,8 +565,7 @@ struct hifn_base_result
#define HIFN_BASE_RES_DSTLEN_M 0x3000 /* 17:16 of dest count */
#define HIFN_BASE_RES_DSTLEN_S 12
-struct hifn_comp_result
-{
+struct hifn_comp_result {
volatile __le16 flags;
volatile __le16 crc;
};
@@ -596,18 +576,16 @@ struct hifn_comp_result
#define HIFN_COMP_RES_ENDMARKER 0x0002 /* LZS: end marker seen */
#define HIFN_COMP_RES_SRC_NOTZERO 0x0001 /* source expired */
-struct hifn_mac_result
-{
- volatile __le16 flags;
- volatile __le16 reserved;
+struct hifn_mac_result {
+ volatile __le16 flags;
+ volatile __le16 reserved;
/* followed by 0, 6, 8, or 10 u16's of the MAC, then crypt */
};
#define HIFN_MAC_RES_MISCOMPARE 0x0002 /* compare failed */
#define HIFN_MAC_RES_SRC_NOTZERO 0x0001 /* source expired */
-struct hifn_crypt_result
-{
+struct hifn_crypt_result {
volatile __le16 flags;
volatile __le16 reserved;
};
@@ -622,11 +600,10 @@ struct hifn_crypt_result
#define HIFN_POLL_SCALAR 0x0
#endif
-#define HIFN_MAX_SEGLEN 0xffff /* maximum dma segment len */
+#define HIFN_MAX_SEGLEN 0xffff /* maximum dma segment len */
#define HIFN_MAX_DMALEN 0x3ffff /* maximum dma length */
-struct hifn_crypto_alg
-{
+struct hifn_crypto_alg {
struct list_head entry;
struct crypto_alg alg;
struct hifn_device *dev;
@@ -634,24 +611,21 @@ struct hifn_crypto_alg
#define ASYNC_SCATTERLIST_CACHE 16
-#define ASYNC_FLAGS_MISALIGNED (1<<0)
+#define ASYNC_FLAGS_MISALIGNED (1 << 0)
-struct hifn_cipher_walk
-{
+struct hifn_cipher_walk {
struct scatterlist cache[ASYNC_SCATTERLIST_CACHE];
u32 flags;
int num;
};
-struct hifn_context
-{
+struct hifn_context {
u8 key[HIFN_MAX_CRYPT_KEY_LENGTH];
struct hifn_device *dev;
unsigned int keysize;
};
-struct hifn_request_context
-{
+struct hifn_request_context {
u8 *iv;
unsigned int ivsize;
u8 op, type, mode, unused;
@@ -693,7 +667,7 @@ static void hifn_wait_puc(struct hifn_device *dev)
int i;
u32 ret;
- for (i=10000; i > 0; --i) {
+ for (i = 10000; i > 0; --i) {
ret = hifn_read_0(dev, HIFN_0_PUCTRL);
if (!(ret & HIFN_PUCTRL_RESET))
break;
@@ -702,7 +676,7 @@ static void hifn_wait_puc(struct hifn_device *dev)
}
if (!i)
- dprintk("%s: Failed to reset PUC unit.\n", dev->name);
+ dev_err(&dev->pdev->dev, "Failed to reset PUC unit.\n");
}
static void hifn_reset_puc(struct hifn_device *dev)
@@ -749,13 +723,12 @@ static void hifn_reset_dma(struct hifn_device *dev, int full)
hifn_reset_puc(dev);
}
-static u32 hifn_next_signature(u_int32_t a, u_int cnt)
+static u32 hifn_next_signature(u32 a, u_int cnt)
{
int i;
u32 v;
for (i = 0; i < cnt; i++) {
-
/* get the parity */
v = a & 0x80080125;
v ^= v >> 16;
@@ -846,33 +819,28 @@ static int hifn_init_pubrng(struct hifn_device *dev)
hifn_write_1(dev, HIFN_1_PUB_RESET, hifn_read_1(dev, HIFN_1_PUB_RESET) |
HIFN_PUBRST_RESET);
- for (i=100; i > 0; --i) {
+ for (i = 100; i > 0; --i) {
mdelay(1);
if ((hifn_read_1(dev, HIFN_1_PUB_RESET) & HIFN_PUBRST_RESET) == 0)
break;
}
- if (!i)
- dprintk("Chip %s: Failed to initialise public key engine.\n",
- dev->name);
- else {
+ if (!i) {
+ dev_err(&dev->pdev->dev, "Failed to initialise public key engine.\n");
+ } else {
hifn_write_1(dev, HIFN_1_PUB_IEN, HIFN_PUBIEN_DONE);
dev->dmareg |= HIFN_DMAIER_PUBDONE;
hifn_write_1(dev, HIFN_1_DMA_IER, dev->dmareg);
- dprintk("Chip %s: Public key engine has been successfully "
- "initialised.\n", dev->name);
+ dev_dbg(&dev->pdev->dev, "Public key engine has been successfully initialised.\n");
}
- /*
- * Enable RNG engine.
- */
+ /* Enable RNG engine. */
hifn_write_1(dev, HIFN_1_RNG_CONFIG,
hifn_read_1(dev, HIFN_1_RNG_CONFIG) | HIFN_RNGCFG_ENA);
- dprintk("Chip %s: RNG engine has been successfully initialised.\n",
- dev->name);
+ dev_dbg(&dev->pdev->dev, "RNG engine has been successfully initialised.\n");
#ifdef CONFIG_CRYPTO_DEV_HIFN_795X_RNG
/* First value must be discarded */
@@ -896,8 +864,8 @@ static int hifn_enable_crypto(struct hifn_device *dev)
}
}
- if (offtbl == NULL) {
- dprintk("Chip %s: Unknown card!\n", dev->name);
+ if (!offtbl) {
+ dev_err(&dev->pdev->dev, "Unknown card!\n");
return -ENODEV;
}
@@ -912,7 +880,7 @@ static int hifn_enable_crypto(struct hifn_device *dev)
hifn_write_1(dev, HIFN_1_UNLOCK_SECRET2, 0);
mdelay(1);
- for (i=0; i<12; ++i) {
+ for (i = 0; i < 12; ++i) {
addr = hifn_next_signature(addr, offtbl[i] + 0x101);
hifn_write_1(dev, HIFN_1_UNLOCK_SECRET2, addr);
@@ -920,7 +888,7 @@ static int hifn_enable_crypto(struct hifn_device *dev)
}
hifn_write_1(dev, HIFN_1_DMA_CNFG, dmacfg);
- dprintk("Chip %s: %s.\n", dev->name, pci_name(dev->pdev));
+ dev_dbg(&dev->pdev->dev, "%s %s.\n", dev->name, pci_name(dev->pdev));
return 0;
}
@@ -931,16 +899,14 @@ static void hifn_init_dma(struct hifn_device *dev)
u32 dptr = dev->desc_dma;
int i;
- for (i=0; i<HIFN_D_CMD_RSIZE; ++i)
+ for (i = 0; i < HIFN_D_CMD_RSIZE; ++i)
dma->cmdr[i].p = __cpu_to_le32(dptr +
offsetof(struct hifn_dma, command_bufs[i][0]));
- for (i=0; i<HIFN_D_RES_RSIZE; ++i)
+ for (i = 0; i < HIFN_D_RES_RSIZE; ++i)
dma->resr[i].p = __cpu_to_le32(dptr +
offsetof(struct hifn_dma, result_bufs[i][0]));
- /*
- * Setup LAST descriptors.
- */
+ /* Setup LAST descriptors. */
dma->cmdr[HIFN_D_CMD_RSIZE].p = __cpu_to_le32(dptr +
offsetof(struct hifn_dma, cmdr[0]));
dma->srcr[HIFN_D_SRC_RSIZE].p = __cpu_to_le32(dptr +
@@ -960,7 +926,7 @@ static void hifn_init_dma(struct hifn_device *dev)
* to calculate the optimal multiplier. For PCI we assume 66MHz, since that
* allows us to operate without the risk of overclocking the chip. If it
* actually uses 33MHz, the chip will operate at half the speed, this can be
- * overriden by specifying the frequency as module parameter (pci33).
+ * overridden by specifying the frequency as module parameter (pci33).
*
* Unfortunately the PCI clock is not very suitable since the HIFN needs a
* stable clock and the PCI clock frequency may vary, so the default is the
@@ -984,9 +950,8 @@ static void hifn_init_pll(struct hifn_device *dev)
freq = simple_strtoul(hifn_pll_ref + 3, NULL, 10);
else {
freq = 66;
- printk(KERN_INFO "hifn795x: assuming %uMHz clock speed, "
- "override with hifn_pll_ref=%.3s<frequency>\n",
- freq, hifn_pll_ref);
+ dev_info(&dev->pdev->dev, "assuming %uMHz clock speed, override with hifn_pll_ref=%.3s<frequency>\n",
+ freq, hifn_pll_ref);
}
m = HIFN_PLL_FCK_MAX / freq;
@@ -1174,17 +1139,17 @@ static int hifn_setup_cmd_desc(struct hifn_device *dev,
mask = 0;
switch (rctx->op) {
- case ACRYPTO_OP_DECRYPT:
- mask = HIFN_BASE_CMD_CRYPT | HIFN_BASE_CMD_DECODE;
- break;
- case ACRYPTO_OP_ENCRYPT:
- mask = HIFN_BASE_CMD_CRYPT;
- break;
- case ACRYPTO_OP_HMAC:
- mask = HIFN_BASE_CMD_MAC;
- break;
- default:
- goto err_out;
+ case ACRYPTO_OP_DECRYPT:
+ mask = HIFN_BASE_CMD_CRYPT | HIFN_BASE_CMD_DECODE;
+ break;
+ case ACRYPTO_OP_ENCRYPT:
+ mask = HIFN_BASE_CMD_CRYPT;
+ break;
+ case ACRYPTO_OP_HMAC:
+ mask = HIFN_BASE_CMD_MAC;
+ break;
+ default:
+ goto err_out;
}
buf_pos += hifn_setup_base_command(dev, buf_pos, nbytes,
@@ -1199,53 +1164,53 @@ static int hifn_setup_cmd_desc(struct hifn_device *dev,
md |= HIFN_CRYPT_CMD_NEW_IV;
switch (rctx->mode) {
- case ACRYPTO_MODE_ECB:
- md |= HIFN_CRYPT_CMD_MODE_ECB;
- break;
- case ACRYPTO_MODE_CBC:
- md |= HIFN_CRYPT_CMD_MODE_CBC;
- break;
- case ACRYPTO_MODE_CFB:
- md |= HIFN_CRYPT_CMD_MODE_CFB;
- break;
- case ACRYPTO_MODE_OFB:
- md |= HIFN_CRYPT_CMD_MODE_OFB;
- break;
- default:
- goto err_out;
+ case ACRYPTO_MODE_ECB:
+ md |= HIFN_CRYPT_CMD_MODE_ECB;
+ break;
+ case ACRYPTO_MODE_CBC:
+ md |= HIFN_CRYPT_CMD_MODE_CBC;
+ break;
+ case ACRYPTO_MODE_CFB:
+ md |= HIFN_CRYPT_CMD_MODE_CFB;
+ break;
+ case ACRYPTO_MODE_OFB:
+ md |= HIFN_CRYPT_CMD_MODE_OFB;
+ break;
+ default:
+ goto err_out;
}
switch (rctx->type) {
- case ACRYPTO_TYPE_AES_128:
- if (ctx->keysize != 16)
- goto err_out;
- md |= HIFN_CRYPT_CMD_KSZ_128 |
- HIFN_CRYPT_CMD_ALG_AES;
- break;
- case ACRYPTO_TYPE_AES_192:
- if (ctx->keysize != 24)
- goto err_out;
- md |= HIFN_CRYPT_CMD_KSZ_192 |
- HIFN_CRYPT_CMD_ALG_AES;
- break;
- case ACRYPTO_TYPE_AES_256:
- if (ctx->keysize != 32)
- goto err_out;
- md |= HIFN_CRYPT_CMD_KSZ_256 |
- HIFN_CRYPT_CMD_ALG_AES;
- break;
- case ACRYPTO_TYPE_3DES:
- if (ctx->keysize != 24)
- goto err_out;
- md |= HIFN_CRYPT_CMD_ALG_3DES;
- break;
- case ACRYPTO_TYPE_DES:
- if (ctx->keysize != 8)
- goto err_out;
- md |= HIFN_CRYPT_CMD_ALG_DES;
- break;
- default:
+ case ACRYPTO_TYPE_AES_128:
+ if (ctx->keysize != 16)
+ goto err_out;
+ md |= HIFN_CRYPT_CMD_KSZ_128 |
+ HIFN_CRYPT_CMD_ALG_AES;
+ break;
+ case ACRYPTO_TYPE_AES_192:
+ if (ctx->keysize != 24)
goto err_out;
+ md |= HIFN_CRYPT_CMD_KSZ_192 |
+ HIFN_CRYPT_CMD_ALG_AES;
+ break;
+ case ACRYPTO_TYPE_AES_256:
+ if (ctx->keysize != 32)
+ goto err_out;
+ md |= HIFN_CRYPT_CMD_KSZ_256 |
+ HIFN_CRYPT_CMD_ALG_AES;
+ break;
+ case ACRYPTO_TYPE_3DES:
+ if (ctx->keysize != 24)
+ goto err_out;
+ md |= HIFN_CRYPT_CMD_ALG_3DES;
+ break;
+ case ACRYPTO_TYPE_DES:
+ if (ctx->keysize != 8)
+ goto err_out;
+ md |= HIFN_CRYPT_CMD_ALG_DES;
+ break;
+ default:
+ goto err_out;
}
buf_pos += hifn_setup_crypto_command(dev, buf_pos,
@@ -1265,8 +1230,9 @@ static int hifn_setup_cmd_desc(struct hifn_device *dev,
HIFN_D_VALID | HIFN_D_LAST |
HIFN_D_MASKDONEIRQ | HIFN_D_JUMP);
dma->cmdi = 0;
- } else
- dma->cmdr[dma->cmdi-1].l |= __cpu_to_le32(HIFN_D_VALID);
+ } else {
+ dma->cmdr[dma->cmdi - 1].l |= __cpu_to_le32(HIFN_D_VALID);
+ }
if (!(dev->flags & HIFN_FLAG_CMD_BUSY)) {
hifn_write_1(dev, HIFN_1_DMA_CSR, HIFN_DMACSR_C_CTRL_ENA);
@@ -1424,7 +1390,7 @@ static int hifn_cipher_walk_init(struct hifn_cipher_walk *w,
sg_init_table(w->cache, num);
w->num = 0;
- for (i=0; i<num; ++i) {
+ for (i = 0; i < num; ++i) {
struct page *page = alloc_page(gfp_flags);
struct scatterlist *s;
@@ -1444,7 +1410,7 @@ static void hifn_cipher_walk_exit(struct hifn_cipher_walk *w)
{
int i;
- for (i=0; i<w->num; ++i) {
+ for (i = 0; i < w->num; ++i) {
struct scatterlist *s = &w->cache[i];
__free_page(sg_page(s));
@@ -1471,8 +1437,8 @@ static int ablkcipher_add(unsigned int *drestp, struct scatterlist *dst,
drest -= copy;
nbytes -= copy;
- dprintk("%s: copy: %u, size: %u, drest: %u, nbytes: %u.\n",
- __func__, copy, size, drest, nbytes);
+ pr_debug("%s: copy: %u, size: %u, drest: %u, nbytes: %u.\n",
+ __func__, copy, size, drest, nbytes);
dst++;
idx++;
@@ -1499,8 +1465,8 @@ static int hifn_cipher_walk(struct ablkcipher_request *req,
dst = &req->dst[idx];
- dprintk("\n%s: dlen: %u, doff: %u, offset: %u, nbytes: %u.\n",
- __func__, dst->length, dst->offset, offset, nbytes);
+ pr_debug("\n%s: dlen: %u, doff: %u, offset: %u, nbytes: %u.\n",
+ __func__, dst->length, dst->offset, offset, nbytes);
if (!IS_ALIGNED(dst->offset, HIFN_D_DST_DALIGN) ||
!IS_ALIGNED(dst->length, HIFN_D_DST_DALIGN) ||
@@ -1525,10 +1491,10 @@ static int hifn_cipher_walk(struct ablkcipher_request *req,
* to put there additional blocksized chunk,
* so we mark that page as containing only
* blocksize aligned chunks:
- * t->length = (slen & ~(HIFN_D_DST_DALIGN - 1));
+ * t->length = (slen & ~(HIFN_D_DST_DALIGN - 1));
* and increase number of bytes to be processed
* in next chunk:
- * nbytes += diff;
+ * nbytes += diff;
*/
nbytes += diff;
@@ -1536,14 +1502,13 @@ static int hifn_cipher_walk(struct ablkcipher_request *req,
* Temporary of course...
* Kick author if you will catch this one.
*/
- printk(KERN_ERR "%s: dlen: %u, nbytes: %u,"
- "slen: %u, offset: %u.\n",
- __func__, dlen, nbytes, slen, offset);
- printk(KERN_ERR "%s: please contact author to fix this "
- "issue, generally you should not catch "
- "this path under any condition but who "
- "knows how did you use crypto code.\n"
- "Thank you.\n", __func__);
+ pr_err("%s: dlen: %u, nbytes: %u, slen: %u, offset: %u.\n",
+ __func__, dlen, nbytes, slen, offset);
+ pr_err("%s: please contact author to fix this "
+ "issue, generally you should not catch "
+ "this path under any condition but who "
+ "knows how did you use crypto code.\n"
+ "Thank you.\n", __func__);
BUG();
} else {
copy += diff + nbytes;
@@ -1630,70 +1595,16 @@ err_out:
spin_unlock_irqrestore(&dev->lock, flags);
err_out_exit:
if (err) {
- printk("%s: iv: %p [%d], key: %p [%d], mode: %u, op: %u, "
- "type: %u, err: %d.\n",
- dev->name, rctx->iv, rctx->ivsize,
- ctx->key, ctx->keysize,
- rctx->mode, rctx->op, rctx->type, err);
+ dev_info(&dev->pdev->dev, "iv: %p [%d], key: %p [%d], mode: %u, op: %u, "
+ "type: %u, err: %d.\n",
+ rctx->iv, rctx->ivsize,
+ ctx->key, ctx->keysize,
+ rctx->mode, rctx->op, rctx->type, err);
}
return err;
}
-static int hifn_test(struct hifn_device *dev, int encdec, u8 snum)
-{
- int n, err;
- u8 src[16];
- struct hifn_context ctx;
- struct hifn_request_context rctx;
- u8 fips_aes_ecb_from_zero[16] = {
- 0x66, 0xE9, 0x4B, 0xD4,
- 0xEF, 0x8A, 0x2C, 0x3B,
- 0x88, 0x4C, 0xFA, 0x59,
- 0xCA, 0x34, 0x2B, 0x2E};
- struct scatterlist sg;
-
- memset(src, 0, sizeof(src));
- memset(ctx.key, 0, sizeof(ctx.key));
-
- ctx.dev = dev;
- ctx.keysize = 16;
- rctx.ivsize = 0;
- rctx.iv = NULL;
- rctx.op = (encdec)?ACRYPTO_OP_ENCRYPT:ACRYPTO_OP_DECRYPT;
- rctx.mode = ACRYPTO_MODE_ECB;
- rctx.type = ACRYPTO_TYPE_AES_128;
- rctx.walk.cache[0].length = 0;
-
- sg_init_one(&sg, &src, sizeof(src));
-
- err = hifn_setup_dma(dev, &ctx, &rctx, &sg, &sg, sizeof(src), NULL);
- if (err)
- goto err_out;
-
- dev->started = 0;
- msleep(200);
-
- dprintk("%s: decoded: ", dev->name);
- for (n=0; n<sizeof(src); ++n)
- dprintk("%02x ", src[n]);
- dprintk("\n");
- dprintk("%s: FIPS : ", dev->name);
- for (n=0; n<sizeof(fips_aes_ecb_from_zero); ++n)
- dprintk("%02x ", fips_aes_ecb_from_zero[n]);
- dprintk("\n");
-
- if (!memcmp(src, fips_aes_ecb_from_zero, sizeof(fips_aes_ecb_from_zero))) {
- printk(KERN_INFO "%s: AES 128 ECB test has been successfully "
- "passed.\n", dev->name);
- return 0;
- }
-
-err_out:
- printk(KERN_INFO "%s: AES 128 ECB test has been failed.\n", dev->name);
- return -1;
-}
-
static int hifn_start_device(struct hifn_device *dev)
{
int err;
@@ -1739,8 +1650,8 @@ static int ablkcipher_get(void *saddr, unsigned int *srestp, unsigned int offset
saddr += copy;
offset = 0;
- dprintk("%s: copy: %u, size: %u, srest: %u, nbytes: %u.\n",
- __func__, copy, size, srest, nbytes);
+ pr_debug("%s: copy: %u, size: %u, srest: %u, nbytes: %u.\n",
+ __func__, copy, size, srest, nbytes);
dst++;
idx++;
@@ -1760,7 +1671,8 @@ static inline void hifn_complete_sa(struct hifn_device *dev, int i)
dev->sa[i] = NULL;
dev->started--;
if (dev->started < 0)
- printk("%s: started: %d.\n", __func__, dev->started);
+ dev_info(&dev->pdev->dev, "%s: started: %d.\n", __func__,
+ dev->started);
spin_unlock_irqrestore(&dev->lock, flags);
BUG_ON(dev->started < 0);
}
@@ -1779,7 +1691,7 @@ static void hifn_process_ready(struct ablkcipher_request *req, int error)
t = &rctx->walk.cache[idx];
dst = &req->dst[idx];
- dprintk("\n%s: sg_page(t): %p, t->length: %u, "
+ pr_debug("\n%s: sg_page(t): %p, t->length: %u, "
"sg_page(dst): %p, dst->length: %u, "
"nbytes: %u.\n",
__func__, sg_page(t), t->length,
@@ -1815,9 +1727,8 @@ static void hifn_clear_rings(struct hifn_device *dev, int error)
struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
int i, u;
- dprintk("%s: ring cleanup 1: i: %d.%d.%d.%d, u: %d.%d.%d.%d, "
+ dev_dbg(&dev->pdev->dev, "ring cleanup 1: i: %d.%d.%d.%d, u: %d.%d.%d.%d, "
"k: %d.%d.%d.%d.\n",
- dev->name,
dma->cmdi, dma->srci, dma->dsti, dma->resi,
dma->cmdu, dma->srcu, dma->dstu, dma->resu,
dma->cmdk, dma->srck, dma->dstk, dma->resk);
@@ -1870,9 +1781,8 @@ static void hifn_clear_rings(struct hifn_device *dev, int error)
}
dma->dstk = i; dma->dstu = u;
- dprintk("%s: ring cleanup 2: i: %d.%d.%d.%d, u: %d.%d.%d.%d, "
+ dev_dbg(&dev->pdev->dev, "ring cleanup 2: i: %d.%d.%d.%d, u: %d.%d.%d.%d, "
"k: %d.%d.%d.%d.\n",
- dev->name,
dma->cmdi, dma->srci, dma->dsti, dma->resi,
dma->cmdu, dma->srcu, dma->dstu, dma->resu,
dma->cmdk, dma->srck, dma->dstk, dma->resk);
@@ -1921,21 +1831,22 @@ static void hifn_work(struct work_struct *work)
int i;
struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
- printk("%s: r: %08x, active: %d, started: %d, "
- "success: %lu: qlen: %u/%u, reset: %d.\n",
- dev->name, r, dev->active, dev->started,
- dev->success, dev->queue.qlen, dev->queue.max_qlen,
- reset);
+ dev_info(&dev->pdev->dev,
+ "r: %08x, active: %d, started: %d, "
+ "success: %lu: qlen: %u/%u, reset: %d.\n",
+ r, dev->active, dev->started,
+ dev->success, dev->queue.qlen, dev->queue.max_qlen,
+ reset);
- printk("%s: res: ", __func__);
- for (i=0; i<HIFN_D_RES_RSIZE; ++i) {
- printk("%x.%p ", dma->resr[i].l, dev->sa[i]);
+ dev_info(&dev->pdev->dev, "%s: res: ", __func__);
+ for (i = 0; i < HIFN_D_RES_RSIZE; ++i) {
+ pr_info("%x.%p ", dma->resr[i].l, dev->sa[i]);
if (dev->sa[i]) {
hifn_process_ready(dev->sa[i], -ENODEV);
hifn_complete_sa(dev, i);
}
}
- printk("\n");
+ pr_info("\n");
hifn_reset_dma(dev, 1);
hifn_stop_device(dev);
@@ -1957,9 +1868,9 @@ static irqreturn_t hifn_interrupt(int irq, void *data)
dmacsr = hifn_read_1(dev, HIFN_1_DMA_CSR);
- dprintk("%s: 1 dmacsr: %08x, dmareg: %08x, res: %08x [%d], "
+ dev_dbg(&dev->pdev->dev, "1 dmacsr: %08x, dmareg: %08x, res: %08x [%d], "
"i: %d.%d.%d.%d, u: %d.%d.%d.%d.\n",
- dev->name, dmacsr, dev->dmareg, dmacsr & dev->dmareg, dma->cmdi,
+ dmacsr, dev->dmareg, dmacsr & dev->dmareg, dma->cmdi,
dma->cmdi, dma->srci, dma->dsti, dma->resi,
dma->cmdu, dma->srcu, dma->dstu, dma->resu);
@@ -1978,9 +1889,9 @@ static irqreturn_t hifn_interrupt(int irq, void *data)
if (restart) {
u32 puisr = hifn_read_0(dev, HIFN_0_PUISR);
- printk(KERN_WARNING "%s: overflow: r: %d, d: %d, puisr: %08x, d: %u.\n",
- dev->name, !!(dmacsr & HIFN_DMACSR_R_OVER),
- !!(dmacsr & HIFN_DMACSR_D_OVER),
+ dev_warn(&dev->pdev->dev, "overflow: r: %d, d: %d, puisr: %08x, d: %u.\n",
+ !!(dmacsr & HIFN_DMACSR_R_OVER),
+ !!(dmacsr & HIFN_DMACSR_D_OVER),
puisr, !!(puisr & HIFN_PUISR_DSTOVER));
if (!!(puisr & HIFN_PUISR_DSTOVER))
hifn_write_0(dev, HIFN_0_PUISR, HIFN_PUISR_DSTOVER);
@@ -1991,18 +1902,18 @@ static irqreturn_t hifn_interrupt(int irq, void *data)
restart = dmacsr & (HIFN_DMACSR_C_ABORT | HIFN_DMACSR_S_ABORT |
HIFN_DMACSR_D_ABORT | HIFN_DMACSR_R_ABORT);
if (restart) {
- printk(KERN_WARNING "%s: abort: c: %d, s: %d, d: %d, r: %d.\n",
- dev->name, !!(dmacsr & HIFN_DMACSR_C_ABORT),
- !!(dmacsr & HIFN_DMACSR_S_ABORT),
- !!(dmacsr & HIFN_DMACSR_D_ABORT),
- !!(dmacsr & HIFN_DMACSR_R_ABORT));
+ dev_warn(&dev->pdev->dev, "abort: c: %d, s: %d, d: %d, r: %d.\n",
+ !!(dmacsr & HIFN_DMACSR_C_ABORT),
+ !!(dmacsr & HIFN_DMACSR_S_ABORT),
+ !!(dmacsr & HIFN_DMACSR_D_ABORT),
+ !!(dmacsr & HIFN_DMACSR_R_ABORT));
hifn_reset_dma(dev, 1);
hifn_init_dma(dev);
hifn_init_registers(dev);
}
if ((dmacsr & HIFN_DMACSR_C_WAIT) && (dma->cmdu == 0)) {
- dprintk("%s: wait on command.\n", dev->name);
+ dev_dbg(&dev->pdev->dev, "wait on command.\n");
dev->dmareg &= ~(HIFN_DMAIER_C_WAIT);
hifn_write_1(dev, HIFN_1_DMA_IER, dev->dmareg);
}
@@ -2020,19 +1931,19 @@ static void hifn_flush(struct hifn_device *dev)
struct hifn_dma *dma = (struct hifn_dma *)dev->desc_virt;
int i;
- for (i=0; i<HIFN_D_RES_RSIZE; ++i) {
+ for (i = 0; i < HIFN_D_RES_RSIZE; ++i) {
struct hifn_desc *d = &dma->resr[i];
if (dev->sa[i]) {
hifn_process_ready(dev->sa[i],
- (d->l & __cpu_to_le32(HIFN_D_VALID))?-ENODEV:0);
+ (d->l & __cpu_to_le32(HIFN_D_VALID)) ? -ENODEV : 0);
hifn_complete_sa(dev, i);
}
}
spin_lock_irqsave(&dev->lock, flags);
while ((async_req = crypto_dequeue_request(&dev->queue))) {
- req = container_of(async_req, struct ablkcipher_request, base);
+ req = ablkcipher_request_cast(async_req);
spin_unlock_irqrestore(&dev->lock, flags);
hifn_process_ready(req, -ENODEV);
@@ -2057,7 +1968,7 @@ static int hifn_setkey(struct crypto_ablkcipher *cipher, const u8 *key,
if (len == HIFN_DES_KEY_LENGTH) {
u32 tmp[DES_EXPKEY_WORDS];
int ret = des_ekey(tmp, key);
-
+
if (unlikely(ret == 0) && (tfm->crt_flags & CRYPTO_TFM_REQ_WEAK_KEY)) {
tfm->crt_flags |= CRYPTO_TFM_RES_WEAK_KEY;
return -EINVAL;
@@ -2151,7 +2062,7 @@ static int hifn_process_queue(struct hifn_device *dev)
if (backlog)
backlog->complete(backlog, -EINPROGRESS);
- req = container_of(async_req, struct ablkcipher_request, base);
+ req = ablkcipher_request_cast(async_req);
err = hifn_handle_req(req);
if (err)
@@ -2298,9 +2209,7 @@ static inline int hifn_encrypt_3des_ofb(struct ablkcipher_request *req)
ACRYPTO_TYPE_3DES, ACRYPTO_MODE_OFB);
}
-/*
- * 3DES decryption functions.
- */
+/* 3DES decryption functions. */
static inline int hifn_decrypt_3des_ecb(struct ablkcipher_request *req)
{
return hifn_setup_crypto(req, ACRYPTO_OP_DECRYPT,
@@ -2322,8 +2231,7 @@ static inline int hifn_decrypt_3des_ofb(struct ablkcipher_request *req)
ACRYPTO_TYPE_3DES, ACRYPTO_MODE_OFB);
}
-struct hifn_alg_template
-{
+struct hifn_alg_template {
char name[CRYPTO_MAX_ALG_NAME];
char drv_name[CRYPTO_MAX_ALG_NAME];
unsigned int bsize;
@@ -2483,7 +2391,7 @@ static int hifn_alg_alloc(struct hifn_device *dev, struct hifn_alg_template *t)
struct hifn_crypto_alg *alg;
int err;
- alg = kzalloc(sizeof(struct hifn_crypto_alg), GFP_KERNEL);
+ alg = kzalloc(sizeof(*alg), GFP_KERNEL);
if (!alg)
return -ENOMEM;
@@ -2530,7 +2438,7 @@ static int hifn_register_alg(struct hifn_device *dev)
{
int i, err;
- for (i=0; i<ARRAY_SIZE(hifn_alg_templates); ++i) {
+ for (i = 0; i < ARRAY_SIZE(hifn_alg_templates); ++i) {
err = hifn_alg_alloc(dev, &hifn_alg_templates[i]);
if (err)
goto err_out_exit;
@@ -2575,7 +2483,7 @@ static int hifn_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto err_out_disable_pci_device;
snprintf(name, sizeof(name), "hifn%d",
- atomic_inc_return(&hifn_dev_number)-1);
+ atomic_inc_return(&hifn_dev_number) - 1);
err = pci_request_regions(pdev, name);
if (err)
@@ -2584,8 +2492,7 @@ static int hifn_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (pci_resource_len(pdev, 0) < HIFN_BAR0_SIZE ||
pci_resource_len(pdev, 1) < HIFN_BAR1_SIZE ||
pci_resource_len(pdev, 2) < HIFN_BAR2_SIZE) {
- dprintk("%s: Broken hardware - I/O regions are too small.\n",
- pci_name(pdev));
+ dev_err(&pdev->dev, "Broken hardware - I/O regions are too small.\n");
err = -ENODEV;
goto err_out_free_regions;
}
@@ -2602,7 +2509,7 @@ static int hifn_probe(struct pci_dev *pdev, const struct pci_device_id *id)
snprintf(dev->name, sizeof(dev->name), "%s", name);
spin_lock_init(&dev->lock);
- for (i=0; i<3; ++i) {
+ for (i = 0; i < 3; ++i) {
unsigned long addr, size;
addr = pci_resource_start(pdev, i);
@@ -2618,7 +2525,7 @@ static int hifn_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dev->desc_virt = pci_zalloc_consistent(pdev, sizeof(struct hifn_dma),
&dev->desc_dma);
if (!dev->desc_virt) {
- dprintk("Failed to allocate descriptor rings.\n");
+ dev_err(&pdev->dev, "Failed to allocate descriptor rings.\n");
err = -ENOMEM;
goto err_out_unmap_bars;
}
@@ -2626,7 +2533,7 @@ static int hifn_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dev->pdev = pdev;
dev->irq = pdev->irq;
- for (i=0; i<HIFN_D_RES_RSIZE; ++i)
+ for (i = 0; i < HIFN_D_RES_RSIZE; ++i)
dev->sa[i] = NULL;
pci_set_drvdata(pdev, dev);
@@ -2637,7 +2544,8 @@ static int hifn_probe(struct pci_dev *pdev, const struct pci_device_id *id)
err = request_irq(dev->irq, hifn_interrupt, IRQF_SHARED, dev->name, dev);
if (err) {
- dprintk("Failed to request IRQ%d: err: %d.\n", dev->irq, err);
+ dev_err(&pdev->dev, "Failed to request IRQ%d: err: %d.\n",
+ dev->irq, err);
dev->irq = 0;
goto err_out_free_desc;
}
@@ -2646,10 +2554,6 @@ static int hifn_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (err)
goto err_out_free_irq;
- err = hifn_test(dev, 1, 0);
- if (err)
- goto err_out_stop_device;
-
err = hifn_register_rng(dev);
if (err)
goto err_out_stop_device;
@@ -2661,9 +2565,9 @@ static int hifn_probe(struct pci_dev *pdev, const struct pci_device_id *id)
INIT_DELAYED_WORK(&dev->work, hifn_work);
schedule_delayed_work(&dev->work, HZ);
- dprintk("HIFN crypto accelerator card at %s has been "
- "successfully registered as %s.\n",
- pci_name(pdev), dev->name);
+ dev_dbg(&pdev->dev, "HIFN crypto accelerator card at %s has been "
+ "successfully registered as %s.\n",
+ pci_name(pdev), dev->name);
return 0;
@@ -2680,7 +2584,7 @@ err_out_free_desc:
dev->desc_virt, dev->desc_dma);
err_out_unmap_bars:
- for (i=0; i<3; ++i)
+ for (i = 0; i < 3; ++i)
if (dev->bar[i])
iounmap(dev->bar[i]);
@@ -2715,7 +2619,7 @@ static void hifn_remove(struct pci_dev *pdev)
pci_free_consistent(pdev, sizeof(struct hifn_dma),
dev->desc_virt, dev->desc_dma);
- for (i=0; i<3; ++i)
+ for (i = 0; i < 3; ++i)
if (dev->bar[i])
iounmap(dev->bar[i]);
@@ -2750,8 +2654,7 @@ static int __init hifn_init(void)
if (strncmp(hifn_pll_ref, "ext", 3) &&
strncmp(hifn_pll_ref, "pci", 3)) {
- printk(KERN_ERR "hifn795x: invalid hifn_pll_ref clock, "
- "must be pci or ext");
+ pr_err("hifn795x: invalid hifn_pll_ref clock, must be pci or ext");
return -EINVAL;
}
@@ -2763,22 +2666,21 @@ static int __init hifn_init(void)
if (hifn_pll_ref[3] != '\0') {
freq = simple_strtoul(hifn_pll_ref + 3, NULL, 10);
if (freq < 20 || freq > 100) {
- printk(KERN_ERR "hifn795x: invalid hifn_pll_ref "
- "frequency, must be in the range "
- "of 20-100");
+ pr_err("hifn795x: invalid hifn_pll_ref frequency, must"
+ "be in the range of 20-100");
return -EINVAL;
}
}
err = pci_register_driver(&hifn_pci_driver);
if (err < 0) {
- dprintk("Failed to register PCI driver for %s device.\n",
- hifn_pci_driver.name);
+ pr_err("Failed to register PCI driver for %s device.\n",
+ hifn_pci_driver.name);
return -ENODEV;
}
- printk(KERN_INFO "Driver for HIFN 795x crypto accelerator chip "
- "has been successfully registered.\n");
+ pr_info("Driver for HIFN 795x crypto accelerator chip "
+ "has been successfully registered.\n");
return 0;
}
@@ -2787,8 +2689,8 @@ static void __exit hifn_fini(void)
{
pci_unregister_driver(&hifn_pci_driver);
- printk(KERN_INFO "Driver for HIFN 795x crypto accelerator chip "
- "has been successfully unregistered.\n");
+ pr_info("Driver for HIFN 795x crypto accelerator chip "
+ "has been successfully unregistered.\n");
}
module_init(hifn_init);
diff --git a/drivers/crypto/ixp4xx_crypto.c b/drivers/crypto/ixp4xx_crypto.c
index 8f2790353281..e52496a172d0 100644
--- a/drivers/crypto/ixp4xx_crypto.c
+++ b/drivers/crypto/ixp4xx_crypto.c
@@ -510,10 +510,8 @@ npe_error:
printk(KERN_ERR "%s not responding\n", npe_name(npe_c));
ret = -EIO;
err:
- if (ctx_pool)
- dma_pool_destroy(ctx_pool);
- if (buffer_pool)
- dma_pool_destroy(buffer_pool);
+ dma_pool_destroy(ctx_pool);
+ dma_pool_destroy(buffer_pool);
npe_release(npe_c);
return ret;
}
diff --git a/drivers/crypto/marvell/cipher.c b/drivers/crypto/marvell/cipher.c
index 6edae64bb387..dcf1fceb9336 100644
--- a/drivers/crypto/marvell/cipher.c
+++ b/drivers/crypto/marvell/cipher.c
@@ -401,7 +401,15 @@ static int mv_cesa_ablkcipher_req_init(struct ablkcipher_request *req,
return -EINVAL;
creq->src_nents = sg_nents_for_len(req->src, req->nbytes);
+ if (creq->src_nents < 0) {
+ dev_err(cesa_dev->dev, "Invalid number of src SG");
+ return creq->src_nents;
+ }
creq->dst_nents = sg_nents_for_len(req->dst, req->nbytes);
+ if (creq->dst_nents < 0) {
+ dev_err(cesa_dev->dev, "Invalid number of dst SG");
+ return creq->dst_nents;
+ }
mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_OP_CRYPT_ONLY,
CESA_SA_DESC_CFG_OP_MSK);
diff --git a/drivers/crypto/marvell/hash.c b/drivers/crypto/marvell/hash.c
index 6ec55b4a087b..683cca9ac3c4 100644
--- a/drivers/crypto/marvell/hash.c
+++ b/drivers/crypto/marvell/hash.c
@@ -712,6 +712,10 @@ static int mv_cesa_ahash_req_init(struct ahash_request *req, bool *cached)
creq->req.base.type = CESA_STD_REQ;
creq->src_nents = sg_nents_for_len(req->src, req->nbytes);
+ if (creq->src_nents < 0) {
+ dev_err(cesa_dev->dev, "Invalid number of src SG");
+ return creq->src_nents;
+ }
ret = mv_cesa_ahash_cache_req(req, cached);
if (ret)
diff --git a/drivers/crypto/n2_core.c b/drivers/crypto/n2_core.c
index 5450880abb7b..b85a7a7dbf63 100644
--- a/drivers/crypto/n2_core.c
+++ b/drivers/crypto/n2_core.c
@@ -241,7 +241,7 @@ static inline bool n2_should_run_async(struct spu_queue *qp, int this_len)
struct n2_ahash_alg {
struct list_head entry;
- const char *hash_zero;
+ const u8 *hash_zero;
const u32 *hash_init;
u8 hw_op_hashsz;
u8 digest_size;
@@ -1267,7 +1267,7 @@ static LIST_HEAD(cipher_algs);
struct n2_hash_tmpl {
const char *name;
- const char *hash_zero;
+ const u8 *hash_zero;
const u32 *hash_init;
u8 hw_op_hashsz;
u8 digest_size;
@@ -1276,40 +1276,19 @@ struct n2_hash_tmpl {
u8 hmac_type;
};
-static const char md5_zero[MD5_DIGEST_SIZE] = {
- 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
- 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e,
-};
static const u32 md5_init[MD5_HASH_WORDS] = {
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,
- 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8,
- 0x07, 0x09
-};
static const u32 sha1_init[SHA1_DIGEST_SIZE / 4] = {
SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4,
};
-static const char sha256_zero[SHA256_DIGEST_SIZE] = {
- 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a,
- 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae,
- 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99,
- 0x1b, 0x78, 0x52, 0xb8, 0x55
-};
static const u32 sha256_init[SHA256_DIGEST_SIZE / 4] = {
SHA256_H0, SHA256_H1, SHA256_H2, SHA256_H3,
SHA256_H4, SHA256_H5, SHA256_H6, SHA256_H7,
};
-static const char sha224_zero[SHA224_DIGEST_SIZE] = {
- 0xd1, 0x4a, 0x02, 0x8c, 0x2a, 0x3a, 0x2b, 0xc9, 0x47,
- 0x61, 0x02, 0xbb, 0x28, 0x82, 0x34, 0xc4, 0x15, 0xa2,
- 0xb0, 0x1f, 0x82, 0x8e, 0xa6, 0x2a, 0xc5, 0xb3, 0xe4,
- 0x2f
-};
static const u32 sha224_init[SHA256_DIGEST_SIZE / 4] = {
SHA224_H0, SHA224_H1, SHA224_H2, SHA224_H3,
SHA224_H4, SHA224_H5, SHA224_H6, SHA224_H7,
@@ -1317,7 +1296,7 @@ static const u32 sha224_init[SHA256_DIGEST_SIZE / 4] = {
static const struct n2_hash_tmpl hash_tmpls[] = {
{ .name = "md5",
- .hash_zero = md5_zero,
+ .hash_zero = md5_zero_message_hash,
.hash_init = md5_init,
.auth_type = AUTH_TYPE_MD5,
.hmac_type = AUTH_TYPE_HMAC_MD5,
@@ -1325,7 +1304,7 @@ static const struct n2_hash_tmpl hash_tmpls[] = {
.digest_size = MD5_DIGEST_SIZE,
.block_size = MD5_HMAC_BLOCK_SIZE },
{ .name = "sha1",
- .hash_zero = sha1_zero,
+ .hash_zero = sha1_zero_message_hash,
.hash_init = sha1_init,
.auth_type = AUTH_TYPE_SHA1,
.hmac_type = AUTH_TYPE_HMAC_SHA1,
@@ -1333,7 +1312,7 @@ static const struct n2_hash_tmpl hash_tmpls[] = {
.digest_size = SHA1_DIGEST_SIZE,
.block_size = SHA1_BLOCK_SIZE },
{ .name = "sha256",
- .hash_zero = sha256_zero,
+ .hash_zero = sha256_zero_message_hash,
.hash_init = sha256_init,
.auth_type = AUTH_TYPE_SHA256,
.hmac_type = AUTH_TYPE_HMAC_SHA256,
@@ -1341,7 +1320,7 @@ static const struct n2_hash_tmpl hash_tmpls[] = {
.digest_size = SHA256_DIGEST_SIZE,
.block_size = SHA256_BLOCK_SIZE },
{ .name = "sha224",
- .hash_zero = sha224_zero,
+ .hash_zero = sha224_zero_message_hash,
.hash_init = sha224_init,
.auth_type = AUTH_TYPE_SHA256,
.hmac_type = AUTH_TYPE_RESERVED,
@@ -2243,22 +2222,19 @@ static struct platform_driver n2_mau_driver = {
.remove = n2_mau_remove,
};
+static struct platform_driver * const drivers[] = {
+ &n2_crypto_driver,
+ &n2_mau_driver,
+};
+
static int __init n2_init(void)
{
- int err = platform_driver_register(&n2_crypto_driver);
-
- if (!err) {
- err = platform_driver_register(&n2_mau_driver);
- if (err)
- platform_driver_unregister(&n2_crypto_driver);
- }
- return err;
+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
}
static void __exit n2_exit(void)
{
- platform_driver_unregister(&n2_mau_driver);
- platform_driver_unregister(&n2_crypto_driver);
+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
}
module_init(n2_init);
diff --git a/drivers/crypto/nx/nx-842-powernv.c b/drivers/crypto/nx/nx-842-powernv.c
index 9ef51fafdbff..1710f80a09ec 100644
--- a/drivers/crypto/nx/nx-842-powernv.c
+++ b/drivers/crypto/nx/nx-842-powernv.c
@@ -442,6 +442,14 @@ static int nx842_powernv_function(const unsigned char *in, unsigned int inlen,
(unsigned int)ccw,
(unsigned int)be32_to_cpu(crb->ccw));
+ /*
+ * NX842 coprocessor sets 3rd bit in CR register with XER[S0].
+ * XER[S0] is the integer summary overflow bit which is nothing
+ * to do NX. Since this bit can be set with other return values,
+ * mask this bit.
+ */
+ ret &= ~ICSWX_XERS0;
+
switch (ret) {
case ICSWX_INITIATED:
ret = wait_for_csb(wmem, csb);
@@ -454,10 +462,6 @@ static int nx842_powernv_function(const unsigned char *in, unsigned int inlen,
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)
@@ -525,7 +529,6 @@ static int nx842_powernv_decompress(const unsigned char *in, unsigned int inlen,
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;
@@ -534,18 +537,16 @@ static int __init nx842_powernv_probe(struct device_node *dn)
pr_err("ibm,chip-id missing\n");
return -EINVAL;
}
- ct_prop = of_find_property(dn, "ibm,842-coprocessor-type", NULL);
- if (!ct_prop) {
+
+ if (of_property_read_u32(dn, "ibm,842-coprocessor-type", &ct)) {
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) {
+
+ if (of_property_read_u32(dn, "ibm,842-coprocessor-instance", &ci)) {
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)
diff --git a/drivers/crypto/nx/nx-aes-ccm.c b/drivers/crypto/nx/nx-aes-ccm.c
index 73ef49922788..7038f364acb5 100644
--- a/drivers/crypto/nx/nx-aes-ccm.c
+++ b/drivers/crypto/nx/nx-aes-ccm.c
@@ -409,7 +409,7 @@ static int ccm_nx_decrypt(struct aead_request *req,
processed += to_process;
} while (processed < nbytes);
- rc = memcmp(csbcpb->cpb.aes_ccm.out_pat_or_mac, priv->oauth_tag,
+ rc = crypto_memneq(csbcpb->cpb.aes_ccm.out_pat_or_mac, priv->oauth_tag,
authsize) ? -EBADMSG : 0;
out:
spin_unlock_irqrestore(&nx_ctx->lock, irq_flags);
diff --git a/drivers/crypto/nx/nx-aes-gcm.c b/drivers/crypto/nx/nx-aes-gcm.c
index eee624f589b6..abd465f479c4 100644
--- a/drivers/crypto/nx/nx-aes-gcm.c
+++ b/drivers/crypto/nx/nx-aes-gcm.c
@@ -21,6 +21,7 @@
#include <crypto/internal/aead.h>
#include <crypto/aes.h>
+#include <crypto/algapi.h>
#include <crypto/scatterwalk.h>
#include <linux/module.h>
#include <linux/types.h>
@@ -418,7 +419,7 @@ mac:
itag, req->src, req->assoclen + nbytes,
crypto_aead_authsize(crypto_aead_reqtfm(req)),
SCATTERWALK_FROM_SG);
- rc = memcmp(itag, otag,
+ rc = crypto_memneq(itag, otag,
crypto_aead_authsize(crypto_aead_reqtfm(req))) ?
-EBADMSG : 0;
}
diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c
index eba23147c0ee..dd355bd19474 100644
--- a/drivers/crypto/omap-aes.c
+++ b/drivers/crypto/omap-aes.c
@@ -539,8 +539,6 @@ static void omap_aes_finish_req(struct omap_aes_dev *dd, int err)
static int omap_aes_crypt_dma_stop(struct omap_aes_dev *dd)
{
- int err = 0;
-
pr_debug("total: %d\n", dd->total);
omap_aes_dma_stop(dd);
@@ -548,7 +546,7 @@ static int omap_aes_crypt_dma_stop(struct omap_aes_dev *dd)
dmaengine_terminate_all(dd->dma_lch_in);
dmaengine_terminate_all(dd->dma_lch_out);
- return err;
+ return 0;
}
static int omap_aes_check_aligned(struct scatterlist *sg, int total)
diff --git a/drivers/crypto/omap-des.c b/drivers/crypto/omap-des.c
index 0a70e46d5416..dd7b93f2f94c 100644
--- a/drivers/crypto/omap-des.c
+++ b/drivers/crypto/omap-des.c
@@ -527,8 +527,6 @@ static void omap_des_finish_req(struct omap_des_dev *dd, int err)
static int omap_des_crypt_dma_stop(struct omap_des_dev *dd)
{
- int err = 0;
-
pr_debug("total: %d\n", dd->total);
omap_des_dma_stop(dd);
@@ -536,7 +534,7 @@ static int omap_des_crypt_dma_stop(struct omap_des_dev *dd)
dmaengine_terminate_all(dd->dma_lch_in);
dmaengine_terminate_all(dd->dma_lch_out);
- return err;
+ return 0;
}
static int omap_des_copy_needed(struct scatterlist *sg)
@@ -1086,6 +1084,7 @@ static int omap_des_probe(struct platform_device *pdev)
dd->phys_base = res->start;
pm_runtime_enable(dev);
+ pm_runtime_irq_safe(dev);
err = pm_runtime_get_sync(dev);
if (err < 0) {
pm_runtime_put_noidle(dev);
diff --git a/drivers/crypto/padlock-aes.c b/drivers/crypto/padlock-aes.c
index da2d6777bd09..441e86b23571 100644
--- a/drivers/crypto/padlock-aes.c
+++ b/drivers/crypto/padlock-aes.c
@@ -238,7 +238,7 @@ static inline void ecb_crypt(const u8 *in, u8 *out, u32 *key,
/* Padlock in ECB mode fetches at least ecb_fetch_bytes of data.
* We could avoid some copying here but it's probably not worth it.
*/
- if (unlikely(((unsigned long)in & ~PAGE_MASK) + ecb_fetch_bytes > PAGE_SIZE)) {
+ if (unlikely(offset_in_page(in) + ecb_fetch_bytes > PAGE_SIZE)) {
ecb_crypt_copy(in, out, key, cword, count);
return;
}
@@ -250,7 +250,7 @@ static inline u8 *cbc_crypt(const u8 *in, u8 *out, u32 *key,
u8 *iv, struct cword *cword, int count)
{
/* Padlock in CBC mode fetches at least cbc_fetch_bytes of data. */
- if (unlikely(((unsigned long)in & ~PAGE_MASK) + cbc_fetch_bytes > PAGE_SIZE))
+ if (unlikely(offset_in_page(in) + cbc_fetch_bytes > PAGE_SIZE))
return cbc_crypt_copy(in, out, key, iv, cword, count);
return rep_xcrypt_cbc(in, out, key, iv, cword, count);
@@ -515,7 +515,7 @@ static int __init padlock_init(void)
if (!x86_match_cpu(padlock_cpu_id))
return -ENODEV;
- if (!cpu_has_xcrypt_enabled) {
+ if (!boot_cpu_has(X86_FEATURE_XCRYPT_EN)) {
printk(KERN_NOTICE PFX "VIA PadLock detected, but not enabled. Hmm, strange...\n");
return -ENODEV;
}
diff --git a/drivers/crypto/padlock-sha.c b/drivers/crypto/padlock-sha.c
index 4e154c9b9206..8c5f90647b7a 100644
--- a/drivers/crypto/padlock-sha.c
+++ b/drivers/crypto/padlock-sha.c
@@ -540,7 +540,7 @@ static int __init padlock_init(void)
struct shash_alg *sha1;
struct shash_alg *sha256;
- if (!x86_match_cpu(padlock_sha_ids) || !cpu_has_phe_enabled)
+ if (!x86_match_cpu(padlock_sha_ids) || !boot_cpu_has(X86_FEATURE_PHE_EN))
return -ENODEV;
/* Register the newly added algorithm module if on *
diff --git a/drivers/crypto/picoxcell_crypto.c b/drivers/crypto/picoxcell_crypto.c
index 615da961c4d8..3b1c7ecf078f 100644
--- a/drivers/crypto/picoxcell_crypto.c
+++ b/drivers/crypto/picoxcell_crypto.c
@@ -272,12 +272,6 @@ static unsigned spacc_load_ctx(struct spacc_generic_ctx *ctx,
return indx;
}
-/* Count the number of scatterlist entries in a scatterlist. */
-static inline int sg_count(struct scatterlist *sg_list, int nbytes)
-{
- return sg_nents_for_len(sg_list, nbytes);
-}
-
static inline void ddt_set(struct spacc_ddt *ddt, dma_addr_t phys, size_t len)
{
ddt->p = phys;
@@ -295,12 +289,17 @@ static struct spacc_ddt *spacc_sg_to_ddt(struct spacc_engine *engine,
enum dma_data_direction dir,
dma_addr_t *ddt_phys)
{
- unsigned nents, mapped_ents;
+ unsigned mapped_ents;
struct scatterlist *cur;
struct spacc_ddt *ddt;
int i;
+ int nents;
- nents = sg_count(payload, nbytes);
+ nents = sg_nents_for_len(payload, nbytes);
+ if (nents < 0) {
+ dev_err(engine->dev, "Invalid numbers of SG.\n");
+ return NULL;
+ }
mapped_ents = dma_map_sg(engine->dev, payload, nents, dir);
if (mapped_ents + 1 > MAX_DDT_LEN)
@@ -328,7 +327,7 @@ static int spacc_aead_make_ddts(struct aead_request *areq)
struct spacc_engine *engine = req->engine;
struct spacc_ddt *src_ddt, *dst_ddt;
unsigned total;
- unsigned int src_nents, dst_nents;
+ int src_nents, dst_nents;
struct scatterlist *cur;
int i, dst_ents, src_ents;
@@ -336,13 +335,21 @@ static int spacc_aead_make_ddts(struct aead_request *areq)
if (req->is_encrypt)
total += crypto_aead_authsize(aead);
- src_nents = sg_count(areq->src, total);
+ src_nents = sg_nents_for_len(areq->src, total);
+ if (src_nents < 0) {
+ dev_err(engine->dev, "Invalid numbers of src SG.\n");
+ return src_nents;
+ }
if (src_nents + 1 > MAX_DDT_LEN)
return -E2BIG;
dst_nents = 0;
if (areq->src != areq->dst) {
- dst_nents = sg_count(areq->dst, total);
+ dst_nents = sg_nents_for_len(areq->dst, total);
+ if (dst_nents < 0) {
+ dev_err(engine->dev, "Invalid numbers of dst SG.\n");
+ return dst_nents;
+ }
if (src_nents + 1 > MAX_DDT_LEN)
return -E2BIG;
}
@@ -422,13 +429,22 @@ static void spacc_aead_free_ddts(struct spacc_req *req)
(req->is_encrypt ? crypto_aead_authsize(aead) : 0);
struct spacc_aead_ctx *aead_ctx = crypto_aead_ctx(aead);
struct spacc_engine *engine = aead_ctx->generic.engine;
- unsigned nents = sg_count(areq->src, total);
+ int nents = sg_nents_for_len(areq->src, total);
+
+ /* sg_nents_for_len should not fail since it works when mapping sg */
+ if (unlikely(nents < 0)) {
+ dev_err(engine->dev, "Invalid numbers of src SG.\n");
+ return;
+ }
if (areq->src != areq->dst) {
dma_unmap_sg(engine->dev, areq->src, nents, DMA_TO_DEVICE);
- dma_unmap_sg(engine->dev, areq->dst,
- sg_count(areq->dst, total),
- DMA_FROM_DEVICE);
+ nents = sg_nents_for_len(areq->dst, total);
+ if (unlikely(nents < 0)) {
+ dev_err(engine->dev, "Invalid numbers of dst SG.\n");
+ return;
+ }
+ dma_unmap_sg(engine->dev, areq->dst, nents, DMA_FROM_DEVICE);
} else
dma_unmap_sg(engine->dev, areq->src, nents, DMA_BIDIRECTIONAL);
@@ -440,7 +456,12 @@ static void spacc_free_ddt(struct spacc_req *req, struct spacc_ddt *ddt,
dma_addr_t ddt_addr, struct scatterlist *payload,
unsigned nbytes, enum dma_data_direction dir)
{
- unsigned nents = sg_count(payload, nbytes);
+ int nents = sg_nents_for_len(payload, nbytes);
+
+ if (nents < 0) {
+ dev_err(req->engine->dev, "Invalid numbers of SG.\n");
+ return;
+ }
dma_unmap_sg(req->engine->dev, payload, nents, dir);
dma_pool_free(req->engine->req_pool, ddt, ddt_addr);
@@ -835,8 +856,7 @@ static int spacc_ablk_need_fallback(struct spacc_req *req)
static void spacc_ablk_complete(struct spacc_req *req)
{
- struct ablkcipher_request *ablk_req =
- container_of(req->req, struct ablkcipher_request, base);
+ struct ablkcipher_request *ablk_req = ablkcipher_request_cast(req->req);
if (ablk_req->src != ablk_req->dst) {
spacc_free_ddt(req, req->src_ddt, req->src_addr, ablk_req->src,
diff --git a/drivers/crypto/qat/Kconfig b/drivers/crypto/qat/Kconfig
index eefccf7b8be7..85b44e577684 100644
--- a/drivers/crypto/qat/Kconfig
+++ b/drivers/crypto/qat/Kconfig
@@ -22,6 +22,28 @@ config CRYPTO_DEV_QAT_DH895xCC
To compile this as a module, choose M here: the module
will be called qat_dh895xcc.
+config CRYPTO_DEV_QAT_C3XXX
+ tristate "Support for Intel(R) C3XXX"
+ depends on X86 && PCI
+ select CRYPTO_DEV_QAT
+ help
+ Support for Intel(R) C3xxx with Intel(R) QuickAssist Technology
+ for accelerating crypto and compression workloads.
+
+ To compile this as a module, choose M here: the module
+ will be called qat_c3xxx.
+
+config CRYPTO_DEV_QAT_C62X
+ tristate "Support for Intel(R) C62X"
+ depends on X86 && PCI
+ select CRYPTO_DEV_QAT
+ help
+ Support for Intel(R) C62x with Intel(R) QuickAssist Technology
+ for accelerating crypto and compression workloads.
+
+ To compile this as a module, choose M here: the module
+ will be called qat_c62x.
+
config CRYPTO_DEV_QAT_DH895xCCVF
tristate "Support for Intel(R) DH895xCC Virtual Function"
depends on X86 && PCI
@@ -34,3 +56,27 @@ config CRYPTO_DEV_QAT_DH895xCCVF
To compile this as a module, choose M here: the module
will be called qat_dh895xccvf.
+
+config CRYPTO_DEV_QAT_C3XXXVF
+ tristate "Support for Intel(R) C3XXX Virtual Function"
+ depends on X86 && PCI
+ select PCI_IOV
+ select CRYPTO_DEV_QAT
+ help
+ Support for Intel(R) C3xxx with Intel(R) QuickAssist Technology
+ Virtual Function for accelerating crypto and compression workloads.
+
+ To compile this as a module, choose M here: the module
+ will be called qat_c3xxxvf.
+
+config CRYPTO_DEV_QAT_C62XVF
+ tristate "Support for Intel(R) C62X Virtual Function"
+ depends on X86 && PCI
+ select PCI_IOV
+ select CRYPTO_DEV_QAT
+ help
+ Support for Intel(R) C62x with Intel(R) QuickAssist Technology
+ Virtual Function for accelerating crypto and compression workloads.
+
+ To compile this as a module, choose M here: the module
+ will be called qat_c62xvf.
diff --git a/drivers/crypto/qat/Makefile b/drivers/crypto/qat/Makefile
index a3ce0b70e32f..8265106f1c8e 100644
--- a/drivers/crypto/qat/Makefile
+++ b/drivers/crypto/qat/Makefile
@@ -1,3 +1,7 @@
obj-$(CONFIG_CRYPTO_DEV_QAT) += qat_common/
obj-$(CONFIG_CRYPTO_DEV_QAT_DH895xCC) += qat_dh895xcc/
+obj-$(CONFIG_CRYPTO_DEV_QAT_C3XXX) += qat_c3xxx/
+obj-$(CONFIG_CRYPTO_DEV_QAT_C62X) += qat_c62x/
obj-$(CONFIG_CRYPTO_DEV_QAT_DH895xCCVF) += qat_dh895xccvf/
+obj-$(CONFIG_CRYPTO_DEV_QAT_C3XXXVF) += qat_c3xxxvf/
+obj-$(CONFIG_CRYPTO_DEV_QAT_C62XVF) += qat_c62xvf/
diff --git a/drivers/crypto/qat/qat_c3xxx/Makefile b/drivers/crypto/qat/qat_c3xxx/Makefile
new file mode 100644
index 000000000000..8f5fd4838a96
--- /dev/null
+++ b/drivers/crypto/qat/qat_c3xxx/Makefile
@@ -0,0 +1,3 @@
+ccflags-y := -I$(src)/../qat_common
+obj-$(CONFIG_CRYPTO_DEV_QAT_C3XXX) += qat_c3xxx.o
+qat_c3xxx-objs := adf_drv.o adf_c3xxx_hw_data.o
diff --git a/drivers/crypto/qat/qat_c3xxx/adf_c3xxx_hw_data.c b/drivers/crypto/qat/qat_c3xxx/adf_c3xxx_hw_data.c
new file mode 100644
index 000000000000..c5bd5a9abc4d
--- /dev/null
+++ b/drivers/crypto/qat/qat_c3xxx/adf_c3xxx_hw_data.c
@@ -0,0 +1,238 @@
+/*
+ This file is provided under a dual BSD/GPLv2 license. When using or
+ redistributing this file, you may do so under either license.
+
+ GPL LICENSE SUMMARY
+ Copyright(c) 2014 Intel Corporation.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ Contact Information:
+ qat-linux@intel.com
+
+ BSD LICENSE
+ Copyright(c) 2014 Intel Corporation.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include <adf_accel_devices.h>
+#include <adf_common_drv.h>
+#include <adf_pf2vf_msg.h>
+#include "adf_c3xxx_hw_data.h"
+
+/* Worker thread to service arbiter mappings based on dev SKUs */
+static const u32 thrd_to_arb_map_6_me_sku[] = {
+ 0x12222AAA, 0x11222AAA, 0x12222AAA,
+ 0x11222AAA, 0x12222AAA, 0x11222AAA
+};
+
+static struct adf_hw_device_class c3xxx_class = {
+ .name = ADF_C3XXX_DEVICE_NAME,
+ .type = DEV_C3XXX,
+ .instances = 0
+};
+
+static u32 get_accel_mask(u32 fuse)
+{
+ return (~fuse) >> ADF_C3XXX_ACCELERATORS_REG_OFFSET &
+ ADF_C3XXX_ACCELERATORS_MASK;
+}
+
+static u32 get_ae_mask(u32 fuse)
+{
+ return (~fuse) & ADF_C3XXX_ACCELENGINES_MASK;
+}
+
+static u32 get_num_accels(struct adf_hw_device_data *self)
+{
+ u32 i, ctr = 0;
+
+ if (!self || !self->accel_mask)
+ return 0;
+
+ for (i = 0; i < ADF_C3XXX_MAX_ACCELERATORS; i++) {
+ if (self->accel_mask & (1 << i))
+ ctr++;
+ }
+ return ctr;
+}
+
+static u32 get_num_aes(struct adf_hw_device_data *self)
+{
+ u32 i, ctr = 0;
+
+ if (!self || !self->ae_mask)
+ return 0;
+
+ for (i = 0; i < ADF_C3XXX_MAX_ACCELENGINES; i++) {
+ if (self->ae_mask & (1 << i))
+ ctr++;
+ }
+ return ctr;
+}
+
+static u32 get_misc_bar_id(struct adf_hw_device_data *self)
+{
+ return ADF_C3XXX_PMISC_BAR;
+}
+
+static u32 get_etr_bar_id(struct adf_hw_device_data *self)
+{
+ return ADF_C3XXX_ETR_BAR;
+}
+
+static u32 get_sram_bar_id(struct adf_hw_device_data *self)
+{
+ return 0;
+}
+
+static enum dev_sku_info get_sku(struct adf_hw_device_data *self)
+{
+ int aes = get_num_aes(self);
+
+ if (aes == 6)
+ return DEV_SKU_4;
+
+ return DEV_SKU_UNKNOWN;
+}
+
+static void adf_get_arbiter_mapping(struct adf_accel_dev *accel_dev,
+ u32 const **arb_map_config)
+{
+ switch (accel_dev->accel_pci_dev.sku) {
+ case DEV_SKU_4:
+ *arb_map_config = thrd_to_arb_map_6_me_sku;
+ break;
+ default:
+ dev_err(&GET_DEV(accel_dev),
+ "The configuration doesn't match any SKU");
+ *arb_map_config = NULL;
+ }
+}
+
+static u32 get_pf2vf_offset(u32 i)
+{
+ return ADF_C3XXX_PF2VF_OFFSET(i);
+}
+
+static u32 get_vintmsk_offset(u32 i)
+{
+ return ADF_C3XXX_VINTMSK_OFFSET(i);
+}
+
+static void adf_enable_error_correction(struct adf_accel_dev *accel_dev)
+{
+ struct adf_hw_device_data *hw_device = accel_dev->hw_device;
+ struct adf_bar *misc_bar = &GET_BARS(accel_dev)[ADF_C3XXX_PMISC_BAR];
+ void __iomem *csr = misc_bar->virt_addr;
+ unsigned int val, i;
+
+ /* Enable Accel Engine error detection & correction */
+ for (i = 0; i < hw_device->get_num_aes(hw_device); i++) {
+ val = ADF_CSR_RD(csr, ADF_C3XXX_AE_CTX_ENABLES(i));
+ val |= ADF_C3XXX_ENABLE_AE_ECC_ERR;
+ ADF_CSR_WR(csr, ADF_C3XXX_AE_CTX_ENABLES(i), val);
+ val = ADF_CSR_RD(csr, ADF_C3XXX_AE_MISC_CONTROL(i));
+ val |= ADF_C3XXX_ENABLE_AE_ECC_PARITY_CORR;
+ ADF_CSR_WR(csr, ADF_C3XXX_AE_MISC_CONTROL(i), val);
+ }
+
+ /* Enable shared memory error detection & correction */
+ for (i = 0; i < hw_device->get_num_accels(hw_device); i++) {
+ val = ADF_CSR_RD(csr, ADF_C3XXX_UERRSSMSH(i));
+ val |= ADF_C3XXX_ERRSSMSH_EN;
+ ADF_CSR_WR(csr, ADF_C3XXX_UERRSSMSH(i), val);
+ val = ADF_CSR_RD(csr, ADF_C3XXX_CERRSSMSH(i));
+ val |= ADF_C3XXX_ERRSSMSH_EN;
+ ADF_CSR_WR(csr, ADF_C3XXX_CERRSSMSH(i), val);
+ }
+}
+
+static void adf_enable_ints(struct adf_accel_dev *accel_dev)
+{
+ void __iomem *addr;
+
+ addr = (&GET_BARS(accel_dev)[ADF_C3XXX_PMISC_BAR])->virt_addr;
+
+ /* Enable bundle and misc interrupts */
+ ADF_CSR_WR(addr, ADF_C3XXX_SMIAPF0_MASK_OFFSET,
+ ADF_C3XXX_SMIA0_MASK);
+ ADF_CSR_WR(addr, ADF_C3XXX_SMIAPF1_MASK_OFFSET,
+ ADF_C3XXX_SMIA1_MASK);
+}
+
+static int adf_pf_enable_vf2pf_comms(struct adf_accel_dev *accel_dev)
+{
+ return 0;
+}
+
+void adf_init_hw_data_c3xxx(struct adf_hw_device_data *hw_data)
+{
+ hw_data->dev_class = &c3xxx_class;
+ hw_data->instance_id = c3xxx_class.instances++;
+ hw_data->num_banks = ADF_C3XXX_ETR_MAX_BANKS;
+ hw_data->num_accel = ADF_C3XXX_MAX_ACCELERATORS;
+ hw_data->num_logical_accel = 1;
+ hw_data->num_engines = ADF_C3XXX_MAX_ACCELENGINES;
+ hw_data->tx_rx_gap = ADF_C3XXX_RX_RINGS_OFFSET;
+ hw_data->tx_rings_mask = ADF_C3XXX_TX_RINGS_MASK;
+ hw_data->alloc_irq = adf_isr_resource_alloc;
+ hw_data->free_irq = adf_isr_resource_free;
+ hw_data->enable_error_correction = adf_enable_error_correction;
+ hw_data->get_accel_mask = get_accel_mask;
+ hw_data->get_ae_mask = get_ae_mask;
+ hw_data->get_num_accels = get_num_accels;
+ hw_data->get_num_aes = get_num_aes;
+ hw_data->get_sram_bar_id = get_sram_bar_id;
+ hw_data->get_etr_bar_id = get_etr_bar_id;
+ hw_data->get_misc_bar_id = get_misc_bar_id;
+ hw_data->get_pf2vf_offset = get_pf2vf_offset;
+ hw_data->get_vintmsk_offset = get_vintmsk_offset;
+ hw_data->get_sku = get_sku;
+ hw_data->fw_name = ADF_C3XXX_FW;
+ hw_data->fw_mmp_name = ADF_C3XXX_MMP;
+ hw_data->init_admin_comms = adf_init_admin_comms;
+ hw_data->exit_admin_comms = adf_exit_admin_comms;
+ hw_data->disable_iov = adf_disable_sriov;
+ hw_data->send_admin_init = adf_send_admin_init;
+ hw_data->init_arb = adf_init_arb;
+ hw_data->exit_arb = adf_exit_arb;
+ hw_data->get_arb_mapping = adf_get_arbiter_mapping;
+ hw_data->enable_ints = adf_enable_ints;
+ hw_data->enable_vf2pf_comms = adf_pf_enable_vf2pf_comms;
+ hw_data->min_iov_compat_ver = ADF_PFVF_COMPATIBILITY_VERSION;
+}
+
+void adf_clean_hw_data_c3xxx(struct adf_hw_device_data *hw_data)
+{
+ hw_data->dev_class->instances--;
+}
diff --git a/drivers/crypto/qat/qat_c3xxx/adf_c3xxx_hw_data.h b/drivers/crypto/qat/qat_c3xxx/adf_c3xxx_hw_data.h
new file mode 100644
index 000000000000..2f2681d3458a
--- /dev/null
+++ b/drivers/crypto/qat/qat_c3xxx/adf_c3xxx_hw_data.h
@@ -0,0 +1,83 @@
+/*
+ This file is provided under a dual BSD/GPLv2 license. When using or
+ redistributing this file, you may do so under either license.
+
+ GPL LICENSE SUMMARY
+ Copyright(c) 2014 Intel Corporation.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ Contact Information:
+ qat-linux@intel.com
+
+ BSD LICENSE
+ Copyright(c) 2014 Intel Corporation.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef ADF_C3XXX_HW_DATA_H_
+#define ADF_C3XXX_HW_DATA_H_
+
+/* PCIe configuration space */
+#define ADF_C3XXX_PMISC_BAR 0
+#define ADF_C3XXX_ETR_BAR 1
+#define ADF_C3XXX_RX_RINGS_OFFSET 8
+#define ADF_C3XXX_TX_RINGS_MASK 0xFF
+#define ADF_C3XXX_MAX_ACCELERATORS 3
+#define ADF_C3XXX_MAX_ACCELENGINES 6
+#define ADF_C3XXX_ACCELERATORS_REG_OFFSET 16
+#define ADF_C3XXX_ACCELERATORS_MASK 0x3
+#define ADF_C3XXX_ACCELENGINES_MASK 0x3F
+#define ADF_C3XXX_ETR_MAX_BANKS 16
+#define ADF_C3XXX_SMIAPF0_MASK_OFFSET (0x3A000 + 0x28)
+#define ADF_C3XXX_SMIAPF1_MASK_OFFSET (0x3A000 + 0x30)
+#define ADF_C3XXX_SMIA0_MASK 0xFFFF
+#define ADF_C3XXX_SMIA1_MASK 0x1
+/* Error detection and correction */
+#define ADF_C3XXX_AE_CTX_ENABLES(i) (i * 0x1000 + 0x20818)
+#define ADF_C3XXX_AE_MISC_CONTROL(i) (i * 0x1000 + 0x20960)
+#define ADF_C3XXX_ENABLE_AE_ECC_ERR BIT(28)
+#define ADF_C3XXX_ENABLE_AE_ECC_PARITY_CORR (BIT(24) | BIT(12))
+#define ADF_C3XXX_UERRSSMSH(i) (i * 0x4000 + 0x18)
+#define ADF_C3XXX_CERRSSMSH(i) (i * 0x4000 + 0x10)
+#define ADF_C3XXX_ERRSSMSH_EN BIT(3)
+
+#define ADF_C3XXX_PF2VF_OFFSET(i) (0x3A000 + 0x280 + ((i) * 0x04))
+#define ADF_C3XXX_VINTMSK_OFFSET(i) (0x3A000 + 0x200 + ((i) * 0x04))
+
+/* Firmware Binary */
+#define ADF_C3XXX_FW "qat_c3xxx.bin"
+#define ADF_C3XXX_MMP "qat_c3xxx_mmp.bin"
+
+void adf_init_hw_data_c3xxx(struct adf_hw_device_data *hw_data);
+void adf_clean_hw_data_c3xxx(struct adf_hw_device_data *hw_data);
+#endif
diff --git a/drivers/crypto/qat/qat_c3xxx/adf_drv.c b/drivers/crypto/qat/qat_c3xxx/adf_drv.c
new file mode 100644
index 000000000000..e13bd08ddd1e
--- /dev/null
+++ b/drivers/crypto/qat/qat_c3xxx/adf_drv.c
@@ -0,0 +1,335 @@
+/*
+ This file is provided under a dual BSD/GPLv2 license. When using or
+ redistributing this file, you may do so under either license.
+
+ GPL LICENSE SUMMARY
+ Copyright(c) 2014 Intel Corporation.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ Contact Information:
+ qat-linux@intel.com
+
+ BSD LICENSE
+ Copyright(c) 2014 Intel Corporation.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/io.h>
+#include <adf_accel_devices.h>
+#include <adf_common_drv.h>
+#include <adf_cfg.h>
+#include "adf_c3xxx_hw_data.h"
+
+#define ADF_SYSTEM_DEVICE(device_id) \
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, device_id)}
+
+static const struct pci_device_id adf_pci_tbl[] = {
+ ADF_SYSTEM_DEVICE(ADF_C3XXX_PCI_DEVICE_ID),
+ {0,}
+};
+MODULE_DEVICE_TABLE(pci, adf_pci_tbl);
+
+static int adf_probe(struct pci_dev *dev, const struct pci_device_id *ent);
+static void adf_remove(struct pci_dev *dev);
+
+static struct pci_driver adf_driver = {
+ .id_table = adf_pci_tbl,
+ .name = ADF_C3XXX_DEVICE_NAME,
+ .probe = adf_probe,
+ .remove = adf_remove,
+ .sriov_configure = adf_sriov_configure,
+};
+
+static void adf_cleanup_pci_dev(struct adf_accel_dev *accel_dev)
+{
+ pci_release_regions(accel_dev->accel_pci_dev.pci_dev);
+ pci_disable_device(accel_dev->accel_pci_dev.pci_dev);
+}
+
+static void adf_cleanup_accel(struct adf_accel_dev *accel_dev)
+{
+ struct adf_accel_pci *accel_pci_dev = &accel_dev->accel_pci_dev;
+ int i;
+
+ for (i = 0; i < ADF_PCI_MAX_BARS; i++) {
+ struct adf_bar *bar = &accel_pci_dev->pci_bars[i];
+
+ if (bar->virt_addr)
+ pci_iounmap(accel_pci_dev->pci_dev, bar->virt_addr);
+ }
+
+ if (accel_dev->hw_device) {
+ switch (accel_pci_dev->pci_dev->device) {
+ case ADF_C3XXX_PCI_DEVICE_ID:
+ adf_clean_hw_data_c3xxx(accel_dev->hw_device);
+ break;
+ default:
+ break;
+ }
+ kfree(accel_dev->hw_device);
+ accel_dev->hw_device = NULL;
+ }
+ adf_cfg_dev_remove(accel_dev);
+ debugfs_remove(accel_dev->debugfs_dir);
+ adf_devmgr_rm_dev(accel_dev, NULL);
+}
+
+static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct adf_accel_dev *accel_dev;
+ struct adf_accel_pci *accel_pci_dev;
+ struct adf_hw_device_data *hw_data;
+ char name[ADF_DEVICE_NAME_LENGTH];
+ unsigned int i, bar_nr;
+ int ret, bar_mask;
+
+ switch (ent->device) {
+ case ADF_C3XXX_PCI_DEVICE_ID:
+ break;
+ default:
+ dev_err(&pdev->dev, "Invalid device 0x%x.\n", ent->device);
+ return -ENODEV;
+ }
+
+ if (num_possible_nodes() > 1 && dev_to_node(&pdev->dev) < 0) {
+ /* If the accelerator is connected to a node with no memory
+ * there is no point in using the accelerator since the remote
+ * memory transaction will be very slow. */
+ dev_err(&pdev->dev, "Invalid NUMA configuration.\n");
+ return -EINVAL;
+ }
+
+ accel_dev = kzalloc_node(sizeof(*accel_dev), GFP_KERNEL,
+ dev_to_node(&pdev->dev));
+ if (!accel_dev)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&accel_dev->crypto_list);
+ accel_pci_dev = &accel_dev->accel_pci_dev;
+ accel_pci_dev->pci_dev = pdev;
+
+ /* Add accel device to accel table.
+ * This should be called before adf_cleanup_accel is called */
+ if (adf_devmgr_add_dev(accel_dev, NULL)) {
+ dev_err(&pdev->dev, "Failed to add new accelerator device.\n");
+ kfree(accel_dev);
+ return -EFAULT;
+ }
+
+ accel_dev->owner = THIS_MODULE;
+ /* Allocate and configure device configuration structure */
+ hw_data = kzalloc_node(sizeof(*hw_data), GFP_KERNEL,
+ dev_to_node(&pdev->dev));
+ if (!hw_data) {
+ ret = -ENOMEM;
+ goto out_err;
+ }
+
+ accel_dev->hw_device = hw_data;
+ adf_init_hw_data_c3xxx(accel_dev->hw_device);
+ pci_read_config_byte(pdev, PCI_REVISION_ID, &accel_pci_dev->revid);
+ pci_read_config_dword(pdev, ADF_DEVICE_FUSECTL_OFFSET,
+ &hw_data->fuses);
+
+ /* Get Accelerators and Accelerators Engines masks */
+ hw_data->accel_mask = hw_data->get_accel_mask(hw_data->fuses);
+ hw_data->ae_mask = hw_data->get_ae_mask(hw_data->fuses);
+ accel_pci_dev->sku = hw_data->get_sku(hw_data);
+ /* If the device has no acceleration engines then ignore it. */
+ if (!hw_data->accel_mask || !hw_data->ae_mask ||
+ ((~hw_data->ae_mask) & 0x01)) {
+ dev_err(&pdev->dev, "No acceleration units found");
+ ret = -EFAULT;
+ goto out_err;
+ }
+
+ /* Create dev top level debugfs entry */
+ snprintf(name, sizeof(name), "%s%s_%02x:%02d.%02d",
+ ADF_DEVICE_NAME_PREFIX, hw_data->dev_class->name,
+ pdev->bus->number, PCI_SLOT(pdev->devfn),
+ PCI_FUNC(pdev->devfn));
+
+ accel_dev->debugfs_dir = debugfs_create_dir(name, NULL);
+ if (!accel_dev->debugfs_dir) {
+ dev_err(&pdev->dev, "Could not create debugfs dir %s\n", name);
+ ret = -EINVAL;
+ goto out_err;
+ }
+
+ /* Create device configuration table */
+ ret = adf_cfg_dev_add(accel_dev);
+ if (ret)
+ goto out_err;
+
+ /* enable PCI device */
+ if (pci_enable_device(pdev)) {
+ ret = -EFAULT;
+ goto out_err;
+ }
+
+ /* set dma identifier */
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
+ if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)))) {
+ dev_err(&pdev->dev, "No usable DMA configuration\n");
+ ret = -EFAULT;
+ goto out_err_disable;
+ } else {
+ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+ }
+
+ } else {
+ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
+ }
+
+ if (pci_request_regions(pdev, ADF_C3XXX_DEVICE_NAME)) {
+ ret = -EFAULT;
+ goto out_err_disable;
+ }
+
+ /* Read accelerator capabilities mask */
+ pci_read_config_dword(pdev, ADF_DEVICE_LEGFUSE_OFFSET,
+ &hw_data->accel_capabilities_mask);
+
+ /* Find and map all the device's BARS */
+ i = 0;
+ bar_mask = pci_select_bars(pdev, IORESOURCE_MEM);
+ for_each_set_bit(bar_nr, (const unsigned long *)&bar_mask,
+ ADF_PCI_MAX_BARS * 2) {
+ struct adf_bar *bar = &accel_pci_dev->pci_bars[i++];
+
+ bar->base_addr = pci_resource_start(pdev, bar_nr);
+ if (!bar->base_addr)
+ break;
+ bar->size = pci_resource_len(pdev, bar_nr);
+ bar->virt_addr = pci_iomap(accel_pci_dev->pci_dev, bar_nr, 0);
+ if (!bar->virt_addr) {
+ dev_err(&pdev->dev, "Failed to map BAR %d\n", bar_nr);
+ ret = -EFAULT;
+ goto out_err_free_reg;
+ }
+ }
+ pci_set_master(pdev);
+
+ if (adf_enable_aer(accel_dev, &adf_driver)) {
+ dev_err(&pdev->dev, "Failed to enable aer\n");
+ ret = -EFAULT;
+ goto out_err_free_reg;
+ }
+
+ if (pci_save_state(pdev)) {
+ dev_err(&pdev->dev, "Failed to save pci state\n");
+ ret = -ENOMEM;
+ goto out_err_free_reg;
+ }
+
+ ret = qat_crypto_dev_config(accel_dev);
+ if (ret)
+ goto out_err_free_reg;
+
+ ret = adf_dev_init(accel_dev);
+ if (ret)
+ goto out_err_dev_shutdown;
+
+ ret = adf_dev_start(accel_dev);
+ if (ret)
+ goto out_err_dev_stop;
+
+ return ret;
+
+out_err_dev_stop:
+ adf_dev_stop(accel_dev);
+out_err_dev_shutdown:
+ adf_dev_shutdown(accel_dev);
+out_err_free_reg:
+ pci_release_regions(accel_pci_dev->pci_dev);
+out_err_disable:
+ pci_disable_device(accel_pci_dev->pci_dev);
+out_err:
+ adf_cleanup_accel(accel_dev);
+ kfree(accel_dev);
+ return ret;
+}
+
+static void adf_remove(struct pci_dev *pdev)
+{
+ struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev);
+
+ if (!accel_dev) {
+ pr_err("QAT: Driver removal failed\n");
+ return;
+ }
+ if (adf_dev_stop(accel_dev))
+ dev_err(&GET_DEV(accel_dev), "Failed to stop QAT accel dev\n");
+
+ adf_dev_shutdown(accel_dev);
+ adf_disable_aer(accel_dev);
+ adf_cleanup_accel(accel_dev);
+ adf_cleanup_pci_dev(accel_dev);
+ kfree(accel_dev);
+}
+
+static int __init adfdrv_init(void)
+{
+ request_module("intel_qat");
+
+ if (pci_register_driver(&adf_driver)) {
+ pr_err("QAT: Driver initialization failed\n");
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static void __exit adfdrv_release(void)
+{
+ pci_unregister_driver(&adf_driver);
+}
+
+module_init(adfdrv_init);
+module_exit(adfdrv_release);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Intel");
+MODULE_DESCRIPTION("Intel(R) QuickAssist Technology");
+MODULE_VERSION(ADF_DRV_VERSION);
diff --git a/drivers/crypto/qat/qat_c3xxxvf/Makefile b/drivers/crypto/qat/qat_c3xxxvf/Makefile
new file mode 100644
index 000000000000..16d178e2eaa2
--- /dev/null
+++ b/drivers/crypto/qat/qat_c3xxxvf/Makefile
@@ -0,0 +1,3 @@
+ccflags-y := -I$(src)/../qat_common
+obj-$(CONFIG_CRYPTO_DEV_QAT_C3XXXVF) += qat_c3xxxvf.o
+qat_c3xxxvf-objs := adf_drv.o adf_c3xxxvf_hw_data.o
diff --git a/drivers/crypto/qat/qat_c3xxxvf/adf_c3xxxvf_hw_data.c b/drivers/crypto/qat/qat_c3xxxvf/adf_c3xxxvf_hw_data.c
new file mode 100644
index 000000000000..1af321c2ce1a
--- /dev/null
+++ b/drivers/crypto/qat/qat_c3xxxvf/adf_c3xxxvf_hw_data.c
@@ -0,0 +1,173 @@
+/*
+ This file is provided under a dual BSD/GPLv2 license. When using or
+ redistributing this file, you may do so under either license.
+
+ GPL LICENSE SUMMARY
+ Copyright(c) 2015 Intel Corporation.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ Contact Information:
+ qat-linux@intel.com
+
+ BSD LICENSE
+ Copyright(c) 2015 Intel Corporation.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include <adf_accel_devices.h>
+#include <adf_pf2vf_msg.h>
+#include <adf_common_drv.h>
+#include "adf_c3xxxvf_hw_data.h"
+
+static struct adf_hw_device_class c3xxxiov_class = {
+ .name = ADF_C3XXXVF_DEVICE_NAME,
+ .type = DEV_C3XXXVF,
+ .instances = 0
+};
+
+static u32 get_accel_mask(u32 fuse)
+{
+ return ADF_C3XXXIOV_ACCELERATORS_MASK;
+}
+
+static u32 get_ae_mask(u32 fuse)
+{
+ return ADF_C3XXXIOV_ACCELENGINES_MASK;
+}
+
+static u32 get_num_accels(struct adf_hw_device_data *self)
+{
+ return ADF_C3XXXIOV_MAX_ACCELERATORS;
+}
+
+static u32 get_num_aes(struct adf_hw_device_data *self)
+{
+ return ADF_C3XXXIOV_MAX_ACCELENGINES;
+}
+
+static u32 get_misc_bar_id(struct adf_hw_device_data *self)
+{
+ return ADF_C3XXXIOV_PMISC_BAR;
+}
+
+static u32 get_etr_bar_id(struct adf_hw_device_data *self)
+{
+ return ADF_C3XXXIOV_ETR_BAR;
+}
+
+static enum dev_sku_info get_sku(struct adf_hw_device_data *self)
+{
+ return DEV_SKU_VF;
+}
+
+static u32 get_pf2vf_offset(u32 i)
+{
+ return ADF_C3XXXIOV_PF2VF_OFFSET;
+}
+
+static u32 get_vintmsk_offset(u32 i)
+{
+ return ADF_C3XXXIOV_VINTMSK_OFFSET;
+}
+
+static int adf_vf_int_noop(struct adf_accel_dev *accel_dev)
+{
+ return 0;
+}
+
+static void adf_vf_void_noop(struct adf_accel_dev *accel_dev)
+{
+}
+
+static int adf_vf2pf_init(struct adf_accel_dev *accel_dev)
+{
+ u32 msg = (ADF_VF2PF_MSGORIGIN_SYSTEM |
+ (ADF_VF2PF_MSGTYPE_INIT << ADF_VF2PF_MSGTYPE_SHIFT));
+
+ if (adf_iov_putmsg(accel_dev, msg, 0)) {
+ dev_err(&GET_DEV(accel_dev),
+ "Failed to send Init event to PF\n");
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static void adf_vf2pf_shutdown(struct adf_accel_dev *accel_dev)
+{
+ u32 msg = (ADF_VF2PF_MSGORIGIN_SYSTEM |
+ (ADF_VF2PF_MSGTYPE_SHUTDOWN << ADF_VF2PF_MSGTYPE_SHIFT));
+
+ if (adf_iov_putmsg(accel_dev, msg, 0))
+ dev_err(&GET_DEV(accel_dev),
+ "Failed to send Shutdown event to PF\n");
+}
+
+void adf_init_hw_data_c3xxxiov(struct adf_hw_device_data *hw_data)
+{
+ hw_data->dev_class = &c3xxxiov_class;
+ hw_data->num_banks = ADF_C3XXXIOV_ETR_MAX_BANKS;
+ hw_data->num_accel = ADF_C3XXXIOV_MAX_ACCELERATORS;
+ hw_data->num_logical_accel = 1;
+ hw_data->num_engines = ADF_C3XXXIOV_MAX_ACCELENGINES;
+ hw_data->tx_rx_gap = ADF_C3XXXIOV_RX_RINGS_OFFSET;
+ hw_data->tx_rings_mask = ADF_C3XXXIOV_TX_RINGS_MASK;
+ hw_data->alloc_irq = adf_vf_isr_resource_alloc;
+ hw_data->free_irq = adf_vf_isr_resource_free;
+ hw_data->enable_error_correction = adf_vf_void_noop;
+ hw_data->init_admin_comms = adf_vf_int_noop;
+ hw_data->exit_admin_comms = adf_vf_void_noop;
+ hw_data->send_admin_init = adf_vf2pf_init;
+ hw_data->init_arb = adf_vf_int_noop;
+ hw_data->exit_arb = adf_vf_void_noop;
+ hw_data->disable_iov = adf_vf2pf_shutdown;
+ hw_data->get_accel_mask = get_accel_mask;
+ hw_data->get_ae_mask = get_ae_mask;
+ hw_data->get_num_accels = get_num_accels;
+ hw_data->get_num_aes = get_num_aes;
+ hw_data->get_etr_bar_id = get_etr_bar_id;
+ hw_data->get_misc_bar_id = get_misc_bar_id;
+ hw_data->get_pf2vf_offset = get_pf2vf_offset;
+ hw_data->get_vintmsk_offset = get_vintmsk_offset;
+ hw_data->get_sku = get_sku;
+ hw_data->enable_ints = adf_vf_void_noop;
+ hw_data->enable_vf2pf_comms = adf_enable_vf2pf_comms;
+ hw_data->min_iov_compat_ver = ADF_PFVF_COMPATIBILITY_VERSION;
+ hw_data->dev_class->instances++;
+ adf_devmgr_update_class_index(hw_data);
+}
+
+void adf_clean_hw_data_c3xxxiov(struct adf_hw_device_data *hw_data)
+{
+ hw_data->dev_class->instances--;
+ adf_devmgr_update_class_index(hw_data);
+}
diff --git a/drivers/crypto/qat/qat_dh895xccvf/adf_drv.h b/drivers/crypto/qat/qat_c3xxxvf/adf_c3xxxvf_hw_data.h
index e270e4a63d14..934f216acf39 100644
--- a/drivers/crypto/qat/qat_dh895xccvf/adf_drv.h
+++ b/drivers/crypto/qat/qat_c3xxxvf/adf_c3xxxvf_hw_data.h
@@ -3,7 +3,7 @@
redistributing this file, you may do so under either license.
GPL LICENSE SUMMARY
- Copyright(c) 2014 Intel Corporation.
+ Copyright(c) 2015 Intel Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation.
@@ -17,7 +17,7 @@
qat-linux@intel.com
BSD LICENSE
- Copyright(c) 2014 Intel Corporation.
+ Copyright(c) 2015 Intel Corporation.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
@@ -44,14 +44,21 @@
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef ADF_DH895xVF_DRV_H_
-#define ADF_DH895xVF_DRV_H_
-#include <adf_accel_devices.h>
-#include <adf_transport.h>
-
-void adf_init_hw_data_dh895xcciov(struct adf_hw_device_data *hw_data);
-void adf_clean_hw_data_dh895xcciov(struct adf_hw_device_data *hw_data);
-int adf_vf_isr_resource_alloc(struct adf_accel_dev *accel_dev);
-void adf_vf_isr_resource_free(struct adf_accel_dev *accel_dev);
-void adf_update_ring_arb_enable(struct adf_etr_ring_data *ring);
+#ifndef ADF_C3XXXVF_HW_DATA_H_
+#define ADF_C3XXXVF_HW_DATA_H_
+
+#define ADF_C3XXXIOV_PMISC_BAR 1
+#define ADF_C3XXXIOV_ACCELERATORS_MASK 0x1
+#define ADF_C3XXXIOV_ACCELENGINES_MASK 0x1
+#define ADF_C3XXXIOV_MAX_ACCELERATORS 1
+#define ADF_C3XXXIOV_MAX_ACCELENGINES 1
+#define ADF_C3XXXIOV_RX_RINGS_OFFSET 8
+#define ADF_C3XXXIOV_TX_RINGS_MASK 0xFF
+#define ADF_C3XXXIOV_ETR_BAR 0
+#define ADF_C3XXXIOV_ETR_MAX_BANKS 1
+#define ADF_C3XXXIOV_PF2VF_OFFSET 0x200
+#define ADF_C3XXXIOV_VINTMSK_OFFSET 0x208
+
+void adf_init_hw_data_c3xxxiov(struct adf_hw_device_data *hw_data);
+void adf_clean_hw_data_c3xxxiov(struct adf_hw_device_data *hw_data);
#endif
diff --git a/drivers/crypto/qat/qat_c3xxxvf/adf_drv.c b/drivers/crypto/qat/qat_c3xxxvf/adf_drv.c
new file mode 100644
index 000000000000..1ac4ae90e072
--- /dev/null
+++ b/drivers/crypto/qat/qat_c3xxxvf/adf_drv.c
@@ -0,0 +1,305 @@
+/*
+ This file is provided under a dual BSD/GPLv2 license. When using or
+ redistributing this file, you may do so under either license.
+
+ GPL LICENSE SUMMARY
+ Copyright(c) 2014 Intel Corporation.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ Contact Information:
+ qat-linux@intel.com
+
+ BSD LICENSE
+ Copyright(c) 2014 Intel Corporation.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/io.h>
+#include <adf_accel_devices.h>
+#include <adf_common_drv.h>
+#include <adf_cfg.h>
+#include "adf_c3xxxvf_hw_data.h"
+
+#define ADF_SYSTEM_DEVICE(device_id) \
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, device_id)}
+
+static const struct pci_device_id adf_pci_tbl[] = {
+ ADF_SYSTEM_DEVICE(ADF_C3XXXIOV_PCI_DEVICE_ID),
+ {0,}
+};
+MODULE_DEVICE_TABLE(pci, adf_pci_tbl);
+
+static int adf_probe(struct pci_dev *dev, const struct pci_device_id *ent);
+static void adf_remove(struct pci_dev *dev);
+
+static struct pci_driver adf_driver = {
+ .id_table = adf_pci_tbl,
+ .name = ADF_C3XXXVF_DEVICE_NAME,
+ .probe = adf_probe,
+ .remove = adf_remove,
+};
+
+static void adf_cleanup_pci_dev(struct adf_accel_dev *accel_dev)
+{
+ pci_release_regions(accel_dev->accel_pci_dev.pci_dev);
+ pci_disable_device(accel_dev->accel_pci_dev.pci_dev);
+}
+
+static void adf_cleanup_accel(struct adf_accel_dev *accel_dev)
+{
+ struct adf_accel_pci *accel_pci_dev = &accel_dev->accel_pci_dev;
+ struct adf_accel_dev *pf;
+ int i;
+
+ for (i = 0; i < ADF_PCI_MAX_BARS; i++) {
+ struct adf_bar *bar = &accel_pci_dev->pci_bars[i];
+
+ if (bar->virt_addr)
+ pci_iounmap(accel_pci_dev->pci_dev, bar->virt_addr);
+ }
+
+ if (accel_dev->hw_device) {
+ switch (accel_pci_dev->pci_dev->device) {
+ case ADF_C3XXXIOV_PCI_DEVICE_ID:
+ adf_clean_hw_data_c3xxxiov(accel_dev->hw_device);
+ break;
+ default:
+ break;
+ }
+ kfree(accel_dev->hw_device);
+ accel_dev->hw_device = NULL;
+ }
+ adf_cfg_dev_remove(accel_dev);
+ debugfs_remove(accel_dev->debugfs_dir);
+ pf = adf_devmgr_pci_to_accel_dev(accel_pci_dev->pci_dev->physfn);
+ adf_devmgr_rm_dev(accel_dev, pf);
+}
+
+static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct adf_accel_dev *accel_dev;
+ struct adf_accel_dev *pf;
+ struct adf_accel_pci *accel_pci_dev;
+ struct adf_hw_device_data *hw_data;
+ char name[ADF_DEVICE_NAME_LENGTH];
+ unsigned int i, bar_nr;
+ int ret, bar_mask;
+
+ switch (ent->device) {
+ case ADF_C3XXXIOV_PCI_DEVICE_ID:
+ break;
+ default:
+ dev_err(&pdev->dev, "Invalid device 0x%x.\n", ent->device);
+ return -ENODEV;
+ }
+
+ accel_dev = kzalloc_node(sizeof(*accel_dev), GFP_KERNEL,
+ dev_to_node(&pdev->dev));
+ if (!accel_dev)
+ return -ENOMEM;
+
+ accel_dev->is_vf = true;
+ pf = adf_devmgr_pci_to_accel_dev(pdev->physfn);
+ accel_pci_dev = &accel_dev->accel_pci_dev;
+ accel_pci_dev->pci_dev = pdev;
+
+ /* Add accel device to accel table */
+ if (adf_devmgr_add_dev(accel_dev, pf)) {
+ dev_err(&pdev->dev, "Failed to add new accelerator device.\n");
+ kfree(accel_dev);
+ return -EFAULT;
+ }
+ INIT_LIST_HEAD(&accel_dev->crypto_list);
+
+ accel_dev->owner = THIS_MODULE;
+ /* Allocate and configure device configuration structure */
+ hw_data = kzalloc_node(sizeof(*hw_data), GFP_KERNEL,
+ dev_to_node(&pdev->dev));
+ if (!hw_data) {
+ ret = -ENOMEM;
+ goto out_err;
+ }
+ accel_dev->hw_device = hw_data;
+ adf_init_hw_data_c3xxxiov(accel_dev->hw_device);
+
+ /* Get Accelerators and Accelerators Engines masks */
+ hw_data->accel_mask = hw_data->get_accel_mask(hw_data->fuses);
+ hw_data->ae_mask = hw_data->get_ae_mask(hw_data->fuses);
+ accel_pci_dev->sku = hw_data->get_sku(hw_data);
+
+ /* Create dev top level debugfs entry */
+ snprintf(name, sizeof(name), "%s%s_%02x:%02d.%02d",
+ ADF_DEVICE_NAME_PREFIX, hw_data->dev_class->name,
+ pdev->bus->number, PCI_SLOT(pdev->devfn),
+ PCI_FUNC(pdev->devfn));
+
+ accel_dev->debugfs_dir = debugfs_create_dir(name, NULL);
+ if (!accel_dev->debugfs_dir) {
+ dev_err(&pdev->dev, "Could not create debugfs dir %s\n", name);
+ ret = -EINVAL;
+ goto out_err;
+ }
+
+ /* Create device configuration table */
+ ret = adf_cfg_dev_add(accel_dev);
+ if (ret)
+ goto out_err;
+
+ /* enable PCI device */
+ if (pci_enable_device(pdev)) {
+ ret = -EFAULT;
+ goto out_err;
+ }
+
+ /* set dma identifier */
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
+ if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)))) {
+ dev_err(&pdev->dev, "No usable DMA configuration\n");
+ ret = -EFAULT;
+ goto out_err_disable;
+ } else {
+ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+ }
+
+ } else {
+ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
+ }
+
+ if (pci_request_regions(pdev, ADF_C3XXXVF_DEVICE_NAME)) {
+ ret = -EFAULT;
+ goto out_err_disable;
+ }
+
+ /* Find and map all the device's BARS */
+ i = 0;
+ bar_mask = pci_select_bars(pdev, IORESOURCE_MEM);
+ for_each_set_bit(bar_nr, (const unsigned long *)&bar_mask,
+ ADF_PCI_MAX_BARS * 2) {
+ struct adf_bar *bar = &accel_pci_dev->pci_bars[i++];
+
+ bar->base_addr = pci_resource_start(pdev, bar_nr);
+ if (!bar->base_addr)
+ break;
+ bar->size = pci_resource_len(pdev, bar_nr);
+ bar->virt_addr = pci_iomap(accel_pci_dev->pci_dev, bar_nr, 0);
+ if (!bar->virt_addr) {
+ dev_err(&pdev->dev, "Failed to map BAR %d\n", bar_nr);
+ ret = -EFAULT;
+ goto out_err_free_reg;
+ }
+ }
+ pci_set_master(pdev);
+ /* Completion for VF2PF request/response message exchange */
+ init_completion(&accel_dev->vf.iov_msg_completion);
+
+ ret = qat_crypto_dev_config(accel_dev);
+ if (ret)
+ goto out_err_free_reg;
+
+ ret = adf_dev_init(accel_dev);
+ if (ret)
+ goto out_err_dev_shutdown;
+
+ ret = adf_dev_start(accel_dev);
+ if (ret)
+ goto out_err_dev_stop;
+
+ return ret;
+
+out_err_dev_stop:
+ adf_dev_stop(accel_dev);
+out_err_dev_shutdown:
+ adf_dev_shutdown(accel_dev);
+out_err_free_reg:
+ pci_release_regions(accel_pci_dev->pci_dev);
+out_err_disable:
+ pci_disable_device(accel_pci_dev->pci_dev);
+out_err:
+ adf_cleanup_accel(accel_dev);
+ kfree(accel_dev);
+ return ret;
+}
+
+static void adf_remove(struct pci_dev *pdev)
+{
+ struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev);
+
+ if (!accel_dev) {
+ pr_err("QAT: Driver removal failed\n");
+ return;
+ }
+ if (adf_dev_stop(accel_dev))
+ dev_err(&GET_DEV(accel_dev), "Failed to stop QAT accel dev\n");
+
+ adf_dev_shutdown(accel_dev);
+ adf_cleanup_accel(accel_dev);
+ adf_cleanup_pci_dev(accel_dev);
+ kfree(accel_dev);
+}
+
+static int __init adfdrv_init(void)
+{
+ request_module("intel_qat");
+
+ if (pci_register_driver(&adf_driver)) {
+ pr_err("QAT: Driver initialization failed\n");
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static void __exit adfdrv_release(void)
+{
+ pci_unregister_driver(&adf_driver);
+ adf_clean_vf_map(true);
+}
+
+module_init(adfdrv_init);
+module_exit(adfdrv_release);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Intel");
+MODULE_DESCRIPTION("Intel(R) QuickAssist Technology");
+MODULE_VERSION(ADF_DRV_VERSION);
diff --git a/drivers/crypto/qat/qat_c62x/Makefile b/drivers/crypto/qat/qat_c62x/Makefile
new file mode 100644
index 000000000000..bd75ace59b76
--- /dev/null
+++ b/drivers/crypto/qat/qat_c62x/Makefile
@@ -0,0 +1,3 @@
+ccflags-y := -I$(src)/../qat_common
+obj-$(CONFIG_CRYPTO_DEV_QAT_C62X) += qat_c62x.o
+qat_c62x-objs := adf_drv.o adf_c62x_hw_data.o
diff --git a/drivers/crypto/qat/qat_c62x/adf_c62x_hw_data.c b/drivers/crypto/qat/qat_c62x/adf_c62x_hw_data.c
new file mode 100644
index 000000000000..879e04cae714
--- /dev/null
+++ b/drivers/crypto/qat/qat_c62x/adf_c62x_hw_data.c
@@ -0,0 +1,248 @@
+/*
+ This file is provided under a dual BSD/GPLv2 license. When using or
+ redistributing this file, you may do so under either license.
+
+ GPL LICENSE SUMMARY
+ Copyright(c) 2014 Intel Corporation.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ Contact Information:
+ qat-linux@intel.com
+
+ BSD LICENSE
+ Copyright(c) 2014 Intel Corporation.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include <adf_accel_devices.h>
+#include <adf_common_drv.h>
+#include <adf_pf2vf_msg.h>
+#include "adf_c62x_hw_data.h"
+
+/* Worker thread to service arbiter mappings based on dev SKUs */
+static const u32 thrd_to_arb_map_8_me_sku[] = {
+ 0x12222AAA, 0x11222AAA, 0x12222AAA, 0x11222AAA, 0x12222AAA,
+ 0x11222AAA, 0x12222AAA, 0x11222AAA, 0, 0
+};
+
+static const u32 thrd_to_arb_map_10_me_sku[] = {
+ 0x12222AAA, 0x11222AAA, 0x12222AAA, 0x11222AAA, 0x12222AAA,
+ 0x11222AAA, 0x12222AAA, 0x11222AAA, 0x12222AAA, 0x11222AAA
+};
+
+static struct adf_hw_device_class c62x_class = {
+ .name = ADF_C62X_DEVICE_NAME,
+ .type = DEV_C62X,
+ .instances = 0
+};
+
+static u32 get_accel_mask(u32 fuse)
+{
+ return (~fuse) >> ADF_C62X_ACCELERATORS_REG_OFFSET &
+ ADF_C62X_ACCELERATORS_MASK;
+}
+
+static u32 get_ae_mask(u32 fuse)
+{
+ return (~fuse) & ADF_C62X_ACCELENGINES_MASK;
+}
+
+static u32 get_num_accels(struct adf_hw_device_data *self)
+{
+ u32 i, ctr = 0;
+
+ if (!self || !self->accel_mask)
+ return 0;
+
+ for (i = 0; i < ADF_C62X_MAX_ACCELERATORS; i++) {
+ if (self->accel_mask & (1 << i))
+ ctr++;
+ }
+ return ctr;
+}
+
+static u32 get_num_aes(struct adf_hw_device_data *self)
+{
+ u32 i, ctr = 0;
+
+ if (!self || !self->ae_mask)
+ return 0;
+
+ for (i = 0; i < ADF_C62X_MAX_ACCELENGINES; i++) {
+ if (self->ae_mask & (1 << i))
+ ctr++;
+ }
+ return ctr;
+}
+
+static u32 get_misc_bar_id(struct adf_hw_device_data *self)
+{
+ return ADF_C62X_PMISC_BAR;
+}
+
+static u32 get_etr_bar_id(struct adf_hw_device_data *self)
+{
+ return ADF_C62X_ETR_BAR;
+}
+
+static u32 get_sram_bar_id(struct adf_hw_device_data *self)
+{
+ return ADF_C62X_SRAM_BAR;
+}
+
+static enum dev_sku_info get_sku(struct adf_hw_device_data *self)
+{
+ int aes = get_num_aes(self);
+
+ if (aes == 8)
+ return DEV_SKU_2;
+ else if (aes == 10)
+ return DEV_SKU_4;
+
+ return DEV_SKU_UNKNOWN;
+}
+
+static void adf_get_arbiter_mapping(struct adf_accel_dev *accel_dev,
+ u32 const **arb_map_config)
+{
+ switch (accel_dev->accel_pci_dev.sku) {
+ case DEV_SKU_2:
+ *arb_map_config = thrd_to_arb_map_8_me_sku;
+ break;
+ case DEV_SKU_4:
+ *arb_map_config = thrd_to_arb_map_10_me_sku;
+ break;
+ default:
+ dev_err(&GET_DEV(accel_dev),
+ "The configuration doesn't match any SKU");
+ *arb_map_config = NULL;
+ }
+}
+
+static u32 get_pf2vf_offset(u32 i)
+{
+ return ADF_C62X_PF2VF_OFFSET(i);
+}
+
+static u32 get_vintmsk_offset(u32 i)
+{
+ return ADF_C62X_VINTMSK_OFFSET(i);
+}
+
+static void adf_enable_error_correction(struct adf_accel_dev *accel_dev)
+{
+ struct adf_hw_device_data *hw_device = accel_dev->hw_device;
+ struct adf_bar *misc_bar = &GET_BARS(accel_dev)[ADF_C62X_PMISC_BAR];
+ void __iomem *csr = misc_bar->virt_addr;
+ unsigned int val, i;
+
+ /* Enable Accel Engine error detection & correction */
+ for (i = 0; i < hw_device->get_num_aes(hw_device); i++) {
+ val = ADF_CSR_RD(csr, ADF_C62X_AE_CTX_ENABLES(i));
+ val |= ADF_C62X_ENABLE_AE_ECC_ERR;
+ ADF_CSR_WR(csr, ADF_C62X_AE_CTX_ENABLES(i), val);
+ val = ADF_CSR_RD(csr, ADF_C62X_AE_MISC_CONTROL(i));
+ val |= ADF_C62X_ENABLE_AE_ECC_PARITY_CORR;
+ ADF_CSR_WR(csr, ADF_C62X_AE_MISC_CONTROL(i), val);
+ }
+
+ /* Enable shared memory error detection & correction */
+ for (i = 0; i < hw_device->get_num_accels(hw_device); i++) {
+ val = ADF_CSR_RD(csr, ADF_C62X_UERRSSMSH(i));
+ val |= ADF_C62X_ERRSSMSH_EN;
+ ADF_CSR_WR(csr, ADF_C62X_UERRSSMSH(i), val);
+ val = ADF_CSR_RD(csr, ADF_C62X_CERRSSMSH(i));
+ val |= ADF_C62X_ERRSSMSH_EN;
+ ADF_CSR_WR(csr, ADF_C62X_CERRSSMSH(i), val);
+ }
+}
+
+static void adf_enable_ints(struct adf_accel_dev *accel_dev)
+{
+ void __iomem *addr;
+
+ addr = (&GET_BARS(accel_dev)[ADF_C62X_PMISC_BAR])->virt_addr;
+
+ /* Enable bundle and misc interrupts */
+ ADF_CSR_WR(addr, ADF_C62X_SMIAPF0_MASK_OFFSET,
+ ADF_C62X_SMIA0_MASK);
+ ADF_CSR_WR(addr, ADF_C62X_SMIAPF1_MASK_OFFSET,
+ ADF_C62X_SMIA1_MASK);
+}
+
+static int adf_pf_enable_vf2pf_comms(struct adf_accel_dev *accel_dev)
+{
+ return 0;
+}
+
+void adf_init_hw_data_c62x(struct adf_hw_device_data *hw_data)
+{
+ hw_data->dev_class = &c62x_class;
+ hw_data->instance_id = c62x_class.instances++;
+ hw_data->num_banks = ADF_C62X_ETR_MAX_BANKS;
+ hw_data->num_accel = ADF_C62X_MAX_ACCELERATORS;
+ hw_data->num_logical_accel = 1;
+ hw_data->num_engines = ADF_C62X_MAX_ACCELENGINES;
+ hw_data->tx_rx_gap = ADF_C62X_RX_RINGS_OFFSET;
+ hw_data->tx_rings_mask = ADF_C62X_TX_RINGS_MASK;
+ hw_data->alloc_irq = adf_isr_resource_alloc;
+ hw_data->free_irq = adf_isr_resource_free;
+ hw_data->enable_error_correction = adf_enable_error_correction;
+ hw_data->get_accel_mask = get_accel_mask;
+ hw_data->get_ae_mask = get_ae_mask;
+ hw_data->get_num_accels = get_num_accels;
+ hw_data->get_num_aes = get_num_aes;
+ hw_data->get_sram_bar_id = get_sram_bar_id;
+ hw_data->get_etr_bar_id = get_etr_bar_id;
+ hw_data->get_misc_bar_id = get_misc_bar_id;
+ hw_data->get_pf2vf_offset = get_pf2vf_offset;
+ hw_data->get_vintmsk_offset = get_vintmsk_offset;
+ hw_data->get_sku = get_sku;
+ hw_data->fw_name = ADF_C62X_FW;
+ hw_data->fw_mmp_name = ADF_C62X_MMP;
+ hw_data->init_admin_comms = adf_init_admin_comms;
+ hw_data->exit_admin_comms = adf_exit_admin_comms;
+ hw_data->disable_iov = adf_disable_sriov;
+ hw_data->send_admin_init = adf_send_admin_init;
+ hw_data->init_arb = adf_init_arb;
+ hw_data->exit_arb = adf_exit_arb;
+ hw_data->get_arb_mapping = adf_get_arbiter_mapping;
+ hw_data->enable_ints = adf_enable_ints;
+ hw_data->enable_vf2pf_comms = adf_pf_enable_vf2pf_comms;
+ hw_data->min_iov_compat_ver = ADF_PFVF_COMPATIBILITY_VERSION;
+}
+
+void adf_clean_hw_data_c62x(struct adf_hw_device_data *hw_data)
+{
+ hw_data->dev_class->instances--;
+}
diff --git a/drivers/crypto/qat/qat_c62x/adf_c62x_hw_data.h b/drivers/crypto/qat/qat_c62x/adf_c62x_hw_data.h
new file mode 100644
index 000000000000..17a8a32d5c63
--- /dev/null
+++ b/drivers/crypto/qat/qat_c62x/adf_c62x_hw_data.h
@@ -0,0 +1,84 @@
+/*
+ This file is provided under a dual BSD/GPLv2 license. When using or
+ redistributing this file, you may do so under either license.
+
+ GPL LICENSE SUMMARY
+ Copyright(c) 2014 Intel Corporation.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ Contact Information:
+ qat-linux@intel.com
+
+ BSD LICENSE
+ Copyright(c) 2014 Intel Corporation.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef ADF_C62X_HW_DATA_H_
+#define ADF_C62X_HW_DATA_H_
+
+/* PCIe configuration space */
+#define ADF_C62X_SRAM_BAR 0
+#define ADF_C62X_PMISC_BAR 1
+#define ADF_C62X_ETR_BAR 2
+#define ADF_C62X_RX_RINGS_OFFSET 8
+#define ADF_C62X_TX_RINGS_MASK 0xFF
+#define ADF_C62X_MAX_ACCELERATORS 5
+#define ADF_C62X_MAX_ACCELENGINES 10
+#define ADF_C62X_ACCELERATORS_REG_OFFSET 16
+#define ADF_C62X_ACCELERATORS_MASK 0x1F
+#define ADF_C62X_ACCELENGINES_MASK 0x3FF
+#define ADF_C62X_ETR_MAX_BANKS 16
+#define ADF_C62X_SMIAPF0_MASK_OFFSET (0x3A000 + 0x28)
+#define ADF_C62X_SMIAPF1_MASK_OFFSET (0x3A000 + 0x30)
+#define ADF_C62X_SMIA0_MASK 0xFFFF
+#define ADF_C62X_SMIA1_MASK 0x1
+/* Error detection and correction */
+#define ADF_C62X_AE_CTX_ENABLES(i) (i * 0x1000 + 0x20818)
+#define ADF_C62X_AE_MISC_CONTROL(i) (i * 0x1000 + 0x20960)
+#define ADF_C62X_ENABLE_AE_ECC_ERR BIT(28)
+#define ADF_C62X_ENABLE_AE_ECC_PARITY_CORR (BIT(24) | BIT(12))
+#define ADF_C62X_UERRSSMSH(i) (i * 0x4000 + 0x18)
+#define ADF_C62X_CERRSSMSH(i) (i * 0x4000 + 0x10)
+#define ADF_C62X_ERRSSMSH_EN BIT(3)
+
+#define ADF_C62X_PF2VF_OFFSET(i) (0x3A000 + 0x280 + ((i) * 0x04))
+#define ADF_C62X_VINTMSK_OFFSET(i) (0x3A000 + 0x200 + ((i) * 0x04))
+
+/* Firmware Binary */
+#define ADF_C62X_FW "qat_c62x.bin"
+#define ADF_C62X_MMP "qat_c62x_mmp.bin"
+
+void adf_init_hw_data_c62x(struct adf_hw_device_data *hw_data);
+void adf_clean_hw_data_c62x(struct adf_hw_device_data *hw_data);
+#endif
diff --git a/drivers/crypto/qat/qat_c62x/adf_drv.c b/drivers/crypto/qat/qat_c62x/adf_drv.c
new file mode 100644
index 000000000000..512c56509718
--- /dev/null
+++ b/drivers/crypto/qat/qat_c62x/adf_drv.c
@@ -0,0 +1,335 @@
+/*
+ This file is provided under a dual BSD/GPLv2 license. When using or
+ redistributing this file, you may do so under either license.
+
+ GPL LICENSE SUMMARY
+ Copyright(c) 2014 Intel Corporation.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ Contact Information:
+ qat-linux@intel.com
+
+ BSD LICENSE
+ Copyright(c) 2014 Intel Corporation.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/io.h>
+#include <adf_accel_devices.h>
+#include <adf_common_drv.h>
+#include <adf_cfg.h>
+#include "adf_c62x_hw_data.h"
+
+#define ADF_SYSTEM_DEVICE(device_id) \
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, device_id)}
+
+static const struct pci_device_id adf_pci_tbl[] = {
+ ADF_SYSTEM_DEVICE(ADF_C62X_PCI_DEVICE_ID),
+ {0,}
+};
+MODULE_DEVICE_TABLE(pci, adf_pci_tbl);
+
+static int adf_probe(struct pci_dev *dev, const struct pci_device_id *ent);
+static void adf_remove(struct pci_dev *dev);
+
+static struct pci_driver adf_driver = {
+ .id_table = adf_pci_tbl,
+ .name = ADF_C62X_DEVICE_NAME,
+ .probe = adf_probe,
+ .remove = adf_remove,
+ .sriov_configure = adf_sriov_configure,
+};
+
+static void adf_cleanup_pci_dev(struct adf_accel_dev *accel_dev)
+{
+ pci_release_regions(accel_dev->accel_pci_dev.pci_dev);
+ pci_disable_device(accel_dev->accel_pci_dev.pci_dev);
+}
+
+static void adf_cleanup_accel(struct adf_accel_dev *accel_dev)
+{
+ struct adf_accel_pci *accel_pci_dev = &accel_dev->accel_pci_dev;
+ int i;
+
+ for (i = 0; i < ADF_PCI_MAX_BARS; i++) {
+ struct adf_bar *bar = &accel_pci_dev->pci_bars[i];
+
+ if (bar->virt_addr)
+ pci_iounmap(accel_pci_dev->pci_dev, bar->virt_addr);
+ }
+
+ if (accel_dev->hw_device) {
+ switch (accel_pci_dev->pci_dev->device) {
+ case ADF_C62X_PCI_DEVICE_ID:
+ adf_clean_hw_data_c62x(accel_dev->hw_device);
+ break;
+ default:
+ break;
+ }
+ kfree(accel_dev->hw_device);
+ accel_dev->hw_device = NULL;
+ }
+ adf_cfg_dev_remove(accel_dev);
+ debugfs_remove(accel_dev->debugfs_dir);
+ adf_devmgr_rm_dev(accel_dev, NULL);
+}
+
+static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct adf_accel_dev *accel_dev;
+ struct adf_accel_pci *accel_pci_dev;
+ struct adf_hw_device_data *hw_data;
+ char name[ADF_DEVICE_NAME_LENGTH];
+ unsigned int i, bar_nr;
+ int ret, bar_mask;
+
+ switch (ent->device) {
+ case ADF_C62X_PCI_DEVICE_ID:
+ break;
+ default:
+ dev_err(&pdev->dev, "Invalid device 0x%x.\n", ent->device);
+ return -ENODEV;
+ }
+
+ if (num_possible_nodes() > 1 && dev_to_node(&pdev->dev) < 0) {
+ /* If the accelerator is connected to a node with no memory
+ * there is no point in using the accelerator since the remote
+ * memory transaction will be very slow. */
+ dev_err(&pdev->dev, "Invalid NUMA configuration.\n");
+ return -EINVAL;
+ }
+
+ accel_dev = kzalloc_node(sizeof(*accel_dev), GFP_KERNEL,
+ dev_to_node(&pdev->dev));
+ if (!accel_dev)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&accel_dev->crypto_list);
+ accel_pci_dev = &accel_dev->accel_pci_dev;
+ accel_pci_dev->pci_dev = pdev;
+
+ /* Add accel device to accel table.
+ * This should be called before adf_cleanup_accel is called */
+ if (adf_devmgr_add_dev(accel_dev, NULL)) {
+ dev_err(&pdev->dev, "Failed to add new accelerator device.\n");
+ kfree(accel_dev);
+ return -EFAULT;
+ }
+
+ accel_dev->owner = THIS_MODULE;
+ /* Allocate and configure device configuration structure */
+ hw_data = kzalloc_node(sizeof(*hw_data), GFP_KERNEL,
+ dev_to_node(&pdev->dev));
+ if (!hw_data) {
+ ret = -ENOMEM;
+ goto out_err;
+ }
+
+ accel_dev->hw_device = hw_data;
+ adf_init_hw_data_c62x(accel_dev->hw_device);
+ pci_read_config_byte(pdev, PCI_REVISION_ID, &accel_pci_dev->revid);
+ pci_read_config_dword(pdev, ADF_DEVICE_FUSECTL_OFFSET,
+ &hw_data->fuses);
+
+ /* Get Accelerators and Accelerators Engines masks */
+ hw_data->accel_mask = hw_data->get_accel_mask(hw_data->fuses);
+ hw_data->ae_mask = hw_data->get_ae_mask(hw_data->fuses);
+ accel_pci_dev->sku = hw_data->get_sku(hw_data);
+ /* If the device has no acceleration engines then ignore it. */
+ if (!hw_data->accel_mask || !hw_data->ae_mask ||
+ ((~hw_data->ae_mask) & 0x01)) {
+ dev_err(&pdev->dev, "No acceleration units found");
+ ret = -EFAULT;
+ goto out_err;
+ }
+
+ /* Create dev top level debugfs entry */
+ snprintf(name, sizeof(name), "%s%s_%02x:%02d.%02d",
+ ADF_DEVICE_NAME_PREFIX, hw_data->dev_class->name,
+ pdev->bus->number, PCI_SLOT(pdev->devfn),
+ PCI_FUNC(pdev->devfn));
+
+ accel_dev->debugfs_dir = debugfs_create_dir(name, NULL);
+ if (!accel_dev->debugfs_dir) {
+ dev_err(&pdev->dev, "Could not create debugfs dir %s\n", name);
+ ret = -EINVAL;
+ goto out_err;
+ }
+
+ /* Create device configuration table */
+ ret = adf_cfg_dev_add(accel_dev);
+ if (ret)
+ goto out_err;
+
+ /* enable PCI device */
+ if (pci_enable_device(pdev)) {
+ ret = -EFAULT;
+ goto out_err;
+ }
+
+ /* set dma identifier */
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
+ if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)))) {
+ dev_err(&pdev->dev, "No usable DMA configuration\n");
+ ret = -EFAULT;
+ goto out_err_disable;
+ } else {
+ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+ }
+
+ } else {
+ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
+ }
+
+ if (pci_request_regions(pdev, ADF_C62X_DEVICE_NAME)) {
+ ret = -EFAULT;
+ goto out_err_disable;
+ }
+
+ /* Read accelerator capabilities mask */
+ pci_read_config_dword(pdev, ADF_DEVICE_LEGFUSE_OFFSET,
+ &hw_data->accel_capabilities_mask);
+
+ /* Find and map all the device's BARS */
+ i = 0;
+ bar_mask = pci_select_bars(pdev, IORESOURCE_MEM);
+ for_each_set_bit(bar_nr, (const unsigned long *)&bar_mask,
+ ADF_PCI_MAX_BARS * 2) {
+ struct adf_bar *bar = &accel_pci_dev->pci_bars[i++];
+
+ bar->base_addr = pci_resource_start(pdev, bar_nr);
+ if (!bar->base_addr)
+ break;
+ bar->size = pci_resource_len(pdev, bar_nr);
+ bar->virt_addr = pci_iomap(accel_pci_dev->pci_dev, bar_nr, 0);
+ if (!bar->virt_addr) {
+ dev_err(&pdev->dev, "Failed to map BAR %d\n", bar_nr);
+ ret = -EFAULT;
+ goto out_err_free_reg;
+ }
+ }
+ pci_set_master(pdev);
+
+ if (adf_enable_aer(accel_dev, &adf_driver)) {
+ dev_err(&pdev->dev, "Failed to enable aer\n");
+ ret = -EFAULT;
+ goto out_err_free_reg;
+ }
+
+ if (pci_save_state(pdev)) {
+ dev_err(&pdev->dev, "Failed to save pci state\n");
+ ret = -ENOMEM;
+ goto out_err_free_reg;
+ }
+
+ ret = qat_crypto_dev_config(accel_dev);
+ if (ret)
+ goto out_err_free_reg;
+
+ ret = adf_dev_init(accel_dev);
+ if (ret)
+ goto out_err_dev_shutdown;
+
+ ret = adf_dev_start(accel_dev);
+ if (ret)
+ goto out_err_dev_stop;
+
+ return ret;
+
+out_err_dev_stop:
+ adf_dev_stop(accel_dev);
+out_err_dev_shutdown:
+ adf_dev_shutdown(accel_dev);
+out_err_free_reg:
+ pci_release_regions(accel_pci_dev->pci_dev);
+out_err_disable:
+ pci_disable_device(accel_pci_dev->pci_dev);
+out_err:
+ adf_cleanup_accel(accel_dev);
+ kfree(accel_dev);
+ return ret;
+}
+
+static void adf_remove(struct pci_dev *pdev)
+{
+ struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev);
+
+ if (!accel_dev) {
+ pr_err("QAT: Driver removal failed\n");
+ return;
+ }
+ if (adf_dev_stop(accel_dev))
+ dev_err(&GET_DEV(accel_dev), "Failed to stop QAT accel dev\n");
+
+ adf_dev_shutdown(accel_dev);
+ adf_disable_aer(accel_dev);
+ adf_cleanup_accel(accel_dev);
+ adf_cleanup_pci_dev(accel_dev);
+ kfree(accel_dev);
+}
+
+static int __init adfdrv_init(void)
+{
+ request_module("intel_qat");
+
+ if (pci_register_driver(&adf_driver)) {
+ pr_err("QAT: Driver initialization failed\n");
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static void __exit adfdrv_release(void)
+{
+ pci_unregister_driver(&adf_driver);
+}
+
+module_init(adfdrv_init);
+module_exit(adfdrv_release);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Intel");
+MODULE_DESCRIPTION("Intel(R) QuickAssist Technology");
+MODULE_VERSION(ADF_DRV_VERSION);
diff --git a/drivers/crypto/qat/qat_c62xvf/Makefile b/drivers/crypto/qat/qat_c62xvf/Makefile
new file mode 100644
index 000000000000..ecd708c213b2
--- /dev/null
+++ b/drivers/crypto/qat/qat_c62xvf/Makefile
@@ -0,0 +1,3 @@
+ccflags-y := -I$(src)/../qat_common
+obj-$(CONFIG_CRYPTO_DEV_QAT_C62XVF) += qat_c62xvf.o
+qat_c62xvf-objs := adf_drv.o adf_c62xvf_hw_data.o
diff --git a/drivers/crypto/qat/qat_c62xvf/adf_c62xvf_hw_data.c b/drivers/crypto/qat/qat_c62xvf/adf_c62xvf_hw_data.c
new file mode 100644
index 000000000000..baf4b509c892
--- /dev/null
+++ b/drivers/crypto/qat/qat_c62xvf/adf_c62xvf_hw_data.c
@@ -0,0 +1,173 @@
+/*
+ This file is provided under a dual BSD/GPLv2 license. When using or
+ redistributing this file, you may do so under either license.
+
+ GPL LICENSE SUMMARY
+ Copyright(c) 2015 Intel Corporation.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ Contact Information:
+ qat-linux@intel.com
+
+ BSD LICENSE
+ Copyright(c) 2015 Intel Corporation.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include <adf_accel_devices.h>
+#include <adf_pf2vf_msg.h>
+#include <adf_common_drv.h>
+#include "adf_c62xvf_hw_data.h"
+
+static struct adf_hw_device_class c62xiov_class = {
+ .name = ADF_C62XVF_DEVICE_NAME,
+ .type = DEV_C62XVF,
+ .instances = 0
+};
+
+static u32 get_accel_mask(u32 fuse)
+{
+ return ADF_C62XIOV_ACCELERATORS_MASK;
+}
+
+static u32 get_ae_mask(u32 fuse)
+{
+ return ADF_C62XIOV_ACCELENGINES_MASK;
+}
+
+static u32 get_num_accels(struct adf_hw_device_data *self)
+{
+ return ADF_C62XIOV_MAX_ACCELERATORS;
+}
+
+static u32 get_num_aes(struct adf_hw_device_data *self)
+{
+ return ADF_C62XIOV_MAX_ACCELENGINES;
+}
+
+static u32 get_misc_bar_id(struct adf_hw_device_data *self)
+{
+ return ADF_C62XIOV_PMISC_BAR;
+}
+
+static u32 get_etr_bar_id(struct adf_hw_device_data *self)
+{
+ return ADF_C62XIOV_ETR_BAR;
+}
+
+static enum dev_sku_info get_sku(struct adf_hw_device_data *self)
+{
+ return DEV_SKU_VF;
+}
+
+static u32 get_pf2vf_offset(u32 i)
+{
+ return ADF_C62XIOV_PF2VF_OFFSET;
+}
+
+static u32 get_vintmsk_offset(u32 i)
+{
+ return ADF_C62XIOV_VINTMSK_OFFSET;
+}
+
+static int adf_vf_int_noop(struct adf_accel_dev *accel_dev)
+{
+ return 0;
+}
+
+static void adf_vf_void_noop(struct adf_accel_dev *accel_dev)
+{
+}
+
+static int adf_vf2pf_init(struct adf_accel_dev *accel_dev)
+{
+ u32 msg = (ADF_VF2PF_MSGORIGIN_SYSTEM |
+ (ADF_VF2PF_MSGTYPE_INIT << ADF_VF2PF_MSGTYPE_SHIFT));
+
+ if (adf_iov_putmsg(accel_dev, msg, 0)) {
+ dev_err(&GET_DEV(accel_dev),
+ "Failed to send Init event to PF\n");
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static void adf_vf2pf_shutdown(struct adf_accel_dev *accel_dev)
+{
+ u32 msg = (ADF_VF2PF_MSGORIGIN_SYSTEM |
+ (ADF_VF2PF_MSGTYPE_SHUTDOWN << ADF_VF2PF_MSGTYPE_SHIFT));
+
+ if (adf_iov_putmsg(accel_dev, msg, 0))
+ dev_err(&GET_DEV(accel_dev),
+ "Failed to send Shutdown event to PF\n");
+}
+
+void adf_init_hw_data_c62xiov(struct adf_hw_device_data *hw_data)
+{
+ hw_data->dev_class = &c62xiov_class;
+ hw_data->num_banks = ADF_C62XIOV_ETR_MAX_BANKS;
+ hw_data->num_accel = ADF_C62XIOV_MAX_ACCELERATORS;
+ hw_data->num_logical_accel = 1;
+ hw_data->num_engines = ADF_C62XIOV_MAX_ACCELENGINES;
+ hw_data->tx_rx_gap = ADF_C62XIOV_RX_RINGS_OFFSET;
+ hw_data->tx_rings_mask = ADF_C62XIOV_TX_RINGS_MASK;
+ hw_data->alloc_irq = adf_vf_isr_resource_alloc;
+ hw_data->free_irq = adf_vf_isr_resource_free;
+ hw_data->enable_error_correction = adf_vf_void_noop;
+ hw_data->init_admin_comms = adf_vf_int_noop;
+ hw_data->exit_admin_comms = adf_vf_void_noop;
+ hw_data->send_admin_init = adf_vf2pf_init;
+ hw_data->init_arb = adf_vf_int_noop;
+ hw_data->exit_arb = adf_vf_void_noop;
+ hw_data->disable_iov = adf_vf2pf_shutdown;
+ hw_data->get_accel_mask = get_accel_mask;
+ hw_data->get_ae_mask = get_ae_mask;
+ hw_data->get_num_accels = get_num_accels;
+ hw_data->get_num_aes = get_num_aes;
+ hw_data->get_etr_bar_id = get_etr_bar_id;
+ hw_data->get_misc_bar_id = get_misc_bar_id;
+ hw_data->get_pf2vf_offset = get_pf2vf_offset;
+ hw_data->get_vintmsk_offset = get_vintmsk_offset;
+ hw_data->get_sku = get_sku;
+ hw_data->enable_ints = adf_vf_void_noop;
+ hw_data->enable_vf2pf_comms = adf_enable_vf2pf_comms;
+ hw_data->min_iov_compat_ver = ADF_PFVF_COMPATIBILITY_VERSION;
+ hw_data->dev_class->instances++;
+ adf_devmgr_update_class_index(hw_data);
+}
+
+void adf_clean_hw_data_c62xiov(struct adf_hw_device_data *hw_data)
+{
+ hw_data->dev_class->instances--;
+ adf_devmgr_update_class_index(hw_data);
+}
diff --git a/drivers/crypto/qat/qat_dh895xcc/adf_drv.h b/drivers/crypto/qat/qat_c62xvf/adf_c62xvf_hw_data.h
index 85ff245bd1d8..a28d83e77422 100644
--- a/drivers/crypto/qat/qat_dh895xcc/adf_drv.h
+++ b/drivers/crypto/qat/qat_c62xvf/adf_c62xvf_hw_data.h
@@ -3,7 +3,7 @@
redistributing this file, you may do so under either license.
GPL LICENSE SUMMARY
- Copyright(c) 2014 Intel Corporation.
+ Copyright(c) 2015 Intel Corporation.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation.
@@ -17,7 +17,7 @@
qat-linux@intel.com
BSD LICENSE
- Copyright(c) 2014 Intel Corporation.
+ Copyright(c) 2015 Intel Corporation.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
@@ -44,15 +44,21 @@
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef ADF_DH895x_DRV_H_
-#define ADF_DH895x_DRV_H_
-#include <adf_accel_devices.h>
-#include <adf_transport.h>
-
-void adf_init_hw_data_dh895xcc(struct adf_hw_device_data *hw_data);
-void adf_clean_hw_data_dh895xcc(struct adf_hw_device_data *hw_data);
-int adf_isr_resource_alloc(struct adf_accel_dev *accel_dev);
-void adf_isr_resource_free(struct adf_accel_dev *accel_dev);
-void adf_get_arbiter_mapping(struct adf_accel_dev *accel_dev,
- uint32_t const **arb_map_config);
+#ifndef ADF_C62XVF_HW_DATA_H_
+#define ADF_C62XVF_HW_DATA_H_
+
+#define ADF_C62XIOV_PMISC_BAR 1
+#define ADF_C62XIOV_ACCELERATORS_MASK 0x1
+#define ADF_C62XIOV_ACCELENGINES_MASK 0x1
+#define ADF_C62XIOV_MAX_ACCELERATORS 1
+#define ADF_C62XIOV_MAX_ACCELENGINES 1
+#define ADF_C62XIOV_RX_RINGS_OFFSET 8
+#define ADF_C62XIOV_TX_RINGS_MASK 0xFF
+#define ADF_C62XIOV_ETR_BAR 0
+#define ADF_C62XIOV_ETR_MAX_BANKS 1
+#define ADF_C62XIOV_PF2VF_OFFSET 0x200
+#define ADF_C62XIOV_VINTMSK_OFFSET 0x208
+
+void adf_init_hw_data_c62xiov(struct adf_hw_device_data *hw_data);
+void adf_clean_hw_data_c62xiov(struct adf_hw_device_data *hw_data);
#endif
diff --git a/drivers/crypto/qat/qat_c62xvf/adf_drv.c b/drivers/crypto/qat/qat_c62xvf/adf_drv.c
new file mode 100644
index 000000000000..d2e4b928f3be
--- /dev/null
+++ b/drivers/crypto/qat/qat_c62xvf/adf_drv.c
@@ -0,0 +1,305 @@
+/*
+ This file is provided under a dual BSD/GPLv2 license. When using or
+ redistributing this file, you may do so under either license.
+
+ GPL LICENSE SUMMARY
+ Copyright(c) 2014 Intel Corporation.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ Contact Information:
+ qat-linux@intel.com
+
+ BSD LICENSE
+ Copyright(c) 2014 Intel Corporation.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/io.h>
+#include <adf_accel_devices.h>
+#include <adf_common_drv.h>
+#include <adf_cfg.h>
+#include "adf_c62xvf_hw_data.h"
+
+#define ADF_SYSTEM_DEVICE(device_id) \
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, device_id)}
+
+static const struct pci_device_id adf_pci_tbl[] = {
+ ADF_SYSTEM_DEVICE(ADF_C62XIOV_PCI_DEVICE_ID),
+ {0,}
+};
+MODULE_DEVICE_TABLE(pci, adf_pci_tbl);
+
+static int adf_probe(struct pci_dev *dev, const struct pci_device_id *ent);
+static void adf_remove(struct pci_dev *dev);
+
+static struct pci_driver adf_driver = {
+ .id_table = adf_pci_tbl,
+ .name = ADF_C62XVF_DEVICE_NAME,
+ .probe = adf_probe,
+ .remove = adf_remove,
+};
+
+static void adf_cleanup_pci_dev(struct adf_accel_dev *accel_dev)
+{
+ pci_release_regions(accel_dev->accel_pci_dev.pci_dev);
+ pci_disable_device(accel_dev->accel_pci_dev.pci_dev);
+}
+
+static void adf_cleanup_accel(struct adf_accel_dev *accel_dev)
+{
+ struct adf_accel_pci *accel_pci_dev = &accel_dev->accel_pci_dev;
+ struct adf_accel_dev *pf;
+ int i;
+
+ for (i = 0; i < ADF_PCI_MAX_BARS; i++) {
+ struct adf_bar *bar = &accel_pci_dev->pci_bars[i];
+
+ if (bar->virt_addr)
+ pci_iounmap(accel_pci_dev->pci_dev, bar->virt_addr);
+ }
+
+ if (accel_dev->hw_device) {
+ switch (accel_pci_dev->pci_dev->device) {
+ case ADF_C62XIOV_PCI_DEVICE_ID:
+ adf_clean_hw_data_c62xiov(accel_dev->hw_device);
+ break;
+ default:
+ break;
+ }
+ kfree(accel_dev->hw_device);
+ accel_dev->hw_device = NULL;
+ }
+ adf_cfg_dev_remove(accel_dev);
+ debugfs_remove(accel_dev->debugfs_dir);
+ pf = adf_devmgr_pci_to_accel_dev(accel_pci_dev->pci_dev->physfn);
+ adf_devmgr_rm_dev(accel_dev, pf);
+}
+
+static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct adf_accel_dev *accel_dev;
+ struct adf_accel_dev *pf;
+ struct adf_accel_pci *accel_pci_dev;
+ struct adf_hw_device_data *hw_data;
+ char name[ADF_DEVICE_NAME_LENGTH];
+ unsigned int i, bar_nr;
+ int ret, bar_mask;
+
+ switch (ent->device) {
+ case ADF_C62XIOV_PCI_DEVICE_ID:
+ break;
+ default:
+ dev_err(&pdev->dev, "Invalid device 0x%x.\n", ent->device);
+ return -ENODEV;
+ }
+
+ accel_dev = kzalloc_node(sizeof(*accel_dev), GFP_KERNEL,
+ dev_to_node(&pdev->dev));
+ if (!accel_dev)
+ return -ENOMEM;
+
+ accel_dev->is_vf = true;
+ pf = adf_devmgr_pci_to_accel_dev(pdev->physfn);
+ accel_pci_dev = &accel_dev->accel_pci_dev;
+ accel_pci_dev->pci_dev = pdev;
+
+ /* Add accel device to accel table */
+ if (adf_devmgr_add_dev(accel_dev, pf)) {
+ dev_err(&pdev->dev, "Failed to add new accelerator device.\n");
+ kfree(accel_dev);
+ return -EFAULT;
+ }
+ INIT_LIST_HEAD(&accel_dev->crypto_list);
+
+ accel_dev->owner = THIS_MODULE;
+ /* Allocate and configure device configuration structure */
+ hw_data = kzalloc_node(sizeof(*hw_data), GFP_KERNEL,
+ dev_to_node(&pdev->dev));
+ if (!hw_data) {
+ ret = -ENOMEM;
+ goto out_err;
+ }
+ accel_dev->hw_device = hw_data;
+ adf_init_hw_data_c62xiov(accel_dev->hw_device);
+
+ /* Get Accelerators and Accelerators Engines masks */
+ hw_data->accel_mask = hw_data->get_accel_mask(hw_data->fuses);
+ hw_data->ae_mask = hw_data->get_ae_mask(hw_data->fuses);
+ accel_pci_dev->sku = hw_data->get_sku(hw_data);
+
+ /* Create dev top level debugfs entry */
+ snprintf(name, sizeof(name), "%s%s_%02x:%02d.%02d",
+ ADF_DEVICE_NAME_PREFIX, hw_data->dev_class->name,
+ pdev->bus->number, PCI_SLOT(pdev->devfn),
+ PCI_FUNC(pdev->devfn));
+
+ accel_dev->debugfs_dir = debugfs_create_dir(name, NULL);
+ if (!accel_dev->debugfs_dir) {
+ dev_err(&pdev->dev, "Could not create debugfs dir %s\n", name);
+ ret = -EINVAL;
+ goto out_err;
+ }
+
+ /* Create device configuration table */
+ ret = adf_cfg_dev_add(accel_dev);
+ if (ret)
+ goto out_err;
+
+ /* enable PCI device */
+ if (pci_enable_device(pdev)) {
+ ret = -EFAULT;
+ goto out_err;
+ }
+
+ /* set dma identifier */
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
+ if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)))) {
+ dev_err(&pdev->dev, "No usable DMA configuration\n");
+ ret = -EFAULT;
+ goto out_err_disable;
+ } else {
+ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+ }
+
+ } else {
+ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
+ }
+
+ if (pci_request_regions(pdev, ADF_C62XVF_DEVICE_NAME)) {
+ ret = -EFAULT;
+ goto out_err_disable;
+ }
+
+ /* Find and map all the device's BARS */
+ i = 0;
+ bar_mask = pci_select_bars(pdev, IORESOURCE_MEM);
+ for_each_set_bit(bar_nr, (const unsigned long *)&bar_mask,
+ ADF_PCI_MAX_BARS * 2) {
+ struct adf_bar *bar = &accel_pci_dev->pci_bars[i++];
+
+ bar->base_addr = pci_resource_start(pdev, bar_nr);
+ if (!bar->base_addr)
+ break;
+ bar->size = pci_resource_len(pdev, bar_nr);
+ bar->virt_addr = pci_iomap(accel_pci_dev->pci_dev, bar_nr, 0);
+ if (!bar->virt_addr) {
+ dev_err(&pdev->dev, "Failed to map BAR %d\n", bar_nr);
+ ret = -EFAULT;
+ goto out_err_free_reg;
+ }
+ }
+ pci_set_master(pdev);
+ /* Completion for VF2PF request/response message exchange */
+ init_completion(&accel_dev->vf.iov_msg_completion);
+
+ ret = qat_crypto_dev_config(accel_dev);
+ if (ret)
+ goto out_err_free_reg;
+
+ ret = adf_dev_init(accel_dev);
+ if (ret)
+ goto out_err_dev_shutdown;
+
+ ret = adf_dev_start(accel_dev);
+ if (ret)
+ goto out_err_dev_stop;
+
+ return ret;
+
+out_err_dev_stop:
+ adf_dev_stop(accel_dev);
+out_err_dev_shutdown:
+ adf_dev_shutdown(accel_dev);
+out_err_free_reg:
+ pci_release_regions(accel_pci_dev->pci_dev);
+out_err_disable:
+ pci_disable_device(accel_pci_dev->pci_dev);
+out_err:
+ adf_cleanup_accel(accel_dev);
+ kfree(accel_dev);
+ return ret;
+}
+
+static void adf_remove(struct pci_dev *pdev)
+{
+ struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev);
+
+ if (!accel_dev) {
+ pr_err("QAT: Driver removal failed\n");
+ return;
+ }
+ if (adf_dev_stop(accel_dev))
+ dev_err(&GET_DEV(accel_dev), "Failed to stop QAT accel dev\n");
+
+ adf_dev_shutdown(accel_dev);
+ adf_cleanup_accel(accel_dev);
+ adf_cleanup_pci_dev(accel_dev);
+ kfree(accel_dev);
+}
+
+static int __init adfdrv_init(void)
+{
+ request_module("intel_qat");
+
+ if (pci_register_driver(&adf_driver)) {
+ pr_err("QAT: Driver initialization failed\n");
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static void __exit adfdrv_release(void)
+{
+ pci_unregister_driver(&adf_driver);
+ adf_clean_vf_map(true);
+}
+
+module_init(adfdrv_init);
+module_exit(adfdrv_release);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Intel");
+MODULE_DESCRIPTION("Intel(R) QuickAssist Technology");
+MODULE_VERSION(ADF_DRV_VERSION);
diff --git a/drivers/crypto/qat/qat_common/Makefile b/drivers/crypto/qat/qat_common/Makefile
index 9e9e196c6d51..29c7c53d2845 100644
--- a/drivers/crypto/qat/qat_common/Makefile
+++ b/drivers/crypto/qat/qat_common/Makefile
@@ -4,10 +4,12 @@ $(obj)/qat_rsaprivkey-asn1.o: $(obj)/qat_rsaprivkey-asn1.c \
$(obj)/qat_rsaprivkey-asn1.h
clean-files += qat_rsapubkey-asn1.c qat_rsapubkey-asn1.h
-clean-files += qat_rsaprivkey-asn1.c qat_rsapvivkey-asn1.h
+clean-files += qat_rsaprivkey-asn1.c qat_rsaprivkey-asn1.h
obj-$(CONFIG_CRYPTO_DEV_QAT) += intel_qat.o
intel_qat-objs := adf_cfg.o \
+ adf_isr.o \
+ adf_vf_isr.o \
adf_ctl_drv.o \
adf_dev_mgr.o \
adf_init.o \
diff --git a/drivers/crypto/qat/qat_common/adf_accel_devices.h b/drivers/crypto/qat/qat_common/adf_accel_devices.h
index ca853d50b4b7..f96d427e502c 100644
--- a/drivers/crypto/qat/qat_common/adf_accel_devices.h
+++ b/drivers/crypto/qat/qat_common/adf_accel_devices.h
@@ -55,8 +55,20 @@
#define ADF_DH895XCC_DEVICE_NAME "dh895xcc"
#define ADF_DH895XCCVF_DEVICE_NAME "dh895xccvf"
+#define ADF_C62X_DEVICE_NAME "c62x"
+#define ADF_C62XVF_DEVICE_NAME "c62xvf"
+#define ADF_C3XXX_DEVICE_NAME "c3xxx"
+#define ADF_C3XXXVF_DEVICE_NAME "c3xxxvf"
#define ADF_DH895XCC_PCI_DEVICE_ID 0x435
#define ADF_DH895XCCIOV_PCI_DEVICE_ID 0x443
+#define ADF_C62X_PCI_DEVICE_ID 0x37c8
+#define ADF_C62XIOV_PCI_DEVICE_ID 0x37c9
+#define ADF_C3XXX_PCI_DEVICE_ID 0x19e2
+#define ADF_C3XXXIOV_PCI_DEVICE_ID 0x19e3
+#define ADF_ERRSOU3 (0x3A000 + 0x0C)
+#define ADF_ERRSOU5 (0x3A000 + 0xD8)
+#define ADF_DEVICE_FUSECTL_OFFSET 0x40
+#define ADF_DEVICE_LEGFUSE_OFFSET 0x4C
#define ADF_PCI_MAX_BARS 3
#define ADF_DEVICE_NAME_LENGTH 32
#define ADF_ETR_MAX_RINGS_PER_BANK 16
@@ -168,11 +180,11 @@ struct adf_hw_device_data {
const char *fw_mmp_name;
uint32_t fuses;
uint32_t accel_capabilities_mask;
+ uint32_t instance_id;
uint16_t accel_mask;
uint16_t ae_mask;
uint16_t tx_rings_mask;
uint8_t tx_rx_gap;
- uint8_t instance_id;
uint8_t num_banks;
uint8_t num_accel;
uint8_t num_logical_accel;
@@ -239,6 +251,6 @@ struct adf_accel_dev {
} vf;
};
bool is_vf;
- uint8_t accel_id;
+ u32 accel_id;
} __packed;
#endif
diff --git a/drivers/crypto/qat/qat_common/adf_accel_engine.c b/drivers/crypto/qat/qat_common/adf_accel_engine.c
index 20b08bdcb146..a42fc42704be 100644
--- a/drivers/crypto/qat/qat_common/adf_accel_engine.c
+++ b/drivers/crypto/qat/qat_common/adf_accel_engine.c
@@ -78,9 +78,12 @@ int adf_ae_fw_load(struct adf_accel_dev *accel_dev)
uof_addr = (void *)loader_data->uof_fw->data;
mmp_size = loader_data->mmp_fw->size;
mmp_addr = (void *)loader_data->mmp_fw->data;
- qat_uclo_wr_mimage(loader_data->fw_loader, mmp_addr, mmp_size);
- if (qat_uclo_map_uof_obj(loader_data->fw_loader, uof_addr, uof_size)) {
- dev_err(&GET_DEV(accel_dev), "Failed to map UOF\n");
+ if (qat_uclo_wr_mimage(loader_data->fw_loader, mmp_addr, mmp_size)) {
+ dev_err(&GET_DEV(accel_dev), "Failed to load MMP\n");
+ goto out_err;
+ }
+ if (qat_uclo_map_obj(loader_data->fw_loader, uof_addr, uof_size)) {
+ dev_err(&GET_DEV(accel_dev), "Failed to map FW\n");
goto out_err;
}
if (qat_uclo_wr_all_uimage(loader_data->fw_loader)) {
diff --git a/drivers/crypto/qat/qat_common/adf_admin.c b/drivers/crypto/qat/qat_common/adf_admin.c
index 147d755fed97..eb557f69e367 100644
--- a/drivers/crypto/qat/qat_common/adf_admin.c
+++ b/drivers/crypto/qat/qat_common/adf_admin.c
@@ -51,6 +51,7 @@
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include "adf_accel_devices.h"
+#include "adf_common_drv.h"
#include "icp_qat_fw_init_admin.h"
/* Admin Messages Registers */
@@ -234,7 +235,8 @@ int adf_init_admin_comms(struct adf_accel_dev *accel_dev)
struct adf_bar *pmisc =
&GET_BARS(accel_dev)[hw_data->get_misc_bar_id(hw_data)];
void __iomem *csr = pmisc->virt_addr;
- void __iomem *mailbox = csr + ADF_DH895XCC_MAILBOX_BASE_OFFSET;
+ void __iomem *mailbox = (void __iomem *)((uintptr_t)csr +
+ ADF_DH895XCC_MAILBOX_BASE_OFFSET);
u64 reg_val;
admin = kzalloc_node(sizeof(*accel_dev->admin), GFP_KERNEL,
diff --git a/drivers/crypto/qat/qat_common/adf_aer.c b/drivers/crypto/qat/qat_common/adf_aer.c
index 0a5ca0ba5d64..e78a1d7d88fc 100644
--- a/drivers/crypto/qat/qat_common/adf_aer.c
+++ b/drivers/crypto/qat/qat_common/adf_aer.c
@@ -82,7 +82,7 @@ struct adf_reset_dev_data {
struct work_struct reset_work;
};
-static void adf_dev_restore(struct adf_accel_dev *accel_dev)
+void adf_dev_restore(struct adf_accel_dev *accel_dev)
{
struct pci_dev *pdev = accel_to_pci_dev(accel_dev);
struct pci_dev *parent = pdev->bus->self;
@@ -197,7 +197,7 @@ static void adf_resume(struct pci_dev *pdev)
dev_info(&pdev->dev, "Device is up and runnig\n");
}
-static struct pci_error_handlers adf_err_handler = {
+static const struct pci_error_handlers adf_err_handler = {
.error_detected = adf_error_detected,
.slot_reset = adf_slot_reset,
.resume = adf_resume,
diff --git a/drivers/crypto/qat/qat_common/adf_cfg_common.h b/drivers/crypto/qat/qat_common/adf_cfg_common.h
index c697fb1cdfb5..8c4f6573ce59 100644
--- a/drivers/crypto/qat/qat_common/adf_cfg_common.h
+++ b/drivers/crypto/qat/qat_common/adf_cfg_common.h
@@ -72,12 +72,16 @@ enum adf_device_type {
DEV_UNKNOWN = 0,
DEV_DH895XCC,
DEV_DH895XCCVF,
+ DEV_C62X,
+ DEV_C62XVF,
+ DEV_C3XXX,
+ DEV_C3XXXVF
};
struct adf_dev_status_info {
enum adf_device_type type;
- uint8_t accel_id;
- uint8_t instance_id;
+ u32 accel_id;
+ u32 instance_id;
uint8_t num_ae;
uint8_t num_accel;
uint8_t num_logical_accel;
diff --git a/drivers/crypto/qat/qat_common/adf_common_drv.h b/drivers/crypto/qat/qat_common/adf_common_drv.h
index 3f76bd495bcb..0e82ce3c383e 100644
--- a/drivers/crypto/qat/qat_common/adf_common_drv.h
+++ b/drivers/crypto/qat/qat_common/adf_common_drv.h
@@ -54,7 +54,7 @@
#include "icp_qat_hal.h"
#define ADF_MAJOR_VERSION 0
-#define ADF_MINOR_VERSION 2
+#define ADF_MINOR_VERSION 6
#define ADF_BUILD_VERSION 0
#define ADF_DRV_VERSION __stringify(ADF_MAJOR_VERSION) "." \
__stringify(ADF_MINOR_VERSION) "." \
@@ -106,8 +106,6 @@ int adf_dev_start(struct adf_accel_dev *accel_dev);
int adf_dev_stop(struct adf_accel_dev *accel_dev);
void adf_dev_shutdown(struct adf_accel_dev *accel_dev);
-void adf_enable_pf2vf_interrupts(struct adf_accel_dev *accel_dev);
-void adf_disable_pf2vf_interrupts(struct adf_accel_dev *accel_dev);
int adf_iov_putmsg(struct adf_accel_dev *accel_dev, u32 msg, u8 vf_nr);
void adf_pf2vf_notify_restarting(struct adf_accel_dev *accel_dev);
int adf_enable_vf2pf_comms(struct adf_accel_dev *accel_dev);
@@ -143,6 +141,7 @@ int adf_ae_stop(struct adf_accel_dev *accel_dev);
int adf_enable_aer(struct adf_accel_dev *accel_dev, struct pci_driver *adf);
void adf_disable_aer(struct adf_accel_dev *accel_dev);
+void adf_dev_restore(struct adf_accel_dev *accel_dev);
int adf_init_aer(void);
void adf_exit_aer(void);
int adf_init_admin_comms(struct adf_accel_dev *accel_dev);
@@ -159,6 +158,7 @@ int adf_init_etr_data(struct adf_accel_dev *accel_dev);
void adf_cleanup_etr_data(struct adf_accel_dev *accel_dev);
int qat_crypto_register(void);
int qat_crypto_unregister(void);
+int qat_crypto_dev_config(struct adf_accel_dev *accel_dev);
struct qat_crypto_instance *qat_crypto_get_instance_node(int node);
void qat_crypto_put_instance(struct qat_crypto_instance *inst);
void qat_alg_callback(void *resp);
@@ -168,6 +168,11 @@ void qat_algs_unregister(void);
int qat_asym_algs_register(void);
void qat_asym_algs_unregister(void);
+int adf_isr_resource_alloc(struct adf_accel_dev *accel_dev);
+void adf_isr_resource_free(struct adf_accel_dev *accel_dev);
+int adf_vf_isr_resource_alloc(struct adf_accel_dev *accel_dev);
+void adf_vf_isr_resource_free(struct adf_accel_dev *accel_dev);
+
int qat_hal_init(struct adf_accel_dev *accel_dev);
void qat_hal_deinit(struct icp_qat_fw_loader_handle *handle);
void qat_hal_start(struct icp_qat_fw_loader_handle *handle, unsigned char ae,
@@ -178,6 +183,8 @@ void qat_hal_reset(struct icp_qat_fw_loader_handle *handle);
int qat_hal_clr_reset(struct icp_qat_fw_loader_handle *handle);
void qat_hal_set_live_ctx(struct icp_qat_fw_loader_handle *handle,
unsigned char ae, unsigned int ctx_mask);
+int qat_hal_check_ae_active(struct icp_qat_fw_loader_handle *handle,
+ unsigned int ae);
int qat_hal_set_ae_lm_mode(struct icp_qat_fw_loader_handle *handle,
unsigned char ae, enum icp_qat_uof_regtype lm_type,
unsigned char mode);
@@ -216,10 +223,10 @@ int qat_hal_wr_lm(struct icp_qat_fw_loader_handle *handle,
unsigned char ae, unsigned short lm_addr, unsigned int value);
int qat_uclo_wr_all_uimage(struct icp_qat_fw_loader_handle *handle);
void qat_uclo_del_uof_obj(struct icp_qat_fw_loader_handle *handle);
-int qat_uclo_map_uof_obj(struct icp_qat_fw_loader_handle *handle,
- void *addr_ptr, int mem_size);
-void qat_uclo_wr_mimage(struct icp_qat_fw_loader_handle *handle,
- void *addr_ptr, int mem_size);
+int qat_uclo_wr_mimage(struct icp_qat_fw_loader_handle *handle, void *addr_ptr,
+ int mem_size);
+int qat_uclo_map_obj(struct icp_qat_fw_loader_handle *handle,
+ void *addr_ptr, int mem_size);
#if defined(CONFIG_PCI_IOV)
int adf_sriov_configure(struct pci_dev *pdev, int numvfs);
void adf_disable_sriov(struct adf_accel_dev *accel_dev);
@@ -227,6 +234,8 @@ void adf_disable_vf2pf_interrupts(struct adf_accel_dev *accel_dev,
uint32_t vf_mask);
void adf_enable_vf2pf_interrupts(struct adf_accel_dev *accel_dev,
uint32_t vf_mask);
+void adf_enable_pf2vf_interrupts(struct adf_accel_dev *accel_dev);
+void adf_disable_pf2vf_interrupts(struct adf_accel_dev *accel_dev);
#else
static inline int adf_sriov_configure(struct pci_dev *pdev, int numvfs)
{
@@ -236,5 +245,13 @@ static inline int adf_sriov_configure(struct pci_dev *pdev, int numvfs)
static inline void adf_disable_sriov(struct adf_accel_dev *accel_dev)
{
}
+
+static inline void adf_enable_pf2vf_interrupts(struct adf_accel_dev *accel_dev)
+{
+}
+
+static inline void adf_disable_pf2vf_interrupts(struct adf_accel_dev *accel_dev)
+{
+}
#endif
#endif
diff --git a/drivers/crypto/qat/qat_common/adf_ctl_drv.c b/drivers/crypto/qat/qat_common/adf_ctl_drv.c
index 473d36d91644..5c897e6e7994 100644
--- a/drivers/crypto/qat/qat_common/adf_ctl_drv.c
+++ b/drivers/crypto/qat/qat_common/adf_ctl_drv.c
@@ -255,12 +255,9 @@ out:
static int adf_ctl_is_device_in_use(int id)
{
- struct list_head *itr, *head = adf_devmgr_get_head();
-
- list_for_each(itr, head) {
- struct adf_accel_dev *dev =
- list_entry(itr, struct adf_accel_dev, list);
+ struct adf_accel_dev *dev;
+ list_for_each_entry(dev, adf_devmgr_get_head(), list) {
if (id == dev->accel_id || id == ADF_CFG_ALL_DEVICES) {
if (adf_devmgr_in_reset(dev) || adf_dev_in_use(dev)) {
dev_info(&GET_DEV(dev),
@@ -275,12 +272,10 @@ static int adf_ctl_is_device_in_use(int id)
static int adf_ctl_stop_devices(uint32_t id)
{
- struct list_head *itr, *head = adf_devmgr_get_head();
+ struct adf_accel_dev *accel_dev;
int ret = 0;
- list_for_each(itr, head) {
- struct adf_accel_dev *accel_dev =
- list_entry(itr, struct adf_accel_dev, list);
+ list_for_each_entry_reverse(accel_dev, adf_devmgr_get_head(), list) {
if (id == accel_dev->accel_id || id == ADF_CFG_ALL_DEVICES) {
if (!adf_dev_started(accel_dev))
continue;
@@ -342,12 +337,10 @@ static int adf_ctl_ioctl_dev_start(struct file *fp, unsigned int cmd,
if (ret)
return ret;
+ ret = -ENODEV;
accel_dev = adf_devmgr_get_dev_by_id(ctl_data->device_id);
- if (!accel_dev) {
- pr_err("QAT: Device %d not found\n", ctl_data->device_id);
- ret = -ENODEV;
+ if (!accel_dev)
goto out;
- }
if (!adf_dev_started(accel_dev)) {
dev_info(&GET_DEV(accel_dev),
diff --git a/drivers/crypto/qat/qat_common/adf_dev_mgr.c b/drivers/crypto/qat/qat_common/adf_dev_mgr.c
index 8dfdb8f90797..b3ebb25f9ca7 100644
--- a/drivers/crypto/qat/qat_common/adf_dev_mgr.c
+++ b/drivers/crypto/qat/qat_common/adf_dev_mgr.c
@@ -53,6 +53,7 @@ static LIST_HEAD(accel_table);
static LIST_HEAD(vfs_table);
static DEFINE_MUTEX(table_lock);
static uint32_t num_devices;
+static u8 id_map[ADF_MAX_DEVICES];
struct vf_id_map {
u32 bdf;
@@ -116,8 +117,10 @@ void adf_clean_vf_map(bool vf)
mutex_lock(&table_lock);
list_for_each_safe(ptr, tmp, &vfs_table) {
map = list_entry(ptr, struct vf_id_map, list);
- if (map->bdf != -1)
+ if (map->bdf != -1) {
+ id_map[map->id] = 0;
num_devices--;
+ }
if (vf && map->bdf == -1)
continue;
@@ -154,6 +157,19 @@ void adf_devmgr_update_class_index(struct adf_hw_device_data *hw_data)
}
EXPORT_SYMBOL_GPL(adf_devmgr_update_class_index);
+static unsigned int adf_find_free_id(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < ADF_MAX_DEVICES; i++) {
+ if (!id_map[i]) {
+ id_map[i] = 1;
+ return i;
+ }
+ }
+ return ADF_MAX_DEVICES + 1;
+}
+
/**
* adf_devmgr_add_dev() - Add accel_dev to the acceleration framework
* @accel_dev: Pointer to acceleration device.
@@ -194,8 +210,12 @@ int adf_devmgr_add_dev(struct adf_accel_dev *accel_dev,
}
list_add_tail(&accel_dev->list, &accel_table);
- accel_dev->accel_id = num_devices++;
-
+ accel_dev->accel_id = adf_find_free_id();
+ if (accel_dev->accel_id > ADF_MAX_DEVICES) {
+ ret = -EFAULT;
+ goto unlock;
+ }
+ num_devices++;
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map) {
ret = -ENOMEM;
@@ -236,8 +256,13 @@ int adf_devmgr_add_dev(struct adf_accel_dev *accel_dev,
ret = -ENOMEM;
goto unlock;
}
-
- accel_dev->accel_id = num_devices++;
+ accel_dev->accel_id = adf_find_free_id();
+ if (accel_dev->accel_id > ADF_MAX_DEVICES) {
+ kfree(map);
+ ret = -EFAULT;
+ goto unlock;
+ }
+ num_devices++;
list_add_tail(&accel_dev->list, &accel_table);
map->bdf = adf_get_vf_num(accel_dev);
map->id = accel_dev->accel_id;
@@ -271,6 +296,7 @@ void adf_devmgr_rm_dev(struct adf_accel_dev *accel_dev,
{
mutex_lock(&table_lock);
if (!accel_dev->is_vf || (accel_dev->is_vf && !pf)) {
+ id_map[accel_dev->accel_id] = 0;
num_devices--;
} else if (accel_dev->is_vf && pf) {
struct vf_id_map *map, *next;
diff --git a/drivers/crypto/qat/qat_common/adf_hw_arbiter.c b/drivers/crypto/qat/qat_common/adf_hw_arbiter.c
index 6849422e04bb..f267d9e42e0b 100644
--- a/drivers/crypto/qat/qat_common/adf_hw_arbiter.c
+++ b/drivers/crypto/qat/qat_common/adf_hw_arbiter.c
@@ -45,6 +45,7 @@
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "adf_accel_devices.h"
+#include "adf_common_drv.h"
#include "adf_transport_internal.h"
#define ADF_ARB_NUM 4
@@ -124,19 +125,12 @@ int adf_init_arb(struct adf_accel_dev *accel_dev)
}
EXPORT_SYMBOL_GPL(adf_init_arb);
-/**
- * adf_update_ring_arb() - update ring arbitration rgister
- * @accel_dev: Pointer to ring data.
- *
- * Function enables or disables rings for/from arbitration.
- */
void adf_update_ring_arb(struct adf_etr_ring_data *ring)
{
WRITE_CSR_ARB_RINGSRVARBEN(ring->bank->csr_addr,
ring->bank->bank_number,
ring->bank->ring_mask & 0xFF);
}
-EXPORT_SYMBOL_GPL(adf_update_ring_arb);
void adf_exit_arb(struct adf_accel_dev *accel_dev)
{
diff --git a/drivers/crypto/qat/qat_common/adf_init.c b/drivers/crypto/qat/qat_common/adf_init.c
index d873eeecc363..ef5575e4a215 100644
--- a/drivers/crypto/qat/qat_common/adf_init.c
+++ b/drivers/crypto/qat/qat_common/adf_init.c
@@ -62,15 +62,6 @@ static void adf_service_add(struct service_hndl *service)
mutex_unlock(&service_lock);
}
-/**
- * adf_service_register() - Register acceleration service in the accel framework
- * @service: Pointer to the service
- *
- * Function adds the acceleration service to the acceleration framework.
- * To be used by QAT device specific drivers.
- *
- * Return: 0 on success, error code otherwise.
- */
int adf_service_register(struct service_hndl *service)
{
service->init_status = 0;
@@ -78,7 +69,6 @@ int adf_service_register(struct service_hndl *service)
adf_service_add(service);
return 0;
}
-EXPORT_SYMBOL_GPL(adf_service_register);
static void adf_service_remove(struct service_hndl *service)
{
@@ -87,15 +77,6 @@ static void adf_service_remove(struct service_hndl *service)
mutex_unlock(&service_lock);
}
-/**
- * adf_service_unregister() - Unregister acceleration service from the framework
- * @service: Pointer to the service
- *
- * Function remove the acceleration service from the acceleration framework.
- * To be used by QAT device specific drivers.
- *
- * Return: 0 on success, error code otherwise.
- */
int adf_service_unregister(struct service_hndl *service)
{
if (service->init_status || service->start_status) {
@@ -105,7 +86,6 @@ int adf_service_unregister(struct service_hndl *service)
adf_service_remove(service);
return 0;
}
-EXPORT_SYMBOL_GPL(adf_service_unregister);
/**
* adf_dev_init() - Init data structures and services for the given accel device
@@ -366,6 +346,7 @@ void adf_dev_shutdown(struct adf_accel_dev *accel_dev)
hw_data->disable_iov(accel_dev);
adf_cleanup_etr_data(accel_dev);
+ adf_dev_restore(accel_dev);
}
EXPORT_SYMBOL_GPL(adf_dev_shutdown);
diff --git a/drivers/crypto/qat/qat_dh895xcc/adf_isr.c b/drivers/crypto/qat/qat_common/adf_isr.c
index 5570f78795c1..b81f79acc4ea 100644
--- a/drivers/crypto/qat/qat_dh895xcc/adf_isr.c
+++ b/drivers/crypto/qat/qat_common/adf_isr.c
@@ -51,15 +51,13 @@
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
-#include <adf_accel_devices.h>
-#include <adf_common_drv.h>
-#include <adf_cfg.h>
-#include <adf_cfg_strings.h>
-#include <adf_cfg_common.h>
-#include <adf_transport_access_macros.h>
-#include <adf_transport_internal.h>
-#include "adf_drv.h"
-#include "adf_dh895xcc_hw_data.h"
+#include "adf_accel_devices.h"
+#include "adf_common_drv.h"
+#include "adf_cfg.h"
+#include "adf_cfg_strings.h"
+#include "adf_cfg_common.h"
+#include "adf_transport_access_macros.h"
+#include "adf_transport_internal.h"
static int adf_enable_msix(struct adf_accel_dev *accel_dev)
{
@@ -109,14 +107,16 @@ static irqreturn_t adf_msix_isr_ae(int irq, void *dev_ptr)
#ifdef CONFIG_PCI_IOV
/* If SR-IOV is enabled (vf_info is non-NULL), check for VF->PF ints */
if (accel_dev->pf.vf_info) {
- void __iomem *pmisc_bar_addr =
- (&GET_BARS(accel_dev)[ADF_DH895XCC_PMISC_BAR])->virt_addr;
+ struct adf_hw_device_data *hw_data = accel_dev->hw_device;
+ struct adf_bar *pmisc =
+ &GET_BARS(accel_dev)[hw_data->get_misc_bar_id(hw_data)];
+ void __iomem *pmisc_bar_addr = pmisc->virt_addr;
u32 vf_mask;
/* Get the interrupt sources triggered by VFs */
- vf_mask = ((ADF_CSR_RD(pmisc_bar_addr, ADF_DH895XCC_ERRSOU5) &
+ vf_mask = ((ADF_CSR_RD(pmisc_bar_addr, ADF_ERRSOU5) &
0x0000FFFF) << 16) |
- ((ADF_CSR_RD(pmisc_bar_addr, ADF_DH895XCC_ERRSOU3) &
+ ((ADF_CSR_RD(pmisc_bar_addr, ADF_ERRSOU3) &
0x01FFFE00) >> 9);
if (vf_mask) {
@@ -301,6 +301,12 @@ static void adf_cleanup_bh(struct adf_accel_dev *accel_dev)
}
}
+/**
+ * adf_vf_isr_resource_free() - Free IRQ for acceleration device
+ * @accel_dev: Pointer to acceleration device.
+ *
+ * Function frees interrupts for acceleration device.
+ */
void adf_isr_resource_free(struct adf_accel_dev *accel_dev)
{
adf_free_irqs(accel_dev);
@@ -308,7 +314,16 @@ void adf_isr_resource_free(struct adf_accel_dev *accel_dev)
adf_disable_msix(&accel_dev->accel_pci_dev);
adf_isr_free_msix_entry_table(accel_dev);
}
-
+EXPORT_SYMBOL_GPL(adf_isr_resource_free);
+
+/**
+ * adf_vf_isr_resource_alloc() - Allocate IRQ for acceleration device
+ * @accel_dev: Pointer to acceleration device.
+ *
+ * Function allocates interrupts for acceleration device.
+ *
+ * Return: 0 on success, error code otherwise.
+ */
int adf_isr_resource_alloc(struct adf_accel_dev *accel_dev)
{
int ret;
@@ -330,3 +345,4 @@ err_out:
adf_isr_resource_free(accel_dev);
return -EFAULT;
}
+EXPORT_SYMBOL_GPL(adf_isr_resource_alloc);
diff --git a/drivers/crypto/qat/qat_common/adf_pf2vf_msg.c b/drivers/crypto/qat/qat_common/adf_pf2vf_msg.c
index 5fdbad809343..b3875fdf6cd7 100644
--- a/drivers/crypto/qat/qat_common/adf_pf2vf_msg.c
+++ b/drivers/crypto/qat/qat_common/adf_pf2vf_msg.c
@@ -45,8 +45,6 @@
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <linux/pci.h>
-#include <linux/mutex.h>
#include <linux/delay.h>
#include "adf_accel_devices.h"
#include "adf_common_drv.h"
@@ -58,12 +56,6 @@
#define ADF_DH895XCC_ERRMSK5 (ADF_DH895XCC_EP_OFFSET + 0xDC)
#define ADF_DH895XCC_ERRMSK5_VF2PF_U_MASK(vf_mask) (vf_mask >> 16)
-/**
- * adf_enable_pf2vf_interrupts() - Enable PF to VF interrupts
- * @accel_dev: Pointer to acceleration device.
- *
- * Function enables PF to VF interrupts
- */
void adf_enable_pf2vf_interrupts(struct adf_accel_dev *accel_dev)
{
struct adf_accel_pci *pci_info = &accel_dev->accel_pci_dev;
@@ -73,14 +65,7 @@ void adf_enable_pf2vf_interrupts(struct adf_accel_dev *accel_dev)
ADF_CSR_WR(pmisc_bar_addr, hw_data->get_vintmsk_offset(0), 0x0);
}
-EXPORT_SYMBOL_GPL(adf_enable_pf2vf_interrupts);
-/**
- * adf_disable_pf2vf_interrupts() - Disable PF to VF interrupts
- * @accel_dev: Pointer to acceleration device.
- *
- * Function disables PF to VF interrupts
- */
void adf_disable_pf2vf_interrupts(struct adf_accel_dev *accel_dev)
{
struct adf_accel_pci *pci_info = &accel_dev->accel_pci_dev;
@@ -90,7 +75,6 @@ void adf_disable_pf2vf_interrupts(struct adf_accel_dev *accel_dev)
ADF_CSR_WR(pmisc_bar_addr, hw_data->get_vintmsk_offset(0), 0x2);
}
-EXPORT_SYMBOL_GPL(adf_disable_pf2vf_interrupts);
void adf_enable_vf2pf_interrupts(struct adf_accel_dev *accel_dev,
u32 vf_mask)
@@ -116,12 +100,6 @@ void adf_enable_vf2pf_interrupts(struct adf_accel_dev *accel_dev,
}
}
-/**
- * adf_disable_pf2vf_interrupts() - Disable VF to PF interrupts
- * @accel_dev: Pointer to acceleration device.
- *
- * Function disables VF to PF interrupts
- */
void adf_disable_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 vf_mask)
{
struct adf_hw_device_data *hw_data = accel_dev->hw_device;
@@ -144,7 +122,6 @@ void adf_disable_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 vf_mask)
ADF_CSR_WR(pmisc_addr, ADF_DH895XCC_ERRMSK5, reg);
}
}
-EXPORT_SYMBOL_GPL(adf_disable_vf2pf_interrupts);
static int __adf_iov_putmsg(struct adf_accel_dev *accel_dev, u32 msg, u8 vf_nr)
{
diff --git a/drivers/crypto/qat/qat_common/adf_transport.c b/drivers/crypto/qat/qat_common/adf_transport.c
index 3865ae8d96d9..57d2622728a5 100644
--- a/drivers/crypto/qat/qat_common/adf_transport.c
+++ b/drivers/crypto/qat/qat_common/adf_transport.c
@@ -122,7 +122,7 @@ int adf_send_message(struct adf_etr_ring_data *ring, uint32_t *msg)
return -EAGAIN;
}
spin_lock_bh(&ring->lock);
- memcpy(ring->base_addr + ring->tail, msg,
+ memcpy((void *)((uintptr_t)ring->base_addr + ring->tail), msg,
ADF_MSG_SIZE_TO_BYTES(ring->msg_size));
ring->tail = adf_modulo(ring->tail +
@@ -137,23 +137,22 @@ int adf_send_message(struct adf_etr_ring_data *ring, uint32_t *msg)
static int adf_handle_response(struct adf_etr_ring_data *ring)
{
uint32_t msg_counter = 0;
- uint32_t *msg = (uint32_t *)(ring->base_addr + ring->head);
+ uint32_t *msg = (uint32_t *)((uintptr_t)ring->base_addr + ring->head);
while (*msg != ADF_RING_EMPTY_SIG) {
ring->callback((uint32_t *)msg);
+ atomic_dec(ring->inflights);
*msg = ADF_RING_EMPTY_SIG;
ring->head = adf_modulo(ring->head +
ADF_MSG_SIZE_TO_BYTES(ring->msg_size),
ADF_RING_SIZE_MODULO(ring->ring_size));
msg_counter++;
- msg = (uint32_t *)(ring->base_addr + ring->head);
+ msg = (uint32_t *)((uintptr_t)ring->base_addr + ring->head);
}
- if (msg_counter > 0) {
+ if (msg_counter > 0)
WRITE_CSR_RING_HEAD(ring->bank->csr_addr,
ring->bank->bank_number,
ring->ring_number, ring->head);
- atomic_sub(msg_counter, ring->inflights);
- }
return 0;
}
@@ -342,27 +341,15 @@ static void adf_ring_response_handler(struct adf_etr_bank_data *bank)
}
}
-/**
- * adf_response_handler() - Bottom half handler response handler
- * @bank_addr: Address of a ring bank for with the BH was scheduled.
- *
- * Function is the bottom half handler for the response from acceleration
- * device. There is one handler for every ring bank. Function checks all
- * communication rings in the bank.
- * To be used by QAT device specific drivers.
- *
- * Return: void
- */
-void adf_response_handler(unsigned long bank_addr)
+void adf_response_handler(uintptr_t bank_addr)
{
struct adf_etr_bank_data *bank = (void *)bank_addr;
- /* Handle all the responses nad reenable IRQs */
+ /* Handle all the responses and reenable IRQs */
adf_ring_response_handler(bank);
WRITE_CSR_INT_FLAG_AND_COL(bank->csr_addr, bank->bank_number,
bank->irq_mask);
}
-EXPORT_SYMBOL_GPL(adf_response_handler);
static inline int adf_get_cfg_int(struct adf_accel_dev *accel_dev,
const char *section, const char *format,
@@ -447,6 +434,7 @@ static int adf_init_bank(struct adf_accel_dev *accel_dev,
goto err;
}
+ WRITE_CSR_INT_FLAG(csr_addr, bank_num, ADF_BANK_INT_FLAG_CLEAR_MASK);
WRITE_CSR_INT_SRCSEL(csr_addr, bank_num);
return 0;
err:
diff --git a/drivers/crypto/qat/qat_common/adf_transport_access_macros.h b/drivers/crypto/qat/qat_common/adf_transport_access_macros.h
index 6ad7e4e1edca..80e02a2a0a09 100644
--- a/drivers/crypto/qat/qat_common/adf_transport_access_macros.h
+++ b/drivers/crypto/qat/qat_common/adf_transport_access_macros.h
@@ -50,12 +50,14 @@
#include "adf_accel_devices.h"
#define ADF_BANK_INT_SRC_SEL_MASK_0 0x4444444CUL
#define ADF_BANK_INT_SRC_SEL_MASK_X 0x44444444UL
+#define ADF_BANK_INT_FLAG_CLEAR_MASK 0xFFFF
#define ADF_RING_CSR_RING_CONFIG 0x000
#define ADF_RING_CSR_RING_LBASE 0x040
#define ADF_RING_CSR_RING_UBASE 0x080
#define ADF_RING_CSR_RING_HEAD 0x0C0
#define ADF_RING_CSR_RING_TAIL 0x100
#define ADF_RING_CSR_E_STAT 0x14C
+#define ADF_RING_CSR_INT_FLAG 0x170
#define ADF_RING_CSR_INT_SRCSEL 0x174
#define ADF_RING_CSR_INT_SRCSEL_2 0x178
#define ADF_RING_CSR_INT_COL_EN 0x17C
@@ -144,6 +146,9 @@ do { \
#define WRITE_CSR_RING_TAIL(csr_base_addr, bank, ring, value) \
ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * bank) + \
ADF_RING_CSR_RING_TAIL + (ring << 2), value)
+#define WRITE_CSR_INT_FLAG(csr_base_addr, bank, value) \
+ ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * (bank)) + \
+ ADF_RING_CSR_INT_FLAG, value)
#define WRITE_CSR_INT_SRCSEL(csr_base_addr, bank) \
do { \
ADF_CSR_WR(csr_base_addr, (ADF_RING_BUNDLE_SIZE * bank) + \
diff --git a/drivers/crypto/qat/qat_common/adf_transport_internal.h b/drivers/crypto/qat/qat_common/adf_transport_internal.h
index a4869627fd57..bb883368ac01 100644
--- a/drivers/crypto/qat/qat_common/adf_transport_internal.h
+++ b/drivers/crypto/qat/qat_common/adf_transport_internal.h
@@ -91,7 +91,7 @@ struct adf_etr_data {
struct dentry *debug;
};
-void adf_response_handler(unsigned long bank_addr);
+void adf_response_handler(uintptr_t bank_addr);
#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
int adf_bank_debugfs_add(struct adf_etr_bank_data *bank);
diff --git a/drivers/crypto/qat/qat_dh895xccvf/adf_isr.c b/drivers/crypto/qat/qat_common/adf_vf_isr.c
index 87c5d8adb125..09427b3d4d55 100644
--- a/drivers/crypto/qat/qat_dh895xccvf/adf_isr.c
+++ b/drivers/crypto/qat/qat_common/adf_vf_isr.c
@@ -51,16 +51,18 @@
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
-#include <adf_accel_devices.h>
-#include <adf_common_drv.h>
-#include <adf_cfg.h>
-#include <adf_cfg_strings.h>
-#include <adf_cfg_common.h>
-#include <adf_transport_access_macros.h>
-#include <adf_transport_internal.h>
-#include <adf_pf2vf_msg.h>
-#include "adf_drv.h"
-#include "adf_dh895xccvf_hw_data.h"
+#include "adf_accel_devices.h"
+#include "adf_common_drv.h"
+#include "adf_cfg.h"
+#include "adf_cfg_strings.h"
+#include "adf_cfg_common.h"
+#include "adf_transport_access_macros.h"
+#include "adf_transport_internal.h"
+#include "adf_pf2vf_msg.h"
+
+#define ADF_VINTSOU_OFFSET 0x204
+#define ADF_VINTSOU_BUN BIT(0)
+#define ADF_VINTSOU_PF2VF BIT(1)
static int adf_enable_msi(struct adf_accel_dev *accel_dev)
{
@@ -91,12 +93,14 @@ static void adf_disable_msi(struct adf_accel_dev *accel_dev)
static void adf_pf2vf_bh_handler(void *data)
{
struct adf_accel_dev *accel_dev = data;
- void __iomem *pmisc_bar_addr =
- (&GET_BARS(accel_dev)[ADF_DH895XCCIOV_PMISC_BAR])->virt_addr;
+ struct adf_hw_device_data *hw_data = accel_dev->hw_device;
+ struct adf_bar *pmisc =
+ &GET_BARS(accel_dev)[hw_data->get_misc_bar_id(hw_data)];
+ void __iomem *pmisc_bar_addr = pmisc->virt_addr;
u32 msg;
/* Read the message from PF */
- msg = ADF_CSR_RD(pmisc_bar_addr, ADF_DH895XCCIOV_PF2VF_OFFSET);
+ msg = ADF_CSR_RD(pmisc_bar_addr, hw_data->get_pf2vf_offset(0));
if (!(msg & ADF_PF2VF_MSGORIGIN_SYSTEM))
/* Ignore legacy non-system (non-kernel) PF2VF messages */
@@ -124,8 +128,8 @@ static void adf_pf2vf_bh_handler(void *data)
}
/* To ack, clear the PF2VFINT bit */
- msg &= ~ADF_DH895XCC_PF2VF_PF2VFINT;
- ADF_CSR_WR(pmisc_bar_addr, ADF_DH895XCCIOV_PF2VF_OFFSET, msg);
+ msg &= ~BIT(0);
+ ADF_CSR_WR(pmisc_bar_addr, hw_data->get_pf2vf_offset(0), msg);
/* Re-enable PF2VF interrupts */
adf_enable_pf2vf_interrupts(accel_dev);
@@ -155,15 +159,17 @@ static void adf_cleanup_pf2vf_bh(struct adf_accel_dev *accel_dev)
static irqreturn_t adf_isr(int irq, void *privdata)
{
struct adf_accel_dev *accel_dev = privdata;
- void __iomem *pmisc_bar_addr =
- (&GET_BARS(accel_dev)[ADF_DH895XCCIOV_PMISC_BAR])->virt_addr;
+ struct adf_hw_device_data *hw_data = accel_dev->hw_device;
+ struct adf_bar *pmisc =
+ &GET_BARS(accel_dev)[hw_data->get_misc_bar_id(hw_data)];
+ void __iomem *pmisc_bar_addr = pmisc->virt_addr;
u32 v_int;
/* Read VF INT source CSR to determine the source of VF interrupt */
- v_int = ADF_CSR_RD(pmisc_bar_addr, ADF_DH895XCCIOV_VINTSOU_OFFSET);
+ v_int = ADF_CSR_RD(pmisc_bar_addr, ADF_VINTSOU_OFFSET);
/* Check for PF2VF interrupt */
- if (v_int & ADF_DH895XCC_VINTSOU_PF2VF) {
+ if (v_int & ADF_VINTSOU_PF2VF) {
/* Disable PF to VF interrupt */
adf_disable_pf2vf_interrupts(accel_dev);
@@ -173,7 +179,7 @@ static irqreturn_t adf_isr(int irq, void *privdata)
}
/* Check bundle interrupt */
- if (v_int & ADF_DH895XCC_VINTSOU_BUN) {
+ if (v_int & ADF_VINTSOU_BUN) {
struct adf_etr_data *etr_data = accel_dev->transport;
struct adf_etr_bank_data *bank = &etr_data->banks[0];
@@ -226,6 +232,12 @@ static void adf_cleanup_bh(struct adf_accel_dev *accel_dev)
tasklet_kill(&priv_data->banks[0].resp_handler);
}
+/**
+ * adf_vf_isr_resource_free() - Free IRQ for acceleration device
+ * @accel_dev: Pointer to acceleration device.
+ *
+ * Function frees interrupts for acceleration device virtual function.
+ */
void adf_vf_isr_resource_free(struct adf_accel_dev *accel_dev)
{
struct pci_dev *pdev = accel_to_pci_dev(accel_dev);
@@ -236,7 +248,16 @@ void adf_vf_isr_resource_free(struct adf_accel_dev *accel_dev)
adf_cleanup_pf2vf_bh(accel_dev);
adf_disable_msi(accel_dev);
}
-
+EXPORT_SYMBOL_GPL(adf_vf_isr_resource_free);
+
+/**
+ * adf_vf_isr_resource_alloc() - Allocate IRQ for acceleration device
+ * @accel_dev: Pointer to acceleration device.
+ *
+ * Function allocates interrupts for acceleration device virtual function.
+ *
+ * Return: 0 on success, error code otherwise.
+ */
int adf_vf_isr_resource_alloc(struct adf_accel_dev *accel_dev)
{
if (adf_enable_msi(accel_dev))
@@ -256,3 +277,4 @@ err_out:
adf_vf_isr_resource_free(accel_dev);
return -EFAULT;
}
+EXPORT_SYMBOL_GPL(adf_vf_isr_resource_alloc);
diff --git a/drivers/crypto/qat/qat_common/icp_qat_fw_loader_handle.h b/drivers/crypto/qat/qat_common/icp_qat_fw_loader_handle.h
index 5e1aa40c0404..2ffef3e4fd68 100644
--- a/drivers/crypto/qat/qat_common/icp_qat_fw_loader_handle.h
+++ b/drivers/crypto/qat/qat_common/icp_qat_fw_loader_handle.h
@@ -68,11 +68,21 @@ struct icp_qat_fw_loader_hal_handle {
struct icp_qat_fw_loader_handle {
struct icp_qat_fw_loader_hal_handle *hal_handle;
+ struct pci_dev *pci_dev;
void *obj_handle;
+ void *sobj_handle;
+ bool fw_auth;
void __iomem *hal_sram_addr_v;
void __iomem *hal_cap_g_ctl_csr_addr_v;
void __iomem *hal_cap_ae_xfer_csr_addr_v;
void __iomem *hal_cap_ae_local_csr_addr_v;
void __iomem *hal_ep_csr_addr_v;
};
+
+struct icp_firml_dram_desc {
+ void __iomem *dram_base_addr;
+ void *dram_base_addr_v;
+ dma_addr_t dram_bus_addr;
+ u64 dram_size;
+};
#endif
diff --git a/drivers/crypto/qat/qat_common/icp_qat_hal.h b/drivers/crypto/qat/qat_common/icp_qat_hal.h
index 85b6d241ea82..7187917533d0 100644
--- a/drivers/crypto/qat/qat_common/icp_qat_hal.h
+++ b/drivers/crypto/qat/qat_common/icp_qat_hal.h
@@ -81,6 +81,31 @@ enum hal_ae_csr {
LOCAL_CSR_STATUS = 0x180,
};
+enum fcu_csr {
+ FCU_CONTROL = 0x8c0,
+ FCU_STATUS = 0x8c4,
+ FCU_STATUS1 = 0x8c8,
+ FCU_DRAM_ADDR_LO = 0x8cc,
+ FCU_DRAM_ADDR_HI = 0x8d0,
+ FCU_RAMBASE_ADDR_HI = 0x8d4,
+ FCU_RAMBASE_ADDR_LO = 0x8d8
+};
+
+enum fcu_cmd {
+ FCU_CTRL_CMD_NOOP = 0,
+ FCU_CTRL_CMD_AUTH = 1,
+ FCU_CTRL_CMD_LOAD = 2,
+ FCU_CTRL_CMD_START = 3
+};
+
+enum fcu_sts {
+ FCU_STS_NO_STS = 0,
+ FCU_STS_VERI_DONE = 1,
+ FCU_STS_LOAD_DONE = 2,
+ FCU_STS_VERI_FAIL = 3,
+ FCU_STS_LOAD_FAIL = 4,
+ FCU_STS_BUSY = 5
+};
#define UA_ECS (0x1 << 31)
#define ACS_ABO_BITPOS 31
#define ACS_ACNO 0x7
@@ -98,6 +123,13 @@ enum hal_ae_csr {
#define LCS_STATUS (0x1)
#define MMC_SHARE_CS_BITPOS 2
#define GLOBAL_CSR 0xA00
+#define FCU_CTRL_AE_POS 0x8
+#define FCU_AUTH_STS_MASK 0x7
+#define FCU_STS_DONE_POS 0x9
+#define FCU_STS_AUTHFWLD_POS 0X8
+#define FCU_LOADED_AE_POS 0x16
+#define FW_AUTH_WAIT_PERIOD 10
+#define FW_AUTH_MAX_RETRY 300
#define SET_CAP_CSR(handle, csr, val) \
ADF_CSR_WR(handle->hal_cap_g_ctl_csr_addr_v, csr, val)
@@ -106,14 +138,14 @@ enum hal_ae_csr {
#define SET_GLB_CSR(handle, csr, val) SET_CAP_CSR(handle, csr + GLOBAL_CSR, val)
#define GET_GLB_CSR(handle, csr) GET_CAP_CSR(handle, GLOBAL_CSR + csr)
#define AE_CSR(handle, ae) \
- (handle->hal_cap_ae_local_csr_addr_v + \
+ ((char __iomem *)handle->hal_cap_ae_local_csr_addr_v + \
((ae & handle->hal_handle->ae_mask) << 12))
#define AE_CSR_ADDR(handle, ae, csr) (AE_CSR(handle, ae) + (0x3ff & csr))
#define SET_AE_CSR(handle, ae, csr, val) \
ADF_CSR_WR(AE_CSR_ADDR(handle, ae, csr), 0, val)
#define GET_AE_CSR(handle, ae, csr) ADF_CSR_RD(AE_CSR_ADDR(handle, ae, csr), 0)
#define AE_XFER(handle, ae) \
- (handle->hal_cap_ae_xfer_csr_addr_v + \
+ ((char __iomem *)handle->hal_cap_ae_xfer_csr_addr_v + \
((ae & handle->hal_handle->ae_mask) << 12))
#define AE_XFER_ADDR(handle, ae, reg) (AE_XFER(handle, ae) + \
((reg & 0xff) << 2))
@@ -121,5 +153,4 @@ enum hal_ae_csr {
ADF_CSR_WR(AE_XFER_ADDR(handle, ae, reg), 0, val)
#define SRAM_WRITE(handle, addr, val) \
ADF_CSR_WR(handle->hal_sram_addr_v, addr, val)
-#define SRAM_READ(handle, addr) ADF_CSR_RD(handle->hal_sram_addr_v, addr)
#endif
diff --git a/drivers/crypto/qat/qat_common/icp_qat_uclo.h b/drivers/crypto/qat/qat_common/icp_qat_uclo.h
index 2132a8cbc4ec..d97db990955d 100644
--- a/drivers/crypto/qat/qat_common/icp_qat_uclo.h
+++ b/drivers/crypto/qat/qat_common/icp_qat_uclo.h
@@ -47,32 +47,55 @@
#ifndef __ICP_QAT_UCLO_H__
#define __ICP_QAT_UCLO_H__
-#define ICP_QAT_AC_C_CPU_TYPE 0x00400000
+#define ICP_QAT_AC_895XCC_DEV_TYPE 0x00400000
+#define ICP_QAT_AC_C62X_DEV_TYPE 0x01000000
+#define ICP_QAT_AC_C3XXX_DEV_TYPE 0x02000000
#define ICP_QAT_UCLO_MAX_AE 12
#define ICP_QAT_UCLO_MAX_CTX 8
#define ICP_QAT_UCLO_MAX_UIMAGE (ICP_QAT_UCLO_MAX_AE * ICP_QAT_UCLO_MAX_CTX)
#define ICP_QAT_UCLO_MAX_USTORE 0x4000
#define ICP_QAT_UCLO_MAX_XFER_REG 128
#define ICP_QAT_UCLO_MAX_GPR_REG 128
-#define ICP_QAT_UCLO_MAX_NN_REG 128
#define ICP_QAT_UCLO_MAX_LMEM_REG 1024
#define ICP_QAT_UCLO_AE_ALL_CTX 0xff
#define ICP_QAT_UOF_OBJID_LEN 8
#define ICP_QAT_UOF_FID 0xc6c2
#define ICP_QAT_UOF_MAJVER 0x4
#define ICP_QAT_UOF_MINVER 0x11
-#define ICP_QAT_UOF_NN_MODE_NOTCARE 0xff
#define ICP_QAT_UOF_OBJS "UOF_OBJS"
#define ICP_QAT_UOF_STRT "UOF_STRT"
-#define ICP_QAT_UOF_GTID "UOF_GTID"
#define ICP_QAT_UOF_IMAG "UOF_IMAG"
#define ICP_QAT_UOF_IMEM "UOF_IMEM"
-#define ICP_QAT_UOF_MSEG "UOF_MSEG"
#define ICP_QAT_UOF_LOCAL_SCOPE 1
#define ICP_QAT_UOF_INIT_EXPR 0
#define ICP_QAT_UOF_INIT_REG 1
#define ICP_QAT_UOF_INIT_REG_CTX 2
#define ICP_QAT_UOF_INIT_EXPR_ENDIAN_SWAP 3
+#define ICP_QAT_SUOF_OBJ_ID_LEN 8
+#define ICP_QAT_SUOF_FID 0x53554f46
+#define ICP_QAT_SUOF_MAJVER 0x0
+#define ICP_QAT_SUOF_MINVER 0x1
+#define ICP_QAT_SIMG_AE_INIT_SEQ_LEN (50 * sizeof(unsigned long long))
+#define ICP_QAT_SIMG_AE_INSTS_LEN (0x4000 * sizeof(unsigned long long))
+#define ICP_QAT_CSS_FWSK_MODULUS_LEN 256
+#define ICP_QAT_CSS_FWSK_EXPONENT_LEN 4
+#define ICP_QAT_CSS_FWSK_PAD_LEN 252
+#define ICP_QAT_CSS_FWSK_PUB_LEN (ICP_QAT_CSS_FWSK_MODULUS_LEN + \
+ ICP_QAT_CSS_FWSK_EXPONENT_LEN + \
+ ICP_QAT_CSS_FWSK_PAD_LEN)
+#define ICP_QAT_CSS_SIGNATURE_LEN 256
+#define ICP_QAT_CSS_AE_IMG_LEN (sizeof(struct icp_qat_simg_ae_mode) + \
+ ICP_QAT_SIMG_AE_INIT_SEQ_LEN + \
+ ICP_QAT_SIMG_AE_INSTS_LEN)
+#define ICP_QAT_CSS_AE_SIMG_LEN (sizeof(struct icp_qat_css_hdr) + \
+ ICP_QAT_CSS_FWSK_PUB_LEN + \
+ ICP_QAT_CSS_SIGNATURE_LEN + \
+ ICP_QAT_CSS_AE_IMG_LEN)
+#define ICP_QAT_AE_IMG_OFFSET (sizeof(struct icp_qat_css_hdr) + \
+ ICP_QAT_CSS_FWSK_MODULUS_LEN + \
+ ICP_QAT_CSS_FWSK_EXPONENT_LEN + \
+ ICP_QAT_CSS_SIGNATURE_LEN)
+#define ICP_QAT_CSS_MAX_IMAGE_LEN 0x40000
#define ICP_QAT_CTX_MODE(ae_mode) ((ae_mode) & 0xf)
#define ICP_QAT_NN_MODE(ae_mode) (((ae_mode) >> 0x4) & 0xf)
@@ -112,6 +135,11 @@ enum icp_qat_uof_regtype {
ICP_NEIGH_REL,
};
+enum icp_qat_css_fwtype {
+ CSS_AE_FIRMWARE = 0,
+ CSS_MMP_FIRMWARE = 1
+};
+
struct icp_qat_uclo_page {
struct icp_qat_uclo_encap_page *encap_page;
struct icp_qat_uclo_region *region;
@@ -235,7 +263,7 @@ struct icp_qat_uof_filechunkhdr {
};
struct icp_qat_uof_objhdr {
- unsigned int cpu_type;
+ unsigned int ac_dev_type;
unsigned short min_cpu_ver;
unsigned short max_cpu_ver;
short max_chunks;
@@ -326,7 +354,7 @@ struct icp_qat_uof_image {
unsigned int img_name;
unsigned int ae_assigned;
unsigned int ctx_assigned;
- unsigned int cpu_type;
+ unsigned int ac_dev_type;
unsigned int entry_address;
unsigned int fill_pattern[2];
unsigned int reloadable_size;
@@ -374,4 +402,127 @@ struct icp_qat_uof_batch_init {
unsigned int size;
struct icp_qat_uof_batch_init *next;
};
+
+struct icp_qat_suof_img_hdr {
+ char *simg_buf;
+ unsigned long simg_len;
+ char *css_header;
+ char *css_key;
+ char *css_signature;
+ char *css_simg;
+ unsigned long simg_size;
+ unsigned int ae_num;
+ unsigned int ae_mask;
+ unsigned int fw_type;
+ unsigned long simg_name;
+ unsigned long appmeta_data;
+};
+
+struct icp_qat_suof_img_tbl {
+ unsigned int num_simgs;
+ struct icp_qat_suof_img_hdr *simg_hdr;
+};
+
+struct icp_qat_suof_handle {
+ unsigned int file_id;
+ unsigned int check_sum;
+ char min_ver;
+ char maj_ver;
+ char fw_type;
+ char *suof_buf;
+ unsigned int suof_size;
+ char *sym_str;
+ unsigned int sym_size;
+ struct icp_qat_suof_img_tbl img_table;
+};
+
+struct icp_qat_fw_auth_desc {
+ unsigned int img_len;
+ unsigned int reserved;
+ unsigned int css_hdr_high;
+ unsigned int css_hdr_low;
+ unsigned int img_high;
+ unsigned int img_low;
+ unsigned int signature_high;
+ unsigned int signature_low;
+ unsigned int fwsk_pub_high;
+ unsigned int fwsk_pub_low;
+ unsigned int img_ae_mode_data_high;
+ unsigned int img_ae_mode_data_low;
+ unsigned int img_ae_init_data_high;
+ unsigned int img_ae_init_data_low;
+ unsigned int img_ae_insts_high;
+ unsigned int img_ae_insts_low;
+};
+
+struct icp_qat_auth_chunk {
+ struct icp_qat_fw_auth_desc fw_auth_desc;
+ u64 chunk_size;
+ u64 chunk_bus_addr;
+};
+
+struct icp_qat_css_hdr {
+ unsigned int module_type;
+ unsigned int header_len;
+ unsigned int header_ver;
+ unsigned int module_id;
+ unsigned int module_vendor;
+ unsigned int date;
+ unsigned int size;
+ unsigned int key_size;
+ unsigned int module_size;
+ unsigned int exponent_size;
+ unsigned int fw_type;
+ unsigned int reserved[21];
+};
+
+struct icp_qat_simg_ae_mode {
+ unsigned int file_id;
+ unsigned short maj_ver;
+ unsigned short min_ver;
+ unsigned int dev_type;
+ unsigned short devmax_ver;
+ unsigned short devmin_ver;
+ unsigned int ae_mask;
+ unsigned int ctx_enables;
+ char fw_type;
+ char ctx_mode;
+ char nn_mode;
+ char lm0_mode;
+ char lm1_mode;
+ char scs_mode;
+ char lm2_mode;
+ char lm3_mode;
+ char tindex_mode;
+ unsigned char reserved[7];
+ char simg_name[256];
+ char appmeta_data[256];
+};
+
+struct icp_qat_suof_filehdr {
+ unsigned int file_id;
+ unsigned int check_sum;
+ char min_ver;
+ char maj_ver;
+ char fw_type;
+ char reserved;
+ unsigned short max_chunks;
+ unsigned short num_chunks;
+};
+
+struct icp_qat_suof_chunk_hdr {
+ char chunk_id[ICP_QAT_SUOF_OBJ_ID_LEN];
+ u64 offset;
+ u64 size;
+};
+
+struct icp_qat_suof_strtable {
+ unsigned int tab_length;
+ unsigned int strings;
+};
+
+struct icp_qat_suof_objhdr {
+ unsigned int img_length;
+ unsigned int reserved;
+};
#endif
diff --git a/drivers/crypto/qat/qat_common/qat_crypto.c b/drivers/crypto/qat/qat_common/qat_crypto.c
index 9cab15497f04..3852d31ce0a4 100644
--- a/drivers/crypto/qat/qat_common/qat_crypto.c
+++ b/drivers/crypto/qat/qat_common/qat_crypto.c
@@ -49,6 +49,7 @@
#include "adf_accel_devices.h"
#include "adf_common_drv.h"
#include "adf_transport.h"
+#include "adf_transport_access_macros.h"
#include "adf_cfg.h"
#include "adf_cfg_strings.h"
#include "qat_crypto.h"
@@ -66,13 +67,10 @@ void qat_crypto_put_instance(struct qat_crypto_instance *inst)
static int qat_crypto_free_instances(struct adf_accel_dev *accel_dev)
{
- struct qat_crypto_instance *inst;
- struct list_head *list_ptr, *tmp;
+ struct qat_crypto_instance *inst, *tmp;
int i;
- list_for_each_safe(list_ptr, tmp, &accel_dev->crypto_list) {
- inst = list_entry(list_ptr, struct qat_crypto_instance, list);
-
+ list_for_each_entry_safe(inst, tmp, &accel_dev->crypto_list, list) {
for (i = 0; i < atomic_read(&inst->refctr); i++)
qat_crypto_put_instance(inst);
@@ -88,7 +86,7 @@ static int qat_crypto_free_instances(struct adf_accel_dev *accel_dev)
if (inst->pke_rx)
adf_remove_ring(inst->pke_rx);
- list_del(list_ptr);
+ list_del(&inst->list);
kfree(inst);
}
return 0;
@@ -96,17 +94,13 @@ static int qat_crypto_free_instances(struct adf_accel_dev *accel_dev)
struct qat_crypto_instance *qat_crypto_get_instance_node(int node)
{
- struct adf_accel_dev *accel_dev = NULL;
- struct qat_crypto_instance *inst = NULL;
- struct list_head *itr;
+ struct adf_accel_dev *accel_dev = NULL, *tmp_dev;
+ struct qat_crypto_instance *inst = NULL, *tmp_inst;
unsigned long best = ~0;
- list_for_each(itr, adf_devmgr_get_head()) {
- struct adf_accel_dev *tmp_dev;
+ list_for_each_entry(tmp_dev, adf_devmgr_get_head(), list) {
unsigned long ctr;
- tmp_dev = list_entry(itr, struct adf_accel_dev, list);
-
if ((node == dev_to_node(&GET_DEV(tmp_dev)) ||
dev_to_node(&GET_DEV(tmp_dev)) < 0) &&
adf_dev_started(tmp_dev) &&
@@ -118,19 +112,16 @@ struct qat_crypto_instance *qat_crypto_get_instance_node(int node)
}
}
}
- if (!accel_dev)
- pr_info("QAT: Could not find a device on node %d\n", node);
-
- /* Get any started device */
- list_for_each(itr, adf_devmgr_get_head()) {
- struct adf_accel_dev *tmp_dev;
- tmp_dev = list_entry(itr, struct adf_accel_dev, list);
-
- if (adf_dev_started(tmp_dev) &&
- !list_empty(&tmp_dev->crypto_list)) {
- accel_dev = tmp_dev;
- break;
+ if (!accel_dev) {
+ pr_info("QAT: Could not find a device on node %d\n", node);
+ /* Get any started device */
+ list_for_each_entry(tmp_dev, adf_devmgr_get_head(), list) {
+ if (adf_dev_started(tmp_dev) &&
+ !list_empty(&tmp_dev->crypto_list)) {
+ accel_dev = tmp_dev;
+ break;
+ }
}
}
@@ -138,11 +129,9 @@ struct qat_crypto_instance *qat_crypto_get_instance_node(int node)
return NULL;
best = ~0;
- list_for_each(itr, &accel_dev->crypto_list) {
- struct qat_crypto_instance *tmp_inst;
+ list_for_each_entry(tmp_inst, &accel_dev->crypto_list, list) {
unsigned long ctr;
- tmp_inst = list_entry(itr, struct qat_crypto_instance, list);
ctr = atomic_read(&tmp_inst->refctr);
if (best > ctr) {
inst = tmp_inst;
@@ -159,6 +148,97 @@ struct qat_crypto_instance *qat_crypto_get_instance_node(int node)
return inst;
}
+/**
+ * qat_crypto_dev_config() - create dev config required to create crypto inst.
+ *
+ * @accel_dev: Pointer to acceleration device.
+ *
+ * Function creates device configuration required to create crypto instances
+ *
+ * Return: 0 on success, error code otherwise.
+ */
+int qat_crypto_dev_config(struct adf_accel_dev *accel_dev)
+{
+ int cpus = num_online_cpus();
+ int banks = GET_MAX_BANKS(accel_dev);
+ int instances = min(cpus, banks);
+ char key[ADF_CFG_MAX_KEY_LEN_IN_BYTES];
+ int i;
+ unsigned long val;
+
+ if (adf_cfg_section_add(accel_dev, ADF_KERNEL_SEC))
+ goto err;
+ if (adf_cfg_section_add(accel_dev, "Accelerator0"))
+ goto err;
+ for (i = 0; i < instances; i++) {
+ val = i;
+ snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_BANK_NUM, i);
+ if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
+ key, (void *)&val, ADF_DEC))
+ goto err;
+
+ snprintf(key, sizeof(key), ADF_CY "%d" ADF_ETRMGR_CORE_AFFINITY,
+ i);
+ if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
+ key, (void *)&val, ADF_DEC))
+ goto err;
+
+ snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_SIZE, i);
+ val = 128;
+ if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
+ key, (void *)&val, ADF_DEC))
+ goto err;
+
+ val = 512;
+ snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_SIZE, i);
+ if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
+ key, (void *)&val, ADF_DEC))
+ goto err;
+
+ val = 0;
+ snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_TX, i);
+ if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
+ key, (void *)&val, ADF_DEC))
+ goto err;
+
+ val = 2;
+ snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_TX, i);
+ if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
+ key, (void *)&val, ADF_DEC))
+ goto err;
+
+ val = 8;
+ snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_RX, i);
+ if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
+ key, (void *)&val, ADF_DEC))
+ goto err;
+
+ val = 10;
+ snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_RX, i);
+ if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
+ key, (void *)&val, ADF_DEC))
+ goto err;
+
+ val = ADF_COALESCING_DEF_TIME;
+ snprintf(key, sizeof(key), ADF_ETRMGR_COALESCE_TIMER_FORMAT, i);
+ if (adf_cfg_add_key_value_param(accel_dev, "Accelerator0",
+ key, (void *)&val, ADF_DEC))
+ goto err;
+ }
+
+ val = i;
+ if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
+ ADF_NUM_CY, (void *)&val, ADF_DEC))
+ goto err;
+
+ set_bit(ADF_STATUS_CONFIGURED, &accel_dev->status);
+ return 0;
+err:
+ dev_err(&GET_DEV(accel_dev), "Failed to start QAT accel dev\n");
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(qat_crypto_dev_config);
+
static int qat_crypto_create_instances(struct adf_accel_dev *accel_dev)
{
int i;
diff --git a/drivers/crypto/qat/qat_common/qat_hal.c b/drivers/crypto/qat/qat_common/qat_hal.c
index 380e761801a7..0ac0ba867611 100644
--- a/drivers/crypto/qat/qat_common/qat_hal.c
+++ b/drivers/crypto/qat/qat_common/qat_hal.c
@@ -45,21 +45,22 @@
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/slab.h>
+#include <linux/delay.h>
#include "adf_accel_devices.h"
#include "adf_common_drv.h"
#include "icp_qat_hal.h"
#include "icp_qat_uclo.h"
-#define BAD_REGADDR 0xffff
-#define MAX_RETRY_TIMES 10000
-#define INIT_CTX_ARB_VALUE 0x0
+#define BAD_REGADDR 0xffff
+#define MAX_RETRY_TIMES 10000
+#define INIT_CTX_ARB_VALUE 0x0
#define INIT_CTX_ENABLE_VALUE 0x0
-#define INIT_PC_VALUE 0x0
+#define INIT_PC_VALUE 0x0
#define INIT_WAKEUP_EVENTS_VALUE 0x1
#define INIT_SIG_EVENTS_VALUE 0x1
#define INIT_CCENABLE_VALUE 0x2000
-#define RST_CSR_QAT_LSB 20
+#define RST_CSR_QAT_LSB 20
#define RST_CSR_AE_LSB 0
#define MC_TIMESTAMP_ENABLE (0x1 << 7)
@@ -185,7 +186,7 @@ static int qat_hal_wait_cycles(struct icp_qat_fw_loader_handle *handle,
if (elapsed_cycles >= 8 && !(csr & (1 << ACS_ABO_BITPOS)))
return 0;
}
- if (!times) {
+ if (times < 0) {
pr_err("QAT: wait_num_cycles time out\n");
return -EFAULT;
}
@@ -391,9 +392,6 @@ static int qat_hal_check_ae_alive(struct icp_qat_fw_loader_handle *handle)
unsigned int times = MAX_RETRY_TIMES;
for (ae = 0; ae < handle->hal_handle->ae_max_num; ae++) {
- if (!(handle->hal_handle->ae_mask & (1 << ae)))
- continue;
-
qat_hal_rd_ae_csr(handle, ae, PROFILE_COUNT,
(unsigned int *)&base_cnt);
base_cnt &= 0xffff;
@@ -413,6 +411,20 @@ static int qat_hal_check_ae_alive(struct icp_qat_fw_loader_handle *handle)
return 0;
}
+int qat_hal_check_ae_active(struct icp_qat_fw_loader_handle *handle,
+ unsigned int ae)
+{
+ unsigned int enable = 0, active = 0;
+
+ qat_hal_rd_ae_csr(handle, ae, CTX_ENABLES, &enable);
+ qat_hal_rd_ae_csr(handle, ae, ACTIVE_CTX_STATUS, &active);
+ if ((enable & (0xff << CE_ENABLE_BITPOS)) ||
+ (active & (1 << ACS_ABO_BITPOS)))
+ return 1;
+ else
+ return 0;
+}
+
static void qat_hal_reset_timestamp(struct icp_qat_fw_loader_handle *handle)
{
unsigned int misc_ctl;
@@ -425,8 +437,6 @@ static void qat_hal_reset_timestamp(struct icp_qat_fw_loader_handle *handle)
(~MC_TIMESTAMP_ENABLE));
for (ae = 0; ae < handle->hal_handle->ae_max_num; ae++) {
- if (!(handle->hal_handle->ae_mask & (1 << ae)))
- continue;
qat_hal_wr_ae_csr(handle, ae, TIMESTAMP_LOW, 0);
qat_hal_wr_ae_csr(handle, ae, TIMESTAMP_HIGH, 0);
}
@@ -440,8 +450,9 @@ static void qat_hal_reset_timestamp(struct icp_qat_fw_loader_handle *handle)
#define ESRAM_AUTO_INIT_CSR_OFFSET 0xC1C
static int qat_hal_init_esram(struct icp_qat_fw_loader_handle *handle)
{
- void __iomem *csr_addr = handle->hal_ep_csr_addr_v +
- ESRAM_AUTO_INIT_CSR_OFFSET;
+ void __iomem *csr_addr =
+ (void __iomem *)((uintptr_t)handle->hal_ep_csr_addr_v +
+ ESRAM_AUTO_INIT_CSR_OFFSET);
unsigned int csr_val, times = 30;
csr_val = ADF_CSR_RD(csr_addr, 0);
@@ -493,8 +504,6 @@ int qat_hal_clr_reset(struct icp_qat_fw_loader_handle *handle)
/* Set undefined power-up/reset states to reasonable default values */
for (ae = 0; ae < handle->hal_handle->ae_max_num; ae++) {
- if (!(handle->hal_handle->ae_mask & (1 << ae)))
- continue;
qat_hal_wr_ae_csr(handle, ae, CTX_ENABLES,
INIT_CTX_ENABLE_VALUE);
qat_hal_wr_indr_csr(handle, ae, ICP_QAT_UCLO_AE_ALL_CTX,
@@ -598,25 +607,31 @@ static void qat_hal_enable_ctx(struct icp_qat_fw_loader_handle *handle,
qat_hal_wr_ae_csr(handle, ae, CTX_ENABLES, ctx);
}
-static int qat_hal_clear_gpr(struct icp_qat_fw_loader_handle *handle)
+static void qat_hal_clear_xfer(struct icp_qat_fw_loader_handle *handle)
{
unsigned char ae;
- unsigned int ctx_mask = ICP_QAT_UCLO_AE_ALL_CTX;
- int times = MAX_RETRY_TIMES;
- unsigned int csr_val = 0;
unsigned short reg;
- unsigned int savctx = 0;
- int ret = 0;
for (ae = 0; ae < handle->hal_handle->ae_max_num; ae++) {
- if (!(handle->hal_handle->ae_mask & (1 << ae)))
- continue;
for (reg = 0; reg < ICP_QAT_UCLO_MAX_GPR_REG; reg++) {
qat_hal_init_rd_xfer(handle, ae, 0, ICP_SR_RD_ABS,
reg, 0);
qat_hal_init_rd_xfer(handle, ae, 0, ICP_DR_RD_ABS,
reg, 0);
}
+ }
+}
+
+static int qat_hal_clear_gpr(struct icp_qat_fw_loader_handle *handle)
+{
+ unsigned char ae;
+ unsigned int ctx_mask = ICP_QAT_UCLO_AE_ALL_CTX;
+ int times = MAX_RETRY_TIMES;
+ unsigned int csr_val = 0;
+ unsigned int savctx = 0;
+ int ret = 0;
+
+ for (ae = 0; ae < handle->hal_handle->ae_max_num; ae++) {
qat_hal_rd_ae_csr(handle, ae, AE_MISC_CONTROL, &csr_val);
csr_val &= ~(1 << MMC_SHARE_CS_BITPOS);
qat_hal_wr_ae_csr(handle, ae, AE_MISC_CONTROL, csr_val);
@@ -638,8 +653,6 @@ static int qat_hal_clear_gpr(struct icp_qat_fw_loader_handle *handle)
qat_hal_enable_ctx(handle, ae, ctx_mask);
}
for (ae = 0; ae < handle->hal_handle->ae_max_num; ae++) {
- if (!(handle->hal_handle->ae_mask & (1 << ae)))
- continue;
/* wait for AE to finish */
do {
ret = qat_hal_wait_cycles(handle, ae, 20, 1);
@@ -667,10 +680,10 @@ static int qat_hal_clear_gpr(struct icp_qat_fw_loader_handle *handle)
return 0;
}
-#define ICP_DH895XCC_AE_OFFSET 0x20000
-#define ICP_DH895XCC_CAP_OFFSET (ICP_DH895XCC_AE_OFFSET + 0x10000)
+#define ICP_QAT_AE_OFFSET 0x20000
+#define ICP_QAT_CAP_OFFSET (ICP_QAT_AE_OFFSET + 0x10000)
#define LOCAL_TO_XFER_REG_OFFSET 0x800
-#define ICP_DH895XCC_EP_OFFSET 0x3a000
+#define ICP_QAT_EP_OFFSET 0x3a000
int qat_hal_init(struct adf_accel_dev *accel_dev)
{
unsigned char ae;
@@ -687,15 +700,22 @@ int qat_hal_init(struct adf_accel_dev *accel_dev)
if (!handle)
return -ENOMEM;
- handle->hal_cap_g_ctl_csr_addr_v = misc_bar->virt_addr +
- ICP_DH895XCC_CAP_OFFSET;
- handle->hal_cap_ae_xfer_csr_addr_v = misc_bar->virt_addr +
- ICP_DH895XCC_AE_OFFSET;
- handle->hal_ep_csr_addr_v = misc_bar->virt_addr +
- ICP_DH895XCC_EP_OFFSET;
- handle->hal_cap_ae_local_csr_addr_v =
- handle->hal_cap_ae_xfer_csr_addr_v + LOCAL_TO_XFER_REG_OFFSET;
handle->hal_sram_addr_v = sram_bar->virt_addr;
+ handle->hal_cap_g_ctl_csr_addr_v =
+ (void __iomem *)((uintptr_t)misc_bar->virt_addr +
+ ICP_QAT_CAP_OFFSET);
+ handle->hal_cap_ae_xfer_csr_addr_v =
+ (void __iomem *)((uintptr_t)misc_bar->virt_addr +
+ ICP_QAT_AE_OFFSET);
+ handle->hal_ep_csr_addr_v =
+ (void __iomem *)((uintptr_t)misc_bar->virt_addr +
+ ICP_QAT_EP_OFFSET);
+ handle->hal_cap_ae_local_csr_addr_v =
+ (void __iomem *)((uintptr_t)handle->hal_cap_ae_xfer_csr_addr_v +
+ LOCAL_TO_XFER_REG_OFFSET);
+ handle->pci_dev = pci_info->pci_dev;
+ handle->fw_auth = (handle->pci_dev->device ==
+ ADF_DH895XCC_PCI_DEVICE_ID) ? false : true;
handle->hal_handle = kzalloc(sizeof(*handle->hal_handle), GFP_KERNEL);
if (!handle->hal_handle)
goto out_hal_handle;
@@ -723,14 +743,16 @@ int qat_hal_init(struct adf_accel_dev *accel_dev)
dev_err(&GET_DEV(accel_dev), "qat_hal_clr_reset error\n");
goto out_err;
}
- if (qat_hal_clear_gpr(handle))
- goto out_err;
+ qat_hal_clear_xfer(handle);
+ if (!handle->fw_auth) {
+ if (qat_hal_clear_gpr(handle))
+ goto out_err;
+ }
+
/* Set SIGNATURE_ENABLE[0] to 0x1 in order to enable ALU_OUT csr */
for (ae = 0; ae < handle->hal_handle->ae_max_num; ae++) {
unsigned int csr_val = 0;
- if (!(hw_data->ae_mask & (1 << ae)))
- continue;
qat_hal_rd_ae_csr(handle, ae, SIGNATURE_ENABLE, &csr_val);
csr_val |= 0x1;
qat_hal_wr_ae_csr(handle, ae, SIGNATURE_ENABLE, csr_val);
@@ -756,15 +778,31 @@ void qat_hal_deinit(struct icp_qat_fw_loader_handle *handle)
void qat_hal_start(struct icp_qat_fw_loader_handle *handle, unsigned char ae,
unsigned int ctx_mask)
{
- qat_hal_put_wakeup_event(handle, ae, (~ctx_mask) &
+ int retry = 0;
+ unsigned int fcu_sts = 0;
+
+ if (handle->fw_auth) {
+ SET_CAP_CSR(handle, FCU_CONTROL, FCU_CTRL_CMD_START);
+ do {
+ msleep(FW_AUTH_WAIT_PERIOD);
+ fcu_sts = GET_CAP_CSR(handle, FCU_STATUS);
+ if (((fcu_sts >> FCU_STS_DONE_POS) & 0x1))
+ return;
+ } while (retry++ < FW_AUTH_MAX_RETRY);
+ pr_err("QAT: start error (AE 0x%x FCU_STS = 0x%x)\n", ae,
+ fcu_sts);
+ } else {
+ qat_hal_put_wakeup_event(handle, ae, (~ctx_mask) &
ICP_QAT_UCLO_AE_ALL_CTX, 0x10000);
- qat_hal_enable_ctx(handle, ae, ctx_mask);
+ qat_hal_enable_ctx(handle, ae, ctx_mask);
+ }
}
void qat_hal_stop(struct icp_qat_fw_loader_handle *handle, unsigned char ae,
unsigned int ctx_mask)
{
- qat_hal_disable_ctx(handle, ae, ctx_mask);
+ if (!handle->fw_auth)
+ qat_hal_disable_ctx(handle, ae, ctx_mask);
}
void qat_hal_set_pc(struct icp_qat_fw_loader_handle *handle,
diff --git a/drivers/crypto/qat/qat_common/qat_uclo.c b/drivers/crypto/qat/qat_common/qat_uclo.c
index c48f181e8941..25d15f19c2b3 100644
--- a/drivers/crypto/qat/qat_common/qat_uclo.c
+++ b/drivers/crypto/qat/qat_common/qat_uclo.c
@@ -47,7 +47,7 @@
#include <linux/slab.h>
#include <linux/ctype.h>
#include <linux/kernel.h>
-
+#include <linux/delay.h>
#include "adf_accel_devices.h"
#include "adf_common_drv.h"
#include "icp_qat_uclo.h"
@@ -119,10 +119,10 @@ static char *qat_uclo_get_string(struct icp_qat_uof_strtable *str_table,
{
if ((!str_table->table_len) || (str_offset > str_table->table_len))
return NULL;
- return (char *)(((unsigned long)(str_table->strings)) + str_offset);
+ return (char *)(((uintptr_t)(str_table->strings)) + str_offset);
}
-static int qat_uclo_check_format(struct icp_qat_uof_filehdr *hdr)
+static int qat_uclo_check_uof_format(struct icp_qat_uof_filehdr *hdr)
{
int maj = hdr->maj_ver & 0xff;
int min = hdr->min_ver & 0xff;
@@ -139,6 +139,31 @@ static int qat_uclo_check_format(struct icp_qat_uof_filehdr *hdr)
return 0;
}
+static int qat_uclo_check_suof_format(struct icp_qat_suof_filehdr *suof_hdr)
+{
+ int maj = suof_hdr->maj_ver & 0xff;
+ int min = suof_hdr->min_ver & 0xff;
+
+ if (suof_hdr->file_id != ICP_QAT_SUOF_FID) {
+ pr_err("QAT: invalid header 0x%x\n", suof_hdr->file_id);
+ return -EINVAL;
+ }
+ if (suof_hdr->fw_type != 0) {
+ pr_err("QAT: unsupported firmware type\n");
+ return -EINVAL;
+ }
+ if (suof_hdr->num_chunks <= 0x1) {
+ pr_err("QAT: SUOF chunk amount is incorrect\n");
+ return -EINVAL;
+ }
+ if (maj != ICP_QAT_SUOF_MAJVER || min != ICP_QAT_SUOF_MINVER) {
+ pr_err("QAT: bad SUOF version, major 0x%x, minor 0x%x\n",
+ maj, min);
+ return -EINVAL;
+ }
+ return 0;
+}
+
static void qat_uclo_wr_sram_by_words(struct icp_qat_fw_loader_handle *handle,
unsigned int addr, unsigned int *val,
unsigned int num_in_bytes)
@@ -275,7 +300,7 @@ static int qat_uclo_create_batch_init_list(struct icp_qat_fw_loader_handle
unsigned int i, flag = 0;
mem_val_attr =
- (struct icp_qat_uof_memvar_attr *)((unsigned long)init_mem +
+ (struct icp_qat_uof_memvar_attr *)((uintptr_t)init_mem +
sizeof(struct icp_qat_uof_initmem));
init_header = *init_tab_base;
@@ -425,8 +450,8 @@ static int qat_uclo_init_memory(struct icp_qat_fw_loader_handle *handle)
if (qat_uclo_init_ae_memory(handle, initmem))
return -EINVAL;
}
- initmem = (struct icp_qat_uof_initmem *)((unsigned long)(
- (unsigned long)initmem +
+ initmem = (struct icp_qat_uof_initmem *)((uintptr_t)(
+ (uintptr_t)initmem +
sizeof(struct icp_qat_uof_initmem)) +
(sizeof(struct icp_qat_uof_memvar_attr) *
initmem->val_attr_num));
@@ -454,7 +479,7 @@ static void *qat_uclo_find_chunk(struct icp_qat_uof_objhdr *obj_hdr,
int i;
struct icp_qat_uof_chunkhdr *chunk_hdr =
(struct icp_qat_uof_chunkhdr *)
- ((unsigned long)obj_hdr + sizeof(struct icp_qat_uof_objhdr));
+ ((uintptr_t)obj_hdr + sizeof(struct icp_qat_uof_objhdr));
for (i = 0; i < obj_hdr->num_chunks; i++) {
if ((cur < (void *)&chunk_hdr[i]) &&
@@ -596,7 +621,7 @@ static void qat_uclo_map_image_page(struct icp_qat_uof_encap_obj
page->uwblock = (struct icp_qat_uclo_encap_uwblock *)uwblock;
for (i = 0; i < uword_block_tab->entry_num; i++)
page->uwblock[i].micro_words =
- (unsigned long)encap_uof_obj->beg_uof + uwblock[i].uword_offset;
+ (uintptr_t)encap_uof_obj->beg_uof + uwblock[i].uword_offset;
}
static int qat_uclo_map_uimage(struct icp_qat_uclo_objhandle *obj_handle,
@@ -697,7 +722,7 @@ qat_uclo_map_str_table(struct icp_qat_uclo_objhdr *obj_hdr,
memcpy(&str_table->table_len, obj_hdr->file_buff +
chunk_hdr->offset, sizeof(str_table->table_len));
hdr_size = (char *)&str_table->strings - (char *)str_table;
- str_table->strings = (unsigned long)obj_hdr->file_buff +
+ str_table->strings = (uintptr_t)obj_hdr->file_buff +
chunk_hdr->offset + hdr_size;
return str_table;
}
@@ -721,13 +746,31 @@ qat_uclo_map_initmem_table(struct icp_qat_uof_encap_obj *encap_uof_obj,
}
}
+static unsigned int
+qat_uclo_get_dev_type(struct icp_qat_fw_loader_handle *handle)
+{
+ switch (handle->pci_dev->device) {
+ case ADF_DH895XCC_PCI_DEVICE_ID:
+ return ICP_QAT_AC_895XCC_DEV_TYPE;
+ case ADF_C62X_PCI_DEVICE_ID:
+ return ICP_QAT_AC_C62X_DEV_TYPE;
+ case ADF_C3XXX_PCI_DEVICE_ID:
+ return ICP_QAT_AC_C3XXX_DEV_TYPE;
+ default:
+ pr_err("QAT: unsupported device 0x%x\n",
+ handle->pci_dev->device);
+ return 0;
+ }
+}
+
static int qat_uclo_check_uof_compat(struct icp_qat_uclo_objhandle *obj_handle)
{
unsigned int maj_ver, prod_type = obj_handle->prod_type;
- if (!(prod_type & obj_handle->encap_uof_obj.obj_hdr->cpu_type)) {
- pr_err("QAT: UOF type 0x%x not match with cur platform 0x%x\n",
- obj_handle->encap_uof_obj.obj_hdr->cpu_type, prod_type);
+ if (!(prod_type & obj_handle->encap_uof_obj.obj_hdr->ac_dev_type)) {
+ pr_err("QAT: UOF type 0x%x doesn't match with platform 0x%x\n",
+ obj_handle->encap_uof_obj.obj_hdr->ac_dev_type,
+ prod_type);
return -EINVAL;
}
maj_ver = obj_handle->prod_rev & 0xff;
@@ -932,7 +975,7 @@ static int qat_uclo_parse_uof_obj(struct icp_qat_fw_loader_handle *handle)
obj_handle->encap_uof_obj.obj_hdr = (struct icp_qat_uof_objhdr *)
obj_handle->obj_hdr->file_buff;
obj_handle->uword_in_bytes = 6;
- obj_handle->prod_type = ICP_QAT_AC_C_CPU_TYPE;
+ obj_handle->prod_type = qat_uclo_get_dev_type(handle);
obj_handle->prod_rev = PID_MAJOR_REV |
(PID_MINOR_REV & handle->hal_handle->revision_id);
if (qat_uclo_check_uof_compat(obj_handle)) {
@@ -969,23 +1012,435 @@ out_err:
return -EFAULT;
}
-void qat_uclo_wr_mimage(struct icp_qat_fw_loader_handle *handle,
- void *addr_ptr, int mem_size)
+static int qat_uclo_map_suof_file_hdr(struct icp_qat_fw_loader_handle *handle,
+ struct icp_qat_suof_filehdr *suof_ptr,
+ int suof_size)
{
- qat_uclo_wr_sram_by_words(handle, 0, addr_ptr, ALIGN(mem_size, 4));
+ unsigned int check_sum = 0;
+ unsigned int min_ver_offset = 0;
+ struct icp_qat_suof_handle *suof_handle = handle->sobj_handle;
+
+ suof_handle->file_id = ICP_QAT_SUOF_FID;
+ suof_handle->suof_buf = (char *)suof_ptr;
+ suof_handle->suof_size = suof_size;
+ min_ver_offset = suof_size - offsetof(struct icp_qat_suof_filehdr,
+ min_ver);
+ check_sum = qat_uclo_calc_str_checksum((char *)&suof_ptr->min_ver,
+ min_ver_offset);
+ if (check_sum != suof_ptr->check_sum) {
+ pr_err("QAT: incorrect SUOF checksum\n");
+ return -EINVAL;
+ }
+ suof_handle->check_sum = suof_ptr->check_sum;
+ suof_handle->min_ver = suof_ptr->min_ver;
+ suof_handle->maj_ver = suof_ptr->maj_ver;
+ suof_handle->fw_type = suof_ptr->fw_type;
+ return 0;
}
-int qat_uclo_map_uof_obj(struct icp_qat_fw_loader_handle *handle,
- void *addr_ptr, int mem_size)
+static void qat_uclo_map_simg(struct icp_qat_suof_handle *suof_handle,
+ struct icp_qat_suof_img_hdr *suof_img_hdr,
+ struct icp_qat_suof_chunk_hdr *suof_chunk_hdr)
{
- struct icp_qat_uof_filehdr *filehdr;
- struct icp_qat_uclo_objhandle *objhdl;
+ struct icp_qat_simg_ae_mode *ae_mode;
+ struct icp_qat_suof_objhdr *suof_objhdr;
+
+ suof_img_hdr->simg_buf = (suof_handle->suof_buf +
+ suof_chunk_hdr->offset +
+ sizeof(*suof_objhdr));
+ suof_img_hdr->simg_len = ((struct icp_qat_suof_objhdr *)(uintptr_t)
+ (suof_handle->suof_buf +
+ suof_chunk_hdr->offset))->img_length;
+
+ suof_img_hdr->css_header = suof_img_hdr->simg_buf;
+ suof_img_hdr->css_key = (suof_img_hdr->css_header +
+ sizeof(struct icp_qat_css_hdr));
+ suof_img_hdr->css_signature = suof_img_hdr->css_key +
+ ICP_QAT_CSS_FWSK_MODULUS_LEN +
+ ICP_QAT_CSS_FWSK_EXPONENT_LEN;
+ suof_img_hdr->css_simg = suof_img_hdr->css_signature +
+ ICP_QAT_CSS_SIGNATURE_LEN;
+
+ ae_mode = (struct icp_qat_simg_ae_mode *)(suof_img_hdr->css_simg);
+ suof_img_hdr->ae_mask = ae_mode->ae_mask;
+ suof_img_hdr->simg_name = (unsigned long)&ae_mode->simg_name;
+ suof_img_hdr->appmeta_data = (unsigned long)&ae_mode->appmeta_data;
+ suof_img_hdr->fw_type = ae_mode->fw_type;
+}
- BUILD_BUG_ON(ICP_QAT_UCLO_MAX_AE >=
- (sizeof(handle->hal_handle->ae_mask) * 8));
+static void
+qat_uclo_map_suof_symobjs(struct icp_qat_suof_handle *suof_handle,
+ struct icp_qat_suof_chunk_hdr *suof_chunk_hdr)
+{
+ char **sym_str = (char **)&suof_handle->sym_str;
+ unsigned int *sym_size = &suof_handle->sym_size;
+ struct icp_qat_suof_strtable *str_table_obj;
+
+ *sym_size = *(unsigned int *)(uintptr_t)
+ (suof_chunk_hdr->offset + suof_handle->suof_buf);
+ *sym_str = (char *)(uintptr_t)
+ (suof_handle->suof_buf + suof_chunk_hdr->offset +
+ sizeof(str_table_obj->tab_length));
+}
- if (!handle || !addr_ptr || mem_size < 24)
+static int qat_uclo_check_simg_compat(struct icp_qat_fw_loader_handle *handle,
+ struct icp_qat_suof_img_hdr *img_hdr)
+{
+ struct icp_qat_simg_ae_mode *img_ae_mode = NULL;
+ unsigned int prod_rev, maj_ver, prod_type;
+
+ prod_type = qat_uclo_get_dev_type(handle);
+ img_ae_mode = (struct icp_qat_simg_ae_mode *)img_hdr->css_simg;
+ prod_rev = PID_MAJOR_REV |
+ (PID_MINOR_REV & handle->hal_handle->revision_id);
+ if (img_ae_mode->dev_type != prod_type) {
+ pr_err("QAT: incompatible product type %x\n",
+ img_ae_mode->dev_type);
return -EINVAL;
+ }
+ maj_ver = prod_rev & 0xff;
+ if ((maj_ver > img_ae_mode->devmax_ver) ||
+ (maj_ver < img_ae_mode->devmin_ver)) {
+ pr_err("QAT: incompatible device majver 0x%x\n", maj_ver);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void qat_uclo_del_suof(struct icp_qat_fw_loader_handle *handle)
+{
+ struct icp_qat_suof_handle *sobj_handle = handle->sobj_handle;
+
+ kfree(sobj_handle->img_table.simg_hdr);
+ sobj_handle->img_table.simg_hdr = NULL;
+ kfree(handle->sobj_handle);
+ handle->sobj_handle = NULL;
+}
+
+static void qat_uclo_tail_img(struct icp_qat_suof_img_hdr *suof_img_hdr,
+ unsigned int img_id, unsigned int num_simgs)
+{
+ struct icp_qat_suof_img_hdr img_header;
+
+ if (img_id != num_simgs - 1) {
+ memcpy(&img_header, &suof_img_hdr[num_simgs - 1],
+ sizeof(*suof_img_hdr));
+ memcpy(&suof_img_hdr[num_simgs - 1], &suof_img_hdr[img_id],
+ sizeof(*suof_img_hdr));
+ memcpy(&suof_img_hdr[img_id], &img_header,
+ sizeof(*suof_img_hdr));
+ }
+}
+
+static int qat_uclo_map_suof(struct icp_qat_fw_loader_handle *handle,
+ struct icp_qat_suof_filehdr *suof_ptr,
+ int suof_size)
+{
+ struct icp_qat_suof_handle *suof_handle = handle->sobj_handle;
+ struct icp_qat_suof_chunk_hdr *suof_chunk_hdr = NULL;
+ struct icp_qat_suof_img_hdr *suof_img_hdr = NULL;
+ int ret = 0, ae0_img = ICP_QAT_UCLO_MAX_AE;
+ unsigned int i = 0;
+ struct icp_qat_suof_img_hdr img_header;
+
+ if (!suof_ptr || (suof_size == 0)) {
+ pr_err("QAT: input parameter SUOF pointer/size is NULL\n");
+ return -EINVAL;
+ }
+ if (qat_uclo_check_suof_format(suof_ptr))
+ return -EINVAL;
+ ret = qat_uclo_map_suof_file_hdr(handle, suof_ptr, suof_size);
+ if (ret)
+ return ret;
+ suof_chunk_hdr = (struct icp_qat_suof_chunk_hdr *)
+ ((uintptr_t)suof_ptr + sizeof(*suof_ptr));
+
+ qat_uclo_map_suof_symobjs(suof_handle, suof_chunk_hdr);
+ suof_handle->img_table.num_simgs = suof_ptr->num_chunks - 1;
+
+ if (suof_handle->img_table.num_simgs != 0) {
+ suof_img_hdr = kzalloc(suof_handle->img_table.num_simgs *
+ sizeof(img_header), GFP_KERNEL);
+ if (!suof_img_hdr)
+ return -ENOMEM;
+ suof_handle->img_table.simg_hdr = suof_img_hdr;
+ }
+
+ for (i = 0; i < suof_handle->img_table.num_simgs; i++) {
+ qat_uclo_map_simg(handle->sobj_handle, &suof_img_hdr[i],
+ &suof_chunk_hdr[1 + i]);
+ ret = qat_uclo_check_simg_compat(handle,
+ &suof_img_hdr[i]);
+ if (ret)
+ return ret;
+ if ((suof_img_hdr[i].ae_mask & 0x1) != 0)
+ ae0_img = i;
+ }
+ qat_uclo_tail_img(suof_img_hdr, ae0_img,
+ suof_handle->img_table.num_simgs);
+ return 0;
+}
+
+#define ADD_ADDR(high, low) ((((uint64_t)high) << 32) + low)
+#define BITS_IN_DWORD 32
+
+static int qat_uclo_auth_fw(struct icp_qat_fw_loader_handle *handle,
+ struct icp_qat_fw_auth_desc *desc)
+{
+ unsigned int fcu_sts, retry = 0;
+ u64 bus_addr;
+
+ bus_addr = ADD_ADDR(desc->css_hdr_high, desc->css_hdr_low)
+ - sizeof(struct icp_qat_auth_chunk);
+ SET_CAP_CSR(handle, FCU_DRAM_ADDR_HI, (bus_addr >> BITS_IN_DWORD));
+ SET_CAP_CSR(handle, FCU_DRAM_ADDR_LO, bus_addr);
+ SET_CAP_CSR(handle, FCU_CONTROL, FCU_CTRL_CMD_AUTH);
+
+ do {
+ msleep(FW_AUTH_WAIT_PERIOD);
+ fcu_sts = GET_CAP_CSR(handle, FCU_STATUS);
+ if ((fcu_sts & FCU_AUTH_STS_MASK) == FCU_STS_VERI_FAIL)
+ goto auth_fail;
+ if (((fcu_sts >> FCU_STS_AUTHFWLD_POS) & 0x1))
+ if ((fcu_sts & FCU_AUTH_STS_MASK) == FCU_STS_VERI_DONE)
+ return 0;
+ } while (retry++ < FW_AUTH_MAX_RETRY);
+auth_fail:
+ pr_err("QAT: authentication error (FCU_STATUS = 0x%x),retry = %d\n",
+ fcu_sts & FCU_AUTH_STS_MASK, retry);
+ return -EINVAL;
+}
+
+static int qat_uclo_simg_alloc(struct icp_qat_fw_loader_handle *handle,
+ struct icp_firml_dram_desc *dram_desc,
+ unsigned int size)
+{
+ void *vptr;
+ dma_addr_t ptr;
+
+ vptr = dma_alloc_coherent(&handle->pci_dev->dev,
+ size, &ptr, GFP_KERNEL);
+ if (!vptr)
+ return -ENOMEM;
+ dram_desc->dram_base_addr_v = vptr;
+ dram_desc->dram_bus_addr = ptr;
+ dram_desc->dram_size = size;
+ return 0;
+}
+
+static void qat_uclo_simg_free(struct icp_qat_fw_loader_handle *handle,
+ struct icp_firml_dram_desc *dram_desc)
+{
+ dma_free_coherent(&handle->pci_dev->dev,
+ (size_t)(dram_desc->dram_size),
+ (dram_desc->dram_base_addr_v),
+ dram_desc->dram_bus_addr);
+ memset(dram_desc, 0, sizeof(*dram_desc));
+}
+
+static void qat_uclo_ummap_auth_fw(struct icp_qat_fw_loader_handle *handle,
+ struct icp_qat_fw_auth_desc **desc)
+{
+ struct icp_firml_dram_desc dram_desc;
+
+ dram_desc.dram_base_addr_v = *desc;
+ dram_desc.dram_bus_addr = ((struct icp_qat_auth_chunk *)
+ (*desc))->chunk_bus_addr;
+ dram_desc.dram_size = ((struct icp_qat_auth_chunk *)
+ (*desc))->chunk_size;
+ qat_uclo_simg_free(handle, &dram_desc);
+}
+
+static int qat_uclo_map_auth_fw(struct icp_qat_fw_loader_handle *handle,
+ char *image, unsigned int size,
+ struct icp_qat_fw_auth_desc **desc)
+{
+ struct icp_qat_css_hdr *css_hdr = (struct icp_qat_css_hdr *)image;
+ struct icp_qat_fw_auth_desc *auth_desc;
+ struct icp_qat_auth_chunk *auth_chunk;
+ u64 virt_addr, bus_addr, virt_base;
+ unsigned int length, simg_offset = sizeof(*auth_chunk);
+ struct icp_firml_dram_desc img_desc;
+
+ if (size > (ICP_QAT_AE_IMG_OFFSET + ICP_QAT_CSS_MAX_IMAGE_LEN)) {
+ pr_err("QAT: error, input image size overflow %d\n", size);
+ return -EINVAL;
+ }
+ length = (css_hdr->fw_type == CSS_AE_FIRMWARE) ?
+ ICP_QAT_CSS_AE_SIMG_LEN + simg_offset :
+ size + ICP_QAT_CSS_FWSK_PAD_LEN + simg_offset;
+ if (qat_uclo_simg_alloc(handle, &img_desc, length)) {
+ pr_err("QAT: error, allocate continuous dram fail\n");
+ return -ENOMEM;
+ }
+
+ auth_chunk = img_desc.dram_base_addr_v;
+ auth_chunk->chunk_size = img_desc.dram_size;
+ auth_chunk->chunk_bus_addr = img_desc.dram_bus_addr;
+ virt_base = (uintptr_t)img_desc.dram_base_addr_v + simg_offset;
+ bus_addr = img_desc.dram_bus_addr + simg_offset;
+ auth_desc = img_desc.dram_base_addr_v;
+ auth_desc->css_hdr_high = (unsigned int)(bus_addr >> BITS_IN_DWORD);
+ auth_desc->css_hdr_low = (unsigned int)bus_addr;
+ virt_addr = virt_base;
+
+ memcpy((void *)(uintptr_t)virt_addr, image, sizeof(*css_hdr));
+ /* pub key */
+ bus_addr = ADD_ADDR(auth_desc->css_hdr_high, auth_desc->css_hdr_low) +
+ sizeof(*css_hdr);
+ virt_addr = virt_addr + sizeof(*css_hdr);
+
+ auth_desc->fwsk_pub_high = (unsigned int)(bus_addr >> BITS_IN_DWORD);
+ auth_desc->fwsk_pub_low = (unsigned int)bus_addr;
+
+ memcpy((void *)(uintptr_t)virt_addr,
+ (void *)(image + sizeof(*css_hdr)),
+ ICP_QAT_CSS_FWSK_MODULUS_LEN);
+ /* padding */
+ memset((void *)(uintptr_t)(virt_addr + ICP_QAT_CSS_FWSK_MODULUS_LEN),
+ 0, ICP_QAT_CSS_FWSK_PAD_LEN);
+
+ /* exponent */
+ memcpy((void *)(uintptr_t)(virt_addr + ICP_QAT_CSS_FWSK_MODULUS_LEN +
+ ICP_QAT_CSS_FWSK_PAD_LEN),
+ (void *)(image + sizeof(*css_hdr) +
+ ICP_QAT_CSS_FWSK_MODULUS_LEN),
+ sizeof(unsigned int));
+
+ /* signature */
+ bus_addr = ADD_ADDR(auth_desc->fwsk_pub_high,
+ auth_desc->fwsk_pub_low) +
+ ICP_QAT_CSS_FWSK_PUB_LEN;
+ virt_addr = virt_addr + ICP_QAT_CSS_FWSK_PUB_LEN;
+ auth_desc->signature_high = (unsigned int)(bus_addr >> BITS_IN_DWORD);
+ auth_desc->signature_low = (unsigned int)bus_addr;
+
+ memcpy((void *)(uintptr_t)virt_addr,
+ (void *)(image + sizeof(*css_hdr) +
+ ICP_QAT_CSS_FWSK_MODULUS_LEN +
+ ICP_QAT_CSS_FWSK_EXPONENT_LEN),
+ ICP_QAT_CSS_SIGNATURE_LEN);
+
+ bus_addr = ADD_ADDR(auth_desc->signature_high,
+ auth_desc->signature_low) +
+ ICP_QAT_CSS_SIGNATURE_LEN;
+ virt_addr += ICP_QAT_CSS_SIGNATURE_LEN;
+
+ auth_desc->img_high = (unsigned int)(bus_addr >> BITS_IN_DWORD);
+ auth_desc->img_low = (unsigned int)bus_addr;
+ auth_desc->img_len = size - ICP_QAT_AE_IMG_OFFSET;
+ memcpy((void *)(uintptr_t)virt_addr,
+ (void *)(image + ICP_QAT_AE_IMG_OFFSET),
+ auth_desc->img_len);
+ virt_addr = virt_base;
+ /* AE firmware */
+ if (((struct icp_qat_css_hdr *)(uintptr_t)virt_addr)->fw_type ==
+ CSS_AE_FIRMWARE) {
+ auth_desc->img_ae_mode_data_high = auth_desc->img_high;
+ auth_desc->img_ae_mode_data_low = auth_desc->img_low;
+ bus_addr = ADD_ADDR(auth_desc->img_ae_mode_data_high,
+ auth_desc->img_ae_mode_data_low) +
+ sizeof(struct icp_qat_simg_ae_mode);
+
+ auth_desc->img_ae_init_data_high = (unsigned int)
+ (bus_addr >> BITS_IN_DWORD);
+ auth_desc->img_ae_init_data_low = (unsigned int)bus_addr;
+ bus_addr += ICP_QAT_SIMG_AE_INIT_SEQ_LEN;
+ auth_desc->img_ae_insts_high = (unsigned int)
+ (bus_addr >> BITS_IN_DWORD);
+ auth_desc->img_ae_insts_low = (unsigned int)bus_addr;
+ } else {
+ auth_desc->img_ae_insts_high = auth_desc->img_high;
+ auth_desc->img_ae_insts_low = auth_desc->img_low;
+ }
+ *desc = auth_desc;
+ return 0;
+}
+
+static int qat_uclo_load_fw(struct icp_qat_fw_loader_handle *handle,
+ struct icp_qat_fw_auth_desc *desc)
+{
+ unsigned int i;
+ unsigned int fcu_sts;
+ struct icp_qat_simg_ae_mode *virt_addr;
+ unsigned int fcu_loaded_ae_pos = FCU_LOADED_AE_POS;
+
+ virt_addr = (void *)((uintptr_t)desc +
+ sizeof(struct icp_qat_auth_chunk) +
+ sizeof(struct icp_qat_css_hdr) +
+ ICP_QAT_CSS_FWSK_PUB_LEN +
+ ICP_QAT_CSS_SIGNATURE_LEN);
+ for (i = 0; i < handle->hal_handle->ae_max_num; i++) {
+ int retry = 0;
+
+ if (!((virt_addr->ae_mask >> i) & 0x1))
+ continue;
+ if (qat_hal_check_ae_active(handle, i)) {
+ pr_err("QAT: AE %d is active\n", i);
+ return -EINVAL;
+ }
+ SET_CAP_CSR(handle, FCU_CONTROL,
+ (FCU_CTRL_CMD_LOAD | (i << FCU_CTRL_AE_POS)));
+
+ do {
+ msleep(FW_AUTH_WAIT_PERIOD);
+ fcu_sts = GET_CAP_CSR(handle, FCU_STATUS);
+ if (((fcu_sts & FCU_AUTH_STS_MASK) ==
+ FCU_STS_LOAD_DONE) &&
+ ((fcu_sts >> fcu_loaded_ae_pos) & (1 << i)))
+ break;
+ } while (retry++ < FW_AUTH_MAX_RETRY);
+ if (retry > FW_AUTH_MAX_RETRY) {
+ pr_err("QAT: firmware load failed timeout %x\n", retry);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int qat_uclo_map_suof_obj(struct icp_qat_fw_loader_handle *handle,
+ void *addr_ptr, int mem_size)
+{
+ struct icp_qat_suof_handle *suof_handle;
+
+ suof_handle = kzalloc(sizeof(*suof_handle), GFP_KERNEL);
+ if (!suof_handle)
+ return -ENOMEM;
+ handle->sobj_handle = suof_handle;
+ if (qat_uclo_map_suof(handle, addr_ptr, mem_size)) {
+ qat_uclo_del_suof(handle);
+ pr_err("QAT: map SUOF failed\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int qat_uclo_wr_mimage(struct icp_qat_fw_loader_handle *handle,
+ void *addr_ptr, int mem_size)
+{
+ struct icp_qat_fw_auth_desc *desc = NULL;
+ int status = 0;
+
+ if (handle->fw_auth) {
+ if (!qat_uclo_map_auth_fw(handle, addr_ptr, mem_size, &desc))
+ status = qat_uclo_auth_fw(handle, desc);
+ qat_uclo_ummap_auth_fw(handle, &desc);
+ } else {
+ if (handle->pci_dev->device == ADF_C3XXX_PCI_DEVICE_ID) {
+ pr_err("QAT: C3XXX doesn't support unsigned MMP\n");
+ return -EINVAL;
+ }
+ qat_uclo_wr_sram_by_words(handle, 0, addr_ptr, mem_size);
+ }
+ return status;
+}
+
+static int qat_uclo_map_uof_obj(struct icp_qat_fw_loader_handle *handle,
+ void *addr_ptr, int mem_size)
+{
+ struct icp_qat_uof_filehdr *filehdr;
+ struct icp_qat_uclo_objhandle *objhdl;
+
objhdl = kzalloc(sizeof(*objhdl), GFP_KERNEL);
if (!objhdl)
return -ENOMEM;
@@ -993,7 +1448,7 @@ int qat_uclo_map_uof_obj(struct icp_qat_fw_loader_handle *handle,
if (!objhdl->obj_buf)
goto out_objbuf_err;
filehdr = (struct icp_qat_uof_filehdr *)objhdl->obj_buf;
- if (qat_uclo_check_format(filehdr))
+ if (qat_uclo_check_uof_format(filehdr))
goto out_objhdr_err;
objhdl->obj_hdr = qat_uclo_map_chunk((char *)objhdl->obj_buf, filehdr,
ICP_QAT_UOF_OBJS);
@@ -1016,11 +1471,27 @@ out_objbuf_err:
return -ENOMEM;
}
+int qat_uclo_map_obj(struct icp_qat_fw_loader_handle *handle,
+ void *addr_ptr, int mem_size)
+{
+ BUILD_BUG_ON(ICP_QAT_UCLO_MAX_AE >=
+ (sizeof(handle->hal_handle->ae_mask) * 8));
+
+ if (!handle || !addr_ptr || mem_size < 24)
+ return -EINVAL;
+
+ return (handle->fw_auth) ?
+ qat_uclo_map_suof_obj(handle, addr_ptr, mem_size) :
+ qat_uclo_map_uof_obj(handle, addr_ptr, mem_size);
+}
+
void qat_uclo_del_uof_obj(struct icp_qat_fw_loader_handle *handle)
{
struct icp_qat_uclo_objhandle *obj_handle = handle->obj_handle;
unsigned int a;
+ if (handle->sobj_handle)
+ qat_uclo_del_suof(handle);
if (!obj_handle)
return;
@@ -1055,7 +1526,7 @@ static void qat_uclo_fill_uwords(struct icp_qat_uclo_objhandle *obj_handle,
encap_page->uwblock[i].words_num - 1) {
raddr -= encap_page->uwblock[i].start_addr;
raddr *= obj_handle->uword_in_bytes;
- memcpy(&uwrd, (void *)(((unsigned long)
+ memcpy(&uwrd, (void *)(((uintptr_t)
encap_page->uwblock[i].micro_words) + raddr),
obj_handle->uword_in_bytes);
uwrd = uwrd & 0xbffffffffffull;
@@ -1147,7 +1618,33 @@ static void qat_uclo_wr_uimage_page(struct icp_qat_fw_loader_handle *handle,
}
}
-int qat_uclo_wr_all_uimage(struct icp_qat_fw_loader_handle *handle)
+static int qat_uclo_wr_suof_img(struct icp_qat_fw_loader_handle *handle)
+{
+ unsigned int i;
+ struct icp_qat_fw_auth_desc *desc = NULL;
+ struct icp_qat_suof_handle *sobj_handle = handle->sobj_handle;
+ struct icp_qat_suof_img_hdr *simg_hdr = sobj_handle->img_table.simg_hdr;
+
+ for (i = 0; i < sobj_handle->img_table.num_simgs; i++) {
+ if (qat_uclo_map_auth_fw(handle,
+ (char *)simg_hdr[i].simg_buf,
+ (unsigned int)
+ (simg_hdr[i].simg_len),
+ &desc))
+ goto wr_err;
+ if (qat_uclo_auth_fw(handle, desc))
+ goto wr_err;
+ if (qat_uclo_load_fw(handle, desc))
+ goto wr_err;
+ qat_uclo_ummap_auth_fw(handle, &desc);
+ }
+ return 0;
+wr_err:
+ qat_uclo_ummap_auth_fw(handle, &desc);
+ return -EINVAL;
+}
+
+static int qat_uclo_wr_uof_img(struct icp_qat_fw_loader_handle *handle)
{
struct icp_qat_uclo_objhandle *obj_handle = handle->obj_handle;
unsigned int i;
@@ -1164,3 +1661,9 @@ int qat_uclo_wr_all_uimage(struct icp_qat_fw_loader_handle *handle)
}
return 0;
}
+
+int qat_uclo_wr_all_uimage(struct icp_qat_fw_loader_handle *handle)
+{
+ return (handle->fw_auth) ? qat_uclo_wr_suof_img(handle) :
+ qat_uclo_wr_uof_img(handle);
+}
diff --git a/drivers/crypto/qat/qat_dh895xcc/Makefile b/drivers/crypto/qat/qat_dh895xcc/Makefile
index 8c79c543740f..180a00ed7f89 100644
--- a/drivers/crypto/qat/qat_dh895xcc/Makefile
+++ b/drivers/crypto/qat/qat_dh895xcc/Makefile
@@ -1,5 +1,3 @@
ccflags-y := -I$(src)/../qat_common
obj-$(CONFIG_CRYPTO_DEV_QAT_DH895xCC) += qat_dh895xcc.o
-qat_dh895xcc-objs := adf_drv.o \
- adf_isr.o \
- adf_dh895xcc_hw_data.o
+qat_dh895xcc-objs := adf_drv.o adf_dh895xcc_hw_data.o
diff --git a/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.c b/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.c
index ff54257eced4..6e1d5e185526 100644
--- a/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.c
+++ b/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.c
@@ -48,7 +48,6 @@
#include <adf_pf2vf_msg.h>
#include <adf_common_drv.h>
#include "adf_dh895xcc_hw_data.h"
-#include "adf_drv.h"
/* Worker thread to service arbiter mappings based on dev SKUs */
static const uint32_t thrd_to_arb_map_sku4[] = {
@@ -143,8 +142,8 @@ static enum dev_sku_info get_sku(struct adf_hw_device_data *self)
return DEV_SKU_UNKNOWN;
}
-void adf_get_arbiter_mapping(struct adf_accel_dev *accel_dev,
- uint32_t const **arb_map_config)
+static void adf_get_arbiter_mapping(struct adf_accel_dev *accel_dev,
+ u32 const **arb_map_config)
{
switch (accel_dev->accel_pci_dev.sku) {
case DEV_SKU_1:
diff --git a/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.h b/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.h
index 88dffb297346..092f7353ed23 100644
--- a/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.h
+++ b/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.h
@@ -53,7 +53,6 @@
#define ADF_DH895XCC_ETR_BAR 2
#define ADF_DH895XCC_RX_RINGS_OFFSET 8
#define ADF_DH895XCC_TX_RINGS_MASK 0xFF
-#define ADF_DH895XCC_FUSECTL_OFFSET 0x40
#define ADF_DH895XCC_FUSECTL_SKU_MASK 0x300000
#define ADF_DH895XCC_FUSECTL_SKU_SHIFT 20
#define ADF_DH895XCC_FUSECTL_SKU_1 0x0
@@ -65,7 +64,6 @@
#define ADF_DH895XCC_ACCELERATORS_REG_OFFSET 13
#define ADF_DH895XCC_ACCELERATORS_MASK 0x3F
#define ADF_DH895XCC_ACCELENGINES_MASK 0xFFF
-#define ADF_DH895XCC_LEGFUSE_OFFSET 0x4C
#define ADF_DH895XCC_ETR_MAX_BANKS 32
#define ADF_DH895XCC_SMIAPF0_MASK_OFFSET (0x3A000 + 0x28)
#define ADF_DH895XCC_SMIAPF1_MASK_OFFSET (0x3A000 + 0x30)
@@ -80,11 +78,12 @@
#define ADF_DH895XCC_CERRSSMSH(i) (i * 0x4000 + 0x10)
#define ADF_DH895XCC_ERRSSMSH_EN BIT(3)
-#define ADF_DH895XCC_ERRSOU3 (0x3A000 + 0x00C)
-#define ADF_DH895XCC_ERRSOU5 (0x3A000 + 0x0D8)
#define ADF_DH895XCC_PF2VF_OFFSET(i) (0x3A000 + 0x280 + ((i) * 0x04))
#define ADF_DH895XCC_VINTMSK_OFFSET(i) (0x3A000 + 0x200 + ((i) * 0x04))
/* FW names */
#define ADF_DH895XCC_FW "qat_895xcc.bin"
-#define ADF_DH895XCC_MMP "qat_mmp.bin"
+#define ADF_DH895XCC_MMP "qat_895xcc_mmp.bin"
+
+void adf_init_hw_data_dh895xcc(struct adf_hw_device_data *hw_data);
+void adf_clean_hw_data_dh895xcc(struct adf_hw_device_data *hw_data);
#endif
diff --git a/drivers/crypto/qat/qat_dh895xcc/adf_drv.c b/drivers/crypto/qat/qat_dh895xcc/adf_drv.c
index f8dd14f232c8..a8c4b92a7cbd 100644
--- a/drivers/crypto/qat/qat_dh895xcc/adf_drv.c
+++ b/drivers/crypto/qat/qat_dh895xcc/adf_drv.c
@@ -60,11 +60,7 @@
#include <adf_accel_devices.h>
#include <adf_common_drv.h>
#include <adf_cfg.h>
-#include <adf_transport_access_macros.h>
#include "adf_dh895xcc_hw_data.h"
-#include "adf_drv.h"
-
-static const char adf_driver_name[] = ADF_DH895XCC_DEVICE_NAME;
#define ADF_SYSTEM_DEVICE(device_id) \
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, device_id)}
@@ -80,7 +76,7 @@ static void adf_remove(struct pci_dev *dev);
static struct pci_driver adf_driver = {
.id_table = adf_pci_tbl,
- .name = adf_driver_name,
+ .name = ADF_DH895XCC_DEVICE_NAME,
.probe = adf_probe,
.remove = adf_remove,
.sriov_configure = adf_sriov_configure,
@@ -120,87 +116,6 @@ static void adf_cleanup_accel(struct adf_accel_dev *accel_dev)
adf_devmgr_rm_dev(accel_dev, NULL);
}
-static int adf_dev_configure(struct adf_accel_dev *accel_dev)
-{
- int cpus = num_online_cpus();
- int banks = GET_MAX_BANKS(accel_dev);
- int instances = min(cpus, banks);
- char key[ADF_CFG_MAX_KEY_LEN_IN_BYTES];
- int i;
- unsigned long val;
-
- if (adf_cfg_section_add(accel_dev, ADF_KERNEL_SEC))
- goto err;
- if (adf_cfg_section_add(accel_dev, "Accelerator0"))
- goto err;
- for (i = 0; i < instances; i++) {
- val = i;
- snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_BANK_NUM, i);
- if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
- key, (void *)&val, ADF_DEC))
- goto err;
-
- snprintf(key, sizeof(key), ADF_CY "%d" ADF_ETRMGR_CORE_AFFINITY,
- i);
- if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
- key, (void *)&val, ADF_DEC))
- goto err;
-
- snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_SIZE, i);
- val = 128;
- if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
- key, (void *)&val, ADF_DEC))
- goto err;
-
- val = 512;
- snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_SIZE, i);
- if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
- key, (void *)&val, ADF_DEC))
- goto err;
-
- val = 0;
- snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_TX, i);
- if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
- key, (void *)&val, ADF_DEC))
- goto err;
-
- val = 2;
- snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_TX, i);
- if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
- key, (void *)&val, ADF_DEC))
- goto err;
-
- val = 8;
- snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_RX, i);
- if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
- key, (void *)&val, ADF_DEC))
- goto err;
-
- val = 10;
- snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_RX, i);
- if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
- key, (void *)&val, ADF_DEC))
- goto err;
-
- val = ADF_COALESCING_DEF_TIME;
- snprintf(key, sizeof(key), ADF_ETRMGR_COALESCE_TIMER_FORMAT, i);
- if (adf_cfg_add_key_value_param(accel_dev, "Accelerator0",
- key, (void *)&val, ADF_DEC))
- goto err;
- }
-
- val = i;
- if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
- ADF_NUM_CY, (void *)&val, ADF_DEC))
- goto err;
-
- set_bit(ADF_STATUS_CONFIGURED, &accel_dev->status);
- return 0;
-err:
- dev_err(&GET_DEV(accel_dev), "Failed to start QAT accel dev\n");
- return -EINVAL;
-}
-
static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct adf_accel_dev *accel_dev;
@@ -253,15 +168,9 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
}
accel_dev->hw_device = hw_data;
- switch (ent->device) {
- case ADF_DH895XCC_PCI_DEVICE_ID:
- adf_init_hw_data_dh895xcc(accel_dev->hw_device);
- break;
- default:
- return -ENODEV;
- }
+ adf_init_hw_data_dh895xcc(accel_dev->hw_device);
pci_read_config_byte(pdev, PCI_REVISION_ID, &accel_pci_dev->revid);
- pci_read_config_dword(pdev, ADF_DH895XCC_FUSECTL_OFFSET,
+ pci_read_config_dword(pdev, ADF_DEVICE_FUSECTL_OFFSET,
&hw_data->fuses);
/* Get Accelerators and Accelerators Engines masks */
@@ -316,13 +225,13 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
}
- if (pci_request_regions(pdev, adf_driver_name)) {
+ if (pci_request_regions(pdev, ADF_DH895XCC_DEVICE_NAME)) {
ret = -EFAULT;
goto out_err_disable;
}
/* Read accelerator capabilities mask */
- pci_read_config_dword(pdev, ADF_DH895XCC_LEGFUSE_OFFSET,
+ pci_read_config_dword(pdev, ADF_DEVICE_LEGFUSE_OFFSET,
&hw_data->accel_capabilities_mask);
/* Find and map all the device's BARS */
@@ -357,7 +266,7 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto out_err_free_reg;
}
- ret = adf_dev_configure(accel_dev);
+ ret = qat_crypto_dev_config(accel_dev);
if (ret)
goto out_err_free_reg;
diff --git a/drivers/crypto/qat/qat_dh895xccvf/Makefile b/drivers/crypto/qat/qat_dh895xccvf/Makefile
index 85399fcbbad4..5c3ccf8267eb 100644
--- a/drivers/crypto/qat/qat_dh895xccvf/Makefile
+++ b/drivers/crypto/qat/qat_dh895xccvf/Makefile
@@ -1,5 +1,3 @@
ccflags-y := -I$(src)/../qat_common
obj-$(CONFIG_CRYPTO_DEV_QAT_DH895xCCVF) += qat_dh895xccvf.o
-qat_dh895xccvf-objs := adf_drv.o \
- adf_isr.o \
- adf_dh895xccvf_hw_data.o
+qat_dh895xccvf-objs := adf_drv.o adf_dh895xccvf_hw_data.o
diff --git a/drivers/crypto/qat/qat_dh895xccvf/adf_dh895xccvf_hw_data.c b/drivers/crypto/qat/qat_dh895xccvf/adf_dh895xccvf_hw_data.c
index a9a27eff41fb..dc04ab68d24d 100644
--- a/drivers/crypto/qat/qat_dh895xccvf/adf_dh895xccvf_hw_data.c
+++ b/drivers/crypto/qat/qat_dh895xccvf/adf_dh895xccvf_hw_data.c
@@ -48,7 +48,6 @@
#include <adf_pf2vf_msg.h>
#include <adf_common_drv.h>
#include "adf_dh895xccvf_hw_data.h"
-#include "adf_drv.h"
static struct adf_hw_device_class dh895xcciov_class = {
.name = ADF_DH895XCCVF_DEVICE_NAME,
@@ -136,7 +135,6 @@ static void adf_vf2pf_shutdown(struct adf_accel_dev *accel_dev)
void adf_init_hw_data_dh895xcciov(struct adf_hw_device_data *hw_data)
{
hw_data->dev_class = &dh895xcciov_class;
- hw_data->instance_id = dh895xcciov_class.instances++;
hw_data->num_banks = ADF_DH895XCCIOV_ETR_MAX_BANKS;
hw_data->num_accel = ADF_DH895XCCIOV_MAX_ACCELERATORS;
hw_data->num_logical_accel = 1;
@@ -164,9 +162,12 @@ void adf_init_hw_data_dh895xcciov(struct adf_hw_device_data *hw_data)
hw_data->enable_ints = adf_vf_void_noop;
hw_data->enable_vf2pf_comms = adf_enable_vf2pf_comms;
hw_data->min_iov_compat_ver = ADF_PFVF_COMPATIBILITY_VERSION;
+ hw_data->dev_class->instances++;
+ adf_devmgr_update_class_index(hw_data);
}
void adf_clean_hw_data_dh895xcciov(struct adf_hw_device_data *hw_data)
{
hw_data->dev_class->instances--;
+ adf_devmgr_update_class_index(hw_data);
}
diff --git a/drivers/crypto/qat/qat_dh895xccvf/adf_dh895xccvf_hw_data.h b/drivers/crypto/qat/qat_dh895xccvf/adf_dh895xccvf_hw_data.h
index 8f6babfef629..6ddc19bd4410 100644
--- a/drivers/crypto/qat/qat_dh895xccvf/adf_dh895xccvf_hw_data.h
+++ b/drivers/crypto/qat/qat_dh895xccvf/adf_dh895xccvf_hw_data.h
@@ -56,13 +56,9 @@
#define ADF_DH895XCCIOV_TX_RINGS_MASK 0xFF
#define ADF_DH895XCCIOV_ETR_BAR 0
#define ADF_DH895XCCIOV_ETR_MAX_BANKS 1
-
#define ADF_DH895XCCIOV_PF2VF_OFFSET 0x200
-#define ADF_DH895XCC_PF2VF_PF2VFINT BIT(0)
-
-#define ADF_DH895XCCIOV_VINTSOU_OFFSET 0x204
-#define ADF_DH895XCC_VINTSOU_BUN BIT(0)
-#define ADF_DH895XCC_VINTSOU_PF2VF BIT(1)
-
#define ADF_DH895XCCIOV_VINTMSK_OFFSET 0x208
+
+void adf_init_hw_data_dh895xcciov(struct adf_hw_device_data *hw_data);
+void adf_clean_hw_data_dh895xcciov(struct adf_hw_device_data *hw_data);
#endif
diff --git a/drivers/crypto/qat/qat_dh895xccvf/adf_drv.c b/drivers/crypto/qat/qat_dh895xccvf/adf_drv.c
index 789426f21882..f8cc4bf0a50c 100644
--- a/drivers/crypto/qat/qat_dh895xccvf/adf_drv.c
+++ b/drivers/crypto/qat/qat_dh895xccvf/adf_drv.c
@@ -60,11 +60,7 @@
#include <adf_accel_devices.h>
#include <adf_common_drv.h>
#include <adf_cfg.h>
-#include <adf_transport_access_macros.h>
#include "adf_dh895xccvf_hw_data.h"
-#include "adf_drv.h"
-
-static const char adf_driver_name[] = ADF_DH895XCCVF_DEVICE_NAME;
#define ADF_SYSTEM_DEVICE(device_id) \
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, device_id)}
@@ -80,7 +76,7 @@ static void adf_remove(struct pci_dev *dev);
static struct pci_driver adf_driver = {
.id_table = adf_pci_tbl,
- .name = adf_driver_name,
+ .name = ADF_DH895XCCVF_DEVICE_NAME,
.probe = adf_probe,
.remove = adf_remove,
};
@@ -121,83 +117,6 @@ static void adf_cleanup_accel(struct adf_accel_dev *accel_dev)
adf_devmgr_rm_dev(accel_dev, pf);
}
-static int adf_dev_configure(struct adf_accel_dev *accel_dev)
-{
- char key[ADF_CFG_MAX_KEY_LEN_IN_BYTES];
- unsigned long val, bank = 0;
-
- if (adf_cfg_section_add(accel_dev, ADF_KERNEL_SEC))
- goto err;
- if (adf_cfg_section_add(accel_dev, "Accelerator0"))
- goto err;
-
- snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_BANK_NUM, 0);
- if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, key,
- (void *)&bank, ADF_DEC))
- goto err;
-
- val = bank;
- snprintf(key, sizeof(key), ADF_CY "%d" ADF_ETRMGR_CORE_AFFINITY, 0);
- if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, key,
- (void *)&val, ADF_DEC))
- goto err;
-
- snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_SIZE, 0);
-
- val = 128;
- if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC, key,
- (void *)&val, ADF_DEC))
- goto err;
-
- val = 512;
- snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_SIZE, 0);
- if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
- key, (void *)&val, ADF_DEC))
- goto err;
-
- val = 0;
- snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_TX, 0);
- if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
- key, (void *)&val, ADF_DEC))
- goto err;
-
- val = 2;
- snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_TX, 0);
- if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
- key, (void *)&val, ADF_DEC))
- goto err;
-
- val = 8;
- snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_ASYM_RX, 0);
- if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
- key, (void *)&val, ADF_DEC))
- goto err;
-
- val = 10;
- snprintf(key, sizeof(key), ADF_CY "%d" ADF_RING_SYM_RX, 0);
- if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
- key, (void *)&val, ADF_DEC))
- goto err;
-
- val = ADF_COALESCING_DEF_TIME;
- snprintf(key, sizeof(key), ADF_ETRMGR_COALESCE_TIMER_FORMAT,
- (int)bank);
- if (adf_cfg_add_key_value_param(accel_dev, "Accelerator0",
- key, (void *)&val, ADF_DEC))
- goto err;
-
- val = 1;
- if (adf_cfg_add_key_value_param(accel_dev, ADF_KERNEL_SEC,
- ADF_NUM_CY, (void *)&val, ADF_DEC))
- goto err;
-
- set_bit(ADF_STATUS_CONFIGURED, &accel_dev->status);
- return 0;
-err:
- dev_err(&GET_DEV(accel_dev), "Failed to configure QAT accel dev\n");
- return -EINVAL;
-}
-
static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct adf_accel_dev *accel_dev;
@@ -243,14 +162,7 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto out_err;
}
accel_dev->hw_device = hw_data;
- switch (ent->device) {
- case ADF_DH895XCCIOV_PCI_DEVICE_ID:
- adf_init_hw_data_dh895xcciov(accel_dev->hw_device);
- break;
- default:
- ret = -ENODEV;
- goto out_err;
- }
+ adf_init_hw_data_dh895xcciov(accel_dev->hw_device);
/* Get Accelerators and Accelerators Engines masks */
hw_data->accel_mask = hw_data->get_accel_mask(hw_data->fuses);
@@ -295,7 +207,7 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
}
- if (pci_request_regions(pdev, adf_driver_name)) {
+ if (pci_request_regions(pdev, ADF_DH895XCCVF_DEVICE_NAME)) {
ret = -EFAULT;
goto out_err_disable;
}
@@ -322,7 +234,7 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* Completion for VF2PF request/response message exchange */
init_completion(&accel_dev->vf.iov_msg_completion);
- ret = adf_dev_configure(accel_dev);
+ ret = qat_crypto_dev_config(accel_dev);
if (ret)
goto out_err_free_reg;
diff --git a/drivers/crypto/qce/ablkcipher.c b/drivers/crypto/qce/ablkcipher.c
index 2c0d63d48747..dbcbbe242bd6 100644
--- a/drivers/crypto/qce/ablkcipher.c
+++ b/drivers/crypto/qce/ablkcipher.c
@@ -83,6 +83,14 @@ qce_ablkcipher_async_req_handle(struct crypto_async_request *async_req)
rctx->dst_nents = sg_nents_for_len(req->dst, req->nbytes);
else
rctx->dst_nents = rctx->src_nents;
+ if (rctx->src_nents < 0) {
+ dev_err(qce->dev, "Invalid numbers of src SG.\n");
+ return rctx->src_nents;
+ }
+ if (rctx->dst_nents < 0) {
+ dev_err(qce->dev, "Invalid numbers of dst SG.\n");
+ return -rctx->dst_nents;
+ }
rctx->dst_nents += 1;
diff --git a/drivers/crypto/qce/sha.c b/drivers/crypto/qce/sha.c
index 0c9973ec80eb..47e114ac09d0 100644
--- a/drivers/crypto/qce/sha.c
+++ b/drivers/crypto/qce/sha.c
@@ -92,6 +92,11 @@ static int qce_ahash_async_req_handle(struct crypto_async_request *async_req)
}
rctx->src_nents = sg_nents_for_len(req->src, req->nbytes);
+ if (rctx->src_nents < 0) {
+ dev_err(qce->dev, "Invalid numbers of src SG.\n");
+ return rctx->src_nents;
+ }
+
ret = dma_map_sg(qce->dev, req->src, rctx->src_nents, DMA_TO_DEVICE);
if (ret < 0)
return ret;
diff --git a/drivers/crypto/rockchip/Makefile b/drivers/crypto/rockchip/Makefile
new file mode 100644
index 000000000000..7051c6c715f3
--- /dev/null
+++ b/drivers/crypto/rockchip/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_CRYPTO_DEV_ROCKCHIP) += rk_crypto.o
+rk_crypto-objs := rk3288_crypto.o \
+ rk3288_crypto_ablkcipher.o \
diff --git a/drivers/crypto/rockchip/rk3288_crypto.c b/drivers/crypto/rockchip/rk3288_crypto.c
new file mode 100644
index 000000000000..da9c73dce4af
--- /dev/null
+++ b/drivers/crypto/rockchip/rk3288_crypto.c
@@ -0,0 +1,394 @@
+/*
+ * Crypto acceleration support for Rockchip RK3288
+ *
+ * Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * Author: Zain Wang <zain.wang@rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * Some ideas are from marvell-cesa.c and s5p-sss.c driver.
+ */
+
+#include "rk3288_crypto.h"
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/crypto.h>
+#include <linux/reset.h>
+
+static int rk_crypto_enable_clk(struct rk_crypto_info *dev)
+{
+ int err;
+
+ err = clk_prepare_enable(dev->sclk);
+ if (err) {
+ dev_err(dev->dev, "[%s:%d], Couldn't enable clock sclk\n",
+ __func__, __LINE__);
+ goto err_return;
+ }
+ err = clk_prepare_enable(dev->aclk);
+ if (err) {
+ dev_err(dev->dev, "[%s:%d], Couldn't enable clock aclk\n",
+ __func__, __LINE__);
+ goto err_aclk;
+ }
+ err = clk_prepare_enable(dev->hclk);
+ if (err) {
+ dev_err(dev->dev, "[%s:%d], Couldn't enable clock hclk\n",
+ __func__, __LINE__);
+ goto err_hclk;
+ }
+ err = clk_prepare_enable(dev->dmaclk);
+ if (err) {
+ dev_err(dev->dev, "[%s:%d], Couldn't enable clock dmaclk\n",
+ __func__, __LINE__);
+ goto err_dmaclk;
+ }
+ return err;
+err_dmaclk:
+ clk_disable_unprepare(dev->hclk);
+err_hclk:
+ clk_disable_unprepare(dev->aclk);
+err_aclk:
+ clk_disable_unprepare(dev->sclk);
+err_return:
+ return err;
+}
+
+static void rk_crypto_disable_clk(struct rk_crypto_info *dev)
+{
+ clk_disable_unprepare(dev->dmaclk);
+ clk_disable_unprepare(dev->hclk);
+ clk_disable_unprepare(dev->aclk);
+ clk_disable_unprepare(dev->sclk);
+}
+
+static int check_alignment(struct scatterlist *sg_src,
+ struct scatterlist *sg_dst,
+ int align_mask)
+{
+ int in, out, align;
+
+ in = IS_ALIGNED((uint32_t)sg_src->offset, 4) &&
+ IS_ALIGNED((uint32_t)sg_src->length, align_mask);
+ if (!sg_dst)
+ return in;
+ out = IS_ALIGNED((uint32_t)sg_dst->offset, 4) &&
+ IS_ALIGNED((uint32_t)sg_dst->length, align_mask);
+ align = in && out;
+
+ return (align && (sg_src->length == sg_dst->length));
+}
+
+static int rk_load_data(struct rk_crypto_info *dev,
+ struct scatterlist *sg_src,
+ struct scatterlist *sg_dst)
+{
+ unsigned int count;
+
+ dev->aligned = dev->aligned ?
+ check_alignment(sg_src, sg_dst, dev->align_size) :
+ dev->aligned;
+ if (dev->aligned) {
+ count = min(dev->left_bytes, sg_src->length);
+ dev->left_bytes -= count;
+
+ if (!dma_map_sg(dev->dev, sg_src, 1, DMA_TO_DEVICE)) {
+ dev_err(dev->dev, "[%s:%d] dma_map_sg(src) error\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ dev->addr_in = sg_dma_address(sg_src);
+
+ if (sg_dst) {
+ if (!dma_map_sg(dev->dev, sg_dst, 1, DMA_FROM_DEVICE)) {
+ dev_err(dev->dev,
+ "[%s:%d] dma_map_sg(dst) error\n",
+ __func__, __LINE__);
+ dma_unmap_sg(dev->dev, sg_src, 1,
+ DMA_TO_DEVICE);
+ return -EINVAL;
+ }
+ dev->addr_out = sg_dma_address(sg_dst);
+ }
+ } else {
+ count = (dev->left_bytes > PAGE_SIZE) ?
+ PAGE_SIZE : dev->left_bytes;
+
+ if (!sg_pcopy_to_buffer(dev->first, dev->nents,
+ dev->addr_vir, count,
+ dev->total - dev->left_bytes)) {
+ dev_err(dev->dev, "[%s:%d] pcopy err\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ dev->left_bytes -= count;
+ sg_init_one(&dev->sg_tmp, dev->addr_vir, count);
+ if (!dma_map_sg(dev->dev, &dev->sg_tmp, 1, DMA_TO_DEVICE)) {
+ dev_err(dev->dev, "[%s:%d] dma_map_sg(sg_tmp) error\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+ dev->addr_in = sg_dma_address(&dev->sg_tmp);
+
+ if (sg_dst) {
+ if (!dma_map_sg(dev->dev, &dev->sg_tmp, 1,
+ DMA_FROM_DEVICE)) {
+ dev_err(dev->dev,
+ "[%s:%d] dma_map_sg(sg_tmp) error\n",
+ __func__, __LINE__);
+ dma_unmap_sg(dev->dev, &dev->sg_tmp, 1,
+ DMA_TO_DEVICE);
+ return -ENOMEM;
+ }
+ dev->addr_out = sg_dma_address(&dev->sg_tmp);
+ }
+ }
+ dev->count = count;
+ return 0;
+}
+
+static void rk_unload_data(struct rk_crypto_info *dev)
+{
+ struct scatterlist *sg_in, *sg_out;
+
+ sg_in = dev->aligned ? dev->sg_src : &dev->sg_tmp;
+ dma_unmap_sg(dev->dev, sg_in, 1, DMA_TO_DEVICE);
+
+ if (dev->sg_dst) {
+ sg_out = dev->aligned ? dev->sg_dst : &dev->sg_tmp;
+ dma_unmap_sg(dev->dev, sg_out, 1, DMA_FROM_DEVICE);
+ }
+}
+
+static irqreturn_t rk_crypto_irq_handle(int irq, void *dev_id)
+{
+ struct rk_crypto_info *dev = platform_get_drvdata(dev_id);
+ u32 interrupt_status;
+ int err = 0;
+
+ spin_lock(&dev->lock);
+ interrupt_status = CRYPTO_READ(dev, RK_CRYPTO_INTSTS);
+ CRYPTO_WRITE(dev, RK_CRYPTO_INTSTS, interrupt_status);
+ if (interrupt_status & 0x0a) {
+ dev_warn(dev->dev, "DMA Error\n");
+ err = -EFAULT;
+ } else if (interrupt_status & 0x05) {
+ err = dev->update(dev);
+ }
+ if (err)
+ dev->complete(dev, err);
+ spin_unlock(&dev->lock);
+ return IRQ_HANDLED;
+}
+
+static void rk_crypto_tasklet_cb(unsigned long data)
+{
+ struct rk_crypto_info *dev = (struct rk_crypto_info *)data;
+ struct crypto_async_request *async_req, *backlog;
+ unsigned long flags;
+ int err = 0;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ backlog = crypto_get_backlog(&dev->queue);
+ async_req = crypto_dequeue_request(&dev->queue);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ if (!async_req) {
+ dev_err(dev->dev, "async_req is NULL !!\n");
+ return;
+ }
+ if (backlog) {
+ backlog->complete(backlog, -EINPROGRESS);
+ backlog = NULL;
+ }
+
+ if (crypto_tfm_alg_type(async_req->tfm) == CRYPTO_ALG_TYPE_ABLKCIPHER)
+ dev->ablk_req = ablkcipher_request_cast(async_req);
+ err = dev->start(dev);
+ if (err)
+ dev->complete(dev, err);
+}
+
+static struct rk_crypto_tmp *rk_cipher_algs[] = {
+ &rk_ecb_aes_alg,
+ &rk_cbc_aes_alg,
+ &rk_ecb_des_alg,
+ &rk_cbc_des_alg,
+ &rk_ecb_des3_ede_alg,
+ &rk_cbc_des3_ede_alg,
+};
+
+static int rk_crypto_register(struct rk_crypto_info *crypto_info)
+{
+ unsigned int i, k;
+ int err = 0;
+
+ for (i = 0; i < ARRAY_SIZE(rk_cipher_algs); i++) {
+ rk_cipher_algs[i]->dev = crypto_info;
+ err = crypto_register_alg(&rk_cipher_algs[i]->alg);
+ if (err)
+ goto err_cipher_algs;
+ }
+ return 0;
+
+err_cipher_algs:
+ for (k = 0; k < i; k++)
+ crypto_unregister_alg(&rk_cipher_algs[k]->alg);
+ return err;
+}
+
+static void rk_crypto_unregister(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(rk_cipher_algs); i++)
+ crypto_unregister_alg(&rk_cipher_algs[i]->alg);
+}
+
+static void rk_crypto_action(void *data)
+{
+ struct rk_crypto_info *crypto_info = data;
+
+ reset_control_assert(crypto_info->rst);
+}
+
+static const struct of_device_id crypto_of_id_table[] = {
+ { .compatible = "rockchip,rk3288-crypto" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, crypto_of_id_table);
+
+static int rk_crypto_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ struct rk_crypto_info *crypto_info;
+ int err = 0;
+
+ crypto_info = devm_kzalloc(&pdev->dev,
+ sizeof(*crypto_info), GFP_KERNEL);
+ if (!crypto_info) {
+ err = -ENOMEM;
+ goto err_crypto;
+ }
+
+ crypto_info->rst = devm_reset_control_get(dev, "crypto-rst");
+ if (IS_ERR(crypto_info->rst)) {
+ err = PTR_ERR(crypto_info->rst);
+ goto err_crypto;
+ }
+
+ reset_control_assert(crypto_info->rst);
+ usleep_range(10, 20);
+ reset_control_deassert(crypto_info->rst);
+
+ err = devm_add_action(dev, rk_crypto_action, crypto_info);
+ if (err) {
+ reset_control_assert(crypto_info->rst);
+ goto err_crypto;
+ }
+
+ spin_lock_init(&crypto_info->lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ crypto_info->reg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(crypto_info->reg)) {
+ err = PTR_ERR(crypto_info->reg);
+ goto err_crypto;
+ }
+
+ crypto_info->aclk = devm_clk_get(&pdev->dev, "aclk");
+ if (IS_ERR(crypto_info->aclk)) {
+ err = PTR_ERR(crypto_info->aclk);
+ goto err_crypto;
+ }
+
+ crypto_info->hclk = devm_clk_get(&pdev->dev, "hclk");
+ if (IS_ERR(crypto_info->hclk)) {
+ err = PTR_ERR(crypto_info->hclk);
+ goto err_crypto;
+ }
+
+ crypto_info->sclk = devm_clk_get(&pdev->dev, "sclk");
+ if (IS_ERR(crypto_info->sclk)) {
+ err = PTR_ERR(crypto_info->sclk);
+ goto err_crypto;
+ }
+
+ crypto_info->dmaclk = devm_clk_get(&pdev->dev, "apb_pclk");
+ if (IS_ERR(crypto_info->dmaclk)) {
+ err = PTR_ERR(crypto_info->dmaclk);
+ goto err_crypto;
+ }
+
+ crypto_info->irq = platform_get_irq(pdev, 0);
+ if (crypto_info->irq < 0) {
+ dev_warn(crypto_info->dev,
+ "control Interrupt is not available.\n");
+ err = crypto_info->irq;
+ goto err_crypto;
+ }
+
+ err = devm_request_irq(&pdev->dev, crypto_info->irq,
+ rk_crypto_irq_handle, IRQF_SHARED,
+ "rk-crypto", pdev);
+
+ if (err) {
+ dev_err(crypto_info->dev, "irq request failed.\n");
+ goto err_crypto;
+ }
+
+ crypto_info->dev = &pdev->dev;
+ platform_set_drvdata(pdev, crypto_info);
+
+ tasklet_init(&crypto_info->crypto_tasklet,
+ rk_crypto_tasklet_cb, (unsigned long)crypto_info);
+ crypto_init_queue(&crypto_info->queue, 50);
+
+ crypto_info->enable_clk = rk_crypto_enable_clk;
+ crypto_info->disable_clk = rk_crypto_disable_clk;
+ crypto_info->load_data = rk_load_data;
+ crypto_info->unload_data = rk_unload_data;
+
+ err = rk_crypto_register(crypto_info);
+ if (err) {
+ dev_err(dev, "err in register alg");
+ goto err_register_alg;
+ }
+
+ dev_info(dev, "Crypto Accelerator successfully registered\n");
+ return 0;
+
+err_register_alg:
+ tasklet_kill(&crypto_info->crypto_tasklet);
+err_crypto:
+ return err;
+}
+
+static int rk_crypto_remove(struct platform_device *pdev)
+{
+ struct rk_crypto_info *crypto_tmp = platform_get_drvdata(pdev);
+
+ rk_crypto_unregister();
+ tasklet_kill(&crypto_tmp->crypto_tasklet);
+ return 0;
+}
+
+static struct platform_driver crypto_driver = {
+ .probe = rk_crypto_probe,
+ .remove = rk_crypto_remove,
+ .driver = {
+ .name = "rk3288-crypto",
+ .of_match_table = crypto_of_id_table,
+ },
+};
+
+module_platform_driver(crypto_driver);
+
+MODULE_AUTHOR("Zain Wang <zain.wang@rock-chips.com>");
+MODULE_DESCRIPTION("Support for Rockchip's cryptographic engine");
+MODULE_LICENSE("GPL");
diff --git a/drivers/crypto/rockchip/rk3288_crypto.h b/drivers/crypto/rockchip/rk3288_crypto.h
new file mode 100644
index 000000000000..e499c2c6c903
--- /dev/null
+++ b/drivers/crypto/rockchip/rk3288_crypto.h
@@ -0,0 +1,216 @@
+#ifndef __RK3288_CRYPTO_H__
+#define __RK3288_CRYPTO_H__
+
+#include <crypto/aes.h>
+#include <crypto/des.h>
+#include <crypto/algapi.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#define _SBF(v, f) ((v) << (f))
+
+/* Crypto control registers*/
+#define RK_CRYPTO_INTSTS 0x0000
+#define RK_CRYPTO_PKA_DONE_INT BIT(5)
+#define RK_CRYPTO_HASH_DONE_INT BIT(4)
+#define RK_CRYPTO_HRDMA_ERR_INT BIT(3)
+#define RK_CRYPTO_HRDMA_DONE_INT BIT(2)
+#define RK_CRYPTO_BCDMA_ERR_INT BIT(1)
+#define RK_CRYPTO_BCDMA_DONE_INT BIT(0)
+
+#define RK_CRYPTO_INTENA 0x0004
+#define RK_CRYPTO_PKA_DONE_ENA BIT(5)
+#define RK_CRYPTO_HASH_DONE_ENA BIT(4)
+#define RK_CRYPTO_HRDMA_ERR_ENA BIT(3)
+#define RK_CRYPTO_HRDMA_DONE_ENA BIT(2)
+#define RK_CRYPTO_BCDMA_ERR_ENA BIT(1)
+#define RK_CRYPTO_BCDMA_DONE_ENA BIT(0)
+
+#define RK_CRYPTO_CTRL 0x0008
+#define RK_CRYPTO_WRITE_MASK _SBF(0xFFFF, 16)
+#define RK_CRYPTO_TRNG_FLUSH BIT(9)
+#define RK_CRYPTO_TRNG_START BIT(8)
+#define RK_CRYPTO_PKA_FLUSH BIT(7)
+#define RK_CRYPTO_HASH_FLUSH BIT(6)
+#define RK_CRYPTO_BLOCK_FLUSH BIT(5)
+#define RK_CRYPTO_PKA_START BIT(4)
+#define RK_CRYPTO_HASH_START BIT(3)
+#define RK_CRYPTO_BLOCK_START BIT(2)
+#define RK_CRYPTO_TDES_START BIT(1)
+#define RK_CRYPTO_AES_START BIT(0)
+
+#define RK_CRYPTO_CONF 0x000c
+/* HASH Receive DMA Address Mode: fix | increment */
+#define RK_CRYPTO_HR_ADDR_MODE BIT(8)
+/* Block Transmit DMA Address Mode: fix | increment */
+#define RK_CRYPTO_BT_ADDR_MODE BIT(7)
+/* Block Receive DMA Address Mode: fix | increment */
+#define RK_CRYPTO_BR_ADDR_MODE BIT(6)
+#define RK_CRYPTO_BYTESWAP_HRFIFO BIT(5)
+#define RK_CRYPTO_BYTESWAP_BTFIFO BIT(4)
+#define RK_CRYPTO_BYTESWAP_BRFIFO BIT(3)
+/* AES = 0 OR DES = 1 */
+#define RK_CRYPTO_DESSEL BIT(2)
+#define RK_CYYPTO_HASHINSEL_INDEPENDENT_SOURCE _SBF(0x00, 0)
+#define RK_CYYPTO_HASHINSEL_BLOCK_CIPHER_INPUT _SBF(0x01, 0)
+#define RK_CYYPTO_HASHINSEL_BLOCK_CIPHER_OUTPUT _SBF(0x02, 0)
+
+/* Block Receiving DMA Start Address Register */
+#define RK_CRYPTO_BRDMAS 0x0010
+/* Block Transmitting DMA Start Address Register */
+#define RK_CRYPTO_BTDMAS 0x0014
+/* Block Receiving DMA Length Register */
+#define RK_CRYPTO_BRDMAL 0x0018
+/* Hash Receiving DMA Start Address Register */
+#define RK_CRYPTO_HRDMAS 0x001c
+/* Hash Receiving DMA Length Register */
+#define RK_CRYPTO_HRDMAL 0x0020
+
+/* AES registers */
+#define RK_CRYPTO_AES_CTRL 0x0080
+#define RK_CRYPTO_AES_BYTESWAP_CNT BIT(11)
+#define RK_CRYPTO_AES_BYTESWAP_KEY BIT(10)
+#define RK_CRYPTO_AES_BYTESWAP_IV BIT(9)
+#define RK_CRYPTO_AES_BYTESWAP_DO BIT(8)
+#define RK_CRYPTO_AES_BYTESWAP_DI BIT(7)
+#define RK_CRYPTO_AES_KEY_CHANGE BIT(6)
+#define RK_CRYPTO_AES_ECB_MODE _SBF(0x00, 4)
+#define RK_CRYPTO_AES_CBC_MODE _SBF(0x01, 4)
+#define RK_CRYPTO_AES_CTR_MODE _SBF(0x02, 4)
+#define RK_CRYPTO_AES_128BIT_key _SBF(0x00, 2)
+#define RK_CRYPTO_AES_192BIT_key _SBF(0x01, 2)
+#define RK_CRYPTO_AES_256BIT_key _SBF(0x02, 2)
+/* Slave = 0 / fifo = 1 */
+#define RK_CRYPTO_AES_FIFO_MODE BIT(1)
+/* Encryption = 0 , Decryption = 1 */
+#define RK_CRYPTO_AES_DEC BIT(0)
+
+#define RK_CRYPTO_AES_STS 0x0084
+#define RK_CRYPTO_AES_DONE BIT(0)
+
+/* AES Input Data 0-3 Register */
+#define RK_CRYPTO_AES_DIN_0 0x0088
+#define RK_CRYPTO_AES_DIN_1 0x008c
+#define RK_CRYPTO_AES_DIN_2 0x0090
+#define RK_CRYPTO_AES_DIN_3 0x0094
+
+/* AES output Data 0-3 Register */
+#define RK_CRYPTO_AES_DOUT_0 0x0098
+#define RK_CRYPTO_AES_DOUT_1 0x009c
+#define RK_CRYPTO_AES_DOUT_2 0x00a0
+#define RK_CRYPTO_AES_DOUT_3 0x00a4
+
+/* AES IV Data 0-3 Register */
+#define RK_CRYPTO_AES_IV_0 0x00a8
+#define RK_CRYPTO_AES_IV_1 0x00ac
+#define RK_CRYPTO_AES_IV_2 0x00b0
+#define RK_CRYPTO_AES_IV_3 0x00b4
+
+/* AES Key Data 0-3 Register */
+#define RK_CRYPTO_AES_KEY_0 0x00b8
+#define RK_CRYPTO_AES_KEY_1 0x00bc
+#define RK_CRYPTO_AES_KEY_2 0x00c0
+#define RK_CRYPTO_AES_KEY_3 0x00c4
+#define RK_CRYPTO_AES_KEY_4 0x00c8
+#define RK_CRYPTO_AES_KEY_5 0x00cc
+#define RK_CRYPTO_AES_KEY_6 0x00d0
+#define RK_CRYPTO_AES_KEY_7 0x00d4
+
+/* des/tdes */
+#define RK_CRYPTO_TDES_CTRL 0x0100
+#define RK_CRYPTO_TDES_BYTESWAP_KEY BIT(8)
+#define RK_CRYPTO_TDES_BYTESWAP_IV BIT(7)
+#define RK_CRYPTO_TDES_BYTESWAP_DO BIT(6)
+#define RK_CRYPTO_TDES_BYTESWAP_DI BIT(5)
+/* 0: ECB, 1: CBC */
+#define RK_CRYPTO_TDES_CHAINMODE_CBC BIT(4)
+/* TDES Key Mode, 0 : EDE, 1 : EEE */
+#define RK_CRYPTO_TDES_EEE BIT(3)
+/* 0: DES, 1:TDES */
+#define RK_CRYPTO_TDES_SELECT BIT(2)
+/* 0: Slave, 1:Fifo */
+#define RK_CRYPTO_TDES_FIFO_MODE BIT(1)
+/* Encryption = 0 , Decryption = 1 */
+#define RK_CRYPTO_TDES_DEC BIT(0)
+
+#define RK_CRYPTO_TDES_STS 0x0104
+#define RK_CRYPTO_TDES_DONE BIT(0)
+
+#define RK_CRYPTO_TDES_DIN_0 0x0108
+#define RK_CRYPTO_TDES_DIN_1 0x010c
+#define RK_CRYPTO_TDES_DOUT_0 0x0110
+#define RK_CRYPTO_TDES_DOUT_1 0x0114
+#define RK_CRYPTO_TDES_IV_0 0x0118
+#define RK_CRYPTO_TDES_IV_1 0x011c
+#define RK_CRYPTO_TDES_KEY1_0 0x0120
+#define RK_CRYPTO_TDES_KEY1_1 0x0124
+#define RK_CRYPTO_TDES_KEY2_0 0x0128
+#define RK_CRYPTO_TDES_KEY2_1 0x012c
+#define RK_CRYPTO_TDES_KEY3_0 0x0130
+#define RK_CRYPTO_TDES_KEY3_1 0x0134
+
+#define CRYPTO_READ(dev, offset) \
+ readl_relaxed(((dev)->reg + (offset)))
+#define CRYPTO_WRITE(dev, offset, val) \
+ writel_relaxed((val), ((dev)->reg + (offset)))
+
+struct rk_crypto_info {
+ struct device *dev;
+ struct clk *aclk;
+ struct clk *hclk;
+ struct clk *sclk;
+ struct clk *dmaclk;
+ struct reset_control *rst;
+ void __iomem *reg;
+ int irq;
+ struct crypto_queue queue;
+ struct tasklet_struct crypto_tasklet;
+ struct ablkcipher_request *ablk_req;
+ /* device lock */
+ spinlock_t lock;
+
+ /* the public variable */
+ struct scatterlist *sg_src;
+ struct scatterlist *sg_dst;
+ struct scatterlist sg_tmp;
+ struct scatterlist *first;
+ unsigned int left_bytes;
+ void *addr_vir;
+ int aligned;
+ int align_size;
+ size_t nents;
+ unsigned int total;
+ unsigned int count;
+ u32 mode;
+ dma_addr_t addr_in;
+ dma_addr_t addr_out;
+ int (*start)(struct rk_crypto_info *dev);
+ int (*update)(struct rk_crypto_info *dev);
+ void (*complete)(struct rk_crypto_info *dev, int err);
+ int (*enable_clk)(struct rk_crypto_info *dev);
+ void (*disable_clk)(struct rk_crypto_info *dev);
+ int (*load_data)(struct rk_crypto_info *dev,
+ struct scatterlist *sg_src,
+ struct scatterlist *sg_dst);
+ void (*unload_data)(struct rk_crypto_info *dev);
+};
+
+/* the private variable of cipher */
+struct rk_cipher_ctx {
+ struct rk_crypto_info *dev;
+ unsigned int keylen;
+};
+
+struct rk_crypto_tmp {
+ struct rk_crypto_info *dev;
+ struct crypto_alg alg;
+};
+
+extern struct rk_crypto_tmp rk_ecb_aes_alg;
+extern struct rk_crypto_tmp rk_cbc_aes_alg;
+extern struct rk_crypto_tmp rk_ecb_des_alg;
+extern struct rk_crypto_tmp rk_cbc_des_alg;
+extern struct rk_crypto_tmp rk_ecb_des3_ede_alg;
+extern struct rk_crypto_tmp rk_cbc_des3_ede_alg;
+
+#endif
diff --git a/drivers/crypto/rockchip/rk3288_crypto_ablkcipher.c b/drivers/crypto/rockchip/rk3288_crypto_ablkcipher.c
new file mode 100644
index 000000000000..d98b681f6c06
--- /dev/null
+++ b/drivers/crypto/rockchip/rk3288_crypto_ablkcipher.c
@@ -0,0 +1,505 @@
+/*
+ * Crypto acceleration support for Rockchip RK3288
+ *
+ * Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * Author: Zain Wang <zain.wang@rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * Some ideas are from marvell-cesa.c and s5p-sss.c driver.
+ */
+#include "rk3288_crypto.h"
+
+#define RK_CRYPTO_DEC BIT(0)
+
+static void rk_crypto_complete(struct rk_crypto_info *dev, int err)
+{
+ if (dev->ablk_req->base.complete)
+ dev->ablk_req->base.complete(&dev->ablk_req->base, err);
+}
+
+static int rk_handle_req(struct rk_crypto_info *dev,
+ struct ablkcipher_request *req)
+{
+ unsigned long flags;
+ int err;
+
+ if (!IS_ALIGNED(req->nbytes, dev->align_size))
+ return -EINVAL;
+
+ dev->left_bytes = req->nbytes;
+ dev->total = req->nbytes;
+ dev->sg_src = req->src;
+ dev->first = req->src;
+ dev->nents = sg_nents(req->src);
+ dev->sg_dst = req->dst;
+ dev->aligned = 1;
+ dev->ablk_req = req;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ err = ablkcipher_enqueue_request(&dev->queue, req);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ tasklet_schedule(&dev->crypto_tasklet);
+ return err;
+}
+
+static int rk_aes_setkey(struct crypto_ablkcipher *cipher,
+ const u8 *key, unsigned int keylen)
+{
+ struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
+ struct rk_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ if (keylen != AES_KEYSIZE_128 && keylen != AES_KEYSIZE_192 &&
+ keylen != AES_KEYSIZE_256) {
+ crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+ }
+ ctx->keylen = keylen;
+ memcpy_toio(ctx->dev->reg + RK_CRYPTO_AES_KEY_0, key, keylen);
+ return 0;
+}
+
+static int rk_tdes_setkey(struct crypto_ablkcipher *cipher,
+ const u8 *key, unsigned int keylen)
+{
+ struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
+ struct rk_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
+ u32 tmp[DES_EXPKEY_WORDS];
+
+ if (keylen != DES_KEY_SIZE && keylen != DES3_EDE_KEY_SIZE) {
+ crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+ }
+
+ if (keylen == DES_KEY_SIZE) {
+ if (!des_ekey(tmp, key) &&
+ (tfm->crt_flags & CRYPTO_TFM_REQ_WEAK_KEY)) {
+ tfm->crt_flags |= CRYPTO_TFM_RES_WEAK_KEY;
+ return -EINVAL;
+ }
+ }
+
+ ctx->keylen = keylen;
+ memcpy_toio(ctx->dev->reg + RK_CRYPTO_TDES_KEY1_0, key, keylen);
+ return 0;
+}
+
+static int rk_aes_ecb_encrypt(struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+ struct rk_cipher_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+ struct rk_crypto_info *dev = ctx->dev;
+
+ dev->mode = RK_CRYPTO_AES_ECB_MODE;
+ return rk_handle_req(dev, req);
+}
+
+static int rk_aes_ecb_decrypt(struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+ struct rk_cipher_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+ struct rk_crypto_info *dev = ctx->dev;
+
+ dev->mode = RK_CRYPTO_AES_ECB_MODE | RK_CRYPTO_DEC;
+ return rk_handle_req(dev, req);
+}
+
+static int rk_aes_cbc_encrypt(struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+ struct rk_cipher_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+ struct rk_crypto_info *dev = ctx->dev;
+
+ dev->mode = RK_CRYPTO_AES_CBC_MODE;
+ return rk_handle_req(dev, req);
+}
+
+static int rk_aes_cbc_decrypt(struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+ struct rk_cipher_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+ struct rk_crypto_info *dev = ctx->dev;
+
+ dev->mode = RK_CRYPTO_AES_CBC_MODE | RK_CRYPTO_DEC;
+ return rk_handle_req(dev, req);
+}
+
+static int rk_des_ecb_encrypt(struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+ struct rk_cipher_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+ struct rk_crypto_info *dev = ctx->dev;
+
+ dev->mode = 0;
+ return rk_handle_req(dev, req);
+}
+
+static int rk_des_ecb_decrypt(struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+ struct rk_cipher_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+ struct rk_crypto_info *dev = ctx->dev;
+
+ dev->mode = RK_CRYPTO_DEC;
+ return rk_handle_req(dev, req);
+}
+
+static int rk_des_cbc_encrypt(struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+ struct rk_cipher_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+ struct rk_crypto_info *dev = ctx->dev;
+
+ dev->mode = RK_CRYPTO_TDES_CHAINMODE_CBC;
+ return rk_handle_req(dev, req);
+}
+
+static int rk_des_cbc_decrypt(struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+ struct rk_cipher_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+ struct rk_crypto_info *dev = ctx->dev;
+
+ dev->mode = RK_CRYPTO_TDES_CHAINMODE_CBC | RK_CRYPTO_DEC;
+ return rk_handle_req(dev, req);
+}
+
+static int rk_des3_ede_ecb_encrypt(struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+ struct rk_cipher_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+ struct rk_crypto_info *dev = ctx->dev;
+
+ dev->mode = RK_CRYPTO_TDES_SELECT;
+ return rk_handle_req(dev, req);
+}
+
+static int rk_des3_ede_ecb_decrypt(struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+ struct rk_cipher_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+ struct rk_crypto_info *dev = ctx->dev;
+
+ dev->mode = RK_CRYPTO_TDES_SELECT | RK_CRYPTO_DEC;
+ return rk_handle_req(dev, req);
+}
+
+static int rk_des3_ede_cbc_encrypt(struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+ struct rk_cipher_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+ struct rk_crypto_info *dev = ctx->dev;
+
+ dev->mode = RK_CRYPTO_TDES_SELECT | RK_CRYPTO_TDES_CHAINMODE_CBC;
+ return rk_handle_req(dev, req);
+}
+
+static int rk_des3_ede_cbc_decrypt(struct ablkcipher_request *req)
+{
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+ struct rk_cipher_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+ struct rk_crypto_info *dev = ctx->dev;
+
+ dev->mode = RK_CRYPTO_TDES_SELECT | RK_CRYPTO_TDES_CHAINMODE_CBC |
+ RK_CRYPTO_DEC;
+ return rk_handle_req(dev, req);
+}
+
+static void rk_ablk_hw_init(struct rk_crypto_info *dev)
+{
+ struct crypto_ablkcipher *cipher =
+ crypto_ablkcipher_reqtfm(dev->ablk_req);
+ struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
+ struct rk_cipher_ctx *ctx = crypto_ablkcipher_ctx(cipher);
+ u32 ivsize, block, conf_reg = 0;
+
+ block = crypto_tfm_alg_blocksize(tfm);
+ ivsize = crypto_ablkcipher_ivsize(cipher);
+
+ if (block == DES_BLOCK_SIZE) {
+ dev->mode |= RK_CRYPTO_TDES_FIFO_MODE |
+ RK_CRYPTO_TDES_BYTESWAP_KEY |
+ RK_CRYPTO_TDES_BYTESWAP_IV;
+ CRYPTO_WRITE(dev, RK_CRYPTO_TDES_CTRL, dev->mode);
+ memcpy_toio(dev->reg + RK_CRYPTO_TDES_IV_0,
+ dev->ablk_req->info, ivsize);
+ conf_reg = RK_CRYPTO_DESSEL;
+ } else {
+ dev->mode |= RK_CRYPTO_AES_FIFO_MODE |
+ RK_CRYPTO_AES_KEY_CHANGE |
+ RK_CRYPTO_AES_BYTESWAP_KEY |
+ RK_CRYPTO_AES_BYTESWAP_IV;
+ if (ctx->keylen == AES_KEYSIZE_192)
+ dev->mode |= RK_CRYPTO_AES_192BIT_key;
+ else if (ctx->keylen == AES_KEYSIZE_256)
+ dev->mode |= RK_CRYPTO_AES_256BIT_key;
+ CRYPTO_WRITE(dev, RK_CRYPTO_AES_CTRL, dev->mode);
+ memcpy_toio(dev->reg + RK_CRYPTO_AES_IV_0,
+ dev->ablk_req->info, ivsize);
+ }
+ conf_reg |= RK_CRYPTO_BYTESWAP_BTFIFO |
+ RK_CRYPTO_BYTESWAP_BRFIFO;
+ CRYPTO_WRITE(dev, RK_CRYPTO_CONF, conf_reg);
+ CRYPTO_WRITE(dev, RK_CRYPTO_INTENA,
+ RK_CRYPTO_BCDMA_ERR_ENA | RK_CRYPTO_BCDMA_DONE_ENA);
+}
+
+static void crypto_dma_start(struct rk_crypto_info *dev)
+{
+ CRYPTO_WRITE(dev, RK_CRYPTO_BRDMAS, dev->addr_in);
+ CRYPTO_WRITE(dev, RK_CRYPTO_BRDMAL, dev->count / 4);
+ CRYPTO_WRITE(dev, RK_CRYPTO_BTDMAS, dev->addr_out);
+ CRYPTO_WRITE(dev, RK_CRYPTO_CTRL, RK_CRYPTO_BLOCK_START |
+ _SBF(RK_CRYPTO_BLOCK_START, 16));
+}
+
+static int rk_set_data_start(struct rk_crypto_info *dev)
+{
+ int err;
+
+ err = dev->load_data(dev, dev->sg_src, dev->sg_dst);
+ if (!err)
+ crypto_dma_start(dev);
+ return err;
+}
+
+static int rk_ablk_start(struct rk_crypto_info *dev)
+{
+ unsigned long flags;
+ int err;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ rk_ablk_hw_init(dev);
+ err = rk_set_data_start(dev);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return err;
+}
+
+static void rk_iv_copyback(struct rk_crypto_info *dev)
+{
+ struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(dev->ablk_req);
+ u32 ivsize = crypto_ablkcipher_ivsize(tfm);
+
+ if (ivsize == DES_BLOCK_SIZE)
+ memcpy_fromio(dev->ablk_req->info,
+ dev->reg + RK_CRYPTO_TDES_IV_0, ivsize);
+ else if (ivsize == AES_BLOCK_SIZE)
+ memcpy_fromio(dev->ablk_req->info,
+ dev->reg + RK_CRYPTO_AES_IV_0, ivsize);
+}
+
+/* return:
+ * true some err was occurred
+ * fault no err, continue
+ */
+static int rk_ablk_rx(struct rk_crypto_info *dev)
+{
+ int err = 0;
+
+ dev->unload_data(dev);
+ if (!dev->aligned) {
+ if (!sg_pcopy_from_buffer(dev->ablk_req->dst, dev->nents,
+ dev->addr_vir, dev->count,
+ dev->total - dev->left_bytes -
+ dev->count)) {
+ err = -EINVAL;
+ goto out_rx;
+ }
+ }
+ if (dev->left_bytes) {
+ if (dev->aligned) {
+ if (sg_is_last(dev->sg_src)) {
+ dev_err(dev->dev, "[%s:%d] Lack of data\n",
+ __func__, __LINE__);
+ err = -ENOMEM;
+ goto out_rx;
+ }
+ dev->sg_src = sg_next(dev->sg_src);
+ dev->sg_dst = sg_next(dev->sg_dst);
+ }
+ err = rk_set_data_start(dev);
+ } else {
+ rk_iv_copyback(dev);
+ /* here show the calculation is over without any err */
+ dev->complete(dev, 0);
+ }
+out_rx:
+ return err;
+}
+
+static int rk_ablk_cra_init(struct crypto_tfm *tfm)
+{
+ struct rk_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct crypto_alg *alg = tfm->__crt_alg;
+ struct rk_crypto_tmp *algt;
+
+ algt = container_of(alg, struct rk_crypto_tmp, alg);
+
+ ctx->dev = algt->dev;
+ ctx->dev->align_size = crypto_tfm_alg_alignmask(tfm) + 1;
+ ctx->dev->start = rk_ablk_start;
+ ctx->dev->update = rk_ablk_rx;
+ ctx->dev->complete = rk_crypto_complete;
+ ctx->dev->addr_vir = (char *)__get_free_page(GFP_KERNEL);
+
+ return ctx->dev->addr_vir ? ctx->dev->enable_clk(ctx->dev) : -ENOMEM;
+}
+
+static void rk_ablk_cra_exit(struct crypto_tfm *tfm)
+{
+ struct rk_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ free_page((unsigned long)ctx->dev->addr_vir);
+ ctx->dev->disable_clk(ctx->dev);
+}
+
+struct rk_crypto_tmp rk_ecb_aes_alg = {
+ .alg = {
+ .cra_name = "ecb(aes)",
+ .cra_driver_name = "ecb-aes-rk",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct rk_cipher_ctx),
+ .cra_alignmask = 0x0f,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_init = rk_ablk_cra_init,
+ .cra_exit = rk_ablk_cra_exit,
+ .cra_u.ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = rk_aes_setkey,
+ .encrypt = rk_aes_ecb_encrypt,
+ .decrypt = rk_aes_ecb_decrypt,
+ }
+ }
+};
+
+struct rk_crypto_tmp rk_cbc_aes_alg = {
+ .alg = {
+ .cra_name = "cbc(aes)",
+ .cra_driver_name = "cbc-aes-rk",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct rk_cipher_ctx),
+ .cra_alignmask = 0x0f,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_init = rk_ablk_cra_init,
+ .cra_exit = rk_ablk_cra_exit,
+ .cra_u.ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .ivsize = AES_BLOCK_SIZE,
+ .setkey = rk_aes_setkey,
+ .encrypt = rk_aes_cbc_encrypt,
+ .decrypt = rk_aes_cbc_decrypt,
+ }
+ }
+};
+
+struct rk_crypto_tmp rk_ecb_des_alg = {
+ .alg = {
+ .cra_name = "ecb(des)",
+ .cra_driver_name = "ecb-des-rk",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct rk_cipher_ctx),
+ .cra_alignmask = 0x07,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_init = rk_ablk_cra_init,
+ .cra_exit = rk_ablk_cra_exit,
+ .cra_u.ablkcipher = {
+ .min_keysize = DES_KEY_SIZE,
+ .max_keysize = DES_KEY_SIZE,
+ .setkey = rk_tdes_setkey,
+ .encrypt = rk_des_ecb_encrypt,
+ .decrypt = rk_des_ecb_decrypt,
+ }
+ }
+};
+
+struct rk_crypto_tmp rk_cbc_des_alg = {
+ .alg = {
+ .cra_name = "cbc(des)",
+ .cra_driver_name = "cbc-des-rk",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct rk_cipher_ctx),
+ .cra_alignmask = 0x07,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_init = rk_ablk_cra_init,
+ .cra_exit = rk_ablk_cra_exit,
+ .cra_u.ablkcipher = {
+ .min_keysize = DES_KEY_SIZE,
+ .max_keysize = DES_KEY_SIZE,
+ .ivsize = DES_BLOCK_SIZE,
+ .setkey = rk_tdes_setkey,
+ .encrypt = rk_des_cbc_encrypt,
+ .decrypt = rk_des_cbc_decrypt,
+ }
+ }
+};
+
+struct rk_crypto_tmp rk_ecb_des3_ede_alg = {
+ .alg = {
+ .cra_name = "ecb(des3_ede)",
+ .cra_driver_name = "ecb-des3-ede-rk",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct rk_cipher_ctx),
+ .cra_alignmask = 0x07,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_init = rk_ablk_cra_init,
+ .cra_exit = rk_ablk_cra_exit,
+ .cra_u.ablkcipher = {
+ .min_keysize = DES3_EDE_KEY_SIZE,
+ .max_keysize = DES3_EDE_KEY_SIZE,
+ .ivsize = DES_BLOCK_SIZE,
+ .setkey = rk_tdes_setkey,
+ .encrypt = rk_des3_ede_ecb_encrypt,
+ .decrypt = rk_des3_ede_ecb_decrypt,
+ }
+ }
+};
+
+struct rk_crypto_tmp rk_cbc_des3_ede_alg = {
+ .alg = {
+ .cra_name = "cbc(des3_ede)",
+ .cra_driver_name = "cbc-des3-ede-rk",
+ .cra_priority = 300,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct rk_cipher_ctx),
+ .cra_alignmask = 0x07,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_init = rk_ablk_cra_init,
+ .cra_exit = rk_ablk_cra_exit,
+ .cra_u.ablkcipher = {
+ .min_keysize = DES3_EDE_KEY_SIZE,
+ .max_keysize = DES3_EDE_KEY_SIZE,
+ .ivsize = DES_BLOCK_SIZE,
+ .setkey = rk_tdes_setkey,
+ .encrypt = rk_des3_ede_cbc_encrypt,
+ .decrypt = rk_des3_ede_cbc_decrypt,
+ }
+ }
+};
diff --git a/drivers/crypto/sahara.c b/drivers/crypto/sahara.c
index f68c24a98277..6c4f91c5e6b3 100644
--- a/drivers/crypto/sahara.c
+++ b/drivers/crypto/sahara.c
@@ -130,18 +130,18 @@
#define SAHARA_REG_IDAR 0x20
struct sahara_hw_desc {
- u32 hdr;
- u32 len1;
- dma_addr_t p1;
- u32 len2;
- dma_addr_t p2;
- dma_addr_t next;
+ u32 hdr;
+ u32 len1;
+ u32 p1;
+ u32 len2;
+ u32 p2;
+ u32 next;
};
struct sahara_hw_link {
- u32 len;
- dma_addr_t p;
- dma_addr_t next;
+ u32 len;
+ u32 p;
+ u32 next;
};
struct sahara_ctx {
@@ -228,9 +228,9 @@ struct sahara_dev {
size_t total;
struct scatterlist *in_sg;
- unsigned int nb_in_sg;
+ int nb_in_sg;
struct scatterlist *out_sg;
- unsigned int nb_out_sg;
+ int nb_out_sg;
u32 error;
};
@@ -416,8 +416,8 @@ static void sahara_dump_descriptors(struct sahara_dev *dev)
return;
for (i = 0; i < SAHARA_MAX_HW_DESC; i++) {
- dev_dbg(dev->device, "Descriptor (%d) (0x%08x):\n",
- i, dev->hw_phys_desc[i]);
+ dev_dbg(dev->device, "Descriptor (%d) (%pad):\n",
+ i, &dev->hw_phys_desc[i]);
dev_dbg(dev->device, "\thdr = 0x%08x\n", dev->hw_desc[i]->hdr);
dev_dbg(dev->device, "\tlen1 = %u\n", dev->hw_desc[i]->len1);
dev_dbg(dev->device, "\tp1 = 0x%08x\n", dev->hw_desc[i]->p1);
@@ -437,8 +437,8 @@ static void sahara_dump_links(struct sahara_dev *dev)
return;
for (i = 0; i < SAHARA_MAX_HW_LINK; i++) {
- dev_dbg(dev->device, "Link (%d) (0x%08x):\n",
- i, dev->hw_phys_link[i]);
+ dev_dbg(dev->device, "Link (%d) (%pad):\n",
+ i, &dev->hw_phys_link[i]);
dev_dbg(dev->device, "\tlen = %u\n", dev->hw_link[i]->len);
dev_dbg(dev->device, "\tp = 0x%08x\n", dev->hw_link[i]->p);
dev_dbg(dev->device, "\tnext = 0x%08x\n",
@@ -477,7 +477,15 @@ static int sahara_hw_descriptor_create(struct sahara_dev *dev)
}
dev->nb_in_sg = sg_nents_for_len(dev->in_sg, dev->total);
+ if (dev->nb_in_sg < 0) {
+ dev_err(dev->device, "Invalid numbers of src SG.\n");
+ return dev->nb_in_sg;
+ }
dev->nb_out_sg = sg_nents_for_len(dev->out_sg, dev->total);
+ if (dev->nb_out_sg < 0) {
+ dev_err(dev->device, "Invalid numbers of dst SG.\n");
+ return dev->nb_out_sg;
+ }
if ((dev->nb_in_sg + dev->nb_out_sg) > SAHARA_MAX_HW_LINK) {
dev_err(dev->device, "not enough hw links (%d)\n",
dev->nb_in_sg + dev->nb_out_sg);
@@ -793,6 +801,10 @@ static int sahara_sha_hw_links_create(struct sahara_dev *dev,
dev->in_sg = rctx->in_sg;
dev->nb_in_sg = sg_nents_for_len(dev->in_sg, rctx->total);
+ if (dev->nb_in_sg < 0) {
+ dev_err(dev->device, "Invalid numbers of src SG.\n");
+ return dev->nb_in_sg;
+ }
if ((dev->nb_in_sg) > SAHARA_MAX_HW_LINK) {
dev_err(dev->device, "not enough hw links (%d)\n",
dev->nb_in_sg + dev->nb_out_sg);
diff --git a/drivers/crypto/sunxi-ss/sun4i-ss-core.c b/drivers/crypto/sunxi-ss/sun4i-ss-core.c
index eab6fe227fa0..107cd2a41cae 100644
--- a/drivers/crypto/sunxi-ss/sun4i-ss-core.c
+++ b/drivers/crypto/sunxi-ss/sun4i-ss-core.c
@@ -39,6 +39,7 @@ static struct sun4i_ss_alg_template ss_algs[] = {
.import = sun4i_hash_import_md5,
.halg = {
.digestsize = MD5_DIGEST_SIZE,
+ .statesize = sizeof(struct md5_state),
.base = {
.cra_name = "md5",
.cra_driver_name = "md5-sun4i-ss",
@@ -66,6 +67,7 @@ static struct sun4i_ss_alg_template ss_algs[] = {
.import = sun4i_hash_import_sha1,
.halg = {
.digestsize = SHA1_DIGEST_SIZE,
+ .statesize = sizeof(struct sha1_state),
.base = {
.cra_name = "sha1",
.cra_driver_name = "sha1-sun4i-ss",
diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c
index 46f531e19ccf..a0d4a08313ae 100644
--- a/drivers/crypto/talitos.c
+++ b/drivers/crypto/talitos.c
@@ -977,7 +977,7 @@ static void ipsec_esp_decrypt_swauth_done(struct device *dev,
} else
oicv = (char *)&edesc->link_tbl[0];
- err = memcmp(oicv, icv, authsize) ? -EBADMSG : 0;
+ err = crypto_memneq(oicv, icv, authsize) ? -EBADMSG : 0;
}
kfree(edesc);
@@ -1216,6 +1216,7 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev,
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;
+ void *err;
if (cryptlen + authsize > max_len) {
dev_err(dev, "length exceeds h/w max limit\n");
@@ -1228,14 +1229,29 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev,
if (!dst || dst == src) {
src_nents = sg_nents_for_len(src,
assoclen + cryptlen + authsize);
+ if (src_nents < 0) {
+ dev_err(dev, "Invalid number of src SG.\n");
+ err = ERR_PTR(-EINVAL);
+ goto error_sg;
+ }
src_nents = (src_nents == 1) ? 0 : src_nents;
dst_nents = dst ? src_nents : 0;
} else { /* dst && dst != src*/
src_nents = sg_nents_for_len(src, assoclen + cryptlen +
(encrypt ? 0 : authsize));
+ if (src_nents < 0) {
+ dev_err(dev, "Invalid number of src SG.\n");
+ err = ERR_PTR(-EINVAL);
+ goto error_sg;
+ }
src_nents = (src_nents == 1) ? 0 : src_nents;
dst_nents = sg_nents_for_len(dst, assoclen + cryptlen +
(encrypt ? authsize : 0));
+ if (dst_nents < 0) {
+ dev_err(dev, "Invalid number of dst SG.\n");
+ err = ERR_PTR(-EINVAL);
+ goto error_sg;
+ }
dst_nents = (dst_nents == 1) ? 0 : dst_nents;
}
@@ -1260,11 +1276,9 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev,
edesc = kmalloc(alloc_len, GFP_DMA | flags);
if (!edesc) {
- if (iv_dma)
- dma_unmap_single(dev, iv_dma, ivsize, DMA_TO_DEVICE);
-
dev_err(dev, "could not allocate edescriptor\n");
- return ERR_PTR(-ENOMEM);
+ err = ERR_PTR(-ENOMEM);
+ goto error_sg;
}
edesc->src_nents = src_nents;
@@ -1277,6 +1291,10 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev,
DMA_BIDIRECTIONAL);
return edesc;
+error_sg:
+ if (iv_dma)
+ dma_unmap_single(dev, iv_dma, ivsize, DMA_TO_DEVICE);
+ return err;
}
static struct talitos_edesc *aead_edesc_alloc(struct aead_request *areq, u8 *iv,
@@ -1830,11 +1848,16 @@ static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes)
unsigned int nbytes_to_hash;
unsigned int to_hash_later;
unsigned int nsg;
+ int nents;
if (!req_ctx->last && (nbytes + req_ctx->nbuf <= blocksize)) {
/* Buffer up to one whole block */
- sg_copy_to_buffer(areq->src,
- sg_nents_for_len(areq->src, nbytes),
+ nents = sg_nents_for_len(areq->src, nbytes);
+ if (nents < 0) {
+ dev_err(ctx->dev, "Invalid number of src SG.\n");
+ return nents;
+ }
+ sg_copy_to_buffer(areq->src, nents,
req_ctx->buf + req_ctx->nbuf, nbytes);
req_ctx->nbuf += nbytes;
return 0;
@@ -1867,7 +1890,11 @@ static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes)
req_ctx->psrc = areq->src;
if (to_hash_later) {
- int nents = sg_nents_for_len(areq->src, nbytes);
+ nents = sg_nents_for_len(areq->src, nbytes);
+ if (nents < 0) {
+ dev_err(ctx->dev, "Invalid number of src SG.\n");
+ return nents;
+ }
sg_pcopy_to_buffer(areq->src, nents,
req_ctx->bufnext,
to_hash_later,
@@ -2297,6 +2324,22 @@ static struct talitos_alg_template driver_algs[] = {
/* ABLKCIPHER algorithms. */
{ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
.alg.crypto = {
+ .cra_name = "ecb(aes)",
+ .cra_driver_name = "ecb-aes-talitos",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .ivsize = AES_BLOCK_SIZE,
+ }
+ },
+ .desc_hdr_template = DESC_HDR_TYPE_COMMON_NONSNOOP_NO_AFEU |
+ DESC_HDR_SEL0_AESU,
+ },
+ { .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .alg.crypto = {
.cra_name = "cbc(aes)",
.cra_driver_name = "cbc-aes-talitos",
.cra_blocksize = AES_BLOCK_SIZE,
@@ -2314,6 +2357,73 @@ static struct talitos_alg_template driver_algs[] = {
},
{ .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
.alg.crypto = {
+ .cra_name = "ctr(aes)",
+ .cra_driver_name = "ctr-aes-talitos",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .ivsize = AES_BLOCK_SIZE,
+ }
+ },
+ .desc_hdr_template = DESC_HDR_TYPE_COMMON_NONSNOOP_NO_AFEU |
+ DESC_HDR_SEL0_AESU |
+ DESC_HDR_MODE0_AESU_CTR,
+ },
+ { .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .alg.crypto = {
+ .cra_name = "ecb(des)",
+ .cra_driver_name = "ecb-des-talitos",
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_ablkcipher = {
+ .min_keysize = DES_KEY_SIZE,
+ .max_keysize = DES_KEY_SIZE,
+ .ivsize = DES_BLOCK_SIZE,
+ }
+ },
+ .desc_hdr_template = DESC_HDR_TYPE_COMMON_NONSNOOP_NO_AFEU |
+ DESC_HDR_SEL0_DEU,
+ },
+ { .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .alg.crypto = {
+ .cra_name = "cbc(des)",
+ .cra_driver_name = "cbc-des-talitos",
+ .cra_blocksize = DES_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_ablkcipher = {
+ .min_keysize = DES_KEY_SIZE,
+ .max_keysize = DES_KEY_SIZE,
+ .ivsize = DES_BLOCK_SIZE,
+ }
+ },
+ .desc_hdr_template = DESC_HDR_TYPE_COMMON_NONSNOOP_NO_AFEU |
+ DESC_HDR_SEL0_DEU |
+ DESC_HDR_MODE0_DEU_CBC,
+ },
+ { .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .alg.crypto = {
+ .cra_name = "ecb(des3_ede)",
+ .cra_driver_name = "ecb-3des-talitos",
+ .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_ablkcipher = {
+ .min_keysize = DES3_EDE_KEY_SIZE,
+ .max_keysize = DES3_EDE_KEY_SIZE,
+ .ivsize = DES3_EDE_BLOCK_SIZE,
+ }
+ },
+ .desc_hdr_template = DESC_HDR_TYPE_COMMON_NONSNOOP_NO_AFEU |
+ DESC_HDR_SEL0_DEU |
+ DESC_HDR_MODE0_DEU_3DES,
+ },
+ { .type = CRYPTO_ALG_TYPE_ABLKCIPHER,
+ .alg.crypto = {
.cra_name = "cbc(des3_ede)",
.cra_driver_name = "cbc-3des-talitos",
.cra_blocksize = DES3_EDE_BLOCK_SIZE,
diff --git a/drivers/crypto/talitos.h b/drivers/crypto/talitos.h
index 0090f3211d68..8dd8f40e2771 100644
--- a/drivers/crypto/talitos.h
+++ b/drivers/crypto/talitos.h
@@ -345,6 +345,7 @@ static inline bool has_ftr_sec1(struct talitos_private *priv)
/* primary execution unit mode (MODE0) and derivatives */
#define DESC_HDR_MODE0_ENCRYPT cpu_to_be32(0x00100000)
#define DESC_HDR_MODE0_AESU_CBC cpu_to_be32(0x00200000)
+#define DESC_HDR_MODE0_AESU_CTR cpu_to_be32(0x00600000)
#define DESC_HDR_MODE0_DEU_CBC cpu_to_be32(0x00400000)
#define DESC_HDR_MODE0_DEU_3DES cpu_to_be32(0x00200000)
#define DESC_HDR_MODE0_MDEU_CONT cpu_to_be32(0x08000000)
diff --git a/drivers/crypto/ux500/Kconfig b/drivers/crypto/ux500/Kconfig
index 30796441b0a6..0e338bf6dfb7 100644
--- a/drivers/crypto/ux500/Kconfig
+++ b/drivers/crypto/ux500/Kconfig
@@ -18,6 +18,8 @@ config CRYPTO_DEV_UX500_HASH
tristate "UX500 crypto driver for HASH block"
depends on CRYPTO_DEV_UX500
select CRYPTO_HASH
+ select CRYPTO_SHA1
+ select CRYPTO_SHA256
help
This selects the hash driver for the UX500_HASH hardware.
Depends on UX500/STM DMA if running in DMA mode.
diff --git a/drivers/crypto/ux500/hash/hash_core.c b/drivers/crypto/ux500/hash/hash_core.c
index f47d112041b2..d6fdc583ce5d 100644
--- a/drivers/crypto/ux500/hash/hash_core.c
+++ b/drivers/crypto/ux500/hash/hash_core.c
@@ -41,22 +41,6 @@ static int hash_mode;
module_param(hash_mode, int, 0);
MODULE_PARM_DESC(hash_mode, "CPU or DMA mode. CPU = 0 (default), DMA = 1");
-/**
- * Pre-calculated empty message digests.
- */
-static const u8 zero_message_hash_sha1[SHA1_DIGEST_SIZE] = {
- 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d,
- 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90,
- 0xaf, 0xd8, 0x07, 0x09
-};
-
-static const u8 zero_message_hash_sha256[SHA256_DIGEST_SIZE] = {
- 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
- 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
- 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
- 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55
-};
-
/* HMAC-SHA1, no key */
static const u8 zero_message_hmac_sha1[SHA1_DIGEST_SIZE] = {
0xfb, 0xdb, 0x1d, 0x1b, 0x18, 0xaa, 0x6c, 0x08,
@@ -242,13 +226,13 @@ static int get_empty_message_digest(
if (HASH_OPER_MODE_HASH == ctx->config.oper_mode) {
if (HASH_ALGO_SHA1 == ctx->config.algorithm) {
- memcpy(zero_hash, &zero_message_hash_sha1[0],
+ memcpy(zero_hash, &sha1_zero_message_hash[0],
SHA1_DIGEST_SIZE);
*zero_hash_size = SHA1_DIGEST_SIZE;
*zero_digest = true;
} else if (HASH_ALGO_SHA256 ==
ctx->config.algorithm) {
- memcpy(zero_hash, &zero_message_hash_sha256[0],
+ memcpy(zero_hash, &sha256_zero_message_hash[0],
SHA256_DIGEST_SIZE);
*zero_hash_size = SHA256_DIGEST_SIZE;
*zero_digest = true;
diff --git a/drivers/crypto/vmx/aes_cbc.c b/drivers/crypto/vmx/aes_cbc.c
index 1881b3f413fa..495577b6d31b 100644
--- a/drivers/crypto/vmx/aes_cbc.c
+++ b/drivers/crypto/vmx/aes_cbc.c
@@ -191,7 +191,7 @@ struct crypto_alg p8_aes_cbc_alg = {
.cra_init = p8_aes_cbc_init,
.cra_exit = p8_aes_cbc_exit,
.cra_blkcipher = {
- .ivsize = 0,
+ .ivsize = AES_BLOCK_SIZE,
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
.setkey = p8_aes_cbc_setkey,
diff --git a/drivers/crypto/vmx/aes_ctr.c b/drivers/crypto/vmx/aes_ctr.c
index 2d58b18acc10..0a3c1b04cf3c 100644
--- a/drivers/crypto/vmx/aes_ctr.c
+++ b/drivers/crypto/vmx/aes_ctr.c
@@ -175,7 +175,7 @@ struct crypto_alg p8_aes_ctr_alg = {
.cra_init = p8_aes_ctr_init,
.cra_exit = p8_aes_ctr_exit,
.cra_blkcipher = {
- .ivsize = 0,
+ .ivsize = AES_BLOCK_SIZE,
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
.setkey = p8_aes_ctr_setkey,
diff --git a/drivers/dca/dca-core.c b/drivers/dca/dca-core.c
index 819dfda88236..7afbb28d6a0f 100644
--- a/drivers/dca/dca-core.c
+++ b/drivers/dca/dca-core.c
@@ -321,7 +321,8 @@ EXPORT_SYMBOL_GPL(dca_get_tag);
* @ops - pointer to struct of dca operation function pointers
* @priv_size - size of extra mem to be added for provider's needs
*/
-struct dca_provider *alloc_dca_provider(struct dca_ops *ops, int priv_size)
+struct dca_provider *alloc_dca_provider(const struct dca_ops *ops,
+ int priv_size)
{
struct dca_provider *dca;
int alloc_size;
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index e6cd1a32025a..3a8ce67910c2 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -431,6 +431,18 @@ config STE_DMA40
help
Support for ST-Ericsson DMA40 controller
+config STM32_DMA
+ bool "STMicroelectronics STM32 DMA support"
+ depends on ARCH_STM32
+ select DMA_ENGINE
+ select DMA_OF
+ select DMA_VIRTUAL_CHANNELS
+ help
+ Enable support for the on-chip DMA controller on STMicroelectronics
+ STM32 MCUs.
+ If you have a board based on such a MCU and wish to use DMA say Y or M
+ here.
+
config S3C24XX_DMAC
tristate "Samsung S3C24XX DMA support"
depends on ARCH_S3C24XX
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index ef9c099bd2b6..2dd0a067a0ca 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -56,6 +56,7 @@ obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o
obj-$(CONFIG_RENESAS_DMA) += sh/
obj-$(CONFIG_SIRF_DMA) += sirf-dma.o
obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o
+obj-$(CONFIG_STM32_DMA) += stm32-dma.o
obj-$(CONFIG_S3C24XX_DMAC) += s3c24xx-dma.o
obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o
obj-$(CONFIG_TEGRA20_APB_DMA) += tegra20-apb-dma.o
diff --git a/drivers/dma/acpi-dma.c b/drivers/dma/acpi-dma.c
index 16d0daa058a5..eed6bda01790 100644
--- a/drivers/dma/acpi-dma.c
+++ b/drivers/dma/acpi-dma.c
@@ -15,6 +15,7 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/module.h>
+#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/slab.h>
@@ -72,7 +73,9 @@ static int acpi_dma_parse_resource_group(const struct acpi_csrt_group *grp,
si = (const struct acpi_csrt_shared_info *)&grp[1];
/* Match device by MMIO and IRQ */
- if (si->mmio_base_low != mem || si->gsi_interrupt != irq)
+ if (si->mmio_base_low != lower_32_bits(mem) ||
+ si->mmio_base_high != upper_32_bits(mem) ||
+ si->gsi_interrupt != irq)
return 0;
dev_dbg(&adev->dev, "matches with %.4s%04X (rev %u)\n",
diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c
index 7f039de143f0..39f59666f93f 100644
--- a/drivers/dma/at_xdmac.c
+++ b/drivers/dma/at_xdmac.c
@@ -156,7 +156,7 @@
#define AT_XDMAC_CC_WRIP (0x1 << 23) /* Write in Progress (read only) */
#define AT_XDMAC_CC_WRIP_DONE (0x0 << 23)
#define AT_XDMAC_CC_WRIP_IN_PROGRESS (0x1 << 23)
-#define AT_XDMAC_CC_PERID(i) (0x7f & (h) << 24) /* Channel Peripheral Identifier */
+#define AT_XDMAC_CC_PERID(i) (0x7f & (i) << 24) /* Channel Peripheral Identifier */
#define AT_XDMAC_CDS_MSP 0x2C /* Channel Data Stride Memory Set Pattern */
#define AT_XDMAC_CSUS 0x30 /* Channel Source Microblock Stride */
#define AT_XDMAC_CDUS 0x34 /* Channel Destination Microblock Stride */
@@ -863,8 +863,12 @@ at_xdmac_interleaved_queue_desc(struct dma_chan *chan,
* access. Hopefully we can access DDR through both ports (at least on
* SAMA5D4x), so we can use the same interface for source and dest,
* that solves the fact we don't know the direction.
+ * ERRATA: Even if useless for memory transfers, the PERID has to not
+ * match the one of another channel. If not, it could lead to spurious
+ * flag status.
*/
- u32 chan_cc = AT_XDMAC_CC_DIF(0)
+ u32 chan_cc = AT_XDMAC_CC_PERID(0x3f)
+ | AT_XDMAC_CC_DIF(0)
| AT_XDMAC_CC_SIF(0)
| AT_XDMAC_CC_MBSIZE_SIXTEEN
| AT_XDMAC_CC_TYPE_MEM_TRAN;
@@ -965,7 +969,9 @@ at_xdmac_prep_interleaved(struct dma_chan *chan,
NULL,
src_addr, dst_addr,
xt, xt->sgl);
- for (i = 0; i < xt->numf; i++)
+
+ /* Length of the block is (BLEN+1) microblocks. */
+ for (i = 0; i < xt->numf - 1; i++)
at_xdmac_increment_block_count(chan, first);
dev_dbg(chan2dev(chan), "%s: add desc 0x%p to descs_list 0x%p\n",
@@ -1039,8 +1045,12 @@ at_xdmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
* access DDR through both ports (at least on SAMA5D4x), so we can use
* the same interface for source and dest, that solves the fact we
* don't know the direction.
+ * ERRATA: Even if useless for memory transfers, the PERID has to not
+ * match the one of another channel. If not, it could lead to spurious
+ * flag status.
*/
- u32 chan_cc = AT_XDMAC_CC_DAM_INCREMENTED_AM
+ u32 chan_cc = AT_XDMAC_CC_PERID(0x3f)
+ | AT_XDMAC_CC_DAM_INCREMENTED_AM
| AT_XDMAC_CC_SAM_INCREMENTED_AM
| AT_XDMAC_CC_DIF(0)
| AT_XDMAC_CC_SIF(0)
@@ -1086,6 +1096,7 @@ at_xdmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
/* Check remaining length and change data width if needed. */
dwidth = at_xdmac_align_width(chan,
src_addr | dst_addr | xfer_size);
+ chan_cc &= ~AT_XDMAC_CC_DWIDTH_MASK;
chan_cc |= AT_XDMAC_CC_DWIDTH(dwidth);
ublen = xfer_size >> dwidth;
@@ -1140,8 +1151,12 @@ static struct at_xdmac_desc *at_xdmac_memset_create_desc(struct dma_chan *chan,
* access. Hopefully we can access DDR through both ports (at least on
* SAMA5D4x), so we can use the same interface for source and dest,
* that solves the fact we don't know the direction.
+ * ERRATA: Even if useless for memory transfers, the PERID has to not
+ * match the one of another channel. If not, it could lead to spurious
+ * flag status.
*/
- u32 chan_cc = AT_XDMAC_CC_DAM_UBS_AM
+ u32 chan_cc = AT_XDMAC_CC_PERID(0x3f)
+ | AT_XDMAC_CC_DAM_UBS_AM
| AT_XDMAC_CC_SAM_INCREMENTED_AM
| AT_XDMAC_CC_DIF(0)
| AT_XDMAC_CC_SIF(0)
@@ -1333,7 +1348,7 @@ at_xdmac_prep_dma_memset_sg(struct dma_chan *chan, struct scatterlist *sgl,
* since we don't care about the stride anymore.
*/
if ((i == (sg_len - 1)) &&
- sg_dma_len(ppsg) == sg_dma_len(psg)) {
+ sg_dma_len(psg) == sg_dma_len(sg)) {
dev_dbg(chan2dev(chan),
"%s: desc 0x%p can be merged with desc 0x%p\n",
__func__, desc, pdesc);
@@ -1995,8 +2010,6 @@ static int at_xdmac_remove(struct platform_device *pdev)
dma_async_device_unregister(&atxdmac->dma);
clk_disable_unprepare(atxdmac->clk);
- synchronize_irq(atxdmac->irq);
-
free_irq(atxdmac->irq, atxdmac->dma.dev);
for (i = 0; i < atxdmac->dma.chancnt; i++) {
diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c
index c92d6a70ccf3..996c4b00d323 100644
--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -31,6 +31,7 @@
*/
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -62,6 +63,11 @@ struct bcm2835_dma_cb {
uint32_t pad[2];
};
+struct bcm2835_cb_entry {
+ struct bcm2835_dma_cb *cb;
+ dma_addr_t paddr;
+};
+
struct bcm2835_chan {
struct virt_dma_chan vc;
struct list_head node;
@@ -72,18 +78,18 @@ struct bcm2835_chan {
int ch;
struct bcm2835_desc *desc;
+ struct dma_pool *cb_pool;
void __iomem *chan_base;
int irq_number;
};
struct bcm2835_desc {
+ struct bcm2835_chan *c;
struct virt_dma_desc vd;
enum dma_transfer_direction dir;
- unsigned int control_block_size;
- struct bcm2835_dma_cb *control_block_base;
- dma_addr_t control_block_base_phys;
+ struct bcm2835_cb_entry *cb_list;
unsigned int frames;
size_t size;
@@ -143,10 +149,13 @@ static inline struct bcm2835_desc *to_bcm2835_dma_desc(
static void bcm2835_dma_desc_free(struct virt_dma_desc *vd)
{
struct bcm2835_desc *desc = container_of(vd, struct bcm2835_desc, vd);
- dma_free_coherent(desc->vd.tx.chan->device->dev,
- desc->control_block_size,
- desc->control_block_base,
- desc->control_block_base_phys);
+ int i;
+
+ for (i = 0; i < desc->frames; i++)
+ dma_pool_free(desc->c->cb_pool, desc->cb_list[i].cb,
+ desc->cb_list[i].paddr);
+
+ kfree(desc->cb_list);
kfree(desc);
}
@@ -199,7 +208,7 @@ static void bcm2835_dma_start_desc(struct bcm2835_chan *c)
c->desc = d = to_bcm2835_dma_desc(&vd->tx);
- writel(d->control_block_base_phys, c->chan_base + BCM2835_DMA_ADDR);
+ writel(d->cb_list[0].paddr, c->chan_base + BCM2835_DMA_ADDR);
writel(BCM2835_DMA_ACTIVE, c->chan_base + BCM2835_DMA_CS);
}
@@ -232,9 +241,16 @@ static irqreturn_t bcm2835_dma_callback(int irq, void *data)
static int bcm2835_dma_alloc_chan_resources(struct dma_chan *chan)
{
struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
+ struct device *dev = c->vc.chan.device->dev;
+
+ dev_dbg(dev, "Allocating DMA channel %d\n", c->ch);
- dev_dbg(c->vc.chan.device->dev,
- "Allocating DMA channel %d\n", c->ch);
+ c->cb_pool = dma_pool_create(dev_name(dev), dev,
+ sizeof(struct bcm2835_dma_cb), 0, 0);
+ if (!c->cb_pool) {
+ dev_err(dev, "unable to allocate descriptor pool\n");
+ return -ENOMEM;
+ }
return request_irq(c->irq_number,
bcm2835_dma_callback, 0, "DMA IRQ", c);
@@ -246,6 +262,7 @@ static void bcm2835_dma_free_chan_resources(struct dma_chan *chan)
vchan_free_chan_resources(&c->vc);
free_irq(c->irq_number, c);
+ dma_pool_destroy(c->cb_pool);
dev_dbg(c->vc.chan.device->dev, "Freeing DMA channel %u\n", c->ch);
}
@@ -261,8 +278,7 @@ static size_t bcm2835_dma_desc_size_pos(struct bcm2835_desc *d, dma_addr_t addr)
size_t size;
for (size = i = 0; i < d->frames; i++) {
- struct bcm2835_dma_cb *control_block =
- &d->control_block_base[i];
+ struct bcm2835_dma_cb *control_block = d->cb_list[i].cb;
size_t this_size = control_block->length;
dma_addr_t dma;
@@ -343,6 +359,7 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
dma_addr_t dev_addr;
unsigned int es, sync_type;
unsigned int frame;
+ int i;
/* Grab configuration */
if (!is_slave_direction(direction)) {
@@ -374,27 +391,31 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
if (!d)
return NULL;
+ d->c = c;
d->dir = direction;
d->frames = buf_len / period_len;
- /* Allocate memory for control blocks */
- d->control_block_size = d->frames * sizeof(struct bcm2835_dma_cb);
- d->control_block_base = dma_zalloc_coherent(chan->device->dev,
- d->control_block_size, &d->control_block_base_phys,
- GFP_NOWAIT);
-
- if (!d->control_block_base) {
+ d->cb_list = kcalloc(d->frames, sizeof(*d->cb_list), GFP_KERNEL);
+ if (!d->cb_list) {
kfree(d);
return NULL;
}
+ /* Allocate memory for control blocks */
+ for (i = 0; i < d->frames; i++) {
+ struct bcm2835_cb_entry *cb_entry = &d->cb_list[i];
+
+ cb_entry->cb = dma_pool_zalloc(c->cb_pool, GFP_ATOMIC,
+ &cb_entry->paddr);
+ if (!cb_entry->cb)
+ goto error_cb;
+ }
/*
* Iterate over all frames, create a control block
* for each frame and link them together.
*/
for (frame = 0; frame < d->frames; frame++) {
- struct bcm2835_dma_cb *control_block =
- &d->control_block_base[frame];
+ struct bcm2835_dma_cb *control_block = d->cb_list[frame].cb;
/* Setup adresses */
if (d->dir == DMA_DEV_TO_MEM) {
@@ -428,12 +449,21 @@ static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
* This DMA engine driver currently only supports cyclic DMA.
* Therefore, wrap around at number of frames.
*/
- control_block->next = d->control_block_base_phys +
- sizeof(struct bcm2835_dma_cb)
- * ((frame + 1) % d->frames);
+ control_block->next = d->cb_list[((frame + 1) % d->frames)].paddr;
}
return vchan_tx_prep(&c->vc, &d->vd, flags);
+error_cb:
+ i--;
+ for (; i >= 0; i--) {
+ struct bcm2835_cb_entry *cb_entry = &d->cb_list[i];
+
+ dma_pool_free(c->cb_pool, cb_entry->cb, cb_entry->paddr);
+ }
+
+ kfree(d->cb_list);
+ kfree(d);
+ return NULL;
}
static int bcm2835_dma_slave_config(struct dma_chan *chan,
diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c
index 5b2395e7e04d..c3468094393e 100644
--- a/drivers/dma/dma-axi-dmac.c
+++ b/drivers/dma/dma-axi-dmac.c
@@ -307,6 +307,13 @@ static int axi_dmac_terminate_all(struct dma_chan *c)
return 0;
}
+static void axi_dmac_synchronize(struct dma_chan *c)
+{
+ struct axi_dmac_chan *chan = to_axi_dmac_chan(c);
+
+ vchan_synchronize(&chan->vchan);
+}
+
static void axi_dmac_issue_pending(struct dma_chan *c)
{
struct axi_dmac_chan *chan = to_axi_dmac_chan(c);
@@ -613,6 +620,7 @@ static int axi_dmac_probe(struct platform_device *pdev)
dma_dev->device_prep_dma_cyclic = axi_dmac_prep_dma_cyclic;
dma_dev->device_prep_interleaved_dma = axi_dmac_prep_interleaved;
dma_dev->device_terminate_all = axi_dmac_terminate_all;
+ dma_dev->device_synchronize = axi_dmac_synchronize;
dma_dev->dev = &pdev->dev;
dma_dev->chancnt = 1;
dma_dev->src_addr_widths = BIT(dmac->chan.src_width);
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index 3ecec1445adf..c50a247be2e0 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -43,6 +43,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/module.h>
@@ -265,8 +266,11 @@ static void dma_chan_put(struct dma_chan *chan)
module_put(dma_chan_to_owner(chan));
/* This channel is not in use anymore, free it */
- if (!chan->client_count && chan->device->device_free_chan_resources)
+ if (!chan->client_count && chan->device->device_free_chan_resources) {
+ /* Make sure all operations have completed */
+ dmaengine_synchronize(chan);
chan->device->device_free_chan_resources(chan);
+ }
/* If the channel is used via a DMA request router, free the mapping */
if (chan->router && chan->router->route_free) {
@@ -493,6 +497,7 @@ int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps)
caps->dst_addr_widths = device->dst_addr_widths;
caps->directions = device->directions;
caps->residue_granularity = device->residue_granularity;
+ caps->descriptor_reuse = device->descriptor_reuse;
/*
* Some devices implement only pause (e.g. to get residuum) but no
@@ -511,7 +516,7 @@ static struct dma_chan *private_candidate(const dma_cap_mask_t *mask,
{
struct dma_chan *chan;
- if (!__dma_device_satisfies_mask(dev, mask)) {
+ if (mask && !__dma_device_satisfies_mask(dev, mask)) {
pr_debug("%s: wrong capabilities\n", __func__);
return NULL;
}
@@ -542,6 +547,42 @@ static struct dma_chan *private_candidate(const dma_cap_mask_t *mask,
return NULL;
}
+static struct dma_chan *find_candidate(struct dma_device *device,
+ const dma_cap_mask_t *mask,
+ dma_filter_fn fn, void *fn_param)
+{
+ struct dma_chan *chan = private_candidate(mask, device, fn, fn_param);
+ int err;
+
+ if (chan) {
+ /* Found a suitable channel, try to grab, prep, and return it.
+ * We first set DMA_PRIVATE to disable balance_ref_count as this
+ * channel will not be published in the general-purpose
+ * allocator
+ */
+ dma_cap_set(DMA_PRIVATE, device->cap_mask);
+ device->privatecnt++;
+ err = dma_chan_get(chan);
+
+ if (err) {
+ if (err == -ENODEV) {
+ pr_debug("%s: %s module removed\n", __func__,
+ dma_chan_name(chan));
+ list_del_rcu(&device->global_node);
+ } else
+ pr_debug("%s: failed to get %s: (%d)\n",
+ __func__, dma_chan_name(chan), err);
+
+ if (--device->privatecnt == 0)
+ dma_cap_clear(DMA_PRIVATE, device->cap_mask);
+
+ chan = ERR_PTR(err);
+ }
+ }
+
+ return chan ? chan : ERR_PTR(-EPROBE_DEFER);
+}
+
/**
* dma_get_slave_channel - try to get specific channel exclusively
* @chan: target channel
@@ -580,7 +621,6 @@ struct dma_chan *dma_get_any_slave_channel(struct dma_device *device)
{
dma_cap_mask_t mask;
struct dma_chan *chan;
- int err;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
@@ -588,23 +628,11 @@ struct dma_chan *dma_get_any_slave_channel(struct dma_device *device)
/* lock against __dma_request_channel */
mutex_lock(&dma_list_mutex);
- chan = private_candidate(&mask, device, NULL, NULL);
- if (chan) {
- dma_cap_set(DMA_PRIVATE, device->cap_mask);
- device->privatecnt++;
- err = dma_chan_get(chan);
- if (err) {
- pr_debug("%s: failed to get %s: (%d)\n",
- __func__, dma_chan_name(chan), err);
- chan = NULL;
- if (--device->privatecnt == 0)
- dma_cap_clear(DMA_PRIVATE, device->cap_mask);
- }
- }
+ chan = find_candidate(device, &mask, NULL, NULL);
mutex_unlock(&dma_list_mutex);
- return chan;
+ return IS_ERR(chan) ? NULL : chan;
}
EXPORT_SYMBOL_GPL(dma_get_any_slave_channel);
@@ -621,35 +649,15 @@ struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask,
{
struct dma_device *device, *_d;
struct dma_chan *chan = NULL;
- int err;
/* Find a channel */
mutex_lock(&dma_list_mutex);
list_for_each_entry_safe(device, _d, &dma_device_list, global_node) {
- chan = private_candidate(mask, device, fn, fn_param);
- if (chan) {
- /* Found a suitable channel, try to grab, prep, and
- * return it. We first set DMA_PRIVATE to disable
- * balance_ref_count as this channel will not be
- * published in the general-purpose allocator
- */
- dma_cap_set(DMA_PRIVATE, device->cap_mask);
- device->privatecnt++;
- err = dma_chan_get(chan);
+ chan = find_candidate(device, mask, fn, fn_param);
+ if (!IS_ERR(chan))
+ break;
- if (err == -ENODEV) {
- pr_debug("%s: %s module removed\n",
- __func__, dma_chan_name(chan));
- list_del_rcu(&device->global_node);
- } else if (err)
- pr_debug("%s: failed to get %s: (%d)\n",
- __func__, dma_chan_name(chan), err);
- else
- break;
- if (--device->privatecnt == 0)
- dma_cap_clear(DMA_PRIVATE, device->cap_mask);
- chan = NULL;
- }
+ chan = NULL;
}
mutex_unlock(&dma_list_mutex);
@@ -662,27 +670,73 @@ struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask,
}
EXPORT_SYMBOL_GPL(__dma_request_channel);
+static const struct dma_slave_map *dma_filter_match(struct dma_device *device,
+ const char *name,
+ struct device *dev)
+{
+ int i;
+
+ if (!device->filter.mapcnt)
+ return NULL;
+
+ for (i = 0; i < device->filter.mapcnt; i++) {
+ const struct dma_slave_map *map = &device->filter.map[i];
+
+ if (!strcmp(map->devname, dev_name(dev)) &&
+ !strcmp(map->slave, name))
+ return map;
+ }
+
+ return NULL;
+}
+
/**
- * dma_request_slave_channel_reason - try to allocate an exclusive slave channel
+ * dma_request_chan - try to allocate an exclusive slave channel
* @dev: pointer to client device structure
* @name: slave channel name
*
* Returns pointer to appropriate DMA channel on success or an error pointer.
*/
-struct dma_chan *dma_request_slave_channel_reason(struct device *dev,
- const char *name)
+struct dma_chan *dma_request_chan(struct device *dev, const char *name)
{
+ struct dma_device *d, *_d;
+ struct dma_chan *chan = NULL;
+
/* If device-tree is present get slave info from here */
if (dev->of_node)
- return of_dma_request_slave_channel(dev->of_node, name);
+ chan = of_dma_request_slave_channel(dev->of_node, name);
/* If device was enumerated by ACPI get slave info from here */
- if (ACPI_HANDLE(dev))
- return acpi_dma_request_slave_chan_by_name(dev, name);
+ if (has_acpi_companion(dev) && !chan)
+ chan = acpi_dma_request_slave_chan_by_name(dev, name);
+
+ if (chan) {
+ /* Valid channel found or requester need to be deferred */
+ if (!IS_ERR(chan) || PTR_ERR(chan) == -EPROBE_DEFER)
+ return chan;
+ }
- return ERR_PTR(-ENODEV);
+ /* Try to find the channel via the DMA filter map(s) */
+ mutex_lock(&dma_list_mutex);
+ list_for_each_entry_safe(d, _d, &dma_device_list, global_node) {
+ dma_cap_mask_t mask;
+ const struct dma_slave_map *map = dma_filter_match(d, name, dev);
+
+ if (!map)
+ continue;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ chan = find_candidate(d, &mask, d->filter.fn, map->param);
+ if (!IS_ERR(chan))
+ break;
+ }
+ mutex_unlock(&dma_list_mutex);
+
+ return chan ? chan : ERR_PTR(-EPROBE_DEFER);
}
-EXPORT_SYMBOL_GPL(dma_request_slave_channel_reason);
+EXPORT_SYMBOL_GPL(dma_request_chan);
/**
* dma_request_slave_channel - try to allocate an exclusive slave channel
@@ -694,17 +748,35 @@ EXPORT_SYMBOL_GPL(dma_request_slave_channel_reason);
struct dma_chan *dma_request_slave_channel(struct device *dev,
const char *name)
{
- struct dma_chan *ch = dma_request_slave_channel_reason(dev, name);
+ struct dma_chan *ch = dma_request_chan(dev, name);
if (IS_ERR(ch))
return NULL;
- dma_cap_set(DMA_PRIVATE, ch->device->cap_mask);
- ch->device->privatecnt++;
-
return ch;
}
EXPORT_SYMBOL_GPL(dma_request_slave_channel);
+/**
+ * dma_request_chan_by_mask - allocate a channel satisfying certain capabilities
+ * @mask: capabilities that the channel must satisfy
+ *
+ * Returns pointer to appropriate DMA channel on success or an error pointer.
+ */
+struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask)
+{
+ struct dma_chan *chan;
+
+ if (!mask)
+ return ERR_PTR(-ENODEV);
+
+ chan = __dma_request_channel(mask, NULL, NULL);
+ if (!chan)
+ chan = ERR_PTR(-ENODEV);
+
+ return chan;
+}
+EXPORT_SYMBOL_GPL(dma_request_chan_by_mask);
+
void dma_release_channel(struct dma_chan *chan)
{
mutex_lock(&dma_list_mutex);
diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c
index 7067b6ddc1db..8b20930ade98 100644
--- a/drivers/dma/dw/core.c
+++ b/drivers/dma/dw/core.c
@@ -622,12 +622,17 @@ static void dw_dma_tasklet(unsigned long data)
static irqreturn_t dw_dma_interrupt(int irq, void *dev_id)
{
struct dw_dma *dw = dev_id;
- u32 status = dma_readl(dw, STATUS_INT);
+ u32 status;
+ /* Check if we have any interrupt from the DMAC which is not in use */
+ if (!dw->in_use)
+ return IRQ_NONE;
+
+ status = dma_readl(dw, STATUS_INT);
dev_vdbg(dw->dma.dev, "%s: status=0x%x\n", __func__, status);
/* Check if we have any interrupt from the DMAC */
- if (!status || !dw->in_use)
+ if (!status)
return IRQ_NONE;
/*
diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c
index 68a4815750b5..26edbe3a27ac 100644
--- a/drivers/dma/dw/platform.c
+++ b/drivers/dma/dw/platform.c
@@ -103,18 +103,21 @@ dw_dma_parse_dt(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
struct dw_dma_platform_data *pdata;
u32 tmp, arr[DW_DMA_MAX_NR_MASTERS];
+ u32 nr_channels;
if (!np) {
dev_err(&pdev->dev, "Missing DT data\n");
return NULL;
}
+ if (of_property_read_u32(np, "dma-channels", &nr_channels))
+ return NULL;
+
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return NULL;
- if (of_property_read_u32(np, "dma-channels", &pdata->nr_channels))
- return NULL;
+ pdata->nr_channels = nr_channels;
if (of_property_read_bool(np, "is_private"))
pdata->is_private = true;
@@ -155,7 +158,6 @@ static int dw_probe(struct platform_device *pdev)
struct dw_dma_chip *chip;
struct device *dev = &pdev->dev;
struct resource *mem;
- const struct acpi_device_id *id;
struct dw_dma_platform_data *pdata;
int err;
@@ -179,11 +181,6 @@ static int dw_probe(struct platform_device *pdev)
pdata = dev_get_platdata(dev);
if (!pdata)
pdata = dw_dma_parse_dt(pdev);
- if (!pdata && has_acpi_companion(dev)) {
- id = acpi_match_device(dev->driver->acpi_match_table, dev);
- if (id)
- pdata = (struct dw_dma_platform_data *)id->driver_data;
- }
chip->dev = dev;
@@ -239,7 +236,19 @@ static void dw_shutdown(struct platform_device *pdev)
{
struct dw_dma_chip *chip = platform_get_drvdata(pdev);
+ /*
+ * We have to call dw_dma_disable() to stop any ongoing transfer. On
+ * some platforms we can't do that since DMA device is powered off.
+ * Moreover we have no possibility to check if the platform is affected
+ * or not. That's why we call pm_runtime_get_sync() / pm_runtime_put()
+ * unconditionally. On the other hand we can't use
+ * pm_runtime_suspended() because runtime PM framework is not fully
+ * used by the driver.
+ */
+ pm_runtime_get_sync(chip->dev);
dw_dma_disable(chip);
+ pm_runtime_put_sync_suspend(chip->dev);
+
clk_disable_unprepare(chip->clk);
}
@@ -252,17 +261,8 @@ MODULE_DEVICE_TABLE(of, dw_dma_of_id_table);
#endif
#ifdef CONFIG_ACPI
-static struct dw_dma_platform_data dw_dma_acpi_pdata = {
- .nr_channels = 8,
- .is_private = true,
- .chan_allocation_order = CHAN_ALLOCATION_ASCENDING,
- .chan_priority = CHAN_PRIORITY_ASCENDING,
- .block_size = 4095,
- .nr_masters = 2,
-};
-
static const struct acpi_device_id dw_dma_acpi_id_table[] = {
- { "INTL9C60", (kernel_ulong_t)&dw_dma_acpi_pdata },
+ { "INTL9C60", 0 },
{ }
};
MODULE_DEVICE_TABLE(acpi, dw_dma_acpi_id_table);
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c
index 0675e268d577..50584015e046 100644
--- a/drivers/dma/edma.c
+++ b/drivers/dma/edma.c
@@ -1752,16 +1752,14 @@ static enum dma_status edma_tx_status(struct dma_chan *chan,
return ret;
}
-static bool edma_is_memcpy_channel(int ch_num, u16 *memcpy_channels)
+static bool edma_is_memcpy_channel(int ch_num, s32 *memcpy_channels)
{
- s16 *memcpy_ch = memcpy_channels;
-
if (!memcpy_channels)
return false;
- while (*memcpy_ch != -1) {
- if (*memcpy_ch == ch_num)
+ while (*memcpy_channels != -1) {
+ if (*memcpy_channels == ch_num)
return true;
- memcpy_ch++;
+ memcpy_channels++;
}
return false;
}
@@ -1775,7 +1773,7 @@ static void edma_dma_init(struct edma_cc *ecc, bool legacy_mode)
{
struct dma_device *s_ddev = &ecc->dma_slave;
struct dma_device *m_ddev = NULL;
- s16 *memcpy_channels = ecc->info->memcpy_channels;
+ s32 *memcpy_channels = ecc->info->memcpy_channels;
int i, j;
dma_cap_zero(s_ddev->cap_mask);
@@ -1996,16 +1994,16 @@ static struct edma_soc_info *edma_setup_info_from_dt(struct device *dev,
prop = of_find_property(dev->of_node, "ti,edma-memcpy-channels", &sz);
if (prop) {
const char pname[] = "ti,edma-memcpy-channels";
- size_t nelm = sz / sizeof(s16);
- s16 *memcpy_ch;
+ size_t nelm = sz / sizeof(s32);
+ s32 *memcpy_ch;
- memcpy_ch = devm_kcalloc(dev, nelm + 1, sizeof(s16),
+ memcpy_ch = devm_kcalloc(dev, nelm + 1, sizeof(s32),
GFP_KERNEL);
if (!memcpy_ch)
return ERR_PTR(-ENOMEM);
- ret = of_property_read_u16_array(dev->of_node, pname,
- (u16 *)memcpy_ch, nelm);
+ ret = of_property_read_u32_array(dev->of_node, pname,
+ (u32 *)memcpy_ch, nelm);
if (ret)
return ERR_PTR(ret);
@@ -2017,31 +2015,50 @@ static struct edma_soc_info *edma_setup_info_from_dt(struct device *dev,
&sz);
if (prop) {
const char pname[] = "ti,edma-reserved-slot-ranges";
+ u32 (*tmp)[2];
s16 (*rsv_slots)[2];
- size_t nelm = sz / sizeof(*rsv_slots);
+ size_t nelm = sz / sizeof(*tmp);
struct edma_rsv_info *rsv_info;
+ int i;
if (!nelm)
return info;
+ tmp = kcalloc(nelm, sizeof(*tmp), GFP_KERNEL);
+ if (!tmp)
+ return ERR_PTR(-ENOMEM);
+
rsv_info = devm_kzalloc(dev, sizeof(*rsv_info), GFP_KERNEL);
- if (!rsv_info)
+ if (!rsv_info) {
+ kfree(tmp);
return ERR_PTR(-ENOMEM);
+ }
rsv_slots = devm_kcalloc(dev, nelm + 1, sizeof(*rsv_slots),
GFP_KERNEL);
- if (!rsv_slots)
+ if (!rsv_slots) {
+ kfree(tmp);
return ERR_PTR(-ENOMEM);
+ }
- ret = of_property_read_u16_array(dev->of_node, pname,
- (u16 *)rsv_slots, nelm * 2);
- if (ret)
+ ret = of_property_read_u32_array(dev->of_node, pname,
+ (u32 *)tmp, nelm * 2);
+ if (ret) {
+ kfree(tmp);
return ERR_PTR(ret);
+ }
+ for (i = 0; i < nelm; i++) {
+ rsv_slots[i][0] = tmp[i][0];
+ rsv_slots[i][1] = tmp[i][1];
+ }
rsv_slots[nelm][0] = -1;
rsv_slots[nelm][1] = -1;
+
info->rsv = rsv_info;
info->rsv->rsv_slots = (const s16 (*)[2])rsv_slots;
+
+ kfree(tmp);
}
return info;
@@ -2297,6 +2314,10 @@ static int edma_probe(struct platform_device *pdev)
edma_set_chmap(&ecc->slave_chans[i], ecc->dummy_slot);
}
+ ecc->dma_slave.filter.map = info->slave_map;
+ ecc->dma_slave.filter.mapcnt = info->slavecnt;
+ ecc->dma_slave.filter.fn = edma_filter_fn;
+
ret = dma_async_device_register(&ecc->dma_slave);
if (ret) {
dev_err(dev, "slave ddev registration failed (%d)\n", ret);
@@ -2404,7 +2425,13 @@ static struct platform_driver edma_driver = {
},
};
+static int edma_tptc_probe(struct platform_device *pdev)
+{
+ return 0;
+}
+
static struct platform_driver edma_tptc_driver = {
+ .probe = edma_tptc_probe,
.driver = {
.name = "edma3-tptc",
.of_match_table = edma_tptc_of_ids,
diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c
index 915eec3cc279..be2e62b87948 100644
--- a/drivers/dma/fsl-edma.c
+++ b/drivers/dma/fsl-edma.c
@@ -116,6 +116,10 @@
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES)
+enum fsl_edma_pm_state {
+ RUNNING = 0,
+ SUSPENDED,
+};
struct fsl_edma_hw_tcd {
__le32 saddr;
@@ -147,6 +151,9 @@ struct fsl_edma_slave_config {
struct fsl_edma_chan {
struct virt_dma_chan vchan;
enum dma_status status;
+ enum fsl_edma_pm_state pm_state;
+ bool idle;
+ u32 slave_id;
struct fsl_edma_engine *edma;
struct fsl_edma_desc *edesc;
struct fsl_edma_slave_config fsc;
@@ -298,6 +305,7 @@ static int fsl_edma_terminate_all(struct dma_chan *chan)
spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
fsl_edma_disable_request(fsl_chan);
fsl_chan->edesc = NULL;
+ fsl_chan->idle = true;
vchan_get_all_descriptors(&fsl_chan->vchan, &head);
spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
@@ -313,6 +321,7 @@ static int fsl_edma_pause(struct dma_chan *chan)
if (fsl_chan->edesc) {
fsl_edma_disable_request(fsl_chan);
fsl_chan->status = DMA_PAUSED;
+ fsl_chan->idle = true;
}
spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
return 0;
@@ -327,6 +336,7 @@ static int fsl_edma_resume(struct dma_chan *chan)
if (fsl_chan->edesc) {
fsl_edma_enable_request(fsl_chan);
fsl_chan->status = DMA_IN_PROGRESS;
+ fsl_chan->idle = false;
}
spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
return 0;
@@ -648,6 +658,7 @@ static void fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan)
fsl_edma_set_tcd_regs(fsl_chan, fsl_chan->edesc->tcd[0].vtcd);
fsl_edma_enable_request(fsl_chan);
fsl_chan->status = DMA_IN_PROGRESS;
+ fsl_chan->idle = false;
}
static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id)
@@ -676,6 +687,7 @@ static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id)
vchan_cookie_complete(&fsl_chan->edesc->vdesc);
fsl_chan->edesc = NULL;
fsl_chan->status = DMA_COMPLETE;
+ fsl_chan->idle = true;
} else {
vchan_cyclic_callback(&fsl_chan->edesc->vdesc);
}
@@ -704,6 +716,7 @@ static irqreturn_t fsl_edma_err_handler(int irq, void *dev_id)
edma_writeb(fsl_edma, EDMA_CERR_CERR(ch),
fsl_edma->membase + EDMA_CERR);
fsl_edma->chans[ch].status = DMA_ERROR;
+ fsl_edma->chans[ch].idle = true;
}
}
return IRQ_HANDLED;
@@ -724,6 +737,12 @@ static void fsl_edma_issue_pending(struct dma_chan *chan)
spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+ if (unlikely(fsl_chan->pm_state != RUNNING)) {
+ spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
+ /* cannot submit due to suspend */
+ return;
+ }
+
if (vchan_issue_pending(&fsl_chan->vchan) && !fsl_chan->edesc)
fsl_edma_xfer_desc(fsl_chan);
@@ -735,6 +754,7 @@ static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec,
{
struct fsl_edma_engine *fsl_edma = ofdma->of_dma_data;
struct dma_chan *chan, *_chan;
+ struct fsl_edma_chan *fsl_chan;
unsigned long chans_per_mux = fsl_edma->n_chans / DMAMUX_NR;
if (dma_spec->args_count != 2)
@@ -748,8 +768,10 @@ static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec,
chan = dma_get_slave_channel(chan);
if (chan) {
chan->device->privatecnt++;
- fsl_edma_chan_mux(to_fsl_edma_chan(chan),
- dma_spec->args[1], true);
+ fsl_chan = to_fsl_edma_chan(chan);
+ fsl_chan->slave_id = dma_spec->args[1];
+ fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id,
+ true);
mutex_unlock(&fsl_edma->fsl_edma_mutex);
return chan;
}
@@ -888,7 +910,9 @@ static int fsl_edma_probe(struct platform_device *pdev)
struct fsl_edma_chan *fsl_chan = &fsl_edma->chans[i];
fsl_chan->edma = fsl_edma;
-
+ fsl_chan->pm_state = RUNNING;
+ fsl_chan->slave_id = 0;
+ fsl_chan->idle = true;
fsl_chan->vchan.desc_free = fsl_edma_free_desc;
vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev);
@@ -959,6 +983,60 @@ static int fsl_edma_remove(struct platform_device *pdev)
return 0;
}
+static int fsl_edma_suspend_late(struct device *dev)
+{
+ struct fsl_edma_engine *fsl_edma = dev_get_drvdata(dev);
+ struct fsl_edma_chan *fsl_chan;
+ unsigned long flags;
+ int i;
+
+ for (i = 0; i < fsl_edma->n_chans; i++) {
+ fsl_chan = &fsl_edma->chans[i];
+ spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+ /* Make sure chan is idle or will force disable. */
+ if (unlikely(!fsl_chan->idle)) {
+ dev_warn(dev, "WARN: There is non-idle channel.");
+ fsl_edma_disable_request(fsl_chan);
+ fsl_edma_chan_mux(fsl_chan, 0, false);
+ }
+
+ fsl_chan->pm_state = SUSPENDED;
+ spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
+ }
+
+ return 0;
+}
+
+static int fsl_edma_resume_early(struct device *dev)
+{
+ struct fsl_edma_engine *fsl_edma = dev_get_drvdata(dev);
+ struct fsl_edma_chan *fsl_chan;
+ int i;
+
+ for (i = 0; i < fsl_edma->n_chans; i++) {
+ fsl_chan = &fsl_edma->chans[i];
+ fsl_chan->pm_state = RUNNING;
+ edma_writew(fsl_edma, 0x0, fsl_edma->membase + EDMA_TCD_CSR(i));
+ if (fsl_chan->slave_id != 0)
+ fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id, true);
+ }
+
+ edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA,
+ fsl_edma->membase + EDMA_CR);
+
+ return 0;
+}
+
+/*
+ * eDMA provides the service to others, so it should be suspend late
+ * and resume early. When eDMA suspend, all of the clients should stop
+ * the DMA data transmission and let the channel idle.
+ */
+static const struct dev_pm_ops fsl_edma_pm_ops = {
+ .suspend_late = fsl_edma_suspend_late,
+ .resume_early = fsl_edma_resume_early,
+};
+
static const struct of_device_id fsl_edma_dt_ids[] = {
{ .compatible = "fsl,vf610-edma", },
{ /* sentinel */ }
@@ -969,6 +1047,7 @@ static struct platform_driver fsl_edma_driver = {
.driver = {
.name = "fsl-edma",
.of_match_table = fsl_edma_dt_ids,
+ .pm = &fsl_edma_pm_ops,
},
.probe = fsl_edma_probe,
.remove = fsl_edma_remove,
diff --git a/drivers/dma/hsu/hsu.c b/drivers/dma/hsu/hsu.c
index 823ad728aecf..eef145edb936 100644
--- a/drivers/dma/hsu/hsu.c
+++ b/drivers/dma/hsu/hsu.c
@@ -228,6 +228,8 @@ static struct dma_async_tx_descriptor *hsu_dma_prep_slave_sg(
for_each_sg(sgl, sg, sg_len, i) {
desc->sg[i].addr = sg_dma_address(sg);
desc->sg[i].len = sg_dma_len(sg);
+
+ desc->length += sg_dma_len(sg);
}
desc->nents = sg_len;
@@ -249,21 +251,10 @@ static void hsu_dma_issue_pending(struct dma_chan *chan)
spin_unlock_irqrestore(&hsuc->vchan.lock, flags);
}
-static size_t hsu_dma_desc_size(struct hsu_dma_desc *desc)
-{
- size_t bytes = 0;
- unsigned int i;
-
- for (i = desc->active; i < desc->nents; i++)
- bytes += desc->sg[i].len;
-
- return bytes;
-}
-
static size_t hsu_dma_active_desc_size(struct hsu_dma_chan *hsuc)
{
struct hsu_dma_desc *desc = hsuc->desc;
- size_t bytes = hsu_dma_desc_size(desc);
+ size_t bytes = desc->length;
int i;
i = desc->active % HSU_DMA_CHAN_NR_DESC;
@@ -294,7 +285,7 @@ static enum dma_status hsu_dma_tx_status(struct dma_chan *chan,
dma_set_residue(state, bytes);
status = hsuc->desc->status;
} else if (vdesc) {
- bytes = hsu_dma_desc_size(to_hsu_dma_desc(vdesc));
+ bytes = to_hsu_dma_desc(vdesc)->length;
dma_set_residue(state, bytes);
}
spin_unlock_irqrestore(&hsuc->vchan.lock, flags);
diff --git a/drivers/dma/hsu/hsu.h b/drivers/dma/hsu/hsu.h
index f06579c6d548..578a8ee8cd05 100644
--- a/drivers/dma/hsu/hsu.h
+++ b/drivers/dma/hsu/hsu.h
@@ -65,6 +65,7 @@ struct hsu_dma_desc {
enum dma_transfer_direction direction;
struct hsu_dma_sg *sg;
unsigned int nents;
+ size_t length;
unsigned int active;
enum dma_status status;
};
diff --git a/drivers/dma/idma64.c b/drivers/dma/idma64.c
index 7d56b47e4fcf..3cb7b2c78197 100644
--- a/drivers/dma/idma64.c
+++ b/drivers/dma/idma64.c
@@ -178,20 +178,12 @@ static irqreturn_t idma64_irq(int irq, void *dev)
if (!status)
return IRQ_NONE;
- /* Disable interrupts */
- channel_clear_bit(idma64, MASK(XFER), idma64->all_chan_mask);
- channel_clear_bit(idma64, MASK(ERROR), idma64->all_chan_mask);
-
status_xfer = dma_readl(idma64, RAW(XFER));
status_err = dma_readl(idma64, RAW(ERROR));
for (i = 0; i < idma64->dma.chancnt; i++)
idma64_chan_irq(idma64, i, status_err, status_xfer);
- /* Re-enable interrupts */
- channel_set_bit(idma64, MASK(XFER), idma64->all_chan_mask);
- channel_set_bit(idma64, MASK(ERROR), idma64->all_chan_mask);
-
return IRQ_HANDLED;
}
@@ -239,7 +231,7 @@ static void idma64_vdesc_free(struct virt_dma_desc *vdesc)
idma64_desc_free(idma64c, to_idma64_desc(vdesc));
}
-static u64 idma64_hw_desc_fill(struct idma64_hw_desc *hw,
+static void idma64_hw_desc_fill(struct idma64_hw_desc *hw,
struct dma_slave_config *config,
enum dma_transfer_direction direction, u64 llp)
{
@@ -276,26 +268,26 @@ static u64 idma64_hw_desc_fill(struct idma64_hw_desc *hw,
IDMA64C_CTLL_SRC_WIDTH(src_width);
lli->llp = llp;
- return hw->llp;
}
static void idma64_desc_fill(struct idma64_chan *idma64c,
struct idma64_desc *desc)
{
struct dma_slave_config *config = &idma64c->config;
- struct idma64_hw_desc *hw = &desc->hw[desc->ndesc - 1];
+ unsigned int i = desc->ndesc;
+ struct idma64_hw_desc *hw = &desc->hw[i - 1];
struct idma64_lli *lli = hw->lli;
u64 llp = 0;
- unsigned int i = desc->ndesc;
/* Fill the hardware descriptors and link them to a list */
do {
hw = &desc->hw[--i];
- llp = idma64_hw_desc_fill(hw, config, desc->direction, llp);
+ idma64_hw_desc_fill(hw, config, desc->direction, llp);
+ llp = hw->llp;
desc->length += hw->len;
} while (i);
- /* Trigger interrupt after last block */
+ /* Trigger an interrupt after the last block is transfered */
lli->ctllo |= IDMA64C_CTLL_INT_EN;
}
@@ -596,6 +588,8 @@ static int idma64_probe(struct idma64_chip *chip)
idma64->dma.dev = chip->dev;
+ dma_set_max_seg_size(idma64->dma.dev, IDMA64C_CTLH_BLOCK_TS_MASK);
+
ret = dma_async_device_register(&idma64->dma);
if (ret)
return ret;
diff --git a/drivers/dma/idma64.h b/drivers/dma/idma64.h
index f6aeff0af8a5..8423f13ed0da 100644
--- a/drivers/dma/idma64.h
+++ b/drivers/dma/idma64.h
@@ -54,7 +54,8 @@
#define IDMA64C_CTLL_LLP_S_EN (1 << 28) /* src block chain */
/* Bitfields in CTL_HI */
-#define IDMA64C_CTLH_BLOCK_TS(x) ((x) & ((1 << 17) - 1))
+#define IDMA64C_CTLH_BLOCK_TS_MASK ((1 << 17) - 1)
+#define IDMA64C_CTLH_BLOCK_TS(x) ((x) & IDMA64C_CTLH_BLOCK_TS_MASK)
#define IDMA64C_CTLH_DONE (1 << 17)
/* Bitfields in CFG_LO */
diff --git a/drivers/dma/img-mdc-dma.c b/drivers/dma/img-mdc-dma.c
index 9ca56830cc63..a4c53be482cf 100644
--- a/drivers/dma/img-mdc-dma.c
+++ b/drivers/dma/img-mdc-dma.c
@@ -651,6 +651,48 @@ static enum dma_status mdc_tx_status(struct dma_chan *chan,
return ret;
}
+static unsigned int mdc_get_new_events(struct mdc_chan *mchan)
+{
+ u32 val, processed, done1, done2;
+ unsigned int ret;
+
+ val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED);
+ processed = (val >> MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT) &
+ MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK;
+ /*
+ * CMDS_DONE may have incremented between reading CMDS_PROCESSED
+ * and clearing INT_ACTIVE. Re-read CMDS_PROCESSED to ensure we
+ * didn't miss a command completion.
+ */
+ do {
+ val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED);
+
+ done1 = (val >> MDC_CMDS_PROCESSED_CMDS_DONE_SHIFT) &
+ MDC_CMDS_PROCESSED_CMDS_DONE_MASK;
+
+ val &= ~((MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK <<
+ MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT) |
+ MDC_CMDS_PROCESSED_INT_ACTIVE);
+
+ val |= done1 << MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT;
+
+ mdc_chan_writel(mchan, val, MDC_CMDS_PROCESSED);
+
+ val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED);
+
+ done2 = (val >> MDC_CMDS_PROCESSED_CMDS_DONE_SHIFT) &
+ MDC_CMDS_PROCESSED_CMDS_DONE_MASK;
+ } while (done1 != done2);
+
+ if (done1 >= processed)
+ ret = done1 - processed;
+ else
+ ret = ((MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK + 1) -
+ processed) + done1;
+
+ return ret;
+}
+
static int mdc_terminate_all(struct dma_chan *chan)
{
struct mdc_chan *mchan = to_mdc_chan(chan);
@@ -667,6 +709,8 @@ static int mdc_terminate_all(struct dma_chan *chan)
mchan->desc = NULL;
vchan_get_all_descriptors(&mchan->vc, &head);
+ mdc_get_new_events(mchan);
+
spin_unlock_irqrestore(&mchan->vc.lock, flags);
if (mdesc)
@@ -703,35 +747,17 @@ static irqreturn_t mdc_chan_irq(int irq, void *dev_id)
{
struct mdc_chan *mchan = (struct mdc_chan *)dev_id;
struct mdc_tx_desc *mdesc;
- u32 val, processed, done1, done2;
- unsigned int i;
+ unsigned int i, new_events;
spin_lock(&mchan->vc.lock);
- val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED);
- processed = (val >> MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT) &
- MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK;
- /*
- * CMDS_DONE may have incremented between reading CMDS_PROCESSED
- * and clearing INT_ACTIVE. Re-read CMDS_PROCESSED to ensure we
- * didn't miss a command completion.
- */
- do {
- val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED);
- done1 = (val >> MDC_CMDS_PROCESSED_CMDS_DONE_SHIFT) &
- MDC_CMDS_PROCESSED_CMDS_DONE_MASK;
- val &= ~((MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK <<
- MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT) |
- MDC_CMDS_PROCESSED_INT_ACTIVE);
- val |= done1 << MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT;
- mdc_chan_writel(mchan, val, MDC_CMDS_PROCESSED);
- val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED);
- done2 = (val >> MDC_CMDS_PROCESSED_CMDS_DONE_SHIFT) &
- MDC_CMDS_PROCESSED_CMDS_DONE_MASK;
- } while (done1 != done2);
-
dev_dbg(mdma2dev(mchan->mdma), "IRQ on channel %d\n", mchan->chan_nr);
+ new_events = mdc_get_new_events(mchan);
+
+ if (!new_events)
+ goto out;
+
mdesc = mchan->desc;
if (!mdesc) {
dev_warn(mdma2dev(mchan->mdma),
@@ -740,8 +766,7 @@ static irqreturn_t mdc_chan_irq(int irq, void *dev_id)
goto out;
}
- for (i = processed; i != done1;
- i = (i + 1) % (MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK + 1)) {
+ for (i = 0; i < new_events; i++) {
/*
* The first interrupt in a transfer indicates that the
* command list has been loaded, not that a command has
@@ -979,7 +1004,6 @@ static int mdc_dma_remove(struct platform_device *pdev)
vc.chan.device_node) {
list_del(&mchan->vc.chan.device_node);
- synchronize_irq(mchan->irq);
devm_free_irq(&pdev->dev, mchan->irq, mchan);
tasklet_kill(&mchan->vc.task);
diff --git a/drivers/dma/ioat/dca.c b/drivers/dma/ioat/dca.c
index 2cb7c308d5c7..0b9b6b07db9e 100644
--- a/drivers/dma/ioat/dca.c
+++ b/drivers/dma/ioat/dca.c
@@ -224,7 +224,7 @@ static u8 ioat_dca_get_tag(struct dca_provider *dca,
return tag;
}
-static struct dca_ops ioat_dca_ops = {
+static const struct dca_ops ioat_dca_ops = {
.add_requester = ioat_dca_add_requester,
.remove_requester = ioat_dca_remove_requester,
.get_tag = ioat_dca_get_tag,
diff --git a/drivers/dma/ioat/dma.h b/drivers/dma/ioat/dma.h
index 8f4e607d5817..b8f48074789f 100644
--- a/drivers/dma/ioat/dma.h
+++ b/drivers/dma/ioat/dma.h
@@ -235,43 +235,11 @@ ioat_chan_by_index(struct ioatdma_device *ioat_dma, int index)
return ioat_dma->idx[index];
}
-static inline u64 ioat_chansts_32(struct ioatdma_chan *ioat_chan)
-{
- u8 ver = ioat_chan->ioat_dma->version;
- u64 status;
- u32 status_lo;
-
- /* We need to read the low address first as this causes the
- * chipset to latch the upper bits for the subsequent read
- */
- status_lo = readl(ioat_chan->reg_base + IOAT_CHANSTS_OFFSET_LOW(ver));
- status = readl(ioat_chan->reg_base + IOAT_CHANSTS_OFFSET_HIGH(ver));
- status <<= 32;
- status |= status_lo;
-
- return status;
-}
-
-#if BITS_PER_LONG == 64
-
static inline u64 ioat_chansts(struct ioatdma_chan *ioat_chan)
{
- u8 ver = ioat_chan->ioat_dma->version;
- u64 status;
-
- /* With IOAT v3.3 the status register is 64bit. */
- if (ver >= IOAT_VER_3_3)
- status = readq(ioat_chan->reg_base + IOAT_CHANSTS_OFFSET(ver));
- else
- status = ioat_chansts_32(ioat_chan);
-
- return status;
+ return readq(ioat_chan->reg_base + IOAT_CHANSTS_OFFSET);
}
-#else
-#define ioat_chansts ioat_chansts_32
-#endif
-
static inline u64 ioat_chansts_to_addr(u64 status)
{
return status & IOAT_CHANSTS_COMPLETED_DESCRIPTOR_ADDR;
diff --git a/drivers/dma/ioat/registers.h b/drivers/dma/ioat/registers.h
index 909352f74c89..4994a3623aee 100644
--- a/drivers/dma/ioat/registers.h
+++ b/drivers/dma/ioat/registers.h
@@ -99,19 +99,9 @@
#define IOAT_DMA_COMP_V1 0x0001 /* Compatibility with DMA version 1 */
#define IOAT_DMA_COMP_V2 0x0002 /* Compatibility with DMA version 2 */
-
-#define IOAT1_CHANSTS_OFFSET 0x04 /* 64-bit Channel Status Register */
-#define IOAT2_CHANSTS_OFFSET 0x08 /* 64-bit Channel Status Register */
-#define IOAT_CHANSTS_OFFSET(ver) ((ver) < IOAT_VER_2_0 \
- ? IOAT1_CHANSTS_OFFSET : IOAT2_CHANSTS_OFFSET)
-#define IOAT1_CHANSTS_OFFSET_LOW 0x04
-#define IOAT2_CHANSTS_OFFSET_LOW 0x08
-#define IOAT_CHANSTS_OFFSET_LOW(ver) ((ver) < IOAT_VER_2_0 \
- ? IOAT1_CHANSTS_OFFSET_LOW : IOAT2_CHANSTS_OFFSET_LOW)
-#define IOAT1_CHANSTS_OFFSET_HIGH 0x08
-#define IOAT2_CHANSTS_OFFSET_HIGH 0x0C
-#define IOAT_CHANSTS_OFFSET_HIGH(ver) ((ver) < IOAT_VER_2_0 \
- ? IOAT1_CHANSTS_OFFSET_HIGH : IOAT2_CHANSTS_OFFSET_HIGH)
+/* IOAT1 define left for i7300_idle driver to not fail compiling */
+#define IOAT1_CHANSTS_OFFSET 0x04
+#define IOAT_CHANSTS_OFFSET 0x08 /* 64-bit Channel Status Register */
#define IOAT_CHANSTS_COMPLETED_DESCRIPTOR_ADDR (~0x3fULL)
#define IOAT_CHANSTS_SOFT_ERR 0x10ULL
#define IOAT_CHANSTS_UNAFFILIATED_ERR 0x8ULL
diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c
index 1c2de9a834a9..14091f878f80 100644
--- a/drivers/dma/mv_xor.c
+++ b/drivers/dma/mv_xor.c
@@ -139,46 +139,10 @@ static void mv_chan_clear_err_status(struct mv_xor_chan *chan)
}
static void mv_chan_set_mode(struct mv_xor_chan *chan,
- enum dma_transaction_type type)
+ u32 op_mode)
{
- u32 op_mode;
u32 config = readl_relaxed(XOR_CONFIG(chan));
- switch (type) {
- case DMA_XOR:
- op_mode = XOR_OPERATION_MODE_XOR;
- break;
- case DMA_MEMCPY:
- op_mode = XOR_OPERATION_MODE_MEMCPY;
- break;
- default:
- dev_err(mv_chan_to_devp(chan),
- "error: unsupported operation %d\n",
- type);
- BUG();
- return;
- }
-
- config &= ~0x7;
- config |= op_mode;
-
-#if defined(__BIG_ENDIAN)
- config |= XOR_DESCRIPTOR_SWAP;
-#else
- config &= ~XOR_DESCRIPTOR_SWAP;
-#endif
-
- writel_relaxed(config, XOR_CONFIG(chan));
- chan->current_type = type;
-}
-
-static void mv_chan_set_mode_to_desc(struct mv_xor_chan *chan)
-{
- u32 op_mode;
- u32 config = readl_relaxed(XOR_CONFIG(chan));
-
- op_mode = XOR_OPERATION_MODE_IN_DESC;
-
config &= ~0x7;
config |= op_mode;
@@ -1043,9 +1007,9 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
mv_chan_unmask_interrupts(mv_chan);
if (mv_chan->op_in_desc == XOR_MODE_IN_DESC)
- mv_chan_set_mode_to_desc(mv_chan);
+ mv_chan_set_mode(mv_chan, XOR_OPERATION_MODE_IN_DESC);
else
- mv_chan_set_mode(mv_chan, DMA_XOR);
+ mv_chan_set_mode(mv_chan, XOR_OPERATION_MODE_XOR);
spin_lock_init(&mv_chan->lock);
INIT_LIST_HEAD(&mv_chan->chain);
@@ -1121,6 +1085,57 @@ mv_xor_conf_mbus_windows(struct mv_xor_device *xordev,
writel(0, base + WINDOW_OVERRIDE_CTRL(1));
}
+/*
+ * Since this XOR driver is basically used only for RAID5, we don't
+ * need to care about synchronizing ->suspend with DMA activity,
+ * because the DMA engine will naturally be quiet due to the block
+ * devices being suspended.
+ */
+static int mv_xor_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mv_xor_device *xordev = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < MV_XOR_MAX_CHANNELS; i++) {
+ struct mv_xor_chan *mv_chan = xordev->channels[i];
+
+ if (!mv_chan)
+ continue;
+
+ mv_chan->saved_config_reg =
+ readl_relaxed(XOR_CONFIG(mv_chan));
+ mv_chan->saved_int_mask_reg =
+ readl_relaxed(XOR_INTR_MASK(mv_chan));
+ }
+
+ return 0;
+}
+
+static int mv_xor_resume(struct platform_device *dev)
+{
+ struct mv_xor_device *xordev = platform_get_drvdata(dev);
+ const struct mbus_dram_target_info *dram;
+ int i;
+
+ for (i = 0; i < MV_XOR_MAX_CHANNELS; i++) {
+ struct mv_xor_chan *mv_chan = xordev->channels[i];
+
+ if (!mv_chan)
+ continue;
+
+ writel_relaxed(mv_chan->saved_config_reg,
+ XOR_CONFIG(mv_chan));
+ writel_relaxed(mv_chan->saved_int_mask_reg,
+ XOR_INTR_MASK(mv_chan));
+ }
+
+ dram = mv_mbus_dram_info();
+ if (dram)
+ mv_xor_conf_mbus_windows(xordev, dram);
+
+ return 0;
+}
+
static const struct of_device_id mv_xor_dt_ids[] = {
{ .compatible = "marvell,orion-xor", .data = (void *)XOR_MODE_IN_REG },
{ .compatible = "marvell,armada-380-xor", .data = (void *)XOR_MODE_IN_DESC },
@@ -1282,6 +1297,8 @@ err_channel_add:
static struct platform_driver mv_xor_driver = {
.probe = mv_xor_probe,
+ .suspend = mv_xor_suspend,
+ .resume = mv_xor_resume,
.driver = {
.name = MV_XOR_NAME,
.of_match_table = of_match_ptr(mv_xor_dt_ids),
diff --git a/drivers/dma/mv_xor.h b/drivers/dma/mv_xor.h
index b7455b42137b..c19fe30e5ae9 100644
--- a/drivers/dma/mv_xor.h
+++ b/drivers/dma/mv_xor.h
@@ -110,7 +110,6 @@ struct mv_xor_chan {
void __iomem *mmr_high_base;
unsigned int idx;
int irq;
- enum dma_transaction_type current_type;
struct list_head chain;
struct list_head free_slots;
struct list_head allocated_slots;
@@ -126,6 +125,7 @@ struct mv_xor_chan {
char dummy_src[MV_XOR_MIN_BYTE_COUNT];
char dummy_dst[MV_XOR_MIN_BYTE_COUNT];
dma_addr_t dummy_src_addr, dummy_dst_addr;
+ u32 saved_config_reg, saved_int_mask_reg;
};
/**
diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c
index 1dfc71c90123..9794b073d7d7 100644
--- a/drivers/dma/omap-dma.c
+++ b/drivers/dma/omap-dma.c
@@ -28,8 +28,6 @@
struct omap_dmadev {
struct dma_device ddev;
spinlock_t lock;
- struct tasklet_struct task;
- struct list_head pending;
void __iomem *base;
const struct omap_dma_reg *reg_map;
struct omap_system_dma_plat_info *plat;
@@ -42,7 +40,6 @@ struct omap_dmadev {
struct omap_chan {
struct virt_dma_chan vc;
- struct list_head node;
void __iomem *channel_base;
const struct omap_dma_reg *reg_map;
uint32_t ccr;
@@ -454,33 +451,6 @@ static void omap_dma_callback(int ch, u16 status, void *data)
spin_unlock_irqrestore(&c->vc.lock, flags);
}
-/*
- * This callback schedules all pending channels. We could be more
- * clever here by postponing allocation of the real DMA channels to
- * this point, and freeing them when our virtual channel becomes idle.
- *
- * We would then need to deal with 'all channels in-use'
- */
-static void omap_dma_sched(unsigned long data)
-{
- struct omap_dmadev *d = (struct omap_dmadev *)data;
- LIST_HEAD(head);
-
- spin_lock_irq(&d->lock);
- list_splice_tail_init(&d->pending, &head);
- spin_unlock_irq(&d->lock);
-
- while (!list_empty(&head)) {
- struct omap_chan *c = list_first_entry(&head,
- struct omap_chan, node);
-
- spin_lock_irq(&c->vc.lock);
- list_del_init(&c->node);
- omap_dma_start_desc(c);
- spin_unlock_irq(&c->vc.lock);
- }
-}
-
static irqreturn_t omap_dma_irq(int irq, void *devid)
{
struct omap_dmadev *od = devid;
@@ -703,8 +673,14 @@ static enum dma_status omap_dma_tx_status(struct dma_chan *chan,
struct omap_chan *c = to_omap_dma_chan(chan);
struct virt_dma_desc *vd;
enum dma_status ret;
+ uint32_t ccr;
unsigned long flags;
+ ccr = omap_dma_chan_read(c, CCR);
+ /* The channel is no longer active, handle the completion right away */
+ if (!(ccr & CCR_ENABLE))
+ omap_dma_callback(c->dma_ch, 0, c);
+
ret = dma_cookie_status(chan, cookie, txstate);
if (ret == DMA_COMPLETE || !txstate)
return ret;
@@ -719,7 +695,7 @@ static enum dma_status omap_dma_tx_status(struct dma_chan *chan,
if (d->dir == DMA_MEM_TO_DEV)
pos = omap_dma_get_src_pos(c);
- else if (d->dir == DMA_DEV_TO_MEM)
+ else if (d->dir == DMA_DEV_TO_MEM || d->dir == DMA_MEM_TO_MEM)
pos = omap_dma_get_dst_pos(c);
else
pos = 0;
@@ -739,22 +715,8 @@ static void omap_dma_issue_pending(struct dma_chan *chan)
unsigned long flags;
spin_lock_irqsave(&c->vc.lock, flags);
- if (vchan_issue_pending(&c->vc) && !c->desc) {
- /*
- * c->cyclic is used only by audio and in this case the DMA need
- * to be started without delay.
- */
- if (!c->cyclic) {
- struct omap_dmadev *d = to_omap_dma_dev(chan->device);
- spin_lock(&d->lock);
- if (list_empty(&c->node))
- list_add_tail(&c->node, &d->pending);
- spin_unlock(&d->lock);
- tasklet_schedule(&d->task);
- } else {
- omap_dma_start_desc(c);
- }
- }
+ if (vchan_issue_pending(&c->vc) && !c->desc)
+ omap_dma_start_desc(c);
spin_unlock_irqrestore(&c->vc.lock, flags);
}
@@ -768,7 +730,7 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
struct scatterlist *sgent;
struct omap_desc *d;
dma_addr_t dev_addr;
- unsigned i, j = 0, es, en, frame_bytes;
+ unsigned i, es, en, frame_bytes;
u32 burst;
if (dir == DMA_DEV_TO_MEM) {
@@ -845,13 +807,12 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
en = burst;
frame_bytes = es_bytes[es] * en;
for_each_sg(sgl, sgent, sglen, i) {
- d->sg[j].addr = sg_dma_address(sgent);
- d->sg[j].en = en;
- d->sg[j].fn = sg_dma_len(sgent) / frame_bytes;
- j++;
+ d->sg[i].addr = sg_dma_address(sgent);
+ d->sg[i].en = en;
+ d->sg[i].fn = sg_dma_len(sgent) / frame_bytes;
}
- d->sglen = j;
+ d->sglen = sglen;
return vchan_tx_prep(&c->vc, &d->vd, tx_flags);
}
@@ -1018,17 +979,11 @@ static int omap_dma_slave_config(struct dma_chan *chan, struct dma_slave_config
static int omap_dma_terminate_all(struct dma_chan *chan)
{
struct omap_chan *c = to_omap_dma_chan(chan);
- struct omap_dmadev *d = to_omap_dma_dev(c->vc.chan.device);
unsigned long flags;
LIST_HEAD(head);
spin_lock_irqsave(&c->vc.lock, flags);
- /* Prevent this channel being scheduled */
- spin_lock(&d->lock);
- list_del_init(&c->node);
- spin_unlock(&d->lock);
-
/*
* Stop DMA activity: we assume the callback will not be called
* after omap_dma_stop() returns (even if it does, it will see
@@ -1102,14 +1057,12 @@ static int omap_dma_chan_init(struct omap_dmadev *od)
c->reg_map = od->reg_map;
c->vc.desc_free = omap_dma_desc_free;
vchan_init(&c->vc, &od->ddev);
- INIT_LIST_HEAD(&c->node);
return 0;
}
static void omap_dma_free(struct omap_dmadev *od)
{
- tasklet_kill(&od->task);
while (!list_empty(&od->ddev.channels)) {
struct omap_chan *c = list_first_entry(&od->ddev.channels,
struct omap_chan, vc.chan.device_node);
@@ -1165,12 +1118,9 @@ static int omap_dma_probe(struct platform_device *pdev)
od->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
od->ddev.dev = &pdev->dev;
INIT_LIST_HEAD(&od->ddev.channels);
- INIT_LIST_HEAD(&od->pending);
spin_lock_init(&od->lock);
spin_lock_init(&od->irq_lock);
- tasklet_init(&od->task, omap_dma_sched, (unsigned long)od);
-
od->dma_requests = OMAP_SDMA_REQUESTS;
if (pdev->dev.of_node && of_property_read_u32(pdev->dev.of_node,
"dma-requests",
@@ -1203,6 +1153,10 @@ static int omap_dma_probe(struct platform_device *pdev)
return rc;
}
+ od->ddev.filter.map = od->plat->slave_map;
+ od->ddev.filter.mapcnt = od->plat->slavecnt;
+ od->ddev.filter.fn = omap_dma_filter_fn;
+
rc = dma_async_device_register(&od->ddev);
if (rc) {
pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n",
diff --git a/drivers/dma/pxa_dma.c b/drivers/dma/pxa_dma.c
index fc4156afa070..f2a0310ae771 100644
--- a/drivers/dma/pxa_dma.c
+++ b/drivers/dma/pxa_dma.c
@@ -1414,6 +1414,7 @@ static int pxad_probe(struct platform_device *op)
pdev->slave.dst_addr_widths = widths;
pdev->slave.directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM);
pdev->slave.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
+ pdev->slave.descriptor_reuse = true;
pdev->slave.dev = &op->dev;
ret = pxad_init_dmadev(op, pdev, dma_channels);
diff --git a/drivers/dma/sh/Kconfig b/drivers/dma/sh/Kconfig
index 9fda65af841e..f32c430eb16c 100644
--- a/drivers/dma/sh/Kconfig
+++ b/drivers/dma/sh/Kconfig
@@ -47,12 +47,6 @@ config RCAR_DMAC
This driver supports the general purpose DMA controller found in the
Renesas R-Car second generation SoCs.
-config RCAR_HPB_DMAE
- tristate "Renesas R-Car HPB DMAC support"
- depends on SH_DMAE_BASE
- help
- Enable support for the Renesas R-Car series DMA controllers.
-
config RENESAS_USB_DMAC
tristate "Renesas USB-DMA Controller"
depends on ARCH_SHMOBILE || COMPILE_TEST
diff --git a/drivers/dma/sh/Makefile b/drivers/dma/sh/Makefile
index 0133e4658196..f1e2fd64f279 100644
--- a/drivers/dma/sh/Makefile
+++ b/drivers/dma/sh/Makefile
@@ -14,6 +14,5 @@ shdma-objs := $(shdma-y)
obj-$(CONFIG_SH_DMAE) += shdma.o
obj-$(CONFIG_RCAR_DMAC) += rcar-dmac.o
-obj-$(CONFIG_RCAR_HPB_DMAE) += rcar-hpbdma.o
obj-$(CONFIG_RENESAS_USB_DMAC) += usb-dmac.o
obj-$(CONFIG_SUDMAC) += sudmac.o
diff --git a/drivers/dma/sh/rcar-hpbdma.c b/drivers/dma/sh/rcar-hpbdma.c
deleted file mode 100644
index 749f26ecd3b3..000000000000
--- a/drivers/dma/sh/rcar-hpbdma.c
+++ /dev/null
@@ -1,669 +0,0 @@
-/*
- * Copyright (C) 2011-2013 Renesas Electronics Corporation
- * Copyright (C) 2013 Cogent Embedded, Inc.
- *
- * This file is based on the drivers/dma/sh/shdma.c
- *
- * Renesas SuperH DMA Engine support
- *
- * This 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.
- *
- * - DMA of SuperH does not have Hardware DMA chain mode.
- * - max DMA size is 16MB.
- *
- */
-
-#include <linux/dmaengine.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/platform_data/dma-rcar-hpbdma.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/shdma-base.h>
-#include <linux/slab.h>
-
-/* DMA channel registers */
-#define HPB_DMAE_DSAR0 0x00
-#define HPB_DMAE_DDAR0 0x04
-#define HPB_DMAE_DTCR0 0x08
-#define HPB_DMAE_DSAR1 0x0C
-#define HPB_DMAE_DDAR1 0x10
-#define HPB_DMAE_DTCR1 0x14
-#define HPB_DMAE_DSASR 0x18
-#define HPB_DMAE_DDASR 0x1C
-#define HPB_DMAE_DTCSR 0x20
-#define HPB_DMAE_DPTR 0x24
-#define HPB_DMAE_DCR 0x28
-#define HPB_DMAE_DCMDR 0x2C
-#define HPB_DMAE_DSTPR 0x30
-#define HPB_DMAE_DSTSR 0x34
-#define HPB_DMAE_DDBGR 0x38
-#define HPB_DMAE_DDBGR2 0x3C
-#define HPB_DMAE_CHAN(n) (0x40 * (n))
-
-/* DMA command register (DCMDR) bits */
-#define HPB_DMAE_DCMDR_BDOUT BIT(7)
-#define HPB_DMAE_DCMDR_DQSPD BIT(6)
-#define HPB_DMAE_DCMDR_DQSPC BIT(5)
-#define HPB_DMAE_DCMDR_DMSPD BIT(4)
-#define HPB_DMAE_DCMDR_DMSPC BIT(3)
-#define HPB_DMAE_DCMDR_DQEND BIT(2)
-#define HPB_DMAE_DCMDR_DNXT BIT(1)
-#define HPB_DMAE_DCMDR_DMEN BIT(0)
-
-/* DMA forced stop register (DSTPR) bits */
-#define HPB_DMAE_DSTPR_DMSTP BIT(0)
-
-/* DMA status register (DSTSR) bits */
-#define HPB_DMAE_DSTSR_DQSTS BIT(2)
-#define HPB_DMAE_DSTSR_DMSTS BIT(0)
-
-/* DMA common registers */
-#define HPB_DMAE_DTIMR 0x00
-#define HPB_DMAE_DINTSR0 0x0C
-#define HPB_DMAE_DINTSR1 0x10
-#define HPB_DMAE_DINTCR0 0x14
-#define HPB_DMAE_DINTCR1 0x18
-#define HPB_DMAE_DINTMR0 0x1C
-#define HPB_DMAE_DINTMR1 0x20
-#define HPB_DMAE_DACTSR0 0x24
-#define HPB_DMAE_DACTSR1 0x28
-#define HPB_DMAE_HSRSTR(n) (0x40 + (n) * 4)
-#define HPB_DMAE_HPB_DMASPR(n) (0x140 + (n) * 4)
-#define HPB_DMAE_HPB_DMLVLR0 0x160
-#define HPB_DMAE_HPB_DMLVLR1 0x164
-#define HPB_DMAE_HPB_DMSHPT0 0x168
-#define HPB_DMAE_HPB_DMSHPT1 0x16C
-
-#define HPB_DMA_SLAVE_NUMBER 256
-#define HPB_DMA_TCR_MAX 0x01000000 /* 16 MiB */
-
-struct hpb_dmae_chan {
- struct shdma_chan shdma_chan;
- int xfer_mode; /* DMA transfer mode */
-#define XFER_SINGLE 1
-#define XFER_DOUBLE 2
- unsigned plane_idx; /* current DMA information set */
- bool first_desc; /* first/next transfer */
- int xmit_shift; /* log_2(bytes_per_xfer) */
- void __iomem *base;
- const struct hpb_dmae_slave_config *cfg;
- char dev_id[16]; /* unique name per DMAC of channel */
- dma_addr_t slave_addr;
-};
-
-struct hpb_dmae_device {
- struct shdma_dev shdma_dev;
- spinlock_t reg_lock; /* comm_reg operation lock */
- struct hpb_dmae_pdata *pdata;
- void __iomem *chan_reg;
- void __iomem *comm_reg;
- void __iomem *reset_reg;
- void __iomem *mode_reg;
-};
-
-struct hpb_dmae_regs {
- u32 sar; /* SAR / source address */
- u32 dar; /* DAR / destination address */
- u32 tcr; /* TCR / transfer count */
-};
-
-struct hpb_desc {
- struct shdma_desc shdma_desc;
- struct hpb_dmae_regs hw;
- unsigned plane_idx;
-};
-
-#define to_chan(schan) container_of(schan, struct hpb_dmae_chan, shdma_chan)
-#define to_desc(sdesc) container_of(sdesc, struct hpb_desc, shdma_desc)
-#define to_dev(sc) container_of(sc->shdma_chan.dma_chan.device, \
- struct hpb_dmae_device, shdma_dev.dma_dev)
-
-static void ch_reg_write(struct hpb_dmae_chan *hpb_dc, u32 data, u32 reg)
-{
- iowrite32(data, hpb_dc->base + reg);
-}
-
-static u32 ch_reg_read(struct hpb_dmae_chan *hpb_dc, u32 reg)
-{
- return ioread32(hpb_dc->base + reg);
-}
-
-static void dcmdr_write(struct hpb_dmae_device *hpbdev, u32 data)
-{
- iowrite32(data, hpbdev->chan_reg + HPB_DMAE_DCMDR);
-}
-
-static void hsrstr_write(struct hpb_dmae_device *hpbdev, u32 ch)
-{
- iowrite32(0x1, hpbdev->comm_reg + HPB_DMAE_HSRSTR(ch));
-}
-
-static u32 dintsr_read(struct hpb_dmae_device *hpbdev, u32 ch)
-{
- u32 v;
-
- if (ch < 32)
- v = ioread32(hpbdev->comm_reg + HPB_DMAE_DINTSR0) >> ch;
- else
- v = ioread32(hpbdev->comm_reg + HPB_DMAE_DINTSR1) >> (ch - 32);
- return v & 0x1;
-}
-
-static void dintcr_write(struct hpb_dmae_device *hpbdev, u32 ch)
-{
- if (ch < 32)
- iowrite32((0x1 << ch), hpbdev->comm_reg + HPB_DMAE_DINTCR0);
- else
- iowrite32((0x1 << (ch - 32)),
- hpbdev->comm_reg + HPB_DMAE_DINTCR1);
-}
-
-static void asyncmdr_write(struct hpb_dmae_device *hpbdev, u32 data)
-{
- iowrite32(data, hpbdev->mode_reg);
-}
-
-static u32 asyncmdr_read(struct hpb_dmae_device *hpbdev)
-{
- return ioread32(hpbdev->mode_reg);
-}
-
-static void hpb_dmae_enable_int(struct hpb_dmae_device *hpbdev, u32 ch)
-{
- u32 intreg;
-
- spin_lock_irq(&hpbdev->reg_lock);
- if (ch < 32) {
- intreg = ioread32(hpbdev->comm_reg + HPB_DMAE_DINTMR0);
- iowrite32(BIT(ch) | intreg,
- hpbdev->comm_reg + HPB_DMAE_DINTMR0);
- } else {
- intreg = ioread32(hpbdev->comm_reg + HPB_DMAE_DINTMR1);
- iowrite32(BIT(ch - 32) | intreg,
- hpbdev->comm_reg + HPB_DMAE_DINTMR1);
- }
- spin_unlock_irq(&hpbdev->reg_lock);
-}
-
-static void hpb_dmae_async_reset(struct hpb_dmae_device *hpbdev, u32 data)
-{
- u32 rstr;
- int timeout = 10000; /* 100 ms */
-
- spin_lock(&hpbdev->reg_lock);
- rstr = ioread32(hpbdev->reset_reg);
- rstr |= data;
- iowrite32(rstr, hpbdev->reset_reg);
- do {
- rstr = ioread32(hpbdev->reset_reg);
- if ((rstr & data) == data)
- break;
- udelay(10);
- } while (timeout--);
-
- if (timeout < 0)
- dev_err(hpbdev->shdma_dev.dma_dev.dev,
- "%s timeout\n", __func__);
-
- rstr &= ~data;
- iowrite32(rstr, hpbdev->reset_reg);
- spin_unlock(&hpbdev->reg_lock);
-}
-
-static void hpb_dmae_set_async_mode(struct hpb_dmae_device *hpbdev,
- u32 mask, u32 data)
-{
- u32 mode;
-
- spin_lock_irq(&hpbdev->reg_lock);
- mode = asyncmdr_read(hpbdev);
- mode &= ~mask;
- mode |= data;
- asyncmdr_write(hpbdev, mode);
- spin_unlock_irq(&hpbdev->reg_lock);
-}
-
-static void hpb_dmae_ctl_stop(struct hpb_dmae_device *hpbdev)
-{
- dcmdr_write(hpbdev, HPB_DMAE_DCMDR_DQSPD);
-}
-
-static void hpb_dmae_reset(struct hpb_dmae_device *hpbdev)
-{
- u32 ch;
-
- for (ch = 0; ch < hpbdev->pdata->num_hw_channels; ch++)
- hsrstr_write(hpbdev, ch);
-}
-
-static unsigned int calc_xmit_shift(struct hpb_dmae_chan *hpb_chan)
-{
- struct hpb_dmae_device *hpbdev = to_dev(hpb_chan);
- struct hpb_dmae_pdata *pdata = hpbdev->pdata;
- int width = ch_reg_read(hpb_chan, HPB_DMAE_DCR);
- int i;
-
- switch (width & (HPB_DMAE_DCR_SPDS_MASK | HPB_DMAE_DCR_DPDS_MASK)) {
- case HPB_DMAE_DCR_SPDS_8BIT | HPB_DMAE_DCR_DPDS_8BIT:
- default:
- i = XMIT_SZ_8BIT;
- break;
- case HPB_DMAE_DCR_SPDS_16BIT | HPB_DMAE_DCR_DPDS_16BIT:
- i = XMIT_SZ_16BIT;
- break;
- case HPB_DMAE_DCR_SPDS_32BIT | HPB_DMAE_DCR_DPDS_32BIT:
- i = XMIT_SZ_32BIT;
- break;
- }
- return pdata->ts_shift[i];
-}
-
-static void hpb_dmae_set_reg(struct hpb_dmae_chan *hpb_chan,
- struct hpb_dmae_regs *hw, unsigned plane)
-{
- ch_reg_write(hpb_chan, hw->sar,
- plane ? HPB_DMAE_DSAR1 : HPB_DMAE_DSAR0);
- ch_reg_write(hpb_chan, hw->dar,
- plane ? HPB_DMAE_DDAR1 : HPB_DMAE_DDAR0);
- ch_reg_write(hpb_chan, hw->tcr >> hpb_chan->xmit_shift,
- plane ? HPB_DMAE_DTCR1 : HPB_DMAE_DTCR0);
-}
-
-static void hpb_dmae_start(struct hpb_dmae_chan *hpb_chan, bool next)
-{
- ch_reg_write(hpb_chan, (next ? HPB_DMAE_DCMDR_DNXT : 0) |
- HPB_DMAE_DCMDR_DMEN, HPB_DMAE_DCMDR);
-}
-
-static void hpb_dmae_halt(struct shdma_chan *schan)
-{
- struct hpb_dmae_chan *chan = to_chan(schan);
-
- ch_reg_write(chan, HPB_DMAE_DCMDR_DQEND, HPB_DMAE_DCMDR);
- ch_reg_write(chan, HPB_DMAE_DSTPR_DMSTP, HPB_DMAE_DSTPR);
-
- chan->plane_idx = 0;
- chan->first_desc = true;
-}
-
-static const struct hpb_dmae_slave_config *
-hpb_dmae_find_slave(struct hpb_dmae_chan *hpb_chan, int slave_id)
-{
- struct hpb_dmae_device *hpbdev = to_dev(hpb_chan);
- struct hpb_dmae_pdata *pdata = hpbdev->pdata;
- int i;
-
- if (slave_id >= HPB_DMA_SLAVE_NUMBER)
- return NULL;
-
- for (i = 0; i < pdata->num_slaves; i++)
- if (pdata->slaves[i].id == slave_id)
- return pdata->slaves + i;
-
- return NULL;
-}
-
-static void hpb_dmae_start_xfer(struct shdma_chan *schan,
- struct shdma_desc *sdesc)
-{
- struct hpb_dmae_chan *chan = to_chan(schan);
- struct hpb_dmae_device *hpbdev = to_dev(chan);
- struct hpb_desc *desc = to_desc(sdesc);
-
- if (chan->cfg->flags & HPB_DMAE_SET_ASYNC_RESET)
- hpb_dmae_async_reset(hpbdev, chan->cfg->rstr);
-
- desc->plane_idx = chan->plane_idx;
- hpb_dmae_set_reg(chan, &desc->hw, chan->plane_idx);
- hpb_dmae_start(chan, !chan->first_desc);
-
- if (chan->xfer_mode == XFER_DOUBLE) {
- chan->plane_idx ^= 1;
- chan->first_desc = false;
- }
-}
-
-static bool hpb_dmae_desc_completed(struct shdma_chan *schan,
- struct shdma_desc *sdesc)
-{
- /*
- * This is correct since we always have at most single
- * outstanding DMA transfer per channel, and by the time
- * we get completion interrupt the transfer is completed.
- * This will change if we ever use alternating DMA
- * information sets and submit two descriptors at once.
- */
- return true;
-}
-
-static bool hpb_dmae_chan_irq(struct shdma_chan *schan, int irq)
-{
- struct hpb_dmae_chan *chan = to_chan(schan);
- struct hpb_dmae_device *hpbdev = to_dev(chan);
- int ch = chan->cfg->dma_ch;
-
- /* Check Complete DMA Transfer */
- if (dintsr_read(hpbdev, ch)) {
- /* Clear Interrupt status */
- dintcr_write(hpbdev, ch);
- return true;
- }
- return false;
-}
-
-static int hpb_dmae_desc_setup(struct shdma_chan *schan,
- struct shdma_desc *sdesc,
- dma_addr_t src, dma_addr_t dst, size_t *len)
-{
- struct hpb_desc *desc = to_desc(sdesc);
-
- if (*len > (size_t)HPB_DMA_TCR_MAX)
- *len = (size_t)HPB_DMA_TCR_MAX;
-
- desc->hw.sar = src;
- desc->hw.dar = dst;
- desc->hw.tcr = *len;
-
- return 0;
-}
-
-static size_t hpb_dmae_get_partial(struct shdma_chan *schan,
- struct shdma_desc *sdesc)
-{
- struct hpb_desc *desc = to_desc(sdesc);
- struct hpb_dmae_chan *chan = to_chan(schan);
- u32 tcr = ch_reg_read(chan, desc->plane_idx ?
- HPB_DMAE_DTCR1 : HPB_DMAE_DTCR0);
-
- return (desc->hw.tcr - tcr) << chan->xmit_shift;
-}
-
-static bool hpb_dmae_channel_busy(struct shdma_chan *schan)
-{
- struct hpb_dmae_chan *chan = to_chan(schan);
- u32 dstsr = ch_reg_read(chan, HPB_DMAE_DSTSR);
-
- if (chan->xfer_mode == XFER_DOUBLE)
- return dstsr & HPB_DMAE_DSTSR_DQSTS;
- else
- return dstsr & HPB_DMAE_DSTSR_DMSTS;
-}
-
-static int
-hpb_dmae_alloc_chan_resources(struct hpb_dmae_chan *hpb_chan,
- const struct hpb_dmae_slave_config *cfg)
-{
- struct hpb_dmae_device *hpbdev = to_dev(hpb_chan);
- struct hpb_dmae_pdata *pdata = hpbdev->pdata;
- const struct hpb_dmae_channel *channel = pdata->channels;
- int slave_id = cfg->id;
- int i, err;
-
- for (i = 0; i < pdata->num_channels; i++, channel++) {
- if (channel->s_id == slave_id) {
- struct device *dev = hpb_chan->shdma_chan.dev;
-
- hpb_chan->base = hpbdev->chan_reg +
- HPB_DMAE_CHAN(cfg->dma_ch);
-
- dev_dbg(dev, "Detected Slave device\n");
- dev_dbg(dev, " -- slave_id : 0x%x\n", slave_id);
- dev_dbg(dev, " -- cfg->dma_ch : %d\n", cfg->dma_ch);
- dev_dbg(dev, " -- channel->ch_irq: %d\n",
- channel->ch_irq);
- break;
- }
- }
-
- err = shdma_request_irq(&hpb_chan->shdma_chan, channel->ch_irq,
- IRQF_SHARED, hpb_chan->dev_id);
- if (err) {
- dev_err(hpb_chan->shdma_chan.dev,
- "DMA channel request_irq %d failed with error %d\n",
- channel->ch_irq, err);
- return err;
- }
-
- hpb_chan->plane_idx = 0;
- hpb_chan->first_desc = true;
-
- if ((cfg->dcr & (HPB_DMAE_DCR_CT | HPB_DMAE_DCR_DIP)) == 0) {
- hpb_chan->xfer_mode = XFER_SINGLE;
- } else if ((cfg->dcr & (HPB_DMAE_DCR_CT | HPB_DMAE_DCR_DIP)) ==
- (HPB_DMAE_DCR_CT | HPB_DMAE_DCR_DIP)) {
- hpb_chan->xfer_mode = XFER_DOUBLE;
- } else {
- dev_err(hpb_chan->shdma_chan.dev, "DCR setting error");
- return -EINVAL;
- }
-
- if (cfg->flags & HPB_DMAE_SET_ASYNC_MODE)
- hpb_dmae_set_async_mode(hpbdev, cfg->mdm, cfg->mdr);
- ch_reg_write(hpb_chan, cfg->dcr, HPB_DMAE_DCR);
- ch_reg_write(hpb_chan, cfg->port, HPB_DMAE_DPTR);
- hpb_chan->xmit_shift = calc_xmit_shift(hpb_chan);
- hpb_dmae_enable_int(hpbdev, cfg->dma_ch);
-
- return 0;
-}
-
-static int hpb_dmae_set_slave(struct shdma_chan *schan, int slave_id,
- dma_addr_t slave_addr, bool try)
-{
- struct hpb_dmae_chan *chan = to_chan(schan);
- const struct hpb_dmae_slave_config *sc =
- hpb_dmae_find_slave(chan, slave_id);
-
- if (!sc)
- return -ENODEV;
- if (try)
- return 0;
- chan->cfg = sc;
- chan->slave_addr = slave_addr ? : sc->addr;
- return hpb_dmae_alloc_chan_resources(chan, sc);
-}
-
-static void hpb_dmae_setup_xfer(struct shdma_chan *schan, int slave_id)
-{
-}
-
-static dma_addr_t hpb_dmae_slave_addr(struct shdma_chan *schan)
-{
- struct hpb_dmae_chan *chan = to_chan(schan);
-
- return chan->slave_addr;
-}
-
-static struct shdma_desc *hpb_dmae_embedded_desc(void *buf, int i)
-{
- return &((struct hpb_desc *)buf)[i].shdma_desc;
-}
-
-static const struct shdma_ops hpb_dmae_ops = {
- .desc_completed = hpb_dmae_desc_completed,
- .halt_channel = hpb_dmae_halt,
- .channel_busy = hpb_dmae_channel_busy,
- .slave_addr = hpb_dmae_slave_addr,
- .desc_setup = hpb_dmae_desc_setup,
- .set_slave = hpb_dmae_set_slave,
- .setup_xfer = hpb_dmae_setup_xfer,
- .start_xfer = hpb_dmae_start_xfer,
- .embedded_desc = hpb_dmae_embedded_desc,
- .chan_irq = hpb_dmae_chan_irq,
- .get_partial = hpb_dmae_get_partial,
-};
-
-static int hpb_dmae_chan_probe(struct hpb_dmae_device *hpbdev, int id)
-{
- struct shdma_dev *sdev = &hpbdev->shdma_dev;
- struct platform_device *pdev =
- to_platform_device(hpbdev->shdma_dev.dma_dev.dev);
- struct hpb_dmae_chan *new_hpb_chan;
- struct shdma_chan *schan;
-
- /* Alloc channel */
- new_hpb_chan = devm_kzalloc(&pdev->dev,
- sizeof(struct hpb_dmae_chan), GFP_KERNEL);
- if (!new_hpb_chan) {
- dev_err(hpbdev->shdma_dev.dma_dev.dev,
- "No free memory for allocating DMA channels!\n");
- return -ENOMEM;
- }
-
- schan = &new_hpb_chan->shdma_chan;
- schan->max_xfer_len = HPB_DMA_TCR_MAX;
-
- shdma_chan_probe(sdev, schan, id);
-
- if (pdev->id >= 0)
- snprintf(new_hpb_chan->dev_id, sizeof(new_hpb_chan->dev_id),
- "hpb-dmae%d.%d", pdev->id, id);
- else
- snprintf(new_hpb_chan->dev_id, sizeof(new_hpb_chan->dev_id),
- "hpb-dma.%d", id);
-
- return 0;
-}
-
-static int hpb_dmae_probe(struct platform_device *pdev)
-{
- const enum dma_slave_buswidth widths = DMA_SLAVE_BUSWIDTH_1_BYTE |
- DMA_SLAVE_BUSWIDTH_2_BYTES | DMA_SLAVE_BUSWIDTH_4_BYTES;
- struct hpb_dmae_pdata *pdata = pdev->dev.platform_data;
- struct hpb_dmae_device *hpbdev;
- struct dma_device *dma_dev;
- struct resource *chan, *comm, *rest, *mode, *irq_res;
- int err, i;
-
- /* Get platform data */
- if (!pdata || !pdata->num_channels)
- return -ENODEV;
-
- chan = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- comm = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- rest = platform_get_resource(pdev, IORESOURCE_MEM, 2);
- mode = platform_get_resource(pdev, IORESOURCE_MEM, 3);
-
- irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!irq_res)
- return -ENODEV;
-
- hpbdev = devm_kzalloc(&pdev->dev, sizeof(struct hpb_dmae_device),
- GFP_KERNEL);
- if (!hpbdev) {
- dev_err(&pdev->dev, "Not enough memory\n");
- return -ENOMEM;
- }
-
- hpbdev->chan_reg = devm_ioremap_resource(&pdev->dev, chan);
- if (IS_ERR(hpbdev->chan_reg))
- return PTR_ERR(hpbdev->chan_reg);
-
- hpbdev->comm_reg = devm_ioremap_resource(&pdev->dev, comm);
- if (IS_ERR(hpbdev->comm_reg))
- return PTR_ERR(hpbdev->comm_reg);
-
- hpbdev->reset_reg = devm_ioremap_resource(&pdev->dev, rest);
- if (IS_ERR(hpbdev->reset_reg))
- return PTR_ERR(hpbdev->reset_reg);
-
- hpbdev->mode_reg = devm_ioremap_resource(&pdev->dev, mode);
- if (IS_ERR(hpbdev->mode_reg))
- return PTR_ERR(hpbdev->mode_reg);
-
- dma_dev = &hpbdev->shdma_dev.dma_dev;
-
- spin_lock_init(&hpbdev->reg_lock);
-
- /* Platform data */
- hpbdev->pdata = pdata;
-
- pm_runtime_enable(&pdev->dev);
- err = pm_runtime_get_sync(&pdev->dev);
- if (err < 0)
- dev_err(&pdev->dev, "%s(): GET = %d\n", __func__, err);
-
- /* Reset DMA controller */
- hpb_dmae_reset(hpbdev);
-
- pm_runtime_put(&pdev->dev);
-
- dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask);
- dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);
- dma_dev->src_addr_widths = widths;
- dma_dev->dst_addr_widths = widths;
- dma_dev->directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM);
- dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
-
- hpbdev->shdma_dev.ops = &hpb_dmae_ops;
- hpbdev->shdma_dev.desc_size = sizeof(struct hpb_desc);
- err = shdma_init(&pdev->dev, &hpbdev->shdma_dev, pdata->num_channels);
- if (err < 0)
- goto error;
-
- /* Create DMA channels */
- for (i = 0; i < pdata->num_channels; i++)
- hpb_dmae_chan_probe(hpbdev, i);
-
- platform_set_drvdata(pdev, hpbdev);
- err = dma_async_device_register(dma_dev);
- if (!err)
- return 0;
-
- shdma_cleanup(&hpbdev->shdma_dev);
-error:
- pm_runtime_disable(&pdev->dev);
- return err;
-}
-
-static void hpb_dmae_chan_remove(struct hpb_dmae_device *hpbdev)
-{
- struct shdma_chan *schan;
- int i;
-
- shdma_for_each_chan(schan, &hpbdev->shdma_dev, i) {
- BUG_ON(!schan);
-
- shdma_chan_remove(schan);
- }
-}
-
-static int hpb_dmae_remove(struct platform_device *pdev)
-{
- struct hpb_dmae_device *hpbdev = platform_get_drvdata(pdev);
-
- dma_async_device_unregister(&hpbdev->shdma_dev.dma_dev);
-
- pm_runtime_disable(&pdev->dev);
-
- hpb_dmae_chan_remove(hpbdev);
-
- return 0;
-}
-
-static void hpb_dmae_shutdown(struct platform_device *pdev)
-{
- struct hpb_dmae_device *hpbdev = platform_get_drvdata(pdev);
- hpb_dmae_ctl_stop(hpbdev);
-}
-
-static struct platform_driver hpb_dmae_driver = {
- .probe = hpb_dmae_probe,
- .remove = hpb_dmae_remove,
- .shutdown = hpb_dmae_shutdown,
- .driver = {
- .name = "hpb-dma-engine",
- },
-};
-module_platform_driver(hpb_dmae_driver);
-
-MODULE_AUTHOR("Max Filippov <max.filippov@cogentembedded.com>");
-MODULE_DESCRIPTION("Renesas HPB DMA Engine driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c
index f1bcc2a163b3..749f1bd5d65d 100644
--- a/drivers/dma/sh/usb-dmac.c
+++ b/drivers/dma/sh/usb-dmac.c
@@ -448,7 +448,7 @@ usb_dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
static int usb_dmac_chan_terminate_all(struct dma_chan *chan)
{
struct usb_dmac_chan *uchan = to_usb_dmac_chan(chan);
- struct usb_dmac_desc *desc;
+ struct usb_dmac_desc *desc, *_desc;
unsigned long flags;
LIST_HEAD(head);
LIST_HEAD(list);
@@ -459,7 +459,7 @@ static int usb_dmac_chan_terminate_all(struct dma_chan *chan)
if (uchan->desc)
uchan->desc = NULL;
list_splice_init(&uchan->desc_got, &list);
- list_for_each_entry(desc, &list, node)
+ list_for_each_entry_safe(desc, _desc, &list, node)
list_move_tail(&desc->node, &uchan->desc_freed);
spin_unlock_irqrestore(&uchan->vc.lock, flags);
vchan_dma_desc_free_list(&uchan->vc, &head);
diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c
index dd3e7ba273ad..6fb8307468ab 100644
--- a/drivers/dma/ste_dma40.c
+++ b/drivers/dma/ste_dma40.c
@@ -3543,8 +3543,8 @@ static int __init d40_probe(struct platform_device *pdev)
struct stedma40_platform_data *plat_data = dev_get_platdata(&pdev->dev);
struct device_node *np = pdev->dev.of_node;
int ret = -ENOENT;
- struct d40_base *base = NULL;
- struct resource *res = NULL;
+ struct d40_base *base;
+ struct resource *res;
int num_reserved_chans;
u32 val;
@@ -3552,17 +3552,17 @@ static int __init d40_probe(struct platform_device *pdev)
if (np) {
if (d40_of_probe(pdev, np)) {
ret = -ENOMEM;
- goto failure;
+ goto report_failure;
}
} else {
d40_err(&pdev->dev, "No pdata or Device Tree provided\n");
- goto failure;
+ goto report_failure;
}
}
base = d40_hw_detect_init(pdev);
if (!base)
- goto failure;
+ goto report_failure;
num_reserved_chans = d40_phy_res_init(base);
@@ -3693,51 +3693,48 @@ static int __init d40_probe(struct platform_device *pdev)
return 0;
failure:
- if (base) {
- if (base->desc_slab)
- kmem_cache_destroy(base->desc_slab);
- if (base->virtbase)
- iounmap(base->virtbase);
-
- if (base->lcla_pool.base && base->plat_data->use_esram_lcla) {
- iounmap(base->lcla_pool.base);
- base->lcla_pool.base = NULL;
- }
+ kmem_cache_destroy(base->desc_slab);
+ if (base->virtbase)
+ iounmap(base->virtbase);
- if (base->lcla_pool.dma_addr)
- dma_unmap_single(base->dev, base->lcla_pool.dma_addr,
- SZ_1K * base->num_phy_chans,
- DMA_TO_DEVICE);
-
- if (!base->lcla_pool.base_unaligned && base->lcla_pool.base)
- free_pages((unsigned long)base->lcla_pool.base,
- base->lcla_pool.pages);
-
- kfree(base->lcla_pool.base_unaligned);
-
- if (base->phy_lcpa)
- release_mem_region(base->phy_lcpa,
- base->lcpa_size);
- if (base->phy_start)
- release_mem_region(base->phy_start,
- base->phy_size);
- if (base->clk) {
- clk_disable_unprepare(base->clk);
- clk_put(base->clk);
- }
+ if (base->lcla_pool.base && base->plat_data->use_esram_lcla) {
+ iounmap(base->lcla_pool.base);
+ base->lcla_pool.base = NULL;
+ }
- if (base->lcpa_regulator) {
- regulator_disable(base->lcpa_regulator);
- regulator_put(base->lcpa_regulator);
- }
+ if (base->lcla_pool.dma_addr)
+ dma_unmap_single(base->dev, base->lcla_pool.dma_addr,
+ SZ_1K * base->num_phy_chans,
+ DMA_TO_DEVICE);
- kfree(base->lcla_pool.alloc_map);
- kfree(base->lookup_log_chans);
- kfree(base->lookup_phy_chans);
- kfree(base->phy_res);
- kfree(base);
+ if (!base->lcla_pool.base_unaligned && base->lcla_pool.base)
+ free_pages((unsigned long)base->lcla_pool.base,
+ base->lcla_pool.pages);
+
+ kfree(base->lcla_pool.base_unaligned);
+
+ if (base->phy_lcpa)
+ release_mem_region(base->phy_lcpa,
+ base->lcpa_size);
+ if (base->phy_start)
+ release_mem_region(base->phy_start,
+ base->phy_size);
+ if (base->clk) {
+ clk_disable_unprepare(base->clk);
+ clk_put(base->clk);
+ }
+
+ if (base->lcpa_regulator) {
+ regulator_disable(base->lcpa_regulator);
+ regulator_put(base->lcpa_regulator);
}
+ kfree(base->lcla_pool.alloc_map);
+ kfree(base->lookup_log_chans);
+ kfree(base->lookup_phy_chans);
+ kfree(base->phy_res);
+ kfree(base);
+report_failure:
d40_err(&pdev->dev, "probe failed\n");
return ret;
}
diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c
new file mode 100644
index 000000000000..047476a1383d
--- /dev/null
+++ b/drivers/dma/stm32-dma.c
@@ -0,0 +1,1141 @@
+/*
+ * Driver for STM32 DMA controller
+ *
+ * Inspired by dma-jz4740.c and tegra20-apb-dma.c
+ *
+ * Copyright (C) M'boumba Cedric Madianga 2015
+ * Author: M'boumba Cedric Madianga <cedric.madianga@gmail.com>
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include "virt-dma.h"
+
+#define STM32_DMA_LISR 0x0000 /* DMA Low Int Status Reg */
+#define STM32_DMA_HISR 0x0004 /* DMA High Int Status Reg */
+#define STM32_DMA_LIFCR 0x0008 /* DMA Low Int Flag Clear Reg */
+#define STM32_DMA_HIFCR 0x000c /* DMA High Int Flag Clear Reg */
+#define STM32_DMA_TCI BIT(5) /* Transfer Complete Interrupt */
+#define STM32_DMA_TEI BIT(3) /* Transfer Error Interrupt */
+#define STM32_DMA_DMEI BIT(2) /* Direct Mode Error Interrupt */
+#define STM32_DMA_FEI BIT(0) /* FIFO Error Interrupt */
+
+/* DMA Stream x Configuration Register */
+#define STM32_DMA_SCR(x) (0x0010 + 0x18 * (x)) /* x = 0..7 */
+#define STM32_DMA_SCR_REQ(n) ((n & 0x7) << 25)
+#define STM32_DMA_SCR_MBURST_MASK GENMASK(24, 23)
+#define STM32_DMA_SCR_MBURST(n) ((n & 0x3) << 23)
+#define STM32_DMA_SCR_PBURST_MASK GENMASK(22, 21)
+#define STM32_DMA_SCR_PBURST(n) ((n & 0x3) << 21)
+#define STM32_DMA_SCR_PL_MASK GENMASK(17, 16)
+#define STM32_DMA_SCR_PL(n) ((n & 0x3) << 16)
+#define STM32_DMA_SCR_MSIZE_MASK GENMASK(14, 13)
+#define STM32_DMA_SCR_MSIZE(n) ((n & 0x3) << 13)
+#define STM32_DMA_SCR_PSIZE_MASK GENMASK(12, 11)
+#define STM32_DMA_SCR_PSIZE(n) ((n & 0x3) << 11)
+#define STM32_DMA_SCR_PSIZE_GET(n) ((n & STM32_DMA_SCR_PSIZE_MASK) >> 11)
+#define STM32_DMA_SCR_DIR_MASK GENMASK(7, 6)
+#define STM32_DMA_SCR_DIR(n) ((n & 0x3) << 6)
+#define STM32_DMA_SCR_CT BIT(19) /* Target in double buffer */
+#define STM32_DMA_SCR_DBM BIT(18) /* Double Buffer Mode */
+#define STM32_DMA_SCR_PINCOS BIT(15) /* Peripheral inc offset size */
+#define STM32_DMA_SCR_MINC BIT(10) /* Memory increment mode */
+#define STM32_DMA_SCR_PINC BIT(9) /* Peripheral increment mode */
+#define STM32_DMA_SCR_CIRC BIT(8) /* Circular mode */
+#define STM32_DMA_SCR_PFCTRL BIT(5) /* Peripheral Flow Controller */
+#define STM32_DMA_SCR_TCIE BIT(4) /* Transfer Cplete Int Enable*/
+#define STM32_DMA_SCR_TEIE BIT(2) /* Transfer Error Int Enable */
+#define STM32_DMA_SCR_DMEIE BIT(1) /* Direct Mode Err Int Enable */
+#define STM32_DMA_SCR_EN BIT(0) /* Stream Enable */
+#define STM32_DMA_SCR_CFG_MASK (STM32_DMA_SCR_PINC \
+ | STM32_DMA_SCR_MINC \
+ | STM32_DMA_SCR_PINCOS \
+ | STM32_DMA_SCR_PL_MASK)
+#define STM32_DMA_SCR_IRQ_MASK (STM32_DMA_SCR_TCIE \
+ | STM32_DMA_SCR_TEIE \
+ | STM32_DMA_SCR_DMEIE)
+
+/* DMA Stream x number of data register */
+#define STM32_DMA_SNDTR(x) (0x0014 + 0x18 * (x))
+
+/* DMA stream peripheral address register */
+#define STM32_DMA_SPAR(x) (0x0018 + 0x18 * (x))
+
+/* DMA stream x memory 0 address register */
+#define STM32_DMA_SM0AR(x) (0x001c + 0x18 * (x))
+
+/* DMA stream x memory 1 address register */
+#define STM32_DMA_SM1AR(x) (0x0020 + 0x18 * (x))
+
+/* DMA stream x FIFO control register */
+#define STM32_DMA_SFCR(x) (0x0024 + 0x18 * (x))
+#define STM32_DMA_SFCR_FTH_MASK GENMASK(1, 0)
+#define STM32_DMA_SFCR_FTH(n) (n & STM32_DMA_SFCR_FTH_MASK)
+#define STM32_DMA_SFCR_FEIE BIT(7) /* FIFO error interrupt enable */
+#define STM32_DMA_SFCR_DMDIS BIT(2) /* Direct mode disable */
+#define STM32_DMA_SFCR_MASK (STM32_DMA_SFCR_FEIE \
+ | STM32_DMA_SFCR_DMDIS)
+
+/* DMA direction */
+#define STM32_DMA_DEV_TO_MEM 0x00
+#define STM32_DMA_MEM_TO_DEV 0x01
+#define STM32_DMA_MEM_TO_MEM 0x02
+
+/* DMA priority level */
+#define STM32_DMA_PRIORITY_LOW 0x00
+#define STM32_DMA_PRIORITY_MEDIUM 0x01
+#define STM32_DMA_PRIORITY_HIGH 0x02
+#define STM32_DMA_PRIORITY_VERY_HIGH 0x03
+
+/* DMA FIFO threshold selection */
+#define STM32_DMA_FIFO_THRESHOLD_1QUARTERFULL 0x00
+#define STM32_DMA_FIFO_THRESHOLD_HALFFULL 0x01
+#define STM32_DMA_FIFO_THRESHOLD_3QUARTERSFULL 0x02
+#define STM32_DMA_FIFO_THRESHOLD_FULL 0x03
+
+#define STM32_DMA_MAX_DATA_ITEMS 0xffff
+#define STM32_DMA_MAX_CHANNELS 0x08
+#define STM32_DMA_MAX_REQUEST_ID 0x08
+#define STM32_DMA_MAX_DATA_PARAM 0x03
+
+enum stm32_dma_width {
+ STM32_DMA_BYTE,
+ STM32_DMA_HALF_WORD,
+ STM32_DMA_WORD,
+};
+
+enum stm32_dma_burst_size {
+ STM32_DMA_BURST_SINGLE,
+ STM32_DMA_BURST_INCR4,
+ STM32_DMA_BURST_INCR8,
+ STM32_DMA_BURST_INCR16,
+};
+
+struct stm32_dma_cfg {
+ u32 channel_id;
+ u32 request_line;
+ u32 stream_config;
+ u32 threshold;
+};
+
+struct stm32_dma_chan_reg {
+ u32 dma_lisr;
+ u32 dma_hisr;
+ u32 dma_lifcr;
+ u32 dma_hifcr;
+ u32 dma_scr;
+ u32 dma_sndtr;
+ u32 dma_spar;
+ u32 dma_sm0ar;
+ u32 dma_sm1ar;
+ u32 dma_sfcr;
+};
+
+struct stm32_dma_sg_req {
+ u32 len;
+ struct stm32_dma_chan_reg chan_reg;
+};
+
+struct stm32_dma_desc {
+ struct virt_dma_desc vdesc;
+ bool cyclic;
+ u32 num_sgs;
+ struct stm32_dma_sg_req sg_req[];
+};
+
+struct stm32_dma_chan {
+ struct virt_dma_chan vchan;
+ bool config_init;
+ bool busy;
+ u32 id;
+ u32 irq;
+ struct stm32_dma_desc *desc;
+ u32 next_sg;
+ struct dma_slave_config dma_sconfig;
+ struct stm32_dma_chan_reg chan_reg;
+};
+
+struct stm32_dma_device {
+ struct dma_device ddev;
+ void __iomem *base;
+ struct clk *clk;
+ struct reset_control *rst;
+ bool mem2mem;
+ struct stm32_dma_chan chan[STM32_DMA_MAX_CHANNELS];
+};
+
+static struct stm32_dma_device *stm32_dma_get_dev(struct stm32_dma_chan *chan)
+{
+ return container_of(chan->vchan.chan.device, struct stm32_dma_device,
+ ddev);
+}
+
+static struct stm32_dma_chan *to_stm32_dma_chan(struct dma_chan *c)
+{
+ return container_of(c, struct stm32_dma_chan, vchan.chan);
+}
+
+static struct stm32_dma_desc *to_stm32_dma_desc(struct virt_dma_desc *vdesc)
+{
+ return container_of(vdesc, struct stm32_dma_desc, vdesc);
+}
+
+static struct device *chan2dev(struct stm32_dma_chan *chan)
+{
+ return &chan->vchan.chan.dev->device;
+}
+
+static u32 stm32_dma_read(struct stm32_dma_device *dmadev, u32 reg)
+{
+ return readl_relaxed(dmadev->base + reg);
+}
+
+static void stm32_dma_write(struct stm32_dma_device *dmadev, u32 reg, u32 val)
+{
+ writel_relaxed(val, dmadev->base + reg);
+}
+
+static struct stm32_dma_desc *stm32_dma_alloc_desc(u32 num_sgs)
+{
+ return kzalloc(sizeof(struct stm32_dma_desc) +
+ sizeof(struct stm32_dma_sg_req) * num_sgs, GFP_NOWAIT);
+}
+
+static int stm32_dma_get_width(struct stm32_dma_chan *chan,
+ enum dma_slave_buswidth width)
+{
+ switch (width) {
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ return STM32_DMA_BYTE;
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ return STM32_DMA_HALF_WORD;
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ return STM32_DMA_WORD;
+ default:
+ dev_err(chan2dev(chan), "Dma bus width not supported\n");
+ return -EINVAL;
+ }
+}
+
+static int stm32_dma_get_burst(struct stm32_dma_chan *chan, u32 maxburst)
+{
+ switch (maxburst) {
+ case 0:
+ case 1:
+ return STM32_DMA_BURST_SINGLE;
+ case 4:
+ return STM32_DMA_BURST_INCR4;
+ case 8:
+ return STM32_DMA_BURST_INCR8;
+ case 16:
+ return STM32_DMA_BURST_INCR16;
+ default:
+ dev_err(chan2dev(chan), "Dma burst size not supported\n");
+ return -EINVAL;
+ }
+}
+
+static void stm32_dma_set_fifo_config(struct stm32_dma_chan *chan,
+ u32 src_maxburst, u32 dst_maxburst)
+{
+ chan->chan_reg.dma_sfcr &= ~STM32_DMA_SFCR_MASK;
+ chan->chan_reg.dma_scr &= ~STM32_DMA_SCR_DMEIE;
+
+ if ((!src_maxburst) && (!dst_maxburst)) {
+ /* Using direct mode */
+ chan->chan_reg.dma_scr |= STM32_DMA_SCR_DMEIE;
+ } else {
+ /* Using FIFO mode */
+ chan->chan_reg.dma_sfcr |= STM32_DMA_SFCR_MASK;
+ }
+}
+
+static int stm32_dma_slave_config(struct dma_chan *c,
+ struct dma_slave_config *config)
+{
+ struct stm32_dma_chan *chan = to_stm32_dma_chan(c);
+
+ memcpy(&chan->dma_sconfig, config, sizeof(*config));
+
+ chan->config_init = true;
+
+ return 0;
+}
+
+static u32 stm32_dma_irq_status(struct stm32_dma_chan *chan)
+{
+ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
+ u32 flags, dma_isr;
+
+ /*
+ * Read "flags" from DMA_xISR register corresponding to the selected
+ * DMA channel at the correct bit offset inside that register.
+ *
+ * If (ch % 4) is 2 or 3, left shift the mask by 16 bits.
+ * If (ch % 4) is 1 or 3, additionally left shift the mask by 6 bits.
+ */
+
+ if (chan->id & 4)
+ dma_isr = stm32_dma_read(dmadev, STM32_DMA_HISR);
+ else
+ dma_isr = stm32_dma_read(dmadev, STM32_DMA_LISR);
+
+ flags = dma_isr >> (((chan->id & 2) << 3) | ((chan->id & 1) * 6));
+
+ return flags;
+}
+
+static void stm32_dma_irq_clear(struct stm32_dma_chan *chan, u32 flags)
+{
+ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
+ u32 dma_ifcr;
+
+ /*
+ * Write "flags" to the DMA_xIFCR register corresponding to the selected
+ * DMA channel at the correct bit offset inside that register.
+ *
+ * If (ch % 4) is 2 or 3, left shift the mask by 16 bits.
+ * If (ch % 4) is 1 or 3, additionally left shift the mask by 6 bits.
+ */
+ dma_ifcr = flags << (((chan->id & 2) << 3) | ((chan->id & 1) * 6));
+
+ if (chan->id & 4)
+ stm32_dma_write(dmadev, STM32_DMA_HIFCR, dma_ifcr);
+ else
+ stm32_dma_write(dmadev, STM32_DMA_LIFCR, dma_ifcr);
+}
+
+static int stm32_dma_disable_chan(struct stm32_dma_chan *chan)
+{
+ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
+ unsigned long timeout = jiffies + msecs_to_jiffies(5000);
+ u32 dma_scr, id;
+
+ id = chan->id;
+ dma_scr = stm32_dma_read(dmadev, STM32_DMA_SCR(id));
+
+ if (dma_scr & STM32_DMA_SCR_EN) {
+ dma_scr &= ~STM32_DMA_SCR_EN;
+ stm32_dma_write(dmadev, STM32_DMA_SCR(id), dma_scr);
+
+ do {
+ dma_scr = stm32_dma_read(dmadev, STM32_DMA_SCR(id));
+ dma_scr &= STM32_DMA_SCR_EN;
+ if (!dma_scr)
+ break;
+
+ if (time_after_eq(jiffies, timeout)) {
+ dev_err(chan2dev(chan), "%s: timeout!\n",
+ __func__);
+ return -EBUSY;
+ }
+ cond_resched();
+ } while (1);
+ }
+
+ return 0;
+}
+
+static void stm32_dma_stop(struct stm32_dma_chan *chan)
+{
+ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
+ u32 dma_scr, dma_sfcr, status;
+ int ret;
+
+ /* Disable interrupts */
+ dma_scr = stm32_dma_read(dmadev, STM32_DMA_SCR(chan->id));
+ dma_scr &= ~STM32_DMA_SCR_IRQ_MASK;
+ stm32_dma_write(dmadev, STM32_DMA_SCR(chan->id), dma_scr);
+ dma_sfcr = stm32_dma_read(dmadev, STM32_DMA_SFCR(chan->id));
+ dma_sfcr &= ~STM32_DMA_SFCR_FEIE;
+ stm32_dma_write(dmadev, STM32_DMA_SFCR(chan->id), dma_sfcr);
+
+ /* Disable DMA */
+ ret = stm32_dma_disable_chan(chan);
+ if (ret < 0)
+ return;
+
+ /* Clear interrupt status if it is there */
+ status = stm32_dma_irq_status(chan);
+ if (status) {
+ dev_dbg(chan2dev(chan), "%s(): clearing interrupt: 0x%08x\n",
+ __func__, status);
+ stm32_dma_irq_clear(chan, status);
+ }
+
+ chan->busy = false;
+}
+
+static int stm32_dma_terminate_all(struct dma_chan *c)
+{
+ struct stm32_dma_chan *chan = to_stm32_dma_chan(c);
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ spin_lock_irqsave(&chan->vchan.lock, flags);
+
+ if (chan->busy) {
+ stm32_dma_stop(chan);
+ chan->desc = NULL;
+ }
+
+ vchan_get_all_descriptors(&chan->vchan, &head);
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
+ vchan_dma_desc_free_list(&chan->vchan, &head);
+
+ return 0;
+}
+
+static void stm32_dma_dump_reg(struct stm32_dma_chan *chan)
+{
+ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
+ u32 scr = stm32_dma_read(dmadev, STM32_DMA_SCR(chan->id));
+ u32 ndtr = stm32_dma_read(dmadev, STM32_DMA_SNDTR(chan->id));
+ u32 spar = stm32_dma_read(dmadev, STM32_DMA_SPAR(chan->id));
+ u32 sm0ar = stm32_dma_read(dmadev, STM32_DMA_SM0AR(chan->id));
+ u32 sm1ar = stm32_dma_read(dmadev, STM32_DMA_SM1AR(chan->id));
+ u32 sfcr = stm32_dma_read(dmadev, STM32_DMA_SFCR(chan->id));
+
+ dev_dbg(chan2dev(chan), "SCR: 0x%08x\n", scr);
+ dev_dbg(chan2dev(chan), "NDTR: 0x%08x\n", ndtr);
+ dev_dbg(chan2dev(chan), "SPAR: 0x%08x\n", spar);
+ dev_dbg(chan2dev(chan), "SM0AR: 0x%08x\n", sm0ar);
+ dev_dbg(chan2dev(chan), "SM1AR: 0x%08x\n", sm1ar);
+ dev_dbg(chan2dev(chan), "SFCR: 0x%08x\n", sfcr);
+}
+
+static int stm32_dma_start_transfer(struct stm32_dma_chan *chan)
+{
+ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
+ struct virt_dma_desc *vdesc;
+ struct stm32_dma_sg_req *sg_req;
+ struct stm32_dma_chan_reg *reg;
+ u32 status;
+ int ret;
+
+ ret = stm32_dma_disable_chan(chan);
+ if (ret < 0)
+ return ret;
+
+ if (!chan->desc) {
+ vdesc = vchan_next_desc(&chan->vchan);
+ if (!vdesc)
+ return -EPERM;
+
+ chan->desc = to_stm32_dma_desc(vdesc);
+ chan->next_sg = 0;
+ }
+
+ if (chan->next_sg == chan->desc->num_sgs)
+ chan->next_sg = 0;
+
+ sg_req = &chan->desc->sg_req[chan->next_sg];
+ reg = &sg_req->chan_reg;
+
+ stm32_dma_write(dmadev, STM32_DMA_SCR(chan->id), reg->dma_scr);
+ stm32_dma_write(dmadev, STM32_DMA_SPAR(chan->id), reg->dma_spar);
+ stm32_dma_write(dmadev, STM32_DMA_SM0AR(chan->id), reg->dma_sm0ar);
+ stm32_dma_write(dmadev, STM32_DMA_SFCR(chan->id), reg->dma_sfcr);
+ stm32_dma_write(dmadev, STM32_DMA_SM1AR(chan->id), reg->dma_sm1ar);
+ stm32_dma_write(dmadev, STM32_DMA_SNDTR(chan->id), reg->dma_sndtr);
+
+ chan->next_sg++;
+
+ /* Clear interrupt status if it is there */
+ status = stm32_dma_irq_status(chan);
+ if (status)
+ stm32_dma_irq_clear(chan, status);
+
+ stm32_dma_dump_reg(chan);
+
+ /* Start DMA */
+ reg->dma_scr |= STM32_DMA_SCR_EN;
+ stm32_dma_write(dmadev, STM32_DMA_SCR(chan->id), reg->dma_scr);
+
+ chan->busy = true;
+
+ return 0;
+}
+
+static void stm32_dma_configure_next_sg(struct stm32_dma_chan *chan)
+{
+ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
+ struct stm32_dma_sg_req *sg_req;
+ u32 dma_scr, dma_sm0ar, dma_sm1ar, id;
+
+ id = chan->id;
+ dma_scr = stm32_dma_read(dmadev, STM32_DMA_SCR(id));
+
+ if (dma_scr & STM32_DMA_SCR_DBM) {
+ if (chan->next_sg == chan->desc->num_sgs)
+ chan->next_sg = 0;
+
+ sg_req = &chan->desc->sg_req[chan->next_sg];
+
+ if (dma_scr & STM32_DMA_SCR_CT) {
+ dma_sm0ar = sg_req->chan_reg.dma_sm0ar;
+ stm32_dma_write(dmadev, STM32_DMA_SM0AR(id), dma_sm0ar);
+ dev_dbg(chan2dev(chan), "CT=1 <=> SM0AR: 0x%08x\n",
+ stm32_dma_read(dmadev, STM32_DMA_SM0AR(id)));
+ } else {
+ dma_sm1ar = sg_req->chan_reg.dma_sm1ar;
+ stm32_dma_write(dmadev, STM32_DMA_SM1AR(id), dma_sm1ar);
+ dev_dbg(chan2dev(chan), "CT=0 <=> SM1AR: 0x%08x\n",
+ stm32_dma_read(dmadev, STM32_DMA_SM1AR(id)));
+ }
+
+ chan->next_sg++;
+ }
+}
+
+static void stm32_dma_handle_chan_done(struct stm32_dma_chan *chan)
+{
+ if (chan->desc) {
+ if (chan->desc->cyclic) {
+ vchan_cyclic_callback(&chan->desc->vdesc);
+ stm32_dma_configure_next_sg(chan);
+ } else {
+ chan->busy = false;
+ if (chan->next_sg == chan->desc->num_sgs) {
+ list_del(&chan->desc->vdesc.node);
+ vchan_cookie_complete(&chan->desc->vdesc);
+ chan->desc = NULL;
+ }
+ stm32_dma_start_transfer(chan);
+ }
+ }
+}
+
+static irqreturn_t stm32_dma_chan_irq(int irq, void *devid)
+{
+ struct stm32_dma_chan *chan = devid;
+ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
+ u32 status, scr, sfcr;
+
+ spin_lock(&chan->vchan.lock);
+
+ status = stm32_dma_irq_status(chan);
+ scr = stm32_dma_read(dmadev, STM32_DMA_SCR(chan->id));
+ sfcr = stm32_dma_read(dmadev, STM32_DMA_SFCR(chan->id));
+
+ if ((status & STM32_DMA_TCI) && (scr & STM32_DMA_SCR_TCIE)) {
+ stm32_dma_irq_clear(chan, STM32_DMA_TCI);
+ stm32_dma_handle_chan_done(chan);
+
+ } else {
+ stm32_dma_irq_clear(chan, status);
+ dev_err(chan2dev(chan), "DMA error: status=0x%08x\n", status);
+ }
+
+ spin_unlock(&chan->vchan.lock);
+
+ return IRQ_HANDLED;
+}
+
+static void stm32_dma_issue_pending(struct dma_chan *c)
+{
+ struct stm32_dma_chan *chan = to_stm32_dma_chan(c);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&chan->vchan.lock, flags);
+ if (!chan->busy) {
+ if (vchan_issue_pending(&chan->vchan) && !chan->desc) {
+ ret = stm32_dma_start_transfer(chan);
+ if ((!ret) && (chan->desc->cyclic))
+ stm32_dma_configure_next_sg(chan);
+ }
+ }
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
+}
+
+static int stm32_dma_set_xfer_param(struct stm32_dma_chan *chan,
+ enum dma_transfer_direction direction,
+ enum dma_slave_buswidth *buswidth)
+{
+ enum dma_slave_buswidth src_addr_width, dst_addr_width;
+ int src_bus_width, dst_bus_width;
+ int src_burst_size, dst_burst_size;
+ u32 src_maxburst, dst_maxburst;
+ dma_addr_t src_addr, dst_addr;
+ u32 dma_scr = 0;
+
+ src_addr_width = chan->dma_sconfig.src_addr_width;
+ dst_addr_width = chan->dma_sconfig.dst_addr_width;
+ src_maxburst = chan->dma_sconfig.src_maxburst;
+ dst_maxburst = chan->dma_sconfig.dst_maxburst;
+ src_addr = chan->dma_sconfig.src_addr;
+ dst_addr = chan->dma_sconfig.dst_addr;
+
+ switch (direction) {
+ case DMA_MEM_TO_DEV:
+ dst_bus_width = stm32_dma_get_width(chan, dst_addr_width);
+ if (dst_bus_width < 0)
+ return dst_bus_width;
+
+ dst_burst_size = stm32_dma_get_burst(chan, dst_maxburst);
+ if (dst_burst_size < 0)
+ return dst_burst_size;
+
+ if (!src_addr_width)
+ src_addr_width = dst_addr_width;
+
+ src_bus_width = stm32_dma_get_width(chan, src_addr_width);
+ if (src_bus_width < 0)
+ return src_bus_width;
+
+ src_burst_size = stm32_dma_get_burst(chan, src_maxburst);
+ if (src_burst_size < 0)
+ return src_burst_size;
+
+ dma_scr = STM32_DMA_SCR_DIR(STM32_DMA_MEM_TO_DEV) |
+ STM32_DMA_SCR_PSIZE(dst_bus_width) |
+ STM32_DMA_SCR_MSIZE(src_bus_width) |
+ STM32_DMA_SCR_PBURST(dst_burst_size) |
+ STM32_DMA_SCR_MBURST(src_burst_size);
+
+ chan->chan_reg.dma_spar = chan->dma_sconfig.dst_addr;
+ *buswidth = dst_addr_width;
+ break;
+
+ case DMA_DEV_TO_MEM:
+ src_bus_width = stm32_dma_get_width(chan, src_addr_width);
+ if (src_bus_width < 0)
+ return src_bus_width;
+
+ src_burst_size = stm32_dma_get_burst(chan, src_maxburst);
+ if (src_burst_size < 0)
+ return src_burst_size;
+
+ if (!dst_addr_width)
+ dst_addr_width = src_addr_width;
+
+ dst_bus_width = stm32_dma_get_width(chan, dst_addr_width);
+ if (dst_bus_width < 0)
+ return dst_bus_width;
+
+ dst_burst_size = stm32_dma_get_burst(chan, dst_maxburst);
+ if (dst_burst_size < 0)
+ return dst_burst_size;
+
+ dma_scr = STM32_DMA_SCR_DIR(STM32_DMA_DEV_TO_MEM) |
+ STM32_DMA_SCR_PSIZE(src_bus_width) |
+ STM32_DMA_SCR_MSIZE(dst_bus_width) |
+ STM32_DMA_SCR_PBURST(src_burst_size) |
+ STM32_DMA_SCR_MBURST(dst_burst_size);
+
+ chan->chan_reg.dma_spar = chan->dma_sconfig.src_addr;
+ *buswidth = chan->dma_sconfig.src_addr_width;
+ break;
+
+ default:
+ dev_err(chan2dev(chan), "Dma direction is not supported\n");
+ return -EINVAL;
+ }
+
+ stm32_dma_set_fifo_config(chan, src_maxburst, dst_maxburst);
+
+ chan->chan_reg.dma_scr &= ~(STM32_DMA_SCR_DIR_MASK |
+ STM32_DMA_SCR_PSIZE_MASK | STM32_DMA_SCR_MSIZE_MASK |
+ STM32_DMA_SCR_PBURST_MASK | STM32_DMA_SCR_MBURST_MASK);
+ chan->chan_reg.dma_scr |= dma_scr;
+
+ return 0;
+}
+
+static void stm32_dma_clear_reg(struct stm32_dma_chan_reg *regs)
+{
+ memset(regs, 0, sizeof(struct stm32_dma_chan_reg));
+}
+
+static struct dma_async_tx_descriptor *stm32_dma_prep_slave_sg(
+ struct dma_chan *c, struct scatterlist *sgl,
+ u32 sg_len, enum dma_transfer_direction direction,
+ unsigned long flags, void *context)
+{
+ struct stm32_dma_chan *chan = to_stm32_dma_chan(c);
+ struct stm32_dma_desc *desc;
+ struct scatterlist *sg;
+ enum dma_slave_buswidth buswidth;
+ u32 nb_data_items;
+ int i, ret;
+
+ if (!chan->config_init) {
+ dev_err(chan2dev(chan), "dma channel is not configured\n");
+ return NULL;
+ }
+
+ if (sg_len < 1) {
+ dev_err(chan2dev(chan), "Invalid segment length %d\n", sg_len);
+ return NULL;
+ }
+
+ desc = stm32_dma_alloc_desc(sg_len);
+ if (!desc)
+ return NULL;
+
+ ret = stm32_dma_set_xfer_param(chan, direction, &buswidth);
+ if (ret < 0)
+ goto err;
+
+ /* Set peripheral flow controller */
+ if (chan->dma_sconfig.device_fc)
+ chan->chan_reg.dma_scr |= STM32_DMA_SCR_PFCTRL;
+ else
+ chan->chan_reg.dma_scr &= ~STM32_DMA_SCR_PFCTRL;
+
+ for_each_sg(sgl, sg, sg_len, i) {
+ desc->sg_req[i].len = sg_dma_len(sg);
+
+ nb_data_items = desc->sg_req[i].len / buswidth;
+ if (nb_data_items > STM32_DMA_MAX_DATA_ITEMS) {
+ dev_err(chan2dev(chan), "nb items not supported\n");
+ goto err;
+ }
+
+ stm32_dma_clear_reg(&desc->sg_req[i].chan_reg);
+ desc->sg_req[i].chan_reg.dma_scr = chan->chan_reg.dma_scr;
+ desc->sg_req[i].chan_reg.dma_sfcr = chan->chan_reg.dma_sfcr;
+ desc->sg_req[i].chan_reg.dma_spar = chan->chan_reg.dma_spar;
+ desc->sg_req[i].chan_reg.dma_sm0ar = sg_dma_address(sg);
+ desc->sg_req[i].chan_reg.dma_sm1ar = sg_dma_address(sg);
+ desc->sg_req[i].chan_reg.dma_sndtr = nb_data_items;
+ }
+
+ desc->num_sgs = sg_len;
+ desc->cyclic = false;
+
+ return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
+
+err:
+ kfree(desc);
+ return NULL;
+}
+
+static struct dma_async_tx_descriptor *stm32_dma_prep_dma_cyclic(
+ struct dma_chan *c, dma_addr_t buf_addr, size_t buf_len,
+ size_t period_len, enum dma_transfer_direction direction,
+ unsigned long flags)
+{
+ struct stm32_dma_chan *chan = to_stm32_dma_chan(c);
+ struct stm32_dma_desc *desc;
+ enum dma_slave_buswidth buswidth;
+ u32 num_periods, nb_data_items;
+ int i, ret;
+
+ if (!buf_len || !period_len) {
+ dev_err(chan2dev(chan), "Invalid buffer/period len\n");
+ return NULL;
+ }
+
+ if (!chan->config_init) {
+ dev_err(chan2dev(chan), "dma channel is not configured\n");
+ return NULL;
+ }
+
+ if (buf_len % period_len) {
+ dev_err(chan2dev(chan), "buf_len not multiple of period_len\n");
+ return NULL;
+ }
+
+ /*
+ * We allow to take more number of requests till DMA is
+ * not started. The driver will loop over all requests.
+ * Once DMA is started then new requests can be queued only after
+ * terminating the DMA.
+ */
+ if (chan->busy) {
+ dev_err(chan2dev(chan), "Request not allowed when dma busy\n");
+ return NULL;
+ }
+
+ ret = stm32_dma_set_xfer_param(chan, direction, &buswidth);
+ if (ret < 0)
+ return NULL;
+
+ nb_data_items = period_len / buswidth;
+ if (nb_data_items > STM32_DMA_MAX_DATA_ITEMS) {
+ dev_err(chan2dev(chan), "number of items not supported\n");
+ return NULL;
+ }
+
+ /* Enable Circular mode or double buffer mode */
+ if (buf_len == period_len)
+ chan->chan_reg.dma_scr |= STM32_DMA_SCR_CIRC;
+ else
+ chan->chan_reg.dma_scr |= STM32_DMA_SCR_DBM;
+
+ /* Clear periph ctrl if client set it */
+ chan->chan_reg.dma_scr &= ~STM32_DMA_SCR_PFCTRL;
+
+ num_periods = buf_len / period_len;
+
+ desc = stm32_dma_alloc_desc(num_periods);
+ if (!desc)
+ return NULL;
+
+ for (i = 0; i < num_periods; i++) {
+ desc->sg_req[i].len = period_len;
+
+ stm32_dma_clear_reg(&desc->sg_req[i].chan_reg);
+ desc->sg_req[i].chan_reg.dma_scr = chan->chan_reg.dma_scr;
+ desc->sg_req[i].chan_reg.dma_sfcr = chan->chan_reg.dma_sfcr;
+ desc->sg_req[i].chan_reg.dma_spar = chan->chan_reg.dma_spar;
+ desc->sg_req[i].chan_reg.dma_sm0ar = buf_addr;
+ desc->sg_req[i].chan_reg.dma_sm1ar = buf_addr;
+ desc->sg_req[i].chan_reg.dma_sndtr = nb_data_items;
+ buf_addr += period_len;
+ }
+
+ desc->num_sgs = num_periods;
+ desc->cyclic = true;
+
+ return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
+}
+
+static struct dma_async_tx_descriptor *stm32_dma_prep_dma_memcpy(
+ struct dma_chan *c, dma_addr_t dest,
+ dma_addr_t src, size_t len, unsigned long flags)
+{
+ struct stm32_dma_chan *chan = to_stm32_dma_chan(c);
+ u32 num_sgs;
+ struct stm32_dma_desc *desc;
+ size_t xfer_count, offset;
+ int i;
+
+ num_sgs = DIV_ROUND_UP(len, STM32_DMA_MAX_DATA_ITEMS);
+ desc = stm32_dma_alloc_desc(num_sgs);
+ if (!desc)
+ return NULL;
+
+ for (offset = 0, i = 0; offset < len; offset += xfer_count, i++) {
+ xfer_count = min_t(size_t, len - offset,
+ STM32_DMA_MAX_DATA_ITEMS);
+
+ desc->sg_req[i].len = xfer_count;
+
+ stm32_dma_clear_reg(&desc->sg_req[i].chan_reg);
+ desc->sg_req[i].chan_reg.dma_scr =
+ STM32_DMA_SCR_DIR(STM32_DMA_MEM_TO_MEM) |
+ STM32_DMA_SCR_MINC |
+ STM32_DMA_SCR_PINC |
+ STM32_DMA_SCR_TCIE |
+ STM32_DMA_SCR_TEIE;
+ desc->sg_req[i].chan_reg.dma_sfcr = STM32_DMA_SFCR_DMDIS |
+ STM32_DMA_SFCR_FTH(STM32_DMA_FIFO_THRESHOLD_FULL) |
+ STM32_DMA_SFCR_FEIE;
+ desc->sg_req[i].chan_reg.dma_spar = src + offset;
+ desc->sg_req[i].chan_reg.dma_sm0ar = dest + offset;
+ desc->sg_req[i].chan_reg.dma_sndtr = xfer_count;
+ }
+
+ desc->num_sgs = num_sgs;
+ desc->cyclic = false;
+
+ return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
+}
+
+static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan,
+ struct stm32_dma_desc *desc,
+ u32 next_sg)
+{
+ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
+ u32 dma_scr, width, residue, count;
+ int i;
+
+ residue = 0;
+
+ for (i = next_sg; i < desc->num_sgs; i++)
+ residue += desc->sg_req[i].len;
+
+ if (next_sg != 0) {
+ dma_scr = stm32_dma_read(dmadev, STM32_DMA_SCR(chan->id));
+ width = STM32_DMA_SCR_PSIZE_GET(dma_scr);
+ count = stm32_dma_read(dmadev, STM32_DMA_SNDTR(chan->id));
+
+ residue += count << width;
+ }
+
+ return residue;
+}
+
+static enum dma_status stm32_dma_tx_status(struct dma_chan *c,
+ dma_cookie_t cookie,
+ struct dma_tx_state *state)
+{
+ struct stm32_dma_chan *chan = to_stm32_dma_chan(c);
+ struct virt_dma_desc *vdesc;
+ enum dma_status status;
+ unsigned long flags;
+ u32 residue;
+
+ status = dma_cookie_status(c, cookie, state);
+ if ((status == DMA_COMPLETE) || (!state))
+ return status;
+
+ spin_lock_irqsave(&chan->vchan.lock, flags);
+ vdesc = vchan_find_desc(&chan->vchan, cookie);
+ if (cookie == chan->desc->vdesc.tx.cookie) {
+ residue = stm32_dma_desc_residue(chan, chan->desc,
+ chan->next_sg);
+ } else if (vdesc) {
+ residue = stm32_dma_desc_residue(chan,
+ to_stm32_dma_desc(vdesc), 0);
+ } else {
+ residue = 0;
+ }
+
+ dma_set_residue(state, residue);
+
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
+
+ return status;
+}
+
+static int stm32_dma_alloc_chan_resources(struct dma_chan *c)
+{
+ struct stm32_dma_chan *chan = to_stm32_dma_chan(c);
+ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
+ int ret;
+
+ chan->config_init = false;
+ ret = clk_prepare_enable(dmadev->clk);
+ if (ret < 0) {
+ dev_err(chan2dev(chan), "clk_prepare_enable failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = stm32_dma_disable_chan(chan);
+ if (ret < 0)
+ clk_disable_unprepare(dmadev->clk);
+
+ return ret;
+}
+
+static void stm32_dma_free_chan_resources(struct dma_chan *c)
+{
+ struct stm32_dma_chan *chan = to_stm32_dma_chan(c);
+ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
+ unsigned long flags;
+
+ dev_dbg(chan2dev(chan), "Freeing channel %d\n", chan->id);
+
+ if (chan->busy) {
+ spin_lock_irqsave(&chan->vchan.lock, flags);
+ stm32_dma_stop(chan);
+ chan->desc = NULL;
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
+ }
+
+ clk_disable_unprepare(dmadev->clk);
+
+ vchan_free_chan_resources(to_virt_chan(c));
+}
+
+static void stm32_dma_desc_free(struct virt_dma_desc *vdesc)
+{
+ kfree(container_of(vdesc, struct stm32_dma_desc, vdesc));
+}
+
+void stm32_dma_set_config(struct stm32_dma_chan *chan,
+ struct stm32_dma_cfg *cfg)
+{
+ stm32_dma_clear_reg(&chan->chan_reg);
+
+ chan->chan_reg.dma_scr = cfg->stream_config & STM32_DMA_SCR_CFG_MASK;
+ chan->chan_reg.dma_scr |= STM32_DMA_SCR_REQ(cfg->request_line);
+
+ /* Enable Interrupts */
+ chan->chan_reg.dma_scr |= STM32_DMA_SCR_TEIE | STM32_DMA_SCR_TCIE;
+
+ chan->chan_reg.dma_sfcr = cfg->threshold & STM32_DMA_SFCR_FTH_MASK;
+}
+
+static struct dma_chan *stm32_dma_of_xlate(struct of_phandle_args *dma_spec,
+ struct of_dma *ofdma)
+{
+ struct stm32_dma_device *dmadev = ofdma->of_dma_data;
+ struct stm32_dma_cfg cfg;
+ struct stm32_dma_chan *chan;
+ struct dma_chan *c;
+
+ if (dma_spec->args_count < 3)
+ return NULL;
+
+ cfg.channel_id = dma_spec->args[0];
+ cfg.request_line = dma_spec->args[1];
+ cfg.stream_config = dma_spec->args[2];
+ cfg.threshold = 0;
+
+ if ((cfg.channel_id >= STM32_DMA_MAX_CHANNELS) || (cfg.request_line >=
+ STM32_DMA_MAX_REQUEST_ID))
+ return NULL;
+
+ if (dma_spec->args_count > 3)
+ cfg.threshold = dma_spec->args[3];
+
+ chan = &dmadev->chan[cfg.channel_id];
+
+ c = dma_get_slave_channel(&chan->vchan.chan);
+ if (c)
+ stm32_dma_set_config(chan, &cfg);
+
+ return c;
+}
+
+static const struct of_device_id stm32_dma_of_match[] = {
+ { .compatible = "st,stm32-dma", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, stm32_dma_of_match);
+
+static int stm32_dma_probe(struct platform_device *pdev)
+{
+ struct stm32_dma_chan *chan;
+ struct stm32_dma_device *dmadev;
+ struct dma_device *dd;
+ const struct of_device_id *match;
+ struct resource *res;
+ int i, ret;
+
+ match = of_match_device(stm32_dma_of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+
+ dmadev = devm_kzalloc(&pdev->dev, sizeof(*dmadev), GFP_KERNEL);
+ if (!dmadev)
+ return -ENOMEM;
+
+ dd = &dmadev->ddev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dmadev->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dmadev->base))
+ return PTR_ERR(dmadev->base);
+
+ dmadev->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dmadev->clk)) {
+ dev_err(&pdev->dev, "Error: Missing controller clock\n");
+ return PTR_ERR(dmadev->clk);
+ }
+
+ dmadev->mem2mem = of_property_read_bool(pdev->dev.of_node,
+ "st,mem2mem");
+
+ dmadev->rst = devm_reset_control_get(&pdev->dev, NULL);
+ if (!IS_ERR(dmadev->rst)) {
+ reset_control_assert(dmadev->rst);
+ udelay(2);
+ reset_control_deassert(dmadev->rst);
+ }
+
+ dma_cap_set(DMA_SLAVE, dd->cap_mask);
+ dma_cap_set(DMA_PRIVATE, dd->cap_mask);
+ dma_cap_set(DMA_CYCLIC, dd->cap_mask);
+ dd->device_alloc_chan_resources = stm32_dma_alloc_chan_resources;
+ dd->device_free_chan_resources = stm32_dma_free_chan_resources;
+ dd->device_tx_status = stm32_dma_tx_status;
+ dd->device_issue_pending = stm32_dma_issue_pending;
+ dd->device_prep_slave_sg = stm32_dma_prep_slave_sg;
+ dd->device_prep_dma_cyclic = stm32_dma_prep_dma_cyclic;
+ dd->device_config = stm32_dma_slave_config;
+ dd->device_terminate_all = stm32_dma_terminate_all;
+ dd->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
+ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
+ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+ dd->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
+ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
+ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+ dd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+ dd->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
+ dd->dev = &pdev->dev;
+ INIT_LIST_HEAD(&dd->channels);
+
+ if (dmadev->mem2mem) {
+ dma_cap_set(DMA_MEMCPY, dd->cap_mask);
+ dd->device_prep_dma_memcpy = stm32_dma_prep_dma_memcpy;
+ dd->directions |= BIT(DMA_MEM_TO_MEM);
+ }
+
+ for (i = 0; i < STM32_DMA_MAX_CHANNELS; i++) {
+ chan = &dmadev->chan[i];
+ chan->id = i;
+ chan->vchan.desc_free = stm32_dma_desc_free;
+ vchan_init(&chan->vchan, dd);
+ }
+
+ ret = dma_async_device_register(dd);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < STM32_DMA_MAX_CHANNELS; i++) {
+ chan = &dmadev->chan[i];
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
+ if (!res) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev, "No irq resource for chan %d\n", i);
+ goto err_unregister;
+ }
+ chan->irq = res->start;
+ ret = devm_request_irq(&pdev->dev, chan->irq,
+ stm32_dma_chan_irq, 0,
+ dev_name(chan2dev(chan)), chan);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "request_irq failed with err %d channel %d\n",
+ ret, i);
+ goto err_unregister;
+ }
+ }
+
+ ret = of_dma_controller_register(pdev->dev.of_node,
+ stm32_dma_of_xlate, dmadev);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "STM32 DMA DMA OF registration failed %d\n", ret);
+ goto err_unregister;
+ }
+
+ platform_set_drvdata(pdev, dmadev);
+
+ dev_info(&pdev->dev, "STM32 DMA driver registered\n");
+
+ return 0;
+
+err_unregister:
+ dma_async_device_unregister(dd);
+
+ return ret;
+}
+
+static struct platform_driver stm32_dma_driver = {
+ .driver = {
+ .name = "stm32-dma",
+ .of_match_table = stm32_dma_of_match,
+ },
+};
+
+static int __init stm32_dma_init(void)
+{
+ return platform_driver_probe(&stm32_dma_driver, stm32_dma_probe);
+}
+subsys_initcall(stm32_dma_init);
diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c
index c8f79dcaaee8..935da8192f59 100644
--- a/drivers/dma/tegra20-apb-dma.c
+++ b/drivers/dma/tegra20-apb-dma.c
@@ -296,7 +296,7 @@ static struct tegra_dma_desc *tegra_dma_desc_get(
spin_unlock_irqrestore(&tdc->lock, flags);
/* Allocate DMA desc */
- dma_desc = kzalloc(sizeof(*dma_desc), GFP_ATOMIC);
+ dma_desc = kzalloc(sizeof(*dma_desc), GFP_NOWAIT);
if (!dma_desc) {
dev_err(tdc2dev(tdc), "dma_desc alloc failed\n");
return NULL;
@@ -336,7 +336,7 @@ static struct tegra_dma_sg_req *tegra_dma_sg_req_get(
}
spin_unlock_irqrestore(&tdc->lock, flags);
- sg_req = kzalloc(sizeof(struct tegra_dma_sg_req), GFP_ATOMIC);
+ sg_req = kzalloc(sizeof(struct tegra_dma_sg_req), GFP_NOWAIT);
if (!sg_req)
dev_err(tdc2dev(tdc), "sg_req alloc failed\n");
return sg_req;
@@ -1186,10 +1186,12 @@ static int tegra_dma_alloc_chan_resources(struct dma_chan *dc)
dma_cookie_init(&tdc->dma_chan);
tdc->config_init = false;
- ret = clk_prepare_enable(tdma->dma_clk);
+
+ ret = pm_runtime_get_sync(tdma->dev);
if (ret < 0)
- dev_err(tdc2dev(tdc), "clk_prepare_enable failed: %d\n", ret);
- return ret;
+ return ret;
+
+ return 0;
}
static void tegra_dma_free_chan_resources(struct dma_chan *dc)
@@ -1232,7 +1234,7 @@ static void tegra_dma_free_chan_resources(struct dma_chan *dc)
list_del(&sg_req->node);
kfree(sg_req);
}
- clk_disable_unprepare(tdma->dma_clk);
+ pm_runtime_put(tdma->dev);
tdc->slave_id = 0;
}
@@ -1356,20 +1358,14 @@ static int tegra_dma_probe(struct platform_device *pdev)
spin_lock_init(&tdma->global_lock);
pm_runtime_enable(&pdev->dev);
- if (!pm_runtime_enabled(&pdev->dev)) {
+ if (!pm_runtime_enabled(&pdev->dev))
ret = tegra_dma_runtime_resume(&pdev->dev);
- if (ret) {
- dev_err(&pdev->dev, "dma_runtime_resume failed %d\n",
- ret);
- goto err_pm_disable;
- }
- }
+ else
+ ret = pm_runtime_get_sync(&pdev->dev);
- /* Enable clock before accessing registers */
- ret = clk_prepare_enable(tdma->dma_clk);
if (ret < 0) {
- dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
- goto err_pm_disable;
+ pm_runtime_disable(&pdev->dev);
+ return ret;
}
/* Reset DMA controller */
@@ -1382,7 +1378,7 @@ static int tegra_dma_probe(struct platform_device *pdev)
tdma_write(tdma, TEGRA_APBDMA_CONTROL, 0);
tdma_write(tdma, TEGRA_APBDMA_IRQ_MASK_SET, 0xFFFFFFFFul);
- clk_disable_unprepare(tdma->dma_clk);
+ pm_runtime_put(&pdev->dev);
INIT_LIST_HEAD(&tdma->dma_dev.channels);
for (i = 0; i < cdata->nr_channels; i++) {
@@ -1400,8 +1396,7 @@ static int tegra_dma_probe(struct platform_device *pdev)
}
tdc->irq = res->start;
snprintf(tdc->name, sizeof(tdc->name), "apbdma.%d", i);
- ret = devm_request_irq(&pdev->dev, tdc->irq,
- tegra_dma_isr, 0, tdc->name, tdc);
+ ret = request_irq(tdc->irq, tegra_dma_isr, 0, tdc->name, tdc);
if (ret) {
dev_err(&pdev->dev,
"request_irq failed with err %d channel %d\n",
@@ -1482,10 +1477,11 @@ err_unregister_dma_dev:
err_irq:
while (--i >= 0) {
struct tegra_dma_channel *tdc = &tdma->channels[i];
+
+ free_irq(tdc->irq, tdc);
tasklet_kill(&tdc->tasklet);
}
-err_pm_disable:
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
tegra_dma_runtime_suspend(&pdev->dev);
@@ -1502,6 +1498,7 @@ static int tegra_dma_remove(struct platform_device *pdev)
for (i = 0; i < tdma->chip_data->nr_channels; ++i) {
tdc = &tdma->channels[i];
+ free_irq(tdc->irq, tdc);
tasklet_kill(&tdc->tasklet);
}
@@ -1514,8 +1511,7 @@ static int tegra_dma_remove(struct platform_device *pdev)
static int tegra_dma_runtime_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct tegra_dma *tdma = platform_get_drvdata(pdev);
+ struct tegra_dma *tdma = dev_get_drvdata(dev);
clk_disable_unprepare(tdma->dma_clk);
return 0;
@@ -1523,8 +1519,7 @@ static int tegra_dma_runtime_suspend(struct device *dev)
static int tegra_dma_runtime_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct tegra_dma *tdma = platform_get_drvdata(pdev);
+ struct tegra_dma *tdma = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(tdma->dma_clk);
@@ -1543,7 +1538,7 @@ static int tegra_dma_pm_suspend(struct device *dev)
int ret;
/* Enable clock before accessing register */
- ret = tegra_dma_runtime_resume(dev);
+ ret = pm_runtime_get_sync(dev);
if (ret < 0)
return ret;
@@ -1552,15 +1547,22 @@ static int tegra_dma_pm_suspend(struct device *dev)
struct tegra_dma_channel *tdc = &tdma->channels[i];
struct tegra_dma_channel_regs *ch_reg = &tdc->channel_reg;
+ /* Only save the state of DMA channels that are in use */
+ if (!tdc->config_init)
+ continue;
+
ch_reg->csr = tdc_read(tdc, TEGRA_APBDMA_CHAN_CSR);
ch_reg->ahb_ptr = tdc_read(tdc, TEGRA_APBDMA_CHAN_AHBPTR);
ch_reg->apb_ptr = tdc_read(tdc, TEGRA_APBDMA_CHAN_APBPTR);
ch_reg->ahb_seq = tdc_read(tdc, TEGRA_APBDMA_CHAN_AHBSEQ);
ch_reg->apb_seq = tdc_read(tdc, TEGRA_APBDMA_CHAN_APBSEQ);
+ if (tdma->chip_data->support_separate_wcount_reg)
+ ch_reg->wcount = tdc_read(tdc,
+ TEGRA_APBDMA_CHAN_WCOUNT);
}
/* Disable clock */
- tegra_dma_runtime_suspend(dev);
+ pm_runtime_put(dev);
return 0;
}
@@ -1571,7 +1573,7 @@ static int tegra_dma_pm_resume(struct device *dev)
int ret;
/* Enable clock before accessing register */
- ret = tegra_dma_runtime_resume(dev);
+ ret = pm_runtime_get_sync(dev);
if (ret < 0)
return ret;
@@ -1583,6 +1585,13 @@ static int tegra_dma_pm_resume(struct device *dev)
struct tegra_dma_channel *tdc = &tdma->channels[i];
struct tegra_dma_channel_regs *ch_reg = &tdc->channel_reg;
+ /* Only restore the state of DMA channels that are in use */
+ if (!tdc->config_init)
+ continue;
+
+ if (tdma->chip_data->support_separate_wcount_reg)
+ tdc_write(tdc, TEGRA_APBDMA_CHAN_WCOUNT,
+ ch_reg->wcount);
tdc_write(tdc, TEGRA_APBDMA_CHAN_APBSEQ, ch_reg->apb_seq);
tdc_write(tdc, TEGRA_APBDMA_CHAN_APBPTR, ch_reg->apb_ptr);
tdc_write(tdc, TEGRA_APBDMA_CHAN_AHBSEQ, ch_reg->ahb_seq);
@@ -1592,16 +1601,14 @@ static int tegra_dma_pm_resume(struct device *dev)
}
/* Disable clock */
- tegra_dma_runtime_suspend(dev);
+ pm_runtime_put(dev);
return 0;
}
#endif
static const struct dev_pm_ops tegra_dma_dev_pm_ops = {
-#ifdef CONFIG_PM
- .runtime_suspend = tegra_dma_runtime_suspend,
- .runtime_resume = tegra_dma_runtime_resume,
-#endif
+ SET_RUNTIME_PM_OPS(tegra_dma_runtime_suspend, tegra_dma_runtime_resume,
+ NULL)
SET_SYSTEM_SLEEP_PM_OPS(tegra_dma_pm_suspend, tegra_dma_pm_resume)
};
diff --git a/drivers/dma/ti-dma-crossbar.c b/drivers/dma/ti-dma-crossbar.c
index a415edbe61b1..e107779b1a2e 100644
--- a/drivers/dma/ti-dma-crossbar.c
+++ b/drivers/dma/ti-dma-crossbar.c
@@ -12,7 +12,6 @@
#include <linux/init.h>
#include <linux/list.h>
#include <linux/io.h>
-#include <linux/idr.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_dma.h>
@@ -198,7 +197,8 @@ struct ti_dra7_xbar_data {
void __iomem *iomem;
struct dma_router dmarouter;
- struct idr map_idr;
+ struct mutex mutex;
+ unsigned long *dma_inuse;
u16 safe_val; /* Value to rest the crossbar lines */
u32 xbar_requests; /* number of DMA requests connected to XBAR */
@@ -225,7 +225,9 @@ static void ti_dra7_xbar_free(struct device *dev, void *route_data)
map->xbar_in, map->xbar_out);
ti_dra7_xbar_write(xbar->iomem, map->xbar_out, xbar->safe_val);
- idr_remove(&xbar->map_idr, map->xbar_out);
+ mutex_lock(&xbar->mutex);
+ clear_bit(map->xbar_out, xbar->dma_inuse);
+ mutex_unlock(&xbar->mutex);
kfree(map);
}
@@ -255,8 +257,17 @@ static void *ti_dra7_xbar_route_allocate(struct of_phandle_args *dma_spec,
return ERR_PTR(-ENOMEM);
}
- map->xbar_out = idr_alloc(&xbar->map_idr, NULL, 0, xbar->dma_requests,
- GFP_KERNEL);
+ mutex_lock(&xbar->mutex);
+ map->xbar_out = find_first_zero_bit(xbar->dma_inuse,
+ xbar->dma_requests);
+ mutex_unlock(&xbar->mutex);
+ if (map->xbar_out == xbar->dma_requests) {
+ dev_err(&pdev->dev, "Run out of free DMA requests\n");
+ kfree(map);
+ return ERR_PTR(-ENOMEM);
+ }
+ set_bit(map->xbar_out, xbar->dma_inuse);
+
map->xbar_in = (u16)dma_spec->args[0];
dma_spec->args[0] = map->xbar_out + xbar->dma_offset;
@@ -278,17 +289,29 @@ static const struct of_device_id ti_dra7_master_match[] = {
.compatible = "ti,edma3",
.data = (void *)TI_XBAR_EDMA_OFFSET,
},
+ {
+ .compatible = "ti,edma3-tpcc",
+ .data = (void *)TI_XBAR_EDMA_OFFSET,
+ },
{},
};
+static inline void ti_dra7_xbar_reserve(int offset, int len, unsigned long *p)
+{
+ for (; len > 0; len--)
+ clear_bit(offset + (len - 1), p);
+}
+
static int ti_dra7_xbar_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
const struct of_device_id *match;
struct device_node *dma_node;
struct ti_dra7_xbar_data *xbar;
+ struct property *prop;
struct resource *res;
u32 safe_val;
+ size_t sz;
void __iomem *iomem;
int i, ret;
@@ -299,8 +322,6 @@ static int ti_dra7_xbar_probe(struct platform_device *pdev)
if (!xbar)
return -ENOMEM;
- idr_init(&xbar->map_idr);
-
dma_node = of_parse_phandle(node, "dma-masters", 0);
if (!dma_node) {
dev_err(&pdev->dev, "Can't get DMA master node\n");
@@ -322,6 +343,12 @@ static int ti_dra7_xbar_probe(struct platform_device *pdev)
}
of_node_put(dma_node);
+ xbar->dma_inuse = devm_kcalloc(&pdev->dev,
+ BITS_TO_LONGS(xbar->dma_requests),
+ sizeof(unsigned long), GFP_KERNEL);
+ if (!xbar->dma_inuse)
+ return -ENOMEM;
+
if (of_property_read_u32(node, "dma-requests", &xbar->xbar_requests)) {
dev_info(&pdev->dev,
"Missing XBAR input information, using %u.\n",
@@ -332,6 +359,33 @@ static int ti_dra7_xbar_probe(struct platform_device *pdev)
if (!of_property_read_u32(node, "ti,dma-safe-map", &safe_val))
xbar->safe_val = (u16)safe_val;
+
+ prop = of_find_property(node, "ti,reserved-dma-request-ranges", &sz);
+ if (prop) {
+ const char pname[] = "ti,reserved-dma-request-ranges";
+ u32 (*rsv_events)[2];
+ size_t nelm = sz / sizeof(*rsv_events);
+ int i;
+
+ if (!nelm)
+ return -EINVAL;
+
+ rsv_events = kcalloc(nelm, sizeof(*rsv_events), GFP_KERNEL);
+ if (!rsv_events)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(node, pname, (u32 *)rsv_events,
+ nelm * 2);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < nelm; i++) {
+ ti_dra7_xbar_reserve(rsv_events[i][0], rsv_events[i][1],
+ xbar->dma_inuse);
+ }
+ kfree(rsv_events);
+ }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
iomem = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(iomem))
@@ -343,18 +397,23 @@ static int ti_dra7_xbar_probe(struct platform_device *pdev)
xbar->dmarouter.route_free = ti_dra7_xbar_free;
xbar->dma_offset = (u32)match->data;
+ mutex_init(&xbar->mutex);
platform_set_drvdata(pdev, xbar);
/* Reset the crossbar */
- for (i = 0; i < xbar->dma_requests; i++)
- ti_dra7_xbar_write(xbar->iomem, i, xbar->safe_val);
+ for (i = 0; i < xbar->dma_requests; i++) {
+ if (!test_bit(i, xbar->dma_inuse))
+ ti_dra7_xbar_write(xbar->iomem, i, xbar->safe_val);
+ }
ret = of_dma_router_register(node, ti_dra7_xbar_route_allocate,
&xbar->dmarouter);
if (ret) {
/* Restore the defaults for the crossbar */
- for (i = 0; i < xbar->dma_requests; i++)
- ti_dra7_xbar_write(xbar->iomem, i, i);
+ for (i = 0; i < xbar->dma_requests; i++) {
+ if (!test_bit(i, xbar->dma_inuse))
+ ti_dra7_xbar_write(xbar->iomem, i, i);
+ }
}
return ret;
diff --git a/drivers/dma/virt-dma.c b/drivers/dma/virt-dma.c
index 6f80432a3f0a..a35c211857dd 100644
--- a/drivers/dma/virt-dma.c
+++ b/drivers/dma/virt-dma.c
@@ -29,7 +29,7 @@ dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *tx)
spin_lock_irqsave(&vc->lock, flags);
cookie = dma_cookie_assign(tx);
- list_add_tail(&vd->node, &vc->desc_submitted);
+ list_move_tail(&vd->node, &vc->desc_submitted);
spin_unlock_irqrestore(&vc->lock, flags);
dev_dbg(vc->chan.device->dev, "vchan %p: txd %p[%x]: submitted\n",
@@ -39,6 +39,33 @@ dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *tx)
}
EXPORT_SYMBOL_GPL(vchan_tx_submit);
+/**
+ * vchan_tx_desc_free - free a reusable descriptor
+ * @tx: the transfer
+ *
+ * This function frees a previously allocated reusable descriptor. The only
+ * other way is to clear the DMA_CTRL_REUSE flag and submit one last time the
+ * transfer.
+ *
+ * Returns 0 upon success
+ */
+int vchan_tx_desc_free(struct dma_async_tx_descriptor *tx)
+{
+ struct virt_dma_chan *vc = to_virt_chan(tx->chan);
+ struct virt_dma_desc *vd = to_virt_desc(tx);
+ unsigned long flags;
+
+ spin_lock_irqsave(&vc->lock, flags);
+ list_del(&vd->node);
+ spin_unlock_irqrestore(&vc->lock, flags);
+
+ dev_dbg(vc->chan.device->dev, "vchan %p: txd %p[%x]: freeing\n",
+ vc, vd, vd->tx.cookie);
+ vc->desc_free(vd);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vchan_tx_desc_free);
+
struct virt_dma_desc *vchan_find_desc(struct virt_dma_chan *vc,
dma_cookie_t cookie)
{
@@ -83,8 +110,10 @@ static void vchan_complete(unsigned long arg)
cb_data = vd->tx.callback_param;
list_del(&vd->node);
-
- vc->desc_free(vd);
+ if (dmaengine_desc_test_reuse(&vd->tx))
+ list_add(&vd->node, &vc->desc_allocated);
+ else
+ vc->desc_free(vd);
if (cb)
cb(cb_data);
@@ -96,9 +125,13 @@ void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head)
while (!list_empty(head)) {
struct virt_dma_desc *vd = list_first_entry(head,
struct virt_dma_desc, node);
- list_del(&vd->node);
- dev_dbg(vc->chan.device->dev, "txd %p: freeing\n", vd);
- vc->desc_free(vd);
+ if (dmaengine_desc_test_reuse(&vd->tx)) {
+ list_move_tail(&vd->node, &vc->desc_allocated);
+ } else {
+ dev_dbg(vc->chan.device->dev, "txd %p: freeing\n", vd);
+ list_del(&vd->node);
+ vc->desc_free(vd);
+ }
}
}
EXPORT_SYMBOL_GPL(vchan_dma_desc_free_list);
@@ -108,6 +141,7 @@ void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev)
dma_cookie_init(&vc->chan);
spin_lock_init(&vc->lock);
+ INIT_LIST_HEAD(&vc->desc_allocated);
INIT_LIST_HEAD(&vc->desc_submitted);
INIT_LIST_HEAD(&vc->desc_issued);
INIT_LIST_HEAD(&vc->desc_completed);
diff --git a/drivers/dma/virt-dma.h b/drivers/dma/virt-dma.h
index 2fa47745a41f..d9731ca5e262 100644
--- a/drivers/dma/virt-dma.h
+++ b/drivers/dma/virt-dma.h
@@ -29,6 +29,7 @@ struct virt_dma_chan {
spinlock_t lock;
/* protected by vc.lock */
+ struct list_head desc_allocated;
struct list_head desc_submitted;
struct list_head desc_issued;
struct list_head desc_completed;
@@ -55,10 +56,17 @@ static inline struct dma_async_tx_descriptor *vchan_tx_prep(struct virt_dma_chan
struct virt_dma_desc *vd, unsigned long tx_flags)
{
extern dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *);
+ extern int vchan_tx_desc_free(struct dma_async_tx_descriptor *);
+ unsigned long flags;
dma_async_tx_descriptor_init(&vd->tx, &vc->chan);
vd->tx.flags = tx_flags;
vd->tx.tx_submit = vchan_tx_submit;
+ vd->tx.desc_free = vchan_tx_desc_free;
+
+ spin_lock_irqsave(&vc->lock, flags);
+ list_add_tail(&vd->node, &vc->desc_allocated);
+ spin_unlock_irqrestore(&vc->lock, flags);
return &vd->tx;
}
@@ -134,6 +142,7 @@ static inline struct virt_dma_desc *vchan_next_desc(struct virt_dma_chan *vc)
static inline void vchan_get_all_descriptors(struct virt_dma_chan *vc,
struct list_head *head)
{
+ list_splice_tail_init(&vc->desc_allocated, head);
list_splice_tail_init(&vc->desc_submitted, head);
list_splice_tail_init(&vc->desc_issued, head);
list_splice_tail_init(&vc->desc_completed, head);
@@ -141,14 +150,30 @@ static inline void vchan_get_all_descriptors(struct virt_dma_chan *vc,
static inline void vchan_free_chan_resources(struct virt_dma_chan *vc)
{
+ struct virt_dma_desc *vd;
unsigned long flags;
LIST_HEAD(head);
spin_lock_irqsave(&vc->lock, flags);
vchan_get_all_descriptors(vc, &head);
+ list_for_each_entry(vd, &head, node)
+ dmaengine_desc_clear_reuse(&vd->tx);
spin_unlock_irqrestore(&vc->lock, flags);
vchan_dma_desc_free_list(vc, &head);
}
+/**
+ * vchan_synchronize() - synchronize callback execution to the current context
+ * @vc: virtual channel to synchronize
+ *
+ * Makes sure that all scheduled or active callbacks have finished running. For
+ * proper operation the caller has to ensure that no new callbacks are scheduled
+ * after the invocation of this function started.
+ */
+static inline void vchan_synchronize(struct virt_dma_chan *vc)
+{
+ tasklet_kill(&vc->task);
+}
+
#endif
diff --git a/drivers/dma/xgene-dma.c b/drivers/dma/xgene-dma.c
index 9dfa2b0fa5da..9cb93c5b655d 100644
--- a/drivers/dma/xgene-dma.c
+++ b/drivers/dma/xgene-dma.c
@@ -29,6 +29,7 @@
#include <linux/dmapool.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of_device.h>
@@ -1610,6 +1611,7 @@ static int xgene_dma_request_irqs(struct xgene_dma *pdma)
/* Register DMA channel rx irq */
for (i = 0; i < XGENE_DMA_MAX_CHANNEL; i++) {
chan = &pdma->chan[i];
+ irq_set_status_flags(chan->rx_irq, IRQ_DISABLE_UNLAZY);
ret = devm_request_irq(chan->dev, chan->rx_irq,
xgene_dma_chan_ring_isr,
0, chan->name, chan);
@@ -1620,6 +1622,7 @@ static int xgene_dma_request_irqs(struct xgene_dma *pdma)
for (j = 0; j < i; j++) {
chan = &pdma->chan[i];
+ irq_clear_status_flags(chan->rx_irq, IRQ_DISABLE_UNLAZY);
devm_free_irq(chan->dev, chan->rx_irq, chan);
}
@@ -1640,6 +1643,7 @@ static void xgene_dma_free_irqs(struct xgene_dma *pdma)
for (i = 0; i < XGENE_DMA_MAX_CHANNEL; i++) {
chan = &pdma->chan[i];
+ irq_clear_status_flags(chan->rx_irq, IRQ_DISABLE_UNLAZY);
devm_free_irq(chan->dev, chan->rx_irq, chan);
}
}
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index dbf53e08bdd1..be163e20fe56 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -10,7 +10,7 @@ obj-$(CONFIG_EDAC) := edac_stub.o
obj-$(CONFIG_EDAC_MM_EDAC) += edac_core.o
edac_core-y := edac_mc.o edac_device.o edac_mc_sysfs.o
-edac_core-y += edac_module.o edac_device_sysfs.o
+edac_core-y += edac_module.o edac_device_sysfs.o wq.o
edac_core-$(CONFIG_EDAC_DEBUG) += debugfs.o
diff --git a/drivers/edac/edac_device.c b/drivers/edac/edac_device.c
index 592af5f0cf39..a97900333e2d 100644
--- a/drivers/edac/edac_device.c
+++ b/drivers/edac/edac_device.c
@@ -390,11 +390,9 @@ static void edac_device_workq_function(struct work_struct *work_req)
* between integral seconds
*/
if (edac_dev->poll_msec == 1000)
- queue_delayed_work(edac_workqueue, &edac_dev->work,
- round_jiffies_relative(edac_dev->delay));
+ edac_queue_work(&edac_dev->work, round_jiffies_relative(edac_dev->delay));
else
- queue_delayed_work(edac_workqueue, &edac_dev->work,
- edac_dev->delay);
+ edac_queue_work(&edac_dev->work, edac_dev->delay);
}
/*
@@ -402,8 +400,8 @@ static void edac_device_workq_function(struct work_struct *work_req)
* initialize a workq item for this edac_device instance
* passing in the new delay period in msec
*/
-void edac_device_workq_setup(struct edac_device_ctl_info *edac_dev,
- unsigned msec)
+static void edac_device_workq_setup(struct edac_device_ctl_info *edac_dev,
+ unsigned msec)
{
edac_dbg(0, "\n");
@@ -422,29 +420,23 @@ void edac_device_workq_setup(struct edac_device_ctl_info *edac_dev,
* to fire together on the 1 second exactly
*/
if (edac_dev->poll_msec == 1000)
- queue_delayed_work(edac_workqueue, &edac_dev->work,
- round_jiffies_relative(edac_dev->delay));
+ edac_queue_work(&edac_dev->work, round_jiffies_relative(edac_dev->delay));
else
- queue_delayed_work(edac_workqueue, &edac_dev->work,
- edac_dev->delay);
+ edac_queue_work(&edac_dev->work, edac_dev->delay);
}
/*
* edac_device_workq_teardown
* stop the workq processing on this edac_dev
*/
-void edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev)
+static void edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev)
{
- int status;
-
if (!edac_dev->edac_check)
return;
- status = cancel_delayed_work(&edac_dev->work);
- if (status == 0) {
- /* workq instance might be running, wait for it */
- flush_workqueue(edac_workqueue);
- }
+ edac_dev->op_state = OP_OFFLINE;
+
+ edac_stop_work(&edac_dev->work);
}
/*
@@ -457,16 +449,15 @@ void edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev)
void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev,
unsigned long value)
{
- /* cancel the current workq request, without the mutex lock */
- edac_device_workq_teardown(edac_dev);
+ unsigned long jiffs = msecs_to_jiffies(value);
- /* acquire the mutex before doing the workq setup */
- mutex_lock(&device_ctls_mutex);
+ if (value == 1000)
+ jiffs = round_jiffies_relative(value);
- /* restart the workq request, with new delay value */
- edac_device_workq_setup(edac_dev, value);
+ edac_dev->poll_msec = value;
+ edac_dev->delay = jiffs;
- mutex_unlock(&device_ctls_mutex);
+ edac_mod_work(&edac_dev->work, jiffs);
}
/*
diff --git a/drivers/edac/edac_device_sysfs.c b/drivers/edac/edac_device_sysfs.c
index fb68a06ad683..93da1a45c716 100644
--- a/drivers/edac/edac_device_sysfs.c
+++ b/drivers/edac/edac_device_sysfs.c
@@ -237,11 +237,6 @@ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev)
/* get the /sys/devices/system/edac reference */
edac_subsys = edac_get_sysfs_subsys();
- if (edac_subsys == NULL) {
- edac_dbg(1, "no edac_subsys error\n");
- err = -ENODEV;
- goto err_out;
- }
/* Point to the 'edac_subsys' this instance 'reports' to */
edac_dev->edac_subsys = edac_subsys;
@@ -256,7 +251,7 @@ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev)
if (!try_module_get(edac_dev->owner)) {
err = -ENODEV;
- goto err_mod_get;
+ goto err_out;
}
/* register */
@@ -282,9 +277,6 @@ int edac_device_register_sysfs_main_kobj(struct edac_device_ctl_info *edac_dev)
err_kobj_reg:
module_put(edac_dev->owner);
-err_mod_get:
- edac_put_sysfs_subsys();
-
err_out:
return err;
}
@@ -306,7 +298,6 @@ void edac_device_unregister_sysfs_main_kobj(struct edac_device_ctl_info *dev)
* b) 'kfree' the memory
*/
kobject_put(&dev->kobj);
- edac_put_sysfs_subsys();
}
/* edac_dev -> instance information */
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index 77ecd6a4179a..8adfc167c2e3 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -548,8 +548,7 @@ static void edac_mc_workq_function(struct work_struct *work_req)
mutex_unlock(&mem_ctls_mutex);
/* Reschedule */
- queue_delayed_work(edac_workqueue, &mci->work,
- msecs_to_jiffies(edac_mc_get_poll_msec()));
+ edac_queue_work(&mci->work, msecs_to_jiffies(edac_mc_get_poll_msec()));
}
/*
@@ -561,8 +560,7 @@ static void edac_mc_workq_function(struct work_struct *work_req)
*
* called with the mem_ctls_mutex held
*/
-static void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec,
- bool init)
+static void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec)
{
edac_dbg(0, "\n");
@@ -570,10 +568,9 @@ static void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec,
if (mci->op_state != OP_RUNNING_POLL)
return;
- if (init)
- INIT_DELAYED_WORK(&mci->work, edac_mc_workq_function);
+ INIT_DELAYED_WORK(&mci->work, edac_mc_workq_function);
- mod_delayed_work(edac_workqueue, &mci->work, msecs_to_jiffies(msec));
+ edac_queue_work(&mci->work, msecs_to_jiffies(msec));
}
/*
@@ -586,18 +583,9 @@ static void edac_mc_workq_setup(struct mem_ctl_info *mci, unsigned msec,
*/
static void edac_mc_workq_teardown(struct mem_ctl_info *mci)
{
- int status;
-
- if (mci->op_state != OP_RUNNING_POLL)
- return;
-
- status = cancel_delayed_work(&mci->work);
- if (status == 0) {
- edac_dbg(0, "not canceled, flush the queue\n");
+ mci->op_state = OP_OFFLINE;
- /* workq instance might be running, wait for it */
- flush_workqueue(edac_workqueue);
- }
+ edac_stop_work(&mci->work);
}
/*
@@ -616,9 +604,8 @@ void edac_mc_reset_delay_period(unsigned long value)
list_for_each(item, &mc_devices) {
mci = list_entry(item, struct mem_ctl_info, link);
- edac_mc_workq_setup(mci, value, false);
+ edac_mod_work(&mci->work, value);
}
-
mutex_unlock(&mem_ctls_mutex);
}
@@ -789,7 +776,7 @@ int edac_mc_add_mc_with_groups(struct mem_ctl_info *mci,
/* This instance is NOW RUNNING */
mci->op_state = OP_RUNNING_POLL;
- edac_mc_workq_setup(mci, edac_mc_get_poll_msec(), true);
+ edac_mc_workq_setup(mci, edac_mc_get_poll_msec());
} else {
mci->op_state = OP_RUNNING_INTERRUPT;
}
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index a75acea0f674..26e65ab5932a 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -880,21 +880,26 @@ static struct device_type mci_attr_type = {
int edac_create_sysfs_mci_device(struct mem_ctl_info *mci,
const struct attribute_group **groups)
{
+ char *name;
int i, err;
/*
* The memory controller needs its own bus, in order to avoid
* namespace conflicts at /sys/bus/edac.
*/
- mci->bus->name = kasprintf(GFP_KERNEL, "mc%d", mci->mc_idx);
- if (!mci->bus->name)
+ name = kasprintf(GFP_KERNEL, "mc%d", mci->mc_idx);
+ if (!name)
return -ENOMEM;
+ mci->bus->name = name;
+
edac_dbg(0, "creating bus %s\n", mci->bus->name);
err = bus_register(mci->bus);
- if (err < 0)
- goto fail_free_name;
+ if (err < 0) {
+ kfree(name);
+ return err;
+ }
/* get the /sys/devices/system/edac subsys reference */
mci->dev.type = &mci_attr_type;
@@ -961,8 +966,8 @@ fail_unregister_dimm:
device_unregister(&mci->dev);
fail_unregister_bus:
bus_unregister(mci->bus);
-fail_free_name:
- kfree(mci->bus->name);
+ kfree(name);
+
return err;
}
@@ -993,10 +998,12 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
void edac_unregister_sysfs(struct mem_ctl_info *mci)
{
+ const char *name = mci->bus->name;
+
edac_dbg(1, "Unregistering device %s\n", dev_name(&mci->dev));
device_unregister(&mci->dev);
bus_unregister(mci->bus);
- kfree(mci->bus->name);
+ kfree(name);
}
static void mc_attr_release(struct device *dev)
@@ -1018,24 +1025,15 @@ static struct device_type mc_attr_type = {
*/
int __init edac_mc_sysfs_init(void)
{
- struct bus_type *edac_subsys;
int err;
- /* get the /sys/devices/system/edac subsys reference */
- edac_subsys = edac_get_sysfs_subsys();
- if (edac_subsys == NULL) {
- edac_dbg(1, "no edac_subsys\n");
- err = -EINVAL;
- goto out;
- }
-
mci_pdev = kzalloc(sizeof(*mci_pdev), GFP_KERNEL);
if (!mci_pdev) {
err = -ENOMEM;
- goto out_put_sysfs;
+ goto out;
}
- mci_pdev->bus = edac_subsys;
+ mci_pdev->bus = edac_get_sysfs_subsys();
mci_pdev->type = &mc_attr_type;
device_initialize(mci_pdev);
dev_set_name(mci_pdev, "mc");
@@ -1050,8 +1048,6 @@ int __init edac_mc_sysfs_init(void)
out_dev_free:
kfree(mci_pdev);
- out_put_sysfs:
- edac_put_sysfs_subsys();
out:
return err;
}
@@ -1059,5 +1055,4 @@ int __init edac_mc_sysfs_init(void)
void edac_mc_sysfs_exit(void)
{
device_unregister(mci_pdev);
- edac_put_sysfs_subsys();
}
diff --git a/drivers/edac/edac_module.c b/drivers/edac/edac_module.c
index 9cb082a19d8a..5f8543be995a 100644
--- a/drivers/edac/edac_module.c
+++ b/drivers/edac/edac_module.c
@@ -43,9 +43,6 @@ module_param_call(edac_debug_level, edac_set_debug_level, param_get_int,
MODULE_PARM_DESC(edac_debug_level, "EDAC debug level: [0-4], default: 2");
#endif
-/* scope is to module level only */
-struct workqueue_struct *edac_workqueue;
-
/*
* edac_op_state_to_string()
*/
@@ -66,31 +63,37 @@ char *edac_op_state_to_string(int opstate)
}
/*
- * edac_workqueue_setup
- * initialize the edac work queue for polling operations
+ * sysfs object: /sys/devices/system/edac
+ * need to export to other files
*/
-static int edac_workqueue_setup(void)
+static struct bus_type edac_subsys = {
+ .name = "edac",
+ .dev_name = "edac",
+};
+
+static int edac_subsys_init(void)
{
- edac_workqueue = create_singlethread_workqueue("edac-poller");
- if (edac_workqueue == NULL)
- return -ENODEV;
- else
- return 0;
+ int err;
+
+ /* create the /sys/devices/system/edac directory */
+ err = subsys_system_register(&edac_subsys, NULL);
+ if (err)
+ printk(KERN_ERR "Error registering toplevel EDAC sysfs dir\n");
+
+ return err;
}
-/*
- * edac_workqueue_teardown
- * teardown the edac workqueue
- */
-static void edac_workqueue_teardown(void)
+static void edac_subsys_exit(void)
{
- if (edac_workqueue) {
- flush_workqueue(edac_workqueue);
- destroy_workqueue(edac_workqueue);
- edac_workqueue = NULL;
- }
+ bus_unregister(&edac_subsys);
}
+/* return pointer to the 'edac' node in sysfs */
+struct bus_type *edac_get_sysfs_subsys(void)
+{
+ return &edac_subsys;
+}
+EXPORT_SYMBOL_GPL(edac_get_sysfs_subsys);
/*
* edac_init
* module initialization entry point
@@ -101,6 +104,10 @@ static int __init edac_init(void)
edac_printk(KERN_INFO, EDAC_MC, EDAC_VERSION "\n");
+ err = edac_subsys_init();
+ if (err)
+ return err;
+
/*
* Harvest and clear any boot/initialization PCI parity errors
*
@@ -129,6 +136,8 @@ err_wq:
edac_mc_sysfs_exit();
err_sysfs:
+ edac_subsys_exit();
+
return err;
}
@@ -144,6 +153,7 @@ static void __exit edac_exit(void)
edac_workqueue_teardown();
edac_mc_sysfs_exit();
edac_debugfs_exit();
+ edac_subsys_exit();
}
/*
diff --git a/drivers/edac/edac_module.h b/drivers/edac/edac_module.h
index b95a48fc723d..cfaacb99c973 100644
--- a/drivers/edac/edac_module.h
+++ b/drivers/edac/edac_module.h
@@ -47,10 +47,12 @@ extern int edac_device_create_sysfs(struct edac_device_ctl_info *edac_dev);
extern void edac_device_remove_sysfs(struct edac_device_ctl_info *edac_dev);
/* edac core workqueue: single CPU mode */
-extern struct workqueue_struct *edac_workqueue;
-extern void edac_device_workq_setup(struct edac_device_ctl_info *edac_dev,
- unsigned msec);
-extern void edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev);
+int edac_workqueue_setup(void);
+void edac_workqueue_teardown(void);
+bool edac_queue_work(struct delayed_work *work, unsigned long delay);
+bool edac_stop_work(struct delayed_work *work);
+bool edac_mod_work(struct delayed_work *work, unsigned long delay);
+
extern void edac_device_reset_delay_period(struct edac_device_ctl_info
*edac_dev, unsigned long value);
extern void edac_mc_reset_delay_period(unsigned long value);
diff --git a/drivers/edac/edac_pci.c b/drivers/edac/edac_pci.c
index 2cf44b4db80c..99685388d3fb 100644
--- a/drivers/edac/edac_pci.c
+++ b/drivers/edac/edac_pci.c
@@ -178,41 +178,6 @@ static void del_edac_pci_from_global_list(struct edac_pci_ctl_info *pci)
INIT_LIST_HEAD(&pci->link);
}
-#if 0
-/* Older code, but might use in the future */
-
-/*
- * edac_pci_find()
- * Search for an edac_pci_ctl_info structure whose index is 'idx'
- *
- * If found, return a pointer to the structure
- * Else return NULL.
- *
- * Caller must hold pci_ctls_mutex.
- */
-struct edac_pci_ctl_info *edac_pci_find(int idx)
-{
- struct list_head *item;
- struct edac_pci_ctl_info *pci;
-
- /* Iterage over list, looking for exact match of ID */
- list_for_each(item, &edac_pci_list) {
- pci = list_entry(item, struct edac_pci_ctl_info, link);
-
- if (pci->pci_idx >= idx) {
- if (pci->pci_idx == idx)
- return pci;
-
- /* not on list, so terminate early */
- break;
- }
- }
-
- return NULL;
-}
-EXPORT_SYMBOL_GPL(edac_pci_find);
-#endif
-
/*
* edac_pci_workq_function()
*
@@ -244,7 +209,7 @@ static void edac_pci_workq_function(struct work_struct *work_req)
delay = msecs_to_jiffies(msec);
/* Reschedule only if we are in POLL mode */
- queue_delayed_work(edac_workqueue, &pci->work, delay);
+ edac_queue_work(&pci->work, delay);
}
mutex_unlock(&edac_pci_ctls_mutex);
@@ -264,8 +229,8 @@ static void edac_pci_workq_setup(struct edac_pci_ctl_info *pci,
edac_dbg(0, "\n");
INIT_DELAYED_WORK(&pci->work, edac_pci_workq_function);
- queue_delayed_work(edac_workqueue, &pci->work,
- msecs_to_jiffies(edac_pci_get_poll_msec()));
+
+ edac_queue_work(&pci->work, msecs_to_jiffies(edac_pci_get_poll_msec()));
}
/*
@@ -274,37 +239,12 @@ static void edac_pci_workq_setup(struct edac_pci_ctl_info *pci,
*/
static void edac_pci_workq_teardown(struct edac_pci_ctl_info *pci)
{
- int status;
-
- edac_dbg(0, "\n");
-
- status = cancel_delayed_work(&pci->work);
- if (status == 0)
- flush_workqueue(edac_workqueue);
-}
-
-/*
- * edac_pci_reset_delay_period
- *
- * called with a new period value for the workq period
- * a) stop current workq timer
- * b) restart workq timer with new value
- */
-void edac_pci_reset_delay_period(struct edac_pci_ctl_info *pci,
- unsigned long value)
-{
edac_dbg(0, "\n");
- edac_pci_workq_teardown(pci);
-
- /* need to lock for the setup */
- mutex_lock(&edac_pci_ctls_mutex);
-
- edac_pci_workq_setup(pci, value);
+ pci->op_state = OP_OFFLINE;
- mutex_unlock(&edac_pci_ctls_mutex);
+ edac_stop_work(&pci->work);
}
-EXPORT_SYMBOL_GPL(edac_pci_reset_delay_period);
/*
* edac_pci_alloc_index: Allocate a unique PCI index number
diff --git a/drivers/edac/edac_pci_sysfs.c b/drivers/edac/edac_pci_sysfs.c
index 24d877f6e577..6e3428ba400f 100644
--- a/drivers/edac/edac_pci_sysfs.c
+++ b/drivers/edac/edac_pci_sysfs.c
@@ -331,10 +331,7 @@ static struct kobj_type ktype_edac_pci_main_kobj = {
};
/**
- * edac_pci_main_kobj_setup()
- *
- * setup the sysfs for EDAC PCI attributes
- * assumes edac_subsys has already been initialized
+ * edac_pci_main_kobj_setup: Setup the sysfs for EDAC PCI attributes.
*/
static int edac_pci_main_kobj_setup(void)
{
@@ -351,11 +348,6 @@ static int edac_pci_main_kobj_setup(void)
* controls and attributes
*/
edac_subsys = edac_get_sysfs_subsys();
- if (edac_subsys == NULL) {
- edac_dbg(1, "no edac_subsys\n");
- err = -ENODEV;
- goto decrement_count_fail;
- }
/* Bump the reference count on this module to ensure the
* modules isn't unloaded until we deconstruct the top
@@ -364,7 +356,7 @@ static int edac_pci_main_kobj_setup(void)
if (!try_module_get(THIS_MODULE)) {
edac_dbg(1, "try_module_get() failed\n");
err = -ENODEV;
- goto mod_get_fail;
+ goto decrement_count_fail;
}
edac_pci_top_main_kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL);
@@ -399,9 +391,6 @@ kobject_init_and_add_fail:
kzalloc_fail:
module_put(THIS_MODULE);
-mod_get_fail:
- edac_put_sysfs_subsys();
-
decrement_count_fail:
/* if are on this error exit, nothing to tear down */
atomic_dec(&edac_pci_sysfs_refcount);
@@ -426,7 +415,6 @@ static void edac_pci_main_kobj_teardown(void)
if (atomic_dec_return(&edac_pci_sysfs_refcount) == 0) {
edac_dbg(0, "called kobject_put on main kobj\n");
kobject_put(edac_pci_top_main_kobj);
- edac_put_sysfs_subsys();
}
}
diff --git a/drivers/edac/edac_stub.c b/drivers/edac/edac_stub.c
index ff07aae5b7fb..952e411f01f2 100644
--- a/drivers/edac/edac_stub.c
+++ b/drivers/edac/edac_stub.c
@@ -26,8 +26,6 @@ EXPORT_SYMBOL_GPL(edac_handlers);
int edac_err_assert = 0;
EXPORT_SYMBOL_GPL(edac_err_assert);
-static atomic_t edac_subsys_valid = ATOMIC_INIT(0);
-
int edac_report_status = EDAC_REPORTING_ENABLED;
EXPORT_SYMBOL_GPL(edac_report_status);
@@ -68,42 +66,3 @@ void edac_atomic_assert_error(void)
edac_err_assert++;
}
EXPORT_SYMBOL_GPL(edac_atomic_assert_error);
-
-/*
- * sysfs object: /sys/devices/system/edac
- * need to export to other files
- */
-struct bus_type edac_subsys = {
- .name = "edac",
- .dev_name = "edac",
-};
-EXPORT_SYMBOL_GPL(edac_subsys);
-
-/* return pointer to the 'edac' node in sysfs */
-struct bus_type *edac_get_sysfs_subsys(void)
-{
- int err = 0;
-
- if (atomic_read(&edac_subsys_valid))
- goto out;
-
- /* create the /sys/devices/system/edac directory */
- err = subsys_system_register(&edac_subsys, NULL);
- if (err) {
- printk(KERN_ERR "Error registering toplevel EDAC sysfs dir\n");
- return NULL;
- }
-
-out:
- atomic_inc(&edac_subsys_valid);
- return &edac_subsys;
-}
-EXPORT_SYMBOL_GPL(edac_get_sysfs_subsys);
-
-void edac_put_sysfs_subsys(void)
-{
- /* last user unregisters it */
- if (atomic_dec_and_test(&edac_subsys_valid))
- bus_unregister(&edac_subsys);
-}
-EXPORT_SYMBOL_GPL(edac_put_sysfs_subsys);
diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c
index 40917775dca1..c655162caf08 100644
--- a/drivers/edac/i5100_edac.c
+++ b/drivers/edac/i5100_edac.c
@@ -575,9 +575,7 @@ static void i5100_check_error(struct mem_ctl_info *mci)
static void i5100_refresh_scrubbing(struct work_struct *work)
{
- struct delayed_work *i5100_scrubbing = container_of(work,
- struct delayed_work,
- work);
+ struct delayed_work *i5100_scrubbing = to_delayed_work(work);
struct i5100_priv *priv = container_of(i5100_scrubbing,
struct i5100_priv,
i5100_scrubbing);
diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c
index 23ef8e9f2c9a..b7139c160baf 100644
--- a/drivers/edac/mpc85xx_edac.c
+++ b/drivers/edac/mpc85xx_edac.c
@@ -20,6 +20,7 @@
#include <linux/edac.h>
#include <linux/smp.h>
#include <linux/gfp.h>
+#include <linux/fsl/edac.h>
#include <linux/of_platform.h>
#include <linux/of_device.h>
@@ -238,10 +239,12 @@ static irqreturn_t mpc85xx_pci_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
-int mpc85xx_pci_err_probe(struct platform_device *op)
+static int mpc85xx_pci_err_probe(struct platform_device *op)
{
struct edac_pci_ctl_info *pci;
struct mpc85xx_pci_pdata *pdata;
+ struct mpc85xx_edac_pci_plat_data *plat_data;
+ struct device_node *of_node;
struct resource r;
int res = 0;
@@ -266,7 +269,15 @@ int mpc85xx_pci_err_probe(struct platform_device *op)
pdata->name = "mpc85xx_pci_err";
pdata->irq = NO_IRQ;
- if (mpc85xx_pcie_find_capability(op->dev.of_node) > 0)
+ plat_data = op->dev.platform_data;
+ if (!plat_data) {
+ dev_err(&op->dev, "no platform data");
+ res = -ENXIO;
+ goto err;
+ }
+ of_node = plat_data->of_node;
+
+ if (mpc85xx_pcie_find_capability(of_node) > 0)
pdata->is_pcie = true;
dev_set_drvdata(&op->dev, pci);
@@ -284,7 +295,7 @@ int mpc85xx_pci_err_probe(struct platform_device *op)
pdata->edac_idx = edac_pci_idx++;
- res = of_address_to_resource(op->dev.of_node, 0, &r);
+ res = of_address_to_resource(of_node, 0, &r);
if (res) {
printk(KERN_ERR "%s: Unable to get resource for "
"PCI err regs\n", __func__);
@@ -339,7 +350,7 @@ int mpc85xx_pci_err_probe(struct platform_device *op)
}
if (edac_op_state == EDAC_OPSTATE_INT) {
- pdata->irq = irq_of_parse_and_map(op->dev.of_node, 0);
+ pdata->irq = irq_of_parse_and_map(of_node, 0);
res = devm_request_irq(&op->dev, pdata->irq,
mpc85xx_pci_isr,
IRQF_SHARED,
@@ -386,8 +397,22 @@ err:
devres_release_group(&op->dev, mpc85xx_pci_err_probe);
return res;
}
-EXPORT_SYMBOL(mpc85xx_pci_err_probe);
+static const struct platform_device_id mpc85xx_pci_err_match[] = {
+ {
+ .name = "mpc85xx-pci-edac"
+ },
+ {}
+};
+
+static struct platform_driver mpc85xx_pci_err_driver = {
+ .probe = mpc85xx_pci_err_probe,
+ .id_table = mpc85xx_pci_err_match,
+ .driver = {
+ .name = "mpc85xx_pci_err",
+ .suppress_bind_attrs = true,
+ },
+};
#endif /* CONFIG_PCI */
/**************************** L2 Err device ***************************/
@@ -1208,6 +1233,14 @@ static void __init mpc85xx_mc_clear_rfxe(void *data)
}
#endif
+static struct platform_driver * const drivers[] = {
+ &mpc85xx_mc_err_driver,
+ &mpc85xx_l2_err_driver,
+#ifdef CONFIG_PCI
+ &mpc85xx_pci_err_driver,
+#endif
+};
+
static int __init mpc85xx_mc_init(void)
{
int res = 0;
@@ -1226,13 +1259,9 @@ static int __init mpc85xx_mc_init(void)
break;
}
- res = platform_driver_register(&mpc85xx_mc_err_driver);
- if (res)
- printk(KERN_WARNING EDAC_MOD_STR "MC fails to register\n");
-
- res = platform_driver_register(&mpc85xx_l2_err_driver);
+ res = platform_register_drivers(drivers, ARRAY_SIZE(drivers));
if (res)
- printk(KERN_WARNING EDAC_MOD_STR "L2 fails to register\n");
+ printk(KERN_WARNING EDAC_MOD_STR "drivers fail to register\n");
#ifdef CONFIG_FSL_SOC_BOOKE
pvr = mfspr(SPRN_PVR);
@@ -1270,8 +1299,7 @@ static void __exit mpc85xx_mc_exit(void)
on_each_cpu(mpc85xx_mc_restore_hid1, NULL, 0);
}
#endif
- platform_driver_unregister(&mpc85xx_l2_err_driver);
- platform_driver_unregister(&mpc85xx_mc_err_driver);
+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
}
module_exit(mpc85xx_mc_exit);
diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c
index 0574e1bbe45c..6c54127e6eae 100644
--- a/drivers/edac/mv64x60_edac.c
+++ b/drivers/edac/mv64x60_edac.c
@@ -847,6 +847,15 @@ static struct platform_driver mv64x60_mc_err_driver = {
}
};
+static struct platform_driver * const drivers[] = {
+ &mv64x60_mc_err_driver,
+ &mv64x60_cpu_err_driver,
+ &mv64x60_sram_err_driver,
+#ifdef CONFIG_PCI
+ &mv64x60_pci_err_driver,
+#endif
+};
+
static int __init mv64x60_edac_init(void)
{
int ret = 0;
@@ -863,39 +872,13 @@ static int __init mv64x60_edac_init(void)
break;
}
- ret = platform_driver_register(&mv64x60_mc_err_driver);
- if (ret)
- printk(KERN_WARNING EDAC_MOD_STR "MC err failed to register\n");
-
- ret = platform_driver_register(&mv64x60_cpu_err_driver);
- if (ret)
- printk(KERN_WARNING EDAC_MOD_STR
- "CPU err failed to register\n");
-
- ret = platform_driver_register(&mv64x60_sram_err_driver);
- if (ret)
- printk(KERN_WARNING EDAC_MOD_STR
- "SRAM err failed to register\n");
-
-#ifdef CONFIG_PCI
- ret = platform_driver_register(&mv64x60_pci_err_driver);
- if (ret)
- printk(KERN_WARNING EDAC_MOD_STR
- "PCI err failed to register\n");
-#endif
-
- return ret;
+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
}
module_init(mv64x60_edac_init);
static void __exit mv64x60_edac_exit(void)
{
-#ifdef CONFIG_PCI
- platform_driver_unregister(&mv64x60_pci_err_driver);
-#endif
- platform_driver_unregister(&mv64x60_sram_err_driver);
- platform_driver_unregister(&mv64x60_cpu_err_driver);
- platform_driver_unregister(&mv64x60_mc_err_driver);
+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
}
module_exit(mv64x60_edac_exit);
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
index 429309c62699..e438ee5b433f 100644
--- a/drivers/edac/sb_edac.c
+++ b/drivers/edac/sb_edac.c
@@ -65,15 +65,20 @@ static const u32 ibridge_dram_rule[] = {
0xd8, 0xe0, 0xe8, 0xf0, 0xf8,
};
-#define SAD_LIMIT(reg) ((GET_BITFIELD(reg, 6, 25) << 26) | 0x3ffffff)
-#define DRAM_ATTR(reg) GET_BITFIELD(reg, 2, 3)
-#define INTERLEAVE_MODE(reg) GET_BITFIELD(reg, 1, 1)
+static const u32 knl_dram_rule[] = {
+ 0x60, 0x68, 0x70, 0x78, 0x80, /* 0-4 */
+ 0x88, 0x90, 0x98, 0xa0, 0xa8, /* 5-9 */
+ 0xb0, 0xb8, 0xc0, 0xc8, 0xd0, /* 10-14 */
+ 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, /* 15-19 */
+ 0x100, 0x108, 0x110, 0x118, /* 20-23 */
+};
+
#define DRAM_RULE_ENABLE(reg) GET_BITFIELD(reg, 0, 0)
#define A7MODE(reg) GET_BITFIELD(reg, 26, 26)
-static char *get_dram_attr(u32 reg)
+static char *show_dram_attr(u32 attr)
{
- switch(DRAM_ATTR(reg)) {
+ switch (attr) {
case 0:
return "DRAM";
case 1:
@@ -97,6 +102,14 @@ static const u32 ibridge_interleave_list[] = {
0xdc, 0xe4, 0xec, 0xf4, 0xfc,
};
+static const u32 knl_interleave_list[] = {
+ 0x64, 0x6c, 0x74, 0x7c, 0x84, /* 0-4 */
+ 0x8c, 0x94, 0x9c, 0xa4, 0xac, /* 5-9 */
+ 0xb4, 0xbc, 0xc4, 0xcc, 0xd4, /* 10-14 */
+ 0xdc, 0xe4, 0xec, 0xf4, 0xfc, /* 15-19 */
+ 0x104, 0x10c, 0x114, 0x11c, /* 20-23 */
+};
+
struct interleave_pkg {
unsigned char start;
unsigned char end;
@@ -134,10 +147,13 @@ static inline int sad_pkg(const struct interleave_pkg *table, u32 reg,
/* Devices 12 Function 7 */
#define TOLM 0x80
-#define TOHM 0x84
+#define TOHM 0x84
#define HASWELL_TOLM 0xd0
#define HASWELL_TOHM_0 0xd4
#define HASWELL_TOHM_1 0xd8
+#define KNL_TOLM 0xd0
+#define KNL_TOHM_0 0xd4
+#define KNL_TOHM_1 0xd8
#define GET_TOLM(reg) ((GET_BITFIELD(reg, 0, 3) << 28) | 0x3ffffff)
#define GET_TOHM(reg) ((GET_BITFIELD(reg, 0, 20) << 25) | 0x3ffffff)
@@ -148,6 +164,8 @@ static inline int sad_pkg(const struct interleave_pkg *table, u32 reg,
#define SOURCE_ID(reg) GET_BITFIELD(reg, 9, 11)
+#define SOURCE_ID_KNL(reg) GET_BITFIELD(reg, 12, 14)
+
#define SAD_CONTROL 0xf4
/* Device 14 function 0 */
@@ -170,6 +188,7 @@ static const u32 tad_dram_rule[] = {
/* Device 15, function 0 */
#define MCMTR 0x7c
+#define KNL_MCMTR 0x624
#define IS_ECC_ENABLED(mcmtr) GET_BITFIELD(mcmtr, 2, 2)
#define IS_LOCKSTEP_ENABLED(mcmtr) GET_BITFIELD(mcmtr, 1, 1)
@@ -186,6 +205,8 @@ static const int mtr_regs[] = {
0x80, 0x84, 0x88,
};
+static const int knl_mtr_reg = 0xb60;
+
#define RANK_DISABLE(mtr) GET_BITFIELD(mtr, 16, 19)
#define IS_DIMM_PRESENT(mtr) GET_BITFIELD(mtr, 14, 14)
#define RANK_CNT_BITS(mtr) GET_BITFIELD(mtr, 12, 13)
@@ -256,6 +277,9 @@ static const u32 correrrthrsld[] = {
#define NUM_CHANNELS 8 /* 2MC per socket, four chan per MC */
#define MAX_DIMMS 3 /* Max DIMMS per channel */
+#define KNL_MAX_CHAS 38 /* KNL max num. of Cache Home Agents */
+#define KNL_MAX_CHANNELS 6 /* KNL max num. of PCI channels */
+#define KNL_MAX_EDCS 8 /* Embedded DRAM controllers */
#define CHANNEL_UNSPECIFIED 0xf /* Intel IA32 SDM 15-14 */
enum type {
@@ -263,6 +287,7 @@ enum type {
IVY_BRIDGE,
HASWELL,
BROADWELL,
+ KNIGHTS_LANDING,
};
struct sbridge_pvt;
@@ -273,6 +298,10 @@ struct sbridge_info {
u64 (*get_tolm)(struct sbridge_pvt *pvt);
u64 (*get_tohm)(struct sbridge_pvt *pvt);
u64 (*rir_limit)(u32 reg);
+ u64 (*sad_limit)(u32 reg);
+ u32 (*interleave_mode)(u32 reg);
+ char* (*show_interleave_mode)(u32 reg);
+ u32 (*dram_attr)(u32 reg);
const u32 *dram_rule;
const u32 *interleave_list;
const struct interleave_pkg *interleave_pkg;
@@ -308,6 +337,16 @@ struct sbridge_dev {
struct mem_ctl_info *mci;
};
+struct knl_pvt {
+ struct pci_dev *pci_cha[KNL_MAX_CHAS];
+ struct pci_dev *pci_channel[KNL_MAX_CHANNELS];
+ struct pci_dev *pci_mc0;
+ struct pci_dev *pci_mc1;
+ struct pci_dev *pci_mc0_misc;
+ struct pci_dev *pci_mc1_misc;
+ struct pci_dev *pci_mc_info; /* tolm, tohm */
+};
+
struct sbridge_pvt {
struct pci_dev *pci_ta, *pci_ddrio, *pci_ras;
struct pci_dev *pci_sad0, *pci_sad1;
@@ -336,6 +375,7 @@ struct sbridge_pvt {
/* Memory description */
u64 tolm, tohm;
+ struct knl_pvt knl;
};
#define PCI_DESCR(device_id, opt) \
@@ -509,6 +549,50 @@ static const struct pci_id_table pci_dev_descr_haswell_table[] = {
{0,} /* 0 terminated list. */
};
+/* Knight's Landing Support */
+/*
+ * KNL's memory channels are swizzled between memory controllers.
+ * MC0 is mapped to CH3,5,6 and MC1 is mapped to CH0,1,2
+ */
+#define knl_channel_remap(channel) ((channel + 3) % 6)
+
+/* Memory controller, TAD tables, error injection - 2-8-0, 2-9-0 (2 of these) */
+#define PCI_DEVICE_ID_INTEL_KNL_IMC_MC 0x7840
+/* DRAM channel stuff; bank addrs, dimmmtr, etc.. 2-8-2 - 2-9-4 (6 of these) */
+#define PCI_DEVICE_ID_INTEL_KNL_IMC_CHANNEL 0x7843
+/* kdrwdbu TAD limits/offsets, MCMTR - 2-10-1, 2-11-1 (2 of these) */
+#define PCI_DEVICE_ID_INTEL_KNL_IMC_TA 0x7844
+/* CHA broadcast registers, dram rules - 1-29-0 (1 of these) */
+#define PCI_DEVICE_ID_INTEL_KNL_IMC_SAD0 0x782a
+/* SAD target - 1-29-1 (1 of these) */
+#define PCI_DEVICE_ID_INTEL_KNL_IMC_SAD1 0x782b
+/* Caching / Home Agent */
+#define PCI_DEVICE_ID_INTEL_KNL_IMC_CHA 0x782c
+/* Device with TOLM and TOHM, 0-5-0 (1 of these) */
+#define PCI_DEVICE_ID_INTEL_KNL_IMC_TOLHM 0x7810
+
+/*
+ * KNL differs from SB, IB, and Haswell in that it has multiple
+ * instances of the same device with the same device ID, so we handle that
+ * by creating as many copies in the table as we expect to find.
+ * (Like device ID must be grouped together.)
+ */
+
+static const struct pci_id_descr pci_dev_descr_knl[] = {
+ [0] = { PCI_DESCR(PCI_DEVICE_ID_INTEL_KNL_IMC_SAD0, 0) },
+ [1] = { PCI_DESCR(PCI_DEVICE_ID_INTEL_KNL_IMC_SAD1, 0) },
+ [2 ... 3] = { PCI_DESCR(PCI_DEVICE_ID_INTEL_KNL_IMC_MC, 0)},
+ [4 ... 41] = { PCI_DESCR(PCI_DEVICE_ID_INTEL_KNL_IMC_CHA, 0) },
+ [42 ... 47] = { PCI_DESCR(PCI_DEVICE_ID_INTEL_KNL_IMC_CHANNEL, 0) },
+ [48] = { PCI_DESCR(PCI_DEVICE_ID_INTEL_KNL_IMC_TA, 0) },
+ [49] = { PCI_DESCR(PCI_DEVICE_ID_INTEL_KNL_IMC_TOLHM, 0) },
+};
+
+static const struct pci_id_table pci_dev_descr_knl_table[] = {
+ PCI_ID_TABLE_ENTRY(pci_dev_descr_knl),
+ {0,}
+};
+
/*
* Broadwell support
*
@@ -585,6 +669,7 @@ static const struct pci_device_id sbridge_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KNL_IMC_SAD0)},
{0,} /* 0 terminated list. */
};
@@ -598,7 +683,7 @@ static inline int numrank(enum type type, u32 mtr)
int ranks = (1 << RANK_CNT_BITS(mtr));
int max = 4;
- if (type == HASWELL || type == BROADWELL)
+ if (type == HASWELL || type == BROADWELL || type == KNIGHTS_LANDING)
max = 8;
if (ranks > max) {
@@ -636,10 +721,19 @@ static inline int numcol(u32 mtr)
return 1 << cols;
}
-static struct sbridge_dev *get_sbridge_dev(u8 bus)
+static struct sbridge_dev *get_sbridge_dev(u8 bus, int multi_bus)
{
struct sbridge_dev *sbridge_dev;
+ /*
+ * If we have devices scattered across several busses that pertain
+ * to the same memory controller, we'll lump them all together.
+ */
+ if (multi_bus) {
+ return list_first_entry_or_null(&sbridge_edac_list,
+ struct sbridge_dev, list);
+ }
+
list_for_each_entry(sbridge_dev, &sbridge_edac_list, list) {
if (sbridge_dev->bus == bus)
return sbridge_dev;
@@ -718,6 +812,67 @@ static u64 rir_limit(u32 reg)
return ((u64)GET_BITFIELD(reg, 1, 10) << 29) | 0x1fffffff;
}
+static u64 sad_limit(u32 reg)
+{
+ return (GET_BITFIELD(reg, 6, 25) << 26) | 0x3ffffff;
+}
+
+static u32 interleave_mode(u32 reg)
+{
+ return GET_BITFIELD(reg, 1, 1);
+}
+
+char *show_interleave_mode(u32 reg)
+{
+ return interleave_mode(reg) ? "8:6" : "[8:6]XOR[18:16]";
+}
+
+static u32 dram_attr(u32 reg)
+{
+ return GET_BITFIELD(reg, 2, 3);
+}
+
+static u64 knl_sad_limit(u32 reg)
+{
+ return (GET_BITFIELD(reg, 7, 26) << 26) | 0x3ffffff;
+}
+
+static u32 knl_interleave_mode(u32 reg)
+{
+ return GET_BITFIELD(reg, 1, 2);
+}
+
+static char *knl_show_interleave_mode(u32 reg)
+{
+ char *s;
+
+ switch (knl_interleave_mode(reg)) {
+ case 0:
+ s = "use address bits [8:6]";
+ break;
+ case 1:
+ s = "use address bits [10:8]";
+ break;
+ case 2:
+ s = "use address bits [14:12]";
+ break;
+ case 3:
+ s = "use address bits [32:30]";
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+
+ return s;
+}
+
+static u32 dram_attr_knl(u32 reg)
+{
+ return GET_BITFIELD(reg, 3, 4);
+}
+
+
static enum mem_type get_memory_type(struct sbridge_pvt *pvt)
{
u32 reg;
@@ -769,6 +924,12 @@ out:
return mtype;
}
+static enum dev_type knl_get_width(struct sbridge_pvt *pvt, u32 mtr)
+{
+ /* for KNL value is fixed */
+ return DEV_X16;
+}
+
static enum dev_type sbridge_get_width(struct sbridge_pvt *pvt, u32 mtr)
{
/* there's no way to figure out */
@@ -812,6 +973,12 @@ static enum dev_type broadwell_get_width(struct sbridge_pvt *pvt, u32 mtr)
return __ibridge_get_width(GET_BITFIELD(mtr, 8, 9));
}
+static enum mem_type knl_get_memory_type(struct sbridge_pvt *pvt)
+{
+ /* DDR4 RDIMMS and LRDIMMS are supported */
+ return MEM_RDDR4;
+}
+
static u8 get_node_id(struct sbridge_pvt *pvt)
{
u32 reg;
@@ -827,6 +994,15 @@ static u8 haswell_get_node_id(struct sbridge_pvt *pvt)
return GET_BITFIELD(reg, 0, 3);
}
+static u8 knl_get_node_id(struct sbridge_pvt *pvt)
+{
+ u32 reg;
+
+ pci_read_config_dword(pvt->pci_sad1, SAD_CONTROL, &reg);
+ return GET_BITFIELD(reg, 0, 2);
+}
+
+
static u64 haswell_get_tolm(struct sbridge_pvt *pvt)
{
u32 reg;
@@ -848,6 +1024,26 @@ static u64 haswell_get_tohm(struct sbridge_pvt *pvt)
return rc | 0x1ffffff;
}
+static u64 knl_get_tolm(struct sbridge_pvt *pvt)
+{
+ u32 reg;
+
+ pci_read_config_dword(pvt->knl.pci_mc_info, KNL_TOLM, &reg);
+ return (GET_BITFIELD(reg, 26, 31) << 26) | 0x3ffffff;
+}
+
+static u64 knl_get_tohm(struct sbridge_pvt *pvt)
+{
+ u64 rc;
+ u32 reg_lo, reg_hi;
+
+ pci_read_config_dword(pvt->knl.pci_mc_info, KNL_TOHM_0, &reg_lo);
+ pci_read_config_dword(pvt->knl.pci_mc_info, KNL_TOHM_1, &reg_hi);
+ rc = ((u64)reg_hi << 32) | reg_lo;
+ return rc | 0x3ffffff;
+}
+
+
static u64 haswell_rir_limit(u32 reg)
{
return (((u64)GET_BITFIELD(reg, 1, 11) + 1) << 29) - 1;
@@ -905,11 +1101,22 @@ static int check_if_ecc_is_active(const u8 bus, enum type type)
case BROADWELL:
id = PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0_TA;
break;
+ case KNIGHTS_LANDING:
+ /*
+ * KNL doesn't group things by bus the same way
+ * SB/IB/Haswell does.
+ */
+ id = PCI_DEVICE_ID_INTEL_KNL_IMC_TA;
+ break;
default:
return -ENODEV;
}
- pdev = get_pdev_same_bus(bus, id);
+ if (type != KNIGHTS_LANDING)
+ pdev = get_pdev_same_bus(bus, id);
+ else
+ pdev = pci_get_device(PCI_VENDOR_ID_INTEL, id, 0);
+
if (!pdev) {
sbridge_printk(KERN_ERR, "Couldn't find PCI device "
"%04x:%04x! on bus %02d\n",
@@ -917,7 +1124,8 @@ static int check_if_ecc_is_active(const u8 bus, enum type type)
return -ENODEV;
}
- pci_read_config_dword(pdev, MCMTR, &mcmtr);
+ pci_read_config_dword(pdev,
+ type == KNIGHTS_LANDING ? KNL_MCMTR : MCMTR, &mcmtr);
if (!IS_ECC_ENABLED(mcmtr)) {
sbridge_printk(KERN_ERR, "ECC is disabled. Aborting\n");
return -ENODEV;
@@ -925,6 +1133,476 @@ static int check_if_ecc_is_active(const u8 bus, enum type type)
return 0;
}
+/* Low bits of TAD limit, and some metadata. */
+static const u32 knl_tad_dram_limit_lo[] = {
+ 0x400, 0x500, 0x600, 0x700,
+ 0x800, 0x900, 0xa00, 0xb00,
+};
+
+/* Low bits of TAD offset. */
+static const u32 knl_tad_dram_offset_lo[] = {
+ 0x404, 0x504, 0x604, 0x704,
+ 0x804, 0x904, 0xa04, 0xb04,
+};
+
+/* High 16 bits of TAD limit and offset. */
+static const u32 knl_tad_dram_hi[] = {
+ 0x408, 0x508, 0x608, 0x708,
+ 0x808, 0x908, 0xa08, 0xb08,
+};
+
+/* Number of ways a tad entry is interleaved. */
+static const u32 knl_tad_ways[] = {
+ 8, 6, 4, 3, 2, 1,
+};
+
+/*
+ * Retrieve the n'th Target Address Decode table entry
+ * from the memory controller's TAD table.
+ *
+ * @pvt: driver private data
+ * @entry: which entry you want to retrieve
+ * @mc: which memory controller (0 or 1)
+ * @offset: output tad range offset
+ * @limit: output address of first byte above tad range
+ * @ways: output number of interleave ways
+ *
+ * The offset value has curious semantics. It's a sort of running total
+ * of the sizes of all the memory regions that aren't mapped in this
+ * tad table.
+ */
+static int knl_get_tad(const struct sbridge_pvt *pvt,
+ const int entry,
+ const int mc,
+ u64 *offset,
+ u64 *limit,
+ int *ways)
+{
+ u32 reg_limit_lo, reg_offset_lo, reg_hi;
+ struct pci_dev *pci_mc;
+ int way_id;
+
+ switch (mc) {
+ case 0:
+ pci_mc = pvt->knl.pci_mc0;
+ break;
+ case 1:
+ pci_mc = pvt->knl.pci_mc1;
+ break;
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ pci_read_config_dword(pci_mc,
+ knl_tad_dram_limit_lo[entry], &reg_limit_lo);
+ pci_read_config_dword(pci_mc,
+ knl_tad_dram_offset_lo[entry], &reg_offset_lo);
+ pci_read_config_dword(pci_mc,
+ knl_tad_dram_hi[entry], &reg_hi);
+
+ /* Is this TAD entry enabled? */
+ if (!GET_BITFIELD(reg_limit_lo, 0, 0))
+ return -ENODEV;
+
+ way_id = GET_BITFIELD(reg_limit_lo, 3, 5);
+
+ if (way_id < ARRAY_SIZE(knl_tad_ways)) {
+ *ways = knl_tad_ways[way_id];
+ } else {
+ *ways = 0;
+ sbridge_printk(KERN_ERR,
+ "Unexpected value %d in mc_tad_limit_lo wayness field\n",
+ way_id);
+ return -ENODEV;
+ }
+
+ /*
+ * The least significant 6 bits of base and limit are truncated.
+ * For limit, we fill the missing bits with 1s.
+ */
+ *offset = ((u64) GET_BITFIELD(reg_offset_lo, 6, 31) << 6) |
+ ((u64) GET_BITFIELD(reg_hi, 0, 15) << 32);
+ *limit = ((u64) GET_BITFIELD(reg_limit_lo, 6, 31) << 6) | 63 |
+ ((u64) GET_BITFIELD(reg_hi, 16, 31) << 32);
+
+ return 0;
+}
+
+/* Determine which memory controller is responsible for a given channel. */
+static int knl_channel_mc(int channel)
+{
+ WARN_ON(channel < 0 || channel >= 6);
+
+ return channel < 3 ? 1 : 0;
+}
+
+/*
+ * Get the Nth entry from EDC_ROUTE_TABLE register.
+ * (This is the per-tile mapping of logical interleave targets to
+ * physical EDC modules.)
+ *
+ * entry 0: 0:2
+ * 1: 3:5
+ * 2: 6:8
+ * 3: 9:11
+ * 4: 12:14
+ * 5: 15:17
+ * 6: 18:20
+ * 7: 21:23
+ * reserved: 24:31
+ */
+static u32 knl_get_edc_route(int entry, u32 reg)
+{
+ WARN_ON(entry >= KNL_MAX_EDCS);
+ return GET_BITFIELD(reg, entry*3, (entry*3)+2);
+}
+
+/*
+ * Get the Nth entry from MC_ROUTE_TABLE register.
+ * (This is the per-tile mapping of logical interleave targets to
+ * physical DRAM channels modules.)
+ *
+ * entry 0: mc 0:2 channel 18:19
+ * 1: mc 3:5 channel 20:21
+ * 2: mc 6:8 channel 22:23
+ * 3: mc 9:11 channel 24:25
+ * 4: mc 12:14 channel 26:27
+ * 5: mc 15:17 channel 28:29
+ * reserved: 30:31
+ *
+ * Though we have 3 bits to identify the MC, we should only see
+ * the values 0 or 1.
+ */
+
+static u32 knl_get_mc_route(int entry, u32 reg)
+{
+ int mc, chan;
+
+ WARN_ON(entry >= KNL_MAX_CHANNELS);
+
+ mc = GET_BITFIELD(reg, entry*3, (entry*3)+2);
+ chan = GET_BITFIELD(reg, (entry*2) + 18, (entry*2) + 18 + 1);
+
+ return knl_channel_remap(mc*3 + chan);
+}
+
+/*
+ * Render the EDC_ROUTE register in human-readable form.
+ * Output string s should be at least KNL_MAX_EDCS*2 bytes.
+ */
+static void knl_show_edc_route(u32 reg, char *s)
+{
+ int i;
+
+ for (i = 0; i < KNL_MAX_EDCS; i++) {
+ s[i*2] = knl_get_edc_route(i, reg) + '0';
+ s[i*2+1] = '-';
+ }
+
+ s[KNL_MAX_EDCS*2 - 1] = '\0';
+}
+
+/*
+ * Render the MC_ROUTE register in human-readable form.
+ * Output string s should be at least KNL_MAX_CHANNELS*2 bytes.
+ */
+static void knl_show_mc_route(u32 reg, char *s)
+{
+ int i;
+
+ for (i = 0; i < KNL_MAX_CHANNELS; i++) {
+ s[i*2] = knl_get_mc_route(i, reg) + '0';
+ s[i*2+1] = '-';
+ }
+
+ s[KNL_MAX_CHANNELS*2 - 1] = '\0';
+}
+
+#define KNL_EDC_ROUTE 0xb8
+#define KNL_MC_ROUTE 0xb4
+
+/* Is this dram rule backed by regular DRAM in flat mode? */
+#define KNL_EDRAM(reg) GET_BITFIELD(reg, 29, 29)
+
+/* Is this dram rule cached? */
+#define KNL_CACHEABLE(reg) GET_BITFIELD(reg, 28, 28)
+
+/* Is this rule backed by edc ? */
+#define KNL_EDRAM_ONLY(reg) GET_BITFIELD(reg, 29, 29)
+
+/* Is this rule backed by DRAM, cacheable in EDRAM? */
+#define KNL_CACHEABLE(reg) GET_BITFIELD(reg, 28, 28)
+
+/* Is this rule mod3? */
+#define KNL_MOD3(reg) GET_BITFIELD(reg, 27, 27)
+
+/*
+ * Figure out how big our RAM modules are.
+ *
+ * The DIMMMTR register in KNL doesn't tell us the size of the DIMMs, so we
+ * have to figure this out from the SAD rules, interleave lists, route tables,
+ * and TAD rules.
+ *
+ * SAD rules can have holes in them (e.g. the 3G-4G hole), so we have to
+ * inspect the TAD rules to figure out how large the SAD regions really are.
+ *
+ * When we know the real size of a SAD region and how many ways it's
+ * interleaved, we know the individual contribution of each channel to
+ * TAD is size/ways.
+ *
+ * Finally, we have to check whether each channel participates in each SAD
+ * region.
+ *
+ * Fortunately, KNL only supports one DIMM per channel, so once we know how
+ * much memory the channel uses, we know the DIMM is at least that large.
+ * (The BIOS might possibly choose not to map all available memory, in which
+ * case we will underreport the size of the DIMM.)
+ *
+ * In theory, we could try to determine the EDC sizes as well, but that would
+ * only work in flat mode, not in cache mode.
+ *
+ * @mc_sizes: Output sizes of channels (must have space for KNL_MAX_CHANNELS
+ * elements)
+ */
+static int knl_get_dimm_capacity(struct sbridge_pvt *pvt, u64 *mc_sizes)
+{
+ u64 sad_base, sad_size, sad_limit = 0;
+ u64 tad_base, tad_size, tad_limit, tad_deadspace, tad_livespace;
+ int sad_rule = 0;
+ int tad_rule = 0;
+ int intrlv_ways, tad_ways;
+ u32 first_pkg, pkg;
+ int i;
+ u64 sad_actual_size[2]; /* sad size accounting for holes, per mc */
+ u32 dram_rule, interleave_reg;
+ u32 mc_route_reg[KNL_MAX_CHAS];
+ u32 edc_route_reg[KNL_MAX_CHAS];
+ int edram_only;
+ char edc_route_string[KNL_MAX_EDCS*2];
+ char mc_route_string[KNL_MAX_CHANNELS*2];
+ int cur_reg_start;
+ int mc;
+ int channel;
+ int way;
+ int participants[KNL_MAX_CHANNELS];
+ int participant_count = 0;
+
+ for (i = 0; i < KNL_MAX_CHANNELS; i++)
+ mc_sizes[i] = 0;
+
+ /* Read the EDC route table in each CHA. */
+ cur_reg_start = 0;
+ for (i = 0; i < KNL_MAX_CHAS; i++) {
+ pci_read_config_dword(pvt->knl.pci_cha[i],
+ KNL_EDC_ROUTE, &edc_route_reg[i]);
+
+ if (i > 0 && edc_route_reg[i] != edc_route_reg[i-1]) {
+ knl_show_edc_route(edc_route_reg[i-1],
+ edc_route_string);
+ if (cur_reg_start == i-1)
+ edac_dbg(0, "edc route table for CHA %d: %s\n",
+ cur_reg_start, edc_route_string);
+ else
+ edac_dbg(0, "edc route table for CHA %d-%d: %s\n",
+ cur_reg_start, i-1, edc_route_string);
+ cur_reg_start = i;
+ }
+ }
+ knl_show_edc_route(edc_route_reg[i-1], edc_route_string);
+ if (cur_reg_start == i-1)
+ edac_dbg(0, "edc route table for CHA %d: %s\n",
+ cur_reg_start, edc_route_string);
+ else
+ edac_dbg(0, "edc route table for CHA %d-%d: %s\n",
+ cur_reg_start, i-1, edc_route_string);
+
+ /* Read the MC route table in each CHA. */
+ cur_reg_start = 0;
+ for (i = 0; i < KNL_MAX_CHAS; i++) {
+ pci_read_config_dword(pvt->knl.pci_cha[i],
+ KNL_MC_ROUTE, &mc_route_reg[i]);
+
+ if (i > 0 && mc_route_reg[i] != mc_route_reg[i-1]) {
+ knl_show_mc_route(mc_route_reg[i-1], mc_route_string);
+ if (cur_reg_start == i-1)
+ edac_dbg(0, "mc route table for CHA %d: %s\n",
+ cur_reg_start, mc_route_string);
+ else
+ edac_dbg(0, "mc route table for CHA %d-%d: %s\n",
+ cur_reg_start, i-1, mc_route_string);
+ cur_reg_start = i;
+ }
+ }
+ knl_show_mc_route(mc_route_reg[i-1], mc_route_string);
+ if (cur_reg_start == i-1)
+ edac_dbg(0, "mc route table for CHA %d: %s\n",
+ cur_reg_start, mc_route_string);
+ else
+ edac_dbg(0, "mc route table for CHA %d-%d: %s\n",
+ cur_reg_start, i-1, mc_route_string);
+
+ /* Process DRAM rules */
+ for (sad_rule = 0; sad_rule < pvt->info.max_sad; sad_rule++) {
+ /* previous limit becomes the new base */
+ sad_base = sad_limit;
+
+ pci_read_config_dword(pvt->pci_sad0,
+ pvt->info.dram_rule[sad_rule], &dram_rule);
+
+ if (!DRAM_RULE_ENABLE(dram_rule))
+ break;
+
+ edram_only = KNL_EDRAM_ONLY(dram_rule);
+
+ sad_limit = pvt->info.sad_limit(dram_rule)+1;
+ sad_size = sad_limit - sad_base;
+
+ pci_read_config_dword(pvt->pci_sad0,
+ pvt->info.interleave_list[sad_rule], &interleave_reg);
+
+ /*
+ * Find out how many ways this dram rule is interleaved.
+ * We stop when we see the first channel again.
+ */
+ first_pkg = sad_pkg(pvt->info.interleave_pkg,
+ interleave_reg, 0);
+ for (intrlv_ways = 1; intrlv_ways < 8; intrlv_ways++) {
+ pkg = sad_pkg(pvt->info.interleave_pkg,
+ interleave_reg, intrlv_ways);
+
+ if ((pkg & 0x8) == 0) {
+ /*
+ * 0 bit means memory is non-local,
+ * which KNL doesn't support
+ */
+ edac_dbg(0, "Unexpected interleave target %d\n",
+ pkg);
+ return -1;
+ }
+
+ if (pkg == first_pkg)
+ break;
+ }
+ if (KNL_MOD3(dram_rule))
+ intrlv_ways *= 3;
+
+ edac_dbg(3, "dram rule %d (base 0x%llx, limit 0x%llx), %d way interleave%s\n",
+ sad_rule,
+ sad_base,
+ sad_limit,
+ intrlv_ways,
+ edram_only ? ", EDRAM" : "");
+
+ /*
+ * Find out how big the SAD region really is by iterating
+ * over TAD tables (SAD regions may contain holes).
+ * Each memory controller might have a different TAD table, so
+ * we have to look at both.
+ *
+ * Livespace is the memory that's mapped in this TAD table,
+ * deadspace is the holes (this could be the MMIO hole, or it
+ * could be memory that's mapped by the other TAD table but
+ * not this one).
+ */
+ for (mc = 0; mc < 2; mc++) {
+ sad_actual_size[mc] = 0;
+ tad_livespace = 0;
+ for (tad_rule = 0;
+ tad_rule < ARRAY_SIZE(
+ knl_tad_dram_limit_lo);
+ tad_rule++) {
+ if (knl_get_tad(pvt,
+ tad_rule,
+ mc,
+ &tad_deadspace,
+ &tad_limit,
+ &tad_ways))
+ break;
+
+ tad_size = (tad_limit+1) -
+ (tad_livespace + tad_deadspace);
+ tad_livespace += tad_size;
+ tad_base = (tad_limit+1) - tad_size;
+
+ if (tad_base < sad_base) {
+ if (tad_limit > sad_base)
+ edac_dbg(0, "TAD region overlaps lower SAD boundary -- TAD tables may be configured incorrectly.\n");
+ } else if (tad_base < sad_limit) {
+ if (tad_limit+1 > sad_limit) {
+ edac_dbg(0, "TAD region overlaps upper SAD boundary -- TAD tables may be configured incorrectly.\n");
+ } else {
+ /* TAD region is completely inside SAD region */
+ edac_dbg(3, "TAD region %d 0x%llx - 0x%llx (%lld bytes) table%d\n",
+ tad_rule, tad_base,
+ tad_limit, tad_size,
+ mc);
+ sad_actual_size[mc] += tad_size;
+ }
+ }
+ tad_base = tad_limit+1;
+ }
+ }
+
+ for (mc = 0; mc < 2; mc++) {
+ edac_dbg(3, " total TAD DRAM footprint in table%d : 0x%llx (%lld bytes)\n",
+ mc, sad_actual_size[mc], sad_actual_size[mc]);
+ }
+
+ /* Ignore EDRAM rule */
+ if (edram_only)
+ continue;
+
+ /* Figure out which channels participate in interleave. */
+ for (channel = 0; channel < KNL_MAX_CHANNELS; channel++)
+ participants[channel] = 0;
+
+ /* For each channel, does at least one CHA have
+ * this channel mapped to the given target?
+ */
+ for (channel = 0; channel < KNL_MAX_CHANNELS; channel++) {
+ for (way = 0; way < intrlv_ways; way++) {
+ int target;
+ int cha;
+
+ if (KNL_MOD3(dram_rule))
+ target = way;
+ else
+ target = 0x7 & sad_pkg(
+ pvt->info.interleave_pkg, interleave_reg, way);
+
+ for (cha = 0; cha < KNL_MAX_CHAS; cha++) {
+ if (knl_get_mc_route(target,
+ mc_route_reg[cha]) == channel
+ && participants[channel]) {
+ participant_count++;
+ participants[channel] = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ if (participant_count != intrlv_ways)
+ edac_dbg(0, "participant_count (%d) != interleave_ways (%d): DIMM size may be incorrect\n",
+ participant_count, intrlv_ways);
+
+ for (channel = 0; channel < KNL_MAX_CHANNELS; channel++) {
+ mc = knl_channel_mc(channel);
+ if (participants[channel]) {
+ edac_dbg(4, "mc channel %d contributes %lld bytes via sad entry %d\n",
+ channel,
+ sad_actual_size[mc]/intrlv_ways,
+ sad_rule);
+ mc_sizes[channel] +=
+ sad_actual_size[mc]/intrlv_ways;
+ }
+ }
+ }
+
+ return 0;
+}
+
static int get_dimm_config(struct mem_ctl_info *mci)
{
struct sbridge_pvt *pvt = mci->pvt_info;
@@ -934,13 +1612,20 @@ static int get_dimm_config(struct mem_ctl_info *mci)
u32 reg;
enum edac_type mode;
enum mem_type mtype;
+ int channels = pvt->info.type == KNIGHTS_LANDING ?
+ KNL_MAX_CHANNELS : NUM_CHANNELS;
+ u64 knl_mc_sizes[KNL_MAX_CHANNELS];
- if (pvt->info.type == HASWELL || pvt->info.type == BROADWELL)
+ if (pvt->info.type == HASWELL || pvt->info.type == BROADWELL ||
+ pvt->info.type == KNIGHTS_LANDING)
pci_read_config_dword(pvt->pci_sad1, SAD_TARGET, &reg);
else
pci_read_config_dword(pvt->pci_br0, SAD_TARGET, &reg);
- pvt->sbridge_dev->source_id = SOURCE_ID(reg);
+ if (pvt->info.type == KNIGHTS_LANDING)
+ pvt->sbridge_dev->source_id = SOURCE_ID_KNL(reg);
+ else
+ pvt->sbridge_dev->source_id = SOURCE_ID(reg);
pvt->sbridge_dev->node_id = pvt->info.get_node_id(pvt);
edac_dbg(0, "mc#%d: Node ID: %d, source ID: %d\n",
@@ -948,31 +1633,42 @@ static int get_dimm_config(struct mem_ctl_info *mci)
pvt->sbridge_dev->node_id,
pvt->sbridge_dev->source_id);
- pci_read_config_dword(pvt->pci_ras, RASENABLES, &reg);
- if (IS_MIRROR_ENABLED(reg)) {
- edac_dbg(0, "Memory mirror is enabled\n");
- pvt->is_mirrored = true;
- } else {
- edac_dbg(0, "Memory mirror is disabled\n");
+ /* KNL doesn't support mirroring or lockstep,
+ * and is always closed page
+ */
+ if (pvt->info.type == KNIGHTS_LANDING) {
+ mode = EDAC_S4ECD4ED;
pvt->is_mirrored = false;
- }
- pci_read_config_dword(pvt->pci_ta, MCMTR, &pvt->info.mcmtr);
- if (IS_LOCKSTEP_ENABLED(pvt->info.mcmtr)) {
- edac_dbg(0, "Lockstep is enabled\n");
- mode = EDAC_S8ECD8ED;
- pvt->is_lockstep = true;
+ if (knl_get_dimm_capacity(pvt, knl_mc_sizes) != 0)
+ return -1;
} else {
- edac_dbg(0, "Lockstep is disabled\n");
- mode = EDAC_S4ECD4ED;
- pvt->is_lockstep = false;
- }
- if (IS_CLOSE_PG(pvt->info.mcmtr)) {
- edac_dbg(0, "address map is on closed page mode\n");
- pvt->is_close_pg = true;
- } else {
- edac_dbg(0, "address map is on open page mode\n");
- pvt->is_close_pg = false;
+ pci_read_config_dword(pvt->pci_ras, RASENABLES, &reg);
+ if (IS_MIRROR_ENABLED(reg)) {
+ edac_dbg(0, "Memory mirror is enabled\n");
+ pvt->is_mirrored = true;
+ } else {
+ edac_dbg(0, "Memory mirror is disabled\n");
+ pvt->is_mirrored = false;
+ }
+
+ pci_read_config_dword(pvt->pci_ta, MCMTR, &pvt->info.mcmtr);
+ if (IS_LOCKSTEP_ENABLED(pvt->info.mcmtr)) {
+ edac_dbg(0, "Lockstep is enabled\n");
+ mode = EDAC_S8ECD8ED;
+ pvt->is_lockstep = true;
+ } else {
+ edac_dbg(0, "Lockstep is disabled\n");
+ mode = EDAC_S4ECD4ED;
+ pvt->is_lockstep = false;
+ }
+ if (IS_CLOSE_PG(pvt->info.mcmtr)) {
+ edac_dbg(0, "address map is on closed page mode\n");
+ pvt->is_close_pg = true;
+ } else {
+ edac_dbg(0, "address map is on open page mode\n");
+ pvt->is_close_pg = false;
+ }
}
mtype = pvt->info.get_memory_type(pvt);
@@ -988,23 +1684,46 @@ static int get_dimm_config(struct mem_ctl_info *mci)
else
banks = 8;
- for (i = 0; i < NUM_CHANNELS; i++) {
+ for (i = 0; i < channels; i++) {
u32 mtr;
- if (!pvt->pci_tad[i])
- continue;
- for (j = 0; j < ARRAY_SIZE(mtr_regs); j++) {
+ int max_dimms_per_channel;
+
+ if (pvt->info.type == KNIGHTS_LANDING) {
+ max_dimms_per_channel = 1;
+ if (!pvt->knl.pci_channel[i])
+ continue;
+ } else {
+ max_dimms_per_channel = ARRAY_SIZE(mtr_regs);
+ if (!pvt->pci_tad[i])
+ continue;
+ }
+
+ for (j = 0; j < max_dimms_per_channel; j++) {
dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, mci->n_layers,
i, j, 0);
- pci_read_config_dword(pvt->pci_tad[i],
- mtr_regs[j], &mtr);
+ if (pvt->info.type == KNIGHTS_LANDING) {
+ pci_read_config_dword(pvt->knl.pci_channel[i],
+ knl_mtr_reg, &mtr);
+ } else {
+ pci_read_config_dword(pvt->pci_tad[i],
+ mtr_regs[j], &mtr);
+ }
edac_dbg(4, "Channel #%d MTR%d = %x\n", i, j, mtr);
if (IS_DIMM_PRESENT(mtr)) {
pvt->channel[i].dimms++;
ranks = numrank(pvt->info.type, mtr);
- rows = numrow(mtr);
- cols = numcol(mtr);
+
+ if (pvt->info.type == KNIGHTS_LANDING) {
+ /* For DDR4, this is fixed. */
+ cols = 1 << 10;
+ rows = knl_mc_sizes[i] /
+ ((u64) cols * ranks * banks * 8);
+ } else {
+ rows = numrow(mtr);
+ cols = numcol(mtr);
+ }
size = ((u64)rows * cols * banks * ranks) >> (20 - 3);
npages = MiB_TO_PAGES(size);
@@ -1069,7 +1788,7 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
/* SAD_LIMIT Address range is 45:26 */
pci_read_config_dword(pvt->pci_sad0, pvt->info.dram_rule[n_sads],
&reg);
- limit = SAD_LIMIT(reg);
+ limit = pvt->info.sad_limit(reg);
if (!DRAM_RULE_ENABLE(reg))
continue;
@@ -1081,10 +1800,10 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
gb = div_u64_rem(tmp_mb, 1024, &mb);
edac_dbg(0, "SAD#%d %s up to %u.%03u GB (0x%016Lx) Interleave: %s reg=0x%08x\n",
n_sads,
- get_dram_attr(reg),
+ show_dram_attr(pvt->info.dram_attr(reg)),
gb, (mb*1000)/1024,
((u64)tmp_mb) << 20L,
- INTERLEAVE_MODE(reg) ? "8:6" : "[8:6]XOR[18:16]",
+ pvt->info.show_interleave_mode(reg),
reg);
prv = limit;
@@ -1101,6 +1820,9 @@ static void get_memory_layout(const struct mem_ctl_info *mci)
}
}
+ if (pvt->info.type == KNIGHTS_LANDING)
+ return;
+
/*
* Step 3) Get TAD range
*/
@@ -1248,7 +1970,7 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
if (!DRAM_RULE_ENABLE(reg))
continue;
- limit = SAD_LIMIT(reg);
+ limit = pvt->info.sad_limit(reg);
if (limit <= prv) {
sprintf(msg, "Can't discover the memory socket");
return -EINVAL;
@@ -1262,8 +1984,8 @@ static int get_memory_error_data(struct mem_ctl_info *mci,
return -EINVAL;
}
dram_rule = reg;
- *area_type = get_dram_attr(dram_rule);
- interleave_mode = INTERLEAVE_MODE(dram_rule);
+ *area_type = show_dram_attr(pvt->info.dram_attr(dram_rule));
+ interleave_mode = pvt->info.interleave_mode(dram_rule);
pci_read_config_dword(pvt->pci_sad0, pvt->info.interleave_list[n_sads],
&reg);
@@ -1567,7 +2289,8 @@ static void sbridge_put_all_devices(void)
static int sbridge_get_onedevice(struct pci_dev **prev,
u8 *num_mc,
const struct pci_id_table *table,
- const unsigned devno)
+ const unsigned devno,
+ const int multi_bus)
{
struct sbridge_dev *sbridge_dev;
const struct pci_id_descr *dev_descr = &table->descr[devno];
@@ -1603,7 +2326,7 @@ static int sbridge_get_onedevice(struct pci_dev **prev,
}
bus = pdev->bus->number;
- sbridge_dev = get_sbridge_dev(bus);
+ sbridge_dev = get_sbridge_dev(bus, multi_bus);
if (!sbridge_dev) {
sbridge_dev = alloc_sbridge_dev(bus, table);
if (!sbridge_dev) {
@@ -1652,21 +2375,32 @@ static int sbridge_get_onedevice(struct pci_dev **prev,
* @num_mc: pointer to the memory controllers count, to be incremented in case
* of success.
* @table: model specific table
+ * @allow_dups: allow for multiple devices to exist with the same device id
+ * (as implemented, this isn't expected to work correctly in the
+ * multi-socket case).
+ * @multi_bus: don't assume devices on different buses belong to different
+ * memory controllers.
*
* returns 0 in case of success or error code
*/
-static int sbridge_get_all_devices(u8 *num_mc,
- const struct pci_id_table *table)
+static int sbridge_get_all_devices_full(u8 *num_mc,
+ const struct pci_id_table *table,
+ int allow_dups,
+ int multi_bus)
{
int i, rc;
struct pci_dev *pdev = NULL;
while (table && table->descr) {
for (i = 0; i < table->n_devs; i++) {
- pdev = NULL;
+ if (!allow_dups || i == 0 ||
+ table->descr[i].dev_id !=
+ table->descr[i-1].dev_id) {
+ pdev = NULL;
+ }
do {
rc = sbridge_get_onedevice(&pdev, num_mc,
- table, i);
+ table, i, multi_bus);
if (rc < 0) {
if (i == 0) {
i = table->n_devs;
@@ -1675,7 +2409,7 @@ static int sbridge_get_all_devices(u8 *num_mc,
sbridge_put_all_devices();
return -ENODEV;
}
- } while (pdev);
+ } while (pdev && !allow_dups);
}
table++;
}
@@ -1683,6 +2417,11 @@ static int sbridge_get_all_devices(u8 *num_mc,
return 0;
}
+#define sbridge_get_all_devices(num_mc, table) \
+ sbridge_get_all_devices_full(num_mc, table, 0, 0)
+#define sbridge_get_all_devices_knl(num_mc, table) \
+ sbridge_get_all_devices_full(num_mc, table, 1, 1)
+
static int sbridge_mci_bind_devs(struct mem_ctl_info *mci,
struct sbridge_dev *sbridge_dev)
{
@@ -2038,6 +2777,131 @@ enodev:
return -ENODEV;
}
+static int knl_mci_bind_devs(struct mem_ctl_info *mci,
+ struct sbridge_dev *sbridge_dev)
+{
+ struct sbridge_pvt *pvt = mci->pvt_info;
+ struct pci_dev *pdev;
+ int dev, func;
+
+ int i;
+ int devidx;
+
+ for (i = 0; i < sbridge_dev->n_devs; i++) {
+ pdev = sbridge_dev->pdev[i];
+ if (!pdev)
+ continue;
+
+ /* Extract PCI device and function. */
+ dev = (pdev->devfn >> 3) & 0x1f;
+ func = pdev->devfn & 0x7;
+
+ switch (pdev->device) {
+ case PCI_DEVICE_ID_INTEL_KNL_IMC_MC:
+ if (dev == 8)
+ pvt->knl.pci_mc0 = pdev;
+ else if (dev == 9)
+ pvt->knl.pci_mc1 = pdev;
+ else {
+ sbridge_printk(KERN_ERR,
+ "Memory controller in unexpected place! (dev %d, fn %d)\n",
+ dev, func);
+ continue;
+ }
+ break;
+
+ case PCI_DEVICE_ID_INTEL_KNL_IMC_SAD0:
+ pvt->pci_sad0 = pdev;
+ break;
+
+ case PCI_DEVICE_ID_INTEL_KNL_IMC_SAD1:
+ pvt->pci_sad1 = pdev;
+ break;
+
+ case PCI_DEVICE_ID_INTEL_KNL_IMC_CHA:
+ /* There are one of these per tile, and range from
+ * 1.14.0 to 1.18.5.
+ */
+ devidx = ((dev-14)*8)+func;
+
+ if (devidx < 0 || devidx >= KNL_MAX_CHAS) {
+ sbridge_printk(KERN_ERR,
+ "Caching and Home Agent in unexpected place! (dev %d, fn %d)\n",
+ dev, func);
+ continue;
+ }
+
+ WARN_ON(pvt->knl.pci_cha[devidx] != NULL);
+
+ pvt->knl.pci_cha[devidx] = pdev;
+ break;
+
+ case PCI_DEVICE_ID_INTEL_KNL_IMC_CHANNEL:
+ devidx = -1;
+
+ /*
+ * MC0 channels 0-2 are device 9 function 2-4,
+ * MC1 channels 3-5 are device 8 function 2-4.
+ */
+
+ if (dev == 9)
+ devidx = func-2;
+ else if (dev == 8)
+ devidx = 3 + (func-2);
+
+ if (devidx < 0 || devidx >= KNL_MAX_CHANNELS) {
+ sbridge_printk(KERN_ERR,
+ "DRAM Channel Registers in unexpected place! (dev %d, fn %d)\n",
+ dev, func);
+ continue;
+ }
+
+ WARN_ON(pvt->knl.pci_channel[devidx] != NULL);
+ pvt->knl.pci_channel[devidx] = pdev;
+ break;
+
+ case PCI_DEVICE_ID_INTEL_KNL_IMC_TOLHM:
+ pvt->knl.pci_mc_info = pdev;
+ break;
+
+ case PCI_DEVICE_ID_INTEL_KNL_IMC_TA:
+ pvt->pci_ta = pdev;
+ break;
+
+ default:
+ sbridge_printk(KERN_ERR, "Unexpected device %d\n",
+ pdev->device);
+ break;
+ }
+ }
+
+ if (!pvt->knl.pci_mc0 || !pvt->knl.pci_mc1 ||
+ !pvt->pci_sad0 || !pvt->pci_sad1 ||
+ !pvt->pci_ta) {
+ goto enodev;
+ }
+
+ for (i = 0; i < KNL_MAX_CHANNELS; i++) {
+ if (!pvt->knl.pci_channel[i]) {
+ sbridge_printk(KERN_ERR, "Missing channel %d\n", i);
+ goto enodev;
+ }
+ }
+
+ for (i = 0; i < KNL_MAX_CHAS; i++) {
+ if (!pvt->knl.pci_cha[i]) {
+ sbridge_printk(KERN_ERR, "Missing CHA %d\n", i);
+ goto enodev;
+ }
+ }
+
+ return 0;
+
+enodev:
+ sbridge_printk(KERN_ERR, "Some needed devices are missing\n");
+ return -ENODEV;
+}
+
/****************************************************************************
Error check routines
****************************************************************************/
@@ -2127,8 +2991,36 @@ static void sbridge_mce_output_error(struct mem_ctl_info *mci,
if (!GET_BITFIELD(m->status, 58, 58))
return;
- rc = get_memory_error_data(mci, m->addr, &socket, &ha,
- &channel_mask, &rank, &area_type, msg);
+ if (pvt->info.type == KNIGHTS_LANDING) {
+ if (channel == 14) {
+ edac_dbg(0, "%s%s err_code:%04x:%04x EDRAM bank %d\n",
+ overflow ? " OVERFLOW" : "",
+ (uncorrected_error && recoverable)
+ ? " recoverable" : "",
+ mscod, errcode,
+ m->bank);
+ } else {
+ char A = *("A");
+
+ channel = knl_channel_remap(channel);
+ channel_mask = 1 << channel;
+ snprintf(msg, sizeof(msg),
+ "%s%s err_code:%04x:%04x channel:%d (DIMM_%c)",
+ overflow ? " OVERFLOW" : "",
+ (uncorrected_error && recoverable)
+ ? " recoverable" : " ",
+ mscod, errcode, channel, A + channel);
+ edac_mc_handle_error(tp_event, mci, core_err_cnt,
+ m->addr >> PAGE_SHIFT, m->addr & ~PAGE_MASK, 0,
+ channel, 0, -1,
+ optype, msg);
+ }
+ return;
+ } else {
+ rc = get_memory_error_data(mci, m->addr, &socket, &ha,
+ &channel_mask, &rank, &area_type, msg);
+ }
+
if (rc < 0)
goto err_parsing;
new_mci = get_mci_for_node_id(socket);
@@ -2359,10 +3251,11 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
/* allocate a new MC control structure */
layers[0].type = EDAC_MC_LAYER_CHANNEL;
- layers[0].size = NUM_CHANNELS;
+ layers[0].size = type == KNIGHTS_LANDING ?
+ KNL_MAX_CHANNELS : NUM_CHANNELS;
layers[0].is_virt_csrow = false;
layers[1].type = EDAC_MC_LAYER_SLOT;
- layers[1].size = MAX_DIMMS;
+ layers[1].size = type == KNIGHTS_LANDING ? 1 : MAX_DIMMS;
layers[1].is_virt_csrow = true;
mci = edac_mc_alloc(sbridge_dev->mc, ARRAY_SIZE(layers), layers,
sizeof(*pvt));
@@ -2380,7 +3273,8 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
pvt->sbridge_dev = sbridge_dev;
sbridge_dev->mci = mci;
- mci->mtype_cap = MEM_FLAG_DDR3;
+ mci->mtype_cap = type == KNIGHTS_LANDING ?
+ MEM_FLAG_DDR4 : MEM_FLAG_DDR3;
mci->edac_ctl_cap = EDAC_FLAG_NONE;
mci->edac_cap = EDAC_FLAG_NONE;
mci->mod_name = "sbridge_edac.c";
@@ -2401,6 +3295,10 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
pvt->info.get_memory_type = get_memory_type;
pvt->info.get_node_id = get_node_id;
pvt->info.rir_limit = rir_limit;
+ pvt->info.sad_limit = sad_limit;
+ pvt->info.interleave_mode = interleave_mode;
+ pvt->info.show_interleave_mode = show_interleave_mode;
+ pvt->info.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule);
pvt->info.interleave_list = ibridge_interleave_list;
pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list);
@@ -2421,6 +3319,10 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
pvt->info.get_memory_type = get_memory_type;
pvt->info.get_node_id = get_node_id;
pvt->info.rir_limit = rir_limit;
+ pvt->info.sad_limit = sad_limit;
+ pvt->info.interleave_mode = interleave_mode;
+ pvt->info.show_interleave_mode = show_interleave_mode;
+ pvt->info.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(sbridge_dram_rule);
pvt->info.interleave_list = sbridge_interleave_list;
pvt->info.max_interleave = ARRAY_SIZE(sbridge_interleave_list);
@@ -2441,6 +3343,10 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
pvt->info.get_memory_type = haswell_get_memory_type;
pvt->info.get_node_id = haswell_get_node_id;
pvt->info.rir_limit = haswell_rir_limit;
+ pvt->info.sad_limit = sad_limit;
+ pvt->info.interleave_mode = interleave_mode;
+ pvt->info.show_interleave_mode = show_interleave_mode;
+ pvt->info.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule);
pvt->info.interleave_list = ibridge_interleave_list;
pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list);
@@ -2461,6 +3367,10 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
pvt->info.get_memory_type = haswell_get_memory_type;
pvt->info.get_node_id = haswell_get_node_id;
pvt->info.rir_limit = haswell_rir_limit;
+ pvt->info.sad_limit = sad_limit;
+ pvt->info.interleave_mode = interleave_mode;
+ pvt->info.show_interleave_mode = show_interleave_mode;
+ pvt->info.dram_attr = dram_attr;
pvt->info.max_sad = ARRAY_SIZE(ibridge_dram_rule);
pvt->info.interleave_list = ibridge_interleave_list;
pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list);
@@ -2473,6 +3383,30 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
if (unlikely(rc < 0))
goto fail0;
break;
+ case KNIGHTS_LANDING:
+ /* pvt->info.rankcfgr == ??? */
+ pvt->info.get_tolm = knl_get_tolm;
+ pvt->info.get_tohm = knl_get_tohm;
+ pvt->info.dram_rule = knl_dram_rule;
+ pvt->info.get_memory_type = knl_get_memory_type;
+ pvt->info.get_node_id = knl_get_node_id;
+ pvt->info.rir_limit = NULL;
+ pvt->info.sad_limit = knl_sad_limit;
+ pvt->info.interleave_mode = knl_interleave_mode;
+ pvt->info.show_interleave_mode = knl_show_interleave_mode;
+ pvt->info.dram_attr = dram_attr_knl;
+ pvt->info.max_sad = ARRAY_SIZE(knl_dram_rule);
+ pvt->info.interleave_list = knl_interleave_list;
+ pvt->info.max_interleave = ARRAY_SIZE(knl_interleave_list);
+ pvt->info.interleave_pkg = ibridge_interleave_pkg;
+ pvt->info.get_width = knl_get_width;
+ mci->ctl_name = kasprintf(GFP_KERNEL,
+ "Knights Landing Socket#%d", mci->mc_idx);
+
+ rc = knl_mci_bind_devs(mci, sbridge_dev);
+ if (unlikely(rc < 0))
+ goto fail0;
+ break;
}
/* Get dimm basic config and the memory layout */
@@ -2527,20 +3461,29 @@ static int sbridge_probe(struct pci_dev *pdev, const struct pci_device_id *id)
switch (pdev->device) {
case PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA:
- rc = sbridge_get_all_devices(&num_mc, pci_dev_descr_ibridge_table);
+ rc = sbridge_get_all_devices(&num_mc,
+ pci_dev_descr_ibridge_table);
type = IVY_BRIDGE;
break;
case PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_HA0:
- rc = sbridge_get_all_devices(&num_mc, pci_dev_descr_sbridge_table);
+ rc = sbridge_get_all_devices(&num_mc,
+ pci_dev_descr_sbridge_table);
type = SANDY_BRIDGE;
break;
case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0:
- rc = sbridge_get_all_devices(&num_mc, pci_dev_descr_haswell_table);
+ rc = sbridge_get_all_devices(&num_mc,
+ pci_dev_descr_haswell_table);
type = HASWELL;
break;
case PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0:
- rc = sbridge_get_all_devices(&num_mc, pci_dev_descr_broadwell_table);
+ rc = sbridge_get_all_devices(&num_mc,
+ pci_dev_descr_broadwell_table);
type = BROADWELL;
+ break;
+ case PCI_DEVICE_ID_INTEL_KNL_IMC_SAD0:
+ rc = sbridge_get_all_devices_knl(&num_mc,
+ pci_dev_descr_knl_table);
+ type = KNIGHTS_LANDING;
break;
}
if (unlikely(rc < 0)) {
diff --git a/drivers/edac/wq.c b/drivers/edac/wq.c
new file mode 100644
index 000000000000..1b8c07e44fd8
--- /dev/null
+++ b/drivers/edac/wq.c
@@ -0,0 +1,42 @@
+#include "edac_module.h"
+
+static struct workqueue_struct *wq;
+
+bool edac_queue_work(struct delayed_work *work, unsigned long delay)
+{
+ return queue_delayed_work(wq, work, delay);
+}
+EXPORT_SYMBOL_GPL(edac_queue_work);
+
+bool edac_mod_work(struct delayed_work *work, unsigned long delay)
+{
+ return mod_delayed_work(wq, work, delay);
+}
+EXPORT_SYMBOL_GPL(edac_mod_work);
+
+bool edac_stop_work(struct delayed_work *work)
+{
+ bool ret;
+
+ ret = cancel_delayed_work_sync(work);
+ flush_workqueue(wq);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(edac_stop_work);
+
+int edac_workqueue_setup(void)
+{
+ wq = create_singlethread_workqueue("edac-poller");
+ if (!wq)
+ return -ENODEV;
+ else
+ return 0;
+}
+
+void edac_workqueue_teardown(void)
+{
+ flush_workqueue(wq);
+ destroy_workqueue(wq);
+ wq = NULL;
+}
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
index 0cebbf668886..3d89e60a3e71 100644
--- a/drivers/extcon/Kconfig
+++ b/drivers/extcon/Kconfig
@@ -52,6 +52,15 @@ config EXTCON_MAX14577
Maxim MAX14577/77836. The MAX14577/77836 MUIC is a USB port accessory
detector and switch.
+config EXTCON_MAX3355
+ tristate "Maxim MAX3355 USB OTG EXTCON Support"
+ depends on GPIOLIB || COMPILE_TEST
+ help
+ If you say yes here you get support for the USB OTG role detection by
+ MAX3355. The MAX3355 chip integrates a charge pump and comparators to
+ enable a system with an integrated USB OTG dual-role transceiver to
+ function as an USB OTG dual-role device.
+
config EXTCON_MAX77693
tristate "Maxim MAX77693 EXTCON Support"
depends on MFD_MAX77693 && INPUT
diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile
index ba787d04295b..2a0e4f45d5b2 100644
--- a/drivers/extcon/Makefile
+++ b/drivers/extcon/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o
obj-$(CONFIG_EXTCON_AXP288) += extcon-axp288.o
obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o
obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o
+obj-$(CONFIG_EXTCON_MAX3355) += extcon-max3355.o
obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o
obj-$(CONFIG_EXTCON_MAX77843) += extcon-max77843.o
obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o
diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c
index e4890dd4fefd..c121d01a5cd6 100644
--- a/drivers/extcon/extcon-arizona.c
+++ b/drivers/extcon/extcon-arizona.c
@@ -1201,10 +1201,58 @@ static void arizona_micd_set_level(struct arizona *arizona, int index,
regmap_update_bits(arizona->regmap, reg, mask, level);
}
-static int arizona_extcon_device_get_pdata(struct arizona *arizona)
+static int arizona_extcon_get_micd_configs(struct device *dev,
+ struct arizona *arizona)
+{
+ const char * const prop = "wlf,micd-configs";
+ const int entries_per_config = 3;
+ struct arizona_micd_config *micd_configs;
+ int nconfs, ret;
+ int i, j;
+ u32 *vals;
+
+ nconfs = device_property_read_u32_array(arizona->dev, prop, NULL, 0);
+ if (nconfs <= 0)
+ return 0;
+
+ vals = kcalloc(nconfs, sizeof(u32), GFP_KERNEL);
+ if (!vals)
+ return -ENOMEM;
+
+ ret = device_property_read_u32_array(arizona->dev, prop, vals, nconfs);
+ if (ret < 0)
+ goto out;
+
+ nconfs /= entries_per_config;
+
+ micd_configs = devm_kzalloc(dev,
+ nconfs * sizeof(struct arizona_micd_range),
+ GFP_KERNEL);
+ if (!micd_configs) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0, j = 0; i < nconfs; ++i) {
+ micd_configs[i].src = vals[j++] ? ARIZONA_ACCDET_SRC : 0;
+ micd_configs[i].bias = vals[j++];
+ micd_configs[i].gpio = vals[j++];
+ }
+
+ arizona->pdata.micd_configs = micd_configs;
+ arizona->pdata.num_micd_configs = nconfs;
+
+out:
+ kfree(vals);
+ return ret;
+}
+
+static int arizona_extcon_device_get_pdata(struct device *dev,
+ struct arizona *arizona)
{
struct arizona_pdata *pdata = &arizona->pdata;
unsigned int val = ARIZONA_ACCDET_MODE_HPL;
+ int ret;
device_property_read_u32(arizona->dev, "wlf,hpdet-channel", &val);
switch (val) {
@@ -1230,12 +1278,29 @@ static int arizona_extcon_device_get_pdata(struct arizona *arizona)
device_property_read_u32(arizona->dev, "wlf,micd-dbtime",
&pdata->micd_dbtime);
- device_property_read_u32(arizona->dev, "wlf,micd-timeout",
+ device_property_read_u32(arizona->dev, "wlf,micd-timeout-ms",
&pdata->micd_timeout);
pdata->micd_force_micbias = device_property_read_bool(arizona->dev,
"wlf,micd-force-micbias");
+ pdata->micd_software_compare = device_property_read_bool(arizona->dev,
+ "wlf,micd-software-compare");
+
+ pdata->jd_invert = device_property_read_bool(arizona->dev,
+ "wlf,jd-invert");
+
+ device_property_read_u32(arizona->dev, "wlf,gpsw", &pdata->gpsw);
+
+ pdata->jd_gpio5 = device_property_read_bool(arizona->dev,
+ "wlf,use-jd2");
+ pdata->jd_gpio5_nopull = device_property_read_bool(arizona->dev,
+ "wlf,use-jd2-nopull");
+
+ ret = arizona_extcon_get_micd_configs(dev, arizona);
+ if (ret < 0)
+ dev_err(arizona->dev, "Failed to read micd configs: %d\n", ret);
+
return 0;
}
@@ -1257,7 +1322,7 @@ static int arizona_extcon_probe(struct platform_device *pdev)
return -ENOMEM;
if (!dev_get_platdata(arizona->dev))
- arizona_extcon_device_get_pdata(arizona);
+ arizona_extcon_device_get_pdata(&pdev->dev, arizona);
info->micvdd = devm_regulator_get(&pdev->dev, "MICVDD");
if (IS_ERR(info->micvdd)) {
diff --git a/drivers/extcon/extcon-max14577.c b/drivers/extcon/extcon-max14577.c
index 601dbd996487..b30ab97ce75f 100644
--- a/drivers/extcon/extcon-max14577.c
+++ b/drivers/extcon/extcon-max14577.c
@@ -692,7 +692,7 @@ static int max14577_muic_probe(struct platform_device *pdev)
/* Support irq domain for max14577 MUIC device */
for (i = 0; i < info->muic_irqs_num; i++) {
struct max14577_muic_irq *muic_irq = &info->muic_irqs[i];
- unsigned int virq = 0;
+ int virq = 0;
virq = regmap_irq_get_virq(max14577->irq_data, muic_irq->irq);
if (virq <= 0)
diff --git a/drivers/extcon/extcon-max3355.c b/drivers/extcon/extcon-max3355.c
new file mode 100644
index 000000000000..c24abec5d06c
--- /dev/null
+++ b/drivers/extcon/extcon-max3355.c
@@ -0,0 +1,146 @@
+/*
+ * Maxim Integrated MAX3355 USB OTG chip extcon driver
+ *
+ * Copyright (C) 2014-2015 Cogent Embedded, Inc.
+ * Author: Sergei Shtylyov <sergei.shtylyov@cogentembedded.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.
+ */
+
+#include <linux/extcon.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+struct max3355_data {
+ struct extcon_dev *edev;
+ struct gpio_desc *id_gpiod;
+ struct gpio_desc *shdn_gpiod;
+};
+
+static const unsigned int max3355_cable[] = {
+ EXTCON_USB,
+ EXTCON_USB_HOST,
+ EXTCON_NONE,
+};
+
+static irqreturn_t max3355_id_irq(int irq, void *dev_id)
+{
+ struct max3355_data *data = dev_id;
+ int id = gpiod_get_value_cansleep(data->id_gpiod);
+
+ if (id) {
+ /*
+ * ID = 1 means USB HOST cable detached.
+ * As we don't have event for USB peripheral cable attached,
+ * we simulate USB peripheral attach here.
+ */
+ extcon_set_cable_state_(data->edev, EXTCON_USB_HOST, false);
+ extcon_set_cable_state_(data->edev, EXTCON_USB, true);
+ } else {
+ /*
+ * ID = 0 means USB HOST cable attached.
+ * As we don't have event for USB peripheral cable detached,
+ * we simulate USB peripheral detach here.
+ */
+ extcon_set_cable_state_(data->edev, EXTCON_USB, false);
+ extcon_set_cable_state_(data->edev, EXTCON_USB_HOST, true);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int max3355_probe(struct platform_device *pdev)
+{
+ struct max3355_data *data;
+ struct gpio_desc *gpiod;
+ int irq, err;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct max3355_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ gpiod = devm_gpiod_get(&pdev->dev, "id", GPIOD_IN);
+ if (IS_ERR(gpiod)) {
+ dev_err(&pdev->dev, "failed to get ID_OUT GPIO\n");
+ return PTR_ERR(gpiod);
+ }
+ data->id_gpiod = gpiod;
+
+ gpiod = devm_gpiod_get(&pdev->dev, "maxim,shdn", GPIOD_OUT_HIGH);
+ if (IS_ERR(gpiod)) {
+ dev_err(&pdev->dev, "failed to get SHDN# GPIO\n");
+ return PTR_ERR(gpiod);
+ }
+ data->shdn_gpiod = gpiod;
+
+ data->edev = devm_extcon_dev_allocate(&pdev->dev, max3355_cable);
+ if (IS_ERR(data->edev)) {
+ dev_err(&pdev->dev, "failed to allocate extcon device\n");
+ return PTR_ERR(data->edev);
+ }
+
+ err = devm_extcon_dev_register(&pdev->dev, data->edev);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to register extcon device\n");
+ return err;
+ }
+
+ irq = gpiod_to_irq(data->id_gpiod);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to translate ID_OUT GPIO to IRQ\n");
+ return irq;
+ }
+
+ err = devm_request_threaded_irq(&pdev->dev, irq, NULL, max3355_id_irq,
+ IRQF_ONESHOT | IRQF_NO_SUSPEND |
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING,
+ pdev->name, data);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to request ID_OUT IRQ\n");
+ return err;
+ }
+
+ platform_set_drvdata(pdev, data);
+
+ /* Perform initial detection */
+ max3355_id_irq(irq, data);
+
+ return 0;
+}
+
+static int max3355_remove(struct platform_device *pdev)
+{
+ struct max3355_data *data = platform_get_drvdata(pdev);
+
+ gpiod_set_value_cansleep(data->shdn_gpiod, 0);
+
+ return 0;
+}
+
+static const struct of_device_id max3355_match_table[] = {
+ { .compatible = "maxim,max3355", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max3355_match_table);
+
+static struct platform_driver max3355_driver = {
+ .probe = max3355_probe,
+ .remove = max3355_remove,
+ .driver = {
+ .name = "extcon-max3355",
+ .of_match_table = max3355_match_table,
+ },
+};
+
+module_platform_driver(max3355_driver);
+
+MODULE_AUTHOR("Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>");
+MODULE_DESCRIPTION("Maxim MAX3355 extcon driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c
index 44c499e1beee..fdf8f5d4d4e9 100644
--- a/drivers/extcon/extcon-max77693.c
+++ b/drivers/extcon/extcon-max77693.c
@@ -1127,11 +1127,11 @@ static int max77693_muic_probe(struct platform_device *pdev)
/* Support irq domain for MAX77693 MUIC device */
for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) {
struct max77693_muic_irq *muic_irq = &muic_irqs[i];
- unsigned int virq = 0;
+ int virq;
virq = regmap_irq_get_virq(max77693->irq_data_muic,
muic_irq->irq);
- if (!virq)
+ if (virq <= 0)
return -EINVAL;
muic_irq->virq = virq;
diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c
index 9f9ea334399c..74dfb7f4f277 100644
--- a/drivers/extcon/extcon-max77843.c
+++ b/drivers/extcon/extcon-max77843.c
@@ -811,7 +811,7 @@ static int max77843_muic_probe(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(max77843_muic_irqs); i++) {
struct max77843_muic_irq *muic_irq = &max77843_muic_irqs[i];
- unsigned int virq = 0;
+ int virq = 0;
virq = regmap_irq_get_virq(max77843->irq_data_muic,
muic_irq->irq);
diff --git a/drivers/extcon/extcon-rt8973a.c b/drivers/extcon/extcon-rt8973a.c
index 36bf1d63791c..e1bb82809bef 100644
--- a/drivers/extcon/extcon-rt8973a.c
+++ b/drivers/extcon/extcon-rt8973a.c
@@ -603,7 +603,7 @@ static int rt8973a_muic_i2c_probe(struct i2c_client *i2c,
ret = devm_request_threaded_irq(info->dev, virq, NULL,
rt8973a_muic_irq_handler,
- IRQF_NO_SUSPEND,
+ IRQF_NO_SUSPEND | IRQF_ONESHOT,
muic_irq->name, info);
if (ret) {
dev_err(info->dev,
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index cf478fe6b335..49a3a1185bb6 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -173,6 +173,9 @@ config QCOM_SCM_64
def_bool y
depends on QCOM_SCM && ARM64
+config HAVE_ARM_SMCCC
+ bool
+
source "drivers/firmware/broadcom/Kconfig"
source "drivers/firmware/google/Kconfig"
source "drivers/firmware/efi/Kconfig"
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index ac1ce4a73edf..0e08e665f715 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -521,6 +521,7 @@ static int __init dmi_present(const u8 *buf)
dmi_ver = smbios_ver;
else
dmi_ver = (buf[14] & 0xF0) << 4 | (buf[14] & 0x0F);
+ dmi_ver <<= 8;
dmi_num = get_unaligned_le16(buf + 12);
dmi_len = get_unaligned_le16(buf + 6);
dmi_base = get_unaligned_le32(buf + 8);
@@ -528,15 +529,14 @@ static int __init dmi_present(const u8 *buf)
if (dmi_walk_early(dmi_decode) == 0) {
if (smbios_ver) {
pr_info("SMBIOS %d.%d present.\n",
- dmi_ver >> 8, dmi_ver & 0xFF);
+ dmi_ver >> 16, (dmi_ver >> 8) & 0xFF);
} else {
smbios_entry_point_size = 15;
memcpy(smbios_entry_point, buf,
smbios_entry_point_size);
pr_info("Legacy DMI %d.%d present.\n",
- dmi_ver >> 8, dmi_ver & 0xFF);
+ dmi_ver >> 16, (dmi_ver >> 8) & 0xFF);
}
- dmi_ver <<= 8;
dmi_format_ids(dmi_ids_string, sizeof(dmi_ids_string));
printk(KERN_DEBUG "DMI: %s\n", dmi_ids_string);
return 0;
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
index ec379a4164cc..62e654f255f4 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -18,3 +18,7 @@ obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o
obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o
obj-$(CONFIG_EFI_STUB) += libstub/
obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_mem.o
+
+arm-obj-$(CONFIG_EFI) := arm-init.o arm-runtime.o
+obj-$(CONFIG_ARM) += $(arm-obj-y)
+obj-$(CONFIG_ARM64) += $(arm-obj-y)
diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c
new file mode 100644
index 000000000000..9e15d571b53c
--- /dev/null
+++ b/drivers/firmware/efi/arm-init.c
@@ -0,0 +1,209 @@
+/*
+ * Extensible Firmware Interface
+ *
+ * Based on Extensible Firmware Interface Specification version 2.4
+ *
+ * Copyright (C) 2013 - 2015 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/efi.h>
+#include <linux/init.h>
+#include <linux/memblock.h>
+#include <linux/mm_types.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+
+#include <asm/efi.h>
+
+struct efi_memory_map memmap;
+
+u64 efi_system_table;
+
+static int __init is_normal_ram(efi_memory_desc_t *md)
+{
+ if (md->attribute & EFI_MEMORY_WB)
+ return 1;
+ return 0;
+}
+
+/*
+ * Translate a EFI virtual address into a physical address: this is necessary,
+ * as some data members of the EFI system table are virtually remapped after
+ * SetVirtualAddressMap() has been called.
+ */
+static phys_addr_t efi_to_phys(unsigned long addr)
+{
+ efi_memory_desc_t *md;
+
+ for_each_efi_memory_desc(&memmap, md) {
+ if (!(md->attribute & EFI_MEMORY_RUNTIME))
+ continue;
+ if (md->virt_addr == 0)
+ /* no virtual mapping has been installed by the stub */
+ break;
+ if (md->virt_addr <= addr &&
+ (addr - md->virt_addr) < (md->num_pages << EFI_PAGE_SHIFT))
+ return md->phys_addr + addr - md->virt_addr;
+ }
+ return addr;
+}
+
+static int __init uefi_init(void)
+{
+ efi_char16_t *c16;
+ void *config_tables;
+ size_t table_size;
+ char vendor[100] = "unknown";
+ int i, retval;
+
+ efi.systab = early_memremap(efi_system_table,
+ sizeof(efi_system_table_t));
+ if (efi.systab == NULL) {
+ pr_warn("Unable to map EFI system table.\n");
+ return -ENOMEM;
+ }
+
+ set_bit(EFI_BOOT, &efi.flags);
+ if (IS_ENABLED(CONFIG_64BIT))
+ set_bit(EFI_64BIT, &efi.flags);
+
+ /*
+ * Verify the EFI Table
+ */
+ if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) {
+ pr_err("System table signature incorrect\n");
+ retval = -EINVAL;
+ goto out;
+ }
+ if ((efi.systab->hdr.revision >> 16) < 2)
+ pr_warn("Warning: EFI system table version %d.%02d, expected 2.00 or greater\n",
+ efi.systab->hdr.revision >> 16,
+ efi.systab->hdr.revision & 0xffff);
+
+ /* Show what we know for posterity */
+ c16 = early_memremap(efi_to_phys(efi.systab->fw_vendor),
+ sizeof(vendor) * sizeof(efi_char16_t));
+ if (c16) {
+ for (i = 0; i < (int) sizeof(vendor) - 1 && *c16; ++i)
+ vendor[i] = c16[i];
+ vendor[i] = '\0';
+ early_memunmap(c16, sizeof(vendor) * sizeof(efi_char16_t));
+ }
+
+ pr_info("EFI v%u.%.02u by %s\n",
+ efi.systab->hdr.revision >> 16,
+ efi.systab->hdr.revision & 0xffff, vendor);
+
+ table_size = sizeof(efi_config_table_64_t) * efi.systab->nr_tables;
+ config_tables = early_memremap(efi_to_phys(efi.systab->tables),
+ table_size);
+ if (config_tables == NULL) {
+ pr_warn("Unable to map EFI config table array.\n");
+ retval = -ENOMEM;
+ goto out;
+ }
+ retval = efi_config_parse_tables(config_tables, efi.systab->nr_tables,
+ sizeof(efi_config_table_t), NULL);
+
+ early_memunmap(config_tables, table_size);
+out:
+ early_memunmap(efi.systab, sizeof(efi_system_table_t));
+ return retval;
+}
+
+/*
+ * Return true for RAM regions we want to permanently reserve.
+ */
+static __init int is_reserve_region(efi_memory_desc_t *md)
+{
+ switch (md->type) {
+ case EFI_LOADER_CODE:
+ case EFI_LOADER_DATA:
+ case EFI_BOOT_SERVICES_CODE:
+ case EFI_BOOT_SERVICES_DATA:
+ case EFI_CONVENTIONAL_MEMORY:
+ case EFI_PERSISTENT_MEMORY:
+ return 0;
+ default:
+ break;
+ }
+ return is_normal_ram(md);
+}
+
+static __init void reserve_regions(void)
+{
+ efi_memory_desc_t *md;
+ u64 paddr, npages, size;
+
+ if (efi_enabled(EFI_DBG))
+ pr_info("Processing EFI memory map:\n");
+
+ for_each_efi_memory_desc(&memmap, md) {
+ paddr = md->phys_addr;
+ npages = md->num_pages;
+
+ if (efi_enabled(EFI_DBG)) {
+ char buf[64];
+
+ pr_info(" 0x%012llx-0x%012llx %s",
+ paddr, paddr + (npages << EFI_PAGE_SHIFT) - 1,
+ efi_md_typeattr_format(buf, sizeof(buf), md));
+ }
+
+ memrange_efi_to_native(&paddr, &npages);
+ size = npages << PAGE_SHIFT;
+
+ if (is_normal_ram(md))
+ early_init_dt_add_memory_arch(paddr, size);
+
+ if (is_reserve_region(md)) {
+ memblock_mark_nomap(paddr, size);
+ if (efi_enabled(EFI_DBG))
+ pr_cont("*");
+ }
+
+ if (efi_enabled(EFI_DBG))
+ pr_cont("\n");
+ }
+
+ set_bit(EFI_MEMMAP, &efi.flags);
+}
+
+void __init efi_init(void)
+{
+ struct efi_fdt_params params;
+
+ /* Grab UEFI information placed in FDT by stub */
+ if (!efi_get_fdt_params(&params))
+ return;
+
+ efi_system_table = params.system_table;
+
+ memmap.phys_map = params.mmap;
+ memmap.map = early_memremap(params.mmap, params.mmap_size);
+ if (memmap.map == NULL) {
+ /*
+ * If we are booting via UEFI, the UEFI memory map is the only
+ * description of memory we have, so there is little point in
+ * proceeding if we cannot access it.
+ */
+ panic("Unable to map EFI memory map.\n");
+ }
+ memmap.map_end = memmap.map + params.mmap_size;
+ memmap.desc_size = params.desc_size;
+ memmap.desc_version = params.desc_ver;
+
+ if (uefi_init() < 0)
+ return;
+
+ reserve_regions();
+ early_memunmap(memmap.map, params.mmap_size);
+ memblock_mark_nomap(params.mmap & PAGE_MASK,
+ PAGE_ALIGN(params.mmap_size +
+ (params.mmap & ~PAGE_MASK)));
+}
diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c
new file mode 100644
index 000000000000..6ae21e41a429
--- /dev/null
+++ b/drivers/firmware/efi/arm-runtime.c
@@ -0,0 +1,135 @@
+/*
+ * Extensible Firmware Interface
+ *
+ * Based on Extensible Firmware Interface Specification version 2.4
+ *
+ * Copyright (C) 2013, 2014 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/efi.h>
+#include <linux/io.h>
+#include <linux/memblock.h>
+#include <linux/mm_types.h>
+#include <linux/preempt.h>
+#include <linux/rbtree.h>
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <asm/cacheflush.h>
+#include <asm/efi.h>
+#include <asm/mmu.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+
+extern u64 efi_system_table;
+
+static struct mm_struct efi_mm = {
+ .mm_rb = RB_ROOT,
+ .mm_users = ATOMIC_INIT(2),
+ .mm_count = ATOMIC_INIT(1),
+ .mmap_sem = __RWSEM_INITIALIZER(efi_mm.mmap_sem),
+ .page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock),
+ .mmlist = LIST_HEAD_INIT(efi_mm.mmlist),
+};
+
+static bool __init efi_virtmap_init(void)
+{
+ efi_memory_desc_t *md;
+
+ efi_mm.pgd = pgd_alloc(&efi_mm);
+ init_new_context(NULL, &efi_mm);
+
+ for_each_efi_memory_desc(&memmap, md) {
+ phys_addr_t phys = md->phys_addr;
+ int ret;
+
+ if (!(md->attribute & EFI_MEMORY_RUNTIME))
+ continue;
+ if (md->virt_addr == 0)
+ return false;
+
+ ret = efi_create_mapping(&efi_mm, md);
+ if (!ret) {
+ pr_info(" EFI remap %pa => %p\n",
+ &phys, (void *)(unsigned long)md->virt_addr);
+ } else {
+ pr_warn(" EFI remap %pa: failed to create mapping (%d)\n",
+ &phys, ret);
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ * Enable the UEFI Runtime Services if all prerequisites are in place, i.e.,
+ * non-early mapping of the UEFI system table and virtual mappings for all
+ * EFI_MEMORY_RUNTIME regions.
+ */
+static int __init arm_enable_runtime_services(void)
+{
+ u64 mapsize;
+
+ if (!efi_enabled(EFI_BOOT)) {
+ pr_info("EFI services will not be available.\n");
+ return 0;
+ }
+
+ if (efi_runtime_disabled()) {
+ pr_info("EFI runtime services will be disabled.\n");
+ return 0;
+ }
+
+ pr_info("Remapping and enabling EFI services.\n");
+
+ mapsize = memmap.map_end - memmap.map;
+ memmap.map = (__force void *)ioremap_cache(memmap.phys_map,
+ mapsize);
+ if (!memmap.map) {
+ pr_err("Failed to remap EFI memory map\n");
+ return -ENOMEM;
+ }
+ memmap.map_end = memmap.map + mapsize;
+ efi.memmap = &memmap;
+
+ efi.systab = (__force void *)ioremap_cache(efi_system_table,
+ sizeof(efi_system_table_t));
+ if (!efi.systab) {
+ pr_err("Failed to remap EFI System Table\n");
+ return -ENOMEM;
+ }
+ set_bit(EFI_SYSTEM_TABLES, &efi.flags);
+
+ if (!efi_virtmap_init()) {
+ pr_err("No UEFI virtual mapping was installed -- runtime services will not be available\n");
+ return -ENOMEM;
+ }
+
+ /* Set up runtime services function pointers */
+ efi_native_runtime_setup();
+ set_bit(EFI_RUNTIME_SERVICES, &efi.flags);
+
+ efi.runtime_version = efi.systab->hdr.revision;
+
+ return 0;
+}
+early_initcall(arm_enable_runtime_services);
+
+void efi_virtmap_load(void)
+{
+ preempt_disable();
+ efi_set_pgd(&efi_mm);
+}
+
+void efi_virtmap_unload(void)
+{
+ efi_set_pgd(current->active_mm);
+ preempt_enable();
+}
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 027ca212179f..2cd37dad67a6 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -25,6 +25,8 @@
#include <linux/io.h>
#include <linux/platform_device.h>
+#include <asm/early_ioremap.h>
+
struct efi __read_mostly efi = {
.mps = EFI_INVALID_TABLE_ADDR,
.acpi = EFI_INVALID_TABLE_ADDR,
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index 3c0467d3688c..9c12e18031d5 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -8,7 +8,7 @@ cflags-$(CONFIG_X86_32) := -march=i386
cflags-$(CONFIG_X86_64) := -mcmodel=small
cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ $(LINUX_INCLUDE) -O2 \
-fPIC -fno-strict-aliasing -mno-red-zone \
- -mno-mmx -mno-sse -DDISABLE_BRANCH_PROFILING
+ -mno-mmx -mno-sse
cflags-$(CONFIG_ARM64) := $(subst -pg,,$(KBUILD_CFLAGS))
cflags-$(CONFIG_ARM) := $(subst -pg,,$(KBUILD_CFLAGS)) \
@@ -16,7 +16,7 @@ cflags-$(CONFIG_ARM) := $(subst -pg,,$(KBUILD_CFLAGS)) \
cflags-$(CONFIG_EFI_ARMSTUB) += -I$(srctree)/scripts/dtc/libfdt
-KBUILD_CFLAGS := $(cflags-y) \
+KBUILD_CFLAGS := $(cflags-y) -DDISABLE_BRANCH_PROFILING \
$(call cc-option,-ffreestanding) \
$(call cc-option,-fno-stack-protector)
@@ -34,6 +34,7 @@ $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE
lib-$(CONFIG_EFI_ARMSTUB) += arm-stub.o fdt.o string.o \
$(patsubst %.c,lib-%.o,$(arm-deps))
+lib-$(CONFIG_ARM) += arm32-stub.o
lib-$(CONFIG_ARM64) += arm64-stub.o
CFLAGS_arm64-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
@@ -67,3 +68,11 @@ quiet_cmd_stubcopy = STUBCPY $@
$(OBJDUMP) -r $@ | grep $(STUBCOPY_RELOC-y) \
&& (echo >&2 "$@: absolute symbol references not allowed in the EFI stub"; \
rm -f $@; /bin/false); else /bin/false; fi
+
+#
+# ARM discards the .data section because it disallows r/w data in the
+# decompressor. So move our .data to .data.efistub, which is preserved
+# explicitly by the decompressor linker script.
+#
+STUBCOPY_FLAGS-$(CONFIG_ARM) += --rename-section .data=.data.efistub
+STUBCOPY_RELOC-$(CONFIG_ARM) := R_ARM_ABS
diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c
index 950c87f5d279..3397902e4040 100644
--- a/drivers/firmware/efi/libstub/arm-stub.c
+++ b/drivers/firmware/efi/libstub/arm-stub.c
@@ -303,8 +303,10 @@ fail:
* The value chosen is the largest non-zero power of 2 suitable for this purpose
* both on 32-bit and 64-bit ARM CPUs, to maximize the likelihood that it can
* be mapped efficiently.
+ * Since 32-bit ARM could potentially execute with a 1G/3G user/kernel split,
+ * map everything below 1 GB.
*/
-#define EFI_RT_VIRTUAL_BASE 0x40000000
+#define EFI_RT_VIRTUAL_BASE SZ_512M
static int cmp_mem_desc(const void *l, const void *r)
{
diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c
new file mode 100644
index 000000000000..495ebd657e38
--- /dev/null
+++ b/drivers/firmware/efi/libstub/arm32-stub.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2013 Linaro Ltd; <roy.franz@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 <linux/efi.h>
+#include <asm/efi.h>
+
+efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
+ unsigned long *image_addr,
+ unsigned long *image_size,
+ unsigned long *reserve_addr,
+ unsigned long *reserve_size,
+ unsigned long dram_base,
+ efi_loaded_image_t *image)
+{
+ unsigned long nr_pages;
+ efi_status_t status;
+ /* Use alloc_addr to tranlsate between types */
+ efi_physical_addr_t alloc_addr;
+
+ /*
+ * Verify that the DRAM base address is compatible with the ARM
+ * boot protocol, which determines the base of DRAM by masking
+ * off the low 27 bits of the address at which the zImage is
+ * loaded. These assumptions are made by the decompressor,
+ * before any memory map is available.
+ */
+ dram_base = round_up(dram_base, SZ_128M);
+
+ /*
+ * Reserve memory for the uncompressed kernel image. This is
+ * all that prevents any future allocations from conflicting
+ * with the kernel. Since we can't tell from the compressed
+ * image how much DRAM the kernel actually uses (due to BSS
+ * size uncertainty) we allocate the maximum possible size.
+ * Do this very early, as prints can cause memory allocations
+ * that may conflict with this.
+ */
+ alloc_addr = dram_base;
+ *reserve_size = MAX_UNCOMP_KERNEL_SIZE;
+ nr_pages = round_up(*reserve_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
+ status = sys_table->boottime->allocate_pages(EFI_ALLOCATE_ADDRESS,
+ EFI_LOADER_DATA,
+ nr_pages, &alloc_addr);
+ if (status != EFI_SUCCESS) {
+ *reserve_size = 0;
+ pr_efi_err(sys_table, "Unable to allocate memory for uncompressed kernel.\n");
+ return status;
+ }
+ *reserve_addr = alloc_addr;
+
+ /*
+ * Relocate the zImage, so that it appears in the lowest 128 MB
+ * memory window.
+ */
+ *image_size = image->image_size;
+ status = efi_relocate_kernel(sys_table, image_addr, *image_size,
+ *image_size,
+ dram_base + MAX_UNCOMP_KERNEL_SIZE, 0);
+ if (status != EFI_SUCCESS) {
+ pr_efi_err(sys_table, "Failed to relocate kernel.\n");
+ efi_free(sys_table, *reserve_size, *reserve_addr);
+ *reserve_size = 0;
+ return status;
+ }
+
+ /*
+ * Check to see if we were able to allocate memory low enough
+ * in memory. The kernel determines the base of DRAM from the
+ * address at which the zImage is loaded.
+ */
+ if (*image_addr + *image_size > dram_base + ZIMAGE_OFFSET_LIMIT) {
+ pr_efi_err(sys_table, "Failed to relocate kernel, no low memory available.\n");
+ efi_free(sys_table, *reserve_size, *reserve_addr);
+ *reserve_size = 0;
+ efi_free(sys_table, *image_size, *image_addr);
+ *image_size = 0;
+ return EFI_LOAD_ERROR;
+ }
+ return EFI_SUCCESS;
+}
diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c
index b62e2f5dcab3..cf7b7d46302a 100644
--- a/drivers/firmware/efi/libstub/fdt.c
+++ b/drivers/firmware/efi/libstub/fdt.c
@@ -253,7 +253,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
sys_table->boottime->free_pool(memory_map);
new_fdt_size += EFI_PAGE_SIZE;
} else {
- pr_efi_err(sys_table, "Unable to constuct new device tree.\n");
+ pr_efi_err(sys_table, "Unable to construct new device tree.\n");
goto fail_free_mmap;
}
}
diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c
index d24f35d74b27..f25cd79c8a79 100644
--- a/drivers/firmware/psci.c
+++ b/drivers/firmware/psci.c
@@ -13,6 +13,7 @@
#define pr_fmt(fmt) "psci: " fmt
+#include <linux/arm-smccc.h>
#include <linux/errno.h>
#include <linux/linkage.h>
#include <linux/of.h>
@@ -58,8 +59,6 @@ struct psci_operations psci_ops;
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 {
@@ -107,6 +106,26 @@ bool psci_power_state_is_valid(u32 state)
return !(state & ~valid_mask);
}
+static unsigned long __invoke_psci_fn_hvc(unsigned long function_id,
+ unsigned long arg0, unsigned long arg1,
+ unsigned long arg2)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_hvc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res);
+ return res.a0;
+}
+
+static unsigned long __invoke_psci_fn_smc(unsigned long function_id,
+ unsigned long arg0, unsigned long arg1,
+ unsigned long arg2)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res);
+ return res.a0;
+}
+
static int psci_to_linux_errno(int errno)
{
switch (errno) {
diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
index a24f5cb877e0..953dc9195937 100644
--- a/drivers/fpga/fpga-mgr.c
+++ b/drivers/fpga/fpga-mgr.c
@@ -122,12 +122,10 @@ int fpga_mgr_firmware_load(struct fpga_manager *mgr, u32 flags,
}
ret = fpga_mgr_buf_load(mgr, flags, fw->data, fw->size);
- if (ret)
- return ret;
release_firmware(fw);
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(fpga_mgr_firmware_load);
@@ -256,7 +254,6 @@ int fpga_mgr_register(struct device *dev, const char *name,
void *priv)
{
struct fpga_manager *mgr;
- const char *dt_label;
int id, ret;
if (!mops || !mops->write_init || !mops->write ||
@@ -300,11 +297,9 @@ int fpga_mgr_register(struct device *dev, const char *name,
mgr->dev.id = id;
dev_set_drvdata(dev, mgr);
- dt_label = of_get_property(mgr->dev.of_node, "label", NULL);
- if (dt_label)
- ret = dev_set_name(&mgr->dev, "%s", dt_label);
- else
- ret = dev_set_name(&mgr->dev, "fpga%d", id);
+ ret = dev_set_name(&mgr->dev, "fpga%d", id);
+ if (ret)
+ goto error_device;
ret = device_add(&mgr->dev);
if (ret)
diff --git a/drivers/gpio/gpio-74xx-mmio.c b/drivers/gpio/gpio-74xx-mmio.c
index 6ed7c0fb3378..6b186829087c 100644
--- a/drivers/gpio/gpio-74xx-mmio.c
+++ b/drivers/gpio/gpio-74xx-mmio.c
@@ -113,13 +113,16 @@ static int mmio_74xx_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
static int mmio_74xx_gpio_probe(struct platform_device *pdev)
{
- const struct of_device_id *of_id =
- of_match_device(mmio_74xx_gpio_ids, &pdev->dev);
+ const struct of_device_id *of_id;
struct mmio_74xx_gpio_priv *priv;
struct resource *res;
void __iomem *dat;
int err;
+ of_id = of_match_device(mmio_74xx_gpio_ids, &pdev->dev);
+ if (!of_id)
+ return -ENODEV;
+
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c
index ca002739616a..624ea5421995 100644
--- a/drivers/gpio/gpio-arizona.c
+++ b/drivers/gpio/gpio-arizona.c
@@ -122,6 +122,10 @@ static int arizona_gpio_probe(struct platform_device *pdev)
case WM1814:
arizona_gpio->gpio_chip.ngpio = 5;
break;
+ case WM1831:
+ case CS47L24:
+ arizona_gpio->gpio_chip.ngpio = 2;
+ break;
default:
dev_err(&pdev->dev, "Unknown chip variant %d\n",
arizona->type);
diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c
index e5827a56ff3b..5eaea8b812cf 100644
--- a/drivers/gpio/gpio-ath79.c
+++ b/drivers/gpio/gpio-ath79.c
@@ -113,7 +113,7 @@ static int ar934x_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
__raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_CLEAR);
__raw_writel(
- __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) & BIT(offset),
+ __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) & ~BIT(offset),
ctrl->base + AR71XX_GPIO_REG_OE);
spin_unlock_irqrestore(&ctrl->lock, flags);
diff --git a/drivers/gpio/gpio-generic.c b/drivers/gpio/gpio-generic.c
index bd5193c67a9c..88ae70ddb127 100644
--- a/drivers/gpio/gpio-generic.c
+++ b/drivers/gpio/gpio-generic.c
@@ -141,9 +141,9 @@ static int bgpio_get_set(struct gpio_chip *gc, unsigned int gpio)
unsigned long pinmask = bgc->pin2mask(bgc, gpio);
if (bgc->dir & pinmask)
- return bgc->read_reg(bgc->reg_set) & pinmask;
+ return !!(bgc->read_reg(bgc->reg_set) & pinmask);
else
- return bgc->read_reg(bgc->reg_dat) & pinmask;
+ return !!(bgc->read_reg(bgc->reg_dat) & pinmask);
}
static int bgpio_get(struct gpio_chip *gc, unsigned int gpio)
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index 56d2d026e62e..f7fbb46d5d79 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -1122,8 +1122,6 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
/* MPUIO is a bit different, reading IRQ status clears it */
if (bank->is_mpuio) {
irqc->irq_ack = dummy_irq_chip.irq_ack;
- irqc->irq_mask = irq_gc_mask_set_bit;
- irqc->irq_unmask = irq_gc_mask_clr_bit;
if (!bank->regs->wkup_en)
irqc->irq_set_wake = NULL;
}
diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c
index 171a6389f9ce..52b447c071cb 100644
--- a/drivers/gpio/gpio-palmas.c
+++ b/drivers/gpio/gpio-palmas.c
@@ -167,6 +167,8 @@ static int palmas_gpio_probe(struct platform_device *pdev)
const struct palmas_device_data *dev_data;
match = of_match_device(of_palmas_gpio_match, &pdev->dev);
+ if (!match)
+ return -ENODEV;
dev_data = match->data;
if (!dev_data)
dev_data = &palmas_dev_data;
diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c
index 045a952576c7..7b25fdf64802 100644
--- a/drivers/gpio/gpio-syscon.c
+++ b/drivers/gpio/gpio-syscon.c
@@ -187,11 +187,15 @@ MODULE_DEVICE_TABLE(of, syscon_gpio_ids);
static int syscon_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- const struct of_device_id *of_id = of_match_device(syscon_gpio_ids, dev);
+ const struct of_device_id *of_id;
struct syscon_gpio_priv *priv;
struct device_node *np = dev->of_node;
int ret;
+ of_id = of_match_device(syscon_gpio_ids, dev);
+ if (!of_id)
+ return -ENODEV;
+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c
index 027e5f47dd28..896bf29776b0 100644
--- a/drivers/gpio/gpio-tegra.c
+++ b/drivers/gpio/gpio-tegra.c
@@ -375,6 +375,60 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable)
}
#endif
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+static int dbg_gpio_show(struct seq_file *s, void *unused)
+{
+ int i;
+ int j;
+
+ for (i = 0; i < tegra_gpio_bank_count; i++) {
+ for (j = 0; j < 4; j++) {
+ int gpio = tegra_gpio_compose(i, j, 0);
+ seq_printf(s,
+ "%d:%d %02x %02x %02x %02x %02x %02x %06x\n",
+ i, j,
+ tegra_gpio_readl(GPIO_CNF(gpio)),
+ tegra_gpio_readl(GPIO_OE(gpio)),
+ tegra_gpio_readl(GPIO_OUT(gpio)),
+ tegra_gpio_readl(GPIO_IN(gpio)),
+ tegra_gpio_readl(GPIO_INT_STA(gpio)),
+ tegra_gpio_readl(GPIO_INT_ENB(gpio)),
+ tegra_gpio_readl(GPIO_INT_LVL(gpio)));
+ }
+ }
+ return 0;
+}
+
+static int dbg_gpio_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dbg_gpio_show, &inode->i_private);
+}
+
+static const struct file_operations debug_fops = {
+ .open = dbg_gpio_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void tegra_gpio_debuginit(void)
+{
+ (void) debugfs_create_file("tegra_gpio", S_IRUGO,
+ NULL, NULL, &debug_fops);
+}
+
+#else
+
+static inline void tegra_gpio_debuginit(void)
+{
+}
+
+#endif
+
static struct irq_chip tegra_gpio_irq_chip = {
.name = "GPIO",
.irq_ack = tegra_gpio_irq_ack,
@@ -519,6 +573,8 @@ static int tegra_gpio_probe(struct platform_device *pdev)
spin_lock_init(&bank->lvl_lock[j]);
}
+ tegra_gpio_debuginit();
+
return 0;
}
@@ -536,52 +592,3 @@ static int __init tegra_gpio_init(void)
return platform_driver_register(&tegra_gpio_driver);
}
postcore_initcall(tegra_gpio_init);
-
-#ifdef CONFIG_DEBUG_FS
-
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-
-static int dbg_gpio_show(struct seq_file *s, void *unused)
-{
- int i;
- int j;
-
- for (i = 0; i < tegra_gpio_bank_count; i++) {
- for (j = 0; j < 4; j++) {
- int gpio = tegra_gpio_compose(i, j, 0);
- seq_printf(s,
- "%d:%d %02x %02x %02x %02x %02x %02x %06x\n",
- i, j,
- tegra_gpio_readl(GPIO_CNF(gpio)),
- tegra_gpio_readl(GPIO_OE(gpio)),
- tegra_gpio_readl(GPIO_OUT(gpio)),
- tegra_gpio_readl(GPIO_IN(gpio)),
- tegra_gpio_readl(GPIO_INT_STA(gpio)),
- tegra_gpio_readl(GPIO_INT_ENB(gpio)),
- tegra_gpio_readl(GPIO_INT_LVL(gpio)));
- }
- }
- return 0;
-}
-
-static int dbg_gpio_open(struct inode *inode, struct file *file)
-{
- return single_open(file, dbg_gpio_show, &inode->i_private);
-}
-
-static const struct file_operations debug_fops = {
- .open = dbg_gpio_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int __init tegra_gpio_debuginit(void)
-{
- (void) debugfs_create_file("tegra_gpio", S_IRUGO,
- NULL, NULL, &debug_fops);
- return 0;
-}
-late_initcall(tegra_gpio_debuginit);
-#endif
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index 16a7b6816744..bc34bc51a948 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -417,10 +417,15 @@ static int acpi_find_gpio(struct acpi_resource *ares, void *data)
* ActiveLow is only specified for GpioInt resource. If
* GpioIo is used then the only way to set the flag is
* to use _DSD "gpios" property.
+ * Note: we expect here:
+ * - ACPI_ACTIVE_LOW == GPIO_ACTIVE_LOW
+ * - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH
*/
- if (lookup->info.gpioint)
- lookup->info.active_low =
- agpio->polarity == ACPI_ACTIVE_LOW;
+ if (lookup->info.gpioint) {
+ lookup->info.polarity = agpio->polarity;
+ lookup->info.triggering = agpio->triggering;
+ }
+
}
return 1;
@@ -447,7 +452,7 @@ static int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup,
if (info) {
*info = lookup->info;
if (lookup->active_low)
- info->active_low = lookup->active_low;
+ info->polarity = lookup->active_low;
}
return 0;
}
@@ -595,6 +600,7 @@ struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
{
int idx, i;
+ unsigned int irq_flags;
for (i = 0, idx = 0; idx <= index; i++) {
struct acpi_gpio_info info;
@@ -603,8 +609,23 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
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);
+ if (info.gpioint && idx++ == index) {
+ int irq = gpiod_to_irq(desc);
+
+ if (irq < 0)
+ return irq;
+
+ irq_flags = acpi_dev_get_irq_type(info.triggering,
+ info.polarity);
+
+ /* Set type if specified and different than the current one */
+ if (irq_flags != IRQ_TYPE_NONE &&
+ irq_flags != irq_get_trigger_type(irq))
+ irq_set_irq_type(irq, irq_flags);
+
+ return irq;
+ }
+
}
return -ENOENT;
}
@@ -922,3 +943,46 @@ int acpi_gpio_count(struct device *dev, const char *con_id)
}
return count;
}
+
+struct acpi_crs_lookup {
+ struct list_head node;
+ struct acpi_device *adev;
+ const char *con_id;
+};
+
+static DEFINE_MUTEX(acpi_crs_lookup_lock);
+static LIST_HEAD(acpi_crs_lookup_list);
+
+bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id)
+{
+ struct acpi_crs_lookup *l, *lookup = NULL;
+
+ /* Never allow fallback if the device has properties */
+ if (adev->data.properties || adev->driver_gpios)
+ return false;
+
+ mutex_lock(&acpi_crs_lookup_lock);
+
+ list_for_each_entry(l, &acpi_crs_lookup_list, node) {
+ if (l->adev == adev) {
+ lookup = l;
+ break;
+ }
+ }
+
+ if (!lookup) {
+ lookup = kmalloc(sizeof(*lookup), GFP_KERNEL);
+ if (lookup) {
+ lookup->adev = adev;
+ lookup->con_id = con_id;
+ list_add_tail(&lookup->node, &acpi_crs_lookup_list);
+ }
+ }
+
+ mutex_unlock(&acpi_crs_lookup_lock);
+
+ return lookup &&
+ ((!lookup->con_id && !con_id) ||
+ (lookup->con_id && con_id &&
+ strcmp(lookup->con_id, con_id) == 0));
+}
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index a18f00fc1bb8..3346abd29b52 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -233,7 +233,7 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name)
for (i = 0; i != chip->ngpio; ++i) {
struct gpio_desc *gpio = &chip->desc[i];
- if (!gpio->name)
+ if (!gpio->name || !name)
continue;
if (!strcmp(gpio->name, name)) {
@@ -1279,7 +1279,13 @@ static int _gpiod_get_raw_value(const struct gpio_desc *desc)
chip = desc->chip;
offset = gpio_chip_hwgpio(desc);
value = chip->get ? chip->get(chip, offset) : -EIO;
- value = value < 0 ? value : !!value;
+ /*
+ * FIXME: fix all drivers to clamp to [0,1] or return negative,
+ * then change this to:
+ * value = value < 0 ? value : !!value;
+ * so we can properly propagate error codes.
+ */
+ value = !!value;
trace_gpio_value(desc_to_gpio(desc), 1, value);
return value;
}
@@ -1868,12 +1874,15 @@ static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id,
/* Then from plain _CRS GPIOs */
if (IS_ERR(desc)) {
+ if (!acpi_can_fallback_to_crs(adev, con_id))
+ return ERR_PTR(-ENOENT);
+
desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info);
if (IS_ERR(desc))
return desc;
}
- if (info.active_low)
+ if (info.polarity == GPIO_ACTIVE_LOW)
*flags |= GPIO_ACTIVE_LOW;
return desc;
@@ -2211,7 +2220,7 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
desc = acpi_node_get_gpiod(fwnode, propname, 0, &info);
if (!IS_ERR(desc))
- active_low = info.active_low;
+ active_low = info.polarity == GPIO_ACTIVE_LOW;
}
if (IS_ERR(desc))
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 98ab08c0aa2d..be3a977d0349 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -26,7 +26,8 @@ struct acpi_device;
*/
struct acpi_gpio_info {
bool gpioint;
- bool active_low;
+ int polarity;
+ int triggering;
};
/* gpio suffixes used for ACPI and device tree lookup */
@@ -47,6 +48,8 @@ struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
struct acpi_gpio_info *info);
int acpi_gpio_count(struct device *dev, const char *con_id);
+
+bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id);
#else
static inline void acpi_gpiochip_add(struct gpio_chip *chip) { }
static inline void acpi_gpiochip_remove(struct gpio_chip *chip) { }
@@ -73,6 +76,12 @@ static inline int acpi_gpio_count(struct device *dev, const char *con_id)
{
return -ENODEV;
}
+
+static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev,
+ const char *con_id)
+{
+ return false;
+}
#endif
struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index 306f75700bf8..048cfe073dae 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -496,6 +496,7 @@ struct amdgpu_bo_va_mapping {
/* bo virtual addresses in a specific vm */
struct amdgpu_bo_va {
+ struct mutex mutex;
/* protected by bo being reserved */
struct list_head bo_list;
struct fence *last_pt_update;
@@ -538,6 +539,7 @@ struct amdgpu_bo {
/* Constant after initialization */
struct amdgpu_device *adev;
struct drm_gem_object gem_base;
+ struct amdgpu_bo *parent;
struct ttm_bo_kmap_obj dma_buf_vmap;
pid_t pid;
@@ -928,8 +930,6 @@ struct amdgpu_vm_id {
};
struct amdgpu_vm {
- struct mutex mutex;
-
struct rb_root va;
/* protecting invalidated */
@@ -956,6 +956,8 @@ struct amdgpu_vm {
struct amdgpu_vm_id ids[AMDGPU_MAX_RINGS];
/* for interval tree */
spinlock_t it_lock;
+ /* protecting freed */
+ spinlock_t freed_lock;
};
struct amdgpu_vm_manager {
@@ -1262,7 +1264,8 @@ struct amdgpu_cs_parser {
struct ww_acquire_ctx ticket;
/* user fence */
- struct amdgpu_user_fence uf;
+ struct amdgpu_user_fence uf;
+ struct amdgpu_bo_list_entry uf_entry;
};
struct amdgpu_job {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index 3afcf0237c25..25a3e2485cc2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -127,6 +127,37 @@ int amdgpu_cs_get_ring(struct amdgpu_device *adev, u32 ip_type,
return 0;
}
+static int amdgpu_cs_user_fence_chunk(struct amdgpu_cs_parser *p,
+ struct drm_amdgpu_cs_chunk_fence *fence_data)
+{
+ struct drm_gem_object *gobj;
+ uint32_t handle;
+
+ handle = fence_data->handle;
+ gobj = drm_gem_object_lookup(p->adev->ddev, p->filp,
+ fence_data->handle);
+ if (gobj == NULL)
+ return -EINVAL;
+
+ p->uf.bo = amdgpu_bo_ref(gem_to_amdgpu_bo(gobj));
+ p->uf.offset = fence_data->offset;
+
+ if (amdgpu_ttm_tt_has_userptr(p->uf.bo->tbo.ttm)) {
+ drm_gem_object_unreference_unlocked(gobj);
+ return -EINVAL;
+ }
+
+ p->uf_entry.robj = amdgpu_bo_ref(p->uf.bo);
+ p->uf_entry.prefered_domains = AMDGPU_GEM_DOMAIN_GTT;
+ p->uf_entry.allowed_domains = AMDGPU_GEM_DOMAIN_GTT;
+ p->uf_entry.priority = 0;
+ p->uf_entry.tv.bo = &p->uf_entry.robj->tbo;
+ p->uf_entry.tv.shared = true;
+
+ drm_gem_object_unreference_unlocked(gobj);
+ return 0;
+}
+
int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data)
{
union drm_amdgpu_cs *cs = data;
@@ -207,26 +238,15 @@ int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data)
case AMDGPU_CHUNK_ID_FENCE:
size = sizeof(struct drm_amdgpu_cs_chunk_fence);
- if (p->chunks[i].length_dw * sizeof(uint32_t) >= size) {
- uint32_t handle;
- struct drm_gem_object *gobj;
- struct drm_amdgpu_cs_chunk_fence *fence_data;
-
- fence_data = (void *)p->chunks[i].kdata;
- handle = fence_data->handle;
- gobj = drm_gem_object_lookup(p->adev->ddev,
- p->filp, handle);
- if (gobj == NULL) {
- ret = -EINVAL;
- goto free_partial_kdata;
- }
-
- p->uf.bo = gem_to_amdgpu_bo(gobj);
- p->uf.offset = fence_data->offset;
- } else {
+ if (p->chunks[i].length_dw * sizeof(uint32_t) < size) {
ret = -EINVAL;
goto free_partial_kdata;
}
+
+ ret = amdgpu_cs_user_fence_chunk(p, (void *)p->chunks[i].kdata);
+ if (ret)
+ goto free_partial_kdata;
+
break;
case AMDGPU_CHUNK_ID_DEPENDENCIES:
@@ -389,6 +409,9 @@ static int amdgpu_cs_parser_relocs(struct amdgpu_cs_parser *p)
p->vm_bos = amdgpu_vm_get_bos(p->adev, &fpriv->vm,
&p->validated);
+ if (p->uf.bo)
+ list_add(&p->uf_entry.tv.head, &p->validated);
+
if (need_mmap_lock)
down_read(&current->mm->mmap_sem);
@@ -486,8 +509,8 @@ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser, int error, bo
for (i = 0; i < parser->num_ibs; i++)
amdgpu_ib_free(parser->adev, &parser->ibs[i]);
kfree(parser->ibs);
- if (parser->uf.bo)
- drm_gem_object_unreference_unlocked(&parser->uf.bo->gem_base);
+ amdgpu_bo_unref(&parser->uf.bo);
+ amdgpu_bo_unref(&parser->uf_entry.robj);
}
static int amdgpu_bo_vm_update_pte(struct amdgpu_cs_parser *p,
@@ -776,7 +799,7 @@ static int amdgpu_cs_free_job(struct amdgpu_job *job)
amdgpu_ib_free(job->adev, &job->ibs[i]);
kfree(job->ibs);
if (job->uf.bo)
- drm_gem_object_unreference_unlocked(&job->uf.bo->gem_base);
+ amdgpu_bo_unref(&job->uf.bo);
return 0;
}
@@ -784,8 +807,6 @@ int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
{
struct amdgpu_device *adev = dev->dev_private;
union drm_amdgpu_cs *cs = data;
- struct amdgpu_fpriv *fpriv = filp->driver_priv;
- struct amdgpu_vm *vm = &fpriv->vm;
struct amdgpu_cs_parser parser = {};
bool reserved_buffers = false;
int i, r;
@@ -803,7 +824,6 @@ int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
r = amdgpu_cs_handle_lockup(adev, r);
return r;
}
- mutex_lock(&vm->mutex);
r = amdgpu_cs_parser_relocs(&parser);
if (r == -ENOMEM)
DRM_ERROR("Not enough memory for command submission!\n");
@@ -888,7 +908,6 @@ int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
out:
amdgpu_cs_parser_fini(&parser, r, reserved_buffers);
- mutex_unlock(&vm->mutex);
r = amdgpu_cs_handle_lockup(adev, r);
return r;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
index e173a5a02f0d..5580d3420c3a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
@@ -73,6 +73,8 @@ static void amdgpu_flip_work_func(struct work_struct *__work)
struct drm_crtc *crtc = &amdgpuCrtc->base;
unsigned long flags;
unsigned i;
+ int vpos, hpos, stat, min_udelay;
+ struct drm_vblank_crtc *vblank = &crtc->dev->vblank[work->crtc_id];
amdgpu_flip_wait_fence(adev, &work->excl);
for (i = 0; i < work->shared_count; ++i)
@@ -81,6 +83,41 @@ static void amdgpu_flip_work_func(struct work_struct *__work)
/* We borrow the event spin lock for protecting flip_status */
spin_lock_irqsave(&crtc->dev->event_lock, flags);
+ /* If this happens to execute within the "virtually extended" vblank
+ * interval before the start of the real vblank interval then it needs
+ * to delay programming the mmio flip until the real vblank is entered.
+ * This prevents completing a flip too early due to the way we fudge
+ * our vblank counter and vblank timestamps in order to work around the
+ * problem that the hw fires vblank interrupts before actual start of
+ * vblank (when line buffer refilling is done for a frame). It
+ * complements the fudging logic in amdgpu_get_crtc_scanoutpos() for
+ * timestamping and amdgpu_get_vblank_counter_kms() for vblank counts.
+ *
+ * In practice this won't execute very often unless on very fast
+ * machines because the time window for this to happen is very small.
+ */
+ for (;;) {
+ /* GET_DISTANCE_TO_VBLANKSTART returns distance to real vblank
+ * start in hpos, and to the "fudged earlier" vblank start in
+ * vpos.
+ */
+ stat = amdgpu_get_crtc_scanoutpos(adev->ddev, work->crtc_id,
+ GET_DISTANCE_TO_VBLANKSTART,
+ &vpos, &hpos, NULL, NULL,
+ &crtc->hwmode);
+
+ if ((stat & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE)) !=
+ (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE) ||
+ !(vpos >= 0 && hpos <= 0))
+ break;
+
+ /* Sleep at least until estimated real start of hw vblank */
+ spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+ min_udelay = (-hpos + 1) * max(vblank->linedur_ns / 1000, 5);
+ usleep_range(min_udelay, 2 * min_udelay);
+ spin_lock_irqsave(&crtc->dev->event_lock, flags);
+ };
+
/* do the flip (mmio) */
adev->mode_info.funcs->page_flip(adev, work->crtc_id, work->base);
/* set the flip status */
@@ -109,7 +146,7 @@ static void amdgpu_unpin_work_func(struct work_struct *__work)
} else
DRM_ERROR("failed to reserve buffer after flip\n");
- drm_gem_object_unreference_unlocked(&work->old_rbo->gem_base);
+ amdgpu_bo_unref(&work->old_rbo);
kfree(work->shared);
kfree(work);
}
@@ -148,8 +185,8 @@ int amdgpu_crtc_page_flip(struct drm_crtc *crtc,
obj = old_amdgpu_fb->obj;
/* take a reference to the old object */
- drm_gem_object_reference(obj);
work->old_rbo = gem_to_amdgpu_bo(obj);
+ amdgpu_bo_ref(work->old_rbo);
new_amdgpu_fb = to_amdgpu_framebuffer(fb);
obj = new_amdgpu_fb->obj;
@@ -222,7 +259,7 @@ pflip_cleanup:
amdgpu_bo_unreserve(new_rbo);
cleanup:
- drm_gem_object_unreference_unlocked(&work->old_rbo->gem_base);
+ amdgpu_bo_unref(&work->old_rbo);
fence_put(work->excl);
for (i = 0; i < work->shared_count; ++i)
fence_put(work->shared[i]);
@@ -712,6 +749,15 @@ bool amdgpu_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
* \param dev Device to query.
* \param pipe Crtc to query.
* \param flags Flags from caller (DRM_CALLED_FROM_VBLIRQ or 0).
+ * For driver internal use only also supports these flags:
+ *
+ * USE_REAL_VBLANKSTART to use the real start of vblank instead
+ * of a fudged earlier start of vblank.
+ *
+ * GET_DISTANCE_TO_VBLANKSTART to return distance to the
+ * fudged earlier start of vblank in *vpos and the distance
+ * to true start of vblank in *hpos.
+ *
* \param *vpos Location where vertical scanout position should be stored.
* \param *hpos Location where horizontal scanout position should go.
* \param *stime Target location for timestamp taken immediately before
@@ -776,10 +822,40 @@ int amdgpu_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
vbl_end = 0;
}
+ /* Called from driver internal vblank counter query code? */
+ if (flags & GET_DISTANCE_TO_VBLANKSTART) {
+ /* Caller wants distance from real vbl_start in *hpos */
+ *hpos = *vpos - vbl_start;
+ }
+
+ /* Fudge vblank to start a few scanlines earlier to handle the
+ * problem that vblank irqs fire a few scanlines before start
+ * of vblank. Some driver internal callers need the true vblank
+ * start to be used and signal this via the USE_REAL_VBLANKSTART flag.
+ *
+ * The cause of the "early" vblank irq is that the irq is triggered
+ * by the line buffer logic when the line buffer read position enters
+ * the vblank, whereas our crtc scanout position naturally lags the
+ * line buffer read position.
+ */
+ if (!(flags & USE_REAL_VBLANKSTART))
+ vbl_start -= adev->mode_info.crtcs[pipe]->lb_vblank_lead_lines;
+
/* Test scanout position against vblank region. */
if ((*vpos < vbl_start) && (*vpos >= vbl_end))
in_vbl = false;
+ /* In vblank? */
+ if (in_vbl)
+ ret |= DRM_SCANOUTPOS_IN_VBLANK;
+
+ /* Called from driver internal vblank counter query code? */
+ if (flags & GET_DISTANCE_TO_VBLANKSTART) {
+ /* Caller wants distance from fudged earlier vbl_start */
+ *vpos -= vbl_start;
+ return ret;
+ }
+
/* Check if inside vblank area and apply corrective offsets:
* vpos will then be >=0 in video scanout area, but negative
* within vblank area, counting down the number of lines until
@@ -795,32 +871,6 @@ int amdgpu_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
/* Correct for shifted end of vbl at vbl_end. */
*vpos = *vpos - vbl_end;
- /* In vblank? */
- if (in_vbl)
- ret |= DRM_SCANOUTPOS_IN_VBLANK;
-
- /* Is vpos outside nominal vblank area, but less than
- * 1/100 of a frame height away from start of vblank?
- * If so, assume this isn't a massively delayed vblank
- * interrupt, but a vblank interrupt that fired a few
- * microseconds before true start of vblank. Compensate
- * by adding a full frame duration to the final timestamp.
- * Happens, e.g., on ATI R500, R600.
- *
- * We only do this if DRM_CALLED_FROM_VBLIRQ.
- */
- if ((flags & DRM_CALLED_FROM_VBLIRQ) && !in_vbl) {
- vbl_start = mode->crtc_vdisplay;
- vtotal = mode->crtc_vtotal;
-
- if (vbl_start - *vpos < vtotal / 100) {
- *vpos -= vtotal;
-
- /* Signal this correction as "applied". */
- ret |= 0x8;
- }
- }
-
return ret;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
index 00c5b580f56c..9c253c535d26 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
@@ -115,12 +115,9 @@ int amdgpu_gem_object_open(struct drm_gem_object *obj, struct drm_file *file_pri
struct amdgpu_vm *vm = &fpriv->vm;
struct amdgpu_bo_va *bo_va;
int r;
- mutex_lock(&vm->mutex);
r = amdgpu_bo_reserve(rbo, false);
- if (r) {
- mutex_unlock(&vm->mutex);
+ if (r)
return r;
- }
bo_va = amdgpu_vm_bo_find(vm, rbo);
if (!bo_va) {
@@ -129,7 +126,6 @@ int amdgpu_gem_object_open(struct drm_gem_object *obj, struct drm_file *file_pri
++bo_va->ref_count;
}
amdgpu_bo_unreserve(rbo);
- mutex_unlock(&vm->mutex);
return 0;
}
@@ -142,10 +138,8 @@ void amdgpu_gem_object_close(struct drm_gem_object *obj,
struct amdgpu_vm *vm = &fpriv->vm;
struct amdgpu_bo_va *bo_va;
int r;
- mutex_lock(&vm->mutex);
r = amdgpu_bo_reserve(rbo, true);
if (r) {
- mutex_unlock(&vm->mutex);
dev_err(adev->dev, "leaking bo va because "
"we fail to reserve bo (%d)\n", r);
return;
@@ -157,7 +151,6 @@ void amdgpu_gem_object_close(struct drm_gem_object *obj,
}
}
amdgpu_bo_unreserve(rbo);
- mutex_unlock(&vm->mutex);
}
static int amdgpu_gem_handle_lockup(struct amdgpu_device *adev, int r)
@@ -242,8 +235,9 @@ int amdgpu_gem_userptr_ioctl(struct drm_device *dev, void *data,
AMDGPU_GEM_USERPTR_REGISTER))
return -EINVAL;
- if (!(args->flags & AMDGPU_GEM_USERPTR_ANONONLY) ||
- !(args->flags & AMDGPU_GEM_USERPTR_REGISTER)) {
+ if (!(args->flags & AMDGPU_GEM_USERPTR_READONLY) && (
+ !(args->flags & AMDGPU_GEM_USERPTR_ANONONLY) ||
+ !(args->flags & AMDGPU_GEM_USERPTR_REGISTER))) {
/* if we want to write to it we must require anonymous
memory and install a MMU notifier */
@@ -483,6 +477,14 @@ static void amdgpu_gem_va_update_vm(struct amdgpu_device *adev,
if (domain == AMDGPU_GEM_DOMAIN_CPU)
goto error_unreserve;
}
+ list_for_each_entry(entry, &duplicates, head) {
+ domain = amdgpu_mem_type_to_domain(entry->bo->mem.mem_type);
+ /* if anything is swapped out don't swap it in here,
+ just abort and wait for the next CS */
+ if (domain == AMDGPU_GEM_DOMAIN_CPU)
+ goto error_unreserve;
+ }
+
r = amdgpu_vm_update_page_directory(adev, bo_va->vm);
if (r)
goto error_unreserve;
@@ -553,7 +555,6 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data,
gobj = drm_gem_object_lookup(dev, filp, args->handle);
if (gobj == NULL)
return -ENOENT;
- mutex_lock(&fpriv->vm.mutex);
rbo = gem_to_amdgpu_bo(gobj);
INIT_LIST_HEAD(&list);
INIT_LIST_HEAD(&duplicates);
@@ -568,7 +569,6 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data,
}
r = ttm_eu_reserve_buffers(&ticket, &list, true, &duplicates);
if (r) {
- mutex_unlock(&fpriv->vm.mutex);
drm_gem_object_unreference_unlocked(gobj);
return r;
}
@@ -577,7 +577,6 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data,
if (!bo_va) {
ttm_eu_backoff_reservation(&ticket, &list);
drm_gem_object_unreference_unlocked(gobj);
- mutex_unlock(&fpriv->vm.mutex);
return -ENOENT;
}
@@ -602,7 +601,7 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data,
ttm_eu_backoff_reservation(&ticket, &list);
if (!r && !(args->flags & AMDGPU_VM_DELAY_UPDATE))
amdgpu_gem_va_update_vm(adev, bo_va, args->operation);
- mutex_unlock(&fpriv->vm.mutex);
+
drm_gem_object_unreference_unlocked(gobj);
return r;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
index 1618e2294a16..e23843f4d877 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
@@ -611,13 +611,59 @@ void amdgpu_driver_preclose_kms(struct drm_device *dev,
u32 amdgpu_get_vblank_counter_kms(struct drm_device *dev, unsigned int pipe)
{
struct amdgpu_device *adev = dev->dev_private;
+ int vpos, hpos, stat;
+ u32 count;
if (pipe >= adev->mode_info.num_crtc) {
DRM_ERROR("Invalid crtc %u\n", pipe);
return -EINVAL;
}
- return amdgpu_display_vblank_get_counter(adev, pipe);
+ /* The hw increments its frame counter at start of vsync, not at start
+ * of vblank, as is required by DRM core vblank counter handling.
+ * Cook the hw count here to make it appear to the caller as if it
+ * incremented at start of vblank. We measure distance to start of
+ * vblank in vpos. vpos therefore will be >= 0 between start of vblank
+ * and start of vsync, so vpos >= 0 means to bump the hw frame counter
+ * result by 1 to give the proper appearance to caller.
+ */
+ if (adev->mode_info.crtcs[pipe]) {
+ /* Repeat readout if needed to provide stable result if
+ * we cross start of vsync during the queries.
+ */
+ do {
+ count = amdgpu_display_vblank_get_counter(adev, pipe);
+ /* Ask amdgpu_get_crtc_scanoutpos to return vpos as
+ * distance to start of vblank, instead of regular
+ * vertical scanout pos.
+ */
+ stat = amdgpu_get_crtc_scanoutpos(
+ dev, pipe, GET_DISTANCE_TO_VBLANKSTART,
+ &vpos, &hpos, NULL, NULL,
+ &adev->mode_info.crtcs[pipe]->base.hwmode);
+ } while (count != amdgpu_display_vblank_get_counter(adev, pipe));
+
+ if (((stat & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE)) !=
+ (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE))) {
+ DRM_DEBUG_VBL("Query failed! stat %d\n", stat);
+ } else {
+ DRM_DEBUG_VBL("crtc %d: dist from vblank start %d\n",
+ pipe, vpos);
+
+ /* Bump counter if we are at >= leading edge of vblank,
+ * but before vsync where vpos would turn negative and
+ * the hw counter really increments.
+ */
+ if (vpos >= 0)
+ count++;
+ }
+ } else {
+ /* Fallback to use value as is. */
+ count = amdgpu_display_vblank_get_counter(adev, pipe);
+ DRM_DEBUG_VBL("NULL mode info! Returned count may be wrong.\n");
+ }
+
+ return count;
}
/**
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
index b62c1710cab6..064ebb347074 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
@@ -407,6 +407,7 @@ struct amdgpu_crtc {
u32 line_time;
u32 wm_low;
u32 wm_high;
+ u32 lb_vblank_lead_lines;
struct drm_display_mode hw_mode;
};
@@ -528,6 +529,10 @@ struct amdgpu_framebuffer {
#define ENCODER_MODE_IS_DP(em) (((em) == ATOM_ENCODER_MODE_DP) || \
((em) == ATOM_ENCODER_MODE_DP_MST))
+/* Driver internal use only flags of amdgpu_get_crtc_scanoutpos() */
+#define USE_REAL_VBLANKSTART (1 << 30)
+#define GET_DISTANCE_TO_VBLANKSTART (1 << 31)
+
void amdgpu_link_encoder_connector(struct drm_device *dev);
struct drm_connector *
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
index 0d524384ff79..c3ce103b6a33 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
@@ -100,6 +100,7 @@ static void amdgpu_ttm_bo_destroy(struct ttm_buffer_object *tbo)
list_del_init(&bo->list);
mutex_unlock(&bo->adev->gem.mutex);
drm_gem_object_release(&bo->gem_base);
+ amdgpu_bo_unref(&bo->parent);
kfree(bo->metadata);
kfree(bo);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index d4bac5f49939..8a1752ff3d8e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -587,9 +587,13 @@ static int amdgpu_ttm_backend_bind(struct ttm_tt *ttm,
uint32_t flags = amdgpu_ttm_tt_pte_flags(gtt->adev, ttm, bo_mem);
int r;
- if (gtt->userptr)
- amdgpu_ttm_tt_pin_userptr(ttm);
-
+ if (gtt->userptr) {
+ r = amdgpu_ttm_tt_pin_userptr(ttm);
+ if (r) {
+ DRM_ERROR("failed to pin userptr\n");
+ return r;
+ }
+ }
gtt->offset = (unsigned long)(bo_mem->start << PAGE_SHIFT);
if (!ttm->num_pages) {
WARN(1, "nothing to bind %lu pages for mreg %p back %p!\n",
@@ -797,11 +801,12 @@ uint32_t amdgpu_ttm_tt_pte_flags(struct amdgpu_device *adev, struct ttm_tt *ttm,
if (mem && mem->mem_type != TTM_PL_SYSTEM)
flags |= AMDGPU_PTE_VALID;
- if (mem && mem->mem_type == TTM_PL_TT)
+ if (mem && mem->mem_type == TTM_PL_TT) {
flags |= AMDGPU_PTE_SYSTEM;
- if (!ttm || ttm->caching_state == tt_cached)
- flags |= AMDGPU_PTE_SNOOPED;
+ if (ttm->caching_state == tt_cached)
+ flags |= AMDGPU_PTE_SNOOPED;
+ }
if (adev->asic_type >= CHIP_TOPAZ)
flags |= AMDGPU_PTE_EXECUTABLE;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
index 03f0c3bae516..a745eeeb5d82 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
@@ -392,7 +392,10 @@ int amdgpu_vce_get_create_msg(struct amdgpu_ring *ring, uint32_t handle,
ib->ptr[ib->length_dw++] = 0x00000001; /* session cmd */
ib->ptr[ib->length_dw++] = handle;
- ib->ptr[ib->length_dw++] = 0x00000030; /* len */
+ if ((ring->adev->vce.fw_version >> 24) >= 52)
+ ib->ptr[ib->length_dw++] = 0x00000040; /* len */
+ else
+ ib->ptr[ib->length_dw++] = 0x00000030; /* len */
ib->ptr[ib->length_dw++] = 0x01000001; /* create cmd */
ib->ptr[ib->length_dw++] = 0x00000000;
ib->ptr[ib->length_dw++] = 0x00000042;
@@ -404,6 +407,12 @@ int amdgpu_vce_get_create_msg(struct amdgpu_ring *ring, uint32_t handle,
ib->ptr[ib->length_dw++] = 0x00000100;
ib->ptr[ib->length_dw++] = 0x0000000c;
ib->ptr[ib->length_dw++] = 0x00000000;
+ if ((ring->adev->vce.fw_version >> 24) >= 52) {
+ ib->ptr[ib->length_dw++] = 0x00000000;
+ ib->ptr[ib->length_dw++] = 0x00000000;
+ ib->ptr[ib->length_dw++] = 0x00000000;
+ ib->ptr[ib->length_dw++] = 0x00000000;
+ }
ib->ptr[ib->length_dw++] = 0x00000014; /* len */
ib->ptr[ib->length_dw++] = 0x05000005; /* feedback buffer */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
index 159ce54bbd8d..b53d273eb7a1 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
@@ -885,17 +885,21 @@ int amdgpu_vm_clear_freed(struct amdgpu_device *adev,
struct amdgpu_bo_va_mapping *mapping;
int r;
+ spin_lock(&vm->freed_lock);
while (!list_empty(&vm->freed)) {
mapping = list_first_entry(&vm->freed,
struct amdgpu_bo_va_mapping, list);
list_del(&mapping->list);
-
+ spin_unlock(&vm->freed_lock);
r = amdgpu_vm_bo_update_mapping(adev, vm, mapping, 0, 0, NULL);
kfree(mapping);
if (r)
return r;
+ spin_lock(&vm->freed_lock);
}
+ spin_unlock(&vm->freed_lock);
+
return 0;
}
@@ -922,8 +926,9 @@ int amdgpu_vm_clear_invalids(struct amdgpu_device *adev,
bo_va = list_first_entry(&vm->invalidated,
struct amdgpu_bo_va, vm_status);
spin_unlock(&vm->status_lock);
-
+ mutex_lock(&bo_va->mutex);
r = amdgpu_vm_bo_update(adev, bo_va, NULL);
+ mutex_unlock(&bo_va->mutex);
if (r)
return r;
@@ -967,7 +972,7 @@ struct amdgpu_bo_va *amdgpu_vm_bo_add(struct amdgpu_device *adev,
INIT_LIST_HEAD(&bo_va->valids);
INIT_LIST_HEAD(&bo_va->invalids);
INIT_LIST_HEAD(&bo_va->vm_status);
-
+ mutex_init(&bo_va->mutex);
list_add_tail(&bo_va->bo_list, &bo->va);
return bo_va;
@@ -1045,7 +1050,9 @@ int amdgpu_vm_bo_map(struct amdgpu_device *adev,
mapping->offset = offset;
mapping->flags = flags;
+ mutex_lock(&bo_va->mutex);
list_add(&mapping->list, &bo_va->invalids);
+ mutex_unlock(&bo_va->mutex);
spin_lock(&vm->it_lock);
interval_tree_insert(&mapping->it, &vm->va);
spin_unlock(&vm->it_lock);
@@ -1076,6 +1083,11 @@ int amdgpu_vm_bo_map(struct amdgpu_device *adev,
if (r)
goto error_free;
+ /* Keep a reference to the page table to avoid freeing
+ * them up in the wrong order.
+ */
+ pt->parent = amdgpu_bo_ref(vm->page_directory);
+
r = amdgpu_vm_clear_bo(adev, pt);
if (r) {
amdgpu_bo_unref(&pt);
@@ -1121,7 +1133,7 @@ int amdgpu_vm_bo_unmap(struct amdgpu_device *adev,
bool valid = true;
saddr /= AMDGPU_GPU_PAGE_SIZE;
-
+ mutex_lock(&bo_va->mutex);
list_for_each_entry(mapping, &bo_va->valids, list) {
if (mapping->it.start == saddr)
break;
@@ -1135,20 +1147,25 @@ int amdgpu_vm_bo_unmap(struct amdgpu_device *adev,
break;
}
- if (&mapping->list == &bo_va->invalids)
+ if (&mapping->list == &bo_va->invalids) {
+ mutex_unlock(&bo_va->mutex);
return -ENOENT;
+ }
}
-
+ mutex_unlock(&bo_va->mutex);
list_del(&mapping->list);
spin_lock(&vm->it_lock);
interval_tree_remove(&mapping->it, &vm->va);
spin_unlock(&vm->it_lock);
trace_amdgpu_vm_bo_unmap(bo_va, mapping);
- if (valid)
+ if (valid) {
+ spin_lock(&vm->freed_lock);
list_add(&mapping->list, &vm->freed);
- else
+ spin_unlock(&vm->freed_lock);
+ } else {
kfree(mapping);
+ }
return 0;
}
@@ -1181,7 +1198,9 @@ void amdgpu_vm_bo_rmv(struct amdgpu_device *adev,
interval_tree_remove(&mapping->it, &vm->va);
spin_unlock(&vm->it_lock);
trace_amdgpu_vm_bo_unmap(bo_va, mapping);
+ spin_lock(&vm->freed_lock);
list_add(&mapping->list, &vm->freed);
+ spin_unlock(&vm->freed_lock);
}
list_for_each_entry_safe(mapping, next, &bo_va->invalids, list) {
list_del(&mapping->list);
@@ -1190,8 +1209,8 @@ void amdgpu_vm_bo_rmv(struct amdgpu_device *adev,
spin_unlock(&vm->it_lock);
kfree(mapping);
}
-
fence_put(bo_va->last_pt_update);
+ mutex_destroy(&bo_va->mutex);
kfree(bo_va);
}
@@ -1236,13 +1255,13 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm)
vm->ids[i].id = 0;
vm->ids[i].flushed_updates = NULL;
}
- mutex_init(&vm->mutex);
vm->va = RB_ROOT;
spin_lock_init(&vm->status_lock);
INIT_LIST_HEAD(&vm->invalidated);
INIT_LIST_HEAD(&vm->cleared);
INIT_LIST_HEAD(&vm->freed);
spin_lock_init(&vm->it_lock);
+ spin_lock_init(&vm->freed_lock);
pd_size = amdgpu_vm_directory_size(adev);
pd_entries = amdgpu_vm_num_pdes(adev);
@@ -1320,7 +1339,6 @@ void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm)
fence_put(vm->ids[i].flushed_updates);
}
- mutex_destroy(&vm->mutex);
}
/**
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
index cb0f7747e3dc..4dcc8fba5792 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
@@ -1250,7 +1250,7 @@ static void dce_v10_0_program_watermarks(struct amdgpu_device *adev,
u32 pixel_period;
u32 line_time = 0;
u32 latency_watermark_a = 0, latency_watermark_b = 0;
- u32 tmp, wm_mask;
+ u32 tmp, wm_mask, lb_vblank_lead_lines = 0;
if (amdgpu_crtc->base.enabled && num_heads && mode) {
pixel_period = 1000000 / (u32)mode->clock;
@@ -1333,6 +1333,7 @@ static void dce_v10_0_program_watermarks(struct amdgpu_device *adev,
(adev->mode_info.disp_priority == 2)) {
DRM_DEBUG_KMS("force priority to high\n");
}
+ lb_vblank_lead_lines = DIV_ROUND_UP(lb_size, mode->crtc_hdisplay);
}
/* select wm A */
@@ -1357,6 +1358,8 @@ static void dce_v10_0_program_watermarks(struct amdgpu_device *adev,
amdgpu_crtc->line_time = line_time;
amdgpu_crtc->wm_high = latency_watermark_a;
amdgpu_crtc->wm_low = latency_watermark_b;
+ /* Save number of lines the linebuffer leads before the scanout */
+ amdgpu_crtc->lb_vblank_lead_lines = lb_vblank_lead_lines;
}
/**
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
index 5af3721851d6..8f1e51128b33 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
@@ -1238,7 +1238,7 @@ static void dce_v11_0_program_watermarks(struct amdgpu_device *adev,
u32 pixel_period;
u32 line_time = 0;
u32 latency_watermark_a = 0, latency_watermark_b = 0;
- u32 tmp, wm_mask;
+ u32 tmp, wm_mask, lb_vblank_lead_lines = 0;
if (amdgpu_crtc->base.enabled && num_heads && mode) {
pixel_period = 1000000 / (u32)mode->clock;
@@ -1321,6 +1321,7 @@ static void dce_v11_0_program_watermarks(struct amdgpu_device *adev,
(adev->mode_info.disp_priority == 2)) {
DRM_DEBUG_KMS("force priority to high\n");
}
+ lb_vblank_lead_lines = DIV_ROUND_UP(lb_size, mode->crtc_hdisplay);
}
/* select wm A */
@@ -1345,6 +1346,8 @@ static void dce_v11_0_program_watermarks(struct amdgpu_device *adev,
amdgpu_crtc->line_time = line_time;
amdgpu_crtc->wm_high = latency_watermark_a;
amdgpu_crtc->wm_low = latency_watermark_b;
+ /* Save number of lines the linebuffer leads before the scanout */
+ amdgpu_crtc->lb_vblank_lead_lines = lb_vblank_lead_lines;
}
/**
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
index 4f7b49a6dc50..42d954dc436d 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
@@ -1193,7 +1193,7 @@ static void dce_v8_0_program_watermarks(struct amdgpu_device *adev,
u32 pixel_period;
u32 line_time = 0;
u32 latency_watermark_a = 0, latency_watermark_b = 0;
- u32 tmp, wm_mask;
+ u32 tmp, wm_mask, lb_vblank_lead_lines = 0;
if (amdgpu_crtc->base.enabled && num_heads && mode) {
pixel_period = 1000000 / (u32)mode->clock;
@@ -1276,6 +1276,7 @@ static void dce_v8_0_program_watermarks(struct amdgpu_device *adev,
(adev->mode_info.disp_priority == 2)) {
DRM_DEBUG_KMS("force priority to high\n");
}
+ lb_vblank_lead_lines = DIV_ROUND_UP(lb_size, mode->crtc_hdisplay);
}
/* select wm A */
@@ -1302,6 +1303,8 @@ static void dce_v8_0_program_watermarks(struct amdgpu_device *adev,
amdgpu_crtc->line_time = line_time;
amdgpu_crtc->wm_high = latency_watermark_a;
amdgpu_crtc->wm_low = latency_watermark_b;
+ /* Save number of lines the linebuffer leads before the scanout */
+ amdgpu_crtc->lb_vblank_lead_lines = lb_vblank_lead_lines;
}
/**
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c
index 7427d8cd4c43..ed8abb58a785 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c
@@ -513,7 +513,7 @@ static int gmc_v7_0_gart_enable(struct amdgpu_device *adev)
WREG32(mmVM_L2_CNTL3, tmp);
/* setup context0 */
WREG32(mmVM_CONTEXT0_PAGE_TABLE_START_ADDR, adev->mc.gtt_start >> 12);
- WREG32(mmVM_CONTEXT0_PAGE_TABLE_END_ADDR, (adev->mc.gtt_end >> 12) - 1);
+ WREG32(mmVM_CONTEXT0_PAGE_TABLE_END_ADDR, adev->mc.gtt_end >> 12);
WREG32(mmVM_CONTEXT0_PAGE_TABLE_BASE_ADDR, adev->gart.table_addr >> 12);
WREG32(mmVM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR,
(u32)(adev->dummy_page.addr >> 12));
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c
index cb0e50ebb528..d39028440814 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c
@@ -657,7 +657,7 @@ static int gmc_v8_0_gart_enable(struct amdgpu_device *adev)
WREG32(mmVM_L2_CNTL4, tmp);
/* setup context0 */
WREG32(mmVM_CONTEXT0_PAGE_TABLE_START_ADDR, adev->mc.gtt_start >> 12);
- WREG32(mmVM_CONTEXT0_PAGE_TABLE_END_ADDR, (adev->mc.gtt_end >> 12) - 1);
+ WREG32(mmVM_CONTEXT0_PAGE_TABLE_END_ADDR, adev->mc.gtt_end >> 12);
WREG32(mmVM_CONTEXT0_PAGE_TABLE_BASE_ADDR, adev->gart.table_addr >> 12);
WREG32(mmVM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR,
(u32)(adev->dummy_page.addr >> 12));
diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c
index 6a52db6ad8d7..370c6c9d81c2 100644
--- a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c
@@ -40,6 +40,9 @@
#define GRBM_GFX_INDEX__VCE_INSTANCE__SHIFT 0x04
#define GRBM_GFX_INDEX__VCE_INSTANCE_MASK 0x10
+#define mmVCE_LMI_VCPU_CACHE_40BIT_BAR0 0x8616
+#define mmVCE_LMI_VCPU_CACHE_40BIT_BAR1 0x8617
+#define mmVCE_LMI_VCPU_CACHE_40BIT_BAR2 0x8618
#define VCE_V3_0_FW_SIZE (384 * 1024)
#define VCE_V3_0_STACK_SIZE (64 * 1024)
@@ -130,9 +133,11 @@ static int vce_v3_0_start(struct amdgpu_device *adev)
/* set BUSY flag */
WREG32_P(mmVCE_STATUS, 1, ~1);
-
- WREG32_P(mmVCE_VCPU_CNTL, VCE_VCPU_CNTL__CLK_EN_MASK,
- ~VCE_VCPU_CNTL__CLK_EN_MASK);
+ if (adev->asic_type >= CHIP_STONEY)
+ WREG32_P(mmVCE_VCPU_CNTL, 1, ~0x200001);
+ else
+ WREG32_P(mmVCE_VCPU_CNTL, VCE_VCPU_CNTL__CLK_EN_MASK,
+ ~VCE_VCPU_CNTL__CLK_EN_MASK);
WREG32_P(mmVCE_SOFT_RESET,
VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK,
@@ -391,8 +396,12 @@ static void vce_v3_0_mc_resume(struct amdgpu_device *adev, int idx)
WREG32(mmVCE_LMI_SWAP_CNTL, 0);
WREG32(mmVCE_LMI_SWAP_CNTL1, 0);
WREG32(mmVCE_LMI_VM_CTRL, 0);
-
- WREG32(mmVCE_LMI_VCPU_CACHE_40BIT_BAR, (adev->vce.gpu_addr >> 8));
+ if (adev->asic_type >= CHIP_STONEY) {
+ WREG32(mmVCE_LMI_VCPU_CACHE_40BIT_BAR0, (adev->vce.gpu_addr >> 8));
+ WREG32(mmVCE_LMI_VCPU_CACHE_40BIT_BAR1, (adev->vce.gpu_addr >> 8));
+ WREG32(mmVCE_LMI_VCPU_CACHE_40BIT_BAR2, (adev->vce.gpu_addr >> 8));
+ } else
+ WREG32(mmVCE_LMI_VCPU_CACHE_40BIT_BAR, (adev->vce.gpu_addr >> 8));
offset = AMDGPU_VCE_FIRMWARE_OFFSET;
size = VCE_V3_0_FW_SIZE;
WREG32(mmVCE_VCPU_CACHE_OFFSET0, offset & 0x7fffffff);
@@ -576,6 +585,11 @@ static int vce_v3_0_process_interrupt(struct amdgpu_device *adev,
struct amdgpu_iv_entry *entry)
{
DRM_DEBUG("IH: VCE\n");
+
+ WREG32_P(mmVCE_SYS_INT_STATUS,
+ VCE_SYS_INT_STATUS__VCE_SYS_INT_TRAP_INTERRUPT_INT_MASK,
+ ~VCE_SYS_INT_STATUS__VCE_SYS_INT_TRAP_INTERRUPT_INT_MASK);
+
switch (entry->src_data) {
case 0:
amdgpu_fence_process(&adev->vce.ring[0]);
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
index c6a1b4cc6458..d2b49c026cf6 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
@@ -558,20 +558,10 @@ static int kfd_ioctl_dbg_address_watch(struct file *filep,
return -EINVAL;
/* this is the actual buffer to work with */
-
- args_buff = kmalloc(args->buf_size_in_bytes -
- sizeof(*args), GFP_KERNEL);
- if (args_buff == NULL)
- return -ENOMEM;
-
- status = copy_from_user(args_buff, cmd_from_user,
+ args_buff = memdup_user(cmd_from_user,
args->buf_size_in_bytes - sizeof(*args));
-
- if (status != 0) {
- pr_debug("Failed to copy address watch user data\n");
- kfree(args_buff);
- return -EINVAL;
- }
+ if (IS_ERR(args_buff))
+ return PTR_ERR(args_buff);
aw_info.process = p;
@@ -677,22 +667,12 @@ static int kfd_ioctl_dbg_wave_control(struct file *filep,
if (cmd_from_user == NULL)
return -EINVAL;
- /* this is the actual buffer to work with */
-
- args_buff = kmalloc(args->buf_size_in_bytes - sizeof(*args),
- GFP_KERNEL);
-
- if (args_buff == NULL)
- return -ENOMEM;
+ /* copy the entire buffer from user */
- /* Now copy the entire buffer from user */
- status = copy_from_user(args_buff, cmd_from_user,
+ args_buff = memdup_user(cmd_from_user,
args->buf_size_in_bytes - sizeof(*args));
- if (status != 0) {
- pr_debug("Failed to copy wave control user data\n");
- kfree(args_buff);
- return -EINVAL;
- }
+ if (IS_ERR(args_buff))
+ return PTR_ERR(args_buff);
/* move ptr to the start of the "pay-load" area */
wac_info.process = p;
diff --git a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c
index ea30d6ad4c13..3a4820e863ec 100644
--- a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c
+++ b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.c
@@ -30,8 +30,7 @@
#define CREATE_TRACE_POINTS
#include "gpu_sched_trace.h"
-static struct amd_sched_job *
-amd_sched_entity_pop_job(struct amd_sched_entity *entity);
+static bool amd_sched_entity_is_ready(struct amd_sched_entity *entity);
static void amd_sched_wakeup(struct amd_gpu_scheduler *sched);
struct kmem_cache *sched_fence_slab;
@@ -64,36 +63,36 @@ static void amd_sched_rq_remove_entity(struct amd_sched_rq *rq,
}
/**
- * Select next job from a specified run queue with round robin policy.
- * Return NULL if nothing available.
+ * Select an entity which could provide a job to run
+ *
+ * @rq The run queue to check.
+ *
+ * Try to find a ready entity, returns NULL if none found.
*/
-static struct amd_sched_job *
-amd_sched_rq_select_job(struct amd_sched_rq *rq)
+static struct amd_sched_entity *
+amd_sched_rq_select_entity(struct amd_sched_rq *rq)
{
struct amd_sched_entity *entity;
- struct amd_sched_job *sched_job;
spin_lock(&rq->lock);
entity = rq->current_entity;
if (entity) {
list_for_each_entry_continue(entity, &rq->entities, list) {
- sched_job = amd_sched_entity_pop_job(entity);
- if (sched_job) {
+ if (amd_sched_entity_is_ready(entity)) {
rq->current_entity = entity;
spin_unlock(&rq->lock);
- return sched_job;
+ return entity;
}
}
}
list_for_each_entry(entity, &rq->entities, list) {
- sched_job = amd_sched_entity_pop_job(entity);
- if (sched_job) {
+ if (amd_sched_entity_is_ready(entity)) {
rq->current_entity = entity;
spin_unlock(&rq->lock);
- return sched_job;
+ return entity;
}
if (entity == rq->current_entity)
@@ -177,6 +176,24 @@ static bool amd_sched_entity_is_idle(struct amd_sched_entity *entity)
}
/**
+ * Check if entity is ready
+ *
+ * @entity The pointer to a valid scheduler entity
+ *
+ * Return true if entity could provide a job.
+ */
+static bool amd_sched_entity_is_ready(struct amd_sched_entity *entity)
+{
+ if (kfifo_is_empty(&entity->job_queue))
+ return false;
+
+ if (ACCESS_ONCE(entity->dependency))
+ return false;
+
+ return true;
+}
+
+/**
* Destroy a context entity
*
* @sched Pointer to scheduler instance
@@ -211,32 +228,53 @@ static void amd_sched_entity_wakeup(struct fence *f, struct fence_cb *cb)
amd_sched_wakeup(entity->sched);
}
+static bool amd_sched_entity_add_dependency_cb(struct amd_sched_entity *entity)
+{
+ struct amd_gpu_scheduler *sched = entity->sched;
+ struct fence * fence = entity->dependency;
+ struct amd_sched_fence *s_fence;
+
+ if (fence->context == entity->fence_context) {
+ /* We can ignore fences from ourself */
+ fence_put(entity->dependency);
+ return false;
+ }
+
+ s_fence = to_amd_sched_fence(fence);
+ if (s_fence && s_fence->sched == sched) {
+ /* Fence is from the same scheduler */
+ if (test_bit(AMD_SCHED_FENCE_SCHEDULED_BIT, &fence->flags)) {
+ /* Ignore it when it is already scheduled */
+ fence_put(entity->dependency);
+ return false;
+ }
+
+ /* Wait for fence to be scheduled */
+ entity->cb.func = amd_sched_entity_wakeup;
+ list_add_tail(&entity->cb.node, &s_fence->scheduled_cb);
+ return true;
+ }
+
+ if (!fence_add_callback(entity->dependency, &entity->cb,
+ amd_sched_entity_wakeup))
+ return true;
+
+ fence_put(entity->dependency);
+ return false;
+}
+
static struct amd_sched_job *
amd_sched_entity_pop_job(struct amd_sched_entity *entity)
{
struct amd_gpu_scheduler *sched = entity->sched;
struct amd_sched_job *sched_job;
- if (ACCESS_ONCE(entity->dependency))
- return NULL;
-
if (!kfifo_out_peek(&entity->job_queue, &sched_job, sizeof(sched_job)))
return NULL;
- while ((entity->dependency = sched->ops->dependency(sched_job))) {
-
- if (entity->dependency->context == entity->fence_context) {
- /* We can ignore fences from ourself */
- fence_put(entity->dependency);
- continue;
- }
-
- if (fence_add_callback(entity->dependency, &entity->cb,
- amd_sched_entity_wakeup))
- fence_put(entity->dependency);
- else
+ while ((entity->dependency = sched->ops->dependency(sched_job)))
+ if (amd_sched_entity_add_dependency_cb(entity))
return NULL;
- }
return sched_job;
}
@@ -250,6 +288,7 @@ amd_sched_entity_pop_job(struct amd_sched_entity *entity)
*/
static bool amd_sched_entity_in(struct amd_sched_job *sched_job)
{
+ struct amd_gpu_scheduler *sched = sched_job->sched;
struct amd_sched_entity *entity = sched_job->s_entity;
bool added, first = false;
@@ -264,7 +303,7 @@ static bool amd_sched_entity_in(struct amd_sched_job *sched_job)
/* first job wakes up scheduler */
if (first)
- amd_sched_wakeup(sched_job->sched);
+ amd_sched_wakeup(sched);
return added;
}
@@ -280,9 +319,9 @@ void amd_sched_entity_push_job(struct amd_sched_job *sched_job)
{
struct amd_sched_entity *entity = sched_job->s_entity;
+ trace_amd_sched_job(sched_job);
wait_event(entity->sched->job_scheduled,
amd_sched_entity_in(sched_job));
- trace_amd_sched_job(sched_job);
}
/**
@@ -304,22 +343,22 @@ static void amd_sched_wakeup(struct amd_gpu_scheduler *sched)
}
/**
- * Select next to run
+ * Select next entity to process
*/
-static struct amd_sched_job *
-amd_sched_select_job(struct amd_gpu_scheduler *sched)
+static struct amd_sched_entity *
+amd_sched_select_entity(struct amd_gpu_scheduler *sched)
{
- struct amd_sched_job *sched_job;
+ struct amd_sched_entity *entity;
if (!amd_sched_ready(sched))
return NULL;
/* Kernel run queue has higher priority than normal run queue*/
- sched_job = amd_sched_rq_select_job(&sched->kernel_rq);
- if (sched_job == NULL)
- sched_job = amd_sched_rq_select_job(&sched->sched_rq);
+ entity = amd_sched_rq_select_entity(&sched->kernel_rq);
+ if (entity == NULL)
+ entity = amd_sched_rq_select_entity(&sched->sched_rq);
- return sched_job;
+ return entity;
}
static void amd_sched_process_job(struct fence *f, struct fence_cb *cb)
@@ -381,13 +420,16 @@ static int amd_sched_main(void *param)
unsigned long flags;
wait_event_interruptible(sched->wake_up_worker,
- kthread_should_stop() ||
- (sched_job = amd_sched_select_job(sched)));
+ (entity = amd_sched_select_entity(sched)) ||
+ kthread_should_stop());
+ if (!entity)
+ continue;
+
+ sched_job = amd_sched_entity_pop_job(entity);
if (!sched_job)
continue;
- entity = sched_job->s_entity;
s_fence = sched_job->s_fence;
if (sched->timeout != MAX_SCHEDULE_TIMEOUT) {
@@ -400,6 +442,7 @@ static int amd_sched_main(void *param)
atomic_inc(&sched->hw_rq_count);
fence = sched->ops->run_job(sched_job);
+ amd_sched_fence_scheduled(s_fence);
if (fence) {
r = fence_add_callback(fence, &s_fence->cb,
amd_sched_process_job);
diff --git a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h
index 939692b14f4b..a0f0ae53aacd 100644
--- a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h
+++ b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h
@@ -27,6 +27,8 @@
#include <linux/kfifo.h>
#include <linux/fence.h>
+#define AMD_SCHED_FENCE_SCHEDULED_BIT FENCE_FLAG_USER_BITS
+
struct amd_gpu_scheduler;
struct amd_sched_rq;
@@ -68,6 +70,7 @@ struct amd_sched_rq {
struct amd_sched_fence {
struct fence base;
struct fence_cb cb;
+ struct list_head scheduled_cb;
struct amd_gpu_scheduler *sched;
spinlock_t lock;
void *owner;
@@ -134,7 +137,7 @@ void amd_sched_entity_push_job(struct amd_sched_job *sched_job);
struct amd_sched_fence *amd_sched_fence_create(
struct amd_sched_entity *s_entity, void *owner);
+void amd_sched_fence_scheduled(struct amd_sched_fence *fence);
void amd_sched_fence_signal(struct amd_sched_fence *fence);
-
#endif
diff --git a/drivers/gpu/drm/amd/scheduler/sched_fence.c b/drivers/gpu/drm/amd/scheduler/sched_fence.c
index 8d2130b9ff05..87c78eecea64 100644
--- a/drivers/gpu/drm/amd/scheduler/sched_fence.c
+++ b/drivers/gpu/drm/amd/scheduler/sched_fence.c
@@ -35,6 +35,8 @@ struct amd_sched_fence *amd_sched_fence_create(struct amd_sched_entity *s_entity
fence = kmem_cache_zalloc(sched_fence_slab, GFP_KERNEL);
if (fence == NULL)
return NULL;
+
+ INIT_LIST_HEAD(&fence->scheduled_cb);
fence->owner = owner;
fence->sched = s_entity->sched;
spin_lock_init(&fence->lock);
@@ -55,6 +57,17 @@ void amd_sched_fence_signal(struct amd_sched_fence *fence)
FENCE_TRACE(&fence->base, "was already signaled\n");
}
+void amd_sched_fence_scheduled(struct amd_sched_fence *s_fence)
+{
+ struct fence_cb *cur, *tmp;
+
+ set_bit(AMD_SCHED_FENCE_SCHEDULED_BIT, &s_fence->base.flags);
+ list_for_each_entry_safe(cur, tmp, &s_fence->scheduled_cb, node) {
+ list_del_init(&cur->node);
+ cur->func(&s_fence->base, cur);
+ }
+}
+
static const char *amd_sched_fence_get_driver_name(struct fence *fence)
{
return "amd_sched";
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 9362609df38a..7dd6728dd092 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -160,6 +160,11 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
goto out_unlock;
}
+ if (!file_priv->allowed_master) {
+ ret = drm_new_set_master(dev, file_priv);
+ goto out_unlock;
+ }
+
file_priv->minor->master = drm_master_get(file_priv->master);
file_priv->is_master = 1;
if (dev->driver->master_set) {
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index c59ce4d0ef75..6b5625e66119 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -126,6 +126,60 @@ static int drm_cpu_valid(void)
}
/**
+ * drm_new_set_master - Allocate a new master object and become master for the
+ * associated master realm.
+ *
+ * @dev: The associated device.
+ * @fpriv: File private identifying the client.
+ *
+ * This function must be called with dev::struct_mutex held.
+ * Returns negative error code on failure. Zero on success.
+ */
+int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
+{
+ struct drm_master *old_master;
+ int ret;
+
+ lockdep_assert_held_once(&dev->master_mutex);
+
+ /* create a new master */
+ fpriv->minor->master = drm_master_create(fpriv->minor);
+ if (!fpriv->minor->master)
+ return -ENOMEM;
+
+ /* take another reference for the copy in the local file priv */
+ old_master = fpriv->master;
+ fpriv->master = drm_master_get(fpriv->minor->master);
+
+ if (dev->driver->master_create) {
+ ret = dev->driver->master_create(dev, fpriv->master);
+ if (ret)
+ goto out_err;
+ }
+ if (dev->driver->master_set) {
+ ret = dev->driver->master_set(dev, fpriv, true);
+ if (ret)
+ goto out_err;
+ }
+
+ fpriv->is_master = 1;
+ fpriv->allowed_master = 1;
+ fpriv->authenticated = 1;
+ if (old_master)
+ drm_master_put(&old_master);
+
+ return 0;
+
+out_err:
+ /* drop both references and restore old master on failure */
+ drm_master_put(&fpriv->minor->master);
+ drm_master_put(&fpriv->master);
+ fpriv->master = old_master;
+
+ return ret;
+}
+
+/**
* Called whenever a process opens /dev/drm.
*
* \param filp file pointer.
@@ -189,35 +243,9 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor)
mutex_lock(&dev->master_mutex);
if (drm_is_primary_client(priv) && !priv->minor->master) {
/* create a new master */
- priv->minor->master = drm_master_create(priv->minor);
- if (!priv->minor->master) {
- ret = -ENOMEM;
+ ret = drm_new_set_master(dev, priv);
+ if (ret)
goto out_close;
- }
-
- priv->is_master = 1;
- /* take another reference for the copy in the local file priv */
- priv->master = drm_master_get(priv->minor->master);
- priv->authenticated = 1;
-
- if (dev->driver->master_create) {
- ret = dev->driver->master_create(dev, priv->master);
- if (ret) {
- /* drop both references if this fails */
- drm_master_put(&priv->minor->master);
- drm_master_put(&priv->master);
- goto out_close;
- }
- }
- if (dev->driver->master_set) {
- ret = dev->driver->master_set(dev, priv, true);
- if (ret) {
- /* drop both references if this fails */
- drm_master_put(&priv->minor->master);
- drm_master_put(&priv->master);
- goto out_close;
- }
- }
} else if (drm_is_primary_client(priv)) {
/* get a reference to the master */
priv->master = drm_master_get(priv->minor->master);
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 2151ea551d3b..607f493ae801 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -980,7 +980,8 @@ static void send_vblank_event(struct drm_device *dev,
struct drm_pending_vblank_event *e,
unsigned long seq, struct timeval *now)
{
- WARN_ON_SMP(!spin_is_locked(&dev->event_lock));
+ assert_spin_locked(&dev->event_lock);
+
e->event.sequence = seq;
e->event.tv_sec = now->tv_sec;
e->event.tv_usec = now->tv_usec;
@@ -993,6 +994,57 @@ static void send_vblank_event(struct drm_device *dev,
}
/**
+ * drm_arm_vblank_event - arm vblank event after pageflip
+ * @dev: DRM device
+ * @pipe: CRTC index
+ * @e: the event to prepare to send
+ *
+ * A lot of drivers need to generate vblank events for the very next vblank
+ * interrupt. For example when the page flip interrupt happens when the page
+ * flip gets armed, but not when it actually executes within the next vblank
+ * period. This helper function implements exactly the required vblank arming
+ * behaviour.
+ *
+ * Caller must hold event lock. Caller must also hold a vblank reference for
+ * the event @e, which will be dropped when the next vblank arrives.
+ *
+ * This is the legacy version of drm_crtc_arm_vblank_event().
+ */
+void drm_arm_vblank_event(struct drm_device *dev, unsigned int pipe,
+ struct drm_pending_vblank_event *e)
+{
+ assert_spin_locked(&dev->event_lock);
+
+ e->pipe = pipe;
+ e->event.sequence = drm_vblank_count(dev, pipe);
+ list_add_tail(&e->base.link, &dev->vblank_event_list);
+}
+EXPORT_SYMBOL(drm_arm_vblank_event);
+
+/**
+ * drm_crtc_arm_vblank_event - arm vblank event after pageflip
+ * @crtc: the source CRTC of the vblank event
+ * @e: the event to send
+ *
+ * A lot of drivers need to generate vblank events for the very next vblank
+ * interrupt. For example when the page flip interrupt happens when the page
+ * flip gets armed, but not when it actually executes within the next vblank
+ * period. This helper function implements exactly the required vblank arming
+ * behaviour.
+ *
+ * Caller must hold event lock. Caller must also hold a vblank reference for
+ * the event @e, which will be dropped when the next vblank arrives.
+ *
+ * This is the native KMS version of drm_arm_vblank_event().
+ */
+void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
+ struct drm_pending_vblank_event *e)
+{
+ drm_arm_vblank_event(crtc->dev, drm_crtc_index(crtc), e);
+}
+EXPORT_SYMBOL(drm_crtc_arm_vblank_event);
+
+/**
* drm_send_vblank_event - helper to send vblank event after pageflip
* @dev: DRM device
* @pipe: CRTC index
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index a18164f2f6d2..f8b5fcfa91a2 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -229,7 +229,8 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
mode_flags |= DRM_MODE_FLAG_3D_MASK;
list_for_each_entry(mode, &connector->modes, head) {
- mode->status = drm_mode_validate_basic(mode);
+ if (mode->status == MODE_OK)
+ mode->status = drm_mode_validate_basic(mode);
if (mode->status == MODE_OK)
mode->status = drm_mode_validate_size(mode, maxX, maxY);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index b3ba27fd9a6b..e69357172ffb 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -55,6 +55,9 @@ static int exynos_crtc_atomic_check(struct drm_crtc *crtc,
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+ if (!state->enable)
+ return 0;
+
if (exynos_crtc->ops->atomic_check)
return exynos_crtc->ops->atomic_check(exynos_crtc, state);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.h b/drivers/gpu/drm/exynos/exynos_drm_fb.h
index 726a2d44371f..cf39f9816a87 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fb.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_fb.h
@@ -12,7 +12,7 @@
*/
#ifndef _EXYNOS_DRM_FB_H_
-#define _EXYNOS_DRM_FB_H
+#define _EXYNOS_DRM_FB_H_
#include "exynos_drm_gem.h"
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index a3b22bdacd44..8aab974b0564 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -2734,6 +2734,8 @@ static const char *power_domain_str(enum intel_display_power_domain domain)
return "AUX_C";
case POWER_DOMAIN_AUX_D:
return "AUX_D";
+ case POWER_DOMAIN_GMBUS:
+ return "GMBUS";
case POWER_DOMAIN_INIT:
return "INIT";
default:
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 95bb27de774f..f4af19a0d569 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -199,6 +199,7 @@ enum intel_display_power_domain {
POWER_DOMAIN_AUX_B,
POWER_DOMAIN_AUX_C,
POWER_DOMAIN_AUX_D,
+ POWER_DOMAIN_GMBUS,
POWER_DOMAIN_INIT,
POWER_DOMAIN_NUM,
@@ -2192,8 +2193,17 @@ struct drm_i915_gem_request {
struct drm_i915_private *i915;
struct intel_engine_cs *ring;
- /** GEM sequence number associated with this request. */
- uint32_t seqno;
+ /** GEM sequence number associated with the previous request,
+ * when the HWS breadcrumb is equal to this the GPU is processing
+ * this request.
+ */
+ u32 previous_seqno;
+
+ /** GEM sequence number associated with this request,
+ * when the HWS breadcrumb is equal or greater than this the GPU
+ * has finished processing this request.
+ */
+ u32 seqno;
/** Position in the ringbuffer of the start of the request */
u32 head;
@@ -2838,6 +2848,7 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
u32 flags);
+void __i915_vma_set_map_and_fenceable(struct i915_vma *vma);
int __must_check i915_vma_unbind(struct i915_vma *vma);
/*
* BEWARE: Do not use the function below unless you can _absolutely_
@@ -2909,15 +2920,17 @@ i915_seqno_passed(uint32_t seq1, uint32_t seq2)
return (int32_t)(seq1 - seq2) >= 0;
}
+static inline bool i915_gem_request_started(struct drm_i915_gem_request *req,
+ bool lazy_coherency)
+{
+ u32 seqno = req->ring->get_seqno(req->ring, lazy_coherency);
+ return i915_seqno_passed(seqno, req->previous_seqno);
+}
+
static inline bool i915_gem_request_completed(struct drm_i915_gem_request *req,
bool lazy_coherency)
{
- u32 seqno;
-
- BUG_ON(req == NULL);
-
- seqno = req->ring->get_seqno(req->ring, lazy_coherency);
-
+ u32 seqno = req->ring->get_seqno(req->ring, lazy_coherency);
return i915_seqno_passed(seqno, req->seqno);
}
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 91bb1fc27420..f56af0aaafde 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -1146,23 +1146,74 @@ static bool missed_irq(struct drm_i915_private *dev_priv,
return test_bit(ring->id, &dev_priv->gpu_error.missed_irq_rings);
}
-static int __i915_spin_request(struct drm_i915_gem_request *req)
+static unsigned long local_clock_us(unsigned *cpu)
+{
+ unsigned long t;
+
+ /* Cheaply and approximately convert from nanoseconds to microseconds.
+ * The result and subsequent calculations are also defined in the same
+ * approximate microseconds units. The principal source of timing
+ * error here is from the simple truncation.
+ *
+ * Note that local_clock() is only defined wrt to the current CPU;
+ * the comparisons are no longer valid if we switch CPUs. Instead of
+ * blocking preemption for the entire busywait, we can detect the CPU
+ * switch and use that as indicator of system load and a reason to
+ * stop busywaiting, see busywait_stop().
+ */
+ *cpu = get_cpu();
+ t = local_clock() >> 10;
+ put_cpu();
+
+ return t;
+}
+
+static bool busywait_stop(unsigned long timeout, unsigned cpu)
+{
+ unsigned this_cpu;
+
+ if (time_after(local_clock_us(&this_cpu), timeout))
+ return true;
+
+ return this_cpu != cpu;
+}
+
+static int __i915_spin_request(struct drm_i915_gem_request *req, int state)
{
unsigned long timeout;
+ unsigned cpu;
+
+ /* When waiting for high frequency requests, e.g. during synchronous
+ * rendering split between the CPU and GPU, the finite amount of time
+ * required to set up the irq and wait upon it limits the response
+ * rate. By busywaiting on the request completion for a short while we
+ * can service the high frequency waits as quick as possible. However,
+ * if it is a slow request, we want to sleep as quickly as possible.
+ * The tradeoff between waiting and sleeping is roughly the time it
+ * takes to sleep on a request, on the order of a microsecond.
+ */
- if (i915_gem_request_get_ring(req)->irq_refcount)
+ if (req->ring->irq_refcount)
return -EBUSY;
- timeout = jiffies + 1;
+ /* Only spin if we know the GPU is processing this request */
+ if (!i915_gem_request_started(req, true))
+ return -EAGAIN;
+
+ timeout = local_clock_us(&cpu) + 5;
while (!need_resched()) {
if (i915_gem_request_completed(req, true))
return 0;
- if (time_after_eq(jiffies, timeout))
+ if (signal_pending_state(state, current))
+ break;
+
+ if (busywait_stop(timeout, cpu))
break;
cpu_relax_lowlatency();
}
+
if (i915_gem_request_completed(req, false))
return 0;
@@ -1197,6 +1248,7 @@ int __i915_wait_request(struct drm_i915_gem_request *req,
struct drm_i915_private *dev_priv = dev->dev_private;
const bool irq_test_in_progress =
ACCESS_ONCE(dev_priv->gpu_error.test_irq_rings) & intel_ring_flag(ring);
+ int state = interruptible ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE;
DEFINE_WAIT(wait);
unsigned long timeout_expire;
s64 before, now;
@@ -1210,8 +1262,16 @@ int __i915_wait_request(struct drm_i915_gem_request *req,
if (i915_gem_request_completed(req, true))
return 0;
- timeout_expire = timeout ?
- jiffies + nsecs_to_jiffies_timeout((u64)*timeout) : 0;
+ timeout_expire = 0;
+ if (timeout) {
+ if (WARN_ON(*timeout < 0))
+ return -EINVAL;
+
+ if (*timeout == 0)
+ return -ETIME;
+
+ timeout_expire = jiffies + nsecs_to_jiffies_timeout(*timeout);
+ }
if (INTEL_INFO(dev_priv)->gen >= 6)
gen6_rps_boost(dev_priv, rps, req->emitted_jiffies);
@@ -1221,7 +1281,7 @@ int __i915_wait_request(struct drm_i915_gem_request *req,
before = ktime_get_raw_ns();
/* Optimistic spin for the next jiffie before touching IRQs */
- ret = __i915_spin_request(req);
+ ret = __i915_spin_request(req, state);
if (ret == 0)
goto out;
@@ -1233,8 +1293,7 @@ int __i915_wait_request(struct drm_i915_gem_request *req,
for (;;) {
struct timer_list timer;
- prepare_to_wait(&ring->irq_queue, &wait,
- interruptible ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+ prepare_to_wait(&ring->irq_queue, &wait, state);
/* We need to check whether any gpu reset happened in between
* the caller grabbing the seqno and now ... */
@@ -1252,7 +1311,7 @@ int __i915_wait_request(struct drm_i915_gem_request *req,
break;
}
- if (interruptible && signal_pending(current)) {
+ if (signal_pending_state(state, current)) {
ret = -ERESTARTSYS;
break;
}
@@ -2546,6 +2605,7 @@ void __i915_add_request(struct drm_i915_gem_request *request,
request->batch_obj = obj;
request->emitted_jiffies = jiffies;
+ request->previous_seqno = ring->last_submitted_seqno;
ring->last_submitted_seqno = request->seqno;
list_add_tail(&request->list, &ring->request_list);
@@ -4072,6 +4132,29 @@ i915_vma_misplaced(struct i915_vma *vma, uint32_t alignment, uint64_t flags)
return false;
}
+void __i915_vma_set_map_and_fenceable(struct i915_vma *vma)
+{
+ struct drm_i915_gem_object *obj = vma->obj;
+ bool mappable, fenceable;
+ u32 fence_size, fence_alignment;
+
+ fence_size = i915_gem_get_gtt_size(obj->base.dev,
+ obj->base.size,
+ obj->tiling_mode);
+ fence_alignment = i915_gem_get_gtt_alignment(obj->base.dev,
+ obj->base.size,
+ obj->tiling_mode,
+ true);
+
+ fenceable = (vma->node.size == fence_size &&
+ (vma->node.start & (fence_alignment - 1)) == 0);
+
+ mappable = (vma->node.start + fence_size <=
+ to_i915(obj->base.dev)->gtt.mappable_end);
+
+ obj->map_and_fenceable = mappable && fenceable;
+}
+
static int
i915_gem_object_do_pin(struct drm_i915_gem_object *obj,
struct i915_address_space *vm,
@@ -4139,25 +4222,7 @@ i915_gem_object_do_pin(struct drm_i915_gem_object *obj,
if (ggtt_view && ggtt_view->type == I915_GGTT_VIEW_NORMAL &&
(bound ^ vma->bound) & GLOBAL_BIND) {
- bool mappable, fenceable;
- u32 fence_size, fence_alignment;
-
- fence_size = i915_gem_get_gtt_size(obj->base.dev,
- obj->base.size,
- obj->tiling_mode);
- fence_alignment = i915_gem_get_gtt_alignment(obj->base.dev,
- obj->base.size,
- obj->tiling_mode,
- true);
-
- fenceable = (vma->node.size == fence_size &&
- (vma->node.start & (fence_alignment - 1)) == 0);
-
- mappable = (vma->node.start + fence_size <=
- dev_priv->gtt.mappable_end);
-
- obj->map_and_fenceable = mappable && fenceable;
-
+ __i915_vma_set_map_and_fenceable(vma);
WARN_ON(flags & PIN_MAPPABLE && !obj->map_and_fenceable);
}
diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
index 8c688a5f1589..02ceb7a4b481 100644
--- a/drivers/gpu/drm/i915/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/i915_gem_context.c
@@ -141,8 +141,6 @@ static void i915_gem_context_clean(struct intel_context *ctx)
if (!ppgtt)
return;
- WARN_ON(!list_empty(&ppgtt->base.active_list));
-
list_for_each_entry_safe(vma, next, &ppgtt->base.inactive_list,
mm_list) {
if (WARN_ON(__i915_vma_unbind_no_wait(vma)))
diff --git a/drivers/gpu/drm/i915/i915_gem_fence.c b/drivers/gpu/drm/i915/i915_gem_fence.c
index 40a10b25956c..f010391b87f5 100644
--- a/drivers/gpu/drm/i915/i915_gem_fence.c
+++ b/drivers/gpu/drm/i915/i915_gem_fence.c
@@ -642,11 +642,10 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
}
/* check for L-shaped memory aka modified enhanced addressing */
- if (IS_GEN4(dev)) {
- uint32_t ddc2 = I915_READ(DCC2);
-
- if (!(ddc2 & DCC2_MODIFIED_ENHANCED_DISABLE))
- dev_priv->quirks |= QUIRK_PIN_SWIZZLED_PAGES;
+ if (IS_GEN4(dev) &&
+ !(I915_READ(DCC2) & DCC2_MODIFIED_ENHANCED_DISABLE)) {
+ swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN;
+ swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
}
if (dcc == 0xffffffff) {
@@ -675,16 +674,35 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
* matching, which was the case for the swizzling required in
* the table above, or from the 1-ch value being less than
* the minimum size of a rank.
+ *
+ * Reports indicate that the swizzling actually
+ * varies depending upon page placement inside the
+ * channels, i.e. we see swizzled pages where the
+ * banks of memory are paired and unswizzled on the
+ * uneven portion, so leave that as unknown.
*/
- if (I915_READ16(C0DRB3) != I915_READ16(C1DRB3)) {
- swizzle_x = I915_BIT_6_SWIZZLE_NONE;
- swizzle_y = I915_BIT_6_SWIZZLE_NONE;
- } else {
+ if (I915_READ16(C0DRB3) == I915_READ16(C1DRB3)) {
swizzle_x = I915_BIT_6_SWIZZLE_9_10;
swizzle_y = I915_BIT_6_SWIZZLE_9;
}
}
+ if (swizzle_x == I915_BIT_6_SWIZZLE_UNKNOWN ||
+ swizzle_y == I915_BIT_6_SWIZZLE_UNKNOWN) {
+ /* Userspace likes to explode if it sees unknown swizzling,
+ * so lie. We will finish the lie when reporting through
+ * the get-tiling-ioctl by reporting the physical swizzle
+ * mode as unknown instead.
+ *
+ * As we don't strictly know what the swizzling is, it may be
+ * bit17 dependent, and so we need to also prevent the pages
+ * from being moved.
+ */
+ dev_priv->quirks |= QUIRK_PIN_SWIZZLED_PAGES;
+ swizzle_x = I915_BIT_6_SWIZZLE_NONE;
+ swizzle_y = I915_BIT_6_SWIZZLE_NONE;
+ }
+
dev_priv->mm.bit_6_swizzle_x = swizzle_x;
dev_priv->mm.bit_6_swizzle_y = swizzle_y;
}
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index 43f35d12b677..86c7500454b4 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -2676,6 +2676,7 @@ static int i915_gem_setup_global_gtt(struct drm_device *dev,
return ret;
}
vma->bound |= GLOBAL_BIND;
+ __i915_vma_set_map_and_fenceable(vma);
list_add_tail(&vma->mm_list, &ggtt_vm->inactive_list);
}
diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c
index cdacf3f5b77a..87e919a06b27 100644
--- a/drivers/gpu/drm/i915/i915_gem_stolen.c
+++ b/drivers/gpu/drm/i915/i915_gem_stolen.c
@@ -687,6 +687,7 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev,
}
vma->bound |= GLOBAL_BIND;
+ __i915_vma_set_map_and_fenceable(vma);
list_add_tail(&vma->mm_list, &ggtt->inactive_list);
}
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 71860f8680f9..32cf97346978 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -116,6 +116,7 @@ static void skylake_pfit_enable(struct intel_crtc *crtc);
static void ironlake_pfit_disable(struct intel_crtc *crtc, bool force);
static void ironlake_pfit_enable(struct intel_crtc *crtc);
static void intel_modeset_setup_hw_state(struct drm_device *dev);
+static void intel_pre_disable_primary(struct drm_crtc *crtc);
typedef struct {
int min, max;
@@ -2607,6 +2608,8 @@ intel_find_initial_plane_obj(struct intel_crtc *intel_crtc,
struct drm_i915_gem_object *obj;
struct drm_plane *primary = intel_crtc->base.primary;
struct drm_plane_state *plane_state = primary->state;
+ struct drm_crtc_state *crtc_state = intel_crtc->base.state;
+ struct intel_plane *intel_plane = to_intel_plane(primary);
struct drm_framebuffer *fb;
if (!plane_config->fb)
@@ -2643,6 +2646,18 @@ intel_find_initial_plane_obj(struct intel_crtc *intel_crtc,
}
}
+ /*
+ * We've failed to reconstruct the BIOS FB. Current display state
+ * indicates that the primary plane is visible, but has a NULL FB,
+ * which will lead to problems later if we don't fix it up. The
+ * simplest solution is to just disable the primary plane now and
+ * pretend the BIOS never had it enabled.
+ */
+ to_intel_plane_state(plane_state)->visible = false;
+ crtc_state->plane_mask &= ~(1 << drm_plane_index(primary));
+ intel_pre_disable_primary(&intel_crtc->base);
+ intel_plane->disable_plane(primary, &intel_crtc->base);
+
return;
valid_fb:
@@ -5194,11 +5209,31 @@ static enum intel_display_power_domain port_to_power_domain(enum port port)
case PORT_E:
return POWER_DOMAIN_PORT_DDI_E_2_LANES;
default:
- WARN_ON_ONCE(1);
+ MISSING_CASE(port);
return POWER_DOMAIN_PORT_OTHER;
}
}
+static enum intel_display_power_domain port_to_aux_power_domain(enum port port)
+{
+ switch (port) {
+ case PORT_A:
+ return POWER_DOMAIN_AUX_A;
+ case PORT_B:
+ return POWER_DOMAIN_AUX_B;
+ case PORT_C:
+ return POWER_DOMAIN_AUX_C;
+ case PORT_D:
+ return POWER_DOMAIN_AUX_D;
+ case PORT_E:
+ /* FIXME: Check VBT for actual wiring of PORT E */
+ return POWER_DOMAIN_AUX_D;
+ default:
+ MISSING_CASE(port);
+ return POWER_DOMAIN_AUX_A;
+ }
+}
+
#define for_each_power_domain(domain, mask) \
for ((domain) = 0; (domain) < POWER_DOMAIN_NUM; (domain)++) \
if ((1 << (domain)) & (mask))
@@ -5230,6 +5265,36 @@ intel_display_port_power_domain(struct intel_encoder *intel_encoder)
}
}
+enum intel_display_power_domain
+intel_display_port_aux_power_domain(struct intel_encoder *intel_encoder)
+{
+ struct drm_device *dev = intel_encoder->base.dev;
+ struct intel_digital_port *intel_dig_port;
+
+ switch (intel_encoder->type) {
+ case INTEL_OUTPUT_UNKNOWN:
+ case INTEL_OUTPUT_HDMI:
+ /*
+ * Only DDI platforms should ever use these output types.
+ * We can get here after the HDMI detect code has already set
+ * the type of the shared encoder. Since we can't be sure
+ * what's the status of the given connectors, play safe and
+ * run the DP detection too.
+ */
+ WARN_ON_ONCE(!HAS_DDI(dev));
+ case INTEL_OUTPUT_DISPLAYPORT:
+ case INTEL_OUTPUT_EDP:
+ intel_dig_port = enc_to_dig_port(&intel_encoder->base);
+ return port_to_aux_power_domain(intel_dig_port->port);
+ case INTEL_OUTPUT_DP_MST:
+ intel_dig_port = enc_to_mst(&intel_encoder->base)->primary;
+ return port_to_aux_power_domain(intel_dig_port->port);
+ default:
+ MISSING_CASE(intel_encoder->type);
+ return POWER_DOMAIN_AUX_A;
+ }
+}
+
static unsigned long get_crtc_power_domains(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
@@ -6259,9 +6324,11 @@ static void intel_crtc_disable_noatomic(struct drm_crtc *crtc)
if (to_intel_plane_state(crtc->primary->state)->visible) {
intel_crtc_wait_for_pending_flips(crtc);
intel_pre_disable_primary(crtc);
+
+ intel_crtc_disable_planes(crtc, 1 << drm_plane_index(crtc->primary));
+ to_intel_plane_state(crtc->primary->state)->visible = false;
}
- intel_crtc_disable_planes(crtc, crtc->state->plane_mask);
dev_priv->display.crtc_disable(crtc);
intel_crtc->active = false;
intel_update_watermarks(crtc);
@@ -9858,14 +9925,14 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc,
return true;
}
-static void i845_update_cursor(struct drm_crtc *crtc, u32 base)
+static void i845_update_cursor(struct drm_crtc *crtc, u32 base, bool on)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
uint32_t cntl = 0, size = 0;
- if (base) {
+ if (on) {
unsigned int width = intel_crtc->base.cursor->state->crtc_w;
unsigned int height = intel_crtc->base.cursor->state->crtc_h;
unsigned int stride = roundup_pow_of_two(width) * 4;
@@ -9920,16 +9987,15 @@ static void i845_update_cursor(struct drm_crtc *crtc, u32 base)
}
}
-static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base)
+static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base, bool on)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe;
- uint32_t cntl;
+ uint32_t cntl = 0;
- cntl = 0;
- if (base) {
+ if (on) {
cntl = MCURSOR_GAMMA_ENABLE;
switch (intel_crtc->base.cursor->state->crtc_w) {
case 64:
@@ -9980,18 +10046,17 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
int y = cursor_state->crtc_y;
u32 base = 0, pos = 0;
- if (on)
- base = intel_crtc->cursor_addr;
+ base = intel_crtc->cursor_addr;
if (x >= intel_crtc->config->pipe_src_w)
- base = 0;
+ on = false;
if (y >= intel_crtc->config->pipe_src_h)
- base = 0;
+ on = false;
if (x < 0) {
if (x + cursor_state->crtc_w <= 0)
- base = 0;
+ on = false;
pos |= CURSOR_POS_SIGN << CURSOR_X_SHIFT;
x = -x;
@@ -10000,16 +10065,13 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
if (y < 0) {
if (y + cursor_state->crtc_h <= 0)
- base = 0;
+ on = false;
pos |= CURSOR_POS_SIGN << CURSOR_Y_SHIFT;
y = -y;
}
pos |= y << CURSOR_Y_SHIFT;
- if (base == 0 && intel_crtc->cursor_base == 0)
- return;
-
I915_WRITE(CURPOS(pipe), pos);
/* ILK+ do this automagically */
@@ -10020,9 +10082,9 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
}
if (IS_845G(dev) || IS_I865G(dev))
- i845_update_cursor(crtc, base);
+ i845_update_cursor(crtc, base, on);
else
- i9xx_update_cursor(crtc, base);
+ i9xx_update_cursor(crtc, base, on);
}
static bool cursor_size_ok(struct drm_device *dev,
@@ -12061,18 +12123,22 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc,
static bool check_digital_port_conflicts(struct drm_atomic_state *state)
{
struct drm_device *dev = state->dev;
- struct intel_encoder *encoder;
struct drm_connector *connector;
- struct drm_connector_state *connector_state;
unsigned int used_ports = 0;
- int i;
/*
* Walk the connector list instead of the encoder
* list to detect the problem on ddi platforms
* where there's just one encoder per digital port.
*/
- for_each_connector_in_state(state, connector, connector_state, i) {
+ drm_for_each_connector(connector, dev) {
+ struct drm_connector_state *connector_state;
+ struct intel_encoder *encoder;
+
+ connector_state = drm_atomic_get_existing_connector_state(state, connector);
+ if (!connector_state)
+ connector_state = connector->state;
+
if (!connector_state->best_encoder)
continue;
@@ -12460,7 +12526,6 @@ intel_pipe_config_compare(struct drm_device *dev,
if (INTEL_INFO(dev)->gen < 8) {
PIPE_CONF_CHECK_M_N(dp_m_n);
- PIPE_CONF_CHECK_I(has_drrs);
if (current_config->has_drrs)
PIPE_CONF_CHECK_M_N(dp_m2_n2);
} else
@@ -13667,6 +13732,7 @@ intel_check_cursor_plane(struct drm_plane *plane,
struct drm_crtc *crtc = crtc_state->base.crtc;
struct drm_framebuffer *fb = state->base.fb;
struct drm_i915_gem_object *obj = intel_fb_obj(fb);
+ enum pipe pipe = to_intel_plane(plane)->pipe;
unsigned stride;
int ret;
@@ -13700,6 +13766,22 @@ intel_check_cursor_plane(struct drm_plane *plane,
return -EINVAL;
}
+ /*
+ * There's something wrong with the cursor on CHV pipe C.
+ * If it straddles the left edge of the screen then
+ * moving it away from the edge or disabling it often
+ * results in a pipe underrun, and often that can lead to
+ * dead pipe (constant underrun reported, and it scans
+ * out just a solid color). To recover from that, the
+ * display power well must be turned off and on again.
+ * Refuse the put the cursor into that compromised position.
+ */
+ if (IS_CHERRYVIEW(plane->dev) && pipe == PIPE_C &&
+ state->visible && state->base.crtc_x < 0) {
+ DRM_DEBUG_KMS("CHV cursor C not allowed to straddle the left screen edge\n");
+ return -EINVAL;
+ }
+
return 0;
}
@@ -13723,9 +13805,6 @@ intel_commit_cursor_plane(struct drm_plane *plane,
crtc = crtc ? crtc : plane->crtc;
intel_crtc = to_intel_crtc(crtc);
- if (intel_crtc->cursor_bo == obj)
- goto update;
-
if (!obj)
addr = 0;
else if (!INTEL_INFO(dev)->cursor_needs_physical)
@@ -13734,9 +13813,7 @@ intel_commit_cursor_plane(struct drm_plane *plane,
addr = obj->phys_handle->busaddr;
intel_crtc->cursor_addr = addr;
- intel_crtc->cursor_bo = obj;
-update:
if (crtc->state->active)
intel_crtc_update_cursor(crtc, state->visible);
}
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 09bdd94ca3ba..78b8ec84d576 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -277,7 +277,7 @@ static void pps_lock(struct intel_dp *intel_dp)
* See vlv_power_sequencer_reset() why we need
* a power domain reference here.
*/
- power_domain = intel_display_port_power_domain(encoder);
+ power_domain = intel_display_port_aux_power_domain(encoder);
intel_display_power_get(dev_priv, power_domain);
mutex_lock(&dev_priv->pps_mutex);
@@ -293,7 +293,7 @@ static void pps_unlock(struct intel_dp *intel_dp)
mutex_unlock(&dev_priv->pps_mutex);
- power_domain = intel_display_port_power_domain(encoder);
+ power_domain = intel_display_port_aux_power_domain(encoder);
intel_display_power_put(dev_priv, power_domain);
}
@@ -816,8 +816,6 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
intel_dp_check_edp(intel_dp);
- intel_aux_display_runtime_get(dev_priv);
-
/* Try to wait for any previous AUX channel activity */
for (try = 0; try < 3; try++) {
status = I915_READ_NOTRACE(ch_ctl);
@@ -926,7 +924,6 @@ done:
ret = recv_bytes;
out:
pm_qos_update_request(&dev_priv->pm_qos, PM_QOS_DEFAULT_VALUE);
- intel_aux_display_runtime_put(dev_priv);
if (vdd)
edp_panel_vdd_off(intel_dp, false);
@@ -1784,7 +1781,7 @@ static bool edp_panel_vdd_on(struct intel_dp *intel_dp)
if (edp_have_panel_vdd(intel_dp))
return need_to_disable;
- power_domain = intel_display_port_power_domain(intel_encoder);
+ power_domain = intel_display_port_aux_power_domain(intel_encoder);
intel_display_power_get(dev_priv, power_domain);
DRM_DEBUG_KMS("Turning eDP port %c VDD on\n",
@@ -1874,7 +1871,7 @@ static void edp_panel_vdd_off_sync(struct intel_dp *intel_dp)
if ((pp & POWER_TARGET_ON) == 0)
intel_dp->last_power_cycle = jiffies;
- power_domain = intel_display_port_power_domain(intel_encoder);
+ power_domain = intel_display_port_aux_power_domain(intel_encoder);
intel_display_power_put(dev_priv, power_domain);
}
@@ -2025,7 +2022,7 @@ static void edp_panel_off(struct intel_dp *intel_dp)
wait_panel_off(intel_dp);
/* We got a reference when we enabled the VDD. */
- power_domain = intel_display_port_power_domain(intel_encoder);
+ power_domain = intel_display_port_aux_power_domain(intel_encoder);
intel_display_power_put(dev_priv, power_domain);
}
@@ -4765,26 +4762,6 @@ intel_dp_unset_edid(struct intel_dp *intel_dp)
intel_dp->has_audio = false;
}
-static enum intel_display_power_domain
-intel_dp_power_get(struct intel_dp *dp)
-{
- struct intel_encoder *encoder = &dp_to_dig_port(dp)->base;
- enum intel_display_power_domain power_domain;
-
- power_domain = intel_display_port_power_domain(encoder);
- intel_display_power_get(to_i915(encoder->base.dev), power_domain);
-
- return power_domain;
-}
-
-static void
-intel_dp_power_put(struct intel_dp *dp,
- enum intel_display_power_domain power_domain)
-{
- struct intel_encoder *encoder = &dp_to_dig_port(dp)->base;
- intel_display_power_put(to_i915(encoder->base.dev), power_domain);
-}
-
static enum drm_connector_status
intel_dp_detect(struct drm_connector *connector, bool force)
{
@@ -4808,7 +4785,8 @@ intel_dp_detect(struct drm_connector *connector, bool force)
return connector_status_disconnected;
}
- power_domain = intel_dp_power_get(intel_dp);
+ power_domain = intel_display_port_aux_power_domain(intel_encoder);
+ intel_display_power_get(to_i915(dev), power_domain);
/* Can't disconnect eDP, but you can close the lid... */
if (is_edp(intel_dp))
@@ -4853,7 +4831,7 @@ intel_dp_detect(struct drm_connector *connector, bool force)
}
out:
- intel_dp_power_put(intel_dp, power_domain);
+ intel_display_power_put(to_i915(dev), power_domain);
return status;
}
@@ -4862,6 +4840,7 @@ intel_dp_force(struct drm_connector *connector)
{
struct intel_dp *intel_dp = intel_attached_dp(connector);
struct intel_encoder *intel_encoder = &dp_to_dig_port(intel_dp)->base;
+ struct drm_i915_private *dev_priv = to_i915(intel_encoder->base.dev);
enum intel_display_power_domain power_domain;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
@@ -4871,11 +4850,12 @@ intel_dp_force(struct drm_connector *connector)
if (connector->status != connector_status_connected)
return;
- power_domain = intel_dp_power_get(intel_dp);
+ power_domain = intel_display_port_aux_power_domain(intel_encoder);
+ intel_display_power_get(dev_priv, power_domain);
intel_dp_set_edid(intel_dp);
- intel_dp_power_put(intel_dp, power_domain);
+ intel_display_power_put(dev_priv, power_domain);
if (intel_encoder->type != INTEL_OUTPUT_EDP)
intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
@@ -5091,7 +5071,7 @@ static void intel_edp_panel_vdd_sanitize(struct intel_dp *intel_dp)
* indefinitely.
*/
DRM_DEBUG_KMS("VDD left on by BIOS, adjusting state tracking\n");
- power_domain = intel_display_port_power_domain(&intel_dig_port->base);
+ power_domain = intel_display_port_aux_power_domain(&intel_dig_port->base);
intel_display_power_get(dev_priv, power_domain);
edp_panel_vdd_schedule_off(intel_dp);
@@ -5153,7 +5133,8 @@ intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd)
enum intel_display_power_domain power_domain;
enum irqreturn ret = IRQ_NONE;
- if (intel_dig_port->base.type != INTEL_OUTPUT_EDP)
+ if (intel_dig_port->base.type != INTEL_OUTPUT_EDP &&
+ intel_dig_port->base.type != INTEL_OUTPUT_HDMI)
intel_dig_port->base.type = INTEL_OUTPUT_DISPLAYPORT;
if (long_hpd && intel_dig_port->base.type == INTEL_OUTPUT_EDP) {
@@ -5172,7 +5153,7 @@ intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd)
port_name(intel_dig_port->port),
long_hpd ? "long" : "short");
- power_domain = intel_display_port_power_domain(intel_encoder);
+ power_domain = intel_display_port_aux_power_domain(intel_encoder);
intel_display_power_get(dev_priv, power_domain);
if (long_hpd) {
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 0598932ce623..0d00f07b7163 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -550,7 +550,6 @@ struct intel_crtc {
int adjusted_x;
int adjusted_y;
- struct drm_i915_gem_object *cursor_bo;
uint32_t cursor_addr;
uint32_t cursor_cntl;
uint32_t cursor_size;
@@ -1169,6 +1168,8 @@ void hsw_enable_ips(struct intel_crtc *crtc);
void hsw_disable_ips(struct intel_crtc *crtc);
enum intel_display_power_domain
intel_display_port_power_domain(struct intel_encoder *intel_encoder);
+enum intel_display_power_domain
+intel_display_port_aux_power_domain(struct intel_encoder *intel_encoder);
void intel_mode_from_pipe_config(struct drm_display_mode *mode,
struct intel_crtc_state *pipe_config);
void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc);
@@ -1377,8 +1378,6 @@ void intel_display_power_get(struct drm_i915_private *dev_priv,
enum intel_display_power_domain domain);
void intel_display_power_put(struct drm_i915_private *dev_priv,
enum intel_display_power_domain domain);
-void intel_aux_display_runtime_get(struct drm_i915_private *dev_priv);
-void intel_aux_display_runtime_put(struct drm_i915_private *dev_priv);
void intel_runtime_pm_get(struct drm_i915_private *dev_priv);
void intel_runtime_pm_get_noresume(struct drm_i915_private *dev_priv);
void intel_runtime_pm_put(struct drm_i915_private *dev_priv);
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 9eafa191cee2..e6c035b0fc1c 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -1335,21 +1335,17 @@ intel_hdmi_set_edid(struct drm_connector *connector, bool force)
{
struct drm_i915_private *dev_priv = to_i915(connector->dev);
struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
- struct intel_encoder *intel_encoder =
- &hdmi_to_dig_port(intel_hdmi)->base;
- enum intel_display_power_domain power_domain;
struct edid *edid = NULL;
bool connected = false;
- power_domain = intel_display_port_power_domain(intel_encoder);
- intel_display_power_get(dev_priv, power_domain);
+ intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
if (force)
edid = drm_get_edid(connector,
intel_gmbus_get_adapter(dev_priv,
intel_hdmi->ddc_bus));
- intel_display_power_put(dev_priv, power_domain);
+ intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
to_intel_connector(connector)->detect_edid = edid;
if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) {
@@ -1378,15 +1374,18 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
struct drm_i915_private *dev_priv = to_i915(connector->dev);
bool live_status = false;
- unsigned int retry = 3;
+ unsigned int try;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
connector->base.id, connector->name);
- while (!live_status && --retry) {
+ intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
+
+ for (try = 0; !live_status && try < 9; try++) {
+ if (try)
+ msleep(10);
live_status = intel_digital_port_connected(dev_priv,
hdmi_to_dig_port(intel_hdmi));
- mdelay(10);
}
if (!live_status)
@@ -1402,6 +1401,8 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
} else
status = connector_status_disconnected;
+ intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
+
return status;
}
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
index 1369fc41d039..8324654037b6 100644
--- a/drivers/gpu/drm/i915/intel_i2c.c
+++ b/drivers/gpu/drm/i915/intel_i2c.c
@@ -483,7 +483,7 @@ gmbus_xfer(struct i2c_adapter *adapter,
int i = 0, inc, try = 0;
int ret = 0;
- intel_aux_display_runtime_get(dev_priv);
+ intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
mutex_lock(&dev_priv->gmbus_mutex);
if (bus->force_bit) {
@@ -595,7 +595,9 @@ timeout:
out:
mutex_unlock(&dev_priv->gmbus_mutex);
- intel_aux_display_runtime_put(dev_priv);
+
+ intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
+
return ret;
}
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 071a76b9ac52..f091ad12d694 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -4782,8 +4782,7 @@ static void gen9_enable_rc6(struct drm_device *dev)
/* 2b: Program RC6 thresholds.*/
/* WaRsDoubleRc6WrlWithCoarsePowerGating: Doubling WRL only when CPG is enabled */
- if (IS_SKYLAKE(dev) && !((IS_SKL_GT3(dev) || IS_SKL_GT4(dev)) &&
- (INTEL_REVID(dev) <= SKL_REVID_E0)))
+ if (IS_SKYLAKE(dev))
I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 108 << 16);
else
I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 54 << 16);
@@ -4825,7 +4824,7 @@ static void gen9_enable_rc6(struct drm_device *dev)
* WaRsDisableCoarsePowerGating:skl,bxt - Render/Media PG need to be disabled with RC6.
*/
if ((IS_BROXTON(dev) && (INTEL_REVID(dev) < BXT_REVID_B0)) ||
- ((IS_SKL_GT3(dev) || IS_SKL_GT4(dev)) && (INTEL_REVID(dev) <= SKL_REVID_E0)))
+ ((IS_SKL_GT3(dev) || IS_SKL_GT4(dev)) && (INTEL_REVID(dev) <= SKL_REVID_F0)))
I915_WRITE(GEN9_PG_ENABLE, 0);
else
I915_WRITE(GEN9_PG_ENABLE, (rc6_mask & GEN6_RC_CTL_RC6_ENABLE) ?
diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c
index d89c1d0aa1b7..7e23d65c9b24 100644
--- a/drivers/gpu/drm/i915/intel_runtime_pm.c
+++ b/drivers/gpu/drm/i915/intel_runtime_pm.c
@@ -362,6 +362,7 @@ static void hsw_set_power_well(struct drm_i915_private *dev_priv,
BIT(POWER_DOMAIN_AUX_C) | \
BIT(POWER_DOMAIN_AUDIO) | \
BIT(POWER_DOMAIN_VGA) | \
+ BIT(POWER_DOMAIN_GMBUS) | \
BIT(POWER_DOMAIN_INIT))
#define BXT_DISPLAY_POWERWELL_1_POWER_DOMAINS ( \
BXT_DISPLAY_POWERWELL_2_POWER_DOMAINS | \
@@ -1483,6 +1484,7 @@ void intel_display_power_put(struct drm_i915_private *dev_priv,
BIT(POWER_DOMAIN_AUX_B) | \
BIT(POWER_DOMAIN_AUX_C) | \
BIT(POWER_DOMAIN_AUX_D) | \
+ BIT(POWER_DOMAIN_GMBUS) | \
BIT(POWER_DOMAIN_INIT))
#define HSW_DISPLAY_POWER_DOMAINS ( \
(POWER_DOMAIN_MASK & ~HSW_ALWAYS_ON_POWER_DOMAINS) | \
@@ -1845,6 +1847,8 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv)
i915.disable_power_well = sanitize_disable_power_well_option(dev_priv,
i915.disable_power_well);
+ BUILD_BUG_ON(POWER_DOMAIN_NUM > 31);
+
mutex_init(&power_domains->lock);
/*
@@ -2064,36 +2068,6 @@ void intel_power_domains_init_hw(struct drm_i915_private *dev_priv)
}
/**
- * intel_aux_display_runtime_get - grab an auxiliary power domain reference
- * @dev_priv: i915 device instance
- *
- * This function grabs a power domain reference for the auxiliary power domain
- * (for access to the GMBUS and DP AUX blocks) and ensures that it and all its
- * parents are powered up. Therefore users should only grab a reference to the
- * innermost power domain they need.
- *
- * Any power domain reference obtained by this function must have a symmetric
- * call to intel_aux_display_runtime_put() to release the reference again.
- */
-void intel_aux_display_runtime_get(struct drm_i915_private *dev_priv)
-{
- intel_runtime_pm_get(dev_priv);
-}
-
-/**
- * intel_aux_display_runtime_put - release an auxiliary power domain reference
- * @dev_priv: i915 device instance
- *
- * This function drops the auxiliary power domain reference obtained by
- * intel_aux_display_runtime_get() and might power down the corresponding
- * hardware block right away if this is the last reference.
- */
-void intel_aux_display_runtime_put(struct drm_i915_private *dev_priv)
-{
- intel_runtime_pm_put(dev_priv);
-}
-
-/**
* intel_runtime_pm_get - grab a runtime pm reference
* @dev_priv: i915 device instance
*
diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c
index 64f16ea779ef..7b990b4e96d2 100644
--- a/drivers/gpu/drm/imx/imx-drm-core.c
+++ b/drivers/gpu/drm/imx/imx-drm-core.c
@@ -63,8 +63,7 @@ static void imx_drm_driver_lastclose(struct drm_device *drm)
#if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER)
struct imx_drm_device *imxdrm = drm->dev_private;
- if (imxdrm->fbhelper)
- drm_fbdev_cma_restore_mode(imxdrm->fbhelper);
+ drm_fbdev_cma_restore_mode(imxdrm->fbhelper);
#endif
}
@@ -340,7 +339,7 @@ err_kms:
* imx_drm_add_crtc - add a new crtc
*/
int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
- struct imx_drm_crtc **new_crtc,
+ struct imx_drm_crtc **new_crtc, struct drm_plane *primary_plane,
const struct imx_drm_crtc_helper_funcs *imx_drm_helper_funcs,
struct device_node *port)
{
@@ -379,7 +378,7 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
drm_crtc_helper_add(crtc,
imx_drm_crtc->imx_drm_helper_funcs.crtc_helper_funcs);
- drm_crtc_init(drm, crtc,
+ drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
imx_drm_crtc->imx_drm_helper_funcs.crtc_funcs);
return 0;
diff --git a/drivers/gpu/drm/imx/imx-drm.h b/drivers/gpu/drm/imx/imx-drm.h
index 28e776d8d9d2..83284b4d4be1 100644
--- a/drivers/gpu/drm/imx/imx-drm.h
+++ b/drivers/gpu/drm/imx/imx-drm.h
@@ -9,6 +9,7 @@ struct drm_display_mode;
struct drm_encoder;
struct drm_fbdev_cma;
struct drm_framebuffer;
+struct drm_plane;
struct imx_drm_crtc;
struct platform_device;
@@ -24,7 +25,7 @@ struct imx_drm_crtc_helper_funcs {
};
int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
- struct imx_drm_crtc **new_crtc,
+ struct imx_drm_crtc **new_crtc, struct drm_plane *primary_plane,
const struct imx_drm_crtc_helper_funcs *imx_helper_funcs,
struct device_node *port);
int imx_drm_remove_crtc(struct imx_drm_crtc *);
diff --git a/drivers/gpu/drm/imx/imx-tve.c b/drivers/gpu/drm/imx/imx-tve.c
index e671ad369416..f9597146dc67 100644
--- a/drivers/gpu/drm/imx/imx-tve.c
+++ b/drivers/gpu/drm/imx/imx-tve.c
@@ -721,6 +721,7 @@ static const struct of_device_id imx_tve_dt_ids[] = {
{ .compatible = "fsl,imx53-tve", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, imx_tve_dt_ids);
static struct platform_driver imx_tve_driver = {
.probe = imx_tve_probe,
diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c
index 7bc8301fafff..4ab841eebee1 100644
--- a/drivers/gpu/drm/imx/ipuv3-crtc.c
+++ b/drivers/gpu/drm/imx/ipuv3-crtc.c
@@ -212,7 +212,8 @@ static void ipu_crtc_handle_pageflip(struct ipu_crtc *ipu_crtc)
spin_lock_irqsave(&drm->event_lock, flags);
if (ipu_crtc->page_flip_event)
- drm_send_vblank_event(drm, -1, ipu_crtc->page_flip_event);
+ drm_crtc_send_vblank_event(&ipu_crtc->base,
+ ipu_crtc->page_flip_event);
ipu_crtc->page_flip_event = NULL;
imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc);
spin_unlock_irqrestore(&drm->event_lock, flags);
@@ -349,7 +350,6 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
int dp = -EINVAL;
int ret;
- int id;
ret = ipu_get_resources(ipu_crtc, pdata);
if (ret) {
@@ -358,18 +358,23 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
return ret;
}
+ if (pdata->dp >= 0)
+ dp = IPU_DP_FLOW_SYNC_BG;
+ ipu_crtc->plane[0] = ipu_plane_init(drm, ipu, pdata->dma[0], dp, 0,
+ DRM_PLANE_TYPE_PRIMARY);
+ if (IS_ERR(ipu_crtc->plane[0])) {
+ ret = PTR_ERR(ipu_crtc->plane[0]);
+ goto err_put_resources;
+ }
+
ret = imx_drm_add_crtc(drm, &ipu_crtc->base, &ipu_crtc->imx_crtc,
- &ipu_crtc_helper_funcs, ipu_crtc->dev->of_node);
+ &ipu_crtc->plane[0]->base, &ipu_crtc_helper_funcs,
+ ipu_crtc->dev->of_node);
if (ret) {
dev_err(ipu_crtc->dev, "adding crtc failed with %d.\n", ret);
goto err_put_resources;
}
- if (pdata->dp >= 0)
- dp = IPU_DP_FLOW_SYNC_BG;
- id = imx_drm_crtc_id(ipu_crtc->imx_crtc);
- ipu_crtc->plane[0] = ipu_plane_init(ipu_crtc->base.dev, ipu,
- pdata->dma[0], dp, BIT(id), true);
ret = ipu_plane_get_resources(ipu_crtc->plane[0]);
if (ret) {
dev_err(ipu_crtc->dev, "getting plane 0 resources failed with %d.\n",
@@ -379,10 +384,10 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
/* If this crtc is using the DP, add an overlay plane */
if (pdata->dp >= 0 && pdata->dma[1] > 0) {
- ipu_crtc->plane[1] = ipu_plane_init(ipu_crtc->base.dev, ipu,
- pdata->dma[1],
- IPU_DP_FLOW_SYNC_FG,
- BIT(id), false);
+ ipu_crtc->plane[1] = ipu_plane_init(drm, ipu, pdata->dma[1],
+ IPU_DP_FLOW_SYNC_FG,
+ drm_crtc_mask(&ipu_crtc->base),
+ DRM_PLANE_TYPE_OVERLAY);
if (IS_ERR(ipu_crtc->plane[1]))
ipu_crtc->plane[1] = NULL;
}
@@ -407,28 +412,6 @@ err_put_resources:
return ret;
}
-static struct device_node *ipu_drm_get_port_by_id(struct device_node *parent,
- int port_id)
-{
- struct device_node *port;
- int id, ret;
-
- port = of_get_child_by_name(parent, "port");
- while (port) {
- ret = of_property_read_u32(port, "reg", &id);
- if (!ret && id == port_id)
- return port;
-
- do {
- port = of_get_next_child(parent, port);
- if (!port)
- return NULL;
- } while (of_node_cmp(port->name, "port"));
- }
-
- return NULL;
-}
-
static int ipu_drm_bind(struct device *dev, struct device *master, void *data)
{
struct ipu_client_platformdata *pdata = dev->platform_data;
@@ -470,23 +453,11 @@ static const struct component_ops ipu_crtc_ops = {
static int ipu_drm_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct ipu_client_platformdata *pdata = dev->platform_data;
int ret;
if (!dev->platform_data)
return -EINVAL;
- if (!dev->of_node) {
- /* Associate crtc device with the corresponding DI port node */
- dev->of_node = ipu_drm_get_port_by_id(dev->parent->of_node,
- pdata->di + 2);
- if (!dev->of_node) {
- dev_err(dev, "missing port@%d node in %s\n",
- pdata->di + 2, dev->parent->of_node->full_name);
- return -ENODEV;
- }
- }
-
ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
if (ret)
return ret;
diff --git a/drivers/gpu/drm/imx/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3-plane.c
index 575f4c84388f..e2ff410bab74 100644
--- a/drivers/gpu/drm/imx/ipuv3-plane.c
+++ b/drivers/gpu/drm/imx/ipuv3-plane.c
@@ -381,7 +381,7 @@ static struct drm_plane_funcs ipu_plane_funcs = {
struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
int dma, int dp, unsigned int possible_crtcs,
- bool priv)
+ enum drm_plane_type type)
{
struct ipu_plane *ipu_plane;
int ret;
@@ -399,10 +399,9 @@ struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
ipu_plane->dma = dma;
ipu_plane->dp_flow = dp;
- ret = drm_plane_init(dev, &ipu_plane->base, possible_crtcs,
- &ipu_plane_funcs, ipu_plane_formats,
- ARRAY_SIZE(ipu_plane_formats),
- priv);
+ ret = drm_universal_plane_init(dev, &ipu_plane->base, possible_crtcs,
+ &ipu_plane_funcs, ipu_plane_formats,
+ ARRAY_SIZE(ipu_plane_formats), type);
if (ret) {
DRM_ERROR("failed to initialize plane\n");
kfree(ipu_plane);
diff --git a/drivers/gpu/drm/imx/ipuv3-plane.h b/drivers/gpu/drm/imx/ipuv3-plane.h
index 9b5eff18f5b8..3a443b413c60 100644
--- a/drivers/gpu/drm/imx/ipuv3-plane.h
+++ b/drivers/gpu/drm/imx/ipuv3-plane.h
@@ -34,7 +34,7 @@ struct ipu_plane {
struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
int dma, int dp, unsigned int possible_crtcs,
- bool priv);
+ enum drm_plane_type type);
/* Init IDMAC, DMFC, DP */
int ipu_plane_mode_set(struct ipu_plane *plane, struct drm_crtc *crtc,
diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c
index b4deb9cf9d71..2e9b9f1b5cd2 100644
--- a/drivers/gpu/drm/imx/parallel-display.c
+++ b/drivers/gpu/drm/imx/parallel-display.c
@@ -54,7 +54,11 @@ static int imx_pd_connector_get_modes(struct drm_connector *connector)
if (imxpd->panel && imxpd->panel->funcs &&
imxpd->panel->funcs->get_modes) {
+ struct drm_display_info *di = &connector->display_info;
+
num_modes = imxpd->panel->funcs->get_modes(imxpd->panel);
+ if (!imxpd->bus_format && di->num_bus_formats)
+ imxpd->bus_format = di->bus_formats[0];
if (num_modes > 0)
return num_modes;
}
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/core/device.h b/drivers/gpu/drm/nouveau/include/nvkm/core/device.h
index 8f760002e401..913192c94876 100644
--- a/drivers/gpu/drm/nouveau/include/nvkm/core/device.h
+++ b/drivers/gpu/drm/nouveau/include/nvkm/core/device.h
@@ -159,7 +159,6 @@ struct nvkm_device_func {
struct nvkm_device_quirk {
u8 tv_pin_mask;
u8 tv_gpio;
- bool War00C800_0;
};
struct nvkm_device_chip {
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/instmem.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/instmem.h
index 28bc202f9753..40f845e31272 100644
--- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/instmem.h
+++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/instmem.h
@@ -7,6 +7,7 @@ struct nvkm_instmem {
const struct nvkm_instmem_func *func;
struct nvkm_subdev subdev;
+ spinlock_t lock;
struct list_head list;
u32 reserved;
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
index 8b8332e46f24..d5e6938cc6bc 100644
--- a/drivers/gpu/drm/nouveau/nouveau_acpi.c
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
@@ -367,6 +367,7 @@ static int nouveau_rom_call(acpi_handle rom_handle, uint8_t *bios,
return -ENODEV;
}
obj = (union acpi_object *)buffer.pointer;
+ len = min(len, (int)obj->buffer.length);
memcpy(bios+offset, obj->buffer.pointer, len);
kfree(buffer.pointer);
return len;
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index db6bc6760545..64c8d932d5f1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -829,7 +829,6 @@ nouveau_finish_page_flip(struct nouveau_channel *chan,
struct drm_device *dev = drm->dev;
struct nouveau_page_flip_state *s;
unsigned long flags;
- int crtcid = -1;
spin_lock_irqsave(&dev->event_lock, flags);
@@ -841,15 +840,19 @@ nouveau_finish_page_flip(struct nouveau_channel *chan,
s = list_first_entry(&fctx->flip, struct nouveau_page_flip_state, head);
if (s->event) {
- /* Vblank timestamps/counts are only correct on >= NV-50 */
- if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA)
- crtcid = s->crtc;
+ if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) {
+ drm_arm_vblank_event(dev, s->crtc, s->event);
+ } else {
+ drm_send_vblank_event(dev, s->crtc, s->event);
- drm_send_vblank_event(dev, crtcid, s->event);
+ /* Give up ownership of vblank for page-flipped crtc */
+ drm_vblank_put(dev, s->crtc);
+ }
+ }
+ else {
+ /* Give up ownership of vblank for page-flipped crtc */
+ drm_vblank_put(dev, s->crtc);
}
-
- /* Give up ownership of vblank for page-flipped crtc */
- drm_vblank_put(dev, s->crtc);
list_del(&s->head);
if (ps)
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.h b/drivers/gpu/drm/nouveau/nouveau_drm.h
index 3050042e6c6d..a02813e994ec 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.h
@@ -39,6 +39,7 @@
#include <nvif/client.h>
#include <nvif/device.h>
+#include <nvif/ioctl.h>
#include <drmP.h>
@@ -65,9 +66,10 @@ struct nouveau_drm_tile {
};
enum nouveau_drm_object_route {
- NVDRM_OBJECT_NVIF = 0,
+ NVDRM_OBJECT_NVIF = NVIF_IOCTL_V0_OWNER_NVIF,
NVDRM_OBJECT_USIF,
NVDRM_OBJECT_ABI16,
+ NVDRM_OBJECT_ANY = NVIF_IOCTL_V0_OWNER_ANY,
};
enum nouveau_drm_notify_route {
diff --git a/drivers/gpu/drm/nouveau/nouveau_usif.c b/drivers/gpu/drm/nouveau/nouveau_usif.c
index 89dc4ce63490..6ae1b3494bcd 100644
--- a/drivers/gpu/drm/nouveau/nouveau_usif.c
+++ b/drivers/gpu/drm/nouveau/nouveau_usif.c
@@ -313,7 +313,10 @@ usif_ioctl(struct drm_file *filp, void __user *user, u32 argc)
if (nvif_unpack(argv->v0, 0, 0, true)) {
/* block access to objects not created via this interface */
owner = argv->v0.owner;
- argv->v0.owner = NVDRM_OBJECT_USIF;
+ if (argv->v0.object == 0ULL)
+ argv->v0.owner = NVDRM_OBJECT_ANY; /* except client */
+ else
+ argv->v0.owner = NVDRM_OBJECT_USIF;
} else
goto done;
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/pci.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/pci.c
index e3c783d0e2ab..62ad0300cfa5 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/pci.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/pci.c
@@ -259,12 +259,6 @@ nvkm_device_pci_10de_0df4[] = {
};
static const struct nvkm_device_pci_vendor
-nvkm_device_pci_10de_0fcd[] = {
- { 0x17aa, 0x3801, NULL, { .War00C800_0 = true } }, /* Lenovo Y510P */
- {}
-};
-
-static const struct nvkm_device_pci_vendor
nvkm_device_pci_10de_0fd2[] = {
{ 0x1028, 0x0595, "GeForce GT 640M LE" },
{ 0x1028, 0x05b2, "GeForce GT 640M LE" },
@@ -684,7 +678,6 @@ nvkm_device_pci_10de_1189[] = {
static const struct nvkm_device_pci_vendor
nvkm_device_pci_10de_1199[] = {
{ 0x1458, 0xd001, "GeForce GTX 760" },
- { 0x1462, 0x1106, "GeForce GTX 780M", { .War00C800_0 = true } }, /* Medion Erazer X7827 */
{}
};
@@ -695,14 +688,6 @@ nvkm_device_pci_10de_11e3[] = {
};
static const struct nvkm_device_pci_vendor
-nvkm_device_pci_10de_11fc[] = {
- { 0x1179, 0x0001, NULL, { .War00C800_0 = true } }, /* Toshiba Tecra W50 */
- { 0x17aa, 0x2211, NULL, { .War00C800_0 = true } }, /* Lenovo W541 */
- { 0x17aa, 0x221e, NULL, { .War00C800_0 = true } }, /* Lenovo W541 */
- {}
-};
-
-static const struct nvkm_device_pci_vendor
nvkm_device_pci_10de_1247[] = {
{ 0x1043, 0x212a, "GeForce GT 635M" },
{ 0x1043, 0x212b, "GeForce GT 635M" },
@@ -1356,7 +1341,7 @@ nvkm_device_pci_10de[] = {
{ 0x0fc6, "GeForce GTX 650" },
{ 0x0fc8, "GeForce GT 740" },
{ 0x0fc9, "GeForce GT 730" },
- { 0x0fcd, "GeForce GT 755M", nvkm_device_pci_10de_0fcd },
+ { 0x0fcd, "GeForce GT 755M" },
{ 0x0fce, "GeForce GT 640M LE" },
{ 0x0fd1, "GeForce GT 650M" },
{ 0x0fd2, "GeForce GT 640M", nvkm_device_pci_10de_0fd2 },
@@ -1490,7 +1475,7 @@ nvkm_device_pci_10de[] = {
{ 0x11e2, "GeForce GTX 765M" },
{ 0x11e3, "GeForce GTX 760M", nvkm_device_pci_10de_11e3 },
{ 0x11fa, "Quadro K4000" },
- { 0x11fc, "Quadro K2100M", nvkm_device_pci_10de_11fc },
+ { 0x11fc, "Quadro K2100M" },
{ 0x1200, "GeForce GTX 560 Ti" },
{ 0x1201, "GeForce GTX 560" },
{ 0x1203, "GeForce GTX 460 SE v2" },
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf117.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf117.c
index b5b875928aba..74de7a96c22a 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf117.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf117.c
@@ -207,6 +207,8 @@ gf117_grctx_generate_attrib(struct gf100_grctx *info)
const u32 b = beta * gr->ppc_tpc_nr[gpc][ppc];
const u32 t = timeslice_mode;
const u32 o = PPC_UNIT(gpc, ppc, 0);
+ if (!(gr->ppc_mask[gpc] & (1 << ppc)))
+ continue;
mmio_skip(info, o + 0xc0, (t << 28) | (b << 16) | ++bo);
mmio_wr32(info, o + 0xc0, (t << 28) | (b << 16) | --bo);
bo += grctx->attrib_nr_max * gr->ppc_tpc_nr[gpc][ppc];
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpc.fuc b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpc.fuc
index 194afe910d21..7dacb3cc0668 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpc.fuc
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpc.fuc
@@ -52,10 +52,12 @@ mmio_list_base:
#endif
#ifdef INCLUDE_CODE
+#define gpc_addr(reg,addr) /*
+*/ imm32(reg,addr) /*
+*/ or reg NV_PGRAPH_GPCX_GPCCS_MMIO_CTRL_BASE_ENABLE
#define gpc_wr32(addr,reg) /*
+*/ gpc_addr($r14,addr) /*
*/ mov b32 $r15 reg /*
-*/ imm32($r14, addr) /*
-*/ or $r14 NV_PGRAPH_GPCX_GPCCS_MMIO_CTRL_BASE_ENABLE /*
*/ call(nv_wr32)
// reports an exception to the host
@@ -161,7 +163,7 @@ init:
#if NV_PGRAPH_GPCX_UNK__SIZE > 0
// figure out which, and how many, UNKs are actually present
- imm32($r14, 0x500c30)
+ gpc_addr($r14, 0x500c30)
clear b32 $r2
clear b32 $r3
clear b32 $r4
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3.h
index 64d07df4b8b1..bb820ff28621 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgf117.fuc3.h
@@ -314,7 +314,7 @@ uint32_t gf117_grgpc_code[] = {
0x03f01200,
0x0002d000,
0x17f104bd,
- 0x10fe0542,
+ 0x10fe0545,
0x0007f100,
0x0003f007,
0xbd0000d0,
@@ -338,184 +338,184 @@ uint32_t gf117_grgpc_code[] = {
0x02d00103,
0xf104bd00,
0xf00c30e7,
- 0x24bd50e3,
- 0x44bd34bd,
-/* 0x0430: init_unk_loop */
- 0xb06821f4,
- 0x0bf400f6,
- 0x01f7f00f,
- 0xfd04f2bb,
- 0x30b6054f,
-/* 0x0445: init_unk_next */
- 0x0120b601,
- 0xb004e0b6,
- 0x1bf40126,
-/* 0x0451: init_unk_done */
- 0x070380e2,
- 0xf1080480,
- 0xf0010027,
- 0x22cf0223,
- 0x9534bd00,
- 0x07f10825,
- 0x03f0c000,
- 0x0005d001,
- 0x07f104bd,
- 0x03f0c100,
- 0x0005d001,
- 0x0e9804bd,
- 0x010f9800,
- 0x015021f5,
- 0xbb002fbb,
- 0x0e98003f,
- 0x020f9801,
- 0x015021f5,
- 0xfd050e98,
- 0x2ebb00ef,
- 0x003ebb00,
- 0x98020e98,
- 0x21f5030f,
- 0x0e980150,
- 0x00effd07,
- 0xbb002ebb,
- 0x35b6003e,
- 0x0007f102,
- 0x0103f0d3,
- 0xbd0003d0,
- 0x0825b604,
- 0xb60635b6,
- 0x30b60120,
- 0x0824b601,
- 0xb90834b6,
- 0x21f5022f,
- 0x2fbb02d3,
- 0x003fbb00,
- 0x010007f1,
- 0xd00203f0,
+ 0xe5f050e3,
+ 0xbd24bd01,
+/* 0x0433: init_unk_loop */
+ 0xf444bd34,
+ 0xf6b06821,
+ 0x0f0bf400,
+ 0xbb01f7f0,
+ 0x4ffd04f2,
+ 0x0130b605,
+/* 0x0448: init_unk_next */
+ 0xb60120b6,
+ 0x26b004e0,
+ 0xe21bf401,
+/* 0x0454: init_unk_done */
+ 0x80070380,
+ 0x27f10804,
+ 0x23f00100,
+ 0x0022cf02,
+ 0x259534bd,
+ 0x0007f108,
+ 0x0103f0c0,
+ 0xbd0005d0,
+ 0x0007f104,
+ 0x0103f0c1,
+ 0xbd0005d0,
+ 0x000e9804,
+ 0xf5010f98,
+ 0xbb015021,
+ 0x3fbb002f,
+ 0x010e9800,
+ 0xf5020f98,
+ 0x98015021,
+ 0xeffd050e,
+ 0x002ebb00,
+ 0x98003ebb,
+ 0x0f98020e,
+ 0x5021f503,
+ 0x070e9801,
+ 0xbb00effd,
+ 0x3ebb002e,
+ 0x0235b600,
+ 0xd30007f1,
+ 0xd00103f0,
0x04bd0003,
- 0x29f024bd,
- 0x0007f11f,
- 0x0203f008,
- 0xbd0002d0,
-/* 0x0505: main */
- 0x0031f404,
- 0xf00028f4,
- 0x21f424d7,
- 0xf401f439,
- 0xf404e4b0,
- 0x81fe1e18,
- 0x0627f001,
- 0x12fd20bd,
- 0x01e4b604,
- 0xfe051efd,
- 0x21f50018,
- 0x0ef405fa,
-/* 0x0535: main_not_ctx_xfer */
- 0x10ef94d3,
- 0xf501f5f0,
- 0xf4037e21,
-/* 0x0542: ih */
- 0x80f9c60e,
- 0xf90188fe,
- 0xf990f980,
- 0xf9b0f9a0,
- 0xf9e0f9d0,
- 0xf104bdf0,
- 0xf00200a7,
- 0xaacf00a3,
- 0x04abc400,
- 0xf02c0bf4,
- 0xe7f124d7,
- 0xe3f01a00,
- 0x00eecf00,
- 0x1900f7f1,
- 0xcf00f3f0,
- 0x21f400ff,
- 0x01e7f004,
- 0x1d0007f1,
- 0xd00003f0,
- 0x04bd000e,
-/* 0x0590: ih_no_fifo */
- 0x010007f1,
- 0xd00003f0,
- 0x04bd000a,
- 0xe0fcf0fc,
- 0xb0fcd0fc,
- 0x90fca0fc,
- 0x88fe80fc,
- 0xf480fc00,
- 0x01f80032,
-/* 0x05b4: hub_barrier_done */
- 0x9801f7f0,
- 0xfebb040e,
- 0x02ffb904,
- 0x9418e7f1,
- 0xf440e3f0,
- 0x00f89d21,
-/* 0x05cc: ctx_redswitch */
- 0xf120f7f0,
+ 0xb60825b6,
+ 0x20b60635,
+ 0x0130b601,
+ 0xb60824b6,
+ 0x2fb90834,
+ 0xd321f502,
+ 0x002fbb02,
+ 0xf1003fbb,
+ 0xf0010007,
+ 0x03d00203,
+ 0xbd04bd00,
+ 0x1f29f024,
+ 0x080007f1,
+ 0xd00203f0,
+ 0x04bd0002,
+/* 0x0508: main */
+ 0xf40031f4,
+ 0xd7f00028,
+ 0x3921f424,
+ 0xb0f401f4,
+ 0x18f404e4,
+ 0x0181fe1e,
+ 0xbd0627f0,
+ 0x0412fd20,
+ 0xfd01e4b6,
+ 0x18fe051e,
+ 0xfd21f500,
+ 0xd30ef405,
+/* 0x0538: main_not_ctx_xfer */
+ 0xf010ef94,
+ 0x21f501f5,
+ 0x0ef4037e,
+/* 0x0545: ih */
+ 0xfe80f9c6,
+ 0x80f90188,
+ 0xa0f990f9,
+ 0xd0f9b0f9,
+ 0xf0f9e0f9,
+ 0xa7f104bd,
+ 0xa3f00200,
+ 0x00aacf00,
+ 0xf404abc4,
+ 0xd7f02c0b,
+ 0x00e7f124,
+ 0x00e3f01a,
+ 0xf100eecf,
+ 0xf01900f7,
+ 0xffcf00f3,
+ 0x0421f400,
+ 0xf101e7f0,
+ 0xf01d0007,
+ 0x0ed00003,
+/* 0x0593: ih_no_fifo */
+ 0xf104bd00,
+ 0xf0010007,
+ 0x0ad00003,
+ 0xfc04bd00,
+ 0xfce0fcf0,
+ 0xfcb0fcd0,
+ 0xfc90fca0,
+ 0x0088fe80,
+ 0x32f480fc,
+/* 0x05b7: hub_barrier_done */
+ 0xf001f800,
+ 0x0e9801f7,
+ 0x04febb04,
+ 0xf102ffb9,
+ 0xf09418e7,
+ 0x21f440e3,
+/* 0x05cf: ctx_redswitch */
+ 0xf000f89d,
+ 0x07f120f7,
+ 0x03f08500,
+ 0x000fd001,
+ 0xe7f004bd,
+/* 0x05e1: ctx_redswitch_delay */
+ 0x01e2b608,
+ 0xf1fd1bf4,
+ 0xf10800f5,
+ 0xf10200f5,
0xf0850007,
0x0fd00103,
- 0xf004bd00,
-/* 0x05de: ctx_redswitch_delay */
- 0xe2b608e7,
- 0xfd1bf401,
- 0x0800f5f1,
- 0x0200f5f1,
- 0x850007f1,
- 0xd00103f0,
- 0x04bd000f,
-/* 0x05fa: ctx_xfer */
- 0x07f100f8,
- 0x03f08100,
- 0x000fd002,
- 0x11f404bd,
- 0xcc21f507,
-/* 0x060d: ctx_xfer_not_load */
- 0x6a21f505,
- 0xf124bd02,
- 0xf047fc07,
- 0x02d00203,
- 0xf004bd00,
- 0x20b6012c,
- 0xfc07f103,
- 0x0203f04a,
- 0xbd0002d0,
- 0x01acf004,
- 0xf102a5f0,
- 0xf00000b7,
- 0x0c9850b3,
- 0x0fc4b604,
- 0x9800bcbb,
- 0x0d98000c,
- 0x00e7f001,
- 0x016f21f5,
- 0xf101acf0,
- 0xf04000b7,
- 0x0c9850b3,
- 0x0fc4b604,
- 0x9800bcbb,
- 0x0d98010c,
- 0x060f9802,
- 0x0800e7f1,
- 0x016f21f5,
+ 0xf804bd00,
+/* 0x05fd: ctx_xfer */
+ 0x0007f100,
+ 0x0203f081,
+ 0xbd000fd0,
+ 0x0711f404,
+ 0x05cf21f5,
+/* 0x0610: ctx_xfer_not_load */
+ 0x026a21f5,
+ 0x07f124bd,
+ 0x03f047fc,
+ 0x0002d002,
+ 0x2cf004bd,
+ 0x0320b601,
+ 0x4afc07f1,
+ 0xd00203f0,
+ 0x04bd0002,
0xf001acf0,
- 0xb7f104a5,
- 0xb3f03000,
+ 0xb7f102a5,
+ 0xb3f00000,
0x040c9850,
0xbb0fc4b6,
0x0c9800bc,
- 0x030d9802,
- 0xf1080f98,
- 0xf50200e7,
- 0xf5016f21,
- 0xf4025e21,
- 0x12f40601,
-/* 0x06a9: ctx_xfer_post */
- 0x7f21f507,
-/* 0x06ad: ctx_xfer_done */
- 0xb421f502,
- 0x0000f805,
- 0x00000000,
+ 0x010d9800,
+ 0xf500e7f0,
+ 0xf0016f21,
+ 0xb7f101ac,
+ 0xb3f04000,
+ 0x040c9850,
+ 0xbb0fc4b6,
+ 0x0c9800bc,
+ 0x020d9801,
+ 0xf1060f98,
+ 0xf50800e7,
+ 0xf0016f21,
+ 0xa5f001ac,
+ 0x00b7f104,
+ 0x50b3f030,
+ 0xb6040c98,
+ 0xbcbb0fc4,
+ 0x020c9800,
+ 0x98030d98,
+ 0xe7f1080f,
+ 0x21f50200,
+ 0x21f5016f,
+ 0x01f4025e,
+ 0x0712f406,
+/* 0x06ac: ctx_xfer_post */
+ 0x027f21f5,
+/* 0x06b0: ctx_xfer_done */
+ 0x05b721f5,
+ 0x000000f8,
0x00000000,
0x00000000,
0x00000000,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3.h
index 2f596433c222..911976d20940 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk104.fuc3.h
@@ -314,7 +314,7 @@ uint32_t gk104_grgpc_code[] = {
0x03f01200,
0x0002d000,
0x17f104bd,
- 0x10fe0542,
+ 0x10fe0545,
0x0007f100,
0x0003f007,
0xbd0000d0,
@@ -338,184 +338,184 @@ uint32_t gk104_grgpc_code[] = {
0x02d00103,
0xf104bd00,
0xf00c30e7,
- 0x24bd50e3,
- 0x44bd34bd,
-/* 0x0430: init_unk_loop */
- 0xb06821f4,
- 0x0bf400f6,
- 0x01f7f00f,
- 0xfd04f2bb,
- 0x30b6054f,
-/* 0x0445: init_unk_next */
- 0x0120b601,
- 0xb004e0b6,
- 0x1bf40126,
-/* 0x0451: init_unk_done */
- 0x070380e2,
- 0xf1080480,
- 0xf0010027,
- 0x22cf0223,
- 0x9534bd00,
- 0x07f10825,
- 0x03f0c000,
- 0x0005d001,
- 0x07f104bd,
- 0x03f0c100,
- 0x0005d001,
- 0x0e9804bd,
- 0x010f9800,
- 0x015021f5,
- 0xbb002fbb,
- 0x0e98003f,
- 0x020f9801,
- 0x015021f5,
- 0xfd050e98,
- 0x2ebb00ef,
- 0x003ebb00,
- 0x98020e98,
- 0x21f5030f,
- 0x0e980150,
- 0x00effd07,
- 0xbb002ebb,
- 0x35b6003e,
- 0x0007f102,
- 0x0103f0d3,
- 0xbd0003d0,
- 0x0825b604,
- 0xb60635b6,
- 0x30b60120,
- 0x0824b601,
- 0xb90834b6,
- 0x21f5022f,
- 0x2fbb02d3,
- 0x003fbb00,
- 0x010007f1,
- 0xd00203f0,
+ 0xe5f050e3,
+ 0xbd24bd01,
+/* 0x0433: init_unk_loop */
+ 0xf444bd34,
+ 0xf6b06821,
+ 0x0f0bf400,
+ 0xbb01f7f0,
+ 0x4ffd04f2,
+ 0x0130b605,
+/* 0x0448: init_unk_next */
+ 0xb60120b6,
+ 0x26b004e0,
+ 0xe21bf401,
+/* 0x0454: init_unk_done */
+ 0x80070380,
+ 0x27f10804,
+ 0x23f00100,
+ 0x0022cf02,
+ 0x259534bd,
+ 0x0007f108,
+ 0x0103f0c0,
+ 0xbd0005d0,
+ 0x0007f104,
+ 0x0103f0c1,
+ 0xbd0005d0,
+ 0x000e9804,
+ 0xf5010f98,
+ 0xbb015021,
+ 0x3fbb002f,
+ 0x010e9800,
+ 0xf5020f98,
+ 0x98015021,
+ 0xeffd050e,
+ 0x002ebb00,
+ 0x98003ebb,
+ 0x0f98020e,
+ 0x5021f503,
+ 0x070e9801,
+ 0xbb00effd,
+ 0x3ebb002e,
+ 0x0235b600,
+ 0xd30007f1,
+ 0xd00103f0,
0x04bd0003,
- 0x29f024bd,
- 0x0007f11f,
- 0x0203f008,
- 0xbd0002d0,
-/* 0x0505: main */
- 0x0031f404,
- 0xf00028f4,
- 0x21f424d7,
- 0xf401f439,
- 0xf404e4b0,
- 0x81fe1e18,
- 0x0627f001,
- 0x12fd20bd,
- 0x01e4b604,
- 0xfe051efd,
- 0x21f50018,
- 0x0ef405fa,
-/* 0x0535: main_not_ctx_xfer */
- 0x10ef94d3,
- 0xf501f5f0,
- 0xf4037e21,
-/* 0x0542: ih */
- 0x80f9c60e,
- 0xf90188fe,
- 0xf990f980,
- 0xf9b0f9a0,
- 0xf9e0f9d0,
- 0xf104bdf0,
- 0xf00200a7,
- 0xaacf00a3,
- 0x04abc400,
- 0xf02c0bf4,
- 0xe7f124d7,
- 0xe3f01a00,
- 0x00eecf00,
- 0x1900f7f1,
- 0xcf00f3f0,
- 0x21f400ff,
- 0x01e7f004,
- 0x1d0007f1,
- 0xd00003f0,
- 0x04bd000e,
-/* 0x0590: ih_no_fifo */
- 0x010007f1,
- 0xd00003f0,
- 0x04bd000a,
- 0xe0fcf0fc,
- 0xb0fcd0fc,
- 0x90fca0fc,
- 0x88fe80fc,
- 0xf480fc00,
- 0x01f80032,
-/* 0x05b4: hub_barrier_done */
- 0x9801f7f0,
- 0xfebb040e,
- 0x02ffb904,
- 0x9418e7f1,
- 0xf440e3f0,
- 0x00f89d21,
-/* 0x05cc: ctx_redswitch */
- 0xf120f7f0,
+ 0xb60825b6,
+ 0x20b60635,
+ 0x0130b601,
+ 0xb60824b6,
+ 0x2fb90834,
+ 0xd321f502,
+ 0x002fbb02,
+ 0xf1003fbb,
+ 0xf0010007,
+ 0x03d00203,
+ 0xbd04bd00,
+ 0x1f29f024,
+ 0x080007f1,
+ 0xd00203f0,
+ 0x04bd0002,
+/* 0x0508: main */
+ 0xf40031f4,
+ 0xd7f00028,
+ 0x3921f424,
+ 0xb0f401f4,
+ 0x18f404e4,
+ 0x0181fe1e,
+ 0xbd0627f0,
+ 0x0412fd20,
+ 0xfd01e4b6,
+ 0x18fe051e,
+ 0xfd21f500,
+ 0xd30ef405,
+/* 0x0538: main_not_ctx_xfer */
+ 0xf010ef94,
+ 0x21f501f5,
+ 0x0ef4037e,
+/* 0x0545: ih */
+ 0xfe80f9c6,
+ 0x80f90188,
+ 0xa0f990f9,
+ 0xd0f9b0f9,
+ 0xf0f9e0f9,
+ 0xa7f104bd,
+ 0xa3f00200,
+ 0x00aacf00,
+ 0xf404abc4,
+ 0xd7f02c0b,
+ 0x00e7f124,
+ 0x00e3f01a,
+ 0xf100eecf,
+ 0xf01900f7,
+ 0xffcf00f3,
+ 0x0421f400,
+ 0xf101e7f0,
+ 0xf01d0007,
+ 0x0ed00003,
+/* 0x0593: ih_no_fifo */
+ 0xf104bd00,
+ 0xf0010007,
+ 0x0ad00003,
+ 0xfc04bd00,
+ 0xfce0fcf0,
+ 0xfcb0fcd0,
+ 0xfc90fca0,
+ 0x0088fe80,
+ 0x32f480fc,
+/* 0x05b7: hub_barrier_done */
+ 0xf001f800,
+ 0x0e9801f7,
+ 0x04febb04,
+ 0xf102ffb9,
+ 0xf09418e7,
+ 0x21f440e3,
+/* 0x05cf: ctx_redswitch */
+ 0xf000f89d,
+ 0x07f120f7,
+ 0x03f08500,
+ 0x000fd001,
+ 0xe7f004bd,
+/* 0x05e1: ctx_redswitch_delay */
+ 0x01e2b608,
+ 0xf1fd1bf4,
+ 0xf10800f5,
+ 0xf10200f5,
0xf0850007,
0x0fd00103,
- 0xf004bd00,
-/* 0x05de: ctx_redswitch_delay */
- 0xe2b608e7,
- 0xfd1bf401,
- 0x0800f5f1,
- 0x0200f5f1,
- 0x850007f1,
- 0xd00103f0,
- 0x04bd000f,
-/* 0x05fa: ctx_xfer */
- 0x07f100f8,
- 0x03f08100,
- 0x000fd002,
- 0x11f404bd,
- 0xcc21f507,
-/* 0x060d: ctx_xfer_not_load */
- 0x6a21f505,
- 0xf124bd02,
- 0xf047fc07,
- 0x02d00203,
- 0xf004bd00,
- 0x20b6012c,
- 0xfc07f103,
- 0x0203f04a,
- 0xbd0002d0,
- 0x01acf004,
- 0xf102a5f0,
- 0xf00000b7,
- 0x0c9850b3,
- 0x0fc4b604,
- 0x9800bcbb,
- 0x0d98000c,
- 0x00e7f001,
- 0x016f21f5,
- 0xf101acf0,
- 0xf04000b7,
- 0x0c9850b3,
- 0x0fc4b604,
- 0x9800bcbb,
- 0x0d98010c,
- 0x060f9802,
- 0x0800e7f1,
- 0x016f21f5,
+ 0xf804bd00,
+/* 0x05fd: ctx_xfer */
+ 0x0007f100,
+ 0x0203f081,
+ 0xbd000fd0,
+ 0x0711f404,
+ 0x05cf21f5,
+/* 0x0610: ctx_xfer_not_load */
+ 0x026a21f5,
+ 0x07f124bd,
+ 0x03f047fc,
+ 0x0002d002,
+ 0x2cf004bd,
+ 0x0320b601,
+ 0x4afc07f1,
+ 0xd00203f0,
+ 0x04bd0002,
0xf001acf0,
- 0xb7f104a5,
- 0xb3f03000,
+ 0xb7f102a5,
+ 0xb3f00000,
0x040c9850,
0xbb0fc4b6,
0x0c9800bc,
- 0x030d9802,
- 0xf1080f98,
- 0xf50200e7,
- 0xf5016f21,
- 0xf4025e21,
- 0x12f40601,
-/* 0x06a9: ctx_xfer_post */
- 0x7f21f507,
-/* 0x06ad: ctx_xfer_done */
- 0xb421f502,
- 0x0000f805,
- 0x00000000,
+ 0x010d9800,
+ 0xf500e7f0,
+ 0xf0016f21,
+ 0xb7f101ac,
+ 0xb3f04000,
+ 0x040c9850,
+ 0xbb0fc4b6,
+ 0x0c9800bc,
+ 0x020d9801,
+ 0xf1060f98,
+ 0xf50800e7,
+ 0xf0016f21,
+ 0xa5f001ac,
+ 0x00b7f104,
+ 0x50b3f030,
+ 0xb6040c98,
+ 0xbcbb0fc4,
+ 0x020c9800,
+ 0x98030d98,
+ 0xe7f1080f,
+ 0x21f50200,
+ 0x21f5016f,
+ 0x01f4025e,
+ 0x0712f406,
+/* 0x06ac: ctx_xfer_post */
+ 0x027f21f5,
+/* 0x06b0: ctx_xfer_done */
+ 0x05b721f5,
+ 0x000000f8,
0x00000000,
0x00000000,
0x00000000,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3.h
index ee8e54db8fc9..1c6e11b05df2 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk110.fuc3.h
@@ -314,7 +314,7 @@ uint32_t gk110_grgpc_code[] = {
0x03f01200,
0x0002d000,
0x17f104bd,
- 0x10fe0542,
+ 0x10fe0545,
0x0007f100,
0x0003f007,
0xbd0000d0,
@@ -338,184 +338,184 @@ uint32_t gk110_grgpc_code[] = {
0x02d00103,
0xf104bd00,
0xf00c30e7,
- 0x24bd50e3,
- 0x44bd34bd,
-/* 0x0430: init_unk_loop */
- 0xb06821f4,
- 0x0bf400f6,
- 0x01f7f00f,
- 0xfd04f2bb,
- 0x30b6054f,
-/* 0x0445: init_unk_next */
- 0x0120b601,
- 0xb004e0b6,
- 0x1bf40226,
-/* 0x0451: init_unk_done */
- 0x070380e2,
- 0xf1080480,
- 0xf0010027,
- 0x22cf0223,
- 0x9534bd00,
- 0x07f10825,
- 0x03f0c000,
- 0x0005d001,
- 0x07f104bd,
- 0x03f0c100,
- 0x0005d001,
- 0x0e9804bd,
- 0x010f9800,
- 0x015021f5,
- 0xbb002fbb,
- 0x0e98003f,
- 0x020f9801,
- 0x015021f5,
- 0xfd050e98,
- 0x2ebb00ef,
- 0x003ebb00,
- 0x98020e98,
- 0x21f5030f,
- 0x0e980150,
- 0x00effd07,
- 0xbb002ebb,
- 0x35b6003e,
- 0x0007f102,
- 0x0103f0d3,
- 0xbd0003d0,
- 0x0825b604,
- 0xb60635b6,
- 0x30b60120,
- 0x0824b601,
- 0xb90834b6,
- 0x21f5022f,
- 0x2fbb02d3,
- 0x003fbb00,
- 0x010007f1,
- 0xd00203f0,
+ 0xe5f050e3,
+ 0xbd24bd01,
+/* 0x0433: init_unk_loop */
+ 0xf444bd34,
+ 0xf6b06821,
+ 0x0f0bf400,
+ 0xbb01f7f0,
+ 0x4ffd04f2,
+ 0x0130b605,
+/* 0x0448: init_unk_next */
+ 0xb60120b6,
+ 0x26b004e0,
+ 0xe21bf402,
+/* 0x0454: init_unk_done */
+ 0x80070380,
+ 0x27f10804,
+ 0x23f00100,
+ 0x0022cf02,
+ 0x259534bd,
+ 0x0007f108,
+ 0x0103f0c0,
+ 0xbd0005d0,
+ 0x0007f104,
+ 0x0103f0c1,
+ 0xbd0005d0,
+ 0x000e9804,
+ 0xf5010f98,
+ 0xbb015021,
+ 0x3fbb002f,
+ 0x010e9800,
+ 0xf5020f98,
+ 0x98015021,
+ 0xeffd050e,
+ 0x002ebb00,
+ 0x98003ebb,
+ 0x0f98020e,
+ 0x5021f503,
+ 0x070e9801,
+ 0xbb00effd,
+ 0x3ebb002e,
+ 0x0235b600,
+ 0xd30007f1,
+ 0xd00103f0,
0x04bd0003,
- 0x29f024bd,
- 0x0007f11f,
- 0x0203f030,
- 0xbd0002d0,
-/* 0x0505: main */
- 0x0031f404,
- 0xf00028f4,
- 0x21f424d7,
- 0xf401f439,
- 0xf404e4b0,
- 0x81fe1e18,
- 0x0627f001,
- 0x12fd20bd,
- 0x01e4b604,
- 0xfe051efd,
- 0x21f50018,
- 0x0ef405fa,
-/* 0x0535: main_not_ctx_xfer */
- 0x10ef94d3,
- 0xf501f5f0,
- 0xf4037e21,
-/* 0x0542: ih */
- 0x80f9c60e,
- 0xf90188fe,
- 0xf990f980,
- 0xf9b0f9a0,
- 0xf9e0f9d0,
- 0xf104bdf0,
- 0xf00200a7,
- 0xaacf00a3,
- 0x04abc400,
- 0xf02c0bf4,
- 0xe7f124d7,
- 0xe3f01a00,
- 0x00eecf00,
- 0x1900f7f1,
- 0xcf00f3f0,
- 0x21f400ff,
- 0x01e7f004,
- 0x1d0007f1,
- 0xd00003f0,
- 0x04bd000e,
-/* 0x0590: ih_no_fifo */
- 0x010007f1,
- 0xd00003f0,
- 0x04bd000a,
- 0xe0fcf0fc,
- 0xb0fcd0fc,
- 0x90fca0fc,
- 0x88fe80fc,
- 0xf480fc00,
- 0x01f80032,
-/* 0x05b4: hub_barrier_done */
- 0x9801f7f0,
- 0xfebb040e,
- 0x02ffb904,
- 0x9418e7f1,
- 0xf440e3f0,
- 0x00f89d21,
-/* 0x05cc: ctx_redswitch */
- 0xf120f7f0,
+ 0xb60825b6,
+ 0x20b60635,
+ 0x0130b601,
+ 0xb60824b6,
+ 0x2fb90834,
+ 0xd321f502,
+ 0x002fbb02,
+ 0xf1003fbb,
+ 0xf0010007,
+ 0x03d00203,
+ 0xbd04bd00,
+ 0x1f29f024,
+ 0x300007f1,
+ 0xd00203f0,
+ 0x04bd0002,
+/* 0x0508: main */
+ 0xf40031f4,
+ 0xd7f00028,
+ 0x3921f424,
+ 0xb0f401f4,
+ 0x18f404e4,
+ 0x0181fe1e,
+ 0xbd0627f0,
+ 0x0412fd20,
+ 0xfd01e4b6,
+ 0x18fe051e,
+ 0xfd21f500,
+ 0xd30ef405,
+/* 0x0538: main_not_ctx_xfer */
+ 0xf010ef94,
+ 0x21f501f5,
+ 0x0ef4037e,
+/* 0x0545: ih */
+ 0xfe80f9c6,
+ 0x80f90188,
+ 0xa0f990f9,
+ 0xd0f9b0f9,
+ 0xf0f9e0f9,
+ 0xa7f104bd,
+ 0xa3f00200,
+ 0x00aacf00,
+ 0xf404abc4,
+ 0xd7f02c0b,
+ 0x00e7f124,
+ 0x00e3f01a,
+ 0xf100eecf,
+ 0xf01900f7,
+ 0xffcf00f3,
+ 0x0421f400,
+ 0xf101e7f0,
+ 0xf01d0007,
+ 0x0ed00003,
+/* 0x0593: ih_no_fifo */
+ 0xf104bd00,
+ 0xf0010007,
+ 0x0ad00003,
+ 0xfc04bd00,
+ 0xfce0fcf0,
+ 0xfcb0fcd0,
+ 0xfc90fca0,
+ 0x0088fe80,
+ 0x32f480fc,
+/* 0x05b7: hub_barrier_done */
+ 0xf001f800,
+ 0x0e9801f7,
+ 0x04febb04,
+ 0xf102ffb9,
+ 0xf09418e7,
+ 0x21f440e3,
+/* 0x05cf: ctx_redswitch */
+ 0xf000f89d,
+ 0x07f120f7,
+ 0x03f08500,
+ 0x000fd001,
+ 0xe7f004bd,
+/* 0x05e1: ctx_redswitch_delay */
+ 0x01e2b608,
+ 0xf1fd1bf4,
+ 0xf10800f5,
+ 0xf10200f5,
0xf0850007,
0x0fd00103,
- 0xf004bd00,
-/* 0x05de: ctx_redswitch_delay */
- 0xe2b608e7,
- 0xfd1bf401,
- 0x0800f5f1,
- 0x0200f5f1,
- 0x850007f1,
- 0xd00103f0,
- 0x04bd000f,
-/* 0x05fa: ctx_xfer */
- 0x07f100f8,
- 0x03f08100,
- 0x000fd002,
- 0x11f404bd,
- 0xcc21f507,
-/* 0x060d: ctx_xfer_not_load */
- 0x6a21f505,
- 0xf124bd02,
- 0xf047fc07,
- 0x02d00203,
- 0xf004bd00,
- 0x20b6012c,
- 0xfc07f103,
- 0x0203f04a,
- 0xbd0002d0,
- 0x01acf004,
- 0xf102a5f0,
- 0xf00000b7,
- 0x0c9850b3,
- 0x0fc4b604,
- 0x9800bcbb,
- 0x0d98000c,
- 0x00e7f001,
- 0x016f21f5,
- 0xf101acf0,
- 0xf04000b7,
- 0x0c9850b3,
- 0x0fc4b604,
- 0x9800bcbb,
- 0x0d98010c,
- 0x060f9802,
- 0x0800e7f1,
- 0x016f21f5,
+ 0xf804bd00,
+/* 0x05fd: ctx_xfer */
+ 0x0007f100,
+ 0x0203f081,
+ 0xbd000fd0,
+ 0x0711f404,
+ 0x05cf21f5,
+/* 0x0610: ctx_xfer_not_load */
+ 0x026a21f5,
+ 0x07f124bd,
+ 0x03f047fc,
+ 0x0002d002,
+ 0x2cf004bd,
+ 0x0320b601,
+ 0x4afc07f1,
+ 0xd00203f0,
+ 0x04bd0002,
0xf001acf0,
- 0xb7f104a5,
- 0xb3f03000,
+ 0xb7f102a5,
+ 0xb3f00000,
0x040c9850,
0xbb0fc4b6,
0x0c9800bc,
- 0x030d9802,
- 0xf1080f98,
- 0xf50200e7,
- 0xf5016f21,
- 0xf4025e21,
- 0x12f40601,
-/* 0x06a9: ctx_xfer_post */
- 0x7f21f507,
-/* 0x06ad: ctx_xfer_done */
- 0xb421f502,
- 0x0000f805,
- 0x00000000,
+ 0x010d9800,
+ 0xf500e7f0,
+ 0xf0016f21,
+ 0xb7f101ac,
+ 0xb3f04000,
+ 0x040c9850,
+ 0xbb0fc4b6,
+ 0x0c9800bc,
+ 0x020d9801,
+ 0xf1060f98,
+ 0xf50800e7,
+ 0xf0016f21,
+ 0xa5f001ac,
+ 0x00b7f104,
+ 0x50b3f030,
+ 0xb6040c98,
+ 0xbcbb0fc4,
+ 0x020c9800,
+ 0x98030d98,
+ 0xe7f1080f,
+ 0x21f50200,
+ 0x21f5016f,
+ 0x01f4025e,
+ 0x0712f406,
+/* 0x06ac: ctx_xfer_post */
+ 0x027f21f5,
+/* 0x06b0: ctx_xfer_done */
+ 0x05b721f5,
+ 0x000000f8,
0x00000000,
0x00000000,
0x00000000,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5.h
index fbcc342f896f..84af7ec6a78e 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgk208.fuc5.h
@@ -276,7 +276,7 @@ uint32_t gk208_grgpc_code[] = {
0x02020014,
0xf6120040,
0x04bd0002,
- 0xfe048141,
+ 0xfe048441,
0x00400010,
0x0000f607,
0x040204bd,
@@ -295,165 +295,165 @@ uint32_t gk208_grgpc_code[] = {
0x01c90080,
0xbd0002f6,
0x0c308e04,
- 0xbd24bd50,
-/* 0x0383: init_unk_loop */
- 0x7e44bd34,
- 0xb0000065,
- 0x0bf400f6,
- 0xbb010f0e,
- 0x4ffd04f2,
- 0x0130b605,
-/* 0x0398: init_unk_next */
- 0xb60120b6,
- 0x26b004e0,
- 0xe21bf401,
-/* 0x03a4: init_unk_done */
- 0xb50703b5,
- 0x00820804,
- 0x22cf0201,
- 0x9534bd00,
- 0x00800825,
- 0x05f601c0,
- 0x8004bd00,
- 0xf601c100,
- 0x04bd0005,
- 0x98000e98,
- 0x207e010f,
- 0x2fbb0001,
- 0x003fbb00,
- 0x98010e98,
- 0x207e020f,
- 0x0e980001,
- 0x00effd05,
- 0xbb002ebb,
- 0x0e98003e,
- 0x030f9802,
- 0x0001207e,
- 0xfd070e98,
- 0x2ebb00ef,
- 0x003ebb00,
- 0x800235b6,
- 0xf601d300,
- 0x04bd0003,
- 0xb60825b6,
- 0x20b60635,
- 0x0130b601,
- 0xb60824b6,
- 0x2fb20834,
- 0x0002687e,
- 0xbb002fbb,
- 0x0080003f,
- 0x03f60201,
- 0xbd04bd00,
- 0x1f29f024,
- 0x02300080,
- 0xbd0002f6,
-/* 0x0445: main */
- 0x0031f404,
- 0x0d0028f4,
- 0x00377e24,
- 0xf401f400,
- 0xf404e4b0,
- 0x81fe1d18,
- 0xbd060201,
- 0x0412fd20,
- 0xfd01e4b6,
- 0x18fe051e,
- 0x05187e00,
- 0xd40ef400,
-/* 0x0474: main_not_ctx_xfer */
- 0xf010ef94,
- 0xf87e01f5,
- 0x0ef40002,
-/* 0x0481: ih */
- 0xfe80f9c7,
- 0x80f90188,
- 0xa0f990f9,
- 0xd0f9b0f9,
- 0xf0f9e0f9,
- 0x004a04bd,
- 0x00aacf02,
- 0xf404abc4,
- 0x240d1f0b,
- 0xcf1a004e,
- 0x004f00ee,
- 0x00ffcf19,
- 0x0000047e,
- 0x0040010e,
- 0x000ef61d,
-/* 0x04be: ih_no_fifo */
- 0x004004bd,
- 0x000af601,
- 0xf0fc04bd,
- 0xd0fce0fc,
- 0xa0fcb0fc,
- 0x80fc90fc,
- 0xfc0088fe,
- 0x0032f480,
-/* 0x04de: hub_barrier_done */
- 0x010f01f8,
- 0xbb040e98,
- 0xffb204fe,
- 0x4094188e,
- 0x00008f7e,
-/* 0x04f2: ctx_redswitch */
- 0x200f00f8,
+ 0x01e5f050,
+ 0x34bd24bd,
+/* 0x0386: init_unk_loop */
+ 0x657e44bd,
+ 0xf6b00000,
+ 0x0e0bf400,
+ 0xf2bb010f,
+ 0x054ffd04,
+/* 0x039b: init_unk_next */
+ 0xb60130b6,
+ 0xe0b60120,
+ 0x0126b004,
+/* 0x03a7: init_unk_done */
+ 0xb5e21bf4,
+ 0x04b50703,
+ 0x01008208,
+ 0x0022cf02,
+ 0x259534bd,
+ 0xc0008008,
+ 0x0005f601,
+ 0x008004bd,
+ 0x05f601c1,
+ 0x9804bd00,
+ 0x0f98000e,
+ 0x01207e01,
+ 0x002fbb00,
+ 0x98003fbb,
+ 0x0f98010e,
+ 0x01207e02,
+ 0x050e9800,
+ 0xbb00effd,
+ 0x3ebb002e,
+ 0x020e9800,
+ 0x7e030f98,
+ 0x98000120,
+ 0xeffd070e,
+ 0x002ebb00,
+ 0xb6003ebb,
+ 0x00800235,
+ 0x03f601d3,
+ 0xb604bd00,
+ 0x35b60825,
+ 0x0120b606,
+ 0xb60130b6,
+ 0x34b60824,
+ 0x7e2fb208,
+ 0xbb000268,
+ 0x3fbb002f,
+ 0x01008000,
+ 0x0003f602,
+ 0x24bd04bd,
+ 0x801f29f0,
+ 0xf6023000,
+ 0x04bd0002,
+/* 0x0448: main */
+ 0xf40031f4,
+ 0x240d0028,
+ 0x0000377e,
+ 0xb0f401f4,
+ 0x18f404e4,
+ 0x0181fe1d,
+ 0x20bd0602,
+ 0xb60412fd,
+ 0x1efd01e4,
+ 0x0018fe05,
+ 0x00051b7e,
+/* 0x0477: main_not_ctx_xfer */
+ 0x94d40ef4,
+ 0xf5f010ef,
+ 0x02f87e01,
+ 0xc70ef400,
+/* 0x0484: ih */
+ 0x88fe80f9,
+ 0xf980f901,
+ 0xf9a0f990,
+ 0xf9d0f9b0,
+ 0xbdf0f9e0,
+ 0x02004a04,
+ 0xc400aacf,
+ 0x0bf404ab,
+ 0x4e240d1f,
+ 0xeecf1a00,
+ 0x19004f00,
+ 0x7e00ffcf,
+ 0x0e000004,
+ 0x1d004001,
+ 0xbd000ef6,
+/* 0x04c1: ih_no_fifo */
+ 0x01004004,
+ 0xbd000af6,
+ 0xfcf0fc04,
+ 0xfcd0fce0,
+ 0xfca0fcb0,
+ 0xfe80fc90,
+ 0x80fc0088,
+ 0xf80032f4,
+/* 0x04e1: hub_barrier_done */
+ 0x98010f01,
+ 0xfebb040e,
+ 0x8effb204,
+ 0x7e409418,
+ 0xf800008f,
+/* 0x04f5: ctx_redswitch */
+ 0x80200f00,
+ 0xf6018500,
+ 0x04bd000f,
+/* 0x0502: ctx_redswitch_delay */
+ 0xe2b6080e,
+ 0xfd1bf401,
+ 0x0800f5f1,
+ 0x0200f5f1,
0x01850080,
0xbd000ff6,
-/* 0x04ff: ctx_redswitch_delay */
- 0xb6080e04,
- 0x1bf401e2,
- 0x00f5f1fd,
- 0x00f5f108,
- 0x85008002,
- 0x000ff601,
- 0x00f804bd,
-/* 0x0518: ctx_xfer */
- 0x02810080,
- 0xbd000ff6,
- 0x0711f404,
- 0x0004f27e,
-/* 0x0528: ctx_xfer_not_load */
- 0x0002167e,
- 0xfc8024bd,
- 0x02f60247,
- 0xf004bd00,
- 0x20b6012c,
- 0x4afc8003,
+/* 0x051b: ctx_xfer */
+ 0x8000f804,
+ 0xf6028100,
+ 0x04bd000f,
+ 0x7e0711f4,
+/* 0x052b: ctx_xfer_not_load */
+ 0x7e0004f5,
+ 0xbd000216,
+ 0x47fc8024,
0x0002f602,
- 0xacf004bd,
- 0x02a5f001,
- 0x5000008b,
- 0xb6040c98,
- 0xbcbb0fc4,
- 0x000c9800,
- 0x0e010d98,
- 0x013d7e00,
- 0x01acf000,
- 0x5040008b,
- 0xb6040c98,
- 0xbcbb0fc4,
- 0x010c9800,
- 0x98020d98,
- 0x004e060f,
- 0x013d7e08,
- 0x01acf000,
- 0x8b04a5f0,
- 0x98503000,
+ 0x2cf004bd,
+ 0x0320b601,
+ 0x024afc80,
+ 0xbd0002f6,
+ 0x01acf004,
+ 0x8b02a5f0,
+ 0x98500000,
0xc4b6040c,
0x00bcbb0f,
- 0x98020c98,
- 0x0f98030d,
- 0x02004e08,
+ 0x98000c98,
+ 0x000e010d,
0x00013d7e,
- 0x00020a7e,
- 0xf40601f4,
-/* 0x05b2: ctx_xfer_post */
- 0x277e0712,
-/* 0x05b6: ctx_xfer_done */
- 0xde7e0002,
- 0x00f80004,
- 0x00000000,
+ 0x8b01acf0,
+ 0x98504000,
+ 0xc4b6040c,
+ 0x00bcbb0f,
+ 0x98010c98,
+ 0x0f98020d,
+ 0x08004e06,
+ 0x00013d7e,
+ 0xf001acf0,
+ 0x008b04a5,
+ 0x0c985030,
+ 0x0fc4b604,
+ 0x9800bcbb,
+ 0x0d98020c,
+ 0x080f9803,
+ 0x7e02004e,
+ 0x7e00013d,
+ 0xf400020a,
+ 0x12f40601,
+/* 0x05b5: ctx_xfer_post */
+ 0x02277e07,
+/* 0x05b9: ctx_xfer_done */
+ 0x04e17e00,
+ 0x0000f800,
0x00000000,
0x00000000,
0x00000000,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5.h
index 51f5c3c6e966..11bf363a6ae9 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/fuc/gpcgm107.fuc5.h
@@ -289,7 +289,7 @@ uint32_t gm107_grgpc_code[] = {
0x020014fe,
0x12004002,
0xbd0002f6,
- 0x05b04104,
+ 0x05b34104,
0x400010fe,
0x00f60700,
0x0204bd00,
@@ -308,259 +308,259 @@ uint32_t gm107_grgpc_code[] = {
0xc900800f,
0x0002f601,
0x308e04bd,
- 0x24bd500c,
- 0x44bd34bd,
-/* 0x03b0: init_unk_loop */
- 0x0000657e,
- 0xf400f6b0,
- 0x010f0e0b,
- 0xfd04f2bb,
- 0x30b6054f,
-/* 0x03c5: init_unk_next */
- 0x0120b601,
- 0xb004e0b6,
- 0x1bf40226,
-/* 0x03d1: init_unk_done */
- 0x0703b5e2,
- 0x820804b5,
- 0xcf020100,
- 0x34bd0022,
- 0x80082595,
- 0xf601c000,
+ 0xe5f0500c,
+ 0xbd24bd01,
+/* 0x03b3: init_unk_loop */
+ 0x7e44bd34,
+ 0xb0000065,
+ 0x0bf400f6,
+ 0xbb010f0e,
+ 0x4ffd04f2,
+ 0x0130b605,
+/* 0x03c8: init_unk_next */
+ 0xb60120b6,
+ 0x26b004e0,
+ 0xe21bf402,
+/* 0x03d4: init_unk_done */
+ 0xb50703b5,
+ 0x00820804,
+ 0x22cf0201,
+ 0x9534bd00,
+ 0x00800825,
+ 0x05f601c0,
+ 0x8004bd00,
+ 0xf601c100,
0x04bd0005,
- 0x01c10080,
- 0xbd0005f6,
- 0x000e9804,
- 0x7e010f98,
- 0xbb000120,
- 0x3fbb002f,
- 0x010e9800,
- 0x7e020f98,
- 0x98000120,
- 0xeffd050e,
- 0x002ebb00,
- 0x98003ebb,
- 0x0f98020e,
- 0x01207e03,
- 0x070e9800,
- 0xbb00effd,
- 0x3ebb002e,
- 0x0235b600,
- 0x01d30080,
- 0xbd0003f6,
- 0x0825b604,
- 0xb60635b6,
- 0x30b60120,
- 0x0824b601,
- 0xb20834b6,
- 0x02687e2f,
- 0x002fbb00,
- 0x0f003fbb,
- 0x8effb23f,
- 0xf0501d60,
- 0x8f7e01e5,
- 0x0c0f0000,
- 0xa88effb2,
- 0xe5f0501d,
- 0x008f7e01,
- 0x03147e00,
- 0xb23f0f00,
- 0x1d608eff,
- 0x01e5f050,
- 0x00008f7e,
- 0xffb2000f,
- 0x501d9c8e,
- 0x7e01e5f0,
- 0x0f00008f,
- 0x03147e01,
- 0x8effb200,
+ 0x98000e98,
+ 0x207e010f,
+ 0x2fbb0001,
+ 0x003fbb00,
+ 0x98010e98,
+ 0x207e020f,
+ 0x0e980001,
+ 0x00effd05,
+ 0xbb002ebb,
+ 0x0e98003e,
+ 0x030f9802,
+ 0x0001207e,
+ 0xfd070e98,
+ 0x2ebb00ef,
+ 0x003ebb00,
+ 0x800235b6,
+ 0xf601d300,
+ 0x04bd0003,
+ 0xb60825b6,
+ 0x20b60635,
+ 0x0130b601,
+ 0xb60824b6,
+ 0x2fb20834,
+ 0x0002687e,
+ 0xbb002fbb,
+ 0x3f0f003f,
+ 0x501d608e,
+ 0xb201e5f0,
+ 0x008f7eff,
+ 0x8e0c0f00,
0xf0501da8,
- 0x8f7e01e5,
- 0xff0f0000,
- 0x988effb2,
+ 0xffb201e5,
+ 0x00008f7e,
+ 0x0003147e,
+ 0x608e3f0f,
0xe5f0501d,
- 0x008f7e01,
- 0xb2020f00,
- 0x1da88eff,
+ 0x7effb201,
+ 0x0f00008f,
+ 0x1d9c8e00,
0x01e5f050,
- 0x00008f7e,
+ 0x8f7effb2,
+ 0x010f0000,
0x0003147e,
- 0x85050498,
- 0x98504000,
- 0x64b60406,
- 0x0056bb0f,
-/* 0x04e0: tpc_strand_init_tpc_loop */
- 0x05705eb8,
- 0x00657e00,
- 0xbdf6b200,
-/* 0x04ed: tpc_strand_init_idx_loop */
- 0x605eb874,
- 0x7fb20005,
- 0x00008f7e,
- 0x05885eb8,
- 0x082f9500,
- 0x00008f7e,
- 0x058c5eb8,
- 0x082f9500,
+ 0x501da88e,
+ 0xb201e5f0,
+ 0x008f7eff,
+ 0x8eff0f00,
+ 0xf0501d98,
+ 0xffb201e5,
0x00008f7e,
- 0x05905eb8,
- 0x00657e00,
- 0x06f5b600,
- 0xb601f0b6,
- 0x2fbb08f4,
- 0x003fbb00,
- 0xb60170b6,
- 0x1bf40162,
- 0x0050b7bf,
- 0x0142b608,
- 0x0fa81bf4,
- 0x8effb23f,
- 0xf0501d60,
- 0x8f7e01e5,
- 0x0d0f0000,
- 0xa88effb2,
+ 0xa88e020f,
0xe5f0501d,
- 0x008f7e01,
- 0x03147e00,
- 0x01008000,
- 0x0003f602,
- 0x24bd04bd,
- 0x801f29f0,
- 0xf6023000,
- 0x04bd0002,
-/* 0x0574: main */
- 0xf40031f4,
- 0x240d0028,
- 0x0000377e,
- 0xb0f401f4,
- 0x18f404e4,
- 0x0181fe1d,
- 0x20bd0602,
- 0xb60412fd,
- 0x1efd01e4,
- 0x0018fe05,
- 0x0006477e,
-/* 0x05a3: main_not_ctx_xfer */
- 0x94d40ef4,
- 0xf5f010ef,
- 0x02f87e01,
- 0xc70ef400,
-/* 0x05b0: ih */
- 0x88fe80f9,
- 0xf980f901,
- 0xf9a0f990,
- 0xf9d0f9b0,
- 0xbdf0f9e0,
- 0x02004a04,
- 0xc400aacf,
- 0x0bf404ab,
- 0x4e240d1f,
- 0xeecf1a00,
- 0x19004f00,
- 0x7e00ffcf,
- 0x0e000004,
- 0x1d004001,
- 0xbd000ef6,
-/* 0x05ed: ih_no_fifo */
- 0x01004004,
- 0xbd000af6,
- 0xfcf0fc04,
- 0xfcd0fce0,
- 0xfca0fcb0,
- 0xfe80fc90,
- 0x80fc0088,
- 0xf80032f4,
-/* 0x060d: hub_barrier_done */
- 0x98010f01,
- 0xfebb040e,
- 0x8effb204,
- 0x7e409418,
- 0xf800008f,
-/* 0x0621: ctx_redswitch */
- 0x80200f00,
+ 0x7effb201,
+ 0x7e00008f,
+ 0x98000314,
+ 0x00850504,
+ 0x06985040,
+ 0x0f64b604,
+/* 0x04e3: tpc_strand_init_tpc_loop */
+ 0xb80056bb,
+ 0x0005705e,
+ 0x0000657e,
+ 0x74bdf6b2,
+/* 0x04f0: tpc_strand_init_idx_loop */
+ 0x05605eb8,
+ 0x7e7fb200,
+ 0xb800008f,
+ 0x0005885e,
+ 0x7e082f95,
+ 0xb800008f,
+ 0x00058c5e,
+ 0x7e082f95,
+ 0xb800008f,
+ 0x0005905e,
+ 0x0000657e,
+ 0xb606f5b6,
+ 0xf4b601f0,
+ 0x002fbb08,
+ 0xb6003fbb,
+ 0x62b60170,
+ 0xbf1bf401,
+ 0x080050b7,
+ 0xf40142b6,
+ 0x3f0fa81b,
+ 0x501d608e,
+ 0xb201e5f0,
+ 0x008f7eff,
+ 0x8e0d0f00,
+ 0xf0501da8,
+ 0xffb201e5,
+ 0x00008f7e,
+ 0x0003147e,
+ 0x02010080,
+ 0xbd0003f6,
+ 0xf024bd04,
+ 0x00801f29,
+ 0x02f60230,
+/* 0x0577: main */
+ 0xf404bd00,
+ 0x28f40031,
+ 0x7e240d00,
+ 0xf4000037,
+ 0xe4b0f401,
+ 0x1d18f404,
+ 0x020181fe,
+ 0xfd20bd06,
+ 0xe4b60412,
+ 0x051efd01,
+ 0x7e0018fe,
+ 0xf400064a,
+/* 0x05a6: main_not_ctx_xfer */
+ 0xef94d40e,
+ 0x01f5f010,
+ 0x0002f87e,
+/* 0x05b3: ih */
+ 0xf9c70ef4,
+ 0x0188fe80,
+ 0x90f980f9,
+ 0xb0f9a0f9,
+ 0xe0f9d0f9,
+ 0x04bdf0f9,
+ 0xcf02004a,
+ 0xabc400aa,
+ 0x1f0bf404,
+ 0x004e240d,
+ 0x00eecf1a,
+ 0xcf19004f,
+ 0x047e00ff,
+ 0x010e0000,
+ 0xf61d0040,
+ 0x04bd000e,
+/* 0x05f0: ih_no_fifo */
+ 0xf6010040,
+ 0x04bd000a,
+ 0xe0fcf0fc,
+ 0xb0fcd0fc,
+ 0x90fca0fc,
+ 0x88fe80fc,
+ 0xf480fc00,
+ 0x01f80032,
+/* 0x0610: hub_barrier_done */
+ 0x0e98010f,
+ 0x04febb04,
+ 0x188effb2,
+ 0x8f7e4094,
+ 0x00f80000,
+/* 0x0624: ctx_redswitch */
+ 0x0080200f,
+ 0x0ff60185,
+ 0x0e04bd00,
+/* 0x0631: ctx_redswitch_delay */
+ 0x01e2b608,
+ 0xf1fd1bf4,
+ 0xf10800f5,
+ 0x800200f5,
0xf6018500,
0x04bd000f,
-/* 0x062e: ctx_redswitch_delay */
- 0xe2b6080e,
- 0xfd1bf401,
- 0x0800f5f1,
- 0x0200f5f1,
- 0x01850080,
- 0xbd000ff6,
-/* 0x0647: ctx_xfer */
- 0x8000f804,
- 0xf6028100,
- 0x04bd000f,
- 0xc48effb2,
- 0xe5f0501d,
- 0x008f7e01,
- 0x0711f400,
- 0x0006217e,
-/* 0x0664: ctx_xfer_not_load */
- 0x0002167e,
- 0xfc8024bd,
- 0x02f60247,
- 0xf004bd00,
- 0x20b6012c,
- 0x4afc8003,
+/* 0x064a: ctx_xfer */
+ 0x008000f8,
+ 0x0ff60281,
+ 0x8e04bd00,
+ 0xf0501dc4,
+ 0xffb201e5,
+ 0x00008f7e,
+ 0x7e0711f4,
+/* 0x0667: ctx_xfer_not_load */
+ 0x7e000624,
+ 0xbd000216,
+ 0x47fc8024,
0x0002f602,
- 0x0c0f04bd,
- 0xa88effb2,
- 0xe5f0501d,
- 0x008f7e01,
- 0x03147e00,
- 0xb23f0f00,
- 0x1d608eff,
- 0x01e5f050,
+ 0x2cf004bd,
+ 0x0320b601,
+ 0x024afc80,
+ 0xbd0002f6,
+ 0x8e0c0f04,
+ 0xf0501da8,
+ 0xffb201e5,
0x00008f7e,
- 0xffb2000f,
- 0x501d9c8e,
- 0x7e01e5f0,
+ 0x0003147e,
+ 0x608e3f0f,
+ 0xe5f0501d,
+ 0x7effb201,
0x0f00008f,
- 0x03147e01,
- 0x01fcf000,
- 0xb203f0b6,
- 0x1da88eff,
+ 0x1d9c8e00,
0x01e5f050,
- 0x00008f7e,
- 0xf001acf0,
- 0x008b02a5,
- 0x0c985000,
- 0x0fc4b604,
- 0x9800bcbb,
- 0x0d98000c,
- 0x7e000e01,
- 0xf000013d,
- 0x008b01ac,
- 0x0c985040,
- 0x0fc4b604,
- 0x9800bcbb,
- 0x0d98010c,
- 0x060f9802,
- 0x7e08004e,
- 0xf000013d,
+ 0x8f7effb2,
+ 0x010f0000,
+ 0x0003147e,
+ 0xb601fcf0,
+ 0xa88e03f0,
+ 0xe5f0501d,
+ 0x7effb201,
+ 0xf000008f,
0xa5f001ac,
- 0x30008b04,
+ 0x00008b02,
0x040c9850,
0xbb0fc4b6,
0x0c9800bc,
- 0x030d9802,
- 0x4e080f98,
- 0x3d7e0200,
- 0x0a7e0001,
- 0x147e0002,
- 0x01f40003,
- 0x1a12f406,
-/* 0x073c: ctx_xfer_post */
- 0x0002277e,
- 0xffb20d0f,
- 0x501da88e,
- 0x7e01e5f0,
- 0x7e00008f,
-/* 0x0753: ctx_xfer_done */
- 0x7e000314,
- 0xf800060d,
- 0x00000000,
+ 0x010d9800,
+ 0x3d7e000e,
+ 0xacf00001,
+ 0x40008b01,
+ 0x040c9850,
+ 0xbb0fc4b6,
+ 0x0c9800bc,
+ 0x020d9801,
+ 0x4e060f98,
+ 0x3d7e0800,
+ 0xacf00001,
+ 0x04a5f001,
+ 0x5030008b,
+ 0xb6040c98,
+ 0xbcbb0fc4,
+ 0x020c9800,
+ 0x98030d98,
+ 0x004e080f,
+ 0x013d7e02,
+ 0x020a7e00,
+ 0x03147e00,
+ 0x0601f400,
+/* 0x073f: ctx_xfer_post */
+ 0x7e1a12f4,
+ 0x0f000227,
+ 0x1da88e0d,
+ 0x01e5f050,
+ 0x8f7effb2,
+ 0x147e0000,
+/* 0x0756: ctx_xfer_done */
+ 0x107e0003,
+ 0x00f80006,
0x00000000,
0x00000000,
0x00000000,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
index dda7a7d224c9..9f5dfc85147a 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
@@ -143,7 +143,7 @@ gf100_gr_zbc_depth_get(struct gf100_gr *gr, int format,
static int
gf100_fermi_mthd_zbc_color(struct nvkm_object *object, void *data, u32 size)
{
- struct gf100_gr *gr = (void *)object->engine;
+ struct gf100_gr *gr = gf100_gr(nvkm_gr(object->engine));
union {
struct fermi_a_zbc_color_v0 v0;
} *args = data;
@@ -189,7 +189,7 @@ gf100_fermi_mthd_zbc_color(struct nvkm_object *object, void *data, u32 size)
static int
gf100_fermi_mthd_zbc_depth(struct nvkm_object *object, void *data, u32 size)
{
- struct gf100_gr *gr = (void *)object->engine;
+ struct gf100_gr *gr = gf100_gr(nvkm_gr(object->engine));
union {
struct fermi_a_zbc_depth_v0 v0;
} *args = data;
@@ -1530,6 +1530,8 @@ gf100_gr_oneinit(struct nvkm_gr *base)
gr->ppc_nr[i] = gr->func->ppc_nr;
for (j = 0; j < gr->ppc_nr[i]; j++) {
u8 mask = nvkm_rd32(device, GPC_UNIT(i, 0x0c30 + (j * 4)));
+ if (mask)
+ gr->ppc_mask[i] |= (1 << j);
gr->ppc_tpc_nr[i][j] = hweight8(mask);
}
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h
index 4611961b1187..02e78b8d93f6 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h
@@ -97,6 +97,7 @@ struct gf100_gr {
u8 tpc_nr[GPC_MAX];
u8 tpc_total;
u8 ppc_nr[GPC_MAX];
+ u8 ppc_mask[GPC_MAX];
u8 ppc_tpc_nr[GPC_MAX][4];
struct nvkm_memory *unk4188b4;
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.c
index ffa902ece872..05a895496fc6 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/nv40.c
@@ -156,6 +156,7 @@ nv40_gr_chan_new(struct nvkm_gr *base, struct nvkm_fifo_chan *fifoch,
return -ENOMEM;
nvkm_object_ctor(&nv40_gr_chan, oclass, &chan->object);
chan->gr = gr;
+ chan->fifo = fifoch;
*pobject = &chan->object;
spin_lock_irqsave(&chan->gr->base.engine.lock, flags);
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/fan.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/fan.c
index 43006db6fd58..80fed7e78dcb 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/fan.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/fan.c
@@ -83,6 +83,7 @@ nvbios_fan_parse(struct nvkm_bios *bios, struct nvbios_therm_fan *fan)
fan->type = NVBIOS_THERM_FAN_UNK;
}
+ fan->fan_mode = NVBIOS_THERM_FAN_LINEAR;
fan->min_duty = nvbios_rd08(bios, data + 0x02);
fan->max_duty = nvbios_rd08(bios, data + 0x03);
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c
index 895ba74057d4..1d7dd38292b3 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c
@@ -97,7 +97,9 @@ static void *
nvkm_instobj_dtor(struct nvkm_memory *memory)
{
struct nvkm_instobj *iobj = nvkm_instobj(memory);
+ spin_lock(&iobj->imem->lock);
list_del(&iobj->head);
+ spin_unlock(&iobj->imem->lock);
nvkm_memory_del(&iobj->parent);
return iobj;
}
@@ -190,7 +192,9 @@ nvkm_instobj_new(struct nvkm_instmem *imem, u32 size, u32 align, bool zero,
nvkm_memory_ctor(&nvkm_instobj_func_slow, &iobj->memory);
iobj->parent = memory;
iobj->imem = imem;
+ spin_lock(&iobj->imem->lock);
list_add_tail(&iobj->head, &imem->list);
+ spin_unlock(&iobj->imem->lock);
memory = &iobj->memory;
}
@@ -309,5 +313,6 @@ nvkm_instmem_ctor(const struct nvkm_instmem_func *func,
{
nvkm_subdev_ctor(&nvkm_instmem, device, index, 0, &imem->subdev);
imem->func = func;
+ spin_lock_init(&imem->lock);
INIT_LIST_HEAD(&imem->list);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c
index d942fa7b9f18..86f9f3b13f71 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c
@@ -81,9 +81,7 @@ gk104_pmu_pgob(struct nvkm_pmu *pmu, bool enable)
nvkm_mask(device, 0x000200, 0x00001000, 0x00001000);
nvkm_rd32(device, 0x000200);
- if ( nvkm_boolopt(device->cfgopt, "War00C800_0",
- device->quirk ? device->quirk->War00C800_0 : false)) {
- nvkm_info(&pmu->subdev, "hw bug workaround enabled\n");
+ if (nvkm_boolopt(device->cfgopt, "War00C800_0", true)) {
switch (device->chipset) {
case 0xe4:
magic(device, 0x04000000);
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk104.c
index b61509e26ec9..b735173a18ff 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk104.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk104.c
@@ -59,7 +59,7 @@ gk104_volt_set(struct nvkm_volt *base, u32 uv)
duty = (uv - bios->base) * div / bios->pwm_range;
nvkm_wr32(device, 0x20340, div);
- nvkm_wr32(device, 0x20344, 0x8000000 | duty);
+ nvkm_wr32(device, 0x20344, 0x80000000 | duty);
return 0;
}
diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
index b8e4cdec28c3..24f92bea39c7 100644
--- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
+++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
@@ -112,11 +112,8 @@ static int omap_fbdev_create(struct drm_fb_helper *helper,
dma_addr_t paddr;
int ret;
- /* only doing ARGB32 since this is what is needed to alpha-blend
- * with video overlays:
- */
sizes->surface_bpp = 32;
- sizes->surface_depth = 32;
+ sizes->surface_depth = 24;
DBG("create fbdev: %dx%d@%d (%dx%d)", sizes->surface_width,
sizes->surface_height, sizes->surface_bpp,
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c
index 248953d2fdb7..f81fb2641097 100644
--- a/drivers/gpu/drm/radeon/cik.c
+++ b/drivers/gpu/drm/radeon/cik.c
@@ -4173,11 +4173,7 @@ void cik_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib)
control |= ib->length_dw | (vm_id << 24);
radeon_ring_write(ring, header);
- radeon_ring_write(ring,
-#ifdef __BIG_ENDIAN
- (2 << 0) |
-#endif
- (ib->gpu_addr & 0xFFFFFFFC));
+ radeon_ring_write(ring, (ib->gpu_addr & 0xFFFFFFFC));
radeon_ring_write(ring, upper_32_bits(ib->gpu_addr) & 0xFFFF);
radeon_ring_write(ring, control);
}
@@ -8472,7 +8468,7 @@ restart_ih:
if (queue_dp)
schedule_work(&rdev->dp_work);
if (queue_hotplug)
- schedule_work(&rdev->hotplug_work);
+ schedule_delayed_work(&rdev->hotplug_work, 0);
if (queue_reset) {
rdev->needs_reset = true;
wake_up_all(&rdev->fence_queue);
@@ -9630,6 +9626,9 @@ static void dce8_program_watermarks(struct radeon_device *rdev,
(rdev->disp_priority == 2)) {
DRM_DEBUG_KMS("force priority to high\n");
}
+
+ /* Save number of lines the linebuffer leads before the scanout */
+ radeon_crtc->lb_vblank_lead_lines = DIV_ROUND_UP(lb_size, mode->crtc_hdisplay);
}
/* select wm A */
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index 7f33767d7ed6..2ad462896896 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -2372,6 +2372,9 @@ static void evergreen_program_watermarks(struct radeon_device *rdev,
c.full = dfixed_div(c, a);
priority_b_mark = dfixed_trunc(c);
priority_b_cnt |= priority_b_mark & PRIORITY_MARK_MASK;
+
+ /* Save number of lines the linebuffer leads before the scanout */
+ radeon_crtc->lb_vblank_lead_lines = DIV_ROUND_UP(lb_size, mode->crtc_hdisplay);
}
/* select wm A */
@@ -5344,7 +5347,7 @@ restart_ih:
if (queue_dp)
schedule_work(&rdev->dp_work);
if (queue_hotplug)
- schedule_work(&rdev->hotplug_work);
+ schedule_delayed_work(&rdev->hotplug_work, 0);
if (queue_hdmi)
schedule_work(&rdev->audio_work);
if (queue_thermal && rdev->pm.dpm_enabled)
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index 238b13f045c1..9e7e2bf03b81 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -806,7 +806,7 @@ int r100_irq_process(struct radeon_device *rdev)
status = r100_irq_ack(rdev);
}
if (queue_hotplug)
- schedule_work(&rdev->hotplug_work);
+ schedule_delayed_work(&rdev->hotplug_work, 0);
if (rdev->msi_enabled) {
switch (rdev->family) {
case CHIP_RS400:
@@ -3217,6 +3217,9 @@ void r100_bandwidth_update(struct radeon_device *rdev)
uint32_t pixel_bytes1 = 0;
uint32_t pixel_bytes2 = 0;
+ /* Guess line buffer size to be 8192 pixels */
+ u32 lb_size = 8192;
+
if (!rdev->mode_info.mode_config_initialized)
return;
@@ -3631,6 +3634,13 @@ void r100_bandwidth_update(struct radeon_device *rdev)
DRM_DEBUG_KMS("GRPH2_BUFFER_CNTL from to %x\n",
(unsigned int)RREG32(RADEON_GRPH2_BUFFER_CNTL));
}
+
+ /* Save number of lines the linebuffer leads before the scanout */
+ if (mode1)
+ rdev->mode_info.crtcs[0]->lb_vblank_lead_lines = DIV_ROUND_UP(lb_size, mode1->crtc_hdisplay);
+
+ if (mode2)
+ rdev->mode_info.crtcs[1]->lb_vblank_lead_lines = DIV_ROUND_UP(lb_size, mode2->crtc_hdisplay);
}
int r100_ring_test(struct radeon_device *rdev, struct radeon_ring *ring)
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index 4ea5b10ff5f4..cc2fdf0be37a 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -4276,7 +4276,7 @@ restart_ih:
WREG32(IH_RB_RPTR, rptr);
}
if (queue_hotplug)
- schedule_work(&rdev->hotplug_work);
+ schedule_delayed_work(&rdev->hotplug_work, 0);
if (queue_hdmi)
schedule_work(&rdev->audio_work);
if (queue_thermal && rdev->pm.dpm_enabled)
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index b6cbd816537e..87db64983ea8 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -2414,7 +2414,7 @@ struct radeon_device {
struct r600_ih ih; /* r6/700 interrupt ring */
struct radeon_rlc rlc;
struct radeon_mec mec;
- struct work_struct hotplug_work;
+ struct delayed_work hotplug_work;
struct work_struct dp_work;
struct work_struct audio_work;
int num_crtc; /* number of crtcs */
diff --git a/drivers/gpu/drm/radeon/radeon_agp.c b/drivers/gpu/drm/radeon/radeon_agp.c
index fe994aac3b04..c77d349c561c 100644
--- a/drivers/gpu/drm/radeon/radeon_agp.c
+++ b/drivers/gpu/drm/radeon/radeon_agp.c
@@ -54,6 +54,9 @@ static struct radeon_agpmode_quirk radeon_agpmode_quirk_list[] = {
/* Intel 82855PM host bridge / Mobility 9600 M10 RV350 Needs AGPMode 1 (lp #195051) */
{ PCI_VENDOR_ID_INTEL, 0x3340, PCI_VENDOR_ID_ATI, 0x4e50,
PCI_VENDOR_ID_IBM, 0x0550, 1},
+ /* Intel 82855PM host bridge / RV250/M9 GL [Mobility FireGL 9000/Radeon 9000] needs AGPMode 1 (Thinkpad T40p) */
+ { PCI_VENDOR_ID_INTEL, 0x3340, PCI_VENDOR_ID_ATI, 0x4c66,
+ PCI_VENDOR_ID_IBM, 0x054d, 1},
/* Intel 82855PM host bridge / Mobility M7 needs AGPMode 1 */
{ PCI_VENDOR_ID_INTEL, 0x3340, PCI_VENDOR_ID_ATI, 0x4c57,
PCI_VENDOR_ID_IBM, 0x0530, 1},
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 5a2cafb4f1bc..340f3f549f29 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -1234,13 +1234,32 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
if (r < 0)
return connector_status_disconnected;
+ if (radeon_connector->detected_hpd_without_ddc) {
+ force = true;
+ radeon_connector->detected_hpd_without_ddc = false;
+ }
+
if (!force && radeon_check_hpd_status_unchanged(connector)) {
ret = connector->status;
goto exit;
}
- if (radeon_connector->ddc_bus)
+ if (radeon_connector->ddc_bus) {
dret = radeon_ddc_probe(radeon_connector, false);
+
+ /* Sometimes the pins required for the DDC probe on DVI
+ * connectors don't make contact at the same time that the ones
+ * for HPD do. If the DDC probe fails even though we had an HPD
+ * signal, try again later */
+ if (!dret && !force &&
+ connector->status != connector_status_connected) {
+ DRM_DEBUG_KMS("hpd detected without ddc, retrying in 1 second\n");
+ radeon_connector->detected_hpd_without_ddc = true;
+ schedule_delayed_work(&rdev->hotplug_work,
+ msecs_to_jiffies(1000));
+ goto exit;
+ }
+ }
if (dret) {
radeon_connector->detected_by_load = false;
radeon_connector_free_edid(connector);
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index a8d9927ed9eb..1eca0acac016 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -322,7 +322,9 @@ void radeon_crtc_handle_vblank(struct radeon_device *rdev, int crtc_id)
* to complete in this vblank?
*/
if (update_pending &&
- (DRM_SCANOUTPOS_VALID & radeon_get_crtc_scanoutpos(rdev->ddev, crtc_id, 0,
+ (DRM_SCANOUTPOS_VALID & radeon_get_crtc_scanoutpos(rdev->ddev,
+ crtc_id,
+ USE_REAL_VBLANKSTART,
&vpos, &hpos, NULL, NULL,
&rdev->mode_info.crtcs[crtc_id]->base.hwmode)) &&
((vpos >= (99 * rdev->mode_info.crtcs[crtc_id]->base.hwmode.crtc_vdisplay)/100) ||
@@ -401,6 +403,8 @@ static void radeon_flip_work_func(struct work_struct *__work)
struct drm_crtc *crtc = &radeon_crtc->base;
unsigned long flags;
int r;
+ int vpos, hpos, stat, min_udelay;
+ struct drm_vblank_crtc *vblank = &crtc->dev->vblank[work->crtc_id];
down_read(&rdev->exclusive_lock);
if (work->fence) {
@@ -437,6 +441,41 @@ static void radeon_flip_work_func(struct work_struct *__work)
/* set the proper interrupt */
radeon_irq_kms_pflip_irq_get(rdev, radeon_crtc->crtc_id);
+ /* If this happens to execute within the "virtually extended" vblank
+ * interval before the start of the real vblank interval then it needs
+ * to delay programming the mmio flip until the real vblank is entered.
+ * This prevents completing a flip too early due to the way we fudge
+ * our vblank counter and vblank timestamps in order to work around the
+ * problem that the hw fires vblank interrupts before actual start of
+ * vblank (when line buffer refilling is done for a frame). It
+ * complements the fudging logic in radeon_get_crtc_scanoutpos() for
+ * timestamping and radeon_get_vblank_counter_kms() for vblank counts.
+ *
+ * In practice this won't execute very often unless on very fast
+ * machines because the time window for this to happen is very small.
+ */
+ for (;;) {
+ /* GET_DISTANCE_TO_VBLANKSTART returns distance to real vblank
+ * start in hpos, and to the "fudged earlier" vblank start in
+ * vpos.
+ */
+ stat = radeon_get_crtc_scanoutpos(rdev->ddev, work->crtc_id,
+ GET_DISTANCE_TO_VBLANKSTART,
+ &vpos, &hpos, NULL, NULL,
+ &crtc->hwmode);
+
+ if ((stat & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE)) !=
+ (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE) ||
+ !(vpos >= 0 && hpos <= 0))
+ break;
+
+ /* Sleep at least until estimated real start of hw vblank */
+ spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+ min_udelay = (-hpos + 1) * max(vblank->linedur_ns / 1000, 5);
+ usleep_range(min_udelay, 2 * min_udelay);
+ spin_lock_irqsave(&crtc->dev->event_lock, flags);
+ };
+
/* do the flip (mmio) */
radeon_page_flip(rdev, radeon_crtc->crtc_id, work->base);
@@ -1768,6 +1807,15 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
* \param dev Device to query.
* \param crtc Crtc to query.
* \param flags Flags from caller (DRM_CALLED_FROM_VBLIRQ or 0).
+ * For driver internal use only also supports these flags:
+ *
+ * USE_REAL_VBLANKSTART to use the real start of vblank instead
+ * of a fudged earlier start of vblank.
+ *
+ * GET_DISTANCE_TO_VBLANKSTART to return distance to the
+ * fudged earlier start of vblank in *vpos and the distance
+ * to true start of vblank in *hpos.
+ *
* \param *vpos Location where vertical scanout position should be stored.
* \param *hpos Location where horizontal scanout position should go.
* \param *stime Target location for timestamp taken immediately before
@@ -1911,10 +1959,40 @@ int radeon_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
vbl_end = 0;
}
+ /* Called from driver internal vblank counter query code? */
+ if (flags & GET_DISTANCE_TO_VBLANKSTART) {
+ /* Caller wants distance from real vbl_start in *hpos */
+ *hpos = *vpos - vbl_start;
+ }
+
+ /* Fudge vblank to start a few scanlines earlier to handle the
+ * problem that vblank irqs fire a few scanlines before start
+ * of vblank. Some driver internal callers need the true vblank
+ * start to be used and signal this via the USE_REAL_VBLANKSTART flag.
+ *
+ * The cause of the "early" vblank irq is that the irq is triggered
+ * by the line buffer logic when the line buffer read position enters
+ * the vblank, whereas our crtc scanout position naturally lags the
+ * line buffer read position.
+ */
+ if (!(flags & USE_REAL_VBLANKSTART))
+ vbl_start -= rdev->mode_info.crtcs[pipe]->lb_vblank_lead_lines;
+
/* Test scanout position against vblank region. */
if ((*vpos < vbl_start) && (*vpos >= vbl_end))
in_vbl = false;
+ /* In vblank? */
+ if (in_vbl)
+ ret |= DRM_SCANOUTPOS_IN_VBLANK;
+
+ /* Called from driver internal vblank counter query code? */
+ if (flags & GET_DISTANCE_TO_VBLANKSTART) {
+ /* Caller wants distance from fudged earlier vbl_start */
+ *vpos -= vbl_start;
+ return ret;
+ }
+
/* Check if inside vblank area and apply corrective offsets:
* vpos will then be >=0 in video scanout area, but negative
* within vblank area, counting down the number of lines until
@@ -1930,31 +2008,5 @@ int radeon_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
/* Correct for shifted end of vbl at vbl_end. */
*vpos = *vpos - vbl_end;
- /* In vblank? */
- if (in_vbl)
- ret |= DRM_SCANOUTPOS_IN_VBLANK;
-
- /* Is vpos outside nominal vblank area, but less than
- * 1/100 of a frame height away from start of vblank?
- * If so, assume this isn't a massively delayed vblank
- * interrupt, but a vblank interrupt that fired a few
- * microseconds before true start of vblank. Compensate
- * by adding a full frame duration to the final timestamp.
- * Happens, e.g., on ATI R500, R600.
- *
- * We only do this if DRM_CALLED_FROM_VBLIRQ.
- */
- if ((flags & DRM_CALLED_FROM_VBLIRQ) && !in_vbl) {
- vbl_start = mode->crtc_vdisplay;
- vtotal = mode->crtc_vtotal;
-
- if (vbl_start - *vpos < vtotal / 100) {
- *vpos -= vtotal;
-
- /* Signal this correction as "applied". */
- ret |= 0x8;
- }
- }
-
return ret;
}
diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c
index 171d3e43c30c..979f3bf65f2c 100644
--- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
@@ -74,7 +74,7 @@ irqreturn_t radeon_driver_irq_handler_kms(int irq, void *arg)
static void radeon_hotplug_work_func(struct work_struct *work)
{
struct radeon_device *rdev = container_of(work, struct radeon_device,
- hotplug_work);
+ hotplug_work.work);
struct drm_device *dev = rdev->ddev;
struct drm_mode_config *mode_config = &dev->mode_config;
struct drm_connector *connector;
@@ -302,7 +302,7 @@ int radeon_irq_kms_init(struct radeon_device *rdev)
}
}
- INIT_WORK(&rdev->hotplug_work, radeon_hotplug_work_func);
+ INIT_DELAYED_WORK(&rdev->hotplug_work, radeon_hotplug_work_func);
INIT_WORK(&rdev->dp_work, radeon_dp_work_func);
INIT_WORK(&rdev->audio_work, r600_audio_update_hdmi);
@@ -310,7 +310,7 @@ int radeon_irq_kms_init(struct radeon_device *rdev)
r = drm_irq_install(rdev->ddev, rdev->ddev->pdev->irq);
if (r) {
rdev->irq.installed = false;
- flush_work(&rdev->hotplug_work);
+ flush_delayed_work(&rdev->hotplug_work);
return r;
}
@@ -333,7 +333,7 @@ void radeon_irq_kms_fini(struct radeon_device *rdev)
rdev->irq.installed = false;
if (rdev->msi_enabled)
pci_disable_msi(rdev->pdev);
- flush_work(&rdev->hotplug_work);
+ flush_delayed_work(&rdev->hotplug_work);
}
}
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
index 0ec6fcca16d3..d290a8a09036 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -755,6 +755,8 @@ void radeon_driver_preclose_kms(struct drm_device *dev,
*/
u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc)
{
+ int vpos, hpos, stat;
+ u32 count;
struct radeon_device *rdev = dev->dev_private;
if (crtc < 0 || crtc >= rdev->num_crtc) {
@@ -762,7 +764,53 @@ u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc)
return -EINVAL;
}
- return radeon_get_vblank_counter(rdev, crtc);
+ /* The hw increments its frame counter at start of vsync, not at start
+ * of vblank, as is required by DRM core vblank counter handling.
+ * Cook the hw count here to make it appear to the caller as if it
+ * incremented at start of vblank. We measure distance to start of
+ * vblank in vpos. vpos therefore will be >= 0 between start of vblank
+ * and start of vsync, so vpos >= 0 means to bump the hw frame counter
+ * result by 1 to give the proper appearance to caller.
+ */
+ if (rdev->mode_info.crtcs[crtc]) {
+ /* Repeat readout if needed to provide stable result if
+ * we cross start of vsync during the queries.
+ */
+ do {
+ count = radeon_get_vblank_counter(rdev, crtc);
+ /* Ask radeon_get_crtc_scanoutpos to return vpos as
+ * distance to start of vblank, instead of regular
+ * vertical scanout pos.
+ */
+ stat = radeon_get_crtc_scanoutpos(
+ dev, crtc, GET_DISTANCE_TO_VBLANKSTART,
+ &vpos, &hpos, NULL, NULL,
+ &rdev->mode_info.crtcs[crtc]->base.hwmode);
+ } while (count != radeon_get_vblank_counter(rdev, crtc));
+
+ if (((stat & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE)) !=
+ (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE))) {
+ DRM_DEBUG_VBL("Query failed! stat %d\n", stat);
+ }
+ else {
+ DRM_DEBUG_VBL("crtc %d: dist from vblank start %d\n",
+ crtc, vpos);
+
+ /* Bump counter if we are at >= leading edge of vblank,
+ * but before vsync where vpos would turn negative and
+ * the hw counter really increments.
+ */
+ if (vpos >= 0)
+ count++;
+ }
+ }
+ else {
+ /* Fallback to use value as is. */
+ count = radeon_get_vblank_counter(rdev, crtc);
+ DRM_DEBUG_VBL("NULL mode info! Returned count may be wrong.\n");
+ }
+
+ return count;
}
/**
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 830e171c3a9e..bba112628b47 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -367,6 +367,7 @@ struct radeon_crtc {
u32 line_time;
u32 wm_low;
u32 wm_high;
+ u32 lb_vblank_lead_lines;
struct drm_display_mode hw_mode;
enum radeon_output_csc output_csc;
};
@@ -553,6 +554,7 @@ struct radeon_connector {
void *con_priv;
bool dac_load_detect;
bool detected_by_load; /* if the connection status was determined by load */
+ bool detected_hpd_without_ddc; /* if an HPD signal was detected on DVI, but ddc probing failed */
uint16_t connector_object_id;
struct radeon_hpd hpd;
struct radeon_router router;
@@ -686,6 +688,9 @@ struct atom_voltage_table
struct atom_voltage_table_entry entries[MAX_VOLTAGE_ENTRIES];
};
+/* Driver internal use only flags of radeon_get_crtc_scanoutpos() */
+#define USE_REAL_VBLANKSTART (1 << 30)
+#define GET_DISTANCE_TO_VBLANKSTART (1 << 31)
extern void
radeon_add_atom_connector(struct drm_device *dev,
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
index f4f03dcc1530..59abebd6b5dc 100644
--- a/drivers/gpu/drm/radeon/radeon_pm.c
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
@@ -1756,7 +1756,9 @@ static bool radeon_pm_in_vbl(struct radeon_device *rdev)
*/
for (crtc = 0; (crtc < rdev->num_crtc) && in_vbl; crtc++) {
if (rdev->pm.active_crtcs & (1 << crtc)) {
- vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev, crtc, 0,
+ vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev,
+ crtc,
+ USE_REAL_VBLANKSTART,
&vpos, &hpos, NULL, NULL,
&rdev->mode_info.crtcs[crtc]->base.hwmode);
if ((vbl_status & DRM_SCANOUTPOS_VALID) &&
diff --git a/drivers/gpu/drm/radeon/radeon_vce.c b/drivers/gpu/drm/radeon/radeon_vce.c
index 574f62bbd215..7eb1ae758906 100644
--- a/drivers/gpu/drm/radeon/radeon_vce.c
+++ b/drivers/gpu/drm/radeon/radeon_vce.c
@@ -361,31 +361,31 @@ int radeon_vce_get_create_msg(struct radeon_device *rdev, int ring,
/* stitch together an VCE create msg */
ib.length_dw = 0;
- ib.ptr[ib.length_dw++] = 0x0000000c; /* len */
- ib.ptr[ib.length_dw++] = 0x00000001; /* session cmd */
- ib.ptr[ib.length_dw++] = handle;
-
- ib.ptr[ib.length_dw++] = 0x00000030; /* len */
- ib.ptr[ib.length_dw++] = 0x01000001; /* create cmd */
- ib.ptr[ib.length_dw++] = 0x00000000;
- ib.ptr[ib.length_dw++] = 0x00000042;
- ib.ptr[ib.length_dw++] = 0x0000000a;
- ib.ptr[ib.length_dw++] = 0x00000001;
- ib.ptr[ib.length_dw++] = 0x00000080;
- ib.ptr[ib.length_dw++] = 0x00000060;
- ib.ptr[ib.length_dw++] = 0x00000100;
- ib.ptr[ib.length_dw++] = 0x00000100;
- ib.ptr[ib.length_dw++] = 0x0000000c;
- ib.ptr[ib.length_dw++] = 0x00000000;
-
- ib.ptr[ib.length_dw++] = 0x00000014; /* len */
- ib.ptr[ib.length_dw++] = 0x05000005; /* feedback buffer */
- ib.ptr[ib.length_dw++] = upper_32_bits(dummy);
- ib.ptr[ib.length_dw++] = dummy;
- ib.ptr[ib.length_dw++] = 0x00000001;
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x0000000c); /* len */
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000001); /* session cmd */
+ ib.ptr[ib.length_dw++] = cpu_to_le32(handle);
+
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000030); /* len */
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x01000001); /* create cmd */
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000000);
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000042);
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x0000000a);
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000001);
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000080);
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000060);
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000100);
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000100);
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x0000000c);
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000000);
+
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000014); /* len */
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x05000005); /* feedback buffer */
+ ib.ptr[ib.length_dw++] = cpu_to_le32(upper_32_bits(dummy));
+ ib.ptr[ib.length_dw++] = cpu_to_le32(dummy);
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000001);
for (i = ib.length_dw; i < ib_size_dw; ++i)
- ib.ptr[i] = 0x0;
+ ib.ptr[i] = cpu_to_le32(0x0);
r = radeon_ib_schedule(rdev, &ib, NULL, false);
if (r) {
@@ -428,21 +428,21 @@ int radeon_vce_get_destroy_msg(struct radeon_device *rdev, int ring,
/* stitch together an VCE destroy msg */
ib.length_dw = 0;
- ib.ptr[ib.length_dw++] = 0x0000000c; /* len */
- ib.ptr[ib.length_dw++] = 0x00000001; /* session cmd */
- ib.ptr[ib.length_dw++] = handle;
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x0000000c); /* len */
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000001); /* session cmd */
+ ib.ptr[ib.length_dw++] = cpu_to_le32(handle);
- ib.ptr[ib.length_dw++] = 0x00000014; /* len */
- ib.ptr[ib.length_dw++] = 0x05000005; /* feedback buffer */
- ib.ptr[ib.length_dw++] = upper_32_bits(dummy);
- ib.ptr[ib.length_dw++] = dummy;
- ib.ptr[ib.length_dw++] = 0x00000001;
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000014); /* len */
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x05000005); /* feedback buffer */
+ ib.ptr[ib.length_dw++] = cpu_to_le32(upper_32_bits(dummy));
+ ib.ptr[ib.length_dw++] = cpu_to_le32(dummy);
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000001);
- ib.ptr[ib.length_dw++] = 0x00000008; /* len */
- ib.ptr[ib.length_dw++] = 0x02000001; /* destroy cmd */
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x00000008); /* len */
+ ib.ptr[ib.length_dw++] = cpu_to_le32(0x02000001); /* destroy cmd */
for (i = ib.length_dw; i < ib_size_dw; ++i)
- ib.ptr[i] = 0x0;
+ ib.ptr[i] = cpu_to_le32(0x0);
r = radeon_ib_schedule(rdev, &ib, NULL, false);
if (r) {
@@ -699,12 +699,12 @@ bool radeon_vce_semaphore_emit(struct radeon_device *rdev,
{
uint64_t addr = semaphore->gpu_addr;
- radeon_ring_write(ring, VCE_CMD_SEMAPHORE);
- radeon_ring_write(ring, (addr >> 3) & 0x000FFFFF);
- radeon_ring_write(ring, (addr >> 23) & 0x000FFFFF);
- radeon_ring_write(ring, 0x01003000 | (emit_wait ? 1 : 0));
+ radeon_ring_write(ring, cpu_to_le32(VCE_CMD_SEMAPHORE));
+ radeon_ring_write(ring, cpu_to_le32((addr >> 3) & 0x000FFFFF));
+ radeon_ring_write(ring, cpu_to_le32((addr >> 23) & 0x000FFFFF));
+ radeon_ring_write(ring, cpu_to_le32(0x01003000 | (emit_wait ? 1 : 0)));
if (!emit_wait)
- radeon_ring_write(ring, VCE_CMD_END);
+ radeon_ring_write(ring, cpu_to_le32(VCE_CMD_END));
return true;
}
@@ -719,10 +719,10 @@ bool radeon_vce_semaphore_emit(struct radeon_device *rdev,
void radeon_vce_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib)
{
struct radeon_ring *ring = &rdev->ring[ib->ring];
- radeon_ring_write(ring, VCE_CMD_IB);
- radeon_ring_write(ring, ib->gpu_addr);
- radeon_ring_write(ring, upper_32_bits(ib->gpu_addr));
- radeon_ring_write(ring, ib->length_dw);
+ radeon_ring_write(ring, cpu_to_le32(VCE_CMD_IB));
+ radeon_ring_write(ring, cpu_to_le32(ib->gpu_addr));
+ radeon_ring_write(ring, cpu_to_le32(upper_32_bits(ib->gpu_addr)));
+ radeon_ring_write(ring, cpu_to_le32(ib->length_dw));
}
/**
@@ -738,12 +738,12 @@ void radeon_vce_fence_emit(struct radeon_device *rdev,
struct radeon_ring *ring = &rdev->ring[fence->ring];
uint64_t addr = rdev->fence_drv[fence->ring].gpu_addr;
- radeon_ring_write(ring, VCE_CMD_FENCE);
- radeon_ring_write(ring, addr);
- radeon_ring_write(ring, upper_32_bits(addr));
- radeon_ring_write(ring, fence->seq);
- radeon_ring_write(ring, VCE_CMD_TRAP);
- radeon_ring_write(ring, VCE_CMD_END);
+ radeon_ring_write(ring, cpu_to_le32(VCE_CMD_FENCE));
+ radeon_ring_write(ring, cpu_to_le32(addr));
+ radeon_ring_write(ring, cpu_to_le32(upper_32_bits(addr)));
+ radeon_ring_write(ring, cpu_to_le32(fence->seq));
+ radeon_ring_write(ring, cpu_to_le32(VCE_CMD_TRAP));
+ radeon_ring_write(ring, cpu_to_le32(VCE_CMD_END));
}
/**
@@ -765,7 +765,7 @@ int radeon_vce_ring_test(struct radeon_device *rdev, struct radeon_ring *ring)
ring->idx, r);
return r;
}
- radeon_ring_write(ring, VCE_CMD_END);
+ radeon_ring_write(ring, cpu_to_le32(VCE_CMD_END));
radeon_ring_unlock_commit(rdev, ring, false);
for (i = 0; i < rdev->usec_timeout; i++) {
diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c
index 97a904835759..6244f4e44e9a 100644
--- a/drivers/gpu/drm/radeon/rs600.c
+++ b/drivers/gpu/drm/radeon/rs600.c
@@ -813,7 +813,7 @@ int rs600_irq_process(struct radeon_device *rdev)
status = rs600_irq_ack(rdev);
}
if (queue_hotplug)
- schedule_work(&rdev->hotplug_work);
+ schedule_delayed_work(&rdev->hotplug_work, 0);
if (queue_hdmi)
schedule_work(&rdev->audio_work);
if (rdev->msi_enabled) {
diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c
index 516ca27cfa12..6bc44c24e837 100644
--- a/drivers/gpu/drm/radeon/rs690.c
+++ b/drivers/gpu/drm/radeon/rs690.c
@@ -207,6 +207,9 @@ void rs690_line_buffer_adjust(struct radeon_device *rdev,
{
u32 tmp;
+ /* Guess line buffer size to be 8192 pixels */
+ u32 lb_size = 8192;
+
/*
* Line Buffer Setup
* There is a single line buffer shared by both display controllers.
@@ -243,6 +246,13 @@ void rs690_line_buffer_adjust(struct radeon_device *rdev,
tmp |= V_006520_DC_LB_MEMORY_SPLIT_D1_1Q_D2_3Q;
}
WREG32(R_006520_DC_LB_MEMORY_SPLIT, tmp);
+
+ /* Save number of lines the linebuffer leads before the scanout */
+ if (mode1)
+ rdev->mode_info.crtcs[0]->lb_vblank_lead_lines = DIV_ROUND_UP(lb_size, mode1->crtc_hdisplay);
+
+ if (mode2)
+ rdev->mode_info.crtcs[1]->lb_vblank_lead_lines = DIV_ROUND_UP(lb_size, mode2->crtc_hdisplay);
}
struct rs690_watermark {
diff --git a/drivers/gpu/drm/radeon/rv730_dpm.c b/drivers/gpu/drm/radeon/rv730_dpm.c
index 3f5e1cf138ba..d37ba2cb886e 100644
--- a/drivers/gpu/drm/radeon/rv730_dpm.c
+++ b/drivers/gpu/drm/radeon/rv730_dpm.c
@@ -464,7 +464,7 @@ void rv730_stop_dpm(struct radeon_device *rdev)
result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_TwoLevelsDisabled);
if (result != PPSMC_Result_OK)
- DRM_ERROR("Could not force DPM to low\n");
+ DRM_DEBUG("Could not force DPM to low\n");
WREG32_P(GENERAL_PWRMGT, 0, ~GLOBAL_PWRMGT_EN);
diff --git a/drivers/gpu/drm/radeon/rv770_dpm.c b/drivers/gpu/drm/radeon/rv770_dpm.c
index b9c770745a7a..e830c8935db0 100644
--- a/drivers/gpu/drm/radeon/rv770_dpm.c
+++ b/drivers/gpu/drm/radeon/rv770_dpm.c
@@ -193,7 +193,7 @@ void rv770_stop_dpm(struct radeon_device *rdev)
result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_TwoLevelsDisabled);
if (result != PPSMC_Result_OK)
- DRM_ERROR("Could not force DPM to low.\n");
+ DRM_DEBUG("Could not force DPM to low.\n");
WREG32_P(GENERAL_PWRMGT, 0, ~GLOBAL_PWRMGT_EN);
@@ -1418,7 +1418,7 @@ int rv770_resume_smc(struct radeon_device *rdev)
int rv770_set_sw_state(struct radeon_device *rdev)
{
if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_SwitchToSwState) != PPSMC_Result_OK)
- return -EINVAL;
+ DRM_DEBUG("rv770_set_sw_state failed\n");
return 0;
}
diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c
index 07037e32dea3..f878d6962da5 100644
--- a/drivers/gpu/drm/radeon/si.c
+++ b/drivers/gpu/drm/radeon/si.c
@@ -2376,6 +2376,9 @@ static void dce6_program_watermarks(struct radeon_device *rdev,
c.full = dfixed_div(c, a);
priority_b_mark = dfixed_trunc(c);
priority_b_cnt |= priority_b_mark & PRIORITY_MARK_MASK;
+
+ /* Save number of lines the linebuffer leads before the scanout */
+ radeon_crtc->lb_vblank_lead_lines = DIV_ROUND_UP(lb_size, mode->crtc_hdisplay);
}
/* select wm A */
@@ -6848,7 +6851,7 @@ restart_ih:
if (queue_dp)
schedule_work(&rdev->dp_work);
if (queue_hotplug)
- schedule_work(&rdev->hotplug_work);
+ schedule_delayed_work(&rdev->hotplug_work, 0);
if (queue_thermal && rdev->pm.dpm_enabled)
schedule_work(&rdev->pm.dpm.thermal.work);
rdev->ih.rptr = rptr;
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
index 8caea0a33dd8..d908321b94ce 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -67,6 +67,7 @@ static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj,
* VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap().
*/
vma->vm_flags &= ~VM_PFNMAP;
+ vma->vm_pgoff = 0;
ret = dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
obj->size, &rk_obj->dma_attrs);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 5d8ae5e49c44..03c47eeadc81 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -374,6 +374,7 @@ static const struct of_device_id vop_driver_dt_match[] = {
.data = &rk3288_vop },
{},
};
+MODULE_DEVICE_TABLE(of, vop_driver_dt_match);
static inline void vop_writel(struct vop *vop, uint32_t offset, uint32_t v)
{
@@ -959,8 +960,8 @@ static int vop_update_plane_event(struct drm_plane *plane,
val = (dest.y2 - dest.y1 - 1) << 16;
val |= (dest.x2 - dest.x1 - 1) & 0xffff;
VOP_WIN_SET(vop, win, dsp_info, val);
- val = (dsp_sty - 1) << 16;
- val |= (dsp_stx - 1) & 0xffff;
+ val = dsp_sty << 16;
+ val |= dsp_stx & 0xffff;
VOP_WIN_SET(vop, win, dsp_st, val);
VOP_WIN_SET(vop, win, rb_swap, rb_swap);
@@ -1289,7 +1290,7 @@ static void vop_win_state_complete(struct vop_win *vop_win,
if (state->event) {
spin_lock_irqsave(&drm->event_lock, flags);
- drm_send_vblank_event(drm, -1, state->event);
+ drm_crtc_send_vblank_event(crtc, state->event);
spin_unlock_irqrestore(&drm->event_lock, flags);
}
@@ -1575,32 +1576,25 @@ static int vop_initial(struct vop *vop)
return PTR_ERR(vop->dclk);
}
- ret = clk_prepare(vop->hclk);
- if (ret < 0) {
- dev_err(vop->dev, "failed to prepare hclk\n");
- return ret;
- }
-
ret = clk_prepare(vop->dclk);
if (ret < 0) {
dev_err(vop->dev, "failed to prepare dclk\n");
- goto err_unprepare_hclk;
+ return ret;
}
- ret = clk_prepare(vop->aclk);
+ /* Enable both the hclk and aclk to setup the vop */
+ ret = clk_prepare_enable(vop->hclk);
if (ret < 0) {
- dev_err(vop->dev, "failed to prepare aclk\n");
+ dev_err(vop->dev, "failed to prepare/enable hclk\n");
goto err_unprepare_dclk;
}
- /*
- * enable hclk, so that we can config vop register.
- */
- ret = clk_enable(vop->hclk);
+ ret = clk_prepare_enable(vop->aclk);
if (ret < 0) {
- dev_err(vop->dev, "failed to prepare aclk\n");
- goto err_unprepare_aclk;
+ dev_err(vop->dev, "failed to prepare/enable aclk\n");
+ goto err_disable_hclk;
}
+
/*
* do hclk_reset, reset all vop registers.
*/
@@ -1608,7 +1602,7 @@ static int vop_initial(struct vop *vop)
if (IS_ERR(ahb_rst)) {
dev_err(vop->dev, "failed to get ahb reset\n");
ret = PTR_ERR(ahb_rst);
- goto err_disable_hclk;
+ goto err_disable_aclk;
}
reset_control_assert(ahb_rst);
usleep_range(10, 20);
@@ -1634,26 +1628,25 @@ static int vop_initial(struct vop *vop)
if (IS_ERR(vop->dclk_rst)) {
dev_err(vop->dev, "failed to get dclk reset\n");
ret = PTR_ERR(vop->dclk_rst);
- goto err_unprepare_aclk;
+ goto err_disable_aclk;
}
reset_control_assert(vop->dclk_rst);
usleep_range(10, 20);
reset_control_deassert(vop->dclk_rst);
clk_disable(vop->hclk);
+ clk_disable(vop->aclk);
vop->is_enabled = false;
return 0;
+err_disable_aclk:
+ clk_disable_unprepare(vop->aclk);
err_disable_hclk:
- clk_disable(vop->hclk);
-err_unprepare_aclk:
- clk_unprepare(vop->aclk);
+ clk_disable_unprepare(vop->hclk);
err_unprepare_dclk:
clk_unprepare(vop->dclk);
-err_unprepare_hclk:
- clk_unprepare(vop->hclk);
return ret;
}
diff --git a/drivers/gpu/drm/ttm/ttm_lock.c b/drivers/gpu/drm/ttm/ttm_lock.c
index 6a954544727f..f154fb1929bd 100644
--- a/drivers/gpu/drm/ttm/ttm_lock.c
+++ b/drivers/gpu/drm/ttm/ttm_lock.c
@@ -180,7 +180,7 @@ int ttm_write_lock(struct ttm_lock *lock, bool interruptible)
spin_unlock(&lock->lock);
}
} else
- wait_event(lock->queue, __ttm_read_lock(lock));
+ wait_event(lock->queue, __ttm_write_lock(lock));
return ret;
}
diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c
index f545913a56c7..578fe0a9324c 100644
--- a/drivers/gpu/drm/virtio/virtgpu_display.c
+++ b/drivers/gpu/drm/virtio/virtgpu_display.c
@@ -412,7 +412,7 @@ static const struct drm_connector_funcs virtio_gpu_connector_funcs = {
.save = virtio_gpu_conn_save,
.restore = virtio_gpu_conn_restore,
.detect = virtio_gpu_conn_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
+ .fill_modes = drm_helper_probe_single_connector_modes_nomerge,
.destroy = virtio_gpu_conn_destroy,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index a09cf8529b9f..c49812b80dd0 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -1233,6 +1233,7 @@ static void vmw_master_drop(struct drm_device *dev,
vmw_fp->locked_master = drm_master_get(file_priv->master);
ret = ttm_vt_lock(&vmaster->lock, false, vmw_fp->tfile);
+ vmw_kms_legacy_hotspot_clear(dev_priv);
if (unlikely((ret != 0))) {
DRM_ERROR("Unable to lock TTM at VT switch.\n");
drm_master_put(&vmw_fp->locked_master);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
index a8ae9dfb83b7..469cdd520615 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
@@ -925,6 +925,7 @@ int vmw_kms_present(struct vmw_private *dev_priv,
uint32_t num_clips);
int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
+void vmw_kms_legacy_hotspot_clear(struct vmw_private *dev_priv);
int vmw_dumb_create(struct drm_file *file_priv,
struct drm_device *dev,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c
index a8baf5f5e765..b6a0806b06bf 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c
@@ -390,7 +390,7 @@ void *vmw_fifo_reserve_dx(struct vmw_private *dev_priv, uint32_t bytes,
else if (ctx_id == SVGA3D_INVALID_ID)
ret = vmw_local_fifo_reserve(dev_priv, bytes);
else {
- WARN_ON("Command buffer has not been allocated.\n");
+ WARN(1, "Command buffer has not been allocated.\n");
ret = NULL;
}
if (IS_ERR_OR_NULL(ret)) {
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index 9fcd7f82995c..9b4bb9e74d73 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -133,13 +133,19 @@ void vmw_cursor_update_position(struct vmw_private *dev_priv,
vmw_mmio_write(++count, fifo_mem + SVGA_FIFO_CURSOR_COUNT);
}
-int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
- uint32_t handle, uint32_t width, uint32_t height)
+
+/*
+ * vmw_du_crtc_cursor_set2 - Driver cursor_set2 callback.
+ */
+int vmw_du_crtc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv,
+ uint32_t handle, uint32_t width, uint32_t height,
+ int32_t hot_x, int32_t hot_y)
{
struct vmw_private *dev_priv = vmw_priv(crtc->dev);
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
struct vmw_surface *surface = NULL;
struct vmw_dma_buffer *dmabuf = NULL;
+ s32 hotspot_x, hotspot_y;
int ret;
/*
@@ -151,6 +157,8 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
*/
drm_modeset_unlock_crtc(crtc);
drm_modeset_lock_all(dev_priv->dev);
+ hotspot_x = hot_x + du->hotspot_x;
+ hotspot_y = hot_y + du->hotspot_y;
/* A lot of the code assumes this */
if (handle && (width != 64 || height != 64)) {
@@ -187,31 +195,34 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
vmw_dmabuf_unreference(&du->cursor_dmabuf);
/* setup new image */
+ ret = 0;
if (surface) {
/* vmw_user_surface_lookup takes one reference */
du->cursor_surface = surface;
du->cursor_surface->snooper.crtc = crtc;
du->cursor_age = du->cursor_surface->snooper.age;
- vmw_cursor_update_image(dev_priv, surface->snooper.image,
- 64, 64, du->hotspot_x, du->hotspot_y);
+ ret = vmw_cursor_update_image(dev_priv, surface->snooper.image,
+ 64, 64, hotspot_x, hotspot_y);
} else if (dmabuf) {
/* vmw_user_surface_lookup takes one reference */
du->cursor_dmabuf = dmabuf;
ret = vmw_cursor_update_dmabuf(dev_priv, dmabuf, width, height,
- du->hotspot_x, du->hotspot_y);
+ hotspot_x, hotspot_y);
} else {
vmw_cursor_update_position(dev_priv, false, 0, 0);
- ret = 0;
goto out;
}
- vmw_cursor_update_position(dev_priv, true,
- du->cursor_x + du->hotspot_x,
- du->cursor_y + du->hotspot_y);
+ if (!ret) {
+ vmw_cursor_update_position(dev_priv, true,
+ du->cursor_x + hotspot_x,
+ du->cursor_y + hotspot_y);
+ du->core_hotspot_x = hot_x;
+ du->core_hotspot_y = hot_y;
+ }
- ret = 0;
out:
drm_modeset_unlock_all(dev_priv->dev);
drm_modeset_lock_crtc(crtc, crtc->cursor);
@@ -239,8 +250,10 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
drm_modeset_lock_all(dev_priv->dev);
vmw_cursor_update_position(dev_priv, shown,
- du->cursor_x + du->hotspot_x,
- du->cursor_y + du->hotspot_y);
+ du->cursor_x + du->hotspot_x +
+ du->core_hotspot_x,
+ du->cursor_y + du->hotspot_y +
+ du->core_hotspot_y);
drm_modeset_unlock_all(dev_priv->dev);
drm_modeset_lock_crtc(crtc, crtc->cursor);
@@ -334,6 +347,29 @@ err_unreserve:
ttm_bo_unreserve(bo);
}
+/**
+ * vmw_kms_legacy_hotspot_clear - Clear legacy hotspots
+ *
+ * @dev_priv: Pointer to the device private struct.
+ *
+ * Clears all legacy hotspots.
+ */
+void vmw_kms_legacy_hotspot_clear(struct vmw_private *dev_priv)
+{
+ struct drm_device *dev = dev_priv->dev;
+ struct vmw_display_unit *du;
+ struct drm_crtc *crtc;
+
+ drm_modeset_lock_all(dev);
+ drm_for_each_crtc(crtc, dev) {
+ du = vmw_crtc_to_du(crtc);
+
+ du->hotspot_x = 0;
+ du->hotspot_y = 0;
+ }
+ drm_modeset_unlock_all(dev);
+}
+
void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv)
{
struct drm_device *dev = dev_priv->dev;
@@ -351,7 +387,9 @@ void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv)
du->cursor_age = du->cursor_surface->snooper.age;
vmw_cursor_update_image(dev_priv,
du->cursor_surface->snooper.image,
- 64, 64, du->hotspot_x, du->hotspot_y);
+ 64, 64,
+ du->hotspot_x + du->core_hotspot_x,
+ du->hotspot_y + du->core_hotspot_y);
}
mutex_unlock(&dev->mode_config.mutex);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
index 782df7ca9794..edd81503516d 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
@@ -159,6 +159,8 @@ struct vmw_display_unit {
int hotspot_x;
int hotspot_y;
+ s32 core_hotspot_x;
+ s32 core_hotspot_y;
unsigned unit;
@@ -193,8 +195,9 @@ void vmw_du_crtc_restore(struct drm_crtc *crtc);
void vmw_du_crtc_gamma_set(struct drm_crtc *crtc,
u16 *r, u16 *g, u16 *b,
uint32_t start, uint32_t size);
-int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
- uint32_t handle, uint32_t width, uint32_t height);
+int vmw_du_crtc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv,
+ uint32_t handle, uint32_t width, uint32_t height,
+ int32_t hot_x, int32_t hot_y);
int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y);
int vmw_du_connector_dpms(struct drm_connector *connector, int mode);
void vmw_du_connector_save(struct drm_connector *connector);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
index bb63e4d795fa..52caecb4502e 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
@@ -297,7 +297,7 @@ static int vmw_ldu_crtc_set_config(struct drm_mode_set *set)
static struct drm_crtc_funcs vmw_legacy_crtc_funcs = {
.save = vmw_du_crtc_save,
.restore = vmw_du_crtc_restore,
- .cursor_set = vmw_du_crtc_cursor_set,
+ .cursor_set2 = vmw_du_crtc_cursor_set2,
.cursor_move = vmw_du_crtc_cursor_move,
.gamma_set = vmw_du_crtc_gamma_set,
.destroy = vmw_ldu_crtc_destroy,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
index b96d1ab610c5..13926ff192e3 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
@@ -533,7 +533,7 @@ out_no_fence:
static struct drm_crtc_funcs vmw_screen_object_crtc_funcs = {
.save = vmw_du_crtc_save,
.restore = vmw_du_crtc_restore,
- .cursor_set = vmw_du_crtc_cursor_set,
+ .cursor_set2 = vmw_du_crtc_cursor_set2,
.cursor_move = vmw_du_crtc_cursor_move,
.gamma_set = vmw_du_crtc_gamma_set,
.destroy = vmw_sou_crtc_destroy,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
index b1fc1c02792d..f823fc3efed7 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
@@ -1043,7 +1043,7 @@ out_finish:
static struct drm_crtc_funcs vmw_stdu_crtc_funcs = {
.save = vmw_du_crtc_save,
.restore = vmw_du_crtc_restore,
- .cursor_set = vmw_du_crtc_cursor_set,
+ .cursor_set2 = vmw_du_crtc_cursor_set2,
.cursor_move = vmw_du_crtc_cursor_move,
.gamma_set = vmw_du_crtc_gamma_set,
.destroy = vmw_stdu_crtc_destroy,
diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c
index ba47b30d28fa..f2e13eb8339f 100644
--- a/drivers/gpu/ipu-v3/ipu-common.c
+++ b/drivers/gpu/ipu-v3/ipu-common.c
@@ -28,6 +28,7 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/of_device.h>
+#include <linux/of_graph.h>
#include <drm/drm_fourcc.h>
@@ -993,12 +994,26 @@ static void platform_device_unregister_children(struct platform_device *pdev)
struct ipu_platform_reg {
struct ipu_client_platformdata pdata;
const char *name;
- int reg_offset;
};
+/* These must be in the order of the corresponding device tree port nodes */
static const struct ipu_platform_reg client_reg[] = {
{
.pdata = {
+ .csi = 0,
+ .dma[0] = IPUV3_CHANNEL_CSI0,
+ .dma[1] = -EINVAL,
+ },
+ .name = "imx-ipuv3-camera",
+ }, {
+ .pdata = {
+ .csi = 1,
+ .dma[0] = IPUV3_CHANNEL_CSI1,
+ .dma[1] = -EINVAL,
+ },
+ .name = "imx-ipuv3-camera",
+ }, {
+ .pdata = {
.di = 0,
.dc = 5,
.dp = IPU_DP_FLOW_SYNC_BG,
@@ -1015,22 +1030,6 @@ static const struct ipu_platform_reg client_reg[] = {
.dma[1] = -EINVAL,
},
.name = "imx-ipuv3-crtc",
- }, {
- .pdata = {
- .csi = 0,
- .dma[0] = IPUV3_CHANNEL_CSI0,
- .dma[1] = -EINVAL,
- },
- .reg_offset = IPU_CM_CSI0_REG_OFS,
- .name = "imx-ipuv3-camera",
- }, {
- .pdata = {
- .csi = 1,
- .dma[0] = IPUV3_CHANNEL_CSI1,
- .dma[1] = -EINVAL,
- },
- .reg_offset = IPU_CM_CSI1_REG_OFS,
- .name = "imx-ipuv3-camera",
},
};
@@ -1051,22 +1050,30 @@ static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base)
for (i = 0; i < ARRAY_SIZE(client_reg); i++) {
const struct ipu_platform_reg *reg = &client_reg[i];
struct platform_device *pdev;
- struct resource res;
-
- if (reg->reg_offset) {
- memset(&res, 0, sizeof(res));
- res.flags = IORESOURCE_MEM;
- res.start = ipu_base + ipu->devtype->cm_ofs + reg->reg_offset;
- res.end = res.start + PAGE_SIZE - 1;
- pdev = platform_device_register_resndata(dev, reg->name,
- id++, &res, 1, &reg->pdata, sizeof(reg->pdata));
- } else {
- pdev = platform_device_register_data(dev, reg->name,
- id++, &reg->pdata, sizeof(reg->pdata));
+
+ pdev = platform_device_alloc(reg->name, id++);
+ if (!pdev) {
+ ret = -ENOMEM;
+ goto err_register;
+ }
+
+ pdev->dev.parent = dev;
+
+ /* Associate subdevice with the corresponding port node */
+ pdev->dev.of_node = of_graph_get_port_by_id(dev->of_node, i);
+ if (!pdev->dev.of_node) {
+ dev_err(dev, "missing port@%d node in %s\n", i,
+ dev->of_node->full_name);
+ ret = -ENODEV;
+ goto err_register;
}
- if (IS_ERR(pdev)) {
- ret = PTR_ERR(pdev);
+ ret = platform_device_add_data(pdev, &reg->pdata,
+ sizeof(reg->pdata));
+ if (!ret)
+ ret = platform_device_add(pdev);
+ if (ret) {
+ platform_device_put(pdev);
goto err_register;
}
}
diff --git a/drivers/gpu/ipu-v3/ipu-cpmem.c b/drivers/gpu/ipu-v3/ipu-cpmem.c
index 63eb16bf2cf0..883a314cd83a 100644
--- a/drivers/gpu/ipu-v3/ipu-cpmem.c
+++ b/drivers/gpu/ipu-v3/ipu-cpmem.c
@@ -161,7 +161,7 @@ static u32 ipu_ch_param_read_field(struct ipuv3_channel *ch, u32 wbs)
* The DRM pixel formats and IPU internal representation are ordered the other
* way around, with the first named component ordered at the most significant
* bits. Further, V4L2 formats are not well defined:
- * http://linuxtv.org/downloads/v4l-dvb-apis/packed-rgb.html
+ * https://linuxtv.org/downloads/v4l-dvb-apis/packed-rgb.html
* We choose the interpretation which matches GStreamer behavior.
*/
static int v4l2_pix_fmt_to_drm_fourcc(u32 pixelformat)
diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c
index 3166e4bc4eb6..f17cb0431833 100644
--- a/drivers/gpu/vga/vgaarb.c
+++ b/drivers/gpu/vga/vgaarb.c
@@ -395,8 +395,10 @@ int vga_get(struct pci_dev *pdev, unsigned int rsrc, int interruptible)
set_current_state(interruptible ?
TASK_INTERRUPTIBLE :
TASK_UNINTERRUPTIBLE);
- if (signal_pending(current)) {
- rc = -EINTR;
+ if (interruptible && signal_pending(current)) {
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&vga_wait_queue, &wait);
+ rc = -ERESTARTSYS;
break;
}
schedule();
@@ -1161,12 +1163,8 @@ done:
static unsigned int vga_arb_fpoll(struct file *file, poll_table *wait)
{
- struct vga_arb_private *priv = file->private_data;
-
pr_debug("%s\n", __func__);
- if (priv == NULL)
- return -ENODEV;
poll_wait(file, &vga_wait_queue, wait);
return POLLIN;
}
@@ -1207,9 +1205,6 @@ static int vga_arb_release(struct inode *inode, struct file *file)
pr_debug("%s\n", __func__);
- if (priv == NULL)
- return -ENODEV;
-
spin_lock_irqsave(&vga_user_lock, flags);
list_del(&priv->list);
for (i = 0; i < MAX_USER_CARDS; i++) {
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index c6f7a694f67a..7e89288b1537 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -625,7 +625,7 @@ static void hid_close_report(struct hid_device *device)
static void hid_device_release(struct device *dev)
{
- struct hid_device *hid = container_of(dev, struct hid_device, dev);
+ struct hid_device *hid = to_hid_device(dev);
hid_close_report(hid);
kfree(hid->dev_rdesc);
@@ -1571,8 +1571,8 @@ read_report_descriptor(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct device *dev = kobj_to_dev(kobj);
+ struct hid_device *hdev = to_hid_device(dev);
if (off >= hdev->rsize)
return 0;
@@ -1589,7 +1589,7 @@ static ssize_t
show_country(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
return sprintf(buf, "%02x\n", hdev->country & 0xff);
}
@@ -1691,11 +1691,6 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
hid_warn(hdev,
"can't create sysfs country code attribute err: %d\n", ret);
- ret = device_create_bin_file(&hdev->dev, &dev_bin_attr_report_desc);
- if (ret)
- hid_warn(hdev,
- "can't create sysfs report descriptor attribute err: %d\n", ret);
-
hid_info(hdev, "%s: %s HID v%x.%02x %s [%s] on %s\n",
buf, bus, hdev->version >> 8, hdev->version & 0xff,
type, hdev->name, hdev->phys);
@@ -1707,7 +1702,6 @@ EXPORT_SYMBOL_GPL(hid_connect);
void hid_disconnect(struct hid_device *hdev)
{
device_remove_file(&hdev->dev, &dev_attr_country);
- device_remove_bin_file(&hdev->dev, &dev_bin_attr_report_desc);
if (hdev->claimed & HID_CLAIMED_INPUT)
hidinput_disconnect(hdev);
if (hdev->claimed & HID_CLAIMED_HIDDEV)
@@ -1902,6 +1896,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) },
@@ -2076,7 +2071,7 @@ struct hid_dynid {
static ssize_t store_new_id(struct device_driver *drv, const char *buf,
size_t count)
{
- struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver);
+ struct hid_driver *hdrv = to_hid_driver(drv);
struct hid_dynid *dynid;
__u32 bus, vendor, product;
unsigned long driver_data = 0;
@@ -2138,17 +2133,16 @@ static const struct hid_device_id *hid_match_device(struct hid_device *hdev,
static int hid_bus_match(struct device *dev, struct device_driver *drv)
{
- struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver);
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_driver *hdrv = to_hid_driver(drv);
+ struct hid_device *hdev = to_hid_device(dev);
return hid_match_device(hdev, hdrv) != NULL;
}
static int hid_device_probe(struct device *dev)
{
- struct hid_driver *hdrv = container_of(dev->driver,
- struct hid_driver, driver);
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_driver *hdrv = to_hid_driver(dev->driver);
+ struct hid_device *hdev = to_hid_device(dev);
const struct hid_device_id *id;
int ret = 0;
@@ -2190,7 +2184,7 @@ unlock_driver_lock:
static int hid_device_remove(struct device *dev)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct hid_driver *hdrv;
int ret = 0;
@@ -2223,12 +2217,9 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
char *buf)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
- int len;
-
- len = snprintf(buf, PAGE_SIZE, "hid:b%04Xg%04Xv%08Xp%08X\n",
- hdev->bus, hdev->group, hdev->vendor, hdev->product);
- return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
+ return scnprintf(buf, PAGE_SIZE, "hid:b%04Xg%04Xv%08Xp%08X\n",
+ hdev->bus, hdev->group, hdev->vendor, hdev->product);
}
static DEVICE_ATTR_RO(modalias);
@@ -2236,11 +2227,19 @@ static struct attribute *hid_dev_attrs[] = {
&dev_attr_modalias.attr,
NULL,
};
-ATTRIBUTE_GROUPS(hid_dev);
+static struct bin_attribute *hid_dev_bin_attrs[] = {
+ &dev_bin_attr_report_desc,
+ NULL
+};
+static const struct attribute_group hid_dev_group = {
+ .attrs = hid_dev_attrs,
+ .bin_attrs = hid_dev_bin_attrs,
+};
+__ATTRIBUTE_GROUPS(hid_dev);
static int hid_uevent(struct device *dev, struct kobj_uevent_env *env)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
if (add_uevent_var(env, "HID_ID=%04X:%08X:%08X",
hdev->bus, hdev->vendor, hdev->product))
@@ -2408,6 +2407,7 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICK16F1454) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICK16F1454_V2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR, USB_DEVICE_ID_N_S_HARMONY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20) },
@@ -2660,6 +2660,7 @@ struct hid_device *hid_allocate_device(void)
device_initialize(&hdev->dev);
hdev->dev.release = hid_device_release;
hdev->dev.bus = &hid_bus_type;
+ device_enable_async_suspend(&hdev->dev);
hid_close_report(hdev);
diff --git a/drivers/hid/hid-corsair.c b/drivers/hid/hid-corsair.c
index bcefb9ebb026..58551964ce86 100644
--- a/drivers/hid/hid-corsair.c
+++ b/drivers/hid/hid-corsair.c
@@ -655,18 +655,7 @@ static struct hid_driver corsair_driver = {
.input_mapping = corsair_input_mapping,
};
-static int __init corsair_init(void)
-{
- return hid_register_driver(&corsair_driver);
-}
-
-static void corsair_exit(void)
-{
- hid_unregister_driver(&corsair_driver);
-}
-
-module_init(corsair_init);
-module_exit(corsair_exit);
+module_hid_driver(corsair_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Clement Vuchener");
diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c
index 7afc3fcc122c..7c38bfa05fac 100644
--- a/drivers/hid/hid-cp2112.c
+++ b/drivers/hid/hid-cp2112.c
@@ -807,7 +807,7 @@ static ssize_t name##_store(struct device *kdev, \
struct device_attribute *attr, const char *buf, \
size_t count) \
{ \
- struct hid_device *hdev = container_of(kdev, struct hid_device, dev); \
+ struct hid_device *hdev = to_hid_device(kdev); \
struct cp2112_usb_config_report cfg; \
int ret = cp2112_get_usb_config(hdev, &cfg); \
if (ret) \
@@ -822,7 +822,7 @@ static ssize_t name##_store(struct device *kdev, \
static ssize_t name##_show(struct device *kdev, \
struct device_attribute *attr, char *buf) \
{ \
- struct hid_device *hdev = container_of(kdev, struct hid_device, dev); \
+ struct hid_device *hdev = to_hid_device(kdev); \
struct cp2112_usb_config_report cfg; \
int ret = cp2112_get_usb_config(hdev, &cfg); \
if (ret) \
@@ -887,7 +887,7 @@ static ssize_t pstr_store(struct device *kdev,
struct device_attribute *kattr, const char *buf,
size_t count)
{
- struct hid_device *hdev = container_of(kdev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(kdev);
struct cp2112_pstring_attribute *attr =
container_of(kattr, struct cp2112_pstring_attribute, attr);
struct cp2112_string_report report;
@@ -918,7 +918,7 @@ static ssize_t pstr_store(struct device *kdev,
static ssize_t pstr_show(struct device *kdev,
struct device_attribute *kattr, char *buf)
{
- struct hid_device *hdev = container_of(kdev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(kdev);
struct cp2112_pstring_attribute *attr =
container_of(kattr, struct cp2112_pstring_attribute, attr);
struct cp2112_string_report report;
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index 2886b645ced7..acfb522a432a 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -659,13 +659,13 @@ EXPORT_SYMBOL_GPL(hid_dump_device);
/* enqueue string to 'events' ring buffer */
void hid_debug_event(struct hid_device *hdev, char *buf)
{
- int i;
+ unsigned i;
struct hid_debug_list *list;
unsigned long flags;
spin_lock_irqsave(&hdev->debug_list_lock, flags);
list_for_each_entry(list, &hdev->debug_list, node) {
- for (i = 0; i < strlen(buf); i++)
+ for (i = 0; buf[i]; i++)
list->hid_debug_buf[(list->tail + i) % HID_DEBUG_BUFSIZE] =
buf[i];
list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE;
diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
index 0d6f135e266c..a298fbd8db6b 100644
--- a/drivers/hid/hid-gt683r.c
+++ b/drivers/hid/hid-gt683r.c
@@ -70,7 +70,7 @@ static void gt683r_brightness_set(struct led_classdev *led_cdev,
{
int i;
struct device *dev = led_cdev->dev->parent;
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct gt683r_led *led = hid_get_drvdata(hdev);
for (i = 0; i < GT683R_LED_COUNT; i++) {
@@ -89,8 +89,7 @@ static ssize_t mode_show(struct device *dev,
char *buf)
{
u8 sysfs_mode;
- struct hid_device *hdev = container_of(dev->parent,
- struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev->parent);
struct gt683r_led *led = hid_get_drvdata(hdev);
if (led->mode == GT683R_LED_NORMAL)
@@ -108,8 +107,7 @@ static ssize_t mode_store(struct device *dev,
const char *buf, size_t count)
{
u8 sysfs_mode;
- struct hid_device *hdev = container_of(dev->parent,
- struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev->parent);
struct gt683r_led *led = hid_get_drvdata(hdev);
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index ac1feea51be3..b6ff6e78ac54 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -316,11 +316,6 @@
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001
#define USB_VENDOR_ID_ELAN 0x04f3
-#define USB_DEVICE_ID_ELAN_TOUCHSCREEN 0x0089
-#define USB_DEVICE_ID_ELAN_TOUCHSCREEN_009B 0x009b
-#define USB_DEVICE_ID_ELAN_TOUCHSCREEN_0103 0x0103
-#define USB_DEVICE_ID_ELAN_TOUCHSCREEN_010c 0x010c
-#define USB_DEVICE_ID_ELAN_TOUCHSCREEN_016F 0x016f
#define USB_VENDOR_ID_ELECOM 0x056e
#define USB_DEVICE_ID_ELECOM_BM084 0x0061
@@ -515,6 +510,7 @@
#define USB_VENDOR_ID_ITE 0x048d
#define USB_DEVICE_ID_ITE_LENOVO_YOGA 0x8386
#define USB_DEVICE_ID_ITE_LENOVO_YOGA2 0x8350
+#define USB_DEVICE_ID_ITE_LENOVO_YOGA900 0x8396
#define USB_VENDOR_ID_JABRA 0x0b0e
#define USB_DEVICE_ID_JABRA_SPEAK_410 0x0412
@@ -609,6 +605,7 @@
#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110
#define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
#define USB_DEVICE_ID_LOGITECH_HARMONY_PS3 0x0306
+#define USB_DEVICE_ID_LOGITECH_KEYBOARD_G710_PLUS 0xc24d
#define USB_DEVICE_ID_LOGITECH_MOUSE_C01A 0xc01a
#define USB_DEVICE_ID_LOGITECH_MOUSE_C05A 0xc05a
#define USB_DEVICE_ID_LOGITECH_MOUSE_C06A 0xc06a
@@ -619,6 +616,7 @@
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219
#define USB_DEVICE_ID_LOGITECH_G29_WHEEL 0xc24f
+#define USB_DEVICE_ID_LOGITECH_G920_WHEEL 0xc262
#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283
#define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286
#define USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940 0xc287
@@ -668,6 +666,7 @@
#define USB_DEVICE_ID_PICOLCD 0xc002
#define USB_DEVICE_ID_PICOLCD_BOOTLOADER 0xf002
#define USB_DEVICE_ID_PICK16F1454 0x0042
+#define USB_DEVICE_ID_PICK16F1454_V2 0xf2f7
#define USB_VENDOR_ID_MICROSOFT 0x045e
#define USB_DEVICE_ID_SIDEWINDER_GV 0x003b
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 2ba6bf69b7d0..bcfaf32d9e5e 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -303,6 +303,7 @@ static enum power_supply_property hidinput_battery_props[] = {
#define HID_BATTERY_QUIRK_PERCENT (1 << 0) /* always reports percent */
#define HID_BATTERY_QUIRK_FEATURE (1 << 1) /* ask for feature report */
+#define HID_BATTERY_QUIRK_IGNORE (1 << 2) /* completely ignore the battery */
static const struct hid_device_id hid_battery_quirks[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
@@ -320,6 +321,9 @@ static const struct hid_device_id hid_battery_quirks[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI),
HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM,
+ USB_DEVICE_ID_ELECOM_BM084),
+ HID_BATTERY_QUIRK_IGNORE },
{}
};
@@ -408,6 +412,14 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
if (dev->battery != NULL)
goto out; /* already initialized? */
+ quirks = find_battery_quirk(dev);
+
+ hid_dbg(dev, "device %x:%x:%x %d quirks %d\n",
+ dev->bus, dev->vendor, dev->product, dev->version, quirks);
+
+ if (quirks & HID_BATTERY_QUIRK_IGNORE)
+ goto out;
+
psy_desc = kzalloc(sizeof(*psy_desc), GFP_KERNEL);
if (psy_desc == NULL)
goto out;
@@ -424,11 +436,6 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
psy_desc->use_for_apm = 0;
psy_desc->get_property = hidinput_get_battery_property;
- quirks = find_battery_quirk(dev);
-
- hid_dbg(dev, "device %x:%x:%x %d quirks %d\n",
- dev->bus, dev->vendor, dev->product, dev->version, quirks);
-
min = field->logical_minimum;
max = field->logical_maximum;
@@ -960,6 +967,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
goto ignore;
case HID_UP_LOGIVENDOR:
+ /* intentional fallback */
+ case HID_UP_LOGIVENDOR2:
+ /* intentional fallback */
+ case HID_UP_LOGIVENDOR3:
goto ignore;
case HID_UP_PID:
diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c
index 8979f1fd5208..0125e356bd8d 100644
--- a/drivers/hid/hid-lenovo.c
+++ b/drivers/hid/hid-lenovo.c
@@ -220,7 +220,7 @@ static ssize_t attr_fn_lock_show_cptkbd(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", cptkbd_data->fn_lock);
@@ -231,7 +231,7 @@ static ssize_t attr_fn_lock_store_cptkbd(struct device *dev,
const char *buf,
size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
int value;
@@ -250,7 +250,7 @@ static ssize_t attr_sensitivity_show_cptkbd(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n",
@@ -262,7 +262,7 @@ static ssize_t attr_sensitivity_store_cptkbd(struct device *dev,
const char *buf,
size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
int value;
@@ -387,7 +387,7 @@ static ssize_t attr_press_to_select_show_tpkbd(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select);
@@ -398,7 +398,7 @@ static ssize_t attr_press_to_select_store_tpkbd(struct device *dev,
const char *buf,
size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
int value;
@@ -417,7 +417,7 @@ static ssize_t attr_dragging_show_tpkbd(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging);
@@ -428,7 +428,7 @@ static ssize_t attr_dragging_store_tpkbd(struct device *dev,
const char *buf,
size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
int value;
@@ -447,7 +447,7 @@ static ssize_t attr_release_to_select_show_tpkbd(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select);
@@ -458,7 +458,7 @@ static ssize_t attr_release_to_select_store_tpkbd(struct device *dev,
const char *buf,
size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
int value;
@@ -477,7 +477,7 @@ static ssize_t attr_select_right_show_tpkbd(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right);
@@ -488,7 +488,7 @@ static ssize_t attr_select_right_store_tpkbd(struct device *dev,
const char *buf,
size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
int value;
@@ -507,7 +507,7 @@ static ssize_t attr_sensitivity_show_tpkbd(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n",
@@ -519,7 +519,7 @@ static ssize_t attr_sensitivity_store_tpkbd(struct device *dev,
const char *buf,
size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
int value;
@@ -536,7 +536,7 @@ static ssize_t attr_press_speed_show_tpkbd(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n",
@@ -548,7 +548,7 @@ static ssize_t attr_press_speed_store_tpkbd(struct device *dev,
const char *buf,
size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
int value;
@@ -609,7 +609,7 @@ static enum led_brightness lenovo_led_brightness_get_tpkbd(
struct led_classdev *led_cdev)
{
struct device *dev = led_cdev->dev->parent;
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
int led_nr = 0;
@@ -625,7 +625,7 @@ static void lenovo_led_brightness_set_tpkbd(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct device *dev = led_cdev->dev->parent;
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
struct hid_report *report;
int led_nr = 0;
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index c20ac76c0a8c..c690fae02cf8 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -665,8 +665,9 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
struct lg_drv_data *drv_data;
int ret;
- /* Only work with the 1st interface (G29 presents multiple) */
- if (iface_num != 0) {
+ /* G29 only work with the 1st interface */
+ if ((hdev->product == USB_DEVICE_ID_LOGITECH_G29_WHEEL) &&
+ (iface_num != 0)) {
dbg_hid("%s: ignoring ifnum %d\n", __func__, iface_num);
return -ENODEV;
}
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index fbddcb37ae98..af3a8ec8a746 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -33,8 +33,6 @@
#include "hid-lg4ff.h"
#include "hid-ids.h"
-#define to_hid_device(pdev) container_of(pdev, struct hid_device, dev)
-
#define LG4FF_MMODE_IS_MULTIMODE 0
#define LG4FF_MMODE_SWITCHED 1
#define LG4FF_MMODE_NOT_MULTIMODE 2
@@ -1020,7 +1018,7 @@ static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct device *dev = led_cdev->dev->parent;
- struct hid_device *hid = container_of(dev, struct hid_device, dev);
+ struct hid_device *hid = to_hid_device(dev);
struct lg_drv_data *drv_data = hid_get_drvdata(hid);
struct lg4ff_device_entry *entry;
int i, state = 0;
@@ -1055,7 +1053,7 @@ static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cdev)
{
struct device *dev = led_cdev->dev->parent;
- struct hid_device *hid = container_of(dev, struct hid_device, dev);
+ struct hid_device *hid = to_hid_device(dev);
struct lg_drv_data *drv_data = hid_get_drvdata(hid);
struct lg4ff_device_entry *entry;
int i, value = 0;
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 5fd97860aec4..bd2ab476c65e 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -40,18 +40,22 @@ MODULE_PARM_DESC(disable_tap_to_click,
#define REPORT_ID_HIDPP_SHORT 0x10
#define REPORT_ID_HIDPP_LONG 0x11
+#define REPORT_ID_HIDPP_VERY_LONG 0x12
#define HIDPP_REPORT_SHORT_LENGTH 7
#define HIDPP_REPORT_LONG_LENGTH 20
+#define HIDPP_REPORT_VERY_LONG_LENGTH 64
#define HIDPP_QUIRK_CLASS_WTP BIT(0)
#define HIDPP_QUIRK_CLASS_M560 BIT(1)
#define HIDPP_QUIRK_CLASS_K400 BIT(2)
+#define HIDPP_QUIRK_CLASS_G920 BIT(3)
/* bits 2..20 are reserved for classes */
#define HIDPP_QUIRK_CONNECT_EVENTS BIT(21)
#define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22)
#define HIDPP_QUIRK_NO_HIDINPUT BIT(23)
+#define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS BIT(24)
#define HIDPP_QUIRK_DELAYED_INIT (HIDPP_QUIRK_NO_HIDINPUT | \
HIDPP_QUIRK_CONNECT_EVENTS)
@@ -81,13 +85,13 @@ MODULE_PARM_DESC(disable_tap_to_click,
struct fap {
u8 feature_index;
u8 funcindex_clientid;
- u8 params[HIDPP_REPORT_LONG_LENGTH - 4U];
+ u8 params[HIDPP_REPORT_VERY_LONG_LENGTH - 4U];
};
struct rap {
u8 sub_id;
u8 reg_address;
- u8 params[HIDPP_REPORT_LONG_LENGTH - 4U];
+ u8 params[HIDPP_REPORT_VERY_LONG_LENGTH - 4U];
};
struct hidpp_report {
@@ -144,8 +148,11 @@ static void hidpp_connect_event(struct hidpp_device *hidpp_dev);
static int __hidpp_send_report(struct hid_device *hdev,
struct hidpp_report *hidpp_report)
{
+ struct hidpp_device *hidpp = hid_get_drvdata(hdev);
int fields_count, ret;
+ hidpp = hid_get_drvdata(hdev);
+
switch (hidpp_report->report_id) {
case REPORT_ID_HIDPP_SHORT:
fields_count = HIDPP_REPORT_SHORT_LENGTH;
@@ -153,6 +160,9 @@ static int __hidpp_send_report(struct hid_device *hdev,
case REPORT_ID_HIDPP_LONG:
fields_count = HIDPP_REPORT_LONG_LENGTH;
break;
+ case REPORT_ID_HIDPP_VERY_LONG:
+ fields_count = HIDPP_REPORT_VERY_LONG_LENGTH;
+ break;
default:
return -ENODEV;
}
@@ -163,9 +173,13 @@ static int __hidpp_send_report(struct hid_device *hdev,
*/
hidpp_report->device_index = 0xff;
- ret = hid_hw_raw_request(hdev, hidpp_report->report_id,
- (u8 *)hidpp_report, fields_count, HID_OUTPUT_REPORT,
- HID_REQ_SET_REPORT);
+ if (hidpp->quirks & HIDPP_QUIRK_FORCE_OUTPUT_REPORTS) {
+ ret = hid_hw_output_report(hdev, (u8 *)hidpp_report, fields_count);
+ } else {
+ ret = hid_hw_raw_request(hdev, hidpp_report->report_id,
+ (u8 *)hidpp_report, fields_count, HID_OUTPUT_REPORT,
+ HID_REQ_SET_REPORT);
+ }
return ret == fields_count ? 0 : -1;
}
@@ -217,8 +231,9 @@ static int hidpp_send_message_sync(struct hidpp_device *hidpp,
goto exit;
}
- if (response->report_id == REPORT_ID_HIDPP_LONG &&
- response->fap.feature_index == HIDPP20_ERROR) {
+ if ((response->report_id == REPORT_ID_HIDPP_LONG ||
+ response->report_id == REPORT_ID_HIDPP_VERY_LONG) &&
+ response->fap.feature_index == HIDPP20_ERROR) {
ret = response->fap.params[1];
dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret);
goto exit;
@@ -243,7 +258,11 @@ static int hidpp_send_fap_command_sync(struct hidpp_device *hidpp,
message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL);
if (!message)
return -ENOMEM;
- message->report_id = REPORT_ID_HIDPP_LONG;
+
+ if (param_count > (HIDPP_REPORT_LONG_LENGTH - 4))
+ message->report_id = REPORT_ID_HIDPP_VERY_LONG;
+ else
+ message->report_id = REPORT_ID_HIDPP_LONG;
message->fap.feature_index = feat_index;
message->fap.funcindex_clientid = funcindex_clientid;
memcpy(&message->fap.params, params, param_count);
@@ -258,13 +277,23 @@ static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev,
struct hidpp_report *response)
{
struct hidpp_report *message;
- int ret;
+ int ret, max_count;
- if ((report_id != REPORT_ID_HIDPP_SHORT) &&
- (report_id != REPORT_ID_HIDPP_LONG))
+ switch (report_id) {
+ case REPORT_ID_HIDPP_SHORT:
+ max_count = HIDPP_REPORT_SHORT_LENGTH - 4;
+ break;
+ case REPORT_ID_HIDPP_LONG:
+ max_count = HIDPP_REPORT_LONG_LENGTH - 4;
+ break;
+ case REPORT_ID_HIDPP_VERY_LONG:
+ max_count = HIDPP_REPORT_VERY_LONG_LENGTH - 4;
+ break;
+ default:
return -EINVAL;
+ }
- if (param_count > sizeof(message->rap.params))
+ if (param_count > max_count)
return -EINVAL;
message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL);
@@ -508,10 +537,19 @@ static int hidpp_devicenametype_get_device_name(struct hidpp_device *hidpp,
if (ret)
return ret;
- if (response.report_id == REPORT_ID_HIDPP_LONG)
+ switch (response.report_id) {
+ case REPORT_ID_HIDPP_VERY_LONG:
+ count = HIDPP_REPORT_VERY_LONG_LENGTH - 4;
+ break;
+ case REPORT_ID_HIDPP_LONG:
count = HIDPP_REPORT_LONG_LENGTH - 4;
- else
+ break;
+ case REPORT_ID_HIDPP_SHORT:
count = HIDPP_REPORT_SHORT_LENGTH - 4;
+ break;
+ default:
+ return -EPROTO;
+ }
if (len_buf < count)
count = len_buf;
@@ -1257,6 +1295,131 @@ static int k400_connect(struct hid_device *hdev, bool connected)
return k400_disable_tap_to_click(hidpp);
}
+/* ------------------------------------------------------------------------- */
+/* Logitech G920 Driving Force Racing Wheel for Xbox One */
+/* ------------------------------------------------------------------------- */
+
+#define HIDPP_PAGE_G920_FORCE_FEEDBACK 0x8123
+
+/* Using session ID = 1 */
+#define CMD_G920_FORCE_GET_APERTURE 0x51
+#define CMD_G920_FORCE_SET_APERTURE 0x61
+
+struct g920_private_data {
+ u8 force_feature;
+ u16 range;
+};
+
+static ssize_t g920_range_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct hid_device *hid = to_hid_device(dev);
+ struct hidpp_device *hidpp = hid_get_drvdata(hid);
+ struct g920_private_data *pdata;
+
+ pdata = hidpp->private_data;
+ if (!pdata) {
+ hid_err(hid, "Private driver data not found!\n");
+ return -EINVAL;
+ }
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", pdata->range);
+}
+
+static ssize_t g920_range_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct hid_device *hid = to_hid_device(dev);
+ struct hidpp_device *hidpp = hid_get_drvdata(hid);
+ struct g920_private_data *pdata;
+ struct hidpp_report response;
+ u8 params[2];
+ int ret;
+ u16 range = simple_strtoul(buf, NULL, 10);
+
+ pdata = hidpp->private_data;
+ if (!pdata) {
+ hid_err(hid, "Private driver data not found!\n");
+ return -EINVAL;
+ }
+
+ if (range < 180)
+ range = 180;
+ else if (range > 900)
+ range = 900;
+
+ params[0] = range >> 8;
+ params[1] = range & 0x00FF;
+
+ ret = hidpp_send_fap_command_sync(hidpp, pdata->force_feature,
+ CMD_G920_FORCE_SET_APERTURE, params, 2, &response);
+ if (ret)
+ return ret;
+
+ pdata->range = range;
+ return count;
+}
+
+static DEVICE_ATTR(range, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, g920_range_show, g920_range_store);
+
+static int g920_allocate(struct hid_device *hdev)
+{
+ struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+ struct g920_private_data *pdata;
+
+ pdata = devm_kzalloc(&hdev->dev, sizeof(struct g920_private_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ hidpp->private_data = pdata;
+
+ return 0;
+}
+
+static int g920_get_config(struct hidpp_device *hidpp)
+{
+ struct g920_private_data *pdata = hidpp->private_data;
+ struct hidpp_report response;
+ u8 feature_type;
+ u8 feature_index;
+ int ret;
+
+ pdata = hidpp->private_data;
+ if (!pdata) {
+ hid_err(hidpp->hid_dev, "Private driver data not found!\n");
+ return -EINVAL;
+ }
+
+ /* Find feature and store for later use */
+ ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_G920_FORCE_FEEDBACK,
+ &feature_index, &feature_type);
+ if (ret)
+ return ret;
+
+ pdata->force_feature = feature_index;
+
+ /* Read current Range */
+ ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+ CMD_G920_FORCE_GET_APERTURE, NULL, 0, &response);
+ if (ret > 0) {
+ hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
+ __func__, ret);
+ return -EPROTO;
+ }
+ if (ret)
+ return ret;
+
+ pdata->range = get_unaligned_be16(&response.fap.params[0]);
+
+ /* Create sysfs interface */
+ ret = device_create_file(&(hidpp->hid_dev->dev), &dev_attr_range);
+ if (ret)
+ hid_warn(hidpp->hid_dev, "Unable to create sysfs interface for \"range\", errno %d\n", ret);
+
+ return 0;
+}
+
/* -------------------------------------------------------------------------- */
/* Generic HID++ devices */
/* -------------------------------------------------------------------------- */
@@ -1276,6 +1439,25 @@ static int hidpp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
return 0;
}
+static int hidpp_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+
+ /* Ensure that Logitech G920 is not given a default fuzz/flat value */
+ if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
+ if (usage->type == EV_ABS && (usage->code == ABS_X ||
+ usage->code == ABS_Y || usage->code == ABS_Z ||
+ usage->code == ABS_RZ)) {
+ field->application = HID_GD_MULTIAXIS;
+ }
+ }
+
+ return 0;
+}
+
+
static void hidpp_populate_input(struct hidpp_device *hidpp,
struct input_dev *input, bool origin_is_hid_core)
{
@@ -1347,6 +1529,14 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
/* Generic HID++ processing. */
switch (data[0]) {
+ case REPORT_ID_HIDPP_VERY_LONG:
+ if (size != HIDPP_REPORT_VERY_LONG_LENGTH) {
+ hid_err(hdev, "received hid++ report of bad size (%d)",
+ size);
+ return 1;
+ }
+ ret = hidpp_raw_hidpp_event(hidpp, data, size);
+ break;
case REPORT_ID_HIDPP_LONG:
if (size != HIDPP_REPORT_LONG_LENGTH) {
hid_err(hdev, "received hid++ report of bad size (%d)",
@@ -1393,10 +1583,12 @@ static void hidpp_overwrite_name(struct hid_device *hdev, bool use_unifying)
else
name = hidpp_get_device_name(hidpp);
- if (!name)
+ if (!name) {
hid_err(hdev, "unable to retrieve the name of the device");
- else
+ } else {
+ dbg_hid("HID++: Got name: %s\n", name);
snprintf(hdev->name, sizeof(hdev->name), "%s", name);
+ }
kfree(name);
}
@@ -1547,6 +1739,10 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
ret = k400_allocate(hdev);
if (ret)
goto allocate_fail;
+ } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
+ ret = g920_allocate(hdev);
+ if (ret)
+ goto allocate_fail;
}
INIT_WORK(&hidpp->work, delayed_work_cb);
@@ -1559,6 +1755,25 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto hid_parse_fail;
}
+ if (hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT)
+ connect_mask &= ~HID_CONNECT_HIDINPUT;
+
+ if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
+ ret = hid_hw_start(hdev, connect_mask);
+ if (ret) {
+ hid_err(hdev, "hw start failed\n");
+ goto hid_hw_start_fail;
+ }
+ ret = hid_hw_open(hdev);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "%s:hid_hw_open returned error:%d\n",
+ __func__, ret);
+ hid_hw_stop(hdev);
+ goto hid_hw_start_fail;
+ }
+ }
+
+
/* Allow incoming packets */
hid_device_io_start(hdev);
@@ -1567,8 +1782,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (!connected) {
ret = -ENODEV;
hid_err(hdev, "Device not connected");
- hid_device_io_stop(hdev);
- goto hid_parse_fail;
+ goto hid_hw_open_failed;
}
hid_info(hdev, "HID++ %u.%u device connected.\n",
@@ -1581,19 +1795,22 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) {
ret = wtp_get_config(hidpp);
if (ret)
- goto hid_parse_fail;
+ goto hid_hw_open_failed;
+ } else if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_G920)) {
+ ret = g920_get_config(hidpp);
+ if (ret)
+ goto hid_hw_open_failed;
}
/* Block incoming packets */
hid_device_io_stop(hdev);
- if (hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT)
- connect_mask &= ~HID_CONNECT_HIDINPUT;
-
- ret = hid_hw_start(hdev, connect_mask);
- if (ret) {
- hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
- goto hid_hw_start_fail;
+ if (!(hidpp->quirks & HIDPP_QUIRK_CLASS_G920)) {
+ ret = hid_hw_start(hdev, connect_mask);
+ if (ret) {
+ hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
+ goto hid_hw_start_fail;
+ }
}
if (hidpp->quirks & HIDPP_QUIRK_CONNECT_EVENTS) {
@@ -1605,6 +1822,13 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
return ret;
+hid_hw_open_failed:
+ hid_device_io_stop(hdev);
+ if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
+ device_remove_file(&hdev->dev, &dev_attr_range);
+ hid_hw_close(hdev);
+ hid_hw_stop(hdev);
+ }
hid_hw_start_fail:
hid_parse_fail:
cancel_work_sync(&hidpp->work);
@@ -1618,9 +1842,13 @@ static void hidpp_remove(struct hid_device *hdev)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+ if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
+ device_remove_file(&hdev->dev, &dev_attr_range);
+ hid_hw_close(hdev);
+ }
+ hid_hw_stop(hdev);
cancel_work_sync(&hidpp->work);
mutex_destroy(&hidpp->send_mutex);
- hid_hw_stop(hdev);
}
static const struct hid_device_id hidpp_devices[] = {
@@ -1648,6 +1876,9 @@ static const struct hid_device_id hidpp_devices[] = {
{ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},
+
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL),
+ .driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
{}
};
@@ -1661,6 +1892,7 @@ static struct hid_driver hidpp_driver = {
.raw_event = hidpp_raw_event,
.input_configured = hidpp_input_configured,
.input_mapping = hidpp_input_mapping,
+ .input_mapped = hidpp_input_mapped,
};
module_hid_driver(hidpp_driver);
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 3d664d01305e..296d4991560e 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -272,7 +272,7 @@ static ssize_t mt_show_quirks(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct mt_device *td = hid_get_drvdata(hdev);
return sprintf(buf, "%u\n", td->mtclass.quirks);
@@ -282,7 +282,7 @@ static ssize_t mt_set_quirks(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct mt_device *td = hid_get_drvdata(hdev);
unsigned long val;
@@ -357,8 +357,19 @@ static void mt_feature_mapping(struct hid_device *hdev,
break;
}
- td->inputmode = field->report->id;
- td->inputmode_index = usage->usage_index;
+ if (td->inputmode < 0) {
+ td->inputmode = field->report->id;
+ td->inputmode_index = usage->usage_index;
+ } else {
+ /*
+ * Some elan panels wrongly declare 2 input mode
+ * features, and silently ignore when we set the
+ * value in the second field. Skip the second feature
+ * and hope for the best.
+ */
+ dev_info(&hdev->dev,
+ "Ignoring the extra HID_DG_INPUTMODE\n");
+ }
break;
case HID_DG_CONTACTMAX:
@@ -486,6 +497,11 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
mt_store_field(usage, td, hi);
return 1;
case HID_DG_CONFIDENCE:
+ if (cls->name == MT_CLS_WIN_8 &&
+ field->application == HID_DG_TOUCHPAD) {
+ cls->quirks &= ~MT_QUIRK_ALWAYS_VALID;
+ cls->quirks |= MT_QUIRK_VALID_IS_CONFIDENCE;
+ }
mt_store_field(usage, td, hi);
return 1;
case HID_DG_TIPSWITCH:
diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c
index 756d1ef9bd99..1b0084d4af2e 100644
--- a/drivers/hid/hid-ntrig.c
+++ b/drivers/hid/hid-ntrig.c
@@ -173,7 +173,7 @@ static ssize_t show_phys_width(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
return sprintf(buf, "%d\n", nd->sensor_physical_width);
@@ -185,7 +185,7 @@ static ssize_t show_phys_height(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
return sprintf(buf, "%d\n", nd->sensor_physical_height);
@@ -197,7 +197,7 @@ static ssize_t show_log_width(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
return sprintf(buf, "%d\n", nd->sensor_logical_width);
@@ -209,7 +209,7 @@ static ssize_t show_log_height(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
return sprintf(buf, "%d\n", nd->sensor_logical_height);
@@ -221,7 +221,7 @@ static ssize_t show_min_width(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
return sprintf(buf, "%d\n", nd->min_width *
@@ -233,7 +233,7 @@ static ssize_t set_min_width(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
unsigned long val;
@@ -256,7 +256,7 @@ static ssize_t show_min_height(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
return sprintf(buf, "%d\n", nd->min_height *
@@ -268,7 +268,7 @@ static ssize_t set_min_height(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
unsigned long val;
@@ -292,7 +292,7 @@ static ssize_t show_activate_slack(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
return sprintf(buf, "%d\n", nd->activate_slack);
@@ -302,7 +302,7 @@ static ssize_t set_activate_slack(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
unsigned long val;
@@ -325,7 +325,7 @@ static ssize_t show_activation_width(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
return sprintf(buf, "%d\n", nd->activation_width *
@@ -337,7 +337,7 @@ static ssize_t set_activation_width(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
unsigned long val;
@@ -361,7 +361,7 @@ static ssize_t show_activation_height(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
return sprintf(buf, "%d\n", nd->activation_height *
@@ -373,7 +373,7 @@ static ssize_t set_activation_height(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
unsigned long val;
@@ -397,7 +397,7 @@ static ssize_t show_deactivate_slack(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
return sprintf(buf, "%d\n", -nd->deactivate_slack);
@@ -407,7 +407,7 @@ static ssize_t set_deactivate_slack(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct ntrig_data *nd = hid_get_drvdata(hdev);
unsigned long val;
diff --git a/drivers/hid/hid-picolcd_leds.c b/drivers/hid/hid-picolcd_leds.c
index e994f9c29012..a802b4f49c7b 100644
--- a/drivers/hid/hid-picolcd_leds.c
+++ b/drivers/hid/hid-picolcd_leds.c
@@ -66,7 +66,7 @@ static void picolcd_led_set_brightness(struct led_classdev *led_cdev,
int i, state = 0;
dev = led_cdev->dev->parent;
- hdev = container_of(dev, struct hid_device, dev);
+ hdev = to_hid_device(dev);
data = hid_get_drvdata(hdev);
if (!data)
return;
@@ -93,7 +93,7 @@ static enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_c
int i, value = 0;
dev = led_cdev->dev->parent;
- hdev = container_of(dev, struct hid_device, dev);
+ hdev = to_hid_device(dev);
data = hid_get_drvdata(hdev);
for (i = 0; i < 8; i++)
if (led_cdev == data->led[i]) {
diff --git a/drivers/hid/hid-prodikeys.c b/drivers/hid/hid-prodikeys.c
index 3a207c0ac0e3..f095bf8a3aa9 100644
--- a/drivers/hid/hid-prodikeys.c
+++ b/drivers/hid/hid-prodikeys.c
@@ -103,7 +103,7 @@ MODULE_PARM_DESC(enable, "Enable for the PC-MIDI virtual audio driver");
static ssize_t show_channel(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct pk_device *pk = hid_get_drvdata(hdev);
dbg_hid("pcmidi sysfs read channel=%u\n", pk->pm->midi_channel);
@@ -116,7 +116,7 @@ static ssize_t show_channel(struct device *dev,
static ssize_t store_channel(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct pk_device *pk = hid_get_drvdata(hdev);
unsigned channel = 0;
@@ -140,7 +140,7 @@ static struct device_attribute *sysfs_device_attr_channel = {
static ssize_t show_sustain(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct pk_device *pk = hid_get_drvdata(hdev);
dbg_hid("pcmidi sysfs read sustain=%u\n", pk->pm->midi_sustain);
@@ -153,7 +153,7 @@ static ssize_t show_sustain(struct device *dev,
static ssize_t store_sustain(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct pk_device *pk = hid_get_drvdata(hdev);
unsigned sustain = 0;
@@ -179,7 +179,7 @@ static struct device_attribute *sysfs_device_attr_sustain = {
static ssize_t show_octave(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct pk_device *pk = hid_get_drvdata(hdev);
dbg_hid("pcmidi sysfs read octave=%d\n", pk->pm->midi_octave);
@@ -192,7 +192,7 @@ static ssize_t show_octave(struct device *dev,
static ssize_t store_octave(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct pk_device *pk = hid_get_drvdata(hdev);
int octave = 0;
diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c
index 1948208fe038..329c5d1270f9 100644
--- a/drivers/hid/hid-roccat-arvo.c
+++ b/drivers/hid/hid-roccat-arvo.c
@@ -191,8 +191,7 @@ static ssize_t arvo_sysfs_write(struct file *fp,
struct kobject *kobj, void const *buf,
loff_t off, size_t count, size_t real_size, uint command)
{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct arvo_device *arvo = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval;
@@ -211,8 +210,7 @@ static ssize_t arvo_sysfs_read(struct file *fp,
struct kobject *kobj, void *buf, loff_t off,
size_t count, size_t real_size, uint command)
{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct arvo_device *arvo = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval;
diff --git a/drivers/hid/hid-roccat-common.c b/drivers/hid/hid-roccat-common.c
index 02e28e9f4ea7..8155ac5fede2 100644
--- a/drivers/hid/hid-roccat-common.c
+++ b/drivers/hid/hid-roccat-common.c
@@ -134,8 +134,7 @@ ssize_t roccat_common2_sysfs_read(struct file *fp, struct kobject *kobj,
char *buf, loff_t off, size_t count,
size_t real_size, uint command)
{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct roccat_common2_device *roccat_dev = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval;
@@ -158,8 +157,7 @@ ssize_t roccat_common2_sysfs_write(struct file *fp, struct kobject *kobj,
void const *buf, loff_t off, size_t count,
size_t real_size, uint command)
{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct roccat_common2_device *roccat_dev = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval;
diff --git a/drivers/hid/hid-roccat-isku.c b/drivers/hid/hid-roccat-isku.c
index bc62ed91e451..02db537f8f3e 100644
--- a/drivers/hid/hid-roccat-isku.c
+++ b/drivers/hid/hid-roccat-isku.c
@@ -121,8 +121,7 @@ static ssize_t isku_sysfs_read(struct file *fp, struct kobject *kobj,
char *buf, loff_t off, size_t count,
size_t real_size, uint command)
{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct isku_device *isku = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval;
@@ -144,8 +143,7 @@ static ssize_t isku_sysfs_write(struct file *fp, struct kobject *kobj,
void const *buf, loff_t off, size_t count,
size_t real_size, uint command)
{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct isku_device *isku = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval;
diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c
index c29265055ac1..bf4675a27396 100644
--- a/drivers/hid/hid-roccat-kone.c
+++ b/drivers/hid/hid-roccat-kone.c
@@ -269,8 +269,7 @@ static int kone_get_firmware_version(struct usb_device *usb_dev, int *result)
static ssize_t kone_sysfs_read_settings(struct file *fp, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count) {
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
if (off >= sizeof(struct kone_settings))
@@ -294,8 +293,7 @@ static ssize_t kone_sysfs_read_settings(struct file *fp, struct kobject *kobj,
static ssize_t kone_sysfs_write_settings(struct file *fp, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count) {
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval = 0, difference, old_profile;
@@ -332,8 +330,7 @@ static BIN_ATTR(settings, 0660, kone_sysfs_read_settings,
static ssize_t kone_sysfs_read_profilex(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr,
char *buf, loff_t off, size_t count) {
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
if (off >= sizeof(struct kone_profile))
@@ -353,8 +350,7 @@ static ssize_t kone_sysfs_read_profilex(struct file *fp,
static ssize_t kone_sysfs_write_profilex(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr,
char *buf, loff_t off, size_t count) {
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
struct kone_profile *profile;
diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c
index 5e99fcdc71b9..09e8fc72aa1d 100644
--- a/drivers/hid/hid-roccat-koneplus.c
+++ b/drivers/hid/hid-roccat-koneplus.c
@@ -87,8 +87,7 @@ static ssize_t koneplus_sysfs_read(struct file *fp, struct kobject *kobj,
char *buf, loff_t off, size_t count,
size_t real_size, uint command)
{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct koneplus_device *koneplus = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval;
@@ -113,8 +112,7 @@ static ssize_t koneplus_sysfs_write(struct file *fp, struct kobject *kobj,
void const *buf, loff_t off, size_t count,
size_t real_size, uint command)
{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct koneplus_device *koneplus = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval;
@@ -193,8 +191,7 @@ static ssize_t koneplus_sysfs_read_profilex_settings(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
ssize_t retval;
@@ -212,8 +209,7 @@ static ssize_t koneplus_sysfs_read_profilex_buttons(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
ssize_t retval;
diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c
index 966047711fbf..43617fb28b87 100644
--- a/drivers/hid/hid-roccat-kovaplus.c
+++ b/drivers/hid/hid-roccat-kovaplus.c
@@ -128,8 +128,7 @@ static ssize_t kovaplus_sysfs_read(struct file *fp, struct kobject *kobj,
char *buf, loff_t off, size_t count,
size_t real_size, uint command)
{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval;
@@ -154,8 +153,7 @@ static ssize_t kovaplus_sysfs_write(struct file *fp, struct kobject *kobj,
void const *buf, loff_t off, size_t count,
size_t real_size, uint command)
{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval;
@@ -221,8 +219,7 @@ static ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
ssize_t retval;
@@ -240,8 +237,7 @@ static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
ssize_t retval;
diff --git a/drivers/hid/hid-roccat-lua.c b/drivers/hid/hid-roccat-lua.c
index 65e2e76bf2fe..ac1a7313e259 100644
--- a/drivers/hid/hid-roccat-lua.c
+++ b/drivers/hid/hid-roccat-lua.c
@@ -30,7 +30,7 @@ static ssize_t lua_sysfs_read(struct file *fp, struct kobject *kobj,
char *buf, loff_t off, size_t count,
size_t real_size, uint command)
{
- struct device *dev = container_of(kobj, struct device, kobj);
+ struct device *dev = kobj_to_dev(kobj);
struct lua_device *lua = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval;
@@ -52,7 +52,7 @@ static ssize_t lua_sysfs_write(struct file *fp, struct kobject *kobj,
void const *buf, loff_t off, size_t count,
size_t real_size, uint command)
{
- struct device *dev = container_of(kobj, struct device, kobj);
+ struct device *dev = kobj_to_dev(kobj);
struct lua_device *lua = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval;
diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c
index 47d7e74231e5..b30aa7b82bf8 100644
--- a/drivers/hid/hid-roccat-pyra.c
+++ b/drivers/hid/hid-roccat-pyra.c
@@ -90,8 +90,7 @@ static ssize_t pyra_sysfs_read(struct file *fp, struct kobject *kobj,
char *buf, loff_t off, size_t count,
size_t real_size, uint command)
{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval;
@@ -116,8 +115,7 @@ static ssize_t pyra_sysfs_write(struct file *fp, struct kobject *kobj,
void const *buf, loff_t off, size_t count,
size_t real_size, uint command)
{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval;
@@ -191,8 +189,7 @@ static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
ssize_t retval;
@@ -210,8 +207,7 @@ static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
ssize_t retval;
@@ -248,8 +244,7 @@ static ssize_t pyra_sysfs_write_settings(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
- struct device *dev =
- container_of(kobj, struct device, kobj)->parent->parent;
+ struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval = 0;
diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c
index 92870cdb52d9..58ed8f25ab21 100644
--- a/drivers/hid/hid-sensor-hub.c
+++ b/drivers/hid/hid-sensor-hub.c
@@ -794,6 +794,9 @@ static const struct hid_device_id sensor_hub_devices[] = {
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_ITE,
USB_DEVICE_ID_ITE_LENOVO_YOGA2),
.driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
+ { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_ITE,
+ USB_DEVICE_ID_ITE_LENOVO_YOGA900),
+ .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, HID_ANY_ID,
HID_ANY_ID) },
{ }
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 774cd2210566..9b8db0e0ef1c 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1028,6 +1028,7 @@ struct sony_sc {
struct led_classdev *leds[MAX_LEDS];
unsigned long quirks;
struct work_struct state_worker;
+ void(*send_output_report)(struct sony_sc*);
struct power_supply *battery;
struct power_supply_desc battery_desc;
int device_id;
@@ -1044,6 +1045,7 @@ struct sony_sc {
__u8 battery_charging;
__u8 battery_capacity;
__u8 led_state[MAX_LEDS];
+ __u8 resume_led_state[MAX_LEDS];
__u8 led_delay_on[MAX_LEDS];
__u8 led_delay_off[MAX_LEDS];
__u8 led_count;
@@ -1137,11 +1139,11 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
* the gyroscope values to corresponding axes so we need a
* modified one.
*/
- if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && *rsize == 467) {
+ if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
hid_info(hdev, "Using modified Dualshock 4 report descriptor with gyroscope axes\n");
rdesc = dualshock4_usb_rdesc;
*rsize = sizeof(dualshock4_usb_rdesc);
- } else if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) && *rsize == 357) {
+ } else if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
hid_info(hdev, "Using modified Dualshock 4 Bluetooth report descriptor\n");
rdesc = dualshock4_bt_rdesc;
*rsize = sizeof(dualshock4_bt_rdesc);
@@ -1549,7 +1551,7 @@ static void sony_led_set_brightness(struct led_classdev *led,
enum led_brightness value)
{
struct device *dev = led->dev->parent;
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct sony_sc *drv_data;
int n;
@@ -1591,7 +1593,7 @@ static void sony_led_set_brightness(struct led_classdev *led,
static enum led_brightness sony_led_get_brightness(struct led_classdev *led)
{
struct device *dev = led->dev->parent;
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct sony_sc *drv_data;
int n;
@@ -1614,7 +1616,7 @@ static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on,
unsigned long *delay_off)
{
struct device *dev = led->dev->parent;
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct sony_sc *drv_data = hid_get_drvdata(hdev);
int n;
__u8 new_on, new_off;
@@ -1789,7 +1791,7 @@ error_leds:
return ret;
}
-static void sixaxis_state_worker(struct work_struct *work)
+static void sixaxis_send_output_report(struct sony_sc *sc)
{
static const union sixaxis_output_report_01 default_report = {
.buf = {
@@ -1803,7 +1805,6 @@ static void sixaxis_state_worker(struct work_struct *work)
0x00, 0x00, 0x00, 0x00, 0x00
}
};
- struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
struct sixaxis_output_report *report =
(struct sixaxis_output_report *)sc->output_report_dmabuf;
int n;
@@ -1846,9 +1847,8 @@ static void sixaxis_state_worker(struct work_struct *work)
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
}
-static void dualshock4_state_worker(struct work_struct *work)
+static void dualshock4_send_output_report(struct sony_sc *sc)
{
- struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
struct hid_device *hdev = sc->hdev;
__u8 *buf = sc->output_report_dmabuf;
int offset;
@@ -1893,9 +1893,8 @@ 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)
+static void motion_send_output_report(struct sony_sc *sc)
{
- 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;
@@ -1914,6 +1913,18 @@ static void motion_state_worker(struct work_struct *work)
hid_hw_output_report(hdev, (__u8 *)report, MOTION_REPORT_0x02_SIZE);
}
+static inline void sony_send_output_report(struct sony_sc *sc)
+{
+ if (sc->send_output_report)
+ sc->send_output_report(sc);
+}
+
+static void sony_state_worker(struct work_struct *work)
+{
+ struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
+ sc->send_output_report(sc);
+}
+
static int sony_allocate_output_report(struct sony_sc *sc)
{
if ((sc->quirks & SIXAXIS_CONTROLLER) ||
@@ -2241,11 +2252,13 @@ static void sony_release_device_id(struct sony_sc *sc)
}
}
-static inline void sony_init_work(struct sony_sc *sc,
- void (*worker)(struct work_struct *))
+static inline void sony_init_output_report(struct sony_sc *sc,
+ void(*send_output_report)(struct sony_sc*))
{
+ sc->send_output_report = send_output_report;
+
if (!sc->worker_initialized)
- INIT_WORK(&sc->state_worker, worker);
+ INIT_WORK(&sc->state_worker, sony_state_worker);
sc->worker_initialized = 1;
}
@@ -2319,7 +2332,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
ret = sixaxis_set_operational_usb(hdev);
- sony_init_work(sc, sixaxis_state_worker);
+ sony_init_output_report(sc, sixaxis_send_output_report);
} else if ((sc->quirks & SIXAXIS_CONTROLLER_BT) ||
(sc->quirks & NAVIGATION_CONTROLLER_BT)) {
/*
@@ -2328,7 +2341,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
*/
hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
ret = sixaxis_set_operational_bt(hdev);
- sony_init_work(sc, sixaxis_state_worker);
+ sony_init_output_report(sc, sixaxis_send_output_report);
} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
/*
@@ -2343,9 +2356,9 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
}
- sony_init_work(sc, dualshock4_state_worker);
+ sony_init_output_report(sc, dualshock4_send_output_report);
} else if (sc->quirks & MOTION_CONTROLLER) {
- sony_init_work(sc, motion_state_worker);
+ sony_init_output_report(sc, motion_send_output_report);
} else {
ret = 0;
}
@@ -2421,6 +2434,56 @@ static void sony_remove(struct hid_device *hdev)
hid_hw_stop(hdev);
}
+#ifdef CONFIG_PM
+
+static int sony_suspend(struct hid_device *hdev, pm_message_t message)
+{
+ /*
+ * On suspend save the current LED state,
+ * stop running force-feedback and blank the LEDS.
+ */
+ if (SONY_LED_SUPPORT || SONY_FF_SUPPORT) {
+ struct sony_sc *sc = hid_get_drvdata(hdev);
+
+#ifdef CONFIG_SONY_FF
+ sc->left = sc->right = 0;
+#endif
+
+ memcpy(sc->resume_led_state, sc->led_state,
+ sizeof(sc->resume_led_state));
+ memset(sc->led_state, 0, sizeof(sc->led_state));
+
+ sony_send_output_report(sc);
+ }
+
+ return 0;
+}
+
+static int sony_resume(struct hid_device *hdev)
+{
+ /* Restore the state of controller LEDs on resume */
+ if (SONY_LED_SUPPORT) {
+ struct sony_sc *sc = hid_get_drvdata(hdev);
+
+ memcpy(sc->led_state, sc->resume_led_state,
+ sizeof(sc->led_state));
+
+ /*
+ * The Sixaxis and navigation controllers on USB need to be
+ * reinitialized on resume or they won't behave properly.
+ */
+ if ((sc->quirks & SIXAXIS_CONTROLLER_USB) ||
+ (sc->quirks & NAVIGATION_CONTROLLER_USB))
+ sixaxis_set_operational_usb(sc->hdev);
+
+ sony_set_leds(sc);
+ }
+
+ return 0;
+}
+
+#endif
+
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 },
@@ -2470,7 +2533,13 @@ static struct hid_driver sony_driver = {
.probe = sony_probe,
.remove = sony_remove,
.report_fixup = sony_report_fixup,
- .raw_event = sony_raw_event
+ .raw_event = sony_raw_event,
+
+#ifdef CONFIG_PM
+ .suspend = sony_suspend,
+ .resume = sony_resume,
+ .reset_resume = sony_resume,
+#endif
};
static int __init sony_init(void)
diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c
index 3edd4ac36494..ec18768b124a 100644
--- a/drivers/hid/hid-steelseries.c
+++ b/drivers/hid/hid-steelseries.c
@@ -141,7 +141,7 @@ static void steelseries_srws1_led_all_set_brightness(struct led_classdev *led_cd
enum led_brightness value)
{
struct device *dev = led_cdev->dev->parent;
- struct hid_device *hid = container_of(dev, struct hid_device, dev);
+ struct hid_device *hid = to_hid_device(dev);
struct steelseries_srws1_data *drv_data = hid_get_drvdata(hid);
if (!drv_data) {
@@ -160,7 +160,7 @@ static void steelseries_srws1_led_all_set_brightness(struct led_classdev *led_cd
static enum led_brightness steelseries_srws1_led_all_get_brightness(struct led_classdev *led_cdev)
{
struct device *dev = led_cdev->dev->parent;
- struct hid_device *hid = container_of(dev, struct hid_device, dev);
+ struct hid_device *hid = to_hid_device(dev);
struct steelseries_srws1_data *drv_data;
drv_data = hid_get_drvdata(hid);
@@ -177,7 +177,7 @@ static void steelseries_srws1_led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct device *dev = led_cdev->dev->parent;
- struct hid_device *hid = container_of(dev, struct hid_device, dev);
+ struct hid_device *hid = to_hid_device(dev);
struct steelseries_srws1_data *drv_data = hid_get_drvdata(hid);
int i, state = 0;
@@ -205,7 +205,7 @@ static void steelseries_srws1_led_set_brightness(struct led_classdev *led_cdev,
static enum led_brightness steelseries_srws1_led_get_brightness(struct led_classdev *led_cdev)
{
struct device *dev = led_cdev->dev->parent;
- struct hid_device *hid = container_of(dev, struct hid_device, dev);
+ struct hid_device *hid = to_hid_device(dev);
struct steelseries_srws1_data *drv_data;
int i, value = 0;
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index 05e23c417d50..4390eee2ce84 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -296,14 +296,12 @@ static const struct wiimod_ops wiimod_battery = {
static enum led_brightness wiimod_led_get(struct led_classdev *led_dev)
{
- struct wiimote_data *wdata;
struct device *dev = led_dev->dev->parent;
+ struct wiimote_data *wdata = dev_to_wii(dev);
int i;
unsigned long flags;
bool value = false;
- wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev));
-
for (i = 0; i < 4; ++i) {
if (wdata->leds[i] == led_dev) {
spin_lock_irqsave(&wdata->state.lock, flags);
@@ -319,14 +317,12 @@ static enum led_brightness wiimod_led_get(struct led_classdev *led_dev)
static void wiimod_led_set(struct led_classdev *led_dev,
enum led_brightness value)
{
- struct wiimote_data *wdata;
struct device *dev = led_dev->dev->parent;
+ struct wiimote_data *wdata = dev_to_wii(dev);
int i;
unsigned long flags;
__u8 state, flag;
- wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev));
-
for (i = 0; i < 4; ++i) {
if (wdata->leds[i] == led_dev) {
flag = WIIPROTO_FLAG_LED(i + 1);
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
index 875694d43e4d..510ca77fe14e 100644
--- a/drivers/hid/hid-wiimote.h
+++ b/drivers/hid/hid-wiimote.h
@@ -256,8 +256,7 @@ enum wiiproto_reqs {
WIIPROTO_REQ_MAX
};
-#define dev_to_wii(pdev) hid_get_drvdata(container_of(pdev, struct hid_device, \
- dev))
+#define dev_to_wii(pdev) hid_get_drvdata(to_hid_device(pdev))
void __wiimote_schedule(struct wiimote_data *wdata);
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
index 10bd8e6e4c9c..b9216938a718 100644
--- a/drivers/hid/i2c-hid/i2c-hid.c
+++ b/drivers/hid/i2c-hid/i2c-hid.c
@@ -151,6 +151,7 @@ struct i2c_hid {
struct i2c_hid_platform_data pdata;
bool irq_wake_enabled;
+ struct mutex reset_lock;
};
static int __i2c_hid_command(struct i2c_client *client,
@@ -356,9 +357,16 @@ static int i2c_hid_hwreset(struct i2c_client *client)
i2c_hid_dbg(ihid, "%s\n", __func__);
+ /*
+ * This prevents sending feature reports while the device is
+ * being reset. Otherwise we may lose the reset complete
+ * interrupt.
+ */
+ mutex_lock(&ihid->reset_lock);
+
ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
if (ret)
- return ret;
+ goto out_unlock;
i2c_hid_dbg(ihid, "resetting...\n");
@@ -366,10 +374,11 @@ static int i2c_hid_hwreset(struct i2c_client *client)
if (ret) {
dev_err(&client->dev, "failed to reset device.\n");
i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
- return ret;
}
- return 0;
+out_unlock:
+ mutex_unlock(&ihid->reset_lock);
+ return ret;
}
static void i2c_hid_get_input(struct i2c_hid *ihid)
@@ -587,12 +596,15 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
size_t count, unsigned char report_type, bool use_data)
{
struct i2c_client *client = hid->driver_data;
+ struct i2c_hid *ihid = i2c_get_clientdata(client);
int report_id = buf[0];
int ret;
if (report_type == HID_INPUT_REPORT)
return -EINVAL;
+ mutex_lock(&ihid->reset_lock);
+
if (report_id) {
buf++;
count--;
@@ -605,6 +617,8 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
if (report_id && ret >= 0)
ret++; /* add report_id to the number of transfered bytes */
+ mutex_unlock(&ihid->reset_lock);
+
return ret;
}
@@ -990,6 +1004,7 @@ static int i2c_hid_probe(struct i2c_client *client,
ihid->wHIDDescRegister = cpu_to_le16(hidRegister);
init_waitqueue_head(&ihid->wait);
+ mutex_init(&ihid->reset_lock);
/* we need to allocate the command buffer without knowing the maximum
* size of the reports. Let's use HID_MIN_BUFFER_SIZE, then we do the
@@ -1184,7 +1199,6 @@ MODULE_DEVICE_TABLE(i2c, i2c_hid_id_table);
static struct i2c_driver i2c_hid_driver = {
.driver = {
.name = "i2c_hid",
- .owner = THIS_MODULE,
.pm = &i2c_hid_pm,
.acpi_match_table = ACPI_PTR(i2c_hid_acpi_match),
.of_match_table = of_match_ptr(i2c_hid_of_match),
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 36712e9f56c2..ad71160b9ea4 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -274,10 +274,10 @@ static void hid_irq_in(struct urb *urb)
switch (urb->status) {
case 0: /* success */
- usbhid_mark_busy(usbhid);
usbhid->retry_delay = 0;
if ((hid->quirks & HID_QUIRK_ALWAYS_POLL) && !hid->open)
break;
+ usbhid_mark_busy(usbhid);
if (!test_bit(HID_RESUME_RUNNING, &usbhid->iofl)) {
hid_input_report(urb->context, HID_INPUT_REPORT,
urb->transfer_buffer,
@@ -477,8 +477,6 @@ static void hid_ctrl(struct urb *urb)
struct usbhid_device *usbhid = hid->driver_data;
int unplug = 0, status = urb->status;
- spin_lock(&usbhid->lock);
-
switch (status) {
case 0: /* success */
if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN)
@@ -498,6 +496,8 @@ static void hid_ctrl(struct urb *urb)
hid_warn(urb->dev, "ctrl urb status %d received\n", status);
}
+ spin_lock(&usbhid->lock);
+
if (unplug) {
usbhid->ctrltail = usbhid->ctrlhead;
} else {
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 94bb137abe32..7dd0953cd70f 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -72,11 +72,7 @@ static const struct hid_blacklist {
{ 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_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU, HID_QUIRK_MULTI_INPUT },
- { 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 },
- { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_0103, HID_QUIRK_ALWAYS_POLL },
- { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_010c, HID_QUIRK_ALWAYS_POLL },
- { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_016F, HID_QUIRK_ALWAYS_POLL },
+ { USB_VENDOR_ID_ELAN, HID_ANY_ID, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
@@ -84,6 +80,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0B4A, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_C077, HID_QUIRK_ALWAYS_POLL },
+ { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KEYBOARD_G710_PLUS, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C01A, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C05A, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C06A, HID_QUIRK_ALWAYS_POLL },
@@ -339,7 +336,8 @@ static const struct hid_blacklist *usbhid_exists_squirk(const u16 idVendor,
for (; hid_blacklist[n].idVendor; n++)
if (hid_blacklist[n].idVendor == idVendor &&
- hid_blacklist[n].idProduct == idProduct)
+ (hid_blacklist[n].idProduct == (__u16) HID_ANY_ID ||
+ hid_blacklist[n].idProduct == idProduct))
bl_entry = &hid_blacklist[n];
if (bl_entry != NULL)
diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h
index 807922b49aa4..fa47d666cfcf 100644
--- a/drivers/hid/usbhid/usbhid.h
+++ b/drivers/hid/usbhid/usbhid.h
@@ -96,7 +96,7 @@ struct usbhid_device {
};
#define hid_to_usb_dev(hid_dev) \
- container_of(hid_dev->dev.parent->parent, struct usb_device, dev)
+ to_usb_device(hid_dev->dev.parent->parent)
#endif
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index e06af5b9f59e..5cb21dd91094 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -686,7 +686,7 @@ out:
static ssize_t wacom_led_select_store(struct device *dev, int set_id,
const char *buf, size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct wacom *wacom = hid_get_drvdata(hdev);
unsigned int id;
int err;
@@ -714,7 +714,7 @@ static ssize_t wacom_led##SET_ID##_select_store(struct device *dev, \
static ssize_t wacom_led##SET_ID##_select_show(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);\
+ struct hid_device *hdev = to_hid_device(dev);\
struct wacom *wacom = hid_get_drvdata(hdev); \
return scnprintf(buf, PAGE_SIZE, "%d\n", \
wacom->led.select[SET_ID]); \
@@ -750,7 +750,7 @@ static ssize_t wacom_luminance_store(struct wacom *wacom, u8 *dest,
static ssize_t wacom_##name##_luminance_store(struct device *dev, \
struct device_attribute *attr, const char *buf, size_t count) \
{ \
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);\
+ struct hid_device *hdev = to_hid_device(dev);\
struct wacom *wacom = hid_get_drvdata(hdev); \
\
return wacom_luminance_store(wacom, &wacom->led.field, \
@@ -773,7 +773,7 @@ DEVICE_LUMINANCE_ATTR(buttons, img_lum);
static ssize_t wacom_button_image_store(struct device *dev, int button_id,
const char *buf, size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct wacom *wacom = hid_get_drvdata(hdev);
int err;
unsigned len;
@@ -1097,7 +1097,7 @@ static ssize_t wacom_show_speed(struct device *dev,
struct device_attribute
*attr, char *buf)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct wacom *wacom = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%i\n", wacom->wacom_wac.bt_high_speed);
@@ -1107,7 +1107,7 @@ static ssize_t wacom_store_speed(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct hid_device *hdev = to_hid_device(dev);
struct wacom *wacom = hid_get_drvdata(hdev);
u8 new_speed;
@@ -1130,8 +1130,8 @@ static ssize_t wacom_show_remote_mode(struct kobject *kobj,
struct kobj_attribute *kattr,
char *buf, int index)
{
- struct device *dev = container_of(kobj->parent, struct device, kobj);
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct device *dev = kobj_to_dev(kobj->parent);
+ struct hid_device *hdev = to_hid_device(dev);
struct wacom *wacom = hid_get_drvdata(hdev);
u8 mode;
@@ -1241,8 +1241,8 @@ static ssize_t wacom_store_unpair_remote(struct kobject *kobj,
const char *buf, size_t count)
{
unsigned char selector = 0;
- struct device *dev = container_of(kobj->parent, struct device, kobj);
- struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct device *dev = kobj_to_dev(kobj->parent);
+ struct hid_device *hdev = to_hid_device(dev);
struct wacom *wacom = hid_get_drvdata(hdev);
int err;
@@ -1353,8 +1353,7 @@ static void wacom_clean_inputs(struct wacom *wacom)
else
input_free_device(wacom->wacom_wac.pad_input);
}
- if (wacom->remote_dir)
- kobject_put(wacom->remote_dir);
+ kobject_put(wacom->remote_dir);
wacom->wacom_wac.pen_input = NULL;
wacom->wacom_wac.touch_input = NULL;
wacom->wacom_wac.pad_input = NULL;
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index 01a4f05c1642..99ef77fcfb80 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -34,6 +34,9 @@
*/
#define WACOM_CONTACT_AREA_SCALE 2607
+static void wacom_report_numbered_buttons(struct input_dev *input_dev,
+ int button_count, int mask);
+
/*
* Percent of battery capacity for Graphire.
* 8th value means AC online and show 100% capacity.
@@ -436,16 +439,142 @@ exit:
static void wacom_intuos_schedule_prox_event(struct wacom_wac *wacom_wac)
{
struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
+ struct wacom_features *features = &wacom_wac->features;
struct hid_report *r;
struct hid_report_enum *re;
re = &(wacom->hdev->report_enum[HID_FEATURE_REPORT]);
- r = re->report_id_hash[WACOM_REPORT_INTUOSREAD];
+ if (features->type == INTUOSHT2)
+ r = re->report_id_hash[WACOM_REPORT_INTUOSHT2_ID];
+ else
+ r = re->report_id_hash[WACOM_REPORT_INTUOS_ID1];
if (r) {
hid_hw_request(wacom->hdev, r, HID_REQ_GET_REPORT);
}
}
+static int wacom_intuos_pad(struct wacom_wac *wacom)
+{
+ struct wacom_features *features = &wacom->features;
+ unsigned char *data = wacom->data;
+ struct input_dev *input = wacom->pad_input;
+ int i;
+ int buttons = 0, nbuttons = features->numbered_buttons;
+ int keys = 0, nkeys = 0;
+ int ring1 = 0, ring2 = 0;
+ int strip1 = 0, strip2 = 0;
+ bool prox = false;
+
+ /* pad packets. Works as a second tool and is always in prox */
+ if (!(data[0] == WACOM_REPORT_INTUOSPAD || data[0] == WACOM_REPORT_INTUOS5PAD ||
+ data[0] == WACOM_REPORT_CINTIQPAD))
+ return 0;
+
+ if (features->type >= INTUOS4S && features->type <= INTUOS4L) {
+ buttons = (data[3] << 1) | (data[2] & 0x01);
+ ring1 = data[1];
+ } else if (features->type == DTK) {
+ buttons = data[6];
+ } else if (features->type == WACOM_13HD) {
+ buttons = (data[4] << 1) | (data[3] & 0x01);
+ } else if (features->type == WACOM_24HD) {
+ buttons = (data[8] << 8) | data[6];
+ ring1 = data[1];
+ ring2 = data[2];
+
+ /*
+ * Three "buttons" are available on the 24HD which are
+ * physically implemented as a touchstrip. Each button
+ * is approximately 3 bits wide with a 2 bit spacing.
+ * The raw touchstrip bits are stored at:
+ * ((data[3] & 0x1f) << 8) | data[4])
+ */
+ nkeys = 3;
+ keys = ((data[3] & 0x1C) ? 1<<2 : 0) |
+ ((data[4] & 0xE0) ? 1<<1 : 0) |
+ ((data[4] & 0x07) ? 1<<0 : 0);
+ } else if (features->type == WACOM_27QHD) {
+ nkeys = 3;
+ keys = data[2] & 0x07;
+
+ input_report_abs(input, ABS_X, be16_to_cpup((__be16 *)&data[4]));
+ input_report_abs(input, ABS_Y, be16_to_cpup((__be16 *)&data[6]));
+ input_report_abs(input, ABS_Z, be16_to_cpup((__be16 *)&data[8]));
+ } else if (features->type == CINTIQ_HYBRID) {
+ /*
+ * Do not send hardware buttons under Android. They
+ * are already sent to the system through GPIO (and
+ * have different meaning).
+ *
+ * d-pad right -> data[4] & 0x10
+ * d-pad up -> data[4] & 0x20
+ * d-pad left -> data[4] & 0x40
+ * d-pad down -> data[4] & 0x80
+ * d-pad center -> data[3] & 0x01
+ */
+ buttons = (data[4] << 1) | (data[3] & 0x01);
+ } else if (features->type == CINTIQ_COMPANION_2) {
+ /* d-pad right -> data[4] & 0x10
+ * d-pad up -> data[4] & 0x20
+ * d-pad left -> data[4] & 0x40
+ * d-pad down -> data[4] & 0x80
+ * d-pad center -> data[3] & 0x01
+ */
+ buttons = ((data[2] >> 4) << 7) |
+ ((data[1] & 0x04) << 6) |
+ ((data[2] & 0x0F) << 2) |
+ (data[1] & 0x03);
+ } else if (features->type >= INTUOS5S && features->type <= INTUOSPL) {
+ /*
+ * ExpressKeys on Intuos5/Intuos Pro have a capacitive sensor in
+ * addition to the mechanical switch. Switch data is
+ * stored in data[4], capacitive data in data[5].
+ *
+ * Touch ring mode switch (data[3]) has no capacitive sensor
+ */
+ buttons = (data[4] << 1) | (data[3] & 0x01);
+ ring1 = data[2];
+ } else {
+ if (features->type == WACOM_21UX2 || features->type == WACOM_22HD) {
+ buttons = (data[8] << 10) | ((data[7] & 0x01) << 9) |
+ (data[6] << 1) | (data[5] & 0x01);
+
+ if (features->type == WACOM_22HD) {
+ nkeys = 3;
+ keys = data[9] & 0x07;
+ }
+ } else {
+ buttons = ((data[6] & 0x10) << 10) |
+ ((data[5] & 0x10) << 9) |
+ ((data[6] & 0x0F) << 4) |
+ (data[5] & 0x0F);
+ }
+ strip1 = ((data[1] & 0x1f) << 8) | data[2];
+ strip2 = ((data[3] & 0x1f) << 8) | data[4];
+ }
+
+ prox = (buttons & ~(~0 << nbuttons)) | (keys & ~(~0 << nkeys)) |
+ (ring1 & 0x80) | (ring2 & 0x80) | strip1 | strip2;
+
+ wacom_report_numbered_buttons(input, nbuttons, buttons);
+
+ for (i = 0; i < nkeys; i++)
+ input_report_key(input, KEY_PROG1 + i, keys & (1 << i));
+
+ input_report_abs(input, ABS_RX, strip1);
+ input_report_abs(input, ABS_RY, strip2);
+
+ input_report_abs(input, ABS_WHEEL, (ring1 & 0x80) ? (ring1 & 0x7f) : 0);
+ input_report_abs(input, ABS_THROTTLE, (ring2 & 0x80) ? (ring2 & 0x7f) : 0);
+
+ input_report_key(input, wacom->tool[1], prox ? 1 : 0);
+ input_report_abs(input, ABS_MISC, prox ? PAD_DEVICE_ID : 0);
+
+ input_event(input, EV_MSC, MSC_SERIAL, 0xffffffff);
+
+ return 1;
+}
+
static int wacom_intuos_inout(struct wacom_wac *wacom)
{
struct wacom_features *features = &wacom->features;
@@ -755,19 +884,40 @@ static int wacom_remote_status_irq(struct wacom_wac *wacom_wac, size_t len)
return 0;
}
-static void wacom_intuos_general(struct wacom_wac *wacom)
+static int wacom_intuos_general(struct wacom_wac *wacom)
{
struct wacom_features *features = &wacom->features;
unsigned char *data = wacom->data;
struct input_dev *input = wacom->pen_input;
- unsigned int t;
+ int idx = (features->type == INTUOS) ? (data[1] & 0x01) : 0;
+ unsigned char type = (data[1] >> 1) & 0x0F;
+ unsigned int x, y, distance, t;
- /* general pen packet */
- if ((data[1] & 0xb8) == 0xa0) {
- t = (data[6] << 2) | ((data[7] >> 6) & 3);
- if (features->pressure_max == 2047) {
- t = (t << 1) | (data[1] & 1);
- }
+ if (data[0] != WACOM_REPORT_PENABLED && data[0] != WACOM_REPORT_CINTIQ &&
+ data[0] != WACOM_REPORT_INTUOS_PEN)
+ return 0;
+
+ x = (be16_to_cpup((__be16 *)&data[2]) << 1) | ((data[9] >> 1) & 1);
+ y = (be16_to_cpup((__be16 *)&data[4]) << 1) | (data[9] & 1);
+ distance = data[9] >> 2;
+ if (features->type < INTUOS3S) {
+ x >>= 1;
+ y >>= 1;
+ distance >>= 1;
+ }
+ input_report_abs(input, ABS_X, x);
+ input_report_abs(input, ABS_Y, y);
+ input_report_abs(input, ABS_DISTANCE, distance);
+
+ switch (type) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ /* general pen packet */
+ t = (data[6] << 3) | ((data[7] & 0xC0) >> 5) | (data[1] & 1);
+ if (features->pressure_max < 2047)
+ t >>= 1;
input_report_abs(input, ABS_PRESSURE, t);
if (features->type != INTUOSHT2) {
input_report_abs(input, ABS_TILT_X,
@@ -777,29 +927,112 @@ static void wacom_intuos_general(struct wacom_wac *wacom)
input_report_key(input, BTN_STYLUS, data[1] & 2);
input_report_key(input, BTN_STYLUS2, data[1] & 4);
input_report_key(input, BTN_TOUCH, t > 10);
- }
+ break;
- /* airbrush second packet */
- if ((data[1] & 0xbc) == 0xb4) {
+ case 0x0a:
+ /* airbrush second packet */
input_report_abs(input, ABS_WHEEL,
(data[6] << 2) | ((data[7] >> 6) & 3));
input_report_abs(input, ABS_TILT_X,
(((data[7] << 1) & 0x7e) | (data[8] >> 7)) - 64);
input_report_abs(input, ABS_TILT_Y, (data[8] & 0x7f) - 64);
+ break;
+
+ case 0x05:
+ /* Rotation packet */
+ if (features->type >= INTUOS3S) {
+ /* I3 marker pen rotation */
+ t = (data[6] << 3) | ((data[7] >> 5) & 7);
+ t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) :
+ ((t-1) / 2 + 450)) : (450 - t / 2) ;
+ input_report_abs(input, ABS_Z, t);
+ } else {
+ /* 4D mouse 2nd packet */
+ t = (data[6] << 3) | ((data[7] >> 5) & 7);
+ input_report_abs(input, ABS_RZ, (data[7] & 0x20) ?
+ ((t - 1) / 2) : -t / 2);
+ }
+ break;
+
+ case 0x04:
+ /* 4D mouse 1st packet */
+ input_report_key(input, BTN_LEFT, data[8] & 0x01);
+ input_report_key(input, BTN_MIDDLE, data[8] & 0x02);
+ input_report_key(input, BTN_RIGHT, data[8] & 0x04);
+
+ input_report_key(input, BTN_SIDE, data[8] & 0x20);
+ input_report_key(input, BTN_EXTRA, data[8] & 0x10);
+ t = (data[6] << 2) | ((data[7] >> 6) & 3);
+ input_report_abs(input, ABS_THROTTLE, (data[8] & 0x08) ? -t : t);
+ break;
+
+ case 0x06:
+ /* I4 mouse */
+ input_report_key(input, BTN_LEFT, data[6] & 0x01);
+ input_report_key(input, BTN_MIDDLE, data[6] & 0x02);
+ input_report_key(input, BTN_RIGHT, data[6] & 0x04);
+ input_report_rel(input, REL_WHEEL, ((data[7] & 0x80) >> 7)
+ - ((data[7] & 0x40) >> 6));
+ input_report_key(input, BTN_SIDE, data[6] & 0x08);
+ input_report_key(input, BTN_EXTRA, data[6] & 0x10);
+
+ input_report_abs(input, ABS_TILT_X,
+ (((data[7] << 1) & 0x7e) | (data[8] >> 7)) - 64);
+ input_report_abs(input, ABS_TILT_Y, (data[8] & 0x7f) - 64);
+ break;
+
+ case 0x08:
+ if (wacom->tool[idx] == BTN_TOOL_MOUSE) {
+ /* 2D mouse packet */
+ input_report_key(input, BTN_LEFT, data[8] & 0x04);
+ input_report_key(input, BTN_MIDDLE, data[8] & 0x08);
+ input_report_key(input, BTN_RIGHT, data[8] & 0x10);
+ input_report_rel(input, REL_WHEEL, (data[8] & 0x01)
+ - ((data[8] & 0x02) >> 1));
+
+ /* I3 2D mouse side buttons */
+ if (features->type >= INTUOS3S && features->type <= INTUOS3L) {
+ input_report_key(input, BTN_SIDE, data[8] & 0x40);
+ input_report_key(input, BTN_EXTRA, data[8] & 0x20);
+ }
+ }
+ else if (wacom->tool[idx] == BTN_TOOL_LENS) {
+ /* Lens cursor packets */
+ input_report_key(input, BTN_LEFT, data[8] & 0x01);
+ input_report_key(input, BTN_MIDDLE, data[8] & 0x02);
+ input_report_key(input, BTN_RIGHT, data[8] & 0x04);
+ input_report_key(input, BTN_SIDE, data[8] & 0x10);
+ input_report_key(input, BTN_EXTRA, data[8] & 0x08);
+ }
+ break;
+
+ case 0x07:
+ case 0x09:
+ case 0x0b:
+ case 0x0c:
+ case 0x0d:
+ case 0x0e:
+ case 0x0f:
+ /* unhandled */
+ break;
}
+
+ input_report_abs(input, ABS_MISC, wacom->id[idx]); /* report tool id */
+ input_report_key(input, wacom->tool[idx], 1);
+ input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+ wacom->reporting_data = true;
+ return 2;
}
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->pen_input;
- unsigned int t;
- int idx = 0, result;
+ int result;
if (data[0] != WACOM_REPORT_PENABLED &&
- data[0] != WACOM_REPORT_INTUOSREAD &&
- data[0] != WACOM_REPORT_INTUOSWRITE &&
+ data[0] != WACOM_REPORT_INTUOS_ID1 &&
+ data[0] != WACOM_REPORT_INTUOS_ID2 &&
data[0] != WACOM_REPORT_INTUOSPAD &&
data[0] != WACOM_REPORT_INTUOS_PEN &&
data[0] != WACOM_REPORT_CINTIQ &&
@@ -810,339 +1043,22 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
return 0;
}
- /* tool number */
- if (features->type == INTUOS)
- idx = data[1] & 0x01;
-
- /* pad packets. Works as a second tool and is always in prox */
- if (data[0] == WACOM_REPORT_INTUOSPAD || data[0] == WACOM_REPORT_INTUOS5PAD ||
- data[0] == WACOM_REPORT_CINTIQPAD) {
- input = wacom->pad_input;
- if (features->type >= INTUOS4S && features->type <= INTUOS4L) {
- input_report_key(input, BTN_0, (data[2] & 0x01));
- input_report_key(input, BTN_1, (data[3] & 0x01));
- input_report_key(input, BTN_2, (data[3] & 0x02));
- input_report_key(input, BTN_3, (data[3] & 0x04));
- input_report_key(input, BTN_4, (data[3] & 0x08));
- input_report_key(input, BTN_5, (data[3] & 0x10));
- input_report_key(input, BTN_6, (data[3] & 0x20));
- if (data[1] & 0x80) {
- input_report_abs(input, ABS_WHEEL, (data[1] & 0x7f));
- } else {
- /* Out of proximity, clear wheel value. */
- input_report_abs(input, ABS_WHEEL, 0);
- }
- if (features->type != INTUOS4S) {
- input_report_key(input, BTN_7, (data[3] & 0x40));
- input_report_key(input, BTN_8, (data[3] & 0x80));
- }
- if (data[1] | (data[2] & 0x01) | data[3]) {
- input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
- } else {
- input_report_abs(input, ABS_MISC, 0);
- }
- } else if (features->type == DTK) {
- input_report_key(input, BTN_0, (data[6] & 0x01));
- input_report_key(input, BTN_1, (data[6] & 0x02));
- input_report_key(input, BTN_2, (data[6] & 0x04));
- input_report_key(input, BTN_3, (data[6] & 0x08));
- input_report_key(input, BTN_4, (data[6] & 0x10));
- input_report_key(input, BTN_5, (data[6] & 0x20));
- if (data[6] & 0x3f) {
- input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
- } else {
- input_report_abs(input, ABS_MISC, 0);
- }
- } else if (features->type == WACOM_13HD) {
- input_report_key(input, BTN_0, (data[3] & 0x01));
- input_report_key(input, BTN_1, (data[4] & 0x01));
- input_report_key(input, BTN_2, (data[4] & 0x02));
- input_report_key(input, BTN_3, (data[4] & 0x04));
- input_report_key(input, BTN_4, (data[4] & 0x08));
- input_report_key(input, BTN_5, (data[4] & 0x10));
- input_report_key(input, BTN_6, (data[4] & 0x20));
- input_report_key(input, BTN_7, (data[4] & 0x40));
- input_report_key(input, BTN_8, (data[4] & 0x80));
- if ((data[3] & 0x01) | data[4]) {
- input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
- } else {
- input_report_abs(input, ABS_MISC, 0);
- }
- } else if (features->type == WACOM_24HD) {
- input_report_key(input, BTN_0, (data[6] & 0x01));
- input_report_key(input, BTN_1, (data[6] & 0x02));
- input_report_key(input, BTN_2, (data[6] & 0x04));
- input_report_key(input, BTN_3, (data[6] & 0x08));
- input_report_key(input, BTN_4, (data[6] & 0x10));
- input_report_key(input, BTN_5, (data[6] & 0x20));
- input_report_key(input, BTN_6, (data[6] & 0x40));
- input_report_key(input, BTN_7, (data[6] & 0x80));
- input_report_key(input, BTN_8, (data[8] & 0x01));
- input_report_key(input, BTN_9, (data[8] & 0x02));
- input_report_key(input, BTN_A, (data[8] & 0x04));
- input_report_key(input, BTN_B, (data[8] & 0x08));
- input_report_key(input, BTN_C, (data[8] & 0x10));
- input_report_key(input, BTN_X, (data[8] & 0x20));
- input_report_key(input, BTN_Y, (data[8] & 0x40));
- input_report_key(input, BTN_Z, (data[8] & 0x80));
-
- /*
- * Three "buttons" are available on the 24HD which are
- * physically implemented as a touchstrip. Each button
- * is approximately 3 bits wide with a 2 bit spacing.
- * The raw touchstrip bits are stored at:
- * ((data[3] & 0x1f) << 8) | data[4])
- */
- input_report_key(input, KEY_PROG1, data[4] & 0x07);
- input_report_key(input, KEY_PROG2, data[4] & 0xE0);
- input_report_key(input, KEY_PROG3, data[3] & 0x1C);
-
- if (data[1] & 0x80) {
- input_report_abs(input, ABS_WHEEL, (data[1] & 0x7f));
- } else {
- /* Out of proximity, clear wheel value. */
- input_report_abs(input, ABS_WHEEL, 0);
- }
-
- if (data[2] & 0x80) {
- input_report_abs(input, ABS_THROTTLE, (data[2] & 0x7f));
- } else {
- /* Out of proximity, clear second wheel value. */
- input_report_abs(input, ABS_THROTTLE, 0);
- }
-
- if (data[1] | data[2] | (data[3] & 0x1f) | data[4] | data[6] | data[8]) {
- input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
- } else {
- input_report_abs(input, ABS_MISC, 0);
- }
- } else if (features->type == WACOM_27QHD) {
- input_report_key(input, KEY_PROG1, data[2] & 0x01);
- input_report_key(input, KEY_PROG2, data[2] & 0x02);
- input_report_key(input, KEY_PROG3, data[2] & 0x04);
-
- input_report_abs(input, ABS_X, be16_to_cpup((__be16 *)&data[4]));
- input_report_abs(input, ABS_Y, be16_to_cpup((__be16 *)&data[6]));
- input_report_abs(input, ABS_Z, be16_to_cpup((__be16 *)&data[8]));
- if ((data[2] & 0x07) | data[4] | data[5] | data[6] | data[7] | data[8] | data[9]) {
- input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
- } else {
- input_report_abs(input, ABS_MISC, 0);
- }
- } else if (features->type == CINTIQ_HYBRID) {
- /*
- * Do not send hardware buttons under Android. They
- * are already sent to the system through GPIO (and
- * have different meaning).
- */
- input_report_key(input, BTN_1, (data[4] & 0x01));
- input_report_key(input, BTN_2, (data[4] & 0x02));
- input_report_key(input, BTN_3, (data[4] & 0x04));
- input_report_key(input, BTN_4, (data[4] & 0x08));
-
- input_report_key(input, BTN_5, (data[4] & 0x10)); /* Right */
- input_report_key(input, BTN_6, (data[4] & 0x20)); /* Up */
- input_report_key(input, BTN_7, (data[4] & 0x40)); /* Left */
- input_report_key(input, BTN_8, (data[4] & 0x80)); /* Down */
- input_report_key(input, BTN_0, (data[3] & 0x01)); /* Center */
-
- if (data[4] | (data[3] & 0x01)) {
- input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
- } else {
- input_report_abs(input, ABS_MISC, 0);
- }
-
- } else if (features->type == CINTIQ_COMPANION_2) {
- input_report_key(input, BTN_1, (data[1] & 0x02));
- input_report_key(input, BTN_2, (data[2] & 0x01));
- input_report_key(input, BTN_3, (data[2] & 0x02));
- input_report_key(input, BTN_4, (data[2] & 0x04));
- input_report_key(input, BTN_5, (data[2] & 0x08));
- input_report_key(input, BTN_6, (data[1] & 0x04));
-
- input_report_key(input, BTN_7, (data[2] & 0x10)); /* Right */
- input_report_key(input, BTN_8, (data[2] & 0x20)); /* Up */
- input_report_key(input, BTN_9, (data[2] & 0x40)); /* Left */
- input_report_key(input, BTN_A, (data[2] & 0x80)); /* Down */
- input_report_key(input, BTN_0, (data[1] & 0x01)); /* Center */
-
- if (data[2] | (data[1] & 0x07)) {
- input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
- } else {
- input_report_abs(input, ABS_MISC, 0);
- }
-
- } else if (features->type >= INTUOS5S && features->type <= INTUOSPL) {
- int i;
-
- /* Touch ring mode switch has no capacitive sensor */
- input_report_key(input, BTN_0, (data[3] & 0x01));
-
- /*
- * ExpressKeys on Intuos5/Intuos Pro have a capacitive sensor in
- * addition to the mechanical switch. Switch data is
- * stored in data[4], capacitive data in data[5].
- */
- for (i = 0; i < 8; i++)
- input_report_key(input, BTN_1 + i, data[4] & (1 << i));
-
- if (data[2] & 0x80) {
- input_report_abs(input, ABS_WHEEL, (data[2] & 0x7f));
- } else {
- /* Out of proximity, clear wheel value. */
- input_report_abs(input, ABS_WHEEL, 0);
- }
-
- if (data[2] | (data[3] & 0x01) | data[4] | data[5]) {
- input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
- } else {
- input_report_abs(input, ABS_MISC, 0);
- }
- } else {
- if (features->type == WACOM_21UX2 || features->type == WACOM_22HD) {
- input_report_key(input, BTN_0, (data[5] & 0x01));
- input_report_key(input, BTN_1, (data[6] & 0x01));
- input_report_key(input, BTN_2, (data[6] & 0x02));
- input_report_key(input, BTN_3, (data[6] & 0x04));
- input_report_key(input, BTN_4, (data[6] & 0x08));
- input_report_key(input, BTN_5, (data[6] & 0x10));
- input_report_key(input, BTN_6, (data[6] & 0x20));
- input_report_key(input, BTN_7, (data[6] & 0x40));
- input_report_key(input, BTN_8, (data[6] & 0x80));
- input_report_key(input, BTN_9, (data[7] & 0x01));
- input_report_key(input, BTN_A, (data[8] & 0x01));
- input_report_key(input, BTN_B, (data[8] & 0x02));
- input_report_key(input, BTN_C, (data[8] & 0x04));
- input_report_key(input, BTN_X, (data[8] & 0x08));
- input_report_key(input, BTN_Y, (data[8] & 0x10));
- input_report_key(input, BTN_Z, (data[8] & 0x20));
- input_report_key(input, BTN_BASE, (data[8] & 0x40));
- input_report_key(input, BTN_BASE2, (data[8] & 0x80));
-
- if (features->type == WACOM_22HD) {
- input_report_key(input, KEY_PROG1, data[9] & 0x01);
- input_report_key(input, KEY_PROG2, data[9] & 0x02);
- input_report_key(input, KEY_PROG3, data[9] & 0x04);
- }
- } else {
- input_report_key(input, BTN_0, (data[5] & 0x01));
- input_report_key(input, BTN_1, (data[5] & 0x02));
- input_report_key(input, BTN_2, (data[5] & 0x04));
- input_report_key(input, BTN_3, (data[5] & 0x08));
- input_report_key(input, BTN_4, (data[6] & 0x01));
- input_report_key(input, BTN_5, (data[6] & 0x02));
- input_report_key(input, BTN_6, (data[6] & 0x04));
- input_report_key(input, BTN_7, (data[6] & 0x08));
- input_report_key(input, BTN_8, (data[5] & 0x10));
- input_report_key(input, BTN_9, (data[6] & 0x10));
- }
- input_report_abs(input, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]);
- input_report_abs(input, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]);
-
- if ((data[5] & 0x1f) | data[6] | (data[1] & 0x1f) |
- data[2] | (data[3] & 0x1f) | data[4] | data[8] |
- (data[7] & 0x01)) {
- input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
- } else {
- input_report_abs(input, ABS_MISC, 0);
- }
- }
- return 1;
- }
+ /* process pad events */
+ result = wacom_intuos_pad(wacom);
+ if (result)
+ return result;
/* process in/out prox events */
result = wacom_intuos_inout(wacom);
if (result)
- return result - 1;
-
- if (features->type >= INTUOS3S) {
- input_report_abs(input, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1));
- input_report_abs(input, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1));
- input_report_abs(input, ABS_DISTANCE, ((data[9] >> 2) & 0x3f));
- } else {
- input_report_abs(input, ABS_X, be16_to_cpup((__be16 *)&data[2]));
- input_report_abs(input, ABS_Y, be16_to_cpup((__be16 *)&data[4]));
- input_report_abs(input, ABS_DISTANCE, ((data[9] >> 3) & 0x1f));
- }
+ return result - 1;
/* process general packets */
- wacom_intuos_general(wacom);
-
- /* 4D mouse, 2D mouse, marker pen rotation, tilt mouse, or Lens cursor packets */
- if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0 || (data[1] & 0xbc) == 0xac) {
-
- if (data[1] & 0x02) {
- /* Rotation packet */
- if (features->type >= INTUOS3S) {
- /* I3 marker pen rotation */
- t = (data[6] << 3) | ((data[7] >> 5) & 7);
- t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) :
- ((t-1) / 2 + 450)) : (450 - t / 2) ;
- input_report_abs(input, ABS_Z, t);
- } else {
- /* 4D mouse rotation packet */
- t = (data[6] << 3) | ((data[7] >> 5) & 7);
- input_report_abs(input, ABS_RZ, (data[7] & 0x20) ?
- ((t - 1) / 2) : -t / 2);
- }
-
- } else if (!(data[1] & 0x10) && features->type < INTUOS3S) {
- /* 4D mouse packet */
- input_report_key(input, BTN_LEFT, data[8] & 0x01);
- input_report_key(input, BTN_MIDDLE, data[8] & 0x02);
- input_report_key(input, BTN_RIGHT, data[8] & 0x04);
-
- input_report_key(input, BTN_SIDE, data[8] & 0x20);
- input_report_key(input, BTN_EXTRA, data[8] & 0x10);
- t = (data[6] << 2) | ((data[7] >> 6) & 3);
- input_report_abs(input, ABS_THROTTLE, (data[8] & 0x08) ? -t : t);
-
- } else if (wacom->tool[idx] == BTN_TOOL_MOUSE) {
- /* I4 mouse */
- if (features->type >= INTUOS4S && features->type <= INTUOSPL) {
- input_report_key(input, BTN_LEFT, data[6] & 0x01);
- input_report_key(input, BTN_MIDDLE, data[6] & 0x02);
- input_report_key(input, BTN_RIGHT, data[6] & 0x04);
- input_report_rel(input, REL_WHEEL, ((data[7] & 0x80) >> 7)
- - ((data[7] & 0x40) >> 6));
- input_report_key(input, BTN_SIDE, data[6] & 0x08);
- input_report_key(input, BTN_EXTRA, data[6] & 0x10);
-
- input_report_abs(input, ABS_TILT_X,
- (((data[7] << 1) & 0x7e) | (data[8] >> 7)) - 64);
- input_report_abs(input, ABS_TILT_Y, (data[8] & 0x7f) - 64);
- } else {
- /* 2D mouse packet */
- input_report_key(input, BTN_LEFT, data[8] & 0x04);
- input_report_key(input, BTN_MIDDLE, data[8] & 0x08);
- input_report_key(input, BTN_RIGHT, data[8] & 0x10);
- input_report_rel(input, REL_WHEEL, (data[8] & 0x01)
- - ((data[8] & 0x02) >> 1));
-
- /* I3 2D mouse side buttons */
- if (features->type >= INTUOS3S && features->type <= INTUOS3L) {
- input_report_key(input, BTN_SIDE, data[8] & 0x40);
- input_report_key(input, BTN_EXTRA, data[8] & 0x20);
- }
- }
- } else if ((features->type < INTUOS3S || features->type == INTUOS3L ||
- features->type == INTUOS4L || features->type == INTUOS5L ||
- features->type == INTUOSPL) &&
- wacom->tool[idx] == BTN_TOOL_LENS) {
- /* Lens cursor packets */
- input_report_key(input, BTN_LEFT, data[8] & 0x01);
- input_report_key(input, BTN_MIDDLE, data[8] & 0x02);
- input_report_key(input, BTN_RIGHT, data[8] & 0x04);
- input_report_key(input, BTN_SIDE, data[8] & 0x10);
- input_report_key(input, BTN_EXTRA, data[8] & 0x08);
- }
- }
+ result = wacom_intuos_general(wacom);
+ if (result)
+ return result - 1;
- input_report_abs(input, ABS_MISC, wacom->id[idx]); /* report tool id */
- input_report_key(input, wacom->tool[idx], 1);
- input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
- wacom->reporting_data = true;
- return 1;
+ return 0;
}
static int int_dist(int x1, int y1, int x2, int y2)
@@ -2509,7 +2425,7 @@ void wacom_setup_device_quirks(struct wacom *wacom)
features->quirks |= WACOM_QUIRK_BATTERY;
/* quirk for bamboo touch with 2 low res touches */
- if (features->type == BAMBOO_PT &&
+ if ((features->type == BAMBOO_PT || features->type == BAMBOO_TOUCH) &&
features->pktlen == WACOM_PKGLEN_BBTOUCH) {
features->x_max <<= 5;
features->y_max <<= 5;
@@ -2806,6 +2722,19 @@ static void wacom_setup_numbered_buttons(struct input_dev *input_dev,
__set_bit(BTN_BASE + (i-16), input_dev->keybit);
}
+static void wacom_report_numbered_buttons(struct input_dev *input_dev,
+ int button_count, int mask)
+{
+ int i;
+
+ for (i = 0; i < button_count && i < 10; i++)
+ input_report_key(input_dev, BTN_0 + i, mask & (1 << i));
+ for (i = 10; i < button_count && i < 16; i++)
+ input_report_key(input_dev, BTN_A + (i-10), mask & (1 << i));
+ for (i = 16; i < button_count && i < 18; i++)
+ input_report_key(input_dev, BTN_BASE + (i-16), mask & (1 << i));
+}
+
int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
struct wacom_wac *wacom_wac)
{
diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h
index 877c24a5df94..25baa7f29599 100644
--- a/drivers/hid/wacom_wac.h
+++ b/drivers/hid/wacom_wac.h
@@ -47,8 +47,8 @@
/* wacom data packet report IDs */
#define WACOM_REPORT_PENABLED 2
#define WACOM_REPORT_PENABLED_BT 3
-#define WACOM_REPORT_INTUOSREAD 5
-#define WACOM_REPORT_INTUOSWRITE 6
+#define WACOM_REPORT_INTUOS_ID1 5
+#define WACOM_REPORT_INTUOS_ID2 6
#define WACOM_REPORT_INTUOSPAD 12
#define WACOM_REPORT_INTUOS5PAD 3
#define WACOM_REPORT_DTUSPAD 21
@@ -70,6 +70,7 @@
#define WACOM_REPORT_DEVICE_LIST 16
#define WACOM_REPORT_INTUOS_PEN 16
#define WACOM_REPORT_REMOTE 17
+#define WACOM_REPORT_INTUOSHT2_ID 8
/* device quirks */
#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0001
diff --git a/drivers/hsi/controllers/omap_ssi.c b/drivers/hsi/controllers/omap_ssi.c
index f6d3100b7a32..27b91f14ba7a 100644
--- a/drivers/hsi/controllers/omap_ssi.c
+++ b/drivers/hsi/controllers/omap_ssi.c
@@ -323,11 +323,10 @@ static int __init ssi_add_controller(struct hsi_controller *ssi,
return -ENOMEM;
}
- ssi->id = ida_simple_get(&platform_omap_ssi_ida, 0, 0, GFP_KERNEL);
- if (ssi->id < 0) {
- err = ssi->id;
+ err = ida_simple_get(&platform_omap_ssi_ida, 0, 0, GFP_KERNEL);
+ if (err < 0)
goto out_err;
- }
+ ssi->id = err;
ssi->owner = THIS_MODULE;
ssi->device.parent = &pd->dev;
diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c
index 02e66032ae73..e80a66e20998 100644
--- a/drivers/hsi/controllers/omap_ssi_port.c
+++ b/drivers/hsi/controllers/omap_ssi_port.c
@@ -1147,13 +1147,13 @@ static int __init ssi_port_probe(struct platform_device *pd)
goto error;
}
- cawake_gpio = of_get_named_gpio(np, "ti,ssi-cawake-gpio", 0);
- if (cawake_gpio < 0) {
+ err = of_get_named_gpio(np, "ti,ssi-cawake-gpio", 0);
+ if (err < 0) {
dev_err(&pd->dev, "DT data is missing cawake gpio (err=%d)\n",
- cawake_gpio);
- err = -ENODEV;
+ err);
goto error;
}
+ cawake_gpio = err;
err = devm_gpio_request_one(&port->device, cawake_gpio, GPIOF_DIR_IN,
"cawake");
diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c
index c4dcab048cb8..1161d68a1863 100644
--- a/drivers/hv/channel.c
+++ b/drivers/hv/channel.c
@@ -28,6 +28,7 @@
#include <linux/module.h>
#include <linux/hyperv.h>
#include <linux/uio.h>
+#include <linux/interrupt.h>
#include "hyperv_vmbus.h"
@@ -496,8 +497,33 @@ static void reset_channel_cb(void *arg)
static int vmbus_close_internal(struct vmbus_channel *channel)
{
struct vmbus_channel_close_channel *msg;
+ struct tasklet_struct *tasklet;
int ret;
+ /*
+ * process_chn_event(), running in the tasklet, can race
+ * with vmbus_close_internal() in the case of SMP guest, e.g., when
+ * the former is accessing channel->inbound.ring_buffer, the latter
+ * could be freeing the ring_buffer pages.
+ *
+ * To resolve the race, we can serialize them by disabling the
+ * tasklet when the latter is running here.
+ */
+ tasklet = hv_context.event_dpc[channel->target_cpu];
+ tasklet_disable(tasklet);
+
+ /*
+ * In case a device driver's probe() fails (e.g.,
+ * util_probe() -> vmbus_open() returns -ENOMEM) and the device is
+ * rescinded later (e.g., we dynamically disble an Integrated Service
+ * in Hyper-V Manager), the driver's remove() invokes vmbus_close():
+ * here we should skip most of the below cleanup work.
+ */
+ if (channel->state != CHANNEL_OPENED_STATE) {
+ ret = -EINVAL;
+ goto out;
+ }
+
channel->state = CHANNEL_OPEN_STATE;
channel->sc_creation_callback = NULL;
/* Stop callback and cancel the timer asap */
@@ -525,7 +551,7 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
* If we failed to post the close msg,
* it is perhaps better to leak memory.
*/
- return ret;
+ goto out;
}
/* Tear down the gpadl for the channel's ring buffer */
@@ -538,7 +564,7 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
* If we failed to teardown gpadl,
* it is perhaps better to leak memory.
*/
- return ret;
+ goto out;
}
}
@@ -549,12 +575,9 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
free_pages((unsigned long)channel->ringbuffer_pages,
get_order(channel->ringbuffer_pagecount * PAGE_SIZE));
- /*
- * If the channel has been rescinded; process device removal.
- */
- if (channel->rescind)
- hv_process_channel_removal(channel,
- channel->offermsg.child_relid);
+out:
+ tasklet_enable(tasklet);
+
return ret;
}
@@ -630,10 +653,19 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer,
* on the ring. We will not signal if more data is
* to be placed.
*
+ * Based on the channel signal state, we will decide
+ * which signaling policy will be applied.
+ *
* If we cannot write to the ring-buffer; signal the host
* even if we may not have written anything. This is a rare
* enough condition that it should not matter.
*/
+
+ if (channel->signal_policy)
+ signal = true;
+ else
+ kick_q = true;
+
if (((ret == 0) && kick_q && signal) || (ret))
vmbus_setevent(channel);
@@ -733,10 +765,19 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
* on the ring. We will not signal if more data is
* to be placed.
*
+ * Based on the channel signal state, we will decide
+ * which signaling policy will be applied.
+ *
* If we cannot write to the ring-buffer; signal the host
* even if we may not have written anything. This is a rare
* enough condition that it should not matter.
*/
+
+ if (channel->signal_policy)
+ signal = true;
+ else
+ kick_q = true;
+
if (((ret == 0) && kick_q && signal) || (ret))
vmbus_setevent(channel);
@@ -881,46 +922,29 @@ EXPORT_SYMBOL_GPL(vmbus_sendpacket_multipagebuffer);
*
* Mainly used by Hyper-V drivers.
*/
-int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer,
- u32 bufferlen, u32 *buffer_actual_len, u64 *requestid)
+static inline int
+__vmbus_recvpacket(struct vmbus_channel *channel, void *buffer,
+ u32 bufferlen, u32 *buffer_actual_len, u64 *requestid,
+ bool raw)
{
- struct vmpacket_descriptor desc;
- u32 packetlen;
- u32 userlen;
int ret;
bool signal = false;
- *buffer_actual_len = 0;
- *requestid = 0;
-
-
- ret = hv_ringbuffer_peek(&channel->inbound, &desc,
- sizeof(struct vmpacket_descriptor));
- if (ret != 0)
- return 0;
-
- packetlen = desc.len8 << 3;
- userlen = packetlen - (desc.offset8 << 3);
-
- *buffer_actual_len = userlen;
-
- if (userlen > bufferlen) {
-
- pr_err("Buffer too small - got %d needs %d\n",
- bufferlen, userlen);
- return -ETOOSMALL;
- }
-
- *requestid = desc.trans_id;
-
- /* Copy over the packet to the user buffer */
- ret = hv_ringbuffer_read(&channel->inbound, buffer, userlen,
- (desc.offset8 << 3), &signal);
+ ret = hv_ringbuffer_read(&channel->inbound, buffer, bufferlen,
+ buffer_actual_len, requestid, &signal, raw);
if (signal)
vmbus_setevent(channel);
- return 0;
+ return ret;
+}
+
+int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer,
+ u32 bufferlen, u32 *buffer_actual_len,
+ u64 *requestid)
+{
+ return __vmbus_recvpacket(channel, buffer, bufferlen,
+ buffer_actual_len, requestid, false);
}
EXPORT_SYMBOL(vmbus_recvpacket);
@@ -931,37 +955,7 @@ int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer,
u32 bufferlen, u32 *buffer_actual_len,
u64 *requestid)
{
- struct vmpacket_descriptor desc;
- u32 packetlen;
- int ret;
- bool signal = false;
-
- *buffer_actual_len = 0;
- *requestid = 0;
-
-
- ret = hv_ringbuffer_peek(&channel->inbound, &desc,
- sizeof(struct vmpacket_descriptor));
- if (ret != 0)
- return 0;
-
-
- packetlen = desc.len8 << 3;
-
- *buffer_actual_len = packetlen;
-
- if (packetlen > bufferlen)
- return -ENOBUFS;
-
- *requestid = desc.trans_id;
-
- /* Copy over the entire packet to the user buffer */
- ret = hv_ringbuffer_read(&channel->inbound, buffer, packetlen, 0,
- &signal);
-
- if (signal)
- vmbus_setevent(channel);
-
- return ret;
+ return __vmbus_recvpacket(channel, buffer, bufferlen,
+ buffer_actual_len, requestid, true);
}
EXPORT_SYMBOL_GPL(vmbus_recvpacket_raw);
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
index 652afd11a9ef..1c1ad47042c5 100644
--- a/drivers/hv/channel_mgmt.c
+++ b/drivers/hv/channel_mgmt.c
@@ -177,19 +177,24 @@ static void percpu_channel_deq(void *arg)
}
-void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
+static void vmbus_release_relid(u32 relid)
{
struct vmbus_channel_relid_released msg;
- unsigned long flags;
- struct vmbus_channel *primary_channel;
memset(&msg, 0, sizeof(struct vmbus_channel_relid_released));
msg.child_relid = relid;
msg.header.msgtype = CHANNELMSG_RELID_RELEASED;
vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released));
+}
- if (channel == NULL)
- return;
+void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
+{
+ unsigned long flags;
+ struct vmbus_channel *primary_channel;
+
+ vmbus_release_relid(relid);
+
+ BUG_ON(!channel->rescind);
if (channel->target_cpu != get_cpu()) {
put_cpu();
@@ -201,9 +206,9 @@ void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
}
if (channel->primary_channel == NULL) {
- spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
+ mutex_lock(&vmbus_connection.channel_mutex);
list_del(&channel->listentry);
- spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
+ mutex_unlock(&vmbus_connection.channel_mutex);
primary_channel = channel;
} else {
@@ -230,9 +235,7 @@ void vmbus_free_channels(void)
list_for_each_entry_safe(channel, tmp, &vmbus_connection.chn_list,
listentry) {
- /* if we don't set rescind to true, vmbus_close_internal()
- * won't invoke hv_process_channel_removal().
- */
+ /* hv_process_channel_removal() needs this */
channel->rescind = true;
vmbus_device_unregister(channel->device_obj);
@@ -250,7 +253,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
unsigned long flags;
/* Make sure this is a new offer */
- spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
+ mutex_lock(&vmbus_connection.channel_mutex);
list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
if (!uuid_le_cmp(channel->offermsg.offer.if_type,
@@ -266,7 +269,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
list_add_tail(&newchannel->listentry,
&vmbus_connection.chn_list);
- spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
+ mutex_unlock(&vmbus_connection.channel_mutex);
if (!fnew) {
/*
@@ -336,9 +339,11 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
return;
err_deq_chan:
- spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
+ vmbus_release_relid(newchannel->offermsg.child_relid);
+
+ mutex_lock(&vmbus_connection.channel_mutex);
list_del(&newchannel->listentry);
- spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
+ mutex_unlock(&vmbus_connection.channel_mutex);
if (newchannel->target_cpu != get_cpu()) {
put_cpu();
@@ -356,8 +361,10 @@ err_free_chan:
enum {
IDE = 0,
SCSI,
+ FC,
NIC,
ND_NIC,
+ PCIE,
MAX_PERF_CHN,
};
@@ -371,10 +378,14 @@ static const struct hv_vmbus_device_id hp_devs[] = {
{ HV_IDE_GUID, },
/* Storage - SCSI */
{ HV_SCSI_GUID, },
+ /* Storage - FC */
+ { HV_SYNTHFC_GUID, },
/* Network */
{ HV_NIC_GUID, },
/* NetworkDirect Guest RDMA */
{ HV_ND_GUID, },
+ /* PCI Express Pass Through */
+ { HV_PCIE_GUID, },
};
@@ -405,8 +416,7 @@ static void init_vp_index(struct vmbus_channel *channel, const uuid_le *type_gui
struct cpumask *alloced_mask;
for (i = IDE; i < MAX_PERF_CHN; i++) {
- if (!memcmp(type_guid->b, hp_devs[i].guid,
- sizeof(uuid_le))) {
+ if (!uuid_le_cmp(*type_guid, hp_devs[i].guid)) {
perf_chn = true;
break;
}
@@ -585,7 +595,11 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
channel = relid2channel(rescind->child_relid);
if (channel == NULL) {
- hv_process_channel_removal(NULL, rescind->child_relid);
+ /*
+ * This is very impossible, because in
+ * vmbus_process_offer(), we have already invoked
+ * vmbus_release_relid() on error.
+ */
return;
}
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index 4fc2e8836e60..3dc5a9c7fad6 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -83,10 +83,13 @@ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo,
msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages[0]);
msg->monitor_page2 = virt_to_phys(vmbus_connection.monitor_pages[1]);
- if (version >= VERSION_WIN8_1) {
- msg->target_vcpu = hv_context.vp_index[get_cpu()];
- put_cpu();
- }
+ /*
+ * We want all channel messages to be delivered on CPU 0.
+ * This has been the behavior pre-win8. This is not
+ * perf issue and having all channel messages delivered on CPU 0
+ * would be ok.
+ */
+ msg->target_vcpu = 0;
/*
* Add to list before we send the request since we may
@@ -146,7 +149,7 @@ int vmbus_connect(void)
spin_lock_init(&vmbus_connection.channelmsg_lock);
INIT_LIST_HEAD(&vmbus_connection.chn_list);
- spin_lock_init(&vmbus_connection.channel_lock);
+ mutex_init(&vmbus_connection.channel_mutex);
/*
* Setup the vmbus event connection for channel interrupt
@@ -282,11 +285,10 @@ struct vmbus_channel *relid2channel(u32 relid)
{
struct vmbus_channel *channel;
struct vmbus_channel *found_channel = NULL;
- unsigned long flags;
struct list_head *cur, *tmp;
struct vmbus_channel *cur_sc;
- spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
+ mutex_lock(&vmbus_connection.channel_mutex);
list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
if (channel->offermsg.child_relid == relid) {
found_channel = channel;
@@ -305,7 +307,7 @@ struct vmbus_channel *relid2channel(u32 relid)
}
}
}
- spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
+ mutex_unlock(&vmbus_connection.channel_mutex);
return found_channel;
}
diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c
index 6341be8739ae..11bca51ef5ff 100644
--- a/drivers/hv/hv.c
+++ b/drivers/hv/hv.c
@@ -89,9 +89,9 @@ static int query_hypervisor_info(void)
}
/*
- * do_hypercall- Invoke the specified hypercall
+ * hv_do_hypercall- Invoke the specified hypercall
*/
-static u64 do_hypercall(u64 control, void *input, void *output)
+u64 hv_do_hypercall(u64 control, void *input, void *output)
{
u64 input_address = (input) ? virt_to_phys(input) : 0;
u64 output_address = (output) ? virt_to_phys(output) : 0;
@@ -132,6 +132,7 @@ static u64 do_hypercall(u64 control, void *input, void *output)
return hv_status_lo | ((u64)hv_status_hi << 32);
#endif /* !x86_64 */
}
+EXPORT_SYMBOL_GPL(hv_do_hypercall);
#ifdef CONFIG_X86_64
static cycle_t read_hv_clock_tsc(struct clocksource *arg)
@@ -139,7 +140,7 @@ static cycle_t read_hv_clock_tsc(struct clocksource *arg)
cycle_t current_tick;
struct ms_hyperv_tsc_page *tsc_pg = hv_context.tsc_page;
- if (tsc_pg->tsc_sequence != -1) {
+ if (tsc_pg->tsc_sequence != 0) {
/*
* Use the tsc page to compute the value.
*/
@@ -161,7 +162,7 @@ static cycle_t read_hv_clock_tsc(struct clocksource *arg)
if (tsc_pg->tsc_sequence == sequence)
return current_tick;
- if (tsc_pg->tsc_sequence != -1)
+ if (tsc_pg->tsc_sequence != 0)
continue;
/*
* Fallback using MSR method.
@@ -192,9 +193,7 @@ int hv_init(void)
{
int max_leaf;
union hv_x64_msr_hypercall_contents hypercall_msr;
- union hv_x64_msr_hypercall_contents tsc_msr;
void *virtaddr = NULL;
- void *va_tsc = NULL;
memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS);
memset(hv_context.synic_message_page, 0,
@@ -240,6 +239,9 @@ int hv_init(void)
#ifdef CONFIG_X86_64
if (ms_hyperv.features & HV_X64_MSR_REFERENCE_TSC_AVAILABLE) {
+ union hv_x64_msr_hypercall_contents tsc_msr;
+ void *va_tsc;
+
va_tsc = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL);
if (!va_tsc)
goto cleanup;
@@ -315,7 +317,7 @@ int hv_post_message(union hv_connection_id connection_id,
{
struct hv_input_post_message *aligned_msg;
- u16 status;
+ u64 status;
if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
return -EMSGSIZE;
@@ -329,11 +331,10 @@ int hv_post_message(union hv_connection_id connection_id,
aligned_msg->payload_size = payload_size;
memcpy((void *)aligned_msg->payload, payload, payload_size);
- status = do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL)
- & 0xFFFF;
+ status = hv_do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL);
put_cpu();
- return status;
+ return status & 0xFFFF;
}
@@ -343,13 +344,13 @@ int hv_post_message(union hv_connection_id connection_id,
*
* This involves a hypercall.
*/
-u16 hv_signal_event(void *con_id)
+int hv_signal_event(void *con_id)
{
- u16 status;
+ u64 status;
- status = (do_hypercall(HVCALL_SIGNAL_EVENT, con_id, NULL) & 0xFFFF);
+ status = hv_do_hypercall(HVCALL_SIGNAL_EVENT, con_id, NULL);
- return status;
+ return status & 0xFFFF;
}
static int hv_ce_set_next_event(unsigned long delta,
diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c
index db4b887b889d..c37a71e13de0 100644
--- a/drivers/hv/hv_fcopy.c
+++ b/drivers/hv/hv_fcopy.c
@@ -51,7 +51,6 @@ static struct {
struct hv_fcopy_hdr *fcopy_msg; /* current message */
struct vmbus_channel *recv_channel; /* chn we got the request */
u64 recv_req_id; /* request ID. */
- void *fcopy_context; /* for the channel callback */
} fcopy_transaction;
static void fcopy_respond_to_host(int error);
@@ -67,6 +66,13 @@ static struct hvutil_transport *hvt;
*/
static int dm_reg_value;
+static void fcopy_poll_wrapper(void *channel)
+{
+ /* Transaction is finished, reset the state here to avoid races. */
+ fcopy_transaction.state = HVUTIL_READY;
+ hv_fcopy_onchannelcallback(channel);
+}
+
static void fcopy_timeout_func(struct work_struct *dummy)
{
/*
@@ -74,13 +80,7 @@ static void fcopy_timeout_func(struct work_struct *dummy)
* process the pending transaction.
*/
fcopy_respond_to_host(HV_E_FAIL);
-
- /* Transaction is finished, reset the state. */
- if (fcopy_transaction.state > HVUTIL_READY)
- fcopy_transaction.state = HVUTIL_READY;
-
- hv_poll_channel(fcopy_transaction.fcopy_context,
- hv_fcopy_onchannelcallback);
+ hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper);
}
static int fcopy_handle_handshake(u32 version)
@@ -108,9 +108,7 @@ static int fcopy_handle_handshake(u32 version)
return -EINVAL;
}
pr_debug("FCP: userspace daemon ver. %d registered\n", version);
- fcopy_transaction.state = HVUTIL_READY;
- hv_poll_channel(fcopy_transaction.fcopy_context,
- hv_fcopy_onchannelcallback);
+ hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper);
return 0;
}
@@ -227,15 +225,8 @@ void hv_fcopy_onchannelcallback(void *context)
int util_fw_version;
int fcopy_srv_version;
- if (fcopy_transaction.state > HVUTIL_READY) {
- /*
- * We will defer processing this callback once
- * the current transaction is complete.
- */
- fcopy_transaction.fcopy_context = context;
+ if (fcopy_transaction.state > HVUTIL_READY)
return;
- }
- fcopy_transaction.fcopy_context = NULL;
vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen,
&requestid);
@@ -275,7 +266,8 @@ void hv_fcopy_onchannelcallback(void *context)
* Send the information to the user-level daemon.
*/
schedule_work(&fcopy_send_work);
- schedule_delayed_work(&fcopy_timeout_work, 5*HZ);
+ schedule_delayed_work(&fcopy_timeout_work,
+ HV_UTIL_TIMEOUT * HZ);
return;
}
icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
@@ -304,9 +296,8 @@ static int fcopy_on_msg(void *msg, int len)
if (cancel_delayed_work_sync(&fcopy_timeout_work)) {
fcopy_transaction.state = HVUTIL_USERSPACE_RECV;
fcopy_respond_to_host(*val);
- fcopy_transaction.state = HVUTIL_READY;
- hv_poll_channel(fcopy_transaction.fcopy_context,
- hv_fcopy_onchannelcallback);
+ hv_poll_channel(fcopy_transaction.recv_channel,
+ fcopy_poll_wrapper);
}
return 0;
diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c
index 74c38a9f34a6..d4ab81bcd515 100644
--- a/drivers/hv/hv_kvp.c
+++ b/drivers/hv/hv_kvp.c
@@ -66,7 +66,6 @@ static struct {
struct hv_kvp_msg *kvp_msg; /* current message */
struct vmbus_channel *recv_channel; /* chn we got the request */
u64 recv_req_id; /* request ID. */
- void *kvp_context; /* for the channel callback */
} kvp_transaction;
/*
@@ -94,6 +93,13 @@ static struct hvutil_transport *hvt;
*/
#define HV_DRV_VERSION "3.1"
+static void kvp_poll_wrapper(void *channel)
+{
+ /* Transaction is finished, reset the state here to avoid races. */
+ kvp_transaction.state = HVUTIL_READY;
+ hv_kvp_onchannelcallback(channel);
+}
+
static void
kvp_register(int reg_value)
{
@@ -121,12 +127,7 @@ static void kvp_timeout_func(struct work_struct *dummy)
*/
kvp_respond_to_host(NULL, HV_E_FAIL);
- /* Transaction is finished, reset the state. */
- if (kvp_transaction.state > HVUTIL_READY)
- kvp_transaction.state = HVUTIL_READY;
-
- hv_poll_channel(kvp_transaction.kvp_context,
- hv_kvp_onchannelcallback);
+ hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper);
}
static int kvp_handle_handshake(struct hv_kvp_msg *msg)
@@ -153,7 +154,7 @@ static int kvp_handle_handshake(struct hv_kvp_msg *msg)
pr_debug("KVP: userspace daemon ver. %d registered\n",
KVP_OP_REGISTER);
kvp_register(dm_reg_value);
- kvp_transaction.state = HVUTIL_READY;
+ hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper);
return 0;
}
@@ -218,9 +219,7 @@ static int kvp_on_msg(void *msg, int len)
*/
if (cancel_delayed_work_sync(&kvp_timeout_work)) {
kvp_respond_to_host(message, error);
- kvp_transaction.state = HVUTIL_READY;
- hv_poll_channel(kvp_transaction.kvp_context,
- hv_kvp_onchannelcallback);
+ hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper);
}
return 0;
@@ -596,15 +595,8 @@ void hv_kvp_onchannelcallback(void *context)
int util_fw_version;
int kvp_srv_version;
- if (kvp_transaction.state > HVUTIL_READY) {
- /*
- * We will defer processing this callback once
- * the current transaction is complete.
- */
- kvp_transaction.kvp_context = context;
+ if (kvp_transaction.state > HVUTIL_READY)
return;
- }
- kvp_transaction.kvp_context = NULL;
vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 4, &recvlen,
&requestid);
@@ -668,7 +660,8 @@ void hv_kvp_onchannelcallback(void *context)
* user-mode not responding.
*/
schedule_work(&kvp_sendkey_work);
- schedule_delayed_work(&kvp_timeout_work, 5*HZ);
+ schedule_delayed_work(&kvp_timeout_work,
+ HV_UTIL_TIMEOUT * HZ);
return;
diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c
index 815405f2e777..67def4a831c8 100644
--- a/drivers/hv/hv_snapshot.c
+++ b/drivers/hv/hv_snapshot.c
@@ -53,7 +53,6 @@ static struct {
struct vmbus_channel *recv_channel; /* chn we got the request */
u64 recv_req_id; /* request ID. */
struct hv_vss_msg *msg; /* current message */
- void *vss_context; /* for the channel callback */
} vss_transaction;
@@ -74,6 +73,13 @@ static void vss_timeout_func(struct work_struct *dummy);
static DECLARE_DELAYED_WORK(vss_timeout_work, vss_timeout_func);
static DECLARE_WORK(vss_send_op_work, vss_send_op);
+static void vss_poll_wrapper(void *channel)
+{
+ /* Transaction is finished, reset the state here to avoid races. */
+ vss_transaction.state = HVUTIL_READY;
+ hv_vss_onchannelcallback(channel);
+}
+
/*
* Callback when data is received from user mode.
*/
@@ -86,12 +92,7 @@ static void vss_timeout_func(struct work_struct *dummy)
pr_warn("VSS: timeout waiting for daemon to reply\n");
vss_respond_to_host(HV_E_FAIL);
- /* Transaction is finished, reset the state. */
- if (vss_transaction.state > HVUTIL_READY)
- vss_transaction.state = HVUTIL_READY;
-
- hv_poll_channel(vss_transaction.vss_context,
- hv_vss_onchannelcallback);
+ hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper);
}
static int vss_handle_handshake(struct hv_vss_msg *vss_msg)
@@ -112,7 +113,7 @@ static int vss_handle_handshake(struct hv_vss_msg *vss_msg)
default:
return -EINVAL;
}
- vss_transaction.state = HVUTIL_READY;
+ hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper);
pr_debug("VSS: userspace daemon ver. %d registered\n", dm_reg_value);
return 0;
}
@@ -138,9 +139,8 @@ static int vss_on_msg(void *msg, int len)
if (cancel_delayed_work_sync(&vss_timeout_work)) {
vss_respond_to_host(vss_msg->error);
/* Transaction is finished, reset the state. */
- vss_transaction.state = HVUTIL_READY;
- hv_poll_channel(vss_transaction.vss_context,
- hv_vss_onchannelcallback);
+ hv_poll_channel(vss_transaction.recv_channel,
+ vss_poll_wrapper);
}
} else {
/* This is a spurious call! */
@@ -238,15 +238,8 @@ void hv_vss_onchannelcallback(void *context)
struct icmsg_hdr *icmsghdrp;
struct icmsg_negotiate *negop = NULL;
- if (vss_transaction.state > HVUTIL_READY) {
- /*
- * We will defer processing this callback once
- * the current transaction is complete.
- */
- vss_transaction.vss_context = context;
+ if (vss_transaction.state > HVUTIL_READY)
return;
- }
- vss_transaction.vss_context = NULL;
vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen,
&requestid);
@@ -338,6 +331,11 @@ static void vss_on_reset(void)
int
hv_vss_init(struct hv_util_service *srv)
{
+ if (vmbus_proto_version < VERSION_WIN8_1) {
+ pr_warn("Integration service 'Backup (volume snapshot)'"
+ " not supported on this host version.\n");
+ return -ENOTSUPP;
+ }
recv_buffer = srv->recv_buffer;
/*
diff --git a/drivers/hv/hv_utils_transport.c b/drivers/hv/hv_utils_transport.c
index 6a9d80a5332d..4f42c0e20c20 100644
--- a/drivers/hv/hv_utils_transport.c
+++ b/drivers/hv/hv_utils_transport.c
@@ -27,11 +27,9 @@ static struct list_head hvt_list = LIST_HEAD_INIT(hvt_list);
static void hvt_reset(struct hvutil_transport *hvt)
{
- mutex_lock(&hvt->outmsg_lock);
kfree(hvt->outmsg);
hvt->outmsg = NULL;
hvt->outmsg_len = 0;
- mutex_unlock(&hvt->outmsg_lock);
if (hvt->on_reset)
hvt->on_reset();
}
@@ -44,10 +42,17 @@ static ssize_t hvt_op_read(struct file *file, char __user *buf,
hvt = container_of(file->f_op, struct hvutil_transport, fops);
- if (wait_event_interruptible(hvt->outmsg_q, hvt->outmsg_len > 0))
+ if (wait_event_interruptible(hvt->outmsg_q, hvt->outmsg_len > 0 ||
+ hvt->mode != HVUTIL_TRANSPORT_CHARDEV))
return -EINTR;
- mutex_lock(&hvt->outmsg_lock);
+ mutex_lock(&hvt->lock);
+
+ if (hvt->mode == HVUTIL_TRANSPORT_DESTROY) {
+ ret = -EBADF;
+ goto out_unlock;
+ }
+
if (!hvt->outmsg) {
ret = -EAGAIN;
goto out_unlock;
@@ -68,7 +73,7 @@ static ssize_t hvt_op_read(struct file *file, char __user *buf,
hvt->outmsg_len = 0;
out_unlock:
- mutex_unlock(&hvt->outmsg_lock);
+ mutex_unlock(&hvt->lock);
return ret;
}
@@ -77,19 +82,22 @@ static ssize_t hvt_op_write(struct file *file, const char __user *buf,
{
struct hvutil_transport *hvt;
u8 *inmsg;
+ int ret;
hvt = container_of(file->f_op, struct hvutil_transport, fops);
- inmsg = kzalloc(count, GFP_KERNEL);
- if (copy_from_user(inmsg, buf, count)) {
- kfree(inmsg);
- return -EFAULT;
- }
- if (hvt->on_msg(inmsg, count))
- return -EFAULT;
+ inmsg = memdup_user(buf, count);
+ if (IS_ERR(inmsg))
+ return PTR_ERR(inmsg);
+
+ if (hvt->mode == HVUTIL_TRANSPORT_DESTROY)
+ ret = -EBADF;
+ else
+ ret = hvt->on_msg(inmsg, count);
+
kfree(inmsg);
- return count;
+ return ret ? ret : count;
}
static unsigned int hvt_op_poll(struct file *file, poll_table *wait)
@@ -99,6 +107,10 @@ static unsigned int hvt_op_poll(struct file *file, poll_table *wait)
hvt = container_of(file->f_op, struct hvutil_transport, fops);
poll_wait(file, &hvt->outmsg_q, wait);
+
+ if (hvt->mode == HVUTIL_TRANSPORT_DESTROY)
+ return POLLERR | POLLHUP;
+
if (hvt->outmsg_len > 0)
return POLLIN | POLLRDNORM;
@@ -108,40 +120,68 @@ static unsigned int hvt_op_poll(struct file *file, poll_table *wait)
static int hvt_op_open(struct inode *inode, struct file *file)
{
struct hvutil_transport *hvt;
+ int ret = 0;
+ bool issue_reset = false;
hvt = container_of(file->f_op, struct hvutil_transport, fops);
- /*
- * Switching to CHARDEV mode. We switch bach to INIT when device
- * gets released.
- */
- if (hvt->mode == HVUTIL_TRANSPORT_INIT)
+ mutex_lock(&hvt->lock);
+
+ if (hvt->mode == HVUTIL_TRANSPORT_DESTROY) {
+ ret = -EBADF;
+ } else if (hvt->mode == HVUTIL_TRANSPORT_INIT) {
+ /*
+ * Switching to CHARDEV mode. We switch bach to INIT when
+ * device gets released.
+ */
hvt->mode = HVUTIL_TRANSPORT_CHARDEV;
+ }
else if (hvt->mode == HVUTIL_TRANSPORT_NETLINK) {
/*
* We're switching from netlink communication to using char
* device. Issue the reset first.
*/
- hvt_reset(hvt);
+ issue_reset = true;
hvt->mode = HVUTIL_TRANSPORT_CHARDEV;
- } else
- return -EBUSY;
+ } else {
+ ret = -EBUSY;
+ }
- return 0;
+ if (issue_reset)
+ hvt_reset(hvt);
+
+ mutex_unlock(&hvt->lock);
+
+ return ret;
+}
+
+static void hvt_transport_free(struct hvutil_transport *hvt)
+{
+ misc_deregister(&hvt->mdev);
+ kfree(hvt->outmsg);
+ kfree(hvt);
}
static int hvt_op_release(struct inode *inode, struct file *file)
{
struct hvutil_transport *hvt;
+ int mode_old;
hvt = container_of(file->f_op, struct hvutil_transport, fops);
- hvt->mode = HVUTIL_TRANSPORT_INIT;
+ mutex_lock(&hvt->lock);
+ mode_old = hvt->mode;
+ if (hvt->mode != HVUTIL_TRANSPORT_DESTROY)
+ hvt->mode = HVUTIL_TRANSPORT_INIT;
/*
* Cleanup message buffers to avoid spurious messages when the daemon
* connects back.
*/
hvt_reset(hvt);
+ mutex_unlock(&hvt->lock);
+
+ if (mode_old == HVUTIL_TRANSPORT_DESTROY)
+ hvt_transport_free(hvt);
return 0;
}
@@ -168,6 +208,7 @@ static void hvt_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
* Switching to NETLINK mode. Switching to CHARDEV happens when someone
* opens the device.
*/
+ mutex_lock(&hvt->lock);
if (hvt->mode == HVUTIL_TRANSPORT_INIT)
hvt->mode = HVUTIL_TRANSPORT_NETLINK;
@@ -175,6 +216,7 @@ static void hvt_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
hvt_found->on_msg(msg->data, msg->len);
else
pr_warn("hvt_cn_callback: unexpected netlink message!\n");
+ mutex_unlock(&hvt->lock);
}
int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len)
@@ -182,7 +224,8 @@ int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len)
struct cn_msg *cn_msg;
int ret = 0;
- if (hvt->mode == HVUTIL_TRANSPORT_INIT) {
+ if (hvt->mode == HVUTIL_TRANSPORT_INIT ||
+ hvt->mode == HVUTIL_TRANSPORT_DESTROY) {
return -EINVAL;
} else if (hvt->mode == HVUTIL_TRANSPORT_NETLINK) {
cn_msg = kzalloc(sizeof(*cn_msg) + len, GFP_ATOMIC);
@@ -197,18 +240,26 @@ int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len)
return ret;
}
/* HVUTIL_TRANSPORT_CHARDEV */
- mutex_lock(&hvt->outmsg_lock);
+ mutex_lock(&hvt->lock);
+ if (hvt->mode != HVUTIL_TRANSPORT_CHARDEV) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
if (hvt->outmsg) {
/* Previous message wasn't received */
ret = -EFAULT;
goto out_unlock;
}
hvt->outmsg = kzalloc(len, GFP_KERNEL);
- memcpy(hvt->outmsg, msg, len);
- hvt->outmsg_len = len;
- wake_up_interruptible(&hvt->outmsg_q);
+ if (hvt->outmsg) {
+ memcpy(hvt->outmsg, msg, len);
+ hvt->outmsg_len = len;
+ wake_up_interruptible(&hvt->outmsg_q);
+ } else
+ ret = -ENOMEM;
out_unlock:
- mutex_unlock(&hvt->outmsg_lock);
+ mutex_unlock(&hvt->lock);
return ret;
}
@@ -239,7 +290,7 @@ struct hvutil_transport *hvutil_transport_init(const char *name,
hvt->mdev.fops = &hvt->fops;
init_waitqueue_head(&hvt->outmsg_q);
- mutex_init(&hvt->outmsg_lock);
+ mutex_init(&hvt->lock);
spin_lock(&hvt_list_lock);
list_add(&hvt->list, &hvt_list);
@@ -265,12 +316,25 @@ err_free_hvt:
void hvutil_transport_destroy(struct hvutil_transport *hvt)
{
+ int mode_old;
+
+ mutex_lock(&hvt->lock);
+ mode_old = hvt->mode;
+ hvt->mode = HVUTIL_TRANSPORT_DESTROY;
+ wake_up_interruptible(&hvt->outmsg_q);
+ mutex_unlock(&hvt->lock);
+
+ /*
+ * In case we were in 'chardev' mode we still have an open fd so we
+ * have to defer freeing the device. Netlink interface can be freed
+ * now.
+ */
spin_lock(&hvt_list_lock);
list_del(&hvt->list);
spin_unlock(&hvt_list_lock);
if (hvt->cn_id.idx > 0 && hvt->cn_id.val > 0)
cn_del_callback(&hvt->cn_id);
- misc_deregister(&hvt->mdev);
- kfree(hvt->outmsg);
- kfree(hvt);
+
+ if (mode_old != HVUTIL_TRANSPORT_CHARDEV)
+ hvt_transport_free(hvt);
}
diff --git a/drivers/hv/hv_utils_transport.h b/drivers/hv/hv_utils_transport.h
index 314c76ce1b07..06254a165a18 100644
--- a/drivers/hv/hv_utils_transport.h
+++ b/drivers/hv/hv_utils_transport.h
@@ -25,6 +25,7 @@ enum hvutil_transport_mode {
HVUTIL_TRANSPORT_INIT = 0,
HVUTIL_TRANSPORT_NETLINK,
HVUTIL_TRANSPORT_CHARDEV,
+ HVUTIL_TRANSPORT_DESTROY,
};
struct hvutil_transport {
@@ -38,7 +39,7 @@ struct hvutil_transport {
u8 *outmsg; /* message to the userspace */
int outmsg_len; /* its length */
wait_queue_head_t outmsg_q; /* poll/read wait queue */
- struct mutex outmsg_lock; /* protects outmsg */
+ struct mutex lock; /* protects struct members */
};
struct hvutil_transport *hvutil_transport_init(const char *name,
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 3782636562a1..4ebc796b4f33 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -31,6 +31,11 @@
#include <linux/hyperv.h>
/*
+ * Timeout for services such as KVP and fcopy.
+ */
+#define HV_UTIL_TIMEOUT 30
+
+/*
* The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent
* is set by CPUID(HVCPUID_VERSION_FEATURES).
*/
@@ -63,10 +68,6 @@ enum hv_cpuid_function {
/* Define version of the synthetic interrupt controller. */
#define HV_SYNIC_VERSION (1)
-/* Define synthetic interrupt controller message constants. */
-#define HV_MESSAGE_SIZE (256)
-#define HV_MESSAGE_PAYLOAD_BYTE_COUNT (240)
-#define HV_MESSAGE_PAYLOAD_QWORD_COUNT (30)
#define HV_ANY_VP (0xFFFFFFFF)
/* Define synthetic interrupt controller flag constants. */
@@ -74,48 +75,9 @@ enum hv_cpuid_function {
#define HV_EVENT_FLAGS_BYTE_COUNT (256)
#define HV_EVENT_FLAGS_DWORD_COUNT (256 / sizeof(u32))
-/* Define hypervisor message types. */
-enum hv_message_type {
- HVMSG_NONE = 0x00000000,
-
- /* Memory access messages. */
- HVMSG_UNMAPPED_GPA = 0x80000000,
- HVMSG_GPA_INTERCEPT = 0x80000001,
-
- /* Timer notification messages. */
- HVMSG_TIMER_EXPIRED = 0x80000010,
-
- /* Error messages. */
- HVMSG_INVALID_VP_REGISTER_VALUE = 0x80000020,
- HVMSG_UNRECOVERABLE_EXCEPTION = 0x80000021,
- HVMSG_UNSUPPORTED_FEATURE = 0x80000022,
-
- /* Trace buffer complete messages. */
- HVMSG_EVENTLOG_BUFFERCOMPLETE = 0x80000040,
-
- /* Platform-specific processor intercept messages. */
- HVMSG_X64_IOPORT_INTERCEPT = 0x80010000,
- HVMSG_X64_MSR_INTERCEPT = 0x80010001,
- HVMSG_X64_CPUID_INTERCEPT = 0x80010002,
- HVMSG_X64_EXCEPTION_INTERCEPT = 0x80010003,
- HVMSG_X64_APIC_EOI = 0x80010004,
- HVMSG_X64_LEGACY_FP_ERROR = 0x80010005
-};
-
-#define HV_SYNIC_STIMER_COUNT (4)
-
/* Define invalid partition identifier. */
#define HV_PARTITION_ID_INVALID ((u64)0x0)
-/* Define port identifier type. */
-union hv_port_id {
- u32 asu32;
- struct {
- u32 id:24;
- u32 reserved:8;
- } u ;
-};
-
/* Define port type. */
enum hv_port_type {
HVPORT_MSG = 1,
@@ -163,27 +125,6 @@ struct hv_connection_info {
};
};
-/* Define synthetic interrupt controller message flags. */
-union hv_message_flags {
- u8 asu8;
- struct {
- u8 msg_pending:1;
- u8 reserved:7;
- };
-};
-
-/* Define synthetic interrupt controller message header. */
-struct hv_message_header {
- enum hv_message_type message_type;
- u8 payload_size;
- union hv_message_flags message_flags;
- u8 reserved[2];
- union {
- u64 sender;
- union hv_port_id port;
- };
-};
-
/*
* Timer configuration register.
*/
@@ -200,31 +141,9 @@ union hv_timer_config {
};
};
-
-/* Define timer message payload structure. */
-struct hv_timer_message_payload {
- u32 timer_index;
- u32 reserved;
- u64 expiration_time; /* When the timer expired */
- u64 delivery_time; /* When the message was delivered */
-};
-
-/* Define synthetic interrupt controller message format. */
-struct hv_message {
- struct hv_message_header header;
- union {
- u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT];
- } u ;
-};
-
/* Define the number of message buffers associated with each port. */
#define HV_PORT_MESSAGE_BUFFER_COUNT (16)
-/* Define the synthetic interrupt message page layout. */
-struct hv_message_page {
- struct hv_message sint_message[HV_SYNIC_SINT_COUNT];
-};
-
/* Define the synthetic interrupt controller event flags format. */
union hv_synic_event_flags {
u8 flags8[HV_EVENT_FLAGS_BYTE_COUNT];
@@ -347,7 +266,7 @@ enum hv_call_code {
struct hv_input_post_message {
union hv_connection_id connectionid;
u32 reserved;
- enum hv_message_type message_type;
+ u32 message_type;
u32 payload_size;
u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT];
};
@@ -582,7 +501,7 @@ extern int hv_post_message(union hv_connection_id connection_id,
enum hv_message_type message_type,
void *payload, size_t payload_size);
-extern u16 hv_signal_event(void *con_id);
+extern int hv_signal_event(void *con_id);
extern int hv_synic_alloc(void);
@@ -614,14 +533,9 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *ring_info,
struct kvec *kv_list,
u32 kv_count, bool *signal);
-int hv_ringbuffer_peek(struct hv_ring_buffer_info *ring_info, void *buffer,
- u32 buflen);
-
-int hv_ringbuffer_read(struct hv_ring_buffer_info *ring_info,
- void *buffer,
- u32 buflen,
- u32 offset, bool *signal);
-
+int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info,
+ void *buffer, u32 buflen, u32 *buffer_actual_len,
+ u64 *requestid, bool *signal, bool raw);
void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info,
struct hv_ring_buffer_debug_info *debug_info);
@@ -678,7 +592,7 @@ struct vmbus_connection {
/* List of channels */
struct list_head chn_list;
- spinlock_t channel_lock;
+ struct mutex channel_mutex;
struct workqueue_struct *work_queue;
};
@@ -759,11 +673,7 @@ static inline void hv_poll_channel(struct vmbus_channel *channel,
if (!channel)
return;
- if (channel->target_cpu != smp_processor_id())
- smp_call_function_single(channel->target_cpu,
- cb, channel, true);
- else
- cb(channel);
+ smp_call_function_single(channel->target_cpu, cb, channel, true);
}
enum hvutil_device_state {
diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c
index 70a1a9a22f87..b53702ce692f 100644
--- a/drivers/hv/ring_buffer.c
+++ b/drivers/hv/ring_buffer.c
@@ -112,9 +112,7 @@ static bool hv_need_to_signal_on_read(u32 prev_write_sz,
u32 read_loc = rbi->ring_buffer->read_index;
u32 pending_sz = rbi->ring_buffer->pending_send_sz;
- /*
- * If the other end is not blocked on write don't bother.
- */
+ /* If the other end is not blocked on write don't bother. */
if (pending_sz == 0)
return false;
@@ -128,12 +126,7 @@ static bool hv_need_to_signal_on_read(u32 prev_write_sz,
return false;
}
-/*
- * hv_get_next_write_location()
- *
- * Get the next write location for the specified ring buffer
- *
- */
+/* Get the next write location for the specified ring buffer. */
static inline u32
hv_get_next_write_location(struct hv_ring_buffer_info *ring_info)
{
@@ -142,12 +135,7 @@ hv_get_next_write_location(struct hv_ring_buffer_info *ring_info)
return next;
}
-/*
- * hv_set_next_write_location()
- *
- * Set the next write location for the specified ring buffer
- *
- */
+/* Set the next write location for the specified ring buffer. */
static inline void
hv_set_next_write_location(struct hv_ring_buffer_info *ring_info,
u32 next_write_location)
@@ -155,11 +143,7 @@ hv_set_next_write_location(struct hv_ring_buffer_info *ring_info,
ring_info->ring_buffer->write_index = next_write_location;
}
-/*
- * hv_get_next_read_location()
- *
- * Get the next read location for the specified ring buffer
- */
+/* Get the next read location for the specified ring buffer. */
static inline u32
hv_get_next_read_location(struct hv_ring_buffer_info *ring_info)
{
@@ -169,10 +153,8 @@ hv_get_next_read_location(struct hv_ring_buffer_info *ring_info)
}
/*
- * hv_get_next_readlocation_withoffset()
- *
* Get the next read location + offset for the specified ring buffer.
- * This allows the caller to skip
+ * This allows the caller to skip.
*/
static inline u32
hv_get_next_readlocation_withoffset(struct hv_ring_buffer_info *ring_info,
@@ -186,13 +168,7 @@ hv_get_next_readlocation_withoffset(struct hv_ring_buffer_info *ring_info,
return next;
}
-/*
- *
- * hv_set_next_read_location()
- *
- * Set the next read location for the specified ring buffer
- *
- */
+/* Set the next read location for the specified ring buffer. */
static inline void
hv_set_next_read_location(struct hv_ring_buffer_info *ring_info,
u32 next_read_location)
@@ -201,12 +177,7 @@ hv_set_next_read_location(struct hv_ring_buffer_info *ring_info,
}
-/*
- *
- * hv_get_ring_buffer()
- *
- * Get the start of the ring buffer
- */
+/* Get the start of the ring buffer. */
static inline void *
hv_get_ring_buffer(struct hv_ring_buffer_info *ring_info)
{
@@ -214,25 +185,14 @@ hv_get_ring_buffer(struct hv_ring_buffer_info *ring_info)
}
-/*
- *
- * hv_get_ring_buffersize()
- *
- * Get the size of the ring buffer
- */
+/* Get the size of the ring buffer. */
static inline u32
hv_get_ring_buffersize(struct hv_ring_buffer_info *ring_info)
{
return ring_info->ring_datasize;
}
-/*
- *
- * hv_get_ring_bufferindices()
- *
- * Get the read and write indices as u64 of the specified ring buffer
- *
- */
+/* Get the read and write indices as u64 of the specified ring buffer. */
static inline u64
hv_get_ring_bufferindices(struct hv_ring_buffer_info *ring_info)
{
@@ -240,12 +200,8 @@ hv_get_ring_bufferindices(struct hv_ring_buffer_info *ring_info)
}
/*
- *
- * hv_copyfrom_ringbuffer()
- *
* Helper routine to copy to source from ring buffer.
* Assume there is enough room. Handles wrap-around in src case only!!
- *
*/
static u32 hv_copyfrom_ringbuffer(
struct hv_ring_buffer_info *ring_info,
@@ -277,12 +233,8 @@ static u32 hv_copyfrom_ringbuffer(
/*
- *
- * hv_copyto_ringbuffer()
- *
* Helper routine to copy from source to ring buffer.
* Assume there is enough room. Handles wrap-around in dest case only!!
- *
*/
static u32 hv_copyto_ringbuffer(
struct hv_ring_buffer_info *ring_info,
@@ -308,13 +260,7 @@ static u32 hv_copyto_ringbuffer(
return start_write_offset;
}
-/*
- *
- * hv_ringbuffer_get_debuginfo()
- *
- * Get various debug metrics for the specified ring buffer
- *
- */
+/* Get various debug metrics for the specified ring buffer. */
void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info,
struct hv_ring_buffer_debug_info *debug_info)
{
@@ -337,13 +283,7 @@ void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info,
}
}
-/*
- *
- * hv_ringbuffer_init()
- *
- *Initialize the ring buffer
- *
- */
+/* Initialize the ring buffer. */
int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
void *buffer, u32 buflen)
{
@@ -356,9 +296,7 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
ring_info->ring_buffer->read_index =
ring_info->ring_buffer->write_index = 0;
- /*
- * Set the feature bit for enabling flow control.
- */
+ /* Set the feature bit for enabling flow control. */
ring_info->ring_buffer->feature_bits.value = 1;
ring_info->ring_size = buflen;
@@ -369,24 +307,12 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
return 0;
}
-/*
- *
- * hv_ringbuffer_cleanup()
- *
- * Cleanup the ring buffer
- *
- */
+/* Cleanup the ring buffer. */
void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info)
{
}
-/*
- *
- * hv_ringbuffer_write()
- *
- * Write to the ring buffer
- *
- */
+/* Write to the ring buffer. */
int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
struct kvec *kv_list, u32 kv_count, bool *signal)
{
@@ -411,10 +337,11 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
&bytes_avail_toread,
&bytes_avail_towrite);
-
- /* If there is only room for the packet, assume it is full. */
- /* Otherwise, the next time around, we think the ring buffer */
- /* is empty since the read index == write index */
+ /*
+ * If there is only room for the packet, assume it is full.
+ * Otherwise, the next time around, we think the ring buffer
+ * is empty since the read index == write index.
+ */
if (bytes_avail_towrite <= totalbytes_towrite) {
spin_unlock_irqrestore(&outring_info->ring_lock, flags);
return -EAGAIN;
@@ -453,80 +380,59 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
return 0;
}
-
-/*
- *
- * hv_ringbuffer_peek()
- *
- * Read without advancing the read index
- *
- */
-int hv_ringbuffer_peek(struct hv_ring_buffer_info *Inring_info,
- void *Buffer, u32 buflen)
-{
- u32 bytes_avail_towrite;
- u32 bytes_avail_toread;
- u32 next_read_location = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&Inring_info->ring_lock, flags);
-
- hv_get_ringbuffer_availbytes(Inring_info,
- &bytes_avail_toread,
- &bytes_avail_towrite);
-
- /* Make sure there is something to read */
- if (bytes_avail_toread < buflen) {
-
- spin_unlock_irqrestore(&Inring_info->ring_lock, flags);
-
- return -EAGAIN;
- }
-
- /* Convert to byte offset */
- next_read_location = hv_get_next_read_location(Inring_info);
-
- next_read_location = hv_copyfrom_ringbuffer(Inring_info,
- Buffer,
- buflen,
- next_read_location);
-
- spin_unlock_irqrestore(&Inring_info->ring_lock, flags);
-
- return 0;
-}
-
-
-/*
- *
- * hv_ringbuffer_read()
- *
- * Read and advance the read index
- *
- */
-int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer,
- u32 buflen, u32 offset, bool *signal)
+int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info,
+ void *buffer, u32 buflen, u32 *buffer_actual_len,
+ u64 *requestid, bool *signal, bool raw)
{
u32 bytes_avail_towrite;
u32 bytes_avail_toread;
u32 next_read_location = 0;
u64 prev_indices = 0;
unsigned long flags;
+ struct vmpacket_descriptor desc;
+ u32 offset;
+ u32 packetlen;
+ int ret = 0;
if (buflen <= 0)
return -EINVAL;
spin_lock_irqsave(&inring_info->ring_lock, flags);
+ *buffer_actual_len = 0;
+ *requestid = 0;
+
hv_get_ringbuffer_availbytes(inring_info,
&bytes_avail_toread,
&bytes_avail_towrite);
/* Make sure there is something to read */
- if (bytes_avail_toread < buflen) {
- spin_unlock_irqrestore(&inring_info->ring_lock, flags);
+ if (bytes_avail_toread < sizeof(desc)) {
+ /*
+ * No error is set when there is even no header, drivers are
+ * supposed to analyze buffer_actual_len.
+ */
+ goto out_unlock;
+ }
- return -EAGAIN;
+ next_read_location = hv_get_next_read_location(inring_info);
+ next_read_location = hv_copyfrom_ringbuffer(inring_info, &desc,
+ sizeof(desc),
+ next_read_location);
+
+ offset = raw ? 0 : (desc.offset8 << 3);
+ packetlen = (desc.len8 << 3) - offset;
+ *buffer_actual_len = packetlen;
+ *requestid = desc.trans_id;
+
+ if (bytes_avail_toread < packetlen + offset) {
+ ret = -EAGAIN;
+ goto out_unlock;
+ }
+
+ if (packetlen > buflen) {
+ ret = -ENOBUFS;
+ goto out_unlock;
}
next_read_location =
@@ -534,7 +440,7 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer,
next_read_location = hv_copyfrom_ringbuffer(inring_info,
buffer,
- buflen,
+ packetlen,
next_read_location);
next_read_location = hv_copyfrom_ringbuffer(inring_info,
@@ -542,17 +448,19 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer,
sizeof(u64),
next_read_location);
- /* Make sure all reads are done before we update the read index since */
- /* the writer may start writing to the read area once the read index */
- /*is updated */
+ /*
+ * Make sure all reads are done before we update the read index since
+ * the writer may start writing to the read area once the read index
+ * is updated.
+ */
mb();
/* Update the read index */
hv_set_next_read_location(inring_info, next_read_location);
- spin_unlock_irqrestore(&inring_info->ring_lock, flags);
-
*signal = hv_need_to_signal_on_read(bytes_avail_towrite, inring_info);
- return 0;
+out_unlock:
+ spin_unlock_irqrestore(&inring_info->ring_lock, flags);
+ return ret;
}
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index f19b6f7a467a..328e4c3808e0 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -47,7 +47,6 @@ static struct acpi_device *hv_acpi_dev;
static struct tasklet_struct msg_dpc;
static struct completion probe_event;
-static int irq;
static void hyperv_report_panic(struct pt_regs *regs)
@@ -531,9 +530,9 @@ static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env)
static const uuid_le null_guid;
-static inline bool is_null_guid(const __u8 *guid)
+static inline bool is_null_guid(const uuid_le *guid)
{
- if (memcmp(guid, &null_guid, sizeof(uuid_le)))
+ if (uuid_le_cmp(*guid, null_guid))
return false;
return true;
}
@@ -544,10 +543,10 @@ static inline bool is_null_guid(const __u8 *guid)
*/
static const struct hv_vmbus_device_id *hv_vmbus_get_id(
const struct hv_vmbus_device_id *id,
- const __u8 *guid)
+ const uuid_le *guid)
{
- for (; !is_null_guid(id->guid); id++)
- if (!memcmp(&id->guid, guid, sizeof(uuid_le)))
+ for (; !is_null_guid(&id->guid); id++)
+ if (!uuid_le_cmp(id->guid, *guid))
return id;
return NULL;
@@ -563,7 +562,7 @@ static int vmbus_match(struct device *device, struct device_driver *driver)
struct hv_driver *drv = drv_to_hv_drv(driver);
struct hv_device *hv_dev = device_to_hv_device(device);
- if (hv_vmbus_get_id(drv->id_table, hv_dev->dev_type.b))
+ if (hv_vmbus_get_id(drv->id_table, &hv_dev->dev_type))
return 1;
return 0;
@@ -580,7 +579,7 @@ static int vmbus_probe(struct device *child_device)
struct hv_device *dev = device_to_hv_device(child_device);
const struct hv_vmbus_device_id *dev_id;
- dev_id = hv_vmbus_get_id(drv->id_table, dev->dev_type.b);
+ dev_id = hv_vmbus_get_id(drv->id_table, &dev->dev_type);
if (drv->probe) {
ret = drv->probe(dev, dev_id);
if (ret != 0)
@@ -602,23 +601,11 @@ static int vmbus_remove(struct device *child_device)
{
struct hv_driver *drv;
struct hv_device *dev = device_to_hv_device(child_device);
- u32 relid = dev->channel->offermsg.child_relid;
if (child_device->driver) {
drv = drv_to_hv_drv(child_device->driver);
if (drv->remove)
drv->remove(dev);
- else {
- hv_process_channel_removal(dev->channel, relid);
- pr_err("remove not set for driver %s\n",
- dev_name(child_device));
- }
- } else {
- /*
- * We don't have a driver for this device; deal with the
- * rescind message by removing the channel.
- */
- hv_process_channel_removal(dev->channel, relid);
}
return 0;
@@ -653,7 +640,10 @@ static void vmbus_shutdown(struct device *child_device)
static void vmbus_device_release(struct device *device)
{
struct hv_device *hv_dev = device_to_hv_device(device);
+ struct vmbus_channel *channel = hv_dev->channel;
+ hv_process_channel_removal(channel,
+ channel->offermsg.child_relid);
kfree(hv_dev);
}
@@ -835,10 +825,9 @@ static void vmbus_isr(void)
* Here, we
* - initialize the vmbus driver context
* - invoke the vmbus hv main init routine
- * - get the irq resource
* - retrieve the channel offers
*/
-static int vmbus_bus_init(int irq)
+static int vmbus_bus_init(void)
{
int ret;
@@ -867,7 +856,7 @@ static int vmbus_bus_init(int irq)
on_each_cpu(hv_synic_init, NULL, 1);
ret = vmbus_connect();
if (ret)
- goto err_alloc;
+ goto err_connect;
if (vmbus_proto_version > VERSION_WIN7)
cpu_hotplug_disable();
@@ -885,6 +874,8 @@ static int vmbus_bus_init(int irq)
return 0;
+err_connect:
+ on_each_cpu(hv_synic_cleanup, NULL, 1);
err_alloc:
hv_synic_free();
hv_remove_vmbus_irq();
@@ -1031,9 +1022,6 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx)
struct resource **prev_res = NULL;
switch (res->type) {
- case ACPI_RESOURCE_TYPE_IRQ:
- irq = res->data.irq.interrupts[0];
- return AE_OK;
/*
* "Address" descriptors are for bus windows. Ignore
@@ -1075,12 +1063,28 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx)
new_res->start = start;
new_res->end = end;
+ /*
+ * Stick ranges from higher in address space at the front of the list.
+ * If two ranges are adjacent, merge them.
+ */
do {
if (!*old_res) {
*old_res = new_res;
break;
}
+ if (((*old_res)->end + 1) == new_res->start) {
+ (*old_res)->end = new_res->end;
+ kfree(new_res);
+ break;
+ }
+
+ if ((*old_res)->start == new_res->end + 1) {
+ (*old_res)->start = new_res->start;
+ kfree(new_res);
+ break;
+ }
+
if ((*old_res)->end < new_res->start) {
new_res->sibling = *old_res;
if (prev_res)
@@ -1191,6 +1195,23 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
}
EXPORT_SYMBOL_GPL(vmbus_allocate_mmio);
+/**
+ * vmbus_cpu_number_to_vp_number() - Map CPU to VP.
+ * @cpu_number: CPU number in Linux terms
+ *
+ * This function returns the mapping between the Linux processor
+ * number and the hypervisor's virtual processor number, useful
+ * in making hypercalls and such that talk about specific
+ * processors.
+ *
+ * Return: Virtual processor number in Hyper-V terms
+ */
+int vmbus_cpu_number_to_vp_number(int cpu_number)
+{
+ return hv_context.vp_index[cpu_number];
+}
+EXPORT_SYMBOL_GPL(vmbus_cpu_number_to_vp_number);
+
static int vmbus_acpi_add(struct acpi_device *device)
{
acpi_status result;
@@ -1275,7 +1296,7 @@ static int __init hv_acpi_init(void)
init_completion(&probe_event);
/*
- * Get irq resources first.
+ * Get ACPI resources first.
*/
ret = acpi_bus_register_driver(&vmbus_acpi_driver);
@@ -1288,12 +1309,7 @@ static int __init hv_acpi_init(void)
goto cleanup;
}
- if (irq <= 0) {
- ret = -ENODEV;
- goto cleanup;
- }
-
- ret = vmbus_bus_init(irq);
+ ret = vmbus_bus_init();
if (ret)
goto cleanup;
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 8f59f057cdf4..60fb80bd353d 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -859,16 +859,6 @@ config SENSORS_MAX31790
This driver can also be built as a module. If so, the module
will be called max31790.
-config SENSORS_HTU21
- tristate "Measurement Specialties HTU21D humidity/temperature sensors"
- depends on I2C
- help
- If you say yes here you get support for the Measurement Specialties
- HTU21D humidity and temperature sensors.
-
- This driver can also be built as a module. If so, the module
- will be called htu21.
-
config SENSORS_MCP3021
tristate "Microchip MCP3021 and compatibles"
depends on I2C
@@ -1217,6 +1207,7 @@ config SENSORS_PWM_FAN
config SENSORS_SHT15
tristate "Sensiron humidity and temperature sensors. SHT15 and compat."
depends on GPIOLIB || COMPILE_TEST
+ select BITREVERSE
help
If you say yes here you get support for the Sensiron SHT10, SHT11,
SHT15, SHT71, SHT75 humidity and temperature sensors.
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 12a32398fdcc..30c94df31465 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -68,7 +68,6 @@ obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o
obj-$(CONFIG_SENSORS_HIH6130) += hih6130.o
-obj-$(CONFIG_SENSORS_HTU21) += htu21.o
obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o
obj-$(CONFIG_SENSORS_I5500) += i5500_temp.o
obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
index 5f7067d7b625..f77eb971ce95 100644
--- a/drivers/hwmon/fam15h_power.c
+++ b/drivers/hwmon/fam15h_power.c
@@ -47,6 +47,8 @@ MODULE_LICENSE("GPL");
#define MSR_F15H_CU_MAX_PWR_ACCUMULATOR 0xc001007b
+#define PCI_DEVICE_ID_AMD_15H_M70H_NB_F4 0x15b4
+
struct fam15h_power_data {
struct pci_dev *pdev;
unsigned int tdp_to_watts;
@@ -124,7 +126,7 @@ static int fam15h_power_init_attrs(struct pci_dev *pdev,
if (c->x86 == 0x15 &&
(c->x86_model <= 0xf ||
- (c->x86_model >= 0x60 && c->x86_model <= 0x6f)))
+ (c->x86_model >= 0x60 && c->x86_model <= 0x7f)))
n += 1;
fam15h_power_attrs = devm_kcalloc(&pdev->dev, n,
@@ -138,7 +140,7 @@ static int fam15h_power_init_attrs(struct pci_dev *pdev,
fam15h_power_attrs[n++] = &dev_attr_power1_crit.attr;
if (c->x86 == 0x15 &&
(c->x86_model <= 0xf ||
- (c->x86_model >= 0x60 && c->x86_model <= 0x6f)))
+ (c->x86_model >= 0x60 && c->x86_model <= 0x7f)))
fam15h_power_attrs[n++] = &dev_attr_power1_input.attr;
data->group.attrs = fam15h_power_attrs;
@@ -296,6 +298,7 @@ static const struct pci_device_id fam15h_power_id_table[] = {
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F4) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F4) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M70H_NB_F4) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F4) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F4) },
{}
diff --git a/drivers/hwmon/htu21.c b/drivers/hwmon/htu21.c
deleted file mode 100644
index 4c3bbb72f82a..000000000000
--- a/drivers/hwmon/htu21.c
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Measurement Specialties HTU21D humidity and temperature sensor driver
- *
- * Copyright (C) 2013 William Markezana <william.markezana@meas-spec.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/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-#include <linux/err.h>
-#include <linux/mutex.h>
-#include <linux/device.h>
-#include <linux/jiffies.h>
-
-/* HTU21 Commands */
-#define HTU21_T_MEASUREMENT_HM 0xE3
-#define HTU21_RH_MEASUREMENT_HM 0xE5
-
-struct htu21 {
- struct i2c_client *client;
- struct mutex lock;
- bool valid;
- unsigned long last_update;
- int temperature;
- int humidity;
-};
-
-static inline int htu21_temp_ticks_to_millicelsius(int ticks)
-{
- ticks &= ~0x0003; /* clear status bits */
- /*
- * Formula T = -46.85 + 175.72 * ST / 2^16 from datasheet p14,
- * optimized for integer fixed point (3 digits) arithmetic
- */
- return ((21965 * ticks) >> 13) - 46850;
-}
-
-static inline int htu21_rh_ticks_to_per_cent_mille(int ticks)
-{
- ticks &= ~0x0003; /* clear status bits */
- /*
- * Formula RH = -6 + 125 * SRH / 2^16 from datasheet p14,
- * optimized for integer fixed point (3 digits) arithmetic
- */
- return ((15625 * ticks) >> 13) - 6000;
-}
-
-static int htu21_update_measurements(struct device *dev)
-{
- struct htu21 *htu21 = dev_get_drvdata(dev);
- struct i2c_client *client = htu21->client;
- int ret = 0;
-
- mutex_lock(&htu21->lock);
-
- if (time_after(jiffies, htu21->last_update + HZ / 2) ||
- !htu21->valid) {
- ret = i2c_smbus_read_word_swapped(client,
- HTU21_T_MEASUREMENT_HM);
- if (ret < 0)
- goto out;
- htu21->temperature = htu21_temp_ticks_to_millicelsius(ret);
- ret = i2c_smbus_read_word_swapped(client,
- HTU21_RH_MEASUREMENT_HM);
- if (ret < 0)
- goto out;
- htu21->humidity = htu21_rh_ticks_to_per_cent_mille(ret);
- htu21->last_update = jiffies;
- htu21->valid = true;
- }
-out:
- mutex_unlock(&htu21->lock);
-
- return ret >= 0 ? 0 : ret;
-}
-
-static ssize_t htu21_show_temperature(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct htu21 *htu21 = dev_get_drvdata(dev);
- int ret;
-
- ret = htu21_update_measurements(dev);
- if (ret < 0)
- return ret;
- return sprintf(buf, "%d\n", htu21->temperature);
-}
-
-static ssize_t htu21_show_humidity(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct htu21 *htu21 = dev_get_drvdata(dev);
- int ret;
-
- ret = htu21_update_measurements(dev);
- if (ret < 0)
- return ret;
- return sprintf(buf, "%d\n", htu21->humidity);
-}
-
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
- htu21_show_temperature, NULL, 0);
-static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO,
- htu21_show_humidity, NULL, 0);
-
-static struct attribute *htu21_attrs[] = {
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_humidity1_input.dev_attr.attr,
- NULL
-};
-
-ATTRIBUTE_GROUPS(htu21);
-
-static int htu21_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct device *dev = &client->dev;
- struct htu21 *htu21;
- struct device *hwmon_dev;
-
- if (!i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_READ_WORD_DATA)) {
- dev_err(&client->dev,
- "adapter does not support SMBus word transactions\n");
- return -ENODEV;
- }
-
- htu21 = devm_kzalloc(dev, sizeof(*htu21), GFP_KERNEL);
- if (!htu21)
- return -ENOMEM;
-
- htu21->client = client;
- mutex_init(&htu21->lock);
-
- hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
- htu21,
- htu21_groups);
- return PTR_ERR_OR_ZERO(hwmon_dev);
-}
-
-static const struct i2c_device_id htu21_id[] = {
- { "htu21", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, htu21_id);
-
-static struct i2c_driver htu21_driver = {
- .class = I2C_CLASS_HWMON,
- .driver = {
- .name = "htu21",
- },
- .probe = htu21_probe,
- .id_table = htu21_id,
-};
-
-module_i2c_driver(htu21_driver);
-
-MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");
-MODULE_DESCRIPTION("MEAS HTU21D humidity and temperature sensor driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c
index 7a8a6fbf11ff..1f643782ce04 100644
--- a/drivers/hwmon/ibmaem.c
+++ b/drivers/hwmon/ibmaem.c
@@ -920,8 +920,8 @@ static ssize_t aem_set_power_period(struct device *dev,
/* Discover sensors on an AEM device */
static int aem_register_sensors(struct aem_data *data,
- struct aem_ro_sensor_template *ro,
- struct aem_rw_sensor_template *rw)
+ const struct aem_ro_sensor_template *ro,
+ const struct aem_rw_sensor_template *rw)
{
struct device *dev = &data->pdev->dev;
struct sensor_device_attribute *sensors = data->sensors;
@@ -1020,19 +1020,19 @@ static void aem_remove_sensors(struct aem_data *data)
/* Sensor probe functions */
/* Description of AEM1 sensors */
-static struct aem_ro_sensor_template aem1_ro_sensors[] = {
+static const struct aem_ro_sensor_template aem1_ro_sensors[] = {
{"energy1_input", aem_show_energy, 0},
{"power1_average", aem_show_power, 0},
{NULL, NULL, 0},
};
-static struct aem_rw_sensor_template aem1_rw_sensors[] = {
+static const struct aem_rw_sensor_template aem1_rw_sensors[] = {
{"power1_average_interval", aem_show_power_period, aem_set_power_period, 0},
{NULL, NULL, NULL, 0},
};
/* Description of AEM2 sensors */
-static struct aem_ro_sensor_template aem2_ro_sensors[] = {
+static const struct aem_ro_sensor_template aem2_ro_sensors[] = {
{"energy1_input", aem_show_energy, 0},
{"energy2_input", aem_show_energy, 1},
{"power1_average", aem_show_power, 0},
@@ -1050,7 +1050,7 @@ static struct aem_ro_sensor_template aem2_ro_sensors[] = {
{NULL, NULL, 0},
};
-static struct aem_rw_sensor_template aem2_rw_sensors[] = {
+static const struct aem_rw_sensor_template aem2_rw_sensors[] = {
{"power1_average_interval", aem_show_power_period, aem_set_power_period, 0},
{"power2_average_interval", aem_show_power_period, aem_set_power_period, 1},
{NULL, NULL, NULL, 0},
diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c
index 37f01702d081..559c596b24f9 100644
--- a/drivers/hwmon/nct6683.c
+++ b/drivers/hwmon/nct6683.c
@@ -29,7 +29,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/acpi.h>
-#include <linux/dmi.h>
+#include <linux/delay.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
@@ -45,7 +45,7 @@ enum kinds { nct6683 };
static bool force;
module_param(force, bool, 0);
-MODULE_PARM_DESC(force, "Set to one to enable detection on non-Intel boards");
+MODULE_PARM_DESC(force, "Set to one to enable support for unknown vendors");
static const char * const nct6683_device_names[] = {
"nct6683",
@@ -141,6 +141,7 @@ superio_exit(int ioreg)
#define NCT6683_REG_MON(x) (0x100 + (x) * 2)
#define NCT6683_REG_FAN_RPM(x) (0x140 + (x) * 2)
#define NCT6683_REG_PWM(x) (0x160 + (x))
+#define NCT6683_REG_PWM_WRITE(x) (0xa28 + (x))
#define NCT6683_REG_MON_STS(x) (0x174 + (x))
#define NCT6683_REG_IDLE(x) (0x178 + (x))
@@ -165,8 +166,13 @@ superio_exit(int ioreg)
#define NCT6683_REG_FAN_MIN(x) (0x3b8 + (x) * 2) /* 16 bit */
+#define NCT6683_REG_FAN_CFG_CTRL 0xa01
+#define NCT6683_FAN_CFG_REQ 0x80
+#define NCT6683_FAN_CFG_DONE 0x40
+
#define NCT6683_REG_CUSTOMER_ID 0x602
#define NCT6683_CUSTOMER_ID_INTEL 0x805
+#define NCT6683_CUSTOMER_ID_MITAC 0xa0e
#define NCT6683_REG_BUILD_YEAR 0x604
#define NCT6683_REG_BUILD_MONTH 0x605
@@ -394,7 +400,8 @@ struct sensor_template_group {
};
static struct attribute_group *
-nct6683_create_attr_group(struct device *dev, struct sensor_template_group *tg,
+nct6683_create_attr_group(struct device *dev,
+ const struct sensor_template_group *tg,
int repeat)
{
struct sensor_device_attribute_2 *a2;
@@ -559,6 +566,7 @@ static int get_temp_reg(struct nct6683_data *data, int nr, int index)
break;
}
break;
+ case NCT6683_CUSTOMER_ID_MITAC:
default:
switch (nr) {
default:
@@ -703,7 +711,7 @@ static struct sensor_device_template *nct6683_attributes_in_template[] = {
NULL
};
-static struct sensor_template_group nct6683_in_template_group = {
+static const struct sensor_template_group nct6683_in_template_group = {
.templates = nct6683_attributes_in_template,
.is_visible = nct6683_in_is_visible,
};
@@ -774,7 +782,7 @@ static struct sensor_device_template *nct6683_attributes_fan_template[] = {
NULL
};
-static struct sensor_template_group nct6683_fan_template_group = {
+static const struct sensor_template_group nct6683_fan_template_group = {
.templates = nct6683_attributes_fan_template,
.is_visible = nct6683_fan_is_visible,
.base = 1,
@@ -902,7 +910,7 @@ static struct sensor_device_template *nct6683_attributes_temp_template[] = {
NULL
};
-static struct sensor_template_group nct6683_temp_template_group = {
+static const struct sensor_template_group nct6683_temp_template_group = {
.templates = nct6683_attributes_temp_template,
.is_visible = nct6683_temp_is_visible,
.base = 1,
@@ -918,7 +926,29 @@ show_pwm(struct device *dev, struct device_attribute *attr, char *buf)
return sprintf(buf, "%d\n", data->pwm[index]);
}
-SENSOR_TEMPLATE(pwm, "pwm%d", S_IRUGO, show_pwm, NULL, 0);
+static ssize_t
+store_pwm(struct device *dev, struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct6683_data *data = dev_get_drvdata(dev);
+ int index = sattr->index;
+ unsigned long val;
+
+ if (kstrtoul(buf, 10, &val) || val > 255)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ nct6683_write(data, NCT6683_REG_FAN_CFG_CTRL, NCT6683_FAN_CFG_REQ);
+ usleep_range(1000, 2000);
+ nct6683_write(data, NCT6683_REG_PWM_WRITE(index), val);
+ nct6683_write(data, NCT6683_REG_FAN_CFG_CTRL, NCT6683_FAN_CFG_DONE);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+SENSOR_TEMPLATE(pwm, "pwm%d", S_IRUGO, show_pwm, store_pwm, 0);
static umode_t nct6683_pwm_is_visible(struct kobject *kobj,
struct attribute *attr, int index)
@@ -930,6 +960,10 @@ static umode_t nct6683_pwm_is_visible(struct kobject *kobj,
if (!(data->have_pwm & (1 << pwm)))
return 0;
+ /* Only update pwm values for Mitac boards */
+ if (data->customer_id == NCT6683_CUSTOMER_ID_MITAC)
+ return attr->mode | S_IWUSR;
+
return attr->mode;
}
@@ -938,7 +972,7 @@ static struct sensor_device_template *nct6683_attributes_pwm_template[] = {
NULL
};
-static struct sensor_template_group nct6683_pwm_template_group = {
+static const struct sensor_template_group nct6683_pwm_template_group = {
.templates = nct6683_attributes_pwm_template,
.is_visible = nct6683_pwm_is_visible,
.base = 1,
@@ -1170,6 +1204,7 @@ static int nct6683_probe(struct platform_device *pdev)
struct device *hwmon_dev;
struct resource *res;
int groups = 0;
+ char build[16];
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!devm_request_region(dev, res->start, IOREGION_LENGTH, DRVNAME))
@@ -1187,6 +1222,17 @@ static int nct6683_probe(struct platform_device *pdev)
data->customer_id = nct6683_read16(data, NCT6683_REG_CUSTOMER_ID);
+ /* By default only instantiate driver if the customer ID is known */
+ switch (data->customer_id) {
+ case NCT6683_CUSTOMER_ID_INTEL:
+ break;
+ case NCT6683_CUSTOMER_ID_MITAC:
+ break;
+ default:
+ if (!force)
+ return -ENODEV;
+ }
+
nct6683_init_device(data);
nct6683_setup_fans(data);
nct6683_setup_sensors(data);
@@ -1230,13 +1276,22 @@ static int nct6683_probe(struct platform_device *pdev)
}
data->groups[groups++] = &nct6683_group_other;
- dev_info(dev, "%s EC firmware version %d.%d build %02x/%02x/%02x\n",
+ if (data->customer_id == NCT6683_CUSTOMER_ID_INTEL)
+ scnprintf(build, sizeof(build), "%02x/%02x/%02x",
+ nct6683_read(data, NCT6683_REG_BUILD_MONTH),
+ nct6683_read(data, NCT6683_REG_BUILD_DAY),
+ nct6683_read(data, NCT6683_REG_BUILD_YEAR));
+ else
+ scnprintf(build, sizeof(build), "%02d/%02d/%02d",
+ nct6683_read(data, NCT6683_REG_BUILD_MONTH),
+ nct6683_read(data, NCT6683_REG_BUILD_DAY),
+ nct6683_read(data, NCT6683_REG_BUILD_YEAR));
+
+ dev_info(dev, "%s EC firmware version %d.%d build %s\n",
nct6683_chip_names[data->kind],
nct6683_read(data, NCT6683_REG_VERSION_HI),
nct6683_read(data, NCT6683_REG_VERSION_LO),
- nct6683_read(data, NCT6683_REG_BUILD_MONTH),
- nct6683_read(data, NCT6683_REG_BUILD_DAY),
- nct6683_read(data, NCT6683_REG_BUILD_YEAR));
+ build);
hwmon_dev = devm_hwmon_device_register_with_groups(dev,
nct6683_device_names[data->kind], data, data->groups);
@@ -1292,20 +1347,10 @@ static struct platform_driver nct6683_driver = {
static int __init nct6683_find(int sioaddr, struct nct6683_sio_data *sio_data)
{
- const char *board_vendor;
int addr;
u16 val;
int err;
- /*
- * Only run on Intel boards unless the 'force' module parameter is set
- */
- if (!force) {
- board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
- if (!board_vendor || strcmp(board_vendor, "Intel Corporation"))
- return -ENODEV;
- }
-
err = superio_enter(sioaddr);
if (err)
return err;
diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c
index d7ebdf8651f5..d087a8e00cf5 100644
--- a/drivers/hwmon/nct6775.c
+++ b/drivers/hwmon/nct6775.c
@@ -1045,7 +1045,8 @@ struct sensor_template_group {
};
static struct attribute_group *
-nct6775_create_attr_group(struct device *dev, struct sensor_template_group *tg,
+nct6775_create_attr_group(struct device *dev,
+ const struct sensor_template_group *tg,
int repeat)
{
struct attribute_group *group;
@@ -1827,7 +1828,7 @@ static struct sensor_device_template *nct6775_attributes_in_template[] = {
NULL
};
-static struct sensor_template_group nct6775_in_template_group = {
+static const struct sensor_template_group nct6775_in_template_group = {
.templates = nct6775_attributes_in_template,
.is_visible = nct6775_in_is_visible,
};
@@ -2046,7 +2047,7 @@ static struct sensor_device_template *nct6775_attributes_fan_template[] = {
NULL
};
-static struct sensor_template_group nct6775_fan_template_group = {
+static const struct sensor_template_group nct6775_fan_template_group = {
.templates = nct6775_attributes_fan_template,
.is_visible = nct6775_fan_is_visible,
.base = 1,
@@ -2255,7 +2256,7 @@ static struct sensor_device_template *nct6775_attributes_temp_template[] = {
NULL
};
-static struct sensor_template_group nct6775_temp_template_group = {
+static const struct sensor_template_group nct6775_temp_template_group = {
.templates = nct6775_attributes_temp_template,
.is_visible = nct6775_temp_is_visible,
.base = 1,
@@ -3117,7 +3118,7 @@ static struct sensor_device_template *nct6775_attributes_pwm_template[] = {
NULL
};
-static struct sensor_template_group nct6775_pwm_template_group = {
+static const struct sensor_template_group nct6775_pwm_template_group = {
.templates = nct6775_attributes_pwm_template,
.is_visible = nct6775_pwm_is_visible,
.base = 1,
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index df6ebb2b8f0f..7e5cc3d025ef 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -65,6 +65,16 @@ config SENSORS_LTC2978_REGULATOR
If you say yes here you get regulator support for Linear
Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, and LTM4676.
+config SENSORS_LTC3815
+ tristate "Linear Technologies LTC3815"
+ default n
+ help
+ If you say yes here you get hardware monitoring support for Linear
+ Technology LTC3815.
+
+ This driver can also be built as a module. If so, the module will
+ be called ltc3815.
+
config SENSORS_MAX16064
tristate "Maxim MAX16064"
default n
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index bce046d37f02..562132054aaf 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o
obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o
obj-$(CONFIG_SENSORS_LM25066) += lm25066.o
obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
+obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o
obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
obj-$(CONFIG_SENSORS_MAX20751) += max20751.o
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
diff --git a/drivers/hwmon/pmbus/ltc3815.c b/drivers/hwmon/pmbus/ltc3815.c
new file mode 100644
index 000000000000..bb32e6276622
--- /dev/null
+++ b/drivers/hwmon/pmbus/ltc3815.c
@@ -0,0 +1,215 @@
+/*
+ * Hardware monitoring driver for LTC3815
+ *
+ * Copyright (c) 2015 Linear Technology
+ * Copyright (c) 2015 Guenter Roeck
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "pmbus.h"
+
+#define LTC3815_MFR_IOUT_PEAK 0xd7
+#define LTC3815_MFR_VOUT_PEAK 0xdd
+#define LTC3815_MFR_VIN_PEAK 0xde
+#define LTC3815_MFR_TEMP_PEAK 0xdf
+#define LTC3815_MFR_IIN_PEAK 0xe1
+#define LTC3815_MFR_SPECIAL_ID 0xe7
+
+#define LTC3815_ID 0x8000
+#define LTC3815_ID_MASK 0xff00
+
+static int ltc3815_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VOUT_MODE:
+ /*
+ * The chip returns 0x3e, suggesting VID mode with manufacturer
+ * specific VID codes. Since the output voltage is reported
+ * with a LSB of 0.5mV, override and report direct mode with
+ * appropriate coefficients.
+ */
+ ret = 0x40;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int ltc3815_write_byte(struct i2c_client *client, int page, u8 reg)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_CLEAR_FAULTS:
+ /*
+ * LTC3815 does not support the CLEAR_FAULTS command.
+ * Emulate it by clearing the status register.
+ */
+ ret = pmbus_read_word_data(client, 0, PMBUS_STATUS_WORD);
+ if (ret > 0) {
+ pmbus_write_word_data(client, 0, PMBUS_STATUS_WORD,
+ ret);
+ ret = 0;
+ }
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int ltc3815_read_word_data(struct i2c_client *client, int page, int reg)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_VIN_MAX:
+ ret = pmbus_read_word_data(client, page, LTC3815_MFR_VIN_PEAK);
+ break;
+ case PMBUS_VIRT_READ_VOUT_MAX:
+ ret = pmbus_read_word_data(client, page, LTC3815_MFR_VOUT_PEAK);
+ break;
+ case PMBUS_VIRT_READ_TEMP_MAX:
+ ret = pmbus_read_word_data(client, page, LTC3815_MFR_TEMP_PEAK);
+ break;
+ case PMBUS_VIRT_READ_IOUT_MAX:
+ ret = pmbus_read_word_data(client, page, LTC3815_MFR_IOUT_PEAK);
+ break;
+ case PMBUS_VIRT_READ_IIN_MAX:
+ ret = pmbus_read_word_data(client, page, LTC3815_MFR_IIN_PEAK);
+ break;
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ case PMBUS_VIRT_RESET_VIN_HISTORY:
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ case PMBUS_VIRT_RESET_IIN_HISTORY:
+ ret = 0;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int ltc3815_write_word_data(struct i2c_client *client, int page,
+ int reg, u16 word)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_RESET_IIN_HISTORY:
+ ret = pmbus_write_word_data(client, page,
+ LTC3815_MFR_IIN_PEAK, 0);
+ break;
+ case PMBUS_VIRT_RESET_IOUT_HISTORY:
+ ret = pmbus_write_word_data(client, page,
+ LTC3815_MFR_IOUT_PEAK, 0);
+ break;
+ case PMBUS_VIRT_RESET_VOUT_HISTORY:
+ ret = pmbus_write_word_data(client, page,
+ LTC3815_MFR_VOUT_PEAK, 0);
+ break;
+ case PMBUS_VIRT_RESET_VIN_HISTORY:
+ ret = pmbus_write_word_data(client, page,
+ LTC3815_MFR_VIN_PEAK, 0);
+ break;
+ case PMBUS_VIRT_RESET_TEMP_HISTORY:
+ ret = pmbus_write_word_data(client, page,
+ LTC3815_MFR_TEMP_PEAK, 0);
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static const struct i2c_device_id ltc3815_id[] = {
+ {"ltc3815", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ltc3815_id);
+
+static struct pmbus_driver_info ltc3815_info = {
+ .pages = 1,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_CURRENT_IN] = direct,
+ .format[PSC_CURRENT_OUT] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+ .m[PSC_VOLTAGE_IN] = 250,
+ .b[PSC_VOLTAGE_IN] = 0,
+ .R[PSC_VOLTAGE_IN] = 0,
+ .m[PSC_VOLTAGE_OUT] = 2,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = 3,
+ .m[PSC_CURRENT_IN] = 1,
+ .b[PSC_CURRENT_IN] = 0,
+ .R[PSC_CURRENT_IN] = 2,
+ .m[PSC_CURRENT_OUT] = 1,
+ .b[PSC_CURRENT_OUT] = 0,
+ .R[PSC_CURRENT_OUT] = 2,
+ .m[PSC_TEMPERATURE] = 1,
+ .b[PSC_TEMPERATURE] = 0,
+ .R[PSC_TEMPERATURE] = 0,
+ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_VOUT |
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP,
+ .read_byte_data = ltc3815_read_byte_data,
+ .read_word_data = ltc3815_read_word_data,
+ .write_byte = ltc3815_write_byte,
+ .write_word_data = ltc3815_write_word_data,
+};
+
+static int ltc3815_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int chip_id;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_WORD_DATA))
+ return -ENODEV;
+
+ chip_id = i2c_smbus_read_word_data(client, LTC3815_MFR_SPECIAL_ID);
+ if (chip_id < 0)
+ return chip_id;
+ if ((chip_id & LTC3815_ID_MASK) != LTC3815_ID)
+ return -ENODEV;
+
+ return pmbus_do_probe(client, id, &ltc3815_info);
+}
+
+static struct i2c_driver ltc3815_driver = {
+ .driver = {
+ .name = "ltc3815",
+ },
+ .probe = ltc3815_probe,
+ .remove = pmbus_do_remove,
+ .id_table = ltc3815_id,
+};
+
+module_i2c_driver(ltc3815_driver);
+
+MODULE_AUTHOR("Guenter Roeck");
+MODULE_DESCRIPTION("PMBus driver for LTC3815");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c
index 65482624ea2c..5289aa0980a8 100644
--- a/drivers/hwmon/tmp102.c
+++ b/drivers/hwmon/tmp102.c
@@ -58,6 +58,7 @@ struct tmp102 {
u16 config_orig;
unsigned long last_update;
int temp[3];
+ bool first_time;
};
/* convert left adjusted 13-bit TMP102 register value to milliCelsius */
@@ -93,6 +94,7 @@ static struct tmp102 *tmp102_update_device(struct device *dev)
tmp102->temp[i] = tmp102_reg_to_mC(status);
}
tmp102->last_update = jiffies;
+ tmp102->first_time = false;
}
mutex_unlock(&tmp102->lock);
return tmp102;
@@ -102,6 +104,12 @@ static int tmp102_read_temp(void *dev, int *temp)
{
struct tmp102 *tmp102 = tmp102_update_device(dev);
+ /* Is it too early even to return a conversion? */
+ if (tmp102->first_time) {
+ dev_dbg(dev, "%s: Conversion not ready yet..\n", __func__);
+ return -EAGAIN;
+ }
+
*temp = tmp102->temp[0];
return 0;
@@ -114,6 +122,10 @@ static ssize_t tmp102_show_temp(struct device *dev,
struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
struct tmp102 *tmp102 = tmp102_update_device(dev);
+ /* Is it too early even to return a read? */
+ if (tmp102->first_time)
+ return -EAGAIN;
+
return sprintf(buf, "%d\n", tmp102->temp[sda->index]);
}
@@ -207,7 +219,9 @@ static int tmp102_probe(struct i2c_client *client,
status = -ENODEV;
goto fail_restore_config;
}
- tmp102->last_update = jiffies - HZ;
+ tmp102->last_update = jiffies;
+ /* Mark that we are not ready with data until conversion is complete */
+ tmp102->first_time = true;
mutex_init(&tmp102->lock);
hwmon_dev = hwmon_device_register_with_groups(dev, client->name,
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
index 6c8921140f02..c85935f3525a 100644
--- a/drivers/hwtracing/coresight/Kconfig
+++ b/drivers/hwtracing/coresight/Kconfig
@@ -8,7 +8,7 @@ menuconfig CORESIGHT
This framework provides a kernel interface for the CoreSight debug
and trace drivers to register themselves with. It's intended to build
a topological view of the CoreSight components based on a DT
- specification and configure the right serie of components when a
+ specification and configure the right series of components when a
trace source gets enabled.
if CORESIGHT
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index e25492137d8b..93738dfbf631 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -548,7 +548,7 @@ static int coresight_name_match(struct device *dev, void *data)
to_match = data;
i_csdev = to_coresight_device(dev);
- if (!strcmp(to_match, dev_name(&i_csdev->dev)))
+ if (to_match && !strcmp(to_match, dev_name(&i_csdev->dev)))
return 1;
return 0;
diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c
index 899bede81b31..9d233bbde5e1 100644
--- a/drivers/i2c/algos/i2c-algo-bit.c
+++ b/drivers/i2c/algos/i2c-algo-bit.c
@@ -617,6 +617,10 @@ const struct i2c_algorithm i2c_bit_algo = {
};
EXPORT_SYMBOL(i2c_bit_algo);
+const struct i2c_adapter_quirks i2c_bit_quirk_no_clk_stretch = {
+ .flags = I2C_AQ_NO_CLK_STRETCH,
+};
+
/*
* registering functions to load algorithms at runtime
*/
@@ -635,6 +639,8 @@ static int __i2c_bit_add_bus(struct i2c_adapter *adap,
/* register new adapter to i2c module... */
adap->algo = &i2c_bit_algo;
adap->retries = 3;
+ if (bit_adap->getscl == NULL)
+ adap->quirks = &i2c_bit_quirk_no_clk_stretch;
ret = add_adapter(adap);
if (ret < 0)
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 7b0aa82ea38b..0299dfa746a3 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -516,7 +516,7 @@ config I2C_EFM32
config I2C_EG20T
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C"
- depends on PCI && (X86_32 || COMPILE_TEST)
+ depends on PCI && (X86_32 || MIPS || COMPILE_TEST)
help
This driver is for PCH(Platform controller Hub) I2C of EG20T which
is an IOH(Input/Output Hub) for x86 embedded processor.
@@ -532,6 +532,7 @@ config I2C_EG20T
config I2C_EMEV2
tristate "EMMA Mobile series I2C adapter"
depends on HAVE_CLK
+ select I2C_SLAVE
help
If you say yes to this option, support will be included for the
I2C interface on the Renesas Electronics EM/EV family of processors.
@@ -963,11 +964,11 @@ config I2C_XILINX
will be called xilinx_i2c.
config I2C_XLR
- tristate "XLR I2C support"
- depends on CPU_XLR
+ tristate "Netlogic XLR and Sigma Designs I2C support"
+ depends on CPU_XLR || ARCH_TANGOX
help
This driver enables support for the on-chip I2C interface of
- the Netlogic XLR/XLS MIPS processors.
+ the Netlogic XLR/XLS MIPS processors and Sigma Designs SOCs.
This driver can also be built as a module. If so, the module
will be called i2c-xlr.
diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c
index 10835d1f559b..921d32bfcda8 100644
--- a/drivers/i2c/busses/i2c-at91.c
+++ b/drivers/i2c/busses/i2c-at91.c
@@ -64,6 +64,8 @@
#define AT91_TWI_IADR 0x000c /* Internal Address Register */
#define AT91_TWI_CWGR 0x0010 /* Clock Waveform Generator Reg */
+#define AT91_TWI_CWGR_HOLD_MAX 0x1f
+#define AT91_TWI_CWGR_HOLD(x) (((x) & AT91_TWI_CWGR_HOLD_MAX) << 24)
#define AT91_TWI_SR 0x0020 /* Status Register */
#define AT91_TWI_TXCOMP BIT(0) /* Transmission Complete */
@@ -110,6 +112,7 @@ struct at91_twi_pdata {
unsigned clk_offset;
bool has_unre_flag;
bool has_alt_cmd;
+ bool has_hold_field;
struct at_dma_slave dma_slave;
};
@@ -187,10 +190,11 @@ static void at91_init_twi_bus(struct at91_twi_dev *dev)
*/
static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
{
- int ckdiv, cdiv, div;
+ int ckdiv, cdiv, div, hold = 0;
struct at91_twi_pdata *pdata = dev->pdata;
int offset = pdata->clk_offset;
int max_ckdiv = pdata->clk_max_div;
+ u32 twd_hold_time_ns = 0;
div = max(0, (int)DIV_ROUND_UP(clk_get_rate(dev->clk),
2 * twi_clk) - offset);
@@ -204,8 +208,33 @@ static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
cdiv = 255;
}
- dev->twi_cwgr_reg = (ckdiv << 16) | (cdiv << 8) | cdiv;
- dev_dbg(dev->dev, "cdiv %d ckdiv %d\n", cdiv, ckdiv);
+ if (pdata->has_hold_field) {
+ of_property_read_u32(dev->dev->of_node, "i2c-sda-hold-time-ns",
+ &twd_hold_time_ns);
+
+ /*
+ * hold time = HOLD + 3 x T_peripheral_clock
+ * Use clk rate in kHz to prevent overflows when computing
+ * hold.
+ */
+ hold = DIV_ROUND_UP(twd_hold_time_ns
+ * (clk_get_rate(dev->clk) / 1000), 1000000);
+ hold -= 3;
+ if (hold < 0)
+ hold = 0;
+ if (hold > AT91_TWI_CWGR_HOLD_MAX) {
+ dev_warn(dev->dev,
+ "HOLD field set to its maximum value (%d instead of %d)\n",
+ AT91_TWI_CWGR_HOLD_MAX, hold);
+ hold = AT91_TWI_CWGR_HOLD_MAX;
+ }
+ }
+
+ dev->twi_cwgr_reg = (ckdiv << 16) | (cdiv << 8) | cdiv
+ | AT91_TWI_CWGR_HOLD(hold);
+
+ dev_dbg(dev->dev, "cdiv %d ckdiv %d hold %d (%d ns)\n",
+ cdiv, ckdiv, hold, twd_hold_time_ns);
}
static void at91_twi_dma_cleanup(struct at91_twi_dev *dev)
@@ -797,6 +826,7 @@ static struct at91_twi_pdata at91rm9200_config = {
.clk_offset = 3,
.has_unre_flag = true,
.has_alt_cmd = false,
+ .has_hold_field = false,
};
static struct at91_twi_pdata at91sam9261_config = {
@@ -804,6 +834,7 @@ static struct at91_twi_pdata at91sam9261_config = {
.clk_offset = 4,
.has_unre_flag = false,
.has_alt_cmd = false,
+ .has_hold_field = false,
};
static struct at91_twi_pdata at91sam9260_config = {
@@ -811,6 +842,7 @@ static struct at91_twi_pdata at91sam9260_config = {
.clk_offset = 4,
.has_unre_flag = false,
.has_alt_cmd = false,
+ .has_hold_field = false,
};
static struct at91_twi_pdata at91sam9g20_config = {
@@ -818,6 +850,7 @@ static struct at91_twi_pdata at91sam9g20_config = {
.clk_offset = 4,
.has_unre_flag = false,
.has_alt_cmd = false,
+ .has_hold_field = false,
};
static struct at91_twi_pdata at91sam9g10_config = {
@@ -825,6 +858,7 @@ static struct at91_twi_pdata at91sam9g10_config = {
.clk_offset = 4,
.has_unre_flag = false,
.has_alt_cmd = false,
+ .has_hold_field = false,
};
static const struct platform_device_id at91_twi_devtypes[] = {
@@ -854,6 +888,15 @@ static struct at91_twi_pdata at91sam9x5_config = {
.clk_offset = 4,
.has_unre_flag = false,
.has_alt_cmd = false,
+ .has_hold_field = false,
+};
+
+static struct at91_twi_pdata sama5d4_config = {
+ .clk_max_div = 7,
+ .clk_offset = 4,
+ .has_unre_flag = false,
+ .has_alt_cmd = false,
+ .has_hold_field = true,
};
static struct at91_twi_pdata sama5d2_config = {
@@ -861,6 +904,7 @@ static struct at91_twi_pdata sama5d2_config = {
.clk_offset = 4,
.has_unre_flag = true,
.has_alt_cmd = true,
+ .has_hold_field = true,
};
static const struct of_device_id atmel_twi_dt_ids[] = {
@@ -883,6 +927,9 @@ static const struct of_device_id atmel_twi_dt_ids[] = {
.compatible = "atmel,at91sam9x5-i2c",
.data = &at91sam9x5_config,
}, {
+ .compatible = "atmel,sama5d4-i2c",
+ .data = &sama5d4_config,
+ }, {
.compatible = "atmel,sama5d2-i2c",
.data = &sama5d2_config,
}, {
diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c
index 3032b89ac60b..818b051d25e6 100644
--- a/drivers/i2c/busses/i2c-bcm2835.c
+++ b/drivers/i2c/busses/i2c-bcm2835.c
@@ -222,6 +222,15 @@ static const struct i2c_algorithm bcm2835_i2c_algo = {
.functionality = bcm2835_i2c_func,
};
+/*
+ * This HW was reported to have problems with clock stretching:
+ * http://www.advamation.com/knowhow/raspberrypi/rpi-i2c-bug.html
+ * https://www.raspberrypi.org/forums/viewtopic.php?p=146272
+ */
+static const struct i2c_adapter_quirks bcm2835_i2c_quirks = {
+ .flags = I2C_AQ_NO_CLK_STRETCH,
+};
+
static int bcm2835_i2c_probe(struct platform_device *pdev)
{
struct bcm2835_i2c_dev *i2c_dev;
@@ -293,6 +302,7 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
adap->algo = &bcm2835_i2c_algo;
adap->dev.parent = &pdev->dev;
adap->dev.of_node = pdev->dev.of_node;
+ adap->quirks = &bcm2835_i2c_quirks;
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, 0);
diff --git a/drivers/i2c/busses/i2c-brcmstb.c b/drivers/i2c/busses/i2c-brcmstb.c
index 8e9637eea512..3711df1d4526 100644
--- a/drivers/i2c/busses/i2c-brcmstb.c
+++ b/drivers/i2c/busses/i2c-brcmstb.c
@@ -25,13 +25,16 @@
#include <linux/version.h>
#define N_DATA_REGS 8
-#define N_DATA_BYTES (N_DATA_REGS * 4)
-/* BSC count register field definitions */
-#define BSC_CNT_REG1_MASK 0x0000003f
-#define BSC_CNT_REG1_SHIFT 0
-#define BSC_CNT_REG2_MASK 0x00000fc0
-#define BSC_CNT_REG2_SHIFT 6
+/*
+ * PER_I2C/BSC count register mask depends on 1 byte/4 byte data register
+ * size. Cable modem and DSL SoCs with Peripheral i2c cores use 1 byte per
+ * data register whereas STB SoCs use 4 byte per data register transfer,
+ * account for this difference in total count per transaction and mask to
+ * use.
+ */
+#define BSC_CNT_REG1_MASK(nb) (nb == 1 ? GENMASK(3, 0) : GENMASK(5, 0))
+#define BSC_CNT_REG1_SHIFT 0
/* BSC CTL register field definitions */
#define BSC_CTL_REG_DTF_MASK 0x00000003
@@ -41,7 +44,7 @@
#define BSC_CTL_REG_INT_EN_SHIFT 6
#define BSC_CTL_REG_DIV_CLK_MASK 0x00000080
-/* BSC_IIC_ENABLE r/w enable and interrupt field defintions */
+/* BSC_IIC_ENABLE r/w enable and interrupt field definitions */
#define BSC_IIC_EN_RESTART_MASK 0x00000040
#define BSC_IIC_EN_NOSTART_MASK 0x00000020
#define BSC_IIC_EN_NOSTOP_MASK 0x00000010
@@ -169,6 +172,7 @@ struct brcmstb_i2c_dev {
struct completion done;
bool is_suspended;
u32 clk_freq_hz;
+ int data_regsz;
};
/* register accessors for both be and le cpu arch */
@@ -186,6 +190,16 @@ struct brcmstb_i2c_dev {
#define bsc_writel(_dev, _val, _reg) \
__bsc_writel(_val, _dev->base + offsetof(struct bsc_regs, _reg))
+static inline int brcmstb_i2c_get_xfersz(struct brcmstb_i2c_dev *dev)
+{
+ return (N_DATA_REGS * dev->data_regsz);
+}
+
+static inline int brcmstb_i2c_get_data_regsz(struct brcmstb_i2c_dev *dev)
+{
+ return dev->data_regsz;
+}
+
static void brcmstb_i2c_enable_disable_irq(struct brcmstb_i2c_dev *dev,
bool int_en)
{
@@ -323,14 +337,16 @@ static int brcmstb_i2c_xfer_bsc_data(struct brcmstb_i2c_dev *dev,
u8 *buf, unsigned int len,
struct i2c_msg *pmsg)
{
- int cnt, byte, rc;
+ int cnt, byte, i, rc;
enum bsc_xfer_cmd cmd;
u32 ctl_reg;
struct bsc_regs *pi2creg = dev->bsc_regmap;
int no_ack = pmsg->flags & I2C_M_IGNORE_NAK;
+ int data_regsz = brcmstb_i2c_get_data_regsz(dev);
+ int xfersz = brcmstb_i2c_get_xfersz(dev);
/* see if the transaction needs to check NACK conditions */
- if (no_ack || len <= N_DATA_BYTES) {
+ if (no_ack || len <= xfersz) {
cmd = (pmsg->flags & I2C_M_RD) ? CMD_RD_NOACK
: CMD_WR_NOACK;
pi2creg->ctlhi_reg |= BSC_CTLHI_REG_IGNORE_ACK_MASK;
@@ -348,20 +364,22 @@ static int brcmstb_i2c_xfer_bsc_data(struct brcmstb_i2c_dev *dev,
pi2creg->ctl_reg = ctl_reg | DTF_RD_MASK;
/* set the read/write length */
- bsc_writel(dev, BSC_CNT_REG1_MASK & (len << BSC_CNT_REG1_SHIFT),
- cnt_reg);
+ bsc_writel(dev, BSC_CNT_REG1_MASK(data_regsz) &
+ (len << BSC_CNT_REG1_SHIFT), cnt_reg);
/* Write data into data_in register */
+
if (cmd == CMD_WR || cmd == CMD_WR_NOACK) {
- for (cnt = 0; cnt < len; cnt += 4) {
+ for (cnt = 0, i = 0; cnt < len; cnt += data_regsz, i++) {
u32 word = 0;
- for (byte = 0; byte < 4; byte++) {
- word >>= 8;
+ for (byte = 0; byte < data_regsz; byte++) {
+ word >>= BITS_PER_BYTE;
if ((cnt + byte) < len)
- word |= buf[cnt + byte] << 24;
+ word |= buf[cnt + byte] <<
+ (BITS_PER_BYTE * (data_regsz - 1));
}
- bsc_writel(dev, word, data_in[cnt >> 2]);
+ bsc_writel(dev, word, data_in[i]);
}
}
@@ -373,14 +391,15 @@ static int brcmstb_i2c_xfer_bsc_data(struct brcmstb_i2c_dev *dev,
return rc;
}
+ /* Read data from data_out register */
if (cmd == CMD_RD || cmd == CMD_RD_NOACK) {
- for (cnt = 0; cnt < len; cnt += 4) {
- u32 data = bsc_readl(dev, data_out[cnt >> 2]);
+ for (cnt = 0, i = 0; cnt < len; cnt += data_regsz, i++) {
+ u32 data = bsc_readl(dev, data_out[i]);
- for (byte = 0; byte < 4 &&
+ for (byte = 0; byte < data_regsz &&
(byte + cnt) < len; byte++) {
buf[cnt + byte] = data & 0xff;
- data >>= 8;
+ data >>= BITS_PER_BYTE;
}
}
}
@@ -448,6 +467,7 @@ static int brcmstb_i2c_xfer(struct i2c_adapter *adapter,
int bytes_to_xfer;
u8 *tmp_buf;
int len = 0;
+ int xfersz = brcmstb_i2c_get_xfersz(dev);
if (dev->is_suspended)
return -EBUSY;
@@ -482,9 +502,9 @@ static int brcmstb_i2c_xfer(struct i2c_adapter *adapter,
/* Perform data transfer */
while (len) {
- bytes_to_xfer = min(len, N_DATA_BYTES);
+ bytes_to_xfer = min(len, xfersz);
- if (len <= N_DATA_BYTES && i == (num - 1))
+ if (len <= xfersz && i == (num - 1))
brcmstb_set_i2c_start_stop(dev,
~(COND_START_STOP));
@@ -542,8 +562,12 @@ static void brcmstb_i2c_set_bus_speed(struct brcmstb_i2c_dev *dev)
static void brcmstb_i2c_set_bsc_reg_defaults(struct brcmstb_i2c_dev *dev)
{
- /* 4 byte data register */
- dev->bsc_regmap->ctlhi_reg = BSC_CTLHI_REG_DATAREG_SIZE_MASK;
+ if (brcmstb_i2c_get_data_regsz(dev) == sizeof(u32))
+ /* set 4 byte data in/out xfers */
+ dev->bsc_regmap->ctlhi_reg = BSC_CTLHI_REG_DATAREG_SIZE_MASK;
+ else
+ dev->bsc_regmap->ctlhi_reg &= ~BSC_CTLHI_REG_DATAREG_SIZE_MASK;
+
bsc_writel(dev, dev->bsc_regmap->ctlhi_reg, ctlhi_reg);
/* set bus speed */
brcmstb_i2c_set_bus_speed(dev);
@@ -608,6 +632,13 @@ static int brcmstb_i2c_probe(struct platform_device *pdev)
dev->clk_freq_hz = bsc_clk[0].hz;
}
+ /* set the data in/out register size for compatible SoCs */
+ if (of_device_is_compatible(dev->device->of_node,
+ "brcmstb,brcmper-i2c"))
+ dev->data_regsz = sizeof(u8);
+ else
+ dev->data_regsz = sizeof(u32);
+
brcmstb_i2c_set_bsc_reg_defaults(dev);
/* Add the i2c adapter */
@@ -674,6 +705,7 @@ static SIMPLE_DEV_PM_OPS(brcmstb_i2c_pm, brcmstb_i2c_suspend,
static const struct of_device_id brcmstb_i2c_of_match[] = {
{.compatible = "brcm,brcmstb-i2c"},
+ {.compatible = "brcm,brcmper-i2c"},
{},
};
MODULE_DEVICE_TABLE(of, brcmstb_i2c_of_match);
diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c
index 84deed6571bd..6b08d1607b7a 100644
--- a/drivers/i2c/busses/i2c-cadence.c
+++ b/drivers/i2c/busses/i2c-cadence.c
@@ -18,6 +18,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
+#include <linux/pm_runtime.h>
/* Register offsets for the I2C device. */
#define CDNS_I2C_CR_OFFSET 0x00 /* Control Register, RW */
@@ -96,6 +97,8 @@
CDNS_I2C_IXR_COMP)
#define CDNS_I2C_TIMEOUT msecs_to_jiffies(1000)
+/* timeout for pm runtime autosuspend */
+#define CNDS_I2C_PM_TIMEOUT 1000 /* ms */
#define CDNS_I2C_FIFO_DEPTH 16
/* FIFO depth at which the DATA interrupt occurs */
@@ -128,7 +131,6 @@
* @xfer_done: Transfer complete status
* @p_send_buf: Pointer to transmit buffer
* @p_recv_buf: Pointer to receive buffer
- * @suspended: Flag holding the device's PM status
* @send_count: Number of bytes still expected to send
* @recv_count: Number of bytes still expected to receive
* @curr_recv_count: Number of bytes to be received in current transfer
@@ -141,6 +143,7 @@
* @quirks: flag for broken hold bit usage in r1p10
*/
struct cdns_i2c {
+ struct device *dev;
void __iomem *membase;
struct i2c_adapter adap;
struct i2c_msg *p_msg;
@@ -148,7 +151,6 @@ struct cdns_i2c {
struct completion xfer_done;
unsigned char *p_send_buf;
unsigned char *p_recv_buf;
- u8 suspended;
unsigned int send_count;
unsigned int recv_count;
unsigned int curr_recv_count;
@@ -569,9 +571,14 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
struct cdns_i2c *id = adap->algo_data;
bool hold_quirk;
+ ret = pm_runtime_get_sync(id->dev);
+ if (ret < 0)
+ return ret;
/* Check if the bus is free */
- if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA)
- return -EAGAIN;
+ if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA) {
+ ret = -EAGAIN;
+ goto out;
+ }
hold_quirk = !!(id->quirks & CDNS_I2C_BROKEN_HOLD_BIT);
/*
@@ -590,7 +597,8 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
if (msgs[count].flags & I2C_M_RD) {
dev_warn(adap->dev.parent,
"Can't do repeated start after a receive message\n");
- return -EOPNOTSUPP;
+ ret = -EOPNOTSUPP;
+ goto out;
}
}
id->bus_hold_flag = 1;
@@ -608,20 +616,26 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
ret = cdns_i2c_process_msg(id, msgs, adap);
if (ret)
- return ret;
+ goto out;
/* Report the other error interrupts to application */
if (id->err_status) {
cdns_i2c_master_reset(adap);
- if (id->err_status & CDNS_I2C_IXR_NACK)
- return -ENXIO;
-
- return -EIO;
+ if (id->err_status & CDNS_I2C_IXR_NACK) {
+ ret = -ENXIO;
+ goto out;
+ }
+ ret = -EIO;
+ goto out;
}
}
- return num;
+ ret = num;
+out:
+ pm_runtime_mark_last_busy(id->dev);
+ pm_runtime_put_autosuspend(id->dev);
+ return ret;
}
/**
@@ -760,7 +774,7 @@ static int cdns_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
struct clk_notifier_data *ndata = data;
struct cdns_i2c *id = to_cdns_i2c(nb);
- if (id->suspended)
+ if (pm_runtime_suspended(id->dev))
return NOTIFY_OK;
switch (event) {
@@ -808,14 +822,12 @@ static int cdns_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
*
* Return: 0 always
*/
-static int __maybe_unused cdns_i2c_suspend(struct device *_dev)
+static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev)
{
- struct platform_device *pdev = container_of(_dev,
- struct platform_device, dev);
+ struct platform_device *pdev = to_platform_device(dev);
struct cdns_i2c *xi2c = platform_get_drvdata(pdev);
clk_disable(xi2c->clk);
- xi2c->suspended = 1;
return 0;
}
@@ -828,26 +840,25 @@ static int __maybe_unused cdns_i2c_suspend(struct device *_dev)
*
* Return: 0 on success and error value on error
*/
-static int __maybe_unused cdns_i2c_resume(struct device *_dev)
+static int __maybe_unused cdns_i2c_runtime_resume(struct device *dev)
{
- struct platform_device *pdev = container_of(_dev,
- struct platform_device, dev);
+ struct platform_device *pdev = to_platform_device(dev);
struct cdns_i2c *xi2c = platform_get_drvdata(pdev);
int ret;
ret = clk_enable(xi2c->clk);
if (ret) {
- dev_err(_dev, "Cannot enable clock.\n");
+ dev_err(dev, "Cannot enable clock.\n");
return ret;
}
- xi2c->suspended = 0;
-
return 0;
}
-static SIMPLE_DEV_PM_OPS(cdns_i2c_dev_pm_ops, cdns_i2c_suspend,
- cdns_i2c_resume);
+static const struct dev_pm_ops cdns_i2c_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(cdns_i2c_runtime_suspend,
+ cdns_i2c_runtime_resume, NULL)
+};
static const struct cdns_platform_data r1p10_i2c_def = {
.quirks = CDNS_I2C_BROKEN_HOLD_BIT,
@@ -881,6 +892,7 @@ static int cdns_i2c_probe(struct platform_device *pdev)
if (!id)
return -ENOMEM;
+ id->dev = &pdev->dev;
platform_set_drvdata(pdev, id);
match = of_match_node(cdns_i2c_of_match, pdev->dev.of_node);
@@ -913,10 +925,14 @@ static int cdns_i2c_probe(struct platform_device *pdev)
return PTR_ERR(id->clk);
}
ret = clk_prepare_enable(id->clk);
- if (ret) {
+ if (ret)
dev_err(&pdev->dev, "Unable to enable clock.\n");
- return ret;
- }
+
+ pm_runtime_enable(id->dev);
+ pm_runtime_set_autosuspend_delay(id->dev, CNDS_I2C_PM_TIMEOUT);
+ pm_runtime_use_autosuspend(id->dev);
+ pm_runtime_set_active(id->dev);
+
id->clk_rate_change_nb.notifier_call = cdns_i2c_clk_notifier_cb;
if (clk_notifier_register(id->clk, &id->clk_rate_change_nb))
dev_warn(&pdev->dev, "Unable to register clock notifier.\n");
@@ -966,6 +982,8 @@ static int cdns_i2c_probe(struct platform_device *pdev)
err_clk_dis:
clk_disable_unprepare(id->clk);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
return ret;
}
@@ -984,6 +1002,7 @@ static int cdns_i2c_remove(struct platform_device *pdev)
i2c_del_adapter(&id->adap);
clk_notifier_unregister(id->clk, &id->clk_rate_change_nb);
clk_disable_unprepare(id->clk);
+ pm_runtime_disable(&pdev->dev);
return 0;
}
diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c
index c5628a42170a..a8bdcb5292f5 100644
--- a/drivers/i2c/busses/i2c-davinci.c
+++ b/drivers/i2c/busses/i2c-davinci.c
@@ -202,8 +202,15 @@ static void i2c_davinci_calc_clk_dividers(struct davinci_i2c_dev *dev)
* d is always 6 on Keystone I2C controller
*/
- /* get minimum of 7 MHz clock, but max of 12 MHz */
- psc = (input_clock / 7000000) - 1;
+ /*
+ * Both Davinci and current Keystone User Guides recommend a value
+ * between 7MHz and 12MHz. In reality 7MHz module clock doesn't
+ * always produce enough margin between SDA and SCL transitions.
+ * Measurements show that the higher the module clock is, the
+ * bigger is the margin, providing more reliable communication.
+ * So we better target for 12MHz.
+ */
+ psc = (input_clock / 12000000) - 1;
if ((input_clock / (psc + 1)) > 12000000)
psc++; /* better to run under spec than over */
d = (psc >= 2) ? 5 : 7 - psc;
diff --git a/drivers/i2c/busses/i2c-designware-baytrail.c b/drivers/i2c/busses/i2c-designware-baytrail.c
index 7d7ae97476e2..e38c2bbba940 100644
--- a/drivers/i2c/busses/i2c-designware-baytrail.c
+++ b/drivers/i2c/busses/i2c-designware-baytrail.c
@@ -34,8 +34,7 @@ static int get_sem(struct device *dev, u32 *sem)
u32 data;
int ret;
- ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, PUNIT_SEMAPHORE,
- &data);
+ ret = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ, PUNIT_SEMAPHORE, &data);
if (ret) {
dev_err(dev, "iosf failed to read punit semaphore\n");
return ret;
@@ -50,21 +49,19 @@ static void reset_semaphore(struct device *dev)
{
u32 data;
- if (iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
- PUNIT_SEMAPHORE, &data)) {
+ if (iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ, PUNIT_SEMAPHORE, &data)) {
dev_err(dev, "iosf failed to reset punit semaphore during read\n");
return;
}
data &= ~PUNIT_SEMAPHORE_BIT;
- if (iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
- PUNIT_SEMAPHORE, data))
+ if (iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE, PUNIT_SEMAPHORE, data))
dev_err(dev, "iosf failed to reset punit semaphore during write\n");
}
static int baytrail_i2c_acquire(struct dw_i2c_dev *dev)
{
- u32 sem;
+ u32 sem = PUNIT_SEMAPHORE_ACQUIRE;
int ret;
unsigned long start, end;
@@ -77,8 +74,7 @@ static int baytrail_i2c_acquire(struct dw_i2c_dev *dev)
return 0;
/* host driver writes to side band semaphore register */
- ret = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
- PUNIT_SEMAPHORE, PUNIT_SEMAPHORE_ACQUIRE);
+ ret = iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE, PUNIT_SEMAPHORE, sem);
if (ret) {
dev_err(dev->dev, "iosf punit semaphore request failed\n");
return ret;
@@ -102,8 +98,7 @@ static int baytrail_i2c_acquire(struct dw_i2c_dev *dev)
dev_err(dev->dev, "punit semaphore timed out, resetting\n");
reset_semaphore(dev->dev);
- ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
- PUNIT_SEMAPHORE, &sem);
+ ret = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ, PUNIT_SEMAPHORE, &sem);
if (ret)
dev_err(dev->dev, "iosf failed to read punit semaphore\n");
else
diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c
index 8c48b27ba059..ba9732c236c5 100644
--- a/drivers/i2c/busses/i2c-designware-core.c
+++ b/drivers/i2c/busses/i2c-designware-core.c
@@ -271,6 +271,17 @@ static void __i2c_dw_enable(struct dw_i2c_dev *dev, bool enable)
enable ? "en" : "dis");
}
+static unsigned long i2c_dw_clk_rate(struct dw_i2c_dev *dev)
+{
+ /*
+ * Clock is not necessary if we got LCNT/HCNT values directly from
+ * the platform code.
+ */
+ if (WARN_ON_ONCE(!dev->get_clk_rate_khz))
+ return 0;
+ return dev->get_clk_rate_khz(dev);
+}
+
/**
* i2c_dw_init() - initialize the designware i2c master hardware
* @dev: device private data
@@ -281,7 +292,6 @@ static void __i2c_dw_enable(struct dw_i2c_dev *dev, bool enable)
*/
int i2c_dw_init(struct dw_i2c_dev *dev)
{
- u32 input_clock_khz;
u32 hcnt, lcnt;
u32 reg;
u32 sda_falling_time, scl_falling_time;
@@ -295,8 +305,6 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
}
}
- input_clock_khz = dev->get_clk_rate_khz(dev);
-
reg = dw_readl(dev, DW_IC_COMP_TYPE);
if (reg == ___constant_swab32(DW_IC_COMP_TYPE_VALUE)) {
/* Configure register endianess access */
@@ -325,12 +333,12 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
hcnt = dev->ss_hcnt;
lcnt = dev->ss_lcnt;
} else {
- hcnt = i2c_dw_scl_hcnt(input_clock_khz,
+ hcnt = i2c_dw_scl_hcnt(i2c_dw_clk_rate(dev),
4000, /* tHD;STA = tHIGH = 4.0 us */
sda_falling_time,
0, /* 0: DW default, 1: Ideal */
0); /* No offset */
- lcnt = i2c_dw_scl_lcnt(input_clock_khz,
+ lcnt = i2c_dw_scl_lcnt(i2c_dw_clk_rate(dev),
4700, /* tLOW = 4.7 us */
scl_falling_time,
0); /* No offset */
@@ -344,12 +352,12 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
hcnt = dev->fs_hcnt;
lcnt = dev->fs_lcnt;
} else {
- hcnt = i2c_dw_scl_hcnt(input_clock_khz,
+ hcnt = i2c_dw_scl_hcnt(i2c_dw_clk_rate(dev),
600, /* tHD;STA = tHIGH = 0.6 us */
sda_falling_time,
0, /* 0: DW default, 1: Ideal */
0); /* No offset */
- lcnt = i2c_dw_scl_lcnt(input_clock_khz,
+ lcnt = i2c_dw_scl_lcnt(i2c_dw_clk_rate(dev),
1300, /* tLOW = 1.3 us */
scl_falling_time,
0); /* No offset */
@@ -813,6 +821,12 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
tx_aborted:
if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || dev->msg_err)
complete(&dev->cmd_complete);
+ else if (unlikely(dev->accessor_flags & ACCESS_INTR_MASK)) {
+ /* workaround to trigger pending interrupt */
+ stat = dw_readl(dev, DW_IC_INTR_MASK);
+ i2c_dw_disable_int(dev);
+ dw_writel(dev, stat, DW_IC_INTR_MASK);
+ }
return IRQ_HANDLED;
}
@@ -854,6 +868,7 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)
snprintf(adap->name, sizeof(adap->name),
"Synopsys DesignWare I2C adapter");
+ adap->retries = 3;
adap->algo = &i2c_dw_algo;
adap->dev.parent = dev->dev;
i2c_set_adapdata(adap, dev);
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index 1d50898e7b24..9ffb63a60f95 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -111,6 +111,7 @@ struct dw_i2c_dev {
#define ACCESS_SWAP 0x00000001
#define ACCESS_16BIT 0x00000002
+#define ACCESS_INTR_MASK 0x00000004
extern int i2c_dw_init(struct dw_i2c_dev *dev);
extern void i2c_dw_disable(struct dw_i2c_dev *dev);
diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c
index 1543d35d228d..7368be000c96 100644
--- a/drivers/i2c/busses/i2c-designware-pcidrv.c
+++ b/drivers/i2c/busses/i2c-designware-pcidrv.c
@@ -162,7 +162,7 @@ static struct dw_pci_controller dw_pci_controllers[] = {
#ifdef CONFIG_PM
static int i2c_dw_pci_suspend(struct device *dev)
{
- struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
i2c_dw_disable(pci_get_drvdata(pdev));
return 0;
@@ -170,7 +170,7 @@ static int i2c_dw_pci_suspend(struct device *dev)
static int i2c_dw_pci_resume(struct device *dev)
{
- struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
return i2c_dw_init(pci_get_drvdata(pdev));
}
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 809579ecb5a4..438f1b4964c0 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -36,6 +36,7 @@
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
+#include <linux/property.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/acpi.h>
@@ -93,6 +94,7 @@ static void dw_i2c_acpi_params(struct platform_device *pdev, char method[],
static int dw_i2c_acpi_configure(struct platform_device *pdev)
{
struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
+ const struct acpi_device_id *id;
dev->adapter.nr = -1;
dev->tx_fifo_depth = 32;
@@ -106,6 +108,10 @@ static int dw_i2c_acpi_configure(struct platform_device *pdev)
dw_i2c_acpi_params(pdev, "FMCN", &dev->fs_hcnt, &dev->fs_lcnt,
&dev->sda_hold_time);
+ id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
+ if (id && id->driver_data)
+ dev->accessor_flags |= (u32)id->driver_data;
+
return 0;
}
@@ -116,7 +122,9 @@ static const struct acpi_device_id dw_i2c_acpi_match[] = {
{ "INT3433", 0 },
{ "80860F41", 0 },
{ "808622C1", 0 },
- { "AMD0010", 0 },
+ { "AMD0010", ACCESS_INTR_MASK },
+ { "AMDI0510", 0 },
+ { "APMC0D0F", 0 },
{ }
};
MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match);
@@ -127,12 +135,24 @@ static inline int dw_i2c_acpi_configure(struct platform_device *pdev)
}
#endif
+static int i2c_dw_plat_prepare_clk(struct dw_i2c_dev *i_dev, bool prepare)
+{
+ if (IS_ERR(i_dev->clk))
+ return PTR_ERR(i_dev->clk);
+
+ if (prepare)
+ return clk_prepare_enable(i_dev->clk);
+
+ clk_disable_unprepare(i_dev->clk);
+ return 0;
+}
+
static int dw_i2c_plat_probe(struct platform_device *pdev)
{
+ struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct dw_i2c_dev *dev;
struct i2c_adapter *adap;
struct resource *mem;
- struct dw_i2c_platform_data *pdata;
int irq, r;
u32 clk_freq, ht = 0;
@@ -156,33 +176,28 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
/* fast mode by default because of legacy reasons */
clk_freq = 400000;
- if (has_acpi_companion(&pdev->dev)) {
- dw_i2c_acpi_configure(pdev);
- } else if (pdev->dev.of_node) {
- of_property_read_u32(pdev->dev.of_node,
- "i2c-sda-hold-time-ns", &ht);
-
- of_property_read_u32(pdev->dev.of_node,
- "i2c-sda-falling-time-ns",
- &dev->sda_falling_time);
- of_property_read_u32(pdev->dev.of_node,
- "i2c-scl-falling-time-ns",
- &dev->scl_falling_time);
-
- of_property_read_u32(pdev->dev.of_node, "clock-frequency",
- &clk_freq);
-
- /* Only standard mode at 100kHz and fast mode at 400kHz
- * are supported.
- */
- if (clk_freq != 100000 && clk_freq != 400000) {
- dev_err(&pdev->dev, "Only 100kHz and 400kHz supported");
- return -EINVAL;
- }
+ if (pdata) {
+ clk_freq = pdata->i2c_scl_freq;
} else {
- pdata = dev_get_platdata(&pdev->dev);
- if (pdata)
- clk_freq = pdata->i2c_scl_freq;
+ device_property_read_u32(&pdev->dev, "i2c-sda-hold-time-ns",
+ &ht);
+ device_property_read_u32(&pdev->dev, "i2c-sda-falling-time-ns",
+ &dev->sda_falling_time);
+ device_property_read_u32(&pdev->dev, "i2c-scl-falling-time-ns",
+ &dev->scl_falling_time);
+ device_property_read_u32(&pdev->dev, "clock-frequency",
+ &clk_freq);
+ }
+
+ if (has_acpi_companion(&pdev->dev))
+ dw_i2c_acpi_configure(pdev);
+
+ /*
+ * Only standard mode at 100kHz and fast mode at 400kHz are supported.
+ */
+ if (clk_freq != 100000 && clk_freq != 400000) {
+ dev_err(&pdev->dev, "Only 100kHz and 400kHz supported");
+ return -EINVAL;
}
r = i2c_dw_eval_lock_support(dev);
@@ -204,16 +219,13 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST;
dev->clk = devm_clk_get(&pdev->dev, NULL);
- dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
- if (IS_ERR(dev->clk))
- return PTR_ERR(dev->clk);
- clk_prepare_enable(dev->clk);
-
- if (!dev->sda_hold_time && ht) {
- u32 ic_clk = dev->get_clk_rate_khz(dev);
+ if (!i2c_dw_plat_prepare_clk(dev, true)) {
+ dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
- dev->sda_hold_time = div_u64((u64)ic_clk * ht + 500000,
- 1000000);
+ if (!dev->sda_hold_time && ht)
+ dev->sda_hold_time = div_u64(
+ (u64)dev->get_clk_rate_khz(dev) * ht + 500000,
+ 1000000);
}
if (!dev->tx_fifo_depth) {
@@ -240,12 +252,10 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
}
r = i2c_dw_probe(dev);
- if (r) {
+ if (r && !dev->pm_runtime_disabled)
pm_runtime_disable(&pdev->dev);
- return r;
- }
- return 0;
+ return r;
}
static int dw_i2c_plat_remove(struct platform_device *pdev)
@@ -260,7 +270,8 @@ static int dw_i2c_plat_remove(struct platform_device *pdev)
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_put_sync(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
+ if (!dev->pm_runtime_disabled)
+ pm_runtime_disable(&pdev->dev);
return 0;
}
@@ -296,7 +307,7 @@ static int dw_i2c_plat_suspend(struct device *dev)
struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev);
i2c_dw_disable(i_dev);
- clk_disable_unprepare(i_dev->clk);
+ i2c_dw_plat_prepare_clk(i_dev, false);
return 0;
}
@@ -306,7 +317,7 @@ static int dw_i2c_plat_resume(struct device *dev)
struct platform_device *pdev = to_platform_device(dev);
struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev);
- clk_prepare_enable(i_dev->clk);
+ i2c_dw_plat_prepare_clk(i_dev, true);
if (!i_dev->pm_runtime_disabled)
i2c_dw_init(i_dev);
diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c
index 76e699f9ed97..137125b5eae7 100644
--- a/drivers/i2c/busses/i2c-eg20t.c
+++ b/drivers/i2c/busses/i2c-eg20t.c
@@ -795,6 +795,7 @@ static int pch_i2c_probe(struct pci_dev *pdev,
/* base_addr + offset; */
adap_info->pch_data[i].pch_base_address = base_addr + 0x100 * i;
+ pch_adap->dev.of_node = pdev->dev.of_node;
pch_adap->dev.parent = &pdev->dev;
pch_i2c_init(&adap_info->pch_data[i]);
diff --git a/drivers/i2c/busses/i2c-emev2.c b/drivers/i2c/busses/i2c-emev2.c
index 192ef6b50c79..96bb4e749012 100644
--- a/drivers/i2c/busses/i2c-emev2.c
+++ b/drivers/i2c/busses/i2c-emev2.c
@@ -71,6 +71,7 @@ struct em_i2c_device {
struct i2c_adapter adap;
struct completion msg_done;
struct clk *sclk;
+ struct i2c_client *slave;
};
static inline void em_clear_set_bit(struct em_i2c_device *priv, u8 clear, u8 set, u8 reg)
@@ -226,22 +227,131 @@ static int em_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
return num;
}
+static bool em_i2c_slave_irq(struct em_i2c_device *priv)
+{
+ u8 status, value;
+ enum i2c_slave_event event;
+ int ret;
+
+ if (!priv->slave)
+ return false;
+
+ status = readb(priv->base + I2C_OFS_IICSE0);
+
+ /* Extension code, do not participate */
+ if (status & I2C_BIT_EXC0) {
+ em_clear_set_bit(priv, 0, I2C_BIT_LREL0, I2C_OFS_IICC0);
+ return true;
+ }
+
+ /* Stop detected, we don't know if it's for slave or master */
+ if (status & I2C_BIT_SPD0) {
+ /* Notify slave device */
+ i2c_slave_event(priv->slave, I2C_SLAVE_STOP, &value);
+ /* Pretend we did not handle the interrupt */
+ return false;
+ }
+
+ /* Only handle interrupts addressed to us */
+ if (!(status & I2C_BIT_COI0))
+ return false;
+
+ /* Enable stop interrupts */
+ em_clear_set_bit(priv, 0, I2C_BIT_SPIE0, I2C_OFS_IICC0);
+
+ /* Transmission or Reception */
+ if (status & I2C_BIT_TRC0) {
+ if (status & I2C_BIT_ACKD0) {
+ /* 9 bit interrupt mode */
+ em_clear_set_bit(priv, 0, I2C_BIT_WTIM0, I2C_OFS_IICC0);
+
+ /* Send data */
+ event = status & I2C_BIT_STD0 ?
+ I2C_SLAVE_READ_REQUESTED :
+ I2C_SLAVE_READ_PROCESSED;
+ i2c_slave_event(priv->slave, event, &value);
+ writeb(value, priv->base + I2C_OFS_IIC0);
+ } else {
+ /* NACK, stop transmitting */
+ em_clear_set_bit(priv, 0, I2C_BIT_LREL0, I2C_OFS_IICC0);
+ }
+ } else {
+ /* 8 bit interrupt mode */
+ em_clear_set_bit(priv, I2C_BIT_WTIM0, I2C_BIT_ACKE0,
+ I2C_OFS_IICC0);
+ em_clear_set_bit(priv, I2C_BIT_WTIM0, I2C_BIT_WREL0,
+ I2C_OFS_IICC0);
+
+ if (status & I2C_BIT_STD0) {
+ i2c_slave_event(priv->slave, I2C_SLAVE_WRITE_REQUESTED,
+ &value);
+ } else {
+ /* Recv data */
+ value = readb(priv->base + I2C_OFS_IIC0);
+ ret = i2c_slave_event(priv->slave,
+ I2C_SLAVE_WRITE_RECEIVED, &value);
+ if (ret < 0)
+ em_clear_set_bit(priv, I2C_BIT_ACKE0, 0,
+ I2C_OFS_IICC0);
+ }
+ }
+
+ return true;
+}
+
static irqreturn_t em_i2c_irq_handler(int this_irq, void *dev_id)
{
struct em_i2c_device *priv = dev_id;
+ if (em_i2c_slave_irq(priv))
+ return IRQ_HANDLED;
+
complete(&priv->msg_done);
+
return IRQ_HANDLED;
}
static u32 em_i2c_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SLAVE;
+}
+
+static int em_i2c_reg_slave(struct i2c_client *slave)
+{
+ struct em_i2c_device *priv = i2c_get_adapdata(slave->adapter);
+
+ if (priv->slave)
+ return -EBUSY;
+
+ if (slave->flags & I2C_CLIENT_TEN)
+ return -EAFNOSUPPORT;
+
+ priv->slave = slave;
+
+ /* Set slave address */
+ writeb(slave->addr << 1, priv->base + I2C_OFS_SVA0);
+
+ return 0;
+}
+
+static int em_i2c_unreg_slave(struct i2c_client *slave)
+{
+ struct em_i2c_device *priv = i2c_get_adapdata(slave->adapter);
+
+ WARN_ON(!priv->slave);
+
+ writeb(0, priv->base + I2C_OFS_SVA0);
+
+ priv->slave = NULL;
+
+ return 0;
}
static struct i2c_algorithm em_i2c_algo = {
.master_xfer = em_i2c_xfer,
.functionality = em_i2c_func,
+ .reg_slave = em_i2c_reg_slave,
+ .unreg_slave = em_i2c_unreg_slave,
};
static int em_i2c_probe(struct platform_device *pdev)
diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c
index ab492301581a..b6c080334297 100644
--- a/drivers/i2c/busses/i2c-ibm_iic.c
+++ b/drivers/i2c/busses/i2c-ibm_iic.c
@@ -99,7 +99,7 @@ static void dump_iic_regs(const char* header, struct ibm_iic_private* dev)
#endif
/* Bus timings (in ns) for bit-banging */
-static struct i2c_timings {
+static struct ibm_iic_timings {
unsigned int hd_sta;
unsigned int su_sto;
unsigned int low;
@@ -241,7 +241,7 @@ static int iic_dc_wait(volatile struct iic_regs __iomem *iic, u8 mask)
static int iic_smbus_quick(struct ibm_iic_private* dev, const struct i2c_msg* p)
{
volatile struct iic_regs __iomem *iic = dev->vaddr;
- const struct i2c_timings* t = &timings[dev->fast_mode ? 1 : 0];
+ const struct ibm_iic_timings *t = &timings[dev->fast_mode ? 1 : 0];
u8 mask, v, sda;
int i, res;
diff --git a/drivers/i2c/busses/i2c-img-scb.c b/drivers/i2c/busses/i2c-img-scb.c
index 3795fe130ef2..379ef9c31664 100644
--- a/drivers/i2c/busses/i2c-img-scb.c
+++ b/drivers/i2c/busses/i2c-img-scb.c
@@ -151,10 +151,11 @@
#define INT_FIFO_EMPTYING BIT(12)
#define INT_TRANSACTION_DONE BIT(15)
#define INT_SLAVE_EVENT BIT(16)
+#define INT_MASTER_HALTED BIT(17)
#define INT_TIMING BIT(18)
+#define INT_STOP_DETECTED BIT(19)
#define INT_FIFO_FULL_FILLING (INT_FIFO_FULL | INT_FIFO_FILLING)
-#define INT_FIFO_EMPTY_EMPTYING (INT_FIFO_EMPTY | INT_FIFO_EMPTYING)
/* Level interrupts need clearing after handling instead of before */
#define INT_LEVEL 0x01e00
@@ -177,7 +178,8 @@
INT_FIFO_FULL | \
INT_FIFO_FILLING | \
INT_FIFO_EMPTY | \
- INT_FIFO_EMPTYING)
+ INT_MASTER_HALTED | \
+ INT_STOP_DETECTED)
#define INT_ENABLE_MASK_WAITSTOP (INT_SLAVE_EVENT | \
INT_ADDR_ACK_ERR | \
@@ -511,7 +513,17 @@ static void img_i2c_soft_reset(struct img_i2c *i2c)
SCB_CONTROL_CLK_ENABLE | SCB_CONTROL_SOFT_RESET);
}
-/* enable or release transaction halt for control of repeated starts */
+/*
+ * Enable or release transaction halt for control of repeated starts.
+ * In version 3.3 of the IP when transaction halt is set, an interrupt
+ * will be generated after each byte of a transfer instead of after
+ * every transfer but before the stop bit.
+ * Due to this behaviour we have to be careful that every time we
+ * release the transaction halt we have to re-enable it straight away
+ * so that we only process a single byte, not doing so will result in
+ * all remaining bytes been processed and a stop bit being issued,
+ * which will prevent us having a repeated start.
+ */
static void img_i2c_transaction_halt(struct img_i2c *i2c, bool t_halt)
{
u32 val;
@@ -580,7 +592,6 @@ static void img_i2c_read(struct img_i2c *i2c)
img_i2c_writel(i2c, SCB_READ_ADDR_REG, i2c->msg.addr);
img_i2c_writel(i2c, SCB_READ_COUNT_REG, i2c->msg.len);
- img_i2c_transaction_halt(i2c, false);
mod_timer(&i2c->check_timer, jiffies + msecs_to_jiffies(1));
}
@@ -594,7 +605,6 @@ static void img_i2c_write(struct img_i2c *i2c)
img_i2c_writel(i2c, SCB_WRITE_ADDR_REG, i2c->msg.addr);
img_i2c_writel(i2c, SCB_WRITE_COUNT_REG, i2c->msg.len);
- img_i2c_transaction_halt(i2c, false);
mod_timer(&i2c->check_timer, jiffies + msecs_to_jiffies(1));
img_i2c_write_fifo(i2c);
@@ -750,7 +760,9 @@ static unsigned int img_i2c_atomic(struct img_i2c *i2c,
next_cmd = CMD_RET_ACK;
break;
case CMD_RET_ACK:
- if (i2c->line_status & LINESTAT_ACK_DET) {
+ if (i2c->line_status & LINESTAT_ACK_DET ||
+ (i2c->line_status & LINESTAT_NACK_DET &&
+ i2c->msg.flags & I2C_M_IGNORE_NAK)) {
if (i2c->msg.len == 0) {
next_cmd = CMD_GEN_STOP;
} else if (i2c->msg.flags & I2C_M_RD) {
@@ -858,34 +870,42 @@ static unsigned int img_i2c_auto(struct img_i2c *i2c,
/* Enable transaction halt on start bit */
if (!i2c->last_msg && line_status & LINESTAT_START_BIT_DET) {
- img_i2c_transaction_halt(i2c, true);
+ img_i2c_transaction_halt(i2c, !i2c->last_msg);
/* we're no longer interested in the slave event */
i2c->int_enable &= ~INT_SLAVE_EVENT;
}
mod_timer(&i2c->check_timer, jiffies + msecs_to_jiffies(1));
+ if (int_status & INT_STOP_DETECTED) {
+ /* Drain remaining data in FIFO and complete transaction */
+ if (i2c->msg.flags & I2C_M_RD)
+ img_i2c_read_fifo(i2c);
+ return ISR_COMPLETE(0);
+ }
+
if (i2c->msg.flags & I2C_M_RD) {
- if (int_status & INT_FIFO_FULL_FILLING) {
+ if (int_status & (INT_FIFO_FULL_FILLING | INT_MASTER_HALTED)) {
img_i2c_read_fifo(i2c);
if (i2c->msg.len == 0)
return ISR_WAITSTOP;
}
} else {
- if (int_status & INT_FIFO_EMPTY_EMPTYING) {
- /*
- * The write fifo empty indicates that we're in the
- * last byte so it's safe to start a new write
- * transaction without losing any bytes from the
- * previous one.
- * see 2.3.7 Repeated Start Transactions.
- */
+ if (int_status & (INT_FIFO_EMPTY | INT_MASTER_HALTED)) {
if ((int_status & INT_FIFO_EMPTY) &&
i2c->msg.len == 0)
return ISR_WAITSTOP;
img_i2c_write_fifo(i2c);
}
}
+ if (int_status & INT_MASTER_HALTED) {
+ /*
+ * Release and then enable transaction halt, to
+ * allow only a single byte to proceed.
+ */
+ img_i2c_transaction_halt(i2c, false);
+ img_i2c_transaction_halt(i2c, !i2c->last_msg);
+ }
return 0;
}
@@ -1017,20 +1037,23 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
return -EIO;
for (i = 0; i < num; i++) {
- if (likely(msgs[i].len))
- continue;
/*
* 0 byte reads are not possible because the slave could try
* and pull the data line low, preventing a stop bit.
*/
- if (unlikely(msgs[i].flags & I2C_M_RD))
+ if (!msgs[i].len && msgs[i].flags & I2C_M_RD)
return -EIO;
/*
* 0 byte writes are possible and used for probing, but we
* cannot do them in automatic mode, so use atomic mode
* instead.
+ *
+ * Also, the I2C_M_IGNORE_NAK mode can only be implemented
+ * in atomic mode.
*/
- atomic = true;
+ if (!msgs[i].len ||
+ (msgs[i].flags & I2C_M_IGNORE_NAK))
+ atomic = true;
}
ret = clk_prepare_enable(i2c->scb_clk);
@@ -1069,12 +1092,31 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
img_i2c_writel(i2c, SCB_INT_CLEAR_REG, ~0);
img_i2c_writel(i2c, SCB_CLEAR_REG, ~0);
- if (atomic)
+ if (atomic) {
img_i2c_atomic_start(i2c);
- else if (msg->flags & I2C_M_RD)
- img_i2c_read(i2c);
- else
- img_i2c_write(i2c);
+ } else {
+ /*
+ * Enable transaction halt if not the last message in
+ * the queue so that we can control repeated starts.
+ */
+ img_i2c_transaction_halt(i2c, !i2c->last_msg);
+
+ if (msg->flags & I2C_M_RD)
+ img_i2c_read(i2c);
+ else
+ img_i2c_write(i2c);
+
+ /*
+ * Release and then enable transaction halt, to
+ * allow only a single byte to proceed.
+ * This doesn't have an effect on the initial transfer
+ * but will allow the following transfers to start
+ * processing if the previous transfer was marked as
+ * complete while the i2c block was halted.
+ */
+ img_i2c_transaction_halt(i2c, false);
+ img_i2c_transaction_halt(i2c, !i2c->last_msg);
+ }
spin_unlock_irqrestore(&i2c->lock, flags);
time_left = wait_for_completion_timeout(&i2c->msg_complete,
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 9bb0b056b25f..a2b132cef717 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -29,9 +29,6 @@
*
*/
-/** Includes *******************************************************************
-*******************************************************************************/
-
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
@@ -53,12 +50,10 @@
#include <linux/pinctrl/consumer.h>
#include <linux/platform_data/i2c-imx.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/sched.h>
#include <linux/slab.h>
-/** Defines ********************************************************************
-*******************************************************************************/
-
/* This will be the driver name the kernel reports */
#define DRIVER_NAME "imx-i2c"
@@ -120,8 +115,7 @@
#define I2CR_IEN_OPCODE_0 0x0
#define I2CR_IEN_OPCODE_1 I2CR_IEN
-/** Variables ******************************************************************
-*******************************************************************************/
+#define I2C_PM_TIMEOUT 10 /* ms */
/*
* sorted list of clock divider, register value pairs
@@ -346,7 +340,7 @@ fail_tx:
dma_release_channel(dma->chan_tx);
fail_al:
devm_kfree(dev, dma);
- dev_info(dev, "can't use DMA\n");
+ dev_info(dev, "can't use DMA, using PIO instead.\n");
}
static void i2c_imx_dma_callback(void *arg)
@@ -393,6 +387,7 @@ static int i2c_imx_dma_xfer(struct imx_i2c_struct *i2c_imx,
return 0;
err_submit:
+ dmaengine_terminate_all(dma->chan_using);
err_desc:
dma_unmap_single(chan_dev, dma->dma_buf,
dma->dma_len, dma->dma_data_dir);
@@ -416,9 +411,6 @@ static void i2c_imx_dma_free(struct imx_i2c_struct *i2c_imx)
dma->chan_using = NULL;
}
-/** Functions for IMX I2C adapter driver ***************************************
-*******************************************************************************/
-
static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy)
{
unsigned long orig_jiffies = jiffies;
@@ -527,9 +519,6 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
i2c_imx_set_clk(i2c_imx);
- result = clk_prepare_enable(i2c_imx->clk);
- if (result)
- return result;
imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR);
/* Enable I2C controller */
imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
@@ -582,7 +571,6 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
/* Disable I2C controller */
temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
- clk_disable_unprepare(i2c_imx->clk);
}
static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
@@ -901,6 +889,10 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
+ result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
+ if (result < 0)
+ goto out;
+
/* Start I2C transfer */
result = i2c_imx_start(i2c_imx);
if (result) {
@@ -964,6 +956,10 @@ fail0:
/* Stop I2C transfer */
i2c_imx_stop(i2c_imx);
+ pm_runtime_mark_last_busy(i2c_imx->adapter.dev.parent);
+ pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent);
+
+out:
dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
(result < 0) ? "error" : "success msg",
(result < 0) ? result : num);
@@ -997,10 +993,8 @@ static void i2c_imx_init_recovery_info(struct imx_i2c_struct *i2c_imx,
PINCTRL_STATE_DEFAULT);
i2c_imx->pinctrl_pins_gpio = pinctrl_lookup_state(i2c_imx->pinctrl,
"gpio");
- rinfo->sda_gpio = of_get_named_gpio_flags(pdev->dev.of_node,
- "sda-gpios", 0, NULL);
- rinfo->scl_gpio = of_get_named_gpio_flags(pdev->dev.of_node,
- "scl-gpios", 0, NULL);
+ rinfo->sda_gpio = of_get_named_gpio(pdev->dev.of_node, "sda-gpios", 0);
+ rinfo->scl_gpio = of_get_named_gpio(pdev->dev.of_node, "scl-gpios", 0);
if (!gpio_is_valid(rinfo->sda_gpio) ||
!gpio_is_valid(rinfo->scl_gpio) ||
@@ -1083,7 +1077,7 @@ static int i2c_imx_probe(struct platform_device *pdev)
ret = clk_prepare_enable(i2c_imx->clk);
if (ret) {
- dev_err(&pdev->dev, "can't enable I2C clock\n");
+ dev_err(&pdev->dev, "can't enable I2C clock, ret=%d\n", ret);
return ret;
}
@@ -1107,6 +1101,18 @@ static int i2c_imx_probe(struct platform_device *pdev)
/* Set up adapter data */
i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);
+ /* Set up platform driver data */
+ platform_set_drvdata(pdev, i2c_imx);
+
+ pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0)
+ goto rpm_disable;
+
/* Set up clock divider */
i2c_imx->bitrate = IMX_I2C_BIT_RATE;
ret = of_property_read_u32(pdev->dev.of_node,
@@ -1119,18 +1125,17 @@ static int i2c_imx_probe(struct platform_device *pdev)
i2c_imx, IMX_I2C_I2CR);
imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
+ i2c_imx_init_recovery_info(i2c_imx, pdev);
+
/* Add I2C adapter */
ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
if (ret < 0) {
dev_err(&pdev->dev, "registration failed\n");
- goto clk_disable;
+ goto rpm_disable;
}
- i2c_imx_init_recovery_info(i2c_imx, pdev);
-
- /* Set up platform driver data */
- platform_set_drvdata(pdev, i2c_imx);
- clk_disable_unprepare(i2c_imx->clk);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_put_autosuspend(&pdev->dev);
dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq);
dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res);
@@ -1143,6 +1148,12 @@ static int i2c_imx_probe(struct platform_device *pdev)
return 0; /* Return OK */
+rpm_disable:
+ pm_runtime_put_noidle(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
+
clk_disable:
clk_disable_unprepare(i2c_imx->clk);
return ret;
@@ -1151,6 +1162,11 @@ clk_disable:
static int i2c_imx_remove(struct platform_device *pdev)
{
struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0)
+ return ret;
/* remove adapter */
dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n");
@@ -1165,17 +1181,54 @@ static int i2c_imx_remove(struct platform_device *pdev)
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2CR);
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR);
+ clk_disable_unprepare(i2c_imx->clk);
+
+ pm_runtime_put_noidle(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int i2c_imx_runtime_suspend(struct device *dev)
+{
+ struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(i2c_imx->clk);
+
return 0;
}
+static int i2c_imx_runtime_resume(struct device *dev)
+{
+ struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(i2c_imx->clk);
+ if (ret)
+ dev_err(dev, "can't enable I2C clock, ret=%d\n", ret);
+
+ return ret;
+}
+
+static const struct dev_pm_ops i2c_imx_pm_ops = {
+ SET_RUNTIME_PM_OPS(i2c_imx_runtime_suspend,
+ i2c_imx_runtime_resume, NULL)
+};
+#define I2C_IMX_PM_OPS (&i2c_imx_pm_ops)
+#else
+#define I2C_IMX_PM_OPS NULL
+#endif /* CONFIG_PM */
+
static struct platform_driver i2c_imx_driver = {
.probe = i2c_imx_probe,
.remove = i2c_imx_remove,
- .driver = {
- .name = DRIVER_NAME,
+ .driver = {
+ .name = DRIVER_NAME,
+ .pm = I2C_IMX_PM_OPS,
.of_match_table = i2c_imx_dt_ids,
},
- .id_table = imx_i2c_devtype,
+ .id_table = imx_i2c_devtype,
};
static int __init i2c_adap_imx_init(void)
diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c
index 9b867169142f..aec8e6ce38a4 100644
--- a/drivers/i2c/busses/i2c-mt65xx.c
+++ b/drivers/i2c/busses/i2c-mt65xx.c
@@ -132,6 +132,7 @@ struct mtk_i2c_compatible {
unsigned char pmic_i2c: 1;
unsigned char dcm: 1;
unsigned char auto_restart: 1;
+ unsigned char aux_len_reg: 1;
};
struct mtk_i2c {
@@ -153,6 +154,8 @@ struct mtk_i2c {
enum mtk_trans_op op;
u16 timing_reg;
u16 high_speed_reg;
+ unsigned char auto_restart;
+ bool ignore_restart_irq;
const struct mtk_i2c_compatible *dev_comp;
};
@@ -178,6 +181,7 @@ static const struct mtk_i2c_compatible mt6577_compat = {
.pmic_i2c = 0,
.dcm = 1,
.auto_restart = 0,
+ .aux_len_reg = 0,
};
static const struct mtk_i2c_compatible mt6589_compat = {
@@ -185,6 +189,7 @@ static const struct mtk_i2c_compatible mt6589_compat = {
.pmic_i2c = 1,
.dcm = 0,
.auto_restart = 0,
+ .aux_len_reg = 0,
};
static const struct mtk_i2c_compatible mt8173_compat = {
@@ -192,6 +197,7 @@ static const struct mtk_i2c_compatible mt8173_compat = {
.pmic_i2c = 0,
.dcm = 1,
.auto_restart = 1,
+ .aux_len_reg = 1,
};
static const struct of_device_id mtk_i2c_of_match[] = {
@@ -373,7 +379,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
i2c->irq_stat = 0;
- if (i2c->dev_comp->auto_restart)
+ if (i2c->auto_restart)
restart_flag = I2C_RS_TRANSFER;
reinit_completion(&i2c->msg_complete);
@@ -411,8 +417,14 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
/* Set transfer and transaction len */
if (i2c->op == I2C_MASTER_WRRD) {
- writew(msgs->len | ((msgs + 1)->len) << 8,
- i2c->base + OFFSET_TRANSFER_LEN);
+ if (i2c->dev_comp->aux_len_reg) {
+ writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN);
+ writew((msgs + 1)->len, i2c->base +
+ OFFSET_TRANSFER_LEN_AUX);
+ } else {
+ writew(msgs->len | ((msgs + 1)->len) << 8,
+ i2c->base + OFFSET_TRANSFER_LEN);
+ }
writew(I2C_WRRD_TRANAC_VALUE, i2c->base + OFFSET_TRANSAC_LEN);
} else {
writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN);
@@ -461,7 +473,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
writel(I2C_DMA_START_EN, i2c->pdmabase + OFFSET_EN);
- if (!i2c->dev_comp->auto_restart) {
+ if (!i2c->auto_restart) {
start_reg = I2C_TRANSAC_START;
} else {
start_reg = I2C_TRANSAC_START | I2C_RS_MUL_TRIG;
@@ -518,6 +530,24 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
if (ret)
return ret;
+ i2c->auto_restart = i2c->dev_comp->auto_restart;
+
+ /* checking if we can skip restart and optimize using WRRD mode */
+ if (i2c->auto_restart && num == 2) {
+ if (!(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD) &&
+ msgs[0].addr == msgs[1].addr) {
+ i2c->auto_restart = 0;
+ }
+ }
+
+ if (i2c->auto_restart && num >= 2 && i2c->speed_hz > MAX_FS_MODE_SPEED)
+ /* ignore the first restart irq after the master code,
+ * otherwise the first transfer will be discarded.
+ */
+ i2c->ignore_restart_irq = true;
+ else
+ i2c->ignore_restart_irq = false;
+
while (left_num--) {
if (!msgs->buf) {
dev_dbg(i2c->dev, "data buffer is NULL.\n");
@@ -530,7 +560,7 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
else
i2c->op = I2C_MASTER_WR;
- if (!i2c->dev_comp->auto_restart) {
+ if (!i2c->auto_restart) {
if (num > 1) {
/* combined two messages into one transaction */
i2c->op = I2C_MASTER_WRRD;
@@ -559,7 +589,7 @@ static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
u16 restart_flag = 0;
u16 intr_stat;
- if (i2c->dev_comp->auto_restart)
+ if (i2c->auto_restart)
restart_flag = I2C_RS_TRANSFER;
intr_stat = readw(i2c->base + OFFSET_INTR_STAT);
@@ -571,8 +601,16 @@ static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
* i2c->irq_stat need keep the two interrupt value.
*/
i2c->irq_stat |= intr_stat;
- if (i2c->irq_stat & (I2C_TRANSAC_COMP | restart_flag))
- complete(&i2c->msg_complete);
+
+ if (i2c->ignore_restart_irq && (i2c->irq_stat & restart_flag)) {
+ i2c->ignore_restart_irq = false;
+ i2c->irq_stat = 0;
+ writew(I2C_RS_MUL_CNFG | I2C_RS_MUL_TRIG | I2C_TRANSAC_START,
+ i2c->base + OFFSET_START);
+ } else {
+ if (i2c->irq_stat & (I2C_TRANSAC_COMP | restart_flag))
+ complete(&i2c->msg_complete);
+ }
return IRQ_HANDLED;
}
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index 5801227b97ab..43207f52e5a3 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -146,6 +146,8 @@ struct mv64xxx_i2c_data {
bool errata_delay;
struct reset_control *rstc;
bool irq_clear_inverted;
+ /* Clk div is 2 to the power n, not 2 to the power n + 1 */
+ bool clk_n_base_0;
};
static struct mv64xxx_i2c_regs mv64xxx_i2c_regs_mv64xxx = {
@@ -757,25 +759,29 @@ MODULE_DEVICE_TABLE(of, mv64xxx_i2c_of_match_table);
#ifdef CONFIG_OF
#ifdef CONFIG_HAVE_CLK
static int
-mv64xxx_calc_freq(const int tclk, const int n, const int m)
+mv64xxx_calc_freq(struct mv64xxx_i2c_data *drv_data,
+ const int tclk, const int n, const int m)
{
- return tclk / (10 * (m + 1) * (2 << n));
+ if (drv_data->clk_n_base_0)
+ return tclk / (10 * (m + 1) * (1 << n));
+ else
+ return tclk / (10 * (m + 1) * (2 << n));
}
static bool
-mv64xxx_find_baud_factors(const u32 req_freq, const u32 tclk, u32 *best_n,
- u32 *best_m)
+mv64xxx_find_baud_factors(struct mv64xxx_i2c_data *drv_data,
+ const u32 req_freq, const u32 tclk)
{
int freq, delta, best_delta = INT_MAX;
int m, n;
for (n = 0; n <= 7; n++)
for (m = 0; m <= 15; m++) {
- freq = mv64xxx_calc_freq(tclk, n, m);
+ freq = mv64xxx_calc_freq(drv_data, tclk, n, m);
delta = req_freq - freq;
if (delta >= 0 && delta < best_delta) {
- *best_m = m;
- *best_n = n;
+ drv_data->freq_m = m;
+ drv_data->freq_n = n;
best_delta = delta;
}
if (best_delta == 0)
@@ -813,8 +819,11 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
if (of_property_read_u32(np, "clock-frequency", &bus_freq))
bus_freq = 100000; /* 100kHz by default */
- if (!mv64xxx_find_baud_factors(bus_freq, tclk,
- &drv_data->freq_n, &drv_data->freq_m)) {
+ if (of_device_is_compatible(np, "allwinner,sun4i-a10-i2c") ||
+ of_device_is_compatible(np, "allwinner,sun6i-a31-i2c"))
+ drv_data->clk_n_base_0 = true;
+
+ if (!mv64xxx_find_baud_factors(drv_data, bus_freq, tclk)) {
rc = -EINVAL;
goto out;
}
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
index 630bce68bf38..e04598595073 100644
--- a/drivers/i2c/busses/i2c-piix4.c
+++ b/drivers/i2c/busses/i2c-piix4.c
@@ -23,6 +23,9 @@
Note: we assume there can only be one device, with one or more
SMBus interfaces.
+ The device can register multiple i2c_adapters (up to PIIX4_MAX_ADAPTERS).
+ For devices supporting multiple ports the i2c_adapter should provide
+ an i2c_algorithm to access them.
*/
#include <linux/module.h>
@@ -37,6 +40,7 @@
#include <linux/dmi.h>
#include <linux/acpi.h>
#include <linux/io.h>
+#include <linux/mutex.h>
/* PIIX4 SMBus address offsets */
@@ -75,6 +79,16 @@
#define PIIX4_WORD_DATA 0x0C
#define PIIX4_BLOCK_DATA 0x14
+/* Multi-port constants */
+#define PIIX4_MAX_ADAPTERS 4
+
+/* SB800 constants */
+#define SB800_PIIX4_SMB_IDX 0xcd6
+
+/* SB800 port is selected by bits 2:1 of the smb_en register (0x2c) */
+#define SB800_PIIX4_PORT_IDX 0x2c
+#define SB800_PIIX4_PORT_IDX_MASK 0x06
+
/* insmod parameters */
/* If force is set to anything different from 0, we forcibly enable the
@@ -122,8 +136,19 @@ static const struct dmi_system_id piix4_dmi_ibm[] = {
{ },
};
+/* SB800 globals */
+static const char *piix4_main_port_names_sb800[PIIX4_MAX_ADAPTERS] = {
+ "SDA0", "SDA2", "SDA3", "SDA4"
+};
+static const char *piix4_aux_port_name_sb800 = "SDA1";
+
struct i2c_piix4_adapdata {
unsigned short smba;
+
+ /* SB800 */
+ bool sb800_main;
+ unsigned short port;
+ struct mutex *mutex;
};
static int piix4_setup(struct pci_dev *PIIX4_dev,
@@ -229,7 +254,6 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
const struct pci_device_id *id, u8 aux)
{
unsigned short piix4_smba;
- unsigned short smba_idx = 0xcd6;
u8 smba_en_lo, smba_en_hi, smb_en, smb_en_status;
u8 i2ccfg, i2ccfg_offset = 0x10;
@@ -251,16 +275,10 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
else
smb_en = (aux) ? 0x28 : 0x2c;
- if (!request_region(smba_idx, 2, "smba_idx")) {
- dev_err(&PIIX4_dev->dev, "SMBus base address index region "
- "0x%x already in use!\n", smba_idx);
- return -EBUSY;
- }
- outb_p(smb_en, smba_idx);
- smba_en_lo = inb_p(smba_idx + 1);
- outb_p(smb_en + 1, smba_idx);
- smba_en_hi = inb_p(smba_idx + 1);
- release_region(smba_idx, 2);
+ outb_p(smb_en, SB800_PIIX4_SMB_IDX);
+ smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
+ outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX);
+ smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1);
if (!smb_en) {
smb_en_status = smba_en_lo & 0x10;
@@ -483,7 +501,7 @@ static s32 piix4_access(struct i2c_adapter * adap, u16 addr,
if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
return -EINVAL;
outb_p(len, SMBHSTDAT0);
- i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */
+ inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */
for (i = 1; i <= len; i++)
outb_p(data->block[i], SMBBLKDAT);
}
@@ -516,7 +534,7 @@ static s32 piix4_access(struct i2c_adapter * adap, u16 addr,
data->block[0] = inb_p(SMBHSTDAT0);
if (data->block[0] == 0 || data->block[0] > I2C_SMBUS_BLOCK_MAX)
return -EPROTO;
- i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */
+ inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */
for (i = 1; i <= data->block[0]; i++)
data->block[i] = inb_p(SMBBLKDAT);
break;
@@ -524,6 +542,43 @@ static s32 piix4_access(struct i2c_adapter * adap, u16 addr,
return 0;
}
+/*
+ * Handles access to multiple SMBus ports on the SB800.
+ * The port is selected by bits 2:1 of the smb_en register (0x2c).
+ * Returns negative errno on error.
+ *
+ * Note: The selected port must be returned to the initial selection to avoid
+ * problems on certain systems.
+ */
+static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size, union i2c_smbus_data *data)
+{
+ struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap);
+ u8 smba_en_lo;
+ u8 port;
+ int retval;
+
+ mutex_lock(adapdata->mutex);
+
+ outb_p(SB800_PIIX4_PORT_IDX, SB800_PIIX4_SMB_IDX);
+ smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
+
+ port = adapdata->port;
+ if ((smba_en_lo & SB800_PIIX4_PORT_IDX_MASK) != (port << 1))
+ outb_p((smba_en_lo & ~SB800_PIIX4_PORT_IDX_MASK) | (port << 1),
+ SB800_PIIX4_SMB_IDX + 1);
+
+ retval = piix4_access(adap, addr, flags, read_write,
+ command, size, data);
+
+ outb_p(smba_en_lo, SB800_PIIX4_SMB_IDX + 1);
+
+ mutex_unlock(adapdata->mutex);
+
+ return retval;
+}
+
static u32 piix4_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
@@ -536,6 +591,11 @@ static const struct i2c_algorithm smbus_algorithm = {
.functionality = piix4_func,
};
+static const struct i2c_algorithm piix4_smbus_algorithm_sb800 = {
+ .smbus_xfer = piix4_access_sb800,
+ .functionality = piix4_func,
+};
+
static const struct pci_device_id piix4_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3) },
@@ -561,11 +621,11 @@ static const struct pci_device_id piix4_ids[] = {
MODULE_DEVICE_TABLE (pci, piix4_ids);
-static struct i2c_adapter *piix4_main_adapter;
+static struct i2c_adapter *piix4_main_adapters[PIIX4_MAX_ADAPTERS];
static struct i2c_adapter *piix4_aux_adapter;
static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
- struct i2c_adapter **padap)
+ const char *name, struct i2c_adapter **padap)
{
struct i2c_adapter *adap;
struct i2c_piix4_adapdata *adapdata;
@@ -594,7 +654,7 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
adap->dev.parent = &dev->dev;
snprintf(adap->name, sizeof(adap->name),
- "SMBus PIIX4 adapter at %04x", smba);
+ "SMBus PIIX4 adapter %s at %04x", name, smba);
i2c_set_adapdata(adap, adapdata);
@@ -611,6 +671,54 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
return 0;
}
+static int piix4_add_adapters_sb800(struct pci_dev *dev, unsigned short smba)
+{
+ struct mutex *mutex;
+ struct i2c_piix4_adapdata *adapdata;
+ int port;
+ int retval;
+
+ mutex = kzalloc(sizeof(*mutex), GFP_KERNEL);
+ if (mutex == NULL)
+ return -ENOMEM;
+
+ mutex_init(mutex);
+
+ for (port = 0; port < PIIX4_MAX_ADAPTERS; port++) {
+ retval = piix4_add_adapter(dev, smba,
+ piix4_main_port_names_sb800[port],
+ &piix4_main_adapters[port]);
+ if (retval < 0)
+ goto error;
+
+ piix4_main_adapters[port]->algo = &piix4_smbus_algorithm_sb800;
+
+ adapdata = i2c_get_adapdata(piix4_main_adapters[port]);
+ adapdata->sb800_main = true;
+ adapdata->port = port;
+ adapdata->mutex = mutex;
+ }
+
+ return retval;
+
+error:
+ dev_err(&dev->dev,
+ "Error setting up SB800 adapters. Unregistering!\n");
+ while (--port >= 0) {
+ adapdata = i2c_get_adapdata(piix4_main_adapters[port]);
+ if (adapdata->smba) {
+ i2c_del_adapter(piix4_main_adapters[port]);
+ kfree(adapdata);
+ kfree(piix4_main_adapters[port]);
+ piix4_main_adapters[port] = NULL;
+ }
+ }
+
+ kfree(mutex);
+
+ return retval;
+}
+
static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int retval;
@@ -618,20 +726,41 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
if ((dev->vendor == PCI_VENDOR_ID_ATI &&
dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
dev->revision >= 0x40) ||
- dev->vendor == PCI_VENDOR_ID_AMD)
+ dev->vendor == PCI_VENDOR_ID_AMD) {
+ if (!request_region(SB800_PIIX4_SMB_IDX, 2, "smba_idx")) {
+ dev_err(&dev->dev,
+ "SMBus base address index region 0x%x already in use!\n",
+ SB800_PIIX4_SMB_IDX);
+ return -EBUSY;
+ }
+
/* base address location etc changed in SB800 */
retval = piix4_setup_sb800(dev, id, 0);
- else
- retval = piix4_setup(dev, id);
+ if (retval < 0) {
+ release_region(SB800_PIIX4_SMB_IDX, 2);
+ return retval;
+ }
- /* If no main SMBus found, give up */
- if (retval < 0)
- return retval;
+ /*
+ * Try to register multiplexed main SMBus adapter,
+ * give up if we can't
+ */
+ retval = piix4_add_adapters_sb800(dev, retval);
+ if (retval < 0) {
+ release_region(SB800_PIIX4_SMB_IDX, 2);
+ return retval;
+ }
+ } else {
+ retval = piix4_setup(dev, id);
+ if (retval < 0)
+ return retval;
- /* Try to register main SMBus adapter, give up if we can't */
- retval = piix4_add_adapter(dev, retval, &piix4_main_adapter);
- if (retval < 0)
- return retval;
+ /* Try to register main SMBus adapter, give up if we can't */
+ retval = piix4_add_adapter(dev, retval, "main",
+ &piix4_main_adapters[0]);
+ if (retval < 0)
+ return retval;
+ }
/* Check for auxiliary SMBus on some AMD chipsets */
retval = -ENODEV;
@@ -654,7 +783,8 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (retval > 0) {
/* Try to add the aux adapter if it exists,
* piix4_add_adapter will clean up if this fails */
- piix4_add_adapter(dev, retval, &piix4_aux_adapter);
+ piix4_add_adapter(dev, retval, piix4_aux_port_name_sb800,
+ &piix4_aux_adapter);
}
return 0;
@@ -666,7 +796,13 @@ static void piix4_adap_remove(struct i2c_adapter *adap)
if (adapdata->smba) {
i2c_del_adapter(adap);
- release_region(adapdata->smba, SMBIOSIZE);
+ if (adapdata->port == 0) {
+ release_region(adapdata->smba, SMBIOSIZE);
+ if (adapdata->sb800_main) {
+ kfree(adapdata->mutex);
+ release_region(SB800_PIIX4_SMB_IDX, 2);
+ }
+ }
kfree(adapdata);
kfree(adap);
}
@@ -674,9 +810,13 @@ static void piix4_adap_remove(struct i2c_adapter *adap)
static void piix4_remove(struct pci_dev *dev)
{
- if (piix4_main_adapter) {
- piix4_adap_remove(piix4_main_adapter);
- piix4_main_adapter = NULL;
+ int port = PIIX4_MAX_ADAPTERS;
+
+ while (--port >= 0) {
+ if (piix4_main_adapters[port]) {
+ piix4_adap_remove(piix4_main_adapters[port]);
+ piix4_main_adapters[port] = NULL;
+ }
}
if (piix4_aux_adapter) {
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
index b0ae560b38c3..1abeadc8ab79 100644
--- a/drivers/i2c/busses/i2c-rcar.c
+++ b/drivers/i2c/busses/i2c-rcar.c
@@ -1,7 +1,8 @@
/*
* Driver for the Renesas RCar I2C unit
*
- * Copyright (C) 2014 Wolfram Sang <wsa@sang-engineering.com>
+ * Copyright (C) 2014-15 Wolfram Sang <wsa@sang-engineering.com>
+ * Copyright (C) 2011-2015 Renesas Electronics Corporation
*
* Copyright (C) 2012-14 Renesas Solutions Corp.
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
@@ -9,9 +10,6 @@
* This file is based on the drivers/i2c/busses/i2c-sh7760.c
* (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau@msc-ge.com>
*
- * This file used out-of-tree driver i2c-rcar.c
- * Copyright (C) 2011-2012 Renesas Electronics Corporation
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
@@ -33,7 +31,6 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
-#include <linux/spinlock.h>
/* register offsets */
#define ICSCR 0x00 /* slave ctrl */
@@ -84,6 +81,7 @@
#define RCAR_BUS_PHASE_START (MDBS | MIE | ESG)
#define RCAR_BUS_PHASE_DATA (MDBS | MIE)
+#define RCAR_BUS_MASK_DATA (~(ESG | FSB) & 0xFF)
#define RCAR_BUS_PHASE_STOP (MDBS | MIE | FSB)
#define RCAR_IRQ_SEND (MNR | MAL | MST | MAT | MDE)
@@ -94,10 +92,13 @@
#define RCAR_IRQ_ACK_RECV (~(MAT | MDR) & 0xFF)
#define ID_LAST_MSG (1 << 0)
-#define ID_IOERROR (1 << 1)
+#define ID_FIRST_MSG (1 << 1)
#define ID_DONE (1 << 2)
#define ID_ARBLOST (1 << 3)
#define ID_NACK (1 << 4)
+/* persistent flags */
+#define ID_P_PM_BLOCKED (1 << 31)
+#define ID_P_MASK ID_P_PM_BLOCKED
enum rcar_i2c_type {
I2C_RCAR_GEN1,
@@ -108,10 +109,10 @@ enum rcar_i2c_type {
struct rcar_i2c_priv {
void __iomem *io;
struct i2c_adapter adap;
- struct i2c_msg *msg;
+ struct i2c_msg *msg;
+ int msgs_left;
struct clk *clk;
- spinlock_t lock;
wait_queue_head_t wait;
int pos;
@@ -124,9 +125,6 @@ struct rcar_i2c_priv {
#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
#define rcar_i2c_is_recv(p) ((p)->msg->flags & I2C_M_RD)
-#define rcar_i2c_flags_set(p, f) ((p)->flags |= (f))
-#define rcar_i2c_flags_has(p, f) ((p)->flags & (f))
-
#define LOOP_TIMEOUT 1024
@@ -144,9 +142,10 @@ static void rcar_i2c_init(struct rcar_i2c_priv *priv)
{
/* reset master mode */
rcar_i2c_write(priv, ICMIER, 0);
- rcar_i2c_write(priv, ICMCR, 0);
+ rcar_i2c_write(priv, ICMCR, MDBS);
rcar_i2c_write(priv, ICMSR, 0);
- rcar_i2c_write(priv, ICMAR, 0);
+ /* start clock */
+ rcar_i2c_write(priv, ICCCR, priv->icccr);
}
static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
@@ -163,15 +162,17 @@ static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
return -EBUSY;
}
-static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
- u32 bus_speed,
- struct device *dev)
+static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv, struct i2c_timings *t)
{
- u32 scgd, cdf;
- u32 round, ick;
- u32 scl;
- u32 cdf_width;
+ u32 scgd, cdf, round, ick, sum, scl, cdf_width;
unsigned long rate;
+ struct device *dev = rcar_i2c_priv_to_dev(priv);
+
+ /* Fall back to previously used values if not supplied */
+ t->bus_freq_hz = t->bus_freq_hz ?: 100000;
+ t->scl_fall_ns = t->scl_fall_ns ?: 35;
+ t->scl_rise_ns = t->scl_rise_ns ?: 200;
+ t->scl_int_delay_ns = t->scl_int_delay_ns ?: 50;
switch (priv->devtype) {
case I2C_RCAR_GEN1:
@@ -195,9 +196,9 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
* SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
*
* ick : I2C internal clock < 20 MHz
- * ticf : I2C SCL falling time = 35 ns here
- * tr : I2C SCL rising time = 200 ns here
- * intd : LSI internal delay = 50 ns here
+ * ticf : I2C SCL falling time
+ * tr : I2C SCL rising time
+ * intd : LSI internal delay
* clkp : peripheral_clk
* F[] : integer up-valuation
*/
@@ -213,12 +214,12 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
* it is impossible to calculate large scale
* number on u32. separate it
*
- * F[(ticf + tr + intd) * ick]
- * = F[(35 + 200 + 50)ns * ick]
- * = F[285 * ick / 1000000000]
- * = F[(ick / 1000000) * 285 / 1000]
+ * F[(ticf + tr + intd) * ick] with sum = (ticf + tr + intd)
+ * = F[sum * ick / 1000000000]
+ * = F[(ick / 1000000) * sum / 1000]
*/
- round = (ick + 500000) / 1000000 * 285;
+ sum = t->scl_fall_ns + t->scl_rise_ns + t->scl_int_delay_ns;
+ round = (ick + 500000) / 1000000 * sum;
round = (round + 500) / 1000;
/*
@@ -235,7 +236,7 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
*/
for (scgd = 0; scgd < 0x40; scgd++) {
scl = ick / (20 + (scgd * 8) + round);
- if (scl <= bus_speed)
+ if (scl <= t->bus_freq_hz)
goto scgd_find;
}
dev_err(dev, "it is impossible to calculate best SCL\n");
@@ -243,11 +244,9 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
scgd_find:
dev_dbg(dev, "clk %d/%d(%lu), round %u, CDF:0x%x, SCGD: 0x%x\n",
- scl, bus_speed, clk_get_rate(priv->clk), round, cdf, scgd);
+ scl, t->bus_freq_hz, clk_get_rate(priv->clk), round, cdf, scgd);
- /*
- * keep icccr value
- */
+ /* keep icccr value */
priv->icccr = scgd << cdf_width | cdf;
return 0;
@@ -257,33 +256,44 @@ static void rcar_i2c_prepare_msg(struct rcar_i2c_priv *priv)
{
int read = !!rcar_i2c_is_recv(priv);
+ priv->pos = 0;
+ if (priv->msgs_left == 1)
+ priv->flags |= ID_LAST_MSG;
+
rcar_i2c_write(priv, ICMAR, (priv->msg->addr << 1) | read);
- rcar_i2c_write(priv, ICMSR, 0);
- rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
+ /*
+ * We don't have a testcase but the HW engineers say that the write order
+ * of ICMSR and ICMCR depends on whether we issue START or REP_START. Since
+ * it didn't cause a drawback for me, let's rather be safe than sorry.
+ */
+ if (priv->flags & ID_FIRST_MSG) {
+ rcar_i2c_write(priv, ICMSR, 0);
+ rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
+ } else {
+ rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
+ rcar_i2c_write(priv, ICMSR, 0);
+ }
rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND);
}
+static void rcar_i2c_next_msg(struct rcar_i2c_priv *priv)
+{
+ priv->msg++;
+ priv->msgs_left--;
+ priv->flags &= ID_P_MASK;
+ rcar_i2c_prepare_msg(priv);
+}
+
/*
* interrupt functions
*/
-static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
+static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
{
struct i2c_msg *msg = priv->msg;
- /*
- * FIXME
- * sometimes, unknown interrupt happened.
- * Do nothing
- */
+ /* FIXME: sometimes, unknown interrupt happened. Do nothing */
if (!(msr & MDE))
- return 0;
-
- /*
- * If address transfer phase finished,
- * goto data phase.
- */
- if (msr & MAT)
- rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_DATA);
+ return;
if (priv->pos < msg->len) {
/*
@@ -305,67 +315,50 @@ static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
* [ICRXTX] -> [SHIFT] -> [I2C bus]
*/
- if (priv->flags & ID_LAST_MSG)
+ if (priv->flags & ID_LAST_MSG) {
/*
* If current msg is the _LAST_ msg,
* prepare stop condition here.
* ID_DONE will be set on STOP irq.
*/
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP);
- else
- /*
- * If current msg is _NOT_ last msg,
- * it doesn't call stop phase.
- * thus, there is no STOP irq.
- * return ID_DONE here.
- */
- return ID_DONE;
+ } else {
+ rcar_i2c_next_msg(priv);
+ return;
+ }
}
rcar_i2c_write(priv, ICMSR, RCAR_IRQ_ACK_SEND);
-
- return 0;
}
-static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
+static void rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
{
struct i2c_msg *msg = priv->msg;
- /*
- * FIXME
- * sometimes, unknown interrupt happened.
- * Do nothing
- */
+ /* FIXME: sometimes, unknown interrupt happened. Do nothing */
if (!(msr & MDR))
- return 0;
+ return;
if (msr & MAT) {
- /*
- * Address transfer phase finished,
- * but, there is no data at this point.
- * Do nothing.
- */
+ /* Address transfer phase finished, but no data at this point. */
} else if (priv->pos < msg->len) {
- /*
- * get received data
- */
+ /* get received data */
msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX);
priv->pos++;
}
/*
- * If next received data is the _LAST_,
- * go to STOP phase,
- * otherwise, go to DATA phase.
+ * If next received data is the _LAST_, go to STOP phase. Might be
+ * overwritten by REP START when setting up a new msg. Not elegant
+ * but the only stable sequence for REP START I have found so far.
*/
if (priv->pos + 1 >= msg->len)
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP);
- else
- rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_DATA);
-
- rcar_i2c_write(priv, ICMSR, RCAR_IRQ_ACK_RECV);
- return 0;
+ if (priv->pos == msg->len && !(priv->flags & ID_LAST_MSG))
+ rcar_i2c_next_msg(priv);
+ else
+ rcar_i2c_write(priv, ICMSR, RCAR_IRQ_ACK_RECV);
}
static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
@@ -426,62 +419,57 @@ static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
{
struct rcar_i2c_priv *priv = ptr;
- irqreturn_t result = IRQ_HANDLED;
- u32 msr;
-
- /*-------------- spin lock -----------------*/
- spin_lock(&priv->lock);
+ u32 msr, val;
- if (rcar_i2c_slave_irq(priv))
- goto exit;
+ /* Clear START or STOP as soon as we can */
+ val = rcar_i2c_read(priv, ICMCR);
+ rcar_i2c_write(priv, ICMCR, val & RCAR_BUS_MASK_DATA);
msr = rcar_i2c_read(priv, ICMSR);
/* Only handle interrupts that are currently enabled */
msr &= rcar_i2c_read(priv, ICMIER);
if (!msr) {
- result = IRQ_NONE;
- goto exit;
+ if (rcar_i2c_slave_irq(priv))
+ return IRQ_HANDLED;
+
+ return IRQ_NONE;
}
/* Arbitration lost */
if (msr & MAL) {
- rcar_i2c_flags_set(priv, (ID_DONE | ID_ARBLOST));
+ priv->flags |= ID_DONE | ID_ARBLOST;
goto out;
}
/* Nack */
if (msr & MNR) {
- /* go to stop phase */
- rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP);
+ /* HW automatically sends STOP after received NACK */
rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP);
- rcar_i2c_flags_set(priv, ID_NACK);
+ priv->flags |= ID_NACK;
goto out;
}
/* Stop */
if (msr & MST) {
- rcar_i2c_flags_set(priv, ID_DONE);
+ priv->msgs_left--; /* The last message also made it */
+ priv->flags |= ID_DONE;
goto out;
}
if (rcar_i2c_is_recv(priv))
- rcar_i2c_flags_set(priv, rcar_i2c_irq_recv(priv, msr));
+ rcar_i2c_irq_recv(priv, msr);
else
- rcar_i2c_flags_set(priv, rcar_i2c_irq_send(priv, msr));
+ rcar_i2c_irq_send(priv, msr);
out:
- if (rcar_i2c_flags_has(priv, ID_DONE)) {
+ if (priv->flags & ID_DONE) {
rcar_i2c_write(priv, ICMIER, 0);
rcar_i2c_write(priv, ICMSR, 0);
wake_up(&priv->wait);
}
-exit:
- spin_unlock(&priv->lock);
- /*-------------- spin unlock -----------------*/
-
- return result;
+ return IRQ_HANDLED;
}
static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
@@ -490,22 +478,11 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
{
struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
struct device *dev = rcar_i2c_priv_to_dev(priv);
- unsigned long flags;
int i, ret;
- long timeout;
+ long time_left;
pm_runtime_get_sync(dev);
- /*-------------- spin lock -----------------*/
- spin_lock_irqsave(&priv->lock, flags);
-
- rcar_i2c_init(priv);
- /* start clock */
- rcar_i2c_write(priv, ICCCR, priv->icccr);
-
- spin_unlock_irqrestore(&priv->lock, flags);
- /*-------------- spin unlock -----------------*/
-
ret = rcar_i2c_bus_barrier(priv);
if (ret < 0)
goto out;
@@ -514,48 +491,27 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
/* This HW can't send STOP after address phase */
if (msgs[i].len == 0) {
ret = -EOPNOTSUPP;
- break;
- }
-
- /*-------------- spin lock -----------------*/
- spin_lock_irqsave(&priv->lock, flags);
-
- /* init each data */
- priv->msg = &msgs[i];
- priv->pos = 0;
- priv->flags = 0;
- if (i == num - 1)
- rcar_i2c_flags_set(priv, ID_LAST_MSG);
-
- rcar_i2c_prepare_msg(priv);
-
- spin_unlock_irqrestore(&priv->lock, flags);
- /*-------------- spin unlock -----------------*/
-
- timeout = wait_event_timeout(priv->wait,
- rcar_i2c_flags_has(priv, ID_DONE),
- adap->timeout);
- if (!timeout) {
- ret = -ETIMEDOUT;
- break;
- }
-
- if (rcar_i2c_flags_has(priv, ID_NACK)) {
- ret = -ENXIO;
- break;
- }
-
- if (rcar_i2c_flags_has(priv, ID_ARBLOST)) {
- ret = -EAGAIN;
- break;
- }
-
- if (rcar_i2c_flags_has(priv, ID_IOERROR)) {
- ret = -EIO;
- break;
+ goto out;
}
+ }
- ret = i + 1; /* The number of transfer */
+ /* init first message */
+ priv->msg = msgs;
+ priv->msgs_left = num;
+ priv->flags = (priv->flags & ID_P_MASK) | ID_FIRST_MSG;
+ rcar_i2c_prepare_msg(priv);
+
+ time_left = wait_event_timeout(priv->wait, priv->flags & ID_DONE,
+ num * adap->timeout);
+ if (!time_left) {
+ rcar_i2c_init(priv);
+ ret = -ETIMEDOUT;
+ } else if (priv->flags & ID_NACK) {
+ ret = -ENXIO;
+ } else if (priv->flags & ID_ARBLOST) {
+ ret = -EAGAIN;
+ } else {
+ ret = num - priv->msgs_left; /* The number of transfer */
}
out:
pm_runtime_put(dev);
@@ -576,7 +532,7 @@ static int rcar_reg_slave(struct i2c_client *slave)
if (slave->flags & I2C_CLIENT_TEN)
return -EAFNOSUPPORT;
- pm_runtime_forbid(rcar_i2c_priv_to_dev(priv));
+ pm_runtime_get_sync(rcar_i2c_priv_to_dev(priv));
priv->slave = slave;
rcar_i2c_write(priv, ICSAR, slave->addr);
@@ -598,7 +554,7 @@ static int rcar_unreg_slave(struct i2c_client *slave)
priv->slave = NULL;
- pm_runtime_allow(rcar_i2c_priv_to_dev(priv));
+ pm_runtime_put(rcar_i2c_priv_to_dev(priv));
return 0;
}
@@ -637,7 +593,7 @@ static int rcar_i2c_probe(struct platform_device *pdev)
struct i2c_adapter *adap;
struct resource *res;
struct device *dev = &pdev->dev;
- u32 bus_speed;
+ struct i2c_timings i2c_t;
int irq, ret;
priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL);
@@ -650,23 +606,13 @@ static int rcar_i2c_probe(struct platform_device *pdev)
return PTR_ERR(priv->clk);
}
- bus_speed = 100000; /* default 100 kHz */
- of_property_read_u32(dev->of_node, "clock-frequency", &bus_speed);
-
- priv->devtype = (enum rcar_i2c_type)of_match_device(rcar_i2c_dt_ids, dev)->data;
-
- ret = rcar_i2c_clock_calculate(priv, bus_speed, dev);
- if (ret < 0)
- return ret;
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->io = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->io))
return PTR_ERR(priv->io);
- irq = platform_get_irq(pdev, 0);
+ priv->devtype = (enum rcar_i2c_type)of_match_device(rcar_i2c_dt_ids, dev)->data;
init_waitqueue_head(&priv->wait);
- spin_lock_init(&priv->lock);
adap = &priv->adap;
adap->nr = pdev->id;
@@ -678,26 +624,47 @@ static int rcar_i2c_probe(struct platform_device *pdev)
i2c_set_adapdata(adap, priv);
strlcpy(adap->name, pdev->name, sizeof(adap->name));
- ret = devm_request_irq(dev, irq, rcar_i2c_irq, 0,
- dev_name(dev), priv);
+ i2c_parse_fw_timings(dev, &i2c_t, false);
+
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+ ret = rcar_i2c_clock_calculate(priv, &i2c_t);
+ if (ret < 0)
+ goto out_pm_put;
+
+ rcar_i2c_init(priv);
+
+ /* Don't suspend when multi-master to keep arbitration working */
+ if (of_property_read_bool(dev->of_node, "multi-master"))
+ priv->flags |= ID_P_PM_BLOCKED;
+ else
+ pm_runtime_put(dev);
+
+
+ irq = platform_get_irq(pdev, 0);
+ ret = devm_request_irq(dev, irq, rcar_i2c_irq, 0, dev_name(dev), priv);
if (ret < 0) {
dev_err(dev, "cannot get irq %d\n", irq);
- return ret;
+ goto out_pm_disable;
}
- pm_runtime_enable(dev);
platform_set_drvdata(pdev, priv);
ret = i2c_add_numbered_adapter(adap);
if (ret < 0) {
dev_err(dev, "reg adap failed: %d\n", ret);
- pm_runtime_disable(dev);
- return ret;
+ goto out_pm_disable;
}
dev_info(dev, "probed\n");
return 0;
+
+ out_pm_put:
+ pm_runtime_put(dev);
+ out_pm_disable:
+ pm_runtime_disable(dev);
+ return ret;
}
static int rcar_i2c_remove(struct platform_device *pdev)
@@ -706,6 +673,8 @@ static int rcar_i2c_remove(struct platform_device *pdev)
struct device *dev = &pdev->dev;
i2c_del_adapter(&priv->adap);
+ if (priv->flags & ID_P_PM_BLOCKED)
+ pm_runtime_put(dev);
pm_runtime_disable(dev);
return 0;
diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c
index c1935ebd6a9c..9096d17beb5b 100644
--- a/drivers/i2c/busses/i2c-rk3x.c
+++ b/drivers/i2c/busses/i2c-rk3x.c
@@ -908,7 +908,7 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
&i2c->scl_fall_ns))
i2c->scl_fall_ns = 300;
if (of_property_read_u32(pdev->dev.of_node, "i2c-sda-falling-time-ns",
- &i2c->scl_fall_ns))
+ &i2c->sda_fall_ns))
i2c->sda_fall_ns = i2c->scl_fall_ns;
strlcpy(i2c->adap.name, "rk3x-i2c", sizeof(i2c->adap.name));
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index 5df819610d52..362a6de54833 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -784,7 +784,6 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
int retry;
int ret;
- pm_runtime_get_sync(&adap->dev);
ret = clk_enable(i2c->clk);
if (ret)
return ret;
@@ -795,7 +794,6 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
if (ret != -EAGAIN) {
clk_disable(i2c->clk);
- pm_runtime_put(&adap->dev);
return ret;
}
@@ -805,7 +803,6 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
}
clk_disable(i2c->clk);
- pm_runtime_put(&adap->dev);
return -EREMOTEIO;
}
@@ -1256,8 +1253,6 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
return ret;
}
- pm_runtime_enable(&i2c->adap.dev);
-
dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
return 0;
}
@@ -1273,7 +1268,6 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev)
clk_unprepare(i2c->clk);
- pm_runtime_disable(&i2c->adap.dev);
pm_runtime_disable(&pdev->dev);
s3c24xx_i2c_deregister_cpufreq(i2c);
diff --git a/drivers/i2c/busses/i2c-st.c b/drivers/i2c/busses/i2c-st.c
index ea72dca32fdf..6ee77159ac57 100644
--- a/drivers/i2c/busses/i2c-st.c
+++ b/drivers/i2c/busses/i2c-st.c
@@ -708,8 +708,7 @@ static int st_i2c_xfer(struct i2c_adapter *i2c_adap,
#ifdef CONFIG_PM_SLEEP
static int st_i2c_suspend(struct device *dev)
{
- struct platform_device *pdev =
- container_of(dev, struct platform_device, dev);
+ struct platform_device *pdev = to_platform_device(dev);
struct st_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
if (i2c_dev->busy)
@@ -822,7 +821,7 @@ static int st_i2c_probe(struct platform_device *pdev)
adap = &i2c_dev->adap;
i2c_set_adapdata(adap, i2c_dev);
- snprintf(adap->name, sizeof(adap->name), "ST I2C(0x%pa)", &res->start);
+ snprintf(adap->name, sizeof(adap->name), "ST I2C(%pa)", &res->start);
adap->owner = THIS_MODULE;
adap->timeout = 2 * HZ;
adap->retries = 0;
diff --git a/drivers/i2c/busses/i2c-taos-evm.c b/drivers/i2c/busses/i2c-taos-evm.c
index 4c7fc2d47014..210ca82f8aa0 100644
--- a/drivers/i2c/busses/i2c-taos-evm.c
+++ b/drivers/i2c/busses/i2c-taos-evm.c
@@ -130,7 +130,13 @@ static int taos_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
return 0;
} else {
if (p[0] == 'x') {
- data->byte = simple_strtol(p + 1, NULL, 16);
+ /*
+ * Voluntarily dropping error code of kstrtou8 since all
+ * error code that it could return are invalid according
+ * to Documentation/i2c/fault-codes.
+ */
+ if (kstrtou8(p + 1, 16, &data->byte))
+ return -EPROTO;
return 0;
}
}
diff --git a/drivers/i2c/busses/i2c-uniphier-f.c b/drivers/i2c/busses/i2c-uniphier-f.c
index e8d03bcfe3e0..f3e5ff8522f0 100644
--- a/drivers/i2c/busses/i2c-uniphier-f.c
+++ b/drivers/i2c/busses/i2c-uniphier-f.c
@@ -466,6 +466,11 @@ static int uniphier_fi2c_clk_init(struct device *dev,
if (of_property_read_u32(np, "clock-frequency", &bus_speed))
bus_speed = UNIPHIER_FI2C_DEFAULT_SPEED;
+ if (!bus_speed) {
+ dev_err(dev, "clock-freqyency should not be zero\n");
+ return -EINVAL;
+ }
+
if (bus_speed > UNIPHIER_FI2C_MAX_SPEED)
bus_speed = UNIPHIER_FI2C_MAX_SPEED;
@@ -481,6 +486,10 @@ static int uniphier_fi2c_clk_init(struct device *dev,
return ret;
clk_rate = clk_get_rate(priv->clk);
+ if (!clk_rate) {
+ dev_err(dev, "input clock rate should not be zero\n");
+ return -EINVAL;
+ }
uniphier_fi2c_reset(priv);
@@ -531,7 +540,7 @@ static int uniphier_fi2c_probe(struct platform_device *pdev)
ret = uniphier_fi2c_clk_init(dev, priv);
if (ret)
- return ret;
+ goto err;
ret = devm_request_irq(dev, irq, uniphier_fi2c_interrupt, 0,
pdev->name, priv);
diff --git a/drivers/i2c/busses/i2c-uniphier.c b/drivers/i2c/busses/i2c-uniphier.c
index e3c3861c3325..1f4f3f53819c 100644
--- a/drivers/i2c/busses/i2c-uniphier.c
+++ b/drivers/i2c/busses/i2c-uniphier.c
@@ -327,6 +327,11 @@ static int uniphier_i2c_clk_init(struct device *dev,
if (of_property_read_u32(np, "clock-frequency", &bus_speed))
bus_speed = UNIPHIER_I2C_DEFAULT_SPEED;
+ if (!bus_speed) {
+ dev_err(dev, "clock-freqyency should not be zero\n");
+ return -EINVAL;
+ }
+
if (bus_speed > UNIPHIER_I2C_MAX_SPEED)
bus_speed = UNIPHIER_I2C_MAX_SPEED;
@@ -342,6 +347,10 @@ static int uniphier_i2c_clk_init(struct device *dev,
return ret;
clk_rate = clk_get_rate(priv->clk);
+ if (!clk_rate) {
+ dev_err(dev, "input clock rate should not be zero\n");
+ return -EINVAL;
+ }
uniphier_i2c_reset(priv, true);
@@ -388,7 +397,7 @@ static int uniphier_i2c_probe(struct platform_device *pdev)
ret = uniphier_i2c_clk_init(dev, priv);
if (ret)
- return ret;
+ goto err;
ret = devm_request_irq(dev, irq, uniphier_i2c_interrupt, 0, pdev->name,
priv);
diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c
index 0b20449e48cf..6efd20095d5d 100644
--- a/drivers/i2c/busses/i2c-xiic.c
+++ b/drivers/i2c/busses/i2c-xiic.c
@@ -70,7 +70,7 @@ struct xiic_i2c {
wait_queue_head_t wait;
struct i2c_adapter adap;
struct i2c_msg *tx_msg;
- spinlock_t lock;
+ struct mutex lock;
unsigned int tx_pos;
unsigned int nmsgs;
enum xilinx_i2c_state state;
@@ -369,7 +369,7 @@ static irqreturn_t xiic_process(int irq, void *dev_id)
* To find which interrupts are pending; AND interrupts pending with
* interrupts masked.
*/
- spin_lock(&i2c->lock);
+ mutex_lock(&i2c->lock);
isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET);
ier = xiic_getreg32(i2c, XIIC_IIER_OFFSET);
pend = isr & ier;
@@ -497,7 +497,7 @@ out:
dev_dbg(i2c->adap.dev.parent, "%s clr: 0x%x\n", __func__, clr);
xiic_setreg32(i2c, XIIC_IISR_OFFSET, clr);
- spin_unlock(&i2c->lock);
+ mutex_unlock(&i2c->lock);
return IRQ_HANDLED;
}
@@ -662,10 +662,10 @@ static void __xiic_start_xfer(struct xiic_i2c *i2c)
static void xiic_start_xfer(struct xiic_i2c *i2c)
{
- spin_lock(&i2c->lock);
+ mutex_lock(&i2c->lock);
xiic_reinit(i2c);
__xiic_start_xfer(i2c);
- spin_unlock(&i2c->lock);
+ mutex_unlock(&i2c->lock);
}
static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
@@ -745,7 +745,7 @@ static int xiic_i2c_probe(struct platform_device *pdev)
i2c->adap.dev.parent = &pdev->dev;
i2c->adap.dev.of_node = pdev->dev.of_node;
- spin_lock_init(&i2c->lock);
+ mutex_init(&i2c->lock);
init_waitqueue_head(&i2c->wait);
ret = devm_request_threaded_irq(&pdev->dev, irq, xiic_isr,
diff --git a/drivers/i2c/busses/i2c-xlr.c b/drivers/i2c/busses/i2c-xlr.c
index 8b36bcfd952d..613c3a4f2c51 100644
--- a/drivers/i2c/busses/i2c-xlr.c
+++ b/drivers/i2c/busses/i2c-xlr.c
@@ -17,6 +17,10 @@
#include <linux/i2c.h>
#include <linux/io.h>
#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
/* XLR I2C REGISTERS */
#define XLR_I2C_CFG 0x00
@@ -30,6 +34,10 @@
#define XLR_I2C_BYTECNT 0x08
#define XLR_I2C_HDSTATIM 0x09
+/* Sigma Designs additional registers */
+#define XLR_I2C_INT_EN 0x09
+#define XLR_I2C_INT_STAT 0x0a
+
/* XLR I2C REGISTERS FLAGS */
#define XLR_I2C_BUS_BUSY 0x01
#define XLR_I2C_SDOEMPTY 0x02
@@ -63,11 +71,98 @@ static inline u32 xlr_i2c_rdreg(u32 __iomem *base, unsigned int reg)
return __raw_readl(base + reg);
}
+#define XLR_I2C_FLAG_IRQ 1
+
+struct xlr_i2c_config {
+ u32 flags; /* optional feature support */
+ u32 status_busy; /* value of STATUS[0] when busy */
+ u32 cfg_extra; /* extra CFG bits to set */
+};
+
struct xlr_i2c_private {
struct i2c_adapter adap;
u32 __iomem *iobase;
+ int irq;
+ int pos;
+ struct i2c_msg *msg;
+ const struct xlr_i2c_config *cfg;
+ wait_queue_head_t wait;
+ struct clk *clk;
};
+static int xlr_i2c_busy(struct xlr_i2c_private *priv, u32 status)
+{
+ return (status & XLR_I2C_BUS_BUSY) == priv->cfg->status_busy;
+}
+
+static int xlr_i2c_idle(struct xlr_i2c_private *priv)
+{
+ return !xlr_i2c_busy(priv, xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS));
+}
+
+static int xlr_i2c_wait(struct xlr_i2c_private *priv, unsigned long timeout)
+{
+ int status;
+ int t;
+
+ t = wait_event_timeout(priv->wait, xlr_i2c_idle(priv),
+ msecs_to_jiffies(timeout));
+ if (!t)
+ return -ETIMEDOUT;
+
+ status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
+
+ return status & XLR_I2C_ACK_ERR ? -EIO : 0;
+}
+
+static void xlr_i2c_tx_irq(struct xlr_i2c_private *priv, u32 status)
+{
+ struct i2c_msg *msg = priv->msg;
+
+ if (status & XLR_I2C_SDOEMPTY)
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT,
+ msg->buf[priv->pos++]);
+}
+
+static void xlr_i2c_rx_irq(struct xlr_i2c_private *priv, u32 status)
+{
+ struct i2c_msg *msg = priv->msg;
+
+ if (status & XLR_I2C_RXRDY)
+ msg->buf[priv->pos++] =
+ xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
+}
+
+static irqreturn_t xlr_i2c_irq(int irq, void *dev_id)
+{
+ struct xlr_i2c_private *priv = dev_id;
+ struct i2c_msg *msg = priv->msg;
+ u32 int_stat, status;
+
+ int_stat = xlr_i2c_rdreg(priv->iobase, XLR_I2C_INT_STAT);
+ if (!int_stat)
+ return IRQ_NONE;
+
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_STAT, int_stat);
+
+ if (!msg)
+ return IRQ_HANDLED;
+
+ status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
+
+ if (priv->pos < msg->len) {
+ if (msg->flags & I2C_M_RD)
+ xlr_i2c_rx_irq(priv, status);
+ else
+ xlr_i2c_tx_irq(priv, status);
+ }
+
+ if (!xlr_i2c_busy(priv, status))
+ wake_up(&priv->wait);
+
+ return IRQ_HANDLED;
+}
+
static int xlr_i2c_tx(struct xlr_i2c_private *priv, u16 len,
u8 *buf, u16 addr)
{
@@ -75,37 +170,48 @@ static int xlr_i2c_tx(struct xlr_i2c_private *priv, u16 len,
unsigned long timeout, stoptime, checktime;
u32 i2c_status;
int pos, timedout;
- u8 offset, byte;
+ u8 offset;
+ u32 xfer;
+
+ if (!len)
+ return -EOPNOTSUPP;
offset = buf[0];
xlr_i2c_wreg(priv->iobase, XLR_I2C_ADDR, offset);
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
- xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG, XLR_I2C_CFG_ADDR);
- xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
+ XLR_I2C_CFG_ADDR | priv->cfg->cfg_extra);
timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
stoptime = jiffies + timeout;
timedout = 0;
- pos = 1;
-retry:
+
if (len == 1) {
- xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR,
- XLR_I2C_STARTXFR_ND);
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
+ xfer = XLR_I2C_STARTXFR_ND;
+ pos = 1;
} else {
- xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[pos]);
- xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR,
- XLR_I2C_STARTXFR_WR);
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 2);
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[1]);
+ xfer = XLR_I2C_STARTXFR_WR;
+ pos = 2;
}
+ priv->pos = pos;
+
+retry:
+ /* retry can only happen on the first byte */
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, xfer);
+
+ if (priv->irq > 0)
+ return xlr_i2c_wait(priv, XLR_I2C_TIMEOUT * len);
+
while (!timedout) {
checktime = jiffies;
i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
- if (i2c_status & XLR_I2C_SDOEMPTY) {
- pos++;
- /* need to do a empty dataout after the last byte */
- byte = (pos < len) ? buf[pos] : 0;
- xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, byte);
+ if ((i2c_status & XLR_I2C_SDOEMPTY) && pos < len) {
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[pos++]);
/* reset timeout on successful xmit */
stoptime = jiffies + timeout;
@@ -121,7 +227,7 @@ retry:
if (i2c_status & XLR_I2C_ACK_ERR)
return -EIO;
- if ((i2c_status & XLR_I2C_BUS_BUSY) == 0 && pos >= len)
+ if (!xlr_i2c_busy(priv, i2c_status) && pos >= len)
return 0;
}
dev_err(&adap->dev, "I2C transmit timeout\n");
@@ -134,12 +240,17 @@ static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 len, u8 *buf, u16 addr)
u32 i2c_status;
unsigned long timeout, stoptime, checktime;
int nbytes, timedout;
- u8 byte;
- xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG, XLR_I2C_CFG_NOADDR);
- xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len);
+ if (!len)
+ return -EOPNOTSUPP;
+
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
+ XLR_I2C_CFG_NOADDR | priv->cfg->cfg_extra);
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
+ priv->pos = 0;
+
timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
stoptime = jiffies + timeout;
timedout = 0;
@@ -147,18 +258,18 @@ static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 len, u8 *buf, u16 addr)
retry:
xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, XLR_I2C_STARTXFR_RD);
+ if (priv->irq > 0)
+ return xlr_i2c_wait(priv, XLR_I2C_TIMEOUT * len);
+
while (!timedout) {
checktime = jiffies;
i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
if (i2c_status & XLR_I2C_RXRDY) {
- if (nbytes > len)
+ if (nbytes >= len)
return -EIO; /* should not happen */
- /* we need to do a dummy datain when nbytes == len */
- byte = xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
- if (nbytes < len)
- buf[nbytes] = byte;
- nbytes++;
+ buf[nbytes++] =
+ xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
/* reset timeout on successful read */
stoptime = jiffies + timeout;
@@ -174,7 +285,7 @@ retry:
if (i2c_status & XLR_I2C_ACK_ERR)
return -EIO;
- if ((i2c_status & XLR_I2C_BUS_BUSY) == 0)
+ if (!xlr_i2c_busy(priv, i2c_status))
return 0;
}
@@ -190,8 +301,17 @@ static int xlr_i2c_xfer(struct i2c_adapter *adap,
int ret = 0;
struct xlr_i2c_private *priv = i2c_get_adapdata(adap);
+ ret = clk_enable(priv->clk);
+ if (ret)
+ return ret;
+
+ if (priv->irq)
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_EN, 0xf);
+
+
for (i = 0; ret == 0 && i < num; i++) {
msg = &msgs[i];
+ priv->msg = msg;
if (msg->flags & I2C_M_RD)
ret = xlr_i2c_rx(priv, msg->len, &msg->buf[0],
msg->addr);
@@ -200,13 +320,19 @@ static int xlr_i2c_xfer(struct i2c_adapter *adap,
msg->addr);
}
+ if (priv->irq)
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_EN, 0);
+
+ clk_disable(priv->clk);
+ priv->msg = NULL;
+
return (ret != 0) ? ret : num;
}
static u32 xlr_func(struct i2c_adapter *adap)
{
/* Emulate SMBUS over I2C */
- return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
+ return (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | I2C_FUNC_I2C;
}
static struct i2c_algorithm xlr_i2c_algo = {
@@ -214,22 +340,89 @@ static struct i2c_algorithm xlr_i2c_algo = {
.functionality = xlr_func,
};
+static const struct xlr_i2c_config xlr_i2c_config_default = {
+ .status_busy = XLR_I2C_BUS_BUSY,
+ .cfg_extra = 0,
+};
+
+static const struct xlr_i2c_config xlr_i2c_config_tangox = {
+ .flags = XLR_I2C_FLAG_IRQ,
+ .status_busy = 0,
+ .cfg_extra = 1 << 8,
+};
+
+static const struct of_device_id xlr_i2c_dt_ids[] = {
+ {
+ .compatible = "sigma,smp8642-i2c",
+ .data = &xlr_i2c_config_tangox,
+ },
+ { }
+};
+
static int xlr_i2c_probe(struct platform_device *pdev)
{
+ const struct of_device_id *match;
struct xlr_i2c_private *priv;
struct resource *res;
+ struct clk *clk;
+ unsigned long clk_rate;
+ unsigned long clk_div;
+ u32 busfreq;
+ int irq;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
+ match = of_match_device(xlr_i2c_dt_ids, &pdev->dev);
+ if (match)
+ priv->cfg = match->data;
+ else
+ priv->cfg = &xlr_i2c_config_default;
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->iobase = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->iobase))
return PTR_ERR(priv->iobase);
+ irq = platform_get_irq(pdev, 0);
+
+ if (irq > 0 && (priv->cfg->flags & XLR_I2C_FLAG_IRQ)) {
+ priv->irq = irq;
+
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_EN, 0);
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_STAT, 0xf);
+
+ ret = devm_request_irq(&pdev->dev, priv->irq, xlr_i2c_irq,
+ IRQF_SHARED, dev_name(&pdev->dev),
+ priv);
+ if (ret)
+ return ret;
+
+ init_waitqueue_head(&priv->wait);
+ }
+
+ if (of_property_read_u32(pdev->dev.of_node, "clock-frequency",
+ &busfreq))
+ busfreq = 100000;
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (!IS_ERR(clk)) {
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ return ret;
+
+ clk_rate = clk_get_rate(clk);
+ clk_div = DIV_ROUND_UP(clk_rate, 2 * busfreq);
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_CLKDIV, clk_div);
+
+ clk_disable(clk);
+ priv->clk = clk;
+ }
+
priv->adap.dev.parent = &pdev->dev;
+ priv->adap.dev.of_node = pdev->dev.of_node;
priv->adap.owner = THIS_MODULE;
priv->adap.algo_data = priv;
priv->adap.algo = &xlr_i2c_algo;
@@ -255,6 +448,8 @@ static int xlr_i2c_remove(struct platform_device *pdev)
priv = platform_get_drvdata(pdev);
i2c_del_adapter(&priv->adap);
+ clk_unprepare(priv->clk);
+
return 0;
}
@@ -263,6 +458,7 @@ static struct platform_driver xlr_i2c_driver = {
.remove = xlr_i2c_remove,
.driver = {
.name = "xlr-i2cbus",
+ .of_match_table = xlr_i2c_dt_ids,
},
};
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index ba8eb087f224..ffe715d346d8 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -53,6 +53,7 @@
#include <linux/jump_label.h>
#include <asm/uaccess.h>
#include <linux/err.h>
+#include <linux/property.h>
#include "i2c-core.h"
@@ -1563,6 +1564,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
pm_runtime_no_callbacks(&adap->dev);
+ pm_runtime_enable(&adap->dev);
#ifdef CONFIG_I2C_COMPAT
res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
@@ -1817,6 +1819,8 @@ void i2c_del_adapter(struct i2c_adapter *adap)
/* device name is gone after device_unregister */
dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name);
+ pm_runtime_disable(&adap->dev);
+
/* wait until all references to the device are gone
*
* FIXME: This is old code and should ideally be replaced by an
@@ -1839,6 +1843,58 @@ void i2c_del_adapter(struct i2c_adapter *adap)
}
EXPORT_SYMBOL(i2c_del_adapter);
+/**
+ * i2c_parse_fw_timings - get I2C related timing parameters from firmware
+ * @dev: The device to scan for I2C timing properties
+ * @t: the i2c_timings struct to be filled with values
+ * @use_defaults: bool to use sane defaults derived from the I2C specification
+ * when properties are not found, otherwise use 0
+ *
+ * Scan the device for the generic I2C properties describing timing parameters
+ * for the signal and fill the given struct with the results. If a property was
+ * not found and use_defaults was true, then maximum timings are assumed which
+ * are derived from the I2C specification. If use_defaults is not used, the
+ * results will be 0, so drivers can apply their own defaults later. The latter
+ * is mainly intended for avoiding regressions of existing drivers which want
+ * to switch to this function. New drivers almost always should use the defaults.
+ */
+
+void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_defaults)
+{
+ int ret;
+
+ memset(t, 0, sizeof(*t));
+
+ ret = device_property_read_u32(dev, "clock-frequency", &t->bus_freq_hz);
+ if (ret && use_defaults)
+ t->bus_freq_hz = 100000;
+
+ ret = device_property_read_u32(dev, "i2c-scl-rising-time-ns", &t->scl_rise_ns);
+ if (ret && use_defaults) {
+ if (t->bus_freq_hz <= 100000)
+ t->scl_rise_ns = 1000;
+ else if (t->bus_freq_hz <= 400000)
+ t->scl_rise_ns = 300;
+ else
+ t->scl_rise_ns = 120;
+ }
+
+ ret = device_property_read_u32(dev, "i2c-scl-falling-time-ns", &t->scl_fall_ns);
+ if (ret && use_defaults) {
+ if (t->bus_freq_hz <= 400000)
+ t->scl_fall_ns = 300;
+ else
+ t->scl_fall_ns = 120;
+ }
+
+ device_property_read_u32(dev, "i2c-scl-internal-delay-ns", &t->scl_int_delay_ns);
+
+ ret = device_property_read_u32(dev, "i2c-sda-falling-time-ns", &t->sda_fall_ns);
+ if (ret && use_defaults)
+ t->sda_fall_ns = t->scl_fall_ns;
+}
+EXPORT_SYMBOL_GPL(i2c_parse_fw_timings);
+
/* ------------------------------------------------------------------------- */
int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *))
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 66792e707d74..505e921f0b19 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -22,6 +22,14 @@ if IIO_BUFFER
source "drivers/iio/buffer/Kconfig"
endif # IIO_BUFFER
+config IIO_CONFIGFS
+ tristate "Enable IIO configuration via configfs"
+ select CONFIGFS_FS
+ help
+ This allows configuring various IIO bits through configfs
+ (e.g. software triggers). For more info see
+ Documentation/iio/iio_configfs.txt.
+
config IIO_TRIGGER
bool "Enable triggered sampling support"
help
@@ -38,6 +46,14 @@ config IIO_CONSUMERS_PER_TRIGGER
This value controls the maximum number of consumers that a
given trigger may handle. Default is 2.
+config IIO_SW_TRIGGER
+ tristate "Enable software triggers support"
+ select IIO_CONFIGFS
+ help
+ Provides IIO core support for software triggers. A software
+ trigger can be created via configfs or directly by a driver
+ using the API provided.
+
config IIO_TRIGGERED_EVENT
tristate
select IIO_TRIGGER
@@ -50,8 +66,10 @@ source "drivers/iio/amplifiers/Kconfig"
source "drivers/iio/chemical/Kconfig"
source "drivers/iio/common/Kconfig"
source "drivers/iio/dac/Kconfig"
+source "drivers/iio/dummy/Kconfig"
source "drivers/iio/frequency/Kconfig"
source "drivers/iio/gyro/Kconfig"
+source "drivers/iio/health/Kconfig"
source "drivers/iio/humidity/Kconfig"
source "drivers/iio/imu/Kconfig"
source "drivers/iio/light/Kconfig"
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index aeca7269fe44..20f649073462 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -7,6 +7,8 @@ industrialio-y := industrialio-core.o industrialio-event.o inkern.o
industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
+obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
+obj-$(CONFIG_IIO_SW_TRIGGER) += industrialio-sw-trigger.o
obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o
obj-y += accel/
@@ -16,8 +18,10 @@ obj-y += buffer/
obj-y += chemical/
obj-y += common/
obj-y += dac/
+obj-y += dummy/
obj-y += gyro/
obj-y += frequency/
+obj-y += health/
obj-y += humidity/
obj-y += imu/
obj-y += light/
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index 969428dd6329..edc29b173f6c 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -64,7 +64,7 @@ config IIO_ST_ACCEL_3AXIS
help
Say yes here to build support for STMicroelectronics accelerometers:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC,
- LIS331DLH, LSM303DL, LSM303DLM, LSM330.
+ LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12.
This driver can also be built as a module. If so, these modules
will be created:
@@ -107,6 +107,35 @@ config KXCJK1013
To compile this driver as a module, choose M here: the module will
be called kxcjk-1013.
+config MMA7455
+ tristate
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+
+config MMA7455_I2C
+ tristate "Freescale MMA7455L/MMA7456L Accelerometer I2C Driver"
+ depends on I2C
+ select MMA7455
+ select REGMAP_I2C
+ help
+ Say yes here to build support for the Freescale MMA7455L and
+ MMA7456L 3-axis accelerometer.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mma7455_i2c.
+
+config MMA7455_SPI
+ tristate "Freescale MMA7455L/MMA7456L Accelerometer SPI Driver"
+ depends on SPI_MASTER
+ select MMA7455
+ select REGMAP_SPI
+ help
+ Say yes here to build support for the Freescale MMA7455L and
+ MMA7456L 3-axis accelerometer.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mma7455_spi.
+
config MMA8452
tristate "Freescale MMA8452Q and similar Accelerometers Driver"
depends on I2C
@@ -158,6 +187,17 @@ config MXC4005
To compile this driver as a module, choose M. The module will be
called mxc4005.
+config MXC6255
+ tristate "Memsic MXC6255 Orientation Sensing Accelerometer Driver"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say yes here to build support for the Memsic MXC6255 Orientation
+ Sensing Accelerometer Driver.
+
+ To compile this driver as a module, choose M here: the module will be
+ called mxc6255.
+
config STK8312
tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
depends on I2C
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index 7925f166e6e9..71b6794de885 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -10,6 +10,11 @@ obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o
obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
obj-$(CONFIG_KXSD9) += kxsd9.o
+
+obj-$(CONFIG_MMA7455) += mma7455_core.o
+obj-$(CONFIG_MMA7455_I2C) += mma7455_i2c.o
+obj-$(CONFIG_MMA7455_SPI) += mma7455_spi.o
+
obj-$(CONFIG_MMA8452) += mma8452.o
obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o
@@ -17,6 +22,7 @@ obj-$(CONFIG_MMA9551) += mma9551.o
obj-$(CONFIG_MMA9553) += mma9553.o
obj-$(CONFIG_MXC4005) += mxc4005.o
+obj-$(CONFIG_MXC6255) += mxc6255.o
obj-$(CONFIG_STK8312) += stk8312.o
obj-$(CONFIG_STK8BA50) += stk8ba50.o
diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c
index 2d33f1e821db..c73331f7782b 100644
--- a/drivers/iio/accel/bmc150-accel-core.c
+++ b/drivers/iio/accel/bmc150-accel-core.c
@@ -1623,24 +1623,22 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
}
}
- ret = iio_device_register(indio_dev);
- if (ret < 0) {
- dev_err(dev, "Unable to register iio device\n");
- goto err_trigger_unregister;
- }
-
ret = pm_runtime_set_active(dev);
if (ret)
- goto err_iio_unregister;
+ goto err_trigger_unregister;
pm_runtime_enable(dev);
pm_runtime_set_autosuspend_delay(dev, BMC150_AUTO_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(dev, "Unable to register iio device\n");
+ goto err_trigger_unregister;
+ }
+
return 0;
-err_iio_unregister:
- iio_device_unregister(indio_dev);
err_trigger_unregister:
bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1);
err_buffer_cleanup:
@@ -1655,12 +1653,12 @@ int bmc150_accel_core_remove(struct device *dev)
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct bmc150_accel_data *data = iio_priv(indio_dev);
+ iio_device_unregister(indio_dev);
+
pm_runtime_disable(data->dev);
pm_runtime_set_suspended(data->dev);
pm_runtime_put_noidle(data->dev);
- iio_device_unregister(indio_dev);
-
bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1);
iio_triggered_buffer_cleanup(indio_dev);
diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c
index 18c1b06684c1..edec1d099e91 100644
--- a/drivers/iio/accel/kxcjk-1013.c
+++ b/drivers/iio/accel/kxcjk-1013.c
@@ -1264,25 +1264,23 @@ static int kxcjk1013_probe(struct i2c_client *client,
goto err_trigger_unregister;
}
- ret = iio_device_register(indio_dev);
- if (ret < 0) {
- dev_err(&client->dev, "unable to register iio device\n");
- goto err_buffer_cleanup;
- }
-
ret = pm_runtime_set_active(&client->dev);
if (ret)
- goto err_iio_unregister;
+ goto err_buffer_cleanup;
pm_runtime_enable(&client->dev);
pm_runtime_set_autosuspend_delay(&client->dev,
KXCJK1013_SLEEP_DELAY_MS);
pm_runtime_use_autosuspend(&client->dev);
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "unable to register iio device\n");
+ goto err_buffer_cleanup;
+ }
+
return 0;
-err_iio_unregister:
- iio_device_unregister(indio_dev);
err_buffer_cleanup:
if (data->dready_trig)
iio_triggered_buffer_cleanup(indio_dev);
@@ -1302,12 +1300,12 @@ static int kxcjk1013_remove(struct i2c_client *client)
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct kxcjk1013_data *data = iio_priv(indio_dev);
+ iio_device_unregister(indio_dev);
+
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
- iio_device_unregister(indio_dev);
-
if (data->dready_trig) {
iio_triggered_buffer_cleanup(indio_dev);
iio_trigger_unregister(data->dready_trig);
diff --git a/drivers/iio/accel/mma7455.h b/drivers/iio/accel/mma7455.h
new file mode 100644
index 000000000000..2b1152c53d4f
--- /dev/null
+++ b/drivers/iio/accel/mma7455.h
@@ -0,0 +1,19 @@
+/*
+ * IIO accel driver for Freescale MMA7455L 3-axis 10-bit accelerometer
+ * Copyright 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.
+ */
+
+#ifndef __MMA7455_H
+#define __MMA7455_H
+
+extern const struct regmap_config mma7455_core_regmap;
+
+int mma7455_core_probe(struct device *dev, struct regmap *regmap,
+ const char *name);
+int mma7455_core_remove(struct device *dev);
+
+#endif
diff --git a/drivers/iio/accel/mma7455_core.c b/drivers/iio/accel/mma7455_core.c
new file mode 100644
index 000000000000..c633cc2c0789
--- /dev/null
+++ b/drivers/iio/accel/mma7455_core.c
@@ -0,0 +1,311 @@
+/*
+ * IIO accel core driver for Freescale MMA7455L 3-axis 10-bit accelerometer
+ * Copyright 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.
+ *
+ * UNSUPPORTED hardware features:
+ * - 8-bit mode with different scales
+ * - INT1/INT2 interrupts
+ * - Offset calibration
+ * - Events
+ */
+
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "mma7455.h"
+
+#define MMA7455_REG_XOUTL 0x00
+#define MMA7455_REG_XOUTH 0x01
+#define MMA7455_REG_YOUTL 0x02
+#define MMA7455_REG_YOUTH 0x03
+#define MMA7455_REG_ZOUTL 0x04
+#define MMA7455_REG_ZOUTH 0x05
+#define MMA7455_REG_STATUS 0x09
+#define MMA7455_STATUS_DRDY BIT(0)
+#define MMA7455_REG_WHOAMI 0x0f
+#define MMA7455_WHOAMI_ID 0x55
+#define MMA7455_REG_MCTL 0x16
+#define MMA7455_MCTL_MODE_STANDBY 0x00
+#define MMA7455_MCTL_MODE_MEASURE 0x01
+#define MMA7455_REG_CTL1 0x18
+#define MMA7455_CTL1_DFBW_MASK BIT(7)
+#define MMA7455_CTL1_DFBW_125HZ BIT(7)
+#define MMA7455_CTL1_DFBW_62_5HZ 0
+#define MMA7455_REG_TW 0x1e
+
+/*
+ * When MMA7455 is used in 10-bit it has a fullscale of -8g
+ * corresponding to raw value -512. The userspace interface
+ * uses m/s^2 and we declare micro units.
+ * So scale factor is given by:
+ * g * 8 * 1e6 / 512 = 153228.90625, with g = 9.80665
+ */
+#define MMA7455_10BIT_SCALE 153229
+
+struct mma7455_data {
+ struct regmap *regmap;
+ struct device *dev;
+};
+
+static int mma7455_drdy(struct mma7455_data *mma7455)
+{
+ unsigned int reg;
+ int tries = 3;
+ int ret;
+
+ while (tries-- > 0) {
+ ret = regmap_read(mma7455->regmap, MMA7455_REG_STATUS, &reg);
+ if (ret)
+ return ret;
+
+ if (reg & MMA7455_STATUS_DRDY)
+ return 0;
+
+ msleep(20);
+ }
+
+ dev_warn(mma7455->dev, "data not ready\n");
+
+ return -EIO;
+}
+
+static irqreturn_t mma7455_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct mma7455_data *mma7455 = iio_priv(indio_dev);
+ u8 buf[16]; /* 3 x 16-bit channels + padding + ts */
+ int ret;
+
+ ret = mma7455_drdy(mma7455);
+ if (ret)
+ goto done;
+
+ ret = regmap_bulk_read(mma7455->regmap, MMA7455_REG_XOUTL, buf,
+ sizeof(__le16) * 3);
+ if (ret)
+ goto done;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns());
+
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int mma7455_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct mma7455_data *mma7455 = iio_priv(indio_dev);
+ unsigned int reg;
+ __le16 data;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+
+ ret = mma7455_drdy(mma7455);
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_read(mma7455->regmap, chan->address, &data,
+ sizeof(data));
+ if (ret)
+ return ret;
+
+ *val = sign_extend32(le16_to_cpu(data), 9);
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = MMA7455_10BIT_SCALE;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = regmap_read(mma7455->regmap, MMA7455_REG_CTL1, &reg);
+ if (ret)
+ return ret;
+
+ if (reg & MMA7455_CTL1_DFBW_MASK)
+ *val = 250;
+ else
+ *val = 125;
+
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int mma7455_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct mma7455_data *mma7455 = iio_priv(indio_dev);
+ int i;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (val == 250 && val2 == 0)
+ i = MMA7455_CTL1_DFBW_125HZ;
+ else if (val == 125 && val2 == 0)
+ i = MMA7455_CTL1_DFBW_62_5HZ;
+ else
+ return -EINVAL;
+
+ return regmap_update_bits(mma7455->regmap, MMA7455_REG_CTL1,
+ MMA7455_CTL1_DFBW_MASK, i);
+
+ case IIO_CHAN_INFO_SCALE:
+ /* In 10-bit mode there is only one scale available */
+ if (val == 0 && val2 == MMA7455_10BIT_SCALE)
+ return 0;
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static IIO_CONST_ATTR(sampling_frequency_available, "125 250");
+
+static struct attribute *mma7455_attributes[] = {
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group mma7455_group = {
+ .attrs = mma7455_attributes,
+};
+
+static const struct iio_info mma7455_info = {
+ .attrs = &mma7455_group,
+ .read_raw = mma7455_read_raw,
+ .write_raw = mma7455_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+#define MMA7455_CHANNEL(axis, idx) { \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .address = MMA7455_REG_##axis##OUTL,\
+ .channel2 = IIO_MOD_##axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = idx, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 10, \
+ .storagebits = 16, \
+ .endianness = IIO_LE, \
+ }, \
+}
+
+static const struct iio_chan_spec mma7455_channels[] = {
+ MMA7455_CHANNEL(X, 0),
+ MMA7455_CHANNEL(Y, 1),
+ MMA7455_CHANNEL(Z, 2),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const unsigned long mma7455_scan_masks[] = {0x7, 0};
+
+const struct regmap_config mma7455_core_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MMA7455_REG_TW,
+};
+EXPORT_SYMBOL_GPL(mma7455_core_regmap);
+
+int mma7455_core_probe(struct device *dev, struct regmap *regmap,
+ const char *name)
+{
+ struct mma7455_data *mma7455;
+ struct iio_dev *indio_dev;
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(regmap, MMA7455_REG_WHOAMI, &reg);
+ if (ret) {
+ dev_err(dev, "unable to read reg\n");
+ return ret;
+ }
+
+ if (reg != MMA7455_WHOAMI_ID) {
+ dev_err(dev, "device id mismatch\n");
+ return -ENODEV;
+ }
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*mma7455));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, indio_dev);
+ mma7455 = iio_priv(indio_dev);
+ mma7455->regmap = regmap;
+ mma7455->dev = dev;
+
+ indio_dev->info = &mma7455_info;
+ indio_dev->name = name;
+ indio_dev->dev.parent = dev;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = mma7455_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mma7455_channels);
+ indio_dev->available_scan_masks = mma7455_scan_masks;
+
+ regmap_write(mma7455->regmap, MMA7455_REG_MCTL,
+ MMA7455_MCTL_MODE_MEASURE);
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ mma7455_trigger_handler, NULL);
+ if (ret) {
+ dev_err(dev, "unable to setup triggered buffer\n");
+ return ret;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(dev, "unable to register device\n");
+ iio_triggered_buffer_cleanup(indio_dev);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mma7455_core_probe);
+
+int mma7455_core_remove(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct mma7455_data *mma7455 = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ regmap_write(mma7455->regmap, MMA7455_REG_MCTL,
+ MMA7455_MCTL_MODE_STANDBY);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mma7455_core_remove);
+
+MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
+MODULE_DESCRIPTION("Freescale MMA7455L core accelerometer driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/mma7455_i2c.c b/drivers/iio/accel/mma7455_i2c.c
new file mode 100644
index 000000000000..3cab5fb4a3c4
--- /dev/null
+++ b/drivers/iio/accel/mma7455_i2c.c
@@ -0,0 +1,56 @@
+/*
+ * IIO accel I2C driver for Freescale MMA7455L 3-axis 10-bit accelerometer
+ * Copyright 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/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "mma7455.h"
+
+static int mma7455_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regmap *regmap;
+ const char *name = NULL;
+
+ regmap = devm_regmap_init_i2c(i2c, &mma7455_core_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ if (id)
+ name = id->name;
+
+ return mma7455_core_probe(&i2c->dev, regmap, name);
+}
+
+static int mma7455_i2c_remove(struct i2c_client *i2c)
+{
+ return mma7455_core_remove(&i2c->dev);
+}
+
+static const struct i2c_device_id mma7455_i2c_ids[] = {
+ { "mma7455", 0 },
+ { "mma7456", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mma7455_i2c_ids);
+
+static struct i2c_driver mma7455_i2c_driver = {
+ .probe = mma7455_i2c_probe,
+ .remove = mma7455_i2c_remove,
+ .id_table = mma7455_i2c_ids,
+ .driver = {
+ .name = "mma7455-i2c",
+ },
+};
+module_i2c_driver(mma7455_i2c_driver);
+
+MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
+MODULE_DESCRIPTION("Freescale MMA7455L I2C accelerometer driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/mma7455_spi.c b/drivers/iio/accel/mma7455_spi.c
new file mode 100644
index 000000000000..79df8f27cf99
--- /dev/null
+++ b/drivers/iio/accel/mma7455_spi.c
@@ -0,0 +1,52 @@
+/*
+ * IIO accel SPI driver for Freescale MMA7455L 3-axis 10-bit accelerometer
+ * Copyright 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/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "mma7455.h"
+
+static int mma7455_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_spi(spi, &mma7455_core_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return mma7455_core_probe(&spi->dev, regmap, id->name);
+}
+
+static int mma7455_spi_remove(struct spi_device *spi)
+{
+ return mma7455_core_remove(&spi->dev);
+}
+
+static const struct spi_device_id mma7455_spi_ids[] = {
+ { "mma7455", 0 },
+ { "mma7456", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, mma7455_spi_ids);
+
+static struct spi_driver mma7455_spi_driver = {
+ .probe = mma7455_spi_probe,
+ .remove = mma7455_spi_remove,
+ .id_table = mma7455_spi_ids,
+ .driver = {
+ .name = "mma7455-spi",
+ },
+};
+module_spi_driver(mma7455_spi_driver);
+
+MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
+MODULE_DESCRIPTION("Freescale MMA7455L SPI accelerometer driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c
index 1eccc2dcf14c..ccc632a7cf01 100644
--- a/drivers/iio/accel/mma8452.c
+++ b/drivers/iio/accel/mma8452.c
@@ -29,6 +29,7 @@
#include <linux/iio/events.h>
#include <linux/delay.h>
#include <linux/of_device.h>
+#include <linux/of_irq.h>
#define MMA8452_STATUS 0x00
#define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
@@ -57,7 +58,6 @@
#define MMA8452_FF_MT_COUNT 0x18
#define MMA8452_TRANSIENT_CFG 0x1d
#define MMA8452_TRANSIENT_CFG_HPF_BYP BIT(0)
-#define MMA8452_TRANSIENT_CFG_CHAN(chan) BIT(chan + 1)
#define MMA8452_TRANSIENT_CFG_ELE BIT(4)
#define MMA8452_TRANSIENT_SRC 0x1e
#define MMA8452_TRANSIENT_SRC_XTRANSE BIT(1)
@@ -143,6 +143,13 @@ struct mma_chip_info {
u8 ev_count;
};
+enum {
+ idx_x,
+ idx_y,
+ idx_z,
+ idx_ts,
+};
+
static int mma8452_drdy(struct mma8452_data *data)
{
int tries = 150;
@@ -816,31 +823,31 @@ static struct attribute_group mma8452_event_attribute_group = {
}
static const struct iio_chan_spec mma8452_channels[] = {
- MMA8452_CHANNEL(X, 0, 12),
- MMA8452_CHANNEL(Y, 1, 12),
- MMA8452_CHANNEL(Z, 2, 12),
- IIO_CHAN_SOFT_TIMESTAMP(3),
+ MMA8452_CHANNEL(X, idx_x, 12),
+ MMA8452_CHANNEL(Y, idx_y, 12),
+ MMA8452_CHANNEL(Z, idx_z, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
};
static const struct iio_chan_spec mma8453_channels[] = {
- MMA8452_CHANNEL(X, 0, 10),
- MMA8452_CHANNEL(Y, 1, 10),
- MMA8452_CHANNEL(Z, 2, 10),
- IIO_CHAN_SOFT_TIMESTAMP(3),
+ MMA8452_CHANNEL(X, idx_x, 10),
+ MMA8452_CHANNEL(Y, idx_y, 10),
+ MMA8452_CHANNEL(Z, idx_z, 10),
+ IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
};
static const struct iio_chan_spec mma8652_channels[] = {
- MMA8652_CHANNEL(X, 0, 12),
- MMA8652_CHANNEL(Y, 1, 12),
- MMA8652_CHANNEL(Z, 2, 12),
- IIO_CHAN_SOFT_TIMESTAMP(3),
+ MMA8652_CHANNEL(X, idx_x, 12),
+ MMA8652_CHANNEL(Y, idx_y, 12),
+ MMA8652_CHANNEL(Z, idx_z, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
};
static const struct iio_chan_spec mma8653_channels[] = {
- MMA8652_CHANNEL(X, 0, 10),
- MMA8652_CHANNEL(Y, 1, 10),
- MMA8652_CHANNEL(Z, 2, 10),
- IIO_CHAN_SOFT_TIMESTAMP(3),
+ MMA8652_CHANNEL(X, idx_x, 10),
+ MMA8652_CHANNEL(Y, idx_y, 10),
+ MMA8652_CHANNEL(Z, idx_z, 10),
+ IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
};
enum {
@@ -1130,13 +1137,21 @@ static int mma8452_probe(struct i2c_client *client,
MMA8452_INT_FF_MT;
int enabled_interrupts = MMA8452_INT_TRANS |
MMA8452_INT_FF_MT;
+ int irq2;
- /* Assume wired to INT1 pin */
- ret = i2c_smbus_write_byte_data(client,
- MMA8452_CTRL_REG5,
- supported_interrupts);
- if (ret < 0)
- return ret;
+ irq2 = of_irq_get_byname(client->dev.of_node, "INT2");
+
+ if (irq2 == client->irq) {
+ dev_dbg(&client->dev, "using interrupt line INT2\n");
+ } else {
+ ret = i2c_smbus_write_byte_data(client,
+ MMA8452_CTRL_REG5,
+ supported_interrupts);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(&client->dev, "using interrupt line INT1\n");
+ }
ret = i2c_smbus_write_byte_data(client,
MMA8452_CTRL_REG4,
diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c
index 7db7cc0bf362..d899a4d4307f 100644
--- a/drivers/iio/accel/mma9551.c
+++ b/drivers/iio/accel/mma9551.c
@@ -495,25 +495,23 @@ static int mma9551_probe(struct i2c_client *client,
if (ret < 0)
goto out_poweroff;
- ret = iio_device_register(indio_dev);
- if (ret < 0) {
- dev_err(&client->dev, "unable to register iio device\n");
- goto out_poweroff;
- }
-
ret = pm_runtime_set_active(&client->dev);
if (ret < 0)
- goto out_iio_unregister;
+ goto out_poweroff;
pm_runtime_enable(&client->dev);
pm_runtime_set_autosuspend_delay(&client->dev,
MMA9551_AUTO_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(&client->dev);
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "unable to register iio device\n");
+ goto out_poweroff;
+ }
+
return 0;
-out_iio_unregister:
- iio_device_unregister(indio_dev);
out_poweroff:
mma9551_set_device_state(client, false);
@@ -525,11 +523,12 @@ static int mma9551_remove(struct i2c_client *client)
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct mma9551_data *data = iio_priv(indio_dev);
+ iio_device_unregister(indio_dev);
+
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
- iio_device_unregister(indio_dev);
mutex_lock(&data->mutex);
mma9551_set_device_state(data->client, false);
mutex_unlock(&data->mutex);
diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c
index 9408ef3add58..fa7d36217c4b 100644
--- a/drivers/iio/accel/mma9553.c
+++ b/drivers/iio/accel/mma9553.c
@@ -1133,27 +1133,24 @@ static int mma9553_probe(struct i2c_client *client,
}
}
- ret = iio_device_register(indio_dev);
- if (ret < 0) {
- dev_err(&client->dev, "unable to register iio device\n");
- goto out_poweroff;
- }
-
ret = pm_runtime_set_active(&client->dev);
if (ret < 0)
- goto out_iio_unregister;
+ goto out_poweroff;
pm_runtime_enable(&client->dev);
pm_runtime_set_autosuspend_delay(&client->dev,
MMA9551_AUTO_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(&client->dev);
- dev_dbg(&indio_dev->dev, "Registered device %s\n", name);
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "unable to register iio device\n");
+ goto out_poweroff;
+ }
+ dev_dbg(&indio_dev->dev, "Registered device %s\n", name);
return 0;
-out_iio_unregister:
- iio_device_unregister(indio_dev);
out_poweroff:
mma9551_set_device_state(client, false);
return ret;
@@ -1164,11 +1161,12 @@ static int mma9553_remove(struct i2c_client *client)
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct mma9553_data *data = iio_priv(indio_dev);
+ iio_device_unregister(indio_dev);
+
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
- iio_device_unregister(indio_dev);
mutex_lock(&data->mutex);
mma9551_set_device_state(data->client, false);
mutex_unlock(&data->mutex);
diff --git a/drivers/iio/accel/mxc6255.c b/drivers/iio/accel/mxc6255.c
new file mode 100644
index 000000000000..97ccde722e7b
--- /dev/null
+++ b/drivers/iio/accel/mxc6255.c
@@ -0,0 +1,198 @@
+/*
+ * MXC6255 - MEMSIC orientation sensing accelerometer
+ *
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * IIO driver for MXC6255 (7-bit I2C slave address 0x15).
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/iio/iio.h>
+#include <linux/delay.h>
+#include <linux/acpi.h>
+#include <linux/regmap.h>
+#include <linux/iio/sysfs.h>
+
+#define MXC6255_DRV_NAME "mxc6255"
+#define MXC6255_REGMAP_NAME "mxc6255_regmap"
+
+#define MXC6255_REG_XOUT 0x00
+#define MXC6255_REG_YOUT 0x01
+#define MXC6255_REG_CHIP_ID 0x08
+
+#define MXC6255_CHIP_ID 0x05
+
+/*
+ * MXC6255 has only one measurement range: +/- 2G.
+ * The acceleration output is an 8-bit value.
+ *
+ * Scale is calculated as follows:
+ * (2 + 2) * 9.80665 / (2^8 - 1) = 0.153829
+ *
+ * Scale value for +/- 2G measurement range
+ */
+#define MXC6255_SCALE 153829
+
+enum mxc6255_axis {
+ AXIS_X,
+ AXIS_Y,
+};
+
+struct mxc6255_data {
+ struct i2c_client *client;
+ struct regmap *regmap;
+};
+
+static int mxc6255_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct mxc6255_data *data = iio_priv(indio_dev);
+ unsigned int reg;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = regmap_read(data->regmap, chan->address, &reg);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "Error reading reg %lu\n", chan->address);
+ return ret;
+ }
+
+ *val = sign_extend32(reg, 7);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = MXC6255_SCALE;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info mxc6255_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = mxc6255_read_raw,
+};
+
+#define MXC6255_CHANNEL(_axis, reg) { \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##_axis, \
+ .address = reg, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec mxc6255_channels[] = {
+ MXC6255_CHANNEL(X, MXC6255_REG_XOUT),
+ MXC6255_CHANNEL(Y, MXC6255_REG_YOUT),
+};
+
+static bool mxc6255_is_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MXC6255_REG_XOUT:
+ case MXC6255_REG_YOUT:
+ case MXC6255_REG_CHIP_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config mxc6255_regmap_config = {
+ .name = MXC6255_REGMAP_NAME,
+
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .readable_reg = mxc6255_is_readable_reg,
+};
+
+static int mxc6255_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mxc6255_data *data;
+ struct iio_dev *indio_dev;
+ struct regmap *regmap;
+ unsigned int chip_id;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_i2c(client, &mxc6255_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "Error initializing regmap\n");
+ return PTR_ERR(regmap);
+ }
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+ data->regmap = regmap;
+
+ indio_dev->name = MXC6255_DRV_NAME;
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->channels = mxc6255_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mxc6255_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &mxc6255_info;
+
+ ret = regmap_read(data->regmap, MXC6255_REG_CHIP_ID, &chip_id);
+ if (ret < 0) {
+ dev_err(&client->dev, "Error reading chip id %d\n", ret);
+ return ret;
+ }
+
+ if (chip_id != MXC6255_CHIP_ID) {
+ dev_err(&client->dev, "Invalid chip id %x\n", chip_id);
+ return -ENODEV;
+ }
+
+ dev_dbg(&client->dev, "Chip id %x\n", chip_id);
+
+ ret = devm_iio_device_register(&client->dev, indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "Could not register IIO device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct acpi_device_id mxc6255_acpi_match[] = {
+ {"MXC6255", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, mxc6255_acpi_match);
+
+static const struct i2c_device_id mxc6255_id[] = {
+ {"mxc6255", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mxc6255_id);
+
+static struct i2c_driver mxc6255_driver = {
+ .driver = {
+ .name = MXC6255_DRV_NAME,
+ .acpi_match_table = ACPI_PTR(mxc6255_acpi_match),
+ },
+ .probe = mxc6255_probe,
+ .id_table = mxc6255_id,
+};
+
+module_i2c_driver(mxc6255_driver);
+
+MODULE_AUTHOR("Teodora Baluta <teodora.baluta@intel.com>");
+MODULE_DESCRIPTION("MEMSIC MXC6255 orientation sensing accelerometer driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h
index 468f21fa2950..5d4a1897b293 100644
--- a/drivers/iio/accel/st_accel.h
+++ b/drivers/iio/accel/st_accel.h
@@ -27,6 +27,7 @@
#define LSM303DLM_ACCEL_DEV_NAME "lsm303dlm_accel"
#define LSM330_ACCEL_DEV_NAME "lsm330_accel"
#define LSM303AGR_ACCEL_DEV_NAME "lsm303agr_accel"
+#define LIS2DH12_ACCEL_DEV_NAME "lis2dh12_accel"
/**
* struct st_sensors_platform_data - default accel platform data
diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c
index 197a08b4e2f3..70f042797f15 100644
--- a/drivers/iio/accel/st_accel_core.c
+++ b/drivers/iio/accel/st_accel_core.c
@@ -232,6 +232,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
[3] = LSM330DL_ACCEL_DEV_NAME,
[4] = LSM330DLC_ACCEL_DEV_NAME,
[5] = LSM303AGR_ACCEL_DEV_NAME,
+ [6] = LIS2DH12_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
.odr = {
diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c
index 8b9cc84fd44f..294a32f89367 100644
--- a/drivers/iio/accel/st_accel_i2c.c
+++ b/drivers/iio/accel/st_accel_i2c.c
@@ -72,6 +72,10 @@ static const struct of_device_id st_accel_of_match[] = {
.compatible = "st,lsm303agr-accel",
.data = LSM303AGR_ACCEL_DEV_NAME,
},
+ {
+ .compatible = "st,lis2dh12-accel",
+ .data = LIS2DH12_ACCEL_DEV_NAME,
+ },
{},
};
MODULE_DEVICE_TABLE(of, st_accel_of_match);
@@ -121,6 +125,7 @@ static const struct i2c_device_id st_accel_id_table[] = {
{ LSM303DLM_ACCEL_DEV_NAME },
{ LSM330_ACCEL_DEV_NAME },
{ LSM303AGR_ACCEL_DEV_NAME },
+ { LIS2DH12_ACCEL_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c
index f71b0d391272..fcd5847a3fd3 100644
--- a/drivers/iio/accel/st_accel_spi.c
+++ b/drivers/iio/accel/st_accel_spi.c
@@ -58,6 +58,7 @@ static const struct spi_device_id st_accel_id_table[] = {
{ LSM303DLM_ACCEL_DEV_NAME },
{ LSM330_ACCEL_DEV_NAME },
{ LSM303AGR_ACCEL_DEV_NAME },
+ { LIS2DH12_ACCEL_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(spi, st_accel_id_table);
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 7868c744fd4b..605ff42c4631 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -194,6 +194,25 @@ config HI8435
This driver can also be built as a module. If so, the module will be
called hi8435.
+config INA2XX_ADC
+ tristate "Texas Instruments INA2xx Power Monitors IIO driver"
+ depends on I2C && !SENSORS_INA2XX
+ select REGMAP_I2C
+ select IIO_BUFFER
+ select IIO_KFIFO_BUF
+ help
+ Say yes here to build support for TI INA2xx family of Power Monitors.
+ This driver is mutually exclusive with the HWMON version.
+
+config IMX7D_ADC
+ tristate "IMX7D ADC driver"
+ depends on ARCH_MXC || COMPILE_TEST
+ help
+ Say yes here to build support for IMX7D ADC.
+
+ This driver can also be built as a module. If so, the module will be
+ called imx7d_adc.
+
config LP8788_ADC
tristate "LP8788 ADC driver"
depends on MFD_LP8788
@@ -275,6 +294,14 @@ config NAU7802
To compile this driver as a module, choose M here: the
module will be called nau7802.
+config PALMAS_GPADC
+ tristate "TI Palmas General Purpose ADC"
+ depends on MFD_PALMAS
+ help
+ Palmas series pmic chip by Texas Instruments (twl6035/6037)
+ is used in smartphones and tablets and supports a 16 channel
+ general purpose ADC.
+
config QCOM_SPMI_IADC
tristate "Qualcomm SPMI PMIC current ADC"
depends on SPMI
@@ -324,15 +351,25 @@ config TI_ADC081C
called ti-adc081c.
config TI_ADC128S052
- tristate "Texas Instruments ADC128S052/ADC122S021"
+ tristate "Texas Instruments ADC128S052/ADC122S021/ADC124S021"
depends on SPI
help
- If you say yes here you get support for Texas Instruments ADC128S052
- and ADC122S021 chips.
+ If you say yes here you get support for Texas Instruments ADC128S052,
+ ADC122S021 and ADC124S021 chips.
This driver can also be built as a module. If so, the module will be
called ti-adc128s052.
+config TI_ADS8688
+ tristate "Texas Instruments ADS8688"
+ depends on SPI && OF
+ help
+ If you say yes here you get support for Texas Instruments ADS8684 and
+ and ADS8688 ADC chips
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-ads8688.
+
config TI_AM335X_ADC
tristate "TI's AM335X ADC driver"
depends on MFD_TI_AM335X_TSCADC
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 99b37a963a1e..6435780e9b71 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -20,6 +20,8 @@ obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
obj-$(CONFIG_HI8435) += hi8435.o
+obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
+obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_MAX1027) += max1027.o
obj-$(CONFIG_MAX1363) += max1363.o
@@ -27,11 +29,13 @@ obj-$(CONFIG_MCP320X) += mcp320x.o
obj-$(CONFIG_MCP3422) += mcp3422.o
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
obj-$(CONFIG_NAU7802) += nau7802.o
+obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
+obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c
index 4d960d3b93c0..7b07bb651671 100644
--- a/drivers/iio/adc/ad7793.c
+++ b/drivers/iio/adc/ad7793.c
@@ -478,10 +478,9 @@ static int ad7793_read_raw(struct iio_dev *indio_dev,
*val2 = st->
scale_avail[(st->conf >> 8) & 0x7][1];
return IIO_VAL_INT_PLUS_NANO;
- } else {
- /* 1170mV / 2^23 * 6 */
- scale_uv = (1170ULL * 1000000000ULL * 6ULL);
}
+ /* 1170mV / 2^23 * 6 */
+ scale_uv = (1170ULL * 1000000000ULL * 6ULL);
break;
case IIO_TEMP:
/* 1170mV / 0.81 mV/C / 2^23 */
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
index 7b40925dd4ff..f284cd6a93d6 100644
--- a/drivers/iio/adc/at91_adc.c
+++ b/drivers/iio/adc/at91_adc.c
@@ -742,7 +742,7 @@ static int at91_adc_of_get_resolution(struct at91_adc_state *st,
return count;
}
- resolutions = kmalloc(count * sizeof(*resolutions), GFP_KERNEL);
+ resolutions = kmalloc_array(count, sizeof(*resolutions), GFP_KERNEL);
if (!resolutions)
return -ENOMEM;
diff --git a/drivers/iio/adc/imx7d_adc.c b/drivers/iio/adc/imx7d_adc.c
new file mode 100644
index 000000000000..e2241ee94783
--- /dev/null
+++ b/drivers/iio/adc/imx7d_adc.c
@@ -0,0 +1,609 @@
+/*
+ * Freescale i.MX7D ADC driver
+ *
+ * Copyright (C) 2015 Freescale Semiconductor, 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/clk.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/sysfs.h>
+
+/* ADC register */
+#define IMX7D_REG_ADC_CH_A_CFG1 0x00
+#define IMX7D_REG_ADC_CH_A_CFG2 0x10
+#define IMX7D_REG_ADC_CH_B_CFG1 0x20
+#define IMX7D_REG_ADC_CH_B_CFG2 0x30
+#define IMX7D_REG_ADC_CH_C_CFG1 0x40
+#define IMX7D_REG_ADC_CH_C_CFG2 0x50
+#define IMX7D_REG_ADC_CH_D_CFG1 0x60
+#define IMX7D_REG_ADC_CH_D_CFG2 0x70
+#define IMX7D_REG_ADC_CH_SW_CFG 0x80
+#define IMX7D_REG_ADC_TIMER_UNIT 0x90
+#define IMX7D_REG_ADC_DMA_FIFO 0xa0
+#define IMX7D_REG_ADC_FIFO_STATUS 0xb0
+#define IMX7D_REG_ADC_INT_SIG_EN 0xc0
+#define IMX7D_REG_ADC_INT_EN 0xd0
+#define IMX7D_REG_ADC_INT_STATUS 0xe0
+#define IMX7D_REG_ADC_CHA_B_CNV_RSLT 0xf0
+#define IMX7D_REG_ADC_CHC_D_CNV_RSLT 0x100
+#define IMX7D_REG_ADC_CH_SW_CNV_RSLT 0x110
+#define IMX7D_REG_ADC_DMA_FIFO_DAT 0x120
+#define IMX7D_REG_ADC_ADC_CFG 0x130
+
+#define IMX7D_REG_ADC_CHANNEL_CFG2_BASE 0x10
+#define IMX7D_EACH_CHANNEL_REG_OFFSET 0x20
+
+#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN (0x1 << 31)
+#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE BIT(30)
+#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN BIT(29)
+#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(x) ((x) << 24)
+
+#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4 (0x0 << 12)
+#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8 (0x1 << 12)
+#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16 (0x2 << 12)
+#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32 (0x3 << 12)
+
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4 (0x0 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8 (0x1 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16 (0x2 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32 (0x3 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64 (0x4 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128 (0x5 << 29)
+
+#define IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN BIT(31)
+#define IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN BIT(1)
+#define IMX7D_REG_ADC_ADC_CFG_ADC_EN BIT(0)
+
+#define IMX7D_REG_ADC_INT_CHA_COV_INT_EN BIT(8)
+#define IMX7D_REG_ADC_INT_CHB_COV_INT_EN BIT(9)
+#define IMX7D_REG_ADC_INT_CHC_COV_INT_EN BIT(10)
+#define IMX7D_REG_ADC_INT_CHD_COV_INT_EN BIT(11)
+#define IMX7D_REG_ADC_INT_CHANNEL_INT_EN \
+ (IMX7D_REG_ADC_INT_CHA_COV_INT_EN | \
+ IMX7D_REG_ADC_INT_CHB_COV_INT_EN | \
+ IMX7D_REG_ADC_INT_CHC_COV_INT_EN | \
+ IMX7D_REG_ADC_INT_CHD_COV_INT_EN)
+#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS 0xf00
+#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT 0xf0000
+
+#define IMX7D_ADC_TIMEOUT msecs_to_jiffies(100)
+
+enum imx7d_adc_clk_pre_div {
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_4,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_8,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_16,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_32,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_64,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_128,
+};
+
+enum imx7d_adc_average_num {
+ IMX7D_ADC_AVERAGE_NUM_4,
+ IMX7D_ADC_AVERAGE_NUM_8,
+ IMX7D_ADC_AVERAGE_NUM_16,
+ IMX7D_ADC_AVERAGE_NUM_32,
+};
+
+struct imx7d_adc_feature {
+ enum imx7d_adc_clk_pre_div clk_pre_div;
+ enum imx7d_adc_average_num avg_num;
+
+ u32 core_time_unit; /* impact the sample rate */
+
+ bool average_en;
+};
+
+struct imx7d_adc {
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *clk;
+
+ u32 vref_uv;
+ u32 value;
+ u32 channel;
+ u32 pre_div_num;
+
+ struct regulator *vref;
+ struct imx7d_adc_feature adc_feature;
+
+ struct completion completion;
+};
+
+struct imx7d_adc_analogue_core_clk {
+ u32 pre_div;
+ u32 reg_config;
+};
+
+#define IMX7D_ADC_ANALOGUE_CLK_CONFIG(_pre_div, _reg_conf) { \
+ .pre_div = (_pre_div), \
+ .reg_config = (_reg_conf), \
+}
+
+static const struct imx7d_adc_analogue_core_clk imx7d_adc_analogue_clk[] = {
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(4, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(8, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(16, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(32, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(64, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(128, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128),
+};
+
+#define IMX7D_ADC_CHAN(_idx) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+}
+
+static const struct iio_chan_spec imx7d_adc_iio_channels[] = {
+ IMX7D_ADC_CHAN(0),
+ IMX7D_ADC_CHAN(1),
+ IMX7D_ADC_CHAN(2),
+ IMX7D_ADC_CHAN(3),
+ IMX7D_ADC_CHAN(4),
+ IMX7D_ADC_CHAN(5),
+ IMX7D_ADC_CHAN(6),
+ IMX7D_ADC_CHAN(7),
+ IMX7D_ADC_CHAN(8),
+ IMX7D_ADC_CHAN(9),
+ IMX7D_ADC_CHAN(10),
+ IMX7D_ADC_CHAN(11),
+ IMX7D_ADC_CHAN(12),
+ IMX7D_ADC_CHAN(13),
+ IMX7D_ADC_CHAN(14),
+ IMX7D_ADC_CHAN(15),
+};
+
+static const u32 imx7d_adc_average_num[] = {
+ IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4,
+ IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8,
+ IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16,
+ IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32,
+};
+
+static void imx7d_adc_feature_config(struct imx7d_adc *info)
+{
+ info->adc_feature.clk_pre_div = IMX7D_ADC_ANALOG_CLK_PRE_DIV_4;
+ info->adc_feature.avg_num = IMX7D_ADC_AVERAGE_NUM_32;
+ info->adc_feature.core_time_unit = 1;
+ info->adc_feature.average_en = true;
+}
+
+static void imx7d_adc_sample_rate_set(struct imx7d_adc *info)
+{
+ struct imx7d_adc_feature *adc_feature = &info->adc_feature;
+ struct imx7d_adc_analogue_core_clk adc_analogure_clk;
+ u32 i;
+ u32 tmp_cfg1;
+ u32 sample_rate = 0;
+
+ /*
+ * Before sample set, disable channel A,B,C,D. Here we
+ * clear the bit 31 of register REG_ADC_CH_A\B\C\D_CFG1.
+ */
+ for (i = 0; i < 4; i++) {
+ tmp_cfg1 =
+ readl(info->regs + i * IMX7D_EACH_CHANNEL_REG_OFFSET);
+ tmp_cfg1 &= ~IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN;
+ writel(tmp_cfg1,
+ info->regs + i * IMX7D_EACH_CHANNEL_REG_OFFSET);
+ }
+
+ adc_analogure_clk = imx7d_adc_analogue_clk[adc_feature->clk_pre_div];
+ sample_rate |= adc_analogure_clk.reg_config;
+ info->pre_div_num = adc_analogure_clk.pre_div;
+
+ sample_rate |= adc_feature->core_time_unit;
+ writel(sample_rate, info->regs + IMX7D_REG_ADC_TIMER_UNIT);
+}
+
+static void imx7d_adc_hw_init(struct imx7d_adc *info)
+{
+ u32 cfg;
+
+ /* power up and enable adc analogue core */
+ cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG);
+ cfg &= ~(IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN |
+ IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN);
+ cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_EN;
+ writel(cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
+
+ /* enable channel A,B,C,D interrupt */
+ writel(IMX7D_REG_ADC_INT_CHANNEL_INT_EN,
+ info->regs + IMX7D_REG_ADC_INT_SIG_EN);
+ writel(IMX7D_REG_ADC_INT_CHANNEL_INT_EN,
+ info->regs + IMX7D_REG_ADC_INT_EN);
+
+ imx7d_adc_sample_rate_set(info);
+}
+
+static void imx7d_adc_channel_set(struct imx7d_adc *info)
+{
+ u32 cfg1 = 0;
+ u32 cfg2;
+ u32 channel;
+
+ channel = info->channel;
+
+ /* the channel choose single conversion, and enable average mode */
+ cfg1 |= (IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN |
+ IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE);
+ if (info->adc_feature.average_en)
+ cfg1 |= IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN;
+
+ /*
+ * physical channel 0 chose logical channel A
+ * physical channel 1 chose logical channel B
+ * physical channel 2 chose logical channel C
+ * physical channel 3 chose logical channel D
+ */
+ cfg1 |= IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(channel);
+
+ /*
+ * read register REG_ADC_CH_A\B\C\D_CFG2, according to the
+ * channel chosen
+ */
+ cfg2 = readl(info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel +
+ IMX7D_REG_ADC_CHANNEL_CFG2_BASE);
+
+ cfg2 |= imx7d_adc_average_num[info->adc_feature.avg_num];
+
+ /*
+ * write the register REG_ADC_CH_A\B\C\D_CFG2, according to
+ * the channel chosen
+ */
+ writel(cfg2, info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel +
+ IMX7D_REG_ADC_CHANNEL_CFG2_BASE);
+ writel(cfg1, info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel);
+}
+
+static u32 imx7d_adc_get_sample_rate(struct imx7d_adc *info)
+{
+ /* input clock is always 24MHz */
+ u32 input_clk = 24000000;
+ u32 analogue_core_clk;
+ u32 core_time_unit = info->adc_feature.core_time_unit;
+ u32 tmp;
+
+ analogue_core_clk = input_clk / info->pre_div_num;
+ tmp = (core_time_unit + 1) * 6;
+
+ return analogue_core_clk / tmp;
+}
+
+static int imx7d_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct imx7d_adc *info = iio_priv(indio_dev);
+
+ u32 channel;
+ long ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ reinit_completion(&info->completion);
+
+ channel = chan->channel & 0x03;
+ info->channel = channel;
+ imx7d_adc_channel_set(info);
+
+ ret = wait_for_completion_interruptible_timeout
+ (&info->completion, IMX7D_ADC_TIMEOUT);
+ if (ret == 0) {
+ mutex_unlock(&indio_dev->mlock);
+ return -ETIMEDOUT;
+ }
+ if (ret < 0) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+
+ *val = info->value;
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ info->vref_uv = regulator_get_voltage(info->vref);
+ *val = info->vref_uv / 1000;
+ *val2 = 12;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = imx7d_adc_get_sample_rate(info);
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int imx7d_adc_read_data(struct imx7d_adc *info)
+{
+ u32 channel;
+ u32 value;
+
+ channel = info->channel & 0x03;
+
+ /*
+ * channel A and B conversion result share one register,
+ * bit[27~16] is the channel B conversion result,
+ * bit[11~0] is the channel A conversion result.
+ * channel C and D is the same.
+ */
+ if (channel < 2)
+ value = readl(info->regs + IMX7D_REG_ADC_CHA_B_CNV_RSLT);
+ else
+ value = readl(info->regs + IMX7D_REG_ADC_CHC_D_CNV_RSLT);
+ if (channel & 0x1) /* channel B or D */
+ value = (value >> 16) & 0xFFF;
+ else /* channel A or C */
+ value &= 0xFFF;
+
+ return value;
+}
+
+static irqreturn_t imx7d_adc_isr(int irq, void *dev_id)
+{
+ struct imx7d_adc *info = (struct imx7d_adc *)dev_id;
+ int status;
+
+ status = readl(info->regs + IMX7D_REG_ADC_INT_STATUS);
+ if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS) {
+ info->value = imx7d_adc_read_data(info);
+ complete(&info->completion);
+
+ /*
+ * The register IMX7D_REG_ADC_INT_STATUS can't clear
+ * itself after read operation, need software to write
+ * 0 to the related bit. Here we clear the channel A/B/C/D
+ * conversion finished flag.
+ */
+ status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS;
+ writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
+ }
+
+ /*
+ * If the channel A/B/C/D conversion timeout, report it and clear these
+ * timeout flags.
+ */
+ if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT) {
+ pr_err("%s: ADC got conversion time out interrupt: 0x%08x\n",
+ dev_name(info->dev), status);
+ status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT;
+ writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int imx7d_adc_reg_access(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval,
+ unsigned *readval)
+{
+ struct imx7d_adc *info = iio_priv(indio_dev);
+
+ if (!readval || reg % 4 || reg > IMX7D_REG_ADC_ADC_CFG)
+ return -EINVAL;
+
+ *readval = readl(info->regs + reg);
+
+ return 0;
+}
+
+static const struct iio_info imx7d_adc_iio_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &imx7d_adc_read_raw,
+ .debugfs_reg_access = &imx7d_adc_reg_access,
+};
+
+static const struct of_device_id imx7d_adc_match[] = {
+ { .compatible = "fsl,imx7d-adc", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx7d_adc_match);
+
+static void imx7d_adc_power_down(struct imx7d_adc *info)
+{
+ u32 adc_cfg;
+
+ adc_cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG);
+ adc_cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN |
+ IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN;
+ adc_cfg &= ~IMX7D_REG_ADC_ADC_CFG_ADC_EN;
+ writel(adc_cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
+}
+
+static int imx7d_adc_probe(struct platform_device *pdev)
+{
+ struct imx7d_adc *info;
+ struct iio_dev *indio_dev;
+ struct resource *mem;
+ int irq;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "Failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ info = iio_priv(indio_dev);
+ info->dev = &pdev->dev;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ info->regs = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(info->regs)) {
+ ret = PTR_ERR(info->regs);
+ dev_err(&pdev->dev,
+ "Failed to remap adc memory, err = %d\n", ret);
+ return ret;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "No irq resource?\n");
+ return irq;
+ }
+
+ info->clk = devm_clk_get(&pdev->dev, "adc");
+ if (IS_ERR(info->clk)) {
+ ret = PTR_ERR(info->clk);
+ dev_err(&pdev->dev, "Failed getting clock, err = %d\n", ret);
+ return ret;
+ }
+
+ info->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(info->vref)) {
+ ret = PTR_ERR(info->vref);
+ dev_err(&pdev->dev,
+ "Failed getting reference voltage, err = %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(info->vref);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Can't enable adc reference top voltage, err = %d\n",
+ ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ init_completion(&info->completion);
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &imx7d_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = imx7d_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(imx7d_adc_iio_channels);
+
+ ret = clk_prepare_enable(info->clk);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Could not prepare or enable the clock.\n");
+ goto error_adc_clk_enable;
+ }
+
+ ret = devm_request_irq(info->dev, irq,
+ imx7d_adc_isr, 0,
+ dev_name(&pdev->dev), info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed requesting irq, irq = %d\n", irq);
+ goto error_iio_device_register;
+ }
+
+ imx7d_adc_feature_config(info);
+ imx7d_adc_hw_init(info);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ imx7d_adc_power_down(info);
+ dev_err(&pdev->dev, "Couldn't register the device.\n");
+ goto error_iio_device_register;
+ }
+
+ return 0;
+
+error_iio_device_register:
+ clk_disable_unprepare(info->clk);
+error_adc_clk_enable:
+ regulator_disable(info->vref);
+
+ return ret;
+}
+
+static int imx7d_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct imx7d_adc *info = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ imx7d_adc_power_down(info);
+
+ clk_disable_unprepare(info->clk);
+ regulator_disable(info->vref);
+
+ return 0;
+}
+
+static int __maybe_unused imx7d_adc_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct imx7d_adc *info = iio_priv(indio_dev);
+
+ imx7d_adc_power_down(info);
+
+ clk_disable_unprepare(info->clk);
+ regulator_disable(info->vref);
+
+ return 0;
+}
+
+static int __maybe_unused imx7d_adc_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct imx7d_adc *info = iio_priv(indio_dev);
+ int ret;
+
+ ret = regulator_enable(info->vref);
+ if (ret) {
+ dev_err(info->dev,
+ "Can't enable adc reference top voltage, err = %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(info->clk);
+ if (ret) {
+ dev_err(info->dev,
+ "Could not prepare or enable clock.\n");
+ regulator_disable(info->vref);
+ return ret;
+ }
+
+ imx7d_adc_hw_init(info);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(imx7d_adc_pm_ops, imx7d_adc_suspend, imx7d_adc_resume);
+
+static struct platform_driver imx7d_adc_driver = {
+ .probe = imx7d_adc_probe,
+ .remove = imx7d_adc_remove,
+ .driver = {
+ .name = "imx7d_adc",
+ .of_match_table = imx7d_adc_match,
+ .pm = &imx7d_adc_pm_ops,
+ },
+};
+
+module_platform_driver(imx7d_adc_driver);
+
+MODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>");
+MODULE_DESCRIPTION("Freeacale IMX7D ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c
new file mode 100644
index 000000000000..d803e5018a42
--- /dev/null
+++ b/drivers/iio/adc/ina2xx-adc.c
@@ -0,0 +1,745 @@
+/*
+ * INA2XX Current and Power Monitors
+ *
+ * Copyright 2015 Baylibre SAS.
+ *
+ * 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.
+ *
+ * Based on linux/drivers/iio/adc/ad7291.c
+ * Copyright 2010-2011 Analog Devices Inc.
+ *
+ * Based on linux/drivers/hwmon/ina2xx.c
+ * Copyright 2012 Lothar Felten <l-felten@ti.com>
+ *
+ * Licensed under the GPL-2 or later.
+ *
+ * IIO driver for INA219-220-226-230-231
+ *
+ * Configurable 7-bit I2C slave address from 0x40 to 0x4F
+ */
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/sysfs.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/platform_data/ina2xx.h>
+
+#include <linux/util_macros.h>
+
+/* INA2XX registers definition */
+#define INA2XX_CONFIG 0x00
+#define INA2XX_SHUNT_VOLTAGE 0x01 /* readonly */
+#define INA2XX_BUS_VOLTAGE 0x02 /* readonly */
+#define INA2XX_POWER 0x03 /* readonly */
+#define INA2XX_CURRENT 0x04 /* readonly */
+#define INA2XX_CALIBRATION 0x05
+
+#define INA226_ALERT_MASK 0x06
+#define INA266_CVRF BIT(3)
+
+#define INA2XX_MAX_REGISTERS 8
+
+/* settings - depend on use case */
+#define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */
+#define INA226_CONFIG_DEFAULT 0x4327
+#define INA226_DEFAULT_AVG 4
+#define INA226_DEFAULT_IT 1110
+
+#define INA2XX_RSHUNT_DEFAULT 10000
+
+/*
+ * bit mask for reading the averaging setting in the configuration register
+ * FIXME: use regmap_fields.
+ */
+#define INA2XX_MODE_MASK GENMASK(3, 0)
+
+#define INA226_AVG_MASK GENMASK(11, 9)
+#define INA226_SHIFT_AVG(val) ((val) << 9)
+
+/* Integration time for VBus */
+#define INA226_ITB_MASK GENMASK(8, 6)
+#define INA226_SHIFT_ITB(val) ((val) << 6)
+
+/* Integration time for VShunt */
+#define INA226_ITS_MASK GENMASK(5, 3)
+#define INA226_SHIFT_ITS(val) ((val) << 3)
+
+/* Cosmetic macro giving the sampling period for a full P=UxI cycle */
+#define SAMPLING_PERIOD(c) ((c->int_time_vbus + c->int_time_vshunt) \
+ * c->avg)
+
+static bool ina2xx_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return (reg == INA2XX_CONFIG) || (reg > INA2XX_CURRENT);
+}
+
+static bool ina2xx_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return (reg != INA2XX_CONFIG);
+}
+
+static inline bool is_signed_reg(unsigned int reg)
+{
+ return (reg == INA2XX_SHUNT_VOLTAGE) || (reg == INA2XX_CURRENT);
+}
+
+static const struct regmap_config ina2xx_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = INA2XX_MAX_REGISTERS,
+ .writeable_reg = ina2xx_is_writeable_reg,
+ .volatile_reg = ina2xx_is_volatile_reg,
+};
+
+enum ina2xx_ids { ina219, ina226 };
+
+struct ina2xx_config {
+ u16 config_default;
+ int calibration_factor;
+ int shunt_div;
+ int bus_voltage_shift;
+ int bus_voltage_lsb; /* uV */
+ int power_lsb; /* uW */
+};
+
+struct ina2xx_chip_info {
+ struct regmap *regmap;
+ struct task_struct *task;
+ const struct ina2xx_config *config;
+ struct mutex state_lock;
+ unsigned int shunt_resistor;
+ int avg;
+ s64 prev_ns; /* track buffer capture time, check for underruns*/
+ int int_time_vbus; /* Bus voltage integration time uS */
+ int int_time_vshunt; /* Shunt voltage integration time uS */
+ bool allow_async_readout;
+};
+
+static const struct ina2xx_config ina2xx_config[] = {
+ [ina219] = {
+ .config_default = INA219_CONFIG_DEFAULT,
+ .calibration_factor = 40960000,
+ .shunt_div = 100,
+ .bus_voltage_shift = 3,
+ .bus_voltage_lsb = 4000,
+ .power_lsb = 20000,
+ },
+ [ina226] = {
+ .config_default = INA226_CONFIG_DEFAULT,
+ .calibration_factor = 5120000,
+ .shunt_div = 400,
+ .bus_voltage_shift = 0,
+ .bus_voltage_lsb = 1250,
+ .power_lsb = 25000,
+ },
+};
+
+static int ina2xx_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret;
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+ unsigned int regval;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = regmap_read(chip->regmap, chan->address, &regval);
+ if (ret < 0)
+ return ret;
+
+ if (is_signed_reg(chan->address))
+ *val = (s16) regval;
+ else
+ *val = regval;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *val = chip->avg;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_INT_TIME:
+ *val = 0;
+ if (chan->address == INA2XX_SHUNT_VOLTAGE)
+ *val2 = chip->int_time_vshunt;
+ else
+ *val2 = chip->int_time_vbus;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ /*
+ * Sample freq is read only, it is a consequence of
+ * 1/AVG*(CT_bus+CT_shunt).
+ */
+ *val = DIV_ROUND_CLOSEST(1000000, SAMPLING_PERIOD(chip));
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->address) {
+ case INA2XX_SHUNT_VOLTAGE:
+ /* processed (mV) = raw*1000/shunt_div */
+ *val2 = chip->config->shunt_div;
+ *val = 1000;
+ return IIO_VAL_FRACTIONAL;
+
+ case INA2XX_BUS_VOLTAGE:
+ /* processed (mV) = raw*lsb (uV) / (1000 << shift) */
+ *val = chip->config->bus_voltage_lsb;
+ *val2 = 1000 << chip->config->bus_voltage_shift;
+ return IIO_VAL_FRACTIONAL;
+
+ case INA2XX_POWER:
+ /* processed (mW) = raw*lsb (uW) / 1000 */
+ *val = chip->config->power_lsb;
+ *val2 = 1000;
+ return IIO_VAL_FRACTIONAL;
+
+ case INA2XX_CURRENT:
+ /* processed (mA) = raw (mA) */
+ *val = 1;
+ return IIO_VAL_INT;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * Available averaging rates for ina226. The indices correspond with
+ * the bit values expected by the chip (according to the ina226 datasheet,
+ * table 3 AVG bit settings, found at
+ * http://www.ti.com/lit/ds/symlink/ina226.pdf.
+ */
+static const int ina226_avg_tab[] = { 1, 4, 16, 64, 128, 256, 512, 1024 };
+
+static int ina226_set_average(struct ina2xx_chip_info *chip, unsigned int val,
+ unsigned int *config)
+{
+ int bits;
+
+ if (val > 1024 || val < 1)
+ return -EINVAL;
+
+ bits = find_closest(val, ina226_avg_tab,
+ ARRAY_SIZE(ina226_avg_tab));
+
+ chip->avg = ina226_avg_tab[bits];
+
+ *config &= ~INA226_AVG_MASK;
+ *config |= INA226_SHIFT_AVG(bits) & INA226_AVG_MASK;
+
+ return 0;
+}
+
+/* Conversion times in uS */
+static const int ina226_conv_time_tab[] = { 140, 204, 332, 588, 1100,
+ 2116, 4156, 8244 };
+
+static int ina226_set_int_time_vbus(struct ina2xx_chip_info *chip,
+ unsigned int val_us, unsigned int *config)
+{
+ int bits;
+
+ if (val_us > 8244 || val_us < 140)
+ return -EINVAL;
+
+ bits = find_closest(val_us, ina226_conv_time_tab,
+ ARRAY_SIZE(ina226_conv_time_tab));
+
+ chip->int_time_vbus = ina226_conv_time_tab[bits];
+
+ *config &= ~INA226_ITB_MASK;
+ *config |= INA226_SHIFT_ITB(bits) & INA226_ITB_MASK;
+
+ return 0;
+}
+
+static int ina226_set_int_time_vshunt(struct ina2xx_chip_info *chip,
+ unsigned int val_us, unsigned int *config)
+{
+ int bits;
+
+ if (val_us > 8244 || val_us < 140)
+ return -EINVAL;
+
+ bits = find_closest(val_us, ina226_conv_time_tab,
+ ARRAY_SIZE(ina226_conv_time_tab));
+
+ chip->int_time_vshunt = ina226_conv_time_tab[bits];
+
+ *config &= ~INA226_ITS_MASK;
+ *config |= INA226_SHIFT_ITS(bits) & INA226_ITS_MASK;
+
+ return 0;
+}
+
+static int ina2xx_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+ int ret;
+ unsigned int config, tmp;
+
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+
+ mutex_lock(&chip->state_lock);
+
+ ret = regmap_read(chip->regmap, INA2XX_CONFIG, &config);
+ if (ret < 0)
+ goto _err;
+
+ tmp = config;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ ret = ina226_set_average(chip, val, &tmp);
+ break;
+
+ case IIO_CHAN_INFO_INT_TIME:
+ if (chan->address == INA2XX_SHUNT_VOLTAGE)
+ ret = ina226_set_int_time_vshunt(chip, val2, &tmp);
+ else
+ ret = ina226_set_int_time_vbus(chip, val2, &tmp);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ if (!ret && (tmp != config))
+ ret = regmap_write(chip->regmap, INA2XX_CONFIG, tmp);
+_err:
+ mutex_unlock(&chip->state_lock);
+
+ return ret;
+}
+
+
+static ssize_t ina2xx_allow_async_readout_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
+
+ return sprintf(buf, "%d\n", chip->allow_async_readout);
+}
+
+static ssize_t ina2xx_allow_async_readout_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
+ bool val;
+ int ret;
+
+ ret = strtobool((const char *) buf, &val);
+ if (ret)
+ return ret;
+
+ chip->allow_async_readout = val;
+
+ return len;
+}
+
+static int set_shunt_resistor(struct ina2xx_chip_info *chip, unsigned int val)
+{
+ if (val <= 0 || val > chip->config->calibration_factor)
+ return -EINVAL;
+
+ chip->shunt_resistor = val;
+ return 0;
+}
+
+static ssize_t ina2xx_shunt_resistor_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
+
+ return sprintf(buf, "%d\n", chip->shunt_resistor);
+}
+
+static ssize_t ina2xx_shunt_resistor_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul((const char *) buf, 10, &val);
+ if (ret)
+ return ret;
+
+ ret = set_shunt_resistor(chip, val);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+#define INA2XX_CHAN(_type, _index, _address) { \
+ .type = (_type), \
+ .address = (_address), \
+ .indexed = 1, \
+ .channel = (_index), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \
+ | BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .scan_index = (_index), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_CPU, \
+ } \
+}
+
+/*
+ * Sampling Freq is a consequence of the integration times of
+ * the Voltage channels.
+ */
+#define INA2XX_CHAN_VOLTAGE(_index, _address) { \
+ .type = IIO_VOLTAGE, \
+ .address = (_address), \
+ .indexed = 1, \
+ .channel = (_index), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_INT_TIME), \
+ .scan_index = (_index), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_LE, \
+ } \
+}
+
+static const struct iio_chan_spec ina2xx_channels[] = {
+ INA2XX_CHAN_VOLTAGE(0, INA2XX_SHUNT_VOLTAGE),
+ INA2XX_CHAN_VOLTAGE(1, INA2XX_BUS_VOLTAGE),
+ INA2XX_CHAN(IIO_POWER, 2, INA2XX_POWER),
+ INA2XX_CHAN(IIO_CURRENT, 3, INA2XX_CURRENT),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+};
+
+static int ina2xx_work_buffer(struct iio_dev *indio_dev)
+{
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+ unsigned short data[8];
+ int bit, ret, i = 0;
+ unsigned long buffer_us, elapsed_us;
+ s64 time_a, time_b;
+ unsigned int alert;
+
+ time_a = iio_get_time_ns();
+
+ /*
+ * Because the timer thread and the chip conversion clock
+ * are asynchronous, the period difference will eventually
+ * result in reading V[k-1] again, or skip V[k] at time Tk.
+ * In order to resync the timer with the conversion process
+ * we check the ConVersionReadyFlag.
+ * On hardware that supports using the ALERT pin to toggle a
+ * GPIO a triggered buffer could be used instead.
+ * For now, we pay for that extra read of the ALERT register
+ */
+ if (!chip->allow_async_readout)
+ do {
+ ret = regmap_read(chip->regmap, INA226_ALERT_MASK,
+ &alert);
+ if (ret < 0)
+ return ret;
+
+ alert &= INA266_CVRF;
+ trace_printk("Conversion ready: %d\n", !!alert);
+
+ } while (!alert);
+
+ /*
+ * Single register reads: bulk_read will not work with ina226
+ * as there is no auto-increment of the address register for
+ * data length longer than 16bits.
+ */
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ unsigned int val;
+
+ ret = regmap_read(chip->regmap,
+ INA2XX_SHUNT_VOLTAGE + bit, &val);
+ if (ret < 0)
+ return ret;
+
+ data[i++] = val;
+ }
+
+ time_b = iio_get_time_ns();
+
+ iio_push_to_buffers_with_timestamp(indio_dev,
+ (unsigned int *)data, time_a);
+
+ buffer_us = (unsigned long)(time_b - time_a) / 1000;
+ elapsed_us = (unsigned long)(time_a - chip->prev_ns) / 1000;
+
+ trace_printk("uS: elapsed: %lu, buf: %lu\n", elapsed_us, buffer_us);
+
+ chip->prev_ns = time_a;
+
+ return buffer_us;
+};
+
+static int ina2xx_capture_thread(void *data)
+{
+ struct iio_dev *indio_dev = (struct iio_dev *)data;
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+ unsigned int sampling_us = SAMPLING_PERIOD(chip);
+ int buffer_us;
+
+ /*
+ * Poll a bit faster than the chip internal Fs, in case
+ * we wish to sync with the conversion ready flag.
+ */
+ if (!chip->allow_async_readout)
+ sampling_us -= 200;
+
+ do {
+ buffer_us = ina2xx_work_buffer(indio_dev);
+ if (buffer_us < 0)
+ return buffer_us;
+
+ if (sampling_us > buffer_us)
+ udelay(sampling_us - buffer_us);
+
+ } while (!kthread_should_stop());
+
+ return 0;
+}
+
+static int ina2xx_buffer_enable(struct iio_dev *indio_dev)
+{
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+ unsigned int sampling_us = SAMPLING_PERIOD(chip);
+
+ trace_printk("Enabling buffer w/ scan_mask %02x, freq = %d, avg =%u\n",
+ (unsigned int)(*indio_dev->active_scan_mask),
+ 1000000/sampling_us, chip->avg);
+
+ trace_printk("Expected work period: %u us\n", sampling_us);
+ trace_printk("Async readout mode: %d\n", chip->allow_async_readout);
+
+ chip->prev_ns = iio_get_time_ns();
+
+ chip->task = kthread_run(ina2xx_capture_thread, (void *)indio_dev,
+ "%s:%d-%uus", indio_dev->name, indio_dev->id,
+ sampling_us);
+
+ return PTR_ERR_OR_ZERO(chip->task);
+}
+
+static int ina2xx_buffer_disable(struct iio_dev *indio_dev)
+{
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+
+ if (chip->task) {
+ kthread_stop(chip->task);
+ chip->task = NULL;
+ }
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ina2xx_setup_ops = {
+ .postenable = &ina2xx_buffer_enable,
+ .predisable = &ina2xx_buffer_disable,
+};
+
+static int ina2xx_debug_reg(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval, unsigned *readval)
+{
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+
+ if (!readval)
+ return regmap_write(chip->regmap, reg, writeval);
+
+ return regmap_read(chip->regmap, reg, readval);
+}
+
+/* Possible integration times for vshunt and vbus */
+static IIO_CONST_ATTR_INT_TIME_AVAIL \
+ ("0.000140 0.000204 0.000332 0.000588 0.001100 0.002116 0.004156 0.008244");
+
+static IIO_DEVICE_ATTR(in_allow_async_readout, S_IRUGO | S_IWUSR,
+ ina2xx_allow_async_readout_show,
+ ina2xx_allow_async_readout_store, 0);
+
+static IIO_DEVICE_ATTR(in_shunt_resistor, S_IRUGO | S_IWUSR,
+ ina2xx_shunt_resistor_show,
+ ina2xx_shunt_resistor_store, 0);
+
+static struct attribute *ina2xx_attributes[] = {
+ &iio_dev_attr_in_allow_async_readout.dev_attr.attr,
+ &iio_const_attr_integration_time_available.dev_attr.attr,
+ &iio_dev_attr_in_shunt_resistor.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ina2xx_attribute_group = {
+ .attrs = ina2xx_attributes,
+};
+
+static const struct iio_info ina2xx_info = {
+ .debugfs_reg_access = &ina2xx_debug_reg,
+ .read_raw = &ina2xx_read_raw,
+ .write_raw = &ina2xx_write_raw,
+ .attrs = &ina2xx_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+/* Initialize the configuration and calibration registers. */
+static int ina2xx_init(struct ina2xx_chip_info *chip, unsigned int config)
+{
+ u16 regval;
+ int ret = regmap_write(chip->regmap, INA2XX_CONFIG, config);
+
+ if (ret < 0)
+ return ret;
+ /*
+ * Set current LSB to 1mA, shunt is in uOhms
+ * (equation 13 in datasheet). We hardcode a Current_LSB
+ * of 1.0 x10-6. The only remaining parameter is RShunt.
+ * There is no need to expose the CALIBRATION register
+ * to the user for now.
+ */
+ regval = DIV_ROUND_CLOSEST(chip->config->calibration_factor,
+ chip->shunt_resistor);
+
+ return regmap_write(chip->regmap, INA2XX_CALIBRATION, regval);
+}
+
+static int ina2xx_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ina2xx_chip_info *chip;
+ struct iio_dev *indio_dev;
+ struct iio_buffer *buffer;
+ int ret;
+ unsigned int val;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ chip = iio_priv(indio_dev);
+
+ chip->config = &ina2xx_config[id->driver_data];
+
+ if (of_property_read_u32(client->dev.of_node,
+ "shunt-resistor", &val) < 0) {
+ struct ina2xx_platform_data *pdata =
+ dev_get_platdata(&client->dev);
+
+ if (pdata)
+ val = pdata->shunt_uohms;
+ else
+ val = INA2XX_RSHUNT_DEFAULT;
+ }
+
+ ret = set_shunt_resistor(chip, val);
+ if (ret)
+ return ret;
+
+ mutex_init(&chip->state_lock);
+
+ /* This is only used for device removal purposes. */
+ i2c_set_clientdata(client, indio_dev);
+
+ indio_dev->name = id->name;
+ indio_dev->channels = ina2xx_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ina2xx_channels);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &ina2xx_info;
+ indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+
+ chip->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ dev_err(&client->dev, "failed to allocate register map\n");
+ return PTR_ERR(chip->regmap);
+ }
+
+ /* Patch the current config register with default. */
+ val = chip->config->config_default;
+
+ if (id->driver_data == ina226) {
+ ina226_set_average(chip, INA226_DEFAULT_AVG, &val);
+ ina226_set_int_time_vbus(chip, INA226_DEFAULT_IT, &val);
+ ina226_set_int_time_vshunt(chip, INA226_DEFAULT_IT, &val);
+ }
+
+ ret = ina2xx_init(chip, val);
+ if (ret < 0) {
+ dev_err(&client->dev, "error configuring the device: %d\n",
+ ret);
+ return -ENODEV;
+ }
+
+ buffer = devm_iio_kfifo_allocate(&indio_dev->dev);
+ if (!buffer)
+ return -ENOMEM;
+
+ indio_dev->setup_ops = &ina2xx_setup_ops;
+
+ iio_device_attach_buffer(indio_dev, buffer);
+
+ return iio_device_register(indio_dev);
+}
+
+
+static int ina2xx_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ /* Powerdown */
+ return regmap_update_bits(chip->regmap, INA2XX_CONFIG,
+ INA2XX_MODE_MASK, 0);
+}
+
+
+static const struct i2c_device_id ina2xx_id[] = {
+ {"ina219", ina219},
+ {"ina220", ina219},
+ {"ina226", ina226},
+ {"ina230", ina226},
+ {"ina231", ina226},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ina2xx_id);
+
+static struct i2c_driver ina2xx_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ },
+ .probe = ina2xx_probe,
+ .remove = ina2xx_remove,
+ .id_table = ina2xx_id,
+};
+
+module_i2c_driver(ina2xx_driver);
+
+MODULE_AUTHOR("Marc Titinger <marc.titinger@baylibre.com>");
+MODULE_DESCRIPTION("Texas Instruments INA2XX ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c
index 8569c8e1f4b2..d1c05f6eed18 100644
--- a/drivers/iio/adc/mcp320x.c
+++ b/drivers/iio/adc/mcp320x.c
@@ -354,6 +354,7 @@ static int mcp320x_remove(struct spi_device *spi)
#if defined(CONFIG_OF)
static const struct of_device_id mcp320x_dt_ids[] = {
+ /* NOTE: The use of compatibles with no vendor prefix is deprecated. */
{
.compatible = "mcp3001",
.data = &mcp320x_chip_infos[mcp3001],
@@ -382,6 +383,33 @@ static const struct of_device_id mcp320x_dt_ids[] = {
.compatible = "mcp3301",
.data = &mcp320x_chip_infos[mcp3301],
}, {
+ .compatible = "microchip,mcp3001",
+ .data = &mcp320x_chip_infos[mcp3001],
+ }, {
+ .compatible = "microchip,mcp3002",
+ .data = &mcp320x_chip_infos[mcp3002],
+ }, {
+ .compatible = "microchip,mcp3004",
+ .data = &mcp320x_chip_infos[mcp3004],
+ }, {
+ .compatible = "microchip,mcp3008",
+ .data = &mcp320x_chip_infos[mcp3008],
+ }, {
+ .compatible = "microchip,mcp3201",
+ .data = &mcp320x_chip_infos[mcp3201],
+ }, {
+ .compatible = "microchip,mcp3202",
+ .data = &mcp320x_chip_infos[mcp3202],
+ }, {
+ .compatible = "microchip,mcp3204",
+ .data = &mcp320x_chip_infos[mcp3204],
+ }, {
+ .compatible = "microchip,mcp3208",
+ .data = &mcp320x_chip_infos[mcp3208],
+ }, {
+ .compatible = "microchip,mcp3301",
+ .data = &mcp320x_chip_infos[mcp3301],
+ }, {
}
};
MODULE_DEVICE_TABLE(of, mcp320x_dt_ids);
diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c
index 3555122008b4..6eca7aea8a37 100644
--- a/drivers/iio/adc/mcp3422.c
+++ b/drivers/iio/adc/mcp3422.c
@@ -305,6 +305,10 @@ static const struct attribute_group mcp3422_attribute_group = {
.attrs = mcp3422_attributes,
};
+static const struct iio_chan_spec mcp3421_channels[] = {
+ MCP3422_CHAN(0),
+};
+
static const struct iio_chan_spec mcp3422_channels[] = {
MCP3422_CHAN(0),
MCP3422_CHAN(1),
@@ -352,6 +356,10 @@ static int mcp3422_probe(struct i2c_client *client,
indio_dev->info = &mcp3422_info;
switch (adc->id) {
+ case 1:
+ indio_dev->channels = mcp3421_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mcp3421_channels);
+ break;
case 2:
case 3:
case 6:
@@ -383,6 +391,7 @@ static int mcp3422_probe(struct i2c_client *client,
}
static const struct i2c_device_id mcp3422_id[] = {
+ { "mcp3421", 1 },
{ "mcp3422", 2 },
{ "mcp3423", 3 },
{ "mcp3424", 4 },
diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c
new file mode 100644
index 000000000000..f42eb8a7d21f
--- /dev/null
+++ b/drivers/iio/adc/palmas_gpadc.c
@@ -0,0 +1,859 @@
+/*
+ * palmas-adc.c -- TI PALMAS GPADC.
+ *
+ * Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
+ *
+ * Author: Pradeep Goudagunta <pgoudagunta@nvidia.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 version 2.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/pm.h>
+#include <linux/mfd/palmas.h>
+#include <linux/completion.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+
+#define MOD_NAME "palmas-gpadc"
+#define PALMAS_ADC_CONVERSION_TIMEOUT (msecs_to_jiffies(5000))
+#define PALMAS_TO_BE_CALCULATED 0
+#define PALMAS_GPADC_TRIMINVALID -1
+
+struct palmas_gpadc_info {
+/* calibration codes and regs */
+ int x1; /* lower ideal code */
+ int x2; /* higher ideal code */
+ int v1; /* expected lower volt reading */
+ int v2; /* expected higher volt reading */
+ u8 trim1_reg; /* register number for lower trim */
+ u8 trim2_reg; /* register number for upper trim */
+ int gain; /* calculated from above (after reading trim regs) */
+ int offset; /* calculated from above (after reading trim regs) */
+ int gain_error; /* calculated from above (after reading trim regs) */
+ bool is_uncalibrated; /* if channel has calibration data */
+};
+
+#define PALMAS_ADC_INFO(_chan, _x1, _x2, _v1, _v2, _t1, _t2, _is_uncalibrated) \
+ [PALMAS_ADC_CH_##_chan] = { \
+ .x1 = _x1, \
+ .x2 = _x2, \
+ .v1 = _v1, \
+ .v2 = _v2, \
+ .gain = PALMAS_TO_BE_CALCULATED, \
+ .offset = PALMAS_TO_BE_CALCULATED, \
+ .gain_error = PALMAS_TO_BE_CALCULATED, \
+ .trim1_reg = PALMAS_GPADC_TRIM##_t1, \
+ .trim2_reg = PALMAS_GPADC_TRIM##_t2, \
+ .is_uncalibrated = _is_uncalibrated \
+ }
+
+static struct palmas_gpadc_info palmas_gpadc_info[] = {
+ PALMAS_ADC_INFO(IN0, 2064, 3112, 630, 950, 1, 2, false),
+ PALMAS_ADC_INFO(IN1, 2064, 3112, 630, 950, 1, 2, false),
+ PALMAS_ADC_INFO(IN2, 2064, 3112, 1260, 1900, 3, 4, false),
+ PALMAS_ADC_INFO(IN3, 2064, 3112, 630, 950, 1, 2, false),
+ PALMAS_ADC_INFO(IN4, 2064, 3112, 630, 950, 1, 2, false),
+ PALMAS_ADC_INFO(IN5, 2064, 3112, 630, 950, 1, 2, false),
+ PALMAS_ADC_INFO(IN6, 2064, 3112, 2520, 3800, 5, 6, false),
+ PALMAS_ADC_INFO(IN7, 2064, 3112, 2520, 3800, 7, 8, false),
+ PALMAS_ADC_INFO(IN8, 2064, 3112, 3150, 4750, 9, 10, false),
+ PALMAS_ADC_INFO(IN9, 2064, 3112, 5670, 8550, 11, 12, false),
+ PALMAS_ADC_INFO(IN10, 2064, 3112, 3465, 5225, 13, 14, false),
+ PALMAS_ADC_INFO(IN11, 0, 0, 0, 0, INVALID, INVALID, true),
+ PALMAS_ADC_INFO(IN12, 0, 0, 0, 0, INVALID, INVALID, true),
+ PALMAS_ADC_INFO(IN13, 0, 0, 0, 0, INVALID, INVALID, true),
+ PALMAS_ADC_INFO(IN14, 2064, 3112, 3645, 5225, 15, 16, false),
+ PALMAS_ADC_INFO(IN15, 0, 0, 0, 0, INVALID, INVALID, true),
+};
+
+/**
+ * struct palmas_gpadc - the palmas_gpadc structure
+ * @ch0_current: channel 0 current source setting
+ * 0: 0 uA
+ * 1: 5 uA
+ * 2: 15 uA
+ * 3: 20 uA
+ * @ch3_current: channel 0 current source setting
+ * 0: 0 uA
+ * 1: 10 uA
+ * 2: 400 uA
+ * 3: 800 uA
+ * @extended_delay: enable the gpadc extended delay mode
+ * @auto_conversion_period: define the auto_conversion_period
+ *
+ * This is the palmas_gpadc structure to store run-time information
+ * and pointers for this driver instance.
+ */
+
+struct palmas_gpadc {
+ struct device *dev;
+ struct palmas *palmas;
+ u8 ch0_current;
+ u8 ch3_current;
+ bool extended_delay;
+ int irq;
+ int irq_auto_0;
+ int irq_auto_1;
+ struct palmas_gpadc_info *adc_info;
+ struct completion conv_completion;
+ struct palmas_adc_wakeup_property wakeup1_data;
+ struct palmas_adc_wakeup_property wakeup2_data;
+ bool wakeup1_enable;
+ bool wakeup2_enable;
+ int auto_conversion_period;
+};
+
+/*
+ * GPADC lock issue in AUTO mode.
+ * Impact: In AUTO mode, GPADC conversion can be locked after disabling AUTO
+ * mode feature.
+ * Details:
+ * When the AUTO mode is the only conversion mode enabled, if the AUTO
+ * mode feature is disabled with bit GPADC_AUTO_CTRL. AUTO_CONV1_EN = 0
+ * or bit GPADC_AUTO_CTRL. AUTO_CONV0_EN = 0 during a conversion, the
+ * conversion mechanism can be seen as locked meaning that all following
+ * conversion will give 0 as a result. Bit GPADC_STATUS.GPADC_AVAILABLE
+ * will stay at 0 meaning that GPADC is busy. An RT conversion can unlock
+ * the GPADC.
+ *
+ * Workaround(s):
+ * To avoid the lock mechanism, the workaround to follow before any stop
+ * conversion request is:
+ * Force the GPADC state machine to be ON by using the GPADC_CTRL1.
+ * GPADC_FORCE bit = 1
+ * Shutdown the GPADC AUTO conversion using
+ * GPADC_AUTO_CTRL.SHUTDOWN_CONV[01] = 0.
+ * After 100us, force the GPADC state machine to be OFF by using the
+ * GPADC_CTRL1. GPADC_FORCE bit = 0
+ */
+
+static int palmas_disable_auto_conversion(struct palmas_gpadc *adc)
+{
+ int ret;
+
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_CTRL1,
+ PALMAS_GPADC_CTRL1_GPADC_FORCE,
+ PALMAS_GPADC_CTRL1_GPADC_FORCE);
+ if (ret < 0) {
+ dev_err(adc->dev, "GPADC_CTRL1 update failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_AUTO_CTRL,
+ PALMAS_GPADC_AUTO_CTRL_SHUTDOWN_CONV1 |
+ PALMAS_GPADC_AUTO_CTRL_SHUTDOWN_CONV0,
+ 0);
+ if (ret < 0) {
+ dev_err(adc->dev, "AUTO_CTRL update failed: %d\n", ret);
+ return ret;
+ }
+
+ udelay(100);
+
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_CTRL1,
+ PALMAS_GPADC_CTRL1_GPADC_FORCE, 0);
+ if (ret < 0)
+ dev_err(adc->dev, "GPADC_CTRL1 update failed: %d\n", ret);
+
+ return ret;
+}
+
+static irqreturn_t palmas_gpadc_irq(int irq, void *data)
+{
+ struct palmas_gpadc *adc = data;
+
+ complete(&adc->conv_completion);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t palmas_gpadc_irq_auto(int irq, void *data)
+{
+ struct palmas_gpadc *adc = data;
+
+ dev_dbg(adc->dev, "Threshold interrupt %d occurs\n", irq);
+ palmas_disable_auto_conversion(adc);
+
+ return IRQ_HANDLED;
+}
+
+static int palmas_gpadc_start_mask_interrupt(struct palmas_gpadc *adc,
+ bool mask)
+{
+ int ret;
+
+ if (!mask)
+ ret = palmas_update_bits(adc->palmas, PALMAS_INTERRUPT_BASE,
+ PALMAS_INT3_MASK,
+ PALMAS_INT3_MASK_GPADC_EOC_SW, 0);
+ else
+ ret = palmas_update_bits(adc->palmas, PALMAS_INTERRUPT_BASE,
+ PALMAS_INT3_MASK,
+ PALMAS_INT3_MASK_GPADC_EOC_SW,
+ PALMAS_INT3_MASK_GPADC_EOC_SW);
+ if (ret < 0)
+ dev_err(adc->dev, "GPADC INT MASK update failed: %d\n", ret);
+
+ return ret;
+}
+
+static int palmas_gpadc_enable(struct palmas_gpadc *adc, int adc_chan,
+ int enable)
+{
+ unsigned int mask, val;
+ int ret;
+
+ if (enable) {
+ val = (adc->extended_delay
+ << PALMAS_GPADC_RT_CTRL_EXTEND_DELAY_SHIFT);
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_RT_CTRL,
+ PALMAS_GPADC_RT_CTRL_EXTEND_DELAY, val);
+ if (ret < 0) {
+ dev_err(adc->dev, "RT_CTRL update failed: %d\n", ret);
+ return ret;
+ }
+
+ mask = (PALMAS_GPADC_CTRL1_CURRENT_SRC_CH0_MASK |
+ PALMAS_GPADC_CTRL1_CURRENT_SRC_CH3_MASK |
+ PALMAS_GPADC_CTRL1_GPADC_FORCE);
+ val = (adc->ch0_current
+ << PALMAS_GPADC_CTRL1_CURRENT_SRC_CH0_SHIFT);
+ val |= (adc->ch3_current
+ << PALMAS_GPADC_CTRL1_CURRENT_SRC_CH3_SHIFT);
+ val |= PALMAS_GPADC_CTRL1_GPADC_FORCE;
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_CTRL1, mask, val);
+ if (ret < 0) {
+ dev_err(adc->dev,
+ "Failed to update current setting: %d\n", ret);
+ return ret;
+ }
+
+ mask = (PALMAS_GPADC_SW_SELECT_SW_CONV0_SEL_MASK |
+ PALMAS_GPADC_SW_SELECT_SW_CONV_EN);
+ val = (adc_chan | PALMAS_GPADC_SW_SELECT_SW_CONV_EN);
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_SW_SELECT, mask, val);
+ if (ret < 0) {
+ dev_err(adc->dev, "SW_SELECT update failed: %d\n", ret);
+ return ret;
+ }
+ } else {
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_SW_SELECT, 0);
+ if (ret < 0)
+ dev_err(adc->dev, "SW_SELECT write failed: %d\n", ret);
+
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_CTRL1,
+ PALMAS_GPADC_CTRL1_GPADC_FORCE, 0);
+ if (ret < 0) {
+ dev_err(adc->dev, "CTRL1 update failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int palmas_gpadc_read_prepare(struct palmas_gpadc *adc, int adc_chan)
+{
+ int ret;
+
+ ret = palmas_gpadc_enable(adc, adc_chan, true);
+ if (ret < 0)
+ return ret;
+
+ return palmas_gpadc_start_mask_interrupt(adc, 0);
+}
+
+static void palmas_gpadc_read_done(struct palmas_gpadc *adc, int adc_chan)
+{
+ palmas_gpadc_start_mask_interrupt(adc, 1);
+ palmas_gpadc_enable(adc, adc_chan, false);
+}
+
+static int palmas_gpadc_calibrate(struct palmas_gpadc *adc, int adc_chan)
+{
+ int k;
+ int d1;
+ int d2;
+ int ret;
+ int gain;
+ int x1 = adc->adc_info[adc_chan].x1;
+ int x2 = adc->adc_info[adc_chan].x2;
+ int v1 = adc->adc_info[adc_chan].v1;
+ int v2 = adc->adc_info[adc_chan].v2;
+
+ ret = palmas_read(adc->palmas, PALMAS_TRIM_GPADC_BASE,
+ adc->adc_info[adc_chan].trim1_reg, &d1);
+ if (ret < 0) {
+ dev_err(adc->dev, "TRIM read failed: %d\n", ret);
+ goto scrub;
+ }
+
+ ret = palmas_read(adc->palmas, PALMAS_TRIM_GPADC_BASE,
+ adc->adc_info[adc_chan].trim2_reg, &d2);
+ if (ret < 0) {
+ dev_err(adc->dev, "TRIM read failed: %d\n", ret);
+ goto scrub;
+ }
+
+ /* gain error calculation */
+ k = (1000 + (1000 * (d2 - d1)) / (x2 - x1));
+
+ /* gain calculation */
+ gain = ((v2 - v1) * 1000) / (x2 - x1);
+
+ adc->adc_info[adc_chan].gain_error = k;
+ adc->adc_info[adc_chan].gain = gain;
+ /* offset Calculation */
+ adc->adc_info[adc_chan].offset = (d1 * 1000) - ((k - 1000) * x1);
+
+scrub:
+ return ret;
+}
+
+static int palmas_gpadc_start_conversion(struct palmas_gpadc *adc, int adc_chan)
+{
+ unsigned int val;
+ int ret;
+
+ init_completion(&adc->conv_completion);
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_SW_SELECT,
+ PALMAS_GPADC_SW_SELECT_SW_START_CONV0,
+ PALMAS_GPADC_SW_SELECT_SW_START_CONV0);
+ if (ret < 0) {
+ dev_err(adc->dev, "SELECT_SW_START write failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = wait_for_completion_timeout(&adc->conv_completion,
+ PALMAS_ADC_CONVERSION_TIMEOUT);
+ if (ret == 0) {
+ dev_err(adc->dev, "conversion not completed\n");
+ return -ETIMEDOUT;
+ }
+
+ ret = palmas_bulk_read(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_SW_CONV0_LSB, &val, 2);
+ if (ret < 0) {
+ dev_err(adc->dev, "SW_CONV0_LSB read failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = val & 0xFFF;
+
+ return ret;
+}
+
+static int palmas_gpadc_get_calibrated_code(struct palmas_gpadc *adc,
+ int adc_chan, int val)
+{
+ if (!adc->adc_info[adc_chan].is_uncalibrated)
+ val = (val*1000 - adc->adc_info[adc_chan].offset) /
+ adc->adc_info[adc_chan].gain_error;
+
+ if (val < 0) {
+ dev_err(adc->dev, "Mismatch with calibration\n");
+ return 0;
+ }
+
+ val = (val * adc->adc_info[adc_chan].gain) / 1000;
+
+ return val;
+}
+
+static int palmas_gpadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long mask)
+{
+ struct palmas_gpadc *adc = iio_priv(indio_dev);
+ int adc_chan = chan->channel;
+ int ret = 0;
+
+ if (adc_chan > PALMAS_ADC_CH_MAX)
+ return -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = palmas_gpadc_read_prepare(adc, adc_chan);
+ if (ret < 0)
+ goto out;
+
+ ret = palmas_gpadc_start_conversion(adc, adc_chan);
+ if (ret < 0) {
+ dev_err(adc->dev,
+ "ADC start conversion failed\n");
+ goto out;
+ }
+
+ if (mask == IIO_CHAN_INFO_PROCESSED)
+ ret = palmas_gpadc_get_calibrated_code(
+ adc, adc_chan, ret);
+
+ *val = ret;
+
+ ret = IIO_VAL_INT;
+ goto out;
+ }
+
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+
+out:
+ palmas_gpadc_read_done(adc, adc_chan);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static const struct iio_info palmas_gpadc_iio_info = {
+ .read_raw = palmas_gpadc_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+#define PALMAS_ADC_CHAN_IIO(chan, _type, chan_info) \
+{ \
+ .datasheet_name = PALMAS_DATASHEET_NAME(chan), \
+ .type = _type, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(chan_info), \
+ .indexed = 1, \
+ .channel = PALMAS_ADC_CH_##chan, \
+}
+
+static const struct iio_chan_spec palmas_gpadc_iio_channel[] = {
+ PALMAS_ADC_CHAN_IIO(IN0, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN1, IIO_TEMP, IIO_CHAN_INFO_RAW),
+ PALMAS_ADC_CHAN_IIO(IN2, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN3, IIO_TEMP, IIO_CHAN_INFO_RAW),
+ PALMAS_ADC_CHAN_IIO(IN4, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN5, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN6, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN7, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN8, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN9, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN10, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN11, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN12, IIO_TEMP, IIO_CHAN_INFO_RAW),
+ PALMAS_ADC_CHAN_IIO(IN13, IIO_TEMP, IIO_CHAN_INFO_RAW),
+ PALMAS_ADC_CHAN_IIO(IN14, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN15, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+};
+
+static int palmas_gpadc_get_adc_dt_data(struct platform_device *pdev,
+ struct palmas_gpadc_platform_data **gpadc_pdata)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct palmas_gpadc_platform_data *gp_data;
+ int ret;
+ u32 pval;
+
+ gp_data = devm_kzalloc(&pdev->dev, sizeof(*gp_data), GFP_KERNEL);
+ if (!gp_data)
+ return -ENOMEM;
+
+ ret = of_property_read_u32(np, "ti,channel0-current-microamp", &pval);
+ if (!ret)
+ gp_data->ch0_current = pval;
+
+ ret = of_property_read_u32(np, "ti,channel3-current-microamp", &pval);
+ if (!ret)
+ gp_data->ch3_current = pval;
+
+ gp_data->extended_delay = of_property_read_bool(np,
+ "ti,enable-extended-delay");
+
+ *gpadc_pdata = gp_data;
+
+ return 0;
+}
+
+static int palmas_gpadc_probe(struct platform_device *pdev)
+{
+ struct palmas_gpadc *adc;
+ struct palmas_platform_data *pdata;
+ struct palmas_gpadc_platform_data *gpadc_pdata = NULL;
+ struct iio_dev *indio_dev;
+ int ret, i;
+
+ pdata = dev_get_platdata(pdev->dev.parent);
+
+ if (pdata && pdata->gpadc_pdata)
+ gpadc_pdata = pdata->gpadc_pdata;
+
+ if (!gpadc_pdata && pdev->dev.of_node) {
+ ret = palmas_gpadc_get_adc_dt_data(pdev, &gpadc_pdata);
+ if (ret < 0)
+ return ret;
+ }
+ if (!gpadc_pdata)
+ return -EINVAL;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "iio_device_alloc failed\n");
+ return -ENOMEM;
+ }
+
+ adc = iio_priv(indio_dev);
+ adc->dev = &pdev->dev;
+ adc->palmas = dev_get_drvdata(pdev->dev.parent);
+ adc->adc_info = palmas_gpadc_info;
+ init_completion(&adc->conv_completion);
+ dev_set_drvdata(&pdev->dev, indio_dev);
+
+ adc->auto_conversion_period = gpadc_pdata->auto_conversion_period_ms;
+ adc->irq = palmas_irq_get_virq(adc->palmas, PALMAS_GPADC_EOC_SW_IRQ);
+ if (adc->irq < 0) {
+ dev_err(adc->dev,
+ "get virq failed: %d\n", adc->irq);
+ ret = adc->irq;
+ goto out;
+ }
+ ret = request_threaded_irq(adc->irq, NULL,
+ palmas_gpadc_irq,
+ IRQF_ONESHOT | IRQF_EARLY_RESUME, dev_name(adc->dev),
+ adc);
+ if (ret < 0) {
+ dev_err(adc->dev,
+ "request irq %d failed: %d\n", adc->irq, ret);
+ goto out;
+ }
+
+ if (gpadc_pdata->adc_wakeup1_data) {
+ memcpy(&adc->wakeup1_data, gpadc_pdata->adc_wakeup1_data,
+ sizeof(adc->wakeup1_data));
+ adc->wakeup1_enable = true;
+ adc->irq_auto_0 = platform_get_irq(pdev, 1);
+ ret = request_threaded_irq(adc->irq_auto_0, NULL,
+ palmas_gpadc_irq_auto,
+ IRQF_ONESHOT | IRQF_EARLY_RESUME,
+ "palmas-adc-auto-0", adc);
+ if (ret < 0) {
+ dev_err(adc->dev, "request auto0 irq %d failed: %d\n",
+ adc->irq_auto_0, ret);
+ goto out_irq_free;
+ }
+ }
+
+ if (gpadc_pdata->adc_wakeup2_data) {
+ memcpy(&adc->wakeup2_data, gpadc_pdata->adc_wakeup2_data,
+ sizeof(adc->wakeup2_data));
+ adc->wakeup2_enable = true;
+ adc->irq_auto_1 = platform_get_irq(pdev, 2);
+ ret = request_threaded_irq(adc->irq_auto_1, NULL,
+ palmas_gpadc_irq_auto,
+ IRQF_ONESHOT | IRQF_EARLY_RESUME,
+ "palmas-adc-auto-1", adc);
+ if (ret < 0) {
+ dev_err(adc->dev, "request auto1 irq %d failed: %d\n",
+ adc->irq_auto_1, ret);
+ goto out_irq_auto0_free;
+ }
+ }
+
+ /* set the current source 0 (value 0/5/15/20 uA => 0..3) */
+ if (gpadc_pdata->ch0_current <= 1)
+ adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_0;
+ else if (gpadc_pdata->ch0_current <= 5)
+ adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_5;
+ else if (gpadc_pdata->ch0_current <= 15)
+ adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_15;
+ else
+ adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_20;
+
+ /* set the current source 3 (value 0/10/400/800 uA => 0..3) */
+ if (gpadc_pdata->ch3_current <= 1)
+ adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_0;
+ else if (gpadc_pdata->ch3_current <= 10)
+ adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_10;
+ else if (gpadc_pdata->ch3_current <= 400)
+ adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_400;
+ else
+ adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_800;
+
+ adc->extended_delay = gpadc_pdata->extended_delay;
+
+ indio_dev->name = MOD_NAME;
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &palmas_gpadc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = palmas_gpadc_iio_channel;
+ indio_dev->num_channels = ARRAY_SIZE(palmas_gpadc_iio_channel);
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(adc->dev, "iio_device_register() failed: %d\n", ret);
+ goto out_irq_auto1_free;
+ }
+
+ device_set_wakeup_capable(&pdev->dev, 1);
+ for (i = 0; i < PALMAS_ADC_CH_MAX; i++) {
+ if (!(adc->adc_info[i].is_uncalibrated))
+ palmas_gpadc_calibrate(adc, i);
+ }
+
+ if (adc->wakeup1_enable || adc->wakeup2_enable)
+ device_wakeup_enable(&pdev->dev);
+
+ return 0;
+
+out_irq_auto1_free:
+ if (gpadc_pdata->adc_wakeup2_data)
+ free_irq(adc->irq_auto_1, adc);
+out_irq_auto0_free:
+ if (gpadc_pdata->adc_wakeup1_data)
+ free_irq(adc->irq_auto_0, adc);
+out_irq_free:
+ free_irq(adc->irq, adc);
+out:
+ return ret;
+}
+
+static int palmas_gpadc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(&pdev->dev);
+ struct palmas_gpadc *adc = iio_priv(indio_dev);
+
+ if (adc->wakeup1_enable || adc->wakeup2_enable)
+ device_wakeup_disable(&pdev->dev);
+ iio_device_unregister(indio_dev);
+ free_irq(adc->irq, adc);
+ if (adc->wakeup1_enable)
+ free_irq(adc->irq_auto_0, adc);
+ if (adc->wakeup2_enable)
+ free_irq(adc->irq_auto_1, adc);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int palmas_adc_wakeup_configure(struct palmas_gpadc *adc)
+{
+ int adc_period, conv;
+ int i;
+ int ch0 = 0, ch1 = 0;
+ int thres;
+ int ret;
+
+ adc_period = adc->auto_conversion_period;
+ for (i = 0; i < 16; ++i) {
+ if (((1000 * (1 << i)) / 32) < adc_period)
+ continue;
+ }
+ if (i > 0)
+ i--;
+ adc_period = i;
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_AUTO_CTRL,
+ PALMAS_GPADC_AUTO_CTRL_COUNTER_CONV_MASK,
+ adc_period);
+ if (ret < 0) {
+ dev_err(adc->dev, "AUTO_CTRL write failed: %d\n", ret);
+ return ret;
+ }
+
+ conv = 0;
+ if (adc->wakeup1_enable) {
+ int polarity;
+
+ ch0 = adc->wakeup1_data.adc_channel_number;
+ conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN;
+ if (adc->wakeup1_data.adc_high_threshold > 0) {
+ thres = adc->wakeup1_data.adc_high_threshold;
+ polarity = 0;
+ } else {
+ thres = adc->wakeup1_data.adc_low_threshold;
+ polarity = PALMAS_GPADC_THRES_CONV0_MSB_THRES_CONV0_POL;
+ }
+
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_THRES_CONV0_LSB, thres & 0xFF);
+ if (ret < 0) {
+ dev_err(adc->dev,
+ "THRES_CONV0_LSB write failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_THRES_CONV0_MSB,
+ ((thres >> 8) & 0xF) | polarity);
+ if (ret < 0) {
+ dev_err(adc->dev,
+ "THRES_CONV0_MSB write failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (adc->wakeup2_enable) {
+ int polarity;
+
+ ch1 = adc->wakeup2_data.adc_channel_number;
+ conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN;
+ if (adc->wakeup2_data.adc_high_threshold > 0) {
+ thres = adc->wakeup2_data.adc_high_threshold;
+ polarity = 0;
+ } else {
+ thres = adc->wakeup2_data.adc_low_threshold;
+ polarity = PALMAS_GPADC_THRES_CONV1_MSB_THRES_CONV1_POL;
+ }
+
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_THRES_CONV1_LSB, thres & 0xFF);
+ if (ret < 0) {
+ dev_err(adc->dev,
+ "THRES_CONV1_LSB write failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_THRES_CONV1_MSB,
+ ((thres >> 8) & 0xF) | polarity);
+ if (ret < 0) {
+ dev_err(adc->dev,
+ "THRES_CONV1_MSB write failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_AUTO_SELECT, (ch1 << 4) | ch0);
+ if (ret < 0) {
+ dev_err(adc->dev, "AUTO_SELECT write failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_AUTO_CTRL,
+ PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN |
+ PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN, conv);
+ if (ret < 0)
+ dev_err(adc->dev, "AUTO_CTRL write failed: %d\n", ret);
+
+ return ret;
+}
+
+static int palmas_adc_wakeup_reset(struct palmas_gpadc *adc)
+{
+ int ret;
+
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_AUTO_SELECT, 0);
+ if (ret < 0) {
+ dev_err(adc->dev, "AUTO_SELECT write failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_disable_auto_conversion(adc);
+ if (ret < 0)
+ dev_err(adc->dev, "Disable auto conversion failed: %d\n", ret);
+
+ return ret;
+}
+
+static int palmas_gpadc_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct palmas_gpadc *adc = iio_priv(indio_dev);
+ int wakeup = adc->wakeup1_enable || adc->wakeup2_enable;
+ int ret;
+
+ if (!device_may_wakeup(dev) || !wakeup)
+ return 0;
+
+ ret = palmas_adc_wakeup_configure(adc);
+ if (ret < 0)
+ return ret;
+
+ if (adc->wakeup1_enable)
+ enable_irq_wake(adc->irq_auto_0);
+
+ if (adc->wakeup2_enable)
+ enable_irq_wake(adc->irq_auto_1);
+
+ return 0;
+}
+
+static int palmas_gpadc_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct palmas_gpadc *adc = iio_priv(indio_dev);
+ int wakeup = adc->wakeup1_enable || adc->wakeup2_enable;
+ int ret;
+
+ if (!device_may_wakeup(dev) || !wakeup)
+ return 0;
+
+ ret = palmas_adc_wakeup_reset(adc);
+ if (ret < 0)
+ return ret;
+
+ if (adc->wakeup1_enable)
+ disable_irq_wake(adc->irq_auto_0);
+
+ if (adc->wakeup2_enable)
+ disable_irq_wake(adc->irq_auto_1);
+
+ return 0;
+};
+#endif
+
+static const struct dev_pm_ops palmas_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(palmas_gpadc_suspend,
+ palmas_gpadc_resume)
+};
+
+static const struct of_device_id of_palmas_gpadc_match_tbl[] = {
+ { .compatible = "ti,palmas-gpadc", },
+ { /* end */ }
+};
+MODULE_DEVICE_TABLE(of, of_palmas_gpadc_match_tbl);
+
+static struct platform_driver palmas_gpadc_driver = {
+ .probe = palmas_gpadc_probe,
+ .remove = palmas_gpadc_remove,
+ .driver = {
+ .name = MOD_NAME,
+ .pm = &palmas_pm_ops,
+ .of_match_table = of_palmas_gpadc_match_tbl,
+ },
+};
+
+static int __init palmas_gpadc_init(void)
+{
+ return platform_driver_register(&palmas_gpadc_driver);
+}
+module_init(palmas_gpadc_init);
+
+static void __exit palmas_gpadc_exit(void)
+{
+ platform_driver_unregister(&palmas_gpadc_driver);
+}
+module_exit(palmas_gpadc_exit);
+
+MODULE_DESCRIPTION("palmas GPADC driver");
+MODULE_AUTHOR("Pradeep Goudagunta<pgoudagunta@nvidia.com>");
+MODULE_ALIAS("platform:palmas-gpadc");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c
index 0c4618b4d515..c2babe50a0d8 100644
--- a/drivers/iio/adc/qcom-spmi-vadc.c
+++ b/drivers/iio/adc/qcom-spmi-vadc.c
@@ -839,8 +839,10 @@ static int vadc_get_dt_data(struct vadc_priv *vadc, struct device_node *node)
for_each_available_child_of_node(node, child) {
ret = vadc_get_dt_channel_data(vadc->dev, &prop, child);
- if (ret)
+ if (ret) {
+ of_node_put(child);
return ret;
+ }
vadc->chan_props[index] = prop;
diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c
index ff6f7f63c8d9..bc58867d6e8d 100644
--- a/drivers/iio/adc/ti-adc128s052.c
+++ b/drivers/iio/adc/ti-adc128s052.c
@@ -1,10 +1,11 @@
/*
* Copyright (C) 2014 Angelo Compagnucci <angelo.compagnucci@gmail.com>
*
- * Driver for Texas Instruments' ADC128S052 and ADC122S021 ADC chip.
+ * Driver for Texas Instruments' ADC128S052, ADC122S021 and ADC124S021 ADC chip.
* Datasheets can be found here:
* http://www.ti.com/lit/ds/symlink/adc128s052.pdf
* http://www.ti.com/lit/ds/symlink/adc122s021.pdf
+ * http://www.ti.com/lit/ds/symlink/adc124s021.pdf
*
* 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
@@ -114,9 +115,17 @@ static const struct iio_chan_spec adc122s021_channels[] = {
ADC128_VOLTAGE_CHANNEL(1),
};
+static const struct iio_chan_spec adc124s021_channels[] = {
+ ADC128_VOLTAGE_CHANNEL(0),
+ ADC128_VOLTAGE_CHANNEL(1),
+ ADC128_VOLTAGE_CHANNEL(2),
+ ADC128_VOLTAGE_CHANNEL(3),
+};
+
static const struct adc128_configuration adc128_config[] = {
{ adc128s052_channels, ARRAY_SIZE(adc128s052_channels) },
{ adc122s021_channels, ARRAY_SIZE(adc122s021_channels) },
+ { adc124s021_channels, ARRAY_SIZE(adc124s021_channels) },
};
static const struct iio_info adc128_info = {
@@ -177,6 +186,7 @@ static int adc128_remove(struct spi_device *spi)
static const struct of_device_id adc128_of_match[] = {
{ .compatible = "ti,adc128s052", },
{ .compatible = "ti,adc122s021", },
+ { .compatible = "ti,adc124s021", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, adc128_of_match);
@@ -184,6 +194,7 @@ MODULE_DEVICE_TABLE(of, adc128_of_match);
static const struct spi_device_id adc128_id[] = {
{ "adc128s052", 0}, /* index into adc128_config */
{ "adc122s021", 1},
+ { "adc124s021", 2},
{ }
};
MODULE_DEVICE_TABLE(spi, adc128_id);
diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c
new file mode 100644
index 000000000000..03e907028cb6
--- /dev/null
+++ b/drivers/iio/adc/ti-ads8688.c
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2015 Prevas A/S
+ *
+ * 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/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define ADS8688_CMD_REG(x) (x << 8)
+#define ADS8688_CMD_REG_NOOP 0x00
+#define ADS8688_CMD_REG_RST 0x85
+#define ADS8688_CMD_REG_MAN_CH(chan) (0xC0 | (4 * chan))
+#define ADS8688_CMD_DONT_CARE_BITS 16
+
+#define ADS8688_PROG_REG(x) (x << 9)
+#define ADS8688_PROG_REG_RANGE_CH(chan) (0x05 + chan)
+#define ADS8688_PROG_WR_BIT BIT(8)
+#define ADS8688_PROG_DONT_CARE_BITS 8
+
+#define ADS8688_REG_PLUSMINUS25VREF 0
+#define ADS8688_REG_PLUSMINUS125VREF 1
+#define ADS8688_REG_PLUSMINUS0625VREF 2
+#define ADS8688_REG_PLUS25VREF 5
+#define ADS8688_REG_PLUS125VREF 6
+
+#define ADS8688_VREF_MV 4096
+#define ADS8688_REALBITS 16
+
+/*
+ * enum ads8688_range - ADS8688 reference voltage range
+ * @ADS8688_PLUSMINUS25VREF: Device is configured for input range ±2.5 * VREF
+ * @ADS8688_PLUSMINUS125VREF: Device is configured for input range ±1.25 * VREF
+ * @ADS8688_PLUSMINUS0625VREF: Device is configured for input range ±0.625 * VREF
+ * @ADS8688_PLUS25VREF: Device is configured for input range 0 - 2.5 * VREF
+ * @ADS8688_PLUS125VREF: Device is configured for input range 0 - 1.25 * VREF
+ */
+enum ads8688_range {
+ ADS8688_PLUSMINUS25VREF,
+ ADS8688_PLUSMINUS125VREF,
+ ADS8688_PLUSMINUS0625VREF,
+ ADS8688_PLUS25VREF,
+ ADS8688_PLUS125VREF,
+};
+
+struct ads8688_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+};
+
+struct ads8688_state {
+ struct mutex lock;
+ const struct ads8688_chip_info *chip_info;
+ struct spi_device *spi;
+ struct regulator *reg;
+ unsigned int vref_mv;
+ enum ads8688_range range[8];
+ union {
+ __be32 d32;
+ u8 d8[4];
+ } data[2] ____cacheline_aligned;
+};
+
+enum ads8688_id {
+ ID_ADS8684,
+ ID_ADS8688,
+};
+
+struct ads8688_ranges {
+ enum ads8688_range range;
+ unsigned int scale;
+ int offset;
+ u8 reg;
+};
+
+static const struct ads8688_ranges ads8688_range_def[5] = {
+ {
+ .range = ADS8688_PLUSMINUS25VREF,
+ .scale = 76295,
+ .offset = -(1 << (ADS8688_REALBITS - 1)),
+ .reg = ADS8688_REG_PLUSMINUS25VREF,
+ }, {
+ .range = ADS8688_PLUSMINUS125VREF,
+ .scale = 38148,
+ .offset = -(1 << (ADS8688_REALBITS - 1)),
+ .reg = ADS8688_REG_PLUSMINUS125VREF,
+ }, {
+ .range = ADS8688_PLUSMINUS0625VREF,
+ .scale = 19074,
+ .offset = -(1 << (ADS8688_REALBITS - 1)),
+ .reg = ADS8688_REG_PLUSMINUS0625VREF,
+ }, {
+ .range = ADS8688_PLUS25VREF,
+ .scale = 38148,
+ .offset = 0,
+ .reg = ADS8688_REG_PLUS25VREF,
+ }, {
+ .range = ADS8688_PLUS125VREF,
+ .scale = 19074,
+ .offset = 0,
+ .reg = ADS8688_REG_PLUS125VREF,
+ }
+};
+
+static ssize_t ads8688_show_scales(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ads8688_state *st = iio_priv(dev_to_iio_dev(dev));
+
+ return sprintf(buf, "0.%09u 0.%09u 0.%09u\n",
+ ads8688_range_def[0].scale * st->vref_mv,
+ ads8688_range_def[1].scale * st->vref_mv,
+ ads8688_range_def[2].scale * st->vref_mv);
+}
+
+static ssize_t ads8688_show_offsets(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d %d\n", ads8688_range_def[0].offset,
+ ads8688_range_def[3].offset);
+}
+
+static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO,
+ ads8688_show_scales, NULL, 0);
+static IIO_DEVICE_ATTR(in_voltage_offset_available, S_IRUGO,
+ ads8688_show_offsets, NULL, 0);
+
+static struct attribute *ads8688_attributes[] = {
+ &iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage_offset_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ads8688_attribute_group = {
+ .attrs = ads8688_attributes,
+};
+
+#define ADS8688_CHAN(index) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \
+ | BIT(IIO_CHAN_INFO_SCALE) \
+ | BIT(IIO_CHAN_INFO_OFFSET), \
+}
+
+static const struct iio_chan_spec ads8684_channels[] = {
+ ADS8688_CHAN(0),
+ ADS8688_CHAN(1),
+ ADS8688_CHAN(2),
+ ADS8688_CHAN(3),
+};
+
+static const struct iio_chan_spec ads8688_channels[] = {
+ ADS8688_CHAN(0),
+ ADS8688_CHAN(1),
+ ADS8688_CHAN(2),
+ ADS8688_CHAN(3),
+ ADS8688_CHAN(4),
+ ADS8688_CHAN(5),
+ ADS8688_CHAN(6),
+ ADS8688_CHAN(7),
+};
+
+static int ads8688_prog_write(struct iio_dev *indio_dev, unsigned int addr,
+ unsigned int val)
+{
+ struct ads8688_state *st = iio_priv(indio_dev);
+ u32 tmp;
+
+ tmp = ADS8688_PROG_REG(addr) | ADS8688_PROG_WR_BIT | val;
+ tmp <<= ADS8688_PROG_DONT_CARE_BITS;
+ st->data[0].d32 = cpu_to_be32(tmp);
+
+ return spi_write(st->spi, &st->data[0].d8[1], 3);
+}
+
+static int ads8688_reset(struct iio_dev *indio_dev)
+{
+ struct ads8688_state *st = iio_priv(indio_dev);
+ u32 tmp;
+
+ tmp = ADS8688_CMD_REG(ADS8688_CMD_REG_RST);
+ tmp <<= ADS8688_CMD_DONT_CARE_BITS;
+ st->data[0].d32 = cpu_to_be32(tmp);
+
+ return spi_write(st->spi, &st->data[0].d8[0], 4);
+}
+
+static int ads8688_read(struct iio_dev *indio_dev, unsigned int chan)
+{
+ struct ads8688_state *st = iio_priv(indio_dev);
+ int ret;
+ u32 tmp;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->data[0].d8[0],
+ .len = 4,
+ .cs_change = 1,
+ }, {
+ .tx_buf = &st->data[1].d8[0],
+ .rx_buf = &st->data[1].d8[0],
+ .len = 4,
+ },
+ };
+
+ tmp = ADS8688_CMD_REG(ADS8688_CMD_REG_MAN_CH(chan));
+ tmp <<= ADS8688_CMD_DONT_CARE_BITS;
+ st->data[0].d32 = cpu_to_be32(tmp);
+
+ tmp = ADS8688_CMD_REG(ADS8688_CMD_REG_NOOP);
+ tmp <<= ADS8688_CMD_DONT_CARE_BITS;
+ st->data[1].d32 = cpu_to_be32(tmp);
+
+ ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
+ if (ret < 0)
+ return ret;
+
+ return be32_to_cpu(st->data[1].d32) & 0xffff;
+}
+
+static int ads8688_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long m)
+{
+ int ret, offset;
+ unsigned long scale_mv;
+
+ struct ads8688_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->lock);
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ads8688_read(indio_dev, chan->channel);
+ mutex_unlock(&st->lock);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ scale_mv = st->vref_mv;
+ scale_mv *= ads8688_range_def[st->range[chan->channel]].scale;
+ *val = 0;
+ *val2 = scale_mv;
+ mutex_unlock(&st->lock);
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OFFSET:
+ offset = ads8688_range_def[st->range[chan->channel]].offset;
+ *val = offset;
+ mutex_unlock(&st->lock);
+ return IIO_VAL_INT;
+ }
+ mutex_unlock(&st->lock);
+
+ return -EINVAL;
+}
+
+static int ads8688_write_reg_range(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ enum ads8688_range range)
+{
+ unsigned int tmp;
+ int ret;
+
+ tmp = ADS8688_PROG_REG_RANGE_CH(chan->channel);
+ ret = ads8688_prog_write(indio_dev, tmp, range);
+
+ return ret;
+}
+
+static int ads8688_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ads8688_state *st = iio_priv(indio_dev);
+ unsigned int scale = 0;
+ int ret = -EINVAL, i, offset = 0;
+
+ mutex_lock(&st->lock);
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ /* If the offset is 0 the ±2.5 * VREF mode is not available */
+ offset = ads8688_range_def[st->range[chan->channel]].offset;
+ if (offset == 0 && val2 == ads8688_range_def[0].scale * st->vref_mv) {
+ mutex_unlock(&st->lock);
+ return -EINVAL;
+ }
+
+ /* Lookup new mode */
+ for (i = 0; i < ARRAY_SIZE(ads8688_range_def); i++)
+ if (val2 == ads8688_range_def[i].scale * st->vref_mv &&
+ offset == ads8688_range_def[i].offset) {
+ ret = ads8688_write_reg_range(indio_dev, chan,
+ ads8688_range_def[i].reg);
+ break;
+ }
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ /*
+ * There are only two available offsets:
+ * 0 and -(1 << (ADS8688_REALBITS - 1))
+ */
+ if (!(ads8688_range_def[0].offset == val ||
+ ads8688_range_def[3].offset == val)) {
+ mutex_unlock(&st->lock);
+ return -EINVAL;
+ }
+
+ /*
+ * If the device are in ±2.5 * VREF mode, it's not allowed to
+ * switch to a mode where the offset is 0
+ */
+ if (val == 0 &&
+ st->range[chan->channel] == ADS8688_PLUSMINUS25VREF) {
+ mutex_unlock(&st->lock);
+ return -EINVAL;
+ }
+
+ scale = ads8688_range_def[st->range[chan->channel]].scale;
+
+ /* Lookup new mode */
+ for (i = 0; i < ARRAY_SIZE(ads8688_range_def); i++)
+ if (val == ads8688_range_def[i].offset &&
+ scale == ads8688_range_def[i].scale) {
+ ret = ads8688_write_reg_range(indio_dev, chan,
+ ads8688_range_def[i].reg);
+ break;
+ }
+ break;
+ }
+
+ if (!ret)
+ st->range[chan->channel] = ads8688_range_def[i].range;
+
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int ads8688_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OFFSET:
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info ads8688_info = {
+ .read_raw = &ads8688_read_raw,
+ .write_raw = &ads8688_write_raw,
+ .write_raw_get_fmt = &ads8688_write_raw_get_fmt,
+ .attrs = &ads8688_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct ads8688_chip_info ads8688_chip_info_tbl[] = {
+ [ID_ADS8684] = {
+ .channels = ads8684_channels,
+ .num_channels = ARRAY_SIZE(ads8684_channels),
+ },
+ [ID_ADS8688] = {
+ .channels = ads8688_channels,
+ .num_channels = ARRAY_SIZE(ads8688_channels),
+ },
+};
+
+static int ads8688_probe(struct spi_device *spi)
+{
+ struct ads8688_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ st->reg = devm_regulator_get_optional(&spi->dev, "vref");
+ if (!IS_ERR(st->reg)) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_voltage(st->reg);
+ if (ret < 0)
+ goto error_out;
+
+ st->vref_mv = ret / 1000;
+ } else {
+ /* Use internal reference */
+ st->vref_mv = ADS8688_VREF_MV;
+ }
+
+ st->chip_info = &ads8688_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+
+ spi->mode = SPI_MODE_1;
+
+ spi_set_drvdata(spi, indio_dev);
+
+ st->spi = spi;
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
+ indio_dev->info = &ads8688_info;
+
+ ads8688_reset(indio_dev);
+
+ mutex_init(&st->lock);
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_out;
+
+ return 0;
+
+error_out:
+ if (!IS_ERR_OR_NULL(st->reg))
+ regulator_disable(st->reg);
+
+ return ret;
+}
+
+static int ads8688_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ads8688_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ if (!IS_ERR_OR_NULL(st->reg))
+ regulator_disable(st->reg);
+
+ return 0;
+}
+
+static const struct spi_device_id ads8688_id[] = {
+ {"ads8684", ID_ADS8684},
+ {"ads8688", ID_ADS8688},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ads8688_id);
+
+static const struct of_device_id ads8688_of_match[] = {
+ { .compatible = "ti,ads8684" },
+ { .compatible = "ti,ads8688" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ads8688_of_match);
+
+static struct spi_driver ads8688_driver = {
+ .driver = {
+ .name = "ads8688",
+ .owner = THIS_MODULE,
+ },
+ .probe = ads8688_probe,
+ .remove = ads8688_remove,
+ .id_table = ads8688_id,
+};
+module_spi_driver(ads8688_driver);
+
+MODULE_AUTHOR("Sean Nyekjaer <sean.nyekjaer@prevas.dk>");
+MODULE_DESCRIPTION("Texas Instruments ADS8688 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c
index 02e636a1c49a..0a6beb3d99cb 100644
--- a/drivers/iio/adc/xilinx-xadc-core.c
+++ b/drivers/iio/adc/xilinx-xadc-core.c
@@ -803,7 +803,7 @@ err:
return ret;
}
-static struct iio_buffer_setup_ops xadc_buffer_ops = {
+static const struct iio_buffer_setup_ops xadc_buffer_ops = {
.preenable = &xadc_preenable,
.postenable = &iio_triggered_buffer_postenable,
.predisable = &iio_triggered_buffer_predisable,
diff --git a/drivers/iio/buffer/Kconfig b/drivers/iio/buffer/Kconfig
index 0a7b2fd3699b..4ffd3db7817f 100644
--- a/drivers/iio/buffer/Kconfig
+++ b/drivers/iio/buffer/Kconfig
@@ -9,6 +9,26 @@ config IIO_BUFFER_CB
Should be selected by any drivers that do in-kernel push
usage. That is, those where the data is pushed to the consumer.
+config IIO_BUFFER_DMA
+ tristate
+ help
+ Provides the generic IIO DMA buffer infrastructure that can be used by
+ drivers for devices with DMA support to implement the IIO buffer.
+
+ Should be selected by drivers that want to use the generic DMA buffer
+ infrastructure.
+
+config IIO_BUFFER_DMAENGINE
+ tristate
+ select IIO_BUFFER_DMA
+ help
+ Provides a bonding of the generic IIO DMA buffer infrastructure with the
+ DMAengine framework. This can be used by converter drivers with a DMA port
+ connected to an external DMA controller which is supported by the
+ DMAengine framework.
+
+ Should be selected by drivers that want to use this functionality.
+
config IIO_KFIFO_BUF
tristate "Industrial I/O buffering based on kfifo"
help
diff --git a/drivers/iio/buffer/Makefile b/drivers/iio/buffer/Makefile
index 4d193b9a9123..85beaae831ae 100644
--- a/drivers/iio/buffer/Makefile
+++ b/drivers/iio/buffer/Makefile
@@ -4,5 +4,7 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_IIO_BUFFER_CB) += industrialio-buffer-cb.o
+obj-$(CONFIG_IIO_BUFFER_DMA) += industrialio-buffer-dma.o
+obj-$(CONFIG_IIO_BUFFER_DMAENGINE) += industrialio-buffer-dmaengine.o
obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c
new file mode 100644
index 000000000000..212cbedc7abb
--- /dev/null
+++ b/drivers/iio/buffer/industrialio-buffer-dma.c
@@ -0,0 +1,683 @@
+/*
+ * Copyright 2013-2015 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/buffer-dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/sizes.h>
+
+/*
+ * For DMA buffers the storage is sub-divided into so called blocks. Each block
+ * has its own memory buffer. The size of the block is the granularity at which
+ * memory is exchanged between the hardware and the application. Increasing the
+ * basic unit of data exchange from one sample to one block decreases the
+ * management overhead that is associated with each sample. E.g. if we say the
+ * management overhead for one exchange is x and the unit of exchange is one
+ * sample the overhead will be x for each sample. Whereas when using a block
+ * which contains n samples the overhead per sample is reduced to x/n. This
+ * allows to achieve much higher samplerates than what can be sustained with
+ * the one sample approach.
+ *
+ * Blocks are exchanged between the DMA controller and the application via the
+ * means of two queues. The incoming queue and the outgoing queue. Blocks on the
+ * incoming queue are waiting for the DMA controller to pick them up and fill
+ * them with data. Block on the outgoing queue have been filled with data and
+ * are waiting for the application to dequeue them and read the data.
+ *
+ * A block can be in one of the following states:
+ * * Owned by the application. In this state the application can read data from
+ * the block.
+ * * On the incoming list: Blocks on the incoming list are queued up to be
+ * processed by the DMA controller.
+ * * Owned by the DMA controller: The DMA controller is processing the block
+ * and filling it with data.
+ * * On the outgoing list: Blocks on the outgoing list have been successfully
+ * processed by the DMA controller and contain data. They can be dequeued by
+ * the application.
+ * * Dead: A block that is dead has been marked as to be freed. It might still
+ * be owned by either the application or the DMA controller at the moment.
+ * But once they are done processing it instead of going to either the
+ * incoming or outgoing queue the block will be freed.
+ *
+ * In addition to this blocks are reference counted and the memory associated
+ * with both the block structure as well as the storage memory for the block
+ * will be freed when the last reference to the block is dropped. This means a
+ * block must not be accessed without holding a reference.
+ *
+ * The iio_dma_buffer implementation provides a generic infrastructure for
+ * managing the blocks.
+ *
+ * A driver for a specific piece of hardware that has DMA capabilities need to
+ * implement the submit() callback from the iio_dma_buffer_ops structure. This
+ * callback is supposed to initiate the DMA transfer copying data from the
+ * converter to the memory region of the block. Once the DMA transfer has been
+ * completed the driver must call iio_dma_buffer_block_done() for the completed
+ * block.
+ *
+ * Prior to this it must set the bytes_used field of the block contains
+ * the actual number of bytes in the buffer. Typically this will be equal to the
+ * size of the block, but if the DMA hardware has certain alignment requirements
+ * for the transfer length it might choose to use less than the full size. In
+ * either case it is expected that bytes_used is a multiple of the bytes per
+ * datum, i.e. the block must not contain partial samples.
+ *
+ * The driver must call iio_dma_buffer_block_done() for each block it has
+ * received through its submit_block() callback, even if it does not actually
+ * perform a DMA transfer for the block, e.g. because the buffer was disabled
+ * before the block transfer was started. In this case it should set bytes_used
+ * to 0.
+ *
+ * In addition it is recommended that a driver implements the abort() callback.
+ * It will be called when the buffer is disabled and can be used to cancel
+ * pending and stop active transfers.
+ *
+ * The specific driver implementation should use the default callback
+ * implementations provided by this module for the iio_buffer_access_funcs
+ * struct. It may overload some callbacks with custom variants if the hardware
+ * has special requirements that are not handled by the generic functions. If a
+ * driver chooses to overload a callback it has to ensure that the generic
+ * callback is called from within the custom callback.
+ */
+
+static void iio_buffer_block_release(struct kref *kref)
+{
+ struct iio_dma_buffer_block *block = container_of(kref,
+ struct iio_dma_buffer_block, kref);
+
+ WARN_ON(block->state != IIO_BLOCK_STATE_DEAD);
+
+ dma_free_coherent(block->queue->dev, PAGE_ALIGN(block->size),
+ block->vaddr, block->phys_addr);
+
+ iio_buffer_put(&block->queue->buffer);
+ kfree(block);
+}
+
+static void iio_buffer_block_get(struct iio_dma_buffer_block *block)
+{
+ kref_get(&block->kref);
+}
+
+static void iio_buffer_block_put(struct iio_dma_buffer_block *block)
+{
+ kref_put(&block->kref, iio_buffer_block_release);
+}
+
+/*
+ * dma_free_coherent can sleep, hence we need to take some special care to be
+ * able to drop a reference from an atomic context.
+ */
+static LIST_HEAD(iio_dma_buffer_dead_blocks);
+static DEFINE_SPINLOCK(iio_dma_buffer_dead_blocks_lock);
+
+static void iio_dma_buffer_cleanup_worker(struct work_struct *work)
+{
+ struct iio_dma_buffer_block *block, *_block;
+ LIST_HEAD(block_list);
+
+ spin_lock_irq(&iio_dma_buffer_dead_blocks_lock);
+ list_splice_tail_init(&iio_dma_buffer_dead_blocks, &block_list);
+ spin_unlock_irq(&iio_dma_buffer_dead_blocks_lock);
+
+ list_for_each_entry_safe(block, _block, &block_list, head)
+ iio_buffer_block_release(&block->kref);
+}
+static DECLARE_WORK(iio_dma_buffer_cleanup_work, iio_dma_buffer_cleanup_worker);
+
+static void iio_buffer_block_release_atomic(struct kref *kref)
+{
+ struct iio_dma_buffer_block *block;
+ unsigned long flags;
+
+ block = container_of(kref, struct iio_dma_buffer_block, kref);
+
+ spin_lock_irqsave(&iio_dma_buffer_dead_blocks_lock, flags);
+ list_add_tail(&block->head, &iio_dma_buffer_dead_blocks);
+ spin_unlock_irqrestore(&iio_dma_buffer_dead_blocks_lock, flags);
+
+ schedule_work(&iio_dma_buffer_cleanup_work);
+}
+
+/*
+ * Version of iio_buffer_block_put() that can be called from atomic context
+ */
+static void iio_buffer_block_put_atomic(struct iio_dma_buffer_block *block)
+{
+ kref_put(&block->kref, iio_buffer_block_release_atomic);
+}
+
+static struct iio_dma_buffer_queue *iio_buffer_to_queue(struct iio_buffer *buf)
+{
+ return container_of(buf, struct iio_dma_buffer_queue, buffer);
+}
+
+static struct iio_dma_buffer_block *iio_dma_buffer_alloc_block(
+ struct iio_dma_buffer_queue *queue, size_t size)
+{
+ struct iio_dma_buffer_block *block;
+
+ block = kzalloc(sizeof(*block), GFP_KERNEL);
+ if (!block)
+ return NULL;
+
+ block->vaddr = dma_alloc_coherent(queue->dev, PAGE_ALIGN(size),
+ &block->phys_addr, GFP_KERNEL);
+ if (!block->vaddr) {
+ kfree(block);
+ return NULL;
+ }
+
+ block->size = size;
+ block->state = IIO_BLOCK_STATE_DEQUEUED;
+ block->queue = queue;
+ INIT_LIST_HEAD(&block->head);
+ kref_init(&block->kref);
+
+ iio_buffer_get(&queue->buffer);
+
+ return block;
+}
+
+static void _iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
+{
+ struct iio_dma_buffer_queue *queue = block->queue;
+
+ /*
+ * The buffer has already been freed by the application, just drop the
+ * reference.
+ */
+ if (block->state != IIO_BLOCK_STATE_DEAD) {
+ block->state = IIO_BLOCK_STATE_DONE;
+ list_add_tail(&block->head, &queue->outgoing);
+ }
+}
+
+/**
+ * iio_dma_buffer_block_done() - Indicate that a block has been completed
+ * @block: The completed block
+ *
+ * Should be called when the DMA controller has finished handling the block to
+ * pass back ownership of the block to the queue.
+ */
+void iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
+{
+ struct iio_dma_buffer_queue *queue = block->queue;
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->list_lock, flags);
+ _iio_dma_buffer_block_done(block);
+ spin_unlock_irqrestore(&queue->list_lock, flags);
+
+ iio_buffer_block_put_atomic(block);
+ wake_up_interruptible_poll(&queue->buffer.pollq, POLLIN | POLLRDNORM);
+}
+EXPORT_SYMBOL_GPL(iio_dma_buffer_block_done);
+
+/**
+ * iio_dma_buffer_block_list_abort() - Indicate that a list block has been
+ * aborted
+ * @queue: Queue for which to complete blocks.
+ * @list: List of aborted blocks. All blocks in this list must be from @queue.
+ *
+ * Typically called from the abort() callback after the DMA controller has been
+ * stopped. This will set bytes_used to 0 for each block in the list and then
+ * hand the blocks back to the queue.
+ */
+void iio_dma_buffer_block_list_abort(struct iio_dma_buffer_queue *queue,
+ struct list_head *list)
+{
+ struct iio_dma_buffer_block *block, *_block;
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->list_lock, flags);
+ list_for_each_entry_safe(block, _block, list, head) {
+ list_del(&block->head);
+ block->bytes_used = 0;
+ _iio_dma_buffer_block_done(block);
+ iio_buffer_block_put_atomic(block);
+ }
+ spin_unlock_irqrestore(&queue->list_lock, flags);
+
+ wake_up_interruptible_poll(&queue->buffer.pollq, POLLIN | POLLRDNORM);
+}
+EXPORT_SYMBOL_GPL(iio_dma_buffer_block_list_abort);
+
+static bool iio_dma_block_reusable(struct iio_dma_buffer_block *block)
+{
+ /*
+ * If the core owns the block it can be re-used. This should be the
+ * default case when enabling the buffer, unless the DMA controller does
+ * not support abort and has not given back the block yet.
+ */
+ switch (block->state) {
+ case IIO_BLOCK_STATE_DEQUEUED:
+ case IIO_BLOCK_STATE_QUEUED:
+ case IIO_BLOCK_STATE_DONE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * iio_dma_buffer_request_update() - DMA buffer request_update callback
+ * @buffer: The buffer which to request an update
+ *
+ * Should be used as the iio_dma_buffer_request_update() callback for
+ * iio_buffer_access_ops struct for DMA buffers.
+ */
+int iio_dma_buffer_request_update(struct iio_buffer *buffer)
+{
+ struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer);
+ struct iio_dma_buffer_block *block;
+ bool try_reuse = false;
+ size_t size;
+ int ret = 0;
+ int i;
+
+ /*
+ * Split the buffer into two even parts. This is used as a double
+ * buffering scheme with usually one block at a time being used by the
+ * DMA and the other one by the application.
+ */
+ size = DIV_ROUND_UP(queue->buffer.bytes_per_datum *
+ queue->buffer.length, 2);
+
+ mutex_lock(&queue->lock);
+
+ /* Allocations are page aligned */
+ if (PAGE_ALIGN(queue->fileio.block_size) == PAGE_ALIGN(size))
+ try_reuse = true;
+
+ queue->fileio.block_size = size;
+ queue->fileio.active_block = NULL;
+
+ spin_lock_irq(&queue->list_lock);
+ for (i = 0; i < 2; i++) {
+ block = queue->fileio.blocks[i];
+
+ /* If we can't re-use it free it */
+ if (block && (!iio_dma_block_reusable(block) || !try_reuse))
+ block->state = IIO_BLOCK_STATE_DEAD;
+ }
+
+ /*
+ * At this point all blocks are either owned by the core or marked as
+ * dead. This means we can reset the lists without having to fear
+ * corrution.
+ */
+ INIT_LIST_HEAD(&queue->outgoing);
+ spin_unlock_irq(&queue->list_lock);
+
+ INIT_LIST_HEAD(&queue->incoming);
+
+ for (i = 0; i < 2; i++) {
+ if (queue->fileio.blocks[i]) {
+ block = queue->fileio.blocks[i];
+ if (block->state == IIO_BLOCK_STATE_DEAD) {
+ /* Could not reuse it */
+ iio_buffer_block_put(block);
+ block = NULL;
+ } else {
+ block->size = size;
+ }
+ } else {
+ block = NULL;
+ }
+
+ if (!block) {
+ block = iio_dma_buffer_alloc_block(queue, size);
+ if (!block) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+ queue->fileio.blocks[i] = block;
+ }
+
+ block->state = IIO_BLOCK_STATE_QUEUED;
+ list_add_tail(&block->head, &queue->incoming);
+ }
+
+out_unlock:
+ mutex_unlock(&queue->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_dma_buffer_request_update);
+
+static void iio_dma_buffer_submit_block(struct iio_dma_buffer_queue *queue,
+ struct iio_dma_buffer_block *block)
+{
+ int ret;
+
+ /*
+ * If the hardware has already been removed we put the block into
+ * limbo. It will neither be on the incoming nor outgoing list, nor will
+ * it ever complete. It will just wait to be freed eventually.
+ */
+ if (!queue->ops)
+ return;
+
+ block->state = IIO_BLOCK_STATE_ACTIVE;
+ iio_buffer_block_get(block);
+ ret = queue->ops->submit(queue, block);
+ if (ret) {
+ /*
+ * This is a bit of a problem and there is not much we can do
+ * other then wait for the buffer to be disabled and re-enabled
+ * and try again. But it should not really happen unless we run
+ * out of memory or something similar.
+ *
+ * TODO: Implement support in the IIO core to allow buffers to
+ * notify consumers that something went wrong and the buffer
+ * should be disabled.
+ */
+ iio_buffer_block_put(block);
+ }
+}
+
+/**
+ * iio_dma_buffer_enable() - Enable DMA buffer
+ * @buffer: IIO buffer to enable
+ * @indio_dev: IIO device the buffer is attached to
+ *
+ * Needs to be called when the device that the buffer is attached to starts
+ * sampling. Typically should be the iio_buffer_access_ops enable callback.
+ *
+ * This will allocate the DMA buffers and start the DMA transfers.
+ */
+int iio_dma_buffer_enable(struct iio_buffer *buffer,
+ struct iio_dev *indio_dev)
+{
+ struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer);
+ struct iio_dma_buffer_block *block, *_block;
+
+ mutex_lock(&queue->lock);
+ queue->active = true;
+ list_for_each_entry_safe(block, _block, &queue->incoming, head) {
+ list_del(&block->head);
+ iio_dma_buffer_submit_block(queue, block);
+ }
+ mutex_unlock(&queue->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iio_dma_buffer_enable);
+
+/**
+ * iio_dma_buffer_disable() - Disable DMA buffer
+ * @buffer: IIO DMA buffer to disable
+ * @indio_dev: IIO device the buffer is attached to
+ *
+ * Needs to be called when the device that the buffer is attached to stops
+ * sampling. Typically should be the iio_buffer_access_ops disable callback.
+ */
+int iio_dma_buffer_disable(struct iio_buffer *buffer,
+ struct iio_dev *indio_dev)
+{
+ struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer);
+
+ mutex_lock(&queue->lock);
+ queue->active = false;
+
+ if (queue->ops && queue->ops->abort)
+ queue->ops->abort(queue);
+ mutex_unlock(&queue->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iio_dma_buffer_disable);
+
+static void iio_dma_buffer_enqueue(struct iio_dma_buffer_queue *queue,
+ struct iio_dma_buffer_block *block)
+{
+ if (block->state == IIO_BLOCK_STATE_DEAD) {
+ iio_buffer_block_put(block);
+ } else if (queue->active) {
+ iio_dma_buffer_submit_block(queue, block);
+ } else {
+ block->state = IIO_BLOCK_STATE_QUEUED;
+ list_add_tail(&block->head, &queue->incoming);
+ }
+}
+
+static struct iio_dma_buffer_block *iio_dma_buffer_dequeue(
+ struct iio_dma_buffer_queue *queue)
+{
+ struct iio_dma_buffer_block *block;
+
+ spin_lock_irq(&queue->list_lock);
+ block = list_first_entry_or_null(&queue->outgoing, struct
+ iio_dma_buffer_block, head);
+ if (block != NULL) {
+ list_del(&block->head);
+ block->state = IIO_BLOCK_STATE_DEQUEUED;
+ }
+ spin_unlock_irq(&queue->list_lock);
+
+ return block;
+}
+
+/**
+ * iio_dma_buffer_read() - DMA buffer read callback
+ * @buffer: Buffer to read form
+ * @n: Number of bytes to read
+ * @user_buffer: Userspace buffer to copy the data to
+ *
+ * Should be used as the read_first_n callback for iio_buffer_access_ops
+ * struct for DMA buffers.
+ */
+int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
+ char __user *user_buffer)
+{
+ struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer);
+ struct iio_dma_buffer_block *block;
+ int ret;
+
+ if (n < buffer->bytes_per_datum)
+ return -EINVAL;
+
+ mutex_lock(&queue->lock);
+
+ if (!queue->fileio.active_block) {
+ block = iio_dma_buffer_dequeue(queue);
+ if (block == NULL) {
+ ret = 0;
+ goto out_unlock;
+ }
+ queue->fileio.pos = 0;
+ queue->fileio.active_block = block;
+ } else {
+ block = queue->fileio.active_block;
+ }
+
+ n = rounddown(n, buffer->bytes_per_datum);
+ if (n > block->bytes_used - queue->fileio.pos)
+ n = block->bytes_used - queue->fileio.pos;
+
+ if (copy_to_user(user_buffer, block->vaddr + queue->fileio.pos, n)) {
+ ret = -EFAULT;
+ goto out_unlock;
+ }
+
+ queue->fileio.pos += n;
+
+ if (queue->fileio.pos == block->bytes_used) {
+ queue->fileio.active_block = NULL;
+ iio_dma_buffer_enqueue(queue, block);
+ }
+
+ ret = n;
+
+out_unlock:
+ mutex_unlock(&queue->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_dma_buffer_read);
+
+/**
+ * iio_dma_buffer_data_available() - DMA buffer data_available callback
+ * @buf: Buffer to check for data availability
+ *
+ * Should be used as the data_available callback for iio_buffer_access_ops
+ * struct for DMA buffers.
+ */
+size_t iio_dma_buffer_data_available(struct iio_buffer *buf)
+{
+ struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buf);
+ struct iio_dma_buffer_block *block;
+ size_t data_available = 0;
+
+ /*
+ * For counting the available bytes we'll use the size of the block not
+ * the number of actual bytes available in the block. Otherwise it is
+ * possible that we end up with a value that is lower than the watermark
+ * but won't increase since all blocks are in use.
+ */
+
+ mutex_lock(&queue->lock);
+ if (queue->fileio.active_block)
+ data_available += queue->fileio.active_block->size;
+
+ spin_lock_irq(&queue->list_lock);
+ list_for_each_entry(block, &queue->outgoing, head)
+ data_available += block->size;
+ spin_unlock_irq(&queue->list_lock);
+ mutex_unlock(&queue->lock);
+
+ return data_available;
+}
+EXPORT_SYMBOL_GPL(iio_dma_buffer_data_available);
+
+/**
+ * iio_dma_buffer_set_bytes_per_datum() - DMA buffer set_bytes_per_datum callback
+ * @buffer: Buffer to set the bytes-per-datum for
+ * @bpd: The new bytes-per-datum value
+ *
+ * Should be used as the set_bytes_per_datum callback for iio_buffer_access_ops
+ * struct for DMA buffers.
+ */
+int iio_dma_buffer_set_bytes_per_datum(struct iio_buffer *buffer, size_t bpd)
+{
+ buffer->bytes_per_datum = bpd;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iio_dma_buffer_set_bytes_per_datum);
+
+/**
+ * iio_dma_buffer_set_length - DMA buffer set_length callback
+ * @buffer: Buffer to set the length for
+ * @length: The new buffer length
+ *
+ * Should be used as the set_length callback for iio_buffer_access_ops
+ * struct for DMA buffers.
+ */
+int iio_dma_buffer_set_length(struct iio_buffer *buffer, int length)
+{
+ /* Avoid an invalid state */
+ if (length < 2)
+ length = 2;
+ buffer->length = length;
+ buffer->watermark = length / 2;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iio_dma_buffer_set_length);
+
+/**
+ * iio_dma_buffer_init() - Initialize DMA buffer queue
+ * @queue: Buffer to initialize
+ * @dev: DMA device
+ * @ops: DMA buffer queue callback operations
+ *
+ * The DMA device will be used by the queue to do DMA memory allocations. So it
+ * should refer to the device that will perform the DMA to ensure that
+ * allocations are done from a memory region that can be accessed by the device.
+ */
+int iio_dma_buffer_init(struct iio_dma_buffer_queue *queue,
+ struct device *dev, const struct iio_dma_buffer_ops *ops)
+{
+ iio_buffer_init(&queue->buffer);
+ queue->buffer.length = PAGE_SIZE;
+ queue->buffer.watermark = queue->buffer.length / 2;
+ queue->dev = dev;
+ queue->ops = ops;
+
+ INIT_LIST_HEAD(&queue->incoming);
+ INIT_LIST_HEAD(&queue->outgoing);
+
+ mutex_init(&queue->lock);
+ spin_lock_init(&queue->list_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iio_dma_buffer_init);
+
+/**
+ * iio_dma_buffer_exit() - Cleanup DMA buffer queue
+ * @queue: Buffer to cleanup
+ *
+ * After this function has completed it is safe to free any resources that are
+ * associated with the buffer and are accessed inside the callback operations.
+ */
+void iio_dma_buffer_exit(struct iio_dma_buffer_queue *queue)
+{
+ unsigned int i;
+
+ mutex_lock(&queue->lock);
+
+ spin_lock_irq(&queue->list_lock);
+ for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) {
+ if (!queue->fileio.blocks[i])
+ continue;
+ queue->fileio.blocks[i]->state = IIO_BLOCK_STATE_DEAD;
+ }
+ INIT_LIST_HEAD(&queue->outgoing);
+ spin_unlock_irq(&queue->list_lock);
+
+ INIT_LIST_HEAD(&queue->incoming);
+
+ for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) {
+ if (!queue->fileio.blocks[i])
+ continue;
+ iio_buffer_block_put(queue->fileio.blocks[i]);
+ queue->fileio.blocks[i] = NULL;
+ }
+ queue->fileio.active_block = NULL;
+ queue->ops = NULL;
+
+ mutex_unlock(&queue->lock);
+}
+EXPORT_SYMBOL_GPL(iio_dma_buffer_exit);
+
+/**
+ * iio_dma_buffer_release() - Release final buffer resources
+ * @queue: Buffer to release
+ *
+ * Frees resources that can't yet be freed in iio_dma_buffer_exit(). Should be
+ * called in the buffers release callback implementation right before freeing
+ * the memory associated with the buffer.
+ */
+void iio_dma_buffer_release(struct iio_dma_buffer_queue *queue)
+{
+ mutex_destroy(&queue->lock);
+}
+EXPORT_SYMBOL_GPL(iio_dma_buffer_release);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("DMA buffer for the IIO framework");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
new file mode 100644
index 000000000000..ebdb838d3a1c
--- /dev/null
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2014-2015 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/buffer-dma.h>
+#include <linux/iio/buffer-dmaengine.h>
+
+/*
+ * The IIO DMAengine buffer combines the generic IIO DMA buffer infrastructure
+ * with the DMAengine framework. The generic IIO DMA buffer infrastructure is
+ * used to manage the buffer memory and implement the IIO buffer operations
+ * while the DMAengine framework is used to perform the DMA transfers. Combined
+ * this results in a device independent fully functional DMA buffer
+ * implementation that can be used by device drivers for peripherals which are
+ * connected to a DMA controller which has a DMAengine driver implementation.
+ */
+
+struct dmaengine_buffer {
+ struct iio_dma_buffer_queue queue;
+
+ struct dma_chan *chan;
+ struct list_head active;
+
+ size_t align;
+ size_t max_size;
+};
+
+static struct dmaengine_buffer *iio_buffer_to_dmaengine_buffer(
+ struct iio_buffer *buffer)
+{
+ return container_of(buffer, struct dmaengine_buffer, queue.buffer);
+}
+
+static void iio_dmaengine_buffer_block_done(void *data)
+{
+ struct iio_dma_buffer_block *block = data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&block->queue->list_lock, flags);
+ list_del(&block->head);
+ spin_unlock_irqrestore(&block->queue->list_lock, flags);
+ iio_dma_buffer_block_done(block);
+}
+
+static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
+ struct iio_dma_buffer_block *block)
+{
+ struct dmaengine_buffer *dmaengine_buffer =
+ iio_buffer_to_dmaengine_buffer(&queue->buffer);
+ struct dma_async_tx_descriptor *desc;
+ dma_cookie_t cookie;
+
+ block->bytes_used = min(block->size, dmaengine_buffer->max_size);
+ block->bytes_used = rounddown(block->bytes_used,
+ dmaengine_buffer->align);
+
+ desc = dmaengine_prep_slave_single(dmaengine_buffer->chan,
+ block->phys_addr, block->bytes_used, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!desc)
+ return -ENOMEM;
+
+ desc->callback = iio_dmaengine_buffer_block_done;
+ desc->callback_param = block;
+
+ cookie = dmaengine_submit(desc);
+ if (dma_submit_error(cookie))
+ return dma_submit_error(cookie);
+
+ spin_lock_irq(&dmaengine_buffer->queue.list_lock);
+ list_add_tail(&block->head, &dmaengine_buffer->active);
+ spin_unlock_irq(&dmaengine_buffer->queue.list_lock);
+
+ dma_async_issue_pending(dmaengine_buffer->chan);
+
+ return 0;
+}
+
+static void iio_dmaengine_buffer_abort(struct iio_dma_buffer_queue *queue)
+{
+ struct dmaengine_buffer *dmaengine_buffer =
+ iio_buffer_to_dmaengine_buffer(&queue->buffer);
+
+ dmaengine_terminate_all(dmaengine_buffer->chan);
+ /* FIXME: There is a slight chance of a race condition here.
+ * dmaengine_terminate_all() does not guarantee that all transfer
+ * callbacks have finished running. Need to introduce a
+ * dmaengine_terminate_all_sync().
+ */
+ iio_dma_buffer_block_list_abort(queue, &dmaengine_buffer->active);
+}
+
+static void iio_dmaengine_buffer_release(struct iio_buffer *buf)
+{
+ struct dmaengine_buffer *dmaengine_buffer =
+ iio_buffer_to_dmaengine_buffer(buf);
+
+ iio_dma_buffer_release(&dmaengine_buffer->queue);
+ kfree(dmaengine_buffer);
+}
+
+static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
+ .read_first_n = iio_dma_buffer_read,
+ .set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
+ .set_length = iio_dma_buffer_set_length,
+ .request_update = iio_dma_buffer_request_update,
+ .enable = iio_dma_buffer_enable,
+ .disable = iio_dma_buffer_disable,
+ .data_available = iio_dma_buffer_data_available,
+ .release = iio_dmaengine_buffer_release,
+
+ .modes = INDIO_BUFFER_HARDWARE,
+ .flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK,
+};
+
+static const struct iio_dma_buffer_ops iio_dmaengine_default_ops = {
+ .submit = iio_dmaengine_buffer_submit_block,
+ .abort = iio_dmaengine_buffer_abort,
+};
+
+/**
+ * iio_dmaengine_buffer_alloc() - Allocate new buffer which uses DMAengine
+ * @dev: Parent device for the buffer
+ * @channel: DMA channel name, typically "rx".
+ *
+ * This allocates a new IIO buffer which internally uses the DMAengine framework
+ * to perform its transfers. The parent device will be used to request the DMA
+ * channel.
+ *
+ * Once done using the buffer iio_dmaengine_buffer_free() should be used to
+ * release it.
+ */
+struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
+ const char *channel)
+{
+ struct dmaengine_buffer *dmaengine_buffer;
+ unsigned int width, src_width, dest_width;
+ struct dma_slave_caps caps;
+ struct dma_chan *chan;
+ int ret;
+
+ dmaengine_buffer = kzalloc(sizeof(*dmaengine_buffer), GFP_KERNEL);
+ if (!dmaengine_buffer)
+ return ERR_PTR(-ENOMEM);
+
+ chan = dma_request_slave_channel_reason(dev, channel);
+ if (IS_ERR(chan)) {
+ ret = PTR_ERR(chan);
+ goto err_free;
+ }
+
+ ret = dma_get_slave_caps(chan, &caps);
+ if (ret < 0)
+ goto err_free;
+
+ /* Needs to be aligned to the maximum of the minimums */
+ if (caps.src_addr_widths)
+ src_width = __ffs(caps.src_addr_widths);
+ else
+ src_width = 1;
+ if (caps.dst_addr_widths)
+ dest_width = __ffs(caps.dst_addr_widths);
+ else
+ dest_width = 1;
+ width = max(src_width, dest_width);
+
+ INIT_LIST_HEAD(&dmaengine_buffer->active);
+ dmaengine_buffer->chan = chan;
+ dmaengine_buffer->align = width;
+ dmaengine_buffer->max_size = dma_get_max_seg_size(chan->device->dev);
+
+ iio_dma_buffer_init(&dmaengine_buffer->queue, chan->device->dev,
+ &iio_dmaengine_default_ops);
+
+ dmaengine_buffer->queue.buffer.access = &iio_dmaengine_buffer_ops;
+
+ return &dmaengine_buffer->queue.buffer;
+
+err_free:
+ kfree(dmaengine_buffer);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(iio_dmaengine_buffer_alloc);
+
+/**
+ * iio_dmaengine_buffer_free() - Free dmaengine buffer
+ * @buffer: Buffer to free
+ *
+ * Frees a buffer previously allocated with iio_dmaengine_buffer_alloc().
+ */
+void iio_dmaengine_buffer_free(struct iio_buffer *buffer)
+{
+ struct dmaengine_buffer *dmaengine_buffer =
+ iio_buffer_to_dmaengine_buffer(buffer);
+
+ iio_dma_buffer_exit(&dmaengine_buffer->queue);
+ dma_release_channel(dmaengine_buffer->chan);
+
+ iio_buffer_put(buffer);
+}
+EXPORT_SYMBOL_GPL(iio_dmaengine_buffer_free);
diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig
index 3061b7299f0f..f16de61be46d 100644
--- a/drivers/iio/chemical/Kconfig
+++ b/drivers/iio/chemical/Kconfig
@@ -4,6 +4,14 @@
menu "Chemical Sensors"
+config IAQCORE
+ tristate "AMS iAQ-Core VOC sensors"
+ depends on I2C
+ help
+ Say Y here to build I2C interface support for the AMS
+ iAQ-Core Continuous/Pulsed VOC (Volatile Organic Compounds)
+ sensors
+
config VZ89X
tristate "SGX Sensortech MiCS VZ89X VOC sensor"
depends on I2C
diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile
index 7292f2ded587..167861fadfab 100644
--- a/drivers/iio/chemical/Makefile
+++ b/drivers/iio/chemical/Makefile
@@ -3,4 +3,5 @@
#
# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
obj-$(CONFIG_VZ89X) += vz89x.o
diff --git a/drivers/iio/chemical/ams-iaq-core.c b/drivers/iio/chemical/ams-iaq-core.c
new file mode 100644
index 000000000000..41a8e6f2e31d
--- /dev/null
+++ b/drivers/iio/chemical/ams-iaq-core.c
@@ -0,0 +1,200 @@
+/*
+ * ams-iaq-core.c - Support for AMS iAQ-Core VOC sensors
+ *
+ * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+
+#define AMS_IAQCORE_DATA_SIZE 9
+
+#define AMS_IAQCORE_VOC_CO2_IDX 0
+#define AMS_IAQCORE_VOC_RESISTANCE_IDX 1
+#define AMS_IAQCORE_VOC_TVOC_IDX 2
+
+struct ams_iaqcore_reading {
+ __be16 co2_ppm;
+ u8 status;
+ __be32 resistance;
+ __be16 voc_ppb;
+} __attribute__((__packed__));
+
+struct ams_iaqcore_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ unsigned long last_update;
+
+ struct ams_iaqcore_reading buffer;
+};
+
+static const struct iio_chan_spec ams_iaqcore_channels[] = {
+ {
+ .type = IIO_CONCENTRATION,
+ .channel2 = IIO_MOD_CO2,
+ .modified = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .address = AMS_IAQCORE_VOC_CO2_IDX,
+ },
+ {
+ .type = IIO_RESISTANCE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .address = AMS_IAQCORE_VOC_RESISTANCE_IDX,
+ },
+ {
+ .type = IIO_CONCENTRATION,
+ .channel2 = IIO_MOD_VOC,
+ .modified = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .address = AMS_IAQCORE_VOC_TVOC_IDX,
+ },
+};
+
+static int ams_iaqcore_read_measurement(struct ams_iaqcore_data *data)
+{
+ struct i2c_client *client = data->client;
+ int ret;
+
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = client->flags | I2C_M_RD,
+ .len = AMS_IAQCORE_DATA_SIZE,
+ .buf = (char *) &data->buffer,
+ };
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+
+ return (ret == AMS_IAQCORE_DATA_SIZE) ? 0 : ret;
+}
+
+static int ams_iaqcore_get_measurement(struct ams_iaqcore_data *data)
+{
+ int ret;
+
+ /* sensor can only be polled once a second max per datasheet */
+ if (!time_after(jiffies, data->last_update + HZ))
+ return 0;
+
+ ret = ams_iaqcore_read_measurement(data);
+ if (ret < 0)
+ return ret;
+
+ data->last_update = jiffies;
+
+ return 0;
+}
+
+static int ams_iaqcore_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct ams_iaqcore_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (mask != IIO_CHAN_INFO_PROCESSED)
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ ret = ams_iaqcore_get_measurement(data);
+
+ if (ret)
+ goto err_out;
+
+ switch (chan->address) {
+ case AMS_IAQCORE_VOC_CO2_IDX:
+ *val = 0;
+ *val2 = be16_to_cpu(data->buffer.co2_ppm);
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ case AMS_IAQCORE_VOC_RESISTANCE_IDX:
+ *val = be32_to_cpu(data->buffer.resistance);
+ ret = IIO_VAL_INT;
+ break;
+ case AMS_IAQCORE_VOC_TVOC_IDX:
+ *val = 0;
+ *val2 = be16_to_cpu(data->buffer.voc_ppb);
+ ret = IIO_VAL_INT_PLUS_NANO;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+err_out:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static const struct iio_info ams_iaqcore_info = {
+ .read_raw = ams_iaqcore_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int ams_iaqcore_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct iio_dev *indio_dev;
+ struct ams_iaqcore_data *data;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+
+ /* so initial reading will complete */
+ data->last_update = jiffies - HZ;
+ mutex_init(&data->lock);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &ams_iaqcore_info,
+ indio_dev->name = dev_name(&client->dev);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ indio_dev->channels = ams_iaqcore_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ams_iaqcore_channels);
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id ams_iaqcore_id[] = {
+ { "ams-iaq-core", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ams_iaqcore_id);
+
+static const struct of_device_id ams_iaqcore_dt_ids[] = {
+ { .compatible = "ams,iaq-core" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ams_iaqcore_dt_ids);
+
+static struct i2c_driver ams_iaqcore_driver = {
+ .driver = {
+ .name = "ams-iaq-core",
+ .of_match_table = of_match_ptr(ams_iaqcore_dt_ids),
+ },
+ .probe = ams_iaqcore_probe,
+ .id_table = ams_iaqcore_id,
+};
+module_i2c_driver(ams_iaqcore_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_DESCRIPTION("AMS iAQ-Core VOC sensors");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/chemical/vz89x.c b/drivers/iio/chemical/vz89x.c
index 11e59a5a5112..b8b804923230 100644
--- a/drivers/iio/chemical/vz89x.c
+++ b/drivers/iio/chemical/vz89x.c
@@ -34,8 +34,9 @@
struct vz89x_data {
struct i2c_client *client;
struct mutex lock;
- unsigned long last_update;
+ int (*xfer)(struct vz89x_data *data, u8 cmd);
+ unsigned long last_update;
u8 buffer[VZ89X_REG_MEASUREMENT_SIZE];
};
@@ -100,27 +101,60 @@ static int vz89x_measurement_is_valid(struct vz89x_data *data)
return !!(data->buffer[VZ89X_REG_MEASUREMENT_SIZE - 1] > 0);
}
-static int vz89x_get_measurement(struct vz89x_data *data)
+static int vz89x_i2c_xfer(struct vz89x_data *data, u8 cmd)
{
+ struct i2c_client *client = data->client;
+ struct i2c_msg msg[2];
int ret;
- int i;
+ u8 buf[3] = { cmd, 0, 0};
- /* sensor can only be polled once a second max per datasheet */
- if (!time_after(jiffies, data->last_update + HZ))
- return 0;
+ msg[0].addr = client->addr;
+ msg[0].flags = client->flags;
+ msg[0].len = 3;
+ msg[0].buf = (char *) &buf;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = client->flags | I2C_M_RD;
+ msg[1].len = VZ89X_REG_MEASUREMENT_SIZE;
+ msg[1].buf = (char *) &data->buffer;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
- ret = i2c_smbus_write_word_data(data->client,
- VZ89X_REG_MEASUREMENT, 0);
+ return (ret == 2) ? 0 : ret;
+}
+
+static int vz89x_smbus_xfer(struct vz89x_data *data, u8 cmd)
+{
+ struct i2c_client *client = data->client;
+ int ret;
+ int i;
+
+ ret = i2c_smbus_write_word_data(client, cmd, 0);
if (ret < 0)
return ret;
for (i = 0; i < VZ89X_REG_MEASUREMENT_SIZE; i++) {
- ret = i2c_smbus_read_byte(data->client);
+ ret = i2c_smbus_read_byte(client);
if (ret < 0)
return ret;
data->buffer[i] = ret;
}
+ return 0;
+}
+
+static int vz89x_get_measurement(struct vz89x_data *data)
+{
+ int ret;
+
+ /* sensor can only be polled once a second max per datasheet */
+ if (!time_after(jiffies, data->last_update + HZ))
+ return 0;
+
+ ret = data->xfer(data, VZ89X_REG_MEASUREMENT);
+ if (ret < 0)
+ return ret;
+
ret = vz89x_measurement_is_valid(data);
if (ret)
return -EAGAIN;
@@ -204,15 +238,19 @@ static int vz89x_probe(struct i2c_client *client,
struct iio_dev *indio_dev;
struct vz89x_data *data;
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA |
- I2C_FUNC_SMBUS_BYTE))
- return -ENODEV;
-
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
-
data = iio_priv(indio_dev);
+
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ data->xfer = vz89x_i2c_xfer;
+ else if (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE))
+ data->xfer = vz89x_smbus_xfer;
+ else
+ return -ENOTSUPP;
+
i2c_set_clientdata(client, indio_dev);
data->client = client;
data->last_update = jiffies - HZ;
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
index 25258e2c1a82..8447c31e27f2 100644
--- a/drivers/iio/common/st_sensors/st_sensors_core.c
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -18,9 +18,6 @@
#include <asm/unaligned.h>
#include <linux/iio/common/st_sensors.h>
-
-#define ST_SENSORS_WAI_ADDRESS 0x0f
-
static inline u32 st_sensors_get_unaligned_le24(const u8 *p)
{
return (s32)((p[0] | p[1] << 8 | p[2] << 16) << 8) >> 8;
diff --git a/drivers/iio/dummy/Kconfig b/drivers/iio/dummy/Kconfig
new file mode 100644
index 000000000000..71805ced1aae
--- /dev/null
+++ b/drivers/iio/dummy/Kconfig
@@ -0,0 +1,36 @@
+#
+# Industrial I/O subsystem Dummy Driver configuration
+#
+menu "IIO dummy driver"
+ depends on IIO
+
+config IIO_DUMMY_EVGEN
+ select IRQ_WORK
+ tristate
+
+config IIO_SIMPLE_DUMMY
+ tristate "An example driver with no hardware requirements"
+ help
+ Driver intended mainly as documentation for how to write
+ a driver. May also be useful for testing userspace code
+ without hardware.
+
+if IIO_SIMPLE_DUMMY
+
+config IIO_SIMPLE_DUMMY_EVENTS
+ bool "Event generation support"
+ select IIO_DUMMY_EVGEN
+ help
+ Add some dummy events to the simple dummy driver.
+
+config IIO_SIMPLE_DUMMY_BUFFER
+ bool "Buffered capture support"
+ select IIO_BUFFER
+ select IIO_TRIGGER
+ select IIO_KFIFO_BUF
+ help
+ Add buffered data capture to the simple dummy driver.
+
+endif # IIO_SIMPLE_DUMMY
+
+endmenu
diff --git a/drivers/iio/dummy/Makefile b/drivers/iio/dummy/Makefile
new file mode 100644
index 000000000000..0765e93d7804
--- /dev/null
+++ b/drivers/iio/dummy/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the IIO Dummy Driver
+#
+
+obj-$(CONFIG_IIO_SIMPLE_DUMMY) += iio_dummy.o
+iio_dummy-y := iio_simple_dummy.o
+iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_EVENTS) += iio_simple_dummy_events.o
+iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_BUFFER) += iio_simple_dummy_buffer.o
+
+obj-$(CONFIG_IIO_DUMMY_EVGEN) += iio_dummy_evgen.o
diff --git a/drivers/staging/iio/iio_dummy_evgen.c b/drivers/iio/dummy/iio_dummy_evgen.c
index 9e83f348df51..9e83f348df51 100644
--- a/drivers/staging/iio/iio_dummy_evgen.c
+++ b/drivers/iio/dummy/iio_dummy_evgen.c
diff --git a/drivers/staging/iio/iio_dummy_evgen.h b/drivers/iio/dummy/iio_dummy_evgen.h
index d044b946e74a..d044b946e74a 100644
--- a/drivers/staging/iio/iio_dummy_evgen.h
+++ b/drivers/iio/dummy/iio_dummy_evgen.h
diff --git a/drivers/staging/iio/iio_simple_dummy.c b/drivers/iio/dummy/iio_simple_dummy.c
index 43fe4ba7d0dc..43fe4ba7d0dc 100644
--- a/drivers/staging/iio/iio_simple_dummy.c
+++ b/drivers/iio/dummy/iio_simple_dummy.c
diff --git a/drivers/staging/iio/iio_simple_dummy.h b/drivers/iio/dummy/iio_simple_dummy.h
index b9069a180672..b9069a180672 100644
--- a/drivers/staging/iio/iio_simple_dummy.h
+++ b/drivers/iio/dummy/iio_simple_dummy.h
diff --git a/drivers/staging/iio/iio_simple_dummy_buffer.c b/drivers/iio/dummy/iio_simple_dummy_buffer.c
index cf44a6f79431..cf44a6f79431 100644
--- a/drivers/staging/iio/iio_simple_dummy_buffer.c
+++ b/drivers/iio/dummy/iio_simple_dummy_buffer.c
diff --git a/drivers/staging/iio/iio_simple_dummy_events.c b/drivers/iio/dummy/iio_simple_dummy_events.c
index bfbf1c56bd22..6eb600ff7056 100644
--- a/drivers/staging/iio/iio_simple_dummy_events.c
+++ b/drivers/iio/dummy/iio_simple_dummy_events.c
@@ -159,7 +159,7 @@ static irqreturn_t iio_simple_dummy_get_timestamp(int irq, void *private)
struct iio_dummy_state *st = iio_priv(indio_dev);
st->event_timestamp = iio_get_time_ns();
- return IRQ_HANDLED;
+ return IRQ_WAKE_THREAD;
}
/**
diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c
index f8d1c2210066..b04faf93e1bc 100644
--- a/drivers/iio/gyro/adis16136.c
+++ b/drivers/iio/gyro/adis16136.c
@@ -435,7 +435,9 @@ static int adis16136_initial_setup(struct iio_dev *indio_dev)
if (ret)
return ret;
- sscanf(indio_dev->name, "adis%u\n", &device_id);
+ ret = sscanf(indio_dev->name, "adis%u\n", &device_id);
+ if (ret != 1)
+ return -EINVAL;
if (prod_id != device_id)
dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c
index 02ff789852a0..bbce3b09ac45 100644
--- a/drivers/iio/gyro/bmg160_core.c
+++ b/drivers/iio/gyro/bmg160_core.c
@@ -1077,25 +1077,23 @@ int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq,
goto err_trigger_unregister;
}
- ret = iio_device_register(indio_dev);
- if (ret < 0) {
- dev_err(dev, "unable to register iio device\n");
- goto err_buffer_cleanup;
- }
-
ret = pm_runtime_set_active(dev);
if (ret)
- goto err_iio_unregister;
+ goto err_buffer_cleanup;
pm_runtime_enable(dev);
pm_runtime_set_autosuspend_delay(dev,
BMG160_AUTO_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(dev, "unable to register iio device\n");
+ goto err_buffer_cleanup;
+ }
+
return 0;
-err_iio_unregister:
- iio_device_unregister(indio_dev);
err_buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
err_trigger_unregister:
@@ -1113,11 +1111,12 @@ void bmg160_core_remove(struct device *dev)
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct bmg160_data *data = iio_priv(indio_dev);
+ iio_device_unregister(indio_dev);
+
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
pm_runtime_put_noidle(dev);
- iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
if (data->dready_trig) {
diff --git a/drivers/iio/health/Kconfig b/drivers/iio/health/Kconfig
new file mode 100644
index 000000000000..a647679da805
--- /dev/null
+++ b/drivers/iio/health/Kconfig
@@ -0,0 +1,21 @@
+#
+# Health sensors
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Health sensors"
+
+config MAX30100
+ tristate "MAX30100 heart rate and pulse oximeter sensor"
+ depends on I2C
+ select REGMAP_I2C
+ select IIO_BUFFER
+ select IIO_KFIFO_BUF
+ help
+ Say Y here to build I2C interface support for the Maxim
+ MAX30100 heart rate, and pulse oximeter sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called max30100.
+
+endmenu
diff --git a/drivers/iio/health/Makefile b/drivers/iio/health/Makefile
new file mode 100644
index 000000000000..7c475d7faad8
--- /dev/null
+++ b/drivers/iio/health/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for IIO Health sensors
+#
+
+# When adding new entries keep the list in alphabetical order
+
+obj-$(CONFIG_MAX30100) += max30100.o
diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c
new file mode 100644
index 000000000000..9d1c81f91dd7
--- /dev/null
+++ b/drivers/iio/health/max30100.c
@@ -0,0 +1,453 @@
+/*
+ * max30100.c - Support for MAX30100 heart rate and pulse oximeter sensor
+ *
+ * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * TODO: allow LED current and pulse length controls via device tree properties
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/kfifo_buf.h>
+
+#define MAX30100_REGMAP_NAME "max30100_regmap"
+#define MAX30100_DRV_NAME "max30100"
+
+#define MAX30100_REG_INT_STATUS 0x00
+#define MAX30100_REG_INT_STATUS_PWR_RDY BIT(0)
+#define MAX30100_REG_INT_STATUS_SPO2_RDY BIT(4)
+#define MAX30100_REG_INT_STATUS_HR_RDY BIT(5)
+#define MAX30100_REG_INT_STATUS_FIFO_RDY BIT(7)
+
+#define MAX30100_REG_INT_ENABLE 0x01
+#define MAX30100_REG_INT_ENABLE_SPO2_EN BIT(0)
+#define MAX30100_REG_INT_ENABLE_HR_EN BIT(1)
+#define MAX30100_REG_INT_ENABLE_FIFO_EN BIT(3)
+#define MAX30100_REG_INT_ENABLE_MASK 0xf0
+#define MAX30100_REG_INT_ENABLE_MASK_SHIFT 4
+
+#define MAX30100_REG_FIFO_WR_PTR 0x02
+#define MAX30100_REG_FIFO_OVR_CTR 0x03
+#define MAX30100_REG_FIFO_RD_PTR 0x04
+#define MAX30100_REG_FIFO_DATA 0x05
+#define MAX30100_REG_FIFO_DATA_ENTRY_COUNT 16
+#define MAX30100_REG_FIFO_DATA_ENTRY_LEN 4
+
+#define MAX30100_REG_MODE_CONFIG 0x06
+#define MAX30100_REG_MODE_CONFIG_MODE_SPO2_EN BIT(0)
+#define MAX30100_REG_MODE_CONFIG_MODE_HR_EN BIT(1)
+#define MAX30100_REG_MODE_CONFIG_MODE_MASK 0x03
+#define MAX30100_REG_MODE_CONFIG_TEMP_EN BIT(3)
+#define MAX30100_REG_MODE_CONFIG_PWR BIT(7)
+
+#define MAX30100_REG_SPO2_CONFIG 0x07
+#define MAX30100_REG_SPO2_CONFIG_100HZ BIT(2)
+#define MAX30100_REG_SPO2_CONFIG_HI_RES_EN BIT(6)
+#define MAX30100_REG_SPO2_CONFIG_1600US 0x3
+
+#define MAX30100_REG_LED_CONFIG 0x09
+#define MAX30100_REG_LED_CONFIG_RED_LED_SHIFT 4
+
+#define MAX30100_REG_LED_CONFIG_24MA 0x07
+#define MAX30100_REG_LED_CONFIG_50MA 0x0f
+
+#define MAX30100_REG_TEMP_INTEGER 0x16
+#define MAX30100_REG_TEMP_FRACTION 0x17
+
+struct max30100_data {
+ struct i2c_client *client;
+ struct iio_dev *indio_dev;
+ struct mutex lock;
+ struct regmap *regmap;
+
+ __be16 buffer[2]; /* 2 16-bit channels */
+};
+
+static bool max30100_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX30100_REG_INT_STATUS:
+ case MAX30100_REG_MODE_CONFIG:
+ case MAX30100_REG_FIFO_WR_PTR:
+ case MAX30100_REG_FIFO_OVR_CTR:
+ case MAX30100_REG_FIFO_RD_PTR:
+ case MAX30100_REG_FIFO_DATA:
+ case MAX30100_REG_TEMP_INTEGER:
+ case MAX30100_REG_TEMP_FRACTION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config max30100_regmap_config = {
+ .name = MAX30100_REGMAP_NAME,
+
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = MAX30100_REG_TEMP_FRACTION,
+ .cache_type = REGCACHE_FLAT,
+
+ .volatile_reg = max30100_is_volatile_reg,
+};
+
+static const unsigned long max30100_scan_masks[] = {0x3, 0};
+
+static const struct iio_chan_spec max30100_channels[] = {
+ {
+ .type = IIO_INTENSITY,
+ .channel2 = IIO_MOD_LIGHT_IR,
+ .modified = 1,
+
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
+ },
+ {
+ .type = IIO_INTENSITY,
+ .channel2 = IIO_MOD_LIGHT_RED,
+ .modified = 1,
+
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
+ },
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = -1,
+ },
+};
+
+static int max30100_set_powermode(struct max30100_data *data, bool state)
+{
+ return regmap_update_bits(data->regmap, MAX30100_REG_MODE_CONFIG,
+ MAX30100_REG_MODE_CONFIG_PWR,
+ state ? 0 : MAX30100_REG_MODE_CONFIG_PWR);
+}
+
+static int max30100_clear_fifo(struct max30100_data *data)
+{
+ int ret;
+
+ ret = regmap_write(data->regmap, MAX30100_REG_FIFO_WR_PTR, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(data->regmap, MAX30100_REG_FIFO_OVR_CTR, 0);
+ if (ret)
+ return ret;
+
+ return regmap_write(data->regmap, MAX30100_REG_FIFO_RD_PTR, 0);
+}
+
+static int max30100_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct max30100_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = max30100_set_powermode(data, true);
+ if (ret)
+ return ret;
+
+ return max30100_clear_fifo(data);
+}
+
+static int max30100_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct max30100_data *data = iio_priv(indio_dev);
+
+ return max30100_set_powermode(data, false);
+}
+
+static const struct iio_buffer_setup_ops max30100_buffer_setup_ops = {
+ .postenable = max30100_buffer_postenable,
+ .predisable = max30100_buffer_predisable,
+};
+
+static inline int max30100_fifo_count(struct max30100_data *data)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(data->regmap, MAX30100_REG_INT_STATUS, &val);
+ if (ret)
+ return ret;
+
+ /* FIFO is almost full */
+ if (val & MAX30100_REG_INT_STATUS_FIFO_RDY)
+ return MAX30100_REG_FIFO_DATA_ENTRY_COUNT - 1;
+
+ return 0;
+}
+
+static int max30100_read_measurement(struct max30100_data *data)
+{
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(data->client,
+ MAX30100_REG_FIFO_DATA,
+ MAX30100_REG_FIFO_DATA_ENTRY_LEN,
+ (u8 *) &data->buffer);
+
+ return (ret == MAX30100_REG_FIFO_DATA_ENTRY_LEN) ? 0 : ret;
+}
+
+static irqreturn_t max30100_interrupt_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct max30100_data *data = iio_priv(indio_dev);
+ int ret, cnt = 0;
+
+ mutex_lock(&data->lock);
+
+ while (cnt-- || (cnt = max30100_fifo_count(data) > 0)) {
+ ret = max30100_read_measurement(data);
+ if (ret)
+ break;
+
+ iio_push_to_buffers(data->indio_dev, data->buffer);
+ }
+
+ mutex_unlock(&data->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int max30100_chip_init(struct max30100_data *data)
+{
+ int ret;
+
+ /* RED IR LED = 24mA, IR LED = 50mA */
+ ret = regmap_write(data->regmap, MAX30100_REG_LED_CONFIG,
+ (MAX30100_REG_LED_CONFIG_24MA <<
+ MAX30100_REG_LED_CONFIG_RED_LED_SHIFT) |
+ MAX30100_REG_LED_CONFIG_50MA);
+ if (ret)
+ return ret;
+
+ /* enable hi-res SPO2 readings at 100Hz */
+ ret = regmap_write(data->regmap, MAX30100_REG_SPO2_CONFIG,
+ MAX30100_REG_SPO2_CONFIG_HI_RES_EN |
+ MAX30100_REG_SPO2_CONFIG_100HZ);
+ if (ret)
+ return ret;
+
+ /* enable SPO2 mode */
+ ret = regmap_update_bits(data->regmap, MAX30100_REG_MODE_CONFIG,
+ MAX30100_REG_MODE_CONFIG_MODE_MASK,
+ MAX30100_REG_MODE_CONFIG_MODE_HR_EN |
+ MAX30100_REG_MODE_CONFIG_MODE_SPO2_EN);
+ if (ret)
+ return ret;
+
+ /* enable FIFO interrupt */
+ return regmap_update_bits(data->regmap, MAX30100_REG_INT_ENABLE,
+ MAX30100_REG_INT_ENABLE_MASK,
+ MAX30100_REG_INT_ENABLE_FIFO_EN
+ << MAX30100_REG_INT_ENABLE_MASK_SHIFT);
+}
+
+static int max30100_read_temp(struct max30100_data *data, int *val)
+{
+ int ret;
+ unsigned int reg;
+
+ ret = regmap_read(data->regmap, MAX30100_REG_TEMP_INTEGER, &reg);
+ if (ret < 0)
+ return ret;
+ *val = reg << 4;
+
+ ret = regmap_read(data->regmap, MAX30100_REG_TEMP_FRACTION, &reg);
+ if (ret < 0)
+ return ret;
+
+ *val |= reg & 0xf;
+ *val = sign_extend32(*val, 11);
+
+ return 0;
+}
+
+static int max30100_get_temp(struct max30100_data *data, int *val)
+{
+ int ret;
+
+ /* start acquisition */
+ ret = regmap_update_bits(data->regmap, MAX30100_REG_MODE_CONFIG,
+ MAX30100_REG_MODE_CONFIG_TEMP_EN,
+ MAX30100_REG_MODE_CONFIG_TEMP_EN);
+ if (ret)
+ return ret;
+
+ usleep_range(35000, 50000);
+
+ return max30100_read_temp(data, val);
+}
+
+static int max30100_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct max30100_data *data = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ /*
+ * Temperature reading can only be acquired while engine
+ * is running
+ */
+ mutex_lock(&indio_dev->mlock);
+
+ if (!iio_buffer_enabled(indio_dev))
+ ret = -EAGAIN;
+ else {
+ ret = max30100_get_temp(data, val);
+ if (!ret)
+ ret = IIO_VAL_INT;
+
+ }
+
+ mutex_unlock(&indio_dev->mlock);
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 1; /* 0.0625 */
+ *val2 = 16;
+ ret = IIO_VAL_FRACTIONAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct iio_info max30100_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = max30100_read_raw,
+};
+
+static int max30100_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max30100_data *data;
+ struct iio_buffer *buffer;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ buffer = devm_iio_kfifo_allocate(&client->dev);
+ if (!buffer)
+ return -ENOMEM;
+
+ iio_device_attach_buffer(indio_dev, buffer);
+
+ indio_dev->name = MAX30100_DRV_NAME;
+ indio_dev->channels = max30100_channels;
+ indio_dev->info = &max30100_info;
+ indio_dev->num_channels = ARRAY_SIZE(max30100_channels);
+ indio_dev->available_scan_masks = max30100_scan_masks;
+ indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
+ indio_dev->setup_ops = &max30100_buffer_setup_ops;
+
+ data = iio_priv(indio_dev);
+ data->indio_dev = indio_dev;
+ data->client = client;
+
+ mutex_init(&data->lock);
+ i2c_set_clientdata(client, indio_dev);
+
+ data->regmap = devm_regmap_init_i2c(client, &max30100_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ dev_err(&client->dev, "regmap initialization failed.\n");
+ return PTR_ERR(data->regmap);
+ }
+ max30100_set_powermode(data, false);
+
+ ret = max30100_chip_init(data);
+ if (ret)
+ return ret;
+
+ if (client->irq <= 0) {
+ dev_err(&client->dev, "no valid irq defined\n");
+ return -EINVAL;
+ }
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, max30100_interrupt_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "max30100_irq", indio_dev);
+ if (ret) {
+ dev_err(&client->dev, "request irq (%d) failed\n", client->irq);
+ return ret;
+ }
+
+ return iio_device_register(indio_dev);
+}
+
+static int max30100_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct max30100_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ max30100_set_powermode(data, false);
+
+ return 0;
+}
+
+static const struct i2c_device_id max30100_id[] = {
+ { "max30100", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, max30100_id);
+
+static const struct of_device_id max30100_dt_ids[] = {
+ { .compatible = "maxim,max30100" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max30100_dt_ids);
+
+static struct i2c_driver max30100_driver = {
+ .driver = {
+ .name = MAX30100_DRV_NAME,
+ .of_match_table = of_match_ptr(max30100_dt_ids),
+ },
+ .probe = max30100_probe,
+ .remove = max30100_remove,
+ .id_table = max30100_id,
+};
+module_i2c_driver(max30100_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_DESCRIPTION("MAX30100 heart rate and pulse oximeter sensor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/adis16400_core.c b/drivers/iio/imu/adis16400_core.c
index 0618f831ecd4..fb7c0dbed51c 100644
--- a/drivers/iio/imu/adis16400_core.c
+++ b/drivers/iio/imu/adis16400_core.c
@@ -288,7 +288,11 @@ static int adis16400_initial_setup(struct iio_dev *indio_dev)
if (ret)
goto err_ret;
- sscanf(indio_dev->name, "adis%u\n", &device_id);
+ ret = sscanf(indio_dev->name, "adis%u\n", &device_id);
+ if (ret != 1) {
+ ret = -EINVAL;
+ goto err_ret;
+ }
if (prod_id != device_id)
dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c
index 2485b88ee1b6..8cf84d3488b2 100644
--- a/drivers/iio/imu/adis16480.c
+++ b/drivers/iio/imu/adis16480.c
@@ -765,7 +765,9 @@ static int adis16480_initial_setup(struct iio_dev *indio_dev)
if (ret)
return ret;
- sscanf(indio_dev->name, "adis%u\n", &device_id);
+ ret = sscanf(indio_dev->name, "adis%u\n", &device_id);
+ if (ret != 1)
+ return -EINVAL;
if (prod_id != device_id)
dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c
index dbf5e9936635..e5306b4e020e 100644
--- a/drivers/iio/imu/kmx61.c
+++ b/drivers/iio/imu/kmx61.c
@@ -1390,6 +1390,14 @@ static int kmx61_probe(struct i2c_client *client,
}
}
+ ret = pm_runtime_set_active(&client->dev);
+ if (ret < 0)
+ goto err_buffer_cleanup_mag;
+
+ pm_runtime_enable(&client->dev);
+ pm_runtime_set_autosuspend_delay(&client->dev, KMX61_SLEEP_DELAY_MS);
+ pm_runtime_use_autosuspend(&client->dev);
+
ret = iio_device_register(data->acc_indio_dev);
if (ret < 0) {
dev_err(&client->dev, "Failed to register acc iio device\n");
@@ -1402,18 +1410,8 @@ static int kmx61_probe(struct i2c_client *client,
goto err_iio_unregister_acc;
}
- ret = pm_runtime_set_active(&client->dev);
- if (ret < 0)
- goto err_iio_unregister_mag;
-
- pm_runtime_enable(&client->dev);
- pm_runtime_set_autosuspend_delay(&client->dev, KMX61_SLEEP_DELAY_MS);
- pm_runtime_use_autosuspend(&client->dev);
-
return 0;
-err_iio_unregister_mag:
- iio_device_unregister(data->mag_indio_dev);
err_iio_unregister_acc:
iio_device_unregister(data->acc_indio_dev);
err_buffer_cleanup_mag:
@@ -1437,13 +1435,13 @@ static int kmx61_remove(struct i2c_client *client)
{
struct kmx61_data *data = i2c_get_clientdata(client);
+ iio_device_unregister(data->acc_indio_dev);
+ iio_device_unregister(data->mag_indio_dev);
+
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
- iio_device_unregister(data->acc_indio_dev);
- iio_device_unregister(data->mag_indio_dev);
-
if (client->irq > 0) {
iio_triggered_buffer_cleanup(data->acc_indio_dev);
iio_triggered_buffer_cleanup(data->mag_indio_dev);
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index d7e908acb480..139ae916225f 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -193,7 +193,8 @@ void iio_buffer_init(struct iio_buffer *buffer)
INIT_LIST_HEAD(&buffer->buffer_list);
init_waitqueue_head(&buffer->pollq);
kref_init(&buffer->ref);
- buffer->watermark = 1;
+ if (!buffer->watermark)
+ buffer->watermark = 1;
}
EXPORT_SYMBOL(iio_buffer_init);
@@ -302,7 +303,7 @@ static int iio_scan_mask_set(struct iio_dev *indio_dev,
if (trialmask == NULL)
return -ENOMEM;
if (!indio_dev->masklength) {
- WARN_ON("Trying to set scanmask prior to registering buffer\n");
+ WARN(1, "Trying to set scanmask prior to registering buffer\n");
goto err_invalid_mask;
}
bitmap_copy(trialmask, buffer->scan_mask, indio_dev->masklength);
@@ -567,6 +568,22 @@ static void iio_buffer_deactivate_all(struct iio_dev *indio_dev)
iio_buffer_deactivate(buffer);
}
+static int iio_buffer_enable(struct iio_buffer *buffer,
+ struct iio_dev *indio_dev)
+{
+ if (!buffer->access->enable)
+ return 0;
+ return buffer->access->enable(buffer, indio_dev);
+}
+
+static int iio_buffer_disable(struct iio_buffer *buffer,
+ struct iio_dev *indio_dev)
+{
+ if (!buffer->access->disable)
+ return 0;
+ return buffer->access->disable(buffer, indio_dev);
+}
+
static void iio_buffer_update_bytes_per_datum(struct iio_dev *indio_dev,
struct iio_buffer *buffer)
{
@@ -610,6 +627,7 @@ static void iio_free_scan_mask(struct iio_dev *indio_dev,
struct iio_device_config {
unsigned int mode;
+ unsigned int watermark;
const unsigned long *scan_mask;
unsigned int scan_bytes;
bool scan_timestamp;
@@ -642,10 +660,14 @@ static int iio_verify_update(struct iio_dev *indio_dev,
if (buffer == remove_buffer)
continue;
modes &= buffer->access->modes;
+ config->watermark = min(config->watermark, buffer->watermark);
}
- if (insert_buffer)
+ if (insert_buffer) {
modes &= insert_buffer->access->modes;
+ config->watermark = min(config->watermark,
+ insert_buffer->watermark);
+ }
/* Definitely possible for devices to support both of these. */
if ((modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) {
@@ -713,6 +735,7 @@ static int iio_verify_update(struct iio_dev *indio_dev,
static int iio_enable_buffers(struct iio_dev *indio_dev,
struct iio_device_config *config)
{
+ struct iio_buffer *buffer;
int ret;
indio_dev->active_scan_mask = config->scan_mask;
@@ -743,6 +766,16 @@ static int iio_enable_buffers(struct iio_dev *indio_dev,
}
}
+ if (indio_dev->info->hwfifo_set_watermark)
+ indio_dev->info->hwfifo_set_watermark(indio_dev,
+ config->watermark);
+
+ list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
+ ret = iio_buffer_enable(buffer, indio_dev);
+ if (ret)
+ goto err_disable_buffers;
+ }
+
indio_dev->currentmode = config->mode;
if (indio_dev->setup_ops->postenable) {
@@ -750,12 +783,16 @@ static int iio_enable_buffers(struct iio_dev *indio_dev,
if (ret) {
dev_dbg(&indio_dev->dev,
"Buffer not started: postenable failed (%d)\n", ret);
- goto err_run_postdisable;
+ goto err_disable_buffers;
}
}
return 0;
+err_disable_buffers:
+ list_for_each_entry_continue_reverse(buffer, &indio_dev->buffer_list,
+ buffer_list)
+ iio_buffer_disable(buffer, indio_dev);
err_run_postdisable:
indio_dev->currentmode = INDIO_DIRECT_MODE;
if (indio_dev->setup_ops->postdisable)
@@ -768,6 +805,7 @@ err_undo_config:
static int iio_disable_buffers(struct iio_dev *indio_dev)
{
+ struct iio_buffer *buffer;
int ret = 0;
int ret2;
@@ -788,6 +826,12 @@ static int iio_disable_buffers(struct iio_dev *indio_dev)
ret = ret2;
}
+ list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
+ ret2 = iio_buffer_disable(buffer, indio_dev);
+ if (ret2 && !ret)
+ ret = ret2;
+ }
+
indio_dev->currentmode = INDIO_DIRECT_MODE;
if (indio_dev->setup_ops->postdisable) {
@@ -974,9 +1018,6 @@ static ssize_t iio_buffer_store_watermark(struct device *dev,
}
buffer->watermark = val;
-
- if (indio_dev->info->hwfifo_set_watermark)
- indio_dev->info->hwfifo_set_watermark(indio_dev, val);
out:
mutex_unlock(&indio_dev->mlock);
@@ -991,6 +1032,8 @@ static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR,
iio_buffer_show_enable, iio_buffer_store_enable);
static DEVICE_ATTR(watermark, S_IRUGO | S_IWUSR,
iio_buffer_show_watermark, iio_buffer_store_watermark);
+static struct device_attribute dev_attr_watermark_ro = __ATTR(watermark,
+ S_IRUGO, iio_buffer_show_watermark, NULL);
static struct attribute *iio_buffer_attrs[] = {
&dev_attr_length.attr,
@@ -1033,6 +1076,9 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
if (!buffer->access->set_length)
attr[0] = &dev_attr_length_ro.attr;
+ if (buffer->access->flags & INDIO_BUFFER_FLAG_FIXED_WATERMARK)
+ attr[2] = &dev_attr_watermark_ro.attr;
+
if (buffer->attrs)
memcpy(&attr[ARRAY_SIZE(iio_buffer_attrs)], buffer->attrs,
sizeof(struct attribute *) * attrcount);
diff --git a/drivers/iio/industrialio-configfs.c b/drivers/iio/industrialio-configfs.c
new file mode 100644
index 000000000000..45ce2bc47180
--- /dev/null
+++ b/drivers/iio/industrialio-configfs.c
@@ -0,0 +1,51 @@
+/*
+ * Industrial I/O configfs bits
+ *
+ * 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 version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/configfs.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/configfs.h>
+
+static struct config_item_type iio_root_group_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+struct configfs_subsystem iio_configfs_subsys = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "iio",
+ .ci_type = &iio_root_group_type,
+ },
+ },
+ .su_mutex = __MUTEX_INITIALIZER(iio_configfs_subsys.su_mutex),
+};
+EXPORT_SYMBOL(iio_configfs_subsys);
+
+static int __init iio_configfs_init(void)
+{
+ config_group_init(&iio_configfs_subsys.su_group);
+
+ return configfs_register_subsystem(&iio_configfs_subsys);
+}
+module_init(iio_configfs_init);
+
+static void __exit iio_configfs_exit(void)
+{
+ configfs_unregister_subsystem(&iio_configfs_subsys);
+}
+module_exit(iio_configfs_exit);
+
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
+MODULE_DESCRIPTION("Industrial I/O configfs support");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 208358f9e7e3..fd01f3493fc7 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -470,6 +470,7 @@ ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals)
return 0;
}
}
+EXPORT_SYMBOL_GPL(iio_format_value);
static ssize_t iio_read_channel_info(struct device *dev,
struct device_attribute *attr,
@@ -512,6 +513,12 @@ int iio_str_to_fixpoint(const char *str, int fract_mult,
int i = 0, f = 0;
bool integer_part = true, negative = false;
+ if (fract_mult == 0) {
+ *fract = 0;
+
+ return kstrtoint(str, 0, integer);
+ }
+
if (str[0] == '-') {
negative = true;
str++;
@@ -571,6 +578,9 @@ static ssize_t iio_write_channel_info(struct device *dev,
if (indio_dev->info->write_raw_get_fmt)
switch (indio_dev->info->write_raw_get_fmt(indio_dev,
this_attr->c, this_attr->address)) {
+ case IIO_VAL_INT:
+ fract_mult = 0;
+ break;
case IIO_VAL_INT_PLUS_MICRO:
fract_mult = 100000;
break;
@@ -655,7 +665,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
break;
case IIO_SEPARATE:
if (!chan->indexed) {
- WARN_ON("Differential channels must be indexed\n");
+ WARN(1, "Differential channels must be indexed\n");
ret = -EINVAL;
goto error_free_full_postfix;
}
diff --git a/drivers/iio/industrialio-sw-trigger.c b/drivers/iio/industrialio-sw-trigger.c
new file mode 100644
index 000000000000..311f9fe5aa34
--- /dev/null
+++ b/drivers/iio/industrialio-sw-trigger.c
@@ -0,0 +1,184 @@
+/*
+ * The Industrial I/O core, software trigger functions
+ *
+ * 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 version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+
+#include <linux/iio/sw_trigger.h>
+#include <linux/iio/configfs.h>
+#include <linux/configfs.h>
+
+static struct config_group *iio_triggers_group;
+static struct config_item_type iio_trigger_type_group_type;
+
+static struct config_item_type iio_triggers_group_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static LIST_HEAD(iio_trigger_types_list);
+static DEFINE_MUTEX(iio_trigger_types_lock);
+
+static
+struct iio_sw_trigger_type *__iio_find_sw_trigger_type(const char *name,
+ unsigned len)
+{
+ struct iio_sw_trigger_type *t = NULL, *iter;
+
+ list_for_each_entry(iter, &iio_trigger_types_list, list)
+ if (!strcmp(iter->name, name)) {
+ t = iter;
+ break;
+ }
+
+ return t;
+}
+
+int iio_register_sw_trigger_type(struct iio_sw_trigger_type *t)
+{
+ struct iio_sw_trigger_type *iter;
+ int ret = 0;
+
+ mutex_lock(&iio_trigger_types_lock);
+ iter = __iio_find_sw_trigger_type(t->name, strlen(t->name));
+ if (iter)
+ ret = -EBUSY;
+ else
+ list_add_tail(&t->list, &iio_trigger_types_list);
+ mutex_unlock(&iio_trigger_types_lock);
+
+ if (ret)
+ return ret;
+
+ t->group = configfs_register_default_group(iio_triggers_group, t->name,
+ &iio_trigger_type_group_type);
+ if (IS_ERR(t->group))
+ ret = PTR_ERR(t->group);
+
+ return ret;
+}
+EXPORT_SYMBOL(iio_register_sw_trigger_type);
+
+void iio_unregister_sw_trigger_type(struct iio_sw_trigger_type *t)
+{
+ struct iio_sw_trigger_type *iter;
+
+ mutex_lock(&iio_trigger_types_lock);
+ iter = __iio_find_sw_trigger_type(t->name, strlen(t->name));
+ if (iter)
+ list_del(&t->list);
+ mutex_unlock(&iio_trigger_types_lock);
+
+ configfs_unregister_default_group(t->group);
+}
+EXPORT_SYMBOL(iio_unregister_sw_trigger_type);
+
+static
+struct iio_sw_trigger_type *iio_get_sw_trigger_type(const char *name)
+{
+ struct iio_sw_trigger_type *t;
+
+ mutex_lock(&iio_trigger_types_lock);
+ t = __iio_find_sw_trigger_type(name, strlen(name));
+ if (t && !try_module_get(t->owner))
+ t = NULL;
+ mutex_unlock(&iio_trigger_types_lock);
+
+ return t;
+}
+
+struct iio_sw_trigger *iio_sw_trigger_create(const char *type, const char *name)
+{
+ struct iio_sw_trigger *t;
+ struct iio_sw_trigger_type *tt;
+
+ tt = iio_get_sw_trigger_type(type);
+ if (!tt) {
+ pr_err("Invalid trigger type: %s\n", type);
+ return ERR_PTR(-EINVAL);
+ }
+ t = tt->ops->probe(name);
+ if (IS_ERR(t))
+ goto out_module_put;
+
+ t->trigger_type = tt;
+
+ return t;
+out_module_put:
+ module_put(tt->owner);
+ return t;
+}
+EXPORT_SYMBOL(iio_sw_trigger_create);
+
+void iio_sw_trigger_destroy(struct iio_sw_trigger *t)
+{
+ struct iio_sw_trigger_type *tt = t->trigger_type;
+
+ tt->ops->remove(t);
+ module_put(tt->owner);
+}
+EXPORT_SYMBOL(iio_sw_trigger_destroy);
+
+static struct config_group *trigger_make_group(struct config_group *group,
+ const char *name)
+{
+ struct iio_sw_trigger *t;
+
+ t = iio_sw_trigger_create(group->cg_item.ci_name, name);
+ if (IS_ERR(t))
+ return ERR_CAST(t);
+
+ config_item_set_name(&t->group.cg_item, "%s", name);
+
+ return &t->group;
+}
+
+static void trigger_drop_group(struct config_group *group,
+ struct config_item *item)
+{
+ struct iio_sw_trigger *t = to_iio_sw_trigger(item);
+
+ iio_sw_trigger_destroy(t);
+ config_item_put(item);
+}
+
+static struct configfs_group_operations trigger_ops = {
+ .make_group = &trigger_make_group,
+ .drop_item = &trigger_drop_group,
+};
+
+static struct config_item_type iio_trigger_type_group_type = {
+ .ct_group_ops = &trigger_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static int __init iio_sw_trigger_init(void)
+{
+ iio_triggers_group =
+ configfs_register_default_group(&iio_configfs_subsys.su_group,
+ "triggers",
+ &iio_triggers_group_type);
+ if (IS_ERR(iio_triggers_group))
+ return PTR_ERR(iio_triggers_group);
+ return 0;
+}
+module_init(iio_sw_trigger_init);
+
+static void __exit iio_sw_trigger_exit(void)
+{
+ configfs_unregister_default_group(iio_triggers_group);
+}
+module_exit(iio_sw_trigger_exit);
+
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
+MODULE_DESCRIPTION("Industrial I/O software triggers support");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index c8bad3cf891d..80fbbfd76faf 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -61,12 +61,10 @@ EXPORT_SYMBOL_GPL(iio_map_array_register);
int iio_map_array_unregister(struct iio_dev *indio_dev)
{
int ret = -ENODEV;
- struct iio_map_internal *mapi;
- struct list_head *pos, *tmp;
+ struct iio_map_internal *mapi, *next;
mutex_lock(&iio_map_list_lock);
- list_for_each_safe(pos, tmp, &iio_map_list) {
- mapi = list_entry(pos, struct iio_map_internal, l);
+ list_for_each_entry_safe(mapi, next, &iio_map_list, l) {
if (indio_dev == mapi->indio_dev) {
list_del(&mapi->l);
kfree(mapi);
diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c
index 7d269ef9e062..f6a07dc32ae4 100644
--- a/drivers/iio/light/apds9960.c
+++ b/drivers/iio/light/apds9960.c
@@ -453,6 +453,7 @@ static int apds9960_set_power_state(struct apds9960_data *data, bool on)
usleep_range(data->als_adc_int_us,
APDS9960_MAX_INT_TIME_IN_US);
} else {
+ pm_runtime_mark_last_busy(dev);
ret = pm_runtime_put_autosuspend(dev);
}
diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c
index 076bc46fad03..e56937c40a18 100644
--- a/drivers/iio/light/lm3533-als.c
+++ b/drivers/iio/light/lm3533-als.c
@@ -743,8 +743,10 @@ static int lm3533_als_set_resistor(struct lm3533_als *als, u8 val)
{
int ret;
- if (val < LM3533_ALS_RESISTOR_MIN || val > LM3533_ALS_RESISTOR_MAX)
+ if (val < LM3533_ALS_RESISTOR_MIN || val > LM3533_ALS_RESISTOR_MAX) {
+ dev_err(&als->pdev->dev, "invalid resistor value\n");
return -EINVAL;
+ };
ret = lm3533_write(als->lm3533, LM3533_REG_ALS_RESISTOR_SELECT, val);
if (ret) {
diff --git a/drivers/iio/light/pa12203001.c b/drivers/iio/light/pa12203001.c
index 45f7bde02bbf..76a9e12b46bc 100644
--- a/drivers/iio/light/pa12203001.c
+++ b/drivers/iio/light/pa12203001.c
@@ -381,17 +381,23 @@ static int pa12203001_probe(struct i2c_client *client,
return ret;
ret = pm_runtime_set_active(&client->dev);
- if (ret < 0) {
- pa12203001_power_chip(indio_dev, PA12203001_CHIP_DISABLE);
- return ret;
- }
+ if (ret < 0)
+ goto out_err;
pm_runtime_enable(&client->dev);
pm_runtime_set_autosuspend_delay(&client->dev,
PA12203001_SLEEP_DELAY_MS);
pm_runtime_use_autosuspend(&client->dev);
- return iio_device_register(indio_dev);
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto out_err;
+
+ return 0;
+
+out_err:
+ pa12203001_power_chip(indio_dev, PA12203001_CHIP_DISABLE);
+ return ret;
}
static int pa12203001_remove(struct i2c_client *client)
diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
index 4b75bb0998b3..7de0f397194b 100644
--- a/drivers/iio/light/rpr0521.c
+++ b/drivers/iio/light/rpr0521.c
@@ -507,34 +507,28 @@ static int rpr0521_probe(struct i2c_client *client,
dev_err(&client->dev, "rpr0521 chip init failed\n");
return ret;
}
- ret = iio_device_register(indio_dev);
- if (ret < 0)
- return ret;
ret = pm_runtime_set_active(&client->dev);
if (ret < 0)
- goto err_iio_unregister;
+ return ret;
pm_runtime_enable(&client->dev);
pm_runtime_set_autosuspend_delay(&client->dev, RPR0521_SLEEP_DELAY_MS);
pm_runtime_use_autosuspend(&client->dev);
- return 0;
-
-err_iio_unregister:
- iio_device_unregister(indio_dev);
- return ret;
+ return iio_device_register(indio_dev);
}
static int rpr0521_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ iio_device_unregister(indio_dev);
+
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
- iio_device_unregister(indio_dev);
rpr0521_poweroff(iio_priv(indio_dev));
return 0;
diff --git a/drivers/iio/light/us5182d.c b/drivers/iio/light/us5182d.c
index 49dab3cb3e23..45bc2f742f46 100644
--- a/drivers/iio/light/us5182d.c
+++ b/drivers/iio/light/us5182d.c
@@ -20,14 +20,21 @@
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/i2c.h>
+#include <linux/iio/events.h>
#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
#include <linux/iio/sysfs.h>
#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
#define US5182D_REG_CFG0 0x00
#define US5182D_CFG0_ONESHOT_EN BIT(6)
#define US5182D_CFG0_SHUTDOWN_EN BIT(7)
#define US5182D_CFG0_WORD_ENABLE BIT(0)
+#define US5182D_CFG0_PROX BIT(3)
+#define US5182D_CFG0_PX_IRQ BIT(2)
#define US5182D_REG_CFG1 0x01
#define US5182D_CFG1_ALS_RES16 BIT(4)
@@ -39,6 +46,7 @@
#define US5182D_REG_CFG3 0x03
#define US5182D_CFG3_LED_CURRENT100 (BIT(4) | BIT(5))
+#define US5182D_CFG3_INT_SOURCE_PX BIT(3)
#define US5182D_REG_CFG4 0x10
@@ -53,6 +61,13 @@
#define US5182D_REG_AUTO_LDARK_GAIN 0x29
#define US5182D_REG_AUTO_HDARK_GAIN 0x2a
+/* Thresholds for events: px low (0x08-l, 0x09-h), px high (0x0a-l 0x0b-h) */
+#define US5182D_REG_PXL_TH 0x08
+#define US5182D_REG_PXH_TH 0x0a
+
+#define US5182D_REG_PXL_TH_DEFAULT 1000
+#define US5182D_REG_PXH_TH_DEFAULT 30000
+
#define US5182D_OPMODE_ALS 0x01
#define US5182D_OPMODE_PX 0x02
#define US5182D_OPMODE_SHIFT 4
@@ -81,6 +96,9 @@
#define US5182D_READ_BYTE 1
#define US5182D_READ_WORD 2
#define US5182D_OPSTORE_SLEEP_TIME 20 /* ms */
+#define US5182D_SLEEP_MS 3000 /* ms */
+#define US5182D_PXH_TH_DISABLE 0xffff
+#define US5182D_PXL_TH_DISABLE 0x0000
/* Available ranges: [12354, 7065, 3998, 2202, 1285, 498, 256, 138] lux */
static const int us5182d_scales[] = {188500, 107800, 61000, 33600, 19600, 7600,
@@ -99,6 +117,11 @@ enum mode {
US5182D_PX_ONLY
};
+enum pmode {
+ US5182D_CONTINUOUS,
+ US5182D_ONESHOT
+};
+
struct us5182d_data {
struct i2c_client *client;
struct mutex lock;
@@ -111,7 +134,19 @@ struct us5182d_data {
u8 upper_dark_gain;
u16 *us5182d_dark_ths;
+ u16 px_low_th;
+ u16 px_high_th;
+
+ int rising_en;
+ int falling_en;
+
u8 opmode;
+ u8 power_mode;
+
+ bool als_enabled;
+ bool px_enabled;
+
+ bool default_continuous;
};
static IIO_CONST_ATTR(in_illuminance_scale_available,
@@ -130,16 +165,30 @@ static const struct {
u8 reg;
u8 val;
} us5182d_regvals[] = {
- {US5182D_REG_CFG0, (US5182D_CFG0_SHUTDOWN_EN |
- US5182D_CFG0_WORD_ENABLE)},
+ {US5182D_REG_CFG0, US5182D_CFG0_WORD_ENABLE},
{US5182D_REG_CFG1, US5182D_CFG1_ALS_RES16},
{US5182D_REG_CFG2, (US5182D_CFG2_PX_RES16 |
US5182D_CFG2_PXGAIN_DEFAULT)},
- {US5182D_REG_CFG3, US5182D_CFG3_LED_CURRENT100},
- {US5182D_REG_MODE_STORE, US5182D_STORE_MODE},
+ {US5182D_REG_CFG3, US5182D_CFG3_LED_CURRENT100 |
+ US5182D_CFG3_INT_SOURCE_PX},
{US5182D_REG_CFG4, 0x00},
};
+static const struct iio_event_spec us5182d_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
static const struct iio_chan_spec us5182d_channels[] = {
{
.type = IIO_LIGHT,
@@ -149,40 +198,39 @@ static const struct iio_chan_spec us5182d_channels[] = {
{
.type = IIO_PROXIMITY,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .event_spec = us5182d_events,
+ .num_event_specs = ARRAY_SIZE(us5182d_events),
}
};
-static int us5182d_get_als(struct us5182d_data *data)
+static int us5182d_oneshot_en(struct us5182d_data *data)
{
int ret;
- unsigned long result;
- ret = i2c_smbus_read_word_data(data->client,
- US5182D_REG_ADL);
+ ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG0);
if (ret < 0)
return ret;
- result = ret * data->ga / US5182D_GA_RESOLUTION;
- if (result > 0xffff)
- result = 0xffff;
+ /*
+ * In oneshot mode the chip will power itself down after taking the
+ * required measurement.
+ */
+ ret = ret | US5182D_CFG0_ONESHOT_EN;
- return result;
+ return i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG0, ret);
}
static int us5182d_set_opmode(struct us5182d_data *data, u8 mode)
{
int ret;
+ if (mode == data->opmode)
+ return 0;
+
ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG0);
if (ret < 0)
return ret;
- /*
- * In oneshot mode the chip will power itself down after taking the
- * required measurement.
- */
- ret = ret | US5182D_CFG0_ONESHOT_EN;
-
/* update mode */
ret = ret & ~US5182D_OPMODE_MASK;
ret = ret | (mode << US5182D_OPMODE_SHIFT);
@@ -196,9 +244,6 @@ static int us5182d_set_opmode(struct us5182d_data *data, u8 mode)
if (ret < 0)
return ret;
- if (mode == data->opmode)
- return 0;
-
ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_MODE_STORE,
US5182D_STORE_MODE);
if (ret < 0)
@@ -210,6 +255,177 @@ static int us5182d_set_opmode(struct us5182d_data *data, u8 mode)
return 0;
}
+static int us5182d_als_enable(struct us5182d_data *data)
+{
+ int ret;
+ u8 mode;
+
+ if (data->power_mode == US5182D_ONESHOT) {
+ ret = us5182d_set_opmode(data, US5182D_ALS_ONLY);
+ if (ret < 0)
+ return ret;
+ data->px_enabled = false;
+ }
+
+ if (data->als_enabled)
+ return 0;
+
+ mode = data->px_enabled ? US5182D_ALS_PX : US5182D_ALS_ONLY;
+
+ ret = us5182d_set_opmode(data, mode);
+ if (ret < 0)
+ return ret;
+
+ data->als_enabled = true;
+
+ return 0;
+}
+
+static int us5182d_px_enable(struct us5182d_data *data)
+{
+ int ret;
+ u8 mode;
+
+ if (data->power_mode == US5182D_ONESHOT) {
+ ret = us5182d_set_opmode(data, US5182D_PX_ONLY);
+ if (ret < 0)
+ return ret;
+ data->als_enabled = false;
+ }
+
+ if (data->px_enabled)
+ return 0;
+
+ mode = data->als_enabled ? US5182D_ALS_PX : US5182D_PX_ONLY;
+
+ ret = us5182d_set_opmode(data, mode);
+ if (ret < 0)
+ return ret;
+
+ data->px_enabled = true;
+
+ return 0;
+}
+
+static int us5182d_get_als(struct us5182d_data *data)
+{
+ int ret;
+ unsigned long result;
+
+ ret = us5182d_als_enable(data);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_word_data(data->client,
+ US5182D_REG_ADL);
+ if (ret < 0)
+ return ret;
+
+ result = ret * data->ga / US5182D_GA_RESOLUTION;
+ if (result > 0xffff)
+ result = 0xffff;
+
+ return result;
+}
+
+static int us5182d_get_px(struct us5182d_data *data)
+{
+ int ret;
+
+ ret = us5182d_px_enable(data);
+ if (ret < 0)
+ return ret;
+
+ return i2c_smbus_read_word_data(data->client,
+ US5182D_REG_PDL);
+}
+
+static int us5182d_shutdown_en(struct us5182d_data *data, u8 state)
+{
+ int ret;
+
+ if (data->power_mode == US5182D_ONESHOT)
+ return 0;
+
+ ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG0);
+ if (ret < 0)
+ return ret;
+
+ ret = ret & ~US5182D_CFG0_SHUTDOWN_EN;
+ ret = ret | state;
+
+ ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG0, ret);
+ if (ret < 0)
+ return ret;
+
+ if (state & US5182D_CFG0_SHUTDOWN_EN) {
+ data->als_enabled = false;
+ data->px_enabled = false;
+ }
+
+ return ret;
+}
+
+
+static int us5182d_set_power_state(struct us5182d_data *data, bool on)
+{
+ int ret;
+
+ if (data->power_mode == US5182D_ONESHOT)
+ return 0;
+
+ if (on) {
+ ret = pm_runtime_get_sync(&data->client->dev);
+ if (ret < 0)
+ pm_runtime_put_noidle(&data->client->dev);
+ } else {
+ pm_runtime_mark_last_busy(&data->client->dev);
+ ret = pm_runtime_put_autosuspend(&data->client->dev);
+ }
+
+ return ret;
+}
+
+static int us5182d_read_value(struct us5182d_data *data,
+ struct iio_chan_spec const *chan)
+{
+ int ret, value;
+
+ mutex_lock(&data->lock);
+
+ if (data->power_mode == US5182D_ONESHOT) {
+ ret = us5182d_oneshot_en(data);
+ if (ret < 0)
+ goto out_err;
+ }
+
+ ret = us5182d_set_power_state(data, true);
+ if (ret < 0)
+ goto out_err;
+
+ if (chan->type == IIO_LIGHT)
+ ret = us5182d_get_als(data);
+ else
+ ret = us5182d_get_px(data);
+ if (ret < 0)
+ goto out_poweroff;
+
+ value = ret;
+
+ ret = us5182d_set_power_state(data, false);
+ if (ret < 0)
+ goto out_err;
+
+ mutex_unlock(&data->lock);
+ return value;
+
+out_poweroff:
+ us5182d_set_power_state(data, false);
+out_err:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
static int us5182d_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
@@ -219,53 +435,21 @@ static int us5182d_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
- switch (chan->type) {
- case IIO_LIGHT:
- mutex_lock(&data->lock);
- ret = us5182d_set_opmode(data, US5182D_OPMODE_ALS);
- if (ret < 0)
- goto out_err;
-
- ret = us5182d_get_als(data);
- if (ret < 0)
- goto out_err;
- mutex_unlock(&data->lock);
- *val = ret;
- return IIO_VAL_INT;
- case IIO_PROXIMITY:
- mutex_lock(&data->lock);
- ret = us5182d_set_opmode(data, US5182D_OPMODE_PX);
- if (ret < 0)
- goto out_err;
-
- ret = i2c_smbus_read_word_data(data->client,
- US5182D_REG_PDL);
- if (ret < 0)
- goto out_err;
- mutex_unlock(&data->lock);
- *val = ret;
- return IIO_VAL_INT;
- default:
- return -EINVAL;
- }
-
+ ret = us5182d_read_value(data, chan);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG1);
if (ret < 0)
return ret;
-
*val = 0;
*val2 = us5182d_scales[ret & US5182D_AGAIN_MASK];
-
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
-
- return -EINVAL;
-out_err:
- mutex_unlock(&data->lock);
- return ret;
}
/**
@@ -343,11 +527,201 @@ static int us5182d_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
+static int us5182d_setup_prox(struct iio_dev *indio_dev,
+ enum iio_event_direction dir, u16 val)
+{
+ struct us5182d_data *data = iio_priv(indio_dev);
+
+ if (dir == IIO_EV_DIR_FALLING)
+ return i2c_smbus_write_word_data(data->client,
+ US5182D_REG_PXL_TH, val);
+ else if (dir == IIO_EV_DIR_RISING)
+ return i2c_smbus_write_word_data(data->client,
+ US5182D_REG_PXH_TH, val);
+
+ return 0;
+}
+
+static int us5182d_read_thresh(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir, enum iio_event_info info, int *val,
+ int *val2)
+{
+ struct us5182d_data *data = iio_priv(indio_dev);
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ mutex_lock(&data->lock);
+ *val = data->px_high_th;
+ mutex_unlock(&data->lock);
+ break;
+ case IIO_EV_DIR_FALLING:
+ mutex_lock(&data->lock);
+ *val = data->px_low_th;
+ mutex_unlock(&data->lock);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return IIO_VAL_INT;
+}
+
+static int us5182d_write_thresh(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir, enum iio_event_info info, int val,
+ int val2)
+{
+ struct us5182d_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (val < 0 || val > USHRT_MAX || val2 != 0)
+ return -EINVAL;
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ mutex_lock(&data->lock);
+ if (data->rising_en) {
+ ret = us5182d_setup_prox(indio_dev, dir, val);
+ if (ret < 0)
+ goto err;
+ }
+ data->px_high_th = val;
+ mutex_unlock(&data->lock);
+ break;
+ case IIO_EV_DIR_FALLING:
+ mutex_lock(&data->lock);
+ if (data->falling_en) {
+ ret = us5182d_setup_prox(indio_dev, dir, val);
+ if (ret < 0)
+ goto err;
+ }
+ data->px_low_th = val;
+ mutex_unlock(&data->lock);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+err:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static int us5182d_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct us5182d_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ mutex_lock(&data->lock);
+ ret = data->rising_en;
+ mutex_unlock(&data->lock);
+ break;
+ case IIO_EV_DIR_FALLING:
+ mutex_lock(&data->lock);
+ ret = data->falling_en;
+ mutex_unlock(&data->lock);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int us5182d_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir, int state)
+{
+ struct us5182d_data *data = iio_priv(indio_dev);
+ int ret;
+ u16 new_th;
+
+ mutex_lock(&data->lock);
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ if (data->rising_en == state) {
+ mutex_unlock(&data->lock);
+ return 0;
+ }
+ new_th = US5182D_PXH_TH_DISABLE;
+ if (state) {
+ data->power_mode = US5182D_CONTINUOUS;
+ ret = us5182d_set_power_state(data, true);
+ if (ret < 0)
+ goto err;
+ ret = us5182d_px_enable(data);
+ if (ret < 0)
+ goto err_poweroff;
+ new_th = data->px_high_th;
+ }
+ ret = us5182d_setup_prox(indio_dev, dir, new_th);
+ if (ret < 0)
+ goto err_poweroff;
+ data->rising_en = state;
+ break;
+ case IIO_EV_DIR_FALLING:
+ if (data->falling_en == state) {
+ mutex_unlock(&data->lock);
+ return 0;
+ }
+ new_th = US5182D_PXL_TH_DISABLE;
+ if (state) {
+ data->power_mode = US5182D_CONTINUOUS;
+ ret = us5182d_set_power_state(data, true);
+ if (ret < 0)
+ goto err;
+ ret = us5182d_px_enable(data);
+ if (ret < 0)
+ goto err_poweroff;
+ new_th = data->px_low_th;
+ }
+ ret = us5182d_setup_prox(indio_dev, dir, new_th);
+ if (ret < 0)
+ goto err_poweroff;
+ data->falling_en = state;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (!state) {
+ ret = us5182d_set_power_state(data, false);
+ if (ret < 0)
+ goto err;
+ }
+
+ if (!data->falling_en && !data->rising_en && !data->default_continuous)
+ data->power_mode = US5182D_ONESHOT;
+
+ mutex_unlock(&data->lock);
+ return 0;
+
+err_poweroff:
+ if (state)
+ us5182d_set_power_state(data, false);
+err:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
static const struct iio_info us5182d_info = {
.driver_module = THIS_MODULE,
.read_raw = us5182d_read_raw,
.write_raw = us5182d_write_raw,
.attrs = &us5182d_attr_group,
+ .read_event_value = &us5182d_read_thresh,
+ .write_event_value = &us5182d_write_thresh,
+ .read_event_config = &us5182d_read_event_config,
+ .write_event_config = &us5182d_write_event_config,
};
static int us5182d_reset(struct iio_dev *indio_dev)
@@ -368,6 +742,10 @@ static int us5182d_init(struct iio_dev *indio_dev)
return ret;
data->opmode = 0;
+ data->power_mode = US5182D_CONTINUOUS;
+ data->px_low_th = US5182D_REG_PXL_TH_DEFAULT;
+ data->px_high_th = US5182D_REG_PXH_TH_DEFAULT;
+
for (i = 0; i < ARRAY_SIZE(us5182d_regvals); i++) {
ret = i2c_smbus_write_byte_data(data->client,
us5182d_regvals[i].reg,
@@ -376,7 +754,17 @@ static int us5182d_init(struct iio_dev *indio_dev)
return ret;
}
- return 0;
+ data->als_enabled = true;
+ data->px_enabled = true;
+
+ if (!data->default_continuous) {
+ ret = us5182d_shutdown_en(data, US5182D_CFG0_SHUTDOWN_EN);
+ if (ret < 0)
+ return ret;
+ data->power_mode = US5182D_ONESHOT;
+ }
+
+ return ret;
}
static void us5182d_get_platform_data(struct iio_dev *indio_dev)
@@ -399,6 +787,8 @@ static void us5182d_get_platform_data(struct iio_dev *indio_dev)
"upisemi,lower-dark-gain",
&data->lower_dark_gain))
data->lower_dark_gain = US5182D_REG_AUTO_LDARK_GAIN_DEFAULT;
+ data->default_continuous = device_property_read_bool(&data->client->dev,
+ "upisemi,continuous");
}
static int us5182d_dark_gain_config(struct iio_dev *indio_dev)
@@ -426,6 +816,33 @@ static int us5182d_dark_gain_config(struct iio_dev *indio_dev)
US5182D_REG_DARK_AUTO_EN_DEFAULT);
}
+static irqreturn_t us5182d_irq_thread_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct us5182d_data *data = iio_priv(indio_dev);
+ enum iio_event_direction dir;
+ int ret;
+ u64 ev;
+
+ ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG0);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "i2c transfer error in irq\n");
+ return IRQ_HANDLED;
+ }
+
+ dir = ret & US5182D_CFG0_PROX ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING;
+ ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 1, IIO_EV_TYPE_THRESH, dir);
+
+ iio_push_event(indio_dev, ev, iio_get_time_ns());
+
+ ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG0,
+ ret & ~US5182D_CFG0_PX_IRQ);
+ if (ret < 0)
+ dev_err(&data->client->dev, "i2c transfer error in irq\n");
+
+ return IRQ_HANDLED;
+}
+
static int us5182d_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -457,6 +874,16 @@ static int us5182d_probe(struct i2c_client *client,
return (ret < 0) ? ret : -ENODEV;
}
+ if (client->irq > 0) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ us5182d_irq_thread_handler,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "us5182d-irq", indio_dev);
+ if (ret < 0)
+ return ret;
+ } else
+ dev_warn(&client->dev, "no valid irq found\n");
+
us5182d_get_platform_data(indio_dev);
ret = us5182d_init(indio_dev);
if (ret < 0)
@@ -464,18 +891,73 @@ static int us5182d_probe(struct i2c_client *client,
ret = us5182d_dark_gain_config(indio_dev);
if (ret < 0)
- return ret;
+ goto out_err;
+
+ if (data->default_continuous) {
+ pm_runtime_set_active(&client->dev);
+ if (ret < 0)
+ goto out_err;
+ }
+
+ pm_runtime_enable(&client->dev);
+ pm_runtime_set_autosuspend_delay(&client->dev,
+ US5182D_SLEEP_MS);
+ pm_runtime_use_autosuspend(&client->dev);
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto out_err;
+
+ return 0;
+
+out_err:
+ us5182d_shutdown_en(data, US5182D_CFG0_SHUTDOWN_EN);
+ return ret;
- return iio_device_register(indio_dev);
}
static int us5182d_remove(struct i2c_client *client)
{
+ struct us5182d_data *data = iio_priv(i2c_get_clientdata(client));
+
iio_device_unregister(i2c_get_clientdata(client));
- return i2c_smbus_write_byte_data(client, US5182D_REG_CFG0,
- US5182D_CFG0_SHUTDOWN_EN);
+
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+
+ return us5182d_shutdown_en(data, US5182D_CFG0_SHUTDOWN_EN);
+}
+
+#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM)
+static int us5182d_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct us5182d_data *data = iio_priv(indio_dev);
+
+ if (data->power_mode == US5182D_CONTINUOUS)
+ return us5182d_shutdown_en(data, US5182D_CFG0_SHUTDOWN_EN);
+
+ return 0;
}
+static int us5182d_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct us5182d_data *data = iio_priv(indio_dev);
+
+ if (data->power_mode == US5182D_CONTINUOUS)
+ return us5182d_shutdown_en(data,
+ ~US5182D_CFG0_SHUTDOWN_EN & 0xff);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops us5182d_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(us5182d_suspend, us5182d_resume)
+ SET_RUNTIME_PM_OPS(us5182d_suspend, us5182d_resume, NULL)
+};
+
static const struct acpi_device_id us5182d_acpi_match[] = {
{ "USD5182", 0},
{}
@@ -493,6 +975,7 @@ MODULE_DEVICE_TABLE(i2c, us5182d_id);
static struct i2c_driver us5182d_driver = {
.driver = {
.name = US5182D_DRV_NAME,
+ .pm = &us5182d_pm_ops,
.acpi_match_table = ACPI_PTR(us5182d_acpi_match),
},
.probe = us5182d_probe,
diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c
index 1615b23d7b2a..ffcb75ea64fb 100644
--- a/drivers/iio/magnetometer/bmc150_magn.c
+++ b/drivers/iio/magnetometer/bmc150_magn.c
@@ -928,27 +928,24 @@ static int bmc150_magn_probe(struct i2c_client *client,
goto err_free_irq;
}
- ret = iio_device_register(indio_dev);
- if (ret < 0) {
- dev_err(&client->dev, "unable to register iio device\n");
- goto err_buffer_cleanup;
- }
-
ret = pm_runtime_set_active(&client->dev);
if (ret)
- goto err_iio_unregister;
+ goto err_buffer_cleanup;
pm_runtime_enable(&client->dev);
pm_runtime_set_autosuspend_delay(&client->dev,
BMC150_MAGN_AUTO_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(&client->dev);
- dev_dbg(&indio_dev->dev, "Registered device %s\n", name);
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "unable to register iio device\n");
+ goto err_buffer_cleanup;
+ }
+ dev_dbg(&indio_dev->dev, "Registered device %s\n", name);
return 0;
-err_iio_unregister:
- iio_device_unregister(indio_dev);
err_buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
err_free_irq:
@@ -967,11 +964,12 @@ static int bmc150_magn_remove(struct i2c_client *client)
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct bmc150_magn_data *data = iio_priv(indio_dev);
+ iio_device_unregister(indio_dev);
+
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
- iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
if (client->irq > 0)
diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
index 961f9f990faf..93e29fb67fa0 100644
--- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
+++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
@@ -13,7 +13,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * TODO: runtime pm, interrupt mode, and signal strength reporting
+ * TODO: interrupt mode, and signal strength reporting
*/
#include <linux/err.h>
@@ -21,6 +21,7 @@
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/module.h>
+#include <linux/pm_runtime.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
@@ -35,8 +36,11 @@
#define LIDAR_REG_STATUS_INVALID BIT(3)
#define LIDAR_REG_STATUS_READY BIT(0)
-#define LIDAR_REG_DATA_HBYTE 0x0f
-#define LIDAR_REG_DATA_LBYTE 0x10
+#define LIDAR_REG_DATA_HBYTE 0x0f
+#define LIDAR_REG_DATA_LBYTE 0x10
+#define LIDAR_REG_DATA_WORD_READ BIT(7)
+
+#define LIDAR_REG_PWR_CONTROL 0x65
#define LIDAR_DRV_NAME "lidar"
@@ -44,6 +48,9 @@ struct lidar_data {
struct iio_dev *indio_dev;
struct i2c_client *client;
+ int (*xfer)(struct lidar_data *data, u8 reg, u8 *val, int len);
+ int i2c_enabled;
+
u16 buffer[8]; /* 2 byte distance + 8 byte timestamp */
};
@@ -62,7 +69,28 @@ static const struct iio_chan_spec lidar_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(1),
};
-static int lidar_read_byte(struct lidar_data *data, int reg)
+static int lidar_i2c_xfer(struct lidar_data *data, u8 reg, u8 *val, int len)
+{
+ struct i2c_client *client = data->client;
+ struct i2c_msg msg[2];
+ int ret;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = client->flags | I2C_M_STOP;
+ msg[0].len = 1;
+ msg[0].buf = (char *) &reg;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = client->flags | I2C_M_RD;
+ msg[1].len = len;
+ msg[1].buf = (char *) val;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+
+ return (ret == 2) ? 0 : ret;
+}
+
+static int lidar_smbus_xfer(struct lidar_data *data, u8 reg, u8 *val, int len)
{
struct i2c_client *client = data->client;
int ret;
@@ -72,17 +100,35 @@ static int lidar_read_byte(struct lidar_data *data, int reg)
* so in turn i2c_smbus_read_byte_data cannot be used
*/
- ret = i2c_smbus_write_byte(client, reg);
- if (ret < 0) {
- dev_err(&client->dev, "cannot write addr value");
- return ret;
+ while (len--) {
+ ret = i2c_smbus_write_byte(client, reg++);
+ if (ret < 0) {
+ dev_err(&client->dev, "cannot write addr value");
+ return ret;
+ }
+
+ ret = i2c_smbus_read_byte(client);
+ if (ret < 0) {
+ dev_err(&client->dev, "cannot read data value");
+ return ret;
+ }
+
+ *(val++) = ret;
}
- ret = i2c_smbus_read_byte(client);
+ return 0;
+}
+
+static int lidar_read_byte(struct lidar_data *data, u8 reg)
+{
+ int ret;
+ u8 val;
+
+ ret = data->xfer(data, reg, &val, 1);
if (ret < 0)
- dev_err(&client->dev, "cannot read data value");
+ return ret;
- return ret;
+ return val;
}
static inline int lidar_write_control(struct lidar_data *data, int val)
@@ -90,24 +136,22 @@ static inline int lidar_write_control(struct lidar_data *data, int val)
return i2c_smbus_write_byte_data(data->client, LIDAR_REG_CONTROL, val);
}
-static int lidar_read_measurement(struct lidar_data *data, u16 *reg)
+static inline int lidar_write_power(struct lidar_data *data, int val)
{
- int ret;
- int val;
-
- ret = lidar_read_byte(data, LIDAR_REG_DATA_HBYTE);
- if (ret < 0)
- return ret;
- val = ret << 8;
+ return i2c_smbus_write_byte_data(data->client,
+ LIDAR_REG_PWR_CONTROL, val);
+}
- ret = lidar_read_byte(data, LIDAR_REG_DATA_LBYTE);
- if (ret < 0)
- return ret;
+static int lidar_read_measurement(struct lidar_data *data, u16 *reg)
+{
+ int ret = data->xfer(data, LIDAR_REG_DATA_HBYTE |
+ (data->i2c_enabled ? LIDAR_REG_DATA_WORD_READ : 0),
+ (u8 *) reg, 2);
- val |= ret;
- *reg = val;
+ if (!ret)
+ *reg = be16_to_cpu(*reg);
- return 0;
+ return ret;
}
static int lidar_get_measurement(struct lidar_data *data, u16 *reg)
@@ -116,6 +160,8 @@ static int lidar_get_measurement(struct lidar_data *data, u16 *reg)
int tries = 10;
int ret;
+ pm_runtime_get_sync(&client->dev);
+
/* start sample */
ret = lidar_write_control(data, LIDAR_REG_CONTROL_ACQUIRE);
if (ret < 0) {
@@ -130,10 +176,10 @@ static int lidar_get_measurement(struct lidar_data *data, u16 *reg)
if (ret < 0)
break;
- /* return 0 since laser is likely pointed out of range */
+ /* return -EINVAL since laser is likely pointed out of range */
if (ret & LIDAR_REG_STATUS_INVALID) {
*reg = 0;
- ret = 0;
+ ret = -EINVAL;
break;
}
@@ -144,6 +190,8 @@ static int lidar_get_measurement(struct lidar_data *data, u16 *reg)
}
ret = -EIO;
}
+ pm_runtime_mark_last_busy(&client->dev);
+ pm_runtime_put_autosuspend(&client->dev);
return ret;
}
@@ -197,7 +245,7 @@ static irqreturn_t lidar_trigger_handler(int irq, void *private)
if (!ret) {
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
iio_get_time_ns());
- } else {
+ } else if (ret != -EINVAL) {
dev_err(&data->client->dev, "cannot read LIDAR measurement");
}
@@ -221,6 +269,16 @@ static int lidar_probe(struct i2c_client *client,
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
+ data = iio_priv(indio_dev);
+
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ data->xfer = lidar_i2c_xfer;
+ data->i2c_enabled = 1;
+ } else if (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE))
+ data->xfer = lidar_smbus_xfer;
+ else
+ return -ENOTSUPP;
indio_dev->info = &lidar_info;
indio_dev->name = LIDAR_DRV_NAME;
@@ -228,7 +286,6 @@ static int lidar_probe(struct i2c_client *client,
indio_dev->num_channels = ARRAY_SIZE(lidar_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
- data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
@@ -243,6 +300,17 @@ static int lidar_probe(struct i2c_client *client,
if (ret)
goto error_unreg_buffer;
+ pm_runtime_set_autosuspend_delay(&client->dev, 1000);
+ pm_runtime_use_autosuspend(&client->dev);
+
+ ret = pm_runtime_set_active(&client->dev);
+ if (ret)
+ goto error_unreg_buffer;
+ pm_runtime_enable(&client->dev);
+
+ pm_runtime_mark_last_busy(&client->dev);
+ pm_runtime_idle(&client->dev);
+
return 0;
error_unreg_buffer:
@@ -258,6 +326,9 @@ static int lidar_remove(struct i2c_client *client)
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+
return 0;
}
@@ -273,10 +344,38 @@ static const struct of_device_id lidar_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, lidar_dt_ids);
+#ifdef CONFIG_PM
+static int lidar_pm_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct lidar_data *data = iio_priv(indio_dev);
+
+ return lidar_write_power(data, 0x0f);
+}
+
+static int lidar_pm_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct lidar_data *data = iio_priv(indio_dev);
+ int ret = lidar_write_power(data, 0);
+
+ /* regulator and FPGA needs settling time */
+ usleep_range(15000, 20000);
+
+ return ret;
+}
+#endif
+
+static const struct dev_pm_ops lidar_pm_ops = {
+ SET_RUNTIME_PM_OPS(lidar_pm_runtime_suspend,
+ lidar_pm_runtime_resume, NULL)
+};
+
static struct i2c_driver lidar_driver = {
.driver = {
.name = LIDAR_DRV_NAME,
.of_match_table = of_match_ptr(lidar_dt_ids),
+ .pm = &lidar_pm_ops,
},
.probe = lidar_probe,
.remove = lidar_remove,
diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig
index 79996123a71b..519e6772f6f5 100644
--- a/drivers/iio/trigger/Kconfig
+++ b/drivers/iio/trigger/Kconfig
@@ -5,6 +5,16 @@
menu "Triggers - standalone"
+config IIO_HRTIMER_TRIGGER
+ tristate "High resolution timer trigger"
+ depends on IIO_SW_TRIGGER
+ help
+ Provides a frequency based IIO trigger using high resolution
+ timers as interrupt source.
+
+ To compile this driver as a module, choose M here: the
+ module will be called iio-trig-hrtimer.
+
config IIO_INTERRUPT_TRIGGER
tristate "Generic interrupt trigger"
help
diff --git a/drivers/iio/trigger/Makefile b/drivers/iio/trigger/Makefile
index 0694daecaf22..fe06eb564367 100644
--- a/drivers/iio/trigger/Makefile
+++ b/drivers/iio/trigger/Makefile
@@ -3,5 +3,7 @@
#
# When adding new entries keep the list in alphabetical order
+
+obj-$(CONFIG_IIO_HRTIMER_TRIGGER) += iio-trig-hrtimer.o
obj-$(CONFIG_IIO_INTERRUPT_TRIGGER) += iio-trig-interrupt.o
obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o
diff --git a/drivers/iio/trigger/iio-trig-hrtimer.c b/drivers/iio/trigger/iio-trig-hrtimer.c
new file mode 100644
index 000000000000..5e6d451febeb
--- /dev/null
+++ b/drivers/iio/trigger/iio-trig-hrtimer.c
@@ -0,0 +1,193 @@
+/**
+ * The industrial I/O periodic hrtimer trigger driver
+ *
+ * Copyright (C) Intuitive Aerial AB
+ * Written by Marten Svanfeldt, marten@intuitiveaerial.com
+ * Copyright (C) 2012, Analog Device Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ * 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 version 2 as published by
+ * the Free Software Foundation.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/hrtimer.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/sw_trigger.h>
+
+/* default sampling frequency - 100Hz */
+#define HRTIMER_DEFAULT_SAMPLING_FREQUENCY 100
+
+struct iio_hrtimer_info {
+ struct iio_sw_trigger swt;
+ struct hrtimer timer;
+ unsigned long sampling_frequency;
+ ktime_t period;
+};
+
+static struct config_item_type iio_hrtimer_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static
+ssize_t iio_hrtimer_show_sampling_frequency(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_trigger *trig = to_iio_trigger(dev);
+ struct iio_hrtimer_info *info = iio_trigger_get_drvdata(trig);
+
+ return snprintf(buf, PAGE_SIZE, "%lu\n", info->sampling_frequency);
+}
+
+static
+ssize_t iio_hrtimer_store_sampling_frequency(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_trigger *trig = to_iio_trigger(dev);
+ struct iio_hrtimer_info *info = iio_trigger_get_drvdata(trig);
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ if (!val || val > NSEC_PER_SEC)
+ return -EINVAL;
+
+ info->sampling_frequency = val;
+ info->period = ktime_set(0, NSEC_PER_SEC / val);
+
+ return len;
+}
+
+static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR,
+ iio_hrtimer_show_sampling_frequency,
+ iio_hrtimer_store_sampling_frequency);
+
+static struct attribute *iio_hrtimer_attrs[] = {
+ &dev_attr_sampling_frequency.attr,
+ NULL
+};
+
+static const struct attribute_group iio_hrtimer_attr_group = {
+ .attrs = iio_hrtimer_attrs,
+};
+
+static const struct attribute_group *iio_hrtimer_attr_groups[] = {
+ &iio_hrtimer_attr_group,
+ NULL
+};
+
+static enum hrtimer_restart iio_hrtimer_trig_handler(struct hrtimer *timer)
+{
+ struct iio_hrtimer_info *info;
+
+ info = container_of(timer, struct iio_hrtimer_info, timer);
+
+ hrtimer_forward_now(timer, info->period);
+ iio_trigger_poll(info->swt.trigger);
+
+ return HRTIMER_RESTART;
+}
+
+static int iio_trig_hrtimer_set_state(struct iio_trigger *trig, bool state)
+{
+ struct iio_hrtimer_info *trig_info;
+
+ trig_info = iio_trigger_get_drvdata(trig);
+
+ if (state)
+ hrtimer_start(&trig_info->timer, trig_info->period,
+ HRTIMER_MODE_REL);
+ else
+ hrtimer_cancel(&trig_info->timer);
+
+ return 0;
+}
+
+static const struct iio_trigger_ops iio_hrtimer_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = iio_trig_hrtimer_set_state,
+};
+
+static struct iio_sw_trigger *iio_trig_hrtimer_probe(const char *name)
+{
+ struct iio_hrtimer_info *trig_info;
+ int ret;
+
+ trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL);
+ if (!trig_info)
+ return ERR_PTR(-ENOMEM);
+
+ trig_info->swt.trigger = iio_trigger_alloc("%s", name);
+ if (!trig_info->swt.trigger) {
+ ret = -ENOMEM;
+ goto err_free_trig_info;
+ }
+
+ iio_trigger_set_drvdata(trig_info->swt.trigger, trig_info);
+ trig_info->swt.trigger->ops = &iio_hrtimer_trigger_ops;
+ trig_info->swt.trigger->dev.groups = iio_hrtimer_attr_groups;
+
+ hrtimer_init(&trig_info->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ trig_info->timer.function = iio_hrtimer_trig_handler;
+
+ trig_info->sampling_frequency = HRTIMER_DEFAULT_SAMPLING_FREQUENCY;
+ trig_info->period = ktime_set(0, NSEC_PER_SEC /
+ trig_info->sampling_frequency);
+
+ ret = iio_trigger_register(trig_info->swt.trigger);
+ if (ret)
+ goto err_free_trigger;
+
+ iio_swt_group_init_type_name(&trig_info->swt, name, &iio_hrtimer_type);
+ return &trig_info->swt;
+err_free_trigger:
+ iio_trigger_free(trig_info->swt.trigger);
+err_free_trig_info:
+ kfree(trig_info);
+
+ return ERR_PTR(ret);
+}
+
+static int iio_trig_hrtimer_remove(struct iio_sw_trigger *swt)
+{
+ struct iio_hrtimer_info *trig_info;
+
+ trig_info = iio_trigger_get_drvdata(swt->trigger);
+
+ iio_trigger_unregister(swt->trigger);
+
+ /* cancel the timer after unreg to make sure no one rearms it */
+ hrtimer_cancel(&trig_info->timer);
+ iio_trigger_free(swt->trigger);
+ kfree(trig_info);
+
+ return 0;
+}
+
+static const struct iio_sw_trigger_ops iio_trig_hrtimer_ops = {
+ .probe = iio_trig_hrtimer_probe,
+ .remove = iio_trig_hrtimer_remove,
+};
+
+static struct iio_sw_trigger_type iio_trig_hrtimer = {
+ .name = "hrtimer",
+ .owner = THIS_MODULE,
+ .ops = &iio_trig_hrtimer_ops,
+};
+
+module_iio_sw_trigger_driver(iio_trig_hrtimer);
+
+MODULE_AUTHOR("Marten Svanfeldt <marten@intuitiveaerial.com>");
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
+MODULE_DESCRIPTION("Periodic hrtimer trigger for the IIO subsystem");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 944cd90417bc..2d762a2ecd81 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -1126,10 +1126,7 @@ static bool validate_ipv4_net_dev(struct net_device *net_dev,
rcu_read_lock();
err = fib_lookup(dev_net(net_dev), &fl4, &res, 0);
- if (err)
- return false;
-
- ret = FIB_RES_DEV(res) == net_dev;
+ ret = err == 0 && FIB_RES_DEV(res) == net_dev;
rcu_read_unlock();
return ret;
@@ -1268,15 +1265,17 @@ static bool cma_protocol_roce(const struct rdma_cm_id *id)
return cma_protocol_roce_dev_port(device, port_num);
}
-static bool cma_match_net_dev(const struct rdma_id_private *id_priv,
- const struct net_device *net_dev)
+static bool cma_match_net_dev(const struct rdma_cm_id *id,
+ const struct net_device *net_dev,
+ u8 port_num)
{
- const struct rdma_addr *addr = &id_priv->id.route.addr;
+ const struct rdma_addr *addr = &id->route.addr;
if (!net_dev)
/* This request is an AF_IB request or a RoCE request */
- return addr->src_addr.ss_family == AF_IB ||
- cma_protocol_roce(&id_priv->id);
+ return (!id->port_num || id->port_num == port_num) &&
+ (addr->src_addr.ss_family == AF_IB ||
+ cma_protocol_roce_dev_port(id->device, port_num));
return !addr->dev_addr.bound_dev_if ||
(net_eq(dev_net(net_dev), addr->dev_addr.net) &&
@@ -1298,13 +1297,13 @@ static struct rdma_id_private *cma_find_listener(
hlist_for_each_entry(id_priv, &bind_list->owners, node) {
if (cma_match_private_data(id_priv, ib_event->private_data)) {
if (id_priv->id.device == cm_id->device &&
- cma_match_net_dev(id_priv, net_dev))
+ cma_match_net_dev(&id_priv->id, net_dev, req->port))
return id_priv;
list_for_each_entry(id_priv_dev,
&id_priv->listen_list,
listen_list) {
if (id_priv_dev->id.device == cm_id->device &&
- cma_match_net_dev(id_priv_dev, net_dev))
+ cma_match_net_dev(&id_priv_dev->id, net_dev, req->port))
return id_priv_dev;
}
}
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index 8d8af7a41a30..2281de122038 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -1811,6 +1811,11 @@ static int validate_mad(const struct ib_mad_hdr *mad_hdr,
if (qp_num == 0)
valid = 1;
} else {
+ /* CM attributes other than ClassPortInfo only use Send method */
+ if ((mad_hdr->mgmt_class == IB_MGMT_CLASS_CM) &&
+ (mad_hdr->attr_id != IB_MGMT_CLASSPORTINFO_ATTR_ID) &&
+ (mad_hdr->method != IB_MGMT_METHOD_SEND))
+ goto out;
/* Filter GSI packets sent to QP0 */
if (qp_num != 0)
valid = 1;
diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c
index 2aba774f835b..a95a32ba596e 100644
--- a/drivers/infiniband/core/sa_query.c
+++ b/drivers/infiniband/core/sa_query.c
@@ -512,7 +512,7 @@ static int ib_nl_get_path_rec_attrs_len(ib_sa_comp_mask comp_mask)
return len;
}
-static int ib_nl_send_msg(struct ib_sa_query *query)
+static int ib_nl_send_msg(struct ib_sa_query *query, gfp_t gfp_mask)
{
struct sk_buff *skb = NULL;
struct nlmsghdr *nlh;
@@ -526,7 +526,7 @@ static int ib_nl_send_msg(struct ib_sa_query *query)
if (len <= 0)
return -EMSGSIZE;
- skb = nlmsg_new(len, GFP_KERNEL);
+ skb = nlmsg_new(len, gfp_mask);
if (!skb)
return -ENOMEM;
@@ -544,7 +544,7 @@ static int ib_nl_send_msg(struct ib_sa_query *query)
/* Repair the nlmsg header length */
nlmsg_end(skb, nlh);
- ret = ibnl_multicast(skb, nlh, RDMA_NL_GROUP_LS, GFP_KERNEL);
+ ret = ibnl_multicast(skb, nlh, RDMA_NL_GROUP_LS, gfp_mask);
if (!ret)
ret = len;
else
@@ -553,7 +553,7 @@ static int ib_nl_send_msg(struct ib_sa_query *query)
return ret;
}
-static int ib_nl_make_request(struct ib_sa_query *query)
+static int ib_nl_make_request(struct ib_sa_query *query, gfp_t gfp_mask)
{
unsigned long flags;
unsigned long delay;
@@ -562,25 +562,27 @@ static int ib_nl_make_request(struct ib_sa_query *query)
INIT_LIST_HEAD(&query->list);
query->seq = (u32)atomic_inc_return(&ib_nl_sa_request_seq);
+ /* Put the request on the list first.*/
spin_lock_irqsave(&ib_nl_request_lock, flags);
- ret = ib_nl_send_msg(query);
- if (ret <= 0) {
- ret = -EIO;
- goto request_out;
- } else {
- ret = 0;
- }
-
delay = msecs_to_jiffies(sa_local_svc_timeout_ms);
query->timeout = delay + jiffies;
list_add_tail(&query->list, &ib_nl_request_list);
/* Start the timeout if this is the only request */
if (ib_nl_request_list.next == &query->list)
queue_delayed_work(ib_nl_wq, &ib_nl_timed_work, delay);
-
-request_out:
spin_unlock_irqrestore(&ib_nl_request_lock, flags);
+ ret = ib_nl_send_msg(query, gfp_mask);
+ if (ret <= 0) {
+ ret = -EIO;
+ /* Remove the request */
+ spin_lock_irqsave(&ib_nl_request_lock, flags);
+ list_del(&query->list);
+ spin_unlock_irqrestore(&ib_nl_request_lock, flags);
+ } else {
+ ret = 0;
+ }
+
return ret;
}
@@ -1108,7 +1110,7 @@ static int send_mad(struct ib_sa_query *query, int timeout_ms, gfp_t gfp_mask)
if (query->flags & IB_SA_ENABLE_LOCAL_SERVICE) {
if (!ibnl_chk_listeners(RDMA_NL_GROUP_LS)) {
- if (!ib_nl_make_request(query))
+ if (!ib_nl_make_request(query, gfp_mask))
return id;
}
ib_sa_disable_local_svc(query);
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index 94816aeb95a0..1c02deab068f 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -62,9 +62,11 @@ static struct uverbs_lock_class rule_lock_class = { .name = "RULE-uobj" };
* The ib_uobject locking scheme is as follows:
*
* - ib_uverbs_idr_lock protects the uverbs idrs themselves, so it
- * needs to be held during all idr operations. When an object is
+ * needs to be held during all idr write operations. When an object is
* looked up, a reference must be taken on the object's kref before
- * dropping this lock.
+ * dropping this lock. For read operations, the rcu_read_lock()
+ * and rcu_write_lock() but similarly the kref reference is grabbed
+ * before the rcu_read_unlock().
*
* - Each object also has an rwsem. This rwsem must be held for
* reading while an operation that uses the object is performed.
@@ -96,7 +98,7 @@ static void init_uobj(struct ib_uobject *uobj, u64 user_handle,
static void release_uobj(struct kref *kref)
{
- kfree(container_of(kref, struct ib_uobject, ref));
+ kfree_rcu(container_of(kref, struct ib_uobject, ref), rcu);
}
static void put_uobj(struct ib_uobject *uobj)
@@ -145,7 +147,7 @@ static struct ib_uobject *__idr_get_uobj(struct idr *idr, int id,
{
struct ib_uobject *uobj;
- spin_lock(&ib_uverbs_idr_lock);
+ rcu_read_lock();
uobj = idr_find(idr, id);
if (uobj) {
if (uobj->context == context)
@@ -153,7 +155,7 @@ static struct ib_uobject *__idr_get_uobj(struct idr *idr, int id,
else
uobj = NULL;
}
- spin_unlock(&ib_uverbs_idr_lock);
+ rcu_read_unlock();
return uobj;
}
@@ -2446,6 +2448,7 @@ ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file,
int i, sg_ind;
int is_ud;
ssize_t ret = -EINVAL;
+ size_t next_size;
if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;
@@ -2490,7 +2493,8 @@ ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file,
goto out_put;
}
- ud = alloc_wr(sizeof(*ud), user_wr->num_sge);
+ next_size = sizeof(*ud);
+ ud = alloc_wr(next_size, user_wr->num_sge);
if (!ud) {
ret = -ENOMEM;
goto out_put;
@@ -2511,7 +2515,8 @@ ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file,
user_wr->opcode == IB_WR_RDMA_READ) {
struct ib_rdma_wr *rdma;
- rdma = alloc_wr(sizeof(*rdma), user_wr->num_sge);
+ next_size = sizeof(*rdma);
+ rdma = alloc_wr(next_size, user_wr->num_sge);
if (!rdma) {
ret = -ENOMEM;
goto out_put;
@@ -2525,7 +2530,8 @@ ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file,
user_wr->opcode == IB_WR_ATOMIC_FETCH_AND_ADD) {
struct ib_atomic_wr *atomic;
- atomic = alloc_wr(sizeof(*atomic), user_wr->num_sge);
+ next_size = sizeof(*atomic);
+ atomic = alloc_wr(next_size, user_wr->num_sge);
if (!atomic) {
ret = -ENOMEM;
goto out_put;
@@ -2540,7 +2546,8 @@ ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file,
} else if (user_wr->opcode == IB_WR_SEND ||
user_wr->opcode == IB_WR_SEND_WITH_IMM ||
user_wr->opcode == IB_WR_SEND_WITH_INV) {
- next = alloc_wr(sizeof(*next), user_wr->num_sge);
+ next_size = sizeof(*next);
+ next = alloc_wr(next_size, user_wr->num_sge);
if (!next) {
ret = -ENOMEM;
goto out_put;
@@ -2572,7 +2579,7 @@ ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file,
if (next->num_sge) {
next->sg_list = (void *) next +
- ALIGN(sizeof *next, sizeof (struct ib_sge));
+ ALIGN(next_size, sizeof(struct ib_sge));
if (copy_from_user(next->sg_list,
buf + sizeof cmd +
cmd.wr_count * cmd.wqe_size +
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
index 043a60ee6836..545906dec26d 100644
--- a/drivers/infiniband/core/verbs.c
+++ b/drivers/infiniband/core/verbs.c
@@ -1516,7 +1516,7 @@ EXPORT_SYMBOL(ib_map_mr_sg);
* @sg_nents: number of entries in sg
* @set_page: driver page assignment function pointer
*
- * Core service helper for drivers to covert the largest
+ * Core service helper for drivers to convert the largest
* prefix of given sg list to a page vector. The sg list
* prefix converted is the prefix that meet the requirements
* of ib_map_mr_sg.
@@ -1533,7 +1533,7 @@ int ib_sg_to_pages(struct ib_mr *mr,
u64 last_end_dma_addr = 0, last_page_addr = 0;
unsigned int last_page_off = 0;
u64 page_mask = ~((u64)mr->page_size - 1);
- int i;
+ int i, ret;
mr->iova = sg_dma_address(&sgl[0]);
mr->length = 0;
@@ -1544,27 +1544,29 @@ int ib_sg_to_pages(struct ib_mr *mr,
u64 end_dma_addr = dma_addr + dma_len;
u64 page_addr = dma_addr & page_mask;
- if (i && page_addr != dma_addr) {
- if (last_end_dma_addr != dma_addr) {
- /* gap */
- goto done;
-
- } else if (last_page_off + dma_len <= mr->page_size) {
- /* chunk this fragment with the last */
- mr->length += dma_len;
- last_end_dma_addr += dma_len;
- last_page_off += dma_len;
- continue;
- } else {
- /* map starting from the next page */
- page_addr = last_page_addr + mr->page_size;
- dma_len -= mr->page_size - last_page_off;
- }
+ /*
+ * For the second and later elements, check whether either the
+ * end of element i-1 or the start of element i is not aligned
+ * on a page boundary.
+ */
+ if (i && (last_page_off != 0 || page_addr != dma_addr)) {
+ /* Stop mapping if there is a gap. */
+ if (last_end_dma_addr != dma_addr)
+ break;
+
+ /*
+ * Coalesce this element with the last. If it is small
+ * enough just update mr->length. Otherwise start
+ * mapping from the next page.
+ */
+ goto next_page;
}
do {
- if (unlikely(set_page(mr, page_addr)))
- goto done;
+ ret = set_page(mr, page_addr);
+ if (unlikely(ret < 0))
+ return i ? : ret;
+next_page:
page_addr += mr->page_size;
} while (page_addr < end_dma_addr);
@@ -1574,7 +1576,6 @@ int ib_sg_to_pages(struct ib_mr *mr,
last_page_off = end_dma_addr & ~page_mask;
}
-done:
return i;
}
EXPORT_SYMBOL(ib_sg_to_pages);
diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
index c9cffced00ca..326d07d823a5 100644
--- a/drivers/infiniband/hw/cxgb4/cm.c
+++ b/drivers/infiniband/hw/cxgb4/cm.c
@@ -449,7 +449,7 @@ static void act_open_req_arp_failure(void *handle, struct sk_buff *skb)
{
struct c4iw_ep *ep = handle;
- printk(KERN_ERR MOD "ARP failure duing connect\n");
+ printk(KERN_ERR MOD "ARP failure during connect\n");
kfree_skb(skb);
connect_reply_upcall(ep, -EHOSTUNREACH);
state_set(&ep->com, DEAD);
diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c
index 870e56b6b25f..26833bfa639b 100644
--- a/drivers/infiniband/hw/mlx4/mad.c
+++ b/drivers/infiniband/hw/mlx4/mad.c
@@ -40,6 +40,7 @@
#include <linux/gfp.h>
#include <rdma/ib_pma.h>
+#include <linux/mlx4/driver.h>
#include "mlx4_ib.h"
enum {
@@ -606,8 +607,8 @@ static int mlx4_ib_demux_mad(struct ib_device *ibdev, u8 port,
struct ib_mad *mad)
{
struct mlx4_ib_dev *dev = to_mdev(ibdev);
- int err;
- int slave;
+ int err, other_port;
+ int slave = -1;
u8 *slave_id;
int is_eth = 0;
@@ -625,7 +626,17 @@ static int mlx4_ib_demux_mad(struct ib_device *ibdev, u8 port,
mlx4_ib_warn(ibdev, "RoCE mgmt class is not CM\n");
return -EINVAL;
}
- if (mlx4_get_slave_from_roce_gid(dev->dev, port, grh->dgid.raw, &slave)) {
+ err = mlx4_get_slave_from_roce_gid(dev->dev, port, grh->dgid.raw, &slave);
+ if (err && mlx4_is_mf_bonded(dev->dev)) {
+ other_port = (port == 1) ? 2 : 1;
+ err = mlx4_get_slave_from_roce_gid(dev->dev, other_port, grh->dgid.raw, &slave);
+ if (!err) {
+ port = other_port;
+ pr_debug("resolved slave %d from gid %pI6 wire port %d other %d\n",
+ slave, grh->dgid.raw, port, other_port);
+ }
+ }
+ if (err) {
mlx4_ib_warn(ibdev, "failed matching grh\n");
return -ENOENT;
}
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index f567160a4a56..97d6878f9938 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -456,7 +456,7 @@ static int mlx4_ib_query_device(struct ib_device *ibdev,
props->max_qp_wr = dev->dev->caps.max_wqes - MLX4_IB_SQ_MAX_SPARE;
props->max_sge = min(dev->dev->caps.max_sq_sg,
dev->dev->caps.max_rq_sg);
- props->max_sge_rd = props->max_sge;
+ props->max_sge_rd = MLX4_MAX_SGE_RD;
props->max_cq = dev->dev->quotas.cq;
props->max_cqe = dev->dev->caps.max_cqes;
props->max_mr = dev->dev->quotas.mpt;
diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c
index a2e4ca56da44..13eaaf45288f 100644
--- a/drivers/infiniband/hw/mlx4/qp.c
+++ b/drivers/infiniband/hw/mlx4/qp.c
@@ -34,6 +34,7 @@
#include <linux/log2.h>
#include <linux/slab.h>
#include <linux/netdevice.h>
+#include <linux/vmalloc.h>
#include <rdma/ib_cache.h>
#include <rdma/ib_pack.h>
@@ -795,8 +796,14 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd,
if (err)
goto err_mtt;
- qp->sq.wrid = kmalloc(qp->sq.wqe_cnt * sizeof (u64), gfp);
- qp->rq.wrid = kmalloc(qp->rq.wqe_cnt * sizeof (u64), gfp);
+ qp->sq.wrid = kmalloc(qp->sq.wqe_cnt * sizeof(u64), gfp);
+ if (!qp->sq.wrid)
+ qp->sq.wrid = __vmalloc(qp->sq.wqe_cnt * sizeof(u64),
+ gfp, PAGE_KERNEL);
+ qp->rq.wrid = kmalloc(qp->rq.wqe_cnt * sizeof(u64), gfp);
+ if (!qp->rq.wrid)
+ qp->rq.wrid = __vmalloc(qp->rq.wqe_cnt * sizeof(u64),
+ gfp, PAGE_KERNEL);
if (!qp->sq.wrid || !qp->rq.wrid) {
err = -ENOMEM;
goto err_wrid;
@@ -886,8 +893,8 @@ err_wrid:
if (qp_has_rq(init_attr))
mlx4_ib_db_unmap_user(to_mucontext(pd->uobject->context), &qp->db);
} else {
- kfree(qp->sq.wrid);
- kfree(qp->rq.wrid);
+ kvfree(qp->sq.wrid);
+ kvfree(qp->rq.wrid);
}
err_mtt:
@@ -1062,8 +1069,8 @@ static void destroy_qp_common(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp,
&qp->db);
ib_umem_release(qp->umem);
} else {
- kfree(qp->sq.wrid);
- kfree(qp->rq.wrid);
+ kvfree(qp->sq.wrid);
+ kvfree(qp->rq.wrid);
if (qp->mlx4_ib_qp_type & (MLX4_IB_QPT_PROXY_SMI_OWNER |
MLX4_IB_QPT_PROXY_SMI | MLX4_IB_QPT_PROXY_GSI))
free_proxy_bufs(&dev->ib_dev, qp);
diff --git a/drivers/infiniband/hw/mlx4/srq.c b/drivers/infiniband/hw/mlx4/srq.c
index dce5dfe3a70e..c394376ebe06 100644
--- a/drivers/infiniband/hw/mlx4/srq.c
+++ b/drivers/infiniband/hw/mlx4/srq.c
@@ -34,6 +34,7 @@
#include <linux/mlx4/qp.h>
#include <linux/mlx4/srq.h>
#include <linux/slab.h>
+#include <linux/vmalloc.h>
#include "mlx4_ib.h"
#include "user.h"
@@ -172,8 +173,12 @@ struct ib_srq *mlx4_ib_create_srq(struct ib_pd *pd,
srq->wrid = kmalloc(srq->msrq.max * sizeof (u64), GFP_KERNEL);
if (!srq->wrid) {
- err = -ENOMEM;
- goto err_mtt;
+ srq->wrid = __vmalloc(srq->msrq.max * sizeof(u64),
+ GFP_KERNEL, PAGE_KERNEL);
+ if (!srq->wrid) {
+ err = -ENOMEM;
+ goto err_mtt;
+ }
}
}
@@ -204,7 +209,7 @@ err_wrid:
if (pd->uobject)
mlx4_ib_db_unmap_user(to_mucontext(pd->uobject->context), &srq->db);
else
- kfree(srq->wrid);
+ kvfree(srq->wrid);
err_mtt:
mlx4_mtt_cleanup(dev->dev, &srq->mtt);
@@ -281,7 +286,7 @@ int mlx4_ib_destroy_srq(struct ib_srq *srq)
mlx4_ib_db_unmap_user(to_mucontext(srq->uobject->context), &msrq->db);
ib_umem_release(msrq->umem);
} else {
- kfree(msrq->wrid);
+ kvfree(msrq->wrid);
mlx4_buf_free(dev->dev, msrq->msrq.max << msrq->msrq.wqe_shift,
&msrq->buf);
mlx4_db_free(dev->dev, &msrq->db);
diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
index 7e97cb55a6bf..b0ec175cc6ba 100644
--- a/drivers/infiniband/hw/mlx5/main.c
+++ b/drivers/infiniband/hw/mlx5/main.c
@@ -43,6 +43,9 @@
#include <linux/mlx5/vport.h>
#include <rdma/ib_smi.h>
#include <rdma/ib_umem.h>
+#include <linux/in.h>
+#include <linux/etherdevice.h>
+#include <linux/mlx5/fs.h>
#include "user.h"
#include "mlx5_ib.h"
@@ -835,6 +838,457 @@ static int mlx5_ib_dealloc_pd(struct ib_pd *pd)
return 0;
}
+static bool outer_header_zero(u32 *match_criteria)
+{
+ int size = MLX5_ST_SZ_BYTES(fte_match_param);
+ char *outer_headers_c = MLX5_ADDR_OF(fte_match_param, match_criteria,
+ outer_headers);
+
+ return outer_headers_c[0] == 0 && !memcmp(outer_headers_c,
+ outer_headers_c + 1,
+ size - 1);
+}
+
+static int parse_flow_attr(u32 *match_c, u32 *match_v,
+ union ib_flow_spec *ib_spec)
+{
+ void *outer_headers_c = MLX5_ADDR_OF(fte_match_param, match_c,
+ outer_headers);
+ void *outer_headers_v = MLX5_ADDR_OF(fte_match_param, match_v,
+ outer_headers);
+ switch (ib_spec->type) {
+ case IB_FLOW_SPEC_ETH:
+ if (ib_spec->size != sizeof(ib_spec->eth))
+ return -EINVAL;
+
+ ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_c,
+ dmac_47_16),
+ ib_spec->eth.mask.dst_mac);
+ ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_v,
+ dmac_47_16),
+ ib_spec->eth.val.dst_mac);
+
+ if (ib_spec->eth.mask.vlan_tag) {
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c,
+ vlan_tag, 1);
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v,
+ vlan_tag, 1);
+
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c,
+ first_vid, ntohs(ib_spec->eth.mask.vlan_tag));
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v,
+ first_vid, ntohs(ib_spec->eth.val.vlan_tag));
+
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c,
+ first_cfi,
+ ntohs(ib_spec->eth.mask.vlan_tag) >> 12);
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v,
+ first_cfi,
+ ntohs(ib_spec->eth.val.vlan_tag) >> 12);
+
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c,
+ first_prio,
+ ntohs(ib_spec->eth.mask.vlan_tag) >> 13);
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v,
+ first_prio,
+ ntohs(ib_spec->eth.val.vlan_tag) >> 13);
+ }
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c,
+ ethertype, ntohs(ib_spec->eth.mask.ether_type));
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v,
+ ethertype, ntohs(ib_spec->eth.val.ether_type));
+ break;
+ case IB_FLOW_SPEC_IPV4:
+ if (ib_spec->size != sizeof(ib_spec->ipv4))
+ return -EINVAL;
+
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c,
+ ethertype, 0xffff);
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v,
+ ethertype, ETH_P_IP);
+
+ memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_c,
+ src_ipv4_src_ipv6.ipv4_layout.ipv4),
+ &ib_spec->ipv4.mask.src_ip,
+ sizeof(ib_spec->ipv4.mask.src_ip));
+ memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_v,
+ src_ipv4_src_ipv6.ipv4_layout.ipv4),
+ &ib_spec->ipv4.val.src_ip,
+ sizeof(ib_spec->ipv4.val.src_ip));
+ memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_c,
+ dst_ipv4_dst_ipv6.ipv4_layout.ipv4),
+ &ib_spec->ipv4.mask.dst_ip,
+ sizeof(ib_spec->ipv4.mask.dst_ip));
+ memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, outer_headers_v,
+ dst_ipv4_dst_ipv6.ipv4_layout.ipv4),
+ &ib_spec->ipv4.val.dst_ip,
+ sizeof(ib_spec->ipv4.val.dst_ip));
+ break;
+ case IB_FLOW_SPEC_TCP:
+ if (ib_spec->size != sizeof(ib_spec->tcp_udp))
+ return -EINVAL;
+
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, ip_protocol,
+ 0xff);
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, ip_protocol,
+ IPPROTO_TCP);
+
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, tcp_sport,
+ ntohs(ib_spec->tcp_udp.mask.src_port));
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, tcp_sport,
+ ntohs(ib_spec->tcp_udp.val.src_port));
+
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, tcp_dport,
+ ntohs(ib_spec->tcp_udp.mask.dst_port));
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, tcp_dport,
+ ntohs(ib_spec->tcp_udp.val.dst_port));
+ break;
+ case IB_FLOW_SPEC_UDP:
+ if (ib_spec->size != sizeof(ib_spec->tcp_udp))
+ return -EINVAL;
+
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, ip_protocol,
+ 0xff);
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, ip_protocol,
+ IPPROTO_UDP);
+
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, udp_sport,
+ ntohs(ib_spec->tcp_udp.mask.src_port));
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, udp_sport,
+ ntohs(ib_spec->tcp_udp.val.src_port));
+
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_c, udp_dport,
+ ntohs(ib_spec->tcp_udp.mask.dst_port));
+ MLX5_SET(fte_match_set_lyr_2_4, outer_headers_v, udp_dport,
+ ntohs(ib_spec->tcp_udp.val.dst_port));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* If a flow could catch both multicast and unicast packets,
+ * it won't fall into the multicast flow steering table and this rule
+ * could steal other multicast packets.
+ */
+static bool flow_is_multicast_only(struct ib_flow_attr *ib_attr)
+{
+ struct ib_flow_spec_eth *eth_spec;
+
+ if (ib_attr->type != IB_FLOW_ATTR_NORMAL ||
+ ib_attr->size < sizeof(struct ib_flow_attr) +
+ sizeof(struct ib_flow_spec_eth) ||
+ ib_attr->num_of_specs < 1)
+ return false;
+
+ eth_spec = (struct ib_flow_spec_eth *)(ib_attr + 1);
+ if (eth_spec->type != IB_FLOW_SPEC_ETH ||
+ eth_spec->size != sizeof(*eth_spec))
+ return false;
+
+ return is_multicast_ether_addr(eth_spec->mask.dst_mac) &&
+ is_multicast_ether_addr(eth_spec->val.dst_mac);
+}
+
+static bool is_valid_attr(struct ib_flow_attr *flow_attr)
+{
+ union ib_flow_spec *ib_spec = (union ib_flow_spec *)(flow_attr + 1);
+ bool has_ipv4_spec = false;
+ bool eth_type_ipv4 = true;
+ unsigned int spec_index;
+
+ /* Validate that ethertype is correct */
+ for (spec_index = 0; spec_index < flow_attr->num_of_specs; spec_index++) {
+ if (ib_spec->type == IB_FLOW_SPEC_ETH &&
+ ib_spec->eth.mask.ether_type) {
+ if (!((ib_spec->eth.mask.ether_type == htons(0xffff)) &&
+ ib_spec->eth.val.ether_type == htons(ETH_P_IP)))
+ eth_type_ipv4 = false;
+ } else if (ib_spec->type == IB_FLOW_SPEC_IPV4) {
+ has_ipv4_spec = true;
+ }
+ ib_spec = (void *)ib_spec + ib_spec->size;
+ }
+ return !has_ipv4_spec || eth_type_ipv4;
+}
+
+static void put_flow_table(struct mlx5_ib_dev *dev,
+ struct mlx5_ib_flow_prio *prio, bool ft_added)
+{
+ prio->refcount -= !!ft_added;
+ if (!prio->refcount) {
+ mlx5_destroy_flow_table(prio->flow_table);
+ prio->flow_table = NULL;
+ }
+}
+
+static int mlx5_ib_destroy_flow(struct ib_flow *flow_id)
+{
+ struct mlx5_ib_dev *dev = to_mdev(flow_id->qp->device);
+ struct mlx5_ib_flow_handler *handler = container_of(flow_id,
+ struct mlx5_ib_flow_handler,
+ ibflow);
+ struct mlx5_ib_flow_handler *iter, *tmp;
+
+ mutex_lock(&dev->flow_db.lock);
+
+ list_for_each_entry_safe(iter, tmp, &handler->list, list) {
+ mlx5_del_flow_rule(iter->rule);
+ list_del(&iter->list);
+ kfree(iter);
+ }
+
+ mlx5_del_flow_rule(handler->rule);
+ put_flow_table(dev, &dev->flow_db.prios[handler->prio], true);
+ mutex_unlock(&dev->flow_db.lock);
+
+ kfree(handler);
+
+ return 0;
+}
+
+#define MLX5_FS_MAX_TYPES 10
+#define MLX5_FS_MAX_ENTRIES 32000UL
+static struct mlx5_ib_flow_prio *get_flow_table(struct mlx5_ib_dev *dev,
+ struct ib_flow_attr *flow_attr)
+{
+ struct mlx5_flow_namespace *ns = NULL;
+ struct mlx5_ib_flow_prio *prio;
+ struct mlx5_flow_table *ft;
+ int num_entries;
+ int num_groups;
+ int priority;
+ int err = 0;
+
+ if (flow_attr->type == IB_FLOW_ATTR_NORMAL) {
+ if (flow_is_multicast_only(flow_attr))
+ priority = MLX5_IB_FLOW_MCAST_PRIO;
+ else
+ priority = flow_attr->priority;
+ ns = mlx5_get_flow_namespace(dev->mdev,
+ MLX5_FLOW_NAMESPACE_BYPASS);
+ num_entries = MLX5_FS_MAX_ENTRIES;
+ num_groups = MLX5_FS_MAX_TYPES;
+ prio = &dev->flow_db.prios[priority];
+ } else if (flow_attr->type == IB_FLOW_ATTR_ALL_DEFAULT ||
+ flow_attr->type == IB_FLOW_ATTR_MC_DEFAULT) {
+ ns = mlx5_get_flow_namespace(dev->mdev,
+ MLX5_FLOW_NAMESPACE_LEFTOVERS);
+ build_leftovers_ft_param(&priority,
+ &num_entries,
+ &num_groups);
+ prio = &dev->flow_db.prios[MLX5_IB_FLOW_LEFTOVERS_PRIO];
+ }
+
+ if (!ns)
+ return ERR_PTR(-ENOTSUPP);
+
+ ft = prio->flow_table;
+ if (!ft) {
+ ft = mlx5_create_auto_grouped_flow_table(ns, priority,
+ num_entries,
+ num_groups);
+
+ if (!IS_ERR(ft)) {
+ prio->refcount = 0;
+ prio->flow_table = ft;
+ } else {
+ err = PTR_ERR(ft);
+ }
+ }
+
+ return err ? ERR_PTR(err) : prio;
+}
+
+static struct mlx5_ib_flow_handler *create_flow_rule(struct mlx5_ib_dev *dev,
+ struct mlx5_ib_flow_prio *ft_prio,
+ struct ib_flow_attr *flow_attr,
+ struct mlx5_flow_destination *dst)
+{
+ struct mlx5_flow_table *ft = ft_prio->flow_table;
+ struct mlx5_ib_flow_handler *handler;
+ void *ib_flow = flow_attr + 1;
+ u8 match_criteria_enable = 0;
+ unsigned int spec_index;
+ u32 *match_c;
+ u32 *match_v;
+ int err = 0;
+
+ if (!is_valid_attr(flow_attr))
+ return ERR_PTR(-EINVAL);
+
+ match_c = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
+ match_v = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
+ handler = kzalloc(sizeof(*handler), GFP_KERNEL);
+ if (!handler || !match_c || !match_v) {
+ err = -ENOMEM;
+ goto free;
+ }
+
+ INIT_LIST_HEAD(&handler->list);
+
+ for (spec_index = 0; spec_index < flow_attr->num_of_specs; spec_index++) {
+ err = parse_flow_attr(match_c, match_v, ib_flow);
+ if (err < 0)
+ goto free;
+
+ ib_flow += ((union ib_flow_spec *)ib_flow)->size;
+ }
+
+ /* Outer header support only */
+ match_criteria_enable = (!outer_header_zero(match_c)) << 0;
+ handler->rule = mlx5_add_flow_rule(ft, match_criteria_enable,
+ match_c, match_v,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG,
+ dst);
+
+ if (IS_ERR(handler->rule)) {
+ err = PTR_ERR(handler->rule);
+ goto free;
+ }
+
+ handler->prio = ft_prio - dev->flow_db.prios;
+
+ ft_prio->flow_table = ft;
+free:
+ if (err)
+ kfree(handler);
+ kfree(match_c);
+ kfree(match_v);
+ return err ? ERR_PTR(err) : handler;
+}
+
+enum {
+ LEFTOVERS_MC,
+ LEFTOVERS_UC,
+};
+
+static struct mlx5_ib_flow_handler *create_leftovers_rule(struct mlx5_ib_dev *dev,
+ struct mlx5_ib_flow_prio *ft_prio,
+ struct ib_flow_attr *flow_attr,
+ struct mlx5_flow_destination *dst)
+{
+ struct mlx5_ib_flow_handler *handler_ucast = NULL;
+ struct mlx5_ib_flow_handler *handler = NULL;
+
+ static struct {
+ struct ib_flow_attr flow_attr;
+ struct ib_flow_spec_eth eth_flow;
+ } leftovers_specs[] = {
+ [LEFTOVERS_MC] = {
+ .flow_attr = {
+ .num_of_specs = 1,
+ .size = sizeof(leftovers_specs[0])
+ },
+ .eth_flow = {
+ .type = IB_FLOW_SPEC_ETH,
+ .size = sizeof(struct ib_flow_spec_eth),
+ .mask = {.dst_mac = {0x1} },
+ .val = {.dst_mac = {0x1} }
+ }
+ },
+ [LEFTOVERS_UC] = {
+ .flow_attr = {
+ .num_of_specs = 1,
+ .size = sizeof(leftovers_specs[0])
+ },
+ .eth_flow = {
+ .type = IB_FLOW_SPEC_ETH,
+ .size = sizeof(struct ib_flow_spec_eth),
+ .mask = {.dst_mac = {0x1} },
+ .val = {.dst_mac = {} }
+ }
+ }
+ };
+
+ handler = create_flow_rule(dev, ft_prio,
+ &leftovers_specs[LEFTOVERS_MC].flow_attr,
+ dst);
+ if (!IS_ERR(handler) &&
+ flow_attr->type == IB_FLOW_ATTR_ALL_DEFAULT) {
+ handler_ucast = create_flow_rule(dev, ft_prio,
+ &leftovers_specs[LEFTOVERS_UC].flow_attr,
+ dst);
+ if (IS_ERR(handler_ucast)) {
+ kfree(handler);
+ handler = handler_ucast;
+ } else {
+ list_add(&handler_ucast->list, &handler->list);
+ }
+ }
+
+ return handler;
+}
+
+static struct ib_flow *mlx5_ib_create_flow(struct ib_qp *qp,
+ struct ib_flow_attr *flow_attr,
+ int domain)
+{
+ struct mlx5_ib_dev *dev = to_mdev(qp->device);
+ struct mlx5_ib_flow_handler *handler = NULL;
+ struct mlx5_flow_destination *dst = NULL;
+ struct mlx5_ib_flow_prio *ft_prio;
+ int err;
+
+ if (flow_attr->priority > MLX5_IB_FLOW_LAST_PRIO)
+ return ERR_PTR(-ENOSPC);
+
+ if (domain != IB_FLOW_DOMAIN_USER ||
+ flow_attr->port > MLX5_CAP_GEN(dev->mdev, num_ports) ||
+ flow_attr->flags)
+ return ERR_PTR(-EINVAL);
+
+ dst = kzalloc(sizeof(*dst), GFP_KERNEL);
+ if (!dst)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_lock(&dev->flow_db.lock);
+
+ ft_prio = get_flow_table(dev, flow_attr);
+ if (IS_ERR(ft_prio)) {
+ err = PTR_ERR(ft_prio);
+ goto unlock;
+ }
+
+ dst->type = MLX5_FLOW_DESTINATION_TYPE_TIR;
+ dst->tir_num = to_mqp(qp)->raw_packet_qp.rq.tirn;
+
+ if (flow_attr->type == IB_FLOW_ATTR_NORMAL) {
+ handler = create_flow_rule(dev, ft_prio, flow_attr,
+ dst);
+ } else if (flow_attr->type == IB_FLOW_ATTR_ALL_DEFAULT ||
+ flow_attr->type == IB_FLOW_ATTR_MC_DEFAULT) {
+ handler = create_leftovers_rule(dev, ft_prio, flow_attr,
+ dst);
+ } else {
+ err = -EINVAL;
+ goto destroy_ft;
+ }
+
+ if (IS_ERR(handler)) {
+ err = PTR_ERR(handler);
+ handler = NULL;
+ goto destroy_ft;
+ }
+
+ ft_prio->refcount++;
+ mutex_unlock(&dev->flow_db.lock);
+ kfree(dst);
+
+ return &handler->ibflow;
+
+destroy_ft:
+ put_flow_table(dev, ft_prio, false);
+unlock:
+ mutex_unlock(&dev->flow_db.lock);
+ kfree(dst);
+ kfree(handler);
+ return ERR_PTR(err);
+}
+
static int mlx5_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
{
struct mlx5_ib_dev *dev = to_mdev(ibqp->device);
@@ -1439,10 +1893,19 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
(1ull << IB_USER_VERBS_CMD_CLOSE_XRCD);
}
+ if (mlx5_ib_port_link_layer(&dev->ib_dev) ==
+ IB_LINK_LAYER_ETHERNET) {
+ dev->ib_dev.create_flow = mlx5_ib_create_flow;
+ dev->ib_dev.destroy_flow = mlx5_ib_destroy_flow;
+ dev->ib_dev.uverbs_ex_cmd_mask |=
+ (1ull << IB_USER_VERBS_EX_CMD_CREATE_FLOW) |
+ (1ull << IB_USER_VERBS_EX_CMD_DESTROY_FLOW);
+ }
err = init_node_data(dev);
if (err)
goto err_dealloc;
+ mutex_init(&dev->flow_db.lock);
mutex_init(&dev->cap_mask_mutex);
err = create_dev_resources(&dev->devr);
diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h
index 633347260b79..1474cccd1e0f 100644
--- a/drivers/infiniband/hw/mlx5/mlx5_ib.h
+++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h
@@ -105,6 +105,36 @@ struct mlx5_ib_pd {
u32 pdn;
};
+#define MLX5_IB_FLOW_MCAST_PRIO (MLX5_BY_PASS_NUM_PRIOS - 1)
+#define MLX5_IB_FLOW_LAST_PRIO (MLX5_IB_FLOW_MCAST_PRIO - 1)
+#if (MLX5_IB_FLOW_LAST_PRIO <= 0)
+#error "Invalid number of bypass priorities"
+#endif
+#define MLX5_IB_FLOW_LEFTOVERS_PRIO (MLX5_IB_FLOW_MCAST_PRIO + 1)
+
+#define MLX5_IB_NUM_FLOW_FT (MLX5_IB_FLOW_LEFTOVERS_PRIO + 1)
+struct mlx5_ib_flow_prio {
+ struct mlx5_flow_table *flow_table;
+ unsigned int refcount;
+};
+
+struct mlx5_ib_flow_handler {
+ struct list_head list;
+ struct ib_flow ibflow;
+ unsigned int prio;
+ struct mlx5_flow_rule *rule;
+};
+
+struct mlx5_ib_flow_db {
+ struct mlx5_ib_flow_prio prios[MLX5_IB_NUM_FLOW_FT];
+ /* Protect flow steering bypass flow tables
+ * when add/del flow rules.
+ * only single add/removal of flow steering rule could be done
+ * simultaneously.
+ */
+ struct mutex lock;
+};
+
/* Use macros here so that don't have to duplicate
* enum ib_send_flags and enum ib_qp_type for low-level driver
*/
@@ -171,9 +201,21 @@ struct mlx5_ib_pfault {
struct mlx5_pagefault mpfault;
};
+struct mlx5_ib_rq {
+ u32 tirn;
+};
+
+struct mlx5_ib_raw_packet_qp {
+ struct mlx5_ib_rq rq;
+};
+
struct mlx5_ib_qp {
struct ib_qp ibqp;
- struct mlx5_core_qp mqp;
+ union {
+ struct mlx5_core_qp mqp;
+ struct mlx5_ib_raw_packet_qp raw_packet_qp;
+ };
+
struct mlx5_buf buf;
struct mlx5_db db;
@@ -431,6 +473,7 @@ struct mlx5_ib_dev {
*/
struct srcu_struct mr_srcu;
#endif
+ struct mlx5_ib_flow_db flow_db;
};
static inline struct mlx5_ib_cq *to_mibcq(struct mlx5_core_cq *mcq)
diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c
index ec8993a7b3be..6000f7aeede9 100644
--- a/drivers/infiniband/hw/mlx5/mr.c
+++ b/drivers/infiniband/hw/mlx5/mr.c
@@ -381,7 +381,19 @@ static void __cache_work_func(struct mlx5_cache_ent *ent)
}
}
} else if (ent->cur > 2 * ent->limit) {
- if (!someone_adding(cache) &&
+ /*
+ * The remove_keys() logic is performed as garbage collection
+ * task. Such task is intended to be run when no other active
+ * processes are running.
+ *
+ * The need_resched() will return TRUE if there are user tasks
+ * to be activated in near future.
+ *
+ * In such case, we don't execute remove_keys() and postpone
+ * the garbage collection work to try to run in next cycle,
+ * in order to free CPU resources to other tasks.
+ */
+ if (!need_resched() && !someone_adding(cache) &&
time_after(jiffies, cache->last_add + 300 * HZ)) {
remove_keys(dev, i, 1);
if (ent->cur > ent->limit)
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma.h b/drivers/infiniband/hw/ocrdma/ocrdma.h
index ae80590aabdf..040bb8b5cb15 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma.h
+++ b/drivers/infiniband/hw/ocrdma/ocrdma.h
@@ -232,6 +232,10 @@ struct phy_info {
u16 interface_type;
};
+enum ocrdma_flags {
+ OCRDMA_FLAGS_LINK_STATUS_INIT = 0x01
+};
+
struct ocrdma_dev {
struct ib_device ibdev;
struct ocrdma_dev_attr attr;
@@ -287,6 +291,7 @@ struct ocrdma_dev {
atomic_t update_sl;
u16 pvid;
u32 asic_id;
+ u32 flags;
ulong last_stats_time;
struct mutex stats_lock; /* provide synch for debugfs operations */
@@ -591,4 +596,9 @@ static inline u8 ocrdma_is_enabled_and_synced(u32 state)
(state & OCRDMA_STATE_FLAG_SYNC);
}
+static inline u8 ocrdma_get_ae_link_state(u32 ae_state)
+{
+ return ((ae_state & OCRDMA_AE_LSC_LS_MASK) >> OCRDMA_AE_LSC_LS_SHIFT);
+}
+
#endif
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_hw.c b/drivers/infiniband/hw/ocrdma/ocrdma_hw.c
index 30f67bebffa3..283ca842ff74 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_hw.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_hw.c
@@ -579,6 +579,8 @@ static int ocrdma_mbx_create_mq(struct ocrdma_dev *dev,
cmd->async_event_bitmap = BIT(OCRDMA_ASYNC_GRP5_EVE_CODE);
cmd->async_event_bitmap |= BIT(OCRDMA_ASYNC_RDMA_EVE_CODE);
+ /* Request link events on this MQ. */
+ cmd->async_event_bitmap |= BIT(OCRDMA_ASYNC_LINK_EVE_CODE);
cmd->async_cqid_ringsize = cq->id;
cmd->async_cqid_ringsize |= (ocrdma_encoded_q_len(mq->len) <<
@@ -819,20 +821,42 @@ static void ocrdma_process_grp5_aync(struct ocrdma_dev *dev,
}
}
+static void ocrdma_process_link_state(struct ocrdma_dev *dev,
+ struct ocrdma_ae_mcqe *cqe)
+{
+ struct ocrdma_ae_lnkst_mcqe *evt;
+ u8 lstate;
+
+ evt = (struct ocrdma_ae_lnkst_mcqe *)cqe;
+ lstate = ocrdma_get_ae_link_state(evt->speed_state_ptn);
+
+ if (!(lstate & OCRDMA_AE_LSC_LLINK_MASK))
+ return;
+
+ if (dev->flags & OCRDMA_FLAGS_LINK_STATUS_INIT)
+ ocrdma_update_link_state(dev, (lstate & OCRDMA_LINK_ST_MASK));
+}
+
static void ocrdma_process_acqe(struct ocrdma_dev *dev, void *ae_cqe)
{
/* async CQE processing */
struct ocrdma_ae_mcqe *cqe = ae_cqe;
u32 evt_code = (cqe->valid_ae_event & OCRDMA_AE_MCQE_EVENT_CODE_MASK) >>
OCRDMA_AE_MCQE_EVENT_CODE_SHIFT;
-
- if (evt_code == OCRDMA_ASYNC_RDMA_EVE_CODE)
+ switch (evt_code) {
+ case OCRDMA_ASYNC_LINK_EVE_CODE:
+ ocrdma_process_link_state(dev, cqe);
+ break;
+ case OCRDMA_ASYNC_RDMA_EVE_CODE:
ocrdma_dispatch_ibevent(dev, cqe);
- else if (evt_code == OCRDMA_ASYNC_GRP5_EVE_CODE)
+ break;
+ case OCRDMA_ASYNC_GRP5_EVE_CODE:
ocrdma_process_grp5_aync(dev, cqe);
- else
+ break;
+ default:
pr_err("%s(%d) invalid evt code=0x%x\n", __func__,
dev->id, evt_code);
+ }
}
static void ocrdma_process_mcqe(struct ocrdma_dev *dev, struct ocrdma_mcqe *cqe)
@@ -1363,7 +1387,8 @@ mbx_err:
return status;
}
-int ocrdma_mbx_get_link_speed(struct ocrdma_dev *dev, u8 *lnk_speed)
+int ocrdma_mbx_get_link_speed(struct ocrdma_dev *dev, u8 *lnk_speed,
+ u8 *lnk_state)
{
int status = -ENOMEM;
struct ocrdma_get_link_speed_rsp *rsp;
@@ -1384,8 +1409,11 @@ int ocrdma_mbx_get_link_speed(struct ocrdma_dev *dev, u8 *lnk_speed)
goto mbx_err;
rsp = (struct ocrdma_get_link_speed_rsp *)cmd;
- *lnk_speed = (rsp->pflt_pps_ld_pnum & OCRDMA_PHY_PS_MASK)
- >> OCRDMA_PHY_PS_SHIFT;
+ if (lnk_speed)
+ *lnk_speed = (rsp->pflt_pps_ld_pnum & OCRDMA_PHY_PS_MASK)
+ >> OCRDMA_PHY_PS_SHIFT;
+ if (lnk_state)
+ *lnk_state = (rsp->res_lnk_st & OCRDMA_LINK_ST_MASK);
mbx_err:
kfree(cmd);
@@ -2515,9 +2543,10 @@ static int ocrdma_set_av_params(struct ocrdma_qp *qp,
ocrdma_cpu_to_le32(&cmd->params.sgid[0], sizeof(cmd->params.sgid));
cmd->params.vlan_dmac_b4_to_b5 = mac_addr[4] | (mac_addr[5] << 8);
- if (vlan_id < 0x1000) {
- if (dev->pfc_state) {
- vlan_id = 0;
+ if (vlan_id == 0xFFFF)
+ vlan_id = 0;
+ if (vlan_id || dev->pfc_state) {
+ if (!vlan_id) {
pr_err("ocrdma%d:Using VLAN with PFC is recommended\n",
dev->id);
pr_err("ocrdma%d:Using VLAN 0 for this connection\n",
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_hw.h b/drivers/infiniband/hw/ocrdma/ocrdma_hw.h
index 7ed885c1851e..ebc1f442aec3 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_hw.h
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_hw.h
@@ -106,7 +106,8 @@ void ocrdma_ring_cq_db(struct ocrdma_dev *, u16 cq_id, bool armed,
bool solicited, u16 cqe_popped);
/* verbs specific mailbox commands */
-int ocrdma_mbx_get_link_speed(struct ocrdma_dev *dev, u8 *lnk_speed);
+int ocrdma_mbx_get_link_speed(struct ocrdma_dev *dev, u8 *lnk_speed,
+ u8 *lnk_st);
int ocrdma_query_config(struct ocrdma_dev *,
struct ocrdma_mbx_query_config *config);
@@ -153,5 +154,6 @@ char *port_speed_string(struct ocrdma_dev *dev);
void ocrdma_init_service_level(struct ocrdma_dev *);
void ocrdma_alloc_pd_pool(struct ocrdma_dev *dev);
void ocrdma_free_pd_range(struct ocrdma_dev *dev);
+void ocrdma_update_link_state(struct ocrdma_dev *dev, u8 lstate);
#endif /* __OCRDMA_HW_H__ */
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_main.c b/drivers/infiniband/hw/ocrdma/ocrdma_main.c
index 62b7009daa6c..3afb40b85159 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_main.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_main.c
@@ -290,6 +290,7 @@ static void ocrdma_remove_sysfiles(struct ocrdma_dev *dev)
static struct ocrdma_dev *ocrdma_add(struct be_dev_info *dev_info)
{
int status = 0, i;
+ u8 lstate = 0;
struct ocrdma_dev *dev;
dev = (struct ocrdma_dev *)ib_alloc_device(sizeof(struct ocrdma_dev));
@@ -319,6 +320,11 @@ static struct ocrdma_dev *ocrdma_add(struct be_dev_info *dev_info)
if (status)
goto alloc_err;
+ /* Query Link state and update */
+ status = ocrdma_mbx_get_link_speed(dev, NULL, &lstate);
+ if (!status)
+ ocrdma_update_link_state(dev, lstate);
+
for (i = 0; i < ARRAY_SIZE(ocrdma_attributes); i++)
if (device_create_file(&dev->ibdev.dev, ocrdma_attributes[i]))
goto sysfs_err;
@@ -373,7 +379,7 @@ static void ocrdma_remove(struct ocrdma_dev *dev)
ocrdma_remove_free(dev);
}
-static int ocrdma_open(struct ocrdma_dev *dev)
+static int ocrdma_dispatch_port_active(struct ocrdma_dev *dev)
{
struct ib_event port_event;
@@ -384,32 +390,9 @@ static int ocrdma_open(struct ocrdma_dev *dev)
return 0;
}
-static int ocrdma_close(struct ocrdma_dev *dev)
+static int ocrdma_dispatch_port_error(struct ocrdma_dev *dev)
{
- int i;
- struct ocrdma_qp *qp, **cur_qp;
struct ib_event err_event;
- struct ib_qp_attr attrs;
- int attr_mask = IB_QP_STATE;
-
- attrs.qp_state = IB_QPS_ERR;
- mutex_lock(&dev->dev_lock);
- if (dev->qp_tbl) {
- cur_qp = dev->qp_tbl;
- for (i = 0; i < OCRDMA_MAX_QP; i++) {
- qp = cur_qp[i];
- if (qp && qp->ibqp.qp_type != IB_QPT_GSI) {
- /* change the QP state to ERROR */
- _ocrdma_modify_qp(&qp->ibqp, &attrs, attr_mask);
-
- err_event.event = IB_EVENT_QP_FATAL;
- err_event.element.qp = &qp->ibqp;
- err_event.device = &dev->ibdev;
- ib_dispatch_event(&err_event);
- }
- }
- }
- mutex_unlock(&dev->dev_lock);
err_event.event = IB_EVENT_PORT_ERR;
err_event.element.port_num = 1;
@@ -420,7 +403,7 @@ static int ocrdma_close(struct ocrdma_dev *dev)
static void ocrdma_shutdown(struct ocrdma_dev *dev)
{
- ocrdma_close(dev);
+ ocrdma_dispatch_port_error(dev);
ocrdma_remove(dev);
}
@@ -431,18 +414,28 @@ static void ocrdma_shutdown(struct ocrdma_dev *dev)
static void ocrdma_event_handler(struct ocrdma_dev *dev, u32 event)
{
switch (event) {
- case BE_DEV_UP:
- ocrdma_open(dev);
- break;
- case BE_DEV_DOWN:
- ocrdma_close(dev);
- break;
case BE_DEV_SHUTDOWN:
ocrdma_shutdown(dev);
break;
+ default:
+ break;
}
}
+void ocrdma_update_link_state(struct ocrdma_dev *dev, u8 lstate)
+{
+ if (!(dev->flags & OCRDMA_FLAGS_LINK_STATUS_INIT)) {
+ dev->flags |= OCRDMA_FLAGS_LINK_STATUS_INIT;
+ if (!lstate)
+ return;
+ }
+
+ if (!lstate)
+ ocrdma_dispatch_port_error(dev);
+ else
+ ocrdma_dispatch_port_active(dev);
+}
+
static struct ocrdma_driver ocrdma_drv = {
.name = "ocrdma_driver",
.add = ocrdma_add,
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_sli.h b/drivers/infiniband/hw/ocrdma/ocrdma_sli.h
index 6a38268bbe9f..99dd6fdf06d7 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_sli.h
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_sli.h
@@ -465,8 +465,11 @@ struct ocrdma_ae_qp_mcqe {
u32 valid_ae_event;
};
-#define OCRDMA_ASYNC_RDMA_EVE_CODE 0x14
-#define OCRDMA_ASYNC_GRP5_EVE_CODE 0x5
+enum ocrdma_async_event_code {
+ OCRDMA_ASYNC_LINK_EVE_CODE = 0x01,
+ OCRDMA_ASYNC_GRP5_EVE_CODE = 0x05,
+ OCRDMA_ASYNC_RDMA_EVE_CODE = 0x14
+};
enum ocrdma_async_grp5_events {
OCRDMA_ASYNC_EVENT_QOS_VALUE = 0x01,
@@ -489,6 +492,44 @@ enum OCRDMA_ASYNC_EVENT_TYPE {
OCRDMA_MAX_ASYNC_ERRORS
};
+struct ocrdma_ae_lnkst_mcqe {
+ u32 speed_state_ptn;
+ u32 qos_reason_falut;
+ u32 evt_tag;
+ u32 valid_ae_event;
+};
+
+enum {
+ OCRDMA_AE_LSC_PORT_NUM_MASK = 0x3F,
+ OCRDMA_AE_LSC_PT_SHIFT = 0x06,
+ OCRDMA_AE_LSC_PT_MASK = (0x03 <<
+ OCRDMA_AE_LSC_PT_SHIFT),
+ OCRDMA_AE_LSC_LS_SHIFT = 0x08,
+ OCRDMA_AE_LSC_LS_MASK = (0xFF <<
+ OCRDMA_AE_LSC_LS_SHIFT),
+ OCRDMA_AE_LSC_LD_SHIFT = 0x10,
+ OCRDMA_AE_LSC_LD_MASK = (0xFF <<
+ OCRDMA_AE_LSC_LD_SHIFT),
+ OCRDMA_AE_LSC_PPS_SHIFT = 0x18,
+ OCRDMA_AE_LSC_PPS_MASK = (0xFF <<
+ OCRDMA_AE_LSC_PPS_SHIFT),
+ OCRDMA_AE_LSC_PPF_MASK = 0xFF,
+ OCRDMA_AE_LSC_ER_SHIFT = 0x08,
+ OCRDMA_AE_LSC_ER_MASK = (0xFF <<
+ OCRDMA_AE_LSC_ER_SHIFT),
+ OCRDMA_AE_LSC_QOS_SHIFT = 0x10,
+ OCRDMA_AE_LSC_QOS_MASK = (0xFFFF <<
+ OCRDMA_AE_LSC_QOS_SHIFT)
+};
+
+enum {
+ OCRDMA_AE_LSC_PLINK_DOWN = 0x00,
+ OCRDMA_AE_LSC_PLINK_UP = 0x01,
+ OCRDMA_AE_LSC_LLINK_DOWN = 0x02,
+ OCRDMA_AE_LSC_LLINK_MASK = 0x02,
+ OCRDMA_AE_LSC_LLINK_UP = 0x03
+};
+
/* mailbox command request and responses */
enum {
OCRDMA_MBX_QUERY_CFG_CQ_OVERFLOW_SHIFT = 2,
@@ -676,7 +717,7 @@ enum {
OCRDMA_PHY_PFLT_SHIFT = 0x18,
OCRDMA_QOS_LNKSP_MASK = 0xFFFF0000,
OCRDMA_QOS_LNKSP_SHIFT = 0x10,
- OCRDMA_LLST_MASK = 0xFF,
+ OCRDMA_LINK_ST_MASK = 0x01,
OCRDMA_PLFC_MASK = 0x00000400,
OCRDMA_PLFC_SHIFT = 0x8,
OCRDMA_PLRFC_MASK = 0x00000200,
@@ -691,7 +732,7 @@ struct ocrdma_get_link_speed_rsp {
u32 pflt_pps_ld_pnum;
u32 qos_lsp;
- u32 res_lls;
+ u32 res_lnk_st;
};
enum {
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
index 583001bcfb8f..76e96f97b3f6 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
@@ -171,7 +171,7 @@ static inline void get_link_speed_and_width(struct ocrdma_dev *dev,
int status;
u8 speed;
- status = ocrdma_mbx_get_link_speed(dev, &speed);
+ status = ocrdma_mbx_get_link_speed(dev, &speed, NULL);
if (status)
speed = OCRDMA_PHYS_LINK_SPEED_ZERO;
diff --git a/drivers/infiniband/hw/qib/qib_qsfp.c b/drivers/infiniband/hw/qib/qib_qsfp.c
index 5e27f76805e2..4c7c3c84a741 100644
--- a/drivers/infiniband/hw/qib/qib_qsfp.c
+++ b/drivers/infiniband/hw/qib/qib_qsfp.c
@@ -292,7 +292,7 @@ int qib_refresh_qsfp_cache(struct qib_pportdata *ppd, struct qib_qsfp_cache *cp)
qib_dev_porterr(ppd->dd, ppd->port,
"QSFP byte0 is 0x%02X, S/B 0x0C/D\n", peek[0]);
- if ((peek[2] & 2) == 0) {
+ if ((peek[2] & 4) == 0) {
/*
* If cable is paged, rather than "flat memory", we need to
* set the page to zero, Even if it already appears to be zero.
@@ -538,7 +538,7 @@ int qib_qsfp_dump(struct qib_pportdata *ppd, char *buf, int len)
sofar += scnprintf(buf + sofar, len - sofar, "Date:%.*s\n",
QSFP_DATE_LEN, cd.date);
sofar += scnprintf(buf + sofar, len - sofar, "Lot:%.*s\n",
- QSFP_LOT_LEN, cd.date);
+ QSFP_LOT_LEN, cd.lot);
while (bidx < QSFP_DEFAULT_HDR_CNT) {
int iidx;
diff --git a/drivers/infiniband/hw/qib/qib_verbs.h b/drivers/infiniband/hw/qib/qib_verbs.h
index 2baf5ad251ed..bc803f33d5f6 100644
--- a/drivers/infiniband/hw/qib/qib_verbs.h
+++ b/drivers/infiniband/hw/qib/qib_verbs.h
@@ -329,9 +329,9 @@ struct qib_sge {
struct qib_mr {
struct ib_mr ibmr;
struct ib_umem *umem;
- struct qib_mregion mr; /* must be last */
u64 *pages;
u32 npages;
+ struct qib_mregion mr; /* must be last */
};
/*
diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c
index a93070210109..42f4da620f2e 100644
--- a/drivers/infiniband/ulp/iser/iser_verbs.c
+++ b/drivers/infiniband/ulp/iser/iser_verbs.c
@@ -1293,7 +1293,7 @@ u8 iser_check_task_pi_status(struct iscsi_iser_task *iser_task,
if (mr_status.fail_status & IB_MR_CHECK_SIG_STATUS) {
sector_t sector_off = mr_status.sig_err.sig_err_offset;
- do_div(sector_off, sector_size + 8);
+ sector_div(sector_off, sector_size + 8);
*sector = scsi_get_lba(iser_task->sc) + sector_off;
pr_err("PI error found type %d at sector %llx "
diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
index dfbbbb28090b..8a51c3b5d657 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.c
+++ b/drivers/infiniband/ulp/isert/ib_isert.c
@@ -157,16 +157,9 @@ isert_create_qp(struct isert_conn *isert_conn,
attr.recv_cq = comp->cq;
attr.cap.max_send_wr = ISERT_QP_MAX_REQ_DTOS;
attr.cap.max_recv_wr = ISERT_QP_MAX_RECV_DTOS + 1;
- /*
- * FIXME: Use devattr.max_sge - 2 for max_send_sge as
- * work-around for RDMA_READs with ConnectX-2.
- *
- * Also, still make sure to have at least two SGEs for
- * outgoing control PDU responses.
- */
- attr.cap.max_send_sge = max(2, device->dev_attr.max_sge - 2);
- isert_conn->max_sge = attr.cap.max_send_sge;
-
+ attr.cap.max_send_sge = device->dev_attr.max_sge;
+ isert_conn->max_sge = min(device->dev_attr.max_sge,
+ device->dev_attr.max_sge_rd);
attr.cap.max_recv_sge = 1;
attr.sq_sig_type = IB_SIGNAL_REQ_WR;
attr.qp_type = IB_QPT_RC;
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index 9909022dc6c3..3db9a659719b 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -488,7 +488,7 @@ static int srp_create_ch_ib(struct srp_rdma_ch *ch)
struct ib_qp *qp;
struct ib_fmr_pool *fmr_pool = NULL;
struct srp_fr_pool *fr_pool = NULL;
- const int m = 1 + dev->use_fast_reg;
+ const int m = dev->use_fast_reg ? 3 : 1;
struct ib_cq_init_attr cq_attr = {};
int ret;
@@ -994,16 +994,16 @@ static int srp_connect_ch(struct srp_rdma_ch *ch, bool multich)
ret = srp_lookup_path(ch);
if (ret)
- return ret;
+ goto out;
while (1) {
init_completion(&ch->done);
ret = srp_send_req(ch, multich);
if (ret)
- return ret;
+ goto out;
ret = wait_for_completion_interruptible(&ch->done);
if (ret < 0)
- return ret;
+ goto out;
/*
* The CM event handling code will set status to
@@ -1011,15 +1011,16 @@ static int srp_connect_ch(struct srp_rdma_ch *ch, bool multich)
* back, or SRP_DLID_REDIRECT if we get a lid/qp
* redirect REJ back.
*/
- switch (ch->status) {
+ ret = ch->status;
+ switch (ret) {
case 0:
ch->connected = true;
- return 0;
+ goto out;
case SRP_PORT_REDIRECT:
ret = srp_lookup_path(ch);
if (ret)
- return ret;
+ goto out;
break;
case SRP_DLID_REDIRECT:
@@ -1028,13 +1029,16 @@ static int srp_connect_ch(struct srp_rdma_ch *ch, bool multich)
case SRP_STALE_CONN:
shost_printk(KERN_ERR, target->scsi_host, PFX
"giving up on stale connection\n");
- ch->status = -ECONNRESET;
- return ch->status;
+ ret = -ECONNRESET;
+ goto out;
default:
- return ch->status;
+ goto out;
}
}
+
+out:
+ return ret <= 0 ? ret : -ENODEV;
}
static int srp_inv_rkey(struct srp_rdma_ch *ch, u32 rkey)
@@ -1309,7 +1313,7 @@ reset_state:
}
static int srp_map_finish_fr(struct srp_map_state *state,
- struct srp_rdma_ch *ch)
+ struct srp_rdma_ch *ch, int sg_nents)
{
struct srp_target_port *target = ch->target;
struct srp_device *dev = target->srp_host->srp_dev;
@@ -1324,10 +1328,10 @@ static int srp_map_finish_fr(struct srp_map_state *state,
WARN_ON_ONCE(!dev->use_fast_reg);
- if (state->sg_nents == 0)
+ if (sg_nents == 0)
return 0;
- if (state->sg_nents == 1 && target->global_mr) {
+ if (sg_nents == 1 && target->global_mr) {
srp_map_desc(state, sg_dma_address(state->sg),
sg_dma_len(state->sg),
target->global_mr->rkey);
@@ -1341,8 +1345,7 @@ static int srp_map_finish_fr(struct srp_map_state *state,
rkey = ib_inc_rkey(desc->mr->rkey);
ib_update_fast_reg_key(desc->mr, rkey);
- n = ib_map_mr_sg(desc->mr, state->sg, state->sg_nents,
- dev->mr_page_size);
+ n = ib_map_mr_sg(desc->mr, state->sg, sg_nents, dev->mr_page_size);
if (unlikely(n < 0))
return n;
@@ -1448,16 +1451,15 @@ static int srp_map_sg_fr(struct srp_map_state *state, struct srp_rdma_ch *ch,
state->fr.next = req->fr_list;
state->fr.end = req->fr_list + ch->target->cmd_sg_cnt;
state->sg = scat;
- state->sg_nents = scsi_sg_count(req->scmnd);
- while (state->sg_nents) {
+ while (count) {
int i, n;
- n = srp_map_finish_fr(state, ch);
+ n = srp_map_finish_fr(state, ch, count);
if (unlikely(n < 0))
return n;
- state->sg_nents -= n;
+ count -= n;
for (i = 0; i < n; i++)
state->sg = sg_next(state->sg);
}
@@ -1517,10 +1519,12 @@ static int srp_map_idb(struct srp_rdma_ch *ch, struct srp_request *req,
if (dev->use_fast_reg) {
state.sg = idb_sg;
- state.sg_nents = 1;
sg_set_buf(idb_sg, req->indirect_desc, idb_len);
idb_sg->dma_address = req->indirect_dma_addr; /* hack! */
- ret = srp_map_finish_fr(&state, ch);
+#ifdef CONFIG_NEED_SG_DMA_LENGTH
+ idb_sg->dma_length = idb_sg->length; /* hack^2 */
+#endif
+ ret = srp_map_finish_fr(&state, ch, 1);
if (ret < 0)
return ret;
} else if (dev->use_fmr) {
@@ -1655,7 +1659,7 @@ static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_rdma_ch *ch,
return ret;
req->nmdesc++;
} else {
- idb_rkey = target->global_mr->rkey;
+ idb_rkey = cpu_to_be32(target->global_mr->rkey);
}
indirect_hdr->table_desc.va = cpu_to_be64(req->indirect_dma_addr);
diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h
index 87a2a919dc43..f6af531f9f32 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.h
+++ b/drivers/infiniband/ulp/srp/ib_srp.h
@@ -300,10 +300,7 @@ struct srp_map_state {
dma_addr_t base_dma_addr;
u32 dma_len;
u32 total_len;
- union {
- unsigned int npages;
- int sg_nents;
- };
+ unsigned int npages;
unsigned int nmdesc;
unsigned int ndesc;
};
diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c
index 932d07307454..da326090c2b0 100644
--- a/drivers/input/joystick/db9.c
+++ b/drivers/input/joystick/db9.c
@@ -592,6 +592,7 @@ static void db9_attach(struct parport *pp)
return;
}
+ memset(&db9_parport_cb, 0, sizeof(db9_parport_cb));
db9_parport_cb.flags = PARPORT_FLAG_EXCL;
pd = parport_register_dev_model(pp, "db9", &db9_parport_cb, port_idx);
diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c
index 5a672dcac0d8..eae14d512353 100644
--- a/drivers/input/joystick/gamecon.c
+++ b/drivers/input/joystick/gamecon.c
@@ -951,6 +951,7 @@ static void gc_attach(struct parport *pp)
pads = gc_cfg[port_idx].args + 1;
n_pads = gc_cfg[port_idx].nargs - 1;
+ memset(&gc_parport_cb, 0, sizeof(gc_parport_cb));
gc_parport_cb.flags = PARPORT_FLAG_EXCL;
pd = parport_register_dev_model(pp, "gamecon", &gc_parport_cb,
diff --git a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c
index 9f5bca26bd2f..77f575dd0901 100644
--- a/drivers/input/joystick/turbografx.c
+++ b/drivers/input/joystick/turbografx.c
@@ -181,6 +181,7 @@ static void tgfx_attach(struct parport *pp)
n_buttons = tgfx_cfg[port_idx].args + 1;
n_devs = tgfx_cfg[port_idx].nargs - 1;
+ memset(&tgfx_parport_cb, 0, sizeof(tgfx_parport_cb));
tgfx_parport_cb.flags = PARPORT_FLAG_EXCL;
pd = parport_register_dev_model(pp, "turbografx", &tgfx_parport_cb,
diff --git a/drivers/input/joystick/walkera0701.c b/drivers/input/joystick/walkera0701.c
index 9c07fe911075..70a893a17467 100644
--- a/drivers/input/joystick/walkera0701.c
+++ b/drivers/input/joystick/walkera0701.c
@@ -218,6 +218,7 @@ static void walkera0701_attach(struct parport *pp)
w->parport = pp;
+ memset(&walkera0701_parport_cb, 0, sizeof(walkera0701_parport_cb));
walkera0701_parport_cb.flags = PARPORT_FLAG_EXCL;
walkera0701_parport_cb.irq_func = walkera0701_irq_handler;
walkera0701_parport_cb.private = w;
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index bef317ff7352..b9f01bd1b7ef 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -96,7 +96,7 @@ struct gpio_keys_drvdata {
* Return value of this function can be used to allocate bitmap
* large enough to hold all bits for given type.
*/
-static inline int get_n_events_by_type(int type)
+static int get_n_events_by_type(int type)
{
BUG_ON(type != EV_SW && type != EV_KEY);
@@ -104,6 +104,22 @@ static inline int get_n_events_by_type(int type)
}
/**
+ * get_bm_events_by_type() - returns bitmap of supported events per @type
+ * @input: input device from which bitmap is retrieved
+ * @type: type of button (%EV_KEY, %EV_SW)
+ *
+ * Return value of this function can be used to allocate bitmap
+ * large enough to hold all bits for given type.
+ */
+static const unsigned long *get_bm_events_by_type(struct input_dev *dev,
+ int type)
+{
+ BUG_ON(type != EV_SW && type != EV_KEY);
+
+ return (type == EV_KEY) ? dev->keybit : dev->swbit;
+}
+
+/**
* gpio_keys_disable_button() - disables given GPIO button
* @bdata: button data for button to be disabled
*
@@ -213,6 +229,7 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
const char *buf, unsigned int type)
{
int n_events = get_n_events_by_type(type);
+ const unsigned long *bitmap = get_bm_events_by_type(ddata->input, type);
unsigned long *bits;
ssize_t error;
int i;
@@ -226,6 +243,11 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
goto out;
/* First validate */
+ if (!bitmap_subset(bits, bitmap, n_events)) {
+ error = -EINVAL;
+ goto out;
+ }
+
for (i = 0; i < ddata->pdata->nbuttons; i++) {
struct gpio_button_data *bdata = &ddata->data[i];
@@ -239,11 +261,6 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
}
}
- if (i == ddata->pdata->nbuttons) {
- error = -EINVAL;
- goto out;
- }
-
mutex_lock(&ddata->disable_lock);
for (i = 0; i < ddata->pdata->nbuttons; i++) {
diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c
index 7502e46165fa..e0d72c8c01e4 100644
--- a/drivers/input/keyboard/omap-keypad.c
+++ b/drivers/input/keyboard/omap-keypad.c
@@ -155,14 +155,6 @@ static void omap_kp_tasklet(unsigned long data)
"pressed" : "released");
#else
key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)];
- if (key < 0) {
- printk(KERN_WARNING
- "omap-keypad: Spurious key event %d-%d\n",
- col, row);
- /* We scan again after a couple of seconds */
- spurious = 1;
- continue;
- }
if (!(kp_cur_group == (key & GROUP_MASK) ||
kp_cur_group == -1))
@@ -292,8 +284,8 @@ static int omap_kp_probe(struct platform_device *pdev)
setup_timer(&omap_kp->timer, omap_kp_timer, (unsigned long)omap_kp);
/* get the irq and init timer*/
- tasklet_enable(&kp_tasklet);
kp_tasklet.data = (unsigned long) omap_kp;
+ tasklet_enable(&kp_tasklet);
ret = device_create_file(&pdev->dev, &dev_attr_enable);
if (ret < 0)
diff --git a/drivers/input/misc/arizona-haptics.c b/drivers/input/misc/arizona-haptics.c
index 4bf678541496..d5994a745ffa 100644
--- a/drivers/input/misc/arizona-haptics.c
+++ b/drivers/input/misc/arizona-haptics.c
@@ -97,8 +97,7 @@ static void arizona_haptics_work(struct work_struct *work)
ret = regmap_update_bits(arizona->regmap,
ARIZONA_HAPTICS_CONTROL_1,
- ARIZONA_HAP_CTRL_MASK,
- 1 << ARIZONA_HAP_CTRL_SHIFT);
+ ARIZONA_HAP_CTRL_MASK, 0);
if (ret != 0) {
dev_err(arizona->dev, "Failed to stop haptics: %d\n",
ret);
diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c
index 1d0e61d7c131..b0d445390ee4 100644
--- a/drivers/input/misc/bma150.c
+++ b/drivers/input/misc/bma150.c
@@ -147,7 +147,7 @@ struct bma150_data {
* are stated and verified by Bosch Sensortec where they are configured
* to provide a generic sensitivity performance.
*/
-static struct bma150_cfg default_cfg = {
+static const struct bma150_cfg default_cfg = {
.any_motion_int = 1,
.hg_int = 1,
.lg_int = 1,
diff --git a/drivers/input/misc/da9063_onkey.c b/drivers/input/misc/da9063_onkey.c
index 8eb697db82d0..bb863e062b03 100644
--- a/drivers/input/misc/da9063_onkey.c
+++ b/drivers/input/misc/da9063_onkey.c
@@ -179,13 +179,13 @@ static irqreturn_t da9063_onkey_irq_handler(int irq, void *data)
input_report_key(onkey->input, KEY_POWER, 1);
input_sync(onkey->input);
schedule_delayed_work(&onkey->work, 0);
- dev_dbg(onkey->dev, "KEY_POWER pressed.\n");
+ dev_dbg(onkey->dev, "KEY_POWER long press.\n");
} else {
- input_report_key(onkey->input, KEY_SLEEP, 1);
+ input_report_key(onkey->input, KEY_POWER, 1);
input_sync(onkey->input);
- input_report_key(onkey->input, KEY_SLEEP, 0);
+ input_report_key(onkey->input, KEY_POWER, 0);
input_sync(onkey->input);
- dev_dbg(onkey->dev, "KEY_SLEEP pressed.\n");
+ dev_dbg(onkey->dev, "KEY_POWER short press.\n");
}
return IRQ_HANDLED;
diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c
index 6f997aa49183..4a5afc7fe96e 100644
--- a/drivers/input/misc/sparcspkr.c
+++ b/drivers/input/misc/sparcspkr.c
@@ -345,23 +345,19 @@ static struct platform_driver grover_beep_driver = {
.shutdown = sparcspkr_shutdown,
};
+static struct platform_driver * const drivers[] = {
+ &bbc_beep_driver,
+ &grover_beep_driver,
+};
+
static int __init sparcspkr_init(void)
{
- int err = platform_driver_register(&bbc_beep_driver);
-
- if (!err) {
- err = platform_driver_register(&grover_beep_driver);
- if (err)
- platform_driver_unregister(&bbc_beep_driver);
- }
-
- return err;
+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
}
static void __exit sparcspkr_exit(void)
{
- platform_driver_unregister(&bbc_beep_driver);
- platform_driver_unregister(&grover_beep_driver);
+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
}
module_init(sparcspkr_init);
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 5adbcedcb81c..4eb9e4d94f46 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -256,13 +256,29 @@ static void uinput_destroy_device(struct uinput_device *udev)
static int uinput_create_device(struct uinput_device *udev)
{
struct input_dev *dev = udev->dev;
- int error;
+ int error, nslot;
if (udev->state != UIST_SETUP_COMPLETE) {
printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
return -EINVAL;
}
+ if (test_bit(ABS_MT_SLOT, dev->absbit)) {
+ nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
+ error = input_mt_init_slots(dev, nslot, 0);
+ if (error)
+ goto fail1;
+ } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
+ input_set_events_per_packet(dev, 60);
+ }
+
+ if (test_bit(EV_FF, dev->evbit) && !udev->ff_effects_max) {
+ printk(KERN_DEBUG "%s: ff_effects_max should be non-zero when FF_BIT is set\n",
+ UINPUT_NAME);
+ error = -EINVAL;
+ goto fail1;
+ }
+
if (udev->ff_effects_max) {
error = input_ff_create(dev, udev->ff_effects_max);
if (error)
@@ -308,10 +324,35 @@ static int uinput_open(struct inode *inode, struct file *file)
return 0;
}
+static int uinput_validate_absinfo(struct input_dev *dev, unsigned int code,
+ const struct input_absinfo *abs)
+{
+ int min, max;
+
+ min = abs->minimum;
+ max = abs->maximum;
+
+ if ((min != 0 || max != 0) && max <= min) {
+ printk(KERN_DEBUG
+ "%s: invalid abs[%02x] min:%d max:%d\n",
+ UINPUT_NAME, code, min, max);
+ return -EINVAL;
+ }
+
+ if (abs->flat > max - min) {
+ printk(KERN_DEBUG
+ "%s: abs_flat #%02x out of range: %d (min:%d/max:%d)\n",
+ UINPUT_NAME, code, abs->flat, min, max);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int uinput_validate_absbits(struct input_dev *dev)
{
unsigned int cnt;
- int nslot;
+ int error;
if (!test_bit(EV_ABS, dev->evbit))
return 0;
@@ -321,38 +362,12 @@ static int uinput_validate_absbits(struct input_dev *dev)
*/
for_each_set_bit(cnt, dev->absbit, ABS_CNT) {
- int min, max;
-
- min = input_abs_get_min(dev, cnt);
- max = input_abs_get_max(dev, cnt);
-
- if ((min != 0 || max != 0) && max <= min) {
- printk(KERN_DEBUG
- "%s: invalid abs[%02x] min:%d max:%d\n",
- UINPUT_NAME, cnt,
- input_abs_get_min(dev, cnt),
- input_abs_get_max(dev, cnt));
+ if (!dev->absinfo)
return -EINVAL;
- }
- if (input_abs_get_flat(dev, cnt) >
- input_abs_get_max(dev, cnt) - input_abs_get_min(dev, cnt)) {
- printk(KERN_DEBUG
- "%s: abs_flat #%02x out of range: %d "
- "(min:%d/max:%d)\n",
- UINPUT_NAME, cnt,
- input_abs_get_flat(dev, cnt),
- input_abs_get_min(dev, cnt),
- input_abs_get_max(dev, cnt));
- return -EINVAL;
- }
- }
-
- if (test_bit(ABS_MT_SLOT, dev->absbit)) {
- nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
- input_mt_init_slots(dev, nslot, 0);
- } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
- input_set_events_per_packet(dev, 60);
+ error = uinput_validate_absinfo(dev, cnt, &dev->absinfo[cnt]);
+ if (error)
+ return error;
}
return 0;
@@ -370,8 +385,71 @@ static int uinput_allocate_device(struct uinput_device *udev)
return 0;
}
-static int uinput_setup_device(struct uinput_device *udev,
- const char __user *buffer, size_t count)
+static int uinput_dev_setup(struct uinput_device *udev,
+ struct uinput_setup __user *arg)
+{
+ struct uinput_setup setup;
+ struct input_dev *dev;
+
+ if (udev->state == UIST_CREATED)
+ return -EINVAL;
+
+ if (copy_from_user(&setup, arg, sizeof(setup)))
+ return -EFAULT;
+
+ if (!setup.name[0])
+ return -EINVAL;
+
+ dev = udev->dev;
+ dev->id = setup.id;
+ udev->ff_effects_max = setup.ff_effects_max;
+
+ kfree(dev->name);
+ dev->name = kstrndup(setup.name, UINPUT_MAX_NAME_SIZE, GFP_KERNEL);
+ if (!dev->name)
+ return -ENOMEM;
+
+ udev->state = UIST_SETUP_COMPLETE;
+ return 0;
+}
+
+static int uinput_abs_setup(struct uinput_device *udev,
+ struct uinput_setup __user *arg, size_t size)
+{
+ struct uinput_abs_setup setup = {};
+ struct input_dev *dev;
+ int error;
+
+ if (size > sizeof(setup))
+ return -E2BIG;
+
+ if (udev->state == UIST_CREATED)
+ return -EINVAL;
+
+ if (copy_from_user(&setup, arg, size))
+ return -EFAULT;
+
+ if (setup.code > ABS_MAX)
+ return -ERANGE;
+
+ dev = udev->dev;
+
+ error = uinput_validate_absinfo(dev, setup.code, &setup.absinfo);
+ if (error)
+ return error;
+
+ input_alloc_absinfo(dev);
+ if (!dev->absinfo)
+ return -ENOMEM;
+
+ set_bit(setup.code, dev->absbit);
+ dev->absinfo[setup.code] = setup.absinfo;
+ return 0;
+}
+
+/* legacy setup via write() */
+static int uinput_setup_device_legacy(struct uinput_device *udev,
+ const char __user *buffer, size_t count)
{
struct uinput_user_dev *user_dev;
struct input_dev *dev;
@@ -474,7 +552,7 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer,
retval = udev->state == UIST_CREATED ?
uinput_inject_events(udev, buffer, count) :
- uinput_setup_device(udev, buffer, count);
+ uinput_setup_device_legacy(udev, buffer, count);
mutex_unlock(&udev->mutex);
@@ -735,6 +813,12 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
uinput_destroy_device(udev);
goto out;
+ case UI_DEV_SETUP:
+ retval = uinput_dev_setup(udev, p);
+ goto out;
+
+ /* UI_ABS_SETUP is handled in the variable size ioctls */
+
case UI_SET_EVBIT:
retval = uinput_set_bit(arg, evbit, EV_MAX);
goto out;
@@ -879,6 +963,10 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
name = dev_name(&udev->dev->dev);
retval = uinput_str_to_user(p, name, size);
goto out;
+
+ case UI_ABS_SETUP & ~IOCSIZE_MASK:
+ retval = uinput_abs_setup(udev, p, size);
+ goto out;
}
retval = -EINVAL;
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index 41e6cb501e6a..936f07a4e35f 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -31,6 +31,7 @@
#define ALPS_CMD_NIBBLE_10 0x01f2
#define ALPS_REG_BASE_RUSHMORE 0xc2c0
+#define ALPS_REG_BASE_V7 0xc2c0
#define ALPS_REG_BASE_PINNACLE 0x0000
static const struct alps_nibble_commands alps_v3_nibble_commands[] = {
@@ -2047,7 +2048,7 @@ static int alps_absolute_mode_v3(struct psmouse *psmouse)
return 0;
}
-static int alps_probe_trackstick_v3(struct psmouse *psmouse, int reg_base)
+static int alps_probe_trackstick_v3_v7(struct psmouse *psmouse, int reg_base)
{
int ret = -EIO, reg_val;
@@ -2128,15 +2129,12 @@ error:
static int alps_hw_init_v3(struct psmouse *psmouse)
{
+ struct alps_data *priv = psmouse->private;
struct ps2dev *ps2dev = &psmouse->ps2dev;
int reg_val;
unsigned char param[4];
- reg_val = alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE);
- if (reg_val == -EIO)
- goto error;
-
- if (reg_val == 0 &&
+ if ((priv->flags & ALPS_DUALPOINT) &&
alps_setup_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE) == -EIO)
goto error;
@@ -2613,6 +2611,11 @@ static int alps_set_protocol(struct psmouse *psmouse,
priv->decode_fields = alps_decode_pinnacle;
priv->nibble_commands = alps_v3_nibble_commands;
priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
+
+ if (alps_probe_trackstick_v3_v7(psmouse,
+ ALPS_REG_BASE_PINNACLE) < 0)
+ priv->flags &= ~ALPS_DUALPOINT;
+
break;
case ALPS_PROTO_V3_RUSHMORE:
@@ -2625,8 +2628,8 @@ static int alps_set_protocol(struct psmouse *psmouse,
priv->x_bits = 16;
priv->y_bits = 12;
- if (alps_probe_trackstick_v3(psmouse,
- ALPS_REG_BASE_RUSHMORE) < 0)
+ if (alps_probe_trackstick_v3_v7(psmouse,
+ ALPS_REG_BASE_RUSHMORE) < 0)
priv->flags &= ~ALPS_DUALPOINT;
break;
@@ -2676,6 +2679,9 @@ static int alps_set_protocol(struct psmouse *psmouse,
if (priv->fw_ver[1] != 0xba)
priv->flags |= ALPS_BUTTONPAD;
+ if (alps_probe_trackstick_v3_v7(psmouse, ALPS_REG_BASE_V7) < 0)
+ priv->flags &= ~ALPS_DUALPOINT;
+
break;
case ALPS_PROTO_V8:
diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
index 5e1665bbaa0b..2f589857a039 100644
--- a/drivers/input/mouse/elan_i2c_core.c
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -41,6 +41,7 @@
#define DRIVER_NAME "elan_i2c"
#define ELAN_DRIVER_VERSION "1.6.1"
+#define ELAN_VENDOR_ID 0x04f3
#define ETP_MAX_PRESSURE 255
#define ETP_FWIDTH_REDUCE 90
#define ETP_FINGER_WIDTH 15
@@ -914,6 +915,8 @@ static int elan_setup_input_device(struct elan_tp_data *data)
input->name = "Elan Touchpad";
input->id.bustype = BUS_I2C;
+ input->id.vendor = ELAN_VENDOR_ID;
+ input->id.product = data->product_id;
input_set_drvdata(input, data);
error = input_mt_init_slots(input, ETP_MAX_FINGERS,
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 537ebb0e193a..78f93cf68840 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -1222,7 +1222,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
ETP_WMAX_V2, 0, 0);
}
- input_mt_init_slots(dev, 2, 0);
+ input_mt_init_slots(dev, 2, INPUT_MT_SEMI_MT);
input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
break;
diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c
index 4d5576de81be..c8c6a8cc329d 100644
--- a/drivers/input/mouse/focaltech.c
+++ b/drivers/input/mouse/focaltech.c
@@ -49,12 +49,6 @@ int focaltech_detect(struct psmouse *psmouse, bool set_properties)
return 0;
}
-static void focaltech_reset(struct psmouse *psmouse)
-{
- ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
- psmouse_reset(psmouse);
-}
-
#ifdef CONFIG_MOUSE_PS2_FOCALTECH
/*
@@ -300,6 +294,12 @@ static int focaltech_switch_protocol(struct psmouse *psmouse)
return 0;
}
+static void focaltech_reset(struct psmouse *psmouse)
+{
+ ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+ psmouse_reset(psmouse);
+}
+
static void focaltech_disconnect(struct psmouse *psmouse)
{
focaltech_reset(psmouse);
@@ -456,14 +456,4 @@ fail:
kfree(priv);
return error;
}
-
-#else /* CONFIG_MOUSE_PS2_FOCALTECH */
-
-int focaltech_init(struct psmouse *psmouse)
-{
- focaltech_reset(psmouse);
-
- return 0;
-}
-
#endif /* CONFIG_MOUSE_PS2_FOCALTECH */
diff --git a/drivers/input/mouse/focaltech.h b/drivers/input/mouse/focaltech.h
index ca61ebff373e..783b28e8e10b 100644
--- a/drivers/input/mouse/focaltech.h
+++ b/drivers/input/mouse/focaltech.h
@@ -18,6 +18,14 @@
#define _FOCALTECH_H
int focaltech_detect(struct psmouse *psmouse, bool set_properties);
+
+#ifdef CONFIG_MOUSE_PS2_FOCALTECH
int focaltech_init(struct psmouse *psmouse);
+#else
+static inline int focaltech_init(struct psmouse *psmouse)
+{
+ return -ENOSYS;
+}
+#endif
#endif
diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c
index 136e222e2a16..422da1cd9e76 100644
--- a/drivers/input/mouse/logips2pp.c
+++ b/drivers/input/mouse/logips2pp.c
@@ -325,7 +325,7 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse,
* that support it.
*/
-int ps2pp_init(struct psmouse *psmouse, bool set_properties)
+int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4];
diff --git a/drivers/input/mouse/logips2pp.h b/drivers/input/mouse/logips2pp.h
index 0c186f0282d9..bf629453e095 100644
--- a/drivers/input/mouse/logips2pp.h
+++ b/drivers/input/mouse/logips2pp.h
@@ -12,9 +12,9 @@
#define _LOGIPS2PP_H
#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP
-int ps2pp_init(struct psmouse *psmouse, bool set_properties);
+int ps2pp_detect(struct psmouse *psmouse, bool set_properties);
#else
-inline int ps2pp_init(struct psmouse *psmouse, bool set_properties)
+static inline int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
{
return -ENOSYS;
}
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index ad18dab0ac47..b9e4ee34c132 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -119,6 +119,7 @@ struct psmouse_protocol {
enum psmouse_type type;
bool maxproto;
bool ignore_parity; /* Protocol should ignore parity errors from KBC */
+ bool try_passthru; /* Try protocol also on passthrough ports */
const char *name;
const char *alias;
int (*detect)(struct psmouse *, bool);
@@ -129,7 +130,6 @@ struct psmouse_protocol {
* psmouse_process_byte() analyzes the PS/2 data stream and reports
* relevant events to the input module once full packet has arrived.
*/
-
psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
{
struct input_dev *dev = psmouse->dev;
@@ -138,22 +138,16 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
if (psmouse->pktcnt < psmouse->pktsize)
return PSMOUSE_GOOD_DATA;
-/*
- * Full packet accumulated, process it
- */
+ /* Full packet accumulated, process it */
-/*
- * Scroll wheel on IntelliMice, scroll buttons on NetMice
- */
-
- if (psmouse->type == PSMOUSE_IMPS || psmouse->type == PSMOUSE_GENPS)
+ switch (psmouse->type) {
+ case PSMOUSE_IMPS:
+ /* IntelliMouse has scroll wheel */
input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]);
+ break;
-/*
- * Scroll wheel and buttons on IntelliMouse Explorer
- */
-
- if (psmouse->type == PSMOUSE_IMEX) {
+ case PSMOUSE_IMEX:
+ /* Scroll wheel and buttons on IntelliMouse Explorer */
switch (packet[3] & 0xC0) {
case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */
input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31));
@@ -168,39 +162,42 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1);
break;
}
- }
+ break;
-/*
- * Extra buttons on Genius NewNet 3D
- */
+ case PSMOUSE_GENPS:
+ /* Report scroll buttons on NetMice */
+ input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]);
- if (psmouse->type == PSMOUSE_GENPS) {
+ /* Extra buttons on Genius NewNet 3D */
input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1);
input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1);
- }
+ break;
-/*
- * Extra button on ThinkingMouse
- */
- if (psmouse->type == PSMOUSE_THINKPS) {
+ case PSMOUSE_THINKPS:
+ /* Extra button on ThinkingMouse */
input_report_key(dev, BTN_EXTRA, (packet[0] >> 3) & 1);
- /* Without this bit of weirdness moving up gives wildly high Y changes. */
+
+ /*
+ * Without this bit of weirdness moving up gives wildly
+ * high Y changes.
+ */
packet[1] |= (packet[0] & 0x40) << 1;
- }
+ break;
-/*
- * Cortron PS2 Trackball reports SIDE button on the 4th bit of the first
- * byte.
- */
- if (psmouse->type == PSMOUSE_CORTRON) {
+ case PSMOUSE_CORTRON:
+ /*
+ * Cortron PS2 Trackball reports SIDE button in the
+ * 4th bit of the first byte.
+ */
input_report_key(dev, BTN_SIDE, (packet[0] >> 3) & 1);
packet[0] |= 0x08;
- }
+ break;
-/*
- * Generic PS/2 Mouse
- */
+ default:
+ break;
+ }
+ /* Generic PS/2 Mouse */
input_report_key(dev, BTN_LEFT, packet[0] & 1);
input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1);
@@ -222,7 +219,6 @@ void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work,
/*
* __psmouse_set_state() sets new psmouse state and resets all flags.
*/
-
static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
{
psmouse->state = new_state;
@@ -231,13 +227,11 @@ static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_sta
psmouse->last = jiffies;
}
-
/*
* psmouse_set_state() sets new psmouse state and resets all flags and
* counters while holding serio lock so fighting with interrupt handler
* is not a concern.
*/
-
void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
{
serio_pause_rx(psmouse->ps2dev.serio);
@@ -249,7 +243,6 @@ void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
* psmouse_handle_byte() processes one byte of the input data stream
* by calling corresponding protocol handler.
*/
-
static int psmouse_handle_byte(struct psmouse *psmouse)
{
psmouse_ret_t rc = psmouse->protocol_handler(psmouse);
@@ -292,7 +285,6 @@ static int psmouse_handle_byte(struct psmouse *psmouse)
* psmouse_interrupt() handles incoming characters, either passing them
* for normal processing or gathering them as command response.
*/
-
static irqreturn_t psmouse_interrupt(struct serio *serio,
unsigned char data, unsigned int flags)
{
@@ -335,9 +327,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
}
psmouse->packet[psmouse->pktcnt++] = data;
-/*
- * Check if this is a new device announcement (0xAA 0x00)
- */
+
+ /* Check if this is a new device announcement (0xAA 0x00) */
if (unlikely(psmouse->packet[0] == PSMOUSE_RET_BAT && psmouse->pktcnt <= 2)) {
if (psmouse->pktcnt == 1) {
psmouse->last = jiffies;
@@ -351,9 +342,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
serio_reconnect(serio);
goto out;
}
-/*
- * Not a new device, try processing first byte normally
- */
+
+ /* Not a new device, try processing first byte normally */
psmouse->pktcnt = 1;
if (psmouse_handle_byte(psmouse))
goto out;
@@ -361,9 +351,10 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
psmouse->packet[psmouse->pktcnt++] = data;
}
-/*
- * See if we need to force resync because mouse was idle for too long
- */
+ /*
+ * See if we need to force resync because mouse was idle for
+ * too long.
+ */
if (psmouse->state == PSMOUSE_ACTIVATED &&
psmouse->pktcnt == 1 && psmouse->resync_time &&
time_after(jiffies, psmouse->last + psmouse->resync_time * HZ)) {
@@ -380,7 +371,6 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
return IRQ_HANDLED;
}
-
/*
* psmouse_sliced_command() sends an extended PS/2 command to the mouse
* using sliced syntax, understood by advanced devices, such as Logitech
@@ -404,7 +394,6 @@ int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command)
return 0;
}
-
/*
* psmouse_reset() resets the mouse into power-on state.
*/
@@ -424,7 +413,6 @@ int psmouse_reset(struct psmouse *psmouse)
/*
* Here we set the mouse resolution.
*/
-
void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
{
static const unsigned char params[] = { 0, 1, 2, 2, 3 };
@@ -441,7 +429,6 @@ void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
/*
* Here we set the mouse report rate.
*/
-
static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate)
{
static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 };
@@ -457,7 +444,6 @@ static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate)
/*
* Here we set the mouse scaling.
*/
-
static void psmouse_set_scale(struct psmouse *psmouse, enum psmouse_scale scale)
{
ps2_command(&psmouse->ps2dev, NULL,
@@ -468,7 +454,6 @@ static void psmouse_set_scale(struct psmouse *psmouse, enum psmouse_scale scale)
/*
* psmouse_poll() - default poll handler. Everyone except for ALPS uses it.
*/
-
static int psmouse_poll(struct psmouse *psmouse)
{
return ps2_command(&psmouse->ps2dev, psmouse->packet,
@@ -602,7 +587,7 @@ static int im_explorer_detect(struct psmouse *psmouse, bool set_properties)
if (param[0] != 4)
return -1;
-/* Magic to enable horizontal scrolling on IntelliMouse 4.0 */
+ /* Magic to enable horizontal scrolling on IntelliMouse 4.0 */
param[0] = 200;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
param[0] = 80;
@@ -672,10 +657,10 @@ static int ps2bare_detect(struct psmouse *psmouse, bool set_properties)
if (!psmouse->name)
psmouse->name = "Mouse";
-/*
- * We have no way of figuring true number of buttons so let's
- * assume that the device has 3.
- */
+ /*
+ * We have no way of figuring true number of buttons so let's
+ * assume that the device has 3.
+ */
__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
}
@@ -699,284 +684,6 @@ static int cortron_detect(struct psmouse *psmouse, bool set_properties)
return 0;
}
-/*
- * Apply default settings to the psmouse structure. Most of them will
- * be overridden by individual protocol initialization routines.
- */
-
-static void psmouse_apply_defaults(struct psmouse *psmouse)
-{
- struct input_dev *input_dev = psmouse->dev;
-
- memset(input_dev->evbit, 0, sizeof(input_dev->evbit));
- memset(input_dev->keybit, 0, sizeof(input_dev->keybit));
- memset(input_dev->relbit, 0, sizeof(input_dev->relbit));
- memset(input_dev->absbit, 0, sizeof(input_dev->absbit));
- memset(input_dev->mscbit, 0, sizeof(input_dev->mscbit));
-
- __set_bit(EV_KEY, input_dev->evbit);
- __set_bit(EV_REL, input_dev->evbit);
-
- __set_bit(BTN_LEFT, input_dev->keybit);
- __set_bit(BTN_RIGHT, input_dev->keybit);
-
- __set_bit(REL_X, input_dev->relbit);
- __set_bit(REL_Y, input_dev->relbit);
-
- __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
-
- psmouse->set_rate = psmouse_set_rate;
- psmouse->set_resolution = psmouse_set_resolution;
- psmouse->set_scale = psmouse_set_scale;
- psmouse->poll = psmouse_poll;
- psmouse->protocol_handler = psmouse_process_byte;
- psmouse->pktsize = 3;
- psmouse->reconnect = NULL;
- psmouse->disconnect = NULL;
- psmouse->cleanup = NULL;
- psmouse->pt_activate = NULL;
- psmouse->pt_deactivate = NULL;
-}
-
-/*
- * Apply default settings to the psmouse structure and call specified
- * protocol detection or initialization routine.
- */
-static int psmouse_do_detect(int (*detect)(struct psmouse *psmouse,
- bool set_properties),
- struct psmouse *psmouse, bool set_properties)
-{
- if (set_properties)
- psmouse_apply_defaults(psmouse);
-
- return detect(psmouse, set_properties);
-}
-
-/*
- * psmouse_extensions() probes for any extensions to the basic PS/2 protocol
- * the mouse may have.
- */
-
-static int psmouse_extensions(struct psmouse *psmouse,
- unsigned int max_proto, bool set_properties)
-{
- bool synaptics_hardware = false;
-
-/* Always check for focaltech, this is safe as it uses pnp-id matching */
- if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) {
- if (max_proto > PSMOUSE_IMEX) {
- if (!set_properties || focaltech_init(psmouse) == 0) {
- if (IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH))
- return PSMOUSE_FOCALTECH;
- /*
- * Note that we need to also restrict
- * psmouse_max_proto so that psmouse_initialize()
- * does not try to reset rate and resolution,
- * because even that upsets the device.
- */
- psmouse_max_proto = PSMOUSE_PS2;
- return PSMOUSE_PS2;
- }
- }
- }
-
-/*
- * We always check for lifebook because it does not disturb mouse
- * (it only checks DMI information).
- */
- if (psmouse_do_detect(lifebook_detect, psmouse, set_properties) == 0) {
- if (max_proto > PSMOUSE_IMEX) {
- if (!set_properties || lifebook_init(psmouse) == 0)
- return PSMOUSE_LIFEBOOK;
- }
- }
-
- if (psmouse_do_detect(vmmouse_detect, psmouse, set_properties) == 0) {
- if (max_proto > PSMOUSE_IMEX) {
- if (!set_properties || vmmouse_init(psmouse) == 0)
- return PSMOUSE_VMMOUSE;
- }
- }
-
-/*
- * Try Kensington ThinkingMouse (we try first, because synaptics probe
- * upsets the thinkingmouse).
- */
-
- if (max_proto > PSMOUSE_IMEX &&
- psmouse_do_detect(thinking_detect, psmouse, set_properties) == 0) {
- return PSMOUSE_THINKPS;
- }
-
-/*
- * Try Synaptics TouchPad. Note that probing is done even if Synaptics protocol
- * support is disabled in config - we need to know if it is synaptics so we
- * can reset it properly after probing for intellimouse.
- */
- if (max_proto > PSMOUSE_PS2 &&
- psmouse_do_detect(synaptics_detect, psmouse, set_properties) == 0) {
- synaptics_hardware = true;
-
- if (max_proto > PSMOUSE_IMEX) {
-/*
- * Try activating protocol, but check if support is enabled first, since
- * we try detecting Synaptics even when protocol is disabled.
- */
- if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) &&
- (!set_properties || synaptics_init(psmouse) == 0)) {
- return PSMOUSE_SYNAPTICS;
- }
-
-/*
- * Some Synaptics touchpads can emulate extended protocols (like IMPS/2).
- * Unfortunately Logitech/Genius probes confuse some firmware versions so
- * we'll have to skip them.
- */
- max_proto = PSMOUSE_IMEX;
- }
-/*
- * Make sure that touchpad is in relative mode, gestures (taps) are enabled
- */
- synaptics_reset(psmouse);
- }
-
-/*
- * Try Cypress Trackpad.
- * Must try it before Finger Sensing Pad because Finger Sensing Pad probe
- * upsets some modules of Cypress Trackpads.
- */
- if (max_proto > PSMOUSE_IMEX &&
- cypress_detect(psmouse, set_properties) == 0) {
- if (IS_ENABLED(CONFIG_MOUSE_PS2_CYPRESS)) {
- if (cypress_init(psmouse) == 0)
- return PSMOUSE_CYPRESS;
-
- /*
- * Finger Sensing Pad probe upsets some modules of
- * Cypress Trackpad, must avoid Finger Sensing Pad
- * probe if Cypress Trackpad device detected.
- */
- return PSMOUSE_PS2;
- }
-
- max_proto = PSMOUSE_IMEX;
- }
-
-/*
- * Try ALPS TouchPad
- */
- if (max_proto > PSMOUSE_IMEX) {
- ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
- if (psmouse_do_detect(alps_detect,
- psmouse, set_properties) == 0) {
- if (!set_properties || alps_init(psmouse) == 0)
- return PSMOUSE_ALPS;
-/*
- * Init failed, try basic relative protocols
- */
- max_proto = PSMOUSE_IMEX;
- }
- }
-
-/*
- * Try OLPC HGPK touchpad.
- */
- if (max_proto > PSMOUSE_IMEX &&
- psmouse_do_detect(hgpk_detect, psmouse, set_properties) == 0) {
- if (!set_properties || hgpk_init(psmouse) == 0)
- return PSMOUSE_HGPK;
-/*
- * Init failed, try basic relative protocols
- */
- max_proto = PSMOUSE_IMEX;
- }
-
-/*
- * Try Elantech touchpad.
- */
- if (max_proto > PSMOUSE_IMEX &&
- psmouse_do_detect(elantech_detect, psmouse, set_properties) == 0) {
- if (!set_properties || elantech_init(psmouse) == 0)
- return PSMOUSE_ELANTECH;
-/*
- * Init failed, try basic relative protocols
- */
- max_proto = PSMOUSE_IMEX;
- }
-
- if (max_proto > PSMOUSE_IMEX) {
- if (psmouse_do_detect(genius_detect,
- psmouse, set_properties) == 0)
- return PSMOUSE_GENPS;
-
- if (psmouse_do_detect(ps2pp_init,
- psmouse, set_properties) == 0)
- return PSMOUSE_PS2PP;
-
- if (psmouse_do_detect(trackpoint_detect,
- psmouse, set_properties) == 0)
- return PSMOUSE_TRACKPOINT;
-
- if (psmouse_do_detect(touchkit_ps2_detect,
- psmouse, set_properties) == 0)
- return PSMOUSE_TOUCHKIT_PS2;
- }
-
-/*
- * Try Finger Sensing Pad. We do it here because its probe upsets
- * Trackpoint devices (causing TP_READ_ID command to time out).
- */
- if (max_proto > PSMOUSE_IMEX) {
- if (psmouse_do_detect(fsp_detect,
- psmouse, set_properties) == 0) {
- if (!set_properties || fsp_init(psmouse) == 0)
- return PSMOUSE_FSP;
-/*
- * Init failed, try basic relative protocols
- */
- max_proto = PSMOUSE_IMEX;
- }
- }
-
-/*
- * Reset to defaults in case the device got confused by extended
- * protocol probes. Note that we follow up with full reset because
- * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS.
- */
- ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
- psmouse_reset(psmouse);
-
- if (max_proto >= PSMOUSE_IMEX &&
- psmouse_do_detect(im_explorer_detect,
- psmouse, set_properties) == 0) {
- return PSMOUSE_IMEX;
- }
-
- if (max_proto >= PSMOUSE_IMPS &&
- psmouse_do_detect(intellimouse_detect,
- psmouse, set_properties) == 0) {
- return PSMOUSE_IMPS;
- }
-
-/*
- * Okay, all failed, we have a standard mouse here. The number of the buttons
- * is still a question, though. We assume 3.
- */
- psmouse_do_detect(ps2bare_detect, psmouse, set_properties);
-
- if (synaptics_hardware) {
-/*
- * We detected Synaptics hardware but it did not respond to IMPS/2 probes.
- * We need to reset the touchpad because if there is a track point on the
- * pass through port it could get disabled while probing for protocol
- * extensions.
- */
- psmouse_reset(psmouse);
- }
-
- return PSMOUSE_PS2;
-}
-
static const struct psmouse_protocol psmouse_protocols[] = {
{
.type = PSMOUSE_PS2,
@@ -985,13 +692,14 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.maxproto = true,
.ignore_parity = true,
.detect = ps2bare_detect,
+ .try_passthru = true,
},
#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP
{
.type = PSMOUSE_PS2PP,
.name = "PS2++",
.alias = "logitech",
- .detect = ps2pp_init,
+ .detect = ps2pp_detect,
},
#endif
{
@@ -1022,6 +730,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.maxproto = true,
.ignore_parity = true,
.detect = intellimouse_detect,
+ .try_passthru = true,
},
{
.type = PSMOUSE_IMEX,
@@ -1030,6 +739,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.maxproto = true,
.ignore_parity = true,
.detect = im_explorer_detect,
+ .try_passthru = true,
},
#ifdef CONFIG_MOUSE_PS2_SYNAPTICS
{
@@ -1061,6 +771,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.type = PSMOUSE_LIFEBOOK,
.name = "LBPS/2",
.alias = "lifebook",
+ .detect = lifebook_detect,
.init = lifebook_init,
},
#endif
@@ -1070,6 +781,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.name = "TPPS/2",
.alias = "trackpoint",
.detect = trackpoint_detect,
+ .try_passthru = true,
},
#endif
#ifdef CONFIG_MOUSE_PS2_TOUCHKIT
@@ -1138,7 +850,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
},
};
-static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type)
+static const struct psmouse_protocol *__psmouse_protocol_by_type(enum psmouse_type type)
{
int i;
@@ -1146,6 +858,17 @@ static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type
if (psmouse_protocols[i].type == type)
return &psmouse_protocols[i];
+ return NULL;
+}
+
+static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type)
+{
+ const struct psmouse_protocol *proto;
+
+ proto = __psmouse_protocol_by_type(type);
+ if (proto)
+ return proto;
+
WARN_ON(1);
return &psmouse_protocols[0];
}
@@ -1166,23 +889,288 @@ static const struct psmouse_protocol *psmouse_protocol_by_name(const char *name,
return NULL;
}
+/*
+ * Apply default settings to the psmouse structure. Most of them will
+ * be overridden by individual protocol initialization routines.
+ */
+static void psmouse_apply_defaults(struct psmouse *psmouse)
+{
+ struct input_dev *input_dev = psmouse->dev;
+
+ memset(input_dev->evbit, 0, sizeof(input_dev->evbit));
+ memset(input_dev->keybit, 0, sizeof(input_dev->keybit));
+ memset(input_dev->relbit, 0, sizeof(input_dev->relbit));
+ memset(input_dev->absbit, 0, sizeof(input_dev->absbit));
+ memset(input_dev->mscbit, 0, sizeof(input_dev->mscbit));
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(EV_REL, input_dev->evbit);
+
+ __set_bit(BTN_LEFT, input_dev->keybit);
+ __set_bit(BTN_RIGHT, input_dev->keybit);
+
+ __set_bit(REL_X, input_dev->relbit);
+ __set_bit(REL_Y, input_dev->relbit);
+
+ __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
+ psmouse->set_rate = psmouse_set_rate;
+ psmouse->set_resolution = psmouse_set_resolution;
+ psmouse->set_scale = psmouse_set_scale;
+ psmouse->poll = psmouse_poll;
+ psmouse->protocol_handler = psmouse_process_byte;
+ psmouse->pktsize = 3;
+ psmouse->reconnect = NULL;
+ psmouse->disconnect = NULL;
+ psmouse->cleanup = NULL;
+ psmouse->pt_activate = NULL;
+ psmouse->pt_deactivate = NULL;
+}
+
+static bool psmouse_try_protocol(struct psmouse *psmouse,
+ enum psmouse_type type,
+ unsigned int *max_proto,
+ bool set_properties, bool init_allowed)
+{
+ const struct psmouse_protocol *proto;
+
+ proto = __psmouse_protocol_by_type(type);
+ if (!proto)
+ return false;
+
+ if (psmouse->ps2dev.serio->id.type == SERIO_PS_PSTHRU &&
+ !proto->try_passthru) {
+ return false;
+ }
+
+ if (set_properties)
+ psmouse_apply_defaults(psmouse);
+
+ if (proto->detect(psmouse, set_properties) != 0)
+ return false;
+
+ if (set_properties && proto->init && init_allowed) {
+ if (proto->init(psmouse) != 0) {
+ /*
+ * We detected device, but init failed. Adjust
+ * max_proto so we only try standard protocols.
+ */
+ if (*max_proto > PSMOUSE_IMEX)
+ *max_proto = PSMOUSE_IMEX;
+
+ return false;
+ }
+ }
+
+ return true;
+}
/*
- * psmouse_probe() probes for a PS/2 mouse.
+ * psmouse_extensions() probes for any extensions to the basic PS/2 protocol
+ * the mouse may have.
*/
+static int psmouse_extensions(struct psmouse *psmouse,
+ unsigned int max_proto, bool set_properties)
+{
+ bool synaptics_hardware = false;
+
+ /*
+ * Always check for focaltech, this is safe as it uses pnp-id
+ * matching.
+ */
+ if (psmouse_try_protocol(psmouse, PSMOUSE_FOCALTECH,
+ &max_proto, set_properties, false)) {
+ if (max_proto > PSMOUSE_IMEX &&
+ IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH) &&
+ (!set_properties || focaltech_init(psmouse) == 0)) {
+ return PSMOUSE_FOCALTECH;
+ }
+ /*
+ * Restrict psmouse_max_proto so that psmouse_initialize()
+ * does not try to reset rate and resolution, because even
+ * that upsets the device.
+ * This also causes us to basically fall through to basic
+ * protocol detection, where we fully reset the mouse,
+ * and set it up as bare PS/2 protocol device.
+ */
+ psmouse_max_proto = max_proto = PSMOUSE_PS2;
+ }
+
+ /*
+ * We always check for LifeBook because it does not disturb mouse
+ * (it only checks DMI information).
+ */
+ if (psmouse_try_protocol(psmouse, PSMOUSE_LIFEBOOK, &max_proto,
+ set_properties, max_proto > PSMOUSE_IMEX))
+ return PSMOUSE_LIFEBOOK;
+ if (psmouse_try_protocol(psmouse, PSMOUSE_VMMOUSE, &max_proto,
+ set_properties, max_proto > PSMOUSE_IMEX))
+ return PSMOUSE_VMMOUSE;
+
+ /*
+ * Try Kensington ThinkingMouse (we try first, because Synaptics
+ * probe upsets the ThinkingMouse).
+ */
+ if (max_proto > PSMOUSE_IMEX &&
+ psmouse_try_protocol(psmouse, PSMOUSE_THINKPS, &max_proto,
+ set_properties, true)) {
+ return PSMOUSE_THINKPS;
+ }
+
+ /*
+ * Try Synaptics TouchPad. Note that probing is done even if
+ * Synaptics protocol support is disabled in config - we need to
+ * know if it is Synaptics so we can reset it properly after
+ * probing for IntelliMouse.
+ */
+ if (max_proto > PSMOUSE_PS2 &&
+ psmouse_try_protocol(psmouse, PSMOUSE_SYNAPTICS, &max_proto,
+ set_properties, false)) {
+ synaptics_hardware = true;
+
+ if (max_proto > PSMOUSE_IMEX) {
+ /*
+ * Try activating protocol, but check if support is
+ * enabled first, since we try detecting Synaptics
+ * even when protocol is disabled.
+ */
+ if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) &&
+ (!set_properties || synaptics_init(psmouse) == 0)) {
+ return PSMOUSE_SYNAPTICS;
+ }
+
+ /*
+ * Some Synaptics touchpads can emulate extended
+ * protocols (like IMPS/2). Unfortunately
+ * Logitech/Genius probes confuse some firmware
+ * versions so we'll have to skip them.
+ */
+ max_proto = PSMOUSE_IMEX;
+ }
+
+ /*
+ * Make sure that touchpad is in relative mode, gestures
+ * (taps) are enabled.
+ */
+ synaptics_reset(psmouse);
+ }
+
+ /*
+ * Try Cypress Trackpad. We must try it before Finger Sensing Pad
+ * because Finger Sensing Pad probe upsets some modules of Cypress
+ * Trackpads.
+ */
+ if (max_proto > PSMOUSE_IMEX &&
+ psmouse_try_protocol(psmouse, PSMOUSE_CYPRESS, &max_proto,
+ set_properties, true)) {
+ return PSMOUSE_CYPRESS;
+ }
+
+ /* Try ALPS TouchPad */
+ if (max_proto > PSMOUSE_IMEX) {
+ ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+ if (psmouse_try_protocol(psmouse, PSMOUSE_ALPS,
+ &max_proto, set_properties, true))
+ return PSMOUSE_ALPS;
+ }
+
+ /* Try OLPC HGPK touchpad */
+ if (max_proto > PSMOUSE_IMEX &&
+ psmouse_try_protocol(psmouse, PSMOUSE_HGPK, &max_proto,
+ set_properties, true)) {
+ return PSMOUSE_HGPK;
+ }
+
+ /* Try Elantech touchpad */
+ if (max_proto > PSMOUSE_IMEX &&
+ psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH,
+ &max_proto, set_properties, true)) {
+ return PSMOUSE_ELANTECH;
+ }
+
+ if (max_proto > PSMOUSE_IMEX) {
+ if (psmouse_try_protocol(psmouse, PSMOUSE_GENPS,
+ &max_proto, set_properties, true))
+ return PSMOUSE_GENPS;
+
+ if (psmouse_try_protocol(psmouse, PSMOUSE_PS2PP,
+ &max_proto, set_properties, true))
+ return PSMOUSE_PS2PP;
+
+ if (psmouse_try_protocol(psmouse, PSMOUSE_TRACKPOINT,
+ &max_proto, set_properties, true))
+ return PSMOUSE_TRACKPOINT;
+
+ if (psmouse_try_protocol(psmouse, PSMOUSE_TOUCHKIT_PS2,
+ &max_proto, set_properties, true))
+ return PSMOUSE_TOUCHKIT_PS2;
+ }
+
+ /*
+ * Try Finger Sensing Pad. We do it here because its probe upsets
+ * Trackpoint devices (causing TP_READ_ID command to time out).
+ */
+ if (max_proto > PSMOUSE_IMEX &&
+ psmouse_try_protocol(psmouse, PSMOUSE_FSP,
+ &max_proto, set_properties, true)) {
+ return PSMOUSE_FSP;
+ }
+
+ /*
+ * Reset to defaults in case the device got confused by extended
+ * protocol probes. Note that we follow up with full reset because
+ * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS.
+ */
+ ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+ psmouse_reset(psmouse);
+
+ if (max_proto >= PSMOUSE_IMEX &&
+ psmouse_try_protocol(psmouse, PSMOUSE_IMEX,
+ &max_proto, set_properties, true)) {
+ return PSMOUSE_IMEX;
+ }
+
+ if (max_proto >= PSMOUSE_IMPS &&
+ psmouse_try_protocol(psmouse, PSMOUSE_IMPS,
+ &max_proto, set_properties, true)) {
+ return PSMOUSE_IMPS;
+ }
+
+ /*
+ * Okay, all failed, we have a standard mouse here. The number of
+ * the buttons is still a question, though. We assume 3.
+ */
+ psmouse_try_protocol(psmouse, PSMOUSE_PS2,
+ &max_proto, set_properties, true);
+
+ if (synaptics_hardware) {
+ /*
+ * We detected Synaptics hardware but it did not respond to
+ * IMPS/2 probes. We need to reset the touchpad because if
+ * there is a track point on the pass through port it could
+ * get disabled while probing for protocol extensions.
+ */
+ psmouse_reset(psmouse);
+ }
+
+ return PSMOUSE_PS2;
+}
+
+/*
+ * psmouse_probe() probes for a PS/2 mouse.
+ */
static int psmouse_probe(struct psmouse *psmouse)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[2];
-/*
- * First, we check if it's a mouse. It should send 0x00 or 0x03
- * in case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer.
- * Sunrex K8561 IR Keyboard/Mouse reports 0xff on second and subsequent
- * ID queries, probably due to a firmware bug.
- */
-
+ /*
+ * First, we check if it's a mouse. It should send 0x00 or 0x03 in
+ * case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer.
+ * Sunrex K8561 IR Keyboard/Mouse reports 0xff on second and
+ * subsequent ID queries, probably due to a firmware bug.
+ */
param[0] = 0xa5;
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETID))
return -1;
@@ -1191,10 +1179,10 @@ static int psmouse_probe(struct psmouse *psmouse)
param[0] != 0x04 && param[0] != 0xff)
return -1;
-/*
- * Then we reset and disable the mouse so that it doesn't generate events.
- */
-
+ /*
+ * Then we reset and disable the mouse so that it doesn't generate
+ * events.
+ */
if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS))
psmouse_warn(psmouse, "Failed to reset mouse on %s\n",
ps2dev->serio->phys);
@@ -1205,13 +1193,11 @@ static int psmouse_probe(struct psmouse *psmouse)
/*
* psmouse_initialize() initializes the mouse to a sane state.
*/
-
static void psmouse_initialize(struct psmouse *psmouse)
{
-/*
- * We set the mouse report rate, resolution and scaling.
- */
-
+ /*
+ * We set the mouse report rate, resolution and scaling.
+ */
if (psmouse_max_proto != PSMOUSE_PS2) {
psmouse->set_rate(psmouse, psmouse->rate);
psmouse->set_resolution(psmouse, psmouse->resolution);
@@ -1222,7 +1208,6 @@ static void psmouse_initialize(struct psmouse *psmouse)
/*
* psmouse_activate() enables the mouse so that we get motion reports from it.
*/
-
int psmouse_activate(struct psmouse *psmouse)
{
if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
@@ -1236,10 +1221,9 @@ int psmouse_activate(struct psmouse *psmouse)
}
/*
- * psmouse_deactivate() puts the mouse into poll mode so that we don't get motion
- * reports from it unless we explicitly request it.
+ * psmouse_deactivate() puts the mouse into poll mode so that we don't get
+ * motion reports from it unless we explicitly request it.
*/
-
int psmouse_deactivate(struct psmouse *psmouse)
{
if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) {
@@ -1252,11 +1236,9 @@ int psmouse_deactivate(struct psmouse *psmouse)
return 0;
}
-
/*
* psmouse_resync() attempts to re-validate current protocol.
*/
-
static void psmouse_resync(struct work_struct *work)
{
struct psmouse *parent = NULL, *psmouse =
@@ -1276,16 +1258,16 @@ static void psmouse_resync(struct work_struct *work)
psmouse_deactivate(parent);
}
-/*
- * Some mice don't ACK commands sent while they are in the middle of
- * transmitting motion packet. To avoid delay we use ps2_sendbyte()
- * instead of ps2_command() which would wait for 200ms for an ACK
- * that may never come.
- * As an additional quirk ALPS touchpads may not only forget to ACK
- * disable command but will stop reporting taps, so if we see that
- * mouse at least once ACKs disable we will do full reconnect if ACK
- * is missing.
- */
+ /*
+ * Some mice don't ACK commands sent while they are in the middle of
+ * transmitting motion packet. To avoid delay we use ps2_sendbyte()
+ * instead of ps2_command() which would wait for 200ms for an ACK
+ * that may never come.
+ * As an additional quirk ALPS touchpads may not only forget to ACK
+ * disable command but will stop reporting taps, so if we see that
+ * mouse at least once ACKs disable we will do full reconnect if ACK
+ * is missing.
+ */
psmouse->num_resyncs++;
if (ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE, 20)) {
@@ -1294,13 +1276,13 @@ static void psmouse_resync(struct work_struct *work)
} else
psmouse->acks_disable_command = true;
-/*
- * Poll the mouse. If it was reset the packet will be shorter than
- * psmouse->pktsize and ps2_command will fail. We do not expect and
- * do not handle scenario when mouse "upgrades" its protocol while
- * disconnected since it would require additional delay. If we ever
- * see a mouse that does it we'll adjust the code.
- */
+ /*
+ * Poll the mouse. If it was reset the packet will be shorter than
+ * psmouse->pktsize and ps2_command will fail. We do not expect and
+ * do not handle scenario when mouse "upgrades" its protocol while
+ * disconnected since it would require additional delay. If we ever
+ * see a mouse that does it we'll adjust the code.
+ */
if (!failed) {
if (psmouse->poll(psmouse))
failed = true;
@@ -1317,11 +1299,12 @@ static void psmouse_resync(struct work_struct *work)
psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
}
}
-/*
- * Now try to enable mouse. We try to do that even if poll failed and also
- * repeat our attempts 5 times, otherwise we may be left out with disabled
- * mouse.
- */
+
+ /*
+ * Now try to enable mouse. We try to do that even if poll failed
+ * and also repeat our attempts 5 times, otherwise we may be left
+ * out with disabled mouse.
+ */
for (i = 0; i < 5; i++) {
if (!ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
enabled = true;
@@ -1353,7 +1336,6 @@ static void psmouse_resync(struct work_struct *work)
/*
* psmouse_cleanup() resets the mouse into power-on state.
*/
-
static void psmouse_cleanup(struct serio *serio)
{
struct psmouse *psmouse = serio_get_drvdata(serio);
@@ -1378,15 +1360,15 @@ static void psmouse_cleanup(struct serio *serio)
if (psmouse->cleanup)
psmouse->cleanup(psmouse);
-/*
- * Reset the mouse to defaults (bare PS/2 protocol).
- */
+ /*
+ * Reset the mouse to defaults (bare PS/2 protocol).
+ */
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
-/*
- * Some boxes, such as HP nx7400, get terribly confused if mouse
- * is not fully enabled before suspending/shutting down.
- */
+ /*
+ * Some boxes, such as HP nx7400, get terribly confused if mouse
+ * is not fully enabled before suspending/shutting down.
+ */
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE);
if (parent) {
@@ -1402,7 +1384,6 @@ static void psmouse_cleanup(struct serio *serio)
/*
* psmouse_disconnect() closes and frees.
*/
-
static void psmouse_disconnect(struct serio *serio)
{
struct psmouse *psmouse, *parent = NULL;
@@ -1602,7 +1583,6 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
goto out;
}
-
static int psmouse_reconnect(struct serio *serio)
{
struct psmouse *psmouse = serio_get_drvdata(serio);
diff --git a/drivers/input/serio/hyperv-keyboard.c b/drivers/input/serio/hyperv-keyboard.c
index e74e5d6e5f9f..c948866edf87 100644
--- a/drivers/input/serio/hyperv-keyboard.c
+++ b/drivers/input/serio/hyperv-keyboard.c
@@ -412,16 +412,6 @@ static int hv_kbd_remove(struct hv_device *hv_dev)
return 0;
}
-/*
- * Keyboard GUID
- * {f912ad6d-2b17-48ea-bd65-f927a61c7684}
- */
-#define HV_KBD_GUID \
- .guid = { \
- 0x6d, 0xad, 0x12, 0xf9, 0x17, 0x2b, 0xea, 0x48, \
- 0xbd, 0x65, 0xf9, 0x27, 0xa6, 0x1c, 0x76, 0x84 \
- }
-
static const struct hv_vmbus_device_id id_table[] = {
/* Keyboard guid */
{ HV_KBD_GUID, },
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index c11556563ef0..68f5f4a0f1e7 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -258,6 +258,13 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
},
},
{
+ /* Fujitsu Lifebook U745 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U745"),
+ },
+ },
+ {
/* Fujitsu T70H */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
diff --git a/drivers/input/serio/parkbd.c b/drivers/input/serio/parkbd.c
index 92c31b8f8fb4..1edfac78d4ac 100644
--- a/drivers/input/serio/parkbd.c
+++ b/drivers/input/serio/parkbd.c
@@ -145,6 +145,7 @@ static int parkbd_getport(struct parport *pp)
{
struct pardev_cb parkbd_parport_cb;
+ memset(&parkbd_parport_cb, 0, sizeof(parkbd_parport_cb));
parkbd_parport_cb.irq_func = parkbd_interrupt;
parkbd_parport_cb.flags = PARPORT_FLAG_EXCL;
diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c
index e7f966da6efa..78ca44840d60 100644
--- a/drivers/input/tablet/aiptek.c
+++ b/drivers/input/tablet/aiptek.c
@@ -1819,6 +1819,14 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id)
input_set_abs_params(inputdev, ABS_TILT_Y, AIPTEK_TILT_MIN, AIPTEK_TILT_MAX, 0, 0);
input_set_abs_params(inputdev, ABS_WHEEL, AIPTEK_WHEEL_MIN, AIPTEK_WHEEL_MAX - 1, 0, 0);
+ /* Verify that a device really has an endpoint */
+ if (intf->altsetting[0].desc.bNumEndpoints < 1) {
+ dev_err(&intf->dev,
+ "interface has %d endpoints, but must have minimum 1\n",
+ intf->altsetting[0].desc.bNumEndpoints);
+ err = -EINVAL;
+ goto fail3;
+ }
endpoint = &intf->altsetting[0].endpoint[0].desc;
/* Go set up our URB, which is called when the tablet receives
@@ -1861,6 +1869,7 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id)
if (i == ARRAY_SIZE(speeds)) {
dev_info(&intf->dev,
"Aiptek tried all speeds, no sane response\n");
+ err = -EINVAL;
goto fail3;
}
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index ae33da7ab51f..53a97b379c9f 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -295,6 +295,16 @@ config TOUCHSCREEN_EGALAX
To compile this driver as a module, choose M here: the
module will be called egalax_ts.
+config TOUCHSCREEN_EGALAX_SERIAL
+ tristate "EETI eGalax serial touchscreen"
+ select SERIO
+ help
+ Say Y here to enable support for serial connected EETI
+ eGalax touch panels.
+
+ To compile this driver as a module, choose M here: the
+ module will be called egalax_ts_serial.
+
config TOUCHSCREEN_FT6236
tristate "FT6236 I2C touchscreen"
depends on I2C
@@ -324,6 +334,7 @@ config TOUCHSCREEN_FUJITSU
config TOUCHSCREEN_GOODIX
tristate "Goodix I2C touchscreen"
depends on I2C
+ depends on GPIOLIB
help
Say Y here if you have the Goodix touchscreen (such as one
installed in Onda v975w tablets) connected to your
@@ -927,6 +938,22 @@ config TOUCHSCREEN_TOUCHIT213
To compile this driver as a module, choose M here: the
module will be called touchit213.
+config TOUCHSCREEN_TS4800
+ tristate "TS-4800 touchscreen"
+ depends on HAS_IOMEM && OF
+ select MFD_SYSCON
+ select INPUT_POLLDEV
+ help
+ Say Y here if you have a touchscreen on a TS-4800 board.
+
+ On TS-4800, the touchscreen is not handled directly by Linux but by
+ a companion FPGA.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ts4800_ts.
+
config TOUCHSCREEN_TSC_SERIO
tristate "TSC-10/25/40 serial touchscreen support"
select SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index cbaa6abb08da..968ff12e3132 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o
+obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL) += egalax_ts_serial.o
obj-$(CONFIG_TOUCHSCREEN_FT6236) += ft6236.o
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o
@@ -68,6 +69,7 @@ obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
+obj-$(CONFIG_TOUCHSCREEN_TS4800) += ts4800-ts.o
obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO) += tsc40.o
obj-$(CONFIG_TOUCHSCREEN_TSC200X_CORE) += tsc200x-core.o
obj-$(CONFIG_TOUCHSCREEN_TSC2004) += tsc2004.o
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index c5622058c22b..2d5794ec338b 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -2487,6 +2487,31 @@ static struct mxt_acpi_platform_data samus_platform_data[] = {
{ }
};
+static unsigned int chromebook_tp_buttons[] = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ BTN_LEFT
+};
+
+static struct mxt_acpi_platform_data chromebook_platform_data[] = {
+ {
+ /* Touchpad */
+ .hid = "ATML0000",
+ .pdata = {
+ .t19_num_keys = ARRAY_SIZE(chromebook_tp_buttons),
+ .t19_keymap = chromebook_tp_buttons,
+ },
+ },
+ {
+ /* Touchscreen */
+ .hid = "ATML0001",
+ },
+ { }
+};
+
static const struct dmi_system_id mxt_dmi_table[] = {
{
/* 2015 Google Pixel */
@@ -2497,6 +2522,14 @@ static const struct dmi_system_id mxt_dmi_table[] = {
},
.driver_data = samus_platform_data,
},
+ {
+ /* Other Google Chromebooks */
+ .ident = "Chromebook",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
+ },
+ .driver_data = chromebook_platform_data,
+ },
{ }
};
@@ -2701,6 +2734,7 @@ static const struct i2c_device_id mxt_id[] = {
{ "qt602240_ts", 0 },
{ "atmel_mxt_ts", 0 },
{ "atmel_mxt_tp", 0 },
+ { "maxtouch", 0 },
{ "mXT224", 0 },
{ }
};
diff --git a/drivers/input/touchscreen/egalax_ts_serial.c b/drivers/input/touchscreen/egalax_ts_serial.c
new file mode 100644
index 000000000000..657bbae608c8
--- /dev/null
+++ b/drivers/input/touchscreen/egalax_ts_serial.c
@@ -0,0 +1,194 @@
+/*
+ * EETI Egalax serial touchscreen driver
+ *
+ * Copyright (c) 2015 Zoltán Böszörményi <zboszor@pr.hu>
+ *
+ * based on the
+ *
+ * Hampshire serial touchscreen driver (Copyright (c) 2010 Adam Bennett)
+ */
+
+/*
+ * 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/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "EETI Egalax serial touchscreen driver"
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define EGALAX_FORMAT_MAX_LENGTH 6
+#define EGALAX_FORMAT_START_BIT BIT(7)
+#define EGALAX_FORMAT_PRESSURE_BIT BIT(6)
+#define EGALAX_FORMAT_TOUCH_BIT BIT(0)
+#define EGALAX_FORMAT_RESOLUTION_MASK 0x06
+
+#define EGALAX_MIN_XC 0
+#define EGALAX_MAX_XC 0x4000
+#define EGALAX_MIN_YC 0
+#define EGALAX_MAX_YC 0x4000
+
+/*
+ * Per-touchscreen data.
+ */
+struct egalax {
+ struct input_dev *input;
+ struct serio *serio;
+ int idx;
+ u8 data[EGALAX_FORMAT_MAX_LENGTH];
+ char phys[32];
+};
+
+static void egalax_process_data(struct egalax *egalax)
+{
+ struct input_dev *dev = egalax->input;
+ u8 *data = egalax->data;
+ u16 x, y;
+ u8 shift;
+ u8 mask;
+
+ shift = 3 - ((data[0] & EGALAX_FORMAT_RESOLUTION_MASK) >> 1);
+ mask = 0xff >> (shift + 1);
+
+ x = (((u16)(data[1] & mask) << 7) | (data[2] & 0x7f)) << shift;
+ y = (((u16)(data[3] & mask) << 7) | (data[4] & 0x7f)) << shift;
+
+ input_report_key(dev, BTN_TOUCH, data[0] & EGALAX_FORMAT_TOUCH_BIT);
+ input_report_abs(dev, ABS_X, x);
+ input_report_abs(dev, ABS_Y, y);
+ input_sync(dev);
+}
+
+static irqreturn_t egalax_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct egalax *egalax = serio_get_drvdata(serio);
+ int pkt_len;
+
+ egalax->data[egalax->idx++] = data;
+
+ if (likely(egalax->data[0] & EGALAX_FORMAT_START_BIT)) {
+ pkt_len = egalax->data[0] & EGALAX_FORMAT_PRESSURE_BIT ? 6 : 5;
+ if (pkt_len == egalax->idx) {
+ egalax_process_data(egalax);
+ egalax->idx = 0;
+ }
+ } else {
+ dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n",
+ egalax->data[0]);
+ egalax->idx = 0;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * egalax_connect() is the routine that is called when someone adds a
+ * new serio device that supports egalax protocol and registers it as
+ * an input device. This is usually accomplished using inputattach.
+ */
+static int egalax_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct egalax *egalax;
+ struct input_dev *input_dev;
+ int error;
+
+ egalax = kzalloc(sizeof(struct egalax), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!egalax || !input_dev) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ egalax->serio = serio;
+ egalax->input = input_dev;
+ snprintf(egalax->phys, sizeof(egalax->phys),
+ "%s/input0", serio->phys);
+
+ input_dev->name = "EETI eGalaxTouch Serial TouchScreen";
+ input_dev->phys = egalax->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_EGALAX;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0001;
+ input_dev->dev.parent = &serio->dev;
+
+ input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+ input_set_abs_params(input_dev, ABS_X,
+ EGALAX_MIN_XC, EGALAX_MAX_XC, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ EGALAX_MIN_YC, EGALAX_MAX_YC, 0, 0);
+
+ serio_set_drvdata(serio, egalax);
+
+ error = serio_open(serio, drv);
+ if (error)
+ goto err_reset_drvdata;
+
+ error = input_register_device(input_dev);
+ if (error)
+ goto err_close_serio;
+
+ return 0;
+
+err_close_serio:
+ serio_close(serio);
+err_reset_drvdata:
+ serio_set_drvdata(serio, NULL);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(egalax);
+ return error;
+}
+
+static void egalax_disconnect(struct serio *serio)
+{
+ struct egalax *egalax = serio_get_drvdata(serio);
+
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_unregister_device(egalax->input);
+ kfree(egalax);
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static const struct serio_device_id egalax_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_EGALAX,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, egalax_serio_ids);
+
+static struct serio_driver egalax_drv = {
+ .driver = {
+ .name = "egalax",
+ },
+ .description = DRIVER_DESC,
+ .id_table = egalax_serio_ids,
+ .interrupt = egalax_interrupt,
+ .connect = egalax_connect,
+ .disconnect = egalax_disconnect,
+};
+module_serio_driver(egalax_drv);
+
+MODULE_AUTHOR("Zoltán Böszörményi <zboszor@pr.hu>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c
index 17cc20ef4923..ac09855fa435 100644
--- a/drivers/input/touchscreen/elants_i2c.c
+++ b/drivers/input/touchscreen/elants_i2c.c
@@ -1316,7 +1316,13 @@ static int __maybe_unused elants_i2c_suspend(struct device *dev)
disable_irq(client->irq);
- if (device_may_wakeup(dev) || ts->keep_power_in_suspend) {
+ if (device_may_wakeup(dev)) {
+ /*
+ * The device will automatically enter idle mode
+ * that has reduced power consumption.
+ */
+ ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
+ } else if (ts->keep_power_in_suspend) {
for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
error = elants_i2c_send(client, set_sleep_cmd,
sizeof(set_sleep_cmd));
@@ -1326,10 +1332,6 @@ static int __maybe_unused elants_i2c_suspend(struct device *dev)
dev_err(&client->dev,
"suspend command failed: %d\n", error);
}
-
- if (device_may_wakeup(dev))
- ts->wake_irq_enabled =
- (enable_irq_wake(client->irq) == 0);
} else {
elants_i2c_power_off(ts);
}
@@ -1345,10 +1347,11 @@ static int __maybe_unused elants_i2c_resume(struct device *dev)
int retry_cnt;
int error;
- if (device_may_wakeup(dev) && ts->wake_irq_enabled)
- disable_irq_wake(client->irq);
-
- if (ts->keep_power_in_suspend) {
+ if (device_may_wakeup(dev)) {
+ if (ts->wake_irq_enabled)
+ disable_irq_wake(client->irq);
+ elants_i2c_sw_reset(client);
+ } else if (ts->keep_power_in_suspend) {
for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
error = elants_i2c_send(client, set_active_cmd,
sizeof(set_active_cmd));
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 4d113c9e4b77..240b16f3ee97 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -2,6 +2,7 @@
* Driver for Goodix Touchscreens
*
* Copyright (c) 2014 Red Hat Inc.
+ * Copyright (c) 2015 K. Merker <merker@debian.org>
*
* This code is based on gt9xx.c authored by andrew@goodix.com:
*
@@ -16,6 +17,8 @@
#include <linux/kernel.h>
#include <linux/dmi.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/mt.h>
@@ -33,11 +36,24 @@ struct goodix_ts_data {
struct input_dev *input_dev;
int abs_x_max;
int abs_y_max;
+ bool swapped_x_y;
+ bool inverted_x;
+ bool inverted_y;
unsigned int max_touch_num;
unsigned int int_trigger_type;
- bool rotated_screen;
+ int cfg_len;
+ struct gpio_desc *gpiod_int;
+ struct gpio_desc *gpiod_rst;
+ u16 id;
+ u16 version;
+ const char *cfg_name;
+ struct completion firmware_loading_complete;
+ unsigned long irq_flags;
};
+#define GOODIX_GPIO_INT_NAME "irq"
+#define GOODIX_GPIO_RST_NAME "reset"
+
#define GOODIX_MAX_HEIGHT 4096
#define GOODIX_MAX_WIDTH 4096
#define GOODIX_INT_TRIGGER 1
@@ -45,8 +61,13 @@ struct goodix_ts_data {
#define GOODIX_MAX_CONTACTS 10
#define GOODIX_CONFIG_MAX_LENGTH 240
+#define GOODIX_CONFIG_911_LENGTH 186
+#define GOODIX_CONFIG_967_LENGTH 228
/* Register defines */
+#define GOODIX_REG_COMMAND 0x8040
+#define GOODIX_CMD_SCREEN_OFF 0x05
+
#define GOODIX_READ_COOR_ADDR 0x814E
#define GOODIX_REG_CONFIG_DATA 0x8047
#define GOODIX_REG_ID 0x8140
@@ -115,6 +136,63 @@ static int goodix_i2c_read(struct i2c_client *client,
return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
}
+/**
+ * goodix_i2c_write - write data to a register of the i2c slave device.
+ *
+ * @client: i2c device.
+ * @reg: the register to write to.
+ * @buf: raw data buffer to write.
+ * @len: length of the buffer to write
+ */
+static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf,
+ unsigned len)
+{
+ u8 *addr_buf;
+ struct i2c_msg msg;
+ int ret;
+
+ addr_buf = kmalloc(len + 2, GFP_KERNEL);
+ if (!addr_buf)
+ return -ENOMEM;
+
+ addr_buf[0] = reg >> 8;
+ addr_buf[1] = reg & 0xFF;
+ memcpy(&addr_buf[2], buf, len);
+
+ msg.flags = 0;
+ msg.addr = client->addr;
+ msg.buf = addr_buf;
+ msg.len = len + 2;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ kfree(addr_buf);
+ return ret < 0 ? ret : (ret != 1 ? -EIO : 0);
+}
+
+static int goodix_i2c_write_u8(struct i2c_client *client, u16 reg, u8 value)
+{
+ return goodix_i2c_write(client, reg, &value, sizeof(value));
+}
+
+static int goodix_get_cfg_len(u16 id)
+{
+ switch (id) {
+ case 911:
+ case 9271:
+ case 9110:
+ case 927:
+ case 928:
+ return GOODIX_CONFIG_911_LENGTH;
+
+ case 912:
+ case 967:
+ return GOODIX_CONFIG_967_LENGTH;
+
+ default:
+ return GOODIX_CONFIG_MAX_LENGTH;
+ }
+}
+
static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
{
int touch_num;
@@ -155,10 +233,13 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data)
int input_y = get_unaligned_le16(&coor_data[3]);
int input_w = get_unaligned_le16(&coor_data[5]);
- if (ts->rotated_screen) {
+ /* Inversions have to happen before axis swapping */
+ if (ts->inverted_x)
input_x = ts->abs_x_max - input_x;
+ if (ts->inverted_y)
input_y = ts->abs_y_max - input_y;
- }
+ if (ts->swapped_x_y)
+ swap(input_x, input_y);
input_mt_slot(ts->input_dev, id);
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
@@ -202,21 +283,195 @@ static void goodix_process_events(struct goodix_ts_data *ts)
*/
static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
{
- static const u8 end_cmd[] = {
- GOODIX_READ_COOR_ADDR >> 8,
- GOODIX_READ_COOR_ADDR & 0xff,
- 0
- };
struct goodix_ts_data *ts = dev_id;
goodix_process_events(ts);
- if (i2c_master_send(ts->client, end_cmd, sizeof(end_cmd)) < 0)
+ if (goodix_i2c_write_u8(ts->client, GOODIX_READ_COOR_ADDR, 0) < 0)
dev_err(&ts->client->dev, "I2C write end_cmd error\n");
return IRQ_HANDLED;
}
+static void goodix_free_irq(struct goodix_ts_data *ts)
+{
+ devm_free_irq(&ts->client->dev, ts->client->irq, ts);
+}
+
+static int goodix_request_irq(struct goodix_ts_data *ts)
+{
+ return devm_request_threaded_irq(&ts->client->dev, ts->client->irq,
+ NULL, goodix_ts_irq_handler,
+ ts->irq_flags, ts->client->name, ts);
+}
+
+/**
+ * goodix_check_cfg - Checks if config fw is valid
+ *
+ * @ts: goodix_ts_data pointer
+ * @cfg: firmware config data
+ */
+static int goodix_check_cfg(struct goodix_ts_data *ts,
+ const struct firmware *cfg)
+{
+ int i, raw_cfg_len;
+ u8 check_sum = 0;
+
+ if (cfg->size > GOODIX_CONFIG_MAX_LENGTH) {
+ dev_err(&ts->client->dev,
+ "The length of the config fw is not correct");
+ return -EINVAL;
+ }
+
+ raw_cfg_len = cfg->size - 2;
+ for (i = 0; i < raw_cfg_len; i++)
+ check_sum += cfg->data[i];
+ check_sum = (~check_sum) + 1;
+ if (check_sum != cfg->data[raw_cfg_len]) {
+ dev_err(&ts->client->dev,
+ "The checksum of the config fw is not correct");
+ return -EINVAL;
+ }
+
+ if (cfg->data[raw_cfg_len + 1] != 1) {
+ dev_err(&ts->client->dev,
+ "Config fw must have Config_Fresh register set");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * goodix_send_cfg - Write fw config to device
+ *
+ * @ts: goodix_ts_data pointer
+ * @cfg: config firmware to write to device
+ */
+static int goodix_send_cfg(struct goodix_ts_data *ts,
+ const struct firmware *cfg)
+{
+ int error;
+
+ error = goodix_check_cfg(ts, cfg);
+ if (error)
+ return error;
+
+ error = goodix_i2c_write(ts->client, GOODIX_REG_CONFIG_DATA, cfg->data,
+ cfg->size);
+ if (error) {
+ dev_err(&ts->client->dev, "Failed to write config data: %d",
+ error);
+ return error;
+ }
+ dev_dbg(&ts->client->dev, "Config sent successfully.");
+
+ /* Let the firmware reconfigure itself, so sleep for 10ms */
+ usleep_range(10000, 11000);
+
+ return 0;
+}
+
+static int goodix_int_sync(struct goodix_ts_data *ts)
+{
+ int error;
+
+ error = gpiod_direction_output(ts->gpiod_int, 0);
+ if (error)
+ return error;
+
+ msleep(50); /* T5: 50ms */
+
+ error = gpiod_direction_input(ts->gpiod_int);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+/**
+ * goodix_reset - Reset device during power on
+ *
+ * @ts: goodix_ts_data pointer
+ */
+static int goodix_reset(struct goodix_ts_data *ts)
+{
+ int error;
+
+ /* begin select I2C slave addr */
+ error = gpiod_direction_output(ts->gpiod_rst, 0);
+ if (error)
+ return error;
+
+ msleep(20); /* T2: > 10ms */
+
+ /* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */
+ error = gpiod_direction_output(ts->gpiod_int, ts->client->addr == 0x14);
+ if (error)
+ return error;
+
+ usleep_range(100, 2000); /* T3: > 100us */
+
+ error = gpiod_direction_output(ts->gpiod_rst, 1);
+ if (error)
+ return error;
+
+ usleep_range(6000, 10000); /* T4: > 5ms */
+
+ /* end select I2C slave addr */
+ error = gpiod_direction_input(ts->gpiod_rst);
+ if (error)
+ return error;
+
+ error = goodix_int_sync(ts);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+/**
+ * goodix_get_gpio_config - Get GPIO config from ACPI/DT
+ *
+ * @ts: goodix_ts_data pointer
+ */
+static int goodix_get_gpio_config(struct goodix_ts_data *ts)
+{
+ int error;
+ struct device *dev;
+ struct gpio_desc *gpiod;
+
+ if (!ts->client)
+ return -EINVAL;
+ dev = &ts->client->dev;
+
+ /* Get the interrupt GPIO pin number */
+ gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN);
+ if (IS_ERR(gpiod)) {
+ error = PTR_ERR(gpiod);
+ if (error != -EPROBE_DEFER)
+ dev_dbg(dev, "Failed to get %s GPIO: %d\n",
+ GOODIX_GPIO_INT_NAME, error);
+ return error;
+ }
+
+ ts->gpiod_int = gpiod;
+
+ /* Get the reset line GPIO pin number */
+ gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_RST_NAME, GPIOD_IN);
+ if (IS_ERR(gpiod)) {
+ error = PTR_ERR(gpiod);
+ if (error != -EPROBE_DEFER)
+ dev_dbg(dev, "Failed to get %s GPIO: %d\n",
+ GOODIX_GPIO_RST_NAME, error);
+ return error;
+ }
+
+ ts->gpiod_rst = gpiod;
+
+ return 0;
+}
+
/**
* goodix_read_config - Read the embedded configuration of the panel
*
@@ -230,14 +485,15 @@ static void goodix_read_config(struct goodix_ts_data *ts)
int error;
error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA,
- config,
- GOODIX_CONFIG_MAX_LENGTH);
+ config, ts->cfg_len);
if (error) {
dev_warn(&ts->client->dev,
"Error reading config (%d), using defaults\n",
error);
ts->abs_x_max = GOODIX_MAX_WIDTH;
ts->abs_y_max = GOODIX_MAX_HEIGHT;
+ if (ts->swapped_x_y)
+ swap(ts->abs_x_max, ts->abs_y_max);
ts->int_trigger_type = GOODIX_INT_TRIGGER;
ts->max_touch_num = GOODIX_MAX_CONTACTS;
return;
@@ -245,6 +501,8 @@ static void goodix_read_config(struct goodix_ts_data *ts)
ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]);
ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]);
+ if (ts->swapped_x_y)
+ swap(ts->abs_x_max, ts->abs_y_max);
ts->int_trigger_type = config[TRIGGER_LOC] & 0x03;
ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f;
if (!ts->abs_x_max || !ts->abs_y_max || !ts->max_touch_num) {
@@ -252,42 +510,45 @@ static void goodix_read_config(struct goodix_ts_data *ts)
"Invalid config, using defaults\n");
ts->abs_x_max = GOODIX_MAX_WIDTH;
ts->abs_y_max = GOODIX_MAX_HEIGHT;
+ if (ts->swapped_x_y)
+ swap(ts->abs_x_max, ts->abs_y_max);
ts->max_touch_num = GOODIX_MAX_CONTACTS;
}
- ts->rotated_screen = dmi_check_system(rotated_screen);
- if (ts->rotated_screen)
+ if (dmi_check_system(rotated_screen)) {
+ ts->inverted_x = true;
+ ts->inverted_y = true;
dev_dbg(&ts->client->dev,
"Applying '180 degrees rotated screen' quirk\n");
+ }
}
/**
* goodix_read_version - Read goodix touchscreen version
*
- * @client: the i2c client
- * @version: output buffer containing the version on success
- * @id: output buffer containing the id on success
+ * @ts: our goodix_ts_data pointer
*/
-static int goodix_read_version(struct i2c_client *client, u16 *version, u16 *id)
+static int goodix_read_version(struct goodix_ts_data *ts)
{
int error;
u8 buf[6];
char id_str[5];
- error = goodix_i2c_read(client, GOODIX_REG_ID, buf, sizeof(buf));
+ error = goodix_i2c_read(ts->client, GOODIX_REG_ID, buf, sizeof(buf));
if (error) {
- dev_err(&client->dev, "read version failed: %d\n", error);
+ dev_err(&ts->client->dev, "read version failed: %d\n", error);
return error;
}
memcpy(id_str, buf, 4);
id_str[4] = 0;
- if (kstrtou16(id_str, 10, id))
- *id = 0x1001;
+ if (kstrtou16(id_str, 10, &ts->id))
+ ts->id = 0x1001;
- *version = get_unaligned_le16(&buf[4]);
+ ts->version = get_unaligned_le16(&buf[4]);
- dev_info(&client->dev, "ID %d, version: %04x\n", *id, *version);
+ dev_info(&ts->client->dev, "ID %d, version: %04x\n", ts->id,
+ ts->version);
return 0;
}
@@ -321,13 +582,10 @@ static int goodix_i2c_test(struct i2c_client *client)
* goodix_request_input_dev - Allocate, populate and register the input device
*
* @ts: our goodix_ts_data pointer
- * @version: device firmware version
- * @id: device ID
*
* Must be called during probe
*/
-static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version,
- u16 id)
+static int goodix_request_input_dev(struct goodix_ts_data *ts)
{
int error;
@@ -351,8 +609,8 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version,
ts->input_dev->phys = "input/ts";
ts->input_dev->id.bustype = BUS_I2C;
ts->input_dev->id.vendor = 0x0416;
- ts->input_dev->id.product = id;
- ts->input_dev->id.version = version;
+ ts->input_dev->id.product = ts->id;
+ ts->input_dev->id.version = ts->version;
error = input_register_device(ts->input_dev);
if (error) {
@@ -364,13 +622,75 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version,
return 0;
}
+/**
+ * goodix_configure_dev - Finish device initialization
+ *
+ * @ts: our goodix_ts_data pointer
+ *
+ * Must be called from probe to finish initialization of the device.
+ * Contains the common initialization code for both devices that
+ * declare gpio pins and devices that do not. It is either called
+ * directly from probe or from request_firmware_wait callback.
+ */
+static int goodix_configure_dev(struct goodix_ts_data *ts)
+{
+ int error;
+
+ ts->swapped_x_y = device_property_read_bool(&ts->client->dev,
+ "touchscreen-swapped-x-y");
+ ts->inverted_x = device_property_read_bool(&ts->client->dev,
+ "touchscreen-inverted-x");
+ ts->inverted_y = device_property_read_bool(&ts->client->dev,
+ "touchscreen-inverted-y");
+
+ goodix_read_config(ts);
+
+ error = goodix_request_input_dev(ts);
+ if (error)
+ return error;
+
+ ts->irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
+ error = goodix_request_irq(ts);
+ if (error) {
+ dev_err(&ts->client->dev, "request IRQ failed: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+/**
+ * goodix_config_cb - Callback to finish device init
+ *
+ * @ts: our goodix_ts_data pointer
+ *
+ * request_firmware_wait callback that finishes
+ * initialization of the device.
+ */
+static void goodix_config_cb(const struct firmware *cfg, void *ctx)
+{
+ struct goodix_ts_data *ts = ctx;
+ int error;
+
+ if (cfg) {
+ /* send device configuration to the firmware */
+ error = goodix_send_cfg(ts, cfg);
+ if (error)
+ goto err_release_cfg;
+ }
+
+ goodix_configure_dev(ts);
+
+err_release_cfg:
+ release_firmware(cfg);
+ complete_all(&ts->firmware_loading_complete);
+}
+
static int goodix_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct goodix_ts_data *ts;
- unsigned long irq_flags;
int error;
- u16 version_info, id_info;
dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr);
@@ -385,6 +705,20 @@ static int goodix_ts_probe(struct i2c_client *client,
ts->client = client;
i2c_set_clientdata(client, ts);
+ init_completion(&ts->firmware_loading_complete);
+
+ error = goodix_get_gpio_config(ts);
+ if (error)
+ return error;
+
+ if (ts->gpiod_int && ts->gpiod_rst) {
+ /* reset the controller */
+ error = goodix_reset(ts);
+ if (error) {
+ dev_err(&client->dev, "Controller reset failed.\n");
+ return error;
+ }
+ }
error = goodix_i2c_test(client);
if (error) {
@@ -392,30 +726,125 @@ static int goodix_ts_probe(struct i2c_client *client,
return error;
}
- error = goodix_read_version(client, &version_info, &id_info);
+ error = goodix_read_version(ts);
if (error) {
dev_err(&client->dev, "Read version failed.\n");
return error;
}
- goodix_read_config(ts);
+ ts->cfg_len = goodix_get_cfg_len(ts->id);
+
+ if (ts->gpiod_int && ts->gpiod_rst) {
+ /* update device config */
+ ts->cfg_name = devm_kasprintf(&client->dev, GFP_KERNEL,
+ "goodix_%d_cfg.bin", ts->id);
+ if (!ts->cfg_name)
+ return -ENOMEM;
+
+ error = request_firmware_nowait(THIS_MODULE, true, ts->cfg_name,
+ &client->dev, GFP_KERNEL, ts,
+ goodix_config_cb);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to invoke firmware loader: %d\n",
+ error);
+ return error;
+ }
- error = goodix_request_input_dev(ts, version_info, id_info);
- if (error)
- return error;
+ return 0;
+ } else {
+ error = goodix_configure_dev(ts);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+static int goodix_ts_remove(struct i2c_client *client)
+{
+ struct goodix_ts_data *ts = i2c_get_clientdata(client);
+
+ if (ts->gpiod_int && ts->gpiod_rst)
+ wait_for_completion(&ts->firmware_loading_complete);
+
+ return 0;
+}
+
+static int __maybe_unused goodix_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct goodix_ts_data *ts = i2c_get_clientdata(client);
+ int error;
- irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
- error = devm_request_threaded_irq(&ts->client->dev, client->irq,
- NULL, goodix_ts_irq_handler,
- irq_flags, client->name, ts);
+ /* We need gpio pins to suspend/resume */
+ if (!ts->gpiod_int || !ts->gpiod_rst)
+ return 0;
+
+ wait_for_completion(&ts->firmware_loading_complete);
+
+ /* Free IRQ as IRQ pin is used as output in the suspend sequence */
+ goodix_free_irq(ts);
+
+ /* Output LOW on the INT pin for 5 ms */
+ error = gpiod_direction_output(ts->gpiod_int, 0);
if (error) {
- dev_err(&client->dev, "request IRQ failed: %d\n", error);
+ goodix_request_irq(ts);
return error;
}
+ usleep_range(5000, 6000);
+
+ error = goodix_i2c_write_u8(ts->client, GOODIX_REG_COMMAND,
+ GOODIX_CMD_SCREEN_OFF);
+ if (error) {
+ dev_err(&ts->client->dev, "Screen off command failed\n");
+ gpiod_direction_input(ts->gpiod_int);
+ goodix_request_irq(ts);
+ return -EAGAIN;
+ }
+
+ /*
+ * The datasheet specifies that the interval between sending screen-off
+ * command and wake-up should be longer than 58 ms. To avoid waking up
+ * sooner, delay 58ms here.
+ */
+ msleep(58);
+ return 0;
+}
+
+static int __maybe_unused goodix_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct goodix_ts_data *ts = i2c_get_clientdata(client);
+ int error;
+
+ if (!ts->gpiod_int || !ts->gpiod_rst)
+ return 0;
+
+ /*
+ * Exit sleep mode by outputting HIGH level to INT pin
+ * for 2ms~5ms.
+ */
+ error = gpiod_direction_output(ts->gpiod_int, 1);
+ if (error)
+ return error;
+
+ usleep_range(2000, 5000);
+
+ error = goodix_int_sync(ts);
+ if (error)
+ return error;
+
+ error = goodix_request_irq(ts);
+ if (error)
+ return error;
+
return 0;
}
+static SIMPLE_DEV_PM_OPS(goodix_pm_ops, goodix_suspend, goodix_resume);
+
static const struct i2c_device_id goodix_ts_id[] = {
{ "GDIX1001:00", 0 },
{ }
@@ -446,11 +875,13 @@ MODULE_DEVICE_TABLE(of, goodix_of_match);
static struct i2c_driver goodix_ts_driver = {
.probe = goodix_ts_probe,
+ .remove = goodix_ts_remove,
.id_table = goodix_ts_id,
.driver = {
.name = "Goodix-TS",
.acpi_match_table = ACPI_PTR(goodix_acpi_match),
.of_match_table = of_match_ptr(goodix_of_match),
+ .pm = &goodix_pm_ops,
},
};
module_i2c_driver(goodix_ts_driver);
diff --git a/drivers/input/touchscreen/pcap_ts.c b/drivers/input/touchscreen/pcap_ts.c
index 23a354a392ae..0e3fc419a3cf 100644
--- a/drivers/input/touchscreen/pcap_ts.c
+++ b/drivers/input/touchscreen/pcap_ts.c
@@ -87,7 +87,7 @@ static void pcap_ts_read_xy(void *data, u16 res[2])
static void pcap_ts_work(struct work_struct *work)
{
- struct delayed_work *dw = container_of(work, struct delayed_work, work);
+ struct delayed_work *dw = to_delayed_work(work);
struct pcap_ts *pcap_ts = container_of(dw, struct pcap_ts, work);
u8 ch[2];
diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
index 4b961ad9f0b5..09523a3d3f23 100644
--- a/drivers/input/touchscreen/pixcir_i2c_ts.c
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -38,6 +38,8 @@ struct pixcir_i2c_ts_data {
struct input_dev *input;
struct gpio_desc *gpio_attb;
struct gpio_desc *gpio_reset;
+ struct gpio_desc *gpio_enable;
+ struct gpio_desc *gpio_wake;
const struct pixcir_i2c_chip_data *chip;
int max_fingers; /* Max fingers supported in this instance */
bool running;
@@ -208,6 +210,11 @@ static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts,
struct device *dev = &ts->client->dev;
int ret;
+ if (mode == PIXCIR_POWER_ACTIVE || mode == PIXCIR_POWER_IDLE) {
+ if (ts->gpio_wake)
+ gpiod_set_value_cansleep(ts->gpio_wake, 1);
+ }
+
ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_POWER_MODE);
if (ret < 0) {
dev_err(dev, "%s: can't read reg 0x%x : %d\n",
@@ -228,6 +235,11 @@ static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts,
return ret;
}
+ if (mode == PIXCIR_POWER_HALT) {
+ if (ts->gpio_wake)
+ gpiod_set_value_cansleep(ts->gpio_wake, 0);
+ }
+
return 0;
}
@@ -302,6 +314,11 @@ static int pixcir_start(struct pixcir_i2c_ts_data *ts)
struct device *dev = &ts->client->dev;
int error;
+ if (ts->gpio_enable) {
+ gpiod_set_value_cansleep(ts->gpio_enable, 1);
+ msleep(100);
+ }
+
/* LEVEL_TOUCH interrupt with active low polarity */
error = pixcir_set_int_mode(ts, PIXCIR_INT_LEVEL_TOUCH, 0);
if (error) {
@@ -343,6 +360,9 @@ static int pixcir_stop(struct pixcir_i2c_ts_data *ts)
/* Wait till running ISR is complete */
synchronize_irq(ts->client->irq);
+ if (ts->gpio_enable)
+ gpiod_set_value_cansleep(ts->gpio_enable, 0);
+
return 0;
}
@@ -534,6 +554,27 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
return error;
}
+ tsdata->gpio_wake = devm_gpiod_get_optional(dev, "wake",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(tsdata->gpio_wake)) {
+ error = PTR_ERR(tsdata->gpio_wake);
+ if (error != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get wake gpio: %d\n", error);
+ return error;
+ }
+
+ tsdata->gpio_enable = devm_gpiod_get_optional(dev, "enable",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(tsdata->gpio_enable)) {
+ error = PTR_ERR(tsdata->gpio_enable);
+ if (error != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get enable gpio: %d\n", error);
+ return error;
+ }
+
+ if (tsdata->gpio_enable)
+ msleep(100);
+
error = devm_request_threaded_irq(dev, client->irq, NULL, pixcir_ts_isr,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
client->name, tsdata);
diff --git a/drivers/input/touchscreen/rohm_bu21023.c b/drivers/input/touchscreen/rohm_bu21023.c
index ba6024f93469..611156a2ef80 100644
--- a/drivers/input/touchscreen/rohm_bu21023.c
+++ b/drivers/input/touchscreen/rohm_bu21023.c
@@ -725,7 +725,7 @@ static int rohm_ts_load_firmware(struct i2c_client *client,
break;
error = -EIO;
- } while (++retry >= FIRMWARE_RETRY_MAX);
+ } while (++retry <= FIRMWARE_RETRY_MAX);
out:
error2 = i2c_smbus_write_byte_data(client, INT_MASK, INT_ALL);
diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c
index d214f22ed305..b6c4d03de340 100644
--- a/drivers/input/touchscreen/sur40.c
+++ b/drivers/input/touchscreen/sur40.c
@@ -444,7 +444,7 @@ static void sur40_process_video(struct sur40_state *sur40)
goto err_poll;
/* mark as finished */
- v4l2_get_timestamp(&new_buf->vb.timestamp);
+ new_buf->vb.vb2_buf.timestamp = ktime_get_ns();
new_buf->vb.sequence = sur40->sequence++;
new_buf->vb.field = V4L2_FIELD_NONE;
vb2_buffer_done(&new_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
@@ -644,22 +644,21 @@ static void sur40_disconnect(struct usb_interface *interface)
* minimum number: many DMA engines need a minimum of 2 buffers in the
* queue and you need to have another available for userspace processing.
*/
-static int sur40_queue_setup(struct vb2_queue *q, const void *parg,
+static int sur40_queue_setup(struct vb2_queue *q,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct sur40_state *sur40 = vb2_get_drv_priv(q);
if (q->num_buffers + *nbuffers < 3)
*nbuffers = 3 - q->num_buffers;
+ alloc_ctxs[0] = sur40->alloc_ctx;
- if (fmt && fmt->fmt.pix.sizeimage < sur40_video_format.sizeimage)
- return -EINVAL;
+ if (*nplanes)
+ return sizes[0] < sur40_video_format.sizeimage ? -EINVAL : 0;
*nplanes = 1;
- sizes[0] = fmt ? fmt->fmt.pix.sizeimage : sur40_video_format.sizeimage;
- alloc_ctxs[0] = sur40->alloc_ctx;
+ sizes[0] = sur40_video_format.sizeimage;
return 0;
}
diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
index 191a1b87895f..a21a07c3ab6d 100644
--- a/drivers/input/touchscreen/ti_am335x_tsc.c
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -273,8 +273,6 @@ static irqreturn_t titsc_irq(int irq, void *dev)
status = titsc_readl(ts_dev, REG_RAWIRQSTATUS);
if (status & IRQENB_HW_PEN) {
ts_dev->pen_down = true;
- titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00);
- titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
irqclr |= IRQENB_HW_PEN;
}
diff --git a/drivers/input/touchscreen/ts4800-ts.c b/drivers/input/touchscreen/ts4800-ts.c
new file mode 100644
index 000000000000..3c3dd78303be
--- /dev/null
+++ b/drivers/input/touchscreen/ts4800-ts.c
@@ -0,0 +1,216 @@
+/*
+ * Touchscreen driver for the TS-4800 board
+ *
+ * Copyright (c) 2015 - Savoir-faire Linux
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/bitops.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/* polling interval in ms */
+#define POLL_INTERVAL 3
+
+#define DEBOUNCE_COUNT 1
+
+/* sensor values are 12-bit wide */
+#define MAX_12BIT ((1 << 12) - 1)
+
+#define PENDOWN_MASK 0x1
+
+#define X_OFFSET 0x0
+#define Y_OFFSET 0x2
+
+struct ts4800_ts {
+ struct input_polled_dev *poll_dev;
+ struct device *dev;
+ char phys[32];
+
+ void __iomem *base;
+ struct regmap *regmap;
+ unsigned int reg;
+ unsigned int bit;
+
+ bool pendown;
+ int debounce;
+};
+
+static void ts4800_ts_open(struct input_polled_dev *dev)
+{
+ struct ts4800_ts *ts = dev->private;
+ int ret;
+
+ ts->pendown = false;
+ ts->debounce = DEBOUNCE_COUNT;
+
+ ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, ts->bit);
+ if (ret)
+ dev_warn(ts->dev, "Failed to enable touchscreen\n");
+}
+
+static void ts4800_ts_close(struct input_polled_dev *dev)
+{
+ struct ts4800_ts *ts = dev->private;
+ int ret;
+
+ ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, 0);
+ if (ret)
+ dev_warn(ts->dev, "Failed to disable touchscreen\n");
+
+}
+
+static void ts4800_ts_poll(struct input_polled_dev *dev)
+{
+ struct input_dev *input_dev = dev->input;
+ struct ts4800_ts *ts = dev->private;
+ u16 last_x = readw(ts->base + X_OFFSET);
+ u16 last_y = readw(ts->base + Y_OFFSET);
+ bool pendown = last_x & PENDOWN_MASK;
+
+ if (pendown) {
+ if (ts->debounce) {
+ ts->debounce--;
+ return;
+ }
+
+ if (!ts->pendown) {
+ input_report_key(input_dev, BTN_TOUCH, 1);
+ ts->pendown = true;
+ }
+
+ last_x = ((~last_x) >> 4) & MAX_12BIT;
+ last_y = ((~last_y) >> 4) & MAX_12BIT;
+
+ input_report_abs(input_dev, ABS_X, last_x);
+ input_report_abs(input_dev, ABS_Y, last_y);
+ input_sync(input_dev);
+ } else if (ts->pendown) {
+ ts->pendown = false;
+ ts->debounce = DEBOUNCE_COUNT;
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_sync(input_dev);
+ }
+}
+
+static int ts4800_parse_dt(struct platform_device *pdev,
+ struct ts4800_ts *ts)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct device_node *syscon_np;
+ u32 reg, bit;
+ int error;
+
+ syscon_np = of_parse_phandle(np, "syscon", 0);
+ if (!syscon_np) {
+ dev_err(dev, "no syscon property\n");
+ return -ENODEV;
+ }
+
+ error = of_property_read_u32_index(np, "syscon", 1, &reg);
+ if (error < 0) {
+ dev_err(dev, "no offset in syscon\n");
+ return error;
+ }
+
+ ts->reg = reg;
+
+ error = of_property_read_u32_index(np, "syscon", 2, &bit);
+ if (error < 0) {
+ dev_err(dev, "no bit in syscon\n");
+ return error;
+ }
+
+ ts->bit = BIT(bit);
+
+ ts->regmap = syscon_node_to_regmap(syscon_np);
+ if (IS_ERR(ts->regmap)) {
+ dev_err(dev, "cannot get parent's regmap\n");
+ return PTR_ERR(ts->regmap);
+ }
+
+ return 0;
+}
+
+static int ts4800_ts_probe(struct platform_device *pdev)
+{
+ struct input_polled_dev *poll_dev;
+ struct ts4800_ts *ts;
+ struct resource *res;
+ int error;
+
+ ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ error = ts4800_parse_dt(pdev, ts);
+ if (error)
+ return error;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ts->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ts->base))
+ return PTR_ERR(ts->base);
+
+ poll_dev = devm_input_allocate_polled_device(&pdev->dev);
+ if (!poll_dev)
+ return -ENOMEM;
+
+ snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&pdev->dev));
+ ts->poll_dev = poll_dev;
+ ts->dev = &pdev->dev;
+
+ poll_dev->private = ts;
+ poll_dev->poll_interval = POLL_INTERVAL;
+ poll_dev->open = ts4800_ts_open;
+ poll_dev->close = ts4800_ts_close;
+ poll_dev->poll = ts4800_ts_poll;
+
+ poll_dev->input->name = "TS-4800 Touchscreen";
+ poll_dev->input->phys = ts->phys;
+
+ input_set_capability(poll_dev->input, EV_KEY, BTN_TOUCH);
+ input_set_abs_params(poll_dev->input, ABS_X, 0, MAX_12BIT, 0, 0);
+ input_set_abs_params(poll_dev->input, ABS_Y, 0, MAX_12BIT, 0, 0);
+
+ error = input_register_polled_device(poll_dev);
+ if (error) {
+ dev_err(&pdev->dev,
+ "Unabled to register polled input device (%d)\n",
+ error);
+ return error;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id ts4800_ts_of_match[] = {
+ { .compatible = "technologic,ts4800-ts", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ts4800_ts_of_match);
+
+static struct platform_driver ts4800_ts_driver = {
+ .driver = {
+ .name = "ts4800-ts",
+ .of_match_table = ts4800_ts_of_match,
+ },
+ .probe = ts4800_ts_probe,
+};
+module_platform_driver(ts4800_ts_driver);
+
+MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>");
+MODULE_DESCRIPTION("TS-4800 Touchscreen Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:ts4800_ts");
diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c
index 2792ca397dd0..bab3c6acf6a2 100644
--- a/drivers/input/touchscreen/wacom_w8001.c
+++ b/drivers/input/touchscreen/wacom_w8001.c
@@ -80,7 +80,8 @@ struct w8001_touch_query {
*/
struct w8001 {
- struct input_dev *dev;
+ struct input_dev *pen_dev;
+ struct input_dev *touch_dev;
struct serio *serio;
struct completion cmd_done;
int id;
@@ -95,7 +96,10 @@ struct w8001 {
u16 max_touch_y;
u16 max_pen_x;
u16 max_pen_y;
- char name[64];
+ char pen_name[64];
+ char touch_name[64];
+ int open_count;
+ struct mutex mutex;
};
static void parse_pen_data(u8 *data, struct w8001_coord *coord)
@@ -141,7 +145,7 @@ static void scale_touch_coordinates(struct w8001 *w8001,
static void parse_multi_touch(struct w8001 *w8001)
{
- struct input_dev *dev = w8001->dev;
+ struct input_dev *dev = w8001->touch_dev;
unsigned char *data = w8001->data;
unsigned int x, y;
int i;
@@ -151,7 +155,6 @@ static void parse_multi_touch(struct w8001 *w8001)
bool touch = data[0] & (1 << i);
input_mt_slot(dev, i);
- input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch);
if (touch) {
x = (data[6 * i + 1] << 7) | data[6 * i + 2];
y = (data[6 * i + 3] << 7) | data[6 * i + 4];
@@ -207,7 +210,7 @@ static void parse_touchquery(u8 *data, struct w8001_touch_query *query)
static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
{
- struct input_dev *dev = w8001->dev;
+ struct input_dev *dev = w8001->pen_dev;
/*
* We have 1 bit for proximity (rdy) and 3 bits for tip, side,
@@ -233,11 +236,6 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
break;
case BTN_TOOL_FINGER:
- input_report_key(dev, BTN_TOUCH, 0);
- input_report_key(dev, BTN_TOOL_FINGER, 0);
- input_sync(dev);
- /* fall through */
-
case KEY_RESERVED:
w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
break;
@@ -261,7 +259,7 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord)
{
- struct input_dev *dev = w8001->dev;
+ struct input_dev *dev = w8001->touch_dev;
unsigned int x = coord->x;
unsigned int y = coord->y;
@@ -271,7 +269,6 @@ static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord)
input_report_abs(dev, ABS_X, x);
input_report_abs(dev, ABS_Y, y);
input_report_key(dev, BTN_TOUCH, coord->tsw);
- input_report_key(dev, BTN_TOOL_FINGER, coord->tsw);
input_sync(dev);
@@ -369,22 +366,36 @@ static int w8001_command(struct w8001 *w8001, unsigned char command,
static int w8001_open(struct input_dev *dev)
{
struct w8001 *w8001 = input_get_drvdata(dev);
+ int err;
- return w8001_command(w8001, W8001_CMD_START, false);
+ err = mutex_lock_interruptible(&w8001->mutex);
+ if (err)
+ return err;
+
+ if (w8001->open_count++ == 0) {
+ err = w8001_command(w8001, W8001_CMD_START, false);
+ if (err)
+ w8001->open_count--;
+ }
+
+ mutex_unlock(&w8001->mutex);
+ return err;
}
static void w8001_close(struct input_dev *dev)
{
struct w8001 *w8001 = input_get_drvdata(dev);
- w8001_command(w8001, W8001_CMD_STOP, false);
+ mutex_lock(&w8001->mutex);
+
+ if (--w8001->open_count == 0)
+ w8001_command(w8001, W8001_CMD_STOP, false);
+
+ mutex_unlock(&w8001->mutex);
}
-static int w8001_setup(struct w8001 *w8001)
+static int w8001_detect(struct w8001 *w8001)
{
- struct input_dev *dev = w8001->dev;
- struct w8001_coord coord;
- struct w8001_touch_query touch;
int error;
error = w8001_command(w8001, W8001_CMD_STOP, false);
@@ -393,105 +404,145 @@ static int w8001_setup(struct w8001 *w8001)
msleep(250); /* wait 250ms before querying the device */
- dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name));
+ return 0;
+}
- __set_bit(INPUT_PROP_DIRECT, dev->propbit);
+static int w8001_setup_pen(struct w8001 *w8001, char *basename,
+ size_t basename_sz)
+{
+ struct input_dev *dev = w8001->pen_dev;
+ struct w8001_coord coord;
+ int error;
/* penabled? */
error = w8001_command(w8001, W8001_CMD_QUERY, true);
- if (!error) {
- __set_bit(BTN_TOUCH, dev->keybit);
- __set_bit(BTN_TOOL_PEN, dev->keybit);
- __set_bit(BTN_TOOL_RUBBER, dev->keybit);
- __set_bit(BTN_STYLUS, dev->keybit);
- __set_bit(BTN_STYLUS2, dev->keybit);
-
- parse_pen_data(w8001->response, &coord);
- w8001->max_pen_x = coord.x;
- w8001->max_pen_y = coord.y;
-
- input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
- input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
- input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION);
- input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION);
- input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0);
- if (coord.tilt_x && coord.tilt_y) {
- input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
- input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
- }
- w8001->id = 0x90;
- strlcat(w8001->name, " Penabled", sizeof(w8001->name));
+ if (error)
+ return error;
+
+ __set_bit(EV_KEY, dev->evbit);
+ __set_bit(EV_ABS, dev->evbit);
+ __set_bit(BTN_TOUCH, dev->keybit);
+ __set_bit(BTN_TOOL_PEN, dev->keybit);
+ __set_bit(BTN_TOOL_RUBBER, dev->keybit);
+ __set_bit(BTN_STYLUS, dev->keybit);
+ __set_bit(BTN_STYLUS2, dev->keybit);
+ __set_bit(INPUT_PROP_DIRECT, dev->propbit);
+
+ parse_pen_data(w8001->response, &coord);
+ w8001->max_pen_x = coord.x;
+ w8001->max_pen_y = coord.y;
+
+ input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
+ input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION);
+ input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION);
+ input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0);
+ if (coord.tilt_x && coord.tilt_y) {
+ input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
+ input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
}
+ w8001->id = 0x90;
+ strlcat(basename, " Penabled", basename_sz);
+
+ return 0;
+}
+
+static int w8001_setup_touch(struct w8001 *w8001, char *basename,
+ size_t basename_sz)
+{
+ struct input_dev *dev = w8001->touch_dev;
+ struct w8001_touch_query touch;
+ int error;
+
+
/* Touch enabled? */
error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true);
-
+ if (error)
+ return error;
/*
* Some non-touch devices may reply to the touch query. But their
* second byte is empty, which indicates touch is not supported.
*/
- if (!error && w8001->response[1]) {
- __set_bit(BTN_TOUCH, dev->keybit);
- __set_bit(BTN_TOOL_FINGER, dev->keybit);
-
- parse_touchquery(w8001->response, &touch);
- w8001->max_touch_x = touch.x;
- w8001->max_touch_y = touch.y;
-
- if (w8001->max_pen_x && w8001->max_pen_y) {
- /* if pen is supported scale to pen maximum */
- touch.x = w8001->max_pen_x;
- touch.y = w8001->max_pen_y;
- touch.panel_res = W8001_PEN_RESOLUTION;
- }
+ if (!w8001->response[1])
+ return -ENXIO;
- input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0);
- input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0);
- input_abs_set_res(dev, ABS_X, touch.panel_res);
- input_abs_set_res(dev, ABS_Y, touch.panel_res);
-
- switch (touch.sensor_id) {
- case 0:
- case 2:
- w8001->pktlen = W8001_PKTLEN_TOUCH93;
- w8001->id = 0x93;
- strlcat(w8001->name, " 1FG", sizeof(w8001->name));
- break;
+ __set_bit(EV_KEY, dev->evbit);
+ __set_bit(EV_ABS, dev->evbit);
+ __set_bit(BTN_TOUCH, dev->keybit);
+ __set_bit(INPUT_PROP_DIRECT, dev->propbit);
- case 1:
- case 3:
- case 4:
- w8001->pktlen = W8001_PKTLEN_TOUCH9A;
- strlcat(w8001->name, " 1FG", sizeof(w8001->name));
- w8001->id = 0x9a;
- break;
+ parse_touchquery(w8001->response, &touch);
+ w8001->max_touch_x = touch.x;
+ w8001->max_touch_y = touch.y;
- case 5:
- w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
-
- input_mt_init_slots(dev, 2, 0);
- input_set_abs_params(dev, ABS_MT_POSITION_X,
- 0, touch.x, 0, 0);
- input_set_abs_params(dev, ABS_MT_POSITION_Y,
- 0, touch.y, 0, 0);
- input_set_abs_params(dev, ABS_MT_TOOL_TYPE,
- 0, MT_TOOL_MAX, 0, 0);
-
- strlcat(w8001->name, " 2FG", sizeof(w8001->name));
- if (w8001->max_pen_x && w8001->max_pen_y)
- w8001->id = 0xE3;
- else
- w8001->id = 0xE2;
- break;
- }
+ if (w8001->max_pen_x && w8001->max_pen_y) {
+ /* if pen is supported scale to pen maximum */
+ touch.x = w8001->max_pen_x;
+ touch.y = w8001->max_pen_y;
+ touch.panel_res = W8001_PEN_RESOLUTION;
+ }
+
+ input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0);
+ input_abs_set_res(dev, ABS_X, touch.panel_res);
+ input_abs_set_res(dev, ABS_Y, touch.panel_res);
+
+ switch (touch.sensor_id) {
+ case 0:
+ case 2:
+ w8001->pktlen = W8001_PKTLEN_TOUCH93;
+ w8001->id = 0x93;
+ strlcat(basename, " 1FG", basename_sz);
+ break;
+
+ case 1:
+ case 3:
+ case 4:
+ w8001->pktlen = W8001_PKTLEN_TOUCH9A;
+ strlcat(basename, " 1FG", basename_sz);
+ w8001->id = 0x9a;
+ break;
+
+ case 5:
+ w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
+
+ __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+ input_mt_init_slots(dev, 2, 0);
+ input_set_abs_params(dev, ABS_MT_POSITION_X,
+ 0, touch.x, 0, 0);
+ input_set_abs_params(dev, ABS_MT_POSITION_Y,
+ 0, touch.y, 0, 0);
+
+ strlcat(basename, " 2FG", basename_sz);
+ if (w8001->max_pen_x && w8001->max_pen_y)
+ w8001->id = 0xE3;
+ else
+ w8001->id = 0xE2;
+ break;
}
- strlcat(w8001->name, " Touchscreen", sizeof(w8001->name));
+ strlcat(basename, " Touchscreen", basename_sz);
return 0;
}
+static void w8001_set_devdata(struct input_dev *dev, struct w8001 *w8001,
+ struct serio *serio)
+{
+ dev->phys = w8001->phys;
+ dev->id.bustype = BUS_RS232;
+ dev->id.product = w8001->id;
+ dev->id.vendor = 0x056a;
+ dev->id.version = 0x0100;
+ dev->open = w8001_open;
+ dev->close = w8001_close;
+
+ dev->dev.parent = &serio->dev;
+
+ input_set_drvdata(dev, w8001);
+}
+
/*
* w8001_disconnect() is the opposite of w8001_connect()
*/
@@ -502,7 +553,10 @@ static void w8001_disconnect(struct serio *serio)
serio_close(serio);
- input_unregister_device(w8001->dev);
+ if (w8001->pen_dev)
+ input_unregister_device(w8001->pen_dev);
+ if (w8001->touch_dev)
+ input_unregister_device(w8001->touch_dev);
kfree(w8001);
serio_set_drvdata(serio, NULL);
@@ -517,18 +571,23 @@ static void w8001_disconnect(struct serio *serio)
static int w8001_connect(struct serio *serio, struct serio_driver *drv)
{
struct w8001 *w8001;
- struct input_dev *input_dev;
- int err;
+ struct input_dev *input_dev_pen;
+ struct input_dev *input_dev_touch;
+ char basename[64];
+ int err, err_pen, err_touch;
w8001 = kzalloc(sizeof(struct w8001), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!w8001 || !input_dev) {
+ input_dev_pen = input_allocate_device();
+ input_dev_touch = input_allocate_device();
+ if (!w8001 || !input_dev_pen || !input_dev_touch) {
err = -ENOMEM;
goto fail1;
}
w8001->serio = serio;
- w8001->dev = input_dev;
+ w8001->pen_dev = input_dev_pen;
+ w8001->touch_dev = input_dev_touch;
+ mutex_init(&w8001->mutex);
init_completion(&w8001->cmd_done);
snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys);
@@ -537,35 +596,67 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
if (err)
goto fail2;
- err = w8001_setup(w8001);
+ err = w8001_detect(w8001);
if (err)
goto fail3;
- input_dev->name = w8001->name;
- input_dev->phys = w8001->phys;
- input_dev->id.product = w8001->id;
- input_dev->id.bustype = BUS_RS232;
- input_dev->id.vendor = 0x056a;
- input_dev->id.version = 0x0100;
- input_dev->dev.parent = &serio->dev;
+ /* For backwards-compatibility we compose the basename based on
+ * capabilities and then just append the tool type
+ */
+ strlcpy(basename, "Wacom Serial", sizeof(basename));
+
+ err_pen = w8001_setup_pen(w8001, basename, sizeof(basename));
+ err_touch = w8001_setup_touch(w8001, basename, sizeof(basename));
+ if (err_pen && err_touch) {
+ err = -ENXIO;
+ goto fail3;
+ }
+
+ if (!err_pen) {
+ strlcpy(w8001->pen_name, basename, sizeof(w8001->pen_name));
+ strlcat(w8001->pen_name, " Pen", sizeof(w8001->pen_name));
+ input_dev_pen->name = w8001->pen_name;
- input_dev->open = w8001_open;
- input_dev->close = w8001_close;
+ w8001_set_devdata(input_dev_pen, w8001, serio);
- input_set_drvdata(input_dev, w8001);
+ err = input_register_device(w8001->pen_dev);
+ if (err)
+ goto fail3;
+ } else {
+ input_free_device(input_dev_pen);
+ input_dev_pen = NULL;
+ w8001->pen_dev = NULL;
+ }
- err = input_register_device(w8001->dev);
- if (err)
- goto fail3;
+ if (!err_touch) {
+ strlcpy(w8001->touch_name, basename, sizeof(w8001->touch_name));
+ strlcat(w8001->touch_name, " Finger",
+ sizeof(w8001->touch_name));
+ input_dev_touch->name = w8001->touch_name;
+
+ w8001_set_devdata(input_dev_touch, w8001, serio);
+
+ err = input_register_device(w8001->touch_dev);
+ if (err)
+ goto fail4;
+ } else {
+ input_free_device(input_dev_touch);
+ input_dev_touch = NULL;
+ w8001->touch_dev = NULL;
+ }
return 0;
+fail4:
+ if (w8001->pen_dev)
+ input_unregister_device(w8001->pen_dev);
fail3:
serio_close(serio);
fail2:
serio_set_drvdata(serio, NULL);
fail1:
- input_free_device(input_dev);
+ input_free_device(input_dev_pen);
+ input_free_device(input_dev_touch);
kfree(w8001);
return err;
}
diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c
index d21d4edf7236..7caf2fa237f2 100644
--- a/drivers/iommu/amd_iommu_v2.c
+++ b/drivers/iommu/amd_iommu_v2.c
@@ -494,6 +494,22 @@ static void handle_fault_error(struct fault *fault)
}
}
+static bool access_error(struct vm_area_struct *vma, struct fault *fault)
+{
+ unsigned long requested = 0;
+
+ if (fault->flags & PPR_FAULT_EXEC)
+ requested |= VM_EXEC;
+
+ if (fault->flags & PPR_FAULT_READ)
+ requested |= VM_READ;
+
+ if (fault->flags & PPR_FAULT_WRITE)
+ requested |= VM_WRITE;
+
+ return (requested & ~vma->vm_flags) != 0;
+}
+
static void do_fault(struct work_struct *work)
{
struct fault *fault = container_of(work, struct fault, work);
@@ -516,8 +532,8 @@ static void do_fault(struct work_struct *work)
goto out;
}
- if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))) {
- /* handle_mm_fault would BUG_ON() */
+ /* Check if we have the right permissions on the vma */
+ if (access_error(vma, fault)) {
up_read(&mm->mmap_sem);
handle_fault_error(fault);
goto out;
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 3a20db4f8604..72d6182666cb 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -21,10 +21,13 @@
#include <linux/device.h>
#include <linux/dma-iommu.h>
+#include <linux/gfp.h>
#include <linux/huge_mm.h>
#include <linux/iommu.h>
#include <linux/iova.h>
#include <linux/mm.h>
+#include <linux/scatterlist.h>
+#include <linux/vmalloc.h>
int iommu_dma_init(void)
{
@@ -191,6 +194,7 @@ static struct page **__iommu_dma_alloc_pages(unsigned int count, gfp_t gfp)
{
struct page **pages;
unsigned int i = 0, array_size = count * sizeof(*pages);
+ unsigned int order = MAX_ORDER;
if (array_size <= PAGE_SIZE)
pages = kzalloc(array_size, GFP_KERNEL);
@@ -204,14 +208,15 @@ static struct page **__iommu_dma_alloc_pages(unsigned int count, gfp_t gfp)
while (count) {
struct page *page = NULL;
- int j, order = __fls(count);
+ int j;
/*
* Higher-order allocations are a convenience rather
* than a necessity, hence using __GFP_NORETRY until
* falling back to single-page allocations.
*/
- for (order = min(order, MAX_ORDER); order > 0; order--) {
+ for (order = min_t(unsigned int, order, __fls(count));
+ order > 0; order--) {
page = alloc_pages(gfp | __GFP_NORETRY, order);
if (!page)
continue;
@@ -453,7 +458,7 @@ int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
size_t s_offset = iova_offset(iovad, s->offset);
size_t s_length = s->length;
- sg_dma_address(s) = s->offset;
+ sg_dma_address(s) = s_offset;
sg_dma_len(s) = s_length;
s->offset -= s_offset;
s_length = iova_align(iovad, s_length + s_offset);
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index f1042daef9ad..ac7387686ddc 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -2159,7 +2159,7 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
sg_res = aligned_nrpages(sg->offset, sg->length);
sg->dma_address = ((dma_addr_t)iov_pfn << VTD_PAGE_SHIFT) + sg->offset;
sg->dma_length = sg->length;
- pteval = (sg_phys(sg) & PAGE_MASK) | prot;
+ pteval = page_to_phys(sg_page(sg)) | prot;
phys_pfn = pteval >> VTD_PAGE_SHIFT;
}
@@ -3704,7 +3704,7 @@ static int intel_nontranslate_map_sg(struct device *hddev,
for_each_sg(sglist, sg, nelems, i) {
BUG_ON(!sg_page(sg));
- sg->dma_address = sg_phys(sg);
+ sg->dma_address = page_to_phys(sg_page(sg)) + sg->offset;
sg->dma_length = sg->length;
}
return nelems;
diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c
index c69e3f9ec958..50464833d0b8 100644
--- a/drivers/iommu/intel-svm.c
+++ b/drivers/iommu/intel-svm.c
@@ -484,6 +484,23 @@ struct page_req_dsc {
};
#define PRQ_RING_MASK ((0x1000 << PRQ_ORDER) - 0x10)
+
+static bool access_error(struct vm_area_struct *vma, struct page_req_dsc *req)
+{
+ unsigned long requested = 0;
+
+ if (req->exe_req)
+ requested |= VM_EXEC;
+
+ if (req->rd_req)
+ requested |= VM_READ;
+
+ if (req->wr_req)
+ requested |= VM_WRITE;
+
+ return (requested & ~vma->vm_flags) != 0;
+}
+
static irqreturn_t prq_event_thread(int irq, void *d)
{
struct intel_iommu *iommu = d;
@@ -539,6 +556,9 @@ static irqreturn_t prq_event_thread(int irq, void *d)
if (!vma || address < vma->vm_start)
goto invalid;
+ if (access_error(vma, req))
+ goto invalid;
+
ret = handle_mm_fault(svm->mm, vma, address,
req->wr_req ? FAULT_FLAG_WRITE : 0);
if (ret & VM_FAULT_ERROR)
diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c
index 1fae1881648c..c12ba4516df2 100644
--- a/drivers/iommu/intel_irq_remapping.c
+++ b/drivers/iommu/intel_irq_remapping.c
@@ -753,7 +753,7 @@ static inline void set_irq_posting_cap(void)
* should have X86_FEATURE_CX16 support, this has been confirmed
* with Intel hardware guys.
*/
- if ( cpu_has_cx16 )
+ if (boot_cpu_has(X86_FEATURE_CX16))
intel_irq_remap_ops.capability |= 1 << IRQ_POSTING_CAP;
for_each_iommu(iommu, drhd)
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index abae363c7b9b..0e3b0092ec92 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -1430,7 +1430,7 @@ size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
min_pagesz = 1 << __ffs(domain->ops->pgsize_bitmap);
for_each_sg(sg, s, nents, i) {
- phys_addr_t phys = sg_phys(s);
+ phys_addr_t phys = page_to_phys(sg_page(s)) + s->offset;
/*
* We are mapping on IOMMU page boundaries, so offset within
diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c
index 8cf605fa9946..dfb868e2d129 100644
--- a/drivers/iommu/ipmmu-vmsa.c
+++ b/drivers/iommu/ipmmu-vmsa.c
@@ -295,7 +295,7 @@ static struct iommu_gather_ops ipmmu_gather_ops = {
static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain)
{
- phys_addr_t ttbr;
+ u64 ttbr;
/*
* Allocate the page table operations.
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 4d7294e5d982..11fc2a27fa2e 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -8,6 +8,11 @@ config ARM_GIC
select IRQ_DOMAIN_HIERARCHY
select MULTI_IRQ_HANDLER
+config ARM_GIC_MAX_NR
+ int
+ default 2 if ARCH_REALVIEW
+ default 1
+
config ARM_GIC_V2M
bool
depends on ARM_GIC
@@ -27,6 +32,14 @@ config ARM_GIC_V3_ITS
bool
select PCI_MSI_IRQ_DOMAIN
+config HISILICON_IRQ_MBIGEN
+ bool "Support mbigen interrupt controller"
+ default n
+ depends on ARM_GIC_V3 && ARM_GIC_V3_ITS && GENERIC_MSI_IRQ_DOMAIN
+ help
+ Enable the mbigen interrupt controller used on
+ Hisilicon platform.
+
config ARM_NVIC
bool
select IRQ_DOMAIN
@@ -138,6 +151,12 @@ config TB10X_IRQC
select IRQ_DOMAIN
select GENERIC_IRQ_CHIP
+config TS4800_IRQ
+ tristate "TS-4800 IRQ controller"
+ select IRQ_DOMAIN
+ help
+ Support for the TS-4800 FPGA IRQ controller
+
config VERSATILE_FPGA_IRQ
bool
select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 177f78f6e6d6..d4c2e4ebc308 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -21,9 +21,11 @@ obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o
obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o
obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o
+obj-$(CONFIG_REALVIEW_DT) += irq-gic-realview.o
obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o
obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o
obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v3-its-pci-msi.o irq-gic-v3-its-platform-msi.o
+obj-$(CONFIG_HISILICON_IRQ_MBIGEN) += irq-mbigen.o
obj-$(CONFIG_ARM_NVIC) += irq-nvic.o
obj-$(CONFIG_ARM_VIC) += irq-vic.o
obj-$(CONFIG_ATMEL_AIC_IRQ) += irq-atmel-aic-common.o irq-atmel-aic.o
@@ -39,6 +41,7 @@ obj-$(CONFIG_ARCH_NSPIRE) += irq-zevio.o
obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o
obj-$(CONFIG_ST_IRQCHIP) += irq-st.o
obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o
+obj-$(CONFIG_TS4800_IRQ) += irq-ts4800.o
obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o
obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o
obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o
diff --git a/drivers/irqchip/irq-bcm2836.c b/drivers/irqchip/irq-bcm2836.c
index f68708281fcf..963065a0d774 100644
--- a/drivers/irqchip/irq-bcm2836.c
+++ b/drivers/irqchip/irq-bcm2836.c
@@ -21,6 +21,9 @@
#include <linux/irqdomain.h>
#include <asm/exception.h>
+#define LOCAL_CONTROL 0x000
+#define LOCAL_PRESCALER 0x008
+
/*
* The low 2 bits identify the CPU that the GPU IRQ goes to, and the
* next 2 bits identify the CPU that the GPU FIQ goes to.
@@ -50,14 +53,16 @@
/* Same status bits as above, but for FIQ. */
#define LOCAL_FIQ_PENDING0 0x070
/*
- * Mailbox0 write-to-set bits. There are 16 mailboxes, 4 per CPU, and
+ * Mailbox write-to-set bits. There are 16 mailboxes, 4 per CPU, and
* these bits are organized by mailbox number and then CPU number. We
* use mailbox 0 for IPIs. The mailbox's interrupt is raised while
* any bit is set.
*/
#define LOCAL_MAILBOX0_SET0 0x080
-/* Mailbox0 write-to-clear bits. */
+#define LOCAL_MAILBOX3_SET0 0x08c
+/* Mailbox write-to-clear bits. */
#define LOCAL_MAILBOX0_CLR0 0x0c0
+#define LOCAL_MAILBOX3_CLR0 0x0cc
#define LOCAL_IRQ_CNTPSIRQ 0
#define LOCAL_IRQ_CNTPNSIRQ 1
@@ -162,7 +167,7 @@ __exception_irq_entry bcm2836_arm_irqchip_handle_irq(struct pt_regs *regs)
u32 stat;
stat = readl_relaxed(intc.base + LOCAL_IRQ_PENDING0 + 4 * cpu);
- if (stat & 0x10) {
+ if (stat & BIT(LOCAL_IRQ_MAILBOX0)) {
#ifdef CONFIG_SMP
void __iomem *mailbox0 = (intc.base +
LOCAL_MAILBOX0_CLR0 + 16 * cpu);
@@ -172,7 +177,7 @@ __exception_irq_entry bcm2836_arm_irqchip_handle_irq(struct pt_regs *regs)
writel(1 << ipi, mailbox0);
handle_IPI(ipi, regs);
#endif
- } else {
+ } else if (stat) {
u32 hwirq = ffs(stat) - 1;
handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs);
@@ -217,6 +222,24 @@ static struct notifier_block bcm2836_arm_irqchip_cpu_notifier = {
.notifier_call = bcm2836_arm_irqchip_cpu_notify,
.priority = 100,
};
+
+int __init bcm2836_smp_boot_secondary(unsigned int cpu,
+ struct task_struct *idle)
+{
+ unsigned long secondary_startup_phys =
+ (unsigned long)virt_to_phys((void *)secondary_startup);
+
+ dsb();
+ writel(secondary_startup_phys,
+ intc.base + LOCAL_MAILBOX3_SET0 + 16 * cpu);
+
+ return 0;
+}
+
+static const struct smp_operations bcm2836_smp_ops __initconst = {
+ .smp_boot_secondary = bcm2836_smp_boot_secondary,
+};
+
#endif
static const struct irq_domain_ops bcm2836_arm_irqchip_intc_ops = {
@@ -234,9 +257,31 @@ bcm2836_arm_irqchip_smp_init(void)
register_cpu_notifier(&bcm2836_arm_irqchip_cpu_notifier);
set_smp_cross_call(bcm2836_arm_irqchip_send_ipi);
+ smp_set_ops(&bcm2836_smp_ops);
#endif
}
+/*
+ * The LOCAL_IRQ_CNT* timer firings are based off of the external
+ * oscillator with some scaling. The firmware sets up CNTFRQ to
+ * report 19.2Mhz, but doesn't set up the scaling registers.
+ */
+static void bcm2835_init_local_timer_frequency(void)
+{
+ /*
+ * Set the timer to source from the 19.2Mhz crystal clock (bit
+ * 8 unset), and only increment by 1 instead of 2 (bit 9
+ * unset).
+ */
+ writel(0, intc.base + LOCAL_CONTROL);
+
+ /*
+ * Set the timer prescaler to 1:1 (timer freq = input freq *
+ * 2**31 / prescaler)
+ */
+ writel(0x80000000, intc.base + LOCAL_PRESCALER);
+}
+
static int __init bcm2836_arm_irqchip_l1_intc_of_init(struct device_node *node,
struct device_node *parent)
{
@@ -246,6 +291,8 @@ static int __init bcm2836_arm_irqchip_l1_intc_of_init(struct device_node *node,
node->full_name);
}
+ bcm2835_init_local_timer_frequency();
+
intc.domain = irq_domain_add_linear(node, LAST_IRQ + 1,
&bcm2836_arm_irqchip_intc_ops,
NULL);
diff --git a/drivers/irqchip/irq-gic-realview.c b/drivers/irqchip/irq-gic-realview.c
new file mode 100644
index 000000000000..aa46eb280a7f
--- /dev/null
+++ b/drivers/irqchip/irq-gic-realview.c
@@ -0,0 +1,43 @@
+/*
+ * Special GIC quirks for the ARM RealView
+ * Copyright (C) 2015 Linus Walleij
+ */
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/bitops.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/arm-gic.h>
+
+#define REALVIEW_SYS_LOCK_OFFSET 0x20
+#define REALVIEW_PB11MP_SYS_PLD_CTRL1 0x74
+#define VERSATILE_LOCK_VAL 0xA05F
+#define PLD_INTMODE_MASK BIT(22)|BIT(23)|BIT(24)
+#define PLD_INTMODE_LEGACY 0x0
+#define PLD_INTMODE_NEW_DCC BIT(22)
+#define PLD_INTMODE_NEW_NO_DCC BIT(23)
+#define PLD_INTMODE_FIQ_ENABLE BIT(24)
+
+static int __init
+realview_gic_of_init(struct device_node *node, struct device_node *parent)
+{
+ static struct regmap *map;
+
+ /* The PB11MPCore GIC needs to be configured in the syscon */
+ map = syscon_regmap_lookup_by_compatible("arm,realview-pb11mp-syscon");
+ if (!IS_ERR(map)) {
+ /* new irq mode with no DCC */
+ regmap_write(map, REALVIEW_SYS_LOCK_OFFSET,
+ VERSATILE_LOCK_VAL);
+ regmap_update_bits(map, REALVIEW_PB11MP_SYS_PLD_CTRL1,
+ PLD_INTMODE_NEW_NO_DCC,
+ PLD_INTMODE_MASK);
+ regmap_write(map, REALVIEW_SYS_LOCK_OFFSET, 0x0000);
+ pr_info("TC11MP GIC: set up interrupt controller to NEW mode, no DCC\n");
+ } else {
+ pr_err("TC11MP GIC setup: could not find syscon\n");
+ return -ENXIO;
+ }
+ return gic_of_init(node, parent);
+}
+IRQCHIP_DECLARE(armtc11mp_gic, "arm,tc11mp-gic", realview_gic_of_init);
diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c
index 87f8d104acab..c779f83e511d 100644
--- a/drivers/irqchip/irq-gic-v2m.c
+++ b/drivers/irqchip/irq-gic-v2m.c
@@ -15,9 +15,11 @@
#define pr_fmt(fmt) "GICv2m: " fmt
+#include <linux/acpi.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
+#include <linux/msi.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
#include <linux/slab.h>
@@ -55,7 +57,7 @@ static DEFINE_SPINLOCK(v2m_lock);
struct v2m_data {
struct list_head entry;
- struct device_node *node;
+ struct fwnode_handle *fwnode;
struct resource res; /* GICv2m resource */
void __iomem *base; /* GICv2m virt address */
u32 spi_start; /* The SPI number that MSIs start */
@@ -138,6 +140,11 @@ static int gicv2m_irq_gic_domain_alloc(struct irq_domain *domain,
fwspec.param[0] = 0;
fwspec.param[1] = hwirq - 32;
fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
+ } else if (is_fwnode_irqchip(domain->parent->fwnode)) {
+ fwspec.fwnode = domain->parent->fwnode;
+ fwspec.param_count = 2;
+ fwspec.param[0] = hwirq;
+ fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
} else {
return -EINVAL;
}
@@ -254,7 +261,9 @@ static void gicv2m_teardown(void)
list_del(&v2m->entry);
kfree(v2m->bm);
iounmap(v2m->base);
- of_node_put(v2m->node);
+ of_node_put(to_of_node(v2m->fwnode));
+ if (is_fwnode_irqchip(v2m->fwnode))
+ irq_domain_free_fwnode(v2m->fwnode);
kfree(v2m);
}
}
@@ -268,7 +277,7 @@ static int gicv2m_allocate_domains(struct irq_domain *parent)
if (!v2m)
return 0;
- inner_domain = irq_domain_create_tree(of_node_to_fwnode(v2m->node),
+ inner_domain = irq_domain_create_tree(v2m->fwnode,
&gicv2m_domain_ops, v2m);
if (!inner_domain) {
pr_err("Failed to create GICv2m domain\n");
@@ -277,10 +286,10 @@ static int gicv2m_allocate_domains(struct irq_domain *parent)
inner_domain->bus_token = DOMAIN_BUS_NEXUS;
inner_domain->parent = parent;
- pci_domain = pci_msi_create_irq_domain(of_node_to_fwnode(v2m->node),
+ pci_domain = pci_msi_create_irq_domain(v2m->fwnode,
&gicv2m_msi_domain_info,
inner_domain);
- plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(v2m->node),
+ plat_domain = platform_msi_create_irq_domain(v2m->fwnode,
&gicv2m_pmsi_domain_info,
inner_domain);
if (!pci_domain || !plat_domain) {
@@ -296,8 +305,9 @@ static int gicv2m_allocate_domains(struct irq_domain *parent)
return 0;
}
-static int __init gicv2m_init_one(struct device_node *node,
- struct irq_domain *parent)
+static int __init gicv2m_init_one(struct fwnode_handle *fwnode,
+ u32 spi_start, u32 nr_spis,
+ struct resource *res)
{
int ret;
struct v2m_data *v2m;
@@ -309,13 +319,9 @@ static int __init gicv2m_init_one(struct device_node *node,
}
INIT_LIST_HEAD(&v2m->entry);
- v2m->node = node;
+ v2m->fwnode = fwnode;
- ret = of_address_to_resource(node, 0, &v2m->res);
- if (ret) {
- pr_err("Failed to allocate v2m resource.\n");
- goto err_free_v2m;
- }
+ memcpy(&v2m->res, res, sizeof(struct resource));
v2m->base = ioremap(v2m->res.start, resource_size(&v2m->res));
if (!v2m->base) {
@@ -324,10 +330,9 @@ static int __init gicv2m_init_one(struct device_node *node,
goto err_free_v2m;
}
- if (!of_property_read_u32(node, "arm,msi-base-spi", &v2m->spi_start) &&
- !of_property_read_u32(node, "arm,msi-num-spis", &v2m->nr_spis)) {
- pr_info("Overriding V2M MSI_TYPER (base:%u, num:%u)\n",
- v2m->spi_start, v2m->nr_spis);
+ if (spi_start && nr_spis) {
+ v2m->spi_start = spi_start;
+ v2m->nr_spis = nr_spis;
} else {
u32 typer = readl_relaxed(v2m->base + V2M_MSI_TYPER);
@@ -359,10 +364,9 @@ static int __init gicv2m_init_one(struct device_node *node,
}
list_add_tail(&v2m->entry, &v2m_nodes);
- pr_info("Node %s: range[%#lx:%#lx], SPI[%d:%d]\n", node->name,
- (unsigned long)v2m->res.start, (unsigned long)v2m->res.end,
- v2m->spi_start, (v2m->spi_start + v2m->nr_spis));
+ pr_info("range%pR, SPI[%d:%d]\n", res,
+ v2m->spi_start, (v2m->spi_start + v2m->nr_spis - 1));
return 0;
err_iounmap:
@@ -377,19 +381,36 @@ static struct of_device_id gicv2m_device_id[] = {
{},
};
-int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent)
+static int __init gicv2m_of_init(struct fwnode_handle *parent_handle,
+ struct irq_domain *parent)
{
int ret = 0;
+ struct device_node *node = to_of_node(parent_handle);
struct device_node *child;
for (child = of_find_matching_node(node, gicv2m_device_id); child;
child = of_find_matching_node(child, gicv2m_device_id)) {
+ u32 spi_start = 0, nr_spis = 0;
+ struct resource res;
+
if (!of_find_property(child, "msi-controller", NULL))
continue;
- ret = gicv2m_init_one(child, parent);
+ ret = of_address_to_resource(child, 0, &res);
+ if (ret) {
+ pr_err("Failed to allocate v2m resource.\n");
+ break;
+ }
+
+ if (!of_property_read_u32(child, "arm,msi-base-spi",
+ &spi_start) &&
+ !of_property_read_u32(child, "arm,msi-num-spis", &nr_spis))
+ pr_info("DT overriding V2M MSI_TYPER (base:%u, num:%u)\n",
+ spi_start, nr_spis);
+
+ ret = gicv2m_init_one(&child->fwnode, spi_start, nr_spis, &res);
if (ret) {
- of_node_put(node);
+ of_node_put(child);
break;
}
}
@@ -400,3 +421,101 @@ int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent)
gicv2m_teardown();
return ret;
}
+
+#ifdef CONFIG_ACPI
+static int acpi_num_msi;
+
+static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev)
+{
+ struct v2m_data *data;
+
+ if (WARN_ON(acpi_num_msi <= 0))
+ return NULL;
+
+ /* We only return the fwnode of the first MSI frame. */
+ data = list_first_entry_or_null(&v2m_nodes, struct v2m_data, entry);
+ if (!data)
+ return NULL;
+
+ return data->fwnode;
+}
+
+static int __init
+acpi_parse_madt_msi(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ int ret;
+ struct resource res;
+ u32 spi_start = 0, nr_spis = 0;
+ struct acpi_madt_generic_msi_frame *m;
+ struct fwnode_handle *fwnode;
+
+ m = (struct acpi_madt_generic_msi_frame *)header;
+ if (BAD_MADT_ENTRY(m, end))
+ return -EINVAL;
+
+ res.start = m->base_address;
+ res.end = m->base_address + SZ_4K - 1;
+ res.flags = IORESOURCE_MEM;
+
+ if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) {
+ spi_start = m->spi_base;
+ nr_spis = m->spi_count;
+
+ pr_info("ACPI overriding V2M MSI_TYPER (base:%u, num:%u)\n",
+ spi_start, nr_spis);
+ }
+
+ fwnode = irq_domain_alloc_fwnode((void *)m->base_address);
+ if (!fwnode) {
+ pr_err("Unable to allocate GICv2m domain token\n");
+ return -EINVAL;
+ }
+
+ ret = gicv2m_init_one(fwnode, spi_start, nr_spis, &res);
+ if (ret)
+ irq_domain_free_fwnode(fwnode);
+
+ return ret;
+}
+
+static int __init gicv2m_acpi_init(struct irq_domain *parent)
+{
+ int ret;
+
+ if (acpi_num_msi > 0)
+ return 0;
+
+ acpi_num_msi = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_MSI_FRAME,
+ acpi_parse_madt_msi, 0);
+
+ if (acpi_num_msi <= 0)
+ goto err_out;
+
+ ret = gicv2m_allocate_domains(parent);
+ if (ret)
+ goto err_out;
+
+ pci_msi_register_fwnode_provider(&gicv2m_get_fwnode);
+
+ return 0;
+
+err_out:
+ gicv2m_teardown();
+ return -EINVAL;
+}
+#else /* CONFIG_ACPI */
+static int __init gicv2m_acpi_init(struct irq_domain *parent)
+{
+ return -EINVAL;
+}
+#endif /* CONFIG_ACPI */
+
+int __init gicv2m_init(struct fwnode_handle *parent_handle,
+ struct irq_domain *parent)
+{
+ if (is_of_node(parent_handle))
+ return gicv2m_of_init(parent_handle, parent);
+
+ return gicv2m_acpi_init(parent);
+}
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index abf2ffaed392..911758c056c1 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -69,6 +69,7 @@ union gic_base {
};
struct gic_chip_data {
+ struct irq_chip chip;
union gic_base dist_base;
union gic_base cpu_base;
#ifdef CONFIG_CPU_PM
@@ -99,11 +100,7 @@ static u8 gic_cpu_map[NR_GIC_CPU_IF] __read_mostly;
static struct static_key supports_deactivate = STATIC_KEY_INIT_TRUE;
-#ifndef MAX_GIC_NR
-#define MAX_GIC_NR 1
-#endif
-
-static struct gic_chip_data gic_data[MAX_GIC_NR] __read_mostly;
+static struct gic_chip_data gic_data[CONFIG_ARM_GIC_MAX_NR] __read_mostly;
#ifdef CONFIG_GIC_NON_BANKED
static void __iomem *gic_get_percpu_base(union gic_base *base)
@@ -336,7 +333,7 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
irqnr = irqstat & GICC_IAR_INT_ID_MASK;
- if (likely(irqnr > 15 && irqnr < 1021)) {
+ if (likely(irqnr > 15 && irqnr < 1020)) {
if (static_key_true(&supports_deactivate))
writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
handle_domain_irq(gic->domain, irqnr, regs);
@@ -383,7 +380,6 @@ static void gic_handle_cascade_irq(struct irq_desc *desc)
}
static struct irq_chip gic_chip = {
- .name = "GIC",
.irq_mask = gic_mask_irq,
.irq_unmask = gic_unmask_irq,
.irq_eoi = gic_eoi_irq,
@@ -417,8 +413,7 @@ static struct irq_chip gic_eoimode1_chip = {
void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
{
- if (gic_nr >= MAX_GIC_NR)
- BUG();
+ BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR);
irq_set_chained_handler_and_data(irq, gic_handle_cascade_irq,
&gic_data[gic_nr]);
}
@@ -524,7 +519,7 @@ int gic_cpu_if_down(unsigned int gic_nr)
void __iomem *cpu_base;
u32 val = 0;
- if (gic_nr >= MAX_GIC_NR)
+ if (gic_nr >= CONFIG_ARM_GIC_MAX_NR)
return -EINVAL;
cpu_base = gic_data_cpu_base(&gic_data[gic_nr]);
@@ -548,8 +543,7 @@ static void gic_dist_save(unsigned int gic_nr)
void __iomem *dist_base;
int i;
- if (gic_nr >= MAX_GIC_NR)
- BUG();
+ BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR);
gic_irqs = gic_data[gic_nr].gic_irqs;
dist_base = gic_data_dist_base(&gic_data[gic_nr]);
@@ -587,8 +581,7 @@ static void gic_dist_restore(unsigned int gic_nr)
unsigned int i;
void __iomem *dist_base;
- if (gic_nr >= MAX_GIC_NR)
- BUG();
+ BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR);
gic_irqs = gic_data[gic_nr].gic_irqs;
dist_base = gic_data_dist_base(&gic_data[gic_nr]);
@@ -634,8 +627,7 @@ static void gic_cpu_save(unsigned int gic_nr)
void __iomem *dist_base;
void __iomem *cpu_base;
- if (gic_nr >= MAX_GIC_NR)
- BUG();
+ BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR);
dist_base = gic_data_dist_base(&gic_data[gic_nr]);
cpu_base = gic_data_cpu_base(&gic_data[gic_nr]);
@@ -664,8 +656,7 @@ static void gic_cpu_restore(unsigned int gic_nr)
void __iomem *dist_base;
void __iomem *cpu_base;
- if (gic_nr >= MAX_GIC_NR)
- BUG();
+ BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR);
dist_base = gic_data_dist_base(&gic_data[gic_nr]);
cpu_base = gic_data_cpu_base(&gic_data[gic_nr]);
@@ -703,7 +694,7 @@ static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v)
{
int i;
- for (i = 0; i < MAX_GIC_NR; i++) {
+ for (i = 0; i < CONFIG_ARM_GIC_MAX_NR; i++) {
#ifdef CONFIG_GIC_NON_BANKED
/* Skip over unused GICs */
if (!gic_data[i].get_base)
@@ -835,8 +826,7 @@ void gic_migrate_target(unsigned int new_cpu_id)
int i, ror_val, cpu = smp_processor_id();
u32 val, cur_target_mask, active_mask;
- if (gic_nr >= MAX_GIC_NR)
- BUG();
+ BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR);
dist_base = gic_data_dist_base(&gic_data[gic_nr]);
if (!dist_base)
@@ -925,20 +915,15 @@ void __init gic_init_physaddr(struct device_node *node)
static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{
- struct irq_chip *chip = &gic_chip;
-
- if (static_key_true(&supports_deactivate)) {
- if (d->host_data == (void *)&gic_data[0])
- chip = &gic_eoimode1_chip;
- }
+ struct gic_chip_data *gic = d->host_data;
if (hw < 32) {
irq_set_percpu_devid(irq);
- irq_domain_set_info(d, irq, hw, chip, d->host_data,
+ irq_domain_set_info(d, irq, hw, &gic->chip, d->host_data,
handle_percpu_devid_irq, NULL, NULL);
irq_set_status_flags(irq, IRQ_NOAUTOEN);
} else {
- irq_domain_set_info(d, irq, hw, chip, d->host_data,
+ irq_domain_set_info(d, irq, hw, &gic->chip, d->host_data,
handle_fasteoi_irq, NULL, NULL);
irq_set_probe(irq);
}
@@ -972,7 +957,7 @@ static int gic_irq_domain_translate(struct irq_domain *d,
return 0;
}
- if (fwspec->fwnode->type == FWNODE_IRQCHIP) {
+ if (is_fwnode_irqchip(fwspec->fwnode)) {
if(fwspec->param_count != 2)
return -EINVAL;
@@ -1040,11 +1025,20 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start,
struct gic_chip_data *gic;
int gic_irqs, irq_base, i;
- BUG_ON(gic_nr >= MAX_GIC_NR);
+ BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR);
gic_check_cpu_features();
gic = &gic_data[gic_nr];
+
+ /* Initialize irq_chip */
+ if (static_key_true(&supports_deactivate) && gic_nr == 0) {
+ gic->chip = gic_eoimode1_chip;
+ } else {
+ gic->chip = gic_chip;
+ gic->chip.name = kasprintf(GFP_KERNEL, "GIC-%d", gic_nr);
+ }
+
#ifdef CONFIG_GIC_NON_BANKED
if (percpu_offset) { /* Frankein-GIC without banked registers... */
unsigned int cpu;
@@ -1196,7 +1190,7 @@ static bool gic_check_eoimode(struct device_node *node, void __iomem **base)
return true;
}
-static int __init
+int __init
gic_of_init(struct device_node *node, struct device_node *parent)
{
void __iomem *cpu_base;
@@ -1234,7 +1228,7 @@ gic_of_init(struct device_node *node, struct device_node *parent)
}
if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
- gicv2m_of_init(node, gic_data[gic_cnt].domain);
+ gicv2m_init(&node->fwnode, gic_data[gic_cnt].domain);
gic_cnt++;
return 0;
@@ -1359,6 +1353,10 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header,
__gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_handle);
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle);
+
+ if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
+ gicv2m_init(NULL, gic_data[0].domain);
+
return 0;
}
IRQCHIP_ACPI_DECLARE(gic_v2, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR,
diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c
new file mode 100644
index 000000000000..4dd3eb8a40b3
--- /dev/null
+++ b/drivers/irqchip/irq-mbigen.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2015 Hisilicon Limited, All Rights Reserved.
+ * Author: Jun Ma <majun258@huawei.com>
+ * Author: Yun Wu <wuyun.wu@huawei.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irqchip.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+/* Interrupt numbers per mbigen node supported */
+#define IRQS_PER_MBIGEN_NODE 128
+
+/* 64 irqs (Pin0-pin63) are reserved for each mbigen chip */
+#define RESERVED_IRQ_PER_MBIGEN_CHIP 64
+
+/* The maximum IRQ pin number of mbigen chip(start from 0) */
+#define MAXIMUM_IRQ_PIN_NUM 1407
+
+/**
+ * In mbigen vector register
+ * bit[21:12]: event id value
+ * bit[11:0]: device id
+ */
+#define IRQ_EVENT_ID_SHIFT 12
+#define IRQ_EVENT_ID_MASK 0x3ff
+
+/* register range of each mbigen node */
+#define MBIGEN_NODE_OFFSET 0x1000
+
+/* offset of vector register in mbigen node */
+#define REG_MBIGEN_VEC_OFFSET 0x200
+
+/**
+ * offset of clear register in mbigen node
+ * This register is used to clear the status
+ * of interrupt
+ */
+#define REG_MBIGEN_CLEAR_OFFSET 0xa000
+
+/**
+ * offset of interrupt type register
+ * This register is used to configure interrupt
+ * trigger type
+ */
+#define REG_MBIGEN_TYPE_OFFSET 0x0
+
+/**
+ * struct mbigen_device - holds the information of mbigen device.
+ *
+ * @pdev: pointer to the platform device structure of mbigen chip.
+ * @base: mapped address of this mbigen chip.
+ */
+struct mbigen_device {
+ struct platform_device *pdev;
+ void __iomem *base;
+};
+
+static inline unsigned int get_mbigen_vec_reg(irq_hw_number_t hwirq)
+{
+ unsigned int nid, pin;
+
+ hwirq -= RESERVED_IRQ_PER_MBIGEN_CHIP;
+ nid = hwirq / IRQS_PER_MBIGEN_NODE + 1;
+ pin = hwirq % IRQS_PER_MBIGEN_NODE;
+
+ return pin * 4 + nid * MBIGEN_NODE_OFFSET
+ + REG_MBIGEN_VEC_OFFSET;
+}
+
+static inline void get_mbigen_type_reg(irq_hw_number_t hwirq,
+ u32 *mask, u32 *addr)
+{
+ unsigned int nid, irq_ofst, ofst;
+
+ hwirq -= RESERVED_IRQ_PER_MBIGEN_CHIP;
+ nid = hwirq / IRQS_PER_MBIGEN_NODE + 1;
+ irq_ofst = hwirq % IRQS_PER_MBIGEN_NODE;
+
+ *mask = 1 << (irq_ofst % 32);
+ ofst = irq_ofst / 32 * 4;
+
+ *addr = ofst + nid * MBIGEN_NODE_OFFSET
+ + REG_MBIGEN_TYPE_OFFSET;
+}
+
+static inline void get_mbigen_clear_reg(irq_hw_number_t hwirq,
+ u32 *mask, u32 *addr)
+{
+ unsigned int ofst;
+
+ hwirq -= RESERVED_IRQ_PER_MBIGEN_CHIP;
+ ofst = hwirq / 32 * 4;
+
+ *mask = 1 << (hwirq % 32);
+ *addr = ofst + REG_MBIGEN_CLEAR_OFFSET;
+}
+
+static void mbigen_eoi_irq(struct irq_data *data)
+{
+ void __iomem *base = data->chip_data;
+ u32 mask, addr;
+
+ get_mbigen_clear_reg(data->hwirq, &mask, &addr);
+
+ writel_relaxed(mask, base + addr);
+
+ irq_chip_eoi_parent(data);
+}
+
+static int mbigen_set_type(struct irq_data *data, unsigned int type)
+{
+ void __iomem *base = data->chip_data;
+ u32 mask, addr, val;
+
+ if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING)
+ return -EINVAL;
+
+ get_mbigen_type_reg(data->hwirq, &mask, &addr);
+
+ val = readl_relaxed(base + addr);
+
+ if (type == IRQ_TYPE_LEVEL_HIGH)
+ val |= mask;
+ else
+ val &= ~mask;
+
+ writel_relaxed(val, base + addr);
+
+ return 0;
+}
+
+static struct irq_chip mbigen_irq_chip = {
+ .name = "mbigen-v2",
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_eoi = mbigen_eoi_irq,
+ .irq_set_type = mbigen_set_type,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+};
+
+static void mbigen_write_msg(struct msi_desc *desc, struct msi_msg *msg)
+{
+ struct irq_data *d = irq_get_irq_data(desc->irq);
+ void __iomem *base = d->chip_data;
+ u32 val;
+
+ base += get_mbigen_vec_reg(d->hwirq);
+ val = readl_relaxed(base);
+
+ val &= ~(IRQ_EVENT_ID_MASK << IRQ_EVENT_ID_SHIFT);
+ val |= (msg->data << IRQ_EVENT_ID_SHIFT);
+
+ /* The address of doorbell is encoded in mbigen register by default
+ * So,we don't need to program the doorbell address at here
+ */
+ writel_relaxed(val, base);
+}
+
+static int mbigen_domain_translate(struct irq_domain *d,
+ struct irq_fwspec *fwspec,
+ unsigned long *hwirq,
+ unsigned int *type)
+{
+ if (is_of_node(fwspec->fwnode)) {
+ if (fwspec->param_count != 2)
+ return -EINVAL;
+
+ if ((fwspec->param[0] > MAXIMUM_IRQ_PIN_NUM) ||
+ (fwspec->param[0] < RESERVED_IRQ_PER_MBIGEN_CHIP))
+ return -EINVAL;
+ else
+ *hwirq = fwspec->param[0];
+
+ /* If there is no valid irq type, just use the default type */
+ if ((fwspec->param[1] == IRQ_TYPE_EDGE_RISING) ||
+ (fwspec->param[1] == IRQ_TYPE_LEVEL_HIGH))
+ *type = fwspec->param[1];
+ else
+ return -EINVAL;
+
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int mbigen_irq_domain_alloc(struct irq_domain *domain,
+ unsigned int virq,
+ unsigned int nr_irqs,
+ void *args)
+{
+ struct irq_fwspec *fwspec = args;
+ irq_hw_number_t hwirq;
+ unsigned int type;
+ struct mbigen_device *mgn_chip;
+ int i, err;
+
+ err = mbigen_domain_translate(domain, fwspec, &hwirq, &type);
+ if (err)
+ return err;
+
+ err = platform_msi_domain_alloc(domain, virq, nr_irqs);
+ if (err)
+ return err;
+
+ mgn_chip = platform_msi_get_host_data(domain);
+
+ for (i = 0; i < nr_irqs; i++)
+ irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+ &mbigen_irq_chip, mgn_chip->base);
+
+ return 0;
+}
+
+static struct irq_domain_ops mbigen_domain_ops = {
+ .translate = mbigen_domain_translate,
+ .alloc = mbigen_irq_domain_alloc,
+ .free = irq_domain_free_irqs_common,
+};
+
+static int mbigen_device_probe(struct platform_device *pdev)
+{
+ struct mbigen_device *mgn_chip;
+ struct resource *res;
+ struct irq_domain *domain;
+ u32 num_pins;
+
+ mgn_chip = devm_kzalloc(&pdev->dev, sizeof(*mgn_chip), GFP_KERNEL);
+ if (!mgn_chip)
+ return -ENOMEM;
+
+ mgn_chip->pdev = pdev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mgn_chip->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mgn_chip->base))
+ return PTR_ERR(mgn_chip->base);
+
+ if (of_property_read_u32(pdev->dev.of_node, "num-pins", &num_pins) < 0) {
+ dev_err(&pdev->dev, "No num-pins property\n");
+ return -EINVAL;
+ }
+
+ domain = platform_msi_create_device_domain(&pdev->dev, num_pins,
+ mbigen_write_msg,
+ &mbigen_domain_ops,
+ mgn_chip);
+
+ if (!domain)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, mgn_chip);
+
+ dev_info(&pdev->dev, "Allocated %d MSIs\n", num_pins);
+
+ return 0;
+}
+
+static const struct of_device_id mbigen_of_match[] = {
+ { .compatible = "hisilicon,mbigen-v2" },
+ { /* END */ }
+};
+MODULE_DEVICE_TABLE(of, mbigen_of_match);
+
+static struct platform_driver mbigen_platform_driver = {
+ .driver = {
+ .name = "Hisilicon MBIGEN-V2",
+ .owner = THIS_MODULE,
+ .of_match_table = mbigen_of_match,
+ },
+ .probe = mbigen_device_probe,
+};
+
+module_platform_driver(mbigen_platform_driver);
+
+MODULE_AUTHOR("Jun Ma <majun258@huawei.com>");
+MODULE_AUTHOR("Yun Wu <wuyun.wu@huawei.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Hisilicon MBI Generator driver");
diff --git a/drivers/irqchip/irq-omap-intc.c b/drivers/irqchip/irq-omap-intc.c
index 8587d0f8d8c0..9d1bcfc33e4c 100644
--- a/drivers/irqchip/irq-omap-intc.c
+++ b/drivers/irqchip/irq-omap-intc.c
@@ -47,6 +47,7 @@
#define INTC_ILR0 0x0100
#define ACTIVEIRQ_MASK 0x7f /* omap2/3 active interrupt bits */
+#define SPURIOUSIRQ_MASK (0x1ffffff << 7)
#define INTCPS_NR_ILR_REGS 128
#define INTCPS_NR_MIR_REGS 4
@@ -207,7 +208,6 @@ static int __init omap_alloc_gc_of(struct irq_domain *d, void __iomem *base)
ct = gc->chip_types;
ct->type = IRQ_TYPE_LEVEL_MASK;
- ct->handler = handle_level_irq;
ct->chip.irq_ack = omap_mask_ack_irq;
ct->chip.irq_mask = irq_gc_mask_disable_reg;
@@ -330,11 +330,35 @@ static int __init omap_init_irq(u32 base, struct device_node *node)
static asmlinkage void __exception_irq_entry
omap_intc_handle_irq(struct pt_regs *regs)
{
+ extern unsigned long irq_err_count;
u32 irqnr;
irqnr = intc_readl(INTC_SIR);
+
+ /*
+ * A spurious IRQ can result if interrupt that triggered the
+ * sorting is no longer active during the sorting (10 INTC
+ * functional clock cycles after interrupt assertion). Or a
+ * change in interrupt mask affected the result during sorting
+ * time. There is no special handling required except ignoring
+ * the SIR register value just read and retrying.
+ * See section 6.2.5 of AM335x TRM Literature Number: SPRUH73K
+ *
+ * Many a times, a spurious interrupt situation has been fixed
+ * by adding a flush for the posted write acking the IRQ in
+ * the device driver. Typically, this is going be the device
+ * driver whose interrupt was handled just before the spurious
+ * IRQ occurred. Pay attention to those device drivers if you
+ * run into hitting the spurious IRQ condition below.
+ */
+ if (unlikely((irqnr & SPURIOUSIRQ_MASK) == SPURIOUSIRQ_MASK)) {
+ pr_err_once("%s: spurious irq!\n", __func__);
+ irq_err_count++;
+ omap_ack_irq(NULL);
+ return;
+ }
+
irqnr &= ACTIVEIRQ_MASK;
- WARN_ONCE(!irqnr, "Spurious IRQ ?\n");
handle_domain_irq(domain, irqnr, regs);
}
diff --git a/drivers/irqchip/irq-renesas-h8300h.c b/drivers/irqchip/irq-renesas-h8300h.c
index 6fd30d5ee14d..c378768d75b3 100644
--- a/drivers/irqchip/irq-renesas-h8300h.c
+++ b/drivers/irqchip/irq-renesas-h8300h.c
@@ -21,9 +21,9 @@ static const char ipr_bit[] = {
10, 10, 10, 10, 9, 9, 9, 9,
};
-static void *intc_baseaddr;
+static void __iomem *intc_baseaddr;
-#define IPR ((unsigned long)intc_baseaddr + 6)
+#define IPR (intc_baseaddr + 6)
static void h8300h_disable_irq(struct irq_data *data)
{
@@ -81,8 +81,8 @@ static int __init h8300h_intc_of_init(struct device_node *intc,
BUG_ON(!intc_baseaddr);
/* All interrupt priority low */
- ctrl_outb(0x00, IPR + 0);
- ctrl_outb(0x00, IPR + 1);
+ writeb(0x00, IPR + 0);
+ writeb(0x00, IPR + 1);
domain = irq_domain_add_linear(intc, NR_IRQS, &irq_ops, NULL);
BUG_ON(!domain);
diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c
index c325806561be..713177d97c7a 100644
--- a/drivers/irqchip/irq-renesas-intc-irqpin.c
+++ b/drivers/irqchip/irq-renesas-intc-irqpin.c
@@ -31,7 +31,6 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/of_device.h>
-#include <linux/platform_data/irq-renesas-intc-irqpin.h>
#include <linux/pm_runtime.h>
#define INTC_IRQPIN_MAX 8 /* maximum 8 interrupts per driver instance */
@@ -75,18 +74,20 @@ struct intc_irqpin_irq {
struct intc_irqpin_priv {
struct intc_irqpin_iomem iomem[INTC_IRQPIN_REG_NR];
struct intc_irqpin_irq irq[INTC_IRQPIN_MAX];
- struct renesas_intc_irqpin_config config;
- unsigned int number_of_irqs;
+ unsigned int sense_bitfield_width;
struct platform_device *pdev;
struct irq_chip irq_chip;
struct irq_domain *irq_domain;
struct clk *clk;
- bool shared_irqs;
+ unsigned shared_irqs:1;
+ unsigned needs_clk:1;
u8 shared_irq_mask;
};
-struct intc_irqpin_irlm_config {
+struct intc_irqpin_config {
unsigned int irlm_bit;
+ unsigned needs_irlm:1;
+ unsigned needs_clk:1;
};
static unsigned long intc_irqpin_read32(void __iomem *iomem)
@@ -171,7 +172,7 @@ static void intc_irqpin_mask_unmask_prio(struct intc_irqpin_priv *p,
static int intc_irqpin_set_sense(struct intc_irqpin_priv *p, int irq, int value)
{
/* The SENSE register is assumed to be 32-bit. */
- int bitfield_width = p->config.sense_bitfield_width;
+ int bitfield_width = p->sense_bitfield_width;
int shift = 32 - (irq + 1) * bitfield_width;
dev_dbg(&p->pdev->dev, "sense irq = %d, mode = %d\n", irq, value);
@@ -361,8 +362,15 @@ static const struct irq_domain_ops intc_irqpin_irq_domain_ops = {
.xlate = irq_domain_xlate_twocell,
};
-static const struct intc_irqpin_irlm_config intc_irqpin_irlm_r8a777x = {
+static const struct intc_irqpin_config intc_irqpin_irlm_r8a777x = {
.irlm_bit = 23, /* ICR0.IRLM0 */
+ .needs_irlm = 1,
+ .needs_clk = 0,
+};
+
+static const struct intc_irqpin_config intc_irqpin_rmobile = {
+ .needs_irlm = 0,
+ .needs_clk = 1,
};
static const struct of_device_id intc_irqpin_dt_ids[] = {
@@ -371,14 +379,18 @@ static const struct of_device_id intc_irqpin_dt_ids[] = {
.data = &intc_irqpin_irlm_r8a777x },
{ .compatible = "renesas,intc-irqpin-r8a7779",
.data = &intc_irqpin_irlm_r8a777x },
+ { .compatible = "renesas,intc-irqpin-r8a7740",
+ .data = &intc_irqpin_rmobile },
+ { .compatible = "renesas,intc-irqpin-sh73a0",
+ .data = &intc_irqpin_rmobile },
{},
};
MODULE_DEVICE_TABLE(of, intc_irqpin_dt_ids);
static int intc_irqpin_probe(struct platform_device *pdev)
{
+ const struct intc_irqpin_config *config = NULL;
struct device *dev = &pdev->dev;
- struct renesas_intc_irqpin_config *pdata = dev->platform_data;
const struct of_device_id *of_id;
struct intc_irqpin_priv *p;
struct intc_irqpin_iomem *i;
@@ -388,6 +400,8 @@ static int intc_irqpin_probe(struct platform_device *pdev)
void (*enable_fn)(struct irq_data *d);
void (*disable_fn)(struct irq_data *d);
const char *name = dev_name(dev);
+ bool control_parent;
+ unsigned int nirqs;
int ref_irq;
int ret;
int k;
@@ -399,23 +413,28 @@ static int intc_irqpin_probe(struct platform_device *pdev)
}
/* deal with driver instance configuration */
- if (pdata) {
- memcpy(&p->config, pdata, sizeof(*pdata));
- } else {
- of_property_read_u32(dev->of_node, "sense-bitfield-width",
- &p->config.sense_bitfield_width);
- p->config.control_parent = of_property_read_bool(dev->of_node,
- "control-parent");
- }
- if (!p->config.sense_bitfield_width)
- p->config.sense_bitfield_width = 4; /* default to 4 bits */
+ of_property_read_u32(dev->of_node, "sense-bitfield-width",
+ &p->sense_bitfield_width);
+ control_parent = of_property_read_bool(dev->of_node, "control-parent");
+ if (!p->sense_bitfield_width)
+ p->sense_bitfield_width = 4; /* default to 4 bits */
p->pdev = pdev;
platform_set_drvdata(pdev, p);
+ of_id = of_match_device(intc_irqpin_dt_ids, dev);
+ if (of_id && of_id->data) {
+ config = of_id->data;
+ p->needs_clk = config->needs_clk;
+ }
+
p->clk = devm_clk_get(dev, NULL);
if (IS_ERR(p->clk)) {
- dev_warn(dev, "unable to get clock\n");
+ if (p->needs_clk) {
+ dev_err(dev, "unable to get clock\n");
+ ret = PTR_ERR(p->clk);
+ goto err0;
+ }
p->clk = NULL;
}
@@ -443,8 +462,8 @@ static int intc_irqpin_probe(struct platform_device *pdev)
p->irq[k].requested_irq = irq->start;
}
- p->number_of_irqs = k;
- if (p->number_of_irqs < 1) {
+ nirqs = k;
+ if (nirqs < 1) {
dev_err(dev, "not enough IRQ resources\n");
ret = -EINVAL;
goto err0;
@@ -485,20 +504,16 @@ static int intc_irqpin_probe(struct platform_device *pdev)
}
/* configure "individual IRQ mode" where needed */
- of_id = of_match_device(intc_irqpin_dt_ids, dev);
- if (of_id && of_id->data) {
- const struct intc_irqpin_irlm_config *irlm_config = of_id->data;
-
+ if (config && config->needs_irlm) {
if (io[INTC_IRQPIN_REG_IRLM])
intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_IRLM,
- irlm_config->irlm_bit,
- 1, 1);
+ config->irlm_bit, 1, 1);
else
dev_warn(dev, "unable to select IRLM mode\n");
}
/* mask all interrupts using priority */
- for (k = 0; k < p->number_of_irqs; k++)
+ for (k = 0; k < nirqs; k++)
intc_irqpin_mask_unmask_prio(p, k, 1);
/* clear all pending interrupts */
@@ -506,16 +521,16 @@ static int intc_irqpin_probe(struct platform_device *pdev)
/* scan for shared interrupt lines */
ref_irq = p->irq[0].requested_irq;
- p->shared_irqs = true;
- for (k = 1; k < p->number_of_irqs; k++) {
+ p->shared_irqs = 1;
+ for (k = 1; k < nirqs; k++) {
if (ref_irq != p->irq[k].requested_irq) {
- p->shared_irqs = false;
+ p->shared_irqs = 0;
break;
}
}
/* use more severe masking method if requested */
- if (p->config.control_parent) {
+ if (control_parent) {
enable_fn = intc_irqpin_irq_enable_force;
disable_fn = intc_irqpin_irq_disable_force;
} else if (!p->shared_irqs) {
@@ -534,9 +549,7 @@ static int intc_irqpin_probe(struct platform_device *pdev)
irq_chip->irq_set_wake = intc_irqpin_irq_set_wake;
irq_chip->flags = IRQCHIP_MASK_ON_SUSPEND;
- p->irq_domain = irq_domain_add_simple(dev->of_node,
- p->number_of_irqs,
- p->config.irq_base,
+ p->irq_domain = irq_domain_add_simple(dev->of_node, nirqs, 0,
&intc_irqpin_irq_domain_ops, p);
if (!p->irq_domain) {
ret = -ENXIO;
@@ -555,7 +568,7 @@ static int intc_irqpin_probe(struct platform_device *pdev)
}
} else {
/* request interrupts one by one */
- for (k = 0; k < p->number_of_irqs; k++) {
+ for (k = 0; k < nirqs; k++) {
if (devm_request_irq(dev, p->irq[k].requested_irq,
intc_irqpin_irq_handler, 0, name,
&p->irq[k])) {
@@ -567,17 +580,10 @@ static int intc_irqpin_probe(struct platform_device *pdev)
}
/* unmask all interrupts on prio level */
- for (k = 0; k < p->number_of_irqs; k++)
+ for (k = 0; k < nirqs; k++)
intc_irqpin_mask_unmask_prio(p, k, 0);
- dev_info(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(dev, "irq base mismatch (%d/%d)\n",
- p->config.irq_base, p->irq[0].domain_irq);
- }
+ dev_info(dev, "driving %d irqs\n", nirqs);
return 0;
diff --git a/drivers/irqchip/irq-sunxi-nmi.c b/drivers/irqchip/irq-sunxi-nmi.c
index 4ef178078e5b..0820f67cc9a7 100644
--- a/drivers/irqchip/irq-sunxi-nmi.c
+++ b/drivers/irqchip/irq-sunxi-nmi.c
@@ -50,6 +50,12 @@ static struct sunxi_sc_nmi_reg_offs sun6i_reg_offs = {
.enable = 0x34,
};
+static struct sunxi_sc_nmi_reg_offs sun9i_reg_offs = {
+ .ctrl = 0x00,
+ .pend = 0x08,
+ .enable = 0x04,
+};
+
static inline void sunxi_sc_nmi_write(struct irq_chip_generic *gc, u32 off,
u32 val)
{
@@ -207,3 +213,10 @@ static int __init sun7i_sc_nmi_irq_init(struct device_node *node,
return sunxi_sc_nmi_irq_init(node, &sun7i_reg_offs);
}
IRQCHIP_DECLARE(sun7i_sc_nmi, "allwinner,sun7i-a20-sc-nmi", sun7i_sc_nmi_irq_init);
+
+static int __init sun9i_nmi_irq_init(struct device_node *node,
+ struct device_node *parent)
+{
+ return sunxi_sc_nmi_irq_init(node, &sun9i_reg_offs);
+}
+IRQCHIP_DECLARE(sun9i_nmi, "allwinner,sun9i-a80-nmi", sun9i_nmi_irq_init);
diff --git a/drivers/irqchip/irq-ts4800.c b/drivers/irqchip/irq-ts4800.c
new file mode 100644
index 000000000000..4192bdcd2734
--- /dev/null
+++ b/drivers/irqchip/irq-ts4800.c
@@ -0,0 +1,163 @@
+/*
+ * Multiplexed-IRQs driver for TS-4800's FPGA
+ *
+ * Copyright (c) 2015 - Savoir-faire Linux
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+#define IRQ_MASK 0x4
+#define IRQ_STATUS 0x8
+
+struct ts4800_irq_data {
+ void __iomem *base;
+ struct irq_domain *domain;
+ struct irq_chip irq_chip;
+};
+
+static void ts4800_irq_mask(struct irq_data *d)
+{
+ struct ts4800_irq_data *data = irq_data_get_irq_chip_data(d);
+ u16 reg = readw(data->base + IRQ_MASK);
+ u16 mask = 1 << d->hwirq;
+
+ writew(reg | mask, data->base + IRQ_MASK);
+}
+
+static void ts4800_irq_unmask(struct irq_data *d)
+{
+ struct ts4800_irq_data *data = irq_data_get_irq_chip_data(d);
+ u16 reg = readw(data->base + IRQ_MASK);
+ u16 mask = 1 << d->hwirq;
+
+ writew(reg & ~mask, data->base + IRQ_MASK);
+}
+
+static int ts4800_irqdomain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct ts4800_irq_data *data = d->host_data;
+
+ irq_set_chip_and_handler(irq, &data->irq_chip, handle_simple_irq);
+ irq_set_chip_data(irq, data);
+ irq_set_noprobe(irq);
+
+ return 0;
+}
+
+struct irq_domain_ops ts4800_ic_ops = {
+ .map = ts4800_irqdomain_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static void ts4800_ic_chained_handle_irq(struct irq_desc *desc)
+{
+ struct ts4800_irq_data *data = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ u16 status = readw(data->base + IRQ_STATUS);
+
+ chained_irq_enter(chip, desc);
+
+ if (unlikely(status == 0)) {
+ handle_bad_irq(desc);
+ goto out;
+ }
+
+ do {
+ unsigned int bit = __ffs(status);
+ int irq = irq_find_mapping(data->domain, bit);
+
+ status &= ~(1 << bit);
+ generic_handle_irq(irq);
+ } while (status);
+
+out:
+ chained_irq_exit(chip, desc);
+}
+
+static int ts4800_ic_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct ts4800_irq_data *data;
+ struct irq_chip *irq_chip;
+ struct resource *res;
+ int parent_irq;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ data->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(data->base))
+ return PTR_ERR(data->base);
+
+ writew(0xFFFF, data->base + IRQ_MASK);
+
+ parent_irq = irq_of_parse_and_map(node, 0);
+ if (!parent_irq) {
+ dev_err(&pdev->dev, "failed to get parent IRQ\n");
+ return -EINVAL;
+ }
+
+ irq_chip = &data->irq_chip;
+ irq_chip->name = dev_name(&pdev->dev);
+ irq_chip->irq_mask = ts4800_irq_mask;
+ irq_chip->irq_unmask = ts4800_irq_unmask;
+
+ data->domain = irq_domain_add_linear(node, 8, &ts4800_ic_ops, data);
+ if (!data->domain) {
+ dev_err(&pdev->dev, "cannot add IRQ domain\n");
+ return -ENOMEM;
+ }
+
+ irq_set_chained_handler_and_data(parent_irq,
+ ts4800_ic_chained_handle_irq, data);
+
+ platform_set_drvdata(pdev, data);
+
+ return 0;
+}
+
+static int ts4800_ic_remove(struct platform_device *pdev)
+{
+ struct ts4800_irq_data *data = platform_get_drvdata(pdev);
+
+ irq_domain_remove(data->domain);
+
+ return 0;
+}
+
+static const struct of_device_id ts4800_ic_of_match[] = {
+ { .compatible = "technologic,ts4800-irqc", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ts4800_ic_of_match);
+
+static struct platform_driver ts4800_ic_driver = {
+ .probe = ts4800_ic_probe,
+ .remove = ts4800_ic_remove,
+ .driver = {
+ .name = "ts4800-irqc",
+ .of_match_table = ts4800_ic_of_match,
+ },
+};
+module_platform_driver(ts4800_ic_driver);
+
+MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:ts4800_irqc");
diff --git a/drivers/irqchip/irq-versatile-fpga.c b/drivers/irqchip/irq-versatile-fpga.c
index 598ab3f0e0ac..cadf104e3074 100644
--- a/drivers/irqchip/irq-versatile-fpga.c
+++ b/drivers/irqchip/irq-versatile-fpga.c
@@ -210,7 +210,12 @@ int __init fpga_irq_of_init(struct device_node *node,
parent_irq = -1;
}
+#ifdef CONFIG_ARCH_VERSATILE
+ fpga_irq_init(base, node->name, IRQ_SIC_START, parent_irq, valid_mask,
+ node);
+#else
fpga_irq_init(base, node->name, 0, parent_irq, valid_mask, node);
+#endif
writel(clear_mask, base + IRQ_ENABLE_CLEAR);
writel(clear_mask, base + FIQ_ENABLE_CLEAR);
diff --git a/drivers/irqchip/irq-zevio.c b/drivers/irqchip/irq-zevio.c
index 4c48fa88a03d..cb9d8ec37507 100644
--- a/drivers/irqchip/irq-zevio.c
+++ b/drivers/irqchip/irq-zevio.c
@@ -43,8 +43,7 @@ static void __iomem *zevio_irq_io;
static void zevio_irq_ack(struct irq_data *irqd)
{
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(irqd);
- struct irq_chip_regs *regs =
- &container_of(irqd->chip, struct irq_chip_type, chip)->regs;
+ struct irq_chip_regs *regs = &irq_data_get_chip_type(irqd)->regs;
readl(gc->reg_base + regs->ack);
}
diff --git a/drivers/isdn/Makefile b/drivers/isdn/Makefile
index f1f777570e8e..91c81965e7ca 100644
--- a/drivers/isdn/Makefile
+++ b/drivers/isdn/Makefile
@@ -10,7 +10,6 @@ obj-$(CONFIG_ISDN_DIVERSION) += divert/
obj-$(CONFIG_ISDN_DRV_HISAX) += hisax/
obj-$(CONFIG_ISDN_DRV_ICN) += icn/
obj-$(CONFIG_ISDN_DRV_PCBIT) += pcbit/
-obj-$(CONFIG_ISDN_DRV_SC) += sc/
obj-$(CONFIG_ISDN_DRV_LOOP) += isdnloop/
obj-$(CONFIG_ISDN_DRV_ACT2000) += act2000/
obj-$(CONFIG_HYSDN) += hysdn/
diff --git a/drivers/isdn/act2000/module.c b/drivers/isdn/act2000/module.c
index c3a1b061838d..68073d0da0e3 100644
--- a/drivers/isdn/act2000/module.c
+++ b/drivers/isdn/act2000/module.c
@@ -37,7 +37,7 @@ MODULE_DESCRIPTION("ISDN4Linux: Driver for IBM Active 2000 ISDN card");
MODULE_AUTHOR("Fritz Elfert");
MODULE_LICENSE("GPL");
MODULE_PARM_DESC(act_bus, "BusType of first card, 1=ISA, 2=MCA, 3=PCMCIA, currently only ISA");
-MODULE_PARM_DESC(membase, "Base port address of first card");
+MODULE_PARM_DESC(act_port, "Base port address of first card");
MODULE_PARM_DESC(act_irq, "IRQ of first card");
MODULE_PARM_DESC(act_id, "ID-String of first card");
module_param(act_bus, int, 0);
diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c
index 375be509e95f..2a506fe0c8a4 100644
--- a/drivers/isdn/gigaset/ser-gigaset.c
+++ b/drivers/isdn/gigaset/ser-gigaset.c
@@ -67,8 +67,7 @@ static int write_modem(struct cardstate *cs)
struct sk_buff *skb = bcs->tx_skb;
int sent = -EOPNOTSUPP;
- if (!tty || !tty->driver || !skb)
- return -EINVAL;
+ WARN_ON(!tty || !tty->ops || !skb);
if (!skb->len) {
dev_kfree_skb_any(skb);
@@ -109,8 +108,7 @@ static int send_cb(struct cardstate *cs)
unsigned long flags;
int sent = 0;
- if (!tty || !tty->driver)
- return -EFAULT;
+ WARN_ON(!tty || !tty->ops);
cb = cs->cmdbuf;
if (!cb)
@@ -370,19 +368,18 @@ static void gigaset_freecshw(struct cardstate *cs)
tasklet_kill(&cs->write_tasklet);
if (!cs->hw.ser)
return;
- dev_set_drvdata(&cs->hw.ser->dev.dev, NULL);
platform_device_unregister(&cs->hw.ser->dev);
- kfree(cs->hw.ser);
- cs->hw.ser = NULL;
}
static void gigaset_device_release(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
+ struct cardstate *cs = dev_get_drvdata(dev);
- /* adapted from platform_device_release() in drivers/base/platform.c */
- kfree(dev->platform_data);
- kfree(pdev->resource);
+ if (!cs)
+ return;
+ dev_set_drvdata(dev, NULL);
+ kfree(cs->hw.ser);
+ cs->hw.ser = NULL;
}
/*
@@ -432,7 +429,9 @@ static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state,
struct tty_struct *tty = cs->hw.ser->tty;
unsigned int set, clear;
- if (!tty || !tty->driver || !tty->ops->tiocmset)
+ WARN_ON(!tty || !tty->ops);
+ /* tiocmset is an optional tty driver method */
+ if (!tty->ops->tiocmset)
return -EINVAL;
set = new_state & ~old_state;
clear = old_state & ~new_state;
diff --git a/drivers/isdn/hardware/mISDN/mISDNipac.c b/drivers/isdn/hardware/mISDN/mISDNipac.c
index a77eea594b69..cb428b9ee441 100644
--- a/drivers/isdn/hardware/mISDN/mISDNipac.c
+++ b/drivers/isdn/hardware/mISDN/mISDNipac.c
@@ -1170,7 +1170,7 @@ mISDNipac_irq(struct ipac_hw *ipac, int maxloop)
if (ipac->type & IPAC_TYPE_IPACX) {
ista = ReadIPAC(ipac, ISACX_ISTA);
- while (ista && cnt--) {
+ while (ista && --cnt) {
pr_debug("%s: ISTA %02x\n", ipac->name, ista);
if (ista & IPACX__ICA)
ipac_irq(&ipac->hscx[0], ista);
@@ -1182,7 +1182,7 @@ mISDNipac_irq(struct ipac_hw *ipac, int maxloop)
}
} else if (ipac->type & IPAC_TYPE_IPAC) {
ista = ReadIPAC(ipac, IPAC_ISTA);
- while (ista && cnt--) {
+ while (ista && --cnt) {
pr_debug("%s: ISTA %02x\n", ipac->name, ista);
if (ista & (IPAC__ICD | IPAC__EXD)) {
istad = ReadISAC(isac, ISAC_ISTA);
@@ -1200,7 +1200,7 @@ mISDNipac_irq(struct ipac_hw *ipac, int maxloop)
ista = ReadIPAC(ipac, IPAC_ISTA);
}
} else if (ipac->type & IPAC_TYPE_HSCX) {
- while (cnt) {
+ while (--cnt) {
ista = ReadIPAC(ipac, IPAC_ISTAB + ipac->hscx[1].off);
pr_debug("%s: B2 ISTA %02x\n", ipac->name, ista);
if (ista)
@@ -1211,7 +1211,6 @@ mISDNipac_irq(struct ipac_hw *ipac, int maxloop)
mISDNisac_irq(isac, istad);
if (0 == (ista | istad))
break;
- cnt--;
}
}
if (cnt > maxloop) /* only for ISAC/HSCX without PCI IRQ test */
diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c
index b33f53b3ca93..bf04d2a3cf4a 100644
--- a/drivers/isdn/hisax/config.c
+++ b/drivers/isdn/hisax/config.c
@@ -1896,7 +1896,7 @@ static void EChannel_proc_rcv(struct hisax_d_if *d_if)
ptr--;
*ptr++ = '\n';
*ptr = 0;
- HiSax_putstatus(cs, NULL, "%s", cs->dlog);
+ HiSax_putstatus(cs, NULL, cs->dlog);
} else
HiSax_putstatus(cs, "LogEcho: ",
"warning Frame too big (%d)",
diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c
index 4a4825528188..90449e1e91e5 100644
--- a/drivers/isdn/hisax/hfc_pci.c
+++ b/drivers/isdn/hisax/hfc_pci.c
@@ -901,7 +901,7 @@ Begin:
ptr--;
*ptr++ = '\n';
*ptr = 0;
- HiSax_putstatus(cs, NULL, "%s", cs->dlog);
+ HiSax_putstatus(cs, NULL, cs->dlog);
} else
HiSax_putstatus(cs, "LogEcho: ", "warning Frame too big (%d)", total - 3);
}
diff --git a/drivers/isdn/hisax/hfc_sx.c b/drivers/isdn/hisax/hfc_sx.c
index b1fad81f0722..13b2151c10f5 100644
--- a/drivers/isdn/hisax/hfc_sx.c
+++ b/drivers/isdn/hisax/hfc_sx.c
@@ -674,7 +674,7 @@ receive_emsg(struct IsdnCardState *cs)
ptr--;
*ptr++ = '\n';
*ptr = 0;
- HiSax_putstatus(cs, NULL, "%s", cs->dlog);
+ HiSax_putstatus(cs, NULL, cs->dlog);
} else
HiSax_putstatus(cs, "LogEcho: ", "warning Frame too big (%d)", skb->len);
}
diff --git a/drivers/isdn/hisax/q931.c b/drivers/isdn/hisax/q931.c
index b420f8bd862e..ba4beb25d872 100644
--- a/drivers/isdn/hisax/q931.c
+++ b/drivers/isdn/hisax/q931.c
@@ -1179,7 +1179,7 @@ LogFrame(struct IsdnCardState *cs, u_char *buf, int size)
dp--;
*dp++ = '\n';
*dp = 0;
- HiSax_putstatus(cs, NULL, "%s", cs->dlog);
+ HiSax_putstatus(cs, NULL, cs->dlog);
} else
HiSax_putstatus(cs, "LogFrame: ", "warning Frame too big (%d)", size);
}
@@ -1246,7 +1246,7 @@ dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir)
}
if (finish) {
*dp = 0;
- HiSax_putstatus(cs, NULL, "%s", cs->dlog);
+ HiSax_putstatus(cs, NULL, cs->dlog);
return;
}
if ((0xfe & buf[0]) == PROTO_DIS_N0) { /* 1TR6 */
@@ -1509,5 +1509,5 @@ dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir)
dp += sprintf(dp, "Unknown protocol %x!", buf[0]);
}
*dp = 0;
- HiSax_putstatus(cs, NULL, "%s", cs->dlog);
+ HiSax_putstatus(cs, NULL, cs->dlog);
}
diff --git a/drivers/isdn/i4l/Kconfig b/drivers/isdn/i4l/Kconfig
index 9c6650ea848e..f5b714cd7618 100644
--- a/drivers/isdn/i4l/Kconfig
+++ b/drivers/isdn/i4l/Kconfig
@@ -130,8 +130,6 @@ source "drivers/isdn/icn/Kconfig"
source "drivers/isdn/pcbit/Kconfig"
-source "drivers/isdn/sc/Kconfig"
-
source "drivers/isdn/act2000/Kconfig"
endmenu
diff --git a/drivers/isdn/sc/Kconfig b/drivers/isdn/sc/Kconfig
deleted file mode 100644
index 7469863a7925..000000000000
--- a/drivers/isdn/sc/Kconfig
+++ /dev/null
@@ -1,8 +0,0 @@
-config ISDN_DRV_SC
- tristate "Spellcaster support"
- depends on ISA
- help
- This enables support for the Spellcaster BRI ISDN boards. This
- driver currently builds only in a modularized version.
- To build it, choose M here: the module will be called sc.
- See <file:Documentation/isdn/README.sc> for more information.
diff --git a/drivers/isdn/sc/Makefile b/drivers/isdn/sc/Makefile
deleted file mode 100644
index 0f2b7d602ac0..000000000000
--- a/drivers/isdn/sc/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-# Makefile for the sc ISDN device driver
-
-# Each configuration option enables a list of files.
-
-obj-$(CONFIG_ISDN_DRV_SC) += sc.o
-
-# Multipart objects.
-
-sc-y := shmem.o init.o packet.o command.o event.o \
- ioctl.o interrupt.o message.o timer.o
diff --git a/drivers/isdn/sc/card.h b/drivers/isdn/sc/card.h
deleted file mode 100644
index 3da69ee43da7..000000000000
--- a/drivers/isdn/sc/card.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/* $Id: card.h,v 1.1.10.1 2001/09/23 22:24:59 kai Exp $
- *
- * Driver parameters for SpellCaster ISA ISDN adapters
- *
- * Copyright (C) 1996 SpellCaster Telecommunications Inc.
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * For more information, please contact gpl-info@spellcast.com or write:
- *
- * SpellCaster Telecommunications Inc.
- * 5621 Finch Avenue East, Unit #3
- * Scarborough, Ontario Canada
- * M1B 2T9
- * +1 (416) 297-8565
- * +1 (416) 297-6433 Facsimile
- */
-
-#ifndef CARD_H
-#define CARD_H
-
-/*
- * We need these if they're not already included
- */
-#include <linux/timer.h>
-#include <linux/time.h>
-#include <linux/isdnif.h>
-#include <linux/irqreturn.h>
-#include "message.h"
-#include "scioc.h"
-
-/*
- * Amount of time to wait for a reset to complete
- */
-#define CHECKRESET_TIME msecs_to_jiffies(4000)
-
-/*
- * Amount of time between line status checks
- */
-#define CHECKSTAT_TIME msecs_to_jiffies(8000)
-
-/*
- * The maximum amount of time to wait for a message response
- * to arrive. Use exclusively by send_and_receive
- */
-#define SAR_TIMEOUT msecs_to_jiffies(10000)
-
-/*
- * Macro to determine is a card id is valid
- */
-#define IS_VALID_CARD(x) ((x >= 0) && (x <= cinst))
-
-/*
- * Per channel status and configuration
- */
-typedef struct {
- int l2_proto;
- int l3_proto;
- char dn[50];
- unsigned long first_sendbuf; /* Offset of first send buffer */
- unsigned int num_sendbufs; /* Number of send buffers */
- unsigned int free_sendbufs; /* Number of free sendbufs */
- unsigned int next_sendbuf; /* Next sequential buffer */
- char eazlist[50]; /* Set with SETEAZ */
- char sillist[50]; /* Set with SETSIL */
- int eazclear; /* Don't accept calls if TRUE */
-} bchan;
-
-/*
- * Everything you want to know about the adapter ...
- */
-typedef struct {
- int model;
- int driverId; /* LL Id */
- char devicename[20]; /* The device name */
- isdn_if *card; /* ISDN4Linux structure */
- bchan *channel; /* status of the B channels */
- char nChannels; /* Number of channels */
- unsigned int interrupt; /* Interrupt number */
- int iobase; /* I/O Base address */
- int ioport[MAX_IO_REGS]; /* Index to I/O ports */
- int shmem_pgport; /* port for the exp mem page reg. */
- int shmem_magic; /* adapter magic number */
- unsigned int rambase; /* Shared RAM base address */
- unsigned int ramsize; /* Size of shared memory */
- RspMessage async_msg; /* Async response message */
- int want_async_messages; /* Snoop the Q ? */
- unsigned char seq_no; /* Next send seq. number */
- struct timer_list reset_timer; /* Check reset timer */
- struct timer_list stat_timer; /* Check startproc timer */
- unsigned char nphystat; /* Latest PhyStat info */
- unsigned char phystat; /* Last PhyStat info */
- HWConfig_pl hwconfig; /* Hardware config info */
- char load_ver[11]; /* CommManage Version string */
- char proc_ver[11]; /* CommEngine Version */
- int StartOnReset; /* Indicates startproc after reset */
- int EngineUp; /* Indicates CommEngine Up */
- int trace_mode; /* Indicate if tracing is on */
- spinlock_t lock; /* local lock */
-} board;
-
-
-extern board *sc_adapter[];
-extern int cinst;
-
-void memcpy_toshmem(int card, void *dest, const void *src, size_t n);
-void memcpy_fromshmem(int card, void *dest, const void *src, size_t n);
-int get_card_from_id(int driver);
-int indicate_status(int card, int event, ulong Channel, char *Data);
-irqreturn_t interrupt_handler(int interrupt, void *cardptr);
-int sndpkt(int devId, int channel, int ack, struct sk_buff *data);
-void rcvpkt(int card, RspMessage *rcvmsg);
-int command(isdn_ctrl *cmd);
-int reset(int card);
-int startproc(int card);
-int send_and_receive(int card, unsigned int procid, unsigned char type,
- unsigned char class, unsigned char code,
- unsigned char link, unsigned char data_len,
- unsigned char *data, RspMessage *mesgdata, int timeout);
-void flushreadfifo(int card);
-int sendmessage(int card, unsigned int procid, unsigned int type,
- unsigned int class, unsigned int code, unsigned int link,
- unsigned int data_len, unsigned int *data);
-int receivemessage(int card, RspMessage *rspmsg);
-int sc_ioctl(int card, scs_ioctl *data);
-int setup_buffers(int card, int c);
-void sc_check_reset(unsigned long data);
-void check_phystat(unsigned long data);
-
-#endif /* CARD_H */
diff --git a/drivers/isdn/sc/command.c b/drivers/isdn/sc/command.c
deleted file mode 100644
index 4a4e66152ce7..000000000000
--- a/drivers/isdn/sc/command.c
+++ /dev/null
@@ -1,363 +0,0 @@
-/* $Id: command.c,v 1.4.10.1 2001/09/23 22:24:59 kai Exp $
- *
- * Copyright (C) 1996 SpellCaster Telecommunications Inc.
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * For more information, please contact gpl-info@spellcast.com or write:
- *
- * SpellCaster Telecommunications Inc.
- * 5621 Finch Avenue East, Unit #3
- * Scarborough, Ontario Canada
- * M1B 2T9
- * +1 (416) 297-8565
- * +1 (416) 297-6433 Facsimile
- */
-
-#include <linux/module.h>
-#include "includes.h" /* This must be first */
-#include "hardware.h"
-#include "message.h"
-#include "card.h"
-#include "scioc.h"
-
-static int dial(int card, unsigned long channel, setup_parm setup);
-static int hangup(int card, unsigned long channel);
-static int answer(int card, unsigned long channel);
-static int clreaz(int card, unsigned long channel);
-static int seteaz(int card, unsigned long channel, char *);
-static int setl2(int card, unsigned long arg);
-static int setl3(int card, unsigned long arg);
-static int acceptb(int card, unsigned long channel);
-
-#ifdef DEBUG
-/*
- * Translate command codes to strings
- */
-static char *commands[] = { "ISDN_CMD_IOCTL",
- "ISDN_CMD_DIAL",
- "ISDN_CMD_ACCEPTB",
- "ISDN_CMD_ACCEPTB",
- "ISDN_CMD_HANGUP",
- "ISDN_CMD_CLREAZ",
- "ISDN_CMD_SETEAZ",
- NULL,
- NULL,
- NULL,
- "ISDN_CMD_SETL2",
- NULL,
- "ISDN_CMD_SETL3",
- NULL,
- NULL,
- NULL,
- NULL,
- NULL, };
-
-/*
- * Translates ISDN4Linux protocol codes to strings for debug messages
- */
-static char *l3protos[] = { "ISDN_PROTO_L3_TRANS" };
-static char *l2protos[] = { "ISDN_PROTO_L2_X75I",
- "ISDN_PROTO_L2_X75UI",
- "ISDN_PROTO_L2_X75BUI",
- "ISDN_PROTO_L2_HDLC",
- "ISDN_PROTO_L2_TRANS" };
-#endif
-
-int get_card_from_id(int driver)
-{
- int i;
-
- for (i = 0; i < cinst; i++) {
- if (sc_adapter[i]->driverId == driver)
- return i;
- }
- return -ENODEV;
-}
-
-/*
- * command
- */
-
-int command(isdn_ctrl *cmd)
-{
- int card;
-
- card = get_card_from_id(cmd->driver);
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- /*
- * Dispatch the command
- */
- switch (cmd->command) {
- case ISDN_CMD_IOCTL:
- {
- unsigned long cmdptr;
- scs_ioctl ioc;
-
- memcpy(&cmdptr, cmd->parm.num, sizeof(unsigned long));
- if (copy_from_user(&ioc, (scs_ioctl __user *)cmdptr,
- sizeof(scs_ioctl))) {
- pr_debug("%s: Failed to verify user space 0x%lx\n",
- sc_adapter[card]->devicename, cmdptr);
- return -EFAULT;
- }
- return sc_ioctl(card, &ioc);
- }
- case ISDN_CMD_DIAL:
- return dial(card, cmd->arg, cmd->parm.setup);
- case ISDN_CMD_HANGUP:
- return hangup(card, cmd->arg);
- case ISDN_CMD_ACCEPTD:
- return answer(card, cmd->arg);
- case ISDN_CMD_ACCEPTB:
- return acceptb(card, cmd->arg);
- case ISDN_CMD_CLREAZ:
- return clreaz(card, cmd->arg);
- case ISDN_CMD_SETEAZ:
- return seteaz(card, cmd->arg, cmd->parm.num);
- case ISDN_CMD_SETL2:
- return setl2(card, cmd->arg);
- case ISDN_CMD_SETL3:
- return setl3(card, cmd->arg);
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-/*
- * start the onboard firmware
- */
-int startproc(int card)
-{
- int status;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- /*
- * send start msg
- */
- status = sendmessage(card, CMPID, cmReqType2,
- cmReqClass0,
- cmReqStartProc,
- 0, 0, NULL);
- pr_debug("%s: Sent startProc\n", sc_adapter[card]->devicename);
-
- return status;
-}
-
-
-/*
- * Dials the number passed in
- */
-static int dial(int card, unsigned long channel, setup_parm setup)
-{
- int status;
- char Phone[48];
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- /*extract ISDN number to dial from eaz/msn string*/
- strcpy(Phone, setup.phone);
-
- /*send the connection message*/
- status = sendmessage(card, CEPID, ceReqTypePhy,
- ceReqClass1,
- ceReqPhyConnect,
- (unsigned char)channel + 1,
- strlen(Phone),
- (unsigned int *)Phone);
-
- pr_debug("%s: Dialing %s on channel %lu\n",
- sc_adapter[card]->devicename, Phone, channel + 1);
-
- return status;
-}
-
-/*
- * Answer an incoming call
- */
-static int answer(int card, unsigned long channel)
-{
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- if (setup_buffers(card, channel + 1)) {
- hangup(card, channel + 1);
- return -ENOBUFS;
- }
-
- indicate_status(card, ISDN_STAT_BCONN, channel, NULL);
- pr_debug("%s: Answered incoming call on channel %lu\n",
- sc_adapter[card]->devicename, channel + 1);
- return 0;
-}
-
-/*
- * Hangup up the call on specified channel
- */
-static int hangup(int card, unsigned long channel)
-{
- int status;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- status = sendmessage(card, CEPID, ceReqTypePhy,
- ceReqClass1,
- ceReqPhyDisconnect,
- (unsigned char)channel + 1,
- 0,
- NULL);
- pr_debug("%s: Sent HANGUP message to channel %lu\n",
- sc_adapter[card]->devicename, channel + 1);
- return status;
-}
-
-/*
- * Set the layer 2 protocol (X.25, HDLC, Raw)
- */
-static int setl2(int card, unsigned long arg)
-{
- int status = 0;
- int protocol, channel;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
- protocol = arg >> 8;
- channel = arg & 0xff;
- sc_adapter[card]->channel[channel].l2_proto = protocol;
-
- /*
- * check that the adapter is also set to the correct protocol
- */
- pr_debug("%s: Sending GetFrameFormat for channel %d\n",
- sc_adapter[card]->devicename, channel + 1);
- status = sendmessage(card, CEPID, ceReqTypeCall,
- ceReqClass0,
- ceReqCallGetFrameFormat,
- (unsigned char)channel + 1,
- 1,
- (unsigned int *)protocol);
- if (status)
- return status;
- return 0;
-}
-
-/*
- * Set the layer 3 protocol
- */
-static int setl3(int card, unsigned long channel)
-{
- int protocol = channel >> 8;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- sc_adapter[card]->channel[channel].l3_proto = protocol;
- return 0;
-}
-
-static int acceptb(int card, unsigned long channel)
-{
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- if (setup_buffers(card, channel + 1))
- {
- hangup(card, channel + 1);
- return -ENOBUFS;
- }
-
- pr_debug("%s: B-Channel connection accepted on channel %lu\n",
- sc_adapter[card]->devicename, channel + 1);
- indicate_status(card, ISDN_STAT_BCONN, channel, NULL);
- return 0;
-}
-
-static int clreaz(int card, unsigned long arg)
-{
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- strcpy(sc_adapter[card]->channel[arg].eazlist, "");
- sc_adapter[card]->channel[arg].eazclear = 1;
- pr_debug("%s: EAZ List cleared for channel %lu\n",
- sc_adapter[card]->devicename, arg + 1);
- return 0;
-}
-
-static int seteaz(int card, unsigned long arg, char *num)
-{
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- strcpy(sc_adapter[card]->channel[arg].eazlist, num);
- sc_adapter[card]->channel[arg].eazclear = 0;
- pr_debug("%s: EAZ list for channel %lu set to: %s\n",
- sc_adapter[card]->devicename, arg + 1,
- sc_adapter[card]->channel[arg].eazlist);
- return 0;
-}
-
-int reset(int card)
-{
- unsigned long flags;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- indicate_status(card, ISDN_STAT_STOP, 0, NULL);
-
- if (sc_adapter[card]->EngineUp) {
- del_timer(&sc_adapter[card]->stat_timer);
- }
-
- sc_adapter[card]->EngineUp = 0;
-
- spin_lock_irqsave(&sc_adapter[card]->lock, flags);
- init_timer(&sc_adapter[card]->reset_timer);
- sc_adapter[card]->reset_timer.function = sc_check_reset;
- sc_adapter[card]->reset_timer.data = card;
- sc_adapter[card]->reset_timer.expires = jiffies + CHECKRESET_TIME;
- add_timer(&sc_adapter[card]->reset_timer);
- spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
-
- outb(0x1, sc_adapter[card]->ioport[SFT_RESET]);
-
- pr_debug("%s: Adapter Reset\n", sc_adapter[card]->devicename);
- return 0;
-}
-
-void flushreadfifo(int card)
-{
- while (inb(sc_adapter[card]->ioport[FIFO_STATUS]) & RF_HAS_DATA)
- inb(sc_adapter[card]->ioport[FIFO_READ]);
-}
diff --git a/drivers/isdn/sc/event.c b/drivers/isdn/sc/event.c
deleted file mode 100644
index 833d96c2cf92..000000000000
--- a/drivers/isdn/sc/event.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/* $Id: event.c,v 1.4.8.1 2001/09/23 22:24:59 kai Exp $
- *
- * Copyright (C) 1996 SpellCaster Telecommunications Inc.
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * For more information, please contact gpl-info@spellcast.com or write:
- *
- * SpellCaster Telecommunications Inc.
- * 5621 Finch Avenue East, Unit #3
- * Scarborough, Ontario Canada
- * M1B 2T9
- * +1 (416) 297-8565
- * +1 (416) 297-6433 Facsimile
- */
-
-#include "includes.h"
-#include "hardware.h"
-#include "message.h"
-#include "card.h"
-
-#ifdef DEBUG
-static char *events[] = { "ISDN_STAT_STAVAIL",
- "ISDN_STAT_ICALL",
- "ISDN_STAT_RUN",
- "ISDN_STAT_STOP",
- "ISDN_STAT_DCONN",
- "ISDN_STAT_BCONN",
- "ISDN_STAT_DHUP",
- "ISDN_STAT_BHUP",
- "ISDN_STAT_CINF",
- "ISDN_STAT_LOAD",
- "ISDN_STAT_UNLOAD",
- "ISDN_STAT_BSENT",
- "ISDN_STAT_NODCH",
- "ISDN_STAT_ADDCH",
- "ISDN_STAT_CAUSE" };
-#endif
-
-int indicate_status(int card, int event, ulong Channel, char *Data)
-{
- isdn_ctrl cmd;
-
-#ifdef DEBUG
- pr_debug("%s: Indicating event %s on Channel %d\n",
- sc_adapter[card]->devicename, events[event - 256], Channel);
-#endif
- if (Data != NULL) {
- pr_debug("%s: Event data: %s\n", sc_adapter[card]->devicename,
- Data);
- switch (event) {
- case ISDN_STAT_BSENT:
- memcpy(&cmd.parm.length, Data, sizeof(cmd.parm.length));
- break;
- case ISDN_STAT_ICALL:
- memcpy(&cmd.parm.setup, Data, sizeof(cmd.parm.setup));
- break;
- default:
- strlcpy(cmd.parm.num, Data, sizeof(cmd.parm.num));
- }
- }
-
- cmd.command = event;
- cmd.driver = sc_adapter[card]->driverId;
- cmd.arg = Channel;
- return sc_adapter[card]->card->statcallb(&cmd);
-}
diff --git a/drivers/isdn/sc/hardware.h b/drivers/isdn/sc/hardware.h
deleted file mode 100644
index 81fbe78701f0..000000000000
--- a/drivers/isdn/sc/hardware.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Hardware specific macros, defines and structures
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#ifndef HARDWARE_H
-#define HARDWARE_H
-
-#include <asm/param.h> /* For HZ */
-
-/*
- * General hardware parameters common to all ISA adapters
- */
-
-#define MAX_CARDS 4 /* The maximum number of cards to
- control or probe for. */
-
-#define SIGNATURE 0x87654321 /* Board reset signature */
-#define SIG_OFFSET 0x1004 /* Where to find signature in shared RAM */
-#define TRACE_OFFSET 0x1008 /* Trace enable word offset in shared RAM */
-#define BUFFER_OFFSET 0x1800 /* Beginning of buffers */
-
-/* I/O Port parameters */
-#define IOBASE_MIN 0x180 /* Lowest I/O port address */
-#define IOBASE_MAX 0x3C0 /* Highest I/O port address */
-#define IOBASE_OFFSET 0x20 /* Inter-board I/O port gap used during
- probing */
-#define FIFORD_OFFSET 0x0
-#define FIFOWR_OFFSET 0x400
-#define FIFOSTAT_OFFSET 0x1000
-#define RESET_OFFSET 0x2800
-#define PG0_OFFSET 0x3000 /* Offset from I/O Base for Page 0 register */
-#define PG1_OFFSET 0x3400 /* Offset from I/O Base for Page 1 register */
-#define PG2_OFFSET 0x3800 /* Offset from I/O Base for Page 2 register */
-#define PG3_OFFSET 0x3C00 /* Offset from I/O Base for Page 3 register */
-
-#define FIFO_READ 0 /* FIFO Read register */
-#define FIFO_WRITE 1 /* FIFO Write rgister */
-#define LO_ADDR_PTR 2 /* Extended RAM Low Addr Pointer */
-#define HI_ADDR_PTR 3 /* Extended RAM High Addr Pointer */
-#define NOT_USED_1 4
-#define FIFO_STATUS 5 /* FIFO Status Register */
-#define NOT_USED_2 6
-#define MEM_OFFSET 7
-#define SFT_RESET 10 /* Reset Register */
-#define EXP_BASE 11 /* Shared RAM Base address */
-#define EXP_PAGE0 12 /* Shared RAM Page0 register */
-#define EXP_PAGE1 13 /* Shared RAM Page1 register */
-#define EXP_PAGE2 14 /* Shared RAM Page2 register */
-#define EXP_PAGE3 15 /* Shared RAM Page3 register */
-#define IRQ_SELECT 16 /* IRQ selection register */
-#define MAX_IO_REGS 17 /* Total number of I/O ports */
-
-/* FIFO register values */
-#define RF_HAS_DATA 0x01 /* fifo has data */
-#define RF_QUART_FULL 0x02 /* fifo quarter full */
-#define RF_HALF_FULL 0x04 /* fifo half full */
-#define RF_NOT_FULL 0x08 /* fifo not full */
-#define WF_HAS_DATA 0x10 /* fifo has data */
-#define WF_QUART_FULL 0x20 /* fifo quarter full */
-#define WF_HALF_FULL 0x40 /* fifo half full */
-#define WF_NOT_FULL 0x80 /* fifo not full */
-
-/* Shared RAM parameters */
-#define SRAM_MIN 0xC0000 /* Lowest host shared RAM address */
-#define SRAM_MAX 0xEFFFF /* Highest host shared RAM address */
-#define SRAM_PAGESIZE 0x4000 /* Size of one RAM page (16K) */
-
-/* Shared RAM buffer parameters */
-#define BUFFER_SIZE 0x800 /* The size of a buffer in bytes */
-#define BUFFER_BASE BUFFER_OFFSET /* Offset from start of shared RAM
- where buffer start */
-#define BUFFERS_MAX 16 /* Maximum number of send/receive
- buffers per channel */
-#define HDLC_PROTO 0x01 /* Frame Format for Layer 2 */
-
-#define BRI_BOARD 0
-#define POTS_BOARD 1
-#define PRI_BOARD 2
-
-/*
- * Specific hardware parameters for the DataCommute/BRI
- */
-#define BRI_CHANNELS 2 /* Number of B channels */
-#define BRI_BASEPG_VAL 0x98
-#define BRI_MAGIC 0x60000 /* Magic Number */
-#define BRI_MEMSIZE 0x10000 /* Amount of RAM (64K) */
-#define BRI_PARTNO "72-029"
-#define BRI_FEATURES ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L3_TRANS;
-/*
- * Specific hardware parameters for the DataCommute/PRI
- */
-#define PRI_CHANNELS 23 /* Number of B channels */
-#define PRI_BASEPG_VAL 0x88
-#define PRI_MAGIC 0x20000 /* Magic Number */
-#define PRI_MEMSIZE 0x100000 /* Amount of RAM (1M) */
-#define PRI_PARTNO "72-030"
-#define PRI_FEATURES ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L3_TRANS;
-
-/*
- * Some handy macros
- */
-
-/* Determine if a channel number is valid for the adapter */
-#define IS_VALID_CHANNEL(y, x) ((x > 0) && (x <= sc_adapter[y]->channels))
-
-#endif
diff --git a/drivers/isdn/sc/includes.h b/drivers/isdn/sc/includes.h
deleted file mode 100644
index 4766e5b77378..000000000000
--- a/drivers/isdn/sc/includes.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/errno.h>
-#include <asm/io.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/timer.h>
-#include <linux/wait.h>
-#include <linux/isdnif.h>
diff --git a/drivers/isdn/sc/init.c b/drivers/isdn/sc/init.c
deleted file mode 100644
index 3597ef47b28a..000000000000
--- a/drivers/isdn/sc/init.c
+++ /dev/null
@@ -1,549 +0,0 @@
-/*
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include "includes.h"
-#include "hardware.h"
-#include "card.h"
-
-MODULE_DESCRIPTION("ISDN4Linux: Driver for Spellcaster card");
-MODULE_AUTHOR("Spellcaster Telecommunications Inc.");
-MODULE_LICENSE("GPL");
-
-board *sc_adapter[MAX_CARDS];
-int cinst;
-
-static char devname[] = "scX";
-static const char version[] = "2.0b1";
-
-static const char *boardname[] = { "DataCommute/BRI", "DataCommute/PRI", "TeleCommute/BRI" };
-
-/* insmod set parameters */
-static unsigned int io[] = {0, 0, 0, 0};
-static unsigned char irq[] = {0, 0, 0, 0};
-static unsigned long ram[] = {0, 0, 0, 0};
-static bool do_reset;
-
-module_param_array(io, int, NULL, 0);
-module_param_array(irq, byte, NULL, 0);
-module_param_array(ram, long, NULL, 0);
-module_param(do_reset, bool, 0);
-
-static int identify_board(unsigned long, unsigned int);
-
-static int __init sc_init(void)
-{
- int b = -1;
- int i, j;
- int status = -ENODEV;
-
- unsigned long memsize = 0;
- unsigned long features = 0;
- isdn_if *interface;
- unsigned char channels;
- unsigned char pgport;
- unsigned long magic;
- int model;
- int last_base = IOBASE_MIN;
- int probe_exhasted = 0;
-
-#ifdef MODULE
- pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s Loaded\n", version);
-#else
- pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s\n", version);
-#endif
- pr_info("Copyright (C) 1996 SpellCaster Telecommunications Inc.\n");
-
- while (b++ < MAX_CARDS - 1) {
- pr_debug("Probing for adapter #%d\n", b);
- /*
- * Initialize reusable variables
- */
- model = -1;
- magic = 0;
- channels = 0;
- pgport = 0;
-
- /*
- * See if we should probe for IO base
- */
- pr_debug("I/O Base for board %d is 0x%x, %s probe\n", b, io[b],
- io[b] == 0 ? "will" : "won't");
- if (io[b]) {
- /*
- * No, I/O Base has been provided
- */
- for (i = 0; i < MAX_IO_REGS - 1; i++) {
- if (!request_region(io[b] + i * 0x400, 1, "sc test")) {
- pr_debug("request_region for 0x%x failed\n", io[b] + i * 0x400);
- io[b] = 0;
- break;
- } else
- release_region(io[b] + i * 0x400, 1);
- }
-
- /*
- * Confirm the I/O Address with a test
- */
- if (io[b] == 0) {
- pr_debug("I/O Address invalid.\n");
- continue;
- }
-
- outb(0x18, io[b] + 0x400 * EXP_PAGE0);
- if (inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) {
- pr_debug("I/O Base 0x%x fails test\n",
- io[b] + 0x400 * EXP_PAGE0);
- continue;
- }
- } else {
- /*
- * Yes, probe for I/O Base
- */
- if (probe_exhasted) {
- pr_debug("All probe addresses exhausted, skipping\n");
- continue;
- }
- pr_debug("Probing for I/O...\n");
- for (i = last_base; i <= IOBASE_MAX; i += IOBASE_OFFSET) {
- int found_io = 1;
- if (i == IOBASE_MAX) {
- probe_exhasted = 1; /* No more addresses to probe */
- pr_debug("End of Probes\n");
- }
- last_base = i + IOBASE_OFFSET;
- pr_debug(" checking 0x%x...", i);
- for (j = 0; j < MAX_IO_REGS - 1; j++) {
- if (!request_region(i + j * 0x400, 1, "sc test")) {
- pr_debug("Failed\n");
- found_io = 0;
- break;
- } else
- release_region(i + j * 0x400, 1);
- }
-
- if (found_io) {
- io[b] = i;
- outb(0x18, io[b] + 0x400 * EXP_PAGE0);
- if (inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) {
- pr_debug("Failed by test\n");
- continue;
- }
- pr_debug("Passed\n");
- break;
- }
- }
- if (probe_exhasted) {
- continue;
- }
- }
-
- /*
- * See if we should probe for shared RAM
- */
- if (do_reset) {
- pr_debug("Doing a SAFE probe reset\n");
- outb(0xFF, io[b] + RESET_OFFSET);
- msleep_interruptible(10000);
- }
- pr_debug("RAM Base for board %d is 0x%lx, %s probe\n", b,
- ram[b], ram[b] == 0 ? "will" : "won't");
-
- if (ram[b]) {
- /*
- * No, the RAM base has been provided
- * Just look for a signature and ID the
- * board model
- */
- if (request_region(ram[b], SRAM_PAGESIZE, "sc test")) {
- pr_debug("request_region for RAM base 0x%lx succeeded\n", ram[b]);
- model = identify_board(ram[b], io[b]);
- release_region(ram[b], SRAM_PAGESIZE);
- }
- } else {
- /*
- * Yes, probe for free RAM and look for
- * a signature and id the board model
- */
- for (i = SRAM_MIN; i < SRAM_MAX; i += SRAM_PAGESIZE) {
- pr_debug("Checking RAM address 0x%x...\n", i);
- if (request_region(i, SRAM_PAGESIZE, "sc test")) {
- pr_debug(" request_region succeeded\n");
- model = identify_board(i, io[b]);
- release_region(i, SRAM_PAGESIZE);
- if (model >= 0) {
- pr_debug(" Identified a %s\n",
- boardname[model]);
- ram[b] = i;
- break;
- }
- pr_debug(" Unidentified or inaccessible\n");
- continue;
- }
- pr_debug(" request failed\n");
- }
- }
- /*
- * See if we found free RAM and the board model
- */
- if (!ram[b] || model < 0) {
- /*
- * Nope, there was no place in RAM for the
- * board, or it couldn't be identified
- */
- pr_debug("Failed to find an adapter at 0x%lx\n", ram[b]);
- continue;
- }
-
- /*
- * Set the board's magic number, memory size and page register
- */
- switch (model) {
- case PRI_BOARD:
- channels = 23;
- magic = 0x20000;
- memsize = 0x100000;
- features = PRI_FEATURES;
- break;
-
- case BRI_BOARD:
- case POTS_BOARD:
- channels = 2;
- magic = 0x60000;
- memsize = 0x10000;
- features = BRI_FEATURES;
- break;
- }
- switch (ram[b] >> 12 & 0x0F) {
- case 0x0:
- pr_debug("RAM Page register set to EXP_PAGE0\n");
- pgport = EXP_PAGE0;
- break;
-
- case 0x4:
- pr_debug("RAM Page register set to EXP_PAGE1\n");
- pgport = EXP_PAGE1;
- break;
-
- case 0x8:
- pr_debug("RAM Page register set to EXP_PAGE2\n");
- pgport = EXP_PAGE2;
- break;
-
- case 0xC:
- pr_debug("RAM Page register set to EXP_PAGE3\n");
- pgport = EXP_PAGE3;
- break;
-
- default:
- pr_debug("RAM base address doesn't fall on 16K boundary\n");
- continue;
- }
-
- pr_debug("current IRQ: %d b: %d\n", irq[b], b);
-
- /*
- * Make sure we got an IRQ
- */
- if (!irq[b]) {
- /*
- * No interrupt could be used
- */
- pr_debug("Failed to acquire an IRQ line\n");
- continue;
- }
-
- /*
- * Horray! We found a board, Make sure we can register
- * it with ISDN4Linux
- */
- interface = kzalloc(sizeof(isdn_if), GFP_KERNEL);
- if (interface == NULL) {
- /*
- * Oops, can't malloc isdn_if
- */
- continue;
- }
-
- interface->owner = THIS_MODULE;
- interface->hl_hdrlen = 0;
- interface->channels = channels;
- interface->maxbufsize = BUFFER_SIZE;
- interface->features = features;
- interface->writebuf_skb = sndpkt;
- interface->writecmd = NULL;
- interface->command = command;
- strcpy(interface->id, devname);
- interface->id[2] = '0' + cinst;
-
- /*
- * Allocate the board structure
- */
- sc_adapter[cinst] = kzalloc(sizeof(board), GFP_KERNEL);
- if (sc_adapter[cinst] == NULL) {
- /*
- * Oops, can't alloc memory for the board
- */
- kfree(interface);
- continue;
- }
- spin_lock_init(&sc_adapter[cinst]->lock);
-
- if (!register_isdn(interface)) {
- /*
- * Oops, couldn't register for some reason
- */
- kfree(interface);
- kfree(sc_adapter[cinst]);
- continue;
- }
-
- sc_adapter[cinst]->card = interface;
- sc_adapter[cinst]->driverId = interface->channels;
- strcpy(sc_adapter[cinst]->devicename, interface->id);
- sc_adapter[cinst]->nChannels = channels;
- sc_adapter[cinst]->ramsize = memsize;
- sc_adapter[cinst]->shmem_magic = magic;
- sc_adapter[cinst]->shmem_pgport = pgport;
- sc_adapter[cinst]->StartOnReset = 1;
-
- /*
- * Allocate channels status structures
- */
- sc_adapter[cinst]->channel = kzalloc(sizeof(bchan) * channels, GFP_KERNEL);
- if (sc_adapter[cinst]->channel == NULL) {
- /*
- * Oops, can't alloc memory for the channels
- */
- indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL); /* Fix me */
- kfree(interface);
- kfree(sc_adapter[cinst]);
- continue;
- }
-
- /*
- * Lock down the hardware resources
- */
- sc_adapter[cinst]->interrupt = irq[b];
- if (request_irq(sc_adapter[cinst]->interrupt, interrupt_handler,
- 0, interface->id,
- (void *)(unsigned long) cinst)) {
- kfree(sc_adapter[cinst]->channel);
- indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL); /* Fix me */
- kfree(interface);
- kfree(sc_adapter[cinst]);
- continue;
-
- }
- sc_adapter[cinst]->iobase = io[b];
- for (i = 0; i < MAX_IO_REGS - 1; i++) {
- sc_adapter[cinst]->ioport[i] = io[b] + i * 0x400;
- request_region(sc_adapter[cinst]->ioport[i], 1,
- interface->id);
- pr_debug("Requesting I/O Port %#x\n",
- sc_adapter[cinst]->ioport[i]);
- }
- sc_adapter[cinst]->ioport[IRQ_SELECT] = io[b] + 0x2;
- request_region(sc_adapter[cinst]->ioport[IRQ_SELECT], 1,
- interface->id);
- pr_debug("Requesting I/O Port %#x\n",
- sc_adapter[cinst]->ioport[IRQ_SELECT]);
- sc_adapter[cinst]->rambase = ram[b];
- request_region(sc_adapter[cinst]->rambase, SRAM_PAGESIZE,
- interface->id);
-
- pr_info(" %s (%d) - %s %d channels IRQ %d, I/O Base 0x%x, RAM Base 0x%lx\n",
- sc_adapter[cinst]->devicename,
- sc_adapter[cinst]->driverId,
- boardname[model], channels, irq[b], io[b], ram[b]);
-
- /*
- * reset the adapter to put things in motion
- */
- reset(cinst);
-
- cinst++;
- status = 0;
- }
- if (status)
- pr_info("Failed to find any adapters, driver unloaded\n");
- return status;
-}
-
-static void __exit sc_exit(void)
-{
- int i, j;
-
- for (i = 0; i < cinst; i++) {
- pr_debug("Cleaning up after adapter %d\n", i);
- /*
- * kill the timers
- */
- del_timer_sync(&(sc_adapter[i]->reset_timer));
- del_timer_sync(&(sc_adapter[i]->stat_timer));
-
- /*
- * Tell I4L we're toast
- */
- indicate_status(i, ISDN_STAT_STOP, 0, NULL);
- indicate_status(i, ISDN_STAT_UNLOAD, 0, NULL);
-
- /*
- * Release shared RAM
- */
- release_region(sc_adapter[i]->rambase, SRAM_PAGESIZE);
-
- /*
- * Release the IRQ
- */
- free_irq(sc_adapter[i]->interrupt, NULL);
-
- /*
- * Reset for a clean start
- */
- outb(0xFF, sc_adapter[i]->ioport[SFT_RESET]);
-
- /*
- * Release the I/O Port regions
- */
- for (j = 0; j < MAX_IO_REGS - 1; j++) {
- release_region(sc_adapter[i]->ioport[j], 1);
- pr_debug("Releasing I/O Port %#x\n",
- sc_adapter[i]->ioport[j]);
- }
- release_region(sc_adapter[i]->ioport[IRQ_SELECT], 1);
- pr_debug("Releasing I/O Port %#x\n",
- sc_adapter[i]->ioport[IRQ_SELECT]);
-
- /*
- * Release any memory we alloced
- */
- kfree(sc_adapter[i]->channel);
- kfree(sc_adapter[i]->card);
- kfree(sc_adapter[i]);
- }
- pr_info("SpellCaster ISA ISDN Adapter Driver Unloaded.\n");
-}
-
-static int identify_board(unsigned long rambase, unsigned int iobase)
-{
- unsigned int pgport;
- unsigned long sig;
- DualPortMemory *dpm;
- RspMessage rcvmsg;
- ReqMessage sndmsg;
- HWConfig_pl hwci;
- int x;
-
- pr_debug("Attempting to identify adapter @ 0x%lx io 0x%x\n",
- rambase, iobase);
-
- /*
- * Enable the base pointer
- */
- outb(rambase >> 12, iobase + 0x2c00);
-
- switch (rambase >> 12 & 0x0F) {
- case 0x0:
- pgport = iobase + PG0_OFFSET;
- pr_debug("Page Register offset is 0x%x\n", PG0_OFFSET);
- break;
-
- case 0x4:
- pgport = iobase + PG1_OFFSET;
- pr_debug("Page Register offset is 0x%x\n", PG1_OFFSET);
- break;
-
- case 0x8:
- pgport = iobase + PG2_OFFSET;
- pr_debug("Page Register offset is 0x%x\n", PG2_OFFSET);
- break;
-
- case 0xC:
- pgport = iobase + PG3_OFFSET;
- pr_debug("Page Register offset is 0x%x\n", PG3_OFFSET);
- break;
- default:
- pr_debug("Invalid rambase 0x%lx\n", rambase);
- return -1;
- }
-
- /*
- * Try to identify a PRI card
- */
- outb(PRI_BASEPG_VAL, pgport);
- msleep_interruptible(1000);
- sig = readl(rambase + SIG_OFFSET);
- pr_debug("Looking for a signature, got 0x%lx\n", sig);
- if (sig == SIGNATURE)
- return PRI_BOARD;
-
- /*
- * Try to identify a PRI card
- */
- outb(BRI_BASEPG_VAL, pgport);
- msleep_interruptible(1000);
- sig = readl(rambase + SIG_OFFSET);
- pr_debug("Looking for a signature, got 0x%lx\n", sig);
- if (sig == SIGNATURE)
- return BRI_BOARD;
-
- return -1;
-
- /*
- * Try to spot a card
- */
- sig = readl(rambase + SIG_OFFSET);
- pr_debug("Looking for a signature, got 0x%lx\n", sig);
- if (sig != SIGNATURE)
- return -1;
-
- dpm = (DualPortMemory *) rambase;
-
- memset(&sndmsg, 0, MSG_LEN);
- sndmsg.msg_byte_cnt = 3;
- sndmsg.type = cmReqType1;
- sndmsg.class = cmReqClass0;
- sndmsg.code = cmReqHWConfig;
- memcpy_toio(&(dpm->req_queue[dpm->req_head++]), &sndmsg, MSG_LEN);
- outb(0, iobase + 0x400);
- pr_debug("Sent HWConfig message\n");
- /*
- * Wait for the response
- */
- x = 0;
- while ((inb(iobase + FIFOSTAT_OFFSET) & RF_HAS_DATA) && x < 100) {
- schedule_timeout_interruptible(1);
- x++;
- }
- if (x == 100) {
- pr_debug("Timeout waiting for response\n");
- return -1;
- }
-
- memcpy_fromio(&rcvmsg, &(dpm->rsp_queue[dpm->rsp_tail]), MSG_LEN);
- pr_debug("Got HWConfig response, status = 0x%x\n", rcvmsg.rsp_status);
- memcpy(&hwci, &(rcvmsg.msg_data.HWCresponse), sizeof(HWConfig_pl));
- pr_debug("Hardware Config: Interface: %s, RAM Size: %ld, Serial: %s\n"
- " Part: %s, Rev: %s\n",
- hwci.st_u_sense ? "S/T" : "U", hwci.ram_size,
- hwci.serial_no, hwci.part_no, hwci.rev_no);
-
- if (!strncmp(PRI_PARTNO, hwci.part_no, 6))
- return PRI_BOARD;
- if (!strncmp(BRI_PARTNO, hwci.part_no, 6))
- return BRI_BOARD;
-
- return -1;
-}
-
-module_init(sc_init);
-module_exit(sc_exit);
diff --git a/drivers/isdn/sc/interrupt.c b/drivers/isdn/sc/interrupt.c
deleted file mode 100644
index e80cc76bc314..000000000000
--- a/drivers/isdn/sc/interrupt.c
+++ /dev/null
@@ -1,247 +0,0 @@
-/* $Id: interrupt.c,v 1.4.8.3 2001/09/23 22:24:59 kai Exp $
- *
- * Copyright (C) 1996 SpellCaster Telecommunications Inc.
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * For more information, please contact gpl-info@spellcast.com or write:
- *
- * SpellCaster Telecommunications Inc.
- * 5621 Finch Avenue East, Unit #3
- * Scarborough, Ontario Canada
- * M1B 2T9
- * +1 (416) 297-8565
- * +1 (416) 297-6433 Facsimile
- */
-
-#include "includes.h"
-#include "hardware.h"
-#include "message.h"
-#include "card.h"
-#include <linux/interrupt.h>
-
-/*
- *
- */
-irqreturn_t interrupt_handler(int dummy, void *card_inst)
-{
-
- RspMessage rcvmsg;
- int channel;
- int card = (int)(unsigned long) card_inst;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return IRQ_NONE;
- }
-
- pr_debug("%s: Entered Interrupt handler\n",
- sc_adapter[card]->devicename);
-
- /*
- * Pull all of the waiting messages off the response queue
- */
- while (!receivemessage(card, &rcvmsg)) {
- /*
- * Push the message to the adapter structure for
- * send_and_receive to snoop
- */
- if (sc_adapter[card]->want_async_messages)
- memcpy(&(sc_adapter[card]->async_msg),
- &rcvmsg, sizeof(RspMessage));
-
- channel = (unsigned int) rcvmsg.phy_link_no;
-
- /*
- * Trap Invalid request messages
- */
- if (IS_CM_MESSAGE(rcvmsg, 0, 0, Invalid)) {
- pr_debug("%s: Invalid request Message, rsp_status = %d\n",
- sc_adapter[card]->devicename,
- rcvmsg.rsp_status);
- break;
- }
-
- /*
- * Check for a linkRead message
- */
- if (IS_CE_MESSAGE(rcvmsg, Lnk, 1, Read))
- {
- pr_debug("%s: Received packet 0x%x bytes long at 0x%lx\n",
- sc_adapter[card]->devicename,
- rcvmsg.msg_data.response.msg_len,
- rcvmsg.msg_data.response.buff_offset);
- rcvpkt(card, &rcvmsg);
- continue;
-
- }
-
- /*
- * Handle a write acknoledgement
- */
- if (IS_CE_MESSAGE(rcvmsg, Lnk, 1, Write)) {
- pr_debug("%s: Packet Send ACK on channel %d\n",
- sc_adapter[card]->devicename,
- rcvmsg.phy_link_no);
- sc_adapter[card]->channel[rcvmsg.phy_link_no - 1].free_sendbufs++;
- continue;
- }
-
- /*
- * Handle a connection message
- */
- if (IS_CE_MESSAGE(rcvmsg, Phy, 1, Connect))
- {
- unsigned int callid;
- setup_parm setup;
- pr_debug("%s: Connect message: line %d: status %d: cause 0x%x\n",
- sc_adapter[card]->devicename,
- rcvmsg.phy_link_no,
- rcvmsg.rsp_status,
- rcvmsg.msg_data.byte_array[2]);
-
- memcpy(&callid, rcvmsg.msg_data.byte_array, sizeof(int));
- if (callid >= 0x8000 && callid <= 0xFFFF)
- {
- pr_debug("%s: Got Dial-Out Rsp\n",
- sc_adapter[card]->devicename);
- indicate_status(card, ISDN_STAT_DCONN,
- (unsigned long)rcvmsg.phy_link_no - 1, NULL);
-
- }
- else if (callid >= 0x0000 && callid <= 0x7FFF)
- {
- int len;
-
- pr_debug("%s: Got Incoming Call\n",
- sc_adapter[card]->devicename);
- len = strlcpy(setup.phone, &(rcvmsg.msg_data.byte_array[4]),
- sizeof(setup.phone));
- if (len >= sizeof(setup.phone))
- continue;
- len = strlcpy(setup.eazmsn,
- sc_adapter[card]->channel[rcvmsg.phy_link_no - 1].dn,
- sizeof(setup.eazmsn));
- if (len >= sizeof(setup.eazmsn))
- continue;
- setup.si1 = 7;
- setup.si2 = 0;
- setup.plan = 0;
- setup.screen = 0;
-
- indicate_status(card, ISDN_STAT_ICALL, (unsigned long)rcvmsg.phy_link_no - 1, (char *)&setup);
- indicate_status(card, ISDN_STAT_DCONN, (unsigned long)rcvmsg.phy_link_no - 1, NULL);
- }
- continue;
- }
-
- /*
- * Handle a disconnection message
- */
- if (IS_CE_MESSAGE(rcvmsg, Phy, 1, Disconnect))
- {
- pr_debug("%s: disconnect message: line %d: status %d: cause 0x%x\n",
- sc_adapter[card]->devicename,
- rcvmsg.phy_link_no,
- rcvmsg.rsp_status,
- rcvmsg.msg_data.byte_array[2]);
-
- indicate_status(card, ISDN_STAT_BHUP, (unsigned long)rcvmsg.phy_link_no - 1, NULL);
- indicate_status(card, ISDN_STAT_DHUP, (unsigned long)rcvmsg.phy_link_no - 1, NULL);
- continue;
-
- }
-
- /*
- * Handle a startProc engine up message
- */
- if (IS_CM_MESSAGE(rcvmsg, 5, 0, MiscEngineUp)) {
- pr_debug("%s: Received EngineUp message\n",
- sc_adapter[card]->devicename);
- sc_adapter[card]->EngineUp = 1;
- sendmessage(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetMyNumber, 1, 0, NULL);
- sendmessage(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetMyNumber, 2, 0, NULL);
- init_timer(&sc_adapter[card]->stat_timer);
- sc_adapter[card]->stat_timer.function = check_phystat;
- sc_adapter[card]->stat_timer.data = card;
- sc_adapter[card]->stat_timer.expires = jiffies + CHECKSTAT_TIME;
- add_timer(&sc_adapter[card]->stat_timer);
- continue;
- }
-
- /*
- * Start proc response
- */
- if (IS_CM_MESSAGE(rcvmsg, 2, 0, StartProc)) {
- pr_debug("%s: StartProc Response Status %d\n",
- sc_adapter[card]->devicename,
- rcvmsg.rsp_status);
- continue;
- }
-
- /*
- * Handle a GetMyNumber Rsp
- */
- if (IS_CE_MESSAGE(rcvmsg, Call, 0, GetMyNumber)) {
- strlcpy(sc_adapter[card]->channel[rcvmsg.phy_link_no - 1].dn,
- rcvmsg.msg_data.byte_array,
- sizeof(rcvmsg.msg_data.byte_array));
- continue;
- }
-
- /*
- * PhyStatus response
- */
- if (IS_CE_MESSAGE(rcvmsg, Phy, 2, Status)) {
- unsigned int b1stat, b2stat;
-
- /*
- * Covert the message data to the adapter->phystat code
- */
- b1stat = (unsigned int) rcvmsg.msg_data.byte_array[0];
- b2stat = (unsigned int) rcvmsg.msg_data.byte_array[1];
-
- sc_adapter[card]->nphystat = (b2stat >> 8) | b1stat; /* endian?? */
- pr_debug("%s: PhyStat is 0x%2x\n",
- sc_adapter[card]->devicename,
- sc_adapter[card]->nphystat);
- continue;
- }
-
-
- /*
- * Handle a GetFramFormat
- */
- if (IS_CE_MESSAGE(rcvmsg, Call, 0, GetFrameFormat)) {
- if (rcvmsg.msg_data.byte_array[0] != HDLC_PROTO) {
- unsigned int proto = HDLC_PROTO;
- /*
- * Set board format to HDLC if it wasn't already
- */
- pr_debug("%s: current frame format: 0x%x, will change to HDLC\n",
- sc_adapter[card]->devicename,
- rcvmsg.msg_data.byte_array[0]);
- sendmessage(card, CEPID, ceReqTypeCall,
- ceReqClass0,
- ceReqCallSetFrameFormat,
- (unsigned char)channel + 1,
- 1, &proto);
- }
- continue;
- }
-
- /*
- * Hmm...
- */
- pr_debug("%s: Received unhandled message (%d,%d,%d) link %d\n",
- sc_adapter[card]->devicename,
- rcvmsg.type, rcvmsg.class, rcvmsg.code,
- rcvmsg.phy_link_no);
-
- } /* while */
-
- pr_debug("%s: Exiting Interrupt Handler\n",
- sc_adapter[card]->devicename);
- return IRQ_HANDLED;
-}
diff --git a/drivers/isdn/sc/ioctl.c b/drivers/isdn/sc/ioctl.c
deleted file mode 100644
index e63983aa1d27..000000000000
--- a/drivers/isdn/sc/ioctl.c
+++ /dev/null
@@ -1,582 +0,0 @@
-/*
- * Copyright (C) 1996 SpellCaster Telecommunications Inc.
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- */
-
-#include "includes.h"
-#include "hardware.h"
-#include "message.h"
-#include "card.h"
-#include "scioc.h"
-
-static int GetStatus(int card, boardInfo *);
-
-/*
- * Process private IOCTL messages (typically from scctrl)
- */
-int sc_ioctl(int card, scs_ioctl *data)
-{
- int status;
- RspMessage *rcvmsg;
- char *spid;
- char *dn;
- char switchtype;
- char speed;
-
- rcvmsg = kmalloc(sizeof(RspMessage), GFP_KERNEL);
- if (!rcvmsg)
- return -ENOMEM;
-
- switch (data->command) {
- case SCIOCRESET: /* Perform a hard reset of the adapter */
- {
- pr_debug("%s: SCIOCRESET: ioctl received\n",
- sc_adapter[card]->devicename);
- sc_adapter[card]->StartOnReset = 0;
- kfree(rcvmsg);
- return reset(card);
- }
-
- case SCIOCLOAD:
- {
- char *srec;
-
- srec = kmalloc(SCIOC_SRECSIZE, GFP_KERNEL);
- if (!srec) {
- kfree(rcvmsg);
- return -ENOMEM;
- }
- pr_debug("%s: SCIOLOAD: ioctl received\n",
- sc_adapter[card]->devicename);
- if (sc_adapter[card]->EngineUp) {
- pr_debug("%s: SCIOCLOAD: command failed, LoadProc while engine running.\n",
- sc_adapter[card]->devicename);
- kfree(rcvmsg);
- kfree(srec);
- return -1;
- }
-
- /*
- * Get the SRec from user space
- */
- if (copy_from_user(srec, data->dataptr, SCIOC_SRECSIZE)) {
- kfree(rcvmsg);
- kfree(srec);
- return -EFAULT;
- }
-
- status = send_and_receive(card, CMPID, cmReqType2, cmReqClass0, cmReqLoadProc,
- 0, SCIOC_SRECSIZE, srec, rcvmsg, SAR_TIMEOUT);
- kfree(rcvmsg);
- kfree(srec);
-
- if (status) {
- pr_debug("%s: SCIOCLOAD: command failed, status = %d\n",
- sc_adapter[card]->devicename, status);
- return -1;
- }
- else {
- pr_debug("%s: SCIOCLOAD: command successful\n",
- sc_adapter[card]->devicename);
- return 0;
- }
- }
-
- case SCIOCSTART:
- {
- kfree(rcvmsg);
- pr_debug("%s: SCIOSTART: ioctl received\n",
- sc_adapter[card]->devicename);
- if (sc_adapter[card]->EngineUp) {
- pr_debug("%s: SCIOCSTART: command failed, engine already running.\n",
- sc_adapter[card]->devicename);
- return -1;
- }
-
- sc_adapter[card]->StartOnReset = 1;
- startproc(card);
- return 0;
- }
-
- case SCIOCSETSWITCH:
- {
- pr_debug("%s: SCIOSETSWITCH: ioctl received\n",
- sc_adapter[card]->devicename);
-
- /*
- * Get the switch type from user space
- */
- if (copy_from_user(&switchtype, data->dataptr, sizeof(char))) {
- kfree(rcvmsg);
- return -EFAULT;
- }
-
- pr_debug("%s: SCIOCSETSWITCH: setting switch type to %d\n",
- sc_adapter[card]->devicename,
- switchtype);
- status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallSetSwitchType,
- 0, sizeof(char), &switchtype, rcvmsg, SAR_TIMEOUT);
- if (!status && !(rcvmsg->rsp_status)) {
- pr_debug("%s: SCIOCSETSWITCH: command successful\n",
- sc_adapter[card]->devicename);
- kfree(rcvmsg);
- return 0;
- }
- else {
- pr_debug("%s: SCIOCSETSWITCH: command failed (status = %d)\n",
- sc_adapter[card]->devicename, status);
- kfree(rcvmsg);
- return status;
- }
- }
-
- case SCIOCGETSWITCH:
- {
- pr_debug("%s: SCIOGETSWITCH: ioctl received\n",
- sc_adapter[card]->devicename);
-
- /*
- * Get the switch type from the board
- */
- status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
- ceReqCallGetSwitchType, 0, 0, NULL, rcvmsg, SAR_TIMEOUT);
- if (!status && !(rcvmsg->rsp_status)) {
- pr_debug("%s: SCIOCGETSWITCH: command successful\n",
- sc_adapter[card]->devicename);
- }
- else {
- pr_debug("%s: SCIOCGETSWITCH: command failed (status = %d)\n",
- sc_adapter[card]->devicename, status);
- kfree(rcvmsg);
- return status;
- }
-
- switchtype = rcvmsg->msg_data.byte_array[0];
-
- /*
- * Package the switch type and send to user space
- */
- if (copy_to_user(data->dataptr, &switchtype,
- sizeof(char))) {
- kfree(rcvmsg);
- return -EFAULT;
- }
-
- kfree(rcvmsg);
- return 0;
- }
-
- case SCIOCGETSPID:
- {
- pr_debug("%s: SCIOGETSPID: ioctl received\n",
- sc_adapter[card]->devicename);
-
- spid = kzalloc(SCIOC_SPIDSIZE, GFP_KERNEL);
- if (!spid) {
- kfree(rcvmsg);
- return -ENOMEM;
- }
- /*
- * Get the spid from the board
- */
- status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetSPID,
- data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT);
- if (!status) {
- pr_debug("%s: SCIOCGETSPID: command successful\n",
- sc_adapter[card]->devicename);
- } else {
- pr_debug("%s: SCIOCGETSPID: command failed (status = %d)\n",
- sc_adapter[card]->devicename, status);
- kfree(spid);
- kfree(rcvmsg);
- return status;
- }
- strlcpy(spid, rcvmsg->msg_data.byte_array, SCIOC_SPIDSIZE);
-
- /*
- * Package the switch type and send to user space
- */
- if (copy_to_user(data->dataptr, spid, SCIOC_SPIDSIZE)) {
- kfree(spid);
- kfree(rcvmsg);
- return -EFAULT;
- }
-
- kfree(spid);
- kfree(rcvmsg);
- return 0;
- }
-
- case SCIOCSETSPID:
- {
- pr_debug("%s: DCBIOSETSPID: ioctl received\n",
- sc_adapter[card]->devicename);
-
- /*
- * Get the spid from user space
- */
- spid = memdup_user(data->dataptr, SCIOC_SPIDSIZE);
- if (IS_ERR(spid)) {
- kfree(rcvmsg);
- return PTR_ERR(spid);
- }
-
- pr_debug("%s: SCIOCSETSPID: setting channel %d spid to %s\n",
- sc_adapter[card]->devicename, data->channel, spid);
- status = send_and_receive(card, CEPID, ceReqTypeCall,
- ceReqClass0, ceReqCallSetSPID, data->channel,
- strlen(spid), spid, rcvmsg, SAR_TIMEOUT);
- if (!status && !(rcvmsg->rsp_status)) {
- pr_debug("%s: SCIOCSETSPID: command successful\n",
- sc_adapter[card]->devicename);
- kfree(rcvmsg);
- kfree(spid);
- return 0;
- }
- else {
- pr_debug("%s: SCIOCSETSPID: command failed (status = %d)\n",
- sc_adapter[card]->devicename, status);
- kfree(rcvmsg);
- kfree(spid);
- return status;
- }
- }
-
- case SCIOCGETDN:
- {
- pr_debug("%s: SCIOGETDN: ioctl received\n",
- sc_adapter[card]->devicename);
-
- /*
- * Get the dn from the board
- */
- status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetMyNumber,
- data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT);
- if (!status) {
- pr_debug("%s: SCIOCGETDN: command successful\n",
- sc_adapter[card]->devicename);
- }
- else {
- pr_debug("%s: SCIOCGETDN: command failed (status = %d)\n",
- sc_adapter[card]->devicename, status);
- kfree(rcvmsg);
- return status;
- }
-
- dn = kzalloc(SCIOC_DNSIZE, GFP_KERNEL);
- if (!dn) {
- kfree(rcvmsg);
- return -ENOMEM;
- }
- strlcpy(dn, rcvmsg->msg_data.byte_array, SCIOC_DNSIZE);
- kfree(rcvmsg);
-
- /*
- * Package the dn and send to user space
- */
- if (copy_to_user(data->dataptr, dn, SCIOC_DNSIZE)) {
- kfree(dn);
- return -EFAULT;
- }
- kfree(dn);
- return 0;
- }
-
- case SCIOCSETDN:
- {
- pr_debug("%s: SCIOSETDN: ioctl received\n",
- sc_adapter[card]->devicename);
-
- /*
- * Get the spid from user space
- */
- dn = memdup_user(data->dataptr, SCIOC_DNSIZE);
- if (IS_ERR(dn)) {
- kfree(rcvmsg);
- return PTR_ERR(dn);
- }
-
- pr_debug("%s: SCIOCSETDN: setting channel %d dn to %s\n",
- sc_adapter[card]->devicename, data->channel, dn);
- status = send_and_receive(card, CEPID, ceReqTypeCall,
- ceReqClass0, ceReqCallSetMyNumber, data->channel,
- strlen(dn), dn, rcvmsg, SAR_TIMEOUT);
- if (!status && !(rcvmsg->rsp_status)) {
- pr_debug("%s: SCIOCSETDN: command successful\n",
- sc_adapter[card]->devicename);
- kfree(rcvmsg);
- kfree(dn);
- return 0;
- }
- else {
- pr_debug("%s: SCIOCSETDN: command failed (status = %d)\n",
- sc_adapter[card]->devicename, status);
- kfree(rcvmsg);
- kfree(dn);
- return status;
- }
- }
-
- case SCIOCTRACE:
-
- pr_debug("%s: SCIOTRACE: ioctl received\n",
- sc_adapter[card]->devicename);
-/* sc_adapter[card]->trace = !sc_adapter[card]->trace;
- pr_debug("%s: SCIOCTRACE: tracing turned %s\n",
- sc_adapter[card]->devicename,
- sc_adapter[card]->trace ? "ON" : "OFF"); */
- break;
-
- case SCIOCSTAT:
- {
- boardInfo *bi;
-
- pr_debug("%s: SCIOSTAT: ioctl received\n",
- sc_adapter[card]->devicename);
-
- bi = kzalloc(sizeof(boardInfo), GFP_KERNEL);
- if (!bi) {
- kfree(rcvmsg);
- return -ENOMEM;
- }
-
- kfree(rcvmsg);
- GetStatus(card, bi);
-
- if (copy_to_user(data->dataptr, bi, sizeof(boardInfo))) {
- kfree(bi);
- return -EFAULT;
- }
-
- kfree(bi);
- return 0;
- }
-
- case SCIOCGETSPEED:
- {
- pr_debug("%s: SCIOGETSPEED: ioctl received\n",
- sc_adapter[card]->devicename);
-
- /*
- * Get the speed from the board
- */
- status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
- ceReqCallGetCallType, data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT);
- if (!status && !(rcvmsg->rsp_status)) {
- pr_debug("%s: SCIOCGETSPEED: command successful\n",
- sc_adapter[card]->devicename);
- }
- else {
- pr_debug("%s: SCIOCGETSPEED: command failed (status = %d)\n",
- sc_adapter[card]->devicename, status);
- kfree(rcvmsg);
- return status;
- }
-
- speed = rcvmsg->msg_data.byte_array[0];
-
- kfree(rcvmsg);
-
- /*
- * Package the switch type and send to user space
- */
-
- if (copy_to_user(data->dataptr, &speed, sizeof(char)))
- return -EFAULT;
-
- return 0;
- }
-
- case SCIOCSETSPEED:
- pr_debug("%s: SCIOCSETSPEED: ioctl received\n",
- sc_adapter[card]->devicename);
- break;
-
- case SCIOCLOOPTST:
- pr_debug("%s: SCIOCLOOPTST: ioctl received\n",
- sc_adapter[card]->devicename);
- break;
-
- default:
- kfree(rcvmsg);
- return -1;
- }
-
- kfree(rcvmsg);
- return 0;
-}
-
-static int GetStatus(int card, boardInfo *bi)
-{
- RspMessage rcvmsg;
- int i, status;
-
- /*
- * Fill in some of the basic info about the board
- */
- bi->modelid = sc_adapter[card]->model;
- strcpy(bi->serial_no, sc_adapter[card]->hwconfig.serial_no);
- strcpy(bi->part_no, sc_adapter[card]->hwconfig.part_no);
- bi->iobase = sc_adapter[card]->iobase;
- bi->rambase = sc_adapter[card]->rambase;
- bi->irq = sc_adapter[card]->interrupt;
- bi->ramsize = sc_adapter[card]->hwconfig.ram_size;
- bi->interface = sc_adapter[card]->hwconfig.st_u_sense;
- strcpy(bi->load_ver, sc_adapter[card]->load_ver);
- strcpy(bi->proc_ver, sc_adapter[card]->proc_ver);
-
- /*
- * Get the current PhyStats and LnkStats
- */
- status = send_and_receive(card, CEPID, ceReqTypePhy, ceReqClass2,
- ceReqPhyStatus, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
- if (!status) {
- if (sc_adapter[card]->model < PRI_BOARD) {
- bi->l1_status = rcvmsg.msg_data.byte_array[2];
- for (i = 0; i < BRI_CHANNELS; i++)
- bi->status.bristats[i].phy_stat =
- rcvmsg.msg_data.byte_array[i];
- }
- else {
- bi->l1_status = rcvmsg.msg_data.byte_array[0];
- bi->l2_status = rcvmsg.msg_data.byte_array[1];
- for (i = 0; i < PRI_CHANNELS; i++)
- bi->status.pristats[i].phy_stat =
- rcvmsg.msg_data.byte_array[i + 2];
- }
- }
-
- /*
- * Get the call types for each channel
- */
- for (i = 0; i < sc_adapter[card]->nChannels; i++) {
- status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
- ceReqCallGetCallType, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
- if (!status) {
- if (sc_adapter[card]->model == PRI_BOARD) {
- bi->status.pristats[i].call_type =
- rcvmsg.msg_data.byte_array[0];
- }
- else {
- bi->status.bristats[i].call_type =
- rcvmsg.msg_data.byte_array[0];
- }
- }
- }
-
- /*
- * If PRI, get the call states and service states for each channel
- */
- if (sc_adapter[card]->model == PRI_BOARD) {
- /*
- * Get the call states
- */
- status = send_and_receive(card, CEPID, ceReqTypeStat, ceReqClass2,
- ceReqPhyChCallState, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
- if (!status) {
- for (i = 0; i < PRI_CHANNELS; i++)
- bi->status.pristats[i].call_state =
- rcvmsg.msg_data.byte_array[i];
- }
-
- /*
- * Get the service states
- */
- status = send_and_receive(card, CEPID, ceReqTypeStat, ceReqClass2,
- ceReqPhyChServState, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
- if (!status) {
- for (i = 0; i < PRI_CHANNELS; i++)
- bi->status.pristats[i].serv_state =
- rcvmsg.msg_data.byte_array[i];
- }
-
- /*
- * Get the link stats for the channels
- */
- for (i = 1; i <= PRI_CHANNELS; i++) {
- status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
- ceReqLnkGetStats, i, 0, NULL, &rcvmsg, SAR_TIMEOUT);
- if (!status) {
- bi->status.pristats[i - 1].link_stats.tx_good =
- (unsigned long)rcvmsg.msg_data.byte_array[0];
- bi->status.pristats[i - 1].link_stats.tx_bad =
- (unsigned long)rcvmsg.msg_data.byte_array[4];
- bi->status.pristats[i - 1].link_stats.rx_good =
- (unsigned long)rcvmsg.msg_data.byte_array[8];
- bi->status.pristats[i - 1].link_stats.rx_bad =
- (unsigned long)rcvmsg.msg_data.byte_array[12];
- }
- }
-
- /*
- * Link stats for the D channel
- */
- status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
- ceReqLnkGetStats, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
- if (!status) {
- bi->dch_stats.tx_good = (unsigned long)rcvmsg.msg_data.byte_array[0];
- bi->dch_stats.tx_bad = (unsigned long)rcvmsg.msg_data.byte_array[4];
- bi->dch_stats.rx_good = (unsigned long)rcvmsg.msg_data.byte_array[8];
- bi->dch_stats.rx_bad = (unsigned long)rcvmsg.msg_data.byte_array[12];
- }
-
- return 0;
- }
-
- /*
- * If BRI or POTS, Get SPID, DN and call types for each channel
- */
-
- /*
- * Get the link stats for the channels
- */
- status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
- ceReqLnkGetStats, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
- if (!status) {
- bi->dch_stats.tx_good = (unsigned long)rcvmsg.msg_data.byte_array[0];
- bi->dch_stats.tx_bad = (unsigned long)rcvmsg.msg_data.byte_array[4];
- bi->dch_stats.rx_good = (unsigned long)rcvmsg.msg_data.byte_array[8];
- bi->dch_stats.rx_bad = (unsigned long)rcvmsg.msg_data.byte_array[12];
- bi->status.bristats[0].link_stats.tx_good =
- (unsigned long)rcvmsg.msg_data.byte_array[16];
- bi->status.bristats[0].link_stats.tx_bad =
- (unsigned long)rcvmsg.msg_data.byte_array[20];
- bi->status.bristats[0].link_stats.rx_good =
- (unsigned long)rcvmsg.msg_data.byte_array[24];
- bi->status.bristats[0].link_stats.rx_bad =
- (unsigned long)rcvmsg.msg_data.byte_array[28];
- bi->status.bristats[1].link_stats.tx_good =
- (unsigned long)rcvmsg.msg_data.byte_array[32];
- bi->status.bristats[1].link_stats.tx_bad =
- (unsigned long)rcvmsg.msg_data.byte_array[36];
- bi->status.bristats[1].link_stats.rx_good =
- (unsigned long)rcvmsg.msg_data.byte_array[40];
- bi->status.bristats[1].link_stats.rx_bad =
- (unsigned long)rcvmsg.msg_data.byte_array[44];
- }
-
- /*
- * Get the SPIDs
- */
- for (i = 0; i < BRI_CHANNELS; i++) {
- status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
- ceReqCallGetSPID, i + 1, 0, NULL, &rcvmsg, SAR_TIMEOUT);
- if (!status)
- strcpy(bi->status.bristats[i].spid, rcvmsg.msg_data.byte_array);
- }
-
- /*
- * Get the DNs
- */
- for (i = 0; i < BRI_CHANNELS; i++) {
- status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
- ceReqCallGetMyNumber, i + 1, 0, NULL, &rcvmsg, SAR_TIMEOUT);
- if (!status)
- strcpy(bi->status.bristats[i].dn, rcvmsg.msg_data.byte_array);
- }
-
- return 0;
-}
diff --git a/drivers/isdn/sc/message.c b/drivers/isdn/sc/message.c
deleted file mode 100644
index 9679a1902b32..000000000000
--- a/drivers/isdn/sc/message.c
+++ /dev/null
@@ -1,230 +0,0 @@
-/* $Id: message.c,v 1.5.8.2 2001/09/23 22:24:59 kai Exp $
- *
- * functions for sending and receiving control messages
- *
- * Copyright (C) 1996 SpellCaster Telecommunications Inc.
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * For more information, please contact gpl-info@spellcast.com or write:
- *
- * SpellCaster Telecommunications Inc.
- * 5621 Finch Avenue East, Unit #3
- * Scarborough, Ontario Canada
- * M1B 2T9
- * +1 (416) 297-8565
- * +1 (416) 297-6433 Facsimile
- */
-#include <linux/sched.h>
-#include "includes.h"
-#include "hardware.h"
-#include "message.h"
-#include "card.h"
-
-/*
- * receive a message from the board
- */
-int receivemessage(int card, RspMessage *rspmsg)
-{
- DualPortMemory *dpm;
- unsigned long flags;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -EINVAL;
- }
-
- pr_debug("%s: Entered receivemessage\n",
- sc_adapter[card]->devicename);
-
- /*
- * See if there are messages waiting
- */
- if (inb(sc_adapter[card]->ioport[FIFO_STATUS]) & RF_HAS_DATA) {
- /*
- * Map in the DPM to the base page and copy the message
- */
- spin_lock_irqsave(&sc_adapter[card]->lock, flags);
- outb((sc_adapter[card]->shmem_magic >> 14) | 0x80,
- sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
- dpm = (DualPortMemory *) sc_adapter[card]->rambase;
- memcpy_fromio(rspmsg, &(dpm->rsp_queue[dpm->rsp_tail]),
- MSG_LEN);
- dpm->rsp_tail = (dpm->rsp_tail + 1) % MAX_MESSAGES;
- inb(sc_adapter[card]->ioport[FIFO_READ]);
- spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
- /*
- * Tell the board that the message is received
- */
- pr_debug("%s: Received Message seq:%d pid:%d time:%d cmd:%d "
- "cnt:%d (type,class,code):(%d,%d,%d) "
- "link:%d stat:0x%x\n",
- sc_adapter[card]->devicename,
- rspmsg->sequence_no,
- rspmsg->process_id,
- rspmsg->time_stamp,
- rspmsg->cmd_sequence_no,
- rspmsg->msg_byte_cnt,
- rspmsg->type,
- rspmsg->class,
- rspmsg->code,
- rspmsg->phy_link_no,
- rspmsg->rsp_status);
-
- return 0;
- }
- return -ENOMSG;
-}
-
-/*
- * send a message to the board
- */
-int sendmessage(int card,
- unsigned int procid,
- unsigned int type,
- unsigned int class,
- unsigned int code,
- unsigned int link,
- unsigned int data_len,
- unsigned int *data)
-{
- DualPortMemory *dpm;
- ReqMessage sndmsg;
- unsigned long flags;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -EINVAL;
- }
-
- /*
- * Make sure we only send CEPID messages when the engine is up
- * and CMPID messages when it is down
- */
- if (sc_adapter[card]->EngineUp && procid == CMPID) {
- pr_debug("%s: Attempt to send CM message with engine up\n",
- sc_adapter[card]->devicename);
- return -ESRCH;
- }
-
- if (!sc_adapter[card]->EngineUp && procid == CEPID) {
- pr_debug("%s: Attempt to send CE message with engine down\n",
- sc_adapter[card]->devicename);
- return -ESRCH;
- }
-
- memset(&sndmsg, 0, MSG_LEN);
- sndmsg.msg_byte_cnt = 4;
- sndmsg.type = type;
- sndmsg.class = class;
- sndmsg.code = code;
- sndmsg.phy_link_no = link;
-
- if (data_len > 0) {
- if (data_len > MSG_DATA_LEN)
- data_len = MSG_DATA_LEN;
- memcpy(&(sndmsg.msg_data), data, data_len);
- sndmsg.msg_byte_cnt = data_len + 8;
- }
-
- sndmsg.process_id = procid;
- sndmsg.sequence_no = sc_adapter[card]->seq_no++ % 256;
-
- /*
- * wait for an empty slot in the queue
- */
- while (!(inb(sc_adapter[card]->ioport[FIFO_STATUS]) & WF_NOT_FULL))
- udelay(1);
-
- /*
- * Disable interrupts and map in shared memory
- */
- spin_lock_irqsave(&sc_adapter[card]->lock, flags);
- outb((sc_adapter[card]->shmem_magic >> 14) | 0x80,
- sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
- dpm = (DualPortMemory *) sc_adapter[card]->rambase; /* Fix me */
- memcpy_toio(&(dpm->req_queue[dpm->req_head]), &sndmsg, MSG_LEN);
- dpm->req_head = (dpm->req_head + 1) % MAX_MESSAGES;
- outb(sndmsg.sequence_no, sc_adapter[card]->ioport[FIFO_WRITE]);
- spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
-
- pr_debug("%s: Sent Message seq:%d pid:%d time:%d "
- "cnt:%d (type,class,code):(%d,%d,%d) "
- "link:%d\n ",
- sc_adapter[card]->devicename,
- sndmsg.sequence_no,
- sndmsg.process_id,
- sndmsg.time_stamp,
- sndmsg.msg_byte_cnt,
- sndmsg.type,
- sndmsg.class,
- sndmsg.code,
- sndmsg.phy_link_no);
-
- return 0;
-}
-
-int send_and_receive(int card,
- unsigned int procid,
- unsigned char type,
- unsigned char class,
- unsigned char code,
- unsigned char link,
- unsigned char data_len,
- unsigned char *data,
- RspMessage *mesgdata,
- int timeout)
-{
- int retval;
- int tries;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return -EINVAL;
- }
-
- sc_adapter[card]->want_async_messages = 1;
- retval = sendmessage(card, procid, type, class, code, link,
- data_len, (unsigned int *) data);
-
- if (retval) {
- pr_debug("%s: SendMessage failed in SAR\n",
- sc_adapter[card]->devicename);
- sc_adapter[card]->want_async_messages = 0;
- return -EIO;
- }
-
- tries = 0;
- /* wait for the response */
- while (tries < timeout) {
- schedule_timeout_interruptible(1);
-
- pr_debug("SAR waiting..\n");
-
- /*
- * See if we got our message back
- */
- if ((sc_adapter[card]->async_msg.type == type) &&
- (sc_adapter[card]->async_msg.class == class) &&
- (sc_adapter[card]->async_msg.code == code) &&
- (sc_adapter[card]->async_msg.phy_link_no == link)) {
-
- /*
- * Got it!
- */
- pr_debug("%s: Got ASYNC message\n",
- sc_adapter[card]->devicename);
- memcpy(mesgdata, &(sc_adapter[card]->async_msg),
- sizeof(RspMessage));
- sc_adapter[card]->want_async_messages = 0;
- return 0;
- }
-
- tries++;
- }
-
- pr_debug("%s: SAR message timeout\n", sc_adapter[card]->devicename);
- sc_adapter[card]->want_async_messages = 0;
- return -ETIME;
-}
diff --git a/drivers/isdn/sc/message.h b/drivers/isdn/sc/message.h
deleted file mode 100644
index 5e6f4a5c15f8..000000000000
--- a/drivers/isdn/sc/message.h
+++ /dev/null
@@ -1,245 +0,0 @@
-/* $Id: message.h,v 1.1.10.1 2001/09/23 22:24:59 kai Exp $
- *
- * Copyright (C) 1996 SpellCaster Telecommunications Inc.
- *
- * structures, macros and defines useful for sending
- * messages to the adapter
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * For more information, please contact gpl-info@spellcast.com or write:
- *
- * SpellCaster Telecommunications Inc.
- * 5621 Finch Avenue East, Unit #3
- * Scarborough, Ontario Canada
- * M1B 2T9
- * +1 (416) 297-8565
- * +1 (416) 297-6433 Facsimile
- */
-
-/*
- * Board message macros, defines and structures
- */
-
-#ifndef MESSAGE_H
-#define MESSAGE_H
-
-#define MAX_MESSAGES 32 /* Maximum messages that can be
- queued */
-#define MSG_DATA_LEN 48 /* Maximum size of message payload */
-#define MSG_LEN 64 /* Size of a message */
-#define CMPID 0 /* Loader message process ID */
-#define CEPID 64 /* Firmware message process ID */
-
-/*
- * Macro to determine if a message is a loader message
- */
-#define IS_CM_MESSAGE(mesg, tx, cx, dx) \
- ((mesg.type == cmRspType##tx) \
- && (mesg.class == cmRspClass##cx) \
- && (mesg.code == cmRsp##dx))
-
-/*
- * Macro to determine if a message is a firmware message
- */
-#define IS_CE_MESSAGE(mesg, tx, cx, dx) \
- ((mesg.type == ceRspType##tx) \
- && (mesg.class == ceRspClass##cx) \
- && (mesg.code == ceRsp##tx##dx))
-
-/*
- * Loader Request and Response Messages
- */
-
-/* message types */
-#define cmReqType1 1
-#define cmReqType2 2
-#define cmRspType0 0
-#define cmRspType1 1
-#define cmRspType2 2
-#define cmRspType5 5
-
-/* message classes */
-#define cmReqClass0 0
-#define cmRspClass0 0
-
-/* message codes */
-#define cmReqHWConfig 1 /* 1,0,1 */
-#define cmReqMsgLpbk 2 /* 1,0,2 */
-#define cmReqVersion 3 /* 1,0,3 */
-#define cmReqLoadProc 1 /* 2,0,1 */
-#define cmReqStartProc 2 /* 2,0,2 */
-#define cmReqReadMem 6 /* 2,0,6 */
-#define cmRspHWConfig cmReqHWConfig
-#define cmRspMsgLpbk cmReqMsgLpbk
-#define cmRspVersion cmReqVersion
-#define cmRspLoadProc cmReqLoadProc
-#define cmRspStartProc cmReqStartProc
-#define cmRspReadMem cmReqReadMem
-#define cmRspMiscEngineUp 1 /* 5,0,1 */
-#define cmRspInvalid 0 /* 0,0,0 */
-
-
-/*
- * Firmware Request and Response Messages
- */
-
-/* message types */
-#define ceReqTypePhy 1
-#define ceReqTypeLnk 2
-#define ceReqTypeCall 3
-#define ceReqTypeStat 1
-#define ceRspTypeErr 0
-#define ceRspTypePhy ceReqTypePhy
-#define ceRspTypeLnk ceReqTypeLnk
-#define ceRspTypeCall ceReqTypeCall
-#define ceRspTypeStat ceReqTypeStat
-
-/* message classes */
-#define ceReqClass0 0
-#define ceReqClass1 1
-#define ceReqClass2 2
-#define ceReqClass3 3
-#define ceRspClass0 ceReqClass0
-#define ceRspClass1 ceReqClass1
-#define ceRspClass2 ceReqClass2
-#define ceRspClass3 ceReqClass3
-
-/* message codes (B) = BRI only, (P) = PRI only, (V) = POTS only */
-#define ceReqPhyProcInfo 1 /* 1,0,1 */
-#define ceReqPhyConnect 1 /* 1,1,1 */
-#define ceReqPhyDisconnect 2 /* 1,1,2 */
-#define ceReqPhySetParams 3 /* 1,1,3 (P) */
-#define ceReqPhyGetParams 4 /* 1,1,4 (P) */
-#define ceReqPhyStatus 1 /* 1,2,1 */
-#define ceReqPhyAcfaStatus 2 /* 1,2,2 (P) */
-#define ceReqPhyChCallState 3 /* 1,2,3 (P) */
-#define ceReqPhyChServState 4 /* 1,2,4 (P) */
-#define ceReqPhyRLoopBack 1 /* 1,3,1 */
-#define ceRspPhyProcInfo ceReqPhyProcInfo
-#define ceRspPhyConnect ceReqPhyConnect
-#define ceRspPhyDisconnect ceReqPhyDisconnect
-#define ceRspPhySetParams ceReqPhySetParams
-#define ceRspPhyGetParams ceReqPhyGetParams
-#define ceRspPhyStatus ceReqPhyStatus
-#define ceRspPhyAcfaStatus ceReqPhyAcfaStatus
-#define ceRspPhyChCallState ceReqPhyChCallState
-#define ceRspPhyChServState ceReqPhyChServState
-#define ceRspPhyRLoopBack ceReqphyRLoopBack
-#define ceReqLnkSetParam 1 /* 2,0,1 */
-#define ceReqLnkGetParam 2 /* 2,0,2 */
-#define ceReqLnkGetStats 3 /* 2,0,3 */
-#define ceReqLnkWrite 1 /* 2,1,1 */
-#define ceReqLnkRead 2 /* 2,1,2 */
-#define ceReqLnkFlush 3 /* 2,1,3 */
-#define ceReqLnkWrBufTrc 4 /* 2,1,4 */
-#define ceReqLnkRdBufTrc 5 /* 2,1,5 */
-#define ceRspLnkSetParam ceReqLnkSetParam
-#define ceRspLnkGetParam ceReqLnkGetParam
-#define ceRspLnkGetStats ceReqLnkGetStats
-#define ceRspLnkWrite ceReqLnkWrite
-#define ceRspLnkRead ceReqLnkRead
-#define ceRspLnkFlush ceReqLnkFlush
-#define ceRspLnkWrBufTrc ceReqLnkWrBufTrc
-#define ceRspLnkRdBufTrc ceReqLnkRdBufTrc
-#define ceReqCallSetSwitchType 1 /* 3,0,1 */
-#define ceReqCallGetSwitchType 2 /* 3,0,2 */
-#define ceReqCallSetFrameFormat 3 /* 3,0,3 */
-#define ceReqCallGetFrameFormat 4 /* 3,0,4 */
-#define ceReqCallSetCallType 5 /* 3,0,5 */
-#define ceReqCallGetCallType 6 /* 3,0,6 */
-#define ceReqCallSetSPID 7 /* 3,0,7 (!P) */
-#define ceReqCallGetSPID 8 /* 3,0,8 (!P) */
-#define ceReqCallSetMyNumber 9 /* 3,0,9 (!P) */
-#define ceReqCallGetMyNumber 10 /* 3,0,10 (!P) */
-#define ceRspCallSetSwitchType ceReqCallSetSwitchType
-#define ceRspCallGetSwitchType ceReqCallSetSwitchType
-#define ceRspCallSetFrameFormat ceReqCallSetFrameFormat
-#define ceRspCallGetFrameFormat ceReqCallGetFrameFormat
-#define ceRspCallSetCallType ceReqCallSetCallType
-#define ceRspCallGetCallType ceReqCallGetCallType
-#define ceRspCallSetSPID ceReqCallSetSPID
-#define ceRspCallGetSPID ceReqCallGetSPID
-#define ceRspCallSetMyNumber ceReqCallSetMyNumber
-#define ceRspCallGetMyNumber ceReqCallGetMyNumber
-#define ceRspStatAcfaStatus 2
-#define ceRspStat
-#define ceRspErrError 0 /* 0,0,0 */
-
-/*
- * Call Types
- */
-#define CALLTYPE_64K 0
-#define CALLTYPE_56K 1
-#define CALLTYPE_SPEECH 2
-#define CALLTYPE_31KHZ 3
-
-/*
- * Link Level data contains a pointer to and the length of
- * a buffer in shared RAM. Used by LnkRead and LnkWrite message
- * types. Part of RspMsgStruct and ReqMsgStruct.
- */
-typedef struct {
- unsigned long buff_offset;
- unsigned short msg_len;
-} LLData;
-
-
-/*
- * Message payload template for an HWConfig message
- */
-typedef struct {
- char st_u_sense;
- char powr_sense;
- char sply_sense;
- unsigned char asic_id;
- long ram_size;
- char serial_no[13];
- char part_no[13];
- char rev_no[2];
-} HWConfig_pl;
-
-/*
- * A Message
- */
-struct message {
- unsigned char sequence_no;
- unsigned char process_id;
- unsigned char time_stamp;
- unsigned char cmd_sequence_no; /* Rsp messages only */
- unsigned char reserved1[3];
- unsigned char msg_byte_cnt;
- unsigned char type;
- unsigned char class;
- unsigned char code;
- unsigned char phy_link_no;
- unsigned char rsp_status; /* Rsp messages only */
- unsigned char reseved2[3];
- union {
- unsigned char byte_array[MSG_DATA_LEN];
- LLData response;
- HWConfig_pl HWCresponse;
- } msg_data;
-};
-
-typedef struct message ReqMessage; /* Request message */
-typedef struct message RspMessage; /* Response message */
-
-/*
- * The first 5010 bytes of shared memory contain the message queues,
- * indexes and other data. This structure is its template
- */
-typedef struct {
- volatile ReqMessage req_queue[MAX_MESSAGES];
- volatile RspMessage rsp_queue[MAX_MESSAGES];
- volatile unsigned char req_head;
- volatile unsigned char req_tail;
- volatile unsigned char rsp_head;
- volatile unsigned char rsp_tail;
- volatile unsigned long signature;
- volatile unsigned long trace_enable;
- volatile unsigned char reserved[4];
-} DualPortMemory;
-
-#endif
diff --git a/drivers/isdn/sc/packet.c b/drivers/isdn/sc/packet.c
deleted file mode 100644
index 2446957085e0..000000000000
--- a/drivers/isdn/sc/packet.c
+++ /dev/null
@@ -1,204 +0,0 @@
-/* $Id: packet.c,v 1.5.8.1 2001/09/23 22:24:59 kai Exp $
- *
- * Copyright (C) 1996 SpellCaster Telecommunications Inc.
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * For more information, please contact gpl-info@spellcast.com or write:
- *
- * SpellCaster Telecommunications Inc.
- * 5621 Finch Avenue East, Unit #3
- * Scarborough, Ontario Canada
- * M1B 2T9
- * +1 (416) 297-8565
- * +1 (416) 297-6433 Facsimile
- */
-
-#include "includes.h"
-#include "hardware.h"
-#include "message.h"
-#include "card.h"
-
-int sndpkt(int devId, int channel, int ack, struct sk_buff *data)
-{
- LLData ReqLnkWrite;
- int status;
- int card;
- unsigned long len;
-
- card = get_card_from_id(devId);
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- pr_debug("%s: sndpkt: frst = 0x%lx nxt = %d f = %d n = %d\n",
- sc_adapter[card]->devicename,
- sc_adapter[card]->channel[channel].first_sendbuf,
- sc_adapter[card]->channel[channel].next_sendbuf,
- sc_adapter[card]->channel[channel].free_sendbufs,
- sc_adapter[card]->channel[channel].num_sendbufs);
-
- if (!sc_adapter[card]->channel[channel].free_sendbufs) {
- pr_debug("%s: out of TX buffers\n",
- sc_adapter[card]->devicename);
- return -EINVAL;
- }
-
- if (data->len > BUFFER_SIZE) {
- pr_debug("%s: data overflows buffer size (data > buffer)\n",
- sc_adapter[card]->devicename);
- return -EINVAL;
- }
-
- ReqLnkWrite.buff_offset = sc_adapter[card]->channel[channel].next_sendbuf *
- BUFFER_SIZE + sc_adapter[card]->channel[channel].first_sendbuf;
- ReqLnkWrite.msg_len = data->len; /* sk_buff size */
- pr_debug("%s: writing %d bytes to buffer offset 0x%lx\n",
- sc_adapter[card]->devicename,
- ReqLnkWrite.msg_len, ReqLnkWrite.buff_offset);
- memcpy_toshmem(card, (char *)ReqLnkWrite.buff_offset, data->data, ReqLnkWrite.msg_len);
-
- /*
- * sendmessage
- */
- pr_debug("%s: sndpkt size=%d, buf_offset=0x%lx buf_indx=%d\n",
- sc_adapter[card]->devicename,
- ReqLnkWrite.msg_len, ReqLnkWrite.buff_offset,
- sc_adapter[card]->channel[channel].next_sendbuf);
-
- status = sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkWrite,
- channel + 1, sizeof(LLData), (unsigned int *)&ReqLnkWrite);
- len = data->len;
- if (status) {
- pr_debug("%s: failed to send packet, status = %d\n",
- sc_adapter[card]->devicename, status);
- return -1;
- }
- else {
- sc_adapter[card]->channel[channel].free_sendbufs--;
- sc_adapter[card]->channel[channel].next_sendbuf =
- ++sc_adapter[card]->channel[channel].next_sendbuf ==
- sc_adapter[card]->channel[channel].num_sendbufs ? 0 :
- sc_adapter[card]->channel[channel].next_sendbuf;
- pr_debug("%s: packet sent successfully\n", sc_adapter[card]->devicename);
- dev_kfree_skb(data);
- indicate_status(card, ISDN_STAT_BSENT, channel, (char *)&len);
- }
- return len;
-}
-
-void rcvpkt(int card, RspMessage *rcvmsg)
-{
- LLData newll;
- struct sk_buff *skb;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("invalid param: %d is not a valid card id\n", card);
- return;
- }
-
- switch (rcvmsg->rsp_status) {
- case 0x01:
- case 0x02:
- case 0x70:
- pr_debug("%s: error status code: 0x%x\n",
- sc_adapter[card]->devicename, rcvmsg->rsp_status);
- return;
- case 0x00:
- if (!(skb = dev_alloc_skb(rcvmsg->msg_data.response.msg_len))) {
- printk(KERN_WARNING "%s: rcvpkt out of memory, dropping packet\n",
- sc_adapter[card]->devicename);
- return;
- }
- skb_put(skb, rcvmsg->msg_data.response.msg_len);
- pr_debug("%s: getting data from offset: 0x%lx\n",
- sc_adapter[card]->devicename,
- rcvmsg->msg_data.response.buff_offset);
- memcpy_fromshmem(card,
- skb_put(skb, rcvmsg->msg_data.response.msg_len),
- (char *)rcvmsg->msg_data.response.buff_offset,
- rcvmsg->msg_data.response.msg_len);
- sc_adapter[card]->card->rcvcallb_skb(sc_adapter[card]->driverId,
- rcvmsg->phy_link_no - 1, skb);
-
- case 0x03:
- /*
- * Recycle the buffer
- */
- pr_debug("%s: buffer size : %d\n",
- sc_adapter[card]->devicename, BUFFER_SIZE);
-/* memset_shmem(card, rcvmsg->msg_data.response.buff_offset, 0, BUFFER_SIZE); */
- newll.buff_offset = rcvmsg->msg_data.response.buff_offset;
- newll.msg_len = BUFFER_SIZE;
- pr_debug("%s: recycled buffer at offset 0x%lx size %d\n",
- sc_adapter[card]->devicename,
- newll.buff_offset, newll.msg_len);
- sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkRead,
- rcvmsg->phy_link_no, sizeof(LLData), (unsigned int *)&newll);
- }
-
-}
-
-int setup_buffers(int card, int c)
-{
- unsigned int nBuffers, i, cBase;
- unsigned int buffer_size;
- LLData RcvBuffOffset;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("invalid param: %d is not a valid card id\n", card);
- return -ENODEV;
- }
-
- /*
- * Calculate the buffer offsets (send/recv/send/recv)
- */
- pr_debug("%s: setting up channel buffer space in shared RAM\n",
- sc_adapter[card]->devicename);
- buffer_size = BUFFER_SIZE;
- nBuffers = ((sc_adapter[card]->ramsize - BUFFER_BASE) / buffer_size) / 2;
- nBuffers = nBuffers > BUFFERS_MAX ? BUFFERS_MAX : nBuffers;
- pr_debug("%s: calculating buffer space: %d buffers, %d big\n",
- sc_adapter[card]->devicename,
- nBuffers, buffer_size);
- if (nBuffers < 2) {
- pr_debug("%s: not enough buffer space\n",
- sc_adapter[card]->devicename);
- return -1;
- }
- cBase = (nBuffers * buffer_size) * (c - 1);
- pr_debug("%s: channel buffer offset from shared RAM: 0x%x\n",
- sc_adapter[card]->devicename, cBase);
- sc_adapter[card]->channel[c - 1].first_sendbuf = BUFFER_BASE + cBase;
- sc_adapter[card]->channel[c - 1].num_sendbufs = nBuffers / 2;
- sc_adapter[card]->channel[c - 1].free_sendbufs = nBuffers / 2;
- sc_adapter[card]->channel[c - 1].next_sendbuf = 0;
- pr_debug("%s: send buffer setup complete: first=0x%lx n=%d f=%d, nxt=%d\n",
- sc_adapter[card]->devicename,
- sc_adapter[card]->channel[c - 1].first_sendbuf,
- sc_adapter[card]->channel[c - 1].num_sendbufs,
- sc_adapter[card]->channel[c - 1].free_sendbufs,
- sc_adapter[card]->channel[c - 1].next_sendbuf);
-
- /*
- * Prep the receive buffers
- */
- pr_debug("%s: adding %d RecvBuffers:\n",
- sc_adapter[card]->devicename, nBuffers / 2);
- for (i = 0; i < nBuffers / 2; i++) {
- RcvBuffOffset.buff_offset =
- ((sc_adapter[card]->channel[c - 1].first_sendbuf +
- (nBuffers / 2) * buffer_size) + (buffer_size * i));
- RcvBuffOffset.msg_len = buffer_size;
- pr_debug("%s: adding RcvBuffer #%d offset=0x%lx sz=%d bufsz:%d\n",
- sc_adapter[card]->devicename,
- i + 1, RcvBuffOffset.buff_offset,
- RcvBuffOffset.msg_len, buffer_size);
- sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkRead,
- c, sizeof(LLData), (unsigned int *)&RcvBuffOffset);
- }
- return 0;
-}
diff --git a/drivers/isdn/sc/scioc.h b/drivers/isdn/sc/scioc.h
deleted file mode 100644
index a50e143779e7..000000000000
--- a/drivers/isdn/sc/scioc.h
+++ /dev/null
@@ -1,110 +0,0 @@
-#ifndef __ISDN_SC_SCIOC_H__
-#define __ISDN_SC_SCIOC_H__
-
-/*
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- */
-
-/*
- * IOCTL Command Codes
- */
-#define SCIOCLOAD 0x01 /* Load a firmware record */
-#define SCIOCRESET 0x02 /* Perform hard reset */
-#define SCIOCDEBUG 0x03 /* Set debug level */
-#define SCIOCREV 0x04 /* Get driver revision(s) */
-#define SCIOCSTART 0x05 /* Start the firmware */
-#define SCIOCGETSWITCH 0x06 /* Get switch type */
-#define SCIOCSETSWITCH 0x07 /* Set switch type */
-#define SCIOCGETSPID 0x08 /* Get channel SPID */
-#define SCIOCSETSPID 0x09 /* Set channel SPID */
-#define SCIOCGETDN 0x0A /* Get channel DN */
-#define SCIOCSETDN 0x0B /* Set channel DN */
-#define SCIOCTRACE 0x0C /* Toggle trace mode */
-#define SCIOCSTAT 0x0D /* Get line status */
-#define SCIOCGETSPEED 0x0E /* Set channel speed */
-#define SCIOCSETSPEED 0x0F /* Set channel speed */
-#define SCIOCLOOPTST 0x10 /* Perform loopback test */
-
-typedef struct {
- int device;
- int channel;
- unsigned long command;
- void __user *dataptr;
-} scs_ioctl;
-
-/* Size of strings */
-#define SCIOC_SPIDSIZE 49
-#define SCIOC_DNSIZE SCIOC_SPIDSIZE
-#define SCIOC_REVSIZE SCIOC_SPIDSIZE
-#define SCIOC_SRECSIZE 49
-
-typedef struct {
- unsigned long tx_good;
- unsigned long tx_bad;
- unsigned long rx_good;
- unsigned long rx_bad;
-} ChLinkStats;
-
-typedef struct {
- char spid[49];
- char dn[49];
- char call_type;
- char phy_stat;
- ChLinkStats link_stats;
-} BRIStat;
-
-typedef BRIStat POTStat;
-
-typedef struct {
- char call_type;
- char call_state;
- char serv_state;
- char phy_stat;
- ChLinkStats link_stats;
-} PRIStat;
-
-typedef char PRIInfo;
-typedef char BRIInfo;
-typedef char POTInfo;
-
-
-typedef struct {
- char acfa_nos;
- char acfa_ais;
- char acfa_los;
- char acfa_rra;
- char acfa_slpp;
- char acfa_slpn;
- char acfa_fsrf;
-} ACFAStat;
-
-typedef struct {
- unsigned char modelid;
- char serial_no[13];
- char part_no[13];
- char load_ver[11];
- char proc_ver[11];
- int iobase;
- long rambase;
- char irq;
- long ramsize;
- char interface;
- char switch_type;
- char l1_status;
- char l2_status;
- ChLinkStats dch_stats;
- ACFAStat AcfaStats;
- union {
- PRIStat pristats[23];
- BRIStat bristats[2];
- POTStat potsstats[2];
- } status;
- union {
- PRIInfo priinfo;
- BRIInfo briinfo;
- POTInfo potsinfo;
- } info;
-} boardInfo;
-
-#endif /* __ISDN_SC_SCIOC_H__ */
diff --git a/drivers/isdn/sc/shmem.c b/drivers/isdn/sc/shmem.c
deleted file mode 100644
index d24506ceb6e8..000000000000
--- a/drivers/isdn/sc/shmem.c
+++ /dev/null
@@ -1,138 +0,0 @@
-/* $Id: shmem.c,v 1.2.10.1 2001/09/23 22:24:59 kai Exp $
- *
- * Copyright (C) 1996 SpellCaster Telecommunications Inc.
- *
- * Card functions implementing ISDN4Linux functionality
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * For more information, please contact gpl-info@spellcast.com or write:
- *
- * SpellCaster Telecommunications Inc.
- * 5621 Finch Avenue East, Unit #3
- * Scarborough, Ontario Canada
- * M1B 2T9
- * +1 (416) 297-8565
- * +1 (416) 297-6433 Facsimile
- */
-
-#include "includes.h" /* This must be first */
-#include "hardware.h"
-#include "card.h"
-
-/*
- *
- */
-void memcpy_toshmem(int card, void *dest, const void *src, size_t n)
-{
- unsigned long flags;
- unsigned char ch;
- unsigned long dest_rem = ((unsigned long) dest) % 0x4000;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return;
- }
-
- if (n > SRAM_PAGESIZE)
- return;
-
- /*
- * determine the page to load from the address
- */
- ch = (unsigned long) dest / SRAM_PAGESIZE;
- pr_debug("%s: loaded page %d\n", sc_adapter[card]->devicename, ch);
- /*
- * Block interrupts and load the page
- */
- spin_lock_irqsave(&sc_adapter[card]->lock, flags);
-
- outb(((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
- sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
- memcpy_toio((void __iomem *)(sc_adapter[card]->rambase + dest_rem), src, n);
- spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
- pr_debug("%s: set page to %#x\n", sc_adapter[card]->devicename,
- ((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80);
- pr_debug("%s: copying %zu bytes from %#lx to %#lx\n",
- sc_adapter[card]->devicename, n,
- (unsigned long) src,
- sc_adapter[card]->rambase + ((unsigned long) dest % 0x4000));
-}
-
-/*
- * Reverse of above
- */
-void memcpy_fromshmem(int card, void *dest, const void *src, size_t n)
-{
- unsigned long flags;
- unsigned char ch;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return;
- }
-
- if (n > SRAM_PAGESIZE) {
- return;
- }
-
- /*
- * determine the page to load from the address
- */
- ch = (unsigned long) src / SRAM_PAGESIZE;
- pr_debug("%s: loaded page %d\n", sc_adapter[card]->devicename, ch);
-
-
- /*
- * Block interrupts and load the page
- */
- spin_lock_irqsave(&sc_adapter[card]->lock, flags);
-
- outb(((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
- sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
- memcpy_fromio(dest, (void *)(sc_adapter[card]->rambase +
- ((unsigned long) src % 0x4000)), n);
- spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
- pr_debug("%s: set page to %#x\n", sc_adapter[card]->devicename,
- ((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80);
-/* pr_debug("%s: copying %d bytes from %#x to %#x\n",
- sc_adapter[card]->devicename, n,
- sc_adapter[card]->rambase + ((unsigned long) src %0x4000), (unsigned long) dest); */
-}
-
-#if 0
-void memset_shmem(int card, void *dest, int c, size_t n)
-{
- unsigned long flags;
- unsigned char ch;
-
- if (!IS_VALID_CARD(card)) {
- pr_debug("Invalid param: %d is not a valid card id\n", card);
- return;
- }
-
- if (n > SRAM_PAGESIZE) {
- return;
- }
-
- /*
- * determine the page to load from the address
- */
- ch = (unsigned long) dest / SRAM_PAGESIZE;
- pr_debug("%s: loaded page %d\n", sc_adapter[card]->devicename, ch);
-
- /*
- * Block interrupts and load the page
- */
- spin_lock_irqsave(&sc_adapter[card]->lock, flags);
-
- outb(((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
- sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
- memset_io(sc_adapter[card]->rambase +
- ((unsigned long) dest % 0x4000), c, n);
- pr_debug("%s: set page to %#x\n", sc_adapter[card]->devicename,
- ((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80);
- spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
-}
-#endif /* 0 */
diff --git a/drivers/isdn/sc/timer.c b/drivers/isdn/sc/timer.c
deleted file mode 100644
index 6fbac2230d7e..000000000000
--- a/drivers/isdn/sc/timer.c
+++ /dev/null
@@ -1,122 +0,0 @@
-/* $Id: timer.c,v 1.3.6.1 2001/09/23 22:24:59 kai Exp $
- *
- * Copyright (C) 1996 SpellCaster Telecommunications Inc.
- *
- * This software may be used and distributed according to the terms
- * of the GNU General Public License, incorporated herein by reference.
- *
- * For more information, please contact gpl-info@spellcast.com or write:
- *
- * SpellCaster Telecommunications Inc.
- * 5621 Finch Avenue East, Unit #3
- * Scarborough, Ontario Canada
- * M1B 2T9
- * +1 (416) 297-8565
- * +1 (416) 297-6433 Facsimile
- */
-
-#include "includes.h"
-#include "hardware.h"
-#include "message.h"
-#include "card.h"
-
-
-/*
- * Write the proper values into the I/O ports following a reset
- */
-static void setup_ports(int card)
-{
-
- outb((sc_adapter[card]->rambase >> 12), sc_adapter[card]->ioport[EXP_BASE]);
-
- /* And the IRQ */
- outb((sc_adapter[card]->interrupt | 0x80),
- sc_adapter[card]->ioport[IRQ_SELECT]);
-}
-
-/*
- * Timed function to check the status of a previous reset
- * Must be very fast as this function runs in the context of
- * an interrupt handler.
- *
- * Setup the ioports for the board that were cleared by the reset.
- * Then, check to see if the signate has been set. Next, set the
- * signature to a known value and issue a startproc if needed.
- */
-void sc_check_reset(unsigned long data)
-{
- unsigned long flags;
- unsigned long sig;
- int card = (unsigned int) data;
-
- pr_debug("%s: check_timer timer called\n",
- sc_adapter[card]->devicename);
-
- /* Setup the io ports */
- setup_ports(card);
-
- spin_lock_irqsave(&sc_adapter[card]->lock, flags);
- outb(sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport],
- (sc_adapter[card]->shmem_magic >> 14) | 0x80);
- sig = (unsigned long) *((unsigned long *)(sc_adapter[card]->rambase + SIG_OFFSET));
-
- /* check the signature */
- if (sig == SIGNATURE) {
- flushreadfifo(card);
- spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
- /* See if we need to do a startproc */
- if (sc_adapter[card]->StartOnReset)
- startproc(card);
- } else {
- pr_debug("%s: No signature yet, waiting another %lu jiffies.\n",
- sc_adapter[card]->devicename, CHECKRESET_TIME);
- mod_timer(&sc_adapter[card]->reset_timer, jiffies + CHECKRESET_TIME);
- spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
- }
-}
-
-/*
- * Timed function to check the status of a previous reset
- * Must be very fast as this function runs in the context of
- * an interrupt handler.
- *
- * Send check sc_adapter->phystat to see if the channels are up
- * If they are, tell ISDN4Linux that the board is up. If not,
- * tell IADN4Linux that it is up. Always reset the timer to
- * fire again (endless loop).
- */
-void check_phystat(unsigned long data)
-{
- unsigned long flags;
- int card = (unsigned int) data;
-
- pr_debug("%s: Checking status...\n", sc_adapter[card]->devicename);
- /*
- * check the results of the last PhyStat and change only if
- * has changed drastically
- */
- if (sc_adapter[card]->nphystat && !sc_adapter[card]->phystat) { /* All is well */
- pr_debug("PhyStat transition to RUN\n");
- pr_info("%s: Switch contacted, transmitter enabled\n",
- sc_adapter[card]->devicename);
- indicate_status(card, ISDN_STAT_RUN, 0, NULL);
- }
- else if (!sc_adapter[card]->nphystat && sc_adapter[card]->phystat) { /* All is not well */
- pr_debug("PhyStat transition to STOP\n");
- pr_info("%s: Switch connection lost, transmitter disabled\n",
- sc_adapter[card]->devicename);
-
- indicate_status(card, ISDN_STAT_STOP, 0, NULL);
- }
-
- sc_adapter[card]->phystat = sc_adapter[card]->nphystat;
-
- /* Reinitialize the timer */
- spin_lock_irqsave(&sc_adapter[card]->lock, flags);
- mod_timer(&sc_adapter[card]->stat_timer, jiffies + CHECKSTAT_TIME);
- spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
-
- /* Send a new cePhyStatus message */
- sendmessage(card, CEPID, ceReqTypePhy, ceReqClass2,
- ceReqPhyStatus, 0, 0, NULL);
-}
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index b1ab8bdf8251..7f940c24a16b 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -52,6 +52,7 @@ config LEDS_AAT1290
config LEDS_BCM6328
tristate "LED Support for Broadcom BCM6328"
depends on LEDS_CLASS
+ depends on HAS_IOMEM
depends on OF
help
This option enables support for LEDs connected to the BCM6328
@@ -60,6 +61,7 @@ config LEDS_BCM6328
config LEDS_BCM6358
tristate "LED Support for Broadcom BCM6358"
depends on LEDS_CLASS
+ depends on HAS_IOMEM
depends on OF
help
This option enables support for LEDs connected to the BCM6358
diff --git a/drivers/leds/led-class-flash.c b/drivers/leds/led-class-flash.c
index 3b2573411a37..cf398275a53c 100644
--- a/drivers/leds/led-class-flash.c
+++ b/drivers/leds/led-class-flash.c
@@ -108,7 +108,7 @@ static ssize_t flash_strobe_store(struct device *dev,
if (ret)
goto unlock;
- if (state < 0 || state > 1) {
+ if (state > 1) {
ret = -EINVAL;
goto unlock;
}
@@ -298,7 +298,7 @@ int led_classdev_flash_register(struct device *parent,
led_cdev = &fled_cdev->led_cdev;
if (led_cdev->flags & LED_DEV_CAP_FLASH) {
- if (!led_cdev->brightness_set_sync)
+ if (!led_cdev->brightness_set_blocking)
return -EINVAL;
ops = fled_cdev->ops;
@@ -316,10 +316,6 @@ int led_classdev_flash_register(struct device *parent,
if (ret < 0)
return ret;
- /* Setting a torch brightness needs to have immediate effect */
- led_cdev->flags &= ~SET_BRIGHTNESS_ASYNC;
- led_cdev->flags |= SET_BRIGHTNESS_SYNC;
-
return 0;
}
EXPORT_SYMBOL_GPL(led_classdev_flash_register);
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 7385f98dd54b..14139c337312 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -109,7 +109,7 @@ static const struct attribute_group *led_groups[] = {
void led_classdev_suspend(struct led_classdev *led_cdev)
{
led_cdev->flags |= LED_SUSPENDED;
- led_cdev->brightness_set(led_cdev, 0);
+ led_set_brightness_nopm(led_cdev, 0);
}
EXPORT_SYMBOL_GPL(led_classdev_suspend);
@@ -119,7 +119,7 @@ EXPORT_SYMBOL_GPL(led_classdev_suspend);
*/
void led_classdev_resume(struct led_classdev *led_cdev)
{
- led_cdev->brightness_set(led_cdev, led_cdev->brightness);
+ led_set_brightness_nopm(led_cdev, led_cdev->brightness);
if (led_cdev->flash_resume)
led_cdev->flash_resume(led_cdev);
@@ -215,8 +215,6 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
if (!led_cdev->max_brightness)
led_cdev->max_brightness = LED_FULL;
- led_cdev->flags |= SET_BRIGHTNESS_ASYNC;
-
led_update_brightness(led_cdev);
led_init_core(led_cdev);
@@ -247,12 +245,13 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
up_write(&led_cdev->trigger_lock);
#endif
- cancel_work_sync(&led_cdev->set_brightness_work);
-
/* Stop blinking */
led_stop_software_blink(led_cdev);
+
led_set_brightness(led_cdev, LED_OFF);
+ flush_work(&led_cdev->set_brightness_work);
+
device_unregister(led_cdev->dev);
down_write(&leds_list_lock);
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index c1c3af089634..19e1e60dfaa3 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -32,7 +32,7 @@ static void led_timer_function(unsigned long data)
unsigned long delay;
if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
- led_set_brightness_async(led_cdev, LED_OFF);
+ led_set_brightness_nosleep(led_cdev, LED_OFF);
return;
}
@@ -44,23 +44,23 @@ static void led_timer_function(unsigned long data)
brightness = led_get_brightness(led_cdev);
if (!brightness) {
/* Time to switch the LED on. */
- if (led_cdev->delayed_set_value) {
- led_cdev->blink_brightness =
- led_cdev->delayed_set_value;
- led_cdev->delayed_set_value = 0;
- }
brightness = led_cdev->blink_brightness;
delay = led_cdev->blink_delay_on;
} else {
/* Store the current brightness value to be able
* to restore it when the delay_off period is over.
+ * Do it only if there is no pending blink brightness
+ * change, to avoid overwriting the new value.
*/
- led_cdev->blink_brightness = brightness;
+ if (!(led_cdev->flags & LED_BLINK_BRIGHTNESS_CHANGE))
+ led_cdev->blink_brightness = brightness;
+ else
+ led_cdev->flags &= ~LED_BLINK_BRIGHTNESS_CHANGE;
brightness = LED_OFF;
delay = led_cdev->blink_delay_off;
}
- led_set_brightness_async(led_cdev, brightness);
+ led_set_brightness_nosleep(led_cdev, brightness);
/* Return in next iteration if led is in one-shot mode and we are in
* the final blink state so that the led is toggled each delay_on +
@@ -83,10 +83,24 @@ static void set_brightness_delayed(struct work_struct *ws)
{
struct led_classdev *led_cdev =
container_of(ws, struct led_classdev, set_brightness_work);
+ int ret = 0;
- led_stop_software_blink(led_cdev);
+ if (led_cdev->flags & LED_BLINK_DISABLE) {
+ led_cdev->delayed_set_value = LED_OFF;
+ led_stop_software_blink(led_cdev);
+ led_cdev->flags &= ~LED_BLINK_DISABLE;
+ }
- led_set_brightness_async(led_cdev, led_cdev->delayed_set_value);
+ if (led_cdev->brightness_set)
+ led_cdev->brightness_set(led_cdev, led_cdev->delayed_set_value);
+ else if (led_cdev->brightness_set_blocking)
+ ret = led_cdev->brightness_set_blocking(led_cdev,
+ led_cdev->delayed_set_value);
+ else
+ ret = -ENOTSUPP;
+ if (ret < 0)
+ dev_err(led_cdev->dev,
+ "Setting an LED's brightness failed (%d)\n", ret);
}
static void led_set_software_blink(struct led_classdev *led_cdev,
@@ -106,13 +120,14 @@ static void led_set_software_blink(struct led_classdev *led_cdev,
/* never on - just set to off */
if (!delay_on) {
- led_set_brightness_async(led_cdev, LED_OFF);
+ led_set_brightness_nosleep(led_cdev, LED_OFF);
return;
}
/* never off - just set to brightness */
if (!delay_off) {
- led_set_brightness_async(led_cdev, led_cdev->blink_brightness);
+ led_set_brightness_nosleep(led_cdev,
+ led_cdev->blink_brightness);
return;
}
@@ -156,7 +171,7 @@ void led_blink_set(struct led_classdev *led_cdev,
led_blink_setup(led_cdev, delay_on, delay_off);
}
-EXPORT_SYMBOL(led_blink_set);
+EXPORT_SYMBOL_GPL(led_blink_set);
void led_blink_set_oneshot(struct led_classdev *led_cdev,
unsigned long *delay_on,
@@ -177,7 +192,7 @@ void led_blink_set_oneshot(struct led_classdev *led_cdev,
led_blink_setup(led_cdev, delay_on, delay_off);
}
-EXPORT_SYMBOL(led_blink_set_oneshot);
+EXPORT_SYMBOL_GPL(led_blink_set_oneshot);
void led_stop_software_blink(struct led_classdev *led_cdev)
{
@@ -190,29 +205,74 @@ EXPORT_SYMBOL_GPL(led_stop_software_blink);
void led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
- int ret = 0;
-
- /* delay brightness if soft-blink is active */
+ /*
+ * In case blinking is on delay brightness setting
+ * until the next timer tick.
+ */
if (led_cdev->blink_delay_on || led_cdev->blink_delay_off) {
- led_cdev->delayed_set_value = brightness;
- if (brightness == LED_OFF)
+ /*
+ * If we need to disable soft blinking delegate this to the
+ * work queue task to avoid problems in case we are called
+ * from hard irq context.
+ */
+ if (brightness == LED_OFF) {
+ led_cdev->flags |= LED_BLINK_DISABLE;
schedule_work(&led_cdev->set_brightness_work);
+ } else {
+ led_cdev->flags |= LED_BLINK_BRIGHTNESS_CHANGE;
+ led_cdev->blink_brightness = brightness;
+ }
return;
}
- if (led_cdev->flags & SET_BRIGHTNESS_ASYNC) {
- led_set_brightness_async(led_cdev, brightness);
+ led_set_brightness_nosleep(led_cdev, brightness);
+}
+EXPORT_SYMBOL_GPL(led_set_brightness);
+
+void led_set_brightness_nopm(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ /* Use brightness_set op if available, it is guaranteed not to sleep */
+ if (led_cdev->brightness_set) {
+ led_cdev->brightness_set(led_cdev, value);
return;
- } else if (led_cdev->flags & SET_BRIGHTNESS_SYNC)
- ret = led_set_brightness_sync(led_cdev, brightness);
- else
- ret = -EINVAL;
+ }
- if (ret < 0)
- dev_dbg(led_cdev->dev, "Setting LED brightness failed (%d)\n",
- ret);
+ /* If brightness setting can sleep, delegate it to a work queue task */
+ led_cdev->delayed_set_value = value;
+ schedule_work(&led_cdev->set_brightness_work);
+}
+EXPORT_SYMBOL_GPL(led_set_brightness_nopm);
+
+void led_set_brightness_nosleep(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ led_cdev->brightness = min(value, led_cdev->max_brightness);
+
+ if (led_cdev->flags & LED_SUSPENDED)
+ return;
+
+ led_set_brightness_nopm(led_cdev, led_cdev->brightness);
+}
+EXPORT_SYMBOL_GPL(led_set_brightness_nosleep);
+
+int led_set_brightness_sync(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ if (led_cdev->blink_delay_on || led_cdev->blink_delay_off)
+ return -EBUSY;
+
+ led_cdev->brightness = min(value, led_cdev->max_brightness);
+
+ if (led_cdev->flags & LED_SUSPENDED)
+ return 0;
+
+ if (led_cdev->brightness_set_blocking)
+ return led_cdev->brightness_set_blocking(led_cdev,
+ led_cdev->brightness);
+ return -ENOTSUPP;
}
-EXPORT_SYMBOL(led_set_brightness);
+EXPORT_SYMBOL_GPL(led_set_brightness_sync);
int led_update_brightness(struct led_classdev *led_cdev)
{
@@ -228,7 +288,7 @@ int led_update_brightness(struct led_classdev *led_cdev)
return ret;
}
-EXPORT_SYMBOL(led_update_brightness);
+EXPORT_SYMBOL_GPL(led_update_brightness);
/* Caller must ensure led_cdev->led_access held */
void led_sysfs_disable(struct led_classdev *led_cdev)
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index e8b1120f486d..e1e933424ac9 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -249,6 +249,34 @@ void led_trigger_unregister(struct led_trigger *trig)
}
EXPORT_SYMBOL_GPL(led_trigger_unregister);
+static void devm_led_trigger_release(struct device *dev, void *res)
+{
+ led_trigger_unregister(*(struct led_trigger **)res);
+}
+
+int devm_led_trigger_register(struct device *dev,
+ struct led_trigger *trig)
+{
+ struct led_trigger **dr;
+ int rc;
+
+ dr = devres_alloc(devm_led_trigger_release, sizeof(*dr),
+ GFP_KERNEL);
+ if (!dr)
+ return -ENOMEM;
+
+ *dr = trig;
+
+ rc = led_trigger_register(trig);
+ if (rc)
+ devres_free(dr);
+ else
+ devres_add(dev, dr);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(devm_led_trigger_register);
+
/* Simple LED Tigger Interface */
void led_trigger_event(struct led_trigger *trig,
diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c
index 7870840e7cc9..1ad4d03a0a3c 100644
--- a/drivers/leds/leds-88pm860x.c
+++ b/drivers/leds/leds-88pm860x.c
@@ -16,7 +16,6 @@
#include <linux/i2c.h>
#include <linux/leds.h>
#include <linux/slab.h>
-#include <linux/workqueue.h>
#include <linux/mfd/88pm860x.h>
#include <linux/module.h>
@@ -33,7 +32,6 @@
struct pm860x_led {
struct led_classdev cdev;
struct i2c_client *i2c;
- struct work_struct work;
struct pm860x_chip *chip;
struct mutex lock;
char name[MFD_NAME_SIZE];
@@ -69,17 +67,18 @@ static int led_power_set(struct pm860x_chip *chip, int port, int on)
return ret;
}
-static void pm860x_led_work(struct work_struct *work)
+static int pm860x_led_set(struct led_classdev *cdev,
+ enum led_brightness value)
{
-
- struct pm860x_led *led;
+ struct pm860x_led *led = container_of(cdev, struct pm860x_led, cdev);
struct pm860x_chip *chip;
unsigned char buf[3];
int ret;
- led = container_of(work, struct pm860x_led, work);
chip = led->chip;
mutex_lock(&led->lock);
+ led->brightness = value >> 3;
+
if ((led->current_brightness == 0) && led->brightness) {
led_power_set(chip, led->port, 1);
if (led->iset) {
@@ -112,15 +111,8 @@ static void pm860x_led_work(struct work_struct *work)
dev_dbg(chip->dev, "Update LED. (reg:%d, brightness:%d)\n",
led->reg_control, led->brightness);
mutex_unlock(&led->lock);
-}
-static void pm860x_led_set(struct led_classdev *cdev,
- enum led_brightness value)
-{
- struct pm860x_led *data = container_of(cdev, struct pm860x_led, cdev);
-
- data->brightness = value >> 3;
- schedule_work(&data->work);
+ return 0;
}
#ifdef CONFIG_OF
@@ -213,9 +205,8 @@ static int pm860x_led_probe(struct platform_device *pdev)
data->current_brightness = 0;
data->cdev.name = data->name;
- data->cdev.brightness_set = pm860x_led_set;
+ data->cdev.brightness_set_blocking = pm860x_led_set;
mutex_init(&data->lock);
- INIT_WORK(&data->work, pm860x_led_work);
ret = led_classdev_register(chip->dev, &data->cdev);
if (ret < 0) {
diff --git a/drivers/leds/leds-aat1290.c b/drivers/leds/leds-aat1290.c
index ac77d36b630c..def3cf9f7e92 100644
--- a/drivers/leds/leds-aat1290.c
+++ b/drivers/leds/leds-aat1290.c
@@ -20,7 +20,6 @@
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <linux/workqueue.h>
#include <media/v4l2-flash-led-class.h>
#define AAT1290_MOVIE_MODE_CURRENT_ADDR 17
@@ -82,8 +81,6 @@ struct aat1290_led {
/* brightness cache */
unsigned int torch_brightness;
- /* assures led-triggers compatibility */
- struct work_struct work_brightness_set;
};
static struct aat1290_led *fled_cdev_to_led(
@@ -92,6 +89,12 @@ static struct aat1290_led *fled_cdev_to_led(
return container_of(fled_cdev, struct aat1290_led, fled_cdev);
}
+static struct led_classdev_flash *led_cdev_to_fled_cdev(
+ struct led_classdev *led_cdev)
+{
+ return container_of(led_cdev, struct led_classdev_flash, led_cdev);
+}
+
static void aat1290_as2cwire_write(struct aat1290_led *led, int addr, int value)
{
int i;
@@ -134,9 +137,14 @@ static void aat1290_set_flash_safety_timer(struct aat1290_led *led,
flash_tm_reg);
}
-static void aat1290_brightness_set(struct aat1290_led *led,
+/* LED subsystem callbacks */
+
+static int aat1290_led_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
+ struct led_classdev_flash *fled_cdev = led_cdev_to_fled_cdev(led_cdev);
+ struct aat1290_led *led = fled_cdev_to_led(fled_cdev);
+
mutex_lock(&led->lock);
if (brightness == 0) {
@@ -158,35 +166,6 @@ static void aat1290_brightness_set(struct aat1290_led *led,
}
mutex_unlock(&led->lock);
-}
-
-/* LED subsystem callbacks */
-
-static void aat1290_brightness_set_work(struct work_struct *work)
-{
- struct aat1290_led *led =
- container_of(work, struct aat1290_led, work_brightness_set);
-
- aat1290_brightness_set(led, led->torch_brightness);
-}
-
-static void aat1290_led_brightness_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
-{
- struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
- struct aat1290_led *led = fled_cdev_to_led(fled_cdev);
-
- led->torch_brightness = brightness;
- schedule_work(&led->work_brightness_set);
-}
-
-static int aat1290_led_brightness_set_sync(struct led_classdev *led_cdev,
- enum led_brightness brightness)
-{
- struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
- struct aat1290_led *led = fled_cdev_to_led(fled_cdev);
-
- aat1290_brightness_set(led, brightness);
return 0;
}
@@ -296,7 +275,7 @@ static int aat1290_led_parse_dt(struct aat1290_led *led,
if (ret < 0) {
dev_err(dev,
"flash-max-microamp DT property missing\n");
- return ret;
+ goto err_parse_dt;
}
ret = of_property_read_u32(child_node, "flash-max-timeout-us",
@@ -304,13 +283,14 @@ static int aat1290_led_parse_dt(struct aat1290_led *led,
if (ret < 0) {
dev_err(dev,
"flash-max-timeout-us DT property missing\n");
- return ret;
+ goto err_parse_dt;
}
- of_node_put(child_node);
-
*sub_node = child_node;
+err_parse_dt:
+ of_node_put(child_node);
+
return ret;
}
@@ -509,11 +489,9 @@ static int aat1290_led_probe(struct platform_device *pdev)
mutex_init(&led->lock);
/* Initialize LED Flash class device */
- led_cdev->brightness_set = aat1290_led_brightness_set;
- led_cdev->brightness_set_sync = aat1290_led_brightness_set_sync;
+ led_cdev->brightness_set_blocking = aat1290_led_brightness_set;
led_cdev->max_brightness = led_cfg.max_brightness;
led_cdev->flags |= LED_DEV_CAP_FLASH;
- INIT_WORK(&led->work_brightness_set, aat1290_brightness_set_work);
aat1290_init_flash_timeout(led, &led_cfg);
@@ -548,7 +526,6 @@ static int aat1290_led_remove(struct platform_device *pdev)
v4l2_flash_release(led->v4l2_flash);
led_classdev_flash_unregister(&led->fled_cdev);
- cancel_work_sync(&led->work_brightness_set);
mutex_destroy(&led->lock);
diff --git a/drivers/leds/leds-adp5520.c b/drivers/leds/leds-adp5520.c
index 07e66cae32d3..853b2d3bdb17 100644
--- a/drivers/leds/leds-adp5520.c
+++ b/drivers/leds/leds-adp5520.c
@@ -17,34 +17,24 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
-#include <linux/workqueue.h>
#include <linux/mfd/adp5520.h>
#include <linux/slab.h>
struct adp5520_led {
struct led_classdev cdev;
- struct work_struct work;
struct device *master;
- enum led_brightness new_brightness;
int id;
int flags;
};
-static void adp5520_led_work(struct work_struct *work)
-{
- struct adp5520_led *led = container_of(work, struct adp5520_led, work);
- adp5520_write(led->master, ADP5520_LED1_CURRENT + led->id - 1,
- led->new_brightness >> 2);
-}
-
-static void adp5520_led_set(struct led_classdev *led_cdev,
+static int adp5520_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct adp5520_led *led;
led = container_of(led_cdev, struct adp5520_led, cdev);
- led->new_brightness = value;
- schedule_work(&led->work);
+ return adp5520_write(led->master, ADP5520_LED1_CURRENT + led->id - 1,
+ value >> 2);
}
static int adp5520_led_setup(struct adp5520_led *led)
@@ -135,7 +125,7 @@ static int adp5520_led_probe(struct platform_device *pdev)
led_dat->cdev.name = cur_led->name;
led_dat->cdev.default_trigger = cur_led->default_trigger;
- led_dat->cdev.brightness_set = adp5520_led_set;
+ led_dat->cdev.brightness_set_blocking = adp5520_led_set;
led_dat->cdev.brightness = LED_OFF;
if (cur_led->flags & ADP5520_FLAG_LED_MASK)
@@ -146,9 +136,6 @@ static int adp5520_led_probe(struct platform_device *pdev)
led_dat->id = led_dat->flags & ADP5520_FLAG_LED_MASK;
led_dat->master = pdev->dev.parent;
- led_dat->new_brightness = LED_OFF;
-
- INIT_WORK(&led_dat->work, adp5520_led_work);
ret = led_classdev_register(led_dat->master, &led_dat->cdev);
if (ret) {
@@ -170,10 +157,8 @@ static int adp5520_led_probe(struct platform_device *pdev)
err:
if (i > 0) {
- for (i = i - 1; i >= 0; i--) {
+ for (i = i - 1; i >= 0; i--)
led_classdev_unregister(&led[i].cdev);
- cancel_work_sync(&led[i].work);
- }
}
return ret;
@@ -192,7 +177,6 @@ static int adp5520_led_remove(struct platform_device *pdev)
for (i = 0; i < pdata->num_leds; i++) {
led_classdev_unregister(&led[i].cdev);
- cancel_work_sync(&led[i].work);
}
return 0;
diff --git a/drivers/leds/leds-bcm6328.c b/drivers/leds/leds-bcm6328.c
index c7ea5c626331..1548259297c1 100644
--- a/drivers/leds/leds-bcm6328.c
+++ b/drivers/leds/leds-bcm6328.c
@@ -42,16 +42,16 @@
#define BCM6328_LED_SHIFT_TEST BIT(30)
#define BCM6328_LED_TEST BIT(31)
#define BCM6328_INIT_MASK (BCM6328_SERIAL_LED_EN | \
- BCM6328_SERIAL_LED_MUX | \
+ BCM6328_SERIAL_LED_MUX | \
BCM6328_SERIAL_LED_CLK_NPOL | \
BCM6328_SERIAL_LED_DATA_PPOL | \
BCM6328_SERIAL_LED_SHIFT_DIR)
#define BCM6328_LED_MODE_MASK 3
-#define BCM6328_LED_MODE_OFF 0
+#define BCM6328_LED_MODE_ON 0
#define BCM6328_LED_MODE_FAST 1
#define BCM6328_LED_MODE_BLINK 2
-#define BCM6328_LED_MODE_ON 3
+#define BCM6328_LED_MODE_OFF 3
#define BCM6328_LED_SHIFT(X) ((X) << 1)
/**
@@ -76,12 +76,20 @@ struct bcm6328_led {
static void bcm6328_led_write(void __iomem *reg, unsigned long data)
{
+#ifdef CONFIG_CPU_BIG_ENDIAN
iowrite32be(data, reg);
+#else
+ writel(data, reg);
+#endif
}
static unsigned long bcm6328_led_read(void __iomem *reg)
{
+#ifdef CONFIG_CPU_BIG_ENDIAN
return ioread32be(reg);
+#else
+ return readl(reg);
+#endif
}
/**
@@ -126,34 +134,45 @@ static void bcm6328_led_set(struct led_classdev *led_cdev,
*(led->blink_leds) &= ~BIT(led->pin);
if ((led->active_low && value == LED_OFF) ||
(!led->active_low && value != LED_OFF))
- bcm6328_led_mode(led, BCM6328_LED_MODE_OFF);
- else
bcm6328_led_mode(led, BCM6328_LED_MODE_ON);
+ else
+ bcm6328_led_mode(led, BCM6328_LED_MODE_OFF);
spin_unlock_irqrestore(led->lock, flags);
}
+static unsigned long bcm6328_blink_delay(unsigned long delay)
+{
+ unsigned long bcm6328_delay;
+
+ bcm6328_delay = delay + BCM6328_LED_INTERVAL_MS / 2;
+ bcm6328_delay = bcm6328_delay / BCM6328_LED_INTERVAL_MS;
+ if (bcm6328_delay == 0)
+ bcm6328_delay = 1;
+
+ return bcm6328_delay;
+}
+
static int bcm6328_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on, unsigned long *delay_off)
{
struct bcm6328_led *led =
container_of(led_cdev, struct bcm6328_led, cdev);
unsigned long delay, flags;
+ int rc;
if (!*delay_on)
*delay_on = BCM6328_LED_DEF_DELAY;
if (!*delay_off)
*delay_off = BCM6328_LED_DEF_DELAY;
- if (*delay_on != *delay_off) {
+ delay = bcm6328_blink_delay(*delay_on);
+ if (delay != bcm6328_blink_delay(*delay_off)) {
dev_dbg(led_cdev->dev,
"fallback to soft blinking (delay_on != delay_off)\n");
return -EINVAL;
}
- delay = *delay_on / BCM6328_LED_INTERVAL_MS;
- if (delay == 0)
- delay = 1;
- else if (delay > BCM6328_LED_INTV_MASK) {
+ if (delay > BCM6328_LED_INTV_MASK) {
dev_dbg(led_cdev->dev,
"fallback to soft blinking (delay > %ums)\n",
BCM6328_LED_INTV_MASK * BCM6328_LED_INTERVAL_MS);
@@ -175,16 +194,15 @@ static int bcm6328_blink_set(struct led_classdev *led_cdev,
bcm6328_led_write(led->mem + BCM6328_REG_INIT, val);
bcm6328_led_mode(led, BCM6328_LED_MODE_BLINK);
-
- spin_unlock_irqrestore(led->lock, flags);
+ rc = 0;
} else {
- spin_unlock_irqrestore(led->lock, flags);
dev_dbg(led_cdev->dev,
"fallback to soft blinking (delay already set)\n");
- return -EINVAL;
+ rc = -EINVAL;
}
+ spin_unlock_irqrestore(led->lock, flags);
- return 0;
+ return rc;
}
static int bcm6328_hwled(struct device *dev, struct device_node *nc, u32 reg,
@@ -264,7 +282,6 @@ static int bcm6328_led(struct device *dev, struct device_node *nc, u32 reg,
unsigned long *blink_leds, unsigned long *blink_delay)
{
struct bcm6328_led *led;
- unsigned long flags;
const char *state;
int rc;
@@ -286,7 +303,6 @@ static int bcm6328_led(struct device *dev, struct device_node *nc, u32 reg,
"linux,default-trigger",
NULL);
- spin_lock_irqsave(lock, flags);
if (!of_property_read_string(nc, "default-state", &state)) {
if (!strcmp(state, "on")) {
led->cdev.brightness = LED_FULL;
@@ -303,8 +319,8 @@ static int bcm6328_led(struct device *dev, struct device_node *nc, u32 reg,
val = bcm6328_led_read(mode) >>
BCM6328_LED_SHIFT(shift % 16);
val &= BCM6328_LED_MODE_MASK;
- if ((led->active_low && val == BCM6328_LED_MODE_ON) ||
- (!led->active_low && val == BCM6328_LED_MODE_OFF))
+ if ((led->active_low && val == BCM6328_LED_MODE_OFF) ||
+ (!led->active_low && val == BCM6328_LED_MODE_ON))
led->cdev.brightness = LED_FULL;
else
led->cdev.brightness = LED_OFF;
@@ -315,12 +331,7 @@ static int bcm6328_led(struct device *dev, struct device_node *nc, u32 reg,
led->cdev.brightness = LED_OFF;
}
- if ((led->active_low && led->cdev.brightness == LED_FULL) ||
- (!led->active_low && led->cdev.brightness == LED_OFF))
- bcm6328_led_mode(led, BCM6328_LED_MODE_ON);
- else
- bcm6328_led_mode(led, BCM6328_LED_MODE_OFF);
- spin_unlock_irqrestore(lock, flags);
+ bcm6328_led_set(&led->cdev, led->cdev.brightness);
led->cdev.brightness_set = bcm6328_led_set;
led->cdev.blink_set = bcm6328_blink_set;
@@ -341,7 +352,7 @@ static int bcm6328_leds_probe(struct platform_device *pdev)
struct device_node *child;
struct resource *mem_r;
void __iomem *mem;
- spinlock_t *lock;
+ spinlock_t *lock; /* memory lock */
unsigned long val, *blink_leds, *blink_delay;
mem_r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
diff --git a/drivers/leds/leds-bcm6358.c b/drivers/leds/leds-bcm6358.c
index 82b4ee1bc87e..b2cc06618abe 100644
--- a/drivers/leds/leds-bcm6358.c
+++ b/drivers/leds/leds-bcm6358.c
@@ -49,12 +49,20 @@ struct bcm6358_led {
static void bcm6358_led_write(void __iomem *reg, unsigned long data)
{
+#ifdef CONFIG_CPU_BIG_ENDIAN
iowrite32be(data, reg);
+#else
+ writel(data, reg);
+#endif
}
static unsigned long bcm6358_led_read(void __iomem *reg)
{
+#ifdef CONFIG_CPU_BIG_ENDIAN
return ioread32be(reg);
+#else
+ return readl(reg);
+#endif
}
static unsigned long bcm6358_led_busy(void __iomem *mem)
@@ -68,12 +76,15 @@ static unsigned long bcm6358_led_busy(void __iomem *mem)
return val;
}
-static void bcm6358_led_mode(struct bcm6358_led *led, unsigned long value)
+static void bcm6358_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
{
- unsigned long val;
+ struct bcm6358_led *led =
+ container_of(led_cdev, struct bcm6358_led, cdev);
+ unsigned long flags, val;
+ spin_lock_irqsave(led->lock, flags);
bcm6358_led_busy(led->mem);
-
val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
if ((led->active_low && value == LED_OFF) ||
(!led->active_low && value != LED_OFF))
@@ -81,17 +92,6 @@ static void bcm6358_led_mode(struct bcm6358_led *led, unsigned long value)
else
val &= ~(BIT(led->pin));
bcm6358_led_write(led->mem + BCM6358_REG_MODE, val);
-}
-
-static void bcm6358_led_set(struct led_classdev *led_cdev,
- enum led_brightness value)
-{
- struct bcm6358_led *led =
- container_of(led_cdev, struct bcm6358_led, cdev);
- unsigned long flags;
-
- spin_lock_irqsave(led->lock, flags);
- bcm6358_led_mode(led, value);
spin_unlock_irqrestore(led->lock, flags);
}
@@ -99,7 +99,6 @@ static int bcm6358_led(struct device *dev, struct device_node *nc, u32 reg,
void __iomem *mem, spinlock_t *lock)
{
struct bcm6358_led *led;
- unsigned long flags;
const char *state;
int rc;
@@ -119,15 +118,11 @@ static int bcm6358_led(struct device *dev, struct device_node *nc, u32 reg,
"linux,default-trigger",
NULL);
- spin_lock_irqsave(lock, flags);
if (!of_property_read_string(nc, "default-state", &state)) {
if (!strcmp(state, "on")) {
led->cdev.brightness = LED_FULL;
} else if (!strcmp(state, "keep")) {
unsigned long val;
-
- bcm6358_led_busy(led->mem);
-
val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
val &= BIT(led->pin);
if ((led->active_low && !val) ||
@@ -141,8 +136,8 @@ static int bcm6358_led(struct device *dev, struct device_node *nc, u32 reg,
} else {
led->cdev.brightness = LED_OFF;
}
- bcm6358_led_mode(led, led->cdev.brightness);
- spin_unlock_irqrestore(lock, flags);
+
+ bcm6358_led_set(&led->cdev, led->cdev.brightness);
led->cdev.brightness_set = bcm6358_led_set;
diff --git a/drivers/leds/leds-bd2802.c b/drivers/leds/leds-bd2802.c
index 6078c15d3452..6b4de762a760 100644
--- a/drivers/leds/leds-bd2802.c
+++ b/drivers/leds/leds-bd2802.c
@@ -72,7 +72,6 @@ struct bd2802_led {
struct bd2802_led_platform_data *pdata;
struct i2c_client *client;
struct rw_semaphore rwsem;
- struct work_struct work;
struct led_state led[2];
@@ -518,29 +517,22 @@ static struct device_attribute *bd2802_attributes[] = {
&bd2802_rgb_current_attr,
};
-static void bd2802_led_work(struct work_struct *work)
-{
- struct bd2802_led *led = container_of(work, struct bd2802_led, work);
-
- if (led->state)
- bd2802_turn_on(led, led->led_id, led->color, led->state);
- else
- bd2802_turn_off(led, led->led_id, led->color);
-}
-
#define BD2802_CONTROL_RGBS(name, id, clr) \
-static void bd2802_set_##name##_brightness(struct led_classdev *led_cdev,\
+static int bd2802_set_##name##_brightness(struct led_classdev *led_cdev,\
enum led_brightness value) \
{ \
struct bd2802_led *led = \
container_of(led_cdev, struct bd2802_led, cdev_##name); \
led->led_id = id; \
led->color = clr; \
- if (value == LED_OFF) \
+ if (value == LED_OFF) { \
led->state = BD2802_OFF; \
- else \
+ bd2802_turn_off(led, led->led_id, led->color); \
+ } else { \
led->state = BD2802_ON; \
- schedule_work(&led->work); \
+ bd2802_turn_on(led, led->led_id, led->color, BD2802_ON);\
+ } \
+ return 0; \
} \
static int bd2802_set_##name##_blink(struct led_classdev *led_cdev, \
unsigned long *delay_on, unsigned long *delay_off) \
@@ -552,7 +544,7 @@ static int bd2802_set_##name##_blink(struct led_classdev *led_cdev, \
led->led_id = id; \
led->color = clr; \
led->state = BD2802_BLINK; \
- schedule_work(&led->work); \
+ bd2802_turn_on(led, led->led_id, led->color, BD2802_BLINK); \
return 0; \
}
@@ -567,11 +559,9 @@ static int bd2802_register_led_classdev(struct bd2802_led *led)
{
int ret;
- INIT_WORK(&led->work, bd2802_led_work);
-
led->cdev_led1r.name = "led1_R";
led->cdev_led1r.brightness = LED_OFF;
- led->cdev_led1r.brightness_set = bd2802_set_led1r_brightness;
+ led->cdev_led1r.brightness_set_blocking = bd2802_set_led1r_brightness;
led->cdev_led1r.blink_set = bd2802_set_led1r_blink;
ret = led_classdev_register(&led->client->dev, &led->cdev_led1r);
@@ -583,7 +573,7 @@ static int bd2802_register_led_classdev(struct bd2802_led *led)
led->cdev_led1g.name = "led1_G";
led->cdev_led1g.brightness = LED_OFF;
- led->cdev_led1g.brightness_set = bd2802_set_led1g_brightness;
+ led->cdev_led1g.brightness_set_blocking = bd2802_set_led1g_brightness;
led->cdev_led1g.blink_set = bd2802_set_led1g_blink;
ret = led_classdev_register(&led->client->dev, &led->cdev_led1g);
@@ -595,7 +585,7 @@ static int bd2802_register_led_classdev(struct bd2802_led *led)
led->cdev_led1b.name = "led1_B";
led->cdev_led1b.brightness = LED_OFF;
- led->cdev_led1b.brightness_set = bd2802_set_led1b_brightness;
+ led->cdev_led1b.brightness_set_blocking = bd2802_set_led1b_brightness;
led->cdev_led1b.blink_set = bd2802_set_led1b_blink;
ret = led_classdev_register(&led->client->dev, &led->cdev_led1b);
@@ -607,7 +597,7 @@ static int bd2802_register_led_classdev(struct bd2802_led *led)
led->cdev_led2r.name = "led2_R";
led->cdev_led2r.brightness = LED_OFF;
- led->cdev_led2r.brightness_set = bd2802_set_led2r_brightness;
+ led->cdev_led2r.brightness_set_blocking = bd2802_set_led2r_brightness;
led->cdev_led2r.blink_set = bd2802_set_led2r_blink;
ret = led_classdev_register(&led->client->dev, &led->cdev_led2r);
@@ -619,7 +609,7 @@ static int bd2802_register_led_classdev(struct bd2802_led *led)
led->cdev_led2g.name = "led2_G";
led->cdev_led2g.brightness = LED_OFF;
- led->cdev_led2g.brightness_set = bd2802_set_led2g_brightness;
+ led->cdev_led2g.brightness_set_blocking = bd2802_set_led2g_brightness;
led->cdev_led2g.blink_set = bd2802_set_led2g_blink;
ret = led_classdev_register(&led->client->dev, &led->cdev_led2g);
@@ -631,7 +621,7 @@ static int bd2802_register_led_classdev(struct bd2802_led *led)
led->cdev_led2b.name = "led2_B";
led->cdev_led2b.brightness = LED_OFF;
- led->cdev_led2b.brightness_set = bd2802_set_led2b_brightness;
+ led->cdev_led2b.brightness_set_blocking = bd2802_set_led2b_brightness;
led->cdev_led2b.blink_set = bd2802_set_led2b_blink;
led->cdev_led2b.flags |= LED_CORE_SUSPENDRESUME;
@@ -661,7 +651,6 @@ failed_unregister_led1_R:
static void bd2802_unregister_led_classdev(struct bd2802_led *led)
{
- cancel_work_sync(&led->work);
led_classdev_unregister(&led->cdev_led2b);
led_classdev_unregister(&led->cdev_led2g);
led_classdev_unregister(&led->cdev_led2r);
diff --git a/drivers/leds/leds-blinkm.c b/drivers/leds/leds-blinkm.c
index d0452b099aee..617fe975bf6e 100644
--- a/drivers/leds/leds-blinkm.c
+++ b/drivers/leds/leds-blinkm.c
@@ -39,16 +39,9 @@ struct blinkm_led {
struct i2c_client *i2c_client;
struct led_classdev led_cdev;
int id;
- atomic_t active;
-};
-
-struct blinkm_work {
- struct blinkm_led *blinkm_led;
- struct work_struct work;
};
#define cdev_to_blmled(c) container_of(c, struct blinkm_led, led_cdev)
-#define work_to_blmwork(c) container_of(c, struct blinkm_work, work)
struct blinkm_data {
struct i2c_client *i2c_client;
@@ -439,65 +432,30 @@ static int blinkm_transfer_hw(struct i2c_client *client, int cmd)
return 0;
}
-static void led_work(struct work_struct *work)
-{
- int ret;
- struct blinkm_led *led;
- struct blinkm_data *data;
- struct blinkm_work *blm_work = work_to_blmwork(work);
-
- led = blm_work->blinkm_led;
- data = i2c_get_clientdata(led->i2c_client);
- ret = blinkm_transfer_hw(led->i2c_client, BLM_GO_RGB);
- atomic_dec(&led->active);
- dev_dbg(&led->i2c_client->dev,
- "# DONE # next_red = %d, next_green = %d,"
- " next_blue = %d, active = %d\n",
- data->next_red, data->next_green,
- data->next_blue, atomic_read(&led->active));
- kfree(blm_work);
-}
-
static int blinkm_led_common_set(struct led_classdev *led_cdev,
enum led_brightness value, int color)
{
/* led_brightness is 0, 127 or 255 - we just use it here as-is */
struct blinkm_led *led = cdev_to_blmled(led_cdev);
struct blinkm_data *data = i2c_get_clientdata(led->i2c_client);
- struct blinkm_work *bl_work;
switch (color) {
case RED:
/* bail out if there's no change */
if (data->next_red == (u8) value)
return 0;
- /* we assume a quite fast sequence here ([off]->on->off)
- * think of network led trigger - we cannot blink that fast, so
- * in case we already have a off->on->off transition queued up,
- * we refuse to queue up more.
- * Revisit: fast-changing brightness. */
- if (atomic_read(&led->active) > 1)
- return 0;
data->next_red = (u8) value;
break;
case GREEN:
/* bail out if there's no change */
if (data->next_green == (u8) value)
return 0;
- /* we assume a quite fast sequence here ([off]->on->off)
- * Revisit: fast-changing brightness. */
- if (atomic_read(&led->active) > 1)
- return 0;
data->next_green = (u8) value;
break;
case BLUE:
/* bail out if there's no change */
if (data->next_blue == (u8) value)
return 0;
- /* we assume a quite fast sequence here ([off]->on->off)
- * Revisit: fast-changing brightness. */
- if (atomic_read(&led->active) > 1)
- return 0;
data->next_blue = (u8) value;
break;
@@ -506,42 +464,31 @@ static int blinkm_led_common_set(struct led_classdev *led_cdev,
return -EINVAL;
}
- bl_work = kzalloc(sizeof(*bl_work), GFP_ATOMIC);
- if (!bl_work)
- return -ENOMEM;
-
- atomic_inc(&led->active);
+ blinkm_transfer_hw(led->i2c_client, BLM_GO_RGB);
dev_dbg(&led->i2c_client->dev,
- "#TO_SCHED# next_red = %d, next_green = %d,"
- " next_blue = %d, active = %d\n",
+ "# DONE # next_red = %d, next_green = %d,"
+ " next_blue = %d\n",
data->next_red, data->next_green,
- data->next_blue, atomic_read(&led->active));
-
- /* a fresh work _item_ for each change */
- bl_work->blinkm_led = led;
- INIT_WORK(&bl_work->work, led_work);
- /* queue work in own queue for easy sync on exit*/
- schedule_work(&bl_work->work);
-
+ data->next_blue);
return 0;
}
-static void blinkm_led_red_set(struct led_classdev *led_cdev,
+static int blinkm_led_red_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
- blinkm_led_common_set(led_cdev, value, RED);
+ return blinkm_led_common_set(led_cdev, value, RED);
}
-static void blinkm_led_green_set(struct led_classdev *led_cdev,
+static int blinkm_led_green_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
- blinkm_led_common_set(led_cdev, value, GREEN);
+ return blinkm_led_common_set(led_cdev, value, GREEN);
}
-static void blinkm_led_blue_set(struct led_classdev *led_cdev,
+static int blinkm_led_blue_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
- blinkm_led_common_set(led_cdev, value, BLUE);
+ return blinkm_led_common_set(led_cdev, value, BLUE);
}
static void blinkm_init_hw(struct i2c_client *client)
@@ -669,7 +616,6 @@ static int blinkm_probe(struct i2c_client *client,
led[i]->id = i;
led[i]->led_cdev.max_brightness = 255;
led[i]->led_cdev.flags = LED_CORE_SUSPENDRESUME;
- atomic_set(&led[i]->active, 0);
switch (i) {
case RED:
snprintf(blinkm_led_name, sizeof(blinkm_led_name),
@@ -677,7 +623,8 @@ static int blinkm_probe(struct i2c_client *client,
client->adapter->nr,
client->addr);
led[i]->led_cdev.name = blinkm_led_name;
- led[i]->led_cdev.brightness_set = blinkm_led_red_set;
+ led[i]->led_cdev.brightness_set_blocking =
+ blinkm_led_red_set;
err = led_classdev_register(&client->dev,
&led[i]->led_cdev);
if (err < 0) {
@@ -693,7 +640,8 @@ static int blinkm_probe(struct i2c_client *client,
client->adapter->nr,
client->addr);
led[i]->led_cdev.name = blinkm_led_name;
- led[i]->led_cdev.brightness_set = blinkm_led_green_set;
+ led[i]->led_cdev.brightness_set_blocking =
+ blinkm_led_green_set;
err = led_classdev_register(&client->dev,
&led[i]->led_cdev);
if (err < 0) {
@@ -709,7 +657,8 @@ static int blinkm_probe(struct i2c_client *client,
client->adapter->nr,
client->addr);
led[i]->led_cdev.name = blinkm_led_name;
- led[i]->led_cdev.brightness_set = blinkm_led_blue_set;
+ led[i]->led_cdev.brightness_set_blocking =
+ blinkm_led_blue_set;
err = led_classdev_register(&client->dev,
&led[i]->led_cdev);
if (err < 0) {
@@ -746,10 +695,8 @@ static int blinkm_remove(struct i2c_client *client)
int i;
/* make sure no workqueue entries are pending */
- for (i = 0; i < 3; i++) {
- flush_scheduled_work();
+ for (i = 0; i < 3; i++)
led_classdev_unregister(&data->blinkm_leds[i].led_cdev);
- }
/* reset rgb */
data->next_red = 0x00;
diff --git a/drivers/leds/leds-da903x.c b/drivers/leds/leds-da903x.c
index 952ba96e5b38..4752a2b6ba2b 100644
--- a/drivers/leds/leds-da903x.c
+++ b/drivers/leds/leds-da903x.c
@@ -16,7 +16,6 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
-#include <linux/workqueue.h>
#include <linux/mfd/da903x.h>
#include <linux/slab.h>
@@ -33,9 +32,7 @@
struct da903x_led {
struct led_classdev cdev;
- struct work_struct work;
struct device *master;
- enum led_brightness new_brightness;
int id;
int flags;
};
@@ -43,11 +40,13 @@ struct da903x_led {
#define DA9030_LED_OFFSET(id) ((id) - DA9030_ID_LED_1)
#define DA9034_LED_OFFSET(id) ((id) - DA9034_ID_LED_1)
-static void da903x_led_work(struct work_struct *work)
+static int da903x_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
{
- struct da903x_led *led = container_of(work, struct da903x_led, work);
+ struct da903x_led *led =
+ container_of(led_cdev, struct da903x_led, cdev);
uint8_t val;
- int offset;
+ int offset, ret = -EINVAL;
switch (led->id) {
case DA9030_ID_LED_1:
@@ -57,37 +56,31 @@ static void da903x_led_work(struct work_struct *work)
case DA9030_ID_LED_PC:
offset = DA9030_LED_OFFSET(led->id);
val = led->flags & ~0x87;
- val |= (led->new_brightness) ? 0x80 : 0; /* EN bit */
- val |= (0x7 - (led->new_brightness >> 5)) & 0x7; /* PWM<2:0> */
- da903x_write(led->master, DA9030_LED1_CONTROL + offset, val);
+ val |= value ? 0x80 : 0; /* EN bit */
+ val |= (0x7 - (value >> 5)) & 0x7; /* PWM<2:0> */
+ ret = da903x_write(led->master, DA9030_LED1_CONTROL + offset,
+ val);
break;
case DA9030_ID_VIBRA:
val = led->flags & ~0x80;
- val |= (led->new_brightness) ? 0x80 : 0; /* EN bit */
- da903x_write(led->master, DA9030_MISC_CONTROL_A, val);
+ val |= value ? 0x80 : 0; /* EN bit */
+ ret = da903x_write(led->master, DA9030_MISC_CONTROL_A, val);
break;
case DA9034_ID_LED_1:
case DA9034_ID_LED_2:
offset = DA9034_LED_OFFSET(led->id);
- val = (led->new_brightness * 0x5f / LED_FULL) & 0x7f;
+ val = (value * 0x5f / LED_FULL) & 0x7f;
val |= (led->flags & DA9034_LED_RAMP) ? 0x80 : 0;
- da903x_write(led->master, DA9034_LED1_CONTROL + offset, val);
+ ret = da903x_write(led->master, DA9034_LED1_CONTROL + offset,
+ val);
break;
case DA9034_ID_VIBRA:
- val = led->new_brightness & 0xfe;
- da903x_write(led->master, DA9034_VIBRA, val);
+ val = value & 0xfe;
+ ret = da903x_write(led->master, DA9034_VIBRA, val);
break;
}
-}
-static void da903x_led_set(struct led_classdev *led_cdev,
- enum led_brightness value)
-{
- struct da903x_led *led;
-
- led = container_of(led_cdev, struct da903x_led, cdev);
- led->new_brightness = value;
- schedule_work(&led->work);
+ return ret;
}
static int da903x_led_probe(struct platform_device *pdev)
@@ -113,15 +106,12 @@ static int da903x_led_probe(struct platform_device *pdev)
led->cdev.name = pdata->name;
led->cdev.default_trigger = pdata->default_trigger;
- led->cdev.brightness_set = da903x_led_set;
+ led->cdev.brightness_set_blocking = da903x_led_set;
led->cdev.brightness = LED_OFF;
led->id = id;
led->flags = pdata->flags;
led->master = pdev->dev.parent;
- led->new_brightness = LED_OFF;
-
- INIT_WORK(&led->work, da903x_led_work);
ret = led_classdev_register(led->master, &led->cdev);
if (ret) {
diff --git a/drivers/leds/leds-da9052.c b/drivers/leds/leds-da9052.c
index 28291b6acc8e..f8c7d82c2652 100644
--- a/drivers/leds/leds-da9052.c
+++ b/drivers/leds/leds-da9052.c
@@ -16,7 +16,6 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
-#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/mfd/da9052/reg.h>
@@ -32,11 +31,9 @@
struct da9052_led {
struct led_classdev cdev;
- struct work_struct work;
struct da9052 *da9052;
unsigned char led_index;
unsigned char id;
- int brightness;
};
static unsigned char led_reg[] = {
@@ -44,12 +41,13 @@ static unsigned char led_reg[] = {
DA9052_LED_CONT_5_REG,
};
-static int da9052_set_led_brightness(struct da9052_led *led)
+static int da9052_set_led_brightness(struct da9052_led *led,
+ enum led_brightness brightness)
{
u8 val;
int error;
- val = (led->brightness & 0x7f) | DA9052_LED_CONT_DIM;
+ val = (brightness & 0x7f) | DA9052_LED_CONT_DIM;
error = da9052_reg_write(led->da9052, led_reg[led->led_index], val);
if (error < 0)
@@ -58,21 +56,13 @@ static int da9052_set_led_brightness(struct da9052_led *led)
return error;
}
-static void da9052_led_work(struct work_struct *work)
-{
- struct da9052_led *led = container_of(work, struct da9052_led, work);
-
- da9052_set_led_brightness(led);
-}
-
-static void da9052_led_set(struct led_classdev *led_cdev,
+static int da9052_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
- struct da9052_led *led;
+ struct da9052_led *led =
+ container_of(led_cdev, struct da9052_led, cdev);
- led = container_of(led_cdev, struct da9052_led, cdev);
- led->brightness = value;
- schedule_work(&led->work);
+ return da9052_set_led_brightness(led, value);
}
static int da9052_configure_leds(struct da9052 *da9052)
@@ -133,13 +123,11 @@ static int da9052_led_probe(struct platform_device *pdev)
for (i = 0; i < pled->num_leds; i++) {
led[i].cdev.name = pled->leds[i].name;
- led[i].cdev.brightness_set = da9052_led_set;
+ led[i].cdev.brightness_set_blocking = da9052_led_set;
led[i].cdev.brightness = LED_OFF;
led[i].cdev.max_brightness = DA9052_MAX_BRIGHTNESS;
- led[i].brightness = LED_OFF;
led[i].led_index = pled->leds[i].flags;
led[i].da9052 = dev_get_drvdata(pdev->dev.parent);
- INIT_WORK(&led[i].work, da9052_led_work);
error = led_classdev_register(pdev->dev.parent, &led[i].cdev);
if (error) {
@@ -148,7 +136,8 @@ static int da9052_led_probe(struct platform_device *pdev)
goto err_register;
}
- error = da9052_set_led_brightness(&led[i]);
+ error = da9052_set_led_brightness(&led[i],
+ led[i].cdev.brightness);
if (error) {
dev_err(&pdev->dev, "Unable to init led %d\n",
led[i].led_index);
@@ -166,10 +155,8 @@ static int da9052_led_probe(struct platform_device *pdev)
return 0;
err_register:
- for (i = i - 1; i >= 0; i--) {
+ for (i = i - 1; i >= 0; i--)
led_classdev_unregister(&led[i].cdev);
- cancel_work_sync(&led[i].work);
- }
err:
return error;
}
@@ -187,10 +174,8 @@ static int da9052_led_remove(struct platform_device *pdev)
pled = pdata->pled;
for (i = 0; i < pled->num_leds; i++) {
- led[i].brightness = 0;
- da9052_set_led_brightness(&led[i]);
+ da9052_set_led_brightness(&led[i], LED_OFF);
led_classdev_unregister(&led[i].cdev);
- cancel_work_sync(&led[i].work);
}
return 0;
diff --git a/drivers/leds/leds-dac124s085.c b/drivers/leds/leds-dac124s085.c
index 314159610d24..5a5a86d5f1f5 100644
--- a/drivers/leds/leds-dac124s085.c
+++ b/drivers/leds/leds-dac124s085.c
@@ -13,20 +13,15 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/workqueue.h>
#include <linux/spi/spi.h>
struct dac124s085_led {
struct led_classdev ldev;
struct spi_device *spi;
int id;
- int brightness;
char name[sizeof("dac124s085-3")];
struct mutex mutex;
- struct work_struct work;
- spinlock_t lock;
};
struct dac124s085 {
@@ -38,29 +33,21 @@ struct dac124s085 {
#define ALL_WRITE_UPDATE (2 << 12)
#define POWER_DOWN_OUTPUT (3 << 12)
-static void dac124s085_led_work(struct work_struct *work)
+static int dac124s085_set_brightness(struct led_classdev *ldev,
+ enum led_brightness brightness)
{
- struct dac124s085_led *led = container_of(work, struct dac124s085_led,
- work);
+ struct dac124s085_led *led = container_of(ldev, struct dac124s085_led,
+ ldev);
u16 word;
+ int ret;
mutex_lock(&led->mutex);
word = cpu_to_le16(((led->id) << 14) | REG_WRITE_UPDATE |
- (led->brightness & 0xfff));
- spi_write(led->spi, (const u8 *)&word, sizeof(word));
+ (brightness & 0xfff));
+ ret = spi_write(led->spi, (const u8 *)&word, sizeof(word));
mutex_unlock(&led->mutex);
-}
-
-static void dac124s085_set_brightness(struct led_classdev *ldev,
- enum led_brightness brightness)
-{
- struct dac124s085_led *led = container_of(ldev, struct dac124s085_led,
- ldev);
- spin_lock(&led->lock);
- led->brightness = brightness;
- schedule_work(&led->work);
- spin_unlock(&led->lock);
+ return ret;
}
static int dac124s085_probe(struct spi_device *spi)
@@ -78,16 +65,13 @@ static int dac124s085_probe(struct spi_device *spi)
for (i = 0; i < ARRAY_SIZE(dac->leds); i++) {
led = dac->leds + i;
led->id = i;
- led->brightness = LED_OFF;
led->spi = spi;
snprintf(led->name, sizeof(led->name), "dac124s085-%d", i);
- spin_lock_init(&led->lock);
- INIT_WORK(&led->work, dac124s085_led_work);
mutex_init(&led->mutex);
led->ldev.name = led->name;
led->ldev.brightness = LED_OFF;
led->ldev.max_brightness = 0xfff;
- led->ldev.brightness_set = dac124s085_set_brightness;
+ led->ldev.brightness_set_blocking = dac124s085_set_brightness;
ret = led_classdev_register(&spi->dev, &led->ldev);
if (ret < 0)
goto eledcr;
@@ -109,10 +93,8 @@ static int dac124s085_remove(struct spi_device *spi)
struct dac124s085 *dac = spi_get_drvdata(spi);
int i;
- for (i = 0; i < ARRAY_SIZE(dac->leds); i++) {
+ for (i = 0; i < ARRAY_SIZE(dac->leds); i++)
led_classdev_unregister(&dac->leds[i].ldev);
- cancel_work_sync(&dac->leds[i].work);
- }
return 0;
}
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
index 5db4515a4fd7..7bc53280dbfd 100644
--- a/drivers/leds/leds-gpio.c
+++ b/drivers/leds/leds-gpio.c
@@ -20,32 +20,16 @@
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/slab.h>
-#include <linux/workqueue.h>
struct gpio_led_data {
struct led_classdev cdev;
struct gpio_desc *gpiod;
- struct work_struct work;
- u8 new_level;
u8 can_sleep;
u8 blinking;
int (*platform_gpio_blink_set)(struct gpio_desc *desc, int state,
unsigned long *delay_on, unsigned long *delay_off);
};
-static void gpio_led_work(struct work_struct *work)
-{
- struct gpio_led_data *led_dat =
- container_of(work, struct gpio_led_data, work);
-
- if (led_dat->blinking) {
- led_dat->platform_gpio_blink_set(led_dat->gpiod,
- led_dat->new_level, NULL, NULL);
- led_dat->blinking = 0;
- } else
- gpiod_set_value_cansleep(led_dat->gpiod, led_dat->new_level);
-}
-
static void gpio_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
@@ -58,23 +42,25 @@ static void gpio_led_set(struct led_classdev *led_cdev,
else
level = 1;
- /* Setting GPIOs with I2C/etc requires a task context, and we don't
- * seem to have a reliable way to know if we're already in one; so
- * let's just assume the worst.
- */
- if (led_dat->can_sleep) {
- led_dat->new_level = level;
- schedule_work(&led_dat->work);
+ if (led_dat->blinking) {
+ led_dat->platform_gpio_blink_set(led_dat->gpiod, level,
+ NULL, NULL);
+ led_dat->blinking = 0;
} else {
- if (led_dat->blinking) {
- led_dat->platform_gpio_blink_set(led_dat->gpiod, level,
- NULL, NULL);
- led_dat->blinking = 0;
- } else
+ if (led_dat->can_sleep)
+ gpiod_set_value_cansleep(led_dat->gpiod, level);
+ else
gpiod_set_value(led_dat->gpiod, level);
}
}
+static int gpio_led_set_blocking(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ gpio_led_set(led_cdev, value);
+ return 0;
+}
+
static int gpio_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on, unsigned long *delay_off)
{
@@ -125,12 +111,15 @@ static int create_gpio_led(const struct gpio_led *template,
led_dat->cdev.name = template->name;
led_dat->cdev.default_trigger = template->default_trigger;
led_dat->can_sleep = gpiod_cansleep(led_dat->gpiod);
+ if (!led_dat->can_sleep)
+ led_dat->cdev.brightness_set = gpio_led_set;
+ else
+ led_dat->cdev.brightness_set_blocking = gpio_led_set_blocking;
led_dat->blinking = 0;
if (blink_set) {
led_dat->platform_gpio_blink_set = blink_set;
led_dat->cdev.blink_set = gpio_blink_set;
}
- led_dat->cdev.brightness_set = gpio_led_set;
if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
state = !!gpiod_get_value_cansleep(led_dat->gpiod);
else
@@ -143,17 +132,9 @@ static int create_gpio_led(const struct gpio_led *template,
if (ret < 0)
return ret;
- INIT_WORK(&led_dat->work, gpio_led_work);
-
return led_classdev_register(parent, &led_dat->cdev);
}
-static void delete_gpio_led(struct gpio_led_data *led)
-{
- led_classdev_unregister(&led->cdev);
- cancel_work_sync(&led->work);
-}
-
struct gpio_leds_priv {
int num_leds;
struct gpio_led_data leds[];
@@ -233,7 +214,7 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
err:
for (count = priv->num_leds - 1; count >= 0; count--)
- delete_gpio_led(&priv->leds[count]);
+ led_classdev_unregister(&priv->leds[count].cdev);
return ERR_PTR(ret);
}
@@ -265,7 +246,8 @@ static int gpio_led_probe(struct platform_device *pdev)
if (ret < 0) {
/* On failure: unwind the led creations */
for (i = i - 1; i >= 0; i--)
- delete_gpio_led(&priv->leds[i]);
+ led_classdev_unregister(
+ &priv->leds[i].cdev);
return ret;
}
}
@@ -286,7 +268,7 @@ static int gpio_led_remove(struct platform_device *pdev)
int i;
for (i = 0; i < priv->num_leds; i++)
- delete_gpio_led(&priv->leds[i]);
+ led_classdev_unregister(&priv->leds[i].cdev);
return 0;
}
diff --git a/drivers/leds/leds-ipaq-micro.c b/drivers/leds/leds-ipaq-micro.c
index fa262b6b25eb..02f17331379d 100644
--- a/drivers/leds/leds-ipaq-micro.c
+++ b/drivers/leds/leds-ipaq-micro.c
@@ -20,7 +20,7 @@
#define LED_AUTOSTOP (1 << 5) /* LED ON/OFF auto stop set 0:disable, 1:enable */
#define LED_ALWAYS (1 << 6) /* LED Interrupt Mask 0:No mask, 1:mask */
-static void micro_leds_brightness_set(struct led_classdev *led_cdev,
+static int micro_leds_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct ipaq_micro *micro = dev_get_drvdata(led_cdev->dev->parent->parent);
@@ -50,7 +50,7 @@ static void micro_leds_brightness_set(struct led_classdev *led_cdev,
msg.tx_data[2] = 1;
msg.tx_data[3] = 0; /* Duty cycle 256 */
}
- ipaq_micro_tx_msg_sync(micro, &msg);
+ return ipaq_micro_tx_msg_sync(micro, &msg);
}
/* Maximum duty cycle in ms 256/10 sec = 25600 ms */
@@ -102,7 +102,7 @@ static int micro_leds_blink_set(struct led_classdev *led_cdev,
static struct led_classdev micro_led = {
.name = "led-ipaq-micro",
- .brightness_set = micro_leds_brightness_set,
+ .brightness_set_blocking = micro_leds_brightness_set,
.blink_set = micro_leds_blink_set,
.flags = LED_CORE_SUSPENDRESUME,
};
diff --git a/drivers/leds/leds-ktd2692.c b/drivers/leds/leds-ktd2692.c
index feca07be85f5..bf23ba191ad0 100644
--- a/drivers/leds/leds-ktd2692.c
+++ b/drivers/leds/leds-ktd2692.c
@@ -18,7 +18,6 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
-#include <linux/workqueue.h>
/* Value related the movie mode */
#define KTD2692_MOVIE_MODE_CURRENT_LEVELS 16
@@ -82,7 +81,6 @@ struct ktd2692_context {
/* secures access to the device */
struct mutex lock;
struct regulator *regulator;
- struct work_struct work_brightness_set;
struct gpio_desc *aux_gpio;
struct gpio_desc *ctrl_gpio;
@@ -158,9 +156,12 @@ static void ktd2692_expresswire_write(struct ktd2692_context *led, u8 value)
ktd2692_expresswire_end(led);
}
-static void ktd2692_brightness_set(struct ktd2692_context *led,
- enum led_brightness brightness)
+static int ktd2692_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
{
+ struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
+ struct ktd2692_context *led = fled_cdev_to_led(fled_cdev);
+
mutex_lock(&led->lock);
if (brightness == LED_OFF) {
@@ -174,33 +175,6 @@ static void ktd2692_brightness_set(struct ktd2692_context *led,
ktd2692_expresswire_write(led, led->mode | KTD2692_REG_MODE_BASE);
mutex_unlock(&led->lock);
-}
-
-static void ktd2692_brightness_set_work(struct work_struct *work)
-{
- struct ktd2692_context *led =
- container_of(work, struct ktd2692_context, work_brightness_set);
-
- ktd2692_brightness_set(led, led->torch_brightness);
-}
-
-static void ktd2692_led_brightness_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
-{
- struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
- struct ktd2692_context *led = fled_cdev_to_led(fled_cdev);
-
- led->torch_brightness = brightness;
- schedule_work(&led->work_brightness_set);
-}
-
-static int ktd2692_led_brightness_set_sync(struct led_classdev *led_cdev,
- enum led_brightness brightness)
-{
- struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
- struct ktd2692_context *led = fled_cdev_to_led(fled_cdev);
-
- ktd2692_brightness_set(led, brightness);
return 0;
}
@@ -332,21 +306,24 @@ static int ktd2692_parse_dt(struct ktd2692_context *led, struct device *dev,
&cfg->movie_max_microamp);
if (ret) {
dev_err(dev, "failed to parse led-max-microamp\n");
- return ret;
+ goto err_parse_dt;
}
ret = of_property_read_u32(child_node, "flash-max-microamp",
&cfg->flash_max_microamp);
if (ret) {
dev_err(dev, "failed to parse flash-max-microamp\n");
- return ret;
+ goto err_parse_dt;
}
ret = of_property_read_u32(child_node, "flash-max-timeout-us",
&cfg->flash_max_timeout);
- if (ret)
+ if (ret) {
dev_err(dev, "failed to parse flash-max-timeout-us\n");
+ goto err_parse_dt;
+ }
+err_parse_dt:
of_node_put(child_node);
return ret;
}
@@ -381,12 +358,10 @@ static int ktd2692_probe(struct platform_device *pdev)
fled_cdev->ops = &flash_ops;
led_cdev->max_brightness = led_cfg.max_brightness;
- led_cdev->brightness_set = ktd2692_led_brightness_set;
- led_cdev->brightness_set_sync = ktd2692_led_brightness_set_sync;
+ led_cdev->brightness_set_blocking = ktd2692_led_brightness_set;
led_cdev->flags |= LED_CORE_SUSPENDRESUME | LED_DEV_CAP_FLASH;
mutex_init(&led->lock);
- INIT_WORK(&led->work_brightness_set, ktd2692_brightness_set_work);
platform_set_drvdata(pdev, led);
@@ -408,7 +383,6 @@ static int ktd2692_remove(struct platform_device *pdev)
int ret;
led_classdev_flash_unregister(&led->fled_cdev);
- cancel_work_sync(&led->work_brightness_set);
if (led->regulator) {
ret = regulator_disable(led->regulator);
diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c
index 6e2e02035dd7..196dcb5e6004 100644
--- a/drivers/leds/leds-lm3533.c
+++ b/drivers/leds/leds-lm3533.c
@@ -17,7 +17,6 @@
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <linux/workqueue.h>
#include <linux/mfd/lm3533.h>
@@ -53,9 +52,6 @@ struct lm3533_led {
struct mutex mutex;
unsigned long flags;
-
- struct work_struct work;
- u8 new_brightness;
};
@@ -123,27 +119,17 @@ out:
return ret;
}
-static void lm3533_led_work(struct work_struct *work)
-{
- struct lm3533_led *led = container_of(work, struct lm3533_led, work);
-
- dev_dbg(led->cdev.dev, "%s - %u\n", __func__, led->new_brightness);
-
- if (led->new_brightness == 0)
- lm3533_led_pattern_enable(led, 0); /* disable blink */
-
- lm3533_ctrlbank_set_brightness(&led->cb, led->new_brightness);
-}
-
-static void lm3533_led_set(struct led_classdev *cdev,
+static int lm3533_led_set(struct led_classdev *cdev,
enum led_brightness value)
{
struct lm3533_led *led = to_lm3533_led(cdev);
dev_dbg(led->cdev.dev, "%s - %d\n", __func__, value);
- led->new_brightness = value;
- schedule_work(&led->work);
+ if (value == 0)
+ lm3533_led_pattern_enable(led, 0); /* disable blink */
+
+ return lm3533_ctrlbank_set_brightness(&led->cb, value);
}
static enum led_brightness lm3533_led_get(struct led_classdev *cdev)
@@ -693,7 +679,7 @@ static int lm3533_led_probe(struct platform_device *pdev)
led->lm3533 = lm3533;
led->cdev.name = pdata->name;
led->cdev.default_trigger = pdata->default_trigger;
- led->cdev.brightness_set = lm3533_led_set;
+ led->cdev.brightness_set_blocking = lm3533_led_set;
led->cdev.brightness_get = lm3533_led_get;
led->cdev.blink_set = lm3533_led_blink_set;
led->cdev.brightness = LED_OFF;
@@ -701,7 +687,6 @@ static int lm3533_led_probe(struct platform_device *pdev)
led->id = pdev->id;
mutex_init(&led->mutex);
- INIT_WORK(&led->work, lm3533_led_work);
/* The class framework makes a callback to get brightness during
* registration so use parent device (for error reporting) until
@@ -733,7 +718,6 @@ static int lm3533_led_probe(struct platform_device *pdev)
err_unregister:
led_classdev_unregister(&led->cdev);
- flush_work(&led->work);
return ret;
}
@@ -746,7 +730,6 @@ static int lm3533_led_remove(struct platform_device *pdev)
lm3533_ctrlbank_disable(&led->cb);
led_classdev_unregister(&led->cdev);
- flush_work(&led->work);
return 0;
}
@@ -760,7 +743,6 @@ static void lm3533_led_shutdown(struct platform_device *pdev)
lm3533_ctrlbank_disable(&led->cb);
lm3533_led_set(&led->cdev, LED_OFF); /* disable blink */
- flush_work(&led->work);
}
static struct platform_driver lm3533_led_driver = {
diff --git a/drivers/leds/leds-lm355x.c b/drivers/leds/leds-lm355x.c
index 48872997d6b4..6cb94f9a2f3f 100644
--- a/drivers/leds/leds-lm355x.c
+++ b/drivers/leds/leds-lm355x.c
@@ -16,7 +16,6 @@
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/regmap.h>
-#include <linux/workqueue.h>
#include <linux/platform_data/leds-lm355x.h>
enum lm355x_type {
@@ -59,14 +58,6 @@ struct lm355x_chip_data {
struct led_classdev cdev_torch;
struct led_classdev cdev_indicator;
- struct work_struct work_flash;
- struct work_struct work_torch;
- struct work_struct work_indicator;
-
- u8 br_flash;
- u8 br_torch;
- u8 br_indicator;
-
struct lm355x_platform_data *pdata;
struct regmap *regmap;
struct mutex lock;
@@ -204,7 +195,7 @@ out:
}
/* chip control */
-static void lm355x_control(struct lm355x_chip_data *chip,
+static int lm355x_control(struct lm355x_chip_data *chip,
u8 brightness, enum lm355x_mode opmode)
{
int ret;
@@ -301,7 +292,7 @@ static void lm355x_control(struct lm355x_chip_data *chip,
case MODE_SHDN:
break;
default:
- return;
+ return -EINVAL;
}
/* operation mode control */
ret = regmap_update_bits(chip->regmap, preg[REG_OPMODE].regno,
@@ -309,73 +300,55 @@ static void lm355x_control(struct lm355x_chip_data *chip,
opmode << preg[REG_OPMODE].shift);
if (ret < 0)
goto out;
- return;
+ return ret;
out:
dev_err(chip->dev, "%s:i2c access fail to register\n", __func__);
- return;
+ return ret;
}
/* torch */
-static void lm355x_deferred_torch_brightness_set(struct work_struct *work)
-{
- struct lm355x_chip_data *chip =
- container_of(work, struct lm355x_chip_data, work_torch);
- mutex_lock(&chip->lock);
- lm355x_control(chip, chip->br_torch, MODE_TORCH);
- mutex_unlock(&chip->lock);
-}
-
-static void lm355x_torch_brightness_set(struct led_classdev *cdev,
+static int lm355x_torch_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct lm355x_chip_data *chip =
container_of(cdev, struct lm355x_chip_data, cdev_torch);
-
- chip->br_torch = brightness;
- schedule_work(&chip->work_torch);
-}
-
-/* flash */
-static void lm355x_deferred_strobe_brightness_set(struct work_struct *work)
-{
- struct lm355x_chip_data *chip =
- container_of(work, struct lm355x_chip_data, work_flash);
+ int ret;
mutex_lock(&chip->lock);
- lm355x_control(chip, chip->br_flash, MODE_FLASH);
+ ret = lm355x_control(chip, brightness, MODE_TORCH);
mutex_unlock(&chip->lock);
+ return ret;
}
-static void lm355x_strobe_brightness_set(struct led_classdev *cdev,
+/* flash */
+
+static int lm355x_strobe_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct lm355x_chip_data *chip =
container_of(cdev, struct lm355x_chip_data, cdev_flash);
-
- chip->br_flash = brightness;
- schedule_work(&chip->work_flash);
-}
-
-/* indicator */
-static void lm355x_deferred_indicator_brightness_set(struct work_struct *work)
-{
- struct lm355x_chip_data *chip =
- container_of(work, struct lm355x_chip_data, work_indicator);
+ int ret;
mutex_lock(&chip->lock);
- lm355x_control(chip, chip->br_indicator, MODE_INDIC);
+ ret = lm355x_control(chip, brightness, MODE_FLASH);
mutex_unlock(&chip->lock);
+ return ret;
}
-static void lm355x_indicator_brightness_set(struct led_classdev *cdev,
+/* indicator */
+
+static int lm355x_indicator_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct lm355x_chip_data *chip =
container_of(cdev, struct lm355x_chip_data, cdev_indicator);
+ int ret;
- chip->br_indicator = brightness;
- schedule_work(&chip->work_indicator);
+ mutex_lock(&chip->lock);
+ ret = lm355x_control(chip, brightness, MODE_INDIC);
+ mutex_unlock(&chip->lock);
+ return ret;
}
/* indicator pattern only for lm3556*/
@@ -479,34 +452,31 @@ static int lm355x_probe(struct i2c_client *client,
goto err_out;
/* flash */
- INIT_WORK(&chip->work_flash, lm355x_deferred_strobe_brightness_set);
chip->cdev_flash.name = "flash";
chip->cdev_flash.max_brightness = 16;
- chip->cdev_flash.brightness_set = lm355x_strobe_brightness_set;
+ chip->cdev_flash.brightness_set_blocking = lm355x_strobe_brightness_set;
chip->cdev_flash.default_trigger = "flash";
err = led_classdev_register((struct device *)
&client->dev, &chip->cdev_flash);
if (err < 0)
goto err_out;
/* torch */
- INIT_WORK(&chip->work_torch, lm355x_deferred_torch_brightness_set);
chip->cdev_torch.name = "torch";
chip->cdev_torch.max_brightness = 8;
- chip->cdev_torch.brightness_set = lm355x_torch_brightness_set;
+ chip->cdev_torch.brightness_set_blocking = lm355x_torch_brightness_set;
chip->cdev_torch.default_trigger = "torch";
err = led_classdev_register((struct device *)
&client->dev, &chip->cdev_torch);
if (err < 0)
goto err_create_torch_file;
/* indicator */
- INIT_WORK(&chip->work_indicator,
- lm355x_deferred_indicator_brightness_set);
chip->cdev_indicator.name = "indicator";
if (id->driver_data == CHIP_LM3554)
chip->cdev_indicator.max_brightness = 4;
else
chip->cdev_indicator.max_brightness = 8;
- chip->cdev_indicator.brightness_set = lm355x_indicator_brightness_set;
+ chip->cdev_indicator.brightness_set_blocking =
+ lm355x_indicator_brightness_set;
/* indicator pattern control only for LM3556 */
if (id->driver_data == CHIP_LM3556)
chip->cdev_indicator.groups = lm355x_indicator_groups;
@@ -534,11 +504,8 @@ static int lm355x_remove(struct i2c_client *client)
regmap_write(chip->regmap, preg[REG_OPMODE].regno, 0);
led_classdev_unregister(&chip->cdev_indicator);
- flush_work(&chip->work_indicator);
led_classdev_unregister(&chip->cdev_torch);
- flush_work(&chip->work_torch);
led_classdev_unregister(&chip->cdev_flash);
- flush_work(&chip->work_flash);
dev_info(&client->dev, "%s is removed\n", lm355x_name[chip->type]);
return 0;
diff --git a/drivers/leds/leds-lm3642.c b/drivers/leds/leds-lm3642.c
index 02ebe342f5af..cada0848db7b 100644
--- a/drivers/leds/leds-lm3642.c
+++ b/drivers/leds/leds-lm3642.c
@@ -15,7 +15,6 @@
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/regmap.h>
-#include <linux/workqueue.h>
#include <linux/platform_data/leds-lm3642.h>
#define REG_FILT_TIME (0x0)
@@ -73,10 +72,6 @@ struct lm3642_chip_data {
struct led_classdev cdev_torch;
struct led_classdev cdev_indicator;
- struct work_struct work_flash;
- struct work_struct work_torch;
- struct work_struct work_indicator;
-
u8 br_flash;
u8 br_torch;
u8 br_indicator;
@@ -209,24 +204,18 @@ out_strtoint:
static DEVICE_ATTR(torch_pin, S_IWUSR, NULL, lm3642_torch_pin_store);
-static void lm3642_deferred_torch_brightness_set(struct work_struct *work)
-{
- struct lm3642_chip_data *chip =
- container_of(work, struct lm3642_chip_data, work_torch);
-
- mutex_lock(&chip->lock);
- lm3642_control(chip, chip->br_torch, MODES_TORCH);
- mutex_unlock(&chip->lock);
-}
-
-static void lm3642_torch_brightness_set(struct led_classdev *cdev,
+static int lm3642_torch_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct lm3642_chip_data *chip =
container_of(cdev, struct lm3642_chip_data, cdev_torch);
+ int ret;
+ mutex_lock(&chip->lock);
chip->br_torch = brightness;
- schedule_work(&chip->work_torch);
+ ret = lm3642_control(chip, chip->br_torch, MODES_TORCH);
+ mutex_unlock(&chip->lock);
+ return ret;
}
/* flash */
@@ -266,45 +255,33 @@ out_strtoint:
static DEVICE_ATTR(strobe_pin, S_IWUSR, NULL, lm3642_strobe_pin_store);
-static void lm3642_deferred_strobe_brightness_set(struct work_struct *work)
-{
- struct lm3642_chip_data *chip =
- container_of(work, struct lm3642_chip_data, work_flash);
-
- mutex_lock(&chip->lock);
- lm3642_control(chip, chip->br_flash, MODES_FLASH);
- mutex_unlock(&chip->lock);
-}
-
-static void lm3642_strobe_brightness_set(struct led_classdev *cdev,
+static int lm3642_strobe_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct lm3642_chip_data *chip =
container_of(cdev, struct lm3642_chip_data, cdev_flash);
-
- chip->br_flash = brightness;
- schedule_work(&chip->work_flash);
-}
-
-/* indicator */
-static void lm3642_deferred_indicator_brightness_set(struct work_struct *work)
-{
- struct lm3642_chip_data *chip =
- container_of(work, struct lm3642_chip_data, work_indicator);
+ int ret;
mutex_lock(&chip->lock);
- lm3642_control(chip, chip->br_indicator, MODES_INDIC);
+ chip->br_flash = brightness;
+ ret = lm3642_control(chip, chip->br_flash, MODES_FLASH);
mutex_unlock(&chip->lock);
+ return ret;
}
-static void lm3642_indicator_brightness_set(struct led_classdev *cdev,
+/* indicator */
+static int lm3642_indicator_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct lm3642_chip_data *chip =
container_of(cdev, struct lm3642_chip_data, cdev_indicator);
+ int ret;
+ mutex_lock(&chip->lock);
chip->br_indicator = brightness;
- schedule_work(&chip->work_indicator);
+ ret = lm3642_control(chip, chip->br_indicator, MODES_INDIC);
+ mutex_unlock(&chip->lock);
+ return ret;
}
static const struct regmap_config lm3642_regmap = {
@@ -371,10 +348,9 @@ static int lm3642_probe(struct i2c_client *client,
goto err_out;
/* flash */
- INIT_WORK(&chip->work_flash, lm3642_deferred_strobe_brightness_set);
chip->cdev_flash.name = "flash";
chip->cdev_flash.max_brightness = 16;
- chip->cdev_flash.brightness_set = lm3642_strobe_brightness_set;
+ chip->cdev_flash.brightness_set_blocking = lm3642_strobe_brightness_set;
chip->cdev_flash.default_trigger = "flash";
chip->cdev_flash.groups = lm3642_flash_groups,
err = led_classdev_register((struct device *)
@@ -385,10 +361,9 @@ static int lm3642_probe(struct i2c_client *client,
}
/* torch */
- INIT_WORK(&chip->work_torch, lm3642_deferred_torch_brightness_set);
chip->cdev_torch.name = "torch";
chip->cdev_torch.max_brightness = 8;
- chip->cdev_torch.brightness_set = lm3642_torch_brightness_set;
+ chip->cdev_torch.brightness_set_blocking = lm3642_torch_brightness_set;
chip->cdev_torch.default_trigger = "torch";
chip->cdev_torch.groups = lm3642_torch_groups,
err = led_classdev_register((struct device *)
@@ -399,11 +374,10 @@ static int lm3642_probe(struct i2c_client *client,
}
/* indicator */
- INIT_WORK(&chip->work_indicator,
- lm3642_deferred_indicator_brightness_set);
chip->cdev_indicator.name = "indicator";
chip->cdev_indicator.max_brightness = 8;
- chip->cdev_indicator.brightness_set = lm3642_indicator_brightness_set;
+ chip->cdev_indicator.brightness_set_blocking =
+ lm3642_indicator_brightness_set;
err = led_classdev_register((struct device *)
&client->dev, &chip->cdev_indicator);
if (err < 0) {
@@ -427,11 +401,8 @@ static int lm3642_remove(struct i2c_client *client)
struct lm3642_chip_data *chip = i2c_get_clientdata(client);
led_classdev_unregister(&chip->cdev_indicator);
- flush_work(&chip->work_indicator);
led_classdev_unregister(&chip->cdev_torch);
- flush_work(&chip->work_torch);
led_classdev_unregister(&chip->cdev_flash);
- flush_work(&chip->work_flash);
regmap_write(chip->regmap, REG_ENABLE, 0);
return 0;
}
diff --git a/drivers/leds/leds-lp3944.c b/drivers/leds/leds-lp3944.c
index 53144fb96167..6c758aea1bbd 100644
--- a/drivers/leds/leds-lp3944.c
+++ b/drivers/leds/leds-lp3944.c
@@ -31,7 +31,6 @@
#include <linux/slab.h>
#include <linux/leds.h>
#include <linux/mutex.h>
-#include <linux/workqueue.h>
#include <linux/leds-lp3944.h>
/* Read Only Registers */
@@ -68,10 +67,8 @@
struct lp3944_led_data {
u8 id;
enum lp3944_type type;
- enum lp3944_status status;
struct led_classdev ldev;
struct i2c_client *client;
- struct work_struct work;
};
struct lp3944_data {
@@ -275,13 +272,12 @@ static int lp3944_led_set_blink(struct led_classdev *led_cdev,
dev_dbg(&led->client->dev, "%s: OK hardware accelerated blink!\n",
__func__);
- led->status = LP3944_LED_STATUS_DIM0;
- schedule_work(&led->work);
+ lp3944_led_set(led, LP3944_LED_STATUS_DIM0);
return 0;
}
-static void lp3944_led_set_brightness(struct led_classdev *led_cdev,
+static int lp3944_led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct lp3944_led_data *led = ldev_to_led(led_cdev);
@@ -289,16 +285,7 @@ static void lp3944_led_set_brightness(struct led_classdev *led_cdev,
dev_dbg(&led->client->dev, "%s: %s, %d\n",
__func__, led_cdev->name, brightness);
- led->status = !!brightness;
- schedule_work(&led->work);
-}
-
-static void lp3944_led_work(struct work_struct *work)
-{
- struct lp3944_led_data *led;
-
- led = container_of(work, struct lp3944_led_data, work);
- lp3944_led_set(led, led->status);
+ return lp3944_led_set(led, !!brightness);
}
static int lp3944_configure(struct i2c_client *client,
@@ -318,14 +305,13 @@ static int lp3944_configure(struct i2c_client *client,
case LP3944_LED_TYPE_LED:
case LP3944_LED_TYPE_LED_INVERTED:
led->type = pled->type;
- led->status = pled->status;
led->ldev.name = pled->name;
led->ldev.max_brightness = 1;
- led->ldev.brightness_set = lp3944_led_set_brightness;
+ led->ldev.brightness_set_blocking =
+ lp3944_led_set_brightness;
led->ldev.blink_set = lp3944_led_set_blink;
led->ldev.flags = LED_CORE_SUSPENDRESUME;
- INIT_WORK(&led->work, lp3944_led_work);
err = led_classdev_register(&client->dev, &led->ldev);
if (err < 0) {
dev_err(&client->dev,
@@ -336,14 +322,14 @@ static int lp3944_configure(struct i2c_client *client,
/* to expose the default value to userspace */
led->ldev.brightness =
- (enum led_brightness) led->status;
+ (enum led_brightness) pled->status;
/* Set the default led status */
- err = lp3944_led_set(led, led->status);
+ err = lp3944_led_set(led, pled->status);
if (err < 0) {
dev_err(&client->dev,
"%s couldn't set STATUS %d\n",
- led->ldev.name, led->status);
+ led->ldev.name, pled->status);
goto exit;
}
break;
@@ -364,7 +350,6 @@ exit:
case LP3944_LED_TYPE_LED:
case LP3944_LED_TYPE_LED_INVERTED:
led_classdev_unregister(&data->leds[i].ldev);
- cancel_work_sync(&data->leds[i].work);
break;
case LP3944_LED_TYPE_NONE:
@@ -424,7 +409,6 @@ static int lp3944_remove(struct i2c_client *client)
case LP3944_LED_TYPE_LED:
case LP3944_LED_TYPE_LED_INVERTED:
led_classdev_unregister(&data->leds[i].ldev);
- cancel_work_sync(&data->leds[i].work);
break;
case LP3944_LED_TYPE_NONE:
diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
index 63a92542c8cb..549b315ca8fe 100644
--- a/drivers/leds/leds-lp5521.c
+++ b/drivers/leds/leds-lp5521.c
@@ -362,16 +362,17 @@ static int lp5521_run_selftest(struct lp55xx_chip *chip, char *buf)
return 0;
}
-static void lp5521_led_brightness_work(struct work_struct *work)
+static int lp5521_led_brightness(struct lp55xx_led *led)
{
- struct lp55xx_led *led = container_of(work, struct lp55xx_led,
- brightness_work);
struct lp55xx_chip *chip = led->chip;
+ int ret;
mutex_lock(&chip->lock);
- lp55xx_write(chip, LP5521_REG_LED_PWM_BASE + led->chan_nr,
+ ret = lp55xx_write(chip, LP5521_REG_LED_PWM_BASE + led->chan_nr,
led->brightness);
mutex_unlock(&chip->lock);
+
+ return ret;
}
static ssize_t show_engine_mode(struct device *dev,
@@ -501,7 +502,7 @@ static struct lp55xx_device_config lp5521_cfg = {
},
.max_channel = LP5521_MAX_LEDS,
.post_init_device = lp5521_post_init_device,
- .brightness_work_fn = lp5521_led_brightness_work,
+ .brightness_fn = lp5521_led_brightness,
.set_led_current = lp5521_set_led_current,
.firmware_cb = lp5521_firmware_loaded,
.run_engine = lp5521_run_engine,
diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c
index 1d0187f42941..c5b30f06218a 100644
--- a/drivers/leds/leds-lp5523.c
+++ b/drivers/leds/leds-lp5523.c
@@ -802,16 +802,16 @@ leave:
return ret;
}
-static void lp5523_led_brightness_work(struct work_struct *work)
+static int lp5523_led_brightness(struct lp55xx_led *led)
{
- struct lp55xx_led *led = container_of(work, struct lp55xx_led,
- brightness_work);
struct lp55xx_chip *chip = led->chip;
+ int ret;
mutex_lock(&chip->lock);
- lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr,
+ ret = lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr,
led->brightness);
mutex_unlock(&chip->lock);
+ return ret;
}
static LP55XX_DEV_ATTR_RW(engine1_mode, show_engine1_mode, store_engine1_mode);
@@ -867,7 +867,7 @@ static struct lp55xx_device_config lp5523_cfg = {
},
.max_channel = LP5523_MAX_LEDS,
.post_init_device = lp5523_post_init_device,
- .brightness_work_fn = lp5523_led_brightness_work,
+ .brightness_fn = lp5523_led_brightness,
.set_led_current = lp5523_set_led_current,
.firmware_cb = lp5523_firmware_loaded,
.run_engine = lp5523_run_engine,
diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c
index 0360c59dbdc9..b75333803a63 100644
--- a/drivers/leds/leds-lp5562.c
+++ b/drivers/leds/leds-lp5562.c
@@ -311,10 +311,8 @@ static int lp5562_post_init_device(struct lp55xx_chip *chip)
return 0;
}
-static void lp5562_led_brightness_work(struct work_struct *work)
+static int lp5562_led_brightness(struct lp55xx_led *led)
{
- struct lp55xx_led *led = container_of(work, struct lp55xx_led,
- brightness_work);
struct lp55xx_chip *chip = led->chip;
u8 addr[] = {
LP5562_REG_R_PWM,
@@ -322,10 +320,13 @@ static void lp5562_led_brightness_work(struct work_struct *work)
LP5562_REG_B_PWM,
LP5562_REG_W_PWM,
};
+ int ret;
mutex_lock(&chip->lock);
- lp55xx_write(chip, addr[led->chan_nr], led->brightness);
+ ret = lp55xx_write(chip, addr[led->chan_nr], led->brightness);
mutex_unlock(&chip->lock);
+
+ return ret;
}
static void lp5562_write_program_memory(struct lp55xx_chip *chip,
@@ -503,7 +504,7 @@ static struct lp55xx_device_config lp5562_cfg = {
},
.post_init_device = lp5562_post_init_device,
.set_led_current = lp5562_set_led_current,
- .brightness_work_fn = lp5562_led_brightness_work,
+ .brightness_fn = lp5562_led_brightness,
.run_engine = lp5562_run_engine,
.firmware_cb = lp5562_firmware_loaded,
.dev_attr_group = &lp5562_group,
diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c
index 59b76833f0d3..5377f22ff994 100644
--- a/drivers/leds/leds-lp55xx-common.c
+++ b/drivers/leds/leds-lp55xx-common.c
@@ -134,13 +134,14 @@ static struct attribute *lp55xx_led_attrs[] = {
};
ATTRIBUTE_GROUPS(lp55xx_led);
-static void lp55xx_set_brightness(struct led_classdev *cdev,
+static int lp55xx_set_brightness(struct led_classdev *cdev,
enum led_brightness brightness)
{
struct lp55xx_led *led = cdev_to_lp55xx_led(cdev);
+ struct lp55xx_device_config *cfg = led->chip->cfg;
led->brightness = (u8)brightness;
- schedule_work(&led->brightness_work);
+ return cfg->brightness_fn(led);
}
static int lp55xx_init_led(struct lp55xx_led *led,
@@ -172,7 +173,7 @@ static int lp55xx_init_led(struct lp55xx_led *led,
return -EINVAL;
}
- led->cdev.brightness_set = lp55xx_set_brightness;
+ led->cdev.brightness_set_blocking = lp55xx_set_brightness;
led->cdev.groups = lp55xx_led_groups;
if (pdata->led_config[chan].name) {
@@ -464,7 +465,7 @@ int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip)
int ret;
int i;
- if (!cfg->brightness_work_fn) {
+ if (!cfg->brightness_fn) {
dev_err(&chip->cl->dev, "empty brightness configuration\n");
return -EINVAL;
}
@@ -481,8 +482,6 @@ int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip)
if (ret)
goto err_init_led;
- INIT_WORK(&each->brightness_work, cfg->brightness_work_fn);
-
chip->num_leds++;
each->chip = chip;
@@ -507,7 +506,6 @@ void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip)
for (i = 0; i < chip->num_leds; i++) {
each = led + i;
led_classdev_unregister(&each->cdev);
- flush_work(&each->brightness_work);
}
}
EXPORT_SYMBOL_GPL(lp55xx_unregister_leds);
diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h
index c7f1e6155001..abf1fb5da37d 100644
--- a/drivers/leds/leds-lp55xx-common.h
+++ b/drivers/leds/leds-lp55xx-common.h
@@ -95,7 +95,7 @@ struct lp55xx_reg {
* @enable : Chip specific enable command
* @max_channel : Maximum number of channels
* @post_init_device : Chip specific initialization code
- * @brightness_work_fn : Brightness work function
+ * @brightness_fn : Brightness function
* @set_led_current : LED current set function
* @firmware_cb : Call function when the firmware is loaded
* @run_engine : Run internal engine for pattern
@@ -110,7 +110,7 @@ struct lp55xx_device_config {
int (*post_init_device) (struct lp55xx_chip *chip);
/* access brightness register */
- void (*brightness_work_fn)(struct work_struct *work);
+ int (*brightness_fn)(struct lp55xx_led *led);
/* current setting function */
void (*set_led_current) (struct lp55xx_led *led, u8 led_current);
@@ -164,7 +164,6 @@ struct lp55xx_chip {
* @cdev : LED class device
* @led_current : Current setting at each led channel
* @max_current : Maximun current at each led channel
- * @brightness_work : Workqueue for brightness control
* @brightness : Brightness value
* @chip : The lp55xx chip data
*/
@@ -173,7 +172,6 @@ struct lp55xx_led {
struct led_classdev cdev;
u8 led_current;
u8 max_current;
- struct work_struct brightness_work;
u8 brightness;
struct lp55xx_chip *chip;
};
diff --git a/drivers/leds/leds-lp8501.c b/drivers/leds/leds-lp8501.c
index 3f54f6f2b821..3f9675bd214a 100644
--- a/drivers/leds/leds-lp8501.c
+++ b/drivers/leds/leds-lp8501.c
@@ -272,16 +272,17 @@ static void lp8501_firmware_loaded(struct lp55xx_chip *chip)
lp8501_update_program_memory(chip, fw->data, fw->size);
}
-static void lp8501_led_brightness_work(struct work_struct *work)
+static int lp8501_led_brightness(struct lp55xx_led *led)
{
- struct lp55xx_led *led = container_of(work, struct lp55xx_led,
- brightness_work);
struct lp55xx_chip *chip = led->chip;
+ int ret;
mutex_lock(&chip->lock);
- lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + led->chan_nr,
+ ret = lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + led->chan_nr,
led->brightness);
mutex_unlock(&chip->lock);
+
+ return ret;
}
/* Chip specific configurations */
@@ -296,7 +297,7 @@ static struct lp55xx_device_config lp8501_cfg = {
},
.max_channel = LP8501_MAX_LEDS,
.post_init_device = lp8501_post_init_device,
- .brightness_work_fn = lp8501_led_brightness_work,
+ .brightness_fn = lp8501_led_brightness,
.set_led_current = lp8501_set_led_current,
.firmware_cb = lp8501_firmware_loaded,
.run_engine = lp8501_run_engine,
diff --git a/drivers/leds/leds-lp8788.c b/drivers/leds/leds-lp8788.c
index 3409f03c1fa8..0eee38fc0565 100644
--- a/drivers/leds/leds-lp8788.c
+++ b/drivers/leds/leds-lp8788.c
@@ -26,10 +26,8 @@
struct lp8788_led {
struct lp8788 *lp;
struct mutex lock;
- struct work_struct work;
struct led_classdev led_dev;
enum lp8788_isink_number isink_num;
- enum led_brightness brightness;
int on;
};
@@ -76,24 +74,29 @@ static int lp8788_led_init_device(struct lp8788_led *led,
return lp8788_update_bits(led->lp, addr, mask, val);
}
-static void lp8788_led_enable(struct lp8788_led *led,
+static int lp8788_led_enable(struct lp8788_led *led,
enum lp8788_isink_number num, int on)
{
+ int ret;
+
u8 mask = 1 << num;
u8 val = on << num;
- if (lp8788_update_bits(led->lp, LP8788_ISINK_CTRL, mask, val))
- return;
+ ret = lp8788_update_bits(led->lp, LP8788_ISINK_CTRL, mask, val);
+ if (ret == 0)
+ led->on = on;
- led->on = on;
+ return ret;
}
-static void lp8788_led_work(struct work_struct *work)
+static int lp8788_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness val)
{
- struct lp8788_led *led = container_of(work, struct lp8788_led, work);
+ struct lp8788_led *led =
+ container_of(led_cdev, struct lp8788_led, led_dev);
+
enum lp8788_isink_number num = led->isink_num;
- int enable;
- u8 val = led->brightness;
+ int enable, ret;
mutex_lock(&led->lock);
@@ -101,28 +104,21 @@ static void lp8788_led_work(struct work_struct *work)
case LP8788_ISINK_1:
case LP8788_ISINK_2:
case LP8788_ISINK_3:
- lp8788_write_byte(led->lp, lp8788_pwm_addr[num], val);
+ ret = lp8788_write_byte(led->lp, lp8788_pwm_addr[num], val);
+ if (ret < 0)
+ goto unlock;
break;
default:
mutex_unlock(&led->lock);
- return;
+ return -EINVAL;
}
enable = (val > 0) ? 1 : 0;
if (enable != led->on)
- lp8788_led_enable(led, num, enable);
-
+ ret = lp8788_led_enable(led, num, enable);
+unlock:
mutex_unlock(&led->lock);
-}
-
-static void lp8788_brightness_set(struct led_classdev *led_cdev,
- enum led_brightness brt_val)
-{
- struct lp8788_led *led =
- container_of(led_cdev, struct lp8788_led, led_dev);
-
- led->brightness = brt_val;
- schedule_work(&led->work);
+ return ret;
}
static int lp8788_led_probe(struct platform_device *pdev)
@@ -139,7 +135,7 @@ static int lp8788_led_probe(struct platform_device *pdev)
led->lp = lp;
led->led_dev.max_brightness = MAX_BRIGHTNESS;
- led->led_dev.brightness_set = lp8788_brightness_set;
+ led->led_dev.brightness_set_blocking = lp8788_brightness_set;
led_pdata = lp->pdata ? lp->pdata->led_pdata : NULL;
@@ -149,7 +145,6 @@ static int lp8788_led_probe(struct platform_device *pdev)
led->led_dev.name = led_pdata->name;
mutex_init(&led->lock);
- INIT_WORK(&led->work, lp8788_led_work);
platform_set_drvdata(pdev, led);
@@ -173,7 +168,6 @@ static int lp8788_led_remove(struct platform_device *pdev)
struct lp8788_led *led = platform_get_drvdata(pdev);
led_classdev_unregister(&led->led_dev);
- flush_work(&led->work);
return 0;
}
diff --git a/drivers/leds/leds-lp8860.c b/drivers/leds/leds-lp8860.c
index 79f084354e67..3e70775a2d54 100644
--- a/drivers/leds/leds-lp8860.c
+++ b/drivers/leds/leds-lp8860.c
@@ -91,26 +91,22 @@
/**
* struct lp8860_led -
* @lock - Lock for reading/writing the device
- * @work - Work item used to off load the brightness register writes
* @client - Pointer to the I2C client
* @led_dev - led class device pointer
* @regmap - Devices register map
* @eeprom_regmap - EEPROM register map
* @enable_gpio - VDDIO/EN gpio to enable communication interface
* @regulator - LED supply regulator pointer
- * @brightness - Current brightness value requested
* @label - LED label
**/
struct lp8860_led {
struct mutex lock;
- struct work_struct work;
struct i2c_client *client;
struct led_classdev led_dev;
struct regmap *regmap;
struct regmap *eeprom_regmap;
struct gpio_desc *enable_gpio;
struct regulator *regulator;
- enum led_brightness brightness;
const char *label;
};
@@ -212,11 +208,13 @@ out:
return ret;
}
-static void lp8860_led_brightness_work(struct work_struct *work)
+static int lp8860_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brt_val)
{
- struct lp8860_led *led = container_of(work, struct lp8860_led, work);
+ struct lp8860_led *led =
+ container_of(led_cdev, struct lp8860_led, led_dev);
+ int disp_brightness = brt_val * 255;
int ret;
- int disp_brightness = led->brightness * 255;
mutex_lock(&led->lock);
@@ -241,16 +239,7 @@ static void lp8860_led_brightness_work(struct work_struct *work)
}
out:
mutex_unlock(&led->lock);
-}
-
-static void lp8860_brightness_set(struct led_classdev *led_cdev,
- enum led_brightness brt_val)
-{
- struct lp8860_led *led =
- container_of(led_cdev, struct lp8860_led, led_dev);
-
- led->brightness = brt_val;
- schedule_work(&led->work);
+ return ret;
}
static int lp8860_init(struct lp8860_led *led)
@@ -406,10 +395,9 @@ static int lp8860_probe(struct i2c_client *client,
led->client = client;
led->led_dev.name = led->label;
led->led_dev.max_brightness = LED_FULL;
- led->led_dev.brightness_set = lp8860_brightness_set;
+ led->led_dev.brightness_set_blocking = lp8860_brightness_set;
mutex_init(&led->lock);
- INIT_WORK(&led->work, lp8860_led_brightness_work);
i2c_set_clientdata(client, led);
@@ -448,7 +436,6 @@ static int lp8860_remove(struct i2c_client *client)
int ret;
led_classdev_unregister(&led->led_dev);
- cancel_work_sync(&led->work);
if (led->enable_gpio)
gpiod_direction_output(led->enable_gpio, 0);
diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c
index 9f41124765cc..a7ff510cbdd0 100644
--- a/drivers/leds/leds-lt3593.c
+++ b/drivers/leds/leds-lt3593.c
@@ -19,7 +19,6 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
-#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/slab.h>
@@ -28,15 +27,14 @@
struct lt3593_led_data {
struct led_classdev cdev;
unsigned gpio;
- struct work_struct work;
- u8 new_level;
};
-static void lt3593_led_work(struct work_struct *work)
+static int lt3593_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
{
- int pulses;
struct lt3593_led_data *led_dat =
- container_of(work, struct lt3593_led_data, work);
+ container_of(led_cdev, struct lt3593_led_data, cdev);
+ int pulses;
/*
* The LT3593 resets its internal current level register to the maximum
@@ -47,18 +45,18 @@ static void lt3593_led_work(struct work_struct *work)
* applied is to the output driver.
*/
- if (led_dat->new_level == 0) {
+ if (value == 0) {
gpio_set_value_cansleep(led_dat->gpio, 0);
- return;
+ return 0;
}
- pulses = 32 - (led_dat->new_level * 32) / 255;
+ pulses = 32 - (value * 32) / 255;
if (pulses == 0) {
gpio_set_value_cansleep(led_dat->gpio, 0);
mdelay(1);
gpio_set_value_cansleep(led_dat->gpio, 1);
- return;
+ return 0;
}
gpio_set_value_cansleep(led_dat->gpio, 1);
@@ -69,16 +67,8 @@ static void lt3593_led_work(struct work_struct *work)
gpio_set_value_cansleep(led_dat->gpio, 1);
udelay(1);
}
-}
-static void lt3593_led_set(struct led_classdev *led_cdev,
- enum led_brightness value)
-{
- struct lt3593_led_data *led_dat =
- container_of(led_cdev, struct lt3593_led_data, cdev);
-
- led_dat->new_level = value;
- schedule_work(&led_dat->work);
+ return 0;
}
static int create_lt3593_led(const struct gpio_led *template,
@@ -97,7 +87,7 @@ static int create_lt3593_led(const struct gpio_led *template,
led_dat->cdev.default_trigger = template->default_trigger;
led_dat->gpio = template->gpio;
- led_dat->cdev.brightness_set = lt3593_led_set;
+ led_dat->cdev.brightness_set_blocking = lt3593_led_set;
state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
@@ -111,8 +101,6 @@ static int create_lt3593_led(const struct gpio_led *template,
if (ret < 0)
return ret;
- INIT_WORK(&led_dat->work, lt3593_led_work);
-
ret = led_classdev_register(parent, &led_dat->cdev);
if (ret < 0)
return ret;
@@ -129,7 +117,6 @@ static void delete_lt3593_led(struct lt3593_led_data *led)
return;
led_classdev_unregister(&led->cdev);
- cancel_work_sync(&led->work);
}
static int lt3593_led_probe(struct platform_device *pdev)
diff --git a/drivers/leds/leds-max77693.c b/drivers/leds/leds-max77693.c
index afbb1409b2e2..1eb58ef6aefe 100644
--- a/drivers/leds/leds-max77693.c
+++ b/drivers/leds/leds-max77693.c
@@ -20,7 +20,6 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
-#include <linux/workqueue.h>
#include <media/v4l2-flash-led-class.h>
#define MODE_OFF 0
@@ -62,8 +61,6 @@ struct max77693_sub_led {
int fled_id;
/* corresponding LED Flash class device */
struct led_classdev_flash fled_cdev;
- /* assures led-triggers compatibility */
- struct work_struct work_brightness_set;
/* V4L2 Flash device */
struct v4l2_flash *v4l2_flash;
@@ -463,10 +460,14 @@ static int max77693_setup(struct max77693_led_device *led,
return max77693_set_mode_reg(led, MODE_OFF);
}
-static int __max77693_led_brightness_set(struct max77693_led_device *led,
- int fled_id, enum led_brightness value)
+/* LED subsystem callbacks */
+static int max77693_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
{
- int ret;
+ struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
+ struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev);
+ struct max77693_led_device *led = sub_led_to_led(sub_led);
+ int fled_id = sub_led->fled_id, ret;
mutex_lock(&led->lock);
@@ -494,43 +495,8 @@ static int __max77693_led_brightness_set(struct max77693_led_device *led,
ret);
unlock:
mutex_unlock(&led->lock);
- return ret;
-}
-
-static void max77693_led_brightness_set_work(
- struct work_struct *work)
-{
- struct max77693_sub_led *sub_led =
- container_of(work, struct max77693_sub_led,
- work_brightness_set);
- struct max77693_led_device *led = sub_led_to_led(sub_led);
-
- __max77693_led_brightness_set(led, sub_led->fled_id,
- sub_led->torch_brightness);
-}
-
-/* LED subsystem callbacks */
-
-static int max77693_led_brightness_set_sync(
- struct led_classdev *led_cdev,
- enum led_brightness value)
-{
- struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
- struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev);
- struct max77693_led_device *led = sub_led_to_led(sub_led);
-
- return __max77693_led_brightness_set(led, sub_led->fled_id, value);
-}
-static void max77693_led_brightness_set(
- struct led_classdev *led_cdev,
- enum led_brightness value)
-{
- struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
- struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev);
-
- sub_led->torch_brightness = value;
- schedule_work(&sub_led->work_brightness_set);
+ return ret;
}
static int max77693_led_flash_brightness_set(
@@ -682,6 +648,7 @@ static int max77693_led_parse_dt(struct max77693_led_device *led,
if (sub_nodes[fled_id]) {
dev_err(dev,
"Conflicting \"led-sources\" DT properties\n");
+ of_node_put(child_node);
return -EINVAL;
}
@@ -931,16 +898,13 @@ static void max77693_init_fled_cdev(struct max77693_sub_led *sub_led,
led_cdev->name = led_cfg->label[fled_id];
- led_cdev->brightness_set = max77693_led_brightness_set;
- led_cdev->brightness_set_sync = max77693_led_brightness_set_sync;
+ led_cdev->brightness_set_blocking = max77693_led_brightness_set;
led_cdev->max_brightness = (led->iout_joint ?
led_cfg->iout_torch_max[FLED1] +
led_cfg->iout_torch_max[FLED2] :
led_cfg->iout_torch_max[fled_id]) /
TORCH_IOUT_STEP;
led_cdev->flags |= LED_DEV_CAP_FLASH;
- INIT_WORK(&sub_led->work_brightness_set,
- max77693_led_brightness_set_work);
max77693_init_flash_settings(sub_led, led_cfg);
@@ -1062,13 +1026,11 @@ static int max77693_led_remove(struct platform_device *pdev)
if (led->iout_joint || max77693_fled_used(led, FLED1)) {
v4l2_flash_release(sub_leds[FLED1].v4l2_flash);
led_classdev_flash_unregister(&sub_leds[FLED1].fled_cdev);
- cancel_work_sync(&sub_leds[FLED1].work_brightness_set);
}
if (!led->iout_joint && max77693_fled_used(led, FLED2)) {
v4l2_flash_release(sub_leds[FLED2].v4l2_flash);
led_classdev_flash_unregister(&sub_leds[FLED2].fled_cdev);
- cancel_work_sync(&sub_leds[FLED2].work_brightness_set);
}
mutex_destroy(&led->lock);
diff --git a/drivers/leds/leds-max8997.c b/drivers/leds/leds-max8997.c
index c592aa5662bb..01b459069358 100644
--- a/drivers/leds/leds-max8997.c
+++ b/drivers/leds/leds-max8997.c
@@ -13,7 +13,6 @@
#include <linux/module.h>
#include <linux/err.h>
#include <linux/slab.h>
-#include <linux/workqueue.h>
#include <linux/leds.h>
#include <linux/mfd/max8997.h>
#include <linux/mfd/max8997-private.h>
diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c
index e2b847fe22a1..a2e4c1792e17 100644
--- a/drivers/leds/leds-mc13783.c
+++ b/drivers/leds/leds-mc13783.c
@@ -20,7 +20,6 @@
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/of.h>
-#include <linux/workqueue.h>
#include <linux/mfd/mc13xxx.h>
struct mc13xxx_led_devtype {
@@ -32,8 +31,6 @@ struct mc13xxx_led_devtype {
struct mc13xxx_led {
struct led_classdev cdev;
- struct work_struct work;
- enum led_brightness new_brightness;
int id;
struct mc13xxx_leds *leds;
};
@@ -55,9 +52,11 @@ static unsigned int mc13xxx_max_brightness(int id)
return 0x3f;
}
-static void mc13xxx_led_work(struct work_struct *work)
+static int mc13xxx_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
{
- struct mc13xxx_led *led = container_of(work, struct mc13xxx_led, work);
+ struct mc13xxx_led *led =
+ container_of(led_cdev, struct mc13xxx_led, cdev);
struct mc13xxx_leds *leds = led->leds;
unsigned int reg, bank, off, shift;
@@ -105,19 +104,9 @@ static void mc13xxx_led_work(struct work_struct *work)
BUG();
}
- mc13xxx_reg_rmw(leds->master, leds->devtype->ledctrl_base + reg,
+ return mc13xxx_reg_rmw(leds->master, leds->devtype->ledctrl_base + reg,
mc13xxx_max_brightness(led->id) << shift,
- led->new_brightness << shift);
-}
-
-static void mc13xxx_led_set(struct led_classdev *led_cdev,
- enum led_brightness value)
-{
- struct mc13xxx_led *led =
- container_of(led_cdev, struct mc13xxx_led, cdev);
-
- led->new_brightness = value;
- schedule_work(&led->work);
+ value << shift);
}
#ifdef CONFIG_OF
@@ -257,11 +246,9 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev)
leds->led[i].cdev.name = name;
leds->led[i].cdev.default_trigger = trig;
leds->led[i].cdev.flags = LED_CORE_SUSPENDRESUME;
- leds->led[i].cdev.brightness_set = mc13xxx_led_set;
+ leds->led[i].cdev.brightness_set_blocking = mc13xxx_led_set;
leds->led[i].cdev.max_brightness = mc13xxx_max_brightness(id);
- INIT_WORK(&leds->led[i].work, mc13xxx_led_work);
-
ret = led_classdev_register(dev->parent, &leds->led[i].cdev);
if (ret) {
dev_err(dev, "Failed to register LED %i\n", id);
@@ -270,10 +257,8 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev)
}
if (ret)
- while (--i >= 0) {
+ while (--i >= 0)
led_classdev_unregister(&leds->led[i].cdev);
- cancel_work_sync(&leds->led[i].work);
- }
return ret;
}
@@ -283,10 +268,8 @@ static int mc13xxx_led_remove(struct platform_device *pdev)
struct mc13xxx_leds *leds = platform_get_drvdata(pdev);
int i;
- for (i = 0; i < leds->num_leds; i++) {
+ for (i = 0; i < leds->num_leds; i++)
led_classdev_unregister(&leds->led[i].cdev);
- cancel_work_sync(&leds->led[i].work);
- }
return 0;
}
diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c
index a95a61220169..506b75b190e7 100644
--- a/drivers/leds/leds-ns2.c
+++ b/drivers/leds/leds-ns2.c
@@ -45,24 +45,12 @@ struct ns2_led_data {
unsigned cmd;
unsigned slow;
bool can_sleep;
- int mode_index;
unsigned char sata; /* True when SATA mode active. */
rwlock_t rw_lock; /* Lock GPIOs. */
- struct work_struct work;
int num_modes;
struct ns2_led_modval *modval;
};
-static void ns2_led_work(struct work_struct *work)
-{
- struct ns2_led_data *led_dat =
- container_of(work, struct ns2_led_data, work);
- int i = led_dat->mode_index;
-
- gpio_set_value_cansleep(led_dat->cmd, led_dat->modval[i].cmd_level);
- gpio_set_value_cansleep(led_dat->slow, led_dat->modval[i].slow_level);
-}
-
static int ns2_led_get_mode(struct ns2_led_data *led_dat,
enum ns2_led_modes *mode)
{
@@ -112,8 +100,8 @@ static void ns2_led_set_mode(struct ns2_led_data *led_dat,
goto exit_unlock;
}
- led_dat->mode_index = i;
- schedule_work(&led_dat->work);
+ gpio_set_value_cansleep(led_dat->cmd, led_dat->modval[i].cmd_level);
+ gpio_set_value_cansleep(led_dat->slow, led_dat->modval[i].slow_level);
exit_unlock:
write_unlock_irqrestore(&led_dat->rw_lock, flags);
@@ -136,6 +124,13 @@ static void ns2_led_set(struct led_classdev *led_cdev,
ns2_led_set_mode(led_dat, mode);
}
+static int ns2_led_set_blocking(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ ns2_led_set(led_cdev, value);
+ return 0;
+}
+
static ssize_t ns2_led_sata_store(struct device *dev,
struct device_attribute *attr,
const char *buff, size_t count)
@@ -219,13 +214,16 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
led_dat->cdev.name = template->name;
led_dat->cdev.default_trigger = template->default_trigger;
led_dat->cdev.blink_set = NULL;
- led_dat->cdev.brightness_set = ns2_led_set;
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
led_dat->cdev.groups = ns2_led_groups;
led_dat->cmd = template->cmd;
led_dat->slow = template->slow;
led_dat->can_sleep = gpio_cansleep(led_dat->cmd) |
gpio_cansleep(led_dat->slow);
+ if (led_dat->can_sleep)
+ led_dat->cdev.brightness_set_blocking = ns2_led_set_blocking;
+ else
+ led_dat->cdev.brightness_set = ns2_led_set;
led_dat->modval = template->modval;
led_dat->num_modes = template->num_modes;
@@ -238,8 +236,6 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
led_dat->cdev.brightness =
(mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL;
- INIT_WORK(&led_dat->work, ns2_led_work);
-
ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
if (ret < 0)
return ret;
@@ -250,7 +246,6 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
static void delete_ns2_led(struct ns2_led_data *led_dat)
{
led_classdev_unregister(&led_dat->cdev);
- cancel_work_sync(&led_dat->work);
}
#ifdef CONFIG_OF_GPIO
diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c
index 5a6363d161a2..17c63ec9fb9e 100644
--- a/drivers/leds/leds-pca9532.c
+++ b/drivers/leds/leds-pca9532.c
@@ -158,7 +158,7 @@ static void pca9532_setled(struct pca9532_led *led)
mutex_unlock(&data->update_lock);
}
-static void pca9532_set_brightness(struct led_classdev *led_cdev,
+static int pca9532_set_brightness(struct led_classdev *led_cdev,
enum led_brightness value)
{
int err = 0;
@@ -172,9 +172,12 @@ static void pca9532_set_brightness(struct led_classdev *led_cdev,
led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */
err = pca9532_calcpwm(led->client, 0, 0, value);
if (err)
- return; /* XXX: led api doesn't allow error code? */
+ return err;
}
- schedule_work(&led->work);
+ if (led->state == PCA9532_PWM0)
+ pca9532_setpwm(led->client, 0);
+ pca9532_setled(led);
+ return err;
}
static int pca9532_set_blink(struct led_classdev *led_cdev,
@@ -198,7 +201,10 @@ static int pca9532_set_blink(struct led_classdev *led_cdev,
err = pca9532_calcpwm(client, 0, psc, led_cdev->brightness);
if (err)
return err;
- schedule_work(&led->work);
+ if (led->state == PCA9532_PWM0)
+ pca9532_setpwm(led->client, 0);
+ pca9532_setled(led);
+
return 0;
}
@@ -233,15 +239,6 @@ static void pca9532_input_work(struct work_struct *work)
mutex_unlock(&data->update_lock);
}
-static void pca9532_led_work(struct work_struct *work)
-{
- struct pca9532_led *led;
- led = container_of(work, struct pca9532_led, work);
- if (led->state == PCA9532_PWM0)
- pca9532_setpwm(led->client, 0);
- pca9532_setled(led);
-}
-
#ifdef CONFIG_LEDS_PCA9532_GPIO
static int pca9532_gpio_request_pin(struct gpio_chip *gc, unsigned offset)
{
@@ -307,7 +304,6 @@ static int pca9532_destroy_devices(struct pca9532_data *data, int n_devs)
break;
case PCA9532_TYPE_LED:
led_classdev_unregister(&data->leds[i].ldev);
- cancel_work_sync(&data->leds[i].work);
break;
case PCA9532_TYPE_N2100_BEEP:
if (data->idev != NULL) {
@@ -359,9 +355,9 @@ static int pca9532_configure(struct i2c_client *client,
led->name = pled->name;
led->ldev.name = led->name;
led->ldev.brightness = LED_OFF;
- led->ldev.brightness_set = pca9532_set_brightness;
+ led->ldev.brightness_set_blocking =
+ pca9532_set_brightness;
led->ldev.blink_set = pca9532_set_blink;
- INIT_WORK(&led->work, pca9532_led_work);
err = led_classdev_register(&client->dev, &led->ldev);
if (err < 0) {
dev_err(&client->dev,
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
index b775e1efecd3..840401ae9a4e 100644
--- a/drivers/leds/leds-pca955x.c
+++ b/drivers/leds/leds-pca955x.c
@@ -47,7 +47,6 @@
#include <linux/leds.h>
#include <linux/err.h>
#include <linux/i2c.h>
-#include <linux/workqueue.h>
#include <linux/slab.h>
/* LED select registers determine the source that drives LED outputs */
@@ -110,8 +109,6 @@ struct pca955x {
struct pca955x_led {
struct pca955x *pca955x;
- struct work_struct work;
- enum led_brightness brightness;
struct led_classdev led_cdev;
int led_num; /* 0 .. 15 potentially */
char name[32];
@@ -193,7 +190,8 @@ static u8 pca955x_read_ls(struct i2c_client *client, int n)
pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n);
}
-static void pca955x_led_work(struct work_struct *work)
+static int pca955x_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
{
struct pca955x_led *pca955x_led;
struct pca955x *pca955x;
@@ -201,7 +199,7 @@ static void pca955x_led_work(struct work_struct *work)
int chip_ls; /* which LSx to use (0-3 potentially) */
int ls_led; /* which set of bits within LSx to use (0-3) */
- pca955x_led = container_of(work, struct pca955x_led, work);
+ pca955x_led = container_of(led_cdev, struct pca955x_led, led_cdev);
pca955x = pca955x_led->pca955x;
chip_ls = pca955x_led->led_num / 4;
@@ -211,7 +209,7 @@ static void pca955x_led_work(struct work_struct *work)
ls = pca955x_read_ls(pca955x->client, chip_ls);
- switch (pca955x_led->brightness) {
+ switch (value) {
case LED_FULL:
ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_ON);
break;
@@ -230,7 +228,7 @@ static void pca955x_led_work(struct work_struct *work)
* just turning off for all other values.
*/
pca955x_write_pwm(pca955x->client, 1,
- 255 - pca955x_led->brightness);
+ 255 - value);
ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1);
break;
}
@@ -238,21 +236,8 @@ static void pca955x_led_work(struct work_struct *work)
pca955x_write_ls(pca955x->client, chip_ls, ls);
mutex_unlock(&pca955x->lock);
-}
-
-static void pca955x_led_set(struct led_classdev *led_cdev, enum led_brightness value)
-{
- struct pca955x_led *pca955x;
-
- pca955x = container_of(led_cdev, struct pca955x_led, led_cdev);
-
- pca955x->brightness = value;
- /*
- * Must use workqueue for the actual I/O since I2C operations
- * can sleep.
- */
- schedule_work(&pca955x->work);
+ return 0;
}
static int pca955x_probe(struct i2c_client *client,
@@ -328,9 +313,7 @@ static int pca955x_probe(struct i2c_client *client,
}
pca955x_led->led_cdev.name = pca955x_led->name;
- pca955x_led->led_cdev.brightness_set = pca955x_led_set;
-
- INIT_WORK(&pca955x_led->work, pca955x_led_work);
+ pca955x_led->led_cdev.brightness_set_blocking = pca955x_led_set;
err = led_classdev_register(&client->dev,
&pca955x_led->led_cdev);
@@ -355,10 +338,8 @@ static int pca955x_probe(struct i2c_client *client,
return 0;
exit:
- while (i--) {
+ while (i--)
led_classdev_unregister(&pca955x->leds[i].led_cdev);
- cancel_work_sync(&pca955x->leds[i].work);
- }
return err;
}
@@ -368,10 +349,8 @@ static int pca955x_remove(struct i2c_client *client)
struct pca955x *pca955x = i2c_get_clientdata(client);
int i;
- for (i = 0; i < pca955x->chipdef->bits; i++) {
+ for (i = 0; i < pca955x->chipdef->bits; i++)
led_classdev_unregister(&pca955x->leds[i].led_cdev);
- cancel_work_sync(&pca955x->leds[i].work);
- }
return 0;
}
diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c
index 41f269fe0920..407eba11e187 100644
--- a/drivers/leds/leds-pca963x.c
+++ b/drivers/leds/leds-pca963x.c
@@ -32,7 +32,6 @@
#include <linux/leds.h>
#include <linux/err.h>
#include <linux/i2c.h>
-#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/platform_data/leds-pca963x.h>
@@ -96,11 +95,6 @@ static const struct i2c_device_id pca963x_id[] = {
};
MODULE_DEVICE_TABLE(i2c, pca963x_id);
-enum pca963x_cmd {
- BRIGHTNESS_SET,
- BLINK_SET,
-};
-
struct pca963x_led;
struct pca963x {
@@ -112,47 +106,52 @@ struct pca963x {
struct pca963x_led {
struct pca963x *chip;
- struct work_struct work;
- enum led_brightness brightness;
struct led_classdev led_cdev;
int led_num; /* 0 .. 15 potentially */
- enum pca963x_cmd cmd;
char name[32];
u8 gdc;
u8 gfrq;
};
-static void pca963x_brightness_work(struct pca963x_led *pca963x)
+static int pca963x_brightness(struct pca963x_led *pca963x,
+ enum led_brightness brightness)
{
u8 ledout_addr = pca963x->chip->chipdef->ledout_base
+ (pca963x->led_num / 4);
u8 ledout;
int shift = 2 * (pca963x->led_num % 4);
u8 mask = 0x3 << shift;
+ int ret;
mutex_lock(&pca963x->chip->mutex);
ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr);
- switch (pca963x->brightness) {
+ switch (brightness) {
case LED_FULL:
- i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr,
+ ret = i2c_smbus_write_byte_data(pca963x->chip->client,
+ ledout_addr,
(ledout & ~mask) | (PCA963X_LED_ON << shift));
break;
case LED_OFF:
- i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr,
- ledout & ~mask);
+ ret = i2c_smbus_write_byte_data(pca963x->chip->client,
+ ledout_addr, ledout & ~mask);
break;
default:
- i2c_smbus_write_byte_data(pca963x->chip->client,
+ ret = i2c_smbus_write_byte_data(pca963x->chip->client,
PCA963X_PWM_BASE + pca963x->led_num,
- pca963x->brightness);
- i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr,
+ brightness);
+ if (ret < 0)
+ goto unlock;
+ ret = i2c_smbus_write_byte_data(pca963x->chip->client,
+ ledout_addr,
(ledout & ~mask) | (PCA963X_LED_PWM << shift));
break;
}
+unlock:
mutex_unlock(&pca963x->chip->mutex);
+ return ret;
}
-static void pca963x_blink_work(struct pca963x_led *pca963x)
+static void pca963x_blink(struct pca963x_led *pca963x)
{
u8 ledout_addr = pca963x->chip->chipdef->ledout_base +
(pca963x->led_num / 4);
@@ -180,36 +179,14 @@ static void pca963x_blink_work(struct pca963x_led *pca963x)
mutex_unlock(&pca963x->chip->mutex);
}
-static void pca963x_work(struct work_struct *work)
-{
- struct pca963x_led *pca963x = container_of(work,
- struct pca963x_led, work);
-
- switch (pca963x->cmd) {
- case BRIGHTNESS_SET:
- pca963x_brightness_work(pca963x);
- break;
- case BLINK_SET:
- pca963x_blink_work(pca963x);
- break;
- }
-}
-
-static void pca963x_led_set(struct led_classdev *led_cdev,
+static int pca963x_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct pca963x_led *pca963x;
pca963x = container_of(led_cdev, struct pca963x_led, led_cdev);
- pca963x->cmd = BRIGHTNESS_SET;
- pca963x->brightness = value;
-
- /*
- * Must use workqueue for the actual I/O since I2C operations
- * can sleep.
- */
- schedule_work(&pca963x->work);
+ return pca963x_brightness(pca963x, value);
}
static int pca963x_blink_set(struct led_classdev *led_cdev,
@@ -254,15 +231,10 @@ static int pca963x_blink_set(struct led_classdev *led_cdev,
*/
gfrq = (period * 24 / 1000) - 1;
- pca963x->cmd = BLINK_SET;
pca963x->gdc = gdc;
pca963x->gfrq = gfrq;
- /*
- * Must use workqueue for the actual I/O since I2C operations
- * can sleep.
- */
- schedule_work(&pca963x->work);
+ pca963x_blink(pca963x);
*delay_on = time_on;
*delay_off = time_off;
@@ -409,13 +381,11 @@ static int pca963x_probe(struct i2c_client *client,
client->addr, i);
pca963x[i].led_cdev.name = pca963x[i].name;
- pca963x[i].led_cdev.brightness_set = pca963x_led_set;
+ pca963x[i].led_cdev.brightness_set_blocking = pca963x_led_set;
if (pdata && pdata->blink_type == PCA963X_HW_BLINK)
pca963x[i].led_cdev.blink_set = pca963x_blink_set;
- INIT_WORK(&pca963x[i].work, pca963x_work);
-
err = led_classdev_register(&client->dev, &pca963x[i].led_cdev);
if (err < 0)
goto exit;
@@ -435,10 +405,8 @@ static int pca963x_probe(struct i2c_client *client,
return 0;
exit:
- while (i--) {
+ while (i--)
led_classdev_unregister(&pca963x[i].led_cdev);
- cancel_work_sync(&pca963x[i].work);
- }
return err;
}
@@ -448,10 +416,8 @@ static int pca963x_remove(struct i2c_client *client)
struct pca963x *pca963x = i2c_get_clientdata(client);
int i;
- for (i = 0; i < pca963x->chipdef->n_leds; i++) {
+ for (i = 0; i < pca963x->chipdef->n_leds; i++)
led_classdev_unregister(&pca963x->leds[i].led_cdev);
- cancel_work_sync(&pca963x->leds[i].work);
- }
return 0;
}
diff --git a/drivers/leds/leds-powernv.c b/drivers/leds/leds-powernv.c
index 1e75e1fe9b72..dfb8bd390125 100644
--- a/drivers/leds/leds-powernv.c
+++ b/drivers/leds/leds-powernv.c
@@ -77,7 +77,7 @@ static int powernv_get_led_type(const char *led_type_desc)
* This function is called from work queue task context when ever it gets
* scheduled. This function can sleep at opal_async_wait_response call.
*/
-static void powernv_led_set(struct powernv_led_data *powernv_led,
+static int powernv_led_set(struct powernv_led_data *powernv_led,
enum led_brightness value)
{
int rc, token;
@@ -99,7 +99,7 @@ static void powernv_led_set(struct powernv_led_data *powernv_led,
if (token != -ERESTARTSYS)
dev_err(dev, "%s: Couldn't get OPAL async token\n",
__func__);
- return;
+ return token;
}
rc = opal_leds_set_ind(token, powernv_led->loc_code,
@@ -125,6 +125,7 @@ static void powernv_led_set(struct powernv_led_data *powernv_led,
out_token:
opal_async_release_token(token);
+ return rc;
}
/*
@@ -173,20 +174,23 @@ static enum led_brightness powernv_led_get(struct powernv_led_data *powernv_led)
* LED classdev 'brightness_get' function. This schedules work
* to update LED state.
*/
-static void powernv_brightness_set(struct led_classdev *led_cdev,
+static int powernv_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct powernv_led_data *powernv_led =
container_of(led_cdev, struct powernv_led_data, cdev);
struct powernv_led_common *powernv_led_common = powernv_led->common;
+ int rc;
/* Do not modify LED in unload path */
if (powernv_led_common->led_disabled)
- return;
+ return 0;
mutex_lock(&powernv_led_common->lock);
- powernv_led_set(powernv_led, value);
+ rc = powernv_led_set(powernv_led, value);
mutex_unlock(&powernv_led_common->lock);
+
+ return rc;
}
/* LED classdev 'brightness_get' function */
@@ -227,7 +231,7 @@ static int powernv_led_create(struct device *dev,
return -ENOMEM;
}
- powernv_led->cdev.brightness_set = powernv_brightness_set;
+ powernv_led->cdev.brightness_set_blocking = powernv_brightness_set;
powernv_led->cdev.brightness_get = powernv_brightness_get;
powernv_led->cdev.brightness = LED_OFF;
powernv_led->cdev.max_brightness = LED_FULL;
@@ -256,8 +260,6 @@ static int powernv_led_classdev(struct platform_device *pdev,
for_each_child_of_node(led_node, np) {
p = of_find_property(np, "led-types", NULL);
- if (!p)
- continue;
while ((cur = of_prop_next_string(p, cur)) != NULL) {
powernv_led = devm_kzalloc(dev, sizeof(*powernv_led),
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c
index 1d07e3e83d29..4783bacb2e9d 100644
--- a/drivers/leds/leds-pwm.c
+++ b/drivers/leds/leds-pwm.c
@@ -22,12 +22,10 @@
#include <linux/pwm.h>
#include <linux/leds_pwm.h>
#include <linux/slab.h>
-#include <linux/workqueue.h>
struct led_pwm_data {
struct led_classdev cdev;
struct pwm_device *pwm;
- struct work_struct work;
unsigned int active_low;
unsigned int period;
int duty;
@@ -51,14 +49,6 @@ static void __led_pwm_set(struct led_pwm_data *led_dat)
pwm_enable(led_dat->pwm);
}
-static void led_pwm_work(struct work_struct *work)
-{
- struct led_pwm_data *led_dat =
- container_of(work, struct led_pwm_data, work);
-
- __led_pwm_set(led_dat);
-}
-
static void led_pwm_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
@@ -75,10 +65,14 @@ static void led_pwm_set(struct led_classdev *led_cdev,
led_dat->duty = duty;
- if (led_dat->can_sleep)
- schedule_work(&led_dat->work);
- else
- __led_pwm_set(led_dat);
+ __led_pwm_set(led_dat);
+}
+
+static int led_pwm_set_blocking(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ led_pwm_set(led_cdev, brightness);
+ return 0;
}
static inline size_t sizeof_pwm_leds_priv(int num_leds)
@@ -89,11 +83,8 @@ static inline size_t sizeof_pwm_leds_priv(int num_leds)
static void led_pwm_cleanup(struct led_pwm_priv *priv)
{
- while (priv->num_leds--) {
+ while (priv->num_leds--)
led_classdev_unregister(&priv->leds[priv->num_leds].cdev);
- if (priv->leds[priv->num_leds].can_sleep)
- cancel_work_sync(&priv->leds[priv->num_leds].work);
- }
}
static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
@@ -105,7 +96,6 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
led_data->active_low = led->active_low;
led_data->cdev.name = led->name;
led_data->cdev.default_trigger = led->default_trigger;
- led_data->cdev.brightness_set = led_pwm_set;
led_data->cdev.brightness = LED_OFF;
led_data->cdev.max_brightness = led->max_brightness;
led_data->cdev.flags = LED_CORE_SUSPENDRESUME;
@@ -122,8 +112,10 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
}
led_data->can_sleep = pwm_can_sleep(led_data->pwm);
- if (led_data->can_sleep)
- INIT_WORK(&led_data->work, led_pwm_work);
+ if (!led_data->can_sleep)
+ led_data->cdev.brightness_set = led_pwm_set;
+ else
+ led_data->cdev.brightness_set_blocking = led_pwm_set_blocking;
led_data->period = pwm_get_period(led_data->pwm);
if (!led_data->period && (led->pwm_period_ns > 0))
@@ -132,6 +124,7 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
ret = led_classdev_register(dev, &led_data->cdev);
if (ret == 0) {
priv->num_leds++;
+ led_pwm_set(&led_data->cdev, led_data->cdev.brightness);
} else {
dev_err(dev, "failed to register PWM led for %s: %d\n",
led->name, ret);
@@ -236,6 +229,6 @@ static struct platform_driver led_pwm_driver = {
module_platform_driver(led_pwm_driver);
MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
-MODULE_DESCRIPTION("PWM LED driver for PXA");
-MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("generic PWM LED driver");
+MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:leds-pwm");
diff --git a/drivers/leds/leds-regulator.c b/drivers/leds/leds-regulator.c
index ffc21397a675..acf77ca47558 100644
--- a/drivers/leds/leds-regulator.c
+++ b/drivers/leds/leds-regulator.c
@@ -14,7 +14,6 @@
#include <linux/module.h>
#include <linux/err.h>
#include <linux/slab.h>
-#include <linux/workqueue.h>
#include <linux/leds.h>
#include <linux/leds-regulator.h>
#include <linux/platform_device.h>
@@ -25,10 +24,8 @@
struct regulator_led {
struct led_classdev cdev;
- enum led_brightness value;
int enabled;
struct mutex mutex;
- struct work_struct work;
struct regulator *vcc;
};
@@ -94,22 +91,24 @@ static void regulator_led_disable(struct regulator_led *led)
led->enabled = 0;
}
-static void regulator_led_set_value(struct regulator_led *led)
+static int regulator_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
{
+ struct regulator_led *led = to_regulator_led(led_cdev);
int voltage;
- int ret;
+ int ret = 0;
mutex_lock(&led->mutex);
- if (led->value == LED_OFF) {
+ if (value == LED_OFF) {
regulator_led_disable(led);
goto out;
}
if (led->cdev.max_brightness > 1) {
- voltage = led_regulator_get_voltage(led->vcc, led->value);
+ voltage = led_regulator_get_voltage(led->vcc, value);
dev_dbg(led->cdev.dev, "brightness: %d voltage: %d\n",
- led->value, voltage);
+ value, voltage);
ret = regulator_set_voltage(led->vcc, voltage, voltage);
if (ret != 0)
@@ -121,23 +120,7 @@ static void regulator_led_set_value(struct regulator_led *led)
out:
mutex_unlock(&led->mutex);
-}
-
-static void led_work(struct work_struct *work)
-{
- struct regulator_led *led;
-
- led = container_of(work, struct regulator_led, work);
- regulator_led_set_value(led);
-}
-
-static void regulator_led_brightness_set(struct led_classdev *led_cdev,
- enum led_brightness value)
-{
- struct regulator_led *led = to_regulator_led(led_cdev);
-
- led->value = value;
- schedule_work(&led->work);
+ return ret;
}
static int regulator_led_probe(struct platform_device *pdev)
@@ -169,9 +152,8 @@ static int regulator_led_probe(struct platform_device *pdev)
pdata->brightness);
return -EINVAL;
}
- led->value = pdata->brightness;
- led->cdev.brightness_set = regulator_led_brightness_set;
+ led->cdev.brightness_set_blocking = regulator_led_brightness_set;
led->cdev.name = pdata->name;
led->cdev.flags |= LED_CORE_SUSPENDRESUME;
led->vcc = vcc;
@@ -181,21 +163,18 @@ static int regulator_led_probe(struct platform_device *pdev)
led->enabled = 1;
mutex_init(&led->mutex);
- INIT_WORK(&led->work, led_work);
platform_set_drvdata(pdev, led);
ret = led_classdev_register(&pdev->dev, &led->cdev);
- if (ret < 0) {
- cancel_work_sync(&led->work);
+ if (ret < 0)
return ret;
- }
/* to expose the default value to userspace */
- led->cdev.brightness = led->value;
+ led->cdev.brightness = pdata->brightness;
/* Set the default led status */
- regulator_led_set_value(led);
+ regulator_led_brightness_set(&led->cdev, led->cdev.brightness);
return 0;
}
@@ -205,7 +184,6 @@ static int regulator_led_remove(struct platform_device *pdev)
struct regulator_led *led = platform_get_drvdata(pdev);
led_classdev_unregister(&led->cdev);
- cancel_work_sync(&led->work);
regulator_led_disable(led);
return 0;
}
diff --git a/drivers/leds/leds-sunfire.c b/drivers/leds/leds-sunfire.c
index c2553c54f2cf..7c09db8bd4e8 100644
--- a/drivers/leds/leds-sunfire.c
+++ b/drivers/leds/leds-sunfire.c
@@ -234,28 +234,19 @@ static struct platform_driver sunfire_fhc_led_driver = {
},
};
+static struct platform_driver * const drivers[] = {
+ &sunfire_clockboard_led_driver,
+ &sunfire_fhc_led_driver,
+};
+
static int __init sunfire_leds_init(void)
{
- int err = platform_driver_register(&sunfire_clockboard_led_driver);
-
- if (err) {
- pr_err("Could not register clock board LED driver\n");
- return err;
- }
-
- err = platform_driver_register(&sunfire_fhc_led_driver);
- if (err) {
- pr_err("Could not register FHC LED driver\n");
- platform_driver_unregister(&sunfire_clockboard_led_driver);
- }
-
- return err;
+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
}
static void __exit sunfire_leds_exit(void)
{
- platform_driver_unregister(&sunfire_clockboard_led_driver);
- platform_driver_unregister(&sunfire_fhc_led_driver);
+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
}
module_init(sunfire_leds_init);
diff --git a/drivers/leds/leds-syscon.c b/drivers/leds/leds-syscon.c
index b88900d721e4..3be40f74f12a 100644
--- a/drivers/leds/leds-syscon.c
+++ b/drivers/leds/leds-syscon.c
@@ -20,7 +20,7 @@
* MA 02111-1307 USA
*/
#include <linux/io.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
@@ -139,29 +139,17 @@ static int syscon_led_probe(struct platform_device *pdev)
return 0;
}
-static int syscon_led_remove(struct platform_device *pdev)
-{
- struct syscon_led *sled = platform_get_drvdata(pdev);
-
- led_classdev_unregister(&sled->cdev);
- /* Turn it off */
- regmap_update_bits(sled->map, sled->offset, sled->mask, 0);
- return 0;
-}
-
static const struct of_device_id of_syscon_leds_match[] = {
{ .compatible = "register-bit-led", },
{},
};
-MODULE_DEVICE_TABLE(of, of_syscon_leds_match);
-
static struct platform_driver syscon_led_driver = {
.probe = syscon_led_probe,
- .remove = syscon_led_remove,
.driver = {
.name = "leds-syscon",
.of_match_table = of_syscon_leds_match,
+ .suppress_bind_attrs = true,
},
};
-module_platform_driver(syscon_led_driver);
+builtin_platform_driver(syscon_led_driver);
diff --git a/drivers/leds/leds-tlc591xx.c b/drivers/leds/leds-tlc591xx.c
index b806eca83d27..304531644938 100644
--- a/drivers/leds/leds-tlc591xx.c
+++ b/drivers/leds/leds-tlc591xx.c
@@ -14,7 +14,6 @@
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
-#include <linux/workqueue.h>
#define TLC591XX_MAX_LEDS 16
@@ -42,13 +41,11 @@
#define LEDOUT_MASK 0x3
#define ldev_to_led(c) container_of(c, struct tlc591xx_led, ldev)
-#define work_to_led(work) container_of(work, struct tlc591xx_led, work)
struct tlc591xx_led {
bool active;
unsigned int led_no;
struct led_classdev ldev;
- struct work_struct work;
struct tlc591xx_priv *priv;
};
@@ -110,12 +107,12 @@ tlc591xx_set_pwm(struct tlc591xx_priv *priv, struct tlc591xx_led *led,
return regmap_write(priv->regmap, pwm, brightness);
}
-static void
-tlc591xx_led_work(struct work_struct *work)
+static int
+tlc591xx_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
{
- struct tlc591xx_led *led = work_to_led(work);
+ struct tlc591xx_led *led = ldev_to_led(led_cdev);
struct tlc591xx_priv *priv = led->priv;
- enum led_brightness brightness = led->ldev.brightness;
int err;
switch (brightness) {
@@ -131,18 +128,7 @@ tlc591xx_led_work(struct work_struct *work)
err = tlc591xx_set_pwm(priv, led, brightness);
}
- if (err)
- dev_err(led->ldev.dev, "Failed setting brightness\n");
-}
-
-static void
-tlc591xx_brightness_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
-{
- struct tlc591xx_led *led = ldev_to_led(led_cdev);
-
- led->ldev.brightness = brightness;
- schedule_work(&led->work);
+ return err;
}
static void
@@ -151,10 +137,8 @@ tlc591xx_destroy_devices(struct tlc591xx_priv *priv, unsigned int j)
int i = j;
while (--i >= 0) {
- if (priv->leds[i].active) {
+ if (priv->leds[i].active)
led_classdev_unregister(&priv->leds[i].ldev);
- cancel_work_sync(&priv->leds[i].work);
- }
}
}
@@ -175,9 +159,8 @@ tlc591xx_configure(struct device *dev,
led->priv = priv;
led->led_no = i;
- led->ldev.brightness_set = tlc591xx_brightness_set;
+ led->ldev.brightness_set_blocking = tlc591xx_brightness_set;
led->ldev.max_brightness = LED_FULL;
- INIT_WORK(&led->work, tlc591xx_led_work);
err = led_classdev_register(dev, &led->ldev);
if (err < 0) {
dev_err(dev, "couldn't register LED %s\n",
diff --git a/drivers/leds/leds-wm831x-status.c b/drivers/leds/leds-wm831x-status.c
index 56027ef7c7e8..64a22263e7fc 100644
--- a/drivers/leds/leds-wm831x-status.c
+++ b/drivers/leds/leds-wm831x-status.c
@@ -23,7 +23,6 @@
struct wm831x_status {
struct led_classdev cdev;
struct wm831x *wm831x;
- struct work_struct work;
struct mutex mutex;
spinlock_t value_lock;
@@ -40,10 +39,8 @@ struct wm831x_status {
#define to_wm831x_status(led_cdev) \
container_of(led_cdev, struct wm831x_status, cdev)
-static void wm831x_status_work(struct work_struct *work)
+static void wm831x_status_set(struct wm831x_status *led)
{
- struct wm831x_status *led = container_of(work, struct wm831x_status,
- work);
unsigned long flags;
mutex_lock(&led->mutex);
@@ -70,8 +67,8 @@ static void wm831x_status_work(struct work_struct *work)
mutex_unlock(&led->mutex);
}
-static void wm831x_status_set(struct led_classdev *led_cdev,
- enum led_brightness value)
+static int wm831x_status_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
{
struct wm831x_status *led = to_wm831x_status(led_cdev);
unsigned long flags;
@@ -80,8 +77,10 @@ static void wm831x_status_set(struct led_classdev *led_cdev,
led->brightness = value;
if (value == LED_OFF)
led->blink = 0;
- schedule_work(&led->work);
spin_unlock_irqrestore(&led->value_lock, flags);
+ wm831x_status_set(led);
+
+ return 0;
}
static int wm831x_status_blink_set(struct led_classdev *led_cdev,
@@ -147,11 +146,8 @@ static int wm831x_status_blink_set(struct led_classdev *led_cdev,
else
led->blink = 0;
- /* Always update; if we fail turn off blinking since we expect
- * a software fallback. */
- schedule_work(&led->work);
-
spin_unlock_irqrestore(&led->value_lock, flags);
+ wm831x_status_set(led);
return ret;
}
@@ -206,11 +202,9 @@ static ssize_t wm831x_status_src_store(struct device *dev,
for (i = 0; i < ARRAY_SIZE(led_src_texts); i++) {
if (!strcmp(name, led_src_texts[i])) {
mutex_lock(&led->mutex);
-
led->src = i;
- schedule_work(&led->work);
-
mutex_unlock(&led->mutex);
+ wm831x_status_set(led);
}
}
@@ -262,7 +256,6 @@ static int wm831x_status_probe(struct platform_device *pdev)
pdata.name = dev_name(&pdev->dev);
mutex_init(&drvdata->mutex);
- INIT_WORK(&drvdata->work, wm831x_status_work);
spin_lock_init(&drvdata->value_lock);
/* We cache the configuration register and read startup values
@@ -287,7 +280,7 @@ static int wm831x_status_probe(struct platform_device *pdev)
drvdata->cdev.name = pdata.name;
drvdata->cdev.default_trigger = pdata.default_trigger;
- drvdata->cdev.brightness_set = wm831x_status_set;
+ drvdata->cdev.brightness_set_blocking = wm831x_status_brightness_set;
drvdata->cdev.blink_set = wm831x_status_blink_set;
drvdata->cdev.groups = wm831x_status_groups;
diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c
index 0d121835673f..e1e4e9d0b8b1 100644
--- a/drivers/leds/leds-wm8350.c
+++ b/drivers/leds/leds-wm8350.c
@@ -89,40 +89,42 @@ static const int isink_cur[] = {
#define to_wm8350_led(led_cdev) \
container_of(led_cdev, struct wm8350_led, cdev)
-static void wm8350_led_enable(struct wm8350_led *led)
+static int wm8350_led_enable(struct wm8350_led *led)
{
- int ret;
+ int ret = 0;
if (led->enabled)
- return;
+ return ret;
ret = regulator_enable(led->isink);
if (ret != 0) {
dev_err(led->cdev.dev, "Failed to enable ISINK: %d\n", ret);
- return;
+ return ret;
}
ret = regulator_enable(led->dcdc);
if (ret != 0) {
dev_err(led->cdev.dev, "Failed to enable DCDC: %d\n", ret);
regulator_disable(led->isink);
- return;
+ return ret;
}
led->enabled = 1;
+
+ return ret;
}
-static void wm8350_led_disable(struct wm8350_led *led)
+static int wm8350_led_disable(struct wm8350_led *led)
{
- int ret;
+ int ret = 0;
if (!led->enabled)
- return;
+ return ret;
ret = regulator_disable(led->dcdc);
if (ret != 0) {
dev_err(led->cdev.dev, "Failed to disable DCDC: %d\n", ret);
- return;
+ return ret;
}
ret = regulator_disable(led->isink);
@@ -132,27 +134,29 @@ static void wm8350_led_disable(struct wm8350_led *led)
if (ret != 0)
dev_err(led->cdev.dev, "Failed to reenable DCDC: %d\n",
ret);
- return;
+ return ret;
}
led->enabled = 0;
+
+ return ret;
}
-static void led_work(struct work_struct *work)
+static int wm8350_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
{
- struct wm8350_led *led = container_of(work, struct wm8350_led, work);
+ struct wm8350_led *led = to_wm8350_led(led_cdev);
+ unsigned long flags;
int ret;
int uA;
- unsigned long flags;
- mutex_lock(&led->mutex);
+ led->value = value;
spin_lock_irqsave(&led->value_lock, flags);
if (led->value == LED_OFF) {
spin_unlock_irqrestore(&led->value_lock, flags);
- wm8350_led_disable(led);
- goto out;
+ return wm8350_led_disable(led);
}
/* This scales linearly into the index of valid current
@@ -166,36 +170,21 @@ static void led_work(struct work_struct *work)
ret = regulator_set_current_limit(led->isink, isink_cur[uA],
isink_cur[uA]);
- if (ret != 0)
+ if (ret != 0) {
dev_err(led->cdev.dev, "Failed to set %duA: %d\n",
isink_cur[uA], ret);
+ return ret;
+ }
- wm8350_led_enable(led);
-
-out:
- mutex_unlock(&led->mutex);
-}
-
-static void wm8350_led_set(struct led_classdev *led_cdev,
- enum led_brightness value)
-{
- struct wm8350_led *led = to_wm8350_led(led_cdev);
- unsigned long flags;
-
- spin_lock_irqsave(&led->value_lock, flags);
- led->value = value;
- schedule_work(&led->work);
- spin_unlock_irqrestore(&led->value_lock, flags);
+ return wm8350_led_enable(led);
}
static void wm8350_led_shutdown(struct platform_device *pdev)
{
struct wm8350_led *led = platform_get_drvdata(pdev);
- mutex_lock(&led->mutex);
led->value = LED_OFF;
wm8350_led_disable(led);
- mutex_unlock(&led->mutex);
}
static int wm8350_led_probe(struct platform_device *pdev)
@@ -232,7 +221,7 @@ static int wm8350_led_probe(struct platform_device *pdev)
if (led == NULL)
return -ENOMEM;
- led->cdev.brightness_set = wm8350_led_set;
+ led->cdev.brightness_set_blocking = wm8350_led_set;
led->cdev.default_trigger = pdata->default_trigger;
led->cdev.name = pdata->name;
led->cdev.flags |= LED_CORE_SUSPENDRESUME;
@@ -251,8 +240,6 @@ static int wm8350_led_probe(struct platform_device *pdev)
pdata->max_uA);
spin_lock_init(&led->value_lock);
- mutex_init(&led->mutex);
- INIT_WORK(&led->work, led_work);
led->value = LED_OFF;
platform_set_drvdata(pdev, led);
@@ -264,7 +251,6 @@ static int wm8350_led_remove(struct platform_device *pdev)
struct wm8350_led *led = platform_get_drvdata(pdev);
led_classdev_unregister(&led->cdev);
- flush_work(&led->work);
wm8350_led_disable(led);
return 0;
}
diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h
index 4238fbc31d35..db3f20da7221 100644
--- a/drivers/leds/leds.h
+++ b/drivers/leds/leds.h
@@ -16,29 +16,6 @@
#include <linux/rwsem.h>
#include <linux/leds.h>
-static inline void led_set_brightness_async(struct led_classdev *led_cdev,
- enum led_brightness value)
-{
- value = min(value, led_cdev->max_brightness);
- led_cdev->brightness = value;
-
- if (!(led_cdev->flags & LED_SUSPENDED))
- led_cdev->brightness_set(led_cdev, value);
-}
-
-static inline int led_set_brightness_sync(struct led_classdev *led_cdev,
- enum led_brightness value)
-{
- int ret = 0;
-
- led_cdev->brightness = min(value, led_cdev->max_brightness);
-
- if (!(led_cdev->flags & LED_SUSPENDED))
- ret = led_cdev->brightness_set_sync(led_cdev,
- led_cdev->brightness);
- return ret;
-}
-
static inline int led_get_brightness(struct led_classdev *led_cdev)
{
return led_cdev->brightness;
@@ -46,6 +23,10 @@ static inline int led_get_brightness(struct led_classdev *led_cdev)
void led_init_core(struct led_classdev *led_cdev);
void led_stop_software_blink(struct led_classdev *led_cdev);
+void led_set_brightness_nopm(struct led_classdev *led_cdev,
+ enum led_brightness value);
+void led_set_brightness_nosleep(struct led_classdev *led_cdev,
+ enum led_brightness value);
extern struct rw_semaphore leds_list_lock;
extern struct list_head leds_list;
diff --git a/drivers/leds/trigger/ledtrig-backlight.c b/drivers/leds/trigger/ledtrig-backlight.c
index 59eca17d9661..1ca1f1608f76 100644
--- a/drivers/leds/trigger/ledtrig-backlight.c
+++ b/drivers/leds/trigger/ledtrig-backlight.c
@@ -51,9 +51,9 @@ static int fb_notifier_callback(struct notifier_block *p,
if ((n->old_status == UNBLANK) ^ n->invert) {
n->brightness = led->brightness;
- led_set_brightness_async(led, LED_OFF);
+ led_set_brightness_nosleep(led, LED_OFF);
} else {
- led_set_brightness_async(led, n->brightness);
+ led_set_brightness_nosleep(led, n->brightness);
}
n->old_status = new_status;
@@ -89,9 +89,9 @@ static ssize_t bl_trig_invert_store(struct device *dev,
/* After inverting, we need to update the LED. */
if ((n->old_status == BLANK) ^ n->invert)
- led_set_brightness_async(led, LED_OFF);
+ led_set_brightness_nosleep(led, LED_OFF);
else
- led_set_brightness_async(led, n->brightness);
+ led_set_brightness_nosleep(led, n->brightness);
return num;
}
diff --git a/drivers/leds/trigger/ledtrig-cpu.c b/drivers/leds/trigger/ledtrig-cpu.c
index aec0f02b6b3e..938467fb82be 100644
--- a/drivers/leds/trigger/ledtrig-cpu.c
+++ b/drivers/leds/trigger/ledtrig-cpu.c
@@ -19,7 +19,6 @@
*
*/
-#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -140,27 +139,4 @@ static int __init ledtrig_cpu_init(void)
return 0;
}
-module_init(ledtrig_cpu_init);
-
-static void __exit ledtrig_cpu_exit(void)
-{
- int cpu;
-
- unregister_cpu_notifier(&ledtrig_cpu_nb);
-
- for_each_possible_cpu(cpu) {
- struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu);
-
- led_trigger_unregister_simple(trig->_trig);
- trig->_trig = NULL;
- memset(trig->name, 0, MAX_NAME_LEN);
- }
-
- unregister_syscore_ops(&ledtrig_cpu_syscore_ops);
-}
-module_exit(ledtrig_cpu_exit);
-
-MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
-MODULE_AUTHOR("Bryan Wu <bryan.wu@canonical.com>");
-MODULE_DESCRIPTION("CPU LED trigger");
-MODULE_LICENSE("GPL");
+device_initcall(ledtrig_cpu_init);
diff --git a/drivers/leds/trigger/ledtrig-default-on.c b/drivers/leds/trigger/ledtrig-default-on.c
index 6f38f883aaf1..ff455cb46680 100644
--- a/drivers/leds/trigger/ledtrig-default-on.c
+++ b/drivers/leds/trigger/ledtrig-default-on.c
@@ -19,7 +19,7 @@
static void defon_trig_activate(struct led_classdev *led_cdev)
{
- led_set_brightness_async(led_cdev, led_cdev->max_brightness);
+ led_set_brightness_nosleep(led_cdev, led_cdev->max_brightness);
}
static struct led_trigger defon_led_trigger = {
diff --git a/drivers/leds/trigger/ledtrig-gpio.c b/drivers/leds/trigger/ledtrig-gpio.c
index 4cc7040746c6..51288a45fbcb 100644
--- a/drivers/leds/trigger/ledtrig-gpio.c
+++ b/drivers/leds/trigger/ledtrig-gpio.c
@@ -54,12 +54,12 @@ static void gpio_trig_work(struct work_struct *work)
if (tmp) {
if (gpio_data->desired_brightness)
- led_set_brightness_async(gpio_data->led,
+ led_set_brightness_nosleep(gpio_data->led,
gpio_data->desired_brightness);
else
- led_set_brightness_async(gpio_data->led, LED_FULL);
+ led_set_brightness_nosleep(gpio_data->led, LED_FULL);
} else {
- led_set_brightness_async(gpio_data->led, LED_OFF);
+ led_set_brightness_nosleep(gpio_data->led, LED_OFF);
}
}
diff --git a/drivers/leds/trigger/ledtrig-heartbeat.c b/drivers/leds/trigger/ledtrig-heartbeat.c
index 8622ce651ae2..410c39c62dc7 100644
--- a/drivers/leds/trigger/ledtrig-heartbeat.c
+++ b/drivers/leds/trigger/ledtrig-heartbeat.c
@@ -38,7 +38,7 @@ static void led_heartbeat_function(unsigned long data)
unsigned long delay = 0;
if (unlikely(panic_heartbeats)) {
- led_set_brightness(led_cdev, LED_OFF);
+ led_set_brightness_nosleep(led_cdev, LED_OFF);
return;
}
@@ -81,7 +81,7 @@ static void led_heartbeat_function(unsigned long data)
break;
}
- led_set_brightness_async(led_cdev, brightness);
+ led_set_brightness_nosleep(led_cdev, brightness);
mod_timer(&heartbeat_data->timer, jiffies + delay);
}
diff --git a/drivers/leds/trigger/ledtrig-ide-disk.c b/drivers/leds/trigger/ledtrig-ide-disk.c
index 2cd7c0cf5924..c02a3ac3cd2b 100644
--- a/drivers/leds/trigger/ledtrig-ide-disk.c
+++ b/drivers/leds/trigger/ledtrig-ide-disk.c
@@ -11,7 +11,6 @@
*
*/
-#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/leds.h>
@@ -33,15 +32,4 @@ static int __init ledtrig_ide_init(void)
led_trigger_register_simple("ide-disk", &ledtrig_ide);
return 0;
}
-
-static void __exit ledtrig_ide_exit(void)
-{
- led_trigger_unregister_simple(ledtrig_ide);
-}
-
-module_init(ledtrig_ide_init);
-module_exit(ledtrig_ide_exit);
-
-MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
-MODULE_DESCRIPTION("LED IDE Disk Activity Trigger");
-MODULE_LICENSE("GPL");
+device_initcall(ledtrig_ide_init);
diff --git a/drivers/leds/trigger/ledtrig-oneshot.c b/drivers/leds/trigger/ledtrig-oneshot.c
index fbd02cdc3ad7..b8ea9f0f1e19 100644
--- a/drivers/leds/trigger/ledtrig-oneshot.c
+++ b/drivers/leds/trigger/ledtrig-oneshot.c
@@ -63,9 +63,9 @@ static ssize_t led_invert_store(struct device *dev,
oneshot_data->invert = !!state;
if (oneshot_data->invert)
- led_set_brightness_async(led_cdev, LED_FULL);
+ led_set_brightness_nosleep(led_cdev, LED_FULL);
else
- led_set_brightness_async(led_cdev, LED_OFF);
+ led_set_brightness_nosleep(led_cdev, LED_OFF);
return size;
}
@@ -201,4 +201,4 @@ module_exit(oneshot_trig_exit);
MODULE_AUTHOR("Fabio Baltieri <fabio.baltieri@gmail.com>");
MODULE_DESCRIPTION("One-shot LED trigger");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/trigger/ledtrig-transient.c b/drivers/leds/trigger/ledtrig-transient.c
index 3c34de404d18..7e6011bd3646 100644
--- a/drivers/leds/trigger/ledtrig-transient.c
+++ b/drivers/leds/trigger/ledtrig-transient.c
@@ -41,7 +41,7 @@ static void transient_timer_function(unsigned long data)
struct transient_trig_data *transient_data = led_cdev->trigger_data;
transient_data->activate = 0;
- led_set_brightness_async(led_cdev, transient_data->restore_state);
+ led_set_brightness_nosleep(led_cdev, transient_data->restore_state);
}
static ssize_t transient_activate_show(struct device *dev,
@@ -72,7 +72,7 @@ static ssize_t transient_activate_store(struct device *dev,
if (state == 0 && transient_data->activate == 1) {
del_timer(&transient_data->timer);
transient_data->activate = state;
- led_set_brightness_async(led_cdev,
+ led_set_brightness_nosleep(led_cdev,
transient_data->restore_state);
return size;
}
@@ -81,11 +81,11 @@ static ssize_t transient_activate_store(struct device *dev,
if (state == 1 && transient_data->activate == 0 &&
transient_data->duration != 0) {
transient_data->activate = state;
- led_set_brightness_async(led_cdev, transient_data->state);
+ led_set_brightness_nosleep(led_cdev, transient_data->state);
transient_data->restore_state =
(transient_data->state == LED_FULL) ? LED_OFF : LED_FULL;
mod_timer(&transient_data->timer,
- jiffies + transient_data->duration);
+ jiffies + msecs_to_jiffies(transient_data->duration));
}
/* state == 0 && transient_data->activate == 0
@@ -204,7 +204,7 @@ static void transient_trig_deactivate(struct led_classdev *led_cdev)
if (led_cdev->activated) {
del_timer_sync(&transient_data->timer);
- led_set_brightness_async(led_cdev,
+ led_set_brightness_nosleep(led_cdev,
transient_data->restore_state);
device_remove_file(led_cdev->dev, &dev_attr_activate);
device_remove_file(led_cdev->dev, &dev_attr_duration);
diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c
index 312ffd3d0017..9e385b38debf 100644
--- a/drivers/lguest/core.c
+++ b/drivers/lguest/core.c
@@ -22,7 +22,8 @@
unsigned long switcher_addr;
struct page **lg_switcher_pages;
-static struct vm_struct *switcher_vma;
+static struct vm_struct *switcher_text_vma;
+static struct vm_struct *switcher_stacks_vma;
/* This One Big lock protects all inter-guest data structures. */
DEFINE_MUTEX(lguest_lock);
@@ -83,54 +84,80 @@ static __init int map_switcher(void)
}
/*
+ * Copy in the compiled-in Switcher code (from x86/switcher_32.S).
+ * It goes in the first page, which we map in momentarily.
+ */
+ memcpy(kmap(lg_switcher_pages[0]), start_switcher_text,
+ end_switcher_text - start_switcher_text);
+ kunmap(lg_switcher_pages[0]);
+
+ /*
* We place the Switcher underneath the fixmap area, which is the
* highest virtual address we can get. This is important, since we
* tell the Guest it can't access this memory, so we want its ceiling
* as high as possible.
*/
- switcher_addr = FIXADDR_START - (TOTAL_SWITCHER_PAGES+1)*PAGE_SIZE;
+ switcher_addr = FIXADDR_START - TOTAL_SWITCHER_PAGES*PAGE_SIZE;
/*
- * Now we reserve the "virtual memory area" we want. We might
- * not get it in theory, but in practice it's worked so far.
- * The end address needs +1 because __get_vm_area allocates an
- * extra guard page, so we need space for that.
+ * Now we reserve the "virtual memory area"s we want. We might
+ * not get them in theory, but in practice it's worked so far.
+ *
+ * We want the switcher text to be read-only and executable, and
+ * the stacks to be read-write and non-executable.
*/
- switcher_vma = __get_vm_area(TOTAL_SWITCHER_PAGES * PAGE_SIZE,
- VM_ALLOC, switcher_addr, switcher_addr
- + (TOTAL_SWITCHER_PAGES+1) * PAGE_SIZE);
- if (!switcher_vma) {
+ switcher_text_vma = __get_vm_area(PAGE_SIZE, VM_ALLOC|VM_NO_GUARD,
+ switcher_addr,
+ switcher_addr + PAGE_SIZE);
+
+ if (!switcher_text_vma) {
err = -ENOMEM;
printk("lguest: could not map switcher pages high\n");
goto free_pages;
}
+ switcher_stacks_vma = __get_vm_area(SWITCHER_STACK_PAGES * PAGE_SIZE,
+ VM_ALLOC|VM_NO_GUARD,
+ switcher_addr + PAGE_SIZE,
+ switcher_addr + TOTAL_SWITCHER_PAGES * PAGE_SIZE);
+ if (!switcher_stacks_vma) {
+ err = -ENOMEM;
+ printk("lguest: could not map switcher pages high\n");
+ goto free_text_vma;
+ }
+
/*
* This code actually sets up the pages we've allocated to appear at
* switcher_addr. map_vm_area() takes the vma we allocated above, the
- * kind of pages we're mapping (kernel pages), and a pointer to our
- * array of struct pages.
+ * kind of pages we're mapping (kernel text pages and kernel writable
+ * pages respectively), and a pointer to our array of struct pages.
*/
- err = map_vm_area(switcher_vma, PAGE_KERNEL_EXEC, lg_switcher_pages);
+ err = map_vm_area(switcher_text_vma, PAGE_KERNEL_RX, lg_switcher_pages);
+ if (err) {
+ printk("lguest: text map_vm_area failed: %i\n", err);
+ goto free_vmas;
+ }
+
+ err = map_vm_area(switcher_stacks_vma, PAGE_KERNEL,
+ lg_switcher_pages + SWITCHER_TEXT_PAGES);
if (err) {
- printk("lguest: map_vm_area failed: %i\n", err);
- goto free_vma;
+ printk("lguest: stacks map_vm_area failed: %i\n", err);
+ goto free_vmas;
}
/*
* Now the Switcher is mapped at the right address, we can't fail!
- * Copy in the compiled-in Switcher code (from x86/switcher_32.S).
*/
- memcpy(switcher_vma->addr, start_switcher_text,
- end_switcher_text - start_switcher_text);
-
printk(KERN_INFO "lguest: mapped switcher at %p\n",
- switcher_vma->addr);
+ switcher_text_vma->addr);
/* And we succeeded... */
return 0;
-free_vma:
- vunmap(switcher_vma->addr);
+free_vmas:
+ /* Undoes map_vm_area and __get_vm_area */
+ vunmap(switcher_stacks_vma->addr);
+free_text_vma:
+ vunmap(switcher_text_vma->addr);
free_pages:
i = TOTAL_SWITCHER_PAGES;
free_some_pages:
@@ -148,7 +175,8 @@ static void unmap_switcher(void)
unsigned int i;
/* vunmap() undoes *both* map_vm_area() and __get_vm_area(). */
- vunmap(switcher_vma->addr);
+ vunmap(switcher_text_vma->addr);
+ vunmap(switcher_stacks_vma->addr);
/* Now we just need to free the pages we copied the switcher into */
for (i = 0; i < TOTAL_SWITCHER_PAGES; i++)
__free_pages(lg_switcher_pages[i], 0);
diff --git a/drivers/lightnvm/Kconfig b/drivers/lightnvm/Kconfig
index a16bf56d3f28..85a339030e4b 100644
--- a/drivers/lightnvm/Kconfig
+++ b/drivers/lightnvm/Kconfig
@@ -18,6 +18,7 @@ if NVM
config NVM_DEBUG
bool "Open-Channel SSD debugging support"
+ default n
---help---
Exposes a debug management interface to create/remove targets at:
diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c
index f659e605a406..8f41b245cd55 100644
--- a/drivers/lightnvm/core.c
+++ b/drivers/lightnvm/core.c
@@ -74,7 +74,7 @@ EXPORT_SYMBOL(nvm_unregister_target);
void *nvm_dev_dma_alloc(struct nvm_dev *dev, gfp_t mem_flags,
dma_addr_t *dma_handler)
{
- return dev->ops->dev_dma_alloc(dev->q, dev->ppalist_pool, mem_flags,
+ return dev->ops->dev_dma_alloc(dev, dev->ppalist_pool, mem_flags,
dma_handler);
}
EXPORT_SYMBOL(nvm_dev_dma_alloc);
@@ -97,15 +97,47 @@ static struct nvmm_type *nvm_find_mgr_type(const char *name)
return NULL;
}
+struct nvmm_type *nvm_init_mgr(struct nvm_dev *dev)
+{
+ struct nvmm_type *mt;
+ int ret;
+
+ lockdep_assert_held(&nvm_lock);
+
+ list_for_each_entry(mt, &nvm_mgrs, list) {
+ ret = mt->register_mgr(dev);
+ if (ret < 0) {
+ pr_err("nvm: media mgr failed to init (%d) on dev %s\n",
+ ret, dev->name);
+ return NULL; /* initialization failed */
+ } else if (ret > 0)
+ return mt;
+ }
+
+ return NULL;
+}
+
int nvm_register_mgr(struct nvmm_type *mt)
{
+ struct nvm_dev *dev;
int ret = 0;
down_write(&nvm_lock);
- if (nvm_find_mgr_type(mt->name))
+ if (nvm_find_mgr_type(mt->name)) {
ret = -EEXIST;
- else
+ goto finish;
+ } else {
list_add(&mt->list, &nvm_mgrs);
+ }
+
+ /* try to register media mgr if any device have none configured */
+ list_for_each_entry(dev, &nvm_devices, devices) {
+ if (dev->mt)
+ continue;
+
+ dev->mt = nvm_init_mgr(dev);
+ }
+finish:
up_write(&nvm_lock);
return ret;
@@ -160,11 +192,6 @@ int nvm_erase_blk(struct nvm_dev *dev, struct nvm_block *blk)
}
EXPORT_SYMBOL(nvm_erase_blk);
-static void nvm_core_free(struct nvm_dev *dev)
-{
- kfree(dev);
-}
-
static int nvm_core_init(struct nvm_dev *dev)
{
struct nvm_id *id = &dev->identity;
@@ -179,12 +206,21 @@ static int nvm_core_init(struct nvm_dev *dev)
dev->sec_size = grp->csecs;
dev->oob_size = grp->sos;
dev->sec_per_pg = grp->fpg_sz / grp->csecs;
- dev->addr_mode = id->ppat;
- dev->addr_format = id->ppaf;
+ memcpy(&dev->ppaf, &id->ppaf, sizeof(struct nvm_addr_format));
dev->plane_mode = NVM_PLANE_SINGLE;
dev->max_rq_size = dev->ops->max_phys_sect * dev->sec_size;
+ if (grp->mtype != 0) {
+ pr_err("nvm: memory type not supported\n");
+ return -EINVAL;
+ }
+
+ if (grp->fmtype != 0 && grp->fmtype != 1) {
+ pr_err("nvm: flash type not supported\n");
+ return -EINVAL;
+ }
+
if (grp->mpos & 0x020202)
dev->plane_mode = NVM_PLANE_DOUBLE;
if (grp->mpos & 0x040404)
@@ -213,21 +249,17 @@ static void nvm_free(struct nvm_dev *dev)
if (dev->mt)
dev->mt->unregister_mgr(dev);
-
- nvm_core_free(dev);
}
static int nvm_init(struct nvm_dev *dev)
{
- struct nvmm_type *mt;
- int ret = 0;
+ int ret = -EINVAL;
if (!dev->q || !dev->ops)
- return -EINVAL;
+ return ret;
- if (dev->ops->identity(dev->q, &dev->identity)) {
+ if (dev->ops->identity(dev, &dev->identity)) {
pr_err("nvm: device could not be identified\n");
- ret = -EINVAL;
goto err;
}
@@ -251,29 +283,12 @@ static int nvm_init(struct nvm_dev *dev)
goto err;
}
- /* register with device with a supported manager */
- list_for_each_entry(mt, &nvm_mgrs, list) {
- ret = mt->register_mgr(dev);
- if (ret < 0)
- goto err; /* initialization failed */
- if (ret > 0) {
- dev->mt = mt;
- break; /* successfully initialized */
- }
- }
-
- if (!ret) {
- pr_info("nvm: no compatible manager found.\n");
- return 0;
- }
-
pr_info("nvm: registered %s [%u/%u/%u/%u/%u/%u]\n",
dev->name, dev->sec_per_pg, dev->nr_planes,
dev->pgs_per_blk, dev->blks_per_lun, dev->nr_luns,
dev->nr_chnls);
return 0;
err:
- nvm_free(dev);
pr_err("nvm: failed to initialize nvm\n");
return ret;
}
@@ -308,22 +323,27 @@ int nvm_register(struct request_queue *q, char *disk_name,
if (ret)
goto err_init;
- down_write(&nvm_lock);
- list_add(&dev->devices, &nvm_devices);
- up_write(&nvm_lock);
+ if (dev->ops->max_phys_sect > 256) {
+ pr_info("nvm: max sectors supported is 256.\n");
+ ret = -EINVAL;
+ goto err_init;
+ }
if (dev->ops->max_phys_sect > 1) {
- dev->ppalist_pool = dev->ops->create_dma_pool(dev->q,
- "ppalist");
+ dev->ppalist_pool = dev->ops->create_dma_pool(dev, "ppalist");
if (!dev->ppalist_pool) {
pr_err("nvm: could not create ppa pool\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_init;
}
- } else if (dev->ops->max_phys_sect > 256) {
- pr_info("nvm: max sectors supported is 256.\n");
- return -EINVAL;
}
+ /* register device with a supported media manager */
+ down_write(&nvm_lock);
+ dev->mt = nvm_init_mgr(dev);
+ list_add(&dev->devices, &nvm_devices);
+ up_write(&nvm_lock);
+
return 0;
err_init:
kfree(dev);
@@ -333,19 +353,22 @@ EXPORT_SYMBOL(nvm_register);
void nvm_unregister(char *disk_name)
{
- struct nvm_dev *dev = nvm_find_nvm_dev(disk_name);
+ struct nvm_dev *dev;
+ down_write(&nvm_lock);
+ dev = nvm_find_nvm_dev(disk_name);
if (!dev) {
pr_err("nvm: could not find device %s to unregister\n",
disk_name);
+ up_write(&nvm_lock);
return;
}
- nvm_exit(dev);
-
- down_write(&nvm_lock);
list_del(&dev->devices);
up_write(&nvm_lock);
+
+ nvm_exit(dev);
+ kfree(dev);
}
EXPORT_SYMBOL(nvm_unregister);
@@ -358,38 +381,24 @@ static int nvm_create_target(struct nvm_dev *dev,
{
struct nvm_ioctl_create_simple *s = &create->conf.s;
struct request_queue *tqueue;
- struct nvmm_type *mt;
struct gendisk *tdisk;
struct nvm_tgt_type *tt;
struct nvm_target *t;
void *targetdata;
- int ret = 0;
if (!dev->mt) {
- /* register with device with a supported NVM manager */
- list_for_each_entry(mt, &nvm_mgrs, list) {
- ret = mt->register_mgr(dev);
- if (ret < 0)
- return ret; /* initialization failed */
- if (ret > 0) {
- dev->mt = mt;
- break; /* successfully initialized */
- }
- }
-
- if (!ret) {
- pr_info("nvm: no compatible nvm manager found.\n");
- return -ENODEV;
- }
+ pr_info("nvm: device has no media manager registered.\n");
+ return -ENODEV;
}
+ down_write(&nvm_lock);
tt = nvm_find_target_type(create->tgttype);
if (!tt) {
pr_err("nvm: target type %s not found\n", create->tgttype);
+ up_write(&nvm_lock);
return -EINVAL;
}
- down_write(&nvm_lock);
list_for_each_entry(t, &dev->online_targets, list) {
if (!strcmp(create->tgtname, t->disk->disk_name)) {
pr_err("nvm: target name already exists.\n");
@@ -457,11 +466,11 @@ static void nvm_remove_target(struct nvm_target *t)
lockdep_assert_held(&nvm_lock);
del_gendisk(tdisk);
+ blk_cleanup_queue(q);
+
if (tt->exit)
tt->exit(tdisk->private_data);
- blk_cleanup_queue(q);
-
put_disk(tdisk);
list_del(&t->list);
@@ -473,7 +482,9 @@ static int __nvm_configure_create(struct nvm_ioctl_create *create)
struct nvm_dev *dev;
struct nvm_ioctl_create_simple *s;
+ down_write(&nvm_lock);
dev = nvm_find_nvm_dev(create->dev);
+ up_write(&nvm_lock);
if (!dev) {
pr_err("nvm: device not found\n");
return -EINVAL;
@@ -532,7 +543,9 @@ static int nvm_configure_show(const char *val)
return -EINVAL;
}
+ down_write(&nvm_lock);
dev = nvm_find_nvm_dev(devname);
+ up_write(&nvm_lock);
if (!dev) {
pr_err("nvm: device not found\n");
return -EINVAL;
@@ -541,7 +554,7 @@ static int nvm_configure_show(const char *val)
if (!dev->mt)
return 0;
- dev->mt->free_blocks_print(dev);
+ dev->mt->lun_info_print(dev);
return 0;
}
@@ -677,8 +690,10 @@ static long nvm_ioctl_info(struct file *file, void __user *arg)
info->tgtsize = tgt_iter;
up_write(&nvm_lock);
- if (copy_to_user(arg, info, sizeof(struct nvm_ioctl_info)))
+ if (copy_to_user(arg, info, sizeof(struct nvm_ioctl_info))) {
+ kfree(info);
return -EFAULT;
+ }
kfree(info);
return 0;
@@ -721,8 +736,11 @@ static long nvm_ioctl_get_devices(struct file *file, void __user *arg)
devices->nr_devices = i;
- if (copy_to_user(arg, devices, sizeof(struct nvm_ioctl_get_devices)))
+ if (copy_to_user(arg, devices,
+ sizeof(struct nvm_ioctl_get_devices))) {
+ kfree(devices);
return -EFAULT;
+ }
kfree(devices);
return 0;
diff --git a/drivers/lightnvm/gennvm.c b/drivers/lightnvm/gennvm.c
index ae1fb2bdc5f4..a54b339951a3 100644
--- a/drivers/lightnvm/gennvm.c
+++ b/drivers/lightnvm/gennvm.c
@@ -60,23 +60,27 @@ static int gennvm_luns_init(struct nvm_dev *dev, struct gen_nvm *gn)
lun->vlun.lun_id = i % dev->luns_per_chnl;
lun->vlun.chnl_id = i / dev->luns_per_chnl;
lun->vlun.nr_free_blocks = dev->blks_per_lun;
+ lun->vlun.nr_inuse_blocks = 0;
+ lun->vlun.nr_bad_blocks = 0;
}
return 0;
}
-static int gennvm_block_bb(u32 lun_id, void *bb_bitmap, unsigned int nr_blocks,
+static int gennvm_block_bb(struct ppa_addr ppa, int nr_blocks, u8 *blks,
void *private)
{
struct gen_nvm *gn = private;
- struct gen_lun *lun = &gn->luns[lun_id];
+ struct nvm_dev *dev = gn->dev;
+ struct gen_lun *lun;
struct nvm_block *blk;
int i;
- if (unlikely(bitmap_empty(bb_bitmap, nr_blocks)))
- return 0;
+ lun = &gn->luns[(dev->luns_per_chnl * ppa.g.ch) + ppa.g.lun];
+
+ for (i = 0; i < nr_blocks; i++) {
+ if (blks[i] == 0)
+ continue;
- i = -1;
- while ((i = find_next_bit(bb_bitmap, nr_blocks, i + 1)) < nr_blocks) {
blk = &lun->vlun.blocks[i];
if (!blk) {
pr_err("gennvm: BB data is out of bounds.\n");
@@ -84,6 +88,7 @@ static int gennvm_block_bb(u32 lun_id, void *bb_bitmap, unsigned int nr_blocks,
}
list_move_tail(&blk->list, &lun->bb_list);
+ lun->vlun.nr_bad_blocks++;
}
return 0;
@@ -136,6 +141,7 @@ static int gennvm_block_map(u64 slba, u32 nlb, __le64 *entries, void *private)
list_move_tail(&blk->list, &lun->used_list);
blk->type = 1;
lun->vlun.nr_free_blocks--;
+ lun->vlun.nr_inuse_blocks++;
}
}
@@ -164,22 +170,32 @@ static int gennvm_blocks_init(struct nvm_dev *dev, struct gen_nvm *gn)
block->id = cur_block_id++;
/* First block is reserved for device */
- if (unlikely(lun_iter == 0 && blk_iter == 0))
+ if (unlikely(lun_iter == 0 && blk_iter == 0)) {
+ lun->vlun.nr_free_blocks--;
continue;
+ }
list_add_tail(&block->list, &lun->free_list);
}
if (dev->ops->get_bb_tbl) {
- ret = dev->ops->get_bb_tbl(dev->q, lun->vlun.id,
- dev->blks_per_lun, gennvm_block_bb, gn);
+ struct ppa_addr ppa;
+
+ ppa.ppa = 0;
+ ppa.g.ch = lun->vlun.chnl_id;
+ ppa.g.lun = lun->vlun.id;
+ ppa = generic_to_dev_addr(dev, ppa);
+
+ ret = dev->ops->get_bb_tbl(dev, ppa,
+ dev->blks_per_lun,
+ gennvm_block_bb, gn);
if (ret)
pr_err("gennvm: could not read BB table\n");
}
}
if (dev->ops->get_l2p_tbl) {
- ret = dev->ops->get_l2p_tbl(dev->q, 0, dev->total_pages,
+ ret = dev->ops->get_l2p_tbl(dev, 0, dev->total_pages,
gennvm_block_map, dev);
if (ret) {
pr_err("gennvm: could not read L2P table.\n");
@@ -190,15 +206,27 @@ static int gennvm_blocks_init(struct nvm_dev *dev, struct gen_nvm *gn)
return 0;
}
+static void gennvm_free(struct nvm_dev *dev)
+{
+ gennvm_blocks_free(dev);
+ gennvm_luns_free(dev);
+ kfree(dev->mp);
+ dev->mp = NULL;
+}
+
static int gennvm_register(struct nvm_dev *dev)
{
struct gen_nvm *gn;
int ret;
+ if (!try_module_get(THIS_MODULE))
+ return -ENODEV;
+
gn = kzalloc(sizeof(struct gen_nvm), GFP_KERNEL);
if (!gn)
return -ENOMEM;
+ gn->dev = dev;
gn->nr_luns = dev->nr_luns;
dev->mp = gn;
@@ -216,16 +244,15 @@ static int gennvm_register(struct nvm_dev *dev)
return 1;
err:
- kfree(gn);
+ gennvm_free(dev);
+ module_put(THIS_MODULE);
return ret;
}
static void gennvm_unregister(struct nvm_dev *dev)
{
- gennvm_blocks_free(dev);
- gennvm_luns_free(dev);
- kfree(dev->mp);
- dev->mp = NULL;
+ gennvm_free(dev);
+ module_put(THIS_MODULE);
}
static struct nvm_block *gennvm_get_blk(struct nvm_dev *dev,
@@ -240,23 +267,21 @@ static struct nvm_block *gennvm_get_blk(struct nvm_dev *dev,
if (list_empty(&lun->free_list)) {
pr_err_ratelimited("gennvm: lun %u have no free pages available",
lun->vlun.id);
- spin_unlock(&vlun->lock);
goto out;
}
- while (!is_gc && lun->vlun.nr_free_blocks < lun->reserved_blocks) {
- spin_unlock(&vlun->lock);
+ if (!is_gc && lun->vlun.nr_free_blocks < lun->reserved_blocks)
goto out;
- }
blk = list_first_entry(&lun->free_list, struct nvm_block, list);
list_move_tail(&blk->list, &lun->used_list);
blk->type = 1;
lun->vlun.nr_free_blocks--;
+ lun->vlun.nr_inuse_blocks++;
- spin_unlock(&vlun->lock);
out:
+ spin_unlock(&vlun->lock);
return blk;
}
@@ -271,16 +296,21 @@ static void gennvm_put_blk(struct nvm_dev *dev, struct nvm_block *blk)
case 1:
list_move_tail(&blk->list, &lun->free_list);
lun->vlun.nr_free_blocks++;
+ lun->vlun.nr_inuse_blocks--;
blk->type = 0;
break;
case 2:
list_move_tail(&blk->list, &lun->bb_list);
+ lun->vlun.nr_bad_blocks++;
+ lun->vlun.nr_inuse_blocks--;
break;
default:
WARN_ON_ONCE(1);
pr_err("gennvm: erroneous block type (%lu -> %u)\n",
blk->id, blk->type);
list_move_tail(&blk->list, &lun->bb_list);
+ lun->vlun.nr_bad_blocks++;
+ lun->vlun.nr_inuse_blocks--;
}
spin_unlock(&vlun->lock);
@@ -292,10 +322,10 @@ static void gennvm_addr_to_generic_mode(struct nvm_dev *dev, struct nvm_rq *rqd)
if (rqd->nr_pages > 1) {
for (i = 0; i < rqd->nr_pages; i++)
- rqd->ppa_list[i] = addr_to_generic_mode(dev,
+ rqd->ppa_list[i] = dev_to_generic_addr(dev,
rqd->ppa_list[i]);
} else {
- rqd->ppa_addr = addr_to_generic_mode(dev, rqd->ppa_addr);
+ rqd->ppa_addr = dev_to_generic_addr(dev, rqd->ppa_addr);
}
}
@@ -305,10 +335,10 @@ static void gennvm_generic_to_addr_mode(struct nvm_dev *dev, struct nvm_rq *rqd)
if (rqd->nr_pages > 1) {
for (i = 0; i < rqd->nr_pages; i++)
- rqd->ppa_list[i] = generic_to_addr_mode(dev,
+ rqd->ppa_list[i] = generic_to_dev_addr(dev,
rqd->ppa_list[i]);
} else {
- rqd->ppa_addr = generic_to_addr_mode(dev, rqd->ppa_addr);
+ rqd->ppa_addr = generic_to_dev_addr(dev, rqd->ppa_addr);
}
}
@@ -321,7 +351,7 @@ static int gennvm_submit_io(struct nvm_dev *dev, struct nvm_rq *rqd)
gennvm_generic_to_addr_mode(dev, rqd);
rqd->dev = dev;
- return dev->ops->submit_io(dev->q, rqd);
+ return dev->ops->submit_io(dev, rqd);
}
static void gennvm_blk_set_type(struct nvm_dev *dev, struct ppa_addr *ppa,
@@ -354,10 +384,10 @@ static void gennvm_mark_blk_bad(struct nvm_dev *dev, struct nvm_rq *rqd)
{
int i;
- if (!dev->ops->set_bb)
+ if (!dev->ops->set_bb_tbl)
return;
- if (dev->ops->set_bb(dev->q, rqd, 1))
+ if (dev->ops->set_bb_tbl(dev, rqd, 1))
return;
gennvm_addr_to_generic_mode(dev, rqd);
@@ -425,7 +455,7 @@ static int gennvm_erase_blk(struct nvm_dev *dev, struct nvm_block *blk,
gennvm_generic_to_addr_mode(dev, &rqd);
- ret = dev->ops->erase_block(dev->q, &rqd);
+ ret = dev->ops->erase_block(dev, &rqd);
if (plane_cnt)
nvm_dev_dma_free(dev, rqd.ppa_list, rqd.dma_ppa_list);
@@ -440,15 +470,24 @@ static struct nvm_lun *gennvm_get_lun(struct nvm_dev *dev, int lunid)
return &gn->luns[lunid].vlun;
}
-static void gennvm_free_blocks_print(struct nvm_dev *dev)
+static void gennvm_lun_info_print(struct nvm_dev *dev)
{
struct gen_nvm *gn = dev->mp;
struct gen_lun *lun;
unsigned int i;
- gennvm_for_each_lun(gn, lun, i)
- pr_info("%s: lun%8u\t%u\n",
- dev->name, i, lun->vlun.nr_free_blocks);
+
+ gennvm_for_each_lun(gn, lun, i) {
+ spin_lock(&lun->vlun.lock);
+
+ pr_info("%s: lun%8u\t%u\t%u\t%u\n",
+ dev->name, i,
+ lun->vlun.nr_free_blocks,
+ lun->vlun.nr_inuse_blocks,
+ lun->vlun.nr_bad_blocks);
+
+ spin_unlock(&lun->vlun.lock);
+ }
}
static struct nvmm_type gennvm = {
@@ -466,7 +505,7 @@ static struct nvmm_type gennvm = {
.erase_blk = gennvm_erase_blk,
.get_lun = gennvm_get_lun,
- .free_blocks_print = gennvm_free_blocks_print,
+ .lun_info_print = gennvm_lun_info_print,
};
static int __init gennvm_module_init(void)
diff --git a/drivers/lightnvm/gennvm.h b/drivers/lightnvm/gennvm.h
index d23bd3501ddc..9c24b5b32dac 100644
--- a/drivers/lightnvm/gennvm.h
+++ b/drivers/lightnvm/gennvm.h
@@ -35,6 +35,8 @@ struct gen_lun {
};
struct gen_nvm {
+ struct nvm_dev *dev;
+
int nr_luns;
struct gen_lun *luns;
};
diff --git a/drivers/lightnvm/rrpc.c b/drivers/lightnvm/rrpc.c
index 7ba64c87ba1c..134e4faba482 100644
--- a/drivers/lightnvm/rrpc.c
+++ b/drivers/lightnvm/rrpc.c
@@ -123,12 +123,42 @@ static u64 block_to_addr(struct rrpc *rrpc, struct rrpc_block *rblk)
return blk->id * rrpc->dev->pgs_per_blk;
}
+static struct ppa_addr linear_to_generic_addr(struct nvm_dev *dev,
+ struct ppa_addr r)
+{
+ struct ppa_addr l;
+ int secs, pgs, blks, luns;
+ sector_t ppa = r.ppa;
+
+ l.ppa = 0;
+
+ div_u64_rem(ppa, dev->sec_per_pg, &secs);
+ l.g.sec = secs;
+
+ sector_div(ppa, dev->sec_per_pg);
+ div_u64_rem(ppa, dev->sec_per_blk, &pgs);
+ l.g.pg = pgs;
+
+ sector_div(ppa, dev->pgs_per_blk);
+ div_u64_rem(ppa, dev->blks_per_lun, &blks);
+ l.g.blk = blks;
+
+ sector_div(ppa, dev->blks_per_lun);
+ div_u64_rem(ppa, dev->luns_per_chnl, &luns);
+ l.g.lun = luns;
+
+ sector_div(ppa, dev->luns_per_chnl);
+ l.g.ch = ppa;
+
+ return l;
+}
+
static struct ppa_addr rrpc_ppa_to_gaddr(struct nvm_dev *dev, u64 addr)
{
struct ppa_addr paddr;
paddr.ppa = addr;
- return __linear_to_generic_addr(dev, paddr);
+ return linear_to_generic_addr(dev, paddr);
}
/* requires lun->lock taken */
@@ -152,7 +182,7 @@ static struct rrpc_block *rrpc_get_blk(struct rrpc *rrpc, struct rrpc_lun *rlun,
struct nvm_block *blk;
struct rrpc_block *rblk;
- blk = nvm_get_blk(rrpc->dev, rlun->parent, 0);
+ blk = nvm_get_blk(rrpc->dev, rlun->parent, flags);
if (!blk)
return NULL;
@@ -172,6 +202,20 @@ static void rrpc_put_blk(struct rrpc *rrpc, struct rrpc_block *rblk)
nvm_put_blk(rrpc->dev, rblk->parent);
}
+static void rrpc_put_blks(struct rrpc *rrpc)
+{
+ struct rrpc_lun *rlun;
+ int i;
+
+ for (i = 0; i < rrpc->nr_luns; i++) {
+ rlun = &rrpc->luns[i];
+ if (rlun->cur)
+ rrpc_put_blk(rrpc, rlun->cur);
+ if (rlun->gc_cur)
+ rrpc_put_blk(rrpc, rlun->gc_cur);
+ }
+}
+
static struct rrpc_lun *get_next_lun(struct rrpc *rrpc)
{
int next = atomic_inc_return(&rrpc->next_lun);
@@ -972,7 +1016,7 @@ static int rrpc_map_init(struct rrpc *rrpc)
return 0;
/* Bring up the mapping table from device */
- ret = dev->ops->get_l2p_tbl(dev->q, 0, dev->total_pages,
+ ret = dev->ops->get_l2p_tbl(dev, 0, dev->total_pages,
rrpc_l2p_update, rrpc);
if (ret) {
pr_err("nvm: rrpc: could not read L2P table.\n");
@@ -1194,18 +1238,21 @@ static int rrpc_luns_configure(struct rrpc *rrpc)
rblk = rrpc_get_blk(rrpc, rlun, 0);
if (!rblk)
- return -EINVAL;
+ goto err;
rrpc_set_lun_cur(rlun, rblk);
/* Emergency gc block */
rblk = rrpc_get_blk(rrpc, rlun, 1);
if (!rblk)
- return -EINVAL;
+ goto err;
rlun->gc_cur = rblk;
}
return 0;
+err:
+ rrpc_put_blks(rrpc);
+ return -EINVAL;
}
static struct nvm_tgt_type tt_rrpc;
diff --git a/drivers/mailbox/mailbox-sti.c b/drivers/mailbox/mailbox-sti.c
index 4835817c5365..2394cfe892b6 100644
--- a/drivers/mailbox/mailbox-sti.c
+++ b/drivers/mailbox/mailbox-sti.c
@@ -384,7 +384,7 @@ static struct mbox_chan *sti_mbox_xlate(struct mbox_controller *mbox,
return chan;
}
-static struct mbox_chan_ops sti_mbox_ops = {
+static const struct mbox_chan_ops sti_mbox_ops = {
.startup = sti_mbox_startup_chan,
.shutdown = sti_mbox_shutdown_chan,
.send_data = sti_mbox_send_data,
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 7913fdcfc849..0a2e7273db9e 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -240,6 +240,15 @@ config DM_BUFIO
as a cache, holding recently-read blocks in memory and performing
delayed writes.
+config DM_DEBUG_BLOCK_STACK_TRACING
+ bool "Keep stack trace of persistent data block lock holders"
+ depends on STACKTRACE_SUPPORT && DM_BUFIO
+ select STACKTRACE
+ ---help---
+ Enable this for messages that may help debug problems with the
+ block manager locking used by thin provisioning and caching.
+
+ If unsure, say N.
config DM_BIO_PRISON
tristate
depends on BLK_DEV_DM
@@ -458,6 +467,18 @@ config DM_VERITY
If unsure, say N.
+config DM_VERITY_FEC
+ bool "Verity forward error correction support"
+ depends on DM_VERITY
+ select REED_SOLOMON
+ select REED_SOLOMON_DEC8
+ ---help---
+ Add forward error correction support to dm-verity. This option
+ makes it possible to use pre-generated error correction data to
+ recover from corrupted blocks.
+
+ If unsure, say N.
+
config DM_SWITCH
tristate "Switch target support (EXPERIMENTAL)"
depends on BLK_DEV_DM
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index f34979cd141a..62a65764e8e0 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -16,6 +16,7 @@ dm-cache-mq-y += dm-cache-policy-mq.o
dm-cache-smq-y += dm-cache-policy-smq.o
dm-cache-cleaner-y += dm-cache-policy-cleaner.o
dm-era-y += dm-era-target.o
+dm-verity-y += dm-verity-target.o
md-mod-y += md.o bitmap.o
raid456-y += raid5.o raid5-cache.o
@@ -63,3 +64,7 @@ obj-$(CONFIG_DM_LOG_WRITES) += dm-log-writes.o
ifeq ($(CONFIG_DM_UEVENT),y)
dm-mod-objs += dm-uevent.o
endif
+
+ifeq ($(CONFIG_DM_VERITY_FEC),y)
+dm-verity-objs += dm-verity-fec.o
+endif
diff --git a/drivers/md/bcache/util.c b/drivers/md/bcache/util.c
index db3ae4c2b223..dde6172f3f10 100644
--- a/drivers/md/bcache/util.c
+++ b/drivers/md/bcache/util.c
@@ -230,7 +230,7 @@ void bch_bio_map(struct bio *bio, void *base)
BUG_ON(!bio->bi_iter.bi_size);
BUG_ON(bio->bi_vcnt);
- bv->bv_offset = base ? ((unsigned long) base) % PAGE_SIZE : 0;
+ bv->bv_offset = base ? offset_in_page(base) : 0;
goto start;
for (; size; bio->bi_vcnt++, bv++) {
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index 2dd33085b331..cd77216beff1 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -16,6 +16,7 @@
#include <linux/shrinker.h>
#include <linux/module.h>
#include <linux/rbtree.h>
+#include <linux/stacktrace.h>
#define DM_MSG_PREFIX "bufio"
@@ -149,6 +150,11 @@ struct dm_buffer {
struct list_head write_list;
struct bio bio;
struct bio_vec bio_vec[DM_BUFIO_INLINE_VECS];
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
+#define MAX_STACK 10
+ struct stack_trace stack_trace;
+ unsigned long stack_entries[MAX_STACK];
+#endif
};
/*----------------------------------------------------------------*/
@@ -253,6 +259,17 @@ static LIST_HEAD(dm_bufio_all_clients);
*/
static DEFINE_MUTEX(dm_bufio_clients_lock);
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
+static void buffer_record_stack(struct dm_buffer *b)
+{
+ b->stack_trace.nr_entries = 0;
+ b->stack_trace.max_entries = MAX_STACK;
+ b->stack_trace.entries = b->stack_entries;
+ b->stack_trace.skip = 2;
+ save_stack_trace(&b->stack_trace);
+}
+#endif
+
/*----------------------------------------------------------------
* A red/black tree acts as an index for all the buffers.
*--------------------------------------------------------------*/
@@ -454,6 +471,9 @@ static struct dm_buffer *alloc_buffer(struct dm_bufio_client *c, gfp_t gfp_mask)
adjust_total_allocated(b->data_mode, (long)c->block_size);
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
+ memset(&b->stack_trace, 0, sizeof(b->stack_trace));
+#endif
return b;
}
@@ -630,7 +650,7 @@ static void use_inline_bio(struct dm_buffer *b, int rw, sector_t block,
do {
if (!bio_add_page(&b->bio, virt_to_page(ptr),
len < PAGE_SIZE ? len : PAGE_SIZE,
- virt_to_phys(ptr) & (PAGE_SIZE - 1))) {
+ offset_in_page(ptr))) {
BUG_ON(b->c->block_size <= PAGE_SIZE);
use_dmio(b, rw, block, end_io);
return;
@@ -1063,12 +1083,16 @@ static void *new_read(struct dm_bufio_client *c, sector_t block,
dm_bufio_lock(c);
b = __bufio_new(c, block, nf, &need_submit, &write_list);
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
+ if (b && b->hold_count == 1)
+ buffer_record_stack(b);
+#endif
dm_bufio_unlock(c);
__flush_write_list(&write_list);
if (!b)
- return b;
+ return NULL;
if (need_submit)
submit_io(b, READ, b->block, read_endio);
@@ -1462,6 +1486,7 @@ static void drop_buffers(struct dm_bufio_client *c)
{
struct dm_buffer *b;
int i;
+ bool warned = false;
BUG_ON(dm_bufio_in_request());
@@ -1476,9 +1501,21 @@ static void drop_buffers(struct dm_bufio_client *c)
__free_buffer_wake(b);
for (i = 0; i < LIST_SIZE; i++)
- list_for_each_entry(b, &c->lru[i], lru_list)
+ list_for_each_entry(b, &c->lru[i], lru_list) {
+ WARN_ON(!warned);
+ warned = true;
DMERR("leaked buffer %llx, hold count %u, list %d",
(unsigned long long)b->block, b->hold_count, i);
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
+ print_stack_trace(&b->stack_trace, 1);
+ b->hold_count = 0; /* mark unclaimed to avoid BUG_ON below */
+#endif
+ }
+
+#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
+ while ((b = __get_unclaimed_buffer(c)))
+ __free_buffer_wake(b);
+#endif
for (i = 0; i < LIST_SIZE; i++)
BUG_ON(!list_empty(&c->lru[i]));
@@ -1891,8 +1928,7 @@ static void __exit dm_bufio_exit(void)
bug = 1;
}
- if (bug)
- BUG();
+ BUG_ON(bug);
}
module_init(dm_bufio_init)
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index 2fd4c8296144..5780accffa30 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -118,14 +118,12 @@ static void iot_io_end(struct io_tracker *iot, sector_t len)
*/
struct dm_hook_info {
bio_end_io_t *bi_end_io;
- void *bi_private;
};
static void dm_hook_bio(struct dm_hook_info *h, struct bio *bio,
bio_end_io_t *bi_end_io, void *bi_private)
{
h->bi_end_io = bio->bi_end_io;
- h->bi_private = bio->bi_private;
bio->bi_end_io = bi_end_io;
bio->bi_private = bi_private;
@@ -134,7 +132,6 @@ static void dm_hook_bio(struct dm_hook_info *h, struct bio *bio,
static void dm_unhook_bio(struct dm_hook_info *h, struct bio *bio)
{
bio->bi_end_io = h->bi_end_io;
- bio->bi_private = h->bi_private;
}
/*----------------------------------------------------------------*/
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 917d47e290ae..3147c8d09ea8 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -112,7 +112,8 @@ struct iv_tcw_private {
* and encrypts / decrypts at the same time.
*/
enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID,
- DM_CRYPT_SAME_CPU, DM_CRYPT_NO_OFFLOAD };
+ DM_CRYPT_SAME_CPU, DM_CRYPT_NO_OFFLOAD,
+ DM_CRYPT_EXIT_THREAD};
/*
* The fields in here must be read only after initialization.
@@ -1203,20 +1204,18 @@ continue_locked:
if (!RB_EMPTY_ROOT(&cc->write_tree))
goto pop_from_list;
+ if (unlikely(test_bit(DM_CRYPT_EXIT_THREAD, &cc->flags))) {
+ spin_unlock_irq(&cc->write_thread_wait.lock);
+ break;
+ }
+
__set_current_state(TASK_INTERRUPTIBLE);
__add_wait_queue(&cc->write_thread_wait, &wait);
spin_unlock_irq(&cc->write_thread_wait.lock);
- if (unlikely(kthread_should_stop())) {
- set_task_state(current, TASK_RUNNING);
- remove_wait_queue(&cc->write_thread_wait, &wait);
- break;
- }
-
schedule();
- set_task_state(current, TASK_RUNNING);
spin_lock_irq(&cc->write_thread_wait.lock);
__remove_wait_queue(&cc->write_thread_wait, &wait);
goto continue_locked;
@@ -1531,8 +1530,13 @@ static void crypt_dtr(struct dm_target *ti)
if (!cc)
return;
- if (cc->write_thread)
+ if (cc->write_thread) {
+ spin_lock_irq(&cc->write_thread_wait.lock);
+ set_bit(DM_CRYPT_EXIT_THREAD, &cc->flags);
+ wake_up_locked(&cc->write_thread_wait);
+ spin_unlock_irq(&cc->write_thread_wait.lock);
kthread_stop(cc->write_thread);
+ }
if (cc->io_queue)
destroy_workqueue(cc->io_queue);
diff --git a/drivers/md/dm-exception-store.h b/drivers/md/dm-exception-store.h
index fae34e7a0b1e..12b5216c2cfe 100644
--- a/drivers/md/dm-exception-store.h
+++ b/drivers/md/dm-exception-store.h
@@ -69,7 +69,7 @@ struct dm_exception_store_type {
* Update the metadata with this exception.
*/
void (*commit_exception) (struct dm_exception_store *store,
- struct dm_exception *e,
+ struct dm_exception *e, int valid,
void (*callback) (void *, int success),
void *callback_context);
diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c
index 81c5e1a1f363..06d426eb5a30 100644
--- a/drivers/md/dm-io.c
+++ b/drivers/md/dm-io.c
@@ -246,7 +246,7 @@ static void vm_dp_init(struct dpages *dp, void *data)
{
dp->get_page = vm_get_page;
dp->next_page = vm_next_page;
- dp->context_u = ((unsigned long) data) & (PAGE_SIZE - 1);
+ dp->context_u = offset_in_page(data);
dp->context_ptr = data;
}
@@ -271,7 +271,7 @@ static void km_dp_init(struct dpages *dp, void *data)
{
dp->get_page = km_get_page;
dp->next_page = km_next_page;
- dp->context_u = ((unsigned long) data) & (PAGE_SIZE - 1);
+ dp->context_u = offset_in_page(data);
dp->context_ptr = data;
}
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index aaa6caa46a9f..cfa29f574c2a 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -1537,32 +1537,34 @@ static int multipath_prepare_ioctl(struct dm_target *ti,
struct block_device **bdev, fmode_t *mode)
{
struct multipath *m = ti->private;
- struct pgpath *pgpath;
unsigned long flags;
int r;
- r = 0;
-
spin_lock_irqsave(&m->lock, flags);
if (!m->current_pgpath)
__choose_pgpath(m, 0);
- pgpath = m->current_pgpath;
-
- if (pgpath) {
- *bdev = pgpath->path.dev->bdev;
- *mode = pgpath->path.dev->mode;
+ if (m->current_pgpath) {
+ if (!m->queue_io) {
+ *bdev = m->current_pgpath->path.dev->bdev;
+ *mode = m->current_pgpath->path.dev->mode;
+ r = 0;
+ } else {
+ /* pg_init has not started or completed */
+ r = -ENOTCONN;
+ }
+ } else {
+ /* No path is available */
+ if (m->queue_if_no_path)
+ r = -ENOTCONN;
+ else
+ r = -EIO;
}
- if ((pgpath && m->queue_io) || (!pgpath && m->queue_if_no_path))
- r = -ENOTCONN;
- else if (!*bdev)
- r = -EIO;
-
spin_unlock_irqrestore(&m->lock, flags);
- if (r == -ENOTCONN && !fatal_signal_pending(current)) {
+ if (r == -ENOTCONN) {
spin_lock_irqsave(&m->lock, flags);
if (!m->current_pg) {
/* Path status changed, redo selection */
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c
index 3164b8bce294..4d3909393f2c 100644
--- a/drivers/md/dm-snap-persistent.c
+++ b/drivers/md/dm-snap-persistent.c
@@ -695,7 +695,7 @@ static int persistent_prepare_exception(struct dm_exception_store *store,
}
static void persistent_commit_exception(struct dm_exception_store *store,
- struct dm_exception *e,
+ struct dm_exception *e, int valid,
void (*callback) (void *, int success),
void *callback_context)
{
@@ -704,6 +704,9 @@ static void persistent_commit_exception(struct dm_exception_store *store,
struct core_exception ce;
struct commit_callback *cb;
+ if (!valid)
+ ps->valid = 0;
+
ce.old_chunk = e->old_chunk;
ce.new_chunk = e->new_chunk;
write_exception(ps, ps->current_committed++, &ce);
diff --git a/drivers/md/dm-snap-transient.c b/drivers/md/dm-snap-transient.c
index 9b7c8c8049d6..4d50a12cf00c 100644
--- a/drivers/md/dm-snap-transient.c
+++ b/drivers/md/dm-snap-transient.c
@@ -52,12 +52,12 @@ static int transient_prepare_exception(struct dm_exception_store *store,
}
static void transient_commit_exception(struct dm_exception_store *store,
- struct dm_exception *e,
+ struct dm_exception *e, int valid,
void (*callback) (void *, int success),
void *callback_context)
{
/* Just succeed */
- callback(callback_context, 1);
+ callback(callback_context, valid);
}
static void transient_usage(struct dm_exception_store *store,
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index c06b74e91cd6..3766386080a4 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -207,7 +207,6 @@ struct dm_snap_pending_exception {
*/
struct bio *full_bio;
bio_end_io_t *full_bio_end_io;
- void *full_bio_private;
};
/*
@@ -1438,8 +1437,9 @@ static void __invalidate_snapshot(struct dm_snapshot *s, int err)
dm_table_event(s->ti->table);
}
-static void pending_complete(struct dm_snap_pending_exception *pe, int success)
+static void pending_complete(void *context, int success)
{
+ struct dm_snap_pending_exception *pe = context;
struct dm_exception *e;
struct dm_snapshot *s = pe->snap;
struct bio *origin_bios = NULL;
@@ -1485,10 +1485,8 @@ out:
snapshot_bios = bio_list_get(&pe->snapshot_bios);
origin_bios = bio_list_get(&pe->origin_bios);
full_bio = pe->full_bio;
- if (full_bio) {
+ if (full_bio)
full_bio->bi_end_io = pe->full_bio_end_io;
- full_bio->bi_private = pe->full_bio_private;
- }
increment_pending_exceptions_done_count();
up_write(&s->lock);
@@ -1509,24 +1507,13 @@ out:
free_pending_exception(pe);
}
-static void commit_callback(void *context, int success)
-{
- struct dm_snap_pending_exception *pe = context;
-
- pending_complete(pe, success);
-}
-
static void complete_exception(struct dm_snap_pending_exception *pe)
{
struct dm_snapshot *s = pe->snap;
- if (unlikely(pe->copy_error))
- pending_complete(pe, 0);
-
- else
- /* Update the metadata if we are persistent */
- s->store->type->commit_exception(s->store, &pe->e,
- commit_callback, pe);
+ /* Update the metadata if we are persistent */
+ s->store->type->commit_exception(s->store, &pe->e, !pe->copy_error,
+ pending_complete, pe);
}
/*
@@ -1605,7 +1592,6 @@ static void start_full_bio(struct dm_snap_pending_exception *pe,
pe->full_bio = bio;
pe->full_bio_end_io = bio->bi_end_io;
- pe->full_bio_private = bio->bi_private;
callback_data = dm_kcopyd_prepare_callback(s->kcopyd_client,
copy_callback, pe);
diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c
index 1fa45695b68a..f962d6453afd 100644
--- a/drivers/md/dm-thin-metadata.c
+++ b/drivers/md/dm-thin-metadata.c
@@ -1207,6 +1207,12 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd)
dm_block_t held_root;
/*
+ * We commit to ensure the btree roots which we increment in a
+ * moment are up to date.
+ */
+ __commit_transaction(pmd);
+
+ /*
* Copy the superblock.
*/
dm_sm_inc_block(pmd->metadata_sm, THIN_SUPERBLOCK_LOCATION);
@@ -1389,8 +1395,21 @@ static bool __snapshotted_since(struct dm_thin_device *td, uint32_t time)
return td->snapshotted_time > time;
}
-int dm_thin_find_block(struct dm_thin_device *td, dm_block_t block,
- int can_issue_io, struct dm_thin_lookup_result *result)
+static void unpack_lookup_result(struct dm_thin_device *td, __le64 value,
+ struct dm_thin_lookup_result *result)
+{
+ uint64_t block_time = 0;
+ dm_block_t exception_block;
+ uint32_t exception_time;
+
+ block_time = le64_to_cpu(value);
+ unpack_block_time(block_time, &exception_block, &exception_time);
+ result->block = exception_block;
+ result->shared = __snapshotted_since(td, exception_time);
+}
+
+static int __find_block(struct dm_thin_device *td, dm_block_t block,
+ int can_issue_io, struct dm_thin_lookup_result *result)
{
int r;
__le64 value;
@@ -1398,39 +1417,56 @@ int dm_thin_find_block(struct dm_thin_device *td, dm_block_t block,
dm_block_t keys[2] = { td->id, block };
struct dm_btree_info *info;
- down_read(&pmd->root_lock);
- if (pmd->fail_io) {
- up_read(&pmd->root_lock);
- return -EINVAL;
- }
-
if (can_issue_io) {
info = &pmd->info;
} else
info = &pmd->nb_info;
r = dm_btree_lookup(info, pmd->root, keys, &value);
- if (!r) {
- uint64_t block_time = 0;
- dm_block_t exception_block;
- uint32_t exception_time;
-
- block_time = le64_to_cpu(value);
- unpack_block_time(block_time, &exception_block,
- &exception_time);
- result->block = exception_block;
- result->shared = __snapshotted_since(td, exception_time);
+ if (!r)
+ unpack_lookup_result(td, value, result);
+
+ return r;
+}
+
+int dm_thin_find_block(struct dm_thin_device *td, dm_block_t block,
+ int can_issue_io, struct dm_thin_lookup_result *result)
+{
+ int r;
+ struct dm_pool_metadata *pmd = td->pmd;
+
+ down_read(&pmd->root_lock);
+ if (pmd->fail_io) {
+ up_read(&pmd->root_lock);
+ return -EINVAL;
}
+ r = __find_block(td, block, can_issue_io, result);
+
up_read(&pmd->root_lock);
return r;
}
-/* FIXME: write a more efficient one in btree */
-int dm_thin_find_mapped_range(struct dm_thin_device *td,
- dm_block_t begin, dm_block_t end,
- dm_block_t *thin_begin, dm_block_t *thin_end,
- dm_block_t *pool_begin, bool *maybe_shared)
+static int __find_next_mapped_block(struct dm_thin_device *td, dm_block_t block,
+ dm_block_t *vblock,
+ struct dm_thin_lookup_result *result)
+{
+ int r;
+ __le64 value;
+ struct dm_pool_metadata *pmd = td->pmd;
+ dm_block_t keys[2] = { td->id, block };
+
+ r = dm_btree_lookup_next(&pmd->info, pmd->root, keys, vblock, &value);
+ if (!r)
+ unpack_lookup_result(td, value, result);
+
+ return r;
+}
+
+static int __find_mapped_range(struct dm_thin_device *td,
+ dm_block_t begin, dm_block_t end,
+ dm_block_t *thin_begin, dm_block_t *thin_end,
+ dm_block_t *pool_begin, bool *maybe_shared)
{
int r;
dm_block_t pool_end;
@@ -1439,21 +1475,11 @@ int dm_thin_find_mapped_range(struct dm_thin_device *td,
if (end < begin)
return -ENODATA;
- /*
- * Find first mapped block.
- */
- while (begin < end) {
- r = dm_thin_find_block(td, begin, true, &lookup);
- if (r) {
- if (r != -ENODATA)
- return r;
- } else
- break;
-
- begin++;
- }
+ r = __find_next_mapped_block(td, begin, &begin, &lookup);
+ if (r)
+ return r;
- if (begin == end)
+ if (begin >= end)
return -ENODATA;
*thin_begin = begin;
@@ -1463,7 +1489,7 @@ int dm_thin_find_mapped_range(struct dm_thin_device *td,
begin++;
pool_end = *pool_begin + 1;
while (begin != end) {
- r = dm_thin_find_block(td, begin, true, &lookup);
+ r = __find_block(td, begin, true, &lookup);
if (r) {
if (r == -ENODATA)
break;
@@ -1483,6 +1509,24 @@ int dm_thin_find_mapped_range(struct dm_thin_device *td,
return 0;
}
+int dm_thin_find_mapped_range(struct dm_thin_device *td,
+ dm_block_t begin, dm_block_t end,
+ dm_block_t *thin_begin, dm_block_t *thin_end,
+ dm_block_t *pool_begin, bool *maybe_shared)
+{
+ int r = -EINVAL;
+ struct dm_pool_metadata *pmd = td->pmd;
+
+ down_read(&pmd->root_lock);
+ if (!pmd->fail_io) {
+ r = __find_mapped_range(td, begin, end, thin_begin, thin_end,
+ pool_begin, maybe_shared);
+ }
+ up_read(&pmd->root_lock);
+
+ return r;
+}
+
static int __insert(struct dm_thin_device *td, dm_block_t block,
dm_block_t data_block)
{
@@ -1538,7 +1582,7 @@ static int __remove(struct dm_thin_device *td, dm_block_t block)
static int __remove_range(struct dm_thin_device *td, dm_block_t begin, dm_block_t end)
{
int r;
- unsigned count;
+ unsigned count, total_count = 0;
struct dm_pool_metadata *pmd = td->pmd;
dm_block_t keys[1] = { td->id };
__le64 value;
@@ -1561,11 +1605,29 @@ static int __remove_range(struct dm_thin_device *td, dm_block_t begin, dm_block_
if (r)
return r;
- r = dm_btree_remove_leaves(&pmd->bl_info, mapping_root, &begin, end, &mapping_root, &count);
- if (r)
- return r;
+ /*
+ * Remove leaves stops at the first unmapped entry, so we have to
+ * loop round finding mapped ranges.
+ */
+ while (begin < end) {
+ r = dm_btree_lookup_next(&pmd->bl_info, mapping_root, &begin, &begin, &value);
+ if (r == -ENODATA)
+ break;
+
+ if (r)
+ return r;
+
+ if (begin >= end)
+ break;
+
+ r = dm_btree_remove_leaves(&pmd->bl_info, mapping_root, &begin, end, &mapping_root, &count);
+ if (r)
+ return r;
+
+ total_count += count;
+ }
- td->mapped_blocks -= count;
+ td->mapped_blocks -= total_count;
td->changed = 1;
/*
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index 3897b90bd462..72d91f477683 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -2432,6 +2432,7 @@ static void set_pool_mode(struct pool *pool, enum pool_mode new_mode)
case PM_WRITE:
if (old_mode != new_mode)
notify_of_pool_mode_change(pool, "write");
+ pool->pf.error_if_no_space = pt->requested_pf.error_if_no_space;
dm_pool_metadata_read_write(pool->pmd);
pool->process_bio = process_bio;
pool->process_discard = process_discard_bio;
@@ -3452,8 +3453,8 @@ static void pool_postsuspend(struct dm_target *ti)
struct pool_c *pt = ti->private;
struct pool *pool = pt->pool;
- cancel_delayed_work(&pool->waker);
- cancel_delayed_work(&pool->no_space_timeout);
+ cancel_delayed_work_sync(&pool->waker);
+ cancel_delayed_work_sync(&pool->no_space_timeout);
flush_workqueue(pool->wq);
(void) commit(pool);
}
@@ -3885,7 +3886,7 @@ static struct target_type pool_target = {
.name = "thin-pool",
.features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
DM_TARGET_IMMUTABLE,
- .version = {1, 16, 0},
+ .version = {1, 17, 0},
.module = THIS_MODULE,
.ctr = pool_ctr,
.dtr = pool_dtr,
@@ -4249,10 +4250,9 @@ static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits)
{
struct thin_c *tc = ti->private;
struct pool *pool = tc->pool;
- struct queue_limits *pool_limits = dm_get_queue_limits(pool->pool_md);
- if (!pool_limits->discard_granularity)
- return; /* pool's discard support is disabled */
+ if (!pool->pf.discard_enabled)
+ return;
limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT;
limits->max_discard_sectors = 2048 * 1024 * 16; /* 16G */
@@ -4260,7 +4260,7 @@ static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits)
static struct target_type thin_target = {
.name = "thin",
- .version = {1, 16, 0},
+ .version = {1, 17, 0},
.module = THIS_MODULE,
.ctr = thin_ctr,
.dtr = thin_dtr,
diff --git a/drivers/md/dm-verity-fec.c b/drivers/md/dm-verity-fec.c
new file mode 100644
index 000000000000..1cc10c4de701
--- /dev/null
+++ b/drivers/md/dm-verity-fec.c
@@ -0,0 +1,818 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Author: Sami Tolvanen <samitolvanen@google.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 "dm-verity-fec.h"
+#include <linux/math64.h>
+
+#define DM_MSG_PREFIX "verity-fec"
+
+/*
+ * If error correction has been configured, returns true.
+ */
+bool verity_fec_is_enabled(struct dm_verity *v)
+{
+ return v->fec && v->fec->dev;
+}
+
+/*
+ * Return a pointer to dm_verity_fec_io after dm_verity_io and its variable
+ * length fields.
+ */
+static inline struct dm_verity_fec_io *fec_io(struct dm_verity_io *io)
+{
+ return (struct dm_verity_fec_io *) verity_io_digest_end(io->v, io);
+}
+
+/*
+ * Return an interleaved offset for a byte in RS block.
+ */
+static inline u64 fec_interleave(struct dm_verity *v, u64 offset)
+{
+ u32 mod;
+
+ mod = do_div(offset, v->fec->rsn);
+ return offset + mod * (v->fec->rounds << v->data_dev_block_bits);
+}
+
+/*
+ * Decode an RS block using Reed-Solomon.
+ */
+static int fec_decode_rs8(struct dm_verity *v, struct dm_verity_fec_io *fio,
+ u8 *data, u8 *fec, int neras)
+{
+ int i;
+ uint16_t par[DM_VERITY_FEC_RSM - DM_VERITY_FEC_MIN_RSN];
+
+ for (i = 0; i < v->fec->roots; i++)
+ par[i] = fec[i];
+
+ return decode_rs8(fio->rs, data, par, v->fec->rsn, NULL, neras,
+ fio->erasures, 0, NULL);
+}
+
+/*
+ * Read error-correcting codes for the requested RS block. Returns a pointer
+ * to the data block. Caller is responsible for releasing buf.
+ */
+static u8 *fec_read_parity(struct dm_verity *v, u64 rsb, int index,
+ unsigned *offset, struct dm_buffer **buf)
+{
+ u64 position, block;
+ u8 *res;
+
+ position = (index + rsb) * v->fec->roots;
+ block = position >> v->data_dev_block_bits;
+ *offset = (unsigned)(position - (block << v->data_dev_block_bits));
+
+ res = dm_bufio_read(v->fec->bufio, v->fec->start + block, buf);
+ if (unlikely(IS_ERR(res))) {
+ DMERR("%s: FEC %llu: parity read failed (block %llu): %ld",
+ v->data_dev->name, (unsigned long long)rsb,
+ (unsigned long long)(v->fec->start + block),
+ PTR_ERR(res));
+ *buf = NULL;
+ }
+
+ return res;
+}
+
+/* Loop over each preallocated buffer slot. */
+#define fec_for_each_prealloc_buffer(__i) \
+ for (__i = 0; __i < DM_VERITY_FEC_BUF_PREALLOC; __i++)
+
+/* Loop over each extra buffer slot. */
+#define fec_for_each_extra_buffer(io, __i) \
+ for (__i = DM_VERITY_FEC_BUF_PREALLOC; __i < DM_VERITY_FEC_BUF_MAX; __i++)
+
+/* Loop over each allocated buffer. */
+#define fec_for_each_buffer(io, __i) \
+ for (__i = 0; __i < (io)->nbufs; __i++)
+
+/* Loop over each RS block in each allocated buffer. */
+#define fec_for_each_buffer_rs_block(io, __i, __j) \
+ fec_for_each_buffer(io, __i) \
+ for (__j = 0; __j < 1 << DM_VERITY_FEC_BUF_RS_BITS; __j++)
+
+/*
+ * Return a pointer to the current RS block when called inside
+ * fec_for_each_buffer_rs_block.
+ */
+static inline u8 *fec_buffer_rs_block(struct dm_verity *v,
+ struct dm_verity_fec_io *fio,
+ unsigned i, unsigned j)
+{
+ return &fio->bufs[i][j * v->fec->rsn];
+}
+
+/*
+ * Return an index to the current RS block when called inside
+ * fec_for_each_buffer_rs_block.
+ */
+static inline unsigned fec_buffer_rs_index(unsigned i, unsigned j)
+{
+ return (i << DM_VERITY_FEC_BUF_RS_BITS) + j;
+}
+
+/*
+ * Decode all RS blocks from buffers and copy corrected bytes into fio->output
+ * starting from block_offset.
+ */
+static int fec_decode_bufs(struct dm_verity *v, struct dm_verity_fec_io *fio,
+ u64 rsb, int byte_index, unsigned block_offset,
+ int neras)
+{
+ int r, corrected = 0, res;
+ struct dm_buffer *buf;
+ unsigned n, i, offset;
+ u8 *par, *block;
+
+ par = fec_read_parity(v, rsb, block_offset, &offset, &buf);
+ if (IS_ERR(par))
+ return PTR_ERR(par);
+
+ /*
+ * Decode the RS blocks we have in bufs. Each RS block results in
+ * one corrected target byte and consumes fec->roots parity bytes.
+ */
+ fec_for_each_buffer_rs_block(fio, n, i) {
+ block = fec_buffer_rs_block(v, fio, n, i);
+ res = fec_decode_rs8(v, fio, block, &par[offset], neras);
+ if (res < 0) {
+ dm_bufio_release(buf);
+
+ r = res;
+ goto error;
+ }
+
+ corrected += res;
+ fio->output[block_offset] = block[byte_index];
+
+ block_offset++;
+ if (block_offset >= 1 << v->data_dev_block_bits)
+ goto done;
+
+ /* read the next block when we run out of parity bytes */
+ offset += v->fec->roots;
+ if (offset >= 1 << v->data_dev_block_bits) {
+ dm_bufio_release(buf);
+
+ par = fec_read_parity(v, rsb, block_offset, &offset, &buf);
+ if (unlikely(IS_ERR(par)))
+ return PTR_ERR(par);
+ }
+ }
+done:
+ r = corrected;
+error:
+ if (r < 0 && neras)
+ DMERR_LIMIT("%s: FEC %llu: failed to correct: %d",
+ v->data_dev->name, (unsigned long long)rsb, r);
+ else if (r > 0)
+ DMWARN_LIMIT("%s: FEC %llu: corrected %d errors",
+ v->data_dev->name, (unsigned long long)rsb, r);
+
+ return r;
+}
+
+/*
+ * Locate data block erasures using verity hashes.
+ */
+static int fec_is_erasure(struct dm_verity *v, struct dm_verity_io *io,
+ u8 *want_digest, u8 *data)
+{
+ if (unlikely(verity_hash(v, verity_io_hash_desc(v, io),
+ data, 1 << v->data_dev_block_bits,
+ verity_io_real_digest(v, io))))
+ return 0;
+
+ return memcmp(verity_io_real_digest(v, io), want_digest,
+ v->digest_size) != 0;
+}
+
+/*
+ * Read data blocks that are part of the RS block and deinterleave as much as
+ * fits into buffers. Check for erasure locations if @neras is non-NULL.
+ */
+static int fec_read_bufs(struct dm_verity *v, struct dm_verity_io *io,
+ u64 rsb, u64 target, unsigned block_offset,
+ int *neras)
+{
+ bool is_zero;
+ int i, j, target_index = -1;
+ struct dm_buffer *buf;
+ struct dm_bufio_client *bufio;
+ struct dm_verity_fec_io *fio = fec_io(io);
+ u64 block, ileaved;
+ u8 *bbuf, *rs_block;
+ u8 want_digest[v->digest_size];
+ unsigned n, k;
+
+ if (neras)
+ *neras = 0;
+
+ /*
+ * read each of the rsn data blocks that are part of the RS block, and
+ * interleave contents to available bufs
+ */
+ for (i = 0; i < v->fec->rsn; i++) {
+ ileaved = fec_interleave(v, rsb * v->fec->rsn + i);
+
+ /*
+ * target is the data block we want to correct, target_index is
+ * the index of this block within the rsn RS blocks
+ */
+ if (ileaved == target)
+ target_index = i;
+
+ block = ileaved >> v->data_dev_block_bits;
+ bufio = v->fec->data_bufio;
+
+ if (block >= v->data_blocks) {
+ block -= v->data_blocks;
+
+ /*
+ * blocks outside the area were assumed to contain
+ * zeros when encoding data was generated
+ */
+ if (unlikely(block >= v->fec->hash_blocks))
+ continue;
+
+ block += v->hash_start;
+ bufio = v->bufio;
+ }
+
+ bbuf = dm_bufio_read(bufio, block, &buf);
+ if (unlikely(IS_ERR(bbuf))) {
+ DMWARN_LIMIT("%s: FEC %llu: read failed (%llu): %ld",
+ v->data_dev->name,
+ (unsigned long long)rsb,
+ (unsigned long long)block, PTR_ERR(bbuf));
+
+ /* assume the block is corrupted */
+ if (neras && *neras <= v->fec->roots)
+ fio->erasures[(*neras)++] = i;
+
+ continue;
+ }
+
+ /* locate erasures if the block is on the data device */
+ if (bufio == v->fec->data_bufio &&
+ verity_hash_for_block(v, io, block, want_digest,
+ &is_zero) == 0) {
+ /* skip known zero blocks entirely */
+ if (is_zero)
+ continue;
+
+ /*
+ * skip if we have already found the theoretical
+ * maximum number (i.e. fec->roots) of erasures
+ */
+ if (neras && *neras <= v->fec->roots &&
+ fec_is_erasure(v, io, want_digest, bbuf))
+ fio->erasures[(*neras)++] = i;
+ }
+
+ /*
+ * deinterleave and copy the bytes that fit into bufs,
+ * starting from block_offset
+ */
+ fec_for_each_buffer_rs_block(fio, n, j) {
+ k = fec_buffer_rs_index(n, j) + block_offset;
+
+ if (k >= 1 << v->data_dev_block_bits)
+ goto done;
+
+ rs_block = fec_buffer_rs_block(v, fio, n, j);
+ rs_block[i] = bbuf[k];
+ }
+done:
+ dm_bufio_release(buf);
+ }
+
+ return target_index;
+}
+
+/*
+ * Allocate RS control structure and FEC buffers from preallocated mempools,
+ * and attempt to allocate as many extra buffers as available.
+ */
+static int fec_alloc_bufs(struct dm_verity *v, struct dm_verity_fec_io *fio)
+{
+ unsigned n;
+
+ if (!fio->rs) {
+ fio->rs = mempool_alloc(v->fec->rs_pool, 0);
+ if (unlikely(!fio->rs)) {
+ DMERR("failed to allocate RS");
+ return -ENOMEM;
+ }
+ }
+
+ fec_for_each_prealloc_buffer(n) {
+ if (fio->bufs[n])
+ continue;
+
+ fio->bufs[n] = mempool_alloc(v->fec->prealloc_pool, GFP_NOIO);
+ if (unlikely(!fio->bufs[n])) {
+ DMERR("failed to allocate FEC buffer");
+ return -ENOMEM;
+ }
+ }
+
+ /* try to allocate the maximum number of buffers */
+ fec_for_each_extra_buffer(fio, n) {
+ if (fio->bufs[n])
+ continue;
+
+ fio->bufs[n] = mempool_alloc(v->fec->extra_pool, GFP_NOIO);
+ /* we can manage with even one buffer if necessary */
+ if (unlikely(!fio->bufs[n]))
+ break;
+ }
+ fio->nbufs = n;
+
+ if (!fio->output) {
+ fio->output = mempool_alloc(v->fec->output_pool, GFP_NOIO);
+
+ if (!fio->output) {
+ DMERR("failed to allocate FEC page");
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize buffers and clear erasures. fec_read_bufs() assumes buffers are
+ * zeroed before deinterleaving.
+ */
+static void fec_init_bufs(struct dm_verity *v, struct dm_verity_fec_io *fio)
+{
+ unsigned n;
+
+ fec_for_each_buffer(fio, n)
+ memset(fio->bufs[n], 0, v->fec->rsn << DM_VERITY_FEC_BUF_RS_BITS);
+
+ memset(fio->erasures, 0, sizeof(fio->erasures));
+}
+
+/*
+ * Decode all RS blocks in a single data block and return the target block
+ * (indicated by @offset) in fio->output. If @use_erasures is non-zero, uses
+ * hashes to locate erasures.
+ */
+static int fec_decode_rsb(struct dm_verity *v, struct dm_verity_io *io,
+ struct dm_verity_fec_io *fio, u64 rsb, u64 offset,
+ bool use_erasures)
+{
+ int r, neras = 0;
+ unsigned pos;
+
+ r = fec_alloc_bufs(v, fio);
+ if (unlikely(r < 0))
+ return r;
+
+ for (pos = 0; pos < 1 << v->data_dev_block_bits; ) {
+ fec_init_bufs(v, fio);
+
+ r = fec_read_bufs(v, io, rsb, offset, pos,
+ use_erasures ? &neras : NULL);
+ if (unlikely(r < 0))
+ return r;
+
+ r = fec_decode_bufs(v, fio, rsb, r, pos, neras);
+ if (r < 0)
+ return r;
+
+ pos += fio->nbufs << DM_VERITY_FEC_BUF_RS_BITS;
+ }
+
+ /* Always re-validate the corrected block against the expected hash */
+ r = verity_hash(v, verity_io_hash_desc(v, io), fio->output,
+ 1 << v->data_dev_block_bits,
+ verity_io_real_digest(v, io));
+ if (unlikely(r < 0))
+ return r;
+
+ if (memcmp(verity_io_real_digest(v, io), verity_io_want_digest(v, io),
+ v->digest_size)) {
+ DMERR_LIMIT("%s: FEC %llu: failed to correct (%d erasures)",
+ v->data_dev->name, (unsigned long long)rsb, neras);
+ return -EILSEQ;
+ }
+
+ return 0;
+}
+
+static int fec_bv_copy(struct dm_verity *v, struct dm_verity_io *io, u8 *data,
+ size_t len)
+{
+ struct dm_verity_fec_io *fio = fec_io(io);
+
+ memcpy(data, &fio->output[fio->output_pos], len);
+ fio->output_pos += len;
+
+ return 0;
+}
+
+/*
+ * Correct errors in a block. Copies corrected block to dest if non-NULL,
+ * otherwise to a bio_vec starting from iter.
+ */
+int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
+ enum verity_block_type type, sector_t block, u8 *dest,
+ struct bvec_iter *iter)
+{
+ int r;
+ struct dm_verity_fec_io *fio = fec_io(io);
+ u64 offset, res, rsb;
+
+ if (!verity_fec_is_enabled(v))
+ return -EOPNOTSUPP;
+
+ if (type == DM_VERITY_BLOCK_TYPE_METADATA)
+ block += v->data_blocks;
+
+ /*
+ * For RS(M, N), the continuous FEC data is divided into blocks of N
+ * bytes. Since block size may not be divisible by N, the last block
+ * is zero padded when decoding.
+ *
+ * Each byte of the block is covered by a different RS(M, N) code,
+ * and each code is interleaved over N blocks to make it less likely
+ * that bursty corruption will leave us in unrecoverable state.
+ */
+
+ offset = block << v->data_dev_block_bits;
+
+ res = offset;
+ div64_u64(res, v->fec->rounds << v->data_dev_block_bits);
+
+ /*
+ * The base RS block we can feed to the interleaver to find out all
+ * blocks required for decoding.
+ */
+ rsb = offset - res * (v->fec->rounds << v->data_dev_block_bits);
+
+ /*
+ * Locating erasures is slow, so attempt to recover the block without
+ * them first. Do a second attempt with erasures if the corruption is
+ * bad enough.
+ */
+ r = fec_decode_rsb(v, io, fio, rsb, offset, false);
+ if (r < 0) {
+ r = fec_decode_rsb(v, io, fio, rsb, offset, true);
+ if (r < 0)
+ return r;
+ }
+
+ if (dest)
+ memcpy(dest, fio->output, 1 << v->data_dev_block_bits);
+ else if (iter) {
+ fio->output_pos = 0;
+ r = verity_for_bv_block(v, io, iter, fec_bv_copy);
+ }
+
+ return r;
+}
+
+/*
+ * Clean up per-bio data.
+ */
+void verity_fec_finish_io(struct dm_verity_io *io)
+{
+ unsigned n;
+ struct dm_verity_fec *f = io->v->fec;
+ struct dm_verity_fec_io *fio = fec_io(io);
+
+ if (!verity_fec_is_enabled(io->v))
+ return;
+
+ mempool_free(fio->rs, f->rs_pool);
+
+ fec_for_each_prealloc_buffer(n)
+ mempool_free(fio->bufs[n], f->prealloc_pool);
+
+ fec_for_each_extra_buffer(fio, n)
+ mempool_free(fio->bufs[n], f->extra_pool);
+
+ mempool_free(fio->output, f->output_pool);
+}
+
+/*
+ * Initialize per-bio data.
+ */
+void verity_fec_init_io(struct dm_verity_io *io)
+{
+ struct dm_verity_fec_io *fio = fec_io(io);
+
+ if (!verity_fec_is_enabled(io->v))
+ return;
+
+ fio->rs = NULL;
+ memset(fio->bufs, 0, sizeof(fio->bufs));
+ fio->nbufs = 0;
+ fio->output = NULL;
+}
+
+/*
+ * Append feature arguments and values to the status table.
+ */
+unsigned verity_fec_status_table(struct dm_verity *v, unsigned sz,
+ char *result, unsigned maxlen)
+{
+ if (!verity_fec_is_enabled(v))
+ return sz;
+
+ DMEMIT(" " DM_VERITY_OPT_FEC_DEV " %s "
+ DM_VERITY_OPT_FEC_BLOCKS " %llu "
+ DM_VERITY_OPT_FEC_START " %llu "
+ DM_VERITY_OPT_FEC_ROOTS " %d",
+ v->fec->dev->name,
+ (unsigned long long)v->fec->blocks,
+ (unsigned long long)v->fec->start,
+ v->fec->roots);
+
+ return sz;
+}
+
+void verity_fec_dtr(struct dm_verity *v)
+{
+ struct dm_verity_fec *f = v->fec;
+
+ if (!verity_fec_is_enabled(v))
+ goto out;
+
+ mempool_destroy(f->rs_pool);
+ mempool_destroy(f->prealloc_pool);
+ mempool_destroy(f->extra_pool);
+ kmem_cache_destroy(f->cache);
+
+ if (f->data_bufio)
+ dm_bufio_client_destroy(f->data_bufio);
+ if (f->bufio)
+ dm_bufio_client_destroy(f->bufio);
+
+ if (f->dev)
+ dm_put_device(v->ti, f->dev);
+out:
+ kfree(f);
+ v->fec = NULL;
+}
+
+static void *fec_rs_alloc(gfp_t gfp_mask, void *pool_data)
+{
+ struct dm_verity *v = (struct dm_verity *)pool_data;
+
+ return init_rs(8, 0x11d, 0, 1, v->fec->roots);
+}
+
+static void fec_rs_free(void *element, void *pool_data)
+{
+ struct rs_control *rs = (struct rs_control *)element;
+
+ if (rs)
+ free_rs(rs);
+}
+
+bool verity_is_fec_opt_arg(const char *arg_name)
+{
+ return (!strcasecmp(arg_name, DM_VERITY_OPT_FEC_DEV) ||
+ !strcasecmp(arg_name, DM_VERITY_OPT_FEC_BLOCKS) ||
+ !strcasecmp(arg_name, DM_VERITY_OPT_FEC_START) ||
+ !strcasecmp(arg_name, DM_VERITY_OPT_FEC_ROOTS));
+}
+
+int verity_fec_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v,
+ unsigned *argc, const char *arg_name)
+{
+ int r;
+ struct dm_target *ti = v->ti;
+ const char *arg_value;
+ unsigned long long num_ll;
+ unsigned char num_c;
+ char dummy;
+
+ if (!*argc) {
+ ti->error = "FEC feature arguments require a value";
+ return -EINVAL;
+ }
+
+ arg_value = dm_shift_arg(as);
+ (*argc)--;
+
+ if (!strcasecmp(arg_name, DM_VERITY_OPT_FEC_DEV)) {
+ r = dm_get_device(ti, arg_value, FMODE_READ, &v->fec->dev);
+ if (r) {
+ ti->error = "FEC device lookup failed";
+ return r;
+ }
+
+ } else if (!strcasecmp(arg_name, DM_VERITY_OPT_FEC_BLOCKS)) {
+ if (sscanf(arg_value, "%llu%c", &num_ll, &dummy) != 1 ||
+ ((sector_t)(num_ll << (v->data_dev_block_bits - SECTOR_SHIFT))
+ >> (v->data_dev_block_bits - SECTOR_SHIFT) != num_ll)) {
+ ti->error = "Invalid " DM_VERITY_OPT_FEC_BLOCKS;
+ return -EINVAL;
+ }
+ v->fec->blocks = num_ll;
+
+ } else if (!strcasecmp(arg_name, DM_VERITY_OPT_FEC_START)) {
+ if (sscanf(arg_value, "%llu%c", &num_ll, &dummy) != 1 ||
+ ((sector_t)(num_ll << (v->data_dev_block_bits - SECTOR_SHIFT)) >>
+ (v->data_dev_block_bits - SECTOR_SHIFT) != num_ll)) {
+ ti->error = "Invalid " DM_VERITY_OPT_FEC_START;
+ return -EINVAL;
+ }
+ v->fec->start = num_ll;
+
+ } else if (!strcasecmp(arg_name, DM_VERITY_OPT_FEC_ROOTS)) {
+ if (sscanf(arg_value, "%hhu%c", &num_c, &dummy) != 1 || !num_c ||
+ num_c < (DM_VERITY_FEC_RSM - DM_VERITY_FEC_MAX_RSN) ||
+ num_c > (DM_VERITY_FEC_RSM - DM_VERITY_FEC_MIN_RSN)) {
+ ti->error = "Invalid " DM_VERITY_OPT_FEC_ROOTS;
+ return -EINVAL;
+ }
+ v->fec->roots = num_c;
+
+ } else {
+ ti->error = "Unrecognized verity FEC feature request";
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Allocate dm_verity_fec for v->fec. Must be called before verity_fec_ctr.
+ */
+int verity_fec_ctr_alloc(struct dm_verity *v)
+{
+ struct dm_verity_fec *f;
+
+ f = kzalloc(sizeof(struct dm_verity_fec), GFP_KERNEL);
+ if (!f) {
+ v->ti->error = "Cannot allocate FEC structure";
+ return -ENOMEM;
+ }
+ v->fec = f;
+
+ return 0;
+}
+
+/*
+ * Validate arguments and preallocate memory. Must be called after arguments
+ * have been parsed using verity_fec_parse_opt_args.
+ */
+int verity_fec_ctr(struct dm_verity *v)
+{
+ struct dm_verity_fec *f = v->fec;
+ struct dm_target *ti = v->ti;
+ u64 hash_blocks;
+
+ if (!verity_fec_is_enabled(v)) {
+ verity_fec_dtr(v);
+ return 0;
+ }
+
+ /*
+ * FEC is computed over data blocks, possible metadata, and
+ * hash blocks. In other words, FEC covers total of fec_blocks
+ * blocks consisting of the following:
+ *
+ * data blocks | hash blocks | metadata (optional)
+ *
+ * We allow metadata after hash blocks to support a use case
+ * where all data is stored on the same device and FEC covers
+ * the entire area.
+ *
+ * If metadata is included, we require it to be available on the
+ * hash device after the hash blocks.
+ */
+
+ hash_blocks = v->hash_blocks - v->hash_start;
+
+ /*
+ * Require matching block sizes for data and hash devices for
+ * simplicity.
+ */
+ if (v->data_dev_block_bits != v->hash_dev_block_bits) {
+ ti->error = "Block sizes must match to use FEC";
+ return -EINVAL;
+ }
+
+ if (!f->roots) {
+ ti->error = "Missing " DM_VERITY_OPT_FEC_ROOTS;
+ return -EINVAL;
+ }
+ f->rsn = DM_VERITY_FEC_RSM - f->roots;
+
+ if (!f->blocks) {
+ ti->error = "Missing " DM_VERITY_OPT_FEC_BLOCKS;
+ return -EINVAL;
+ }
+
+ f->rounds = f->blocks;
+ if (sector_div(f->rounds, f->rsn))
+ f->rounds++;
+
+ /*
+ * Due to optional metadata, f->blocks can be larger than
+ * data_blocks and hash_blocks combined.
+ */
+ if (f->blocks < v->data_blocks + hash_blocks || !f->rounds) {
+ ti->error = "Invalid " DM_VERITY_OPT_FEC_BLOCKS;
+ return -EINVAL;
+ }
+
+ /*
+ * Metadata is accessed through the hash device, so we require
+ * it to be large enough.
+ */
+ f->hash_blocks = f->blocks - v->data_blocks;
+ if (dm_bufio_get_device_size(v->bufio) < f->hash_blocks) {
+ ti->error = "Hash device is too small for "
+ DM_VERITY_OPT_FEC_BLOCKS;
+ return -E2BIG;
+ }
+
+ f->bufio = dm_bufio_client_create(f->dev->bdev,
+ 1 << v->data_dev_block_bits,
+ 1, 0, NULL, NULL);
+ if (IS_ERR(f->bufio)) {
+ ti->error = "Cannot initialize FEC bufio client";
+ return PTR_ERR(f->bufio);
+ }
+
+ if (dm_bufio_get_device_size(f->bufio) <
+ ((f->start + f->rounds * f->roots) >> v->data_dev_block_bits)) {
+ ti->error = "FEC device is too small";
+ return -E2BIG;
+ }
+
+ f->data_bufio = dm_bufio_client_create(v->data_dev->bdev,
+ 1 << v->data_dev_block_bits,
+ 1, 0, NULL, NULL);
+ if (IS_ERR(f->data_bufio)) {
+ ti->error = "Cannot initialize FEC data bufio client";
+ return PTR_ERR(f->data_bufio);
+ }
+
+ if (dm_bufio_get_device_size(f->data_bufio) < v->data_blocks) {
+ ti->error = "Data device is too small";
+ return -E2BIG;
+ }
+
+ /* Preallocate an rs_control structure for each worker thread */
+ f->rs_pool = mempool_create(num_online_cpus(), fec_rs_alloc,
+ fec_rs_free, (void *) v);
+ if (!f->rs_pool) {
+ ti->error = "Cannot allocate RS pool";
+ return -ENOMEM;
+ }
+
+ f->cache = kmem_cache_create("dm_verity_fec_buffers",
+ f->rsn << DM_VERITY_FEC_BUF_RS_BITS,
+ 0, 0, NULL);
+ if (!f->cache) {
+ ti->error = "Cannot create FEC buffer cache";
+ return -ENOMEM;
+ }
+
+ /* Preallocate DM_VERITY_FEC_BUF_PREALLOC buffers for each thread */
+ f->prealloc_pool = mempool_create_slab_pool(num_online_cpus() *
+ DM_VERITY_FEC_BUF_PREALLOC,
+ f->cache);
+ if (!f->prealloc_pool) {
+ ti->error = "Cannot allocate FEC buffer prealloc pool";
+ return -ENOMEM;
+ }
+
+ f->extra_pool = mempool_create_slab_pool(0, f->cache);
+ if (!f->extra_pool) {
+ ti->error = "Cannot allocate FEC buffer extra pool";
+ return -ENOMEM;
+ }
+
+ /* Preallocate an output buffer for each thread */
+ f->output_pool = mempool_create_kmalloc_pool(num_online_cpus(),
+ 1 << v->data_dev_block_bits);
+ if (!f->output_pool) {
+ ti->error = "Cannot allocate FEC output pool";
+ return -ENOMEM;
+ }
+
+ /* Reserve space for our per-bio data */
+ ti->per_bio_data_size += sizeof(struct dm_verity_fec_io);
+
+ return 0;
+}
diff --git a/drivers/md/dm-verity-fec.h b/drivers/md/dm-verity-fec.h
new file mode 100644
index 000000000000..7fa0298b995e
--- /dev/null
+++ b/drivers/md/dm-verity-fec.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Author: Sami Tolvanen <samitolvanen@google.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 DM_VERITY_FEC_H
+#define DM_VERITY_FEC_H
+
+#include "dm-verity.h"
+#include <linux/rslib.h>
+
+/* Reed-Solomon(M, N) parameters */
+#define DM_VERITY_FEC_RSM 255
+#define DM_VERITY_FEC_MAX_RSN 253
+#define DM_VERITY_FEC_MIN_RSN 231 /* ~10% space overhead */
+
+/* buffers for deinterleaving and decoding */
+#define DM_VERITY_FEC_BUF_PREALLOC 1 /* buffers to preallocate */
+#define DM_VERITY_FEC_BUF_RS_BITS 4 /* 1 << RS blocks per buffer */
+/* we need buffers for at most 1 << block size RS blocks */
+#define DM_VERITY_FEC_BUF_MAX \
+ (1 << (PAGE_SHIFT - DM_VERITY_FEC_BUF_RS_BITS))
+
+#define DM_VERITY_OPT_FEC_DEV "use_fec_from_device"
+#define DM_VERITY_OPT_FEC_BLOCKS "fec_blocks"
+#define DM_VERITY_OPT_FEC_START "fec_start"
+#define DM_VERITY_OPT_FEC_ROOTS "fec_roots"
+
+/* configuration */
+struct dm_verity_fec {
+ struct dm_dev *dev; /* parity data device */
+ struct dm_bufio_client *data_bufio; /* for data dev access */
+ struct dm_bufio_client *bufio; /* for parity data access */
+ sector_t start; /* parity data start in blocks */
+ sector_t blocks; /* number of blocks covered */
+ sector_t rounds; /* number of interleaving rounds */
+ sector_t hash_blocks; /* blocks covered after v->hash_start */
+ unsigned char roots; /* number of parity bytes, M-N of RS(M, N) */
+ unsigned char rsn; /* N of RS(M, N) */
+ mempool_t *rs_pool; /* mempool for fio->rs */
+ mempool_t *prealloc_pool; /* mempool for preallocated buffers */
+ mempool_t *extra_pool; /* mempool for extra buffers */
+ mempool_t *output_pool; /* mempool for output */
+ struct kmem_cache *cache; /* cache for buffers */
+};
+
+/* per-bio data */
+struct dm_verity_fec_io {
+ struct rs_control *rs; /* Reed-Solomon state */
+ int erasures[DM_VERITY_FEC_MAX_RSN]; /* erasures for decode_rs8 */
+ u8 *bufs[DM_VERITY_FEC_BUF_MAX]; /* bufs for deinterleaving */
+ unsigned nbufs; /* number of buffers allocated */
+ u8 *output; /* buffer for corrected output */
+ size_t output_pos;
+};
+
+#ifdef CONFIG_DM_VERITY_FEC
+
+/* each feature parameter requires a value */
+#define DM_VERITY_OPTS_FEC 8
+
+extern bool verity_fec_is_enabled(struct dm_verity *v);
+
+extern int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
+ enum verity_block_type type, sector_t block,
+ u8 *dest, struct bvec_iter *iter);
+
+extern unsigned verity_fec_status_table(struct dm_verity *v, unsigned sz,
+ char *result, unsigned maxlen);
+
+extern void verity_fec_finish_io(struct dm_verity_io *io);
+extern void verity_fec_init_io(struct dm_verity_io *io);
+
+extern bool verity_is_fec_opt_arg(const char *arg_name);
+extern int verity_fec_parse_opt_args(struct dm_arg_set *as,
+ struct dm_verity *v, unsigned *argc,
+ const char *arg_name);
+
+extern void verity_fec_dtr(struct dm_verity *v);
+
+extern int verity_fec_ctr_alloc(struct dm_verity *v);
+extern int verity_fec_ctr(struct dm_verity *v);
+
+#else /* !CONFIG_DM_VERITY_FEC */
+
+#define DM_VERITY_OPTS_FEC 0
+
+static inline bool verity_fec_is_enabled(struct dm_verity *v)
+{
+ return false;
+}
+
+static inline int verity_fec_decode(struct dm_verity *v,
+ struct dm_verity_io *io,
+ enum verity_block_type type,
+ sector_t block, u8 *dest,
+ struct bvec_iter *iter)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline unsigned verity_fec_status_table(struct dm_verity *v,
+ unsigned sz, char *result,
+ unsigned maxlen)
+{
+ return sz;
+}
+
+static inline void verity_fec_finish_io(struct dm_verity_io *io)
+{
+}
+
+static inline void verity_fec_init_io(struct dm_verity_io *io)
+{
+}
+
+static inline bool verity_is_fec_opt_arg(const char *arg_name)
+{
+ return false;
+}
+
+static inline int verity_fec_parse_opt_args(struct dm_arg_set *as,
+ struct dm_verity *v,
+ unsigned *argc,
+ const char *arg_name)
+{
+ return -EINVAL;
+}
+
+static inline void verity_fec_dtr(struct dm_verity *v)
+{
+}
+
+static inline int verity_fec_ctr_alloc(struct dm_verity *v)
+{
+ return 0;
+}
+
+static inline int verity_fec_ctr(struct dm_verity *v)
+{
+ return 0;
+}
+
+#endif /* CONFIG_DM_VERITY_FEC */
+
+#endif /* DM_VERITY_FEC_H */
diff --git a/drivers/md/dm-verity.c b/drivers/md/dm-verity-target.c
index ccf41886ebcf..5c5d30cb6ec5 100644
--- a/drivers/md/dm-verity.c
+++ b/drivers/md/dm-verity-target.c
@@ -14,12 +14,11 @@
* access behavior.
*/
-#include "dm-bufio.h"
+#include "dm-verity.h"
+#include "dm-verity-fec.h"
#include <linux/module.h>
-#include <linux/device-mapper.h>
#include <linux/reboot.h>
-#include <crypto/hash.h>
#define DM_MSG_PREFIX "verity"
@@ -28,83 +27,18 @@
#define DM_VERITY_DEFAULT_PREFETCH_SIZE 262144
-#define DM_VERITY_MAX_LEVELS 63
#define DM_VERITY_MAX_CORRUPTED_ERRS 100
#define DM_VERITY_OPT_LOGGING "ignore_corruption"
#define DM_VERITY_OPT_RESTART "restart_on_corruption"
+#define DM_VERITY_OPT_IGN_ZEROES "ignore_zero_blocks"
+
+#define DM_VERITY_OPTS_MAX (2 + DM_VERITY_OPTS_FEC)
static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE;
module_param_named(prefetch_cluster, dm_verity_prefetch_cluster, uint, S_IRUGO | S_IWUSR);
-enum verity_mode {
- DM_VERITY_MODE_EIO,
- DM_VERITY_MODE_LOGGING,
- DM_VERITY_MODE_RESTART
-};
-
-enum verity_block_type {
- DM_VERITY_BLOCK_TYPE_DATA,
- DM_VERITY_BLOCK_TYPE_METADATA
-};
-
-struct dm_verity {
- struct dm_dev *data_dev;
- struct dm_dev *hash_dev;
- struct dm_target *ti;
- struct dm_bufio_client *bufio;
- char *alg_name;
- struct crypto_shash *tfm;
- u8 *root_digest; /* digest of the root block */
- u8 *salt; /* salt: its size is salt_size */
- unsigned salt_size;
- sector_t data_start; /* data offset in 512-byte sectors */
- sector_t hash_start; /* hash start in blocks */
- sector_t data_blocks; /* the number of data blocks */
- sector_t hash_blocks; /* the number of hash blocks */
- unsigned char data_dev_block_bits; /* log2(data blocksize) */
- unsigned char hash_dev_block_bits; /* log2(hash blocksize) */
- unsigned char hash_per_block_bits; /* log2(hashes in hash block) */
- unsigned char levels; /* the number of tree levels */
- unsigned char version;
- unsigned digest_size; /* digest size for the current hash algorithm */
- unsigned shash_descsize;/* the size of temporary space for crypto */
- int hash_failed; /* set to 1 if hash of any block failed */
- enum verity_mode mode; /* mode for handling verification errors */
- unsigned corrupted_errs;/* Number of errors for corrupted blocks */
-
- struct workqueue_struct *verify_wq;
-
- /* starting blocks for each tree level. 0 is the lowest level. */
- sector_t hash_level_block[DM_VERITY_MAX_LEVELS];
-};
-
-struct dm_verity_io {
- struct dm_verity *v;
-
- /* original values of bio->bi_end_io and bio->bi_private */
- bio_end_io_t *orig_bi_end_io;
- void *orig_bi_private;
-
- sector_t block;
- unsigned n_blocks;
-
- struct bvec_iter iter;
-
- struct work_struct work;
-
- /*
- * Three variably-size fields follow this struct:
- *
- * u8 hash_desc[v->shash_descsize];
- * u8 real_digest[v->digest_size];
- * u8 want_digest[v->digest_size];
- *
- * To access them use: io_hash_desc(), io_real_digest() and io_want_digest().
- */
-};
-
struct dm_verity_prefetch_work {
struct work_struct work;
struct dm_verity *v;
@@ -112,21 +46,6 @@ struct dm_verity_prefetch_work {
unsigned n_blocks;
};
-static struct shash_desc *io_hash_desc(struct dm_verity *v, struct dm_verity_io *io)
-{
- return (struct shash_desc *)(io + 1);
-}
-
-static u8 *io_real_digest(struct dm_verity *v, struct dm_verity_io *io)
-{
- return (u8 *)(io + 1) + v->shash_descsize;
-}
-
-static u8 *io_want_digest(struct dm_verity *v, struct dm_verity_io *io)
-{
- return (u8 *)(io + 1) + v->shash_descsize + v->digest_size;
-}
-
/*
* Auxiliary structure appended to each dm-bufio buffer. If the value
* hash_verified is nonzero, hash of the block has been verified.
@@ -173,6 +92,84 @@ static sector_t verity_position_at_level(struct dm_verity *v, sector_t block,
return block >> (level * v->hash_per_block_bits);
}
+/*
+ * Wrapper for crypto_shash_init, which handles verity salting.
+ */
+static int verity_hash_init(struct dm_verity *v, struct shash_desc *desc)
+{
+ int r;
+
+ desc->tfm = v->tfm;
+ desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+ r = crypto_shash_init(desc);
+
+ if (unlikely(r < 0)) {
+ DMERR("crypto_shash_init failed: %d", r);
+ return r;
+ }
+
+ if (likely(v->version >= 1)) {
+ r = crypto_shash_update(desc, v->salt, v->salt_size);
+
+ if (unlikely(r < 0)) {
+ DMERR("crypto_shash_update failed: %d", r);
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+static int verity_hash_update(struct dm_verity *v, struct shash_desc *desc,
+ const u8 *data, size_t len)
+{
+ int r = crypto_shash_update(desc, data, len);
+
+ if (unlikely(r < 0))
+ DMERR("crypto_shash_update failed: %d", r);
+
+ return r;
+}
+
+static int verity_hash_final(struct dm_verity *v, struct shash_desc *desc,
+ u8 *digest)
+{
+ int r;
+
+ if (unlikely(!v->version)) {
+ r = crypto_shash_update(desc, v->salt, v->salt_size);
+
+ if (r < 0) {
+ DMERR("crypto_shash_update failed: %d", r);
+ return r;
+ }
+ }
+
+ r = crypto_shash_final(desc, digest);
+
+ if (unlikely(r < 0))
+ DMERR("crypto_shash_final failed: %d", r);
+
+ return r;
+}
+
+int verity_hash(struct dm_verity *v, struct shash_desc *desc,
+ const u8 *data, size_t len, u8 *digest)
+{
+ int r;
+
+ r = verity_hash_init(v, desc);
+ if (unlikely(r < 0))
+ return r;
+
+ r = verity_hash_update(v, desc, data, len);
+ if (unlikely(r < 0))
+ return r;
+
+ return verity_hash_final(v, desc, digest);
+}
+
static void verity_hash_at_level(struct dm_verity *v, sector_t block, int level,
sector_t *hash_block, unsigned *offset)
{
@@ -246,17 +243,17 @@ out:
* Verify hash of a metadata block pertaining to the specified data block
* ("block" argument) at a specified level ("level" argument).
*
- * On successful return, io_want_digest(v, io) contains the hash value for
- * a lower tree level or for the data block (if we're at the lowest leve).
+ * On successful return, verity_io_want_digest(v, io) contains the hash value
+ * for a lower tree level or for the data block (if we're at the lowest level).
*
* If "skip_unverified" is true, unverified buffer is skipped and 1 is returned.
* If "skip_unverified" is false, unverified buffer is hashed and verified
- * against current value of io_want_digest(v, io).
+ * against current value of verity_io_want_digest(v, io).
*/
-static int verity_verify_level(struct dm_verity_io *io, sector_t block,
- int level, bool skip_unverified)
+static int verity_verify_level(struct dm_verity *v, struct dm_verity_io *io,
+ sector_t block, int level, bool skip_unverified,
+ u8 *want_digest)
{
- struct dm_verity *v = io->v;
struct dm_buffer *buf;
struct buffer_aux *aux;
u8 *data;
@@ -273,72 +270,128 @@ static int verity_verify_level(struct dm_verity_io *io, sector_t block,
aux = dm_bufio_get_aux_data(buf);
if (!aux->hash_verified) {
- struct shash_desc *desc;
- u8 *result;
-
if (skip_unverified) {
r = 1;
goto release_ret_r;
}
- desc = io_hash_desc(v, io);
- desc->tfm = v->tfm;
- desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
- r = crypto_shash_init(desc);
- if (r < 0) {
- DMERR("crypto_shash_init failed: %d", r);
+ r = verity_hash(v, verity_io_hash_desc(v, io),
+ data, 1 << v->hash_dev_block_bits,
+ verity_io_real_digest(v, io));
+ if (unlikely(r < 0))
goto release_ret_r;
- }
-
- if (likely(v->version >= 1)) {
- r = crypto_shash_update(desc, v->salt, v->salt_size);
- if (r < 0) {
- DMERR("crypto_shash_update failed: %d", r);
- goto release_ret_r;
- }
- }
- r = crypto_shash_update(desc, data, 1 << v->hash_dev_block_bits);
- if (r < 0) {
- DMERR("crypto_shash_update failed: %d", r);
+ if (likely(memcmp(verity_io_real_digest(v, io), want_digest,
+ v->digest_size) == 0))
+ aux->hash_verified = 1;
+ else if (verity_fec_decode(v, io,
+ DM_VERITY_BLOCK_TYPE_METADATA,
+ hash_block, data, NULL) == 0)
+ aux->hash_verified = 1;
+ else if (verity_handle_err(v,
+ DM_VERITY_BLOCK_TYPE_METADATA,
+ hash_block)) {
+ r = -EIO;
goto release_ret_r;
}
+ }
- if (!v->version) {
- r = crypto_shash_update(desc, v->salt, v->salt_size);
- if (r < 0) {
- DMERR("crypto_shash_update failed: %d", r);
- goto release_ret_r;
- }
- }
+ data += offset;
+ memcpy(want_digest, data, v->digest_size);
+ r = 0;
- result = io_real_digest(v, io);
- r = crypto_shash_final(desc, result);
- if (r < 0) {
- DMERR("crypto_shash_final failed: %d", r);
- goto release_ret_r;
- }
- if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
- if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_METADATA,
- hash_block)) {
- r = -EIO;
- goto release_ret_r;
- }
- } else
- aux->hash_verified = 1;
+release_ret_r:
+ dm_bufio_release(buf);
+ return r;
+}
+
+/*
+ * Find a hash for a given block, write it to digest and verify the integrity
+ * of the hash tree if necessary.
+ */
+int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
+ sector_t block, u8 *digest, bool *is_zero)
+{
+ int r = 0, i;
+
+ if (likely(v->levels)) {
+ /*
+ * First, we try to get the requested hash for
+ * the current block. If the hash block itself is
+ * verified, zero is returned. If it isn't, this
+ * function returns 1 and we fall back to whole
+ * chain verification.
+ */
+ r = verity_verify_level(v, io, block, 0, true, digest);
+ if (likely(r <= 0))
+ goto out;
}
- data += offset;
+ memcpy(digest, v->root_digest, v->digest_size);
- memcpy(io_want_digest(v, io), data, v->digest_size);
+ for (i = v->levels - 1; i >= 0; i--) {
+ r = verity_verify_level(v, io, block, i, false, digest);
+ if (unlikely(r))
+ goto out;
+ }
+out:
+ if (!r && v->zero_digest)
+ *is_zero = !memcmp(v->zero_digest, digest, v->digest_size);
+ else
+ *is_zero = false;
+
+ return r;
+}
+
+/*
+ * Calls function process for 1 << v->data_dev_block_bits bytes in the bio_vec
+ * starting from iter.
+ */
+int verity_for_bv_block(struct dm_verity *v, struct dm_verity_io *io,
+ struct bvec_iter *iter,
+ int (*process)(struct dm_verity *v,
+ struct dm_verity_io *io, u8 *data,
+ size_t len))
+{
+ unsigned todo = 1 << v->data_dev_block_bits;
+ struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_bio_data_size);
+
+ do {
+ int r;
+ u8 *page;
+ unsigned len;
+ struct bio_vec bv = bio_iter_iovec(bio, *iter);
+
+ page = kmap_atomic(bv.bv_page);
+ len = bv.bv_len;
+
+ if (likely(len >= todo))
+ len = todo;
+
+ r = process(v, io, page + bv.bv_offset, len);
+ kunmap_atomic(page);
+
+ if (r < 0)
+ return r;
+
+ bio_advance_iter(bio, iter, len);
+ todo -= len;
+ } while (todo);
- dm_bufio_release(buf);
return 0;
+}
-release_ret_r:
- dm_bufio_release(buf);
+static int verity_bv_hash_update(struct dm_verity *v, struct dm_verity_io *io,
+ u8 *data, size_t len)
+{
+ return verity_hash_update(v, verity_io_hash_desc(v, io), data, len);
+}
- return r;
+static int verity_bv_zero(struct dm_verity *v, struct dm_verity_io *io,
+ u8 *data, size_t len)
+{
+ memset(data, 0, len);
+ return 0;
}
/*
@@ -346,99 +399,56 @@ release_ret_r:
*/
static int verity_verify_io(struct dm_verity_io *io)
{
+ bool is_zero;
struct dm_verity *v = io->v;
- struct bio *bio = dm_bio_from_per_bio_data(io,
- v->ti->per_bio_data_size);
+ struct bvec_iter start;
unsigned b;
- int i;
for (b = 0; b < io->n_blocks; b++) {
- struct shash_desc *desc;
- u8 *result;
int r;
- unsigned todo;
+ struct shash_desc *desc = verity_io_hash_desc(v, io);
+
+ r = verity_hash_for_block(v, io, io->block + b,
+ verity_io_want_digest(v, io),
+ &is_zero);
+ if (unlikely(r < 0))
+ return r;
- if (likely(v->levels)) {
+ if (is_zero) {
/*
- * First, we try to get the requested hash for
- * the current block. If the hash block itself is
- * verified, zero is returned. If it isn't, this
- * function returns 0 and we fall back to whole
- * chain verification.
+ * If we expect a zero block, don't validate, just
+ * return zeros.
*/
- int r = verity_verify_level(io, io->block + b, 0, true);
- if (likely(!r))
- goto test_block_hash;
- if (r < 0)
+ r = verity_for_bv_block(v, io, &io->iter,
+ verity_bv_zero);
+ if (unlikely(r < 0))
return r;
- }
- memcpy(io_want_digest(v, io), v->root_digest, v->digest_size);
-
- for (i = v->levels - 1; i >= 0; i--) {
- int r = verity_verify_level(io, io->block + b, i, false);
- if (unlikely(r))
- return r;
+ continue;
}
-test_block_hash:
- desc = io_hash_desc(v, io);
- desc->tfm = v->tfm;
- desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
- r = crypto_shash_init(desc);
- if (r < 0) {
- DMERR("crypto_shash_init failed: %d", r);
+ r = verity_hash_init(v, desc);
+ if (unlikely(r < 0))
return r;
- }
-
- if (likely(v->version >= 1)) {
- r = crypto_shash_update(desc, v->salt, v->salt_size);
- if (r < 0) {
- DMERR("crypto_shash_update failed: %d", r);
- return r;
- }
- }
- todo = 1 << v->data_dev_block_bits;
- do {
- u8 *page;
- unsigned len;
- struct bio_vec bv = bio_iter_iovec(bio, io->iter);
-
- page = kmap_atomic(bv.bv_page);
- len = bv.bv_len;
- if (likely(len >= todo))
- len = todo;
- r = crypto_shash_update(desc, page + bv.bv_offset, len);
- kunmap_atomic(page);
-
- if (r < 0) {
- DMERR("crypto_shash_update failed: %d", r);
- return r;
- }
-
- bio_advance_iter(bio, &io->iter, len);
- todo -= len;
- } while (todo);
- if (!v->version) {
- r = crypto_shash_update(desc, v->salt, v->salt_size);
- if (r < 0) {
- DMERR("crypto_shash_update failed: %d", r);
- return r;
- }
- }
+ start = io->iter;
+ r = verity_for_bv_block(v, io, &io->iter, verity_bv_hash_update);
+ if (unlikely(r < 0))
+ return r;
- result = io_real_digest(v, io);
- r = crypto_shash_final(desc, result);
- if (r < 0) {
- DMERR("crypto_shash_final failed: %d", r);
+ r = verity_hash_final(v, desc, verity_io_real_digest(v, io));
+ if (unlikely(r < 0))
return r;
- }
- if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
- if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA,
- io->block + b))
- return -EIO;
- }
+
+ if (likely(memcmp(verity_io_real_digest(v, io),
+ verity_io_want_digest(v, io), v->digest_size) == 0))
+ continue;
+ else if (verity_fec_decode(v, io, DM_VERITY_BLOCK_TYPE_DATA,
+ io->block + b, NULL, &start) == 0)
+ continue;
+ else if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA,
+ io->block + b))
+ return -EIO;
}
return 0;
@@ -453,9 +463,10 @@ static void verity_finish_io(struct dm_verity_io *io, int error)
struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_bio_data_size);
bio->bi_end_io = io->orig_bi_end_io;
- bio->bi_private = io->orig_bi_private;
bio->bi_error = error;
+ verity_fec_finish_io(io);
+
bio_endio(bio);
}
@@ -470,7 +481,7 @@ static void verity_end_io(struct bio *bio)
{
struct dm_verity_io *io = bio->bi_private;
- if (bio->bi_error) {
+ if (bio->bi_error && !verity_fec_is_enabled(io->v)) {
verity_finish_io(io, bio->bi_error);
return;
}
@@ -566,7 +577,6 @@ static int verity_map(struct dm_target *ti, struct bio *bio)
io = dm_per_bio_data(bio, ti->per_bio_data_size);
io->v = v;
io->orig_bi_end_io = bio->bi_end_io;
- io->orig_bi_private = bio->bi_private;
io->block = bio->bi_iter.bi_sector >> (v->data_dev_block_bits - SECTOR_SHIFT);
io->n_blocks = bio->bi_iter.bi_size >> v->data_dev_block_bits;
@@ -574,6 +584,8 @@ static int verity_map(struct dm_target *ti, struct bio *bio)
bio->bi_private = io;
io->iter = bio->bi_iter;
+ verity_fec_init_io(io);
+
verity_submit_prefetch(v, io);
generic_make_request(bio);
@@ -588,6 +600,7 @@ static void verity_status(struct dm_target *ti, status_type_t type,
unsigned status_flags, char *result, unsigned maxlen)
{
struct dm_verity *v = ti->private;
+ unsigned args = 0;
unsigned sz = 0;
unsigned x;
@@ -614,8 +627,17 @@ static void verity_status(struct dm_target *ti, status_type_t type,
else
for (x = 0; x < v->salt_size; x++)
DMEMIT("%02x", v->salt[x]);
+ if (v->mode != DM_VERITY_MODE_EIO)
+ args++;
+ if (verity_fec_is_enabled(v))
+ args += DM_VERITY_OPTS_FEC;
+ if (v->zero_digest)
+ args++;
+ if (!args)
+ return;
+ DMEMIT(" %u", args);
if (v->mode != DM_VERITY_MODE_EIO) {
- DMEMIT(" 1 ");
+ DMEMIT(" ");
switch (v->mode) {
case DM_VERITY_MODE_LOGGING:
DMEMIT(DM_VERITY_OPT_LOGGING);
@@ -627,6 +649,9 @@ static void verity_status(struct dm_target *ti, status_type_t type,
BUG();
}
}
+ if (v->zero_digest)
+ DMEMIT(" " DM_VERITY_OPT_IGN_ZEROES);
+ sz = verity_fec_status_table(v, sz, result, maxlen);
break;
}
}
@@ -677,6 +702,7 @@ static void verity_dtr(struct dm_target *ti)
kfree(v->salt);
kfree(v->root_digest);
+ kfree(v->zero_digest);
if (v->tfm)
crypto_free_shash(v->tfm);
@@ -689,9 +715,94 @@ static void verity_dtr(struct dm_target *ti)
if (v->data_dev)
dm_put_device(ti, v->data_dev);
+ verity_fec_dtr(v);
+
kfree(v);
}
+static int verity_alloc_zero_digest(struct dm_verity *v)
+{
+ int r = -ENOMEM;
+ struct shash_desc *desc;
+ u8 *zero_data;
+
+ v->zero_digest = kmalloc(v->digest_size, GFP_KERNEL);
+
+ if (!v->zero_digest)
+ return r;
+
+ desc = kmalloc(v->shash_descsize, GFP_KERNEL);
+
+ if (!desc)
+ return r; /* verity_dtr will free zero_digest */
+
+ zero_data = kzalloc(1 << v->data_dev_block_bits, GFP_KERNEL);
+
+ if (!zero_data)
+ goto out;
+
+ r = verity_hash(v, desc, zero_data, 1 << v->data_dev_block_bits,
+ v->zero_digest);
+
+out:
+ kfree(desc);
+ kfree(zero_data);
+
+ return r;
+}
+
+static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
+{
+ int r;
+ unsigned argc;
+ struct dm_target *ti = v->ti;
+ const char *arg_name;
+
+ static struct dm_arg _args[] = {
+ {0, DM_VERITY_OPTS_MAX, "Invalid number of feature args"},
+ };
+
+ r = dm_read_arg_group(_args, as, &argc, &ti->error);
+ if (r)
+ return -EINVAL;
+
+ if (!argc)
+ return 0;
+
+ do {
+ arg_name = dm_shift_arg(as);
+ argc--;
+
+ if (!strcasecmp(arg_name, DM_VERITY_OPT_LOGGING)) {
+ v->mode = DM_VERITY_MODE_LOGGING;
+ continue;
+
+ } else if (!strcasecmp(arg_name, DM_VERITY_OPT_RESTART)) {
+ v->mode = DM_VERITY_MODE_RESTART;
+ continue;
+
+ } else if (!strcasecmp(arg_name, DM_VERITY_OPT_IGN_ZEROES)) {
+ r = verity_alloc_zero_digest(v);
+ if (r) {
+ ti->error = "Cannot allocate zero digest";
+ return r;
+ }
+ continue;
+
+ } else if (verity_is_fec_opt_arg(arg_name)) {
+ r = verity_fec_parse_opt_args(as, v, &argc, arg_name);
+ if (r)
+ return r;
+ continue;
+ }
+
+ ti->error = "Unrecognized verity feature request";
+ return -EINVAL;
+ } while (argc && !r);
+
+ return r;
+}
+
/*
* Target parameters:
* <version> The current format is version 1.
@@ -710,18 +821,13 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
{
struct dm_verity *v;
struct dm_arg_set as;
- const char *opt_string;
- unsigned int num, opt_params;
+ unsigned int num;
unsigned long long num_ll;
int r;
int i;
sector_t hash_position;
char dummy;
- static struct dm_arg _args[] = {
- {0, 1, "Invalid number of feature args"},
- };
-
v = kzalloc(sizeof(struct dm_verity), GFP_KERNEL);
if (!v) {
ti->error = "Cannot allocate verity structure";
@@ -730,6 +836,10 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
ti->private = v;
v->ti = ti;
+ r = verity_fec_ctr_alloc(v);
+ if (r)
+ goto bad;
+
if ((dm_table_get_mode(ti->table) & ~FMODE_READ)) {
ti->error = "Device must be readonly";
r = -EINVAL;
@@ -866,29 +976,9 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
as.argc = argc;
as.argv = argv;
- r = dm_read_arg_group(_args, &as, &opt_params, &ti->error);
- if (r)
+ r = verity_parse_opt_args(&as, v);
+ if (r < 0)
goto bad;
-
- while (opt_params) {
- opt_params--;
- opt_string = dm_shift_arg(&as);
- if (!opt_string) {
- ti->error = "Not enough feature arguments";
- r = -EINVAL;
- goto bad;
- }
-
- if (!strcasecmp(opt_string, DM_VERITY_OPT_LOGGING))
- v->mode = DM_VERITY_MODE_LOGGING;
- else if (!strcasecmp(opt_string, DM_VERITY_OPT_RESTART))
- v->mode = DM_VERITY_MODE_RESTART;
- else {
- ti->error = "Invalid feature arguments";
- r = -EINVAL;
- goto bad;
- }
- }
}
v->hash_per_block_bits =
@@ -938,8 +1028,6 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
goto bad;
}
- ti->per_bio_data_size = roundup(sizeof(struct dm_verity_io) + v->shash_descsize + v->digest_size * 2, __alignof__(struct dm_verity_io));
-
/* WQ_UNBOUND greatly improves performance when running on ramdisk */
v->verify_wq = alloc_workqueue("kverityd", WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM | WQ_UNBOUND, num_online_cpus());
if (!v->verify_wq) {
@@ -948,6 +1036,16 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
goto bad;
}
+ ti->per_bio_data_size = sizeof(struct dm_verity_io) +
+ v->shash_descsize + v->digest_size * 2;
+
+ r = verity_fec_ctr(v);
+ if (r)
+ goto bad;
+
+ ti->per_bio_data_size = roundup(ti->per_bio_data_size,
+ __alignof__(struct dm_verity_io));
+
return 0;
bad:
@@ -958,7 +1056,7 @@ bad:
static struct target_type verity_target = {
.name = "verity",
- .version = {1, 2, 0},
+ .version = {1, 3, 0},
.module = THIS_MODULE,
.ctr = verity_ctr,
.dtr = verity_dtr,
diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h
new file mode 100644
index 000000000000..fb419f422d73
--- /dev/null
+++ b/drivers/md/dm-verity.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc.
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * Author: Mikulas Patocka <mpatocka@redhat.com>
+ *
+ * Based on Chromium dm-verity driver (C) 2011 The Chromium OS Authors
+ *
+ * This file is released under the GPLv2.
+ */
+
+#ifndef DM_VERITY_H
+#define DM_VERITY_H
+
+#include "dm-bufio.h"
+#include <linux/device-mapper.h>
+#include <crypto/hash.h>
+
+#define DM_VERITY_MAX_LEVELS 63
+
+enum verity_mode {
+ DM_VERITY_MODE_EIO,
+ DM_VERITY_MODE_LOGGING,
+ DM_VERITY_MODE_RESTART
+};
+
+enum verity_block_type {
+ DM_VERITY_BLOCK_TYPE_DATA,
+ DM_VERITY_BLOCK_TYPE_METADATA
+};
+
+struct dm_verity_fec;
+
+struct dm_verity {
+ struct dm_dev *data_dev;
+ struct dm_dev *hash_dev;
+ struct dm_target *ti;
+ struct dm_bufio_client *bufio;
+ char *alg_name;
+ struct crypto_shash *tfm;
+ u8 *root_digest; /* digest of the root block */
+ u8 *salt; /* salt: its size is salt_size */
+ u8 *zero_digest; /* digest for a zero block */
+ unsigned salt_size;
+ sector_t data_start; /* data offset in 512-byte sectors */
+ sector_t hash_start; /* hash start in blocks */
+ sector_t data_blocks; /* the number of data blocks */
+ sector_t hash_blocks; /* the number of hash blocks */
+ unsigned char data_dev_block_bits; /* log2(data blocksize) */
+ unsigned char hash_dev_block_bits; /* log2(hash blocksize) */
+ unsigned char hash_per_block_bits; /* log2(hashes in hash block) */
+ unsigned char levels; /* the number of tree levels */
+ unsigned char version;
+ unsigned digest_size; /* digest size for the current hash algorithm */
+ unsigned shash_descsize;/* the size of temporary space for crypto */
+ int hash_failed; /* set to 1 if hash of any block failed */
+ enum verity_mode mode; /* mode for handling verification errors */
+ unsigned corrupted_errs;/* Number of errors for corrupted blocks */
+
+ struct workqueue_struct *verify_wq;
+
+ /* starting blocks for each tree level. 0 is the lowest level. */
+ sector_t hash_level_block[DM_VERITY_MAX_LEVELS];
+
+ struct dm_verity_fec *fec; /* forward error correction */
+};
+
+struct dm_verity_io {
+ struct dm_verity *v;
+
+ /* original value of bio->bi_end_io */
+ bio_end_io_t *orig_bi_end_io;
+
+ sector_t block;
+ unsigned n_blocks;
+
+ struct bvec_iter iter;
+
+ struct work_struct work;
+
+ /*
+ * Three variably-size fields follow this struct:
+ *
+ * u8 hash_desc[v->shash_descsize];
+ * u8 real_digest[v->digest_size];
+ * u8 want_digest[v->digest_size];
+ *
+ * To access them use: verity_io_hash_desc(), verity_io_real_digest()
+ * and verity_io_want_digest().
+ */
+};
+
+static inline struct shash_desc *verity_io_hash_desc(struct dm_verity *v,
+ struct dm_verity_io *io)
+{
+ return (struct shash_desc *)(io + 1);
+}
+
+static inline u8 *verity_io_real_digest(struct dm_verity *v,
+ struct dm_verity_io *io)
+{
+ return (u8 *)(io + 1) + v->shash_descsize;
+}
+
+static inline u8 *verity_io_want_digest(struct dm_verity *v,
+ struct dm_verity_io *io)
+{
+ return (u8 *)(io + 1) + v->shash_descsize + v->digest_size;
+}
+
+static inline u8 *verity_io_digest_end(struct dm_verity *v,
+ struct dm_verity_io *io)
+{
+ return verity_io_want_digest(v, io) + v->digest_size;
+}
+
+extern int verity_for_bv_block(struct dm_verity *v, struct dm_verity_io *io,
+ struct bvec_iter *iter,
+ int (*process)(struct dm_verity *v,
+ struct dm_verity_io *io,
+ u8 *data, size_t len));
+
+extern int verity_hash(struct dm_verity *v, struct shash_desc *desc,
+ const u8 *data, size_t len, u8 *digest);
+
+extern int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
+ sector_t block, u8 *digest, bool *is_zero);
+
+#endif /* DM_VERITY_H */
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 6e15f3565892..5df40480228b 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -591,7 +591,7 @@ retry:
out:
dm_put_live_table(md, *srcu_idx);
- if (r == -ENOTCONN) {
+ if (r == -ENOTCONN && !fatal_signal_pending(current)) {
msleep(10);
goto retry;
}
@@ -603,9 +603,10 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
{
struct mapped_device *md = bdev->bd_disk->private_data;
struct dm_target *tgt;
+ struct block_device *tgt_bdev = NULL;
int srcu_idx, r;
- r = dm_get_live_table_for_ioctl(md, &tgt, &bdev, &mode, &srcu_idx);
+ r = dm_get_live_table_for_ioctl(md, &tgt, &tgt_bdev, &mode, &srcu_idx);
if (r < 0)
return r;
@@ -620,7 +621,7 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
goto out;
}
- r = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
+ r = __blkdev_driver_ioctl(tgt_bdev, mode, cmd, arg);
out:
dm_put_live_table(md, srcu_idx);
return r;
diff --git a/drivers/md/md-cluster.c b/drivers/md/md-cluster.c
index d6a1126d85ce..0ded8e97751d 100644
--- a/drivers/md/md-cluster.c
+++ b/drivers/md/md-cluster.c
@@ -48,13 +48,29 @@ struct resync_info {
#define MD_CLUSTER_SUSPEND_READ_BALANCING 2
#define MD_CLUSTER_BEGIN_JOIN_CLUSTER 3
+/* Lock the send communication. This is done through
+ * bit manipulation as opposed to a mutex in order to
+ * accomodate lock and hold. See next comment.
+ */
+#define MD_CLUSTER_SEND_LOCK 4
+/* If cluster operations (such as adding a disk) must lock the
+ * communication channel, so as to perform extra operations
+ * (update metadata) and no other operation is allowed on the
+ * MD. Token needs to be locked and held until the operation
+ * completes witha md_update_sb(), which would eventually release
+ * the lock.
+ */
+#define MD_CLUSTER_SEND_LOCKED_ALREADY 5
+
struct md_cluster_info {
/* dlm lock space and resources for clustered raid. */
dlm_lockspace_t *lockspace;
int slot_number;
struct completion completion;
+ struct mutex recv_mutex;
struct dlm_lock_resource *bitmap_lockres;
+ struct dlm_lock_resource **other_bitmap_lockres;
struct dlm_lock_resource *resync_lockres;
struct list_head suspend_list;
spinlock_t suspend_lock;
@@ -67,6 +83,7 @@ struct md_cluster_info {
struct dlm_lock_resource *no_new_dev_lockres;
struct md_thread *recv_thread;
struct completion newdisk_completion;
+ wait_queue_head_t wait;
unsigned long state;
};
@@ -431,8 +448,10 @@ static void process_add_new_disk(struct mddev *mddev, struct cluster_msg *cmsg)
static void process_metadata_update(struct mddev *mddev, struct cluster_msg *msg)
{
struct md_cluster_info *cinfo = mddev->cluster_info;
- md_reload_sb(mddev, le32_to_cpu(msg->raid_slot));
+ mddev->good_device_nr = le32_to_cpu(msg->raid_slot);
+ set_bit(MD_RELOAD_SB, &mddev->flags);
dlm_lock_sync(cinfo->no_new_dev_lockres, DLM_LOCK_CR);
+ md_wakeup_thread(mddev->thread);
}
static void process_remove_disk(struct mddev *mddev, struct cluster_msg *msg)
@@ -440,8 +459,11 @@ static void process_remove_disk(struct mddev *mddev, struct cluster_msg *msg)
struct md_rdev *rdev = md_find_rdev_nr_rcu(mddev,
le32_to_cpu(msg->raid_slot));
- if (rdev)
- md_kick_rdev_from_array(rdev);
+ if (rdev) {
+ set_bit(ClusterRemove, &rdev->flags);
+ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
+ md_wakeup_thread(mddev->thread);
+ }
else
pr_warn("%s: %d Could not find disk(%d) to REMOVE\n",
__func__, __LINE__, le32_to_cpu(msg->raid_slot));
@@ -502,9 +524,11 @@ static void recv_daemon(struct md_thread *thread)
struct cluster_msg msg;
int ret;
+ mutex_lock(&cinfo->recv_mutex);
/*get CR on Message*/
if (dlm_lock_sync(message_lockres, DLM_LOCK_CR)) {
pr_err("md/raid1:failed to get CR on MESSAGE\n");
+ mutex_unlock(&cinfo->recv_mutex);
return;
}
@@ -528,33 +552,45 @@ static void recv_daemon(struct md_thread *thread)
ret = dlm_unlock_sync(message_lockres);
if (unlikely(ret != 0))
pr_info("unlock msg failed return %d\n", ret);
+ mutex_unlock(&cinfo->recv_mutex);
}
-/* lock_comm()
+/* lock_token()
* Takes the lock on the TOKEN lock resource so no other
* node can communicate while the operation is underway.
- * If called again, and the TOKEN lock is alread in EX mode
- * return success. However, care must be taken that unlock_comm()
- * is called only once.
*/
-static int lock_comm(struct md_cluster_info *cinfo)
+static int lock_token(struct md_cluster_info *cinfo)
{
int error;
- if (cinfo->token_lockres->mode == DLM_LOCK_EX)
- return 0;
-
error = dlm_lock_sync(cinfo->token_lockres, DLM_LOCK_EX);
if (error)
pr_err("md-cluster(%s:%d): failed to get EX on TOKEN (%d)\n",
__func__, __LINE__, error);
+
+ /* Lock the receive sequence */
+ mutex_lock(&cinfo->recv_mutex);
return error;
}
+/* lock_comm()
+ * Sets the MD_CLUSTER_SEND_LOCK bit to lock the send channel.
+ */
+static int lock_comm(struct md_cluster_info *cinfo)
+{
+ wait_event(cinfo->wait,
+ !test_and_set_bit(MD_CLUSTER_SEND_LOCK, &cinfo->state));
+
+ return lock_token(cinfo);
+}
+
static void unlock_comm(struct md_cluster_info *cinfo)
{
WARN_ON(cinfo->token_lockres->mode != DLM_LOCK_EX);
+ mutex_unlock(&cinfo->recv_mutex);
dlm_unlock_sync(cinfo->token_lockres);
+ clear_bit(MD_CLUSTER_SEND_LOCK, &cinfo->state);
+ wake_up(&cinfo->wait);
}
/* __sendmsg()
@@ -707,6 +743,8 @@ static int join(struct mddev *mddev, int nodes)
spin_lock_init(&cinfo->suspend_lock);
init_completion(&cinfo->completion);
set_bit(MD_CLUSTER_BEGIN_JOIN_CLUSTER, &cinfo->state);
+ init_waitqueue_head(&cinfo->wait);
+ mutex_init(&cinfo->recv_mutex);
mddev->cluster_info = cinfo;
@@ -800,6 +838,7 @@ static void resync_bitmap(struct mddev *mddev)
__func__, __LINE__, err);
}
+static void unlock_all_bitmaps(struct mddev *mddev);
static int leave(struct mddev *mddev)
{
struct md_cluster_info *cinfo = mddev->cluster_info;
@@ -820,6 +859,7 @@ static int leave(struct mddev *mddev)
lockres_free(cinfo->ack_lockres);
lockres_free(cinfo->no_new_dev_lockres);
lockres_free(cinfo->bitmap_lockres);
+ unlock_all_bitmaps(mddev);
dlm_release_lockspace(cinfo->lockspace, 2);
return 0;
}
@@ -835,9 +875,25 @@ static int slot_number(struct mddev *mddev)
return cinfo->slot_number - 1;
}
+/*
+ * Check if the communication is already locked, else lock the communication
+ * channel.
+ * If it is already locked, token is in EX mode, and hence lock_token()
+ * should not be called.
+ */
static int metadata_update_start(struct mddev *mddev)
{
- return lock_comm(mddev->cluster_info);
+ struct md_cluster_info *cinfo = mddev->cluster_info;
+
+ wait_event(cinfo->wait,
+ !test_and_set_bit(MD_CLUSTER_SEND_LOCK, &cinfo->state) ||
+ test_and_clear_bit(MD_CLUSTER_SEND_LOCKED_ALREADY, &cinfo->state));
+
+ /* If token is already locked, return 0 */
+ if (cinfo->token_lockres->mode == DLM_LOCK_EX)
+ return 0;
+
+ return lock_token(cinfo);
}
static int metadata_update_finish(struct mddev *mddev)
@@ -862,6 +918,7 @@ static int metadata_update_finish(struct mddev *mddev)
ret = __sendmsg(cinfo, &cmsg);
} else
pr_warn("md-cluster: No good device id found to send\n");
+ clear_bit(MD_CLUSTER_SEND_LOCKED_ALREADY, &cinfo->state);
unlock_comm(cinfo);
return ret;
}
@@ -869,6 +926,7 @@ static int metadata_update_finish(struct mddev *mddev)
static void metadata_update_cancel(struct mddev *mddev)
{
struct md_cluster_info *cinfo = mddev->cluster_info;
+ clear_bit(MD_CLUSTER_SEND_LOCKED_ALREADY, &cinfo->state);
unlock_comm(cinfo);
}
@@ -882,8 +940,16 @@ static int resync_start(struct mddev *mddev)
static int resync_info_update(struct mddev *mddev, sector_t lo, sector_t hi)
{
struct md_cluster_info *cinfo = mddev->cluster_info;
+ struct resync_info ri;
struct cluster_msg cmsg = {0};
+ /* do not send zero again, if we have sent before */
+ if (hi == 0) {
+ memcpy(&ri, cinfo->bitmap_lockres->lksb.sb_lvbptr, sizeof(struct resync_info));
+ if (le64_to_cpu(ri.hi) == 0)
+ return 0;
+ }
+
add_resync_info(cinfo->bitmap_lockres, lo, hi);
/* Re-acquire the lock to refresh LVB */
dlm_lock_sync(cinfo->bitmap_lockres, DLM_LOCK_PW);
@@ -954,14 +1020,30 @@ static int add_new_disk(struct mddev *mddev, struct md_rdev *rdev)
ret = -ENOENT;
if (ret)
unlock_comm(cinfo);
- else
+ else {
dlm_lock_sync(cinfo->no_new_dev_lockres, DLM_LOCK_CR);
+ /* Since MD_CHANGE_DEVS will be set in add_bound_rdev which
+ * will run soon after add_new_disk, the below path will be
+ * invoked:
+ * md_wakeup_thread(mddev->thread)
+ * -> conf->thread (raid1d)
+ * -> md_check_recovery -> md_update_sb
+ * -> metadata_update_start/finish
+ * MD_CLUSTER_SEND_LOCKED_ALREADY will be cleared eventually.
+ *
+ * For other failure cases, metadata_update_cancel and
+ * add_new_disk_cancel also clear below bit as well.
+ * */
+ set_bit(MD_CLUSTER_SEND_LOCKED_ALREADY, &cinfo->state);
+ wake_up(&cinfo->wait);
+ }
return ret;
}
static void add_new_disk_cancel(struct mddev *mddev)
{
struct md_cluster_info *cinfo = mddev->cluster_info;
+ clear_bit(MD_CLUSTER_SEND_LOCKED_ALREADY, &cinfo->state);
unlock_comm(cinfo);
}
@@ -986,7 +1068,59 @@ static int remove_disk(struct mddev *mddev, struct md_rdev *rdev)
struct md_cluster_info *cinfo = mddev->cluster_info;
cmsg.type = cpu_to_le32(REMOVE);
cmsg.raid_slot = cpu_to_le32(rdev->desc_nr);
- return __sendmsg(cinfo, &cmsg);
+ return sendmsg(cinfo, &cmsg);
+}
+
+static int lock_all_bitmaps(struct mddev *mddev)
+{
+ int slot, my_slot, ret, held = 1, i = 0;
+ char str[64];
+ struct md_cluster_info *cinfo = mddev->cluster_info;
+
+ cinfo->other_bitmap_lockres = kzalloc((mddev->bitmap_info.nodes - 1) *
+ sizeof(struct dlm_lock_resource *),
+ GFP_KERNEL);
+ if (!cinfo->other_bitmap_lockres) {
+ pr_err("md: can't alloc mem for other bitmap locks\n");
+ return 0;
+ }
+
+ my_slot = slot_number(mddev);
+ for (slot = 0; slot < mddev->bitmap_info.nodes; slot++) {
+ if (slot == my_slot)
+ continue;
+
+ memset(str, '\0', 64);
+ snprintf(str, 64, "bitmap%04d", slot);
+ cinfo->other_bitmap_lockres[i] = lockres_init(mddev, str, NULL, 1);
+ if (!cinfo->other_bitmap_lockres[i])
+ return -ENOMEM;
+
+ cinfo->other_bitmap_lockres[i]->flags |= DLM_LKF_NOQUEUE;
+ ret = dlm_lock_sync(cinfo->other_bitmap_lockres[i], DLM_LOCK_PW);
+ if (ret)
+ held = -1;
+ i++;
+ }
+
+ return held;
+}
+
+static void unlock_all_bitmaps(struct mddev *mddev)
+{
+ struct md_cluster_info *cinfo = mddev->cluster_info;
+ int i;
+
+ /* release other node's bitmap lock if they are existed */
+ if (cinfo->other_bitmap_lockres) {
+ for (i = 0; i < mddev->bitmap_info.nodes - 1; i++) {
+ if (cinfo->other_bitmap_lockres[i]) {
+ dlm_unlock_sync(cinfo->other_bitmap_lockres[i]);
+ lockres_free(cinfo->other_bitmap_lockres[i]);
+ }
+ }
+ kfree(cinfo->other_bitmap_lockres);
+ }
}
static int gather_bitmaps(struct md_rdev *rdev)
@@ -1034,6 +1168,8 @@ static struct md_cluster_operations cluster_ops = {
.new_disk_ack = new_disk_ack,
.remove_disk = remove_disk,
.gather_bitmaps = gather_bitmaps,
+ .lock_all_bitmaps = lock_all_bitmaps,
+ .unlock_all_bitmaps = unlock_all_bitmaps,
};
static int __init cluster_init(void)
diff --git a/drivers/md/md-cluster.h b/drivers/md/md-cluster.h
index e75ea2613184..45ce6c97d8bd 100644
--- a/drivers/md/md-cluster.h
+++ b/drivers/md/md-cluster.h
@@ -24,6 +24,8 @@ struct md_cluster_operations {
int (*new_disk_ack)(struct mddev *mddev, bool ack);
int (*remove_disk)(struct mddev *mddev, struct md_rdev *rdev);
int (*gather_bitmaps)(struct md_rdev *rdev);
+ int (*lock_all_bitmaps)(struct mddev *mddev);
+ void (*unlock_all_bitmaps)(struct mddev *mddev);
};
#endif /* _MD_CLUSTER_H */
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 807095f4c793..e55e6cf9ec17 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -34,6 +34,7 @@
#include <linux/kthread.h>
#include <linux/blkdev.h>
+#include <linux/badblocks.h>
#include <linux/sysctl.h>
#include <linux/seq_file.h>
#include <linux/fs.h>
@@ -205,15 +206,6 @@ void md_new_event(struct mddev *mddev)
}
EXPORT_SYMBOL_GPL(md_new_event);
-/* Alternate version that can be called from interrupts
- * when calling sysfs_notify isn't needed.
- */
-static void md_new_event_inintr(struct mddev *mddev)
-{
- atomic_inc(&md_event_count);
- wake_up(&md_event_waiters);
-}
-
/*
* Enables to iterate over all existing md arrays
* all_mddevs_lock protects this list.
@@ -259,8 +251,7 @@ static blk_qc_t md_make_request(struct request_queue *q, struct bio *bio)
blk_queue_split(q, &bio, q->bio_split);
- if (mddev == NULL || mddev->pers == NULL
- || !mddev->ready) {
+ if (mddev == NULL || mddev->pers == NULL) {
bio_io_error(bio);
return BLK_QC_T_NONE;
}
@@ -314,8 +305,8 @@ static blk_qc_t md_make_request(struct request_queue *q, struct bio *bio)
*/
void mddev_suspend(struct mddev *mddev)
{
- BUG_ON(mddev->suspended);
- mddev->suspended = 1;
+ if (mddev->suspended++)
+ return;
synchronize_rcu();
wait_event(mddev->sb_wait, atomic_read(&mddev->active_io) == 0);
mddev->pers->quiesce(mddev, 1);
@@ -326,7 +317,8 @@ EXPORT_SYMBOL_GPL(mddev_suspend);
void mddev_resume(struct mddev *mddev)
{
- mddev->suspended = 0;
+ if (--mddev->suspended)
+ return;
wake_up(&mddev->sb_wait);
mddev->pers->quiesce(mddev, 0);
@@ -709,8 +701,7 @@ void md_rdev_clear(struct md_rdev *rdev)
put_page(rdev->bb_page);
rdev->bb_page = NULL;
}
- kfree(rdev->badblocks.page);
- rdev->badblocks.page = NULL;
+ badblocks_exit(&rdev->badblocks);
}
EXPORT_SYMBOL_GPL(md_rdev_clear);
@@ -1025,8 +1016,9 @@ static int super_90_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor
* (not needed for Linear and RAID0 as metadata doesn't
* record this size)
*/
- if (rdev->sectors >= (2ULL << 32) && sb->level >= 1)
- rdev->sectors = (2ULL << 32) - 2;
+ if (IS_ENABLED(CONFIG_LBDAF) && (u64)rdev->sectors >= (2ULL << 32) &&
+ sb->level >= 1)
+ rdev->sectors = (sector_t)(2ULL << 32) - 2;
if (rdev->sectors < ((sector_t)sb->size) * 2 && sb->level >= 1)
/* "this cannot possibly happen" ... */
@@ -1198,13 +1190,13 @@ static void super_90_sync(struct mddev *mddev, struct md_rdev *rdev)
memcpy(&sb->set_uuid2, mddev->uuid+8, 4);
memcpy(&sb->set_uuid3, mddev->uuid+12,4);
- sb->ctime = mddev->ctime;
+ sb->ctime = clamp_t(time64_t, mddev->ctime, 0, U32_MAX);
sb->level = mddev->level;
sb->size = mddev->dev_sectors / 2;
sb->raid_disks = mddev->raid_disks;
sb->md_minor = mddev->md_minor;
sb->not_persistent = 0;
- sb->utime = mddev->utime;
+ sb->utime = clamp_t(time64_t, mddev->utime, 0, U32_MAX);
sb->state = 0;
sb->events_hi = (mddev->events>>32);
sb->events_lo = (u32)mddev->events;
@@ -1319,8 +1311,9 @@ super_90_rdev_size_change(struct md_rdev *rdev, sector_t num_sectors)
/* Limit to 4TB as metadata cannot record more than that.
* 4TB == 2^32 KB, or 2*2^32 sectors.
*/
- if (num_sectors >= (2ULL << 32) && rdev->mddev->level >= 1)
- num_sectors = (2ULL << 32) - 2;
+ if (IS_ENABLED(CONFIG_LBDAF) && (u64)num_sectors >= (2ULL << 32) &&
+ rdev->mddev->level >= 1)
+ num_sectors = (sector_t)(2ULL << 32) - 2;
md_super_write(rdev->mddev, rdev, rdev->sb_start, rdev->sb_size,
rdev->sb_page);
md_super_wait(rdev->mddev);
@@ -1360,8 +1353,6 @@ static __le32 calc_sb_1_csum(struct mdp_superblock_1 *sb)
return cpu_to_le32(csum);
}
-static int md_set_badblocks(struct badblocks *bb, sector_t s, int sectors,
- int acknowledged);
static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_version)
{
struct mdp_superblock_1 *sb;
@@ -1486,8 +1477,7 @@ static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_
count <<= sb->bblog_shift;
if (bb + 1 == 0)
break;
- if (md_set_badblocks(&rdev->badblocks,
- sector, count, 1) == 0)
+ if (badblocks_set(&rdev->badblocks, sector, count, 1))
return -EINVAL;
}
} else if (sb->bblog_offset != 0)
@@ -1544,8 +1534,8 @@ static int super_1_validate(struct mddev *mddev, struct md_rdev *rdev)
mddev->patch_version = 0;
mddev->external = 0;
mddev->chunk_sectors = le32_to_cpu(sb->chunksize);
- mddev->ctime = le64_to_cpu(sb->ctime) & ((1ULL << 32)-1);
- mddev->utime = le64_to_cpu(sb->utime) & ((1ULL << 32)-1);
+ mddev->ctime = le64_to_cpu(sb->ctime);
+ mddev->utime = le64_to_cpu(sb->utime);
mddev->level = le32_to_cpu(sb->level);
mddev->clevel[0] = 0;
mddev->layout = le32_to_cpu(sb->layout);
@@ -1604,6 +1594,11 @@ static int super_1_validate(struct mddev *mddev, struct md_rdev *rdev)
mddev->new_chunk_sectors = mddev->chunk_sectors;
}
+ if (le32_to_cpu(sb->feature_map) & MD_FEATURE_JOURNAL) {
+ set_bit(MD_HAS_JOURNAL, &mddev->flags);
+ if (mddev->recovery_cp == MaxSector)
+ set_bit(MD_JOURNAL_CLEAN, &mddev->flags);
+ }
} else if (mddev->pers == NULL) {
/* Insist of good event counter while assembling, except for
* spares (which don't need an event count) */
@@ -1650,9 +1645,7 @@ static int super_1_validate(struct mddev *mddev, struct md_rdev *rdev)
}
set_bit(Journal, &rdev->flags);
rdev->journal_tail = le64_to_cpu(sb->journal_tail);
- if (mddev->recovery_cp == MaxSector)
- set_bit(MD_JOURNAL_CLEAN, &mddev->flags);
- rdev->raid_disk = mddev->raid_disks;
+ rdev->raid_disk = 0;
break;
default:
rdev->saved_raid_disk = role;
@@ -1671,8 +1664,6 @@ static int super_1_validate(struct mddev *mddev, struct md_rdev *rdev)
set_bit(WriteMostly, &rdev->flags);
if (le32_to_cpu(sb->feature_map) & MD_FEATURE_REPLACEMENT)
set_bit(Replacement, &rdev->flags);
- if (le32_to_cpu(sb->feature_map) & MD_FEATURE_JOURNAL)
- set_bit(MD_HAS_JOURNAL, &mddev->flags);
} else /* MULTIPATH are always insync */
set_bit(In_sync, &rdev->flags);
@@ -2016,28 +2007,32 @@ int md_integrity_register(struct mddev *mddev)
}
EXPORT_SYMBOL(md_integrity_register);
-/* Disable data integrity if non-capable/non-matching disk is being added */
-void md_integrity_add_rdev(struct md_rdev *rdev, struct mddev *mddev)
+/*
+ * Attempt to add an rdev, but only if it is consistent with the current
+ * integrity profile
+ */
+int md_integrity_add_rdev(struct md_rdev *rdev, struct mddev *mddev)
{
struct blk_integrity *bi_rdev;
struct blk_integrity *bi_mddev;
+ char name[BDEVNAME_SIZE];
if (!mddev->gendisk)
- return;
+ return 0;
bi_rdev = bdev_get_integrity(rdev->bdev);
bi_mddev = blk_get_integrity(mddev->gendisk);
if (!bi_mddev) /* nothing to do */
- return;
- if (rdev->raid_disk < 0) /* skip spares */
- return;
- if (bi_rdev && blk_integrity_compare(mddev->gendisk,
- rdev->bdev->bd_disk) >= 0)
- return;
- WARN_ON_ONCE(!mddev->suspended);
- printk(KERN_NOTICE "disabling data integrity on %s\n", mdname(mddev));
- blk_integrity_unregister(mddev->gendisk);
+ return 0;
+
+ if (blk_integrity_compare(mddev->gendisk, rdev->bdev->bd_disk) != 0) {
+ printk(KERN_NOTICE "%s: incompatible integrity profile for %s\n",
+ mdname(mddev), bdevname(rdev->bdev, name));
+ return -ENXIO;
+ }
+
+ return 0;
}
EXPORT_SYMBOL(md_integrity_add_rdev);
@@ -2052,8 +2047,9 @@ static int bind_rdev_to_array(struct md_rdev *rdev, struct mddev *mddev)
return -EEXIST;
/* make sure rdev->sectors exceeds mddev->dev_sectors */
- if (rdev->sectors && (mddev->dev_sectors == 0 ||
- rdev->sectors < mddev->dev_sectors)) {
+ if (!test_bit(Journal, &rdev->flags) &&
+ rdev->sectors &&
+ (mddev->dev_sectors == 0 || rdev->sectors < mddev->dev_sectors)) {
if (mddev->pers) {
/* Cannot change size, so fail
* If mddev->level <= 0, then we don't care
@@ -2084,7 +2080,8 @@ static int bind_rdev_to_array(struct md_rdev *rdev, struct mddev *mddev)
}
}
rcu_read_unlock();
- if (mddev->max_disks && rdev->desc_nr >= mddev->max_disks) {
+ if (!test_bit(Journal, &rdev->flags) &&
+ mddev->max_disks && rdev->desc_nr >= mddev->max_disks) {
printk(KERN_WARNING "md: %s: array is limited to %d devices\n",
mdname(mddev), mddev->max_disks);
return -EBUSY;
@@ -2319,7 +2316,7 @@ repeat:
rdev_for_each(rdev, mddev) {
if (rdev->badblocks.changed) {
rdev->badblocks.changed = 0;
- md_ack_all_badblocks(&rdev->badblocks);
+ ack_all_badblocks(&rdev->badblocks);
md_error(mddev, rdev);
}
clear_bit(Blocked, &rdev->flags);
@@ -2333,7 +2330,7 @@ repeat:
spin_lock(&mddev->lock);
- mddev->utime = get_seconds();
+ mddev->utime = ktime_get_real_seconds();
if (test_and_clear_bit(MD_CHANGE_DEVS, &mddev->flags))
force_change = 1;
@@ -2445,7 +2442,7 @@ repeat:
clear_bit(Blocked, &rdev->flags);
if (any_badblocks_changed)
- md_ack_all_badblocks(&rdev->badblocks);
+ ack_all_badblocks(&rdev->badblocks);
clear_bit(BlockedBadBlocks, &rdev->flags);
wake_up(&rdev->blocked_wait);
}
@@ -2459,15 +2456,20 @@ static int add_bound_rdev(struct md_rdev *rdev)
{
struct mddev *mddev = rdev->mddev;
int err = 0;
+ bool add_journal = test_bit(Journal, &rdev->flags);
- if (!mddev->pers->hot_remove_disk) {
+ if (!mddev->pers->hot_remove_disk || add_journal) {
/* If there is hot_add_disk but no hot_remove_disk
* then added disks for geometry changes,
* and should be added immediately.
*/
super_types[mddev->major_version].
validate_super(mddev, rdev);
+ if (add_journal)
+ mddev_suspend(mddev);
err = mddev->pers->hot_add_disk(mddev, rdev);
+ if (add_journal)
+ mddev_resume(mddev);
if (err) {
unbind_rdev_from_array(rdev);
export_rdev(rdev);
@@ -2773,6 +2775,7 @@ slot_store(struct md_rdev *rdev, const char *buf, size_t len)
/* Activating a spare .. or possibly reactivating
* if we ever get bitmaps working here.
*/
+ int err;
if (rdev->raid_disk != -1)
return -EBUSY;
@@ -2794,9 +2797,15 @@ slot_store(struct md_rdev *rdev, const char *buf, size_t len)
rdev->saved_raid_disk = -1;
clear_bit(In_sync, &rdev->flags);
clear_bit(Bitmap_sync, &rdev->flags);
- remove_and_add_spares(rdev->mddev, rdev);
- if (rdev->raid_disk == -1)
- return -EBUSY;
+ err = rdev->mddev->pers->
+ hot_add_disk(rdev->mddev, rdev);
+ if (err) {
+ rdev->raid_disk = -1;
+ return err;
+ } else
+ sysfs_notify_dirent_safe(rdev->sysfs_state);
+ if (sysfs_link_rdev(rdev->mddev, rdev))
+ /* failure here is OK */;
/* don't wakeup anyone, leave that to userspace. */
} else {
if (slot >= rdev->mddev->raid_disks &&
@@ -3046,11 +3055,17 @@ static ssize_t recovery_start_store(struct md_rdev *rdev, const char *buf, size_
static struct rdev_sysfs_entry rdev_recovery_start =
__ATTR(recovery_start, S_IRUGO|S_IWUSR, recovery_start_show, recovery_start_store);
-static ssize_t
-badblocks_show(struct badblocks *bb, char *page, int unack);
-static ssize_t
-badblocks_store(struct badblocks *bb, const char *page, size_t len, int unack);
-
+/* sysfs access to bad-blocks list.
+ * We present two files.
+ * 'bad-blocks' lists sector numbers and lengths of ranges that
+ * are recorded as bad. The list is truncated to fit within
+ * the one-page limit of sysfs.
+ * Writing "sector length" to this file adds an acknowledged
+ * bad block list.
+ * 'unacknowledged-bad-blocks' lists bad blocks that have not yet
+ * been acknowledged. Writing to this file adds bad blocks
+ * without acknowledging them. This is largely for testing.
+ */
static ssize_t bb_show(struct md_rdev *rdev, char *page)
{
return badblocks_show(&rdev->badblocks, page, 0);
@@ -3165,14 +3180,7 @@ int md_rdev_init(struct md_rdev *rdev)
* This reserves the space even on arrays where it cannot
* be used - I wonder if that matters
*/
- rdev->badblocks.count = 0;
- rdev->badblocks.shift = -1; /* disabled until explicitly enabled */
- rdev->badblocks.page = kmalloc(PAGE_SIZE, GFP_KERNEL);
- seqlock_init(&rdev->badblocks.lock);
- if (rdev->badblocks.page == NULL)
- return -ENOMEM;
-
- return 0;
+ return badblocks_init(&rdev->badblocks, 0);
}
EXPORT_SYMBOL_GPL(md_rdev_init);
/*
@@ -4318,8 +4326,7 @@ action_store(struct mddev *mddev, const char *page, size_t len)
}
mddev_unlock(mddev);
}
- } else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) ||
- test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))
+ } else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
return -EBUSY;
else if (cmd_match(page, "resync"))
clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
@@ -4332,8 +4339,12 @@ action_store(struct mddev *mddev, const char *page, size_t len)
return -EINVAL;
err = mddev_lock(mddev);
if (!err) {
- clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
- err = mddev->pers->start_reshape(mddev);
+ if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
+ err = -EBUSY;
+ else {
+ clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+ err = mddev->pers->start_reshape(mddev);
+ }
mddev_unlock(mddev);
}
if (err)
@@ -5292,7 +5303,6 @@ int md_run(struct mddev *mddev)
smp_wmb();
spin_lock(&mddev->lock);
mddev->pers = pers;
- mddev->ready = 1;
spin_unlock(&mddev->lock);
rdev_for_each(rdev, mddev)
if (rdev->raid_disk >= 0)
@@ -5492,7 +5502,6 @@ static void __md_stop(struct mddev *mddev)
/* Ensure ->event_work is done */
flush_workqueue(md_misc_wq);
spin_lock(&mddev->lock);
- mddev->ready = 0;
mddev->pers = NULL;
spin_unlock(&mddev->lock);
pers->free(mddev, mddev->private);
@@ -5830,7 +5839,7 @@ static int get_array_info(struct mddev *mddev, void __user *arg)
info.major_version = mddev->major_version;
info.minor_version = mddev->minor_version;
info.patch_version = MD_PATCHLEVEL_VERSION;
- info.ctime = mddev->ctime;
+ info.ctime = clamp_t(time64_t, mddev->ctime, 0, U32_MAX);
info.level = mddev->level;
info.size = mddev->dev_sectors / 2;
if (info.size != mddev->dev_sectors / 2) /* overflow */
@@ -5840,7 +5849,7 @@ static int get_array_info(struct mddev *mddev, void __user *arg)
info.md_minor = mddev->md_minor;
info.not_persistent= !mddev->persistent;
- info.utime = mddev->utime;
+ info.utime = clamp_t(time64_t, mddev->utime, 0, U32_MAX);
info.state = 0;
if (mddev->in_sync)
info.state = (1<<MD_SB_CLEAN);
@@ -6031,8 +6040,23 @@ static int add_new_disk(struct mddev *mddev, mdu_disk_info_t *info)
else
clear_bit(WriteMostly, &rdev->flags);
- if (info->state & (1<<MD_DISK_JOURNAL))
+ if (info->state & (1<<MD_DISK_JOURNAL)) {
+ struct md_rdev *rdev2;
+ bool has_journal = false;
+
+ /* make sure no existing journal disk */
+ rdev_for_each(rdev2, mddev) {
+ if (test_bit(Journal, &rdev2->flags)) {
+ has_journal = true;
+ break;
+ }
+ }
+ if (has_journal) {
+ export_rdev(rdev);
+ return -EBUSY;
+ }
set_bit(Journal, &rdev->flags);
+ }
/*
* check whether the device shows up in other nodes
*/
@@ -6123,15 +6147,11 @@ static int hot_remove_disk(struct mddev *mddev, dev_t dev)
{
char b[BDEVNAME_SIZE];
struct md_rdev *rdev;
- int ret = -1;
rdev = find_rdev(mddev, dev);
if (!rdev)
return -ENXIO;
- if (mddev_is_clustered(mddev))
- ret = md_cluster_ops->metadata_update_start(mddev);
-
if (rdev->raid_disk < 0)
goto kick_rdev;
@@ -6142,7 +6162,7 @@ static int hot_remove_disk(struct mddev *mddev, dev_t dev)
goto busy;
kick_rdev:
- if (mddev_is_clustered(mddev) && ret == 0)
+ if (mddev_is_clustered(mddev))
md_cluster_ops->remove_disk(mddev, rdev);
md_kick_rdev_from_array(rdev);
@@ -6151,9 +6171,6 @@ kick_rdev:
return 0;
busy:
- if (mddev_is_clustered(mddev) && ret == 0)
- md_cluster_ops->metadata_update_cancel(mddev);
-
printk(KERN_WARNING "md: cannot remove active disk %s from %s ...\n",
bdevname(rdev->bdev,b), mdname(mddev));
return -EBUSY;
@@ -6347,13 +6364,13 @@ static int set_array_info(struct mddev *mddev, mdu_array_info_t *info)
/* ensure mddev_put doesn't delete this now that there
* is some minimal configuration.
*/
- mddev->ctime = get_seconds();
+ mddev->ctime = ktime_get_real_seconds();
return 0;
}
mddev->major_version = MD_MAJOR_VERSION;
mddev->minor_version = MD_MINOR_VERSION;
mddev->patch_version = MD_PATCHLEVEL_VERSION;
- mddev->ctime = get_seconds();
+ mddev->ctime = ktime_get_real_seconds();
mddev->level = info->level;
mddev->clevel[0] = 0;
@@ -6595,6 +6612,19 @@ static int update_array_info(struct mddev *mddev, mdu_array_info_t *info)
rv = -EINVAL;
goto err;
}
+ if (mddev->bitmap_info.nodes) {
+ /* hold PW on all the bitmap lock */
+ if (md_cluster_ops->lock_all_bitmaps(mddev) <= 0) {
+ printk("md: can't change bitmap to none since the"
+ " array is in use by more than one node\n");
+ rv = -EPERM;
+ md_cluster_ops->unlock_all_bitmaps(mddev);
+ goto err;
+ }
+
+ mddev->bitmap_info.nodes = 0;
+ md_cluster_ops->leave(mddev);
+ }
mddev->pers->quiesce(mddev, 1);
bitmap_destroy(mddev);
mddev->pers->quiesce(mddev, 0);
@@ -7173,7 +7203,7 @@ void md_error(struct mddev *mddev, struct md_rdev *rdev)
md_wakeup_thread(mddev->thread);
if (mddev->event_work.func)
queue_work(md_misc_wq, &mddev->event_work);
- md_new_event_inintr(mddev);
+ md_new_event(mddev);
}
EXPORT_SYMBOL(md_error);
@@ -7697,7 +7727,7 @@ EXPORT_SYMBOL(md_write_end);
* attempting a GFP_KERNEL allocation while holding the mddev lock.
* Must be called with mddev_lock held.
*
- * In the ->external case MD_CHANGE_CLEAN can not be cleared until mddev->lock
+ * In the ->external case MD_CHANGE_PENDING can not be cleared until mddev->lock
* is dropped, so return -EAGAIN after notifying userspace.
*/
int md_allow_write(struct mddev *mddev)
@@ -8162,19 +8192,20 @@ static int remove_and_add_spares(struct mddev *mddev,
continue;
if (test_bit(Faulty, &rdev->flags))
continue;
- if (test_bit(Journal, &rdev->flags))
- continue;
- if (mddev->ro &&
- ! (rdev->saved_raid_disk >= 0 &&
- !test_bit(Bitmap_sync, &rdev->flags)))
- continue;
+ if (!test_bit(Journal, &rdev->flags)) {
+ if (mddev->ro &&
+ ! (rdev->saved_raid_disk >= 0 &&
+ !test_bit(Bitmap_sync, &rdev->flags)))
+ continue;
- rdev->recovery_offset = 0;
+ rdev->recovery_offset = 0;
+ }
if (mddev->pers->
hot_add_disk(mddev, rdev) == 0) {
if (sysfs_link_rdev(mddev, rdev))
/* failure here is OK */;
- spares++;
+ if (!test_bit(Journal, &rdev->flags))
+ spares++;
md_new_event(mddev);
set_bit(MD_CHANGE_DEVS, &mddev->flags);
}
@@ -8269,6 +8300,7 @@ void md_check_recovery(struct mddev *mddev)
(mddev->flags & MD_UPDATE_SB_FLAGS & ~ (1<<MD_CHANGE_PENDING)) ||
test_bit(MD_RECOVERY_NEEDED, &mddev->recovery) ||
test_bit(MD_RECOVERY_DONE, &mddev->recovery) ||
+ test_bit(MD_RELOAD_SB, &mddev->flags) ||
(mddev->external == 0 && mddev->safemode == 1) ||
(mddev->safemode == 2 && ! atomic_read(&mddev->writes_pending)
&& !mddev->in_sync && mddev->recovery_cp == MaxSector)
@@ -8307,6 +8339,21 @@ void md_check_recovery(struct mddev *mddev)
goto unlock;
}
+ if (mddev_is_clustered(mddev)) {
+ struct md_rdev *rdev;
+ /* kick the device if another node issued a
+ * remove disk.
+ */
+ rdev_for_each(rdev, mddev) {
+ if (test_and_clear_bit(ClusterRemove, &rdev->flags) &&
+ rdev->raid_disk < 0)
+ md_kick_rdev_from_array(rdev);
+ }
+
+ if (test_and_clear_bit(MD_RELOAD_SB, &mddev->flags))
+ md_reload_sb(mddev, mddev->good_device_nr);
+ }
+
if (!mddev->external) {
int did_change = 0;
spin_lock(&mddev->lock);
@@ -8478,254 +8525,9 @@ void md_finish_reshape(struct mddev *mddev)
}
EXPORT_SYMBOL(md_finish_reshape);
-/* Bad block management.
- * We can record which blocks on each device are 'bad' and so just
- * fail those blocks, or that stripe, rather than the whole device.
- * Entries in the bad-block table are 64bits wide. This comprises:
- * Length of bad-range, in sectors: 0-511 for lengths 1-512
- * Start of bad-range, sector offset, 54 bits (allows 8 exbibytes)
- * A 'shift' can be set so that larger blocks are tracked and
- * consequently larger devices can be covered.
- * 'Acknowledged' flag - 1 bit. - the most significant bit.
- *
- * Locking of the bad-block table uses a seqlock so md_is_badblock
- * might need to retry if it is very unlucky.
- * We will sometimes want to check for bad blocks in a bi_end_io function,
- * so we use the write_seqlock_irq variant.
- *
- * When looking for a bad block we specify a range and want to
- * know if any block in the range is bad. So we binary-search
- * to the last range that starts at-or-before the given endpoint,
- * (or "before the sector after the target range")
- * then see if it ends after the given start.
- * We return
- * 0 if there are no known bad blocks in the range
- * 1 if there are known bad block which are all acknowledged
- * -1 if there are bad blocks which have not yet been acknowledged in metadata.
- * plus the start/length of the first bad section we overlap.
- */
-int md_is_badblock(struct badblocks *bb, sector_t s, int sectors,
- sector_t *first_bad, int *bad_sectors)
-{
- int hi;
- int lo;
- u64 *p = bb->page;
- int rv;
- sector_t target = s + sectors;
- unsigned seq;
-
- if (bb->shift > 0) {
- /* round the start down, and the end up */
- s >>= bb->shift;
- target += (1<<bb->shift) - 1;
- target >>= bb->shift;
- sectors = target - s;
- }
- /* 'target' is now the first block after the bad range */
-
-retry:
- seq = read_seqbegin(&bb->lock);
- lo = 0;
- rv = 0;
- hi = bb->count;
-
- /* Binary search between lo and hi for 'target'
- * i.e. for the last range that starts before 'target'
- */
- /* INVARIANT: ranges before 'lo' and at-or-after 'hi'
- * are known not to be the last range before target.
- * VARIANT: hi-lo is the number of possible
- * ranges, and decreases until it reaches 1
- */
- while (hi - lo > 1) {
- int mid = (lo + hi) / 2;
- sector_t a = BB_OFFSET(p[mid]);
- if (a < target)
- /* This could still be the one, earlier ranges
- * could not. */
- lo = mid;
- else
- /* This and later ranges are definitely out. */
- hi = mid;
- }
- /* 'lo' might be the last that started before target, but 'hi' isn't */
- if (hi > lo) {
- /* need to check all range that end after 's' to see if
- * any are unacknowledged.
- */
- while (lo >= 0 &&
- BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > s) {
- if (BB_OFFSET(p[lo]) < target) {
- /* starts before the end, and finishes after
- * the start, so they must overlap
- */
- if (rv != -1 && BB_ACK(p[lo]))
- rv = 1;
- else
- rv = -1;
- *first_bad = BB_OFFSET(p[lo]);
- *bad_sectors = BB_LEN(p[lo]);
- }
- lo--;
- }
- }
-
- if (read_seqretry(&bb->lock, seq))
- goto retry;
-
- return rv;
-}
-EXPORT_SYMBOL_GPL(md_is_badblock);
-
-/*
- * Add a range of bad blocks to the table.
- * This might extend the table, or might contract it
- * if two adjacent ranges can be merged.
- * We binary-search to find the 'insertion' point, then
- * decide how best to handle it.
- */
-static int md_set_badblocks(struct badblocks *bb, sector_t s, int sectors,
- int acknowledged)
-{
- u64 *p;
- int lo, hi;
- int rv = 1;
- unsigned long flags;
-
- if (bb->shift < 0)
- /* badblocks are disabled */
- return 0;
-
- if (bb->shift) {
- /* round the start down, and the end up */
- sector_t next = s + sectors;
- s >>= bb->shift;
- next += (1<<bb->shift) - 1;
- next >>= bb->shift;
- sectors = next - s;
- }
-
- write_seqlock_irqsave(&bb->lock, flags);
-
- p = bb->page;
- lo = 0;
- hi = bb->count;
- /* Find the last range that starts at-or-before 's' */
- while (hi - lo > 1) {
- int mid = (lo + hi) / 2;
- sector_t a = BB_OFFSET(p[mid]);
- if (a <= s)
- lo = mid;
- else
- hi = mid;
- }
- if (hi > lo && BB_OFFSET(p[lo]) > s)
- hi = lo;
-
- if (hi > lo) {
- /* we found a range that might merge with the start
- * of our new range
- */
- sector_t a = BB_OFFSET(p[lo]);
- sector_t e = a + BB_LEN(p[lo]);
- int ack = BB_ACK(p[lo]);
- if (e >= s) {
- /* Yes, we can merge with a previous range */
- if (s == a && s + sectors >= e)
- /* new range covers old */
- ack = acknowledged;
- else
- ack = ack && acknowledged;
-
- if (e < s + sectors)
- e = s + sectors;
- if (e - a <= BB_MAX_LEN) {
- p[lo] = BB_MAKE(a, e-a, ack);
- s = e;
- } else {
- /* does not all fit in one range,
- * make p[lo] maximal
- */
- if (BB_LEN(p[lo]) != BB_MAX_LEN)
- p[lo] = BB_MAKE(a, BB_MAX_LEN, ack);
- s = a + BB_MAX_LEN;
- }
- sectors = e - s;
- }
- }
- if (sectors && hi < bb->count) {
- /* 'hi' points to the first range that starts after 's'.
- * Maybe we can merge with the start of that range */
- sector_t a = BB_OFFSET(p[hi]);
- sector_t e = a + BB_LEN(p[hi]);
- int ack = BB_ACK(p[hi]);
- if (a <= s + sectors) {
- /* merging is possible */
- if (e <= s + sectors) {
- /* full overlap */
- e = s + sectors;
- ack = acknowledged;
- } else
- ack = ack && acknowledged;
-
- a = s;
- if (e - a <= BB_MAX_LEN) {
- p[hi] = BB_MAKE(a, e-a, ack);
- s = e;
- } else {
- p[hi] = BB_MAKE(a, BB_MAX_LEN, ack);
- s = a + BB_MAX_LEN;
- }
- sectors = e - s;
- lo = hi;
- hi++;
- }
- }
- if (sectors == 0 && hi < bb->count) {
- /* we might be able to combine lo and hi */
- /* Note: 's' is at the end of 'lo' */
- sector_t a = BB_OFFSET(p[hi]);
- int lolen = BB_LEN(p[lo]);
- int hilen = BB_LEN(p[hi]);
- int newlen = lolen + hilen - (s - a);
- if (s >= a && newlen < BB_MAX_LEN) {
- /* yes, we can combine them */
- int ack = BB_ACK(p[lo]) && BB_ACK(p[hi]);
- p[lo] = BB_MAKE(BB_OFFSET(p[lo]), newlen, ack);
- memmove(p + hi, p + hi + 1,
- (bb->count - hi - 1) * 8);
- bb->count--;
- }
- }
- while (sectors) {
- /* didn't merge (it all).
- * Need to add a range just before 'hi' */
- if (bb->count >= MD_MAX_BADBLOCKS) {
- /* No room for more */
- rv = 0;
- break;
- } else {
- int this_sectors = sectors;
- memmove(p + hi + 1, p + hi,
- (bb->count - hi) * 8);
- bb->count++;
-
- if (this_sectors > BB_MAX_LEN)
- this_sectors = BB_MAX_LEN;
- p[hi] = BB_MAKE(s, this_sectors, acknowledged);
- sectors -= this_sectors;
- s += this_sectors;
- }
- }
-
- bb->changed = 1;
- if (!acknowledged)
- bb->unacked_exist = 1;
- write_sequnlock_irqrestore(&bb->lock, flags);
-
- return rv;
-}
+/* Bad block management */
+/* Returns 1 on success, 0 on failure */
int rdev_set_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
int is_new)
{
@@ -8734,114 +8536,19 @@ int rdev_set_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
s += rdev->new_data_offset;
else
s += rdev->data_offset;
- rv = md_set_badblocks(&rdev->badblocks,
- s, sectors, 0);
- if (rv) {
+ rv = badblocks_set(&rdev->badblocks, s, sectors, 0);
+ if (rv == 0) {
/* Make sure they get written out promptly */
sysfs_notify_dirent_safe(rdev->sysfs_state);
set_bit(MD_CHANGE_CLEAN, &rdev->mddev->flags);
set_bit(MD_CHANGE_PENDING, &rdev->mddev->flags);
md_wakeup_thread(rdev->mddev->thread);
- }
- return rv;
+ return 1;
+ } else
+ return 0;
}
EXPORT_SYMBOL_GPL(rdev_set_badblocks);
-/*
- * Remove a range of bad blocks from the table.
- * This may involve extending the table if we spilt a region,
- * but it must not fail. So if the table becomes full, we just
- * drop the remove request.
- */
-static int md_clear_badblocks(struct badblocks *bb, sector_t s, int sectors)
-{
- u64 *p;
- int lo, hi;
- sector_t target = s + sectors;
- int rv = 0;
-
- if (bb->shift > 0) {
- /* When clearing we round the start up and the end down.
- * This should not matter as the shift should align with
- * the block size and no rounding should ever be needed.
- * However it is better the think a block is bad when it
- * isn't than to think a block is not bad when it is.
- */
- s += (1<<bb->shift) - 1;
- s >>= bb->shift;
- target >>= bb->shift;
- sectors = target - s;
- }
-
- write_seqlock_irq(&bb->lock);
-
- p = bb->page;
- lo = 0;
- hi = bb->count;
- /* Find the last range that starts before 'target' */
- while (hi - lo > 1) {
- int mid = (lo + hi) / 2;
- sector_t a = BB_OFFSET(p[mid]);
- if (a < target)
- lo = mid;
- else
- hi = mid;
- }
- if (hi > lo) {
- /* p[lo] is the last range that could overlap the
- * current range. Earlier ranges could also overlap,
- * but only this one can overlap the end of the range.
- */
- if (BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > target) {
- /* Partial overlap, leave the tail of this range */
- int ack = BB_ACK(p[lo]);
- sector_t a = BB_OFFSET(p[lo]);
- sector_t end = a + BB_LEN(p[lo]);
-
- if (a < s) {
- /* we need to split this range */
- if (bb->count >= MD_MAX_BADBLOCKS) {
- rv = -ENOSPC;
- goto out;
- }
- memmove(p+lo+1, p+lo, (bb->count - lo) * 8);
- bb->count++;
- p[lo] = BB_MAKE(a, s-a, ack);
- lo++;
- }
- p[lo] = BB_MAKE(target, end - target, ack);
- /* there is no longer an overlap */
- hi = lo;
- lo--;
- }
- while (lo >= 0 &&
- BB_OFFSET(p[lo]) + BB_LEN(p[lo]) > s) {
- /* This range does overlap */
- if (BB_OFFSET(p[lo]) < s) {
- /* Keep the early parts of this range. */
- int ack = BB_ACK(p[lo]);
- sector_t start = BB_OFFSET(p[lo]);
- p[lo] = BB_MAKE(start, s - start, ack);
- /* now low doesn't overlap, so.. */
- break;
- }
- lo--;
- }
- /* 'lo' is strictly before, 'hi' is strictly after,
- * anything between needs to be discarded
- */
- if (hi - lo > 1) {
- memmove(p+lo+1, p+hi, (bb->count - hi) * 8);
- bb->count -= (hi - lo - 1);
- }
- }
-
- bb->changed = 1;
-out:
- write_sequnlock_irq(&bb->lock);
- return rv;
-}
-
int rdev_clear_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
int is_new)
{
@@ -8849,133 +8556,11 @@ int rdev_clear_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
s += rdev->new_data_offset;
else
s += rdev->data_offset;
- return md_clear_badblocks(&rdev->badblocks,
+ return badblocks_clear(&rdev->badblocks,
s, sectors);
}
EXPORT_SYMBOL_GPL(rdev_clear_badblocks);
-/*
- * Acknowledge all bad blocks in a list.
- * This only succeeds if ->changed is clear. It is used by
- * in-kernel metadata updates
- */
-void md_ack_all_badblocks(struct badblocks *bb)
-{
- if (bb->page == NULL || bb->changed)
- /* no point even trying */
- return;
- write_seqlock_irq(&bb->lock);
-
- if (bb->changed == 0 && bb->unacked_exist) {
- u64 *p = bb->page;
- int i;
- for (i = 0; i < bb->count ; i++) {
- if (!BB_ACK(p[i])) {
- sector_t start = BB_OFFSET(p[i]);
- int len = BB_LEN(p[i]);
- p[i] = BB_MAKE(start, len, 1);
- }
- }
- bb->unacked_exist = 0;
- }
- write_sequnlock_irq(&bb->lock);
-}
-EXPORT_SYMBOL_GPL(md_ack_all_badblocks);
-
-/* sysfs access to bad-blocks list.
- * We present two files.
- * 'bad-blocks' lists sector numbers and lengths of ranges that
- * are recorded as bad. The list is truncated to fit within
- * the one-page limit of sysfs.
- * Writing "sector length" to this file adds an acknowledged
- * bad block list.
- * 'unacknowledged-bad-blocks' lists bad blocks that have not yet
- * been acknowledged. Writing to this file adds bad blocks
- * without acknowledging them. This is largely for testing.
- */
-
-static ssize_t
-badblocks_show(struct badblocks *bb, char *page, int unack)
-{
- size_t len;
- int i;
- u64 *p = bb->page;
- unsigned seq;
-
- if (bb->shift < 0)
- return 0;
-
-retry:
- seq = read_seqbegin(&bb->lock);
-
- len = 0;
- i = 0;
-
- while (len < PAGE_SIZE && i < bb->count) {
- sector_t s = BB_OFFSET(p[i]);
- unsigned int length = BB_LEN(p[i]);
- int ack = BB_ACK(p[i]);
- i++;
-
- if (unack && ack)
- continue;
-
- len += snprintf(page+len, PAGE_SIZE-len, "%llu %u\n",
- (unsigned long long)s << bb->shift,
- length << bb->shift);
- }
- if (unack && len == 0)
- bb->unacked_exist = 0;
-
- if (read_seqretry(&bb->lock, seq))
- goto retry;
-
- return len;
-}
-
-#define DO_DEBUG 1
-
-static ssize_t
-badblocks_store(struct badblocks *bb, const char *page, size_t len, int unack)
-{
- unsigned long long sector;
- int length;
- char newline;
-#ifdef DO_DEBUG
- /* Allow clearing via sysfs *only* for testing/debugging.
- * Normally only a successful write may clear a badblock
- */
- int clear = 0;
- if (page[0] == '-') {
- clear = 1;
- page++;
- }
-#endif /* DO_DEBUG */
-
- switch (sscanf(page, "%llu %d%c", &sector, &length, &newline)) {
- case 3:
- if (newline != '\n')
- return -EINVAL;
- case 2:
- if (length <= 0)
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
-
-#ifdef DO_DEBUG
- if (clear) {
- md_clear_badblocks(bb, sector, length);
- return len;
- }
-#endif /* DO_DEBUG */
- if (md_set_badblocks(bb, sector, length, !unack))
- return len;
- else
- return -ENOSPC;
-}
-
static int md_notify_reboot(struct notifier_block *this,
unsigned long code, void *x)
{
@@ -9090,7 +8675,6 @@ static void check_sb_changes(struct mddev *mddev, struct md_rdev *rdev)
ret = remove_and_add_spares(mddev, rdev2);
pr_info("Activated spare: %s\n",
bdevname(rdev2->bdev,b));
- continue;
}
/* device faulty
* We just want to do the minimum to mark the disk
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 2bea51edfab7..b5c4be73e6e4 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -17,6 +17,7 @@
#include <linux/blkdev.h>
#include <linux/backing-dev.h>
+#include <linux/badblocks.h>
#include <linux/kobject.h>
#include <linux/list.h>
#include <linux/mm.h>
@@ -28,13 +29,6 @@
#define MaxSector (~(sector_t)0)
-/* Bad block numbers are stored sorted in a single page.
- * 64bits is used for each block or extent.
- * 54 bits are sector number, 9 bits are extent size,
- * 1 bit is an 'acknowledged' flag.
- */
-#define MD_MAX_BADBLOCKS (PAGE_SIZE/8)
-
/*
* MD's 'extended' device
*/
@@ -117,22 +111,7 @@ struct md_rdev {
struct kernfs_node *sysfs_state; /* handle for 'state'
* sysfs entry */
- struct badblocks {
- int count; /* count of bad blocks */
- int unacked_exist; /* there probably are unacknowledged
- * bad blocks. This is only cleared
- * when a read discovers none
- */
- int shift; /* shift from sectors to block size
- * a -ve shift means badblocks are
- * disabled.*/
- u64 *page; /* badblock list */
- int changed;
- seqlock_t lock;
-
- sector_t sector;
- sector_t size; /* in sectors */
- } badblocks;
+ struct badblocks badblocks;
};
enum flag_bits {
Faulty, /* device is known to have a fault */
@@ -183,24 +162,14 @@ enum flag_bits {
* Usually, this device should be faster
* than other devices in the array
*/
+ ClusterRemove,
};
-#define BB_LEN_MASK (0x00000000000001FFULL)
-#define BB_OFFSET_MASK (0x7FFFFFFFFFFFFE00ULL)
-#define BB_ACK_MASK (0x8000000000000000ULL)
-#define BB_MAX_LEN 512
-#define BB_OFFSET(x) (((x) & BB_OFFSET_MASK) >> 9)
-#define BB_LEN(x) (((x) & BB_LEN_MASK) + 1)
-#define BB_ACK(x) (!!((x) & BB_ACK_MASK))
-#define BB_MAKE(a, l, ack) (((a)<<9) | ((l)-1) | ((u64)(!!(ack)) << 63))
-
-extern int md_is_badblock(struct badblocks *bb, sector_t s, int sectors,
- sector_t *first_bad, int *bad_sectors);
static inline int is_badblock(struct md_rdev *rdev, sector_t s, int sectors,
sector_t *first_bad, int *bad_sectors)
{
if (unlikely(rdev->badblocks.count)) {
- int rv = md_is_badblock(&rdev->badblocks, rdev->data_offset + s,
+ int rv = badblocks_check(&rdev->badblocks, rdev->data_offset + s,
sectors,
first_bad, bad_sectors);
if (rv)
@@ -213,8 +182,6 @@ extern int rdev_set_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
int is_new);
extern int rdev_clear_badblocks(struct md_rdev *rdev, sector_t s, int sectors,
int is_new);
-extern void md_ack_all_badblocks(struct badblocks *bb);
-
struct md_cluster_info;
struct mddev {
@@ -234,6 +201,9 @@ struct mddev {
*/
#define MD_JOURNAL_CLEAN 5 /* A raid with journal is already clean */
#define MD_HAS_JOURNAL 6 /* The raid array has journal feature set */
+#define MD_RELOAD_SB 7 /* Reload the superblock because another node
+ * updated it.
+ */
int suspended;
atomic_t active_io;
@@ -242,8 +212,6 @@ struct mddev {
* are happening, so run/
* takeover/stop are not safe
*/
- int ready; /* See when safe to pass
- * IO requests down */
struct gendisk *gendisk;
struct kobject kobj;
@@ -260,7 +228,7 @@ struct mddev {
* managed externally */
char metadata_type[17]; /* externally set*/
int chunk_sectors;
- time_t ctime, utime;
+ time64_t ctime, utime;
int level, layout;
char clevel[16];
int raid_disks;
@@ -464,6 +432,7 @@ struct mddev {
struct work_struct event_work; /* used by dm to report failure event */
void (*sync_super)(struct mddev *mddev, struct md_rdev *rdev);
struct md_cluster_info *cluster_info;
+ unsigned int good_device_nr; /* good device num within cluster raid */
};
static inline int __must_check mddev_lock(struct mddev *mddev)
@@ -566,7 +535,9 @@ static inline char * mdname (struct mddev * mddev)
static inline int sysfs_link_rdev(struct mddev *mddev, struct md_rdev *rdev)
{
char nm[20];
- if (!test_bit(Replacement, &rdev->flags) && mddev->kobj.sd) {
+ if (!test_bit(Replacement, &rdev->flags) &&
+ !test_bit(Journal, &rdev->flags) &&
+ mddev->kobj.sd) {
sprintf(nm, "rd%d", rdev->raid_disk);
return sysfs_create_link(&mddev->kobj, &rdev->kobj, nm);
} else
@@ -576,7 +547,9 @@ static inline int sysfs_link_rdev(struct mddev *mddev, struct md_rdev *rdev)
static inline void sysfs_unlink_rdev(struct mddev *mddev, struct md_rdev *rdev)
{
char nm[20];
- if (!test_bit(Replacement, &rdev->flags) && mddev->kobj.sd) {
+ if (!test_bit(Replacement, &rdev->flags) &&
+ !test_bit(Journal, &rdev->flags) &&
+ mddev->kobj.sd) {
sprintf(nm, "rd%d", rdev->raid_disk);
sysfs_remove_link(&mddev->kobj, nm);
}
@@ -653,7 +626,7 @@ extern void md_wait_for_blocked_rdev(struct md_rdev *rdev, struct mddev *mddev);
extern void md_set_array_sectors(struct mddev *mddev, sector_t array_sectors);
extern int md_check_no_bitmap(struct mddev *mddev);
extern int md_integrity_register(struct mddev *mddev);
-extern void md_integrity_add_rdev(struct md_rdev *rdev, struct mddev *mddev);
+extern int md_integrity_add_rdev(struct md_rdev *rdev, struct mddev *mddev);
extern int strict_strtoul_scaled(const char *cp, unsigned long *res, int scale);
extern void mddev_init(struct mddev *mddev);
diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c
index 7331a80d89f1..0a72ab6e6c20 100644
--- a/drivers/md/multipath.c
+++ b/drivers/md/multipath.c
@@ -257,6 +257,9 @@ static int multipath_add_disk(struct mddev *mddev, struct md_rdev *rdev)
disk_stack_limits(mddev->gendisk, rdev->bdev,
rdev->data_offset << 9);
+ err = md_integrity_add_rdev(rdev, mddev);
+ if (err)
+ break;
spin_lock_irq(&conf->device_lock);
mddev->degraded--;
rdev->raid_disk = path;
@@ -264,9 +267,6 @@ static int multipath_add_disk(struct mddev *mddev, struct md_rdev *rdev)
spin_unlock_irq(&conf->device_lock);
rcu_assign_pointer(p->rdev, rdev);
err = 0;
- mddev_suspend(mddev);
- md_integrity_add_rdev(rdev, mddev);
- mddev_resume(mddev);
break;
}
diff --git a/drivers/md/persistent-data/Kconfig b/drivers/md/persistent-data/Kconfig
index 78c74bb71ba4..a53cbc928af1 100644
--- a/drivers/md/persistent-data/Kconfig
+++ b/drivers/md/persistent-data/Kconfig
@@ -7,12 +7,3 @@ config DM_PERSISTENT_DATA
Library providing immutable on-disk data structure support for
device-mapper targets such as the thin provisioning target.
-config DM_DEBUG_BLOCK_STACK_TRACING
- bool "Keep stack trace of persistent data block lock holders"
- depends on STACKTRACE_SUPPORT && DM_PERSISTENT_DATA
- select STACKTRACE
- ---help---
- Enable this for messages that may help debug problems with the
- block manager locking used by thin provisioning and caching.
-
- If unsure, say N.
diff --git a/drivers/md/persistent-data/dm-block-manager.c b/drivers/md/persistent-data/dm-block-manager.c
index f2393ba838eb..1e33dd51c21f 100644
--- a/drivers/md/persistent-data/dm-block-manager.c
+++ b/drivers/md/persistent-data/dm-block-manager.c
@@ -97,10 +97,6 @@ static void __del_holder(struct block_lock *lock, struct task_struct *task)
static int __check_holder(struct block_lock *lock)
{
unsigned i;
-#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
- static struct stack_trace t;
- static stack_entries entries;
-#endif
for (i = 0; i < MAX_HOLDERS; i++) {
if (lock->holders[i] == current) {
@@ -110,12 +106,7 @@ static int __check_holder(struct block_lock *lock)
print_stack_trace(lock->traces + i, 4);
DMERR("subsequent acquisition attempted here:");
- t.nr_entries = 0;
- t.max_entries = MAX_STACK;
- t.entries = entries;
- t.skip = 3;
- save_stack_trace(&t);
- print_stack_trace(&t, 4);
+ dump_stack();
#endif
return -EINVAL;
}
diff --git a/drivers/md/persistent-data/dm-btree.c b/drivers/md/persistent-data/dm-btree.c
index c573402033b2..ea3d3b656fd0 100644
--- a/drivers/md/persistent-data/dm-btree.c
+++ b/drivers/md/persistent-data/dm-btree.c
@@ -63,6 +63,11 @@ int lower_bound(struct btree_node *n, uint64_t key)
return bsearch(n, key, 0);
}
+static int upper_bound(struct btree_node *n, uint64_t key)
+{
+ return bsearch(n, key, 1);
+}
+
void inc_children(struct dm_transaction_manager *tm, struct btree_node *n,
struct dm_btree_value_type *vt)
{
@@ -252,6 +257,16 @@ static void pop_frame(struct del_stack *s)
dm_tm_unlock(s->tm, f->b);
}
+static void unlock_all_frames(struct del_stack *s)
+{
+ struct frame *f;
+
+ while (unprocessed_frames(s)) {
+ f = s->spine + s->top--;
+ dm_tm_unlock(s->tm, f->b);
+ }
+}
+
int dm_btree_del(struct dm_btree_info *info, dm_block_t root)
{
int r;
@@ -308,9 +323,13 @@ int dm_btree_del(struct dm_btree_info *info, dm_block_t root)
pop_frame(s);
}
}
-
out:
+ if (r) {
+ /* cleanup all frames of del_stack */
+ unlock_all_frames(s);
+ }
kfree(s);
+
return r;
}
EXPORT_SYMBOL_GPL(dm_btree_del);
@@ -392,6 +411,82 @@ int dm_btree_lookup(struct dm_btree_info *info, dm_block_t root,
}
EXPORT_SYMBOL_GPL(dm_btree_lookup);
+static int dm_btree_lookup_next_single(struct dm_btree_info *info, dm_block_t root,
+ uint64_t key, uint64_t *rkey, void *value_le)
+{
+ int r, i;
+ uint32_t flags, nr_entries;
+ struct dm_block *node;
+ struct btree_node *n;
+
+ r = bn_read_lock(info, root, &node);
+ if (r)
+ return r;
+
+ n = dm_block_data(node);
+ flags = le32_to_cpu(n->header.flags);
+ nr_entries = le32_to_cpu(n->header.nr_entries);
+
+ if (flags & INTERNAL_NODE) {
+ i = lower_bound(n, key);
+ if (i < 0 || i >= nr_entries) {
+ r = -ENODATA;
+ goto out;
+ }
+
+ r = dm_btree_lookup_next_single(info, value64(n, i), key, rkey, value_le);
+ if (r == -ENODATA && i < (nr_entries - 1)) {
+ i++;
+ r = dm_btree_lookup_next_single(info, value64(n, i), key, rkey, value_le);
+ }
+
+ } else {
+ i = upper_bound(n, key);
+ if (i < 0 || i >= nr_entries) {
+ r = -ENODATA;
+ goto out;
+ }
+
+ *rkey = le64_to_cpu(n->keys[i]);
+ memcpy(value_le, value_ptr(n, i), info->value_type.size);
+ }
+out:
+ dm_tm_unlock(info->tm, node);
+ return r;
+}
+
+int dm_btree_lookup_next(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, uint64_t *rkey, void *value_le)
+{
+ unsigned level;
+ int r = -ENODATA;
+ __le64 internal_value_le;
+ struct ro_spine spine;
+
+ init_ro_spine(&spine, info);
+ for (level = 0; level < info->levels - 1u; level++) {
+ r = btree_lookup_raw(&spine, root, keys[level],
+ lower_bound, rkey,
+ &internal_value_le, sizeof(uint64_t));
+ if (r)
+ goto out;
+
+ if (*rkey != keys[level]) {
+ r = -ENODATA;
+ goto out;
+ }
+
+ root = le64_to_cpu(internal_value_le);
+ }
+
+ r = dm_btree_lookup_next_single(info, root, keys[level], rkey, value_le);
+out:
+ exit_ro_spine(&spine);
+ return r;
+}
+
+EXPORT_SYMBOL_GPL(dm_btree_lookup_next);
+
/*
* Splits a node by creating a sibling node and shifting half the nodes
* contents across. Assumes there is a parent node, and it has room for
@@ -473,8 +568,10 @@ static int btree_split_sibling(struct shadow_spine *s, unsigned parent_index,
r = insert_at(sizeof(__le64), pn, parent_index + 1,
le64_to_cpu(rn->keys[0]), &location);
- if (r)
+ if (r) {
+ unlock_block(s->info, right);
return r;
+ }
if (key < le64_to_cpu(rn->keys[0])) {
unlock_block(s->info, right);
@@ -657,12 +754,19 @@ static int btree_insert_raw(struct shadow_spine *s, dm_block_t root,
return 0;
}
+static bool need_insert(struct btree_node *node, uint64_t *keys,
+ unsigned level, unsigned index)
+{
+ return ((index >= le32_to_cpu(node->header.nr_entries)) ||
+ (le64_to_cpu(node->keys[index]) != keys[level]));
+}
+
static int insert(struct dm_btree_info *info, dm_block_t root,
uint64_t *keys, void *value, dm_block_t *new_root,
int *inserted)
__dm_written_to_disk(value)
{
- int r, need_insert;
+ int r;
unsigned level, index = -1, last_level = info->levels - 1;
dm_block_t block = root;
struct shadow_spine spine;
@@ -678,10 +782,8 @@ static int insert(struct dm_btree_info *info, dm_block_t root,
goto bad;
n = dm_block_data(shadow_current(&spine));
- need_insert = ((index >= le32_to_cpu(n->header.nr_entries)) ||
- (le64_to_cpu(n->keys[index]) != keys[level]));
- if (need_insert) {
+ if (need_insert(n, keys, level, index)) {
dm_block_t new_tree;
__le64 new_le;
@@ -708,10 +810,8 @@ static int insert(struct dm_btree_info *info, dm_block_t root,
goto bad;
n = dm_block_data(shadow_current(&spine));
- need_insert = ((index >= le32_to_cpu(n->header.nr_entries)) ||
- (le64_to_cpu(n->keys[index]) != keys[level]));
- if (need_insert) {
+ if (need_insert(n, keys, level, index)) {
if (inserted)
*inserted = 1;
diff --git a/drivers/md/persistent-data/dm-btree.h b/drivers/md/persistent-data/dm-btree.h
index 11d8cf78621d..c74301fa5a37 100644
--- a/drivers/md/persistent-data/dm-btree.h
+++ b/drivers/md/persistent-data/dm-btree.h
@@ -110,6 +110,13 @@ int dm_btree_lookup(struct dm_btree_info *info, dm_block_t root,
uint64_t *keys, void *value_le);
/*
+ * Tries to find the first key where the bottom level key is >= to that
+ * given. Useful for skipping empty sections of the btree.
+ */
+int dm_btree_lookup_next(struct dm_btree_info *info, dm_block_t root,
+ uint64_t *keys, uint64_t *rkey, void *value_le);
+
+/*
* Insertion (or overwrite an existing value). O(ln(n))
*/
int dm_btree_insert(struct dm_btree_info *info, dm_block_t root,
@@ -135,9 +142,10 @@ int dm_btree_remove(struct dm_btree_info *info, dm_block_t root,
uint64_t *keys, dm_block_t *new_root);
/*
- * Removes values between 'keys' and keys2, where keys2 is keys with the
- * final key replaced with 'end_key'. 'end_key' is the one-past-the-end
- * value. 'keys' may be altered.
+ * Removes a _contiguous_ run of values starting from 'keys' and not
+ * reaching keys2 (where keys2 is keys with the final key replaced with
+ * 'end_key'). 'end_key' is the one-past-the-end value. 'keys' may be
+ * altered.
*/
int dm_btree_remove_leaves(struct dm_btree_info *info, dm_block_t root,
uint64_t *keys, uint64_t end_key,
diff --git a/drivers/md/persistent-data/dm-space-map-metadata.c b/drivers/md/persistent-data/dm-space-map-metadata.c
index 53091295fce9..7e44005595c1 100644
--- a/drivers/md/persistent-data/dm-space-map-metadata.c
+++ b/drivers/md/persistent-data/dm-space-map-metadata.c
@@ -136,7 +136,7 @@ static int brb_push(struct bop_ring_buffer *brb,
return 0;
}
-static int brb_pop(struct bop_ring_buffer *brb, struct block_op *result)
+static int brb_peek(struct bop_ring_buffer *brb, struct block_op *result)
{
struct block_op *bop;
@@ -147,6 +147,14 @@ static int brb_pop(struct bop_ring_buffer *brb, struct block_op *result)
result->type = bop->type;
result->block = bop->block;
+ return 0;
+}
+
+static int brb_pop(struct bop_ring_buffer *brb)
+{
+ if (brb_empty(brb))
+ return -ENODATA;
+
brb->begin = brb_next(brb, brb->begin);
return 0;
@@ -211,7 +219,7 @@ static int apply_bops(struct sm_metadata *smm)
while (!brb_empty(&smm->uncommitted)) {
struct block_op bop;
- r = brb_pop(&smm->uncommitted, &bop);
+ r = brb_peek(&smm->uncommitted, &bop);
if (r) {
DMERR("bug in bop ring buffer");
break;
@@ -220,6 +228,8 @@ static int apply_bops(struct sm_metadata *smm)
r = commit_bop(smm, &bop);
if (r)
break;
+
+ brb_pop(&smm->uncommitted);
}
return r;
@@ -683,7 +693,6 @@ static struct dm_space_map bootstrap_ops = {
static int sm_metadata_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
{
int r, i;
- enum allocation_event ev;
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
dm_block_t old_len = smm->ll.nr_blocks;
@@ -705,11 +714,12 @@ static int sm_metadata_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
* allocate any new blocks.
*/
do {
- for (i = old_len; !r && i < smm->begin; i++) {
- r = sm_ll_inc(&smm->ll, i, &ev);
- if (r)
- goto out;
- }
+ for (i = old_len; !r && i < smm->begin; i++)
+ r = add_bop(smm, BOP_INC, i);
+
+ if (r)
+ goto out;
+
old_len = smm->begin;
r = apply_bops(smm);
@@ -754,7 +764,6 @@ int dm_sm_metadata_create(struct dm_space_map *sm,
{
int r;
dm_block_t i;
- enum allocation_event ev;
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
smm->begin = superblock + 1;
@@ -782,7 +791,7 @@ int dm_sm_metadata_create(struct dm_space_map *sm,
* allocated blocks that they were built from.
*/
for (i = superblock; !r && i < smm->begin; i++)
- r = sm_ll_inc(&smm->ll, i, &ev);
+ r = add_bop(smm, BOP_INC, i);
if (r)
return r;
diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
index f8e5db0cb5aa..2ea12c6bf659 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -549,13 +549,13 @@ static void *raid0_takeover_raid10(struct mddev *mddev)
* - all mirrors must be already degraded
*/
if (mddev->layout != ((1 << 8) + 2)) {
- printk(KERN_ERR "md/raid0:%s:: Raid0 cannot takover layout: 0x%x\n",
+ printk(KERN_ERR "md/raid0:%s:: Raid0 cannot takeover layout: 0x%x\n",
mdname(mddev),
mddev->layout);
return ERR_PTR(-EINVAL);
}
if (mddev->raid_disks & 1) {
- printk(KERN_ERR "md/raid0:%s: Raid0 cannot takover Raid10 with odd disk number.\n",
+ printk(KERN_ERR "md/raid0:%s: Raid0 cannot takeover Raid10 with odd disk number.\n",
mdname(mddev));
return ERR_PTR(-EINVAL);
}
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index e2169ff6e0f0..c4b913409226 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1589,6 +1589,9 @@ static int raid1_add_disk(struct mddev *mddev, struct md_rdev *rdev)
if (mddev->recovery_disabled == conf->recovery_disabled)
return -EBUSY;
+ if (md_integrity_add_rdev(rdev, mddev))
+ return -ENXIO;
+
if (rdev->raid_disk >= 0)
first = last = rdev->raid_disk;
@@ -1632,9 +1635,6 @@ static int raid1_add_disk(struct mddev *mddev, struct md_rdev *rdev)
break;
}
}
- mddev_suspend(mddev);
- md_integrity_add_rdev(rdev, mddev);
- mddev_resume(mddev);
if (mddev->queue && blk_queue_discard(bdev_get_queue(rdev->bdev)))
queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mddev->queue);
print_conf(conf);
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 41d70bc9ba2f..ce959b4ae4df 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1698,6 +1698,9 @@ static int raid10_add_disk(struct mddev *mddev, struct md_rdev *rdev)
if (rdev->saved_raid_disk < 0 && !_enough(conf, 1, -1))
return -EINVAL;
+ if (md_integrity_add_rdev(rdev, mddev))
+ return -ENXIO;
+
if (rdev->raid_disk >= 0)
first = last = rdev->raid_disk;
@@ -1739,9 +1742,6 @@ static int raid10_add_disk(struct mddev *mddev, struct md_rdev *rdev)
rcu_assign_pointer(p->rdev, rdev);
break;
}
- mddev_suspend(mddev);
- md_integrity_add_rdev(rdev, mddev);
- mddev_resume(mddev);
if (mddev->queue && blk_queue_discard(bdev_get_queue(rdev->bdev)))
queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mddev->queue);
@@ -1946,6 +1946,8 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
first = i;
fbio = r10_bio->devs[i].bio;
+ fbio->bi_iter.bi_size = r10_bio->sectors << 9;
+ fbio->bi_iter.bi_idx = 0;
vcnt = (r10_bio->sectors + (PAGE_SIZE >> 9) - 1) >> (PAGE_SHIFT - 9);
/* now find blocks with errors */
@@ -1989,7 +1991,7 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
bio_reset(tbio);
tbio->bi_vcnt = vcnt;
- tbio->bi_iter.bi_size = r10_bio->sectors << 9;
+ tbio->bi_iter.bi_size = fbio->bi_iter.bi_size;
tbio->bi_rw = WRITE;
tbio->bi_private = r10_bio;
tbio->bi_iter.bi_sector = r10_bio->devs[i].addr;
diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
index b887e04d7e5c..9531f5f05b93 100644
--- a/drivers/md/raid5-cache.c
+++ b/drivers/md/raid5-cache.c
@@ -34,6 +34,12 @@
#define RECLAIM_MAX_FREE_SPACE (10 * 1024 * 1024 * 2) /* sector */
#define RECLAIM_MAX_FREE_SPACE_SHIFT (2)
+/*
+ * We only need 2 bios per I/O unit to make progress, but ensure we
+ * have a few more available to not get too tight.
+ */
+#define R5L_POOL_SIZE 4
+
struct r5l_log {
struct md_rdev *rdev;
@@ -69,7 +75,12 @@ struct r5l_log {
struct list_head finished_ios; /* io_units which settle down in log disk */
struct bio flush_bio;
+ struct list_head no_mem_stripes; /* pending stripes, -ENOMEM */
+
struct kmem_cache *io_kc;
+ mempool_t *io_pool;
+ struct bio_set *bs;
+ mempool_t *meta_pool;
struct md_thread *reclaim_thread;
unsigned long reclaim_target; /* number of space that need to be
@@ -150,27 +161,6 @@ static bool r5l_has_free_space(struct r5l_log *log, sector_t size)
return log->device_size > used_size + size;
}
-static void r5l_free_io_unit(struct r5l_log *log, struct r5l_io_unit *io)
-{
- __free_page(io->meta_page);
- kmem_cache_free(log->io_kc, io);
-}
-
-static void r5l_move_io_unit_list(struct list_head *from, struct list_head *to,
- enum r5l_io_unit_state state)
-{
- struct r5l_io_unit *io;
-
- while (!list_empty(from)) {
- io = list_first_entry(from, struct r5l_io_unit, log_sibling);
- /* don't change list order */
- if (io->state >= state)
- list_move_tail(&io->log_sibling, to);
- else
- break;
- }
-}
-
static void __r5l_set_io_unit_state(struct r5l_io_unit *io,
enum r5l_io_unit_state state)
{
@@ -206,6 +196,20 @@ static void r5l_log_run_stripes(struct r5l_log *log)
}
}
+static void r5l_move_to_end_ios(struct r5l_log *log)
+{
+ struct r5l_io_unit *io, *next;
+
+ assert_spin_locked(&log->io_list_lock);
+
+ list_for_each_entry_safe(io, next, &log->running_ios, log_sibling) {
+ /* don't change list order */
+ if (io->state < IO_UNIT_IO_END)
+ break;
+ list_move_tail(&io->log_sibling, &log->io_end_ios);
+ }
+}
+
static void r5l_log_endio(struct bio *bio)
{
struct r5l_io_unit *io = bio->bi_private;
@@ -216,12 +220,12 @@ static void r5l_log_endio(struct bio *bio)
md_error(log->rdev->mddev, log->rdev);
bio_put(bio);
+ mempool_free(io->meta_page, log->meta_pool);
spin_lock_irqsave(&log->io_list_lock, flags);
__r5l_set_io_unit_state(io, IO_UNIT_IO_END);
if (log->need_cache_flush)
- r5l_move_io_unit_list(&log->running_ios, &log->io_end_ios,
- IO_UNIT_IO_END);
+ r5l_move_to_end_ios(log);
else
r5l_log_run_stripes(log);
spin_unlock_irqrestore(&log->io_list_lock, flags);
@@ -255,7 +259,7 @@ static void r5l_submit_current_io(struct r5l_log *log)
static struct bio *r5l_bio_alloc(struct r5l_log *log)
{
- struct bio *bio = bio_kmalloc(GFP_NOIO | __GFP_NOFAIL, BIO_MAX_PAGES);
+ struct bio *bio = bio_alloc_bioset(GFP_NOIO, BIO_MAX_PAGES, log->bs);
bio->bi_rw = WRITE;
bio->bi_bdev = log->rdev->bdev;
@@ -286,15 +290,19 @@ static struct r5l_io_unit *r5l_new_meta(struct r5l_log *log)
struct r5l_io_unit *io;
struct r5l_meta_block *block;
- /* We can't handle memory allocate failure so far */
- io = kmem_cache_zalloc(log->io_kc, GFP_NOIO | __GFP_NOFAIL);
+ io = mempool_alloc(log->io_pool, GFP_ATOMIC);
+ if (!io)
+ return NULL;
+ memset(io, 0, sizeof(*io));
+
io->log = log;
INIT_LIST_HEAD(&io->log_sibling);
INIT_LIST_HEAD(&io->stripe_list);
io->state = IO_UNIT_RUNNING;
- io->meta_page = alloc_page(GFP_NOIO | __GFP_NOFAIL | __GFP_ZERO);
+ io->meta_page = mempool_alloc(log->meta_pool, GFP_NOIO);
block = page_address(io->meta_page);
+ clear_page(block);
block->magic = cpu_to_le32(R5LOG_MAGIC);
block->version = R5LOG_VERSION;
block->seq = cpu_to_le64(log->seq);
@@ -324,8 +332,12 @@ static int r5l_get_meta(struct r5l_log *log, unsigned int payload_size)
log->current_io->meta_offset + payload_size > PAGE_SIZE)
r5l_submit_current_io(log);
- if (!log->current_io)
+ if (!log->current_io) {
log->current_io = r5l_new_meta(log);
+ if (!log->current_io)
+ return -ENOMEM;
+ }
+
return 0;
}
@@ -370,11 +382,12 @@ static void r5l_append_payload_page(struct r5l_log *log, struct page *page)
r5_reserve_log_entry(log, io);
}
-static void r5l_log_stripe(struct r5l_log *log, struct stripe_head *sh,
+static int r5l_log_stripe(struct r5l_log *log, struct stripe_head *sh,
int data_pages, int parity_pages)
{
int i;
int meta_size;
+ int ret;
struct r5l_io_unit *io;
meta_size =
@@ -383,7 +396,10 @@ static void r5l_log_stripe(struct r5l_log *log, struct stripe_head *sh,
sizeof(struct r5l_payload_data_parity) +
sizeof(__le32) * parity_pages;
- r5l_get_meta(log, meta_size);
+ ret = r5l_get_meta(log, meta_size);
+ if (ret)
+ return ret;
+
io = log->current_io;
for (i = 0; i < sh->disks; i++) {
@@ -413,6 +429,8 @@ static void r5l_log_stripe(struct r5l_log *log, struct stripe_head *sh,
list_add_tail(&sh->log_list, &io->stripe_list);
atomic_inc(&io->pending_stripe);
sh->log_io = io;
+
+ return 0;
}
static void r5l_wake_reclaim(struct r5l_log *log, sector_t space);
@@ -427,6 +445,7 @@ int r5l_write_stripe(struct r5l_log *log, struct stripe_head *sh)
int meta_size;
int reserve;
int i;
+ int ret = 0;
if (!log)
return -EAGAIN;
@@ -475,17 +494,22 @@ int r5l_write_stripe(struct r5l_log *log, struct stripe_head *sh)
mutex_lock(&log->io_mutex);
/* meta + data */
reserve = (1 + write_disks) << (PAGE_SHIFT - 9);
- if (r5l_has_free_space(log, reserve))
- r5l_log_stripe(log, sh, data_pages, parity_pages);
- else {
+ if (!r5l_has_free_space(log, reserve)) {
spin_lock(&log->no_space_stripes_lock);
list_add_tail(&sh->log_list, &log->no_space_stripes);
spin_unlock(&log->no_space_stripes_lock);
r5l_wake_reclaim(log, reserve);
+ } else {
+ ret = r5l_log_stripe(log, sh, data_pages, parity_pages);
+ if (ret) {
+ spin_lock_irq(&log->io_list_lock);
+ list_add_tail(&sh->log_list, &log->no_mem_stripes);
+ spin_unlock_irq(&log->io_list_lock);
+ }
}
- mutex_unlock(&log->io_mutex);
+ mutex_unlock(&log->io_mutex);
return 0;
}
@@ -538,6 +562,21 @@ static sector_t r5l_reclaimable_space(struct r5l_log *log)
log->next_checkpoint);
}
+static void r5l_run_no_mem_stripe(struct r5l_log *log)
+{
+ struct stripe_head *sh;
+
+ assert_spin_locked(&log->io_list_lock);
+
+ if (!list_empty(&log->no_mem_stripes)) {
+ sh = list_first_entry(&log->no_mem_stripes,
+ struct stripe_head, log_list);
+ list_del_init(&sh->log_list);
+ set_bit(STRIPE_HANDLE, &sh->state);
+ raid5_release_stripe(sh);
+ }
+}
+
static bool r5l_complete_finished_ios(struct r5l_log *log)
{
struct r5l_io_unit *io, *next;
@@ -554,7 +593,8 @@ static bool r5l_complete_finished_ios(struct r5l_log *log)
log->next_cp_seq = io->seq;
list_del(&io->log_sibling);
- r5l_free_io_unit(log, io);
+ mempool_free(io, log->io_pool);
+ r5l_run_no_mem_stripe(log);
found = true;
}
@@ -787,6 +827,13 @@ void r5l_quiesce(struct r5l_log *log, int state)
return;
if (state == 0) {
log->in_teardown = 0;
+ /*
+ * This is a special case for hotadd. In suspend, the array has
+ * no journal. In resume, journal is initialized as well as the
+ * reclaim thread.
+ */
+ if (log->reclaim_thread)
+ return;
log->reclaim_thread = md_register_thread(r5l_reclaim_thread,
log->rdev->mddev, "reclaim");
} else if (state == 1) {
@@ -806,10 +853,18 @@ void r5l_quiesce(struct r5l_log *log, int state)
bool r5l_log_disk_error(struct r5conf *conf)
{
+ struct r5l_log *log;
+ bool ret;
/* don't allow write if journal disk is missing */
- if (!conf->log)
- return test_bit(MD_HAS_JOURNAL, &conf->mddev->flags);
- return test_bit(Faulty, &conf->log->rdev->flags);
+ rcu_read_lock();
+ log = rcu_dereference(conf->log);
+
+ if (!log)
+ ret = test_bit(MD_HAS_JOURNAL, &conf->mddev->flags);
+ else
+ ret = test_bit(Faulty, &log->rdev->flags);
+ rcu_read_unlock();
+ return ret;
}
struct r5l_recovery_ctx {
@@ -1160,23 +1215,45 @@ int r5l_init_log(struct r5conf *conf, struct md_rdev *rdev)
if (!log->io_kc)
goto io_kc;
+ log->io_pool = mempool_create_slab_pool(R5L_POOL_SIZE, log->io_kc);
+ if (!log->io_pool)
+ goto io_pool;
+
+ log->bs = bioset_create(R5L_POOL_SIZE, 0);
+ if (!log->bs)
+ goto io_bs;
+
+ log->meta_pool = mempool_create_page_pool(R5L_POOL_SIZE, 0);
+ if (!log->meta_pool)
+ goto out_mempool;
+
log->reclaim_thread = md_register_thread(r5l_reclaim_thread,
log->rdev->mddev, "reclaim");
if (!log->reclaim_thread)
goto reclaim_thread;
init_waitqueue_head(&log->iounit_wait);
+ INIT_LIST_HEAD(&log->no_mem_stripes);
+
INIT_LIST_HEAD(&log->no_space_stripes);
spin_lock_init(&log->no_space_stripes_lock);
if (r5l_load_log(log))
goto error;
- conf->log = log;
+ rcu_assign_pointer(conf->log, log);
+ set_bit(MD_HAS_JOURNAL, &conf->mddev->flags);
return 0;
+
error:
md_unregister_thread(&log->reclaim_thread);
reclaim_thread:
+ mempool_destroy(log->meta_pool);
+out_mempool:
+ bioset_free(log->bs);
+io_bs:
+ mempool_destroy(log->io_pool);
+io_pool:
kmem_cache_destroy(log->io_kc);
io_kc:
kfree(log);
@@ -1186,6 +1263,9 @@ io_kc:
void r5l_exit_log(struct r5l_log *log)
{
md_unregister_thread(&log->reclaim_thread);
+ mempool_destroy(log->meta_pool);
+ bioset_free(log->bs);
+ mempool_destroy(log->io_pool);
kmem_cache_destroy(log->io_kc);
kfree(log);
}
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 704ef7fcfbf8..a086014dcd49 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -772,8 +772,6 @@ static void stripe_add_to_batch_list(struct r5conf *conf, struct stripe_head *sh
int hash;
int dd_idx;
- if (!stripe_can_batch(sh))
- return;
/* Don't cross chunks, so stripe pd_idx/qd_idx is the same */
tmp_sec = sh->sector;
if (!sector_div(tmp_sec, conf->chunk_sectors))
@@ -7141,14 +7139,19 @@ static int raid5_remove_disk(struct mddev *mddev, struct md_rdev *rdev)
struct disk_info *p = conf->disks + number;
print_raid5_conf(conf);
- if (test_bit(Journal, &rdev->flags)) {
+ if (test_bit(Journal, &rdev->flags) && conf->log) {
+ struct r5l_log *log;
/*
- * journal disk is not removable, but we need give a chance to
- * update superblock of other disks. Otherwise journal disk
- * will be considered as 'fresh'
+ * we can't wait pending write here, as this is called in
+ * raid5d, wait will deadlock.
*/
- set_bit(MD_CHANGE_DEVS, &mddev->flags);
- return -EINVAL;
+ if (atomic_read(&mddev->writes_pending))
+ return -EBUSY;
+ log = conf->log;
+ conf->log = NULL;
+ synchronize_rcu();
+ r5l_exit_log(log);
+ return 0;
}
if (rdev == p->rdev)
rdevp = &p->rdev;
@@ -7212,8 +7215,21 @@ static int raid5_add_disk(struct mddev *mddev, struct md_rdev *rdev)
int first = 0;
int last = conf->raid_disks - 1;
- if (test_bit(Journal, &rdev->flags))
- return -EINVAL;
+ if (test_bit(Journal, &rdev->flags)) {
+ char b[BDEVNAME_SIZE];
+ if (conf->log)
+ return -EBUSY;
+
+ rdev->raid_disk = 0;
+ /*
+ * The array is in readonly mode if journal is missing, so no
+ * write requests running. We should be safe
+ */
+ r5l_init_log(conf, rdev);
+ printk(KERN_INFO"md/raid:%s: using device %s as journal\n",
+ mdname(mddev), bdevname(rdev->bdev, b));
+ return 0;
+ }
if (mddev->recovery_disabled == conf->recovery_disabled)
return -EBUSY;
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 3ef3d6c6bbf8..a8518fb3bca7 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -9,7 +9,7 @@ menuconfig MEDIA_SUPPORT
If you want to use Webcams, Video grabber devices and/or TV devices
enable this option and other options below.
Additional info and docs are available on the web at
- <http://linuxtv.org>
+ <https://linuxtv.org>
if MEDIA_SUPPORT
@@ -51,7 +51,7 @@ config MEDIA_RADIO_SUPPORT
Enable AM/FM radio support.
Additional info and docs are available on the web at
- <http://linuxtv.org>
+ <https://linuxtv.org>
Say Y when you have a board with radio support.
@@ -97,7 +97,6 @@ config MEDIA_CONTROLLER
config MEDIA_CONTROLLER_DVB
bool "Enable Media controller for DVB (EXPERIMENTAL)"
depends on MEDIA_CONTROLLER
- depends on BROKEN
---help---
Enable the media controller API support for DVB.
diff --git a/drivers/media/common/cx2341x.c b/drivers/media/common/cx2341x.c
index c07b9db51b05..5e4afa0131e6 100644
--- a/drivers/media/common/cx2341x.c
+++ b/drivers/media/common/cx2341x.c
@@ -27,7 +27,7 @@
#include <linux/videodev2.h>
#include <media/tuner.h>
-#include <media/cx2341x.h>
+#include <media/drv-intf/cx2341x.h>
#include <media/v4l2-common.h>
MODULE_DESCRIPTION("cx23415/6/8 driver");
diff --git a/drivers/media/common/saa7146/saa7146_core.c b/drivers/media/common/saa7146/saa7146_core.c
index 1ff9f5323bc3..9f7c5b0a6b45 100644
--- a/drivers/media/common/saa7146/saa7146_core.c
+++ b/drivers/media/common/saa7146/saa7146_core.c
@@ -20,7 +20,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <media/saa7146.h>
+#include <media/drv-intf/saa7146.h>
#include <linux/module.h>
static int saa7146_num;
diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c
index df1e8c975cd8..930d2c94d5d3 100644
--- a/drivers/media/common/saa7146/saa7146_fops.c
+++ b/drivers/media/common/saa7146/saa7146_fops.c
@@ -1,6 +1,6 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <media/saa7146_vv.h>
+#include <media/drv-intf/saa7146_vv.h>
#include <linux/module.h>
/****************************************************************************/
diff --git a/drivers/media/common/saa7146/saa7146_hlp.c b/drivers/media/common/saa7146/saa7146_hlp.c
index 3dc6a838ca6f..6ebcbc6450f5 100644
--- a/drivers/media/common/saa7146/saa7146_hlp.c
+++ b/drivers/media/common/saa7146/saa7146_hlp.c
@@ -2,7 +2,7 @@
#include <linux/kernel.h>
#include <linux/export.h>
-#include <media/saa7146_vv.h>
+#include <media/drv-intf/saa7146_vv.h>
static void calculate_output_format_register(struct saa7146_dev* saa, u32 palette, u32* clip_format)
{
diff --git a/drivers/media/common/saa7146/saa7146_i2c.c b/drivers/media/common/saa7146/saa7146_i2c.c
index 22027198129d..239a2db35068 100644
--- a/drivers/media/common/saa7146/saa7146_i2c.c
+++ b/drivers/media/common/saa7146/saa7146_i2c.c
@@ -1,6 +1,6 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <media/saa7146_vv.h>
+#include <media/drv-intf/saa7146_vv.h>
static u32 saa7146_i2c_func(struct i2c_adapter *adapter)
{
diff --git a/drivers/media/common/saa7146/saa7146_vbi.c b/drivers/media/common/saa7146/saa7146_vbi.c
index 2da995758778..49237518d65f 100644
--- a/drivers/media/common/saa7146/saa7146_vbi.c
+++ b/drivers/media/common/saa7146/saa7146_vbi.c
@@ -1,4 +1,4 @@
-#include <media/saa7146_vv.h>
+#include <media/drv-intf/saa7146_vv.h>
static int vbi_pixel_to_capture = 720 * 2;
diff --git a/drivers/media/common/saa7146/saa7146_video.c b/drivers/media/common/saa7146/saa7146_video.c
index 30779498c173..ea2f3bf7368b 100644
--- a/drivers/media/common/saa7146/saa7146_video.c
+++ b/drivers/media/common/saa7146/saa7146_video.c
@@ -1,6 +1,6 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <media/saa7146_vv.h>
+#include <media/drv-intf/saa7146_vv.h>
#include <media/v4l2-event.h>
#include <media/v4l2-ctrls.h>
#include <linux/module.h>
@@ -502,7 +502,7 @@ static int vidioc_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuf
/* check if overlay is running */
if (IS_OVERLAY_ACTIVE(fh) != 0) {
if (vv->video_fh != fh) {
- DEB_D("refusing to change framebuffer informations while overlay is active in another open\n");
+ DEB_D("refusing to change framebuffer information while overlay is active in another open\n");
return -EBUSY;
}
}
diff --git a/drivers/media/common/siano/smsdvb-main.c b/drivers/media/common/siano/smsdvb-main.c
index f4305ae800f4..d31f468830cf 100644
--- a/drivers/media/common/siano/smsdvb-main.c
+++ b/drivers/media/common/siano/smsdvb-main.c
@@ -617,6 +617,7 @@ static void smsdvb_media_device_unregister(struct smsdvb_client_t *client)
if (!coredev->media_dev)
return;
media_device_unregister(coredev->media_dev);
+ media_device_cleanup(coredev->media_dev);
kfree(coredev->media_dev);
coredev->media_dev = NULL;
#endif
@@ -1183,7 +1184,11 @@ static int smsdvb_hotplug(struct smscore_device_t *coredev,
if (smsdvb_debugfs_create(client) < 0)
pr_info("failed to create debugfs node\n");
- dvb_create_media_graph(&client->adapter);
+ rc = dvb_create_media_graph(&client->adapter, true);
+ if (rc < 0) {
+ pr_err("dvb_create_media_graph failed %d\n", rc);
+ goto client_error;
+ }
pr_info("DVB interface registered.\n");
return 0;
diff --git a/drivers/media/common/siano/smsir.h b/drivers/media/common/siano/smsir.h
index fc8b7925c532..d9abd96ef48b 100644
--- a/drivers/media/common/siano/smsir.h
+++ b/drivers/media/common/siano/smsir.h
@@ -30,8 +30,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <linux/input.h>
#include <media/rc-core.h>
-#define IR_DEFAULT_TIMEOUT 100
-
struct smscore_device_t;
struct ir_t {
diff --git a/drivers/media/dvb-core/demux.h b/drivers/media/dvb-core/demux.h
index ccc1f43cb9a9..6d3b95b8939d 100644
--- a/drivers/media/dvb-core/demux.h
+++ b/drivers/media/dvb-core/demux.h
@@ -32,6 +32,49 @@
#include <linux/time.h>
#include <linux/dvb/dmx.h>
+/**
+ * DOC: Digital TV Demux
+ *
+ * The Kernel Digital TV Demux kABI defines a driver-internal interface for
+ * registering low-level, hardware specific driver to a hardware independent
+ * demux layer. It is only of interest for Digital TV device driver writers.
+ * The header file for this kABI is named demux.h and located in
+ * drivers/media/dvb-core.
+ *
+ * The demux kABI should be implemented for each demux in the system. It is
+ * used to select the TS source of a demux and to manage the demux resources.
+ * When the demux client allocates a resource via the demux kABI, it receives
+ * a pointer to the kABI of that resource.
+ *
+ * Each demux receives its TS input from a DVB front-end or from memory, as
+ * set via this demux kABI. In a system with more than one front-end, the kABI
+ * can be used to select one of the DVB front-ends as a TS source for a demux,
+ * unless this is fixed in the HW platform.
+ *
+ * The demux kABI only controls front-ends regarding to their connections with
+ * demuxes; the kABI used to set the other front-end parameters, such as
+ * tuning, are devined via the Digital TV Frontend kABI.
+ *
+ * The functions that implement the abstract interface demux should be defined
+ * static or module private and registered to the Demux core for external
+ * access. It is not necessary to implement every function in the struct
+ * &dmx_demux. For example, a demux interface might support Section filtering,
+ * but not PES filtering. The kABI client is expected to check the value of any
+ * function pointer before calling the function: the value of NULL means
+ * that the function is not available.
+ *
+ * Whenever the functions of the demux API modify shared data, the
+ * possibilities of lost update and race condition problems should be
+ * addressed, e.g. by protecting parts of code with mutexes.
+ *
+ * Note that functions called from a bottom half context must not sleep.
+ * Even a simple memory allocation without using %GFP_ATOMIC can result in a
+ * kernel thread being put to sleep if swapping is needed. For example, the
+ * Linux Kernel calls the functions of a network device interface from a
+ * bottom half context. Thus, if a demux kABI function is called from network
+ * device code, the function must not sleep.
+ */
+
/*
* Common definitions
*/
@@ -187,8 +230,28 @@ struct dmx_section_feed {
int (*stop_filtering)(struct dmx_section_feed *feed);
};
-/*
- * Callback functions
+/**
+ * DOC: Demux Callback
+ *
+ * This kernel-space API comprises the callback functions that deliver filtered
+ * data to the demux client. Unlike the other DVB kABIs, these functions are
+ * provided by the client and called from the demux code.
+ *
+ * The function pointers of this abstract interface are not packed into a
+ * structure as in the other demux APIs, because the callback functions are
+ * registered and used independent of each other. As an example, it is possible
+ * for the API client to provide several callback functions for receiving TS
+ * packets and no callbacks for PES packets or sections.
+ *
+ * The functions that implement the callback API need not be re-entrant: when
+ * a demux driver calls one of these functions, the driver is not allowed to
+ * call the function again before the original call returns. If a callback is
+ * triggered by a hardware interrupt, it is recommended to use the Linux
+ * bottom half mechanism or start a tasklet instead of making the callback
+ * function call directly from a hardware interrupt.
+ *
+ * This mechanism is implemented by dmx_ts_cb() and dmx_section_cb()
+ * callbacks.
*/
/**
diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c
index ea9abde902e9..a168cbe1c998 100644
--- a/drivers/media/dvb-core/dmxdev.c
+++ b/drivers/media/dvb-core/dmxdev.c
@@ -1244,9 +1244,9 @@ int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter)
}
dvb_register_device(dvb_adapter, &dmxdev->dvbdev, &dvbdev_demux, dmxdev,
- DVB_DEVICE_DEMUX);
+ DVB_DEVICE_DEMUX, dmxdev->filternum);
dvb_register_device(dvb_adapter, &dmxdev->dvr_dvbdev, &dvbdev_dvr,
- dmxdev, DVB_DEVICE_DVR);
+ dmxdev, DVB_DEVICE_DVR, dmxdev->filternum);
dvb_ringbuffer_init(&dmxdev->dvr_buffer, NULL, 8192);
diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h
index 0a46580b5376..1c1c298d2289 100644
--- a/drivers/media/dvb-core/dvb-usb-ids.h
+++ b/drivers/media/dvb-core/dvb-usb-ids.h
@@ -389,4 +389,5 @@
#define USB_PID_PCTV_2002E_SE 0x025d
#define USB_PID_SVEON_STV27 0xd3af
#define USB_PID_TURBOX_DTT_2000 0xd3a4
+#define USB_PID_WINTV_SOLOHD 0x0264
#endif
diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c
index fb66184dc9b6..f82cd1ff4f3a 100644
--- a/drivers/media/dvb-core/dvb_ca_en50221.c
+++ b/drivers/media/dvb-core/dvb_ca_en50221.c
@@ -1695,7 +1695,7 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter,
pubca->private = ca;
/* register the DVB device */
- ret = dvb_register_device(dvb_adapter, &ca->dvbdev, &dvbdev_ca, ca, DVB_DEVICE_CA);
+ ret = dvb_register_device(dvb_adapter, &ca->dvbdev, &dvbdev_ca, ca, DVB_DEVICE_CA, 0);
if (ret)
goto free_slot_info;
diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c
index c38ef1a72b4a..40080645341e 100644
--- a/drivers/media/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb-core/dvb_frontend.c
@@ -622,7 +622,7 @@ static int dvb_enable_media_tuner(struct dvb_frontend *fe)
struct media_device *mdev = adapter->mdev;
struct media_entity *entity, *source;
struct media_link *link, *found_link = NULL;
- int i, ret, n_links = 0, active_links = 0;
+ int ret, n_links = 0, active_links = 0;
fepriv->pipe_start_entity = NULL;
@@ -632,8 +632,7 @@ static int dvb_enable_media_tuner(struct dvb_frontend *fe)
entity = fepriv->dvbdev->entity;
fepriv->pipe_start_entity = entity;
- for (i = 0; i < entity->num_links; i++) {
- link = &entity->links[i];
+ list_for_each_entry(link, &entity->links, list) {
if (link->sink->entity == entity) {
found_link = link;
n_links++;
@@ -659,13 +658,11 @@ static int dvb_enable_media_tuner(struct dvb_frontend *fe)
source = found_link->source->entity;
fepriv->pipe_start_entity = source;
- for (i = 0; i < source->num_links; i++) {
+ list_for_each_entry(link, &source->links, list) {
struct media_entity *sink;
int flags = 0;
- link = &source->links[i];
sink = link->sink->entity;
-
if (sink == entity)
flags = MEDIA_LNK_FL_ENABLED;
@@ -891,21 +888,21 @@ static void dvb_frontend_stop(struct dvb_frontend *fe)
}
/*
- * Sleep until gettimeofday() > waketime + add_usec
- * This needs to be as precise as possible, but as the delay is
- * usually between 2ms and 32ms, it is done using a scheduled msleep
- * followed by usleep (normally a busy-wait loop) for the remainder
+ * Sleep for the amount of time given by add_usec parameter
+ *
+ * This needs to be as precise as possible, as it affects the detection of
+ * the dish tone command at the satellite subsystem. The precision is improved
+ * by using a scheduled msleep followed by udelay for the remainder.
*/
void dvb_frontend_sleep_until(ktime_t *waketime, u32 add_usec)
{
- s32 delta, newdelta;
+ s32 delta;
- ktime_add_us(*waketime, add_usec);
+ *waketime = ktime_add_us(*waketime, add_usec);
delta = ktime_us_delta(ktime_get_real(), *waketime);
if (delta > 2500) {
msleep((delta - 1500) / 1000);
- newdelta = ktime_us_delta(ktime_get_real(), *waketime);
- delta = (newdelta > delta) ? 0 : newdelta;
+ delta = ktime_us_delta(ktime_get_real(), *waketime);
}
if (delta > 0)
udelay(delta);
@@ -2313,9 +2310,9 @@ static int dvb_frontend_ioctl_legacy(struct file *file,
dev_dbg(fe->dvb->device, "%s: current delivery system on cache: %d, V3 type: %d\n",
__func__, c->delivery_system, fe->ops.info.type);
- /* Force the CAN_INVERSION_AUTO bit on. If the frontend doesn't
- * do it, it is done for it. */
- info->caps |= FE_CAN_INVERSION_AUTO;
+ /* Set CAN_INVERSION_AUTO bit on in other than oneshot mode */
+ if (!(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT))
+ info->caps |= FE_CAN_INVERSION_AUTO;
err = 0;
break;
}
@@ -2710,6 +2707,11 @@ int dvb_frontend_resume(struct dvb_frontend *fe)
else if (fe->ops.tuner_ops.init)
ret = fe->ops.tuner_ops.init(fe);
+ if (fe->ops.set_tone && fepriv->tone != -1)
+ fe->ops.set_tone(fe, fepriv->tone);
+ if (fe->ops.set_voltage && fepriv->voltage != -1)
+ fe->ops.set_voltage(fe, fepriv->voltage);
+
fe->exit = DVB_FE_NO_EXIT;
fepriv->state = FESTATE_RETUNE;
dvb_frontend_wakeup(fe);
@@ -2757,7 +2759,7 @@ int dvb_register_frontend(struct dvb_adapter* dvb,
fe->dvb->num, fe->id, fe->ops.info.name);
dvb_register_device (fe->dvb, &fepriv->dvbdev, &dvbdev_template,
- fe, DVB_DEVICE_FRONTEND);
+ fe, DVB_DEVICE_FRONTEND, 0);
/*
* Initialize the cache to the proper values according with the
diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h
index 97661b2f247a..458bcce20e38 100644
--- a/drivers/media/dvb-core/dvb_frontend.h
+++ b/drivers/media/dvb-core/dvb_frontend.h
@@ -42,6 +42,29 @@
#include "dvbdev.h"
+/**
+ * DOC: Digital TV Frontend
+ *
+ * The Digital TV Frontend kABI defines a driver-internal interface for
+ * registering low-level, hardware specific driver to a hardware independent
+ * frontend layer. It is only of interest for Digital TV device driver writers.
+ * The header file for this API is named dvb_frontend.h and located in
+ * drivers/media/dvb-core.
+ *
+ * Before using the Digital TV frontend core, the bridge driver should attach
+ * the frontend demod, tuner and SEC devices and call dvb_register_frontend(),
+ * in order to register the new frontend at the subsystem. At device
+ * detach/removal, the bridge driver should call dvb_unregister_frontend() to
+ * remove the frontend from the core and then dvb_frontend_detach() to free the
+ * memory allocated by the frontend drivers.
+ *
+ * The drivers should also call dvb_frontend_suspend() as part of their
+ * handler for the &device_driver.suspend(), and dvb_frontend_resume() as
+ * part of their handler for &device_driver.resume().
+ *
+ * A few other optional functions are provided to handle some special cases.
+ */
+
/*
* Maximum number of Delivery systems per frontend. It
* should be smaller or equal to 32
@@ -112,16 +135,6 @@ struct analog_parameters {
u64 std;
};
-enum tuner_param {
- DVBFE_TUNER_FREQUENCY = (1 << 0),
- DVBFE_TUNER_TUNERSTEP = (1 << 1),
- DVBFE_TUNER_IFFREQ = (1 << 2),
- DVBFE_TUNER_BANDWIDTH = (1 << 3),
- DVBFE_TUNER_REFCLOCK = (1 << 4),
- DVBFE_TUNER_IQSENSE = (1 << 5),
- DVBFE_TUNER_DUMMY = (1 << 31)
-};
-
/**
* enum dvbfe_algo - defines the algorithm used to tune into a channel
*
@@ -152,15 +165,6 @@ enum dvbfe_algo {
DVBFE_ALGO_RECOVERY = (1 << 31)
};
-struct tuner_state {
- u32 frequency;
- u32 tunerstep;
- u32 ifreq;
- u32 bandwidth;
- u32 iqsense;
- u32 refclock;
-};
-
/**
* enum dvbfe_search - search callback possible return status
*
@@ -209,12 +213,12 @@ enum dvbfe_search {
* are stored at @dvb_frontend.dtv_property_cache;. The
* tuner demod can change the parameters to reflect the
* changes needed for the channel to be tuned, and
- * update statistics.
+ * update statistics. This is the recommended way to set
+ * the tuner parameters and should be used on newer
+ * drivers.
* @set_analog_params: callback function used to tune into an analog TV
* channel on hybrid tuners. It passes @analog_parameters;
* to the driver.
- * @calc_regs: callback function used to pass register data settings
- * for simple tuners.
* @set_config: callback function used to send some tuner-specific
* parameters.
* @get_frequency: get the actual tuned frequency
@@ -227,16 +231,10 @@ enum dvbfe_search {
* via DVBv5 API (@dvb_frontend.dtv_property_cache;).
* @get_afc: Used only by analog TV core. Reports the frequency
* drift due to AFC.
- * @set_frequency: Set a new frequency. Please notice that using
- * set_params is preferred.
- * @set_bandwidth: Set a new frequency. Please notice that using
- * set_params is preferred.
- * @set_state: callback function used on some legacy drivers that
- * don't implement set_params in order to set properties.
- * Shouldn't be used on new drivers.
- * @get_state: callback function used to get properties by some
- * legacy drivers that don't implement set_params.
- * Shouldn't be used on new drivers.
+ * @calc_regs: callback function used to pass register data settings
+ * for simple tuners. Shouldn't be used on newer drivers.
+ * @set_frequency: Set a new frequency. Shouldn't be used on newer drivers.
+ * @set_bandwidth: Set a new frequency. Shouldn't be used on newer drivers.
*
* NOTE: frequencies used on get_frequency and set_frequency are in Hz for
* terrestrial/cable or kHz for satellite.
@@ -252,14 +250,10 @@ struct dvb_tuner_ops {
int (*suspend)(struct dvb_frontend *fe);
int (*resume)(struct dvb_frontend *fe);
- /** This is for simple PLLs - set all parameters in one go. */
+ /* This is the recomended way to set the tuner */
int (*set_params)(struct dvb_frontend *fe);
int (*set_analog_params)(struct dvb_frontend *fe, struct analog_parameters *p);
- /** This is support for demods like the mt352 - fills out the supplied buffer with what to write. */
- int (*calc_regs)(struct dvb_frontend *fe, u8 *buf, int buf_len);
-
- /** This is to allow setting tuner-specific configs */
int (*set_config)(struct dvb_frontend *fe, void *priv_cfg);
int (*get_frequency)(struct dvb_frontend *fe, u32 *frequency);
@@ -272,17 +266,23 @@ struct dvb_tuner_ops {
int (*get_rf_strength)(struct dvb_frontend *fe, u16 *strength);
int (*get_afc)(struct dvb_frontend *fe, s32 *afc);
- /** These are provided separately from set_params in order to facilitate silicon
- * tuners which require sophisticated tuning loops, controlling each parameter separately. */
- int (*set_frequency)(struct dvb_frontend *fe, u32 frequency);
- int (*set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth);
+ /*
+ * This is support for demods like the mt352 - fills out the supplied
+ * buffer with what to write.
+ *
+ * Don't use on newer drivers.
+ */
+ int (*calc_regs)(struct dvb_frontend *fe, u8 *buf, int buf_len);
/*
- * These are provided separately from set_params in order to facilitate silicon
- * tuners which require sophisticated tuning loops, controlling each parameter separately.
+ * These are provided separately from set_params in order to
+ * facilitate silicon tuners which require sophisticated tuning loops,
+ * controlling each parameter separately.
+ *
+ * Don't use on newer drivers.
*/
- int (*set_state)(struct dvb_frontend *fe, enum tuner_param param, struct tuner_state *state);
- int (*get_state)(struct dvb_frontend *fe, enum tuner_param param, struct tuner_state *state);
+ int (*set_frequency)(struct dvb_frontend *fe, u32 frequency);
+ int (*set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth);
};
/**
@@ -404,6 +404,11 @@ struct dtv_frontend_properties;
* FE_ENABLE_HIGH_LNB_VOLTAGE ioctl (only Satellite).
* @dishnetwork_send_legacy_command: callback function to implement the
* FE_DISHNETWORK_SEND_LEGACY_CMD ioctl (only Satellite).
+ * Drivers should not use this, except when the DVB
+ * core emulation fails to provide proper support (e.g.
+ * if set_voltage() takes more than 8ms to work), and
+ * when backward compatibility with this legacy API is
+ * required.
* @i2c_gate_ctrl: controls the I2C gate. Newer drivers should use I2C
* mux support instead.
* @ts_bus_ctrl: callback function used to take control of the TS bus.
@@ -466,7 +471,8 @@ struct dvb_frontend_ops {
int (*ts_bus_ctrl)(struct dvb_frontend* fe, int acquire);
int (*set_lna)(struct dvb_frontend *);
- /* These callbacks are for devices that implement their own
+ /*
+ * These callbacks are for devices that implement their own
* tuning algorithms, rather than a simple swzigzag
*/
enum dvbfe_search (*search)(struct dvb_frontend *fe);
@@ -682,17 +688,126 @@ struct dvb_frontend {
unsigned int exit;
};
-extern int dvb_register_frontend(struct dvb_adapter *dvb,
+/**
+ * dvb_register_frontend() - Registers a DVB frontend at the adapter
+ *
+ * @dvb: pointer to the dvb adapter
+ * @fe: pointer to the frontend struct
+ *
+ * Allocate and initialize the private data needed by the frontend core to
+ * manage the frontend and calls dvb_register_device() to register a new
+ * frontend. It also cleans the property cache that stores the frontend
+ * parameters and selects the first available delivery system.
+ */
+int dvb_register_frontend(struct dvb_adapter *dvb,
struct dvb_frontend *fe);
-extern int dvb_unregister_frontend(struct dvb_frontend *fe);
+/**
+ * dvb_unregister_frontend() - Unregisters a DVB frontend
+ *
+ * @fe: pointer to the frontend struct
+ *
+ * Stops the frontend kthread, calls dvb_unregister_device() and frees the
+ * private frontend data allocated by dvb_register_frontend().
+ *
+ * NOTE: This function doesn't frees the memory allocated by the demod,
+ * by the SEC driver and by the tuner. In order to free it, an explicit call to
+ * dvb_frontend_detach() is needed, after calling this function.
+ */
+int dvb_unregister_frontend(struct dvb_frontend *fe);
-extern void dvb_frontend_detach(struct dvb_frontend *fe);
+/**
+ * dvb_frontend_detach() - Detaches and frees frontend specific data
+ *
+ * @fe: pointer to the frontend struct
+ *
+ * This function should be called after dvb_unregister_frontend(). It
+ * calls the SEC, tuner and demod release functions:
+ * &dvb_frontend_ops.release_sec, &dvb_frontend_ops.tuner_ops.release,
+ * &dvb_frontend_ops.analog_ops.release and &dvb_frontend_ops.release.
+ *
+ * If the driver is compiled with CONFIG_MEDIA_ATTACH, it also decreases
+ * the module reference count, needed to allow userspace to remove the
+ * previously used DVB frontend modules.
+ */
+void dvb_frontend_detach(struct dvb_frontend *fe);
+
+/**
+ * dvb_frontend_suspend() - Suspends a Digital TV frontend
+ *
+ * @fe: pointer to the frontend struct
+ *
+ * This function prepares a Digital TV frontend to suspend.
+ *
+ * In order to prepare the tuner to suspend, if
+ * &dvb_frontend_ops.tuner_ops.suspend() is available, it calls it. Otherwise,
+ * it will call &dvb_frontend_ops.tuner_ops.sleep(), if available.
+ *
+ * It will also call &dvb_frontend_ops.sleep() to put the demod to suspend.
+ *
+ * The drivers should also call dvb_frontend_suspend() as part of their
+ * handler for the &device_driver.suspend().
+ */
+int dvb_frontend_suspend(struct dvb_frontend *fe);
+
+/**
+ * dvb_frontend_resume() - Resumes a Digital TV frontend
+ *
+ * @fe: pointer to the frontend struct
+ *
+ * This function resumes the usual operation of the tuner after resume.
+ *
+ * In order to resume the frontend, it calls the demod &dvb_frontend_ops.init().
+ *
+ * If &dvb_frontend_ops.tuner_ops.resume() is available, It, it calls it.
+ * Otherwise,t will call &dvb_frontend_ops.tuner_ops.init(), if available.
+ *
+ * Once tuner and demods are resumed, it will enforce that the SEC voltage and
+ * tone are restored to their previous values and wake up the frontend's
+ * kthread in order to retune the frontend.
+ *
+ * The drivers should also call dvb_frontend_resume() as part of their
+ * handler for the &device_driver.resume().
+ */
+int dvb_frontend_resume(struct dvb_frontend *fe);
-extern void dvb_frontend_reinitialise(struct dvb_frontend *fe);
-extern int dvb_frontend_suspend(struct dvb_frontend *fe);
-extern int dvb_frontend_resume(struct dvb_frontend *fe);
+/**
+ * dvb_frontend_reinitialise() - forces a reinitialisation at the frontend
+ *
+ * @fe: pointer to the frontend struct
+ *
+ * Calls &dvb_frontend_ops.init() and &dvb_frontend_ops.tuner_ops.init(),
+ * and resets SEC tone and voltage (for Satellite systems).
+ *
+ * NOTE: Currently, this function is used only by one driver (budget-av).
+ * It seems to be due to address some special issue with that specific
+ * frontend.
+ */
+void dvb_frontend_reinitialise(struct dvb_frontend *fe);
-extern void dvb_frontend_sleep_until(ktime_t *waketime, u32 add_usec);
+/**
+ * dvb_frontend_sleep_until() - Sleep for the amount of time given by
+ * add_usec parameter
+ *
+ * @waketime: pointer to a struct ktime_t
+ * @add_usec: time to sleep, in microseconds
+ *
+ * This function is used to measure the time required for the
+ * %FE_DISHNETWORK_SEND_LEGACY_CMD ioctl to work. It needs to be as precise
+ * as possible, as it affects the detection of the dish tone command at the
+ * satellite subsystem.
+ *
+ * Its used internally by the DVB frontend core, in order to emulate
+ * %FE_DISHNETWORK_SEND_LEGACY_CMD using the &dvb_frontend_ops.set_voltage()
+ * callback.
+ *
+ * NOTE: it should not be used at the drivers, as the emulation for the
+ * legacy callback is provided by the Kernel. The only situation where this
+ * should be at the drivers is when there are some bugs at the hardware that
+ * would prevent the core emulation to work. On such cases, the driver would
+ * be writing a &dvb_frontend_ops.dishnetwork_send_legacy_command() and
+ * calling this function directly.
+ */
+void dvb_frontend_sleep_until(ktime_t *waketime, u32 add_usec);
#endif
diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c
index ce4332e80a91..ce6a711b42d4 100644
--- a/drivers/media/dvb-core/dvb_net.c
+++ b/drivers/media/dvb-core/dvb_net.c
@@ -1502,6 +1502,6 @@ int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet,
dvbnet->state[i] = 0;
return dvb_register_device(adap, &dvbnet->dvbdev, &dvbdev_net,
- dvbnet, DVB_DEVICE_NET);
+ dvbnet, DVB_DEVICE_NET, 0);
}
EXPORT_SYMBOL(dvb_net_init);
diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c
index 13bb57f0457f..560450a0b32a 100644
--- a/drivers/media/dvb-core/dvbdev.c
+++ b/drivers/media/dvb-core/dvbdev.c
@@ -34,6 +34,9 @@
#include <linux/mutex.h>
#include "dvbdev.h"
+/* Due to enum tuner_pad_index */
+#include <media/tuner.h>
+
static DEFINE_MUTEX(dvbdev_mutex);
static int dvbdev_debug;
@@ -180,102 +183,255 @@ skip:
return -ENFILE;
}
-static void dvb_register_media_device(struct dvb_device *dvbdev,
- int type, int minor)
+static void dvb_media_device_free(struct dvb_device *dvbdev)
{
#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
- int ret = 0, npads;
+ if (dvbdev->entity) {
+ media_device_unregister_entity(dvbdev->entity);
+ kfree(dvbdev->entity);
+ kfree(dvbdev->pads);
+ dvbdev->entity = NULL;
+ dvbdev->pads = NULL;
+ }
- if (!dvbdev->adapter->mdev)
- return;
+ if (dvbdev->tsout_entity) {
+ int i;
- dvbdev->entity = kzalloc(sizeof(*dvbdev->entity), GFP_KERNEL);
- if (!dvbdev->entity)
- return;
+ for (i = 0; i < dvbdev->tsout_num_entities; i++) {
+ media_device_unregister_entity(&dvbdev->tsout_entity[i]);
+ kfree(dvbdev->tsout_entity[i].name);
+ }
+ kfree(dvbdev->tsout_entity);
+ kfree(dvbdev->tsout_pads);
+ dvbdev->tsout_entity = NULL;
+ dvbdev->tsout_pads = NULL;
- dvbdev->entity->info.dev.major = DVB_MAJOR;
- dvbdev->entity->info.dev.minor = minor;
- dvbdev->entity->name = dvbdev->name;
+ dvbdev->tsout_num_entities = 0;
+ }
+
+ if (dvbdev->intf_devnode) {
+ media_devnode_remove(dvbdev->intf_devnode);
+ dvbdev->intf_devnode = NULL;
+ }
+
+ if (dvbdev->adapter->conn) {
+ media_device_unregister_entity(dvbdev->adapter->conn);
+ dvbdev->adapter->conn = NULL;
+ kfree(dvbdev->adapter->conn_pads);
+ dvbdev->adapter->conn_pads = NULL;
+ }
+#endif
+}
+
+#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
+static int dvb_create_tsout_entity(struct dvb_device *dvbdev,
+ const char *name, int npads)
+{
+ int i, ret = 0;
+
+ dvbdev->tsout_pads = kcalloc(npads, sizeof(*dvbdev->tsout_pads),
+ GFP_KERNEL);
+ if (!dvbdev->tsout_pads)
+ return -ENOMEM;
+
+ dvbdev->tsout_entity = kcalloc(npads, sizeof(*dvbdev->tsout_entity),
+ GFP_KERNEL);
+ if (!dvbdev->tsout_entity)
+ return -ENOMEM;
+
+ dvbdev->tsout_num_entities = npads;
+
+ for (i = 0; i < npads; i++) {
+ struct media_pad *pads = &dvbdev->tsout_pads[i];
+ struct media_entity *entity = &dvbdev->tsout_entity[i];
+
+ entity->name = kasprintf(GFP_KERNEL, "%s #%d", name, i);
+ if (!entity->name)
+ return -ENOMEM;
+
+ entity->function = MEDIA_ENT_F_IO_DTV;
+ pads->flags = MEDIA_PAD_FL_SINK;
+
+ ret = media_entity_pads_init(entity, 1, pads);
+ if (ret < 0)
+ return ret;
+
+ ret = media_device_register_entity(dvbdev->adapter->mdev,
+ entity);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+#define DEMUX_TSOUT "demux-tsout"
+#define DVR_TSOUT "dvr-tsout"
+
+static int dvb_create_media_entity(struct dvb_device *dvbdev,
+ int type, int demux_sink_pads)
+{
+ int i, ret, npads;
switch (type) {
- case DVB_DEVICE_CA:
- case DVB_DEVICE_DEMUX:
case DVB_DEVICE_FRONTEND:
npads = 2;
break;
- case DVB_DEVICE_NET:
- npads = 0;
+ case DVB_DEVICE_DVR:
+ ret = dvb_create_tsout_entity(dvbdev, DVR_TSOUT,
+ demux_sink_pads);
+ return ret;
+ case DVB_DEVICE_DEMUX:
+ npads = 1 + demux_sink_pads;
+ ret = dvb_create_tsout_entity(dvbdev, DEMUX_TSOUT,
+ demux_sink_pads);
+ if (ret < 0)
+ return ret;
+ break;
+ case DVB_DEVICE_CA:
+ npads = 2;
break;
+ case DVB_DEVICE_NET:
+ /*
+ * We should be creating entities for the MPE/ULE
+ * decapsulation hardware (or software implementation).
+ *
+ * However, the number of for the MPE/ULE decaps may not be
+ * fixed. As we don't have yet dynamic support for PADs at
+ * the Media Controller, let's not create the decap
+ * entities yet.
+ */
+ return 0;
default:
- npads = 1;
+ return 0;
}
+ dvbdev->entity = kzalloc(sizeof(*dvbdev->entity), GFP_KERNEL);
+ if (!dvbdev->entity)
+ return -ENOMEM;
+
+ dvbdev->entity->name = dvbdev->name;
+
if (npads) {
dvbdev->pads = kcalloc(npads, sizeof(*dvbdev->pads),
GFP_KERNEL);
- if (!dvbdev->pads) {
- kfree(dvbdev->entity);
- return;
- }
+ if (!dvbdev->pads)
+ return -ENOMEM;
}
switch (type) {
case DVB_DEVICE_FRONTEND:
- dvbdev->entity->type = MEDIA_ENT_T_DEVNODE_DVB_FE;
+ dvbdev->entity->function = MEDIA_ENT_F_DTV_DEMOD;
dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK;
dvbdev->pads[1].flags = MEDIA_PAD_FL_SOURCE;
break;
case DVB_DEVICE_DEMUX:
- dvbdev->entity->type = MEDIA_ENT_T_DEVNODE_DVB_DEMUX;
- dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK;
- dvbdev->pads[1].flags = MEDIA_PAD_FL_SOURCE;
- break;
- case DVB_DEVICE_DVR:
- dvbdev->entity->type = MEDIA_ENT_T_DEVNODE_DVB_DVR;
+ dvbdev->entity->function = MEDIA_ENT_F_TS_DEMUX;
dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK;
+ for (i = 1; i < npads; i++)
+ dvbdev->pads[i].flags = MEDIA_PAD_FL_SOURCE;
break;
case DVB_DEVICE_CA:
- dvbdev->entity->type = MEDIA_ENT_T_DEVNODE_DVB_CA;
+ dvbdev->entity->function = MEDIA_ENT_F_DTV_CA;
dvbdev->pads[0].flags = MEDIA_PAD_FL_SINK;
dvbdev->pads[1].flags = MEDIA_PAD_FL_SOURCE;
break;
- case DVB_DEVICE_NET:
- dvbdev->entity->type = MEDIA_ENT_T_DEVNODE_DVB_NET;
- break;
default:
+ /* Should never happen, as the first switch prevents it */
kfree(dvbdev->entity);
+ kfree(dvbdev->pads);
dvbdev->entity = NULL;
- return;
+ dvbdev->pads = NULL;
+ return 0;
}
- if (npads)
- ret = media_entity_init(dvbdev->entity, npads, dvbdev->pads, 0);
- if (!ret)
- ret = media_device_register_entity(dvbdev->adapter->mdev,
- dvbdev->entity);
- if (ret < 0) {
- printk(KERN_ERR
- "%s: media_device_register_entity failed for %s\n",
- __func__, dvbdev->entity->name);
- kfree(dvbdev->pads);
- kfree(dvbdev->entity);
- dvbdev->entity = NULL;
- return;
+ if (npads) {
+ ret = media_entity_pads_init(dvbdev->entity, npads, dvbdev->pads);
+ if (ret)
+ return ret;
}
+ ret = media_device_register_entity(dvbdev->adapter->mdev,
+ dvbdev->entity);
+ if (ret)
+ return (ret);
- printk(KERN_DEBUG "%s: media device '%s' registered.\n",
+ printk(KERN_DEBUG "%s: media entity '%s' registered.\n",
__func__, dvbdev->entity->name);
+
+ return 0;
+}
+#endif
+
+static int dvb_register_media_device(struct dvb_device *dvbdev,
+ int type, int minor,
+ unsigned demux_sink_pads)
+{
+#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
+ struct media_link *link;
+ u32 intf_type;
+ int ret;
+
+ if (!dvbdev->adapter->mdev)
+ return 0;
+
+ ret = dvb_create_media_entity(dvbdev, type, demux_sink_pads);
+ if (ret)
+ return ret;
+
+ switch (type) {
+ case DVB_DEVICE_FRONTEND:
+ intf_type = MEDIA_INTF_T_DVB_FE;
+ break;
+ case DVB_DEVICE_DEMUX:
+ intf_type = MEDIA_INTF_T_DVB_DEMUX;
+ break;
+ case DVB_DEVICE_DVR:
+ intf_type = MEDIA_INTF_T_DVB_DVR;
+ break;
+ case DVB_DEVICE_CA:
+ intf_type = MEDIA_INTF_T_DVB_CA;
+ break;
+ case DVB_DEVICE_NET:
+ intf_type = MEDIA_INTF_T_DVB_NET;
+ break;
+ default:
+ return 0;
+ }
+
+ dvbdev->intf_devnode = media_devnode_create(dvbdev->adapter->mdev,
+ intf_type, 0,
+ DVB_MAJOR, minor);
+
+ if (!dvbdev->intf_devnode)
+ return -ENOMEM;
+
+ /*
+ * Create the "obvious" link, e. g. the ones that represent
+ * a direct association between an interface and an entity.
+ * Other links should be created elsewhere, like:
+ * DVB FE intf -> tuner
+ * DVB demux intf -> dvr
+ */
+
+ if (!dvbdev->entity)
+ return 0;
+
+ link = media_create_intf_link(dvbdev->entity, &dvbdev->intf_devnode->intf,
+ MEDIA_LNK_FL_ENABLED);
+ if (!link)
+ return -ENOMEM;
#endif
+ return 0;
}
int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
- const struct dvb_device *template, void *priv, int type)
+ const struct dvb_device *template, void *priv, int type,
+ int demux_sink_pads)
{
struct dvb_device *dvbdev;
struct file_operations *dvbdevfops;
struct device *clsdev;
int minor;
- int id;
+ int id, ret;
mutex_lock(&dvbdev_register_lock);
@@ -286,7 +442,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
return -ENFILE;
}
- *pdvbdev = dvbdev = kmalloc(sizeof(struct dvb_device), GFP_KERNEL);
+ *pdvbdev = dvbdev = kzalloc(sizeof(*dvbdev), GFP_KERNEL);
if (!dvbdev){
mutex_unlock(&dvbdev_register_lock);
@@ -335,6 +491,20 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
dvb_minors[minor] = dvbdev;
up_write(&minor_rwsem);
+ ret = dvb_register_media_device(dvbdev, type, minor, demux_sink_pads);
+ if (ret) {
+ printk(KERN_ERR
+ "%s: dvb_register_media_device failed to create the mediagraph\n",
+ __func__);
+
+ dvb_media_device_free(dvbdev);
+ kfree(dvbdevfops);
+ kfree(dvbdev);
+ up_write(&minor_rwsem);
+ mutex_unlock(&dvbdev_register_lock);
+ return ret;
+ }
+
mutex_unlock(&dvbdev_register_lock);
clsdev = device_create(dvb_class, adap->device,
@@ -348,8 +518,6 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
dprintk(KERN_DEBUG "DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
adap->num, dnames[type], id, minor, minor);
- dvb_register_media_device(dvbdev, type, minor);
-
return 0;
}
EXPORT_SYMBOL(dvb_register_device);
@@ -364,15 +532,9 @@ void dvb_unregister_device(struct dvb_device *dvbdev)
dvb_minors[dvbdev->minor] = NULL;
up_write(&minor_rwsem);
- device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor));
+ dvb_media_device_free(dvbdev);
-#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
- if (dvbdev->entity) {
- media_device_unregister_entity(dvbdev->entity);
- kfree(dvbdev->entity);
- kfree(dvbdev->pads);
- }
-#endif
+ device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor));
list_del (&dvbdev->list_head);
kfree (dvbdev->fops);
@@ -382,46 +544,212 @@ EXPORT_SYMBOL(dvb_unregister_device);
#ifdef CONFIG_MEDIA_CONTROLLER_DVB
-void dvb_create_media_graph(struct dvb_adapter *adap)
+
+static int dvb_create_io_intf_links(struct dvb_adapter *adap,
+ struct media_interface *intf,
+ char *name)
+{
+ struct media_device *mdev = adap->mdev;
+ struct media_entity *entity;
+ struct media_link *link;
+
+ media_device_for_each_entity(entity, mdev) {
+ if (entity->function == MEDIA_ENT_F_IO_DTV) {
+ if (strncmp(entity->name, name, strlen(name)))
+ continue;
+ link = media_create_intf_link(entity, intf,
+ MEDIA_LNK_FL_ENABLED);
+ if (!link)
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+int dvb_create_media_graph(struct dvb_adapter *adap,
+ bool create_rf_connector)
{
struct media_device *mdev = adap->mdev;
- struct media_entity *entity, *tuner = NULL, *fe = NULL;
- struct media_entity *demux = NULL, *dvr = NULL, *ca = NULL;
+ struct media_entity *entity, *tuner = NULL, *demod = NULL, *conn;
+ struct media_entity *demux = NULL, *ca = NULL;
+ struct media_link *link;
+ struct media_interface *intf;
+ unsigned demux_pad = 0;
+ unsigned dvr_pad = 0;
+ unsigned ntuner = 0, ndemod = 0;
+ int ret;
+ static const char *connector_name = "Television";
if (!mdev)
- return;
+ return 0;
media_device_for_each_entity(entity, mdev) {
- switch (entity->type) {
- case MEDIA_ENT_T_V4L2_SUBDEV_TUNER:
+ switch (entity->function) {
+ case MEDIA_ENT_F_TUNER:
tuner = entity;
+ ntuner++;
break;
- case MEDIA_ENT_T_DEVNODE_DVB_FE:
- fe = entity;
+ case MEDIA_ENT_F_DTV_DEMOD:
+ demod = entity;
+ ndemod++;
break;
- case MEDIA_ENT_T_DEVNODE_DVB_DEMUX:
+ case MEDIA_ENT_F_TS_DEMUX:
demux = entity;
break;
- case MEDIA_ENT_T_DEVNODE_DVB_DVR:
- dvr = entity;
- break;
- case MEDIA_ENT_T_DEVNODE_DVB_CA:
+ case MEDIA_ENT_F_DTV_CA:
ca = entity;
break;
}
}
- if (tuner && fe)
- media_entity_create_link(tuner, 0, fe, 0, 0);
+ /*
+ * Prepare to signalize to media_create_pad_links() that multiple
+ * entities of the same type exists and a 1:n or n:1 links need to be
+ * created.
+ * NOTE: if both tuner and demod have multiple instances, it is up
+ * to the caller driver to create such links.
+ */
+ if (ntuner > 1)
+ tuner = NULL;
+ if (ndemod > 1)
+ demod = NULL;
+
+ if (create_rf_connector) {
+ conn = kzalloc(sizeof(*conn), GFP_KERNEL);
+ if (!conn)
+ return -ENOMEM;
+ adap->conn = conn;
+
+ adap->conn_pads = kcalloc(1, sizeof(*adap->conn_pads),
+ GFP_KERNEL);
+ if (!adap->conn_pads)
+ return -ENOMEM;
+
+ conn->flags = MEDIA_ENT_FL_CONNECTOR;
+ conn->function = MEDIA_ENT_F_CONN_RF;
+ conn->name = connector_name;
+ adap->conn_pads->flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(conn, 1, adap->conn_pads);
+ if (ret)
+ return ret;
+
+ ret = media_device_register_entity(mdev, conn);
+ if (ret)
+ return ret;
+
+ if (!ntuner)
+ ret = media_create_pad_links(mdev,
+ MEDIA_ENT_F_CONN_RF,
+ conn, 0,
+ MEDIA_ENT_F_DTV_DEMOD,
+ demod, 0,
+ MEDIA_LNK_FL_ENABLED,
+ false);
+ else
+ ret = media_create_pad_links(mdev,
+ MEDIA_ENT_F_CONN_RF,
+ conn, 0,
+ MEDIA_ENT_F_TUNER,
+ tuner, TUNER_PAD_RF_INPUT,
+ MEDIA_LNK_FL_ENABLED,
+ false);
+ if (ret)
+ return ret;
+ }
+
+ if (ntuner && ndemod) {
+ ret = media_create_pad_links(mdev,
+ MEDIA_ENT_F_TUNER,
+ tuner, TUNER_PAD_IF_OUTPUT,
+ MEDIA_ENT_F_DTV_DEMOD,
+ demod, 0, MEDIA_LNK_FL_ENABLED,
+ false);
+ if (ret)
+ return ret;
+ }
+
+ if (ndemod && demux) {
+ ret = media_create_pad_links(mdev,
+ MEDIA_ENT_F_DTV_DEMOD,
+ demod, 1,
+ MEDIA_ENT_F_TS_DEMUX,
+ demux, 0, MEDIA_LNK_FL_ENABLED,
+ false);
+ if (ret)
+ return -ENOMEM;
+ }
+ if (demux && ca) {
+ ret = media_create_pad_link(demux, 1, ca,
+ 0, MEDIA_LNK_FL_ENABLED);
+ if (!ret)
+ return -ENOMEM;
+ }
- if (fe && demux)
- media_entity_create_link(fe, 1, demux, 0, MEDIA_LNK_FL_ENABLED);
+ /* Create demux links for each ringbuffer/pad */
+ if (demux) {
+ media_device_for_each_entity(entity, mdev) {
+ if (entity->function == MEDIA_ENT_F_IO_DTV) {
+ if (!strncmp(entity->name, DVR_TSOUT,
+ strlen(DVR_TSOUT))) {
+ ret = media_create_pad_link(demux,
+ ++dvr_pad,
+ entity, 0, 0);
+ if (ret)
+ return ret;
+ }
+ if (!strncmp(entity->name, DEMUX_TSOUT,
+ strlen(DEMUX_TSOUT))) {
+ ret = media_create_pad_link(demux,
+ ++demux_pad,
+ entity, 0, 0);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+ }
- if (demux && dvr)
- media_entity_create_link(demux, 1, dvr, 0, MEDIA_LNK_FL_ENABLED);
+ /* Create interface links for FE->tuner, DVR->demux and CA->ca */
+ media_device_for_each_intf(intf, mdev) {
+ if (intf->type == MEDIA_INTF_T_DVB_CA && ca) {
+ link = media_create_intf_link(ca, intf,
+ MEDIA_LNK_FL_ENABLED);
+ if (!link)
+ return -ENOMEM;
+ }
- if (demux && ca)
- media_entity_create_link(demux, 1, ca, 0, MEDIA_LNK_FL_ENABLED);
+ if (intf->type == MEDIA_INTF_T_DVB_FE && tuner) {
+ link = media_create_intf_link(tuner, intf,
+ MEDIA_LNK_FL_ENABLED);
+ if (!link)
+ return -ENOMEM;
+ }
+#if 0
+ /*
+ * Indirect link - let's not create yet, as we don't know how
+ * to handle indirect links, nor if this will
+ * actually be needed.
+ */
+ if (intf->type == MEDIA_INTF_T_DVB_DVR && demux) {
+ link = media_create_intf_link(demux, intf,
+ MEDIA_LNK_FL_ENABLED);
+ if (!link)
+ return -ENOMEM;
+ }
+#endif
+ if (intf->type == MEDIA_INTF_T_DVB_DVR) {
+ ret = dvb_create_io_intf_links(adap, intf, DVR_TSOUT);
+ if (ret)
+ return ret;
+ }
+ if (intf->type == MEDIA_INTF_T_DVB_DEMUX) {
+ ret = dvb_create_io_intf_links(adap, intf, DEMUX_TSOUT);
+ if (ret)
+ return ret;
+ }
+ }
+ return 0;
}
EXPORT_SYMBOL_GPL(dvb_create_media_graph);
#endif
diff --git a/drivers/media/dvb-core/dvbdev.h b/drivers/media/dvb-core/dvbdev.h
index 1069a776bbdb..4aff7bd3dea8 100644
--- a/drivers/media/dvb-core/dvbdev.h
+++ b/drivers/media/dvb-core/dvbdev.h
@@ -75,6 +75,9 @@ struct dvb_frontend;
* used.
* @mdev: pointer to struct media_device, used when the media
* controller is used.
+ * @conn: RF connector. Used only if the device has no separate
+ * tuner.
+ * @conn_pads: pointer to struct media_pad associated with @conn;
*/
struct dvb_adapter {
int num;
@@ -94,6 +97,8 @@ struct dvb_adapter {
#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
struct media_device *mdev;
+ struct media_entity *conn;
+ struct media_pad *conn_pads;
#endif
};
@@ -120,6 +125,11 @@ struct dvb_adapter {
* @entity: pointer to struct media_entity associated with the device node
* @pads: pointer to struct media_pad associated with @entity;
* @priv: private data
+ * @intf_devnode: Pointer to media_intf_devnode. Used by the dvbdev core to
+ * store the MC device node interface
+ * @tsout_num_entities: Number of Transport Stream output entities
+ * @tsout_entity: array with MC entities associated to each TS output node
+ * @tsout_pads: array with the source pads for each @tsout_entity
*
* This structure is used by the DVB core (frontend, CA, net, demux) in
* order to create the device nodes. Usually, driver should not initialize
@@ -148,8 +158,11 @@ struct dvb_device {
const char *name;
/* Allocated and filled inside dvbdev.c */
- struct media_entity *entity;
- struct media_pad *pads;
+ struct media_intf_devnode *intf_devnode;
+
+ unsigned tsout_num_entities;
+ struct media_entity *entity, *tsout_entity;
+ struct media_pad *pads, *tsout_pads;
#endif
void *priv;
@@ -185,14 +198,18 @@ int dvb_unregister_adapter(struct dvb_adapter *adap);
* stored
* @template: Template used to create &pdvbdev;
* @priv: private data
- * @type: type of the device: DVB_DEVICE_SEC, DVB_DEVICE_FRONTEND,
- * DVB_DEVICE_DEMUX, DVB_DEVICE_DVR, DVB_DEVICE_CA, DVB_DEVICE_NET
+ * @type: type of the device: %DVB_DEVICE_SEC, %DVB_DEVICE_FRONTEND,
+ * %DVB_DEVICE_DEMUX, %DVB_DEVICE_DVR, %DVB_DEVICE_CA,
+ * %DVB_DEVICE_NET
+ * @demux_sink_pads: Number of demux outputs, to be used to create the TS
+ * outputs via the Media Controller.
*/
int dvb_register_device(struct dvb_adapter *adap,
struct dvb_device **pdvbdev,
const struct dvb_device *template,
void *priv,
- int type);
+ int type,
+ int demux_sink_pads);
/**
* dvb_unregister_device - Unregisters a DVB device
@@ -202,16 +219,43 @@ int dvb_register_device(struct dvb_adapter *adap,
void dvb_unregister_device(struct dvb_device *dvbdev);
#ifdef CONFIG_MEDIA_CONTROLLER_DVB
-void dvb_create_media_graph(struct dvb_adapter *adap);
+/**
+ * dvb_create_media_graph - Creates media graph for the Digital TV part of the
+ * device.
+ *
+ * @adap: pointer to struct dvb_adapter
+ * @create_rf_connector: if true, it creates the RF connector too
+ *
+ * This function checks all DVB-related functions at the media controller
+ * entities and creates the needed links for the media graph. It is
+ * capable of working with multiple tuners or multiple frontends, but it
+ * won't create links if the device has multiple tuners and multiple frontends
+ * or if the device has multiple muxes. In such case, the caller driver should
+ * manually create the remaining links.
+ */
+__must_check int dvb_create_media_graph(struct dvb_adapter *adap,
+ bool create_rf_connector);
+
static inline void dvb_register_media_controller(struct dvb_adapter *adap,
struct media_device *mdev)
{
adap->mdev = mdev;
}
+static inline struct media_device
+*dvb_get_media_controller(struct dvb_adapter *adap)
+{
+ return adap->mdev;
+}
#else
-static inline void dvb_create_media_graph(struct dvb_adapter *adap) {}
+static inline
+int dvb_create_media_graph(struct dvb_adapter *adap,
+ bool create_rf_connector)
+{
+ return 0;
+};
#define dvb_register_media_controller(a, b) {}
+#define dvb_get_media_controller(a) NULL
#endif
int dvb_generic_open (struct inode *inode, struct file *file);
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index 292c9479bb75..310e4b8beae8 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -264,7 +264,7 @@ config DVB_MB86A16
config DVB_TDA10071
tristate "NXP TDA10071"
depends on DVB_CORE && I2C
- select REGMAP
+ select REGMAP_I2C
default m if !MEDIA_SUBDRV_AUTOSELECT
help
Say Y when you want to support this frontend.
diff --git a/drivers/media/dvb-frontends/au8522_common.c b/drivers/media/dvb-frontends/au8522_common.c
index 3559ff230045..f135126bc373 100644
--- a/drivers/media/dvb-frontends/au8522_common.c
+++ b/drivers/media/dvb-frontends/au8522_common.c
@@ -44,7 +44,7 @@ int au8522_writereg(struct au8522_state *state, u16 reg, u8 data)
int ret;
u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data };
- struct i2c_msg msg = { .addr = state->config->demod_address,
+ struct i2c_msg msg = { .addr = state->config.demod_address,
.flags = 0, .buf = buf, .len = 3 };
ret = i2c_transfer(state->i2c, &msg, 1);
@@ -64,9 +64,9 @@ u8 au8522_readreg(struct au8522_state *state, u16 reg)
u8 b1[] = { 0 };
struct i2c_msg msg[] = {
- { .addr = state->config->demod_address, .flags = 0,
+ { .addr = state->config.demod_address, .flags = 0,
.buf = b0, .len = 2 },
- { .addr = state->config->demod_address, .flags = I2C_M_RD,
+ { .addr = state->config.demod_address, .flags = I2C_M_RD,
.buf = b1, .len = 1 } };
ret = i2c_transfer(state->i2c, msg, 2);
@@ -140,7 +140,7 @@ EXPORT_SYMBOL(au8522_release_state);
static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
{
- struct au8522_led_config *led_config = state->config->led_cfg;
+ struct au8522_led_config *led_config = state->config.led_cfg;
u8 val;
/* bail out if we can't control an LED */
@@ -170,7 +170,7 @@ static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
*/
int au8522_led_ctrl(struct au8522_state *state, int led)
{
- struct au8522_led_config *led_config = state->config->led_cfg;
+ struct au8522_led_config *led_config = state->config.led_cfg;
int i, ret = 0;
/* bail out if we can't control an LED */
diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c
index 28d7dc2fee34..73612c5353d1 100644
--- a/drivers/media/dvb-frontends/au8522_decoder.c
+++ b/drivers/media/dvb-frontends/au8522_decoder.c
@@ -730,7 +730,9 @@ static int au8522_probe(struct i2c_client *client,
struct v4l2_ctrl_handler *hdl;
struct v4l2_subdev *sd;
int instance;
- struct au8522_config *demod_config;
+#ifdef CONFIG_MEDIA_CONTROLLER
+ int ret;
+#endif
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter,
@@ -754,19 +756,25 @@ static int au8522_probe(struct i2c_client *client,
break;
}
- demod_config = kzalloc(sizeof(struct au8522_config), GFP_KERNEL);
- if (demod_config == NULL) {
- if (instance == 1)
- kfree(state);
- return -ENOMEM;
- }
- demod_config->demod_address = 0x8e >> 1;
-
- state->config = demod_config;
+ state->config.demod_address = 0x8e >> 1;
state->i2c = client->adapter;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &au8522_ops);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+
+ state->pads[AU8522_PAD_INPUT].flags = MEDIA_PAD_FL_SINK;
+ state->pads[AU8522_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE;
+ state->pads[AU8522_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.function = MEDIA_ENT_F_ATV_DECODER;
+
+ ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(state->pads),
+ state->pads);
+ if (ret < 0) {
+ v4l_info(client, "failed to initialize media entity!\n");
+ return ret;
+ }
+#endif
hdl = &state->hdl;
v4l2_ctrl_handler_init(hdl, 4);
@@ -784,8 +792,7 @@ static int au8522_probe(struct i2c_client *client,
int err = hdl->error;
v4l2_ctrl_handler_free(hdl);
- kfree(demod_config);
- kfree(state);
+ au8522_release_state(state);
return err;
}
diff --git a/drivers/media/dvb-frontends/au8522_dig.c b/drivers/media/dvb-frontends/au8522_dig.c
index f956f13fb3dc..6c1e97640f3f 100644
--- a/drivers/media/dvb-frontends/au8522_dig.c
+++ b/drivers/media/dvb-frontends/au8522_dig.c
@@ -566,7 +566,7 @@ static int au8522_enable_modulation(struct dvb_frontend *fe,
au8522_writereg(state,
VSB_mod_tab[i].reg,
VSB_mod_tab[i].data);
- au8522_set_if(fe, state->config->vsb_if);
+ au8522_set_if(fe, state->config.vsb_if);
break;
case QAM_64:
dprintk("%s() QAM 64\n", __func__);
@@ -574,7 +574,7 @@ static int au8522_enable_modulation(struct dvb_frontend *fe,
au8522_writereg(state,
QAM64_mod_tab[i].reg,
QAM64_mod_tab[i].data);
- au8522_set_if(fe, state->config->qam_if);
+ au8522_set_if(fe, state->config.qam_if);
break;
case QAM_256:
if (zv_mode) {
@@ -583,7 +583,7 @@ static int au8522_enable_modulation(struct dvb_frontend *fe,
au8522_writereg(state,
QAM256_mod_tab_zv_mode[i].reg,
QAM256_mod_tab_zv_mode[i].data);
- au8522_set_if(fe, state->config->qam_if);
+ au8522_set_if(fe, state->config.qam_if);
msleep(100);
au8522_writereg(state, 0x821a, 0x00);
} else {
@@ -592,7 +592,7 @@ static int au8522_enable_modulation(struct dvb_frontend *fe,
au8522_writereg(state,
QAM256_mod_tab[i].reg,
QAM256_mod_tab[i].data);
- au8522_set_if(fe, state->config->qam_if);
+ au8522_set_if(fe, state->config.qam_if);
}
break;
default:
@@ -666,7 +666,7 @@ static int au8522_read_status(struct dvb_frontend *fe, enum fe_status *status)
*status |= FE_HAS_LOCK | FE_HAS_SYNC;
}
- switch (state->config->status_mode) {
+ switch (state->config.status_mode) {
case AU8522_DEMODLOCKING:
dprintk("%s() DEMODLOCKING\n", __func__);
if (*status & FE_HAS_VITERBI)
@@ -704,7 +704,7 @@ static int au8522_read_status(struct dvb_frontend *fe, enum fe_status *status)
static int au8522_led_status(struct au8522_state *state, const u16 *snr)
{
- struct au8522_led_config *led_config = state->config->led_cfg;
+ struct au8522_led_config *led_config = state->config.led_cfg;
int led;
u16 strong;
@@ -758,7 +758,7 @@ static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr)
au8522_readreg(state, 0x4311),
snr);
- if (state->config->led_cfg)
+ if (state->config.led_cfg)
au8522_led_status(state, snr);
return ret;
@@ -866,7 +866,7 @@ struct dvb_frontend *au8522_attach(const struct au8522_config *config,
}
/* setup the state */
- state->config = config;
+ state->config = *config;
state->i2c = i2c;
state->operational_mode = AU8522_DIGITAL_MODE;
diff --git a/drivers/media/dvb-frontends/au8522_priv.h b/drivers/media/dvb-frontends/au8522_priv.h
index 951b3847e6f6..404a0cb0ed8d 100644
--- a/drivers/media/dvb-frontends/au8522_priv.h
+++ b/drivers/media/dvb-frontends/au8522_priv.h
@@ -39,6 +39,14 @@
#define AU8522_DIGITAL_MODE 1
#define AU8522_SUSPEND_MODE 2
+enum au8522_media_pads {
+ AU8522_PAD_INPUT,
+ AU8522_PAD_VID_OUT,
+ AU8522_PAD_VBI_OUT,
+
+ AU8522_NUM_PADS
+};
+
struct au8522_state {
struct i2c_client *c;
struct i2c_adapter *i2c;
@@ -50,7 +58,7 @@ struct au8522_state {
struct list_head hybrid_tuner_instance_list;
/* configuration settings */
- const struct au8522_config *config;
+ struct au8522_config config;
struct dvb_frontend frontend;
@@ -68,6 +76,10 @@ struct au8522_state {
u32 id;
u32 rev;
struct v4l2_ctrl_handler hdl;
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_pad pads[AU8522_NUM_PADS];
+#endif
};
/* These are routines shared by both the VSB/QAM demodulator and the analog
diff --git a/drivers/media/dvb-frontends/bsbe1-d01a.h b/drivers/media/dvb-frontends/bsbe1-d01a.h
index 7ed3c424178c..baaf89e768cf 100644
--- a/drivers/media/dvb-frontends/bsbe1-d01a.h
+++ b/drivers/media/dvb-frontends/bsbe1-d01a.h
@@ -21,7 +21,7 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
- * the project's page is at http://www.linuxtv.org
+ * the project's page is at https://linuxtv.org
*/
#ifndef BSBE1_D01A_H
diff --git a/drivers/media/dvb-frontends/bsbe1.h b/drivers/media/dvb-frontends/bsbe1.h
index 53e4d0dbb745..4ad766154741 100644
--- a/drivers/media/dvb-frontends/bsbe1.h
+++ b/drivers/media/dvb-frontends/bsbe1.h
@@ -19,7 +19,7 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
- * the project's page is at http://www.linuxtv.org
+ * the project's page is at https://linuxtv.org
*/
#ifndef BSBE1_H
diff --git a/drivers/media/dvb-frontends/bsru6.h b/drivers/media/dvb-frontends/bsru6.h
index c2a578e1314d..275c1782597d 100644
--- a/drivers/media/dvb-frontends/bsru6.h
+++ b/drivers/media/dvb-frontends/bsru6.h
@@ -19,7 +19,7 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
- * the project's page is at http://www.linuxtv.org
+ * the project's page is at https://linuxtv.org
*/
#ifndef BSRU6_H
diff --git a/drivers/media/dvb-frontends/isl6405.c b/drivers/media/dvb-frontends/isl6405.c
index b46450a10b80..6913cd687b4d 100644
--- a/drivers/media/dvb-frontends/isl6405.c
+++ b/drivers/media/dvb-frontends/isl6405.c
@@ -22,7 +22,7 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
- * the project's page is at http://www.linuxtv.org
+ * the project's page is at https://linuxtv.org
*/
#include <linux/delay.h>
#include <linux/errno.h>
diff --git a/drivers/media/dvb-frontends/isl6405.h b/drivers/media/dvb-frontends/isl6405.h
index 3c148b830bd1..4a23d3bdf3e6 100644
--- a/drivers/media/dvb-frontends/isl6405.h
+++ b/drivers/media/dvb-frontends/isl6405.h
@@ -22,7 +22,7 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
- * the project's page is at http://www.linuxtv.org
+ * the project's page is at https://linuxtv.org
*/
#ifndef _ISL6405_H
diff --git a/drivers/media/dvb-frontends/isl6421.c b/drivers/media/dvb-frontends/isl6421.c
index 3a4d4606a426..0b6d3837d5de 100644
--- a/drivers/media/dvb-frontends/isl6421.c
+++ b/drivers/media/dvb-frontends/isl6421.c
@@ -22,7 +22,7 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
- * the project's page is at http://www.linuxtv.org
+ * the project's page is at https://linuxtv.org
*/
#include <linux/delay.h>
#include <linux/errno.h>
diff --git a/drivers/media/dvb-frontends/isl6421.h b/drivers/media/dvb-frontends/isl6421.h
index 3273597833fd..00f9874ca5a2 100644
--- a/drivers/media/dvb-frontends/isl6421.h
+++ b/drivers/media/dvb-frontends/isl6421.h
@@ -22,7 +22,7 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
- * the project's page is at http://www.linuxtv.org
+ * the project's page is at https://linuxtv.org
*/
#ifndef _ISL6421_H
diff --git a/drivers/media/dvb-frontends/lnbp21.c b/drivers/media/dvb-frontends/lnbp21.c
index 4aca0fb9a8a7..6261460d93a7 100644
--- a/drivers/media/dvb-frontends/lnbp21.c
+++ b/drivers/media/dvb-frontends/lnbp21.c
@@ -22,7 +22,7 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
- * the project's page is at http://www.linuxtv.org
+ * the project's page is at https://linuxtv.org
*/
#include <linux/delay.h>
#include <linux/errno.h>
diff --git a/drivers/media/dvb-frontends/lnbp21.h b/drivers/media/dvb-frontends/lnbp21.h
index a9b530de62a6..cd9101f6e579 100644
--- a/drivers/media/dvb-frontends/lnbp21.h
+++ b/drivers/media/dvb-frontends/lnbp21.h
@@ -21,7 +21,7 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
- * the project's page is at http://www.linuxtv.org
+ * the project's page is at https://linuxtv.org
*/
#ifndef _LNBP21_H
diff --git a/drivers/media/dvb-frontends/lnbp22.c b/drivers/media/dvb-frontends/lnbp22.c
index d7ca0fdd0084..5c5fd04fd4a7 100644
--- a/drivers/media/dvb-frontends/lnbp22.c
+++ b/drivers/media/dvb-frontends/lnbp22.c
@@ -22,7 +22,7 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
- * the project's page is at http://www.linuxtv.org
+ * the project's page is at https://linuxtv.org
*/
#include <linux/delay.h>
#include <linux/errno.h>
diff --git a/drivers/media/dvb-frontends/lnbp22.h b/drivers/media/dvb-frontends/lnbp22.h
index 628148385182..5d01d92814c2 100644
--- a/drivers/media/dvb-frontends/lnbp22.h
+++ b/drivers/media/dvb-frontends/lnbp22.h
@@ -22,7 +22,7 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
- * the project's page is at http://www.linuxtv.org
+ * the project's page is at https://linuxtv.org
*/
#ifndef _LNBP22_H
diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
index feeeb70d841e..ce73a5ec6036 100644
--- a/drivers/media/dvb-frontends/m88ds3103.c
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -685,7 +685,7 @@ static int m88ds3103_init(struct dvb_frontend *fe)
/* request the firmware, this will block and timeout */
ret = request_firmware(&fw, fw_file, &client->dev);
if (ret) {
- dev_err(&client->dev, "firmare file '%s' not found\n", fw_file);
+ dev_err(&client->dev, "firmware file '%s' not found\n", fw_file);
goto err;
}
diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c
index 78b87b260d74..10f2119935da 100644
--- a/drivers/media/dvb-frontends/rtl2832.c
+++ b/drivers/media/dvb-frontends/rtl2832.c
@@ -976,7 +976,8 @@ static int rtl2832_regmap_read(void *context, const void *reg_buf,
ret = __i2c_transfer(client->adapter, msg, 2);
if (ret != 2) {
- dev_warn(&client->dev, "i2c reg read failed %d\n", ret);
+ dev_warn(&client->dev, "i2c reg read failed %d reg %02x\n",
+ ret, *(u8 *)reg_buf);
if (ret >= 0)
ret = -EREMOTEIO;
return ret;
@@ -999,7 +1000,8 @@ static int rtl2832_regmap_write(void *context, const void *data, size_t count)
ret = __i2c_transfer(client->adapter, msg, 1);
if (ret != 1) {
- dev_warn(&client->dev, "i2c reg write failed %d\n", ret);
+ dev_warn(&client->dev, "i2c reg write failed %d reg %02x\n",
+ ret, *(u8 *)data);
if (ret >= 0)
ret = -EREMOTEIO;
return ret;
@@ -1028,7 +1030,8 @@ static int rtl2832_regmap_gather_write(void *context, const void *reg,
ret = __i2c_transfer(client->adapter, msg, 1);
if (ret != 1) {
- dev_warn(&client->dev, "i2c reg write failed %d\n", ret);
+ dev_warn(&client->dev, "i2c reg write failed %d reg %02x\n",
+ ret, *(u8 const *)reg);
if (ret >= 0)
ret = -EREMOTEIO;
return ret;
@@ -1097,18 +1100,6 @@ static int rtl2832_enable_slave_ts(struct i2c_client *client)
if (ret)
goto err;
- ret = rtl2832_bulk_write(client, 0x022, "\x01", 1);
- if (ret)
- goto err;
-
- ret = rtl2832_bulk_write(client, 0x026, "\x1f", 1);
- if (ret)
- goto err;
-
- ret = rtl2832_bulk_write(client, 0x027, "\xff", 1);
- if (ret)
- goto err;
-
ret = rtl2832_bulk_write(client, 0x192, "\x7f\xf7\xff", 3);
if (ret)
goto err;
diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c
index dcd8d94c1037..b860f02a4e55 100644
--- a/drivers/media/dvb-frontends/rtl2832_sdr.c
+++ b/drivers/media/dvb-frontends/rtl2832_sdr.c
@@ -310,7 +310,7 @@ static void rtl2832_sdr_urb_complete(struct urb *urb)
len = rtl2832_sdr_convert_stream(dev, ptr, urb->transfer_buffer,
urb->actual_length);
vb2_set_plane_payload(&fbuf->vb.vb2_buf, 0, len);
- v4l2_get_timestamp(&fbuf->vb.timestamp);
+ fbuf->vb.vb2_buf.timestamp = ktime_get_ns();
fbuf->vb.sequence = dev->sequence++;
vb2_buffer_done(&fbuf->vb.vb2_buf, VB2_BUF_STATE_DONE);
}
@@ -490,7 +490,7 @@ static int rtl2832_sdr_querycap(struct file *file, void *fh,
/* Videobuf2 operations */
static int rtl2832_sdr_queue_setup(struct vb2_queue *vq,
- const void *parg, unsigned int *nbuffers,
+ unsigned int *nbuffers,
unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[])
{
struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vq);
diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c
index 7c2eeee69757..2b93241d4bc1 100644
--- a/drivers/media/dvb-frontends/si2165.c
+++ b/drivers/media/dvb-frontends/si2165.c
@@ -1,21 +1,21 @@
/*
- Driver for Silicon Labs Si2161 DVB-T and Si2165 DVB-C/-T Demodulator
-
- Copyright (C) 2013-2014 Matthias Schwarzott <zzam@gentoo.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 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.
-
- References:
- http://www.silabs.com/Support%20Documents/TechnicalDocs/Si2165-short.pdf
-*/
+ * Driver for Silicon Labs Si2161 DVB-T and Si2165 DVB-C/-T Demodulator
+ *
+ * Copyright (C) 2013-2014 Matthias Schwarzott <zzam@gentoo.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 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.
+ *
+ * References:
+ * http://www.silabs.com/Support%20Documents/TechnicalDocs/Si2165-short.pdf
+ */
#include <linux/delay.h>
#include <linux/errno.h>
@@ -31,16 +31,18 @@
#include "si2165_priv.h"
#include "si2165.h"
-/* Hauppauge WinTV-HVR-930C-HD B130 / PCTV QuatroStick 521e 1113xx
- * uses 16 MHz xtal */
-
-/* Hauppauge WinTV-HVR-930C-HD B131 / PCTV QuatroStick 522e 1114xx
- * uses 24 MHz clock provided by tuner */
+/*
+ * Hauppauge WinTV-HVR-930C-HD B130 / PCTV QuatroStick 521e 1113xx
+ * uses 16 MHz xtal
+ *
+ * Hauppauge WinTV-HVR-930C-HD B131 / PCTV QuatroStick 522e 1114xx
+ * uses 24 MHz clock provided by tuner
+ */
struct si2165_state {
struct i2c_adapter *i2c;
- struct dvb_frontend frontend;
+ struct dvb_frontend fe;
struct si2165_config config;
@@ -241,6 +243,27 @@ err:
return ret;
}
+#define REG16(reg, val) { (reg), (val) & 0xff }, { (reg)+1, (val)>>8 & 0xff }
+struct si2165_reg_value_pair {
+ u16 reg;
+ u8 val;
+};
+
+static int si2165_write_reg_list(struct si2165_state *state,
+ const struct si2165_reg_value_pair *regs,
+ int count)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < count; i++) {
+ ret = si2165_writereg8(state, regs[i].reg, regs[i].val);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
static int si2165_get_tune_settings(struct dvb_frontend *fe,
struct dvb_frontend_tune_settings *s)
{
@@ -258,8 +281,10 @@ static int si2165_init_pll(struct si2165_state *state)
u8 divl = 12;
u8 buf[4];
- /* hardcoded values can be deleted if calculation is verified
- * or it yields the same values as the windows driver */
+ /*
+ * hardcoded values can be deleted if calculation is verified
+ * or it yields the same values as the windows driver
+ */
switch (ref_freq_Hz) {
case 16000000u:
divn = 56;
@@ -274,8 +299,10 @@ static int si2165_init_pll(struct si2165_state *state)
if (ref_freq_Hz > 16000000u)
divr = 2;
- /* now select divn and divp such that
- * fvco is in 1624..1824 MHz */
+ /*
+ * now select divn and divp such that
+ * fvco is in 1624..1824 MHz
+ */
if (1624000000u * divr > ref_freq_Hz * 2u * 63u)
divp = 4;
@@ -341,10 +368,12 @@ static int si2165_upload_firmware_block(struct si2165_state *state,
if (len % 4 != 0)
return -EINVAL;
- deb_fw_load("si2165_upload_firmware_block called with len=0x%x offset=0x%x blockcount=0x%x\n",
+ deb_fw_load(
+ "si2165_upload_firmware_block called with len=0x%x offset=0x%x blockcount=0x%x\n",
len, offset, block_count);
while (offset+12 <= len && cur_block < block_count) {
- deb_fw_load("si2165_upload_firmware_block in while len=0x%x offset=0x%x cur_block=0x%x blockcount=0x%x\n",
+ deb_fw_load(
+ "si2165_upload_firmware_block in while len=0x%x offset=0x%x cur_block=0x%x blockcount=0x%x\n",
len, offset, cur_block, block_count);
wordcount = data[offset];
if (wordcount < 1 || data[offset+1] ||
@@ -383,7 +412,8 @@ static int si2165_upload_firmware_block(struct si2165_state *state,
cur_block++;
}
- deb_fw_load("si2165_upload_firmware_block after while len=0x%x offset=0x%x cur_block=0x%x blockcount=0x%x\n",
+ deb_fw_load(
+ "si2165_upload_firmware_block after while len=0x%x offset=0x%x cur_block=0x%x blockcount=0x%x\n",
len, offset, cur_block, block_count);
if (poffset)
@@ -511,7 +541,7 @@ static int si2165_upload_firmware(struct si2165_state *state)
&offset, block_count);
if (ret < 0) {
dev_err(&state->i2c->dev,
- "%s: firmare could not be uploaded\n",
+ "%s: firmware could not be uploaded\n",
KBUILD_MODNAME);
goto error;
}
@@ -535,7 +565,7 @@ static int si2165_upload_firmware(struct si2165_state *state)
if (len != offset) {
dev_err(&state->i2c->dev,
- "%s: firmare len mismatch %04x != %04x\n",
+ "%s: firmware len mismatch %04x != %04x\n",
KBUILD_MODNAME, len, offset);
ret = -EINVAL;
goto error;
@@ -633,7 +663,7 @@ static int si2165_init(struct dvb_frontend *fe)
goto error;
/* ber_pkt */
- ret = si2165_writereg16(state, 0x0470 , 0x7530);
+ ret = si2165_writereg16(state, 0x0470, 0x7530);
if (ret < 0)
goto error;
@@ -660,22 +690,19 @@ static int si2165_init(struct dvb_frontend *fe)
goto error;
}
- /* write adc values after each reset*/
- ret = si2165_writereg8(state, 0x012a, 0x46);
- if (ret < 0)
- goto error;
- ret = si2165_writereg8(state, 0x012c, 0x00);
+ /* ts output config */
+ ret = si2165_writereg8(state, 0x04e4, 0x20);
if (ret < 0)
- goto error;
- ret = si2165_writereg8(state, 0x012e, 0x0a);
+ return ret;
+ ret = si2165_writereg16(state, 0x04ef, 0x00fe);
if (ret < 0)
- goto error;
- ret = si2165_writereg8(state, 0x012f, 0xff);
+ return ret;
+ ret = si2165_writereg24(state, 0x04f4, 0x555555);
if (ret < 0)
- goto error;
- ret = si2165_writereg8(state, 0x0123, 0x70);
+ return ret;
+ ret = si2165_writereg8(state, 0x04e5, 0x01);
if (ret < 0)
- goto error;
+ return ret;
return 0;
error:
@@ -733,16 +760,26 @@ static int si2165_set_oversamp(struct si2165_state *state, u32 dvb_rate)
do_div(oversamp, dvb_rate);
reg_value = oversamp & 0x3fffffff;
- /* oversamp, usbdump contained 0x03100000; */
+ dprintk("%s: Write oversamp=%#x\n", __func__, reg_value);
return si2165_writereg32(state, 0x00e4, reg_value);
}
-static int si2165_set_if_freq_shift(struct si2165_state *state, u32 IF)
+static int si2165_set_if_freq_shift(struct si2165_state *state)
{
+ struct dvb_frontend *fe = &state->fe;
u64 if_freq_shift;
s32 reg_value = 0;
u32 fe_clk = si2165_get_fe_clk(state);
+ u32 IF = 0;
+ if (!fe->ops.tuner_ops.get_if_frequency) {
+ dev_err(&state->i2c->dev,
+ "%s: Error: get_if_frequency() not defined at tuner. Can't work without it!\n",
+ KBUILD_MODNAME);
+ return -EINVAL;
+ }
+
+ fe->ops.tuner_ops.get_if_frequency(fe, &IF);
if_freq_shift = IF;
if_freq_shift <<= 29;
@@ -758,25 +795,39 @@ static int si2165_set_if_freq_shift(struct si2165_state *state, u32 IF)
return si2165_writereg32(state, 0x00e8, reg_value);
}
-static int si2165_set_parameters(struct dvb_frontend *fe)
+static const struct si2165_reg_value_pair dvbt_regs[] = {
+ /* standard = DVB-T */
+ { 0x00ec, 0x01 },
+ { 0x08f8, 0x00 },
+ /* impulsive_noise_remover */
+ { 0x031c, 0x01 },
+ { 0x00cb, 0x00 },
+ /* agc2 */
+ { 0x016e, 0x41 },
+ { 0x016c, 0x0e },
+ { 0x016d, 0x10 },
+ /* agc */
+ { 0x015b, 0x03 },
+ { 0x0150, 0x78 },
+ /* agc */
+ { 0x01a0, 0x78 },
+ { 0x01c8, 0x68 },
+ /* freq_sync_range */
+ REG16(0x030c, 0x0064),
+ /* gp_reg0 */
+ { 0x0387, 0x00 }
+};
+
+static int si2165_set_frontend_dvbt(struct dvb_frontend *fe)
{
int ret;
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct si2165_state *state = fe->demodulator_priv;
- u8 val[3];
- u32 IF;
u32 dvb_rate = 0;
u16 bw10k;
dprintk("%s: called\n", __func__);
- if (!fe->ops.tuner_ops.get_if_frequency) {
- dev_err(&state->i2c->dev,
- "%s: Error: get_if_frequency() not defined at tuner. Can't work without it!\n",
- KBUILD_MODNAME);
- return -EINVAL;
- }
-
if (!state->has_dvbt)
return -EINVAL;
@@ -788,34 +839,10 @@ static int si2165_set_parameters(struct dvb_frontend *fe)
bw10k = 800;
}
- /* standard = DVB-T */
- ret = si2165_writereg8(state, 0x00ec, 0x01);
- if (ret < 0)
- return ret;
ret = si2165_adjust_pll_divl(state, 12);
if (ret < 0)
return ret;
- fe->ops.tuner_ops.get_if_frequency(fe, &IF);
- ret = si2165_set_if_freq_shift(state, IF);
- if (ret < 0)
- return ret;
- ret = si2165_writereg8(state, 0x08f8, 0x00);
- if (ret < 0)
- return ret;
- /* ts output config */
- ret = si2165_writereg8(state, 0x04e4, 0x20);
- if (ret < 0)
- return ret;
- ret = si2165_writereg16(state, 0x04ef, 0x00fe);
- if (ret < 0)
- return ret;
- ret = si2165_writereg24(state, 0x04f4, 0x555555);
- if (ret < 0)
- return ret;
- ret = si2165_writereg8(state, 0x04e5, 0x01);
- if (ret < 0)
- return ret;
/* bandwidth in 10KHz steps */
ret = si2165_writereg16(state, 0x0308, bw10k);
if (ret < 0)
@@ -823,48 +850,115 @@ static int si2165_set_parameters(struct dvb_frontend *fe)
ret = si2165_set_oversamp(state, dvb_rate);
if (ret < 0)
return ret;
- /* impulsive_noise_remover */
- ret = si2165_writereg8(state, 0x031c, 0x01);
- if (ret < 0)
- return ret;
- ret = si2165_writereg8(state, 0x00cb, 0x00);
+
+ ret = si2165_write_reg_list(state, dvbt_regs, ARRAY_SIZE(dvbt_regs));
if (ret < 0)
return ret;
+
+ return 0;
+}
+
+static const struct si2165_reg_value_pair dvbc_regs[] = {
+ /* standard = DVB-C */
+ { 0x00ec, 0x05 },
+ { 0x08f8, 0x00 },
+
/* agc2 */
- ret = si2165_writereg8(state, 0x016e, 0x41);
- if (ret < 0)
- return ret;
- ret = si2165_writereg8(state, 0x016c, 0x0e);
- if (ret < 0)
- return ret;
- ret = si2165_writereg8(state, 0x016d, 0x10);
- if (ret < 0)
- return ret;
+ { 0x016e, 0x50 },
+ { 0x016c, 0x0e },
+ { 0x016d, 0x10 },
/* agc */
- ret = si2165_writereg8(state, 0x015b, 0x03);
- if (ret < 0)
- return ret;
- ret = si2165_writereg8(state, 0x0150, 0x78);
- if (ret < 0)
- return ret;
+ { 0x015b, 0x03 },
+ { 0x0150, 0x68 },
/* agc */
- ret = si2165_writereg8(state, 0x01a0, 0x78);
+ { 0x01a0, 0x68 },
+ { 0x01c8, 0x50 },
+
+ { 0x0278, 0x0d },
+
+ { 0x023a, 0x05 },
+ { 0x0261, 0x09 },
+ REG16(0x0350, 0x3e80),
+ { 0x02f4, 0x00 },
+
+ { 0x00cb, 0x01 },
+ REG16(0x024c, 0x0000),
+ REG16(0x027c, 0x0000),
+ { 0x0232, 0x03 },
+ { 0x02f4, 0x0b },
+ { 0x018b, 0x00 },
+};
+
+static int si2165_set_frontend_dvbc(struct dvb_frontend *fe)
+{
+ struct si2165_state *state = fe->demodulator_priv;
+ int ret;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ const u32 dvb_rate = p->symbol_rate;
+ const u32 bw_hz = p->bandwidth_hz;
+
+ if (!state->has_dvbc)
+ return -EINVAL;
+
+ if (dvb_rate == 0)
+ return -EINVAL;
+
+ ret = si2165_adjust_pll_divl(state, 14);
if (ret < 0)
return ret;
- ret = si2165_writereg8(state, 0x01c8, 0x68);
+
+ /* Oversampling */
+ ret = si2165_set_oversamp(state, dvb_rate);
if (ret < 0)
return ret;
- /* freq_sync_range */
- ret = si2165_writereg16(state, 0x030c, 0x0064);
+
+ ret = si2165_writereg32(state, 0x00c4, bw_hz);
if (ret < 0)
return ret;
- /* gp_reg0 */
- ret = si2165_readreg8(state, 0x0387, val);
+
+ ret = si2165_write_reg_list(state, dvbc_regs, ARRAY_SIZE(dvbc_regs));
if (ret < 0)
return ret;
- ret = si2165_writereg8(state, 0x0387, 0x00);
+
+ return 0;
+}
+
+static const struct si2165_reg_value_pair agc_rewrite[] = {
+ { 0x012a, 0x46 },
+ { 0x012c, 0x00 },
+ { 0x012e, 0x0a },
+ { 0x012f, 0xff },
+ { 0x0123, 0x70 }
+};
+
+static int si2165_set_frontend(struct dvb_frontend *fe)
+{
+ struct si2165_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ u32 delsys = p->delivery_system;
+ int ret;
+ u8 val[3];
+
+ /* initial setting of if freq shift */
+ ret = si2165_set_if_freq_shift(state);
if (ret < 0)
return ret;
+
+ switch (delsys) {
+ case SYS_DVBT:
+ ret = si2165_set_frontend_dvbt(fe);
+ if (ret < 0)
+ return ret;
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = si2165_set_frontend_dvbc(fe);
+ if (ret < 0)
+ return ret;
+ break;
+ default:
+ return -EINVAL;
+ }
+
/* dsp_addr_jump */
ret = si2165_writereg32(state, 0x0348, 0xf4000000);
if (ret < 0)
@@ -874,8 +968,7 @@ static int si2165_set_parameters(struct dvb_frontend *fe)
fe->ops.tuner_ops.set_params(fe);
/* recalc if_freq_shift if IF might has changed */
- fe->ops.tuner_ops.get_if_frequency(fe, &IF);
- ret = si2165_set_if_freq_shift(state, IF);
+ ret = si2165_set_if_freq_shift(state);
if (ret < 0)
return ret;
@@ -886,6 +979,7 @@ static int si2165_set_parameters(struct dvb_frontend *fe)
ret = si2165_writereg8(state, 0x0341, 0x00);
if (ret < 0)
return ret;
+
/* reset all */
ret = si2165_writereg8(state, 0x00c0, 0x00);
if (ret < 0)
@@ -894,6 +988,13 @@ static int si2165_set_parameters(struct dvb_frontend *fe)
ret = si2165_writereg32(state, 0x0384, 0x00000000);
if (ret < 0)
return ret;
+
+ /* write adc values after each reset*/
+ ret = si2165_write_reg_list(state, agc_rewrite,
+ ARRAY_SIZE(agc_rewrite));
+ if (ret < 0)
+ return ret;
+
/* start_synchro */
ret = si2165_writereg8(state, 0x02e0, 0x01);
if (ret < 0)
@@ -917,7 +1018,12 @@ static void si2165_release(struct dvb_frontend *fe)
static struct dvb_frontend_ops si2165_ops = {
.info = {
.name = "Silicon Labs ",
- .caps = FE_CAN_FEC_1_2 |
+ /* For DVB-C */
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 7200000,
+ /* For DVB-T */
+ .frequency_stepsize = 166667,
+ .caps = FE_CAN_FEC_1_2 |
FE_CAN_FEC_2_3 |
FE_CAN_FEC_3_4 |
FE_CAN_FEC_5_6 |
@@ -930,7 +1036,6 @@ static struct dvb_frontend_ops si2165_ops = {
FE_CAN_QAM_128 |
FE_CAN_QAM_256 |
FE_CAN_QAM_AUTO |
- FE_CAN_TRANSMISSION_MODE_AUTO |
FE_CAN_GUARD_INTERVAL_AUTO |
FE_CAN_HIERARCHY_AUTO |
FE_CAN_MUTE_TS |
@@ -943,7 +1048,7 @@ static struct dvb_frontend_ops si2165_ops = {
.init = si2165_init,
.sleep = si2165_sleep,
- .set_frontend = si2165_set_parameters,
+ .set_frontend = si2165_set_frontend,
.read_status = si2165_read_status,
.release = si2165_release,
@@ -979,9 +1084,9 @@ struct dvb_frontend *si2165_attach(const struct si2165_config *config,
}
/* create dvb_frontend */
- memcpy(&state->frontend.ops, &si2165_ops,
+ memcpy(&state->fe.ops, &si2165_ops,
sizeof(struct dvb_frontend_ops));
- state->frontend.demodulator_priv = state;
+ state->fe.demodulator_priv = state;
/* powerup */
io_ret = si2165_writereg8(state, 0x0000, state->config.chip_mode);
@@ -1033,20 +1138,22 @@ struct dvb_frontend *si2165_attach(const struct si2165_config *config,
KBUILD_MODNAME, chip_name, rev_char, state->chip_type,
state->chip_revcode);
- strlcat(state->frontend.ops.info.name, chip_name,
- sizeof(state->frontend.ops.info.name));
+ strlcat(state->fe.ops.info.name, chip_name,
+ sizeof(state->fe.ops.info.name));
n = 0;
if (state->has_dvbt) {
- state->frontend.ops.delsys[n++] = SYS_DVBT;
- strlcat(state->frontend.ops.info.name, " DVB-T",
- sizeof(state->frontend.ops.info.name));
+ state->fe.ops.delsys[n++] = SYS_DVBT;
+ strlcat(state->fe.ops.info.name, " DVB-T",
+ sizeof(state->fe.ops.info.name));
+ }
+ if (state->has_dvbc) {
+ state->fe.ops.delsys[n++] = SYS_DVBC_ANNEX_A;
+ strlcat(state->fe.ops.info.name, " DVB-C",
+ sizeof(state->fe.ops.info.name));
}
- if (state->has_dvbc)
- dev_warn(&state->i2c->dev, "%s: DVB-C is not yet supported.\n",
- KBUILD_MODNAME);
- return &state->frontend;
+ return &state->fe;
error:
kfree(state);
diff --git a/drivers/media/dvb-frontends/stb6100.c b/drivers/media/dvb-frontends/stb6100.c
index 4ef8a5c7003e..c978c801c7aa 100644
--- a/drivers/media/dvb-frontends/stb6100.c
+++ b/drivers/media/dvb-frontends/stb6100.c
@@ -252,6 +252,7 @@ static int stb6100_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
{
int rc;
u8 f;
+ u32 bw;
struct stb6100_state *state = fe->tuner_priv;
rc = stb6100_read_reg(state, STB6100_F);
@@ -259,9 +260,9 @@ static int stb6100_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
return rc;
f = rc & STB6100_F_F;
- state->status.bandwidth = (f + 5) * 2000; /* x2 for ZIF */
+ bw = (f + 5) * 2000; /* x2 for ZIF */
- *bandwidth = state->bandwidth = state->status.bandwidth * 1000;
+ *bandwidth = state->bandwidth = bw * 1000;
dprintk(verbose, FE_DEBUG, 1, "bandwidth = %u Hz", state->bandwidth);
return 0;
}
@@ -495,68 +496,28 @@ static int stb6100_sleep(struct dvb_frontend *fe)
static int stb6100_init(struct dvb_frontend *fe)
{
struct stb6100_state *state = fe->tuner_priv;
- struct tuner_state *status = &state->status;
+ int refclk = 27000000; /* Hz */
- status->tunerstep = 125000;
- status->ifreq = 0;
- status->refclock = 27000000; /* Hz */
- status->iqsense = 1;
- status->bandwidth = 36000; /* kHz */
- state->bandwidth = status->bandwidth * 1000; /* Hz */
- state->reference = status->refclock / 1000; /* kHz */
+ /*
+ * iqsense = 1
+ * tunerstep = 125000
+ */
+ state->bandwidth = 36000000; /* Hz */
+ state->reference = refclk / 1000; /* kHz */
/* Set default bandwidth. Modified, PN 13-May-10 */
return 0;
}
-static int stb6100_get_state(struct dvb_frontend *fe,
- enum tuner_param param,
- struct tuner_state *state)
+static int stb6100_set_params(struct dvb_frontend *fe)
{
- switch (param) {
- case DVBFE_TUNER_FREQUENCY:
- stb6100_get_frequency(fe, &state->frequency);
- break;
- case DVBFE_TUNER_TUNERSTEP:
- break;
- case DVBFE_TUNER_IFFREQ:
- break;
- case DVBFE_TUNER_BANDWIDTH:
- stb6100_get_bandwidth(fe, &state->bandwidth);
- break;
- case DVBFE_TUNER_REFCLOCK:
- break;
- default:
- break;
- }
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- return 0;
-}
+ if (c->frequency > 0)
+ stb6100_set_frequency(fe, c->frequency);
-static int stb6100_set_state(struct dvb_frontend *fe,
- enum tuner_param param,
- struct tuner_state *state)
-{
- struct stb6100_state *tstate = fe->tuner_priv;
-
- switch (param) {
- case DVBFE_TUNER_FREQUENCY:
- stb6100_set_frequency(fe, state->frequency);
- tstate->frequency = state->frequency;
- break;
- case DVBFE_TUNER_TUNERSTEP:
- break;
- case DVBFE_TUNER_IFFREQ:
- break;
- case DVBFE_TUNER_BANDWIDTH:
- stb6100_set_bandwidth(fe, state->bandwidth);
- tstate->bandwidth = state->bandwidth;
- break;
- case DVBFE_TUNER_REFCLOCK:
- break;
- default:
- break;
- }
+ if (c->bandwidth_hz > 0)
+ stb6100_set_bandwidth(fe, c->bandwidth_hz);
return 0;
}
@@ -572,8 +533,9 @@ static struct dvb_tuner_ops stb6100_ops = {
.init = stb6100_init,
.sleep = stb6100_sleep,
.get_status = stb6100_get_status,
- .get_state = stb6100_get_state,
- .set_state = stb6100_set_state,
+ .set_params = stb6100_set_params,
+ .get_frequency = stb6100_get_frequency,
+ .get_bandwidth = stb6100_get_bandwidth,
.release = stb6100_release
};
diff --git a/drivers/media/dvb-frontends/stb6100.h b/drivers/media/dvb-frontends/stb6100.h
index 218c8188865d..f7b468b6dc26 100644
--- a/drivers/media/dvb-frontends/stb6100.h
+++ b/drivers/media/dvb-frontends/stb6100.h
@@ -86,7 +86,6 @@ struct stb6100_state {
const struct stb6100_config *config;
struct dvb_tuner_ops ops;
struct dvb_frontend *frontend;
- struct tuner_state status;
u32 frequency;
u32 srate;
diff --git a/drivers/media/dvb-frontends/stb6100_cfg.h b/drivers/media/dvb-frontends/stb6100_cfg.h
index 6edc15365847..2ef67aa768b9 100644
--- a/drivers/media/dvb-frontends/stb6100_cfg.h
+++ b/drivers/media/dvb-frontends/stb6100_cfg.h
@@ -19,20 +19,21 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
static int stb6100_get_frequency(struct dvb_frontend *fe, u32 *frequency)
{
struct dvb_frontend_ops *frontend_ops = &fe->ops;
struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops;
- struct tuner_state t_state;
int err = 0;
- if (tuner_ops->get_state) {
- err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &t_state);
+ if (tuner_ops->get_frequency) {
+ err = tuner_ops->get_frequency(fe, frequency);
if (err < 0) {
printk("%s: Invalid parameter\n", __func__);
return err;
}
- *frequency = t_state.frequency;
}
return 0;
}
@@ -41,13 +42,16 @@ static int stb6100_set_frequency(struct dvb_frontend *fe, u32 frequency)
{
struct dvb_frontend_ops *frontend_ops = &fe->ops;
struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops;
- struct tuner_state t_state;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 bw = c->bandwidth_hz;
int err = 0;
- t_state.frequency = frequency;
+ c->frequency = frequency;
+ c->bandwidth_hz = 0; /* Don't adjust the bandwidth */
- if (tuner_ops->set_state) {
- err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &t_state);
+ if (tuner_ops->set_params) {
+ err = tuner_ops->set_params(fe);
+ c->bandwidth_hz = bw;
if (err < 0) {
printk("%s: Invalid parameter\n", __func__);
return err;
@@ -60,16 +64,14 @@ static int stb6100_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
{
struct dvb_frontend_ops *frontend_ops = &fe->ops;
struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops;
- struct tuner_state t_state;
int err = 0;
- if (tuner_ops->get_state) {
- err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state);
+ if (tuner_ops->get_bandwidth) {
+ err = tuner_ops->get_bandwidth(fe, bandwidth);
if (err < 0) {
printk("%s: Invalid parameter\n", __func__);
return err;
}
- *bandwidth = t_state.bandwidth;
}
return 0;
}
@@ -78,13 +80,16 @@ static int stb6100_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth)
{
struct dvb_frontend_ops *frontend_ops = &fe->ops;
struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops;
- struct tuner_state t_state;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 freq = c->frequency;
int err = 0;
- t_state.bandwidth = bandwidth;
+ c->bandwidth_hz = bandwidth;
+ c->frequency = 0; /* Don't adjust the frequency */
- if (tuner_ops->set_state) {
- err = tuner_ops->set_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state);
+ if (tuner_ops->set_params) {
+ err = tuner_ops->set_params(fe);
+ c->frequency = freq;
if (err < 0) {
printk("%s: Invalid parameter\n", __func__);
return err;
diff --git a/drivers/media/dvb-frontends/stb6100_proc.h b/drivers/media/dvb-frontends/stb6100_proc.h
index bd8a0ec9e2cc..50ffa21e3871 100644
--- a/drivers/media/dvb-frontends/stb6100_proc.h
+++ b/drivers/media/dvb-frontends/stb6100_proc.h
@@ -17,27 +17,27 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
static int stb6100_get_freq(struct dvb_frontend *fe, u32 *frequency)
{
struct dvb_frontend_ops *frontend_ops = &fe->ops;
struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops;
- struct tuner_state state;
int err = 0;
- if (tuner_ops->get_state) {
+ if (tuner_ops->get_frequency) {
if (frontend_ops->i2c_gate_ctrl)
frontend_ops->i2c_gate_ctrl(fe, 1);
- err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &state);
+ err = tuner_ops->get_frequency(fe, frequency);
if (err < 0) {
- printk(KERN_ERR "%s: Invalid parameter\n", __func__);
+ printk("%s: Invalid parameter\n", __func__);
return err;
}
if (frontend_ops->i2c_gate_ctrl)
frontend_ops->i2c_gate_ctrl(fe, 0);
-
- *frequency = state.frequency;
}
return 0;
@@ -47,18 +47,21 @@ static int stb6100_set_freq(struct dvb_frontend *fe, u32 frequency)
{
struct dvb_frontend_ops *frontend_ops = &fe->ops;
struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops;
- struct tuner_state state;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 bw = c->bandwidth_hz;
int err = 0;
- state.frequency = frequency;
+ c->frequency = frequency;
+ c->bandwidth_hz = 0; /* Don't adjust the bandwidth */
- if (tuner_ops->set_state) {
+ if (tuner_ops->set_params) {
if (frontend_ops->i2c_gate_ctrl)
frontend_ops->i2c_gate_ctrl(fe, 1);
- err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &state);
+ err = tuner_ops->set_params(fe);
+ c->bandwidth_hz = bw;
if (err < 0) {
- printk(KERN_ERR "%s: Invalid parameter\n", __func__);
+ printk("%s: Invalid parameter\n", __func__);
return err;
}
@@ -74,14 +77,13 @@ static int stb6100_get_bandw(struct dvb_frontend *fe, u32 *bandwidth)
{
struct dvb_frontend_ops *frontend_ops = &fe->ops;
struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops;
- struct tuner_state state;
int err = 0;
- if (tuner_ops->get_state) {
+ if (tuner_ops->get_bandwidth) {
if (frontend_ops->i2c_gate_ctrl)
frontend_ops->i2c_gate_ctrl(fe, 1);
- err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &state);
+ err = tuner_ops->get_bandwidth(fe, bandwidth);
if (err < 0) {
printk(KERN_ERR "%s: Invalid parameter\n", __func__);
return err;
@@ -89,8 +91,6 @@ static int stb6100_get_bandw(struct dvb_frontend *fe, u32 *bandwidth)
if (frontend_ops->i2c_gate_ctrl)
frontend_ops->i2c_gate_ctrl(fe, 0);
-
- *bandwidth = state.bandwidth;
}
return 0;
@@ -100,16 +100,19 @@ static int stb6100_set_bandw(struct dvb_frontend *fe, u32 bandwidth)
{
struct dvb_frontend_ops *frontend_ops = &fe->ops;
struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops;
- struct tuner_state state;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 freq = c->frequency;
int err = 0;
- state.bandwidth = bandwidth;
+ c->bandwidth_hz = bandwidth;
+ c->frequency = 0; /* Don't adjust the frequency */
- if (tuner_ops->set_state) {
+ if (tuner_ops->set_params) {
if (frontend_ops->i2c_gate_ctrl)
frontend_ops->i2c_gate_ctrl(fe, 1);
- err = tuner_ops->set_state(fe, DVBFE_TUNER_BANDWIDTH, &state);
+ err = tuner_ops->set_params(fe);
+ c->frequency = freq;
if (err < 0) {
printk(KERN_ERR "%s: Invalid parameter\n", __func__);
return err;
diff --git a/drivers/media/dvb-frontends/tda665x.c b/drivers/media/dvb-frontends/tda665x.c
index 63cc12378d9a..82f8cc534f33 100644
--- a/drivers/media/dvb-frontends/tda665x.c
+++ b/drivers/media/dvb-frontends/tda665x.c
@@ -66,26 +66,13 @@ exit:
return err;
}
-static int tda665x_get_state(struct dvb_frontend *fe,
- enum tuner_param param,
- struct tuner_state *tstate)
+static int tda665x_get_frequency(struct dvb_frontend *fe, u32 *frequency)
{
struct tda665x_state *state = fe->tuner_priv;
- int err = 0;
- switch (param) {
- case DVBFE_TUNER_FREQUENCY:
- tstate->frequency = state->frequency;
- break;
- case DVBFE_TUNER_BANDWIDTH:
- break;
- default:
- printk(KERN_ERR "%s: Unknown parameter (param=%d)\n", __func__, param);
- err = -EINVAL;
- break;
- }
+ *frequency = state->frequency;
- return err;
+ return 0;
}
static int tda665x_get_status(struct dvb_frontend *fe, u32 *status)
@@ -111,9 +98,8 @@ exit:
return err;
}
-static int tda665x_set_state(struct dvb_frontend *fe,
- enum tuner_param param,
- struct tuner_state *tstate)
+static int tda665x_set_frequency(struct dvb_frontend *fe,
+ u32 new_frequency)
{
struct tda665x_state *state = fe->tuner_priv;
const struct tda665x_config *config = state->config;
@@ -121,88 +107,96 @@ static int tda665x_set_state(struct dvb_frontend *fe,
u8 buf[4];
int err = 0;
- if (param & DVBFE_TUNER_FREQUENCY) {
-
- frequency = tstate->frequency;
- if ((frequency < config->frequency_max) || (frequency > config->frequency_min)) {
- printk(KERN_ERR "%s: Frequency beyond limits, frequency=%d\n", __func__, frequency);
- return -EINVAL;
- }
-
- frequency += config->frequency_offst;
- frequency *= config->ref_multiplier;
- frequency += config->ref_divider >> 1;
- frequency /= config->ref_divider;
-
- buf[0] = (u8) ((frequency & 0x7f00) >> 8);
- buf[1] = (u8) (frequency & 0x00ff) >> 0;
- buf[2] = 0x80 | 0x40 | 0x02;
- buf[3] = 0x00;
-
- /* restore frequency */
- frequency = tstate->frequency;
-
- if (frequency < 153000000) {
- /* VHF-L */
- buf[3] |= 0x01; /* fc, Low Band, 47 - 153 MHz */
- if (frequency < 68000000)
- buf[3] |= 0x40; /* 83uA */
- if (frequency < 1040000000)
- buf[3] |= 0x60; /* 122uA */
- if (frequency < 1250000000)
- buf[3] |= 0x80; /* 163uA */
- else
- buf[3] |= 0xa0; /* 254uA */
- } else if (frequency < 438000000) {
- /* VHF-H */
- buf[3] |= 0x02; /* fc, Mid Band, 153 - 438 MHz */
- if (frequency < 230000000)
- buf[3] |= 0x40;
- if (frequency < 300000000)
- buf[3] |= 0x60;
- else
- buf[3] |= 0x80;
- } else {
- /* UHF */
- buf[3] |= 0x04; /* fc, High Band, 438 - 862 MHz */
- if (frequency < 470000000)
- buf[3] |= 0x60;
- if (frequency < 526000000)
- buf[3] |= 0x80;
- else
- buf[3] |= 0xa0;
- }
-
- /* Set params */
- err = tda665x_write(state, buf, 5);
- if (err < 0)
- goto exit;
-
- /* sleep for some time */
- printk(KERN_DEBUG "%s: Waiting to Phase LOCK\n", __func__);
- msleep(20);
- /* check status */
- err = tda665x_get_status(fe, &status);
- if (err < 0)
- goto exit;
-
- if (status == 1) {
- printk(KERN_DEBUG "%s: Tuner Phase locked: status=%d\n", __func__, status);
- state->frequency = frequency; /* cache successful state */
- } else {
- printk(KERN_ERR "%s: No Phase lock: status=%d\n", __func__, status);
- }
- } else {
- printk(KERN_ERR "%s: Unknown parameter (param=%d)\n", __func__, param);
+ if ((new_frequency < config->frequency_max)
+ || (new_frequency > config->frequency_min)) {
+ printk(KERN_ERR "%s: Frequency beyond limits, frequency=%d\n",
+ __func__, new_frequency);
return -EINVAL;
}
+ frequency = new_frequency;
+
+ frequency += config->frequency_offst;
+ frequency *= config->ref_multiplier;
+ frequency += config->ref_divider >> 1;
+ frequency /= config->ref_divider;
+
+ buf[0] = (u8) ((frequency & 0x7f00) >> 8);
+ buf[1] = (u8) (frequency & 0x00ff) >> 0;
+ buf[2] = 0x80 | 0x40 | 0x02;
+ buf[3] = 0x00;
+
+ /* restore frequency */
+ frequency = new_frequency;
+
+ if (frequency < 153000000) {
+ /* VHF-L */
+ buf[3] |= 0x01; /* fc, Low Band, 47 - 153 MHz */
+ if (frequency < 68000000)
+ buf[3] |= 0x40; /* 83uA */
+ if (frequency < 1040000000)
+ buf[3] |= 0x60; /* 122uA */
+ if (frequency < 1250000000)
+ buf[3] |= 0x80; /* 163uA */
+ else
+ buf[3] |= 0xa0; /* 254uA */
+ } else if (frequency < 438000000) {
+ /* VHF-H */
+ buf[3] |= 0x02; /* fc, Mid Band, 153 - 438 MHz */
+ if (frequency < 230000000)
+ buf[3] |= 0x40;
+ if (frequency < 300000000)
+ buf[3] |= 0x60;
+ else
+ buf[3] |= 0x80;
+ } else {
+ /* UHF */
+ buf[3] |= 0x04; /* fc, High Band, 438 - 862 MHz */
+ if (frequency < 470000000)
+ buf[3] |= 0x60;
+ if (frequency < 526000000)
+ buf[3] |= 0x80;
+ else
+ buf[3] |= 0xa0;
+ }
+
+ /* Set params */
+ err = tda665x_write(state, buf, 5);
+ if (err < 0)
+ goto exit;
+
+ /* sleep for some time */
+ printk(KERN_DEBUG "%s: Waiting to Phase LOCK\n", __func__);
+ msleep(20);
+ /* check status */
+ err = tda665x_get_status(fe, &status);
+ if (err < 0)
+ goto exit;
+
+ if (status == 1) {
+ printk(KERN_DEBUG "%s: Tuner Phase locked: status=%d\n",
+ __func__, status);
+ state->frequency = frequency; /* cache successful state */
+ } else {
+ printk(KERN_ERR "%s: No Phase lock: status=%d\n",
+ __func__, status);
+ }
+
return 0;
exit:
printk(KERN_ERR "%s: I/O Error\n", __func__);
return err;
}
+static int tda665x_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ tda665x_set_frequency(fe, c->frequency);
+
+ return 0;
+}
+
static int tda665x_release(struct dvb_frontend *fe)
{
struct tda665x_state *state = fe->tuner_priv;
@@ -213,10 +207,9 @@ static int tda665x_release(struct dvb_frontend *fe)
}
static struct dvb_tuner_ops tda665x_ops = {
-
- .set_state = tda665x_set_state,
- .get_state = tda665x_get_state,
.get_status = tda665x_get_status,
+ .set_params = tda665x_set_params,
+ .get_frequency = tda665x_get_frequency,
.release = tda665x_release
};
diff --git a/drivers/media/dvb-frontends/tda8261.c b/drivers/media/dvb-frontends/tda8261.c
index 19c488814e5c..3285b1bc4642 100644
--- a/drivers/media/dvb-frontends/tda8261.c
+++ b/drivers/media/dvb-frontends/tda8261.c
@@ -83,88 +83,71 @@ static int tda8261_get_status(struct dvb_frontend *fe, u32 *status)
static const u32 div_tab[] = { 2000, 1000, 500, 250, 125 }; /* kHz */
static const u8 ref_div[] = { 0x00, 0x01, 0x02, 0x05, 0x07 };
-static int tda8261_get_state(struct dvb_frontend *fe,
- enum tuner_param param,
- struct tuner_state *tstate)
+static int tda8261_get_frequency(struct dvb_frontend *fe, u32 *frequency)
{
struct tda8261_state *state = fe->tuner_priv;
- int err = 0;
- switch (param) {
- case DVBFE_TUNER_FREQUENCY:
- tstate->frequency = state->frequency;
- break;
- case DVBFE_TUNER_BANDWIDTH:
- tstate->bandwidth = 40000000; /* FIXME! need to calculate Bandwidth */
- break;
- default:
- pr_err("%s: Unknown parameter (param=%d)\n", __func__, param);
- err = -EINVAL;
- break;
- }
+ *frequency = state->frequency;
- return err;
+ return 0;
}
-static int tda8261_set_state(struct dvb_frontend *fe,
- enum tuner_param param,
- struct tuner_state *tstate)
+static int tda8261_set_params(struct dvb_frontend *fe)
{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
struct tda8261_state *state = fe->tuner_priv;
const struct tda8261_config *config = state->config;
u32 frequency, N, status = 0;
u8 buf[4];
int err = 0;
- if (param & DVBFE_TUNER_FREQUENCY) {
- /**
- * N = Max VCO Frequency / Channel Spacing
- * Max VCO Frequency = VCO frequency + (channel spacing - 1)
- * (to account for half channel spacing on either side)
- */
- frequency = tstate->frequency;
- if ((frequency < 950000) || (frequency > 2150000)) {
- pr_warn("%s: Frequency beyond limits, frequency=%d\n", __func__, frequency);
- return -EINVAL;
- }
- N = (frequency + (div_tab[config->step_size] - 1)) / div_tab[config->step_size];
- pr_debug("%s: Step size=%d, Divider=%d, PG=0x%02x (%d)\n",
- __func__, config->step_size, div_tab[config->step_size], N, N);
-
- buf[0] = (N >> 8) & 0xff;
- buf[1] = N & 0xff;
- buf[2] = (0x01 << 7) | ((ref_div[config->step_size] & 0x07) << 1);
-
- if (frequency < 1450000)
- buf[3] = 0x00;
- else if (frequency < 2000000)
- buf[3] = 0x40;
- else if (frequency < 2150000)
- buf[3] = 0x80;
-
- /* Set params */
- if ((err = tda8261_write(state, buf)) < 0) {
- pr_err("%s: I/O Error\n", __func__);
- return err;
- }
- /* sleep for some time */
- pr_debug("%s: Waiting to Phase LOCK\n", __func__);
- msleep(20);
- /* check status */
- if ((err = tda8261_get_status(fe, &status)) < 0) {
- pr_err("%s: I/O Error\n", __func__);
- return err;
- }
- if (status == 1) {
- pr_debug("%s: Tuner Phase locked: status=%d\n", __func__, status);
- state->frequency = frequency; /* cache successful state */
- } else {
- pr_debug("%s: No Phase lock: status=%d\n", __func__, status);
- }
- } else {
- pr_err("%s: Unknown parameter (param=%d)\n", __func__, param);
+ /*
+ * N = Max VCO Frequency / Channel Spacing
+ * Max VCO Frequency = VCO frequency + (channel spacing - 1)
+ * (to account for half channel spacing on either side)
+ */
+ frequency = c->frequency;
+ if ((frequency < 950000) || (frequency > 2150000)) {
+ pr_warn("%s: Frequency beyond limits, frequency=%d\n",
+ __func__, frequency);
return -EINVAL;
}
+ N = (frequency + (div_tab[config->step_size] - 1)) / div_tab[config->step_size];
+ pr_debug("%s: Step size=%d, Divider=%d, PG=0x%02x (%d)\n",
+ __func__, config->step_size, div_tab[config->step_size], N, N);
+
+ buf[0] = (N >> 8) & 0xff;
+ buf[1] = N & 0xff;
+ buf[2] = (0x01 << 7) | ((ref_div[config->step_size] & 0x07) << 1);
+
+ if (frequency < 1450000)
+ buf[3] = 0x00;
+ else if (frequency < 2000000)
+ buf[3] = 0x40;
+ else if (frequency < 2150000)
+ buf[3] = 0x80;
+
+ /* Set params */
+ err = tda8261_write(state, buf);
+ if (err < 0) {
+ pr_err("%s: I/O Error\n", __func__);
+ return err;
+ }
+ /* sleep for some time */
+ pr_debug("%s: Waiting to Phase LOCK\n", __func__);
+ msleep(20);
+ /* check status */
+ if ((err = tda8261_get_status(fe, &status)) < 0) {
+ pr_err("%s: I/O Error\n", __func__);
+ return err;
+ }
+ if (status == 1) {
+ pr_debug("%s: Tuner Phase locked: status=%d\n", __func__,
+ status);
+ state->frequency = frequency; /* cache successful state */
+ } else {
+ pr_debug("%s: No Phase lock: status=%d\n", __func__, status);
+ }
return 0;
}
@@ -182,14 +165,13 @@ static struct dvb_tuner_ops tda8261_ops = {
.info = {
.name = "TDA8261",
-// .tuner_name = NULL,
.frequency_min = 950000,
.frequency_max = 2150000,
.frequency_step = 0
},
- .set_state = tda8261_set_state,
- .get_state = tda8261_get_state,
+ .set_params = tda8261_set_params,
+ .get_frequency = tda8261_get_frequency,
.get_status = tda8261_get_status,
.release = tda8261_release
};
@@ -210,10 +192,7 @@ struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe,
fe->ops.tuner_ops = tda8261_ops;
fe->ops.tuner_ops.info.frequency_step = div_tab[config->step_size];
-// fe->ops.tuner_ops.tuner_name = &config->buf;
-// printk("%s: Attaching %s TDA8261 8PSK/QPSK tuner\n",
-// __func__, fe->ops.tuner_ops.tuner_name);
pr_info("%s: Attaching TDA8261 8PSK/QPSK tuner\n", __func__);
return fe;
diff --git a/drivers/media/dvb-frontends/tda8261_cfg.h b/drivers/media/dvb-frontends/tda8261_cfg.h
index 04a19e14ee5a..fe527ff84df4 100644
--- a/drivers/media/dvb-frontends/tda8261_cfg.h
+++ b/drivers/media/dvb-frontends/tda8261_cfg.h
@@ -21,17 +21,15 @@ static int tda8261_get_frequency(struct dvb_frontend *fe, u32 *frequency)
{
struct dvb_frontend_ops *frontend_ops = &fe->ops;
struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops;
- struct tuner_state t_state;
int err = 0;
- if (tuner_ops->get_state) {
- err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &t_state);
+ if (tuner_ops->get_frequency) {
+ err = tuner_ops->get_frequency(fe, frequency);
if (err < 0) {
- printk("%s: Invalid parameter\n", __func__);
+ pr_err("%s: Invalid parameter\n", __func__);
return err;
}
- *frequency = t_state.frequency;
- printk("%s: Frequency=%d\n", __func__, t_state.frequency);
+ pr_debug("%s: Frequency=%d\n", __func__, *frequency);
}
return 0;
}
@@ -40,37 +38,24 @@ static int tda8261_set_frequency(struct dvb_frontend *fe, u32 frequency)
{
struct dvb_frontend_ops *frontend_ops = &fe->ops;
struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops;
- struct tuner_state t_state;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int err = 0;
- t_state.frequency = frequency;
-
- if (tuner_ops->set_state) {
- err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &t_state);
+ if (tuner_ops->set_params) {
+ err = tuner_ops->set_params(fe);
if (err < 0) {
- printk("%s: Invalid parameter\n", __func__);
+ pr_err("%s: Invalid parameter\n", __func__);
return err;
}
}
- printk("%s: Frequency=%d\n", __func__, t_state.frequency);
+ pr_debug("%s: Frequency=%d\n", __func__, c->frequency);
return 0;
}
static int tda8261_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
{
- struct dvb_frontend_ops *frontend_ops = &fe->ops;
- struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops;
- struct tuner_state t_state;
- int err = 0;
+ /* FIXME! need to calculate Bandwidth */
+ *bandwidth = 40000000;
- if (tuner_ops->get_state) {
- err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state);
- if (err < 0) {
- printk("%s: Invalid parameter\n", __func__);
- return err;
- }
- *bandwidth = t_state.bandwidth;
- printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth);
- }
return 0;
}
diff --git a/drivers/media/dvb-frontends/tdhd1.h b/drivers/media/dvb-frontends/tdhd1.h
index 17750985db0c..2b9e8732c802 100644
--- a/drivers/media/dvb-frontends/tdhd1.h
+++ b/drivers/media/dvb-frontends/tdhd1.h
@@ -20,7 +20,7 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
- * The project's page is at http://www.linuxtv.org
+ * The project's page is at https://linuxtv.org
*/
#ifndef TDHD1_H
diff --git a/drivers/media/firewire/firedtv-ci.c b/drivers/media/firewire/firedtv-ci.c
index e63f582378bf..edbb30fdd9d9 100644
--- a/drivers/media/firewire/firedtv-ci.c
+++ b/drivers/media/firewire/firedtv-ci.c
@@ -241,7 +241,7 @@ int fdtv_ca_register(struct firedtv *fdtv)
return -EFAULT;
err = dvb_register_device(&fdtv->adapter, &fdtv->cadev,
- &fdtv_ca, fdtv, DVB_DEVICE_CA);
+ &fdtv_ca, fdtv, DVB_DEVICE_CA, 0);
if (stat.ca_application_info == 0)
dev_err(fdtv->device, "CaApplicationInfo is not set\n");
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 521bbf1b29bc..993dc50c12db 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -83,6 +83,16 @@ config VIDEO_MSP3400
To compile this driver as a module, choose M here: the
module will be called msp3400.
+config VIDEO_CS3308
+ tristate "Cirrus Logic CS3308 audio ADC"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Cirrus Logic CS3308 High Performance 8-Channel
+ Analog Volume Control
+
+ To compile this driver as a module, choose M here: the
+ module will be called cs3308.
+
config VIDEO_CS5345
tristate "Cirrus Logic CS5345 audio ADC"
depends on VIDEO_V4L2 && I2C
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 07db257abfc1..94f2c99e890d 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o
obj-$(CONFIG_VIDEO_TW2804) += tw2804.o
obj-$(CONFIG_VIDEO_TW9903) += tw9903.o
obj-$(CONFIG_VIDEO_TW9906) += tw9906.o
+obj-$(CONFIG_VIDEO_CS3308) += cs3308.o
obj-$(CONFIG_VIDEO_CS5345) += cs5345.o
obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o
obj-$(CONFIG_VIDEO_M52790) += m52790.o
diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c
index 69094ab047b1..788967dadd29 100644
--- a/drivers/media/i2c/ad9389b.c
+++ b/drivers/media/i2c/ad9389b.c
@@ -35,7 +35,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-dv-timings.h>
#include <media/v4l2-ctrls.h>
-#include <media/ad9389b.h>
+#include <media/i2c/ad9389b.h>
static int debug;
module_param(debug, int, 0644);
@@ -1158,7 +1158,7 @@ static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id *
state->rgb_quantization_range_ctrl->is_private = true;
state->pad.flags = MEDIA_PAD_FL_SINK;
- err = media_entity_init(&sd->entity, 1, &state->pad, 0);
+ err = media_entity_pads_init(&sd->entity, 1, &state->pad);
if (err)
goto err_hdl;
diff --git a/drivers/media/i2c/adp1653.c b/drivers/media/i2c/adp1653.c
index 5dd39775d6ca..7e9cbf757e95 100644
--- a/drivers/media/i2c/adp1653.c
+++ b/drivers/media/i2c/adp1653.c
@@ -37,7 +37,7 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/gpio/consumer.h>
-#include <media/adp1653.h>
+#include <media/i2c/adp1653.h>
#include <media/v4l2-device.h>
#define TIMEOUT_MAX 820000
@@ -512,11 +512,11 @@ static int adp1653_probe(struct i2c_client *client,
if (ret)
goto free_and_quit;
- ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0);
+ ret = media_entity_pads_init(&flash->subdev.entity, 0, NULL);
if (ret < 0)
goto free_and_quit;
- flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
+ flash->subdev.entity.function = MEDIA_ENT_F_FLASH;
return 0;
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index f82c8aa164fa..ff57c1dcb8af 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -1112,7 +1112,7 @@ static int init_device(struct adv7180_state *state)
mutex_lock(&state->mutex);
adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES);
- usleep_range(2000, 10000);
+ usleep_range(5000, 10000);
ret = state->chip_info->init(state);
if (ret)
@@ -1213,8 +1213,8 @@ static int adv7180_probe(struct i2c_client *client,
goto err_unregister_vpp_client;
state->pad.flags = MEDIA_PAD_FL_SOURCE;
- sd->entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER;
- ret = media_entity_init(&sd->entity, 1, &state->pad, 0);
+ sd->entity.flags |= MEDIA_ENT_F_ATV_DECODER;
+ ret = media_entity_pads_init(&sd->entity, 1, &state->pad);
if (ret)
goto err_free_ctrl;
diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c
index e2dd1617662f..2bec737881e9 100644
--- a/drivers/media/i2c/adv7183.c
+++ b/drivers/media/i2c/adv7183.c
@@ -27,7 +27,7 @@
#include <linux/types.h>
#include <linux/videodev2.h>
-#include <media/adv7183.h>
+#include <media/i2c/adv7183.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c
index f89d0afcd964..11f9029433cf 100644
--- a/drivers/media/i2c/adv7343.c
+++ b/drivers/media/i2c/adv7343.c
@@ -28,7 +28,7 @@
#include <linux/of.h>
#include <linux/of_graph.h>
-#include <media/adv7343.h>
+#include <media/i2c/adv7343.h>
#include <media/v4l2-async.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
diff --git a/drivers/media/i2c/adv7393.c b/drivers/media/i2c/adv7393.c
index 0215f95c2245..76d987476e35 100644
--- a/drivers/media/i2c/adv7393.c
+++ b/drivers/media/i2c/adv7393.c
@@ -31,7 +31,7 @@
#include <linux/videodev2.h>
#include <linux/uaccess.h>
-#include <media/adv7393.h>
+#include <media/i2c/adv7393.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c
index e4900df1140b..471fd23b5c5c 100644
--- a/drivers/media/i2c/adv7511.c
+++ b/drivers/media/i2c/adv7511.c
@@ -32,7 +32,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-dv-timings.h>
-#include <media/adv7511.h>
+#include <media/i2c/adv7511.h>
static int debug;
module_param(debug, int, 0644);
@@ -1116,7 +1116,7 @@ static int adv7511_set_fmt(struct v4l2_subdev *sd,
adv7511_wr_and_or(sd, 0x55, 0x9f, y << 5);
adv7511_wr_and_or(sd, 0x56, 0x3f, c << 6);
adv7511_wr_and_or(sd, 0x57, 0x83, (ec << 4) | (q << 2));
- adv7511_wr_and_or(sd, 0x59, 0x0f, yq << 4);
+ adv7511_wr_and_or(sd, 0x59, 0x3f, yq << 6);
adv7511_wr_and_or(sd, 0x4a, 0xff, 1);
return 0;
@@ -1482,7 +1482,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
state->rgb_quantization_range_ctrl->is_private = true;
state->pad.flags = MEDIA_PAD_FL_SINK;
- err = media_entity_init(&sd->entity, 1, &state->pad, 0);
+ err = media_entity_pads_init(&sd->entity, 1, &state->pad);
if (err)
goto err_hdl;
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index 5631ec004eed..f8dd7505b529 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -39,7 +39,7 @@
#include <linux/workqueue.h>
#include <linux/regmap.h>
-#include <media/adv7604.h>
+#include <media/i2c/adv7604.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
@@ -905,7 +905,7 @@ static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,
for (i = 0; predef_vid_timings[i].timings.bt.width; i++) {
if (!v4l2_match_dv_timings(timings, &predef_vid_timings[i].timings,
- is_digital_input(sd) ? 250000 : 1000000))
+ is_digital_input(sd) ? 250000 : 1000000, false))
continue;
io_write(sd, 0x00, predef_vid_timings[i].vid_std); /* video std */
io_write(sd, 0x01, (predef_vid_timings[i].v_freq << 4) +
@@ -1479,7 +1479,7 @@ static void adv76xx_fill_optional_dv_timings_fields(struct v4l2_subdev *sd,
for (i = 0; adv76xx_timings[i].bt.width; i++) {
if (v4l2_match_dv_timings(timings, &adv76xx_timings[i],
- is_digital_input(sd) ? 250000 : 1000000)) {
+ is_digital_input(sd) ? 250000 : 1000000, false)) {
*timings = adv76xx_timings[i];
break;
}
@@ -1644,7 +1644,7 @@ static int adv76xx_s_dv_timings(struct v4l2_subdev *sd,
if (!timings)
return -EINVAL;
- if (v4l2_match_dv_timings(&state->timings, timings, 0)) {
+ if (v4l2_match_dv_timings(&state->timings, timings, 0, false)) {
v4l2_dbg(1, debug, sd, "%s: no change\n", __func__);
return 0;
}
@@ -3208,8 +3208,8 @@ static int adv76xx_probe(struct i2c_client *client,
state->pads[i].flags = MEDIA_PAD_FL_SINK;
state->pads[state->source_pad].flags = MEDIA_PAD_FL_SOURCE;
- err = media_entity_init(&sd->entity, state->source_pad + 1,
- state->pads, 0);
+ err = media_entity_pads_init(&sd->entity, state->source_pad + 1,
+ state->pads);
if (err)
goto err_work_queues;
diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c
index b7269b8f040d..5fbb788e7b59 100644
--- a/drivers/media/i2c/adv7842.c
+++ b/drivers/media/i2c/adv7842.c
@@ -43,7 +43,7 @@
#include <media/v4l2-event.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-dv-timings.h>
-#include <media/adv7842.h>
+#include <media/i2c/adv7842.h>
static int debug;
module_param(debug, int, 0644);
@@ -155,7 +155,7 @@ static bool adv7842_check_dv_timings(const struct v4l2_dv_timings *t, void *hdl)
int i;
for (i = 0; adv7842_timings_exceptions[i].bt.width; i++)
- if (v4l2_match_dv_timings(t, adv7842_timings_exceptions + i, 0))
+ if (v4l2_match_dv_timings(t, adv7842_timings_exceptions + i, 0, false))
return false;
return true;
}
@@ -1008,7 +1008,7 @@ static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd,
for (i = 0; predef_vid_timings[i].timings.bt.width; i++) {
if (!v4l2_match_dv_timings(timings, &predef_vid_timings[i].timings,
- is_digital_input(sd) ? 250000 : 1000000))
+ is_digital_input(sd) ? 250000 : 1000000, false))
continue;
/* video std */
io_write(sd, 0x00, predef_vid_timings[i].vid_std);
@@ -1659,7 +1659,7 @@ static int adv7842_s_dv_timings(struct v4l2_subdev *sd,
if (state->mode == ADV7842_MODE_SDP)
return -ENODATA;
- if (v4l2_match_dv_timings(&state->timings, timings, 0)) {
+ if (v4l2_match_dv_timings(&state->timings, timings, 0, false)) {
v4l2_dbg(1, debug, sd, "%s: no change\n", __func__);
return 0;
}
@@ -3309,7 +3309,7 @@ static int adv7842_probe(struct i2c_client *client,
adv7842_delayed_work_enable_hotplug);
state->pad.flags = MEDIA_PAD_FL_SOURCE;
- err = media_entity_init(&sd->entity, 1, &state->pad, 0);
+ err = media_entity_pads_init(&sd->entity, 1, &state->pad);
if (err)
goto err_work_queues;
diff --git a/drivers/media/i2c/ak881x.c b/drivers/media/i2c/ak881x.c
index d3b965ec3bbc..d9f2b6b76d59 100644
--- a/drivers/media/i2c/ak881x.c
+++ b/drivers/media/i2c/ak881x.c
@@ -15,7 +15,7 @@
#include <linux/videodev2.h>
#include <linux/module.h>
-#include <media/ak881x.h>
+#include <media/i2c/ak881x.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
diff --git a/drivers/media/i2c/as3645a.c b/drivers/media/i2c/as3645a.c
index 301084b07887..2e90e4094b79 100644
--- a/drivers/media/i2c/as3645a.c
+++ b/drivers/media/i2c/as3645a.c
@@ -31,7 +31,7 @@
#include <linux/mutex.h>
#include <linux/slab.h>
-#include <media/as3645a.h>
+#include <media/i2c/as3645a.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
@@ -827,11 +827,11 @@ static int as3645a_probe(struct i2c_client *client,
if (ret < 0)
goto done;
- ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0);
+ ret = media_entity_pads_init(&flash->subdev.entity, 0, NULL);
if (ret < 0)
goto done;
- flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
+ flash->subdev.entity.function = MEDIA_ENT_F_FLASH;
mutex_init(&flash->power_lock);
diff --git a/drivers/media/i2c/bt819.c b/drivers/media/i2c/bt819.c
index e00e3104d448..7907bcfbaed3 100644
--- a/drivers/media/i2c/bt819.c
+++ b/drivers/media/i2c/bt819.c
@@ -37,7 +37,7 @@
#include <linux/slab.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
-#include <media/bt819.h>
+#include <media/i2c/bt819.h>
MODULE_DESCRIPTION("Brooktree-819 video decoder driver");
MODULE_AUTHOR("Mike Bernson & Dave Perks");
diff --git a/drivers/media/i2c/cs3308.c b/drivers/media/i2c/cs3308.c
new file mode 100644
index 000000000000..d28b4f37fe5f
--- /dev/null
+++ b/drivers/media/i2c/cs3308.c
@@ -0,0 +1,138 @@
+/*
+ * Cirrus Logic cs3308 8-Channel Analog Volume Control
+ *
+ * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
+ * Copyright (C) 2012 Steven Toth <stoth@kernellabs.com>
+ *
+ * Derived from cs5345.c Copyright (C) 2007 Hans Verkuil
+ *
+ * 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/kernel.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+
+MODULE_DESCRIPTION("i2c device driver for cs3308 8-channel volume control");
+MODULE_AUTHOR("Devin Heitmueller");
+MODULE_LICENSE("GPL");
+
+static inline int cs3308_write(struct v4l2_subdev *sd, u8 reg, u8 value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static inline int cs3308_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cs3308_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ reg->val = cs3308_read(sd, reg->reg & 0xffff);
+ reg->size = 1;
+ return 0;
+}
+
+static int cs3308_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
+{
+ cs3308_write(sd, reg->reg & 0xffff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops cs3308_core_ops = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = cs3308_g_register,
+ .s_register = cs3308_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_ops cs3308_ops = {
+ .core = &cs3308_core_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int cs3308_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct v4l2_subdev *sd;
+ unsigned i;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ if ((i2c_smbus_read_byte_data(client, 0x1c) & 0xf0) != 0xe0)
+ return -ENODEV;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ if (sd == NULL)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(sd, client, &cs3308_ops);
+
+ /* Set some reasonable defaults */
+ cs3308_write(sd, 0x0d, 0x00); /* Power up all channels */
+ cs3308_write(sd, 0x0e, 0x00); /* Master Power */
+ cs3308_write(sd, 0x0b, 0x00); /* Device Configuration */
+ /* Set volume for each channel */
+ for (i = 1; i <= 8; i++)
+ cs3308_write(sd, i, 0xd2);
+ cs3308_write(sd, 0x0a, 0x00); /* Unmute all channels */
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int cs3308_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_device_id cs3308_id[] = {
+ { "cs3308", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cs3308_id);
+
+static struct i2c_driver cs3308_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "cs3308",
+ },
+ .probe = cs3308_probe,
+ .remove = cs3308_remove,
+ .id_table = cs3308_id,
+};
+
+module_i2c_driver(cs3308_driver);
diff --git a/drivers/media/i2c/cx25840/cx25840-audio.c b/drivers/media/i2c/cx25840/cx25840-audio.c
index 34b96c7cfd62..baf3d9c8710e 100644
--- a/drivers/media/i2c/cx25840/cx25840-audio.c
+++ b/drivers/media/i2c/cx25840/cx25840-audio.c
@@ -19,7 +19,7 @@
#include <linux/videodev2.h>
#include <linux/i2c.h>
#include <media/v4l2-common.h>
-#include <media/cx25840.h>
+#include <media/drv-intf/cx25840.h>
#include "cx25840-core.h"
diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c
index fe6eb78b6914..07a3e7173144 100644
--- a/drivers/media/i2c/cx25840/cx25840-core.c
+++ b/drivers/media/i2c/cx25840/cx25840-core.c
@@ -45,7 +45,7 @@
#include <linux/delay.h>
#include <linux/math64.h>
#include <media/v4l2-common.h>
-#include <media/cx25840.h>
+#include <media/drv-intf/cx25840.h>
#include "cx25840-core.h"
@@ -559,7 +559,10 @@ static void cx23885_initialize(struct i2c_client *client)
cx25840_write4(client, 0x414, 0x00107d12);
/* Chroma */
- cx25840_write4(client, 0x420, 0x3d008282);
+ if (is_cx23888(state))
+ cx25840_write4(client, 0x418, 0x1d008282);
+ else
+ cx25840_write4(client, 0x420, 0x3d008282);
/*
* Aux PLL
@@ -666,14 +669,17 @@ static void cx23885_initialize(struct i2c_client *client)
cx25840_write4(client, 0x404, 0x0010253e);
/* CC on - Undocumented Register */
- cx25840_write(client, 0x42f, 0x66);
+ cx25840_write(client, state->vbi_regs_offset + 0x42f, 0x66);
/* HVR-1250 / HVR1850 DIF related */
/* Power everything up */
cx25840_write4(client, 0x130, 0x0);
/* Undocumented */
- cx25840_write4(client, 0x478, 0x6628021F);
+ if (is_cx23888(state))
+ cx25840_write4(client, 0x454, 0x6628021F);
+ else
+ cx25840_write4(client, 0x478, 0x6628021F);
/* AFE_CLK_OUT_CTRL - Select the clock output source as output */
cx25840_write4(client, 0x144, 0x5);
@@ -1106,31 +1112,15 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
cx25840_write4(client, 0x410, 0xffff0dbf);
cx25840_write4(client, 0x414, 0x00137d03);
- /* on the 887, 0x418 is HSCALE_CTRL, on the 888 it is
- CHROMA_CTRL */
- if (is_cx23888(state))
- cx25840_write4(client, 0x418, 0x01008080);
- else
- cx25840_write4(client, 0x418, 0x01000000);
+ cx25840_write4(client, state->vbi_regs_offset + 0x42c, 0x42600000);
+ cx25840_write4(client, state->vbi_regs_offset + 0x430, 0x0000039b);
+ cx25840_write4(client, state->vbi_regs_offset + 0x438, 0x00000000);
- cx25840_write4(client, 0x41c, 0x00000000);
-
- /* on the 887, 0x420 is CHROMA_CTRL, on the 888 it is
- CRUSH_CTRL */
- if (is_cx23888(state))
- cx25840_write4(client, 0x420, 0x001c3e0f);
- else
- cx25840_write4(client, 0x420, 0x001c8282);
-
- cx25840_write4(client, 0x42c, 0x42600000);
- cx25840_write4(client, 0x430, 0x0000039b);
- cx25840_write4(client, 0x438, 0x00000000);
-
- cx25840_write4(client, 0x440, 0xF8E3E824);
- cx25840_write4(client, 0x444, 0x401040dc);
- cx25840_write4(client, 0x448, 0xcd3f02a0);
- cx25840_write4(client, 0x44c, 0x161f1000);
- cx25840_write4(client, 0x450, 0x00000802);
+ cx25840_write4(client, state->vbi_regs_offset + 0x440, 0xF8E3E824);
+ cx25840_write4(client, state->vbi_regs_offset + 0x444, 0x401040dc);
+ cx25840_write4(client, state->vbi_regs_offset + 0x448, 0xcd3f02a0);
+ cx25840_write4(client, state->vbi_regs_offset + 0x44c, 0x161f1000);
+ cx25840_write4(client, state->vbi_regs_offset + 0x450, 0x00000802);
cx25840_write4(client, 0x91c, 0x01000000);
cx25840_write4(client, 0x8e0, 0x03063870);
@@ -1400,8 +1390,14 @@ static int cx25840_set_fmt(struct v4l2_subdev *sd,
Vlines = fmt->height + (is_50Hz ? 4 : 7);
+ /*
+ * We keep 1 margin for the Vsrc < Vlines check since the
+ * cx23888 reports a Vsrc of 486 instead of 487 for the NTSC
+ * height. Without that margin the cx23885 fails in this
+ * check.
+ */
if ((fmt->width * 16 < Hsrc) || (Hsrc < fmt->width) ||
- (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) {
+ (Vlines * 8 < Vsrc) || (Vsrc + 1 < Vlines)) {
v4l_err(client, "%dx%d is not a valid size!\n",
fmt->width, fmt->height);
return -ERANGE;
@@ -1426,14 +1422,20 @@ static int cx25840_set_fmt(struct v4l2_subdev *sd,
fmt->width, fmt->height, HSC, VSC);
/* HSCALE=HSC */
- cx25840_write(client, 0x418, HSC & 0xff);
- cx25840_write(client, 0x419, (HSC >> 8) & 0xff);
- cx25840_write(client, 0x41a, HSC >> 16);
- /* VSCALE=VSC */
- cx25840_write(client, 0x41c, VSC & 0xff);
- cx25840_write(client, 0x41d, VSC >> 8);
- /* VS_INTRLACE=1 VFILT=filter */
- cx25840_write(client, 0x41e, 0x8 | filter);
+ if (is_cx23888(state)) {
+ cx25840_write4(client, 0x434, HSC | (1 << 24));
+ /* VSCALE=VSC VS_INTRLACE=1 VFILT=filter */
+ cx25840_write4(client, 0x438, VSC | (1 << 19) | (filter << 16));
+ } else {
+ cx25840_write(client, 0x418, HSC & 0xff);
+ cx25840_write(client, 0x419, (HSC >> 8) & 0xff);
+ cx25840_write(client, 0x41a, HSC >> 16);
+ /* VSCALE=VSC */
+ cx25840_write(client, 0x41c, VSC & 0xff);
+ cx25840_write(client, 0x41d, VSC >> 8);
+ /* VS_INTRLACE=1 VFILT=filter */
+ cx25840_write(client, 0x41e, 0x8 | filter);
+ }
return 0;
}
@@ -1714,26 +1716,27 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
v4l_dbg(1, cx25840_debug, client, "%s video output\n",
enable ? "enable" : "disable");
+
+ /*
+ * It's not clear what should be done for these devices.
+ * The original code used the same addresses as for the cx25840, but
+ * those addresses do something else entirely on the cx2388x and
+ * cx231xx. Since it never did anything in the first place, just do
+ * nothing.
+ */
+ if (is_cx2388x(state) || is_cx231xx(state))
+ return 0;
+
if (enable) {
- if (is_cx2388x(state) || is_cx231xx(state)) {
- v = cx25840_read(client, 0x421) | 0x0b;
- cx25840_write(client, 0x421, v);
- } else {
- v = cx25840_read(client, 0x115) | 0x0c;
- cx25840_write(client, 0x115, v);
- v = cx25840_read(client, 0x116) | 0x04;
- cx25840_write(client, 0x116, v);
- }
+ v = cx25840_read(client, 0x115) | 0x0c;
+ cx25840_write(client, 0x115, v);
+ v = cx25840_read(client, 0x116) | 0x04;
+ cx25840_write(client, 0x116, v);
} else {
- if (is_cx2388x(state) || is_cx231xx(state)) {
- v = cx25840_read(client, 0x421) & ~(0x0b);
- cx25840_write(client, 0x421, v);
- } else {
- v = cx25840_read(client, 0x115) & ~(0x0c);
- cx25840_write(client, 0x115, v);
- v = cx25840_read(client, 0x116) & ~(0x04);
- cx25840_write(client, 0x116, v);
- }
+ v = cx25840_read(client, 0x115) & ~(0x0c);
+ cx25840_write(client, 0x115, v);
+ v = cx25840_read(client, 0x116) & ~(0x04);
+ cx25840_write(client, 0x116, v);
}
return 0;
}
@@ -4974,7 +4977,7 @@ static void cx23888_std_setup(struct i2c_client *client)
cx25840_write4(client, 0x4b4, 0x20524030);
cx25840_write4(client, 0x47c, 0x010a8263);
- if (std & V4L2_STD_NTSC) {
+ if (std & V4L2_STD_525_60) {
v4l_dbg(1, cx25840_debug, client, "%s() Selecting NTSC",
__func__);
@@ -5208,10 +5211,10 @@ static int cx25840_probe(struct i2c_client *client,
state->pads[CX25840_PAD_INPUT].flags = MEDIA_PAD_FL_SINK;
state->pads[CX25840_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE;
state->pads[CX25840_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE;
- sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_DECODER;
+ sd->entity.function = MEDIA_ENT_F_ATV_DECODER;
- ret = media_entity_init(&sd->entity, ARRAY_SIZE(state->pads),
- state->pads, 0);
+ ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(state->pads),
+ state->pads);
if (ret < 0) {
v4l_info(client, "failed to initialize media entity!\n");
return ret;
@@ -5264,6 +5267,8 @@ static int cx25840_probe(struct i2c_client *client,
state->vbi_line_offset = 8;
state->id = id;
state->rev = device_id;
+ state->vbi_regs_offset = id == CX23888_AV ? 0x500 - 0x424 : 0;
+ state->std = V4L2_STD_NTSC_M;
v4l2_ctrl_handler_init(&state->hdl, 9);
v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
diff --git a/drivers/media/i2c/cx25840/cx25840-core.h b/drivers/media/i2c/cx25840/cx25840-core.h
index fdea48ce0c03..254ef45ce41a 100644
--- a/drivers/media/i2c/cx25840/cx25840-core.h
+++ b/drivers/media/i2c/cx25840/cx25840-core.h
@@ -69,6 +69,7 @@ struct cx25840_state {
enum cx25840_model id;
u32 rev;
int is_initialized;
+ unsigned vbi_regs_offset;
wait_queue_head_t fw_wait; /* wake up when the fw load is finished */
struct work_struct fw_work; /* work entry for fw load */
struct cx25840_ir_state *ir_state;
diff --git a/drivers/media/i2c/cx25840/cx25840-firmware.c b/drivers/media/i2c/cx25840/cx25840-firmware.c
index 9bbb31adc29d..37e052923a87 100644
--- a/drivers/media/i2c/cx25840/cx25840-firmware.c
+++ b/drivers/media/i2c/cx25840/cx25840-firmware.c
@@ -19,7 +19,7 @@
#include <linux/i2c.h>
#include <linux/firmware.h>
#include <media/v4l2-common.h>
-#include <media/cx25840.h>
+#include <media/drv-intf/cx25840.h>
#include "cx25840-core.h"
diff --git a/drivers/media/i2c/cx25840/cx25840-ir.c b/drivers/media/i2c/cx25840/cx25840-ir.c
index 4cf8f18bf097..4b782012cadc 100644
--- a/drivers/media/i2c/cx25840/cx25840-ir.c
+++ b/drivers/media/i2c/cx25840/cx25840-ir.c
@@ -24,7 +24,7 @@
#include <linux/slab.h>
#include <linux/kfifo.h>
#include <linux/module.h>
-#include <media/cx25840.h>
+#include <media/drv-intf/cx25840.h>
#include <media/rc-core.h>
#include "cx25840-core.h"
diff --git a/drivers/media/i2c/cx25840/cx25840-vbi.c b/drivers/media/i2c/cx25840/cx25840-vbi.c
index c39e91dc1137..0470bb6128e1 100644
--- a/drivers/media/i2c/cx25840/cx25840-vbi.c
+++ b/drivers/media/i2c/cx25840/cx25840-vbi.c
@@ -19,7 +19,7 @@
#include <linux/videodev2.h>
#include <linux/i2c.h>
#include <media/v4l2-common.h>
-#include <media/cx25840.h>
+#include <media/drv-intf/cx25840.h>
#include "cx25840-core.h"
@@ -104,7 +104,8 @@ int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *
if (is_pal) {
for (i = 7; i <= 23; i++) {
- u8 v = cx25840_read(client, 0x424 + i - 7);
+ u8 v = cx25840_read(client,
+ state->vbi_regs_offset + 0x424 + i - 7);
svbi->service_lines[0][i] = lcr2vbi[v >> 4];
svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
@@ -113,7 +114,8 @@ int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *
}
} else {
for (i = 10; i <= 21; i++) {
- u8 v = cx25840_read(client, 0x424 + i - 10);
+ u8 v = cx25840_read(client,
+ state->vbi_regs_offset + 0x424 + i - 10);
svbi->service_lines[0][i] = lcr2vbi[v >> 4];
svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
@@ -135,7 +137,10 @@ int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
cx25840_std_setup(client);
/* VBI Offset */
- cx25840_write(client, 0x47f, vbi_offset);
+ if (is_cx23888(state))
+ cx25840_write(client, 0x54f, vbi_offset);
+ else
+ cx25840_write(client, 0x47f, vbi_offset);
cx25840_write(client, 0x404, 0x2e);
return 0;
}
@@ -158,7 +163,10 @@ int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *
/* Sliced VBI */
cx25840_write(client, 0x404, 0x32); /* Ancillary data */
cx25840_write(client, 0x406, 0x13);
- cx25840_write(client, 0x47f, vbi_offset);
+ if (is_cx23888(state))
+ cx25840_write(client, 0x54f, vbi_offset);
+ else
+ cx25840_write(client, 0x47f, vbi_offset);
if (is_pal) {
for (i = 0; i <= 6; i++)
@@ -194,17 +202,23 @@ int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *
}
if (is_pal) {
- for (x = 1, i = 0x424; i <= 0x434; i++, x++)
+ for (x = 1, i = state->vbi_regs_offset + 0x424;
+ i <= state->vbi_regs_offset + 0x434; i++, x++)
cx25840_write(client, i, lcr[6 + x]);
} else {
- for (x = 1, i = 0x424; i <= 0x430; i++, x++)
+ for (x = 1, i = state->vbi_regs_offset + 0x424;
+ i <= state->vbi_regs_offset + 0x430; i++, x++)
cx25840_write(client, i, lcr[9 + x]);
- for (i = 0x431; i <= 0x434; i++)
+ for (i = state->vbi_regs_offset + 0x431;
+ i <= state->vbi_regs_offset + 0x434; i++)
cx25840_write(client, i, 0);
}
- cx25840_write(client, 0x43c, 0x16);
- cx25840_write(client, 0x474, is_pal ? 0x2a : 0x22);
+ cx25840_write(client, state->vbi_regs_offset + 0x43c, 0x16);
+ if (is_cx23888(state))
+ cx25840_write(client, 0x428, is_pal ? 0x2a : 0x22);
+ else
+ cx25840_write(client, 0x474, is_pal ? 0x2a : 0x22);
return 0;
}
diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c
index 728d2cc8a3e7..830491960add 100644
--- a/drivers/media/i2c/ir-kbd-i2c.c
+++ b/drivers/media/i2c/ir-kbd-i2c.c
@@ -47,7 +47,7 @@
#include <linux/workqueue.h>
#include <media/rc-core.h>
-#include <media/ir-kbd-i2c.h>
+#include <media/i2c/ir-kbd-i2c.h>
/* ----------------------------------------------------------------------- */
/* insmod parameters */
diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c
index d9ece4b2d047..251a2aaf98c3 100644
--- a/drivers/media/i2c/lm3560.c
+++ b/drivers/media/i2c/lm3560.c
@@ -24,7 +24,7 @@
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/videodev2.h>
-#include <media/lm3560.h>
+#include <media/i2c/lm3560.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
@@ -365,10 +365,10 @@ static int lm3560_subdev_init(struct lm3560_flash *flash,
rval = lm3560_init_controls(flash, led_no);
if (rval)
goto err_out;
- rval = media_entity_init(&flash->subdev_led[led_no].entity, 0, NULL, 0);
+ rval = media_entity_pads_init(&flash->subdev_led[led_no].entity, 0, NULL);
if (rval < 0)
goto err_out;
- flash->subdev_led[led_no].entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
+ flash->subdev_led[led_no].entity.function = MEDIA_ENT_F_FLASH;
return rval;
diff --git a/drivers/media/i2c/lm3646.c b/drivers/media/i2c/lm3646.c
index 626fb4679c02..7e9967af36ec 100644
--- a/drivers/media/i2c/lm3646.c
+++ b/drivers/media/i2c/lm3646.c
@@ -18,7 +18,7 @@
#include <linux/slab.h>
#include <linux/regmap.h>
#include <linux/videodev2.h>
-#include <media/lm3646.h>
+#include <media/i2c/lm3646.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
@@ -282,10 +282,10 @@ static int lm3646_subdev_init(struct lm3646_flash *flash)
rval = lm3646_init_controls(flash);
if (rval)
goto err_out;
- rval = media_entity_init(&flash->subdev_led.entity, 0, NULL, 0);
+ rval = media_entity_pads_init(&flash->subdev_led.entity, 0, NULL);
if (rval < 0)
goto err_out;
- flash->subdev_led.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
+ flash->subdev_led.entity.function = MEDIA_ENT_F_FLASH;
return rval;
err_out:
diff --git a/drivers/media/i2c/m52790.c b/drivers/media/i2c/m52790.c
index 77eb07eb667e..81171d8e1c2c 100644
--- a/drivers/media/i2c/m52790.c
+++ b/drivers/media/i2c/m52790.c
@@ -27,7 +27,7 @@
#include <asm/uaccess.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
-#include <media/m52790.h>
+#include <media/i2c/m52790.h>
#include <media/v4l2-device.h>
MODULE_DESCRIPTION("i2c device driver for m52790 A/V switch");
diff --git a/drivers/media/i2c/m5mols/m5mols_capture.c b/drivers/media/i2c/m5mols/m5mols_capture.c
index 1a03d02bd4d1..a0cd6dc32eb0 100644
--- a/drivers/media/i2c/m5mols/m5mols_capture.c
+++ b/drivers/media/i2c/m5mols/m5mols_capture.c
@@ -25,8 +25,8 @@
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
-#include <media/m5mols.h>
-#include <media/exynos-fimc.h>
+#include <media/i2c/m5mols.h>
+#include <media/drv-intf/exynos-fimc.h>
#include "m5mols.h"
#include "m5mols_reg.h"
diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c
index 6404c0d93e7a..acb804bceccb 100644
--- a/drivers/media/i2c/m5mols/m5mols_core.c
+++ b/drivers/media/i2c/m5mols/m5mols_core.c
@@ -25,7 +25,7 @@
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
-#include <media/m5mols.h>
+#include <media/i2c/m5mols.h>
#include "m5mols.h"
#include "m5mols_reg.h"
@@ -975,10 +975,10 @@ static int m5mols_probe(struct i2c_client *client,
sd->internal_ops = &m5mols_subdev_internal_ops;
info->pad.flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&sd->entity, 1, &info->pad, 0);
+ ret = media_entity_pads_init(&sd->entity, 1, &info->pad);
if (ret < 0)
return ret;
- sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
init_waitqueue_head(&info->irq_waitq);
mutex_init(&info->lock);
diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c
index bdb94000ba5a..a84561d0d4a8 100644
--- a/drivers/media/i2c/msp3400-driver.c
+++ b/drivers/media/i2c/msp3400-driver.c
@@ -56,8 +56,8 @@
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
-#include <media/msp3400.h>
-#include <media/tvaudio.h>
+#include <media/drv-intf/msp3400.h>
+#include <media/i2c/tvaudio.h>
#include "msp3400-driver.h"
/* ---------------------------------------------------------------------- */
diff --git a/drivers/media/i2c/msp3400-driver.h b/drivers/media/i2c/msp3400-driver.h
index fbe5e0715f93..6cae21366ed5 100644
--- a/drivers/media/i2c/msp3400-driver.h
+++ b/drivers/media/i2c/msp3400-driver.h
@@ -4,7 +4,7 @@
#ifndef MSP3400_DRIVER_H
#define MSP3400_DRIVER_H
-#include <media/msp3400.h>
+#include <media/drv-intf/msp3400.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
diff --git a/drivers/media/i2c/msp3400-kthreads.c b/drivers/media/i2c/msp3400-kthreads.c
index f8b51714f2f9..17120804fab7 100644
--- a/drivers/media/i2c/msp3400-kthreads.c
+++ b/drivers/media/i2c/msp3400-kthreads.c
@@ -26,7 +26,7 @@
#include <linux/freezer.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
-#include <media/msp3400.h>
+#include <media/drv-intf/msp3400.h>
#include <linux/kthread.h>
#include <linux/suspend.h>
#include "msp3400-driver.h"
diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c
index c7747bd0cabb..da076796999e 100644
--- a/drivers/media/i2c/mt9m032.c
+++ b/drivers/media/i2c/mt9m032.c
@@ -31,7 +31,7 @@
#include <linux/v4l2-mediabus.h>
#include <media/media-entity.h>
-#include <media/mt9m032.h>
+#include <media/i2c/mt9m032.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
@@ -671,7 +671,7 @@ static int mt9m032_set_ctrl(struct v4l2_ctrl *ctrl)
return 0;
}
-static struct v4l2_ctrl_ops mt9m032_ctrl_ops = {
+static const struct v4l2_ctrl_ops mt9m032_ctrl_ops = {
.s_ctrl = mt9m032_set_ctrl,
.try_ctrl = mt9m032_try_ctrl,
};
@@ -799,7 +799,7 @@ static int mt9m032_probe(struct i2c_client *client,
sensor->subdev.ctrl_handler = &sensor->ctrls;
sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&sensor->subdev.entity, 1, &sensor->pad, 0);
+ ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad);
if (ret < 0)
goto error_ctrl;
diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
index 0db15f528ac1..237737fec09c 100644
--- a/drivers/media/i2c/mt9p031.c
+++ b/drivers/media/i2c/mt9p031.c
@@ -26,7 +26,7 @@
#include <linux/slab.h>
#include <linux/videodev2.h>
-#include <media/mt9p031.h>
+#include <media/i2c/mt9p031.h>
#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
@@ -817,7 +817,7 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
return 0;
}
-static struct v4l2_ctrl_ops mt9p031_ctrl_ops = {
+static const struct v4l2_ctrl_ops mt9p031_ctrl_ops = {
.s_ctrl = mt9p031_s_ctrl,
};
@@ -1112,7 +1112,7 @@ static int mt9p031_probe(struct i2c_client *client,
mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops;
mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&mt9p031->subdev.entity, 1, &mt9p031->pad, 0);
+ ret = media_entity_pads_init(&mt9p031->subdev.entity, 1, &mt9p031->pad);
if (ret < 0)
goto done;
diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c
index 8ae99f7f254c..702d562f8e39 100644
--- a/drivers/media/i2c/mt9t001.c
+++ b/drivers/media/i2c/mt9t001.c
@@ -21,7 +21,7 @@
#include <linux/videodev2.h>
#include <linux/v4l2-mediabus.h>
-#include <media/mt9t001.h>
+#include <media/i2c/mt9t001.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
@@ -626,7 +626,7 @@ static int mt9t001_s_ctrl(struct v4l2_ctrl *ctrl)
return 0;
}
-static struct v4l2_ctrl_ops mt9t001_ctrl_ops = {
+static const struct v4l2_ctrl_ops mt9t001_ctrl_ops = {
.s_ctrl = mt9t001_s_ctrl,
};
@@ -933,7 +933,7 @@ static int mt9t001_probe(struct i2c_client *client,
mt9t001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
mt9t001->pad.flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&mt9t001->subdev.entity, 1, &mt9t001->pad, 0);
+ ret = media_entity_pads_init(&mt9t001->subdev.entity, 1, &mt9t001->pad);
done:
if (ret < 0) {
diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c
index a4a5c39b599b..b9fea11d6b0b 100644
--- a/drivers/media/i2c/mt9v011.c
+++ b/drivers/media/i2c/mt9v011.c
@@ -13,7 +13,7 @@
#include <asm/div64.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
-#include <media/mt9v011.h>
+#include <media/i2c/mt9v011.h>
MODULE_DESCRIPTION("Micron mt9v011 sensor driver");
MODULE_AUTHOR("Mauro Carvalho Chehab");
@@ -454,7 +454,7 @@ static int mt9v011_s_ctrl(struct v4l2_ctrl *ctrl)
return 0;
}
-static struct v4l2_ctrl_ops mt9v011_ctrl_ops = {
+static const struct v4l2_ctrl_ops mt9v011_ctrl_ops = {
.s_ctrl = mt9v011_s_ctrl,
};
diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c
index a68ce94ee097..2e1d116a64e7 100644
--- a/drivers/media/i2c/mt9v032.c
+++ b/drivers/media/i2c/mt9v032.c
@@ -25,7 +25,7 @@
#include <linux/v4l2-mediabus.h>
#include <linux/module.h>
-#include <media/mt9v032.h>
+#include <media/i2c/mt9v032.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-of.h>
@@ -703,7 +703,7 @@ static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl)
return 0;
}
-static struct v4l2_ctrl_ops mt9v032_ctrl_ops = {
+static const struct v4l2_ctrl_ops mt9v032_ctrl_ops = {
.s_ctrl = mt9v032_s_ctrl,
};
@@ -1046,7 +1046,7 @@ static int mt9v032_probe(struct i2c_client *client,
mt9v032->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&mt9v032->subdev.entity, 1, &mt9v032->pad, 0);
+ ret = media_entity_pads_init(&mt9v032->subdev.entity, 1, &mt9v032->pad);
if (ret < 0)
goto err;
diff --git a/drivers/media/i2c/noon010pc30.c b/drivers/media/i2c/noon010pc30.c
index f197b6cbd407..30cb90b88d75 100644
--- a/drivers/media/i2c/noon010pc30.c
+++ b/drivers/media/i2c/noon010pc30.c
@@ -18,7 +18,7 @@
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
-#include <media/noon010pc30.h>
+#include <media/i2c/noon010pc30.h>
#include <linux/videodev2.h>
#include <linux/module.h>
#include <media/v4l2-ctrls.h>
@@ -779,8 +779,8 @@ static int noon010_probe(struct i2c_client *client,
goto np_err;
info->pad.flags = MEDIA_PAD_FL_SOURCE;
- sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
- ret = media_entity_init(&sd->entity, 1, &info->pad, 0);
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&sd->entity, 1, &info->pad);
if (ret < 0)
goto np_err;
diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c
index 49109f4f5bb4..02b9a3440557 100644
--- a/drivers/media/i2c/ov2659.c
+++ b/drivers/media/i2c/ov2659.c
@@ -37,7 +37,7 @@
#include <linux/videodev2.h>
#include <media/media-entity.h>
-#include <media/ov2659.h>
+#include <media/i2c/ov2659.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
@@ -1249,7 +1249,7 @@ static int ov2659_s_ctrl(struct v4l2_ctrl *ctrl)
return 0;
}
-static struct v4l2_ctrl_ops ov2659_ctrl_ops = {
+static const struct v4l2_ctrl_ops ov2659_ctrl_ops = {
.s_ctrl = ov2659_s_ctrl,
};
@@ -1445,8 +1445,8 @@ static int ov2659_probe(struct i2c_client *client,
#if defined(CONFIG_MEDIA_CONTROLLER)
ov2659->pad.flags = MEDIA_PAD_FL_SOURCE;
- sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
- ret = media_entity_init(&sd->entity, 1, &ov2659->pad, 0);
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&sd->entity, 1, &ov2659->pad);
if (ret < 0) {
v4l2_ctrl_handler_free(&ov2659->ctrls);
return ret;
diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c
index e1b5dc84c14e..56cfb5ca9c95 100644
--- a/drivers/media/i2c/ov7670.c
+++ b/drivers/media/i2c/ov7670.c
@@ -20,7 +20,7 @@
#include <media/v4l2-ctrls.h>
#include <media/v4l2-mediabus.h>
#include <media/v4l2-image-sizes.h>
-#include <media/ov7670.h>
+#include <media/i2c/ov7670.h>
MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors");
diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c
index 1ee6a5527c38..a0b3c9bde53d 100644
--- a/drivers/media/i2c/ov9650.c
+++ b/drivers/media/i2c/ov9650.c
@@ -29,7 +29,7 @@
#include <media/v4l2-image-sizes.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-mediabus.h>
-#include <media/ov9650.h>
+#include <media/i2c/ov9650.h>
static int debug;
module_param(debug, int, 0644);
@@ -1500,8 +1500,8 @@ static int ov965x_probe(struct i2c_client *client,
return ret;
ov965x->pad.flags = MEDIA_PAD_FL_SOURCE;
- sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
- ret = media_entity_init(&sd->entity, 1, &ov965x->pad, 0);
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&sd->entity, 1, &ov965x->pad);
if (ret < 0)
return ret;
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
index 51b26010403c..57b3d27993a4 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
@@ -34,7 +34,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-mediabus.h>
-#include <media/s5c73m3.h>
+#include <media/i2c/s5c73m3.h>
#include <media/v4l2-of.h>
#include "s5c73m3.h"
@@ -1482,11 +1482,11 @@ static int s5c73m3_oif_registered(struct v4l2_subdev *sd)
return ret;
}
- ret = media_entity_create_link(&state->sensor_sd.entity,
+ ret = media_create_pad_link(&state->sensor_sd.entity,
S5C73M3_ISP_PAD, &state->oif_sd.entity, OIF_ISP_PAD,
MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
- ret = media_entity_create_link(&state->sensor_sd.entity,
+ ret = media_create_pad_link(&state->sensor_sd.entity,
S5C73M3_JPEG_PAD, &state->oif_sd.entity, OIF_JPEG_PAD,
MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
@@ -1688,10 +1688,10 @@ static int s5c73m3_probe(struct i2c_client *client,
state->sensor_pads[S5C73M3_JPEG_PAD].flags = MEDIA_PAD_FL_SOURCE;
state->sensor_pads[S5C73M3_ISP_PAD].flags = MEDIA_PAD_FL_SOURCE;
- sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
- ret = media_entity_init(&sd->entity, S5C73M3_NUM_PADS,
- state->sensor_pads, 0);
+ ret = media_entity_pads_init(&sd->entity, S5C73M3_NUM_PADS,
+ state->sensor_pads);
if (ret < 0)
return ret;
@@ -1704,10 +1704,10 @@ static int s5c73m3_probe(struct i2c_client *client,
state->oif_pads[OIF_ISP_PAD].flags = MEDIA_PAD_FL_SINK;
state->oif_pads[OIF_JPEG_PAD].flags = MEDIA_PAD_FL_SINK;
state->oif_pads[OIF_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE;
- oif_sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ oif_sd->entity.function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
- ret = media_entity_init(&oif_sd->entity, OIF_NUM_PADS,
- state->oif_pads, 0);
+ ret = media_entity_pads_init(&oif_sd->entity, OIF_NUM_PADS,
+ state->oif_pads);
if (ret < 0)
return ret;
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c
index 8001cde1db1e..0a060339e516 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c
@@ -32,7 +32,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-mediabus.h>
-#include <media/s5c73m3.h>
+#include <media/i2c/s5c73m3.h>
#include "s5c73m3.h"
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
index 72ef9f936e6c..7d65b36434b1 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c
@@ -37,6 +37,7 @@ enum spi_direction {
SPI_DIR_RX,
SPI_DIR_TX
};
+MODULE_DEVICE_TABLE(of, s5c73m3_spi_ids);
static int spi_xmit(struct spi_device *spi_dev, void *addr, const int len,
enum spi_direction dir)
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3.h b/drivers/media/i2c/s5c73m3/s5c73m3.h
index 13aed59f0f5d..653f68e7ea07 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3.h
+++ b/drivers/media/i2c/s5c73m3/s5c73m3.h
@@ -23,7 +23,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
-#include <media/s5c73m3.h>
+#include <media/i2c/s5c73m3.h>
#define DRIVER_NAME "S5C73M3"
diff --git a/drivers/media/i2c/s5k4ecgx.c b/drivers/media/i2c/s5k4ecgx.c
index 97084237275d..8a0f22da590f 100644
--- a/drivers/media/i2c/s5k4ecgx.c
+++ b/drivers/media/i2c/s5k4ecgx.c
@@ -27,7 +27,7 @@
#include <asm/unaligned.h>
#include <media/media-entity.h>
-#include <media/s5k4ecgx.h>
+#include <media/i2c/s5k4ecgx.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mediabus.h>
@@ -961,8 +961,8 @@ static int s5k4ecgx_probe(struct i2c_client *client,
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
priv->pad.flags = MEDIA_PAD_FL_SOURCE;
- sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
- ret = media_entity_init(&sd->entity, 1, &priv->pad, 0);
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&sd->entity, 1, &priv->pad);
if (ret)
return ret;
diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c
index 774e0d0c94cb..fc3a5a8e6c9c 100644
--- a/drivers/media/i2c/s5k5baf.c
+++ b/drivers/media/i2c/s5k5baf.c
@@ -408,7 +408,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
static inline bool s5k5baf_is_cis_subdev(struct v4l2_subdev *sd)
{
- return sd->entity.type == MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+ return sd->entity.function == MEDIA_ENT_F_CAM_SENSOR;
}
static inline struct s5k5baf *to_s5k5baf(struct v4l2_subdev *sd)
@@ -1756,7 +1756,7 @@ static int s5k5baf_registered(struct v4l2_subdev *sd)
v4l2_err(sd, "failed to register subdev %s\n",
state->cis_sd.name);
else
- ret = media_entity_create_link(&state->cis_sd.entity, PAD_CIS,
+ ret = media_create_pad_link(&state->cis_sd.entity, PAD_CIS,
&state->sd.entity, PAD_CIS,
MEDIA_LNK_FL_IMMUTABLE |
MEDIA_LNK_FL_ENABLED);
@@ -1904,8 +1904,8 @@ static int s5k5baf_configure_subdevs(struct s5k5baf *state,
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
state->cis_pad.flags = MEDIA_PAD_FL_SOURCE;
- sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
- ret = media_entity_init(&sd->entity, NUM_CIS_PADS, &state->cis_pad, 0);
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&sd->entity, NUM_CIS_PADS, &state->cis_pad);
if (ret < 0)
goto err;
@@ -1919,8 +1919,8 @@ static int s5k5baf_configure_subdevs(struct s5k5baf *state,
state->pads[PAD_CIS].flags = MEDIA_PAD_FL_SINK;
state->pads[PAD_OUT].flags = MEDIA_PAD_FL_SOURCE;
- sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
- ret = media_entity_init(&sd->entity, NUM_ISP_PADS, state->pads, 0);
+ sd->entity.function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
+ ret = media_entity_pads_init(&sd->entity, NUM_ISP_PADS, state->pads);
if (!ret)
return 0;
diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c
index b1b1574dfb95..b9e43ffa5085 100644
--- a/drivers/media/i2c/s5k6a3.c
+++ b/drivers/media/i2c/s5k6a3.c
@@ -333,7 +333,7 @@ static int s5k6a3_probe(struct i2c_client *client,
sensor->format.height = S5K6A3_DEFAULT_HEIGHT;
sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0);
+ ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad);
if (ret < 0)
return ret;
diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c
index d0ad6a25bdab..faee11383cb7 100644
--- a/drivers/media/i2c/s5k6aa.c
+++ b/drivers/media/i2c/s5k6aa.c
@@ -28,7 +28,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-mediabus.h>
-#include <media/s5k6aa.h>
+#include <media/i2c/s5k6aa.h>
static int debug;
module_param(debug, int, 0644);
@@ -1577,8 +1577,8 @@ static int s5k6aa_probe(struct i2c_client *client,
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
s5k6aa->pad.flags = MEDIA_PAD_FL_SOURCE;
- sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
- ret = media_entity_init(&sd->entity, 1, &s5k6aa->pad, 0);
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&sd->entity, 1, &s5k6aa->pad);
if (ret)
return ret;
diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c
index 37e65f661d7a..89e458c23983 100644
--- a/drivers/media/i2c/saa6588.c
+++ b/drivers/media/i2c/saa6588.c
@@ -31,7 +31,7 @@
#include <linux/wait.h>
#include <asm/uaccess.h>
-#include <media/saa6588.h>
+#include <media/i2c/saa6588.h>
#include <media/v4l2-device.h>
diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c
index 91e75222c537..24d2b76dbe97 100644
--- a/drivers/media/i2c/saa7115.c
+++ b/drivers/media/i2c/saa7115.c
@@ -46,7 +46,7 @@
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
-#include <media/saa7115.h>
+#include <media/i2c/saa7115.h>
#include <asm/div64.h>
#define VRES_60HZ (480+16)
diff --git a/drivers/media/i2c/saa7127.c b/drivers/media/i2c/saa7127.c
index a43d96da1017..8d94dcbf4366 100644
--- a/drivers/media/i2c/saa7127.c
+++ b/drivers/media/i2c/saa7127.c
@@ -54,7 +54,7 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/saa7127.h>
+#include <media/i2c/saa7127.h>
static int debug;
static int test_image;
diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c
index fb39dfd55e75..a215efe7a8ba 100644
--- a/drivers/media/i2c/smiapp/smiapp-core.c
+++ b/drivers/media/i2c/smiapp/smiapp-core.c
@@ -2487,31 +2487,31 @@ static int smiapp_register_subdevs(struct smiapp_sensor *sensor)
if (!last)
continue;
- rval = media_entity_init(&this->sd.entity,
- this->npads, this->pads, 0);
+ rval = media_entity_pads_init(&this->sd.entity,
+ this->npads, this->pads);
if (rval) {
dev_err(&client->dev,
- "media_entity_init failed\n");
+ "media_entity_pads_init failed\n");
return rval;
}
- rval = media_entity_create_link(&this->sd.entity,
- this->source_pad,
- &last->sd.entity,
- last->sink_pad,
- MEDIA_LNK_FL_ENABLED |
- MEDIA_LNK_FL_IMMUTABLE);
+ rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev,
+ &this->sd);
if (rval) {
dev_err(&client->dev,
- "media_entity_create_link failed\n");
+ "v4l2_device_register_subdev failed\n");
return rval;
}
- rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev,
- &this->sd);
+ rval = media_create_pad_link(&this->sd.entity,
+ this->source_pad,
+ &last->sd.entity,
+ last->sink_pad,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
if (rval) {
dev_err(&client->dev,
- "v4l2_device_register_subdev failed\n");
+ "media_create_pad_link failed\n");
return rval;
}
}
@@ -2763,7 +2763,7 @@ static int smiapp_init(struct smiapp_sensor *sensor)
dev_dbg(&client->dev, "profile %d\n", sensor->minfo.smiapp_profile);
- sensor->pixel_array->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+ sensor->pixel_array->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
/* final steps */
smiapp_read_frame_fmt(sensor);
@@ -3077,8 +3077,8 @@ static int smiapp_probe(struct i2c_client *client,
sensor->src->sensor = sensor;
sensor->src->pads[0].flags = MEDIA_PAD_FL_SOURCE;
- rval = media_entity_init(&sensor->src->sd.entity, 2,
- sensor->src->pads, 0);
+ rval = media_entity_pads_init(&sensor->src->sd.entity, 2,
+ sensor->src->pads);
if (rval < 0)
return rval;
diff --git a/drivers/media/i2c/smiapp/smiapp.h b/drivers/media/i2c/smiapp/smiapp.h
index ed010a8a49d7..f6af0cc4a256 100644
--- a/drivers/media/i2c/smiapp/smiapp.h
+++ b/drivers/media/i2c/smiapp/smiapp.h
@@ -22,7 +22,7 @@
#include <linux/mutex.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
-#include <media/smiapp.h>
+#include <media/i2c/smiapp.h>
#include "smiapp-pll.h"
#include "smiapp-reg.h"
diff --git a/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/mt9m001.c
index 4fbdd1e9f7ee..2e14e52ba2e0 100644
--- a/drivers/media/i2c/soc_camera/mt9m001.c
+++ b/drivers/media/i2c/soc_camera/mt9m001.c
@@ -15,7 +15,7 @@
#include <linux/module.h>
#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
+#include <media/drv-intf/soc_mediabus.h>
#include <media/v4l2-clk.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-ctrls.h>
diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c
index 2f35d31ca58e..6a1b2a9f9a09 100644
--- a/drivers/media/i2c/soc_camera/mt9t112.c
+++ b/drivers/media/i2c/soc_camera/mt9t112.c
@@ -25,7 +25,7 @@
#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
-#include <media/mt9t112.h>
+#include <media/i2c/mt9t112.h>
#include <media/soc_camera.h>
#include <media/v4l2-clk.h>
#include <media/v4l2-common.h>
diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c
index f31377408550..c2ba1fb3694d 100644
--- a/drivers/media/i2c/soc_camera/mt9v022.c
+++ b/drivers/media/i2c/soc_camera/mt9v022.c
@@ -15,9 +15,9 @@
#include <linux/log2.h>
#include <linux/module.h>
-#include <media/mt9v022.h>
+#include <media/i2c/mt9v022.h>
#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
+#include <media/drv-intf/soc_mediabus.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-clk.h>
#include <media/v4l2-ctrls.h>
diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c
index f150a8bd94dc..a43410c1e254 100644
--- a/drivers/media/i2c/soc_camera/ov772x.c
+++ b/drivers/media/i2c/soc_camera/ov772x.c
@@ -24,7 +24,7 @@
#include <linux/v4l2-mediabus.h>
#include <linux/videodev2.h>
-#include <media/ov772x.h>
+#include <media/i2c/ov772x.h>
#include <media/soc_camera.h>
#include <media/v4l2-clk.h>
#include <media/v4l2-ctrls.h>
diff --git a/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/drivers/media/i2c/soc_camera/rj54n1cb0c.c
index c769cf663f84..aa7bfbb4ad71 100644
--- a/drivers/media/i2c/soc_camera/rj54n1cb0c.c
+++ b/drivers/media/i2c/soc_camera/rj54n1cb0c.c
@@ -15,7 +15,7 @@
#include <linux/videodev2.h>
#include <linux/module.h>
-#include <media/rj54n1cb0c.h>
+#include <media/i2c/rj54n1cb0c.h>
#include <media/soc_camera.h>
#include <media/v4l2-clk.h>
#include <media/v4l2-subdev.h>
diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c
index e939c24bfd3c..06aff81787a7 100644
--- a/drivers/media/i2c/soc_camera/tw9910.c
+++ b/drivers/media/i2c/soc_camera/tw9910.c
@@ -26,7 +26,7 @@
#include <linux/videodev2.h>
#include <media/soc_camera.h>
-#include <media/tw9910.h>
+#include <media/i2c/tw9910.h>
#include <media/v4l2-clk.h>
#include <media/v4l2-subdev.h>
diff --git a/drivers/media/i2c/sr030pc30.c b/drivers/media/i2c/sr030pc30.c
index b04c09dd4bfb..0bf031b7e4fa 100644
--- a/drivers/media/i2c/sr030pc30.c
+++ b/drivers/media/i2c/sr030pc30.c
@@ -24,7 +24,7 @@
#include <media/v4l2-subdev.h>
#include <media/v4l2-mediabus.h>
#include <media/v4l2-ctrls.h>
-#include <media/sr030pc30.h>
+#include <media/i2c/sr030pc30.h>
static int debug;
module_param(debug, int, 0644);
diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c
index 9ef5baaf8646..3397eb99c67b 100644
--- a/drivers/media/i2c/tc358743.c
+++ b/drivers/media/i2c/tc358743.c
@@ -42,7 +42,7 @@
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
#include <media/v4l2-of.h>
-#include <media/tc358743.h>
+#include <media/i2c/tc358743.h>
#include "tc358743_regs.h"
@@ -862,7 +862,7 @@ static void tc358743_format_change(struct v4l2_subdev *sd)
v4l2_dbg(1, debug, sd, "%s: Format changed. No signal\n",
__func__);
} else {
- if (!v4l2_match_dv_timings(&state->timings, &timings, 0))
+ if (!v4l2_match_dv_timings(&state->timings, &timings, 0, false))
enable_stream(sd, false);
v4l2_print_dv_timings(sd->name,
@@ -1366,7 +1366,7 @@ static int tc358743_s_dv_timings(struct v4l2_subdev *sd,
v4l2_print_dv_timings(sd->name, "tc358743_s_dv_timings: ",
timings, false);
- if (v4l2_match_dv_timings(&state->timings, timings, 0)) {
+ if (v4l2_match_dv_timings(&state->timings, timings, 0, false)) {
v4l2_dbg(1, debug, sd, "%s: no change\n", __func__);
return 0;
}
@@ -1889,7 +1889,7 @@ static int tc358743_probe(struct i2c_client *client,
}
state->pad.flags = MEDIA_PAD_FL_SOURCE;
- err = media_entity_init(&sd->entity, 1, &state->pad, 0);
+ err = media_entity_pads_init(&sd->entity, 1, &state->pad);
if (err < 0)
goto err_hdl;
diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c
index bda3a6540a60..5bbfcab01c75 100644
--- a/drivers/media/i2c/ths7303.c
+++ b/drivers/media/i2c/ths7303.c
@@ -25,7 +25,7 @@
#include <linux/module.h>
#include <linux/slab.h>
-#include <media/ths7303.h>
+#include <media/i2c/ths7303.h>
#include <media/v4l2-device.h>
#define THS7303_CHANNEL_1 1
diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c
index 2a8114a676fd..fece2a4339a1 100644
--- a/drivers/media/i2c/tvaudio.c
+++ b/drivers/media/i2c/tvaudio.c
@@ -36,7 +36,7 @@
#include <linux/kthread.h>
#include <linux/freezer.h>
-#include <media/tvaudio.h>
+#include <media/i2c/tvaudio.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c
index a93985a9b070..7fa5f1e4fe37 100644
--- a/drivers/media/i2c/tvp514x.c
+++ b/drivers/media/i2c/tvp514x.c
@@ -44,7 +44,7 @@
#include <media/v4l2-mediabus.h>
#include <media/v4l2-of.h>
#include <media/v4l2-ctrls.h>
-#include <media/tvp514x.h>
+#include <media/i2c/tvp514x.h>
#include <media/media-entity.h>
#include "tvp514x_regs.h"
@@ -1095,9 +1095,9 @@ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id)
#if defined(CONFIG_MEDIA_CONTROLLER)
decoder->pad.flags = MEDIA_PAD_FL_SOURCE;
decoder->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- decoder->sd.entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER;
+ decoder->sd.entity.flags |= MEDIA_ENT_F_ATV_DECODER;
- ret = media_entity_init(&decoder->sd.entity, 1, &decoder->pad, 0);
+ ret = media_entity_pads_init(&decoder->sd.entity, 1, &decoder->pad);
if (ret < 0) {
v4l2_err(sd, "%s decoder driver failed to register !!\n",
sd->name);
diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index 3c5fb2509c47..6c3769d44b75 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -12,7 +12,7 @@
#include <linux/module.h>
#include <media/v4l2-async.h>
#include <media/v4l2-device.h>
-#include <media/tvp5150.h>
+#include <media/i2c/tvp5150.h>
#include <media/v4l2-ctrls.h>
#include "tvp5150_reg.h"
diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c
index f617d8b745ee..83c79fa5f61d 100644
--- a/drivers/media/i2c/tvp7002.c
+++ b/drivers/media/i2c/tvp7002.c
@@ -32,7 +32,7 @@
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/v4l2-dv-timings.h>
-#include <media/tvp7002.h>
+#include <media/i2c/tvp7002.h>
#include <media/v4l2-async.h>
#include <media/v4l2-device.h>
#include <media/v4l2-common.h>
@@ -1012,9 +1012,9 @@ static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id)
#if defined(CONFIG_MEDIA_CONTROLLER)
device->pad.flags = MEDIA_PAD_FL_SOURCE;
device->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- device->sd.entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER;
+ device->sd.entity.flags |= MEDIA_ENT_F_ATV_DECODER;
- error = media_entity_init(&device->sd.entity, 1, &device->pad, 0);
+ error = media_entity_pads_init(&device->sd.entity, 1, &device->pad);
if (error < 0)
return error;
#endif
diff --git a/drivers/media/i2c/uda1342.c b/drivers/media/i2c/uda1342.c
index 081786d176d0..8e17a83920d4 100644
--- a/drivers/media/i2c/uda1342.c
+++ b/drivers/media/i2c/uda1342.c
@@ -20,7 +20,7 @@
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/uda1342.h>
+#include <media/i2c/uda1342.h>
#include <linux/slab.h>
static int write_reg(struct i2c_client *client, int reg, int value)
diff --git a/drivers/media/i2c/upd64031a.c b/drivers/media/i2c/upd64031a.c
index 2c0f955abc72..c03567e993cd 100644
--- a/drivers/media/i2c/upd64031a.c
+++ b/drivers/media/i2c/upd64031a.c
@@ -27,7 +27,7 @@
#include <linux/videodev2.h>
#include <linux/slab.h>
#include <media/v4l2-device.h>
-#include <media/upd64031a.h>
+#include <media/i2c/upd64031a.h>
/* --------------------- read registers functions define -------------------- */
diff --git a/drivers/media/i2c/upd64083.c b/drivers/media/i2c/upd64083.c
index f2057a434060..77f122f2e3c9 100644
--- a/drivers/media/i2c/upd64083.c
+++ b/drivers/media/i2c/upd64083.c
@@ -27,7 +27,7 @@
#include <linux/videodev2.h>
#include <linux/slab.h>
#include <media/v4l2-device.h>
-#include <media/upd64083.h>
+#include <media/i2c/upd64083.h>
MODULE_DESCRIPTION("uPD64083 driver");
MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil");
diff --git a/drivers/media/i2c/wm8775.c b/drivers/media/i2c/wm8775.c
index d33d2cd6d034..6e00f145b948 100644
--- a/drivers/media/i2c/wm8775.c
+++ b/drivers/media/i2c/wm8775.c
@@ -34,7 +34,7 @@
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
-#include <media/wm8775.h>
+#include <media/i2c/wm8775.h>
MODULE_DESCRIPTION("wm8775 driver");
MODULE_AUTHOR("Ulf Eklund, Hans Verkuil");
diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
index 7b39440192d6..7dae0ac0f3ae 100644
--- a/drivers/media/media-device.c
+++ b/drivers/media/media-device.c
@@ -22,14 +22,18 @@
#include <linux/compat.h>
#include <linux/export.h>
+#include <linux/idr.h>
#include <linux/ioctl.h>
#include <linux/media.h>
+#include <linux/slab.h>
#include <linux/types.h>
#include <media/media-device.h>
#include <media/media-devnode.h>
#include <media/media-entity.h>
+#ifdef CONFIG_MEDIA_CONTROLLER
+
/* -----------------------------------------------------------------------------
* Userspace API
*/
@@ -75,8 +79,8 @@ static struct media_entity *find_entity(struct media_device *mdev, u32 id)
spin_lock(&mdev->lock);
media_device_for_each_entity(entity, mdev) {
- if ((entity->id == id && !next) ||
- (entity->id > id && next)) {
+ if (((media_entity_id(entity) == id) && !next) ||
+ ((media_entity_id(entity) > id) && next)) {
spin_unlock(&mdev->lock);
return entity;
}
@@ -102,13 +106,13 @@ static long media_device_enum_entities(struct media_device *mdev,
if (ent == NULL)
return -EINVAL;
- u_ent.id = ent->id;
+ u_ent.id = media_entity_id(ent);
if (ent->name)
strlcpy(u_ent.name, ent->name, sizeof(u_ent.name));
- u_ent.type = ent->type;
- u_ent.revision = ent->revision;
+ u_ent.type = ent->function;
+ u_ent.revision = 0; /* Unused */
u_ent.flags = ent->flags;
- u_ent.group_id = ent->group_id;
+ u_ent.group_id = 0; /* Unused */
u_ent.pads = ent->num_pads;
u_ent.links = ent->num_links - ent->num_backlinks;
memcpy(&u_ent.raw, &ent->info, sizeof(ent->info));
@@ -120,7 +124,7 @@ static long media_device_enum_entities(struct media_device *mdev,
static void media_device_kpad_to_upad(const struct media_pad *kpad,
struct media_pad_desc *upad)
{
- upad->entity = kpad->entity->id;
+ upad->entity = media_entity_id(kpad->entity);
upad->index = kpad->index;
upad->flags = kpad->flags;
}
@@ -148,25 +152,25 @@ static long __media_device_enum_links(struct media_device *mdev,
}
if (links->links) {
- struct media_link_desc __user *ulink;
- unsigned int l;
+ struct media_link *link;
+ struct media_link_desc __user *ulink_desc = links->links;
- for (l = 0, ulink = links->links; l < entity->num_links; l++) {
- struct media_link_desc link;
+ list_for_each_entry(link, &entity->links, list) {
+ struct media_link_desc klink_desc;
/* Ignore backlinks. */
- if (entity->links[l].source->entity != entity)
+ if (link->source->entity != entity)
continue;
-
- memset(&link, 0, sizeof(link));
- media_device_kpad_to_upad(entity->links[l].source,
- &link.source);
- media_device_kpad_to_upad(entity->links[l].sink,
- &link.sink);
- link.flags = entity->links[l].flags;
- if (copy_to_user(ulink, &link, sizeof(*ulink)))
+ memset(&klink_desc, 0, sizeof(klink_desc));
+ media_device_kpad_to_upad(link->source,
+ &klink_desc.source);
+ media_device_kpad_to_upad(link->sink,
+ &klink_desc.sink);
+ klink_desc.flags = link->flags;
+ if (copy_to_user(ulink_desc, &klink_desc,
+ sizeof(*ulink_desc)))
return -EFAULT;
- ulink++;
+ ulink_desc++;
}
}
@@ -230,6 +234,164 @@ static long media_device_setup_link(struct media_device *mdev,
return ret;
}
+#if 0 /* Let's postpone it to Kernel 4.6 */
+static long __media_device_get_topology(struct media_device *mdev,
+ struct media_v2_topology *topo)
+{
+ struct media_entity *entity;
+ struct media_interface *intf;
+ struct media_pad *pad;
+ struct media_link *link;
+ struct media_v2_entity kentity, *uentity;
+ struct media_v2_interface kintf, *uintf;
+ struct media_v2_pad kpad, *upad;
+ struct media_v2_link klink, *ulink;
+ unsigned int i;
+ int ret = 0;
+
+ topo->topology_version = mdev->topology_version;
+
+ /* Get entities and number of entities */
+ i = 0;
+ uentity = media_get_uptr(topo->ptr_entities);
+ media_device_for_each_entity(entity, mdev) {
+ i++;
+ if (ret || !uentity)
+ continue;
+
+ if (i > topo->num_entities) {
+ ret = -ENOSPC;
+ continue;
+ }
+
+ /* Copy fields to userspace struct if not error */
+ memset(&kentity, 0, sizeof(kentity));
+ kentity.id = entity->graph_obj.id;
+ kentity.function = entity->function;
+ strncpy(kentity.name, entity->name,
+ sizeof(kentity.name));
+
+ if (copy_to_user(uentity, &kentity, sizeof(kentity)))
+ ret = -EFAULT;
+ uentity++;
+ }
+ topo->num_entities = i;
+
+ /* Get interfaces and number of interfaces */
+ i = 0;
+ uintf = media_get_uptr(topo->ptr_interfaces);
+ media_device_for_each_intf(intf, mdev) {
+ i++;
+ if (ret || !uintf)
+ continue;
+
+ if (i > topo->num_interfaces) {
+ ret = -ENOSPC;
+ continue;
+ }
+
+ memset(&kintf, 0, sizeof(kintf));
+
+ /* Copy intf fields to userspace struct */
+ kintf.id = intf->graph_obj.id;
+ kintf.intf_type = intf->type;
+ kintf.flags = intf->flags;
+
+ if (media_type(&intf->graph_obj) == MEDIA_GRAPH_INTF_DEVNODE) {
+ struct media_intf_devnode *devnode;
+
+ devnode = intf_to_devnode(intf);
+
+ kintf.devnode.major = devnode->major;
+ kintf.devnode.minor = devnode->minor;
+ }
+
+ if (copy_to_user(uintf, &kintf, sizeof(kintf)))
+ ret = -EFAULT;
+ uintf++;
+ }
+ topo->num_interfaces = i;
+
+ /* Get pads and number of pads */
+ i = 0;
+ upad = media_get_uptr(topo->ptr_pads);
+ media_device_for_each_pad(pad, mdev) {
+ i++;
+ if (ret || !upad)
+ continue;
+
+ if (i > topo->num_pads) {
+ ret = -ENOSPC;
+ continue;
+ }
+
+ memset(&kpad, 0, sizeof(kpad));
+
+ /* Copy pad fields to userspace struct */
+ kpad.id = pad->graph_obj.id;
+ kpad.entity_id = pad->entity->graph_obj.id;
+ kpad.flags = pad->flags;
+
+ if (copy_to_user(upad, &kpad, sizeof(kpad)))
+ ret = -EFAULT;
+ upad++;
+ }
+ topo->num_pads = i;
+
+ /* Get links and number of links */
+ i = 0;
+ ulink = media_get_uptr(topo->ptr_links);
+ media_device_for_each_link(link, mdev) {
+ if (link->is_backlink)
+ continue;
+
+ i++;
+
+ if (ret || !ulink)
+ continue;
+
+ if (i > topo->num_links) {
+ ret = -ENOSPC;
+ continue;
+ }
+
+ memset(&klink, 0, sizeof(klink));
+
+ /* Copy link fields to userspace struct */
+ klink.id = link->graph_obj.id;
+ klink.source_id = link->gobj0->id;
+ klink.sink_id = link->gobj1->id;
+ klink.flags = link->flags;
+
+ if (copy_to_user(ulink, &klink, sizeof(klink)))
+ ret = -EFAULT;
+ ulink++;
+ }
+ topo->num_links = i;
+
+ return ret;
+}
+
+static long media_device_get_topology(struct media_device *mdev,
+ struct media_v2_topology __user *utopo)
+{
+ struct media_v2_topology ktopo;
+ int ret;
+
+ if (copy_from_user(&ktopo, utopo, sizeof(ktopo)))
+ return -EFAULT;
+
+ ret = __media_device_get_topology(mdev, &ktopo);
+ if (ret < 0)
+ return ret;
+
+ if (copy_to_user(utopo, &ktopo, sizeof(*utopo)))
+ return -EFAULT;
+
+ return 0;
+}
+#endif
+
static long media_device_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
@@ -262,6 +424,14 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd,
mutex_unlock(&dev->graph_mutex);
break;
+#if 0 /* Let's postpone it to Kernel 4.6 */
+ case MEDIA_IOC_G_TOPOLOGY:
+ mutex_lock(&dev->graph_mutex);
+ ret = media_device_get_topology(dev,
+ (struct media_v2_topology __user *)arg);
+ mutex_unlock(&dev->graph_mutex);
+ break;
+#endif
default:
ret = -ENOIOCTLCMD;
}
@@ -310,6 +480,9 @@ static long media_device_compat_ioctl(struct file *filp, unsigned int cmd,
case MEDIA_IOC_DEVICE_INFO:
case MEDIA_IOC_ENUM_ENTITIES:
case MEDIA_IOC_SETUP_LINK:
+#if 0 /* Let's postpone it to Kernel 4.6 */
+ case MEDIA_IOC_G_TOPOLOGY:
+#endif
return media_device_ioctl(filp, cmd, arg);
case MEDIA_IOC_ENUM_LINKS32:
@@ -357,10 +530,107 @@ static DEVICE_ATTR(model, S_IRUGO, show_model, NULL);
static void media_device_release(struct media_devnode *mdev)
{
+ dev_dbg(mdev->parent, "Media device released\n");
+}
+
+/**
+ * media_device_register_entity - Register an entity with a media device
+ * @mdev: The media device
+ * @entity: The entity
+ */
+int __must_check media_device_register_entity(struct media_device *mdev,
+ struct media_entity *entity)
+{
+ unsigned int i;
+ int ret;
+
+ if (entity->function == MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN ||
+ entity->function == MEDIA_ENT_F_UNKNOWN)
+ dev_warn(mdev->dev,
+ "Entity type for entity %s was not initialized!\n",
+ entity->name);
+
+ /* Warn if we apparently re-register an entity */
+ WARN_ON(entity->graph_obj.mdev != NULL);
+ entity->graph_obj.mdev = mdev;
+ INIT_LIST_HEAD(&entity->links);
+ entity->num_links = 0;
+ entity->num_backlinks = 0;
+
+ if (!ida_pre_get(&mdev->entity_internal_idx, GFP_KERNEL))
+ return -ENOMEM;
+
+ spin_lock(&mdev->lock);
+
+ ret = ida_get_new_above(&mdev->entity_internal_idx, 1,
+ &entity->internal_idx);
+ if (ret < 0) {
+ spin_unlock(&mdev->lock);
+ return ret;
+ }
+
+ mdev->entity_internal_idx_max =
+ max(mdev->entity_internal_idx_max, entity->internal_idx);
+
+ /* Initialize media_gobj embedded at the entity */
+ media_gobj_create(mdev, MEDIA_GRAPH_ENTITY, &entity->graph_obj);
+
+ /* Initialize objects at the pads */
+ for (i = 0; i < entity->num_pads; i++)
+ media_gobj_create(mdev, MEDIA_GRAPH_PAD,
+ &entity->pads[i].graph_obj);
+
+ spin_unlock(&mdev->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(media_device_register_entity);
+
+static void __media_device_unregister_entity(struct media_entity *entity)
+{
+ struct media_device *mdev = entity->graph_obj.mdev;
+ struct media_link *link, *tmp;
+ struct media_interface *intf;
+ unsigned int i;
+
+ ida_simple_remove(&mdev->entity_internal_idx, entity->internal_idx);
+
+ /* Remove all interface links pointing to this entity */
+ list_for_each_entry(intf, &mdev->interfaces, graph_obj.list) {
+ list_for_each_entry_safe(link, tmp, &intf->links, list) {
+ if (link->entity == entity)
+ __media_remove_intf_link(link);
+ }
+ }
+
+ /* Remove all data links that belong to this entity */
+ __media_entity_remove_links(entity);
+
+ /* Remove all pads that belong to this entity */
+ for (i = 0; i < entity->num_pads; i++)
+ media_gobj_destroy(&entity->pads[i].graph_obj);
+
+ /* Remove the entity */
+ media_gobj_destroy(&entity->graph_obj);
+
+ entity->graph_obj.mdev = NULL;
}
+void media_device_unregister_entity(struct media_entity *entity)
+{
+ struct media_device *mdev = entity->graph_obj.mdev;
+
+ if (mdev == NULL)
+ return;
+
+ spin_lock(&mdev->lock);
+ __media_device_unregister_entity(entity);
+ spin_unlock(&mdev->lock);
+}
+EXPORT_SYMBOL_GPL(media_device_unregister_entity);
+
/**
- * media_device_register - register a media device
+ * media_device_init() - initialize a media device
* @mdev: The media device
*
* The caller is responsible for initializing the media device before
@@ -369,23 +639,41 @@ static void media_device_release(struct media_devnode *mdev)
* - dev must point to the parent device
* - model must be filled with the device model name
*/
-int __must_check __media_device_register(struct media_device *mdev,
- struct module *owner)
+void media_device_init(struct media_device *mdev)
{
- int ret;
-
- if (WARN_ON(mdev->dev == NULL || mdev->model[0] == 0))
- return -EINVAL;
-
- mdev->entity_id = 1;
INIT_LIST_HEAD(&mdev->entities);
+ INIT_LIST_HEAD(&mdev->interfaces);
+ INIT_LIST_HEAD(&mdev->pads);
+ INIT_LIST_HEAD(&mdev->links);
spin_lock_init(&mdev->lock);
mutex_init(&mdev->graph_mutex);
+ ida_init(&mdev->entity_internal_idx);
+
+ dev_dbg(mdev->dev, "Media device initialized\n");
+}
+EXPORT_SYMBOL_GPL(media_device_init);
+
+void media_device_cleanup(struct media_device *mdev)
+{
+ ida_destroy(&mdev->entity_internal_idx);
+ mdev->entity_internal_idx_max = 0;
+ mutex_destroy(&mdev->graph_mutex);
+}
+EXPORT_SYMBOL_GPL(media_device_cleanup);
+
+int __must_check __media_device_register(struct media_device *mdev,
+ struct module *owner)
+{
+ int ret;
/* Register the device node. */
mdev->devnode.fops = &media_device_fops;
mdev->devnode.parent = mdev->dev;
mdev->devnode.release = media_device_release;
+
+ /* Set version 0 to indicate user-space that the graph is static */
+ mdev->topology_version = 0;
+
ret = media_devnode_register(&mdev->devnode, owner);
if (ret < 0)
return ret;
@@ -396,69 +684,74 @@ int __must_check __media_device_register(struct media_device *mdev,
return ret;
}
+ dev_dbg(mdev->dev, "Media device registered\n");
+
return 0;
}
EXPORT_SYMBOL_GPL(__media_device_register);
-/**
- * media_device_unregister - unregister a media device
- * @mdev: The media device
- *
- */
void media_device_unregister(struct media_device *mdev)
{
struct media_entity *entity;
struct media_entity *next;
+ struct media_interface *intf, *tmp_intf;
- list_for_each_entry_safe(entity, next, &mdev->entities, list)
- media_device_unregister_entity(entity);
+ if (mdev == NULL)
+ return;
+
+ spin_lock(&mdev->lock);
+
+ /* Check if mdev was ever registered at all */
+ if (!media_devnode_is_registered(&mdev->devnode)) {
+ spin_unlock(&mdev->lock);
+ return;
+ }
+
+ /* Remove all entities from the media device */
+ list_for_each_entry_safe(entity, next, &mdev->entities, graph_obj.list)
+ __media_device_unregister_entity(entity);
+
+ /* Remove all interfaces from the media device */
+ list_for_each_entry_safe(intf, tmp_intf, &mdev->interfaces,
+ graph_obj.list) {
+ __media_remove_intf_links(intf);
+ media_gobj_destroy(&intf->graph_obj);
+ kfree(intf);
+ }
+
+ spin_unlock(&mdev->lock);
device_remove_file(&mdev->devnode.dev, &dev_attr_model);
media_devnode_unregister(&mdev->devnode);
+
+ dev_dbg(mdev->dev, "Media device unregistered\n");
}
EXPORT_SYMBOL_GPL(media_device_unregister);
-/**
- * media_device_register_entity - Register an entity with a media device
- * @mdev: The media device
- * @entity: The entity
- */
-int __must_check media_device_register_entity(struct media_device *mdev,
- struct media_entity *entity)
+static void media_device_release_devres(struct device *dev, void *res)
{
- /* Warn if we apparently re-register an entity */
- WARN_ON(entity->parent != NULL);
- entity->parent = mdev;
-
- spin_lock(&mdev->lock);
- if (entity->id == 0)
- entity->id = mdev->entity_id++;
- else
- mdev->entity_id = max(entity->id + 1, mdev->entity_id);
- list_add_tail(&entity->list, &mdev->entities);
- spin_unlock(&mdev->lock);
-
- return 0;
}
-EXPORT_SYMBOL_GPL(media_device_register_entity);
-/**
- * media_device_unregister_entity - Unregister an entity
- * @entity: The entity
- *
- * If the entity has never been registered this function will return
- * immediately.
- */
-void media_device_unregister_entity(struct media_entity *entity)
+struct media_device *media_device_get_devres(struct device *dev)
{
- struct media_device *mdev = entity->parent;
+ struct media_device *mdev;
- if (mdev == NULL)
- return;
+ mdev = devres_find(dev, media_device_release_devres, NULL, NULL);
+ if (mdev)
+ return mdev;
- spin_lock(&mdev->lock);
- list_del(&entity->list);
- spin_unlock(&mdev->lock);
- entity->parent = NULL;
+ mdev = devres_alloc(media_device_release_devres,
+ sizeof(struct media_device), GFP_KERNEL);
+ if (!mdev)
+ return NULL;
+ return devres_get(dev, mdev, NULL, NULL);
}
-EXPORT_SYMBOL_GPL(media_device_unregister_entity);
+EXPORT_SYMBOL_GPL(media_device_get_devres);
+
+struct media_device *media_device_find_devres(struct device *dev)
+{
+ return devres_find(dev, media_device_release_devres, NULL, NULL);
+}
+EXPORT_SYMBOL_GPL(media_device_find_devres);
+
+#endif /* CONFIG_MEDIA_CONTROLLER */
diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c
index ebf9626e5ae5..cea35bf20011 100644
--- a/drivers/media/media-devnode.c
+++ b/drivers/media/media-devnode.c
@@ -217,20 +217,6 @@ static const struct file_operations media_devnode_fops = {
.llseek = no_llseek,
};
-/**
- * media_devnode_register - register a media device node
- * @mdev: media device node structure we want to register
- *
- * The registration code assigns minor numbers and registers the new device node
- * with the kernel. An error is returned if no free minor number can be found,
- * or if the registration of the device node fails.
- *
- * Zero is returned on success.
- *
- * Note that if the media_devnode_register call fails, the release() callback of
- * the media_devnode structure is *not* called, so the caller is responsible for
- * freeing any data.
- */
int __must_check media_devnode_register(struct media_devnode *mdev,
struct module *owner)
{
@@ -285,16 +271,6 @@ error:
return ret;
}
-/**
- * media_devnode_unregister - unregister a media device node
- * @mdev: the device node to unregister
- *
- * This unregisters the passed device. Future open calls will be met with
- * errors.
- *
- * This function can safely be called if the device node has never been
- * registered or has already been unregistered.
- */
void media_devnode_unregister(struct media_devnode *mdev)
{
/* Check if mdev was ever registered at all */
diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c
index 767fe55ba08e..e89d85a7d31b 100644
--- a/drivers/media/media-entity.c
+++ b/drivers/media/media-entity.c
@@ -26,65 +26,198 @@
#include <media/media-entity.h>
#include <media/media-device.h>
+static inline const char *gobj_type(enum media_gobj_type type)
+{
+ switch (type) {
+ case MEDIA_GRAPH_ENTITY:
+ return "entity";
+ case MEDIA_GRAPH_PAD:
+ return "pad";
+ case MEDIA_GRAPH_LINK:
+ return "link";
+ case MEDIA_GRAPH_INTF_DEVNODE:
+ return "intf-devnode";
+ default:
+ return "unknown";
+ }
+}
+
+static inline const char *intf_type(struct media_interface *intf)
+{
+ switch (intf->type) {
+ case MEDIA_INTF_T_DVB_FE:
+ return "frontend";
+ case MEDIA_INTF_T_DVB_DEMUX:
+ return "demux";
+ case MEDIA_INTF_T_DVB_DVR:
+ return "DVR";
+ case MEDIA_INTF_T_DVB_CA:
+ return "CA";
+ case MEDIA_INTF_T_DVB_NET:
+ return "dvbnet";
+ case MEDIA_INTF_T_V4L_VIDEO:
+ return "video";
+ case MEDIA_INTF_T_V4L_VBI:
+ return "vbi";
+ case MEDIA_INTF_T_V4L_RADIO:
+ return "radio";
+ case MEDIA_INTF_T_V4L_SUBDEV:
+ return "v4l2-subdev";
+ case MEDIA_INTF_T_V4L_SWRADIO:
+ return "swradio";
+ default:
+ return "unknown-intf";
+ }
+};
+
+__must_check int __media_entity_enum_init(struct media_entity_enum *ent_enum,
+ int idx_max)
+{
+ ent_enum->bmap = kcalloc(DIV_ROUND_UP(idx_max, BITS_PER_LONG),
+ sizeof(long), GFP_KERNEL);
+ if (!ent_enum->bmap)
+ return -ENOMEM;
+
+ bitmap_zero(ent_enum->bmap, idx_max);
+ ent_enum->idx_max = idx_max;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__media_entity_enum_init);
+
+void media_entity_enum_cleanup(struct media_entity_enum *ent_enum)
+{
+ kfree(ent_enum->bmap);
+}
+EXPORT_SYMBOL_GPL(media_entity_enum_cleanup);
+
/**
- * media_entity_init - Initialize a media entity
- *
- * @num_pads: Total number of sink and source pads.
- * @extra_links: Initial estimate of the number of extra links.
- * @pads: Array of 'num_pads' pads.
- *
- * The total number of pads is an intrinsic property of entities known by the
- * entity driver, while the total number of links depends on hardware design
- * and is an extrinsic property unknown to the entity driver. However, in most
- * use cases the entity driver can guess the number of links which can safely
- * be assumed to be equal to or larger than the number of pads.
- *
- * For those reasons the links array can be preallocated based on the entity
- * driver guess and will be reallocated later if extra links need to be
- * created.
+ * dev_dbg_obj - Prints in debug mode a change on some object
*
- * This function allocates a links array with enough space to hold at least
- * 'num_pads' + 'extra_links' elements. The media_entity::max_links field will
- * be set to the number of allocated elements.
+ * @event_name: Name of the event to report. Could be __func__
+ * @gobj: Pointer to the object
*
- * The pads array is managed by the entity driver and passed to
- * media_entity_init() where its pointer will be stored in the entity structure.
+ * Enabled only if DEBUG or CONFIG_DYNAMIC_DEBUG. Otherwise, it
+ * won't produce any code.
*/
-int
-media_entity_init(struct media_entity *entity, u16 num_pads,
- struct media_pad *pads, u16 extra_links)
+static void dev_dbg_obj(const char *event_name, struct media_gobj *gobj)
{
- struct media_link *links;
- unsigned int max_links = num_pads + extra_links;
- unsigned int i;
+#if defined(DEBUG) || defined (CONFIG_DYNAMIC_DEBUG)
+ switch (media_type(gobj)) {
+ case MEDIA_GRAPH_ENTITY:
+ dev_dbg(gobj->mdev->dev,
+ "%s id %u: entity '%s'\n",
+ event_name, media_id(gobj),
+ gobj_to_entity(gobj)->name);
+ break;
+ case MEDIA_GRAPH_LINK:
+ {
+ struct media_link *link = gobj_to_link(gobj);
+
+ dev_dbg(gobj->mdev->dev,
+ "%s id %u: %s link id %u ==> id %u\n",
+ event_name, media_id(gobj),
+ media_type(link->gobj0) == MEDIA_GRAPH_PAD ?
+ "data" : "interface",
+ media_id(link->gobj0),
+ media_id(link->gobj1));
+ break;
+ }
+ case MEDIA_GRAPH_PAD:
+ {
+ struct media_pad *pad = gobj_to_pad(gobj);
+
+ dev_dbg(gobj->mdev->dev,
+ "%s id %u: %s%spad '%s':%d\n",
+ event_name, media_id(gobj),
+ pad->flags & MEDIA_PAD_FL_SINK ? "sink " : "",
+ pad->flags & MEDIA_PAD_FL_SOURCE ? "source " : "",
+ pad->entity->name, pad->index);
+ break;
+ }
+ case MEDIA_GRAPH_INTF_DEVNODE:
+ {
+ struct media_interface *intf = gobj_to_intf(gobj);
+ struct media_intf_devnode *devnode = intf_to_devnode(intf);
+
+ dev_dbg(gobj->mdev->dev,
+ "%s id %u: intf_devnode %s - major: %d, minor: %d\n",
+ event_name, media_id(gobj),
+ intf_type(intf),
+ devnode->major, devnode->minor);
+ break;
+ }
+ }
+#endif
+}
- links = kzalloc(max_links * sizeof(links[0]), GFP_KERNEL);
- if (links == NULL)
- return -ENOMEM;
+void media_gobj_create(struct media_device *mdev,
+ enum media_gobj_type type,
+ struct media_gobj *gobj)
+{
+ BUG_ON(!mdev);
+
+ gobj->mdev = mdev;
+
+ /* Create a per-type unique object ID */
+ gobj->id = media_gobj_gen_id(type, ++mdev->id);
+
+ switch (type) {
+ case MEDIA_GRAPH_ENTITY:
+ list_add_tail(&gobj->list, &mdev->entities);
+ break;
+ case MEDIA_GRAPH_PAD:
+ list_add_tail(&gobj->list, &mdev->pads);
+ break;
+ case MEDIA_GRAPH_LINK:
+ list_add_tail(&gobj->list, &mdev->links);
+ break;
+ case MEDIA_GRAPH_INTF_DEVNODE:
+ list_add_tail(&gobj->list, &mdev->interfaces);
+ break;
+ }
+
+ mdev->topology_version++;
+
+ dev_dbg_obj(__func__, gobj);
+}
+
+void media_gobj_destroy(struct media_gobj *gobj)
+{
+ dev_dbg_obj(__func__, gobj);
+
+ gobj->mdev->topology_version++;
+
+ /* Remove the object from mdev list */
+ list_del(&gobj->list);
+}
+
+int media_entity_pads_init(struct media_entity *entity, u16 num_pads,
+ struct media_pad *pads)
+{
+ struct media_device *mdev = entity->graph_obj.mdev;
+ unsigned int i;
- entity->group_id = 0;
- entity->max_links = max_links;
- entity->num_links = 0;
- entity->num_backlinks = 0;
entity->num_pads = num_pads;
entity->pads = pads;
- entity->links = links;
+
+ if (mdev)
+ spin_lock(&mdev->lock);
for (i = 0; i < num_pads; i++) {
pads[i].entity = entity;
pads[i].index = i;
+ if (mdev)
+ media_gobj_create(mdev, MEDIA_GRAPH_PAD,
+ &entity->pads[i].graph_obj);
}
- return 0;
-}
-EXPORT_SYMBOL_GPL(media_entity_init);
+ if (mdev)
+ spin_unlock(&mdev->lock);
-void
-media_entity_cleanup(struct media_entity *entity)
-{
- kfree(entity->links);
+ return 0;
}
-EXPORT_SYMBOL_GPL(media_entity_cleanup);
+EXPORT_SYMBOL_GPL(media_entity_pads_init);
/* -----------------------------------------------------------------------------
* Graph traversal
@@ -108,7 +241,7 @@ static void stack_push(struct media_entity_graph *graph,
return;
}
graph->top++;
- graph->stack[graph->top].link = 0;
+ graph->stack[graph->top].link = entity->links.next;
graph->stack[graph->top].entity = entity;
}
@@ -125,43 +258,51 @@ static struct media_entity *stack_pop(struct media_entity_graph *graph)
#define link_top(en) ((en)->stack[(en)->top].link)
#define stack_top(en) ((en)->stack[(en)->top].entity)
+/*
+ * TODO: Get rid of this.
+ */
+#define MEDIA_ENTITY_MAX_PADS 512
+
/**
- * media_entity_graph_walk_start - Start walking the media graph at a given entity
+ * media_entity_graph_walk_init - Allocate resources for graph walk
* @graph: Media graph structure that will be used to walk the graph
- * @entity: Starting entity
+ * @mdev: Media device
*
- * This function initializes the graph traversal structure to walk the entities
- * graph starting at the given entity. The traversal structure must not be
- * modified by the caller during graph traversal. When done the structure can
- * safely be freed.
+ * Reserve resources for graph walk in media device's current
+ * state. The memory must be released using
+ * media_entity_graph_walk_free().
+ *
+ * Returns error on failure, zero on success.
*/
+__must_check int media_entity_graph_walk_init(
+ struct media_entity_graph *graph, struct media_device *mdev)
+{
+ return media_entity_enum_init(&graph->ent_enum, mdev);
+}
+EXPORT_SYMBOL_GPL(media_entity_graph_walk_init);
+
+/**
+ * media_entity_graph_walk_cleanup - Release resources related to graph walking
+ * @graph: Media graph structure that was used to walk the graph
+ */
+void media_entity_graph_walk_cleanup(struct media_entity_graph *graph)
+{
+ media_entity_enum_cleanup(&graph->ent_enum);
+}
+EXPORT_SYMBOL_GPL(media_entity_graph_walk_cleanup);
+
void media_entity_graph_walk_start(struct media_entity_graph *graph,
struct media_entity *entity)
{
+ media_entity_enum_zero(&graph->ent_enum);
+ media_entity_enum_set(&graph->ent_enum, entity);
+
graph->top = 0;
graph->stack[graph->top].entity = NULL;
- bitmap_zero(graph->entities, MEDIA_ENTITY_ENUM_MAX_ID);
-
- if (WARN_ON(entity->id >= MEDIA_ENTITY_ENUM_MAX_ID))
- return;
-
- __set_bit(entity->id, graph->entities);
stack_push(graph, entity);
}
EXPORT_SYMBOL_GPL(media_entity_graph_walk_start);
-/**
- * media_entity_graph_walk_next - Get the next entity in the graph
- * @graph: Media graph structure
- *
- * Perform a depth-first traversal of the given media entities graph.
- *
- * The graph structure must have been previously initialized with a call to
- * media_entity_graph_walk_start().
- *
- * Return the next entity in the graph or NULL if the whole graph have been
- * traversed.
- */
struct media_entity *
media_entity_graph_walk_next(struct media_entity_graph *graph)
{
@@ -173,30 +314,30 @@ media_entity_graph_walk_next(struct media_entity_graph *graph)
* top of the stack until no more entities on the level can be
* found.
*/
- while (link_top(graph) < stack_top(graph)->num_links) {
+ while (link_top(graph) != &stack_top(graph)->links) {
struct media_entity *entity = stack_top(graph);
- struct media_link *link = &entity->links[link_top(graph)];
+ struct media_link *link;
struct media_entity *next;
+ link = list_entry(link_top(graph), typeof(*link), list);
+
/* The link is not enabled so we do not follow. */
if (!(link->flags & MEDIA_LNK_FL_ENABLED)) {
- link_top(graph)++;
+ link_top(graph) = link_top(graph)->next;
continue;
}
/* Get the entity in the other end of the link . */
next = media_entity_other(entity, link);
- if (WARN_ON(next->id >= MEDIA_ENTITY_ENUM_MAX_ID))
- return NULL;
/* Has the entity already been visited? */
- if (__test_and_set_bit(next->id, graph->entities)) {
- link_top(graph)++;
+ if (media_entity_enum_test_and_set(&graph->ent_enum, next)) {
+ link_top(graph) = link_top(graph)->next;
continue;
}
/* Push the new entity to stack and start over. */
- link_top(graph)++;
+ link_top(graph) = link_top(graph)->next;
stack_push(graph, next);
}
@@ -208,39 +349,36 @@ EXPORT_SYMBOL_GPL(media_entity_graph_walk_next);
* Pipeline management
*/
-/**
- * media_entity_pipeline_start - Mark a pipeline as streaming
- * @entity: Starting entity
- * @pipe: Media pipeline to be assigned to all entities in the pipeline.
- *
- * Mark all entities connected to a given entity through enabled links, either
- * directly or indirectly, as streaming. The given pipeline object is assigned to
- * every entity in the pipeline and stored in the media_entity pipe field.
- *
- * Calls to this function can be nested, in which case the same number of
- * media_entity_pipeline_stop() calls will be required to stop streaming. The
- * pipeline pointer must be identical for all nested calls to
- * media_entity_pipeline_start().
- */
__must_check int media_entity_pipeline_start(struct media_entity *entity,
struct media_pipeline *pipe)
{
- struct media_device *mdev = entity->parent;
- struct media_entity_graph graph;
+ struct media_device *mdev = entity->graph_obj.mdev;
+ struct media_entity_graph *graph = &pipe->graph;
struct media_entity *entity_err = entity;
+ struct media_link *link;
int ret;
mutex_lock(&mdev->graph_mutex);
- media_entity_graph_walk_start(&graph, entity);
+ if (!pipe->streaming_count++) {
+ ret = media_entity_graph_walk_init(&pipe->graph, mdev);
+ if (ret)
+ goto error_graph_walk_start;
+ }
+
+ media_entity_graph_walk_start(&pipe->graph, entity);
- while ((entity = media_entity_graph_walk_next(&graph))) {
+ while ((entity = media_entity_graph_walk_next(graph))) {
DECLARE_BITMAP(active, MEDIA_ENTITY_MAX_PADS);
DECLARE_BITMAP(has_no_links, MEDIA_ENTITY_MAX_PADS);
- unsigned int i;
entity->stream_count++;
- WARN_ON(entity->pipe && entity->pipe != pipe);
+
+ if (WARN_ON(entity->pipe && entity->pipe != pipe)) {
+ ret = -EBUSY;
+ goto error;
+ }
+
entity->pipe = pipe;
/* Already streaming --- no need to check. */
@@ -253,8 +391,7 @@ __must_check int media_entity_pipeline_start(struct media_entity *entity,
bitmap_zero(active, entity->num_pads);
bitmap_fill(has_no_links, entity->num_pads);
- for (i = 0; i < entity->num_links; i++) {
- struct media_link *link = &entity->links[i];
+ list_for_each_entry(link, &entity->links, list) {
struct media_pad *pad = link->sink->entity == entity
? link->sink : link->source;
@@ -280,7 +417,7 @@ __must_check int media_entity_pipeline_start(struct media_entity *entity,
ret = entity->ops->link_validate(link);
if (ret < 0 && ret != -ENOIOCTLCMD) {
- dev_dbg(entity->parent->dev,
+ dev_dbg(entity->graph_obj.mdev->dev,
"link validation failed for \"%s\":%u -> \"%s\":%u, error %d\n",
link->source->entity->name,
link->source->index,
@@ -294,7 +431,7 @@ __must_check int media_entity_pipeline_start(struct media_entity *entity,
if (!bitmap_full(active, entity->num_pads)) {
ret = -EPIPE;
- dev_dbg(entity->parent->dev,
+ dev_dbg(entity->graph_obj.mdev->dev,
"\"%s\":%u must be connected by an enabled link\n",
entity->name,
(unsigned)find_first_zero_bit(
@@ -312,9 +449,9 @@ error:
* Link validation on graph failed. We revert what we did and
* return the error.
*/
- media_entity_graph_walk_start(&graph, entity_err);
+ media_entity_graph_walk_start(graph, entity_err);
- while ((entity_err = media_entity_graph_walk_next(&graph))) {
+ while ((entity_err = media_entity_graph_walk_next(graph))) {
entity_err->stream_count--;
if (entity_err->stream_count == 0)
entity_err->pipe = NULL;
@@ -327,39 +464,36 @@ error:
break;
}
+error_graph_walk_start:
+ if (!--pipe->streaming_count)
+ media_entity_graph_walk_cleanup(graph);
+
mutex_unlock(&mdev->graph_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(media_entity_pipeline_start);
-/**
- * media_entity_pipeline_stop - Mark a pipeline as not streaming
- * @entity: Starting entity
- *
- * Mark all entities connected to a given entity through enabled links, either
- * directly or indirectly, as not streaming. The media_entity pipe field is
- * reset to NULL.
- *
- * If multiple calls to media_entity_pipeline_start() have been made, the same
- * number of calls to this function are required to mark the pipeline as not
- * streaming.
- */
void media_entity_pipeline_stop(struct media_entity *entity)
{
- struct media_device *mdev = entity->parent;
- struct media_entity_graph graph;
+ struct media_device *mdev = entity->graph_obj.mdev;
+ struct media_entity_graph *graph = &entity->pipe->graph;
+ struct media_pipeline *pipe = entity->pipe;
mutex_lock(&mdev->graph_mutex);
- media_entity_graph_walk_start(&graph, entity);
+ WARN_ON(!pipe->streaming_count);
+ media_entity_graph_walk_start(graph, entity);
- while ((entity = media_entity_graph_walk_next(&graph))) {
+ while ((entity = media_entity_graph_walk_next(graph))) {
entity->stream_count--;
if (entity->stream_count == 0)
entity->pipe = NULL;
}
+ if (!--pipe->streaming_count)
+ media_entity_graph_walk_cleanup(graph);
+
mutex_unlock(&mdev->graph_mutex);
}
EXPORT_SYMBOL_GPL(media_entity_pipeline_stop);
@@ -368,44 +502,26 @@ EXPORT_SYMBOL_GPL(media_entity_pipeline_stop);
* Module use count
*/
-/*
- * media_entity_get - Get a reference to the parent module
- * @entity: The entity
- *
- * Get a reference to the parent media device module.
- *
- * The function will return immediately if @entity is NULL.
- *
- * Return a pointer to the entity on success or NULL on failure.
- */
struct media_entity *media_entity_get(struct media_entity *entity)
{
if (entity == NULL)
return NULL;
- if (entity->parent->dev &&
- !try_module_get(entity->parent->dev->driver->owner))
+ if (entity->graph_obj.mdev->dev &&
+ !try_module_get(entity->graph_obj.mdev->dev->driver->owner))
return NULL;
return entity;
}
EXPORT_SYMBOL_GPL(media_entity_get);
-/*
- * media_entity_put - Release the reference to the parent module
- * @entity: The entity
- *
- * Release the reference count acquired by media_entity_get().
- *
- * The function will return immediately if @entity is NULL.
- */
void media_entity_put(struct media_entity *entity)
{
if (entity == NULL)
return;
- if (entity->parent->dev)
- module_put(entity->parent->dev->driver->owner);
+ if (entity->graph_obj.mdev->dev)
+ module_put(entity->graph_obj.mdev->dev->driver->owner);
}
EXPORT_SYMBOL_GPL(media_entity_put);
@@ -413,29 +529,52 @@ EXPORT_SYMBOL_GPL(media_entity_put);
* Links management
*/
-static struct media_link *media_entity_add_link(struct media_entity *entity)
+static struct media_link *media_add_link(struct list_head *head)
{
- if (entity->num_links >= entity->max_links) {
- struct media_link *links = entity->links;
- unsigned int max_links = entity->max_links + 2;
- unsigned int i;
+ struct media_link *link;
+
+ link = kzalloc(sizeof(*link), GFP_KERNEL);
+ if (link == NULL)
+ return NULL;
- links = krealloc(links, max_links * sizeof(*links), GFP_KERNEL);
- if (links == NULL)
- return NULL;
+ list_add_tail(&link->list, head);
- for (i = 0; i < entity->num_links; i++)
- links[i].reverse->reverse = &links[i];
+ return link;
+}
- entity->max_links = max_links;
- entity->links = links;
- }
+static void __media_entity_remove_link(struct media_entity *entity,
+ struct media_link *link)
+{
+ struct media_link *rlink, *tmp;
+ struct media_entity *remote;
+
+ if (link->source->entity == entity)
+ remote = link->sink->entity;
+ else
+ remote = link->source->entity;
- return &entity->links[entity->num_links++];
+ list_for_each_entry_safe(rlink, tmp, &remote->links, list) {
+ if (rlink != link->reverse)
+ continue;
+
+ if (link->source->entity == entity)
+ remote->num_backlinks--;
+
+ /* Remove the remote link */
+ list_del(&rlink->list);
+ media_gobj_destroy(&rlink->graph_obj);
+ kfree(rlink);
+
+ if (--remote->num_links == 0)
+ break;
+ }
+ list_del(&link->list);
+ media_gobj_destroy(&link->graph_obj);
+ kfree(link);
}
int
-media_entity_create_link(struct media_entity *source, u16 source_pad,
+media_create_pad_link(struct media_entity *source, u16 source_pad,
struct media_entity *sink, u16 sink_pad, u32 flags)
{
struct media_link *link;
@@ -445,68 +584,118 @@ media_entity_create_link(struct media_entity *source, u16 source_pad,
BUG_ON(source_pad >= source->num_pads);
BUG_ON(sink_pad >= sink->num_pads);
- link = media_entity_add_link(source);
+ link = media_add_link(&source->links);
if (link == NULL)
return -ENOMEM;
link->source = &source->pads[source_pad];
link->sink = &sink->pads[sink_pad];
- link->flags = flags;
+ link->flags = flags & ~MEDIA_LNK_FL_INTERFACE_LINK;
+
+ /* Initialize graph object embedded at the new link */
+ media_gobj_create(source->graph_obj.mdev, MEDIA_GRAPH_LINK,
+ &link->graph_obj);
/* Create the backlink. Backlinks are used to help graph traversal and
* are not reported to userspace.
*/
- backlink = media_entity_add_link(sink);
+ backlink = media_add_link(&sink->links);
if (backlink == NULL) {
- source->num_links--;
+ __media_entity_remove_link(source, link);
return -ENOMEM;
}
backlink->source = &source->pads[source_pad];
backlink->sink = &sink->pads[sink_pad];
backlink->flags = flags;
+ backlink->is_backlink = true;
+
+ /* Initialize graph object embedded at the new link */
+ media_gobj_create(sink->graph_obj.mdev, MEDIA_GRAPH_LINK,
+ &backlink->graph_obj);
link->reverse = backlink;
backlink->reverse = link;
sink->num_backlinks++;
+ sink->num_links++;
+ source->num_links++;
return 0;
}
-EXPORT_SYMBOL_GPL(media_entity_create_link);
-
-void __media_entity_remove_links(struct media_entity *entity)
+EXPORT_SYMBOL_GPL(media_create_pad_link);
+
+int media_create_pad_links(const struct media_device *mdev,
+ const u32 source_function,
+ struct media_entity *source,
+ const u16 source_pad,
+ const u32 sink_function,
+ struct media_entity *sink,
+ const u16 sink_pad,
+ u32 flags,
+ const bool allow_both_undefined)
{
- unsigned int i;
-
- for (i = 0; i < entity->num_links; i++) {
- struct media_link *link = &entity->links[i];
- struct media_entity *remote;
- unsigned int r = 0;
-
- if (link->source->entity == entity)
- remote = link->sink->entity;
- else
- remote = link->source->entity;
-
- while (r < remote->num_links) {
- struct media_link *rlink = &remote->links[r];
+ struct media_entity *entity;
+ unsigned function;
+ int ret;
- if (rlink != link->reverse) {
- r++;
+ /* Trivial case: 1:1 relation */
+ if (source && sink)
+ return media_create_pad_link(source, source_pad,
+ sink, sink_pad, flags);
+
+ /* Worse case scenario: n:n relation */
+ if (!source && !sink) {
+ if (!allow_both_undefined)
+ return 0;
+ media_device_for_each_entity(source, mdev) {
+ if (source->function != source_function)
continue;
+ media_device_for_each_entity(sink, mdev) {
+ if (sink->function != sink_function)
+ continue;
+ ret = media_create_pad_link(source, source_pad,
+ sink, sink_pad,
+ flags);
+ if (ret)
+ return ret;
+ flags &= ~(MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
}
+ }
+ return 0;
+ }
- if (link->source->entity == entity)
- remote->num_backlinks--;
+ /* Handle 1:n and n:1 cases */
+ if (source)
+ function = sink_function;
+ else
+ function = source_function;
- if (--remote->num_links == 0)
- break;
+ media_device_for_each_entity(entity, mdev) {
+ if (entity->function != function)
+ continue;
- /* Insert last entry in place of the dropped link. */
- *rlink = remote->links[remote->num_links];
- }
+ if (source)
+ ret = media_create_pad_link(source, source_pad,
+ entity, sink_pad, flags);
+ else
+ ret = media_create_pad_link(entity, source_pad,
+ sink, sink_pad, flags);
+ if (ret)
+ return ret;
+ flags &= ~(MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
}
+ return 0;
+}
+EXPORT_SYMBOL_GPL(media_create_pad_links);
+
+void __media_entity_remove_links(struct media_entity *entity)
+{
+ struct media_link *link, *tmp;
+
+ list_for_each_entry_safe(link, tmp, &entity->links, list)
+ __media_entity_remove_link(entity, link);
entity->num_links = 0;
entity->num_backlinks = 0;
@@ -515,13 +704,15 @@ EXPORT_SYMBOL_GPL(__media_entity_remove_links);
void media_entity_remove_links(struct media_entity *entity)
{
+ struct media_device *mdev = entity->graph_obj.mdev;
+
/* Do nothing if the entity is not registered. */
- if (entity->parent == NULL)
+ if (mdev == NULL)
return;
- mutex_lock(&entity->parent->graph_mutex);
+ spin_lock(&mdev->lock);
__media_entity_remove_links(entity);
- mutex_unlock(&entity->parent->graph_mutex);
+ spin_unlock(&mdev->lock);
}
EXPORT_SYMBOL_GPL(media_entity_remove_links);
@@ -549,20 +740,6 @@ static int __media_entity_setup_link_notify(struct media_link *link, u32 flags)
return 0;
}
-/**
- * __media_entity_setup_link - Configure a media link
- * @link: The link being configured
- * @flags: Link configuration flags
- *
- * The bulk of link setup is handled by the two entities connected through the
- * link. This function notifies both entities of the link configuration change.
- *
- * If the link is immutable or if the current and new configuration are
- * identical, return immediately.
- *
- * The user is expected to hold link->source->parent->mutex. If not,
- * media_entity_setup_link() should be used instead.
- */
int __media_entity_setup_link(struct media_link *link, u32 flags)
{
const u32 mask = MEDIA_LNK_FL_ENABLED;
@@ -590,7 +767,7 @@ int __media_entity_setup_link(struct media_link *link, u32 flags)
(source->stream_count || sink->stream_count))
return -EBUSY;
- mdev = source->parent;
+ mdev = source->graph_obj.mdev;
if (mdev->link_notify) {
ret = mdev->link_notify(link, flags,
@@ -611,31 +788,20 @@ int media_entity_setup_link(struct media_link *link, u32 flags)
{
int ret;
- mutex_lock(&link->source->entity->parent->graph_mutex);
+ mutex_lock(&link->graph_obj.mdev->graph_mutex);
ret = __media_entity_setup_link(link, flags);
- mutex_unlock(&link->source->entity->parent->graph_mutex);
+ mutex_unlock(&link->graph_obj.mdev->graph_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(media_entity_setup_link);
-/**
- * media_entity_find_link - Find a link between two pads
- * @source: Source pad
- * @sink: Sink pad
- *
- * Return a pointer to the link between the two entities. If no such link
- * exists, return NULL.
- */
struct media_link *
media_entity_find_link(struct media_pad *source, struct media_pad *sink)
{
struct media_link *link;
- unsigned int i;
-
- for (i = 0; i < source->entity->num_links; ++i) {
- link = &source->entity->links[i];
+ list_for_each_entry(link, &source->entity->links, list) {
if (link->source->entity == source->entity &&
link->source->index == source->index &&
link->sink->entity == sink->entity &&
@@ -647,23 +813,11 @@ media_entity_find_link(struct media_pad *source, struct media_pad *sink)
}
EXPORT_SYMBOL_GPL(media_entity_find_link);
-/**
- * media_entity_remote_pad - Find the pad at the remote end of a link
- * @pad: Pad at the local end of the link
- *
- * Search for a remote pad connected to the given pad by iterating over all
- * links originating or terminating at that pad until an enabled link is found.
- *
- * Return a pointer to the pad at the remote end of the first found enabled
- * link, or NULL if no enabled link has been found.
- */
struct media_pad *media_entity_remote_pad(struct media_pad *pad)
{
- unsigned int i;
-
- for (i = 0; i < pad->entity->num_links; i++) {
- struct media_link *link = &pad->entity->links[i];
+ struct media_link *link;
+ list_for_each_entry(link, &pad->entity->links, list) {
if (!(link->flags & MEDIA_LNK_FL_ENABLED))
continue;
@@ -678,3 +832,113 @@ struct media_pad *media_entity_remote_pad(struct media_pad *pad)
}
EXPORT_SYMBOL_GPL(media_entity_remote_pad);
+
+static void media_interface_init(struct media_device *mdev,
+ struct media_interface *intf,
+ u32 gobj_type,
+ u32 intf_type, u32 flags)
+{
+ intf->type = intf_type;
+ intf->flags = flags;
+ INIT_LIST_HEAD(&intf->links);
+
+ media_gobj_create(mdev, gobj_type, &intf->graph_obj);
+}
+
+/* Functions related to the media interface via device nodes */
+
+struct media_intf_devnode *media_devnode_create(struct media_device *mdev,
+ u32 type, u32 flags,
+ u32 major, u32 minor)
+{
+ struct media_intf_devnode *devnode;
+
+ devnode = kzalloc(sizeof(*devnode), GFP_KERNEL);
+ if (!devnode)
+ return NULL;
+
+ devnode->major = major;
+ devnode->minor = minor;
+
+ media_interface_init(mdev, &devnode->intf, MEDIA_GRAPH_INTF_DEVNODE,
+ type, flags);
+
+ return devnode;
+}
+EXPORT_SYMBOL_GPL(media_devnode_create);
+
+void media_devnode_remove(struct media_intf_devnode *devnode)
+{
+ media_remove_intf_links(&devnode->intf);
+ media_gobj_destroy(&devnode->intf.graph_obj);
+ kfree(devnode);
+}
+EXPORT_SYMBOL_GPL(media_devnode_remove);
+
+struct media_link *media_create_intf_link(struct media_entity *entity,
+ struct media_interface *intf,
+ u32 flags)
+{
+ struct media_link *link;
+
+ link = media_add_link(&intf->links);
+ if (link == NULL)
+ return NULL;
+
+ link->intf = intf;
+ link->entity = entity;
+ link->flags = flags | MEDIA_LNK_FL_INTERFACE_LINK;
+
+ /* Initialize graph object embedded at the new link */
+ media_gobj_create(intf->graph_obj.mdev, MEDIA_GRAPH_LINK,
+ &link->graph_obj);
+
+ return link;
+}
+EXPORT_SYMBOL_GPL(media_create_intf_link);
+
+void __media_remove_intf_link(struct media_link *link)
+{
+ list_del(&link->list);
+ media_gobj_destroy(&link->graph_obj);
+ kfree(link);
+}
+EXPORT_SYMBOL_GPL(__media_remove_intf_link);
+
+void media_remove_intf_link(struct media_link *link)
+{
+ struct media_device *mdev = link->graph_obj.mdev;
+
+ /* Do nothing if the intf is not registered. */
+ if (mdev == NULL)
+ return;
+
+ spin_lock(&mdev->lock);
+ __media_remove_intf_link(link);
+ spin_unlock(&mdev->lock);
+}
+EXPORT_SYMBOL_GPL(media_remove_intf_link);
+
+void __media_remove_intf_links(struct media_interface *intf)
+{
+ struct media_link *link, *tmp;
+
+ list_for_each_entry_safe(link, tmp, &intf->links, list)
+ __media_remove_intf_link(link);
+
+}
+EXPORT_SYMBOL_GPL(__media_remove_intf_links);
+
+void media_remove_intf_links(struct media_interface *intf)
+{
+ struct media_device *mdev = intf->graph_obj.mdev;
+
+ /* Do nothing if the intf is not registered. */
+ if (mdev == NULL)
+ return;
+
+ spin_lock(&mdev->lock);
+ __media_remove_intf_links(intf);
+ spin_unlock(&mdev->lock);
+}
+EXPORT_SYMBOL_GPL(media_remove_intf_links);
diff --git a/drivers/media/pci/bt8xx/bttv-cards.c b/drivers/media/pci/bt8xx/bttv-cards.c
index 4654fb65ca21..8a17cc0bfa07 100644
--- a/drivers/media/pci/bt8xx/bttv-cards.c
+++ b/drivers/media/pci/bt8xx/bttv-cards.c
@@ -41,7 +41,7 @@
#include "bttvp.h"
#include <media/v4l2-common.h>
-#include <media/tvaudio.h>
+#include <media/i2c/tvaudio.h>
#include "bttv-audio-hook.h"
/* fwd decl */
@@ -3808,7 +3808,7 @@ static void bttv_tea575x_set_direction(struct snd_tea575x *tea, bool output)
gpio_inout(mask, (1 << gpio.clk) | (1 << gpio.wren));
}
-static struct snd_tea575x_ops bttv_tea_ops = {
+static const struct snd_tea575x_ops bttv_tea_ops = {
.set_pins = bttv_tea575x_set_pins,
.get_pins = bttv_tea575x_get_pins,
.set_direction = bttv_tea575x_set_direction,
diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c
index 15a4ebc2844d..9400e996087b 100644
--- a/drivers/media/pci/bt8xx/bttv-driver.c
+++ b/drivers/media/pci/bt8xx/bttv-driver.c
@@ -50,15 +50,15 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
-#include <media/tvaudio.h>
-#include <media/msp3400.h>
+#include <media/i2c/tvaudio.h>
+#include <media/drv-intf/msp3400.h>
#include <linux/dma-mapping.h>
#include <asm/io.h>
#include <asm/byteorder.h>
-#include <media/saa6588.h>
+#include <media/i2c/saa6588.h>
#define BTTV_VERSION "0.9.19"
diff --git a/drivers/media/pci/bt8xx/bttvp.h b/drivers/media/pci/bt8xx/bttvp.h
index 31bf79d3b0d2..b1e0023f923c 100644
--- a/drivers/media/pci/bt8xx/bttvp.h
+++ b/drivers/media/pci/bt8xx/bttvp.h
@@ -41,8 +41,8 @@
#include <media/videobuf-dma-sg.h>
#include <media/tveeprom.h>
#include <media/rc-core.h>
-#include <media/ir-kbd-i2c.h>
-#include <media/tea575x.h>
+#include <media/i2c/ir-kbd-i2c.h>
+#include <media/drv-intf/tea575x.h>
#include "bt848.h"
#include "bttv.h"
diff --git a/drivers/media/pci/bt8xx/dst_ca.c b/drivers/media/pci/bt8xx/dst_ca.c
index c5cc14ef8347..da8b414fd824 100644
--- a/drivers/media/pci/bt8xx/dst_ca.c
+++ b/drivers/media/pci/bt8xx/dst_ca.c
@@ -705,7 +705,8 @@ struct dvb_device *dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_
struct dvb_device *dvbdev;
dprintk(verbose, DST_CA_ERROR, 1, "registering DST-CA device");
- if (dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, DVB_DEVICE_CA) == 0) {
+ if (dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst,
+ DVB_DEVICE_CA, 0) == 0) {
dst->dst_ca = dvbdev;
return dst->dst_ca;
}
diff --git a/drivers/media/pci/cobalt/cobalt-driver.c b/drivers/media/pci/cobalt/cobalt-driver.c
index 8fed61ec712e..8d6f04fc8013 100644
--- a/drivers/media/pci/cobalt/cobalt-driver.c
+++ b/drivers/media/pci/cobalt/cobalt-driver.c
@@ -21,9 +21,9 @@
*/
#include <linux/delay.h>
-#include <media/adv7604.h>
-#include <media/adv7842.h>
-#include <media/adv7511.h>
+#include <media/i2c/adv7604.h>
+#include <media/i2c/adv7842.h>
+#include <media/i2c/adv7511.h>
#include <media/v4l2-event.h>
#include <media/v4l2-ctrls.h>
diff --git a/drivers/media/pci/cobalt/cobalt-irq.c b/drivers/media/pci/cobalt/cobalt-irq.c
index 3de26d0714b5..b190d4f81c6e 100644
--- a/drivers/media/pci/cobalt/cobalt-irq.c
+++ b/drivers/media/pci/cobalt/cobalt-irq.c
@@ -18,7 +18,7 @@
* SOFTWARE.
*/
-#include <media/adv7604.h>
+#include <media/i2c/adv7604.h>
#include "cobalt-driver.h"
#include "cobalt-irq.h"
@@ -134,7 +134,7 @@ done:
skip = true;
s->skip_first_frames--;
}
- v4l2_get_timestamp(&cb->vb.timestamp);
+ cb->vb.vb2_buf.timestamp = ktime_get_ns();
/* TODO: the sequence number should be read from the FPGA so we
also know about dropped frames. */
cb->vb.sequence = s->sequence++;
diff --git a/drivers/media/pci/cobalt/cobalt-v4l2.c b/drivers/media/pci/cobalt/cobalt-v4l2.c
index ff46e424262f..c0ba458f6cf3 100644
--- a/drivers/media/pci/cobalt/cobalt-v4l2.c
+++ b/drivers/media/pci/cobalt/cobalt-v4l2.c
@@ -29,8 +29,8 @@
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
#include <media/v4l2-dv-timings.h>
-#include <media/adv7604.h>
-#include <media/adv7842.h>
+#include <media/i2c/adv7604.h>
+#include <media/i2c/adv7842.h>
#include "cobalt-alsa.h"
#include "cobalt-cpld.h"
@@ -43,11 +43,10 @@ static const struct v4l2_dv_timings cea1080p60 = V4L2_DV_BT_CEA_1920X1080P60;
/* vb2 DMA streaming ops */
-static int cobalt_queue_setup(struct vb2_queue *q, const void *parg,
+static int cobalt_queue_setup(struct vb2_queue *q,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct cobalt_stream *s = q->drv_priv;
unsigned size = s->stride * s->height;
@@ -55,14 +54,11 @@ static int cobalt_queue_setup(struct vb2_queue *q, const void *parg,
*num_buffers = 3;
if (*num_buffers > NR_BUFS)
*num_buffers = NR_BUFS;
+ alloc_ctxs[0] = s->cobalt->alloc_ctx;
+ if (*num_planes)
+ return sizes[0] < size ? -EINVAL : 0;
*num_planes = 1;
- if (fmt) {
- if (fmt->fmt.pix.sizeimage < size)
- return -EINVAL;
- size = fmt->fmt.pix.sizeimage;
- }
sizes[0] = size;
- alloc_ctxs[0] = s->cobalt->alloc_ctx;
return 0;
}
@@ -649,7 +645,7 @@ static int cobalt_s_dv_timings(struct file *file, void *priv_fh,
return 0;
}
- if (v4l2_match_dv_timings(timings, &s->timings, 0))
+ if (v4l2_match_dv_timings(timings, &s->timings, 0, false))
return 0;
if (vb2_is_busy(&s->q))
diff --git a/drivers/media/pci/cx18/cx18-cards.c b/drivers/media/pci/cx18/cx18-cards.c
index c07c849b1aaf..5e01ea441dc4 100644
--- a/drivers/media/pci/cx18/cx18-cards.c
+++ b/drivers/media/pci/cx18/cx18-cards.c
@@ -26,7 +26,7 @@
#include "cx18-cards.h"
#include "cx18-av-core.h"
#include "cx18-i2c.h"
-#include <media/cs5345.h>
+#include <media/i2c/cs5345.h>
#define V4L2_STD_PAL_SECAM (V4L2_STD_PAL|V4L2_STD_SECAM)
diff --git a/drivers/media/pci/cx18/cx18-controls.c b/drivers/media/pci/cx18/cx18-controls.c
index 71227a155cba..adb5a8c72c06 100644
--- a/drivers/media/pci/cx18/cx18-controls.c
+++ b/drivers/media/pci/cx18/cx18-controls.c
@@ -126,7 +126,7 @@ static int cx18_s_audio_mode(struct cx2341x_handler *cxhdl, u32 val)
return 0;
}
-struct cx2341x_handler_ops cx18_cxhdl_ops = {
+const struct cx2341x_handler_ops cx18_cxhdl_ops = {
.s_audio_mode = cx18_s_audio_mode,
.s_audio_sampling_freq = cx18_s_audio_sampling_freq,
.s_video_encoding = cx18_s_video_encoding,
diff --git a/drivers/media/pci/cx18/cx18-controls.h b/drivers/media/pci/cx18/cx18-controls.h
index cb5dfc7b2054..326794887863 100644
--- a/drivers/media/pci/cx18/cx18-controls.h
+++ b/drivers/media/pci/cx18/cx18-controls.h
@@ -21,4 +21,4 @@
* 02111-1307 USA
*/
-extern struct cx2341x_handler_ops cx18_cxhdl_ops;
+extern const struct cx2341x_handler_ops cx18_cxhdl_ops;
diff --git a/drivers/media/pci/cx18/cx18-driver.h b/drivers/media/pci/cx18/cx18-driver.h
index b15beed2dc14..7e31f2a2e085 100644
--- a/drivers/media/pci/cx18/cx18-driver.h
+++ b/drivers/media/pci/cx18/cx18-driver.h
@@ -49,7 +49,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-fh.h>
#include <media/tuner.h>
-#include <media/ir-kbd-i2c.h>
+#include <media/i2c/ir-kbd-i2c.h>
#include "cx18-mailbox.h"
#include "cx18-av-core.h"
#include "cx23418.h"
diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c
index 55525af1f482..eeb741c7db1b 100644
--- a/drivers/media/pci/cx18/cx18-ioctl.c
+++ b/drivers/media/pci/cx18/cx18-ioctl.c
@@ -453,8 +453,8 @@ static int cx18_cropcap(struct file *file, void *fh,
if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- cropcap->pixelaspect.numerator = cx->is_50hz ? 59 : 10;
- cropcap->pixelaspect.denominator = cx->is_50hz ? 54 : 11;
+ cropcap->pixelaspect.numerator = cx->is_50hz ? 54 : 11;
+ cropcap->pixelaspect.denominator = cx->is_50hz ? 59 : 10;
return 0;
}
diff --git a/drivers/media/pci/cx18/cx23418.h b/drivers/media/pci/cx18/cx23418.h
index 767a8d23e3f2..67ffe65b56a3 100644
--- a/drivers/media/pci/cx18/cx23418.h
+++ b/drivers/media/pci/cx18/cx23418.h
@@ -22,7 +22,7 @@
#ifndef CX23418_H
#define CX23418_H
-#include <media/cx2341x.h>
+#include <media/drv-intf/cx2341x.h>
#define MGR_CMD_MASK 0x40000000
/* The MSB of the command code indicates that this is the completion of a
diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig
index 2e1b88ccdbf2..3435bbaa3167 100644
--- a/drivers/media/pci/cx23885/Kconfig
+++ b/drivers/media/pci/cx23885/Kconfig
@@ -10,6 +10,7 @@ config VIDEO_CX23885
select VIDEOBUF2_DMA_SG
select VIDEO_CX25840
select VIDEO_CX2341X
+ select VIDEO_CS3308
select DVB_DIB7000P if MEDIA_SUBDRV_AUTOSELECT
select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT
select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT
diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c
index 88a3afb66d10..bd333875a1f7 100644
--- a/drivers/media/pci/cx23885/cx23885-417.c
+++ b/drivers/media/pci/cx23885/cx23885-417.c
@@ -30,7 +30,7 @@
#include <linux/slab.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
-#include <media/cx2341x.h>
+#include <media/drv-intf/cx2341x.h>
#include "cx23885.h"
#include "cx23885-ioctl.h"
@@ -1138,7 +1138,7 @@ static int cx23885_initialize_codec(struct cx23885_dev *dev, int startencoder)
/* ------------------------------------------------------------------ */
-static int queue_setup(struct vb2_queue *q, const void *parg,
+static int queue_setup(struct vb2_queue *q,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c
index f384f295676e..310ee769aed4 100644
--- a/drivers/media/pci/cx23885/cx23885-cards.c
+++ b/drivers/media/pci/cx23885/cx23885-cards.c
@@ -19,7 +19,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
-#include <media/cx25840.h>
+#include <media/drv-intf/cx25840.h>
#include <linux/firmware.h>
#include <misc/altera.h>
@@ -715,6 +715,56 @@ struct cx23885_board cx23885_boards[] = {
.portb = CX23885_MPEG_DVB,
.portc = CX23885_MPEG_DVB,
},
+ [CX23885_BOARD_VIEWCAST_260E] = {
+ .name = "ViewCast 260e",
+ .porta = CX23885_ANALOG_VIDEO,
+ .force_bff = 1,
+ .input = {{
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_VIN6_CH1,
+ .amux = CX25840_AUDIO7,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN5_CH1 |
+ CX25840_SVIDEO_ON,
+ .amux = CX25840_AUDIO7,
+ }, {
+ .type = CX23885_VMUX_COMPONENT,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN6_CH2 |
+ CX25840_VIN5_CH1 |
+ CX25840_COMPONENT_ON,
+ .amux = CX25840_AUDIO7,
+ } },
+ },
+ [CX23885_BOARD_VIEWCAST_460E] = {
+ .name = "ViewCast 460e",
+ .porta = CX23885_ANALOG_VIDEO,
+ .force_bff = 1,
+ .input = {{
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_VIN4_CH1,
+ .amux = CX25840_AUDIO7,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN6_CH1 |
+ CX25840_SVIDEO_ON,
+ .amux = CX25840_AUDIO7,
+ }, {
+ .type = CX23885_VMUX_COMPONENT,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN6_CH1 |
+ CX25840_VIN5_CH2 |
+ CX25840_COMPONENT_ON,
+ .amux = CX25840_AUDIO7,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE2,
+ .vmux = CX25840_VIN6_CH1,
+ .amux = CX25840_AUDIO7,
+ } },
+ },
};
const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
@@ -1002,6 +1052,14 @@ struct cx23885_subid cx23885_subids[] = {
.subvendor = 0x0070,
.subdevice = 0xf038,
.card = CX23885_BOARD_HAUPPAUGE_HVR5525,
+ }, {
+ .subvendor = 0x1576,
+ .subdevice = 0x0260,
+ .card = CX23885_BOARD_VIEWCAST_260E,
+ }, {
+ .subvendor = 0x1576,
+ .subdevice = 0x0460,
+ .card = CX23885_BOARD_VIEWCAST_460E,
},
};
const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -1034,6 +1092,28 @@ void cx23885_card_list(struct cx23885_dev *dev)
dev->name, i, cx23885_boards[i].name);
}
+static void viewcast_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
+{
+ u32 sn;
+
+ /* The serial number record begins with tag 0x59 */
+ if (*(eeprom_data + 0x00) != 0x59) {
+ pr_info("%s() eeprom records are undefined, no serial number\n",
+ __func__);
+ return;
+ }
+
+ sn = (*(eeprom_data + 0x06) << 24) |
+ (*(eeprom_data + 0x05) << 16) |
+ (*(eeprom_data + 0x04) << 8) |
+ (*(eeprom_data + 0x03));
+
+ pr_info("%s: card '%s' sn# MM%d\n",
+ dev->name,
+ cx23885_boards[dev->board].name,
+ sn);
+}
+
static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
{
struct tveeprom tv;
@@ -1671,6 +1751,12 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
cx23885_gpio_set(dev, GPIO_8 | GPIO_9);
msleep(100);
break;
+ case CX23885_BOARD_VIEWCAST_260E:
+ case CX23885_BOARD_VIEWCAST_460E:
+ /* For documentation purposes, it's worth noting that this
+ * card does not have any GPIO's connected to subcomponents.
+ */
+ break;
}
}
@@ -1917,6 +2003,14 @@ void cx23885_card_setup(struct cx23885_dev *dev)
if (dev->i2c_bus[0].i2c_rc == 0)
hauppauge_eeprom(dev, eeprom+0xc0);
break;
+ case CX23885_BOARD_VIEWCAST_260E:
+ case CX23885_BOARD_VIEWCAST_460E:
+ dev->i2c_bus[1].i2c_client.addr = 0xa0 >> 1;
+ tveeprom_read(&dev->i2c_bus[1].i2c_client,
+ eeprom, sizeof(eeprom));
+ if (dev->i2c_bus[0].i2c_rc == 0)
+ viewcast_eeprom(dev, eeprom);
+ break;
}
switch (dev->board) {
@@ -2120,6 +2214,8 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_DVBSKY_S950:
case CX23885_BOARD_DVBSKY_S952:
case CX23885_BOARD_DVBSKY_T982:
+ case CX23885_BOARD_VIEWCAST_260E:
+ case CX23885_BOARD_VIEWCAST_460E:
dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[2].i2c_adap,
"cx25840", 0x88 >> 1, NULL);
@@ -2130,6 +2226,24 @@ void cx23885_card_setup(struct cx23885_dev *dev)
break;
}
+ switch (dev->board) {
+ case CX23885_BOARD_VIEWCAST_260E:
+ v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_bus[0].i2c_adap,
+ "cs3308", 0x82 >> 1, NULL);
+ break;
+ case CX23885_BOARD_VIEWCAST_460E:
+ /* This cs3308 controls the audio from the breakout cable */
+ v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_bus[0].i2c_adap,
+ "cs3308", 0x80 >> 1, NULL);
+ /* This cs3308 controls the audio from the onboard header */
+ v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_bus[0].i2c_adap,
+ "cs3308", 0x82 >> 1, NULL);
+ break;
+ }
+
/* AUX-PLL 27MHz CLK */
switch (dev->board) {
case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c
index e8f847226a19..813c217b5e1a 100644
--- a/drivers/media/pci/cx23885/cx23885-core.c
+++ b/drivers/media/pci/cx23885/cx23885-core.c
@@ -427,7 +427,7 @@ static void cx23885_wakeup(struct cx23885_tsport *port,
buf = list_entry(q->active.next,
struct cx23885_buffer, queue);
- v4l2_get_timestamp(&buf->vb.timestamp);
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
buf->vb.sequence = q->count++;
dprintk(1, "[%p/%d] wakeup reg=%d buf=%d\n", buf,
buf->vb.vb2_buf.index,
@@ -968,6 +968,16 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
call_all(dev, core, s_power, 0);
cx23885_ir_init(dev);
+ if (dev->board == CX23885_BOARD_VIEWCAST_460E) {
+ /*
+ * GPIOs 9/8 are input detection bits for the breakout video
+ * (gpio 8) and audio (gpio 9) cables. When they're attached,
+ * this gpios are pulled high. Make sure these GPIOs are marked
+ * as inputs.
+ */
+ cx23885_gpio_enable(dev, 0x300, 0);
+ }
+
if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) {
if (cx23885_video_register(dev) < 0) {
printk(KERN_ERR "%s() Failed to register analog "
diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c
index c4307ad8594c..80319bb73d94 100644
--- a/drivers/media/pci/cx23885/cx23885-dvb.c
+++ b/drivers/media/pci/cx23885/cx23885-dvb.c
@@ -92,7 +92,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
/* ------------------------------------------------------------------ */
-static int queue_setup(struct vb2_queue *q, const void *parg,
+static int queue_setup(struct vb2_queue *q,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
@@ -2168,11 +2168,12 @@ static int dvb_register(struct cx23885_tsport *port)
}
port->i2c_client_tuner = client_tuner;
break;
- case CX23885_BOARD_HAUPPAUGE_HVR5525:
- switch (port->nr) {
+ case CX23885_BOARD_HAUPPAUGE_HVR5525: {
struct m88rs6000t_config m88rs6000t_config;
struct a8293_platform_data a8293_pdata = {};
+ switch (port->nr) {
+
/* port b - satellite */
case 1:
/* attach frontend */
@@ -2267,6 +2268,7 @@ static int dvb_register(struct cx23885_tsport *port)
break;
}
break;
+ }
default:
printk(KERN_INFO "%s: The frontend of your DVB/ATSC card "
" isn't supported yet\n",
diff --git a/drivers/media/pci/cx23885/cx23885-i2c.c b/drivers/media/pci/cx23885/cx23885-i2c.c
index 1135ea3f6ce5..ae061b358591 100644
--- a/drivers/media/pci/cx23885/cx23885-i2c.c
+++ b/drivers/media/pci/cx23885/cx23885-i2c.c
@@ -279,6 +279,8 @@ static char *i2c_devs[128] = {
[0x10 >> 1] = "tda10048",
[0x12 >> 1] = "dib7000pc",
[0x1c >> 1] = "lgdt3303",
+ [0x80 >> 1] = "cs3308",
+ [0x82 >> 1] = "cs3308",
[0x86 >> 1] = "tda9887",
[0x32 >> 1] = "cx24227",
[0x88 >> 1] = "cx25837",
diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c
index 088799c3b49b..64328d08ac2f 100644
--- a/drivers/media/pci/cx23885/cx23885-input.c
+++ b/drivers/media/pci/cx23885/cx23885-input.c
@@ -268,7 +268,7 @@ int cx23885_input_init(struct cx23885_dev *dev)
struct rc_dev *rc;
char *rc_map;
enum rc_driver_type driver_type;
- unsigned long allowed_protos;
+ u64 allowed_protos;
int ret;
diff --git a/drivers/media/pci/cx23885/cx23885-vbi.c b/drivers/media/pci/cx23885/cx23885-vbi.c
index cf3cb1324c55..39750ebcc04c 100644
--- a/drivers/media/pci/cx23885/cx23885-vbi.c
+++ b/drivers/media/pci/cx23885/cx23885-vbi.c
@@ -83,7 +83,7 @@ int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status)
if (status & VID_BC_MSK_VBI_RISCI1) {
dprintk(1, "%s() VID_BC_MSK_VBI_RISCI1\n", __func__);
spin_lock(&dev->slock);
- count = cx_read(VID_A_GPCNT);
+ count = cx_read(VBI_A_GPCNT);
cx23885_video_wakeup(dev, &dev->vbiq, count);
spin_unlock(&dev->slock);
handled++;
@@ -103,7 +103,6 @@ static int cx23885_start_vbi_dma(struct cx23885_dev *dev,
VBI_LINE_LENGTH, buf->risc.dma);
/* reset counter */
- cx_write(VID_A_GPCNT_CTL, 3);
cx_write(VID_A_VBI_CTRL, 3);
cx_write(VBI_A_GPCNT_CTL, 3);
q->count = 0;
@@ -121,7 +120,7 @@ static int cx23885_start_vbi_dma(struct cx23885_dev *dev,
/* ------------------------------------------------------------------ */
-static int queue_setup(struct vb2_queue *q, const void *parg,
+static int queue_setup(struct vb2_queue *q,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c
index 71a80e2b842c..e1d7d0847167 100644
--- a/drivers/media/pci/cx23885/cx23885-video.c
+++ b/drivers/media/pci/cx23885/cx23885-video.c
@@ -35,7 +35,7 @@
#include "cx23885-ioctl.h"
#include "tuner-xc2028.h"
-#include <media/cx25840.h>
+#include <media/drv-intf/cx25840.h>
MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards");
MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
@@ -105,7 +105,7 @@ void cx23885_video_wakeup(struct cx23885_dev *dev,
struct cx23885_buffer, queue);
buf->vb.sequence = q->count++;
- v4l2_get_timestamp(&buf->vb.timestamp);
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf,
buf->vb.vb2_buf.index, count, q->count);
list_del(&buf->queue);
@@ -114,11 +114,19 @@ void cx23885_video_wakeup(struct cx23885_dev *dev,
int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm)
{
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .format.code = MEDIA_BUS_FMT_FIXED,
+ };
+
dprintk(1, "%s(norm = 0x%08x) name: [%s]\n",
__func__,
(unsigned int)norm,
v4l2_norm_to_name(norm));
+ if (dev->tvnorm == norm)
+ return 0;
+
if (dev->tvnorm != norm) {
if (vb2_is_busy(&dev->vb2_vidq) || vb2_is_busy(&dev->vb2_vbiq) ||
vb2_is_busy(&dev->vb2_mpegq))
@@ -126,9 +134,17 @@ int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm)
}
dev->tvnorm = norm;
+ dev->width = 720;
+ dev->height = norm_maxh(norm);
+ dev->field = V4L2_FIELD_INTERLACED;
call_all(dev, video, s_std, norm);
+ format.format.width = dev->width;
+ format.format.height = dev->height;
+ format.format.field = dev->field;
+ call_all(dev, pad, set_fmt, NULL, &format);
+
return 0;
}
@@ -247,7 +263,9 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
(dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111) ||
(dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) ||
(dev->board == CX23885_BOARD_MYGICA_X8507) ||
- (dev->board == CX23885_BOARD_AVERMEDIA_HC81R)) {
+ (dev->board == CX23885_BOARD_AVERMEDIA_HC81R) ||
+ (dev->board == CX23885_BOARD_VIEWCAST_260E) ||
+ (dev->board == CX23885_BOARD_VIEWCAST_460E)) {
/* Configure audio routing */
v4l2_subdev_call(dev->sd_cx25840, audio, s_routing,
INPUT(input)->amux, 0, 0);
@@ -315,7 +333,7 @@ static int cx23885_start_video_dma(struct cx23885_dev *dev,
return 0;
}
-static int queue_setup(struct vb2_queue *q, const void *parg,
+static int queue_setup(struct vb2_queue *q,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
@@ -545,7 +563,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
return -EINVAL;
field = f->fmt.pix.field;
- maxw = norm_maxw(dev->tvnorm);
+ maxw = 720;
maxh = norm_maxh(dev->tvnorm);
if (V4L2_FIELD_ANY == field) {
@@ -648,6 +666,26 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
+static int vidioc_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cc)
+{
+ struct cx23885_dev *dev = video_drvdata(file);
+ bool is_50hz = dev->tvnorm & V4L2_STD_625_50;
+
+ if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ cc->bounds.left = 0;
+ cc->bounds.top = 0;
+ cc->bounds.width = 720;
+ cc->bounds.height = norm_maxh(dev->tvnorm);
+ cc->defrect = cc->bounds;
+ cc->pixelaspect.numerator = is_50hz ? 54 : 11;
+ cc->pixelaspect.denominator = is_50hz ? 59 : 10;
+
+ return 0;
+}
+
static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
{
struct cx23885_dev *dev = video_drvdata(file);
@@ -1082,6 +1120,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_cropcap = vidioc_cropcap,
.vidioc_s_std = vidioc_s_std,
.vidioc_g_std = vidioc_g_std,
.vidioc_enum_input = vidioc_enum_input,
diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h
index c5ba0833f47a..b1a5409408c7 100644
--- a/drivers/media/pci/cx23885/cx23885.h
+++ b/drivers/media/pci/cx23885/cx23885.h
@@ -30,7 +30,7 @@
#include <media/rc-core.h>
#include "cx23885-reg.h"
-#include "media/cx2341x.h"
+#include "media/drv-intf/cx2341x.h"
#include <linux/mutex.h>
@@ -101,6 +101,8 @@
#define CX23885_BOARD_DVBSKY_T982 51
#define CX23885_BOARD_HAUPPAUGE_HVR5525 52
#define CX23885_BOARD_HAUPPAUGE_STARBURST 53
+#define CX23885_BOARD_VIEWCAST_260E 54
+#define CX23885_BOARD_VIEWCAST_460E 55
#define GPIO_0 0x00000001
#define GPIO_1 0x00000002
@@ -627,11 +629,6 @@ extern int cx23885_risc_databuffer(struct pci_dev *pci,
/* ----------------------------------------------------------- */
/* tv norms */
-static inline unsigned int norm_maxw(v4l2_std_id norm)
-{
- return (norm & V4L2_STD_525_60) ? 720 : 768;
-}
-
static inline unsigned int norm_maxh(v4l2_std_id norm)
{
return (norm & V4L2_STD_525_60) ? 480 : 576;
diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c
index 26e3e296d615..c48bba9daf1f 100644
--- a/drivers/media/pci/cx25821/cx25821-video.c
+++ b/drivers/media/pci/cx25821/cx25821-video.c
@@ -130,7 +130,7 @@ int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status)
buf = list_entry(dmaq->active.next,
struct cx25821_buffer, queue);
- v4l2_get_timestamp(&buf->vb.timestamp);
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
buf->vb.sequence = dmaq->count++;
list_del(&buf->queue);
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
@@ -141,20 +141,20 @@ int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status)
return handled;
}
-static int cx25821_queue_setup(struct vb2_queue *q, const void *parg,
+static int cx25821_queue_setup(struct vb2_queue *q,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct cx25821_channel *chan = q->drv_priv;
unsigned size = (chan->fmt->depth * chan->width * chan->height) >> 3;
- if (fmt && fmt->fmt.pix.sizeimage < size)
- return -EINVAL;
+ alloc_ctxs[0] = chan->dev->alloc_ctx;
+
+ if (*num_planes)
+ return sizes[0] < size ? -EINVAL : 0;
*num_planes = 1;
- sizes[0] = fmt ? fmt->fmt.pix.sizeimage : size;
- alloc_ctxs[0] = chan->dev->alloc_ctx;
+ sizes[0] = size;
return 0;
}
diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c
index 1b5268f9bb24..e158a1da1d41 100644
--- a/drivers/media/pci/cx88/cx88-alsa.c
+++ b/drivers/media/pci/cx88/cx88-alsa.c
@@ -40,7 +40,7 @@
#include <sound/control.h>
#include <sound/initval.h>
#include <sound/tlv.h>
-#include <media/wm8775.h>
+#include <media/i2c/wm8775.h>
#include "cx88.h"
#include "cx88-reg.h"
diff --git a/drivers/media/pci/cx88/cx88-blackbird.c b/drivers/media/pci/cx88/cx88-blackbird.c
index 8b889135be8a..3233d45d1e5b 100644
--- a/drivers/media/pci/cx88/cx88-blackbird.c
+++ b/drivers/media/pci/cx88/cx88-blackbird.c
@@ -36,7 +36,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
-#include <media/cx2341x.h>
+#include <media/drv-intf/cx2341x.h>
#include "cx88.h"
@@ -637,7 +637,7 @@ static int blackbird_stop_codec(struct cx8802_dev *dev)
/* ------------------------------------------------------------------ */
-static int queue_setup(struct vb2_queue *q, const void *parg,
+static int queue_setup(struct vb2_queue *q,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c
index 9a43c7826b60..46fe8c1eb9d4 100644
--- a/drivers/media/pci/cx88/cx88-core.c
+++ b/drivers/media/pci/cx88/cx88-core.c
@@ -518,7 +518,7 @@ void cx88_wakeup(struct cx88_core *core,
buf = list_entry(q->active.next,
struct cx88_buffer, list);
- v4l2_get_timestamp(&buf->vb.timestamp);
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
buf->vb.field = core->field;
buf->vb.sequence = q->count++;
list_del(&buf->list);
diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c
index f04835073844..afb20756d7a5 100644
--- a/drivers/media/pci/cx88/cx88-dvb.c
+++ b/drivers/media/pci/cx88/cx88-dvb.c
@@ -82,7 +82,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
/* ------------------------------------------------------------------ */
-static int queue_setup(struct vb2_queue *q, const void *parg,
+static int queue_setup(struct vb2_queue *q,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
diff --git a/drivers/media/pci/cx88/cx88-vbi.c b/drivers/media/pci/cx88/cx88-vbi.c
index 007a5eee8e5e..ccc646d819f2 100644
--- a/drivers/media/pci/cx88/cx88-vbi.c
+++ b/drivers/media/pci/cx88/cx88-vbi.c
@@ -107,7 +107,7 @@ int cx8800_restart_vbi_queue(struct cx8800_dev *dev,
/* ------------------------------------------------------------------ */
-static int queue_setup(struct vb2_queue *q, const void *parg,
+static int queue_setup(struct vb2_queue *q,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c
index aef9acf351f6..5f331df65fb9 100644
--- a/drivers/media/pci/cx88/cx88-video.c
+++ b/drivers/media/pci/cx88/cx88-video.c
@@ -41,7 +41,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
-#include <media/wm8775.h>
+#include <media/i2c/wm8775.h>
MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
@@ -429,7 +429,7 @@ static int restart_video_queue(struct cx8800_dev *dev,
/* ------------------------------------------------------------------ */
-static int queue_setup(struct vb2_queue *q, const void *parg,
+static int queue_setup(struct vb2_queue *q,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
diff --git a/drivers/media/pci/cx88/cx88.h b/drivers/media/pci/cx88/cx88.h
index 2996eb3ea1fc..78f817ee7e41 100644
--- a/drivers/media/pci/cx88/cx88.h
+++ b/drivers/media/pci/cx88/cx88.h
@@ -30,10 +30,10 @@
#include <media/tuner.h>
#include <media/tveeprom.h>
#include <media/videobuf2-dma-sg.h>
-#include <media/cx2341x.h>
+#include <media/drv-intf/cx2341x.h>
#include <media/videobuf2-dvb.h>
-#include <media/ir-kbd-i2c.h>
-#include <media/wm8775.h>
+#include <media/i2c/ir-kbd-i2c.h>
+#include <media/i2c/wm8775.h>
#include "cx88-reg.h"
#include "tuner-xc2028.h"
diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c
index 0ac2dd35fe50..9d5b314142f1 100644
--- a/drivers/media/pci/ddbridge/ddbridge-core.c
+++ b/drivers/media/pci/ddbridge/ddbridge-core.c
@@ -81,13 +81,13 @@ static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr,
static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd)
{
struct ddb *dev = i2c->dev;
- int stat;
+ long stat;
u32 val;
i2c->done = 0;
ddbwritel((adr << 9) | cmd, i2c->regs + I2C_COMMAND);
stat = wait_event_timeout(i2c->wq, i2c->done == 1, HZ);
- if (stat <= 0) {
+ if (stat == 0) {
printk(KERN_ERR "I2C timeout\n");
{ /* MSI debugging*/
u32 istat = ddbreadl(INTERRUPT_STATUS);
@@ -1065,7 +1065,7 @@ static int ddb_ci_attach(struct ddb_port *port)
port->en, 0, 1);
ret = dvb_register_device(&port->output->adap, &port->output->dev,
&dvbdev_ci, (void *) port->output,
- DVB_DEVICE_SEC);
+ DVB_DEVICE_SEC, 0);
return ret;
}
diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c
index 88915fb87e80..5dd504741b12 100644
--- a/drivers/media/pci/dm1105/dm1105.c
+++ b/drivers/media/pci/dm1105/dm1105.c
@@ -1206,7 +1206,6 @@ static void dm1105_remove(struct pci_dev *pdev)
i2c_del_adapter(&dev->i2c_adap);
dm1105_hw_exit(dev);
- synchronize_irq(pdev->irq);
free_irq(pdev->irq, dev);
pci_iounmap(pdev, dev->io_mem);
pci_release_regions(pdev);
diff --git a/drivers/media/pci/dt3155/dt3155.c b/drivers/media/pci/dt3155/dt3155.c
index d84abde5ea29..568c0c8fb2dc 100644
--- a/drivers/media/pci/dt3155/dt3155.c
+++ b/drivers/media/pci/dt3155/dt3155.c
@@ -131,22 +131,21 @@ static int wait_i2c_reg(void __iomem *addr)
}
static int
-dt3155_queue_setup(struct vb2_queue *vq, const void *parg,
+dt3155_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct dt3155_priv *pd = vb2_get_drv_priv(vq);
unsigned size = pd->width * pd->height;
if (vq->num_buffers + *nbuffers < 2)
*nbuffers = 2 - vq->num_buffers;
- if (fmt && fmt->fmt.pix.sizeimage < size)
- return -EINVAL;
- *num_planes = 1;
- sizes[0] = fmt ? fmt->fmt.pix.sizeimage : size;
alloc_ctxs[0] = pd->alloc_ctx;
+ if (*num_planes)
+ return sizes[0] < size ? -EINVAL : 0;
+ *num_planes = 1;
+ sizes[0] = size;
return 0;
}
@@ -271,7 +270,7 @@ static irqreturn_t dt3155_irq_handler_even(int irq, void *dev_id)
spin_lock(&ipd->lock);
if (ipd->curr_buf && !list_empty(&ipd->dmaq)) {
- v4l2_get_timestamp(&ipd->curr_buf->timestamp);
+ ipd->curr_buf->vb2_buf.timestamp = ktime_get_ns();
ipd->curr_buf->sequence = ipd->sequence++;
ipd->curr_buf->field = V4L2_FIELD_NONE;
vb2_buffer_done(&ipd->curr_buf->vb2_buf, VB2_BUF_STATE_DONE);
diff --git a/drivers/media/pci/ivtv/ivtv-cards.c b/drivers/media/pci/ivtv/ivtv-cards.c
index 145e4749a69d..410d97bdf541 100644
--- a/drivers/media/pci/ivtv/ivtv-cards.c
+++ b/drivers/media/pci/ivtv/ivtv-cards.c
@@ -22,12 +22,12 @@
#include "ivtv-cards.h"
#include "ivtv-i2c.h"
-#include <media/msp3400.h>
-#include <media/m52790.h>
-#include <media/wm8775.h>
-#include <media/cs53l32a.h>
-#include <media/cx25840.h>
-#include <media/upd64031a.h>
+#include <media/drv-intf/msp3400.h>
+#include <media/i2c/m52790.h>
+#include <media/i2c/wm8775.h>
+#include <media/i2c/cs53l32a.h>
+#include <media/drv-intf/cx25840.h>
+#include <media/i2c/upd64031a.h>
#define MSP_TUNER MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \
MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER)
diff --git a/drivers/media/pci/ivtv/ivtv-controls.c b/drivers/media/pci/ivtv/ivtv-controls.c
index 8a55ccb8f0c9..9666ca01549c 100644
--- a/drivers/media/pci/ivtv/ivtv-controls.c
+++ b/drivers/media/pci/ivtv/ivtv-controls.c
@@ -96,7 +96,7 @@ static int ivtv_s_audio_mode(struct cx2341x_handler *cxhdl, u32 val)
return 0;
}
-struct cx2341x_handler_ops ivtv_cxhdl_ops = {
+const struct cx2341x_handler_ops ivtv_cxhdl_ops = {
.s_audio_mode = ivtv_s_audio_mode,
.s_audio_sampling_freq = ivtv_s_audio_sampling_freq,
.s_video_encoding = ivtv_s_video_encoding,
diff --git a/drivers/media/pci/ivtv/ivtv-controls.h b/drivers/media/pci/ivtv/ivtv-controls.h
index 3999e6358312..ea397ba837e3 100644
--- a/drivers/media/pci/ivtv/ivtv-controls.h
+++ b/drivers/media/pci/ivtv/ivtv-controls.h
@@ -21,7 +21,7 @@
#ifndef IVTV_CONTROLS_H
#define IVTV_CONTROLS_H
-extern struct cx2341x_handler_ops ivtv_cxhdl_ops;
+extern const struct cx2341x_handler_ops ivtv_cxhdl_ops;
extern const struct v4l2_ctrl_ops ivtv_hdl_out_ops;
int ivtv_g_pts_frame(struct ivtv *itv, s64 *pts, s64 *frame);
diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c
index 8616fa8193bc..374033a5bdaf 100644
--- a/drivers/media/pci/ivtv/ivtv-driver.c
+++ b/drivers/media/pci/ivtv/ivtv-driver.c
@@ -57,7 +57,7 @@
#include "ivtv-gpio.h"
#include <linux/dma-mapping.h>
#include <media/tveeprom.h>
-#include <media/saa7115.h>
+#include <media/i2c/saa7115.h>
#include "tuner-xc2028.h"
/* If you have already X v4l cards, then set this to X. This way
@@ -805,11 +805,11 @@ static void ivtv_init_struct2(struct ivtv *itv)
{
int i;
- for (i = 0; i < IVTV_CARD_MAX_VIDEO_INPUTS - 1; i++)
+ for (i = 0; i < IVTV_CARD_MAX_VIDEO_INPUTS; i++)
if (itv->card->video_inputs[i].video_type == 0)
break;
itv->nof_inputs = i;
- for (i = 0; i < IVTV_CARD_MAX_AUDIO_INPUTS - 1; i++)
+ for (i = 0; i < IVTV_CARD_MAX_AUDIO_INPUTS; i++)
if (itv->card->audio_inputs[i].audio_type == 0)
break;
itv->nof_audio_inputs = i;
@@ -826,7 +826,7 @@ static void ivtv_init_struct2(struct ivtv *itv)
IVTV_CARD_INPUT_VID_TUNER)
break;
}
- if (i == itv->nof_inputs)
+ if (i >= itv->nof_inputs)
i = 0;
itv->active_input = i;
itv->audio_input = itv->card->video_inputs[i].audio_index;
diff --git a/drivers/media/pci/ivtv/ivtv-driver.h b/drivers/media/pci/ivtv/ivtv-driver.h
index ee0ef6e48c7d..6c08dae67a73 100644
--- a/drivers/media/pci/ivtv/ivtv-driver.h
+++ b/drivers/media/pci/ivtv/ivtv-driver.h
@@ -64,8 +64,8 @@
#include <media/v4l2-device.h>
#include <media/v4l2-fh.h>
#include <media/tuner.h>
-#include <media/cx2341x.h>
-#include <media/ir-kbd-i2c.h>
+#include <media/drv-intf/cx2341x.h>
+#include <media/i2c/ir-kbd-i2c.h>
#include <linux/ivtv.h>
diff --git a/drivers/media/pci/ivtv/ivtv-fileops.c b/drivers/media/pci/ivtv/ivtv-fileops.c
index 605d280d8a5f..c9bd018e53de 100644
--- a/drivers/media/pci/ivtv/ivtv-fileops.c
+++ b/drivers/media/pci/ivtv/ivtv-fileops.c
@@ -34,7 +34,7 @@
#include "ivtv-cards.h"
#include "ivtv-firmware.h"
#include <media/v4l2-event.h>
-#include <media/saa7115.h>
+#include <media/i2c/saa7115.h>
/* This function tries to claim the stream for a specific file descriptor.
If no one else is using this stream then the stream is claimed and
diff --git a/drivers/media/pci/ivtv/ivtv-firmware.c b/drivers/media/pci/ivtv/ivtv-firmware.c
index 4b0e758a7bce..5b3095f65dce 100644
--- a/drivers/media/pci/ivtv/ivtv-firmware.c
+++ b/drivers/media/pci/ivtv/ivtv-firmware.c
@@ -26,7 +26,7 @@
#include "ivtv-ioctl.h"
#include "ivtv-cards.h"
#include <linux/firmware.h>
-#include <media/saa7127.h>
+#include <media/i2c/saa7127.h>
#define IVTV_MASK_SPU_ENABLE 0xFFFFFFFE
#define IVTV_MASK_VPU_ENABLE15 0xFFFFFFF6
diff --git a/drivers/media/pci/ivtv/ivtv-i2c.c b/drivers/media/pci/ivtv/ivtv-i2c.c
index 1a41ba5c7d30..bccbf2d18e30 100644
--- a/drivers/media/pci/ivtv/ivtv-i2c.c
+++ b/drivers/media/pci/ivtv/ivtv-i2c.c
@@ -63,7 +63,7 @@
#include "ivtv-cards.h"
#include "ivtv-gpio.h"
#include "ivtv-i2c.h"
-#include <media/cx25840.h>
+#include <media/drv-intf/cx25840.h>
/* i2c implementation for cx23415/6 chip, ivtv project.
* Author: Kevin Thayer (nufan_wfk at yahoo.com)
diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c
index 9a21c17fc376..2dc4b20f3ac0 100644
--- a/drivers/media/pci/ivtv/ivtv-ioctl.c
+++ b/drivers/media/pci/ivtv/ivtv-ioctl.c
@@ -32,7 +32,7 @@
#include "ivtv-gpio.h"
#include "ivtv-controls.h"
#include "ivtv-cards.h"
-#include <media/saa7127.h>
+#include <media/i2c/saa7127.h>
#include <media/tveeprom.h>
#include <media/v4l2-event.h>
#include <linux/dvb/audio.h>
@@ -831,11 +831,11 @@ static int ivtv_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropca
struct ivtv *itv = id->itv;
if (cropcap->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- cropcap->pixelaspect.numerator = itv->is_50hz ? 59 : 10;
- cropcap->pixelaspect.denominator = itv->is_50hz ? 54 : 11;
+ cropcap->pixelaspect.numerator = itv->is_50hz ? 54 : 11;
+ cropcap->pixelaspect.denominator = itv->is_50hz ? 59 : 10;
} else if (cropcap->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10;
- cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11;
+ cropcap->pixelaspect.numerator = itv->is_out_50hz ? 54 : 11;
+ cropcap->pixelaspect.denominator = itv->is_out_50hz ? 59 : 10;
} else {
return -EINVAL;
}
diff --git a/drivers/media/pci/ivtv/ivtv-routing.c b/drivers/media/pci/ivtv/ivtv-routing.c
index 8898c569a1c9..0c168f238904 100644
--- a/drivers/media/pci/ivtv/ivtv-routing.c
+++ b/drivers/media/pci/ivtv/ivtv-routing.c
@@ -24,10 +24,10 @@
#include "ivtv-gpio.h"
#include "ivtv-routing.h"
-#include <media/msp3400.h>
-#include <media/m52790.h>
-#include <media/upd64031a.h>
-#include <media/upd64083.h>
+#include <media/drv-intf/msp3400.h>
+#include <media/i2c/m52790.h>
+#include <media/i2c/upd64031a.h>
+#include <media/i2c/upd64083.h>
/* Selects the audio input and output according to the current
settings. */
diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c
index 3fdbd81b5580..525ebfefeee8 100644
--- a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c
+++ b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c
@@ -277,7 +277,6 @@ static irqreturn_t netup_unidvb_isr(int irq, void *dev_id)
}
static int netup_unidvb_queue_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *nbuffers,
unsigned int *nplanes,
unsigned int sizes[],
@@ -388,7 +387,7 @@ static int netup_unidvb_dvb_init(struct netup_unidvb_dev *ndev,
vb2_dvb_alloc_frontend(
&ndev->frontends[num], 3) == NULL) {
dev_dbg(&ndev->pci_dev->dev,
- "%s(): unable to to alllocate vb2_dvb_frontend\n",
+ "%s(): unable to allocate vb2_dvb_frontend\n",
__func__);
return -ENOMEM;
}
@@ -580,7 +579,7 @@ static void netup_unidvb_dma_worker(struct work_struct *work)
dev_dbg(&ndev->pci_dev->dev,
"%s(): buffer %p done, size %d\n",
__func__, buf, buf->size);
- v4l2_get_timestamp(&buf->vb.timestamp);
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size);
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
}
diff --git a/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c
index 1b92d836a564..4e924e2d1638 100644
--- a/drivers/media/pci/ngene/ngene-core.c
+++ b/drivers/media/pci/ngene/ngene-core.c
@@ -1513,7 +1513,7 @@ static int init_channel(struct ngene_channel *chan)
set_transfer(&chan->dev->channel[2], 1);
dvb_register_device(adapter, &chan->ci_dev,
&ngene_dvbdev_ci, (void *) chan,
- DVB_DEVICE_SEC);
+ DVB_DEVICE_SEC, 0);
if (!chan->ci_dev)
goto err;
}
diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c
index f720cea80e28..e227b02cc122 100644
--- a/drivers/media/pci/saa7134/saa7134-core.c
+++ b/drivers/media/pci/saa7134/saa7134-core.c
@@ -309,7 +309,7 @@ void saa7134_buffer_finish(struct saa7134_dev *dev,
core_dbg("buffer_finish %p\n", q->curr);
/* finish current buffer */
- v4l2_get_timestamp(&q->curr->vb2.timestamp);
+ q->curr->vb2.vb2_buf.timestamp = ktime_get_ns();
q->curr->vb2.sequence = q->seq_nr++;
vb2_buffer_done(&q->curr->vb2.vb2_buf, state);
q->curr = NULL;
diff --git a/drivers/media/pci/saa7134/saa7134-ts.c b/drivers/media/pci/saa7134/saa7134-ts.c
index 7fb5ee7e20ac..0584a2adbe99 100644
--- a/drivers/media/pci/saa7134/saa7134-ts.c
+++ b/drivers/media/pci/saa7134/saa7134-ts.c
@@ -116,7 +116,7 @@ int saa7134_ts_buffer_prepare(struct vb2_buffer *vb2)
}
EXPORT_SYMBOL_GPL(saa7134_ts_buffer_prepare);
-int saa7134_ts_queue_setup(struct vb2_queue *q, const void *parg,
+int saa7134_ts_queue_setup(struct vb2_queue *q,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
diff --git a/drivers/media/pci/saa7134/saa7134-vbi.c b/drivers/media/pci/saa7134/saa7134-vbi.c
index 6271b0eb0265..e76da37c4a8a 100644
--- a/drivers/media/pci/saa7134/saa7134-vbi.c
+++ b/drivers/media/pci/saa7134/saa7134-vbi.c
@@ -138,7 +138,7 @@ static int buffer_prepare(struct vb2_buffer *vb2)
saa7134_buffer_startpage(buf));
}
-static int queue_setup(struct vb2_queue *q, const void *parg,
+static int queue_setup(struct vb2_queue *q,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c
index 518086c7aed5..a63c1366a64e 100644
--- a/drivers/media/pci/saa7134/saa7134-video.c
+++ b/drivers/media/pci/saa7134/saa7134-video.c
@@ -32,7 +32,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
-#include <media/saa6588.h>
+#include <media/i2c/saa6588.h>
/* ------------------------------------------------------------------ */
@@ -904,7 +904,7 @@ static int buffer_prepare(struct vb2_buffer *vb2)
saa7134_buffer_startpage(buf));
}
-static int queue_setup(struct vb2_queue *q, const void *parg,
+static int queue_setup(struct vb2_queue *q,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h
index 6b6d234f5cab..5938bc781999 100644
--- a/drivers/media/pci/saa7134/saa7134.h
+++ b/drivers/media/pci/saa7134/saa7134.h
@@ -42,7 +42,7 @@
#include <media/v4l2-ctrls.h>
#include <media/tuner.h>
#include <media/rc-core.h>
-#include <media/ir-kbd-i2c.h>
+#include <media/i2c/ir-kbd-i2c.h>
#include <media/videobuf2-dma-sg.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -820,7 +820,7 @@ void saa7134_video_fini(struct saa7134_dev *dev);
int saa7134_ts_buffer_init(struct vb2_buffer *vb2);
int saa7134_ts_buffer_prepare(struct vb2_buffer *vb2);
-int saa7134_ts_queue_setup(struct vb2_queue *q, const void *parg,
+int saa7134_ts_queue_setup(struct vb2_queue *q,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[]);
int saa7134_ts_start_streaming(struct vb2_queue *vq, unsigned int count);
diff --git a/drivers/media/pci/saa7146/hexium_gemini.c b/drivers/media/pci/saa7146/hexium_gemini.c
index 03cbcd2095c6..c889ec9f8a5a 100644
--- a/drivers/media/pci/saa7146/hexium_gemini.c
+++ b/drivers/media/pci/saa7146/hexium_gemini.c
@@ -25,7 +25,7 @@
#define DEBUG_VARIABLE debug
-#include <media/saa7146_vv.h>
+#include <media/drv-intf/saa7146_vv.h>
#include <linux/module.h>
static int debug;
diff --git a/drivers/media/pci/saa7146/hexium_orion.c b/drivers/media/pci/saa7146/hexium_orion.c
index 15f0d66ff78a..c306a92e8909 100644
--- a/drivers/media/pci/saa7146/hexium_orion.c
+++ b/drivers/media/pci/saa7146/hexium_orion.c
@@ -25,7 +25,7 @@
#define DEBUG_VARIABLE debug
-#include <media/saa7146_vv.h>
+#include <media/drv-intf/saa7146_vv.h>
#include <linux/module.h>
static int debug;
diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c
index 0ca1e07ae783..504d78807639 100644
--- a/drivers/media/pci/saa7146/mxb.c
+++ b/drivers/media/pci/saa7146/mxb.c
@@ -25,10 +25,10 @@
#define DEBUG_VARIABLE debug
-#include <media/saa7146_vv.h>
+#include <media/drv-intf/saa7146_vv.h>
#include <media/tuner.h>
#include <media/v4l2-common.h>
-#include <media/saa7115.h>
+#include <media/i2c/saa7115.h>
#include <linux/module.h>
#include "tea6415c.h"
diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c
index 4432fd69b7cb..67a14c41c227 100644
--- a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c
+++ b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c
@@ -531,8 +531,7 @@ static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc,
if (!ret) {
vbuf->sequence = solo_enc->sequence++;
- vbuf->timestamp.tv_sec = vop_sec(vh);
- vbuf->timestamp.tv_usec = vop_usec(vh);
+ vb->timestamp = ktime_get_ns();
/* Check for motion flags */
if (solo_is_motion_on(solo_enc) && enc_buf->motion) {
@@ -663,7 +662,6 @@ static int solo_ring_thread(void *data)
}
static int solo_enc_queue_setup(struct vb2_queue *q,
- const void *parg,
unsigned int *num_buffers,
unsigned int *num_planes, unsigned int sizes[],
void *alloc_ctxs[])
diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2.c b/drivers/media/pci/solo6x10/solo6x10-v4l2.c
index f7ce493b1fee..721ff5320de7 100644
--- a/drivers/media/pci/solo6x10/solo6x10-v4l2.c
+++ b/drivers/media/pci/solo6x10/solo6x10-v4l2.c
@@ -225,7 +225,7 @@ finish_buf:
vb2_set_plane_payload(vb, 0,
solo_vlines(solo_dev) * solo_bytesperline(solo_dev));
vbuf->sequence = solo_dev->sequence++;
- v4l2_get_timestamp(&vbuf->timestamp);
+ vb->timestamp = ktime_get_ns();
}
vb2_buffer_done(vb, error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
@@ -313,7 +313,7 @@ static void solo_stop_thread(struct solo_dev *solo_dev)
solo_dev->kthread = NULL;
}
-static int solo_queue_setup(struct vb2_queue *q, const void *parg,
+static int solo_queue_setup(struct vb2_queue *q,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c
index 6367b455a7e7..753411cbbc9a 100644
--- a/drivers/media/pci/sta2x11/sta2x11_vip.c
+++ b/drivers/media/pci/sta2x11/sta2x11_vip.c
@@ -265,7 +265,7 @@ static void vip_active_buf_next(struct sta2x11_vip *vip)
/* Videobuf2 Operations */
-static int queue_setup(struct vb2_queue *vq, const void *parg,
+static int queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
@@ -817,7 +817,7 @@ static irqreturn_t vip_irq(int irq, struct sta2x11_vip *vip)
/* Disable acquisition */
reg_write(vip, DVP_CTL, reg_read(vip, DVP_CTL) & ~DVP_CTL_ENA);
/* Remove the active buffer from the list */
- v4l2_get_timestamp(&vip->active->vb.timestamp);
+ vip->active->vb.vb2_buf.timestamp = ktime_get_ns();
vip->active->vb.sequence = vip->sequence++;
vb2_buffer_done(&vip->active->vb.vb2_buf, VB2_BUF_STATE_DONE);
}
diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c
index f89364951ebd..a69dc6a0752b 100644
--- a/drivers/media/pci/ttpci/av7110.c
+++ b/drivers/media/pci/ttpci/av7110.c
@@ -26,7 +26,7 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
- * the project's page is at http://www.linuxtv.org/
+ * the project's page is at https://linuxtv.org
*/
@@ -1358,7 +1358,7 @@ static int av7110_register(struct av7110 *av7110)
#ifdef CONFIG_DVB_AV7110_OSD
dvb_register_device(&av7110->dvb_adapter, &av7110->osd_dev,
- &dvbdev_osd, av7110, DVB_DEVICE_OSD);
+ &dvbdev_osd, av7110, DVB_DEVICE_OSD, 0);
#endif
dvb_net_init(&av7110->dvb_adapter, &av7110->dvb_net, &dvbdemux->dmx);
@@ -1537,7 +1537,7 @@ static int get_firmware(struct av7110* av7110)
printk(KERN_ERR "dvb-ttpci: usually this should be in "
"/usr/lib/hotplug/firmware or /lib/firmware\n");
printk(KERN_ERR "dvb-ttpci: and can be downloaded from"
- " http://www.linuxtv.org/download/dvb/firmware/\n");
+ " https://linuxtv.org/download/dvb/firmware/\n");
} else
printk(KERN_ERR "dvb-ttpci: cannot request firmware"
" (error %i)\n", ret);
@@ -2314,7 +2314,7 @@ static int frontend_init(struct av7110 *av7110)
/* Budgetpatch note:
* Original hardware design by Roberto Deza:
* There is a DVB_Wiki at
- * http://www.linuxtv.org/
+ * https://linuxtv.org
*
* New software triggering design by Emard that works on
* original Roberto Deza's hardware:
diff --git a/drivers/media/pci/ttpci/av7110.h b/drivers/media/pci/ttpci/av7110.h
index 3a55927edb95..3707ccd02732 100644
--- a/drivers/media/pci/ttpci/av7110.h
+++ b/drivers/media/pci/ttpci/av7110.h
@@ -32,7 +32,7 @@
#include "stv0297.h"
#include "l64781.h"
-#include <media/saa7146_vv.h>
+#include <media/drv-intf/saa7146_vv.h>
#define ANALOG_TUNER_VES1820 1
diff --git a/drivers/media/pci/ttpci/av7110_av.c b/drivers/media/pci/ttpci/av7110_av.c
index 9ed1ec7d3551..26c5696c193b 100644
--- a/drivers/media/pci/ttpci/av7110_av.c
+++ b/drivers/media/pci/ttpci/av7110_av.c
@@ -25,7 +25,7 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
- * the project's page is at http://www.linuxtv.org/
+ * the project's page is at https://linuxtv.org
*/
#include <linux/types.h>
@@ -280,9 +280,11 @@ int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen)
}
-int av7110_set_volume(struct av7110 *av7110, int volleft, int volright)
+int av7110_set_volume(struct av7110 *av7110, unsigned int volleft,
+ unsigned int volright)
{
- int err, vol, val, balance = 0;
+ unsigned int vol, val, balance = 0;
+ int err;
dprintk(2, "av7110:%p, \n", av7110);
@@ -1043,6 +1045,9 @@ static int play_iframe(struct av7110 *av7110, char __user *buf, unsigned int len
dprintk(2, "av7110:%p, \n", av7110);
+ if (len == 0)
+ return 0;
+
if (!(av7110->playing & RP_VIDEO)) {
if (av7110_av_start_play(av7110, RP_VIDEO) < 0)
return -EBUSY;
@@ -1589,10 +1594,10 @@ int av7110_av_register(struct av7110 *av7110)
memset(&av7110->video_size, 0, sizeof (video_size_t));
dvb_register_device(&av7110->dvb_adapter, &av7110->video_dev,
- &dvbdev_video, av7110, DVB_DEVICE_VIDEO);
+ &dvbdev_video, av7110, DVB_DEVICE_VIDEO, 0);
dvb_register_device(&av7110->dvb_adapter, &av7110->audio_dev,
- &dvbdev_audio, av7110, DVB_DEVICE_AUDIO);
+ &dvbdev_audio, av7110, DVB_DEVICE_AUDIO, 0);
return 0;
}
diff --git a/drivers/media/pci/ttpci/av7110_av.h b/drivers/media/pci/ttpci/av7110_av.h
index 5f02ef85e47d..f52276f47709 100644
--- a/drivers/media/pci/ttpci/av7110_av.h
+++ b/drivers/media/pci/ttpci/av7110_av.h
@@ -10,7 +10,8 @@ extern int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len);
extern int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen);
extern int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len);
-extern int av7110_set_volume(struct av7110 *av7110, int volleft, int volright);
+extern int av7110_set_volume(struct av7110 *av7110, unsigned int volleft,
+ unsigned int volright);
extern int av7110_av_stop(struct av7110 *av7110, int av);
extern int av7110_av_start_record(struct av7110 *av7110, int av,
struct dvb_demux_feed *dvbdmxfeed);
diff --git a/drivers/media/pci/ttpci/av7110_ca.c b/drivers/media/pci/ttpci/av7110_ca.c
index a6079b90252a..96a130fb4595 100644
--- a/drivers/media/pci/ttpci/av7110_ca.c
+++ b/drivers/media/pci/ttpci/av7110_ca.c
@@ -25,7 +25,7 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
- * the project's page is at http://www.linuxtv.org/
+ * the project's page is at https://linuxtv.org
*/
#include <linux/kernel.h>
@@ -378,7 +378,7 @@ static struct dvb_device dvbdev_ca = {
int av7110_ca_register(struct av7110 *av7110)
{
return dvb_register_device(&av7110->dvb_adapter, &av7110->ca_dev,
- &dvbdev_ca, av7110, DVB_DEVICE_CA);
+ &dvbdev_ca, av7110, DVB_DEVICE_CA, 0);
}
void av7110_ca_unregister(struct av7110 *av7110)
diff --git a/drivers/media/pci/ttpci/av7110_hw.c b/drivers/media/pci/ttpci/av7110_hw.c
index 300bd3c94738..0583d56ef5ef 100644
--- a/drivers/media/pci/ttpci/av7110_hw.c
+++ b/drivers/media/pci/ttpci/av7110_hw.c
@@ -22,7 +22,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
- * the project's page is at http://www.linuxtv.org/
+ * the project's page is at https://linuxtv.org
*/
/* for debugging ARM communication: */
diff --git a/drivers/media/pci/ttpci/av7110_v4l.c b/drivers/media/pci/ttpci/av7110_v4l.c
index 6c4076acb131..479aff02db81 100644
--- a/drivers/media/pci/ttpci/av7110_v4l.c
+++ b/drivers/media/pci/ttpci/av7110_v4l.c
@@ -22,7 +22,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
- * the project's page is at http://www.linuxtv.org/
+ * the project's page is at https://linuxtv.org
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
diff --git a/drivers/media/pci/ttpci/budget-av.c b/drivers/media/pci/ttpci/budget-av.c
index 3e469d4e0c87..6f0d0161970e 100644
--- a/drivers/media/pci/ttpci/budget-av.c
+++ b/drivers/media/pci/ttpci/budget-av.c
@@ -30,7 +30,7 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
- * the project's page is at http://www.linuxtv.org/
+ * the project's page is at https://linuxtv.org
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -46,7 +46,7 @@
#include "tda1004x.h"
#include "tua6100.h"
#include "dvb-pll.h"
-#include <media/saa7146_vv.h>
+#include <media/drv-intf/saa7146_vv.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/slab.h>
diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c
index 1feeeff3681b..7b27af4d9658 100644
--- a/drivers/media/pci/ttpci/budget-ci.c
+++ b/drivers/media/pci/ttpci/budget-ci.c
@@ -26,7 +26,7 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
- * the project's page is at http://www.linuxtv.org/
+ * the project's page is at https://linuxtv.org
*/
#include <linux/module.h>
diff --git a/drivers/media/pci/ttpci/budget-core.c b/drivers/media/pci/ttpci/budget-core.c
index e9674b40007c..6d42dcfd4825 100644
--- a/drivers/media/pci/ttpci/budget-core.c
+++ b/drivers/media/pci/ttpci/budget-core.c
@@ -31,7 +31,7 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
- * the project's page is at http://www.linuxtv.org/
+ * the project's page is at https://linuxtv.org
*/
diff --git a/drivers/media/pci/ttpci/budget-patch.c b/drivers/media/pci/ttpci/budget-patch.c
index b5b65962ce8f..591dbdfa2a13 100644
--- a/drivers/media/pci/ttpci/budget-patch.c
+++ b/drivers/media/pci/ttpci/budget-patch.c
@@ -27,7 +27,7 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
- * the project's page is at http://www.linuxtv.org/
+ * the project's page is at https://linuxtv.org
*/
#include "av7110.h"
diff --git a/drivers/media/pci/ttpci/budget.c b/drivers/media/pci/ttpci/budget.c
index 99972beca262..de54310a2660 100644
--- a/drivers/media/pci/ttpci/budget.c
+++ b/drivers/media/pci/ttpci/budget.c
@@ -31,7 +31,7 @@
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*
*
- * the project's page is at http://www.linuxtv.org/
+ * the project's page is at https://linuxtv.org
*/
#include "budget.h"
diff --git a/drivers/media/pci/ttpci/budget.h b/drivers/media/pci/ttpci/budget.h
index 1ccbe1a49a4b..655eef5236ca 100644
--- a/drivers/media/pci/ttpci/budget.h
+++ b/drivers/media/pci/ttpci/budget.h
@@ -13,7 +13,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
-#include <media/saa7146.h>
+#include <media/drv-intf/saa7146.h>
extern int budget_debug;
diff --git a/drivers/media/pci/tw68/tw68-video.c b/drivers/media/pci/tw68/tw68-video.c
index 46642ef9151b..07116a87a57b 100644
--- a/drivers/media/pci/tw68/tw68-video.c
+++ b/drivers/media/pci/tw68/tw68-video.c
@@ -376,28 +376,28 @@ static int tw68_buffer_count(unsigned int size, unsigned int count)
/* ------------------------------------------------------------- */
/* vb2 queue operations */
-static int tw68_queue_setup(struct vb2_queue *q, const void *parg,
+static int tw68_queue_setup(struct vb2_queue *q,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct tw68_dev *dev = vb2_get_drv_priv(q);
unsigned tot_bufs = q->num_buffers + *num_buffers;
+ unsigned size = (dev->fmt->depth * dev->width * dev->height) >> 3;
- sizes[0] = (dev->fmt->depth * dev->width * dev->height) >> 3;
+ if (tot_bufs < 2)
+ tot_bufs = 2;
+ tot_bufs = tw68_buffer_count(size, tot_bufs);
+ *num_buffers = tot_bufs - q->num_buffers;
alloc_ctxs[0] = dev->alloc_ctx;
/*
- * We allow create_bufs, but only if the sizeimage is the same as the
+ * We allow create_bufs, but only if the sizeimage is >= as the
* current sizeimage. The tw68_buffer_count calculation becomes quite
* difficult otherwise.
*/
- if (fmt && fmt->fmt.pix.sizeimage < sizes[0])
- return -EINVAL;
+ if (*num_planes)
+ return sizes[0] < size ? -EINVAL : 0;
*num_planes = 1;
- if (tot_bufs < 2)
- tot_bufs = 2;
- tot_bufs = tw68_buffer_count(sizes[0], tot_bufs);
- *num_buffers = tot_bufs - q->num_buffers;
+ sizes[0] = size;
return 0;
}
@@ -1016,7 +1016,7 @@ void tw68_irq_video_done(struct tw68_dev *dev, unsigned long status)
buf = list_entry(dev->active.next, struct tw68_buf, list);
list_del(&buf->list);
spin_unlock(&dev->slock);
- v4l2_get_timestamp(&buf->vb.timestamp);
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
buf->vb.field = dev->field;
buf->vb.sequence = dev->seqnr++;
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c
index 1136d92af642..9d2697f5b455 100644
--- a/drivers/media/pci/zoran/zoran_card.c
+++ b/drivers/media/pci/zoran/zoran_card.c
@@ -50,7 +50,7 @@
#include <linux/mutex.h>
#include <linux/io.h>
#include <media/v4l2-common.h>
-#include <media/bt819.h>
+#include <media/i2c/bt819.h>
#include "videocodec.h"
#include "zoran.h"
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index ccbc9742cb7a..0c53805dff0e 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -38,7 +38,7 @@ config VIDEO_SH_VOU
depends on MEDIA_CAMERA_SUPPORT
depends on VIDEO_DEV && I2C && HAS_DMA
depends on ARCH_SHMOBILE || COMPILE_TEST
- select VIDEOBUF_DMA_CONTIG
+ select VIDEOBUF2_DMA_CONTIG
help
Support for the Video Output Unit (VOU) on SuperH SoCs.
diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c
index f0480d687f17..de32e3a3d4d1 100644
--- a/drivers/media/platform/am437x/am437x-vpfe.c
+++ b/drivers/media/platform/am437x/am437x-vpfe.c
@@ -1281,7 +1281,7 @@ static inline void vpfe_schedule_bottom_field(struct vpfe_device *vpfe)
*/
static inline void vpfe_process_buffer_complete(struct vpfe_device *vpfe)
{
- v4l2_get_timestamp(&vpfe->cur_frm->vb.timestamp);
+ vpfe->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns();
vpfe->cur_frm->vb.field = vpfe->fmt.fmt.pix.field;
vpfe->cur_frm->vb.sequence = vpfe->sequence++;
vb2_buffer_done(&vpfe->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
@@ -1898,7 +1898,6 @@ static void vpfe_calculate_offsets(struct vpfe_device *vpfe)
/*
* vpfe_queue_setup - Callback function for buffer setup.
* @vq: vb2_queue ptr
- * @fmt: v4l2 format
* @nbuffers: ptr to number of buffers requested by application
* @nplanes:: contains number of distinct video planes needed to hold a frame
* @sizes[]: contains the size (in bytes) of each plane.
@@ -1908,22 +1907,24 @@ static void vpfe_calculate_offsets(struct vpfe_device *vpfe)
* the buffer count and buffer size
*/
static int vpfe_queue_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct vpfe_device *vpfe = vb2_get_drv_priv(vq);
-
- if (fmt && fmt->fmt.pix.sizeimage < vpfe->fmt.fmt.pix.sizeimage)
- return -EINVAL;
+ unsigned size = vpfe->fmt.fmt.pix.sizeimage;
if (vq->num_buffers + *nbuffers < 3)
*nbuffers = 3 - vq->num_buffers;
+ alloc_ctxs[0] = vpfe->alloc_ctx;
+
+ if (*nplanes) {
+ if (sizes[0] < size)
+ return -EINVAL;
+ size = sizes[0];
+ }
*nplanes = 1;
- sizes[0] = fmt ? fmt->fmt.pix.sizeimage : vpfe->fmt.fmt.pix.sizeimage;
- alloc_ctxs[0] = vpfe->alloc_ctx;
+ sizes[0] = size;
vpfe_dbg(1, vpfe,
"nbuffers=%d, size=%u\n", *nbuffers, sizes[0]);
diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c
index 7764b9c482ef..d0092dae7a57 100644
--- a/drivers/media/platform/blackfin/bfin_capture.c
+++ b/drivers/media/platform/blackfin/bfin_capture.c
@@ -202,22 +202,20 @@ static void bcap_free_sensor_formats(struct bcap_device *bcap_dev)
}
static int bcap_queue_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
- if (fmt && fmt->fmt.pix.sizeimage < bcap_dev->fmt.sizeimage)
- return -EINVAL;
-
if (vq->num_buffers + *nbuffers < 2)
*nbuffers = 2;
+ alloc_ctxs[0] = bcap_dev->alloc_ctx;
+
+ if (*nplanes)
+ return sizes[0] < bcap_dev->fmt.sizeimage ? -EINVAL : 0;
*nplanes = 1;
- sizes[0] = fmt ? fmt->fmt.pix.sizeimage : bcap_dev->fmt.sizeimage;
- alloc_ctxs[0] = bcap_dev->alloc_ctx;
+ sizes[0] = bcap_dev->fmt.sizeimage;
return 0;
}
@@ -406,7 +404,7 @@ static irqreturn_t bcap_isr(int irq, void *dev_id)
spin_lock(&bcap_dev->lock);
if (!list_empty(&bcap_dev->dma_queue)) {
- v4l2_get_timestamp(&vbuf->timestamp);
+ vb->timestamp = ktime_get_ns();
if (ppi->err) {
vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
ppi->err = false;
diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c
index 654e964f84a2..7d28899f89ce 100644
--- a/drivers/media/platform/coda/coda-bit.c
+++ b/drivers/media/platform/coda/coda-bit.c
@@ -246,7 +246,7 @@ void coda_fill_bitstream(struct coda_ctx *ctx, bool streaming)
/* Drop frames that do not start/end with a SOI/EOI markers */
if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG &&
- !coda_jpeg_check_buffer(ctx, src_buf)) {
+ !coda_jpeg_check_buffer(ctx, &src_buf->vb2_buf)) {
v4l2_err(&ctx->dev->v4l2_dev,
"dropping invalid JPEG frame %d\n",
ctx->qsequence);
@@ -279,7 +279,7 @@ void coda_fill_bitstream(struct coda_ctx *ctx, bool streaming)
if (meta) {
meta->sequence = src_buf->sequence;
meta->timecode = src_buf->timecode;
- meta->timestamp = src_buf->timestamp;
+ meta->timestamp = src_buf->vb2_buf.timestamp;
meta->start = start;
meta->end = ctx->bitstream_fifo.kfifo.in &
ctx->bitstream_fifo.kfifo.mask;
@@ -1364,7 +1364,7 @@ static void coda_finish_encode(struct coda_ctx *ctx)
dst_buf->flags &= ~V4L2_BUF_FLAG_KEYFRAME;
}
- dst_buf->timestamp = src_buf->timestamp;
+ dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp;
dst_buf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
dst_buf->flags |=
src_buf->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
@@ -2040,7 +2040,7 @@ static void coda_finish_decode(struct coda_ctx *ctx)
dst_buf->flags |= ctx->frame_types[ctx->display_idx];
meta = &ctx->frame_metas[ctx->display_idx];
dst_buf->timecode = meta->timecode;
- dst_buf->timestamp = meta->timestamp;
+ dst_buf->vb2_buf.timestamp = meta->timestamp;
trace_coda_dec_rot_done(ctx, dst_buf, meta);
diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c
index 15516a6e3a39..2d782ce94a67 100644
--- a/drivers/media/platform/coda/coda-common.c
+++ b/drivers/media/platform/coda/coda-common.c
@@ -28,7 +28,7 @@
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <linux/of.h>
-#include <linux/platform_data/coda.h>
+#include <linux/platform_data/media/coda.h>
#include <linux/reset.h>
#include <media/v4l2-ctrls.h>
@@ -131,6 +131,7 @@ static const struct coda_codec coda7_codecs[] = {
CODA_CODEC(CODA7_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1280, 720),
CODA_CODEC(CODA7_MODE_ENCODE_MJPG, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_JPEG, 8192, 8192),
CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088),
+ CODA_CODEC(CODA7_MODE_DECODE_MP2, V4L2_PIX_FMT_MPEG2, V4L2_PIX_FMT_YUV420, 1920, 1088),
CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1088),
CODA_CODEC(CODA7_MODE_DECODE_MJPG, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420, 8192, 8192),
};
@@ -139,6 +140,7 @@ static const struct coda_codec coda9_codecs[] = {
CODA_CODEC(CODA9_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1920, 1088),
CODA_CODEC(CODA9_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1920, 1088),
CODA_CODEC(CODA9_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088),
+ CODA_CODEC(CODA9_MODE_DECODE_MP2, V4L2_PIX_FMT_MPEG2, V4L2_PIX_FMT_YUV420, 1920, 1088),
CODA_CODEC(CODA9_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1088),
};
@@ -187,6 +189,7 @@ static const struct coda_video_device coda_bit_decoder = {
.ops = &coda_bit_decode_ops,
.src_formats = {
V4L2_PIX_FMT_H264,
+ V4L2_PIX_FMT_MPEG2,
V4L2_PIX_FMT_MPEG4,
},
.dst_formats = {
@@ -293,7 +296,8 @@ static void coda_get_max_dimensions(struct coda_dev *dev,
*max_h = h;
}
-const struct coda_video_device *to_coda_video_device(struct video_device *vdev)
+static const struct coda_video_device *to_coda_video_device(struct video_device
+ *vdev)
{
struct coda_dev *dev = video_get_drvdata(vdev);
unsigned int i = vdev - dev->vfd;
@@ -469,6 +473,7 @@ static int coda_try_fmt(struct coda_ctx *ctx, const struct coda_codec *codec,
/* fallthrough */
case V4L2_PIX_FMT_H264:
case V4L2_PIX_FMT_MPEG4:
+ case V4L2_PIX_FMT_MPEG2:
f->fmt.pix.bytesperline = 0;
f->fmt.pix.sizeimage = coda_estimate_sizeimage(ctx,
f->fmt.pix.sizeimage,
@@ -920,6 +925,7 @@ static const struct v4l2_ioctl_ops coda_ioctl_ops = {
.vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
.vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
.vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
.vidioc_streamon = v4l2_m2m_ioctl_streamon,
.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
@@ -1131,7 +1137,7 @@ static void set_default_params(struct coda_ctx *ctx)
/*
* Queue operations
*/
-static int coda_queue_setup(struct vb2_queue *vq, const void *parg,
+static int coda_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
@@ -1250,6 +1256,9 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
struct vb2_v4l2_buffer *buf;
int ret = 0;
+ if (count < 1)
+ return -EINVAL;
+
q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) {
@@ -1262,20 +1271,10 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
ret = -EINVAL;
goto err;
}
- } else {
- if (count < 1) {
- ret = -EINVAL;
- goto err;
- }
}
ctx->streamon_out = 1;
} else {
- if (count < 1) {
- ret = -EINVAL;
- goto err;
- }
-
ctx->streamon_cap = 1;
}
diff --git a/drivers/media/platform/coda/coda-jpeg.c b/drivers/media/platform/coda/coda-jpeg.c
index 96cd42a0baaf..9f899a6cefed 100644
--- a/drivers/media/platform/coda/coda-jpeg.c
+++ b/drivers/media/platform/coda/coda-jpeg.c
@@ -178,14 +178,28 @@ int coda_jpeg_write_tables(struct coda_ctx *ctx)
return 0;
}
-bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_v4l2_buffer *vb)
+bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb)
{
- void *vaddr = vb2_plane_vaddr(&vb->vb2_buf, 0);
- u16 soi = be16_to_cpup((__be16 *)vaddr);
- u16 eoi = be16_to_cpup((__be16 *)(vaddr +
- vb2_get_plane_payload(&vb->vb2_buf, 0) - 2));
+ void *vaddr = vb2_plane_vaddr(vb, 0);
+ u16 soi, eoi;
+ int len, i;
+
+ soi = be16_to_cpup((__be16 *)vaddr);
+ if (soi != SOI_MARKER)
+ return false;
+
+ len = vb2_get_plane_payload(vb, 0);
+ vaddr += len - 2;
+ for (i = 0; i < 32; i++) {
+ eoi = be16_to_cpup((__be16 *)(vaddr - i));
+ if (eoi == EOI_MARKER) {
+ if (i > 0)
+ vb2_set_plane_payload(vb, 0, len - i);
+ return true;
+ }
+ }
- return soi == SOI_MARKER && eoi == EOI_MARKER;
+ return false;
}
/*
diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h
index 96532b06bd9e..d08e9843e9f2 100644
--- a/drivers/media/platform/coda/coda.h
+++ b/drivers/media/platform/coda/coda.h
@@ -138,7 +138,7 @@ struct coda_buffer_meta {
struct list_head list;
u32 sequence;
struct v4l2_timecode timecode;
- struct timeval timestamp;
+ u64 timestamp;
u32 start;
u32 end;
};
@@ -289,7 +289,7 @@ void coda_m2m_buf_done(struct coda_ctx *ctx, struct vb2_v4l2_buffer *buf,
int coda_h264_padding(int size, char *p);
-bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_v4l2_buffer *vb);
+bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb);
int coda_jpeg_write_tables(struct coda_ctx *ctx);
void coda_set_jpeg_compression_quality(struct coda_ctx *ctx, int quality);
diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig
index 469e9d28cec0..554e710de487 100644
--- a/drivers/media/platform/davinci/Kconfig
+++ b/drivers/media/platform/davinci/Kconfig
@@ -3,6 +3,7 @@ config VIDEO_DAVINCI_VPIF_DISPLAY
depends on VIDEO_V4L2
depends on ARCH_DAVINCI || COMPILE_TEST
depends on HAS_DMA
+ depends on I2C
select VIDEOBUF2_DMA_CONTIG
select VIDEO_ADV7343 if MEDIA_SUBDRV_AUTOSELECT
select VIDEO_THS7303 if MEDIA_SUBDRV_AUTOSELECT
@@ -19,6 +20,7 @@ config VIDEO_DAVINCI_VPIF_CAPTURE
depends on VIDEO_V4L2
depends on ARCH_DAVINCI || COMPILE_TEST
depends on HAS_DMA
+ depends on I2C
select VIDEOBUF2_DMA_CONTIG
help
Enables Davinci VPIF module used for capture devices.
@@ -33,6 +35,7 @@ config VIDEO_DM6446_CCDC
depends on VIDEO_V4L2
depends on ARCH_DAVINCI || COMPILE_TEST
depends on HAS_DMA
+ depends on I2C
select VIDEOBUF_DMA_CONTIG
help
Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces
@@ -49,6 +52,7 @@ config VIDEO_DM355_CCDC
depends on VIDEO_V4L2
depends on ARCH_DAVINCI || COMPILE_TEST
depends on HAS_DMA
+ depends on I2C
select VIDEOBUF_DMA_CONTIG
help
Enables DM355 CCD hw module. DM355 CCDC hw interfaces
@@ -64,6 +68,7 @@ config VIDEO_DM365_ISIF
tristate "TI DM365 ISIF video capture driver"
depends on VIDEO_V4L2 && ARCH_DAVINCI
depends on HAS_DMA
+ depends on I2C
select VIDEOBUF_DMA_CONTIG
help
Enables ISIF hw module. This is the hardware module for
@@ -77,6 +82,7 @@ config VIDEO_DAVINCI_VPBE_DISPLAY
tristate "TI DaVinci VPBE V4L2-Display driver"
depends on VIDEO_V4L2 && ARCH_DAVINCI
depends on HAS_DMA
+ depends on I2C
select VIDEOBUF2_DMA_CONTIG
help
Enables Davinci VPBE module used for display devices.
diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c
index 6d91422c4e4c..0abcdfe97a6c 100644
--- a/drivers/media/platform/davinci/vpbe_display.c
+++ b/drivers/media/platform/davinci/vpbe_display.c
@@ -74,7 +74,7 @@ static void vpbe_isr_even_field(struct vpbe_display *disp_obj,
if (layer->cur_frm == layer->next_frm)
return;
- v4l2_get_timestamp(&layer->cur_frm->vb.timestamp);
+ layer->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns();
vb2_buffer_done(&layer->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
/* Make cur_frm pointing to next_frm */
layer->cur_frm = layer->next_frm;
@@ -228,28 +228,27 @@ static int vpbe_buffer_prepare(struct vb2_buffer *vb)
* This function allocates memory for the buffers
*/
static int
-vpbe_buffer_queue_setup(struct vb2_queue *vq, const void *parg,
+vpbe_buffer_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
/* Get the file handle object and layer object */
struct vpbe_layer *layer = vb2_get_drv_priv(vq);
struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n");
- if (fmt && fmt->fmt.pix.sizeimage < layer->pix_fmt.sizeimage)
- return -EINVAL;
-
/* Store number of buffers allocated in numbuffer member */
if (vq->num_buffers + *nbuffers < VPBE_DEFAULT_NUM_BUFS)
*nbuffers = VPBE_DEFAULT_NUM_BUFS - vq->num_buffers;
+ alloc_ctxs[0] = layer->alloc_ctx;
+
+ if (*nplanes)
+ return sizes[0] < layer->pix_fmt.sizeimage ? -EINVAL : 0;
*nplanes = 1;
- sizes[0] = fmt ? fmt->fmt.pix.sizeimage : layer->pix_fmt.sizeimage;
- alloc_ctxs[0] = layer->alloc_ctx;
+ sizes[0] = layer->pix_fmt.sizeimage;
return 0;
}
diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
index c1e573b7cc6f..08f7028c7560 100644
--- a/drivers/media/platform/davinci/vpif_capture.c
+++ b/drivers/media/platform/davinci/vpif_capture.c
@@ -104,7 +104,6 @@ static int vpif_buffer_prepare(struct vb2_buffer *vb)
/**
* vpif_buffer_queue_setup : Callback function for buffer setup.
* @vq: vb2_queue ptr
- * @fmt: v4l2 format
* @nbuffers: ptr to number of buffers requested by application
* @nplanes:: contains number of distinct video planes needed to hold a frame
* @sizes[]: contains the size (in bytes) of each plane.
@@ -114,26 +113,26 @@ static int vpif_buffer_prepare(struct vb2_buffer *vb)
* the buffer count and buffer size
*/
static int vpif_buffer_queue_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct channel_obj *ch = vb2_get_drv_priv(vq);
- struct common_obj *common;
-
- common = &ch->common[VPIF_VIDEO_INDEX];
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ unsigned size = common->fmt.fmt.pix.sizeimage;
vpif_dbg(2, debug, "vpif_buffer_setup\n");
- if (fmt && fmt->fmt.pix.sizeimage < common->fmt.fmt.pix.sizeimage)
- return -EINVAL;
+ if (*nplanes) {
+ if (sizes[0] < size)
+ return -EINVAL;
+ size = sizes[0];
+ }
if (vq->num_buffers + *nbuffers < 3)
*nbuffers = 3 - vq->num_buffers;
*nplanes = 1;
- sizes[0] = fmt ? fmt->fmt.pix.sizeimage : common->fmt.fmt.pix.sizeimage;
+ sizes[0] = size;
alloc_ctxs[0] = common->alloc_ctx;
/* Calculate the offset for Y and C data in the buffer */
@@ -331,7 +330,7 @@ static struct vb2_ops video_qops = {
*/
static void vpif_process_buffer_complete(struct common_obj *common)
{
- v4l2_get_timestamp(&common->cur_frm->vb.timestamp);
+ common->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns();
vb2_buffer_done(&common->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
/* Make curFrm pointing to nextFrm */
common->cur_frm = common->next_frm;
diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c
index fd2780306c17..f40755cf1bf2 100644
--- a/drivers/media/platform/davinci/vpif_display.c
+++ b/drivers/media/platform/davinci/vpif_display.c
@@ -99,7 +99,6 @@ static int vpif_buffer_prepare(struct vb2_buffer *vb)
/**
* vpif_buffer_queue_setup : Callback function for buffer setup.
* @vq: vb2_queue ptr
- * @fmt: v4l2 format
* @nbuffers: ptr to number of buffers requested by application
* @nplanes:: contains number of distinct video planes needed to hold a frame
* @sizes[]: contains the size (in bytes) of each plane.
@@ -109,22 +108,24 @@ static int vpif_buffer_prepare(struct vb2_buffer *vb)
* the buffer count and buffer size
*/
static int vpif_buffer_queue_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct channel_obj *ch = vb2_get_drv_priv(vq);
struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ unsigned size = common->fmt.fmt.pix.sizeimage;
- if (fmt && fmt->fmt.pix.sizeimage < common->fmt.fmt.pix.sizeimage)
- return -EINVAL;
+ if (*nplanes) {
+ if (sizes[0] < size)
+ return -EINVAL;
+ size = sizes[0];
+ }
if (vq->num_buffers + *nbuffers < 3)
*nbuffers = 3 - vq->num_buffers;
*nplanes = 1;
- sizes[0] = fmt ? fmt->fmt.pix.sizeimage : common->fmt.fmt.pix.sizeimage;
+ sizes[0] = size;
alloc_ctxs[0] = common->alloc_ctx;
/* Calculate the offset for Y and C data in the buffer */
@@ -330,7 +331,7 @@ static void process_interlaced_mode(int fid, struct common_obj *common)
/* one frame is displayed If next frame is
* available, release cur_frm and move on */
/* Copy frame display time */
- v4l2_get_timestamp(&common->cur_frm->vb.timestamp);
+ common->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns();
/* Change status of the cur_frm */
vb2_buffer_done(&common->cur_frm->vb.vb2_buf,
VB2_BUF_STATE_DONE);
@@ -386,8 +387,8 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
if (!channel_first_int[i][channel_id]) {
/* Mark status of the cur_frm to
* done and unlock semaphore on it */
- v4l2_get_timestamp(
- &common->cur_frm->vb.timestamp);
+ common->cur_frm->vb.vb2_buf.timestamp =
+ ktime_get_ns();
vb2_buffer_done(&common->cur_frm->vb.vb2_buf,
VB2_BUF_STATE_DONE);
/* Make cur_frm pointing to next_frm */
diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c
index d82e717acba7..93782f15b825 100644
--- a/drivers/media/platform/exynos-gsc/gsc-m2m.c
+++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c
@@ -86,7 +86,7 @@ void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state)
dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
if (src_vb && dst_vb) {
- dst_vb->timestamp = src_vb->timestamp;
+ dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
dst_vb->timecode = src_vb->timecode;
dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
dst_vb->flags |=
@@ -125,7 +125,7 @@ static int gsc_get_bufs(struct gsc_ctx *ctx)
if (ret)
return ret;
- dst_vb->timestamp = src_vb->timestamp;
+ dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
return 0;
}
@@ -212,7 +212,6 @@ put_device:
}
static int gsc_m2m_queue_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *allocators[])
{
diff --git a/drivers/media/platform/exynos4-is/common.c b/drivers/media/platform/exynos4-is/common.c
index 0eb34ecb8ee4..b90f5bb15517 100644
--- a/drivers/media/platform/exynos4-is/common.c
+++ b/drivers/media/platform/exynos4-is/common.c
@@ -10,7 +10,7 @@
*/
#include <linux/module.h>
-#include <media/exynos-fimc.h>
+#include <media/drv-intf/exynos-fimc.h>
#include "common.h"
/* Called with the media graph mutex held or entity->stream_count > 0. */
@@ -22,8 +22,7 @@ struct v4l2_subdev *fimc_find_remote_sensor(struct media_entity *entity)
while (pad->flags & MEDIA_PAD_FL_SINK) {
/* source pad */
pad = media_entity_remote_pad(pad);
- if (pad == NULL ||
- media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
break;
sd = media_entity_to_v4l2_subdev(pad->entity);
diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c
index 99e57320e6f7..bf47d3b9cbe7 100644
--- a/drivers/media/platform/exynos4-is/fimc-capture.c
+++ b/drivers/media/platform/exynos4-is/fimc-capture.c
@@ -193,7 +193,7 @@ void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf)
test_bit(ST_CAPT_RUN, &fimc->state) && deq_buf) {
v_buf = fimc_active_queue_pop(cap);
- v4l2_get_timestamp(&v_buf->vb.timestamp);
+ v_buf->vb.vb2_buf.timestamp = ktime_get_ns();
v_buf->vb.sequence = cap->frame_count++;
vb2_buffer_done(&v_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
@@ -338,37 +338,36 @@ int fimc_capture_resume(struct fimc_dev *fimc)
}
-static int queue_setup(struct vb2_queue *vq, const void *parg,
+static int queue_setup(struct vb2_queue *vq,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *allocators[])
{
- const struct v4l2_format *pfmt = parg;
- const struct v4l2_pix_format_mplane *pixm = NULL;
struct fimc_ctx *ctx = vq->drv_priv;
struct fimc_frame *frame = &ctx->d_frame;
struct fimc_fmt *fmt = frame->fmt;
- unsigned long wh;
+ unsigned long wh = frame->f_width * frame->f_height;
int i;
- if (pfmt) {
- pixm = &pfmt->fmt.pix_mp;
- fmt = fimc_find_format(&pixm->pixelformat, NULL,
- FMT_FLAGS_CAM | FMT_FLAGS_M2M, -1);
- wh = pixm->width * pixm->height;
- } else {
- wh = frame->f_width * frame->f_height;
- }
-
if (fmt == NULL)
return -EINVAL;
+ if (*num_planes) {
+ if (*num_planes != fmt->memplanes)
+ return -EINVAL;
+ for (i = 0; i < *num_planes; i++) {
+ if (sizes[i] < (wh * fmt->depth[i]) / 8)
+ return -EINVAL;
+ allocators[i] = ctx->fimc_dev->alloc_ctx;
+ }
+ return 0;
+ }
+
*num_planes = fmt->memplanes;
for (i = 0; i < fmt->memplanes; i++) {
unsigned int size = (wh * fmt->depth[i]) / 8;
- if (pixm)
- sizes[i] = max(size, pixm->plane_fmt[i].sizeimage);
- else if (fimc_fmt_is_user_defined(fmt->color))
+
+ if (fimc_fmt_is_user_defined(fmt->color))
sizes[i] = frame->payload[i];
else
sizes[i] = max_t(u32, size, frame->payload[i]);
@@ -1137,8 +1136,7 @@ static int fimc_pipeline_validate(struct fimc_dev *fimc)
}
}
- if (src_pad == NULL ||
- media_entity_type(src_pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (!src_pad || !is_media_entity_v4l2_subdev(src_pad->entity))
break;
/* Don't call FIMC subdev operation to avoid nested locking */
@@ -1393,7 +1391,7 @@ static int fimc_link_setup(struct media_entity *entity,
struct fimc_vid_cap *vc = &fimc->vid_cap;
struct v4l2_subdev *sensor;
- if (media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (!is_media_entity_v4l2_subdev(remote->entity))
return -EINVAL;
if (WARN_ON(fimc == NULL))
@@ -1801,7 +1799,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc,
vid_cap->wb_fmt.code = fmt->mbus_code;
vid_cap->vd_pad.flags = MEDIA_PAD_FL_SINK;
- ret = media_entity_init(&vfd->entity, 1, &vid_cap->vd_pad, 0);
+ ret = media_entity_pads_init(&vfd->entity, 1, &vid_cap->vd_pad);
if (ret)
goto err_free_ctx;
@@ -1893,8 +1891,8 @@ int fimc_initialize_capture_subdev(struct fimc_dev *fimc)
fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK_CAM].flags = MEDIA_PAD_FL_SINK;
fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK_FIFO].flags = MEDIA_PAD_FL_SINK;
fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM,
- fimc->vid_cap.sd_pads, 0);
+ ret = media_entity_pads_init(&sd->entity, FIMC_SD_PADS_NUM,
+ fimc->vid_cap.sd_pads);
if (ret)
return ret;
diff --git a/drivers/media/platform/exynos4-is/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h
index d336fa2916df..6b7435453d2a 100644
--- a/drivers/media/platform/exynos4-is/fimc-core.h
+++ b/drivers/media/platform/exynos4-is/fimc-core.h
@@ -27,7 +27,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-mem2mem.h>
#include <media/v4l2-mediabus.h>
-#include <media/exynos-fimc.h>
+#include <media/drv-intf/exynos-fimc.h>
#define dbg(fmt, args...) \
pr_debug("%s:%d: " fmt "\n", __func__, __LINE__, ##args)
diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c
index 6e6648446f00..bf9261eb57a1 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp-video.c
+++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c
@@ -30,7 +30,7 @@
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-dma-contig.h>
-#include <media/exynos-fimc.h>
+#include <media/drv-intf/exynos-fimc.h>
#include "common.h"
#include "media-dev.h"
@@ -39,39 +39,36 @@
#include "fimc-is-param.h"
static int isp_video_capture_queue_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *allocators[])
{
- const struct v4l2_format *pfmt = parg;
struct fimc_isp *isp = vb2_get_drv_priv(vq);
struct v4l2_pix_format_mplane *vid_fmt = &isp->video_capture.pixfmt;
- const struct v4l2_pix_format_mplane *pixm = NULL;
- const struct fimc_fmt *fmt;
+ const struct fimc_fmt *fmt = isp->video_capture.format;
unsigned int wh, i;
- if (pfmt) {
- pixm = &pfmt->fmt.pix_mp;
- fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, -1);
- wh = pixm->width * pixm->height;
- } else {
- fmt = isp->video_capture.format;
- wh = vid_fmt->width * vid_fmt->height;
- }
+ wh = vid_fmt->width * vid_fmt->height;
if (fmt == NULL)
return -EINVAL;
*num_buffers = clamp_t(u32, *num_buffers, FIMC_ISP_REQ_BUFS_MIN,
FIMC_ISP_REQ_BUFS_MAX);
+ if (*num_planes) {
+ if (*num_planes != fmt->memplanes)
+ return -EINVAL;
+ for (i = 0; i < *num_planes; i++) {
+ if (sizes[i] < (wh * fmt->depth[i]) / 8)
+ return -EINVAL;
+ allocators[i] = isp->alloc_ctx;
+ }
+ return 0;
+ }
+
*num_planes = fmt->memplanes;
for (i = 0; i < fmt->memplanes; i++) {
- unsigned int size = (wh * fmt->depth[i]) / 8;
- if (pixm)
- sizes[i] = max(size, pixm->plane_fmt[i].sizeimage);
- else
- sizes[i] = size;
+ sizes[i] = (wh * fmt->depth[i]) / 8;
allocators[i] = isp->alloc_ctx;
}
@@ -254,7 +251,7 @@ void fimc_isp_video_irq_handler(struct fimc_is *is)
buf_index = (is->i2h_cmd.args[1] - 1) % video->buf_count;
vbuf = &video->buffers[buf_index]->vb;
- v4l2_get_timestamp(&vbuf->timestamp);
+ vbuf->vb2_buf.timestamp = ktime_get_ns();
vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE);
video->buf_mask &= ~BIT(buf_index);
@@ -290,7 +287,7 @@ static int isp_video_open(struct file *file)
goto rel_fh;
if (v4l2_fh_is_singular_file(file)) {
- mutex_lock(&me->parent->graph_mutex);
+ mutex_lock(&me->graph_obj.mdev->graph_mutex);
ret = fimc_pipeline_call(ve, open, me, true);
@@ -298,7 +295,7 @@ static int isp_video_open(struct file *file)
if (ret == 0)
me->use_count++;
- mutex_unlock(&me->parent->graph_mutex);
+ mutex_unlock(&me->graph_obj.mdev->graph_mutex);
}
if (!ret)
goto unlock;
@@ -314,7 +311,7 @@ static int isp_video_release(struct file *file)
struct fimc_isp *isp = video_drvdata(file);
struct fimc_is_video *ivc = &isp->video_capture;
struct media_entity *entity = &ivc->ve.vdev.entity;
- struct media_device *mdev = entity->parent;
+ struct media_device *mdev = entity->graph_obj.mdev;
mutex_lock(&isp->video_lock);
@@ -469,8 +466,7 @@ static int isp_video_pipeline_validate(struct fimc_isp *isp)
/* Retrieve format at the source pad */
pad = media_entity_remote_pad(pad);
- if (pad == NULL ||
- media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
break;
sd = media_entity_to_v4l2_subdev(pad->entity);
@@ -620,7 +616,7 @@ int fimc_isp_video_device_register(struct fimc_isp *isp,
vdev->lock = &isp->video_lock;
iv->pad.flags = MEDIA_PAD_FL_SINK;
- ret = media_entity_init(&vdev->entity, 1, &iv->pad, 0);
+ ret = media_entity_pads_init(&vdev->entity, 1, &iv->pad);
if (ret < 0)
return ret;
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c
index 5d78f5716f3b..293b807020c4 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp.c
+++ b/drivers/media/platform/exynos4-is/fimc-isp.c
@@ -708,8 +708,8 @@ int fimc_isp_subdev_create(struct fimc_isp *isp)
isp->subdev_pads[FIMC_ISP_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
isp->subdev_pads[FIMC_ISP_SD_PAD_SRC_FIFO].flags = MEDIA_PAD_FL_SOURCE;
isp->subdev_pads[FIMC_ISP_SD_PAD_SRC_DMA].flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&sd->entity, FIMC_ISP_SD_PADS_NUM,
- isp->subdev_pads, 0);
+ ret = media_entity_pads_init(&sd->entity, FIMC_ISP_SD_PADS_NUM,
+ isp->subdev_pads);
if (ret)
return ret;
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.h b/drivers/media/platform/exynos4-is/fimc-isp.h
index c2d25df85db9..e0686b5f1bf8 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp.h
+++ b/drivers/media/platform/exynos4-is/fimc-isp.h
@@ -24,7 +24,7 @@
#include <media/videobuf2-v4l2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mediabus.h>
-#include <media/exynos-fimc.h>
+#include <media/drv-intf/exynos-fimc.h>
extern int fimc_isp_debug;
diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.c b/drivers/media/platform/exynos4-is/fimc-lite-reg.c
index 0477716a20db..f0acc550d065 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite-reg.c
+++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.c
@@ -12,7 +12,7 @@
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/io.h>
-#include <media/exynos-fimc.h>
+#include <media/drv-intf/exynos-fimc.h>
#include "fimc-lite-reg.h"
#include "fimc-lite.h"
diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c
index 60660c3a5de0..e85649147dc8 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite.c
+++ b/drivers/media/platform/exynos4-is/fimc-lite.c
@@ -30,7 +30,7 @@
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-dma-contig.h>
-#include <media/exynos-fimc.h>
+#include <media/drv-intf/exynos-fimc.h>
#include "common.h"
#include "fimc-core.h"
@@ -292,7 +292,7 @@ static irqreturn_t flite_irq_handler(int irq, void *priv)
test_bit(ST_FLITE_RUN, &fimc->state) &&
!list_empty(&fimc->active_buf_q)) {
vbuf = fimc_lite_active_queue_pop(fimc);
- v4l2_get_timestamp(&vbuf->vb.timestamp);
+ vbuf->vb.vb2_buf.timestamp = ktime_get_ns();
vbuf->vb.sequence = fimc->frame_count++;
flite_hw_mask_dma_buffer(fimc, vbuf->index);
vb2_buffer_done(&vbuf->vb.vb2_buf, VB2_BUF_STATE_DONE);
@@ -355,37 +355,34 @@ static void stop_streaming(struct vb2_queue *q)
fimc_lite_stop_capture(fimc, false);
}
-static int queue_setup(struct vb2_queue *vq, const void *parg,
+static int queue_setup(struct vb2_queue *vq,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *allocators[])
{
- const struct v4l2_format *pfmt = parg;
- const struct v4l2_pix_format_mplane *pixm = NULL;
struct fimc_lite *fimc = vq->drv_priv;
struct flite_frame *frame = &fimc->out_frame;
const struct fimc_fmt *fmt = frame->fmt;
- unsigned long wh;
+ unsigned long wh = frame->f_width * frame->f_height;
int i;
- if (pfmt) {
- pixm = &pfmt->fmt.pix_mp;
- fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, 0, -1);
- wh = pixm->width * pixm->height;
- } else {
- wh = frame->f_width * frame->f_height;
- }
-
if (fmt == NULL)
return -EINVAL;
+ if (*num_planes) {
+ if (*num_planes != fmt->memplanes)
+ return -EINVAL;
+ for (i = 0; i < *num_planes; i++) {
+ if (sizes[i] < (wh * fmt->depth[i]) / 8)
+ return -EINVAL;
+ allocators[i] = fimc->alloc_ctx;
+ }
+ return 0;
+ }
+
*num_planes = fmt->memplanes;
for (i = 0; i < fmt->memplanes; i++) {
- unsigned int size = (wh * fmt->depth[i]) / 8;
- if (pixm)
- sizes[i] = max(size, pixm->plane_fmt[i].sizeimage);
- else
- sizes[i] = size;
+ sizes[i] = (wh * fmt->depth[i]) / 8;
allocators[i] = fimc->alloc_ctx;
}
@@ -497,7 +494,7 @@ static int fimc_lite_open(struct file *file)
atomic_read(&fimc->out_path) != FIMC_IO_DMA)
goto unlock;
- mutex_lock(&me->parent->graph_mutex);
+ mutex_lock(&me->graph_obj.mdev->graph_mutex);
ret = fimc_pipeline_call(&fimc->ve, open, me, true);
@@ -505,7 +502,7 @@ static int fimc_lite_open(struct file *file)
if (ret == 0)
me->use_count++;
- mutex_unlock(&me->parent->graph_mutex);
+ mutex_unlock(&me->graph_obj.mdev->graph_mutex);
if (!ret) {
fimc_lite_clear_event_counters(fimc);
@@ -538,9 +535,9 @@ static int fimc_lite_release(struct file *file)
fimc_pipeline_call(&fimc->ve, close);
clear_bit(ST_FLITE_IN_USE, &fimc->state);
- mutex_lock(&entity->parent->graph_mutex);
+ mutex_lock(&entity->graph_obj.mdev->graph_mutex);
entity->use_count--;
- mutex_unlock(&entity->parent->graph_mutex);
+ mutex_unlock(&entity->graph_obj.mdev->graph_mutex);
}
_vb2_fop_release(file, NULL);
@@ -811,8 +808,7 @@ static int fimc_pipeline_validate(struct fimc_lite *fimc)
}
/* Retrieve format at the source pad */
pad = media_entity_remote_pad(pad);
- if (pad == NULL ||
- media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
break;
sd = media_entity_to_v4l2_subdev(pad->entity);
@@ -985,7 +981,6 @@ static int fimc_lite_link_setup(struct media_entity *entity,
{
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
- unsigned int remote_ent_type = media_entity_type(remote->entity);
int ret = 0;
if (WARN_ON(fimc == NULL))
@@ -997,7 +992,7 @@ static int fimc_lite_link_setup(struct media_entity *entity,
switch (local->index) {
case FLITE_SD_PAD_SINK:
- if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV) {
+ if (!is_media_entity_v4l2_subdev(remote->entity)) {
ret = -EINVAL;
break;
}
@@ -1015,7 +1010,7 @@ static int fimc_lite_link_setup(struct media_entity *entity,
case FLITE_SD_PAD_SOURCE_DMA:
if (!(flags & MEDIA_LNK_FL_ENABLED))
atomic_set(&fimc->out_path, FIMC_IO_NONE);
- else if (remote_ent_type == MEDIA_ENT_T_DEVNODE)
+ else if (is_media_entity_v4l2_io(remote->entity))
atomic_set(&fimc->out_path, FIMC_IO_DMA);
else
ret = -EINVAL;
@@ -1024,7 +1019,7 @@ static int fimc_lite_link_setup(struct media_entity *entity,
case FLITE_SD_PAD_SOURCE_ISP:
if (!(flags & MEDIA_LNK_FL_ENABLED))
atomic_set(&fimc->out_path, FIMC_IO_NONE);
- else if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV)
+ else if (is_media_entity_v4l2_subdev(remote->entity))
atomic_set(&fimc->out_path, FIMC_IO_ISP);
else
ret = -EINVAL;
@@ -1319,7 +1314,7 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd)
return ret;
fimc->vd_pad.flags = MEDIA_PAD_FL_SINK;
- ret = media_entity_init(&vfd->entity, 1, &fimc->vd_pad, 0);
+ ret = media_entity_pads_init(&vfd->entity, 1, &fimc->vd_pad);
if (ret < 0)
return ret;
@@ -1433,8 +1428,8 @@ static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc)
fimc->subdev_pads[FLITE_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
fimc->subdev_pads[FLITE_SD_PAD_SOURCE_DMA].flags = MEDIA_PAD_FL_SOURCE;
fimc->subdev_pads[FLITE_SD_PAD_SOURCE_ISP].flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&sd->entity, FLITE_SD_PADS_NUM,
- fimc->subdev_pads, 0);
+ ret = media_entity_pads_init(&sd->entity, FLITE_SD_PADS_NUM,
+ fimc->subdev_pads);
if (ret)
return ret;
diff --git a/drivers/media/platform/exynos4-is/fimc-lite.h b/drivers/media/platform/exynos4-is/fimc-lite.h
index b302305dedbe..11690d563e06 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite.h
+++ b/drivers/media/platform/exynos4-is/fimc-lite.h
@@ -23,7 +23,7 @@
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mediabus.h>
-#include <media/exynos-fimc.h>
+#include <media/drv-intf/exynos-fimc.h>
#define FIMC_LITE_DRV_NAME "exynos-fimc-lite"
#define FLITE_CLK_NAME "flite"
diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c
index 4d1d64a46b21..55ec4c99d484 100644
--- a/drivers/media/platform/exynos4-is/fimc-m2m.c
+++ b/drivers/media/platform/exynos4-is/fimc-m2m.c
@@ -132,7 +132,7 @@ static void fimc_device_run(void *priv)
if (ret)
goto dma_unlock;
- dst_vb->timestamp = src_vb->timestamp;
+ dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
dst_vb->flags |=
src_vb->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
@@ -176,7 +176,7 @@ static void fimc_job_abort(void *priv)
fimc_m2m_shutdown(priv);
}
-static int fimc_queue_setup(struct vb2_queue *vq, const void *parg,
+static int fimc_queue_setup(struct vb2_queue *vq,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *allocators[])
{
@@ -739,7 +739,7 @@ int fimc_register_m2m_device(struct fimc_dev *fimc,
return PTR_ERR(fimc->m2m.m2m_dev);
}
- ret = media_entity_init(&vfd->entity, 0, NULL, 0);
+ ret = media_entity_pads_init(&vfd->entity, 0, NULL);
if (ret)
goto err_me;
diff --git a/drivers/media/platform/exynos4-is/fimc-reg.c b/drivers/media/platform/exynos4-is/fimc-reg.c
index df0cbcb69b6b..0806724553a2 100644
--- a/drivers/media/platform/exynos4-is/fimc-reg.c
+++ b/drivers/media/platform/exynos4-is/fimc-reg.c
@@ -13,7 +13,7 @@
#include <linux/io.h>
#include <linux/regmap.h>
-#include <media/exynos-fimc.h>
+#include <media/drv-intf/exynos-fimc.h>
#include "media-dev.h"
#include "fimc-reg.h"
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index 4f5586a4cbff..f3b2dd30ec77 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -31,7 +31,7 @@
#include <media/v4l2-ctrls.h>
#include <media/v4l2-of.h>
#include <media/media-device.h>
-#include <media/exynos-fimc.h>
+#include <media/drv-intf/exynos-fimc.h>
#include "media-dev.h"
#include "fimc-core.h"
@@ -88,8 +88,7 @@ static void fimc_pipeline_prepare(struct fimc_pipeline *p,
break;
}
- if (pad == NULL ||
- media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
break;
sd = media_entity_to_v4l2_subdev(pad->entity);
@@ -729,7 +728,7 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd,
flags = ((1 << i) & link_mask) ? MEDIA_LNK_FL_ENABLED : 0;
sink = &fmd->fimc[i]->vid_cap.subdev.entity;
- ret = media_entity_create_link(source, pad, sink,
+ ret = media_create_pad_link(source, pad, sink,
FIMC_SD_PAD_SINK_CAM, flags);
if (ret)
return ret;
@@ -749,7 +748,7 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd,
continue;
sink = &fmd->fimc_lite[i]->subdev.entity;
- ret = media_entity_create_link(source, pad, sink,
+ ret = media_create_pad_link(source, pad, sink,
FLITE_SD_PAD_SINK, 0);
if (ret)
return ret;
@@ -781,13 +780,13 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd)
source = &fimc->subdev.entity;
sink = &fimc->ve.vdev.entity;
/* FIMC-LITE's subdev and video node */
- ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_DMA,
+ ret = media_create_pad_link(source, FLITE_SD_PAD_SOURCE_DMA,
sink, 0, 0);
if (ret)
break;
/* Link from FIMC-LITE to IS-ISP subdev */
sink = &fmd->fimc_is->isp.subdev.entity;
- ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_ISP,
+ ret = media_create_pad_link(source, FLITE_SD_PAD_SOURCE_ISP,
sink, 0, 0);
if (ret)
break;
@@ -811,7 +810,7 @@ static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd)
/* Link from FIMC-IS-ISP subdev to FIMC */
sink = &fmd->fimc[i]->vid_cap.subdev.entity;
- ret = media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_FIFO,
+ ret = media_create_pad_link(source, FIMC_ISP_SD_PAD_SRC_FIFO,
sink, FIMC_SD_PAD_SINK_FIFO, 0);
if (ret)
return ret;
@@ -824,7 +823,7 @@ static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd)
if (sink->num_pads == 0)
return 0;
- return media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_DMA,
+ return media_create_pad_link(source, FIMC_ISP_SD_PAD_SRC_DMA,
sink, 0, 0);
}
@@ -873,7 +872,7 @@ static int fimc_md_create_links(struct fimc_md *fmd)
return -EINVAL;
pad = sensor->entity.num_pads - 1;
- ret = media_entity_create_link(&sensor->entity, pad,
+ ret = media_create_pad_link(&sensor->entity, pad,
&csis->entity, CSIS_PAD_SINK,
MEDIA_LNK_FL_IMMUTABLE |
MEDIA_LNK_FL_ENABLED);
@@ -927,7 +926,7 @@ static int fimc_md_create_links(struct fimc_md *fmd)
source = &fmd->fimc[i]->vid_cap.subdev.entity;
sink = &fmd->fimc[i]->vid_cap.ve.vdev.entity;
- ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE,
+ ret = media_create_pad_link(source, FIMC_SD_PAD_SOURCE,
sink, 0, flags);
if (ret)
break;
@@ -1046,11 +1045,11 @@ static int __fimc_md_modify_pipeline(struct media_entity *entity, bool enable)
return ret;
}
-/* Locking: called with entity->parent->graph_mutex mutex held. */
-static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable)
+/* Locking: called with entity->graph_obj.mdev->graph_mutex mutex held. */
+static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable,
+ struct media_entity_graph *graph)
{
struct media_entity *entity_err = entity;
- struct media_entity_graph graph;
int ret;
/*
@@ -1059,10 +1058,10 @@ static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable)
* through active links. This is needed as we cannot power on/off the
* subdevs in random order.
*/
- media_entity_graph_walk_start(&graph, entity);
+ media_entity_graph_walk_start(graph, entity);
- while ((entity = media_entity_graph_walk_next(&graph))) {
- if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+ while ((entity = media_entity_graph_walk_next(graph))) {
+ if (!is_media_entity_v4l2_io(entity))
continue;
ret = __fimc_md_modify_pipeline(entity, enable);
@@ -1072,11 +1071,12 @@ static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable)
}
return 0;
- err:
- media_entity_graph_walk_start(&graph, entity_err);
- while ((entity_err = media_entity_graph_walk_next(&graph))) {
- if (media_entity_type(entity_err) != MEDIA_ENT_T_DEVNODE)
+err:
+ media_entity_graph_walk_start(graph, entity_err);
+
+ while ((entity_err = media_entity_graph_walk_next(graph))) {
+ if (!is_media_entity_v4l2_io(entity_err))
continue;
__fimc_md_modify_pipeline(entity_err, !enable);
@@ -1091,21 +1091,29 @@ static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable)
static int fimc_md_link_notify(struct media_link *link, unsigned int flags,
unsigned int notification)
{
+ struct media_entity_graph *graph =
+ &container_of(link->graph_obj.mdev, struct fimc_md,
+ media_dev)->link_setup_graph;
struct media_entity *sink = link->sink->entity;
int ret = 0;
/* Before link disconnection */
if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH) {
+ ret = media_entity_graph_walk_init(graph,
+ link->graph_obj.mdev);
+ if (ret)
+ return ret;
if (!(flags & MEDIA_LNK_FL_ENABLED))
- ret = __fimc_md_modify_pipelines(sink, false);
+ ret = __fimc_md_modify_pipelines(sink, false, graph);
#if 0
else
/* TODO: Link state change validation */
#endif
/* After link activation */
- } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
- (link->flags & MEDIA_LNK_FL_ENABLED)) {
- ret = __fimc_md_modify_pipelines(sink, true);
+ } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH) {
+ if (link->flags & MEDIA_LNK_FL_ENABLED)
+ ret = __fimc_md_modify_pipelines(sink, true, graph);
+ media_entity_graph_walk_cleanup(graph);
}
return ret ? -EPIPE : 0;
@@ -1314,7 +1322,10 @@ static int subdev_notifier_complete(struct v4l2_async_notifier *notifier)
ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
unlock:
mutex_unlock(&fmd->media_dev.graph_mutex);
- return ret;
+ if (ret < 0)
+ return ret;
+
+ return media_device_register(&fmd->media_dev);
}
static int fimc_md_probe(struct platform_device *pdev)
@@ -1345,18 +1356,14 @@ static int fimc_md_probe(struct platform_device *pdev)
fmd->use_isp = fimc_md_is_isp_available(dev->of_node);
fmd->user_subdev_api = true;
+ media_device_init(&fmd->media_dev);
+
ret = v4l2_device_register(dev, &fmd->v4l2_dev);
if (ret < 0) {
v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret);
return ret;
}
- ret = media_device_register(&fmd->media_dev);
- if (ret < 0) {
- v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret);
- goto err_v4l2_dev;
- }
-
ret = fimc_md_get_clocks(fmd);
if (ret)
goto err_md;
@@ -1425,8 +1432,7 @@ err_clk:
err_m_ent:
fimc_md_unregister_entities(fmd);
err_md:
- media_device_unregister(&fmd->media_dev);
-err_v4l2_dev:
+ media_device_cleanup(&fmd->media_dev);
v4l2_device_unregister(&fmd->v4l2_dev);
return ret;
}
@@ -1446,6 +1452,7 @@ static int fimc_md_remove(struct platform_device *pdev)
fimc_md_unregister_entities(fmd);
fimc_md_pipelines_free(fmd);
media_device_unregister(&fmd->media_dev);
+ media_device_cleanup(&fmd->media_dev);
fimc_md_put_clocks(fmd);
return 0;
diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
index 03214541f149..ed122cb2dd74 100644
--- a/drivers/media/platform/exynos4-is/media-dev.h
+++ b/drivers/media/platform/exynos4-is/media-dev.h
@@ -19,7 +19,7 @@
#include <media/media-entity.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
-#include <media/exynos-fimc.h>
+#include <media/drv-intf/exynos-fimc.h>
#include "fimc-core.h"
#include "fimc-lite.h"
@@ -154,6 +154,7 @@ struct fimc_md {
bool user_subdev_api;
spinlock_t slock;
struct list_head pipelines;
+ struct media_entity_graph link_setup_graph;
};
static inline
@@ -164,8 +165,8 @@ struct fimc_sensor_info *source_to_sensor_info(struct fimc_source_info *si)
static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me)
{
- return me->parent == NULL ? NULL :
- container_of(me->parent, struct fimc_md, media_dev);
+ return me->graph_obj.mdev == NULL ? NULL :
+ container_of(me->graph_obj.mdev, struct fimc_md, media_dev);
}
static inline struct fimc_md *notifier_to_fimc_md(struct v4l2_async_notifier *n)
@@ -175,12 +176,12 @@ static inline struct fimc_md *notifier_to_fimc_md(struct v4l2_async_notifier *n)
static inline void fimc_md_graph_lock(struct exynos_video_entity *ve)
{
- mutex_lock(&ve->vdev.entity.parent->graph_mutex);
+ mutex_lock(&ve->vdev.entity.graph_obj.mdev->graph_mutex);
}
static inline void fimc_md_graph_unlock(struct exynos_video_entity *ve)
{
- mutex_unlock(&ve->vdev.entity.parent->graph_mutex);
+ mutex_unlock(&ve->vdev.entity.graph_obj.mdev->graph_mutex);
}
int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on);
diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c
index 4b85105dc159..ac5e50e595be 100644
--- a/drivers/media/platform/exynos4-is/mipi-csis.c
+++ b/drivers/media/platform/exynos4-is/mipi-csis.c
@@ -29,7 +29,7 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/videodev2.h>
-#include <media/exynos-fimc.h>
+#include <media/drv-intf/exynos-fimc.h>
#include <media/v4l2-of.h>
#include <media/v4l2-subdev.h>
@@ -866,8 +866,8 @@ static int s5pcsis_probe(struct platform_device *pdev)
state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&state->sd.entity,
- CSIS_PADS_NUM, state->pads, 0);
+ ret = media_entity_pads_init(&state->sd.entity,
+ CSIS_PADS_NUM, state->pads);
if (ret < 0)
goto e_clkdis;
diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c
index 29973f9bf8db..7383818c2be6 100644
--- a/drivers/media/platform/m2m-deinterlace.c
+++ b/drivers/media/platform/m2m-deinterlace.c
@@ -207,7 +207,7 @@ static void dma_callback(void *data)
src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);
- dst_vb->timestamp = src_vb->timestamp;
+ dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
dst_vb->flags |=
src_vb->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
@@ -798,7 +798,6 @@ struct vb2_dc_conf {
};
static int deinterlace_queue_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c
index aa2b44041d3f..9b878deb1437 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.c
+++ b/drivers/media/platform/marvell-ccic/mcam-core.c
@@ -25,7 +25,7 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
-#include <media/ov7670.h>
+#include <media/i2c/ov7670.h>
#include <media/videobuf2-vmalloc.h>
#include <media/videobuf2-dma-contig.h>
#include <media/videobuf2-dma-sg.h>
@@ -226,7 +226,7 @@ static void mcam_buffer_done(struct mcam_camera *cam, int frame,
vbuf->vb2_buf.planes[0].bytesused = cam->pix_format.sizeimage;
vbuf->sequence = cam->buf_seq[frame];
vbuf->field = V4L2_FIELD_NONE;
- v4l2_get_timestamp(&vbuf->timestamp);
+ vbuf->vb2_buf.timestamp = ktime_get_ns();
vb2_set_plane_payload(&vbuf->vb2_buf, 0, cam->pix_format.sizeimage);
vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE);
}
@@ -1049,24 +1049,25 @@ static int mcam_read_setup(struct mcam_camera *cam)
*/
static int mcam_vb_queue_setup(struct vb2_queue *vq,
- const void *parg, unsigned int *nbufs,
+ unsigned int *nbufs,
unsigned int *num_planes, unsigned int sizes[],
void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct mcam_camera *cam = vb2_get_drv_priv(vq);
int minbufs = (cam->buffer_mode == B_DMA_contig) ? 3 : 2;
+ unsigned size = cam->pix_format.sizeimage;
- if (fmt && fmt->fmt.pix.sizeimage < cam->pix_format.sizeimage)
- return -EINVAL;
- sizes[0] = fmt ? fmt->fmt.pix.sizeimage : cam->pix_format.sizeimage;
- *num_planes = 1; /* Someday we have to support planar formats... */
if (*nbufs < minbufs)
*nbufs = minbufs;
if (cam->buffer_mode == B_DMA_contig)
alloc_ctxs[0] = cam->vb_alloc_ctx;
else if (cam->buffer_mode == B_DMA_sg)
alloc_ctxs[0] = cam->vb_alloc_ctx_sg;
+
+ if (*num_planes)
+ return sizes[0] < size ? -EINVAL : 0;
+ sizes[0] = size;
+ *num_planes = 1; /* Someday we have to support planar formats... */
return 0;
}
diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c
index b5f165a68566..816f4b6a7b8e 100644
--- a/drivers/media/platform/marvell-ccic/mmp-driver.c
+++ b/drivers/media/platform/marvell-ccic/mmp-driver.c
@@ -18,7 +18,7 @@
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
-#include <media/mmp-camera.h>
+#include <linux/platform_data/media/mmp-camera.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c
index 03a1b606655d..3c4012d42d69 100644
--- a/drivers/media/platform/mx2_emmaprp.c
+++ b/drivers/media/platform/mx2_emmaprp.c
@@ -375,7 +375,7 @@ static irqreturn_t emmaprp_irq(int irq_emma, void *data)
src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);
- dst_vb->timestamp = src_vb->timestamp;
+ dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
dst_vb->flags &=
~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
dst_vb->flags |=
@@ -689,7 +689,6 @@ static const struct v4l2_ioctl_ops emmaprp_ioctl_ops = {
* Queue operations
*/
static int emmaprp_queue_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c b/drivers/media/platform/omap/omap_vout_vrfb.c
index c6e252760c62..b8638e4e1627 100644
--- a/drivers/media/platform/omap/omap_vout_vrfb.c
+++ b/drivers/media/platform/omap/omap_vout_vrfb.c
@@ -79,10 +79,12 @@ void omap_vout_free_vrfb_buffers(struct omap_vout_device *vout)
int j;
for (j = 0; j < VRFB_NUM_BUFS; j++) {
- omap_vout_free_buffer(vout->smsshado_virt_addr[j],
- vout->smsshado_size);
- vout->smsshado_virt_addr[j] = 0;
- vout->smsshado_phy_addr[j] = 0;
+ if (vout->smsshado_virt_addr[j]) {
+ omap_vout_free_buffer(vout->smsshado_virt_addr[j],
+ vout->smsshado_size);
+ vout->smsshado_virt_addr[j] = 0;
+ vout->smsshado_phy_addr[j] = 0;
+ }
}
}
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index 56e683b19a73..0bcfa553c1aa 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -683,15 +683,15 @@ static irqreturn_t isp_isr(int irq, void *_isp)
*
* Return the total number of users of all video device nodes in the pipeline.
*/
-static int isp_pipeline_pm_use_count(struct media_entity *entity)
+static int isp_pipeline_pm_use_count(struct media_entity *entity,
+ struct media_entity_graph *graph)
{
- struct media_entity_graph graph;
int use = 0;
- media_entity_graph_walk_start(&graph, entity);
+ media_entity_graph_walk_start(graph, entity);
- while ((entity = media_entity_graph_walk_next(&graph))) {
- if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
+ while ((entity = media_entity_graph_walk_next(graph))) {
+ if (is_media_entity_v4l2_io(entity))
use += entity->use_count;
}
@@ -714,7 +714,7 @@ static int isp_pipeline_pm_power_one(struct media_entity *entity, int change)
struct v4l2_subdev *subdev;
int ret;
- subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV
+ subdev = is_media_entity_v4l2_subdev(entity)
? media_entity_to_v4l2_subdev(entity) : NULL;
if (entity->use_count == 0 && change > 0 && subdev != NULL) {
@@ -742,29 +742,29 @@ static int isp_pipeline_pm_power_one(struct media_entity *entity, int change)
*
* Return 0 on success or a negative error code on failure.
*/
-static int isp_pipeline_pm_power(struct media_entity *entity, int change)
+static int isp_pipeline_pm_power(struct media_entity *entity, int change,
+ struct media_entity_graph *graph)
{
- struct media_entity_graph graph;
struct media_entity *first = entity;
int ret = 0;
if (!change)
return 0;
- media_entity_graph_walk_start(&graph, entity);
+ media_entity_graph_walk_start(graph, entity);
- while (!ret && (entity = media_entity_graph_walk_next(&graph)))
- if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+ while (!ret && (entity = media_entity_graph_walk_next(graph)))
+ if (is_media_entity_v4l2_subdev(entity))
ret = isp_pipeline_pm_power_one(entity, change);
if (!ret)
- return 0;
+ return ret;
- media_entity_graph_walk_start(&graph, first);
+ media_entity_graph_walk_start(graph, first);
- while ((first = media_entity_graph_walk_next(&graph))
+ while ((first = media_entity_graph_walk_next(graph))
&& first != entity)
- if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE)
+ if (is_media_entity_v4l2_subdev(first))
isp_pipeline_pm_power_one(first, -change);
return ret;
@@ -782,23 +782,24 @@ static int isp_pipeline_pm_power(struct media_entity *entity, int change)
* off is assumed to never fail. No failure can occur when the use parameter is
* set to 0.
*/
-int omap3isp_pipeline_pm_use(struct media_entity *entity, int use)
+int omap3isp_pipeline_pm_use(struct media_entity *entity, int use,
+ struct media_entity_graph *graph)
{
int change = use ? 1 : -1;
int ret;
- mutex_lock(&entity->parent->graph_mutex);
+ mutex_lock(&entity->graph_obj.mdev->graph_mutex);
/* Apply use count to node. */
entity->use_count += change;
WARN_ON(entity->use_count < 0);
/* Apply power change to connected non-nodes. */
- ret = isp_pipeline_pm_power(entity, change);
+ ret = isp_pipeline_pm_power(entity, change, graph);
if (ret < 0)
entity->use_count -= change;
- mutex_unlock(&entity->parent->graph_mutex);
+ mutex_unlock(&entity->graph_obj.mdev->graph_mutex);
return ret;
}
@@ -820,35 +821,49 @@ int omap3isp_pipeline_pm_use(struct media_entity *entity, int use)
static int isp_pipeline_link_notify(struct media_link *link, u32 flags,
unsigned int notification)
{
+ struct media_entity_graph *graph =
+ &container_of(link->graph_obj.mdev, struct isp_device,
+ media_dev)->pm_count_graph;
struct media_entity *source = link->source->entity;
struct media_entity *sink = link->sink->entity;
- int source_use = isp_pipeline_pm_use_count(source);
- int sink_use = isp_pipeline_pm_use_count(sink);
- int ret;
+ int source_use;
+ int sink_use;
+ int ret = 0;
+
+ if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH) {
+ ret = media_entity_graph_walk_init(graph,
+ link->graph_obj.mdev);
+ if (ret)
+ return ret;
+ }
+
+ source_use = isp_pipeline_pm_use_count(source, graph);
+ sink_use = isp_pipeline_pm_use_count(sink, graph);
if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
!(flags & MEDIA_LNK_FL_ENABLED)) {
/* Powering off entities is assumed to never fail. */
- isp_pipeline_pm_power(source, -sink_use);
- isp_pipeline_pm_power(sink, -source_use);
+ isp_pipeline_pm_power(source, -sink_use, graph);
+ isp_pipeline_pm_power(sink, -source_use, graph);
return 0;
}
if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH &&
(flags & MEDIA_LNK_FL_ENABLED)) {
- ret = isp_pipeline_pm_power(source, sink_use);
+ ret = isp_pipeline_pm_power(source, sink_use, graph);
if (ret < 0)
return ret;
- ret = isp_pipeline_pm_power(sink, source_use);
+ ret = isp_pipeline_pm_power(sink, source_use, graph);
if (ret < 0)
- isp_pipeline_pm_power(source, -sink_use);
-
- return ret;
+ isp_pipeline_pm_power(source, -sink_use, graph);
}
- return 0;
+ if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH)
+ media_entity_graph_walk_cleanup(graph);
+
+ return ret;
}
/* -----------------------------------------------------------------------------
@@ -881,7 +896,7 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe,
* starting entities if the pipeline won't start anyway (those entities
* would then likely fail to stop, making the problem worse).
*/
- if (pipe->entities & isp->crashed)
+ if (media_entity_enum_intersects(&pipe->ent_enum, &isp->crashed))
return -EIO;
spin_lock_irqsave(&pipe->lock, flags);
@@ -897,8 +912,7 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe,
break;
pad = media_entity_remote_pad(pad);
- if (pad == NULL ||
- media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
break;
entity = pad->entity;
@@ -987,8 +1001,7 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe)
break;
pad = media_entity_remote_pad(pad);
- if (pad == NULL ||
- media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
break;
entity = pad->entity;
@@ -1028,7 +1041,8 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe)
dev_info(isp->dev, "Unable to stop %s\n", subdev->name);
isp->stop_failure = true;
if (subdev == &isp->isp_prev.subdev)
- isp->crashed |= 1U << subdev->entity.id;
+ media_entity_enum_set(&isp->crashed,
+ &subdev->entity);
failure = -ETIMEDOUT;
}
}
@@ -1234,7 +1248,7 @@ static int isp_reset(struct isp_device *isp)
}
isp->stop_failure = false;
- isp->crashed = 0;
+ media_entity_enum_zero(&isp->crashed);
return 0;
}
@@ -1645,7 +1659,8 @@ static void __omap3isp_put(struct isp_device *isp, bool save_ctx)
/* Reset the ISP if an entity has failed to stop. This is the
* only way to recover from such conditions.
*/
- if (isp->crashed || isp->stop_failure)
+ if (!media_entity_enum_empty(&isp->crashed) ||
+ isp->stop_failure)
isp_reset(isp);
isp_disable_clocks(isp);
}
@@ -1792,6 +1807,7 @@ static void isp_unregister_entities(struct isp_device *isp)
v4l2_device_unregister(&isp->v4l2_dev);
media_device_unregister(&isp->media_dev);
+ media_device_cleanup(&isp->media_dev);
}
static int isp_link_entity(
@@ -1862,7 +1878,7 @@ static int isp_link_entity(
return -EINVAL;
}
- return media_entity_create_link(entity, i, input, pad, flags);
+ return media_create_pad_link(entity, i, input, pad, flags);
}
static int isp_register_entities(struct isp_device *isp)
@@ -1874,12 +1890,7 @@ static int isp_register_entities(struct isp_device *isp)
sizeof(isp->media_dev.model));
isp->media_dev.hw_revision = isp->revision;
isp->media_dev.link_notify = isp_pipeline_link_notify;
- ret = media_device_register(&isp->media_dev);
- if (ret < 0) {
- dev_err(isp->dev, "%s: Media device registration failed (%d)\n",
- __func__, ret);
- return ret;
- }
+ media_device_init(&isp->media_dev);
isp->v4l2_dev.mdev = &isp->media_dev;
ret = v4l2_device_register(isp->dev, &isp->v4l2_dev);
@@ -1930,6 +1941,118 @@ done:
return ret;
}
+/*
+ * isp_create_links() - Create links for internal and external ISP entities
+ * @isp : Pointer to ISP device
+ *
+ * This function creates all links between ISP internal and external entities.
+ *
+ * Return: A negative error code on failure or zero on success. Possible error
+ * codes are those returned by media_create_pad_link().
+ */
+static int isp_create_links(struct isp_device *isp)
+{
+ int ret;
+
+ /* Create links between entities and video nodes. */
+ ret = media_create_pad_link(
+ &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE,
+ &isp->isp_csi2a.video_out.video.entity, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = media_create_pad_link(
+ &isp->isp_ccp2.video_in.video.entity, 0,
+ &isp->isp_ccp2.subdev.entity, CCP2_PAD_SINK, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = media_create_pad_link(
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF,
+ &isp->isp_ccdc.video_out.video.entity, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = media_create_pad_link(
+ &isp->isp_prev.video_in.video.entity, 0,
+ &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = media_create_pad_link(
+ &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE,
+ &isp->isp_prev.video_out.video.entity, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = media_create_pad_link(
+ &isp->isp_res.video_in.video.entity, 0,
+ &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = media_create_pad_link(
+ &isp->isp_res.subdev.entity, RESZ_PAD_SOURCE,
+ &isp->isp_res.video_out.video.entity, 0, 0);
+
+ if (ret < 0)
+ return ret;
+
+ /* Create links between entities. */
+ ret = media_create_pad_link(
+ &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE,
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = media_create_pad_link(
+ &isp->isp_ccp2.subdev.entity, CCP2_PAD_SOURCE,
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = media_create_pad_link(
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
+ &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = media_create_pad_link(
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF,
+ &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = media_create_pad_link(
+ &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE,
+ &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = media_create_pad_link(
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
+ &isp->isp_aewb.subdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+ if (ret < 0)
+ return ret;
+
+ ret = media_create_pad_link(
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
+ &isp->isp_af.subdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+ if (ret < 0)
+ return ret;
+
+ ret = media_create_pad_link(
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
+ &isp->isp_hist.subdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static void isp_cleanup_modules(struct isp_device *isp)
{
omap3isp_h3a_aewb_cleanup(isp);
@@ -2000,62 +2123,8 @@ static int isp_initialize_modules(struct isp_device *isp)
goto error_h3a_af;
}
- /* Connect the submodules. */
- ret = media_entity_create_link(
- &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE,
- &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(
- &isp->isp_ccp2.subdev.entity, CCP2_PAD_SOURCE,
- &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(
- &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
- &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(
- &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF,
- &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(
- &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE,
- &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(
- &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
- &isp->isp_aewb.subdev.entity, 0,
- MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(
- &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
- &isp->isp_af.subdev.entity, 0,
- MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(
- &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
- &isp->isp_hist.subdev.entity, 0,
- MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
- if (ret < 0)
- goto error_link;
-
return 0;
-error_link:
- omap3isp_h3a_af_cleanup(isp);
error_h3a_af:
omap3isp_h3a_aewb_cleanup(isp);
error_h3a_aewb:
@@ -2149,6 +2218,8 @@ static int isp_remove(struct platform_device *pdev)
isp_detach_iommu(isp);
__omap3isp_put(isp, false);
+ media_entity_enum_cleanup(&isp->crashed);
+
return 0;
}
@@ -2278,28 +2349,43 @@ static int isp_subdev_notifier_bound(struct v4l2_async_notifier *async,
struct v4l2_subdev *subdev,
struct v4l2_async_subdev *asd)
{
- struct isp_device *isp = container_of(async, struct isp_device,
- notifier);
struct isp_async_subdev *isd =
container_of(asd, struct isp_async_subdev, asd);
- int ret;
-
- ret = isp_link_entity(isp, &subdev->entity, isd->bus.interface);
- if (ret < 0)
- return ret;
isd->sd = subdev;
isd->sd->host_priv = &isd->bus;
- return ret;
+ return 0;
}
static int isp_subdev_notifier_complete(struct v4l2_async_notifier *async)
{
struct isp_device *isp = container_of(async, struct isp_device,
notifier);
+ struct v4l2_device *v4l2_dev = &isp->v4l2_dev;
+ struct v4l2_subdev *sd;
+ struct isp_bus_cfg *bus;
+ int ret;
+
+ ret = media_entity_enum_init(&isp->crashed, &isp->media_dev);
+ if (ret)
+ return ret;
- return v4l2_device_register_subdev_nodes(&isp->v4l2_dev);
+ list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
+ /* Only try to link entities whose interface was set on bound */
+ if (sd->host_priv) {
+ bus = (struct isp_bus_cfg *)sd->host_priv;
+ ret = isp_link_entity(isp, &sd->entity, bus->interface);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev);
+ if (ret < 0)
+ return ret;
+
+ return media_device_register(&isp->media_dev);
}
/*
@@ -2465,6 +2551,10 @@ static int isp_probe(struct platform_device *pdev)
if (ret < 0)
goto error_modules;
+ ret = isp_create_links(isp);
+ if (ret < 0)
+ goto error_register_entities;
+
isp->notifier.bound = isp_subdev_notifier_bound;
isp->notifier.complete = isp_subdev_notifier_complete;
diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h
index 5acc2e6511a5..49b7f71ac968 100644
--- a/drivers/media/platform/omap3isp/isp.h
+++ b/drivers/media/platform/omap3isp/isp.h
@@ -17,6 +17,7 @@
#ifndef OMAP3_ISP_CORE_H
#define OMAP3_ISP_CORE_H
+#include <media/media-entity.h>
#include <media/v4l2-async.h>
#include <media/v4l2-device.h>
#include <linux/clk-provider.h>
@@ -152,7 +153,7 @@ struct isp_xclk {
* @stat_lock: Spinlock for handling statistics
* @isp_mutex: Mutex for serializing requests to ISP.
* @stop_failure: Indicates that an entity failed to stop.
- * @crashed: Bitmask of crashed entities (indexed by entity ID)
+ * @crashed: Crashed ent_enum
* @has_context: Context has been saved at least once and can be restored.
* @ref_count: Reference count for handling multiple ISP requests.
* @cam_ick: Pointer to camera interface clock structure.
@@ -176,6 +177,7 @@ struct isp_device {
struct v4l2_device v4l2_dev;
struct v4l2_async_notifier notifier;
struct media_device media_dev;
+ struct media_entity_graph pm_count_graph;
struct device *dev;
u32 revision;
@@ -194,7 +196,7 @@ struct isp_device {
spinlock_t stat_lock; /* common lock for statistic drivers */
struct mutex isp_mutex; /* For handling ref_count field */
bool stop_failure;
- u32 crashed;
+ struct media_entity_enum crashed;
int has_context;
int ref_count;
unsigned int autoidle;
@@ -265,7 +267,8 @@ void omap3isp_subclk_enable(struct isp_device *isp,
void omap3isp_subclk_disable(struct isp_device *isp,
enum isp_subclk_resource res);
-int omap3isp_pipeline_pm_use(struct media_entity *entity, int use);
+int omap3isp_pipeline_pm_use(struct media_entity *entity, int use,
+ struct media_entity_graph *graph);
int omap3isp_register_entities(struct platform_device *pdev,
struct v4l2_device *v4l2_dev);
diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c
index a6a61cce43dd..bb3974c98e37 100644
--- a/drivers/media/platform/omap3isp/ispccdc.c
+++ b/drivers/media/platform/omap3isp/ispccdc.c
@@ -1608,7 +1608,7 @@ static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc)
/* Wait for the CCDC to become idle. */
if (ccdc_sbl_wait_idle(ccdc, 1000)) {
dev_info(isp->dev, "CCDC won't become idle!\n");
- isp->crashed |= 1U << ccdc->subdev.entity.id;
+ media_entity_enum_set(&isp->crashed, &ccdc->subdev.entity);
omap3isp_pipeline_cancel_stream(pipe);
return 0;
}
@@ -2513,9 +2513,14 @@ static int ccdc_link_setup(struct media_entity *entity,
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
struct isp_device *isp = to_isp_device(ccdc);
+ unsigned int index = local->index;
- switch (local->index | media_entity_type(remote->entity)) {
- case CCDC_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* FIXME: this is actually a hack! */
+ if (is_media_entity_v4l2_subdev(remote->entity))
+ index |= 2 << 16;
+
+ switch (index) {
+ case CCDC_PAD_SINK | 2 << 16:
/* Read from the sensor (parallel interface), CCP2, CSI2a or
* CSI2c.
*/
@@ -2543,7 +2548,7 @@ static int ccdc_link_setup(struct media_entity *entity,
* Revisit this when it will be implemented, and return -EBUSY for now.
*/
- case CCDC_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV:
+ case CCDC_PAD_SOURCE_VP | 2 << 16:
/* Write to preview engine, histogram and H3A. When none of
* those links are active, the video port can be disabled.
*/
@@ -2556,7 +2561,7 @@ static int ccdc_link_setup(struct media_entity *entity,
}
break;
- case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_DEVNODE:
+ case CCDC_PAD_SOURCE_OF:
/* Write to memory */
if (flags & MEDIA_LNK_FL_ENABLED) {
if (ccdc->output & ~CCDC_OUTPUT_MEMORY)
@@ -2567,7 +2572,7 @@ static int ccdc_link_setup(struct media_entity *entity,
}
break;
- case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_V4L2_SUBDEV:
+ case CCDC_PAD_SOURCE_OF | 2 << 16:
/* Write to resizer */
if (flags & MEDIA_LNK_FL_ENABLED) {
if (ccdc->output & ~CCDC_OUTPUT_RESIZER)
@@ -2650,7 +2655,7 @@ static int ccdc_init_entities(struct isp_ccdc_device *ccdc)
pads[CCDC_PAD_SOURCE_OF].flags = MEDIA_PAD_FL_SOURCE;
me->ops = &ccdc_media_ops;
- ret = media_entity_init(me, CCDC_PADS_NUM, pads, 0);
+ ret = media_entity_pads_init(me, CCDC_PADS_NUM, pads);
if (ret < 0)
return ret;
@@ -2664,19 +2669,11 @@ static int ccdc_init_entities(struct isp_ccdc_device *ccdc)
ret = omap3isp_video_init(&ccdc->video_out, "CCDC");
if (ret < 0)
- goto error_video;
-
- /* Connect the CCDC subdev to the video node. */
- ret = media_entity_create_link(&ccdc->subdev.entity, CCDC_PAD_SOURCE_OF,
- &ccdc->video_out.video.entity, 0, 0);
- if (ret < 0)
- goto error_link;
+ goto error;
return 0;
-error_link:
- omap3isp_video_cleanup(&ccdc->video_out);
-error_video:
+error:
media_entity_cleanup(me);
return ret;
}
diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c
index 38e6a974c5b1..ca095238510d 100644
--- a/drivers/media/platform/omap3isp/ispccp2.c
+++ b/drivers/media/platform/omap3isp/ispccp2.c
@@ -956,9 +956,14 @@ static int ccp2_link_setup(struct media_entity *entity,
{
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd);
+ unsigned int index = local->index;
- switch (local->index | media_entity_type(remote->entity)) {
- case CCP2_PAD_SINK | MEDIA_ENT_T_DEVNODE:
+ /* FIXME: this is actually a hack! */
+ if (is_media_entity_v4l2_subdev(remote->entity))
+ index |= 2 << 16;
+
+ switch (index) {
+ case CCP2_PAD_SINK:
/* read from memory */
if (flags & MEDIA_LNK_FL_ENABLED) {
if (ccp2->input == CCP2_INPUT_SENSOR)
@@ -970,7 +975,7 @@ static int ccp2_link_setup(struct media_entity *entity,
}
break;
- case CCP2_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ case CCP2_PAD_SINK | 2 << 16:
/* read from sensor/phy */
if (flags & MEDIA_LNK_FL_ENABLED) {
if (ccp2->input == CCP2_INPUT_MEMORY)
@@ -981,7 +986,7 @@ static int ccp2_link_setup(struct media_entity *entity,
ccp2->input = CCP2_INPUT_NONE;
} break;
- case CCP2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ case CCP2_PAD_SOURCE | 2 << 16:
/* write to video port/ccdc */
if (flags & MEDIA_LNK_FL_ENABLED)
ccp2->output = CCP2_OUTPUT_CCDC;
@@ -1071,7 +1076,7 @@ static int ccp2_init_entities(struct isp_ccp2_device *ccp2)
pads[CCP2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
me->ops = &ccp2_media_ops;
- ret = media_entity_init(me, CCP2_PADS_NUM, pads, 0);
+ ret = media_entity_pads_init(me, CCP2_PADS_NUM, pads);
if (ret < 0)
return ret;
@@ -1097,19 +1102,11 @@ static int ccp2_init_entities(struct isp_ccp2_device *ccp2)
ret = omap3isp_video_init(&ccp2->video_in, "CCP2");
if (ret < 0)
- goto error_video;
-
- /* Connect the video node to the ccp2 subdev. */
- ret = media_entity_create_link(&ccp2->video_in.video.entity, 0,
- &ccp2->subdev.entity, CCP2_PAD_SINK, 0);
- if (ret < 0)
- goto error_link;
+ goto error;
return 0;
-error_link:
- omap3isp_video_cleanup(&ccp2->video_in);
-error_video:
+error:
media_entity_cleanup(&ccp2->subdev.entity);
return ret;
}
diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c
index a78338d012b4..f75a1be29d84 100644
--- a/drivers/media/platform/omap3isp/ispcsi2.c
+++ b/drivers/media/platform/omap3isp/ispcsi2.c
@@ -1144,14 +1144,19 @@ static int csi2_link_setup(struct media_entity *entity,
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
struct isp_csi2_ctrl_cfg *ctrl = &csi2->ctrl;
+ unsigned int index = local->index;
/*
* The ISP core doesn't support pipelines with multiple video outputs.
* Revisit this when it will be implemented, and return -EBUSY for now.
*/
- switch (local->index | media_entity_type(remote->entity)) {
- case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+ /* FIXME: this is actually a hack! */
+ if (is_media_entity_v4l2_subdev(remote->entity))
+ index |= 2 << 16;
+
+ switch (index) {
+ case CSI2_PAD_SOURCE:
if (flags & MEDIA_LNK_FL_ENABLED) {
if (csi2->output & ~CSI2_OUTPUT_MEMORY)
return -EBUSY;
@@ -1161,7 +1166,7 @@ static int csi2_link_setup(struct media_entity *entity,
}
break;
- case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ case CSI2_PAD_SOURCE | 2 << 16:
if (flags & MEDIA_LNK_FL_ENABLED) {
if (csi2->output & ~CSI2_OUTPUT_CCDC)
return -EBUSY;
@@ -1245,7 +1250,7 @@ static int csi2_init_entities(struct isp_csi2_device *csi2)
| MEDIA_PAD_FL_MUST_CONNECT;
me->ops = &csi2_media_ops;
- ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0);
+ ret = media_entity_pads_init(me, CSI2_PADS_NUM, pads);
if (ret < 0)
return ret;
@@ -1264,16 +1269,8 @@ static int csi2_init_entities(struct isp_csi2_device *csi2)
if (ret < 0)
goto error_video;
- /* Connect the CSI2 subdev to the video node. */
- ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE,
- &csi2->video_out.video.entity, 0, 0);
- if (ret < 0)
- goto error_link;
-
return 0;
-error_link:
- omap3isp_video_cleanup(&csi2->video_out);
error_video:
media_entity_cleanup(&csi2->subdev.entity);
return ret;
diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c
index 13803270d104..84a96670e2e7 100644
--- a/drivers/media/platform/omap3isp/isppreview.c
+++ b/drivers/media/platform/omap3isp/isppreview.c
@@ -2144,9 +2144,14 @@ static int preview_link_setup(struct media_entity *entity,
{
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+ unsigned int index = local->index;
- switch (local->index | media_entity_type(remote->entity)) {
- case PREV_PAD_SINK | MEDIA_ENT_T_DEVNODE:
+ /* FIXME: this is actually a hack! */
+ if (is_media_entity_v4l2_subdev(remote->entity))
+ index |= 2 << 16;
+
+ switch (index) {
+ case PREV_PAD_SINK:
/* read from memory */
if (flags & MEDIA_LNK_FL_ENABLED) {
if (prev->input == PREVIEW_INPUT_CCDC)
@@ -2158,7 +2163,7 @@ static int preview_link_setup(struct media_entity *entity,
}
break;
- case PREV_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ case PREV_PAD_SINK | 2 << 16:
/* read from ccdc */
if (flags & MEDIA_LNK_FL_ENABLED) {
if (prev->input == PREVIEW_INPUT_MEMORY)
@@ -2175,7 +2180,7 @@ static int preview_link_setup(struct media_entity *entity,
* Revisit this when it will be implemented, and return -EBUSY for now.
*/
- case PREV_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+ case PREV_PAD_SOURCE:
/* write to memory */
if (flags & MEDIA_LNK_FL_ENABLED) {
if (prev->output & ~PREVIEW_OUTPUT_MEMORY)
@@ -2186,7 +2191,7 @@ static int preview_link_setup(struct media_entity *entity,
}
break;
- case PREV_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ case PREV_PAD_SOURCE | 2 << 16:
/* write to resizer */
if (flags & MEDIA_LNK_FL_ENABLED) {
if (prev->output & ~PREVIEW_OUTPUT_RESIZER)
@@ -2282,7 +2287,7 @@ static int preview_init_entities(struct isp_prev_device *prev)
pads[PREV_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
me->ops = &preview_media_ops;
- ret = media_entity_init(me, PREV_PADS_NUM, pads, 0);
+ ret = media_entity_pads_init(me, PREV_PADS_NUM, pads);
if (ret < 0)
return ret;
@@ -2311,21 +2316,8 @@ static int preview_init_entities(struct isp_prev_device *prev)
if (ret < 0)
goto error_video_out;
- /* Connect the video nodes to the previewer subdev. */
- ret = media_entity_create_link(&prev->video_in.video.entity, 0,
- &prev->subdev.entity, PREV_PAD_SINK, 0);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(&prev->subdev.entity, PREV_PAD_SOURCE,
- &prev->video_out.video.entity, 0, 0);
- if (ret < 0)
- goto error_link;
-
return 0;
-error_link:
- omap3isp_video_cleanup(&prev->video_out);
error_video_out:
omap3isp_video_cleanup(&prev->video_in);
error_video_in:
diff --git a/drivers/media/platform/omap3isp/ispresizer.c b/drivers/media/platform/omap3isp/ispresizer.c
index 7cfb43dc0ffd..0b6a87508584 100644
--- a/drivers/media/platform/omap3isp/ispresizer.c
+++ b/drivers/media/platform/omap3isp/ispresizer.c
@@ -1623,9 +1623,14 @@ static int resizer_link_setup(struct media_entity *entity,
{
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
struct isp_res_device *res = v4l2_get_subdevdata(sd);
+ unsigned int index = local->index;
- switch (local->index | media_entity_type(remote->entity)) {
- case RESZ_PAD_SINK | MEDIA_ENT_T_DEVNODE:
+ /* FIXME: this is actually a hack! */
+ if (is_media_entity_v4l2_subdev(remote->entity))
+ index |= 2 << 16;
+
+ switch (index) {
+ case RESZ_PAD_SINK:
/* read from memory */
if (flags & MEDIA_LNK_FL_ENABLED) {
if (res->input == RESIZER_INPUT_VP)
@@ -1637,7 +1642,7 @@ static int resizer_link_setup(struct media_entity *entity,
}
break;
- case RESZ_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ case RESZ_PAD_SINK | 2 << 16:
/* read from ccdc or previewer */
if (flags & MEDIA_LNK_FL_ENABLED) {
if (res->input == RESIZER_INPUT_MEMORY)
@@ -1649,7 +1654,7 @@ static int resizer_link_setup(struct media_entity *entity,
}
break;
- case RESZ_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+ case RESZ_PAD_SOURCE:
/* resizer always write to memory */
break;
@@ -1728,7 +1733,7 @@ static int resizer_init_entities(struct isp_res_device *res)
pads[RESZ_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
me->ops = &resizer_media_ops;
- ret = media_entity_init(me, RESZ_PADS_NUM, pads, 0);
+ ret = media_entity_pads_init(me, RESZ_PADS_NUM, pads);
if (ret < 0)
return ret;
@@ -1755,21 +1760,8 @@ static int resizer_init_entities(struct isp_res_device *res)
res->video_out.video.entity.flags |= MEDIA_ENT_FL_DEFAULT;
- /* Connect the video nodes to the resizer subdev. */
- ret = media_entity_create_link(&res->video_in.video.entity, 0,
- &res->subdev.entity, RESZ_PAD_SINK, 0);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(&res->subdev.entity, RESZ_PAD_SOURCE,
- &res->video_out.video.entity, 0, 0);
- if (ret < 0)
- goto error_link;
-
return 0;
-error_link:
- omap3isp_video_cleanup(&res->video_out);
error_video_out:
omap3isp_video_cleanup(&res->video_in);
error_video_in:
diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c
index 94d4c295d3d0..1b9217d3b1b6 100644
--- a/drivers/media/platform/omap3isp/ispstat.c
+++ b/drivers/media/platform/omap3isp/ispstat.c
@@ -1028,7 +1028,7 @@ static int isp_stat_init_entities(struct ispstat *stat, const char *name,
stat->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
me->ops = NULL;
- return media_entity_init(me, 1, &stat->pad, 0);
+ return media_entity_pads_init(me, 1, &stat->pad);
}
int omap3isp_stat_init(struct ispstat *stat, const char *name,
diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c
index f4f591652432..994dfc0813f6 100644
--- a/drivers/media/platform/omap3isp/ispvideo.c
+++ b/drivers/media/platform/omap3isp/ispvideo.c
@@ -210,8 +210,7 @@ isp_video_remote_subdev(struct isp_video *video, u32 *pad)
remote = media_entity_remote_pad(&video->pad);
- if (remote == NULL ||
- media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (!remote || !is_media_entity_v4l2_subdev(remote->entity))
return NULL;
if (pad)
@@ -226,16 +225,23 @@ static int isp_video_get_graph_data(struct isp_video *video,
{
struct media_entity_graph graph;
struct media_entity *entity = &video->video.entity;
- struct media_device *mdev = entity->parent;
+ struct media_device *mdev = entity->graph_obj.mdev;
struct isp_video *far_end = NULL;
+ int ret;
mutex_lock(&mdev->graph_mutex);
+ ret = media_entity_graph_walk_init(&graph, entity->graph_obj.mdev);
+ if (ret) {
+ mutex_unlock(&mdev->graph_mutex);
+ return ret;
+ }
+
media_entity_graph_walk_start(&graph, entity);
while ((entity = media_entity_graph_walk_next(&graph))) {
struct isp_video *__video;
- pipe->entities |= 1 << entity->id;
+ media_entity_enum_set(&pipe->ent_enum, entity);
if (far_end != NULL)
continue;
@@ -243,7 +249,7 @@ static int isp_video_get_graph_data(struct isp_video *video,
if (entity == &video->video.entity)
continue;
- if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+ if (!is_media_entity_v4l2_io(entity))
continue;
__video = to_isp_video(media_entity_to_video_device(entity));
@@ -253,6 +259,8 @@ static int isp_video_get_graph_data(struct isp_video *video,
mutex_unlock(&mdev->graph_mutex);
+ media_entity_graph_walk_cleanup(&graph);
+
if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
pipe->input = far_end;
pipe->output = video;
@@ -320,7 +328,6 @@ isp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh)
*/
static int isp_video_queue_setup(struct vb2_queue *queue,
- const void *parg,
unsigned int *count, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
@@ -467,7 +474,7 @@ struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)
list_del(&buf->irqlist);
spin_unlock_irqrestore(&video->irqlock, flags);
- v4l2_get_timestamp(&buf->vb.timestamp);
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
/* Do frame number propagation only if this is the output video node.
* Frame number either comes from the CSI receivers or it gets
@@ -901,7 +908,7 @@ static int isp_video_check_external_subdevs(struct isp_video *video,
for (i = 0; i < ARRAY_SIZE(ents); i++) {
/* Is the entity part of the pipeline? */
- if (!(pipe->entities & (1 << ents[i]->id)))
+ if (!media_entity_enum_test(&pipe->ent_enum, ents[i]))
continue;
/* ISP entities have always sink pad == 0. Find source. */
@@ -919,7 +926,7 @@ static int isp_video_check_external_subdevs(struct isp_video *video,
return -EINVAL;
}
- if (media_entity_type(source) != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (!is_media_entity_v4l2_subdev(source))
return 0;
pipe->external = media_entity_to_v4l2_subdev(source);
@@ -953,7 +960,8 @@ static int isp_video_check_external_subdevs(struct isp_video *video,
pipe->external_rate = ctrl.value64;
- if (pipe->entities & (1 << isp->isp_ccdc.subdev.entity.id)) {
+ if (media_entity_enum_test(&pipe->ent_enum,
+ &isp->isp_ccdc.subdev.entity)) {
unsigned int rate = UINT_MAX;
/*
* Check that maximum allowed CCDC pixel rate isn't
@@ -1019,7 +1027,9 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
pipe = video->video.entity.pipe
? to_isp_pipeline(&video->video.entity) : &video->pipe;
- pipe->entities = 0;
+ ret = media_entity_enum_init(&pipe->ent_enum, &video->isp->media_dev);
+ if (ret)
+ goto err_enum_init;
/* TODO: Implement PM QoS */
pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]);
@@ -1093,6 +1103,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
}
mutex_unlock(&video->stream_lock);
+
return 0;
err_set_stream:
@@ -1113,7 +1124,11 @@ err_pipeline_start:
INIT_LIST_HEAD(&video->dmaqueue);
video->queue = NULL;
+ media_entity_enum_cleanup(&pipe->ent_enum);
+
+err_enum_init:
mutex_unlock(&video->stream_lock);
+
return ret;
}
@@ -1165,6 +1180,8 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
/* TODO: Implement PM QoS */
media_entity_pipeline_stop(&video->video.entity);
+ media_entity_enum_cleanup(&pipe->ent_enum);
+
done:
mutex_unlock(&video->stream_lock);
return 0;
@@ -1244,7 +1261,12 @@ static int isp_video_open(struct file *file)
goto done;
}
- ret = omap3isp_pipeline_pm_use(&video->video.entity, 1);
+ ret = media_entity_graph_walk_init(&handle->graph,
+ &video->isp->media_dev);
+ if (ret)
+ goto done;
+
+ ret = omap3isp_pipeline_pm_use(&video->video.entity, 1, &handle->graph);
if (ret < 0) {
omap3isp_put(video->isp);
goto done;
@@ -1275,6 +1297,7 @@ static int isp_video_open(struct file *file)
done:
if (ret < 0) {
v4l2_fh_del(&handle->vfh);
+ media_entity_graph_walk_cleanup(&handle->graph);
kfree(handle);
}
@@ -1294,7 +1317,8 @@ static int isp_video_release(struct file *file)
vb2_queue_release(&handle->queue);
mutex_unlock(&video->queue_lock);
- omap3isp_pipeline_pm_use(&video->video.entity, 0);
+ omap3isp_pipeline_pm_use(&video->video.entity, 0, &handle->graph);
+ media_entity_graph_walk_cleanup(&handle->graph);
/* Release the file handle. */
v4l2_fh_del(vfh);
@@ -1368,7 +1392,7 @@ int omap3isp_video_init(struct isp_video *video, const char *name)
if (IS_ERR(video->alloc_ctx))
return PTR_ERR(video->alloc_ctx);
- ret = media_entity_init(&video->video.entity, 1, &video->pad, 0);
+ ret = media_entity_pads_init(&video->video.entity, 1, &video->pad);
if (ret < 0) {
vb2_dma_contig_cleanup_ctx(video->alloc_ctx);
return ret;
diff --git a/drivers/media/platform/omap3isp/ispvideo.h b/drivers/media/platform/omap3isp/ispvideo.h
index bcf0e0acc8f3..156429878d64 100644
--- a/drivers/media/platform/omap3isp/ispvideo.h
+++ b/drivers/media/platform/omap3isp/ispvideo.h
@@ -80,7 +80,7 @@ enum isp_pipeline_state {
* struct isp_pipeline - An ISP hardware pipeline
* @field: The field being processed by the pipeline
* @error: A hardware error occurred during capture
- * @entities: Bitmask of entities in the pipeline (indexed by entity ID)
+ * @ent_enum: Entities in the pipeline
*/
struct isp_pipeline {
struct media_pipeline pipe;
@@ -89,7 +89,7 @@ struct isp_pipeline {
enum isp_pipeline_stream_state stream_state;
struct isp_video *input;
struct isp_video *output;
- u32 entities;
+ struct media_entity_enum ent_enum;
unsigned long l3_ick;
unsigned int max_rate;
enum v4l2_field field;
@@ -189,6 +189,7 @@ struct isp_video_fh {
struct vb2_queue queue;
struct v4l2_format format;
struct v4l2_fract timeperframe;
+ struct media_entity_graph graph;
};
#define to_isp_video_fh(fh) container_of(fh, struct isp_video_fh, vfh)
diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c
index f8e3e83c52a2..485f5259acb0 100644
--- a/drivers/media/platform/rcar_jpu.c
+++ b/drivers/media/platform/rcar_jpu.c
@@ -1015,28 +1015,33 @@ error_free:
* ============================================================================
*/
static int jpu_queue_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct jpu_ctx *ctx = vb2_get_drv_priv(vq);
struct jpu_q_data *q_data;
unsigned int i;
q_data = jpu_get_q_data(ctx, vq->type);
- *nplanes = q_data->format.num_planes;
+ if (*nplanes) {
+ if (*nplanes != q_data->format.num_planes)
+ return -EINVAL;
- for (i = 0; i < *nplanes; i++) {
- unsigned int q_size = q_data->format.plane_fmt[i].sizeimage;
- unsigned int f_size = fmt ?
- fmt->fmt.pix_mp.plane_fmt[i].sizeimage : 0;
+ for (i = 0; i < *nplanes; i++) {
+ unsigned int q_size = q_data->format.plane_fmt[i].sizeimage;
- if (fmt && f_size < q_size)
- return -EINVAL;
+ if (sizes[i] < q_size)
+ return -EINVAL;
+ alloc_ctxs[i] = ctx->jpu->alloc_ctx;
+ }
+ return 0;
+ }
- sizes[i] = fmt ? f_size : q_size;
+ *nplanes = q_data->format.num_planes;
+
+ for (i = 0; i < *nplanes; i++) {
+ sizes[i] = q_data->format.plane_fmt[i].sizeimage;
alloc_ctxs[i] = ctx->jpu->alloc_ctx;
}
@@ -1300,17 +1305,17 @@ static int jpu_release(struct file *file)
struct jpu *jpu = video_drvdata(file);
struct jpu_ctx *ctx = fh_to_ctx(file->private_data);
- mutex_lock(&jpu->mutex);
- if (--jpu->ref_count == 0)
- clk_disable_unprepare(jpu->clk);
- mutex_unlock(&jpu->mutex);
-
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
v4l2_ctrl_handler_free(&ctx->ctrl_handler);
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
kfree(ctx);
+ mutex_lock(&jpu->mutex);
+ if (--jpu->ref_count == 0)
+ clk_disable_unprepare(jpu->clk);
+ mutex_unlock(&jpu->mutex);
+
return 0;
}
@@ -1560,12 +1565,9 @@ static irqreturn_t jpu_irq_handler(int irq, void *dev_id)
}
dst_buf->field = src_buf->field;
- dst_buf->timestamp = src_buf->timestamp;
+ dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp;
if (src_buf->flags & V4L2_BUF_FLAG_TIMECODE)
dst_buf->timecode = src_buf->timecode;
- dst_buf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
- dst_buf->flags |= src_buf->flags &
- V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
dst_buf->flags = src_buf->flags &
(V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_KEYFRAME |
V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME |
diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c
index 537b858cb94a..bd060ef5d1e1 100644
--- a/drivers/media/platform/s3c-camif/camif-capture.c
+++ b/drivers/media/platform/s3c-camif/camif-capture.c
@@ -338,7 +338,7 @@ irqreturn_t s3c_camif_irq_handler(int irq, void *priv)
if (!WARN_ON(vbuf == NULL)) {
/* Dequeue a filled buffer */
- v4l2_get_timestamp(&vbuf->vb.timestamp);
+ vbuf->vb.vb2_buf.timestamp = ktime_get_ns();
vbuf->vb.sequence = vp->frame_sequence++;
vb2_buffer_done(&vbuf->vb.vb2_buf, VB2_BUF_STATE_DONE);
@@ -435,39 +435,28 @@ static void stop_streaming(struct vb2_queue *vq)
camif_stop_capture(vp);
}
-static int queue_setup(struct vb2_queue *vq, const void *parg,
+static int queue_setup(struct vb2_queue *vq,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *allocators[])
{
- const struct v4l2_format *pfmt = parg;
- const struct v4l2_pix_format *pix = NULL;
struct camif_vp *vp = vb2_get_drv_priv(vq);
struct camif_dev *camif = vp->camif;
struct camif_frame *frame = &vp->out_frame;
- const struct camif_fmt *fmt;
+ const struct camif_fmt *fmt = vp->out_fmt;
unsigned int size;
- if (pfmt) {
- pix = &pfmt->fmt.pix;
- fmt = s3c_camif_find_format(vp, &pix->pixelformat, -1);
- if (fmt == NULL)
- return -EINVAL;
- size = (pix->width * pix->height * fmt->depth) / 8;
- } else {
- fmt = vp->out_fmt;
- if (fmt == NULL)
- return -EINVAL;
- size = (frame->f_width * frame->f_height * fmt->depth) / 8;
- }
-
- *num_planes = 1;
+ if (fmt == NULL)
+ return -EINVAL;
- if (pix)
- sizes[0] = max(size, pix->sizeimage);
- else
- sizes[0] = size;
+ size = (frame->f_width * frame->f_height * fmt->depth) / 8;
allocators[0] = camif->alloc_ctx;
+ if (*num_planes)
+ return sizes[0] < size ? -EINVAL : 0;
+
+ *num_planes = 1;
+ sizes[0] = size;
+
pr_debug("size: %u\n", sizes[0]);
return 0;
}
@@ -833,7 +822,7 @@ static int camif_pipeline_validate(struct camif_dev *camif)
/* Retrieve format at the sensor subdev source pad */
pad = media_entity_remote_pad(&camif->pads[0]);
- if (!pad || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
return -EPIPE;
src_fmt.pad = pad->index;
@@ -1155,7 +1144,7 @@ int s3c_camif_register_video_node(struct camif_dev *camif, int idx)
goto err_vd_rel;
vp->pad.flags = MEDIA_PAD_FL_SINK;
- ret = media_entity_init(&vfd->entity, 1, &vp->pad, 0);
+ ret = media_entity_pads_init(&vfd->entity, 1, &vp->pad);
if (ret)
goto err_vd_rel;
@@ -1570,8 +1559,8 @@ int s3c_camif_create_subdev(struct camif_dev *camif)
camif->pads[CAMIF_SD_PAD_SOURCE_C].flags = MEDIA_PAD_FL_SOURCE;
camif->pads[CAMIF_SD_PAD_SOURCE_P].flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&sd->entity, CAMIF_SD_PADS_NUM,
- camif->pads, 0);
+ ret = media_entity_pads_init(&sd->entity, CAMIF_SD_PADS_NUM,
+ camif->pads);
if (ret)
return ret;
diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c
index 1ba9bb08f5da..0b44b9accf50 100644
--- a/drivers/media/platform/s3c-camif/camif-core.c
+++ b/drivers/media/platform/s3c-camif/camif-core.c
@@ -263,7 +263,7 @@ static int camif_create_media_links(struct camif_dev *camif)
{
int i, ret;
- ret = media_entity_create_link(&camif->sensor.sd->entity, 0,
+ ret = media_create_pad_link(&camif->sensor.sd->entity, 0,
&camif->subdev.entity, CAMIF_SD_PAD_SINK,
MEDIA_LNK_FL_IMMUTABLE |
MEDIA_LNK_FL_ENABLED);
@@ -271,7 +271,7 @@ static int camif_create_media_links(struct camif_dev *camif)
return ret;
for (i = 1; i < CAMIF_SD_PADS_NUM && !ret; i++) {
- ret = media_entity_create_link(&camif->subdev.entity, i,
+ ret = media_create_pad_link(&camif->subdev.entity, i,
&camif->vp[i - 1].vdev.entity, 0,
MEDIA_LNK_FL_IMMUTABLE |
MEDIA_LNK_FL_ENABLED);
@@ -305,7 +305,7 @@ static void camif_unregister_media_entities(struct camif_dev *camif)
/*
* Media device
*/
-static int camif_media_dev_register(struct camif_dev *camif)
+static int camif_media_dev_init(struct camif_dev *camif)
{
struct media_device *md = &camif->media_dev;
struct v4l2_device *v4l2_dev = &camif->v4l2_dev;
@@ -324,14 +324,12 @@ static int camif_media_dev_register(struct camif_dev *camif)
strlcpy(v4l2_dev->name, "s3c-camif", sizeof(v4l2_dev->name));
v4l2_dev->mdev = md;
+ media_device_init(md);
+
ret = v4l2_device_register(camif->dev, v4l2_dev);
if (ret < 0)
return ret;
- ret = media_device_register(md);
- if (ret < 0)
- v4l2_device_unregister(v4l2_dev);
-
return ret;
}
@@ -483,7 +481,7 @@ static int s3c_camif_probe(struct platform_device *pdev)
goto err_alloc;
}
- ret = camif_media_dev_register(camif);
+ ret = camif_media_dev_init(camif);
if (ret < 0)
goto err_mdev;
@@ -510,6 +508,11 @@ static int s3c_camif_probe(struct platform_device *pdev)
goto err_unlock;
mutex_unlock(&camif->media_dev.graph_mutex);
+
+ ret = media_device_register(&camif->media_dev);
+ if (ret < 0)
+ goto err_sens;
+
pm_runtime_put(dev);
return 0;
@@ -518,6 +521,7 @@ err_unlock:
err_sens:
v4l2_device_unregister(&camif->v4l2_dev);
media_device_unregister(&camif->media_dev);
+ media_device_cleanup(&camif->media_dev);
camif_unregister_media_entities(camif);
err_mdev:
vb2_dma_contig_cleanup_ctx(camif->alloc_ctx);
@@ -539,6 +543,7 @@ static int s3c_camif_remove(struct platform_device *pdev)
struct s3c_camif_plat_data *pdata = &camif->pdata;
media_device_unregister(&camif->media_dev);
+ media_device_cleanup(&camif->media_dev);
camif_unregister_media_entities(camif);
v4l2_device_unregister(&camif->v4l2_dev);
diff --git a/drivers/media/platform/s3c-camif/camif-core.h b/drivers/media/platform/s3c-camif/camif-core.h
index adaf1969ef63..57cbc3d9725d 100644
--- a/drivers/media/platform/s3c-camif/camif-core.h
+++ b/drivers/media/platform/s3c-camif/camif-core.h
@@ -26,7 +26,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-mediabus.h>
#include <media/videobuf2-v4l2.h>
-#include <media/s3c_camif.h>
+#include <media/drv-intf/s3c_camif.h>
#define S3C_CAMIF_DRIVER_NAME "s3c-camif"
#define CAMIF_REQ_BUFS_MIN 3
diff --git a/drivers/media/platform/s3c-camif/camif-regs.h b/drivers/media/platform/s3c-camif/camif-regs.h
index af2d472ea1dd..5ad36c1c2a5d 100644
--- a/drivers/media/platform/s3c-camif/camif-regs.h
+++ b/drivers/media/platform/s3c-camif/camif-regs.h
@@ -13,7 +13,7 @@
#define CAMIF_REGS_H_
#include "camif-core.h"
-#include <media/s3c_camif.h>
+#include <media/drv-intf/s3c_camif.h>
/*
* The id argument indicates the processing path:
diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c
index e1936d9d27da..74bd46ca7942 100644
--- a/drivers/media/platform/s5p-g2d/g2d.c
+++ b/drivers/media/platform/s5p-g2d/g2d.c
@@ -101,7 +101,7 @@ static struct g2d_frame *get_frame(struct g2d_ctx *ctx,
}
}
-static int g2d_queue_setup(struct vb2_queue *vq, const void *parg,
+static int g2d_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
@@ -552,7 +552,7 @@ static irqreturn_t g2d_isr(int irq, void *prv)
BUG_ON(dst == NULL);
dst->timecode = src->timecode;
- dst->timestamp = src->timestamp;
+ dst->vb2_buf.timestamp = src->vb2_buf.timestamp;
dst->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
dst->flags |=
src->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c
index 4a608cbe0fdb..c3b13a630edf 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-core.c
+++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c
@@ -2430,7 +2430,6 @@ static struct v4l2_m2m_ops exynos4_jpeg_m2m_ops = {
*/
static int s5p_jpeg_queue_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
@@ -2621,7 +2620,7 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id)
}
dst_buf->timecode = src_buf->timecode;
- dst_buf->timestamp = src_buf->timestamp;
+ dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp;
dst_buf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
dst_buf->flags |=
src_buf->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
@@ -2752,7 +2751,7 @@ static irqreturn_t exynos3250_jpeg_irq(int irq, void *dev_id)
dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx);
dst_buf->timecode = src_buf->timecode;
- dst_buf->timestamp = src_buf->timestamp;
+ dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp;
v4l2_m2m_buf_done(src_buf, state);
if (curr_ctx->mode == S5P_JPEG_ENCODE)
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index 3ffe2ecfd5ef..927ab4928779 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -85,6 +85,26 @@ void set_work_bit_irqsave(struct s5p_mfc_ctx *ctx)
spin_unlock_irqrestore(&dev->condlock, flags);
}
+int s5p_mfc_get_new_ctx(struct s5p_mfc_dev *dev)
+{
+ unsigned long flags;
+ int ctx;
+
+ spin_lock_irqsave(&dev->condlock, flags);
+ ctx = dev->curr_ctx;
+ do {
+ ctx = (ctx + 1) % MFC_NUM_CONTEXTS;
+ if (ctx == dev->curr_ctx) {
+ if (!test_bit(ctx, &dev->ctx_work_bits))
+ ctx = -EAGAIN;
+ break;
+ }
+ } while (!test_bit(ctx, &dev->ctx_work_bits));
+ spin_unlock_irqrestore(&dev->condlock, flags);
+
+ return ctx;
+}
+
/* Wake up context wait_queue */
static void wake_up_ctx(struct s5p_mfc_ctx *ctx, unsigned int reason,
unsigned int err)
@@ -105,6 +125,20 @@ static void wake_up_dev(struct s5p_mfc_dev *dev, unsigned int reason,
wake_up(&dev->queue);
}
+void s5p_mfc_cleanup_queue(struct list_head *lh, struct vb2_queue *vq)
+{
+ struct s5p_mfc_buf *b;
+ int i;
+
+ while (!list_empty(lh)) {
+ b = list_entry(lh->next, struct s5p_mfc_buf, list);
+ for (i = 0; i < b->b->vb2_buf.num_planes; i++)
+ vb2_set_plane_payload(&b->b->vb2_buf, i, 0);
+ vb2_buffer_done(&b->b->vb2_buf, VB2_BUF_STATE_ERROR);
+ list_del(&b->list);
+ }
+}
+
static void s5p_mfc_watchdog(unsigned long arg)
{
struct s5p_mfc_dev *dev = (struct s5p_mfc_dev *)arg;
@@ -150,10 +184,8 @@ static void s5p_mfc_watchdog_worker(struct work_struct *work)
if (!ctx)
continue;
ctx->state = MFCINST_ERROR;
- s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
- &ctx->dst_queue, &ctx->vq_dst);
- s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
- &ctx->src_queue, &ctx->vq_src);
+ s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
+ s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
clear_work_bit(ctx);
wake_up_ctx(ctx, S5P_MFC_R2H_CMD_ERR_RET, 0);
}
@@ -233,8 +265,8 @@ static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx)
== dec_y_addr) {
dst_buf->b->timecode =
src_buf->b->timecode;
- dst_buf->b->timestamp =
- src_buf->b->timestamp;
+ dst_buf->b->vb2_buf.timestamp =
+ src_buf->b->vb2_buf.timestamp;
dst_buf->b->flags &=
~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
dst_buf->b->flags |=
@@ -327,7 +359,6 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,
unsigned int dst_frame_status;
unsigned int dec_frame_status;
struct s5p_mfc_buf *src_buf;
- unsigned long flags;
unsigned int res_change;
dst_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev)
@@ -343,17 +374,16 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,
if (res_change == S5P_FIMV_RES_INCREASE ||
res_change == S5P_FIMV_RES_DECREASE) {
ctx->state = MFCINST_RES_CHANGE_INIT;
- s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
wake_up_ctx(ctx, reason, err);
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_clock_off();
- s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
return;
}
if (ctx->dpb_flush_flag)
ctx->dpb_flush_flag = 0;
- spin_lock_irqsave(&dev->irqlock, flags);
/* All frames remaining in the buffer have been extracted */
if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_EMPTY) {
if (ctx->state == MFCINST_RES_CHANGE_FLUSH) {
@@ -413,11 +443,10 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,
}
}
leave_handle_frame:
- spin_unlock_irqrestore(&dev->irqlock, flags);
if ((ctx->src_queue_cnt == 0 && ctx->state != MFCINST_FINISHING)
|| ctx->dst_queue_cnt < ctx->pb_count)
clear_work_bit(ctx);
- s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
wake_up_ctx(ctx, reason, err);
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_clock_off();
@@ -425,15 +454,13 @@ leave_handle_frame:
if (test_bit(0, &dev->enter_suspend))
wake_up_dev(dev, reason, err);
else
- s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
}
/* Error handling for interrupt */
static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev,
struct s5p_mfc_ctx *ctx, unsigned int reason, unsigned int err)
{
- unsigned long flags;
-
mfc_err("Interrupt Error: %08x\n", err);
if (ctx != NULL) {
@@ -450,13 +477,9 @@ static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev,
clear_work_bit(ctx);
ctx->state = MFCINST_ERROR;
/* Mark all dst buffers as having an error */
- spin_lock_irqsave(&dev->irqlock, flags);
- s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
- &ctx->dst_queue, &ctx->vq_dst);
+ s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
/* Mark all src buffers as having an error */
- s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
- &ctx->src_queue, &ctx->vq_src);
- spin_unlock_irqrestore(&dev->irqlock, flags);
+ s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
wake_up_ctx(ctx, reason, err);
break;
default:
@@ -467,7 +490,7 @@ static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev,
}
}
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
- s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
s5p_mfc_clock_off();
wake_up_dev(dev, reason, err);
return;
@@ -491,7 +514,7 @@ static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx,
ctx->img_height = s5p_mfc_hw_call(dev->mfc_ops, get_img_height,
dev);
- s5p_mfc_hw_call_void(dev->mfc_ops, dec_calc_dpb_size, ctx);
+ s5p_mfc_hw_call(dev->mfc_ops, dec_calc_dpb_size, ctx);
ctx->pb_count = s5p_mfc_hw_call(dev->mfc_ops, get_dpb_count,
dev);
@@ -518,11 +541,11 @@ static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx,
ctx->head_processed = 1;
}
}
- s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
clear_work_bit(ctx);
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_clock_off();
- s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
wake_up_ctx(ctx, reason, err);
}
@@ -532,12 +555,11 @@ static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx,
{
struct s5p_mfc_buf *src_buf;
struct s5p_mfc_dev *dev;
- unsigned long flags;
if (ctx == NULL)
return;
dev = ctx->dev;
- s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
ctx->int_type = reason;
ctx->int_err = err;
ctx->int_cond = 1;
@@ -545,7 +567,6 @@ static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx,
if (err == 0) {
ctx->state = MFCINST_RUNNING;
if (!ctx->dpb_flush_flag && ctx->head_processed) {
- spin_lock_irqsave(&dev->irqlock, flags);
if (!list_empty(&ctx->src_queue)) {
src_buf = list_entry(ctx->src_queue.next,
struct s5p_mfc_buf, list);
@@ -554,7 +575,6 @@ static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx,
vb2_buffer_done(&src_buf->b->vb2_buf,
VB2_BUF_STATE_DONE);
}
- spin_unlock_irqrestore(&dev->irqlock, flags);
} else {
ctx->dpb_flush_flag = 0;
}
@@ -563,7 +583,7 @@ static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx,
s5p_mfc_clock_off();
wake_up(&ctx->queue);
- s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
} else {
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
@@ -582,7 +602,6 @@ static void s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx *ctx)
ctx->state = MFCINST_FINISHED;
- spin_lock(&dev->irqlock);
if (!list_empty(&ctx->dst_queue)) {
mb_entry = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf,
list);
@@ -591,7 +610,6 @@ static void s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx *ctx)
vb2_set_plane_payload(&mb_entry->b->vb2_buf, 0, 0);
vb2_buffer_done(&mb_entry->b->vb2_buf, VB2_BUF_STATE_DONE);
}
- spin_unlock(&dev->irqlock);
clear_work_bit(ctx);
@@ -599,7 +617,7 @@ static void s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx *ctx)
s5p_mfc_clock_off();
wake_up(&ctx->queue);
- s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
}
/* Interrupt processing */
@@ -613,6 +631,7 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)
mfc_debug_enter();
/* Reset the timeout watchdog */
atomic_set(&dev->watchdog_cnt, 0);
+ spin_lock(&dev->irqlock);
ctx = dev->ctx[dev->curr_ctx];
/* Get the reason of interrupt and the error code */
reason = s5p_mfc_hw_call(dev->mfc_ops, get_int_reason, dev);
@@ -639,15 +658,15 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)
if (ctx->state == MFCINST_FINISHING &&
list_empty(&ctx->ref_queue)) {
- s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
s5p_mfc_handle_stream_complete(ctx);
break;
}
- s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
wake_up_ctx(ctx, reason, err);
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_clock_off();
- s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
} else {
s5p_mfc_handle_frame(ctx, reason, err);
}
@@ -677,7 +696,7 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)
case S5P_MFC_R2H_CMD_WAKEUP_RET:
if (ctx)
clear_work_bit(ctx);
- s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
wake_up_dev(dev, reason, err);
clear_bit(0, &dev->hw_lock);
clear_bit(0, &dev->enter_suspend);
@@ -688,7 +707,7 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)
break;
case S5P_MFC_R2H_CMD_COMPLETE_SEQ_RET:
- s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
ctx->int_type = reason;
ctx->int_err = err;
s5p_mfc_handle_stream_complete(ctx);
@@ -702,12 +721,13 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)
default:
mfc_debug(2, "Unknown int reason\n");
- s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
}
+ spin_unlock(&dev->irqlock);
mfc_debug_leave();
return IRQ_HANDLED;
irq_cleanup_hw:
- s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
ctx->int_type = reason;
ctx->int_err = err;
ctx->int_cond = 1;
@@ -716,7 +736,8 @@ irq_cleanup_hw:
s5p_mfc_clock_off();
- s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ spin_unlock(&dev->irqlock);
mfc_debug(2, "Exit via irq_cleanup_hw\n");
return IRQ_HANDLED;
}
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
index d1a3f9b1bc44..9eb2481ec292 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
@@ -308,7 +308,7 @@ struct s5p_mfc_dev {
struct s5p_mfc_pm pm;
struct s5p_mfc_variant *variant;
int num_inst;
- spinlock_t irqlock; /* lock when operating on videobuf2 queues */
+ spinlock_t irqlock; /* lock when operating on context */
spinlock_t condlock; /* lock when changing/checking if a context is
ready to be processed */
struct mutex mfc_mutex; /* video_device lock */
@@ -653,7 +653,7 @@ struct s5p_mfc_ctx {
unsigned int bits;
} slice_size;
- struct s5p_mfc_codec_ops *c_ops;
+ const struct s5p_mfc_codec_ops *c_ops;
struct v4l2_ctrl *ctrls[MFC_MAX_CTRLS];
struct v4l2_ctrl_handler ctrl_handler;
@@ -694,13 +694,7 @@ struct mfc_control {
/* Macro for making hardware specific calls */
#define s5p_mfc_hw_call(f, op, args...) \
- ((f && f->op) ? f->op(args) : -ENODEV)
-
-#define s5p_mfc_hw_call_void(f, op, args...) \
-do { \
- if (f && f->op) \
- f->op(args); \
-} while (0)
+ ((f && f->op) ? f->op(args) : (typeof(f->op(args)))(-ENODEV))
#define fh_to_ctx(__fh) container_of(__fh, struct s5p_mfc_ctx, fh)
#define ctrl_to_ctx(__ctrl) \
@@ -710,6 +704,8 @@ void clear_work_bit(struct s5p_mfc_ctx *ctx);
void set_work_bit(struct s5p_mfc_ctx *ctx);
void clear_work_bit_irqsave(struct s5p_mfc_ctx *ctx);
void set_work_bit_irqsave(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_get_new_ctx(struct s5p_mfc_dev *dev);
+void s5p_mfc_cleanup_queue(struct list_head *lh, struct vb2_queue *vq);
#define HAS_PORTNUM(dev) (dev ? (dev->variant ? \
(dev->variant->port_num ? 1 : 0) : 0) : 0)
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
index 40d8a03a141d..cc888713b3b6 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
@@ -319,7 +319,7 @@ void s5p_mfc_deinit_hw(struct s5p_mfc_dev *dev)
s5p_mfc_clock_on();
s5p_mfc_reset(dev);
- s5p_mfc_hw_call_void(dev->mfc_ops, release_dev_context_buffer, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, release_dev_context_buffer, dev);
s5p_mfc_clock_off();
}
@@ -468,7 +468,7 @@ int s5p_mfc_open_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)
}
set_work_bit_irqsave(ctx);
- s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
if (s5p_mfc_wait_for_done_ctx(ctx,
S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET, 0)) {
/* Error or timeout */
@@ -482,9 +482,9 @@ int s5p_mfc_open_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)
err_free_desc_buf:
if (ctx->type == MFCINST_DECODER)
- s5p_mfc_hw_call_void(dev->mfc_ops, release_dec_desc_buffer, ctx);
+ s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, ctx);
err_free_inst_buf:
- s5p_mfc_hw_call_void(dev->mfc_ops, release_instance_buffer, ctx);
+ s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx);
err:
return ret;
}
@@ -493,17 +493,17 @@ void s5p_mfc_close_mfc_inst(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx)
{
ctx->state = MFCINST_RETURN_INST;
set_work_bit_irqsave(ctx);
- s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
/* Wait until instance is returned or timeout occurred */
if (s5p_mfc_wait_for_done_ctx(ctx,
S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET, 0))
mfc_err("Err returning instance\n");
/* Free resources */
- s5p_mfc_hw_call_void(dev->mfc_ops, release_codec_buffers, ctx);
- s5p_mfc_hw_call_void(dev->mfc_ops, release_instance_buffer, ctx);
+ s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx);
+ s5p_mfc_hw_call(dev->mfc_ops, release_instance_buffer, ctx);
if (ctx->type == MFCINST_DECODER)
- s5p_mfc_hw_call_void(dev->mfc_ops, release_dec_desc_buffer, ctx);
+ s5p_mfc_hw_call(dev->mfc_ops, release_dec_desc_buffer, ctx);
ctx->inst_no = MFC_NO_INSTANCE_SET;
ctx->state = MFCINST_FREE;
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
index 8c5060a7534f..f2d6376ce618 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
@@ -252,7 +252,7 @@ static int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
return 0;
}
-static struct s5p_mfc_codec_ops decoder_codec_ops = {
+static const struct s5p_mfc_codec_ops decoder_codec_ops = {
.pre_seq_start = NULL,
.post_seq_start = NULL,
.pre_frame_start = NULL,
@@ -523,7 +523,7 @@ static int reqbufs_capture(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx,
ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
if (ret)
goto out;
- s5p_mfc_hw_call_void(dev->mfc_ops, release_codec_buffers, ctx);
+ s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers, ctx);
ctx->dst_bufs_cnt = 0;
} else if (ctx->capture_state == QUEUE_FREE) {
WARN_ON(ctx->dst_bufs_cnt != 0);
@@ -551,7 +551,7 @@ static int reqbufs_capture(struct s5p_mfc_dev *dev, struct s5p_mfc_ctx *ctx,
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
- s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
s5p_mfc_wait_for_done_ctx(ctx, S5P_MFC_R2H_CMD_INIT_BUFFERS_RET,
0);
} else {
@@ -831,7 +831,7 @@ static int vidioc_decoder_cmd(struct file *file, void *priv,
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
spin_unlock_irqrestore(&dev->irqlock, flags);
- s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
} else {
mfc_err("EOS: marking last buffer of stream");
buf = list_entry(ctx->src_queue.prev,
@@ -888,7 +888,7 @@ static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = {
};
static int s5p_mfc_queue_setup(struct vb2_queue *vq,
- const void *parg, unsigned int *buf_count,
+ unsigned int *buf_count,
unsigned int *plane_count, unsigned int psize[],
void *allocators[])
{
@@ -1012,7 +1012,7 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
/* If context is ready then dev = work->data;schedule it to run */
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
- s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
return 0;
}
@@ -1023,42 +1023,41 @@ static void s5p_mfc_stop_streaming(struct vb2_queue *q)
struct s5p_mfc_dev *dev = ctx->dev;
int aborted = 0;
+ spin_lock_irqsave(&dev->irqlock, flags);
if ((ctx->state == MFCINST_FINISHING ||
ctx->state == MFCINST_RUNNING) &&
dev->curr_ctx == ctx->num && dev->hw_lock) {
ctx->state = MFCINST_ABORT;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
s5p_mfc_wait_for_done_ctx(ctx,
S5P_MFC_R2H_CMD_FRAME_DONE_RET, 0);
aborted = 1;
+ spin_lock_irqsave(&dev->irqlock, flags);
}
if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- spin_lock_irqsave(&dev->irqlock, flags);
- s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
- &ctx->dst_queue, &ctx->vq_dst);
+ s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
INIT_LIST_HEAD(&ctx->dst_queue);
ctx->dst_queue_cnt = 0;
ctx->dpb_flush_flag = 1;
ctx->dec_dst_flag = 0;
- spin_unlock_irqrestore(&dev->irqlock, flags);
if (IS_MFCV6_PLUS(dev) && (ctx->state == MFCINST_RUNNING)) {
ctx->state = MFCINST_FLUSH;
set_work_bit_irqsave(ctx);
- s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
if (s5p_mfc_wait_for_done_ctx(ctx,
S5P_MFC_R2H_CMD_DPB_FLUSH_RET, 0))
mfc_err("Err flushing buffers\n");
+ spin_lock_irqsave(&dev->irqlock, flags);
}
- }
- if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- spin_lock_irqsave(&dev->irqlock, flags);
- s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
- &ctx->src_queue, &ctx->vq_src);
+ } else if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
INIT_LIST_HEAD(&ctx->src_queue);
ctx->src_queue_cnt = 0;
- spin_unlock_irqrestore(&dev->irqlock, flags);
}
if (aborted)
ctx->state = MFCINST_RUNNING;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
}
@@ -1091,7 +1090,7 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
}
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
- s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
}
static struct vb2_ops s5p_mfc_dec_qops = {
@@ -1104,7 +1103,7 @@ static struct vb2_ops s5p_mfc_dec_qops = {
.buf_queue = s5p_mfc_buf_queue,
};
-struct s5p_mfc_codec_ops *get_dec_codec_ops(void)
+const struct s5p_mfc_codec_ops *get_dec_codec_ops(void)
{
return &decoder_codec_ops;
}
@@ -1119,7 +1118,7 @@ const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void)
return &s5p_mfc_dec_ioctl_ops;
}
-#define IS_MFC51_PRIV(x) ((V4L2_CTRL_ID2CLASS(x) == V4L2_CTRL_CLASS_MPEG) \
+#define IS_MFC51_PRIV(x) ((V4L2_CTRL_ID2WHICH(x) == V4L2_CTRL_CLASS_MPEG) \
&& V4L2_CTRL_DRIVER_PRIV(x))
int s5p_mfc_dec_ctrls_setup(struct s5p_mfc_ctx *ctx)
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.h b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.h
index d06a7cab5eb1..886628b153f0 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.h
@@ -13,7 +13,7 @@
#ifndef S5P_MFC_DEC_H_
#define S5P_MFC_DEC_H_
-struct s5p_mfc_codec_ops *get_dec_codec_ops(void);
+const struct s5p_mfc_codec_ops *get_dec_codec_ops(void);
struct vb2_ops *get_dec_queue_ops(void);
const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void);
struct s5p_mfc_fmt *get_dec_def_fmt(bool src);
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
index 5c678ec9c9f2..0434f02a7175 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
@@ -769,15 +769,12 @@ static int enc_pre_seq_start(struct s5p_mfc_ctx *ctx)
struct s5p_mfc_buf *dst_mb;
unsigned long dst_addr;
unsigned int dst_size;
- unsigned long flags;
- spin_lock_irqsave(&dev->irqlock, flags);
dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
dst_addr = vb2_dma_contig_plane_dma_addr(&dst_mb->b->vb2_buf, 0);
dst_size = vb2_plane_size(&dst_mb->b->vb2_buf, 0);
- s5p_mfc_hw_call_void(dev->mfc_ops, set_enc_stream_buffer, ctx, dst_addr,
+ s5p_mfc_hw_call(dev->mfc_ops, set_enc_stream_buffer, ctx, dst_addr,
dst_size);
- spin_unlock_irqrestore(&dev->irqlock, flags);
return 0;
}
@@ -786,11 +783,9 @@ static int enc_post_seq_start(struct s5p_mfc_ctx *ctx)
struct s5p_mfc_dev *dev = ctx->dev;
struct s5p_mfc_enc_params *p = &ctx->enc_params;
struct s5p_mfc_buf *dst_mb;
- unsigned long flags;
unsigned int enc_pb_count;
if (p->seq_hdr_mode == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) {
- spin_lock_irqsave(&dev->irqlock, flags);
if (!list_empty(&ctx->dst_queue)) {
dst_mb = list_entry(ctx->dst_queue.next,
struct s5p_mfc_buf, list);
@@ -802,14 +797,13 @@ static int enc_post_seq_start(struct s5p_mfc_ctx *ctx)
vb2_buffer_done(&dst_mb->b->vb2_buf,
VB2_BUF_STATE_DONE);
}
- spin_unlock_irqrestore(&dev->irqlock, flags);
}
if (!IS_MFCV6_PLUS(dev)) {
ctx->state = MFCINST_RUNNING;
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
- s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
} else {
enc_pb_count = s5p_mfc_hw_call(dev->mfc_ops,
get_enc_dpb_count, dev);
@@ -826,25 +820,20 @@ static int enc_pre_frame_start(struct s5p_mfc_ctx *ctx)
struct s5p_mfc_dev *dev = ctx->dev;
struct s5p_mfc_buf *dst_mb;
struct s5p_mfc_buf *src_mb;
- unsigned long flags;
unsigned long src_y_addr, src_c_addr, dst_addr;
unsigned int dst_size;
- spin_lock_irqsave(&dev->irqlock, flags);
src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
src_y_addr = vb2_dma_contig_plane_dma_addr(&src_mb->b->vb2_buf, 0);
src_c_addr = vb2_dma_contig_plane_dma_addr(&src_mb->b->vb2_buf, 1);
- s5p_mfc_hw_call_void(dev->mfc_ops, set_enc_frame_buffer, ctx,
+ s5p_mfc_hw_call(dev->mfc_ops, set_enc_frame_buffer, ctx,
src_y_addr, src_c_addr);
- spin_unlock_irqrestore(&dev->irqlock, flags);
- spin_lock_irqsave(&dev->irqlock, flags);
dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
dst_addr = vb2_dma_contig_plane_dma_addr(&dst_mb->b->vb2_buf, 0);
dst_size = vb2_plane_size(&dst_mb->b->vb2_buf, 0);
- s5p_mfc_hw_call_void(dev->mfc_ops, set_enc_stream_buffer, ctx, dst_addr,
+ s5p_mfc_hw_call(dev->mfc_ops, set_enc_stream_buffer, ctx, dst_addr,
dst_size);
- spin_unlock_irqrestore(&dev->irqlock, flags);
return 0;
}
@@ -857,7 +846,6 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx)
unsigned long mb_y_addr, mb_c_addr;
int slice_type;
unsigned int strm_size;
- unsigned long flags;
slice_type = s5p_mfc_hw_call(dev->mfc_ops, get_enc_slice_type, dev);
strm_size = s5p_mfc_hw_call(dev->mfc_ops, get_enc_strm_size, dev);
@@ -865,9 +853,8 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx)
mfc_debug(2, "Encoded stream size: %d\n", strm_size);
mfc_debug(2, "Display order: %d\n",
mfc_read(dev, S5P_FIMV_ENC_SI_PIC_CNT));
- spin_lock_irqsave(&dev->irqlock, flags);
if (slice_type >= 0) {
- s5p_mfc_hw_call_void(dev->mfc_ops, get_enc_frame_buffer, ctx,
+ s5p_mfc_hw_call(dev->mfc_ops, get_enc_frame_buffer, ctx,
&enc_y_addr, &enc_c_addr);
list_for_each_entry(mb_entry, &ctx->src_queue, list) {
mb_y_addr = vb2_dma_contig_plane_dma_addr(
@@ -929,14 +916,13 @@ static int enc_post_frame_start(struct s5p_mfc_ctx *ctx)
vb2_set_plane_payload(&mb_entry->b->vb2_buf, 0, strm_size);
vb2_buffer_done(&mb_entry->b->vb2_buf, VB2_BUF_STATE_DONE);
}
- spin_unlock_irqrestore(&dev->irqlock, flags);
if ((ctx->src_queue_cnt == 0) || (ctx->dst_queue_cnt == 0))
clear_work_bit(ctx);
return 0;
}
-static struct s5p_mfc_codec_ops encoder_codec_ops = {
+static const struct s5p_mfc_codec_ops encoder_codec_ops = {
.pre_seq_start = enc_pre_seq_start,
.post_seq_start = enc_post_seq_start,
.pre_frame_start = enc_pre_frame_start,
@@ -1120,7 +1106,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
pix_fmt_mp->width, pix_fmt_mp->height,
ctx->img_width, ctx->img_height);
- s5p_mfc_hw_call_void(dev->mfc_ops, enc_calc_src_size, ctx);
+ s5p_mfc_hw_call(dev->mfc_ops, enc_calc_src_size, ctx);
pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size;
pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width;
pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
@@ -1178,7 +1164,7 @@ static int vidioc_reqbufs(struct file *file, void *priv,
if (reqbufs->count == 0) {
mfc_debug(2, "Freeing buffers\n");
ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
- s5p_mfc_hw_call_void(dev->mfc_ops, release_codec_buffers,
+ s5p_mfc_hw_call(dev->mfc_ops, release_codec_buffers,
ctx);
ctx->output_state = QUEUE_FREE;
return ret;
@@ -1741,7 +1727,7 @@ static int vidioc_encoder_cmd(struct file *file, void *priv,
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
spin_unlock_irqrestore(&dev->irqlock, flags);
- s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
} else {
mfc_debug(2, "EOS: marking last buffer of stream\n");
buf = list_entry(ctx->src_queue.prev,
@@ -1818,7 +1804,6 @@ static int check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb)
}
static int s5p_mfc_queue_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *buf_count, unsigned int *plane_count,
unsigned int psize[], void *allocators[])
{
@@ -1969,7 +1954,7 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
/* If context is ready then dev = work->data;schedule it to run */
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
- s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
return 0;
}
@@ -1990,15 +1975,13 @@ static void s5p_mfc_stop_streaming(struct vb2_queue *q)
ctx->state = MFCINST_FINISHED;
spin_lock_irqsave(&dev->irqlock, flags);
if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue,
- &ctx->dst_queue, &ctx->vq_dst);
+ s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
INIT_LIST_HEAD(&ctx->dst_queue);
ctx->dst_queue_cnt = 0;
}
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
cleanup_ref_queue(ctx);
- s5p_mfc_hw_call_void(dev->mfc_ops, cleanup_queue, &ctx->src_queue,
- &ctx->vq_src);
+ s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
INIT_LIST_HEAD(&ctx->src_queue);
ctx->src_queue_cnt = 0;
}
@@ -2038,7 +2021,7 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
}
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
- s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
}
static struct vb2_ops s5p_mfc_enc_qops = {
@@ -2052,7 +2035,7 @@ static struct vb2_ops s5p_mfc_enc_qops = {
.buf_queue = s5p_mfc_buf_queue,
};
-struct s5p_mfc_codec_ops *get_enc_codec_ops(void)
+const struct s5p_mfc_codec_ops *get_enc_codec_ops(void)
{
return &encoder_codec_ops;
}
@@ -2067,7 +2050,7 @@ const struct v4l2_ioctl_ops *get_enc_v4l2_ioctl_ops(void)
return &s5p_mfc_enc_ioctl_ops;
}
-#define IS_MFC51_PRIV(x) ((V4L2_CTRL_ID2CLASS(x) == V4L2_CTRL_CLASS_MPEG) \
+#define IS_MFC51_PRIV(x) ((V4L2_CTRL_ID2WHICH(x) == V4L2_CTRL_CLASS_MPEG) \
&& V4L2_CTRL_DRIVER_PRIV(x))
int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx)
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.h b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.h
index 5118d46b3a9e..d0d42f818832 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.h
@@ -13,7 +13,7 @@
#ifndef S5P_MFC_ENC_H_
#define S5P_MFC_ENC_H_
-struct s5p_mfc_codec_ops *get_enc_codec_ops(void);
+const struct s5p_mfc_codec_ops *get_enc_codec_ops(void);
struct vb2_ops *get_enc_queue_ops(void);
const struct v4l2_ioctl_ops *get_enc_v4l2_ioctl_ops(void);
struct s5p_mfc_fmt *get_enc_def_fmt(bool src);
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
index 77a08b19b46d..b6ac417ab63e 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
@@ -20,254 +20,254 @@
struct s5p_mfc_regs {
/* codec common registers */
- volatile void __iomem *risc_on;
- volatile void __iomem *risc2host_int;
- volatile void __iomem *host2risc_int;
- volatile void __iomem *risc_base_address;
- volatile void __iomem *mfc_reset;
- volatile void __iomem *host2risc_command;
- volatile void __iomem *risc2host_command;
- volatile void __iomem *mfc_bus_reset_ctrl;
- volatile void __iomem *firmware_version;
- volatile void __iomem *instance_id;
- volatile void __iomem *codec_type;
- volatile void __iomem *context_mem_addr;
- volatile void __iomem *context_mem_size;
- volatile void __iomem *pixel_format;
- volatile void __iomem *metadata_enable;
- volatile void __iomem *mfc_version;
- volatile void __iomem *dbg_info_enable;
- volatile void __iomem *dbg_buffer_addr;
- volatile void __iomem *dbg_buffer_size;
- volatile void __iomem *hed_control;
- volatile void __iomem *mfc_timeout_value;
- volatile void __iomem *hed_shared_mem_addr;
- volatile void __iomem *dis_shared_mem_addr;/* only v7 */
- volatile void __iomem *ret_instance_id;
- volatile void __iomem *error_code;
- volatile void __iomem *dbg_buffer_output_size;
- volatile void __iomem *metadata_status;
- volatile void __iomem *metadata_addr_mb_info;
- volatile void __iomem *metadata_size_mb_info;
- volatile void __iomem *dbg_info_stage_counter;
+ void __iomem *risc_on;
+ void __iomem *risc2host_int;
+ void __iomem *host2risc_int;
+ void __iomem *risc_base_address;
+ void __iomem *mfc_reset;
+ void __iomem *host2risc_command;
+ void __iomem *risc2host_command;
+ void __iomem *mfc_bus_reset_ctrl;
+ void __iomem *firmware_version;
+ void __iomem *instance_id;
+ void __iomem *codec_type;
+ void __iomem *context_mem_addr;
+ void __iomem *context_mem_size;
+ void __iomem *pixel_format;
+ void __iomem *metadata_enable;
+ void __iomem *mfc_version;
+ void __iomem *dbg_info_enable;
+ void __iomem *dbg_buffer_addr;
+ void __iomem *dbg_buffer_size;
+ void __iomem *hed_control;
+ void __iomem *mfc_timeout_value;
+ void __iomem *hed_shared_mem_addr;
+ void __iomem *dis_shared_mem_addr;/* only v7 */
+ void __iomem *ret_instance_id;
+ void __iomem *error_code;
+ void __iomem *dbg_buffer_output_size;
+ void __iomem *metadata_status;
+ void __iomem *metadata_addr_mb_info;
+ void __iomem *metadata_size_mb_info;
+ void __iomem *dbg_info_stage_counter;
/* decoder registers */
- volatile void __iomem *d_crc_ctrl;
- volatile void __iomem *d_dec_options;
- volatile void __iomem *d_display_delay;
- volatile void __iomem *d_set_frame_width;
- volatile void __iomem *d_set_frame_height;
- volatile void __iomem *d_sei_enable;
- volatile void __iomem *d_min_num_dpb;
- volatile void __iomem *d_min_first_plane_dpb_size;
- volatile void __iomem *d_min_second_plane_dpb_size;
- volatile void __iomem *d_min_third_plane_dpb_size;/* only v8 */
- volatile void __iomem *d_min_num_mv;
- volatile void __iomem *d_mvc_num_views;
- volatile void __iomem *d_min_num_dis;/* only v7 */
- volatile void __iomem *d_min_first_dis_size;/* only v7 */
- volatile void __iomem *d_min_second_dis_size;/* only v7 */
- volatile void __iomem *d_min_third_dis_size;/* only v7 */
- volatile void __iomem *d_post_filter_luma_dpb0;/* v7 and v8 */
- volatile void __iomem *d_post_filter_luma_dpb1;/* v7 and v8 */
- volatile void __iomem *d_post_filter_luma_dpb2;/* only v7 */
- volatile void __iomem *d_post_filter_chroma_dpb0;/* v7 and v8 */
- volatile void __iomem *d_post_filter_chroma_dpb1;/* v7 and v8 */
- volatile void __iomem *d_post_filter_chroma_dpb2;/* only v7 */
- volatile void __iomem *d_num_dpb;
- volatile void __iomem *d_num_mv;
- volatile void __iomem *d_init_buffer_options;
- volatile void __iomem *d_first_plane_dpb_stride_size;/* only v8 */
- volatile void __iomem *d_second_plane_dpb_stride_size;/* only v8 */
- volatile void __iomem *d_third_plane_dpb_stride_size;/* only v8 */
- volatile void __iomem *d_first_plane_dpb_size;
- volatile void __iomem *d_second_plane_dpb_size;
- volatile void __iomem *d_third_plane_dpb_size;/* only v8 */
- volatile void __iomem *d_mv_buffer_size;
- volatile void __iomem *d_first_plane_dpb;
- volatile void __iomem *d_second_plane_dpb;
- volatile void __iomem *d_third_plane_dpb;
- volatile void __iomem *d_mv_buffer;
- volatile void __iomem *d_scratch_buffer_addr;
- volatile void __iomem *d_scratch_buffer_size;
- volatile void __iomem *d_metadata_buffer_addr;
- volatile void __iomem *d_metadata_buffer_size;
- volatile void __iomem *d_nal_start_options;/* v7 and v8 */
- volatile void __iomem *d_cpb_buffer_addr;
- volatile void __iomem *d_cpb_buffer_size;
- volatile void __iomem *d_available_dpb_flag_upper;
- volatile void __iomem *d_available_dpb_flag_lower;
- volatile void __iomem *d_cpb_buffer_offset;
- volatile void __iomem *d_slice_if_enable;
- volatile void __iomem *d_picture_tag;
- volatile void __iomem *d_stream_data_size;
- volatile void __iomem *d_dynamic_dpb_flag_upper;/* v7 and v8 */
- volatile void __iomem *d_dynamic_dpb_flag_lower;/* v7 and v8 */
- volatile void __iomem *d_display_frame_width;
- volatile void __iomem *d_display_frame_height;
- volatile void __iomem *d_display_status;
- volatile void __iomem *d_display_first_plane_addr;
- volatile void __iomem *d_display_second_plane_addr;
- volatile void __iomem *d_display_third_plane_addr;/* only v8 */
- volatile void __iomem *d_display_frame_type;
- volatile void __iomem *d_display_crop_info1;
- volatile void __iomem *d_display_crop_info2;
- volatile void __iomem *d_display_picture_profile;
- volatile void __iomem *d_display_luma_crc;/* v7 and v8 */
- volatile void __iomem *d_display_chroma0_crc;/* v7 and v8 */
- volatile void __iomem *d_display_chroma1_crc;/* only v8 */
- volatile void __iomem *d_display_luma_crc_top;/* only v6 */
- volatile void __iomem *d_display_chroma_crc_top;/* only v6 */
- volatile void __iomem *d_display_luma_crc_bot;/* only v6 */
- volatile void __iomem *d_display_chroma_crc_bot;/* only v6 */
- volatile void __iomem *d_display_aspect_ratio;
- volatile void __iomem *d_display_extended_ar;
- volatile void __iomem *d_decoded_frame_width;
- volatile void __iomem *d_decoded_frame_height;
- volatile void __iomem *d_decoded_status;
- volatile void __iomem *d_decoded_first_plane_addr;
- volatile void __iomem *d_decoded_second_plane_addr;
- volatile void __iomem *d_decoded_third_plane_addr;/* only v8 */
- volatile void __iomem *d_decoded_frame_type;
- volatile void __iomem *d_decoded_crop_info1;
- volatile void __iomem *d_decoded_crop_info2;
- volatile void __iomem *d_decoded_picture_profile;
- volatile void __iomem *d_decoded_nal_size;
- volatile void __iomem *d_decoded_luma_crc;
- volatile void __iomem *d_decoded_chroma0_crc;
- volatile void __iomem *d_decoded_chroma1_crc;/* only v8 */
- volatile void __iomem *d_ret_picture_tag_top;
- volatile void __iomem *d_ret_picture_tag_bot;
- volatile void __iomem *d_ret_picture_time_top;
- volatile void __iomem *d_ret_picture_time_bot;
- volatile void __iomem *d_chroma_format;
- volatile void __iomem *d_vc1_info;/* v7 and v8 */
- volatile void __iomem *d_mpeg4_info;
- volatile void __iomem *d_h264_info;
- volatile void __iomem *d_metadata_addr_concealed_mb;
- volatile void __iomem *d_metadata_size_concealed_mb;
- volatile void __iomem *d_metadata_addr_vc1_param;
- volatile void __iomem *d_metadata_size_vc1_param;
- volatile void __iomem *d_metadata_addr_sei_nal;
- volatile void __iomem *d_metadata_size_sei_nal;
- volatile void __iomem *d_metadata_addr_vui;
- volatile void __iomem *d_metadata_size_vui;
- volatile void __iomem *d_metadata_addr_mvcvui;/* v7 and v8 */
- volatile void __iomem *d_metadata_size_mvcvui;/* v7 and v8 */
- volatile void __iomem *d_mvc_view_id;
- volatile void __iomem *d_frame_pack_sei_avail;
- volatile void __iomem *d_frame_pack_arrgment_id;
- volatile void __iomem *d_frame_pack_sei_info;
- volatile void __iomem *d_frame_pack_grid_pos;
- volatile void __iomem *d_display_recovery_sei_info;/* v7 and v8 */
- volatile void __iomem *d_decoded_recovery_sei_info;/* v7 and v8 */
- volatile void __iomem *d_display_first_addr;/* only v7 */
- volatile void __iomem *d_display_second_addr;/* only v7 */
- volatile void __iomem *d_display_third_addr;/* only v7 */
- volatile void __iomem *d_decoded_first_addr;/* only v7 */
- volatile void __iomem *d_decoded_second_addr;/* only v7 */
- volatile void __iomem *d_decoded_third_addr;/* only v7 */
- volatile void __iomem *d_used_dpb_flag_upper;/* v7 and v8 */
- volatile void __iomem *d_used_dpb_flag_lower;/* v7 and v8 */
+ void __iomem *d_crc_ctrl;
+ void __iomem *d_dec_options;
+ void __iomem *d_display_delay;
+ void __iomem *d_set_frame_width;
+ void __iomem *d_set_frame_height;
+ void __iomem *d_sei_enable;
+ void __iomem *d_min_num_dpb;
+ void __iomem *d_min_first_plane_dpb_size;
+ void __iomem *d_min_second_plane_dpb_size;
+ void __iomem *d_min_third_plane_dpb_size;/* only v8 */
+ void __iomem *d_min_num_mv;
+ void __iomem *d_mvc_num_views;
+ void __iomem *d_min_num_dis;/* only v7 */
+ void __iomem *d_min_first_dis_size;/* only v7 */
+ void __iomem *d_min_second_dis_size;/* only v7 */
+ void __iomem *d_min_third_dis_size;/* only v7 */
+ void __iomem *d_post_filter_luma_dpb0;/* v7 and v8 */
+ void __iomem *d_post_filter_luma_dpb1;/* v7 and v8 */
+ void __iomem *d_post_filter_luma_dpb2;/* only v7 */
+ void __iomem *d_post_filter_chroma_dpb0;/* v7 and v8 */
+ void __iomem *d_post_filter_chroma_dpb1;/* v7 and v8 */
+ void __iomem *d_post_filter_chroma_dpb2;/* only v7 */
+ void __iomem *d_num_dpb;
+ void __iomem *d_num_mv;
+ void __iomem *d_init_buffer_options;
+ void __iomem *d_first_plane_dpb_stride_size;/* only v8 */
+ void __iomem *d_second_plane_dpb_stride_size;/* only v8 */
+ void __iomem *d_third_plane_dpb_stride_size;/* only v8 */
+ void __iomem *d_first_plane_dpb_size;
+ void __iomem *d_second_plane_dpb_size;
+ void __iomem *d_third_plane_dpb_size;/* only v8 */
+ void __iomem *d_mv_buffer_size;
+ void __iomem *d_first_plane_dpb;
+ void __iomem *d_second_plane_dpb;
+ void __iomem *d_third_plane_dpb;
+ void __iomem *d_mv_buffer;
+ void __iomem *d_scratch_buffer_addr;
+ void __iomem *d_scratch_buffer_size;
+ void __iomem *d_metadata_buffer_addr;
+ void __iomem *d_metadata_buffer_size;
+ void __iomem *d_nal_start_options;/* v7 and v8 */
+ void __iomem *d_cpb_buffer_addr;
+ void __iomem *d_cpb_buffer_size;
+ void __iomem *d_available_dpb_flag_upper;
+ void __iomem *d_available_dpb_flag_lower;
+ void __iomem *d_cpb_buffer_offset;
+ void __iomem *d_slice_if_enable;
+ void __iomem *d_picture_tag;
+ void __iomem *d_stream_data_size;
+ void __iomem *d_dynamic_dpb_flag_upper;/* v7 and v8 */
+ void __iomem *d_dynamic_dpb_flag_lower;/* v7 and v8 */
+ void __iomem *d_display_frame_width;
+ void __iomem *d_display_frame_height;
+ void __iomem *d_display_status;
+ void __iomem *d_display_first_plane_addr;
+ void __iomem *d_display_second_plane_addr;
+ void __iomem *d_display_third_plane_addr;/* only v8 */
+ void __iomem *d_display_frame_type;
+ void __iomem *d_display_crop_info1;
+ void __iomem *d_display_crop_info2;
+ void __iomem *d_display_picture_profile;
+ void __iomem *d_display_luma_crc;/* v7 and v8 */
+ void __iomem *d_display_chroma0_crc;/* v7 and v8 */
+ void __iomem *d_display_chroma1_crc;/* only v8 */
+ void __iomem *d_display_luma_crc_top;/* only v6 */
+ void __iomem *d_display_chroma_crc_top;/* only v6 */
+ void __iomem *d_display_luma_crc_bot;/* only v6 */
+ void __iomem *d_display_chroma_crc_bot;/* only v6 */
+ void __iomem *d_display_aspect_ratio;
+ void __iomem *d_display_extended_ar;
+ void __iomem *d_decoded_frame_width;
+ void __iomem *d_decoded_frame_height;
+ void __iomem *d_decoded_status;
+ void __iomem *d_decoded_first_plane_addr;
+ void __iomem *d_decoded_second_plane_addr;
+ void __iomem *d_decoded_third_plane_addr;/* only v8 */
+ void __iomem *d_decoded_frame_type;
+ void __iomem *d_decoded_crop_info1;
+ void __iomem *d_decoded_crop_info2;
+ void __iomem *d_decoded_picture_profile;
+ void __iomem *d_decoded_nal_size;
+ void __iomem *d_decoded_luma_crc;
+ void __iomem *d_decoded_chroma0_crc;
+ void __iomem *d_decoded_chroma1_crc;/* only v8 */
+ void __iomem *d_ret_picture_tag_top;
+ void __iomem *d_ret_picture_tag_bot;
+ void __iomem *d_ret_picture_time_top;
+ void __iomem *d_ret_picture_time_bot;
+ void __iomem *d_chroma_format;
+ void __iomem *d_vc1_info;/* v7 and v8 */
+ void __iomem *d_mpeg4_info;
+ void __iomem *d_h264_info;
+ void __iomem *d_metadata_addr_concealed_mb;
+ void __iomem *d_metadata_size_concealed_mb;
+ void __iomem *d_metadata_addr_vc1_param;
+ void __iomem *d_metadata_size_vc1_param;
+ void __iomem *d_metadata_addr_sei_nal;
+ void __iomem *d_metadata_size_sei_nal;
+ void __iomem *d_metadata_addr_vui;
+ void __iomem *d_metadata_size_vui;
+ void __iomem *d_metadata_addr_mvcvui;/* v7 and v8 */
+ void __iomem *d_metadata_size_mvcvui;/* v7 and v8 */
+ void __iomem *d_mvc_view_id;
+ void __iomem *d_frame_pack_sei_avail;
+ void __iomem *d_frame_pack_arrgment_id;
+ void __iomem *d_frame_pack_sei_info;
+ void __iomem *d_frame_pack_grid_pos;
+ void __iomem *d_display_recovery_sei_info;/* v7 and v8 */
+ void __iomem *d_decoded_recovery_sei_info;/* v7 and v8 */
+ void __iomem *d_display_first_addr;/* only v7 */
+ void __iomem *d_display_second_addr;/* only v7 */
+ void __iomem *d_display_third_addr;/* only v7 */
+ void __iomem *d_decoded_first_addr;/* only v7 */
+ void __iomem *d_decoded_second_addr;/* only v7 */
+ void __iomem *d_decoded_third_addr;/* only v7 */
+ void __iomem *d_used_dpb_flag_upper;/* v7 and v8 */
+ void __iomem *d_used_dpb_flag_lower;/* v7 and v8 */
/* encoder registers */
- volatile void __iomem *e_frame_width;
- volatile void __iomem *e_frame_height;
- volatile void __iomem *e_cropped_frame_width;
- volatile void __iomem *e_cropped_frame_height;
- volatile void __iomem *e_frame_crop_offset;
- volatile void __iomem *e_enc_options;
- volatile void __iomem *e_picture_profile;
- volatile void __iomem *e_vbv_buffer_size;
- volatile void __iomem *e_vbv_init_delay;
- volatile void __iomem *e_fixed_picture_qp;
- volatile void __iomem *e_rc_config;
- volatile void __iomem *e_rc_qp_bound;
- volatile void __iomem *e_rc_qp_bound_pb;/* v7 and v8 */
- volatile void __iomem *e_rc_mode;
- volatile void __iomem *e_mb_rc_config;
- volatile void __iomem *e_padding_ctrl;
- volatile void __iomem *e_air_threshold;
- volatile void __iomem *e_mv_hor_range;
- volatile void __iomem *e_mv_ver_range;
- volatile void __iomem *e_num_dpb;
- volatile void __iomem *e_luma_dpb;
- volatile void __iomem *e_chroma_dpb;
- volatile void __iomem *e_me_buffer;
- volatile void __iomem *e_scratch_buffer_addr;
- volatile void __iomem *e_scratch_buffer_size;
- volatile void __iomem *e_tmv_buffer0;
- volatile void __iomem *e_tmv_buffer1;
- volatile void __iomem *e_ir_buffer_addr;/* v7 and v8 */
- volatile void __iomem *e_source_first_plane_addr;
- volatile void __iomem *e_source_second_plane_addr;
- volatile void __iomem *e_source_third_plane_addr;/* v7 and v8 */
- volatile void __iomem *e_source_first_plane_stride;/* v7 and v8 */
- volatile void __iomem *e_source_second_plane_stride;/* v7 and v8 */
- volatile void __iomem *e_source_third_plane_stride;/* v7 and v8 */
- volatile void __iomem *e_stream_buffer_addr;
- volatile void __iomem *e_stream_buffer_size;
- volatile void __iomem *e_roi_buffer_addr;
- volatile void __iomem *e_param_change;
- volatile void __iomem *e_ir_size;
- volatile void __iomem *e_gop_config;
- volatile void __iomem *e_mslice_mode;
- volatile void __iomem *e_mslice_size_mb;
- volatile void __iomem *e_mslice_size_bits;
- volatile void __iomem *e_frame_insertion;
- volatile void __iomem *e_rc_frame_rate;
- volatile void __iomem *e_rc_bit_rate;
- volatile void __iomem *e_rc_roi_ctrl;
- volatile void __iomem *e_picture_tag;
- volatile void __iomem *e_bit_count_enable;
- volatile void __iomem *e_max_bit_count;
- volatile void __iomem *e_min_bit_count;
- volatile void __iomem *e_metadata_buffer_addr;
- volatile void __iomem *e_metadata_buffer_size;
- volatile void __iomem *e_encoded_source_first_plane_addr;
- volatile void __iomem *e_encoded_source_second_plane_addr;
- volatile void __iomem *e_encoded_source_third_plane_addr;/* v7 and v8 */
- volatile void __iomem *e_stream_size;
- volatile void __iomem *e_slice_type;
- volatile void __iomem *e_picture_count;
- volatile void __iomem *e_ret_picture_tag;
- volatile void __iomem *e_stream_buffer_write_pointer; /* only v6 */
- volatile void __iomem *e_recon_luma_dpb_addr;
- volatile void __iomem *e_recon_chroma_dpb_addr;
- volatile void __iomem *e_metadata_addr_enc_slice;
- volatile void __iomem *e_metadata_size_enc_slice;
- volatile void __iomem *e_mpeg4_options;
- volatile void __iomem *e_mpeg4_hec_period;
- volatile void __iomem *e_aspect_ratio;
- volatile void __iomem *e_extended_sar;
- volatile void __iomem *e_h264_options;
- volatile void __iomem *e_h264_options_2;/* v7 and v8 */
- volatile void __iomem *e_h264_lf_alpha_offset;
- volatile void __iomem *e_h264_lf_beta_offset;
- volatile void __iomem *e_h264_i_period;
- volatile void __iomem *e_h264_fmo_slice_grp_map_type;
- volatile void __iomem *e_h264_fmo_num_slice_grp_minus1;
- volatile void __iomem *e_h264_fmo_slice_grp_change_dir;
- volatile void __iomem *e_h264_fmo_slice_grp_change_rate_minus1;
- volatile void __iomem *e_h264_fmo_run_length_minus1_0;
- volatile void __iomem *e_h264_aso_slice_order_0;
- volatile void __iomem *e_h264_chroma_qp_offset;
- volatile void __iomem *e_h264_num_t_layer;
- volatile void __iomem *e_h264_hierarchical_qp_layer0;
- volatile void __iomem *e_h264_frame_packing_sei_info;
- volatile void __iomem *e_h264_nal_control;/* v7 and v8 */
- volatile void __iomem *e_mvc_frame_qp_view1;
- volatile void __iomem *e_mvc_rc_bit_rate_view1;
- volatile void __iomem *e_mvc_rc_qbound_view1;
- volatile void __iomem *e_mvc_rc_mode_view1;
- volatile void __iomem *e_mvc_inter_view_prediction_on;
- volatile void __iomem *e_vp8_options;/* v7 and v8 */
- volatile void __iomem *e_vp8_filter_options;/* v7 and v8 */
- volatile void __iomem *e_vp8_golden_frame_option;/* v7 and v8 */
- volatile void __iomem *e_vp8_num_t_layer;/* v7 and v8 */
- volatile void __iomem *e_vp8_hierarchical_qp_layer0;/* v7 and v8 */
- volatile void __iomem *e_vp8_hierarchical_qp_layer1;/* v7 and v8 */
- volatile void __iomem *e_vp8_hierarchical_qp_layer2;/* v7 and v8 */
+ void __iomem *e_frame_width;
+ void __iomem *e_frame_height;
+ void __iomem *e_cropped_frame_width;
+ void __iomem *e_cropped_frame_height;
+ void __iomem *e_frame_crop_offset;
+ void __iomem *e_enc_options;
+ void __iomem *e_picture_profile;
+ void __iomem *e_vbv_buffer_size;
+ void __iomem *e_vbv_init_delay;
+ void __iomem *e_fixed_picture_qp;
+ void __iomem *e_rc_config;
+ void __iomem *e_rc_qp_bound;
+ void __iomem *e_rc_qp_bound_pb;/* v7 and v8 */
+ void __iomem *e_rc_mode;
+ void __iomem *e_mb_rc_config;
+ void __iomem *e_padding_ctrl;
+ void __iomem *e_air_threshold;
+ void __iomem *e_mv_hor_range;
+ void __iomem *e_mv_ver_range;
+ void __iomem *e_num_dpb;
+ void __iomem *e_luma_dpb;
+ void __iomem *e_chroma_dpb;
+ void __iomem *e_me_buffer;
+ void __iomem *e_scratch_buffer_addr;
+ void __iomem *e_scratch_buffer_size;
+ void __iomem *e_tmv_buffer0;
+ void __iomem *e_tmv_buffer1;
+ void __iomem *e_ir_buffer_addr;/* v7 and v8 */
+ void __iomem *e_source_first_plane_addr;
+ void __iomem *e_source_second_plane_addr;
+ void __iomem *e_source_third_plane_addr;/* v7 and v8 */
+ void __iomem *e_source_first_plane_stride;/* v7 and v8 */
+ void __iomem *e_source_second_plane_stride;/* v7 and v8 */
+ void __iomem *e_source_third_plane_stride;/* v7 and v8 */
+ void __iomem *e_stream_buffer_addr;
+ void __iomem *e_stream_buffer_size;
+ void __iomem *e_roi_buffer_addr;
+ void __iomem *e_param_change;
+ void __iomem *e_ir_size;
+ void __iomem *e_gop_config;
+ void __iomem *e_mslice_mode;
+ void __iomem *e_mslice_size_mb;
+ void __iomem *e_mslice_size_bits;
+ void __iomem *e_frame_insertion;
+ void __iomem *e_rc_frame_rate;
+ void __iomem *e_rc_bit_rate;
+ void __iomem *e_rc_roi_ctrl;
+ void __iomem *e_picture_tag;
+ void __iomem *e_bit_count_enable;
+ void __iomem *e_max_bit_count;
+ void __iomem *e_min_bit_count;
+ void __iomem *e_metadata_buffer_addr;
+ void __iomem *e_metadata_buffer_size;
+ void __iomem *e_encoded_source_first_plane_addr;
+ void __iomem *e_encoded_source_second_plane_addr;
+ void __iomem *e_encoded_source_third_plane_addr;/* v7 and v8 */
+ void __iomem *e_stream_size;
+ void __iomem *e_slice_type;
+ void __iomem *e_picture_count;
+ void __iomem *e_ret_picture_tag;
+ void __iomem *e_stream_buffer_write_pointer; /* only v6 */
+ void __iomem *e_recon_luma_dpb_addr;
+ void __iomem *e_recon_chroma_dpb_addr;
+ void __iomem *e_metadata_addr_enc_slice;
+ void __iomem *e_metadata_size_enc_slice;
+ void __iomem *e_mpeg4_options;
+ void __iomem *e_mpeg4_hec_period;
+ void __iomem *e_aspect_ratio;
+ void __iomem *e_extended_sar;
+ void __iomem *e_h264_options;
+ void __iomem *e_h264_options_2;/* v7 and v8 */
+ void __iomem *e_h264_lf_alpha_offset;
+ void __iomem *e_h264_lf_beta_offset;
+ void __iomem *e_h264_i_period;
+ void __iomem *e_h264_fmo_slice_grp_map_type;
+ void __iomem *e_h264_fmo_num_slice_grp_minus1;
+ void __iomem *e_h264_fmo_slice_grp_change_dir;
+ void __iomem *e_h264_fmo_slice_grp_change_rate_minus1;
+ void __iomem *e_h264_fmo_run_length_minus1_0;
+ void __iomem *e_h264_aso_slice_order_0;
+ void __iomem *e_h264_chroma_qp_offset;
+ void __iomem *e_h264_num_t_layer;
+ void __iomem *e_h264_hierarchical_qp_layer0;
+ void __iomem *e_h264_frame_packing_sei_info;
+ void __iomem *e_h264_nal_control;/* v7 and v8 */
+ void __iomem *e_mvc_frame_qp_view1;
+ void __iomem *e_mvc_rc_bit_rate_view1;
+ void __iomem *e_mvc_rc_qbound_view1;
+ void __iomem *e_mvc_rc_mode_view1;
+ void __iomem *e_mvc_inter_view_prediction_on;
+ void __iomem *e_vp8_options;/* v7 and v8 */
+ void __iomem *e_vp8_filter_options;/* v7 and v8 */
+ void __iomem *e_vp8_golden_frame_option;/* v7 and v8 */
+ void __iomem *e_vp8_num_t_layer;/* v7 and v8 */
+ void __iomem *e_vp8_hierarchical_qp_layer0;/* v7 and v8 */
+ void __iomem *e_vp8_hierarchical_qp_layer1;/* v7 and v8 */
+ void __iomem *e_vp8_hierarchical_qp_layer2;/* v7 and v8 */
};
struct s5p_mfc_hw_ops {
@@ -281,28 +281,14 @@ struct s5p_mfc_hw_ops {
void (*release_dev_context_buffer)(struct s5p_mfc_dev *dev);
void (*dec_calc_dpb_size)(struct s5p_mfc_ctx *ctx);
void (*enc_calc_src_size)(struct s5p_mfc_ctx *ctx);
- int (*set_dec_stream_buffer)(struct s5p_mfc_ctx *ctx,
- int buf_addr, unsigned int start_num_byte,
- unsigned int buf_size);
- int (*set_dec_frame_buffer)(struct s5p_mfc_ctx *ctx);
int (*set_enc_stream_buffer)(struct s5p_mfc_ctx *ctx,
unsigned long addr, unsigned int size);
void (*set_enc_frame_buffer)(struct s5p_mfc_ctx *ctx,
unsigned long y_addr, unsigned long c_addr);
void (*get_enc_frame_buffer)(struct s5p_mfc_ctx *ctx,
unsigned long *y_addr, unsigned long *c_addr);
- int (*set_enc_ref_buffer)(struct s5p_mfc_ctx *ctx);
- int (*init_decode)(struct s5p_mfc_ctx *ctx);
- int (*init_encode)(struct s5p_mfc_ctx *ctx);
- int (*encode_one_frame)(struct s5p_mfc_ctx *ctx);
void (*try_run)(struct s5p_mfc_dev *dev);
- void (*cleanup_queue)(struct list_head *lh,
- struct vb2_queue *vq);
void (*clear_int_flags)(struct s5p_mfc_dev *dev);
- void (*write_info)(struct s5p_mfc_ctx *ctx, unsigned int data,
- unsigned int ofs);
- unsigned int (*read_info)(struct s5p_mfc_ctx *ctx,
- unsigned long ofs);
int (*get_dspl_y_adr)(struct s5p_mfc_dev *dev);
int (*get_dec_y_adr)(struct s5p_mfc_dev *dev);
int (*get_dspl_status)(struct s5p_mfc_dev *dev);
@@ -313,7 +299,6 @@ struct s5p_mfc_hw_ops {
int (*get_int_reason)(struct s5p_mfc_dev *dev);
int (*get_int_err)(struct s5p_mfc_dev *dev);
int (*err_dec)(unsigned int err);
- int (*err_dspl)(unsigned int err);
int (*get_img_width)(struct s5p_mfc_dev *dev);
int (*get_img_height)(struct s5p_mfc_dev *dev);
int (*get_dpb_count)(struct s5p_mfc_dev *dev);
@@ -322,10 +307,6 @@ struct s5p_mfc_hw_ops {
int (*get_enc_strm_size)(struct s5p_mfc_dev *dev);
int (*get_enc_slice_type)(struct s5p_mfc_dev *dev);
int (*get_enc_dpb_count)(struct s5p_mfc_dev *dev);
- int (*get_enc_pic_count)(struct s5p_mfc_dev *dev);
- int (*get_sei_avail_status)(struct s5p_mfc_ctx *ctx);
- int (*get_mvc_num_views)(struct s5p_mfc_dev *dev);
- int (*get_mvc_view_id)(struct s5p_mfc_dev *dev);
unsigned int (*get_pic_type_top)(struct s5p_mfc_ctx *ctx);
unsigned int (*get_pic_type_bot)(struct s5p_mfc_ctx *ctx);
unsigned int (*get_crop_info_h)(struct s5p_mfc_ctx *ctx);
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
index 873c933bc7d4..81e1e4ce6c24 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
@@ -1153,27 +1153,6 @@ static int s5p_mfc_encode_one_frame_v5(struct s5p_mfc_ctx *ctx)
return 0;
}
-static int s5p_mfc_get_new_ctx(struct s5p_mfc_dev *dev)
-{
- unsigned long flags;
- int new_ctx;
- int cnt;
-
- spin_lock_irqsave(&dev->condlock, flags);
- new_ctx = (dev->curr_ctx + 1) % MFC_NUM_CONTEXTS;
- cnt = 0;
- while (!test_bit(new_ctx, &dev->ctx_work_bits)) {
- new_ctx = (new_ctx + 1) % MFC_NUM_CONTEXTS;
- if (++cnt > MFC_NUM_CONTEXTS) {
- /* No contexts to run */
- spin_unlock_irqrestore(&dev->condlock, flags);
- return -EAGAIN;
- }
- }
- spin_unlock_irqrestore(&dev->condlock, flags);
- return new_ctx;
-}
-
static void s5p_mfc_run_res_change(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
@@ -1187,7 +1166,6 @@ static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame)
{
struct s5p_mfc_dev *dev = ctx->dev;
struct s5p_mfc_buf *temp_vb;
- unsigned long flags;
if (ctx->state == MFCINST_FINISHING) {
last_frame = MFC_DEC_LAST_FRAME;
@@ -1197,11 +1175,9 @@ static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame)
return 0;
}
- spin_lock_irqsave(&dev->irqlock, flags);
/* Frames are being decoded */
if (list_empty(&ctx->src_queue)) {
mfc_debug(2, "No src buffers\n");
- spin_unlock_irqrestore(&dev->irqlock, flags);
return -EAGAIN;
}
/* Get the next source buffer */
@@ -1210,7 +1186,6 @@ static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame)
s5p_mfc_set_dec_stream_buffer_v5(ctx,
vb2_dma_contig_plane_dma_addr(&temp_vb->b->vb2_buf, 0),
ctx->consumed_stream, temp_vb->b->vb2_buf.planes[0].bytesused);
- spin_unlock_irqrestore(&dev->irqlock, flags);
dev->curr_ctx = ctx->num;
if (temp_vb->b->vb2_buf.planes[0].bytesused == 0) {
last_frame = MFC_DEC_LAST_FRAME;
@@ -1224,21 +1199,17 @@ static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame)
static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
- unsigned long flags;
struct s5p_mfc_buf *dst_mb;
struct s5p_mfc_buf *src_mb;
unsigned long src_y_addr, src_c_addr, dst_addr;
unsigned int dst_size;
- spin_lock_irqsave(&dev->irqlock, flags);
if (list_empty(&ctx->src_queue) && ctx->state != MFCINST_FINISHING) {
mfc_debug(2, "no src buffers\n");
- spin_unlock_irqrestore(&dev->irqlock, flags);
return -EAGAIN;
}
if (list_empty(&ctx->dst_queue)) {
mfc_debug(2, "no dst buffers\n");
- spin_unlock_irqrestore(&dev->irqlock, flags);
return -EAGAIN;
}
if (list_empty(&ctx->src_queue)) {
@@ -1270,7 +1241,6 @@ static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx)
dst_addr = vb2_dma_contig_plane_dma_addr(&dst_mb->b->vb2_buf, 0);
dst_size = vb2_plane_size(&dst_mb->b->vb2_buf, 0);
s5p_mfc_set_enc_stream_buffer_v5(ctx, dst_addr, dst_size);
- spin_unlock_irqrestore(&dev->irqlock, flags);
dev->curr_ctx = ctx->num;
mfc_debug(2, "encoding buffer with index=%d state=%d\n",
src_mb ? src_mb->b->vb2_buf.index : -1, ctx->state);
@@ -1281,11 +1251,9 @@ static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx)
static void s5p_mfc_run_init_dec(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
- unsigned long flags;
struct s5p_mfc_buf *temp_vb;
/* Initializing decoding - parsing header */
- spin_lock_irqsave(&dev->irqlock, flags);
mfc_debug(2, "Preparing to init decoding\n");
temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
s5p_mfc_set_dec_desc_buffer(ctx);
@@ -1294,7 +1262,6 @@ static void s5p_mfc_run_init_dec(struct s5p_mfc_ctx *ctx)
s5p_mfc_set_dec_stream_buffer_v5(ctx,
vb2_dma_contig_plane_dma_addr(&temp_vb->b->vb2_buf, 0),
0, temp_vb->b->vb2_buf.planes[0].bytesused);
- spin_unlock_irqrestore(&dev->irqlock, flags);
dev->curr_ctx = ctx->num;
s5p_mfc_init_decode_v5(ctx);
}
@@ -1302,18 +1269,15 @@ static void s5p_mfc_run_init_dec(struct s5p_mfc_ctx *ctx)
static void s5p_mfc_run_init_enc(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
- unsigned long flags;
struct s5p_mfc_buf *dst_mb;
unsigned long dst_addr;
unsigned int dst_size;
s5p_mfc_set_enc_ref_buffer_v5(ctx);
- spin_lock_irqsave(&dev->irqlock, flags);
dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
dst_addr = vb2_dma_contig_plane_dma_addr(&dst_mb->b->vb2_buf, 0);
dst_size = vb2_plane_size(&dst_mb->b->vb2_buf, 0);
s5p_mfc_set_enc_stream_buffer_v5(ctx, dst_addr, dst_size);
- spin_unlock_irqrestore(&dev->irqlock, flags);
dev->curr_ctx = ctx->num;
s5p_mfc_init_encode_v5(ctx);
}
@@ -1321,7 +1285,6 @@ static void s5p_mfc_run_init_enc(struct s5p_mfc_ctx *ctx)
static int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
- unsigned long flags;
struct s5p_mfc_buf *temp_vb;
int ret;
@@ -1335,11 +1298,9 @@ static int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx)
"before starting processing\n");
return -EAGAIN;
}
- spin_lock_irqsave(&dev->irqlock, flags);
if (list_empty(&ctx->src_queue)) {
mfc_err("Header has been deallocated in the middle of"
" initialization\n");
- spin_unlock_irqrestore(&dev->irqlock, flags);
return -EIO;
}
temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
@@ -1348,7 +1309,6 @@ static int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx)
s5p_mfc_set_dec_stream_buffer_v5(ctx,
vb2_dma_contig_plane_dma_addr(&temp_vb->b->vb2_buf, 0),
0, temp_vb->b->vb2_buf.planes[0].bytesused);
- spin_unlock_irqrestore(&dev->irqlock, flags);
dev->curr_ctx = ctx->num;
ret = s5p_mfc_set_dec_frame_buffer_v5(ctx);
if (ret) {
@@ -1472,21 +1432,6 @@ static void s5p_mfc_try_run_v5(struct s5p_mfc_dev *dev)
}
}
-
-static void s5p_mfc_cleanup_queue_v5(struct list_head *lh, struct vb2_queue *vq)
-{
- struct s5p_mfc_buf *b;
- int i;
-
- while (!list_empty(lh)) {
- b = list_entry(lh->next, struct s5p_mfc_buf, list);
- for (i = 0; i < b->b->vb2_buf.num_planes; i++)
- vb2_set_plane_payload(&b->b->vb2_buf, i, 0);
- vb2_buffer_done(&b->b->vb2_buf, VB2_BUF_STATE_ERROR);
- list_del(&b->list);
- }
-}
-
static void s5p_mfc_clear_int_flags_v5(struct s5p_mfc_dev *dev)
{
mfc_write(dev, 0, S5P_FIMV_RISC_HOST_INT);
@@ -1590,11 +1535,6 @@ static int s5p_mfc_err_dec_v5(unsigned int err)
return (err & S5P_FIMV_ERR_DEC_MASK) >> S5P_FIMV_ERR_DEC_SHIFT;
}
-static int s5p_mfc_err_dspl_v5(unsigned int err)
-{
- return (err & S5P_FIMV_ERR_DSPL_MASK) >> S5P_FIMV_ERR_DSPL_SHIFT;
-}
-
static int s5p_mfc_get_img_width_v5(struct s5p_mfc_dev *dev)
{
return mfc_read(dev, S5P_FIMV_SI_HRESOL);
@@ -1636,26 +1576,6 @@ static int s5p_mfc_get_enc_dpb_count_v5(struct s5p_mfc_dev *dev)
return -1;
}
-static int s5p_mfc_get_enc_pic_count_v5(struct s5p_mfc_dev *dev)
-{
- return mfc_read(dev, S5P_FIMV_ENC_SI_PIC_CNT);
-}
-
-static int s5p_mfc_get_sei_avail_status_v5(struct s5p_mfc_ctx *ctx)
-{
- return s5p_mfc_read_info_v5(ctx, FRAME_PACK_SEI_AVAIL);
-}
-
-static int s5p_mfc_get_mvc_num_views_v5(struct s5p_mfc_dev *dev)
-{
- return -1;
-}
-
-static int s5p_mfc_get_mvc_view_id_v5(struct s5p_mfc_dev *dev)
-{
- return -1;
-}
-
static unsigned int s5p_mfc_get_pic_type_top_v5(struct s5p_mfc_ctx *ctx)
{
return s5p_mfc_read_info_v5(ctx, PIC_TIME_TOP);
@@ -1688,20 +1608,11 @@ static struct s5p_mfc_hw_ops s5p_mfc_ops_v5 = {
.release_dev_context_buffer = s5p_mfc_release_dev_context_buffer_v5,
.dec_calc_dpb_size = s5p_mfc_dec_calc_dpb_size_v5,
.enc_calc_src_size = s5p_mfc_enc_calc_src_size_v5,
- .set_dec_stream_buffer = s5p_mfc_set_dec_stream_buffer_v5,
- .set_dec_frame_buffer = s5p_mfc_set_dec_frame_buffer_v5,
.set_enc_stream_buffer = s5p_mfc_set_enc_stream_buffer_v5,
.set_enc_frame_buffer = s5p_mfc_set_enc_frame_buffer_v5,
.get_enc_frame_buffer = s5p_mfc_get_enc_frame_buffer_v5,
- .set_enc_ref_buffer = s5p_mfc_set_enc_ref_buffer_v5,
- .init_decode = s5p_mfc_init_decode_v5,
- .init_encode = s5p_mfc_init_encode_v5,
- .encode_one_frame = s5p_mfc_encode_one_frame_v5,
.try_run = s5p_mfc_try_run_v5,
- .cleanup_queue = s5p_mfc_cleanup_queue_v5,
.clear_int_flags = s5p_mfc_clear_int_flags_v5,
- .write_info = s5p_mfc_write_info_v5,
- .read_info = s5p_mfc_read_info_v5,
.get_dspl_y_adr = s5p_mfc_get_dspl_y_adr_v5,
.get_dec_y_adr = s5p_mfc_get_dec_y_adr_v5,
.get_dspl_status = s5p_mfc_get_dspl_status_v5,
@@ -1712,7 +1623,6 @@ static struct s5p_mfc_hw_ops s5p_mfc_ops_v5 = {
.get_int_reason = s5p_mfc_get_int_reason_v5,
.get_int_err = s5p_mfc_get_int_err_v5,
.err_dec = s5p_mfc_err_dec_v5,
- .err_dspl = s5p_mfc_err_dspl_v5,
.get_img_width = s5p_mfc_get_img_width_v5,
.get_img_height = s5p_mfc_get_img_height_v5,
.get_dpb_count = s5p_mfc_get_dpb_count_v5,
@@ -1721,10 +1631,6 @@ static struct s5p_mfc_hw_ops s5p_mfc_ops_v5 = {
.get_enc_strm_size = s5p_mfc_get_enc_strm_size_v5,
.get_enc_slice_type = s5p_mfc_get_enc_slice_type_v5,
.get_enc_dpb_count = s5p_mfc_get_enc_dpb_count_v5,
- .get_enc_pic_count = s5p_mfc_get_enc_pic_count_v5,
- .get_sei_avail_status = s5p_mfc_get_sei_avail_status_v5,
- .get_mvc_num_views = s5p_mfc_get_mvc_num_views_v5,
- .get_mvc_view_id = s5p_mfc_get_mvc_view_id_v5,
.get_pic_type_top = s5p_mfc_get_pic_type_top_v5,
.get_pic_type_bot = s5p_mfc_get_pic_type_bot_v5,
.get_crop_info_h = s5p_mfc_get_crop_info_h_v5,
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
index b95845347348..d6f207e859ab 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
@@ -505,7 +505,7 @@ static int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx)
}
writel(ctx->inst_no, mfc_regs->instance_id);
- s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
+ s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_CH_INIT_BUFS_V6, NULL);
mfc_debug(2, "After setting buffers.\n");
@@ -603,7 +603,7 @@ static int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx)
}
writel(ctx->inst_no, mfc_regs->instance_id);
- s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
+ s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_CH_INIT_BUFS_V6, NULL);
mfc_debug_leave();
@@ -1378,7 +1378,7 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
writel(ctx->sei_fp_parse & 0x1, mfc_regs->d_sei_enable);
writel(ctx->inst_no, mfc_regs->instance_id);
- s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
+ s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_CH_SEQ_HEADER_V6, NULL);
mfc_debug_leave();
@@ -1393,7 +1393,7 @@ static inline void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush)
if (flush) {
dev->curr_ctx = ctx->num;
writel(ctx->inst_no, mfc_regs->instance_id);
- s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
+ s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_H2R_CMD_FLUSH_V6, NULL);
}
}
@@ -1413,11 +1413,11 @@ static int s5p_mfc_decode_one_frame_v6(struct s5p_mfc_ctx *ctx,
* is the last frame or not. */
switch (last_frame) {
case 0:
- s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
+ s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_CH_FRAME_START_V6, NULL);
break;
case 1:
- s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
+ s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_CH_LAST_FRAME_V6, NULL);
break;
default:
@@ -1455,7 +1455,7 @@ static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx)
}
writel(ctx->inst_no, mfc_regs->instance_id);
- s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
+ s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_CH_SEQ_HEADER_V6, NULL);
return 0;
@@ -1500,37 +1500,13 @@ static int s5p_mfc_encode_one_frame_v6(struct s5p_mfc_ctx *ctx)
cmd = S5P_FIMV_CH_LAST_FRAME_V6;
writel(ctx->inst_no, mfc_regs->instance_id);
- s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev, cmd, NULL);
+ s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev, cmd, NULL);
mfc_debug(2, "--\n");
return 0;
}
-static inline int s5p_mfc_get_new_ctx(struct s5p_mfc_dev *dev)
-{
- unsigned long flags;
- int new_ctx;
- int cnt;
-
- spin_lock_irqsave(&dev->condlock, flags);
- mfc_debug(2, "Previous context: %d (bits %08lx)\n", dev->curr_ctx,
- dev->ctx_work_bits);
- new_ctx = (dev->curr_ctx + 1) % MFC_NUM_CONTEXTS;
- cnt = 0;
- while (!test_bit(new_ctx, &dev->ctx_work_bits)) {
- new_ctx = (new_ctx + 1) % MFC_NUM_CONTEXTS;
- cnt++;
- if (cnt > MFC_NUM_CONTEXTS) {
- /* No contexts to run */
- spin_unlock_irqrestore(&dev->condlock, flags);
- return -EAGAIN;
- }
- }
- spin_unlock_irqrestore(&dev->condlock, flags);
- return new_ctx;
-}
-
static inline void s5p_mfc_run_dec_last_frames(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
@@ -1544,7 +1520,6 @@ static inline int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
struct s5p_mfc_buf *temp_vb;
- unsigned long flags;
int last_frame = 0;
if (ctx->state == MFCINST_FINISHING) {
@@ -1556,11 +1531,9 @@ static inline int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx)
return 0;
}
- spin_lock_irqsave(&dev->irqlock, flags);
/* Frames are being decoded */
if (list_empty(&ctx->src_queue)) {
mfc_debug(2, "No src buffers.\n");
- spin_unlock_irqrestore(&dev->irqlock, flags);
return -EAGAIN;
}
/* Get the next source buffer */
@@ -1570,7 +1543,6 @@ static inline int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx)
vb2_dma_contig_plane_dma_addr(&temp_vb->b->vb2_buf, 0),
ctx->consumed_stream,
temp_vb->b->vb2_buf.planes[0].bytesused);
- spin_unlock_irqrestore(&dev->irqlock, flags);
dev->curr_ctx = ctx->num;
if (temp_vb->b->vb2_buf.planes[0].bytesused == 0) {
@@ -1586,7 +1558,6 @@ static inline int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx)
static inline int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
- unsigned long flags;
struct s5p_mfc_buf *dst_mb;
struct s5p_mfc_buf *src_mb;
unsigned long src_y_addr, src_c_addr, dst_addr;
@@ -1595,17 +1566,13 @@ static inline int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx)
*/
unsigned int dst_size;
- spin_lock_irqsave(&dev->irqlock, flags);
-
if (list_empty(&ctx->src_queue) && ctx->state != MFCINST_FINISHING) {
mfc_debug(2, "no src buffers.\n");
- spin_unlock_irqrestore(&dev->irqlock, flags);
return -EAGAIN;
}
if (list_empty(&ctx->dst_queue)) {
mfc_debug(2, "no dst buffers.\n");
- spin_unlock_irqrestore(&dev->irqlock, flags);
return -EAGAIN;
}
@@ -1639,8 +1606,6 @@ static inline int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx)
s5p_mfc_set_enc_stream_buffer_v6(ctx, dst_addr, dst_size);
- spin_unlock_irqrestore(&dev->irqlock, flags);
-
dev->curr_ctx = ctx->num;
s5p_mfc_encode_one_frame_v6(ctx);
@@ -1650,18 +1615,15 @@ static inline int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx)
static inline void s5p_mfc_run_init_dec(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
- unsigned long flags;
struct s5p_mfc_buf *temp_vb;
/* Initializing decoding - parsing header */
- spin_lock_irqsave(&dev->irqlock, flags);
mfc_debug(2, "Preparing to init decoding.\n");
temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
mfc_debug(2, "Header size: %d\n", temp_vb->b->vb2_buf.planes[0].bytesused);
s5p_mfc_set_dec_stream_buffer_v6(ctx,
vb2_dma_contig_plane_dma_addr(&temp_vb->b->vb2_buf, 0), 0,
temp_vb->b->vb2_buf.planes[0].bytesused);
- spin_unlock_irqrestore(&dev->irqlock, flags);
dev->curr_ctx = ctx->num;
s5p_mfc_init_decode_v6(ctx);
}
@@ -1669,18 +1631,14 @@ static inline void s5p_mfc_run_init_dec(struct s5p_mfc_ctx *ctx)
static inline void s5p_mfc_run_init_enc(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
- unsigned long flags;
struct s5p_mfc_buf *dst_mb;
unsigned long dst_addr;
unsigned int dst_size;
- spin_lock_irqsave(&dev->irqlock, flags);
-
dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
dst_addr = vb2_dma_contig_plane_dma_addr(&dst_mb->b->vb2_buf, 0);
dst_size = vb2_plane_size(&dst_mb->b->vb2_buf, 0);
s5p_mfc_set_enc_stream_buffer_v6(ctx, dst_addr, dst_size);
- spin_unlock_irqrestore(&dev->irqlock, flags);
dev->curr_ctx = ctx->num;
s5p_mfc_init_encode_v6(ctx);
}
@@ -1846,21 +1804,6 @@ static void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev)
}
}
-
-static void s5p_mfc_cleanup_queue_v6(struct list_head *lh, struct vb2_queue *vq)
-{
- struct s5p_mfc_buf *b;
- int i;
-
- while (!list_empty(lh)) {
- b = list_entry(lh->next, struct s5p_mfc_buf, list);
- for (i = 0; i < b->b->vb2_buf.num_planes; i++)
- vb2_set_plane_payload(&b->b->vb2_buf, i, 0);
- vb2_buffer_done(&b->b->vb2_buf, VB2_BUF_STATE_ERROR);
- list_del(&b->list);
- }
-}
-
static void s5p_mfc_clear_int_flags_v6(struct s5p_mfc_dev *dev)
{
const struct s5p_mfc_regs *mfc_regs = dev->mfc_regs;
@@ -1868,14 +1811,6 @@ static void s5p_mfc_clear_int_flags_v6(struct s5p_mfc_dev *dev)
writel(0, mfc_regs->risc2host_int);
}
-static void s5p_mfc_write_info_v6(struct s5p_mfc_ctx *ctx, unsigned int data,
- unsigned int ofs)
-{
- s5p_mfc_clock_on();
- writel(data, (void __iomem *)((unsigned long)ofs));
- s5p_mfc_clock_off();
-}
-
static unsigned int
s5p_mfc_read_info_v6(struct s5p_mfc_ctx *ctx, unsigned long ofs)
{
@@ -1942,11 +1877,6 @@ static int s5p_mfc_err_dec_v6(unsigned int err)
return (err & S5P_FIMV_ERR_DEC_MASK_V6) >> S5P_FIMV_ERR_DEC_SHIFT_V6;
}
-static int s5p_mfc_err_dspl_v6(unsigned int err)
-{
- return (err & S5P_FIMV_ERR_DSPL_MASK_V6) >> S5P_FIMV_ERR_DSPL_SHIFT_V6;
-}
-
static int s5p_mfc_get_img_width_v6(struct s5p_mfc_dev *dev)
{
return readl(dev->mfc_regs->d_display_frame_width);
@@ -1987,27 +1917,6 @@ static int s5p_mfc_get_enc_slice_type_v6(struct s5p_mfc_dev *dev)
return readl(dev->mfc_regs->e_slice_type);
}
-static int s5p_mfc_get_enc_pic_count_v6(struct s5p_mfc_dev *dev)
-{
- return readl(dev->mfc_regs->e_picture_count);
-}
-
-static int s5p_mfc_get_sei_avail_status_v6(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- return readl(dev->mfc_regs->d_frame_pack_sei_avail);
-}
-
-static int s5p_mfc_get_mvc_num_views_v6(struct s5p_mfc_dev *dev)
-{
- return readl(dev->mfc_regs->d_mvc_num_views);
-}
-
-static int s5p_mfc_get_mvc_view_id_v6(struct s5p_mfc_dev *dev)
-{
- return readl(dev->mfc_regs->d_mvc_view_id);
-}
-
static unsigned int s5p_mfc_get_pic_type_top_v6(struct s5p_mfc_ctx *ctx)
{
return s5p_mfc_read_info_v6(ctx,
@@ -2282,20 +2191,11 @@ static struct s5p_mfc_hw_ops s5p_mfc_ops_v6 = {
s5p_mfc_release_dev_context_buffer_v6,
.dec_calc_dpb_size = s5p_mfc_dec_calc_dpb_size_v6,
.enc_calc_src_size = s5p_mfc_enc_calc_src_size_v6,
- .set_dec_stream_buffer = s5p_mfc_set_dec_stream_buffer_v6,
- .set_dec_frame_buffer = s5p_mfc_set_dec_frame_buffer_v6,
.set_enc_stream_buffer = s5p_mfc_set_enc_stream_buffer_v6,
.set_enc_frame_buffer = s5p_mfc_set_enc_frame_buffer_v6,
.get_enc_frame_buffer = s5p_mfc_get_enc_frame_buffer_v6,
- .set_enc_ref_buffer = s5p_mfc_set_enc_ref_buffer_v6,
- .init_decode = s5p_mfc_init_decode_v6,
- .init_encode = s5p_mfc_init_encode_v6,
- .encode_one_frame = s5p_mfc_encode_one_frame_v6,
.try_run = s5p_mfc_try_run_v6,
- .cleanup_queue = s5p_mfc_cleanup_queue_v6,
.clear_int_flags = s5p_mfc_clear_int_flags_v6,
- .write_info = s5p_mfc_write_info_v6,
- .read_info = s5p_mfc_read_info_v6,
.get_dspl_y_adr = s5p_mfc_get_dspl_y_adr_v6,
.get_dec_y_adr = s5p_mfc_get_dec_y_adr_v6,
.get_dspl_status = s5p_mfc_get_dspl_status_v6,
@@ -2306,7 +2206,6 @@ static struct s5p_mfc_hw_ops s5p_mfc_ops_v6 = {
.get_int_reason = s5p_mfc_get_int_reason_v6,
.get_int_err = s5p_mfc_get_int_err_v6,
.err_dec = s5p_mfc_err_dec_v6,
- .err_dspl = s5p_mfc_err_dspl_v6,
.get_img_width = s5p_mfc_get_img_width_v6,
.get_img_height = s5p_mfc_get_img_height_v6,
.get_dpb_count = s5p_mfc_get_dpb_count_v6,
@@ -2315,10 +2214,6 @@ static struct s5p_mfc_hw_ops s5p_mfc_ops_v6 = {
.get_enc_strm_size = s5p_mfc_get_enc_strm_size_v6,
.get_enc_slice_type = s5p_mfc_get_enc_slice_type_v6,
.get_enc_dpb_count = s5p_mfc_get_enc_dpb_count_v6,
- .get_enc_pic_count = s5p_mfc_get_enc_pic_count_v6,
- .get_sei_avail_status = s5p_mfc_get_sei_avail_status_v6,
- .get_mvc_num_views = s5p_mfc_get_mvc_num_views_v6,
- .get_mvc_view_id = s5p_mfc_get_mvc_view_id_v6,
.get_pic_type_top = s5p_mfc_get_pic_type_top_v6,
.get_pic_type_bot = s5p_mfc_get_pic_type_bot_v6,
.get_crop_info_h = s5p_mfc_get_crop_info_h_v6,
diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c
index 79940757b34f..e71b13e40f59 100644
--- a/drivers/media/platform/s5p-tv/hdmi_drv.c
+++ b/drivers/media/platform/s5p-tv/hdmi_drv.c
@@ -33,7 +33,7 @@
#include <linux/regulator/consumer.h>
#include <linux/v4l2-dv-timings.h>
-#include <media/s5p_hdmi.h>
+#include <linux/platform_data/media/s5p_hdmi.h>
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
@@ -627,7 +627,7 @@ static int hdmi_s_dv_timings(struct v4l2_subdev *sd,
for (i = 0; i < ARRAY_SIZE(hdmi_timings); i++)
if (v4l2_match_dv_timings(&hdmi_timings[i].dv_timings,
- timings, 0))
+ timings, 0, false))
break;
if (i == ARRAY_SIZE(hdmi_timings)) {
dev_err(dev, "timings not supported\n");
diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c
index dc1c679e136c..d9e7f030294c 100644
--- a/drivers/media/platform/s5p-tv/mixer_video.c
+++ b/drivers/media/platform/s5p-tv/mixer_video.c
@@ -881,7 +881,7 @@ static const struct v4l2_file_operations mxr_fops = {
.unlocked_ioctl = video_ioctl2,
};
-static int queue_setup(struct vb2_queue *vq, const void *parg,
+static int queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[],
void *alloc_ctxs[])
{
diff --git a/drivers/media/platform/s5p-tv/sii9234_drv.c b/drivers/media/platform/s5p-tv/sii9234_drv.c
index 8d171310af8f..0a97f9ab4f76 100644
--- a/drivers/media/platform/s5p-tv/sii9234_drv.c
+++ b/drivers/media/platform/s5p-tv/sii9234_drv.c
@@ -23,7 +23,7 @@
#include <linux/regulator/machine.h>
#include <linux/slab.h>
-#include <media/sii9234.h>
+#include <linux/platform_data/media/sii9234.h>
#include <media/v4l2-subdev.h>
MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>");
diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c
index d6ab33e7060a..82b5d69b87fa 100644
--- a/drivers/media/platform/sh_veu.c
+++ b/drivers/media/platform/sh_veu.c
@@ -865,32 +865,14 @@ static const struct v4l2_ioctl_ops sh_veu_ioctl_ops = {
/* ========== Queue operations ========== */
static int sh_veu_queue_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *f = parg;
struct sh_veu_dev *veu = vb2_get_drv_priv(vq);
- struct sh_veu_vfmt *vfmt;
- unsigned int size, count = *nbuffers;
-
- if (f) {
- const struct v4l2_pix_format *pix = &f->fmt.pix;
- const struct sh_veu_format *fmt = sh_veu_find_fmt(f);
- struct v4l2_format ftmp = *f;
-
- if (fmt->fourcc != pix->pixelformat)
- return -EINVAL;
- sh_veu_try_fmt(&ftmp, fmt);
- if (ftmp.fmt.pix.width != pix->width ||
- ftmp.fmt.pix.height != pix->height)
- return -EINVAL;
- size = pix->bytesperline ? pix->bytesperline * pix->height * fmt->depth / fmt->ydepth :
- pix->width * pix->height * fmt->depth / fmt->ydepth;
- } else {
- vfmt = sh_veu_get_vfmt(veu, vq->type);
- size = vfmt->bytesperline * vfmt->frame.height * vfmt->fmt->depth / vfmt->fmt->ydepth;
- }
+ struct sh_veu_vfmt *vfmt = sh_veu_get_vfmt(veu, vq->type);
+ unsigned int count = *nbuffers;
+ unsigned int size = vfmt->bytesperline * vfmt->frame.height *
+ vfmt->fmt->depth / vfmt->fmt->ydepth;
if (count < 2)
*nbuffers = count = 2;
@@ -900,6 +882,11 @@ static int sh_veu_queue_setup(struct vb2_queue *vq,
*nbuffers = count;
}
+ if (*nplanes) {
+ alloc_ctxs[0] = veu->alloc_ctx;
+ return sizes[0] < size ? -EINVAL : 0;
+ }
+
*nplanes = 1;
sizes[0] = size;
alloc_ctxs[0] = veu->alloc_ctx;
@@ -1107,7 +1094,7 @@ static irqreturn_t sh_veu_isr(int irq, void *dev_id)
if (!src || !dst)
return IRQ_NONE;
- dst->timestamp = src->timestamp;
+ dst->vb2_buf.timestamp = src->vb2_buf.timestamp;
dst->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
dst->flags |=
src->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c
index 2231f8922df3..115740498274 100644
--- a/drivers/media/platform/sh_vou.c
+++ b/drivers/media/platform/sh_vou.c
@@ -22,7 +22,7 @@
#include <linux/videodev2.h>
#include <linux/module.h>
-#include <media/sh_vou.h>
+#include <media/drv-intf/sh_vou.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
@@ -243,22 +243,21 @@ static void sh_vou_stream_config(struct sh_vou_device *vou_dev)
}
/* Locking: caller holds fop_lock mutex */
-static int sh_vou_queue_setup(struct vb2_queue *vq, const void *parg,
+static int sh_vou_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct sh_vou_device *vou_dev = vb2_get_drv_priv(vq);
struct v4l2_pix_format *pix = &vou_dev->pix;
int bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8;
dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
- if (fmt && fmt->fmt.pix.sizeimage < pix->height * bytes_per_line)
- return -EINVAL;
- *nplanes = 1;
- sizes[0] = fmt ? fmt->fmt.pix.sizeimage : pix->height * bytes_per_line;
alloc_ctxs[0] = vou_dev->alloc_ctx;
+ if (*nplanes)
+ return sizes[0] < pix->height * bytes_per_line ? -EINVAL : 0;
+ *nplanes = 1;
+ sizes[0] = pix->height * bytes_per_line;
return 0;
}
@@ -1071,7 +1070,7 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id)
list_del(&vb->list);
- v4l2_get_timestamp(&vb->vb.timestamp);
+ vb->vb.vb2_buf.timestamp = ktime_get_ns();
vb->vb.sequence = vou_dev->sequence++;
vb->vb.field = V4L2_FIELD_INTERLACED;
vb2_buffer_done(&vb->vb.vb2_buf, VB2_BUF_STATE_DONE);
diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c
index 454f68f0cdad..c398b285180c 100644
--- a/drivers/media/platform/soc_camera/atmel-isi.c
+++ b/drivers/media/platform/soc_camera/atmel-isi.c
@@ -24,7 +24,7 @@
#include <linux/slab.h>
#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
+#include <media/drv-intf/soc_mediabus.h>
#include <media/v4l2-of.h>
#include <media/videobuf2-dma-contig.h>
@@ -79,6 +79,7 @@ struct atmel_isi {
dma_addr_t fb_descriptors_phys;
struct list_head dma_desc_head;
struct isi_dma_desc dma_desc[MAX_BUFFER_NUM];
+ bool enable_preview_path;
struct completion complete;
/* ISI peripherial clock */
@@ -103,13 +104,55 @@ static u32 isi_readl(struct atmel_isi *isi, u32 reg)
return readl(isi->regs + reg);
}
+static u32 setup_cfg2_yuv_swap(struct atmel_isi *isi,
+ const struct soc_camera_format_xlate *xlate)
+{
+ if (xlate->host_fmt->fourcc == V4L2_PIX_FMT_YUYV) {
+ /* all convert to YUYV */
+ switch (xlate->code) {
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ return ISI_CFG2_YCC_SWAP_MODE_3;
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ return ISI_CFG2_YCC_SWAP_MODE_2;
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ return ISI_CFG2_YCC_SWAP_MODE_1;
+ }
+ } else if (xlate->host_fmt->fourcc == V4L2_PIX_FMT_RGB565) {
+ /*
+ * Preview path is enabled, it will convert UYVY to RGB format.
+ * But if sensor output format is not UYVY, we need to set
+ * YCC_SWAP_MODE to convert it as UYVY.
+ */
+ switch (xlate->code) {
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ return ISI_CFG2_YCC_SWAP_MODE_1;
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ return ISI_CFG2_YCC_SWAP_MODE_2;
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ return ISI_CFG2_YCC_SWAP_MODE_3;
+ }
+ }
+
+ /*
+ * By default, no swap for the codec path of Atmel ISI. So codec
+ * output is same as sensor's output.
+ * For instance, if sensor's output is YUYV, then codec outputs YUYV.
+ * And if sensor's output is UYVY, then codec outputs UYVY.
+ */
+ return ISI_CFG2_YCC_SWAP_DEFAULT;
+}
+
static void configure_geometry(struct atmel_isi *isi, u32 width,
- u32 height, u32 code)
+ u32 height, const struct soc_camera_format_xlate *xlate)
{
- u32 cfg2;
+ u32 cfg2, psize;
+ u32 fourcc = xlate->host_fmt->fourcc;
+
+ isi->enable_preview_path = fourcc == V4L2_PIX_FMT_RGB565 ||
+ fourcc == V4L2_PIX_FMT_RGB32;
/* According to sensor's output format to set cfg2 */
- switch (code) {
+ switch (xlate->code) {
default:
/* Grey */
case MEDIA_BUS_FMT_Y8_1X8:
@@ -117,16 +160,11 @@ static void configure_geometry(struct atmel_isi *isi, u32 width,
break;
/* YUV */
case MEDIA_BUS_FMT_VYUY8_2X8:
- cfg2 = ISI_CFG2_YCC_SWAP_MODE_3 | ISI_CFG2_COL_SPACE_YCbCr;
- break;
case MEDIA_BUS_FMT_UYVY8_2X8:
- cfg2 = ISI_CFG2_YCC_SWAP_MODE_2 | ISI_CFG2_COL_SPACE_YCbCr;
- break;
case MEDIA_BUS_FMT_YVYU8_2X8:
- cfg2 = ISI_CFG2_YCC_SWAP_MODE_1 | ISI_CFG2_COL_SPACE_YCbCr;
- break;
case MEDIA_BUS_FMT_YUYV8_2X8:
- cfg2 = ISI_CFG2_YCC_SWAP_DEFAULT | ISI_CFG2_COL_SPACE_YCbCr;
+ cfg2 = ISI_CFG2_COL_SPACE_YCbCr |
+ setup_cfg2_yuv_swap(isi, xlate);
break;
/* RGB, TODO */
}
@@ -139,6 +177,16 @@ static void configure_geometry(struct atmel_isi *isi, u32 width,
cfg2 |= ((height - 1) << ISI_CFG2_IM_VSIZE_OFFSET)
& ISI_CFG2_IM_VSIZE_MASK;
isi_writel(isi, ISI_CFG2, cfg2);
+
+ /* No down sampling, preview size equal to sensor output size */
+ psize = ((width - 1) << ISI_PSIZE_PREV_HSIZE_OFFSET) &
+ ISI_PSIZE_PREV_HSIZE_MASK;
+ psize |= ((height - 1) << ISI_PSIZE_PREV_VSIZE_OFFSET) &
+ ISI_PSIZE_PREV_VSIZE_MASK;
+ isi_writel(isi, ISI_PSIZE, psize);
+ isi_writel(isi, ISI_PDECF, ISI_PDECF_NO_SAMPLING);
+
+ return;
}
static bool is_supported(struct soc_camera_device *icd,
@@ -151,8 +199,9 @@ static bool is_supported(struct soc_camera_device *icd,
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_YVYU:
case V4L2_PIX_FMT_VYUY:
+ /* RGB */
+ case V4L2_PIX_FMT_RGB565:
return true;
- /* RGB, TODO */
default:
return false;
}
@@ -165,7 +214,7 @@ static irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi)
struct frame_buffer *buf = isi->active;
list_del_init(&buf->list);
- v4l2_get_timestamp(&vbuf->timestamp);
+ vbuf->vb2_buf.timestamp = ktime_get_ns();
vbuf->sequence = isi->sequence++;
vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE);
}
@@ -176,11 +225,19 @@ static irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi)
/* start next dma frame. */
isi->active = list_entry(isi->video_buffer_list.next,
struct frame_buffer, list);
- isi_writel(isi, ISI_DMA_C_DSCR,
- (u32)isi->active->p_dma_desc->fbd_phys);
- isi_writel(isi, ISI_DMA_C_CTRL,
- ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE);
- isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH);
+ if (!isi->enable_preview_path) {
+ isi_writel(isi, ISI_DMA_C_DSCR,
+ (u32)isi->active->p_dma_desc->fbd_phys);
+ isi_writel(isi, ISI_DMA_C_CTRL,
+ ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE);
+ isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH);
+ } else {
+ isi_writel(isi, ISI_DMA_P_DSCR,
+ (u32)isi->active->p_dma_desc->fbd_phys);
+ isi_writel(isi, ISI_DMA_P_CTRL,
+ ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE);
+ isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_P_CH);
+ }
}
return IRQ_HANDLED;
}
@@ -207,7 +264,8 @@ static irqreturn_t isi_interrupt(int irq, void *dev_id)
isi_writel(isi, ISI_INTDIS, ISI_CTRL_DIS);
ret = IRQ_HANDLED;
} else {
- if (likely(pending & ISI_SR_CXFR_DONE))
+ if (likely(pending & ISI_SR_CXFR_DONE) ||
+ likely(pending & ISI_SR_PXFR_DONE))
ret = atmel_isi_handle_streaming(isi);
}
@@ -245,7 +303,7 @@ static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset)
/* ------------------------------------------------------------------
Videobuf operations
------------------------------------------------------------------*/
-static int queue_setup(struct vb2_queue *vq, const void *parg,
+static int queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
@@ -352,21 +410,35 @@ static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer)
ISI_SR_CXFR_DONE | ISI_SR_PXFR_DONE);
/* Check if already in a frame */
- if (isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) {
- dev_err(isi->soc_host.icd->parent, "Already in frame handling.\n");
- return;
- }
+ if (!isi->enable_preview_path) {
+ if (isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) {
+ dev_err(isi->soc_host.icd->parent, "Already in frame handling.\n");
+ return;
+ }
- isi_writel(isi, ISI_DMA_C_DSCR, (u32)buffer->p_dma_desc->fbd_phys);
- isi_writel(isi, ISI_DMA_C_CTRL, ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE);
- isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH);
+ isi_writel(isi, ISI_DMA_C_DSCR,
+ (u32)buffer->p_dma_desc->fbd_phys);
+ isi_writel(isi, ISI_DMA_C_CTRL,
+ ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE);
+ isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_C_CH);
+ } else {
+ isi_writel(isi, ISI_DMA_P_DSCR,
+ (u32)buffer->p_dma_desc->fbd_phys);
+ isi_writel(isi, ISI_DMA_P_CTRL,
+ ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE);
+ isi_writel(isi, ISI_DMA_CHER, ISI_DMA_CHSR_P_CH);
+ }
cfg1 &= ~ISI_CFG1_FRATE_DIV_MASK;
/* Enable linked list */
cfg1 |= isi->pdata.frate | ISI_CFG1_DISCR;
- /* Enable codec path and ISI */
- ctrl = ISI_CTRL_CDC | ISI_CTRL_EN;
+ /* Enable ISI */
+ ctrl = ISI_CTRL_EN;
+
+ if (!isi->enable_preview_path)
+ ctrl |= ISI_CTRL_CDC;
+
isi_writel(isi, ISI_CTRL, ctrl);
isi_writel(isi, ISI_CFG1, cfg1);
}
@@ -411,7 +483,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
isi_writel(isi, ISI_INTDIS, (u32)~0UL);
configure_geometry(isi, icd->user_width, icd->user_height,
- icd->current_fmt->code);
+ icd->current_fmt);
spin_lock_irq(&isi->lock);
/* Clear any pending interrupt */
@@ -443,15 +515,17 @@ static void stop_streaming(struct vb2_queue *vq)
}
spin_unlock_irq(&isi->lock);
- timeout = jiffies + FRAME_INTERVAL_MILLI_SEC * HZ;
- /* Wait until the end of the current frame. */
- while ((isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) &&
- time_before(jiffies, timeout))
- msleep(1);
+ if (!isi->enable_preview_path) {
+ timeout = jiffies + FRAME_INTERVAL_MILLI_SEC * HZ;
+ /* Wait until the end of the current frame. */
+ while ((isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) &&
+ time_before(jiffies, timeout))
+ msleep(1);
- if (time_after(jiffies, timeout))
- dev_err(icd->parent,
- "Timeout waiting for finishing codec request\n");
+ if (time_after(jiffies, timeout))
+ dev_err(icd->parent,
+ "Timeout waiting for finishing codec request\n");
+ }
/* Disable interrupts */
isi_writel(isi, ISI_INTDIS,
@@ -617,6 +691,14 @@ static const struct soc_mbus_pixelfmt isi_camera_formats[] = {
.order = SOC_MBUS_ORDER_LE,
.layout = SOC_MBUS_LAYOUT_PACKED,
},
+ {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .name = "RGB565",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
};
/* This will be corrected as we get more formats */
@@ -673,7 +755,7 @@ static int isi_camera_get_formats(struct soc_camera_device *icd,
struct soc_camera_format_xlate *xlate)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- int formats = 0, ret;
+ int formats = 0, ret, i, n;
/* sensor format */
struct v4l2_subdev_mbus_code_enum code = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
@@ -707,11 +789,11 @@ static int isi_camera_get_formats(struct soc_camera_device *icd,
case MEDIA_BUS_FMT_VYUY8_2X8:
case MEDIA_BUS_FMT_YUYV8_2X8:
case MEDIA_BUS_FMT_YVYU8_2X8:
- formats++;
- if (xlate) {
- xlate->host_fmt = &isi_camera_formats[0];
+ n = ARRAY_SIZE(isi_camera_formats);
+ formats += n;
+ for (i = 0; xlate && i < n; i++, xlate++) {
+ xlate->host_fmt = &isi_camera_formats[i];
xlate->code = code.code;
- xlate++;
dev_dbg(icd->parent, "Providing format %s using code %d\n",
isi_camera_formats[0].name, code.code);
}
diff --git a/drivers/media/platform/soc_camera/atmel-isi.h b/drivers/media/platform/soc_camera/atmel-isi.h
index 5acc771d2edc..0acb32a2b65c 100644
--- a/drivers/media/platform/soc_camera/atmel-isi.h
+++ b/drivers/media/platform/soc_camera/atmel-isi.h
@@ -79,6 +79,16 @@
#define ISI_CFG2_IM_VSIZE_MASK (0x7FF << ISI_CFG2_IM_VSIZE_OFFSET)
#define ISI_CFG2_IM_HSIZE_MASK (0x7FF << ISI_CFG2_IM_HSIZE_OFFSET)
+/* Bitfields in PSIZE */
+#define ISI_PSIZE_PREV_VSIZE_OFFSET 0
+#define ISI_PSIZE_PREV_HSIZE_OFFSET 16
+#define ISI_PSIZE_PREV_VSIZE_MASK (0x3FF << ISI_PSIZE_PREV_VSIZE_OFFSET)
+#define ISI_PSIZE_PREV_HSIZE_MASK (0x3FF << ISI_PSIZE_PREV_HSIZE_OFFSET)
+
+/* Bitfields in PDECF */
+#define ISI_PDECF_DEC_FACTOR_MASK (0xFF << 0)
+#define ISI_PDECF_NO_SAMPLING (16)
+
/* Bitfields in CTRL */
/* Also using in SR(ISI_V2) */
#define ISI_CTRL_EN (1 << 0)
diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c
index 1f28d21a3c9a..48dd5b7851b5 100644
--- a/drivers/media/platform/soc_camera/mx2_camera.c
+++ b/drivers/media/platform/soc_camera/mx2_camera.c
@@ -35,11 +35,11 @@
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-dma-contig.h>
#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
+#include <media/drv-intf/soc_mediabus.h>
#include <linux/videodev2.h>
-#include <linux/platform_data/camera-mx2.h>
+#include <linux/platform_data/media/camera-mx2.h>
#include <asm/dma.h>
@@ -469,21 +469,15 @@ static void mx2_camera_clock_stop(struct soc_camera_host *ici)
* Videobuf operations
*/
static int mx2_videobuf_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *count, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx2_camera_dev *pcdev = ici->priv;
dev_dbg(icd->parent, "count=%d, size=%d\n", *count, sizes[0]);
- /* TODO: support for VIDIOC_CREATE_BUFS not ready */
- if (fmt != NULL)
- return -ENOTTY;
-
alloc_ctxs[0] = pcdev->alloc_ctx;
sizes[0] = icd->sizeimage;
@@ -1351,7 +1345,7 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
vb2_get_plane_payload(vb, 0));
list_del_init(&buf->internal.queue);
- v4l2_get_timestamp(&vbuf->timestamp);
+ vb->timestamp = ktime_get_ns();
vbuf->sequence = pcdev->frame_count;
if (err)
vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c
index 49c3a257a916..169ed1150226 100644
--- a/drivers/media/platform/soc_camera/mx3_camera.c
+++ b/drivers/media/platform/soc_camera/mx3_camera.c
@@ -23,9 +23,9 @@
#include <media/v4l2-dev.h>
#include <media/videobuf2-dma-contig.h>
#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
+#include <media/drv-intf/soc_mediabus.h>
-#include <linux/platform_data/camera-mx3.h>
+#include <linux/platform_data/media/camera-mx3.h>
#include <linux/platform_data/dma-imx.h>
#define MX3_CAM_DRV_NAME "mx3-camera"
@@ -155,7 +155,7 @@ static void mx3_cam_dma_done(void *arg)
struct mx3_camera_buffer *buf = to_mx3_vb(vb);
list_del_init(&buf->queue);
- v4l2_get_timestamp(&vb->timestamp);
+ vb->vb2_buf.timestamp = ktime_get_ns();
vb->field = mx3_cam->field;
vb->sequence = mx3_cam->sequence++;
vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE);
@@ -185,11 +185,9 @@ static void mx3_cam_dma_done(void *arg)
* Calculate the __buffer__ (not data) size and number of buffers.
*/
static int mx3_videobuf_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *count, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
@@ -197,33 +195,6 @@ static int mx3_videobuf_setup(struct vb2_queue *vq,
if (!mx3_cam->idmac_channel[0])
return -EINVAL;
- if (fmt) {
- const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
- fmt->fmt.pix.pixelformat);
- unsigned int bytes_per_line;
- int ret;
-
- if (!xlate)
- return -EINVAL;
-
- ret = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
- xlate->host_fmt);
- if (ret < 0)
- return ret;
-
- bytes_per_line = max_t(u32, fmt->fmt.pix.bytesperline, ret);
-
- ret = soc_mbus_image_size(xlate->host_fmt, bytes_per_line,
- fmt->fmt.pix.height);
- if (ret < 0)
- return ret;
-
- sizes[0] = max_t(u32, fmt->fmt.pix.sizeimage, ret);
- } else {
- /* Called from VIDIOC_REQBUFS or in compatibility mode */
- sizes[0] = icd->sizeimage;
- }
-
alloc_ctxs[0] = mx3_cam->alloc_ctx;
if (!vq->num_buffers)
@@ -232,9 +203,14 @@ static int mx3_videobuf_setup(struct vb2_queue *vq,
if (!*count)
*count = 2;
+ /* Called from VIDIOC_REQBUFS or in compatibility mode */
+ if (!*num_planes)
+ sizes[0] = icd->sizeimage;
+ else if (sizes[0] < icd->sizeimage)
+ return -EINVAL;
+
/* If *num_planes != 0, we have already verified *count. */
- if (!*num_planes &&
- sizes[0] * *count + mx3_cam->buf_total > MAX_VIDEO_MEM * 1024 * 1024)
+ if (sizes[0] * *count + mx3_cam->buf_total > MAX_VIDEO_MEM * 1024 * 1024)
*count = (MAX_VIDEO_MEM * 1024 * 1024 - mx3_cam->buf_total) /
sizes[0];
diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c
index ba8dcd11ae0e..bd721e35474a 100644
--- a/drivers/media/platform/soc_camera/omap1_camera.c
+++ b/drivers/media/platform/soc_camera/omap1_camera.c
@@ -28,9 +28,9 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <media/omap1_camera.h>
+#include <linux/platform_data/media/omap1_camera.h>
#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
+#include <media/drv-intf/soc_mediabus.h>
#include <media/videobuf-dma-contig.h>
#include <media/videobuf-dma-sg.h>
diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c
index fcb942de0c7f..415f3bda60bf 100644
--- a/drivers/media/platform/soc_camera/pxa_camera.c
+++ b/drivers/media/platform/soc_camera/pxa_camera.c
@@ -33,13 +33,13 @@
#include <media/v4l2-dev.h>
#include <media/videobuf-dma-sg.h>
#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
+#include <media/drv-intf/soc_mediabus.h>
#include <media/v4l2-of.h>
#include <linux/videodev2.h>
#include <mach/dma.h>
-#include <linux/platform_data/camera-pxa.h>
+#include <linux/platform_data/media/camera-pxa.h>
#define PXA_CAM_VERSION "0.0.6"
#define PXA_CAM_DRV_NAME "pxa27x-camera"
diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c
index efe57b23fac1..b7fd695b9ed5 100644
--- a/drivers/media/platform/soc_camera/rcar_vin.c
+++ b/drivers/media/platform/soc_camera/rcar_vin.c
@@ -21,14 +21,13 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
-#include <linux/platform_data/camera-rcar.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
+#include <media/drv-intf/soc_mediabus.h>
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
@@ -138,6 +137,11 @@
#define TIMEOUT_MS 100
+#define RCAR_VIN_HSYNC_ACTIVE_LOW (1 << 0)
+#define RCAR_VIN_VSYNC_ACTIVE_LOW (1 << 1)
+#define RCAR_VIN_BT601 (1 << 2)
+#define RCAR_VIN_BT656 (1 << 3)
+
enum chip_id {
RCAR_GEN2,
RCAR_H1,
@@ -527,46 +531,14 @@ struct rcar_vin_cam {
* required
*/
static int rcar_vin_videobuf_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *count,
unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct rcar_vin_priv *priv = ici->priv;
- if (fmt) {
- const struct soc_camera_format_xlate *xlate;
- unsigned int bytes_per_line;
- int ret;
-
- if (fmt->fmt.pix.sizeimage < icd->sizeimage)
- return -EINVAL;
-
- xlate = soc_camera_xlate_by_fourcc(icd,
- fmt->fmt.pix.pixelformat);
- if (!xlate)
- return -EINVAL;
- ret = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
- xlate->host_fmt);
- if (ret < 0)
- return ret;
-
- bytes_per_line = max_t(u32, fmt->fmt.pix.bytesperline, ret);
-
- ret = soc_mbus_image_size(xlate->host_fmt, bytes_per_line,
- fmt->fmt.pix.height);
- if (ret < 0)
- return ret;
-
- sizes[0] = max_t(u32, fmt->fmt.pix.sizeimage, ret);
- } else {
- /* Called from VIDIOC_REQBUFS or in compatibility mode */
- sizes[0] = icd->sizeimage;
- }
-
alloc_ctxs[0] = priv->alloc_ctx;
if (!vq->num_buffers)
@@ -576,14 +548,18 @@ static int rcar_vin_videobuf_setup(struct vb2_queue *vq,
*count = 2;
priv->vb_count = *count;
- *num_planes = 1;
-
/* Number of hardware slots */
if (is_continuous_transfer(priv))
priv->nr_hw_slots = MAX_BUFFER_NUM;
else
priv->nr_hw_slots = 1;
+ if (*num_planes)
+ return sizes[0] < icd->sizeimage ? -EINVAL : 0;
+
+ sizes[0] = icd->sizeimage;
+ *num_planes = 1;
+
dev_dbg(icd->parent, "count=%d, size=%u\n", *count, sizes[0]);
return 0;
@@ -912,7 +888,7 @@ static irqreturn_t rcar_vin_irq(int irq, void *data)
priv->queue_buf[slot]->field = priv->field;
priv->queue_buf[slot]->sequence = priv->sequence++;
- v4l2_get_timestamp(&priv->queue_buf[slot]->timestamp);
+ priv->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns();
vb2_buffer_done(&priv->queue_buf[slot]->vb2_buf,
VB2_BUF_STATE_DONE);
priv->queue_buf[slot] = NULL;
@@ -1853,63 +1829,43 @@ static const struct of_device_id rcar_vin_of_table[] = {
MODULE_DEVICE_TABLE(of, rcar_vin_of_table);
#endif
-static struct platform_device_id rcar_vin_id_table[] = {
- { "r8a7779-vin", RCAR_H1 },
- { "r8a7778-vin", RCAR_M1 },
- { "uPD35004-vin", RCAR_E1 },
- {},
-};
-MODULE_DEVICE_TABLE(platform, rcar_vin_id_table);
-
static int rcar_vin_probe(struct platform_device *pdev)
{
const struct of_device_id *match = NULL;
struct rcar_vin_priv *priv;
+ struct v4l2_of_endpoint ep;
+ struct device_node *np;
struct resource *mem;
- struct rcar_vin_platform_data *pdata;
unsigned int pdata_flags;
int irq, ret;
- if (pdev->dev.of_node) {
- struct v4l2_of_endpoint ep;
- struct device_node *np;
+ match = of_match_device(of_match_ptr(rcar_vin_of_table), &pdev->dev);
- match = of_match_device(of_match_ptr(rcar_vin_of_table),
- &pdev->dev);
-
- np = of_graph_get_next_endpoint(pdev->dev.of_node, NULL);
- if (!np) {
- dev_err(&pdev->dev, "could not find endpoint\n");
- return -EINVAL;
- }
+ np = of_graph_get_next_endpoint(pdev->dev.of_node, NULL);
+ if (!np) {
+ dev_err(&pdev->dev, "could not find endpoint\n");
+ return -EINVAL;
+ }
- ret = v4l2_of_parse_endpoint(np, &ep);
- if (ret) {
- dev_err(&pdev->dev, "could not parse endpoint\n");
- return ret;
- }
+ ret = v4l2_of_parse_endpoint(np, &ep);
+ if (ret) {
+ dev_err(&pdev->dev, "could not parse endpoint\n");
+ return ret;
+ }
- if (ep.bus_type == V4L2_MBUS_BT656)
- pdata_flags = RCAR_VIN_BT656;
- else {
- pdata_flags = 0;
- if (ep.bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
- pdata_flags |= RCAR_VIN_HSYNC_ACTIVE_LOW;
- if (ep.bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
- pdata_flags |= RCAR_VIN_VSYNC_ACTIVE_LOW;
- }
+ if (ep.bus_type == V4L2_MBUS_BT656)
+ pdata_flags = RCAR_VIN_BT656;
+ else {
+ pdata_flags = 0;
+ if (ep.bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ pdata_flags |= RCAR_VIN_HSYNC_ACTIVE_LOW;
+ if (ep.bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ pdata_flags |= RCAR_VIN_VSYNC_ACTIVE_LOW;
+ }
- of_node_put(np);
+ of_node_put(np);
- dev_dbg(&pdev->dev, "pdata_flags = %08x\n", pdata_flags);
- } else {
- pdata = pdev->dev.platform_data;
- if (!pdata || !pdata->flags) {
- dev_err(&pdev->dev, "platform data not set\n");
- return -EINVAL;
- }
- pdata_flags = pdata->flags;
- }
+ dev_dbg(&pdev->dev, "pdata_flags = %08x\n", pdata_flags);
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (mem == NULL)
@@ -1992,7 +1948,6 @@ static struct platform_driver rcar_vin_driver = {
.name = DRV_NAME,
.of_match_table = of_match_ptr(rcar_vin_of_table),
},
- .id_table = rcar_vin_id_table,
};
module_platform_driver(rcar_vin_driver);
diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
index 67a669d826b8..90c87f2b4ec0 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -40,11 +40,11 @@
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/soc_camera.h>
-#include <media/sh_mobile_ceu.h>
-#include <media/sh_mobile_csi2.h>
+#include <media/drv-intf/sh_mobile_ceu.h>
+#include <media/drv-intf/sh_mobile_csi2.h>
#include <media/videobuf2-dma-contig.h>
#include <media/v4l2-mediabus.h>
-#include <media/soc_mediabus.h>
+#include <media/drv-intf/soc_mediabus.h>
#include "soc_scale_crop.h"
@@ -210,43 +210,14 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
* for the current frame format if required
*/
static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *count, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct soc_camera_device *icd = container_of(vq,
struct soc_camera_device, vb2_vidq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
- if (fmt) {
- const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
- fmt->fmt.pix.pixelformat);
- unsigned int bytes_per_line;
- int ret;
-
- if (!xlate)
- return -EINVAL;
-
- ret = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
- xlate->host_fmt);
- if (ret < 0)
- return ret;
-
- bytes_per_line = max_t(u32, fmt->fmt.pix.bytesperline, ret);
-
- ret = soc_mbus_image_size(xlate->host_fmt, bytes_per_line,
- fmt->fmt.pix.height);
- if (ret < 0)
- return ret;
-
- sizes[0] = max_t(u32, fmt->fmt.pix.sizeimage, ret);
- } else {
- /* Called from VIDIOC_REQBUFS or in compatibility mode */
- sizes[0] = icd->sizeimage;
- }
-
alloc_ctxs[0] = pcdev->alloc_ctx;
if (!vq->num_buffers)
@@ -255,8 +226,14 @@ static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq,
if (!*count)
*count = 2;
+ /* Called from VIDIOC_REQBUFS or in compatibility mode */
+ if (!*num_planes)
+ sizes[0] = icd->sizeimage;
+ else if (sizes[0] < icd->sizeimage)
+ return -EINVAL;
+
/* If *num_planes != 0, we have already verified *count. */
- if (pcdev->video_limit && !*num_planes) {
+ if (pcdev->video_limit) {
size_t size = PAGE_ALIGN(sizes[0]) * *count;
if (size + pcdev->buf_total > pcdev->video_limit)
@@ -533,7 +510,7 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data)
pcdev->active = NULL;
ret = sh_mobile_ceu_capture(pcdev);
- v4l2_get_timestamp(&vbuf->timestamp);
+ vbuf->vb2_buf.timestamp = ktime_get_ns();
if (!ret) {
vbuf->field = pcdev->field;
vbuf->sequence = pcdev->sequence++;
diff --git a/drivers/media/platform/soc_camera/sh_mobile_csi2.c b/drivers/media/platform/soc_camera/sh_mobile_csi2.c
index 12d3626ecf22..09b18365a4b1 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_csi2.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_csi2.c
@@ -18,10 +18,10 @@
#include <linux/videodev2.h>
#include <linux/module.h>
-#include <media/sh_mobile_ceu.h>
-#include <media/sh_mobile_csi2.h>
+#include <media/drv-intf/sh_mobile_ceu.h>
+#include <media/drv-intf/sh_mobile_csi2.h>
#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
+#include <media/drv-intf/soc_mediabus.h>
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index dc98122e78dc..cc84c6d6a701 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -30,7 +30,7 @@
#include <linux/vmalloc.h>
#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
+#include <media/drv-intf/soc_mediabus.h>
#include <media/v4l2-async.h>
#include <media/v4l2-clk.h>
#include <media/v4l2-common.h>
@@ -1360,7 +1360,7 @@ static int soc_camera_i2c_init(struct soc_camera_device *icd,
struct soc_camera_host_desc *shd = &sdesc->host_desc;
struct i2c_adapter *adap;
struct v4l2_subdev *subdev;
- char clk_name[V4L2_SUBDEV_NAME_SIZE];
+ char clk_name[V4L2_CLK_NAME_SIZE];
int ret;
/* First find out how we link the main client */
@@ -1391,8 +1391,8 @@ static int soc_camera_i2c_init(struct soc_camera_device *icd,
ssdd->sd_pdata.regulators = NULL;
shd->board_info->platform_data = ssdd;
- snprintf(clk_name, sizeof(clk_name), "%d-%04x",
- shd->i2c_adapter_id, shd->board_info->addr);
+ v4l2_clk_name_i2c(clk_name, sizeof(clk_name),
+ shd->i2c_adapter_id, shd->board_info->addr);
icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd);
if (IS_ERR(icd->clk)) {
@@ -1526,7 +1526,7 @@ static int scan_async_group(struct soc_camera_host *ici,
struct soc_camera_async_client *sasc;
struct soc_camera_device *icd;
struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,};
- char clk_name[V4L2_SUBDEV_NAME_SIZE];
+ char clk_name[V4L2_CLK_NAME_SIZE];
unsigned int i;
int ret;
@@ -1572,8 +1572,9 @@ static int scan_async_group(struct soc_camera_host *ici,
icd->sasc = sasc;
icd->parent = ici->v4l2_dev.dev;
- snprintf(clk_name, sizeof(clk_name), "%d-%04x",
- sasd->asd.match.i2c.adapter_id, sasd->asd.match.i2c.address);
+ v4l2_clk_name_i2c(clk_name, sizeof(clk_name),
+ sasd->asd.match.i2c.adapter_id,
+ sasd->asd.match.i2c.address);
icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd);
if (IS_ERR(icd->clk)) {
@@ -1631,7 +1632,7 @@ static int soc_of_bind(struct soc_camera_host *ici,
struct soc_camera_async_client *sasc;
struct soc_of_info *info;
struct i2c_client *client;
- char clk_name[V4L2_SUBDEV_NAME_SIZE + 32];
+ char clk_name[V4L2_CLK_NAME_SIZE];
int ret;
/* allocate a new subdev and add match info to it */
@@ -1674,11 +1675,11 @@ static int soc_of_bind(struct soc_camera_host *ici,
client = of_find_i2c_device_by_node(remote);
if (client)
- snprintf(clk_name, sizeof(clk_name), "%d-%04x",
- client->adapter->nr, client->addr);
+ v4l2_clk_name_i2c(clk_name, sizeof(clk_name),
+ client->adapter->nr, client->addr);
else
- snprintf(clk_name, sizeof(clk_name), "of-%s",
- of_node_full_name(remote));
+ v4l2_clk_name_of(clk_name, sizeof(clk_name),
+ of_node_full_name(remote));
icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, icd);
if (IS_ERR(icd->clk)) {
diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c
index cc8eb0758219..a51d2a42998c 100644
--- a/drivers/media/platform/soc_camera/soc_camera_platform.c
+++ b/drivers/media/platform/soc_camera/soc_camera_platform.c
@@ -18,7 +18,7 @@
#include <linux/videodev2.h>
#include <media/v4l2-subdev.h>
#include <media/soc_camera.h>
-#include <media/soc_camera_platform.h>
+#include <linux/platform_data/media/soc_camera_platform.h>
struct soc_camera_platform_priv {
struct v4l2_subdev subdev;
diff --git a/drivers/media/platform/soc_camera/soc_mediabus.c b/drivers/media/platform/soc_camera/soc_mediabus.c
index 1dbcd426683c..e3e665e1c503 100644
--- a/drivers/media/platform/soc_camera/soc_mediabus.c
+++ b/drivers/media/platform/soc_camera/soc_mediabus.c
@@ -13,7 +13,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-mediabus.h>
-#include <media/soc_mediabus.h>
+#include <media/drv-intf/soc_mediabus.h>
static const struct soc_mbus_lookup mbus_fmt[] = {
{
diff --git a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c
index a0d267e017f6..d12a419c044a 100644
--- a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c
+++ b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c
@@ -191,7 +191,7 @@ static void bdisp_job_finish(struct bdisp_ctx *ctx, int vb_state)
dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
if (src_vb && dst_vb) {
- dst_vb->timestamp = src_vb->timestamp;
+ dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
dst_vb->timecode = src_vb->timecode;
dst_vb->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
dst_vb->flags |= src_vb->flags &
@@ -297,7 +297,7 @@ static int bdisp_get_bufs(struct bdisp_ctx *ctx)
if (ret)
return ret;
- dst_vb->timestamp = src_vb->timestamp;
+ dst_vb->vb2_buf.timestamp = src_vb->vb2_buf.timestamp;
return 0;
}
@@ -438,11 +438,9 @@ static void bdisp_ctrls_delete(struct bdisp_ctx *ctx)
}
static int bdisp_queue_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *nb_buf, unsigned int *nb_planes,
unsigned int sizes[], void *allocators[])
{
- const struct v4l2_format *fmt = parg;
struct bdisp_ctx *ctx = vb2_get_drv_priv(vq);
struct bdisp_frame *frame = ctx_get_frame(ctx, vq->type);
@@ -455,13 +453,13 @@ static int bdisp_queue_setup(struct vb2_queue *vq,
dev_err(ctx->bdisp_dev->dev, "Invalid format\n");
return -EINVAL;
}
+ allocators[0] = ctx->bdisp_dev->alloc_ctx;
- if (fmt && fmt->fmt.pix.sizeimage < frame->sizeimage)
- return -EINVAL;
+ if (*nb_planes)
+ return sizes[0] < frame->sizeimage ? -EINVAL : 0;
*nb_planes = 1;
- sizes[0] = fmt ? fmt->fmt.pix.sizeimage : frame->sizeimage;
- allocators[0] = ctx->bdisp_dev->alloc_ctx;
+ sizes[0] = frame->sizeimage;
return 0;
}
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c
index 95223ab71e19..2dfbe8ab5214 100644
--- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c
+++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c
@@ -209,18 +209,18 @@ void c8sectpfe_tuner_unregister_frontend(struct c8sectpfe *c8sectpfe,
tsin = fei->channel_data[n];
- if (tsin && tsin->frontend) {
- dvb_unregister_frontend(tsin->frontend);
- dvb_frontend_detach(tsin->frontend);
- }
+ if (tsin) {
+ if (tsin->frontend) {
+ dvb_unregister_frontend(tsin->frontend);
+ dvb_frontend_detach(tsin->frontend);
+ }
- if (tsin && tsin->i2c_adapter)
i2c_put_adapter(tsin->i2c_adapter);
- if (tsin && tsin->i2c_client) {
- if (tsin->i2c_client->dev.driver->owner)
+ if (tsin->i2c_client) {
module_put(tsin->i2c_client->dev.driver->owner);
- i2c_unregister_device(tsin->i2c_client);
+ i2c_unregister_device(tsin->i2c_client);
+ }
}
}
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
index 8490a65ae1c6..78e3cb9a628f 100644
--- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
+++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
@@ -823,7 +823,7 @@ static int c8sectpfe_probe(struct platform_device *pdev)
}
of_node_put(i2c_bus);
- tsin->rst_gpio = of_get_named_gpio(child, "rst-gpio", 0);
+ tsin->rst_gpio = of_get_named_gpio(child, "reset-gpios", 0);
ret = gpio_is_valid(tsin->rst_gpio);
if (!ret) {
diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c
index de24effd984f..1fa00c2cf3d7 100644
--- a/drivers/media/platform/ti-vpe/vpe.c
+++ b/drivers/media/platform/ti-vpe/vpe.c
@@ -1288,7 +1288,7 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data)
d_vb = ctx->dst_vb;
d_vb->flags = s_vb->flags;
- d_vb->timestamp = s_vb->timestamp;
+ d_vb->vb2_buf.timestamp = s_vb->vb2_buf.timestamp;
if (s_vb->flags & V4L2_BUF_FLAG_TIMECODE)
d_vb->timecode = s_vb->timecode;
@@ -1796,7 +1796,6 @@ static const struct v4l2_ioctl_ops vpe_ioctl_ops = {
* Queue operations
*/
static int vpe_queue_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
diff --git a/drivers/media/platform/timblogiw.c b/drivers/media/platform/timblogiw.c
index 5820e45b3a9f..113c9f3c0b3e 100644
--- a/drivers/media/platform/timblogiw.c
+++ b/drivers/media/platform/timblogiw.c
@@ -31,7 +31,7 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-device.h>
#include <media/videobuf-dma-contig.h>
-#include <media/timb_video.h>
+#include <linux/platform_data/media/timb_video.h>
#define DRIVER_NAME "timb-video"
diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c
index 32e4ff46daf3..1254f7e4d732 100644
--- a/drivers/media/platform/via-camera.c
+++ b/drivers/media/platform/via-camera.c
@@ -19,7 +19,7 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-image-sizes.h>
-#include <media/ov7670.h>
+#include <media/i2c/ov7670.h>
#include <media/videobuf-dma-sg.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c
index e18fb9f9ed2f..418113c99801 100644
--- a/drivers/media/platform/vim2m.c
+++ b/drivers/media/platform/vim2m.c
@@ -235,7 +235,7 @@ static int device_process(struct vim2m_ctx *ctx,
out_vb->sequence =
get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)->sequence++;
in_vb->sequence = q_data->sequence++;
- out_vb->timestamp = in_vb->timestamp;
+ out_vb->vb2_buf.timestamp = in_vb->vb2_buf.timestamp;
if (in_vb->flags & V4L2_BUF_FLAG_TIMECODE)
out_vb->timecode = in_vb->timecode;
@@ -710,11 +710,9 @@ static const struct v4l2_ioctl_ops vim2m_ioctl_ops = {
*/
static int vim2m_queue_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct vim2m_ctx *ctx = vb2_get_drv_priv(vq);
struct vim2m_q_data *q_data;
unsigned int size, count = *nbuffers;
@@ -723,17 +721,14 @@ static int vim2m_queue_setup(struct vb2_queue *vq,
size = q_data->width * q_data->height * q_data->fmt->depth >> 3;
- if (fmt) {
- if (fmt->fmt.pix.sizeimage < size)
- return -EINVAL;
- size = fmt->fmt.pix.sizeimage;
- }
-
while (size * count > MEM2MEM_VID_MEM_LIMIT)
(count)--;
+ *nbuffers = count;
+
+ if (*nplanes)
+ return sizes[0] < size ? -EINVAL : 0;
*nplanes = 1;
- *nbuffers = count;
sizes[0] = size;
/*
diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h
index 55b304a705d5..751c1ba391e9 100644
--- a/drivers/media/platform/vivid/vivid-core.h
+++ b/drivers/media/platform/vivid/vivid-core.h
@@ -264,6 +264,7 @@ struct vivid_dev {
bool vflip;
bool vbi_cap_interlaced;
bool loop_video;
+ bool reduced_fps;
/* Framebuffer */
unsigned long video_pbase;
@@ -285,7 +286,7 @@ struct vivid_dev {
bool dqbuf_error;
bool seq_wrap;
bool time_wrap;
- __kernel_time_t time_wrap_offset;
+ u64 time_wrap_offset;
unsigned perc_dropped_buffers;
enum vivid_signal_mode std_signal_mode;
unsigned query_std_last;
diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c
index f41ac0b01fec..b98089c95ef5 100644
--- a/drivers/media/platform/vivid/vivid-ctrls.c
+++ b/drivers/media/platform/vivid/vivid-ctrls.c
@@ -78,6 +78,7 @@
#define VIVID_CID_TIME_WRAP (VIVID_CID_VIVID_BASE + 39)
#define VIVID_CID_MAX_EDID_BLOCKS (VIVID_CID_VIVID_BASE + 40)
#define VIVID_CID_PERCENTAGE_FILL (VIVID_CID_VIVID_BASE + 41)
+#define VIVID_CID_REDUCED_FPS (VIVID_CID_VIVID_BASE + 42)
#define VIVID_CID_STD_SIGNAL_MODE (VIVID_CID_VIVID_BASE + 60)
#define VIVID_CID_STANDARD (VIVID_CID_VIVID_BASE + 61)
@@ -424,6 +425,10 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl)
dev->sensor_vflip = ctrl->val;
tpg_s_vflip(&dev->tpg, dev->sensor_vflip ^ dev->vflip);
break;
+ case VIVID_CID_REDUCED_FPS:
+ dev->reduced_fps = ctrl->val;
+ vivid_update_format_cap(dev, true);
+ break;
case VIVID_CID_HAS_CROP_CAP:
dev->has_crop_cap = ctrl->val;
vivid_update_format_cap(dev, true);
@@ -601,6 +606,15 @@ static const struct v4l2_ctrl_config vivid_ctrl_vflip = {
.step = 1,
};
+static const struct v4l2_ctrl_config vivid_ctrl_reduced_fps = {
+ .ops = &vivid_vid_cap_ctrl_ops,
+ .id = VIVID_CID_REDUCED_FPS,
+ .name = "Reduced Framerate",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .max = 1,
+ .step = 1,
+};
+
static const struct v4l2_ctrl_config vivid_ctrl_has_crop_cap = {
.ops = &vivid_vid_cap_ctrl_ops,
.id = VIVID_CID_HAS_CROP_CAP,
@@ -940,7 +954,7 @@ static const struct v4l2_ctrl_config vivid_ctrl_has_scaler_out = {
static int vivid_streaming_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_streaming);
- struct timeval tv;
+ u64 rem;
switch (ctrl->id) {
case VIVID_CID_DQBUF_ERROR:
@@ -979,8 +993,16 @@ static int vivid_streaming_s_ctrl(struct v4l2_ctrl *ctrl)
dev->time_wrap_offset = 0;
break;
}
- v4l2_get_timestamp(&tv);
- dev->time_wrap_offset = -tv.tv_sec - 16;
+ /*
+ * We want to set the time 16 seconds before the 32 bit tv_sec
+ * value of struct timeval would wrap around. So first we
+ * calculate ktime_get_ns() % ((1 << 32) * NSEC_PER_SEC), and
+ * then we set the offset to ((1 << 32) - 16) * NSEC_PER_SEC).
+ */
+ div64_u64_rem(ktime_get_ns(),
+ 0x100000000ULL * NSEC_PER_SEC, &rem);
+ dev->time_wrap_offset =
+ (0x100000000ULL - 16) * NSEC_PER_SEC - rem;
break;
}
return 0;
@@ -1340,11 +1362,13 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap,
v4l2_ctrl_handler_init(hdl_vid_cap, 55);
v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_class, NULL);
v4l2_ctrl_handler_init(hdl_vid_out, 26);
- v4l2_ctrl_new_custom(hdl_vid_out, &vivid_ctrl_class, NULL);
+ if (!no_error_inj)
+ v4l2_ctrl_new_custom(hdl_vid_out, &vivid_ctrl_class, NULL);
v4l2_ctrl_handler_init(hdl_vbi_cap, 21);
v4l2_ctrl_new_custom(hdl_vbi_cap, &vivid_ctrl_class, NULL);
v4l2_ctrl_handler_init(hdl_vbi_out, 19);
- v4l2_ctrl_new_custom(hdl_vbi_out, &vivid_ctrl_class, NULL);
+ if (!no_error_inj)
+ v4l2_ctrl_new_custom(hdl_vbi_out, &vivid_ctrl_class, NULL);
v4l2_ctrl_handler_init(hdl_radio_rx, 17);
v4l2_ctrl_new_custom(hdl_radio_rx, &vivid_ctrl_class, NULL);
v4l2_ctrl_handler_init(hdl_radio_tx, 17);
@@ -1414,6 +1438,7 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap,
v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_vflip, NULL);
v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_insert_sav, NULL);
v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_insert_eav, NULL);
+ v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_reduced_fps, NULL);
if (show_ccs_cap) {
dev->ctrl_has_crop_cap = v4l2_ctrl_new_custom(hdl_vid_cap,
&vivid_ctrl_has_crop_cap, NULL);
diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c
index 83cc6d3b4784..9034281944a4 100644
--- a/drivers/media/platform/vivid/vivid-kthread-cap.c
+++ b/drivers/media/platform/vivid/vivid-kthread-cap.c
@@ -441,7 +441,7 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf)
* "Start of Exposure".
*/
if (dev->tstamp_src_is_soe)
- v4l2_get_timestamp(&buf->vb.timestamp);
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
if (dev->field_cap == V4L2_FIELD_ALTERNATE) {
/*
* 60 Hz standards start with the bottom field, 50 Hz standards
@@ -558,8 +558,8 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf)
* the timestamp now.
*/
if (!dev->tstamp_src_is_soe)
- v4l2_get_timestamp(&buf->vb.timestamp);
- buf->vb.timestamp.tv_sec += dev->time_wrap_offset;
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
+ buf->vb.vb2_buf.timestamp += dev->time_wrap_offset;
}
/*
diff --git a/drivers/media/platform/vivid/vivid-kthread-out.c b/drivers/media/platform/vivid/vivid-kthread-out.c
index c2c46dcdbe95..98eed5889bc1 100644
--- a/drivers/media/platform/vivid/vivid-kthread-out.c
+++ b/drivers/media/platform/vivid/vivid-kthread-out.c
@@ -95,8 +95,8 @@ static void vivid_thread_vid_out_tick(struct vivid_dev *dev)
*/
vid_out_buf->vb.sequence /= 2;
}
- v4l2_get_timestamp(&vid_out_buf->vb.timestamp);
- vid_out_buf->vb.timestamp.tv_sec += dev->time_wrap_offset;
+ vid_out_buf->vb.vb2_buf.timestamp =
+ ktime_get_ns() + dev->time_wrap_offset;
vb2_buffer_done(&vid_out_buf->vb.vb2_buf, dev->dqbuf_error ?
VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
dprintk(dev, 2, "vid_out buffer %d done\n",
@@ -108,8 +108,8 @@ static void vivid_thread_vid_out_tick(struct vivid_dev *dev)
vivid_sliced_vbi_out_process(dev, vbi_out_buf);
vbi_out_buf->vb.sequence = dev->vbi_out_seq_count;
- v4l2_get_timestamp(&vbi_out_buf->vb.timestamp);
- vbi_out_buf->vb.timestamp.tv_sec += dev->time_wrap_offset;
+ vbi_out_buf->vb.vb2_buf.timestamp =
+ ktime_get_ns() + dev->time_wrap_offset;
vb2_buffer_done(&vbi_out_buf->vb.vb2_buf, dev->dqbuf_error ?
VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
dprintk(dev, 2, "vbi_out buffer %d done\n",
diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.c b/drivers/media/platform/vivid/vivid-sdr-cap.c
index 082c401764ce..3d1604cb982f 100644
--- a/drivers/media/platform/vivid/vivid-sdr-cap.c
+++ b/drivers/media/platform/vivid/vivid-sdr-cap.c
@@ -117,8 +117,8 @@ static void vivid_thread_sdr_cap_tick(struct vivid_dev *dev)
if (sdr_cap_buf) {
sdr_cap_buf->vb.sequence = dev->sdr_cap_seq_count;
vivid_sdr_cap_process(dev, sdr_cap_buf);
- v4l2_get_timestamp(&sdr_cap_buf->vb.timestamp);
- sdr_cap_buf->vb.timestamp.tv_sec += dev->time_wrap_offset;
+ sdr_cap_buf->vb.vb2_buf.timestamp =
+ ktime_get_ns() + dev->time_wrap_offset;
vb2_buffer_done(&sdr_cap_buf->vb.vb2_buf, dev->dqbuf_error ?
VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
dev->dqbuf_error = false;
@@ -213,7 +213,7 @@ static int vivid_thread_sdr_cap(void *data)
return 0;
}
-static int sdr_cap_queue_setup(struct vb2_queue *vq, const void *parg,
+static int sdr_cap_queue_setup(struct vb2_queue *vq,
unsigned *nbuffers, unsigned *nplanes,
unsigned sizes[], void *alloc_ctxs[])
{
diff --git a/drivers/media/platform/vivid/vivid-vbi-cap.c b/drivers/media/platform/vivid/vivid-vbi-cap.c
index e903d023e9df..cda45a582bfe 100644
--- a/drivers/media/platform/vivid/vivid-vbi-cap.c
+++ b/drivers/media/platform/vivid/vivid-vbi-cap.c
@@ -108,8 +108,7 @@ void vivid_raw_vbi_cap_process(struct vivid_dev *dev, struct vivid_buffer *buf)
if (!VIVID_INVALID_SIGNAL(dev->std_signal_mode))
vivid_vbi_gen_raw(&dev->vbi_gen, &vbi, vbuf);
- v4l2_get_timestamp(&buf->vb.timestamp);
- buf->vb.timestamp.tv_sec += dev->time_wrap_offset;
+ buf->vb.vb2_buf.timestamp = ktime_get_ns() + dev->time_wrap_offset;
}
@@ -133,11 +132,10 @@ void vivid_sliced_vbi_cap_process(struct vivid_dev *dev,
vbuf[i] = dev->vbi_gen.data[i];
}
- v4l2_get_timestamp(&buf->vb.timestamp);
- buf->vb.timestamp.tv_sec += dev->time_wrap_offset;
+ buf->vb.vb2_buf.timestamp = ktime_get_ns() + dev->time_wrap_offset;
}
-static int vbi_cap_queue_setup(struct vb2_queue *vq, const void *parg,
+static int vbi_cap_queue_setup(struct vb2_queue *vq,
unsigned *nbuffers, unsigned *nplanes,
unsigned sizes[], void *alloc_ctxs[])
{
diff --git a/drivers/media/platform/vivid/vivid-vbi-out.c b/drivers/media/platform/vivid/vivid-vbi-out.c
index 75c5709f938e..3c5a469e6f49 100644
--- a/drivers/media/platform/vivid/vivid-vbi-out.c
+++ b/drivers/media/platform/vivid/vivid-vbi-out.c
@@ -27,7 +27,7 @@
#include "vivid-vbi-out.h"
#include "vivid-vbi-cap.h"
-static int vbi_out_queue_setup(struct vb2_queue *vq, const void *parg,
+static int vbi_out_queue_setup(struct vb2_queue *vq,
unsigned *nbuffers, unsigned *nplanes,
unsigned sizes[], void *alloc_ctxs[])
{
diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c
index ef5412311b2f..b84f081c1b92 100644
--- a/drivers/media/platform/vivid/vivid-vid-cap.c
+++ b/drivers/media/platform/vivid/vivid-vid-cap.c
@@ -95,11 +95,10 @@ static const struct v4l2_discrete_probe webcam_probe = {
VIVID_WEBCAM_SIZES
};
-static int vid_cap_queue_setup(struct vb2_queue *vq, const void *parg,
+static int vid_cap_queue_setup(struct vb2_queue *vq,
unsigned *nbuffers, unsigned *nplanes,
unsigned sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct vivid_dev *dev = vb2_get_drv_priv(vq);
unsigned buffers = tpg_g_buffers(&dev->tpg);
unsigned h = dev->fmt_cap_rect.height;
@@ -122,27 +121,16 @@ static int vid_cap_queue_setup(struct vb2_queue *vq, const void *parg,
dev->queue_setup_error = false;
return -EINVAL;
}
- if (fmt) {
- const struct v4l2_pix_format_mplane *mp;
- struct v4l2_format mp_fmt;
- const struct vivid_fmt *vfmt;
-
- if (!V4L2_TYPE_IS_MULTIPLANAR(fmt->type)) {
- fmt_sp2mp(fmt, &mp_fmt);
- fmt = &mp_fmt;
- }
- mp = &fmt->fmt.pix_mp;
+ if (*nplanes) {
/*
- * Check if the number of planes in the specified format match
+ * Check if the number of requested planes match
* the number of buffers in the current format. You can't mix that.
*/
- if (mp->num_planes != buffers)
+ if (*nplanes != buffers)
return -EINVAL;
- vfmt = vivid_get_format(dev, mp->pixelformat);
for (p = 0; p < buffers; p++) {
- sizes[p] = mp->plane_fmt[p].sizeimage;
if (sizes[p] < tpg_g_line_width(&dev->tpg, p) * h +
- vfmt->data_offset[p])
+ dev->fmt_cap->data_offset[p])
return -EINVAL;
}
} else {
@@ -405,6 +393,7 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls)
{
struct v4l2_bt_timings *bt = &dev->dv_timings_cap.bt;
unsigned size;
+ u64 pixelclock;
switch (dev->input_type[dev->input]) {
case WEBCAM:
@@ -434,8 +423,15 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls)
dev->src_rect.width = bt->width;
dev->src_rect.height = bt->height;
size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt);
+ if (dev->reduced_fps && can_reduce_fps(bt)) {
+ pixelclock = div_u64(bt->pixelclock * 1000, 1001);
+ bt->flags |= V4L2_DV_FL_REDUCED_FPS;
+ } else {
+ pixelclock = bt->pixelclock;
+ bt->flags &= ~V4L2_DV_FL_REDUCED_FPS;
+ }
dev->timeperframe_vid_cap = (struct v4l2_fract) {
- size / 100, (u32)bt->pixelclock / 100
+ size / 100, (u32)pixelclock / 100
};
if (bt->interlaced)
dev->field_cap = V4L2_FIELD_ALTERNATE;
@@ -1662,7 +1658,7 @@ int vivid_vid_cap_s_dv_timings(struct file *file, void *_fh,
!valid_cvt_gtf_timings(timings))
return -EINVAL;
- if (v4l2_match_dv_timings(timings, &dev->dv_timings_cap, 0))
+ if (v4l2_match_dv_timings(timings, &dev->dv_timings_cap, 0, false))
return 0;
if (vb2_is_busy(&dev->vb_vid_cap_q))
return -EBUSY;
diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c
index b77acb6a7013..64e4d66482c1 100644
--- a/drivers/media/platform/vivid/vivid-vid-out.c
+++ b/drivers/media/platform/vivid/vivid-vid-out.c
@@ -31,11 +31,10 @@
#include "vivid-kthread-out.h"
#include "vivid-vid-out.h"
-static int vid_out_queue_setup(struct vb2_queue *vq, const void *parg,
+static int vid_out_queue_setup(struct vb2_queue *vq,
unsigned *nbuffers, unsigned *nplanes,
unsigned sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct vivid_dev *dev = vb2_get_drv_priv(vq);
const struct vivid_fmt *vfmt = dev->fmt_out;
unsigned planes = vfmt->buffers;
@@ -64,26 +63,16 @@ static int vid_out_queue_setup(struct vb2_queue *vq, const void *parg,
return -EINVAL;
}
- if (fmt) {
- const struct v4l2_pix_format_mplane *mp;
- struct v4l2_format mp_fmt;
-
- if (!V4L2_TYPE_IS_MULTIPLANAR(fmt->type)) {
- fmt_sp2mp(fmt, &mp_fmt);
- fmt = &mp_fmt;
- }
- mp = &fmt->fmt.pix_mp;
+ if (*nplanes) {
/*
- * Check if the number of planes in the specified format match
+ * Check if the number of requested planes match
* the number of planes in the current format. You can't mix that.
*/
- if (mp->num_planes != planes)
+ if (*nplanes != planes)
return -EINVAL;
- sizes[0] = mp->plane_fmt[0].sizeimage;
if (sizes[0] < size)
return -EINVAL;
for (p = 1; p < planes; p++) {
- sizes[p] = mp->plane_fmt[p].sizeimage;
if (sizes[p] < dev->bytesperline_out[p] * h)
return -EINVAL;
}
@@ -224,6 +213,7 @@ void vivid_update_format_out(struct vivid_dev *dev)
{
struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt;
unsigned size, p;
+ u64 pixelclock;
switch (dev->output_type[dev->output]) {
case SVID:
@@ -245,8 +235,14 @@ void vivid_update_format_out(struct vivid_dev *dev)
dev->sink_rect.width = bt->width;
dev->sink_rect.height = bt->height;
size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt);
+
+ if (can_reduce_fps(bt) && (bt->flags & V4L2_DV_FL_REDUCED_FPS))
+ pixelclock = div_u64(bt->pixelclock * 1000, 1001);
+ else
+ pixelclock = bt->pixelclock;
+
dev->timeperframe_vid_out = (struct v4l2_fract) {
- size / 100, (u32)bt->pixelclock / 100
+ size / 100, (u32)pixelclock / 100
};
if (bt->interlaced)
dev->field_out = V4L2_FIELD_ALTERNATE;
@@ -1149,7 +1145,7 @@ int vivid_vid_out_s_dv_timings(struct file *file, void *_fh,
0, NULL, NULL) &&
!valid_cvt_gtf_timings(timings))
return -EINVAL;
- if (v4l2_match_dv_timings(timings, &dev->dv_timings_out, 0))
+ if (v4l2_match_dv_timings(timings, &dev->dv_timings_out, 0, true))
return 0;
if (vb2_is_busy(&dev->vb_vid_out_q))
return -EBUSY;
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
index 4e61886384e3..42dff9d020af 100644
--- a/drivers/media/platform/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -101,7 +101,7 @@ static int vsp1_create_links(struct vsp1_device *vsp1, struct vsp1_entity *sink)
if (!(entity->pads[pad].flags & MEDIA_PAD_FL_SINK))
continue;
- ret = media_entity_create_link(&source->subdev.entity,
+ ret = media_create_pad_link(&source->subdev.entity,
source->source_pad,
entity, pad, flags);
if (ret < 0)
@@ -127,6 +127,7 @@ static void vsp1_destroy_entities(struct vsp1_device *vsp1)
v4l2_device_unregister(&vsp1->v4l2_dev);
media_device_unregister(&vsp1->media_dev);
+ media_device_cleanup(&vsp1->media_dev);
}
static int vsp1_create_entities(struct vsp1_device *vsp1)
@@ -141,12 +142,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
strlcpy(mdev->model, "VSP1", sizeof(mdev->model));
snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s",
dev_name(mdev->dev));
- ret = media_device_register(mdev);
- if (ret < 0) {
- dev_err(vsp1->dev, "media device registration failed (%d)\n",
- ret);
- return ret;
- }
+ media_device_init(mdev);
vdev->mdev = mdev;
ret = v4l2_device_register(vsp1->dev, vdev);
@@ -250,34 +246,44 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
list_add_tail(&wpf->entity.list_dev, &vsp1->entities);
}
- /* Create links. */
+ /* Register all subdevs. */
list_for_each_entry(entity, &vsp1->entities, list_dev) {
- if (entity->type == VSP1_ENTITY_LIF ||
- entity->type == VSP1_ENTITY_RPF)
- continue;
-
- ret = vsp1_create_links(vsp1, entity);
+ ret = v4l2_device_register_subdev(&vsp1->v4l2_dev,
+ &entity->subdev);
if (ret < 0)
goto done;
}
+ /* Create links. */
+ list_for_each_entry(entity, &vsp1->entities, list_dev) {
+ if (entity->type == VSP1_ENTITY_LIF) {
+ ret = vsp1_wpf_create_links(vsp1, entity);
+ if (ret < 0)
+ goto done;
+ } else if (entity->type == VSP1_ENTITY_RPF) {
+ ret = vsp1_rpf_create_links(vsp1, entity);
+ if (ret < 0)
+ goto done;
+ } else {
+ ret = vsp1_create_links(vsp1, entity);
+ if (ret < 0)
+ goto done;
+ }
+ }
+
if (vsp1->pdata.features & VSP1_HAS_LIF) {
- ret = media_entity_create_link(
+ ret = media_create_pad_link(
&vsp1->wpf[0]->entity.subdev.entity, RWPF_PAD_SOURCE,
&vsp1->lif->entity.subdev.entity, LIF_PAD_SINK, 0);
if (ret < 0)
return ret;
}
- /* Register all subdevs. */
- list_for_each_entry(entity, &vsp1->entities, list_dev) {
- ret = v4l2_device_register_subdev(&vsp1->v4l2_dev,
- &entity->subdev);
- if (ret < 0)
- goto done;
- }
-
ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ ret = media_device_register(mdev);
done:
if (ret < 0)
diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c
index fd95a75b04f4..d7308530952f 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.c
+++ b/drivers/media/platform/vsp1/vsp1_entity.c
@@ -219,8 +219,8 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
entity->pads[num_pads - 1].flags = MEDIA_PAD_FL_SOURCE;
/* Initialize the media entity. */
- return media_entity_init(&entity->subdev.entity, num_pads,
- entity->pads, 0);
+ return media_entity_pads_init(&entity->subdev.entity, num_pads,
+ entity->pads);
}
void vsp1_entity_destroy(struct vsp1_entity *entity)
diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c
index cd5248a9a271..924538223d3e 100644
--- a/drivers/media/platform/vsp1/vsp1_rpf.c
+++ b/drivers/media/platform/vsp1/vsp1_rpf.c
@@ -277,18 +277,29 @@ struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)
rpf->entity.video = video;
- /* Connect the video device to the RPF. */
- ret = media_entity_create_link(&rpf->video.video.entity, 0,
- &rpf->entity.subdev.entity,
- RWPF_PAD_SINK,
- MEDIA_LNK_FL_ENABLED |
- MEDIA_LNK_FL_IMMUTABLE);
- if (ret < 0)
- goto error;
-
return rpf;
error:
vsp1_entity_destroy(&rpf->entity);
return ERR_PTR(ret);
}
+
+/*
+ * vsp1_rpf_create_links() - RPF pads links creation
+ * @vsp1: Pointer to VSP1 device
+ * @entity: Pointer to VSP1 entity
+ *
+ * return negative error code or zero on success
+ */
+int vsp1_rpf_create_links(struct vsp1_device *vsp1,
+ struct vsp1_entity *entity)
+{
+ struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
+
+ /* Connect the video device to the RPF. */
+ return media_create_pad_link(&rpf->video.video.entity, 0,
+ &rpf->entity.subdev.entity,
+ RWPF_PAD_SINK,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+}
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h
index f452dce1a931..731d36e5258d 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.h
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.h
@@ -50,6 +50,11 @@ static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev)
struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index);
struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index);
+int vsp1_rpf_create_links(struct vsp1_device *vsp1,
+ struct vsp1_entity *entity);
+int vsp1_wpf_create_links(struct vsp1_device *vsp1,
+ struct vsp1_entity *entity);
+
int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code);
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index 5ce88e1f5d71..637d0d6f79fb 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -160,8 +160,7 @@ vsp1_video_remote_subdev(struct media_pad *local, u32 *pad)
struct media_pad *remote;
remote = media_entity_remote_pad(local);
- if (remote == NULL ||
- media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (!remote || !is_media_entity_v4l2_subdev(remote->entity))
return NULL;
if (pad)
@@ -274,35 +273,6 @@ static int __vsp1_video_try_format(struct vsp1_video *video,
return 0;
}
-static bool
-vsp1_video_format_adjust(struct vsp1_video *video,
- const struct v4l2_pix_format_mplane *format,
- struct v4l2_pix_format_mplane *adjust)
-{
- unsigned int i;
-
- *adjust = *format;
- __vsp1_video_try_format(video, adjust, NULL);
-
- if (format->width != adjust->width ||
- format->height != adjust->height ||
- format->pixelformat != adjust->pixelformat ||
- format->num_planes != adjust->num_planes)
- return false;
-
- for (i = 0; i < format->num_planes; ++i) {
- if (format->plane_fmt[i].bytesperline !=
- adjust->plane_fmt[i].bytesperline)
- return false;
-
- adjust->plane_fmt[i].sizeimage =
- max(adjust->plane_fmt[i].sizeimage,
- format->plane_fmt[i].sizeimage);
- }
-
- return true;
-}
-
/* -----------------------------------------------------------------------------
* Pipeline Management
*/
@@ -312,24 +282,35 @@ static int vsp1_pipeline_validate_branch(struct vsp1_pipeline *pipe,
struct vsp1_rwpf *output)
{
struct vsp1_entity *entity;
- unsigned int entities = 0;
+ struct media_entity_enum ent_enum;
struct media_pad *pad;
+ int rval;
bool bru_found = false;
input->location.left = 0;
input->location.top = 0;
+ rval = media_entity_enum_init(
+ &ent_enum, input->entity.pads[RWPF_PAD_SOURCE].graph_obj.mdev);
+ if (rval)
+ return rval;
+
pad = media_entity_remote_pad(&input->entity.pads[RWPF_PAD_SOURCE]);
while (1) {
- if (pad == NULL)
- return -EPIPE;
+ if (pad == NULL) {
+ rval = -EPIPE;
+ goto out;
+ }
/* We've reached a video node, that shouldn't have happened. */
- if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
- return -EPIPE;
+ if (!is_media_entity_v4l2_subdev(pad->entity)) {
+ rval = -EPIPE;
+ goto out;
+ }
- entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity));
+ entity = to_vsp1_entity(
+ media_entity_to_v4l2_subdev(pad->entity));
/* A BRU is present in the pipeline, store the compose rectangle
* location in the input RPF for use when configuring the RPF.
@@ -352,15 +333,18 @@ static int vsp1_pipeline_validate_branch(struct vsp1_pipeline *pipe,
break;
/* Ensure the branch has no loop. */
- if (entities & (1 << entity->subdev.entity.id))
- return -EPIPE;
-
- entities |= 1 << entity->subdev.entity.id;
+ if (media_entity_enum_test_and_set(&ent_enum,
+ &entity->subdev.entity)) {
+ rval = -EPIPE;
+ goto out;
+ }
/* UDS can't be chained. */
if (entity->type == VSP1_ENTITY_UDS) {
- if (pipe->uds)
- return -EPIPE;
+ if (pipe->uds) {
+ rval = -EPIPE;
+ goto out;
+ }
pipe->uds = entity;
pipe->uds_input = bru_found ? pipe->bru
@@ -378,9 +362,12 @@ static int vsp1_pipeline_validate_branch(struct vsp1_pipeline *pipe,
/* The last entity must be the output WPF. */
if (entity != &output->entity)
- return -EPIPE;
+ rval = -EPIPE;
- return 0;
+out:
+ media_entity_enum_cleanup(&ent_enum);
+
+ return rval;
}
static void __vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe)
@@ -409,13 +396,19 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe,
{
struct media_entity_graph graph;
struct media_entity *entity = &video->video.entity;
- struct media_device *mdev = entity->parent;
+ struct media_device *mdev = entity->graph_obj.mdev;
unsigned int i;
int ret;
mutex_lock(&mdev->graph_mutex);
/* Walk the graph to locate the entities and video nodes. */
+ ret = media_entity_graph_walk_init(&graph, mdev);
+ if (ret) {
+ mutex_unlock(&mdev->graph_mutex);
+ return ret;
+ }
+
media_entity_graph_walk_start(&graph, entity);
while ((entity = media_entity_graph_walk_next(&graph))) {
@@ -423,7 +416,7 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe,
struct vsp1_rwpf *rwpf;
struct vsp1_entity *e;
- if (media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV) {
+ if (is_media_entity_v4l2_io(entity)) {
pipe->num_video++;
continue;
}
@@ -449,6 +442,8 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe,
mutex_unlock(&mdev->graph_mutex);
+ media_entity_graph_walk_cleanup(&graph);
+
/* We need one output and at least one input. */
if (pipe->num_inputs == 0 || !pipe->output) {
ret = -EPIPE;
@@ -611,7 +606,7 @@ vsp1_video_complete_buffer(struct vsp1_video *video)
spin_unlock_irqrestore(&video->irqlock, flags);
done->buf.sequence = video->sequence++;
- v4l2_get_timestamp(&done->buf.timestamp);
+ done->buf.vb2_buf.timestamp = ktime_get_ns();
for (i = 0; i < done->buf.vb2_buf.num_planes; ++i)
vb2_set_plane_payload(&done->buf.vb2_buf, i, done->length[i]);
vb2_buffer_done(&done->buf.vb2_buf, VB2_BUF_STATE_DONE);
@@ -692,7 +687,7 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
pad = media_entity_remote_pad(&input->pads[RWPF_PAD_SOURCE]);
while (pad) {
- if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (!is_media_entity_v4l2_subdev(pad->entity))
break;
entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity));
@@ -787,26 +782,24 @@ void vsp1_pipelines_resume(struct vsp1_device *vsp1)
*/
static int
-vsp1_video_queue_setup(struct vb2_queue *vq, const void *parg,
+vsp1_video_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct vsp1_video *video = vb2_get_drv_priv(vq);
- const struct v4l2_pix_format_mplane *format;
- struct v4l2_pix_format_mplane pix_mp;
+ const struct v4l2_pix_format_mplane *format = &video->format;
unsigned int i;
- if (fmt) {
- /* Make sure the format is valid and adjust the sizeimage field
- * if needed.
- */
- if (!vsp1_video_format_adjust(video, &fmt->fmt.pix_mp, &pix_mp))
+ if (*nplanes) {
+ if (*nplanes != format->num_planes)
return -EINVAL;
- format = &pix_mp;
- } else {
- format = &video->format;
+ for (i = 0; i < *nplanes; i++) {
+ if (sizes[i] < format->plane_fmt[i].sizeimage)
+ return -EINVAL;
+ alloc_ctxs[i] = video->alloc_ctx;
+ }
+ return 0;
}
*nplanes = format->num_planes;
@@ -1224,7 +1217,7 @@ int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf)
video->pipe.state = VSP1_PIPELINE_STOPPED;
/* Initialize the media entity... */
- ret = media_entity_init(&video->video.entity, 1, &video->pad, 0);
+ ret = media_entity_pads_init(&video->video.entity, 1, &video->pad);
if (ret < 0)
return ret;
diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c
index 95b62f4f77e7..cbf514a6582d 100644
--- a/drivers/media/platform/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/vsp1/vsp1_wpf.c
@@ -220,7 +220,6 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)
struct v4l2_subdev *subdev;
struct vsp1_video *video;
struct vsp1_rwpf *wpf;
- unsigned int flags;
int ret;
wpf = devm_kzalloc(vsp1->dev, sizeof(*wpf), GFP_KERNEL);
@@ -276,20 +275,6 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)
goto error;
wpf->entity.video = video;
-
- /* Connect the video device to the WPF. All connections are immutable
- * except for the WPF0 source link if a LIF is present.
- */
- flags = MEDIA_LNK_FL_ENABLED;
- if (!(vsp1->pdata.features & VSP1_HAS_LIF) || index != 0)
- flags |= MEDIA_LNK_FL_IMMUTABLE;
-
- ret = media_entity_create_link(&wpf->entity.subdev.entity,
- RWPF_PAD_SOURCE,
- &wpf->video.video.entity, 0, flags);
- if (ret < 0)
- goto error;
-
wpf->entity.sink = &wpf->video.video.entity;
return wpf;
@@ -298,3 +283,28 @@ error:
vsp1_entity_destroy(&wpf->entity);
return ERR_PTR(ret);
}
+
+/*
+ * vsp1_wpf_create_links() - RPF pads links creation
+ * @vsp1: Pointer to VSP1 device
+ * @entity: Pointer to VSP1 entity
+ *
+ * return negative error code or zero on success
+ */
+int vsp1_wpf_create_links(struct vsp1_device *vsp1,
+ struct vsp1_entity *entity)
+{
+ struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
+ unsigned int flags;
+
+ /* Connect the video device to the WPF. All connections are immutable
+ * except for the WPF0 source link if a LIF is present.
+ */
+ flags = MEDIA_LNK_FL_ENABLED;
+ if (!(vsp1->pdata.features & VSP1_HAS_LIF) || entity->index != 0)
+ flags |= MEDIA_LNK_FL_IMMUTABLE;
+
+ return media_create_pad_link(&wpf->entity.subdev.entity,
+ RWPF_PAD_SOURCE,
+ &wpf->video.video.entity, 0, flags);
+}
diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c
index d11cc7072cd5..7f6898b13cac 100644
--- a/drivers/media/platform/xilinx/xilinx-dma.c
+++ b/drivers/media/platform/xilinx/xilinx-dma.c
@@ -49,8 +49,7 @@ xvip_dma_remote_subdev(struct media_pad *local, u32 *pad)
struct media_pad *remote;
remote = media_entity_remote_pad(local);
- if (remote == NULL ||
- media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (!remote || !is_media_entity_v4l2_subdev(remote->entity))
return NULL;
if (pad)
@@ -113,8 +112,7 @@ static int xvip_pipeline_start_stop(struct xvip_pipeline *pipe, bool start)
break;
pad = media_entity_remote_pad(pad);
- if (pad == NULL ||
- media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
break;
entity = pad->entity;
@@ -181,19 +179,26 @@ static int xvip_pipeline_validate(struct xvip_pipeline *pipe,
{
struct media_entity_graph graph;
struct media_entity *entity = &start->video.entity;
- struct media_device *mdev = entity->parent;
+ struct media_device *mdev = entity->graph_obj.mdev;
unsigned int num_inputs = 0;
unsigned int num_outputs = 0;
+ int ret;
mutex_lock(&mdev->graph_mutex);
/* Walk the graph to locate the video nodes. */
+ ret = media_entity_graph_walk_init(&graph, entity->graph_obj.mdev);
+ if (ret) {
+ mutex_unlock(&mdev->graph_mutex);
+ return ret;
+ }
+
media_entity_graph_walk_start(&graph, entity);
while ((entity = media_entity_graph_walk_next(&graph))) {
struct xvip_dma *dma;
- if (entity->type != MEDIA_ENT_T_DEVNODE_V4L)
+ if (entity->function != MEDIA_ENT_F_IO_V4L)
continue;
dma = to_xvip_dma(media_entity_to_video_device(entity));
@@ -208,6 +213,8 @@ static int xvip_pipeline_validate(struct xvip_pipeline *pipe,
mutex_unlock(&mdev->graph_mutex);
+ media_entity_graph_walk_cleanup(&graph);
+
/* We need exactly one output and zero or one input. */
if (num_outputs != 1 || num_inputs > 1)
return -EPIPE;
@@ -303,27 +310,25 @@ static void xvip_dma_complete(void *param)
buf->buf.field = V4L2_FIELD_NONE;
buf->buf.sequence = dma->sequence++;
- v4l2_get_timestamp(&buf->buf.timestamp);
+ buf->buf.vb2_buf.timestamp = ktime_get_ns();
vb2_set_plane_payload(&buf->buf.vb2_buf, 0, dma->format.sizeimage);
vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE);
}
static int
-xvip_dma_queue_setup(struct vb2_queue *vq, const void *parg,
+xvip_dma_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct xvip_dma *dma = vb2_get_drv_priv(vq);
+ alloc_ctxs[0] = dma->alloc_ctx;
/* Make sure the image size is large enough. */
- if (fmt && fmt->fmt.pix.sizeimage < dma->format.sizeimage)
- return -EINVAL;
+ if (*nplanes)
+ return sizes[0] < dma->format.sizeimage ? -EINVAL : 0;
*nplanes = 1;
-
- sizes[0] = fmt ? fmt->fmt.pix.sizeimage : dma->format.sizeimage;
- alloc_ctxs[0] = dma->alloc_ctx;
+ sizes[0] = dma->format.sizeimage;
return 0;
}
@@ -679,7 +684,7 @@ int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma,
dma->pad.flags = type == V4L2_BUF_TYPE_VIDEO_CAPTURE
? MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&dma->video.entity, 1, &dma->pad, 0);
+ ret = media_entity_pads_init(&dma->video.entity, 1, &dma->pad);
if (ret < 0)
goto error;
diff --git a/drivers/media/platform/xilinx/xilinx-tpg.c b/drivers/media/platform/xilinx/xilinx-tpg.c
index b5f7d5ecb7f6..2ec1f6c4b274 100644
--- a/drivers/media/platform/xilinx/xilinx-tpg.c
+++ b/drivers/media/platform/xilinx/xilinx-tpg.c
@@ -731,6 +731,7 @@ static int xtpg_parse_of(struct xtpg_device *xtpg)
format = xvip_of_get_format(port);
if (IS_ERR(format)) {
dev_err(dev, "invalid format in DT");
+ of_node_put(port);
return PTR_ERR(format);
}
@@ -739,6 +740,7 @@ static int xtpg_parse_of(struct xtpg_device *xtpg)
xtpg->vip_format = format;
} else if (xtpg->vip_format != format) {
dev_err(dev, "in/out format mismatch in DT");
+ of_node_put(port);
return -EINVAL;
}
@@ -836,7 +838,7 @@ static int xtpg_probe(struct platform_device *pdev)
subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
subdev->entity.ops = &xtpg_media_ops;
- ret = media_entity_init(&subdev->entity, xtpg->npads, xtpg->pads, 0);
+ ret = media_entity_pads_init(&subdev->entity, xtpg->npads, xtpg->pads);
if (ret < 0)
goto error;
diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c
index 7b7cb9c28d2c..e795a4501e8b 100644
--- a/drivers/media/platform/xilinx/xilinx-vipp.c
+++ b/drivers/media/platform/xilinx/xilinx-vipp.c
@@ -156,7 +156,7 @@ static int xvip_graph_build_one(struct xvip_composite_device *xdev,
local->name, local_pad->index,
remote->name, remote_pad->index);
- ret = media_entity_create_link(local, local_pad->index,
+ ret = media_create_pad_link(local, local_pad->index,
remote, remote_pad->index,
link_flags);
if (ret < 0) {
@@ -270,7 +270,7 @@ static int xvip_graph_build_dma(struct xvip_composite_device *xdev)
source->name, source_pad->index,
sink->name, sink_pad->index);
- ret = media_entity_create_link(source, source_pad->index,
+ ret = media_create_pad_link(source, source_pad->index,
sink, sink_pad->index,
link_flags);
if (ret < 0) {
@@ -311,7 +311,7 @@ static int xvip_graph_notify_complete(struct v4l2_async_notifier *notifier)
if (ret < 0)
dev_err(xdev->dev, "failed to register subdev nodes\n");
- return ret;
+ return media_device_register(&xdev->media_dev);
}
static int xvip_graph_notify_bound(struct v4l2_async_notifier *notifier,
@@ -476,8 +476,10 @@ static int xvip_graph_dma_init(struct xvip_composite_device *xdev)
for_each_child_of_node(ports, port) {
ret = xvip_graph_dma_init_one(xdev, port);
- if (ret < 0)
+ if (ret < 0) {
+ of_node_put(port);
return ret;
+ }
}
return 0;
@@ -571,6 +573,7 @@ static void xvip_composite_v4l2_cleanup(struct xvip_composite_device *xdev)
{
v4l2_device_unregister(&xdev->v4l2_dev);
media_device_unregister(&xdev->media_dev);
+ media_device_cleanup(&xdev->media_dev);
}
static int xvip_composite_v4l2_init(struct xvip_composite_device *xdev)
@@ -582,19 +585,14 @@ static int xvip_composite_v4l2_init(struct xvip_composite_device *xdev)
sizeof(xdev->media_dev.model));
xdev->media_dev.hw_revision = 0;
- ret = media_device_register(&xdev->media_dev);
- if (ret < 0) {
- dev_err(xdev->dev, "media device registration failed (%d)\n",
- ret);
- return ret;
- }
+ media_device_init(&xdev->media_dev);
xdev->v4l2_dev.mdev = &xdev->media_dev;
ret = v4l2_device_register(xdev->dev, &xdev->v4l2_dev);
if (ret < 0) {
dev_err(xdev->dev, "V4L2 device registration failed (%d)\n",
ret);
- media_device_unregister(&xdev->media_dev);
+ media_device_cleanup(&xdev->media_dev);
return ret;
}
diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c
index 5236035f0f2a..70fd8e80198a 100644
--- a/drivers/media/radio/radio-maxiradio.c
+++ b/drivers/media/radio/radio-maxiradio.c
@@ -42,7 +42,7 @@
#include <linux/videodev2.h>
#include <linux/io.h>
#include <linux/slab.h>
-#include <media/tea575x.h>
+#include <media/drv-intf/tea575x.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-fh.h>
@@ -108,7 +108,7 @@ static void maxiradio_tea575x_set_direction(struct snd_tea575x *tea, bool output
{
}
-static struct snd_tea575x_ops maxiradio_tea_ops = {
+static const struct snd_tea575x_ops maxiradio_tea_ops = {
.set_pins = maxiradio_tea575x_set_pins,
.get_pins = maxiradio_tea575x_get_pins,
.set_direction = maxiradio_tea575x_set_direction,
diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c
index b8d61cbc18cb..dc81d422b394 100644
--- a/drivers/media/radio/radio-sf16fmr2.c
+++ b/drivers/media/radio/radio-sf16fmr2.c
@@ -14,7 +14,7 @@
#include <linux/io.h> /* outb, outb_p */
#include <linux/isa.h>
#include <linux/pnp.h>
-#include <media/tea575x.h>
+#include <media/drv-intf/tea575x.h>
MODULE_AUTHOR("Ondrej Zary");
MODULE_DESCRIPTION("MediaForte SF16-FMR2 and SF16-FMD2 FM radio card driver");
@@ -82,7 +82,7 @@ static void fmr2_tea575x_set_direction(struct snd_tea575x *tea, bool output)
{
}
-static struct snd_tea575x_ops fmr2_tea_ops = {
+static const struct snd_tea575x_ops fmr2_tea_ops = {
.set_pins = fmr2_tea575x_set_pins,
.get_pins = fmr2_tea575x_get_pins,
.set_direction = fmr2_tea575x_set_direction,
diff --git a/drivers/media/radio/radio-shark.c b/drivers/media/radio/radio-shark.c
index 050b3bb96fec..85667a95f003 100644
--- a/drivers/media/radio/radio-shark.c
+++ b/drivers/media/radio/radio-shark.c
@@ -33,7 +33,7 @@
#include <linux/usb.h>
#include <linux/workqueue.h>
#include <media/v4l2-device.h>
-#include <media/tea575x.h>
+#include <media/drv-intf/tea575x.h>
#if defined(CONFIG_LEDS_CLASS) || \
(defined(CONFIG_LEDS_CLASS_MODULE) && defined(CONFIG_RADIO_SHARK_MODULE))
@@ -150,7 +150,7 @@ static u32 shark_read_val(struct snd_tea575x *tea)
return val;
}
-static struct snd_tea575x_ops shark_tea_ops = {
+static const struct snd_tea575x_ops shark_tea_ops = {
.write_val = shark_write_val,
.read_val = shark_read_val,
};
diff --git a/drivers/media/radio/radio-shark2.c b/drivers/media/radio/radio-shark2.c
index 8654e0dc5c95..0e65a85d52c6 100644
--- a/drivers/media/radio/radio-shark2.c
+++ b/drivers/media/radio/radio-shark2.c
@@ -137,7 +137,7 @@ static int shark_read_reg(struct radio_tea5777 *tea, u32 *reg_ret)
return 0;
}
-static struct radio_tea5777_ops shark_tea_ops = {
+static const struct radio_tea5777_ops shark_tea_ops = {
.write_reg = shark_write_reg,
.read_reg = shark_read_reg,
};
diff --git a/drivers/media/radio/radio-si476x.c b/drivers/media/radio/radio-si476x.c
index 9cbb8cdf0ac0..859f0c08ee05 100644
--- a/drivers/media/radio/radio-si476x.c
+++ b/drivers/media/radio/radio-si476x.c
@@ -31,7 +31,7 @@
#include <media/v4l2-event.h>
#include <media/v4l2-device.h>
-#include <media/si476x.h>
+#include <media/drv-intf/si476x.h>
#include <linux/mfd/si476x-core.h>
#define FM_FREQ_RANGE_LOW 64000000
diff --git a/drivers/media/radio/radio-tea5777.h b/drivers/media/radio/radio-tea5777.h
index 4ea43a90a151..4bd942526a1b 100644
--- a/drivers/media/radio/radio-tea5777.h
+++ b/drivers/media/radio/radio-tea5777.h
@@ -76,7 +76,7 @@ struct radio_tea5777 {
u32 read_reg;
u64 write_reg;
struct mutex mutex;
- struct radio_tea5777_ops *ops;
+ const struct radio_tea5777_ops *ops;
void *private_data;
u8 card[32];
u8 bus_info[32];
diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c
index 04baafe5e901..a82eb9678d6c 100644
--- a/drivers/media/radio/radio-timb.c
+++ b/drivers/media/radio/radio-timb.c
@@ -26,7 +26,7 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/module.h>
-#include <media/timb_radio.h>
+#include <linux/platform_data/media/timb_radio.h>
#define DRIVER_NAME "timb-radio"
diff --git a/drivers/media/radio/si4713/radio-usb-si4713.c b/drivers/media/radio/si4713/radio-usb-si4713.c
index a77319dcba05..5146be2a1a50 100644
--- a/drivers/media/radio/si4713/radio-usb-si4713.c
+++ b/drivers/media/radio/si4713/radio-usb-si4713.c
@@ -31,7 +31,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
-#include <media/si4713.h>
+#include <linux/platform_data/media/si4713.h>
#include "si4713.h"
diff --git a/drivers/media/radio/si4713/si4713.h b/drivers/media/radio/si4713/si4713.h
index 8a376e142188..29d0e1f104d2 100644
--- a/drivers/media/radio/si4713/si4713.h
+++ b/drivers/media/radio/si4713/si4713.h
@@ -20,7 +20,7 @@
#include <linux/gpio/consumer.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-ctrls.h>
-#include <media/si4713.h>
+#include <linux/platform_data/media/si4713.h>
#define SI4713_PRODUCT_NUMBER 0x0D
diff --git a/drivers/media/radio/tea575x.c b/drivers/media/radio/tea575x.c
index 43d1ea53cb66..3e08475af579 100644
--- a/drivers/media/radio/tea575x.c
+++ b/drivers/media/radio/tea575x.c
@@ -31,7 +31,7 @@
#include <media/v4l2-fh.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
-#include <media/tea575x.h>
+#include <media/drv-intf/tea575x.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips");
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index b6e13116c6f5..bd4d68500085 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -101,7 +101,8 @@ config IR_SHARP_DECODER
---help---
Enable this option if you have an infrared remote control which
- uses the Sharp protocol, and you need software decoding support.
+ uses the Sharp protocol (Sharp, Denon), and you need software
+ decoding support.
config IR_MCE_KBD_DECODER
tristate "Enable IR raw decoder for the MCE keyboard/mouse protocol"
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
index 7dbc9ca6d885..5b63b1f15cb1 100644
--- a/drivers/media/rc/gpio-ir-recv.c
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -21,7 +21,7 @@
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <media/rc-core.h>
-#include <media/gpio-ir-recv.h>
+#include <linux/platform_data/media/gpio-ir-recv.h>
#define GPIO_IR_DRIVER_NAME "gpio-rc-recv"
#define GPIO_IR_DEVICE_NAME "gpio_ir_recv"
@@ -30,6 +30,7 @@ struct gpio_rc_dev {
struct rc_dev *rcdev;
int gpio_nr;
bool active_low;
+ struct timer_list flush_timer;
};
#ifdef CONFIG_OF
@@ -93,12 +94,26 @@ static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id)
if (rc < 0)
goto err_get_value;
+ mod_timer(&gpio_dev->flush_timer,
+ jiffies + nsecs_to_jiffies(gpio_dev->rcdev->timeout));
+
ir_raw_event_handle(gpio_dev->rcdev);
err_get_value:
return IRQ_HANDLED;
}
+static void flush_timer(unsigned long arg)
+{
+ struct gpio_rc_dev *gpio_dev = (struct gpio_rc_dev *)arg;
+ DEFINE_IR_RAW_EVENT(ev);
+
+ ev.timeout = true;
+ ev.duration = gpio_dev->rcdev->timeout;
+ ir_raw_event_store(gpio_dev->rcdev, &ev);
+ ir_raw_event_handle(gpio_dev->rcdev);
+}
+
static int gpio_ir_recv_probe(struct platform_device *pdev)
{
struct gpio_rc_dev *gpio_dev;
@@ -144,6 +159,9 @@ static int gpio_ir_recv_probe(struct platform_device *pdev)
rcdev->input_id.version = 0x0100;
rcdev->dev.parent = &pdev->dev;
rcdev->driver_name = GPIO_IR_DRIVER_NAME;
+ rcdev->min_timeout = 0;
+ rcdev->timeout = IR_DEFAULT_TIMEOUT;
+ rcdev->max_timeout = 10 * IR_DEFAULT_TIMEOUT;
if (pdata->allowed_protos)
rcdev->allowed_protocols = pdata->allowed_protos;
else
@@ -154,6 +172,9 @@ static int gpio_ir_recv_probe(struct platform_device *pdev)
gpio_dev->gpio_nr = pdata->gpio_nr;
gpio_dev->active_low = pdata->active_low;
+ setup_timer(&gpio_dev->flush_timer, flush_timer,
+ (unsigned long)gpio_dev);
+
rc = gpio_request(pdata->gpio_nr, "gpio-ir-recv");
if (rc < 0)
goto err_gpio_request;
@@ -196,6 +217,7 @@ static int gpio_ir_recv_remove(struct platform_device *pdev)
struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
free_irq(gpio_to_irq(gpio_dev->gpio_nr), gpio_dev);
+ del_timer_sync(&gpio_dev->flush_timer);
rc_unregister_device(gpio_dev->rcdev);
gpio_free(gpio_dev->gpio_nr);
kfree(gpio_dev);
diff --git a/drivers/media/rc/ir-jvc-decoder.c b/drivers/media/rc/ir-jvc-decoder.c
index 30bcf188d377..182402f7b4d1 100644
--- a/drivers/media/rc/ir-jvc-decoder.c
+++ b/drivers/media/rc/ir-jvc-decoder.c
@@ -47,9 +47,6 @@ static int ir_jvc_decode(struct rc_dev *dev, struct ir_raw_event ev)
{
struct jvc_dec *data = &dev->raw->jvc;
- if (!(dev->enabled_protocols & RC_BIT_JVC))
- return 0;
-
if (!is_timing_event(ev)) {
if (ev.reset)
data->state = STATE_INACTIVE;
diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c
index a32659fcd266..5effc65d2947 100644
--- a/drivers/media/rc/ir-lirc-codec.c
+++ b/drivers/media/rc/ir-lirc-codec.c
@@ -415,6 +415,7 @@ static int ir_lirc_unregister(struct rc_dev *dev)
lirc_unregister_driver(lirc->drv->minor);
lirc_buffer_free(lirc->drv->rbuf);
+ kfree(lirc->drv->rbuf);
kfree(lirc->drv);
return 0;
diff --git a/drivers/media/rc/ir-mce_kbd-decoder.c b/drivers/media/rc/ir-mce_kbd-decoder.c
index 9f3c9b59f30c..d80986251ee0 100644
--- a/drivers/media/rc/ir-mce_kbd-decoder.c
+++ b/drivers/media/rc/ir-mce_kbd-decoder.c
@@ -216,9 +216,6 @@ static int ir_mce_kbd_decode(struct rc_dev *dev, struct ir_raw_event ev)
u32 scancode;
unsigned long delay;
- if (!(dev->enabled_protocols & RC_BIT_MCE_KBD))
- return 0;
-
if (!is_timing_event(ev)) {
if (ev.reset)
data->state = STATE_INACTIVE;
diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c
index 7b81fec0820f..bea0d1eedee0 100644
--- a/drivers/media/rc/ir-nec-decoder.c
+++ b/drivers/media/rc/ir-nec-decoder.c
@@ -52,9 +52,6 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev)
u8 address, not_address, command, not_command;
bool send_32bits = false;
- if (!(dev->enabled_protocols & RC_BIT_NEC))
- return 0;
-
if (!is_timing_event(ev)) {
if (ev.reset)
data->state = STATE_INACTIVE;
diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c
index 84fa6e9b59a1..6ffe776abf6b 100644
--- a/drivers/media/rc/ir-rc5-decoder.c
+++ b/drivers/media/rc/ir-rc5-decoder.c
@@ -53,9 +53,6 @@ static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev)
u32 scancode;
enum rc_type protocol;
- if (!(dev->enabled_protocols & (RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ)))
- return 0;
-
if (!is_timing_event(ev)) {
if (ev.reset)
data->state = STATE_INACTIVE;
diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c
index d16bc67af732..e0e2edefa651 100644
--- a/drivers/media/rc/ir-rc6-decoder.c
+++ b/drivers/media/rc/ir-rc6-decoder.c
@@ -90,11 +90,6 @@ static int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev)
u8 toggle;
enum rc_type protocol;
- if (!(dev->enabled_protocols &
- (RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 |
- RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE)))
- return 0;
-
if (!is_timing_event(ev)) {
if (ev.reset)
data->state = STATE_INACTIVE;
diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c
index b1e19a26208d..4e1711a40466 100644
--- a/drivers/media/rc/ir-rx51.c
+++ b/drivers/media/rc/ir-rx51.c
@@ -31,7 +31,7 @@
#include <media/lirc.h>
#include <media/lirc_dev.h>
-#include <media/ir-rx51.h>
+#include <linux/platform_data/media/ir-rx51.h>
#define LIRC_RX51_DRIVER_FEATURES (LIRC_CAN_SET_SEND_DUTY_CYCLE | \
LIRC_CAN_SET_SEND_CARRIER | \
diff --git a/drivers/media/rc/ir-sanyo-decoder.c b/drivers/media/rc/ir-sanyo-decoder.c
index ad1dc6ae21fc..7331e5e7c497 100644
--- a/drivers/media/rc/ir-sanyo-decoder.c
+++ b/drivers/media/rc/ir-sanyo-decoder.c
@@ -58,9 +58,6 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev)
u32 scancode;
u8 address, command, not_command;
- if (!(dev->enabled_protocols & RC_BIT_SANYO))
- return 0;
-
if (!is_timing_event(ev)) {
if (ev.reset) {
IR_dprintk(1, "SANYO event reset received. reset to state 0\n");
diff --git a/drivers/media/rc/ir-sharp-decoder.c b/drivers/media/rc/ir-sharp-decoder.c
index b7acdbae8159..317677f06f2c 100644
--- a/drivers/media/rc/ir-sharp-decoder.c
+++ b/drivers/media/rc/ir-sharp-decoder.c
@@ -48,9 +48,6 @@ static int ir_sharp_decode(struct rc_dev *dev, struct ir_raw_event ev)
struct sharp_dec *data = &dev->raw->sharp;
u32 msg, echo, address, command, scancode;
- if (!(dev->enabled_protocols & RC_BIT_SHARP))
- return 0;
-
if (!is_timing_event(ev)) {
if (ev.reset)
data->state = STATE_INACTIVE;
@@ -118,7 +115,9 @@ static int ir_sharp_decode(struct rc_dev *dev, struct ir_raw_event ev)
if (data->count == SHARP_NBITS) {
/* exp,chk bits should be 1,0 */
- if ((data->bits & 0x3) != 0x2)
+ if ((data->bits & 0x3) != 0x2 &&
+ /* DENON variant, both chk bits 0 */
+ (data->bits & 0x3) != 0x0)
break;
data->state = STATE_ECHO_SPACE;
} else {
diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c
index 58ef06f35175..baa972c76e0e 100644
--- a/drivers/media/rc/ir-sony-decoder.c
+++ b/drivers/media/rc/ir-sony-decoder.c
@@ -46,10 +46,6 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev)
u32 scancode;
u8 device, subdevice, function;
- if (!(dev->enabled_protocols &
- (RC_BIT_SONY12 | RC_BIT_SONY15 | RC_BIT_SONY20)))
- return 0;
-
if (!is_timing_event(ev)) {
if (ev.reset)
data->state = STATE_INACTIVE;
diff --git a/drivers/media/rc/ir-xmp-decoder.c b/drivers/media/rc/ir-xmp-decoder.c
index 1017d4816e8d..18596190bbb8 100644
--- a/drivers/media/rc/ir-xmp-decoder.c
+++ b/drivers/media/rc/ir-xmp-decoder.c
@@ -43,9 +43,6 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev)
{
struct xmp_dec *data = &dev->raw->xmp;
- if (!(dev->enabled_protocols & RC_BIT_XMP))
- return 0;
-
if (!is_timing_event(ev)) {
if (ev.reset)
data->state = STATE_INACTIVE;
diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
index 85af7a869167..18adf580f502 100644
--- a/drivers/media/rc/nuvoton-cir.c
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -39,6 +39,18 @@
#include "nuvoton-cir.h"
+static const struct nvt_chip nvt_chips[] = {
+ { "w83667hg", NVT_W83667HG },
+ { "NCT6775F", NVT_6775F },
+ { "NCT6776F", NVT_6776F },
+ { "NCT6779D", NVT_6779D },
+};
+
+static inline bool is_w83667hg(struct nvt_dev *nvt)
+{
+ return nvt->chip_ver == NVT_W83667HG;
+}
+
/* write val to config reg */
static inline void nvt_cr_write(struct nvt_dev *nvt, u8 val, u8 reg)
{
@@ -224,74 +236,60 @@ static void cir_wake_dump_regs(struct nvt_dev *nvt)
pr_cont("\n");
}
+static inline const char *nvt_find_chip(struct nvt_dev *nvt, int id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(nvt_chips); i++)
+ if ((id & SIO_ID_MASK) == nvt_chips[i].chip_ver) {
+ nvt->chip_ver = nvt_chips[i].chip_ver;
+ return nvt_chips[i].name;
+ }
+
+ return NULL;
+}
+
+
/* detect hardware features */
-static int nvt_hw_detect(struct nvt_dev *nvt)
+static void nvt_hw_detect(struct nvt_dev *nvt)
{
- unsigned long flags;
- u8 chip_major, chip_minor;
- char chip_id[12];
- bool chip_unknown = false;
+ const char *chip_name;
+ int chip_id;
nvt_efm_enable(nvt);
/* Check if we're wired for the alternate EFER setup */
- chip_major = nvt_cr_read(nvt, CR_CHIP_ID_HI);
- if (chip_major == 0xff) {
+ nvt->chip_major = nvt_cr_read(nvt, CR_CHIP_ID_HI);
+ if (nvt->chip_major == 0xff) {
nvt->cr_efir = CR_EFIR2;
nvt->cr_efdr = CR_EFDR2;
nvt_efm_enable(nvt);
- chip_major = nvt_cr_read(nvt, CR_CHIP_ID_HI);
+ nvt->chip_major = nvt_cr_read(nvt, CR_CHIP_ID_HI);
}
- chip_minor = nvt_cr_read(nvt, CR_CHIP_ID_LO);
-
- /* these are the known working chip revisions... */
- switch (chip_major) {
- case CHIP_ID_HIGH_667:
- strcpy(chip_id, "w83667hg\0");
- if (chip_minor != CHIP_ID_LOW_667)
- chip_unknown = true;
- break;
- case CHIP_ID_HIGH_677B:
- strcpy(chip_id, "w83677hg\0");
- if (chip_minor != CHIP_ID_LOW_677B2 &&
- chip_minor != CHIP_ID_LOW_677B3)
- chip_unknown = true;
- break;
- case CHIP_ID_HIGH_677C:
- strcpy(chip_id, "w83677hg-c\0");
- if (chip_minor != CHIP_ID_LOW_677C)
- chip_unknown = true;
- break;
- default:
- strcpy(chip_id, "w836x7hg\0");
- chip_unknown = true;
- break;
- }
+ nvt->chip_minor = nvt_cr_read(nvt, CR_CHIP_ID_LO);
+
+ chip_id = nvt->chip_major << 8 | nvt->chip_minor;
+ chip_name = nvt_find_chip(nvt, chip_id);
/* warn, but still let the driver load, if we don't know this chip */
- if (chip_unknown)
- nvt_pr(KERN_WARNING, "%s: unknown chip, id: 0x%02x 0x%02x, "
- "it may not work...", chip_id, chip_major, chip_minor);
+ if (!chip_name)
+ dev_warn(&nvt->pdev->dev,
+ "unknown chip, id: 0x%02x 0x%02x, it may not work...",
+ nvt->chip_major, nvt->chip_minor);
else
- nvt_dbg("%s: chip id: 0x%02x 0x%02x",
- chip_id, chip_major, chip_minor);
+ dev_info(&nvt->pdev->dev,
+ "found %s or compatible: chip id: 0x%02x 0x%02x",
+ chip_name, nvt->chip_major, nvt->chip_minor);
nvt_efm_disable(nvt);
-
- spin_lock_irqsave(&nvt->nvt_lock, flags);
- nvt->chip_major = chip_major;
- nvt->chip_minor = chip_minor;
- spin_unlock_irqrestore(&nvt->nvt_lock, flags);
-
- return 0;
}
static void nvt_cir_ldev_init(struct nvt_dev *nvt)
{
u8 val, psreg, psmask, psval;
- if (nvt->chip_major == CHIP_ID_HIGH_667) {
+ if (is_w83667hg(nvt)) {
psreg = CR_MULTIFUNC_PIN_SEL;
psmask = MULTIFUNC_PIN_SEL_MASK;
psval = MULTIFUNC_ENABLE_CIR | MULTIFUNC_ENABLE_CIRWB;
@@ -485,8 +483,9 @@ static u32 nvt_rx_carrier_detect(struct nvt_dev *nvt)
duration *= SAMPLE_PERIOD;
if (!count || !duration) {
- nvt_pr(KERN_NOTICE, "Unable to determine carrier! (c:%u, d:%u)",
- count, duration);
+ dev_notice(&nvt->pdev->dev,
+ "Unable to determine carrier! (c:%u, d:%u)",
+ count, duration);
return 0;
}
@@ -661,7 +660,7 @@ static void nvt_process_rx_ir_data(struct nvt_dev *nvt)
static void nvt_handle_rx_fifo_overrun(struct nvt_dev *nvt)
{
- nvt_pr(KERN_WARNING, "RX FIFO overrun detected, flushing data!");
+ dev_warn(&nvt->pdev->dev, "RX FIFO overrun detected, flushing data!");
nvt->pkts = 0;
nvt_clear_cir_fifo(nvt);
@@ -719,7 +718,7 @@ static void nvt_get_rx_ir_data(struct nvt_dev *nvt)
static void nvt_cir_log_irqs(u8 status, u8 iren)
{
- nvt_pr(KERN_INFO, "IRQ 0x%02x (IREN 0x%02x) :%s%s%s%s%s%s%s%s%s",
+ nvt_dbg("IRQ 0x%02x (IREN 0x%02x) :%s%s%s%s%s%s%s%s%s",
status, iren,
status & CIR_IRSTS_RDR ? " RDR" : "",
status & CIR_IRSTS_RTR ? " RTR" : "",
@@ -779,7 +778,7 @@ static irqreturn_t nvt_cir_isr(int irq, void *data)
if (!status) {
nvt_dbg_verbose("%s exiting, IRSTS 0x0", __func__);
nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS);
- return IRQ_RETVAL(IRQ_NONE);
+ return IRQ_NONE;
}
/* ack/clear all irq flags we've got */
@@ -790,11 +789,10 @@ static irqreturn_t nvt_cir_isr(int irq, void *data)
iren = nvt_cir_reg_read(nvt, CIR_IREN);
if (!iren) {
nvt_dbg_verbose("%s exiting, CIR not enabled", __func__);
- return IRQ_RETVAL(IRQ_NONE);
+ return IRQ_NONE;
}
- if (debug)
- nvt_cir_log_irqs(status, iren);
+ nvt_cir_log_irqs(status, iren);
if (status & CIR_IRSTS_RTR) {
/* FIXME: add code for study/learn mode */
@@ -853,7 +851,7 @@ static irqreturn_t nvt_cir_isr(int irq, void *data)
}
nvt_dbg_verbose("%s done", __func__);
- return IRQ_RETVAL(IRQ_HANDLED);
+ return IRQ_HANDLED;
}
/* Interrupt service routine for CIR Wake */
@@ -867,7 +865,7 @@ static irqreturn_t nvt_cir_wake_isr(int irq, void *data)
status = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRSTS);
if (!status)
- return IRQ_RETVAL(IRQ_NONE);
+ return IRQ_NONE;
if (status & CIR_WAKE_IRSTS_IR_PENDING)
nvt_clear_cir_wake_fifo(nvt);
@@ -879,7 +877,7 @@ static irqreturn_t nvt_cir_wake_isr(int irq, void *data)
iren = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN);
if (!iren) {
nvt_dbg_wake("%s exiting, wake not enabled", __func__);
- return IRQ_RETVAL(IRQ_HANDLED);
+ return IRQ_HANDLED;
}
if ((status & CIR_WAKE_IRSTS_PE) &&
@@ -896,7 +894,7 @@ static irqreturn_t nvt_cir_wake_isr(int irq, void *data)
}
nvt_dbg_wake("%s done", __func__);
- return IRQ_RETVAL(IRQ_HANDLED);
+ return IRQ_HANDLED;
}
static void nvt_enable_cir(struct nvt_dev *nvt)
@@ -974,7 +972,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
struct rc_dev *rdev;
int ret = -ENOMEM;
- nvt = kzalloc(sizeof(struct nvt_dev), GFP_KERNEL);
+ nvt = devm_kzalloc(&pdev->dev, sizeof(struct nvt_dev), GFP_KERNEL);
if (!nvt)
return ret;
@@ -1026,9 +1024,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
init_waitqueue_head(&nvt->tx.queue);
- ret = nvt_hw_detect(nvt);
- if (ret)
- goto exit_free_dev_rdev;
+ nvt_hw_detect(nvt);
/* Initialize CIR & CIR Wake Logical Devices */
nvt_efm_enable(nvt);
@@ -1074,25 +1070,26 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
ret = -EBUSY;
/* now claim resources */
- if (!request_region(nvt->cir_addr,
+ if (!devm_request_region(&pdev->dev, nvt->cir_addr,
CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
goto exit_unregister_device;
- if (request_irq(nvt->cir_irq, nvt_cir_isr, IRQF_SHARED,
- NVT_DRIVER_NAME, (void *)nvt))
- goto exit_release_cir_addr;
+ if (devm_request_irq(&pdev->dev, nvt->cir_irq, nvt_cir_isr,
+ IRQF_SHARED, NVT_DRIVER_NAME, (void *)nvt))
+ goto exit_unregister_device;
- if (!request_region(nvt->cir_wake_addr,
+ if (!devm_request_region(&pdev->dev, nvt->cir_wake_addr,
CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
- goto exit_free_irq;
+ goto exit_unregister_device;
- if (request_irq(nvt->cir_wake_irq, nvt_cir_wake_isr, IRQF_SHARED,
- NVT_DRIVER_NAME, (void *)nvt))
- goto exit_release_cir_wake_addr;
+ if (devm_request_irq(&pdev->dev, nvt->cir_wake_irq,
+ nvt_cir_wake_isr, IRQF_SHARED,
+ NVT_DRIVER_NAME, (void *)nvt))
+ goto exit_unregister_device;
device_init_wakeup(&pdev->dev, true);
- nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n");
+ dev_notice(&pdev->dev, "driver has been successfully loaded\n");
if (debug) {
cir_dump_regs(nvt);
cir_wake_dump_regs(nvt);
@@ -1100,18 +1097,11 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
return 0;
-exit_release_cir_wake_addr:
- release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH);
-exit_free_irq:
- free_irq(nvt->cir_irq, nvt);
-exit_release_cir_addr:
- release_region(nvt->cir_addr, CIR_IOREG_LENGTH);
exit_unregister_device:
rc_unregister_device(rdev);
rdev = NULL;
exit_free_dev_rdev:
rc_free_device(rdev);
- kfree(nvt);
return ret;
}
@@ -1129,15 +1119,7 @@ static void nvt_remove(struct pnp_dev *pdev)
nvt_enable_wake(nvt);
spin_unlock_irqrestore(&nvt->nvt_lock, flags);
- /* free resources */
- free_irq(nvt->cir_irq, nvt);
- free_irq(nvt->cir_wake_irq, nvt);
- release_region(nvt->cir_addr, CIR_IOREG_LENGTH);
- release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH);
-
rc_unregister_device(nvt->rdev);
-
- kfree(nvt);
}
static int nvt_suspend(struct pnp_dev *pdev, pm_message_t state)
diff --git a/drivers/media/rc/nuvoton-cir.h b/drivers/media/rc/nuvoton-cir.h
index e1cf23c3875b..0ad15d34e9c9 100644
--- a/drivers/media/rc/nuvoton-cir.h
+++ b/drivers/media/rc/nuvoton-cir.h
@@ -35,9 +35,6 @@
static int debug;
-#define nvt_pr(level, text, ...) \
- printk(level KBUILD_MODNAME ": " text, ## __VA_ARGS__)
-
#define nvt_dbg(text, ...) \
if (debug) \
printk(KERN_DEBUG \
@@ -64,6 +61,21 @@ static int debug;
#define TX_BUF_LEN 256
#define RX_BUF_LEN 32
+#define SIO_ID_MASK 0xfff0
+
+enum nvt_chip_ver {
+ NVT_UNKNOWN = 0,
+ NVT_W83667HG = 0xa510,
+ NVT_6775F = 0xb470,
+ NVT_6776F = 0xc330,
+ NVT_6779D = 0xc560
+};
+
+struct nvt_chip {
+ const char *name;
+ enum nvt_chip_ver chip_ver;
+};
+
struct nvt_dev {
struct pnp_dev *pdev;
struct rc_dev *rdev;
@@ -93,6 +105,7 @@ struct nvt_dev {
int cir_irq;
int cir_wake_irq;
+ enum nvt_chip_ver chip_ver;
/* hardware id */
u8 chip_major;
u8 chip_minor;
@@ -326,15 +339,6 @@ struct nvt_dev {
#define EFER_EFM_ENABLE 0x87
#define EFER_EFM_DISABLE 0xaa
-/* Chip IDs found in CR_CHIP_ID_{HI,LO} */
-#define CHIP_ID_HIGH_667 0xa5
-#define CHIP_ID_HIGH_677B 0xb4
-#define CHIP_ID_HIGH_677C 0xc3
-#define CHIP_ID_LOW_667 0x13
-#define CHIP_ID_LOW_677B2 0x72
-#define CHIP_ID_LOW_677B3 0x73
-#define CHIP_ID_LOW_677C 0x33
-
/* Config regs we need to care about */
#define CR_SOFTWARE_RESET 0x02
#define CR_LOGICAL_DEV_SEL 0x07
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
index b68d4f762734..7359f3d03b64 100644
--- a/drivers/media/rc/rc-core-priv.h
+++ b/drivers/media/rc/rc-core-priv.h
@@ -167,75 +167,4 @@ void ir_raw_init(void);
* loads the compiled decoders for their usage with IR raw events
*/
-/* from ir-nec-decoder.c */
-#ifdef CONFIG_IR_NEC_DECODER_MODULE
-#define load_nec_decode() request_module_nowait("ir-nec-decoder")
-#else
-static inline void load_nec_decode(void) { }
-#endif
-
-/* from ir-rc5-decoder.c */
-#ifdef CONFIG_IR_RC5_DECODER_MODULE
-#define load_rc5_decode() request_module_nowait("ir-rc5-decoder")
-#else
-static inline void load_rc5_decode(void) { }
-#endif
-
-/* from ir-rc6-decoder.c */
-#ifdef CONFIG_IR_RC6_DECODER_MODULE
-#define load_rc6_decode() request_module_nowait("ir-rc6-decoder")
-#else
-static inline void load_rc6_decode(void) { }
-#endif
-
-/* from ir-jvc-decoder.c */
-#ifdef CONFIG_IR_JVC_DECODER_MODULE
-#define load_jvc_decode() request_module_nowait("ir-jvc-decoder")
-#else
-static inline void load_jvc_decode(void) { }
-#endif
-
-/* from ir-sony-decoder.c */
-#ifdef CONFIG_IR_SONY_DECODER_MODULE
-#define load_sony_decode() request_module_nowait("ir-sony-decoder")
-#else
-static inline void load_sony_decode(void) { }
-#endif
-
-/* from ir-sanyo-decoder.c */
-#ifdef CONFIG_IR_SANYO_DECODER_MODULE
-#define load_sanyo_decode() request_module_nowait("ir-sanyo-decoder")
-#else
-static inline void load_sanyo_decode(void) { }
-#endif
-
-/* from ir-sharp-decoder.c */
-#ifdef CONFIG_IR_SHARP_DECODER_MODULE
-#define load_sharp_decode() request_module_nowait("ir-sharp-decoder")
-#else
-static inline void load_sharp_decode(void) { }
-#endif
-
-/* from ir-mce_kbd-decoder.c */
-#ifdef CONFIG_IR_MCE_KBD_DECODER_MODULE
-#define load_mce_kbd_decode() request_module_nowait("ir-mce_kbd-decoder")
-#else
-static inline void load_mce_kbd_decode(void) { }
-#endif
-
-/* from ir-lirc-codec.c */
-#ifdef CONFIG_IR_LIRC_CODEC_MODULE
-#define load_lirc_codec() request_module_nowait("ir-lirc-codec")
-#else
-static inline void load_lirc_codec(void) { }
-#endif
-
-/* from ir-xmp-decoder.c */
-#ifdef CONFIG_IR_XMP_DECODER_MODULE
-#define load_xmp_decode() request_module_nowait("ir-xmp-decoder")
-#else
-static inline void load_xmp_decode(void) { }
-#endif
-
-
#endif /* _RC_CORE_PRIV */
diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c
index ad260520a9d4..c69807fe2fef 100644
--- a/drivers/media/rc/rc-ir-raw.c
+++ b/drivers/media/rc/rc-ir-raw.c
@@ -59,7 +59,9 @@ static int ir_raw_event_thread(void *data)
mutex_lock(&ir_raw_handler_lock);
list_for_each_entry(handler, &ir_raw_handler_list, list)
- handler->decode(raw->dev, ev);
+ if (raw->dev->enabled_protocols & handler->protocols ||
+ !handler->protocols)
+ handler->decode(raw->dev, ev);
raw->prev_ev = ev;
mutex_unlock(&ir_raw_handler_lock);
}
@@ -246,6 +248,14 @@ static int change_protocol(struct rc_dev *dev, u64 *rc_type)
return 0;
}
+static void ir_raw_disable_protocols(struct rc_dev *dev, u64 protocols)
+{
+ mutex_lock(&dev->lock);
+ dev->enabled_protocols &= ~protocols;
+ dev->enabled_wakeup_protocols &= ~protocols;
+ mutex_unlock(&dev->lock);
+}
+
/*
* Used to (un)register raw event clients
*/
@@ -337,33 +347,16 @@ EXPORT_SYMBOL(ir_raw_handler_register);
void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler)
{
struct ir_raw_event_ctrl *raw;
+ u64 protocols = ir_raw_handler->protocols;
mutex_lock(&ir_raw_handler_lock);
list_del(&ir_raw_handler->list);
- if (ir_raw_handler->raw_unregister)
- list_for_each_entry(raw, &ir_raw_client_list, list)
+ list_for_each_entry(raw, &ir_raw_client_list, list) {
+ ir_raw_disable_protocols(raw->dev, protocols);
+ if (ir_raw_handler->raw_unregister)
ir_raw_handler->raw_unregister(raw->dev);
- available_protocols &= ~ir_raw_handler->protocols;
+ }
+ available_protocols &= ~protocols;
mutex_unlock(&ir_raw_handler_lock);
}
EXPORT_SYMBOL(ir_raw_handler_unregister);
-
-void ir_raw_init(void)
-{
- /* Load the decoder modules */
-
- load_nec_decode();
- load_rc5_decode();
- load_rc6_decode();
- load_jvc_decode();
- load_sony_decode();
- load_sanyo_decode();
- load_sharp_decode();
- load_mce_kbd_decode();
- load_lirc_codec();
- load_xmp_decode();
-
- /* If needed, we may later add some init code. In this case,
- it is needed to change the CONFIG_MODULE test at rc-core.h
- */
-}
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index 3f0f71adabb4..1042fa331a07 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -61,7 +61,7 @@ struct rc_map *rc_map_get(const char *name)
struct rc_map_list *map;
map = seek_rc_map(name);
-#ifdef MODULE
+#ifdef CONFIG_MODULES
if (!map) {
int rc = request_module("%s", name);
if (rc < 0) {
@@ -777,30 +777,31 @@ static struct class rc_class = {
* used by the sysfs protocols file. Note that the order
* of the entries is relevant.
*/
-static struct {
+static const struct {
u64 type;
- char *name;
+ const char *name;
+ const char *module_name;
} proto_names[] = {
- { RC_BIT_NONE, "none" },
- { RC_BIT_OTHER, "other" },
- { RC_BIT_UNKNOWN, "unknown" },
+ { RC_BIT_NONE, "none", NULL },
+ { RC_BIT_OTHER, "other", NULL },
+ { RC_BIT_UNKNOWN, "unknown", NULL },
{ RC_BIT_RC5 |
- RC_BIT_RC5X, "rc-5" },
- { RC_BIT_NEC, "nec" },
+ RC_BIT_RC5X, "rc-5", "ir-rc5-decoder" },
+ { RC_BIT_NEC, "nec", "ir-nec-decoder" },
{ RC_BIT_RC6_0 |
RC_BIT_RC6_6A_20 |
RC_BIT_RC6_6A_24 |
RC_BIT_RC6_6A_32 |
- RC_BIT_RC6_MCE, "rc-6" },
- { RC_BIT_JVC, "jvc" },
+ RC_BIT_RC6_MCE, "rc-6", "ir-rc6-decoder" },
+ { RC_BIT_JVC, "jvc", "ir-jvc-decoder" },
{ RC_BIT_SONY12 |
RC_BIT_SONY15 |
- RC_BIT_SONY20, "sony" },
- { RC_BIT_RC5_SZ, "rc-5-sz" },
- { RC_BIT_SANYO, "sanyo" },
- { RC_BIT_SHARP, "sharp" },
- { RC_BIT_MCE_KBD, "mce_kbd" },
- { RC_BIT_XMP, "xmp" },
+ RC_BIT_SONY20, "sony", "ir-sony-decoder" },
+ { RC_BIT_RC5_SZ, "rc-5-sz", "ir-rc5-decoder" },
+ { RC_BIT_SANYO, "sanyo", "ir-sanyo-decoder" },
+ { RC_BIT_SHARP, "sharp", "ir-sharp-decoder" },
+ { RC_BIT_MCE_KBD, "mce_kbd", "ir-mce_kbd-decoder" },
+ { RC_BIT_XMP, "xmp", "ir-xmp-decoder" },
};
/**
@@ -979,6 +980,48 @@ static int parse_protocol_change(u64 *protocols, const char *buf)
return count;
}
+static void ir_raw_load_modules(u64 *protocols)
+
+{
+ u64 available;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(proto_names); i++) {
+ if (proto_names[i].type == RC_BIT_NONE ||
+ proto_names[i].type & (RC_BIT_OTHER | RC_BIT_UNKNOWN))
+ continue;
+
+ available = ir_raw_get_allowed_protocols();
+ if (!(*protocols & proto_names[i].type & ~available))
+ continue;
+
+ if (!proto_names[i].module_name) {
+ pr_err("Can't enable IR protocol %s\n",
+ proto_names[i].name);
+ *protocols &= ~proto_names[i].type;
+ continue;
+ }
+
+ ret = request_module("%s", proto_names[i].module_name);
+ if (ret < 0) {
+ pr_err("Couldn't load IR protocol module %s\n",
+ proto_names[i].module_name);
+ *protocols &= ~proto_names[i].type;
+ continue;
+ }
+ msleep(20);
+ available = ir_raw_get_allowed_protocols();
+ if (!(*protocols & proto_names[i].type & ~available))
+ continue;
+
+ pr_err("Loaded IR protocol module %s, \
+ but protocol %s still not available\n",
+ proto_names[i].module_name,
+ proto_names[i].name);
+ *protocols &= ~proto_names[i].type;
+ }
+}
+
/**
* store_protocols() - changes the current/wakeup IR protocol(s)
* @device: the device descriptor
@@ -1045,6 +1088,9 @@ static ssize_t store_protocols(struct device *device,
goto out;
}
+ if (dev->driver_type == RC_DRIVER_IR_RAW)
+ ir_raw_load_modules(&new_protocols);
+
if (new_protocols != old_protocols) {
*current_protocols = new_protocols;
IR_dprintk(1, "Protocols changed to 0x%llx\n",
@@ -1420,17 +1466,13 @@ int rc_register_device(struct rc_dev *dev)
dev->input_dev->rep[REP_PERIOD] = 125;
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
- printk(KERN_INFO "%s: %s as %s\n",
- dev_name(&dev->dev),
- dev->input_name ? dev->input_name : "Unspecified device",
- path ? path : "N/A");
+ dev_info(&dev->dev, "%s as %s\n",
+ dev->input_name ?: "Unspecified device", path ?: "N/A");
kfree(path);
if (dev->driver_type == RC_DRIVER_IR_RAW) {
- /* Load raw decoders, if they aren't already */
if (!raw_init) {
- IR_dprintk(1, "Loading raw decoders\n");
- ir_raw_init();
+ request_module_nowait("ir-lirc-codec");
raw_init = true;
}
/* calls ir_register_device so unlock mutex here*/
diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c
index 37d040158dff..1fa0c9d1c508 100644
--- a/drivers/media/rc/st_rc.c
+++ b/drivers/media/rc/st_rc.c
@@ -16,6 +16,7 @@
#include <linux/reset.h>
#include <media/rc-core.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/pm_wakeirq.h>
struct st_rc_device {
struct device *dev;
@@ -190,6 +191,9 @@ static void st_rc_hardware_init(struct st_rc_device *dev)
static int st_rc_remove(struct platform_device *pdev)
{
struct st_rc_device *rc_dev = platform_get_drvdata(pdev);
+
+ dev_pm_clear_wake_irq(&pdev->dev);
+ device_init_wakeup(&pdev->dev, false);
clk_disable_unprepare(rc_dev->sys_clock);
rc_unregister_device(rc_dev->rdev);
return 0;
@@ -298,22 +302,22 @@ static int st_rc_probe(struct platform_device *pdev)
rdev->map_name = RC_MAP_LIRC;
rdev->input_name = "ST Remote Control Receiver";
- /* enable wake via this device */
- device_set_wakeup_capable(dev, true);
- device_set_wakeup_enable(dev, true);
-
ret = rc_register_device(rdev);
if (ret < 0)
goto clkerr;
rc_dev->rdev = rdev;
if (devm_request_irq(dev, rc_dev->irq, st_rc_rx_interrupt,
- IRQF_NO_SUSPEND, IR_ST_NAME, rc_dev) < 0) {
+ 0, IR_ST_NAME, rc_dev) < 0) {
dev_err(dev, "IRQ %d register failed\n", rc_dev->irq);
ret = -EINVAL;
goto rcerr;
}
+ /* enable wake via this device */
+ device_init_wakeup(dev, true);
+ dev_pm_set_wake_irq(dev, rc_dev->irq);
+
/**
* for LIRC_MODE_MODE2 or LIRC_MODE_PULSE or LIRC_MODE_RAW
* lircd expects a long space first before a signal train to sync.
diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c
index 5a17cb88ff27..815243c65bc3 100644
--- a/drivers/media/rc/streamzap.c
+++ b/drivers/media/rc/streamzap.c
@@ -34,6 +34,7 @@
#include <linux/device.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/ktime.h>
#include <linux/usb.h>
#include <linux/usb/input.h>
#include <media/rc-core.h>
@@ -96,8 +97,8 @@ struct streamzap_ir {
/* sum of signal lengths received since signal start */
unsigned long sum;
/* start time of signal; necessary for gap tracking */
- struct timeval signal_last;
- struct timeval signal_start;
+ ktime_t signal_last;
+ ktime_t signal_start;
bool timeout_enabled;
char name[128];
@@ -136,20 +137,18 @@ static void sz_push_full_pulse(struct streamzap_ir *sz,
DEFINE_IR_RAW_EVENT(rawir);
if (sz->idle) {
- long deltv;
+ int delta;
sz->signal_last = sz->signal_start;
- do_gettimeofday(&sz->signal_start);
+ sz->signal_start = ktime_get_real();
- deltv = sz->signal_start.tv_sec - sz->signal_last.tv_sec;
+ delta = ktime_us_delta(sz->signal_start, sz->signal_last);
rawir.pulse = false;
- if (deltv > 15) {
+ if (delta > (15 * USEC_PER_SEC)) {
/* really long time */
rawir.duration = IR_MAX_DURATION;
} else {
- rawir.duration = (int)(deltv * 1000000 +
- sz->signal_start.tv_usec -
- sz->signal_last.tv_usec);
+ rawir.duration = delta;
rawir.duration -= sz->sum;
rawir.duration = US_TO_NS(rawir.duration);
rawir.duration = (rawir.duration > IR_MAX_DURATION) ?
@@ -428,7 +427,7 @@ static int streamzap_probe(struct usb_interface *intf,
sz->max_timeout = US_TO_NS(SZ_TIMEOUT * SZ_RESOLUTION);
#endif
- do_gettimeofday(&sz->signal_start);
+ sz->signal_start = ktime_get_real();
/* Complete final initialisations */
usb_fill_int_urb(sz->urb_in, usbdev, pipe, sz->buf_in,
diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
index 7830aef3db45..40f77685cc4a 100644
--- a/drivers/media/rc/sunxi-cir.c
+++ b/drivers/media/rc/sunxi-cir.c
@@ -153,6 +153,8 @@ static int sunxi_ir_probe(struct platform_device *pdev)
if (!ir)
return -ENOMEM;
+ spin_lock_init(&ir->ir_lock);
+
if (of_device_is_compatible(dn, "allwinner,sun5i-a13-ir"))
ir->fifo_size = 64;
else
diff --git a/drivers/media/tuners/max2165.c b/drivers/media/tuners/max2165.c
index 95ed46f2cd26..353b178becf6 100644
--- a/drivers/media/tuners/max2165.c
+++ b/drivers/media/tuners/max2165.c
@@ -385,7 +385,7 @@ static const struct dvb_tuner_ops max2165_tuner_ops = {
.info = {
.name = "Maxim MAX2165",
.frequency_min = 470000000,
- .frequency_max = 780000000,
+ .frequency_max = 862000000,
.frequency_step = 50000,
},
diff --git a/drivers/media/tuners/mt2063.c b/drivers/media/tuners/mt2063.c
index 9e9c5eb4cb66..6457ac91ef09 100644
--- a/drivers/media/tuners/mt2063.c
+++ b/drivers/media/tuners/mt2063.c
@@ -225,7 +225,6 @@ struct mt2063_state {
const struct mt2063_config *config;
struct dvb_tuner_ops ops;
struct dvb_frontend *frontend;
- struct tuner_state status;
u32 frequency;
u32 srate;
diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c
index ce157edd45fa..0e1ca2b00e61 100644
--- a/drivers/media/tuners/si2157.c
+++ b/drivers/media/tuners/si2157.c
@@ -168,6 +168,7 @@ static int si2157_init(struct dvb_frontend *fe)
len = fw->data[fw->size - remaining];
if (len > SI2157_ARGLEN) {
dev_err(&client->dev, "Bad firmware length\n");
+ ret = -EINVAL;
goto err_release_firmware;
}
memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len);
diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c
index fcbb49757614..0d4ac5947f3a 100644
--- a/drivers/media/usb/airspy/airspy.c
+++ b/drivers/media/usb/airspy/airspy.c
@@ -134,7 +134,7 @@ struct airspy {
int urbs_submitted;
/* USB control message buffer */
- #define BUF_SIZE 24
+ #define BUF_SIZE 128
u8 buf[BUF_SIZE];
/* Current configuration */
@@ -316,7 +316,7 @@ static void airspy_urb_complete(struct urb *urb)
len = airspy_convert_stream(s, ptr, urb->transfer_buffer,
urb->actual_length);
vb2_set_plane_payload(&fbuf->vb.vb2_buf, 0, len);
- v4l2_get_timestamp(&fbuf->vb.timestamp);
+ fbuf->vb.vb2_buf.timestamp = ktime_get_ns();
fbuf->vb.sequence = s->sequence++;
vb2_buffer_done(&fbuf->vb.vb2_buf, VB2_BUF_STATE_DONE);
}
@@ -488,7 +488,7 @@ static void airspy_disconnect(struct usb_interface *intf)
/* Videobuf2 operations */
static int airspy_queue_setup(struct vb2_queue *vq,
- const void *parg, unsigned int *nbuffers,
+ unsigned int *nbuffers,
unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[])
{
struct airspy *s = vb2_get_drv_priv(vq);
diff --git a/drivers/media/usb/as102/as102_fw.c b/drivers/media/usb/as102/as102_fw.c
index 07d08c49f4d4..5a28ce3a1d49 100644
--- a/drivers/media/usb/as102/as102_fw.c
+++ b/drivers/media/usb/as102/as102_fw.c
@@ -198,6 +198,7 @@ int as102_fw_upload(struct as10x_bus_adapter_t *bus_adap)
pr_info("%s: firmware: %s loaded with success\n",
DRIVER_NAME, fw1);
release_firmware(firmware);
+ firmware = NULL;
/* wait for boot to complete */
mdelay(100);
diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c
index 6b469e8c4c6e..ca861aea68a5 100644
--- a/drivers/media/usb/au0828/au0828-cards.c
+++ b/drivers/media/usb/au0828/au0828-cards.c
@@ -228,6 +228,10 @@ void au0828_card_analog_fe_setup(struct au0828_dev *dev)
"au8522", 0x8e >> 1, NULL);
if (sd == NULL)
pr_err("analog subdev registration failed\n");
+#ifdef CONFIG_MEDIA_CONTROLLER
+ if (sd)
+ dev->decoder = &sd->entity;
+#endif
}
/* Setup tuners */
diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c
index 0934024fb89d..9e29e70a78d7 100644
--- a/drivers/media/usb/au0828/au0828-core.c
+++ b/drivers/media/usb/au0828/au0828-core.c
@@ -27,6 +27,9 @@
#include <media/v4l2-common.h>
#include <linux/mutex.h>
+/* Due to enum tuner_pad_index */
+#include <media/tuner.h>
+
/*
* 1 = General debug messages
* 2 = USB handling
@@ -127,8 +130,23 @@ static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value,
return status;
}
+static void au0828_unregister_media_device(struct au0828_dev *dev)
+{
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+ if (dev->media_dev) {
+ media_device_unregister(dev->media_dev);
+ media_device_cleanup(dev->media_dev);
+ kfree(dev->media_dev);
+ dev->media_dev = NULL;
+ }
+#endif
+}
+
static void au0828_usb_release(struct au0828_dev *dev)
{
+ au0828_unregister_media_device(dev);
+
/* I2C */
au0828_i2c_unregister(dev);
@@ -136,6 +154,20 @@ static void au0828_usb_release(struct au0828_dev *dev)
}
#ifdef CONFIG_VIDEO_AU0828_V4L2
+
+static void au0828_usb_v4l2_media_release(struct au0828_dev *dev)
+{
+#ifdef CONFIG_MEDIA_CONTROLLER
+ int i;
+
+ for (i = 0; i < AU0828_MAX_INPUT; i++) {
+ if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED)
+ return;
+ media_device_unregister_entity(&dev->input_ent[i]);
+ }
+#endif
+}
+
static void au0828_usb_v4l2_release(struct v4l2_device *v4l2_dev)
{
struct au0828_dev *dev =
@@ -143,6 +175,7 @@ static void au0828_usb_v4l2_release(struct v4l2_device *v4l2_dev)
v4l2_ctrl_handler_free(&dev->v4l2_ctrl_hdl);
v4l2_device_unregister(&dev->v4l2_dev);
+ au0828_usb_v4l2_media_release(dev);
au0828_usb_release(dev);
}
#endif
@@ -174,12 +207,123 @@ static void au0828_usb_disconnect(struct usb_interface *interface)
au0828_analog_unregister(dev);
v4l2_device_disconnect(&dev->v4l2_dev);
v4l2_device_put(&dev->v4l2_dev);
+ /*
+ * No need to call au0828_usb_release() if V4L2 is enabled,
+ * as this is already called via au0828_usb_v4l2_release()
+ */
return;
}
#endif
au0828_usb_release(dev);
}
+static int au0828_media_device_init(struct au0828_dev *dev,
+ struct usb_device *udev)
+{
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_device *mdev;
+
+ mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
+ if (!mdev)
+ return -ENOMEM;
+
+ mdev->dev = &udev->dev;
+
+ if (!dev->board.name)
+ strlcpy(mdev->model, "unknown au0828", sizeof(mdev->model));
+ else
+ strlcpy(mdev->model, dev->board.name, sizeof(mdev->model));
+ if (udev->serial)
+ strlcpy(mdev->serial, udev->serial, sizeof(mdev->serial));
+ strcpy(mdev->bus_info, udev->devpath);
+ mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
+ mdev->driver_version = LINUX_VERSION_CODE;
+
+ media_device_init(mdev);
+
+ dev->media_dev = mdev;
+#endif
+ return 0;
+}
+
+
+static int au0828_create_media_graph(struct au0828_dev *dev)
+{
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_device *mdev = dev->media_dev;
+ struct media_entity *entity;
+ struct media_entity *tuner = NULL, *decoder = NULL;
+ int i, ret;
+
+ if (!mdev)
+ return 0;
+
+ media_device_for_each_entity(entity, mdev) {
+ switch (entity->function) {
+ case MEDIA_ENT_F_TUNER:
+ tuner = entity;
+ break;
+ case MEDIA_ENT_F_ATV_DECODER:
+ decoder = entity;
+ break;
+ }
+ }
+
+ /* Analog setup, using tuner as a link */
+
+ /* Something bad happened! */
+ if (!decoder)
+ return -EINVAL;
+
+ if (tuner) {
+ ret = media_create_pad_link(tuner, TUNER_PAD_IF_OUTPUT,
+ decoder, 0,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+ }
+ ret = media_create_pad_link(decoder, 1, &dev->vdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+ ret = media_create_pad_link(decoder, 2, &dev->vbi_dev.entity, 0,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < AU0828_MAX_INPUT; i++) {
+ struct media_entity *ent = &dev->input_ent[i];
+
+ if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED)
+ break;
+
+ switch (AUVI_INPUT(i).type) {
+ case AU0828_VMUX_CABLE:
+ case AU0828_VMUX_TELEVISION:
+ case AU0828_VMUX_DVB:
+ if (!tuner)
+ break;
+
+ ret = media_create_pad_link(ent, 0, tuner,
+ TUNER_PAD_RF_INPUT,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+ break;
+ case AU0828_VMUX_COMPOSITE:
+ case AU0828_VMUX_SVIDEO:
+ default: /* AU0828_VMUX_DEBUG */
+ /* FIXME: fix the decoder PAD */
+ ret = media_create_pad_link(ent, 0, decoder, 0, 0);
+ if (ret)
+ return ret;
+ break;
+ }
+ }
+#endif
+ return 0;
+}
+
static int au0828_usb_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
@@ -224,11 +368,23 @@ static int au0828_usb_probe(struct usb_interface *interface,
dev->boardnr = id->driver_info;
dev->board = au0828_boards[dev->boardnr];
+ /* Initialize the media controller */
+ retval = au0828_media_device_init(dev, usbdev);
+ if (retval) {
+ pr_err("%s() au0828_media_device_init failed\n",
+ __func__);
+ mutex_unlock(&dev->lock);
+ kfree(dev);
+ return retval;
+ }
#ifdef CONFIG_VIDEO_AU0828_V4L2
dev->v4l2_dev.release = au0828_usb_v4l2_release;
/* Create the v4l2_device */
+#ifdef CONFIG_MEDIA_CONTROLLER
+ dev->v4l2_dev.mdev = dev->media_dev;
+#endif
retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
if (retval) {
pr_err("%s() v4l2_device_register failed\n",
@@ -287,6 +443,21 @@ static int au0828_usb_probe(struct usb_interface *interface,
mutex_unlock(&dev->lock);
+ retval = au0828_create_media_graph(dev);
+ if (retval) {
+ pr_err("%s() au0282_dev_register failed to create graph\n",
+ __func__);
+ goto done;
+ }
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+ retval = media_device_register(dev->media_dev);
+#endif
+
+done:
+ if (retval < 0)
+ au0828_usb_disconnect(interface);
+
return retval;
}
diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c
index c267d76f5b3c..94363a3ba400 100644
--- a/drivers/media/usb/au0828/au0828-dvb.c
+++ b/drivers/media/usb/au0828/au0828-dvb.c
@@ -415,6 +415,11 @@ static int dvb_register(struct au0828_dev *dev)
result);
goto fail_adapter;
}
+
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ dvb->adapter.mdev = dev->media_dev;
+#endif
+
dvb->adapter.priv = dev;
/* register frontend */
@@ -480,8 +485,15 @@ static int dvb_register(struct au0828_dev *dev)
dvb->start_count = 0;
dvb->stop_count = 0;
+
+ result = dvb_create_media_graph(&dvb->adapter, false);
+ if (result < 0)
+ goto fail_create_graph;
+
return 0;
+fail_create_graph:
+ dvb_net_release(&dvb->net);
fail_fe_conn:
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
fail_fe_mem:
diff --git a/drivers/media/usb/au0828/au0828-vbi.c b/drivers/media/usb/au0828/au0828-vbi.c
index 130c8b49bf7f..b4efc103ae57 100644
--- a/drivers/media/usb/au0828/au0828-vbi.c
+++ b/drivers/media/usb/au0828/au0828-vbi.c
@@ -30,23 +30,17 @@
/* ------------------------------------------------------------------ */
-static int vbi_queue_setup(struct vb2_queue *vq, const void *parg,
+static int vbi_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct au0828_dev *dev = vb2_get_drv_priv(vq);
- unsigned long img_size = dev->vbi_width * dev->vbi_height * 2;
- unsigned long size;
-
- size = fmt ? (fmt->fmt.vbi.samples_per_line *
- (fmt->fmt.vbi.count[0] + fmt->fmt.vbi.count[1])) : img_size;
- if (size < img_size)
- return -EINVAL;
+ unsigned long size = dev->vbi_width * dev->vbi_height * 2;
+ if (*nplanes)
+ return sizes[0] < size ? -EINVAL : 0;
*nplanes = 1;
sizes[0] = size;
-
return 0;
}
diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c
index 45c622e234f7..8c54fd21022e 100644
--- a/drivers/media/usb/au0828/au0828-video.c
+++ b/drivers/media/usb/au0828/au0828-video.c
@@ -314,7 +314,7 @@ static inline void buffer_filled(struct au0828_dev *dev,
vb->sequence = dev->vbi_frame_count++;
vb->field = V4L2_FIELD_INTERLACED;
- v4l2_get_timestamp(&vb->timestamp);
+ vb->vb2_buf.timestamp = ktime_get_ns();
vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE);
}
@@ -638,22 +638,78 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb)
return rc;
}
-static int queue_setup(struct vb2_queue *vq, const void *parg,
+static int au0828_enable_analog_tuner(struct au0828_dev *dev)
+{
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_device *mdev = dev->media_dev;
+ struct media_entity *source;
+ struct media_link *link, *found_link = NULL;
+ int ret, active_links = 0;
+
+ if (!mdev || !dev->decoder)
+ return 0;
+
+ /*
+ * This will find the tuner that is connected into the decoder.
+ * Technically, this is not 100% correct, as the device may be
+ * using an analog input instead of the tuner. However, as we can't
+ * do DVB streaming while the DMA engine is being used for V4L2,
+ * this should be enough for the actual needs.
+ */
+ list_for_each_entry(link, &dev->decoder->links, list) {
+ if (link->sink->entity == dev->decoder) {
+ found_link = link;
+ if (link->flags & MEDIA_LNK_FL_ENABLED)
+ active_links++;
+ break;
+ }
+ }
+
+ if (active_links == 1 || !found_link)
+ return 0;
+
+ source = found_link->source->entity;
+ list_for_each_entry(link, &source->links, list) {
+ struct media_entity *sink;
+ int flags = 0;
+
+ sink = link->sink->entity;
+
+ if (sink == dev->decoder)
+ flags = MEDIA_LNK_FL_ENABLED;
+
+ ret = media_entity_setup_link(link, flags);
+ if (ret) {
+ pr_err(
+ "Couldn't change link %s->%s to %s. Error %d\n",
+ source->name, sink->name,
+ flags ? "enabled" : "disabled",
+ ret);
+ return ret;
+ } else
+ au0828_isocdbg(
+ "link %s->%s was %s\n",
+ source->name, sink->name,
+ flags ? "ENABLED" : "disabled");
+ }
+#endif
+ return 0;
+}
+
+static int queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct au0828_dev *dev = vb2_get_drv_priv(vq);
- unsigned long img_size = dev->height * dev->bytesperline;
- unsigned long size;
-
- size = fmt ? fmt->fmt.pix.sizeimage : img_size;
- if (size < img_size)
- return -EINVAL;
+ unsigned long size = dev->height * dev->bytesperline;
+ if (*nplanes)
+ return sizes[0] < size ? -EINVAL : 0;
*nplanes = 1;
sizes[0] = size;
+ au0828_enable_analog_tuner(dev);
+
return 0;
}
@@ -1739,6 +1795,69 @@ static int au0828_vb2_setup(struct au0828_dev *dev)
return 0;
}
+static void au0828_analog_create_entities(struct au0828_dev *dev)
+{
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ static const char * const inames[] = {
+ [AU0828_VMUX_COMPOSITE] = "Composite",
+ [AU0828_VMUX_SVIDEO] = "S-Video",
+ [AU0828_VMUX_CABLE] = "Cable TV",
+ [AU0828_VMUX_TELEVISION] = "Television",
+ [AU0828_VMUX_DVB] = "DVB",
+ [AU0828_VMUX_DEBUG] = "tv debug"
+ };
+ int ret, i;
+
+ /* Initialize Video and VBI pads */
+ dev->video_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&dev->vdev.entity, 1, &dev->video_pad);
+ if (ret < 0)
+ pr_err("failed to initialize video media entity!\n");
+
+ dev->vbi_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&dev->vbi_dev.entity, 1, &dev->vbi_pad);
+ if (ret < 0)
+ pr_err("failed to initialize vbi media entity!\n");
+
+ /* Create entities for each input connector */
+ for (i = 0; i < AU0828_MAX_INPUT; i++) {
+ struct media_entity *ent = &dev->input_ent[i];
+
+ if (AUVI_INPUT(i).type == AU0828_VMUX_UNDEFINED)
+ break;
+
+ ent->name = inames[AUVI_INPUT(i).type];
+ ent->flags = MEDIA_ENT_FL_CONNECTOR;
+ dev->input_pad[i].flags = MEDIA_PAD_FL_SOURCE;
+
+ switch (AUVI_INPUT(i).type) {
+ case AU0828_VMUX_COMPOSITE:
+ ent->function = MEDIA_ENT_F_CONN_COMPOSITE;
+ break;
+ case AU0828_VMUX_SVIDEO:
+ ent->function = MEDIA_ENT_F_CONN_SVIDEO;
+ break;
+ case AU0828_VMUX_CABLE:
+ case AU0828_VMUX_TELEVISION:
+ case AU0828_VMUX_DVB:
+ ent->function = MEDIA_ENT_F_CONN_RF;
+ break;
+ default: /* AU0828_VMUX_DEBUG */
+ ent->function = MEDIA_ENT_F_CONN_TEST;
+ break;
+ }
+
+ ret = media_entity_pads_init(ent, 1, &dev->input_pad[i]);
+ if (ret < 0)
+ pr_err("failed to initialize input pad[%d]!\n", i);
+
+ ret = media_device_register_entity(dev->media_dev, ent);
+ if (ret < 0)
+ pr_err("failed to register input entity %d!\n", i);
+ }
+#endif
+}
+
/**************************************************************************/
int au0828_analog_register(struct au0828_dev *dev,
@@ -1827,6 +1946,9 @@ int au0828_analog_register(struct au0828_dev *dev,
dev->vbi_dev.queue->lock = &dev->vb_vbi_queue_lock;
strcpy(dev->vbi_dev.name, "au0828a vbi");
+ /* Init entities at the Media Controller */
+ au0828_analog_create_entities(dev);
+
/* initialize videobuf2 stuff */
retval = au0828_vb2_setup(dev);
if (retval != 0) {
diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h
index 60b59391ea2a..8276072bc55a 100644
--- a/drivers/media/usb/au0828/au0828.h
+++ b/drivers/media/usb/au0828/au0828.h
@@ -33,6 +33,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fh.h>
+#include <media/media-device.h>
/* DVB */
#include "demux.h"
@@ -93,7 +94,6 @@ struct au0828_board {
unsigned char has_ir_i2c:1;
unsigned char has_analog:1;
struct au0828_input input[AU0828_MAX_INPUT];
-
};
struct au0828_dvb {
@@ -276,6 +276,14 @@ struct au0828_dev {
/* Preallocated transfer digital transfer buffers */
char *dig_transfer_buffer[URB_COUNT];
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_device *media_dev;
+ struct media_pad video_pad, vbi_pad;
+ struct media_entity *decoder;
+ struct media_entity input_ent[AU0828_MAX_INPUT];
+ struct media_pad input_pad[AU0828_MAX_INPUT];
+#endif
};
diff --git a/drivers/media/usb/cpia2/cpia2_usb.c b/drivers/media/usb/cpia2/cpia2_usb.c
index 351a78a84c3d..c1aa1ab2ece9 100644
--- a/drivers/media/usb/cpia2/cpia2_usb.c
+++ b/drivers/media/usb/cpia2/cpia2_usb.c
@@ -890,8 +890,7 @@ static void cpia2_usb_disconnect(struct usb_interface *intf)
DBG("Wakeup waiting processes\n");
cam->curbuff->status = FRAME_READY;
cam->curbuff->length = 0;
- if (waitqueue_active(&cam->wq_stream))
- wake_up_interruptible(&cam->wq_stream);
+ wake_up_interruptible(&cam->wq_stream);
}
DBG("Releasing interface\n");
diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c
index 47a98a2014a5..48643b94e694 100644
--- a/drivers/media/usb/cx231xx/cx231xx-417.c
+++ b/drivers/media/usb/cx231xx/cx231xx-417.c
@@ -37,7 +37,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
-#include <media/cx2341x.h>
+#include <media/drv-intf/cx2341x.h>
#include <media/tuner.h>
#define CX231xx_FIRM_IMAGE_SIZE 376836
@@ -1492,6 +1492,27 @@ static struct videobuf_queue_ops cx231xx_qops = {
/* ------------------------------------------------------------------ */
+static int vidioc_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cc)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ bool is_50hz = dev->encodernorm.id & V4L2_STD_625_50;
+
+ if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ cc->bounds.left = 0;
+ cc->bounds.top = 0;
+ cc->bounds.width = dev->ts1.width;
+ cc->bounds.height = dev->ts1.height;
+ cc->defrect = cc->bounds;
+ cc->pixelaspect.numerator = is_50hz ? 54 : 11;
+ cc->pixelaspect.denominator = is_50hz ? 59 : 10;
+
+ return 0;
+}
+
static int vidioc_g_std(struct file *file, void *fh0, v4l2_std_id *norm)
{
struct cx231xx_fh *fh = file->private_data;
@@ -1834,6 +1855,7 @@ static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
.vidioc_g_input = cx231xx_g_input,
.vidioc_s_input = cx231xx_s_input,
.vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_cropcap = vidioc_cropcap,
.vidioc_querycap = cx231xx_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
@@ -1901,7 +1923,7 @@ static int cx231xx_s_audio_sampling_freq(struct cx2341x_handler *cxhdl, u32 idx)
return 0;
}
-static struct cx2341x_handler_ops cx231xx_ops = {
+static const struct cx2341x_handler_ops cx231xx_ops = {
/* needed for the video clock freq */
.s_audio_sampling_freq = cx231xx_s_audio_sampling_freq,
/* needed for setting up the video resolution */
diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index 4a117a58c39a..620b83d03f75 100644
--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
+++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
@@ -30,7 +30,7 @@
#include <media/tveeprom.h>
#include <media/v4l2-common.h>
-#include <media/cx25840.h>
+#include <media/drv-intf/cx25840.h>
#include "dvb-usb-ids.h"
#include "xc5000.h"
#include "tda18271.h"
@@ -352,7 +352,7 @@ struct cx231xx_board cx231xx_boards[] = {
.agc_analog_digital_select_gpio = 0x0c,
.gpio_pin_status_mask = 0x4001000,
.tuner_i2c_master = I2C_1_MUX_1,
- .demod_i2c_master = I2C_2,
+ .demod_i2c_master = I2C_1_MUX_1,
.has_dvb = 1,
.demod_addr = 0x0e,
.norm = V4L2_STD_NTSC,
@@ -713,7 +713,7 @@ struct cx231xx_board cx231xx_boards[] = {
.agc_analog_digital_select_gpio = 0x0c,
.gpio_pin_status_mask = 0x4001000,
.tuner_i2c_master = I2C_1_MUX_3,
- .demod_i2c_master = I2C_2,
+ .demod_i2c_master = I2C_1_MUX_3,
.has_dvb = 1,
.demod_addr = 0x0e,
.norm = V4L2_STD_PAL,
@@ -752,7 +752,7 @@ struct cx231xx_board cx231xx_boards[] = {
.agc_analog_digital_select_gpio = 0x0c,
.gpio_pin_status_mask = 0x4001000,
.tuner_i2c_master = I2C_1_MUX_3,
- .demod_i2c_master = I2C_2,
+ .demod_i2c_master = I2C_1_MUX_3,
.has_dvb = 1,
.demod_addr = 0x0e,
.norm = V4L2_STD_PAL,
@@ -791,7 +791,7 @@ struct cx231xx_board cx231xx_boards[] = {
.agc_analog_digital_select_gpio = 0x0c,
.gpio_pin_status_mask = 0x4001000,
.tuner_i2c_master = I2C_1_MUX_3,
- .demod_i2c_master = I2C_2,
+ .demod_i2c_master = I2C_1_MUX_3,
.has_dvb = 1,
.demod_addr = 0x0e,
.norm = V4L2_STD_NTSC,
@@ -1172,6 +1172,7 @@ static void cx231xx_unregister_media_device(struct cx231xx *dev)
#ifdef CONFIG_MEDIA_CONTROLLER
if (dev->media_dev) {
media_device_unregister(dev->media_dev);
+ media_device_cleanup(dev->media_dev);
kfree(dev->media_dev);
dev->media_dev = NULL;
}
@@ -1185,8 +1186,6 @@ static void cx231xx_unregister_media_device(struct cx231xx *dev)
*/
void cx231xx_release_resources(struct cx231xx *dev)
{
- cx231xx_unregister_media_device(dev);
-
cx231xx_release_analog_resources(dev);
cx231xx_remove_from_devlist(dev);
@@ -1199,22 +1198,23 @@ void cx231xx_release_resources(struct cx231xx *dev)
/* delete v4l2 device */
v4l2_device_unregister(&dev->v4l2_dev);
+ cx231xx_unregister_media_device(dev);
+
usb_put_dev(dev->udev);
/* Mark device as unused */
clear_bit(dev->devno, &cx231xx_devused);
}
-static void cx231xx_media_device_register(struct cx231xx *dev,
- struct usb_device *udev)
+static int cx231xx_media_device_init(struct cx231xx *dev,
+ struct usb_device *udev)
{
#ifdef CONFIG_MEDIA_CONTROLLER
struct media_device *mdev;
- int ret;
mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
if (!mdev)
- return;
+ return -ENOMEM;
mdev->dev = dev->dev;
strlcpy(mdev->model, dev->board.name, sizeof(mdev->model));
@@ -1224,35 +1224,30 @@ static void cx231xx_media_device_register(struct cx231xx *dev,
mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
mdev->driver_version = LINUX_VERSION_CODE;
- ret = media_device_register(mdev);
- if (ret) {
- dev_err(dev->dev,
- "Couldn't create a media device. Error: %d\n",
- ret);
- kfree(mdev);
- return;
- }
+ media_device_init(mdev);
dev->media_dev = mdev;
#endif
+ return 0;
}
-static void cx231xx_create_media_graph(struct cx231xx *dev)
+static int cx231xx_create_media_graph(struct cx231xx *dev)
{
#ifdef CONFIG_MEDIA_CONTROLLER
struct media_device *mdev = dev->media_dev;
struct media_entity *entity;
struct media_entity *tuner = NULL, *decoder = NULL;
+ int ret;
if (!mdev)
- return;
+ return 0;
media_device_for_each_entity(entity, mdev) {
- switch (entity->type) {
- case MEDIA_ENT_T_V4L2_SUBDEV_TUNER:
+ switch (entity->function) {
+ case MEDIA_ENT_F_TUNER:
tuner = entity;
break;
- case MEDIA_ENT_T_V4L2_SUBDEV_DECODER:
+ case MEDIA_ENT_F_ATV_DECODER:
decoder = entity;
break;
}
@@ -1261,16 +1256,24 @@ static void cx231xx_create_media_graph(struct cx231xx *dev)
/* Analog setup, using tuner as a link */
if (!decoder)
- return;
+ return 0;
- if (tuner)
- media_entity_create_link(tuner, 0, decoder, 0,
- MEDIA_LNK_FL_ENABLED);
- media_entity_create_link(decoder, 1, &dev->vdev.entity, 0,
- MEDIA_LNK_FL_ENABLED);
- media_entity_create_link(decoder, 2, &dev->vbi_dev.entity, 0,
- MEDIA_LNK_FL_ENABLED);
+ if (tuner) {
+ ret = media_create_pad_link(tuner, TUNER_PAD_IF_OUTPUT, decoder, 0,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret < 0)
+ return ret;
+ }
+ ret = media_create_pad_link(decoder, 1, &dev->vdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret < 0)
+ return ret;
+ ret = media_create_pad_link(decoder, 2, &dev->vbi_dev.entity, 0,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret < 0)
+ return ret;
#endif
+ return 0;
}
/*
@@ -1660,8 +1663,12 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);
- /* Register the media controller */
- cx231xx_media_device_register(dev, udev);
+ /* Initialize the media controller */
+ retval = cx231xx_media_device_init(dev, udev);
+ if (retval) {
+ dev_err(d, "cx231xx_media_device_init failed\n");
+ goto err_media_init;
+ }
/* Create v4l2 device */
#ifdef CONFIG_MEDIA_CONTROLLER
@@ -1732,9 +1739,19 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
/* load other modules required */
request_modules(dev);
- cx231xx_create_media_graph(dev);
+ retval = cx231xx_create_media_graph(dev);
+ if (retval < 0)
+ goto done;
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+ retval = media_device_register(dev->media_dev);
+#endif
+
+done:
+ if (retval < 0)
+ cx231xx_release_resources(dev);
+ return retval;
- return 0;
err_video_alt:
/* cx231xx_uninit_dev: */
cx231xx_close_extension(dev);
@@ -1746,6 +1763,8 @@ err_video_alt:
err_init:
v4l2_device_unregister(&dev->v4l2_dev);
err_v4l2:
+ cx231xx_unregister_media_device(dev);
+err_media_init:
usb_set_intfdata(interface, NULL);
err_if:
usb_put_dev(udev);
diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c
index a2fd49b6be83..f497888d94bf 100644
--- a/drivers/media/usb/cx231xx/cx231xx-core.c
+++ b/drivers/media/usb/cx231xx/cx231xx-core.c
@@ -914,6 +914,7 @@ EXPORT_SYMBOL_GPL(cx231xx_uninit_isoc);
*/
void cx231xx_uninit_bulk(struct cx231xx *dev)
{
+ struct cx231xx_dmaqueue *dma_q = &dev->video_mode.vidq;
struct urb *urb;
int i;
@@ -931,7 +932,7 @@ void cx231xx_uninit_bulk(struct cx231xx *dev)
if (dev->video_mode.bulk_ctl.transfer_buffer[i]) {
usb_free_coherent(dev->udev,
urb->transfer_buffer_length,
- dev->video_mode.isoc_ctl.
+ dev->video_mode.bulk_ctl.
transfer_buffer[i],
urb->transfer_dma);
}
@@ -943,10 +944,12 @@ void cx231xx_uninit_bulk(struct cx231xx *dev)
kfree(dev->video_mode.bulk_ctl.urb);
kfree(dev->video_mode.bulk_ctl.transfer_buffer);
+ kfree(dma_q->p_left_data);
dev->video_mode.bulk_ctl.urb = NULL;
dev->video_mode.bulk_ctl.transfer_buffer = NULL;
dev->video_mode.bulk_ctl.num_bufs = 0;
+ dma_q->p_left_data = NULL;
if (dev->mode_tv == 0)
cx231xx_capture_start(dev, 0, Raw_Video);
@@ -1196,6 +1199,16 @@ int cx231xx_init_bulk(struct cx231xx *dev, int max_packets,
sb_size, cx231xx_bulk_irq_callback, dma_q);
}
+ /* clear halt */
+ rc = usb_clear_halt(dev->udev, dev->video_mode.bulk_ctl.urb[0]->pipe);
+ if (rc < 0) {
+ dev_err(dev->dev,
+ "failed to clear USB bulk endpoint stall/halt condition (error=%i)\n",
+ rc);
+ cx231xx_uninit_bulk(dev);
+ return rc;
+ }
+
init_waitqueue_head(&dma_q->wq);
/* submit urbs and enables IRQ */
diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c
index 66ee161fc7ba..b8d5b2be9293 100644
--- a/drivers/media/usb/cx231xx/cx231xx-dvb.c
+++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c
@@ -551,10 +551,14 @@ static int register_dvb(struct cx231xx_dvb *dvb,
/* register network adapter */
dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
- dvb_create_media_graph(&dvb->adapter);
+ result = dvb_create_media_graph(&dvb->adapter, false);
+ if (result < 0)
+ goto fail_create_graph;
return 0;
+fail_create_graph:
+ dvb_net_release(&dvb->net);
fail_fe_conn:
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
fail_fe_mem:
@@ -725,7 +729,7 @@ static int dvb_init(struct cx231xx *dev)
dev->dvb->frontend = dvb_attach(lgdt3305_attach,
&hcw_lgdt3305_config,
- tuner_i2c);
+ demod_i2c);
if (dev->dvb->frontend == NULL) {
dev_err(dev->dev,
@@ -746,7 +750,7 @@ static int dvb_init(struct cx231xx *dev)
dev->dvb->frontend = dvb_attach(si2165_attach,
&hauppauge_930C_HD_1113xx_si2165_config,
- tuner_i2c
+ demod_i2c
);
if (dev->dvb->frontend == NULL) {
@@ -779,7 +783,7 @@ static int dvb_init(struct cx231xx *dev)
dev->dvb->frontend = dvb_attach(si2165_attach,
&pctv_quatro_stick_1114xx_si2165_config,
- tuner_i2c
+ demod_i2c
);
if (dev->dvb->frontend == NULL) {
@@ -835,7 +839,7 @@ static int dvb_init(struct cx231xx *dev)
dev->dvb->frontend = dvb_attach(lgdt3306a_attach,
&hauppauge_955q_lgdt3306a_config,
- tuner_i2c
+ demod_i2c
);
if (dev->dvb->frontend == NULL) {
diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c
index a08014d20a5c..15bb573b78ac 100644
--- a/drivers/media/usb/cx231xx/cx231xx-vbi.c
+++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c
@@ -32,7 +32,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
-#include <media/msp3400.h>
+#include <media/drv-intf/msp3400.h>
#include <media/tuner.h>
#include "cx231xx-vbi.h"
diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c
index d0d8f08e37c8..9b88cd8127ac 100644
--- a/drivers/media/usb/cx231xx/cx231xx-video.c
+++ b/drivers/media/usb/cx231xx/cx231xx-video.c
@@ -36,7 +36,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
-#include <media/msp3400.h>
+#include <media/drv-intf/msp3400.h>
#include <media/tuner.h>
#include "dvb_frontend.h"
@@ -106,7 +106,7 @@ static int cx231xx_enable_analog_tuner(struct cx231xx *dev)
struct media_device *mdev = dev->media_dev;
struct media_entity *entity, *decoder = NULL, *source;
struct media_link *link, *found_link = NULL;
- int i, ret, active_links = 0;
+ int ret, active_links = 0;
if (!mdev)
return 0;
@@ -119,7 +119,7 @@ static int cx231xx_enable_analog_tuner(struct cx231xx *dev)
* this should be enough for the actual needs.
*/
media_device_for_each_entity(entity, mdev) {
- if (entity->type == MEDIA_ENT_T_V4L2_SUBDEV_DECODER) {
+ if (entity->function == MEDIA_ENT_F_ATV_DECODER) {
decoder = entity;
break;
}
@@ -127,8 +127,7 @@ static int cx231xx_enable_analog_tuner(struct cx231xx *dev)
if (!decoder)
return 0;
- for (i = 0; i < decoder->num_links; i++) {
- link = &decoder->links[i];
+ list_for_each_entry(link, &decoder->links, list) {
if (link->sink->entity == decoder) {
found_link = link;
if (link->flags & MEDIA_LNK_FL_ENABLED)
@@ -141,11 +140,10 @@ static int cx231xx_enable_analog_tuner(struct cx231xx *dev)
return 0;
source = found_link->source->entity;
- for (i = 0; i < source->num_links; i++) {
+ list_for_each_entry(link, &source->links, list) {
struct media_entity *sink;
int flags = 0;
- link = &source->links[i];
sink = link->sink->entity;
if (sink == entity)
@@ -1444,6 +1442,7 @@ static int vidioc_cropcap(struct file *file, void *priv,
{
struct cx231xx_fh *fh = priv;
struct cx231xx *dev = fh->dev;
+ bool is_50hz = dev->norm & V4L2_STD_625_50;
if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
@@ -1453,8 +1452,8 @@ static int vidioc_cropcap(struct file *file, void *priv,
cc->bounds.width = dev->width;
cc->bounds.height = dev->height;
cc->defrect = cc->bounds;
- cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */
- cc->pixelaspect.denominator = 59;
+ cc->pixelaspect.numerator = is_50hz ? 54 : 11;
+ cc->pixelaspect.denominator = is_50hz ? 59 : 10;
return 0;
}
@@ -2176,7 +2175,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev)
cx231xx_vdev_init(dev, &dev->vdev, &cx231xx_video_template, "video");
#if defined(CONFIG_MEDIA_CONTROLLER)
dev->video_pad.flags = MEDIA_PAD_FL_SINK;
- ret = media_entity_init(&dev->vdev.entity, 1, &dev->video_pad, 0);
+ ret = media_entity_pads_init(&dev->vdev.entity, 1, &dev->video_pad);
if (ret < 0)
dev_err(dev->dev, "failed to initialize video media entity!\n");
#endif
@@ -2203,7 +2202,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev)
#if defined(CONFIG_MEDIA_CONTROLLER)
dev->vbi_pad.flags = MEDIA_PAD_FL_SINK;
- ret = media_entity_init(&dev->vbi_dev.entity, 1, &dev->vbi_pad, 0);
+ ret = media_entity_pads_init(&dev->vbi_dev.entity, 1, &dev->vbi_pad);
if (ret < 0)
dev_err(dev->dev, "failed to initialize vbi media entity!\n");
#endif
diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h
index 54790fbe8fdc..ec6d3f5bc36d 100644
--- a/drivers/media/usb/cx231xx/cx231xx.h
+++ b/drivers/media/usb/cx231xx/cx231xx.h
@@ -30,14 +30,14 @@
#include <linux/mutex.h>
#include <linux/usb.h>
-#include <media/cx2341x.h>
+#include <media/drv-intf/cx2341x.h>
#include <media/videobuf-vmalloc.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fh.h>
#include <media/rc-core.h>
-#include <media/ir-kbd-i2c.h>
+#include <media/i2c/ir-kbd-i2c.h>
#include <media/videobuf-dvb.h>
#include "cx231xx-reg.h"
diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig
index 9facc92c8dea..3dc8ef004f8b 100644
--- a/drivers/media/usb/dvb-usb-v2/Kconfig
+++ b/drivers/media/usb/dvb-usb-v2/Kconfig
@@ -9,7 +9,7 @@ config DVB_USB_V2
<file:Documentation/dvb/README.dvb-usb>.
For a complete list of supported USB devices see the LinuxTV DVB Wiki:
- <http://www.linuxtv.org/wiki/index.php/DVB_USB>
+ <https://linuxtv.org/wiki/index.php/DVB_USB>
Say Y if you own a USB DVB device.
diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
index f5df9eaba04f..f0565bf3673e 100644
--- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
+++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
@@ -400,17 +400,16 @@ skip_feed_stop:
return ret;
}
-static void dvb_usbv2_media_device_register(struct dvb_usb_adapter *adap)
+static int dvb_usbv2_media_device_init(struct dvb_usb_adapter *adap)
{
#ifdef CONFIG_MEDIA_CONTROLLER_DVB
struct media_device *mdev;
struct dvb_usb_device *d = adap_to_d(adap);
struct usb_device *udev = d->udev;
- int ret;
mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
if (!mdev)
- return;
+ return -ENOMEM;
mdev->dev = &udev->dev;
strlcpy(mdev->model, d->name, sizeof(mdev->model));
@@ -420,19 +419,21 @@ static void dvb_usbv2_media_device_register(struct dvb_usb_adapter *adap)
mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
mdev->driver_version = LINUX_VERSION_CODE;
- ret = media_device_register(mdev);
- if (ret) {
- dev_err(&d->udev->dev,
- "Couldn't create a media device. Error: %d\n",
- ret);
- kfree(mdev);
- return;
- }
+ media_device_init(mdev);
dvb_register_media_controller(&adap->dvb_adap, mdev);
dev_info(&d->udev->dev, "media controller created\n");
+#endif
+ return 0;
+}
+static int dvb_usbv2_media_device_register(struct dvb_usb_adapter *adap)
+{
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ return media_device_register(adap->dvb_adap.mdev);
+#else
+ return 0;
#endif
}
@@ -444,6 +445,7 @@ static void dvb_usbv2_media_device_unregister(struct dvb_usb_adapter *adap)
return;
media_device_unregister(adap->dvb_adap.mdev);
+ media_device_cleanup(adap->dvb_adap.mdev);
kfree(adap->dvb_adap.mdev);
adap->dvb_adap.mdev = NULL;
@@ -467,7 +469,12 @@ static int dvb_usbv2_adapter_dvb_init(struct dvb_usb_adapter *adap)
adap->dvb_adap.priv = adap;
- dvb_usbv2_media_device_register(adap);
+ ret = dvb_usbv2_media_device_init(adap);
+ if (ret < 0) {
+ dev_dbg(&d->udev->dev, "%s: dvb_usbv2_media_device_init() failed=%d\n",
+ __func__, ret);
+ goto err_dvb_register_mc;
+ }
if (d->props->read_mac_address) {
ret = d->props->read_mac_address(adap,
@@ -518,6 +525,7 @@ err_dvb_dmxdev_init:
dvb_dmx_release(&adap->demux);
err_dvb_dmx_init:
dvb_usbv2_media_device_unregister(adap);
+err_dvb_register_mc:
dvb_unregister_adapter(&adap->dvb_adap);
err_dvb_register_adapter:
adap->dvb_adap.priv = NULL;
@@ -534,7 +542,6 @@ static int dvb_usbv2_adapter_dvb_exit(struct dvb_usb_adapter *adap)
adap->demux.dmx.close(&adap->demux.dmx);
dvb_dmxdev_release(&adap->dmxdev);
dvb_dmx_release(&adap->demux);
- dvb_usbv2_media_device_unregister(adap);
dvb_unregister_adapter(&adap->dvb_adap);
}
@@ -698,9 +705,13 @@ static int dvb_usbv2_adapter_frontend_init(struct dvb_usb_adapter *adap)
}
}
- dvb_create_media_graph(&adap->dvb_adap);
+ ret = dvb_create_media_graph(&adap->dvb_adap, true);
+ if (ret < 0)
+ goto err_dvb_unregister_frontend;
- return 0;
+ ret = dvb_usbv2_media_device_register(adap);
+
+ return ret;
err_dvb_unregister_frontend:
for (i = count_registered - 1; i >= 0; i--)
@@ -840,6 +851,7 @@ static int dvb_usbv2_adapter_exit(struct dvb_usb_device *d)
dvb_usbv2_adapter_dvb_exit(&d->adapter[i]);
dvb_usbv2_adapter_stream_exit(&d->adapter[i]);
dvb_usbv2_adapter_frontend_exit(&d->adapter[i]);
+ dvb_usbv2_media_device_unregister(&d->adapter[i]);
}
}
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c
index ea3753653368..84f6de6fa07d 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c
@@ -35,7 +35,7 @@ MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
struct mxl111sf_demod_state {
struct mxl111sf_state *mxl_state;
- struct mxl111sf_demod_config *cfg;
+ const struct mxl111sf_demod_config *cfg;
struct dvb_frontend fe;
};
@@ -579,7 +579,7 @@ static struct dvb_frontend_ops mxl111sf_demod_ops = {
};
struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state,
- struct mxl111sf_demod_config *cfg)
+ const struct mxl111sf_demod_config *cfg)
{
struct mxl111sf_demod_state *state = NULL;
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h
index 0bd83e52669c..7065aca81252 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h
@@ -35,11 +35,11 @@ struct mxl111sf_demod_config {
#if IS_ENABLED(CONFIG_DVB_USB_MXL111SF)
extern
struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state,
- struct mxl111sf_demod_config *cfg);
+ const struct mxl111sf_demod_config *cfg);
#else
static inline
struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state,
- struct mxl111sf_demod_config *cfg)
+ const struct mxl111sf_demod_config *cfg)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.c b/drivers/media/usb/dvb-usb-v2/mxl111sf.c
index bec12b0e076b..b669deccc34c 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf.c
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.c
@@ -10,6 +10,7 @@
#include <linux/vmalloc.h>
#include <linux/i2c.h>
+#include <media/tuner.h>
#include "mxl111sf.h"
#include "mxl111sf-reg.h"
@@ -288,9 +289,9 @@ static int mxl111sf_adap_fe_init(struct dvb_frontend *fe)
err = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
mxl_fail(err);
- mxl111sf_enable_usb_output(state);
+ err = mxl111sf_enable_usb_output(state);
mxl_fail(err);
- mxl1x1sf_top_master_ctrl(state, 1);
+ err = mxl1x1sf_top_master_ctrl(state, 1);
mxl_fail(err);
if ((MXL111SF_GPIO_MOD_DVBT != adap_state->gpio_mode) &&
@@ -731,7 +732,7 @@ fail:
return ret;
}
-static struct mxl111sf_demod_config mxl_demod_config = {
+static const struct mxl111sf_demod_config mxl_demod_config = {
.read_reg = mxl111sf_read_reg,
.write_reg = mxl111sf_write_reg,
.program_regs = mxl111sf_ctrl_program_regs,
@@ -868,6 +869,10 @@ static struct mxl111sf_tuner_config mxl_tuner_config = {
static int mxl111sf_attach_tuner(struct dvb_usb_adapter *adap)
{
struct mxl111sf_state *state = adap_to_priv(adap);
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ struct media_device *mdev = dvb_get_media_controller(&adap->dvb_adap);
+ int ret;
+#endif
int i;
pr_debug("%s()\n", __func__);
@@ -879,6 +884,21 @@ static int mxl111sf_attach_tuner(struct dvb_usb_adapter *adap)
adap->fe[i]->ops.read_signal_strength = adap->fe[i]->ops.tuner_ops.get_rf_strength;
}
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ state->tuner.function = MEDIA_ENT_F_TUNER;
+ state->tuner.name = "mxl111sf tuner";
+ state->tuner_pads[TUNER_PAD_RF_INPUT].flags = MEDIA_PAD_FL_SINK;
+ state->tuner_pads[TUNER_PAD_IF_OUTPUT].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&state->tuner,
+ TUNER_NUM_PADS, state->tuner_pads);
+ if (ret)
+ return ret;
+
+ ret = media_device_register_entity(mdev, &state->tuner);
+ if (ret)
+ return ret;
+#endif
return 0;
}
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.h b/drivers/media/usb/dvb-usb-v2/mxl111sf.h
index ee70df1f1e94..846260e0eec0 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf.h
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.h
@@ -17,6 +17,7 @@
#define DVB_USB_LOG_PREFIX "mxl111sf"
#include "dvb_usb.h"
#include <media/tveeprom.h>
+#include <media/media-entity.h>
#define MXL_EP1_REG_READ 1
#define MXL_EP2_REG_WRITE 2
@@ -85,6 +86,10 @@ struct mxl111sf_state {
struct mutex fe_lock;
u8 num_frontends;
struct mxl111sf_adap_state adap_state[3];
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ struct media_entity tuner;
+ struct media_pad tuner_pads[2];
+#endif
};
int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data);
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
index 5a503a6bb8c5..eb5787a3191e 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
@@ -181,11 +181,17 @@ static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
goto err_mutex_unlock;
} else if (msg[0].addr == 0x10) {
/* method 1 - integrated demod */
- req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
- req.index = CMD_DEMOD_RD | dev->page;
- req.size = msg[1].len;
- req.data = &msg[1].buf[0];
- ret = rtl28xxu_ctrl_msg(d, &req);
+ if (msg[0].buf[0] == 0x00) {
+ /* return demod page from driver cache */
+ msg[1].buf[0] = dev->page;
+ ret = 0;
+ } else {
+ req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
+ req.index = CMD_DEMOD_RD | dev->page;
+ req.size = msg[1].len;
+ req.data = &msg[1].buf[0];
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ }
} else if (msg[0].len < 2) {
/* method 2 - old I2C */
req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig
index 128eee61570d..f03b0b70c901 100644
--- a/drivers/media/usb/dvb-usb/Kconfig
+++ b/drivers/media/usb/dvb-usb/Kconfig
@@ -9,7 +9,7 @@ config DVB_USB
<file:Documentation/dvb/README.dvb-usb>.
For a complete list of supported USB devices see the LinuxTV DVB Wiki:
- <http://www.linuxtv.org/wiki/index.php/DVB_USB>
+ <https://linuxtv.org/wiki/index.php/DVB_USB>
Say Y if you own a USB DVB device.
diff --git a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c
index 8a260c854653..9ddfcab268be 100644
--- a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c
+++ b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c
@@ -95,17 +95,16 @@ static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
return dvb_usb_ctrl_feed(dvbdmxfeed, 0);
}
-static void dvb_usb_media_device_register(struct dvb_usb_adapter *adap)
+static int dvb_usb_media_device_init(struct dvb_usb_adapter *adap)
{
#ifdef CONFIG_MEDIA_CONTROLLER_DVB
struct media_device *mdev;
struct dvb_usb_device *d = adap->dev;
struct usb_device *udev = d->udev;
- int ret;
mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
if (!mdev)
- return;
+ return -ENOMEM;
mdev->dev = &udev->dev;
strlcpy(mdev->model, d->desc->name, sizeof(mdev->model));
@@ -115,18 +114,22 @@ static void dvb_usb_media_device_register(struct dvb_usb_adapter *adap)
mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
mdev->driver_version = LINUX_VERSION_CODE;
- ret = media_device_register(mdev);
- if (ret) {
- dev_err(&d->udev->dev,
- "Couldn't create a media device. Error: %d\n",
- ret);
- kfree(mdev);
- return;
- }
+ media_device_init(mdev);
+
dvb_register_media_controller(&adap->dvb_adap, mdev);
dev_info(&d->udev->dev, "media controller created\n");
#endif
+ return 0;
+}
+
+static int dvb_usb_media_device_register(struct dvb_usb_adapter *adap)
+{
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
+ return media_device_register(adap->dvb_adap.mdev);
+#else
+ return 0;
+#endif
}
static void dvb_usb_media_device_unregister(struct dvb_usb_adapter *adap)
@@ -136,6 +139,7 @@ static void dvb_usb_media_device_unregister(struct dvb_usb_adapter *adap)
return;
media_device_unregister(adap->dvb_adap.mdev);
+ media_device_cleanup(adap->dvb_adap.mdev);
kfree(adap->dvb_adap.mdev);
adap->dvb_adap.mdev = NULL;
#endif
@@ -154,7 +158,11 @@ int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, short *adapter_nums)
}
adap->dvb_adap.priv = adap;
- dvb_usb_media_device_register(adap);
+ ret = dvb_usb_media_device_init(adap);
+ if (ret < 0) {
+ deb_info("dvb_usb_media_device_init failed: error %d", ret);
+ goto err_mc;
+ }
if (adap->dev->props.read_mac_address) {
if (adap->dev->props.read_mac_address(adap->dev, adap->dvb_adap.proposed_mac) == 0)
@@ -204,6 +212,7 @@ err_dmx_dev:
dvb_dmx_release(&adap->demux);
err_dmx:
dvb_usb_media_device_unregister(adap);
+err_mc:
dvb_unregister_adapter(&adap->dvb_adap);
err:
return ret;
@@ -318,10 +327,16 @@ int dvb_usb_adapter_frontend_init(struct dvb_usb_adapter *adap)
adap->num_frontends_initialized++;
}
+ if (ret)
+ return ret;
- dvb_create_media_graph(&adap->dvb_adap);
+ ret = dvb_create_media_graph(&adap->dvb_adap, true);
+ if (ret)
+ return ret;
- return 0;
+ ret = dvb_usb_media_device_register(adap);
+
+ return ret;
}
int dvb_usb_adapter_frontend_exit(struct dvb_usb_adapter *adap)
diff --git a/drivers/media/usb/em28xx/em28xx-camera.c b/drivers/media/usb/em28xx/em28xx-camera.c
index ed0b3a87983e..b58acd3fcd99 100644
--- a/drivers/media/usb/em28xx/em28xx-camera.c
+++ b/drivers/media/usb/em28xx/em28xx-camera.c
@@ -21,7 +21,7 @@
#include <linux/i2c.h>
#include <media/soc_camera.h>
-#include <media/mt9v011.h>
+#include <media/i2c/mt9v011.h>
#include <media/v4l2-clk.h>
#include <media/v4l2-common.h>
@@ -322,7 +322,7 @@ int em28xx_detect_sensor(struct em28xx *dev)
int em28xx_init_camera(struct em28xx *dev)
{
- char clk_name[V4L2_SUBDEV_NAME_SIZE];
+ char clk_name[V4L2_CLK_NAME_SIZE];
struct i2c_client *client = &dev->i2c_client[dev->def_i2c_bus];
struct i2c_adapter *adap = &dev->i2c_adap[dev->def_i2c_bus];
struct em28xx_v4l2 *v4l2 = dev->v4l2;
diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
index 394004607059..a1b6ef5894a6 100644
--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -30,10 +30,10 @@
#include <linux/i2c.h>
#include <linux/usb.h>
#include <media/tuner.h>
-#include <media/msp3400.h>
-#include <media/saa7115.h>
-#include <media/tvp5150.h>
-#include <media/tvaudio.h>
+#include <media/drv-intf/msp3400.h>
+#include <media/i2c/saa7115.h>
+#include <media/i2c/tvp5150.h>
+#include <media/i2c/tvaudio.h>
#include <media/i2c-addr.h>
#include <media/tveeprom.h>
#include <media/v4l2-common.h>
@@ -1051,8 +1051,12 @@ struct em28xx_board em28xx_boards[] = {
},
[EM2870_BOARD_TERRATEC_XS_MT2060] = {
.name = "Terratec Cinergy T XS (MT2060)",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .xclk = EM28XX_XCLK_IR_RC5_MODE |
+ EM28XX_XCLK_FREQUENCY_12MHZ,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE,
.tuner_type = TUNER_ABSENT, /* MT2060 */
+ .has_dvb = 1,
+ .tuner_gpio = default_tuner_gpio,
},
[EM2870_BOARD_KWORLD_350U] = {
.name = "Kworld 350 U DVB-T",
@@ -2368,7 +2372,7 @@ struct usb_device_id em28xx_id_table[] = {
{ USB_DEVICE(0x0ccd, 0x0042),
.driver_info = EM2882_BOARD_TERRATEC_HYBRID_XS },
{ USB_DEVICE(0x0ccd, 0x0043),
- .driver_info = EM2870_BOARD_TERRATEC_XS },
+ .driver_info = EM2870_BOARD_TERRATEC_XS_MT2060 },
{ USB_DEVICE(0x0ccd, 0x008e), /* Cinergy HTC USB XS Rev. 1 */
.driver_info = EM2884_BOARD_TERRATEC_HTC_USB_XS },
{ USB_DEVICE(0x0ccd, 0x00ac), /* Cinergy HTC USB XS Rev. 2 */
@@ -2471,6 +2475,8 @@ struct usb_device_id em28xx_id_table[] = {
.driver_info = EM28178_BOARD_PCTV_461E },
{ USB_DEVICE(0x2013, 0x025f),
.driver_info = EM28178_BOARD_PCTV_292E },
+ { USB_DEVICE(0x2040, 0x0264), /* Hauppauge WinTV-soloHD */
+ .driver_info = EM28178_BOARD_PCTV_292E },
{ USB_DEVICE(0x0413, 0x6f07),
.driver_info = EM2861_BOARD_LEADTEK_VC100 },
{ USB_DEVICE(0xeb1a, 0x8179),
diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
index 357be76c7a55..bf5c24467c65 100644
--- a/drivers/media/usb/em28xx/em28xx-dvb.c
+++ b/drivers/media/usb/em28xx/em28xx-dvb.c
@@ -38,6 +38,7 @@
#include "lgdt3305.h"
#include "zl10353.h"
#include "s5h1409.h"
+#include "mt2060.h"
#include "mt352.h"
#include "mt352_priv.h" /* FIXME */
#include "tda1002x.h"
@@ -815,6 +816,10 @@ static struct zl10353_config em28xx_zl10353_no_i2c_gate_dev = {
.parallel_ts = 1,
};
+static struct mt2060_config em28xx_mt2060_config = {
+ .i2c_address = 0x60,
+};
+
static struct qt1010_config em28xx_qt1010_config = {
.i2c_address = 0x62
};
@@ -1142,6 +1147,16 @@ static int em28xx_dvb_init(struct em28xx *dev)
goto out_free;
}
break;
+ case EM2870_BOARD_TERRATEC_XS_MT2060:
+ dvb->fe[0] = dvb_attach(zl10353_attach,
+ &em28xx_zl10353_no_i2c_gate_dev,
+ &dev->i2c_adap[dev->def_i2c_bus]);
+ if (dvb->fe[0] != NULL) {
+ dvb_attach(mt2060_attach, dvb->fe[0],
+ &dev->i2c_adap[dev->def_i2c_bus],
+ &em28xx_mt2060_config, 1220);
+ }
+ break;
case EM2870_BOARD_KWORLD_355U:
dvb->fe[0] = dvb_attach(zl10353_attach,
&em28xx_zl10353_no_i2c_gate_dev,
diff --git a/drivers/media/usb/em28xx/em28xx-vbi.c b/drivers/media/usb/em28xx/em28xx-vbi.c
index e23c285b3108..fe94c9225dd7 100644
--- a/drivers/media/usb/em28xx/em28xx-vbi.c
+++ b/drivers/media/usb/em28xx/em28xx-vbi.c
@@ -31,26 +31,22 @@
/* ------------------------------------------------------------------ */
-static int vbi_queue_setup(struct vb2_queue *vq, const void *parg,
+static int vbi_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct em28xx *dev = vb2_get_drv_priv(vq);
struct em28xx_v4l2 *v4l2 = dev->v4l2;
- unsigned long size;
+ unsigned long size = v4l2->vbi_width * v4l2->vbi_height * 2;
- if (fmt)
- size = fmt->fmt.pix.sizeimage;
- else
- size = v4l2->vbi_width * v4l2->vbi_height * 2;
-
- if (0 == *nbuffers)
- *nbuffers = 32;
if (*nbuffers < 2)
*nbuffers = 2;
- if (*nbuffers > 32)
- *nbuffers = 32;
+
+ if (*nplanes) {
+ if (sizes[0] < size)
+ return -EINVAL;
+ size = sizes[0];
+ }
*nplanes = 1;
sizes[0] = size;
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c
index 6a3cf342e087..0e86ff423c49 100644
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -43,7 +43,7 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
#include <media/v4l2-clk.h>
-#include <media/msp3400.h>
+#include <media/drv-intf/msp3400.h>
#include <media/tuner.h>
#define DRIVER_AUTHOR "Ludovico Cavedon <cavedon@sssup.it>, " \
@@ -438,7 +438,7 @@ static inline void finish_buffer(struct em28xx *dev,
buf->vb.field = V4L2_FIELD_NONE;
else
buf->vb.field = V4L2_FIELD_INTERLACED;
- v4l2_get_timestamp(&buf->vb.timestamp);
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
}
@@ -871,30 +871,19 @@ static void res_free(struct em28xx *dev, enum v4l2_buf_type f_type)
Videobuf2 operations
------------------------------------------------------------------*/
-static int queue_setup(struct vb2_queue *vq, const void *parg,
+static int queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct em28xx *dev = vb2_get_drv_priv(vq);
struct em28xx_v4l2 *v4l2 = dev->v4l2;
- unsigned long size;
-
- if (fmt)
- size = fmt->fmt.pix.sizeimage;
- else
- size =
+ unsigned long size =
(v4l2->width * v4l2->height * v4l2->format->depth + 7) >> 3;
- if (size == 0)
- return -EINVAL;
-
- if (0 == *nbuffers)
- *nbuffers = 32;
-
+ if (*nplanes)
+ return sizes[0] < size ? -EINVAL : 0;
*nplanes = 1;
sizes[0] = size;
-
return 0;
}
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
index 76bf8ba372b3..8ff066c977d9 100644
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -40,7 +40,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fh.h>
-#include <media/ir-kbd-i2c.h>
+#include <media/i2c/ir-kbd-i2c.h>
#include <media/rc-core.h>
#include "tuner-xc2028.h"
#include "xc5000.h"
diff --git a/drivers/media/usb/go7007/go7007-driver.c b/drivers/media/usb/go7007/go7007-driver.c
index ae1cfa792c58..05b1126f263e 100644
--- a/drivers/media/usb/go7007/go7007-driver.c
+++ b/drivers/media/usb/go7007/go7007-driver.c
@@ -466,7 +466,7 @@ static struct go7007_buffer *frame_boundary(struct go7007 *go, struct go7007_buf
else
go7007_set_motion_regions(go, vb, 0);
- v4l2_get_timestamp(&vb->vb.timestamp);
+ vb->vb.vb2_buf.timestamp = ktime_get_ns();
vb_tmp = vb;
spin_lock(&go->spinlock);
list_del(&vb->list);
diff --git a/drivers/media/usb/go7007/go7007-usb.c b/drivers/media/usb/go7007/go7007-usb.c
index 4857c467e76c..3dbf14c85c5c 100644
--- a/drivers/media/usb/go7007/go7007-usb.c
+++ b/drivers/media/usb/go7007/go7007-usb.c
@@ -23,9 +23,9 @@
#include <linux/usb.h>
#include <linux/i2c.h>
#include <asm/byteorder.h>
-#include <media/saa7115.h>
+#include <media/i2c/saa7115.h>
#include <media/tuner.h>
-#include <media/uda1342.h>
+#include <media/i2c/uda1342.h>
#include "go7007-priv.h"
@@ -1289,7 +1289,7 @@ static int go7007_usb_probe(struct usb_interface *intf,
/* Allocate the URBs and buffers for receiving the audio stream */
if ((board->flags & GO7007_USB_EZUSB) &&
- (board->flags & GO7007_BOARD_HAS_AUDIO)) {
+ (board->main_info.flags & GO7007_BOARD_HAS_AUDIO)) {
for (i = 0; i < 8; ++i) {
usb->audio_urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
if (usb->audio_urbs[i] == NULL)
diff --git a/drivers/media/usb/go7007/go7007-v4l2.c b/drivers/media/usb/go7007/go7007-v4l2.c
index f3d187db9368..358c1c186d03 100644
--- a/drivers/media/usb/go7007/go7007-v4l2.c
+++ b/drivers/media/usb/go7007/go7007-v4l2.c
@@ -30,7 +30,7 @@
#include <media/v4l2-subdev.h>
#include <media/v4l2-event.h>
#include <media/videobuf2-vmalloc.h>
-#include <media/saa7115.h>
+#include <media/i2c/saa7115.h>
#include "go7007-priv.h"
@@ -369,7 +369,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
}
static int go7007_queue_setup(struct vb2_queue *q,
- const void *parg,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c
index 146071b8e116..bfff1d1c70ab 100644
--- a/drivers/media/usb/gspca/ov534.c
+++ b/drivers/media/usb/gspca/ov534.c
@@ -1491,8 +1491,13 @@ static void sd_set_streamparm(struct gspca_dev *gspca_dev,
struct v4l2_fract *tpf = &cp->timeperframe;
struct sd *sd = (struct sd *) gspca_dev;
- /* Set requested framerate */
- sd->frame_rate = tpf->denominator / tpf->numerator;
+ if (tpf->numerator == 0 || tpf->denominator == 0)
+ /* Set default framerate */
+ sd->frame_rate = 30;
+ else
+ /* Set requested framerate */
+ sd->frame_rate = tpf->denominator / tpf->numerator;
+
if (gspca_dev->streaming)
set_frame_rate(gspca_dev);
diff --git a/drivers/media/usb/gspca/topro.c b/drivers/media/usb/gspca/topro.c
index c70ff406b07a..c028a5c2438e 100644
--- a/drivers/media/usb/gspca/topro.c
+++ b/drivers/media/usb/gspca/topro.c
@@ -4802,7 +4802,11 @@ static void sd_set_streamparm(struct gspca_dev *gspca_dev,
struct v4l2_fract *tpf = &cp->timeperframe;
int fr, i;
- sd->framerate = tpf->denominator / tpf->numerator;
+ if (tpf->numerator == 0 || tpf->denominator == 0)
+ sd->framerate = 30;
+ else
+ sd->framerate = tpf->denominator / tpf->numerator;
+
if (gspca_dev->streaming)
setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));
diff --git a/drivers/media/usb/hackrf/hackrf.c b/drivers/media/usb/hackrf/hackrf.c
index e05bfec90f46..9e700caf0d66 100644
--- a/drivers/media/usb/hackrf/hackrf.c
+++ b/drivers/media/usb/hackrf/hackrf.c
@@ -24,6 +24,15 @@
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-vmalloc.h>
+/*
+ * Used Avago MGA-81563 RF amplifier could be destroyed pretty easily with too
+ * strong signal or transmitting to bad antenna.
+ * Set RF gain control to 'grabbed' state by default for sure.
+ */
+static bool hackrf_enable_rf_gain_ctrl;
+module_param_named(enable_rf_gain_ctrl, hackrf_enable_rf_gain_ctrl, bool, 0644);
+MODULE_PARM_DESC(enable_rf_gain_ctrl, "enable RX/TX RF amplifier control (warn: could damage amplifier)");
+
/* HackRF USB API commands (from HackRF Library) */
enum {
CMD_SET_TRANSCEIVER_MODE = 0x01,
@@ -517,7 +526,7 @@ static void hackrf_urb_complete_in(struct urb *urb)
urb->transfer_buffer, len);
vb2_set_plane_payload(&buffer->vb.vb2_buf, 0, len);
buffer->vb.sequence = dev->sequence++;
- v4l2_get_timestamp(&buffer->vb.timestamp);
+ buffer->vb.vb2_buf.timestamp = ktime_get_ns();
vb2_buffer_done(&buffer->vb.vb2_buf, VB2_BUF_STATE_DONE);
exit_usb_submit_urb:
usb_submit_urb(urb, GFP_ATOMIC);
@@ -562,7 +571,7 @@ static void hackrf_urb_complete_out(struct urb *urb)
vb2_plane_vaddr(&buffer->vb.vb2_buf, 0), len);
urb->actual_length = len;
buffer->vb.sequence = dev->sequence++;
- v4l2_get_timestamp(&buffer->vb.timestamp);
+ buffer->vb.vb2_buf.timestamp = ktime_get_ns();
vb2_buffer_done(&buffer->vb.vb2_buf, VB2_BUF_STATE_DONE);
exit_usb_submit_urb:
usb_submit_urb(urb, GFP_ATOMIC);
@@ -750,7 +759,7 @@ static void hackrf_return_all_buffers(struct vb2_queue *vq,
}
static int hackrf_queue_setup(struct vb2_queue *vq,
- const void *parg, unsigned int *nbuffers,
+ unsigned int *nbuffers,
unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[])
{
struct hackrf_dev *dev = vb2_get_drv_priv(vq);
@@ -1451,6 +1460,7 @@ static int hackrf_probe(struct usb_interface *intf,
dev_err(dev->dev, "Could not initialize controls\n");
goto err_v4l2_ctrl_handler_free_rx;
}
+ v4l2_ctrl_grab(dev->rx_rf_gain, !hackrf_enable_rf_gain_ctrl);
v4l2_ctrl_handler_setup(&dev->rx_ctrl_handler);
/* Register controls for transmitter */
@@ -1471,6 +1481,7 @@ static int hackrf_probe(struct usb_interface *intf,
dev_err(dev->dev, "Could not initialize controls\n");
goto err_v4l2_ctrl_handler_free_tx;
}
+ v4l2_ctrl_grab(dev->tx_rf_gain, !hackrf_enable_rf_gain_ctrl);
v4l2_ctrl_handler_setup(&dev->tx_ctrl_handler);
/* Register the v4l2_device structure */
@@ -1530,7 +1541,7 @@ err_v4l2_ctrl_handler_free_rx:
err_kfree:
kfree(dev);
err:
- dev_dbg(dev->dev, "failed=%d\n", ret);
+ dev_dbg(&intf->dev, "failed=%d\n", ret);
return ret;
}
diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c
index d8d8c0f519fc..7dee22deebf3 100644
--- a/drivers/media/usb/hdpvr/hdpvr-video.c
+++ b/drivers/media/usb/hdpvr/hdpvr-video.c
@@ -642,7 +642,7 @@ static int vidioc_s_dv_timings(struct file *file, void *_fh,
if (dev->status != STATUS_IDLE)
return -EBUSY;
for (i = 0; i < ARRAY_SIZE(hdpvr_dv_timings); i++)
- if (v4l2_match_dv_timings(timings, hdpvr_dv_timings + i, 0))
+ if (v4l2_match_dv_timings(timings, hdpvr_dv_timings + i, 0, false))
break;
if (i == ARRAY_SIZE(hdpvr_dv_timings))
return -EINVAL;
diff --git a/drivers/media/usb/hdpvr/hdpvr.h b/drivers/media/usb/hdpvr/hdpvr.h
index a3194304182d..78e815441f95 100644
--- a/drivers/media/usb/hdpvr/hdpvr.h
+++ b/drivers/media/usb/hdpvr/hdpvr.h
@@ -17,7 +17,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
-#include <media/ir-kbd-i2c.h>
+#include <media/i2c/ir-kbd-i2c.h>
#define HDPVR_MAX 8
#define HDPVR_I2C_MAX_SIZE 128
diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c
index e06a21a4fbd9..c104315fdc17 100644
--- a/drivers/media/usb/msi2500/msi2500.c
+++ b/drivers/media/usb/msi2500/msi2500.c
@@ -616,7 +616,6 @@ static int msi2500_querycap(struct file *file, void *fh,
/* Videobuf2 operations */
static int msi2500_queue_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *nbuffers,
unsigned int *nplanes, unsigned int sizes[],
void *alloc_ctxs[])
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-audio.c b/drivers/media/usb/pvrusb2/pvrusb2-audio.c
index 45276c628482..5f953d837bf1 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-audio.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-audio.c
@@ -23,7 +23,7 @@
#include "pvrusb2-hdw-internal.h"
#include "pvrusb2-debug.h"
#include <linux/videodev2.h>
-#include <media/msp3400.h>
+#include <media/drv-intf/msp3400.h>
#include <media/v4l2-common.h>
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c
index 1a81aa70509b..7d675fae1846 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c
@@ -32,7 +32,7 @@
#include "pvrusb2-hdw-internal.h"
#include "pvrusb2-debug.h"
-#include <media/cx25840.h>
+#include <media/drv-intf/cx25840.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <linux/errno.h>
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h
index 1f9c02801cee..60141b16d731 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h
@@ -39,8 +39,8 @@
#include "pvrusb2-hdw.h"
#include "pvrusb2-io.h"
#include <media/v4l2-device.h>
-#include <media/cx2341x.h>
-#include <media/ir-kbd-i2c.h>
+#include <media/drv-intf/cx2341x.h>
+#include <media/i2c/ir-kbd-i2c.h>
#include "pvrusb2-devattr.h"
/* Legal values for PVR2_CID_HSM */
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c
index 4baa9d632a4e..14321d0a1833 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c
@@ -20,7 +20,7 @@
#include <linux/i2c.h>
#include <linux/module.h>
-#include <media/ir-kbd-i2c.h>
+#include <media/i2c/ir-kbd-i2c.h>
#include "pvrusb2-i2c-core.h"
#include "pvrusb2-hdw-internal.h"
#include "pvrusb2-debug.h"
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
index 1c5f85bf7ed4..81f788b7b242 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
@@ -628,6 +628,7 @@ static int pvr2_g_ext_ctrls(struct file *file, void *priv,
struct pvr2_v4l2_fh *fh = file->private_data;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
struct v4l2_ext_control *ctrl;
+ struct pvr2_ctrl *cptr;
unsigned int idx;
int val;
int ret;
@@ -635,8 +636,15 @@ static int pvr2_g_ext_ctrls(struct file *file, void *priv,
ret = 0;
for (idx = 0; idx < ctls->count; idx++) {
ctrl = ctls->controls + idx;
- ret = pvr2_ctrl_get_value(
- pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id), &val);
+ cptr = pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id);
+ if (cptr) {
+ if (ctls->which == V4L2_CTRL_WHICH_DEF_VAL)
+ pvr2_ctrl_get_def(cptr, &val);
+ else
+ ret = pvr2_ctrl_get_value(cptr, &val);
+ } else
+ ret = -EINVAL;
+
if (ret) {
ctls->error_idx = idx;
return ret;
@@ -658,6 +666,10 @@ static int pvr2_s_ext_ctrls(struct file *file, void *priv,
unsigned int idx;
int ret;
+ /* Default value cannot be changed */
+ if (ctls->which == V4L2_CTRL_WHICH_DEF_VAL)
+ return -EINVAL;
+
ret = 0;
for (idx = 0; idx < ctls->count; idx++) {
ctrl = ctls->controls + idx;
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c
index 139b39740534..105123ab36aa 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c
@@ -35,7 +35,7 @@
#include "pvrusb2-debug.h"
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
-#include <media/saa7115.h>
+#include <media/i2c/saa7115.h>
#include <linux/errno.h>
struct routing_scheme {
diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c
index b79c36fd8cd2..086cf1c7bd7d 100644
--- a/drivers/media/usb/pwc/pwc-if.c
+++ b/drivers/media/usb/pwc/pwc-if.c
@@ -316,8 +316,7 @@ static void pwc_isoc_handler(struct urb *urb)
struct pwc_frame_buf *fbuf = pdev->fill_buf;
if (pdev->vsync == 1) {
- v4l2_get_timestamp(
- &fbuf->vb.timestamp);
+ fbuf->vb.vb2_buf.timestamp = ktime_get_ns();
pdev->vsync = 2;
}
@@ -571,7 +570,7 @@ static void pwc_video_release(struct v4l2_device *v)
/***************************************************************************/
/* Videobuf2 operations */
-static int queue_setup(struct vb2_queue *vq, const void *parg,
+static int queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c
index e7acb12ad21d..9acdaa3716fb 100644
--- a/drivers/media/usb/s2255/s2255drv.c
+++ b/drivers/media/usb/s2255/s2255drv.c
@@ -574,7 +574,7 @@ static void s2255_got_frame(struct s2255_vc *vc, int jpgsize)
buf = list_entry(vc->buf_list.next,
struct s2255_buffer, list);
list_del(&buf->list);
- v4l2_get_timestamp(&buf->vb.timestamp);
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
buf->vb.field = vc->field;
buf->vb.sequence = vc->frame_count;
spin_unlock_irqrestore(&vc->qlock, flags);
@@ -660,7 +660,7 @@ static void s2255_fillbuff(struct s2255_vc *vc,
Videobuf operations
------------------------------------------------------------------*/
-static int queue_setup(struct vb2_queue *vq, const void *parg,
+static int queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c
index c945e4c2fbd4..8abbd3cc8eba 100644
--- a/drivers/media/usb/siano/smsusb.c
+++ b/drivers/media/usb/siano/smsusb.c
@@ -361,10 +361,11 @@ static void *siano_media_device_register(struct smsusb_device_t *dev,
mdev->hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
mdev->driver_version = LINUX_VERSION_CODE;
+ media_device_init(mdev);
+
ret = media_device_register(mdev);
if (ret) {
- pr_err("Couldn't create a media device. Error: %d\n",
- ret);
+ media_device_cleanup(mdev);
kfree(mdev);
return NULL;
}
diff --git a/drivers/media/usb/stk1160/stk1160-core.c b/drivers/media/usb/stk1160/stk1160-core.c
index 1b6836f15370..bc029478065a 100644
--- a/drivers/media/usb/stk1160/stk1160-core.c
+++ b/drivers/media/usb/stk1160/stk1160-core.c
@@ -34,7 +34,7 @@
#include <linux/usb.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
-#include <media/saa7115.h>
+#include <media/i2c/saa7115.h>
#include "stk1160.h"
#include "stk1160-reg.h"
diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c
index 0bd34f1e7fa9..77131fd614a5 100644
--- a/drivers/media/usb/stk1160/stk1160-v4l.c
+++ b/drivers/media/usb/stk1160/stk1160-v4l.c
@@ -33,7 +33,7 @@
#include <media/v4l2-event.h>
#include <media/videobuf2-vmalloc.h>
-#include <media/saa7115.h>
+#include <media/i2c/saa7115.h>
#include "stk1160.h"
#include "stk1160-reg.h"
@@ -664,7 +664,7 @@ static const struct v4l2_ioctl_ops stk1160_ioctl_ops = {
/*
* Videobuf2 operations
*/
-static int queue_setup(struct vb2_queue *vq, const void *parg,
+static int queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c
index 75654e676e80..46191d5262eb 100644
--- a/drivers/media/usb/stk1160/stk1160-video.c
+++ b/drivers/media/usb/stk1160/stk1160-video.c
@@ -99,7 +99,7 @@ void stk1160_buffer_done(struct stk1160 *dev)
buf->vb.sequence = dev->sequence++;
buf->vb.field = V4L2_FIELD_INTERLACED;
buf->vb.vb2_buf.planes[0].bytesused = buf->bytesused;
- v4l2_get_timestamp(&buf->vb.timestamp);
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->bytesused);
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
diff --git a/drivers/media/usb/tm6000/tm6000-cards.c b/drivers/media/usb/tm6000/tm6000-cards.c
index 2e8c3afe4ec4..8902ee36bc94 100644
--- a/drivers/media/usb/tm6000/tm6000-cards.c
+++ b/drivers/media/usb/tm6000/tm6000-cards.c
@@ -26,7 +26,7 @@
#include <linux/slab.h>
#include <media/v4l2-common.h>
#include <media/tuner.h>
-#include <media/tvaudio.h>
+#include <media/i2c/tvaudio.h>
#include <media/i2c-addr.h>
#include <media/rc-map.h>
diff --git a/drivers/media/usb/ttusb-dec/ttusb_dec.c b/drivers/media/usb/ttusb-dec/ttusb_dec.c
index a5de46f04247..4e36e24cb3a6 100644
--- a/drivers/media/usb/ttusb-dec/ttusb_dec.c
+++ b/drivers/media/usb/ttusb-dec/ttusb_dec.c
@@ -1606,7 +1606,7 @@ static int fe_send_command(struct dvb_frontend* fe, const u8 command,
return ttusb_dec_send_command(dec, command, param_length, params, result_length, cmd_result);
}
-static struct ttusbdecfe_config fe_config = {
+static const struct ttusbdecfe_config fe_config = {
.send_command = fe_send_command
};
diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c
index e645c9df2d94..4ebb33943f9a 100644
--- a/drivers/media/usb/usbtv/usbtv-video.c
+++ b/drivers/media/usb/usbtv/usbtv-video.c
@@ -322,7 +322,7 @@ static void usbtv_image_chunk(struct usbtv *usbtv, __be32 *chunk)
buf->vb.field = V4L2_FIELD_INTERLACED;
buf->vb.sequence = usbtv->sequence++;
- v4l2_get_timestamp(&buf->vb.timestamp);
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
vb2_buffer_done(&buf->vb.vb2_buf, state);
list_del(&buf->list);
@@ -599,19 +599,18 @@ static struct v4l2_file_operations usbtv_fops = {
};
static int usbtv_queue_setup(struct vb2_queue *vq,
- const void *parg, unsigned int *nbuffers,
+ unsigned int *nbuffers,
unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct usbtv *usbtv = vb2_get_drv_priv(vq);
unsigned size = USBTV_CHUNK * usbtv->n_chunks * 2 * sizeof(u32);
if (vq->num_buffers + *nbuffers < 2)
*nbuffers = 2 - vq->num_buffers;
+ if (*nplanes)
+ return sizes[0] < size ? -EINVAL : 0;
*nplanes = 1;
- if (fmt && fmt->fmt.pix.sizeimage < size)
- return -EINVAL;
- sizes[0] = fmt ? fmt->fmt.pix.sizeimage : size;
+ sizes[0] = size;
return 0;
}
diff --git a/drivers/media/usb/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c
index dc3b4d5155c5..1ea04e75fb36 100644
--- a/drivers/media/usb/usbvision/usbvision-core.c
+++ b/drivers/media/usb/usbvision/usbvision-core.c
@@ -37,7 +37,7 @@
#include <linux/videodev2.h>
#include <linux/i2c.h>
-#include <media/saa7115.h>
+#include <media/i2c/saa7115.h>
#include <media/v4l2-common.h>
#include <media/tuner.h>
diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c
index b693206f66dd..de9ff3bb8edd 100644
--- a/drivers/media/usb/usbvision/usbvision-video.c
+++ b/drivers/media/usb/usbvision/usbvision-video.c
@@ -59,7 +59,7 @@
#include <linux/videodev2.h>
#include <linux/i2c.h>
-#include <media/saa7115.h>
+#include <media/i2c/saa7115.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
@@ -1461,11 +1461,32 @@ static int usbvision_probe(struct usb_interface *intf,
printk(KERN_INFO "%s: %s found\n", __func__,
usbvision_device_data[model].model_string);
+ /*
+ * this is a security check.
+ * an exploit using an incorrect bInterfaceNumber is known
+ */
+ if (ifnum >= USB_MAXINTERFACES || !dev->actconfig->interface[ifnum])
+ return -ENODEV;
+
if (usbvision_device_data[model].interface >= 0)
interface = &dev->actconfig->interface[usbvision_device_data[model].interface]->altsetting[0];
- else
+ else if (ifnum < dev->actconfig->desc.bNumInterfaces)
interface = &dev->actconfig->interface[ifnum]->altsetting[0];
+ else {
+ dev_err(&intf->dev, "interface %d is invalid, max is %d\n",
+ ifnum, dev->actconfig->desc.bNumInterfaces - 1);
+ ret = -ENODEV;
+ goto err_usb;
+ }
+
+ if (interface->desc.bNumEndpoints < 2) {
+ dev_err(&intf->dev, "interface %d has %d endpoints, but must"
+ " have minimum 2\n", ifnum, interface->desc.bNumEndpoints);
+ ret = -ENODEV;
+ goto err_usb;
+ }
endpoint = &interface->endpoint[1].desc;
+
if (!usb_endpoint_xfer_isoc(endpoint)) {
dev_err(&intf->dev, "%s: interface %d. has non-ISO endpoint!\n",
__func__, ifnum);
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 3e59b288b8a8..c2ee6e39fd0c 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -227,7 +227,8 @@ static struct uvc_control_info uvc_ctrls[] = {
.size = 4,
.flags = UVC_CTRL_FLAG_SET_CUR
| UVC_CTRL_FLAG_GET_RANGE
- | UVC_CTRL_FLAG_RESTORE,
+ | UVC_CTRL_FLAG_RESTORE
+ | UVC_CTRL_FLAG_AUTO_UPDATE,
},
{
.entity = UVC_GUID_UVC_CAMERA,
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index d11fd6ac2df0..4e7148815a78 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1656,6 +1656,7 @@ static void uvc_delete(struct uvc_device *dev)
#ifdef CONFIG_MEDIA_CONTROLLER
if (media_devnode_is_registered(&dev->mdev.devnode))
media_device_unregister(&dev->mdev);
+ media_device_cleanup(&dev->mdev);
#endif
list_for_each_safe(p, n, &dev->chains) {
@@ -1906,7 +1907,7 @@ static int uvc_probe(struct usb_interface *intf,
"linux-uvc-devel mailing list.\n");
}
- /* Register the media and V4L2 devices. */
+ /* Initialize the media device and register the V4L2 device. */
#ifdef CONFIG_MEDIA_CONTROLLER
dev->mdev.dev = &intf->dev;
strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model));
@@ -1916,8 +1917,7 @@ static int uvc_probe(struct usb_interface *intf,
strcpy(dev->mdev.bus_info, udev->devpath);
dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
dev->mdev.driver_version = LINUX_VERSION_CODE;
- if (media_device_register(&dev->mdev) < 0)
- goto error;
+ media_device_init(&dev->mdev);
dev->vdev.mdev = &dev->mdev;
#endif
@@ -1936,6 +1936,11 @@ static int uvc_probe(struct usb_interface *intf,
if (uvc_register_chains(dev) < 0)
goto error;
+#ifdef CONFIG_MEDIA_CONTROLLER
+ /* Register the media device node */
+ if (media_device_register(&dev->mdev) < 0)
+ goto error;
+#endif
/* Save our data pointer in the interface data. */
usb_set_intfdata(intf, dev);
@@ -2540,7 +2545,8 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_FORCE_Y8 },
/* Generic USB Video Class */
- { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
+ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_UNDEFINED) },
+ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_15) },
{}
};
diff --git a/drivers/media/usb/uvc/uvc_entity.c b/drivers/media/usb/uvc/uvc_entity.c
index dc56a59ecadc..ac386bb547e6 100644
--- a/drivers/media/usb/uvc/uvc_entity.c
+++ b/drivers/media/usb/uvc/uvc_entity.c
@@ -19,12 +19,8 @@
#include "uvcvideo.h"
-/* ------------------------------------------------------------------------
- * Video subdevices registration and unregistration
- */
-
-static int uvc_mc_register_entity(struct uvc_video_chain *chain,
- struct uvc_entity *entity)
+static int uvc_mc_create_links(struct uvc_video_chain *chain,
+ struct uvc_entity *entity)
{
const u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE;
struct media_entity *sink;
@@ -56,16 +52,13 @@ static int uvc_mc_register_entity(struct uvc_video_chain *chain,
continue;
remote_pad = remote->num_pads - 1;
- ret = media_entity_create_link(source, remote_pad,
+ ret = media_create_pad_link(source, remote_pad,
sink, i, flags);
if (ret < 0)
return ret;
}
- if (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING)
- return 0;
-
- return v4l2_device_register_subdev(&chain->dev->vdev, &entity->subdev);
+ return 0;
}
static struct v4l2_subdev_ops uvc_subdev_ops = {
@@ -79,7 +72,8 @@ void uvc_mc_cleanup_entity(struct uvc_entity *entity)
media_entity_cleanup(&entity->vdev->entity);
}
-static int uvc_mc_init_entity(struct uvc_entity *entity)
+static int uvc_mc_init_entity(struct uvc_video_chain *chain,
+ struct uvc_entity *entity)
{
int ret;
@@ -88,11 +82,17 @@ static int uvc_mc_init_entity(struct uvc_entity *entity)
strlcpy(entity->subdev.name, entity->name,
sizeof(entity->subdev.name));
- ret = media_entity_init(&entity->subdev.entity,
- entity->num_pads, entity->pads, 0);
+ ret = media_entity_pads_init(&entity->subdev.entity,
+ entity->num_pads, entity->pads);
+
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_device_register_subdev(&chain->dev->vdev,
+ &entity->subdev);
} else if (entity->vdev != NULL) {
- ret = media_entity_init(&entity->vdev->entity,
- entity->num_pads, entity->pads, 0);
+ ret = media_entity_pads_init(&entity->vdev->entity,
+ entity->num_pads, entity->pads);
if (entity->flags & UVC_ENTITY_FLAG_DEFAULT)
entity->vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT;
} else
@@ -107,7 +107,7 @@ int uvc_mc_register_entities(struct uvc_video_chain *chain)
int ret;
list_for_each_entry(entity, &chain->entities, chain) {
- ret = uvc_mc_init_entity(entity);
+ ret = uvc_mc_init_entity(chain, entity);
if (ret < 0) {
uvc_printk(KERN_INFO, "Failed to initialize entity for "
"entity %u\n", entity->id);
@@ -116,9 +116,9 @@ int uvc_mc_register_entities(struct uvc_video_chain *chain)
}
list_for_each_entry(entity, &chain->entities, chain) {
- ret = uvc_mc_register_entity(chain, entity);
+ ret = uvc_mc_create_links(chain, entity);
if (ret < 0) {
- uvc_printk(KERN_INFO, "Failed to register entity for "
+ uvc_printk(KERN_INFO, "Failed to create links for "
"entity %u\n", entity->id);
return ret;
}
diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index cfb868a48b5f..54394722756f 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -69,23 +69,19 @@ static void uvc_queue_return_buffers(struct uvc_video_queue *queue,
* videobuf2 queue operations
*/
-static int uvc_queue_setup(struct vb2_queue *vq, const void *parg,
+static int uvc_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- const struct v4l2_format *fmt = parg;
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
struct uvc_streaming *stream = uvc_queue_to_stream(queue);
+ unsigned size = stream->ctrl.dwMaxVideoFrameSize;
/* Make sure the image size is large enough. */
- if (fmt && fmt->fmt.pix.sizeimage < stream->ctrl.dwMaxVideoFrameSize)
- return -EINVAL;
-
+ if (*nplanes)
+ return sizes[0] < size ? -EINVAL : 0;
*nplanes = 1;
-
- sizes[0] = fmt ? fmt->fmt.pix.sizeimage
- : stream->ctrl.dwMaxVideoFrameSize;
-
+ sizes[0] = size;
return 0;
}
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 2764f43607c1..d7723ce772b3 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -983,6 +983,22 @@ static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
unsigned int i;
int ret;
+ if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) {
+ for (i = 0; i < ctrls->count; ++ctrl, ++i) {
+ struct v4l2_queryctrl qc = { .id = ctrl->id };
+
+ ret = uvc_query_v4l2_ctrl(chain, &qc);
+ if (ret < 0) {
+ ctrls->error_idx = i;
+ return ret;
+ }
+
+ ctrl->value = qc.default_value;
+ }
+
+ return 0;
+ }
+
ret = uvc_ctrl_begin(chain);
if (ret < 0)
return ret;
@@ -1010,6 +1026,10 @@ static int uvc_ioctl_s_try_ext_ctrls(struct uvc_fh *handle,
unsigned int i;
int ret;
+ /* Default value cannot be changed */
+ if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL)
+ return -EINVAL;
+
ret = uvc_ctrl_begin(chain);
if (ret < 0)
return ret;
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 2b276ab7764f..075a0fe77485 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -694,22 +694,19 @@ void uvc_video_clock_update(struct uvc_streaming *stream,
ts.tv_nsec -= NSEC_PER_SEC;
}
- uvc_trace(UVC_TRACE_CLOCK, "%s: SOF %u.%06llu y %llu ts %lu.%06lu "
- "buf ts %lu.%06lu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %u)\n",
+ uvc_trace(UVC_TRACE_CLOCK, "%s: SOF %u.%06llu y %llu ts %llu "
+ "buf ts %llu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %u)\n",
stream->dev->name,
sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536),
- y, ts.tv_sec, ts.tv_nsec / NSEC_PER_USEC,
- vbuf->timestamp.tv_sec,
- (unsigned long)vbuf->timestamp.tv_usec,
+ y, timespec_to_ns(&ts), vbuf->vb2_buf.timestamp,
x1, first->host_sof, first->dev_sof,
x2, last->host_sof, last->dev_sof, y1, y2);
/* Update the V4L2 buffer. */
- vbuf->timestamp.tv_sec = ts.tv_sec;
- vbuf->timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+ vbuf->vb2_buf.timestamp = timespec_to_ns(&ts);
done:
- spin_unlock_irqrestore(&stream->clock.lock, flags);
+ spin_unlock_irqrestore(&clock->lock, flags);
}
/* ------------------------------------------------------------------------
@@ -1034,9 +1031,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
buf->buf.field = V4L2_FIELD_NONE;
buf->buf.sequence = stream->sequence;
- buf->buf.timestamp.tv_sec = ts.tv_sec;
- buf->buf.timestamp.tv_usec =
- ts.tv_nsec / NSEC_PER_USEC;
+ buf->buf.vb2_buf.timestamp = timespec_to_ns(&ts);
/* TODO: Handle PTS and SCR. */
buf->state = UVC_BUF_STATE_ACTIVE;
diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c
index 581e21ad6801..76496fd282aa 100644
--- a/drivers/media/v4l2-core/tuner-core.c
+++ b/drivers/media/v4l2-core/tuner-core.c
@@ -134,8 +134,9 @@ struct tuner {
unsigned int type; /* chip type id */
void *config;
const char *name;
+
#if defined(CONFIG_MEDIA_CONTROLLER)
- struct media_pad pad;
+ struct media_pad pad[TUNER_NUM_PADS];
#endif
};
@@ -695,11 +696,12 @@ static int tuner_probe(struct i2c_client *client,
/* Should be just before return */
register_client:
#if defined(CONFIG_MEDIA_CONTROLLER)
- t->pad.flags = MEDIA_PAD_FL_SOURCE;
- t->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_TUNER;
+ t->pad[TUNER_PAD_RF_INPUT].flags = MEDIA_PAD_FL_SINK;
+ t->pad[TUNER_PAD_IF_OUTPUT].flags = MEDIA_PAD_FL_SOURCE;
+ t->sd.entity.function = MEDIA_ENT_F_TUNER;
t->sd.entity.name = t->name;
- ret = media_entity_init(&t->sd.entity, 1, &t->pad, 0);
+ ret = media_entity_pads_init(&t->sd.entity, TUNER_NUM_PADS, &t->pad[0]);
if (ret < 0) {
tuner_err("failed to initialize media entity!\n");
kfree(t);
diff --git a/drivers/media/v4l2-core/v4l2-clk.c b/drivers/media/v4l2-core/v4l2-clk.c
index 34e416a554f6..297e10e69898 100644
--- a/drivers/media/v4l2-core/v4l2-clk.c
+++ b/drivers/media/v4l2-core/v4l2-clk.c
@@ -15,6 +15,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/of.h>
#include <linux/slab.h>
#include <linux/string.h>
@@ -39,6 +40,7 @@ struct v4l2_clk *v4l2_clk_get(struct device *dev, const char *id)
{
struct v4l2_clk *clk;
struct clk *ccf_clk = clk_get(dev, id);
+ char clk_name[V4L2_CLK_NAME_SIZE];
if (PTR_ERR(ccf_clk) == -EPROBE_DEFER)
return ERR_PTR(-EPROBE_DEFER);
@@ -57,6 +59,13 @@ struct v4l2_clk *v4l2_clk_get(struct device *dev, const char *id)
mutex_lock(&clk_lock);
clk = v4l2_clk_find(dev_name(dev));
+ /* if dev_name is not found, try use the OF name to find again */
+ if (PTR_ERR(clk) == -ENODEV && dev->of_node) {
+ v4l2_clk_name_of(clk_name, sizeof(clk_name),
+ of_node_full_name(dev->of_node));
+ clk = v4l2_clk_find(clk_name);
+ }
+
if (!IS_ERR(clk))
atomic_inc(&clk->use_count);
mutex_unlock(&clk_lock);
diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index 327e83ac2469..8fd84a67478a 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -630,7 +630,7 @@ static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __
}
struct v4l2_ext_controls32 {
- __u32 ctrl_class;
+ __u32 which;
__u32 count;
__u32 error_idx;
__u32 reserved[2];
@@ -673,7 +673,7 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
compat_caddr_t p;
if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) ||
- get_user(kp->ctrl_class, &up->ctrl_class) ||
+ get_user(kp->which, &up->which) ||
get_user(kp->count, &up->count) ||
get_user(kp->error_idx, &up->error_idx) ||
copy_from_user(kp->reserved, up->reserved,
@@ -723,7 +723,7 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
compat_caddr_t p;
if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_ext_controls32)) ||
- put_user(kp->ctrl_class, &up->ctrl_class) ||
+ put_user(kp->which, &up->which) ||
put_user(kp->count, &up->count) ||
put_user(kp->error_idx, &up->error_idx) ||
copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved)))
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 4a1d9fdd14bb..c9d5537b6af7 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -1491,6 +1491,17 @@ static int new_to_user(struct v4l2_ext_control *c,
return ptr_to_user(c, ctrl, ctrl->p_new);
}
+/* Helper function: copy the initial control value back to the caller */
+static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
+{
+ int idx;
+
+ for (idx = 0; idx < ctrl->elems; idx++)
+ ctrl->type_ops->init(ctrl, idx, ctrl->p_new);
+
+ return ptr_to_user(c, ctrl, ctrl->p_new);
+}
+
/* Helper function: copy the caller-provider value to the given control value */
static int user_to_ptr(struct v4l2_ext_control *c,
struct v4l2_ctrl *ctrl,
@@ -1762,7 +1773,7 @@ static struct v4l2_ctrl_ref *find_private_ref(
list_for_each_entry(ref, &hdl->ctrl_refs, node) {
/* Search for private user controls that are compatible with
VIDIOC_G/S_CTRL. */
- if (V4L2_CTRL_ID2CLASS(ref->ctrl->id) == V4L2_CTRL_CLASS_USER &&
+ if (V4L2_CTRL_ID2WHICH(ref->ctrl->id) == V4L2_CTRL_CLASS_USER &&
V4L2_CTRL_DRIVER_PRIV(ref->ctrl->id)) {
if (!ref->ctrl->is_int)
continue;
@@ -1831,7 +1842,7 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
struct v4l2_ctrl_ref *ref;
struct v4l2_ctrl_ref *new_ref;
u32 id = ctrl->id;
- u32 class_ctrl = V4L2_CTRL_ID2CLASS(id) | 1;
+ u32 class_ctrl = V4L2_CTRL_ID2WHICH(id) | 1;
int bucket = id % hdl->nr_of_buckets; /* which bucket to use */
/*
@@ -2253,9 +2264,9 @@ EXPORT_SYMBOL(v4l2_ctrl_add_handler);
bool v4l2_ctrl_radio_filter(const struct v4l2_ctrl *ctrl)
{
- if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_FM_TX)
+ if (V4L2_CTRL_ID2WHICH(ctrl->id) == V4L2_CTRL_CLASS_FM_TX)
return true;
- if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_FM_RX)
+ if (V4L2_CTRL_ID2WHICH(ctrl->id) == V4L2_CTRL_CLASS_FM_RX)
return true;
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -2710,7 +2721,9 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
cs->error_idx = i;
- if (cs->ctrl_class && V4L2_CTRL_ID2CLASS(id) != cs->ctrl_class)
+ if (cs->which &&
+ cs->which != V4L2_CTRL_WHICH_DEF_VAL &&
+ V4L2_CTRL_ID2WHICH(id) != cs->which)
return -EINVAL;
/* Old-style private controls are not allowed for
@@ -2787,11 +2800,11 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
/* Handles the corner case where cs->count == 0. It checks whether the
specified control class exists. If that class ID is 0, then it checks
whether there are any controls at all. */
-static int class_check(struct v4l2_ctrl_handler *hdl, u32 ctrl_class)
+static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
{
- if (ctrl_class == 0)
+ if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL)
return list_empty(&hdl->ctrl_refs) ? -EINVAL : 0;
- return find_ref_lock(hdl, ctrl_class | 1) ? 0 : -EINVAL;
+ return find_ref_lock(hdl, which | 1) ? 0 : -EINVAL;
}
@@ -2803,15 +2816,18 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
struct v4l2_ctrl_helper *helpers = helper;
int ret;
int i, j;
+ bool def_value;
+
+ def_value = (cs->which == V4L2_CTRL_WHICH_DEF_VAL);
cs->error_idx = cs->count;
- cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
+ cs->which = V4L2_CTRL_ID2WHICH(cs->which);
if (hdl == NULL)
return -EINVAL;
if (cs->count == 0)
- return class_check(hdl, cs->ctrl_class);
+ return class_check(hdl, cs->which);
if (cs->count > ARRAY_SIZE(helper)) {
helpers = kmalloc_array(cs->count, sizeof(helper[0]),
@@ -2829,9 +2845,11 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
for (i = 0; !ret && i < cs->count; i++) {
int (*ctrl_to_user)(struct v4l2_ext_control *c,
- struct v4l2_ctrl *ctrl) = cur_to_user;
+ struct v4l2_ctrl *ctrl);
struct v4l2_ctrl *master;
+ ctrl_to_user = def_value ? def_to_user : cur_to_user;
+
if (helpers[i].mref == NULL)
continue;
@@ -2841,8 +2859,9 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
v4l2_ctrl_lock(master);
/* g_volatile_ctrl will update the new control values */
- if ((master->flags & V4L2_CTRL_FLAG_VOLATILE) ||
- (master->has_volatiles && !is_cur_manual(master))) {
+ if (!def_value &&
+ ((master->flags & V4L2_CTRL_FLAG_VOLATILE) ||
+ (master->has_volatiles && !is_cur_manual(master)))) {
for (j = 0; j < master->ncontrols; j++)
cur_to_new(master->cluster[j]);
ret = call_op(master, g_volatile_ctrl);
@@ -3064,13 +3083,18 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
int ret;
cs->error_idx = cs->count;
- cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
+
+ /* Default value cannot be changed */
+ if (cs->which == V4L2_CTRL_WHICH_DEF_VAL)
+ return -EINVAL;
+
+ cs->which = V4L2_CTRL_ID2WHICH(cs->which);
if (hdl == NULL)
return -EINVAL;
if (cs->count == 0)
- return class_check(hdl, cs->ctrl_class);
+ return class_check(hdl, cs->which);
if (cs->count > ARRAY_SIZE(helper)) {
helpers = kmalloc_array(cs->count, sizeof(helper[0]),
@@ -3300,7 +3324,8 @@ EXPORT_SYMBOL(v4l2_ctrl_notify);
int __v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl,
s64 min, s64 max, u64 step, s64 def)
{
- bool changed;
+ bool value_changed;
+ bool range_changed = false;
int ret;
lockdep_assert_held(ctrl->handler->lock);
@@ -3324,10 +3349,14 @@ int __v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl,
default:
return -EINVAL;
}
- ctrl->minimum = min;
- ctrl->maximum = max;
- ctrl->step = step;
- ctrl->default_value = def;
+ if ((ctrl->minimum != min) || (ctrl->maximum != max) ||
+ (ctrl->step != step) || ctrl->default_value != def) {
+ range_changed = true;
+ ctrl->minimum = min;
+ ctrl->maximum = max;
+ ctrl->step = step;
+ ctrl->default_value = def;
+ }
cur_to_new(ctrl);
if (validate_new(ctrl, ctrl->p_new)) {
if (ctrl->type == V4L2_CTRL_TYPE_INTEGER64)
@@ -3337,12 +3366,12 @@ int __v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl,
}
if (ctrl->type == V4L2_CTRL_TYPE_INTEGER64)
- changed = *ctrl->p_new.p_s64 != *ctrl->p_cur.p_s64;
+ value_changed = *ctrl->p_new.p_s64 != *ctrl->p_cur.p_s64;
else
- changed = *ctrl->p_new.p_s32 != *ctrl->p_cur.p_s32;
- if (changed)
+ value_changed = *ctrl->p_new.p_s32 != *ctrl->p_cur.p_s32;
+ if (value_changed)
ret = set_ctrl(NULL, ctrl, V4L2_EVENT_CTRL_CH_RANGE);
- else
+ else if (range_changed)
send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_RANGE);
return ret;
}
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index 6b1eaeddbdb3..d8e5994cccf1 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -194,9 +194,12 @@ static void v4l2_device_release(struct device *cd)
mutex_unlock(&videodev_lock);
#if defined(CONFIG_MEDIA_CONTROLLER)
- if (v4l2_dev->mdev &&
- vdev->vfl_type != VFL_TYPE_SUBDEV)
- media_device_unregister_entity(&vdev->entity);
+ if (v4l2_dev->mdev) {
+ /* Remove interfaces and interface links */
+ media_devnode_remove(vdev->intf_devnode);
+ if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN)
+ media_device_unregister_entity(&vdev->entity);
+ }
#endif
/* Do not call v4l2_device_put if there is no release callback set.
@@ -723,6 +726,91 @@ static void determine_valid_ioctls(struct video_device *vdev)
BASE_VIDIOC_PRIVATE);
}
+static int video_register_media_controller(struct video_device *vdev, int type)
+{
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ u32 intf_type;
+ int ret;
+
+ if (!vdev->v4l2_dev->mdev)
+ return 0;
+
+ vdev->entity.function = MEDIA_ENT_F_UNKNOWN;
+
+ switch (type) {
+ case VFL_TYPE_GRABBER:
+ intf_type = MEDIA_INTF_T_V4L_VIDEO;
+ vdev->entity.function = MEDIA_ENT_F_IO_V4L;
+ break;
+ case VFL_TYPE_VBI:
+ intf_type = MEDIA_INTF_T_V4L_VBI;
+ vdev->entity.function = MEDIA_ENT_F_IO_VBI;
+ break;
+ case VFL_TYPE_SDR:
+ intf_type = MEDIA_INTF_T_V4L_SWRADIO;
+ vdev->entity.function = MEDIA_ENT_F_IO_SWRADIO;
+ break;
+ case VFL_TYPE_RADIO:
+ intf_type = MEDIA_INTF_T_V4L_RADIO;
+ /*
+ * Radio doesn't have an entity at the V4L2 side to represent
+ * radio input or output. Instead, the audio input/output goes
+ * via either physical wires or ALSA.
+ */
+ break;
+ case VFL_TYPE_SUBDEV:
+ intf_type = MEDIA_INTF_T_V4L_SUBDEV;
+ /* Entity will be created via v4l2_device_register_subdev() */
+ break;
+ default:
+ return 0;
+ }
+
+ if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN) {
+ vdev->entity.name = vdev->name;
+
+ /* Needed just for backward compatibility with legacy MC API */
+ vdev->entity.info.dev.major = VIDEO_MAJOR;
+ vdev->entity.info.dev.minor = vdev->minor;
+
+ ret = media_device_register_entity(vdev->v4l2_dev->mdev,
+ &vdev->entity);
+ if (ret < 0) {
+ printk(KERN_WARNING
+ "%s: media_device_register_entity failed\n",
+ __func__);
+ return ret;
+ }
+ }
+
+ vdev->intf_devnode = media_devnode_create(vdev->v4l2_dev->mdev,
+ intf_type,
+ 0, VIDEO_MAJOR,
+ vdev->minor);
+ if (!vdev->intf_devnode) {
+ media_device_unregister_entity(&vdev->entity);
+ return -ENOMEM;
+ }
+
+ if (vdev->entity.function != MEDIA_ENT_F_UNKNOWN) {
+ struct media_link *link;
+
+ link = media_create_intf_link(&vdev->entity,
+ &vdev->intf_devnode->intf,
+ MEDIA_LNK_FL_ENABLED);
+ if (!link) {
+ media_devnode_remove(vdev->intf_devnode);
+ media_device_unregister_entity(&vdev->entity);
+ return -ENOMEM;
+ }
+ }
+
+ /* FIXME: how to create the other interface links? */
+
+#endif
+ return 0;
+}
+
/**
* __video_register_device - register video4linux devices
* @vdev: video device structure we want to register
@@ -918,22 +1006,9 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
/* Increase v4l2_device refcount */
v4l2_device_get(vdev->v4l2_dev);
-#if defined(CONFIG_MEDIA_CONTROLLER)
/* Part 5: Register the entity. */
- if (vdev->v4l2_dev->mdev &&
- vdev->vfl_type != VFL_TYPE_SUBDEV) {
- vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
- vdev->entity.name = vdev->name;
- vdev->entity.info.dev.major = VIDEO_MAJOR;
- vdev->entity.info.dev.minor = vdev->minor;
- ret = media_device_register_entity(vdev->v4l2_dev->mdev,
- &vdev->entity);
- if (ret < 0)
- printk(KERN_WARNING
- "%s: media_device_register_entity failed\n",
- __func__);
- }
-#endif
+ ret = video_register_media_controller(vdev, type);
+
/* Part 6: Activate this minor. The char device can now be used. */
set_bit(V4L2_FL_REGISTERED, &vdev->flags);
diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c
index 5b0a30b9252b..06fa5f1b2cff 100644
--- a/drivers/media/v4l2-core/v4l2-device.c
+++ b/drivers/media/v4l2-core/v4l2-device.c
@@ -118,11 +118,20 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
if (sd->flags & V4L2_SUBDEV_FL_IS_I2C) {
struct i2c_client *client = v4l2_get_subdevdata(sd);
- /* We need to unregister the i2c client explicitly.
- We cannot rely on i2c_del_adapter to always
- unregister clients for us, since if the i2c bus
- is a platform bus, then it is never deleted. */
- if (client)
+ /*
+ * We need to unregister the i2c client
+ * explicitly. We cannot rely on
+ * i2c_del_adapter to always unregister
+ * clients for us, since if the i2c bus is a
+ * platform bus, then it is never deleted.
+ *
+ * Device tree or ACPI based devices must not
+ * be unregistered as they have not been
+ * registered by us, and would not be
+ * re-created by just probing the V4L2 driver.
+ */
+ if (client &&
+ !client->dev.of_node && !client->dev.fwnode)
i2c_unregister_device(client);
continue;
}
@@ -131,7 +140,7 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
if (sd->flags & V4L2_SUBDEV_FL_IS_SPI) {
struct spi_device *spi = v4l2_get_subdevdata(sd);
- if (spi)
+ if (spi && !spi->dev.of_node && !spi->dev.fwnode)
spi_unregister_device(spi);
continue;
}
@@ -171,26 +180,26 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
return -ENODEV;
sd->v4l2_dev = v4l2_dev;
- if (sd->internal_ops && sd->internal_ops->registered) {
- err = sd->internal_ops->registered(sd);
- if (err)
- goto error_module;
- }
-
/* This just returns 0 if either of the two args is NULL */
err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler, NULL);
if (err)
- goto error_unregister;
+ goto error_module;
#if defined(CONFIG_MEDIA_CONTROLLER)
/* Register the entity. */
if (v4l2_dev->mdev) {
err = media_device_register_entity(v4l2_dev->mdev, entity);
if (err < 0)
- goto error_unregister;
+ goto error_module;
}
#endif
+ if (sd->internal_ops && sd->internal_ops->registered) {
+ err = sd->internal_ops->registered(sd);
+ if (err)
+ goto error_unregister;
+ }
+
spin_lock(&v4l2_dev->lock);
list_add_tail(&sd->list, &v4l2_dev->subdevs);
spin_unlock(&v4l2_dev->lock);
@@ -198,8 +207,9 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
return 0;
error_unregister:
- if (sd->internal_ops && sd->internal_ops->unregistered)
- sd->internal_ops->unregistered(sd);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ media_device_unregister_entity(entity);
+#endif
error_module:
if (!sd->owner_v4l2_dev)
module_put(sd->owner);
@@ -249,6 +259,19 @@ int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
#if defined(CONFIG_MEDIA_CONTROLLER)
sd->entity.info.dev.major = VIDEO_MAJOR;
sd->entity.info.dev.minor = vdev->minor;
+
+ /* Interface is created by __video_register_device() */
+ if (vdev->v4l2_dev->mdev) {
+ struct media_link *link;
+
+ link = media_create_intf_link(&sd->entity,
+ &vdev->intf_devnode->intf,
+ MEDIA_LNK_FL_ENABLED);
+ if (!link) {
+ err = -ENOMEM;
+ goto clean_up;
+ }
+ }
#endif
sd->devnode = vdev;
}
@@ -285,7 +308,10 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
#if defined(CONFIG_MEDIA_CONTROLLER)
if (v4l2_dev->mdev) {
- media_entity_remove_links(&sd->entity);
+ /*
+ * No need to explicitly remove links, as both pads and
+ * links are removed by the function below, in the right order
+ */
media_device_unregister_entity(&sd->entity);
}
#endif
diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c
index 6a83d6191684..ec258b73001a 100644
--- a/drivers/media/v4l2-core/v4l2-dv-timings.c
+++ b/drivers/media/v4l2-core/v4l2-dv-timings.c
@@ -209,8 +209,13 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t,
if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap,
fnc, fnc_handle) &&
v4l2_match_dv_timings(t, v4l2_dv_timings_presets + i,
- pclock_delta)) {
+ pclock_delta, false)) {
+ u32 flags = t->bt.flags & V4L2_DV_FL_REDUCED_FPS;
+
*t = v4l2_dv_timings_presets[i];
+ if (can_reduce_fps(&t->bt))
+ t->bt.flags |= flags;
+
return true;
}
}
@@ -223,12 +228,14 @@ EXPORT_SYMBOL_GPL(v4l2_find_dv_timings_cap);
* @t1 - compare this v4l2_dv_timings struct...
* @t2 - with this struct.
* @pclock_delta - the allowed pixelclock deviation.
+ * @match_reduced_fps - if true, then fail if V4L2_DV_FL_REDUCED_FPS does not
+ * match.
*
* Compare t1 with t2 with a given margin of error for the pixelclock.
*/
bool v4l2_match_dv_timings(const struct v4l2_dv_timings *t1,
const struct v4l2_dv_timings *t2,
- unsigned pclock_delta)
+ unsigned pclock_delta, bool match_reduced_fps)
{
if (t1->type != t2->type || t1->type != V4L2_DV_BT_656_1120)
return false;
@@ -239,9 +246,14 @@ bool v4l2_match_dv_timings(const struct v4l2_dv_timings *t1,
t1->bt.pixelclock >= t2->bt.pixelclock - pclock_delta &&
t1->bt.pixelclock <= t2->bt.pixelclock + pclock_delta &&
t1->bt.hfrontporch == t2->bt.hfrontporch &&
+ t1->bt.hsync == t2->bt.hsync &&
+ t1->bt.hbackporch == t2->bt.hbackporch &&
t1->bt.vfrontporch == t2->bt.vfrontporch &&
t1->bt.vsync == t2->bt.vsync &&
t1->bt.vbackporch == t2->bt.vbackporch &&
+ (!match_reduced_fps ||
+ (t1->bt.flags & V4L2_DV_FL_REDUCED_FPS) ==
+ (t2->bt.flags & V4L2_DV_FL_REDUCED_FPS)) &&
(!t1->bt.interlaced ||
(t1->bt.il_vfrontporch == t2->bt.il_vfrontporch &&
t1->bt.il_vsync == t2->bt.il_vsync &&
diff --git a/drivers/media/v4l2-core/v4l2-flash-led-class.c b/drivers/media/v4l2-core/v4l2-flash-led-class.c
index 5bdfb8d5263a..fc5ff8b215f9 100644
--- a/drivers/media/v4l2-core/v4l2-flash-led-class.c
+++ b/drivers/media/v4l2-core/v4l2-flash-led-class.c
@@ -107,10 +107,10 @@ static void v4l2_flash_set_led_brightness(struct v4l2_flash *v4l2_flash,
if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
return;
- led_set_brightness(&v4l2_flash->fled_cdev->led_cdev,
+ led_set_brightness_sync(&v4l2_flash->fled_cdev->led_cdev,
brightness);
} else {
- led_set_brightness(&v4l2_flash->iled_cdev->led_cdev,
+ led_set_brightness_sync(&v4l2_flash->iled_cdev->led_cdev,
brightness);
}
}
@@ -206,11 +206,11 @@ static int v4l2_flash_s_ctrl(struct v4l2_ctrl *c)
case V4L2_CID_FLASH_LED_MODE:
switch (c->val) {
case V4L2_FLASH_LED_MODE_NONE:
- led_set_brightness(led_cdev, LED_OFF);
+ led_set_brightness_sync(led_cdev, LED_OFF);
return led_set_flash_strobe(fled_cdev, false);
case V4L2_FLASH_LED_MODE_FLASH:
/* Turn the torch LED off */
- led_set_brightness(led_cdev, LED_OFF);
+ led_set_brightness_sync(led_cdev, LED_OFF);
if (ctrls[STROBE_SOURCE]) {
external_strobe = (ctrls[STROBE_SOURCE]->val ==
V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
@@ -651,11 +651,11 @@ struct v4l2_flash *v4l2_flash_init(
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
strlcpy(sd->name, config->dev_name, sizeof(sd->name));
- ret = media_entity_init(&sd->entity, 0, NULL, 0);
+ ret = media_entity_pads_init(&sd->entity, 0, NULL);
if (ret < 0)
return ERR_PTR(ret);
- sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
+ sd->entity.function = MEDIA_ENT_F_FLASH;
ret = v4l2_flash_init_controls(v4l2_flash, config);
if (ret < 0)
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 7486af2c8ae4..8a018c6dd16a 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -565,8 +565,8 @@ static void v4l_print_ext_controls(const void *arg, bool write_only)
const struct v4l2_ext_controls *p = arg;
int i;
- pr_cont("class=0x%x, count=%d, error_idx=%d",
- p->ctrl_class, p->count, p->error_idx);
+ pr_cont("which=0x%x, count=%d, error_idx=%d",
+ p->which, p->count, p->error_idx);
for (i = 0; i < p->count; i++) {
if (!p->controls[i].size)
pr_cont(", id/val=0x%x/0x%x",
@@ -902,13 +902,13 @@ static int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv)
Only when passed in through VIDIOC_G_CTRL and VIDIOC_S_CTRL
is it allowed for backwards compatibility.
*/
- if (!allow_priv && c->ctrl_class == V4L2_CID_PRIVATE_BASE)
+ if (!allow_priv && c->which == V4L2_CID_PRIVATE_BASE)
return 0;
- if (c->ctrl_class == 0)
+ if (!c->which)
return 1;
/* Check that all controls are from the same control class. */
for (i = 0; i < c->count; i++) {
- if (V4L2_CTRL_ID2CLASS(c->controls[i].id) != c->ctrl_class) {
+ if (V4L2_CTRL_ID2WHICH(c->controls[i].id) != c->which) {
c->error_idx = i;
return 0;
}
@@ -1969,7 +1969,7 @@ static int v4l_g_ctrl(const struct v4l2_ioctl_ops *ops,
if (ops->vidioc_g_ext_ctrls == NULL)
return -ENOTTY;
- ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
+ ctrls.which = V4L2_CTRL_ID2WHICH(p->id);
ctrls.count = 1;
ctrls.controls = &ctrl;
ctrl.id = p->id;
@@ -2003,7 +2003,7 @@ static int v4l_s_ctrl(const struct v4l2_ioctl_ops *ops,
if (ops->vidioc_s_ext_ctrls == NULL)
return -ENOTTY;
- ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
+ ctrls.which = V4L2_CTRL_ID2WHICH(p->id);
ctrls.count = 1;
ctrls.controls = &ctrl;
ctrl.id = p->id;
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index 83615b8fb46a..d63083803144 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -526,7 +526,7 @@ static int
v4l2_subdev_link_validate_get_format(struct media_pad *pad,
struct v4l2_subdev_format *fmt)
{
- if (media_entity_type(pad->entity) == MEDIA_ENT_T_V4L2_SUBDEV) {
+ if (is_media_entity_v4l2_subdev(pad->entity)) {
struct v4l2_subdev *sd =
media_entity_to_v4l2_subdev(pad->entity);
@@ -535,9 +535,9 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad,
return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
}
- WARN(pad->entity->type != MEDIA_ENT_T_DEVNODE_V4L,
+ WARN(pad->entity->function != MEDIA_ENT_F_IO_V4L,
"Driver bug! Wrong media entity type 0x%08x, entity %s\n",
- pad->entity->type, pad->entity->name);
+ pad->entity->function, pad->entity->name);
return -EINVAL;
}
@@ -584,7 +584,7 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops)
sd->host_priv = NULL;
#if defined(CONFIG_MEDIA_CONTROLLER)
sd->entity.name = sd->name;
- sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ sd->entity.function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
#endif
}
EXPORT_SYMBOL(v4l2_subdev_init);
diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index 33bdd81065e8..c5d49d7a0d76 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -28,11 +28,161 @@
#include <trace/events/vb2.h>
-#include "videobuf2-internal.h"
+static int debug;
+module_param(debug, int, 0644);
-int vb2_debug;
-EXPORT_SYMBOL_GPL(vb2_debug);
-module_param_named(debug, vb2_debug, int, 0644);
+#define dprintk(level, fmt, arg...) \
+ do { \
+ if (debug >= level) \
+ pr_info("vb2-core: %s: " fmt, __func__, ## arg); \
+ } while (0)
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+
+/*
+ * If advanced debugging is on, then count how often each op is called
+ * successfully, which can either be per-buffer or per-queue.
+ *
+ * This makes it easy to check that the 'init' and 'cleanup'
+ * (and variations thereof) stay balanced.
+ */
+
+#define log_memop(vb, op) \
+ dprintk(2, "call_memop(%p, %d, %s)%s\n", \
+ (vb)->vb2_queue, (vb)->index, #op, \
+ (vb)->vb2_queue->mem_ops->op ? "" : " (nop)")
+
+#define call_memop(vb, op, args...) \
+({ \
+ struct vb2_queue *_q = (vb)->vb2_queue; \
+ int err; \
+ \
+ log_memop(vb, op); \
+ err = _q->mem_ops->op ? _q->mem_ops->op(args) : 0; \
+ if (!err) \
+ (vb)->cnt_mem_ ## op++; \
+ err; \
+})
+
+#define call_ptr_memop(vb, op, args...) \
+({ \
+ struct vb2_queue *_q = (vb)->vb2_queue; \
+ void *ptr; \
+ \
+ log_memop(vb, op); \
+ ptr = _q->mem_ops->op ? _q->mem_ops->op(args) : NULL; \
+ if (!IS_ERR_OR_NULL(ptr)) \
+ (vb)->cnt_mem_ ## op++; \
+ ptr; \
+})
+
+#define call_void_memop(vb, op, args...) \
+({ \
+ struct vb2_queue *_q = (vb)->vb2_queue; \
+ \
+ log_memop(vb, op); \
+ if (_q->mem_ops->op) \
+ _q->mem_ops->op(args); \
+ (vb)->cnt_mem_ ## op++; \
+})
+
+#define log_qop(q, op) \
+ dprintk(2, "call_qop(%p, %s)%s\n", q, #op, \
+ (q)->ops->op ? "" : " (nop)")
+
+#define call_qop(q, op, args...) \
+({ \
+ int err; \
+ \
+ log_qop(q, op); \
+ err = (q)->ops->op ? (q)->ops->op(args) : 0; \
+ if (!err) \
+ (q)->cnt_ ## op++; \
+ err; \
+})
+
+#define call_void_qop(q, op, args...) \
+({ \
+ log_qop(q, op); \
+ if ((q)->ops->op) \
+ (q)->ops->op(args); \
+ (q)->cnt_ ## op++; \
+})
+
+#define log_vb_qop(vb, op, args...) \
+ dprintk(2, "call_vb_qop(%p, %d, %s)%s\n", \
+ (vb)->vb2_queue, (vb)->index, #op, \
+ (vb)->vb2_queue->ops->op ? "" : " (nop)")
+
+#define call_vb_qop(vb, op, args...) \
+({ \
+ int err; \
+ \
+ log_vb_qop(vb, op); \
+ err = (vb)->vb2_queue->ops->op ? \
+ (vb)->vb2_queue->ops->op(args) : 0; \
+ if (!err) \
+ (vb)->cnt_ ## op++; \
+ err; \
+})
+
+#define call_void_vb_qop(vb, op, args...) \
+({ \
+ log_vb_qop(vb, op); \
+ if ((vb)->vb2_queue->ops->op) \
+ (vb)->vb2_queue->ops->op(args); \
+ (vb)->cnt_ ## op++; \
+})
+
+#else
+
+#define call_memop(vb, op, args...) \
+ ((vb)->vb2_queue->mem_ops->op ? \
+ (vb)->vb2_queue->mem_ops->op(args) : 0)
+
+#define call_ptr_memop(vb, op, args...) \
+ ((vb)->vb2_queue->mem_ops->op ? \
+ (vb)->vb2_queue->mem_ops->op(args) : NULL)
+
+#define call_void_memop(vb, op, args...) \
+ do { \
+ if ((vb)->vb2_queue->mem_ops->op) \
+ (vb)->vb2_queue->mem_ops->op(args); \
+ } while (0)
+
+#define call_qop(q, op, args...) \
+ ((q)->ops->op ? (q)->ops->op(args) : 0)
+
+#define call_void_qop(q, op, args...) \
+ do { \
+ if ((q)->ops->op) \
+ (q)->ops->op(args); \
+ } while (0)
+
+#define call_vb_qop(vb, op, args...) \
+ ((vb)->vb2_queue->ops->op ? (vb)->vb2_queue->ops->op(args) : 0)
+
+#define call_void_vb_qop(vb, op, args...) \
+ do { \
+ if ((vb)->vb2_queue->ops->op) \
+ (vb)->vb2_queue->ops->op(args); \
+ } while (0)
+
+#endif
+
+#define call_bufop(q, op, args...) \
+({ \
+ int ret = 0; \
+ if (q && q->buf_ops && q->buf_ops->op) \
+ ret = q->buf_ops->op(args); \
+ ret; \
+})
+
+#define call_void_bufop(q, op, args...) \
+({ \
+ if (q && q->buf_ops && q->buf_ops->op) \
+ q->buf_ops->op(args); \
+})
static void __vb2_queue_cancel(struct vb2_queue *q);
static void __enqueue_in_driver(struct vb2_buffer *vb);
@@ -53,7 +203,7 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
* NOTE: mmapped areas should be page aligned
*/
for (plane = 0; plane < vb->num_planes; ++plane) {
- unsigned long size = PAGE_ALIGN(q->plane_sizes[plane]);
+ unsigned long size = PAGE_ALIGN(vb->planes[plane].length);
mem_priv = call_ptr_memop(vb, alloc, q->alloc_ctx[plane],
size, dma_dir, q->gfp_flags);
@@ -62,7 +212,6 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
/* Associate allocator private data with this plane */
vb->planes[plane].mem_priv = mem_priv;
- vb->planes[plane].length = q->plane_sizes[plane];
}
return 0;
@@ -137,57 +286,30 @@ static void __vb2_buf_dmabuf_put(struct vb2_buffer *vb)
}
/**
- * __setup_lengths() - setup initial lengths for every plane in
- * every buffer on the queue
- */
-static void __setup_lengths(struct vb2_queue *q, unsigned int n)
-{
- unsigned int buffer, plane;
- struct vb2_buffer *vb;
-
- for (buffer = q->num_buffers; buffer < q->num_buffers + n; ++buffer) {
- vb = q->bufs[buffer];
- if (!vb)
- continue;
-
- for (plane = 0; plane < vb->num_planes; ++plane)
- vb->planes[plane].length = q->plane_sizes[plane];
- }
-}
-
-/**
* __setup_offsets() - setup unique offsets ("cookies") for every plane in
- * every buffer on the queue
+ * the buffer.
*/
-static void __setup_offsets(struct vb2_queue *q, unsigned int n)
+static void __setup_offsets(struct vb2_buffer *vb)
{
- unsigned int buffer, plane;
- struct vb2_buffer *vb;
- unsigned long off;
+ struct vb2_queue *q = vb->vb2_queue;
+ unsigned int plane;
+ unsigned long off = 0;
+
+ if (vb->index) {
+ struct vb2_buffer *prev = q->bufs[vb->index - 1];
+ struct vb2_plane *p = &prev->planes[prev->num_planes - 1];
- if (q->num_buffers) {
- struct vb2_plane *p;
- vb = q->bufs[q->num_buffers - 1];
- p = &vb->planes[vb->num_planes - 1];
off = PAGE_ALIGN(p->m.offset + p->length);
- } else {
- off = 0;
}
- for (buffer = q->num_buffers; buffer < q->num_buffers + n; ++buffer) {
- vb = q->bufs[buffer];
- if (!vb)
- continue;
-
- for (plane = 0; plane < vb->num_planes; ++plane) {
- vb->planes[plane].m.offset = off;
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ vb->planes[plane].m.offset = off;
- dprintk(3, "buffer %d, plane %d offset 0x%08lx\n",
- buffer, plane, off);
+ dprintk(3, "buffer %d, plane %d offset 0x%08lx\n",
+ vb->index, plane, off);
- off += vb->planes[plane].length;
- off = PAGE_ALIGN(off);
- }
+ off += vb->planes[plane].length;
+ off = PAGE_ALIGN(off);
}
}
@@ -199,9 +321,10 @@ static void __setup_offsets(struct vb2_queue *q, unsigned int n)
* Returns the number of buffers successfully allocated.
*/
static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory,
- unsigned int num_buffers, unsigned int num_planes)
+ unsigned int num_buffers, unsigned int num_planes,
+ const unsigned plane_sizes[VB2_MAX_PLANES])
{
- unsigned int buffer;
+ unsigned int buffer, plane;
struct vb2_buffer *vb;
int ret;
@@ -219,6 +342,11 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory,
vb->index = q->num_buffers + buffer;
vb->type = q->type;
vb->memory = memory;
+ for (plane = 0; plane < num_planes; ++plane) {
+ vb->planes[plane].length = plane_sizes[plane];
+ vb->planes[plane].min_length = plane_sizes[plane];
+ }
+ q->bufs[vb->index] = vb;
/* Allocate video buffer memory for the MMAP type */
if (memory == VB2_MEMORY_MMAP) {
@@ -226,9 +354,11 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory,
if (ret) {
dprintk(1, "failed allocating memory for "
"buffer %d\n", buffer);
+ q->bufs[vb->index] = NULL;
kfree(vb);
break;
}
+ __setup_offsets(vb);
/*
* Call the driver-provided buffer initialization
* callback, if given. An error in initialization
@@ -239,18 +369,13 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory,
dprintk(1, "buffer %d %p initialization"
" failed\n", buffer, vb);
__vb2_buf_mem_free(vb);
+ q->bufs[vb->index] = NULL;
kfree(vb);
break;
}
}
-
- q->bufs[q->num_buffers + buffer] = vb;
}
- __setup_lengths(q, buffer);
- if (memory == VB2_MEMORY_MMAP)
- __setup_offsets(q, buffer);
-
dprintk(1, "allocated %d buffers, %d plane(s) each\n",
buffer, num_planes);
@@ -330,7 +455,7 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
bool unbalanced = q->cnt_start_streaming != q->cnt_stop_streaming ||
q->cnt_wait_prepare != q->cnt_wait_finish;
- if (unbalanced || vb2_debug) {
+ if (unbalanced || debug) {
pr_info("vb2: counters for queue %p:%s\n", q,
unbalanced ? " UNBALANCED!" : "");
pr_info("vb2: setup: %u start_streaming: %u stop_streaming: %u\n",
@@ -356,7 +481,7 @@ static int __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
vb->cnt_buf_prepare != vb->cnt_buf_finish ||
vb->cnt_buf_init != vb->cnt_buf_cleanup;
- if (unbalanced || vb2_debug) {
+ if (unbalanced || debug) {
pr_info("vb2: counters for queue %p, buffer %d:%s\n",
q, buffer, unbalanced ? " UNBALANCED!" : "");
pr_info("vb2: buf_init: %u buf_cleanup: %u buf_prepare: %u buf_finish: %u\n",
@@ -442,13 +567,10 @@ static bool __buffers_in_use(struct vb2_queue *q)
* Should be called from vidioc_querybuf ioctl handler in driver.
* The passed buffer should have been verified.
* This function fills the relevant information for the userspace.
- *
- * The return values from this function are intended to be directly returned
- * from vidioc_querybuf handler in driver.
*/
-int vb2_core_querybuf(struct vb2_queue *q, unsigned int index, void *pb)
+void vb2_core_querybuf(struct vb2_queue *q, unsigned int index, void *pb)
{
- return call_bufop(q, fill_user_buffer, q->bufs[index], pb);
+ call_void_bufop(q, fill_user_buffer, q->bufs[index], pb);
}
EXPORT_SYMBOL_GPL(vb2_core_querybuf);
@@ -570,6 +692,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
unsigned int *count)
{
unsigned int num_buffers, allocated_buffers, num_planes = 0;
+ unsigned plane_sizes[VB2_MAX_PLANES] = { };
int ret;
if (q->streaming) {
@@ -613,7 +736,6 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
*/
num_buffers = min_t(unsigned int, *count, VB2_MAX_FRAME);
num_buffers = max_t(unsigned int, num_buffers, q->min_buffers_needed);
- memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
q->memory = memory;
@@ -621,14 +743,14 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
* Ask the driver how many buffers and planes per buffer it requires.
* Driver also sets the size and allocator context for each plane.
*/
- ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes,
- q->plane_sizes, q->alloc_ctx);
+ ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes,
+ plane_sizes, q->alloc_ctx);
if (ret)
return ret;
/* Finally, allocate buffers and video memory */
allocated_buffers =
- __vb2_queue_alloc(q, memory, num_buffers, num_planes);
+ __vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes);
if (allocated_buffers == 0) {
dprintk(1, "memory allocation failed\n");
return -ENOMEM;
@@ -646,9 +768,16 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
*/
if (!ret && allocated_buffers < num_buffers) {
num_buffers = allocated_buffers;
+ /*
+ * num_planes is set by the previous queue_setup(), but since it
+ * signals to queue_setup() whether it is called from create_bufs()
+ * vs reqbufs() we zero it here to signal that queue_setup() is
+ * called for the reqbufs() case.
+ */
+ num_planes = 0;
- ret = call_qop(q, queue_setup, q, NULL, &num_buffers,
- &num_planes, q->plane_sizes, q->alloc_ctx);
+ ret = call_qop(q, queue_setup, q, &num_buffers,
+ &num_planes, plane_sizes, q->alloc_ctx);
if (!ret && allocated_buffers < num_buffers)
ret = -ENOMEM;
@@ -701,9 +830,11 @@ EXPORT_SYMBOL_GPL(vb2_core_reqbufs);
* from vidioc_create_bufs handler in driver.
*/
int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
- unsigned int *count, const void *parg)
+ unsigned int *count, unsigned requested_planes,
+ const unsigned requested_sizes[])
{
unsigned int num_planes = 0, num_buffers, allocated_buffers;
+ unsigned plane_sizes[VB2_MAX_PLANES] = { };
int ret;
if (q->num_buffers == VB2_MAX_FRAME) {
@@ -712,7 +843,6 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
}
if (!q->num_buffers) {
- memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
q->memory = memory;
q->waiting_for_buffers = !q->is_output;
@@ -720,18 +850,23 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
num_buffers = min(*count, VB2_MAX_FRAME - q->num_buffers);
+ if (requested_planes && requested_sizes) {
+ num_planes = requested_planes;
+ memcpy(plane_sizes, requested_sizes, sizeof(plane_sizes));
+ }
+
/*
* Ask the driver, whether the requested number of buffers, planes per
* buffer and their sizes are acceptable
*/
- ret = call_qop(q, queue_setup, q, parg, &num_buffers,
- &num_planes, q->plane_sizes, q->alloc_ctx);
+ ret = call_qop(q, queue_setup, q, &num_buffers,
+ &num_planes, plane_sizes, q->alloc_ctx);
if (ret)
return ret;
/* Finally, allocate buffers and video memory */
allocated_buffers = __vb2_queue_alloc(q, memory, num_buffers,
- num_planes);
+ num_planes, plane_sizes);
if (allocated_buffers == 0) {
dprintk(1, "memory allocation failed\n");
return -ENOMEM;
@@ -747,8 +882,8 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
* q->num_buffers contains the total number of buffers, that the
* queue driver has set up
*/
- ret = call_qop(q, queue_setup, q, parg, &num_buffers,
- &num_planes, q->plane_sizes, q->alloc_ctx);
+ ret = call_qop(q, queue_setup, q, &num_buffers,
+ &num_planes, plane_sizes, q->alloc_ctx);
if (!ret && allocated_buffers < num_buffers)
ret = -ENOMEM;
@@ -964,11 +1099,12 @@ static int __qbuf_userptr(struct vb2_buffer *vb, const void *pb)
"reacquiring memory\n", plane);
/* Check if the provided plane buffer is large enough */
- if (planes[plane].length < q->plane_sizes[plane]) {
+ if (planes[plane].length < vb->planes[plane].min_length) {
dprintk(1, "provided buffer size %u is less than "
"setup size %u for plane %d\n",
planes[plane].length,
- q->plane_sizes[plane], plane);
+ vb->planes[plane].min_length,
+ plane);
ret = -EINVAL;
goto err;
}
@@ -1081,7 +1217,7 @@ static int __qbuf_dmabuf(struct vb2_buffer *vb, const void *pb)
if (planes[plane].length == 0)
planes[plane].length = dbuf->size;
- if (planes[plane].length < q->plane_sizes[plane]) {
+ if (planes[plane].length < vb->planes[plane].min_length) {
dprintk(1, "invalid dmabuf length for plane %d\n",
plane);
ret = -EINVAL;
@@ -1263,9 +1399,7 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb)
return ret;
/* Fill buffer information for the userspace */
- ret = call_bufop(q, fill_user_buffer, vb, pb);
- if (ret)
- return ret;
+ call_void_bufop(q, fill_user_buffer, vb, pb);
dprintk(1, "prepare of buffer %d succeeded\n", vb->index);
@@ -1386,7 +1520,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb)
q->waiting_for_buffers = false;
vb->state = VB2_BUF_STATE_QUEUED;
- call_bufop(q, set_timestamp, vb, pb);
+ call_void_bufop(q, copy_timestamp, vb, pb);
trace_vb2_qbuf(q, vb);
@@ -1398,9 +1532,7 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb)
__enqueue_in_driver(vb);
/* Fill buffer information for the userspace */
- ret = call_bufop(q, fill_user_buffer, vb, pb);
- if (ret)
- return ret;
+ call_void_bufop(q, fill_user_buffer, vb, pb);
/*
* If streamon has been called, and we haven't yet called
@@ -1623,9 +1755,7 @@ int vb2_core_dqbuf(struct vb2_queue *q, void *pb, bool nonblocking)
call_void_vb_qop(vb, buf_finish, vb);
/* Fill buffer information for the userspace */
- ret = call_bufop(q, fill_user_buffer, vb, pb);
- if (ret)
- return ret;
+ call_void_bufop(q, fill_user_buffer, vb, pb);
/* Remove from videobuf queue */
list_del(&vb->queued_entry);
@@ -2073,6 +2203,8 @@ int vb2_core_queue_init(struct vb2_queue *q)
}
EXPORT_SYMBOL_GPL(vb2_core_queue_init);
+static int __vb2_init_fileio(struct vb2_queue *q, int read);
+static int __vb2_cleanup_fileio(struct vb2_queue *q);
/**
* vb2_core_queue_release() - stop streaming, release the queue and free memory
* @q: videobuf2 queue
@@ -2083,6 +2215,7 @@ EXPORT_SYMBOL_GPL(vb2_core_queue_init);
*/
void vb2_core_queue_release(struct vb2_queue *q)
{
+ __vb2_cleanup_fileio(q);
__vb2_queue_cancel(q);
mutex_lock(&q->mmap_lock);
__vb2_queue_free(q, q->num_buffers);
@@ -2090,6 +2223,619 @@ void vb2_core_queue_release(struct vb2_queue *q)
}
EXPORT_SYMBOL_GPL(vb2_core_queue_release);
-MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2");
+/**
+ * vb2_core_poll() - implements poll userspace operation
+ * @q: videobuf2 queue
+ * @file: file argument passed to the poll file operation handler
+ * @wait: wait argument passed to the poll file operation handler
+ *
+ * This function implements poll file operation handler for a driver.
+ * For CAPTURE queues, if a buffer is ready to be dequeued, the userspace will
+ * be informed that the file descriptor of a video device is available for
+ * reading.
+ * For OUTPUT queues, if a buffer is ready to be dequeued, the file descriptor
+ * will be reported as available for writing.
+ *
+ * The return values from this function are intended to be directly returned
+ * from poll handler in driver.
+ */
+unsigned int vb2_core_poll(struct vb2_queue *q, struct file *file,
+ poll_table *wait)
+{
+ unsigned long req_events = poll_requested_events(wait);
+ struct vb2_buffer *vb = NULL;
+ unsigned long flags;
+
+ if (!q->is_output && !(req_events & (POLLIN | POLLRDNORM)))
+ return 0;
+ if (q->is_output && !(req_events & (POLLOUT | POLLWRNORM)))
+ return 0;
+
+ /*
+ * Start file I/O emulator only if streaming API has not been used yet.
+ */
+ if (q->num_buffers == 0 && !vb2_fileio_is_active(q)) {
+ if (!q->is_output && (q->io_modes & VB2_READ) &&
+ (req_events & (POLLIN | POLLRDNORM))) {
+ if (__vb2_init_fileio(q, 1))
+ return POLLERR;
+ }
+ if (q->is_output && (q->io_modes & VB2_WRITE) &&
+ (req_events & (POLLOUT | POLLWRNORM))) {
+ if (__vb2_init_fileio(q, 0))
+ return POLLERR;
+ /*
+ * Write to OUTPUT queue can be done immediately.
+ */
+ return POLLOUT | POLLWRNORM;
+ }
+ }
+
+ /*
+ * There is nothing to wait for if the queue isn't streaming, or if the
+ * error flag is set.
+ */
+ if (!vb2_is_streaming(q) || q->error)
+ return POLLERR;
+
+ /*
+ * For output streams you can call write() as long as there are fewer
+ * buffers queued than there are buffers available.
+ */
+ if (q->is_output && q->fileio && q->queued_count < q->num_buffers)
+ return POLLOUT | POLLWRNORM;
+
+ if (list_empty(&q->done_list)) {
+ /*
+ * If the last buffer was dequeued from a capture queue,
+ * return immediately. DQBUF will return -EPIPE.
+ */
+ if (q->last_buffer_dequeued)
+ return POLLIN | POLLRDNORM;
+
+ poll_wait(file, &q->done_wq, wait);
+ }
+
+ /*
+ * Take first buffer available for dequeuing.
+ */
+ spin_lock_irqsave(&q->done_lock, flags);
+ if (!list_empty(&q->done_list))
+ vb = list_first_entry(&q->done_list, struct vb2_buffer,
+ done_entry);
+ spin_unlock_irqrestore(&q->done_lock, flags);
+
+ if (vb && (vb->state == VB2_BUF_STATE_DONE
+ || vb->state == VB2_BUF_STATE_ERROR)) {
+ return (q->is_output) ?
+ POLLOUT | POLLWRNORM :
+ POLLIN | POLLRDNORM;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_core_poll);
+
+/**
+ * struct vb2_fileio_buf - buffer context used by file io emulator
+ *
+ * vb2 provides a compatibility layer and emulator of file io (read and
+ * write) calls on top of streaming API. This structure is used for
+ * tracking context related to the buffers.
+ */
+struct vb2_fileio_buf {
+ void *vaddr;
+ unsigned int size;
+ unsigned int pos;
+ unsigned int queued:1;
+};
+
+/**
+ * struct vb2_fileio_data - queue context used by file io emulator
+ *
+ * @cur_index: the index of the buffer currently being read from or
+ * written to. If equal to q->num_buffers then a new buffer
+ * must be dequeued.
+ * @initial_index: in the read() case all buffers are queued up immediately
+ * in __vb2_init_fileio() and __vb2_perform_fileio() just cycles
+ * buffers. However, in the write() case no buffers are initially
+ * queued, instead whenever a buffer is full it is queued up by
+ * __vb2_perform_fileio(). Only once all available buffers have
+ * been queued up will __vb2_perform_fileio() start to dequeue
+ * buffers. This means that initially __vb2_perform_fileio()
+ * needs to know what buffer index to use when it is queuing up
+ * the buffers for the first time. That initial index is stored
+ * in this field. Once it is equal to q->num_buffers all
+ * available buffers have been queued and __vb2_perform_fileio()
+ * should start the normal dequeue/queue cycle.
+ *
+ * vb2 provides a compatibility layer and emulator of file io (read and
+ * write) calls on top of streaming API. For proper operation it required
+ * this structure to save the driver state between each call of the read
+ * or write function.
+ */
+struct vb2_fileio_data {
+ unsigned int count;
+ unsigned int type;
+ unsigned int memory;
+ struct vb2_buffer *b;
+ struct vb2_fileio_buf bufs[VB2_MAX_FRAME];
+ unsigned int cur_index;
+ unsigned int initial_index;
+ unsigned int q_count;
+ unsigned int dq_count;
+ unsigned read_once:1;
+ unsigned write_immediately:1;
+};
+
+/**
+ * __vb2_init_fileio() - initialize file io emulator
+ * @q: videobuf2 queue
+ * @read: mode selector (1 means read, 0 means write)
+ */
+static int __vb2_init_fileio(struct vb2_queue *q, int read)
+{
+ struct vb2_fileio_data *fileio;
+ int i, ret;
+ unsigned int count = 0;
+
+ /*
+ * Sanity check
+ */
+ if (WARN_ON((read && !(q->io_modes & VB2_READ)) ||
+ (!read && !(q->io_modes & VB2_WRITE))))
+ return -EINVAL;
+
+ /*
+ * Check if device supports mapping buffers to kernel virtual space.
+ */
+ if (!q->mem_ops->vaddr)
+ return -EBUSY;
+
+ /*
+ * Check if streaming api has not been already activated.
+ */
+ if (q->streaming || q->num_buffers > 0)
+ return -EBUSY;
+
+ /*
+ * Start with count 1, driver can increase it in queue_setup()
+ */
+ count = 1;
+
+ dprintk(3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n",
+ (read) ? "read" : "write", count, q->fileio_read_once,
+ q->fileio_write_immediately);
+
+ fileio = kzalloc(sizeof(*fileio), GFP_KERNEL);
+ if (fileio == NULL)
+ return -ENOMEM;
+
+ fileio->b = kzalloc(q->buf_struct_size, GFP_KERNEL);
+ if (fileio->b == NULL) {
+ kfree(fileio);
+ return -ENOMEM;
+ }
+
+ fileio->read_once = q->fileio_read_once;
+ fileio->write_immediately = q->fileio_write_immediately;
+
+ /*
+ * Request buffers and use MMAP type to force driver
+ * to allocate buffers by itself.
+ */
+ fileio->count = count;
+ fileio->memory = VB2_MEMORY_MMAP;
+ fileio->type = q->type;
+ q->fileio = fileio;
+ ret = vb2_core_reqbufs(q, fileio->memory, &fileio->count);
+ if (ret)
+ goto err_kfree;
+
+ /*
+ * Check if plane_count is correct
+ * (multiplane buffers are not supported).
+ */
+ if (q->bufs[0]->num_planes != 1) {
+ ret = -EBUSY;
+ goto err_reqbufs;
+ }
+
+ /*
+ * Get kernel address of each buffer.
+ */
+ for (i = 0; i < q->num_buffers; i++) {
+ fileio->bufs[i].vaddr = vb2_plane_vaddr(q->bufs[i], 0);
+ if (fileio->bufs[i].vaddr == NULL) {
+ ret = -EINVAL;
+ goto err_reqbufs;
+ }
+ fileio->bufs[i].size = vb2_plane_size(q->bufs[i], 0);
+ }
+
+ /*
+ * Read mode requires pre queuing of all buffers.
+ */
+ if (read) {
+ /*
+ * Queue all buffers.
+ */
+ for (i = 0; i < q->num_buffers; i++) {
+ struct vb2_buffer *b = fileio->b;
+
+ memset(b, 0, q->buf_struct_size);
+ b->type = q->type;
+ b->memory = q->memory;
+ b->index = i;
+ ret = vb2_core_qbuf(q, i, b);
+ if (ret)
+ goto err_reqbufs;
+ fileio->bufs[i].queued = 1;
+ }
+ /*
+ * All buffers have been queued, so mark that by setting
+ * initial_index to q->num_buffers
+ */
+ fileio->initial_index = q->num_buffers;
+ fileio->cur_index = q->num_buffers;
+ }
+
+ /*
+ * Start streaming.
+ */
+ ret = vb2_core_streamon(q, q->type);
+ if (ret)
+ goto err_reqbufs;
+
+ return ret;
+
+err_reqbufs:
+ fileio->count = 0;
+ vb2_core_reqbufs(q, fileio->memory, &fileio->count);
+
+err_kfree:
+ q->fileio = NULL;
+ kfree(fileio);
+ return ret;
+}
+
+/**
+ * __vb2_cleanup_fileio() - free resourced used by file io emulator
+ * @q: videobuf2 queue
+ */
+static int __vb2_cleanup_fileio(struct vb2_queue *q)
+{
+ struct vb2_fileio_data *fileio = q->fileio;
+
+ if (fileio) {
+ vb2_core_streamoff(q, q->type);
+ q->fileio = NULL;
+ fileio->count = 0;
+ vb2_core_reqbufs(q, fileio->memory, &fileio->count);
+ kfree(fileio->b);
+ kfree(fileio);
+ dprintk(3, "file io emulator closed\n");
+ }
+ return 0;
+}
+
+/**
+ * __vb2_perform_fileio() - perform a single file io (read or write) operation
+ * @q: videobuf2 queue
+ * @data: pointed to target userspace buffer
+ * @count: number of bytes to read or write
+ * @ppos: file handle position tracking pointer
+ * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking)
+ * @read: access mode selector (1 means read, 0 means write)
+ */
+static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count,
+ loff_t *ppos, int nonblock, int read)
+{
+ struct vb2_fileio_data *fileio;
+ struct vb2_fileio_buf *buf;
+ bool is_multiplanar = q->is_multiplanar;
+ /*
+ * When using write() to write data to an output video node the vb2 core
+ * should copy timestamps if V4L2_BUF_FLAG_TIMESTAMP_COPY is set. Nobody
+ * else is able to provide this information with the write() operation.
+ */
+ bool copy_timestamp = !read && q->copy_timestamp;
+ int ret, index;
+
+ dprintk(3, "mode %s, offset %ld, count %zd, %sblocking\n",
+ read ? "read" : "write", (long)*ppos, count,
+ nonblock ? "non" : "");
+
+ if (!data)
+ return -EINVAL;
+
+ /*
+ * Initialize emulator on first call.
+ */
+ if (!vb2_fileio_is_active(q)) {
+ ret = __vb2_init_fileio(q, read);
+ dprintk(3, "vb2_init_fileio result: %d\n", ret);
+ if (ret)
+ return ret;
+ }
+ fileio = q->fileio;
+
+ /*
+ * Check if we need to dequeue the buffer.
+ */
+ index = fileio->cur_index;
+ if (index >= q->num_buffers) {
+ struct vb2_buffer *b = fileio->b;
+
+ /*
+ * Call vb2_dqbuf to get buffer back.
+ */
+ memset(b, 0, q->buf_struct_size);
+ b->type = q->type;
+ b->memory = q->memory;
+ ret = vb2_core_dqbuf(q, b, nonblock);
+ dprintk(5, "vb2_dqbuf result: %d\n", ret);
+ if (ret)
+ return ret;
+ fileio->dq_count += 1;
+
+ fileio->cur_index = index = b->index;
+ buf = &fileio->bufs[index];
+
+ /*
+ * Get number of bytes filled by the driver
+ */
+ buf->pos = 0;
+ buf->queued = 0;
+ buf->size = read ? vb2_get_plane_payload(q->bufs[index], 0)
+ : vb2_plane_size(q->bufs[index], 0);
+ /* Compensate for data_offset on read in the multiplanar case. */
+ if (is_multiplanar && read &&
+ b->planes[0].data_offset < buf->size) {
+ buf->pos = b->planes[0].data_offset;
+ buf->size -= buf->pos;
+ }
+ } else {
+ buf = &fileio->bufs[index];
+ }
+
+ /*
+ * Limit count on last few bytes of the buffer.
+ */
+ if (buf->pos + count > buf->size) {
+ count = buf->size - buf->pos;
+ dprintk(5, "reducing read count: %zd\n", count);
+ }
+
+ /*
+ * Transfer data to userspace.
+ */
+ dprintk(3, "copying %zd bytes - buffer %d, offset %u\n",
+ count, index, buf->pos);
+ if (read)
+ ret = copy_to_user(data, buf->vaddr + buf->pos, count);
+ else
+ ret = copy_from_user(buf->vaddr + buf->pos, data, count);
+ if (ret) {
+ dprintk(3, "error copying data\n");
+ return -EFAULT;
+ }
+
+ /*
+ * Update counters.
+ */
+ buf->pos += count;
+ *ppos += count;
+
+ /*
+ * Queue next buffer if required.
+ */
+ if (buf->pos == buf->size || (!read && fileio->write_immediately)) {
+ struct vb2_buffer *b = fileio->b;
+
+ /*
+ * Check if this is the last buffer to read.
+ */
+ if (read && fileio->read_once && fileio->dq_count == 1) {
+ dprintk(3, "read limit reached\n");
+ return __vb2_cleanup_fileio(q);
+ }
+
+ /*
+ * Call vb2_qbuf and give buffer to the driver.
+ */
+ memset(b, 0, q->buf_struct_size);
+ b->type = q->type;
+ b->memory = q->memory;
+ b->index = index;
+ b->planes[0].bytesused = buf->pos;
+
+ if (copy_timestamp)
+ b->timestamp = ktime_get_ns();
+ ret = vb2_core_qbuf(q, index, b);
+ dprintk(5, "vb2_dbuf result: %d\n", ret);
+ if (ret)
+ return ret;
+
+ /*
+ * Buffer has been queued, update the status
+ */
+ buf->pos = 0;
+ buf->queued = 1;
+ buf->size = vb2_plane_size(q->bufs[index], 0);
+ fileio->q_count += 1;
+ /*
+ * If we are queuing up buffers for the first time, then
+ * increase initial_index by one.
+ */
+ if (fileio->initial_index < q->num_buffers)
+ fileio->initial_index++;
+ /*
+ * The next buffer to use is either a buffer that's going to be
+ * queued for the first time (initial_index < q->num_buffers)
+ * or it is equal to q->num_buffers, meaning that the next
+ * time we need to dequeue a buffer since we've now queued up
+ * all the 'first time' buffers.
+ */
+ fileio->cur_index = fileio->initial_index;
+ }
+
+ /*
+ * Return proper number of bytes processed.
+ */
+ if (ret == 0)
+ ret = count;
+ return ret;
+}
+
+size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
+ loff_t *ppos, int nonblocking)
+{
+ return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1);
+}
+EXPORT_SYMBOL_GPL(vb2_read);
+
+size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
+ loff_t *ppos, int nonblocking)
+{
+ return __vb2_perform_fileio(q, (char __user *) data, count,
+ ppos, nonblocking, 0);
+}
+EXPORT_SYMBOL_GPL(vb2_write);
+
+struct vb2_threadio_data {
+ struct task_struct *thread;
+ vb2_thread_fnc fnc;
+ void *priv;
+ bool stop;
+};
+
+static int vb2_thread(void *data)
+{
+ struct vb2_queue *q = data;
+ struct vb2_threadio_data *threadio = q->threadio;
+ struct vb2_fileio_data *fileio = q->fileio;
+ bool copy_timestamp = false;
+ int prequeue = 0;
+ int index = 0;
+ int ret = 0;
+
+ if (q->is_output) {
+ prequeue = q->num_buffers;
+ copy_timestamp = q->copy_timestamp;
+ }
+
+ set_freezable();
+
+ for (;;) {
+ struct vb2_buffer *vb;
+ struct vb2_buffer *b = fileio->b;
+
+ /*
+ * Call vb2_dqbuf to get buffer back.
+ */
+ memset(b, 0, q->buf_struct_size);
+ b->type = q->type;
+ b->memory = q->memory;
+ if (prequeue) {
+ b->index = index++;
+ prequeue--;
+ } else {
+ call_void_qop(q, wait_finish, q);
+ if (!threadio->stop)
+ ret = vb2_core_dqbuf(q, b, 0);
+ call_void_qop(q, wait_prepare, q);
+ dprintk(5, "file io: vb2_dqbuf result: %d\n", ret);
+ }
+ if (ret || threadio->stop)
+ break;
+ try_to_freeze();
+
+ vb = q->bufs[b->index];
+ if (b->state == VB2_BUF_STATE_DONE)
+ if (threadio->fnc(vb, threadio->priv))
+ break;
+ call_void_qop(q, wait_finish, q);
+ if (copy_timestamp)
+ b->timestamp = ktime_get_ns();;
+ if (!threadio->stop)
+ ret = vb2_core_qbuf(q, b->index, b);
+ call_void_qop(q, wait_prepare, q);
+ if (ret || threadio->stop)
+ break;
+ }
+
+ /* Hmm, linux becomes *very* unhappy without this ... */
+ while (!kthread_should_stop()) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+ }
+ return 0;
+}
+
+/*
+ * This function should not be used for anything else but the videobuf2-dvb
+ * support. If you think you have another good use-case for this, then please
+ * contact the linux-media mailinglist first.
+ */
+int vb2_thread_start(struct vb2_queue *q, vb2_thread_fnc fnc, void *priv,
+ const char *thread_name)
+{
+ struct vb2_threadio_data *threadio;
+ int ret = 0;
+
+ if (q->threadio)
+ return -EBUSY;
+ if (vb2_is_busy(q))
+ return -EBUSY;
+ if (WARN_ON(q->fileio))
+ return -EBUSY;
+
+ threadio = kzalloc(sizeof(*threadio), GFP_KERNEL);
+ if (threadio == NULL)
+ return -ENOMEM;
+ threadio->fnc = fnc;
+ threadio->priv = priv;
+
+ ret = __vb2_init_fileio(q, !q->is_output);
+ dprintk(3, "file io: vb2_init_fileio result: %d\n", ret);
+ if (ret)
+ goto nomem;
+ q->threadio = threadio;
+ threadio->thread = kthread_run(vb2_thread, q, "vb2-%s", thread_name);
+ if (IS_ERR(threadio->thread)) {
+ ret = PTR_ERR(threadio->thread);
+ threadio->thread = NULL;
+ goto nothread;
+ }
+ return 0;
+
+nothread:
+ __vb2_cleanup_fileio(q);
+nomem:
+ kfree(threadio);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_thread_start);
+
+int vb2_thread_stop(struct vb2_queue *q)
+{
+ struct vb2_threadio_data *threadio = q->threadio;
+ int err;
+
+ if (threadio == NULL)
+ return 0;
+ threadio->stop = true;
+ /* Wake up all pending sleeps in the thread */
+ vb2_queue_error(q);
+ err = kthread_stop(threadio->thread);
+ __vb2_cleanup_fileio(q);
+ threadio->thread = NULL;
+ kfree(threadio);
+ q->threadio = NULL;
+ return err;
+}
+EXPORT_SYMBOL_GPL(vb2_thread_stop);
+
+MODULE_DESCRIPTION("Media buffer core framework");
MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>, Marek Szyprowski");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/v4l2-core/videobuf2-internal.h b/drivers/media/v4l2-core/videobuf2-internal.h
deleted file mode 100644
index 79018c749282..000000000000
--- a/drivers/media/v4l2-core/videobuf2-internal.h
+++ /dev/null
@@ -1,161 +0,0 @@
-#ifndef _MEDIA_VIDEOBUF2_INTERNAL_H
-#define _MEDIA_VIDEOBUF2_INTERNAL_H
-
-#include <linux/err.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <media/videobuf2-core.h>
-
-extern int vb2_debug;
-
-#define dprintk(level, fmt, arg...) \
- do { \
- if (vb2_debug >= level) \
- pr_info("vb2: %s: " fmt, __func__, ## arg); \
- } while (0)
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-
-/*
- * If advanced debugging is on, then count how often each op is called
- * successfully, which can either be per-buffer or per-queue.
- *
- * This makes it easy to check that the 'init' and 'cleanup'
- * (and variations thereof) stay balanced.
- */
-
-#define log_memop(vb, op) \
- dprintk(2, "call_memop(%p, %d, %s)%s\n", \
- (vb)->vb2_queue, (vb)->index, #op, \
- (vb)->vb2_queue->mem_ops->op ? "" : " (nop)")
-
-#define call_memop(vb, op, args...) \
-({ \
- struct vb2_queue *_q = (vb)->vb2_queue; \
- int err; \
- \
- log_memop(vb, op); \
- err = _q->mem_ops->op ? _q->mem_ops->op(args) : 0; \
- if (!err) \
- (vb)->cnt_mem_ ## op++; \
- err; \
-})
-
-#define call_ptr_memop(vb, op, args...) \
-({ \
- struct vb2_queue *_q = (vb)->vb2_queue; \
- void *ptr; \
- \
- log_memop(vb, op); \
- ptr = _q->mem_ops->op ? _q->mem_ops->op(args) : NULL; \
- if (!IS_ERR_OR_NULL(ptr)) \
- (vb)->cnt_mem_ ## op++; \
- ptr; \
-})
-
-#define call_void_memop(vb, op, args...) \
-({ \
- struct vb2_queue *_q = (vb)->vb2_queue; \
- \
- log_memop(vb, op); \
- if (_q->mem_ops->op) \
- _q->mem_ops->op(args); \
- (vb)->cnt_mem_ ## op++; \
-})
-
-#define log_qop(q, op) \
- dprintk(2, "call_qop(%p, %s)%s\n", q, #op, \
- (q)->ops->op ? "" : " (nop)")
-
-#define call_qop(q, op, args...) \
-({ \
- int err; \
- \
- log_qop(q, op); \
- err = (q)->ops->op ? (q)->ops->op(args) : 0; \
- if (!err) \
- (q)->cnt_ ## op++; \
- err; \
-})
-
-#define call_void_qop(q, op, args...) \
-({ \
- log_qop(q, op); \
- if ((q)->ops->op) \
- (q)->ops->op(args); \
- (q)->cnt_ ## op++; \
-})
-
-#define log_vb_qop(vb, op, args...) \
- dprintk(2, "call_vb_qop(%p, %d, %s)%s\n", \
- (vb)->vb2_queue, (vb)->index, #op, \
- (vb)->vb2_queue->ops->op ? "" : " (nop)")
-
-#define call_vb_qop(vb, op, args...) \
-({ \
- int err; \
- \
- log_vb_qop(vb, op); \
- err = (vb)->vb2_queue->ops->op ? \
- (vb)->vb2_queue->ops->op(args) : 0; \
- if (!err) \
- (vb)->cnt_ ## op++; \
- err; \
-})
-
-#define call_void_vb_qop(vb, op, args...) \
-({ \
- log_vb_qop(vb, op); \
- if ((vb)->vb2_queue->ops->op) \
- (vb)->vb2_queue->ops->op(args); \
- (vb)->cnt_ ## op++; \
-})
-
-#else
-
-#define call_memop(vb, op, args...) \
- ((vb)->vb2_queue->mem_ops->op ? \
- (vb)->vb2_queue->mem_ops->op(args) : 0)
-
-#define call_ptr_memop(vb, op, args...) \
- ((vb)->vb2_queue->mem_ops->op ? \
- (vb)->vb2_queue->mem_ops->op(args) : NULL)
-
-#define call_void_memop(vb, op, args...) \
- do { \
- if ((vb)->vb2_queue->mem_ops->op) \
- (vb)->vb2_queue->mem_ops->op(args); \
- } while (0)
-
-#define call_qop(q, op, args...) \
- ((q)->ops->op ? (q)->ops->op(args) : 0)
-
-#define call_void_qop(q, op, args...) \
- do { \
- if ((q)->ops->op) \
- (q)->ops->op(args); \
- } while (0)
-
-#define call_vb_qop(vb, op, args...) \
- ((vb)->vb2_queue->ops->op ? (vb)->vb2_queue->ops->op(args) : 0)
-
-#define call_void_vb_qop(vb, op, args...) \
- do { \
- if ((vb)->vb2_queue->ops->op) \
- (vb)->vb2_queue->ops->op(args); \
- } while (0)
-
-#endif
-
-#define call_bufop(q, op, args...) \
-({ \
- int ret = 0; \
- if (q && q->buf_ops && q->buf_ops->op) \
- ret = q->buf_ops->op(args); \
- ret; \
-})
-
-bool vb2_buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb);
-int vb2_verify_memory_type(struct vb2_queue *q,
- enum vb2_memory memory, unsigned int type);
-#endif /* _MEDIA_VIDEOBUF2_INTERNAL_H */
diff --git a/drivers/media/v4l2-core/videobuf2-v4l2.c b/drivers/media/v4l2-core/videobuf2-v4l2.c
index 27b4b9e7c0c2..c9a28605511a 100644
--- a/drivers/media/v4l2-core/videobuf2-v4l2.c
+++ b/drivers/media/v4l2-core/videobuf2-v4l2.c
@@ -31,7 +31,14 @@
#include <media/videobuf2-v4l2.h>
-#include "videobuf2-internal.h"
+static int debug;
+module_param(debug, int, 0644);
+
+#define dprintk(level, fmt, arg...) \
+ do { \
+ if (debug >= level) \
+ pr_info("vb2-v4l2: %s: " fmt, __func__, ## arg); \
+ } while (0)
/* Flags that are set by the vb2 core */
#define V4L2_BUFFER_MASK_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \
@@ -52,7 +59,7 @@ static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer
return 0;
/* Is memory for copying plane information present? */
- if (NULL == b->m.planes) {
+ if (b->m.planes == NULL) {
dprintk(1, "multi-planar buffer passed but "
"planes array not provided\n");
return -EINVAL;
@@ -107,7 +114,7 @@ static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b)
return 0;
}
-static int __set_timestamp(struct vb2_buffer *vb, const void *pb)
+static void __copy_timestamp(struct vb2_buffer *vb, const void *pb)
{
const struct v4l2_buffer *b = pb;
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
@@ -118,14 +125,12 @@ static int __set_timestamp(struct vb2_buffer *vb, const void *pb)
* For output buffers copy the timestamp if needed,
* and the timecode field and flag if needed.
*/
- if ((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) ==
- V4L2_BUF_FLAG_TIMESTAMP_COPY)
- vbuf->timestamp = b->timestamp;
+ if (q->copy_timestamp)
+ vb->timestamp = timeval_to_ns(&b->timestamp);
vbuf->flags |= b->flags & V4L2_BUF_FLAG_TIMECODE;
if (b->flags & V4L2_BUF_FLAG_TIMECODE)
vbuf->timecode = b->timecode;
}
- return 0;
};
static void vb2_warn_zero_bytesused(struct vb2_buffer *vb)
@@ -176,7 +181,7 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b,
* __fill_v4l2_buffer() - fill in a struct v4l2_buffer with information to be
* returned to userspace
*/
-static int __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
+static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
{
struct v4l2_buffer *b = pb;
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
@@ -191,7 +196,7 @@ static int __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
b->flags = vbuf->flags;
b->field = vbuf->field;
- b->timestamp = vbuf->timestamp;
+ b->timestamp = ns_to_timeval(vb->timestamp);
b->timecode = vbuf->timecode;
b->sequence = vbuf->sequence;
b->reserved2 = 0;
@@ -238,8 +243,7 @@ static int __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
*/
b->flags &= ~V4L2_BUFFER_MASK_FLAGS;
b->flags |= q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK;
- if ((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) !=
- V4L2_BUF_FLAG_TIMESTAMP_COPY) {
+ if (!q->copy_timestamp) {
/*
* For non-COPY timestamps, drop timestamp source bits
* and obtain the timestamp source from the queue.
@@ -272,7 +276,10 @@ static int __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
if (vb2_buffer_in_use(q, vb))
b->flags |= V4L2_BUF_FLAG_MAPPED;
- return 0;
+ if (!q->is_output &&
+ b->flags & V4L2_BUF_FLAG_DONE &&
+ b->flags & V4L2_BUF_FLAG_LAST)
+ q->last_buffer_dequeued = true;
}
/**
@@ -308,8 +315,7 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb,
"for an output buffer\n");
return -EINVAL;
}
- vbuf->timestamp.tv_sec = 0;
- vbuf->timestamp.tv_usec = 0;
+ vb->timestamp = 0;
vbuf->sequence = 0;
if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
@@ -404,8 +410,7 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb,
/* Zero flags that the vb2 core handles */
vbuf->flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS;
- if ((vb->vb2_queue->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) !=
- V4L2_BUF_FLAG_TIMESTAMP_COPY || !V4L2_TYPE_IS_OUTPUT(b->type)) {
+ if (!vb->vb2_queue->copy_timestamp || !V4L2_TYPE_IS_OUTPUT(b->type)) {
/*
* Non-COPY timestamps and non-OUTPUT queues will get
* their timestamp and timestamp source flags from the
@@ -434,7 +439,7 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb,
static const struct vb2_buf_ops v4l2_buf_ops = {
.fill_user_buffer = __fill_v4l2_buffer,
.fill_vb2_buffer = __fill_vb2_buffer,
- .set_timestamp = __set_timestamp,
+ .copy_timestamp = __copy_timestamp,
};
/**
@@ -466,8 +471,9 @@ int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b)
}
vb = q->bufs[b->index];
ret = __verify_planes_array(vb, b);
-
- return ret ? ret : vb2_core_querybuf(q, b->index, b);
+ if (!ret)
+ vb2_core_querybuf(q, b->index, b);
+ return ret;
}
EXPORT_SYMBOL(vb2_querybuf);
@@ -525,14 +531,52 @@ EXPORT_SYMBOL_GPL(vb2_prepare_buf);
*/
int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
{
- int ret = vb2_verify_memory_type(q, create->memory,
- create->format.type);
+ unsigned requested_planes = 1;
+ unsigned requested_sizes[VIDEO_MAX_PLANES];
+ struct v4l2_format *f = &create->format;
+ int ret = vb2_verify_memory_type(q, create->memory, f->type);
+ unsigned i;
create->index = q->num_buffers;
if (create->count == 0)
return ret != -EBUSY ? ret : 0;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ requested_planes = f->fmt.pix_mp.num_planes;
+ if (requested_planes == 0 ||
+ requested_planes > VIDEO_MAX_PLANES)
+ return -EINVAL;
+ for (i = 0; i < requested_planes; i++)
+ requested_sizes[i] =
+ f->fmt.pix_mp.plane_fmt[i].sizeimage;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ requested_sizes[0] = f->fmt.pix.sizeimage;
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ requested_sizes[0] = f->fmt.vbi.samples_per_line *
+ (f->fmt.vbi.count[0] + f->fmt.vbi.count[1]);
+ break;
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ requested_sizes[0] = f->fmt.sliced.io_size;
+ break;
+ case V4L2_BUF_TYPE_SDR_CAPTURE:
+ case V4L2_BUF_TYPE_SDR_OUTPUT:
+ requested_sizes[0] = f->fmt.sdr.buffersize;
+ break;
+ default:
+ return -EINVAL;
+ }
+ for (i = 0; i < requested_planes; i++)
+ if (requested_sizes[i] == 0)
+ return -EINVAL;
return ret ? ret : vb2_core_create_bufs(q, create->memory,
- &create->count, &create->format);
+ &create->count, requested_planes, requested_sizes);
}
EXPORT_SYMBOL_GPL(vb2_create_bufs);
@@ -583,10 +627,6 @@ static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b,
ret = vb2_core_dqbuf(q, b, nonblocking);
- if (!ret && !q->is_output &&
- b->flags & V4L2_BUF_FLAG_LAST)
- q->last_buffer_dequeued = true;
-
return ret;
}
@@ -723,14 +763,13 @@ int vb2_queue_init(struct vb2_queue *q)
q->buf_ops = &v4l2_buf_ops;
q->is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(q->type);
q->is_output = V4L2_TYPE_IS_OUTPUT(q->type);
+ q->copy_timestamp = (q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK)
+ == V4L2_BUF_FLAG_TIMESTAMP_COPY;
return vb2_core_queue_init(q);
}
EXPORT_SYMBOL_GPL(vb2_queue_init);
-static int __vb2_init_fileio(struct vb2_queue *q, int read);
-static int __vb2_cleanup_fileio(struct vb2_queue *q);
-
/**
* vb2_queue_release() - stop streaming, release the queue and free memory
* @q: videobuf2 queue
@@ -741,7 +780,6 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q);
*/
void vb2_queue_release(struct vb2_queue *q)
{
- __vb2_cleanup_fileio(q);
vb2_core_queue_release(q);
}
EXPORT_SYMBOL_GPL(vb2_queue_release);
@@ -769,9 +807,7 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
{
struct video_device *vfd = video_devdata(file);
unsigned long req_events = poll_requested_events(wait);
- struct vb2_buffer *vb = NULL;
unsigned int res = 0;
- unsigned long flags;
if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
struct v4l2_fh *fh = file->private_data;
@@ -782,611 +818,18 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
poll_wait(file, &fh->wait, wait);
}
- if (!q->is_output && !(req_events & (POLLIN | POLLRDNORM)))
- return res;
- if (q->is_output && !(req_events & (POLLOUT | POLLWRNORM)))
- return res;
-
- /*
- * Start file I/O emulator only if streaming API has not been used yet.
- */
- if (q->num_buffers == 0 && !vb2_fileio_is_active(q)) {
- if (!q->is_output && (q->io_modes & VB2_READ) &&
- (req_events & (POLLIN | POLLRDNORM))) {
- if (__vb2_init_fileio(q, 1))
- return res | POLLERR;
- }
- if (q->is_output && (q->io_modes & VB2_WRITE) &&
- (req_events & (POLLOUT | POLLWRNORM))) {
- if (__vb2_init_fileio(q, 0))
- return res | POLLERR;
- /*
- * Write to OUTPUT queue can be done immediately.
- */
- return res | POLLOUT | POLLWRNORM;
- }
- }
-
- /*
- * There is nothing to wait for if the queue isn't streaming, or if the
- * error flag is set.
- */
- if (!vb2_is_streaming(q) || q->error)
- return res | POLLERR;
/*
* For compatibility with vb1: if QBUF hasn't been called yet, then
* return POLLERR as well. This only affects capture queues, output
* queues will always initialize waiting_for_buffers to false.
*/
- if (q->waiting_for_buffers)
- return res | POLLERR;
-
- /*
- * For output streams you can write as long as there are fewer buffers
- * queued than there are buffers available.
- */
- if (q->is_output && q->queued_count < q->num_buffers)
- return res | POLLOUT | POLLWRNORM;
-
- if (list_empty(&q->done_list)) {
- /*
- * If the last buffer was dequeued from a capture queue,
- * return immediately. DQBUF will return -EPIPE.
- */
- if (q->last_buffer_dequeued)
- return res | POLLIN | POLLRDNORM;
-
- poll_wait(file, &q->done_wq, wait);
- }
+ if (q->waiting_for_buffers && (req_events & (POLLIN | POLLRDNORM)))
+ return POLLERR;
- /*
- * Take first buffer available for dequeuing.
- */
- spin_lock_irqsave(&q->done_lock, flags);
- if (!list_empty(&q->done_list))
- vb = list_first_entry(&q->done_list, struct vb2_buffer,
- done_entry);
- spin_unlock_irqrestore(&q->done_lock, flags);
-
- if (vb && (vb->state == VB2_BUF_STATE_DONE
- || vb->state == VB2_BUF_STATE_ERROR)) {
- return (q->is_output) ?
- res | POLLOUT | POLLWRNORM :
- res | POLLIN | POLLRDNORM;
- }
- return res;
+ return res | vb2_core_poll(q, file, wait);
}
EXPORT_SYMBOL_GPL(vb2_poll);
-/**
- * struct vb2_fileio_buf - buffer context used by file io emulator
- *
- * vb2 provides a compatibility layer and emulator of file io (read and
- * write) calls on top of streaming API. This structure is used for
- * tracking context related to the buffers.
- */
-struct vb2_fileio_buf {
- void *vaddr;
- unsigned int size;
- unsigned int pos;
- unsigned int queued:1;
-};
-
-/**
- * struct vb2_fileio_data - queue context used by file io emulator
- *
- * @cur_index: the index of the buffer currently being read from or
- * written to. If equal to q->num_buffers then a new buffer
- * must be dequeued.
- * @initial_index: in the read() case all buffers are queued up immediately
- * in __vb2_init_fileio() and __vb2_perform_fileio() just cycles
- * buffers. However, in the write() case no buffers are initially
- * queued, instead whenever a buffer is full it is queued up by
- * __vb2_perform_fileio(). Only once all available buffers have
- * been queued up will __vb2_perform_fileio() start to dequeue
- * buffers. This means that initially __vb2_perform_fileio()
- * needs to know what buffer index to use when it is queuing up
- * the buffers for the first time. That initial index is stored
- * in this field. Once it is equal to q->num_buffers all
- * available buffers have been queued and __vb2_perform_fileio()
- * should start the normal dequeue/queue cycle.
- *
- * vb2 provides a compatibility layer and emulator of file io (read and
- * write) calls on top of streaming API. For proper operation it required
- * this structure to save the driver state between each call of the read
- * or write function.
- */
-struct vb2_fileio_data {
- struct v4l2_requestbuffers req;
- struct v4l2_plane p;
- struct v4l2_buffer b;
- struct vb2_fileio_buf bufs[VB2_MAX_FRAME];
- unsigned int cur_index;
- unsigned int initial_index;
- unsigned int q_count;
- unsigned int dq_count;
- unsigned read_once:1;
- unsigned write_immediately:1;
-};
-
-/**
- * __vb2_init_fileio() - initialize file io emulator
- * @q: videobuf2 queue
- * @read: mode selector (1 means read, 0 means write)
- */
-static int __vb2_init_fileio(struct vb2_queue *q, int read)
-{
- struct vb2_fileio_data *fileio;
- int i, ret;
- unsigned int count = 0;
-
- /*
- * Sanity check
- */
- if (WARN_ON((read && !(q->io_modes & VB2_READ)) ||
- (!read && !(q->io_modes & VB2_WRITE))))
- return -EINVAL;
-
- /*
- * Check if device supports mapping buffers to kernel virtual space.
- */
- if (!q->mem_ops->vaddr)
- return -EBUSY;
-
- /*
- * Check if streaming api has not been already activated.
- */
- if (q->streaming || q->num_buffers > 0)
- return -EBUSY;
-
- /*
- * Start with count 1, driver can increase it in queue_setup()
- */
- count = 1;
-
- dprintk(3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n",
- (read) ? "read" : "write", count, q->fileio_read_once,
- q->fileio_write_immediately);
-
- fileio = kzalloc(sizeof(struct vb2_fileio_data), GFP_KERNEL);
- if (fileio == NULL)
- return -ENOMEM;
-
- fileio->read_once = q->fileio_read_once;
- fileio->write_immediately = q->fileio_write_immediately;
-
- /*
- * Request buffers and use MMAP type to force driver
- * to allocate buffers by itself.
- */
- fileio->req.count = count;
- fileio->req.memory = VB2_MEMORY_MMAP;
- fileio->req.type = q->type;
- q->fileio = fileio;
- ret = vb2_core_reqbufs(q, fileio->req.memory, &fileio->req.count);
- if (ret)
- goto err_kfree;
-
- /*
- * Check if plane_count is correct
- * (multiplane buffers are not supported).
- */
- if (q->bufs[0]->num_planes != 1) {
- ret = -EBUSY;
- goto err_reqbufs;
- }
-
- /*
- * Get kernel address of each buffer.
- */
- for (i = 0; i < q->num_buffers; i++) {
- fileio->bufs[i].vaddr = vb2_plane_vaddr(q->bufs[i], 0);
- if (fileio->bufs[i].vaddr == NULL) {
- ret = -EINVAL;
- goto err_reqbufs;
- }
- fileio->bufs[i].size = vb2_plane_size(q->bufs[i], 0);
- }
-
- /*
- * Read mode requires pre queuing of all buffers.
- */
- if (read) {
- bool is_multiplanar = q->is_multiplanar;
-
- /*
- * Queue all buffers.
- */
- for (i = 0; i < q->num_buffers; i++) {
- struct v4l2_buffer *b = &fileio->b;
-
- memset(b, 0, sizeof(*b));
- b->type = q->type;
- if (is_multiplanar) {
- memset(&fileio->p, 0, sizeof(fileio->p));
- b->m.planes = &fileio->p;
- b->length = 1;
- }
- b->memory = q->memory;
- b->index = i;
- ret = vb2_internal_qbuf(q, b);
- if (ret)
- goto err_reqbufs;
- fileio->bufs[i].queued = 1;
- }
- /*
- * All buffers have been queued, so mark that by setting
- * initial_index to q->num_buffers
- */
- fileio->initial_index = q->num_buffers;
- fileio->cur_index = q->num_buffers;
- }
-
- /*
- * Start streaming.
- */
- ret = vb2_core_streamon(q, q->type);
- if (ret)
- goto err_reqbufs;
-
- return ret;
-
-err_reqbufs:
- fileio->req.count = 0;
- vb2_core_reqbufs(q, fileio->req.memory, &fileio->req.count);
-
-err_kfree:
- q->fileio = NULL;
- kfree(fileio);
- return ret;
-}
-
-/**
- * __vb2_cleanup_fileio() - free resourced used by file io emulator
- * @q: videobuf2 queue
- */
-static int __vb2_cleanup_fileio(struct vb2_queue *q)
-{
- struct vb2_fileio_data *fileio = q->fileio;
-
- if (fileio) {
- vb2_core_streamoff(q, q->type);
- q->fileio = NULL;
- fileio->req.count = 0;
- vb2_reqbufs(q, &fileio->req);
- kfree(fileio);
- dprintk(3, "file io emulator closed\n");
- }
- return 0;
-}
-
-/**
- * __vb2_perform_fileio() - perform a single file io (read or write) operation
- * @q: videobuf2 queue
- * @data: pointed to target userspace buffer
- * @count: number of bytes to read or write
- * @ppos: file handle position tracking pointer
- * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking)
- * @read: access mode selector (1 means read, 0 means write)
- */
-static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count,
- loff_t *ppos, int nonblock, int read)
-{
- struct vb2_fileio_data *fileio;
- struct vb2_fileio_buf *buf;
- bool is_multiplanar = q->is_multiplanar;
- /*
- * When using write() to write data to an output video node the vb2 core
- * should set timestamps if V4L2_BUF_FLAG_TIMESTAMP_COPY is set. Nobody
- * else is able to provide this information with the write() operation.
- */
- bool set_timestamp = !read &&
- (q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) ==
- V4L2_BUF_FLAG_TIMESTAMP_COPY;
- int ret, index;
-
- dprintk(3, "mode %s, offset %ld, count %zd, %sblocking\n",
- read ? "read" : "write", (long)*ppos, count,
- nonblock ? "non" : "");
-
- if (!data)
- return -EINVAL;
-
- /*
- * Initialize emulator on first call.
- */
- if (!vb2_fileio_is_active(q)) {
- ret = __vb2_init_fileio(q, read);
- dprintk(3, "vb2_init_fileio result: %d\n", ret);
- if (ret)
- return ret;
- }
- fileio = q->fileio;
-
- /*
- * Check if we need to dequeue the buffer.
- */
- index = fileio->cur_index;
- if (index >= q->num_buffers) {
- /*
- * Call vb2_dqbuf to get buffer back.
- */
- memset(&fileio->b, 0, sizeof(fileio->b));
- fileio->b.type = q->type;
- fileio->b.memory = q->memory;
- if (is_multiplanar) {
- memset(&fileio->p, 0, sizeof(fileio->p));
- fileio->b.m.planes = &fileio->p;
- fileio->b.length = 1;
- }
- ret = vb2_internal_dqbuf(q, &fileio->b, nonblock);
- dprintk(5, "vb2_dqbuf result: %d\n", ret);
- if (ret)
- return ret;
- fileio->dq_count += 1;
-
- fileio->cur_index = index = fileio->b.index;
- buf = &fileio->bufs[index];
-
- /*
- * Get number of bytes filled by the driver
- */
- buf->pos = 0;
- buf->queued = 0;
- buf->size = read ? vb2_get_plane_payload(q->bufs[index], 0)
- : vb2_plane_size(q->bufs[index], 0);
- /* Compensate for data_offset on read in the multiplanar case. */
- if (is_multiplanar && read &&
- fileio->b.m.planes[0].data_offset < buf->size) {
- buf->pos = fileio->b.m.planes[0].data_offset;
- buf->size -= buf->pos;
- }
- } else {
- buf = &fileio->bufs[index];
- }
-
- /*
- * Limit count on last few bytes of the buffer.
- */
- if (buf->pos + count > buf->size) {
- count = buf->size - buf->pos;
- dprintk(5, "reducing read count: %zd\n", count);
- }
-
- /*
- * Transfer data to userspace.
- */
- dprintk(3, "copying %zd bytes - buffer %d, offset %u\n",
- count, index, buf->pos);
- if (read)
- ret = copy_to_user(data, buf->vaddr + buf->pos, count);
- else
- ret = copy_from_user(buf->vaddr + buf->pos, data, count);
- if (ret) {
- dprintk(3, "error copying data\n");
- return -EFAULT;
- }
-
- /*
- * Update counters.
- */
- buf->pos += count;
- *ppos += count;
-
- /*
- * Queue next buffer if required.
- */
- if (buf->pos == buf->size || (!read && fileio->write_immediately)) {
- /*
- * Check if this is the last buffer to read.
- */
- if (read && fileio->read_once && fileio->dq_count == 1) {
- dprintk(3, "read limit reached\n");
- return __vb2_cleanup_fileio(q);
- }
-
- /*
- * Call vb2_qbuf and give buffer to the driver.
- */
- memset(&fileio->b, 0, sizeof(fileio->b));
- fileio->b.type = q->type;
- fileio->b.memory = q->memory;
- fileio->b.index = index;
- fileio->b.bytesused = buf->pos;
- if (is_multiplanar) {
- memset(&fileio->p, 0, sizeof(fileio->p));
- fileio->p.bytesused = buf->pos;
- fileio->b.m.planes = &fileio->p;
- fileio->b.length = 1;
- }
- if (set_timestamp)
- v4l2_get_timestamp(&fileio->b.timestamp);
- ret = vb2_internal_qbuf(q, &fileio->b);
- dprintk(5, "vb2_dbuf result: %d\n", ret);
- if (ret)
- return ret;
-
- /*
- * Buffer has been queued, update the status
- */
- buf->pos = 0;
- buf->queued = 1;
- buf->size = vb2_plane_size(q->bufs[index], 0);
- fileio->q_count += 1;
- /*
- * If we are queuing up buffers for the first time, then
- * increase initial_index by one.
- */
- if (fileio->initial_index < q->num_buffers)
- fileio->initial_index++;
- /*
- * The next buffer to use is either a buffer that's going to be
- * queued for the first time (initial_index < q->num_buffers)
- * or it is equal to q->num_buffers, meaning that the next
- * time we need to dequeue a buffer since we've now queued up
- * all the 'first time' buffers.
- */
- fileio->cur_index = fileio->initial_index;
- }
-
- /*
- * Return proper number of bytes processed.
- */
- if (ret == 0)
- ret = count;
- return ret;
-}
-
-size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
- loff_t *ppos, int nonblocking)
-{
- return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1);
-}
-EXPORT_SYMBOL_GPL(vb2_read);
-
-size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
- loff_t *ppos, int nonblocking)
-{
- return __vb2_perform_fileio(q, (char __user *) data, count,
- ppos, nonblocking, 0);
-}
-EXPORT_SYMBOL_GPL(vb2_write);
-
-struct vb2_threadio_data {
- struct task_struct *thread;
- vb2_thread_fnc fnc;
- void *priv;
- bool stop;
-};
-
-static int vb2_thread(void *data)
-{
- struct vb2_queue *q = data;
- struct vb2_threadio_data *threadio = q->threadio;
- struct vb2_fileio_data *fileio = q->fileio;
- bool set_timestamp = false;
- int prequeue = 0;
- int index = 0;
- int ret = 0;
-
- if (q->is_output) {
- prequeue = q->num_buffers;
- set_timestamp =
- (q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) ==
- V4L2_BUF_FLAG_TIMESTAMP_COPY;
- }
-
- set_freezable();
-
- for (;;) {
- struct vb2_buffer *vb;
-
- /*
- * Call vb2_dqbuf to get buffer back.
- */
- memset(&fileio->b, 0, sizeof(fileio->b));
- fileio->b.type = q->type;
- fileio->b.memory = q->memory;
- if (prequeue) {
- fileio->b.index = index++;
- prequeue--;
- } else {
- call_void_qop(q, wait_finish, q);
- if (!threadio->stop)
- ret = vb2_internal_dqbuf(q, &fileio->b, 0);
- call_void_qop(q, wait_prepare, q);
- dprintk(5, "file io: vb2_dqbuf result: %d\n", ret);
- }
- if (ret || threadio->stop)
- break;
- try_to_freeze();
-
- vb = q->bufs[fileio->b.index];
- if (!(fileio->b.flags & V4L2_BUF_FLAG_ERROR))
- if (threadio->fnc(vb, threadio->priv))
- break;
- call_void_qop(q, wait_finish, q);
- if (set_timestamp)
- v4l2_get_timestamp(&fileio->b.timestamp);
- if (!threadio->stop)
- ret = vb2_internal_qbuf(q, &fileio->b);
- call_void_qop(q, wait_prepare, q);
- if (ret || threadio->stop)
- break;
- }
-
- /* Hmm, linux becomes *very* unhappy without this ... */
- while (!kthread_should_stop()) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule();
- }
- return 0;
-}
-
-/*
- * This function should not be used for anything else but the videobuf2-dvb
- * support. If you think you have another good use-case for this, then please
- * contact the linux-media mailinglist first.
- */
-int vb2_thread_start(struct vb2_queue *q, vb2_thread_fnc fnc, void *priv,
- const char *thread_name)
-{
- struct vb2_threadio_data *threadio;
- int ret = 0;
-
- if (q->threadio)
- return -EBUSY;
- if (vb2_is_busy(q))
- return -EBUSY;
- if (WARN_ON(q->fileio))
- return -EBUSY;
-
- threadio = kzalloc(sizeof(*threadio), GFP_KERNEL);
- if (threadio == NULL)
- return -ENOMEM;
- threadio->fnc = fnc;
- threadio->priv = priv;
-
- ret = __vb2_init_fileio(q, !q->is_output);
- dprintk(3, "file io: vb2_init_fileio result: %d\n", ret);
- if (ret)
- goto nomem;
- q->threadio = threadio;
- threadio->thread = kthread_run(vb2_thread, q, "vb2-%s", thread_name);
- if (IS_ERR(threadio->thread)) {
- ret = PTR_ERR(threadio->thread);
- threadio->thread = NULL;
- goto nothread;
- }
- return 0;
-
-nothread:
- __vb2_cleanup_fileio(q);
-nomem:
- kfree(threadio);
- return ret;
-}
-EXPORT_SYMBOL_GPL(vb2_thread_start);
-
-int vb2_thread_stop(struct vb2_queue *q)
-{
- struct vb2_threadio_data *threadio = q->threadio;
- int err;
-
- if (threadio == NULL)
- return 0;
- threadio->stop = true;
- /* Wake up all pending sleeps in the thread */
- vb2_queue_error(q);
- err = kthread_stop(threadio->thread);
- __vb2_cleanup_fileio(q);
- threadio->thread = NULL;
- kfree(threadio);
- q->threadio = NULL;
- return err;
-}
-EXPORT_SYMBOL_GPL(vb2_thread_stop);
-
/*
* The following functions are not part of the vb2 core API, but are helper
* functions that plug into struct v4l2_ioctl_ops, struct v4l2_file_operations
@@ -1440,8 +883,8 @@ int vb2_ioctl_create_bufs(struct file *file, void *priv,
return res;
if (vb2_queue_is_busy(vdev, file))
return -EBUSY;
- res = vb2_core_create_bufs(vdev->queue, p->memory, &p->count,
- &p->format);
+
+ res = vb2_create_bufs(vdev->queue, p);
if (res == 0)
vdev->queue->owner = file->private_data;
return res;
diff --git a/drivers/memory/fsl_ifc.c b/drivers/memory/fsl_ifc.c
index e87459f6d686..acd1460cf787 100644
--- a/drivers/memory/fsl_ifc.c
+++ b/drivers/memory/fsl_ifc.c
@@ -22,6 +22,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/compiler.h>
+#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/slab.h>
diff --git a/drivers/mfd/88pm80x.c b/drivers/mfd/88pm80x.c
index 63445ea6b0bf..3f24ecbe2576 100644
--- a/drivers/mfd/88pm80x.c
+++ b/drivers/mfd/88pm80x.c
@@ -135,7 +135,7 @@ EXPORT_SYMBOL_GPL(pm80x_deinit);
#ifdef CONFIG_PM_SLEEP
static int pm80x_suspend(struct device *dev)
{
- struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *client = to_i2c_client(dev);
struct pm80x_chip *chip = i2c_get_clientdata(client);
if (chip && chip->wu_flag)
@@ -147,7 +147,7 @@ static int pm80x_suspend(struct device *dev)
static int pm80x_resume(struct device *dev)
{
- struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *client = to_i2c_client(dev);
struct pm80x_chip *chip = i2c_get_clientdata(client);
if (chip && chip->wu_flag)
diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c
index 3269a9990b24..25e1aafae60c 100644
--- a/drivers/mfd/88pm860x-core.c
+++ b/drivers/mfd/88pm860x-core.c
@@ -705,10 +705,12 @@ int pm8606_osc_disable(struct pm860x_chip *chip, unsigned short client)
chip->osc_status);
mutex_lock(&chip->osc_lock);
- /*Update voting status */
+ /* Update voting status */
chip->osc_vote &= ~(client);
- /* If reference group is off and this is the last client to release
- * - turn off */
+ /*
+ * If reference group is off and this is the last client to release
+ * - turn off
+ */
if ((chip->osc_status != PM8606_REF_GP_OSC_OFF) &&
(chip->osc_vote == REF_GP_NO_CLIENTS)) {
chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN;
@@ -1218,7 +1220,7 @@ static int pm860x_remove(struct i2c_client *client)
#ifdef CONFIG_PM_SLEEP
static int pm860x_suspend(struct device *dev)
{
- struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *client = to_i2c_client(dev);
struct pm860x_chip *chip = i2c_get_clientdata(client);
if (device_may_wakeup(dev) && chip->wakeup_flag)
@@ -1228,7 +1230,7 @@ static int pm860x_suspend(struct device *dev)
static int pm860x_resume(struct device *dev)
{
- struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *client = to_i2c_client(dev);
struct pm860x_chip *chip = i2c_get_clientdata(client);
if (device_may_wakeup(dev) && chip->wakeup_flag)
@@ -1265,6 +1267,7 @@ static struct i2c_driver pm860x_driver = {
static int __init pm860x_i2c_init(void)
{
int ret;
+
ret = i2c_add_driver(&pm860x_driver);
if (ret != 0)
pr_err("Failed to register 88PM860x I2C driver: %d\n", ret);
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 4d92df6ef9fe..9ca66de0c1c1 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -211,7 +211,7 @@ config MFD_DA9062
of the device.
config MFD_DA9063
- bool "Dialog Semiconductor DA9063 PMIC Support"
+ tristate "Dialog Semiconductor DA9063 PMIC Support"
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
@@ -1370,24 +1370,30 @@ config MFD_ARIZONA
bool
config MFD_ARIZONA_I2C
- tristate "Wolfson Microelectronics Arizona platform with I2C"
+ tristate "Cirrus Logic/Wolfson Microelectronics Arizona platform with I2C"
select MFD_ARIZONA
select MFD_CORE
select REGMAP_I2C
depends on I2C
help
- Support for the Wolfson Microelectronics Arizona platform audio SoC
- core functionality controlled via I2C.
+ Support for the Cirrus Logic/Wolfson Microelectronics Arizona platform
+ audio SoC core functionality controlled via I2C.
config MFD_ARIZONA_SPI
- tristate "Wolfson Microelectronics Arizona platform with SPI"
+ tristate "Cirrus Logic/Wolfson Microelectronics Arizona platform with SPI"
select MFD_ARIZONA
select MFD_CORE
select REGMAP_SPI
depends on SPI_MASTER
help
- Support for the Wolfson Microelectronics Arizona platform audio SoC
- core functionality controlled via I2C.
+ Support for the Cirrus Logic/Wolfson Microelectronics Arizona platform
+ audio SoC core functionality controlled via I2C.
+
+config MFD_CS47L24
+ bool "Cirrus Logic CS47L24 and WM1831"
+ depends on MFD_ARIZONA
+ help
+ Support for Cirrus Logic CS47L24 and WM1831 low power audio SoC
config MFD_WM5102
bool "Wolfson Microelectronics WM5102"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index a8b76b81b467..0f230a6103f8 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -51,6 +51,9 @@ endif
ifeq ($(CONFIG_MFD_WM8998),y)
obj-$(CONFIG_MFD_ARIZONA) += wm8998-tables.o
endif
+ifeq ($(CONFIG_MFD_CS47L24),y)
+obj-$(CONFIG_MFD_ARIZONA) += cs47l24-tables.o
+endif
obj-$(CONFIG_MFD_WM8400) += wm8400-core.o
wm831x-objs := wm831x-core.o wm831x-irq.o wm831x-otp.o
wm831x-objs += wm831x-auxadc.o
@@ -61,7 +64,8 @@ wm8350-objs := wm8350-core.o wm8350-regmap.o wm8350-gpio.o
wm8350-objs += wm8350-irq.o
obj-$(CONFIG_MFD_WM8350) += wm8350.o
obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o
-obj-$(CONFIG_MFD_WM8994) += wm8994-core.o wm8994-irq.o wm8994-regmap.o
+wm8994-objs := wm8994-core.o wm8994-irq.o wm8994-regmap.o
+obj-$(CONFIG_MFD_WM8994) += wm8994.o
obj-$(CONFIG_TPS6105X) += tps6105x.o
obj-$(CONFIG_TPS65010) += tps65010.o
diff --git a/drivers/mfd/aat2870-core.c b/drivers/mfd/aat2870-core.c
index 29b6a2d4ac72..3ba19a45f199 100644
--- a/drivers/mfd/aat2870-core.c
+++ b/drivers/mfd/aat2870-core.c
@@ -373,11 +373,8 @@ static int aat2870_i2c_probe(struct i2c_client *client,
aat2870 = devm_kzalloc(&client->dev, sizeof(struct aat2870_data),
GFP_KERNEL);
- if (!aat2870) {
- dev_err(&client->dev,
- "Failed to allocate memory for aat2870\n");
+ if (!aat2870)
return -ENOMEM;
- }
aat2870->dev = &client->dev;
dev_set_drvdata(aat2870->dev, aat2870);
diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c
index f0afb44271f8..6a5a98806cb8 100644
--- a/drivers/mfd/ab3100-core.c
+++ b/drivers/mfd/ab3100-core.c
@@ -381,9 +381,11 @@ static int ab3100_event_registers_startup_state_get(struct device *dev,
u8 *event)
{
struct ab3100 *ab3100 = dev_get_drvdata(dev->parent);
+
if (!ab3100->startup_events_read)
return -EAGAIN; /* Try again later */
memcpy(event, ab3100->startup_events, 3);
+
return 0;
}
@@ -858,10 +860,8 @@ static int ab3100_probe(struct i2c_client *client,
int i;
ab3100 = devm_kzalloc(&client->dev, sizeof(struct ab3100), GFP_KERNEL);
- if (!ab3100) {
- dev_err(&client->dev, "could not allocate AB3100 device\n");
+ if (!ab3100)
return -ENOMEM;
- }
/* Initialize data structure */
mutex_init(&ab3100->access_mutex);
@@ -883,20 +883,17 @@ static int ab3100_probe(struct i2c_client *client,
for (i = 0; ids[i].id != 0x0; i++) {
if (ids[i].id == ab3100->chip_id) {
- if (ids[i].name != NULL) {
- snprintf(&ab3100->chip_name[0],
- sizeof(ab3100->chip_name) - 1,
- "AB3100 %s",
- ids[i].name);
+ if (ids[i].name)
break;
- } else {
- dev_err(&client->dev,
- "AB3000 is not supported\n");
- goto exit_no_detect;
- }
+
+ dev_err(&client->dev, "AB3000 is not supported\n");
+ goto exit_no_detect;
}
}
+ snprintf(&ab3100->chip_name[0],
+ sizeof(ab3100->chip_name) - 1, "AB3100 %s", ids[i].name);
+
if (ids[i].id == 0x0) {
dev_err(&client->dev, "unknown analog baseband chip id: 0x%x\n",
ab3100->chip_id);
diff --git a/drivers/mfd/ab3100-otp.c b/drivers/mfd/ab3100-otp.c
index f391c5fee1b0..55b207a4b336 100644
--- a/drivers/mfd/ab3100-otp.c
+++ b/drivers/mfd/ab3100-otp.c
@@ -188,10 +188,9 @@ static int __init ab3100_otp_probe(struct platform_device *pdev)
int i;
otp = devm_kzalloc(&pdev->dev, sizeof(struct ab3100_otp), GFP_KERNEL);
- if (!otp) {
- dev_err(&pdev->dev, "could not allocate AB3100 OTP device\n");
+ if (!otp)
return -ENOMEM;
- }
+
otp->dev = &pdev->dev;
/* Replace platform data coming in with a local struct */
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index fefbe4cfa61d..f3d689176fc2 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -113,7 +113,7 @@
#define AB8500_SWITCH_OFF_STATUS 0x00
#define AB8500_TURN_ON_STATUS 0x00
-#define AB8505_TURN_ON_STATUS_2 0x04
+#define AB8505_TURN_ON_STATUS_2 0x04
#define AB8500_CH_USBCH_STAT1_REG 0x02
#define VBUS_DET_DBNC100 0x02
@@ -211,7 +211,7 @@ static int set_register_interruptible(struct ab8500 *ab8500, u8 bank,
/*
* Put the u8 bank and u8 register together into a an u16.
* The bank on higher 8 bits and register in lower 8 bits.
- * */
+ */
u16 addr = ((u16)bank) << 8 | reg;
dev_vdbg(ab8500->dev, "wr: addr %#x <= %#x\n", addr, data);
@@ -243,8 +243,6 @@ static int get_register_interruptible(struct ab8500 *ab8500, u8 bank,
u8 reg, u8 *value)
{
int ret;
- /* put the u8 bank and u8 reg together into a an u16.
- * bank on higher 8 bits and reg in lower */
u16 addr = ((u16)bank) << 8 | reg;
mutex_lock(&ab8500->lock);
@@ -278,8 +276,6 @@ static int mask_and_set_register_interruptible(struct ab8500 *ab8500, u8 bank,
u8 reg, u8 bitmask, u8 bitvalues)
{
int ret;
- /* put the u8 bank and u8 reg together into a an u16.
- * bank on higher 8 bits and reg in lower */
u16 addr = ((u16)bank) << 8 | reg;
mutex_lock(&ab8500->lock);
@@ -449,12 +445,12 @@ static void update_latch_offset(u8 *offset, int i)
{
/* Fix inconsistent ITFromLatch25 bit mapping... */
if (unlikely(*offset == 17))
- *offset = 24;
+ *offset = 24;
/* Fix inconsistent ab8540 bit mapping... */
if (unlikely(*offset == 16))
- *offset = 25;
+ *offset = 25;
if ((i == 3) && (*offset >= 24))
- *offset += 2;
+ *offset += 2;
}
static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500,
@@ -590,12 +586,12 @@ static int ab8500_irq_init(struct ab8500 *ab8500, struct device_node *np)
/* If ->irq_base is zero this will give a linear mapping */
ab8500->domain = irq_domain_add_simple(ab8500->dev->of_node,
- num_irqs, 0,
- &ab8500_irq_ops, ab8500);
+ num_irqs, 0,
+ &ab8500_irq_ops, ab8500);
if (!ab8500->domain) {
dev_err(ab8500->dev, "Failed to create irqdomain\n");
- return -ENOSYS;
+ return -ENODEV;
}
return 0;
@@ -609,442 +605,28 @@ int ab8500_suspend(struct ab8500 *ab8500)
return 0;
}
-static struct resource ab8500_gpadc_resources[] = {
- {
- .name = "HW_CONV_END",
- .start = AB8500_INT_GP_HW_ADC_CONV_END,
- .end = AB8500_INT_GP_HW_ADC_CONV_END,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "SW_CONV_END",
- .start = AB8500_INT_GP_SW_ADC_CONV_END,
- .end = AB8500_INT_GP_SW_ADC_CONV_END,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct resource ab8505_gpadc_resources[] = {
- {
- .name = "SW_CONV_END",
- .start = AB8500_INT_GP_SW_ADC_CONV_END,
- .end = AB8500_INT_GP_SW_ADC_CONV_END,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct resource ab8500_rtc_resources[] = {
- {
- .name = "60S",
- .start = AB8500_INT_RTC_60S,
- .end = AB8500_INT_RTC_60S,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "ALARM",
- .start = AB8500_INT_RTC_ALARM,
- .end = AB8500_INT_RTC_ALARM,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct resource ab8540_rtc_resources[] = {
- {
- .name = "1S",
- .start = AB8540_INT_RTC_1S,
- .end = AB8540_INT_RTC_1S,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "ALARM",
- .start = AB8500_INT_RTC_ALARM,
- .end = AB8500_INT_RTC_ALARM,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct resource ab8500_poweronkey_db_resources[] = {
- {
- .name = "ONKEY_DBF",
- .start = AB8500_INT_PON_KEY1DB_F,
- .end = AB8500_INT_PON_KEY1DB_F,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "ONKEY_DBR",
- .start = AB8500_INT_PON_KEY1DB_R,
- .end = AB8500_INT_PON_KEY1DB_R,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct resource ab8500_av_acc_detect_resources[] = {
- {
- .name = "ACC_DETECT_1DB_F",
- .start = AB8500_INT_ACC_DETECT_1DB_F,
- .end = AB8500_INT_ACC_DETECT_1DB_F,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "ACC_DETECT_1DB_R",
- .start = AB8500_INT_ACC_DETECT_1DB_R,
- .end = AB8500_INT_ACC_DETECT_1DB_R,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "ACC_DETECT_21DB_F",
- .start = AB8500_INT_ACC_DETECT_21DB_F,
- .end = AB8500_INT_ACC_DETECT_21DB_F,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "ACC_DETECT_21DB_R",
- .start = AB8500_INT_ACC_DETECT_21DB_R,
- .end = AB8500_INT_ACC_DETECT_21DB_R,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "ACC_DETECT_22DB_F",
- .start = AB8500_INT_ACC_DETECT_22DB_F,
- .end = AB8500_INT_ACC_DETECT_22DB_F,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "ACC_DETECT_22DB_R",
- .start = AB8500_INT_ACC_DETECT_22DB_R,
- .end = AB8500_INT_ACC_DETECT_22DB_R,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct resource ab8500_charger_resources[] = {
- {
- .name = "MAIN_CH_UNPLUG_DET",
- .start = AB8500_INT_MAIN_CH_UNPLUG_DET,
- .end = AB8500_INT_MAIN_CH_UNPLUG_DET,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "MAIN_CHARGE_PLUG_DET",
- .start = AB8500_INT_MAIN_CH_PLUG_DET,
- .end = AB8500_INT_MAIN_CH_PLUG_DET,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "VBUS_DET_R",
- .start = AB8500_INT_VBUS_DET_R,
- .end = AB8500_INT_VBUS_DET_R,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "VBUS_DET_F",
- .start = AB8500_INT_VBUS_DET_F,
- .end = AB8500_INT_VBUS_DET_F,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "USB_LINK_STATUS",
- .start = AB8500_INT_USB_LINK_STATUS,
- .end = AB8500_INT_USB_LINK_STATUS,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "VBUS_OVV",
- .start = AB8500_INT_VBUS_OVV,
- .end = AB8500_INT_VBUS_OVV,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "USB_CH_TH_PROT_R",
- .start = AB8500_INT_USB_CH_TH_PROT_R,
- .end = AB8500_INT_USB_CH_TH_PROT_R,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "USB_CH_TH_PROT_F",
- .start = AB8500_INT_USB_CH_TH_PROT_F,
- .end = AB8500_INT_USB_CH_TH_PROT_F,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "MAIN_EXT_CH_NOT_OK",
- .start = AB8500_INT_MAIN_EXT_CH_NOT_OK,
- .end = AB8500_INT_MAIN_EXT_CH_NOT_OK,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "MAIN_CH_TH_PROT_R",
- .start = AB8500_INT_MAIN_CH_TH_PROT_R,
- .end = AB8500_INT_MAIN_CH_TH_PROT_R,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "MAIN_CH_TH_PROT_F",
- .start = AB8500_INT_MAIN_CH_TH_PROT_F,
- .end = AB8500_INT_MAIN_CH_TH_PROT_F,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "USB_CHARGER_NOT_OKR",
- .start = AB8500_INT_USB_CHARGER_NOT_OKR,
- .end = AB8500_INT_USB_CHARGER_NOT_OKR,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "CH_WD_EXP",
- .start = AB8500_INT_CH_WD_EXP,
- .end = AB8500_INT_CH_WD_EXP,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "VBUS_CH_DROP_END",
- .start = AB8500_INT_VBUS_CH_DROP_END,
- .end = AB8500_INT_VBUS_CH_DROP_END,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct resource ab8500_btemp_resources[] = {
- {
- .name = "BAT_CTRL_INDB",
- .start = AB8500_INT_BAT_CTRL_INDB,
- .end = AB8500_INT_BAT_CTRL_INDB,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "BTEMP_LOW",
- .start = AB8500_INT_BTEMP_LOW,
- .end = AB8500_INT_BTEMP_LOW,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "BTEMP_HIGH",
- .start = AB8500_INT_BTEMP_HIGH,
- .end = AB8500_INT_BTEMP_HIGH,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "BTEMP_LOW_MEDIUM",
- .start = AB8500_INT_BTEMP_LOW_MEDIUM,
- .end = AB8500_INT_BTEMP_LOW_MEDIUM,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "BTEMP_MEDIUM_HIGH",
- .start = AB8500_INT_BTEMP_MEDIUM_HIGH,
- .end = AB8500_INT_BTEMP_MEDIUM_HIGH,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct resource ab8500_fg_resources[] = {
- {
- .name = "NCONV_ACCU",
- .start = AB8500_INT_CCN_CONV_ACC,
- .end = AB8500_INT_CCN_CONV_ACC,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "BATT_OVV",
- .start = AB8500_INT_BATT_OVV,
- .end = AB8500_INT_BATT_OVV,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "LOW_BAT_F",
- .start = AB8500_INT_LOW_BAT_F,
- .end = AB8500_INT_LOW_BAT_F,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "LOW_BAT_R",
- .start = AB8500_INT_LOW_BAT_R,
- .end = AB8500_INT_LOW_BAT_R,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "CC_INT_CALIB",
- .start = AB8500_INT_CC_INT_CALIB,
- .end = AB8500_INT_CC_INT_CALIB,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "CCEOC",
- .start = AB8500_INT_CCEOC,
- .end = AB8500_INT_CCEOC,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct resource ab8500_chargalg_resources[] = {};
-
-#ifdef CONFIG_DEBUG_FS
-static struct resource ab8500_debug_resources[] = {
- {
- .name = "IRQ_AB8500",
- /*
- * Number will be filled in. NOTE: this is deliberately
- * not flagged as an IRQ in ordet to avoid remapping using
- * the irqdomain in the MFD core, so that this IRQ passes
- * unremapped to the debug code.
- */
- },
- {
- .name = "IRQ_FIRST",
- .start = AB8500_INT_MAIN_EXT_CH_NOT_OK,
- .end = AB8500_INT_MAIN_EXT_CH_NOT_OK,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "IRQ_LAST",
- .start = AB8500_INT_XTAL32K_KO,
- .end = AB8500_INT_XTAL32K_KO,
- .flags = IORESOURCE_IRQ,
- },
-};
-#endif
-
-static struct resource ab8500_usb_resources[] = {
- {
- .name = "ID_WAKEUP_R",
- .start = AB8500_INT_ID_WAKEUP_R,
- .end = AB8500_INT_ID_WAKEUP_R,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "ID_WAKEUP_F",
- .start = AB8500_INT_ID_WAKEUP_F,
- .end = AB8500_INT_ID_WAKEUP_F,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "VBUS_DET_F",
- .start = AB8500_INT_VBUS_DET_F,
- .end = AB8500_INT_VBUS_DET_F,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "VBUS_DET_R",
- .start = AB8500_INT_VBUS_DET_R,
- .end = AB8500_INT_VBUS_DET_R,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "USB_LINK_STATUS",
- .start = AB8500_INT_USB_LINK_STATUS,
- .end = AB8500_INT_USB_LINK_STATUS,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "USB_ADP_PROBE_PLUG",
- .start = AB8500_INT_ADP_PROBE_PLUG,
- .end = AB8500_INT_ADP_PROBE_PLUG,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "USB_ADP_PROBE_UNPLUG",
- .start = AB8500_INT_ADP_PROBE_UNPLUG,
- .end = AB8500_INT_ADP_PROBE_UNPLUG,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct resource ab8505_iddet_resources[] = {
- {
- .name = "KeyDeglitch",
- .start = AB8505_INT_KEYDEGLITCH,
- .end = AB8505_INT_KEYDEGLITCH,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "KP",
- .start = AB8505_INT_KP,
- .end = AB8505_INT_KP,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "IKP",
- .start = AB8505_INT_IKP,
- .end = AB8505_INT_IKP,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "IKR",
- .start = AB8505_INT_IKR,
- .end = AB8505_INT_IKR,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "KeyStuck",
- .start = AB8505_INT_KEYSTUCK,
- .end = AB8505_INT_KEYSTUCK,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "VBUS_DET_R",
- .start = AB8500_INT_VBUS_DET_R,
- .end = AB8500_INT_VBUS_DET_R,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "VBUS_DET_F",
- .start = AB8500_INT_VBUS_DET_F,
- .end = AB8500_INT_VBUS_DET_F,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "ID_DET_PLUGR",
- .start = AB8500_INT_ID_DET_PLUGR,
- .end = AB8500_INT_ID_DET_PLUGR,
- .flags = IORESOURCE_IRQ,
- },
- {
- .name = "ID_DET_PLUGF",
- .start = AB8500_INT_ID_DET_PLUGF,
- .end = AB8500_INT_ID_DET_PLUGF,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct resource ab8500_temp_resources[] = {
- {
- .name = "ABX500_TEMP_WARM",
- .start = AB8500_INT_TEMP_WARM,
- .end = AB8500_INT_TEMP_WARM,
- .flags = IORESOURCE_IRQ,
- },
-};
-
static const struct mfd_cell ab8500_bm_devs[] = {
{
.name = "ab8500-charger",
.of_compatible = "stericsson,ab8500-charger",
- .num_resources = ARRAY_SIZE(ab8500_charger_resources),
- .resources = ab8500_charger_resources,
.platform_data = &ab8500_bm_data,
.pdata_size = sizeof(ab8500_bm_data),
},
{
.name = "ab8500-btemp",
.of_compatible = "stericsson,ab8500-btemp",
- .num_resources = ARRAY_SIZE(ab8500_btemp_resources),
- .resources = ab8500_btemp_resources,
.platform_data = &ab8500_bm_data,
.pdata_size = sizeof(ab8500_bm_data),
},
{
.name = "ab8500-fg",
.of_compatible = "stericsson,ab8500-fg",
- .num_resources = ARRAY_SIZE(ab8500_fg_resources),
- .resources = ab8500_fg_resources,
.platform_data = &ab8500_bm_data,
.pdata_size = sizeof(ab8500_bm_data),
},
{
.name = "ab8500-chargalg",
.of_compatible = "stericsson,ab8500-chargalg",
- .num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
- .resources = ab8500_chargalg_resources,
.platform_data = &ab8500_bm_data,
.pdata_size = sizeof(ab8500_bm_data),
},
@@ -1055,8 +637,6 @@ static const struct mfd_cell ab8500_devs[] = {
{
.name = "ab8500-debug",
.of_compatible = "stericsson,ab8500-debug",
- .num_resources = ARRAY_SIZE(ab8500_debug_resources),
- .resources = ab8500_debug_resources,
},
#endif
{
@@ -1078,27 +658,19 @@ static const struct mfd_cell ab8500_devs[] = {
{
.name = "ab8500-gpadc",
.of_compatible = "stericsson,ab8500-gpadc",
- .num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
- .resources = ab8500_gpadc_resources,
},
{
.name = "ab8500-rtc",
.of_compatible = "stericsson,ab8500-rtc",
- .num_resources = ARRAY_SIZE(ab8500_rtc_resources),
- .resources = ab8500_rtc_resources,
},
{
.name = "ab8500-acc-det",
.of_compatible = "stericsson,ab8500-acc-det",
- .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
- .resources = ab8500_av_acc_detect_resources,
},
{
.name = "ab8500-poweron-key",
.of_compatible = "stericsson,ab8500-poweron-key",
- .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
- .resources = ab8500_poweronkey_db_resources,
},
{
.name = "ab8500-pwm",
@@ -1126,14 +698,10 @@ static const struct mfd_cell ab8500_devs[] = {
{
.name = "abx500-temp",
.of_compatible = "stericsson,abx500-temp",
- .num_resources = ARRAY_SIZE(ab8500_temp_resources),
- .resources = ab8500_temp_resources,
},
{
.name = "ab8500-usb",
.of_compatible = "stericsson,ab8500-usb",
- .num_resources = ARRAY_SIZE(ab8500_usb_resources),
- .resources = ab8500_usb_resources,
},
{
.name = "ab8500-codec",
@@ -1145,8 +713,6 @@ static const struct mfd_cell ab9540_devs[] = {
#ifdef CONFIG_DEBUG_FS
{
.name = "ab8500-debug",
- .num_resources = ARRAY_SIZE(ab8500_debug_resources),
- .resources = ab8500_debug_resources,
},
#endif
{
@@ -1165,23 +731,15 @@ static const struct mfd_cell ab9540_devs[] = {
{
.name = "ab8500-gpadc",
.of_compatible = "stericsson,ab8500-gpadc",
- .num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
- .resources = ab8500_gpadc_resources,
},
{
.name = "ab8500-rtc",
- .num_resources = ARRAY_SIZE(ab8500_rtc_resources),
- .resources = ab8500_rtc_resources,
},
{
.name = "ab8500-acc-det",
- .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
- .resources = ab8500_av_acc_detect_resources,
},
{
.name = "ab8500-poweron-key",
- .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
- .resources = ab8500_poweronkey_db_resources,
},
{
.name = "ab8500-pwm",
@@ -1189,8 +747,6 @@ static const struct mfd_cell ab9540_devs[] = {
},
{
.name = "abx500-temp",
- .num_resources = ARRAY_SIZE(ab8500_temp_resources),
- .resources = ab8500_temp_resources,
},
{
.name = "pinctrl-ab9540",
@@ -1198,16 +754,12 @@ static const struct mfd_cell ab9540_devs[] = {
},
{
.name = "ab9540-usb",
- .num_resources = ARRAY_SIZE(ab8500_usb_resources),
- .resources = ab8500_usb_resources,
},
{
.name = "ab9540-codec",
},
{
.name = "ab-iddet",
- .num_resources = ARRAY_SIZE(ab8505_iddet_resources),
- .resources = ab8505_iddet_resources,
},
};
@@ -1216,8 +768,6 @@ static const struct mfd_cell ab8505_devs[] = {
#ifdef CONFIG_DEBUG_FS
{
.name = "ab8500-debug",
- .num_resources = ARRAY_SIZE(ab8500_debug_resources),
- .resources = ab8500_debug_resources,
},
#endif
{
@@ -1233,23 +783,15 @@ static const struct mfd_cell ab8505_devs[] = {
{
.name = "ab8500-gpadc",
.of_compatible = "stericsson,ab8500-gpadc",
- .num_resources = ARRAY_SIZE(ab8505_gpadc_resources),
- .resources = ab8505_gpadc_resources,
},
{
.name = "ab8500-rtc",
- .num_resources = ARRAY_SIZE(ab8500_rtc_resources),
- .resources = ab8500_rtc_resources,
},
{
.name = "ab8500-acc-det",
- .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
- .resources = ab8500_av_acc_detect_resources,
},
{
.name = "ab8500-poweron-key",
- .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
- .resources = ab8500_poweronkey_db_resources,
},
{
.name = "ab8500-pwm",
@@ -1260,16 +802,12 @@ static const struct mfd_cell ab8505_devs[] = {
},
{
.name = "ab8500-usb",
- .num_resources = ARRAY_SIZE(ab8500_usb_resources),
- .resources = ab8500_usb_resources,
},
{
.name = "ab8500-codec",
},
{
.name = "ab-iddet",
- .num_resources = ARRAY_SIZE(ab8505_iddet_resources),
- .resources = ab8505_iddet_resources,
},
};
@@ -1277,8 +815,6 @@ static const struct mfd_cell ab8540_devs[] = {
#ifdef CONFIG_DEBUG_FS
{
.name = "ab8500-debug",
- .num_resources = ARRAY_SIZE(ab8500_debug_resources),
- .resources = ab8500_debug_resources,
},
#endif
{
@@ -1297,18 +833,12 @@ static const struct mfd_cell ab8540_devs[] = {
{
.name = "ab8500-gpadc",
.of_compatible = "stericsson,ab8500-gpadc",
- .num_resources = ARRAY_SIZE(ab8505_gpadc_resources),
- .resources = ab8505_gpadc_resources,
},
{
.name = "ab8500-acc-det",
- .num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
- .resources = ab8500_av_acc_detect_resources,
},
{
.name = "ab8500-poweron-key",
- .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
- .resources = ab8500_poweronkey_db_resources,
},
{
.name = "ab8500-pwm",
@@ -1316,24 +846,18 @@ static const struct mfd_cell ab8540_devs[] = {
},
{
.name = "abx500-temp",
- .num_resources = ARRAY_SIZE(ab8500_temp_resources),
- .resources = ab8500_temp_resources,
},
{
.name = "pinctrl-ab8540",
},
{
.name = "ab8540-usb",
- .num_resources = ARRAY_SIZE(ab8500_usb_resources),
- .resources = ab8500_usb_resources,
},
{
.name = "ab8540-codec",
},
{
.name = "ab-iddet",
- .num_resources = ARRAY_SIZE(ab8505_iddet_resources),
- .resources = ab8505_iddet_resources,
},
};
@@ -1341,8 +865,6 @@ static const struct mfd_cell ab8540_cut1_devs[] = {
{
.name = "ab8500-rtc",
.of_compatible = "stericsson,ab8500-rtc",
- .num_resources = ARRAY_SIZE(ab8500_rtc_resources),
- .resources = ab8500_rtc_resources,
},
};
@@ -1350,8 +872,6 @@ static const struct mfd_cell ab8540_cut2_devs[] = {
{
.name = "ab8540-rtc",
.of_compatible = "stericsson,ab8540-rtc",
- .num_resources = ARRAY_SIZE(ab8540_rtc_resources),
- .resources = ab8540_rtc_resources,
},
};
@@ -1549,7 +1069,7 @@ static struct attribute_group ab9540_attr_group = {
static int ab8500_probe(struct platform_device *pdev)
{
- static const char *switch_off_status[] = {
+ static const char * const switch_off_status[] = {
"Swoff bit programming",
"Thermal protection activation",
"Vbat lower then BattOk falling threshold",
@@ -1558,7 +1078,7 @@ static int ab8500_probe(struct platform_device *pdev)
"Battery level lower than power on reset threshold",
"Power on key 1 pressed longer than 10 seconds",
"DB8500 thermal shutdown"};
- static const char *turn_on_status[] = {
+ static const char * const turn_on_status[] = {
"Battery rising (Vbat)",
"Power On Key 1 dbF",
"Power On Key 2 dbF",
@@ -1750,12 +1270,6 @@ static int ab8500_probe(struct platform_device *pdev)
if (ret)
return ret;
-#ifdef CONFIG_DEBUG_FS
- /* Pass to debugfs */
- ab8500_debug_resources[0].start = ab8500->irq;
- ab8500_debug_resources[0].end = ab8500->irq;
-#endif
-
if (is_ab9540(ab8500))
ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
ARRAY_SIZE(ab9540_devs), NULL,
diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c
index 0236cd7cdce4..69d9fffe5b5c 100644
--- a/drivers/mfd/ab8500-debugfs.c
+++ b/drivers/mfd/ab8500-debugfs.c
@@ -242,8 +242,10 @@ static struct ab8500_prcmu_ranges ab8500_debug_ranges[AB8500_NUM_BANKS] = {
.first = 0x40,
.last = 0x44,
},
- /* 0x80-0x8B is SIM registers and should
- * not be accessed from here */
+ /*
+ * 0x80-0x8B are SIM registers and should
+ * not be accessed from here
+ */
},
},
[AB8500_USB] = {
@@ -587,8 +589,10 @@ static struct ab8500_prcmu_ranges ab8505_debug_ranges[AB8500_NUM_BANKS] = {
.first = 0x40,
.last = 0x48,
},
- /* 0x80-0x8B is SIM registers and should
- * not be accessed from here */
+ /*
+ * 0x80-0x8B are SIM registers and should
+ * not be accessed from here
+ */
},
},
[AB8500_USB] = {
@@ -1306,8 +1310,10 @@ static int ab8500_registers_print(struct device *dev, u32 bank,
if (s) {
seq_printf(s, " [0x%02X/0x%02X]: 0x%02X\n",
bank, reg, value);
- /* Error is not returned here since
- * the output is wanted in any case */
+ /*
+ * Error is not returned here since
+ * the output is wanted in any case
+ */
if (seq_has_overflowed(s))
return 0;
} else {
@@ -2740,10 +2746,9 @@ static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg,
*cfg = loc;
#ifdef ABB_HWREG_DEBUG
- pr_warn("HWREG request: %s, %s,\n"
- " addr=0x%08X, mask=0x%X, shift=%d" "value=0x%X\n",
- (write) ? "write" : "read",
- REG_FMT_DEC(cfg) ? "decimal" : "hexa",
+ pr_warn("HWREG request: %s, %s,\n", (write) ? "write" : "read",
+ REG_FMT_DEC(cfg) ? "decimal" : "hexa");
+ pr_warn(" addr=0x%08X, mask=0x%X, shift=%d" "value=0x%X\n",
cfg->addr, cfg->mask, cfg->shift, val);
#endif
diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c
index c51c1b188d64..97dcadc8fa8b 100644
--- a/drivers/mfd/ab8500-gpadc.c
+++ b/drivers/mfd/ab8500-gpadc.c
@@ -49,61 +49,61 @@
* OTP register offsets
* Bank : 0x15
*/
-#define AB8500_GPADC_CAL_1 0x0F
-#define AB8500_GPADC_CAL_2 0x10
-#define AB8500_GPADC_CAL_3 0x11
-#define AB8500_GPADC_CAL_4 0x12
-#define AB8500_GPADC_CAL_5 0x13
-#define AB8500_GPADC_CAL_6 0x14
-#define AB8500_GPADC_CAL_7 0x15
+#define AB8500_GPADC_CAL_1 0x0F
+#define AB8500_GPADC_CAL_2 0x10
+#define AB8500_GPADC_CAL_3 0x11
+#define AB8500_GPADC_CAL_4 0x12
+#define AB8500_GPADC_CAL_5 0x13
+#define AB8500_GPADC_CAL_6 0x14
+#define AB8500_GPADC_CAL_7 0x15
/* New calibration for 8540 */
#define AB8540_GPADC_OTP4_REG_7 0x38
#define AB8540_GPADC_OTP4_REG_6 0x39
#define AB8540_GPADC_OTP4_REG_5 0x3A
/* gpadc constants */
-#define EN_VINTCORE12 0x04
-#define EN_VTVOUT 0x02
-#define EN_GPADC 0x01
-#define DIS_GPADC 0x00
-#define AVG_1 0x00
-#define AVG_4 0x20
-#define AVG_8 0x40
-#define AVG_16 0x60
-#define ADC_SW_CONV 0x04
-#define EN_ICHAR 0x80
-#define BTEMP_PULL_UP 0x08
-#define EN_BUF 0x40
-#define DIS_ZERO 0x00
-#define GPADC_BUSY 0x01
-#define EN_FALLING 0x10
-#define EN_TRIG_EDGE 0x02
-#define EN_VBIAS_XTAL_TEMP 0x02
+#define EN_VINTCORE12 0x04
+#define EN_VTVOUT 0x02
+#define EN_GPADC 0x01
+#define DIS_GPADC 0x00
+#define AVG_1 0x00
+#define AVG_4 0x20
+#define AVG_8 0x40
+#define AVG_16 0x60
+#define ADC_SW_CONV 0x04
+#define EN_ICHAR 0x80
+#define BTEMP_PULL_UP 0x08
+#define EN_BUF 0x40
+#define DIS_ZERO 0x00
+#define GPADC_BUSY 0x01
+#define EN_FALLING 0x10
+#define EN_TRIG_EDGE 0x02
+#define EN_VBIAS_XTAL_TEMP 0x02
/* GPADC constants from AB8500 spec, UM0836 */
-#define ADC_RESOLUTION 1024
-#define ADC_CH_BTEMP_MIN 0
-#define ADC_CH_BTEMP_MAX 1350
-#define ADC_CH_DIETEMP_MIN 0
-#define ADC_CH_DIETEMP_MAX 1350
-#define ADC_CH_CHG_V_MIN 0
-#define ADC_CH_CHG_V_MAX 20030
-#define ADC_CH_ACCDET2_MIN 0
-#define ADC_CH_ACCDET2_MAX 2500
-#define ADC_CH_VBAT_MIN 2300
-#define ADC_CH_VBAT_MAX 4800
-#define ADC_CH_CHG_I_MIN 0
-#define ADC_CH_CHG_I_MAX 1500
-#define ADC_CH_BKBAT_MIN 0
-#define ADC_CH_BKBAT_MAX 3200
+#define ADC_RESOLUTION 1024
+#define ADC_CH_BTEMP_MIN 0
+#define ADC_CH_BTEMP_MAX 1350
+#define ADC_CH_DIETEMP_MIN 0
+#define ADC_CH_DIETEMP_MAX 1350
+#define ADC_CH_CHG_V_MIN 0
+#define ADC_CH_CHG_V_MAX 20030
+#define ADC_CH_ACCDET2_MIN 0
+#define ADC_CH_ACCDET2_MAX 2500
+#define ADC_CH_VBAT_MIN 2300
+#define ADC_CH_VBAT_MAX 4800
+#define ADC_CH_CHG_I_MIN 0
+#define ADC_CH_CHG_I_MAX 1500
+#define ADC_CH_BKBAT_MIN 0
+#define ADC_CH_BKBAT_MAX 3200
/* GPADC constants from AB8540 spec */
-#define ADC_CH_IBAT_MIN (-6000) /* mA range measured by ADC for ibat*/
-#define ADC_CH_IBAT_MAX 6000
-#define ADC_CH_IBAT_MIN_V (-60) /* mV range measured by ADC for ibat*/
-#define ADC_CH_IBAT_MAX_V 60
-#define IBAT_VDROP_L (-56) /* mV */
-#define IBAT_VDROP_H 56
+#define ADC_CH_IBAT_MIN (-6000) /* mA range measured by ADC for ibat */
+#define ADC_CH_IBAT_MAX 6000
+#define ADC_CH_IBAT_MIN_V (-60) /* mV range measured by ADC for ibat */
+#define ADC_CH_IBAT_MAX_V 60
+#define IBAT_VDROP_L (-56) /* mV */
+#define IBAT_VDROP_H 56
/* This is used to not lose precision when dividing to get gain and offset */
#define CALIB_SCALE 1000
@@ -179,7 +179,7 @@ struct ab8500_gpadc *ab8500_gpadc_get(char *name)
list_for_each_entry(gpadc, &ab8500_gpadc_list, node) {
if (!strcmp(name, dev_name(gpadc->dev)))
- return gpadc;
+ return gpadc;
}
return ERR_PTR(-ENOENT);
@@ -315,11 +315,12 @@ int ab8500_gpadc_sw_hw_convert(struct ab8500_gpadc *gpadc, u8 channel,
ad_value = ab8500_gpadc_read_raw(gpadc, channel, avg_sample,
trig_edge, trig_timer, conv_type);
-/* On failure retry a second time */
+
+ /* On failure retry a second time */
if (ad_value < 0)
ad_value = ab8500_gpadc_read_raw(gpadc, channel, avg_sample,
trig_edge, trig_timer, conv_type);
-if (ad_value < 0) {
+ if (ad_value < 0) {
dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n",
channel);
return ad_value;
@@ -327,8 +328,9 @@ if (ad_value < 0) {
voltage = ab8500_gpadc_ad_to_voltage(gpadc, channel, ad_value);
if (voltage < 0)
- dev_err(gpadc->dev, "GPADC to voltage conversion failed ch:"
- " %d AD: 0x%x\n", channel, ad_value);
+ dev_err(gpadc->dev,
+ "GPADC to voltage conversion failed ch: %d AD: 0x%x\n",
+ channel, ad_value);
return voltage;
}
@@ -348,10 +350,9 @@ EXPORT_SYMBOL(ab8500_gpadc_sw_hw_convert);
int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type)
{
- int raw_data;
- raw_data = ab8500_gpadc_double_read_raw(gpadc, channel,
- avg_sample, trig_edge, trig_timer, conv_type, NULL);
- return raw_data;
+ return ab8500_gpadc_double_read_raw(gpadc, channel, avg_sample,
+ trig_edge, trig_timer, conv_type,
+ NULL);
}
int ab8500_gpadc_double_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
@@ -388,7 +389,7 @@ int ab8500_gpadc_double_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
goto out;
if (!(val & GPADC_BUSY))
break;
- msleep(10);
+ msleep(20);
} while (++looplimit < 10);
if (looplimit >= 10 && (val & GPADC_BUSY)) {
dev_err(gpadc->dev, "gpadc_conversion: GPADC busy");
@@ -421,8 +422,7 @@ int ab8500_gpadc_double_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
val_reg1 |= EN_TRIG_EDGE;
if (trig_edge)
val_reg1 |= EN_FALLING;
- }
- else
+ } else
ret = abx500_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL2_REG, val);
if (ret < 0) {
@@ -449,7 +449,7 @@ int ab8500_gpadc_double_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
* remove when hardware will be availible
*/
delay_min = 1000; /* Delay in micro seconds */
- delay_max = 10000; /* large range to optimise sleep mode */
+ delay_max = 10000; /* large range optimises sleepmode */
break;
}
/* Intentional fallthrough */
@@ -785,9 +785,10 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
<< CALIB_SHIFT_IBAT)
/ (ADC_CH_IBAT_MAX_V - ADC_CH_IBAT_MIN_V);
- gpadc->cal_data[ADC_INPUT_IBAT].gain = V_gain * V2A_gain;
- gpadc->cal_data[ADC_INPUT_IBAT].offset = V_offset *
- V2A_gain + V2A_offset;
+ gpadc->cal_data[ADC_INPUT_IBAT].gain =
+ V_gain * V2A_gain;
+ gpadc->cal_data[ADC_INPUT_IBAT].offset =
+ V_offset * V2A_gain + V2A_offset;
} else {
gpadc->cal_data[ADC_INPUT_IBAT].gain = 0;
}
@@ -923,11 +924,10 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
int ret = 0;
struct ab8500_gpadc *gpadc;
- gpadc = devm_kzalloc(&pdev->dev, sizeof(struct ab8500_gpadc), GFP_KERNEL);
- if (!gpadc) {
- dev_err(&pdev->dev, "Error: No memory\n");
+ gpadc = devm_kzalloc(&pdev->dev,
+ sizeof(struct ab8500_gpadc), GFP_KERNEL);
+ if (!gpadc)
return -ENOMEM;
- }
gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END");
if (gpadc->irq_sw < 0)
@@ -1072,18 +1072,19 @@ void ab8540_gpadc_get_otp(struct ab8500_gpadc *gpadc,
*vmain_h = gpadc->cal_data[ADC_INPUT_VMAIN].otp_calib_hi;
*btemp_l = gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_lo;
*btemp_h = gpadc->cal_data[ADC_INPUT_BTEMP].otp_calib_hi;
- *vbat_l = gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_lo;
- *vbat_h = gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_hi;
- *ibat_l = gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_lo;
- *ibat_h = gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_hi;
- return ;
+ *vbat_l = gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_lo;
+ *vbat_h = gpadc->cal_data[ADC_INPUT_VBAT].otp_calib_hi;
+ *ibat_l = gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_lo;
+ *ibat_h = gpadc->cal_data[ADC_INPUT_IBAT].otp_calib_hi;
}
subsys_initcall_sync(ab8500_gpadc_init);
module_exit(ab8500_gpadc_exit);
MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Arun R Murthy, Daniel Willerud, Johan Palsson,"
- "M'boumba Cedric Madianga");
+MODULE_AUTHOR("Arun R Murthy");
+MODULE_AUTHOR("Daniel Willerud");
+MODULE_AUTHOR("Johan Palsson");
+MODULE_AUTHOR("M'boumba Cedric Madianga");
MODULE_ALIAS("platform:ab8500_gpadc");
MODULE_DESCRIPTION("AB8500 GPADC driver");
diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c
index 0d1825696153..b9f0010309f9 100644
--- a/drivers/mfd/ab8500-sysctrl.c
+++ b/drivers/mfd/ab8500-sysctrl.c
@@ -27,7 +27,7 @@ static void ab8500_power_off(void)
{
sigset_t old;
sigset_t all;
- static char *pss[] = {"ab8500_ac", "pm2301", "ab8500_usb"};
+ static const char * const pss[] = {"ab8500_ac", "pm2301", "ab8500_usb"};
int i;
bool charger_present = false;
union power_supply_propval val;
@@ -68,10 +68,9 @@ static void ab8500_power_off(void)
ret = power_supply_get_property(psy,
POWER_SUPPLY_PROP_TECHNOLOGY, &val);
if (!ret && val.intval != POWER_SUPPLY_TECHNOLOGY_UNKNOWN) {
- printk(KERN_INFO
- "Charger \"%s\" is connected with known battery."
- " Rebooting.\n",
- pss[i]);
+ pr_info("Charger '%s' is connected with known battery",
+ pss[i]);
+ pr_info(" - Rebooting.\n");
machine_restart("charging");
}
power_supply_put(psy);
@@ -161,8 +160,8 @@ static int ab8500_sysctrl_probe(struct platform_device *pdev)
pdata->initial_req_buf_config[j]);
if (ret < 0) {
dev_err(&pdev->dev,
- "unable to set sysClkReq%dRfClkBuf: "
- "%d\n", j + 1, ret);
+ "Can't set sysClkReq%dRfClkBuf: %d\n",
+ j + 1, ret);
}
}
}
diff --git a/drivers/mfd/adp5520.c b/drivers/mfd/adp5520.c
index ae88654595dc..d817f202da5b 100644
--- a/drivers/mfd/adp5520.c
+++ b/drivers/mfd/adp5520.c
@@ -9,10 +9,10 @@
*
* Derived from da903x:
* Copyright (C) 2008 Compulab, Ltd.
- * Mike Rapoport <mike@compulab.co.il>
+ * Mike Rapoport <mike@compulab.co.il>
*
* Copyright (C) 2006-2008 Marvell International Ltd.
- * Eric Miao <eric.miao@marvell.com>
+ * Eric Miao <eric.miao@marvell.com>
*
* Licensed under the GPL-2 or later.
*/
@@ -355,7 +355,7 @@ static struct i2c_driver adp5520_driver = {
},
.probe = adp5520_probe,
.remove = adp5520_remove,
- .id_table = adp5520_id,
+ .id_table = adp5520_id,
};
module_i2c_driver(adp5520_driver);
diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
index d474732cc65c..5319f252790b 100644
--- a/drivers/mfd/arizona-core.c
+++ b/drivers/mfd/arizona-core.c
@@ -238,7 +238,7 @@ static int arizona_poll_reg(struct arizona *arizona,
if ((val & mask) == target)
return 0;
- msleep(1);
+ usleep_range(1000, 5000);
}
dev_err(arizona->dev, "Polling reg %u timed out: %x\n", reg, val);
@@ -279,14 +279,14 @@ static void arizona_disable_reset(struct arizona *arizona)
case WM5110:
case WM8280:
/* Meet requirements for minimum reset duration */
- msleep(5);
+ usleep_range(5000, 10000);
break;
default:
break;
}
gpio_set_value_cansleep(arizona->pdata.reset, 1);
- msleep(1);
+ usleep_range(1000, 5000);
}
}
@@ -598,6 +598,12 @@ static int arizona_runtime_resume(struct device *dev)
goto err;
}
break;
+ case WM1831:
+ case CS47L24:
+ ret = arizona_wait_for_boot(arizona);
+ if (ret != 0)
+ goto err;
+ break;
default:
ret = arizona_wait_for_boot(arizona);
if (ret != 0)
@@ -682,6 +688,9 @@ static int arizona_runtime_suspend(struct device *dev)
}
}
break;
+ case WM1831:
+ case CS47L24:
+ break;
default:
jd_active = arizona_is_jack_det_active(arizona);
if (jd_active < 0)
@@ -852,6 +861,16 @@ static int arizona_of_get_core_pdata(struct arizona *arizona)
count++;
}
+ count = 0;
+ of_property_for_each_u32(arizona->dev->of_node, "wlf,out-mono", prop,
+ cur, val) {
+ if (count == ARRAY_SIZE(pdata->out_mono))
+ break;
+
+ pdata->out_mono[count] = !!val;
+ count++;
+ }
+
return 0;
}
@@ -862,6 +881,8 @@ const struct of_device_id arizona_of_match[] = {
{ .compatible = "wlf,wm8997", .data = (void *)WM8997 },
{ .compatible = "wlf,wm8998", .data = (void *)WM8998 },
{ .compatible = "wlf,wm1814", .data = (void *)WM1814 },
+ { .compatible = "wlf,wm1831", .data = (void *)WM1831 },
+ { .compatible = "cirrus,cs47l24", .data = (void *)CS47L24 },
{},
};
EXPORT_SYMBOL_GPL(arizona_of_match);
@@ -919,6 +940,23 @@ static const struct mfd_cell wm5110_devs[] = {
},
};
+static const char * const cs47l24_supplies[] = {
+ "MICVDD",
+ "CPVDD",
+ "SPKVDD",
+};
+
+static const struct mfd_cell cs47l24_devs[] = {
+ { .name = "arizona-gpio" },
+ { .name = "arizona-haptics" },
+ { .name = "arizona-pwm" },
+ {
+ .name = "cs47l24-codec",
+ .parent_supplies = cs47l24_supplies,
+ .num_parent_supplies = ARRAY_SIZE(cs47l24_supplies),
+ },
+};
+
static const char * const wm8997_supplies[] = {
"MICVDD",
"DBVDD2",
@@ -963,7 +1001,7 @@ static const struct mfd_cell wm8998_devs[] = {
int arizona_dev_init(struct arizona *arizona)
{
struct device *dev = arizona->dev;
- const char *type_name;
+ const char *type_name = NULL;
unsigned int reg, val, mask;
int (*apply_patch)(struct arizona *) = NULL;
const struct mfd_cell *subdevs = NULL;
@@ -987,6 +1025,8 @@ int arizona_dev_init(struct arizona *arizona)
case WM8997:
case WM8998:
case WM1814:
+ case WM1831:
+ case CS47L24:
for (i = 0; i < ARRAY_SIZE(wm5102_core_supplies); i++)
arizona->core_supplies[i].supply
= wm5102_core_supplies[i];
@@ -1001,11 +1041,18 @@ int arizona_dev_init(struct arizona *arizona)
/* Mark DCVDD as external, LDO1 driver will clear if internal */
arizona->external_dcvdd = true;
- ret = mfd_add_devices(arizona->dev, -1, early_devs,
- ARRAY_SIZE(early_devs), NULL, 0, NULL);
- if (ret != 0) {
- dev_err(dev, "Failed to add early children: %d\n", ret);
- return ret;
+ switch (arizona->type) {
+ case WM1831:
+ case CS47L24:
+ break; /* No LDO1 regulator */
+ default:
+ ret = mfd_add_devices(arizona->dev, -1, early_devs,
+ ARRAY_SIZE(early_devs), NULL, 0, NULL);
+ if (ret != 0) {
+ dev_err(dev, "Failed to add early children: %d\n", ret);
+ return ret;
+ }
+ break;
}
ret = devm_regulator_bulk_get(dev, arizona->num_core_supplies,
@@ -1069,6 +1116,7 @@ int arizona_dev_init(struct arizona *arizona)
case 0x5102:
case 0x5110:
case 0x6349:
+ case 0x6363:
case 0x8997:
break;
default:
@@ -1084,7 +1132,7 @@ int arizona_dev_init(struct arizona *arizona)
goto err_reset;
}
- msleep(1);
+ usleep_range(1000, 5000);
}
/* Ensure device startup is complete */
@@ -1167,6 +1215,30 @@ int arizona_dev_init(struct arizona *arizona)
n_subdevs = ARRAY_SIZE(wm5110_devs);
}
break;
+ case 0x6363:
+ if (IS_ENABLED(CONFIG_MFD_CS47L24)) {
+ switch (arizona->type) {
+ case CS47L24:
+ type_name = "CS47L24";
+ break;
+
+ case WM1831:
+ type_name = "WM1831";
+ break;
+
+ default:
+ dev_warn(arizona->dev,
+ "CS47L24 registered as %d\n",
+ arizona->type);
+ arizona->type = CS47L24;
+ break;
+ }
+
+ apply_patch = cs47l24_patch;
+ subdevs = cs47l24_devs;
+ n_subdevs = ARRAY_SIZE(cs47l24_devs);
+ }
+ break;
case 0x8997:
if (IS_ENABLED(CONFIG_MFD_WM8997)) {
type_name = "WM8997";
diff --git a/drivers/mfd/arizona-i2c.c b/drivers/mfd/arizona-i2c.c
index 4e3afd1861fc..5fe12961cfe5 100644
--- a/drivers/mfd/arizona-i2c.c
+++ b/drivers/mfd/arizona-i2c.c
@@ -88,7 +88,9 @@ static int arizona_i2c_probe(struct i2c_client *i2c,
static int arizona_i2c_remove(struct i2c_client *i2c)
{
struct arizona *arizona = dev_get_drvdata(&i2c->dev);
+
arizona_dev_exit(arizona);
+
return 0;
}
diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c
index 3d425e93ce84..5fef014920a3 100644
--- a/drivers/mfd/arizona-irq.c
+++ b/drivers/mfd/arizona-irq.c
@@ -30,11 +30,13 @@ static int arizona_map_irq(struct arizona *arizona, int irq)
{
int ret;
- ret = regmap_irq_get_virq(arizona->aod_irq_chip, irq);
- if (ret < 0)
- ret = regmap_irq_get_virq(arizona->irq_chip, irq);
+ if (arizona->aod_irq_chip) {
+ ret = regmap_irq_get_virq(arizona->aod_irq_chip, irq);
+ if (ret >= 0)
+ return ret;
+ }
- return ret;
+ return regmap_irq_get_virq(arizona->irq_chip, irq);
}
int arizona_request_irq(struct arizona *arizona, int irq, char *name,
@@ -107,8 +109,8 @@ static irqreturn_t arizona_irq_thread(int irq, void *data)
do {
poll = false;
- /* Always handle the AoD domain */
- handle_nested_irq(irq_find_mapping(arizona->virq, 0));
+ if (arizona->aod_irq_chip)
+ handle_nested_irq(irq_find_mapping(arizona->virq, 0));
/*
* Check if one of the main interrupts is asserted and only
@@ -219,6 +221,15 @@ int arizona_irq_init(struct arizona *arizona)
arizona->ctrlif_error = false;
break;
#endif
+#ifdef CONFIG_MFD_CS47L24
+ case WM1831:
+ case CS47L24:
+ aod = NULL;
+ irq = &cs47l24_irq;
+
+ arizona->ctrlif_error = false;
+ break;
+#endif
#ifdef CONFIG_MFD_WM8997
case WM8997:
aod = &wm8997_aod;
@@ -291,13 +302,16 @@ int arizona_irq_init(struct arizona *arizona)
goto err;
}
- ret = regmap_add_irq_chip(arizona->regmap,
- irq_create_mapping(arizona->virq, 0),
- IRQF_ONESHOT, 0, aod,
- &arizona->aod_irq_chip);
- if (ret != 0) {
- dev_err(arizona->dev, "Failed to add AOD IRQs: %d\n", ret);
- goto err_domain;
+ if (aod) {
+ ret = regmap_add_irq_chip(arizona->regmap,
+ irq_create_mapping(arizona->virq, 0),
+ IRQF_ONESHOT, 0, aod,
+ &arizona->aod_irq_chip);
+ if (ret != 0) {
+ dev_err(arizona->dev,
+ "Failed to add AOD IRQs: %d\n", ret);
+ goto err;
+ }
}
ret = regmap_add_irq_chip(arizona->regmap,
@@ -309,30 +323,6 @@ int arizona_irq_init(struct arizona *arizona)
goto err_aod;
}
- /* Make sure the boot done IRQ is unmasked for resumes */
- i = arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE);
- ret = request_threaded_irq(i, NULL, arizona_boot_done, IRQF_ONESHOT,
- "Boot done", arizona);
- if (ret != 0) {
- dev_err(arizona->dev, "Failed to request boot done %d: %d\n",
- arizona->irq, ret);
- goto err_boot_done;
- }
-
- /* Handle control interface errors in the core */
- if (arizona->ctrlif_error) {
- i = arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR);
- ret = request_threaded_irq(i, NULL, arizona_ctrlif_err,
- IRQF_ONESHOT,
- "Control interface error", arizona);
- if (ret != 0) {
- dev_err(arizona->dev,
- "Failed to request CTRLIF_ERR %d: %d\n",
- arizona->irq, ret);
- goto err_ctrlif;
- }
- }
-
/* Used to emulate edge trigger and to work around broken pinmux */
if (arizona->pdata.irq_gpio) {
if (gpio_to_irq(arizona->pdata.irq_gpio) != arizona->irq) {
@@ -362,21 +352,42 @@ int arizona_irq_init(struct arizona *arizona)
goto err_main_irq;
}
+ /* Make sure the boot done IRQ is unmasked for resumes */
+ i = arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE);
+ ret = request_threaded_irq(i, NULL, arizona_boot_done, IRQF_ONESHOT,
+ "Boot done", arizona);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to request boot done %d: %d\n",
+ arizona->irq, ret);
+ goto err_boot_done;
+ }
+
+ /* Handle control interface errors in the core */
+ if (arizona->ctrlif_error) {
+ i = arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR);
+ ret = request_threaded_irq(i, NULL, arizona_ctrlif_err,
+ IRQF_ONESHOT,
+ "Control interface error", arizona);
+ if (ret != 0) {
+ dev_err(arizona->dev,
+ "Failed to request CTRLIF_ERR %d: %d\n",
+ arizona->irq, ret);
+ goto err_ctrlif;
+ }
+ }
+
return 0;
-err_main_irq:
- if (arizona->ctrlif_error)
- free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_CTRLIF_ERR),
- arizona);
err_ctrlif:
free_irq(arizona_map_irq(arizona, ARIZONA_IRQ_BOOT_DONE), arizona);
err_boot_done:
+ free_irq(arizona->irq, arizona);
+err_main_irq:
regmap_del_irq_chip(irq_create_mapping(arizona->virq, 1),
arizona->irq_chip);
err_aod:
regmap_del_irq_chip(irq_create_mapping(arizona->virq, 0),
arizona->aod_irq_chip);
-err_domain:
err:
return ret;
}
diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c
index befbc89bfd34..5c1ccdeb9b70 100644
--- a/drivers/mfd/arizona-spi.c
+++ b/drivers/mfd/arizona-spi.c
@@ -46,6 +46,11 @@ static int arizona_spi_probe(struct spi_device *spi)
if (IS_ENABLED(CONFIG_MFD_WM5110))
regmap_config = &wm5110_spi_regmap;
break;
+ case WM1831:
+ case CS47L24:
+ if (IS_ENABLED(CONFIG_MFD_CS47L24))
+ regmap_config = &cs47l24_spi_regmap;
+ break;
default:
dev_err(&spi->dev, "Unknown device type %ld\n", type);
return -EINVAL;
@@ -89,6 +94,8 @@ static const struct spi_device_id arizona_spi_ids[] = {
{ "wm5102", WM5102 },
{ "wm5110", WM5110 },
{ "wm8280", WM8280 },
+ { "wm1831", WM1831 },
+ { "cs47l24", CS47L24 },
{ },
};
MODULE_DEVICE_TABLE(spi, arizona_spi_ids);
diff --git a/drivers/mfd/arizona.h b/drivers/mfd/arizona.h
index 3af12e938f57..198e9cea77f9 100644
--- a/drivers/mfd/arizona.h
+++ b/drivers/mfd/arizona.h
@@ -25,6 +25,8 @@ extern const struct regmap_config wm5102_spi_regmap;
extern const struct regmap_config wm5110_i2c_regmap;
extern const struct regmap_config wm5110_spi_regmap;
+extern const struct regmap_config cs47l24_spi_regmap;
+
extern const struct regmap_config wm8997_i2c_regmap;
extern const struct regmap_config wm8998_i2c_regmap;
@@ -40,6 +42,8 @@ extern const struct regmap_irq_chip wm5110_aod;
extern const struct regmap_irq_chip wm5110_irq;
extern const struct regmap_irq_chip wm5110_revd_irq;
+extern const struct regmap_irq_chip cs47l24_irq;
+
extern const struct regmap_irq_chip wm8997_aod;
extern const struct regmap_irq_chip wm8997_irq;
diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c
index d001f7e238f5..94d67a6e1eb7 100644
--- a/drivers/mfd/as3711.c
+++ b/drivers/mfd/as3711.c
@@ -136,17 +136,13 @@ static int as3711_i2c_probe(struct i2c_client *client,
} else {
pdata = devm_kzalloc(&client->dev,
sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
- dev_err(&client->dev, "Failed to allocate pdata\n");
+ if (!pdata)
return -ENOMEM;
- }
}
as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL);
- if (!as3711) {
- dev_err(&client->dev, "Memory allocation failed\n");
+ if (!as3711)
return -ENOMEM;
- }
as3711->dev = &client->dev;
i2c_set_clientdata(client, as3711);
@@ -157,7 +153,8 @@ static int as3711_i2c_probe(struct i2c_client *client,
as3711->regmap = devm_regmap_init_i2c(client, &as3711_regmap_config);
if (IS_ERR(as3711->regmap)) {
ret = PTR_ERR(as3711->regmap);
- dev_err(&client->dev, "regmap initialization failed: %d\n", ret);
+ dev_err(&client->dev,
+ "regmap initialization failed: %d\n", ret);
return ret;
}
@@ -172,12 +169,19 @@ static int as3711_i2c_probe(struct i2c_client *client,
return -ENODEV;
dev_info(as3711->dev, "AS3711 detected: %x:%x\n", id1, id2);
- /* We can reuse as3711_subdevs[], it will be copied in mfd_add_devices() */
+ /*
+ * We can reuse as3711_subdevs[],
+ * it will be copied in mfd_add_devices()
+ */
if (pdata) {
- as3711_subdevs[AS3711_REGULATOR].platform_data = &pdata->regulator;
- as3711_subdevs[AS3711_REGULATOR].pdata_size = sizeof(pdata->regulator);
- as3711_subdevs[AS3711_BACKLIGHT].platform_data = &pdata->backlight;
- as3711_subdevs[AS3711_BACKLIGHT].pdata_size = sizeof(pdata->backlight);
+ as3711_subdevs[AS3711_REGULATOR].platform_data =
+ &pdata->regulator;
+ as3711_subdevs[AS3711_REGULATOR].pdata_size =
+ sizeof(pdata->regulator);
+ as3711_subdevs[AS3711_BACKLIGHT].platform_data =
+ &pdata->backlight;
+ as3711_subdevs[AS3711_BACKLIGHT].pdata_size =
+ sizeof(pdata->backlight);
} else {
as3711_subdevs[AS3711_REGULATOR].platform_data = NULL;
as3711_subdevs[AS3711_REGULATOR].pdata_size = 0;
diff --git a/drivers/mfd/as3722.c b/drivers/mfd/as3722.c
index 924ea90494ae..e1f597f97f86 100644
--- a/drivers/mfd/as3722.c
+++ b/drivers/mfd/as3722.c
@@ -405,6 +405,8 @@ static int as3722_i2c_probe(struct i2c_client *i2c,
goto scrub;
}
+ device_init_wakeup(as3722->dev, true);
+
dev_dbg(as3722->dev, "AS3722 core driver initialized successfully\n");
return 0;
@@ -422,6 +424,29 @@ static int as3722_i2c_remove(struct i2c_client *i2c)
return 0;
}
+static int __maybe_unused as3722_i2c_suspend(struct device *dev)
+{
+ struct as3722 *as3722 = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(as3722->chip_irq);
+ disable_irq(as3722->chip_irq);
+
+ return 0;
+}
+
+static int __maybe_unused as3722_i2c_resume(struct device *dev)
+{
+ struct as3722 *as3722 = dev_get_drvdata(dev);
+
+ enable_irq(as3722->chip_irq);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(as3722->chip_irq);
+
+ return 0;
+}
+
static const struct of_device_id as3722_of_match[] = {
{ .compatible = "ams,as3722", },
{},
@@ -434,10 +459,15 @@ static const struct i2c_device_id as3722_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, as3722_i2c_id);
+static const struct dev_pm_ops as3722_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(as3722_i2c_suspend, as3722_i2c_resume)
+};
+
static struct i2c_driver as3722_i2c_driver = {
.driver = {
.name = "as3722",
.of_match_table = as3722_of_match,
+ .pm = &as3722_pm_ops,
},
.probe = as3722_i2c_probe,
.remove = as3722_i2c_remove,
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c
index a726f01e3b02..4dca6bc61f5b 100644
--- a/drivers/mfd/asic3.c
+++ b/drivers/mfd/asic3.c
@@ -167,7 +167,6 @@ static void asic3_irq_demux(struct irq_desc *desc)
base = ASIC3_GPIO_A_BASE
+ bank * ASIC3_GPIO_BASE_INCR;
-
spin_lock_irqsave(&asic->lock, flags);
istat = asic3_read_register(asic,
base +
@@ -502,7 +501,8 @@ static int asic3_gpio_get(struct gpio_chip *chip,
return -EINVAL;
}
- return asic3_read_register(asic, gpio_base + ASIC3_GPIO_STATUS) & mask;
+ return !!(asic3_read_register(asic,
+ gpio_base + ASIC3_GPIO_STATUS) & mask);
}
static void asic3_gpio_set(struct gpio_chip *chip,
@@ -536,8 +536,6 @@ static void asic3_gpio_set(struct gpio_chip *chip,
asic3_write_register(asic, gpio_base + ASIC3_GPIO_OUT, out_reg);
spin_unlock_irqrestore(&asic->lock, flags);
-
- return;
}
static int asic3_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
@@ -665,18 +663,18 @@ static int ds1wm_enable(struct platform_device *pdev)
asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX0]);
asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX1]);
asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_OWM]);
- msleep(1);
+ usleep_range(1000, 5000);
/* Reset and enable DS1WM */
asic3_set_register(asic, ASIC3_OFFSET(EXTCF, RESET),
ASIC3_EXTCF_OWM_RESET, 1);
- msleep(1);
+ usleep_range(1000, 5000);
asic3_set_register(asic, ASIC3_OFFSET(EXTCF, RESET),
ASIC3_EXTCF_OWM_RESET, 0);
- msleep(1);
+ usleep_range(1000, 5000);
asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
ASIC3_EXTCF_OWM_EN, 1);
- msleep(1);
+ usleep_range(1000, 5000);
return 0;
}
@@ -757,7 +755,7 @@ static int asic3_mmc_enable(struct platform_device *pdev)
* when HCLK is stopped.
*/
asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX1]);
- msleep(1);
+ usleep_range(1000, 5000);
/* HCLK 24.576 MHz, BCLK 12.288 MHz: */
asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL),
@@ -765,7 +763,7 @@ static int asic3_mmc_enable(struct platform_device *pdev)
asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_SD_HOST]);
asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_SD_BUS]);
- msleep(1);
+ usleep_range(1000, 5000);
asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
ASIC3_EXTCF_SD_MEM_ENABLE, 1);
@@ -841,7 +839,7 @@ static int asic3_leds_suspend(struct platform_device *pdev)
struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
while (asic3_gpio_get(&asic->gpio, ASIC3_GPIO(C, cell->id)) != 0)
- msleep(1);
+ usleep_range(1000, 5000);
asic3_clk_disable(asic, &asic->clocks[clock_ledn[cell->id]]);
@@ -900,8 +898,8 @@ static int __init asic3_mfd_probe(struct platform_device *pdev,
/* MMC */
if (mem_sdio) {
- asic->tmio_cnf = ioremap((ASIC3_SD_CONFIG_BASE >> asic->bus_shift) +
- mem_sdio->start,
+ asic->tmio_cnf = ioremap((ASIC3_SD_CONFIG_BASE >>
+ asic->bus_shift) + mem_sdio->start,
ASIC3_SD_CONFIG_SIZE >> asic->bus_shift);
if (!asic->tmio_cnf) {
ret = -ENOMEM;
@@ -962,10 +960,8 @@ static int __init asic3_probe(struct platform_device *pdev)
asic = devm_kzalloc(&pdev->dev,
sizeof(struct asic3), GFP_KERNEL);
- if (asic == NULL) {
- printk(KERN_ERR "kzalloc failed\n");
+ if (!asic)
return -ENOMEM;
- }
spin_lock_init(&asic->lock);
platform_set_drvdata(pdev, asic);
@@ -1074,7 +1070,9 @@ static struct platform_driver asic3_device_driver = {
static int __init asic3_init(void)
{
int retval = 0;
+
retval = platform_driver_probe(&asic3_device_driver, asic3_probe);
+
return retval;
}
diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c
index 56a466469664..9f70de1e4c70 100644
--- a/drivers/mfd/cros_ec_i2c.c
+++ b/drivers/mfd/cros_ec_i2c.c
@@ -292,7 +292,7 @@ static int cros_ec_i2c_probe(struct i2c_client *client,
struct cros_ec_device *ec_dev = NULL;
int err;
- ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);
+ ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);
if (!ec_dev)
return -ENOMEM;
diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c
index 6a0f6ec67c6b..ebe9b9477cb2 100644
--- a/drivers/mfd/cros_ec_spi.c
+++ b/drivers/mfd/cros_ec_spi.c
@@ -113,7 +113,7 @@ static int terminate_request(struct cros_ec_device *ec_dev)
trans.delay_usecs = ec_spi->end_of_msg_delay;
spi_message_add_tail(&trans, &msg);
- ret = spi_sync(ec_spi->spi, &msg);
+ ret = spi_sync_locked(ec_spi->spi, &msg);
/* Reset end-of-response timer */
ec_spi->last_transfer_ns = ktime_get_ns();
@@ -147,7 +147,7 @@ static int receive_n_bytes(struct cros_ec_device *ec_dev, u8 *buf, int n)
spi_message_init(&msg);
spi_message_add_tail(&trans, &msg);
- ret = spi_sync(ec_spi->spi, &msg);
+ ret = spi_sync_locked(ec_spi->spi, &msg);
if (ret < 0)
dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
@@ -175,7 +175,7 @@ static int cros_ec_spi_receive_packet(struct cros_ec_device *ec_dev,
unsigned long deadline;
int todo;
- BUG_ON(EC_MSG_PREAMBLE_COUNT > ec_dev->din_size);
+ BUG_ON(ec_dev->din_size < EC_MSG_PREAMBLE_COUNT);
/* Receive data until we see the header byte */
deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS);
@@ -283,7 +283,7 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,
unsigned long deadline;
int todo;
- BUG_ON(EC_MSG_PREAMBLE_COUNT > ec_dev->din_size);
+ BUG_ON(ec_dev->din_size < EC_MSG_PREAMBLE_COUNT);
/* Receive data until we see the header byte */
deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS);
@@ -391,10 +391,10 @@ static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev,
}
rx_buf = kzalloc(len, GFP_KERNEL);
- if (!rx_buf) {
- ret = -ENOMEM;
- goto exit;
- }
+ if (!rx_buf)
+ return -ENOMEM;
+
+ spi_bus_lock(ec_spi->spi->master);
/*
* Leave a gap between CS assertion and clocking of data to allow the
@@ -414,7 +414,7 @@ static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev,
trans.len = len;
trans.cs_change = 1;
spi_message_add_tail(&trans, &msg);
- ret = spi_sync(ec_spi->spi, &msg);
+ ret = spi_sync_locked(ec_spi->spi, &msg);
/* Get the response */
if (!ret) {
@@ -440,6 +440,9 @@ static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev,
}
final_ret = terminate_request(ec_dev);
+
+ spi_bus_unlock(ec_spi->spi->master);
+
if (!ret)
ret = final_ret;
if (ret < 0)
@@ -520,10 +523,10 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
}
rx_buf = kzalloc(len, GFP_KERNEL);
- if (!rx_buf) {
- ret = -ENOMEM;
- goto exit;
- }
+ if (!rx_buf)
+ return -ENOMEM;
+
+ spi_bus_lock(ec_spi->spi->master);
/* Transmit phase - send our message */
debug_packet(ec_dev->dev, "out", ec_dev->dout, len);
@@ -534,7 +537,7 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
trans.cs_change = 1;
spi_message_init(&msg);
spi_message_add_tail(&trans, &msg);
- ret = spi_sync(ec_spi->spi, &msg);
+ ret = spi_sync_locked(ec_spi->spi, &msg);
/* Get the response */
if (!ret) {
@@ -560,6 +563,9 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
}
final_ret = terminate_request(ec_dev);
+
+ spi_bus_unlock(ec_spi->spi->master);
+
if (!ret)
ret = final_ret;
if (ret < 0)
diff --git a/drivers/mfd/cs47l24-tables.c b/drivers/mfd/cs47l24-tables.c
new file mode 100644
index 000000000000..870800657594
--- /dev/null
+++ b/drivers/mfd/cs47l24-tables.c
@@ -0,0 +1,1629 @@
+/*
+ * Data tables for CS47L24 codec
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Author: Richard Fitzgerald <rf@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+#include <linux/device.h>
+
+#include "arizona.h"
+
+#define CS47L24_NUM_ISR 5
+
+static const struct reg_sequence cs47l24_reva_patch[] = {
+ { 0x80, 0x3 },
+ { 0x27C, 0x0010 },
+ { 0x221, 0x0070 },
+ { 0x80, 0x0 },
+};
+
+int cs47l24_patch(struct arizona *arizona)
+{
+ return regmap_register_patch(arizona->regmap,
+ cs47l24_reva_patch,
+ ARRAY_SIZE(cs47l24_reva_patch));
+}
+EXPORT_SYMBOL_GPL(cs47l24_patch);
+
+static const struct regmap_irq cs47l24_irqs[ARIZONA_NUM_IRQ] = {
+ [ARIZONA_IRQ_GP2] = { .reg_offset = 0, .mask = ARIZONA_GP2_EINT1 },
+ [ARIZONA_IRQ_GP1] = { .reg_offset = 0, .mask = ARIZONA_GP1_EINT1 },
+
+ [ARIZONA_IRQ_DSP3_RAM_RDY] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP3_RAM_RDY_EINT1
+ },
+ [ARIZONA_IRQ_DSP2_RAM_RDY] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP2_RAM_RDY_EINT1
+ },
+ [ARIZONA_IRQ_DSP_IRQ8] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP_IRQ8_EINT1
+ },
+ [ARIZONA_IRQ_DSP_IRQ7] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP_IRQ7_EINT1
+ },
+ [ARIZONA_IRQ_DSP_IRQ6] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP_IRQ6_EINT1
+ },
+ [ARIZONA_IRQ_DSP_IRQ5] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP_IRQ5_EINT1
+ },
+ [ARIZONA_IRQ_DSP_IRQ4] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP_IRQ4_EINT1
+ },
+ [ARIZONA_IRQ_DSP_IRQ3] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP_IRQ3_EINT1
+ },
+ [ARIZONA_IRQ_DSP_IRQ2] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP_IRQ2_EINT1
+ },
+ [ARIZONA_IRQ_DSP_IRQ1] = {
+ .reg_offset = 1, .mask = ARIZONA_DSP_IRQ1_EINT1
+ },
+
+ [ARIZONA_IRQ_SPK_OVERHEAT_WARN] = {
+ .reg_offset = 2, .mask = ARIZONA_SPK_OVERHEAT_WARN_EINT1
+ },
+ [ARIZONA_IRQ_SPK_OVERHEAT] = {
+ .reg_offset = 2, .mask = ARIZONA_SPK_OVERHEAT_EINT1
+ },
+ [ARIZONA_IRQ_WSEQ_DONE] = {
+ .reg_offset = 2, .mask = ARIZONA_WSEQ_DONE_EINT1
+ },
+ [ARIZONA_IRQ_DRC2_SIG_DET] = {
+ .reg_offset = 2, .mask = ARIZONA_DRC2_SIG_DET_EINT1
+ },
+ [ARIZONA_IRQ_DRC1_SIG_DET] = {
+ .reg_offset = 2, .mask = ARIZONA_DRC1_SIG_DET_EINT1
+ },
+ [ARIZONA_IRQ_ASRC2_LOCK] = {
+ .reg_offset = 2, .mask = ARIZONA_ASRC2_LOCK_EINT1
+ },
+ [ARIZONA_IRQ_ASRC1_LOCK] = {
+ .reg_offset = 2, .mask = ARIZONA_ASRC1_LOCK_EINT1
+ },
+ [ARIZONA_IRQ_UNDERCLOCKED] = {
+ .reg_offset = 2, .mask = ARIZONA_UNDERCLOCKED_EINT1
+ },
+ [ARIZONA_IRQ_OVERCLOCKED] = {
+ .reg_offset = 2, .mask = ARIZONA_OVERCLOCKED_EINT1
+ },
+ [ARIZONA_IRQ_FLL2_LOCK] = {
+ .reg_offset = 2, .mask = ARIZONA_FLL2_LOCK_EINT1
+ },
+ [ARIZONA_IRQ_FLL1_LOCK] = {
+ .reg_offset = 2, .mask = ARIZONA_FLL1_LOCK_EINT1
+ },
+ [ARIZONA_IRQ_CLKGEN_ERR] = {
+ .reg_offset = 2, .mask = ARIZONA_CLKGEN_ERR_EINT1
+ },
+ [ARIZONA_IRQ_CLKGEN_ERR_ASYNC] = {
+ .reg_offset = 2, .mask = ARIZONA_CLKGEN_ERR_ASYNC_EINT1
+ },
+
+ [ARIZONA_IRQ_CTRLIF_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_V2_CTRLIF_ERR_EINT1
+ },
+ [ARIZONA_IRQ_MIXER_DROPPED_SAMPLES] = {
+ .reg_offset = 3, .mask = ARIZONA_V2_MIXER_DROPPED_SAMPLE_EINT1
+ },
+ [ARIZONA_IRQ_ASYNC_CLK_ENA_LOW] = {
+ .reg_offset = 3, .mask = ARIZONA_V2_ASYNC_CLK_ENA_LOW_EINT1
+ },
+ [ARIZONA_IRQ_SYSCLK_ENA_LOW] = {
+ .reg_offset = 3, .mask = ARIZONA_V2_SYSCLK_ENA_LOW_EINT1
+ },
+ [ARIZONA_IRQ_ISRC1_CFG_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_V2_ISRC1_CFG_ERR_EINT1
+ },
+ [ARIZONA_IRQ_ISRC2_CFG_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_V2_ISRC2_CFG_ERR_EINT1
+ },
+ [ARIZONA_IRQ_ISRC3_CFG_ERR] = {
+ .reg_offset = 3, .mask = ARIZONA_V2_ISRC3_CFG_ERR_EINT1
+ },
+ [ARIZONA_IRQ_HP1R_DONE] = {
+ .reg_offset = 3, .mask = ARIZONA_HP1R_DONE_EINT1
+ },
+ [ARIZONA_IRQ_HP1L_DONE] = {
+ .reg_offset = 3, .mask = ARIZONA_HP1L_DONE_EINT1
+ },
+
+ [ARIZONA_IRQ_BOOT_DONE] = {
+ .reg_offset = 4, .mask = ARIZONA_BOOT_DONE_EINT1
+ },
+ [ARIZONA_IRQ_ASRC_CFG_ERR] = {
+ .reg_offset = 4, .mask = ARIZONA_V2_ASRC_CFG_ERR_EINT1
+ },
+ [ARIZONA_IRQ_FLL2_CLOCK_OK] = {
+ .reg_offset = 4, .mask = ARIZONA_FLL2_CLOCK_OK_EINT1
+ },
+ [ARIZONA_IRQ_FLL1_CLOCK_OK] = {
+ .reg_offset = 4, .mask = ARIZONA_FLL1_CLOCK_OK_EINT1
+ },
+
+ [ARIZONA_IRQ_DSP_SHARED_WR_COLL] = {
+ .reg_offset = 5, .mask = ARIZONA_DSP_SHARED_WR_COLL_EINT1
+ },
+ [ARIZONA_IRQ_SPK_SHUTDOWN] = {
+ .reg_offset = 5, .mask = ARIZONA_SPK_SHUTDOWN_EINT1
+ },
+ [ARIZONA_IRQ_SPK1R_SHORT] = {
+ .reg_offset = 5, .mask = ARIZONA_SPK1R_SHORT_EINT1
+ },
+ [ARIZONA_IRQ_SPK1L_SHORT] = {
+ .reg_offset = 5, .mask = ARIZONA_SPK1L_SHORT_EINT1
+ },
+ [ARIZONA_IRQ_HP1R_SC_POS] = {
+ .reg_offset = 5, .mask = ARIZONA_HP1R_SC_POS_EINT1
+ },
+ [ARIZONA_IRQ_HP1L_SC_POS] = {
+ .reg_offset = 5, .mask = ARIZONA_HP1L_SC_POS_EINT1
+ },
+};
+
+const struct regmap_irq_chip cs47l24_irq = {
+ .name = "cs47l24 IRQ",
+ .status_base = ARIZONA_INTERRUPT_STATUS_1,
+ .mask_base = ARIZONA_INTERRUPT_STATUS_1_MASK,
+ .ack_base = ARIZONA_INTERRUPT_STATUS_1,
+ .num_regs = 6,
+ .irqs = cs47l24_irqs,
+ .num_irqs = ARRAY_SIZE(cs47l24_irqs),
+};
+EXPORT_SYMBOL_GPL(cs47l24_irq);
+
+static const struct reg_default cs47l24_reg_default[] = {
+ { 0x00000008, 0x0019 }, /* R8 - Ctrl IF SPI CFG 1 */
+ { 0x00000020, 0x0000 }, /* R32 - Tone Generator 1 */
+ { 0x00000021, 0x1000 }, /* R33 - Tone Generator 2 */
+ { 0x00000022, 0x0000 }, /* R34 - Tone Generator 3 */
+ { 0x00000023, 0x1000 }, /* R35 - Tone Generator 4 */
+ { 0x00000024, 0x0000 }, /* R36 - Tone Generator 5 */
+ { 0x00000030, 0x0000 }, /* R48 - PWM Drive 1 */
+ { 0x00000031, 0x0100 }, /* R49 - PWM Drive 2 */
+ { 0x00000032, 0x0100 }, /* R50 - PWM Drive 3 */
+ { 0x00000041, 0x0000 }, /* R65 - Sequence control */
+ { 0x00000061, 0x01FF }, /* R97 - Sample Rate Sequence Select 1 */
+ { 0x00000062, 0x01FF }, /* R98 - Sample Rate Sequence Select 2 */
+ { 0x00000063, 0x01FF }, /* R99 - Sample Rate Sequence Select 3 */
+ { 0x00000064, 0x01FF }, /* R100 - Sample Rate Sequence Select 4 */
+ { 0x00000070, 0x0000 }, /* R112 - Comfort Noise Generator */
+ { 0x00000090, 0x0000 }, /* R144 - Haptics Control 1 */
+ { 0x00000091, 0x7FFF }, /* R145 - Haptics Control 2 */
+ { 0x00000092, 0x0000 }, /* R146 - Haptics phase 1 intensity */
+ { 0x00000093, 0x0000 }, /* R147 - Haptics phase 1 duration */
+ { 0x00000094, 0x0000 }, /* R148 - Haptics phase 2 intensity */
+ { 0x00000095, 0x0000 }, /* R149 - Haptics phase 2 duration */
+ { 0x00000096, 0x0000 }, /* R150 - Haptics phase 3 intensity */
+ { 0x00000097, 0x0000 }, /* R151 - Haptics phase 3 duration */
+ { 0x00000100, 0x0002 }, /* R256 - Clock 32k 1 */
+ { 0x00000101, 0x0504 }, /* R257 - System Clock 1 */
+ { 0x00000102, 0x0011 }, /* R258 - Sample rate 1 */
+ { 0x00000103, 0x0011 }, /* R259 - Sample rate 2 */
+ { 0x00000104, 0x0011 }, /* R260 - Sample rate 3 */
+ { 0x00000112, 0x0305 }, /* R274 - Async clock 1 */
+ { 0x00000113, 0x0011 }, /* R275 - Async sample rate 1 */
+ { 0x00000114, 0x0011 }, /* R276 - Async sample rate 2 */
+ { 0x00000149, 0x0000 }, /* R329 - Output system clock */
+ { 0x0000014A, 0x0000 }, /* R330 - Output async clock */
+ { 0x00000152, 0x0000 }, /* R338 - Rate Estimator 1 */
+ { 0x00000153, 0x0000 }, /* R339 - Rate Estimator 2 */
+ { 0x00000154, 0x0000 }, /* R340 - Rate Estimator 3 */
+ { 0x00000155, 0x0000 }, /* R341 - Rate Estimator 4 */
+ { 0x00000156, 0x0000 }, /* R342 - Rate Estimator 5 */
+ { 0x00000171, 0x0002 }, /* R369 - FLL1 Control 1 */
+ { 0x00000172, 0x0008 }, /* R370 - FLL1 Control 2 */
+ { 0x00000173, 0x0018 }, /* R371 - FLL1 Control 3 */
+ { 0x00000174, 0x007D }, /* R372 - FLL1 Control 4 */
+ { 0x00000175, 0x0006 }, /* R373 - FLL1 Control 5 */
+ { 0x00000176, 0x0000 }, /* R374 - FLL1 Control 6 */
+ { 0x00000177, 0x0281 }, /* R375 - FLL1 Loop Filter Test 1 */
+ { 0x00000178, 0x0000 }, /* R376 - FLL1 NCO Test 0 */
+ { 0x00000179, 0x0000 }, /* R376 - FLL1 Control 7 */
+ { 0x00000181, 0x0000 }, /* R385 - FLL1 Synchroniser 1 */
+ { 0x00000182, 0x0000 }, /* R386 - FLL1 Synchroniser 2 */
+ { 0x00000183, 0x0000 }, /* R387 - FLL1 Synchroniser 3 */
+ { 0x00000184, 0x0000 }, /* R388 - FLL1 Synchroniser 4 */
+ { 0x00000185, 0x0000 }, /* R389 - FLL1 Synchroniser 5 */
+ { 0x00000186, 0x0000 }, /* R390 - FLL1 Synchroniser 6 */
+ { 0x00000187, 0x0001 }, /* R390 - FLL1 Synchroniser 7 */
+ { 0x00000189, 0x0000 }, /* R393 - FLL1 Spread Spectrum */
+ { 0x0000018A, 0x000C }, /* R394 - FLL1 GPIO Clock */
+ { 0x00000191, 0x0002 }, /* R401 - FLL2 Control 1 */
+ { 0x00000192, 0x0008 }, /* R402 - FLL2 Control 2 */
+ { 0x00000193, 0x0018 }, /* R403 - FLL2 Control 3 */
+ { 0x00000194, 0x007D }, /* R404 - FLL2 Control 4 */
+ { 0x00000195, 0x000C }, /* R405 - FLL2 Control 5 */
+ { 0x00000196, 0x0000 }, /* R406 - FLL2 Control 6 */
+ { 0x00000197, 0x0000 }, /* R407 - FLL2 Loop Filter Test 1 */
+ { 0x00000198, 0x0000 }, /* R408 - FLL2 NCO Test 0 */
+ { 0x00000199, 0x0000 }, /* R408 - FLL2 Control 7 */
+ { 0x000001A1, 0x0000 }, /* R417 - FLL2 Synchroniser 1 */
+ { 0x000001A2, 0x0000 }, /* R418 - FLL2 Synchroniser 2 */
+ { 0x000001A3, 0x0000 }, /* R419 - FLL2 Synchroniser 3 */
+ { 0x000001A4, 0x0000 }, /* R420 - FLL2 Synchroniser 4 */
+ { 0x000001A5, 0x0000 }, /* R421 - FLL2 Synchroniser 5 */
+ { 0x000001A6, 0x0000 }, /* R422 - FLL2 Synchroniser 6 */
+ { 0x000001A7, 0x0001 }, /* R422 - FLL2 Synchroniser 7 */
+ { 0x000001A9, 0x0000 }, /* R425 - FLL2 Spread Spectrum */
+ { 0x000001AA, 0x000C }, /* R426 - FLL2 GPIO Clock */
+ { 0x00000218, 0x00E6 }, /* R536 - Mic Bias Ctrl 1 */
+ { 0x00000219, 0x00E6 }, /* R537 - Mic Bias Ctrl 2 */
+ { 0x00000300, 0x0000 }, /* R768 - Input Enables */
+ { 0x00000308, 0x0000 }, /* R776 - Input Rate */
+ { 0x00000309, 0x0022 }, /* R777 - Input Volume Ramp */
+ { 0x0000030C, 0x0002 }, /* R780 - HPF Control */
+ { 0x00000310, 0x2000 }, /* R784 - IN1L Control */
+ { 0x00000311, 0x0180 }, /* R785 - ADC Digital Volume 1L */
+ { 0x00000312, 0x0000 }, /* R786 - DMIC1L Control */
+ { 0x00000314, 0x0000 }, /* R788 - IN1R Control */
+ { 0x00000315, 0x0180 }, /* R789 - ADC Digital Volume 1R */
+ { 0x00000316, 0x0000 }, /* R790 - DMIC1R Control */
+ { 0x00000318, 0x2000 }, /* R792 - IN2L Control */
+ { 0x00000319, 0x0180 }, /* R793 - ADC Digital Volume 2L */
+ { 0x0000031A, 0x0000 }, /* R794 - DMIC2L Control */
+ { 0x0000031C, 0x0000 }, /* R796 - IN2R Control */
+ { 0x0000031D, 0x0180 }, /* R797 - ADC Digital Volume 2R */
+ { 0x0000031E, 0x0000 }, /* R798 - DMIC2R Control */
+ { 0x00000400, 0x0000 }, /* R1024 - Output Enables 1 */
+ { 0x00000408, 0x0000 }, /* R1032 - Output Rate 1 */
+ { 0x00000409, 0x0022 }, /* R1033 - Output Volume Ramp */
+ { 0x00000410, 0x0080 }, /* R1040 - Output Path Config 1L */
+ { 0x00000411, 0x0180 }, /* R1041 - DAC Digital Volume 1L */
+ { 0x00000412, 0x0081 }, /* R1042 - DAC Volume Limit 1L */
+ { 0x00000413, 0x0001 }, /* R1043 - Noise Gate Select 1L */
+ { 0x00000415, 0x0180 }, /* R1045 - DAC Digital Volume 1R */
+ { 0x00000416, 0x0081 }, /* R1046 - DAC Volume Limit 1R */
+ { 0x00000417, 0x0002 }, /* R1047 - Noise Gate Select 1R */
+ { 0x00000429, 0x0180 }, /* R1065 - DAC Digital Volume 4L */
+ { 0x0000042A, 0x0081 }, /* R1066 - Out Volume 4L */
+ { 0x0000042B, 0x0040 }, /* R1067 - Noise Gate Select 4L */
+ { 0x00000450, 0x0000 }, /* R1104 - DAC AEC Control 1 */
+ { 0x00000458, 0x0000 }, /* R1112 - Noise Gate Control */
+ { 0x000004A0, 0x3480 }, /* R1184 - HP1 Short Circuit Ctrl */
+ { 0x00000500, 0x000C }, /* R1280 - AIF1 BCLK Ctrl */
+ { 0x00000501, 0x0008 }, /* R1281 - AIF1 Tx Pin Ctrl */
+ { 0x00000502, 0x0000 }, /* R1282 - AIF1 Rx Pin Ctrl */
+ { 0x00000503, 0x0000 }, /* R1283 - AIF1 Rate Ctrl */
+ { 0x00000504, 0x0000 }, /* R1284 - AIF1 Format */
+ { 0x00000506, 0x0040 }, /* R1286 - AIF1 Rx BCLK Rate */
+ { 0x00000507, 0x1818 }, /* R1287 - AIF1 Frame Ctrl 1 */
+ { 0x00000508, 0x1818 }, /* R1288 - AIF1 Frame Ctrl 2 */
+ { 0x00000509, 0x0000 }, /* R1289 - AIF1 Frame Ctrl 3 */
+ { 0x0000050A, 0x0001 }, /* R1290 - AIF1 Frame Ctrl 4 */
+ { 0x0000050B, 0x0002 }, /* R1291 - AIF1 Frame Ctrl 5 */
+ { 0x0000050C, 0x0003 }, /* R1292 - AIF1 Frame Ctrl 6 */
+ { 0x0000050D, 0x0004 }, /* R1293 - AIF1 Frame Ctrl 7 */
+ { 0x0000050E, 0x0005 }, /* R1294 - AIF1 Frame Ctrl 8 */
+ { 0x0000050F, 0x0006 }, /* R1295 - AIF1 Frame Ctrl 9 */
+ { 0x00000510, 0x0007 }, /* R1296 - AIF1 Frame Ctrl 10 */
+ { 0x00000511, 0x0000 }, /* R1297 - AIF1 Frame Ctrl 11 */
+ { 0x00000512, 0x0001 }, /* R1298 - AIF1 Frame Ctrl 12 */
+ { 0x00000513, 0x0002 }, /* R1299 - AIF1 Frame Ctrl 13 */
+ { 0x00000514, 0x0003 }, /* R1300 - AIF1 Frame Ctrl 14 */
+ { 0x00000515, 0x0004 }, /* R1301 - AIF1 Frame Ctrl 15 */
+ { 0x00000516, 0x0005 }, /* R1302 - AIF1 Frame Ctrl 16 */
+ { 0x00000517, 0x0006 }, /* R1303 - AIF1 Frame Ctrl 17 */
+ { 0x00000518, 0x0007 }, /* R1304 - AIF1 Frame Ctrl 18 */
+ { 0x00000519, 0x0000 }, /* R1305 - AIF1 Tx Enables */
+ { 0x0000051A, 0x0000 }, /* R1306 - AIF1 Rx Enables */
+ { 0x00000540, 0x000C }, /* R1344 - AIF2 BCLK Ctrl */
+ { 0x00000541, 0x0008 }, /* R1345 - AIF2 Tx Pin Ctrl */
+ { 0x00000542, 0x0000 }, /* R1346 - AIF2 Rx Pin Ctrl */
+ { 0x00000543, 0x0000 }, /* R1347 - AIF2 Rate Ctrl */
+ { 0x00000544, 0x0000 }, /* R1348 - AIF2 Format */
+ { 0x00000546, 0x0040 }, /* R1350 - AIF2 Rx BCLK Rate */
+ { 0x00000547, 0x1818 }, /* R1351 - AIF2 Frame Ctrl 1 */
+ { 0x00000548, 0x1818 }, /* R1352 - AIF2 Frame Ctrl 2 */
+ { 0x00000549, 0x0000 }, /* R1353 - AIF2 Frame Ctrl 3 */
+ { 0x0000054A, 0x0001 }, /* R1354 - AIF2 Frame Ctrl 4 */
+ { 0x0000054B, 0x0002 }, /* R1355 - AIF2 Frame Ctrl 5 */
+ { 0x0000054C, 0x0003 }, /* R1356 - AIF2 Frame Ctrl 6 */
+ { 0x0000054D, 0x0004 }, /* R1357 - AIF2 Frame Ctrl 7 */
+ { 0x0000054E, 0x0005 }, /* R1358 - AIF2 Frame Ctrl 8 */
+ { 0x00000551, 0x0000 }, /* R1361 - AIF2 Frame Ctrl 11 */
+ { 0x00000552, 0x0001 }, /* R1362 - AIF2 Frame Ctrl 12 */
+ { 0x00000553, 0x0002 }, /* R1363 - AIF2 Frame Ctrl 13 */
+ { 0x00000554, 0x0003 }, /* R1364 - AIF2 Frame Ctrl 14 */
+ { 0x00000555, 0x0004 }, /* R1365 - AIF2 Frame Ctrl 15 */
+ { 0x00000556, 0x0005 }, /* R1366 - AIF2 Frame Ctrl 16 */
+ { 0x00000559, 0x0000 }, /* R1369 - AIF2 Tx Enables */
+ { 0x0000055A, 0x0000 }, /* R1370 - AIF2 Rx Enables */
+ { 0x00000580, 0x000C }, /* R1408 - AIF3 BCLK Ctrl */
+ { 0x00000581, 0x0008 }, /* R1409 - AIF3 Tx Pin Ctrl */
+ { 0x00000582, 0x0000 }, /* R1410 - AIF3 Rx Pin Ctrl */
+ { 0x00000583, 0x0000 }, /* R1411 - AIF3 Rate Ctrl */
+ { 0x00000584, 0x0000 }, /* R1412 - AIF3 Format */
+ { 0x00000586, 0x0040 }, /* R1414 - AIF3 Rx BCLK Rate */
+ { 0x00000587, 0x1818 }, /* R1415 - AIF3 Frame Ctrl 1 */
+ { 0x00000588, 0x1818 }, /* R1416 - AIF3 Frame Ctrl 2 */
+ { 0x00000589, 0x0000 }, /* R1417 - AIF3 Frame Ctrl 3 */
+ { 0x0000058A, 0x0001 }, /* R1418 - AIF3 Frame Ctrl 4 */
+ { 0x00000591, 0x0000 }, /* R1425 - AIF3 Frame Ctrl 11 */
+ { 0x00000592, 0x0001 }, /* R1426 - AIF3 Frame Ctrl 12 */
+ { 0x00000599, 0x0000 }, /* R1433 - AIF3 Tx Enables */
+ { 0x0000059A, 0x0000 }, /* R1434 - AIF3 Rx Enables */
+ { 0x00000640, 0x0000 }, /* R1600 - PWM1MIX Input 1 Source */
+ { 0x00000641, 0x0080 }, /* R1601 - PWM1MIX Input 1 Volume */
+ { 0x00000642, 0x0000 }, /* R1602 - PWM1MIX Input 2 Source */
+ { 0x00000643, 0x0080 }, /* R1603 - PWM1MIX Input 2 Volume */
+ { 0x00000644, 0x0000 }, /* R1604 - PWM1MIX Input 3 Source */
+ { 0x00000645, 0x0080 }, /* R1605 - PWM1MIX Input 3 Volume */
+ { 0x00000646, 0x0000 }, /* R1606 - PWM1MIX Input 4 Source */
+ { 0x00000647, 0x0080 }, /* R1607 - PWM1MIX Input 4 Volume */
+ { 0x00000648, 0x0000 }, /* R1608 - PWM2MIX Input 1 Source */
+ { 0x00000649, 0x0080 }, /* R1609 - PWM2MIX Input 1 Volume */
+ { 0x0000064A, 0x0000 }, /* R1610 - PWM2MIX Input 2 Source */
+ { 0x0000064B, 0x0080 }, /* R1611 - PWM2MIX Input 2 Volume */
+ { 0x0000064C, 0x0000 }, /* R1612 - PWM2MIX Input 3 Source */
+ { 0x0000064D, 0x0080 }, /* R1613 - PWM2MIX Input 3 Volume */
+ { 0x0000064E, 0x0000 }, /* R1614 - PWM2MIX Input 4 Source */
+ { 0x0000064F, 0x0080 }, /* R1615 - PWM2MIX Input 4 Volume */
+ { 0x00000680, 0x0000 }, /* R1664 - OUT1LMIX Input 1 Source */
+ { 0x00000681, 0x0080 }, /* R1665 - OUT1LMIX Input 1 Volume */
+ { 0x00000682, 0x0000 }, /* R1666 - OUT1LMIX Input 2 Source */
+ { 0x00000683, 0x0080 }, /* R1667 - OUT1LMIX Input 2 Volume */
+ { 0x00000684, 0x0000 }, /* R1668 - OUT1LMIX Input 3 Source */
+ { 0x00000685, 0x0080 }, /* R1669 - OUT1LMIX Input 3 Volume */
+ { 0x00000686, 0x0000 }, /* R1670 - OUT1LMIX Input 4 Source */
+ { 0x00000687, 0x0080 }, /* R1671 - OUT1LMIX Input 4 Volume */
+ { 0x00000688, 0x0000 }, /* R1672 - OUT1RMIX Input 1 Source */
+ { 0x00000689, 0x0080 }, /* R1673 - OUT1RMIX Input 1 Volume */
+ { 0x0000068A, 0x0000 }, /* R1674 - OUT1RMIX Input 2 Source */
+ { 0x0000068B, 0x0080 }, /* R1675 - OUT1RMIX Input 2 Volume */
+ { 0x0000068C, 0x0000 }, /* R1676 - OUT1RMIX Input 3 Source */
+ { 0x0000068D, 0x0080 }, /* R1677 - OUT1RMIX Input 3 Volume */
+ { 0x0000068E, 0x0000 }, /* R1678 - OUT1RMIX Input 4 Source */
+ { 0x0000068F, 0x0080 }, /* R1679 - OUT1RMIX Input 4 Volume */
+ { 0x000006B0, 0x0000 }, /* R1712 - OUT4LMIX Input 1 Source */
+ { 0x000006B1, 0x0080 }, /* R1713 - OUT4LMIX Input 1 Volume */
+ { 0x000006B2, 0x0000 }, /* R1714 - OUT4LMIX Input 2 Source */
+ { 0x000006B3, 0x0080 }, /* R1715 - OUT4LMIX Input 2 Volume */
+ { 0x000006B4, 0x0000 }, /* R1716 - OUT4LMIX Input 3 Source */
+ { 0x000006B5, 0x0080 }, /* R1717 - OUT4LMIX Input 3 Volume */
+ { 0x000006B6, 0x0000 }, /* R1718 - OUT4LMIX Input 4 Source */
+ { 0x000006B7, 0x0080 }, /* R1719 - OUT4LMIX Input 4 Volume */
+ { 0x00000700, 0x0000 }, /* R1792 - AIF1TX1MIX Input 1 Source */
+ { 0x00000701, 0x0080 }, /* R1793 - AIF1TX1MIX Input 1 Volume */
+ { 0x00000702, 0x0000 }, /* R1794 - AIF1TX1MIX Input 2 Source */
+ { 0x00000703, 0x0080 }, /* R1795 - AIF1TX1MIX Input 2 Volume */
+ { 0x00000704, 0x0000 }, /* R1796 - AIF1TX1MIX Input 3 Source */
+ { 0x00000705, 0x0080 }, /* R1797 - AIF1TX1MIX Input 3 Volume */
+ { 0x00000706, 0x0000 }, /* R1798 - AIF1TX1MIX Input 4 Source */
+ { 0x00000707, 0x0080 }, /* R1799 - AIF1TX1MIX Input 4 Volume */
+ { 0x00000708, 0x0000 }, /* R1800 - AIF1TX2MIX Input 1 Source */
+ { 0x00000709, 0x0080 }, /* R1801 - AIF1TX2MIX Input 1 Volume */
+ { 0x0000070A, 0x0000 }, /* R1802 - AIF1TX2MIX Input 2 Source */
+ { 0x0000070B, 0x0080 }, /* R1803 - AIF1TX2MIX Input 2 Volume */
+ { 0x0000070C, 0x0000 }, /* R1804 - AIF1TX2MIX Input 3 Source */
+ { 0x0000070D, 0x0080 }, /* R1805 - AIF1TX2MIX Input 3 Volume */
+ { 0x0000070E, 0x0000 }, /* R1806 - AIF1TX2MIX Input 4 Source */
+ { 0x0000070F, 0x0080 }, /* R1807 - AIF1TX2MIX Input 4 Volume */
+ { 0x00000710, 0x0000 }, /* R1808 - AIF1TX3MIX Input 1 Source */
+ { 0x00000711, 0x0080 }, /* R1809 - AIF1TX3MIX Input 1 Volume */
+ { 0x00000712, 0x0000 }, /* R1810 - AIF1TX3MIX Input 2 Source */
+ { 0x00000713, 0x0080 }, /* R1811 - AIF1TX3MIX Input 2 Volume */
+ { 0x00000714, 0x0000 }, /* R1812 - AIF1TX3MIX Input 3 Source */
+ { 0x00000715, 0x0080 }, /* R1813 - AIF1TX3MIX Input 3 Volume */
+ { 0x00000716, 0x0000 }, /* R1814 - AIF1TX3MIX Input 4 Source */
+ { 0x00000717, 0x0080 }, /* R1815 - AIF1TX3MIX Input 4 Volume */
+ { 0x00000718, 0x0000 }, /* R1816 - AIF1TX4MIX Input 1 Source */
+ { 0x00000719, 0x0080 }, /* R1817 - AIF1TX4MIX Input 1 Volume */
+ { 0x0000071A, 0x0000 }, /* R1818 - AIF1TX4MIX Input 2 Source */
+ { 0x0000071B, 0x0080 }, /* R1819 - AIF1TX4MIX Input 2 Volume */
+ { 0x0000071C, 0x0000 }, /* R1820 - AIF1TX4MIX Input 3 Source */
+ { 0x0000071D, 0x0080 }, /* R1821 - AIF1TX4MIX Input 3 Volume */
+ { 0x0000071E, 0x0000 }, /* R1822 - AIF1TX4MIX Input 4 Source */
+ { 0x0000071F, 0x0080 }, /* R1823 - AIF1TX4MIX Input 4 Volume */
+ { 0x00000720, 0x0000 }, /* R1824 - AIF1TX5MIX Input 1 Source */
+ { 0x00000721, 0x0080 }, /* R1825 - AIF1TX5MIX Input 1 Volume */
+ { 0x00000722, 0x0000 }, /* R1826 - AIF1TX5MIX Input 2 Source */
+ { 0x00000723, 0x0080 }, /* R1827 - AIF1TX5MIX Input 2 Volume */
+ { 0x00000724, 0x0000 }, /* R1828 - AIF1TX5MIX Input 3 Source */
+ { 0x00000725, 0x0080 }, /* R1829 - AIF1TX5MIX Input 3 Volume */
+ { 0x00000726, 0x0000 }, /* R1830 - AIF1TX5MIX Input 4 Source */
+ { 0x00000727, 0x0080 }, /* R1831 - AIF1TX5MIX Input 4 Volume */
+ { 0x00000728, 0x0000 }, /* R1832 - AIF1TX6MIX Input 1 Source */
+ { 0x00000729, 0x0080 }, /* R1833 - AIF1TX6MIX Input 1 Volume */
+ { 0x0000072A, 0x0000 }, /* R1834 - AIF1TX6MIX Input 2 Source */
+ { 0x0000072B, 0x0080 }, /* R1835 - AIF1TX6MIX Input 2 Volume */
+ { 0x0000072C, 0x0000 }, /* R1836 - AIF1TX6MIX Input 3 Source */
+ { 0x0000072D, 0x0080 }, /* R1837 - AIF1TX6MIX Input 3 Volume */
+ { 0x0000072E, 0x0000 }, /* R1838 - AIF1TX6MIX Input 4 Source */
+ { 0x0000072F, 0x0080 }, /* R1839 - AIF1TX6MIX Input 4 Volume */
+ { 0x00000730, 0x0000 }, /* R1840 - AIF1TX7MIX Input 1 Source */
+ { 0x00000731, 0x0080 }, /* R1841 - AIF1TX7MIX Input 1 Volume */
+ { 0x00000732, 0x0000 }, /* R1842 - AIF1TX7MIX Input 2 Source */
+ { 0x00000733, 0x0080 }, /* R1843 - AIF1TX7MIX Input 2 Volume */
+ { 0x00000734, 0x0000 }, /* R1844 - AIF1TX7MIX Input 3 Source */
+ { 0x00000735, 0x0080 }, /* R1845 - AIF1TX7MIX Input 3 Volume */
+ { 0x00000736, 0x0000 }, /* R1846 - AIF1TX7MIX Input 4 Source */
+ { 0x00000737, 0x0080 }, /* R1847 - AIF1TX7MIX Input 4 Volume */
+ { 0x00000738, 0x0000 }, /* R1848 - AIF1TX8MIX Input 1 Source */
+ { 0x00000739, 0x0080 }, /* R1849 - AIF1TX8MIX Input 1 Volume */
+ { 0x0000073A, 0x0000 }, /* R1850 - AIF1TX8MIX Input 2 Source */
+ { 0x0000073B, 0x0080 }, /* R1851 - AIF1TX8MIX Input 2 Volume */
+ { 0x0000073C, 0x0000 }, /* R1852 - AIF1TX8MIX Input 3 Source */
+ { 0x0000073D, 0x0080 }, /* R1853 - AIF1TX8MIX Input 3 Volume */
+ { 0x0000073E, 0x0000 }, /* R1854 - AIF1TX8MIX Input 4 Source */
+ { 0x0000073F, 0x0080 }, /* R1855 - AIF1TX8MIX Input 4 Volume */
+ { 0x00000740, 0x0000 }, /* R1856 - AIF2TX1MIX Input 1 Source */
+ { 0x00000741, 0x0080 }, /* R1857 - AIF2TX1MIX Input 1 Volume */
+ { 0x00000742, 0x0000 }, /* R1858 - AIF2TX1MIX Input 2 Source */
+ { 0x00000743, 0x0080 }, /* R1859 - AIF2TX1MIX Input 2 Volume */
+ { 0x00000744, 0x0000 }, /* R1860 - AIF2TX1MIX Input 3 Source */
+ { 0x00000745, 0x0080 }, /* R1861 - AIF2TX1MIX Input 3 Volume */
+ { 0x00000746, 0x0000 }, /* R1862 - AIF2TX1MIX Input 4 Source */
+ { 0x00000747, 0x0080 }, /* R1863 - AIF2TX1MIX Input 4 Volume */
+ { 0x00000748, 0x0000 }, /* R1864 - AIF2TX2MIX Input 1 Source */
+ { 0x00000749, 0x0080 }, /* R1865 - AIF2TX2MIX Input 1 Volume */
+ { 0x0000074A, 0x0000 }, /* R1866 - AIF2TX2MIX Input 2 Source */
+ { 0x0000074B, 0x0080 }, /* R1867 - AIF2TX2MIX Input 2 Volume */
+ { 0x0000074C, 0x0000 }, /* R1868 - AIF2TX2MIX Input 3 Source */
+ { 0x0000074D, 0x0080 }, /* R1869 - AIF2TX2MIX Input 3 Volume */
+ { 0x0000074E, 0x0000 }, /* R1870 - AIF2TX2MIX Input 4 Source */
+ { 0x0000074F, 0x0080 }, /* R1871 - AIF2TX2MIX Input 4 Volume */
+ { 0x00000750, 0x0000 }, /* R1872 - AIF2TX3MIX Input 1 Source */
+ { 0x00000751, 0x0080 }, /* R1873 - AIF2TX3MIX Input 1 Volume */
+ { 0x00000752, 0x0000 }, /* R1874 - AIF2TX3MIX Input 2 Source */
+ { 0x00000753, 0x0080 }, /* R1875 - AIF2TX3MIX Input 2 Volume */
+ { 0x00000754, 0x0000 }, /* R1876 - AIF2TX3MIX Input 3 Source */
+ { 0x00000755, 0x0080 }, /* R1877 - AIF2TX3MIX Input 3 Volume */
+ { 0x00000756, 0x0000 }, /* R1878 - AIF2TX3MIX Input 4 Source */
+ { 0x00000757, 0x0080 }, /* R1879 - AIF2TX3MIX Input 4 Volume */
+ { 0x00000758, 0x0000 }, /* R1880 - AIF2TX4MIX Input 1 Source */
+ { 0x00000759, 0x0080 }, /* R1881 - AIF2TX4MIX Input 1 Volume */
+ { 0x0000075A, 0x0000 }, /* R1882 - AIF2TX4MIX Input 2 Source */
+ { 0x0000075B, 0x0080 }, /* R1883 - AIF2TX4MIX Input 2 Volume */
+ { 0x0000075C, 0x0000 }, /* R1884 - AIF2TX4MIX Input 3 Source */
+ { 0x0000075D, 0x0080 }, /* R1885 - AIF2TX4MIX Input 3 Volume */
+ { 0x0000075E, 0x0000 }, /* R1886 - AIF2TX4MIX Input 4 Source */
+ { 0x0000075F, 0x0080 }, /* R1887 - AIF2TX4MIX Input 4 Volume */
+ { 0x00000760, 0x0000 }, /* R1888 - AIF2TX5MIX Input 1 Source */
+ { 0x00000761, 0x0080 }, /* R1889 - AIF2TX5MIX Input 1 Volume */
+ { 0x00000762, 0x0000 }, /* R1890 - AIF2TX5MIX Input 2 Source */
+ { 0x00000763, 0x0080 }, /* R1891 - AIF2TX5MIX Input 2 Volume */
+ { 0x00000764, 0x0000 }, /* R1892 - AIF2TX5MIX Input 3 Source */
+ { 0x00000765, 0x0080 }, /* R1893 - AIF2TX5MIX Input 3 Volume */
+ { 0x00000766, 0x0000 }, /* R1894 - AIF2TX5MIX Input 4 Source */
+ { 0x00000767, 0x0080 }, /* R1895 - AIF2TX5MIX Input 4 Volume */
+ { 0x00000768, 0x0000 }, /* R1896 - AIF2TX6MIX Input 1 Source */
+ { 0x00000769, 0x0080 }, /* R1897 - AIF2TX6MIX Input 1 Volume */
+ { 0x0000076A, 0x0000 }, /* R1898 - AIF2TX6MIX Input 2 Source */
+ { 0x0000076B, 0x0080 }, /* R1899 - AIF2TX6MIX Input 2 Volume */
+ { 0x0000076C, 0x0000 }, /* R1900 - AIF2TX6MIX Input 3 Source */
+ { 0x0000076D, 0x0080 }, /* R1901 - AIF2TX6MIX Input 3 Volume */
+ { 0x0000076E, 0x0000 }, /* R1902 - AIF2TX6MIX Input 4 Source */
+ { 0x0000076F, 0x0080 }, /* R1903 - AIF2TX6MIX Input 4 Volume */
+ { 0x00000780, 0x0000 }, /* R1920 - AIF3TX1MIX Input 1 Source */
+ { 0x00000781, 0x0080 }, /* R1921 - AIF3TX1MIX Input 1 Volume */
+ { 0x00000782, 0x0000 }, /* R1922 - AIF3TX1MIX Input 2 Source */
+ { 0x00000783, 0x0080 }, /* R1923 - AIF3TX1MIX Input 2 Volume */
+ { 0x00000784, 0x0000 }, /* R1924 - AIF3TX1MIX Input 3 Source */
+ { 0x00000785, 0x0080 }, /* R1925 - AIF3TX1MIX Input 3 Volume */
+ { 0x00000786, 0x0000 }, /* R1926 - AIF3TX1MIX Input 4 Source */
+ { 0x00000787, 0x0080 }, /* R1927 - AIF3TX1MIX Input 4 Volume */
+ { 0x00000788, 0x0000 }, /* R1928 - AIF3TX2MIX Input 1 Source */
+ { 0x00000789, 0x0080 }, /* R1929 - AIF3TX2MIX Input 1 Volume */
+ { 0x0000078A, 0x0000 }, /* R1930 - AIF3TX2MIX Input 2 Source */
+ { 0x0000078B, 0x0080 }, /* R1931 - AIF3TX2MIX Input 2 Volume */
+ { 0x0000078C, 0x0000 }, /* R1932 - AIF3TX2MIX Input 3 Source */
+ { 0x0000078D, 0x0080 }, /* R1933 - AIF3TX2MIX Input 3 Volume */
+ { 0x0000078E, 0x0000 }, /* R1934 - AIF3TX2MIX Input 4 Source */
+ { 0x0000078F, 0x0080 }, /* R1935 - AIF3TX2MIX Input 4 Volume */
+ { 0x00000880, 0x0000 }, /* R2176 - EQ1MIX Input 1 Source */
+ { 0x00000881, 0x0080 }, /* R2177 - EQ1MIX Input 1 Volume */
+ { 0x00000882, 0x0000 }, /* R2178 - EQ1MIX Input 2 Source */
+ { 0x00000883, 0x0080 }, /* R2179 - EQ1MIX Input 2 Volume */
+ { 0x00000884, 0x0000 }, /* R2180 - EQ1MIX Input 3 Source */
+ { 0x00000885, 0x0080 }, /* R2181 - EQ1MIX Input 3 Volume */
+ { 0x00000886, 0x0000 }, /* R2182 - EQ1MIX Input 4 Source */
+ { 0x00000887, 0x0080 }, /* R2183 - EQ1MIX Input 4 Volume */
+ { 0x00000888, 0x0000 }, /* R2184 - EQ2MIX Input 1 Source */
+ { 0x00000889, 0x0080 }, /* R2185 - EQ2MIX Input 1 Volume */
+ { 0x0000088A, 0x0000 }, /* R2186 - EQ2MIX Input 2 Source */
+ { 0x0000088B, 0x0080 }, /* R2187 - EQ2MIX Input 2 Volume */
+ { 0x0000088C, 0x0000 }, /* R2188 - EQ2MIX Input 3 Source */
+ { 0x0000088D, 0x0080 }, /* R2189 - EQ2MIX Input 3 Volume */
+ { 0x0000088E, 0x0000 }, /* R2190 - EQ2MIX Input 4 Source */
+ { 0x0000088F, 0x0080 }, /* R2191 - EQ2MIX Input 4 Volume */
+ { 0x000008C0, 0x0000 }, /* R2240 - DRC1LMIX Input 1 Source */
+ { 0x000008C1, 0x0080 }, /* R2241 - DRC1LMIX Input 1 Volume */
+ { 0x000008C2, 0x0000 }, /* R2242 - DRC1LMIX Input 2 Source */
+ { 0x000008C3, 0x0080 }, /* R2243 - DRC1LMIX Input 2 Volume */
+ { 0x000008C4, 0x0000 }, /* R2244 - DRC1LMIX Input 3 Source */
+ { 0x000008C5, 0x0080 }, /* R2245 - DRC1LMIX Input 3 Volume */
+ { 0x000008C6, 0x0000 }, /* R2246 - DRC1LMIX Input 4 Source */
+ { 0x000008C7, 0x0080 }, /* R2247 - DRC1LMIX Input 4 Volume */
+ { 0x000008C8, 0x0000 }, /* R2248 - DRC1RMIX Input 1 Source */
+ { 0x000008C9, 0x0080 }, /* R2249 - DRC1RMIX Input 1 Volume */
+ { 0x000008CA, 0x0000 }, /* R2250 - DRC1RMIX Input 2 Source */
+ { 0x000008CB, 0x0080 }, /* R2251 - DRC1RMIX Input 2 Volume */
+ { 0x000008CC, 0x0000 }, /* R2252 - DRC1RMIX Input 3 Source */
+ { 0x000008CD, 0x0080 }, /* R2253 - DRC1RMIX Input 3 Volume */
+ { 0x000008CE, 0x0000 }, /* R2254 - DRC1RMIX Input 4 Source */
+ { 0x000008CF, 0x0080 }, /* R2255 - DRC1RMIX Input 4 Volume */
+ { 0x000008D0, 0x0000 }, /* R2256 - DRC2LMIX Input 1 Source */
+ { 0x000008D1, 0x0080 }, /* R2257 - DRC2LMIX Input 1 Volume */
+ { 0x000008D2, 0x0000 }, /* R2258 - DRC2LMIX Input 2 Source */
+ { 0x000008D3, 0x0080 }, /* R2259 - DRC2LMIX Input 2 Volume */
+ { 0x000008D4, 0x0000 }, /* R2260 - DRC2LMIX Input 3 Source */
+ { 0x000008D5, 0x0080 }, /* R2261 - DRC2LMIX Input 3 Volume */
+ { 0x000008D6, 0x0000 }, /* R2262 - DRC2LMIX Input 4 Source */
+ { 0x000008D7, 0x0080 }, /* R2263 - DRC2LMIX Input 4 Volume */
+ { 0x000008D8, 0x0000 }, /* R2264 - DRC2RMIX Input 1 Source */
+ { 0x000008D9, 0x0080 }, /* R2265 - DRC2RMIX Input 1 Volume */
+ { 0x000008DA, 0x0000 }, /* R2266 - DRC2RMIX Input 2 Source */
+ { 0x000008DB, 0x0080 }, /* R2267 - DRC2RMIX Input 2 Volume */
+ { 0x000008DC, 0x0000 }, /* R2268 - DRC2RMIX Input 3 Source */
+ { 0x000008DD, 0x0080 }, /* R2269 - DRC2RMIX Input 3 Volume */
+ { 0x000008DE, 0x0000 }, /* R2270 - DRC2RMIX Input 4 Source */
+ { 0x000008DF, 0x0080 }, /* R2271 - DRC2RMIX Input 4 Volume */
+ { 0x00000900, 0x0000 }, /* R2304 - HPLP1MIX Input 1 Source */
+ { 0x00000901, 0x0080 }, /* R2305 - HPLP1MIX Input 1 Volume */
+ { 0x00000902, 0x0000 }, /* R2306 - HPLP1MIX Input 2 Source */
+ { 0x00000903, 0x0080 }, /* R2307 - HPLP1MIX Input 2 Volume */
+ { 0x00000904, 0x0000 }, /* R2308 - HPLP1MIX Input 3 Source */
+ { 0x00000905, 0x0080 }, /* R2309 - HPLP1MIX Input 3 Volume */
+ { 0x00000906, 0x0000 }, /* R2310 - HPLP1MIX Input 4 Source */
+ { 0x00000907, 0x0080 }, /* R2311 - HPLP1MIX Input 4 Volume */
+ { 0x00000908, 0x0000 }, /* R2312 - HPLP2MIX Input 1 Source */
+ { 0x00000909, 0x0080 }, /* R2313 - HPLP2MIX Input 1 Volume */
+ { 0x0000090A, 0x0000 }, /* R2314 - HPLP2MIX Input 2 Source */
+ { 0x0000090B, 0x0080 }, /* R2315 - HPLP2MIX Input 2 Volume */
+ { 0x0000090C, 0x0000 }, /* R2316 - HPLP2MIX Input 3 Source */
+ { 0x0000090D, 0x0080 }, /* R2317 - HPLP2MIX Input 3 Volume */
+ { 0x0000090E, 0x0000 }, /* R2318 - HPLP2MIX Input 4 Source */
+ { 0x0000090F, 0x0080 }, /* R2319 - HPLP2MIX Input 4 Volume */
+ { 0x00000910, 0x0000 }, /* R2320 - HPLP3MIX Input 1 Source */
+ { 0x00000911, 0x0080 }, /* R2321 - HPLP3MIX Input 1 Volume */
+ { 0x00000912, 0x0000 }, /* R2322 - HPLP3MIX Input 2 Source */
+ { 0x00000913, 0x0080 }, /* R2323 - HPLP3MIX Input 2 Volume */
+ { 0x00000914, 0x0000 }, /* R2324 - HPLP3MIX Input 3 Source */
+ { 0x00000915, 0x0080 }, /* R2325 - HPLP3MIX Input 3 Volume */
+ { 0x00000916, 0x0000 }, /* R2326 - HPLP3MIX Input 4 Source */
+ { 0x00000917, 0x0080 }, /* R2327 - HPLP3MIX Input 4 Volume */
+ { 0x00000918, 0x0000 }, /* R2328 - HPLP4MIX Input 1 Source */
+ { 0x00000919, 0x0080 }, /* R2329 - HPLP4MIX Input 1 Volume */
+ { 0x0000091A, 0x0000 }, /* R2330 - HPLP4MIX Input 2 Source */
+ { 0x0000091B, 0x0080 }, /* R2331 - HPLP4MIX Input 2 Volume */
+ { 0x0000091C, 0x0000 }, /* R2332 - HPLP4MIX Input 3 Source */
+ { 0x0000091D, 0x0080 }, /* R2333 - HPLP4MIX Input 3 Volume */
+ { 0x0000091E, 0x0000 }, /* R2334 - HPLP4MIX Input 4 Source */
+ { 0x0000091F, 0x0080 }, /* R2335 - HPLP4MIX Input 4 Volume */
+ { 0x00000980, 0x0000 }, /* R2432 - DSP2LMIX Input 1 Source */
+ { 0x00000981, 0x0080 }, /* R2433 - DSP2LMIX Input 1 Volume */
+ { 0x00000982, 0x0000 }, /* R2434 - DSP2LMIX Input 2 Source */
+ { 0x00000983, 0x0080 }, /* R2435 - DSP2LMIX Input 2 Volume */
+ { 0x00000984, 0x0000 }, /* R2436 - DSP2LMIX Input 3 Source */
+ { 0x00000985, 0x0080 }, /* R2437 - DSP2LMIX Input 3 Volume */
+ { 0x00000986, 0x0000 }, /* R2438 - DSP2LMIX Input 4 Source */
+ { 0x00000987, 0x0080 }, /* R2439 - DSP2LMIX Input 4 Volume */
+ { 0x00000988, 0x0000 }, /* R2440 - DSP2RMIX Input 1 Source */
+ { 0x00000989, 0x0080 }, /* R2441 - DSP2RMIX Input 1 Volume */
+ { 0x0000098A, 0x0000 }, /* R2442 - DSP2RMIX Input 2 Source */
+ { 0x0000098B, 0x0080 }, /* R2443 - DSP2RMIX Input 2 Volume */
+ { 0x0000098C, 0x0000 }, /* R2444 - DSP2RMIX Input 3 Source */
+ { 0x0000098D, 0x0080 }, /* R2445 - DSP2RMIX Input 3 Volume */
+ { 0x0000098E, 0x0000 }, /* R2446 - DSP2RMIX Input 4 Source */
+ { 0x0000098F, 0x0080 }, /* R2447 - DSP2RMIX Input 4 Volume */
+ { 0x00000990, 0x0000 }, /* R2448 - DSP2AUX1MIX Input 1 Source */
+ { 0x00000998, 0x0000 }, /* R2456 - DSP2AUX2MIX Input 1 Source */
+ { 0x000009A0, 0x0000 }, /* R2464 - DSP2AUX3MIX Input 1 Source */
+ { 0x000009A8, 0x0000 }, /* R2472 - DSP2AUX4MIX Input 1 Source */
+ { 0x000009B0, 0x0000 }, /* R2480 - DSP2AUX5MIX Input 1 Source */
+ { 0x000009B8, 0x0000 }, /* R2488 - DSP2AUX6MIX Input 1 Source */
+ { 0x000009C0, 0x0000 }, /* R2496 - DSP3LMIX Input 1 Source */
+ { 0x000009C1, 0x0080 }, /* R2497 - DSP3LMIX Input 1 Volume */
+ { 0x000009C2, 0x0000 }, /* R2498 - DSP3LMIX Input 2 Source */
+ { 0x000009C3, 0x0080 }, /* R2499 - DSP3LMIX Input 2 Volume */
+ { 0x000009C4, 0x0000 }, /* R2500 - DSP3LMIX Input 3 Source */
+ { 0x000009C5, 0x0080 }, /* R2501 - DSP3LMIX Input 3 Volume */
+ { 0x000009C6, 0x0000 }, /* R2502 - DSP3LMIX Input 4 Source */
+ { 0x000009C7, 0x0080 }, /* R2503 - DSP3LMIX Input 4 Volume */
+ { 0x000009C8, 0x0000 }, /* R2504 - DSP3RMIX Input 1 Source */
+ { 0x000009C9, 0x0080 }, /* R2505 - DSP3RMIX Input 1 Volume */
+ { 0x000009CA, 0x0000 }, /* R2506 - DSP3RMIX Input 2 Source */
+ { 0x000009CB, 0x0080 }, /* R2507 - DSP3RMIX Input 2 Volume */
+ { 0x000009CC, 0x0000 }, /* R2508 - DSP3RMIX Input 3 Source */
+ { 0x000009CD, 0x0080 }, /* R2509 - DSP3RMIX Input 3 Volume */
+ { 0x000009CE, 0x0000 }, /* R2510 - DSP3RMIX Input 4 Source */
+ { 0x000009CF, 0x0080 }, /* R2511 - DSP3RMIX Input 4 Volume */
+ { 0x000009D0, 0x0000 }, /* R2512 - DSP3AUX1MIX Input 1 Source */
+ { 0x000009D8, 0x0000 }, /* R2520 - DSP3AUX2MIX Input 1 Source */
+ { 0x000009E0, 0x0000 }, /* R2528 - DSP3AUX3MIX Input 1 Source */
+ { 0x000009E8, 0x0000 }, /* R2536 - DSP3AUX4MIX Input 1 Source */
+ { 0x000009F0, 0x0000 }, /* R2544 - DSP3AUX5MIX Input 1 Source */
+ { 0x000009F8, 0x0000 }, /* R2552 - DSP3AUX6MIX Input 1 Source */
+ { 0x00000A80, 0x0000 }, /* R2688 - ASRC1LMIX Input 1 Source */
+ { 0x00000A88, 0x0000 }, /* R2696 - ASRC1RMIX Input 1 Source */
+ { 0x00000A90, 0x0000 }, /* R2704 - ASRC2LMIX Input 1 Source */
+ { 0x00000A98, 0x0000 }, /* R2712 - ASRC2RMIX Input 1 Source */
+ { 0x00000B00, 0x0000 }, /* R2816 - ISRC1DEC1MIX Input 1 Source */
+ { 0x00000B08, 0x0000 }, /* R2824 - ISRC1DEC2MIX Input 1 Source */
+ { 0x00000B10, 0x0000 }, /* R2832 - ISRC1DEC3MIX Input 1 Source */
+ { 0x00000B18, 0x0000 }, /* R2840 - ISRC1DEC4MIX Input 1 Source */
+ { 0x00000B20, 0x0000 }, /* R2848 - ISRC1INT1MIX Input 1 Source */
+ { 0x00000B28, 0x0000 }, /* R2856 - ISRC1INT2MIX Input 1 Source */
+ { 0x00000B30, 0x0000 }, /* R2864 - ISRC1INT3MIX Input 1 Source */
+ { 0x00000B38, 0x0000 }, /* R2872 - ISRC1INT4MIX Input 1 Source */
+ { 0x00000B40, 0x0000 }, /* R2880 - ISRC2DEC1MIX Input 1 Source */
+ { 0x00000B48, 0x0000 }, /* R2888 - ISRC2DEC2MIX Input 1 Source */
+ { 0x00000B50, 0x0000 }, /* R2896 - ISRC2DEC3MIX Input 1 Source */
+ { 0x00000B58, 0x0000 }, /* R2904 - ISRC2DEC4MIX Input 1 Source */
+ { 0x00000B60, 0x0000 }, /* R2912 - ISRC2INT1MIX Input 1 Source */
+ { 0x00000B68, 0x0000 }, /* R2920 - ISRC2INT2MIX Input 1 Source */
+ { 0x00000B70, 0x0000 }, /* R2928 - ISRC2INT3MIX Input 1 Source */
+ { 0x00000B78, 0x0000 }, /* R2936 - ISRC2INT4MIX Input 1 Source */
+ { 0x00000B80, 0x0000 }, /* R2944 - ISRC3DEC1MIX Input 1 Source */
+ { 0x00000B88, 0x0000 }, /* R2952 - ISRC3DEC2MIX Input 1 Source */
+ { 0x00000B90, 0x0000 }, /* R2960 - ISRC3DEC3MIX Input 1 Source */
+ { 0x00000B98, 0x0000 }, /* R2968 - ISRC3DEC4MIX Input 1 Source */
+ { 0x00000BA0, 0x0000 }, /* R2976 - ISRC3INT1MIX Input 1 Source */
+ { 0x00000BA8, 0x0000 }, /* R2984 - ISRC3INT2MIX Input 1 Source */
+ { 0x00000BB0, 0x0000 }, /* R2992 - ISRC3INT3MIX Input 1 Source */
+ { 0x00000BB8, 0x0000 }, /* R3000 - ISRC3INT4MIX Input 1 Source */
+ { 0x00000C00, 0xA101 }, /* R3072 - GPIO1 CTRL */
+ { 0x00000C01, 0xA101 }, /* R3073 - GPIO2 CTRL */
+ { 0x00000C0F, 0x0400 }, /* R3087 - IRQ CTRL 1 */
+ { 0x00000C10, 0x1000 }, /* R3088 - GPIO Debounce Config */
+ { 0x00000C20, 0x0002 }, /* R3104 - Misc Pad Ctrl 1 */
+ { 0x00000C21, 0x8001 }, /* R3105 - Misc Pad Ctrl 2 */
+ { 0x00000C22, 0x0000 }, /* R3106 - Misc Pad Ctrl 3 */
+ { 0x00000C23, 0x0000 }, /* R3107 - Misc Pad Ctrl 4 */
+ { 0x00000C24, 0x0000 }, /* R3108 - Misc Pad Ctrl 5 */
+ { 0x00000C25, 0x0000 }, /* R3109 - Misc Pad Ctrl 6 */
+ { 0x00000C30, 0x0404 }, /* R3120 - Misc Pad Ctrl 7 */
+ { 0x00000C32, 0x0404 }, /* R3122 - Misc Pad Ctrl 9 */
+ { 0x00000C33, 0x0404 }, /* R3123 - Misc Pad Ctrl 10 */
+ { 0x00000C34, 0x0404 }, /* R3124 - Misc Pad Ctrl 11 */
+ { 0x00000C35, 0x0404 }, /* R3125 - Misc Pad Ctrl 12 */
+ { 0x00000C36, 0x0400 }, /* R3126 - Misc Pad Ctrl 13 */
+ { 0x00000C37, 0x0404 }, /* R3127 - Misc Pad Ctrl 14 */
+ { 0x00000C39, 0x0400 }, /* R3129 - Misc Pad Ctrl 16 */
+ { 0x00000D08, 0x0007 }, /* R3336 - Interrupt Status 1 Mask */
+ { 0x00000D09, 0x06FF }, /* R3337 - Interrupt Status 2 Mask */
+ { 0x00000D0A, 0xCFEF }, /* R3338 - Interrupt Status 3 Mask */
+ { 0x00000D0B, 0xFFC3 }, /* R3339 - Interrupt Status 4 Mask */
+ { 0x00000D0C, 0x000B }, /* R3340 - Interrupt Status 5 Mask */
+ { 0x00000D0D, 0xD005 }, /* R3341 - Interrupt Status 6 Mask */
+ { 0x00000D0F, 0x0000 }, /* R3343 - Interrupt Control */
+ { 0x00000D18, 0x0007 }, /* R3352 - IRQ2 Status 1 Mask */
+ { 0x00000D19, 0x06FF }, /* R3353 - IRQ2 Status 2 Mask */
+ { 0x00000D1A, 0xCFEF }, /* R3354 - IRQ2 Status 3 Mask */
+ { 0x00000D1B, 0xFFC3 }, /* R3355 - IRQ2 Status 4 Mask */
+ { 0x00000D1C, 0x000B }, /* R3356 - IRQ2 Status 5 Mask */
+ { 0x00000D1D, 0xD005 }, /* R3357 - IRQ2 Status 6 Mask */
+ { 0x00000D1F, 0x0000 }, /* R3359 - IRQ2 Control */
+ { 0x00000E00, 0x0000 }, /* R3584 - FX_Ctrl1 */
+ { 0x00000E10, 0x6318 }, /* R3600 - EQ1_1 */
+ { 0x00000E11, 0x6300 }, /* R3601 - EQ1_2 */
+ { 0x00000E12, 0x0FC8 }, /* R3602 - EQ1_3 */
+ { 0x00000E13, 0x03FE }, /* R3603 - EQ1_4 */
+ { 0x00000E14, 0x00E0 }, /* R3604 - EQ1_5 */
+ { 0x00000E15, 0x1EC4 }, /* R3605 - EQ1_6 */
+ { 0x00000E16, 0xF136 }, /* R3606 - EQ1_7 */
+ { 0x00000E17, 0x0409 }, /* R3607 - EQ1_8 */
+ { 0x00000E18, 0x04CC }, /* R3608 - EQ1_9 */
+ { 0x00000E19, 0x1C9B }, /* R3609 - EQ1_10 */
+ { 0x00000E1A, 0xF337 }, /* R3610 - EQ1_11 */
+ { 0x00000E1B, 0x040B }, /* R3611 - EQ1_12 */
+ { 0x00000E1C, 0x0CBB }, /* R3612 - EQ1_13 */
+ { 0x00000E1D, 0x16F8 }, /* R3613 - EQ1_14 */
+ { 0x00000E1E, 0xF7D9 }, /* R3614 - EQ1_15 */
+ { 0x00000E1F, 0x040A }, /* R3615 - EQ1_16 */
+ { 0x00000E20, 0x1F14 }, /* R3616 - EQ1_17 */
+ { 0x00000E21, 0x058C }, /* R3617 - EQ1_18 */
+ { 0x00000E22, 0x0563 }, /* R3618 - EQ1_19 */
+ { 0x00000E23, 0x4000 }, /* R3619 - EQ1_20 */
+ { 0x00000E24, 0x0B75 }, /* R3620 - EQ1_21 */
+ { 0x00000E26, 0x6318 }, /* R3622 - EQ2_1 */
+ { 0x00000E27, 0x6300 }, /* R3623 - EQ2_2 */
+ { 0x00000E28, 0x0FC8 }, /* R3624 - EQ2_3 */
+ { 0x00000E29, 0x03FE }, /* R3625 - EQ2_4 */
+ { 0x00000E2A, 0x00E0 }, /* R3626 - EQ2_5 */
+ { 0x00000E2B, 0x1EC4 }, /* R3627 - EQ2_6 */
+ { 0x00000E2C, 0xF136 }, /* R3628 - EQ2_7 */
+ { 0x00000E2D, 0x0409 }, /* R3629 - EQ2_8 */
+ { 0x00000E2E, 0x04CC }, /* R3630 - EQ2_9 */
+ { 0x00000E2F, 0x1C9B }, /* R3631 - EQ2_10 */
+ { 0x00000E30, 0xF337 }, /* R3632 - EQ2_11 */
+ { 0x00000E31, 0x040B }, /* R3633 - EQ2_12 */
+ { 0x00000E32, 0x0CBB }, /* R3634 - EQ2_13 */
+ { 0x00000E33, 0x16F8 }, /* R3635 - EQ2_14 */
+ { 0x00000E34, 0xF7D9 }, /* R3636 - EQ2_15 */
+ { 0x00000E35, 0x040A }, /* R3637 - EQ2_16 */
+ { 0x00000E36, 0x1F14 }, /* R3638 - EQ2_17 */
+ { 0x00000E37, 0x058C }, /* R3639 - EQ2_18 */
+ { 0x00000E38, 0x0563 }, /* R3640 - EQ2_19 */
+ { 0x00000E39, 0x4000 }, /* R3641 - EQ2_20 */
+ { 0x00000E3A, 0x0B75 }, /* R3642 - EQ2_21 */
+ { 0x00000E80, 0x0018 }, /* R3712 - DRC1 ctrl1 */
+ { 0x00000E81, 0x0933 }, /* R3713 - DRC1 ctrl2 */
+ { 0x00000E82, 0x0018 }, /* R3714 - DRC1 ctrl3 */
+ { 0x00000E83, 0x0000 }, /* R3715 - DRC1 ctrl4 */
+ { 0x00000E84, 0x0000 }, /* R3716 - DRC1 ctrl5 */
+ { 0x00000E89, 0x0018 }, /* R3721 - DRC2 ctrl1 */
+ { 0x00000E8A, 0x0933 }, /* R3722 - DRC2 ctrl2 */
+ { 0x00000E8B, 0x0018 }, /* R3723 - DRC2 ctrl3 */
+ { 0x00000E8C, 0x0000 }, /* R3724 - DRC2 ctrl4 */
+ { 0x00000E8D, 0x0000 }, /* R3725 - DRC2 ctrl5 */
+ { 0x00000EC0, 0x0000 }, /* R3776 - HPLPF1_1 */
+ { 0x00000EC1, 0x0000 }, /* R3777 - HPLPF1_2 */
+ { 0x00000EC4, 0x0000 }, /* R3780 - HPLPF2_1 */
+ { 0x00000EC5, 0x0000 }, /* R3781 - HPLPF2_2 */
+ { 0x00000EC8, 0x0000 }, /* R3784 - HPLPF3_1 */
+ { 0x00000EC9, 0x0000 }, /* R3785 - HPLPF3_2 */
+ { 0x00000ECC, 0x0000 }, /* R3788 - HPLPF4_1 */
+ { 0x00000ECD, 0x0000 }, /* R3789 - HPLPF4_2 */
+ { 0x00000EE0, 0x0000 }, /* R3808 - ASRC_ENABLE */
+ { 0x00000EE2, 0x0000 }, /* R3810 - ASRC_RATE1 */
+ { 0x00000EE3, 0x4000 }, /* R3811 - ASRC_RATE2 */
+ { 0x00000EF0, 0x0000 }, /* R3824 - ISRC 1 CTRL 1 */
+ { 0x00000EF1, 0x0000 }, /* R3825 - ISRC 1 CTRL 2 */
+ { 0x00000EF2, 0x0000 }, /* R3826 - ISRC 1 CTRL 3 */
+ { 0x00000EF3, 0x0000 }, /* R3827 - ISRC 2 CTRL 1 */
+ { 0x00000EF4, 0x0000 }, /* R3828 - ISRC 2 CTRL 2 */
+ { 0x00000EF5, 0x0000 }, /* R3829 - ISRC 2 CTRL 3 */
+ { 0x00000EF6, 0x0000 }, /* R3830 - ISRC 3 CTRL 1 */
+ { 0x00000EF7, 0x0000 }, /* R3831 - ISRC 3 CTRL 2 */
+ { 0x00000EF8, 0x0000 }, /* R3832 - ISRC 3 CTRL 3 */
+ { 0x00001200, 0x0010 }, /* R4608 - DSP2 Control 1 */
+ { 0x00001300, 0x0010 }, /* R4864 - DSP3 Control 1 */
+};
+
+static bool cs47l24_is_adsp_memory(unsigned int reg)
+{
+ switch (reg) {
+ case 0x200000 ... 0x205fff: /* DSP2 PM */
+ case 0x280000 ... 0x281fff: /* DSP2 ZM */
+ case 0x290000 ... 0x2a7fff: /* DSP2 XM */
+ case 0x2a8000 ... 0x2b3fff: /* DSP2 YM */
+ case 0x300000 ... 0x308fff: /* DSP3 PM */
+ case 0x380000 ... 0x381fff: /* DSP3 ZM */
+ case 0x390000 ... 0x3a7fff: /* DSP3 XM */
+ case 0x3a8000 ... 0x3b3fff: /* DSP3 YM */
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs47l24_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ARIZONA_SOFTWARE_RESET:
+ case ARIZONA_DEVICE_REVISION:
+ case ARIZONA_CTRL_IF_SPI_CFG_1:
+ case ARIZONA_WRITE_SEQUENCER_CTRL_0:
+ case ARIZONA_WRITE_SEQUENCER_CTRL_1:
+ case ARIZONA_WRITE_SEQUENCER_CTRL_2:
+ case ARIZONA_TONE_GENERATOR_1:
+ case ARIZONA_TONE_GENERATOR_2:
+ case ARIZONA_TONE_GENERATOR_3:
+ case ARIZONA_TONE_GENERATOR_4:
+ case ARIZONA_TONE_GENERATOR_5:
+ case ARIZONA_PWM_DRIVE_1:
+ case ARIZONA_PWM_DRIVE_2:
+ case ARIZONA_PWM_DRIVE_3:
+ case ARIZONA_SEQUENCE_CONTROL:
+ case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_1:
+ case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2:
+ case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3:
+ case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_4:
+ case ARIZONA_COMFORT_NOISE_GENERATOR:
+ case ARIZONA_HAPTICS_CONTROL_1:
+ case ARIZONA_HAPTICS_CONTROL_2:
+ case ARIZONA_HAPTICS_PHASE_1_INTENSITY:
+ case ARIZONA_HAPTICS_PHASE_1_DURATION:
+ case ARIZONA_HAPTICS_PHASE_2_INTENSITY:
+ case ARIZONA_HAPTICS_PHASE_2_DURATION:
+ case ARIZONA_HAPTICS_PHASE_3_INTENSITY:
+ case ARIZONA_HAPTICS_PHASE_3_DURATION:
+ case ARIZONA_HAPTICS_STATUS:
+ case ARIZONA_CLOCK_32K_1:
+ case ARIZONA_SYSTEM_CLOCK_1:
+ case ARIZONA_SAMPLE_RATE_1:
+ case ARIZONA_SAMPLE_RATE_2:
+ case ARIZONA_SAMPLE_RATE_3:
+ case ARIZONA_SAMPLE_RATE_1_STATUS:
+ case ARIZONA_SAMPLE_RATE_2_STATUS:
+ case ARIZONA_SAMPLE_RATE_3_STATUS:
+ case ARIZONA_ASYNC_CLOCK_1:
+ case ARIZONA_ASYNC_SAMPLE_RATE_1:
+ case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS:
+ case ARIZONA_ASYNC_SAMPLE_RATE_2:
+ case ARIZONA_ASYNC_SAMPLE_RATE_2_STATUS:
+ case ARIZONA_OUTPUT_SYSTEM_CLOCK:
+ case ARIZONA_OUTPUT_ASYNC_CLOCK:
+ case ARIZONA_RATE_ESTIMATOR_1:
+ case ARIZONA_RATE_ESTIMATOR_2:
+ case ARIZONA_RATE_ESTIMATOR_3:
+ case ARIZONA_RATE_ESTIMATOR_4:
+ case ARIZONA_RATE_ESTIMATOR_5:
+ case ARIZONA_FLL1_CONTROL_1:
+ case ARIZONA_FLL1_CONTROL_2:
+ case ARIZONA_FLL1_CONTROL_3:
+ case ARIZONA_FLL1_CONTROL_4:
+ case ARIZONA_FLL1_CONTROL_5:
+ case ARIZONA_FLL1_CONTROL_6:
+ case ARIZONA_FLL1_CONTROL_7:
+ case ARIZONA_FLL1_LOOP_FILTER_TEST_1:
+ case ARIZONA_FLL1_NCO_TEST_0:
+ case ARIZONA_FLL1_SYNCHRONISER_1:
+ case ARIZONA_FLL1_SYNCHRONISER_2:
+ case ARIZONA_FLL1_SYNCHRONISER_3:
+ case ARIZONA_FLL1_SYNCHRONISER_4:
+ case ARIZONA_FLL1_SYNCHRONISER_5:
+ case ARIZONA_FLL1_SYNCHRONISER_6:
+ case ARIZONA_FLL1_SYNCHRONISER_7:
+ case ARIZONA_FLL1_SPREAD_SPECTRUM:
+ case ARIZONA_FLL1_GPIO_CLOCK:
+ case ARIZONA_FLL2_CONTROL_1:
+ case ARIZONA_FLL2_CONTROL_2:
+ case ARIZONA_FLL2_CONTROL_3:
+ case ARIZONA_FLL2_CONTROL_4:
+ case ARIZONA_FLL2_CONTROL_5:
+ case ARIZONA_FLL2_CONTROL_6:
+ case ARIZONA_FLL2_CONTROL_7:
+ case ARIZONA_FLL2_LOOP_FILTER_TEST_1:
+ case ARIZONA_FLL2_NCO_TEST_0:
+ case ARIZONA_FLL2_SYNCHRONISER_1:
+ case ARIZONA_FLL2_SYNCHRONISER_2:
+ case ARIZONA_FLL2_SYNCHRONISER_3:
+ case ARIZONA_FLL2_SYNCHRONISER_4:
+ case ARIZONA_FLL2_SYNCHRONISER_5:
+ case ARIZONA_FLL2_SYNCHRONISER_6:
+ case ARIZONA_FLL2_SYNCHRONISER_7:
+ case ARIZONA_FLL2_SPREAD_SPECTRUM:
+ case ARIZONA_FLL2_GPIO_CLOCK:
+ case ARIZONA_MIC_BIAS_CTRL_1:
+ case ARIZONA_MIC_BIAS_CTRL_2:
+ case ARIZONA_HP_CTRL_1L:
+ case ARIZONA_HP_CTRL_1R:
+ case ARIZONA_INPUT_ENABLES:
+ case ARIZONA_INPUT_ENABLES_STATUS:
+ case ARIZONA_INPUT_RATE:
+ case ARIZONA_INPUT_VOLUME_RAMP:
+ case ARIZONA_HPF_CONTROL:
+ case ARIZONA_IN1L_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_1L:
+ case ARIZONA_DMIC1L_CONTROL:
+ case ARIZONA_IN1R_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_1R:
+ case ARIZONA_DMIC1R_CONTROL:
+ case ARIZONA_IN2L_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_2L:
+ case ARIZONA_DMIC2L_CONTROL:
+ case ARIZONA_IN2R_CONTROL:
+ case ARIZONA_ADC_DIGITAL_VOLUME_2R:
+ case ARIZONA_DMIC2R_CONTROL:
+ case ARIZONA_OUTPUT_ENABLES_1:
+ case ARIZONA_OUTPUT_STATUS_1:
+ case ARIZONA_RAW_OUTPUT_STATUS_1:
+ case ARIZONA_OUTPUT_RATE_1:
+ case ARIZONA_OUTPUT_VOLUME_RAMP:
+ case ARIZONA_OUTPUT_PATH_CONFIG_1L:
+ case ARIZONA_DAC_DIGITAL_VOLUME_1L:
+ case ARIZONA_DAC_VOLUME_LIMIT_1L:
+ case ARIZONA_NOISE_GATE_SELECT_1L:
+ case ARIZONA_DAC_DIGITAL_VOLUME_1R:
+ case ARIZONA_DAC_VOLUME_LIMIT_1R:
+ case ARIZONA_NOISE_GATE_SELECT_1R:
+ case ARIZONA_DAC_DIGITAL_VOLUME_4L:
+ case ARIZONA_OUT_VOLUME_4L:
+ case ARIZONA_NOISE_GATE_SELECT_4L:
+ case ARIZONA_DAC_AEC_CONTROL_1:
+ case ARIZONA_NOISE_GATE_CONTROL:
+ case ARIZONA_HP1_SHORT_CIRCUIT_CTRL:
+ case ARIZONA_AIF1_BCLK_CTRL:
+ case ARIZONA_AIF1_TX_PIN_CTRL:
+ case ARIZONA_AIF1_RX_PIN_CTRL:
+ case ARIZONA_AIF1_RATE_CTRL:
+ case ARIZONA_AIF1_FORMAT:
+ case ARIZONA_AIF1_RX_BCLK_RATE:
+ case ARIZONA_AIF1_FRAME_CTRL_1:
+ case ARIZONA_AIF1_FRAME_CTRL_2:
+ case ARIZONA_AIF1_FRAME_CTRL_3:
+ case ARIZONA_AIF1_FRAME_CTRL_4:
+ case ARIZONA_AIF1_FRAME_CTRL_5:
+ case ARIZONA_AIF1_FRAME_CTRL_6:
+ case ARIZONA_AIF1_FRAME_CTRL_7:
+ case ARIZONA_AIF1_FRAME_CTRL_8:
+ case ARIZONA_AIF1_FRAME_CTRL_9:
+ case ARIZONA_AIF1_FRAME_CTRL_10:
+ case ARIZONA_AIF1_FRAME_CTRL_11:
+ case ARIZONA_AIF1_FRAME_CTRL_12:
+ case ARIZONA_AIF1_FRAME_CTRL_13:
+ case ARIZONA_AIF1_FRAME_CTRL_14:
+ case ARIZONA_AIF1_FRAME_CTRL_15:
+ case ARIZONA_AIF1_FRAME_CTRL_16:
+ case ARIZONA_AIF1_FRAME_CTRL_17:
+ case ARIZONA_AIF1_FRAME_CTRL_18:
+ case ARIZONA_AIF1_TX_ENABLES:
+ case ARIZONA_AIF1_RX_ENABLES:
+ case ARIZONA_AIF2_BCLK_CTRL:
+ case ARIZONA_AIF2_TX_PIN_CTRL:
+ case ARIZONA_AIF2_RX_PIN_CTRL:
+ case ARIZONA_AIF2_RATE_CTRL:
+ case ARIZONA_AIF2_FORMAT:
+ case ARIZONA_AIF2_RX_BCLK_RATE:
+ case ARIZONA_AIF2_FRAME_CTRL_1:
+ case ARIZONA_AIF2_FRAME_CTRL_2:
+ case ARIZONA_AIF2_FRAME_CTRL_3:
+ case ARIZONA_AIF2_FRAME_CTRL_4:
+ case ARIZONA_AIF2_FRAME_CTRL_5:
+ case ARIZONA_AIF2_FRAME_CTRL_6:
+ case ARIZONA_AIF2_FRAME_CTRL_7:
+ case ARIZONA_AIF2_FRAME_CTRL_8:
+ case ARIZONA_AIF2_FRAME_CTRL_11:
+ case ARIZONA_AIF2_FRAME_CTRL_12:
+ case ARIZONA_AIF2_FRAME_CTRL_13:
+ case ARIZONA_AIF2_FRAME_CTRL_14:
+ case ARIZONA_AIF2_FRAME_CTRL_15:
+ case ARIZONA_AIF2_FRAME_CTRL_16:
+ case ARIZONA_AIF2_TX_ENABLES:
+ case ARIZONA_AIF2_RX_ENABLES:
+ case ARIZONA_AIF3_BCLK_CTRL:
+ case ARIZONA_AIF3_TX_PIN_CTRL:
+ case ARIZONA_AIF3_RX_PIN_CTRL:
+ case ARIZONA_AIF3_RATE_CTRL:
+ case ARIZONA_AIF3_FORMAT:
+ case ARIZONA_AIF3_RX_BCLK_RATE:
+ case ARIZONA_AIF3_FRAME_CTRL_1:
+ case ARIZONA_AIF3_FRAME_CTRL_2:
+ case ARIZONA_AIF3_FRAME_CTRL_3:
+ case ARIZONA_AIF3_FRAME_CTRL_4:
+ case ARIZONA_AIF3_FRAME_CTRL_11:
+ case ARIZONA_AIF3_FRAME_CTRL_12:
+ case ARIZONA_AIF3_TX_ENABLES:
+ case ARIZONA_AIF3_RX_ENABLES:
+ case ARIZONA_PWM1MIX_INPUT_1_SOURCE:
+ case ARIZONA_PWM1MIX_INPUT_1_VOLUME:
+ case ARIZONA_PWM1MIX_INPUT_2_SOURCE:
+ case ARIZONA_PWM1MIX_INPUT_2_VOLUME:
+ case ARIZONA_PWM1MIX_INPUT_3_SOURCE:
+ case ARIZONA_PWM1MIX_INPUT_3_VOLUME:
+ case ARIZONA_PWM1MIX_INPUT_4_SOURCE:
+ case ARIZONA_PWM1MIX_INPUT_4_VOLUME:
+ case ARIZONA_PWM2MIX_INPUT_1_SOURCE:
+ case ARIZONA_PWM2MIX_INPUT_1_VOLUME:
+ case ARIZONA_PWM2MIX_INPUT_2_SOURCE:
+ case ARIZONA_PWM2MIX_INPUT_2_VOLUME:
+ case ARIZONA_PWM2MIX_INPUT_3_SOURCE:
+ case ARIZONA_PWM2MIX_INPUT_3_VOLUME:
+ case ARIZONA_PWM2MIX_INPUT_4_SOURCE:
+ case ARIZONA_PWM2MIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT1LMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT1LMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT1LMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT1LMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT1LMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT1LMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT1LMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT1LMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT1RMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT1RMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT1RMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT1RMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT1RMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT1RMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT1RMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT1RMIX_INPUT_4_VOLUME:
+ case ARIZONA_OUT4LMIX_INPUT_1_SOURCE:
+ case ARIZONA_OUT4LMIX_INPUT_1_VOLUME:
+ case ARIZONA_OUT4LMIX_INPUT_2_SOURCE:
+ case ARIZONA_OUT4LMIX_INPUT_2_VOLUME:
+ case ARIZONA_OUT4LMIX_INPUT_3_SOURCE:
+ case ARIZONA_OUT4LMIX_INPUT_3_VOLUME:
+ case ARIZONA_OUT4LMIX_INPUT_4_SOURCE:
+ case ARIZONA_OUT4LMIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX1MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX1MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX1MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX1MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX1MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX1MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX1MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX2MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX2MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX2MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX2MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX2MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX2MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX2MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX3MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX3MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX3MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX3MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX3MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX3MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX3MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX4MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX4MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX4MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX4MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX4MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX4MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX4MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX5MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX5MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX5MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX5MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX5MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX5MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX5MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX6MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX6MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX6MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX6MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX6MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX6MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX6MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX7MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX7MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX7MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX7MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX7MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX7MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX7MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF1TX8MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF1TX8MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF1TX8MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF1TX8MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF1TX8MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF1TX8MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF1TX8MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF2TX1MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF2TX1MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF2TX1MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF2TX1MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF2TX1MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF2TX1MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF2TX1MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF2TX2MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF2TX2MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF2TX2MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF2TX2MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF2TX2MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF2TX2MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF2TX2MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF2TX3MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF2TX3MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF2TX3MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF2TX3MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF2TX3MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF2TX3MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF2TX3MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF2TX4MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF2TX4MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF2TX4MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF2TX4MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF2TX4MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF2TX4MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF2TX4MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF2TX5MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF2TX5MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF2TX5MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF2TX5MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF2TX5MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF2TX5MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF2TX5MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF2TX6MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF2TX6MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF2TX6MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF2TX6MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF2TX6MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF2TX6MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF2TX6MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF3TX1MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF3TX1MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF3TX1MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF3TX1MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF3TX1MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF3TX1MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF3TX1MIX_INPUT_4_VOLUME:
+ case ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_AIF3TX2MIX_INPUT_1_VOLUME:
+ case ARIZONA_AIF3TX2MIX_INPUT_2_SOURCE:
+ case ARIZONA_AIF3TX2MIX_INPUT_2_VOLUME:
+ case ARIZONA_AIF3TX2MIX_INPUT_3_SOURCE:
+ case ARIZONA_AIF3TX2MIX_INPUT_3_VOLUME:
+ case ARIZONA_AIF3TX2MIX_INPUT_4_SOURCE:
+ case ARIZONA_AIF3TX2MIX_INPUT_4_VOLUME:
+ case ARIZONA_EQ1MIX_INPUT_1_SOURCE:
+ case ARIZONA_EQ1MIX_INPUT_1_VOLUME:
+ case ARIZONA_EQ1MIX_INPUT_2_SOURCE:
+ case ARIZONA_EQ1MIX_INPUT_2_VOLUME:
+ case ARIZONA_EQ1MIX_INPUT_3_SOURCE:
+ case ARIZONA_EQ1MIX_INPUT_3_VOLUME:
+ case ARIZONA_EQ1MIX_INPUT_4_SOURCE:
+ case ARIZONA_EQ1MIX_INPUT_4_VOLUME:
+ case ARIZONA_EQ2MIX_INPUT_1_SOURCE:
+ case ARIZONA_EQ2MIX_INPUT_1_VOLUME:
+ case ARIZONA_EQ2MIX_INPUT_2_SOURCE:
+ case ARIZONA_EQ2MIX_INPUT_2_VOLUME:
+ case ARIZONA_EQ2MIX_INPUT_3_SOURCE:
+ case ARIZONA_EQ2MIX_INPUT_3_VOLUME:
+ case ARIZONA_EQ2MIX_INPUT_4_SOURCE:
+ case ARIZONA_EQ2MIX_INPUT_4_VOLUME:
+ case ARIZONA_DRC1LMIX_INPUT_1_SOURCE:
+ case ARIZONA_DRC1LMIX_INPUT_1_VOLUME:
+ case ARIZONA_DRC1LMIX_INPUT_2_SOURCE:
+ case ARIZONA_DRC1LMIX_INPUT_2_VOLUME:
+ case ARIZONA_DRC1LMIX_INPUT_3_SOURCE:
+ case ARIZONA_DRC1LMIX_INPUT_3_VOLUME:
+ case ARIZONA_DRC1LMIX_INPUT_4_SOURCE:
+ case ARIZONA_DRC1LMIX_INPUT_4_VOLUME:
+ case ARIZONA_DRC1RMIX_INPUT_1_SOURCE:
+ case ARIZONA_DRC1RMIX_INPUT_1_VOLUME:
+ case ARIZONA_DRC1RMIX_INPUT_2_SOURCE:
+ case ARIZONA_DRC1RMIX_INPUT_2_VOLUME:
+ case ARIZONA_DRC1RMIX_INPUT_3_SOURCE:
+ case ARIZONA_DRC1RMIX_INPUT_3_VOLUME:
+ case ARIZONA_DRC1RMIX_INPUT_4_SOURCE:
+ case ARIZONA_DRC1RMIX_INPUT_4_VOLUME:
+ case ARIZONA_DRC2LMIX_INPUT_1_SOURCE:
+ case ARIZONA_DRC2LMIX_INPUT_1_VOLUME:
+ case ARIZONA_DRC2LMIX_INPUT_2_SOURCE:
+ case ARIZONA_DRC2LMIX_INPUT_2_VOLUME:
+ case ARIZONA_DRC2LMIX_INPUT_3_SOURCE:
+ case ARIZONA_DRC2LMIX_INPUT_3_VOLUME:
+ case ARIZONA_DRC2LMIX_INPUT_4_SOURCE:
+ case ARIZONA_DRC2LMIX_INPUT_4_VOLUME:
+ case ARIZONA_DRC2RMIX_INPUT_1_SOURCE:
+ case ARIZONA_DRC2RMIX_INPUT_1_VOLUME:
+ case ARIZONA_DRC2RMIX_INPUT_2_SOURCE:
+ case ARIZONA_DRC2RMIX_INPUT_2_VOLUME:
+ case ARIZONA_DRC2RMIX_INPUT_3_SOURCE:
+ case ARIZONA_DRC2RMIX_INPUT_3_VOLUME:
+ case ARIZONA_DRC2RMIX_INPUT_4_SOURCE:
+ case ARIZONA_DRC2RMIX_INPUT_4_VOLUME:
+ case ARIZONA_HPLP1MIX_INPUT_1_SOURCE:
+ case ARIZONA_HPLP1MIX_INPUT_1_VOLUME:
+ case ARIZONA_HPLP1MIX_INPUT_2_SOURCE:
+ case ARIZONA_HPLP1MIX_INPUT_2_VOLUME:
+ case ARIZONA_HPLP1MIX_INPUT_3_SOURCE:
+ case ARIZONA_HPLP1MIX_INPUT_3_VOLUME:
+ case ARIZONA_HPLP1MIX_INPUT_4_SOURCE:
+ case ARIZONA_HPLP1MIX_INPUT_4_VOLUME:
+ case ARIZONA_HPLP2MIX_INPUT_1_SOURCE:
+ case ARIZONA_HPLP2MIX_INPUT_1_VOLUME:
+ case ARIZONA_HPLP2MIX_INPUT_2_SOURCE:
+ case ARIZONA_HPLP2MIX_INPUT_2_VOLUME:
+ case ARIZONA_HPLP2MIX_INPUT_3_SOURCE:
+ case ARIZONA_HPLP2MIX_INPUT_3_VOLUME:
+ case ARIZONA_HPLP2MIX_INPUT_4_SOURCE:
+ case ARIZONA_HPLP2MIX_INPUT_4_VOLUME:
+ case ARIZONA_HPLP3MIX_INPUT_1_SOURCE:
+ case ARIZONA_HPLP3MIX_INPUT_1_VOLUME:
+ case ARIZONA_HPLP3MIX_INPUT_2_SOURCE:
+ case ARIZONA_HPLP3MIX_INPUT_2_VOLUME:
+ case ARIZONA_HPLP3MIX_INPUT_3_SOURCE:
+ case ARIZONA_HPLP3MIX_INPUT_3_VOLUME:
+ case ARIZONA_HPLP3MIX_INPUT_4_SOURCE:
+ case ARIZONA_HPLP3MIX_INPUT_4_VOLUME:
+ case ARIZONA_HPLP4MIX_INPUT_1_SOURCE:
+ case ARIZONA_HPLP4MIX_INPUT_1_VOLUME:
+ case ARIZONA_HPLP4MIX_INPUT_2_SOURCE:
+ case ARIZONA_HPLP4MIX_INPUT_2_VOLUME:
+ case ARIZONA_HPLP4MIX_INPUT_3_SOURCE:
+ case ARIZONA_HPLP4MIX_INPUT_3_VOLUME:
+ case ARIZONA_HPLP4MIX_INPUT_4_SOURCE:
+ case ARIZONA_HPLP4MIX_INPUT_4_VOLUME:
+ case ARIZONA_DSP2LMIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP2LMIX_INPUT_1_VOLUME:
+ case ARIZONA_DSP2LMIX_INPUT_2_SOURCE:
+ case ARIZONA_DSP2LMIX_INPUT_2_VOLUME:
+ case ARIZONA_DSP2LMIX_INPUT_3_SOURCE:
+ case ARIZONA_DSP2LMIX_INPUT_3_VOLUME:
+ case ARIZONA_DSP2LMIX_INPUT_4_SOURCE:
+ case ARIZONA_DSP2LMIX_INPUT_4_VOLUME:
+ case ARIZONA_DSP2RMIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP2RMIX_INPUT_1_VOLUME:
+ case ARIZONA_DSP2RMIX_INPUT_2_SOURCE:
+ case ARIZONA_DSP2RMIX_INPUT_2_VOLUME:
+ case ARIZONA_DSP2RMIX_INPUT_3_SOURCE:
+ case ARIZONA_DSP2RMIX_INPUT_3_VOLUME:
+ case ARIZONA_DSP2RMIX_INPUT_4_SOURCE:
+ case ARIZONA_DSP2RMIX_INPUT_4_VOLUME:
+ case ARIZONA_DSP2AUX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP2AUX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP2AUX3MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP2AUX4MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP2AUX5MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP2AUX6MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP3LMIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP3LMIX_INPUT_1_VOLUME:
+ case ARIZONA_DSP3LMIX_INPUT_2_SOURCE:
+ case ARIZONA_DSP3LMIX_INPUT_2_VOLUME:
+ case ARIZONA_DSP3LMIX_INPUT_3_SOURCE:
+ case ARIZONA_DSP3LMIX_INPUT_3_VOLUME:
+ case ARIZONA_DSP3LMIX_INPUT_4_SOURCE:
+ case ARIZONA_DSP3LMIX_INPUT_4_VOLUME:
+ case ARIZONA_DSP3RMIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP3RMIX_INPUT_1_VOLUME:
+ case ARIZONA_DSP3RMIX_INPUT_2_SOURCE:
+ case ARIZONA_DSP3RMIX_INPUT_2_VOLUME:
+ case ARIZONA_DSP3RMIX_INPUT_3_SOURCE:
+ case ARIZONA_DSP3RMIX_INPUT_3_VOLUME:
+ case ARIZONA_DSP3RMIX_INPUT_4_SOURCE:
+ case ARIZONA_DSP3RMIX_INPUT_4_VOLUME:
+ case ARIZONA_DSP3AUX1MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP3AUX2MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP3AUX3MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP3AUX4MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP3AUX5MIX_INPUT_1_SOURCE:
+ case ARIZONA_DSP3AUX6MIX_INPUT_1_SOURCE:
+ case ARIZONA_ASRC1LMIX_INPUT_1_SOURCE:
+ case ARIZONA_ASRC1RMIX_INPUT_1_SOURCE:
+ case ARIZONA_ASRC2LMIX_INPUT_1_SOURCE:
+ case ARIZONA_ASRC2RMIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2DEC3MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2DEC4MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2INT3MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC2INT4MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC3DEC1MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC3DEC2MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC3DEC3MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC3DEC4MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC3INT1MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC3INT2MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC3INT3MIX_INPUT_1_SOURCE:
+ case ARIZONA_ISRC3INT4MIX_INPUT_1_SOURCE:
+ case ARIZONA_GPIO1_CTRL:
+ case ARIZONA_GPIO2_CTRL:
+ case ARIZONA_IRQ_CTRL_1:
+ case ARIZONA_GPIO_DEBOUNCE_CONFIG:
+ case ARIZONA_MISC_PAD_CTRL_1:
+ case ARIZONA_MISC_PAD_CTRL_2:
+ case ARIZONA_MISC_PAD_CTRL_3:
+ case ARIZONA_MISC_PAD_CTRL_4:
+ case ARIZONA_MISC_PAD_CTRL_5:
+ case ARIZONA_MISC_PAD_CTRL_6:
+ case ARIZONA_MISC_PAD_CTRL_7:
+ case ARIZONA_MISC_PAD_CTRL_9:
+ case ARIZONA_MISC_PAD_CTRL_10:
+ case ARIZONA_MISC_PAD_CTRL_11:
+ case ARIZONA_MISC_PAD_CTRL_12:
+ case ARIZONA_MISC_PAD_CTRL_13:
+ case ARIZONA_MISC_PAD_CTRL_14:
+ case ARIZONA_MISC_PAD_CTRL_16:
+ case ARIZONA_INTERRUPT_STATUS_1:
+ case ARIZONA_INTERRUPT_STATUS_2:
+ case ARIZONA_INTERRUPT_STATUS_3:
+ case ARIZONA_INTERRUPT_STATUS_4:
+ case ARIZONA_INTERRUPT_STATUS_5:
+ case ARIZONA_INTERRUPT_STATUS_6:
+ case ARIZONA_INTERRUPT_STATUS_1_MASK:
+ case ARIZONA_INTERRUPT_STATUS_2_MASK:
+ case ARIZONA_INTERRUPT_STATUS_3_MASK:
+ case ARIZONA_INTERRUPT_STATUS_4_MASK:
+ case ARIZONA_INTERRUPT_STATUS_5_MASK:
+ case ARIZONA_INTERRUPT_STATUS_6_MASK:
+ case ARIZONA_INTERRUPT_CONTROL:
+ case ARIZONA_IRQ2_STATUS_1:
+ case ARIZONA_IRQ2_STATUS_2:
+ case ARIZONA_IRQ2_STATUS_3:
+ case ARIZONA_IRQ2_STATUS_4:
+ case ARIZONA_IRQ2_STATUS_5:
+ case ARIZONA_IRQ2_STATUS_6:
+ case ARIZONA_IRQ2_STATUS_1_MASK:
+ case ARIZONA_IRQ2_STATUS_2_MASK:
+ case ARIZONA_IRQ2_STATUS_3_MASK:
+ case ARIZONA_IRQ2_STATUS_4_MASK:
+ case ARIZONA_IRQ2_STATUS_5_MASK:
+ case ARIZONA_IRQ2_STATUS_6_MASK:
+ case ARIZONA_IRQ2_CONTROL:
+ case ARIZONA_INTERRUPT_RAW_STATUS_2:
+ case ARIZONA_INTERRUPT_RAW_STATUS_3:
+ case ARIZONA_INTERRUPT_RAW_STATUS_4:
+ case ARIZONA_INTERRUPT_RAW_STATUS_5:
+ case ARIZONA_INTERRUPT_RAW_STATUS_6:
+ case ARIZONA_INTERRUPT_RAW_STATUS_7:
+ case ARIZONA_INTERRUPT_RAW_STATUS_8:
+ case ARIZONA_INTERRUPT_RAW_STATUS_9:
+ case ARIZONA_IRQ_PIN_STATUS:
+ case ARIZONA_FX_CTRL1:
+ case ARIZONA_FX_CTRL2:
+ case ARIZONA_EQ1_1:
+ case ARIZONA_EQ1_2:
+ case ARIZONA_EQ1_3:
+ case ARIZONA_EQ1_4:
+ case ARIZONA_EQ1_5:
+ case ARIZONA_EQ1_6:
+ case ARIZONA_EQ1_7:
+ case ARIZONA_EQ1_8:
+ case ARIZONA_EQ1_9:
+ case ARIZONA_EQ1_10:
+ case ARIZONA_EQ1_11:
+ case ARIZONA_EQ1_12:
+ case ARIZONA_EQ1_13:
+ case ARIZONA_EQ1_14:
+ case ARIZONA_EQ1_15:
+ case ARIZONA_EQ1_16:
+ case ARIZONA_EQ1_17:
+ case ARIZONA_EQ1_18:
+ case ARIZONA_EQ1_19:
+ case ARIZONA_EQ1_20:
+ case ARIZONA_EQ1_21:
+ case ARIZONA_EQ2_1:
+ case ARIZONA_EQ2_2:
+ case ARIZONA_EQ2_3:
+ case ARIZONA_EQ2_4:
+ case ARIZONA_EQ2_5:
+ case ARIZONA_EQ2_6:
+ case ARIZONA_EQ2_7:
+ case ARIZONA_EQ2_8:
+ case ARIZONA_EQ2_9:
+ case ARIZONA_EQ2_10:
+ case ARIZONA_EQ2_11:
+ case ARIZONA_EQ2_12:
+ case ARIZONA_EQ2_13:
+ case ARIZONA_EQ2_14:
+ case ARIZONA_EQ2_15:
+ case ARIZONA_EQ2_16:
+ case ARIZONA_EQ2_17:
+ case ARIZONA_EQ2_18:
+ case ARIZONA_EQ2_19:
+ case ARIZONA_EQ2_20:
+ case ARIZONA_EQ2_21:
+ case ARIZONA_DRC1_CTRL1:
+ case ARIZONA_DRC1_CTRL2:
+ case ARIZONA_DRC1_CTRL3:
+ case ARIZONA_DRC1_CTRL4:
+ case ARIZONA_DRC1_CTRL5:
+ case ARIZONA_DRC2_CTRL1:
+ case ARIZONA_DRC2_CTRL2:
+ case ARIZONA_DRC2_CTRL3:
+ case ARIZONA_DRC2_CTRL4:
+ case ARIZONA_DRC2_CTRL5:
+ case ARIZONA_HPLPF1_1:
+ case ARIZONA_HPLPF1_2:
+ case ARIZONA_HPLPF2_1:
+ case ARIZONA_HPLPF2_2:
+ case ARIZONA_HPLPF3_1:
+ case ARIZONA_HPLPF3_2:
+ case ARIZONA_HPLPF4_1:
+ case ARIZONA_HPLPF4_2:
+ case ARIZONA_ASRC_ENABLE:
+ case ARIZONA_ASRC_STATUS:
+ case ARIZONA_ASRC_RATE1:
+ case ARIZONA_ASRC_RATE2:
+ case ARIZONA_ISRC_1_CTRL_1:
+ case ARIZONA_ISRC_1_CTRL_2:
+ case ARIZONA_ISRC_1_CTRL_3:
+ case ARIZONA_ISRC_2_CTRL_1:
+ case ARIZONA_ISRC_2_CTRL_2:
+ case ARIZONA_ISRC_2_CTRL_3:
+ case ARIZONA_ISRC_3_CTRL_1:
+ case ARIZONA_ISRC_3_CTRL_2:
+ case ARIZONA_ISRC_3_CTRL_3:
+ case ARIZONA_DSP2_CONTROL_1:
+ case ARIZONA_DSP2_CLOCKING_1:
+ case ARIZONA_DSP2_STATUS_1:
+ case ARIZONA_DSP2_STATUS_2:
+ case ARIZONA_DSP2_STATUS_3:
+ case ARIZONA_DSP2_STATUS_4:
+ case ARIZONA_DSP2_WDMA_BUFFER_1:
+ case ARIZONA_DSP2_WDMA_BUFFER_2:
+ case ARIZONA_DSP2_WDMA_BUFFER_3:
+ case ARIZONA_DSP2_WDMA_BUFFER_4:
+ case ARIZONA_DSP2_WDMA_BUFFER_5:
+ case ARIZONA_DSP2_WDMA_BUFFER_6:
+ case ARIZONA_DSP2_WDMA_BUFFER_7:
+ case ARIZONA_DSP2_WDMA_BUFFER_8:
+ case ARIZONA_DSP2_RDMA_BUFFER_1:
+ case ARIZONA_DSP2_RDMA_BUFFER_2:
+ case ARIZONA_DSP2_RDMA_BUFFER_3:
+ case ARIZONA_DSP2_RDMA_BUFFER_4:
+ case ARIZONA_DSP2_RDMA_BUFFER_5:
+ case ARIZONA_DSP2_RDMA_BUFFER_6:
+ case ARIZONA_DSP2_WDMA_CONFIG_1:
+ case ARIZONA_DSP2_WDMA_CONFIG_2:
+ case ARIZONA_DSP2_WDMA_OFFSET_1:
+ case ARIZONA_DSP2_RDMA_CONFIG_1:
+ case ARIZONA_DSP2_RDMA_OFFSET_1:
+ case ARIZONA_DSP2_EXTERNAL_START_SELECT_1:
+ case ARIZONA_DSP2_SCRATCH_0:
+ case ARIZONA_DSP2_SCRATCH_1:
+ case ARIZONA_DSP2_SCRATCH_2:
+ case ARIZONA_DSP2_SCRATCH_3:
+ case ARIZONA_DSP3_CONTROL_1:
+ case ARIZONA_DSP3_CLOCKING_1:
+ case ARIZONA_DSP3_STATUS_1:
+ case ARIZONA_DSP3_STATUS_2:
+ case ARIZONA_DSP3_STATUS_3:
+ case ARIZONA_DSP3_STATUS_4:
+ case ARIZONA_DSP3_WDMA_BUFFER_1:
+ case ARIZONA_DSP3_WDMA_BUFFER_2:
+ case ARIZONA_DSP3_WDMA_BUFFER_3:
+ case ARIZONA_DSP3_WDMA_BUFFER_4:
+ case ARIZONA_DSP3_WDMA_BUFFER_5:
+ case ARIZONA_DSP3_WDMA_BUFFER_6:
+ case ARIZONA_DSP3_WDMA_BUFFER_7:
+ case ARIZONA_DSP3_WDMA_BUFFER_8:
+ case ARIZONA_DSP3_RDMA_BUFFER_1:
+ case ARIZONA_DSP3_RDMA_BUFFER_2:
+ case ARIZONA_DSP3_RDMA_BUFFER_3:
+ case ARIZONA_DSP3_RDMA_BUFFER_4:
+ case ARIZONA_DSP3_RDMA_BUFFER_5:
+ case ARIZONA_DSP3_RDMA_BUFFER_6:
+ case ARIZONA_DSP3_WDMA_CONFIG_1:
+ case ARIZONA_DSP3_WDMA_CONFIG_2:
+ case ARIZONA_DSP3_WDMA_OFFSET_1:
+ case ARIZONA_DSP3_RDMA_CONFIG_1:
+ case ARIZONA_DSP3_RDMA_OFFSET_1:
+ case ARIZONA_DSP3_EXTERNAL_START_SELECT_1:
+ case ARIZONA_DSP3_SCRATCH_0:
+ case ARIZONA_DSP3_SCRATCH_1:
+ case ARIZONA_DSP3_SCRATCH_2:
+ case ARIZONA_DSP3_SCRATCH_3:
+ return true;
+ default:
+ return cs47l24_is_adsp_memory(reg);
+ }
+}
+
+static bool cs47l24_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ARIZONA_SOFTWARE_RESET:
+ case ARIZONA_DEVICE_REVISION:
+ case ARIZONA_WRITE_SEQUENCER_CTRL_0:
+ case ARIZONA_WRITE_SEQUENCER_CTRL_1:
+ case ARIZONA_WRITE_SEQUENCER_CTRL_2:
+ case ARIZONA_HAPTICS_STATUS:
+ case ARIZONA_SAMPLE_RATE_1_STATUS:
+ case ARIZONA_SAMPLE_RATE_2_STATUS:
+ case ARIZONA_SAMPLE_RATE_3_STATUS:
+ case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS:
+ case ARIZONA_ASYNC_SAMPLE_RATE_2_STATUS:
+ case ARIZONA_HP_CTRL_1L:
+ case ARIZONA_HP_CTRL_1R:
+ case ARIZONA_INPUT_ENABLES_STATUS:
+ case ARIZONA_OUTPUT_STATUS_1:
+ case ARIZONA_RAW_OUTPUT_STATUS_1:
+ case ARIZONA_INTERRUPT_STATUS_1:
+ case ARIZONA_INTERRUPT_STATUS_2:
+ case ARIZONA_INTERRUPT_STATUS_3:
+ case ARIZONA_INTERRUPT_STATUS_4:
+ case ARIZONA_INTERRUPT_STATUS_5:
+ case ARIZONA_INTERRUPT_STATUS_6:
+ case ARIZONA_IRQ2_STATUS_1:
+ case ARIZONA_IRQ2_STATUS_2:
+ case ARIZONA_IRQ2_STATUS_3:
+ case ARIZONA_IRQ2_STATUS_4:
+ case ARIZONA_IRQ2_STATUS_5:
+ case ARIZONA_IRQ2_STATUS_6:
+ case ARIZONA_INTERRUPT_RAW_STATUS_2:
+ case ARIZONA_INTERRUPT_RAW_STATUS_3:
+ case ARIZONA_INTERRUPT_RAW_STATUS_4:
+ case ARIZONA_INTERRUPT_RAW_STATUS_5:
+ case ARIZONA_INTERRUPT_RAW_STATUS_6:
+ case ARIZONA_INTERRUPT_RAW_STATUS_7:
+ case ARIZONA_INTERRUPT_RAW_STATUS_8:
+ case ARIZONA_INTERRUPT_RAW_STATUS_9:
+ case ARIZONA_IRQ_PIN_STATUS:
+ case ARIZONA_FX_CTRL2:
+ case ARIZONA_ASRC_STATUS:
+ case ARIZONA_DSP2_STATUS_1:
+ case ARIZONA_DSP2_STATUS_2:
+ case ARIZONA_DSP2_STATUS_3:
+ case ARIZONA_DSP2_STATUS_4:
+ case ARIZONA_DSP2_WDMA_BUFFER_1:
+ case ARIZONA_DSP2_WDMA_BUFFER_2:
+ case ARIZONA_DSP2_WDMA_BUFFER_3:
+ case ARIZONA_DSP2_WDMA_BUFFER_4:
+ case ARIZONA_DSP2_WDMA_BUFFER_5:
+ case ARIZONA_DSP2_WDMA_BUFFER_6:
+ case ARIZONA_DSP2_WDMA_BUFFER_7:
+ case ARIZONA_DSP2_WDMA_BUFFER_8:
+ case ARIZONA_DSP2_RDMA_BUFFER_1:
+ case ARIZONA_DSP2_RDMA_BUFFER_2:
+ case ARIZONA_DSP2_RDMA_BUFFER_3:
+ case ARIZONA_DSP2_RDMA_BUFFER_4:
+ case ARIZONA_DSP2_RDMA_BUFFER_5:
+ case ARIZONA_DSP2_RDMA_BUFFER_6:
+ case ARIZONA_DSP2_WDMA_CONFIG_1:
+ case ARIZONA_DSP2_WDMA_CONFIG_2:
+ case ARIZONA_DSP2_WDMA_OFFSET_1:
+ case ARIZONA_DSP2_RDMA_CONFIG_1:
+ case ARIZONA_DSP2_RDMA_OFFSET_1:
+ case ARIZONA_DSP2_EXTERNAL_START_SELECT_1:
+ case ARIZONA_DSP2_SCRATCH_0:
+ case ARIZONA_DSP2_SCRATCH_1:
+ case ARIZONA_DSP2_SCRATCH_2:
+ case ARIZONA_DSP2_SCRATCH_3:
+ case ARIZONA_DSP2_CLOCKING_1:
+ case ARIZONA_DSP3_STATUS_1:
+ case ARIZONA_DSP3_STATUS_2:
+ case ARIZONA_DSP3_STATUS_3:
+ case ARIZONA_DSP3_STATUS_4:
+ case ARIZONA_DSP3_WDMA_BUFFER_1:
+ case ARIZONA_DSP3_WDMA_BUFFER_2:
+ case ARIZONA_DSP3_WDMA_BUFFER_3:
+ case ARIZONA_DSP3_WDMA_BUFFER_4:
+ case ARIZONA_DSP3_WDMA_BUFFER_5:
+ case ARIZONA_DSP3_WDMA_BUFFER_6:
+ case ARIZONA_DSP3_WDMA_BUFFER_7:
+ case ARIZONA_DSP3_WDMA_BUFFER_8:
+ case ARIZONA_DSP3_RDMA_BUFFER_1:
+ case ARIZONA_DSP3_RDMA_BUFFER_2:
+ case ARIZONA_DSP3_RDMA_BUFFER_3:
+ case ARIZONA_DSP3_RDMA_BUFFER_4:
+ case ARIZONA_DSP3_RDMA_BUFFER_5:
+ case ARIZONA_DSP3_RDMA_BUFFER_6:
+ case ARIZONA_DSP3_WDMA_CONFIG_1:
+ case ARIZONA_DSP3_WDMA_CONFIG_2:
+ case ARIZONA_DSP3_WDMA_OFFSET_1:
+ case ARIZONA_DSP3_RDMA_CONFIG_1:
+ case ARIZONA_DSP3_RDMA_OFFSET_1:
+ case ARIZONA_DSP3_EXTERNAL_START_SELECT_1:
+ case ARIZONA_DSP3_SCRATCH_0:
+ case ARIZONA_DSP3_SCRATCH_1:
+ case ARIZONA_DSP3_SCRATCH_2:
+ case ARIZONA_DSP3_SCRATCH_3:
+ case ARIZONA_DSP3_CLOCKING_1:
+ return true;
+ default:
+ return cs47l24_is_adsp_memory(reg);
+ }
+}
+
+#define CS47L24_MAX_REGISTER 0x3b3fff
+
+const struct regmap_config cs47l24_spi_regmap = {
+ .reg_bits = 32,
+ .pad_bits = 16,
+ .val_bits = 16,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+
+ .max_register = CS47L24_MAX_REGISTER,
+ .readable_reg = cs47l24_readable_register,
+ .volatile_reg = cs47l24_volatile_register,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = cs47l24_reg_default,
+ .num_reg_defaults = ARRAY_SIZE(cs47l24_reg_default),
+};
+EXPORT_SYMBOL_GPL(cs47l24_spi_regmap);
+
diff --git a/drivers/mfd/cs5535-mfd.c b/drivers/mfd/cs5535-mfd.c
index be91cb5d6e78..f9d277ff4aaf 100644
--- a/drivers/mfd/cs5535-mfd.c
+++ b/drivers/mfd/cs5535-mfd.c
@@ -60,6 +60,7 @@ static int cs5535_mfd_res_enable(struct platform_device *pdev)
static int cs5535_mfd_res_disable(struct platform_device *pdev)
{
struct resource *res;
+
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!res) {
dev_err(&pdev->dev, "can't fetch device resource info\n");
@@ -114,7 +115,10 @@ static struct mfd_cell cs5535_mfd_cells[] = {
#ifdef CONFIG_OLPC
static void cs5535_clone_olpc_cells(void)
{
- const char *acpi_clones[] = { "olpc-xo1-pm-acpi", "olpc-xo1-sci-acpi" };
+ static const char *acpi_clones[] = {
+ "olpc-xo1-pm-acpi",
+ "olpc-xo1-sci-acpi"
+ };
if (!machine_is_olpc())
return;
diff --git a/drivers/mfd/da903x.c b/drivers/mfd/da903x.c
index 37e4426ef061..09f367571c58 100644
--- a/drivers/mfd/da903x.c
+++ b/drivers/mfd/da903x.c
@@ -2,10 +2,10 @@
* Base driver for Dialog Semiconductor DA9030/DA9034
*
* Copyright (C) 2008 Compulab, Ltd.
- * Mike Rapoport <mike@compulab.co.il>
+ * Mike Rapoport <mike@compulab.co.il>
*
* Copyright (C) 2006-2008 Marvell International Ltd.
- * Eric Miao <eric.miao@marvell.com>
+ * Eric Miao <eric.miao@marvell.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
@@ -60,7 +60,7 @@ struct da903x_chip_ops {
struct da903x_chip {
struct i2c_client *client;
struct device *dev;
- struct da903x_chip_ops *ops;
+ const struct da903x_chip_ops *ops;
int type;
uint32_t events_mask;
@@ -424,7 +424,7 @@ static irqreturn_t da903x_irq_handler(int irq, void *data)
return IRQ_HANDLED;
}
-static struct da903x_chip_ops da903x_ops[] = {
+static const struct da903x_chip_ops da903x_ops[] = {
[0] = {
.init_chip = da9030_init_chip,
.unmask_events = da9030_unmask_events,
@@ -565,6 +565,6 @@ static void __exit da903x_exit(void)
module_exit(da903x_exit);
MODULE_DESCRIPTION("PMIC Driver for Dialog Semiconductor DA9034");
-MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"
- "Mike Rapoport <mike@compulab.co.il>");
-MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
+MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c
index 2697ffb08009..578e881067a5 100644
--- a/drivers/mfd/da9052-i2c.c
+++ b/drivers/mfd/da9052-i2c.c
@@ -70,7 +70,7 @@ static int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg)
case DA9053_BA:
case DA9053_BB:
/* A dummy read to a safe register address. */
- if (!i2c_safe_reg(reg))
+ if (!i2c_safe_reg(reg))
return regmap_read(da9052->regmap,
DA9052_PARK_REGISTER,
&val);
diff --git a/drivers/mfd/da9052-irq.c b/drivers/mfd/da9052-irq.c
index f4cb4613140b..cd4ca849ca44 100644
--- a/drivers/mfd/da9052-irq.c
+++ b/drivers/mfd/da9052-irq.c
@@ -283,7 +283,7 @@ regmap_err:
int da9052_irq_exit(struct da9052 *da9052)
{
- da9052_free_irq(da9052, DA9052_IRQ_ADC_EOM , da9052);
+ da9052_free_irq(da9052, DA9052_IRQ_ADC_EOM, da9052);
regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data);
return 0;
diff --git a/drivers/mfd/davinci_voicecodec.c b/drivers/mfd/davinci_voicecodec.c
index 9bbc642a7b9d..dff2f19296b8 100644
--- a/drivers/mfd/davinci_voicecodec.c
+++ b/drivers/mfd/davinci_voicecodec.c
@@ -47,11 +47,8 @@ static int __init davinci_vc_probe(struct platform_device *pdev)
davinci_vc = devm_kzalloc(&pdev->dev,
sizeof(struct davinci_vc), GFP_KERNEL);
- if (!davinci_vc) {
- dev_dbg(&pdev->dev,
- "could not allocate memory for private data\n");
+ if (!davinci_vc)
return -ENOMEM;
- }
davinci_vc->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(davinci_vc->clk)) {
diff --git a/drivers/mfd/dm355evm_msp.c b/drivers/mfd/dm355evm_msp.c
index 4c826f78acd0..bf3e0b21b247 100644
--- a/drivers/mfd/dm355evm_msp.c
+++ b/drivers/mfd/dm355evm_msp.c
@@ -147,7 +147,7 @@ static int msp_gpio_get(struct gpio_chip *chip, unsigned offset)
return status;
if (reg == DM355EVM_MSP_LED)
msp_led_cache = status;
- return status & MSP_GPIO_MASK(offset);
+ return !!(status & MSP_GPIO_MASK(offset));
}
static int msp_gpio_out(struct gpio_chip *chip, unsigned offset, int value)
diff --git a/drivers/mfd/htc-egpio.c b/drivers/mfd/htc-egpio.c
index 6ccaf90d98fd..e4f4a31b76d9 100644
--- a/drivers/mfd/htc-egpio.c
+++ b/drivers/mfd/htc-egpio.c
@@ -163,7 +163,7 @@ static int egpio_get(struct gpio_chip *chip, unsigned offset)
value = egpio_readw(ei, reg);
pr_debug("readw(%p + %x) = %x\n",
ei->base_addr, reg << ei->bus_shift, value);
- return value & bit;
+ return !!(value & bit);
}
static int egpio_direction_input(struct gpio_chip *chip, unsigned offset)
diff --git a/drivers/mfd/intel-lpss-acpi.c b/drivers/mfd/intel-lpss-acpi.c
index b6fd9041f82f..06f00d60be46 100644
--- a/drivers/mfd/intel-lpss-acpi.c
+++ b/drivers/mfd/intel-lpss-acpi.c
@@ -18,6 +18,7 @@
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include "intel-lpss.h"
@@ -25,6 +26,20 @@ static const struct intel_lpss_platform_info spt_info = {
.clk_rate = 120000000,
};
+static struct property_entry spt_i2c_properties[] = {
+ PROPERTY_ENTRY_U32("i2c-sda-hold-time-ns", 230),
+ { },
+};
+
+static struct property_set spt_i2c_pset = {
+ .properties = spt_i2c_properties,
+};
+
+static const struct intel_lpss_platform_info spt_i2c_info = {
+ .clk_rate = 120000000,
+ .pset = &spt_i2c_pset,
+};
+
static const struct intel_lpss_platform_info bxt_info = {
.clk_rate = 100000000,
};
@@ -35,8 +50,8 @@ static const struct intel_lpss_platform_info bxt_i2c_info = {
static const struct acpi_device_id intel_lpss_acpi_ids[] = {
/* SPT */
- { "INT3446", (kernel_ulong_t)&spt_info },
- { "INT3447", (kernel_ulong_t)&spt_info },
+ { "INT3446", (kernel_ulong_t)&spt_i2c_info },
+ { "INT3447", (kernel_ulong_t)&spt_i2c_info },
/* BXT */
{ "80860AAC", (kernel_ulong_t)&bxt_i2c_info },
{ "80860ABC", (kernel_ulong_t)&bxt_info },
diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c
index 5bfdfccbb9a1..a7136c7ae9fb 100644
--- a/drivers/mfd/intel-lpss-pci.c
+++ b/drivers/mfd/intel-lpss-pci.c
@@ -17,6 +17,7 @@
#include <linux/pci.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
+#include <linux/property.h>
#include "intel-lpss.h"
@@ -65,9 +66,35 @@ static const struct intel_lpss_platform_info spt_info = {
.clk_rate = 120000000,
};
+static struct property_entry spt_i2c_properties[] = {
+ PROPERTY_ENTRY_U32("i2c-sda-hold-time-ns", 230),
+ { },
+};
+
+static struct property_set spt_i2c_pset = {
+ .properties = spt_i2c_properties,
+};
+
+static const struct intel_lpss_platform_info spt_i2c_info = {
+ .clk_rate = 120000000,
+ .pset = &spt_i2c_pset,
+};
+
+static struct property_entry uart_properties[] = {
+ PROPERTY_ENTRY_U32("reg-io-width", 4),
+ PROPERTY_ENTRY_U32("reg-shift", 2),
+ PROPERTY_ENTRY_BOOL("snps,uart-16550-compatible"),
+ { },
+};
+
+static struct property_set uart_pset = {
+ .properties = uart_properties,
+};
+
static const struct intel_lpss_platform_info spt_uart_info = {
.clk_rate = 120000000,
.clk_con_id = "baudclk",
+ .pset = &uart_pset,
};
static const struct intel_lpss_platform_info bxt_info = {
@@ -77,6 +104,7 @@ static const struct intel_lpss_platform_info bxt_info = {
static const struct intel_lpss_platform_info bxt_uart_info = {
.clk_rate = 100000000,
.clk_con_id = "baudclk",
+ .pset = &uart_pset,
};
static const struct intel_lpss_platform_info bxt_i2c_info = {
@@ -121,20 +149,20 @@ static const struct pci_device_id intel_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x9d28), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0x9d29), (kernel_ulong_t)&spt_info },
{ PCI_VDEVICE(INTEL, 0x9d2a), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0x9d60), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0x9d61), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0x9d62), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0x9d63), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0x9d64), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0x9d65), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0x9d60), (kernel_ulong_t)&spt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x9d61), (kernel_ulong_t)&spt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x9d62), (kernel_ulong_t)&spt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x9d63), (kernel_ulong_t)&spt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x9d64), (kernel_ulong_t)&spt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0x9d65), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0x9d66), (kernel_ulong_t)&spt_uart_info },
/* SPT-H */
{ PCI_VDEVICE(INTEL, 0xa127), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa128), (kernel_ulong_t)&spt_uart_info },
{ PCI_VDEVICE(INTEL, 0xa129), (kernel_ulong_t)&spt_info },
{ PCI_VDEVICE(INTEL, 0xa12a), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0xa160), (kernel_ulong_t)&spt_info },
- { PCI_VDEVICE(INTEL, 0xa161), (kernel_ulong_t)&spt_info },
+ { PCI_VDEVICE(INTEL, 0xa160), (kernel_ulong_t)&spt_i2c_info },
+ { PCI_VDEVICE(INTEL, 0xa161), (kernel_ulong_t)&spt_i2c_info },
{ PCI_VDEVICE(INTEL, 0xa166), (kernel_ulong_t)&spt_uart_info },
{ }
};
diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c
index 6255513f54c7..1743788f1595 100644
--- a/drivers/mfd/intel-lpss.c
+++ b/drivers/mfd/intel-lpss.c
@@ -24,6 +24,7 @@
#include <linux/mfd/core.h>
#include <linux/pm_qos.h>
#include <linux/pm_runtime.h>
+#include <linux/property.h>
#include <linux/seq_file.h>
#include <linux/io-64-nonatomic-lo-hi.h>
@@ -72,7 +73,7 @@ struct intel_lpss {
enum intel_lpss_dev_type type;
struct clk *clk;
struct clk_lookup *clock;
- const struct mfd_cell *cell;
+ struct mfd_cell *cell;
struct device *dev;
void __iomem *priv;
int devid;
@@ -217,6 +218,7 @@ static void intel_lpss_ltr_hide(struct intel_lpss *lpss)
static int intel_lpss_assign_devs(struct intel_lpss *lpss)
{
+ const struct mfd_cell *cell;
unsigned int type;
type = lpss->caps & LPSS_PRIV_CAPS_TYPE_MASK;
@@ -224,18 +226,22 @@ static int intel_lpss_assign_devs(struct intel_lpss *lpss)
switch (type) {
case LPSS_DEV_I2C:
- lpss->cell = &intel_lpss_i2c_cell;
+ cell = &intel_lpss_i2c_cell;
break;
case LPSS_DEV_UART:
- lpss->cell = &intel_lpss_uart_cell;
+ cell = &intel_lpss_uart_cell;
break;
case LPSS_DEV_SPI:
- lpss->cell = &intel_lpss_spi_cell;
+ cell = &intel_lpss_spi_cell;
break;
default:
return -ENODEV;
}
+ lpss->cell = devm_kmemdup(lpss->dev, cell, sizeof(*cell), GFP_KERNEL);
+ if (!lpss->cell)
+ return -ENOMEM;
+
lpss->type = type;
return 0;
@@ -401,6 +407,8 @@ int intel_lpss_probe(struct device *dev,
if (ret)
return ret;
+ lpss->cell->pset = info->pset;
+
intel_lpss_init_dev(lpss);
lpss->devid = ida_simple_get(&intel_lpss_devid_ida, 0, 0, GFP_KERNEL);
diff --git a/drivers/mfd/intel-lpss.h b/drivers/mfd/intel-lpss.h
index 2c7f8d7c0595..0dcea9eb2d03 100644
--- a/drivers/mfd/intel-lpss.h
+++ b/drivers/mfd/intel-lpss.h
@@ -16,12 +16,14 @@
struct device;
struct resource;
+struct property_set;
struct intel_lpss_platform_info {
struct resource *mem;
int irq;
unsigned long clk_rate;
const char *clk_con_id;
+ struct property_set *pset;
};
int intel_lpss_probe(struct device *dev,
diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c
index b514f3cf140d..bd3aa4578346 100644
--- a/drivers/mfd/lpc_ich.c
+++ b/drivers/mfd/lpc_ich.c
@@ -55,6 +55,7 @@
* document number TBD : Coleto Creek
* document number TBD : Wildcat Point-LP
* document number TBD : 9 Series
+ * document number TBD : Lewisburg
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -213,6 +214,7 @@ enum lpc_chipsets {
LPC_COLETO, /* Coleto Creek */
LPC_WPT_LP, /* Wildcat Point-LP */
LPC_BRASWELL, /* Braswell SoC */
+ LPC_LEWISBURG, /* Lewisburg */
LPC_9S, /* 9 Series */
};
@@ -521,6 +523,10 @@ static struct lpc_ich_info lpc_chipset_info[] = {
.name = "Braswell SoC",
.iTCO_version = 3,
},
+ [LPC_LEWISBURG] = {
+ .name = "Lewisburg",
+ .iTCO_version = 2,
+ },
[LPC_9S] = {
.name = "9 Series",
.iTCO_version = 2,
@@ -757,6 +763,15 @@ static const struct pci_device_id lpc_ich_ids[] = {
{ PCI_VDEVICE(INTEL, 0x9cc6), LPC_WPT_LP},
{ PCI_VDEVICE(INTEL, 0x9cc7), LPC_WPT_LP},
{ PCI_VDEVICE(INTEL, 0x9cc9), LPC_WPT_LP},
+ { PCI_VDEVICE(INTEL, 0xa1c1), LPC_LEWISBURG},
+ { PCI_VDEVICE(INTEL, 0xa1c2), LPC_LEWISBURG},
+ { PCI_VDEVICE(INTEL, 0xa1c3), LPC_LEWISBURG},
+ { PCI_VDEVICE(INTEL, 0xa1c4), LPC_LEWISBURG},
+ { PCI_VDEVICE(INTEL, 0xa1c5), LPC_LEWISBURG},
+ { PCI_VDEVICE(INTEL, 0xa1c6), LPC_LEWISBURG},
+ { PCI_VDEVICE(INTEL, 0xa1c7), LPC_LEWISBURG},
+ { PCI_VDEVICE(INTEL, 0xa242), LPC_LEWISBURG},
+ { PCI_VDEVICE(INTEL, 0xa243), LPC_LEWISBURG},
{ 0, }, /* End of list */
};
MODULE_DEVICE_TABLE(pci, lpc_ich_ids);
diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c
index 56e216dedc91..2280b3fdcf68 100644
--- a/drivers/mfd/max14577.c
+++ b/drivers/mfd/max14577.c
@@ -495,7 +495,7 @@ MODULE_DEVICE_TABLE(i2c, max14577_i2c_id);
#ifdef CONFIG_PM_SLEEP
static int max14577_suspend(struct device *dev)
{
- struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *i2c = to_i2c_client(dev);
struct max14577 *max14577 = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev))
@@ -516,7 +516,7 @@ static int max14577_suspend(struct device *dev)
static int max14577_resume(struct device *dev)
{
- struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *i2c = to_i2c_client(dev);
struct max14577 *max14577 = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev))
diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c
index d19be64cd32b..d959ebbb2194 100644
--- a/drivers/mfd/max77686.c
+++ b/drivers/mfd/max77686.c
@@ -352,7 +352,7 @@ MODULE_DEVICE_TABLE(i2c, max77686_i2c_id);
#ifdef CONFIG_PM_SLEEP
static int max77686_suspend(struct device *dev)
{
- struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *i2c = to_i2c_client(dev);
struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev))
@@ -374,7 +374,7 @@ static int max77686_suspend(struct device *dev)
static int max77686_resume(struct device *dev)
{
- struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *i2c = to_i2c_client(dev);
struct max77686_dev *max77686 = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev))
diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c
index 007f729e150b..b83b7a7da1ae 100644
--- a/drivers/mfd/max77693.c
+++ b/drivers/mfd/max77693.c
@@ -334,7 +334,7 @@ MODULE_DEVICE_TABLE(i2c, max77693_i2c_id);
static int max77693_suspend(struct device *dev)
{
- struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *i2c = to_i2c_client(dev);
struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev)) {
@@ -347,7 +347,7 @@ static int max77693_suspend(struct device *dev)
static int max77693_resume(struct device *dev)
{
- struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *i2c = to_i2c_client(dev);
struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev)) {
diff --git a/drivers/mfd/max77843.c b/drivers/mfd/max77843.c
index 586098f1b233..7cfc95b49c5d 100644
--- a/drivers/mfd/max77843.c
+++ b/drivers/mfd/max77843.c
@@ -197,7 +197,7 @@ MODULE_DEVICE_TABLE(i2c, max77843_id);
static int __maybe_unused max77843_suspend(struct device *dev)
{
- struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *i2c = to_i2c_client(dev);
struct max77693_dev *max77843 = i2c_get_clientdata(i2c);
disable_irq(max77843->irq);
@@ -209,7 +209,7 @@ static int __maybe_unused max77843_suspend(struct device *dev)
static int __maybe_unused max77843_resume(struct device *dev)
{
- struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *i2c = to_i2c_client(dev);
struct max77693_dev *max77843 = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev))
diff --git a/drivers/mfd/max8925-i2c.c b/drivers/mfd/max8925-i2c.c
index b0fe8103e401..70443b161a5b 100644
--- a/drivers/mfd/max8925-i2c.c
+++ b/drivers/mfd/max8925-i2c.c
@@ -215,7 +215,7 @@ static int max8925_remove(struct i2c_client *client)
#ifdef CONFIG_PM_SLEEP
static int max8925_suspend(struct device *dev)
{
- struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *client = to_i2c_client(dev);
struct max8925_chip *chip = i2c_get_clientdata(client);
if (device_may_wakeup(dev) && chip->wakeup_flag)
@@ -225,7 +225,7 @@ static int max8925_suspend(struct device *dev)
static int max8925_resume(struct device *dev)
{
- struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *client = to_i2c_client(dev);
struct max8925_chip *chip = i2c_get_clientdata(client);
if (device_may_wakeup(dev) && chip->wakeup_flag)
diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c
index 156ed6f92aa3..f316348e3d98 100644
--- a/drivers/mfd/max8997.c
+++ b/drivers/mfd/max8997.c
@@ -437,7 +437,7 @@ static u8 max8997_dumpaddr_haptic[] = {
static int max8997_freeze(struct device *dev)
{
- struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *i2c = to_i2c_client(dev);
struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
int i;
@@ -459,7 +459,7 @@ static int max8997_freeze(struct device *dev)
static int max8997_restore(struct device *dev)
{
- struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *i2c = to_i2c_client(dev);
struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
int i;
@@ -481,7 +481,7 @@ static int max8997_restore(struct device *dev)
static int max8997_suspend(struct device *dev)
{
- struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *i2c = to_i2c_client(dev);
struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev))
@@ -491,7 +491,7 @@ static int max8997_suspend(struct device *dev)
static int max8997_resume(struct device *dev)
{
- struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *i2c = to_i2c_client(dev);
struct max8997_dev *max8997 = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev))
diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c
index a7afe3bf27fc..ab28b29400f6 100644
--- a/drivers/mfd/max8998.c
+++ b/drivers/mfd/max8998.c
@@ -274,7 +274,7 @@ MODULE_DEVICE_TABLE(i2c, max8998_i2c_id);
static int max8998_suspend(struct device *dev)
{
- struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *i2c = to_i2c_client(dev);
struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev))
@@ -284,7 +284,7 @@ static int max8998_suspend(struct device *dev)
static int max8998_resume(struct device *dev)
{
- struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *i2c = to_i2c_client(dev);
struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev))
@@ -344,7 +344,7 @@ static struct max8998_reg_dump max8998_dump[] = {
/* Save registers before hibernation */
static int max8998_freeze(struct device *dev)
{
- struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *i2c = to_i2c_client(dev);
int i;
for (i = 0; i < ARRAY_SIZE(max8998_dump); i++)
@@ -357,7 +357,7 @@ static int max8998_freeze(struct device *dev)
/* Restore registers after hibernation */
static int max8998_restore(struct device *dev)
{
- struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *i2c = to_i2c_client(dev);
int i;
for (i = 0; i < ARRAY_SIZE(max8998_dump); i++)
diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c
index 3f9f4c874d2a..d7f54e492aa6 100644
--- a/drivers/mfd/mc13xxx-core.c
+++ b/drivers/mfd/mc13xxx-core.c
@@ -383,16 +383,16 @@ static int mc13xxx_probe_flags_dt(struct mc13xxx *mc13xxx)
if (!np)
return -ENODEV;
- if (of_get_property(np, "fsl,mc13xxx-uses-adc", NULL))
+ if (of_property_read_bool(np, "fsl,mc13xxx-uses-adc"))
mc13xxx->flags |= MC13XXX_USE_ADC;
- if (of_get_property(np, "fsl,mc13xxx-uses-codec", NULL))
+ if (of_property_read_bool(np, "fsl,mc13xxx-uses-codec"))
mc13xxx->flags |= MC13XXX_USE_CODEC;
- if (of_get_property(np, "fsl,mc13xxx-uses-rtc", NULL))
+ if (of_property_read_bool(np, "fsl,mc13xxx-uses-rtc"))
mc13xxx->flags |= MC13XXX_USE_RTC;
- if (of_get_property(np, "fsl,mc13xxx-uses-touch", NULL))
+ if (of_property_read_bool(np, "fsl,mc13xxx-uses-touch"))
mc13xxx->flags |= MC13XXX_USE_TOUCHSCREEN;
return 0;
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index 60b60dc63ddd..88bd1b1e47be 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -14,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
+#include <linux/property.h>
#include <linux/mfd/core.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
@@ -192,6 +193,12 @@ static int mfd_add_device(struct device *parent, int id,
goto fail_alias;
}
+ if (cell->pset) {
+ ret = platform_device_add_properties(pdev, cell->pset);
+ if (ret)
+ goto fail_alias;
+ }
+
ret = mfd_platform_add_cell(pdev, cell, usage_count);
if (ret)
goto fail_alias;
diff --git a/drivers/mfd/qcom-spmi-pmic.c b/drivers/mfd/qcom-spmi-pmic.c
index af6ac1c4b45c..8653e8b9bb4f 100644
--- a/drivers/mfd/qcom-spmi-pmic.c
+++ b/drivers/mfd/qcom-spmi-pmic.c
@@ -127,7 +127,9 @@ static int pmic_spmi_probe(struct spmi_device *sdev)
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- pmic_spmi_show_revid(regmap, &sdev->dev);
+ /* Only the first slave id for a PMIC contains this information */
+ if (sdev->usid % 2 == 0)
+ pmic_spmi_show_revid(regmap, &sdev->dev);
return of_platform_populate(root, NULL, NULL, &sdev->dev);
}
diff --git a/drivers/mfd/qcom_rpm.c b/drivers/mfd/qcom_rpm.c
index 207a3bd68559..1be47ad6441b 100644
--- a/drivers/mfd/qcom_rpm.c
+++ b/drivers/mfd/qcom_rpm.c
@@ -495,6 +495,8 @@ static int qcom_rpm_probe(struct platform_device *pdev)
}
match = of_match_device(qcom_rpm_of_match, &pdev->dev);
+ if (!match)
+ return -ENODEV;
rpm->data = match->data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c
index 989076d6cb83..400e1d7d8d08 100644
--- a/drivers/mfd/sec-core.c
+++ b/drivers/mfd/sec-core.c
@@ -29,6 +29,7 @@
#include <linux/mfd/samsung/s2mps11.h>
#include <linux/mfd/samsung/s2mps13.h>
#include <linux/mfd/samsung/s2mps14.h>
+#include <linux/mfd/samsung/s2mps15.h>
#include <linux/mfd/samsung/s2mpu02.h>
#include <linux/mfd/samsung/s5m8763.h>
#include <linux/mfd/samsung/s5m8767.h>
@@ -67,7 +68,7 @@ static const struct mfd_cell s5m8767_devs[] = {
static const struct mfd_cell s2mps11_devs[] = {
{
- .name = "s2mps11-pmic",
+ .name = "s2mps11-regulator",
}, {
.name = "s2mps14-rtc",
}, {
@@ -77,7 +78,7 @@ static const struct mfd_cell s2mps11_devs[] = {
};
static const struct mfd_cell s2mps13_devs[] = {
- { .name = "s2mps13-pmic", },
+ { .name = "s2mps13-regulator", },
{ .name = "s2mps13-rtc", },
{
.name = "s2mps13-clk",
@@ -87,7 +88,7 @@ static const struct mfd_cell s2mps13_devs[] = {
static const struct mfd_cell s2mps14_devs[] = {
{
- .name = "s2mps14-pmic",
+ .name = "s2mps14-regulator",
}, {
.name = "s2mps14-rtc",
}, {
@@ -96,6 +97,17 @@ static const struct mfd_cell s2mps14_devs[] = {
}
};
+static const struct mfd_cell s2mps15_devs[] = {
+ {
+ .name = "s2mps15-regulator",
+ }, {
+ .name = "s2mps15-rtc",
+ }, {
+ .name = "s2mps13-clk",
+ .of_compatible = "samsung,s2mps13-clk",
+ },
+};
+
static const struct mfd_cell s2mpa01_devs[] = {
{
.name = "s2mpa01-pmic",
@@ -104,7 +116,7 @@ static const struct mfd_cell s2mpa01_devs[] = {
static const struct mfd_cell s2mpu02_devs[] = {
{
- .name = "s2mpu02-pmic",
+ .name = "s2mpu02-regulator",
},
};
@@ -122,6 +134,9 @@ static const struct of_device_id sec_dt_match[] = {
.compatible = "samsung,s2mps14-pmic",
.data = (void *)S2MPS14X,
}, {
+ .compatible = "samsung,s2mps15-pmic",
+ .data = (void *)S2MPS15X,
+ }, {
.compatible = "samsung,s2mpa01-pmic",
.data = (void *)S2MPA01,
}, {
@@ -223,6 +238,15 @@ static const struct regmap_config s2mps14_regmap_config = {
.cache_type = REGCACHE_FLAT,
};
+static const struct regmap_config s2mps15_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = S2MPS15_REG_LDODSCH4,
+ .volatile_reg = s2mps11_volatile,
+ .cache_type = REGCACHE_FLAT,
+};
+
static const struct regmap_config s2mpu02_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
@@ -384,6 +408,9 @@ static int sec_pmic_probe(struct i2c_client *i2c,
case S2MPS14X:
regmap = &s2mps14_regmap_config;
break;
+ case S2MPS15X:
+ regmap = &s2mps15_regmap_config;
+ break;
case S5M8763X:
regmap = &s5m8763_regmap_config;
break;
@@ -442,6 +469,10 @@ static int sec_pmic_probe(struct i2c_client *i2c,
sec_devs = s2mps14_devs;
num_sec_devs = ARRAY_SIZE(s2mps14_devs);
break;
+ case S2MPS15X:
+ sec_devs = s2mps15_devs;
+ num_sec_devs = ARRAY_SIZE(s2mps15_devs);
+ break;
case S2MPU02:
sec_devs = s2mpu02_devs;
num_sec_devs = ARRAY_SIZE(s2mpu02_devs);
@@ -505,7 +536,7 @@ static void sec_pmic_shutdown(struct i2c_client *i2c)
#ifdef CONFIG_PM_SLEEP
static int sec_pmic_suspend(struct device *dev)
{
- struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *i2c = to_i2c_client(dev);
struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev))
@@ -526,7 +557,7 @@ static int sec_pmic_suspend(struct device *dev)
static int sec_pmic_resume(struct device *dev)
{
- struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
+ struct i2c_client *i2c = to_i2c_client(dev);
struct sec_pmic_dev *sec_pmic = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev))
diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c
index 806fa8dbb22d..d77de431cc50 100644
--- a/drivers/mfd/sec-irq.c
+++ b/drivers/mfd/sec-irq.c
@@ -407,6 +407,11 @@ static const struct regmap_irq_chip s2mps14_irq_chip = {
S2MPS1X_IRQ_CHIP_COMMON_DATA,
};
+static const struct regmap_irq_chip s2mps15_irq_chip = {
+ .name = "s2mps15",
+ S2MPS1X_IRQ_CHIP_COMMON_DATA,
+};
+
static const struct regmap_irq_chip s2mpu02_irq_chip = {
.name = "s2mpu02",
.irqs = s2mpu02_irqs,
@@ -466,6 +471,9 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic)
case S2MPS14X:
sec_irq_chip = &s2mps14_irq_chip;
break;
+ case S2MPS15X:
+ sec_irq_chip = &s2mps15_irq_chip;
+ break;
case S2MPU02:
sec_irq_chip = &s2mpu02_irq_chip;
break;
diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c
index b3e5c6f45105..9292202039ee 100644
--- a/drivers/mfd/sta2x11-mfd.c
+++ b/drivers/mfd/sta2x11-mfd.c
@@ -372,12 +372,6 @@ static struct platform_driver sta2x11_sctl_platform_driver = {
.probe = sta2x11_sctl_probe,
};
-static int __init sta2x11_sctl_init(void)
-{
- pr_info("%s\n", __func__);
- return platform_driver_register(&sta2x11_sctl_platform_driver);
-}
-
static struct platform_driver sta2x11_platform_driver = {
.driver = {
.name = STA2X11_MFD_APBREG_NAME,
@@ -385,12 +379,6 @@ static struct platform_driver sta2x11_platform_driver = {
.probe = sta2x11_apbreg_probe,
};
-static int __init sta2x11_apbreg_init(void)
-{
- pr_info("%s\n", __func__);
- return platform_driver_register(&sta2x11_platform_driver);
-}
-
static struct platform_driver sta2x11_apb_soc_regs_platform_driver = {
.driver = {
.name = STA2X11_MFD_APB_SOC_REGS_NAME,
@@ -398,12 +386,6 @@ static struct platform_driver sta2x11_apb_soc_regs_platform_driver = {
.probe = sta2x11_apb_soc_regs_probe,
};
-static int __init sta2x11_apb_soc_regs_init(void)
-{
- pr_info("%s\n", __func__);
- return platform_driver_register(&sta2x11_apb_soc_regs_platform_driver);
-}
-
static struct platform_driver sta2x11_scr_platform_driver = {
.driver = {
.name = STA2X11_MFD_SCR_NAME,
@@ -411,13 +393,18 @@ static struct platform_driver sta2x11_scr_platform_driver = {
.probe = sta2x11_scr_probe,
};
-static int __init sta2x11_scr_init(void)
+static struct platform_driver * const drivers[] = {
+ &sta2x11_platform_driver,
+ &sta2x11_sctl_platform_driver,
+ &sta2x11_apb_soc_regs_platform_driver,
+ &sta2x11_scr_platform_driver,
+};
+
+static int __init sta2x11_drivers_init(void)
{
- pr_info("%s\n", __func__);
- return platform_driver_register(&sta2x11_scr_platform_driver);
+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
}
-
/*
* What follows are the PCI devices that host the above pdevs.
* Each logic block is 4kB and they are all consecutive: we use this info.
@@ -664,10 +651,7 @@ static int __init sta2x11_mfd_init(void)
* prepares platform drivers very early and probe the PCI device later,
* but before other PCI devices.
*/
-subsys_initcall(sta2x11_apbreg_init);
-subsys_initcall(sta2x11_sctl_init);
-subsys_initcall(sta2x11_apb_soc_regs_init);
-subsys_initcall(sta2x11_scr_init);
+subsys_initcall(sta2x11_drivers_init);
rootfs_initcall(sta2x11_mfd_init);
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
index 176bf0fa2685..b7aabeefab07 100644
--- a/drivers/mfd/syscon.c
+++ b/drivers/mfd/syscon.c
@@ -47,6 +47,7 @@ static struct syscon *of_syscon_register(struct device_node *np)
struct syscon *syscon;
struct regmap *regmap;
void __iomem *base;
+ u32 reg_io_width;
int ret;
struct regmap_config syscon_config = syscon_regmap_config;
@@ -69,6 +70,18 @@ static struct syscon *of_syscon_register(struct device_node *np)
else if (of_property_read_bool(np, "little-endian"))
syscon_config.val_format_endian = REGMAP_ENDIAN_LITTLE;
+ /*
+ * search for reg-io-width property in DT. If it is not provided,
+ * default to 4 bytes. regmap_init_mmio will return an error if values
+ * are invalid so there is no need to check them here.
+ */
+ ret = of_property_read_u32(np, "reg-io-width", &reg_io_width);
+ if (ret)
+ reg_io_width = 4;
+
+ syscon_config.reg_stride = reg_io_width;
+ syscon_config.val_bits = reg_io_width * 8;
+
regmap = regmap_init_mmio(NULL, base, &syscon_config);
if (IS_ERR(regmap)) {
pr_err("regmap init failed\n");
diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c
index 8c84a513016b..1ecbfa40d1b3 100644
--- a/drivers/mfd/tc6393xb.c
+++ b/drivers/mfd/tc6393xb.c
@@ -437,8 +437,8 @@ static int tc6393xb_gpio_get(struct gpio_chip *chip,
struct tc6393xb *tc6393xb = container_of(chip, struct tc6393xb, gpio);
/* XXX: does dsr also represent inputs? */
- return tmio_ioread8(tc6393xb->scr + SCR_GPO_DSR(offset / 8))
- & TC_GPIO_BIT(offset);
+ return !!(tmio_ioread8(tc6393xb->scr + SCR_GPO_DSR(offset / 8))
+ & TC_GPIO_BIT(offset));
}
static void __tc6393xb_gpio_set(struct gpio_chip *chip,
diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c
index 6ce36d6970a4..c9339f85359b 100644
--- a/drivers/mfd/timberdale.c
+++ b/drivers/mfd/timberdale.c
@@ -39,8 +39,8 @@
#include <linux/spi/max7301.h>
#include <linux/spi/mc33880.h>
-#include <media/timb_radio.h>
-#include <media/timb_video.h>
+#include <linux/platform_data/media/timb_radio.h>
+#include <linux/platform_data/media/timb_video.h>
#include <linux/timb_dma.h>
diff --git a/drivers/mfd/tps65010.c b/drivers/mfd/tps65010.c
index 448f0a182dc4..677a127619d4 100644
--- a/drivers/mfd/tps65010.c
+++ b/drivers/mfd/tps65010.c
@@ -499,11 +499,11 @@ static int tps65010_gpio_get(struct gpio_chip *chip, unsigned offset)
if (offset < 4) {
value = i2c_smbus_read_byte_data(tps->client, TPS_DEFGPIO);
if (value < 0)
- return 0;
+ return value;
if (value & (1 << (offset + 4))) /* output */
return !(value & (1 << offset));
else /* input */
- return (value & (1 << offset));
+ return !!(value & (1 << offset));
}
/* REVISIT we *could* report LED1/nPG and LED2 state ... */
diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c
index f691d7ecad52..e0dd83fb95d3 100644
--- a/drivers/mfd/ucb1x00-core.c
+++ b/drivers/mfd/ucb1x00-core.c
@@ -133,7 +133,7 @@ static int ucb1x00_gpio_get(struct gpio_chip *chip, unsigned offset)
val = ucb1x00_reg_read(ucb, UCB_IO_DATA);
ucb1x00_disable(ucb);
- return val & (1 << offset);
+ return !!(val & (1 << offset));
}
static int ucb1x00_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c
index 2bb2d0467a92..c18e11f42b3f 100644
--- a/drivers/mfd/wm5110-tables.c
+++ b/drivers/mfd/wm5110-tables.c
@@ -762,9 +762,9 @@ static const struct reg_default wm5110_reg_default[] = {
{ 0x00000200, 0x0006 }, /* R512 - Mic Charge Pump 1 */
{ 0x00000210, 0x0184 }, /* R528 - LDO1 Control 1 */
{ 0x00000213, 0x03E4 }, /* R531 - LDO2 Control 1 */
- { 0x00000218, 0x01A6 }, /* R536 - Mic Bias Ctrl 1 */
- { 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */
- { 0x0000021A, 0x01A6 }, /* R538 - Mic Bias Ctrl 3 */
+ { 0x00000218, 0x00E6 }, /* R536 - Mic Bias Ctrl 1 */
+ { 0x00000219, 0x00E6 }, /* R537 - Mic Bias Ctrl 2 */
+ { 0x0000021A, 0x00E6 }, /* R538 - Mic Bias Ctrl 3 */
{ 0x00000293, 0x0000 }, /* R659 - Accessory Detect Mode 1 */
{ 0x0000029B, 0x0028 }, /* R667 - Headphone Detect 1 */
{ 0x000002A2, 0x0000 }, /* R674 - Micd clamp control */
diff --git a/drivers/mfd/wm831x-otp.c b/drivers/mfd/wm831x-otp.c
index b90f3e06b6c9..ebac0027f8e0 100644
--- a/drivers/mfd/wm831x-otp.c
+++ b/drivers/mfd/wm831x-otp.c
@@ -47,20 +47,14 @@ static ssize_t wm831x_unique_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct wm831x *wm831x = dev_get_drvdata(dev);
- int i, rval;
+ int rval;
char id[WM831X_UNIQUE_ID_LEN];
- ssize_t ret = 0;
rval = wm831x_unique_id_read(wm831x, id);
if (rval < 0)
return 0;
- for (i = 0; i < WM831X_UNIQUE_ID_LEN; i++)
- ret += sprintf(&buf[ret], "%02x", buf[i]);
-
- ret += sprintf(&buf[ret], "\n");
-
- return ret;
+ return sprintf(buf, "%*phN\n", WM831X_UNIQUE_ID_LEN, id);
}
static DEVICE_ATTR(unique_id, 0444, wm831x_unique_id_show, NULL);
diff --git a/drivers/misc/cxl/native.c b/drivers/misc/cxl/native.c
index d2e75c88f4d2..f40909793490 100644
--- a/drivers/misc/cxl/native.c
+++ b/drivers/misc/cxl/native.c
@@ -497,6 +497,7 @@ static u64 calculate_sr(struct cxl_context *ctx)
{
u64 sr = 0;
+ set_endian(sr);
if (ctx->master)
sr |= CXL_PSL_SR_An_MP;
if (mfspr(SPRN_LPCR) & LPCR_TC)
@@ -506,7 +507,6 @@ static u64 calculate_sr(struct cxl_context *ctx)
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;
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index b2f2486b3d75..677d0362f334 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -657,7 +657,9 @@ out:
* @file: pointer to file structure
* @band: band bitmap
*
- * Return: poll mask
+ * Return: negative on error,
+ * 0 if it did no changes,
+ * and positive a process was added or deleted
*/
static int mei_fasync(int fd, struct file *file, int band)
{
@@ -665,7 +667,7 @@ static int mei_fasync(int fd, struct file *file, int band)
struct mei_cl *cl = file->private_data;
if (!mei_cl_is_connected(cl))
- return POLLERR;
+ return -ENODEV;
return fasync_helper(fd, file, band, &cl->ev_async);
}
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index d8486168415a..5914263090fc 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -171,11 +171,7 @@ static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk)
static inline int mmc_get_devidx(struct gendisk *disk)
{
- int devmaj = MAJOR(disk_devt(disk));
- int devidx = MINOR(disk_devt(disk)) / perdev_minors;
-
- if (!devmaj)
- devidx = disk->first_minor / perdev_minors;
+ int devidx = disk->first_minor / perdev_minors;
return devidx;
}
@@ -344,7 +340,7 @@ static struct mmc_blk_ioc_data *mmc_blk_ioctl_copy_from_user(
struct mmc_blk_ioc_data *idata;
int err;
- idata = kzalloc(sizeof(*idata), GFP_KERNEL);
+ idata = kmalloc(sizeof(*idata), GFP_KERNEL);
if (!idata) {
err = -ENOMEM;
goto out;
@@ -364,7 +360,7 @@ static struct mmc_blk_ioc_data *mmc_blk_ioctl_copy_from_user(
if (!idata->buf_bytes)
return idata;
- idata->buf = kzalloc(idata->buf_bytes, GFP_KERNEL);
+ idata->buf = kmalloc(idata->buf_bytes, GFP_KERNEL);
if (!idata->buf) {
err = -ENOMEM;
goto idata_err;
@@ -2244,6 +2240,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
md->disk->queue = md->queue.queue;
md->disk->driverfs_dev = parent;
set_disk_ro(md->disk, md->read_only || default_ro);
+ md->disk->flags = GENHD_FL_EXT_DEVT;
if (area_type & (MMC_BLK_DATA_AREA_RPMB | MMC_BLK_DATA_AREA_BOOT))
md->disk->flags |= GENHD_FL_NO_PART_SCAN;
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 972ff844cf5a..4bc48f10452f 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -349,6 +349,8 @@ int mmc_add_card(struct mmc_card *card)
card->dev.of_node = mmc_of_find_child_device(card->host, 0);
+ device_enable_async_suspend(&card->dev);
+
ret = device_add(&card->dev);
if (ret)
return ret;
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 5ae89e48fd85..f95d41ffc766 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -55,7 +55,6 @@
*/
#define MMC_BKOPS_MAX_TIMEOUT (4 * 60 * 1000) /* max time to wait in ms */
-static struct workqueue_struct *workqueue;
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
/*
@@ -66,21 +65,16 @@ static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
bool use_spi_crc = 1;
module_param(use_spi_crc, bool, 0);
-/*
- * Internal function. Schedule delayed work in the MMC work queue.
- */
static int mmc_schedule_delayed_work(struct delayed_work *work,
unsigned long delay)
{
- return queue_delayed_work(workqueue, work, delay);
-}
-
-/*
- * Internal function. Flush all scheduled work from the MMC work queue.
- */
-static void mmc_flush_scheduled_work(void)
-{
- flush_workqueue(workqueue);
+ /*
+ * We use the system_freezable_wq, because of two reasons.
+ * First, it allows several works (not the same work item) to be
+ * executed simultaneously. Second, the queue becomes frozen when
+ * userspace becomes frozen during system PM.
+ */
+ return queue_delayed_work(system_freezable_wq, work, delay);
}
#ifdef CONFIG_FAIL_MMC_REQUEST
@@ -1485,7 +1479,7 @@ int mmc_regulator_get_supply(struct mmc_host *mmc)
if (IS_ERR(mmc->supply.vmmc)) {
if (PTR_ERR(mmc->supply.vmmc) == -EPROBE_DEFER)
return -EPROBE_DEFER;
- dev_info(dev, "No vmmc regulator found\n");
+ dev_dbg(dev, "No vmmc regulator found\n");
} else {
ret = mmc_regulator_get_ocrmask(mmc->supply.vmmc);
if (ret > 0)
@@ -1497,7 +1491,7 @@ int mmc_regulator_get_supply(struct mmc_host *mmc)
if (IS_ERR(mmc->supply.vqmmc)) {
if (PTR_ERR(mmc->supply.vqmmc) == -EPROBE_DEFER)
return -EPROBE_DEFER;
- dev_info(dev, "No vqmmc regulator found\n");
+ dev_dbg(dev, "No vqmmc regulator found\n");
}
return 0;
@@ -2476,15 +2470,20 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
* sdio_reset sends CMD52 to reset card. Since we do not know
* if the card is being re-initialized, just send it. CMD52
* should be ignored by SD/eMMC cards.
+ * Skip it if we already know that we do not support SDIO commands
*/
- sdio_reset(host);
+ if (!(host->caps2 & MMC_CAP2_NO_SDIO))
+ sdio_reset(host);
+
mmc_go_idle(host);
mmc_send_if_cond(host, host->ocr_avail);
/* Order's important: probe SDIO, then SD, then MMC */
- if (!mmc_attach_sdio(host))
- return 0;
+ if (!(host->caps2 & MMC_CAP2_NO_SDIO))
+ if (!mmc_attach_sdio(host))
+ return 0;
+
if (!mmc_attach_sd(host))
return 0;
if (!mmc_attach_mmc(host))
@@ -2498,9 +2497,6 @@ int _mmc_detect_card_removed(struct mmc_host *host)
{
int ret;
- if (host->caps & MMC_CAP_NONREMOVABLE)
- return 0;
-
if (!host->card || mmc_card_removed(host->card))
return 1;
@@ -2536,6 +2532,9 @@ int mmc_detect_card_removed(struct mmc_host *host)
if (!card)
return 1;
+ if (host->caps & MMC_CAP_NONREMOVABLE)
+ return 0;
+
ret = mmc_card_removed(card);
/*
* The card will be considered unchanged unless we have been asked to
@@ -2567,11 +2566,6 @@ void mmc_rescan(struct work_struct *work)
container_of(work, struct mmc_host, detect.work);
int i;
- if (host->trigger_card_event && host->ops->card_event) {
- host->ops->card_event(host);
- host->trigger_card_event = false;
- }
-
if (host->rescan_disable)
return;
@@ -2580,6 +2574,13 @@ void mmc_rescan(struct work_struct *work)
return;
host->rescan_entered = 1;
+ if (host->trigger_card_event && host->ops->card_event) {
+ mmc_claim_host(host);
+ host->ops->card_event(host);
+ mmc_release_host(host);
+ host->trigger_card_event = false;
+ }
+
mmc_bus_get(host);
/*
@@ -2611,15 +2612,14 @@ void mmc_rescan(struct work_struct *work)
*/
mmc_bus_put(host);
+ mmc_claim_host(host);
if (!(host->caps & MMC_CAP_NONREMOVABLE) && host->ops->get_cd &&
host->ops->get_cd(host) == 0) {
- mmc_claim_host(host);
mmc_power_off(host);
mmc_release_host(host);
goto out;
}
- mmc_claim_host(host);
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
break;
@@ -2663,7 +2663,6 @@ void mmc_stop_host(struct mmc_host *host)
host->rescan_disable = 1;
cancel_delayed_work_sync(&host->detect);
- mmc_flush_scheduled_work();
/* clear pm flags now and let card drivers set them as needed */
host->pm_flags = 0;
@@ -2759,14 +2758,13 @@ int mmc_flush_cache(struct mmc_card *card)
}
EXPORT_SYMBOL(mmc_flush_cache);
-#ifdef CONFIG_PM
-
+#ifdef CONFIG_PM_SLEEP
/* Do the card removal on suspend if card is assumed removeable
* Do that in pm notifier while userspace isn't yet frozen, so we will be able
to sync the card.
*/
-int mmc_pm_notify(struct notifier_block *notify_block,
- unsigned long mode, void *unused)
+static int mmc_pm_notify(struct notifier_block *notify_block,
+ unsigned long mode, void *unused)
{
struct mmc_host *host = container_of(
notify_block, struct mmc_host, pm_notify);
@@ -2813,6 +2811,17 @@ int mmc_pm_notify(struct notifier_block *notify_block,
return 0;
}
+
+void mmc_register_pm_notifier(struct mmc_host *host)
+{
+ host->pm_notify.notifier_call = mmc_pm_notify;
+ register_pm_notifier(&host->pm_notify);
+}
+
+void mmc_unregister_pm_notifier(struct mmc_host *host)
+{
+ unregister_pm_notifier(&host->pm_notify);
+}
#endif
/**
@@ -2836,13 +2845,9 @@ static int __init mmc_init(void)
{
int ret;
- workqueue = alloc_ordered_workqueue("kmmcd", 0);
- if (!workqueue)
- return -ENOMEM;
-
ret = mmc_register_bus();
if (ret)
- goto destroy_workqueue;
+ return ret;
ret = mmc_register_host_class();
if (ret)
@@ -2858,9 +2863,6 @@ unregister_host_class:
mmc_unregister_host_class();
unregister_bus:
mmc_unregister_bus();
-destroy_workqueue:
- destroy_workqueue(workqueue);
-
return ret;
}
@@ -2869,7 +2871,6 @@ static void __exit mmc_exit(void)
sdio_unregister_bus();
mmc_unregister_host_class();
mmc_unregister_bus();
- destroy_workqueue(workqueue);
}
subsys_initcall(mmc_init);
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 09241e56d628..0fa86a2afc26 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -90,5 +90,13 @@ 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);
+#ifdef CONFIG_PM_SLEEP
+void mmc_register_pm_notifier(struct mmc_host *host);
+void mmc_unregister_pm_notifier(struct mmc_host *host);
+#else
+static inline void mmc_register_pm_notifier(struct mmc_host *host) { }
+static inline void mmc_unregister_pm_notifier(struct mmc_host *host) { }
+#endif
+
#endif
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index da950c44204d..0aecd5c00b86 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -21,7 +21,6 @@
#include <linux/export.h>
#include <linux/leds.h>
#include <linux/slab.h>
-#include <linux/suspend.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
@@ -275,7 +274,8 @@ int mmc_of_parse(struct mmc_host *host)
host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE;
if (of_property_read_bool(np, "keep-power-in-suspend"))
host->pm_caps |= MMC_PM_KEEP_POWER;
- if (of_property_read_bool(np, "enable-sdio-wakeup"))
+ if (of_property_read_bool(np, "wakeup-source") ||
+ of_property_read_bool(np, "enable-sdio-wakeup")) /* legacy */
host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
if (of_property_read_bool(np, "mmc-ddr-1_8v"))
host->caps |= MMC_CAP_1_8V_DDR;
@@ -348,9 +348,6 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
-#ifdef CONFIG_PM
- host->pm_notify.notifier_call = mmc_pm_notify;
-#endif
setup_timer(&host->retune_timer, mmc_retune_timer, (unsigned long)host);
/*
@@ -395,7 +392,7 @@ int mmc_add_host(struct mmc_host *host)
#endif
mmc_start_host(host);
- register_pm_notifier(&host->pm_notify);
+ mmc_register_pm_notifier(host);
return 0;
}
@@ -412,7 +409,7 @@ EXPORT_SYMBOL(mmc_add_host);
*/
void mmc_remove_host(struct mmc_host *host)
{
- unregister_pm_notifier(&host->pm_notify);
+ mmc_unregister_pm_notifier(host);
mmc_stop_host(host);
#ifdef CONFIG_DEBUG_FS
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 3a9a79ec4343..bf49e44571f2 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1076,8 +1076,7 @@ static int mmc_select_hs400(struct mmc_card *card)
mmc_set_clock(host, max_dtr);
/* Switch card to HS mode */
- val = EXT_CSD_TIMING_HS |
- card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
+ val = EXT_CSD_TIMING_HS;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, val,
card->ext_csd.generic_cmd6_time,
@@ -1160,8 +1159,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
mmc_set_clock(host, max_dtr);
/* Switch HS400 to HS DDR */
- val = EXT_CSD_TIMING_HS |
- card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
+ val = EXT_CSD_TIMING_HS;
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
val, card->ext_csd.generic_cmd6_time,
true, send_status, true);
@@ -1907,16 +1905,8 @@ static int mmc_shutdown(struct mmc_host *host)
*/
static int mmc_resume(struct mmc_host *host)
{
- int err = 0;
-
- if (!(host->caps & MMC_CAP_RUNTIME_RESUME)) {
- err = _mmc_resume(host);
- pm_runtime_set_active(&host->card->dev);
- pm_runtime_mark_last_busy(&host->card->dev);
- }
pm_runtime_enable(&host->card->dev);
-
- return err;
+ return 0;
}
/*
@@ -1944,12 +1934,9 @@ static int mmc_runtime_resume(struct mmc_host *host)
{
int err;
- if (!(host->caps & (MMC_CAP_AGGRESSIVE_PM | MMC_CAP_RUNTIME_RESUME)))
- return 0;
-
err = _mmc_resume(host);
- if (err)
- pr_err("%s: error %d doing aggressive resume\n",
+ if (err && err != -ENOMEDIUM)
+ pr_err("%s: error %d doing runtime resume\n",
mmc_hostname(host), err);
return 0;
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 1f444269ebbe..2c90635c89af 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -489,6 +489,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned long timeout;
u32 status = 0;
bool use_r1b_resp = use_busy_signal;
+ bool expired = false;
mmc_retune_hold(host);
@@ -545,6 +546,12 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
timeout = jiffies + msecs_to_jiffies(timeout_ms);
do {
if (send_status) {
+ /*
+ * Due to the possibility of being preempted after
+ * sending the status command, check the expiration
+ * time first.
+ */
+ expired = time_after(jiffies, timeout);
err = __mmc_send_status(card, &status, ignore_crc);
if (err)
goto out;
@@ -565,7 +572,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
}
/* Timeout if the device never leaves the program state. */
- if (time_after(jiffies, timeout)) {
+ if (expired && R1_CURRENT_STATE(status) == R1_STATE_PRG) {
pr_err("%s: Card stuck in programming state! %s\n",
mmc_hostname(host), __func__);
err = -ETIMEDOUT;
diff --git a/drivers/mmc/core/pwrseq.h b/drivers/mmc/core/pwrseq.h
index 096da48c6a7e..133de0426687 100644
--- a/drivers/mmc/core/pwrseq.h
+++ b/drivers/mmc/core/pwrseq.h
@@ -16,7 +16,7 @@ struct mmc_pwrseq_ops {
};
struct mmc_pwrseq {
- struct mmc_pwrseq_ops *ops;
+ const struct mmc_pwrseq_ops *ops;
};
#ifdef CONFIG_OF
diff --git a/drivers/mmc/core/pwrseq_emmc.c b/drivers/mmc/core/pwrseq_emmc.c
index ad4f94ec7e8d..4a82bc77fe49 100644
--- a/drivers/mmc/core/pwrseq_emmc.c
+++ b/drivers/mmc/core/pwrseq_emmc.c
@@ -51,7 +51,7 @@ static void mmc_pwrseq_emmc_free(struct mmc_host *host)
kfree(pwrseq);
}
-static struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = {
+static const struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = {
.post_power_on = mmc_pwrseq_emmc_reset,
.free = mmc_pwrseq_emmc_free,
};
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index d10538bb5e07..2b16263458af 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -87,7 +87,7 @@ static void mmc_pwrseq_simple_free(struct mmc_host *host)
kfree(pwrseq);
}
-static struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = {
+static const struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = {
.pre_power_on = mmc_pwrseq_simple_pre_power_on,
.post_power_on = mmc_pwrseq_simple_post_power_on,
.power_off = mmc_pwrseq_simple_power_off,
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 141eaa923e18..f2b164b214ae 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1128,16 +1128,8 @@ out:
*/
static int mmc_sd_resume(struct mmc_host *host)
{
- int err = 0;
-
- if (!(host->caps & MMC_CAP_RUNTIME_RESUME)) {
- err = _mmc_sd_resume(host);
- pm_runtime_set_active(&host->card->dev);
- pm_runtime_mark_last_busy(&host->card->dev);
- }
pm_runtime_enable(&host->card->dev);
-
- return err;
+ return 0;
}
/*
@@ -1165,12 +1157,9 @@ static int mmc_sd_runtime_resume(struct mmc_host *host)
{
int err;
- if (!(host->caps & (MMC_CAP_AGGRESSIVE_PM | MMC_CAP_RUNTIME_RESUME)))
- return 0;
-
err = _mmc_sd_resume(host);
- if (err)
- pr_err("%s: error %d doing aggressive resume\n",
+ if (err && err != -ENOMEDIUM)
+ pr_err("%s: error %d doing runtime resume\n",
mmc_hostname(host), err);
return 0;
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 16d838e6d623..d61ba1a0495e 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -630,7 +630,7 @@ try_again:
*/
if (!powered_resume && (rocr & ocr & R4_18V_PRESENT)) {
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180,
- ocr);
+ ocr_card);
if (err == -EAGAIN) {
sdio_reset(host);
mmc_go_idle(host);
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index 7e327a6dd53d..86f5b3223aae 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -322,6 +322,7 @@ int sdio_add_func(struct sdio_func *func)
sdio_set_of_node(func);
sdio_acpi_set_handle(func);
+ device_enable_async_suspend(&func->dev);
ret = device_add(&func->dev);
if (ret == 0)
sdio_func_set_present(func);
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 1dee533634c9..1526b8a10b09 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -455,6 +455,7 @@ config MMC_TIFM_SD
config MMC_MVSDIO
tristate "Marvell MMC/SD/SDIO host driver"
depends on PLAT_ORION
+ depends on OF
---help---
This selects the Marvell SDIO host driver.
SDIO may currently be found on the Kirkwood 88F6281 and 88F6192
diff --git a/drivers/mmc/host/atmel-mci-regs.h b/drivers/mmc/host/atmel-mci-regs.h
deleted file mode 100644
index 0aa44e679df4..000000000000
--- a/drivers/mmc/host/atmel-mci-regs.h
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Atmel MultiMedia Card Interface driver
- *
- * Copyright (C) 2004-2006 Atmel 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.
- */
-
-/*
- * Superset of MCI IP registers integrated in Atmel AVR32 and AT91 Processors
- * Registers and bitfields marked with [2] are only available in MCI2
- */
-
-#ifndef __DRIVERS_MMC_ATMEL_MCI_H__
-#define __DRIVERS_MMC_ATMEL_MCI_H__
-
-/* MCI Register Definitions */
-#define ATMCI_CR 0x0000 /* Control */
-# define ATMCI_CR_MCIEN ( 1 << 0) /* MCI Enable */
-# define ATMCI_CR_MCIDIS ( 1 << 1) /* MCI Disable */
-# define ATMCI_CR_PWSEN ( 1 << 2) /* Power Save Enable */
-# define ATMCI_CR_PWSDIS ( 1 << 3) /* Power Save Disable */
-# define ATMCI_CR_SWRST ( 1 << 7) /* Software Reset */
-#define ATMCI_MR 0x0004 /* Mode */
-# define ATMCI_MR_CLKDIV(x) ((x) << 0) /* Clock Divider */
-# define ATMCI_MR_PWSDIV(x) ((x) << 8) /* Power Saving Divider */
-# define ATMCI_MR_RDPROOF ( 1 << 11) /* Read Proof */
-# define ATMCI_MR_WRPROOF ( 1 << 12) /* Write Proof */
-# define ATMCI_MR_PDCFBYTE ( 1 << 13) /* Force Byte Transfer */
-# define ATMCI_MR_PDCPADV ( 1 << 14) /* Padding Value */
-# define ATMCI_MR_PDCMODE ( 1 << 15) /* PDC-oriented Mode */
-# define ATMCI_MR_CLKODD(x) ((x) << 16) /* LSB of Clock Divider */
-#define ATMCI_DTOR 0x0008 /* Data Timeout */
-# define ATMCI_DTOCYC(x) ((x) << 0) /* Data Timeout Cycles */
-# define ATMCI_DTOMUL(x) ((x) << 4) /* Data Timeout Multiplier */
-#define ATMCI_SDCR 0x000c /* SD Card / SDIO */
-# define ATMCI_SDCSEL_SLOT_A ( 0 << 0) /* Select SD slot A */
-# define ATMCI_SDCSEL_SLOT_B ( 1 << 0) /* Select SD slot A */
-# define ATMCI_SDCSEL_MASK ( 3 << 0)
-# define ATMCI_SDCBUS_1BIT ( 0 << 6) /* 1-bit data bus */
-# define ATMCI_SDCBUS_4BIT ( 2 << 6) /* 4-bit data bus */
-# define ATMCI_SDCBUS_8BIT ( 3 << 6) /* 8-bit data bus[2] */
-# define ATMCI_SDCBUS_MASK ( 3 << 6)
-#define ATMCI_ARGR 0x0010 /* Command Argument */
-#define ATMCI_CMDR 0x0014 /* Command */
-# define ATMCI_CMDR_CMDNB(x) ((x) << 0) /* Command Opcode */
-# define ATMCI_CMDR_RSPTYP_NONE ( 0 << 6) /* No response */
-# define ATMCI_CMDR_RSPTYP_48BIT ( 1 << 6) /* 48-bit response */
-# define ATMCI_CMDR_RSPTYP_136BIT ( 2 << 6) /* 136-bit response */
-# define ATMCI_CMDR_SPCMD_INIT ( 1 << 8) /* Initialization command */
-# define ATMCI_CMDR_SPCMD_SYNC ( 2 << 8) /* Synchronized command */
-# define ATMCI_CMDR_SPCMD_INT ( 4 << 8) /* Interrupt command */
-# define ATMCI_CMDR_SPCMD_INTRESP ( 5 << 8) /* Interrupt response */
-# define ATMCI_CMDR_OPDCMD ( 1 << 11) /* Open Drain */
-# define ATMCI_CMDR_MAXLAT_5CYC ( 0 << 12) /* Max latency 5 cycles */
-# define ATMCI_CMDR_MAXLAT_64CYC ( 1 << 12) /* Max latency 64 cycles */
-# define ATMCI_CMDR_START_XFER ( 1 << 16) /* Start data transfer */
-# define ATMCI_CMDR_STOP_XFER ( 2 << 16) /* Stop data transfer */
-# define ATMCI_CMDR_TRDIR_WRITE ( 0 << 18) /* Write data */
-# define ATMCI_CMDR_TRDIR_READ ( 1 << 18) /* Read data */
-# define ATMCI_CMDR_BLOCK ( 0 << 19) /* Single-block transfer */
-# define ATMCI_CMDR_MULTI_BLOCK ( 1 << 19) /* Multi-block transfer */
-# define ATMCI_CMDR_STREAM ( 2 << 19) /* MMC Stream transfer */
-# define ATMCI_CMDR_SDIO_BYTE ( 4 << 19) /* SDIO Byte transfer */
-# define ATMCI_CMDR_SDIO_BLOCK ( 5 << 19) /* SDIO Block transfer */
-# define ATMCI_CMDR_SDIO_SUSPEND ( 1 << 24) /* SDIO Suspend Command */
-# define ATMCI_CMDR_SDIO_RESUME ( 2 << 24) /* SDIO Resume Command */
-#define ATMCI_BLKR 0x0018 /* Block */
-# define ATMCI_BCNT(x) ((x) << 0) /* Data Block Count */
-# define ATMCI_BLKLEN(x) ((x) << 16) /* Data Block Length */
-#define ATMCI_CSTOR 0x001c /* Completion Signal Timeout[2] */
-# define ATMCI_CSTOCYC(x) ((x) << 0) /* CST cycles */
-# define ATMCI_CSTOMUL(x) ((x) << 4) /* CST multiplier */
-#define ATMCI_RSPR 0x0020 /* Response 0 */
-#define ATMCI_RSPR1 0x0024 /* Response 1 */
-#define ATMCI_RSPR2 0x0028 /* Response 2 */
-#define ATMCI_RSPR3 0x002c /* Response 3 */
-#define ATMCI_RDR 0x0030 /* Receive Data */
-#define ATMCI_TDR 0x0034 /* Transmit Data */
-#define ATMCI_SR 0x0040 /* Status */
-#define ATMCI_IER 0x0044 /* Interrupt Enable */
-#define ATMCI_IDR 0x0048 /* Interrupt Disable */
-#define ATMCI_IMR 0x004c /* Interrupt Mask */
-# define ATMCI_CMDRDY ( 1 << 0) /* Command Ready */
-# define ATMCI_RXRDY ( 1 << 1) /* Receiver Ready */
-# define ATMCI_TXRDY ( 1 << 2) /* Transmitter Ready */
-# define ATMCI_BLKE ( 1 << 3) /* Data Block Ended */
-# define ATMCI_DTIP ( 1 << 4) /* Data Transfer In Progress */
-# define ATMCI_NOTBUSY ( 1 << 5) /* Data Not Busy */
-# define ATMCI_ENDRX ( 1 << 6) /* End of RX Buffer */
-# define ATMCI_ENDTX ( 1 << 7) /* End of TX Buffer */
-# define ATMCI_SDIOIRQA ( 1 << 8) /* SDIO IRQ in slot A */
-# define ATMCI_SDIOIRQB ( 1 << 9) /* SDIO IRQ in slot B */
-# define ATMCI_SDIOWAIT ( 1 << 12) /* SDIO Read Wait Operation Status */
-# define ATMCI_CSRCV ( 1 << 13) /* CE-ATA Completion Signal Received */
-# define ATMCI_RXBUFF ( 1 << 14) /* RX Buffer Full */
-# define ATMCI_TXBUFE ( 1 << 15) /* TX Buffer Empty */
-# define ATMCI_RINDE ( 1 << 16) /* Response Index Error */
-# define ATMCI_RDIRE ( 1 << 17) /* Response Direction Error */
-# define ATMCI_RCRCE ( 1 << 18) /* Response CRC Error */
-# define ATMCI_RENDE ( 1 << 19) /* Response End Bit Error */
-# define ATMCI_RTOE ( 1 << 20) /* Response Time-Out Error */
-# define ATMCI_DCRCE ( 1 << 21) /* Data CRC Error */
-# define ATMCI_DTOE ( 1 << 22) /* Data Time-Out Error */
-# define ATMCI_CSTOE ( 1 << 23) /* Completion Signal Time-out Error */
-# define ATMCI_BLKOVRE ( 1 << 24) /* DMA Block Overrun Error */
-# define ATMCI_DMADONE ( 1 << 25) /* DMA Transfer Done */
-# define ATMCI_FIFOEMPTY ( 1 << 26) /* FIFO Empty Flag */
-# define ATMCI_XFRDONE ( 1 << 27) /* Transfer Done Flag */
-# define ATMCI_ACKRCV ( 1 << 28) /* Boot Operation Acknowledge Received */
-# define ATMCI_ACKRCVE ( 1 << 29) /* Boot Operation Acknowledge Error */
-# define ATMCI_OVRE ( 1 << 30) /* RX Overrun Error */
-# define ATMCI_UNRE ( 1 << 31) /* TX Underrun Error */
-#define ATMCI_DMA 0x0050 /* DMA Configuration[2] */
-# define ATMCI_DMA_OFFSET(x) ((x) << 0) /* DMA Write Buffer Offset */
-# define ATMCI_DMA_CHKSIZE(x) ((x) << 4) /* DMA Channel Read and Write Chunk Size */
-# define ATMCI_DMAEN ( 1 << 8) /* DMA Hardware Handshaking Enable */
-#define ATMCI_CFG 0x0054 /* Configuration[2] */
-# define ATMCI_CFG_FIFOMODE_1DATA ( 1 << 0) /* MCI Internal FIFO control mode */
-# define ATMCI_CFG_FERRCTRL_COR ( 1 << 4) /* Flow Error flag reset control mode */
-# define ATMCI_CFG_HSMODE ( 1 << 8) /* High Speed Mode */
-# define ATMCI_CFG_LSYNC ( 1 << 12) /* Synchronize on the last block */
-#define ATMCI_WPMR 0x00e4 /* Write Protection Mode[2] */
-# define ATMCI_WP_EN ( 1 << 0) /* WP Enable */
-# define ATMCI_WP_KEY (0x4d4349 << 8) /* WP Key */
-#define ATMCI_WPSR 0x00e8 /* Write Protection Status[2] */
-# define ATMCI_GET_WP_VS(x) ((x) & 0x0f)
-# define ATMCI_GET_WP_VSRC(x) (((x) >> 8) & 0xffff)
-#define ATMCI_VERSION 0x00FC /* Version */
-#define ATMCI_FIFO_APERTURE 0x0200 /* FIFO Aperture[2] */
-
-/* This is not including the FIFO Aperture on MCI2 */
-#define ATMCI_REGS_SIZE 0x100
-
-/* Register access macros */
-#ifdef CONFIG_AVR32
-#define atmci_readl(port, reg) \
- __raw_readl((port)->regs + reg)
-#define atmci_writel(port, reg, value) \
- __raw_writel((value), (port)->regs + reg)
-#else
-#define atmci_readl(port, reg) \
- readl_relaxed((port)->regs + reg)
-#define atmci_writel(port, reg, value) \
- writel_relaxed((value), (port)->regs + reg)
-#endif
-
-/* On AVR chips the Peripheral DMA Controller is not connected to MCI. */
-#ifdef CONFIG_AVR32
-# define ATMCI_PDC_CONNECTED 0
-#else
-# define ATMCI_PDC_CONNECTED 1
-#endif
-
-/*
- * Fix sconfig's burst size according to atmel MCI. We need to convert them as:
- * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3.
- *
- * This can be done by finding most significant bit set.
- */
-static inline unsigned int atmci_convert_chksize(unsigned int maxburst)
-{
- if (maxburst > 1)
- return fls(maxburst) - 2;
- else
- return 0;
-}
-
-#endif /* __DRIVERS_MMC_ATMEL_MCI_H__ */
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index bf62e429f7fc..a36ebdae2388 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -44,7 +44,141 @@
#include <asm/io.h>
#include <asm/unaligned.h>
-#include "atmel-mci-regs.h"
+/*
+ * Superset of MCI IP registers integrated in Atmel AVR32 and AT91 Processors
+ * Registers and bitfields marked with [2] are only available in MCI2
+ */
+
+/* MCI Register Definitions */
+#define ATMCI_CR 0x0000 /* Control */
+#define ATMCI_CR_MCIEN BIT(0) /* MCI Enable */
+#define ATMCI_CR_MCIDIS BIT(1) /* MCI Disable */
+#define ATMCI_CR_PWSEN BIT(2) /* Power Save Enable */
+#define ATMCI_CR_PWSDIS BIT(3) /* Power Save Disable */
+#define ATMCI_CR_SWRST BIT(7) /* Software Reset */
+#define ATMCI_MR 0x0004 /* Mode */
+#define ATMCI_MR_CLKDIV(x) ((x) << 0) /* Clock Divider */
+#define ATMCI_MR_PWSDIV(x) ((x) << 8) /* Power Saving Divider */
+#define ATMCI_MR_RDPROOF BIT(11) /* Read Proof */
+#define ATMCI_MR_WRPROOF BIT(12) /* Write Proof */
+#define ATMCI_MR_PDCFBYTE BIT(13) /* Force Byte Transfer */
+#define ATMCI_MR_PDCPADV BIT(14) /* Padding Value */
+#define ATMCI_MR_PDCMODE BIT(15) /* PDC-oriented Mode */
+#define ATMCI_MR_CLKODD(x) ((x) << 16) /* LSB of Clock Divider */
+#define ATMCI_DTOR 0x0008 /* Data Timeout */
+#define ATMCI_DTOCYC(x) ((x) << 0) /* Data Timeout Cycles */
+#define ATMCI_DTOMUL(x) ((x) << 4) /* Data Timeout Multiplier */
+#define ATMCI_SDCR 0x000c /* SD Card / SDIO */
+#define ATMCI_SDCSEL_SLOT_A (0 << 0) /* Select SD slot A */
+#define ATMCI_SDCSEL_SLOT_B (1 << 0) /* Select SD slot A */
+#define ATMCI_SDCSEL_MASK (3 << 0)
+#define ATMCI_SDCBUS_1BIT (0 << 6) /* 1-bit data bus */
+#define ATMCI_SDCBUS_4BIT (2 << 6) /* 4-bit data bus */
+#define ATMCI_SDCBUS_8BIT (3 << 6) /* 8-bit data bus[2] */
+#define ATMCI_SDCBUS_MASK (3 << 6)
+#define ATMCI_ARGR 0x0010 /* Command Argument */
+#define ATMCI_CMDR 0x0014 /* Command */
+#define ATMCI_CMDR_CMDNB(x) ((x) << 0) /* Command Opcode */
+#define ATMCI_CMDR_RSPTYP_NONE (0 << 6) /* No response */
+#define ATMCI_CMDR_RSPTYP_48BIT (1 << 6) /* 48-bit response */
+#define ATMCI_CMDR_RSPTYP_136BIT (2 << 6) /* 136-bit response */
+#define ATMCI_CMDR_SPCMD_INIT (1 << 8) /* Initialization command */
+#define ATMCI_CMDR_SPCMD_SYNC (2 << 8) /* Synchronized command */
+#define ATMCI_CMDR_SPCMD_INT (4 << 8) /* Interrupt command */
+#define ATMCI_CMDR_SPCMD_INTRESP (5 << 8) /* Interrupt response */
+#define ATMCI_CMDR_OPDCMD (1 << 11) /* Open Drain */
+#define ATMCI_CMDR_MAXLAT_5CYC (0 << 12) /* Max latency 5 cycles */
+#define ATMCI_CMDR_MAXLAT_64CYC (1 << 12) /* Max latency 64 cycles */
+#define ATMCI_CMDR_START_XFER (1 << 16) /* Start data transfer */
+#define ATMCI_CMDR_STOP_XFER (2 << 16) /* Stop data transfer */
+#define ATMCI_CMDR_TRDIR_WRITE (0 << 18) /* Write data */
+#define ATMCI_CMDR_TRDIR_READ (1 << 18) /* Read data */
+#define ATMCI_CMDR_BLOCK (0 << 19) /* Single-block transfer */
+#define ATMCI_CMDR_MULTI_BLOCK (1 << 19) /* Multi-block transfer */
+#define ATMCI_CMDR_STREAM (2 << 19) /* MMC Stream transfer */
+#define ATMCI_CMDR_SDIO_BYTE (4 << 19) /* SDIO Byte transfer */
+#define ATMCI_CMDR_SDIO_BLOCK (5 << 19) /* SDIO Block transfer */
+#define ATMCI_CMDR_SDIO_SUSPEND (1 << 24) /* SDIO Suspend Command */
+#define ATMCI_CMDR_SDIO_RESUME (2 << 24) /* SDIO Resume Command */
+#define ATMCI_BLKR 0x0018 /* Block */
+#define ATMCI_BCNT(x) ((x) << 0) /* Data Block Count */
+#define ATMCI_BLKLEN(x) ((x) << 16) /* Data Block Length */
+#define ATMCI_CSTOR 0x001c /* Completion Signal Timeout[2] */
+#define ATMCI_CSTOCYC(x) ((x) << 0) /* CST cycles */
+#define ATMCI_CSTOMUL(x) ((x) << 4) /* CST multiplier */
+#define ATMCI_RSPR 0x0020 /* Response 0 */
+#define ATMCI_RSPR1 0x0024 /* Response 1 */
+#define ATMCI_RSPR2 0x0028 /* Response 2 */
+#define ATMCI_RSPR3 0x002c /* Response 3 */
+#define ATMCI_RDR 0x0030 /* Receive Data */
+#define ATMCI_TDR 0x0034 /* Transmit Data */
+#define ATMCI_SR 0x0040 /* Status */
+#define ATMCI_IER 0x0044 /* Interrupt Enable */
+#define ATMCI_IDR 0x0048 /* Interrupt Disable */
+#define ATMCI_IMR 0x004c /* Interrupt Mask */
+#define ATMCI_CMDRDY BIT(0) /* Command Ready */
+#define ATMCI_RXRDY BIT(1) /* Receiver Ready */
+#define ATMCI_TXRDY BIT(2) /* Transmitter Ready */
+#define ATMCI_BLKE BIT(3) /* Data Block Ended */
+#define ATMCI_DTIP BIT(4) /* Data Transfer In Progress */
+#define ATMCI_NOTBUSY BIT(5) /* Data Not Busy */
+#define ATMCI_ENDRX BIT(6) /* End of RX Buffer */
+#define ATMCI_ENDTX BIT(7) /* End of TX Buffer */
+#define ATMCI_SDIOIRQA BIT(8) /* SDIO IRQ in slot A */
+#define ATMCI_SDIOIRQB BIT(9) /* SDIO IRQ in slot B */
+#define ATMCI_SDIOWAIT BIT(12) /* SDIO Read Wait Operation Status */
+#define ATMCI_CSRCV BIT(13) /* CE-ATA Completion Signal Received */
+#define ATMCI_RXBUFF BIT(14) /* RX Buffer Full */
+#define ATMCI_TXBUFE BIT(15) /* TX Buffer Empty */
+#define ATMCI_RINDE BIT(16) /* Response Index Error */
+#define ATMCI_RDIRE BIT(17) /* Response Direction Error */
+#define ATMCI_RCRCE BIT(18) /* Response CRC Error */
+#define ATMCI_RENDE BIT(19) /* Response End Bit Error */
+#define ATMCI_RTOE BIT(20) /* Response Time-Out Error */
+#define ATMCI_DCRCE BIT(21) /* Data CRC Error */
+#define ATMCI_DTOE BIT(22) /* Data Time-Out Error */
+#define ATMCI_CSTOE BIT(23) /* Completion Signal Time-out Error */
+#define ATMCI_BLKOVRE BIT(24) /* DMA Block Overrun Error */
+#define ATMCI_DMADONE BIT(25) /* DMA Transfer Done */
+#define ATMCI_FIFOEMPTY BIT(26) /* FIFO Empty Flag */
+#define ATMCI_XFRDONE BIT(27) /* Transfer Done Flag */
+#define ATMCI_ACKRCV BIT(28) /* Boot Operation Acknowledge Received */
+#define ATMCI_ACKRCVE BIT(29) /* Boot Operation Acknowledge Error */
+#define ATMCI_OVRE BIT(30) /* RX Overrun Error */
+#define ATMCI_UNRE BIT(31) /* TX Underrun Error */
+#define ATMCI_DMA 0x0050 /* DMA Configuration[2] */
+#define ATMCI_DMA_OFFSET(x) ((x) << 0) /* DMA Write Buffer Offset */
+#define ATMCI_DMA_CHKSIZE(x) ((x) << 4) /* DMA Channel Read and Write Chunk Size */
+#define ATMCI_DMAEN BIT(8) /* DMA Hardware Handshaking Enable */
+#define ATMCI_CFG 0x0054 /* Configuration[2] */
+#define ATMCI_CFG_FIFOMODE_1DATA BIT(0) /* MCI Internal FIFO control mode */
+#define ATMCI_CFG_FERRCTRL_COR BIT(4) /* Flow Error flag reset control mode */
+#define ATMCI_CFG_HSMODE BIT(8) /* High Speed Mode */
+#define ATMCI_CFG_LSYNC BIT(12) /* Synchronize on the last block */
+#define ATMCI_WPMR 0x00e4 /* Write Protection Mode[2] */
+#define ATMCI_WP_EN BIT(0) /* WP Enable */
+#define ATMCI_WP_KEY (0x4d4349 << 8) /* WP Key */
+#define ATMCI_WPSR 0x00e8 /* Write Protection Status[2] */
+#define ATMCI_GET_WP_VS(x) ((x) & 0x0f)
+#define ATMCI_GET_WP_VSRC(x) (((x) >> 8) & 0xffff)
+#define ATMCI_VERSION 0x00FC /* Version */
+#define ATMCI_FIFO_APERTURE 0x0200 /* FIFO Aperture[2] */
+
+/* This is not including the FIFO Aperture on MCI2 */
+#define ATMCI_REGS_SIZE 0x100
+
+/* Register access macros */
+#define atmci_readl(port, reg) \
+ __raw_readl((port)->regs + reg)
+#define atmci_writel(port, reg, value) \
+ __raw_writel((value), (port)->regs + reg)
+
+/* On AVR chips the Peripheral DMA Controller is not connected to MCI. */
+#ifdef CONFIG_AVR32
+# define ATMCI_PDC_CONNECTED 0
+#else
+# define ATMCI_PDC_CONNECTED 1
+#endif
#define AUTOSUSPEND_DELAY 50
@@ -584,6 +718,29 @@ static inline unsigned int atmci_get_version(struct atmel_mci *host)
return atmci_readl(host, ATMCI_VERSION) & 0x00000fff;
}
+/*
+ * Fix sconfig's burst size according to atmel MCI. We need to convert them as:
+ * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3.
+ * With version 0x600, we need to convert them as: 1 -> 0, 2 -> 1, 4 -> 2,
+ * 8 -> 3, 16 -> 4.
+ *
+ * This can be done by finding most significant bit set.
+ */
+static inline unsigned int atmci_convert_chksize(struct atmel_mci *host,
+ unsigned int maxburst)
+{
+ unsigned int version = atmci_get_version(host);
+ unsigned int offset = 2;
+
+ if (version >= 0x600)
+ offset = 1;
+
+ if (maxburst > 1)
+ return fls(maxburst) - offset;
+ else
+ return 0;
+}
+
static void atmci_timeout_timer(unsigned long data)
{
struct atmel_mci *host;
@@ -1034,11 +1191,13 @@ atmci_prepare_data_dma(struct atmel_mci *host, struct mmc_data *data)
if (data->flags & MMC_DATA_READ) {
direction = DMA_FROM_DEVICE;
host->dma_conf.direction = slave_dirn = DMA_DEV_TO_MEM;
- maxburst = atmci_convert_chksize(host->dma_conf.src_maxburst);
+ maxburst = atmci_convert_chksize(host,
+ host->dma_conf.src_maxburst);
} else {
direction = DMA_TO_DEVICE;
host->dma_conf.direction = slave_dirn = DMA_MEM_TO_DEV;
- maxburst = atmci_convert_chksize(host->dma_conf.dst_maxburst);
+ maxburst = atmci_convert_chksize(host,
+ host->dma_conf.dst_maxburst);
}
if (host->caps.has_dma_conf_reg)
diff --git a/drivers/mmc/host/cb710-mmc.h b/drivers/mmc/host/cb710-mmc.h
index 8984ec878fc9..8ecd9e56636a 100644
--- a/drivers/mmc/host/cb710-mmc.h
+++ b/drivers/mmc/host/cb710-mmc.h
@@ -29,8 +29,7 @@ static inline struct mmc_host *cb710_slot_to_mmc(struct cb710_slot *slot)
static inline struct cb710_slot *cb710_mmc_to_slot(struct mmc_host *mmc)
{
- struct platform_device *pdev = container_of(mmc_dev(mmc),
- struct platform_device, dev);
+ struct platform_device *pdev = to_platform_device(mmc_dev(mmc));
return cb710_pdev_to_slot(pdev);
}
diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index 7e1d13b68b06..81bdeeb05a4d 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -60,7 +60,7 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
/* Get registers' physical base address */
- host->phy_regs = (void *)(regs->start);
+ host->phy_regs = regs->start;
host->regs = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(host->regs))
return PTR_ERR(host->regs);
diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c
index 9becebeeccd1..d9c92f31da64 100644
--- a/drivers/mmc/host/dw_mmc-rockchip.c
+++ b/drivers/mmc/host/dw_mmc-rockchip.c
@@ -239,20 +239,12 @@ static int dw_mci_rockchip_init(struct dw_mci *host)
return 0;
}
-/* Common capabilities of RK3288 SoC */
-static unsigned long dw_mci_rk3288_dwmmc_caps[4] = {
- MMC_CAP_RUNTIME_RESUME, /* emmc */
- MMC_CAP_RUNTIME_RESUME, /* sdmmc */
- MMC_CAP_RUNTIME_RESUME, /* sdio0 */
- MMC_CAP_RUNTIME_RESUME, /* sdio1 */
-};
static const struct dw_mci_drv_data rk2928_drv_data = {
.prepare_command = dw_mci_rockchip_prepare_command,
.init = dw_mci_rockchip_init,
};
static const struct dw_mci_drv_data rk3288_drv_data = {
- .caps = dw_mci_rk3288_dwmmc_caps,
.prepare_command = dw_mci_rockchip_prepare_command,
.set_ios = dw_mci_rk3288_set_ios,
.execute_tuning = dw_mci_rk3288_execute_tuning,
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 7a6cedbe48a8..712835177e8b 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -699,7 +699,7 @@ static int dw_mci_edmac_start_dma(struct dw_mci *host,
int ret = 0;
/* Set external dma config: burst size, burst width */
- cfg.dst_addr = (dma_addr_t)(host->phy_regs + fifo_offset);
+ cfg.dst_addr = host->phy_regs + fifo_offset;
cfg.src_addr = cfg.dst_addr;
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
@@ -1634,12 +1634,6 @@ static int dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd)
else
cmd->error = 0;
- if (cmd->error) {
- /* newer ip versions need a delay between retries */
- if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY)
- mdelay(20);
- }
-
return cmd->error;
}
@@ -2355,16 +2349,6 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
pending = mci_readl(host, MINTSTS); /* read-only mask reg */
- /*
- * DTO fix - version 2.10a and below, and only if internal DMA
- * is configured.
- */
- if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) {
- if (!pending &&
- ((mci_readl(host, STATUS) >> 17) & 0x1fff))
- pending |= SDMMC_INT_DATA_OVER;
- }
-
if (pending) {
/* Check volt switch first, since it can look like an error */
if ((host->state == STATE_SENDING_CMD11) &&
@@ -3165,9 +3149,6 @@ int dw_mci_probe(struct dw_mci *host)
/* Now that slots are all setup, we can enable card detect */
dw_mci_enable_cd(host);
- if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
- dev_info(host->dev, "Internal DMAC interrupt fix enabled.\n");
-
return 0;
err_dmaunmap:
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index 33dfd7e72516..82a97ac4e956 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -972,7 +972,7 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events,
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);
+ dev_dbg(host->dev, "interrupt events: %x\n", events);
msdc_reset_hw(host);
host->error |= REQ_DAT_ERR;
data->bytes_xfered = 0;
@@ -982,10 +982,10 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events,
else if (events & MSDC_INT_DATCRCERR)
data->error = -EILSEQ;
- dev_err(host->dev, "%s: cmd=%d; blocks=%d",
+ dev_dbg(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);
+ dev_dbg(host->dev, "data_error=%d xfer_size=%d\n",
+ (int)data->error, data->bytes_xfered);
}
msdc_data_xfer_next(host, mrq, data);
@@ -1543,7 +1543,6 @@ static int msdc_drv_probe(struct platform_device *pdev)
mmc->f_min = host->src_clk_freq / (4 * 255);
mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23;
- mmc->caps |= MMC_CAP_RUNTIME_RESUME;
/* MMC core transfer sizes tunable parameters */
mmc->max_segs = MAX_BD_NUM;
mmc->max_seg_size = BDMA_DESC_BUFLEN;
diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c
index a448498e3af2..42296e55b9de 100644
--- a/drivers/mmc/host/mvsdio.c
+++ b/drivers/mmc/host/mvsdio.c
@@ -20,15 +20,12 @@
#include <linux/scatterlist.h>
#include <linux/irq.h>
#include <linux/clk.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/mmc/host.h>
#include <linux/mmc/slot-gpio.h>
#include <asm/sizes.h>
#include <asm/unaligned.h>
-#include <linux/platform_data/mmc-mvsdio.h>
#include "mvsdio.h"
@@ -704,6 +701,10 @@ static int mvsd_probe(struct platform_device *pdev)
struct resource *r;
int ret, irq;
+ if (!np) {
+ dev_err(&pdev->dev, "no DT node\n");
+ return -ENODEV;
+ }
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
if (!r || irq < 0)
@@ -727,8 +728,12 @@ static int mvsd_probe(struct platform_device *pdev)
* fixed rate clock).
*/
host->clk = devm_clk_get(&pdev->dev, NULL);
- if (!IS_ERR(host->clk))
- clk_prepare_enable(host->clk);
+ if (IS_ERR(host->clk)) {
+ dev_err(&pdev->dev, "no clock associated\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ clk_prepare_enable(host->clk);
mmc->ops = &mvsd_ops;
@@ -744,45 +749,10 @@ static int mvsd_probe(struct platform_device *pdev)
mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count;
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
- if (np) {
- if (IS_ERR(host->clk)) {
- dev_err(&pdev->dev, "DT platforms must have a clock associated\n");
- ret = -EINVAL;
- goto out;
- }
-
- host->base_clock = clk_get_rate(host->clk) / 2;
- ret = mmc_of_parse(mmc);
- if (ret < 0)
- goto out;
- } else {
- const struct mvsdio_platform_data *mvsd_data;
-
- mvsd_data = pdev->dev.platform_data;
- if (!mvsd_data) {
- ret = -ENXIO;
- goto out;
- }
- mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ |
- MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
- host->base_clock = mvsd_data->clock / 2;
- /* GPIO 0 regarded as invalid for backward compatibility */
- if (mvsd_data->gpio_card_detect &&
- gpio_is_valid(mvsd_data->gpio_card_detect)) {
- ret = mmc_gpio_request_cd(mmc,
- mvsd_data->gpio_card_detect,
- 0);
- if (ret)
- goto out;
- } else {
- mmc->caps |= MMC_CAP_NEEDS_POLL;
- }
-
- if (mvsd_data->gpio_write_protect &&
- gpio_is_valid(mvsd_data->gpio_write_protect))
- mmc_gpio_request_ro(mmc, mvsd_data->gpio_write_protect);
- }
-
+ host->base_clock = clk_get_rate(host->clk) / 2;
+ ret = mmc_of_parse(mmc);
+ if (ret < 0)
+ goto out;
if (maxfreq)
mmc->f_max = maxfreq;
diff --git a/drivers/mmc/host/of_mmc_spi.c b/drivers/mmc/host/of_mmc_spi.c
index 6e218fb1a669..660170cd04d9 100644
--- a/drivers/mmc/host/of_mmc_spi.c
+++ b/drivers/mmc/host/of_mmc_spi.c
@@ -55,8 +55,8 @@ static int of_mmc_spi_init(struct device *dev,
{
struct of_mmc_spi *oms = to_of_mmc_spi(dev);
- return request_threaded_irq(oms->detect_irq, NULL, irqhandler, 0,
- dev_name(dev), mmc);
+ return request_threaded_irq(oms->detect_irq, NULL, irqhandler,
+ IRQF_ONESHOT, dev_name(dev), mmc);
}
static void of_mmc_spi_exit(struct device *dev, void *mmc)
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 7fb0753abe30..b6639ea0bf18 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -2250,10 +2250,8 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
pm_runtime_get_sync(host->dev);
mmc_remove_host(host->mmc);
- if (host->tx_chan)
- dma_release_channel(host->tx_chan);
- if (host->rx_chan)
- dma_release_channel(host->rx_chan);
+ dma_release_channel(host->tx_chan);
+ dma_release_channel(host->rx_chan);
pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev);
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 1f1582f6cccb..f25f29253595 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -76,6 +76,7 @@
#define ESDHC_STD_TUNING_EN (1 << 24)
/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
#define ESDHC_TUNING_START_TAP 0x1
+#define ESDHC_TUNING_STEP_MASK 0x00070000
#define ESDHC_TUNING_STEP_SHIFT 16
/* pinctrl state */
@@ -489,9 +490,11 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
m |= ESDHC_MIX_CTRL_FBCLK_SEL;
tuning_ctrl = readl(host->ioaddr + ESDHC_TUNING_CTRL);
tuning_ctrl |= ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP;
- if (imx_data->boarddata.tuning_step)
+ if (imx_data->boarddata.tuning_step) {
+ tuning_ctrl &= ~ESDHC_TUNING_STEP_MASK;
tuning_ctrl |= imx_data->boarddata.tuning_step << ESDHC_TUNING_STEP_SHIFT;
- writel(tuning_ctrl, host->ioaddr + ESDHC_TUNING_CTRL);
+ }
+ writel(tuning_ctrl, host->ioaddr + ESDHC_TUNING_CTRL);
} else {
v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
}
diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c
index 06d0b50dfe71..7e7d8f0c9438 100644
--- a/drivers/mmc/host/sdhci-of-at91.c
+++ b/drivers/mmc/host/sdhci-of-at91.c
@@ -21,6 +21,8 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
#include "sdhci-pltfm.h"
@@ -51,6 +53,60 @@ static const struct of_device_id sdhci_at91_dt_match[] = {
{}
};
+#ifdef CONFIG_PM
+static int sdhci_at91_runtime_suspend(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_at91_priv *priv = pltfm_host->priv;
+ int ret;
+
+ ret = sdhci_runtime_suspend_host(host);
+
+ clk_disable_unprepare(priv->gck);
+ clk_disable_unprepare(priv->hclock);
+ clk_disable_unprepare(priv->mainck);
+
+ return ret;
+}
+
+static int sdhci_at91_runtime_resume(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_at91_priv *priv = pltfm_host->priv;
+ int ret;
+
+ ret = clk_prepare_enable(priv->mainck);
+ if (ret) {
+ dev_err(dev, "can't enable mainck\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(priv->hclock);
+ if (ret) {
+ dev_err(dev, "can't enable hclock\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(priv->gck);
+ if (ret) {
+ dev_err(dev, "can't enable gck\n");
+ return ret;
+ }
+
+ return sdhci_runtime_resume_host(host);
+}
+#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops sdhci_at91_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(sdhci_at91_runtime_suspend,
+ sdhci_at91_runtime_resume,
+ NULL)
+};
+
static int sdhci_at91_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
@@ -144,12 +200,23 @@ static int sdhci_at91_probe(struct platform_device *pdev)
sdhci_get_of_property(pdev);
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
+ pm_runtime_use_autosuspend(&pdev->dev);
+
ret = sdhci_add_host(host);
if (ret)
- goto clocks_disable_unprepare;
+ goto pm_runtime_disable;
+
+ pm_runtime_put_autosuspend(&pdev->dev);
return 0;
+pm_runtime_disable:
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
clocks_disable_unprepare:
clk_disable_unprepare(priv->gck);
clk_disable_unprepare(priv->mainck);
@@ -165,6 +232,10 @@ static int sdhci_at91_remove(struct platform_device *pdev)
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_at91_priv *priv = pltfm_host->priv;
+ pm_runtime_get_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
sdhci_pltfm_unregister(pdev);
clk_disable_unprepare(priv->gck);
@@ -178,7 +249,7 @@ static struct platform_driver sdhci_at91_driver = {
.driver = {
.name = "sdhci-at91",
.of_match_table = sdhci_at91_dt_match,
- .pm = SDHCI_PLTFM_PMOPS,
+ .pm = &sdhci_at91_dev_pm_ops,
},
.probe = sdhci_at91_probe,
.remove = sdhci_at91_remove,
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index 90e94a028a49..83b1226471c1 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -584,6 +584,8 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
{
struct sdhci_host *host;
struct device_node *np;
+ struct sdhci_pltfm_host *pltfm_host;
+ struct sdhci_esdhc *esdhc;
int ret;
np = pdev->dev.of_node;
@@ -600,6 +602,14 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
sdhci_get_of_property(pdev);
+ pltfm_host = sdhci_priv(host);
+ esdhc = pltfm_host->priv;
+ if (esdhc->vendor_ver == VENDOR_V_22)
+ host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23;
+
+ if (esdhc->vendor_ver > VENDOR_V_22)
+ host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
+
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") ||
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index cf7ad458b4f4..cc851b065d0a 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -277,7 +277,7 @@ static int spt_select_drive_strength(struct sdhci_host *host,
if (sdhci_pci_spt_drive_strength > 0)
drive_strength = sdhci_pci_spt_drive_strength & 0xf;
else
- drive_strength = 1; /* 33-ohm */
+ drive_strength = 0; /* Default 50-ohm */
if ((mmc_driver_type_mask(drive_strength) & card_drv) == 0)
drive_strength = 0; /* Default 50-ohm */
@@ -1464,7 +1464,7 @@ static int sdhci_pci_resume(struct device *dev)
static int sdhci_pci_runtime_suspend(struct device *dev)
{
- struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
struct sdhci_pci_chip *chip;
struct sdhci_pci_slot *slot;
int i, ret;
@@ -1500,7 +1500,7 @@ err_pci_runtime_suspend:
static int sdhci_pci_runtime_resume(struct device *dev)
{
- struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
struct sdhci_pci_chip *chip;
struct sdhci_pci_slot *slot;
int i, ret;
diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c
index 87fb5ea8ebe7..072bb27a65cf 100644
--- a/drivers/mmc/host/sdhci-pltfm.c
+++ b/drivers/mmc/host/sdhci-pltfm.c
@@ -104,7 +104,8 @@ void sdhci_get_of_property(struct platform_device *pdev)
if (of_find_property(np, "keep-power-in-suspend", NULL))
host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
- if (of_find_property(np, "enable-sdio-wakeup", NULL))
+ if (of_property_read_bool(np, "wakeup-source") ||
+ of_property_read_bool(np, "enable-sdio-wakeup")) /* legacy */
host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
}
#else
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index ad28b49f0203..83c4bf7bc16c 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -22,12 +22,20 @@
#include <linux/of_device.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/gpio/consumer.h>
#include "sdhci-pltfm.h"
/* Tegra SDHOST controller vendor register definitions */
+#define SDHCI_TEGRA_VENDOR_CLOCK_CTRL 0x100
+#define SDHCI_CLOCK_CTRL_TAP_MASK 0x00ff0000
+#define SDHCI_CLOCK_CTRL_TAP_SHIFT 16
+#define SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE BIT(5)
+#define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE BIT(3)
+#define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE BIT(2)
+
#define SDHCI_TEGRA_VENDOR_MISC_CTRL 0x120
#define SDHCI_MISC_CTRL_ENABLE_SDR104 0x8
#define SDHCI_MISC_CTRL_ENABLE_SDR50 0x10
@@ -37,9 +45,9 @@
#define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0)
#define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1)
#define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2)
-#define NVQUIRK_DISABLE_SDR50 BIT(3)
-#define NVQUIRK_DISABLE_SDR104 BIT(4)
-#define NVQUIRK_DISABLE_DDR50 BIT(5)
+#define NVQUIRK_ENABLE_SDR50 BIT(3)
+#define NVQUIRK_ENABLE_SDR104 BIT(4)
+#define NVQUIRK_ENABLE_DDR50 BIT(5)
struct sdhci_tegra_soc_data {
const struct sdhci_pltfm_data *pdata;
@@ -49,6 +57,7 @@ struct sdhci_tegra_soc_data {
struct sdhci_tegra {
const struct sdhci_tegra_soc_data *soc_data;
struct gpio_desc *power_gpio;
+ bool ddr_signaling;
};
static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
@@ -124,25 +133,33 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_tegra *tegra_host = pltfm_host->priv;
const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
- u32 misc_ctrl;
+ u32 misc_ctrl, clk_ctrl;
sdhci_reset(host, mask);
if (!(mask & SDHCI_RESET_ALL))
return;
- misc_ctrl = sdhci_readw(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
+ misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
/* Erratum: Enable SDHCI spec v3.00 support */
if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300)
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
- /* Don't advertise UHS modes which aren't supported yet */
- if (soc_data->nvquirks & NVQUIRK_DISABLE_SDR50)
- misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR50;
- if (soc_data->nvquirks & NVQUIRK_DISABLE_DDR50)
- misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_DDR50;
- if (soc_data->nvquirks & NVQUIRK_DISABLE_SDR104)
- misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR104;
- sdhci_writew(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
+ /* Advertise UHS modes as supported by host */
+ if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50)
+ misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50;
+ if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
+ misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50;
+ if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104)
+ misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104;
+ sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
+
+ clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
+ clk_ctrl &= ~SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE;
+ if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50)
+ clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE;
+ sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
+
+ tegra_host->ddr_signaling = false;
}
static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width)
@@ -164,15 +181,99 @@ static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width)
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
}
+static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_tegra *tegra_host = pltfm_host->priv;
+ unsigned long host_clk;
+
+ if (!clock)
+ return;
+
+ host_clk = tegra_host->ddr_signaling ? clock * 2 : clock;
+ clk_set_rate(pltfm_host->clk, host_clk);
+ host->max_clk = clk_get_rate(pltfm_host->clk);
+
+ return sdhci_set_clock(host, clock);
+}
+
+static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host,
+ unsigned timing)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_tegra *tegra_host = pltfm_host->priv;
+
+ if (timing == MMC_TIMING_UHS_DDR50)
+ tegra_host->ddr_signaling = true;
+
+ return sdhci_set_uhs_signaling(host, timing);
+}
+
+static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+
+ /*
+ * DDR modes require the host to run at double the card frequency, so
+ * the maximum rate we can support is half of the module input clock.
+ */
+ return clk_round_rate(pltfm_host->clk, UINT_MAX) / 2;
+}
+
+static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap)
+{
+ u32 reg;
+
+ reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
+ reg &= ~SDHCI_CLOCK_CTRL_TAP_MASK;
+ reg |= tap << SDHCI_CLOCK_CTRL_TAP_SHIFT;
+ sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
+}
+
+static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
+{
+ unsigned int min, max;
+
+ /*
+ * Start search for minimum tap value at 10, as smaller values are
+ * may wrongly be reported as working but fail at higher speeds,
+ * according to the TRM.
+ */
+ min = 10;
+ while (min < 255) {
+ tegra_sdhci_set_tap(host, min);
+ if (!mmc_send_tuning(host->mmc, opcode, NULL))
+ break;
+ min++;
+ }
+
+ /* Find the maximum tap value that still passes. */
+ max = min + 1;
+ while (max < 255) {
+ tegra_sdhci_set_tap(host, max);
+ if (mmc_send_tuning(host->mmc, opcode, NULL)) {
+ max--;
+ break;
+ }
+ max++;
+ }
+
+ /* The TRM states the ideal tap value is at 75% in the passing range. */
+ tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4));
+
+ return mmc_send_tuning(host->mmc, opcode, NULL);
+}
+
static const struct sdhci_ops tegra_sdhci_ops = {
.get_ro = tegra_sdhci_get_ro,
.read_w = tegra_sdhci_readw,
.write_l = tegra_sdhci_writel,
- .set_clock = sdhci_set_clock,
+ .set_clock = tegra_sdhci_set_clock,
.set_bus_width = tegra_sdhci_set_bus_width,
.reset = tegra_sdhci_reset,
- .set_uhs_signaling = sdhci_set_uhs_signaling,
- .get_max_clock = sdhci_pltfm_clk_get_max_clock,
+ .platform_execute_tuning = tegra_sdhci_execute_tuning,
+ .set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
+ .get_max_clock = tegra_sdhci_get_max_clock,
};
static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
@@ -184,7 +285,7 @@ static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
.ops = &tegra_sdhci_ops,
};
-static struct sdhci_tegra_soc_data soc_data_tegra20 = {
+static const struct sdhci_tegra_soc_data soc_data_tegra20 = {
.pdata = &sdhci_tegra20_pdata,
.nvquirks = NVQUIRK_FORCE_SDHCI_SPEC_200 |
NVQUIRK_ENABLE_BLOCK_GAP_DET,
@@ -197,14 +298,15 @@ static const struct sdhci_pltfm_data sdhci_tegra30_pdata = {
SDHCI_QUIRK_NO_HISPD_BIT |
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
.ops = &tegra_sdhci_ops,
};
-static struct sdhci_tegra_soc_data soc_data_tegra30 = {
+static const struct sdhci_tegra_soc_data soc_data_tegra30 = {
.pdata = &sdhci_tegra30_pdata,
.nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 |
- NVQUIRK_DISABLE_SDR50 |
- NVQUIRK_DISABLE_SDR104,
+ NVQUIRK_ENABLE_SDR50 |
+ NVQUIRK_ENABLE_SDR104,
};
static const struct sdhci_ops tegra114_sdhci_ops = {
@@ -212,11 +314,12 @@ static const struct sdhci_ops tegra114_sdhci_ops = {
.read_w = tegra_sdhci_readw,
.write_w = tegra_sdhci_writew,
.write_l = tegra_sdhci_writel,
- .set_clock = sdhci_set_clock,
+ .set_clock = tegra_sdhci_set_clock,
.set_bus_width = tegra_sdhci_set_bus_width,
.reset = tegra_sdhci_reset,
- .set_uhs_signaling = sdhci_set_uhs_signaling,
- .get_max_clock = sdhci_pltfm_clk_get_max_clock,
+ .platform_execute_tuning = tegra_sdhci_execute_tuning,
+ .set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
+ .get_max_clock = tegra_sdhci_get_max_clock,
};
static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
@@ -226,17 +329,34 @@ static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
SDHCI_QUIRK_NO_HISPD_BIT |
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
.ops = &tegra114_sdhci_ops,
};
-static struct sdhci_tegra_soc_data soc_data_tegra114 = {
+static const struct sdhci_tegra_soc_data soc_data_tegra114 = {
.pdata = &sdhci_tegra114_pdata,
- .nvquirks = NVQUIRK_DISABLE_SDR50 |
- NVQUIRK_DISABLE_DDR50 |
- NVQUIRK_DISABLE_SDR104,
+ .nvquirks = NVQUIRK_ENABLE_SDR50 |
+ NVQUIRK_ENABLE_DDR50 |
+ NVQUIRK_ENABLE_SDR104,
+};
+
+static const struct sdhci_pltfm_data sdhci_tegra210_pdata = {
+ .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
+ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
+ SDHCI_QUIRK_SINGLE_POWER_WRITE |
+ SDHCI_QUIRK_NO_HISPD_BIT |
+ SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
+ SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+ .ops = &tegra114_sdhci_ops,
+};
+
+static const struct sdhci_tegra_soc_data soc_data_tegra210 = {
+ .pdata = &sdhci_tegra210_pdata,
};
static const struct of_device_id sdhci_tegra_dt_match[] = {
+ { .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 },
{ .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra114 },
{ .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 },
{ .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
@@ -271,6 +391,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
rc = -ENOMEM;
goto err_alloc_tegra_host;
}
+ tegra_host->ddr_signaling = false;
tegra_host->soc_data = soc_data;
pltfm_host->priv = tegra_host;
@@ -278,6 +399,9 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
if (rc)
goto err_parse_dt;
+ if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
+ host->mmc->caps |= MMC_CAP_1_8V_DDR;
+
tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power",
GPIOD_OUT_HIGH);
if (IS_ERR(tegra_host->power_gpio)) {
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index b48565ed5616..d622435d1bcc 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -492,7 +492,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
host->align_buffer, host->align_buffer_sz, direction);
if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr))
goto fail;
- BUG_ON(host->align_addr & host->align_mask);
+ BUG_ON(host->align_addr & SDHCI_ADMA2_MASK);
host->sg_count = sdhci_pre_dma_transfer(host, data);
if (host->sg_count < 0)
@@ -514,8 +514,8 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
* the (up to three) bytes that screw up the
* alignment.
*/
- offset = (host->align_sz - (addr & host->align_mask)) &
- host->align_mask;
+ offset = (SDHCI_ADMA2_ALIGN - (addr & SDHCI_ADMA2_MASK)) &
+ SDHCI_ADMA2_MASK;
if (offset) {
if (data->flags & MMC_DATA_WRITE) {
buffer = sdhci_kmap_atomic(sg, &flags);
@@ -529,8 +529,8 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
BUG_ON(offset > 65536);
- align += host->align_sz;
- align_addr += host->align_sz;
+ align += SDHCI_ADMA2_ALIGN;
+ align_addr += SDHCI_ADMA2_ALIGN;
desc += host->desc_sz;
@@ -540,9 +540,12 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
BUG_ON(len > 65536);
- /* tran, valid */
- sdhci_adma_write_desc(host, desc, addr, len, ADMA2_TRAN_VALID);
- desc += host->desc_sz;
+ if (len) {
+ /* tran, valid */
+ sdhci_adma_write_desc(host, desc, addr, len,
+ ADMA2_TRAN_VALID);
+ desc += host->desc_sz;
+ }
/*
* If this triggers then we have a calculation bug
@@ -608,7 +611,7 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
/* Do a quick scan of the SG list for any unaligned mappings */
has_unaligned = false;
for_each_sg(data->sg, sg, host->sg_count, i)
- if (sg_dma_address(sg) & host->align_mask) {
+ if (sg_dma_address(sg) & SDHCI_ADMA2_MASK) {
has_unaligned = true;
break;
}
@@ -620,15 +623,15 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
align = host->align_buffer;
for_each_sg(data->sg, sg, host->sg_count, i) {
- if (sg_dma_address(sg) & host->align_mask) {
- size = host->align_sz -
- (sg_dma_address(sg) & host->align_mask);
+ if (sg_dma_address(sg) & SDHCI_ADMA2_MASK) {
+ size = SDHCI_ADMA2_ALIGN -
+ (sg_dma_address(sg) & SDHCI_ADMA2_MASK);
buffer = sdhci_kmap_atomic(sg, &flags);
memcpy(buffer, align, size);
sdhci_kunmap_atomic(buffer, &flags);
- align += host->align_sz;
+ align += SDHCI_ADMA2_ALIGN;
}
}
}
@@ -768,8 +771,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
if (unlikely(broken)) {
for_each_sg(data->sg, sg, data->sg_len, i) {
if (sg->length & 0x3) {
- DBG("Reverting to PIO because of "
- "transfer size (%d)\n",
+ DBG("Reverting to PIO because of transfer size (%d)\n",
sg->length);
host->flags &= ~SDHCI_REQ_USE_DMA;
break;
@@ -803,8 +805,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
if (unlikely(broken)) {
for_each_sg(data->sg, sg, data->sg_len, i) {
if (sg->offset & 0x3) {
- DBG("Reverting to PIO because of "
- "bad alignment\n");
+ DBG("Reverting to PIO because of bad alignment\n");
host->flags &= ~SDHCI_REQ_USE_DMA;
break;
}
@@ -1016,8 +1017,8 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
if (timeout == 0) {
- pr_err("%s: Controller never released "
- "inhibit bit(s).\n", mmc_hostname(host->mmc));
+ pr_err("%s: Controller never released inhibit bit(s).\n",
+ mmc_hostname(host->mmc));
sdhci_dumpregs(host);
cmd->error = -EIO;
tasklet_schedule(&host->finish_tasklet);
@@ -1254,8 +1255,8 @@ clock_set:
while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
& SDHCI_CLOCK_INT_STABLE)) {
if (timeout == 0) {
- pr_err("%s: Internal clock never "
- "stabilised.\n", mmc_hostname(host->mmc));
+ pr_err("%s: Internal clock never stabilised.\n",
+ mmc_hostname(host->mmc));
sdhci_dumpregs(host);
return;
}
@@ -1274,19 +1275,6 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
struct mmc_host *mmc = host->mmc;
u8 pwr = 0;
- if (!IS_ERR(mmc->supply.vmmc)) {
- spin_unlock_irq(&host->lock);
- mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
- spin_lock_irq(&host->lock);
-
- if (mode != MMC_POWER_OFF)
- sdhci_writeb(host, SDHCI_POWER_ON, SDHCI_POWER_CONTROL);
- else
- sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
-
- return;
- }
-
if (mode != MMC_POWER_OFF) {
switch (1 << vdd) {
case MMC_VDD_165_195:
@@ -1301,7 +1289,9 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
pwr = SDHCI_POWER_330;
break;
default:
- BUG();
+ WARN(1, "%s: Invalid vdd %#x\n",
+ mmc_hostname(host->mmc), vdd);
+ break;
}
}
@@ -1345,6 +1335,12 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER)
mdelay(10);
}
+
+ if (!IS_ERR(mmc->supply.vmmc)) {
+ spin_unlock_irq(&host->lock);
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
+ spin_lock_irq(&host->lock);
+ }
}
/*****************************************************************************\
@@ -1540,8 +1536,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
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));
+ pr_warn("%s: invalid driver type, default to driver type B\n",
+ mmc_hostname(mmc));
ctrl_2 |= SDHCI_CTRL_DRV_TYPE_B;
}
@@ -2015,10 +2011,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
spin_lock_irqsave(&host->lock, flags);
if (!host->tuning_done) {
- pr_info(DRIVER_NAME ": Timeout waiting for "
- "Buffer Read Ready interrupt during tuning "
- "procedure, falling back to fixed sampling "
- "clock\n");
+ pr_info(DRIVER_NAME ": Timeout waiting for Buffer Read Ready interrupt during tuning procedure, falling back to fixed sampling clock\n");
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
ctrl &= ~SDHCI_CTRL_TUNED_CLK;
ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
@@ -2046,9 +2039,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
}
if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) {
- pr_info(DRIVER_NAME ": Tuning procedure"
- " failed, falling back to fixed sampling"
- " clock\n");
+ pr_info(DRIVER_NAME ": Tuning procedure failed, falling back to fixed sampling clock\n");
err = -EIO;
}
@@ -2293,8 +2284,8 @@ static void sdhci_timeout_timer(unsigned long data)
spin_lock_irqsave(&host->lock, flags);
if (host->mrq) {
- pr_err("%s: Timeout waiting for hardware "
- "interrupt.\n", mmc_hostname(host->mmc));
+ pr_err("%s: Timeout waiting for hardware interrupt.\n",
+ mmc_hostname(host->mmc));
sdhci_dumpregs(host);
if (host->data) {
@@ -2325,9 +2316,8 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask)
BUG_ON(intmask == 0);
if (!host->cmd) {
- pr_err("%s: Got command interrupt 0x%08x even "
- "though no command operation was in progress.\n",
- mmc_hostname(host->mmc), (unsigned)intmask);
+ pr_err("%s: Got command interrupt 0x%08x even though no command operation was in progress.\n",
+ mmc_hostname(host->mmc), (unsigned)intmask);
sdhci_dumpregs(host);
return;
}
@@ -2356,8 +2346,7 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask)
*/
if (host->cmd->flags & MMC_RSP_BUSY) {
if (host->cmd->data)
- DBG("Cannot wait for busy signal when also "
- "doing a data transfer");
+ DBG("Cannot wait for busy signal when also doing a data transfer");
else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ)
&& !host->busy_handle) {
/* Mark that command complete before busy is ended */
@@ -2451,9 +2440,8 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
}
}
- pr_err("%s: Got data interrupt 0x%08x even "
- "though no data operation was in progress.\n",
- mmc_hostname(host->mmc), (unsigned)intmask);
+ pr_err("%s: Got data interrupt 0x%08x even though no data operation was in progress.\n",
+ mmc_hostname(host->mmc), (unsigned)intmask);
sdhci_dumpregs(host);
return;
@@ -2760,7 +2748,7 @@ static int sdhci_runtime_pm_put(struct sdhci_host *host)
static void sdhci_runtime_pm_bus_on(struct sdhci_host *host)
{
- if (host->runtime_suspended || host->bus_on)
+ if (host->bus_on)
return;
host->bus_on = true;
pm_runtime_get_noresume(host->mmc->parent);
@@ -2768,7 +2756,7 @@ static void sdhci_runtime_pm_bus_on(struct sdhci_host *host)
static void sdhci_runtime_pm_bus_off(struct sdhci_host *host)
{
- if (host->runtime_suspended || !host->bus_on)
+ if (!host->bus_on)
return;
host->bus_on = false;
pm_runtime_put_noidle(host->mmc->parent);
@@ -2896,9 +2884,8 @@ int sdhci_add_host(struct sdhci_host *host)
host->version = (host->version & SDHCI_SPEC_VER_MASK)
>> SDHCI_SPEC_VER_SHIFT;
if (host->version > SDHCI_SPEC_300) {
- pr_err("%s: Unknown controller version (%d). "
- "You may experience problems.\n", mmc_hostname(mmc),
- host->version);
+ pr_err("%s: Unknown controller version (%d). You may experience problems.\n",
+ mmc_hostname(mmc), host->version);
}
caps[0] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
@@ -2967,24 +2954,17 @@ int sdhci_add_host(struct sdhci_host *host)
if (host->flags & SDHCI_USE_64_BIT_DMA) {
host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) *
SDHCI_ADMA2_64_DESC_SZ;
- host->align_buffer_sz = SDHCI_MAX_SEGS *
- SDHCI_ADMA2_64_ALIGN;
host->desc_sz = SDHCI_ADMA2_64_DESC_SZ;
- host->align_sz = SDHCI_ADMA2_64_ALIGN;
- host->align_mask = SDHCI_ADMA2_64_ALIGN - 1;
} else {
host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) *
SDHCI_ADMA2_32_DESC_SZ;
- host->align_buffer_sz = SDHCI_MAX_SEGS *
- SDHCI_ADMA2_32_ALIGN;
host->desc_sz = SDHCI_ADMA2_32_DESC_SZ;
- host->align_sz = SDHCI_ADMA2_32_ALIGN;
- host->align_mask = SDHCI_ADMA2_32_ALIGN - 1;
}
host->adma_table = dma_alloc_coherent(mmc_dev(mmc),
host->adma_table_sz,
&host->adma_addr,
GFP_KERNEL);
+ host->align_buffer_sz = SDHCI_MAX_SEGS * SDHCI_ADMA2_ALIGN;
host->align_buffer = kmalloc(host->align_buffer_sz, GFP_KERNEL);
if (!host->adma_table || !host->align_buffer) {
if (host->adma_table)
@@ -2998,7 +2978,7 @@ int sdhci_add_host(struct sdhci_host *host)
host->flags &= ~SDHCI_USE_ADMA;
host->adma_table = NULL;
host->align_buffer = NULL;
- } else if (host->adma_addr & host->align_mask) {
+ } else if (host->adma_addr & (SDHCI_ADMA2_DESC_ALIGN - 1)) {
pr_warn("%s: unable to allocate aligned ADMA descriptor\n",
mmc_hostname(mmc));
host->flags &= ~SDHCI_USE_ADMA;
@@ -3031,8 +3011,8 @@ int sdhci_add_host(struct sdhci_host *host)
if (host->max_clk == 0 || host->quirks &
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN) {
if (!host->ops->get_max_clock) {
- pr_err("%s: Hardware doesn't specify base clock "
- "frequency.\n", mmc_hostname(mmc));
+ pr_err("%s: Hardware doesn't specify base clock frequency.\n",
+ mmc_hostname(mmc));
return -ENODEV;
}
host->max_clk = host->ops->get_max_clock(host);
@@ -3294,8 +3274,8 @@ int sdhci_add_host(struct sdhci_host *host)
mmc->ocr_avail_mmc &= host->ocr_avail_mmc;
if (mmc->ocr_avail == 0) {
- pr_err("%s: Hardware doesn't report any "
- "support voltages.\n", mmc_hostname(mmc));
+ pr_err("%s: Hardware doesn't report any support voltages.\n",
+ mmc_hostname(mmc));
return -ENODEV;
}
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 9d4aa31b683a..7654ae5d2b4e 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -272,22 +272,27 @@
/* ADMA2 32-bit DMA descriptor size */
#define SDHCI_ADMA2_32_DESC_SZ 8
-/* ADMA2 32-bit DMA alignment */
-#define SDHCI_ADMA2_32_ALIGN 4
-
/* ADMA2 32-bit descriptor */
struct sdhci_adma2_32_desc {
__le16 cmd;
__le16 len;
__le32 addr;
-} __packed __aligned(SDHCI_ADMA2_32_ALIGN);
+} __packed __aligned(4);
+
+/* ADMA2 data alignment */
+#define SDHCI_ADMA2_ALIGN 4
+#define SDHCI_ADMA2_MASK (SDHCI_ADMA2_ALIGN - 1)
+
+/*
+ * ADMA2 descriptor alignment. Some controllers (e.g. Intel) require 8 byte
+ * alignment for the descriptor table even in 32-bit DMA mode. Memory
+ * allocation is at least 8 byte aligned anyway, so just stipulate 8 always.
+ */
+#define SDHCI_ADMA2_DESC_ALIGN 8
/* ADMA2 64-bit DMA descriptor size */
#define SDHCI_ADMA2_64_DESC_SZ 12
-/* ADMA2 64-bit DMA alignment */
-#define SDHCI_ADMA2_64_ALIGN 8
-
/*
* ADMA2 64-bit descriptor. Note 12-byte descriptor can't always be 8-byte
* aligned.
@@ -482,8 +487,6 @@ struct sdhci_host {
dma_addr_t align_addr; /* Mapped bounce buffer */
unsigned int desc_sz; /* ADMA descriptor size */
- unsigned int align_sz; /* ADMA alignment */
- unsigned int align_mask; /* ADMA alignment mask */
struct tasklet_struct finish_tasklet; /* Tasklet structures */
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index ad9ffea7d659..1ca8a1359cbc 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -397,38 +397,26 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host)
}
static struct dma_chan *
-sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
- struct sh_mmcif_plat_data *pdata,
- enum dma_transfer_direction direction)
+sh_mmcif_request_dma_pdata(struct sh_mmcif_host *host, uintptr_t slave_id)
{
- struct dma_slave_config cfg = { 0, };
- 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;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
+ if (slave_id <= 0)
+ return NULL;
- if (pdata)
- slave_data = direction == DMA_MEM_TO_DEV ?
- (void *)pdata->slave_id_tx :
- (void *)pdata->slave_id_rx;
-
- chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
- slave_data, dev,
- direction == DMA_MEM_TO_DEV ? "tx" : "rx");
-
- dev_dbg(dev, "%s: %s: got channel %p\n", __func__,
- direction == DMA_MEM_TO_DEV ? "TX" : "RX", chan);
+ return dma_request_channel(mask, shdma_chan_filter, (void *)slave_id);
+}
- if (!chan)
- return NULL;
+static int sh_mmcif_dma_slave_config(struct sh_mmcif_host *host,
+ struct dma_chan *chan,
+ enum dma_transfer_direction direction)
+{
+ struct resource *res;
+ struct dma_slave_config cfg = { 0, };
res = platform_get_resource(host->pd, IORESOURCE_MEM, 0);
-
cfg.direction = direction;
if (direction == DMA_DEV_TO_MEM) {
@@ -439,38 +427,42 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
}
- ret = dmaengine_slave_config(chan, &cfg);
- if (ret < 0) {
- dma_release_channel(chan);
- return NULL;
- }
-
- return chan;
+ return dmaengine_slave_config(chan, &cfg);
}
-static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
- struct sh_mmcif_plat_data *pdata)
+static void sh_mmcif_request_dma(struct sh_mmcif_host *host)
{
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 (!dev->of_node) {
- return;
+ /* We can only either use DMA for both Tx and Rx or not use it at all */
+ if (IS_ENABLED(CONFIG_SUPERH) && dev->platform_data) {
+ struct sh_mmcif_plat_data *pdata = dev->platform_data;
+
+ host->chan_tx = sh_mmcif_request_dma_pdata(host,
+ pdata->slave_id_tx);
+ host->chan_rx = sh_mmcif_request_dma_pdata(host,
+ pdata->slave_id_rx);
+ } else {
+ host->chan_tx = dma_request_slave_channel(dev, "tx");
+ host->chan_tx = dma_request_slave_channel(dev, "rx");
}
+ dev_dbg(dev, "%s: got channel TX %p RX %p\n", __func__, host->chan_tx,
+ host->chan_rx);
- /* We can only either use DMA for both Tx and Rx or not use it at all */
- host->chan_tx = sh_mmcif_request_dma_one(host, pdata, DMA_MEM_TO_DEV);
- if (!host->chan_tx)
- return;
+ if (!host->chan_tx || !host->chan_rx ||
+ sh_mmcif_dma_slave_config(host, host->chan_tx, DMA_MEM_TO_DEV) ||
+ sh_mmcif_dma_slave_config(host, host->chan_rx, DMA_DEV_TO_MEM))
+ goto error;
- host->chan_rx = sh_mmcif_request_dma_one(host, pdata, DMA_DEV_TO_MEM);
- if (!host->chan_rx) {
+ return;
+
+error:
+ if (host->chan_tx)
dma_release_channel(host->chan_tx);
- host->chan_tx = NULL;
- }
+ if (host->chan_rx)
+ dma_release_channel(host->chan_rx);
+ host->chan_tx = host->chan_rx = NULL;
}
static void sh_mmcif_release_dma(struct sh_mmcif_host *host)
@@ -1102,7 +1094,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, dev->platform_data);
+ sh_mmcif_request_dma(host);
host->card_present = true;
}
sh_mmcif_set_power(host, ios);
diff --git a/drivers/mmc/host/usdhi6rol0.c b/drivers/mmc/host/usdhi6rol0.c
index 4498e92116b8..b47122d3e8d8 100644
--- a/drivers/mmc/host/usdhi6rol0.c
+++ b/drivers/mmc/host/usdhi6rol0.c
@@ -1634,7 +1634,7 @@ static void usdhi6_timeout_work(struct work_struct *work)
struct usdhi6_host *host = container_of(d, struct usdhi6_host, timeout_work);
struct mmc_request *mrq = host->mrq;
struct mmc_data *data = mrq ? mrq->data : NULL;
- struct scatterlist *sg = host->sg ?: data->sg;
+ struct scatterlist *sg;
dev_warn(mmc_dev(host->mmc),
"%s timeout wait %u CMD%d: IRQ 0x%08x:0x%08x, last IRQ 0x%08x\n",
@@ -1666,6 +1666,7 @@ static void usdhi6_timeout_work(struct work_struct *work)
case USDHI6_WAIT_FOR_MWRITE:
case USDHI6_WAIT_FOR_READ:
case USDHI6_WAIT_FOR_WRITE:
+ sg = host->sg ?: data->sg;
dev_dbg(mmc_dev(host->mmc),
"%c: page #%u @ +0x%zx %ux%u in SG%u. Current SG %u bytes @ %u\n",
data->flags & MMC_DATA_READ ? 'R' : 'W', host->page_idx,
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index a03ad2951c7b..42cc953309f1 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -112,7 +112,7 @@ config MTD_CMDLINE_PARTS
config MTD_AFS_PARTS
tristate "ARM Firmware Suite partition parsing"
- depends on ARM
+ depends on (ARM || ARM64)
---help---
The ARM Firmware Suite allows the user to divide flash devices into
multiple 'images'. Each such image has a header containing its name
diff --git a/drivers/mtd/afs.c b/drivers/mtd/afs.c
index 96a33e3f7b00..d61b7edfc938 100644
--- a/drivers/mtd/afs.c
+++ b/drivers/mtd/afs.c
@@ -34,7 +34,9 @@
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
-struct footer_struct {
+#define AFSV1_FOOTER_MAGIC 0xA0FFFF9F
+
+struct footer_v1 {
u32 image_info_base; /* Address of first word of ImageFooter */
u32 image_start; /* Start of area reserved by this footer */
u32 signature; /* 'Magic' number proves it's a footer */
@@ -42,7 +44,7 @@ struct footer_struct {
u32 checksum; /* Just this structure */
};
-struct image_info_struct {
+struct image_info_v1 {
u32 bootFlags; /* Boot flags, compression etc. */
u32 imageNumber; /* Unique number, selects for boot etc. */
u32 loadAddress; /* Address program should be loaded to */
@@ -67,10 +69,10 @@ static u32 word_sum(void *words, int num)
}
static int
-afs_read_footer(struct mtd_info *mtd, u_int *img_start, u_int *iis_start,
- u_int off, u_int mask)
+afs_read_footer_v1(struct mtd_info *mtd, u_int *img_start, u_int *iis_start,
+ u_int off, u_int mask)
{
- struct footer_struct fs;
+ struct footer_v1 fs;
u_int ptr = off + mtd->erasesize - sizeof(fs);
size_t sz;
int ret;
@@ -85,25 +87,23 @@ afs_read_footer(struct mtd_info *mtd, u_int *img_start, u_int *iis_start,
return ret;
}
- ret = 1;
-
/*
* Does it contain the magic number?
*/
- if (fs.signature != 0xa0ffff9f)
- ret = 0;
+ if (fs.signature != AFSV1_FOOTER_MAGIC)
+ return 0;
/*
* Check the checksum.
*/
if (word_sum(&fs, sizeof(fs) / sizeof(u32)) != 0xffffffff)
- ret = 0;
+ return 0;
/*
* Don't touch the SIB.
*/
if (fs.type == 2)
- ret = 0;
+ return 0;
*iis_start = fs.image_info_base & mask;
*img_start = fs.image_start & mask;
@@ -113,20 +113,20 @@ afs_read_footer(struct mtd_info *mtd, u_int *img_start, u_int *iis_start,
* be located after the footer structure.
*/
if (*iis_start >= ptr)
- ret = 0;
+ return 0;
/*
* Check the start of this image. The image
* data can not be located after this block.
*/
if (*img_start > off)
- ret = 0;
+ return 0;
- return ret;
+ return 1;
}
static int
-afs_read_iis(struct mtd_info *mtd, struct image_info_struct *iis, u_int ptr)
+afs_read_iis_v1(struct mtd_info *mtd, struct image_info_v1 *iis, u_int ptr)
{
size_t sz;
int ret, i;
@@ -162,7 +162,7 @@ afs_read_iis(struct mtd_info *mtd, struct image_info_struct *iis, u_int ptr)
}
static int parse_afs_partitions(struct mtd_info *mtd,
- struct mtd_partition **pparts,
+ const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
struct mtd_partition *parts;
@@ -182,24 +182,23 @@ static int parse_afs_partitions(struct mtd_info *mtd,
* the strings.
*/
for (idx = off = sz = 0; off < mtd->size; off += mtd->erasesize) {
- struct image_info_struct iis;
+ struct image_info_v1 iis;
u_int iis_ptr, img_ptr;
- ret = afs_read_footer(mtd, &img_ptr, &iis_ptr, off, mask);
- if (ret < 0)
- break;
- if (ret == 0)
- continue;
-
- ret = afs_read_iis(mtd, &iis, iis_ptr);
+ ret = afs_read_footer_v1(mtd, &img_ptr, &iis_ptr, off, mask);
if (ret < 0)
break;
- if (ret == 0)
- continue;
-
- sz += sizeof(struct mtd_partition);
- sz += strlen(iis.name) + 1;
- idx += 1;
+ if (ret) {
+ ret = afs_read_iis_v1(mtd, &iis, iis_ptr);
+ if (ret < 0)
+ break;
+ if (ret == 0)
+ continue;
+
+ sz += sizeof(struct mtd_partition);
+ sz += strlen(iis.name) + 1;
+ idx += 1;
+ }
}
if (!sz)
@@ -215,18 +214,18 @@ static int parse_afs_partitions(struct mtd_info *mtd,
* Identify the partitions
*/
for (idx = off = 0; off < mtd->size; off += mtd->erasesize) {
- struct image_info_struct iis;
+ struct image_info_v1 iis;
u_int iis_ptr, img_ptr;
/* Read the footer. */
- ret = afs_read_footer(mtd, &img_ptr, &iis_ptr, off, mask);
+ ret = afs_read_footer_v1(mtd, &img_ptr, &iis_ptr, off, mask);
if (ret < 0)
break;
if (ret == 0)
continue;
/* Read the image info block */
- ret = afs_read_iis(mtd, &iis, iis_ptr);
+ ret = afs_read_iis_v1(mtd, &iis, iis_ptr);
if (ret < 0)
break;
if (ret == 0)
@@ -257,25 +256,10 @@ static int parse_afs_partitions(struct mtd_info *mtd,
}
static struct mtd_part_parser afs_parser = {
- .owner = THIS_MODULE,
.parse_fn = parse_afs_partitions,
.name = "afs",
};
-
-static int __init afs_parser_init(void)
-{
- register_mtd_parser(&afs_parser);
- return 0;
-}
-
-static void __exit afs_parser_exit(void)
-{
- deregister_mtd_parser(&afs_parser);
-}
-
-module_init(afs_parser_init);
-module_exit(afs_parser_exit);
-
+module_mtd_part_parser(afs_parser);
MODULE_AUTHOR("ARM Ltd");
MODULE_DESCRIPTION("ARM Firmware Suite partition parser");
diff --git a/drivers/mtd/ar7part.c b/drivers/mtd/ar7part.c
index 7c9172ad2621..90575deff0ae 100644
--- a/drivers/mtd/ar7part.c
+++ b/drivers/mtd/ar7part.c
@@ -43,7 +43,7 @@ struct ar7_bin_rec {
};
static int create_mtd_partitions(struct mtd_info *master,
- struct mtd_partition **pparts,
+ const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
struct ar7_bin_rec header;
@@ -132,24 +132,10 @@ static int create_mtd_partitions(struct mtd_info *master,
}
static struct mtd_part_parser ar7_parser = {
- .owner = THIS_MODULE,
.parse_fn = create_mtd_partitions,
.name = "ar7part",
};
-
-static int __init ar7_parser_init(void)
-{
- register_mtd_parser(&ar7_parser);
- return 0;
-}
-
-static void __exit ar7_parser_exit(void)
-{
- deregister_mtd_parser(&ar7_parser);
-}
-
-module_init(ar7_parser_init);
-module_exit(ar7_parser_exit);
+module_mtd_part_parser(ar7_parser);
MODULE_LICENSE("GPL");
MODULE_AUTHOR( "Felix Fietkau <nbd@openwrt.org>, "
diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c
index c0720c1ee4c9..8282f47bcf5d 100644
--- a/drivers/mtd/bcm47xxpart.c
+++ b/drivers/mtd/bcm47xxpart.c
@@ -82,7 +82,7 @@ out_default:
}
static int bcm47xxpart_parse(struct mtd_info *master,
- struct mtd_partition **pparts,
+ const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
struct mtd_partition *parts;
@@ -313,24 +313,10 @@ static int bcm47xxpart_parse(struct mtd_info *master,
};
static struct mtd_part_parser bcm47xxpart_mtd_parser = {
- .owner = THIS_MODULE,
.parse_fn = bcm47xxpart_parse,
.name = "bcm47xxpart",
};
-
-static int __init bcm47xxpart_init(void)
-{
- register_mtd_parser(&bcm47xxpart_mtd_parser);
- return 0;
-}
-
-static void __exit bcm47xxpart_exit(void)
-{
- deregister_mtd_parser(&bcm47xxpart_mtd_parser);
-}
-
-module_init(bcm47xxpart_init);
-module_exit(bcm47xxpart_exit);
+module_mtd_part_parser(bcm47xxpart_mtd_parser);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MTD partitioning for BCM47XX flash memories");
diff --git a/drivers/mtd/bcm63xxpart.c b/drivers/mtd/bcm63xxpart.c
index b2443f7031c9..440936998593 100644
--- a/drivers/mtd/bcm63xxpart.c
+++ b/drivers/mtd/bcm63xxpart.c
@@ -68,7 +68,7 @@ static int bcm63xx_detect_cfe(struct mtd_info *master)
}
static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
- struct mtd_partition **pparts,
+ const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
/* CFE, NVRAM and global Linux are always present */
@@ -214,24 +214,10 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
};
static struct mtd_part_parser bcm63xx_cfe_parser = {
- .owner = THIS_MODULE,
.parse_fn = bcm63xx_parse_cfe_partitions,
.name = "bcm63xxpart",
};
-
-static int __init bcm63xx_cfe_parser_init(void)
-{
- register_mtd_parser(&bcm63xx_cfe_parser);
- return 0;
-}
-
-static void __exit bcm63xx_cfe_parser_exit(void)
-{
- deregister_mtd_parser(&bcm63xx_cfe_parser);
-}
-
-module_init(bcm63xx_cfe_parser_init);
-module_exit(bcm63xx_cfe_parser_exit);
+module_mtd_part_parser(bcm63xx_cfe_parser);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Daniel Dickinson <openwrt@cshore.neomailbox.net>");
diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig
index 54479c481a7a..3b3dabce58de 100644
--- a/drivers/mtd/chips/Kconfig
+++ b/drivers/mtd/chips/Kconfig
@@ -67,6 +67,10 @@ endchoice
config MTD_CFI_GEOMETRY
bool "Specific CFI Flash geometry selection"
depends on MTD_CFI_ADV_OPTIONS
+ select MTD_MAP_BANK_WIDTH_1 if !(MTD_MAP_BANK_WIDTH_2 || \
+ MTD_MAP_BANK_WIDTH_4 || MTD_MAP_BANK_WIDTH_8 || \
+ MTD_MAP_BANK_WIDTH_16 || MTD_MAP_BANK_WIDTH_32)
+ select MTD_CFI_I1 if !(MTD_CFI_I2 || MTD_CFI_I4 || MTD_CFI_I8)
help
This option does not affect the code directly, but will enable
some other configuration options which would allow you to reduce
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c
index 286b97a304cf..5e1b68cbcd0a 100644
--- a/drivers/mtd/chips/cfi_cmdset_0001.c
+++ b/drivers/mtd/chips/cfi_cmdset_0001.c
@@ -596,7 +596,7 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
mtd->size = devsize * cfi->numchips;
mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
- mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info)
+ mtd->eraseregions = kzalloc(sizeof(struct mtd_erase_region_info)
* mtd->numeraseregions, GFP_KERNEL);
if (!mtd->eraseregions)
goto setup_err;
@@ -614,6 +614,8 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize;
mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum;
mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].lockmap = kmalloc(ernum / 8 + 1, GFP_KERNEL);
+ if (!mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].lockmap)
+ goto setup_err;
}
offset += (ersize * ernum);
}
@@ -650,6 +652,10 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
return mtd;
setup_err:
+ if (mtd->eraseregions)
+ for (i=0; i<cfi->cfiq->NumEraseRegions; i++)
+ for (j=0; j<cfi->numchips; j++)
+ kfree(mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].lockmap);
kfree(mtd->eraseregions);
kfree(mtd);
kfree(cfi->cmdset_priv);
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c
index c3624eb571d1..9dca881bb378 100644
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -615,11 +615,9 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
for (i=0; i<cfi->cfiq->NumEraseRegions / 2; i++) {
int j = (cfi->cfiq->NumEraseRegions-1)-i;
- __u32 swap;
- swap = cfi->cfiq->EraseRegionInfo[i];
- cfi->cfiq->EraseRegionInfo[i] = cfi->cfiq->EraseRegionInfo[j];
- cfi->cfiq->EraseRegionInfo[j] = swap;
+ swap(cfi->cfiq->EraseRegionInfo[i],
+ cfi->cfiq->EraseRegionInfo[j]);
}
}
/* Set the default CFI lock/unlock addresses */
diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c
index 08f62987cc37..fbd5affc0acf 100644
--- a/drivers/mtd/cmdlinepart.c
+++ b/drivers/mtd/cmdlinepart.c
@@ -304,7 +304,7 @@ static int mtdpart_setup_real(char *s)
* the first one in the chain if a NULL mtd_id is passed in.
*/
static int parse_cmdline_partitions(struct mtd_info *master,
- struct mtd_partition **pparts,
+ const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
unsigned long long offset;
@@ -382,7 +382,6 @@ static int __init mtdpart_setup(char *s)
__setup("mtdparts=", mtdpart_setup);
static struct mtd_part_parser cmdline_parser = {
- .owner = THIS_MODULE,
.parse_fn = parse_cmdline_partitions,
.name = "cmdlinepart",
};
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index fe9ceb7b5405..c9c3b7fa3051 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -152,22 +152,6 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
return 0;
}
-static int m25p80_erase(struct spi_nor *nor, loff_t offset)
-{
- struct m25p *flash = nor->priv;
-
- dev_dbg(nor->dev, "%dKiB at 0x%08x\n",
- flash->spi_nor.mtd.erasesize / 1024, (u32)offset);
-
- /* Set up command buffer. */
- flash->command[0] = nor->erase_opcode;
- m25p_addr2cmd(nor, offset, flash->command);
-
- spi_write(flash->spi, flash->command, m25p_cmdsz(nor));
-
- return 0;
-}
-
/*
* board specific setup should have ensured the SPI clock used here
* matches what the READ command supports, at least until this driver
@@ -175,12 +159,11 @@ static int m25p80_erase(struct spi_nor *nor, loff_t offset)
*/
static int m25p_probe(struct spi_device *spi)
{
- struct mtd_part_parser_data ppdata;
struct flash_platform_data *data;
struct m25p *flash;
struct spi_nor *nor;
enum read_mode mode = SPI_NOR_NORMAL;
- char *flash_name = NULL;
+ char *flash_name;
int ret;
data = dev_get_platdata(&spi->dev);
@@ -194,12 +177,11 @@ static int m25p_probe(struct spi_device *spi)
/* install the hooks */
nor->read = m25p80_read;
nor->write = m25p80_write;
- nor->erase = m25p80_erase;
nor->write_reg = m25p80_write_reg;
nor->read_reg = m25p80_read_reg;
nor->dev = &spi->dev;
- nor->flash_node = spi->dev.of_node;
+ spi_nor_set_flash_node(nor, spi->dev.of_node);
nor->priv = flash;
spi_set_drvdata(spi, flash);
@@ -220,6 +202,8 @@ static int m25p_probe(struct spi_device *spi)
*/
if (data && data->type)
flash_name = data->type;
+ else if (!strcmp(spi->modalias, "spi-nor"))
+ flash_name = NULL; /* auto-detect */
else
flash_name = spi->modalias;
@@ -227,11 +211,8 @@ static int m25p_probe(struct spi_device *spi)
if (ret)
return ret;
- ppdata.of_node = spi->dev.of_node;
-
- return mtd_device_parse_register(&nor->mtd, NULL, &ppdata,
- data ? data->parts : NULL,
- data ? data->nr_parts : 0);
+ return mtd_device_register(&nor->mtd, data ? data->parts : NULL,
+ data ? data->nr_parts : 0);
}
@@ -257,14 +238,21 @@ static int m25p_remove(struct spi_device *spi)
*/
static const struct spi_device_id m25p_ids[] = {
/*
+ * Allow non-DT platform devices to bind to the "spi-nor" modalias, and
+ * hack around the fact that the SPI core does not provide uevent
+ * matching for .of_match_table
+ */
+ {"spi-nor"},
+
+ /*
* Entries not used in DTs that should be safe to drop after replacing
- * them with "nor-jedec" in platform data.
+ * them with "spi-nor" in platform data.
*/
{"s25sl064a"}, {"w25x16"}, {"m25p10"}, {"m25px64"},
/*
- * Entries that were used in DTs without "nor-jedec" fallback and should
- * be kept for backward compatibility.
+ * Entries that were used in DTs without "jedec,spi-nor" fallback and
+ * should be kept for backward compatibility.
*/
{"at25df321a"}, {"at25df641"}, {"at26df081a"},
{"mr25h256"},
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c
index e4a88715a844..f9e9bd1cfaa0 100644
--- a/drivers/mtd/devices/mtd_dataflash.c
+++ b/drivers/mtd/devices/mtd_dataflash.c
@@ -624,7 +624,6 @@ static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages,
{
struct dataflash *priv;
struct mtd_info *device;
- struct mtd_part_parser_data ppdata;
struct flash_platform_data *pdata = dev_get_platdata(&spi->dev);
char *otp_tag = "";
int err = 0;
@@ -656,6 +655,7 @@ static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages,
device->priv = priv;
device->dev.parent = &spi->dev;
+ mtd_set_of_node(device, spi->dev.of_node);
if (revision >= 'c')
otp_tag = otp_setup(device, revision);
@@ -665,8 +665,7 @@ static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages,
pagesize, otp_tag);
spi_set_drvdata(spi, priv);
- ppdata.of_node = spi->dev.of_node;
- err = mtd_device_parse_register(device, NULL, &ppdata,
+ err = mtd_device_register(device,
pdata ? pdata->parts : NULL,
pdata ? pdata->nr_parts : 0);
diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c
index 64c7458344d4..dd5069876537 100644
--- a/drivers/mtd/devices/spear_smi.c
+++ b/drivers/mtd/devices/spear_smi.c
@@ -810,7 +810,6 @@ static int spear_smi_setup_banks(struct platform_device *pdev,
u32 bank, struct device_node *np)
{
struct spear_smi *dev = platform_get_drvdata(pdev);
- struct mtd_part_parser_data ppdata = {};
struct spear_smi_flash_info *flash_info;
struct spear_smi_plat_data *pdata;
struct spear_snor_flash *flash;
@@ -855,6 +854,7 @@ static int spear_smi_setup_banks(struct platform_device *pdev,
flash->mtd.name = flash_devices[flash_index].name;
flash->mtd.dev.parent = &pdev->dev;
+ mtd_set_of_node(&flash->mtd, np);
flash->mtd.type = MTD_NORFLASH;
flash->mtd.writesize = 1;
flash->mtd.flags = MTD_CAP_NORFLASH;
@@ -881,10 +881,8 @@ static int spear_smi_setup_banks(struct platform_device *pdev,
count = flash_info->nr_partitions;
}
#endif
- ppdata.of_node = np;
- ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
- count);
+ ret = mtd_device_register(&flash->mtd, parts, count);
if (ret) {
dev_err(&dev->pdev->dev, "Err MTD partition=%d\n", ret);
return ret;
diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c
index 3060025c8af4..5454b4113589 100644
--- a/drivers/mtd/devices/st_spi_fsm.c
+++ b/drivers/mtd/devices/st_spi_fsm.c
@@ -2025,7 +2025,6 @@ boot_device_fail:
static int stfsm_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- struct mtd_part_parser_data ppdata;
struct flash_info *info;
struct resource *res;
struct stfsm *fsm;
@@ -2035,7 +2034,6 @@ static int stfsm_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "No DT found\n");
return -EINVAL;
}
- ppdata.of_node = np;
fsm = devm_kzalloc(&pdev->dev, sizeof(*fsm), GFP_KERNEL);
if (!fsm)
@@ -2106,6 +2104,7 @@ static int stfsm_probe(struct platform_device *pdev)
fsm->mtd.name = info->name;
fsm->mtd.dev.parent = &pdev->dev;
+ mtd_set_of_node(&fsm->mtd, np);
fsm->mtd.type = MTD_NORFLASH;
fsm->mtd.writesize = 4;
fsm->mtd.writebufsize = fsm->mtd.writesize;
@@ -2124,7 +2123,7 @@ static int stfsm_probe(struct platform_device *pdev)
(long long)fsm->mtd.size, (long long)(fsm->mtd.size >> 20),
fsm->mtd.erasesize, (fsm->mtd.erasesize >> 10));
- return mtd_device_parse_register(&fsm->mtd, NULL, &ppdata, NULL, 0);
+ return mtd_device_register(&fsm->mtd, NULL, 0);
}
static int stfsm_remove(struct platform_device *pdev)
diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c
index dabf08450d0b..9fb3b0dcdac2 100644
--- a/drivers/mtd/ftl.c
+++ b/drivers/mtd/ftl.c
@@ -571,12 +571,8 @@ static int copy_erase_unit(partition_t *part, uint16_t srcunit,
/* Update the maps and usage stats*/
- i = xfer->EraseCount;
- xfer->EraseCount = eun->EraseCount;
- eun->EraseCount = i;
- i = xfer->Offset;
- xfer->Offset = eun->Offset;
- eun->Offset = i;
+ swap(xfer->EraseCount, eun->EraseCount);
+ swap(xfer->Offset, eun->Offset);
part->FreeTotal -= eun->Free;
part->FreeTotal += free;
eun->Free = free;
diff --git a/drivers/mtd/maps/lantiq-flash.c b/drivers/mtd/maps/lantiq-flash.c
index 93852054977e..c8febb326fa6 100644
--- a/drivers/mtd/maps/lantiq-flash.c
+++ b/drivers/mtd/maps/lantiq-flash.c
@@ -110,7 +110,6 @@ ltq_copy_to(struct map_info *map, unsigned long to,
static int
ltq_mtd_probe(struct platform_device *pdev)
{
- struct mtd_part_parser_data ppdata;
struct ltq_mtd *ltq_mtd;
struct cfi_private *cfi;
int err;
@@ -161,13 +160,13 @@ ltq_mtd_probe(struct platform_device *pdev)
}
ltq_mtd->mtd->dev.parent = &pdev->dev;
+ mtd_set_of_node(ltq_mtd->mtd, pdev->dev.of_node);
cfi = ltq_mtd->map->fldrv_priv;
cfi->addr_unlock1 ^= 1;
cfi->addr_unlock2 ^= 1;
- ppdata.of_node = pdev->dev.of_node;
- err = mtd_device_parse_register(ltq_mtd->mtd, NULL, &ppdata, NULL, 0);
+ err = mtd_device_register(ltq_mtd->mtd, NULL, 0);
if (err) {
dev_err(&pdev->dev, "failed to add partitions\n");
goto err_destroy;
diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c
index 3dad2111b7e3..70bb403f69f7 100644
--- a/drivers/mtd/maps/pcmciamtd.c
+++ b/drivers/mtd/maps/pcmciamtd.c
@@ -30,7 +30,7 @@
struct pcmciamtd_dev {
struct pcmcia_device *p_dev;
- caddr_t win_base; /* ioremapped address of PCMCIA window */
+ void __iomem *win_base; /* ioremapped address of PCMCIA window */
unsigned int win_size; /* size of window */
unsigned int offset; /* offset into card the window currently points at */
struct map_info pcmcia_map;
@@ -80,7 +80,7 @@ MODULE_PARM_DESC(mem_type, "Set Memory type (0=Flash, 1=RAM, 2=ROM, default=0)")
/* read/write{8,16} copy_{from,to} routines with window remapping
* to access whole card
*/
-static caddr_t remap_window(struct map_info *map, unsigned long to)
+static void __iomem *remap_window(struct map_info *map, unsigned long to)
{
struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1;
struct resource *win = (struct resource *) map->map_priv_2;
@@ -107,7 +107,7 @@ static caddr_t remap_window(struct map_info *map, unsigned long to)
static map_word pcmcia_read8_remap(struct map_info *map, unsigned long ofs)
{
- caddr_t addr;
+ void __iomem *addr;
map_word d = {{0}};
addr = remap_window(map, ofs);
@@ -122,7 +122,7 @@ static map_word pcmcia_read8_remap(struct map_info *map, unsigned long ofs)
static map_word pcmcia_read16_remap(struct map_info *map, unsigned long ofs)
{
- caddr_t addr;
+ void __iomem *addr;
map_word d = {{0}};
addr = remap_window(map, ofs);
@@ -143,7 +143,7 @@ static void pcmcia_copy_from_remap(struct map_info *map, void *to, unsigned long
pr_debug("to = %p from = %lu len = %zd\n", to, from, len);
while(len) {
int toread = win_size - (from & (win_size-1));
- caddr_t addr;
+ void __iomem *addr;
if(toread > len)
toread = len;
@@ -163,7 +163,7 @@ static void pcmcia_copy_from_remap(struct map_info *map, void *to, unsigned long
static void pcmcia_write8_remap(struct map_info *map, map_word d, unsigned long adr)
{
- caddr_t addr = remap_window(map, adr);
+ void __iomem *addr = remap_window(map, adr);
if(!addr)
return;
@@ -175,7 +175,7 @@ static void pcmcia_write8_remap(struct map_info *map, map_word d, unsigned long
static void pcmcia_write16_remap(struct map_info *map, map_word d, unsigned long adr)
{
- caddr_t addr = remap_window(map, adr);
+ void __iomem *addr = remap_window(map, adr);
if(!addr)
return;
@@ -192,7 +192,7 @@ static void pcmcia_copy_to_remap(struct map_info *map, unsigned long to, const v
pr_debug("to = %lu from = %p len = %zd\n", to, from, len);
while(len) {
int towrite = win_size - (to & (win_size-1));
- caddr_t addr;
+ void __iomem *addr;
if(towrite > len)
towrite = len;
@@ -216,7 +216,7 @@ static void pcmcia_copy_to_remap(struct map_info *map, unsigned long to, const v
static map_word pcmcia_read8(struct map_info *map, unsigned long ofs)
{
- caddr_t win_base = (caddr_t)map->map_priv_2;
+ void __iomem *win_base = (void __iomem *)map->map_priv_2;
map_word d = {{0}};
if(DEV_REMOVED(map))
@@ -231,7 +231,7 @@ static map_word pcmcia_read8(struct map_info *map, unsigned long ofs)
static map_word pcmcia_read16(struct map_info *map, unsigned long ofs)
{
- caddr_t win_base = (caddr_t)map->map_priv_2;
+ void __iomem *win_base = (void __iomem *)map->map_priv_2;
map_word d = {{0}};
if(DEV_REMOVED(map))
@@ -246,7 +246,7 @@ static map_word pcmcia_read16(struct map_info *map, unsigned long ofs)
static void pcmcia_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
- caddr_t win_base = (caddr_t)map->map_priv_2;
+ void __iomem *win_base = (void __iomem *)map->map_priv_2;
if(DEV_REMOVED(map))
return;
@@ -258,7 +258,7 @@ static void pcmcia_copy_from(struct map_info *map, void *to, unsigned long from,
static void pcmcia_write8(struct map_info *map, map_word d, unsigned long adr)
{
- caddr_t win_base = (caddr_t)map->map_priv_2;
+ void __iomem *win_base = (void __iomem *)map->map_priv_2;
if(DEV_REMOVED(map))
return;
@@ -271,7 +271,7 @@ static void pcmcia_write8(struct map_info *map, map_word d, unsigned long adr)
static void pcmcia_write16(struct map_info *map, map_word d, unsigned long adr)
{
- caddr_t win_base = (caddr_t)map->map_priv_2;
+ void __iomem *win_base = (void __iomem *)map->map_priv_2;
if(DEV_REMOVED(map))
return;
@@ -284,7 +284,7 @@ static void pcmcia_write16(struct map_info *map, map_word d, unsigned long adr)
static void pcmcia_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
{
- caddr_t win_base = (caddr_t)map->map_priv_2;
+ void __iomem *win_base = (void __iomem *)map->map_priv_2;
if(DEV_REMOVED(map))
return;
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
index e46b4e983666..70c453144f00 100644
--- a/drivers/mtd/maps/physmap_of.c
+++ b/drivers/mtd/maps/physmap_of.c
@@ -166,7 +166,6 @@ static int of_flash_probe(struct platform_device *dev)
int reg_tuple_size;
struct mtd_info **mtd_list = NULL;
resource_size_t res_size;
- struct mtd_part_parser_data ppdata;
bool map_indirect;
const char *mtd_name = NULL;
@@ -310,13 +309,14 @@ static int of_flash_probe(struct platform_device *dev)
if (err)
goto err_out;
- ppdata.of_node = dp;
+ info->cmtd->dev.parent = &dev->dev;
+ mtd_set_of_node(info->cmtd, dp);
part_probe_types = of_get_probes(dp);
if (!part_probe_types) {
err = -ENOMEM;
goto err_out;
}
- mtd_device_parse_register(info->cmtd, part_probe_types, &ppdata,
+ mtd_device_parse_register(info->cmtd, part_probe_types, NULL,
NULL, 0);
of_free_probes(part_probe_types);
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 95c13b2ffa79..309625130b21 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -32,6 +32,7 @@
#include <linux/err.h>
#include <linux/ioctl.h>
#include <linux/init.h>
+#include <linux/of.h>
#include <linux/proc_fs.h>
#include <linux/idr.h>
#include <linux/backing-dev.h>
@@ -426,15 +427,6 @@ int add_mtd_device(struct mtd_info *mtd)
mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
- if (mtd->dev.parent) {
- if (!mtd->owner && mtd->dev.parent->driver)
- mtd->owner = mtd->dev.parent->driver->owner;
- if (!mtd->name)
- mtd->name = dev_name(mtd->dev.parent);
- } else {
- pr_debug("mtd device won't show a device symlink in sysfs\n");
- }
-
/* Some chips always power up locked. Unlock them now */
if ((mtd->flags & MTD_WRITEABLE) && (mtd->flags & MTD_POWERUP_LOCK)) {
error = mtd_unlock(mtd, 0, mtd->size);
@@ -454,6 +446,7 @@ 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);
+ of_node_get(mtd_get_of_node(mtd));
error = device_register(&mtd->dev);
if (error)
goto fail_added;
@@ -476,6 +469,7 @@ int add_mtd_device(struct mtd_info *mtd)
return 0;
fail_added:
+ of_node_put(mtd_get_of_node(mtd));
idr_remove(&mtd_idr, i);
fail_locked:
mutex_unlock(&mtd_table_mutex);
@@ -517,6 +511,7 @@ int del_mtd_device(struct mtd_info *mtd)
device_unregister(&mtd->dev);
idr_remove(&mtd_idr, mtd->index);
+ of_node_put(mtd_get_of_node(mtd));
module_put(THIS_MODULE);
ret = 0;
@@ -528,9 +523,10 @@ out_error:
}
static int mtd_add_device_partitions(struct mtd_info *mtd,
- struct mtd_partition *real_parts,
- int nbparts)
+ struct mtd_partitions *parts)
{
+ const struct mtd_partition *real_parts = parts->parts;
+ int nbparts = parts->nr_parts;
int ret;
if (nbparts == 0 || IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) {
@@ -549,6 +545,21 @@ static int mtd_add_device_partitions(struct mtd_info *mtd,
return 0;
}
+/*
+ * Set a few defaults based on the parent devices, if not provided by the
+ * driver
+ */
+static void mtd_set_dev_defaults(struct mtd_info *mtd)
+{
+ if (mtd->dev.parent) {
+ if (!mtd->owner && mtd->dev.parent->driver)
+ mtd->owner = mtd->dev.parent->driver->owner;
+ if (!mtd->name)
+ mtd->name = dev_name(mtd->dev.parent);
+ } else {
+ pr_debug("mtd device won't show a device symlink in sysfs\n");
+ }
+}
/**
* mtd_device_parse_register - parse partitions and register an MTD device.
@@ -584,27 +595,29 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
const struct mtd_partition *parts,
int nr_parts)
{
+ struct mtd_partitions parsed;
int ret;
- struct mtd_partition *real_parts = NULL;
-
- ret = parse_mtd_partitions(mtd, types, &real_parts, parser_data);
- if (ret <= 0 && nr_parts && parts) {
- real_parts = kmemdup(parts, sizeof(*parts) * nr_parts,
- GFP_KERNEL);
- if (!real_parts)
- ret = -ENOMEM;
- else
- ret = nr_parts;
- }
- /* Didn't come up with either parsed OR fallback partitions */
- if (ret < 0) {
+
+ mtd_set_dev_defaults(mtd);
+
+ memset(&parsed, 0, sizeof(parsed));
+
+ ret = parse_mtd_partitions(mtd, types, &parsed, parser_data);
+ if ((ret < 0 || parsed.nr_parts == 0) && parts && nr_parts) {
+ /* Fall back to driver-provided partitions */
+ parsed = (struct mtd_partitions){
+ .parts = parts,
+ .nr_parts = nr_parts,
+ };
+ } else if (ret < 0) {
+ /* Didn't come up with parsed OR fallback partitions */
pr_info("mtd: failed to find partitions; one or more parsers reports errors (%d)\n",
ret);
/* Don't abort on errors; we can still use unpartitioned MTD */
- ret = 0;
+ memset(&parsed, 0, sizeof(parsed));
}
- ret = mtd_add_device_partitions(mtd, real_parts, ret);
+ ret = mtd_add_device_partitions(mtd, &parsed);
if (ret)
goto out;
@@ -624,7 +637,8 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
}
out:
- kfree(real_parts);
+ /* Cleanup any parsed partitions */
+ mtd_part_parser_cleanup(&parsed);
return ret;
}
EXPORT_SYMBOL_GPL(mtd_device_parse_register);
diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h
index 7b0353399a10..55fdb8e1fd2a 100644
--- a/drivers/mtd/mtdcore.h
+++ b/drivers/mtd/mtdcore.h
@@ -10,10 +10,15 @@ int add_mtd_device(struct mtd_info *mtd);
int del_mtd_device(struct mtd_info *mtd);
int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
int del_mtd_partitions(struct mtd_info *);
+
+struct mtd_partitions;
+
int parse_mtd_partitions(struct mtd_info *master, const char * const *types,
- struct mtd_partition **pparts,
+ struct mtd_partitions *pparts,
struct mtd_part_parser_data *data);
+void mtd_part_parser_cleanup(struct mtd_partitions *parts);
+
int __init init_mtdchar(void);
void __exit cleanup_mtdchar(void);
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index f8ba153f63bf..10bf304027dd 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -48,9 +48,12 @@ struct mtd_part {
/*
* Given a pointer to the MTD object in the mtd_part structure, we can retrieve
- * the pointer to that structure with this macro.
+ * the pointer to that structure.
*/
-#define PART(x) ((struct mtd_part *)(x))
+static inline struct mtd_part *mtd_to_part(const struct mtd_info *mtd)
+{
+ return container_of(mtd, struct mtd_part, mtd);
+}
/*
@@ -61,7 +64,7 @@ struct mtd_part {
static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
struct mtd_ecc_stats stats;
int res;
@@ -80,7 +83,7 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
static int part_point(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, void **virt, resource_size_t *phys)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
return part->master->_point(part->master, from + part->offset, len,
retlen, virt, phys);
@@ -88,7 +91,7 @@ static int part_point(struct mtd_info *mtd, loff_t from, size_t len,
static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
return part->master->_unpoint(part->master, from + part->offset, len);
}
@@ -98,7 +101,7 @@ static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
unsigned long offset,
unsigned long flags)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
offset += part->offset;
return part->master->_get_unmapped_area(part->master, len, offset,
@@ -108,7 +111,7 @@ static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
static int part_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
int res;
if (from >= mtd->size)
@@ -146,7 +149,7 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from,
static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
return part->master->_read_user_prot_reg(part->master, from, len,
retlen, buf);
}
@@ -154,7 +157,7 @@ static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
static int part_get_user_prot_info(struct mtd_info *mtd, size_t len,
size_t *retlen, struct otp_info *buf)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
return part->master->_get_user_prot_info(part->master, len, retlen,
buf);
}
@@ -162,7 +165,7 @@ static int part_get_user_prot_info(struct mtd_info *mtd, size_t len,
static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
return part->master->_read_fact_prot_reg(part->master, from, len,
retlen, buf);
}
@@ -170,7 +173,7 @@ static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len,
size_t *retlen, struct otp_info *buf)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
return part->master->_get_fact_prot_info(part->master, len, retlen,
buf);
}
@@ -178,7 +181,7 @@ static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len,
static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
return part->master->_write(part->master, to + part->offset, len,
retlen, buf);
}
@@ -186,7 +189,7 @@ static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
return part->master->_panic_write(part->master, to + part->offset, len,
retlen, buf);
}
@@ -194,7 +197,7 @@ static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
static int part_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
if (to >= mtd->size)
return -EINVAL;
@@ -206,7 +209,7 @@ static int part_write_oob(struct mtd_info *mtd, loff_t to,
static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
return part->master->_write_user_prot_reg(part->master, from, len,
retlen, buf);
}
@@ -214,21 +217,21 @@ static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
return part->master->_lock_user_prot_reg(part->master, from, len);
}
static int part_writev(struct mtd_info *mtd, const struct kvec *vecs,
unsigned long count, loff_t to, size_t *retlen)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
return part->master->_writev(part->master, vecs, count,
to + part->offset, retlen);
}
static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
int ret;
instr->addr += part->offset;
@@ -244,7 +247,7 @@ static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
void mtd_erase_callback(struct erase_info *instr)
{
if (instr->mtd->_erase == part_erase) {
- struct mtd_part *part = PART(instr->mtd);
+ struct mtd_part *part = mtd_to_part(instr->mtd);
if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
instr->fail_addr -= part->offset;
@@ -257,57 +260,57 @@ EXPORT_SYMBOL_GPL(mtd_erase_callback);
static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
return part->master->_lock(part->master, ofs + part->offset, len);
}
static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
return part->master->_unlock(part->master, ofs + part->offset, len);
}
static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
return part->master->_is_locked(part->master, ofs + part->offset, len);
}
static void part_sync(struct mtd_info *mtd)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
part->master->_sync(part->master);
}
static int part_suspend(struct mtd_info *mtd)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
return part->master->_suspend(part->master);
}
static void part_resume(struct mtd_info *mtd)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
part->master->_resume(part->master);
}
static int part_block_isreserved(struct mtd_info *mtd, loff_t ofs)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
ofs += part->offset;
return part->master->_block_isreserved(part->master, ofs);
}
static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
ofs += part->offset;
return part->master->_block_isbad(part->master, ofs);
}
static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
int res;
ofs += part->offset;
@@ -558,7 +561,7 @@ static ssize_t mtd_partition_offset_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mtd_info *mtd = dev_get_drvdata(dev);
- struct mtd_part *part = PART(mtd);
+ struct mtd_part *part = mtd_to_part(mtd);
return snprintf(buf, PAGE_SIZE, "%lld\n", part->offset);
}
@@ -596,11 +599,10 @@ int mtd_add_partition(struct mtd_info *master, const char *name,
if (length <= 0)
return -EINVAL;
+ memset(&part, 0, sizeof(part));
part.name = name;
part.size = length;
part.offset = offset;
- part.mask_flags = 0;
- part.ecclayout = NULL;
new = allocate_partition(master, &part, -1, offset);
if (IS_ERR(new))
@@ -685,7 +687,7 @@ int add_mtd_partitions(struct mtd_info *master,
static DEFINE_SPINLOCK(part_parser_lock);
static LIST_HEAD(part_parsers);
-static struct mtd_part_parser *get_partition_parser(const char *name)
+static struct mtd_part_parser *mtd_part_parser_get(const char *name)
{
struct mtd_part_parser *p, *ret = NULL;
@@ -702,15 +704,35 @@ static struct mtd_part_parser *get_partition_parser(const char *name)
return ret;
}
-#define put_partition_parser(p) do { module_put((p)->owner); } while (0)
+static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
+{
+ module_put(p->owner);
+}
+
+/*
+ * Many partition parsers just expected the core to kfree() all their data in
+ * one chunk. Do that by default.
+ */
+static void mtd_part_parser_cleanup_default(const struct mtd_partition *pparts,
+ int nr_parts)
+{
+ kfree(pparts);
+}
-void register_mtd_parser(struct mtd_part_parser *p)
+int __register_mtd_parser(struct mtd_part_parser *p, struct module *owner)
{
+ p->owner = owner;
+
+ if (!p->cleanup)
+ p->cleanup = &mtd_part_parser_cleanup_default;
+
spin_lock(&part_parser_lock);
list_add(&p->list, &part_parsers);
spin_unlock(&part_parser_lock);
+
+ return 0;
}
-EXPORT_SYMBOL_GPL(register_mtd_parser);
+EXPORT_SYMBOL_GPL(__register_mtd_parser);
void deregister_mtd_parser(struct mtd_part_parser *p)
{
@@ -734,7 +756,7 @@ static const char * const default_mtd_part_types[] = {
* parse_mtd_partitions - parse MTD partitions
* @master: the master partition (describes whole MTD device)
* @types: names of partition parsers to try or %NULL
- * @pparts: array of partitions found is returned here
+ * @pparts: info about partitions found is returned here
* @data: MTD partition parser-specific data
*
* This function tries to find partition on MTD device @master. It uses MTD
@@ -746,12 +768,13 @@ static const char * const default_mtd_part_types[] = {
*
* This function may return:
* o a negative error code in case of failure
- * o zero if no partitions were found
- * o a positive number of found partitions, in which case on exit @pparts will
- * point to an array containing this number of &struct mtd_info objects.
+ * o zero otherwise, and @pparts will describe the partitions, number of
+ * partitions, and the parser which parsed them. Caller must release
+ * resources with mtd_part_parser_cleanup() when finished with the returned
+ * data.
*/
int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
- struct mtd_partition **pparts,
+ struct mtd_partitions *pparts,
struct mtd_part_parser_data *data)
{
struct mtd_part_parser *parser;
@@ -762,22 +785,24 @@ int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
for ( ; *types; types++) {
pr_debug("%s: parsing partitions %s\n", master->name, *types);
- parser = get_partition_parser(*types);
+ parser = mtd_part_parser_get(*types);
if (!parser && !request_module("%s", *types))
- parser = get_partition_parser(*types);
+ parser = mtd_part_parser_get(*types);
pr_debug("%s: got parser %s\n", master->name,
parser ? parser->name : NULL);
if (!parser)
continue;
- ret = (*parser->parse_fn)(master, pparts, data);
+ ret = (*parser->parse_fn)(master, &pparts->parts, data);
pr_debug("%s: parser %s: %i\n",
master->name, parser->name, ret);
- put_partition_parser(parser);
if (ret > 0) {
printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
ret, parser->name, master->name);
- return ret;
+ pparts->nr_parts = ret;
+ pparts->parser = parser;
+ return 0;
}
+ mtd_part_parser_put(parser);
/*
* Stash the first error we see; only report it if no parser
* succeeds
@@ -788,6 +813,22 @@ int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
return err;
}
+void mtd_part_parser_cleanup(struct mtd_partitions *parts)
+{
+ const struct mtd_part_parser *parser;
+
+ if (!parts)
+ return;
+
+ parser = parts->parser;
+ if (parser) {
+ if (parser->cleanup)
+ parser->cleanup(parts->parts, parts->nr_parts);
+
+ mtd_part_parser_put(parser);
+ }
+}
+
int mtd_is_partition(const struct mtd_info *mtd)
{
struct mtd_part *part;
@@ -811,6 +852,6 @@ uint64_t mtd_get_device_size(const struct mtd_info *mtd)
if (!mtd_is_partition(mtd))
return mtd->size;
- return PART(mtd)->master->size;
+ return mtd_to_part(mtd)->master->size;
}
EXPORT_SYMBOL_GPL(mtd_get_device_size);
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 289664089cf3..20f01b3ec23d 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -55,7 +55,7 @@ config MTD_NAND_DENALI_PCI
config MTD_NAND_DENALI_DT
tristate "Support Denali NAND controller as a DT device"
select MTD_NAND_DENALI
- depends on HAS_DMA && HAVE_CLK
+ depends on HAS_DMA && HAVE_CLK && OF
help
Enable the driver for NAND flash on platforms using a Denali NAND
controller as a DT device.
@@ -480,7 +480,7 @@ config MTD_NAND_MXC
config MTD_NAND_SH_FLCTL
tristate "Support for NAND on Renesas SuperH FLCTL"
- depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
+ depends on SUPERH || COMPILE_TEST
depends on HAS_IOMEM
depends on HAS_DMA
help
@@ -519,6 +519,13 @@ config MTD_NAND_JZ4740
help
Enables support for NAND Flash on JZ4740 SoC based boards.
+config MTD_NAND_JZ4780
+ tristate "Support for NAND on JZ4780 SoC"
+ depends on MACH_JZ4780 && JZ4780_NEMC
+ help
+ Enables support for NAND Flash connected to the NEMC on JZ4780 SoC
+ based boards, using the BCH controller for hardware error correction.
+
config MTD_NAND_FSMC
tristate "Support for NAND on ST Micros FSMC"
depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 2c7f014b349e..9e3623308509 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o
obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
+obj-$(CONFIG_MTD_NAND_JZ4780) += jz4780_nand.o jz4780_bch.o
obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c
index 842f8fe91b56..68b58c85789c 100644
--- a/drivers/mtd/nand/ams-delta.c
+++ b/drivers/mtd/nand/ams-delta.c
@@ -64,8 +64,8 @@ static struct mtd_partition partition_info[] = {
static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
{
- struct nand_chip *this = mtd->priv;
- void __iomem *io_base = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ void __iomem *io_base = (void __iomem *)nand_get_controller_data(this);
writew(0, io_base + OMAP_MPUIO_IO_CNTL);
writew(byte, this->IO_ADDR_W);
@@ -77,8 +77,8 @@ static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
static u_char ams_delta_read_byte(struct mtd_info *mtd)
{
u_char res;
- struct nand_chip *this = mtd->priv;
- void __iomem *io_base = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ void __iomem *io_base = (void __iomem *)nand_get_controller_data(this);
gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 0);
ndelay(40);
@@ -183,22 +183,16 @@ static int ams_delta_init(struct platform_device *pdev)
return -ENXIO;
/* Allocate memory for MTD device structure and private data */
- ams_delta_mtd = kzalloc(sizeof(struct mtd_info) +
- sizeof(struct nand_chip), GFP_KERNEL);
- if (!ams_delta_mtd) {
+ this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
+ if (!this) {
printk (KERN_WARNING "Unable to allocate E3 NAND MTD device structure.\n");
err = -ENOMEM;
goto out;
}
+ ams_delta_mtd = nand_to_mtd(this);
ams_delta_mtd->owner = THIS_MODULE;
- /* Get pointer to private data */
- this = (struct nand_chip *) (&ams_delta_mtd[1]);
-
- /* Link the private data with the MTD structure */
- ams_delta_mtd->priv = this;
-
/*
* Don't try to request the memory region from here,
* it should have been already requested from the
@@ -212,7 +206,7 @@ static int ams_delta_init(struct platform_device *pdev)
goto out_free;
}
- this->priv = io_base;
+ nand_set_controller_data(this, (void *)io_base);
/* Set address of NAND IO lines */
this->IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH;
@@ -256,7 +250,7 @@ out_gpio:
gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB);
iounmap(io_base);
out_free:
- kfree(ams_delta_mtd);
+ kfree(this);
out:
return err;
}
@@ -276,7 +270,7 @@ static int ams_delta_cleanup(struct platform_device *pdev)
iounmap(io_base);
/* Free the MTD device structure */
- kfree(ams_delta_mtd);
+ kfree(mtd_to_nand(ams_delta_mtd));
return 0;
}
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index 583cdd9bb971..bddcf83d6859 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -116,7 +116,6 @@ static struct atmel_nfc nand_nfc;
struct atmel_nand_host {
struct nand_chip nand_chip;
- struct mtd_info mtd;
void __iomem *io_base;
dma_addr_t io_phys;
struct atmel_nand_data board;
@@ -128,7 +127,7 @@ struct atmel_nand_host {
struct atmel_nfc *nfc;
- struct atmel_nand_caps *caps;
+ const struct atmel_nand_caps *caps;
bool has_pmecc;
u8 pmecc_corr_cap;
u16 pmecc_sector_size;
@@ -182,8 +181,8 @@ static void atmel_nand_disable(struct atmel_nand_host *host)
*/
static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct atmel_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
if (ctrl & NAND_CTRL_CHANGE) {
if (ctrl & NAND_NCE)
@@ -205,8 +204,8 @@ static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl
*/
static int atmel_nand_device_ready(struct mtd_info *mtd)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct atmel_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
return gpio_get_value(host->board.rdy_pin) ^
!!host->board.rdy_pin_active_low;
@@ -215,8 +214,8 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
/* Set up for hardware ready pin and enable pin. */
static int atmel_nand_set_enable_ready_pins(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct atmel_nand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(chip);
int res = 0;
if (gpio_is_valid(host->board.rdy_pin)) {
@@ -267,8 +266,8 @@ static int atmel_nand_set_enable_ready_pins(struct mtd_info *mtd)
*/
static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct atmel_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
if (host->nfc && host->nfc->use_nfc_sram && host->nfc->data_in_sram) {
memcpy(buf, host->nfc->data_in_sram, len);
@@ -280,8 +279,8 @@ static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct atmel_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
if (host->nfc && host->nfc->use_nfc_sram && host->nfc->data_in_sram) {
memcpy(buf, host->nfc->data_in_sram, len);
@@ -293,14 +292,14 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
{
- struct nand_chip *nand_chip = mtd->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
__raw_writesb(nand_chip->IO_ADDR_W, buf, len);
}
static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
{
- struct nand_chip *nand_chip = mtd->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
__raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
}
@@ -317,8 +316,10 @@ static int nfc_set_sram_bank(struct atmel_nand_host *host, unsigned int bank)
return -EINVAL;
if (bank) {
+ struct mtd_info *mtd = nand_to_mtd(&host->nand_chip);
+
/* Only for a 2k-page or lower flash, NFC can handle 2 banks */
- if (host->mtd.writesize > 2048)
+ if (mtd->writesize > 2048)
return -EINVAL;
nfc_writel(host->nfc->hsmc_regs, BANK, ATMEL_HSMC_NFC_BANK1);
} else {
@@ -352,8 +353,8 @@ static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
struct dma_async_tx_descriptor *tx = NULL;
dma_cookie_t cookie;
- struct nand_chip *chip = mtd->priv;
- struct atmel_nand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(chip);
void *p = buf;
int err = -EIO;
enum dma_data_direction dir = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
@@ -425,8 +426,8 @@ err_buf:
static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
- struct atmel_nand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(chip);
if (use_dma && len > mtd->oobsize)
/* only use DMA for bigger than oob size: better performances */
@@ -441,8 +442,8 @@ static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
- struct atmel_nand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(chip);
if (use_dma && len > mtd->oobsize)
/* only use DMA for bigger than oob size: better performances */
@@ -533,8 +534,8 @@ static int pmecc_data_alloc(struct atmel_nand_host *host)
static void pmecc_gen_syndrome(struct mtd_info *mtd, int sector)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct atmel_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
int i;
uint32_t value;
@@ -550,8 +551,8 @@ static void pmecc_gen_syndrome(struct mtd_info *mtd, int sector)
static void pmecc_substitute(struct mtd_info *mtd)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct atmel_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
int16_t __iomem *alpha_to = host->pmecc_alpha_to;
int16_t __iomem *index_of = host->pmecc_index_of;
int16_t *partial_syn = host->pmecc_partial_syn;
@@ -592,8 +593,8 @@ static void pmecc_substitute(struct mtd_info *mtd)
static void pmecc_get_sigma(struct mtd_info *mtd)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct atmel_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
int16_t *lmu = host->pmecc_lmu;
int16_t *si = host->pmecc_si;
@@ -750,8 +751,8 @@ static void pmecc_get_sigma(struct mtd_info *mtd)
static int pmecc_err_location(struct mtd_info *mtd)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct atmel_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
unsigned long end_time;
const int cap = host->pmecc_corr_cap;
const int num = 2 * cap + 1;
@@ -802,8 +803,8 @@ static int pmecc_err_location(struct mtd_info *mtd)
static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
int sector_num, int extra_bytes, int err_nbr)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct atmel_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
int i = 0;
int byte_pos, bit_pos, sector_size, pos;
uint32_t tmp;
@@ -848,8 +849,8 @@ static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf,
u8 *ecc)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct atmel_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
int i, err_nbr;
uint8_t *buf_pos;
int max_bitflips = 0;
@@ -919,7 +920,7 @@ static void pmecc_enable(struct atmel_nand_host *host, int ecc_op)
static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
{
- struct atmel_nand_host *host = chip->priv;
+ struct atmel_nand_host *host = nand_get_controller_data(chip);
int eccsize = chip->ecc.size * chip->ecc.steps;
uint8_t *oob = chip->oob_poi;
uint32_t *eccpos = chip->ecc.layout->eccpos;
@@ -957,7 +958,7 @@ static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf, int oob_required,
int page)
{
- struct atmel_nand_host *host = chip->priv;
+ struct atmel_nand_host *host = nand_get_controller_data(chip);
uint32_t *eccpos = chip->ecc.layout->eccpos;
int i, j;
unsigned long end_time;
@@ -992,8 +993,8 @@ static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
static void atmel_pmecc_core_init(struct mtd_info *mtd)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct atmel_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
uint32_t val = 0;
struct nand_ecclayout *ecc_layout;
@@ -1159,8 +1160,8 @@ static uint16_t *create_lookup_table(struct device *dev, int sector_size)
static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
struct atmel_nand_host *host)
{
- struct mtd_info *mtd = &host->mtd;
struct nand_chip *nand_chip = &host->nand_chip;
+ struct mtd_info *mtd = nand_to_mtd(nand_chip);
struct resource *regs, *regs_pmerr, *regs_rom;
uint16_t *galois_table;
int cap, sector_size, err_no;
@@ -1308,8 +1309,8 @@ err:
static int atmel_nand_calculate(struct mtd_info *mtd,
const u_char *dat, unsigned char *ecc_code)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct atmel_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
unsigned int ecc_value;
/* get the first 2 ECC bytes */
@@ -1355,7 +1356,7 @@ static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
* Workaround: Reset the parity registers before reading the
* actual data.
*/
- struct atmel_nand_host *host = chip->priv;
+ struct atmel_nand_host *host = nand_get_controller_data(chip);
if (host->board.need_reset_workaround)
ecc_writel(host->ecc, CR, ATMEL_ECC_RST);
@@ -1412,8 +1413,8 @@ static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *isnull)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct atmel_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
unsigned int ecc_status;
unsigned int ecc_word, ecc_bit;
@@ -1444,7 +1445,7 @@ static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,
* We can't correct so many errors */
dev_dbg(host->dev, "atmel_nand : multiple errors detected."
" Unable to correct.\n");
- return -EIO;
+ return -EBADMSG;
}
/* if there's a single bit error : we can correct it */
@@ -1478,8 +1479,8 @@ static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,
*/
static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct atmel_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
if (host->board.need_reset_workaround)
ecc_writel(host->ecc, CR, ATMEL_ECC_RST);
@@ -1586,8 +1587,8 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
static int atmel_hw_nand_init_params(struct platform_device *pdev,
struct atmel_nand_host *host)
{
- struct mtd_info *mtd = &host->mtd;
struct nand_chip *nand_chip = &host->nand_chip;
+ struct mtd_info *mtd = nand_to_mtd(nand_chip);
struct resource *regs;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
@@ -1771,8 +1772,8 @@ static int nfc_send_command(struct atmel_nand_host *host,
static int nfc_device_ready(struct mtd_info *mtd)
{
u32 status, mask;
- struct nand_chip *nand_chip = mtd->priv;
- struct atmel_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
status = nfc_read_status(host);
mask = nfc_readl(host->nfc->hsmc_regs, IMR);
@@ -1787,8 +1788,8 @@ static int nfc_device_ready(struct mtd_info *mtd)
static void nfc_select_chip(struct mtd_info *mtd, int chip)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct atmel_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
if (chip == -1)
nfc_writel(host->nfc->hsmc_regs, CTRL, NFC_CTRL_DISABLE);
@@ -1799,7 +1800,7 @@ static void nfc_select_chip(struct mtd_info *mtd, int chip)
static int nfc_make_addr(struct mtd_info *mtd, int command, int column,
int page_addr, unsigned int *addr1234, unsigned int *cycle0)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
int acycle = 0;
unsigned char addr_bytes[8];
@@ -1839,8 +1840,8 @@ static int nfc_make_addr(struct mtd_info *mtd, int command, int column,
static void nfc_nand_command(struct mtd_info *mtd, unsigned int command,
int column, int page_addr)
{
- struct nand_chip *chip = mtd->priv;
- struct atmel_nand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(chip);
unsigned long timeout;
unsigned int nfc_addr_cmd = 0;
@@ -1966,7 +1967,7 @@ static int nfc_sram_write_page(struct mtd_info *mtd, struct nand_chip *chip,
{
int cfg, len;
int status = 0;
- struct atmel_nand_host *host = chip->priv;
+ struct atmel_nand_host *host = nand_get_controller_data(chip);
void *sram = host->nfc->sram_bank0 + nfc_get_sram_off(host);
/* Subpage write is not supported */
@@ -2026,8 +2027,8 @@ static int nfc_sram_write_page(struct mtd_info *mtd, struct nand_chip *chip,
static int nfc_sram_init(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct atmel_nand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct atmel_nand_host *host = nand_get_controller_data(chip);
int res = 0;
/* Initialize the NFC CFG register */
@@ -2093,7 +2094,6 @@ static int atmel_nand_probe(struct platform_device *pdev)
struct mtd_info *mtd;
struct nand_chip *nand_chip;
struct resource *mem;
- struct mtd_part_parser_data ppdata = {};
int res, irq;
/* Allocate memory for the device structure (and zero it) */
@@ -2113,10 +2113,11 @@ static int atmel_nand_probe(struct platform_device *pdev)
}
host->io_phys = (dma_addr_t)mem->start;
- mtd = &host->mtd;
nand_chip = &host->nand_chip;
+ mtd = nand_to_mtd(nand_chip);
host->dev = &pdev->dev;
if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
+ nand_set_flash_node(nand_chip, pdev->dev.of_node);
/* Only when CONFIG_OF is enabled of_node can be parsed */
res = atmel_of_init_port(host, pdev->dev.of_node);
if (res)
@@ -2126,8 +2127,8 @@ static int atmel_nand_probe(struct platform_device *pdev)
sizeof(struct atmel_nand_data));
}
- nand_chip->priv = host; /* link the private data structures */
- mtd->priv = nand_chip;
+ /* link the private data structures */
+ nand_set_controller_data(nand_chip, host);
mtd->dev.parent = &pdev->dev;
/* Set address of NAND IO lines */
@@ -2259,9 +2260,8 @@ static int atmel_nand_probe(struct platform_device *pdev)
}
mtd->name = "atmel_nand";
- ppdata.of_node = pdev->dev.of_node;
- res = mtd_device_parse_register(mtd, NULL, &ppdata,
- host->board.parts, host->board.num_parts);
+ res = mtd_device_register(mtd, host->board.parts,
+ host->board.num_parts);
if (!res)
return res;
@@ -2284,7 +2284,7 @@ err_nand_ioremap:
static int atmel_nand_remove(struct platform_device *pdev)
{
struct atmel_nand_host *host = platform_get_drvdata(pdev);
- struct mtd_info *mtd = &host->mtd;
+ struct mtd_info *mtd = nand_to_mtd(&host->nand_chip);
nand_release(mtd);
@@ -2304,11 +2304,11 @@ static int atmel_nand_remove(struct platform_device *pdev)
return 0;
}
-static struct atmel_nand_caps at91rm9200_caps = {
+static const struct atmel_nand_caps at91rm9200_caps = {
.pmecc_correct_erase_page = false,
};
-static struct atmel_nand_caps sama5d4_caps = {
+static const struct atmel_nand_caps sama5d4_caps = {
.pmecc_correct_erase_page = true,
};
diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c
index 08a130f63faf..341ea4904164 100644
--- a/drivers/mtd/nand/au1550nd.c
+++ b/drivers/mtd/nand/au1550nd.c
@@ -23,7 +23,6 @@
struct au1550nd_ctx {
- struct mtd_info info;
struct nand_chip chip;
int cs;
@@ -39,7 +38,7 @@ struct au1550nd_ctx {
*/
static u_char au_read_byte(struct mtd_info *mtd)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
u_char ret = readb(this->IO_ADDR_R);
wmb(); /* drain writebuffer */
return ret;
@@ -54,7 +53,7 @@ static u_char au_read_byte(struct mtd_info *mtd)
*/
static void au_write_byte(struct mtd_info *mtd, u_char byte)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
writeb(byte, this->IO_ADDR_W);
wmb(); /* drain writebuffer */
}
@@ -67,7 +66,7 @@ static void au_write_byte(struct mtd_info *mtd, u_char byte)
*/
static u_char au_read_byte16(struct mtd_info *mtd)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
u_char ret = (u_char) cpu_to_le16(readw(this->IO_ADDR_R));
wmb(); /* drain writebuffer */
return ret;
@@ -82,7 +81,7 @@ static u_char au_read_byte16(struct mtd_info *mtd)
*/
static void au_write_byte16(struct mtd_info *mtd, u_char byte)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
writew(le16_to_cpu((u16) byte), this->IO_ADDR_W);
wmb(); /* drain writebuffer */
}
@@ -95,7 +94,7 @@ static void au_write_byte16(struct mtd_info *mtd, u_char byte)
*/
static u16 au_read_word(struct mtd_info *mtd)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
u16 ret = readw(this->IO_ADDR_R);
wmb(); /* drain writebuffer */
return ret;
@@ -112,7 +111,7 @@ static u16 au_read_word(struct mtd_info *mtd)
static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
for (i = 0; i < len; i++) {
writeb(buf[i], this->IO_ADDR_W);
@@ -131,7 +130,7 @@ static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
int i;
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
for (i = 0; i < len; i++) {
buf[i] = readb(this->IO_ADDR_R);
@@ -150,7 +149,7 @@ static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len)
static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
u16 *p = (u16 *) buf;
len >>= 1;
@@ -172,7 +171,7 @@ static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
{
int i;
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
u16 *p = (u16 *) buf;
len >>= 1;
@@ -197,8 +196,9 @@ static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
static void au1550_hwcontrol(struct mtd_info *mtd, int cmd)
{
- struct au1550nd_ctx *ctx = container_of(mtd, struct au1550nd_ctx, info);
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct au1550nd_ctx *ctx = container_of(this, struct au1550nd_ctx,
+ chip);
switch (cmd) {
@@ -267,8 +267,9 @@ static void au1550_select_chip(struct mtd_info *mtd, int chip)
*/
static void au1550_command(struct mtd_info *mtd, unsigned command, int column, int page_addr)
{
- struct au1550nd_ctx *ctx = container_of(mtd, struct au1550nd_ctx, info);
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct au1550nd_ctx *ctx = container_of(this, struct au1550nd_ctx,
+ chip);
int ce_override = 0, i;
unsigned long flags = 0;
@@ -405,6 +406,7 @@ static int au1550nd_probe(struct platform_device *pdev)
struct au1550nd_platdata *pd;
struct au1550nd_ctx *ctx;
struct nand_chip *this;
+ struct mtd_info *mtd;
struct resource *r;
int ret, cs;
@@ -438,8 +440,8 @@ static int au1550nd_probe(struct platform_device *pdev)
}
this = &ctx->chip;
- ctx->info.priv = this;
- ctx->info.dev.parent = &pdev->dev;
+ mtd = nand_to_mtd(this);
+ mtd->dev.parent = &pdev->dev;
/* figure out which CS# r->start belongs to */
cs = find_nand_cs(r->start);
@@ -467,13 +469,13 @@ static int au1550nd_probe(struct platform_device *pdev)
this->write_buf = (pd->devwidth) ? au_write_buf16 : au_write_buf;
this->read_buf = (pd->devwidth) ? au_read_buf16 : au_read_buf;
- ret = nand_scan(&ctx->info, 1);
+ ret = nand_scan(mtd, 1);
if (ret) {
dev_err(&pdev->dev, "NAND scan failed with %d\n", ret);
goto out3;
}
- mtd_device_register(&ctx->info, pd->parts, pd->num_parts);
+ mtd_device_register(mtd, pd->parts, pd->num_parts);
platform_set_drvdata(pdev, ctx);
@@ -493,7 +495,7 @@ static int au1550nd_remove(struct platform_device *pdev)
struct au1550nd_ctx *ctx = platform_get_drvdata(pdev);
struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- nand_release(&ctx->info);
+ nand_release(nand_to_mtd(&ctx->chip));
iounmap(ctx->base);
release_mem_region(r->start, 0x1000);
kfree(ctx);
diff --git a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h b/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h
index c005a62330b1..8ea75710a854 100644
--- a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h
+++ b/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h
@@ -12,7 +12,6 @@ struct bcm47xxnflash {
struct bcma_drv_cc *cc;
struct nand_chip nand_chip;
- struct mtd_info mtd;
unsigned curr_command;
int curr_page_addr;
diff --git a/drivers/mtd/nand/bcm47xxnflash/main.c b/drivers/mtd/nand/bcm47xxnflash/main.c
index 9ba0c0f2cd9b..fb31429b70a9 100644
--- a/drivers/mtd/nand/bcm47xxnflash/main.c
+++ b/drivers/mtd/nand/bcm47xxnflash/main.c
@@ -27,15 +27,16 @@ static int bcm47xxnflash_probe(struct platform_device *pdev)
{
struct bcma_nflash *nflash = dev_get_platdata(&pdev->dev);
struct bcm47xxnflash *b47n;
+ struct mtd_info *mtd;
int err = 0;
b47n = devm_kzalloc(&pdev->dev, sizeof(*b47n), GFP_KERNEL);
if (!b47n)
return -ENOMEM;
- b47n->nand_chip.priv = b47n;
- b47n->mtd.dev.parent = &pdev->dev;
- b47n->mtd.priv = &b47n->nand_chip; /* Required */
+ nand_set_controller_data(&b47n->nand_chip, b47n);
+ mtd = nand_to_mtd(&b47n->nand_chip);
+ mtd->dev.parent = &pdev->dev;
b47n->cc = container_of(nflash, struct bcma_drv_cc, nflash);
if (b47n->cc->core->bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) {
@@ -49,7 +50,9 @@ static int bcm47xxnflash_probe(struct platform_device *pdev)
return err;
}
- err = mtd_device_parse_register(&b47n->mtd, probes, NULL, NULL, 0);
+ platform_set_drvdata(pdev, b47n);
+
+ err = mtd_device_parse_register(mtd, probes, NULL, NULL, 0);
if (err) {
pr_err("Failed to register MTD device: %d\n", err);
return err;
@@ -60,10 +63,9 @@ static int bcm47xxnflash_probe(struct platform_device *pdev)
static int bcm47xxnflash_remove(struct platform_device *pdev)
{
- struct bcma_nflash *nflash = dev_get_platdata(&pdev->dev);
+ struct bcm47xxnflash *nflash = platform_get_drvdata(pdev);
- if (nflash->mtd)
- mtd_device_unregister(nflash->mtd);
+ nand_release(nand_to_mtd(&nflash->nand_chip));
return 0;
}
diff --git a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c
index 592befc7ffa1..f1da4ea88f2c 100644
--- a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c
+++ b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c
@@ -89,8 +89,8 @@ static int bcm47xxnflash_ops_bcm4706_poll(struct bcma_drv_cc *cc)
static void bcm47xxnflash_ops_bcm4706_read(struct mtd_info *mtd, uint8_t *buf,
int len)
{
- struct nand_chip *nand_chip = (struct nand_chip *)mtd->priv;
- struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
u32 ctlcode;
u32 *dest = (u32 *)buf;
@@ -139,8 +139,8 @@ static void bcm47xxnflash_ops_bcm4706_read(struct mtd_info *mtd, uint8_t *buf,
static void bcm47xxnflash_ops_bcm4706_write(struct mtd_info *mtd,
const uint8_t *buf, int len)
{
- struct nand_chip *nand_chip = (struct nand_chip *)mtd->priv;
- struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
struct bcma_drv_cc *cc = b47n->cc;
u32 ctlcode;
@@ -173,8 +173,8 @@ static void bcm47xxnflash_ops_bcm4706_write(struct mtd_info *mtd,
static void bcm47xxnflash_ops_bcm4706_cmd_ctrl(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
- struct nand_chip *nand_chip = (struct nand_chip *)mtd->priv;
- struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
u32 code = 0;
if (cmd == NAND_CMD_NONE)
@@ -199,8 +199,8 @@ static void bcm47xxnflash_ops_bcm4706_select_chip(struct mtd_info *mtd,
static int bcm47xxnflash_ops_bcm4706_dev_ready(struct mtd_info *mtd)
{
- struct nand_chip *nand_chip = (struct nand_chip *)mtd->priv;
- struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
return !!(bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_CTL) & NCTL_READY);
}
@@ -216,8 +216,8 @@ static void bcm47xxnflash_ops_bcm4706_cmdfunc(struct mtd_info *mtd,
unsigned command, int column,
int page_addr)
{
- struct nand_chip *nand_chip = (struct nand_chip *)mtd->priv;
- struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
struct bcma_drv_cc *cc = b47n->cc;
u32 ctlcode;
int i;
@@ -312,8 +312,8 @@ static void bcm47xxnflash_ops_bcm4706_cmdfunc(struct mtd_info *mtd,
static u8 bcm47xxnflash_ops_bcm4706_read_byte(struct mtd_info *mtd)
{
- struct nand_chip *nand_chip = (struct nand_chip *)mtd->priv;
- struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
struct bcma_drv_cc *cc = b47n->cc;
u32 tmp = 0;
@@ -341,8 +341,8 @@ static u8 bcm47xxnflash_ops_bcm4706_read_byte(struct mtd_info *mtd)
static void bcm47xxnflash_ops_bcm4706_read_buf(struct mtd_info *mtd,
uint8_t *buf, int len)
{
- struct nand_chip *nand_chip = (struct nand_chip *)mtd->priv;
- struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
switch (b47n->curr_command) {
case NAND_CMD_READ0:
@@ -357,8 +357,8 @@ static void bcm47xxnflash_ops_bcm4706_read_buf(struct mtd_info *mtd,
static void bcm47xxnflash_ops_bcm4706_write_buf(struct mtd_info *mtd,
const uint8_t *buf, int len)
{
- struct nand_chip *nand_chip = (struct nand_chip *)mtd->priv;
- struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct bcm47xxnflash *b47n = nand_get_controller_data(nand_chip);
switch (b47n->curr_command) {
case NAND_CMD_SEQIN:
@@ -421,7 +421,7 @@ int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n)
(w4 << 24 | w3 << 18 | w2 << 12 | w1 << 6 | w0));
/* Scan NAND */
- err = nand_scan(&b47n->mtd, 1);
+ err = nand_scan(nand_to_mtd(&b47n->nand_chip), 1);
if (err) {
pr_err("Could not scan NAND flash: %d\n", err);
goto exit;
diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c
index 61bd2160717c..7f6b30e615b7 100644
--- a/drivers/mtd/nand/bf5xx_nand.c
+++ b/drivers/mtd/nand/bf5xx_nand.c
@@ -142,7 +142,6 @@ static struct nand_ecclayout bootrom_ecclayout = {
struct bf5xx_nand_info {
/* mtd info */
struct nand_hw_control controller;
- struct mtd_info mtd;
struct nand_chip chip;
/* platform info */
@@ -160,7 +159,8 @@ struct bf5xx_nand_info {
*/
static struct bf5xx_nand_info *mtd_to_nand_info(struct mtd_info *mtd)
{
- return container_of(mtd, struct bf5xx_nand_info, mtd);
+ return container_of(mtd_to_nand(mtd), struct bf5xx_nand_info,
+ chip);
}
static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev)
@@ -252,7 +252,7 @@ static int bf5xx_nand_correct_data_256(struct mtd_info *mtd, u_char *dat,
*/
if (hweight32(syndrome[0]) == 1) {
dev_err(info->device, "ECC data was incorrect!\n");
- return 1;
+ return -EBADMSG;
}
syndrome[1] = (calced & 0x7FF) ^ (stored & 0x7FF);
@@ -285,7 +285,7 @@ static int bf5xx_nand_correct_data_256(struct mtd_info *mtd, u_char *dat,
data = data ^ (0x1 << failing_bit);
*(dat + failing_byte) = data;
- return 0;
+ return 1;
}
/*
@@ -298,26 +298,34 @@ static int bf5xx_nand_correct_data_256(struct mtd_info *mtd, u_char *dat,
dev_err(info->device,
"Please discard data, mark bad block\n");
- return 1;
+ return -EBADMSG;
}
static int bf5xx_nand_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
- struct nand_chip *chip = mtd->priv;
- int ret;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret, bitflips = 0;
ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc);
+ if (ret < 0)
+ return ret;
+
+ bitflips = ret;
/* If ecc size is 512, correct second 256 bytes */
if (chip->ecc.size == 512) {
dat += 256;
read_ecc += 3;
calc_ecc += 3;
- ret |= bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc);
+ ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc);
+ if (ret < 0)
+ return ret;
+
+ bitflips += ret;
}
- return ret;
+ return bitflips;
}
static void bf5xx_nand_enable_hwecc(struct mtd_info *mtd, int mode)
@@ -329,7 +337,7 @@ static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd,
const u_char *dat, u_char *ecc_code)
{
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
u16 ecc0, ecc1;
u32 code[2];
u8 *p;
@@ -466,7 +474,7 @@ static void bf5xx_nand_dma_rw(struct mtd_info *mtd,
uint8_t *buf, int is_read)
{
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
unsigned short val;
dev_dbg(info->device, " mtd->%p, buf->%p, is_read %d\n",
@@ -532,7 +540,7 @@ static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd,
uint8_t *buf, int len)
{
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
dev_dbg(info->device, "mtd->%p, buf->%p, int %d\n", mtd, buf, len);
@@ -546,7 +554,7 @@ static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd,
const uint8_t *buf, int len)
{
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
dev_dbg(info->device, "mtd->%p, buf->%p, len %d\n", mtd, buf, len);
@@ -660,7 +668,7 @@ static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info)
*/
static int bf5xx_nand_add_partition(struct bf5xx_nand_info *info)
{
- struct mtd_info *mtd = &info->mtd;
+ struct mtd_info *mtd = nand_to_mtd(&info->chip);
struct mtd_partition *parts = info->platform->partitions;
int nr = info->platform->nr_partitions;
@@ -675,7 +683,7 @@ static int bf5xx_nand_remove(struct platform_device *pdev)
* and their partitions, then go through freeing the
* resources used
*/
- nand_release(&info->mtd);
+ nand_release(nand_to_mtd(&info->chip));
peripheral_free_list(bfin_nfc_pin_req);
bf5xx_nand_dma_remove(info);
@@ -685,7 +693,7 @@ static int bf5xx_nand_remove(struct platform_device *pdev)
static int bf5xx_nand_scan(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
int ret;
ret = nand_scan_ident(mtd, 1, NULL);
@@ -756,6 +764,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
/* initialise chip data struct */
chip = &info->chip;
+ mtd = nand_to_mtd(&info->chip);
if (plat->data_width)
chip->options |= NAND_BUSWIDTH_16;
@@ -772,7 +781,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
chip->cmd_ctrl = bf5xx_nand_hwcontrol;
chip->dev_ready = bf5xx_nand_devready;
- chip->priv = &info->mtd;
+ nand_set_controller_data(chip, mtd);
chip->controller = &info->controller;
chip->IO_ADDR_R = (void __iomem *) NFC_READ;
@@ -781,8 +790,6 @@ static int bf5xx_nand_probe(struct platform_device *pdev)
chip->chip_delay = 0;
/* initialise mtd info data struct */
- mtd = &info->mtd;
- mtd->priv = chip;
mtd->dev.parent = &pdev->dev;
/* initialise the hardware */
diff --git a/drivers/mtd/nand/brcmnand/Makefile b/drivers/mtd/nand/brcmnand/Makefile
index 3b1fbfd27d4f..b28ffb59eb43 100644
--- a/drivers/mtd/nand/brcmnand/Makefile
+++ b/drivers/mtd/nand/brcmnand/Makefile
@@ -2,5 +2,6 @@
# 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) += bcm6368_nand.o
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmstb_nand.o
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand.o
diff --git a/drivers/mtd/nand/brcmnand/bcm6368_nand.c b/drivers/mtd/nand/brcmnand/bcm6368_nand.c
new file mode 100644
index 000000000000..34c91b0e1e69
--- /dev/null
+++ b/drivers/mtd/nand/brcmnand/bcm6368_nand.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2015 Simon Arlott
+ *
+ * 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.
+ *
+ * Derived from bcm63138_nand.c:
+ * Copyright © 2015 Broadcom Corporation
+ *
+ * Derived from bcm963xx_4.12L.06B_consumer/shared/opensource/include/bcm963xx/63268_map_part.h:
+ * Copyright 2000-2010 Broadcom Corporation
+ *
+ * Derived from bcm963xx_4.12L.06B_consumer/shared/opensource/flash/nandflash.c:
+ * Copyright 2000-2010 Broadcom Corporation
+ */
+
+#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 bcm6368_nand_soc {
+ struct brcmnand_soc soc;
+ void __iomem *base;
+};
+
+#define BCM6368_NAND_INT 0x00
+#define BCM6368_NAND_STATUS_SHIFT 0
+#define BCM6368_NAND_STATUS_MASK (0xfff << BCM6368_NAND_STATUS_SHIFT)
+#define BCM6368_NAND_ENABLE_SHIFT 16
+#define BCM6368_NAND_ENABLE_MASK (0xffff << BCM6368_NAND_ENABLE_SHIFT)
+#define BCM6368_NAND_BASE_ADDR0 0x04
+#define BCM6368_NAND_BASE_ADDR1 0x0c
+
+enum {
+ BCM6368_NP_READ = BIT(0),
+ BCM6368_BLOCK_ERASE = BIT(1),
+ BCM6368_COPY_BACK = BIT(2),
+ BCM6368_PAGE_PGM = BIT(3),
+ BCM6368_CTRL_READY = BIT(4),
+ BCM6368_DEV_RBPIN = BIT(5),
+ BCM6368_ECC_ERR_UNC = BIT(6),
+ BCM6368_ECC_ERR_CORR = BIT(7),
+};
+
+static bool bcm6368_nand_intc_ack(struct brcmnand_soc *soc)
+{
+ struct bcm6368_nand_soc *priv =
+ container_of(soc, struct bcm6368_nand_soc, soc);
+ void __iomem *mmio = priv->base + BCM6368_NAND_INT;
+ u32 val = brcmnand_readl(mmio);
+
+ if (val & (BCM6368_CTRL_READY << BCM6368_NAND_STATUS_SHIFT)) {
+ /* Ack interrupt */
+ val &= ~BCM6368_NAND_STATUS_MASK;
+ val |= BCM6368_CTRL_READY << BCM6368_NAND_STATUS_SHIFT;
+ brcmnand_writel(val, mmio);
+ return true;
+ }
+
+ return false;
+}
+
+static void bcm6368_nand_intc_set(struct brcmnand_soc *soc, bool en)
+{
+ struct bcm6368_nand_soc *priv =
+ container_of(soc, struct bcm6368_nand_soc, soc);
+ void __iomem *mmio = priv->base + BCM6368_NAND_INT;
+ u32 val = brcmnand_readl(mmio);
+
+ /* Don't ack any interrupts */
+ val &= ~BCM6368_NAND_STATUS_MASK;
+
+ if (en)
+ val |= BCM6368_CTRL_READY << BCM6368_NAND_ENABLE_SHIFT;
+ else
+ val &= ~(BCM6368_CTRL_READY << BCM6368_NAND_ENABLE_SHIFT);
+
+ brcmnand_writel(val, mmio);
+}
+
+static int bcm6368_nand_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct bcm6368_nand_soc *priv;
+ struct brcmnand_soc *soc;
+ struct resource *res;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ soc = &priv->soc;
+
+ 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->ctlrdy_ack = bcm6368_nand_intc_ack;
+ soc->ctlrdy_set_enabled = bcm6368_nand_intc_set;
+
+ /* Disable and ack all interrupts */
+ brcmnand_writel(0, priv->base + BCM6368_NAND_INT);
+ brcmnand_writel(BCM6368_NAND_STATUS_MASK,
+ priv->base + BCM6368_NAND_INT);
+
+ return brcmnand_probe(pdev, soc);
+}
+
+static const struct of_device_id bcm6368_nand_of_match[] = {
+ { .compatible = "brcm,nand-bcm6368" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bcm6368_nand_of_match);
+
+static struct platform_driver bcm6368_nand_driver = {
+ .probe = bcm6368_nand_probe,
+ .remove = brcmnand_remove,
+ .driver = {
+ .name = "bcm6368_nand",
+ .pm = &brcmnand_pm_ops,
+ .of_match_table = bcm6368_nand_of_match,
+ }
+};
+module_platform_driver(bcm6368_nand_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Simon Arlott");
+MODULE_DESCRIPTION("NAND driver for BCM6368");
diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c
index 12c6190c6e33..844fc07d22cd 100644
--- a/drivers/mtd/nand/brcmnand/brcmnand.c
+++ b/drivers/mtd/nand/brcmnand/brcmnand.c
@@ -11,6 +11,7 @@
* GNU General Public License for more details.
*/
+#include <linux/clk.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -122,6 +123,9 @@ struct brcmnand_controller {
/* Some SoCs provide custom interrupt status register(s) */
struct brcmnand_soc *soc;
+ /* Some SoCs have a gateable clock for the controller */
+ struct clk *clk;
+
int cmd_pending;
bool dma_pending;
struct completion done;
@@ -134,7 +138,7 @@ struct brcmnand_controller {
dma_addr_t dma_pa;
/* in-memory cache of the FLASH_CACHE, used only for some commands */
- u32 flash_cache[FC_WORDS];
+ u8 flash_cache[FC_BYTES];
/* Controller revision details */
const u16 *reg_offsets;
@@ -176,10 +180,8 @@ struct brcmnand_cfg {
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;
@@ -874,8 +876,8 @@ static struct nand_ecclayout *brcmstb_choose_ecc_layout(
static void brcmnand_wp(struct mtd_info *mtd, int wp)
{
- struct nand_chip *chip = mtd->priv;
- struct brcmnand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct brcmnand_host *host = nand_get_controller_data(chip);
struct brcmnand_controller *ctrl = host->ctrl;
if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) {
@@ -1040,8 +1042,8 @@ static void brcmnand_cmd_ctrl(struct mtd_info *mtd, int dat,
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 nand_chip *chip = mtd_to_nand(mtd);
+ struct brcmnand_host *host = nand_get_controller_data(chip);
struct brcmnand_controller *ctrl = host->ctrl;
unsigned long timeo = msecs_to_jiffies(100);
@@ -1075,7 +1077,7 @@ 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 mtd_info *mtd = nand_to_mtd(&host->chip);
struct nand_chip *chip = &host->chip;
struct brcmnand_controller *ctrl = host->ctrl;
u32 tmp;
@@ -1114,8 +1116,8 @@ static int brcmnand_low_level_op(struct brcmnand_host *host,
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 nand_chip *chip = mtd_to_nand(mtd);
+ struct brcmnand_host *host = nand_get_controller_data(chip);
struct brcmnand_controller *ctrl = host->ctrl;
u64 addr = (u64)page_addr << chip->page_shift;
int native_cmd = 0;
@@ -1188,6 +1190,8 @@ static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command,
if (native_cmd == CMD_PARAMETER_READ ||
native_cmd == CMD_PARAMETER_CHANGE_COL) {
+ /* Copy flash cache word-wise */
+ u32 *flash_cache = (u32 *)ctrl->flash_cache;
int i;
brcmnand_soc_data_bus_prepare(ctrl->soc);
@@ -1197,7 +1201,11 @@ static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command,
* SECTOR_SIZE_1K may invalidate it
*/
for (i = 0; i < FC_WORDS; i++)
- ctrl->flash_cache[i] = brcmnand_read_fc(ctrl, i);
+ /*
+ * Flash cache is big endian for parameter pages, at
+ * least on STB SoCs
+ */
+ flash_cache[i] = be32_to_cpu(brcmnand_read_fc(ctrl, i));
brcmnand_soc_data_bus_unprepare(ctrl->soc);
@@ -1214,8 +1222,8 @@ static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command,
static uint8_t brcmnand_read_byte(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct brcmnand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct brcmnand_host *host = nand_get_controller_data(chip);
struct brcmnand_controller *ctrl = host->ctrl;
uint8_t ret = 0;
int addr, offs;
@@ -1250,8 +1258,7 @@ static uint8_t brcmnand_read_byte(struct mtd_info *mtd)
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));
+ ret = ctrl->flash_cache[offs];
break;
case NAND_CMD_GET_FEATURES:
if (host->last_byte >= ONFI_SUBFEATURE_PARAM_LEN) {
@@ -1282,8 +1289,8 @@ 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;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct brcmnand_host *host = nand_get_controller_data(chip);
switch (host->last_cmd) {
case NAND_CMD_SET_FEATURES:
@@ -1393,13 +1400,15 @@ 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_host *host = nand_get_controller_data(chip);
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_UNCORR_EXT_ADDR, 0);
+ brcmnand_write_reg(ctrl, BRCMNAND_CORR_EXT_ADDR, 0);
brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
(host->cs << 16) | ((addr >> 32) & 0xffff));
@@ -1454,7 +1463,7 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
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_host *host = nand_get_controller_data(chip);
struct brcmnand_controller *ctrl = host->ctrl;
u64 err_addr = 0;
int err;
@@ -1504,7 +1513,7 @@ static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
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;
+ struct brcmnand_host *host = nand_get_controller_data(chip);
u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
return brcmnand_read(mtd, chip, host->last_addr,
@@ -1514,7 +1523,7 @@ static int brcmnand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
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;
+ struct brcmnand_host *host = nand_get_controller_data(chip);
u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
int ret;
@@ -1536,7 +1545,7 @@ static int brcmnand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
static int brcmnand_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
- struct brcmnand_host *host = chip->priv;
+ struct brcmnand_host *host = nand_get_controller_data(chip);
brcmnand_set_ecc_enabled(host, 0);
brcmnand_read(mtd, chip, (u64)page << chip->page_shift,
@@ -1546,20 +1555,10 @@ static int brcmnand_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
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_host *host = nand_get_controller_data(chip);
struct brcmnand_controller *ctrl = host->ctrl;
unsigned int i, j, trans = mtd->writesize >> FC_SHIFT;
int status, ret = 0;
@@ -1630,7 +1629,7 @@ out:
static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required, int page)
{
- struct brcmnand_host *host = chip->priv;
+ struct brcmnand_host *host = nand_get_controller_data(chip);
void *oob = oob_required ? chip->oob_poi : NULL;
brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob);
@@ -1641,7 +1640,7 @@ static int brcmnand_write_page_raw(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf,
int oob_required, int page)
{
- struct brcmnand_host *host = chip->priv;
+ struct brcmnand_host *host = nand_get_controller_data(chip);
void *oob = oob_required ? chip->oob_poi : NULL;
brcmnand_set_ecc_enabled(host, 0);
@@ -1660,7 +1659,7 @@ static int brcmnand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
static int brcmnand_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
- struct brcmnand_host *host = chip->priv;
+ struct brcmnand_host *host = nand_get_controller_data(chip);
int ret;
brcmnand_set_ecc_enabled(host, 0);
@@ -1806,7 +1805,7 @@ static inline int get_blk_adr_bytes(u64 size, u32 writesize)
static int brcmnand_setup_dev(struct brcmnand_host *host)
{
- struct mtd_info *mtd = &host->mtd;
+ struct mtd_info *mtd = nand_to_mtd(&host->chip);
struct nand_chip *chip = &host->chip;
struct brcmnand_controller *ctrl = host->ctrl;
struct brcmnand_cfg *cfg = &host->hwcfg;
@@ -1816,7 +1815,7 @@ static int brcmnand_setup_dev(struct brcmnand_host *host)
memset(cfg, 0, sizeof(*cfg));
- ret = of_property_read_u32(chip->flash_node,
+ ret = of_property_read_u32(nand_get_flash_node(chip),
"brcm,nand-oob-sector-size",
&oob_sector);
if (ret) {
@@ -1905,16 +1904,14 @@ static int brcmnand_setup_dev(struct brcmnand_host *host)
return 0;
}
-static int brcmnand_init_cs(struct brcmnand_host *host)
+static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
{
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;
u16 cfg_offs;
- struct mtd_part_parser_data ppdata = { .of_node = dn };
ret = of_property_read_u32(dn, "reg", &host->cs);
if (ret) {
@@ -1922,12 +1919,11 @@ static int brcmnand_init_cs(struct brcmnand_host *host)
return -ENXIO;
}
- mtd = &host->mtd;
+ mtd = nand_to_mtd(&host->chip);
chip = &host->chip;
- chip->flash_node = dn;
- chip->priv = host;
- mtd->priv = chip;
+ nand_set_flash_node(chip, dn);
+ nand_set_controller_data(chip, host);
mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d",
host->cs);
mtd->owner = THIS_MODULE;
@@ -1945,7 +1941,6 @@ static int brcmnand_init_cs(struct brcmnand_host *host)
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;
@@ -1993,7 +1988,7 @@ static int brcmnand_init_cs(struct brcmnand_host *host)
if (nand_scan_tail(mtd))
return -ENXIO;
- return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+ return mtd_device_register(mtd, NULL, 0);
}
static void brcmnand_save_restore_cs_config(struct brcmnand_host *host,
@@ -2067,8 +2062,8 @@ static int brcmnand_resume(struct device *dev)
}
list_for_each_entry(host, &ctrl->host_list, node) {
- struct mtd_info *mtd = &host->mtd;
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = &host->chip;
+ struct mtd_info *mtd = nand_to_mtd(chip);
brcmnand_save_restore_cs_config(host, 1);
@@ -2134,10 +2129,24 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
if (IS_ERR(ctrl->nand_base))
return PTR_ERR(ctrl->nand_base);
+ /* Enable clock before using NAND registers */
+ ctrl->clk = devm_clk_get(dev, "nand");
+ if (!IS_ERR(ctrl->clk)) {
+ ret = clk_prepare_enable(ctrl->clk);
+ if (ret)
+ return ret;
+ } else {
+ ret = PTR_ERR(ctrl->clk);
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
+ ctrl->clk = NULL;
+ }
+
/* Initialize NAND revision */
ret = brcmnand_revision_init(ctrl);
if (ret)
- return ret;
+ goto err;
/*
* Most chips have this cache at a fixed offset within 'nand' block.
@@ -2146,8 +2155,10 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
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);
+ if (IS_ERR(ctrl->nand_fc)) {
+ ret = PTR_ERR(ctrl->nand_fc);
+ goto err;
+ }
} else {
ctrl->nand_fc = ctrl->nand_base +
ctrl->reg_offsets[BRCMNAND_FC_BASE];
@@ -2157,8 +2168,10 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
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);
+ if (IS_ERR(ctrl->flash_dma_base)) {
+ ret = PTR_ERR(ctrl->flash_dma_base);
+ goto err;
+ }
flash_dma_writel(ctrl, FLASH_DMA_MODE, 1); /* linked-list */
flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);
@@ -2167,13 +2180,16 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
ctrl->dma_desc = dmam_alloc_coherent(dev,
sizeof(*ctrl->dma_desc),
&ctrl->dma_pa, GFP_KERNEL);
- if (!ctrl->dma_desc)
- return -ENOMEM;
+ if (!ctrl->dma_desc) {
+ ret = -ENOMEM;
+ goto err;
+ }
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 = -ENODEV;
+ goto err;
}
ret = devm_request_irq(dev, ctrl->dma_irq,
@@ -2182,7 +2198,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
if (ret < 0) {
dev_err(dev, "can't allocate IRQ %d: error %d\n",
ctrl->dma_irq, ret);
- return ret;
+ goto err;
}
dev_info(dev, "enabling FLASH_DMA\n");
@@ -2206,7 +2222,8 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
ctrl->irq = platform_get_irq(pdev, 0);
if ((int)ctrl->irq < 0) {
dev_err(dev, "no IRQ defined\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto err;
}
/*
@@ -2230,7 +2247,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
if (ret < 0) {
dev_err(dev, "can't allocate IRQ %d: error %d\n",
ctrl->irq, ret);
- return ret;
+ goto err;
}
for_each_available_child_of_node(dn, child) {
@@ -2238,25 +2255,36 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
struct brcmnand_host *host;
host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
- if (!host)
- return -ENOMEM;
+ if (!host) {
+ of_node_put(child);
+ ret = -ENOMEM;
+ goto err;
+ }
host->pdev = pdev;
host->ctrl = ctrl;
- host->of_node = child;
- ret = brcmnand_init_cs(host);
- if (ret)
+ ret = brcmnand_init_cs(host, child);
+ if (ret) {
+ devm_kfree(dev, host);
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;
+ if (list_empty(&ctrl->host_list)) {
+ ret = -ENODEV;
+ goto err;
+ }
return 0;
+
+err:
+ clk_disable_unprepare(ctrl->clk);
+ return ret;
+
}
EXPORT_SYMBOL_GPL(brcmnand_probe);
@@ -2266,7 +2294,9 @@ int brcmnand_remove(struct platform_device *pdev)
struct brcmnand_host *host;
list_for_each_entry(host, &ctrl->host_list, node)
- nand_release(&host->mtd);
+ nand_release(nand_to_mtd(&host->chip));
+
+ clk_disable_unprepare(ctrl->clk);
dev_set_drvdata(&pdev->dev, NULL);
diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c
index 9de78d2a2eb1..aa1a616b9fb6 100644
--- a/drivers/mtd/nand/cafe_nand.c
+++ b/drivers/mtd/nand/cafe_nand.c
@@ -101,7 +101,8 @@ static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
static int cafe_device_ready(struct mtd_info *mtd)
{
- struct cafe_priv *cafe = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
int result = !!(cafe_readl(cafe, NAND_STATUS) & 0x40000000);
uint32_t irqs = cafe_readl(cafe, NAND_IRQ);
@@ -117,7 +118,8 @@ static int cafe_device_ready(struct mtd_info *mtd)
static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
- struct cafe_priv *cafe = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
if (usedma)
memcpy(cafe->dmabuf + cafe->datalen, buf, len);
@@ -132,7 +134,8 @@ static void cafe_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
- struct cafe_priv *cafe = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
if (usedma)
memcpy(buf, cafe->dmabuf + cafe->datalen, len);
@@ -146,7 +149,8 @@ static void cafe_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
static uint8_t cafe_read_byte(struct mtd_info *mtd)
{
- struct cafe_priv *cafe = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
uint8_t d;
cafe_read_buf(mtd, &d, 1);
@@ -158,7 +162,8 @@ static uint8_t cafe_read_byte(struct mtd_info *mtd)
static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
int column, int page_addr)
{
- struct cafe_priv *cafe = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
int adrbytes = 0;
uint32_t ctl1;
uint32_t doneint = 0x80000000;
@@ -313,7 +318,8 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
static void cafe_select_chip(struct mtd_info *mtd, int chipnr)
{
- struct cafe_priv *cafe = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
cafe_dev_dbg(&cafe->pdev->dev, "select_chip %d\n", chipnr);
@@ -328,7 +334,8 @@ static void cafe_select_chip(struct mtd_info *mtd, int chipnr)
static irqreturn_t cafe_nand_interrupt(int irq, void *id)
{
struct mtd_info *mtd = id;
- struct cafe_priv *cafe = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
uint32_t irqs = cafe_readl(cafe, NAND_IRQ);
cafe_writel(cafe, irqs & ~0x90000000, NAND_IRQ);
if (!irqs)
@@ -377,7 +384,7 @@ static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
- struct cafe_priv *cafe = mtd->priv;
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
unsigned int max_bitflips = 0;
cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n",
@@ -519,7 +526,7 @@ static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
const uint8_t *buf, int oob_required,
int page)
{
- struct cafe_priv *cafe = mtd->priv;
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
chip->write_buf(mtd, buf, mtd->writesize);
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -598,13 +605,13 @@ static int cafe_nand_probe(struct pci_dev *pdev,
pci_set_master(pdev);
- mtd = kzalloc(sizeof(*mtd) + sizeof(struct cafe_priv), GFP_KERNEL);
- if (!mtd)
+ cafe = kzalloc(sizeof(*cafe), GFP_KERNEL);
+ if (!cafe)
return -ENOMEM;
- cafe = (void *)(&mtd[1]);
+ mtd = nand_to_mtd(&cafe->nand);
mtd->dev.parent = &pdev->dev;
- mtd->priv = cafe;
+ nand_set_controller_data(&cafe->nand, cafe);
cafe->pdev = pdev;
cafe->mmio = pci_iomap(pdev, 0, 0);
@@ -784,7 +791,7 @@ static int cafe_nand_probe(struct pci_dev *pdev,
out_ior:
pci_iounmap(pdev, cafe->mmio);
out_free_mtd:
- kfree(mtd);
+ kfree(cafe);
out:
return err;
}
@@ -792,7 +799,8 @@ static int cafe_nand_probe(struct pci_dev *pdev,
static void cafe_nand_remove(struct pci_dev *pdev)
{
struct mtd_info *mtd = pci_get_drvdata(pdev);
- struct cafe_priv *cafe = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
/* Disable NAND IRQ in global IRQ mask register */
cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
@@ -804,7 +812,7 @@ static void cafe_nand_remove(struct pci_dev *pdev)
2112 + sizeof(struct nand_buffers) +
mtd->writesize + mtd->oobsize,
cafe->dmabuf, cafe->dmaaddr);
- kfree(mtd);
+ kfree(cafe);
}
static const struct pci_device_id cafe_nand_tbl[] = {
@@ -819,7 +827,8 @@ static int cafe_nand_resume(struct pci_dev *pdev)
{
uint32_t ctrl;
struct mtd_info *mtd = pci_get_drvdata(pdev);
- struct cafe_priv *cafe = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct cafe_priv *cafe = nand_get_controller_data(chip);
/* Start off by resetting the NAND controller completely */
cafe_writel(cafe, 1, NAND_RESET);
diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c
index 66ec95e6ca6c..6f97ebba52c4 100644
--- a/drivers/mtd/nand/cmx270_nand.c
+++ b/drivers/mtd/nand/cmx270_nand.c
@@ -53,7 +53,7 @@ static struct mtd_partition partition_info[] = {
static u_char cmx270_read_byte(struct mtd_info *mtd)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
return (readl(this->IO_ADDR_R) >> 16);
}
@@ -61,7 +61,7 @@ static u_char cmx270_read_byte(struct mtd_info *mtd)
static void cmx270_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
for (i=0; i<len; i++)
writel((*buf++ << 16), this->IO_ADDR_W);
@@ -70,7 +70,7 @@ static void cmx270_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
static void cmx270_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
int i;
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
for (i=0; i<len; i++)
*buf++ = readl(this->IO_ADDR_R) >> 16;
@@ -94,7 +94,7 @@ static void nand_cs_off(void)
static void cmx270_hwcontrol(struct mtd_info *mtd, int dat,
unsigned int ctrl)
{
- struct nand_chip* this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
unsigned int nandaddr = (unsigned int)this->IO_ADDR_W;
dsb();
@@ -160,10 +160,8 @@ static int __init cmx270_init(void)
gpio_direction_input(GPIO_NAND_RB);
/* Allocate memory for MTD device structure and private data */
- cmx270_nand_mtd = kzalloc(sizeof(struct mtd_info) +
- sizeof(struct nand_chip),
- GFP_KERNEL);
- if (!cmx270_nand_mtd) {
+ this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
+ if (!this) {
ret = -ENOMEM;
goto err_kzalloc;
}
@@ -175,12 +173,10 @@ static int __init cmx270_init(void)
goto err_ioremap;
}
- /* Get pointer to private data */
- this = (struct nand_chip *)(&cmx270_nand_mtd[1]);
+ cmx270_nand_mtd = nand_to_mtd(this);
/* Link the private data with the MTD structure */
cmx270_nand_mtd->owner = THIS_MODULE;
- cmx270_nand_mtd->priv = this;
/* insert callbacks */
this->IO_ADDR_R = cmx270_nand_io;
@@ -216,7 +212,7 @@ static int __init cmx270_init(void)
err_scan:
iounmap(cmx270_nand_io);
err_ioremap:
- kfree(cmx270_nand_mtd);
+ kfree(this);
err_kzalloc:
gpio_free(GPIO_NAND_RB);
err_gpio_request:
@@ -240,8 +236,7 @@ static void __exit cmx270_cleanup(void)
iounmap(cmx270_nand_io);
- /* Free the MTD device structure */
- kfree (cmx270_nand_mtd);
+ kfree(mtd_to_nand(cmx270_nand_mtd));
}
module_exit(cmx270_cleanup);
diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c
index aec6045058c7..a65e4e0f57a1 100644
--- a/drivers/mtd/nand/cs553x_nand.c
+++ b/drivers/mtd/nand/cs553x_nand.c
@@ -97,7 +97,7 @@
static void cs553x_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
while (unlikely(len > 0x800)) {
memcpy_fromio(buf, this->IO_ADDR_R, 0x800);
@@ -109,7 +109,7 @@ static void cs553x_read_buf(struct mtd_info *mtd, u_char *buf, int len)
static void cs553x_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
while (unlikely(len > 0x800)) {
memcpy_toio(this->IO_ADDR_R, buf, 0x800);
@@ -121,13 +121,13 @@ static void cs553x_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
static unsigned char cs553x_read_byte(struct mtd_info *mtd)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
return readb(this->IO_ADDR_R);
}
static void cs553x_write_byte(struct mtd_info *mtd, u_char byte)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
int i = 100000;
while (i && readb(this->IO_ADDR_R + MM_NAND_STS) & CS_NAND_CTLR_BUSY) {
@@ -140,7 +140,7 @@ static void cs553x_write_byte(struct mtd_info *mtd, u_char byte)
static void cs553x_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
void __iomem *mmio_base = this->IO_ADDR_R;
if (ctrl & NAND_CTRL_CHANGE) {
unsigned char ctl = (ctrl & ~NAND_CTRL_CHANGE ) ^ 0x01;
@@ -152,7 +152,7 @@ static void cs553x_hwcontrol(struct mtd_info *mtd, int cmd,
static int cs553x_device_ready(struct mtd_info *mtd)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
void __iomem *mmio_base = this->IO_ADDR_R;
unsigned char foo = readb(mmio_base + MM_NAND_STS);
@@ -161,7 +161,7 @@ static int cs553x_device_ready(struct mtd_info *mtd)
static void cs_enable_hwecc(struct mtd_info *mtd, int mode)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
void __iomem *mmio_base = this->IO_ADDR_R;
writeb(0x07, mmio_base + MM_NAND_ECC_CTL);
@@ -170,7 +170,7 @@ static void cs_enable_hwecc(struct mtd_info *mtd, int mode)
static int cs_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
{
uint32_t ecc;
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
void __iomem *mmio_base = this->IO_ADDR_R;
ecc = readl(mmio_base + MM_NAND_STS);
@@ -197,17 +197,15 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
}
/* Allocate memory for MTD device structure and private data */
- new_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
- if (!new_mtd) {
+ this = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
+ if (!this) {
err = -ENOMEM;
goto out;
}
- /* Get pointer to private data */
- this = (struct nand_chip *)(&new_mtd[1]);
+ new_mtd = nand_to_mtd(this);
/* Link the private data with the MTD structure */
- new_mtd->priv = this;
new_mtd->owner = THIS_MODULE;
/* map physical address */
@@ -257,7 +255,7 @@ out_free:
out_ior:
iounmap(this->IO_ADDR_R);
out_mtd:
- kfree(new_mtd);
+ kfree(this);
out:
return err;
}
@@ -337,19 +335,19 @@ static void __exit cs553x_cleanup(void)
if (!mtd)
continue;
- this = cs553x_mtd[i]->priv;
+ this = mtd_to_nand(mtd);
mmio_base = this->IO_ADDR_R;
/* Release resources, unregister device */
- nand_release(cs553x_mtd[i]);
- kfree(cs553x_mtd[i]->name);
+ nand_release(mtd);
+ kfree(mtd->name);
cs553x_mtd[i] = NULL;
/* unmap physical address */
iounmap(mmio_base);
/* Free the MTD device structure */
- kfree(mtd);
+ kfree(this);
}
}
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
index c72313d66cf6..8cb821b6686e 100644
--- a/drivers/mtd/nand/davinci_nand.c
+++ b/drivers/mtd/nand/davinci_nand.c
@@ -53,7 +53,6 @@
* outputs in a "wire-AND" configuration, with no per-chip signals.
*/
struct davinci_nand_info {
- struct mtd_info mtd;
struct nand_chip chip;
struct nand_ecclayout ecclayout;
@@ -80,8 +79,10 @@ struct davinci_nand_info {
static DEFINE_SPINLOCK(davinci_nand_lock);
static bool ecc4_busy;
-#define to_davinci_nand(m) container_of(m, struct davinci_nand_info, mtd)
-
+static inline struct davinci_nand_info *to_davinci_nand(struct mtd_info *mtd)
+{
+ return container_of(mtd_to_nand(mtd), struct davinci_nand_info, chip);
+}
static inline unsigned int davinci_nand_readl(struct davinci_nand_info *info,
int offset)
@@ -106,7 +107,7 @@ static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd,
{
struct davinci_nand_info *info = to_davinci_nand(mtd);
uint32_t addr = info->current_cs;
- struct nand_chip *nand = mtd->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
/* Did the control lines change? */
if (ctrl & NAND_CTRL_CHANGE) {
@@ -192,7 +193,7 @@ static int nand_davinci_calculate_1bit(struct mtd_info *mtd,
static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
uint32_t eccNand = read_ecc[0] | (read_ecc[1] << 8) |
(read_ecc[2] << 16);
uint32_t eccCalc = calc_ecc[0] | (calc_ecc[1] << 8) |
@@ -206,7 +207,7 @@ static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat,
dat[diff >> (12 + 3)] ^= BIT((diff >> 12) & 7);
return 1;
} else {
- return -1;
+ return -EBADMSG;
}
} else if (!(diff & (diff - 1))) {
/* Single bit ECC error in the ECC itself,
@@ -214,7 +215,7 @@ static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat,
return 1;
} else {
/* Uncorrectable error */
- return -1;
+ return -EBADMSG;
}
}
@@ -316,14 +317,6 @@ static int nand_davinci_correct_4bit(struct mtd_info *mtd,
unsigned num_errors, corrected;
unsigned long timeo;
- /* All bytes 0xff? It's an erased page; ignore its ECC. */
- for (i = 0; i < 10; i++) {
- if (ecc_code[i] != 0xff)
- goto compare;
- }
- return 0;
-
-compare:
/* Unpack ten bytes into eight 10 bit values. We know we're
* little-endian, and use type punning for less shifting/masking.
*/
@@ -390,7 +383,7 @@ compare:
return 0;
case 1: /* five or more errors detected */
davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET);
- return -EIO;
+ return -EBADMSG;
case 2: /* error addresses computed */
case 3:
num_errors = 1 + ((fsr >> 16) & 0x03);
@@ -447,7 +440,7 @@ correct:
*/
static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0)
ioread32_rep(chip->IO_ADDR_R, buf, len >> 2);
@@ -460,7 +453,7 @@ static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
static void nand_davinci_write_buf(struct mtd_info *mtd,
const uint8_t *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0)
iowrite32_rep(chip->IO_ADDR_R, buf, len >> 2);
@@ -636,6 +629,7 @@ static int nand_davinci_probe(struct platform_device *pdev)
int ret;
uint32_t val;
nand_ecc_modes_t ecc_mode;
+ struct mtd_info *mtd;
pdata = nand_davinci_get_pdata(pdev);
if (IS_ERR(pdata))
@@ -682,8 +676,9 @@ static int nand_davinci_probe(struct platform_device *pdev)
info->base = base;
info->vaddr = vaddr;
- info->mtd.priv = &info->chip;
- info->mtd.dev.parent = &pdev->dev;
+ mtd = nand_to_mtd(&info->chip);
+ mtd->dev.parent = &pdev->dev;
+ nand_set_flash_node(&info->chip, pdev->dev.of_node);
info->chip.IO_ADDR_R = vaddr;
info->chip.IO_ADDR_W = vaddr;
@@ -746,6 +741,7 @@ static int nand_davinci_probe(struct platform_device *pdev)
info->chip.ecc.correct = nand_davinci_correct_4bit;
info->chip.ecc.hwctl = nand_davinci_hwctl_4bit;
info->chip.ecc.bytes = 10;
+ info->chip.ecc.options = NAND_ECC_GENERIC_ERASED_CHECK;
} else {
info->chip.ecc.calculate = nand_davinci_calculate_1bit;
info->chip.ecc.correct = nand_davinci_correct_1bit;
@@ -784,7 +780,7 @@ static int nand_davinci_probe(struct platform_device *pdev)
spin_unlock_irq(&davinci_nand_lock);
/* Scan to find existence of the device(s) */
- ret = nand_scan_ident(&info->mtd, pdata->mask_chipsel ? 2 : 1, NULL);
+ ret = nand_scan_ident(mtd, pdata->mask_chipsel ? 2 : 1, NULL);
if (ret < 0) {
dev_dbg(&pdev->dev, "no NAND chip(s) found\n");
goto err;
@@ -796,9 +792,9 @@ static int nand_davinci_probe(struct platform_device *pdev)
* usable: 10 bytes are needed, not 6.
*/
if (pdata->ecc_bits == 4) {
- int chunks = info->mtd.writesize / 512;
+ int chunks = mtd->writesize / 512;
- if (!chunks || info->mtd.oobsize < 16) {
+ if (!chunks || mtd->oobsize < 16) {
dev_dbg(&pdev->dev, "too small\n");
ret = -EINVAL;
goto err;
@@ -810,8 +806,7 @@ static int nand_davinci_probe(struct platform_device *pdev)
*/
if (chunks == 1) {
info->ecclayout = hwecc4_small;
- info->ecclayout.oobfree[1].length =
- info->mtd.oobsize - 16;
+ info->ecclayout.oobfree[1].length = mtd->oobsize - 16;
goto syndrome_done;
}
if (chunks == 4) {
@@ -832,20 +827,15 @@ syndrome_done:
info->chip.ecc.layout = &info->ecclayout;
}
- ret = nand_scan_tail(&info->mtd);
+ ret = nand_scan_tail(mtd);
if (ret < 0)
goto err;
if (pdata->parts)
- ret = mtd_device_parse_register(&info->mtd, NULL, NULL,
+ ret = mtd_device_parse_register(mtd, NULL, NULL,
pdata->parts, pdata->nr_parts);
- else {
- struct mtd_part_parser_data ppdata;
-
- ppdata.of_node = pdev->dev.of_node;
- ret = mtd_device_parse_register(&info->mtd, NULL, &ppdata,
- NULL, 0);
- }
+ else
+ ret = mtd_device_register(mtd, NULL, 0);
if (ret < 0)
goto err;
@@ -875,7 +865,7 @@ static int nand_davinci_remove(struct platform_device *pdev)
ecc4_busy = false;
spin_unlock_irq(&davinci_nand_lock);
- nand_release(&info->mtd);
+ nand_release(nand_to_mtd(&info->chip));
clk_disable_unprepare(info->clk);
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 67eb2be0db87..30bf5f690f78 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -75,7 +75,10 @@ MODULE_PARM_DESC(onfi_timing_mode,
* this macro allows us to convert from an MTD structure to our own
* device context (denali) structure.
*/
-#define mtd_to_denali(m) container_of(m, struct denali_nand_info, mtd)
+static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
+{
+ return container_of(mtd_to_nand(mtd), struct denali_nand_info, nand);
+}
/*
* These constants are defined by the driver to enable common driver
@@ -986,6 +989,8 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf,
* than one NAND connected.
*/
if (err_byte < ECC_SECTOR_SIZE) {
+ struct mtd_info *mtd =
+ nand_to_mtd(&denali->nand);
int offset;
offset = (err_sector *
@@ -995,7 +1000,7 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf,
err_device;
/* correct the ECC error */
buf[offset] ^= err_correction_value;
- denali->mtd.ecc_stats.corrected++;
+ mtd->ecc_stats.corrected++;
bitflips++;
}
} else {
@@ -1062,7 +1067,7 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
dma_addr_t addr = denali->buf.dma_buf;
- size_t size = denali->mtd.writesize + denali->mtd.oobsize;
+ size_t size = mtd->writesize + mtd->oobsize;
uint32_t irq_status;
uint32_t irq_mask = INTR_STATUS__DMA_CMD_COMP |
INTR_STATUS__PROGRAM_FAIL;
@@ -1160,7 +1165,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
struct denali_nand_info *denali = mtd_to_denali(mtd);
dma_addr_t addr = denali->buf.dma_buf;
- size_t size = denali->mtd.writesize + denali->mtd.oobsize;
+ size_t size = mtd->writesize + mtd->oobsize;
uint32_t irq_status;
uint32_t irq_mask = INTR_STATUS__ECC_TRANSACTION_DONE |
@@ -1193,14 +1198,14 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
denali_enable_dma(denali, false);
if (check_erased_page) {
- read_oob_data(&denali->mtd, chip->oob_poi, denali->page);
+ read_oob_data(mtd, chip->oob_poi, denali->page);
/* check ECC failures that may have occurred on erased pages */
if (check_erased_page) {
- if (!is_erased(buf, denali->mtd.writesize))
- denali->mtd.ecc_stats.failed++;
- if (!is_erased(buf, denali->mtd.oobsize))
- denali->mtd.ecc_stats.failed++;
+ if (!is_erased(buf, mtd->writesize))
+ mtd->ecc_stats.failed++;
+ if (!is_erased(buf, mtd->oobsize))
+ mtd->ecc_stats.failed++;
}
}
return max_bitflips;
@@ -1211,7 +1216,7 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
dma_addr_t addr = denali->buf.dma_buf;
- size_t size = denali->mtd.writesize + denali->mtd.oobsize;
+ size_t size = mtd->writesize + mtd->oobsize;
uint32_t irq_mask = INTR_STATUS__DMA_CMD_COMP;
if (page != denali->page) {
@@ -1428,6 +1433,7 @@ static void denali_drv_init(struct denali_nand_info *denali)
int denali_init(struct denali_nand_info *denali)
{
+ struct mtd_info *mtd = nand_to_mtd(&denali->nand);
int ret;
if (denali->platform == INTEL_CE4100) {
@@ -1447,7 +1453,7 @@ int denali_init(struct denali_nand_info *denali)
if (!denali->buf.buf)
return -ENOMEM;
- denali->mtd.dev.parent = denali->dev;
+ mtd->dev.parent = denali->dev;
denali_hw_init(denali);
denali_drv_init(denali);
@@ -1463,8 +1469,7 @@ int denali_init(struct denali_nand_info *denali)
/* now that our ISR is registered, we can enable interrupts */
denali_set_intr_modes(denali, true);
- denali->mtd.name = "denali-nand";
- denali->mtd.priv = &denali->nand;
+ mtd->name = "denali-nand";
/* register the driver with the NAND core subsystem */
denali->nand.select_chip = denali_select_chip;
@@ -1477,7 +1482,7 @@ int denali_init(struct denali_nand_info *denali)
* this is the first stage in a two step process to register
* with the nand subsystem
*/
- if (nand_scan_ident(&denali->mtd, denali->max_banks, NULL)) {
+ if (nand_scan_ident(mtd, denali->max_banks, NULL)) {
ret = -ENXIO;
goto failed_req_irq;
}
@@ -1485,7 +1490,7 @@ int denali_init(struct denali_nand_info *denali)
/* allocate the right size buffer now */
devm_kfree(denali->dev, denali->buf.buf);
denali->buf.buf = devm_kzalloc(denali->dev,
- denali->mtd.writesize + denali->mtd.oobsize,
+ mtd->writesize + mtd->oobsize,
GFP_KERNEL);
if (!denali->buf.buf) {
ret = -ENOMEM;
@@ -1500,7 +1505,7 @@ int denali_init(struct denali_nand_info *denali)
}
denali->buf.dma_buf = dma_map_single(denali->dev, denali->buf.buf,
- denali->mtd.writesize + denali->mtd.oobsize,
+ mtd->writesize + mtd->oobsize,
DMA_BIDIRECTIONAL);
if (dma_mapping_error(denali->dev, denali->buf.dma_buf)) {
dev_err(denali->dev, "Spectra: failed to map DMA buffer\n");
@@ -1521,10 +1526,10 @@ int denali_init(struct denali_nand_info *denali)
denali->nand.bbt_erase_shift += (denali->devnum - 1);
denali->nand.phys_erase_shift = denali->nand.bbt_erase_shift;
denali->nand.chip_shift += (denali->devnum - 1);
- denali->mtd.writesize <<= (denali->devnum - 1);
- denali->mtd.oobsize <<= (denali->devnum - 1);
- denali->mtd.erasesize <<= (denali->devnum - 1);
- denali->mtd.size = denali->nand.numchips * denali->nand.chipsize;
+ mtd->writesize <<= (denali->devnum - 1);
+ mtd->oobsize <<= (denali->devnum - 1);
+ mtd->erasesize <<= (denali->devnum - 1);
+ mtd->size = denali->nand.numchips * denali->nand.chipsize;
denali->bbtskipbytes *= denali->devnum;
/*
@@ -1551,16 +1556,16 @@ int denali_init(struct denali_nand_info *denali)
* SLC if possible.
* */
if (!nand_is_slc(&denali->nand) &&
- (denali->mtd.oobsize > (denali->bbtskipbytes +
- ECC_15BITS * (denali->mtd.writesize /
+ (mtd->oobsize > (denali->bbtskipbytes +
+ ECC_15BITS * (mtd->writesize /
ECC_SECTOR_SIZE)))) {
/* if MLC OOB size is large enough, use 15bit ECC*/
denali->nand.ecc.strength = 15;
denali->nand.ecc.layout = &nand_15bit_oob;
denali->nand.ecc.bytes = ECC_15BITS;
iowrite32(15, denali->flash_reg + ECC_CORRECTION);
- } else if (denali->mtd.oobsize < (denali->bbtskipbytes +
- ECC_8BITS * (denali->mtd.writesize /
+ } else if (mtd->oobsize < (denali->bbtskipbytes +
+ ECC_8BITS * (mtd->writesize /
ECC_SECTOR_SIZE))) {
pr_err("Your NAND chip OOB is not large enough to contain 8bit ECC correction codes");
goto failed_req_irq;
@@ -1574,11 +1579,11 @@ int denali_init(struct denali_nand_info *denali)
denali->nand.ecc.bytes *= denali->devnum;
denali->nand.ecc.strength *= denali->devnum;
denali->nand.ecc.layout->eccbytes *=
- denali->mtd.writesize / ECC_SECTOR_SIZE;
+ mtd->writesize / ECC_SECTOR_SIZE;
denali->nand.ecc.layout->oobfree[0].offset =
denali->bbtskipbytes + denali->nand.ecc.layout->eccbytes;
denali->nand.ecc.layout->oobfree[0].length =
- denali->mtd.oobsize - denali->nand.ecc.layout->eccbytes -
+ mtd->oobsize - denali->nand.ecc.layout->eccbytes -
denali->bbtskipbytes;
/*
@@ -1586,7 +1591,7 @@ int denali_init(struct denali_nand_info *denali)
* contained by each nand chip. blksperchip will help driver to
* know how many blocks is taken by FW.
*/
- denali->totalblks = denali->mtd.size >> denali->nand.phys_erase_shift;
+ denali->totalblks = mtd->size >> denali->nand.phys_erase_shift;
denali->blksperchip = denali->totalblks / denali->nand.numchips;
/* override the default read operations */
@@ -1599,12 +1604,12 @@ int denali_init(struct denali_nand_info *denali)
denali->nand.ecc.write_oob = denali_write_oob;
denali->nand.erase = denali_erase;
- if (nand_scan_tail(&denali->mtd)) {
+ if (nand_scan_tail(mtd)) {
ret = -ENXIO;
goto failed_req_irq;
}
- ret = mtd_device_register(&denali->mtd, NULL, 0);
+ ret = mtd_device_register(mtd, NULL, 0);
if (ret) {
dev_err(denali->dev, "Spectra: Failed to register MTD: %d\n",
ret);
@@ -1622,9 +1627,17 @@ EXPORT_SYMBOL(denali_init);
/* driver exit point */
void denali_remove(struct denali_nand_info *denali)
{
+ struct mtd_info *mtd = nand_to_mtd(&denali->nand);
+ /*
+ * Pre-compute DMA buffer size to avoid any problems in case
+ * nand_release() ever changes in a way that mtd->writesize and
+ * mtd->oobsize are not reliable after this call.
+ */
+ int bufsize = mtd->writesize + mtd->oobsize;
+
+ nand_release(mtd);
denali_irq_cleanup(denali->irq, denali);
- dma_unmap_single(denali->dev, denali->buf.dma_buf,
- denali->mtd.writesize + denali->mtd.oobsize,
+ dma_unmap_single(denali->dev, denali->buf.dma_buf, bufsize,
DMA_BIDIRECTIONAL);
}
EXPORT_SYMBOL(denali_remove);
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 4b12cd302819..e7ab4866a5da 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -450,7 +450,6 @@ struct nand_buf {
#define DT 3
struct denali_nand_info {
- struct mtd_info mtd;
struct nand_chip nand;
int flash_bank; /* currently selected chip */
int status;
diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c
index 0802158a3f75..f170f3c31b34 100644
--- a/drivers/mtd/nand/diskonchip.c
+++ b/drivers/mtd/nand/diskonchip.c
@@ -74,10 +74,6 @@ struct doc_priv {
int (*late_init)(struct mtd_info *mtd);
};
-/* This is the syndrome computed by the HW ecc generator upon reading an empty
- page, one with all 0xff for data and stored ecc code. */
-static u_char empty_read_syndrome[6] = { 0x26, 0xff, 0x6d, 0x47, 0x73, 0x7a };
-
/* This is the ecc value computed by the HW ecc generator upon writing an empty
page, one with all 0xff for data. */
static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 };
@@ -299,8 +295,8 @@ static inline int DoC_WaitReady(struct doc_priv *doc)
static void doc2000_write_byte(struct mtd_info *mtd, u_char datum)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
if (debug)
@@ -311,8 +307,8 @@ static void doc2000_write_byte(struct mtd_info *mtd, u_char datum)
static u_char doc2000_read_byte(struct mtd_info *mtd)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
u_char ret;
@@ -326,8 +322,8 @@ static u_char doc2000_read_byte(struct mtd_info *mtd)
static void doc2000_writebuf(struct mtd_info *mtd, const u_char *buf, int len)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
int i;
if (debug)
@@ -343,8 +339,8 @@ static void doc2000_writebuf(struct mtd_info *mtd, const u_char *buf, int len)
static void doc2000_readbuf(struct mtd_info *mtd, u_char *buf, int len)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
int i;
@@ -358,8 +354,8 @@ static void doc2000_readbuf(struct mtd_info *mtd, u_char *buf, int len)
static void doc2000_readbuf_dword(struct mtd_info *mtd, u_char *buf, int len)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
int i;
@@ -379,8 +375,8 @@ static void doc2000_readbuf_dword(struct mtd_info *mtd, u_char *buf, int len)
static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
uint16_t ret;
doc200x_select_chip(mtd, nr);
@@ -425,8 +421,8 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
static void __init doc2000_count_chips(struct mtd_info *mtd)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
uint16_t mfrid;
int i;
@@ -447,7 +443,7 @@ static void __init doc2000_count_chips(struct mtd_info *mtd)
static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this)
{
- struct doc_priv *doc = this->priv;
+ struct doc_priv *doc = nand_get_controller_data(this);
int status;
@@ -461,8 +457,8 @@ static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this)
static void doc2001_write_byte(struct mtd_info *mtd, u_char datum)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
WriteDOC(datum, docptr, CDSNSlowIO);
@@ -472,8 +468,8 @@ static void doc2001_write_byte(struct mtd_info *mtd, u_char datum)
static u_char doc2001_read_byte(struct mtd_info *mtd)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
//ReadDOC(docptr, CDSNSlowIO);
@@ -486,8 +482,8 @@ static u_char doc2001_read_byte(struct mtd_info *mtd)
static void doc2001_writebuf(struct mtd_info *mtd, const u_char *buf, int len)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
int i;
@@ -499,8 +495,8 @@ static void doc2001_writebuf(struct mtd_info *mtd, const u_char *buf, int len)
static void doc2001_readbuf(struct mtd_info *mtd, u_char *buf, int len)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
int i;
@@ -516,8 +512,8 @@ static void doc2001_readbuf(struct mtd_info *mtd, u_char *buf, int len)
static u_char doc2001plus_read_byte(struct mtd_info *mtd)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
u_char ret;
@@ -531,8 +527,8 @@ static u_char doc2001plus_read_byte(struct mtd_info *mtd)
static void doc2001plus_writebuf(struct mtd_info *mtd, const u_char *buf, int len)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
int i;
@@ -549,8 +545,8 @@ static void doc2001plus_writebuf(struct mtd_info *mtd, const u_char *buf, int le
static void doc2001plus_readbuf(struct mtd_info *mtd, u_char *buf, int len)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
int i;
@@ -580,8 +576,8 @@ static void doc2001plus_readbuf(struct mtd_info *mtd, u_char *buf, int len)
static void doc2001plus_select_chip(struct mtd_info *mtd, int chip)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
int floor = 0;
@@ -607,8 +603,8 @@ static void doc2001plus_select_chip(struct mtd_info *mtd, int chip)
static void doc200x_select_chip(struct mtd_info *mtd, int chip)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
int floor = 0;
@@ -638,8 +634,8 @@ static void doc200x_select_chip(struct mtd_info *mtd, int chip)
static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
if (ctrl & NAND_CTRL_CHANGE) {
@@ -661,8 +657,8 @@ static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd,
static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int column, int page_addr)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
/*
@@ -767,8 +763,8 @@ static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int colu
static int doc200x_dev_ready(struct mtd_info *mtd)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
if (DoC_is_MillenniumPlus(doc)) {
@@ -807,8 +803,8 @@ static int doc200x_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
/* Prime the ECC engine */
@@ -826,8 +822,8 @@ static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode)
static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
/* Prime the ECC engine */
@@ -846,8 +842,8 @@ static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode)
/* This code is only called on write */
static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, unsigned char *ecc_code)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
int i;
int emptymatch = 1;
@@ -907,12 +903,11 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *isnull)
{
int i, ret = 0;
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
void __iomem *docptr = doc->virtadr;
uint8_t calc_ecc[6];
volatile u_char dummy;
- int emptymatch = 1;
/* flush the pipeline */
if (DoC_is_2000(doc)) {
@@ -936,37 +931,9 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat,
calc_ecc[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i);
else
calc_ecc[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
- if (calc_ecc[i] != empty_read_syndrome[i])
- emptymatch = 0;
- }
- /* If emptymatch=1, the read syndrome is consistent with an
- all-0xff data and stored ecc block. Check the stored ecc. */
- if (emptymatch) {
- for (i = 0; i < 6; i++) {
- if (read_ecc[i] == 0xff)
- continue;
- emptymatch = 0;
- break;
- }
}
- /* If emptymatch still =1, check the data block. */
- if (emptymatch) {
- /* Note: this somewhat expensive test should not be triggered
- often. It could be optimized away by examining the data in
- the readbuf routine, and remembering the result. */
- for (i = 0; i < 512; i++) {
- if (dat[i] == 0xff)
- continue;
- emptymatch = 0;
- break;
- }
- }
- /* If emptymatch still =1, this is almost certainly a freshly-
- erased block, in which case the ECC will not come out right.
- We'll suppress the error and tell the caller everything's
- OK. Because it is. */
- if (!emptymatch)
- ret = doc_ecc_decode(rs_decoder, dat, calc_ecc);
+
+ ret = doc_ecc_decode(rs_decoder, dat, calc_ecc);
if (ret > 0)
printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret);
}
@@ -1007,8 +974,8 @@ static struct nand_ecclayout doc200x_oobinfo = {
mh1_page in the DOC private structure. */
static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const char *id, int findmirror)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
unsigned offs;
int ret;
size_t retlen;
@@ -1050,8 +1017,8 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const ch
static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
int ret = 0;
u_char *buf;
struct NFTLMediaHeader *mh;
@@ -1152,8 +1119,8 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partitio
/* This is a stripped-down copy of the code in inftlmount.c */
static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
int ret = 0;
u_char *buf;
struct INFTLMediaHeader *mh;
@@ -1272,8 +1239,8 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partiti
static int __init nftl_scan_bbt(struct mtd_info *mtd)
{
int ret, numparts;
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
struct mtd_partition parts[2];
memset((char *)parts, 0, sizeof(parts));
@@ -1307,8 +1274,8 @@ static int __init nftl_scan_bbt(struct mtd_info *mtd)
static int __init inftl_scan_bbt(struct mtd_info *mtd)
{
int ret, numparts;
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
struct mtd_partition parts[5];
if (this->numchips > doc->chips_per_floor) {
@@ -1360,8 +1327,8 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)
static inline int __init doc2000_init(struct mtd_info *mtd)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
this->read_byte = doc2000_read_byte;
this->write_buf = doc2000_writebuf;
@@ -1376,8 +1343,8 @@ static inline int __init doc2000_init(struct mtd_info *mtd)
static inline int __init doc2001_init(struct mtd_info *mtd)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
this->read_byte = doc2001_read_byte;
this->write_buf = doc2001_writebuf;
@@ -1406,8 +1373,8 @@ static inline int __init doc2001_init(struct mtd_info *mtd)
static inline int __init doc2001plus_init(struct mtd_info *mtd)
{
- struct nand_chip *this = mtd->priv;
- struct doc_priv *doc = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct doc_priv *doc = nand_get_controller_data(this);
this->read_byte = doc2001plus_read_byte;
this->write_buf = doc2001plus_writebuf;
@@ -1523,8 +1490,8 @@ static int __init doc_probe(unsigned long physadr)
for (mtd = doclist; mtd; mtd = doc->nextdoc) {
unsigned char oldval;
unsigned char newval;
- nand = mtd->priv;
- doc = nand->priv;
+ nand = mtd_to_nand(mtd);
+ doc = nand_get_controller_data(nand);
/* Use the alias resolution register to determine if this is
in fact the same DOC aliased to a new address. If writes
to one chip's alias resolution register change the value on
@@ -1556,23 +1523,22 @@ static int __init doc_probe(unsigned long physadr)
printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr);
- len = sizeof(struct mtd_info) +
- sizeof(struct nand_chip) + sizeof(struct doc_priv) + (2 * sizeof(struct nand_bbt_descr));
- mtd = kzalloc(len, GFP_KERNEL);
- if (!mtd) {
+ len = sizeof(struct nand_chip) + sizeof(struct doc_priv) +
+ (2 * sizeof(struct nand_bbt_descr));
+ nand = kzalloc(len, GFP_KERNEL);
+ if (!nand) {
ret = -ENOMEM;
goto fail;
}
- nand = (struct nand_chip *) (mtd + 1);
+ mtd = nand_to_mtd(nand);
doc = (struct doc_priv *) (nand + 1);
nand->bbt_td = (struct nand_bbt_descr *) (doc + 1);
nand->bbt_md = nand->bbt_td + 1;
- mtd->priv = nand;
mtd->owner = THIS_MODULE;
- nand->priv = doc;
+ nand_set_controller_data(nand, doc);
nand->select_chip = doc200x_select_chip;
nand->cmd_ctrl = doc200x_hwcontrol;
nand->dev_ready = doc200x_dev_ready;
@@ -1587,6 +1553,7 @@ static int __init doc_probe(unsigned long physadr)
nand->ecc.size = 512;
nand->ecc.bytes = 6;
nand->ecc.strength = 2;
+ nand->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK;
nand->bbt_options = NAND_BBT_USE_FLASH;
/* Skip the automatic BBT scan so we can run it manually */
nand->options |= NAND_SKIP_BBTSCAN;
@@ -1615,7 +1582,7 @@ static int __init doc_probe(unsigned long physadr)
haven't yet added it. This is handled without incident by
mtd_device_unregister, as far as I can tell. */
nand_release(mtd);
- kfree(mtd);
+ kfree(nand);
goto fail;
}
@@ -1643,14 +1610,14 @@ static void release_nanddoc(void)
struct doc_priv *doc;
for (mtd = doclist; mtd; mtd = nextmtd) {
- nand = mtd->priv;
- doc = nand->priv;
+ nand = mtd_to_nand(mtd);
+ doc = nand_get_controller_data(nand);
nextmtd = doc->nextdoc;
nand_release(mtd);
iounmap(doc->virtadr);
release_mem_region(doc->physadr, DOC_IOREMAP_LEN);
- kfree(mtd);
+ kfree(nand);
}
}
diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c
index 408cf69b854b..df4165b02c62 100644
--- a/drivers/mtd/nand/docg4.c
+++ b/drivers/mtd/nand/docg4.c
@@ -242,7 +242,7 @@ static inline void write_nop(void __iomem *docptr)
static void docg4_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
int i;
- struct nand_chip *nand = mtd->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
uint16_t *p = (uint16_t *) buf;
len >>= 1;
@@ -253,7 +253,7 @@ static void docg4_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
static void docg4_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
{
int i;
- struct nand_chip *nand = mtd->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
uint16_t *p = (uint16_t *) buf;
len >>= 1;
@@ -297,7 +297,7 @@ static int poll_status(struct docg4_priv *doc)
static int docg4_wait(struct mtd_info *mtd, struct nand_chip *nand)
{
- struct docg4_priv *doc = nand->priv;
+ struct docg4_priv *doc = nand_get_controller_data(nand);
int status = NAND_STATUS_WP; /* inverse logic?? */
dev_dbg(doc->dev, "%s...\n", __func__);
@@ -318,8 +318,8 @@ static void docg4_select_chip(struct mtd_info *mtd, int chip)
* Select among multiple cascaded chips ("floors"). Multiple floors are
* not yet supported, so the only valid non-negative value is 0.
*/
- struct nand_chip *nand = mtd->priv;
- struct docg4_priv *doc = nand->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
void __iomem *docptr = doc->virtadr;
dev_dbg(doc->dev, "%s: chip %d\n", __func__, chip);
@@ -337,8 +337,8 @@ static void reset(struct mtd_info *mtd)
{
/* full device reset */
- struct nand_chip *nand = mtd->priv;
- struct docg4_priv *doc = nand->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
void __iomem *docptr = doc->virtadr;
writew(DOC_ASICMODE_RESET | DOC_ASICMODE_MDWREN,
@@ -375,8 +375,8 @@ static int correct_data(struct mtd_info *mtd, uint8_t *buf, int page)
* Up to four bitflips can be corrected.
*/
- struct nand_chip *nand = mtd->priv;
- struct docg4_priv *doc = nand->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
void __iomem *docptr = doc->virtadr;
int i, numerrs, errpos[4];
const uint8_t blank_read_hwecc[8] = {
@@ -464,8 +464,8 @@ static int correct_data(struct mtd_info *mtd, uint8_t *buf, int page)
static uint8_t docg4_read_byte(struct mtd_info *mtd)
{
- struct nand_chip *nand = mtd->priv;
- struct docg4_priv *doc = nand->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
dev_dbg(doc->dev, "%s\n", __func__);
@@ -545,8 +545,8 @@ static int pageprog(struct mtd_info *mtd)
* internal buffer out to the flash array, or some such.
*/
- struct nand_chip *nand = mtd->priv;
- struct docg4_priv *doc = nand->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
void __iomem *docptr = doc->virtadr;
int retval = 0;
@@ -582,8 +582,8 @@ static void sequence_reset(struct mtd_info *mtd)
{
/* common starting sequence for all operations */
- struct nand_chip *nand = mtd->priv;
- struct docg4_priv *doc = nand->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
void __iomem *docptr = doc->virtadr;
writew(DOC_CTRL_UNKNOWN | DOC_CTRL_CE, docptr + DOC_FLASHCONTROL);
@@ -599,8 +599,8 @@ static void read_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr)
{
/* first step in reading a page */
- struct nand_chip *nand = mtd->priv;
- struct docg4_priv *doc = nand->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
void __iomem *docptr = doc->virtadr;
dev_dbg(doc->dev,
@@ -626,8 +626,8 @@ static void write_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr)
{
/* first step in writing a page */
- struct nand_chip *nand = mtd->priv;
- struct docg4_priv *doc = nand->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
void __iomem *docptr = doc->virtadr;
dev_dbg(doc->dev,
@@ -691,8 +691,8 @@ static void docg4_command(struct mtd_info *mtd, unsigned command, int column,
{
/* handle standard nand commands */
- struct nand_chip *nand = mtd->priv;
- struct docg4_priv *doc = nand->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
uint32_t g4_addr = mtd_to_docg4_address(page_addr, column);
dev_dbg(doc->dev, "%s %x, page_addr=%x, column=%x\n",
@@ -756,7 +756,7 @@ static void docg4_command(struct mtd_info *mtd, unsigned command, int column,
static int read_page(struct mtd_info *mtd, struct nand_chip *nand,
uint8_t *buf, int page, bool use_ecc)
{
- struct docg4_priv *doc = nand->priv;
+ struct docg4_priv *doc = nand_get_controller_data(nand);
void __iomem *docptr = doc->virtadr;
uint16_t status, edc_err, *buf16;
int bits_corrected = 0;
@@ -836,7 +836,7 @@ static int docg4_read_page(struct mtd_info *mtd, struct nand_chip *nand,
static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand,
int page)
{
- struct docg4_priv *doc = nand->priv;
+ struct docg4_priv *doc = nand_get_controller_data(nand);
void __iomem *docptr = doc->virtadr;
uint16_t status;
@@ -874,8 +874,8 @@ static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand,
static int docg4_erase_block(struct mtd_info *mtd, int page)
{
- struct nand_chip *nand = mtd->priv;
- struct docg4_priv *doc = nand->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
void __iomem *docptr = doc->virtadr;
uint16_t g4_page;
@@ -923,7 +923,7 @@ static int docg4_erase_block(struct mtd_info *mtd, int page)
static int write_page(struct mtd_info *mtd, struct nand_chip *nand,
const uint8_t *buf, bool use_ecc)
{
- struct docg4_priv *doc = nand->priv;
+ struct docg4_priv *doc = nand_get_controller_data(nand);
void __iomem *docptr = doc->virtadr;
uint8_t ecc_buf[8];
@@ -1003,7 +1003,7 @@ static int docg4_write_oob(struct mtd_info *mtd, struct nand_chip *nand,
*/
/* note that bytes 7..14 are hw generated hamming/ecc and overwritten */
- struct docg4_priv *doc = nand->priv;
+ struct docg4_priv *doc = nand_get_controller_data(nand);
doc->oob_page = page;
memcpy(doc->oob_buf, nand->oob_poi, 16);
return 0;
@@ -1016,8 +1016,8 @@ static int __init read_factory_bbt(struct mtd_info *mtd)
* update the memory-based bbt accordingly.
*/
- struct nand_chip *nand = mtd->priv;
- struct docg4_priv *doc = nand->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
uint32_t g4_addr = mtd_to_docg4_address(DOCG4_FACTORY_BBT_PAGE, 0);
uint8_t *buf;
int i, block;
@@ -1089,8 +1089,8 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs)
int ret, i;
uint8_t *buf;
- struct nand_chip *nand = mtd->priv;
- struct docg4_priv *doc = nand->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
struct nand_bbt_descr *bbtd = nand->badblock_pattern;
int page = (int)(ofs >> nand->page_shift);
uint32_t g4_addr = mtd_to_docg4_address(page, 0);
@@ -1202,8 +1202,8 @@ static void __init init_mtd_structs(struct mtd_info *mtd)
* things as well, such as call nand_set_defaults().
*/
- struct nand_chip *nand = mtd->priv;
- struct docg4_priv *doc = nand->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
mtd->size = DOCG4_CHIP_SIZE;
mtd->name = "Msys_Diskonchip_G4";
@@ -1261,8 +1261,8 @@ static void __init init_mtd_structs(struct mtd_info *mtd)
static int __init read_id_reg(struct mtd_info *mtd)
{
- struct nand_chip *nand = mtd->priv;
- struct docg4_priv *doc = nand->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct docg4_priv *doc = nand_get_controller_data(nand);
void __iomem *docptr = doc->virtadr;
uint16_t id1, id2;
@@ -1305,17 +1305,16 @@ static int __init probe_docg4(struct platform_device *pdev)
return -EIO;
}
- len = sizeof(struct mtd_info) + sizeof(struct nand_chip) +
- sizeof(struct docg4_priv);
- mtd = kzalloc(len, GFP_KERNEL);
- if (mtd == NULL) {
+ len = sizeof(struct nand_chip) + sizeof(struct docg4_priv);
+ nand = kzalloc(len, GFP_KERNEL);
+ if (nand == NULL) {
retval = -ENOMEM;
- goto fail;
+ goto fail_unmap;
}
- nand = (struct nand_chip *) (mtd + 1);
+
+ mtd = nand_to_mtd(nand);
doc = (struct docg4_priv *) (nand + 1);
- mtd->priv = nand;
- nand->priv = doc;
+ nand_set_controller_data(nand, doc);
mtd->dev.parent = &pdev->dev;
doc->virtadr = virtadr;
doc->dev = dev;
@@ -1353,16 +1352,13 @@ static int __init probe_docg4(struct platform_device *pdev)
doc->mtd = mtd;
return 0;
- fail:
+fail:
+ nand_release(mtd); /* deletes partitions and mtd devices */
+ free_bch(doc->bch);
+ kfree(nand);
+
+fail_unmap:
iounmap(virtadr);
- if (mtd) {
- /* re-declarations avoid compiler warning */
- struct nand_chip *nand = mtd->priv;
- struct docg4_priv *doc = nand->priv;
- nand_release(mtd); /* deletes partitions and mtd devices */
- free_bch(doc->bch);
- kfree(mtd);
- }
return retval;
}
@@ -1372,7 +1368,7 @@ static int __exit cleanup_docg4(struct platform_device *pdev)
struct docg4_priv *doc = platform_get_drvdata(pdev);
nand_release(doc->mtd);
free_bch(doc->bch);
- kfree(doc->mtd);
+ kfree(mtd_to_nand(doc->mtd));
iounmap(doc->virtadr);
return 0;
}
diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c
index dcb1f7f4873f..059d5f7ec124 100644
--- a/drivers/mtd/nand/fsl_elbc_nand.c
+++ b/drivers/mtd/nand/fsl_elbc_nand.c
@@ -48,7 +48,6 @@
/* mtd information per set */
struct fsl_elbc_mtd {
- struct mtd_info mtd;
struct nand_chip chip;
struct fsl_lbc_ctrl *ctrl;
@@ -144,8 +143,8 @@ static struct nand_bbt_descr bbt_mirror_descr = {
*/
static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
{
- struct nand_chip *chip = mtd->priv;
- struct fsl_elbc_mtd *priv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
struct fsl_lbc_ctrl *ctrl = priv->ctrl;
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
@@ -195,8 +194,8 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
*/
static int fsl_elbc_run_command(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct fsl_elbc_mtd *priv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
struct fsl_lbc_ctrl *ctrl = priv->ctrl;
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
@@ -268,7 +267,7 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
static void fsl_elbc_do_read(struct nand_chip *chip, int oob)
{
- struct fsl_elbc_mtd *priv = chip->priv;
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
struct fsl_lbc_ctrl *ctrl = priv->ctrl;
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
@@ -300,8 +299,8 @@ static void fsl_elbc_do_read(struct nand_chip *chip, int oob)
static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
int column, int page_addr)
{
- struct nand_chip *chip = mtd->priv;
- struct fsl_elbc_mtd *priv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
struct fsl_lbc_ctrl *ctrl = priv->ctrl;
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
@@ -525,8 +524,8 @@ static void fsl_elbc_select_chip(struct mtd_info *mtd, int chip)
*/
static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
- struct fsl_elbc_mtd *priv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
unsigned int bufsize = mtd->writesize + mtd->oobsize;
@@ -563,8 +562,8 @@ static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
*/
static u8 fsl_elbc_read_byte(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct fsl_elbc_mtd *priv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
/* If there are still bytes in the FCM, then use the next byte. */
@@ -580,8 +579,8 @@ static u8 fsl_elbc_read_byte(struct mtd_info *mtd)
*/
static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
- struct fsl_elbc_mtd *priv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
int avail;
@@ -605,7 +604,7 @@ static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
*/
static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip)
{
- struct fsl_elbc_mtd *priv = chip->priv;
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
if (elbc_fcm_ctrl->status != LTESR_CC)
@@ -619,8 +618,8 @@ static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip)
static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct fsl_elbc_mtd *priv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
struct fsl_lbc_ctrl *ctrl = priv->ctrl;
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
unsigned int al;
@@ -697,7 +696,7 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
- struct fsl_elbc_mtd *priv = chip->priv;
+ struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
struct fsl_lbc_ctrl *ctrl = priv->ctrl;
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
@@ -742,12 +741,13 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
struct nand_chip *chip = &priv->chip;
+ struct mtd_info *mtd = nand_to_mtd(chip);
dev_dbg(priv->dev, "eLBC Set Information for bank %d\n", priv->bank);
/* Fill in fsl_elbc_mtd structure */
- priv->mtd.priv = chip;
- priv->mtd.dev.parent = priv->dev;
+ mtd->dev.parent = priv->dev;
+ nand_set_flash_node(chip, priv->dev->of_node);
/* set timeout to maximum */
priv->fmr = 15 << FMR_CWTO_SHIFT;
@@ -770,7 +770,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
chip->bbt_options = NAND_BBT_USE_FLASH;
chip->controller = &elbc_fcm_ctrl->controller;
- chip->priv = priv;
+ nand_set_controller_data(chip, priv);
chip->ecc.read_page = fsl_elbc_read_page;
chip->ecc.write_page = fsl_elbc_write_page;
@@ -797,9 +797,11 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
{
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
- nand_release(&priv->mtd);
+ struct mtd_info *mtd = nand_to_mtd(&priv->chip);
- kfree(priv->mtd.name);
+ nand_release(mtd);
+
+ kfree(mtd->name);
if (priv->vbase)
iounmap(priv->vbase);
@@ -823,9 +825,8 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev)
int bank;
struct device *dev;
struct device_node *node = pdev->dev.of_node;
- struct mtd_part_parser_data ppdata;
+ struct mtd_info *mtd;
- ppdata.of_node = pdev->dev.of_node;
if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
return -ENODEV;
lbc = fsl_lbc_ctrl_dev->regs;
@@ -887,8 +888,9 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev)
goto err;
}
- priv->mtd.name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start);
- if (!priv->mtd.name) {
+ mtd = nand_to_mtd(&priv->chip);
+ mtd->name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start);
+ if (!nand_to_mtd(&priv->chip)->name) {
ret = -ENOMEM;
goto err;
}
@@ -897,21 +899,21 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev)
if (ret)
goto err;
- ret = nand_scan_ident(&priv->mtd, 1, NULL);
+ ret = nand_scan_ident(mtd, 1, NULL);
if (ret)
goto err;
- ret = fsl_elbc_chip_init_tail(&priv->mtd);
+ ret = fsl_elbc_chip_init_tail(mtd);
if (ret)
goto err;
- ret = nand_scan_tail(&priv->mtd);
+ ret = nand_scan_tail(mtd);
if (ret)
goto err;
/* First look for RedBoot table or partitions on the command
* line, these take precedence over device tree information */
- mtd_device_parse_register(&priv->mtd, part_probe_types, &ppdata,
+ mtd_device_parse_register(mtd, part_probe_types, NULL,
NULL, 0);
printk(KERN_INFO "eLBC NAND device at 0x%llx, bank %d\n",
diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c
index 7f4ac8c19001..43f5a3a4873f 100644
--- a/drivers/mtd/nand/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/fsl_ifc_nand.c
@@ -40,7 +40,6 @@ struct fsl_ifc_ctrl;
/* mtd information per set */
struct fsl_ifc_mtd {
- struct mtd_info mtd;
struct nand_chip chip;
struct fsl_ifc_ctrl *ctrl;
@@ -230,8 +229,8 @@ static struct nand_bbt_descr bbt_mirror_descr = {
*/
static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
{
- struct nand_chip *chip = mtd->priv;
- struct fsl_ifc_mtd *priv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
int buf_num;
@@ -253,8 +252,8 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
static int is_blank(struct mtd_info *mtd, unsigned int bufnum)
{
- struct nand_chip *chip = mtd->priv;
- struct fsl_ifc_mtd *priv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
u8 __iomem *addr = priv->vbase + bufnum * (mtd->writesize * 2);
u32 __iomem *mainarea = (u32 __iomem *)addr;
u8 __iomem *oob = addr + mtd->writesize;
@@ -292,8 +291,8 @@ static int check_read_ecc(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl,
*/
static void fsl_ifc_run_command(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct fsl_ifc_mtd *priv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl;
struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
@@ -370,7 +369,7 @@ static void fsl_ifc_do_read(struct nand_chip *chip,
int oob,
struct mtd_info *mtd)
{
- struct fsl_ifc_mtd *priv = chip->priv;
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
@@ -409,8 +408,8 @@ static void fsl_ifc_do_read(struct nand_chip *chip,
/* cmdfunc send commands to the IFC NAND Machine */
static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
int column, int page_addr) {
- struct nand_chip *chip = mtd->priv;
- struct fsl_ifc_mtd *priv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
@@ -624,8 +623,8 @@ static void fsl_ifc_select_chip(struct mtd_info *mtd, int chip)
*/
static void fsl_ifc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
- struct fsl_ifc_mtd *priv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
unsigned int bufsize = mtd->writesize + mtd->oobsize;
if (len <= 0) {
@@ -650,8 +649,8 @@ static void fsl_ifc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
*/
static uint8_t fsl_ifc_read_byte(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct fsl_ifc_mtd *priv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
unsigned int offset;
/*
@@ -673,8 +672,8 @@ static uint8_t fsl_ifc_read_byte(struct mtd_info *mtd)
*/
static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct fsl_ifc_mtd *priv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
uint16_t data;
/*
@@ -696,8 +695,8 @@ static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd)
*/
static void fsl_ifc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
- struct fsl_ifc_mtd *priv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
int avail;
if (len < 0) {
@@ -722,7 +721,7 @@ static void fsl_ifc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
*/
static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip)
{
- struct fsl_ifc_mtd *priv = chip->priv;
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
u32 nand_fsr;
@@ -751,7 +750,7 @@ static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip)
static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
- struct fsl_ifc_mtd *priv = chip->priv;
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl;
@@ -782,8 +781,8 @@ static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
static int fsl_ifc_chip_init_tail(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct fsl_ifc_mtd *priv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsl_ifc_mtd *priv = nand_get_controller_data(chip);
dev_dbg(priv->dev, "%s: nand->numchips = %d\n", __func__,
chip->numchips);
@@ -877,12 +876,13 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
struct fsl_ifc_ctrl *ctrl = priv->ctrl;
struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
struct nand_chip *chip = &priv->chip;
+ struct mtd_info *mtd = nand_to_mtd(&priv->chip);
struct nand_ecclayout *layout;
u32 csor;
/* Fill in fsl_ifc_mtd structure */
- priv->mtd.priv = chip;
- priv->mtd.dev.parent = priv->dev;
+ mtd->dev.parent = priv->dev;
+ nand_set_flash_node(chip, priv->dev->of_node);
/* fill in nand_chip structure */
/* set up function call table */
@@ -914,7 +914,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
}
chip->controller = &ifc_nand_ctrl->controller;
- chip->priv = priv;
+ nand_set_controller_data(chip, priv);
chip->ecc.read_page = fsl_ifc_read_page;
chip->ecc.write_page = fsl_ifc_write_page;
@@ -993,9 +993,11 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv)
{
- nand_release(&priv->mtd);
+ struct mtd_info *mtd = nand_to_mtd(&priv->chip);
- kfree(priv->mtd.name);
+ nand_release(mtd);
+
+ kfree(mtd->name);
if (priv->vbase)
iounmap(priv->vbase);
@@ -1030,9 +1032,8 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
int ret;
int bank;
struct device_node *node = dev->dev.of_node;
- struct mtd_part_parser_data ppdata;
+ struct mtd_info *mtd;
- ppdata.of_node = dev->dev.of_node;
if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs)
return -ENODEV;
ifc = fsl_ifc_ctrl_dev->regs;
@@ -1104,8 +1105,10 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
IFC_NAND_EVTER_INTR_FTOERIR_EN |
IFC_NAND_EVTER_INTR_WPERIR_EN,
&ifc->ifc_nand.nand_evter_intr_en);
- priv->mtd.name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start);
- if (!priv->mtd.name) {
+
+ mtd = nand_to_mtd(&priv->chip);
+ mtd->name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start);
+ if (!mtd->name) {
ret = -ENOMEM;
goto err;
}
@@ -1114,22 +1117,21 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
if (ret)
goto err;
- ret = nand_scan_ident(&priv->mtd, 1, NULL);
+ ret = nand_scan_ident(mtd, 1, NULL);
if (ret)
goto err;
- ret = fsl_ifc_chip_init_tail(&priv->mtd);
+ ret = fsl_ifc_chip_init_tail(mtd);
if (ret)
goto err;
- ret = nand_scan_tail(&priv->mtd);
+ ret = nand_scan_tail(mtd);
if (ret)
goto err;
/* First look for RedBoot table or partitions on the command
* line, these take precedence over device tree information */
- mtd_device_parse_register(&priv->mtd, part_probe_types, &ppdata,
- NULL, 0);
+ mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0);
dev_info(priv->dev, "IFC NAND device at 0x%llx, bank %d\n",
(unsigned long long)res.start, priv->bank);
diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c
index d326369980c4..cafd12de7276 100644
--- a/drivers/mtd/nand/fsl_upm.c
+++ b/drivers/mtd/nand/fsl_upm.c
@@ -31,7 +31,6 @@
struct fsl_upm_nand {
struct device *dev;
- struct mtd_info mtd;
struct nand_chip chip;
int last_ctrl;
struct mtd_partition *parts;
@@ -49,7 +48,8 @@ struct fsl_upm_nand {
static inline struct fsl_upm_nand *to_fsl_upm_nand(struct mtd_info *mtdinfo)
{
- return container_of(mtdinfo, struct fsl_upm_nand, mtd);
+ return container_of(mtd_to_nand(mtdinfo), struct fsl_upm_nand,
+ chip);
}
static int fun_chip_ready(struct mtd_info *mtd)
@@ -66,9 +66,10 @@ static int fun_chip_ready(struct mtd_info *mtd)
static void fun_wait_rnb(struct fsl_upm_nand *fun)
{
if (fun->rnb_gpio[fun->mchip_number] >= 0) {
+ struct mtd_info *mtd = nand_to_mtd(&fun->chip);
int cnt = 1000000;
- while (--cnt && !fun_chip_ready(&fun->mtd))
+ while (--cnt && !fun_chip_ready(mtd))
cpu_relax();
if (!cnt)
dev_err(fun->dev, "tired waiting for RNB\n");
@@ -79,7 +80,7 @@ static void fun_wait_rnb(struct fsl_upm_nand *fun)
static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
u32 mar;
@@ -109,7 +110,7 @@ static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
static void fun_select_chip(struct mtd_info *mtd, int mchip_nr)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
if (mchip_nr == -1) {
@@ -157,9 +158,9 @@ static int fun_chip_init(struct fsl_upm_nand *fun,
const struct device_node *upm_np,
const struct resource *io_res)
{
+ struct mtd_info *mtd = nand_to_mtd(&fun->chip);
int ret;
struct device_node *flash_np;
- struct mtd_part_parser_data ppdata;
fun->chip.IO_ADDR_R = fun->io_base;
fun->chip.IO_ADDR_W = fun->io_base;
@@ -175,30 +176,29 @@ static int fun_chip_init(struct fsl_upm_nand *fun,
if (fun->rnb_gpio[0] >= 0)
fun->chip.dev_ready = fun_chip_ready;
- fun->mtd.priv = &fun->chip;
- fun->mtd.dev.parent = fun->dev;
+ mtd->dev.parent = fun->dev;
flash_np = of_get_next_child(upm_np, NULL);
if (!flash_np)
return -ENODEV;
- fun->mtd.name = kasprintf(GFP_KERNEL, "0x%llx.%s", (u64)io_res->start,
- flash_np->name);
- if (!fun->mtd.name) {
+ nand_set_flash_node(&fun->chip, flash_np);
+ mtd->name = kasprintf(GFP_KERNEL, "0x%llx.%s", (u64)io_res->start,
+ flash_np->name);
+ if (!mtd->name) {
ret = -ENOMEM;
goto err;
}
- ret = nand_scan(&fun->mtd, fun->mchip_count);
+ ret = nand_scan(mtd, fun->mchip_count);
if (ret)
goto err;
- ppdata.of_node = flash_np;
- ret = mtd_device_parse_register(&fun->mtd, NULL, &ppdata, NULL, 0);
+ ret = mtd_device_register(mtd, NULL, 0);
err:
of_node_put(flash_np);
if (ret)
- kfree(fun->mtd.name);
+ kfree(mtd->name);
return ret;
}
@@ -322,10 +322,11 @@ err1:
static int fun_remove(struct platform_device *ofdev)
{
struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev);
+ struct mtd_info *mtd = nand_to_mtd(&fun->chip);
int i;
- nand_release(&fun->mtd);
- kfree(fun->mtd.name);
+ nand_release(mtd);
+ kfree(mtd->name);
for (i = 0; i < fun->mchip_count; i++) {
if (fun->rnb_gpio[i] < 0)
diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c
index 07af3dc7a4d2..1bdcd4fa26d4 100644
--- a/drivers/mtd/nand/fsmc_nand.c
+++ b/drivers/mtd/nand/fsmc_nand.c
@@ -299,7 +299,6 @@ static struct fsmc_eccplace fsmc_ecc4_sp_place = {
*/
struct fsmc_nand_data {
u32 pid;
- struct mtd_info mtd;
struct nand_chip nand;
struct mtd_partition *partitions;
unsigned int nr_partitions;
@@ -326,13 +325,18 @@ struct fsmc_nand_data {
void (*select_chip)(uint32_t bank, uint32_t busw);
};
+static inline struct fsmc_nand_data *mtd_to_fsmc(struct mtd_info *mtd)
+{
+ return container_of(mtd_to_nand(mtd), struct fsmc_nand_data, nand);
+}
+
/* Assert CS signal based on chipnr */
static void fsmc_select_chip(struct mtd_info *mtd, int chipnr)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
struct fsmc_nand_data *host;
- host = container_of(mtd, struct fsmc_nand_data, mtd);
+ host = mtd_to_fsmc(mtd);
switch (chipnr) {
case -1:
@@ -358,9 +362,8 @@ static void fsmc_select_chip(struct mtd_info *mtd, int chipnr)
*/
static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
- struct nand_chip *this = mtd->priv;
- struct fsmc_nand_data *host = container_of(mtd,
- struct fsmc_nand_data, mtd);
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
void __iomem *regs = host->regs_va;
unsigned int bank = host->bank;
@@ -445,8 +448,7 @@ static void fsmc_nand_setup(void __iomem *regs, uint32_t bank,
*/
static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode)
{
- struct fsmc_nand_data *host = container_of(mtd,
- struct fsmc_nand_data, mtd);
+ struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
void __iomem *regs = host->regs_va;
uint32_t bank = host->bank;
@@ -466,8 +468,7 @@ static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode)
static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data,
uint8_t *ecc)
{
- struct fsmc_nand_data *host = container_of(mtd,
- struct fsmc_nand_data, mtd);
+ struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
void __iomem *regs = host->regs_va;
uint32_t bank = host->bank;
uint32_t ecc_tmp;
@@ -517,8 +518,7 @@ static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data,
static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data,
uint8_t *ecc)
{
- struct fsmc_nand_data *host = container_of(mtd,
- struct fsmc_nand_data, mtd);
+ struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
void __iomem *regs = host->regs_va;
uint32_t bank = host->bank;
uint32_t ecc_tmp;
@@ -629,7 +629,7 @@ unmap_dma:
static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
int i;
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) &&
IS_ALIGNED(len, sizeof(uint32_t))) {
@@ -652,7 +652,7 @@ static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
int i;
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
if (IS_ALIGNED((uint32_t)buf, sizeof(uint32_t)) &&
IS_ALIGNED(len, sizeof(uint32_t))) {
@@ -674,9 +674,8 @@ static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
*/
static void fsmc_read_buf_dma(struct mtd_info *mtd, uint8_t *buf, int len)
{
- struct fsmc_nand_data *host;
+ struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
- host = container_of(mtd, struct fsmc_nand_data, mtd);
dma_xfer(host, buf, len, DMA_FROM_DEVICE);
}
@@ -689,9 +688,8 @@ static void fsmc_read_buf_dma(struct mtd_info *mtd, uint8_t *buf, int len)
static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf,
int len)
{
- struct fsmc_nand_data *host;
+ struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
- host = container_of(mtd, struct fsmc_nand_data, mtd);
dma_xfer(host, (void *)buf, len, DMA_TO_DEVICE);
}
@@ -712,8 +710,7 @@ static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf,
static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
- struct fsmc_nand_data *host = container_of(mtd,
- struct fsmc_nand_data, mtd);
+ struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
struct fsmc_eccplace *ecc_place = host->ecc_place;
int i, j, s, stat, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
@@ -782,9 +779,8 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
static int fsmc_bch8_correct_data(struct mtd_info *mtd, uint8_t *dat,
uint8_t *read_ecc, uint8_t *calc_ecc)
{
- struct fsmc_nand_data *host = container_of(mtd,
- struct fsmc_nand_data, mtd);
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
void __iomem *regs = host->regs_va;
unsigned int bank = host->bank;
uint32_t err_idx[8];
@@ -926,7 +922,6 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
{
struct fsmc_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct device_node __maybe_unused *np = pdev->dev.of_node;
- struct mtd_part_parser_data ppdata = {};
struct fsmc_nand_data *host;
struct mtd_info *mtd;
struct nand_chip *nand;
@@ -1012,12 +1007,12 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
init_completion(&host->dma_access_complete);
/* Link all private pointers */
- mtd = &host->mtd;
+ mtd = nand_to_mtd(&host->nand);
nand = &host->nand;
- mtd->priv = nand;
- nand->priv = host;
+ nand_set_controller_data(nand, host);
+ nand_set_flash_node(nand, np);
- host->mtd.dev.parent = &pdev->dev;
+ mtd->dev.parent = &pdev->dev;
nand->IO_ADDR_R = host->data_va;
nand->IO_ADDR_W = host->data_va;
nand->cmd_ctrl = fsmc_cmd_ctrl;
@@ -1033,7 +1028,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
nand->options = pdata->options;
nand->select_chip = fsmc_select_chip;
nand->badblockbits = 7;
- nand->flash_node = np;
+ nand_set_flash_node(nand, np);
if (pdata->width == FSMC_NAND_BW16)
nand->options |= NAND_BUSWIDTH_16;
@@ -1080,14 +1075,14 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
/*
* Scan to find existence of the device
*/
- if (nand_scan_ident(&host->mtd, 1, NULL)) {
+ if (nand_scan_ident(mtd, 1, NULL)) {
ret = -ENXIO;
dev_err(&pdev->dev, "No NAND Device found!\n");
goto err_scan_ident;
}
if (AMBA_REV_BITS(host->pid) >= 8) {
- switch (host->mtd.oobsize) {
+ switch (mtd->oobsize) {
case 16:
nand->ecc.layout = &fsmc_ecc4_16_layout;
host->ecc_place = &fsmc_ecc4_sp_place;
@@ -1138,7 +1133,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
* generated later in nand_bch_init() later.
*/
if (nand->ecc.mode != NAND_ECC_SOFT_BCH) {
- switch (host->mtd.oobsize) {
+ switch (mtd->oobsize) {
case 16:
nand->ecc.layout = &fsmc_ecc1_16_layout;
break;
@@ -1159,7 +1154,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
}
/* Second stage of scan to fill MTD data-structures */
- if (nand_scan_tail(&host->mtd)) {
+ if (nand_scan_tail(mtd)) {
ret = -ENXIO;
goto err_probe;
}
@@ -1174,10 +1169,8 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
/*
* Check for partition info passed
*/
- host->mtd.name = "nand";
- ppdata.of_node = np;
- ret = mtd_device_parse_register(&host->mtd, NULL, &ppdata,
- host->partitions, host->nr_partitions);
+ mtd->name = "nand";
+ ret = mtd_device_register(mtd, host->partitions, host->nr_partitions);
if (ret)
goto err_probe;
@@ -1207,7 +1200,7 @@ static int fsmc_nand_remove(struct platform_device *pdev)
struct fsmc_nand_data *host = platform_get_drvdata(pdev);
if (host) {
- nand_release(&host->mtd);
+ nand_release(nand_to_mtd(&host->nand));
if (host->mode == USE_DMA_ACCESS) {
dma_release_channel(host->write_dma_chan);
diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c
index 9ab97f934c37..ded658fc7d73 100644
--- a/drivers/mtd/nand/gpio.c
+++ b/drivers/mtd/nand/gpio.c
@@ -35,12 +35,14 @@
struct gpiomtd {
void __iomem *io_sync;
- struct mtd_info mtd_info;
struct nand_chip nand_chip;
struct gpio_nand_platdata plat;
};
-#define gpio_nand_getpriv(x) container_of(x, struct gpiomtd, mtd_info)
+static inline struct gpiomtd *gpio_nand_getpriv(struct mtd_info *mtd)
+{
+ return container_of(mtd_to_nand(mtd), struct gpiomtd, nand_chip);
+}
#ifdef CONFIG_ARM
@@ -195,7 +197,7 @@ static int gpio_nand_remove(struct platform_device *pdev)
{
struct gpiomtd *gpiomtd = platform_get_drvdata(pdev);
- nand_release(&gpiomtd->mtd_info);
+ nand_release(nand_to_mtd(&gpiomtd->nand_chip));
if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
gpio_set_value(gpiomtd->plat.gpio_nwp, 0);
@@ -208,8 +210,8 @@ static int gpio_nand_probe(struct platform_device *pdev)
{
struct gpiomtd *gpiomtd;
struct nand_chip *chip;
+ struct mtd_info *mtd;
struct resource *res;
- struct mtd_part_parser_data ppdata = {};
int ret = 0;
if (!pdev->dev.of_node && !dev_get_platdata(&pdev->dev))
@@ -268,33 +270,31 @@ static int gpio_nand_probe(struct platform_device *pdev)
chip->dev_ready = gpio_nand_devready;
}
+ nand_set_flash_node(chip, pdev->dev.of_node);
chip->IO_ADDR_W = chip->IO_ADDR_R;
chip->ecc.mode = NAND_ECC_SOFT;
chip->options = gpiomtd->plat.options;
chip->chip_delay = gpiomtd->plat.chip_delay;
chip->cmd_ctrl = gpio_nand_cmd_ctrl;
- gpiomtd->mtd_info.priv = chip;
- gpiomtd->mtd_info.dev.parent = &pdev->dev;
+ mtd = nand_to_mtd(chip);
+ mtd->dev.parent = &pdev->dev;
platform_set_drvdata(pdev, gpiomtd);
if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
gpio_direction_output(gpiomtd->plat.gpio_nwp, 1);
- if (nand_scan(&gpiomtd->mtd_info, 1)) {
+ if (nand_scan(mtd, 1)) {
ret = -ENXIO;
goto err_wp;
}
if (gpiomtd->plat.adjust_parts)
- gpiomtd->plat.adjust_parts(&gpiomtd->plat,
- gpiomtd->mtd_info.size);
+ gpiomtd->plat.adjust_parts(&gpiomtd->plat, mtd->size);
- ppdata.of_node = pdev->dev.of_node;
- ret = mtd_device_parse_register(&gpiomtd->mtd_info, NULL, &ppdata,
- gpiomtd->plat.parts,
- gpiomtd->plat.num_parts);
+ ret = mtd_device_register(mtd, gpiomtd->plat.parts,
+ gpiomtd->plat.num_parts);
if (!ret)
return 0;
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
index 43fa16b5f510..0f68a99fc4ad 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
@@ -919,7 +919,7 @@ static int enable_edo_mode(struct gpmi_nand_data *this, int mode)
{
struct resources *r = &this->resources;
struct nand_chip *nand = &this->nand;
- struct mtd_info *mtd = &this->mtd;
+ struct mtd_info *mtd = nand_to_mtd(nand);
uint8_t *feature;
unsigned long rate;
int ret;
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
index 2064adac1d17..235ddcb58f39 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
@@ -107,7 +107,7 @@ static irqreturn_t bch_irq(int irq, void *cookie)
static inline int get_ecc_strength(struct gpmi_nand_data *this)
{
struct bch_geometry *geo = &this->bch_geometry;
- struct mtd_info *mtd = &this->mtd;
+ struct mtd_info *mtd = nand_to_mtd(&this->nand);
int ecc_strength;
ecc_strength = ((mtd->oobsize - geo->metadata_size) * 8)
@@ -139,8 +139,8 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this)
{
struct bch_geometry *geo = &this->bch_geometry;
- struct mtd_info *mtd = &this->mtd;
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = &this->nand;
+ struct mtd_info *mtd = nand_to_mtd(chip);
struct nand_oobfree *of = gpmi_hw_ecclayout.oobfree;
unsigned int block_mark_bit_offset;
@@ -257,7 +257,7 @@ static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this)
static int legacy_set_geometry(struct gpmi_nand_data *this)
{
struct bch_geometry *geo = &this->bch_geometry;
- struct mtd_info *mtd = &this->mtd;
+ struct mtd_info *mtd = nand_to_mtd(&this->nand);
unsigned int metadata_size;
unsigned int status_size;
unsigned int block_mark_bit_offset;
@@ -804,7 +804,7 @@ static int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this)
{
struct bch_geometry *geo = &this->bch_geometry;
struct device *dev = this->dev;
- struct mtd_info *mtd = &this->mtd;
+ struct mtd_info *mtd = nand_to_mtd(&this->nand);
/* [1] Allocate a command buffer. PAGE_SIZE is enough. */
this->cmd_buffer = kzalloc(PAGE_SIZE, GFP_DMA | GFP_KERNEL);
@@ -856,8 +856,8 @@ error_alloc:
static void gpmi_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl)
{
- struct nand_chip *chip = mtd->priv;
- struct gpmi_nand_data *this = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
int ret;
/*
@@ -890,16 +890,16 @@ static void gpmi_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl)
static int gpmi_dev_ready(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct gpmi_nand_data *this = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
return gpmi_is_ready(this, this->current_chip);
}
static void gpmi_select_chip(struct mtd_info *mtd, int chipnr)
{
- struct nand_chip *chip = mtd->priv;
- struct gpmi_nand_data *this = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
if ((this->current_chip < 0) && (chipnr >= 0))
gpmi_begin(this);
@@ -911,8 +911,8 @@ static void gpmi_select_chip(struct mtd_info *mtd, int chipnr)
static void gpmi_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
- struct gpmi_nand_data *this = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
dev_dbg(this->dev, "len is %d\n", len);
this->upper_buf = buf;
@@ -923,8 +923,8 @@ static void gpmi_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
- struct gpmi_nand_data *this = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
dev_dbg(this->dev, "len is %d\n", len);
this->upper_buf = (uint8_t *)buf;
@@ -935,8 +935,8 @@ static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
static uint8_t gpmi_read_byte(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct gpmi_nand_data *this = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
uint8_t *buf = this->data_buffer_dma;
gpmi_read_buf(mtd, buf, 1);
@@ -994,7 +994,7 @@ static void block_mark_swapping(struct gpmi_nand_data *this,
static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
- struct gpmi_nand_data *this = chip->priv;
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
struct bch_geometry *nfc_geo = &this->bch_geometry;
void *payload_virt;
dma_addr_t payload_phys;
@@ -1074,7 +1074,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t offs, uint32_t len, uint8_t *buf, int page)
{
- struct gpmi_nand_data *this = chip->priv;
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
void __iomem *bch_regs = this->resources.bch_regs;
struct bch_geometry old_geo = this->bch_geometry;
struct bch_geometry *geo = &this->bch_geometry;
@@ -1162,7 +1162,7 @@ static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required, int page)
{
- struct gpmi_nand_data *this = chip->priv;
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
struct bch_geometry *nfc_geo = &this->bch_geometry;
const void *payload_virt;
dma_addr_t payload_phys;
@@ -1298,7 +1298,7 @@ exit_auxiliary:
static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
- struct gpmi_nand_data *this = chip->priv;
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
dev_dbg(this->dev, "page number is %d\n", page);
/* clear the OOB buffer */
@@ -1359,7 +1359,7 @@ static int gpmi_ecc_read_page_raw(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf,
int oob_required, int page)
{
- struct gpmi_nand_data *this = chip->priv;
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
struct bch_geometry *nfc_geo = &this->bch_geometry;
int eccsize = nfc_geo->ecc_chunk_size;
int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
@@ -1448,7 +1448,7 @@ static int gpmi_ecc_write_page_raw(struct mtd_info *mtd,
const uint8_t *buf,
int oob_required, int page)
{
- struct gpmi_nand_data *this = chip->priv;
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
struct bch_geometry *nfc_geo = &this->bch_geometry;
int eccsize = nfc_geo->ecc_chunk_size;
int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
@@ -1538,8 +1538,8 @@ static int gpmi_ecc_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
- struct nand_chip *chip = mtd->priv;
- struct gpmi_nand_data *this = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct gpmi_nand_data *this = nand_get_controller_data(chip);
int ret = 0;
uint8_t *block_mark;
int column, page, status, chipnr;
@@ -1600,8 +1600,8 @@ static int mx23_check_transcription_stamp(struct gpmi_nand_data *this)
{
struct boot_rom_geometry *rom_geo = &this->rom_geometry;
struct device *dev = this->dev;
- struct mtd_info *mtd = &this->mtd;
struct nand_chip *chip = &this->nand;
+ struct mtd_info *mtd = nand_to_mtd(chip);
unsigned int search_area_size_in_strides;
unsigned int stride;
unsigned int page;
@@ -1655,8 +1655,8 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this)
{
struct device *dev = this->dev;
struct boot_rom_geometry *rom_geo = &this->rom_geometry;
- struct mtd_info *mtd = &this->mtd;
struct nand_chip *chip = &this->nand;
+ struct mtd_info *mtd = nand_to_mtd(chip);
unsigned int block_size_in_pages;
unsigned int search_area_size_in_strides;
unsigned int search_area_size_in_pages;
@@ -1735,7 +1735,7 @@ static int mx23_boot_init(struct gpmi_nand_data *this)
{
struct device *dev = this->dev;
struct nand_chip *chip = &this->nand;
- struct mtd_info *mtd = &this->mtd;
+ struct mtd_info *mtd = nand_to_mtd(chip);
unsigned int block_count;
unsigned int block;
int chipnr;
@@ -1831,14 +1831,13 @@ static int gpmi_set_geometry(struct gpmi_nand_data *this)
static void gpmi_nand_exit(struct gpmi_nand_data *this)
{
- nand_release(&this->mtd);
+ nand_release(nand_to_mtd(&this->nand));
gpmi_free_dma_buffer(this);
}
static int gpmi_init_last(struct gpmi_nand_data *this)
{
- struct mtd_info *mtd = &this->mtd;
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = &this->nand;
struct nand_ecc_ctrl *ecc = &chip->ecc;
struct bch_geometry *bch_geo = &this->bch_geometry;
int ret;
@@ -1886,21 +1885,20 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
static int gpmi_nand_init(struct gpmi_nand_data *this)
{
- struct mtd_info *mtd = &this->mtd;
struct nand_chip *chip = &this->nand;
- struct mtd_part_parser_data ppdata = {};
+ struct mtd_info *mtd = nand_to_mtd(chip);
int ret;
/* init current chip */
this->current_chip = -1;
/* init the MTD data structures */
- mtd->priv = chip;
mtd->name = "gpmi-nand";
mtd->dev.parent = this->dev;
/* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */
- chip->priv = this;
+ nand_set_controller_data(chip, this);
+ nand_set_flash_node(chip, this->pdev->dev.of_node);
chip->select_chip = gpmi_select_chip;
chip->cmd_ctrl = gpmi_cmd_ctrl;
chip->dev_ready = gpmi_dev_ready;
@@ -1954,8 +1952,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
if (ret)
goto err_out;
- ppdata.of_node = this->pdev->dev.of_node;
- ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+ ret = mtd_device_register(mtd, NULL, 0);
if (ret)
goto err_out;
return 0;
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
index 544062f65020..4e49a1f5fa27 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
@@ -160,7 +160,6 @@ struct gpmi_nand_data {
/* MTD / NAND */
struct nand_chip nand;
- struct mtd_info mtd;
/* General-use Variables */
int current_chip;
diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/hisi504_nand.c
index 0cb2e886937d..f8d37f36a81c 100644
--- a/drivers/mtd/nand/hisi504_nand.c
+++ b/drivers/mtd/nand/hisi504_nand.c
@@ -134,7 +134,6 @@
struct hinfc_host {
struct nand_chip chip;
- struct mtd_info mtd;
struct device *dev;
void __iomem *iobase;
void __iomem *mmio;
@@ -189,8 +188,8 @@ static void wait_controller_finished(struct hinfc_host *host)
static void hisi_nfc_dma_transfer(struct hinfc_host *host, int todev)
{
- struct mtd_info *mtd = &host->mtd;
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = &host->chip;
+ struct mtd_info *mtd = nand_to_mtd(chip);
unsigned long val;
int ret;
@@ -262,7 +261,7 @@ static int hisi_nfc_send_cmd_pageprog(struct hinfc_host *host)
static int hisi_nfc_send_cmd_readstart(struct hinfc_host *host)
{
- struct mtd_info *mtd = &host->mtd;
+ struct mtd_info *mtd = nand_to_mtd(&host->chip);
if ((host->addr_value[0] == host->cache_addr_value[0]) &&
(host->addr_value[1] == host->cache_addr_value[1]))
@@ -357,8 +356,8 @@ static int hisi_nfc_send_cmd_reset(struct hinfc_host *host, int chipselect)
static void hisi_nfc_select_chip(struct mtd_info *mtd, int chipselect)
{
- struct nand_chip *chip = mtd->priv;
- struct hinfc_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct hinfc_host *host = nand_get_controller_data(chip);
if (chipselect < 0)
return;
@@ -368,8 +367,8 @@ static void hisi_nfc_select_chip(struct mtd_info *mtd, int chipselect)
static uint8_t hisi_nfc_read_byte(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct hinfc_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct hinfc_host *host = nand_get_controller_data(chip);
if (host->command == NAND_CMD_STATUS)
return *(uint8_t *)(host->mmio);
@@ -384,8 +383,8 @@ static uint8_t hisi_nfc_read_byte(struct mtd_info *mtd)
static u16 hisi_nfc_read_word(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct hinfc_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct hinfc_host *host = nand_get_controller_data(chip);
host->offset += 2;
return *(u16 *)(host->buffer + host->offset - 2);
@@ -394,8 +393,8 @@ static u16 hisi_nfc_read_word(struct mtd_info *mtd)
static void
hisi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
- struct hinfc_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct hinfc_host *host = nand_get_controller_data(chip);
memcpy(host->buffer + host->offset, buf, len);
host->offset += len;
@@ -403,8 +402,8 @@ hisi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
static void hisi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
- struct hinfc_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct hinfc_host *host = nand_get_controller_data(chip);
memcpy(buf, host->buffer + host->offset, len);
host->offset += len;
@@ -412,8 +411,8 @@ static void hisi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
static void set_addr(struct mtd_info *mtd, int column, int page_addr)
{
- struct nand_chip *chip = mtd->priv;
- struct hinfc_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct hinfc_host *host = nand_get_controller_data(chip);
unsigned int command = host->command;
host->addr_cycle = 0;
@@ -448,8 +447,8 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr)
static void hisi_nfc_cmdfunc(struct mtd_info *mtd, unsigned command, int column,
int page_addr)
{
- struct nand_chip *chip = mtd->priv;
- struct hinfc_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct hinfc_host *host = nand_get_controller_data(chip);
int is_cache_invalid = 1;
unsigned int flag = 0;
@@ -543,7 +542,7 @@ static irqreturn_t hinfc_irq_handle(int irq, void *devid)
static int hisi_nand_read_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
{
- struct hinfc_host *host = chip->priv;
+ struct hinfc_host *host = nand_get_controller_data(chip);
int max_bitflips = 0, stat = 0, stat_max = 0, status_ecc;
int stat_1, stat_2;
@@ -575,7 +574,7 @@ static int hisi_nand_read_page_hwecc(struct mtd_info *mtd,
static int hisi_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
- struct hinfc_host *host = chip->priv;
+ struct hinfc_host *host = nand_get_controller_data(chip);
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -643,7 +642,7 @@ static int hisi_nfc_ecc_probe(struct hinfc_host *host)
int size, strength, ecc_bits;
struct device *dev = host->dev;
struct nand_chip *chip = &host->chip;
- struct mtd_info *mtd = &host->mtd;
+ struct mtd_info *mtd = nand_to_mtd(chip);
struct device_node *np = host->dev->of_node;
size = of_get_nand_ecc_step_size(np);
@@ -704,7 +703,6 @@ static int hisi_nfc_probe(struct platform_device *pdev)
struct mtd_info *mtd;
struct resource *res;
struct device_node *np = dev->of_node;
- struct mtd_part_parser_data ppdata;
host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
if (!host)
@@ -713,7 +711,7 @@ static int hisi_nfc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, host);
chip = &host->chip;
- mtd = &host->mtd;
+ mtd = nand_to_mtd(chip);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
@@ -737,11 +735,11 @@ static int hisi_nfc_probe(struct platform_device *pdev)
goto err_res;
}
- mtd->priv = chip;
mtd->name = "hisi_nand";
mtd->dev.parent = &pdev->dev;
- chip->priv = host;
+ nand_set_controller_data(chip, host);
+ nand_set_flash_node(chip, np);
chip->cmdfunc = hisi_nfc_cmdfunc;
chip->select_chip = hisi_nfc_select_chip;
chip->read_byte = hisi_nfc_read_byte;
@@ -805,8 +803,7 @@ static int hisi_nfc_probe(struct platform_device *pdev)
goto err_res;
}
- ppdata.of_node = np;
- ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+ ret = mtd_device_register(mtd, NULL, 0);
if (ret) {
dev_err(dev, "Err MTD partition=%d\n", ret);
goto err_mtd;
@@ -823,7 +820,7 @@ err_res:
static int hisi_nfc_remove(struct platform_device *pdev)
{
struct hinfc_host *host = platform_get_drvdata(pdev);
- struct mtd_info *mtd = &host->mtd;
+ struct mtd_info *mtd = nand_to_mtd(&host->chip);
nand_release(mtd);
diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c
index 5a99a93ed025..b19d2a9a5eb9 100644
--- a/drivers/mtd/nand/jz4740_nand.c
+++ b/drivers/mtd/nand/jz4740_nand.c
@@ -59,7 +59,6 @@
#define JZ_NAND_MEM_ADDR_OFFSET 0x10000
struct jz_nand {
- struct mtd_info mtd;
struct nand_chip chip;
void __iomem *base;
struct resource *mem;
@@ -76,13 +75,13 @@ struct jz_nand {
static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd)
{
- return container_of(mtd, struct jz_nand, mtd);
+ return container_of(mtd_to_nand(mtd), struct jz_nand, chip);
}
static void jz_nand_select_chip(struct mtd_info *mtd, int chipnr)
{
struct jz_nand *nand = mtd_to_jz_nand(mtd);
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
uint32_t ctrl;
int banknr;
@@ -104,7 +103,7 @@ static void jz_nand_select_chip(struct mtd_info *mtd, int chipnr)
static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
struct jz_nand *nand = mtd_to_jz_nand(mtd);
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
uint32_t reg;
void __iomem *bank_base = nand->bank_base[nand->selected_bank];
@@ -225,24 +224,6 @@ static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat,
uint32_t t;
unsigned int timeout = 1000;
- t = read_ecc[0];
-
- if (t == 0xff) {
- for (i = 1; i < 9; ++i)
- t &= read_ecc[i];
-
- t &= dat[0];
- t &= dat[nand->chip.ecc.size / 2];
- t &= dat[nand->chip.ecc.size - 1];
-
- if (t == 0xff) {
- for (i = 1; i < nand->chip.ecc.size - 1; ++i)
- t &= dat[i];
- if (t == 0xff)
- return 0;
- }
- }
-
for (i = 0; i < 9; ++i)
writeb(read_ecc[i], nand->base + JZ_REG_NAND_PAR0 + i);
@@ -255,7 +236,7 @@ static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat,
} while (!(status & JZ_NAND_STATUS_DEC_FINISH) && --timeout);
if (timeout == 0)
- return -1;
+ return -ETIMEDOUT;
reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL);
reg &= ~JZ_NAND_ECC_CTRL_ENABLE;
@@ -263,7 +244,7 @@ static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat,
if (status & JZ_NAND_STATUS_ERROR) {
if (status & JZ_NAND_STATUS_UNCOR_ERROR)
- return -1;
+ return -EBADMSG;
error_count = (status & JZ_NAND_STATUS_ERR_COUNT) >> 29;
@@ -334,8 +315,8 @@ static int jz_nand_detect_bank(struct platform_device *pdev,
char gpio_name[9];
char res_name[6];
uint32_t ctrl;
- struct mtd_info *mtd = &nand->mtd;
struct nand_chip *chip = &nand->chip;
+ struct mtd_info *mtd = nand_to_mtd(chip);
/* Request GPIO port. */
gpio = JZ_GPIO_MEM_CS0 + bank - 1;
@@ -432,9 +413,8 @@ static int jz_nand_probe(struct platform_device *pdev)
goto err_iounmap_mmio;
}
- mtd = &nand->mtd;
chip = &nand->chip;
- mtd->priv = chip;
+ mtd = nand_to_mtd(chip);
mtd->dev.parent = &pdev->dev;
mtd->name = "jz4740-nand";
@@ -445,6 +425,7 @@ static int jz_nand_probe(struct platform_device *pdev)
chip->ecc.size = 512;
chip->ecc.bytes = 9;
chip->ecc.strength = 4;
+ chip->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK;
if (pdata)
chip->ecc.layout = pdata->ecc_layout;
@@ -543,7 +524,7 @@ static int jz_nand_remove(struct platform_device *pdev)
struct jz_nand *nand = platform_get_drvdata(pdev);
size_t i;
- nand_release(&nand->mtd);
+ nand_release(nand_to_mtd(&nand->chip));
/* Deassert and disable all chips */
writel(0, nand->base + JZ_REG_NAND_CTRL);
diff --git a/drivers/mtd/nand/jz4780_bch.c b/drivers/mtd/nand/jz4780_bch.c
new file mode 100644
index 000000000000..755499c6650e
--- /dev/null
+++ b/drivers/mtd/nand/jz4780_bch.c
@@ -0,0 +1,381 @@
+/*
+ * JZ4780 BCH controller
+ *
+ * Copyright (c) 2015 Imagination Technologies
+ * Author: Alex Smith <alex.smith@imgtec.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/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include "jz4780_bch.h"
+
+#define BCH_BHCR 0x0
+#define BCH_BHCCR 0x8
+#define BCH_BHCNT 0xc
+#define BCH_BHDR 0x10
+#define BCH_BHPAR0 0x14
+#define BCH_BHERR0 0x84
+#define BCH_BHINT 0x184
+#define BCH_BHINTES 0x188
+#define BCH_BHINTEC 0x18c
+#define BCH_BHINTE 0x190
+
+#define BCH_BHCR_BSEL_SHIFT 4
+#define BCH_BHCR_BSEL_MASK (0x7f << BCH_BHCR_BSEL_SHIFT)
+#define BCH_BHCR_ENCE BIT(2)
+#define BCH_BHCR_INIT BIT(1)
+#define BCH_BHCR_BCHE BIT(0)
+
+#define BCH_BHCNT_PARITYSIZE_SHIFT 16
+#define BCH_BHCNT_PARITYSIZE_MASK (0x7f << BCH_BHCNT_PARITYSIZE_SHIFT)
+#define BCH_BHCNT_BLOCKSIZE_SHIFT 0
+#define BCH_BHCNT_BLOCKSIZE_MASK (0x7ff << BCH_BHCNT_BLOCKSIZE_SHIFT)
+
+#define BCH_BHERR_MASK_SHIFT 16
+#define BCH_BHERR_MASK_MASK (0xffff << BCH_BHERR_MASK_SHIFT)
+#define BCH_BHERR_INDEX_SHIFT 0
+#define BCH_BHERR_INDEX_MASK (0x7ff << BCH_BHERR_INDEX_SHIFT)
+
+#define BCH_BHINT_ERRC_SHIFT 24
+#define BCH_BHINT_ERRC_MASK (0x7f << BCH_BHINT_ERRC_SHIFT)
+#define BCH_BHINT_TERRC_SHIFT 16
+#define BCH_BHINT_TERRC_MASK (0x7f << BCH_BHINT_TERRC_SHIFT)
+#define BCH_BHINT_DECF BIT(3)
+#define BCH_BHINT_ENCF BIT(2)
+#define BCH_BHINT_UNCOR BIT(1)
+#define BCH_BHINT_ERR BIT(0)
+
+#define BCH_CLK_RATE (200 * 1000 * 1000)
+
+/* Timeout for BCH calculation/correction. */
+#define BCH_TIMEOUT_US 100000
+
+struct jz4780_bch {
+ struct device *dev;
+ void __iomem *base;
+ struct clk *clk;
+ struct mutex lock;
+};
+
+static void jz4780_bch_init(struct jz4780_bch *bch,
+ struct jz4780_bch_params *params, bool encode)
+{
+ u32 reg;
+
+ /* Clear interrupt status. */
+ writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT);
+
+ /* Set up BCH count register. */
+ reg = params->size << BCH_BHCNT_BLOCKSIZE_SHIFT;
+ reg |= params->bytes << BCH_BHCNT_PARITYSIZE_SHIFT;
+ writel(reg, bch->base + BCH_BHCNT);
+
+ /* Initialise and enable BCH. */
+ reg = BCH_BHCR_BCHE | BCH_BHCR_INIT;
+ reg |= params->strength << BCH_BHCR_BSEL_SHIFT;
+ if (encode)
+ reg |= BCH_BHCR_ENCE;
+ writel(reg, bch->base + BCH_BHCR);
+}
+
+static void jz4780_bch_disable(struct jz4780_bch *bch)
+{
+ writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT);
+ writel(BCH_BHCR_BCHE, bch->base + BCH_BHCCR);
+}
+
+static void jz4780_bch_write_data(struct jz4780_bch *bch, const void *buf,
+ size_t size)
+{
+ size_t size32 = size / sizeof(u32);
+ size_t size8 = size % sizeof(u32);
+ const u32 *src32;
+ const u8 *src8;
+
+ src32 = (const u32 *)buf;
+ while (size32--)
+ writel(*src32++, bch->base + BCH_BHDR);
+
+ src8 = (const u8 *)src32;
+ while (size8--)
+ writeb(*src8++, bch->base + BCH_BHDR);
+}
+
+static void jz4780_bch_read_parity(struct jz4780_bch *bch, void *buf,
+ size_t size)
+{
+ size_t size32 = size / sizeof(u32);
+ size_t size8 = size % sizeof(u32);
+ u32 *dest32;
+ u8 *dest8;
+ u32 val, offset = 0;
+
+ dest32 = (u32 *)buf;
+ while (size32--) {
+ *dest32++ = readl(bch->base + BCH_BHPAR0 + offset);
+ offset += sizeof(u32);
+ }
+
+ dest8 = (u8 *)dest32;
+ val = readl(bch->base + BCH_BHPAR0 + offset);
+ switch (size8) {
+ case 3:
+ dest8[2] = (val >> 16) & 0xff;
+ case 2:
+ dest8[1] = (val >> 8) & 0xff;
+ case 1:
+ dest8[0] = val & 0xff;
+ break;
+ }
+}
+
+static bool jz4780_bch_wait_complete(struct jz4780_bch *bch, unsigned int irq,
+ u32 *status)
+{
+ u32 reg;
+ int ret;
+
+ /*
+ * While we could use interrupts here and sleep until the operation
+ * completes, the controller works fairly quickly (usually a few
+ * microseconds) and so the overhead of sleeping until we get an
+ * interrupt quite noticeably decreases performance.
+ */
+ ret = readl_poll_timeout(bch->base + BCH_BHINT, reg,
+ (reg & irq) == irq, 0, BCH_TIMEOUT_US);
+ if (ret)
+ return false;
+
+ if (status)
+ *status = reg;
+
+ writel(reg, bch->base + BCH_BHINT);
+ return true;
+}
+
+/**
+ * jz4780_bch_calculate() - calculate ECC for a data buffer
+ * @bch: BCH device.
+ * @params: BCH parameters.
+ * @buf: input buffer with raw data.
+ * @ecc_code: output buffer with ECC.
+ *
+ * Return: 0 on success, -ETIMEDOUT if timed out while waiting for BCH
+ * controller.
+ */
+int jz4780_bch_calculate(struct jz4780_bch *bch, struct jz4780_bch_params *params,
+ const u8 *buf, u8 *ecc_code)
+{
+ int ret = 0;
+
+ mutex_lock(&bch->lock);
+ jz4780_bch_init(bch, params, true);
+ jz4780_bch_write_data(bch, buf, params->size);
+
+ if (jz4780_bch_wait_complete(bch, BCH_BHINT_ENCF, NULL)) {
+ jz4780_bch_read_parity(bch, ecc_code, params->bytes);
+ } else {
+ dev_err(bch->dev, "timed out while calculating ECC\n");
+ ret = -ETIMEDOUT;
+ }
+
+ jz4780_bch_disable(bch);
+ mutex_unlock(&bch->lock);
+ return ret;
+}
+EXPORT_SYMBOL(jz4780_bch_calculate);
+
+/**
+ * jz4780_bch_correct() - detect and correct bit errors
+ * @bch: BCH device.
+ * @params: BCH parameters.
+ * @buf: raw data read from the chip.
+ * @ecc_code: ECC read from the chip.
+ *
+ * Given the raw data and the ECC read from the NAND device, detects and
+ * corrects errors in the data.
+ *
+ * Return: the number of bit errors corrected, -EBADMSG if there are too many
+ * errors to correct or -ETIMEDOUT if we timed out waiting for the controller.
+ */
+int jz4780_bch_correct(struct jz4780_bch *bch, struct jz4780_bch_params *params,
+ u8 *buf, u8 *ecc_code)
+{
+ u32 reg, mask, index;
+ int i, ret, count;
+
+ mutex_lock(&bch->lock);
+
+ jz4780_bch_init(bch, params, false);
+ jz4780_bch_write_data(bch, buf, params->size);
+ jz4780_bch_write_data(bch, ecc_code, params->bytes);
+
+ if (!jz4780_bch_wait_complete(bch, BCH_BHINT_DECF, &reg)) {
+ dev_err(bch->dev, "timed out while correcting data\n");
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+
+ if (reg & BCH_BHINT_UNCOR) {
+ dev_warn(bch->dev, "uncorrectable ECC error\n");
+ ret = -EBADMSG;
+ goto out;
+ }
+
+ /* Correct any detected errors. */
+ if (reg & BCH_BHINT_ERR) {
+ count = (reg & BCH_BHINT_ERRC_MASK) >> BCH_BHINT_ERRC_SHIFT;
+ ret = (reg & BCH_BHINT_TERRC_MASK) >> BCH_BHINT_TERRC_SHIFT;
+
+ for (i = 0; i < count; i++) {
+ reg = readl(bch->base + BCH_BHERR0 + (i * 4));
+ mask = (reg & BCH_BHERR_MASK_MASK) >>
+ BCH_BHERR_MASK_SHIFT;
+ index = (reg & BCH_BHERR_INDEX_MASK) >>
+ BCH_BHERR_INDEX_SHIFT;
+ buf[(index * 2) + 0] ^= mask;
+ buf[(index * 2) + 1] ^= mask >> 8;
+ }
+ } else {
+ ret = 0;
+ }
+
+out:
+ jz4780_bch_disable(bch);
+ mutex_unlock(&bch->lock);
+ return ret;
+}
+EXPORT_SYMBOL(jz4780_bch_correct);
+
+/**
+ * jz4780_bch_get() - get the BCH controller device
+ * @np: BCH device tree node.
+ *
+ * Gets the BCH controller device from the specified device tree node. The
+ * device must be released with jz4780_bch_release() when it is no longer being
+ * used.
+ *
+ * Return: a pointer to jz4780_bch, errors are encoded into the pointer.
+ * PTR_ERR(-EPROBE_DEFER) if the device hasn't been initialised yet.
+ */
+static struct jz4780_bch *jz4780_bch_get(struct device_node *np)
+{
+ struct platform_device *pdev;
+ struct jz4780_bch *bch;
+
+ pdev = of_find_device_by_node(np);
+ if (!pdev || !platform_get_drvdata(pdev))
+ return ERR_PTR(-EPROBE_DEFER);
+
+ get_device(&pdev->dev);
+
+ bch = platform_get_drvdata(pdev);
+ clk_prepare_enable(bch->clk);
+
+ bch->dev = &pdev->dev;
+ return bch;
+}
+
+/**
+ * of_jz4780_bch_get() - get the BCH controller from a DT node
+ * @of_node: the node that contains a bch-controller property.
+ *
+ * Get the bch-controller property from the given device tree
+ * node and pass it to jz4780_bch_get to do the work.
+ *
+ * Return: a pointer to jz4780_bch, errors are encoded into the pointer.
+ * PTR_ERR(-EPROBE_DEFER) if the device hasn't been initialised yet.
+ */
+struct jz4780_bch *of_jz4780_bch_get(struct device_node *of_node)
+{
+ struct jz4780_bch *bch = NULL;
+ struct device_node *np;
+
+ np = of_parse_phandle(of_node, "ingenic,bch-controller", 0);
+
+ if (np) {
+ bch = jz4780_bch_get(np);
+ of_node_put(np);
+ }
+ return bch;
+}
+EXPORT_SYMBOL(of_jz4780_bch_get);
+
+/**
+ * jz4780_bch_release() - release the BCH controller device
+ * @bch: BCH device.
+ */
+void jz4780_bch_release(struct jz4780_bch *bch)
+{
+ clk_disable_unprepare(bch->clk);
+ put_device(bch->dev);
+}
+EXPORT_SYMBOL(jz4780_bch_release);
+
+static int jz4780_bch_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct jz4780_bch *bch;
+ struct resource *res;
+
+ bch = devm_kzalloc(dev, sizeof(*bch), GFP_KERNEL);
+ if (!bch)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ bch->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(bch->base))
+ return PTR_ERR(bch->base);
+
+ jz4780_bch_disable(bch);
+
+ bch->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(bch->clk)) {
+ dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(bch->clk));
+ return PTR_ERR(bch->clk);
+ }
+
+ clk_set_rate(bch->clk, BCH_CLK_RATE);
+
+ mutex_init(&bch->lock);
+
+ bch->dev = dev;
+ platform_set_drvdata(pdev, bch);
+
+ return 0;
+}
+
+static const struct of_device_id jz4780_bch_dt_match[] = {
+ { .compatible = "ingenic,jz4780-bch" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, jz4780_bch_dt_match);
+
+static struct platform_driver jz4780_bch_driver = {
+ .probe = jz4780_bch_probe,
+ .driver = {
+ .name = "jz4780-bch",
+ .of_match_table = of_match_ptr(jz4780_bch_dt_match),
+ },
+};
+module_platform_driver(jz4780_bch_driver);
+
+MODULE_AUTHOR("Alex Smith <alex@alex-smith.me.uk>");
+MODULE_AUTHOR("Harvey Hunt <harvey.hunt@imgtec.com>");
+MODULE_DESCRIPTION("Ingenic JZ4780 BCH error correction driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/nand/jz4780_bch.h b/drivers/mtd/nand/jz4780_bch.h
new file mode 100644
index 000000000000..bf4718088a3a
--- /dev/null
+++ b/drivers/mtd/nand/jz4780_bch.h
@@ -0,0 +1,43 @@
+/*
+ * JZ4780 BCH controller
+ *
+ * Copyright (c) 2015 Imagination Technologies
+ * Author: Alex Smith <alex.smith@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#ifndef __DRIVERS_MTD_NAND_JZ4780_BCH_H__
+#define __DRIVERS_MTD_NAND_JZ4780_BCH_H__
+
+#include <linux/types.h>
+
+struct device;
+struct device_node;
+struct jz4780_bch;
+
+/**
+ * struct jz4780_bch_params - BCH parameters
+ * @size: data bytes per ECC step.
+ * @bytes: ECC bytes per step.
+ * @strength: number of correctable bits per ECC step.
+ */
+struct jz4780_bch_params {
+ int size;
+ int bytes;
+ int strength;
+};
+
+int jz4780_bch_calculate(struct jz4780_bch *bch,
+ struct jz4780_bch_params *params,
+ const u8 *buf, u8 *ecc_code);
+int jz4780_bch_correct(struct jz4780_bch *bch,
+ struct jz4780_bch_params *params, u8 *buf,
+ u8 *ecc_code);
+
+void jz4780_bch_release(struct jz4780_bch *bch);
+struct jz4780_bch *of_jz4780_bch_get(struct device_node *np);
+
+#endif /* __DRIVERS_MTD_NAND_JZ4780_BCH_H__ */
diff --git a/drivers/mtd/nand/jz4780_nand.c b/drivers/mtd/nand/jz4780_nand.c
new file mode 100644
index 000000000000..e1c016c9d32d
--- /dev/null
+++ b/drivers/mtd/nand/jz4780_nand.c
@@ -0,0 +1,428 @@
+/*
+ * JZ4780 NAND driver
+ *
+ * Copyright (c) 2015 Imagination Technologies
+ * Author: Alex Smith <alex.smith@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_mtd.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+#include <linux/jz4780-nemc.h>
+
+#include "jz4780_bch.h"
+
+#define DRV_NAME "jz4780-nand"
+
+#define OFFSET_DATA 0x00000000
+#define OFFSET_CMD 0x00400000
+#define OFFSET_ADDR 0x00800000
+
+/* Command delay when there is no R/B pin. */
+#define RB_DELAY_US 100
+
+struct jz4780_nand_cs {
+ unsigned int bank;
+ void __iomem *base;
+};
+
+struct jz4780_nand_controller {
+ struct device *dev;
+ struct jz4780_bch *bch;
+ struct nand_hw_control controller;
+ unsigned int num_banks;
+ struct list_head chips;
+ int selected;
+ struct jz4780_nand_cs cs[];
+};
+
+struct jz4780_nand_chip {
+ struct nand_chip chip;
+ struct list_head chip_list;
+
+ struct nand_ecclayout ecclayout;
+
+ struct gpio_desc *busy_gpio;
+ struct gpio_desc *wp_gpio;
+ unsigned int reading: 1;
+};
+
+static inline struct jz4780_nand_chip *to_jz4780_nand_chip(struct mtd_info *mtd)
+{
+ return container_of(mtd_to_nand(mtd), struct jz4780_nand_chip, chip);
+}
+
+static inline struct jz4780_nand_controller *to_jz4780_nand_controller(struct nand_hw_control *ctrl)
+{
+ return container_of(ctrl, struct jz4780_nand_controller, controller);
+}
+
+static void jz4780_nand_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd);
+ struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(nand->chip.controller);
+ struct jz4780_nand_cs *cs;
+
+ /* Ensure the currently selected chip is deasserted. */
+ if (chipnr == -1 && nfc->selected >= 0) {
+ cs = &nfc->cs[nfc->selected];
+ jz4780_nemc_assert(nfc->dev, cs->bank, false);
+ }
+
+ nfc->selected = chipnr;
+}
+
+static void jz4780_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl)
+{
+ struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd);
+ struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(nand->chip.controller);
+ struct jz4780_nand_cs *cs;
+
+ if (WARN_ON(nfc->selected < 0))
+ return;
+
+ cs = &nfc->cs[nfc->selected];
+
+ jz4780_nemc_assert(nfc->dev, cs->bank, ctrl & NAND_NCE);
+
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ if (ctrl & NAND_ALE)
+ writeb(cmd, cs->base + OFFSET_ADDR);
+ else if (ctrl & NAND_CLE)
+ writeb(cmd, cs->base + OFFSET_CMD);
+}
+
+static int jz4780_nand_dev_ready(struct mtd_info *mtd)
+{
+ struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd);
+
+ return !gpiod_get_value_cansleep(nand->busy_gpio);
+}
+
+static void jz4780_nand_ecc_hwctl(struct mtd_info *mtd, int mode)
+{
+ struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd);
+
+ nand->reading = (mode == NAND_ECC_READ);
+}
+
+static int jz4780_nand_ecc_calculate(struct mtd_info *mtd, const u8 *dat,
+ u8 *ecc_code)
+{
+ struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd);
+ struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(nand->chip.controller);
+ struct jz4780_bch_params params;
+
+ /*
+ * Don't need to generate the ECC when reading, BCH does it for us as
+ * part of decoding/correction.
+ */
+ if (nand->reading)
+ return 0;
+
+ params.size = nand->chip.ecc.size;
+ params.bytes = nand->chip.ecc.bytes;
+ params.strength = nand->chip.ecc.strength;
+
+ return jz4780_bch_calculate(nfc->bch, &params, dat, ecc_code);
+}
+
+static int jz4780_nand_ecc_correct(struct mtd_info *mtd, u8 *dat,
+ u8 *read_ecc, u8 *calc_ecc)
+{
+ struct jz4780_nand_chip *nand = to_jz4780_nand_chip(mtd);
+ struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(nand->chip.controller);
+ struct jz4780_bch_params params;
+
+ params.size = nand->chip.ecc.size;
+ params.bytes = nand->chip.ecc.bytes;
+ params.strength = nand->chip.ecc.strength;
+
+ return jz4780_bch_correct(nfc->bch, &params, dat, read_ecc);
+}
+
+static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *dev)
+{
+ struct nand_chip *chip = &nand->chip;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(chip->controller);
+ struct nand_ecclayout *layout = &nand->ecclayout;
+ u32 start, i;
+
+ chip->ecc.bytes = fls((1 + 8) * chip->ecc.size) *
+ (chip->ecc.strength / 8);
+
+ switch (chip->ecc.mode) {
+ case NAND_ECC_HW:
+ if (!nfc->bch) {
+ dev_err(dev, "HW BCH selected, but BCH controller not found\n");
+ return -ENODEV;
+ }
+
+ chip->ecc.hwctl = jz4780_nand_ecc_hwctl;
+ chip->ecc.calculate = jz4780_nand_ecc_calculate;
+ chip->ecc.correct = jz4780_nand_ecc_correct;
+ /* fall through */
+ case NAND_ECC_SOFT:
+ case NAND_ECC_SOFT_BCH:
+ dev_info(dev, "using %s (strength %d, size %d, bytes %d)\n",
+ (nfc->bch) ? "hardware BCH" : "software ECC",
+ chip->ecc.strength, chip->ecc.size, chip->ecc.bytes);
+ break;
+ case NAND_ECC_NONE:
+ dev_info(dev, "not using ECC\n");
+ break;
+ default:
+ dev_err(dev, "ECC mode %d not supported\n", chip->ecc.mode);
+ return -EINVAL;
+ }
+
+ /* The NAND core will generate the ECC layout for SW ECC */
+ if (chip->ecc.mode != NAND_ECC_HW)
+ return 0;
+
+ /* Generate ECC layout. ECC codes are right aligned in the OOB area. */
+ layout->eccbytes = mtd->writesize / chip->ecc.size * chip->ecc.bytes;
+
+ if (layout->eccbytes > mtd->oobsize - 2) {
+ dev_err(dev,
+ "invalid ECC config: required %d ECC bytes, but only %d are available",
+ layout->eccbytes, mtd->oobsize - 2);
+ return -EINVAL;
+ }
+
+ start = mtd->oobsize - layout->eccbytes;
+ for (i = 0; i < layout->eccbytes; i++)
+ layout->eccpos[i] = start + i;
+
+ layout->oobfree[0].offset = 2;
+ layout->oobfree[0].length = mtd->oobsize - layout->eccbytes - 2;
+
+ chip->ecc.layout = layout;
+ return 0;
+}
+
+static int jz4780_nand_init_chip(struct platform_device *pdev,
+ struct jz4780_nand_controller *nfc,
+ struct device_node *np,
+ unsigned int chipnr)
+{
+ struct device *dev = &pdev->dev;
+ struct jz4780_nand_chip *nand;
+ struct jz4780_nand_cs *cs;
+ struct resource *res;
+ struct nand_chip *chip;
+ struct mtd_info *mtd;
+ const __be32 *reg;
+ int ret = 0;
+
+ cs = &nfc->cs[chipnr];
+
+ reg = of_get_property(np, "reg", NULL);
+ if (!reg)
+ return -EINVAL;
+
+ cs->bank = be32_to_cpu(*reg);
+
+ jz4780_nemc_set_type(nfc->dev, cs->bank, JZ4780_NEMC_BANK_NAND);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, chipnr);
+ cs->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(cs->base))
+ return PTR_ERR(cs->base);
+
+ nand = devm_kzalloc(dev, sizeof(*nand), GFP_KERNEL);
+ if (!nand)
+ return -ENOMEM;
+
+ nand->busy_gpio = devm_gpiod_get_optional(dev, "rb", GPIOD_IN);
+
+ if (IS_ERR(nand->busy_gpio)) {
+ ret = PTR_ERR(nand->busy_gpio);
+ dev_err(dev, "failed to request busy GPIO: %d\n", ret);
+ return ret;
+ } else if (nand->busy_gpio) {
+ nand->chip.dev_ready = jz4780_nand_dev_ready;
+ }
+
+ nand->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_LOW);
+
+ if (IS_ERR(nand->wp_gpio)) {
+ ret = PTR_ERR(nand->wp_gpio);
+ dev_err(dev, "failed to request WP GPIO: %d\n", ret);
+ return ret;
+ }
+
+ chip = &nand->chip;
+ mtd = nand_to_mtd(chip);
+ mtd->name = devm_kasprintf(dev, GFP_KERNEL, "%s.%d", dev_name(dev),
+ cs->bank);
+ if (!mtd->name)
+ return -ENOMEM;
+ mtd->dev.parent = dev;
+
+ chip->IO_ADDR_R = cs->base + OFFSET_DATA;
+ chip->IO_ADDR_W = cs->base + OFFSET_DATA;
+ chip->chip_delay = RB_DELAY_US;
+ chip->options = NAND_NO_SUBPAGE_WRITE;
+ chip->select_chip = jz4780_nand_select_chip;
+ chip->cmd_ctrl = jz4780_nand_cmd_ctrl;
+ chip->ecc.mode = NAND_ECC_HW;
+ chip->controller = &nfc->controller;
+ nand_set_flash_node(chip, np);
+
+ ret = nand_scan_ident(mtd, 1, NULL);
+ if (ret)
+ return ret;
+
+ ret = jz4780_nand_init_ecc(nand, dev);
+ if (ret)
+ return ret;
+
+ ret = nand_scan_tail(mtd);
+ if (ret)
+ return ret;
+
+ ret = mtd_device_register(mtd, NULL, 0);
+ if (ret) {
+ nand_release(mtd);
+ return ret;
+ }
+
+ list_add_tail(&nand->chip_list, &nfc->chips);
+
+ return 0;
+}
+
+static void jz4780_nand_cleanup_chips(struct jz4780_nand_controller *nfc)
+{
+ struct jz4780_nand_chip *chip;
+
+ while (!list_empty(&nfc->chips)) {
+ chip = list_first_entry(&nfc->chips, struct jz4780_nand_chip, chip_list);
+ nand_release(nand_to_mtd(&chip->chip));
+ list_del(&chip->chip_list);
+ }
+}
+
+static int jz4780_nand_init_chips(struct jz4780_nand_controller *nfc,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np;
+ int i = 0;
+ int ret;
+ int num_chips = of_get_child_count(dev->of_node);
+
+ if (num_chips > nfc->num_banks) {
+ dev_err(dev, "found %d chips but only %d banks\n", num_chips, nfc->num_banks);
+ return -EINVAL;
+ }
+
+ for_each_child_of_node(dev->of_node, np) {
+ ret = jz4780_nand_init_chip(pdev, nfc, np, i);
+ if (ret) {
+ jz4780_nand_cleanup_chips(nfc);
+ return ret;
+ }
+
+ i++;
+ }
+
+ return 0;
+}
+
+static int jz4780_nand_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ unsigned int num_banks;
+ struct jz4780_nand_controller *nfc;
+ int ret;
+
+ num_banks = jz4780_nemc_num_banks(dev);
+ if (num_banks == 0) {
+ dev_err(dev, "no banks found\n");
+ return -ENODEV;
+ }
+
+ nfc = devm_kzalloc(dev, sizeof(*nfc) + (sizeof(nfc->cs[0]) * num_banks), GFP_KERNEL);
+ if (!nfc)
+ return -ENOMEM;
+
+ /*
+ * Check for BCH HW before we call nand_scan_ident, to prevent us from
+ * having to call it again if the BCH driver returns -EPROBE_DEFER.
+ */
+ nfc->bch = of_jz4780_bch_get(dev->of_node);
+ if (IS_ERR(nfc->bch))
+ return PTR_ERR(nfc->bch);
+
+ nfc->dev = dev;
+ nfc->num_banks = num_banks;
+
+ spin_lock_init(&nfc->controller.lock);
+ INIT_LIST_HEAD(&nfc->chips);
+ init_waitqueue_head(&nfc->controller.wq);
+
+ ret = jz4780_nand_init_chips(nfc, pdev);
+ if (ret) {
+ if (nfc->bch)
+ jz4780_bch_release(nfc->bch);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, nfc);
+ return 0;
+}
+
+static int jz4780_nand_remove(struct platform_device *pdev)
+{
+ struct jz4780_nand_controller *nfc = platform_get_drvdata(pdev);
+
+ if (nfc->bch)
+ jz4780_bch_release(nfc->bch);
+
+ jz4780_nand_cleanup_chips(nfc);
+
+ return 0;
+}
+
+static const struct of_device_id jz4780_nand_dt_match[] = {
+ { .compatible = "ingenic,jz4780-nand" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, jz4780_nand_dt_match);
+
+static struct platform_driver jz4780_nand_driver = {
+ .probe = jz4780_nand_probe,
+ .remove = jz4780_nand_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(jz4780_nand_dt_match),
+ },
+};
+module_platform_driver(jz4780_nand_driver);
+
+MODULE_AUTHOR("Alex Smith <alex@alex-smith.me.uk>");
+MODULE_AUTHOR("Harvey Hunt <harvey.hunt@imgtec.com>");
+MODULE_DESCRIPTION("Ingenic JZ4780 NAND driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c
index 347510978484..9bc435d72a86 100644
--- a/drivers/mtd/nand/lpc32xx_mlc.c
+++ b/drivers/mtd/nand/lpc32xx_mlc.c
@@ -173,7 +173,6 @@ struct lpc32xx_nand_host {
struct nand_chip nand_chip;
struct lpc32xx_mlc_platform_data *pdata;
struct clk *clk;
- struct mtd_info mtd;
void __iomem *io_base;
int irq;
struct lpc32xx_nand_cfg_mlc *ncfg;
@@ -275,8 +274,8 @@ static void lpc32xx_nand_setup(struct lpc32xx_nand_host *host)
static void lpc32xx_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct lpc32xx_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct lpc32xx_nand_host *host = nand_get_controller_data(nand_chip);
if (cmd != NAND_CMD_NONE) {
if (ctrl & NAND_CLE)
@@ -291,8 +290,8 @@ static void lpc32xx_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
*/
static int lpc32xx_nand_device_ready(struct mtd_info *mtd)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct lpc32xx_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct lpc32xx_nand_host *host = nand_get_controller_data(nand_chip);
if ((readb(MLC_ISR(host->io_base)) &
(MLCISR_CONTROLLER_READY | MLCISR_NAND_READY)) ==
@@ -318,7 +317,7 @@ static irqreturn_t lpc3xxx_nand_irq(int irq, struct lpc32xx_nand_host *host)
static int lpc32xx_waitfunc_nand(struct mtd_info *mtd, struct nand_chip *chip)
{
- struct lpc32xx_nand_host *host = chip->priv;
+ struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
if (readb(MLC_ISR(host->io_base)) & MLCISR_NAND_READY)
goto exit;
@@ -338,7 +337,7 @@ exit:
static int lpc32xx_waitfunc_controller(struct mtd_info *mtd,
struct nand_chip *chip)
{
- struct lpc32xx_nand_host *host = chip->priv;
+ struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
if (readb(MLC_ISR(host->io_base)) & MLCISR_CONTROLLER_READY)
goto exit;
@@ -389,8 +388,8 @@ static void lpc32xx_dma_complete_func(void *completion)
static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
enum dma_transfer_direction dir)
{
- struct nand_chip *chip = mtd->priv;
- struct lpc32xx_nand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
struct dma_async_tx_descriptor *desc;
int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
int res;
@@ -431,7 +430,7 @@ out1:
static int lpc32xx_read_page(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
- struct lpc32xx_nand_host *host = chip->priv;
+ struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
int i, j;
uint8_t *oobbuf = chip->oob_poi;
uint32_t mlc_isr;
@@ -498,7 +497,7 @@ static int lpc32xx_write_page_lowlevel(struct mtd_info *mtd,
const uint8_t *buf, int oob_required,
int page)
{
- struct lpc32xx_nand_host *host = chip->priv;
+ struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
const uint8_t *oobbuf = chip->oob_poi;
uint8_t *dma_buf = (uint8_t *)buf;
int res;
@@ -543,7 +542,7 @@ static int lpc32xx_write_page_lowlevel(struct mtd_info *mtd,
static int lpc32xx_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
- struct lpc32xx_nand_host *host = chip->priv;
+ struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
/* Read whole page - necessary with MLC controller! */
lpc32xx_read_page(mtd, chip, host->dummy_buf, 1, page);
@@ -566,7 +565,7 @@ static void lpc32xx_ecc_enable(struct mtd_info *mtd, int mode)
static int lpc32xx_dma_setup(struct lpc32xx_nand_host *host)
{
- struct mtd_info *mtd = &host->mtd;
+ struct mtd_info *mtd = nand_to_mtd(&host->nand_chip);
dma_cap_mask_t mask;
if (!host->pdata || !host->pdata->dma_filter) {
@@ -647,7 +646,6 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
struct nand_chip *nand_chip;
struct resource *rc;
int res;
- struct mtd_part_parser_data ppdata = {};
/* Allocate memory for the device structure (and zero it) */
host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
@@ -661,8 +659,8 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
host->io_base_phy = rc->start;
- mtd = &host->mtd;
nand_chip = &host->nand_chip;
+ mtd = nand_to_mtd(nand_chip);
if (pdev->dev.of_node)
host->ncfg = lpc32xx_parse_dt(&pdev->dev);
if (!host->ncfg) {
@@ -681,8 +679,9 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
host->pdata = dev_get_platdata(&pdev->dev);
- nand_chip->priv = host; /* link the private data structures */
- mtd->priv = nand_chip;
+ /* link the private data structures */
+ nand_set_controller_data(nand_chip, host);
+ nand_set_flash_node(nand_chip, pdev->dev.of_node);
mtd->dev.parent = &pdev->dev;
/* Get NAND clock */
@@ -786,9 +785,8 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
mtd->name = DRV_NAME;
- ppdata.of_node = pdev->dev.of_node;
- res = mtd_device_parse_register(mtd, NULL, &ppdata, host->ncfg->parts,
- host->ncfg->num_parts);
+ res = mtd_device_register(mtd, host->ncfg->parts,
+ host->ncfg->num_parts);
if (!res)
return res;
@@ -815,7 +813,7 @@ err_exit1:
static int lpc32xx_nand_remove(struct platform_device *pdev)
{
struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
- struct mtd_info *mtd = &host->mtd;
+ struct mtd_info *mtd = nand_to_mtd(&host->nand_chip);
nand_release(mtd);
free_irq(host->irq, host);
diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c
index 4f3d4eb17da0..3b8f3735f3e8 100644
--- a/drivers/mtd/nand/lpc32xx_slc.c
+++ b/drivers/mtd/nand/lpc32xx_slc.c
@@ -204,7 +204,6 @@ struct lpc32xx_nand_host {
struct nand_chip nand_chip;
struct lpc32xx_slc_platform_data *pdata;
struct clk *clk;
- struct mtd_info mtd;
void __iomem *io_base;
struct lpc32xx_nand_cfg_slc *ncfg;
@@ -260,8 +259,8 @@ static void lpc32xx_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
uint32_t tmp;
- struct nand_chip *chip = mtd->priv;
- struct lpc32xx_nand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
/* Does CE state need to be changed? */
tmp = readl(SLC_CFG(host->io_base));
@@ -284,8 +283,8 @@ static void lpc32xx_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
*/
static int lpc32xx_nand_device_ready(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct lpc32xx_nand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
int rdy = 0;
if ((readl(SLC_STAT(host->io_base)) & SLCSTAT_NAND_READY) != 0)
@@ -339,8 +338,8 @@ static int lpc32xx_nand_ecc_calculate(struct mtd_info *mtd,
*/
static uint8_t lpc32xx_nand_read_byte(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct lpc32xx_nand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
return (uint8_t)readl(SLC_DATA(host->io_base));
}
@@ -350,8 +349,8 @@ static uint8_t lpc32xx_nand_read_byte(struct mtd_info *mtd)
*/
static void lpc32xx_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
- struct lpc32xx_nand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
/* Direct device read with no ECC */
while (len-- > 0)
@@ -363,8 +362,8 @@ static void lpc32xx_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
*/
static void lpc32xx_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
- struct lpc32xx_nand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
/* Direct device write with no ECC */
while (len-- > 0)
@@ -428,8 +427,8 @@ static void lpc32xx_dma_complete_func(void *completion)
static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma,
void *mem, int len, enum dma_transfer_direction dir)
{
- struct nand_chip *chip = mtd->priv;
- struct lpc32xx_nand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
struct dma_async_tx_descriptor *desc;
int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
int res;
@@ -488,8 +487,8 @@ out1:
static int lpc32xx_xfer(struct mtd_info *mtd, uint8_t *buf, int eccsubpages,
int read)
{
- struct nand_chip *chip = mtd->priv;
- struct lpc32xx_nand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
int i, status = 0;
unsigned long timeout;
int res;
@@ -604,7 +603,7 @@ static int lpc32xx_nand_read_page_syndrome(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf,
int oob_required, int page)
{
- struct lpc32xx_nand_host *host = chip->priv;
+ struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
int stat, i, status;
uint8_t *oobecc, tmpecc[LPC32XX_ECC_SAVE_SIZE];
@@ -666,7 +665,7 @@ static int lpc32xx_nand_write_page_syndrome(struct mtd_info *mtd,
const uint8_t *buf,
int oob_required, int page)
{
- struct lpc32xx_nand_host *host = chip->priv;
+ struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
uint8_t *pb = chip->oob_poi + chip->ecc.layout->eccpos[0];
int error;
@@ -703,7 +702,7 @@ static int lpc32xx_nand_write_page_raw_syndrome(struct mtd_info *mtd,
static int lpc32xx_nand_dma_setup(struct lpc32xx_nand_host *host)
{
- struct mtd_info *mtd = &host->mtd;
+ struct mtd_info *mtd = nand_to_mtd(&host->nand_chip);
dma_cap_mask_t mask;
if (!host->pdata || !host->pdata->dma_filter) {
@@ -763,7 +762,6 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
struct mtd_info *mtd;
struct nand_chip *chip;
struct resource *rc;
- struct mtd_part_parser_data ppdata = {};
int res;
rc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -800,10 +798,10 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
host->pdata = dev_get_platdata(&pdev->dev);
- mtd = &host->mtd;
chip = &host->nand_chip;
- chip->priv = host;
- mtd->priv = chip;
+ mtd = nand_to_mtd(chip);
+ nand_set_controller_data(chip, host);
+ nand_set_flash_node(chip, pdev->dev.of_node);
mtd->owner = THIS_MODULE;
mtd->dev.parent = &pdev->dev;
@@ -908,9 +906,8 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
}
mtd->name = "nxp_lpc3220_slc";
- ppdata.of_node = pdev->dev.of_node;
- res = mtd_device_parse_register(mtd, NULL, &ppdata, host->ncfg->parts,
- host->ncfg->num_parts);
+ res = mtd_device_register(mtd, host->ncfg->parts,
+ host->ncfg->num_parts);
if (!res)
return res;
@@ -933,7 +930,7 @@ static int lpc32xx_nand_remove(struct platform_device *pdev)
{
uint32_t tmp;
struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
- struct mtd_info *mtd = &host->mtd;
+ struct mtd_info *mtd = nand_to_mtd(&host->nand_chip);
nand_release(mtd);
dma_release_channel(host->dma_chan);
diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c
index d6bbde4a5331..6b93e899d4e9 100644
--- a/drivers/mtd/nand/mpc5121_nfc.c
+++ b/drivers/mtd/nand/mpc5121_nfc.c
@@ -118,7 +118,6 @@
#define NFC_TIMEOUT (HZ / 10) /* 1/10 s */
struct mpc5121_nfc_prv {
- struct mtd_info mtd;
struct nand_chip chip;
int irq;
void __iomem *regs;
@@ -135,8 +134,8 @@ static void mpc5121_nfc_done(struct mtd_info *mtd);
/* Read NFC register */
static inline u16 nfc_read(struct mtd_info *mtd, uint reg)
{
- struct nand_chip *chip = mtd->priv;
- struct mpc5121_nfc_prv *prv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
return in_be16(prv->regs + reg);
}
@@ -144,8 +143,8 @@ static inline u16 nfc_read(struct mtd_info *mtd, uint reg)
/* Write NFC register */
static inline void nfc_write(struct mtd_info *mtd, uint reg, u16 val)
{
- struct nand_chip *chip = mtd->priv;
- struct mpc5121_nfc_prv *prv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
out_be16(prv->regs + reg, val);
}
@@ -214,8 +213,8 @@ static inline void mpc5121_nfc_send_read_status(struct mtd_info *mtd)
static irqreturn_t mpc5121_nfc_irq(int irq, void *data)
{
struct mtd_info *mtd = data;
- struct nand_chip *chip = mtd->priv;
- struct mpc5121_nfc_prv *prv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
nfc_set(mtd, NFC_CONFIG1, NFC_INT_MASK);
wake_up(&prv->irq_waitq);
@@ -226,8 +225,8 @@ static irqreturn_t mpc5121_nfc_irq(int irq, void *data)
/* Wait for operation complete */
static void mpc5121_nfc_done(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct mpc5121_nfc_prv *prv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
int rv;
if ((nfc_read(mtd, NFC_CONFIG2) & NFC_INT) == 0) {
@@ -246,7 +245,7 @@ static void mpc5121_nfc_done(struct mtd_info *mtd)
/* Do address cycle(s) */
static void mpc5121_nfc_addr_cycle(struct mtd_info *mtd, int column, int page)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
u32 pagemask = chip->pagemask;
if (column != -1) {
@@ -281,8 +280,8 @@ static void mpc5121_nfc_select_chip(struct mtd_info *mtd, int chip)
/* Init external chip select logic on ADS5121 board */
static int ads5121_chipselect_init(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct mpc5121_nfc_prv *prv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
struct device_node *dn;
dn = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld");
@@ -303,8 +302,8 @@ static int ads5121_chipselect_init(struct mtd_info *mtd)
/* Control chips select signal on ADS5121 board */
static void ads5121_select_chip(struct mtd_info *mtd, int chip)
{
- struct nand_chip *nand = mtd->priv;
- struct mpc5121_nfc_prv *prv = nand->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(nand);
u8 v;
v = in_8(prv->csreg);
@@ -333,8 +332,8 @@ static int mpc5121_nfc_dev_ready(struct mtd_info *mtd)
static void mpc5121_nfc_command(struct mtd_info *mtd, unsigned command,
int column, int page)
{
- struct nand_chip *chip = mtd->priv;
- struct mpc5121_nfc_prv *prv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
prv->column = (column >= 0) ? column : 0;
prv->spareonly = 0;
@@ -406,8 +405,8 @@ static void mpc5121_nfc_command(struct mtd_info *mtd, unsigned command,
static void mpc5121_nfc_copy_spare(struct mtd_info *mtd, uint offset,
u8 *buffer, uint size, int wr)
{
- struct nand_chip *nand = mtd->priv;
- struct mpc5121_nfc_prv *prv = nand->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(nand);
uint o, s, sbsize, blksize;
/*
@@ -458,8 +457,8 @@ static void mpc5121_nfc_copy_spare(struct mtd_info *mtd, uint offset,
static void mpc5121_nfc_buf_copy(struct mtd_info *mtd, u_char *buf, int len,
int wr)
{
- struct nand_chip *chip = mtd->priv;
- struct mpc5121_nfc_prv *prv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
uint c = prv->column;
uint l;
@@ -536,8 +535,8 @@ static u16 mpc5121_nfc_read_word(struct mtd_info *mtd)
*/
static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct mpc5121_nfc_prv *prv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
struct mpc512x_reset_module *rm;
struct device_node *rmnode;
uint rcw_pagesize = 0;
@@ -615,8 +614,8 @@ out:
/* Free driver resources */
static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct mpc5121_nfc_prv *prv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip);
if (prv->clk)
clk_disable_unprepare(prv->clk);
@@ -639,7 +638,6 @@ static int mpc5121_nfc_probe(struct platform_device *op)
int resettime = 0;
int retval = 0;
int rev, len;
- struct mtd_part_parser_data ppdata;
/*
* Check SoC revision. This driver supports only NFC
@@ -655,12 +653,12 @@ static int mpc5121_nfc_probe(struct platform_device *op)
if (!prv)
return -ENOMEM;
- mtd = &prv->mtd;
chip = &prv->chip;
+ mtd = nand_to_mtd(chip);
- mtd->priv = chip;
mtd->dev.parent = dev;
- chip->priv = prv;
+ nand_set_controller_data(chip, prv);
+ nand_set_flash_node(chip, dn);
prv->dev = dev;
/* Read NFC configuration from Reset Config Word */
@@ -703,7 +701,6 @@ static int mpc5121_nfc_probe(struct platform_device *op)
}
mtd->name = "MPC5121 NAND";
- ppdata.of_node = dn;
chip->dev_ready = mpc5121_nfc_dev_ready;
chip->cmdfunc = mpc5121_nfc_command;
chip->read_byte = mpc5121_nfc_read_byte;
@@ -815,7 +812,7 @@ static int mpc5121_nfc_probe(struct platform_device *op)
dev_set_drvdata(dev, mtd);
/* Register device in MTD */
- retval = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+ retval = mtd_device_register(mtd, NULL, 0);
if (retval) {
dev_err(dev, "Error adding MTD device!\n");
goto error;
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index 136e73a3e07e..854c832597aa 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -173,7 +173,6 @@ struct mxc_nand_devtype_data {
};
struct mxc_nand_host {
- struct mtd_info mtd;
struct nand_chip nand;
struct device *dev;
@@ -532,8 +531,8 @@ static void send_addr_v1_v2(struct mxc_nand_host *host, uint16_t addr, int islas
static void send_page_v3(struct mtd_info *mtd, unsigned int ops)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct mxc_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
uint32_t tmp;
tmp = readl(NFC_V3_CONFIG1);
@@ -548,8 +547,8 @@ static void send_page_v3(struct mtd_info *mtd, unsigned int ops)
static void send_page_v2(struct mtd_info *mtd, unsigned int ops)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct mxc_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
/* NANDFC buffer 0 is used for page read/write */
writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
@@ -562,8 +561,8 @@ static void send_page_v2(struct mtd_info *mtd, unsigned int ops)
static void send_page_v1(struct mtd_info *mtd, unsigned int ops)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct mxc_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
int bufs, i;
if (mtd->writesize > 512)
@@ -663,8 +662,8 @@ static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode)
static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct mxc_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
/*
* 1-Bit errors are automatically corrected in HW. No need for
@@ -675,7 +674,7 @@ static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat,
if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) {
pr_debug("MXC_NAND: HWECC uncorrectable 2-bit ECC error\n");
- return -1;
+ return -EBADMSG;
}
return 0;
@@ -684,8 +683,8 @@ static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat,
static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct mxc_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
u32 ecc_stat, err;
int no_subpages = 1;
int ret = 0;
@@ -702,7 +701,7 @@ static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat,
err = ecc_stat & ecc_bit_mask;
if (err > err_limit) {
printk(KERN_WARNING "UnCorrectable RS-ECC Error\n");
- return -1;
+ return -EBADMSG;
} else {
ret += err;
}
@@ -722,8 +721,8 @@ static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
static u_char mxc_nand_read_byte(struct mtd_info *mtd)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct mxc_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
uint8_t ret;
/* Check for status request */
@@ -746,8 +745,8 @@ static u_char mxc_nand_read_byte(struct mtd_info *mtd)
static uint16_t mxc_nand_read_word(struct mtd_info *mtd)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct mxc_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
uint16_t ret;
ret = *(uint16_t *)(host->data_buf + host->buf_start);
@@ -762,8 +761,8 @@ static uint16_t mxc_nand_read_word(struct mtd_info *mtd)
static void mxc_nand_write_buf(struct mtd_info *mtd,
const u_char *buf, int len)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct mxc_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
u16 col = host->buf_start;
int n = mtd->oobsize + mtd->writesize - col;
@@ -780,8 +779,8 @@ static void mxc_nand_write_buf(struct mtd_info *mtd,
*/
static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct mxc_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
u16 col = host->buf_start;
int n = mtd->oobsize + mtd->writesize - col;
@@ -796,8 +795,8 @@ static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
* deselect of the NAND chip */
static void mxc_nand_select_chip_v1_v3(struct mtd_info *mtd, int chip)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct mxc_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
if (chip == -1) {
/* Disable the NFC clock */
@@ -817,8 +816,8 @@ static void mxc_nand_select_chip_v1_v3(struct mtd_info *mtd, int chip)
static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct mxc_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
if (chip == -1) {
/* Disable the NFC clock */
@@ -850,8 +849,8 @@ static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip)
*/
static void copy_spare(struct mtd_info *mtd, bool bfrom)
{
- struct nand_chip *this = mtd->priv;
- struct mxc_nand_host *host = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(this);
u16 i, oob_chunk_size;
u16 num_chunks = mtd->writesize / 512;
@@ -893,8 +892,8 @@ static void copy_spare(struct mtd_info *mtd, bool bfrom)
*/
static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct mxc_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
/* Write out column address, if necessary */
if (column != -1) {
@@ -979,8 +978,8 @@ static void ecc_8bit_layout_4k(struct nand_ecclayout *layout)
static void preset_v1(struct mtd_info *mtd)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct mxc_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
uint16_t config1 = 0;
if (nand_chip->ecc.mode == NAND_ECC_HW && mtd->writesize)
@@ -1007,8 +1006,8 @@ static void preset_v1(struct mtd_info *mtd)
static void preset_v2(struct mtd_info *mtd)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct mxc_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
uint16_t config1 = 0;
config1 |= NFC_V2_CONFIG1_FP_INT;
@@ -1053,8 +1052,8 @@ static void preset_v2(struct mtd_info *mtd)
static void preset_v3(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct mxc_nand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(chip);
uint32_t config2, config3;
int i, addr_phases;
@@ -1067,8 +1066,7 @@ static void preset_v3(struct mtd_info *mtd)
/* Blocks to be unlocked */
for (i = 0; i < NAND_MAX_CHIPS; i++)
- writel(0x0 | (0xffff << 16),
- NFC_V3_WRPROT_UNLOCK_BLK_ADD0 + (i << 2));
+ writel(0xffff << 16, NFC_V3_WRPROT_UNLOCK_BLK_ADD0 + (i << 2));
writel(0, NFC_V3_IPC);
@@ -1125,8 +1123,8 @@ static void preset_v3(struct mtd_info *mtd)
static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
int column, int page_addr)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct mxc_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
pr_debug("mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n",
command, column, page_addr);
@@ -1515,15 +1513,15 @@ static int mxcnd_probe(struct platform_device *pdev)
host->dev = &pdev->dev;
/* structures must be linked */
this = &host->nand;
- mtd = &host->mtd;
- mtd->priv = this;
+ mtd = nand_to_mtd(this);
mtd->dev.parent = &pdev->dev;
mtd->name = DRIVER_NAME;
/* 50 us command delay time */
this->chip_delay = 5;
- this->priv = host;
+ nand_set_controller_data(this, host);
+ nand_set_flash_node(this, pdev->dev.of_node),
this->dev_ready = mxc_nand_dev_ready;
this->cmdfunc = mxc_nand_command;
this->read_byte = mxc_nand_read_byte;
@@ -1683,9 +1681,7 @@ static int mxcnd_probe(struct platform_device *pdev)
/* Register the partitions */
mtd_device_parse_register(mtd, part_probes,
- &(struct mtd_part_parser_data){
- .of_node = pdev->dev.of_node,
- },
+ NULL,
host->pdata.parts,
host->pdata.nr_parts);
@@ -1704,7 +1700,7 @@ static int mxcnd_remove(struct platform_device *pdev)
{
struct mxc_nand_host *host = platform_get_drvdata(pdev);
- nand_release(&host->mtd);
+ nand_release(nand_to_mtd(&host->nand));
if (host->clk_act)
clk_disable_unprepare(host->clk);
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index ece544efccc3..f2c8ff398d6c 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -106,7 +106,7 @@ DEFINE_LED_TRIGGER(nand_led_trigger);
static int check_offs_len(struct mtd_info *mtd,
loff_t ofs, uint64_t len)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
int ret = 0;
/* Start address must align on block boundary */
@@ -132,7 +132,7 @@ static int check_offs_len(struct mtd_info *mtd,
*/
static void nand_release_device(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
/* Release the controller and the chip */
spin_lock(&chip->controller->lock);
@@ -150,7 +150,7 @@ static void nand_release_device(struct mtd_info *mtd)
*/
static uint8_t nand_read_byte(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
return readb(chip->IO_ADDR_R);
}
@@ -163,7 +163,7 @@ static uint8_t nand_read_byte(struct mtd_info *mtd)
*/
static uint8_t nand_read_byte16(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R));
}
@@ -175,7 +175,7 @@ static uint8_t nand_read_byte16(struct mtd_info *mtd)
*/
static u16 nand_read_word(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
return readw(chip->IO_ADDR_R);
}
@@ -188,7 +188,7 @@ static u16 nand_read_word(struct mtd_info *mtd)
*/
static void nand_select_chip(struct mtd_info *mtd, int chipnr)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
switch (chipnr) {
case -1:
@@ -211,7 +211,7 @@ static void nand_select_chip(struct mtd_info *mtd, int chipnr)
*/
static void nand_write_byte(struct mtd_info *mtd, uint8_t byte)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
chip->write_buf(mtd, &byte, 1);
}
@@ -225,7 +225,7 @@ static void nand_write_byte(struct mtd_info *mtd, uint8_t byte)
*/
static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
uint16_t word = byte;
/*
@@ -257,7 +257,7 @@ static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte)
*/
static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
iowrite8_rep(chip->IO_ADDR_W, buf, len);
}
@@ -272,7 +272,7 @@ static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
*/
static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
ioread8_rep(chip->IO_ADDR_R, buf, len);
}
@@ -287,7 +287,7 @@ static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
*/
static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
u16 *p = (u16 *) buf;
iowrite16_rep(chip->IO_ADDR_W, p, len >> 1);
@@ -303,7 +303,7 @@ static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
*/
static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
u16 *p = (u16 *) buf;
ioread16_rep(chip->IO_ADDR_R, p, len >> 1);
@@ -320,7 +320,7 @@ static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
{
int page, chipnr, res = 0, i = 0;
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
u16 bad;
if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
@@ -380,7 +380,7 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
*/
static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
struct mtd_oob_ops ops;
uint8_t buf[2] = { 0, 0 };
int ret = 0, res, i = 0;
@@ -430,7 +430,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
*/
static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
int res, ret = 0;
if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) {
@@ -471,7 +471,7 @@ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
*/
static int nand_check_wp(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
/* Broken xD cards report WP despite being writable */
if (chip->options & NAND_BROKEN_XD)
@@ -491,7 +491,7 @@ static int nand_check_wp(struct mtd_info *mtd)
*/
static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
if (!chip->bbt)
return 0;
@@ -512,7 +512,7 @@ static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)
static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
int allowbbt)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
if (!chip->bbt)
return chip->block_bad(mtd, ofs, getchip);
@@ -531,7 +531,7 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
*/
static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
int i;
/* Wait for the device to get ready */
@@ -551,7 +551,7 @@ static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo)
*/
void nand_wait_ready(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
unsigned long timeo = 400;
if (in_interrupt() || oops_in_progress)
@@ -582,7 +582,7 @@ EXPORT_SYMBOL_GPL(nand_wait_ready);
*/
static void nand_wait_status_ready(struct mtd_info *mtd, unsigned long timeo)
{
- register struct nand_chip *chip = mtd->priv;
+ register struct nand_chip *chip = mtd_to_nand(mtd);
timeo = jiffies + msecs_to_jiffies(timeo);
do {
@@ -605,7 +605,7 @@ static void nand_wait_status_ready(struct mtd_info *mtd, unsigned long timeo)
static void nand_command(struct mtd_info *mtd, unsigned int command,
int column, int page_addr)
{
- register struct nand_chip *chip = mtd->priv;
+ register struct nand_chip *chip = mtd_to_nand(mtd);
int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE;
/* Write out the command to the device */
@@ -708,7 +708,7 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
int column, int page_addr)
{
- register struct nand_chip *chip = mtd->priv;
+ register struct nand_chip *chip = mtd_to_nand(mtd);
/* Emulate NAND_CMD_READOOB */
if (command == NAND_CMD_READOOB) {
@@ -832,7 +832,7 @@ static void panic_nand_get_device(struct nand_chip *chip,
static int
nand_get_device(struct mtd_info *mtd, int new_state)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
spinlock_t *lock = &chip->controller->lock;
wait_queue_head_t *wq = &chip->controller->wq;
DECLARE_WAITQUEUE(wait, current);
@@ -952,7 +952,7 @@ static int __nand_unlock(struct mtd_info *mtd, loff_t ofs,
{
int ret = 0;
int status, page;
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
/* Submit address of first page to unlock */
page = ofs >> chip->page_shift;
@@ -987,7 +987,7 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
int ret = 0;
int chipnr;
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
pr_debug("%s: start = 0x%012llx, len = %llu\n",
__func__, (unsigned long long)ofs, len);
@@ -1050,7 +1050,7 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
int ret = 0;
int chipnr, status, page;
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
pr_debug("%s: start = 0x%012llx, len = %llu\n",
__func__, (unsigned long long)ofs, len);
@@ -1426,6 +1426,16 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
stat = chip->ecc.correct(mtd, p,
&chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
+ if (stat == -EBADMSG &&
+ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
+ /* check for empty pages with bitflips */
+ stat = nand_check_erased_ecc_chunk(p, chip->ecc.size,
+ &chip->buffers->ecccode[i],
+ chip->ecc.bytes,
+ NULL, 0,
+ chip->ecc.strength);
+ }
+
if (stat < 0) {
mtd->ecc_stats.failed++;
} else {
@@ -1475,6 +1485,15 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
int stat;
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+ if (stat == -EBADMSG &&
+ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
+ /* check for empty pages with bitflips */
+ stat = nand_check_erased_ecc_chunk(p, eccsize,
+ &ecc_code[i], eccbytes,
+ NULL, 0,
+ chip->ecc.strength);
+ }
+
if (stat < 0) {
mtd->ecc_stats.failed++;
} else {
@@ -1527,6 +1546,15 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL);
+ if (stat == -EBADMSG &&
+ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
+ /* check for empty pages with bitflips */
+ stat = nand_check_erased_ecc_chunk(p, eccsize,
+ &ecc_code[i], eccbytes,
+ NULL, 0,
+ chip->ecc.strength);
+ }
+
if (stat < 0) {
mtd->ecc_stats.failed++;
} else {
@@ -1554,6 +1582,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
+ int eccpadbytes = eccbytes + chip->ecc.prepad + chip->ecc.postpad;
uint8_t *p = buf;
uint8_t *oob = chip->oob_poi;
unsigned int max_bitflips = 0;
@@ -1573,19 +1602,29 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
chip->read_buf(mtd, oob, eccbytes);
stat = chip->ecc.correct(mtd, p, oob, NULL);
- if (stat < 0) {
- mtd->ecc_stats.failed++;
- } else {
- mtd->ecc_stats.corrected += stat;
- max_bitflips = max_t(unsigned int, max_bitflips, stat);
- }
-
oob += eccbytes;
if (chip->ecc.postpad) {
chip->read_buf(mtd, oob, chip->ecc.postpad);
oob += chip->ecc.postpad;
}
+
+ if (stat == -EBADMSG &&
+ (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
+ /* check for empty pages with bitflips */
+ stat = nand_check_erased_ecc_chunk(p, chip->ecc.size,
+ oob - eccpadbytes,
+ eccpadbytes,
+ NULL, 0,
+ chip->ecc.strength);
+ }
+
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
}
/* Calculate remaining oob bytes */
@@ -1655,7 +1694,7 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
*/
static int nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
pr_debug("setting READ RETRY mode %d\n", retry_mode);
@@ -1680,7 +1719,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
int chipnr, page, realpage, col, bytes, aligned, oob_required;
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
int ret = 0;
uint32_t readlen = ops->len;
uint32_t oobreadlen = ops->ooblen;
@@ -2024,7 +2063,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
int page, realpage, chipnr;
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
struct mtd_ecc_stats stats;
int readlen = ops->ooblen;
int len;
@@ -2472,7 +2511,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
struct mtd_oob_ops *ops)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
/*
* Initialise to all 0xFF, to avoid the possibility of left over OOB
@@ -2532,7 +2571,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
int chipnr, realpage, page, blockmask, column;
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
uint32_t writelen = ops->len;
uint32_t oobwritelen = ops->ooblen;
@@ -2662,7 +2701,7 @@ err_out:
static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const uint8_t *buf)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
struct mtd_oob_ops ops;
int ret;
@@ -2722,7 +2761,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
int chipnr, page, status, len;
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
pr_debug("%s: to = 0x%08x, len = %i\n",
__func__, (unsigned int)to, (int)ops->ooblen);
@@ -2847,7 +2886,7 @@ out:
*/
static int single_erase(struct mtd_info *mtd, int page)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
/* Send commands to erase a block */
chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
@@ -2879,7 +2918,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
int allowbbt)
{
int page, status, pages_per_block, ret, chipnr;
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
loff_t len;
pr_debug("%s: start = 0x%012llx, len = %llu\n",
@@ -3094,7 +3133,7 @@ static int nand_suspend(struct mtd_info *mtd)
*/
static void nand_resume(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
if (chip->state == FL_PM_SUSPENDED)
nand_release_device(mtd);
@@ -3266,7 +3305,7 @@ ext_out:
static int nand_setup_read_retry_micron(struct mtd_info *mtd, int retry_mode)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode};
return chip->onfi_set_features(mtd, chip, ONFI_FEATURE_ADDR_READ_RETRY,
@@ -3937,11 +3976,14 @@ ident_done:
return type;
}
-static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip,
- struct device_node *dn)
+static int nand_dt_init(struct nand_chip *chip)
{
+ struct device_node *dn = nand_get_flash_node(chip);
int ecc_mode, ecc_strength, ecc_step;
+ if (!dn)
+ return 0;
+
if (of_get_nand_bus_width(dn) == 16)
chip->options |= NAND_BUSWIDTH_16;
@@ -3985,15 +4027,16 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
struct nand_flash_dev *table)
{
int i, nand_maf_id, nand_dev_id;
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
struct nand_flash_dev *type;
int ret;
- if (chip->flash_node) {
- ret = nand_dt_init(mtd, chip, chip->flash_node);
- if (ret)
- return ret;
- }
+ ret = nand_dt_init(chip);
+ if (ret)
+ return ret;
+
+ if (!mtd->name && mtd->dev.parent)
+ mtd->name = dev_name(mtd->dev.parent);
/* Set the default functions */
nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);
@@ -4053,7 +4096,7 @@ EXPORT_SYMBOL(nand_scan_ident);
*/
static bool nand_ecc_strength_good(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &chip->ecc;
int corr, ds_corr;
@@ -4082,7 +4125,7 @@ static bool nand_ecc_strength_good(struct mtd_info *mtd)
int nand_scan_tail(struct mtd_info *mtd)
{
int i;
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &chip->ecc;
struct nand_buffers *nbuf;
@@ -4166,7 +4209,7 @@ int nand_scan_tail(struct mtd_info *mtd)
ecc->write_oob = nand_write_oob_std;
if (!ecc->read_subpage)
ecc->read_subpage = nand_read_subpage;
- if (!ecc->write_subpage)
+ if (!ecc->write_subpage && ecc->hwctl && ecc->calculate)
ecc->write_subpage = nand_write_subpage_hwecc;
case NAND_ECC_HW_SYNDROME:
@@ -4426,7 +4469,7 @@ EXPORT_SYMBOL(nand_scan);
*/
void nand_release(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
if (chip->ecc.mode == NAND_ECC_SOFT_BCH)
nand_bch_free((struct nand_bch_control *)chip->ecc.priv);
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index b1d4f813aedc..4b6a7085b442 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/nand_bbt.c
@@ -172,7 +172,7 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
struct nand_bbt_descr *td, int offs)
{
int res, ret = 0, i, j, act = 0;
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
size_t retlen, len, totlen;
loff_t from;
int bits = td->options & NAND_BBT_NRBITS_MSK;
@@ -263,7 +263,7 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
*/
static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
int res = 0, i;
if (td->options & NAND_BBT_PERCHIP) {
@@ -388,7 +388,7 @@ static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td)
static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
struct nand_bbt_descr *td, struct nand_bbt_descr *md)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
/* Read the primary version, if available */
if (td->options & NAND_BBT_VERSION) {
@@ -454,7 +454,7 @@ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
struct nand_bbt_descr *bd, int chip)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
int i, numblocks, numpages;
int startblock;
loff_t from;
@@ -523,7 +523,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
*/
static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
int i, chips;
int startblock, block, dir;
int scanlen = mtd->writesize + mtd->oobsize;
@@ -618,7 +618,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
struct nand_bbt_descr *td, struct nand_bbt_descr *md,
int chipsel)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
struct erase_info einfo;
int i, res, chip = 0;
int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
@@ -819,7 +819,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
*/
static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
return create_bbt(mtd, this->buffers->databuf, bd, -1);
}
@@ -838,7 +838,7 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b
static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
{
int i, chips, writeops, create, chipsel, res, res2;
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
struct nand_bbt_descr *td = this->bbt_td;
struct nand_bbt_descr *md = this->bbt_md;
struct nand_bbt_descr *rd, *rd2;
@@ -962,7 +962,7 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
*/
static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
int i, j, chips, block, nrblocks, update;
uint8_t oldval;
@@ -1022,7 +1022,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
*/
static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
u32 pattern_len;
u32 bits;
u32 table_size;
@@ -1074,7 +1074,7 @@ static void verify_bbt_descr(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;
+ struct nand_chip *this = mtd_to_nand(mtd);
int len, res;
uint8_t *buf;
struct nand_bbt_descr *td = this->bbt_td;
@@ -1147,7 +1147,7 @@ err:
*/
static int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
int len, res = 0;
int chip, chipsel;
uint8_t *buf;
@@ -1281,7 +1281,7 @@ static int nand_create_badblock_pattern(struct nand_chip *this)
*/
int nand_default_bbt(struct mtd_info *mtd)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
int ret;
/* Is a flash based bad block table requested? */
@@ -1317,7 +1317,7 @@ int nand_default_bbt(struct mtd_info *mtd)
*/
int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
int block;
block = (int)(offs >> this->bbt_erase_shift);
@@ -1332,7 +1332,7 @@ int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs)
*/
int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
int block, res;
block = (int)(offs >> this->bbt_erase_shift);
@@ -1359,7 +1359,7 @@ int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
*/
int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
int block, ret = 0;
block = (int)(offs >> this->bbt_erase_shift);
diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c
index 3803e0bba23b..a87c1b628dfc 100644
--- a/drivers/mtd/nand/nand_bch.c
+++ b/drivers/mtd/nand/nand_bch.c
@@ -52,7 +52,7 @@ struct nand_bch_control {
int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
unsigned char *code)
{
- const struct nand_chip *chip = mtd->priv;
+ const struct nand_chip *chip = mtd_to_nand(mtd);
struct nand_bch_control *nbc = chip->ecc.priv;
unsigned int i;
@@ -79,7 +79,7 @@ EXPORT_SYMBOL(nand_bch_calculate_ecc);
int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf,
unsigned char *read_ecc, unsigned char *calc_ecc)
{
- const struct nand_chip *chip = mtd->priv;
+ const struct nand_chip *chip = mtd_to_nand(mtd);
struct nand_bch_control *nbc = chip->ecc.priv;
unsigned int *errloc = nbc->errloc;
int i, count;
@@ -98,7 +98,7 @@ int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf,
}
} else if (count < 0) {
printk(KERN_ERR "ecc unrecoverable error\n");
- count = -1;
+ count = -EBADMSG;
}
return count;
}
diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c
index 97c4c0216c90..d1770b066396 100644
--- a/drivers/mtd/nand/nand_ecc.c
+++ b/drivers/mtd/nand/nand_ecc.c
@@ -424,7 +424,7 @@ int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
unsigned char *code)
{
__nand_calculate_ecc(buf,
- ((struct nand_chip *)mtd->priv)->ecc.size, code);
+ mtd_to_nand(mtd)->ecc.size, code);
return 0;
}
@@ -507,7 +507,7 @@ int __nand_correct_data(unsigned char *buf,
return 1; /* error in ECC data; no action needed */
pr_err("%s: uncorrectable ECC error\n", __func__);
- return -1;
+ return -EBADMSG;
}
EXPORT_SYMBOL(__nand_correct_data);
@@ -524,7 +524,7 @@ int nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
unsigned char *read_ecc, unsigned char *calc_ecc)
{
return __nand_correct_data(buf, read_ecc, calc_ecc,
- ((struct nand_chip *)mtd->priv)->ecc.size);
+ mtd_to_nand(mtd)->ecc.size);
}
EXPORT_SYMBOL(nand_correct_data);
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
index b16d70aafd9e..1fd519503bb1 100644
--- a/drivers/mtd/nand/nandsim.c
+++ b/drivers/mtd/nand/nandsim.c
@@ -666,8 +666,8 @@ static char *get_partition_name(int i)
*/
static int init_nandsim(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct nandsim *ns = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nandsim *ns = nand_get_controller_data(chip);
int i, ret = 0;
uint64_t remains;
uint64_t next_offset;
@@ -1908,7 +1908,8 @@ static void switch_state(struct nandsim *ns)
static u_char ns_nand_read_byte(struct mtd_info *mtd)
{
- struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nandsim *ns = nand_get_controller_data(chip);
u_char outb = 0x00;
/* Sanity and correctness checks */
@@ -1969,7 +1970,8 @@ static u_char ns_nand_read_byte(struct mtd_info *mtd)
static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
{
- struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nandsim *ns = nand_get_controller_data(chip);
/* Sanity and correctness checks */
if (!ns->lines.ce) {
@@ -2123,7 +2125,8 @@ static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
static void ns_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int bitmask)
{
- struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nandsim *ns = nand_get_controller_data(chip);
ns->lines.cle = bitmask & NAND_CLE ? 1 : 0;
ns->lines.ale = bitmask & NAND_ALE ? 1 : 0;
@@ -2141,7 +2144,7 @@ static int ns_device_ready(struct mtd_info *mtd)
static uint16_t ns_nand_read_word(struct mtd_info *mtd)
{
- struct nand_chip *chip = (struct nand_chip *)mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
NS_DBG("read_word\n");
@@ -2150,7 +2153,8 @@ static uint16_t ns_nand_read_word(struct mtd_info *mtd)
static void ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
- struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nandsim *ns = nand_get_controller_data(chip);
/* Check that chip is expecting data input */
if (!(ns->state & STATE_DATAIN_MASK)) {
@@ -2177,7 +2181,8 @@ static void ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
- struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nandsim *ns = nand_get_controller_data(chip);
/* Sanity and correctness checks */
if (!ns->lines.ce) {
@@ -2198,7 +2203,7 @@ static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
int i;
for (i = 0; i < len; i++)
- buf[i] = ((struct nand_chip *)mtd->priv)->read_byte(mtd);
+ buf[i] = mtd_to_nand(mtd)->read_byte(mtd);
return;
}
@@ -2236,16 +2241,15 @@ static int __init ns_init_module(void)
}
/* Allocate and initialize mtd_info, nand_chip and nandsim structures */
- nsmtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip)
- + sizeof(struct nandsim), GFP_KERNEL);
- if (!nsmtd) {
+ chip = kzalloc(sizeof(struct nand_chip) + sizeof(struct nandsim),
+ GFP_KERNEL);
+ if (!chip) {
NS_ERR("unable to allocate core structures.\n");
return -ENOMEM;
}
- chip = (struct nand_chip *)(nsmtd + 1);
- nsmtd->priv = (void *)chip;
+ nsmtd = nand_to_mtd(chip);
nand = (struct nandsim *)(chip + 1);
- chip->priv = (void *)nand;
+ nand_set_controller_data(chip, (void *)nand);
/*
* Register simulator's callbacks.
@@ -2392,7 +2396,7 @@ err_exit:
for (i = 0;i < ARRAY_SIZE(nand->partitions); ++i)
kfree(nand->partitions[i].name);
error:
- kfree(nsmtd);
+ kfree(chip);
free_lists();
return retval;
@@ -2405,7 +2409,8 @@ module_init(ns_init_module);
*/
static void __exit ns_cleanup_module(void)
{
- struct nandsim *ns = ((struct nand_chip *)nsmtd->priv)->priv;
+ struct nand_chip *chip = mtd_to_nand(nsmtd);
+ struct nandsim *ns = nand_get_controller_data(chip);
int i;
nandsim_debugfs_remove(ns);
@@ -2413,7 +2418,7 @@ static void __exit ns_cleanup_module(void)
nand_release(nsmtd); /* Unregister driver */
for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i)
kfree(ns->partitions[i].name);
- kfree(nsmtd); /* Free other structures */
+ kfree(mtd_to_nand(nsmtd)); /* Free other structures */
free_lists();
}
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c
index 4f0d62f9d22c..218c789ca7ab 100644
--- a/drivers/mtd/nand/ndfc.c
+++ b/drivers/mtd/nand/ndfc.c
@@ -37,7 +37,6 @@
struct ndfc_controller {
struct platform_device *ofdev;
void __iomem *ndfcbase;
- struct mtd_info mtd;
struct nand_chip chip;
int chip_select;
struct nand_hw_control ndfc_control;
@@ -48,8 +47,8 @@ static struct ndfc_controller ndfc_ctrl[NDFC_MAX_CS];
static void ndfc_select_chip(struct mtd_info *mtd, int chip)
{
uint32_t ccr;
- struct nand_chip *nchip = mtd->priv;
- struct ndfc_controller *ndfc = nchip->priv;
+ struct nand_chip *nchip = mtd_to_nand(mtd);
+ struct ndfc_controller *ndfc = nand_get_controller_data(nchip);
ccr = in_be32(ndfc->ndfcbase + NDFC_CCR);
if (chip >= 0) {
@@ -62,8 +61,8 @@ static void ndfc_select_chip(struct mtd_info *mtd, int chip)
static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
- struct nand_chip *chip = mtd->priv;
- struct ndfc_controller *ndfc = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct ndfc_controller *ndfc = nand_get_controller_data(chip);
if (cmd == NAND_CMD_NONE)
return;
@@ -76,8 +75,8 @@ static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
static int ndfc_ready(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct ndfc_controller *ndfc = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct ndfc_controller *ndfc = nand_get_controller_data(chip);
return in_be32(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY;
}
@@ -85,8 +84,8 @@ static int ndfc_ready(struct mtd_info *mtd)
static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode)
{
uint32_t ccr;
- struct nand_chip *chip = mtd->priv;
- struct ndfc_controller *ndfc = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct ndfc_controller *ndfc = nand_get_controller_data(chip);
ccr = in_be32(ndfc->ndfcbase + NDFC_CCR);
ccr |= NDFC_CCR_RESET_ECC;
@@ -97,8 +96,8 @@ static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode)
static int ndfc_calculate_ecc(struct mtd_info *mtd,
const u_char *dat, u_char *ecc_code)
{
- struct nand_chip *chip = mtd->priv;
- struct ndfc_controller *ndfc = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct ndfc_controller *ndfc = nand_get_controller_data(chip);
uint32_t ecc;
uint8_t *p = (uint8_t *)&ecc;
@@ -121,8 +120,8 @@ static int ndfc_calculate_ecc(struct mtd_info *mtd,
*/
static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
- struct ndfc_controller *ndfc = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct ndfc_controller *ndfc = nand_get_controller_data(chip);
uint32_t *p = (uint32_t *) buf;
for(;len > 0; len -= 4)
@@ -131,8 +130,8 @@ static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
- struct ndfc_controller *ndfc = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct ndfc_controller *ndfc = nand_get_controller_data(chip);
uint32_t *p = (uint32_t *) buf;
for(;len > 0; len -= 4)
@@ -147,7 +146,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc,
{
struct device_node *flash_np;
struct nand_chip *chip = &ndfc->chip;
- struct mtd_part_parser_data ppdata;
+ struct mtd_info *mtd = nand_to_mtd(chip);
int ret;
chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA;
@@ -166,33 +165,32 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc,
chip->ecc.size = 256;
chip->ecc.bytes = 3;
chip->ecc.strength = 1;
- chip->priv = ndfc;
+ nand_set_controller_data(chip, ndfc);
- ndfc->mtd.priv = chip;
- ndfc->mtd.dev.parent = &ndfc->ofdev->dev;
+ mtd->dev.parent = &ndfc->ofdev->dev;
flash_np = of_get_next_child(node, NULL);
if (!flash_np)
return -ENODEV;
+ nand_set_flash_node(chip, flash_np);
- ppdata.of_node = flash_np;
- ndfc->mtd.name = kasprintf(GFP_KERNEL, "%s.%s",
- dev_name(&ndfc->ofdev->dev), flash_np->name);
- if (!ndfc->mtd.name) {
+ mtd->name = kasprintf(GFP_KERNEL, "%s.%s", dev_name(&ndfc->ofdev->dev),
+ flash_np->name);
+ if (!mtd->name) {
ret = -ENOMEM;
goto err;
}
- ret = nand_scan(&ndfc->mtd, 1);
+ ret = nand_scan(mtd, 1);
if (ret)
goto err;
- ret = mtd_device_parse_register(&ndfc->mtd, NULL, &ppdata, NULL, 0);
+ ret = mtd_device_register(mtd, NULL, 0);
err:
of_node_put(flash_np);
if (ret)
- kfree(ndfc->mtd.name);
+ kfree(mtd->name);
return ret;
}
@@ -259,9 +257,10 @@ static int ndfc_probe(struct platform_device *ofdev)
static int ndfc_remove(struct platform_device *ofdev)
{
struct ndfc_controller *ndfc = dev_get_drvdata(&ofdev->dev);
+ struct mtd_info *mtd = nand_to_mtd(&ndfc->chip);
- nand_release(&ndfc->mtd);
- kfree(ndfc->mtd.name);
+ nand_release(mtd);
+ kfree(mtd->name);
return 0;
}
diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c
index f0687f71fbd8..220ddfcf29f5 100644
--- a/drivers/mtd/nand/nuc900_nand.c
+++ b/drivers/mtd/nand/nuc900_nand.c
@@ -55,13 +55,17 @@
__raw_writel((val), (dev)->reg + REG_SMADDR)
struct nuc900_nand {
- struct mtd_info mtd;
struct nand_chip chip;
void __iomem *reg;
struct clk *clk;
spinlock_t lock;
};
+static inline struct nuc900_nand *mtd_to_nuc900(struct mtd_info *mtd)
+{
+ return container_of(mtd_to_nand(mtd), struct nuc900_nand, chip);
+}
+
static const struct mtd_partition partitions[] = {
{
.name = "NAND FS 0",
@@ -78,9 +82,7 @@ static const struct mtd_partition partitions[] = {
static unsigned char nuc900_nand_read_byte(struct mtd_info *mtd)
{
unsigned char ret;
- struct nuc900_nand *nand;
-
- nand = container_of(mtd, struct nuc900_nand, mtd);
+ struct nuc900_nand *nand = mtd_to_nuc900(mtd);
ret = (unsigned char)read_data_reg(nand);
@@ -91,9 +93,7 @@ static void nuc900_nand_read_buf(struct mtd_info *mtd,
unsigned char *buf, int len)
{
int i;
- struct nuc900_nand *nand;
-
- nand = container_of(mtd, struct nuc900_nand, mtd);
+ struct nuc900_nand *nand = mtd_to_nuc900(mtd);
for (i = 0; i < len; i++)
buf[i] = (unsigned char)read_data_reg(nand);
@@ -103,9 +103,7 @@ static void nuc900_nand_write_buf(struct mtd_info *mtd,
const unsigned char *buf, int len)
{
int i;
- struct nuc900_nand *nand;
-
- nand = container_of(mtd, struct nuc900_nand, mtd);
+ struct nuc900_nand *nand = mtd_to_nuc900(mtd);
for (i = 0; i < len; i++)
write_data_reg(nand, buf[i]);
@@ -124,11 +122,9 @@ static int nuc900_check_rb(struct nuc900_nand *nand)
static int nuc900_nand_devready(struct mtd_info *mtd)
{
- struct nuc900_nand *nand;
+ struct nuc900_nand *nand = mtd_to_nuc900(mtd);
int ready;
- nand = container_of(mtd, struct nuc900_nand, mtd);
-
ready = (nuc900_check_rb(nand)) ? 1 : 0;
return ready;
}
@@ -136,10 +132,8 @@ static int nuc900_nand_devready(struct mtd_info *mtd)
static void nuc900_nand_command_lp(struct mtd_info *mtd, unsigned int command,
int column, int page_addr)
{
- register struct nand_chip *chip = mtd->priv;
- struct nuc900_nand *nand;
-
- nand = container_of(mtd, struct nuc900_nand, mtd);
+ register struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nuc900_nand *nand = mtd_to_nuc900(mtd);
if (command == NAND_CMD_READOOB) {
column += mtd->writesize;
@@ -241,6 +235,7 @@ static int nuc900_nand_probe(struct platform_device *pdev)
{
struct nuc900_nand *nuc900_nand;
struct nand_chip *chip;
+ struct mtd_info *mtd;
struct resource *res;
nuc900_nand = devm_kzalloc(&pdev->dev, sizeof(struct nuc900_nand),
@@ -248,9 +243,9 @@ static int nuc900_nand_probe(struct platform_device *pdev)
if (!nuc900_nand)
return -ENOMEM;
chip = &(nuc900_nand->chip);
+ mtd = nand_to_mtd(chip);
- nuc900_nand->mtd.priv = chip;
- nuc900_nand->mtd.dev.parent = &pdev->dev;
+ mtd->dev.parent = &pdev->dev;
spin_lock_init(&nuc900_nand->lock);
nuc900_nand->clk = devm_clk_get(&pdev->dev, NULL);
@@ -274,11 +269,10 @@ static int nuc900_nand_probe(struct platform_device *pdev)
nuc900_nand_enable(nuc900_nand);
- if (nand_scan(&(nuc900_nand->mtd), 1))
+ if (nand_scan(mtd, 1))
return -ENXIO;
- mtd_device_register(&(nuc900_nand->mtd), partitions,
- ARRAY_SIZE(partitions));
+ mtd_device_register(mtd, partitions, ARRAY_SIZE(partitions));
platform_set_drvdata(pdev, nuc900_nand);
@@ -289,7 +283,7 @@ static int nuc900_nand_remove(struct platform_device *pdev)
{
struct nuc900_nand *nuc900_nand = platform_get_drvdata(pdev);
- nand_release(&nuc900_nand->mtd);
+ nand_release(nand_to_mtd(&nuc900_nand->chip));
clk_disable(nuc900_nand->clk);
return 0;
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index 93f664cd1c90..c553f78ab83f 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -152,7 +152,6 @@ static struct nand_hw_control omap_gpmc_controller = {
struct omap_nand_info {
struct omap_nand_platform_data *pdata;
- struct mtd_info mtd;
struct nand_chip nand;
struct platform_device *pdev;
@@ -177,6 +176,11 @@ struct omap_nand_info {
struct device_node *of_node;
};
+static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd)
+{
+ return container_of(mtd_to_nand(mtd), struct omap_nand_info, nand);
+}
+
/**
* omap_prefetch_enable - configures and starts prefetch transfer
* @cs: cs (chip select) number
@@ -247,8 +251,7 @@ static int omap_prefetch_reset(int cs, struct omap_nand_info *info)
*/
static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
- struct omap_nand_info *info = container_of(mtd,
- struct omap_nand_info, mtd);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
if (cmd != NAND_CMD_NONE) {
if (ctrl & NAND_CLE)
@@ -270,7 +273,7 @@ static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
*/
static void omap_read_buf8(struct mtd_info *mtd, u_char *buf, int len)
{
- struct nand_chip *nand = mtd->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
ioread8_rep(nand->IO_ADDR_R, buf, len);
}
@@ -283,8 +286,7 @@ static void omap_read_buf8(struct mtd_info *mtd, u_char *buf, int len)
*/
static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len)
{
- struct omap_nand_info *info = container_of(mtd,
- struct omap_nand_info, mtd);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
u_char *p = (u_char *)buf;
u32 status = 0;
@@ -306,7 +308,7 @@ static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len)
*/
static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
{
- struct nand_chip *nand = mtd->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
ioread16_rep(nand->IO_ADDR_R, buf, len / 2);
}
@@ -319,8 +321,7 @@ static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
*/
static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
{
- struct omap_nand_info *info = container_of(mtd,
- struct omap_nand_info, mtd);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
u16 *p = (u16 *) buf;
u32 status = 0;
/* FIXME try bursts of writesw() or DMA ... */
@@ -344,8 +345,7 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
*/
static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len)
{
- struct omap_nand_info *info = container_of(mtd,
- struct omap_nand_info, mtd);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
uint32_t r_count = 0;
int ret = 0;
u32 *p = (u32 *)buf;
@@ -392,8 +392,7 @@ static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len)
static void omap_write_buf_pref(struct mtd_info *mtd,
const u_char *buf, int len)
{
- struct omap_nand_info *info = container_of(mtd,
- struct omap_nand_info, mtd);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
uint32_t w_count = 0;
int i = 0, ret = 0;
u16 *p = (u16 *)buf;
@@ -458,8 +457,7 @@ static void omap_nand_dma_callback(void *data)
static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
unsigned int len, int is_write)
{
- struct omap_nand_info *info = container_of(mtd,
- struct omap_nand_info, mtd);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
struct dma_async_tx_descriptor *tx;
enum dma_data_direction dir = is_write ? DMA_TO_DEVICE :
DMA_FROM_DEVICE;
@@ -623,8 +621,7 @@ done:
*/
static void omap_read_buf_irq_pref(struct mtd_info *mtd, u_char *buf, int len)
{
- struct omap_nand_info *info = container_of(mtd,
- struct omap_nand_info, mtd);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
int ret = 0;
if (len <= mtd->oobsize) {
@@ -671,8 +668,7 @@ out_copy:
static void omap_write_buf_irq_pref(struct mtd_info *mtd,
const u_char *buf, int len)
{
- struct omap_nand_info *info = container_of(mtd,
- struct omap_nand_info, mtd);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
int ret = 0;
unsigned long tim, limit;
u32 val;
@@ -830,12 +826,12 @@ static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
case 1:
/* Uncorrectable error */
pr_debug("ECC UNCORRECTED_ERROR 1\n");
- return -1;
+ return -EBADMSG;
case 11:
/* UN-Correctable error */
pr_debug("ECC UNCORRECTED_ERROR B\n");
- return -1;
+ return -EBADMSG;
case 12:
/* Correctable error */
@@ -865,7 +861,7 @@ static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
return 0;
}
pr_debug("UNCORRECTED_ERROR default\n");
- return -1;
+ return -EBADMSG;
}
}
@@ -886,8 +882,7 @@ static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
static int omap_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
- struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
- mtd);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
int blockCnt = 0, i = 0, ret = 0;
int stat = 0;
@@ -928,8 +923,7 @@ static int omap_correct_data(struct mtd_info *mtd, u_char *dat,
static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
u_char *ecc_code)
{
- struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
- mtd);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
u32 val;
val = readl(info->reg.gpmc_ecc_config);
@@ -953,9 +947,8 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
*/
static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
{
- struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
- mtd);
- struct nand_chip *chip = mtd->priv;
+ struct omap_nand_info *info = mtd_to_omap(mtd);
+ struct nand_chip *chip = mtd_to_nand(mtd);
unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
u32 val;
@@ -1001,9 +994,8 @@ static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
*/
static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip)
{
- struct nand_chip *this = mtd->priv;
- struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
- mtd);
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
unsigned long timeo = jiffies;
int status, state = this->state;
@@ -1031,8 +1023,7 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip)
static int omap_dev_ready(struct mtd_info *mtd)
{
unsigned int val = 0;
- struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
- mtd);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
val = readl(info->reg.gpmc_status);
@@ -1058,10 +1049,9 @@ static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode)
{
unsigned int bch_type;
unsigned int dev_width, nsectors;
- struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
- mtd);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
enum omap_ecc ecc_opt = info->ecc_opt;
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
u32 val, wr_mode;
unsigned int ecc_size1, ecc_size0;
@@ -1162,8 +1152,7 @@ static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2,
static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd,
const u_char *dat, u_char *ecc_calc)
{
- struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
- mtd);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
int eccbytes = info->nand.ecc.bytes;
struct gpmc_nand_regs *gpmc_regs = &info->reg;
u8 *ecc_code;
@@ -1334,8 +1323,7 @@ static int erased_sector_bitflips(u_char *data, u_char *oob,
static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
u_char *read_ecc, u_char *calc_ecc)
{
- struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
- mtd);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
struct nand_ecc_ctrl *ecc = &info->nand.ecc;
int eccsteps = info->nand.ecc.steps;
int i , j, stat = 0;
@@ -1663,7 +1651,6 @@ static int omap_nand_probe(struct platform_device *pdev)
unsigned sig;
unsigned oob_index;
struct resource *res;
- struct mtd_part_parser_data ppdata = {};
pdata = dev_get_platdata(&pdev->dev);
if (pdata == NULL) {
@@ -1683,11 +1670,11 @@ static int omap_nand_probe(struct platform_device *pdev)
info->reg = pdata->reg;
info->of_node = pdata->of_node;
info->ecc_opt = pdata->ecc_opt;
- mtd = &info->mtd;
- mtd->priv = &info->nand;
- mtd->dev.parent = &pdev->dev;
nand_chip = &info->nand;
+ mtd = nand_to_mtd(nand_chip);
+ mtd->dev.parent = &pdev->dev;
nand_chip->ecc.priv = NULL;
+ nand_set_flash_node(nand_chip, pdata->of_node);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
@@ -1909,7 +1896,7 @@ static int omap_nand_probe(struct platform_device *pdev)
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
err = elm_config(info->elm_dev, BCH4_ECC,
- info->mtd.writesize / nand_chip->ecc.size,
+ mtd->writesize / nand_chip->ecc.size,
nand_chip->ecc.size, nand_chip->ecc.bytes);
if (err < 0)
goto return_error;
@@ -1963,7 +1950,7 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.write_page = omap_write_page_bch;
err = elm_config(info->elm_dev, BCH8_ECC,
- info->mtd.writesize / nand_chip->ecc.size,
+ mtd->writesize / nand_chip->ecc.size,
nand_chip->ecc.size, nand_chip->ecc.bytes);
if (err < 0)
goto return_error;
@@ -1993,7 +1980,7 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.write_page = omap_write_page_bch;
err = elm_config(info->elm_dev, BCH16_ECC,
- info->mtd.writesize / nand_chip->ecc.size,
+ mtd->writesize / nand_chip->ecc.size,
nand_chip->ecc.size, nand_chip->ecc.bytes);
if (err < 0)
goto return_error;
@@ -2037,9 +2024,7 @@ scan_tail:
goto return_error;
}
- ppdata.of_node = pdata->of_node;
- mtd_device_parse_register(mtd, NULL, &ppdata, pdata->parts,
- pdata->nr_parts);
+ mtd_device_register(mtd, pdata->parts, pdata->nr_parts);
platform_set_drvdata(pdev, mtd);
@@ -2058,9 +2043,8 @@ return_error:
static int omap_nand_remove(struct platform_device *pdev)
{
struct mtd_info *mtd = platform_get_drvdata(pdev);
- struct nand_chip *nand_chip = mtd->priv;
- struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
- mtd);
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
if (nand_chip->ecc.priv) {
nand_bch_free(nand_chip->ecc.priv);
nand_chip->ecc.priv = NULL;
diff --git a/drivers/mtd/nand/omap_elm.c b/drivers/mtd/nand/omap_elm.c
index 235ec7992b4c..a3f32f939cc1 100644
--- a/drivers/mtd/nand/omap_elm.c
+++ b/drivers/mtd/nand/omap_elm.c
@@ -414,7 +414,7 @@ static int elm_probe(struct platform_device *pdev)
ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0,
pdev->name, info);
if (ret) {
- dev_err(&pdev->dev, "failure requesting irq %i\n", irq->start);
+ dev_err(&pdev->dev, "failure requesting %pr\n", irq);
return ret;
}
diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c
index ee83749fb1d3..d4614bfbfed6 100644
--- a/drivers/mtd/nand/orion_nand.c
+++ b/drivers/mtd/nand/orion_nand.c
@@ -25,8 +25,8 @@
static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
- struct nand_chip *nc = mtd->priv;
- struct orion_nand_data *board = nc->priv;
+ struct nand_chip *nc = mtd_to_nand(mtd);
+ struct orion_nand_data *board = nand_get_controller_data(nc);
u32 offs;
if (cmd == NAND_CMD_NONE)
@@ -47,7 +47,7 @@ static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl
static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
void __iomem *io_base = chip->IO_ADDR_R;
uint64_t *buf64;
int i = 0;
@@ -76,7 +76,6 @@ static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
static int __init orion_nand_probe(struct platform_device *pdev)
{
struct mtd_info *mtd;
- struct mtd_part_parser_data ppdata = {};
struct nand_chip *nc;
struct orion_nand_data *board;
struct resource *res;
@@ -86,11 +85,11 @@ static int __init orion_nand_probe(struct platform_device *pdev)
u32 val = 0;
nc = devm_kzalloc(&pdev->dev,
- sizeof(struct nand_chip) + sizeof(struct mtd_info),
+ sizeof(struct nand_chip),
GFP_KERNEL);
if (!nc)
return -ENOMEM;
- mtd = (struct mtd_info *)(nc + 1);
+ mtd = nand_to_mtd(nc);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
io_base = devm_ioremap_resource(&pdev->dev, res);
@@ -123,10 +122,10 @@ static int __init orion_nand_probe(struct platform_device *pdev)
board = dev_get_platdata(&pdev->dev);
}
- mtd->priv = nc;
mtd->dev.parent = &pdev->dev;
- nc->priv = board;
+ nand_set_controller_data(nc, board);
+ nand_set_flash_node(nc, pdev->dev.of_node);
nc->IO_ADDR_R = nc->IO_ADDR_W = io_base;
nc->cmd_ctrl = orion_nand_cmd_ctrl;
nc->read_buf = orion_nand_read_buf;
@@ -161,9 +160,7 @@ static int __init orion_nand_probe(struct platform_device *pdev)
}
mtd->name = "orion_nand";
- ppdata.of_node = pdev->dev.of_node;
- ret = mtd_device_parse_register(mtd, NULL, &ppdata,
- board->parts, board->nr_parts);
+ ret = mtd_device_register(mtd, board->parts, board->nr_parts);
if (ret) {
nand_release(mtd);
goto no_dev;
diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c
index 83cf021b9651..3ab53ca53cca 100644
--- a/drivers/mtd/nand/pasemi_nand.c
+++ b/drivers/mtd/nand/pasemi_nand.c
@@ -45,7 +45,7 @@ static const char driver_name[] = "pasemi-nand";
static void pasemi_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
while (len > 0x800) {
memcpy_fromio(buf, chip->IO_ADDR_R, 0x800);
@@ -57,7 +57,7 @@ static void pasemi_read_buf(struct mtd_info *mtd, u_char *buf, int len)
static void pasemi_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
while (len > 0x800) {
memcpy_toio(chip->IO_ADDR_R, buf, 0x800);
@@ -70,7 +70,7 @@ static void pasemi_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
static void pasemi_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
if (cmd == NAND_CMD_NONE)
return;
@@ -110,20 +110,17 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
pr_debug("pasemi_nand at %pR\n", &res);
/* Allocate memory for MTD device structure and private data */
- pasemi_nand_mtd = kzalloc(sizeof(struct mtd_info) +
- sizeof(struct nand_chip), GFP_KERNEL);
- if (!pasemi_nand_mtd) {
+ chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
+ if (!chip) {
printk(KERN_WARNING
"Unable to allocate PASEMI NAND MTD device structure\n");
err = -ENOMEM;
goto out;
}
- /* Get pointer to private data */
- chip = (struct nand_chip *)&pasemi_nand_mtd[1];
+ pasemi_nand_mtd = nand_to_mtd(chip);
/* Link the private data with the MTD structure */
- pasemi_nand_mtd->priv = chip;
pasemi_nand_mtd->dev.parent = &ofdev->dev;
chip->IO_ADDR_R = of_iomap(np, 0);
@@ -180,7 +177,7 @@ static int pasemi_nand_probe(struct platform_device *ofdev)
out_ior:
iounmap(chip->IO_ADDR_R);
out_mtd:
- kfree(pasemi_nand_mtd);
+ kfree(chip);
out:
return err;
}
@@ -192,7 +189,7 @@ static int pasemi_nand_remove(struct platform_device *ofdev)
if (!pasemi_nand_mtd)
return 0;
- chip = pasemi_nand_mtd->priv;
+ chip = mtd_to_nand(pasemi_nand_mtd);
/* Release resources, unregister device */
nand_release(pasemi_nand_mtd);
@@ -202,7 +199,7 @@ static int pasemi_nand_remove(struct platform_device *ofdev)
iounmap(chip->IO_ADDR_R);
/* Free the MTD device structure */
- kfree(pasemi_nand_mtd);
+ kfree(chip);
pasemi_nand_mtd = NULL;
diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c
index 65b9dbbe6d6a..a0e26dea1424 100644
--- a/drivers/mtd/nand/plat_nand.c
+++ b/drivers/mtd/nand/plat_nand.c
@@ -20,7 +20,6 @@
struct plat_nand_data {
struct nand_chip chip;
- struct mtd_info mtd;
void __iomem *io_base;
};
@@ -30,8 +29,8 @@ struct plat_nand_data {
static int plat_nand_probe(struct platform_device *pdev)
{
struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev);
- struct mtd_part_parser_data ppdata;
struct plat_nand_data *data;
+ struct mtd_info *mtd;
struct resource *res;
const char **part_types;
int err = 0;
@@ -57,9 +56,9 @@ static int plat_nand_probe(struct platform_device *pdev)
if (IS_ERR(data->io_base))
return PTR_ERR(data->io_base);
- data->chip.priv = &data;
- data->mtd.priv = &data->chip;
- data->mtd.dev.parent = &pdev->dev;
+ nand_set_flash_node(&data->chip, pdev->dev.of_node);
+ mtd = nand_to_mtd(&data->chip);
+ mtd->dev.parent = &pdev->dev;
data->chip.IO_ADDR_R = data->io_base;
data->chip.IO_ADDR_W = data->io_base;
@@ -87,22 +86,21 @@ static int plat_nand_probe(struct platform_device *pdev)
}
/* Scan to find existence of the device */
- if (nand_scan(&data->mtd, pdata->chip.nr_chips)) {
+ if (nand_scan(mtd, pdata->chip.nr_chips)) {
err = -ENXIO;
goto out;
}
part_types = pdata->chip.part_probe_types;
- ppdata.of_node = pdev->dev.of_node;
- err = mtd_device_parse_register(&data->mtd, part_types, &ppdata,
+ err = mtd_device_parse_register(mtd, part_types, NULL,
pdata->chip.partitions,
pdata->chip.nr_partitions);
if (!err)
return err;
- nand_release(&data->mtd);
+ nand_release(mtd);
out:
if (pdata->ctrl.remove)
pdata->ctrl.remove(pdev);
@@ -117,7 +115,7 @@ static int plat_nand_remove(struct platform_device *pdev)
struct plat_nand_data *data = platform_get_drvdata(pdev);
struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev);
- nand_release(&data->mtd);
+ nand_release(nand_to_mtd(&data->chip));
if (pdata->ctrl.remove)
pdata->ctrl.remove(pdev);
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index e453ae9a17fa..86fc245dc71a 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -30,11 +30,6 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_mtd.h>
-
-#if defined(CONFIG_ARM) && (defined(CONFIG_ARCH_PXA) || defined(CONFIG_ARCH_MMP))
-#define ARCH_HAS_DMA
-#endif
-
#include <linux/platform_data/mtd-nand-pxa3xx.h>
#define CHIP_DELAY_TIMEOUT msecs_to_jiffies(200)
@@ -172,7 +167,6 @@ enum pxa3xx_nand_variant {
struct pxa3xx_nand_host {
struct nand_chip chip;
- struct mtd_info *mtd;
void *info_data;
/* page size of attached chip */
@@ -455,14 +449,15 @@ static int pxa3xx_nand_init_timings_compat(struct pxa3xx_nand_host *host,
struct nand_chip *chip = &host->chip;
struct pxa3xx_nand_info *info = host->info_data;
const struct pxa3xx_nand_flash *f = NULL;
+ struct mtd_info *mtd = nand_to_mtd(&host->chip);
int i, id, ntypes;
ntypes = ARRAY_SIZE(builtin_flash_types);
- chip->cmdfunc(host->mtd, NAND_CMD_READID, 0x00, -1);
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
- id = chip->read_byte(host->mtd);
- id |= chip->read_byte(host->mtd) << 0x8;
+ id = chip->read_byte(mtd);
+ id |= chip->read_byte(mtd) << 0x8;
for (i = 0; i < ntypes; i++) {
f = &builtin_flash_types[i];
@@ -895,7 +890,7 @@ static void set_command_address(struct pxa3xx_nand_info *info,
static void prepare_start_command(struct pxa3xx_nand_info *info, int command)
{
struct pxa3xx_nand_host *host = info->host[info->cs];
- struct mtd_info *mtd = host->mtd;
+ struct mtd_info *mtd = nand_to_mtd(&host->chip);
/* reset data and oob column point to handle data */
info->buf_start = 0;
@@ -948,7 +943,7 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
struct mtd_info *mtd;
host = info->host[info->cs];
- mtd = host->mtd;
+ mtd = nand_to_mtd(&host->chip);
addr_cycle = 0;
exec_cmd = 1;
@@ -1118,7 +1113,8 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command,
static void nand_cmdfunc(struct mtd_info *mtd, unsigned command,
int column, int page_addr)
{
- struct pxa3xx_nand_host *host = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
struct pxa3xx_nand_info *info = host->info_data;
int exec_cmd;
@@ -1166,7 +1162,8 @@ static void nand_cmdfunc_extended(struct mtd_info *mtd,
const unsigned command,
int column, int page_addr)
{
- struct pxa3xx_nand_host *host = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
struct pxa3xx_nand_info *info = host->info_data;
int exec_cmd, ext_cmd_type;
@@ -1286,7 +1283,7 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf, int oob_required,
int page)
{
- struct pxa3xx_nand_host *host = mtd->priv;
+ struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
struct pxa3xx_nand_info *info = host->info_data;
chip->read_buf(mtd, buf, mtd->writesize);
@@ -1312,7 +1309,8 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
{
- struct pxa3xx_nand_host *host = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
struct pxa3xx_nand_info *info = host->info_data;
char retval = 0xFF;
@@ -1325,7 +1323,8 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)
static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
{
- struct pxa3xx_nand_host *host = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
struct pxa3xx_nand_info *info = host->info_data;
u16 retval = 0xFFFF;
@@ -1338,7 +1337,8 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)
static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
- struct pxa3xx_nand_host *host = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
struct pxa3xx_nand_info *info = host->info_data;
int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
@@ -1349,7 +1349,8 @@ static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
static void pxa3xx_nand_write_buf(struct mtd_info *mtd,
const uint8_t *buf, int len)
{
- struct pxa3xx_nand_host *host = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
struct pxa3xx_nand_info *info = host->info_data;
int real_len = min_t(size_t, len, info->buf_count - info->buf_start);
@@ -1364,7 +1365,8 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)
static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
{
- struct pxa3xx_nand_host *host = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
struct pxa3xx_nand_info *info = host->info_data;
if (info->need_wait) {
@@ -1387,37 +1389,53 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
return NAND_STATUS_READY;
}
-static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info)
+static int pxa3xx_nand_config_ident(struct pxa3xx_nand_info *info)
{
+ struct pxa3xx_nand_host *host = info->host[info->cs];
struct platform_device *pdev = info->pdev;
struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct pxa3xx_nand_host *host = info->host[info->cs];
- struct mtd_info *mtd = host->mtd;
- struct nand_chip *chip = mtd->priv;
+ const struct nand_sdr_timings *timings;
- /* configure default flash values */
+ /* Configure default flash values */
+ info->chunk_size = PAGE_CHUNK_SIZE;
info->reg_ndcr = 0x0; /* enable all interrupts */
info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
info->reg_ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES);
- info->reg_ndcr |= NDCR_SPARE_EN; /* enable spare by default */
+ info->reg_ndcr |= NDCR_SPARE_EN;
+
+ /* use the common timing to make a try */
+ timings = onfi_async_timing_mode_to_sdr_timings(0);
+ if (IS_ERR(timings))
+ return PTR_ERR(timings);
+
+ pxa3xx_nand_set_sdr_timing(host, timings);
+ return 0;
+}
+
+static void pxa3xx_nand_config_tail(struct pxa3xx_nand_info *info)
+{
+ struct pxa3xx_nand_host *host = info->host[info->cs];
+ struct nand_chip *chip = &host->chip;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
info->reg_ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0;
info->reg_ndcr |= (chip->page_shift == 6) ? NDCR_PG_PER_BLK : 0;
info->reg_ndcr |= (mtd->writesize == 2048) ? NDCR_PAGE_SZ : 0;
-
- return 0;
}
-static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
+static void pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
{
+ struct platform_device *pdev = info->pdev;
+ struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
uint32_t ndcr = nand_readl(info, NDCR);
/* Set an initial chunk size */
info->chunk_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
info->reg_ndcr = ndcr &
~(NDCR_INT_MASK | NDCR_ND_ARB_EN | NFCV1_NDCR_ARB_CNTL);
+ info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
- return 0;
}
static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)
@@ -1483,32 +1501,6 @@ static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info)
kfree(info->data_buff);
}
-static int pxa3xx_nand_sensing(struct pxa3xx_nand_host *host)
-{
- struct pxa3xx_nand_info *info = host->info_data;
- struct mtd_info *mtd;
- struct nand_chip *chip;
- const struct nand_sdr_timings *timings;
- int ret;
-
- mtd = info->host[info->cs]->mtd;
- chip = mtd->priv;
-
- /* use the common timing to make a try */
- timings = onfi_async_timing_mode_to_sdr_timings(0);
- if (IS_ERR(timings))
- return PTR_ERR(timings);
-
- pxa3xx_nand_set_sdr_timing(host, timings);
-
- chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0);
- ret = chip->waitfunc(mtd, chip);
- if (ret & NAND_STATUS_FAIL)
- return -ENODEV;
-
- return 0;
-}
-
static int pxa_ecc_init(struct pxa3xx_nand_info *info,
struct nand_ecc_ctrl *ecc,
int strength, int ecc_stepsize, int page_size)
@@ -1580,34 +1572,22 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
static int pxa3xx_nand_scan(struct mtd_info *mtd)
{
- struct pxa3xx_nand_host *host = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct pxa3xx_nand_host *host = nand_get_controller_data(chip);
struct pxa3xx_nand_info *info = host->info_data;
struct platform_device *pdev = info->pdev;
struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct nand_chip *chip = mtd->priv;
int ret;
uint16_t ecc_strength, ecc_step;
- if (pdata->keep_config && !pxa3xx_nand_detect_config(info))
- goto KEEP_CONFIG;
-
- /* Set a default chunk size */
- info->chunk_size = 512;
-
- ret = pxa3xx_nand_config_flash(info);
- if (ret)
- return ret;
-
- ret = pxa3xx_nand_sensing(host);
- if (ret) {
- dev_info(&info->pdev->dev, "There is no chip on cs %d!\n",
- info->cs);
-
- return ret;
+ if (pdata->keep_config) {
+ pxa3xx_nand_detect_config(info);
+ } else {
+ ret = pxa3xx_nand_config_ident(info);
+ if (ret)
+ return ret;
}
-KEEP_CONFIG:
- info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0;
if (info->reg_ndcr & NDCR_DWIDTH_M)
chip->options |= NAND_BUSWIDTH_16;
@@ -1692,11 +1672,16 @@ KEEP_CONFIG:
host->row_addr_cycles = 3;
else
host->row_addr_cycles = 2;
+
+ if (!pdata->keep_config)
+ pxa3xx_nand_config_tail(info);
+
return nand_scan_tail(mtd);
}
static int alloc_nand_resource(struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
struct pxa3xx_nand_platform_data *pdata;
struct pxa3xx_nand_info *info;
struct pxa3xx_nand_host *host;
@@ -1708,24 +1693,27 @@ static int alloc_nand_resource(struct platform_device *pdev)
pdata = dev_get_platdata(&pdev->dev);
if (pdata->num_cs <= 0)
return -ENODEV;
- info = devm_kzalloc(&pdev->dev, sizeof(*info) + (sizeof(*mtd) +
- sizeof(*host)) * pdata->num_cs, GFP_KERNEL);
+ info = devm_kzalloc(&pdev->dev,
+ sizeof(*info) + sizeof(*host) * pdata->num_cs,
+ GFP_KERNEL);
if (!info)
return -ENOMEM;
info->pdev = pdev;
info->variant = pxa3xx_nand_get_variant(pdev);
for (cs = 0; cs < pdata->num_cs; cs++) {
- mtd = (void *)&info[1] + (sizeof(*mtd) + sizeof(*host)) * cs;
- chip = (struct nand_chip *)(&mtd[1]);
- host = (struct pxa3xx_nand_host *)chip;
+ host = (void *)&info[1] + sizeof(*host) * cs;
+ chip = &host->chip;
+ nand_set_controller_data(chip, host);
+ mtd = nand_to_mtd(chip);
info->host[cs] = host;
- host->mtd = mtd;
host->cs = cs;
host->info_data = info;
- mtd->priv = host;
mtd->dev.parent = &pdev->dev;
+ /* FIXME: all chips use the same device tree partitions */
+ nand_set_flash_node(chip, np);
+ nand_set_controller_data(chip, host);
chip->ecc.read_page = pxa3xx_nand_read_page_hwecc;
chip->ecc.write_page = pxa3xx_nand_write_page_hwecc;
chip->controller = &info->controller;
@@ -1845,7 +1833,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
clk_disable_unprepare(info->clk);
for (cs = 0; cs < pdata->num_cs; cs++)
- nand_release(info->host[cs]->mtd);
+ nand_release(nand_to_mtd(&info->host[cs]->chip));
return 0;
}
@@ -1886,7 +1874,6 @@ static int pxa3xx_nand_probe_dt(struct platform_device *pdev)
static int pxa3xx_nand_probe(struct platform_device *pdev)
{
struct pxa3xx_nand_platform_data *pdata;
- struct mtd_part_parser_data ppdata = {};
struct pxa3xx_nand_info *info;
int ret, cs, probe_success, dma_available;
@@ -1917,7 +1904,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
info = platform_get_drvdata(pdev);
probe_success = 0;
for (cs = 0; cs < pdata->num_cs; cs++) {
- struct mtd_info *mtd = info->host[cs]->mtd;
+ struct mtd_info *mtd = nand_to_mtd(&info->host[cs]->chip);
/*
* The mtd name matches the one used in 'mtdparts' kernel
@@ -1933,10 +1920,8 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
continue;
}
- ppdata.of_node = pdev->dev.of_node;
- ret = mtd_device_parse_register(mtd, NULL,
- &ppdata, pdata->parts[cs],
- pdata->nr_parts[cs]);
+ ret = mtd_device_register(mtd, pdata->parts[cs],
+ pdata->nr_parts[cs]);
if (!ret)
probe_success = 1;
}
@@ -1959,12 +1944,18 @@ static int pxa3xx_nand_suspend(struct device *dev)
return -EAGAIN;
}
+ clk_disable(info->clk);
return 0;
}
static int pxa3xx_nand_resume(struct device *dev)
{
struct pxa3xx_nand_info *info = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_enable(info->clk);
+ if (ret < 0)
+ return ret;
/* We don't want to handle interrupt without calling mtd routine */
disable_int(info, NDCR_INT_MASK);
diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c
index d8bb2be327f1..fc9287af4614 100644
--- a/drivers/mtd/nand/r852.c
+++ b/drivers/mtd/nand/r852.c
@@ -64,8 +64,8 @@ static inline void r852_write_reg_dword(struct r852_device *dev,
/* returns pointer to our private structure */
static inline struct r852_device *r852_get_dev(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- return chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ return nand_get_controller_data(chip);
}
@@ -361,7 +361,7 @@ static void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl)
*/
static int r852_wait(struct mtd_info *mtd, struct nand_chip *chip)
{
- struct r852_device *dev = chip->priv;
+ struct r852_device *dev = nand_get_controller_data(chip);
unsigned long timeout;
int status;
@@ -477,7 +477,7 @@ static int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat,
if (dev->dma_error) {
dev->dma_error = 0;
- return -1;
+ return -EIO;
}
r852_write_reg(dev, R852_CTL, dev->ctlreg | R852_CTL_ECC_ACCESS);
@@ -491,7 +491,7 @@ static int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat,
/* ecc uncorrectable error */
if (ecc_status & R852_ECC_FAIL) {
dbg("ecc: unrecoverable error, in half %d", i);
- error = -1;
+ error = -EBADMSG;
goto exit;
}
@@ -634,25 +634,21 @@ static void r852_update_media_status(struct r852_device *dev)
*/
static int r852_register_nand_device(struct r852_device *dev)
{
- dev->mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
-
- if (!dev->mtd)
- goto error1;
+ struct mtd_info *mtd = nand_to_mtd(dev->chip);
WARN_ON(dev->card_registred);
- dev->mtd->priv = dev->chip;
- dev->mtd->dev.parent = &dev->pci_dev->dev;
+ mtd->dev.parent = &dev->pci_dev->dev;
if (dev->readonly)
dev->chip->options |= NAND_ROM;
r852_engine_enable(dev);
- if (sm_register_device(dev->mtd, dev->sm))
- goto error2;
+ if (sm_register_device(mtd, dev->sm))
+ goto error1;
- if (device_create_file(&dev->mtd->dev, &dev_attr_media_type)) {
+ if (device_create_file(&mtd->dev, &dev_attr_media_type)) {
message("can't create media type sysfs attribute");
goto error3;
}
@@ -660,9 +656,7 @@ static int r852_register_nand_device(struct r852_device *dev)
dev->card_registred = 1;
return 0;
error3:
- nand_release(dev->mtd);
-error2:
- kfree(dev->mtd);
+ nand_release(mtd);
error1:
/* Force card redetect */
dev->card_detected = 0;
@@ -675,15 +669,15 @@ error1:
static void r852_unregister_nand_device(struct r852_device *dev)
{
+ struct mtd_info *mtd = nand_to_mtd(dev->chip);
+
if (!dev->card_registred)
return;
- device_remove_file(&dev->mtd->dev, &dev_attr_media_type);
- nand_release(dev->mtd);
+ device_remove_file(&mtd->dev, &dev_attr_media_type);
+ nand_release(mtd);
r852_engine_disable(dev);
dev->card_registred = 0;
- kfree(dev->mtd);
- dev->mtd = NULL;
}
/* Card state updater */
@@ -885,7 +879,7 @@ static int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
if (!dev)
goto error5;
- chip->priv = dev;
+ nand_set_controller_data(chip, dev);
dev->chip = chip;
dev->pci_dev = pci_dev;
pci_set_drvdata(pci_dev, dev);
@@ -980,7 +974,6 @@ static void r852_remove(struct pci_dev *pci_dev)
/* Stop interrupts */
r852_disable_irqs(dev);
- synchronize_irq(dev->irq);
free_irq(dev->irq, dev);
/* Cleanup */
@@ -1032,6 +1025,7 @@ static int r852_suspend(struct device *device)
static int r852_resume(struct device *device)
{
struct r852_device *dev = pci_get_drvdata(to_pci_dev(device));
+ struct mtd_info *mtd = nand_to_mtd(dev->chip);
r852_disable_irqs(dev);
r852_card_update_present(dev);
@@ -1051,9 +1045,9 @@ static int r852_resume(struct device *device)
/* Otherwise, initialize the card */
if (dev->card_registred) {
r852_engine_enable(dev);
- dev->chip->select_chip(dev->mtd, 0);
- dev->chip->cmdfunc(dev->mtd, NAND_CMD_RESET, -1, -1);
- dev->chip->select_chip(dev->mtd, -1);
+ dev->chip->select_chip(mtd, 0);
+ dev->chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ dev->chip->select_chip(mtd, -1);
}
/* Program card detection IRQ */
diff --git a/drivers/mtd/nand/r852.h b/drivers/mtd/nand/r852.h
index e6a21d9d22c6..d042ddb71a8b 100644
--- a/drivers/mtd/nand/r852.h
+++ b/drivers/mtd/nand/r852.h
@@ -108,7 +108,6 @@
struct r852_device {
void __iomem *mmio; /* mmio */
- struct mtd_info *mtd; /* mtd backpointer */
struct nand_chip *chip; /* nand chip backpointer */
struct pci_dev *pci_dev; /* pci backpointer */
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 05105cadd0db..01ac74fa3b95 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -104,7 +104,6 @@ struct s3c2410_nand_info;
* @scan_res: The result from calling nand_scan_ident().
*/
struct s3c2410_nand_mtd {
- struct mtd_info mtd;
struct nand_chip chip;
struct s3c2410_nand_set *set;
struct s3c2410_nand_info *info;
@@ -168,7 +167,8 @@ struct s3c2410_nand_info {
static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd)
{
- return container_of(mtd, struct s3c2410_nand_mtd, mtd);
+ return container_of(mtd_to_nand(mtd), struct s3c2410_nand_mtd,
+ chip);
}
static struct s3c2410_nand_info *s3c2410_nand_mtd_toinfo(struct mtd_info *mtd)
@@ -382,10 +382,10 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
{
struct s3c2410_nand_info *info;
struct s3c2410_nand_mtd *nmtd;
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
unsigned long cur;
- nmtd = this->priv;
+ nmtd = nand_get_controller_data(this);
info = nmtd->info;
if (chip != -1)
@@ -634,7 +634,7 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
readsb(this->IO_ADDR_R, buf, len);
}
@@ -656,7 +656,7 @@ static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf,
int len)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
writesb(this->IO_ADDR_W, buf, len);
}
@@ -745,7 +745,7 @@ static int s3c24xx_nand_remove(struct platform_device *pdev)
for (mtdno = 0; mtdno < info->mtd_count; mtdno++, ptr++) {
pr_debug("releasing mtd %d (%p)\n", mtdno, ptr);
- nand_release(&ptr->mtd);
+ nand_release(nand_to_mtd(&ptr->chip));
}
}
@@ -762,9 +762,11 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
struct s3c2410_nand_set *set)
{
if (set) {
- mtd->mtd.name = set->name;
+ struct mtd_info *mtdinfo = nand_to_mtd(&mtd->chip);
- return mtd_device_parse_register(&mtd->mtd, NULL, NULL,
+ mtdinfo->name = set->name;
+
+ return mtd_device_parse_register(mtdinfo, NULL, NULL,
set->partitions, set->nr_partitions);
}
@@ -792,7 +794,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
chip->read_buf = s3c2410_nand_read_buf;
chip->select_chip = s3c2410_nand_select_chip;
chip->chip_delay = 50;
- chip->priv = nmtd;
+ nand_set_controller_data(chip, nmtd);
chip->options = set->options;
chip->controller = &info->controller;
@@ -831,7 +833,6 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
chip->IO_ADDR_R = chip->IO_ADDR_W;
nmtd->info = info;
- nmtd->mtd.priv = chip;
nmtd->set = set;
#ifdef CONFIG_MTD_NAND_S3C2410_HWECC
@@ -1012,19 +1013,21 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
nmtd = info->mtds;
for (setno = 0; setno < nr_sets; setno++, nmtd++) {
+ struct mtd_info *mtd = nand_to_mtd(&nmtd->chip);
+
pr_debug("initialising set %d (%p, info %p)\n",
setno, nmtd, info);
- nmtd->mtd.dev.parent = &pdev->dev;
+ mtd->dev.parent = &pdev->dev;
s3c2410_nand_init_chip(info, nmtd, sets);
- nmtd->scan_res = nand_scan_ident(&nmtd->mtd,
+ nmtd->scan_res = nand_scan_ident(mtd,
(sets) ? sets->nr_chips : 1,
NULL);
if (nmtd->scan_res == 0) {
s3c2410_nand_update_chip(info, nmtd);
- nand_scan_tail(&nmtd->mtd);
+ nand_scan_tail(mtd);
s3c2410_nand_add_partition(info, nmtd, sets);
}
diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c
index bcba1a924c75..4814402902f9 100644
--- a/drivers/mtd/nand/sh_flctl.c
+++ b/drivers/mtd/nand/sh_flctl.c
@@ -160,7 +160,7 @@ static void flctl_setup_dma(struct sh_flctl *flctl)
memset(&cfg, 0, sizeof(cfg));
cfg.direction = DMA_MEM_TO_DEV;
- cfg.dst_addr = (dma_addr_t)FLDTFIFO(flctl);
+ cfg.dst_addr = flctl->fifo;
cfg.src_addr = 0;
ret = dmaengine_slave_config(flctl->chan_fifo0_tx, &cfg);
if (ret < 0)
@@ -176,7 +176,7 @@ static void flctl_setup_dma(struct sh_flctl *flctl)
cfg.direction = DMA_DEV_TO_MEM;
cfg.dst_addr = 0;
- cfg.src_addr = (dma_addr_t)FLDTFIFO(flctl);
+ cfg.src_addr = flctl->fifo;
ret = dmaengine_slave_config(flctl->chan_fifo0_rx, &cfg);
if (ret < 0)
goto err;
@@ -607,13 +607,13 @@ static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr)
case FL_REPAIRABLE:
dev_info(&flctl->pdev->dev,
"applied ecc on page 0x%x", page_addr);
- flctl->mtd.ecc_stats.corrected++;
+ mtd->ecc_stats.corrected++;
break;
case FL_ERROR:
dev_warn(&flctl->pdev->dev,
"page 0x%x contains corrupted data\n",
page_addr);
- flctl->mtd.ecc_stats.failed++;
+ mtd->ecc_stats.failed++;
break;
default:
;
@@ -1086,7 +1086,6 @@ static int flctl_probe(struct platform_device *pdev)
struct sh_flctl_platform_data *pdata;
int ret;
int irq;
- struct mtd_part_parser_data ppdata = {};
flctl = devm_kzalloc(&pdev->dev, sizeof(struct sh_flctl), GFP_KERNEL);
if (!flctl)
@@ -1096,6 +1095,7 @@ static int flctl_probe(struct platform_device *pdev)
flctl->reg = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(flctl->reg))
return PTR_ERR(flctl->reg);
+ flctl->fifo = res->start + 0x24; /* FLDTFIFO */
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
@@ -1121,9 +1121,9 @@ static int flctl_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, flctl);
- flctl_mtd = &flctl->mtd;
nand = &flctl->chip;
- flctl_mtd->priv = nand;
+ flctl_mtd = nand_to_mtd(nand);
+ nand_set_flash_node(nand, pdev->dev.of_node);
flctl_mtd->dev.parent = &pdev->dev;
flctl->pdev = pdev;
flctl->hwecc = pdata->has_hwecc;
@@ -1163,9 +1163,7 @@ static int flctl_probe(struct platform_device *pdev)
if (ret)
goto err_chip;
- ppdata.of_node = pdev->dev.of_node;
- ret = mtd_device_parse_register(flctl_mtd, NULL, &ppdata, pdata->parts,
- pdata->nr_parts);
+ ret = mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts);
return 0;
@@ -1180,7 +1178,7 @@ static int flctl_remove(struct platform_device *pdev)
struct sh_flctl *flctl = platform_get_drvdata(pdev);
flctl_release_dma(flctl);
- nand_release(&flctl->mtd);
+ nand_release(nand_to_mtd(&flctl->chip));
pm_runtime_disable(&pdev->dev);
return 0;
diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c
index 082b6009736d..b7d1b55a160b 100644
--- a/drivers/mtd/nand/sharpsl.c
+++ b/drivers/mtd/nand/sharpsl.c
@@ -29,13 +29,15 @@
#include <asm/mach-types.h>
struct sharpsl_nand {
- struct mtd_info mtd;
struct nand_chip chip;
void __iomem *io;
};
-#define mtd_to_sharpsl(_mtd) container_of(_mtd, struct sharpsl_nand, mtd)
+static inline struct sharpsl_nand *mtd_to_sharpsl(struct mtd_info *mtd)
+{
+ return container_of(mtd_to_nand(mtd), struct sharpsl_nand, chip);
+}
/* register offset */
#define ECCLPLB 0x00 /* line parity 7 - 0 bit */
@@ -66,7 +68,7 @@ static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd);
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
if (ctrl & NAND_CTRL_CHANGE) {
unsigned char bits = ctrl & 0x07;
@@ -109,6 +111,7 @@ static int sharpsl_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat,
static int sharpsl_nand_probe(struct platform_device *pdev)
{
struct nand_chip *this;
+ struct mtd_info *mtd;
struct resource *r;
int err = 0;
struct sharpsl_nand *sharpsl;
@@ -143,8 +146,8 @@ static int sharpsl_nand_probe(struct platform_device *pdev)
this = (struct nand_chip *)(&sharpsl->chip);
/* Link the private data with the MTD structure */
- sharpsl->mtd.priv = this;
- sharpsl->mtd.dev.parent = &pdev->dev;
+ mtd = nand_to_mtd(this);
+ mtd->dev.parent = &pdev->dev;
platform_set_drvdata(pdev, sharpsl);
@@ -173,14 +176,14 @@ static int sharpsl_nand_probe(struct platform_device *pdev)
this->ecc.correct = nand_correct_data;
/* Scan to find existence of the device */
- err = nand_scan(&sharpsl->mtd, 1);
+ err = nand_scan(mtd, 1);
if (err)
goto err_scan;
/* Register the partitions */
- sharpsl->mtd.name = "sharpsl-nand";
+ mtd->name = "sharpsl-nand";
- err = mtd_device_parse_register(&sharpsl->mtd, NULL, NULL,
+ err = mtd_device_parse_register(mtd, NULL, NULL,
data->partitions, data->nr_partitions);
if (err)
goto err_add;
@@ -189,7 +192,7 @@ static int sharpsl_nand_probe(struct platform_device *pdev)
return 0;
err_add:
- nand_release(&sharpsl->mtd);
+ nand_release(mtd);
err_scan:
iounmap(sharpsl->io);
@@ -207,7 +210,7 @@ static int sharpsl_nand_remove(struct platform_device *pdev)
struct sharpsl_nand *sharpsl = platform_get_drvdata(pdev);
/* Release resources, unregister device */
- nand_release(&sharpsl->mtd);
+ nand_release(nand_to_mtd(&sharpsl->chip));
iounmap(sharpsl->io);
diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c
index e06b5e5d3287..c514740f9a83 100644
--- a/drivers/mtd/nand/sm_common.c
+++ b/drivers/mtd/nand/sm_common.c
@@ -102,7 +102,7 @@ static struct nand_flash_dev nand_xd_flash_ids[] = {
int sm_register_device(struct mtd_info *mtd, int smartmedia)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
int ret;
chip->options |= NAND_SKIP_BBTSCAN;
diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c
index b94f53427f0f..e3305f9dd6fb 100644
--- a/drivers/mtd/nand/socrates_nand.c
+++ b/drivers/mtd/nand/socrates_nand.c
@@ -30,7 +30,6 @@
struct socrates_nand_host {
struct nand_chip nand_chip;
- struct mtd_info mtd;
void __iomem *io_base;
struct device *dev;
};
@@ -45,8 +44,8 @@ static void socrates_nand_write_buf(struct mtd_info *mtd,
const uint8_t *buf, int len)
{
int i;
- struct nand_chip *this = mtd->priv;
- struct socrates_nand_host *host = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct socrates_nand_host *host = nand_get_controller_data(this);
for (i = 0; i < len; i++) {
out_be32(host->io_base, FPGA_NAND_ENABLE |
@@ -64,8 +63,8 @@ static void socrates_nand_write_buf(struct mtd_info *mtd,
static void socrates_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
int i;
- struct nand_chip *this = mtd->priv;
- struct socrates_nand_host *host = this->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
+ struct socrates_nand_host *host = nand_get_controller_data(this);
uint32_t val;
val = FPGA_NAND_ENABLE | FPGA_NAND_CMD_READ;
@@ -105,8 +104,8 @@ static uint16_t socrates_nand_read_word(struct mtd_info *mtd)
static void socrates_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct socrates_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct socrates_nand_host *host = nand_get_controller_data(nand_chip);
uint32_t val;
if (cmd == NAND_CMD_NONE)
@@ -130,8 +129,8 @@ static void socrates_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
*/
static int socrates_nand_device_ready(struct mtd_info *mtd)
{
- struct nand_chip *nand_chip = mtd->priv;
- struct socrates_nand_host *host = nand_chip->priv;
+ struct nand_chip *nand_chip = mtd_to_nand(mtd);
+ struct socrates_nand_host *host = nand_get_controller_data(nand_chip);
if (in_be32(host->io_base) & FPGA_NAND_BUSY)
return 0; /* busy */
@@ -147,7 +146,6 @@ static int socrates_nand_probe(struct platform_device *ofdev)
struct mtd_info *mtd;
struct nand_chip *nand_chip;
int res;
- struct mtd_part_parser_data ppdata;
/* Allocate memory for the device structure (and zero it) */
host = devm_kzalloc(&ofdev->dev, sizeof(*host), GFP_KERNEL);
@@ -160,15 +158,15 @@ static int socrates_nand_probe(struct platform_device *ofdev)
return -EIO;
}
- mtd = &host->mtd;
nand_chip = &host->nand_chip;
+ mtd = nand_to_mtd(nand_chip);
host->dev = &ofdev->dev;
- nand_chip->priv = host; /* link the private data structures */
- mtd->priv = nand_chip;
+ /* link the private data structures */
+ nand_set_controller_data(nand_chip, host);
+ nand_set_flash_node(nand_chip, ofdev->dev.of_node);
mtd->name = "socrates_nand";
mtd->dev.parent = &ofdev->dev;
- ppdata.of_node = ofdev->dev.of_node;
/*should never be accessed directly */
nand_chip->IO_ADDR_R = (void *)0xdeadbeef;
@@ -200,7 +198,7 @@ static int socrates_nand_probe(struct platform_device *ofdev)
goto out;
}
- res = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+ res = mtd_device_register(mtd, NULL, 0);
if (!res)
return res;
@@ -217,7 +215,7 @@ out:
static int socrates_nand_remove(struct platform_device *ofdev)
{
struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev);
- struct mtd_info *mtd = &host->mtd;
+ struct mtd_info *mtd = nand_to_mtd(&host->nand_chip);
nand_release(mtd);
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index 824711845c44..51e10a35fe08 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -234,7 +234,6 @@ struct sunxi_nand_hw_ecc {
struct sunxi_nand_chip {
struct list_head node;
struct nand_chip nand;
- struct mtd_info mtd;
unsigned long clk_rate;
u32 timing_cfg;
u32 timing_ctl;
@@ -350,7 +349,7 @@ static int sunxi_nfc_rst(struct sunxi_nfc *nfc)
static int sunxi_nfc_dev_ready(struct mtd_info *mtd)
{
- struct nand_chip *nand = mtd->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
struct sunxi_nand_rb *rb;
@@ -388,7 +387,7 @@ static int sunxi_nfc_dev_ready(struct mtd_info *mtd)
static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip)
{
- struct nand_chip *nand = mtd->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
struct sunxi_nand_chip_sel *sel;
@@ -433,7 +432,7 @@ static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip)
static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
- struct nand_chip *nand = mtd->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
int ret;
@@ -466,7 +465,7 @@ static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
int len)
{
- struct nand_chip *nand = mtd->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
int ret;
@@ -507,7 +506,7 @@ static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd)
static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
unsigned int ctrl)
{
- struct nand_chip *nand = mtd->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
int ret;
@@ -541,7 +540,7 @@ static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd)
{
- struct nand_chip *nand = mtd->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct sunxi_nand_hw_ecc *data = nand->ecc.priv;
u32 ecc_ctl;
@@ -556,7 +555,7 @@ static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd)
static void sunxi_nfc_hw_ecc_disable(struct mtd_info *mtd)
{
- struct nand_chip *nand = mtd->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN,
@@ -577,7 +576,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
int *cur_off,
unsigned int *max_bitflips)
{
- struct nand_chip *nand = mtd->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct nand_ecc_ctrl *ecc = &nand->ecc;
u32 status;
@@ -638,7 +637,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd,
u8 *oob, int *cur_off)
{
- struct nand_chip *nand = mtd->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &nand->ecc;
int offset = ((ecc->bytes + 4) * ecc->steps);
int len = mtd->oobsize - offset;
@@ -665,7 +664,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
const u8 *oob, int oob_off,
int *cur_off)
{
- struct nand_chip *nand = mtd->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct nand_ecc_ctrl *ecc = &nand->ecc;
int ret;
@@ -702,7 +701,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd,
u8 *oob, int *cur_off)
{
- struct nand_chip *nand = mtd->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &nand->ecc;
int offset = ((ecc->bytes + 4) * ecc->steps);
int len = mtd->oobsize - offset;
@@ -991,6 +990,7 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip,
struct device_node *np)
{
+ struct mtd_info *mtd = nand_to_mtd(&chip->nand);
const struct nand_sdr_timings *timings;
int ret;
int mode;
@@ -1008,12 +1008,11 @@ static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip,
feature[0] = mode;
for (i = 0; i < chip->nsels; i++) {
- chip->nand.select_chip(&chip->mtd, i);
- ret = chip->nand.onfi_set_features(&chip->mtd,
- &chip->nand,
+ chip->nand.select_chip(mtd, i);
+ ret = chip->nand.onfi_set_features(mtd, &chip->nand,
ONFI_FEATURE_ADDR_TIMING_MODE,
feature);
- chip->nand.select_chip(&chip->mtd, -1);
+ chip->nand.select_chip(mtd, -1);
if (ret)
return ret;
}
@@ -1031,7 +1030,7 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
struct device_node *np)
{
static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
- struct nand_chip *nand = mtd->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
struct sunxi_nand_hw_ecc *data;
@@ -1189,7 +1188,7 @@ static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc)
static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
struct device_node *np)
{
- struct nand_chip *nand = mtd->priv;
+ struct nand_chip *nand = mtd_to_nand(mtd);
int ret;
if (!ecc->size) {
@@ -1232,7 +1231,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
{
const struct nand_sdr_timings *timings;
struct sunxi_nand_chip *chip;
- struct mtd_part_parser_data ppdata;
struct mtd_info *mtd;
struct nand_chip *nand;
int nsels;
@@ -1330,16 +1328,15 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
* in the DT.
*/
nand->ecc.mode = NAND_ECC_HW;
- nand->flash_node = np;
+ nand_set_flash_node(nand, np);
nand->select_chip = sunxi_nfc_select_chip;
nand->cmd_ctrl = sunxi_nfc_cmd_ctrl;
nand->read_buf = sunxi_nfc_read_buf;
nand->write_buf = sunxi_nfc_write_buf;
nand->read_byte = sunxi_nfc_read_byte;
- mtd = &chip->mtd;
+ mtd = nand_to_mtd(nand);
mtd->dev.parent = dev;
- mtd->priv = nand;
ret = nand_scan_ident(mtd, nsels, NULL);
if (ret)
@@ -1366,8 +1363,7 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
return ret;
}
- ppdata.of_node = np;
- ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+ ret = mtd_device_register(mtd, NULL, 0);
if (ret) {
dev_err(dev, "failed to register mtd device: %d\n", ret);
nand_release(mtd);
@@ -1393,8 +1389,10 @@ static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc)
for_each_child_of_node(np, nand_np) {
ret = sunxi_nand_chip_init(dev, nfc, nand_np);
- if (ret)
+ if (ret) {
+ of_node_put(nand_np);
return ret;
+ }
}
return 0;
@@ -1407,7 +1405,7 @@ static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
while (!list_empty(&nfc->chips)) {
chip = list_first_entry(&nfc->chips, struct sunxi_nand_chip,
node);
- nand_release(&chip->mtd);
+ nand_release(nand_to_mtd(&chip->nand));
sunxi_nand_ecc_cleanup(&chip->nand.ecc);
list_del(&chip->node);
}
diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c
index befddf0776e4..08b30549ec0a 100644
--- a/drivers/mtd/nand/tmio_nand.c
+++ b/drivers/mtd/nand/tmio_nand.c
@@ -103,7 +103,6 @@
/*--------------------------------------------------------------------------*/
struct tmio_nand {
- struct mtd_info mtd;
struct nand_chip chip;
struct platform_device *dev;
@@ -119,7 +118,10 @@ struct tmio_nand {
unsigned read_good:1;
};
-#define mtd_to_tmio(m) container_of(m, struct tmio_nand, mtd)
+static inline struct tmio_nand *mtd_to_tmio(struct mtd_info *mtd)
+{
+ return container_of(mtd_to_nand(mtd), struct tmio_nand, chip);
+}
/*--------------------------------------------------------------------------*/
@@ -128,7 +130,7 @@ static void tmio_nand_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
struct tmio_nand *tmio = mtd_to_tmio(mtd);
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
if (ctrl & NAND_CTRL_CHANGE) {
u8 mode;
@@ -378,9 +380,8 @@ static int tmio_probe(struct platform_device *dev)
tmio->dev = dev;
platform_set_drvdata(dev, tmio);
- mtd = &tmio->mtd;
nand_chip = &tmio->chip;
- mtd->priv = nand_chip;
+ mtd = nand_to_mtd(nand_chip);
mtd->name = "tmio-nand";
mtd->dev.parent = &dev->dev;
@@ -456,7 +457,7 @@ static int tmio_remove(struct platform_device *dev)
{
struct tmio_nand *tmio = platform_get_drvdata(dev);
- nand_release(&tmio->mtd);
+ nand_release(nand_to_mtd(&tmio->chip));
tmio_hw_stop(dev, tmio);
return 0;
}
diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c
index 8572519b8441..04d63f56baa4 100644
--- a/drivers/mtd/nand/txx9ndfmc.c
+++ b/drivers/mtd/nand/txx9ndfmc.c
@@ -63,7 +63,6 @@
struct txx9ndfmc_priv {
struct platform_device *dev;
struct nand_chip chip;
- struct mtd_info mtd;
int cs;
const char *mtdname;
};
@@ -79,8 +78,8 @@ struct txx9ndfmc_drvdata {
static struct platform_device *mtd_to_platdev(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct txx9ndfmc_priv *txx9_priv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct txx9ndfmc_priv *txx9_priv = nand_get_controller_data(chip);
return txx9_priv->dev;
}
@@ -135,8 +134,8 @@ static void txx9ndfmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
static void txx9ndfmc_cmd_ctrl(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
- struct nand_chip *chip = mtd->priv;
- struct txx9ndfmc_priv *txx9_priv = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct txx9ndfmc_priv *txx9_priv = nand_get_controller_data(chip);
struct platform_device *dev = txx9_priv->dev;
struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);
@@ -175,7 +174,7 @@ static int txx9ndfmc_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
uint8_t *ecc_code)
{
struct platform_device *dev = mtd_to_platdev(mtd);
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
int eccbytes;
u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);
@@ -195,7 +194,7 @@ static int txx9ndfmc_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
static int txx9ndfmc_correct_data(struct mtd_info *mtd, unsigned char *buf,
unsigned char *read_ecc, unsigned char *calc_ecc)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
int eccsize;
int corrected = 0;
int stat;
@@ -257,7 +256,7 @@ static void txx9ndfmc_initialize(struct platform_device *dev)
static int txx9ndfmc_nand_scan(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
int ret;
ret = nand_scan_ident(mtd, 1, NULL);
@@ -322,11 +321,9 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
if (!txx9_priv)
continue;
chip = &txx9_priv->chip;
- mtd = &txx9_priv->mtd;
+ mtd = nand_to_mtd(chip);
mtd->dev.parent = &dev->dev;
- mtd->priv = chip;
-
chip->read_byte = txx9ndfmc_read_byte;
chip->read_buf = txx9ndfmc_read_buf;
chip->write_buf = txx9ndfmc_write_buf;
@@ -343,7 +340,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
chip->chip_delay = 100;
chip->controller = &drvdata->hw_control;
- chip->priv = txx9_priv;
+ nand_set_controller_data(chip, txx9_priv);
txx9_priv->dev = dev;
if (plat->ch_mask != 1) {
@@ -391,8 +388,8 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev)
if (!mtd)
continue;
- chip = mtd->priv;
- txx9_priv = chip->priv;
+ chip = mtd_to_nand(mtd);
+ txx9_priv = nand_get_controller_data(chip);
nand_release(mtd);
kfree(txx9_priv->mtdname);
diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c
index 8805d6325579..034420f313d5 100644
--- a/drivers/mtd/nand/vf610_nfc.c
+++ b/drivers/mtd/nand/vf610_nfc.c
@@ -156,7 +156,6 @@ enum vf610_nfc_variant {
};
struct vf610_nfc {
- struct mtd_info mtd;
struct nand_chip chip;
struct device *dev;
void __iomem *regs;
@@ -171,7 +170,10 @@ struct vf610_nfc {
u32 ecc_mode;
};
-#define mtd_to_nfc(_mtd) container_of(_mtd, struct vf610_nfc, mtd)
+static inline struct vf610_nfc *mtd_to_nfc(struct mtd_info *mtd)
+{
+ return container_of(mtd_to_nand(mtd), struct vf610_nfc, chip);
+}
static struct nand_ecclayout vf610_nfc_ecc45 = {
.eccbytes = 45,
@@ -674,10 +676,9 @@ static int vf610_nfc_probe(struct platform_device *pdev)
return -ENOMEM;
nfc->dev = &pdev->dev;
- mtd = &nfc->mtd;
chip = &nfc->chip;
+ mtd = nand_to_mtd(chip);
- mtd->priv = chip;
mtd->owner = THIS_MODULE;
mtd->dev.parent = nfc->dev;
mtd->name = DRV_NAME;
@@ -707,18 +708,18 @@ static int vf610_nfc_probe(struct platform_device *pdev)
for_each_available_child_of_node(nfc->dev->of_node, child) {
if (of_device_is_compatible(child, "fsl,vf610-nfc-nandcs")) {
- if (chip->flash_node) {
+ if (nand_get_flash_node(chip)) {
dev_err(nfc->dev,
"Only one NAND chip supported!\n");
err = -EINVAL;
goto error;
}
- chip->flash_node = child;
+ nand_set_flash_node(chip, child);
}
}
- if (!chip->flash_node) {
+ if (!nand_get_flash_node(chip)) {
dev_err(nfc->dev, "NAND chip sub-node missing!\n");
err = -ENODEV;
goto err_clk;
@@ -811,14 +812,10 @@ static int vf610_nfc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, mtd);
/* Register device in MTD */
- return mtd_device_parse_register(mtd, NULL,
- &(struct mtd_part_parser_data){
- .of_node = chip->flash_node,
- },
- NULL, 0);
+ return mtd_device_register(mtd, NULL, 0);
error:
- of_node_put(chip->flash_node);
+ of_node_put(nand_get_flash_node(chip));
err_clk:
clk_disable_unprepare(nfc->clk);
return err;
diff --git a/drivers/mtd/nand/xway_nand.c b/drivers/mtd/nand/xway_nand.c
index 3b28db458ea0..0cf0ac07a8c2 100644
--- a/drivers/mtd/nand/xway_nand.c
+++ b/drivers/mtd/nand/xway_nand.c
@@ -89,7 +89,7 @@ static void xway_select_chip(struct mtd_info *mtd, int chip)
static void xway_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
unsigned long nandaddr = (unsigned long) this->IO_ADDR_W;
unsigned long flags;
@@ -118,7 +118,7 @@ static int xway_dev_ready(struct mtd_info *mtd)
static unsigned char xway_read_byte(struct mtd_info *mtd)
{
- struct nand_chip *this = mtd->priv;
+ struct nand_chip *this = mtd_to_nand(mtd);
unsigned long nandaddr = (unsigned long) this->IO_ADDR_R;
unsigned long flags;
int ret;
diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c
index 669c3452f278..ede407d6e106 100644
--- a/drivers/mtd/ofpart.c
+++ b/drivers/mtd/ofpart.c
@@ -26,9 +26,10 @@ static bool node_has_compatible(struct device_node *pp)
}
static int parse_ofpart_partitions(struct mtd_info *master,
- struct mtd_partition **pparts,
+ const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
+ struct mtd_partition *parts;
struct device_node *mtd_node;
struct device_node *ofpart_node;
const char *partname;
@@ -37,19 +38,25 @@ static int parse_ofpart_partitions(struct mtd_info *master,
bool dedicated = true;
- if (!data)
- return 0;
-
- mtd_node = data->of_node;
+ /* Pull of_node from the master device node */
+ mtd_node = mtd_get_of_node(master);
if (!mtd_node)
return 0;
ofpart_node = of_get_child_by_name(mtd_node, "partitions");
if (!ofpart_node) {
- pr_warn("%s: 'partitions' subnode not found on %s. Trying to parse direct subnodes as partitions.\n",
- master->name, mtd_node->full_name);
+ /*
+ * We might get here even when ofpart isn't used at all (e.g.,
+ * when using another parser), so don't be louder than
+ * KERN_DEBUG
+ */
+ pr_debug("%s: 'partitions' subnode not found on %s. Trying to parse direct subnodes as partitions.\n",
+ master->name, mtd_node->full_name);
ofpart_node = mtd_node;
dedicated = false;
+ } else if (!of_device_is_compatible(ofpart_node, "fixed-partitions")) {
+ /* The 'partitions' subnode might be used by another parser */
+ return 0;
}
/* First count the subnodes */
@@ -64,8 +71,8 @@ static int parse_ofpart_partitions(struct mtd_info *master,
if (nr_parts == 0)
return 0;
- *pparts = kzalloc(nr_parts * sizeof(**pparts), GFP_KERNEL);
- if (!*pparts)
+ parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
+ if (!parts)
return -ENOMEM;
i = 0;
@@ -99,19 +106,19 @@ static int parse_ofpart_partitions(struct mtd_info *master,
goto ofpart_fail;
}
- (*pparts)[i].offset = of_read_number(reg, a_cells);
- (*pparts)[i].size = of_read_number(reg + a_cells, s_cells);
+ parts[i].offset = of_read_number(reg, a_cells);
+ parts[i].size = of_read_number(reg + a_cells, s_cells);
partname = of_get_property(pp, "label", &len);
if (!partname)
partname = of_get_property(pp, "name", &len);
- (*pparts)[i].name = partname;
+ parts[i].name = partname;
if (of_get_property(pp, "read-only", &len))
- (*pparts)[i].mask_flags |= MTD_WRITEABLE;
+ parts[i].mask_flags |= MTD_WRITEABLE;
if (of_get_property(pp, "lock", &len))
- (*pparts)[i].mask_flags |= MTD_POWERUP_LOCK;
+ parts[i].mask_flags |= MTD_POWERUP_LOCK;
i++;
}
@@ -119,6 +126,7 @@ static int parse_ofpart_partitions(struct mtd_info *master,
if (!nr_parts)
goto ofpart_none;
+ *pparts = parts;
return nr_parts;
ofpart_fail:
@@ -127,21 +135,20 @@ ofpart_fail:
ret = -EINVAL;
ofpart_none:
of_node_put(pp);
- kfree(*pparts);
- *pparts = NULL;
+ kfree(parts);
return ret;
}
static struct mtd_part_parser ofpart_parser = {
- .owner = THIS_MODULE,
.parse_fn = parse_ofpart_partitions,
.name = "ofpart",
};
static int parse_ofoldpart_partitions(struct mtd_info *master,
- struct mtd_partition **pparts,
+ const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
+ struct mtd_partition *parts;
struct device_node *dp;
int i, plen, nr_parts;
const struct {
@@ -149,10 +156,8 @@ static int parse_ofoldpart_partitions(struct mtd_info *master,
} *part;
const char *names;
- if (!data)
- return 0;
-
- dp = data->of_node;
+ /* Pull of_node from the master device node */
+ dp = mtd_get_of_node(master);
if (!dp)
return 0;
@@ -165,37 +170,37 @@ static int parse_ofoldpart_partitions(struct mtd_info *master,
nr_parts = plen / sizeof(part[0]);
- *pparts = kzalloc(nr_parts * sizeof(*(*pparts)), GFP_KERNEL);
- if (!*pparts)
+ parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
+ if (!parts)
return -ENOMEM;
names = of_get_property(dp, "partition-names", &plen);
for (i = 0; i < nr_parts; i++) {
- (*pparts)[i].offset = be32_to_cpu(part->offset);
- (*pparts)[i].size = be32_to_cpu(part->len) & ~1;
+ parts[i].offset = be32_to_cpu(part->offset);
+ parts[i].size = be32_to_cpu(part->len) & ~1;
/* bit 0 set signifies read only partition */
if (be32_to_cpu(part->len) & 1)
- (*pparts)[i].mask_flags = MTD_WRITEABLE;
+ parts[i].mask_flags = MTD_WRITEABLE;
if (names && (plen > 0)) {
int len = strlen(names) + 1;
- (*pparts)[i].name = names;
+ parts[i].name = names;
plen -= len;
names += len;
} else {
- (*pparts)[i].name = "unnamed";
+ parts[i].name = "unnamed";
}
part++;
}
+ *pparts = parts;
return nr_parts;
}
static struct mtd_part_parser ofoldpart_parser = {
- .owner = THIS_MODULE,
.parse_fn = parse_ofoldpart_partitions,
.name = "ofoldpart",
};
diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c
index 3e0285696227..0aacf125938b 100644
--- a/drivers/mtd/onenand/omap2.c
+++ b/drivers/mtd/onenand/omap2.c
@@ -614,7 +614,6 @@ static int omap2_onenand_probe(struct platform_device *pdev)
struct onenand_chip *this;
int r;
struct resource *res;
- struct mtd_part_parser_data ppdata = {};
pdata = dev_get_platdata(&pdev->dev);
if (pdata == NULL) {
@@ -713,6 +712,7 @@ static int omap2_onenand_probe(struct platform_device *pdev)
c->mtd.priv = &c->onenand;
c->mtd.dev.parent = &pdev->dev;
+ mtd_set_of_node(&c->mtd, pdata->of_node);
this = &c->onenand;
if (c->dma_channel >= 0) {
@@ -743,10 +743,8 @@ static int omap2_onenand_probe(struct platform_device *pdev)
if ((r = onenand_scan(&c->mtd, 1)) < 0)
goto err_release_regulator;
- ppdata.of_node = pdata->of_node;
- r = mtd_device_parse_register(&c->mtd, NULL, &ppdata,
- pdata ? pdata->parts : NULL,
- pdata ? pdata->nr_parts : 0);
+ r = mtd_device_register(&c->mtd, pdata ? pdata->parts : NULL,
+ pdata ? pdata->nr_parts : 0);
if (r)
goto err_release_onenand;
diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c
index 5da911ebdf49..7623ac5fc586 100644
--- a/drivers/mtd/redboot.c
+++ b/drivers/mtd/redboot.c
@@ -57,7 +57,7 @@ static inline int redboot_checksum(struct fis_image_desc *img)
}
static int parse_redboot_partitions(struct mtd_info *master,
- struct mtd_partition **pparts,
+ const struct mtd_partition **pparts,
struct mtd_part_parser_data *data)
{
int nrparts = 0;
@@ -290,28 +290,13 @@ static int parse_redboot_partitions(struct mtd_info *master,
}
static struct mtd_part_parser redboot_parser = {
- .owner = THIS_MODULE,
.parse_fn = parse_redboot_partitions,
.name = "RedBoot",
};
+module_mtd_part_parser(redboot_parser);
/* mtd parsers will request the module by parser name */
MODULE_ALIAS("RedBoot");
-
-static int __init redboot_parser_init(void)
-{
- register_mtd_parser(&redboot_parser);
- return 0;
-}
-
-static void __exit redboot_parser_exit(void)
-{
- deregister_mtd_parser(&redboot_parser);
-}
-
-module_init(redboot_parser_init);
-module_exit(redboot_parser_exit);
-
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
MODULE_DESCRIPTION("Parsing code for RedBoot Flash Image System (FIS) tables");
diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c
index c23184a47fc4..b096f8bb05ba 100644
--- a/drivers/mtd/sm_ftl.c
+++ b/drivers/mtd/sm_ftl.c
@@ -206,9 +206,10 @@ static loff_t sm_mkoffset(struct sm_ftl *ftl, int zone, int block, int boffset)
}
/* Breaks offset into parts */
-static void sm_break_offset(struct sm_ftl *ftl, loff_t offset,
+static void sm_break_offset(struct sm_ftl *ftl, loff_t loffset,
int *zone, int *block, int *boffset)
{
+ u64 offset = loffset;
*boffset = do_div(offset, ftl->block_size);
*block = do_div(offset, ftl->max_lba);
*zone = offset >= ftl->zone_count ? -1 : offset;
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
index 2fe2a7e90fa9..0dc927540b3d 100644
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -7,6 +7,13 @@ menuconfig MTD_SPI_NOR
if MTD_SPI_NOR
+config MTD_MT81xx_NOR
+ tristate "Mediatek MT81xx SPI NOR flash controller"
+ help
+ This enables access to SPI NOR flash, using MT81xx SPI NOR flash
+ controller. This controller does not support generic SPI BUS, it only
+ supports SPI NOR Flash.
+
config MTD_SPI_NOR_USE_4K_SECTORS
bool "Use small 4096 B erase sectors"
default y
diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile
index e53333ef8582..0bf3a7f81675 100644
--- a/drivers/mtd/spi-nor/Makefile
+++ b/drivers/mtd/spi-nor/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
+obj-$(CONFIG_MTD_MT81xx_NOR) += mtk-quadspi.o
obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o
diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
index 7b10ed413983..54640f1eb3a1 100644
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
@@ -269,7 +269,7 @@ struct fsl_qspi {
struct clk *clk, *clk_en;
struct device *dev;
struct completion c;
- struct fsl_qspi_devtype_data *devtype_data;
+ const struct fsl_qspi_devtype_data *devtype_data;
u32 nor_size;
u32 nor_num;
u32 clk_rate;
@@ -927,15 +927,12 @@ static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
static int fsl_qspi_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- struct mtd_part_parser_data ppdata;
struct device *dev = &pdev->dev;
struct fsl_qspi *q;
struct resource *res;
struct spi_nor *nor;
struct mtd_info *mtd;
int ret, i = 0;
- const struct of_device_id *of_id =
- of_match_device(fsl_qspi_dt_ids, &pdev->dev);
q = devm_kzalloc(dev, sizeof(*q), GFP_KERNEL);
if (!q)
@@ -946,7 +943,9 @@ static int fsl_qspi_probe(struct platform_device *pdev)
return -ENODEV;
q->dev = dev;
- q->devtype_data = (struct fsl_qspi_devtype_data *)of_id->data;
+ q->devtype_data = of_device_get_match_data(dev);
+ if (!q->devtype_data)
+ return -ENODEV;
platform_set_drvdata(pdev, q);
/* find the resources */
@@ -1013,7 +1012,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
mtd = &nor->mtd;
nor->dev = dev;
- nor->flash_node = np;
+ spi_nor_set_flash_node(nor, np);
nor->priv = q;
/* fill the hooks */
@@ -1038,8 +1037,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (ret)
goto mutex_failed;
- ppdata.of_node = np;
- ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+ ret = mtd_device_register(mtd, NULL, 0);
if (ret)
goto mutex_failed;
diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c
new file mode 100644
index 000000000000..d5f850d035bb
--- /dev/null
+++ b/drivers/mtd/spi-nor/mtk-quadspi.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: Bayi Cheng <bayi.cheng@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/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/ioport.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/spi-nor.h>
+
+#define MTK_NOR_CMD_REG 0x00
+#define MTK_NOR_CNT_REG 0x04
+#define MTK_NOR_RDSR_REG 0x08
+#define MTK_NOR_RDATA_REG 0x0c
+#define MTK_NOR_RADR0_REG 0x10
+#define MTK_NOR_RADR1_REG 0x14
+#define MTK_NOR_RADR2_REG 0x18
+#define MTK_NOR_WDATA_REG 0x1c
+#define MTK_NOR_PRGDATA0_REG 0x20
+#define MTK_NOR_PRGDATA1_REG 0x24
+#define MTK_NOR_PRGDATA2_REG 0x28
+#define MTK_NOR_PRGDATA3_REG 0x2c
+#define MTK_NOR_PRGDATA4_REG 0x30
+#define MTK_NOR_PRGDATA5_REG 0x34
+#define MTK_NOR_SHREG0_REG 0x38
+#define MTK_NOR_SHREG1_REG 0x3c
+#define MTK_NOR_SHREG2_REG 0x40
+#define MTK_NOR_SHREG3_REG 0x44
+#define MTK_NOR_SHREG4_REG 0x48
+#define MTK_NOR_SHREG5_REG 0x4c
+#define MTK_NOR_SHREG6_REG 0x50
+#define MTK_NOR_SHREG7_REG 0x54
+#define MTK_NOR_SHREG8_REG 0x58
+#define MTK_NOR_SHREG9_REG 0x5c
+#define MTK_NOR_CFG1_REG 0x60
+#define MTK_NOR_CFG2_REG 0x64
+#define MTK_NOR_CFG3_REG 0x68
+#define MTK_NOR_STATUS0_REG 0x70
+#define MTK_NOR_STATUS1_REG 0x74
+#define MTK_NOR_STATUS2_REG 0x78
+#define MTK_NOR_STATUS3_REG 0x7c
+#define MTK_NOR_FLHCFG_REG 0x84
+#define MTK_NOR_TIME_REG 0x94
+#define MTK_NOR_PP_DATA_REG 0x98
+#define MTK_NOR_PREBUF_STUS_REG 0x9c
+#define MTK_NOR_DELSEL0_REG 0xa0
+#define MTK_NOR_DELSEL1_REG 0xa4
+#define MTK_NOR_INTRSTUS_REG 0xa8
+#define MTK_NOR_INTREN_REG 0xac
+#define MTK_NOR_CHKSUM_CTL_REG 0xb8
+#define MTK_NOR_CHKSUM_REG 0xbc
+#define MTK_NOR_CMD2_REG 0xc0
+#define MTK_NOR_WRPROT_REG 0xc4
+#define MTK_NOR_RADR3_REG 0xc8
+#define MTK_NOR_DUAL_REG 0xcc
+#define MTK_NOR_DELSEL2_REG 0xd0
+#define MTK_NOR_DELSEL3_REG 0xd4
+#define MTK_NOR_DELSEL4_REG 0xd8
+
+/* commands for mtk nor controller */
+#define MTK_NOR_READ_CMD 0x0
+#define MTK_NOR_RDSR_CMD 0x2
+#define MTK_NOR_PRG_CMD 0x4
+#define MTK_NOR_WR_CMD 0x10
+#define MTK_NOR_PIO_WR_CMD 0x90
+#define MTK_NOR_WRSR_CMD 0x20
+#define MTK_NOR_PIO_READ_CMD 0x81
+#define MTK_NOR_WR_BUF_ENABLE 0x1
+#define MTK_NOR_WR_BUF_DISABLE 0x0
+#define MTK_NOR_ENABLE_SF_CMD 0x30
+#define MTK_NOR_DUAD_ADDR_EN 0x8
+#define MTK_NOR_QUAD_READ_EN 0x4
+#define MTK_NOR_DUAL_ADDR_EN 0x2
+#define MTK_NOR_DUAL_READ_EN 0x1
+#define MTK_NOR_DUAL_DISABLE 0x0
+#define MTK_NOR_FAST_READ 0x1
+
+#define SFLASH_WRBUF_SIZE 128
+
+/* Can shift up to 48 bits (6 bytes) of TX/RX */
+#define MTK_NOR_MAX_RX_TX_SHIFT 6
+/* can shift up to 56 bits (7 bytes) transfer by MTK_NOR_PRG_CMD */
+#define MTK_NOR_MAX_SHIFT 7
+
+/* Helpers for accessing the program data / shift data registers */
+#define MTK_NOR_PRG_REG(n) (MTK_NOR_PRGDATA0_REG + 4 * (n))
+#define MTK_NOR_SHREG(n) (MTK_NOR_SHREG0_REG + 4 * (n))
+
+struct mt8173_nor {
+ struct spi_nor nor;
+ struct device *dev;
+ void __iomem *base; /* nor flash base address */
+ struct clk *spi_clk;
+ struct clk *nor_clk;
+};
+
+static void mt8173_nor_set_read_mode(struct mt8173_nor *mt8173_nor)
+{
+ struct spi_nor *nor = &mt8173_nor->nor;
+
+ switch (nor->flash_read) {
+ case SPI_NOR_FAST:
+ writeb(nor->read_opcode, mt8173_nor->base +
+ MTK_NOR_PRGDATA3_REG);
+ writeb(MTK_NOR_FAST_READ, mt8173_nor->base +
+ MTK_NOR_CFG1_REG);
+ break;
+ case SPI_NOR_DUAL:
+ writeb(nor->read_opcode, mt8173_nor->base +
+ MTK_NOR_PRGDATA3_REG);
+ writeb(MTK_NOR_DUAL_READ_EN, mt8173_nor->base +
+ MTK_NOR_DUAL_REG);
+ break;
+ case SPI_NOR_QUAD:
+ writeb(nor->read_opcode, mt8173_nor->base +
+ MTK_NOR_PRGDATA4_REG);
+ writeb(MTK_NOR_QUAD_READ_EN, mt8173_nor->base +
+ MTK_NOR_DUAL_REG);
+ break;
+ default:
+ writeb(MTK_NOR_DUAL_DISABLE, mt8173_nor->base +
+ MTK_NOR_DUAL_REG);
+ break;
+ }
+}
+
+static int mt8173_nor_execute_cmd(struct mt8173_nor *mt8173_nor, u8 cmdval)
+{
+ int reg;
+ u8 val = cmdval & 0x1f;
+
+ writeb(cmdval, mt8173_nor->base + MTK_NOR_CMD_REG);
+ return readl_poll_timeout(mt8173_nor->base + MTK_NOR_CMD_REG, reg,
+ !(reg & val), 100, 10000);
+}
+
+static int mt8173_nor_do_tx_rx(struct mt8173_nor *mt8173_nor, u8 op,
+ u8 *tx, int txlen, u8 *rx, int rxlen)
+{
+ int len = 1 + txlen + rxlen;
+ int i, ret, idx;
+
+ if (len > MTK_NOR_MAX_SHIFT)
+ return -EINVAL;
+
+ writeb(len * 8, mt8173_nor->base + MTK_NOR_CNT_REG);
+
+ /* start at PRGDATA5, go down to PRGDATA0 */
+ idx = MTK_NOR_MAX_RX_TX_SHIFT - 1;
+
+ /* opcode */
+ writeb(op, mt8173_nor->base + MTK_NOR_PRG_REG(idx));
+ idx--;
+
+ /* program TX data */
+ for (i = 0; i < txlen; i++, idx--)
+ writeb(tx[i], mt8173_nor->base + MTK_NOR_PRG_REG(idx));
+
+ /* clear out rest of TX registers */
+ while (idx >= 0) {
+ writeb(0, mt8173_nor->base + MTK_NOR_PRG_REG(idx));
+ idx--;
+ }
+
+ ret = mt8173_nor_execute_cmd(mt8173_nor, MTK_NOR_PRG_CMD);
+ if (ret)
+ return ret;
+
+ /* restart at first RX byte */
+ idx = rxlen - 1;
+
+ /* read out RX data */
+ for (i = 0; i < rxlen; i++, idx--)
+ rx[i] = readb(mt8173_nor->base + MTK_NOR_SHREG(idx));
+
+ return 0;
+}
+
+/* Do a WRSR (Write Status Register) command */
+static int mt8173_nor_wr_sr(struct mt8173_nor *mt8173_nor, u8 sr)
+{
+ writeb(sr, mt8173_nor->base + MTK_NOR_PRGDATA5_REG);
+ writeb(8, mt8173_nor->base + MTK_NOR_CNT_REG);
+ return mt8173_nor_execute_cmd(mt8173_nor, MTK_NOR_WRSR_CMD);
+}
+
+static int mt8173_nor_write_buffer_enable(struct mt8173_nor *mt8173_nor)
+{
+ u8 reg;
+
+ /* the bit0 of MTK_NOR_CFG2_REG is pre-fetch buffer
+ * 0: pre-fetch buffer use for read
+ * 1: pre-fetch buffer use for page program
+ */
+ writel(MTK_NOR_WR_BUF_ENABLE, mt8173_nor->base + MTK_NOR_CFG2_REG);
+ return readb_poll_timeout(mt8173_nor->base + MTK_NOR_CFG2_REG, reg,
+ 0x01 == (reg & 0x01), 100, 10000);
+}
+
+static int mt8173_nor_write_buffer_disable(struct mt8173_nor *mt8173_nor)
+{
+ u8 reg;
+
+ writel(MTK_NOR_WR_BUF_DISABLE, mt8173_nor->base + MTK_NOR_CFG2_REG);
+ return readb_poll_timeout(mt8173_nor->base + MTK_NOR_CFG2_REG, reg,
+ MTK_NOR_WR_BUF_DISABLE == (reg & 0x1), 100,
+ 10000);
+}
+
+static void mt8173_nor_set_addr(struct mt8173_nor *mt8173_nor, u32 addr)
+{
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ writeb(addr & 0xff, mt8173_nor->base + MTK_NOR_RADR0_REG + i * 4);
+ addr >>= 8;
+ }
+ /* Last register is non-contiguous */
+ writeb(addr & 0xff, mt8173_nor->base + MTK_NOR_RADR3_REG);
+}
+
+static int mt8173_nor_read(struct spi_nor *nor, loff_t from, size_t length,
+ size_t *retlen, u_char *buffer)
+{
+ int i, ret;
+ int addr = (int)from;
+ u8 *buf = (u8 *)buffer;
+ struct mt8173_nor *mt8173_nor = nor->priv;
+
+ /* set mode for fast read mode ,dual mode or quad mode */
+ mt8173_nor_set_read_mode(mt8173_nor);
+ mt8173_nor_set_addr(mt8173_nor, addr);
+
+ for (i = 0; i < length; i++, (*retlen)++) {
+ ret = mt8173_nor_execute_cmd(mt8173_nor, MTK_NOR_PIO_READ_CMD);
+ if (ret < 0)
+ return ret;
+ buf[i] = readb(mt8173_nor->base + MTK_NOR_RDATA_REG);
+ }
+ return 0;
+}
+
+static int mt8173_nor_write_single_byte(struct mt8173_nor *mt8173_nor,
+ int addr, int length, u8 *data)
+{
+ int i, ret;
+
+ mt8173_nor_set_addr(mt8173_nor, addr);
+
+ for (i = 0; i < length; i++) {
+ writeb(*data++, mt8173_nor->base + MTK_NOR_WDATA_REG);
+ ret = mt8173_nor_execute_cmd(mt8173_nor, MTK_NOR_PIO_WR_CMD);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int mt8173_nor_write_buffer(struct mt8173_nor *mt8173_nor, int addr,
+ const u8 *buf)
+{
+ int i, bufidx, data;
+
+ mt8173_nor_set_addr(mt8173_nor, addr);
+
+ bufidx = 0;
+ for (i = 0; i < SFLASH_WRBUF_SIZE; i += 4) {
+ data = buf[bufidx + 3]<<24 | buf[bufidx + 2]<<16 |
+ buf[bufidx + 1]<<8 | buf[bufidx];
+ bufidx += 4;
+ writel(data, mt8173_nor->base + MTK_NOR_PP_DATA_REG);
+ }
+ return mt8173_nor_execute_cmd(mt8173_nor, MTK_NOR_WR_CMD);
+}
+
+static void mt8173_nor_write(struct spi_nor *nor, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ int ret;
+ struct mt8173_nor *mt8173_nor = nor->priv;
+
+ ret = mt8173_nor_write_buffer_enable(mt8173_nor);
+ if (ret < 0)
+ dev_warn(mt8173_nor->dev, "write buffer enable failed!\n");
+
+ while (len >= SFLASH_WRBUF_SIZE) {
+ ret = mt8173_nor_write_buffer(mt8173_nor, to, buf);
+ if (ret < 0)
+ dev_err(mt8173_nor->dev, "write buffer failed!\n");
+ len -= SFLASH_WRBUF_SIZE;
+ to += SFLASH_WRBUF_SIZE;
+ buf += SFLASH_WRBUF_SIZE;
+ (*retlen) += SFLASH_WRBUF_SIZE;
+ }
+ ret = mt8173_nor_write_buffer_disable(mt8173_nor);
+ if (ret < 0)
+ dev_warn(mt8173_nor->dev, "write buffer disable failed!\n");
+
+ if (len) {
+ ret = mt8173_nor_write_single_byte(mt8173_nor, to, (int)len,
+ (u8 *)buf);
+ if (ret < 0)
+ dev_err(mt8173_nor->dev, "write single byte failed!\n");
+ (*retlen) += len;
+ }
+}
+
+static int mt8173_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
+{
+ int ret;
+ struct mt8173_nor *mt8173_nor = nor->priv;
+
+ switch (opcode) {
+ case SPINOR_OP_RDSR:
+ ret = mt8173_nor_execute_cmd(mt8173_nor, MTK_NOR_RDSR_CMD);
+ if (ret < 0)
+ return ret;
+ if (len == 1)
+ *buf = readb(mt8173_nor->base + MTK_NOR_RDSR_REG);
+ else
+ dev_err(mt8173_nor->dev, "len should be 1 for read status!\n");
+ break;
+ default:
+ ret = mt8173_nor_do_tx_rx(mt8173_nor, opcode, NULL, 0, buf, len);
+ break;
+ }
+ return ret;
+}
+
+static int mt8173_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
+ int len)
+{
+ int ret;
+ struct mt8173_nor *mt8173_nor = nor->priv;
+
+ switch (opcode) {
+ case SPINOR_OP_WRSR:
+ /* We only handle 1 byte */
+ ret = mt8173_nor_wr_sr(mt8173_nor, *buf);
+ break;
+ default:
+ ret = mt8173_nor_do_tx_rx(mt8173_nor, opcode, buf, len, NULL, 0);
+ if (ret)
+ dev_warn(mt8173_nor->dev, "write reg failure!\n");
+ break;
+ }
+ return ret;
+}
+
+static int __init mtk_nor_init(struct mt8173_nor *mt8173_nor,
+ struct device_node *flash_node)
+{
+ int ret;
+ struct spi_nor *nor;
+
+ /* initialize controller to accept commands */
+ writel(MTK_NOR_ENABLE_SF_CMD, mt8173_nor->base + MTK_NOR_WRPROT_REG);
+
+ nor = &mt8173_nor->nor;
+ nor->dev = mt8173_nor->dev;
+ nor->priv = mt8173_nor;
+ spi_nor_set_flash_node(nor, flash_node);
+
+ /* fill the hooks to spi nor */
+ nor->read = mt8173_nor_read;
+ nor->read_reg = mt8173_nor_read_reg;
+ nor->write = mt8173_nor_write;
+ nor->write_reg = mt8173_nor_write_reg;
+ nor->mtd.name = "mtk_nor";
+ /* initialized with NULL */
+ ret = spi_nor_scan(nor, NULL, SPI_NOR_DUAL);
+ if (ret)
+ return ret;
+
+ return mtd_device_register(&nor->mtd, NULL, 0);
+}
+
+static int mtk_nor_drv_probe(struct platform_device *pdev)
+{
+ struct device_node *flash_np;
+ struct resource *res;
+ int ret;
+ struct mt8173_nor *mt8173_nor;
+
+ if (!pdev->dev.of_node) {
+ dev_err(&pdev->dev, "No DT found\n");
+ return -EINVAL;
+ }
+
+ mt8173_nor = devm_kzalloc(&pdev->dev, sizeof(*mt8173_nor), GFP_KERNEL);
+ if (!mt8173_nor)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, mt8173_nor);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mt8173_nor->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mt8173_nor->base))
+ return PTR_ERR(mt8173_nor->base);
+
+ mt8173_nor->spi_clk = devm_clk_get(&pdev->dev, "spi");
+ if (IS_ERR(mt8173_nor->spi_clk))
+ return PTR_ERR(mt8173_nor->spi_clk);
+
+ mt8173_nor->nor_clk = devm_clk_get(&pdev->dev, "sf");
+ if (IS_ERR(mt8173_nor->nor_clk))
+ return PTR_ERR(mt8173_nor->nor_clk);
+
+ mt8173_nor->dev = &pdev->dev;
+ ret = clk_prepare_enable(mt8173_nor->spi_clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(mt8173_nor->nor_clk);
+ if (ret) {
+ clk_disable_unprepare(mt8173_nor->spi_clk);
+ return ret;
+ }
+ /* only support one attached flash */
+ flash_np = of_get_next_available_child(pdev->dev.of_node, NULL);
+ if (!flash_np) {
+ dev_err(&pdev->dev, "no SPI flash device to configure\n");
+ ret = -ENODEV;
+ goto nor_free;
+ }
+ ret = mtk_nor_init(mt8173_nor, flash_np);
+
+nor_free:
+ if (ret) {
+ clk_disable_unprepare(mt8173_nor->spi_clk);
+ clk_disable_unprepare(mt8173_nor->nor_clk);
+ }
+ return ret;
+}
+
+static int mtk_nor_drv_remove(struct platform_device *pdev)
+{
+ struct mt8173_nor *mt8173_nor = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(mt8173_nor->spi_clk);
+ clk_disable_unprepare(mt8173_nor->nor_clk);
+ return 0;
+}
+
+static const struct of_device_id mtk_nor_of_ids[] = {
+ { .compatible = "mediatek,mt8173-nor"},
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mtk_nor_of_ids);
+
+static struct platform_driver mtk_nor_driver = {
+ .probe = mtk_nor_drv_probe,
+ .remove = mtk_nor_drv_remove,
+ .driver = {
+ .name = "mtk-nor",
+ .of_match_table = mtk_nor_of_ids,
+ },
+};
+
+module_platform_driver(mtk_nor_driver);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MediaTek SPI NOR Flash Driver");
diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c
index 9e82098ae644..ae428cb0e04b 100644
--- a/drivers/mtd/spi-nor/nxp-spifi.c
+++ b/drivers/mtd/spi-nor/nxp-spifi.c
@@ -271,7 +271,6 @@ static void nxp_spifi_dummy_id_read(struct spi_nor *nor)
static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
struct device_node *np)
{
- struct mtd_part_parser_data ppdata;
enum read_mode flash_read;
u32 ctrl, property;
u16 mode = 0;
@@ -330,7 +329,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
writel(ctrl, spifi->io_base + SPIFI_CTRL);
spifi->nor.dev = spifi->dev;
- spifi->nor.flash_node = np;
+ spi_nor_set_flash_node(&spifi->nor, np);
spifi->nor.priv = spifi;
spifi->nor.read = nxp_spifi_read;
spifi->nor.write = nxp_spifi_write;
@@ -361,8 +360,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
return ret;
}
- ppdata.of_node = np;
- ret = mtd_device_parse_register(&spifi->nor.mtd, NULL, &ppdata, NULL, 0);
+ ret = mtd_device_register(&spifi->nor.mtd, NULL, 0);
if (ret) {
dev_err(spifi->dev, "mtd device parse failed\n");
return ret;
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 49883905a434..ed0c19c558b5 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -38,6 +38,7 @@
#define CHIP_ERASE_2MB_READY_WAIT_JIFFIES (40UL * HZ)
#define SPI_NOR_MAX_ID_LEN 6
+#define SPI_NOR_MAX_ADDR_WIDTH 4
struct flash_info {
char *name;
@@ -313,6 +314,29 @@ static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
}
/*
+ * Initiate the erasure of a single sector
+ */
+static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
+{
+ u8 buf[SPI_NOR_MAX_ADDR_WIDTH];
+ int i;
+
+ if (nor->erase)
+ return nor->erase(nor, addr);
+
+ /*
+ * Default implementation, if driver doesn't have a specialized HW
+ * control
+ */
+ for (i = nor->addr_width - 1; i >= 0; i--) {
+ buf[i] = addr & 0xff;
+ addr >>= 8;
+ }
+
+ return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width);
+}
+
+/*
* Erase an address range on the nor chip. The address range may extend
* one or more erase sectors. Return an error is there is a problem erasing.
*/
@@ -371,10 +395,9 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
while (len) {
write_enable(nor);
- if (nor->erase(nor, addr)) {
- ret = -EIO;
+ ret = spi_nor_erase_sector(nor, addr);
+ if (ret)
goto erase_err;
- }
addr += mtd->erasesize;
len -= mtd->erasesize;
@@ -387,17 +410,13 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
write_disable(nor);
+erase_err:
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
- instr->state = MTD_ERASE_DONE;
+ instr->state = ret ? MTD_ERASE_FAILED : MTD_ERASE_DONE;
mtd_erase_callback(instr);
return ret;
-
-erase_err:
- spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
- instr->state = MTD_ERASE_FAILED;
- return ret;
}
static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs,
@@ -459,11 +478,14 @@ static int stm_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
{
struct mtd_info *mtd = &nor->mtd;
- u8 status_old, status_new;
+ int status_old, status_new;
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
u8 shift = ffs(mask) - 1, pow, val;
+ int ret;
status_old = read_sr(nor);
+ if (status_old < 0)
+ return status_old;
/* SPI NOR always locks to the end */
if (ofs + len != mtd->size) {
@@ -498,7 +520,10 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
return -EINVAL;
write_enable(nor);
- return write_sr(nor, status_new);
+ ret = write_sr(nor, status_new);
+ if (ret)
+ return ret;
+ return spi_nor_wait_till_ready(nor);
}
/*
@@ -509,15 +534,18 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
{
struct mtd_info *mtd = &nor->mtd;
- uint8_t status_old, status_new;
+ int status_old, status_new;
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
u8 shift = ffs(mask) - 1, pow, val;
+ int ret;
status_old = read_sr(nor);
+ if (status_old < 0)
+ return status_old;
/* Cannot unlock; would unlock larger region than requested */
- if (stm_is_locked_sr(nor, status_old, ofs - mtd->erasesize,
- mtd->erasesize))
+ if (stm_is_locked_sr(nor, ofs - mtd->erasesize, mtd->erasesize,
+ status_old))
return -EINVAL;
/*
@@ -546,7 +574,10 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
return -EINVAL;
write_enable(nor);
- return write_sr(nor, status_new);
+ ret = write_sr(nor, status_new);
+ if (ret)
+ return ret;
+ return spi_nor_wait_till_ready(nor);
}
/*
@@ -715,9 +746,9 @@ static const struct flash_info spi_nor_ids[] = {
{ "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) },
{ "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) },
{ "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) },
- { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) },
+ { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, SECT_4K) },
{ "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) },
- { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) },
+ { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, SECT_4K) },
{ "mx25u6435f", INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) },
{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
@@ -856,7 +887,7 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
if (tmp < 0) {
- dev_dbg(nor->dev, " error %d reading JEDEC ID\n", tmp);
+ dev_dbg(nor->dev, "error %d reading JEDEC ID\n", tmp);
return ERR_PTR(tmp);
}
@@ -867,7 +898,7 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
return &spi_nor_ids[tmp];
}
}
- dev_err(nor->dev, "unrecognized JEDEC id bytes: %02x, %2x, %2x\n",
+ dev_err(nor->dev, "unrecognized JEDEC id bytes: %02x, %02x, %02x\n",
id[0], id[1], id[2]);
return ERR_PTR(-ENODEV);
}
@@ -1013,6 +1044,8 @@ static int macronix_quad_enable(struct spi_nor *nor)
int ret, val;
val = read_sr(nor);
+ if (val < 0)
+ return val;
write_enable(nor);
write_sr(nor, val | SR_QUAD_EN_MX);
@@ -1138,7 +1171,7 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
static int spi_nor_check(struct spi_nor *nor)
{
if (!nor->dev || !nor->read || !nor->write ||
- !nor->read_reg || !nor->write_reg || !nor->erase) {
+ !nor->read_reg || !nor->write_reg) {
pr_err("spi-nor: please fill all the necessary fields!\n");
return -EINVAL;
}
@@ -1151,7 +1184,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
const struct flash_info *info = NULL;
struct device *dev = nor->dev;
struct mtd_info *mtd = &nor->mtd;
- struct device_node *np = nor->flash_node;
+ struct device_node *np = spi_nor_get_flash_node(nor);
int ret;
int i;
@@ -1200,8 +1233,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
if (JEDEC_MFR(info) == SNOR_MFR_ATMEL ||
JEDEC_MFR(info) == SNOR_MFR_INTEL ||
- JEDEC_MFR(info) == SNOR_MFR_SST ||
- JEDEC_MFR(info) == SNOR_MFR_WINBOND) {
+ JEDEC_MFR(info) == SNOR_MFR_SST) {
write_enable(nor);
write_sr(nor, 0);
}
@@ -1217,8 +1249,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
mtd->_read = spi_nor_read;
/* NOR protection support for STmicro/Micron chips and similar */
- if (JEDEC_MFR(info) == SNOR_MFR_MICRON ||
- JEDEC_MFR(info) == SNOR_MFR_WINBOND) {
+ if (JEDEC_MFR(info) == SNOR_MFR_MICRON) {
nor->flash_lock = stm_lock;
nor->flash_unlock = stm_unlock;
nor->flash_is_locked = stm_is_locked;
@@ -1340,6 +1371,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
nor->addr_width = 3;
}
+ if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) {
+ dev_err(dev, "address width is too large: %u\n",
+ nor->addr_width);
+ return -EINVAL;
+ }
+
nor->read_dummy = spi_nor_read_dummy_cycles(nor);
dev_info(dev, "%s (%lld Kbytes)\n", info->name,
diff --git a/drivers/mtd/tests/pagetest.c b/drivers/mtd/tests/pagetest.c
index ba1890d5632c..ff1e0565b020 100644
--- a/drivers/mtd/tests/pagetest.c
+++ b/drivers/mtd/tests/pagetest.c
@@ -127,13 +127,12 @@ static int crosstest(void)
unsigned char *pp1, *pp2, *pp3, *pp4;
pr_info("crosstest\n");
- pp1 = kmalloc(pgsize * 4, GFP_KERNEL);
+ pp1 = kzalloc(pgsize * 4, GFP_KERNEL);
if (!pp1)
return -ENOMEM;
pp2 = pp1 + pgsize;
pp3 = pp2 + pgsize;
pp4 = pp3 + pgsize;
- memset(pp1, 0, pgsize * 4);
addr0 = 0;
for (i = 0; i < ebcnt && bbt[i]; ++i)
diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c
index b077e43b5ba9..c4cb15a3098c 100644
--- a/drivers/mtd/ubi/debug.c
+++ b/drivers/mtd/ubi/debug.c
@@ -236,7 +236,7 @@ int ubi_debugfs_init(void)
dfs_rootdir = debugfs_create_dir("ubi", NULL);
if (IS_ERR_OR_NULL(dfs_rootdir)) {
- int err = dfs_rootdir ? -ENODEV : PTR_ERR(dfs_rootdir);
+ int err = dfs_rootdir ? PTR_ERR(dfs_rootdir) : -ENODEV;
pr_err("UBI error: cannot create \"ubi\" debugfs directory, error %d\n",
err);
diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c
index 1fc23e48fe8e..10cf3b549959 100644
--- a/drivers/mtd/ubi/io.c
+++ b/drivers/mtd/ubi/io.c
@@ -1299,7 +1299,7 @@ static int self_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum)
if (err && err != UBI_IO_BITFLIPS && !mtd_is_eccerr(err))
goto exit;
- crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_EC_HDR_SIZE_CRC);
+ crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_VID_HDR_SIZE_CRC);
hdr_crc = be32_to_cpu(vid_hdr->hdr_crc);
if (hdr_crc != crc) {
ubi_err(ubi, "bad VID header CRC at PEB %d, calculated %#08x, read %#08x",
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index eb4489f9082f..17ec948ac40e 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -603,6 +603,7 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
return 0;
}
+static int __erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk);
/**
* do_sync_erase - run the erase worker synchronously.
* @ubi: UBI device description object
@@ -615,22 +616,19 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
static int do_sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
int vol_id, int lnum, int torture)
{
- struct ubi_work *wl_wrk;
+ struct ubi_work wl_wrk;
dbg_wl("sync erase of PEB %i", e->pnum);
- wl_wrk = kmalloc(sizeof(struct ubi_work), GFP_NOFS);
- if (!wl_wrk)
- return -ENOMEM;
+ wl_wrk.e = e;
+ wl_wrk.vol_id = vol_id;
+ wl_wrk.lnum = lnum;
+ wl_wrk.torture = torture;
- wl_wrk->e = e;
- wl_wrk->vol_id = vol_id;
- wl_wrk->lnum = lnum;
- wl_wrk->torture = torture;
-
- return erase_worker(ubi, wl_wrk, 0);
+ return __erase_worker(ubi, &wl_wrk);
}
+static int ensure_wear_leveling(struct ubi_device *ubi, int nested);
/**
* wear_leveling_worker - wear-leveling worker function.
* @ubi: UBI device description object
@@ -652,6 +650,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
#endif
struct ubi_wl_entry *e1, *e2;
struct ubi_vid_hdr *vid_hdr;
+ int dst_leb_clean = 0;
kfree(wrk);
if (shutdown)
@@ -756,6 +755,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
err = ubi_io_read_vid_hdr(ubi, e1->pnum, vid_hdr, 0);
if (err && err != UBI_IO_BITFLIPS) {
+ dst_leb_clean = 1;
if (err == UBI_IO_FF) {
/*
* We are trying to move PEB without a VID header. UBI
@@ -801,10 +801,12 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
* protection queue.
*/
protect = 1;
+ dst_leb_clean = 1;
goto out_not_moved;
}
if (err == MOVE_RETRY) {
scrubbing = 1;
+ dst_leb_clean = 1;
goto out_not_moved;
}
if (err == MOVE_TARGET_BITFLIPS || err == MOVE_TARGET_WR_ERR ||
@@ -830,6 +832,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
ubi->erroneous_peb_count);
goto out_error;
}
+ dst_leb_clean = 1;
erroneous = 1;
goto out_not_moved;
}
@@ -900,15 +903,24 @@ out_not_moved:
wl_tree_add(e1, &ubi->scrub);
else
wl_tree_add(e1, &ubi->used);
+ if (dst_leb_clean) {
+ wl_tree_add(e2, &ubi->free);
+ ubi->free_count++;
+ }
+
ubi_assert(!ubi->move_to_put);
ubi->move_from = ubi->move_to = NULL;
ubi->wl_scheduled = 0;
spin_unlock(&ubi->wl_lock);
ubi_free_vid_hdr(ubi, vid_hdr);
- err = do_sync_erase(ubi, e2, vol_id, lnum, torture);
- if (err)
- goto out_ro;
+ if (dst_leb_clean) {
+ ensure_wear_leveling(ubi, 1);
+ } else {
+ err = do_sync_erase(ubi, e2, vol_id, lnum, torture);
+ if (err)
+ goto out_ro;
+ }
mutex_unlock(&ubi->move_mutex);
return 0;
@@ -1014,7 +1026,7 @@ out_unlock:
}
/**
- * erase_worker - physical eraseblock erase worker function.
+ * __erase_worker - physical eraseblock erase worker function.
* @ubi: UBI device description object
* @wl_wrk: the work object
* @shutdown: non-zero if the worker has to free memory and exit
@@ -1025,8 +1037,7 @@ out_unlock:
* needed. Returns zero in case of success and a negative error code in case of
* failure.
*/
-static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
- int shutdown)
+static int __erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk)
{
struct ubi_wl_entry *e = wl_wrk->e;
int pnum = e->pnum;
@@ -1034,21 +1045,11 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
int lnum = wl_wrk->lnum;
int err, available_consumed = 0;
- if (shutdown) {
- dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec);
- kfree(wl_wrk);
- wl_entry_destroy(ubi, e);
- return 0;
- }
-
dbg_wl("erase PEB %d EC %d LEB %d:%d",
pnum, e->ec, wl_wrk->vol_id, wl_wrk->lnum);
err = sync_erase(ubi, e, wl_wrk->torture);
if (!err) {
- /* Fine, we've erased it successfully */
- kfree(wl_wrk);
-
spin_lock(&ubi->wl_lock);
wl_tree_add(e, &ubi->free);
ubi->free_count++;
@@ -1066,7 +1067,6 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
}
ubi_err(ubi, "failed to erase PEB %d, error %d", pnum, err);
- kfree(wl_wrk);
if (err == -EINTR || err == -ENOMEM || err == -EAGAIN ||
err == -EBUSY) {
@@ -1075,6 +1075,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
/* Re-schedule the LEB for erasure */
err1 = schedule_erase(ubi, e, vol_id, lnum, 0);
if (err1) {
+ wl_entry_destroy(ubi, e);
err = err1;
goto out_ro;
}
@@ -1150,6 +1151,25 @@ out_ro:
return err;
}
+static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
+ int shutdown)
+{
+ int ret;
+
+ if (shutdown) {
+ struct ubi_wl_entry *e = wl_wrk->e;
+
+ dbg_wl("cancel erasure of PEB %d EC %d", e->pnum, e->ec);
+ kfree(wl_wrk);
+ wl_entry_destroy(ubi, e);
+ return 0;
+ }
+
+ ret = __erase_worker(ubi, wl_wrk);
+ kfree(wl_wrk);
+ return ret;
+}
+
/**
* ubi_wl_put_peb - return a PEB to the wear-leveling sub-system.
* @ubi: UBI device description object
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index 940e2ebbdea8..4cbb8b27a891 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -93,7 +93,8 @@ enum ad_link_speed_type {
AD_LINK_SPEED_10000MBPS,
AD_LINK_SPEED_20000MBPS,
AD_LINK_SPEED_40000MBPS,
- AD_LINK_SPEED_56000MBPS
+ AD_LINK_SPEED_56000MBPS,
+ AD_LINK_SPEED_100000MBPS,
};
/* compare MAC addresses */
@@ -258,6 +259,7 @@ static inline int __check_agg_selection_timer(struct port *port)
* %AD_LINK_SPEED_20000MBPS
* %AD_LINK_SPEED_40000MBPS
* %AD_LINK_SPEED_56000MBPS
+ * %AD_LINK_SPEED_100000MBPS
*/
static u16 __get_link_speed(struct port *port)
{
@@ -305,6 +307,10 @@ static u16 __get_link_speed(struct port *port)
speed = AD_LINK_SPEED_56000MBPS;
break;
+ case SPEED_100000:
+ speed = AD_LINK_SPEED_100000MBPS;
+ break;
+
default:
/* unknown speed value from ethtool. shouldn't happen */
speed = 0;
@@ -681,6 +687,9 @@ static u32 __get_agg_bandwidth(struct aggregator *aggregator)
case AD_LINK_SPEED_56000MBPS:
bandwidth = aggregator->num_of_ports * 56000;
break;
+ case AD_LINK_SPEED_100000MBPS:
+ bandwidth = aggregator->num_of_ports * 100000;
+ break;
default:
bandwidth = 0; /* to silence the compiler */
}
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 9e0f8a7ef8b1..56b560558884 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -830,7 +830,8 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
}
new_active->delay = 0;
- bond_set_slave_link_state(new_active, BOND_LINK_UP);
+ bond_set_slave_link_state(new_active, BOND_LINK_UP,
+ BOND_SLAVE_NOTIFY_NOW);
if (BOND_MODE(bond) == BOND_MODE_8023AD)
bond_3ad_handle_link_change(new_active, BOND_LINK_UP);
@@ -1066,12 +1067,12 @@ static netdev_features_t bond_fix_features(struct net_device *dev,
return features;
}
-#define BOND_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \
+#define BOND_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \
NETIF_F_HIGHDMA | NETIF_F_LRO)
-#define BOND_ENC_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | NETIF_F_RXCSUM |\
- NETIF_F_ALL_TSO)
+#define BOND_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
+ NETIF_F_RXCSUM | NETIF_F_ALL_TSO)
static void bond_compute_features(struct bonding *bond)
{
@@ -1198,26 +1199,42 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb)
return ret;
}
-static int bond_master_upper_dev_link(struct net_device *bond_dev,
- struct net_device *slave_dev,
- struct slave *slave)
+static enum netdev_lag_tx_type bond_lag_tx_type(struct bonding *bond)
{
+ switch (BOND_MODE(bond)) {
+ case BOND_MODE_ROUNDROBIN:
+ return NETDEV_LAG_TX_TYPE_ROUNDROBIN;
+ case BOND_MODE_ACTIVEBACKUP:
+ return NETDEV_LAG_TX_TYPE_ACTIVEBACKUP;
+ case BOND_MODE_BROADCAST:
+ return NETDEV_LAG_TX_TYPE_BROADCAST;
+ case BOND_MODE_XOR:
+ case BOND_MODE_8023AD:
+ return NETDEV_LAG_TX_TYPE_HASH;
+ default:
+ return NETDEV_LAG_TX_TYPE_UNKNOWN;
+ }
+}
+
+static int bond_master_upper_dev_link(struct bonding *bond, struct slave *slave)
+{
+ struct netdev_lag_upper_info lag_upper_info;
int err;
- err = netdev_master_upper_dev_link_private(slave_dev, bond_dev, slave);
+ lag_upper_info.tx_type = bond_lag_tx_type(bond);
+ err = netdev_master_upper_dev_link(slave->dev, bond->dev, slave,
+ &lag_upper_info);
if (err)
return err;
- slave_dev->flags |= IFF_SLAVE;
- rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE, GFP_KERNEL);
+ rtmsg_ifinfo(RTM_NEWLINK, slave->dev, IFF_SLAVE, GFP_KERNEL);
return 0;
}
-static void bond_upper_dev_unlink(struct net_device *bond_dev,
- struct net_device *slave_dev)
+static void bond_upper_dev_unlink(struct bonding *bond, struct slave *slave)
{
- netdev_upper_dev_unlink(slave_dev, bond_dev);
- slave_dev->flags &= ~IFF_SLAVE;
- rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE, GFP_KERNEL);
+ netdev_upper_dev_unlink(slave->dev, bond->dev);
+ slave->dev->flags &= ~IFF_SLAVE;
+ rtmsg_ifinfo(RTM_NEWLINK, slave->dev, IFF_SLAVE, GFP_KERNEL);
}
static struct slave *bond_alloc_slave(struct bonding *bond)
@@ -1299,6 +1316,16 @@ void bond_queue_slave_event(struct slave *slave)
queue_delayed_work(slave->bond->wq, &nnw->work, 0);
}
+void bond_lower_state_changed(struct slave *slave)
+{
+ struct netdev_lag_lower_state_info info;
+
+ info.link_up = slave->link == BOND_LINK_UP ||
+ slave->link == BOND_LINK_FAIL;
+ info.tx_enabled = bond_is_active_slave(slave);
+ netdev_lower_state_changed(slave->dev, &info);
+}
+
/* enslave device <slave> to bond device <master> */
int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
{
@@ -1351,7 +1378,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
* the current ifenslave will set the interface down prior to
* enslaving it; the old ifenslave will not.
*/
- if ((slave_dev->flags & IFF_UP)) {
+ if (slave_dev->flags & IFF_UP) {
netdev_err(bond_dev, "%s is up - this may be due to an out of date ifenslave\n",
slave_dev->name);
res = -EPERM;
@@ -1465,6 +1492,9 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
}
}
+ /* set slave flag before open to prevent IPv6 addrconf */
+ slave_dev->flags |= IFF_SLAVE;
+
/* open the slave since the application closed it */
res = dev_open(slave_dev);
if (res) {
@@ -1563,21 +1593,26 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
if (bond_check_dev_link(bond, slave_dev, 0) == BMSR_LSTATUS) {
if (bond->params.updelay) {
bond_set_slave_link_state(new_slave,
- BOND_LINK_BACK);
+ BOND_LINK_BACK,
+ BOND_SLAVE_NOTIFY_NOW);
new_slave->delay = bond->params.updelay;
} else {
bond_set_slave_link_state(new_slave,
- BOND_LINK_UP);
+ BOND_LINK_UP,
+ BOND_SLAVE_NOTIFY_NOW);
}
} else {
- bond_set_slave_link_state(new_slave, BOND_LINK_DOWN);
+ bond_set_slave_link_state(new_slave, BOND_LINK_DOWN,
+ BOND_SLAVE_NOTIFY_NOW);
}
} else if (bond->params.arp_interval) {
bond_set_slave_link_state(new_slave,
(netif_carrier_ok(slave_dev) ?
- BOND_LINK_UP : BOND_LINK_DOWN));
+ BOND_LINK_UP : BOND_LINK_DOWN),
+ BOND_SLAVE_NOTIFY_NOW);
} else {
- bond_set_slave_link_state(new_slave, BOND_LINK_UP);
+ bond_set_slave_link_state(new_slave, BOND_LINK_UP,
+ BOND_SLAVE_NOTIFY_NOW);
}
if (new_slave->link != BOND_LINK_DOWN)
@@ -1662,7 +1697,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
goto err_detach;
}
- res = bond_master_upper_dev_link(bond_dev, slave_dev, new_slave);
+ res = bond_master_upper_dev_link(bond, new_slave);
if (res) {
netdev_dbg(bond_dev, "Error %d calling bond_master_upper_dev_link\n", res);
goto err_unregister;
@@ -1698,7 +1733,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
/* Undo stages on error */
err_upper_unlink:
- bond_upper_dev_unlink(bond_dev, slave_dev);
+ bond_upper_dev_unlink(bond, new_slave);
err_unregister:
netdev_rx_handler_unregister(slave_dev);
@@ -1725,6 +1760,7 @@ err_close:
dev_close(slave_dev);
err_restore_mac:
+ slave_dev->flags &= ~IFF_SLAVE;
if (!bond->params.fail_over_mac ||
BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) {
/* XXX TODO - fom follow mode needs to change master's
@@ -1799,12 +1835,14 @@ static int __bond_release_one(struct net_device *bond_dev,
return -EINVAL;
}
+ bond_set_slave_inactive_flags(slave, BOND_SLAVE_NOTIFY_NOW);
+
bond_sysfs_slave_del(slave);
/* recompute stats just before removing the slave */
bond_get_stats(bond->dev, &bond->bond_stats);
- bond_upper_dev_unlink(bond_dev, slave_dev);
+ bond_upper_dev_unlink(bond, slave);
/* unregister rx_handler early so bond_handle_frame wouldn't be called
* for this slave anymore.
*/
@@ -1996,7 +2034,8 @@ static int bond_miimon_inspect(struct bonding *bond)
if (link_state)
continue;
- bond_set_slave_link_state(slave, BOND_LINK_FAIL);
+ bond_set_slave_link_state(slave, BOND_LINK_FAIL,
+ BOND_SLAVE_NOTIFY_LATER);
slave->delay = bond->params.downdelay;
if (slave->delay) {
netdev_info(bond->dev, "link status down for %sinterface %s, disabling it in %d ms\n",
@@ -2011,7 +2050,8 @@ static int bond_miimon_inspect(struct bonding *bond)
case BOND_LINK_FAIL:
if (link_state) {
/* recovered before downdelay expired */
- bond_set_slave_link_state(slave, BOND_LINK_UP);
+ bond_set_slave_link_state(slave, BOND_LINK_UP,
+ BOND_SLAVE_NOTIFY_LATER);
slave->last_link_up = jiffies;
netdev_info(bond->dev, "link status up again after %d ms for interface %s\n",
(bond->params.downdelay - slave->delay) *
@@ -2033,7 +2073,8 @@ static int bond_miimon_inspect(struct bonding *bond)
if (!link_state)
continue;
- bond_set_slave_link_state(slave, BOND_LINK_BACK);
+ bond_set_slave_link_state(slave, BOND_LINK_BACK,
+ BOND_SLAVE_NOTIFY_LATER);
slave->delay = bond->params.updelay;
if (slave->delay) {
@@ -2047,7 +2088,8 @@ static int bond_miimon_inspect(struct bonding *bond)
case BOND_LINK_BACK:
if (!link_state) {
bond_set_slave_link_state(slave,
- BOND_LINK_DOWN);
+ BOND_LINK_DOWN,
+ BOND_SLAVE_NOTIFY_LATER);
netdev_info(bond->dev, "link status down again after %d ms for interface %s\n",
(bond->params.updelay - slave->delay) *
bond->params.miimon,
@@ -2085,7 +2127,8 @@ static void bond_miimon_commit(struct bonding *bond)
continue;
case BOND_LINK_UP:
- bond_set_slave_link_state(slave, BOND_LINK_UP);
+ bond_set_slave_link_state(slave, BOND_LINK_UP,
+ BOND_SLAVE_NOTIFY_NOW);
slave->last_link_up = jiffies;
primary = rtnl_dereference(bond->primary_slave);
@@ -2125,7 +2168,8 @@ static void bond_miimon_commit(struct bonding *bond)
if (slave->link_failure_count < UINT_MAX)
slave->link_failure_count++;
- bond_set_slave_link_state(slave, BOND_LINK_DOWN);
+ bond_set_slave_link_state(slave, BOND_LINK_DOWN,
+ BOND_SLAVE_NOTIFY_NOW);
if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP ||
BOND_MODE(bond) == BOND_MODE_8023AD)
@@ -2708,7 +2752,8 @@ static void bond_ab_arp_commit(struct bonding *bond)
struct slave *current_arp_slave;
current_arp_slave = rtnl_dereference(bond->current_arp_slave);
- bond_set_slave_link_state(slave, BOND_LINK_UP);
+ bond_set_slave_link_state(slave, BOND_LINK_UP,
+ BOND_SLAVE_NOTIFY_NOW);
if (current_arp_slave) {
bond_set_slave_inactive_flags(
current_arp_slave,
@@ -2731,7 +2776,8 @@ static void bond_ab_arp_commit(struct bonding *bond)
if (slave->link_failure_count < UINT_MAX)
slave->link_failure_count++;
- bond_set_slave_link_state(slave, BOND_LINK_DOWN);
+ bond_set_slave_link_state(slave, BOND_LINK_DOWN,
+ BOND_SLAVE_NOTIFY_NOW);
bond_set_slave_inactive_flags(slave,
BOND_SLAVE_NOTIFY_NOW);
@@ -2810,7 +2856,8 @@ static bool bond_ab_arp_probe(struct bonding *bond)
* up when it is actually down
*/
if (!bond_slave_is_up(slave) && slave->link == BOND_LINK_UP) {
- bond_set_slave_link_state(slave, BOND_LINK_DOWN);
+ bond_set_slave_link_state(slave, BOND_LINK_DOWN,
+ BOND_SLAVE_NOTIFY_LATER);
if (slave->link_failure_count < UINT_MAX)
slave->link_failure_count++;
@@ -2830,7 +2877,8 @@ static bool bond_ab_arp_probe(struct bonding *bond)
if (!new_slave)
goto check_state;
- bond_set_slave_link_state(new_slave, BOND_LINK_BACK);
+ bond_set_slave_link_state(new_slave, BOND_LINK_BACK,
+ BOND_SLAVE_NOTIFY_LATER);
bond_set_slave_active_flags(new_slave, BOND_SLAVE_NOTIFY_LATER);
bond_arp_send_all(bond, new_slave);
new_slave->last_link_up = jiffies;
@@ -2838,7 +2886,7 @@ static bool bond_ab_arp_probe(struct bonding *bond)
check_state:
bond_for_each_slave_rcu(bond, slave, iter) {
- if (slave->should_notify) {
+ if (slave->should_notify || slave->should_notify_link) {
should_notify_rtnl = BOND_SLAVE_NOTIFY_NOW;
break;
}
@@ -2893,8 +2941,10 @@ re_arm:
if (should_notify_peers)
call_netdevice_notifiers(NETDEV_NOTIFY_PEERS,
bond->dev);
- if (should_notify_rtnl)
+ if (should_notify_rtnl) {
bond_slave_state_notify(bond);
+ bond_slave_link_notify(bond);
+ }
rtnl_unlock();
}
@@ -4135,7 +4185,6 @@ void bond_setup(struct net_device *bond_dev)
NETIF_F_HW_VLAN_CTAG_RX |
NETIF_F_HW_VLAN_CTAG_FILTER;
- bond_dev->hw_features &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_HW_CSUM);
bond_dev->hw_features |= NETIF_F_GSO_ENCAP_ALL;
bond_dev->features |= bond_dev->hw_features;
}
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index f4ae72086215..e23c3ed737de 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -42,7 +42,6 @@
#include <net/bonding.h>
-#define to_dev(obj) container_of(obj, struct device, kobj)
#define to_bond(cd) ((struct bonding *)(netdev_priv(to_net_dev(cd))))
/* "show" function for the bond_masters attribute.
@@ -481,7 +480,7 @@ static ssize_t bonding_show_mii_status(struct device *d,
char *buf)
{
struct bonding *bond = to_bond(d);
- bool active = !!rcu_access_pointer(bond->curr_active_slave);
+ bool active = netif_carrier_ok(bond->dev);
return sprintf(buf, "%s\n", active ? "up" : "down");
}
diff --git a/drivers/net/can/bfin_can.c b/drivers/net/can/bfin_can.c
index 57dadd52b428..1deb8ff90a89 100644
--- a/drivers/net/can/bfin_can.c
+++ b/drivers/net/can/bfin_can.c
@@ -501,8 +501,6 @@ static int bfin_can_err(struct net_device *dev, u16 isrc, u16 status)
cf->data[2] |= CAN_ERR_PROT_FORM;
else if (status & SER)
cf->data[2] |= CAN_ERR_PROT_STUFF;
- else
- cf->data[2] |= CAN_ERR_PROT_UNSPEC;
}
priv->can.state = state;
diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c
index 5d214d135332..f91b094288da 100644
--- a/drivers/net/can/c_can/c_can.c
+++ b/drivers/net/can/c_can/c_can.c
@@ -962,7 +962,6 @@ static int c_can_handle_bus_err(struct net_device *dev,
* type of the last error to occur on the CAN bus
*/
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
- cf->data[2] |= CAN_ERR_PROT_UNSPEC;
switch (lec_type) {
case LEC_STUFF_ERROR:
@@ -975,8 +974,7 @@ static int c_can_handle_bus_err(struct net_device *dev,
break;
case LEC_ACK_ERROR:
netdev_dbg(dev, "ack error\n");
- cf->data[3] |= (CAN_ERR_PROT_LOC_ACK |
- CAN_ERR_PROT_LOC_ACK_DEL);
+ cf->data[3] = CAN_ERR_PROT_LOC_ACK;
break;
case LEC_BIT1_ERROR:
netdev_dbg(dev, "bit1 error\n");
@@ -988,8 +986,7 @@ static int c_can_handle_bus_err(struct net_device *dev,
break;
case LEC_CRC_ERROR:
netdev_dbg(dev, "CRC error\n");
- cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
- CAN_ERR_PROT_LOC_CRC_DEL);
+ cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
break;
default:
break;
diff --git a/drivers/net/can/cc770/cc770.c b/drivers/net/can/cc770/cc770.c
index 70a8cbb29e75..1e37313054f3 100644
--- a/drivers/net/can/cc770/cc770.c
+++ b/drivers/net/can/cc770/cc770.c
@@ -578,7 +578,7 @@ static int cc770_err(struct net_device *dev, u8 status)
cf->data[2] |= CAN_ERR_PROT_BIT0;
break;
case STAT_LEC_CRC:
- cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
+ cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
break;
}
}
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 868fe945e35a..41c0fc9f3b14 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -535,13 +535,13 @@ static void do_bus_err(struct net_device *dev,
if (reg_esr & FLEXCAN_ESR_ACK_ERR) {
netdev_dbg(dev, "ACK_ERR irq\n");
cf->can_id |= CAN_ERR_ACK;
- cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
+ cf->data[3] = CAN_ERR_PROT_LOC_ACK;
tx_errors = 1;
}
if (reg_esr & FLEXCAN_ESR_CRC_ERR) {
netdev_dbg(dev, "CRC_ERR irq\n");
cf->data[2] |= CAN_ERR_PROT_BIT;
- cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
+ cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
rx_errors = 1;
}
if (reg_esr & FLEXCAN_ESR_FRM_ERR) {
diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
index c1e85368a198..5d04f5464faf 100644
--- a/drivers/net/can/janz-ican3.c
+++ b/drivers/net/can/janz-ican3.c
@@ -1096,7 +1096,6 @@ static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg)
cf->data[2] |= CAN_ERR_PROT_STUFF;
break;
default:
- cf->data[2] |= CAN_ERR_PROT_UNSPEC;
cf->data[3] = ecc & ECC_SEG;
break;
}
diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
index ef655177bb5e..39cf911f7a1e 100644
--- a/drivers/net/can/m_can/m_can.c
+++ b/drivers/net/can/m_can/m_can.c
@@ -487,7 +487,6 @@ static int m_can_handle_lec_err(struct net_device *dev,
* type of the last error to occur on the CAN bus
*/
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
- cf->data[2] |= CAN_ERR_PROT_UNSPEC;
switch (lec_type) {
case LEC_STUFF_ERROR:
@@ -500,8 +499,7 @@ static int m_can_handle_lec_err(struct net_device *dev,
break;
case LEC_ACK_ERROR:
netdev_dbg(dev, "ack error\n");
- cf->data[3] |= (CAN_ERR_PROT_LOC_ACK |
- CAN_ERR_PROT_LOC_ACK_DEL);
+ cf->data[3] = CAN_ERR_PROT_LOC_ACK;
break;
case LEC_BIT1_ERROR:
netdev_dbg(dev, "bit1 error\n");
@@ -513,8 +511,7 @@ static int m_can_handle_lec_err(struct net_device *dev,
break;
case LEC_CRC_ERROR:
netdev_dbg(dev, "CRC error\n");
- cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
- CAN_ERR_PROT_LOC_CRC_DEL);
+ cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
break;
default:
break;
diff --git a/drivers/net/can/pch_can.c b/drivers/net/can/pch_can.c
index e187ca783da0..c1317889d3d8 100644
--- a/drivers/net/can/pch_can.c
+++ b/drivers/net/can/pch_can.c
@@ -559,8 +559,7 @@ static void pch_can_error(struct net_device *ndev, u32 status)
stats->rx_errors++;
break;
case PCH_CRC_ERR:
- cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ |
- CAN_ERR_PROT_LOC_CRC_DEL;
+ cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
priv->can.can_stats.bus_error++;
stats->rx_errors++;
break;
diff --git a/drivers/net/can/rcar_can.c b/drivers/net/can/rcar_can.c
index 7bd54191f962..bc46be39549d 100644
--- a/drivers/net/can/rcar_can.c
+++ b/drivers/net/can/rcar_can.c
@@ -241,17 +241,16 @@ static void rcar_can_error(struct net_device *ndev)
u8 ecsr;
netdev_dbg(priv->ndev, "Bus error interrupt:\n");
- if (skb) {
+ if (skb)
cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
- cf->data[2] = CAN_ERR_PROT_UNSPEC;
- }
+
ecsr = readb(&priv->regs->ecsr);
if (ecsr & RCAR_CAN_ECSR_ADEF) {
netdev_dbg(priv->ndev, "ACK Delimiter Error\n");
tx_errors++;
writeb(~RCAR_CAN_ECSR_ADEF, &priv->regs->ecsr);
if (skb)
- cf->data[3] |= CAN_ERR_PROT_LOC_ACK_DEL;
+ cf->data[3] = CAN_ERR_PROT_LOC_ACK_DEL;
}
if (ecsr & RCAR_CAN_ECSR_BE0F) {
netdev_dbg(priv->ndev, "Bit Error (dominant)\n");
@@ -272,7 +271,7 @@ static void rcar_can_error(struct net_device *ndev)
rx_errors++;
writeb(~RCAR_CAN_ECSR_CEF, &priv->regs->ecsr);
if (skb)
- cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
+ cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
}
if (ecsr & RCAR_CAN_ECSR_AEF) {
netdev_dbg(priv->ndev, "ACK Error\n");
@@ -280,7 +279,7 @@ static void rcar_can_error(struct net_device *ndev)
writeb(~RCAR_CAN_ECSR_AEF, &priv->regs->ecsr);
if (skb) {
cf->can_id |= CAN_ERR_ACK;
- cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
+ cf->data[3] = CAN_ERR_PROT_LOC_ACK;
}
}
if (ecsr & RCAR_CAN_ECSR_FEF) {
diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c
index 7b92e911a616..8dda3b703d39 100644
--- a/drivers/net/can/sja1000/sja1000.c
+++ b/drivers/net/can/sja1000/sja1000.c
@@ -218,6 +218,9 @@ static void sja1000_start(struct net_device *dev)
priv->write_reg(priv, SJA1000_RXERR, 0x0);
priv->read_reg(priv, SJA1000_ECC);
+ /* clear interrupt flags */
+ priv->read_reg(priv, SJA1000_IR);
+
/* leave reset mode */
set_normal_mode(dev);
}
@@ -446,7 +449,6 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
cf->data[2] |= CAN_ERR_PROT_STUFF;
break;
default:
- cf->data[2] |= CAN_ERR_PROT_UNSPEC;
cf->data[3] = ecc & ECC_SEG;
break;
}
diff --git a/drivers/net/can/sun4i_can.c b/drivers/net/can/sun4i_can.c
index d9a42c646783..68ef0a4cd821 100644
--- a/drivers/net/can/sun4i_can.c
+++ b/drivers/net/can/sun4i_can.c
@@ -575,7 +575,6 @@ static int sun4i_can_err(struct net_device *dev, u8 isrc, u8 status)
cf->data[2] |= CAN_ERR_PROT_STUFF;
break;
default:
- cf->data[2] |= CAN_ERR_PROT_UNSPEC;
cf->data[3] = (ecc & SUN4I_STA_ERR_SEG_CODE)
>> 16;
break;
diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c
index cf345cbfe819..680d1ff07a55 100644
--- a/drivers/net/can/ti_hecc.c
+++ b/drivers/net/can/ti_hecc.c
@@ -722,7 +722,6 @@ static int ti_hecc_error(struct net_device *ndev, int int_status,
if (err_status & HECC_BUS_ERROR) {
++priv->can.can_stats.bus_error;
cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
- cf->data[2] |= CAN_ERR_PROT_UNSPEC;
if (err_status & HECC_CANES_FE) {
hecc_set_bit(priv, HECC_CANES, HECC_CANES_FE);
cf->data[2] |= CAN_ERR_PROT_FORM;
@@ -737,13 +736,11 @@ static int ti_hecc_error(struct net_device *ndev, int int_status,
}
if (err_status & HECC_CANES_CRCE) {
hecc_set_bit(priv, HECC_CANES, HECC_CANES_CRCE);
- cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ |
- CAN_ERR_PROT_LOC_CRC_DEL;
+ cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
}
if (err_status & HECC_CANES_ACKE) {
hecc_set_bit(priv, HECC_CANES, HECC_CANES_ACKE);
- cf->data[3] |= CAN_ERR_PROT_LOC_ACK |
- CAN_ERR_PROT_LOC_ACK_DEL;
+ cf->data[3] = CAN_ERR_PROT_LOC_ACK;
}
}
diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c
index 2d390384ef3b..fc5b75675cd8 100644
--- a/drivers/net/can/usb/ems_usb.c
+++ b/drivers/net/can/usb/ems_usb.c
@@ -377,7 +377,6 @@ static void ems_usb_rx_err(struct ems_usb *dev, struct ems_cpc_msg *msg)
cf->data[2] |= CAN_ERR_PROT_STUFF;
break;
default:
- cf->data[2] |= CAN_ERR_PROT_UNSPEC;
cf->data[3] = ecc & SJA1000_ECC_SEG;
break;
}
diff --git a/drivers/net/can/usb/esd_usb2.c b/drivers/net/can/usb/esd_usb2.c
index 0e5a4493ba4f..113e64fcd73b 100644
--- a/drivers/net/can/usb/esd_usb2.c
+++ b/drivers/net/can/usb/esd_usb2.c
@@ -282,7 +282,6 @@ static void esd_usb2_rx_event(struct esd_usb2_net_priv *priv,
cf->data[2] |= CAN_ERR_PROT_STUFF;
break;
default:
- cf->data[2] |= CAN_ERR_PROT_UNSPEC;
cf->data[3] = ecc & SJA1000_ECC_SEG;
break;
}
diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
index 8b17a9065b0b..022bfa13ebfa 100644
--- a/drivers/net/can/usb/kvaser_usb.c
+++ b/drivers/net/can/usb/kvaser_usb.c
@@ -944,10 +944,9 @@ static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
if (es->leaf.error_factor & M16C_EF_ACKE)
- cf->data[3] |= (CAN_ERR_PROT_LOC_ACK);
+ cf->data[3] = CAN_ERR_PROT_LOC_ACK;
if (es->leaf.error_factor & M16C_EF_CRCE)
- cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
- CAN_ERR_PROT_LOC_CRC_DEL);
+ cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
if (es->leaf.error_factor & M16C_EF_FORME)
cf->data[2] |= CAN_ERR_PROT_FORM;
if (es->leaf.error_factor & M16C_EF_STFE)
diff --git a/drivers/net/can/usb/usb_8dev.c b/drivers/net/can/usb/usb_8dev.c
index de95b1ccba3e..a731720f1d13 100644
--- a/drivers/net/can/usb/usb_8dev.c
+++ b/drivers/net/can/usb/usb_8dev.c
@@ -401,9 +401,7 @@ static void usb_8dev_rx_err_msg(struct usb_8dev_priv *priv,
tx_errors = 1;
break;
case USB_8DEV_STATUSMSG_CRC:
- cf->data[2] |= CAN_ERR_PROT_UNSPEC;
- cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ |
- CAN_ERR_PROT_LOC_CRC_DEL;
+ cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
rx_errors = 1;
break;
case USB_8DEV_STATUSMSG_BIT0:
diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c
index fc55e8e0351d..c71a03593595 100644
--- a/drivers/net/can/xilinx_can.c
+++ b/drivers/net/can/xilinx_can.c
@@ -32,6 +32,7 @@
#include <linux/can/dev.h>
#include <linux/can/error.h>
#include <linux/can/led.h>
+#include <linux/pm_runtime.h>
#define DRIVER_NAME "xilinx_can"
@@ -138,7 +139,7 @@ struct xcan_priv {
u32 (*read_reg)(const struct xcan_priv *priv, enum xcan_reg reg);
void (*write_reg)(const struct xcan_priv *priv, enum xcan_reg reg,
u32 val);
- struct net_device *dev;
+ struct device *dev;
void __iomem *reg_base;
unsigned long irq_flags;
struct clk *bus_clk;
@@ -608,17 +609,15 @@ static void xcan_err_interrupt(struct net_device *ndev, u32 isr)
/* Check for error interrupt */
if (isr & XCAN_IXR_ERROR_MASK) {
- if (skb) {
+ if (skb)
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
- cf->data[2] |= CAN_ERR_PROT_UNSPEC;
- }
/* Check for Ack error interrupt */
if (err_status & XCAN_ESR_ACKER_MASK) {
stats->tx_errors++;
if (skb) {
cf->can_id |= CAN_ERR_ACK;
- cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
+ cf->data[3] = CAN_ERR_PROT_LOC_ACK;
}
}
@@ -654,8 +653,7 @@ static void xcan_err_interrupt(struct net_device *ndev, u32 isr)
stats->rx_errors++;
if (skb) {
cf->can_id |= CAN_ERR_PROT;
- cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ |
- CAN_ERR_PROT_LOC_CRC_DEL;
+ cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
}
}
priv->can.can_stats.bus_error++;
@@ -843,6 +841,13 @@ static int xcan_open(struct net_device *ndev)
struct xcan_priv *priv = netdev_priv(ndev);
int ret;
+ ret = pm_runtime_get_sync(priv->dev);
+ if (ret < 0) {
+ netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
ret = request_irq(ndev->irq, xcan_interrupt, priv->irq_flags,
ndev->name, ndev);
if (ret < 0) {
@@ -850,29 +855,17 @@ static int xcan_open(struct net_device *ndev)
goto err;
}
- ret = clk_prepare_enable(priv->can_clk);
- if (ret) {
- netdev_err(ndev, "unable to enable device clock\n");
- goto err_irq;
- }
-
- ret = clk_prepare_enable(priv->bus_clk);
- if (ret) {
- netdev_err(ndev, "unable to enable bus clock\n");
- goto err_can_clk;
- }
-
/* Set chip into reset mode */
ret = set_reset_mode(ndev);
if (ret < 0) {
netdev_err(ndev, "mode resetting failed!\n");
- goto err_bus_clk;
+ goto err_irq;
}
/* Common open */
ret = open_candev(ndev);
if (ret)
- goto err_bus_clk;
+ goto err_irq;
ret = xcan_chip_start(ndev);
if (ret < 0) {
@@ -888,13 +881,11 @@ static int xcan_open(struct net_device *ndev)
err_candev:
close_candev(ndev);
-err_bus_clk:
- clk_disable_unprepare(priv->bus_clk);
-err_can_clk:
- clk_disable_unprepare(priv->can_clk);
err_irq:
free_irq(ndev->irq, ndev);
err:
+ pm_runtime_put(priv->dev);
+
return ret;
}
@@ -911,12 +902,11 @@ static int xcan_close(struct net_device *ndev)
netif_stop_queue(ndev);
napi_disable(&priv->napi);
xcan_chip_stop(ndev);
- clk_disable_unprepare(priv->bus_clk);
- clk_disable_unprepare(priv->can_clk);
free_irq(ndev->irq, ndev);
close_candev(ndev);
can_led_event(ndev, CAN_LED_EVENT_STOP);
+ pm_runtime_put(priv->dev);
return 0;
}
@@ -935,27 +925,20 @@ static int xcan_get_berr_counter(const struct net_device *ndev,
struct xcan_priv *priv = netdev_priv(ndev);
int ret;
- ret = clk_prepare_enable(priv->can_clk);
- if (ret)
- goto err;
-
- ret = clk_prepare_enable(priv->bus_clk);
- if (ret)
- goto err_clk;
+ ret = pm_runtime_get_sync(priv->dev);
+ if (ret < 0) {
+ netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n",
+ __func__, ret);
+ return ret;
+ }
bec->txerr = priv->read_reg(priv, XCAN_ECR_OFFSET) & XCAN_ECR_TEC_MASK;
bec->rxerr = ((priv->read_reg(priv, XCAN_ECR_OFFSET) &
XCAN_ECR_REC_MASK) >> XCAN_ESR_REC_SHIFT);
- clk_disable_unprepare(priv->bus_clk);
- clk_disable_unprepare(priv->can_clk);
+ pm_runtime_put(priv->dev);
return 0;
-
-err_clk:
- clk_disable_unprepare(priv->can_clk);
-err:
- return ret;
}
@@ -968,15 +951,45 @@ static const struct net_device_ops xcan_netdev_ops = {
/**
* xcan_suspend - Suspend method for the driver
- * @dev: Address of the platform_device structure
+ * @dev: Address of the device structure
*
* Put the driver into low power mode.
- * Return: 0 always
+ * Return: 0 on success and failure value on error
*/
static int __maybe_unused xcan_suspend(struct device *dev)
{
- struct platform_device *pdev = dev_get_drvdata(dev);
- struct net_device *ndev = platform_get_drvdata(pdev);
+ if (!device_may_wakeup(dev))
+ return pm_runtime_force_suspend(dev);
+
+ return 0;
+}
+
+/**
+ * xcan_resume - Resume from suspend
+ * @dev: Address of the device structure
+ *
+ * Resume operation after suspend.
+ * Return: 0 on success and failure value on error
+ */
+static int __maybe_unused xcan_resume(struct device *dev)
+{
+ if (!device_may_wakeup(dev))
+ return pm_runtime_force_resume(dev);
+
+ return 0;
+
+}
+
+/**
+ * xcan_runtime_suspend - Runtime suspend method for the driver
+ * @dev: Address of the device structure
+ *
+ * Put the driver into low power mode.
+ * Return: 0 always
+ */
+static int __maybe_unused xcan_runtime_suspend(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
struct xcan_priv *priv = netdev_priv(ndev);
if (netif_running(ndev)) {
@@ -987,43 +1000,55 @@ static int __maybe_unused xcan_suspend(struct device *dev)
priv->write_reg(priv, XCAN_MSR_OFFSET, XCAN_MSR_SLEEP_MASK);
priv->can.state = CAN_STATE_SLEEPING;
- clk_disable(priv->bus_clk);
- clk_disable(priv->can_clk);
+ clk_disable_unprepare(priv->bus_clk);
+ clk_disable_unprepare(priv->can_clk);
return 0;
}
/**
- * xcan_resume - Resume from suspend
- * @dev: Address of the platformdevice structure
+ * xcan_runtime_resume - Runtime resume from suspend
+ * @dev: Address of the device structure
*
* Resume operation after suspend.
* Return: 0 on success and failure value on error
*/
-static int __maybe_unused xcan_resume(struct device *dev)
+static int __maybe_unused xcan_runtime_resume(struct device *dev)
{
- struct platform_device *pdev = dev_get_drvdata(dev);
- struct net_device *ndev = platform_get_drvdata(pdev);
+ struct net_device *ndev = dev_get_drvdata(dev);
struct xcan_priv *priv = netdev_priv(ndev);
int ret;
+ u32 isr, status;
- ret = clk_enable(priv->bus_clk);
+ ret = clk_prepare_enable(priv->bus_clk);
if (ret) {
dev_err(dev, "Cannot enable clock.\n");
return ret;
}
- ret = clk_enable(priv->can_clk);
+ ret = clk_prepare_enable(priv->can_clk);
if (ret) {
dev_err(dev, "Cannot enable clock.\n");
clk_disable_unprepare(priv->bus_clk);
return ret;
}
- priv->write_reg(priv, XCAN_MSR_OFFSET, 0);
- priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK);
- priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
+ isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
+ status = priv->read_reg(priv, XCAN_SR_OFFSET);
if (netif_running(ndev)) {
+ if (isr & XCAN_IXR_BSOFF_MASK) {
+ priv->can.state = CAN_STATE_BUS_OFF;
+ priv->write_reg(priv, XCAN_SRR_OFFSET,
+ XCAN_SRR_RESET_MASK);
+ } else if ((status & XCAN_SR_ESTAT_MASK) ==
+ XCAN_SR_ESTAT_MASK) {
+ priv->can.state = CAN_STATE_ERROR_PASSIVE;
+ } else if (status & XCAN_SR_ERRWRN_MASK) {
+ priv->can.state = CAN_STATE_ERROR_WARNING;
+ } else {
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ }
netif_device_attach(ndev);
netif_start_queue(ndev);
}
@@ -1031,7 +1056,10 @@ static int __maybe_unused xcan_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(xcan_dev_pm_ops, xcan_suspend, xcan_resume);
+static const struct dev_pm_ops xcan_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(xcan_suspend, xcan_resume)
+ SET_RUNTIME_PM_OPS(xcan_runtime_suspend, xcan_runtime_resume, NULL)
+};
/**
* xcan_probe - Platform registration call
@@ -1072,7 +1100,7 @@ static int xcan_probe(struct platform_device *pdev)
return -ENOMEM;
priv = netdev_priv(ndev);
- priv->dev = ndev;
+ priv->dev = &pdev->dev;
priv->can.bittiming_const = &xcan_bittiming_const;
priv->can.do_set_mode = xcan_do_set_mode;
priv->can.do_get_berr_counter = xcan_get_berr_counter;
@@ -1114,21 +1142,17 @@ static int xcan_probe(struct platform_device *pdev)
}
}
- ret = clk_prepare_enable(priv->can_clk);
- if (ret) {
- dev_err(&pdev->dev, "unable to enable device clock\n");
- goto err_free;
- }
-
- ret = clk_prepare_enable(priv->bus_clk);
- if (ret) {
- dev_err(&pdev->dev, "unable to enable bus clock\n");
- goto err_unprepare_disable_dev;
- }
-
priv->write_reg = xcan_write_reg_le;
priv->read_reg = xcan_read_reg_le;
+ pm_runtime_enable(&pdev->dev);
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0) {
+ netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n",
+ __func__, ret);
+ goto err_pmdisable;
+ }
+
if (priv->read_reg(priv, XCAN_SR_OFFSET) != XCAN_SR_CONFIG_MASK) {
priv->write_reg = xcan_write_reg_be;
priv->read_reg = xcan_read_reg_be;
@@ -1141,22 +1165,23 @@ static int xcan_probe(struct platform_device *pdev)
ret = register_candev(ndev);
if (ret) {
dev_err(&pdev->dev, "fail to register failed (err=%d)\n", ret);
- goto err_unprepare_disable_busclk;
+ goto err_disableclks;
}
devm_can_led_init(ndev);
- clk_disable_unprepare(priv->bus_clk);
- clk_disable_unprepare(priv->can_clk);
+
+ pm_runtime_put(&pdev->dev);
+
netdev_dbg(ndev, "reg_base=0x%p irq=%d clock=%d, tx fifo depth:%d\n",
priv->reg_base, ndev->irq, priv->can.clock.freq,
priv->tx_max);
return 0;
-err_unprepare_disable_busclk:
- clk_disable_unprepare(priv->bus_clk);
-err_unprepare_disable_dev:
- clk_disable_unprepare(priv->can_clk);
+err_disableclks:
+ pm_runtime_put(priv->dev);
+err_pmdisable:
+ pm_runtime_disable(&pdev->dev);
err_free:
free_candev(ndev);
err:
@@ -1175,10 +1200,8 @@ static int xcan_remove(struct platform_device *pdev)
struct net_device *ndev = platform_get_drvdata(pdev);
struct xcan_priv *priv = netdev_priv(ndev);
- if (set_reset_mode(ndev) < 0)
- netdev_err(ndev, "mode resetting failed!\n");
-
unregister_candev(ndev);
+ pm_runtime_disable(&pdev->dev);
netif_napi_del(&priv->napi);
free_candev(ndev);
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index b06dba05594a..9fe33fc3c2b9 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -19,6 +19,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/netdevice.h>
+#include <linux/gpio/consumer.h>
#include <linux/phy.h>
#include <net/dsa.h>
#include <net/switchdev.h>
@@ -616,98 +617,112 @@ static void _mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val)
}
static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
- { "in_good_octets", 8, 0x00, },
- { "in_bad_octets", 4, 0x02, },
- { "in_unicast", 4, 0x04, },
- { "in_broadcasts", 4, 0x06, },
- { "in_multicasts", 4, 0x07, },
- { "in_pause", 4, 0x16, },
- { "in_undersize", 4, 0x18, },
- { "in_fragments", 4, 0x19, },
- { "in_oversize", 4, 0x1a, },
- { "in_jabber", 4, 0x1b, },
- { "in_rx_error", 4, 0x1c, },
- { "in_fcs_error", 4, 0x1d, },
- { "out_octets", 8, 0x0e, },
- { "out_unicast", 4, 0x10, },
- { "out_broadcasts", 4, 0x13, },
- { "out_multicasts", 4, 0x12, },
- { "out_pause", 4, 0x15, },
- { "excessive", 4, 0x11, },
- { "collisions", 4, 0x1e, },
- { "deferred", 4, 0x05, },
- { "single", 4, 0x14, },
- { "multiple", 4, 0x17, },
- { "out_fcs_error", 4, 0x03, },
- { "late", 4, 0x1f, },
- { "hist_64bytes", 4, 0x08, },
- { "hist_65_127bytes", 4, 0x09, },
- { "hist_128_255bytes", 4, 0x0a, },
- { "hist_256_511bytes", 4, 0x0b, },
- { "hist_512_1023bytes", 4, 0x0c, },
- { "hist_1024_max_bytes", 4, 0x0d, },
- /* Not all devices have the following counters */
- { "sw_in_discards", 4, 0x110, },
- { "sw_in_filtered", 2, 0x112, },
- { "sw_out_filtered", 2, 0x113, },
-
+ { "in_good_octets", 8, 0x00, BANK0, },
+ { "in_bad_octets", 4, 0x02, BANK0, },
+ { "in_unicast", 4, 0x04, BANK0, },
+ { "in_broadcasts", 4, 0x06, BANK0, },
+ { "in_multicasts", 4, 0x07, BANK0, },
+ { "in_pause", 4, 0x16, BANK0, },
+ { "in_undersize", 4, 0x18, BANK0, },
+ { "in_fragments", 4, 0x19, BANK0, },
+ { "in_oversize", 4, 0x1a, BANK0, },
+ { "in_jabber", 4, 0x1b, BANK0, },
+ { "in_rx_error", 4, 0x1c, BANK0, },
+ { "in_fcs_error", 4, 0x1d, BANK0, },
+ { "out_octets", 8, 0x0e, BANK0, },
+ { "out_unicast", 4, 0x10, BANK0, },
+ { "out_broadcasts", 4, 0x13, BANK0, },
+ { "out_multicasts", 4, 0x12, BANK0, },
+ { "out_pause", 4, 0x15, BANK0, },
+ { "excessive", 4, 0x11, BANK0, },
+ { "collisions", 4, 0x1e, BANK0, },
+ { "deferred", 4, 0x05, BANK0, },
+ { "single", 4, 0x14, BANK0, },
+ { "multiple", 4, 0x17, BANK0, },
+ { "out_fcs_error", 4, 0x03, BANK0, },
+ { "late", 4, 0x1f, BANK0, },
+ { "hist_64bytes", 4, 0x08, BANK0, },
+ { "hist_65_127bytes", 4, 0x09, BANK0, },
+ { "hist_128_255bytes", 4, 0x0a, BANK0, },
+ { "hist_256_511bytes", 4, 0x0b, BANK0, },
+ { "hist_512_1023bytes", 4, 0x0c, BANK0, },
+ { "hist_1024_max_bytes", 4, 0x0d, BANK0, },
+ { "sw_in_discards", 4, 0x10, PORT, },
+ { "sw_in_filtered", 2, 0x12, PORT, },
+ { "sw_out_filtered", 2, 0x13, PORT, },
+ { "in_discards", 4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "in_filtered", 4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "in_accepted", 4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "in_bad_accepted", 4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "tcam_counter_0", 4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "tcam_counter_1", 4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "tcam_counter_2", 4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "tcam_counter_3", 4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "in_da_unknown", 4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "in_management", 4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_queue_0", 4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_queue_1", 4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_queue_2", 4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_queue_3", 4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_queue_4", 4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_queue_5", 4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_queue_6", 4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_queue_7", 4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_cut_through", 4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_octets_a", 4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_octets_b", 4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, },
+ { "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, },
};
-static bool have_sw_in_discards(struct dsa_switch *ds)
+static bool mv88e6xxx_has_stat(struct dsa_switch *ds,
+ struct mv88e6xxx_hw_stat *stat)
{
- struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
-
- switch (ps->id) {
- case PORT_SWITCH_ID_6095: case PORT_SWITCH_ID_6161:
- case PORT_SWITCH_ID_6165: case PORT_SWITCH_ID_6171:
- case PORT_SWITCH_ID_6172: case PORT_SWITCH_ID_6176:
- case PORT_SWITCH_ID_6182: case PORT_SWITCH_ID_6185:
- case PORT_SWITCH_ID_6352:
+ switch (stat->type) {
+ case BANK0:
return true;
- default:
- return false;
- }
-}
-
-static void _mv88e6xxx_get_strings(struct dsa_switch *ds,
- int nr_stats,
- struct mv88e6xxx_hw_stat *stats,
- int port, uint8_t *data)
-{
- int i;
-
- for (i = 0; i < nr_stats; i++) {
- memcpy(data + i * ETH_GSTRING_LEN,
- stats[i].string, ETH_GSTRING_LEN);
+ case BANK1:
+ return mv88e6xxx_6320_family(ds);
+ case PORT:
+ return mv88e6xxx_6095_family(ds) ||
+ mv88e6xxx_6185_family(ds) ||
+ mv88e6xxx_6097_family(ds) ||
+ mv88e6xxx_6165_family(ds) ||
+ mv88e6xxx_6351_family(ds) ||
+ mv88e6xxx_6352_family(ds);
}
+ return false;
}
static uint64_t _mv88e6xxx_get_ethtool_stat(struct dsa_switch *ds,
- int stat,
- struct mv88e6xxx_hw_stat *stats,
+ struct mv88e6xxx_hw_stat *s,
int port)
{
- struct mv88e6xxx_hw_stat *s = stats + stat;
u32 low;
u32 high = 0;
int ret;
u64 value;
- if (s->reg >= 0x100) {
- ret = _mv88e6xxx_reg_read(ds, REG_PORT(port),
- s->reg - 0x100);
+ switch (s->type) {
+ case PORT:
+ ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), s->reg);
if (ret < 0)
return UINT64_MAX;
low = ret;
if (s->sizeof_stat == 4) {
ret = _mv88e6xxx_reg_read(ds, REG_PORT(port),
- s->reg - 0x100 + 1);
+ s->reg + 1);
if (ret < 0)
return UINT64_MAX;
high = ret;
}
- } else {
+ break;
+ case BANK0:
+ case BANK1:
_mv88e6xxx_stats_read(ds, s->reg, &low);
if (s->sizeof_stat == 8)
_mv88e6xxx_stats_read(ds, s->reg + 1, &high);
@@ -716,61 +731,59 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct dsa_switch *ds,
return value;
}
-static void _mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
- int nr_stats,
- struct mv88e6xxx_hw_stat *stats,
- int port, uint64_t *data)
+void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
{
- struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
- int ret;
- int i;
-
- mutex_lock(&ps->smi_mutex);
+ struct mv88e6xxx_hw_stat *stat;
+ int i, j;
- ret = _mv88e6xxx_stats_snapshot(ds, port);
- if (ret < 0) {
- mutex_unlock(&ps->smi_mutex);
- return;
+ for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
+ stat = &mv88e6xxx_hw_stats[i];
+ if (mv88e6xxx_has_stat(ds, stat)) {
+ memcpy(data + j * ETH_GSTRING_LEN, stat->string,
+ ETH_GSTRING_LEN);
+ j++;
+ }
}
-
- /* Read each of the counters. */
- for (i = 0; i < nr_stats; i++)
- data[i] = _mv88e6xxx_get_ethtool_stat(ds, i, stats, port);
-
- mutex_unlock(&ps->smi_mutex);
-}
-
-/* All the statistics in the table */
-void
-mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
-{
- if (have_sw_in_discards(ds))
- _mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6xxx_hw_stats),
- mv88e6xxx_hw_stats, port, data);
- else
- _mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3,
- mv88e6xxx_hw_stats, port, data);
}
int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
{
- if (have_sw_in_discards(ds))
- return ARRAY_SIZE(mv88e6xxx_hw_stats);
- return ARRAY_SIZE(mv88e6xxx_hw_stats) - 3;
+ struct mv88e6xxx_hw_stat *stat;
+ int i, j;
+
+ for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
+ stat = &mv88e6xxx_hw_stats[i];
+ if (mv88e6xxx_has_stat(ds, stat))
+ j++;
+ }
+ return j;
}
void
mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
int port, uint64_t *data)
{
- if (have_sw_in_discards(ds))
- _mv88e6xxx_get_ethtool_stats(
- ds, ARRAY_SIZE(mv88e6xxx_hw_stats),
- mv88e6xxx_hw_stats, port, data);
- else
- _mv88e6xxx_get_ethtool_stats(
- ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3,
- mv88e6xxx_hw_stats, port, data);
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+ struct mv88e6xxx_hw_stat *stat;
+ int ret;
+ int i, j;
+
+ mutex_lock(&ps->smi_mutex);
+
+ ret = _mv88e6xxx_stats_snapshot(ds, port);
+ if (ret < 0) {
+ mutex_unlock(&ps->smi_mutex);
+ return;
+ }
+ for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
+ stat = &mv88e6xxx_hw_stats[i];
+ if (mv88e6xxx_has_stat(ds, stat)) {
+ data[j] = _mv88e6xxx_get_ethtool_stat(ds, stat, port);
+ j++;
+ }
+ }
+
+ mutex_unlock(&ps->smi_mutex);
}
int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
@@ -2323,6 +2336,7 @@ int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
+ struct gpio_desc *gpiod = ds->pd->reset;
unsigned long timeout;
int ret;
int i;
@@ -2336,6 +2350,14 @@ int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active)
/* Wait for transmit queues to drain. */
usleep_range(2000, 4000);
+ /* If there is a gpio connected to the reset pin, toggle it */
+ if (gpiod) {
+ gpiod_set_value_cansleep(gpiod, 1);
+ usleep_range(10000, 20000);
+ gpiod_set_value_cansleep(gpiod, 0);
+ usleep_range(10000, 20000);
+ }
+
/* Reset the switch. Keep the PPU active if requested. The PPU
* needs to be active to support indirect phy register access
* through global registers 0x18 and 0x19.
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h
index 21c8daa03f78..ca08f913d302 100644
--- a/drivers/net/dsa/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx.h
@@ -288,6 +288,7 @@
#define GLOBAL_STATS_OP_HIST_RX ((1 << 10) | GLOBAL_STATS_OP_BUSY)
#define GLOBAL_STATS_OP_HIST_TX ((2 << 10) | GLOBAL_STATS_OP_BUSY)
#define GLOBAL_STATS_OP_HIST_RX_TX ((3 << 10) | GLOBAL_STATS_OP_BUSY)
+#define GLOBAL_STATS_OP_BANK_1 BIT(9)
#define GLOBAL_STATS_COUNTER_32 0x1e
#define GLOBAL_STATS_COUNTER_01 0x1f
@@ -420,10 +421,17 @@ struct mv88e6xxx_priv_state {
struct work_struct bridge_work;
};
+enum stat_type {
+ BANK0,
+ BANK1,
+ PORT,
+};
+
struct mv88e6xxx_hw_stat {
char string[ETH_GSTRING_LEN];
int sizeof_stat;
int reg;
+ enum stat_type type;
};
int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active);
diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c
index 4547a1b8b958..7677c745fb30 100644
--- a/drivers/net/ethernet/3com/3c509.c
+++ b/drivers/net/ethernet/3com/3c509.c
@@ -562,7 +562,7 @@ static void el3_common_remove (struct net_device *dev)
}
#ifdef CONFIG_EISA
-static int __init el3_eisa_probe (struct device *device)
+static int el3_eisa_probe(struct device *device)
{
short i;
int ioaddr, irq, if_port;
diff --git a/drivers/net/ethernet/3com/3c59x.c b/drivers/net/ethernet/3com/3c59x.c
index 2839af00f20c..1c5f3b273e6a 100644
--- a/drivers/net/ethernet/3com/3c59x.c
+++ b/drivers/net/ethernet/3com/3c59x.c
@@ -907,7 +907,7 @@ static struct eisa_device_id vortex_eisa_ids[] = {
};
MODULE_DEVICE_TABLE(eisa, vortex_eisa_ids);
-static int __init vortex_eisa_probe(struct device *device)
+static int vortex_eisa_probe(struct device *device)
{
void __iomem *ioaddr;
struct eisa_device *edev;
diff --git a/drivers/net/ethernet/8390/ax88796.c b/drivers/net/ethernet/8390/ax88796.c
index 0443654f0339..c89b9aeeceb6 100644
--- a/drivers/net/ethernet/8390/ax88796.c
+++ b/drivers/net/ethernet/8390/ax88796.c
@@ -372,7 +372,7 @@ static int ax_mii_probe(struct net_device *dev)
ax->phy_dev = phy_dev;
netdev_info(dev, "PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
- phy_dev->drv->name, dev_name(&phy_dev->dev), phy_dev->irq);
+ phy_dev->drv->name, phydev_name(phy_dev), phy_dev->irq);
return 0;
}
@@ -627,7 +627,7 @@ static int ax_mii_init(struct net_device *dev)
struct platform_device *pdev = to_platform_device(dev->dev.parent);
struct ei_device *ei_local = netdev_priv(dev);
struct ax_device *ax = to_ax_dev(dev);
- int err, i;
+ int err;
ax->bb_ctrl.ops = &bb_ops;
ax->addr_memr = ei_local->mem + AX_MEMR;
@@ -642,23 +642,12 @@ static int ax_mii_init(struct net_device *dev)
snprintf(ax->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
pdev->name, pdev->id);
- ax->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!ax->mii_bus->irq) {
- err = -ENOMEM;
- goto out_free_mdio_bitbang;
- }
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- ax->mii_bus->irq[i] = PHY_POLL;
-
err = mdiobus_register(ax->mii_bus);
if (err)
- goto out_free_irq;
+ goto out_free_mdio_bitbang;
return 0;
- out_free_irq:
- kfree(ax->mii_bus->irq);
out_free_mdio_bitbang:
free_mdio_bitbang(ax->mii_bus);
out:
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 955d06b9cdba..0b13af8e4070 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -29,6 +29,7 @@ source "drivers/net/ethernet/apm/Kconfig"
source "drivers/net/ethernet/apple/Kconfig"
source "drivers/net/ethernet/arc/Kconfig"
source "drivers/net/ethernet/atheros/Kconfig"
+source "drivers/net/ethernet/aurora/Kconfig"
source "drivers/net/ethernet/cadence/Kconfig"
source "drivers/net/ethernet/adi/Kconfig"
source "drivers/net/ethernet/broadcom/Kconfig"
@@ -121,6 +122,7 @@ config FEALNX
cards. <http://www.myson.com.tw/>
source "drivers/net/ethernet/natsemi/Kconfig"
+source "drivers/net/ethernet/netronome/Kconfig"
source "drivers/net/ethernet/8390/Kconfig"
config NET_NETX
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 4a2ee98738f0..38dc1a776a2b 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_NET_XGENE) += apm/
obj-$(CONFIG_NET_VENDOR_APPLE) += apple/
obj-$(CONFIG_NET_VENDOR_ARC) += arc/
obj-$(CONFIG_NET_VENDOR_ATHEROS) += atheros/
+obj-$(CONFIG_NET_VENDOR_AURORA) += aurora/
obj-$(CONFIG_NET_CADENCE) += cadence/
obj-$(CONFIG_NET_BFIN) += adi/
obj-$(CONFIG_NET_VENDOR_BROADCOM) += broadcom/
@@ -52,6 +53,7 @@ obj-$(CONFIG_NET_VENDOR_MOXART) += moxa/
obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/
obj-$(CONFIG_FEALNX) += fealnx.o
obj-$(CONFIG_NET_VENDOR_NATSEMI) += natsemi/
+obj-$(CONFIG_NET_VENDOR_NETRONOME) += netronome/
obj-$(CONFIG_NET_NETX) += netx-eth.o
obj-$(CONFIG_NET_VENDOR_NUVOTON) += nuvoton/
obj-$(CONFIG_NET_VENDOR_NVIDIA) += nvidia/
diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c
index 096531a73124..74139cb7f849 100644
--- a/drivers/net/ethernet/adi/bfin_mac.c
+++ b/drivers/net/ethernet/adi/bfin_mac.c
@@ -380,9 +380,8 @@ static void bfin_mac_adjust_link(struct net_device *dev)
static int mii_probe(struct net_device *dev, int phy_mode)
{
struct bfin_mac_local *lp = netdev_priv(dev);
- struct phy_device *phydev = NULL;
+ struct phy_device *phydev;
unsigned short sysctl;
- int i;
u32 sclk, mdc_div;
/* Enable PHY output early */
@@ -396,18 +395,7 @@ static int mii_probe(struct net_device *dev, int phy_mode)
sysctl = (sysctl & ~MDCDIV) | SET_MDCDIV(mdc_div);
bfin_write_EMAC_SYSCTL(sysctl);
- /* search for connected PHY device */
- for (i = 0; i < PHY_MAX_ADDR; ++i) {
- struct phy_device *const tmp_phydev = lp->mii_bus->phy_map[i];
-
- if (!tmp_phydev)
- continue; /* no PHY here... */
-
- phydev = tmp_phydev;
- break; /* found it */
- }
-
- /* now we are supposed to have a proper phydev, to attach to... */
+ phydev = phy_find_first(lp->mii_bus);
if (!phydev) {
netdev_err(dev, "no phy device found\n");
return -ENODEV;
@@ -419,7 +407,7 @@ static int mii_probe(struct net_device *dev, int phy_mode)
return -EINVAL;
}
- phydev = phy_connect(dev, dev_name(&phydev->dev),
+ phydev = phy_connect(dev, phydev_name(phydev),
&bfin_mac_adjust_link, phy_mode);
if (IS_ERR(phydev)) {
@@ -444,10 +432,8 @@ static int mii_probe(struct net_device *dev, int phy_mode)
lp->old_duplex = -1;
lp->phydev = phydev;
- pr_info("attached PHY driver [%s] "
- "(mii_bus:phy_addr=%s, irq=%d, mdc_clk=%dHz(mdc_div=%d)@sclk=%dMHz)\n",
- phydev->drv->name, dev_name(&phydev->dev), phydev->irq,
- MDC_CLK, mdc_div, sclk/1000000);
+ phy_attached_print(phydev, "mdc_clk=%dHz(mdc_div=%d)@sclk=%dMHz)\n",
+ MDC_CLK, mdc_div, sclk / 1000000);
return 0;
}
@@ -1840,12 +1826,6 @@ static int bfin_mii_bus_probe(struct platform_device *pdev)
snprintf(miibus->id, MII_BUS_ID_SIZE, "%s-%x",
pdev->name, pdev->id);
- miibus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL);
- if (!miibus->irq)
- goto out_err_irq_alloc;
-
- for (i = rc; i < PHY_MAX_ADDR; ++i)
- miibus->irq[i] = PHY_POLL;
rc = clamp(mii_bus_pd->phydev_number, 0, PHY_MAX_ADDR);
if (rc != mii_bus_pd->phydev_number)
@@ -1864,14 +1844,12 @@ static int bfin_mii_bus_probe(struct platform_device *pdev)
rc = mdiobus_register(miibus);
if (rc) {
dev_err(&pdev->dev, "Cannot register MDIO bus!\n");
- goto out_err_mdiobus_register;
+ goto out_err_irq_alloc;
}
platform_set_drvdata(pdev, miibus);
return 0;
-out_err_mdiobus_register:
- kfree(miibus->irq);
out_err_irq_alloc:
mdiobus_free(miibus);
out_err_alloc:
@@ -1887,7 +1865,6 @@ static int bfin_mii_bus_remove(struct platform_device *pdev)
dev_get_platdata(&pdev->dev);
mdiobus_unregister(miibus);
- kfree(miibus->irq);
mdiobus_free(miibus);
peripheral_free_list(mii_bus_pd->mac_peripherals);
@@ -1912,21 +1889,21 @@ static struct platform_driver bfin_mac_driver = {
},
};
+static struct platform_driver * const drivers[] = {
+ &bfin_mii_bus_driver,
+ &bfin_mac_driver,
+};
+
static int __init bfin_mac_init(void)
{
- int ret;
- ret = platform_driver_register(&bfin_mii_bus_driver);
- if (!ret)
- return platform_driver_register(&bfin_mac_driver);
- return -ENODEV;
+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
}
module_init(bfin_mac_init);
static void __exit bfin_mac_cleanup(void)
{
- platform_driver_unregister(&bfin_mac_driver);
- platform_driver_unregister(&bfin_mii_bus_driver);
+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
}
module_exit(bfin_mac_cleanup);
diff --git a/drivers/net/ethernet/aeroflex/greth.c b/drivers/net/ethernet/aeroflex/greth.c
index 20bf55dbd76f..b873531c5575 100644
--- a/drivers/net/ethernet/aeroflex/greth.c
+++ b/drivers/net/ethernet/aeroflex/greth.c
@@ -1337,11 +1337,6 @@ static int greth_mdio_init(struct greth_private *greth)
greth->mdio->write = greth_mdio_write;
greth->mdio->priv = greth;
- greth->mdio->irq = greth->mdio_irqs;
-
- for (phy = 0; phy < PHY_MAX_ADDR; phy++)
- greth->mdio->irq[phy] = PHY_POLL;
-
ret = mdiobus_register(greth->mdio);
if (ret) {
goto error;
diff --git a/drivers/net/ethernet/aeroflex/greth.h b/drivers/net/ethernet/aeroflex/greth.h
index ae16ac94daf8..92dd918e4a83 100644
--- a/drivers/net/ethernet/aeroflex/greth.h
+++ b/drivers/net/ethernet/aeroflex/greth.h
@@ -125,7 +125,6 @@ struct greth_private {
struct phy_device *phy;
struct mii_bus *mdio;
- int mdio_irqs[PHY_MAX_ADDR];
unsigned int link;
unsigned int speed;
unsigned int duplex;
diff --git a/drivers/net/ethernet/agere/et131x.c b/drivers/net/ethernet/agere/et131x.c
index e0f3d197e7f2..3f3bcbea15bd 100644
--- a/drivers/net/ethernet/agere/et131x.c
+++ b/drivers/net/ethernet/agere/et131x.c
@@ -1235,7 +1235,7 @@ static int et131x_mii_read(struct et131x_adapter *adapter, u8 reg, u16 *value)
if (!phydev)
return -EIO;
- return et131x_phy_mii_read(adapter, phydev->addr, reg, value);
+ return et131x_phy_mii_read(adapter, phydev->mdio.addr, reg, value);
}
static int et131x_mii_write(struct et131x_adapter *adapter, u8 addr, u8 reg,
@@ -1462,7 +1462,7 @@ static void et1310_phy_power_switch(struct et131x_adapter *adapter, bool down)
data &= ~BMCR_PDOWN;
if (down)
data |= BMCR_PDOWN;
- et131x_mii_write(adapter, phydev->addr, MII_BMCR, data);
+ et131x_mii_write(adapter, phydev->mdio.addr, MII_BMCR, data);
}
/* et131x_xcvr_init - Init the phy if we are setting it into force mode */
@@ -1490,7 +1490,7 @@ static void et131x_xcvr_init(struct et131x_adapter *adapter)
else
lcr2 |= (LED_VAL_LINKON << LED_TXRX_SHIFT);
- et131x_mii_write(adapter, phydev->addr, PHY_LED_2, lcr2);
+ et131x_mii_write(adapter, phydev->mdio.addr, PHY_LED_2, lcr2);
}
}
@@ -3192,14 +3192,14 @@ static void et131x_adjust_link(struct net_device *netdev)
et131x_mii_read(adapter, PHY_MPHY_CONTROL_REG,
&register18);
- et131x_mii_write(adapter, phydev->addr,
+ et131x_mii_write(adapter, phydev->mdio.addr,
PHY_MPHY_CONTROL_REG,
register18 | 0x4);
- et131x_mii_write(adapter, phydev->addr, PHY_INDEX_REG,
- register18 | 0x8402);
- et131x_mii_write(adapter, phydev->addr, PHY_DATA_REG,
- register18 | 511);
- et131x_mii_write(adapter, phydev->addr,
+ et131x_mii_write(adapter, phydev->mdio.addr,
+ PHY_INDEX_REG, register18 | 0x8402);
+ et131x_mii_write(adapter, phydev->mdio.addr,
+ PHY_DATA_REG, register18 | 511);
+ et131x_mii_write(adapter, phydev->mdio.addr,
PHY_MPHY_CONTROL_REG, register18);
}
@@ -3212,8 +3212,8 @@ static void et131x_adjust_link(struct net_device *netdev)
et131x_mii_read(adapter, PHY_CONFIG, &reg);
reg &= ~ET_PHY_CONFIG_TX_FIFO_DEPTH;
reg |= ET_PHY_CONFIG_FIFO_DEPTH_32;
- et131x_mii_write(adapter, phydev->addr, PHY_CONFIG,
- reg);
+ et131x_mii_write(adapter, phydev->mdio.addr,
+ PHY_CONFIG, reg);
}
et131x_set_rx_dma_timer(adapter);
@@ -3226,14 +3226,14 @@ static void et131x_adjust_link(struct net_device *netdev)
et131x_mii_read(adapter, PHY_MPHY_CONTROL_REG,
&register18);
- et131x_mii_write(adapter, phydev->addr,
+ et131x_mii_write(adapter, phydev->mdio.addr,
PHY_MPHY_CONTROL_REG,
register18 | 0x4);
- et131x_mii_write(adapter, phydev->addr,
+ et131x_mii_write(adapter, phydev->mdio.addr,
PHY_INDEX_REG, register18 | 0x8402);
- et131x_mii_write(adapter, phydev->addr,
+ et131x_mii_write(adapter, phydev->mdio.addr,
PHY_DATA_REG, register18 | 511);
- et131x_mii_write(adapter, phydev->addr,
+ et131x_mii_write(adapter, phydev->mdio.addr,
PHY_MPHY_CONTROL_REG, register18);
}
@@ -3265,7 +3265,7 @@ static int et131x_mii_probe(struct net_device *netdev)
return -ENODEV;
}
- phydev = phy_connect(netdev, dev_name(&phydev->dev),
+ phydev = phy_connect(netdev, phydev_name(phydev),
&et131x_adjust_link, PHY_INTERFACE_MODE_MII);
if (IS_ERR(phydev)) {
@@ -3289,9 +3289,7 @@ static int et131x_mii_probe(struct net_device *netdev)
phydev->autoneg = AUTONEG_ENABLE;
adapter->phydev = phydev;
- dev_info(&adapter->pdev->dev,
- "attached PHY driver [%s] (mii_bus:phy_addr=%s)\n",
- phydev->drv->name, dev_name(&phydev->dev));
+ phy_attached_info(phydev);
return 0;
}
@@ -3327,7 +3325,6 @@ static void et131x_pci_remove(struct pci_dev *pdev)
netif_napi_del(&adapter->napi);
phy_disconnect(adapter->phydev);
mdiobus_unregister(adapter->mii_bus);
- kfree(adapter->mii_bus->irq);
mdiobus_free(adapter->mii_bus);
et131x_adapter_memory_free(adapter);
@@ -3946,7 +3943,6 @@ static int et131x_pci_setup(struct pci_dev *pdev,
struct net_device *netdev;
struct et131x_adapter *adapter;
int rc;
- int ii;
rc = pci_enable_device(pdev);
if (rc < 0) {
@@ -4036,18 +4032,11 @@ static int et131x_pci_setup(struct pci_dev *pdev,
adapter->mii_bus->priv = netdev;
adapter->mii_bus->read = et131x_mdio_read;
adapter->mii_bus->write = et131x_mdio_write;
- adapter->mii_bus->irq = kmalloc_array(PHY_MAX_ADDR, sizeof(int),
- GFP_KERNEL);
- if (!adapter->mii_bus->irq)
- goto err_mdio_free;
-
- for (ii = 0; ii < PHY_MAX_ADDR; ii++)
- adapter->mii_bus->irq[ii] = PHY_POLL;
rc = mdiobus_register(adapter->mii_bus);
if (rc < 0) {
dev_err(&pdev->dev, "failed to register MII bus\n");
- goto err_mdio_free_irq;
+ goto err_mdio_free;
}
rc = et131x_mii_probe(netdev);
@@ -4087,8 +4076,6 @@ err_phy_disconnect:
phy_disconnect(adapter->phydev);
err_mdio_unregister:
mdiobus_unregister(adapter->mii_bus);
-err_mdio_free_irq:
- kfree(adapter->mii_bus->irq);
err_mdio_free:
mdiobus_free(adapter->mii_bus);
err_mem_free:
diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c
index fe644823ceaf..17472851674f 100644
--- a/drivers/net/ethernet/altera/altera_tse_main.c
+++ b/drivers/net/ethernet/altera/altera_tse_main.c
@@ -131,7 +131,6 @@ static int altera_tse_mdio_create(struct net_device *dev, unsigned int id)
{
struct altera_tse_private *priv = netdev_priv(dev);
int ret;
- int i;
struct device_node *mdio_node = NULL;
struct mii_bus *mdio = NULL;
struct device_node *child_node = NULL;
@@ -161,14 +160,6 @@ static int altera_tse_mdio_create(struct net_device *dev, unsigned int id)
mdio->write = &altera_tse_mdio_write;
snprintf(mdio->id, MII_BUS_ID_SIZE, "%s-%u", mdio->name, id);
- mdio->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
- if (mdio->irq == NULL) {
- ret = -ENOMEM;
- goto out_free_mdio;
- }
- for (i = 0; i < PHY_MAX_ADDR; i++)
- mdio->irq[i] = PHY_POLL;
-
mdio->priv = dev;
mdio->parent = priv->device;
@@ -176,7 +167,7 @@ static int altera_tse_mdio_create(struct net_device *dev, unsigned int id)
if (ret != 0) {
netdev_err(dev, "Cannot register MDIO bus %s\n",
mdio->id);
- goto out_free_mdio_irq;
+ goto out_free_mdio;
}
if (netif_msg_drv(priv))
@@ -184,8 +175,6 @@ static int altera_tse_mdio_create(struct net_device *dev, unsigned int id)
priv->mdio = mdio;
return 0;
-out_free_mdio_irq:
- kfree(mdio->irq);
out_free_mdio:
mdiobus_free(mdio);
mdio = NULL;
@@ -855,7 +844,7 @@ static int init_phy(struct net_device *dev)
}
netdev_dbg(dev, "attached to PHY %d UID 0x%08x Link = %d\n",
- phydev->addr, phydev->phy_id, phydev->link);
+ phydev->mdio.addr, phydev->phy_id, phydev->link);
priv->phydev = phydev;
return 0;
diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c
index 5330bcb8a944..d3977d032b48 100644
--- a/drivers/net/ethernet/amd/au1000_eth.c
+++ b/drivers/net/ethernet/amd/au1000_eth.c
@@ -344,9 +344,6 @@ static void au1000_mdio_write(struct net_device *dev, int phy_addr,
static int au1000_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum)
{
- /* WARNING: bus->phy_map[phy_addr].attached_dev == dev does
- * _NOT_ hold (e.g. when PHY is accessed through other MAC's MII bus)
- */
struct net_device *const dev = bus->priv;
/* make sure the MAC associated with this
@@ -502,7 +499,7 @@ static int au1000_mii_probe(struct net_device *dev)
BUG_ON(aup->mac_id < 0 || aup->mac_id > 1);
if (aup->phy_addr)
- phydev = aup->mii_bus->phy_map[aup->phy_addr];
+ phydev = mdiobus_get_phy(aup->mii_bus, aup->phy_addr);
else
netdev_info(dev, "using PHY-less setup\n");
return 0;
@@ -512,8 +509,8 @@ static int au1000_mii_probe(struct net_device *dev)
* on the current MAC's MII bus
*/
for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++)
- if (aup->mii_bus->phy_map[phy_addr]) {
- phydev = aup->mii_bus->phy_map[phy_addr];
+ if (mdiobus_get_phy(aup->mii_bus, aup->phy_addr)) {
+ phydev = mdiobus_get_phy(aup->mii_bus, aup->phy_addr);
if (!aup->phy_search_highest_addr)
/* break out with first one found */
break;
@@ -531,7 +528,8 @@ static int au1000_mii_probe(struct net_device *dev)
*/
for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
struct phy_device *const tmp_phydev =
- aup->mii_bus->phy_map[phy_addr];
+ mdiobus_get_phy(aup->mii_bus,
+ phy_addr);
if (aup->mac_id == 1)
break;
@@ -558,7 +556,7 @@ static int au1000_mii_probe(struct net_device *dev)
/* now we are supposed to have a proper phydev, to attach to... */
BUG_ON(phydev->attached_dev);
- phydev = phy_connect(dev, dev_name(&phydev->dev),
+ phydev = phy_connect(dev, phydev_name(phydev),
&au1000_adjust_link, PHY_INTERFACE_MODE_MII);
if (IS_ERR(phydev)) {
@@ -583,9 +581,7 @@ static int au1000_mii_probe(struct net_device *dev)
aup->old_duplex = -1;
aup->phy_dev = phydev;
- netdev_info(dev, "attached PHY driver [%s] "
- "(mii_bus:phy_addr=%s, irq=%d)\n",
- phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
+ phy_attached_info(phydev);
return 0;
}
@@ -1293,14 +1289,7 @@ static int au1000_probe(struct platform_device *pdev)
aup->mii_bus->name = "au1000_eth_mii";
snprintf(aup->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
pdev->name, aup->mac_id);
- aup->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL);
- if (aup->mii_bus->irq == NULL) {
- err = -ENOMEM;
- goto err_out;
- }
- for (i = 0; i < PHY_MAX_ADDR; ++i)
- aup->mii_bus->irq[i] = PHY_POLL;
/* if known, set corresponding PHY IRQs */
if (aup->phy_static_config)
if (aup->phy_irq && aup->phy_busid == aup->mac_id)
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
index 970781a9e677..f6a7161e3b85 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
@@ -1849,7 +1849,7 @@ static int xgbe_exit(struct xgbe_prv_data *pdata)
usleep_range(10, 15);
/* Poll Until Poll Condition */
- while (count-- && XGMAC_IOREAD_BITS(pdata, DMA_MR, SWR))
+ while (--count && XGMAC_IOREAD_BITS(pdata, DMA_MR, SWR))
usleep_range(500, 600);
if (!count)
@@ -1873,7 +1873,7 @@ static int xgbe_flush_tx_queues(struct xgbe_prv_data *pdata)
/* Poll Until Poll Condition */
for (i = 0; i < pdata->tx_q_count; i++) {
count = 2000;
- while (count-- && XGMAC_MTL_IOREAD_BITS(pdata, i,
+ while (--count && XGMAC_MTL_IOREAD_BITS(pdata, i,
MTL_Q_TQOMR, FTQ))
usleep_range(500, 600);
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
index 53ce1222b11d..8a9b493566c9 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
@@ -2024,7 +2024,6 @@ read_again:
skb->dev = netdev;
skb->protocol = eth_type_trans(skb, netdev);
skb_record_rx_queue(skb, channel->queue_index);
- skb_mark_napi_id(skb, napi);
napi_gro_receive(napi, skb);
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
index c31e691d11fc..db55c9f6e8e1 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
@@ -869,7 +869,7 @@ void xgene_enet_mdio_remove(struct xgene_enet_pdata *pdata)
pdata->mdio_bus = NULL;
}
-struct xgene_mac_ops xgene_gmac_ops = {
+const struct xgene_mac_ops xgene_gmac_ops = {
.init = xgene_gmac_init,
.reset = xgene_gmac_reset,
.rx_enable = xgene_gmac_rx_enable,
@@ -879,7 +879,7 @@ struct xgene_mac_ops xgene_gmac_ops = {
.set_mac_addr = xgene_gmac_set_mac_addr,
};
-struct xgene_port_ops xgene_gport_ops = {
+const struct xgene_port_ops xgene_gport_ops = {
.reset = xgene_enet_reset,
.cle_bypass = xgene_enet_cle_bypass,
.shutdown = xgene_gport_shutdown,
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
index c153a1dc5ff7..8a9091039ab4 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
@@ -340,8 +340,8 @@ int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata);
void xgene_enet_mdio_remove(struct xgene_enet_pdata *pdata);
bool xgene_ring_mgr_init(struct xgene_enet_pdata *p);
-extern struct xgene_mac_ops xgene_gmac_ops;
-extern struct xgene_port_ops xgene_gport_ops;
+extern const struct xgene_mac_ops xgene_gmac_ops;
+extern const struct xgene_port_ops xgene_gport_ops;
extern struct xgene_ring_ops xgene_ring1_ops;
#endif /* __XGENE_ENET_HW_H__ */
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
index 991412ce6f48..a4799c1fc7d4 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
@@ -289,6 +289,7 @@ static int xgene_enet_setup_tx_desc(struct xgene_enet_desc_ring *tx_ring,
struct sk_buff *skb)
{
struct device *dev = ndev_to_dev(tx_ring->ndev);
+ struct xgene_enet_pdata *pdata = netdev_priv(tx_ring->ndev);
struct xgene_enet_raw_desc *raw_desc;
__le64 *exp_desc = NULL, *exp_bufs = NULL;
dma_addr_t dma_addr, pbuf_addr, *frag_dma_addr;
@@ -419,6 +420,7 @@ out:
raw_desc->m0 = cpu_to_le64(SET_VAL(LL, ll) | SET_VAL(NV, nv) |
SET_VAL(USERINFO, tx_ring->tail));
tx_ring->cp_ring->cp_skb[tx_ring->tail] = skb;
+ pdata->tx_level += count;
tx_ring->tail = tail;
return count;
@@ -429,14 +431,13 @@ static netdev_tx_t xgene_enet_start_xmit(struct sk_buff *skb,
{
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
struct xgene_enet_desc_ring *tx_ring = pdata->tx_ring;
- struct xgene_enet_desc_ring *cp_ring = tx_ring->cp_ring;
- u32 tx_level, cq_level;
+ u32 tx_level = pdata->tx_level;
int count;
- tx_level = pdata->ring_ops->len(tx_ring);
- cq_level = pdata->ring_ops->len(cp_ring);
- if (unlikely(tx_level > pdata->tx_qcnt_hi ||
- cq_level > pdata->cp_qcnt_hi)) {
+ if (tx_level < pdata->txc_level)
+ tx_level += ((typeof(pdata->tx_level))~0U);
+
+ if ((tx_level - pdata->txc_level) > pdata->tx_qcnt_hi) {
netif_stop_queue(ndev);
return NETDEV_TX_BUSY;
}
@@ -450,12 +451,12 @@ static netdev_tx_t xgene_enet_start_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
}
- pdata->ring_ops->wr_cmd(tx_ring, count);
skb_tx_timestamp(skb);
pdata->stats.tx_packets++;
pdata->stats.tx_bytes += skb->len;
+ pdata->ring_ops->wr_cmd(tx_ring, count);
return NETDEV_TX_OK;
}
@@ -539,10 +540,13 @@ static int xgene_enet_process_ring(struct xgene_enet_desc_ring *ring,
struct xgene_enet_raw_desc *raw_desc, *exp_desc;
u16 head = ring->head;
u16 slots = ring->slots - 1;
- int ret, count = 0, processed = 0;
+ int ret, desc_count, count = 0, processed = 0;
+ bool is_completion;
do {
raw_desc = &ring->raw_desc[head];
+ desc_count = 0;
+ is_completion = false;
exp_desc = NULL;
if (unlikely(xgene_enet_is_desc_slot_empty(raw_desc)))
break;
@@ -559,18 +563,24 @@ static int xgene_enet_process_ring(struct xgene_enet_desc_ring *ring,
}
dma_rmb();
count++;
+ desc_count++;
}
- if (is_rx_desc(raw_desc))
+ if (is_rx_desc(raw_desc)) {
ret = xgene_enet_rx_frame(ring, raw_desc);
- else
+ } else {
ret = xgene_enet_tx_completion(ring, raw_desc);
+ is_completion = true;
+ }
xgene_enet_mark_desc_slot_empty(raw_desc);
if (exp_desc)
xgene_enet_mark_desc_slot_empty(exp_desc);
head = (head + 1) & slots;
count++;
+ desc_count++;
processed++;
+ if (is_completion)
+ pdata->txc_level += desc_count;
if (ret)
break;
@@ -580,10 +590,8 @@ static int xgene_enet_process_ring(struct xgene_enet_desc_ring *ring,
pdata->ring_ops->wr_cmd(ring, -count);
ring->head = head;
- if (netif_queue_stopped(ring->ndev)) {
- if (pdata->ring_ops->len(ring) < pdata->cp_qcnt_low)
- netif_wake_queue(ring->ndev);
- }
+ if (netif_queue_stopped(ring->ndev))
+ netif_start_queue(ring->ndev);
}
return processed;
@@ -682,16 +690,16 @@ static void xgene_enet_napi_disable(struct xgene_enet_pdata *pdata)
static int xgene_enet_open(struct net_device *ndev)
{
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
- struct xgene_mac_ops *mac_ops = pdata->mac_ops;
+ const struct xgene_mac_ops *mac_ops = pdata->mac_ops;
int ret;
mac_ops->tx_enable(pdata);
mac_ops->rx_enable(pdata);
+ xgene_enet_napi_enable(pdata);
ret = xgene_enet_register_irq(ndev);
if (ret)
return ret;
- xgene_enet_napi_enable(pdata);
if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII)
phy_start(pdata->phy_dev);
@@ -706,7 +714,7 @@ static int xgene_enet_open(struct net_device *ndev)
static int xgene_enet_close(struct net_device *ndev)
{
struct xgene_enet_pdata *pdata = netdev_priv(ndev);
- struct xgene_mac_ops *mac_ops = pdata->mac_ops;
+ const struct xgene_mac_ops *mac_ops = pdata->mac_ops;
netif_stop_queue(ndev);
@@ -715,13 +723,13 @@ static int xgene_enet_close(struct net_device *ndev)
else
cancel_delayed_work_sync(&pdata->link_work);
- xgene_enet_napi_disable(pdata);
- xgene_enet_free_irq(ndev);
- xgene_enet_process_ring(pdata->rx_ring, -1);
-
mac_ops->tx_disable(pdata);
mac_ops->rx_disable(pdata);
+ xgene_enet_free_irq(ndev);
+ xgene_enet_napi_disable(pdata);
+ xgene_enet_process_ring(pdata->rx_ring, -1);
+
return 0;
}
@@ -1033,9 +1041,7 @@ static int xgene_enet_create_desc_rings(struct net_device *ndev)
pdata->tx_ring->cp_ring = cp_ring;
pdata->tx_ring->dst_ring_num = xgene_enet_dst_ring_num(cp_ring);
- pdata->tx_qcnt_hi = pdata->tx_ring->slots / 2;
- pdata->cp_qcnt_hi = pdata->rx_ring->slots / 2;
- pdata->cp_qcnt_low = pdata->cp_qcnt_hi / 2;
+ pdata->tx_qcnt_hi = pdata->tx_ring->slots - 128;
return 0;
@@ -1084,7 +1090,7 @@ static const struct net_device_ops xgene_ndev_ops = {
};
#ifdef CONFIG_ACPI
-static int xgene_get_port_id_acpi(struct device *dev,
+static void xgene_get_port_id_acpi(struct device *dev,
struct xgene_enet_pdata *pdata)
{
acpi_status status;
@@ -1097,24 +1103,19 @@ static int xgene_get_port_id_acpi(struct device *dev,
pdata->port_id = temp;
}
- return 0;
+ return;
}
#endif
-static int xgene_get_port_id_dt(struct device *dev, struct xgene_enet_pdata *pdata)
+static void xgene_get_port_id_dt(struct device *dev, struct xgene_enet_pdata *pdata)
{
u32 id = 0;
- int ret;
- ret = of_property_read_u32(dev->of_node, "port-id", &id);
- if (ret) {
- pdata->port_id = 0;
- ret = 0;
- } else {
- pdata->port_id = id & BIT(0);
- }
+ of_property_read_u32(dev->of_node, "port-id", &id);
- return ret;
+ pdata->port_id = id & BIT(0);
+
+ return;
}
static int xgene_get_tx_delay(struct xgene_enet_pdata *pdata)
@@ -1209,13 +1210,11 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata)
}
if (dev->of_node)
- ret = xgene_get_port_id_dt(dev, pdata);
+ xgene_get_port_id_dt(dev, pdata);
#ifdef CONFIG_ACPI
else
- ret = xgene_get_port_id_acpi(dev, pdata);
+ xgene_get_port_id_acpi(dev, pdata);
#endif
- if (ret)
- return ret;
if (!device_get_mac_address(dev, ndev->dev_addr, ETH_ALEN))
eth_hw_addr_random(ndev);
@@ -1423,7 +1422,7 @@ static int xgene_enet_probe(struct platform_device *pdev)
struct net_device *ndev;
struct xgene_enet_pdata *pdata;
struct device *dev = &pdev->dev;
- struct xgene_mac_ops *mac_ops;
+ const struct xgene_mac_ops *mac_ops;
const struct of_device_id *of_id;
int ret;
@@ -1474,15 +1473,15 @@ static int xgene_enet_probe(struct platform_device *pdev)
}
ndev->hw_features = ndev->features;
- ret = register_netdev(ndev);
+ ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64));
if (ret) {
- netdev_err(ndev, "Failed to register netdev\n");
+ netdev_err(ndev, "No usable DMA configuration\n");
goto err;
}
- ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64));
+ ret = register_netdev(ndev);
if (ret) {
- netdev_err(ndev, "No usable DMA configuration\n");
+ netdev_err(ndev, "Failed to register netdev\n");
goto err;
}
@@ -1490,14 +1489,17 @@ static int xgene_enet_probe(struct platform_device *pdev)
if (ret)
goto err;
- xgene_enet_napi_add(pdata);
mac_ops = pdata->mac_ops;
- if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII)
+ if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) {
ret = xgene_enet_mdio_config(pdata);
- else
+ if (ret)
+ goto err;
+ } else {
INIT_DELAYED_WORK(&pdata->link_work, mac_ops->link_state);
+ }
- return ret;
+ xgene_enet_napi_add(pdata);
+ return 0;
err:
unregister_netdev(ndev);
free_netdev(ndev);
@@ -1507,7 +1509,7 @@ err:
static int xgene_enet_remove(struct platform_device *pdev)
{
struct xgene_enet_pdata *pdata;
- struct xgene_mac_ops *mac_ops;
+ const struct xgene_mac_ops *mac_ops;
struct net_device *ndev;
pdata = platform_get_drvdata(pdev);
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h
index a6e56b88c0a0..70d5b62c125a 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h
@@ -155,11 +155,11 @@ struct xgene_enet_pdata {
enum xgene_enet_id enet_id;
struct xgene_enet_desc_ring *tx_ring;
struct xgene_enet_desc_ring *rx_ring;
+ u16 tx_level;
+ u16 txc_level;
char *dev_name;
u32 rx_buff_cnt;
u32 tx_qcnt_hi;
- u32 cp_qcnt_hi;
- u32 cp_qcnt_low;
u32 rx_irq;
u32 txc_irq;
u8 cq_cnt;
@@ -174,8 +174,8 @@ struct xgene_enet_pdata {
int phy_mode;
enum xgene_enet_rm rm;
struct rtnl_link_stats64 stats;
- struct xgene_mac_ops *mac_ops;
- struct xgene_port_ops *port_ops;
+ const struct xgene_mac_ops *mac_ops;
+ const struct xgene_port_ops *port_ops;
struct xgene_ring_ops *ring_ops;
struct delayed_work link_work;
u32 port_id;
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c
index 05b817e56fde..78475512b683 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c
@@ -405,7 +405,7 @@ static void xgene_enet_link_state(struct work_struct *work)
schedule_delayed_work(&p->link_work, poll_interval);
}
-struct xgene_mac_ops xgene_sgmac_ops = {
+const struct xgene_mac_ops xgene_sgmac_ops = {
.init = xgene_sgmac_init,
.reset = xgene_sgmac_reset,
.rx_enable = xgene_sgmac_rx_enable,
@@ -416,7 +416,7 @@ struct xgene_mac_ops xgene_sgmac_ops = {
.link_state = xgene_enet_link_state
};
-struct xgene_port_ops xgene_sgport_ops = {
+const struct xgene_port_ops xgene_sgport_ops = {
.reset = xgene_enet_reset,
.cle_bypass = xgene_enet_cle_bypass,
.shutdown = xgene_enet_shutdown
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h
index de432465009c..29a71b4dcc44 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h
@@ -35,7 +35,7 @@
#define MPA_IDLE_WITH_QMI_EMPTY BIT(12)
#define SG_RX_DV_GATE_REG_0_ADDR 0x0dfc
-extern struct xgene_mac_ops xgene_sgmac_ops;
-extern struct xgene_port_ops xgene_sgport_ops;
+extern const struct xgene_mac_ops xgene_sgmac_ops;
+extern const struct xgene_port_ops xgene_sgport_ops;
#endif /* __XGENE_ENET_SGMAC_H__ */
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c
index 7a28a48cb2c7..ba030dc1940b 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c
@@ -326,7 +326,7 @@ static void xgene_enet_link_state(struct work_struct *work)
schedule_delayed_work(&pdata->link_work, poll_interval);
}
-struct xgene_mac_ops xgene_xgmac_ops = {
+const struct xgene_mac_ops xgene_xgmac_ops = {
.init = xgene_xgmac_init,
.reset = xgene_xgmac_reset,
.rx_enable = xgene_xgmac_rx_enable,
@@ -338,7 +338,7 @@ struct xgene_mac_ops xgene_xgmac_ops = {
.link_state = xgene_enet_link_state
};
-struct xgene_port_ops xgene_xgport_ops = {
+const struct xgene_port_ops xgene_xgport_ops = {
.reset = xgene_enet_reset,
.cle_bypass = xgene_enet_xgcle_bypass,
.shutdown = xgene_enet_shutdown,
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h
index f8f908dbf51c..0a2dca8a1725 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h
@@ -69,7 +69,7 @@
#define XG_ENET_SPARE_CFG_REG_1_ADDR 0x0410
#define XGENET_RX_DV_GATE_REG_0_ADDR 0x0804
-extern struct xgene_mac_ops xgene_xgmac_ops;
-extern struct xgene_port_ops xgene_xgport_ops;
+extern const struct xgene_mac_ops xgene_xgmac_ops;
+extern const struct xgene_port_ops xgene_xgport_ops;
#endif /* __XGENE_ENET_XGMAC_H__ */
diff --git a/drivers/net/ethernet/arc/Kconfig b/drivers/net/ethernet/arc/Kconfig
index 52a6b16f57d2..689045186064 100644
--- a/drivers/net/ethernet/arc/Kconfig
+++ b/drivers/net/ethernet/arc/Kconfig
@@ -34,9 +34,9 @@ config EMAC_ROCKCHIP
select ARC_EMAC_CORE
depends on OF_IRQ && OF_NET && REGULATOR && HAS_DMA
---help---
- Support for Rockchip RK3066/RK3188 EMAC ethernet controllers.
+ Support for Rockchip RK3036/RK3066/RK3188 EMAC ethernet controllers.
This selects Rockchip SoC glue layer support for the
- emac device driver. This driver is used for RK3066/RK3188
+ emac device driver. This driver is used for RK3036/RK3066/RK3188
EMAC ethernet controller.
endif # NET_VENDOR_ARC
diff --git a/drivers/net/ethernet/arc/emac_rockchip.c b/drivers/net/ethernet/arc/emac_rockchip.c
index c31c7407b753..85e821ccfcd2 100644
--- a/drivers/net/ethernet/arc/emac_rockchip.c
+++ b/drivers/net/ethernet/arc/emac_rockchip.c
@@ -25,17 +25,13 @@
#include "emac.h"
#define DRV_NAME "rockchip_emac"
-#define DRV_VERSION "1.0"
-
-#define GRF_MODE_MII (1UL << 0)
-#define GRF_MODE_RMII (0UL << 0)
-#define GRF_SPEED_10M (0UL << 1)
-#define GRF_SPEED_100M (1UL << 1)
-#define GRF_SPEED_ENABLE_BIT (1UL << 17)
-#define GRF_MODE_ENABLE_BIT (1UL << 16)
+#define DRV_VERSION "1.1"
struct emac_rockchip_soc_data {
- int grf_offset;
+ unsigned int grf_offset;
+ unsigned int grf_mode_offset;
+ unsigned int grf_speed_offset;
+ bool need_div_macclk;
};
struct rockchip_priv_data {
@@ -44,23 +40,22 @@ struct rockchip_priv_data {
const struct emac_rockchip_soc_data *soc_data;
struct regulator *regulator;
struct clk *refclk;
+ struct clk *macclk;
};
static void emac_rockchip_set_mac_speed(void *priv, unsigned int speed)
{
struct rockchip_priv_data *emac = priv;
+ u32 speed_offset = emac->soc_data->grf_speed_offset;
u32 data;
int err = 0;
- /* write-enable bits */
- data = GRF_SPEED_ENABLE_BIT;
-
switch(speed) {
case 10:
- data |= GRF_SPEED_10M;
+ data = (1 << (speed_offset + 16)) | (0 << speed_offset);
break;
case 100:
- data |= GRF_SPEED_100M;
+ data = (1 << (speed_offset + 16)) | (1 << speed_offset);
break;
default:
pr_err("speed %u not supported\n", speed);
@@ -72,14 +67,25 @@ static void emac_rockchip_set_mac_speed(void *priv, unsigned int speed)
pr_err("unable to apply speed %u to grf (%d)\n", speed, err);
}
-static const struct emac_rockchip_soc_data emac_rockchip_dt_data[] = {
- { .grf_offset = 0x154 }, /* rk3066 */
- { .grf_offset = 0x0a4 }, /* rk3188 */
+static const struct emac_rockchip_soc_data emac_rk3036_emac_data = {
+ .grf_offset = 0x140, .grf_mode_offset = 8,
+ .grf_speed_offset = 9, .need_div_macclk = 1,
+};
+
+static const struct emac_rockchip_soc_data emac_rk3066_emac_data = {
+ .grf_offset = 0x154, .grf_mode_offset = 0,
+ .grf_speed_offset = 1, .need_div_macclk = 0,
+};
+
+static const struct emac_rockchip_soc_data emac_rk3188_emac_data = {
+ .grf_offset = 0x0a4, .grf_mode_offset = 0,
+ .grf_speed_offset = 1, .need_div_macclk = 0,
};
static const struct of_device_id emac_rockchip_dt_ids[] = {
- { .compatible = "rockchip,rk3066-emac", .data = &emac_rockchip_dt_data[0] },
- { .compatible = "rockchip,rk3188-emac", .data = &emac_rockchip_dt_data[1] },
+ { .compatible = "rockchip,rk3036-emac", .data = &emac_rk3036_emac_data },
+ { .compatible = "rockchip,rk3066-emac", .data = &emac_rk3066_emac_data },
+ { .compatible = "rockchip,rk3188-emac", .data = &emac_rk3188_emac_data },
{ /* Sentinel */ }
};
@@ -110,7 +116,7 @@ static int emac_rockchip_probe(struct platform_device *pdev)
interface = of_get_phy_mode(dev->of_node);
- /* RK3066 and RK3188 SoCs only support RMII */
+ /* RK3036/RK3066/RK3188 SoCs only support RMII */
if (interface != PHY_INTERFACE_MODE_RMII) {
dev_err(dev, "unsupported phy interface mode %d\n", interface);
err = -ENOTSUPP;
@@ -164,15 +170,12 @@ static int emac_rockchip_probe(struct platform_device *pdev)
}
}
- err = arc_emac_probe(ndev, interface);
- if (err)
- goto out_regulator_disable;
-
- /* write-enable bits */
- data = GRF_MODE_ENABLE_BIT | GRF_SPEED_ENABLE_BIT;
-
- data |= GRF_SPEED_100M;
- data |= GRF_MODE_RMII;
+ /* Set speed 100M */
+ data = (1 << (priv->soc_data->grf_speed_offset + 16)) |
+ (1 << priv->soc_data->grf_speed_offset);
+ /* Set RMII mode */
+ data |= (1 << (priv->soc_data->grf_mode_offset + 16)) |
+ (0 << priv->soc_data->grf_mode_offset);
err = regmap_write(priv->grf, priv->soc_data->grf_offset, data);
if (err) {
@@ -184,6 +187,33 @@ static int emac_rockchip_probe(struct platform_device *pdev)
err = clk_set_rate(priv->refclk, 50000000);
if (err)
dev_err(dev, "failed to change reference clock rate (%d)\n", err);
+
+ if (priv->soc_data->need_div_macclk) {
+ priv->macclk = devm_clk_get(dev, "macclk");
+ if (IS_ERR(priv->macclk)) {
+ dev_err(dev, "failed to retrieve mac clock (%ld)\n", PTR_ERR(priv->macclk));
+ err = PTR_ERR(priv->macclk);
+ goto out_regulator_disable;
+ }
+
+ err = clk_prepare_enable(priv->macclk);
+ if (err) {
+ dev_err(dev, "failed to enable mac clock (%d)\n", err);
+ goto out_regulator_disable;
+ }
+
+ /* RMII TX/RX needs always a rate of 25MHz */
+ err = clk_set_rate(priv->macclk, 25000000);
+ if (err)
+ dev_err(dev, "failed to change mac clock rate (%d)\n", err);
+ }
+
+ err = arc_emac_probe(ndev, interface);
+ if (err) {
+ dev_err(dev, "failed to probe arc emac (%d)\n", err);
+ goto out_regulator_disable;
+ }
+
return 0;
out_regulator_disable:
diff --git a/drivers/net/ethernet/atheros/alx/hw.c b/drivers/net/ethernet/atheros/alx/hw.c
index 7712f068f6d4..1fe35e453d43 100644
--- a/drivers/net/ethernet/atheros/alx/hw.c
+++ b/drivers/net/ethernet/atheros/alx/hw.c
@@ -958,13 +958,13 @@ void alx_configure_basic(struct alx_hw *hw)
alx_write_mem32(hw, ALX_TINT_TPD_THRSHLD, hw->ith_tpd);
alx_write_mem32(hw, ALX_TINT_TIMER, hw->imt);
- raw_mtu = hw->mtu + ETH_HLEN;
- alx_write_mem32(hw, ALX_MTU, raw_mtu + 8);
- if (raw_mtu > ALX_MTU_JUMBO_TH)
+ raw_mtu = ALX_RAW_MTU(hw->mtu);
+ alx_write_mem32(hw, ALX_MTU, raw_mtu);
+ if (raw_mtu > (ALX_MTU_JUMBO_TH + ETH_FCS_LEN + VLAN_HLEN))
hw->rx_ctrl &= ~ALX_MAC_CTRL_FAST_PAUSE;
- if ((raw_mtu + 8) < ALX_TXQ1_JUMBO_TSO_TH)
- val = (raw_mtu + 8 + 7) >> 3;
+ if (raw_mtu < ALX_TXQ1_JUMBO_TSO_TH)
+ val = (raw_mtu + 7) >> 3;
else
val = ALX_TXQ1_JUMBO_TSO_TH >> 3;
alx_write_mem32(hw, ALX_TXQ1, val | ALX_TXQ1_ERRLGPKT_DROP_EN);
diff --git a/drivers/net/ethernet/atheros/alx/hw.h b/drivers/net/ethernet/atheros/alx/hw.h
index 15548802d6f8..f289c05f5cb4 100644
--- a/drivers/net/ethernet/atheros/alx/hw.h
+++ b/drivers/net/ethernet/atheros/alx/hw.h
@@ -37,6 +37,7 @@
#include <linux/types.h>
#include <linux/mdio.h>
#include <linux/pci.h>
+#include <linux/if_vlan.h>
#include "reg.h"
/* Transmit Packet Descriptor, contains 4 32-bit words.
@@ -343,12 +344,14 @@ struct alx_rrd {
ALX_RSS_HASH_TYPE_IPV4_TCP | \
ALX_RSS_HASH_TYPE_IPV6 | \
ALX_RSS_HASH_TYPE_IPV6_TCP)
-#define ALX_DEF_RXBUF_SIZE 1536
+#define ALX_FRAME_PAD 16
+#define ALX_RAW_MTU(_mtu) (_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN)
+#define ALX_MAX_FRAME_LEN(_mtu) (ALIGN((ALX_RAW_MTU(_mtu) + ALX_FRAME_PAD), 8))
+#define ALX_DEF_RXBUF_SIZE ALX_MAX_FRAME_LEN(1500)
#define ALX_MAX_JUMBO_PKT_SIZE (9*1024)
#define ALX_MAX_TSO_PKT_SIZE (7*1024)
#define ALX_MAX_FRAME_SIZE ALX_MAX_JUMBO_PKT_SIZE
-#define ALX_MIN_FRAME_SIZE 68
-#define ALX_RAW_MTU(_mtu) (_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN)
+#define ALX_MIN_FRAME_SIZE (ETH_ZLEN + ETH_FCS_LEN + VLAN_HLEN)
#define ALX_MAX_RX_QUEUES 8
#define ALX_MAX_TX_QUEUES 4
diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c
index c8af3ce3ea38..55b118e876fd 100644
--- a/drivers/net/ethernet/atheros/alx/main.c
+++ b/drivers/net/ethernet/atheros/alx/main.c
@@ -577,7 +577,6 @@ static int alx_alloc_rings(struct alx_priv *alx)
alx->int_mask &= ~ALX_ISR_ALL_QUEUES;
alx->int_mask |= ALX_ISR_TX_Q0 | ALX_ISR_RX_Q0;
- alx->tx_ringsz = alx->tx_ringsz;
netif_napi_add(alx->dev, &alx->napi, alx_poll, 64);
@@ -705,7 +704,7 @@ static int alx_init_sw(struct alx_priv *alx)
hw->smb_timer = 400;
hw->mtu = alx->dev->mtu;
- alx->rxbuf_size = ALIGN(ALX_RAW_MTU(hw->mtu), 8);
+ alx->rxbuf_size = ALX_MAX_FRAME_LEN(hw->mtu);
alx->tx_ringsz = 256;
alx->rx_ringsz = 512;
hw->imt = 200;
@@ -806,7 +805,7 @@ static void alx_reinit(struct alx_priv *alx)
static int alx_change_mtu(struct net_device *netdev, int mtu)
{
struct alx_priv *alx = netdev_priv(netdev);
- int max_frame = mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+ int max_frame = ALX_MAX_FRAME_LEN(mtu);
if ((max_frame < ALX_MIN_FRAME_SIZE) ||
(max_frame > ALX_MAX_FRAME_SIZE))
@@ -817,8 +816,7 @@ static int alx_change_mtu(struct net_device *netdev, int mtu)
netdev->mtu = mtu;
alx->hw.mtu = mtu;
- alx->rxbuf_size = mtu > ALX_DEF_RXBUF_SIZE ?
- ALIGN(max_frame, 8) : ALX_DEF_RXBUF_SIZE;
+ alx->rxbuf_size = max(max_frame, ALX_DEF_RXBUF_SIZE);
netdev_update_features(netdev);
if (netif_running(netdev))
alx_reinit(alx);
@@ -1534,6 +1532,8 @@ static const struct pci_device_id alx_pci_tbl[] = {
.driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG },
{ PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_E2200),
.driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG },
+ { PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_E2400),
+ .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG },
{ PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8162),
.driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG },
{ PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8171) },
diff --git a/drivers/net/ethernet/atheros/alx/reg.h b/drivers/net/ethernet/atheros/alx/reg.h
index af006b44b2a6..0959e6824cb6 100644
--- a/drivers/net/ethernet/atheros/alx/reg.h
+++ b/drivers/net/ethernet/atheros/alx/reg.h
@@ -37,6 +37,7 @@
#define ALX_DEV_ID_AR8161 0x1091
#define ALX_DEV_ID_E2200 0xe091
+#define ALX_DEV_ID_E2400 0xe0a1
#define ALX_DEV_ID_AR8162 0x1090
#define ALX_DEV_ID_AR8171 0x10A1
#define ALX_DEV_ID_AR8172 0x10A0
diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
index 2795d6db10e1..8b5988e210d5 100644
--- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
@@ -1016,13 +1016,12 @@ static int atl1c_setup_ring_resources(struct atl1c_adapter *adapter)
sizeof(struct atl1c_recv_ret_status) * rx_desc_count +
8 * 4;
- ring_header->desc = pci_alloc_consistent(pdev, ring_header->size,
- &ring_header->dma);
+ ring_header->desc = dma_zalloc_coherent(&pdev->dev, ring_header->size,
+ &ring_header->dma, GFP_KERNEL);
if (unlikely(!ring_header->desc)) {
- dev_err(&pdev->dev, "pci_alloc_consistend failed\n");
+ dev_err(&pdev->dev, "could not get memory for DMA buffer\n");
goto err_nomem;
}
- memset(ring_header->desc, 0, ring_header->size);
/* init TPD ring */
tpd_ring[0].dma = roundup(ring_header->dma, 8);
diff --git a/drivers/net/ethernet/aurora/Kconfig b/drivers/net/ethernet/aurora/Kconfig
new file mode 100644
index 000000000000..8ba7f8ff3434
--- /dev/null
+++ b/drivers/net/ethernet/aurora/Kconfig
@@ -0,0 +1,21 @@
+config NET_VENDOR_AURORA
+ bool "Aurora VLSI devices"
+ help
+ If you have a network (Ethernet) device belonging to this class,
+ say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ questions about Aurora devices. If you say Y, you will be asked
+ for your specific device in the following questions.
+
+if NET_VENDOR_AURORA
+
+config AURORA_NB8800
+ tristate "Aurora AU-NB8800 support"
+ depends on HAS_DMA
+ select PHYLIB
+ help
+ Support for the AU-NB8800 gigabit Ethernet controller.
+
+endif
diff --git a/drivers/net/ethernet/aurora/Makefile b/drivers/net/ethernet/aurora/Makefile
new file mode 100644
index 000000000000..6cb528a2fc26
--- /dev/null
+++ b/drivers/net/ethernet/aurora/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_AURORA_NB8800) += nb8800.o
diff --git a/drivers/net/ethernet/aurora/nb8800.c b/drivers/net/ethernet/aurora/nb8800.c
new file mode 100644
index 000000000000..ecc4a334c507
--- /dev/null
+++ b/drivers/net/ethernet/aurora/nb8800.c
@@ -0,0 +1,1552 @@
+/*
+ * Copyright (C) 2015 Mans Rullgard <mans@mansr.com>
+ *
+ * Mostly rewritten, based on driver from Sigma Designs. Original
+ * copyright notice below.
+ *
+ *
+ * Driver for tangox SMP864x/SMP865x/SMP867x/SMP868x builtin Ethernet Mac.
+ *
+ * Copyright (C) 2005 Maxime Bizon <mbizon@freebox.fr>
+ *
+ * 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/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/dma-mapping.h>
+#include <linux/phy.h>
+#include <linux/cache.h>
+#include <linux/jiffies.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <asm/barrier.h>
+
+#include "nb8800.h"
+
+static void nb8800_tx_done(struct net_device *dev);
+static int nb8800_dma_stop(struct net_device *dev);
+
+static inline u8 nb8800_readb(struct nb8800_priv *priv, int reg)
+{
+ return readb_relaxed(priv->base + reg);
+}
+
+static inline u32 nb8800_readl(struct nb8800_priv *priv, int reg)
+{
+ return readl_relaxed(priv->base + reg);
+}
+
+static inline void nb8800_writeb(struct nb8800_priv *priv, int reg, u8 val)
+{
+ writeb_relaxed(val, priv->base + reg);
+}
+
+static inline void nb8800_writew(struct nb8800_priv *priv, int reg, u16 val)
+{
+ writew_relaxed(val, priv->base + reg);
+}
+
+static inline void nb8800_writel(struct nb8800_priv *priv, int reg, u32 val)
+{
+ writel_relaxed(val, priv->base + reg);
+}
+
+static inline void nb8800_maskb(struct nb8800_priv *priv, int reg,
+ u32 mask, u32 val)
+{
+ u32 old = nb8800_readb(priv, reg);
+ u32 new = (old & ~mask) | (val & mask);
+
+ if (new != old)
+ nb8800_writeb(priv, reg, new);
+}
+
+static inline void nb8800_maskl(struct nb8800_priv *priv, int reg,
+ u32 mask, u32 val)
+{
+ u32 old = nb8800_readl(priv, reg);
+ u32 new = (old & ~mask) | (val & mask);
+
+ if (new != old)
+ nb8800_writel(priv, reg, new);
+}
+
+static inline void nb8800_modb(struct nb8800_priv *priv, int reg, u8 bits,
+ bool set)
+{
+ nb8800_maskb(priv, reg, bits, set ? bits : 0);
+}
+
+static inline void nb8800_setb(struct nb8800_priv *priv, int reg, u8 bits)
+{
+ nb8800_maskb(priv, reg, bits, bits);
+}
+
+static inline void nb8800_clearb(struct nb8800_priv *priv, int reg, u8 bits)
+{
+ nb8800_maskb(priv, reg, bits, 0);
+}
+
+static inline void nb8800_modl(struct nb8800_priv *priv, int reg, u32 bits,
+ bool set)
+{
+ nb8800_maskl(priv, reg, bits, set ? bits : 0);
+}
+
+static inline void nb8800_setl(struct nb8800_priv *priv, int reg, u32 bits)
+{
+ nb8800_maskl(priv, reg, bits, bits);
+}
+
+static inline void nb8800_clearl(struct nb8800_priv *priv, int reg, u32 bits)
+{
+ nb8800_maskl(priv, reg, bits, 0);
+}
+
+static int nb8800_mdio_wait(struct mii_bus *bus)
+{
+ struct nb8800_priv *priv = bus->priv;
+ u32 val;
+
+ return readl_poll_timeout_atomic(priv->base + NB8800_MDIO_CMD,
+ val, !(val & MDIO_CMD_GO), 1, 1000);
+}
+
+static int nb8800_mdio_cmd(struct mii_bus *bus, u32 cmd)
+{
+ struct nb8800_priv *priv = bus->priv;
+ int err;
+
+ err = nb8800_mdio_wait(bus);
+ if (err)
+ return err;
+
+ nb8800_writel(priv, NB8800_MDIO_CMD, cmd);
+ udelay(10);
+ nb8800_writel(priv, NB8800_MDIO_CMD, cmd | MDIO_CMD_GO);
+
+ return nb8800_mdio_wait(bus);
+}
+
+static int nb8800_mdio_read(struct mii_bus *bus, int phy_id, int reg)
+{
+ struct nb8800_priv *priv = bus->priv;
+ u32 val;
+ int err;
+
+ err = nb8800_mdio_cmd(bus, MDIO_CMD_ADDR(phy_id) | MDIO_CMD_REG(reg));
+ if (err)
+ return err;
+
+ val = nb8800_readl(priv, NB8800_MDIO_STS);
+ if (val & MDIO_STS_ERR)
+ return 0xffff;
+
+ return val & 0xffff;
+}
+
+static int nb8800_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
+{
+ u32 cmd = MDIO_CMD_ADDR(phy_id) | MDIO_CMD_REG(reg) |
+ MDIO_CMD_DATA(val) | MDIO_CMD_WR;
+
+ return nb8800_mdio_cmd(bus, cmd);
+}
+
+static void nb8800_mac_tx(struct net_device *dev, bool enable)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+
+ while (nb8800_readl(priv, NB8800_TXC_CR) & TCR_EN)
+ cpu_relax();
+
+ nb8800_modb(priv, NB8800_TX_CTL1, TX_EN, enable);
+}
+
+static void nb8800_mac_rx(struct net_device *dev, bool enable)
+{
+ nb8800_modb(netdev_priv(dev), NB8800_RX_CTL, RX_EN, enable);
+}
+
+static void nb8800_mac_af(struct net_device *dev, bool enable)
+{
+ nb8800_modb(netdev_priv(dev), NB8800_RX_CTL, RX_AF_EN, enable);
+}
+
+static void nb8800_start_rx(struct net_device *dev)
+{
+ nb8800_setl(netdev_priv(dev), NB8800_RXC_CR, RCR_EN);
+}
+
+static int nb8800_alloc_rx(struct net_device *dev, unsigned int i, bool napi)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+ struct nb8800_rx_desc *rxd = &priv->rx_descs[i];
+ struct nb8800_rx_buf *rxb = &priv->rx_bufs[i];
+ int size = L1_CACHE_ALIGN(RX_BUF_SIZE);
+ dma_addr_t dma_addr;
+ struct page *page;
+ unsigned long offset;
+ void *data;
+
+ data = napi ? napi_alloc_frag(size) : netdev_alloc_frag(size);
+ if (!data)
+ return -ENOMEM;
+
+ page = virt_to_head_page(data);
+ offset = data - page_address(page);
+
+ dma_addr = dma_map_page(&dev->dev, page, offset, RX_BUF_SIZE,
+ DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(&dev->dev, dma_addr)) {
+ skb_free_frag(data);
+ return -ENOMEM;
+ }
+
+ rxb->page = page;
+ rxb->offset = offset;
+ rxd->desc.s_addr = dma_addr;
+
+ return 0;
+}
+
+static void nb8800_receive(struct net_device *dev, unsigned int i,
+ unsigned int len)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+ struct nb8800_rx_desc *rxd = &priv->rx_descs[i];
+ struct page *page = priv->rx_bufs[i].page;
+ int offset = priv->rx_bufs[i].offset;
+ void *data = page_address(page) + offset;
+ dma_addr_t dma = rxd->desc.s_addr;
+ struct sk_buff *skb;
+ unsigned int size;
+ int err;
+
+ size = len <= RX_COPYBREAK ? len : RX_COPYHDR;
+
+ skb = napi_alloc_skb(&priv->napi, size);
+ if (!skb) {
+ netdev_err(dev, "rx skb allocation failed\n");
+ dev->stats.rx_dropped++;
+ return;
+ }
+
+ if (len <= RX_COPYBREAK) {
+ dma_sync_single_for_cpu(&dev->dev, dma, len, DMA_FROM_DEVICE);
+ memcpy(skb_put(skb, len), data, len);
+ dma_sync_single_for_device(&dev->dev, dma, len,
+ DMA_FROM_DEVICE);
+ } else {
+ err = nb8800_alloc_rx(dev, i, true);
+ if (err) {
+ netdev_err(dev, "rx buffer allocation failed\n");
+ dev->stats.rx_dropped++;
+ return;
+ }
+
+ dma_unmap_page(&dev->dev, dma, RX_BUF_SIZE, DMA_FROM_DEVICE);
+ memcpy(skb_put(skb, RX_COPYHDR), data, RX_COPYHDR);
+ skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page,
+ offset + RX_COPYHDR, len - RX_COPYHDR,
+ RX_BUF_SIZE);
+ }
+
+ skb->protocol = eth_type_trans(skb, dev);
+ napi_gro_receive(&priv->napi, skb);
+}
+
+static void nb8800_rx_error(struct net_device *dev, u32 report)
+{
+ if (report & RX_LENGTH_ERR)
+ dev->stats.rx_length_errors++;
+
+ if (report & RX_FCS_ERR)
+ dev->stats.rx_crc_errors++;
+
+ if (report & RX_FIFO_OVERRUN)
+ dev->stats.rx_fifo_errors++;
+
+ if (report & RX_ALIGNMENT_ERROR)
+ dev->stats.rx_frame_errors++;
+
+ dev->stats.rx_errors++;
+}
+
+static int nb8800_poll(struct napi_struct *napi, int budget)
+{
+ struct net_device *dev = napi->dev;
+ struct nb8800_priv *priv = netdev_priv(dev);
+ struct nb8800_rx_desc *rxd;
+ unsigned int last = priv->rx_eoc;
+ unsigned int next;
+ int work = 0;
+
+ nb8800_tx_done(dev);
+
+again:
+ while (work < budget) {
+ struct nb8800_rx_buf *rxb;
+ unsigned int len;
+
+ next = (last + 1) % RX_DESC_COUNT;
+
+ rxb = &priv->rx_bufs[next];
+ rxd = &priv->rx_descs[next];
+
+ if (!rxd->report)
+ break;
+
+ len = RX_BYTES_TRANSFERRED(rxd->report);
+
+ if (IS_RX_ERROR(rxd->report))
+ nb8800_rx_error(dev, rxd->report);
+ else
+ nb8800_receive(dev, next, len);
+
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += len;
+
+ if (rxd->report & RX_MULTICAST_PKT)
+ dev->stats.multicast++;
+
+ rxd->report = 0;
+ last = next;
+ work++;
+ }
+
+ if (work) {
+ priv->rx_descs[last].desc.config |= DESC_EOC;
+ wmb(); /* ensure new EOC is written before clearing old */
+ priv->rx_descs[priv->rx_eoc].desc.config &= ~DESC_EOC;
+ priv->rx_eoc = last;
+ nb8800_start_rx(dev);
+ }
+
+ if (work < budget) {
+ nb8800_writel(priv, NB8800_RX_ITR, priv->rx_itr_irq);
+
+ /* If a packet arrived after we last checked but
+ * before writing RX_ITR, the interrupt will be
+ * delayed, so we retrieve it now.
+ */
+ if (priv->rx_descs[next].report)
+ goto again;
+
+ napi_complete_done(napi, work);
+ }
+
+ return work;
+}
+
+static void __nb8800_tx_dma_start(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+ struct nb8800_tx_buf *txb;
+ u32 txc_cr;
+
+ txb = &priv->tx_bufs[priv->tx_queue];
+ if (!txb->ready)
+ return;
+
+ txc_cr = nb8800_readl(priv, NB8800_TXC_CR);
+ if (txc_cr & TCR_EN)
+ return;
+
+ nb8800_writel(priv, NB8800_TX_DESC_ADDR, txb->dma_desc);
+ wmb(); /* ensure desc addr is written before starting DMA */
+ nb8800_writel(priv, NB8800_TXC_CR, txc_cr | TCR_EN);
+
+ priv->tx_queue = (priv->tx_queue + txb->chain_len) % TX_DESC_COUNT;
+}
+
+static void nb8800_tx_dma_start(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+
+ spin_lock_irq(&priv->tx_lock);
+ __nb8800_tx_dma_start(dev);
+ spin_unlock_irq(&priv->tx_lock);
+}
+
+static void nb8800_tx_dma_start_irq(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+
+ spin_lock(&priv->tx_lock);
+ __nb8800_tx_dma_start(dev);
+ spin_unlock(&priv->tx_lock);
+}
+
+static int nb8800_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+ struct nb8800_tx_desc *txd;
+ struct nb8800_tx_buf *txb;
+ struct nb8800_dma_desc *desc;
+ dma_addr_t dma_addr;
+ unsigned int dma_len;
+ unsigned int align;
+ unsigned int next;
+
+ if (atomic_read(&priv->tx_free) <= NB8800_DESC_LOW) {
+ netif_stop_queue(dev);
+ return NETDEV_TX_BUSY;
+ }
+
+ align = (8 - (uintptr_t)skb->data) & 7;
+
+ dma_len = skb->len - align;
+ dma_addr = dma_map_single(&dev->dev, skb->data + align,
+ dma_len, DMA_TO_DEVICE);
+
+ if (dma_mapping_error(&dev->dev, dma_addr)) {
+ netdev_err(dev, "tx dma mapping error\n");
+ kfree_skb(skb);
+ dev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+ }
+
+ if (atomic_dec_return(&priv->tx_free) <= NB8800_DESC_LOW) {
+ netif_stop_queue(dev);
+ skb->xmit_more = 0;
+ }
+
+ next = priv->tx_next;
+ txb = &priv->tx_bufs[next];
+ txd = &priv->tx_descs[next];
+ desc = &txd->desc[0];
+
+ next = (next + 1) % TX_DESC_COUNT;
+
+ if (align) {
+ memcpy(txd->buf, skb->data, align);
+
+ desc->s_addr =
+ txb->dma_desc + offsetof(struct nb8800_tx_desc, buf);
+ desc->n_addr = txb->dma_desc + sizeof(txd->desc[0]);
+ desc->config = DESC_BTS(2) | DESC_DS | align;
+
+ desc++;
+ }
+
+ desc->s_addr = dma_addr;
+ desc->n_addr = priv->tx_bufs[next].dma_desc;
+ desc->config = DESC_BTS(2) | DESC_DS | DESC_EOF | dma_len;
+
+ if (!skb->xmit_more)
+ desc->config |= DESC_EOC;
+
+ txb->skb = skb;
+ txb->dma_addr = dma_addr;
+ txb->dma_len = dma_len;
+
+ if (!priv->tx_chain) {
+ txb->chain_len = 1;
+ priv->tx_chain = txb;
+ } else {
+ priv->tx_chain->chain_len++;
+ }
+
+ netdev_sent_queue(dev, skb->len);
+
+ priv->tx_next = next;
+
+ if (!skb->xmit_more) {
+ smp_wmb();
+ priv->tx_chain->ready = true;
+ priv->tx_chain = NULL;
+ nb8800_tx_dma_start(dev);
+ }
+
+ return NETDEV_TX_OK;
+}
+
+static void nb8800_tx_error(struct net_device *dev, u32 report)
+{
+ if (report & TX_LATE_COLLISION)
+ dev->stats.collisions++;
+
+ if (report & TX_PACKET_DROPPED)
+ dev->stats.tx_dropped++;
+
+ if (report & TX_FIFO_UNDERRUN)
+ dev->stats.tx_fifo_errors++;
+
+ dev->stats.tx_errors++;
+}
+
+static void nb8800_tx_done(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+ unsigned int limit = priv->tx_next;
+ unsigned int done = priv->tx_done;
+ unsigned int packets = 0;
+ unsigned int len = 0;
+
+ while (done != limit) {
+ struct nb8800_tx_desc *txd = &priv->tx_descs[done];
+ struct nb8800_tx_buf *txb = &priv->tx_bufs[done];
+ struct sk_buff *skb;
+
+ if (!txd->report)
+ break;
+
+ skb = txb->skb;
+ len += skb->len;
+
+ dma_unmap_single(&dev->dev, txb->dma_addr, txb->dma_len,
+ DMA_TO_DEVICE);
+
+ if (IS_TX_ERROR(txd->report)) {
+ nb8800_tx_error(dev, txd->report);
+ kfree_skb(skb);
+ } else {
+ consume_skb(skb);
+ }
+
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += TX_BYTES_TRANSFERRED(txd->report);
+ dev->stats.collisions += TX_EARLY_COLLISIONS(txd->report);
+
+ txb->skb = NULL;
+ txb->ready = false;
+ txd->report = 0;
+
+ done = (done + 1) % TX_DESC_COUNT;
+ packets++;
+ }
+
+ if (packets) {
+ smp_mb__before_atomic();
+ atomic_add(packets, &priv->tx_free);
+ netdev_completed_queue(dev, packets, len);
+ netif_wake_queue(dev);
+ priv->tx_done = done;
+ }
+}
+
+static irqreturn_t nb8800_irq(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct nb8800_priv *priv = netdev_priv(dev);
+ irqreturn_t ret = IRQ_NONE;
+ u32 val;
+
+ /* tx interrupt */
+ val = nb8800_readl(priv, NB8800_TXC_SR);
+ if (val) {
+ nb8800_writel(priv, NB8800_TXC_SR, val);
+
+ if (val & TSR_DI)
+ nb8800_tx_dma_start_irq(dev);
+
+ if (val & TSR_TI)
+ napi_schedule_irqoff(&priv->napi);
+
+ if (unlikely(val & TSR_DE))
+ netdev_err(dev, "TX DMA error\n");
+
+ /* should never happen with automatic status retrieval */
+ if (unlikely(val & TSR_TO))
+ netdev_err(dev, "TX Status FIFO overflow\n");
+
+ ret = IRQ_HANDLED;
+ }
+
+ /* rx interrupt */
+ val = nb8800_readl(priv, NB8800_RXC_SR);
+ if (val) {
+ nb8800_writel(priv, NB8800_RXC_SR, val);
+
+ if (likely(val & (RSR_RI | RSR_DI))) {
+ nb8800_writel(priv, NB8800_RX_ITR, priv->rx_itr_poll);
+ napi_schedule_irqoff(&priv->napi);
+ }
+
+ if (unlikely(val & RSR_DE))
+ netdev_err(dev, "RX DMA error\n");
+
+ /* should never happen with automatic status retrieval */
+ if (unlikely(val & RSR_RO))
+ netdev_err(dev, "RX Status FIFO overflow\n");
+
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static void nb8800_mac_config(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+ bool gigabit = priv->speed == SPEED_1000;
+ u32 mac_mode_mask = RGMII_MODE | HALF_DUPLEX | GMAC_MODE;
+ u32 mac_mode = 0;
+ u32 slot_time;
+ u32 phy_clk;
+ u32 ict;
+
+ if (!priv->duplex)
+ mac_mode |= HALF_DUPLEX;
+
+ if (gigabit) {
+ if (priv->phy_mode == PHY_INTERFACE_MODE_RGMII)
+ mac_mode |= RGMII_MODE;
+
+ mac_mode |= GMAC_MODE;
+ phy_clk = 125000000;
+
+ /* Should be 512 but register is only 8 bits */
+ slot_time = 255;
+ } else {
+ phy_clk = 25000000;
+ slot_time = 128;
+ }
+
+ ict = DIV_ROUND_UP(phy_clk, clk_get_rate(priv->clk));
+
+ nb8800_writeb(priv, NB8800_IC_THRESHOLD, ict);
+ nb8800_writeb(priv, NB8800_SLOT_TIME, slot_time);
+ nb8800_maskb(priv, NB8800_MAC_MODE, mac_mode_mask, mac_mode);
+}
+
+static void nb8800_pause_config(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+ struct phy_device *phydev = priv->phydev;
+ u32 rxcr;
+
+ if (priv->pause_aneg) {
+ if (!phydev || !phydev->link)
+ return;
+
+ priv->pause_rx = phydev->pause;
+ priv->pause_tx = phydev->pause ^ phydev->asym_pause;
+ }
+
+ nb8800_modb(priv, NB8800_RX_CTL, RX_PAUSE_EN, priv->pause_rx);
+
+ rxcr = nb8800_readl(priv, NB8800_RXC_CR);
+ if (!!(rxcr & RCR_FL) == priv->pause_tx)
+ return;
+
+ if (netif_running(dev)) {
+ napi_disable(&priv->napi);
+ netif_tx_lock_bh(dev);
+ nb8800_dma_stop(dev);
+ nb8800_modl(priv, NB8800_RXC_CR, RCR_FL, priv->pause_tx);
+ nb8800_start_rx(dev);
+ netif_tx_unlock_bh(dev);
+ napi_enable(&priv->napi);
+ } else {
+ nb8800_modl(priv, NB8800_RXC_CR, RCR_FL, priv->pause_tx);
+ }
+}
+
+static void nb8800_link_reconfigure(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+ struct phy_device *phydev = priv->phydev;
+ int change = 0;
+
+ if (phydev->link) {
+ if (phydev->speed != priv->speed) {
+ priv->speed = phydev->speed;
+ change = 1;
+ }
+
+ if (phydev->duplex != priv->duplex) {
+ priv->duplex = phydev->duplex;
+ change = 1;
+ }
+
+ if (change)
+ nb8800_mac_config(dev);
+
+ nb8800_pause_config(dev);
+ }
+
+ if (phydev->link != priv->link) {
+ priv->link = phydev->link;
+ change = 1;
+ }
+
+ if (change)
+ phy_print_status(priv->phydev);
+}
+
+static void nb8800_update_mac_addr(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+ int i;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ nb8800_writeb(priv, NB8800_SRC_ADDR(i), dev->dev_addr[i]);
+
+ for (i = 0; i < ETH_ALEN; i++)
+ nb8800_writeb(priv, NB8800_UC_ADDR(i), dev->dev_addr[i]);
+}
+
+static int nb8800_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *sock = addr;
+
+ if (netif_running(dev))
+ return -EBUSY;
+
+ ether_addr_copy(dev->dev_addr, sock->sa_data);
+ nb8800_update_mac_addr(dev);
+
+ return 0;
+}
+
+static void nb8800_mc_init(struct net_device *dev, int val)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+
+ nb8800_writeb(priv, NB8800_MC_INIT, val);
+ readb_poll_timeout_atomic(priv->base + NB8800_MC_INIT, val, !val,
+ 1, 1000);
+}
+
+static void nb8800_set_rx_mode(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+ struct netdev_hw_addr *ha;
+ int i;
+
+ if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
+ nb8800_mac_af(dev, false);
+ return;
+ }
+
+ nb8800_mac_af(dev, true);
+ nb8800_mc_init(dev, 0);
+
+ netdev_for_each_mc_addr(ha, dev) {
+ for (i = 0; i < ETH_ALEN; i++)
+ nb8800_writeb(priv, NB8800_MC_ADDR(i), ha->addr[i]);
+
+ nb8800_mc_init(dev, 0xff);
+ }
+}
+
+#define RX_DESC_SIZE (RX_DESC_COUNT * sizeof(struct nb8800_rx_desc))
+#define TX_DESC_SIZE (TX_DESC_COUNT * sizeof(struct nb8800_tx_desc))
+
+static void nb8800_dma_free(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+ unsigned int i;
+
+ if (priv->rx_bufs) {
+ for (i = 0; i < RX_DESC_COUNT; i++)
+ if (priv->rx_bufs[i].page)
+ put_page(priv->rx_bufs[i].page);
+
+ kfree(priv->rx_bufs);
+ priv->rx_bufs = NULL;
+ }
+
+ if (priv->tx_bufs) {
+ for (i = 0; i < TX_DESC_COUNT; i++)
+ kfree_skb(priv->tx_bufs[i].skb);
+
+ kfree(priv->tx_bufs);
+ priv->tx_bufs = NULL;
+ }
+
+ if (priv->rx_descs) {
+ dma_free_coherent(dev->dev.parent, RX_DESC_SIZE, priv->rx_descs,
+ priv->rx_desc_dma);
+ priv->rx_descs = NULL;
+ }
+
+ if (priv->tx_descs) {
+ dma_free_coherent(dev->dev.parent, TX_DESC_SIZE, priv->tx_descs,
+ priv->tx_desc_dma);
+ priv->tx_descs = NULL;
+ }
+}
+
+static void nb8800_dma_reset(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+ struct nb8800_rx_desc *rxd;
+ struct nb8800_tx_desc *txd;
+ unsigned int i;
+
+ for (i = 0; i < RX_DESC_COUNT; i++) {
+ dma_addr_t rx_dma = priv->rx_desc_dma + i * sizeof(*rxd);
+
+ rxd = &priv->rx_descs[i];
+ rxd->desc.n_addr = rx_dma + sizeof(*rxd);
+ rxd->desc.r_addr =
+ rx_dma + offsetof(struct nb8800_rx_desc, report);
+ rxd->desc.config = priv->rx_dma_config;
+ rxd->report = 0;
+ }
+
+ rxd->desc.n_addr = priv->rx_desc_dma;
+ rxd->desc.config |= DESC_EOC;
+
+ priv->rx_eoc = RX_DESC_COUNT - 1;
+
+ for (i = 0; i < TX_DESC_COUNT; i++) {
+ struct nb8800_tx_buf *txb = &priv->tx_bufs[i];
+ dma_addr_t r_dma = txb->dma_desc +
+ offsetof(struct nb8800_tx_desc, report);
+
+ txd = &priv->tx_descs[i];
+ txd->desc[0].r_addr = r_dma;
+ txd->desc[1].r_addr = r_dma;
+ txd->report = 0;
+ }
+
+ priv->tx_next = 0;
+ priv->tx_queue = 0;
+ priv->tx_done = 0;
+ atomic_set(&priv->tx_free, TX_DESC_COUNT);
+
+ nb8800_writel(priv, NB8800_RX_DESC_ADDR, priv->rx_desc_dma);
+
+ wmb(); /* ensure all setup is written before starting */
+}
+
+static int nb8800_dma_init(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+ unsigned int n_rx = RX_DESC_COUNT;
+ unsigned int n_tx = TX_DESC_COUNT;
+ unsigned int i;
+ int err;
+
+ priv->rx_descs = dma_alloc_coherent(dev->dev.parent, RX_DESC_SIZE,
+ &priv->rx_desc_dma, GFP_KERNEL);
+ if (!priv->rx_descs)
+ goto err_out;
+
+ priv->rx_bufs = kcalloc(n_rx, sizeof(*priv->rx_bufs), GFP_KERNEL);
+ if (!priv->rx_bufs)
+ goto err_out;
+
+ for (i = 0; i < n_rx; i++) {
+ err = nb8800_alloc_rx(dev, i, false);
+ if (err)
+ goto err_out;
+ }
+
+ priv->tx_descs = dma_alloc_coherent(dev->dev.parent, TX_DESC_SIZE,
+ &priv->tx_desc_dma, GFP_KERNEL);
+ if (!priv->tx_descs)
+ goto err_out;
+
+ priv->tx_bufs = kcalloc(n_tx, sizeof(*priv->tx_bufs), GFP_KERNEL);
+ if (!priv->tx_bufs)
+ goto err_out;
+
+ for (i = 0; i < n_tx; i++)
+ priv->tx_bufs[i].dma_desc =
+ priv->tx_desc_dma + i * sizeof(struct nb8800_tx_desc);
+
+ nb8800_dma_reset(dev);
+
+ return 0;
+
+err_out:
+ nb8800_dma_free(dev);
+
+ return -ENOMEM;
+}
+
+static int nb8800_dma_stop(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+ struct nb8800_tx_buf *txb = &priv->tx_bufs[0];
+ struct nb8800_tx_desc *txd = &priv->tx_descs[0];
+ int retry = 5;
+ u32 txcr;
+ u32 rxcr;
+ int err;
+ unsigned int i;
+
+ /* wait for tx to finish */
+ err = readl_poll_timeout_atomic(priv->base + NB8800_TXC_CR, txcr,
+ !(txcr & TCR_EN) &&
+ priv->tx_done == priv->tx_next,
+ 1000, 1000000);
+ if (err)
+ return err;
+
+ /* The rx DMA only stops if it reaches the end of chain.
+ * To make this happen, we set the EOC flag on all rx
+ * descriptors, put the device in loopback mode, and send
+ * a few dummy frames. The interrupt handler will ignore
+ * these since NAPI is disabled and no real frames are in
+ * the tx queue.
+ */
+
+ for (i = 0; i < RX_DESC_COUNT; i++)
+ priv->rx_descs[i].desc.config |= DESC_EOC;
+
+ txd->desc[0].s_addr =
+ txb->dma_desc + offsetof(struct nb8800_tx_desc, buf);
+ txd->desc[0].config = DESC_BTS(2) | DESC_DS | DESC_EOF | DESC_EOC | 8;
+ memset(txd->buf, 0, sizeof(txd->buf));
+
+ nb8800_mac_af(dev, false);
+ nb8800_setb(priv, NB8800_MAC_MODE, LOOPBACK_EN);
+
+ do {
+ nb8800_writel(priv, NB8800_TX_DESC_ADDR, txb->dma_desc);
+ wmb();
+ nb8800_writel(priv, NB8800_TXC_CR, txcr | TCR_EN);
+
+ err = readl_poll_timeout_atomic(priv->base + NB8800_RXC_CR,
+ rxcr, !(rxcr & RCR_EN),
+ 1000, 100000);
+ } while (err && --retry);
+
+ nb8800_mac_af(dev, true);
+ nb8800_clearb(priv, NB8800_MAC_MODE, LOOPBACK_EN);
+ nb8800_dma_reset(dev);
+
+ return retry ? 0 : -ETIMEDOUT;
+}
+
+static void nb8800_pause_adv(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+ u32 adv = 0;
+
+ if (!priv->phydev)
+ return;
+
+ if (priv->pause_rx)
+ adv |= ADVERTISED_Pause | ADVERTISED_Asym_Pause;
+ if (priv->pause_tx)
+ adv ^= ADVERTISED_Asym_Pause;
+
+ priv->phydev->supported |= adv;
+ priv->phydev->advertising |= adv;
+}
+
+static int nb8800_open(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+ int err;
+
+ /* clear any pending interrupts */
+ nb8800_writel(priv, NB8800_RXC_SR, 0xf);
+ nb8800_writel(priv, NB8800_TXC_SR, 0xf);
+
+ err = nb8800_dma_init(dev);
+ if (err)
+ return err;
+
+ err = request_irq(dev->irq, nb8800_irq, 0, dev_name(&dev->dev), dev);
+ if (err)
+ goto err_free_dma;
+
+ nb8800_mac_rx(dev, true);
+ nb8800_mac_tx(dev, true);
+
+ priv->phydev = of_phy_connect(dev, priv->phy_node,
+ nb8800_link_reconfigure, 0,
+ priv->phy_mode);
+ if (!priv->phydev)
+ goto err_free_irq;
+
+ nb8800_pause_adv(dev);
+
+ netdev_reset_queue(dev);
+ napi_enable(&priv->napi);
+ netif_start_queue(dev);
+
+ nb8800_start_rx(dev);
+ phy_start(priv->phydev);
+
+ return 0;
+
+err_free_irq:
+ free_irq(dev->irq, dev);
+err_free_dma:
+ nb8800_dma_free(dev);
+
+ return err;
+}
+
+static int nb8800_stop(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+
+ phy_stop(priv->phydev);
+
+ netif_stop_queue(dev);
+ napi_disable(&priv->napi);
+
+ nb8800_dma_stop(dev);
+ nb8800_mac_rx(dev, false);
+ nb8800_mac_tx(dev, false);
+
+ phy_disconnect(priv->phydev);
+ priv->phydev = NULL;
+
+ free_irq(dev->irq, dev);
+
+ nb8800_dma_free(dev);
+
+ return 0;
+}
+
+static int nb8800_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+
+ return phy_mii_ioctl(priv->phydev, rq, cmd);
+}
+
+static const struct net_device_ops nb8800_netdev_ops = {
+ .ndo_open = nb8800_open,
+ .ndo_stop = nb8800_stop,
+ .ndo_start_xmit = nb8800_xmit,
+ .ndo_set_mac_address = nb8800_set_mac_address,
+ .ndo_set_rx_mode = nb8800_set_rx_mode,
+ .ndo_do_ioctl = nb8800_ioctl,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static int nb8800_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+
+ if (!priv->phydev)
+ return -ENODEV;
+
+ return phy_ethtool_gset(priv->phydev, cmd);
+}
+
+static int nb8800_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+
+ if (!priv->phydev)
+ return -ENODEV;
+
+ return phy_ethtool_sset(priv->phydev, cmd);
+}
+
+static int nb8800_nway_reset(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+
+ if (!priv->phydev)
+ return -ENODEV;
+
+ return genphy_restart_aneg(priv->phydev);
+}
+
+static void nb8800_get_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *pp)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+
+ pp->autoneg = priv->pause_aneg;
+ pp->rx_pause = priv->pause_rx;
+ pp->tx_pause = priv->pause_tx;
+}
+
+static int nb8800_set_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *pp)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+
+ priv->pause_aneg = pp->autoneg;
+ priv->pause_rx = pp->rx_pause;
+ priv->pause_tx = pp->tx_pause;
+
+ nb8800_pause_adv(dev);
+
+ if (!priv->pause_aneg)
+ nb8800_pause_config(dev);
+ else if (priv->phydev)
+ phy_start_aneg(priv->phydev);
+
+ return 0;
+}
+
+static const char nb8800_stats_names[][ETH_GSTRING_LEN] = {
+ "rx_bytes_ok",
+ "rx_frames_ok",
+ "rx_undersize_frames",
+ "rx_fragment_frames",
+ "rx_64_byte_frames",
+ "rx_127_byte_frames",
+ "rx_255_byte_frames",
+ "rx_511_byte_frames",
+ "rx_1023_byte_frames",
+ "rx_max_size_frames",
+ "rx_oversize_frames",
+ "rx_bad_fcs_frames",
+ "rx_broadcast_frames",
+ "rx_multicast_frames",
+ "rx_control_frames",
+ "rx_pause_frames",
+ "rx_unsup_control_frames",
+ "rx_align_error_frames",
+ "rx_overrun_frames",
+ "rx_jabber_frames",
+ "rx_bytes",
+ "rx_frames",
+
+ "tx_bytes_ok",
+ "tx_frames_ok",
+ "tx_64_byte_frames",
+ "tx_127_byte_frames",
+ "tx_255_byte_frames",
+ "tx_511_byte_frames",
+ "tx_1023_byte_frames",
+ "tx_max_size_frames",
+ "tx_oversize_frames",
+ "tx_broadcast_frames",
+ "tx_multicast_frames",
+ "tx_control_frames",
+ "tx_pause_frames",
+ "tx_underrun_frames",
+ "tx_single_collision_frames",
+ "tx_multi_collision_frames",
+ "tx_deferred_collision_frames",
+ "tx_late_collision_frames",
+ "tx_excessive_collision_frames",
+ "tx_bytes",
+ "tx_frames",
+ "tx_collisions",
+};
+
+#define NB8800_NUM_STATS ARRAY_SIZE(nb8800_stats_names)
+
+static int nb8800_get_sset_count(struct net_device *dev, int sset)
+{
+ if (sset == ETH_SS_STATS)
+ return NB8800_NUM_STATS;
+
+ return -EOPNOTSUPP;
+}
+
+static void nb8800_get_strings(struct net_device *dev, u32 sset, u8 *buf)
+{
+ if (sset == ETH_SS_STATS)
+ memcpy(buf, &nb8800_stats_names, sizeof(nb8800_stats_names));
+}
+
+static u32 nb8800_read_stat(struct net_device *dev, int index)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+
+ nb8800_writeb(priv, NB8800_STAT_INDEX, index);
+
+ return nb8800_readl(priv, NB8800_STAT_DATA);
+}
+
+static void nb8800_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *estats, u64 *st)
+{
+ unsigned int i;
+ u32 rx, tx;
+
+ for (i = 0; i < NB8800_NUM_STATS / 2; i++) {
+ rx = nb8800_read_stat(dev, i);
+ tx = nb8800_read_stat(dev, i | 0x80);
+ st[i] = rx;
+ st[i + NB8800_NUM_STATS / 2] = tx;
+ }
+}
+
+static const struct ethtool_ops nb8800_ethtool_ops = {
+ .get_settings = nb8800_get_settings,
+ .set_settings = nb8800_set_settings,
+ .nway_reset = nb8800_nway_reset,
+ .get_link = ethtool_op_get_link,
+ .get_pauseparam = nb8800_get_pauseparam,
+ .set_pauseparam = nb8800_set_pauseparam,
+ .get_sset_count = nb8800_get_sset_count,
+ .get_strings = nb8800_get_strings,
+ .get_ethtool_stats = nb8800_get_ethtool_stats,
+};
+
+static int nb8800_hw_init(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+ u32 val;
+
+ val = TX_RETRY_EN | TX_PAD_EN | TX_APPEND_FCS;
+ nb8800_writeb(priv, NB8800_TX_CTL1, val);
+
+ /* Collision retry count */
+ nb8800_writeb(priv, NB8800_TX_CTL2, 5);
+
+ val = RX_PAD_STRIP | RX_AF_EN;
+ nb8800_writeb(priv, NB8800_RX_CTL, val);
+
+ /* Chosen by fair dice roll */
+ nb8800_writeb(priv, NB8800_RANDOM_SEED, 4);
+
+ /* TX cycles per deferral period */
+ nb8800_writeb(priv, NB8800_TX_SDP, 12);
+
+ /* The following three threshold values have been
+ * experimentally determined for good results.
+ */
+
+ /* RX/TX FIFO threshold for partial empty (64-bit entries) */
+ nb8800_writeb(priv, NB8800_PE_THRESHOLD, 0);
+
+ /* RX/TX FIFO threshold for partial full (64-bit entries) */
+ nb8800_writeb(priv, NB8800_PF_THRESHOLD, 255);
+
+ /* Buffer size for transmit (64-bit entries) */
+ nb8800_writeb(priv, NB8800_TX_BUFSIZE, 64);
+
+ /* Configure tx DMA */
+
+ val = nb8800_readl(priv, NB8800_TXC_CR);
+ val &= TCR_LE; /* keep endian setting */
+ val |= TCR_DM; /* DMA descriptor mode */
+ val |= TCR_RS; /* automatically store tx status */
+ val |= TCR_DIE; /* interrupt on DMA chain completion */
+ val |= TCR_TFI(7); /* interrupt after 7 frames transmitted */
+ val |= TCR_BTS(2); /* 32-byte bus transaction size */
+ nb8800_writel(priv, NB8800_TXC_CR, val);
+
+ /* TX complete interrupt after 10 ms or 7 frames (see above) */
+ val = clk_get_rate(priv->clk) / 100;
+ nb8800_writel(priv, NB8800_TX_ITR, val);
+
+ /* Configure rx DMA */
+
+ val = nb8800_readl(priv, NB8800_RXC_CR);
+ val &= RCR_LE; /* keep endian setting */
+ val |= RCR_DM; /* DMA descriptor mode */
+ val |= RCR_RS; /* automatically store rx status */
+ val |= RCR_DIE; /* interrupt at end of DMA chain */
+ val |= RCR_RFI(7); /* interrupt after 7 frames received */
+ val |= RCR_BTS(2); /* 32-byte bus transaction size */
+ nb8800_writel(priv, NB8800_RXC_CR, val);
+
+ /* The rx interrupt can fire before the DMA has completed
+ * unless a small delay is added. 50 us is hopefully enough.
+ */
+ priv->rx_itr_irq = clk_get_rate(priv->clk) / 20000;
+
+ /* In NAPI poll mode we want to disable interrupts, but the
+ * hardware does not permit this. Delay 10 ms instead.
+ */
+ priv->rx_itr_poll = clk_get_rate(priv->clk) / 100;
+
+ nb8800_writel(priv, NB8800_RX_ITR, priv->rx_itr_irq);
+
+ priv->rx_dma_config = RX_BUF_SIZE | DESC_BTS(2) | DESC_DS | DESC_EOF;
+
+ /* Flow control settings */
+
+ /* Pause time of 0.1 ms */
+ val = 100000 / 512;
+ nb8800_writeb(priv, NB8800_PQ1, val >> 8);
+ nb8800_writeb(priv, NB8800_PQ2, val & 0xff);
+
+ /* Auto-negotiate by default */
+ priv->pause_aneg = true;
+ priv->pause_rx = true;
+ priv->pause_tx = true;
+
+ nb8800_mc_init(dev, 0);
+
+ return 0;
+}
+
+static int nb8800_tangox_init(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+ u32 pad_mode = PAD_MODE_MII;
+
+ switch (priv->phy_mode) {
+ case PHY_INTERFACE_MODE_MII:
+ case PHY_INTERFACE_MODE_GMII:
+ pad_mode = PAD_MODE_MII;
+ break;
+
+ case PHY_INTERFACE_MODE_RGMII:
+ pad_mode = PAD_MODE_RGMII;
+ break;
+
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ pad_mode = PAD_MODE_RGMII | PAD_MODE_GTX_CLK_DELAY;
+ break;
+
+ default:
+ dev_err(dev->dev.parent, "unsupported phy mode %s\n",
+ phy_modes(priv->phy_mode));
+ return -EINVAL;
+ }
+
+ nb8800_writeb(priv, NB8800_TANGOX_PAD_MODE, pad_mode);
+
+ return 0;
+}
+
+static int nb8800_tangox_reset(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+ int clk_div;
+
+ nb8800_writeb(priv, NB8800_TANGOX_RESET, 0);
+ usleep_range(1000, 10000);
+ nb8800_writeb(priv, NB8800_TANGOX_RESET, 1);
+
+ wmb(); /* ensure reset is cleared before proceeding */
+
+ clk_div = DIV_ROUND_UP(clk_get_rate(priv->clk), 2 * MAX_MDC_CLOCK);
+ nb8800_writew(priv, NB8800_TANGOX_MDIO_CLKDIV, clk_div);
+
+ return 0;
+}
+
+static const struct nb8800_ops nb8800_tangox_ops = {
+ .init = nb8800_tangox_init,
+ .reset = nb8800_tangox_reset,
+};
+
+static int nb8800_tango4_init(struct net_device *dev)
+{
+ struct nb8800_priv *priv = netdev_priv(dev);
+ int err;
+
+ err = nb8800_tangox_init(dev);
+ if (err)
+ return err;
+
+ /* On tango4 interrupt on DMA completion per frame works and gives
+ * better performance despite generating more rx interrupts.
+ */
+
+ /* Disable unnecessary interrupt on rx completion */
+ nb8800_clearl(priv, NB8800_RXC_CR, RCR_RFI(7));
+
+ /* Request interrupt on descriptor DMA completion */
+ priv->rx_dma_config |= DESC_ID;
+
+ return 0;
+}
+
+static const struct nb8800_ops nb8800_tango4_ops = {
+ .init = nb8800_tango4_init,
+ .reset = nb8800_tangox_reset,
+};
+
+static const struct of_device_id nb8800_dt_ids[] = {
+ {
+ .compatible = "aurora,nb8800",
+ },
+ {
+ .compatible = "sigma,smp8642-ethernet",
+ .data = &nb8800_tangox_ops,
+ },
+ {
+ .compatible = "sigma,smp8734-ethernet",
+ .data = &nb8800_tango4_ops,
+ },
+ { }
+};
+
+static int nb8800_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ const struct nb8800_ops *ops = NULL;
+ struct nb8800_priv *priv;
+ struct resource *res;
+ struct net_device *dev;
+ struct mii_bus *bus;
+ const unsigned char *mac;
+ void __iomem *base;
+ int irq;
+ int ret;
+
+ match = of_match_device(nb8800_dt_ids, &pdev->dev);
+ if (match)
+ ops = match->data;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(&pdev->dev, "No IRQ\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ dev_dbg(&pdev->dev, "AU-NB8800 Ethernet at %pa\n", &res->start);
+
+ dev = alloc_etherdev(sizeof(*priv));
+ if (!dev)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ priv = netdev_priv(dev);
+ priv->base = base;
+
+ priv->phy_mode = of_get_phy_mode(pdev->dev.of_node);
+ if (priv->phy_mode < 0)
+ priv->phy_mode = PHY_INTERFACE_MODE_RGMII;
+
+ priv->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ ret = PTR_ERR(priv->clk);
+ goto err_free_dev;
+ }
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ goto err_free_dev;
+
+ spin_lock_init(&priv->tx_lock);
+
+ if (ops && ops->reset) {
+ ret = ops->reset(dev);
+ if (ret)
+ goto err_free_dev;
+ }
+
+ bus = devm_mdiobus_alloc(&pdev->dev);
+ if (!bus) {
+ ret = -ENOMEM;
+ goto err_disable_clk;
+ }
+
+ bus->name = "nb8800-mii";
+ bus->read = nb8800_mdio_read;
+ bus->write = nb8800_mdio_write;
+ bus->parent = &pdev->dev;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%lx.nb8800-mii",
+ (unsigned long)res->start);
+ bus->priv = priv;
+
+ ret = of_mdiobus_register(bus, pdev->dev.of_node);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register MII bus\n");
+ goto err_disable_clk;
+ }
+
+ priv->phy_node = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
+ if (!priv->phy_node) {
+ dev_err(&pdev->dev, "no PHY specified\n");
+ ret = -ENODEV;
+ goto err_free_bus;
+ }
+
+ priv->mii_bus = bus;
+
+ ret = nb8800_hw_init(dev);
+ if (ret)
+ goto err_free_bus;
+
+ if (ops && ops->init) {
+ ret = ops->init(dev);
+ if (ret)
+ goto err_free_bus;
+ }
+
+ dev->netdev_ops = &nb8800_netdev_ops;
+ dev->ethtool_ops = &nb8800_ethtool_ops;
+ dev->flags |= IFF_MULTICAST;
+ dev->irq = irq;
+
+ mac = of_get_mac_address(pdev->dev.of_node);
+ if (mac)
+ ether_addr_copy(dev->dev_addr, mac);
+
+ if (!is_valid_ether_addr(dev->dev_addr))
+ eth_hw_addr_random(dev);
+
+ nb8800_update_mac_addr(dev);
+
+ netif_carrier_off(dev);
+
+ ret = register_netdev(dev);
+ if (ret) {
+ netdev_err(dev, "failed to register netdev\n");
+ goto err_free_dma;
+ }
+
+ netif_napi_add(dev, &priv->napi, nb8800_poll, NAPI_POLL_WEIGHT);
+
+ netdev_info(dev, "MAC address %pM\n", dev->dev_addr);
+
+ return 0;
+
+err_free_dma:
+ nb8800_dma_free(dev);
+err_free_bus:
+ mdiobus_unregister(bus);
+err_disable_clk:
+ clk_disable_unprepare(priv->clk);
+err_free_dev:
+ free_netdev(dev);
+
+ return ret;
+}
+
+static int nb8800_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct nb8800_priv *priv = netdev_priv(ndev);
+
+ unregister_netdev(ndev);
+
+ mdiobus_unregister(priv->mii_bus);
+
+ clk_disable_unprepare(priv->clk);
+
+ nb8800_dma_free(ndev);
+ free_netdev(ndev);
+
+ return 0;
+}
+
+static struct platform_driver nb8800_driver = {
+ .driver = {
+ .name = "nb8800",
+ .of_match_table = nb8800_dt_ids,
+ },
+ .probe = nb8800_probe,
+ .remove = nb8800_remove,
+};
+
+module_platform_driver(nb8800_driver);
+
+MODULE_DESCRIPTION("Aurora AU-NB8800 Ethernet driver");
+MODULE_AUTHOR("Mans Rullgard <mans@mansr.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/aurora/nb8800.h b/drivers/net/ethernet/aurora/nb8800.h
new file mode 100644
index 000000000000..e5adbc2aac9f
--- /dev/null
+++ b/drivers/net/ethernet/aurora/nb8800.h
@@ -0,0 +1,316 @@
+#ifndef _NB8800_H_
+#define _NB8800_H_
+
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/phy.h>
+#include <linux/clk.h>
+#include <linux/bitops.h>
+
+#define RX_DESC_COUNT 256
+#define TX_DESC_COUNT 256
+
+#define NB8800_DESC_LOW 4
+
+#define RX_BUF_SIZE 1552
+
+#define RX_COPYBREAK 256
+#define RX_COPYHDR 128
+
+#define MAX_MDC_CLOCK 2500000
+
+/* Stargate Solutions SSN8800 core registers */
+#define NB8800_TX_CTL1 0x000
+#define TX_TPD BIT(5)
+#define TX_APPEND_FCS BIT(4)
+#define TX_PAD_EN BIT(3)
+#define TX_RETRY_EN BIT(2)
+#define TX_EN BIT(0)
+
+#define NB8800_TX_CTL2 0x001
+
+#define NB8800_RX_CTL 0x004
+#define RX_BC_DISABLE BIT(7)
+#define RX_RUNT BIT(6)
+#define RX_AF_EN BIT(5)
+#define RX_PAUSE_EN BIT(3)
+#define RX_SEND_CRC BIT(2)
+#define RX_PAD_STRIP BIT(1)
+#define RX_EN BIT(0)
+
+#define NB8800_RANDOM_SEED 0x008
+#define NB8800_TX_SDP 0x14
+#define NB8800_TX_TPDP1 0x18
+#define NB8800_TX_TPDP2 0x19
+#define NB8800_SLOT_TIME 0x1c
+
+#define NB8800_MDIO_CMD 0x020
+#define MDIO_CMD_GO BIT(31)
+#define MDIO_CMD_WR BIT(26)
+#define MDIO_CMD_ADDR(x) ((x) << 21)
+#define MDIO_CMD_REG(x) ((x) << 16)
+#define MDIO_CMD_DATA(x) ((x) << 0)
+
+#define NB8800_MDIO_STS 0x024
+#define MDIO_STS_ERR BIT(31)
+
+#define NB8800_MC_ADDR(i) (0x028 + (i))
+#define NB8800_MC_INIT 0x02e
+#define NB8800_UC_ADDR(i) (0x03c + (i))
+
+#define NB8800_MAC_MODE 0x044
+#define RGMII_MODE BIT(7)
+#define HALF_DUPLEX BIT(4)
+#define BURST_EN BIT(3)
+#define LOOPBACK_EN BIT(2)
+#define GMAC_MODE BIT(0)
+
+#define NB8800_IC_THRESHOLD 0x050
+#define NB8800_PE_THRESHOLD 0x051
+#define NB8800_PF_THRESHOLD 0x052
+#define NB8800_TX_BUFSIZE 0x054
+#define NB8800_FIFO_CTL 0x056
+#define NB8800_PQ1 0x060
+#define NB8800_PQ2 0x061
+#define NB8800_SRC_ADDR(i) (0x06a + (i))
+#define NB8800_STAT_DATA 0x078
+#define NB8800_STAT_INDEX 0x07c
+#define NB8800_STAT_CLEAR 0x07d
+
+#define NB8800_SLEEP_MODE 0x07e
+#define SLEEP_MODE BIT(0)
+
+#define NB8800_WAKEUP 0x07f
+#define WAKEUP BIT(0)
+
+/* Aurora NB8800 host interface registers */
+#define NB8800_TXC_CR 0x100
+#define TCR_LK BIT(12)
+#define TCR_DS BIT(11)
+#define TCR_BTS(x) (((x) & 0x7) << 8)
+#define TCR_DIE BIT(7)
+#define TCR_TFI(x) (((x) & 0x7) << 4)
+#define TCR_LE BIT(3)
+#define TCR_RS BIT(2)
+#define TCR_DM BIT(1)
+#define TCR_EN BIT(0)
+
+#define NB8800_TXC_SR 0x104
+#define TSR_DE BIT(3)
+#define TSR_DI BIT(2)
+#define TSR_TO BIT(1)
+#define TSR_TI BIT(0)
+
+#define NB8800_TX_SAR 0x108
+#define NB8800_TX_DESC_ADDR 0x10c
+
+#define NB8800_TX_REPORT_ADDR 0x110
+#define TX_BYTES_TRANSFERRED(x) (((x) >> 16) & 0xffff)
+#define TX_FIRST_DEFERRAL BIT(7)
+#define TX_EARLY_COLLISIONS(x) (((x) >> 3) & 0xf)
+#define TX_LATE_COLLISION BIT(2)
+#define TX_PACKET_DROPPED BIT(1)
+#define TX_FIFO_UNDERRUN BIT(0)
+#define IS_TX_ERROR(r) ((r) & 0x07)
+
+#define NB8800_TX_FIFO_SR 0x114
+#define NB8800_TX_ITR 0x118
+
+#define NB8800_RXC_CR 0x200
+#define RCR_FL BIT(13)
+#define RCR_LK BIT(12)
+#define RCR_DS BIT(11)
+#define RCR_BTS(x) (((x) & 7) << 8)
+#define RCR_DIE BIT(7)
+#define RCR_RFI(x) (((x) & 7) << 4)
+#define RCR_LE BIT(3)
+#define RCR_RS BIT(2)
+#define RCR_DM BIT(1)
+#define RCR_EN BIT(0)
+
+#define NB8800_RXC_SR 0x204
+#define RSR_DE BIT(3)
+#define RSR_DI BIT(2)
+#define RSR_RO BIT(1)
+#define RSR_RI BIT(0)
+
+#define NB8800_RX_SAR 0x208
+#define NB8800_RX_DESC_ADDR 0x20c
+
+#define NB8800_RX_REPORT_ADDR 0x210
+#define RX_BYTES_TRANSFERRED(x) (((x) >> 16) & 0xFFFF)
+#define RX_MULTICAST_PKT BIT(9)
+#define RX_BROADCAST_PKT BIT(8)
+#define RX_LENGTH_ERR BIT(7)
+#define RX_FCS_ERR BIT(6)
+#define RX_RUNT_PKT BIT(5)
+#define RX_FIFO_OVERRUN BIT(4)
+#define RX_LATE_COLLISION BIT(3)
+#define RX_ALIGNMENT_ERROR BIT(2)
+#define RX_ERROR_MASK 0xfc
+#define IS_RX_ERROR(r) ((r) & RX_ERROR_MASK)
+
+#define NB8800_RX_FIFO_SR 0x214
+#define NB8800_RX_ITR 0x218
+
+/* Sigma Designs SMP86xx additional registers */
+#define NB8800_TANGOX_PAD_MODE 0x400
+#define PAD_MODE_MASK 0x7
+#define PAD_MODE_MII 0x0
+#define PAD_MODE_RGMII 0x1
+#define PAD_MODE_GTX_CLK_INV BIT(3)
+#define PAD_MODE_GTX_CLK_DELAY BIT(4)
+
+#define NB8800_TANGOX_MDIO_CLKDIV 0x420
+#define NB8800_TANGOX_RESET 0x424
+
+/* Hardware DMA descriptor */
+struct nb8800_dma_desc {
+ u32 s_addr; /* start address */
+ u32 n_addr; /* next descriptor address */
+ u32 r_addr; /* report address */
+ u32 config;
+} __aligned(8);
+
+#define DESC_ID BIT(23)
+#define DESC_EOC BIT(22)
+#define DESC_EOF BIT(21)
+#define DESC_LK BIT(20)
+#define DESC_DS BIT(19)
+#define DESC_BTS(x) (((x) & 0x7) << 16)
+
+/* DMA descriptor and associated data for rx.
+ * Allocated from coherent memory.
+ */
+struct nb8800_rx_desc {
+ /* DMA descriptor */
+ struct nb8800_dma_desc desc;
+
+ /* Status report filled in by hardware */
+ u32 report;
+};
+
+/* Address of buffer on rx ring */
+struct nb8800_rx_buf {
+ struct page *page;
+ unsigned long offset;
+};
+
+/* DMA descriptors and associated data for tx.
+ * Allocated from coherent memory.
+ */
+struct nb8800_tx_desc {
+ /* DMA descriptor. The second descriptor is used if packet
+ * data is unaligned.
+ */
+ struct nb8800_dma_desc desc[2];
+
+ /* Status report filled in by hardware */
+ u32 report;
+
+ /* Bounce buffer for initial unaligned part of packet */
+ u8 buf[8] __aligned(8);
+};
+
+/* Packet in tx queue */
+struct nb8800_tx_buf {
+ /* Currently queued skb */
+ struct sk_buff *skb;
+
+ /* DMA address of the first descriptor */
+ dma_addr_t dma_desc;
+
+ /* DMA address of packet data */
+ dma_addr_t dma_addr;
+
+ /* Length of DMA mapping, less than skb->len if alignment
+ * buffer is used.
+ */
+ unsigned int dma_len;
+
+ /* Number of packets in chain starting here */
+ unsigned int chain_len;
+
+ /* Packet chain ready to be submitted to hardware */
+ bool ready;
+};
+
+struct nb8800_priv {
+ struct napi_struct napi;
+
+ void __iomem *base;
+
+ /* RX DMA descriptors */
+ struct nb8800_rx_desc *rx_descs;
+
+ /* RX buffers referenced by DMA descriptors */
+ struct nb8800_rx_buf *rx_bufs;
+
+ /* Current end of chain */
+ u32 rx_eoc;
+
+ /* Value for rx interrupt time register in NAPI interrupt mode */
+ u32 rx_itr_irq;
+
+ /* Value for rx interrupt time register in NAPI poll mode */
+ u32 rx_itr_poll;
+
+ /* Value for config field of rx DMA descriptors */
+ u32 rx_dma_config;
+
+ /* TX DMA descriptors */
+ struct nb8800_tx_desc *tx_descs;
+
+ /* TX packet queue */
+ struct nb8800_tx_buf *tx_bufs;
+
+ /* Number of free tx queue entries */
+ atomic_t tx_free;
+
+ /* First free tx queue entry */
+ u32 tx_next;
+
+ /* Next buffer to transmit */
+ u32 tx_queue;
+
+ /* Start of current packet chain */
+ struct nb8800_tx_buf *tx_chain;
+
+ /* Next buffer to reclaim */
+ u32 tx_done;
+
+ /* Lock for DMA activation */
+ spinlock_t tx_lock;
+
+ struct mii_bus *mii_bus;
+ struct device_node *phy_node;
+ struct phy_device *phydev;
+
+ /* PHY connection type from DT */
+ int phy_mode;
+
+ /* Current link status */
+ int speed;
+ int duplex;
+ int link;
+
+ /* Pause settings */
+ bool pause_aneg;
+ bool pause_rx;
+ bool pause_tx;
+
+ /* DMA base address of rx descriptors, see rx_descs above */
+ dma_addr_t rx_desc_dma;
+
+ /* DMA base address of tx descriptors, see tx_descs above */
+ dma_addr_t tx_desc_dma;
+
+ struct clk *clk;
+};
+
+struct nb8800_ops {
+ int (*init)(struct net_device *dev);
+ int (*reset)(struct net_device *dev);
+};
+
+#endif /* _NB8800_H_ */
diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c
index a3b1c07ae0af..74f0a37c4eb6 100644
--- a/drivers/net/ethernet/broadcom/b44.c
+++ b/drivers/net/ethernet/broadcom/b44.c
@@ -2263,24 +2263,16 @@ static int b44_register_phy_one(struct b44 *bp)
mii_bus->parent = sdev->dev;
mii_bus->phy_mask = ~(1 << bp->phy_addr);
snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%x", instance);
- mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!mii_bus->irq) {
- dev_err(sdev->dev, "mii_bus irq allocation failed\n");
- err = -ENOMEM;
- goto err_out_mdiobus;
- }
-
- memset(mii_bus->irq, PHY_POLL, sizeof(int) * PHY_MAX_ADDR);
bp->mii_bus = mii_bus;
err = mdiobus_register(mii_bus);
if (err) {
dev_err(sdev->dev, "failed to register MII bus\n");
- goto err_out_mdiobus_irq;
+ goto err_out_mdiobus;
}
- if (!bp->mii_bus->phy_map[bp->phy_addr] &&
+ if (!mdiobus_is_registered_device(bp->mii_bus, bp->phy_addr) &&
(sprom->boardflags_lo & (B44_BOARDFLAG_ROBO | B44_BOARDFLAG_ADM))) {
dev_info(sdev->dev,
@@ -2313,19 +2305,15 @@ static int b44_register_phy_one(struct b44 *bp)
bp->phydev = phydev;
bp->old_link = 0;
- bp->phy_addr = phydev->addr;
+ bp->phy_addr = phydev->mdio.addr;
- dev_info(sdev->dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s)\n",
- phydev->drv->name, dev_name(&phydev->dev));
+ phy_attached_info(phydev);
return 0;
err_out_mdiobus_unregister:
mdiobus_unregister(mii_bus);
-err_out_mdiobus_irq:
- kfree(mii_bus->irq);
-
err_out_mdiobus:
mdiobus_free(mii_bus);
@@ -2339,7 +2327,6 @@ static void b44_unregister_phy_one(struct b44 *bp)
phy_disconnect(bp->phydev);
mdiobus_unregister(mii_bus);
- kfree(mii_bus->irq);
mdiobus_free(mii_bus);
}
diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c
index 8b1929e9f698..87c6b5bdd616 100644
--- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c
+++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c
@@ -908,8 +908,7 @@ static int bcm_enet_open(struct net_device *dev)
else
phydev->advertising &= ~SUPPORTED_Pause;
- dev_info(kdev, "attached PHY at address %d [%s]\n",
- phydev->addr, phydev->drv->name);
+ phy_attached_info(phydev);
priv->old_link = 0;
priv->old_duplex = -1;
@@ -1849,17 +1848,8 @@ static int bcm_enet_probe(struct platform_device *pdev)
* if a slave is not present on hw */
bus->phy_mask = ~(1 << priv->phy_id);
- bus->irq = devm_kzalloc(&pdev->dev, sizeof(int) * PHY_MAX_ADDR,
- GFP_KERNEL);
- if (!bus->irq) {
- ret = -ENOMEM;
- goto out_free_mdio;
- }
-
if (priv->has_phy_interrupt)
bus->irq[priv->phy_id] = priv->phy_interrupt;
- else
- bus->irq[priv->phy_id] = PHY_POLL;
ret = mdiobus_register(bus);
if (ret) {
@@ -2884,33 +2874,21 @@ struct platform_driver bcm63xx_enet_shared_driver = {
},
};
+static struct platform_driver * const drivers[] = {
+ &bcm63xx_enet_shared_driver,
+ &bcm63xx_enet_driver,
+ &bcm63xx_enetsw_driver,
+};
+
/* entry point */
static int __init bcm_enet_init(void)
{
- int ret;
-
- ret = platform_driver_register(&bcm63xx_enet_shared_driver);
- if (ret)
- return ret;
-
- ret = platform_driver_register(&bcm63xx_enet_driver);
- if (ret)
- platform_driver_unregister(&bcm63xx_enet_shared_driver);
-
- ret = platform_driver_register(&bcm63xx_enetsw_driver);
- if (ret) {
- platform_driver_unregister(&bcm63xx_enet_driver);
- platform_driver_unregister(&bcm63xx_enet_shared_driver);
- }
-
- return ret;
+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
}
static void __exit bcm_enet_exit(void)
{
- platform_driver_unregister(&bcm63xx_enet_driver);
- platform_driver_unregister(&bcm63xx_enetsw_driver);
- platform_driver_unregister(&bcm63xx_enet_shared_driver);
+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
}
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c
index 858106352ce9..993c780bdfab 100644
--- a/drivers/net/ethernet/broadcom/bcmsysport.c
+++ b/drivers/net/ethernet/broadcom/bcmsysport.c
@@ -1216,7 +1216,7 @@ static int bcm_sysport_init_tx_ring(struct bcm_sysport_priv *priv,
/* Initialize SW view of the ring */
spin_lock_init(&ring->lock);
ring->priv = priv;
- netif_napi_add(priv->netdev, &ring->napi, bcm_sysport_tx_poll, 64);
+ netif_tx_napi_add(priv->netdev, &ring->napi, bcm_sysport_tx_poll, 64);
ring->index = index;
ring->size = size;
ring->alloc_size = ring->size;
diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c
index 28f7610b03fe..c7798d360512 100644
--- a/drivers/net/ethernet/broadcom/bgmac.c
+++ b/drivers/net/ethernet/broadcom/bgmac.c
@@ -1471,7 +1471,7 @@ static int bgmac_mii_register(struct bgmac *bgmac)
struct mii_bus *mii_bus;
struct phy_device *phy_dev;
char bus_id[MII_BUS_ID_SIZE + 3];
- int i, err = 0;
+ int err = 0;
if (ci->id == BCMA_CHIP_ID_BCM4707 ||
ci->id == BCMA_CHIP_ID_BCM53018)
@@ -1490,18 +1490,10 @@ static int bgmac_mii_register(struct bgmac *bgmac)
mii_bus->parent = &bgmac->core->dev;
mii_bus->phy_mask = ~(1 << bgmac->phyaddr);
- mii_bus->irq = kmalloc_array(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
- if (!mii_bus->irq) {
- err = -ENOMEM;
- goto err_free_bus;
- }
- for (i = 0; i < PHY_MAX_ADDR; i++)
- mii_bus->irq[i] = PHY_POLL;
-
err = mdiobus_register(mii_bus);
if (err) {
bgmac_err(bgmac, "Registration of mii bus failed\n");
- goto err_free_irq;
+ goto err_free_bus;
}
bgmac->mii_bus = mii_bus;
@@ -1522,8 +1514,6 @@ static int bgmac_mii_register(struct bgmac *bgmac)
err_unregister_bus:
mdiobus_unregister(mii_bus);
-err_free_irq:
- kfree(mii_bus->irq);
err_free_bus:
mdiobus_free(mii_bus);
return err;
@@ -1534,7 +1524,6 @@ static void bgmac_mii_unregister(struct bgmac *bgmac)
struct mii_bus *mii_bus = bgmac->mii_bus;
mdiobus_unregister(mii_bus);
- kfree(mii_bus->irq);
mdiobus_free(mii_bus);
}
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
index b5e64b02200c..cae0956186ce 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
@@ -540,10 +540,6 @@ struct bnx2x_fastpath {
struct napi_struct napi;
-#ifdef CONFIG_NET_RX_BUSY_POLL
- unsigned long busy_poll_state;
-#endif
-
union host_hc_status_block status_blk;
/* chip independent shortcuts into sb structure */
__le16 *sb_index_values;
@@ -594,8 +590,6 @@ struct bnx2x_fastpath {
/* The last maximal completed SGE */
u16 last_max_sge;
__le16 *rx_cons_sb;
- unsigned long rx_pkt,
- rx_calls;
/* TPA related */
struct bnx2x_agg_info *tpa_info;
@@ -617,115 +611,6 @@ struct bnx2x_fastpath {
#define bnx2x_fp_stats(bp, fp) (&((bp)->fp_stats[(fp)->index]))
#define bnx2x_fp_qstats(bp, fp) (&((bp)->fp_stats[(fp)->index].eth_q_stats))
-#ifdef CONFIG_NET_RX_BUSY_POLL
-
-enum bnx2x_fp_state {
- BNX2X_STATE_FP_NAPI = BIT(0), /* NAPI handler owns the queue */
-
- BNX2X_STATE_FP_NAPI_REQ_BIT = 1, /* NAPI would like to own the queue */
- BNX2X_STATE_FP_NAPI_REQ = BIT(1),
-
- BNX2X_STATE_FP_POLL_BIT = 2,
- BNX2X_STATE_FP_POLL = BIT(2), /* busy_poll owns the queue */
-
- BNX2X_STATE_FP_DISABLE_BIT = 3, /* queue is dismantled */
-};
-
-static inline void bnx2x_fp_busy_poll_init(struct bnx2x_fastpath *fp)
-{
- WRITE_ONCE(fp->busy_poll_state, 0);
-}
-
-/* called from the device poll routine to get ownership of a FP */
-static inline bool bnx2x_fp_lock_napi(struct bnx2x_fastpath *fp)
-{
- unsigned long prev, old = READ_ONCE(fp->busy_poll_state);
-
- while (1) {
- switch (old) {
- case BNX2X_STATE_FP_POLL:
- /* make sure bnx2x_fp_lock_poll() wont starve us */
- set_bit(BNX2X_STATE_FP_NAPI_REQ_BIT,
- &fp->busy_poll_state);
- /* fallthrough */
- case BNX2X_STATE_FP_POLL | BNX2X_STATE_FP_NAPI_REQ:
- return false;
- default:
- break;
- }
- prev = cmpxchg(&fp->busy_poll_state, old, BNX2X_STATE_FP_NAPI);
- if (unlikely(prev != old)) {
- old = prev;
- continue;
- }
- return true;
- }
-}
-
-static inline void bnx2x_fp_unlock_napi(struct bnx2x_fastpath *fp)
-{
- smp_wmb();
- fp->busy_poll_state = 0;
-}
-
-/* called from bnx2x_low_latency_poll() */
-static inline bool bnx2x_fp_lock_poll(struct bnx2x_fastpath *fp)
-{
- return cmpxchg(&fp->busy_poll_state, 0, BNX2X_STATE_FP_POLL) == 0;
-}
-
-static inline void bnx2x_fp_unlock_poll(struct bnx2x_fastpath *fp)
-{
- smp_mb__before_atomic();
- clear_bit(BNX2X_STATE_FP_POLL_BIT, &fp->busy_poll_state);
-}
-
-/* true if a socket is polling */
-static inline bool bnx2x_fp_ll_polling(struct bnx2x_fastpath *fp)
-{
- return READ_ONCE(fp->busy_poll_state) & BNX2X_STATE_FP_POLL;
-}
-
-/* false if fp is currently owned */
-static inline bool bnx2x_fp_ll_disable(struct bnx2x_fastpath *fp)
-{
- set_bit(BNX2X_STATE_FP_DISABLE_BIT, &fp->busy_poll_state);
- return !bnx2x_fp_ll_polling(fp);
-
-}
-#else
-static inline void bnx2x_fp_busy_poll_init(struct bnx2x_fastpath *fp)
-{
-}
-
-static inline bool bnx2x_fp_lock_napi(struct bnx2x_fastpath *fp)
-{
- return true;
-}
-
-static inline void bnx2x_fp_unlock_napi(struct bnx2x_fastpath *fp)
-{
-}
-
-static inline bool bnx2x_fp_lock_poll(struct bnx2x_fastpath *fp)
-{
- return false;
-}
-
-static inline void bnx2x_fp_unlock_poll(struct bnx2x_fastpath *fp)
-{
-}
-
-static inline bool bnx2x_fp_ll_polling(struct bnx2x_fastpath *fp)
-{
- return false;
-}
-static inline bool bnx2x_fp_ll_disable(struct bnx2x_fastpath *fp)
-{
- return true;
-}
-#endif /* CONFIG_NET_RX_BUSY_POLL */
-
/* Use 2500 as a mini-jumbo MTU for FCoE */
#define BNX2X_FCOE_MINI_JUMBO_MTU 2500
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
index f8d7a2f06950..9695a4c4a434 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
@@ -46,7 +46,6 @@ static void bnx2x_add_all_napi_cnic(struct bnx2x *bp)
for_each_rx_queue_cnic(bp, i) {
netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi),
bnx2x_poll, NAPI_POLL_WEIGHT);
- napi_hash_add(&bnx2x_fp(bp, i, napi));
}
}
@@ -58,7 +57,6 @@ static void bnx2x_add_all_napi(struct bnx2x *bp)
for_each_eth_queue(bp, i) {
netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi),
bnx2x_poll, NAPI_POLL_WEIGHT);
- napi_hash_add(&bnx2x_fp(bp, i, napi));
}
}
@@ -560,10 +558,8 @@ static int bnx2x_alloc_rx_sge(struct bnx2x *bp, struct bnx2x_fastpath *fp,
put_page(pool->page);
pool->page = alloc_pages(gfp_mask, PAGES_PER_SGE_SHIFT);
- if (unlikely(!pool->page)) {
- BNX2X_ERR("Can't alloc sge\n");
+ if (unlikely(!pool->page))
return -ENOMEM;
- }
pool->offset = 0;
}
@@ -747,7 +743,7 @@ static void bnx2x_gro_receive(struct bnx2x *bp, struct bnx2x_fastpath *fp,
bnx2x_gro_csum(bp, skb, bnx2x_gro_ipv6_csum);
break;
default:
- BNX2X_ERR("Error: FW GRO supports only IPv4/IPv6, not 0x%04x\n",
+ WARN_ONCE(1, "Error: FW GRO supports only IPv4/IPv6, not 0x%04x\n",
be16_to_cpu(skb->protocol));
}
}
@@ -1094,12 +1090,7 @@ reuse_rx:
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
le16_to_cpu(cqe_fp->vlan_tag));
- skb_mark_napi_id(skb, &fp->napi);
-
- if (bnx2x_fp_ll_polling(fp))
- netif_receive_skb(skb);
- else
- napi_gro_receive(&fp->napi, skb);
+ napi_gro_receive(&fp->napi, skb);
next_rx:
rx_buf->data = NULL;
@@ -1131,9 +1122,6 @@ next_cqe:
bnx2x_update_rx_prod(bp, fp, bd_prod_fw, sw_comp_prod,
fp->rx_sge_prod);
- fp->rx_pkt += rx_pkt;
- fp->rx_calls++;
-
return rx_pkt;
}
@@ -1869,7 +1857,6 @@ static void bnx2x_napi_enable_cnic(struct bnx2x *bp)
int i;
for_each_rx_queue_cnic(bp, i) {
- bnx2x_fp_busy_poll_init(&bp->fp[i]);
napi_enable(&bnx2x_fp(bp, i, napi));
}
}
@@ -1879,7 +1866,6 @@ static void bnx2x_napi_enable(struct bnx2x *bp)
int i;
for_each_eth_queue(bp, i) {
- bnx2x_fp_busy_poll_init(&bp->fp[i]);
napi_enable(&bnx2x_fp(bp, i, napi));
}
}
@@ -1890,8 +1876,6 @@ static void bnx2x_napi_disable_cnic(struct bnx2x *bp)
for_each_rx_queue_cnic(bp, i) {
napi_disable(&bnx2x_fp(bp, i, napi));
- while (!bnx2x_fp_ll_disable(&bp->fp[i]))
- usleep_range(1000, 2000);
}
}
@@ -1901,8 +1885,6 @@ static void bnx2x_napi_disable(struct bnx2x *bp)
for_each_eth_queue(bp, i) {
napi_disable(&bnx2x_fp(bp, i, napi));
- while (!bnx2x_fp_ll_disable(&bp->fp[i]))
- usleep_range(1000, 2000);
}
}
@@ -3219,49 +3201,32 @@ int bnx2x_set_power_state(struct bnx2x *bp, pci_power_t state)
*/
static int bnx2x_poll(struct napi_struct *napi, int budget)
{
- int work_done = 0;
- u8 cos;
struct bnx2x_fastpath *fp = container_of(napi, struct bnx2x_fastpath,
napi);
struct bnx2x *bp = fp->bp;
+ int rx_work_done;
+ u8 cos;
- while (1) {
#ifdef BNX2X_STOP_ON_ERROR
- if (unlikely(bp->panic)) {
- napi_complete(napi);
- return 0;
- }
+ if (unlikely(bp->panic)) {
+ napi_complete(napi);
+ return 0;
+ }
#endif
- if (!bnx2x_fp_lock_napi(fp))
- return budget;
-
- for_each_cos_in_tx_queue(fp, cos)
- if (bnx2x_tx_queue_has_work(fp->txdata_ptr[cos]))
- bnx2x_tx_int(bp, fp->txdata_ptr[cos]);
-
- if (bnx2x_has_rx_work(fp)) {
- work_done += bnx2x_rx_int(fp, budget - work_done);
-
- /* must not complete if we consumed full budget */
- if (work_done >= budget) {
- bnx2x_fp_unlock_napi(fp);
- break;
- }
- }
-
- bnx2x_fp_unlock_napi(fp);
+ for_each_cos_in_tx_queue(fp, cos)
+ if (bnx2x_tx_queue_has_work(fp->txdata_ptr[cos]))
+ bnx2x_tx_int(bp, fp->txdata_ptr[cos]);
- /* Fall out from the NAPI loop if needed */
- if (!(bnx2x_has_rx_work(fp) || bnx2x_has_tx_work(fp))) {
+ rx_work_done = (bnx2x_has_rx_work(fp)) ? bnx2x_rx_int(fp, budget) : 0;
- /* No need to update SB for FCoE L2 ring as long as
- * it's connected to the default SB and the SB
- * has been updated when NAPI was scheduled.
- */
- if (IS_FCOE_FP(fp)) {
- napi_complete(napi);
- break;
- }
+ if (rx_work_done < budget) {
+ /* No need to update SB for FCoE L2 ring as long as
+ * it's connected to the default SB and the SB
+ * has been updated when NAPI was scheduled.
+ */
+ if (IS_FCOE_FP(fp)) {
+ napi_complete(napi);
+ } else {
bnx2x_update_fpsb_idx(fp);
/* bnx2x_has_rx_work() reads the status block,
* thus we need to ensure that status block indices
@@ -3286,40 +3251,15 @@ static int bnx2x_poll(struct napi_struct *napi, int budget)
bnx2x_ack_sb(bp, fp->igu_sb_id, USTORM_ID,
le16_to_cpu(fp->fp_hc_idx),
IGU_INT_ENABLE, 1);
- break;
+ } else {
+ rx_work_done = budget;
}
}
}
- return work_done;
+ return rx_work_done;
}
-#ifdef CONFIG_NET_RX_BUSY_POLL
-/* must be called with local_bh_disable()d */
-int bnx2x_low_latency_recv(struct napi_struct *napi)
-{
- struct bnx2x_fastpath *fp = container_of(napi, struct bnx2x_fastpath,
- napi);
- struct bnx2x *bp = fp->bp;
- int found = 0;
-
- if ((bp->state == BNX2X_STATE_CLOSED) ||
- (bp->state == BNX2X_STATE_ERROR) ||
- (bp->dev->features & (NETIF_F_LRO | NETIF_F_GRO)))
- return LL_FLUSH_FAILED;
-
- if (!bnx2x_fp_lock_poll(fp))
- return LL_FLUSH_BUSY;
-
- if (bnx2x_has_rx_work(fp))
- found = bnx2x_rx_int(fp, 4);
-
- bnx2x_fp_unlock_poll(fp);
-
- return found;
-}
-#endif
-
/* we split the first BD into headers and data BDs
* to ease the pain of our fellow microcode engineers
* we use one mapping for both BDs
@@ -3430,25 +3370,29 @@ static u32 bnx2x_xmit_type(struct bnx2x *bp, struct sk_buff *skb)
return rc;
}
-#if (MAX_SKB_FRAGS >= MAX_FETCH_BD - 3)
+/* VXLAN: 4 = 1 (for linear data BD) + 3 (2 for PBD and last BD) */
+#define BNX2X_NUM_VXLAN_TSO_WIN_SUB_BDS 4
+
+/* Regular: 3 = 1 (for linear data BD) + 2 (for PBD and last BD) */
+#define BNX2X_NUM_TSO_WIN_SUB_BDS 3
+
+#if (MAX_SKB_FRAGS >= MAX_FETCH_BD - BDS_PER_TX_PKT)
/* check if packet requires linearization (packet is too fragmented)
no need to check fragmentation if page size > 8K (there will be no
violation to FW restrictions) */
static int bnx2x_pkt_req_lin(struct bnx2x *bp, struct sk_buff *skb,
u32 xmit_type)
{
- int to_copy = 0;
- int hlen = 0;
- int first_bd_sz = 0;
+ int first_bd_sz = 0, num_tso_win_sub = BNX2X_NUM_TSO_WIN_SUB_BDS;
+ int to_copy = 0, hlen = 0;
- /* 3 = 1 (for linear data BD) + 2 (for PBD and last BD) */
- if (skb_shinfo(skb)->nr_frags >= (MAX_FETCH_BD - 3)) {
+ if (xmit_type & XMIT_GSO_ENC)
+ num_tso_win_sub = BNX2X_NUM_VXLAN_TSO_WIN_SUB_BDS;
+ if (skb_shinfo(skb)->nr_frags >= (MAX_FETCH_BD - num_tso_win_sub)) {
if (xmit_type & XMIT_GSO) {
unsigned short lso_mss = skb_shinfo(skb)->gso_size;
- /* Check if LSO packet needs to be copied:
- 3 = 1 (for headers BD) + 2 (for PBD and last BD) */
- int wnd_size = MAX_FETCH_BD - 3;
+ int wnd_size = MAX_FETCH_BD - num_tso_win_sub;
/* Number of windows to check */
int num_wnds = skb_shinfo(skb)->nr_frags - wnd_size;
int wnd_idx = 0;
@@ -4490,7 +4434,6 @@ static int bnx2x_alloc_rx_bds(struct bnx2x_fastpath *fp,
/* Limit the CQE producer by the CQE ring size */
fp->rx_comp_prod = min_t(u16, NUM_RCQ_RINGS*RCQ_DESC_CNT,
cqe_ring_prod);
- fp->rx_pkt = fp->rx_calls = 0;
bnx2x_fp_stats(bp, fp)->eth_q_stats.rx_skb_alloc_failed += failure_cnt;
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
index b7d32e8412f1..4cbb03f87b5a 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
@@ -570,13 +570,6 @@ int bnx2x_enable_msix(struct bnx2x *bp);
int bnx2x_enable_msi(struct bnx2x *bp);
/**
- * bnx2x_low_latency_recv - LL callback
- *
- * @napi: napi structure
- */
-int bnx2x_low_latency_recv(struct napi_struct *napi);
-
-/**
* bnx2x_alloc_mem_bp - allocate memories outsize main driver structure
*
* @bp: driver handle
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
index d84efcd34fac..820b7e04bb5f 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
@@ -52,7 +52,7 @@ static const struct {
{ Q_STATS_OFFSET32(rx_skb_alloc_failed),
4, "[%s]: rx_skb_alloc_discard" },
{ Q_STATS_OFFSET32(hw_csum_err), 4, "[%s]: rx_csum_offload_errors" },
-
+ { Q_STATS_OFFSET32(driver_xoff), 4, "[%s]: tx_exhaustion_events" },
{ Q_STATS_OFFSET32(total_bytes_transmitted_hi), 8, "[%s]: tx_bytes" },
/* 10 */{ Q_STATS_OFFSET32(total_unicast_packets_transmitted_hi),
8, "[%s]: tx_ucast_packets" },
@@ -74,117 +74,115 @@ static const struct {
static const struct {
long offset;
int size;
- u32 flags;
-#define STATS_FLAGS_PORT 1
-#define STATS_FLAGS_FUNC 2
-#define STATS_FLAGS_BOTH (STATS_FLAGS_FUNC | STATS_FLAGS_PORT)
+ bool is_port_stat;
char string[ETH_GSTRING_LEN];
} bnx2x_stats_arr[] = {
/* 1 */ { STATS_OFFSET32(total_bytes_received_hi),
- 8, STATS_FLAGS_BOTH, "rx_bytes" },
+ 8, false, "rx_bytes" },
{ STATS_OFFSET32(error_bytes_received_hi),
- 8, STATS_FLAGS_BOTH, "rx_error_bytes" },
+ 8, false, "rx_error_bytes" },
{ STATS_OFFSET32(total_unicast_packets_received_hi),
- 8, STATS_FLAGS_BOTH, "rx_ucast_packets" },
+ 8, false, "rx_ucast_packets" },
{ STATS_OFFSET32(total_multicast_packets_received_hi),
- 8, STATS_FLAGS_BOTH, "rx_mcast_packets" },
+ 8, false, "rx_mcast_packets" },
{ STATS_OFFSET32(total_broadcast_packets_received_hi),
- 8, STATS_FLAGS_BOTH, "rx_bcast_packets" },
+ 8, false, "rx_bcast_packets" },
{ STATS_OFFSET32(rx_stat_dot3statsfcserrors_hi),
- 8, STATS_FLAGS_PORT, "rx_crc_errors" },
+ 8, true, "rx_crc_errors" },
{ STATS_OFFSET32(rx_stat_dot3statsalignmenterrors_hi),
- 8, STATS_FLAGS_PORT, "rx_align_errors" },
+ 8, true, "rx_align_errors" },
{ STATS_OFFSET32(rx_stat_etherstatsundersizepkts_hi),
- 8, STATS_FLAGS_PORT, "rx_undersize_packets" },
+ 8, true, "rx_undersize_packets" },
{ STATS_OFFSET32(etherstatsoverrsizepkts_hi),
- 8, STATS_FLAGS_PORT, "rx_oversize_packets" },
+ 8, true, "rx_oversize_packets" },
/* 10 */{ STATS_OFFSET32(rx_stat_etherstatsfragments_hi),
- 8, STATS_FLAGS_PORT, "rx_fragments" },
+ 8, true, "rx_fragments" },
{ STATS_OFFSET32(rx_stat_etherstatsjabbers_hi),
- 8, STATS_FLAGS_PORT, "rx_jabbers" },
+ 8, true, "rx_jabbers" },
{ STATS_OFFSET32(no_buff_discard_hi),
- 8, STATS_FLAGS_BOTH, "rx_discards" },
+ 8, false, "rx_discards" },
{ STATS_OFFSET32(mac_filter_discard),
- 4, STATS_FLAGS_PORT, "rx_filtered_packets" },
+ 4, true, "rx_filtered_packets" },
{ STATS_OFFSET32(mf_tag_discard),
- 4, STATS_FLAGS_PORT, "rx_mf_tag_discard" },
+ 4, true, "rx_mf_tag_discard" },
{ STATS_OFFSET32(pfc_frames_received_hi),
- 8, STATS_FLAGS_PORT, "pfc_frames_received" },
+ 8, true, "pfc_frames_received" },
{ STATS_OFFSET32(pfc_frames_sent_hi),
- 8, STATS_FLAGS_PORT, "pfc_frames_sent" },
+ 8, true, "pfc_frames_sent" },
{ STATS_OFFSET32(brb_drop_hi),
- 8, STATS_FLAGS_PORT, "rx_brb_discard" },
+ 8, true, "rx_brb_discard" },
{ STATS_OFFSET32(brb_truncate_hi),
- 8, STATS_FLAGS_PORT, "rx_brb_truncate" },
+ 8, true, "rx_brb_truncate" },
{ STATS_OFFSET32(pause_frames_received_hi),
- 8, STATS_FLAGS_PORT, "rx_pause_frames" },
+ 8, true, "rx_pause_frames" },
{ STATS_OFFSET32(rx_stat_maccontrolframesreceived_hi),
- 8, STATS_FLAGS_PORT, "rx_mac_ctrl_frames" },
+ 8, true, "rx_mac_ctrl_frames" },
{ STATS_OFFSET32(nig_timer_max),
- 4, STATS_FLAGS_PORT, "rx_constant_pause_events" },
+ 4, true, "rx_constant_pause_events" },
/* 20 */{ STATS_OFFSET32(rx_err_discard_pkt),
- 4, STATS_FLAGS_BOTH, "rx_phy_ip_err_discards"},
+ 4, false, "rx_phy_ip_err_discards"},
{ STATS_OFFSET32(rx_skb_alloc_failed),
- 4, STATS_FLAGS_BOTH, "rx_skb_alloc_discard" },
+ 4, false, "rx_skb_alloc_discard" },
{ STATS_OFFSET32(hw_csum_err),
- 4, STATS_FLAGS_BOTH, "rx_csum_offload_errors" },
-
+ 4, false, "rx_csum_offload_errors" },
+ { STATS_OFFSET32(driver_xoff),
+ 4, false, "tx_exhaustion_events" },
{ STATS_OFFSET32(total_bytes_transmitted_hi),
- 8, STATS_FLAGS_BOTH, "tx_bytes" },
+ 8, false, "tx_bytes" },
{ STATS_OFFSET32(tx_stat_ifhcoutbadoctets_hi),
- 8, STATS_FLAGS_PORT, "tx_error_bytes" },
+ 8, true, "tx_error_bytes" },
{ STATS_OFFSET32(total_unicast_packets_transmitted_hi),
- 8, STATS_FLAGS_BOTH, "tx_ucast_packets" },
+ 8, false, "tx_ucast_packets" },
{ STATS_OFFSET32(total_multicast_packets_transmitted_hi),
- 8, STATS_FLAGS_BOTH, "tx_mcast_packets" },
+ 8, false, "tx_mcast_packets" },
{ STATS_OFFSET32(total_broadcast_packets_transmitted_hi),
- 8, STATS_FLAGS_BOTH, "tx_bcast_packets" },
+ 8, false, "tx_bcast_packets" },
{ STATS_OFFSET32(tx_stat_dot3statsinternalmactransmiterrors_hi),
- 8, STATS_FLAGS_PORT, "tx_mac_errors" },
+ 8, true, "tx_mac_errors" },
{ STATS_OFFSET32(rx_stat_dot3statscarriersenseerrors_hi),
- 8, STATS_FLAGS_PORT, "tx_carrier_errors" },
+ 8, true, "tx_carrier_errors" },
/* 30 */{ STATS_OFFSET32(tx_stat_dot3statssinglecollisionframes_hi),
- 8, STATS_FLAGS_PORT, "tx_single_collisions" },
+ 8, true, "tx_single_collisions" },
{ STATS_OFFSET32(tx_stat_dot3statsmultiplecollisionframes_hi),
- 8, STATS_FLAGS_PORT, "tx_multi_collisions" },
+ 8, true, "tx_multi_collisions" },
{ STATS_OFFSET32(tx_stat_dot3statsdeferredtransmissions_hi),
- 8, STATS_FLAGS_PORT, "tx_deferred" },
+ 8, true, "tx_deferred" },
{ STATS_OFFSET32(tx_stat_dot3statsexcessivecollisions_hi),
- 8, STATS_FLAGS_PORT, "tx_excess_collisions" },
+ 8, true, "tx_excess_collisions" },
{ STATS_OFFSET32(tx_stat_dot3statslatecollisions_hi),
- 8, STATS_FLAGS_PORT, "tx_late_collisions" },
+ 8, true, "tx_late_collisions" },
{ STATS_OFFSET32(tx_stat_etherstatscollisions_hi),
- 8, STATS_FLAGS_PORT, "tx_total_collisions" },
+ 8, true, "tx_total_collisions" },
{ STATS_OFFSET32(tx_stat_etherstatspkts64octets_hi),
- 8, STATS_FLAGS_PORT, "tx_64_byte_packets" },
+ 8, true, "tx_64_byte_packets" },
{ STATS_OFFSET32(tx_stat_etherstatspkts65octetsto127octets_hi),
- 8, STATS_FLAGS_PORT, "tx_65_to_127_byte_packets" },
+ 8, true, "tx_65_to_127_byte_packets" },
{ STATS_OFFSET32(tx_stat_etherstatspkts128octetsto255octets_hi),
- 8, STATS_FLAGS_PORT, "tx_128_to_255_byte_packets" },
+ 8, true, "tx_128_to_255_byte_packets" },
{ STATS_OFFSET32(tx_stat_etherstatspkts256octetsto511octets_hi),
- 8, STATS_FLAGS_PORT, "tx_256_to_511_byte_packets" },
+ 8, true, "tx_256_to_511_byte_packets" },
/* 40 */{ STATS_OFFSET32(tx_stat_etherstatspkts512octetsto1023octets_hi),
- 8, STATS_FLAGS_PORT, "tx_512_to_1023_byte_packets" },
+ 8, true, "tx_512_to_1023_byte_packets" },
{ STATS_OFFSET32(etherstatspkts1024octetsto1522octets_hi),
- 8, STATS_FLAGS_PORT, "tx_1024_to_1522_byte_packets" },
+ 8, true, "tx_1024_to_1522_byte_packets" },
{ STATS_OFFSET32(etherstatspktsover1522octets_hi),
- 8, STATS_FLAGS_PORT, "tx_1523_to_9022_byte_packets" },
+ 8, true, "tx_1523_to_9022_byte_packets" },
{ STATS_OFFSET32(pause_frames_sent_hi),
- 8, STATS_FLAGS_PORT, "tx_pause_frames" },
+ 8, true, "tx_pause_frames" },
{ STATS_OFFSET32(total_tpa_aggregations_hi),
- 8, STATS_FLAGS_FUNC, "tpa_aggregations" },
+ 8, false, "tpa_aggregations" },
{ STATS_OFFSET32(total_tpa_aggregated_frames_hi),
- 8, STATS_FLAGS_FUNC, "tpa_aggregated_frames"},
+ 8, false, "tpa_aggregated_frames"},
{ STATS_OFFSET32(total_tpa_bytes_hi),
- 8, STATS_FLAGS_FUNC, "tpa_bytes"},
+ 8, false, "tpa_bytes"},
{ STATS_OFFSET32(recoverable_error),
- 4, STATS_FLAGS_FUNC, "recoverable_errors" },
+ 4, false, "recoverable_errors" },
{ STATS_OFFSET32(unrecoverable_error),
- 4, STATS_FLAGS_FUNC, "unrecoverable_errors" },
+ 4, false, "unrecoverable_errors" },
{ STATS_OFFSET32(driver_filtered_tx_pkt),
- 4, STATS_FLAGS_FUNC, "driver_filtered_tx_pkt" },
+ 4, false, "driver_filtered_tx_pkt" },
{ STATS_OFFSET32(eee_tx_lpi),
- 4, STATS_FLAGS_PORT, "Tx LPI entry count"}
+ 4, true, "Tx LPI entry count"}
};
#define BNX2X_NUM_STATS ARRAY_SIZE(bnx2x_stats_arr)
@@ -3065,12 +3063,8 @@ static void bnx2x_self_test(struct net_device *dev,
}
}
-#define IS_PORT_STAT(i) \
- ((bnx2x_stats_arr[i].flags & STATS_FLAGS_BOTH) == STATS_FLAGS_PORT)
-#define IS_FUNC_STAT(i) (bnx2x_stats_arr[i].flags & STATS_FLAGS_FUNC)
-#define HIDE_PORT_STAT(bp) \
- ((IS_MF(bp) && !(bp->msg_enable & BNX2X_MSG_STATS)) || \
- IS_VF(bp))
+#define IS_PORT_STAT(i) (bnx2x_stats_arr[i].is_port_stat)
+#define HIDE_PORT_STAT(bp) IS_VF(bp)
/* ethtool statistics are displayed for all regular ethernet queues and the
* fcoe L2 queue if not disabled
@@ -3094,7 +3088,7 @@ static int bnx2x_get_sset_count(struct net_device *dev, int stringset)
num_strings = 0;
if (HIDE_PORT_STAT(bp)) {
for (i = 0; i < BNX2X_NUM_STATS; i++)
- if (IS_FUNC_STAT(i))
+ if (!IS_PORT_STAT(i))
num_strings++;
} else
num_strings += BNX2X_NUM_STATS;
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h
index cafd5de675cf..27aa0802d87d 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h
@@ -3013,8 +3013,8 @@ struct afex_stats {
};
#define BCM_5710_FW_MAJOR_VERSION 7
-#define BCM_5710_FW_MINOR_VERSION 12
-#define BCM_5710_FW_REVISION_VERSION 30
+#define BCM_5710_FW_MINOR_VERSION 13
+#define BCM_5710_FW_REVISION_VERSION 1
#define BCM_5710_FW_ENGINEERING_VERSION 0
#define BCM_5710_FW_COMPILE_FLAGS 1
@@ -3583,7 +3583,7 @@ enum classify_rule {
CLASSIFY_RULE_OPCODE_MAC,
CLASSIFY_RULE_OPCODE_VLAN,
CLASSIFY_RULE_OPCODE_PAIR,
- CLASSIFY_RULE_OPCODE_VXLAN,
+ CLASSIFY_RULE_OPCODE_IMAC_VNI,
MAX_CLASSIFY_RULE
};
@@ -3826,6 +3826,17 @@ struct eth_classify_header {
__le32 echo;
};
+/*
+ * Command for adding/removing a Inner-MAC/VNI classification rule
+ */
+struct eth_classify_imac_vni_cmd {
+ struct eth_classify_cmd_header header;
+ __le32 vni;
+ __le16 imac_lsb;
+ __le16 imac_mid;
+ __le16 imac_msb;
+ __le16 reserved1;
+};
/*
* Command for adding/removing a MAC classification rule
@@ -3869,14 +3880,6 @@ struct eth_classify_vlan_cmd {
/*
* Command for adding/removing a VXLAN classification rule
*/
-struct eth_classify_vxlan_cmd {
- struct eth_classify_cmd_header header;
- __le32 vni;
- __le16 inner_mac_lsb;
- __le16 inner_mac_mid;
- __le16 inner_mac_msb;
- __le16 reserved1;
-};
/*
* union for eth classification rule
@@ -3885,7 +3888,7 @@ union eth_classify_rule_cmd {
struct eth_classify_mac_cmd mac;
struct eth_classify_vlan_cmd vlan;
struct eth_classify_pair_cmd pair;
- struct eth_classify_vxlan_cmd vxlan;
+ struct eth_classify_imac_vni_cmd imac_vni;
};
/*
@@ -5623,6 +5626,14 @@ enum igu_mode {
MAX_IGU_MODE
};
+/*
+ * Inner Headers Classification Type
+ */
+enum inner_clss_type {
+ INNER_CLSS_DISABLED,
+ INNER_CLSS_USE_VLAN,
+ INNER_CLSS_USE_VNI,
+ MAX_INNER_CLSS_TYPE};
/*
* IP versions
@@ -5953,14 +5964,6 @@ enum ts_offset_cmd {
MAX_TS_OFFSET_CMD
};
-/* Tunnel Mode */
-enum tunnel_mode {
- TUNN_MODE_NONE,
- TUNN_MODE_VXLAN,
- TUNN_MODE_GRE,
- MAX_TUNNEL_MODE
-};
-
/* zone A per-queue data */
struct ustorm_queue_zone_data {
struct ustorm_eth_rx_producers eth_rx_producers;
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index c9b036789184..6c4e3a69976f 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -10139,8 +10139,8 @@ static void __bnx2x_del_vxlan_port(struct bnx2x *bp, u16 port)
DP(BNX2X_MSG_SP, "Invalid vxlan port\n");
return;
}
- bp->vxlan_dst_port--;
- if (bp->vxlan_dst_port)
+ bp->vxlan_dst_port_count--;
+ if (bp->vxlan_dst_port_count)
return;
if (netif_running(bp->dev)) {
@@ -13004,9 +13004,6 @@ static const struct net_device_ops bnx2x_netdev_ops = {
.ndo_fcoe_get_wwn = bnx2x_fcoe_get_wwn,
#endif
-#ifdef CONFIG_NET_RX_BUSY_POLL
- .ndo_busy_poll = bnx2x_low_latency_recv,
-#endif
.ndo_get_phys_port_id = bnx2x_get_phys_port_id,
.ndo_set_vf_link_state = bnx2x_set_vf_link_state,
.ndo_features_check = bnx2x_features_check,
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index db15c5ee09c5..df835f5e46d8 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -72,8 +72,10 @@ MODULE_VERSION(DRV_MODULE_VERSION);
#define BNXT_TX_PUSH_THRESH 92
enum board_idx {
+ BCM57301,
BCM57302,
BCM57304,
+ BCM57402,
BCM57404,
BCM57406,
BCM57304_VF,
@@ -84,17 +86,21 @@ enum board_idx {
static const struct {
char *name;
} board_info[] = {
- { "Broadcom BCM57302 NetXtreme-C Single-port 10Gb/25Gb/40Gb/50Gb Ethernet" },
+ { "Broadcom BCM57301 NetXtreme-C Single-port 10Gb Ethernet" },
+ { "Broadcom BCM57302 NetXtreme-C Dual-port 10Gb/25Gb Ethernet" },
{ "Broadcom BCM57304 NetXtreme-C Dual-port 10Gb/25Gb/40Gb/50Gb Ethernet" },
+ { "Broadcom BCM57402 NetXtreme-E Dual-port 10Gb Ethernet" },
{ "Broadcom BCM57404 NetXtreme-E Dual-port 10Gb/25Gb Ethernet" },
- { "Broadcom BCM57406 NetXtreme-E Dual-port 10Gb Ethernet" },
+ { "Broadcom BCM57406 NetXtreme-E Dual-port 10GBase-T Ethernet" },
{ "Broadcom BCM57304 NetXtreme-C Ethernet Virtual Function" },
{ "Broadcom BCM57404 NetXtreme-E Ethernet Virtual Function" },
};
static const struct pci_device_id bnxt_pci_tbl[] = {
+ { PCI_VDEVICE(BROADCOM, 0x16c8), .driver_data = BCM57301 },
{ PCI_VDEVICE(BROADCOM, 0x16c9), .driver_data = BCM57302 },
{ PCI_VDEVICE(BROADCOM, 0x16ca), .driver_data = BCM57304 },
+ { PCI_VDEVICE(BROADCOM, 0x16d0), .driver_data = BCM57402 },
{ PCI_VDEVICE(BROADCOM, 0x16d1), .driver_data = BCM57404 },
{ PCI_VDEVICE(BROADCOM, 0x16d2), .driver_data = BCM57406 },
#ifdef CONFIG_BNXT_SRIOV
@@ -173,7 +179,6 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev)
u32 len, free_size, vlan_tag_flags, cfa_action, flags;
u16 prod, last_frag;
struct pci_dev *pdev = bp->pdev;
- struct bnxt_napi *bnapi;
struct bnxt_tx_ring_info *txr;
struct bnxt_sw_tx_bd *tx_buf;
@@ -183,8 +188,7 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
}
- bnapi = bp->bnapi[i];
- txr = &bnapi->tx_ring;
+ txr = &bp->tx_ring[i];
txq = netdev_get_tx_queue(dev, i);
prod = txr->tx_prod;
@@ -417,8 +421,8 @@ tx_dma_error:
static void bnxt_tx_int(struct bnxt *bp, struct bnxt_napi *bnapi, int nr_pkts)
{
- struct bnxt_tx_ring_info *txr = &bnapi->tx_ring;
- int index = bnapi->index;
+ struct bnxt_tx_ring_info *txr = bnapi->tx_ring;
+ int index = txr - &bp->tx_ring[0];
struct netdev_queue *txq = netdev_get_tx_queue(bp->dev, index);
u16 cons = txr->tx_cons;
struct pci_dev *pdev = bp->pdev;
@@ -596,7 +600,7 @@ static void bnxt_reuse_rx_agg_bufs(struct bnxt_napi *bnapi, u16 cp_cons,
{
struct bnxt *bp = bnapi->bp;
struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
- struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+ struct bnxt_rx_ring_info *rxr = bnapi->rx_ring;
u16 prod = rxr->rx_agg_prod;
u16 sw_prod = rxr->rx_sw_agg_prod;
u32 i;
@@ -675,7 +679,7 @@ static struct sk_buff *bnxt_rx_pages(struct bnxt *bp, struct bnxt_napi *bnapi,
{
struct pci_dev *pdev = bp->pdev;
struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
- struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+ struct bnxt_rx_ring_info *rxr = bnapi->rx_ring;
u16 prod = rxr->rx_agg_prod;
u32 i;
@@ -856,8 +860,13 @@ static inline struct sk_buff *bnxt_gro_skb(struct bnxt_tpa_info *tpa_info,
struct tcphdr *th;
int payload_off, tcp_opt_len = 0;
int len, nw_off;
+ u16 segs;
+
+ segs = TPA_END_TPA_SEGS(tpa_end);
+ if (segs == 1)
+ return skb;
- NAPI_GRO_CB(skb)->count = TPA_END_TPA_SEGS(tpa_end);
+ NAPI_GRO_CB(skb)->count = segs;
skb_shinfo(skb)->gso_size =
le32_to_cpu(tpa_end1->rx_tpa_end_cmp_seg_len);
skb_shinfo(skb)->gso_type = tpa_info->gso_type;
@@ -929,7 +938,7 @@ static inline struct sk_buff *bnxt_tpa_end(struct bnxt *bp,
bool *agg_event)
{
struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
- struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+ struct bnxt_rx_ring_info *rxr = bnapi->rx_ring;
u8 agg_id = TPA_END_AGG_ID(tpa_end);
u8 *data, agg_bufs;
u16 cp_cons = RING_CMP(*raw_cons);
@@ -1045,7 +1054,7 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_napi *bnapi, u32 *raw_cons,
bool *agg_event)
{
struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
- struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+ struct bnxt_rx_ring_info *rxr = bnapi->rx_ring;
struct net_device *dev = bp->dev;
struct rx_cmp *rxcmp;
struct rx_cmp_ext *rxcmp1;
@@ -1187,8 +1196,10 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_napi *bnapi, u32 *raw_cons,
skb->csum_level = RX_CMP_ENCAP(rxcmp1);
}
} else {
- if (rxcmp1->rx_cmp_cfa_code_errors_v2 & RX_CMP_L4_CS_ERR_BITS)
- cpr->rx_l4_csum_errors++;
+ if (rxcmp1->rx_cmp_cfa_code_errors_v2 & RX_CMP_L4_CS_ERR_BITS) {
+ if (dev->features & NETIF_F_RXCSUM)
+ cpr->rx_l4_csum_errors++;
+ }
}
skb_record_rx_queue(skb, bnapi->index);
@@ -1377,7 +1388,7 @@ static int bnxt_poll_work(struct bnxt *bp, struct bnxt_napi *bnapi, int budget)
bnxt_tx_int(bp, bnapi, tx_pkts);
if (rx_event) {
- struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+ struct bnxt_rx_ring_info *rxr = bnapi->rx_ring;
writel(DB_KEY_RX | rxr->rx_prod, rxr->rx_doorbell);
writel(DB_KEY_RX | rxr->rx_prod, rxr->rx_doorbell);
@@ -1446,19 +1457,14 @@ static void bnxt_free_tx_skbs(struct bnxt *bp)
int i, max_idx;
struct pci_dev *pdev = bp->pdev;
- if (!bp->bnapi)
+ if (!bp->tx_ring)
return;
max_idx = bp->tx_nr_pages * TX_DESC_CNT;
for (i = 0; i < bp->tx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_tx_ring_info *txr;
+ struct bnxt_tx_ring_info *txr = &bp->tx_ring[i];
int j;
- if (!bnapi)
- continue;
-
- txr = &bnapi->tx_ring;
for (j = 0; j < max_idx;) {
struct bnxt_sw_tx_bd *tx_buf = &txr->tx_buf_ring[j];
struct sk_buff *skb = tx_buf->skb;
@@ -1504,21 +1510,15 @@ static void bnxt_free_rx_skbs(struct bnxt *bp)
int i, max_idx, max_agg_idx;
struct pci_dev *pdev = bp->pdev;
- if (!bp->bnapi)
+ if (!bp->rx_ring)
return;
max_idx = bp->rx_nr_pages * RX_DESC_CNT;
max_agg_idx = bp->rx_agg_nr_pages * RX_DESC_CNT;
for (i = 0; i < bp->rx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_rx_ring_info *rxr;
+ struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i];
int j;
- if (!bnapi)
- continue;
-
- rxr = &bnapi->rx_ring;
-
if (rxr->rx_tpa) {
for (j = 0; j < MAX_TPA; j++) {
struct bnxt_tpa_info *tpa_info =
@@ -1646,19 +1646,13 @@ static void bnxt_free_rx_rings(struct bnxt *bp)
{
int i;
- if (!bp->bnapi)
+ if (!bp->rx_ring)
return;
for (i = 0; i < bp->rx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_rx_ring_info *rxr;
+ struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i];
struct bnxt_ring_struct *ring;
- if (!bnapi)
- continue;
-
- rxr = &bnapi->rx_ring;
-
kfree(rxr->rx_tpa);
rxr->rx_tpa = NULL;
@@ -1677,6 +1671,9 @@ static int bnxt_alloc_rx_rings(struct bnxt *bp)
{
int i, rc, agg_rings = 0, tpa_rings = 0;
+ if (!bp->rx_ring)
+ return -ENOMEM;
+
if (bp->flags & BNXT_FLAG_AGG_RINGS)
agg_rings = 1;
@@ -1684,14 +1681,9 @@ static int bnxt_alloc_rx_rings(struct bnxt *bp)
tpa_rings = 1;
for (i = 0; i < bp->rx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_rx_ring_info *rxr;
+ struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i];
struct bnxt_ring_struct *ring;
- if (!bnapi)
- continue;
-
- rxr = &bnapi->rx_ring;
ring = &rxr->rx_ring_struct;
rc = bnxt_alloc_ring(bp, ring);
@@ -1729,19 +1721,13 @@ static void bnxt_free_tx_rings(struct bnxt *bp)
int i;
struct pci_dev *pdev = bp->pdev;
- if (!bp->bnapi)
+ if (!bp->tx_ring)
return;
for (i = 0; i < bp->tx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_tx_ring_info *txr;
+ struct bnxt_tx_ring_info *txr = &bp->tx_ring[i];
struct bnxt_ring_struct *ring;
- if (!bnapi)
- continue;
-
- txr = &bnapi->tx_ring;
-
if (txr->tx_push) {
dma_free_coherent(&pdev->dev, bp->tx_push_size,
txr->tx_push, txr->tx_push_mapping);
@@ -1775,14 +1761,9 @@ static int bnxt_alloc_tx_rings(struct bnxt *bp)
}
for (i = 0, j = 0; i < bp->tx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_tx_ring_info *txr;
+ struct bnxt_tx_ring_info *txr = &bp->tx_ring[i];
struct bnxt_ring_struct *ring;
- if (!bnapi)
- continue;
-
- txr = &bnapi->tx_ring;
ring = &txr->tx_ring_struct;
rc = bnxt_alloc_ring(bp, ring);
@@ -1885,7 +1866,10 @@ static void bnxt_init_ring_struct(struct bnxt *bp)
ring->dma_arr = cpr->cp_desc_mapping;
ring->vmem_size = 0;
- rxr = &bnapi->rx_ring;
+ rxr = bnapi->rx_ring;
+ if (!rxr)
+ goto skip_rx;
+
ring = &rxr->rx_ring_struct;
ring->nr_pages = bp->rx_nr_pages;
ring->page_size = HW_RXBD_RING_SIZE;
@@ -1902,7 +1886,11 @@ static void bnxt_init_ring_struct(struct bnxt *bp)
ring->vmem_size = SW_RXBD_AGG_RING_SIZE * bp->rx_agg_nr_pages;
ring->vmem = (void **)&rxr->rx_agg_ring;
- txr = &bnapi->tx_ring;
+skip_rx:
+ txr = bnapi->tx_ring;
+ if (!txr)
+ continue;
+
ring = &txr->tx_ring_struct;
ring->nr_pages = bp->tx_nr_pages;
ring->page_size = HW_RXBD_RING_SIZE;
@@ -1938,22 +1926,18 @@ static void bnxt_init_rxbd_pages(struct bnxt_ring_struct *ring, u32 type)
static int bnxt_init_one_rx_ring(struct bnxt *bp, int ring_nr)
{
struct net_device *dev = bp->dev;
- struct bnxt_napi *bnapi = bp->bnapi[ring_nr];
struct bnxt_rx_ring_info *rxr;
struct bnxt_ring_struct *ring;
u32 prod, type;
int i;
- if (!bnapi)
- return -EINVAL;
-
type = (bp->rx_buf_use_size << RX_BD_LEN_SHIFT) |
RX_BD_TYPE_RX_PACKET_BD | RX_BD_FLAGS_EOP;
if (NET_IP_ALIGN == 2)
type |= RX_BD_FLAGS_SOP;
- rxr = &bnapi->rx_ring;
+ rxr = &bp->rx_ring[ring_nr];
ring = &rxr->rx_ring_struct;
bnxt_init_rxbd_pages(ring, type);
@@ -1969,11 +1953,12 @@ static int bnxt_init_one_rx_ring(struct bnxt *bp, int ring_nr)
rxr->rx_prod = prod;
ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring = &rxr->rx_agg_ring_struct;
+ ring->fw_ring_id = INVALID_HW_RING_ID;
+
if (!(bp->flags & BNXT_FLAG_AGG_RINGS))
return 0;
- ring = &rxr->rx_agg_ring_struct;
-
type = ((u32)PAGE_SIZE << RX_BD_LEN_SHIFT) |
RX_BD_TYPE_RX_AGG_BD | RX_BD_FLAGS_SOP;
@@ -1989,7 +1974,6 @@ static int bnxt_init_one_rx_ring(struct bnxt *bp, int ring_nr)
prod = NEXT_RX_AGG(prod);
}
rxr->rx_agg_prod = prod;
- ring->fw_ring_id = INVALID_HW_RING_ID;
if (bp->flags & BNXT_FLAG_TPA) {
if (rxr->rx_tpa) {
@@ -2035,8 +2019,7 @@ static int bnxt_init_tx_rings(struct bnxt *bp)
MAX_SKB_FRAGS + 1);
for (i = 0; i < bp->tx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_tx_ring_info *txr = &bnapi->tx_ring;
+ struct bnxt_tx_ring_info *txr = &bp->tx_ring[i];
struct bnxt_ring_struct *ring = &txr->tx_ring_struct;
ring->fw_ring_id = INVALID_HW_RING_ID;
@@ -2423,14 +2406,18 @@ static void bnxt_clear_ring_indices(struct bnxt *bp)
cpr = &bnapi->cp_ring;
cpr->cp_raw_cons = 0;
- txr = &bnapi->tx_ring;
- txr->tx_prod = 0;
- txr->tx_cons = 0;
+ txr = bnapi->tx_ring;
+ if (txr) {
+ txr->tx_prod = 0;
+ txr->tx_cons = 0;
+ }
- rxr = &bnapi->rx_ring;
- rxr->rx_prod = 0;
- rxr->rx_agg_prod = 0;
- rxr->rx_sw_agg_prod = 0;
+ rxr = bnapi->rx_ring;
+ if (rxr) {
+ rxr->rx_prod = 0;
+ rxr->rx_agg_prod = 0;
+ rxr->rx_sw_agg_prod = 0;
+ }
}
}
@@ -2496,6 +2483,10 @@ static void bnxt_free_mem(struct bnxt *bp, bool irq_re_init)
bnxt_free_stats(bp);
bnxt_free_ring_grps(bp);
bnxt_free_vnics(bp);
+ kfree(bp->tx_ring);
+ bp->tx_ring = NULL;
+ kfree(bp->rx_ring);
+ bp->rx_ring = NULL;
kfree(bp->bnapi);
bp->bnapi = NULL;
} else {
@@ -2505,7 +2496,7 @@ static void bnxt_free_mem(struct bnxt *bp, bool irq_re_init)
static int bnxt_alloc_mem(struct bnxt *bp, bool irq_re_init)
{
- int i, rc, size, arr_size;
+ int i, j, rc, size, arr_size;
void *bnapi;
if (irq_re_init) {
@@ -2527,6 +2518,33 @@ static int bnxt_alloc_mem(struct bnxt *bp, bool irq_re_init)
bp->bnapi[i]->bp = bp;
}
+ bp->rx_ring = kcalloc(bp->rx_nr_rings,
+ sizeof(struct bnxt_rx_ring_info),
+ GFP_KERNEL);
+ if (!bp->rx_ring)
+ return -ENOMEM;
+
+ for (i = 0; i < bp->rx_nr_rings; i++) {
+ bp->rx_ring[i].bnapi = bp->bnapi[i];
+ bp->bnapi[i]->rx_ring = &bp->rx_ring[i];
+ }
+
+ bp->tx_ring = kcalloc(bp->tx_nr_rings,
+ sizeof(struct bnxt_tx_ring_info),
+ GFP_KERNEL);
+ if (!bp->tx_ring)
+ return -ENOMEM;
+
+ if (bp->flags & BNXT_FLAG_SHARED_RINGS)
+ j = 0;
+ else
+ j = bp->rx_nr_rings;
+
+ for (i = 0; i < bp->tx_nr_rings; i++, j++) {
+ bp->tx_ring[i].bnapi = bp->bnapi[j];
+ bp->bnapi[j]->tx_ring = &bp->tx_ring[i];
+ }
+
rc = bnxt_alloc_stats(bp);
if (rc)
goto alloc_mem_err;
@@ -2596,6 +2614,9 @@ int _hwrm_send_message(struct bnxt *bp, void *msg, u32 msg_len, int timeout)
/* Write request msg to hwrm channel */
__iowrite32_copy(bp->bar0, data, msg_len / 4);
+ for (i = msg_len; i < HWRM_MAX_REQ_LEN; i += 4)
+ writel(0, bp->bar0 + i);
+
/* currently supports only one outstanding message */
if (intr_process)
bp->hwrm_intr_seq_id = le32_to_cpu(req->target_id_seq_id) &
@@ -2693,17 +2714,16 @@ static int bnxt_hwrm_func_drv_rgtr(struct bnxt *bp)
req.ver_upd = DRV_VER_UPD;
if (BNXT_PF(bp)) {
- unsigned long vf_req_snif_bmap[4];
+ DECLARE_BITMAP(vf_req_snif_bmap, 256);
u32 *data = (u32 *)vf_req_snif_bmap;
- memset(vf_req_snif_bmap, 0, 32);
+ memset(vf_req_snif_bmap, 0, sizeof(vf_req_snif_bmap));
for (i = 0; i < ARRAY_SIZE(bnxt_vf_req_snif); i++)
__set_bit(bnxt_vf_req_snif[i], vf_req_snif_bmap);
- for (i = 0; i < 8; i++) {
- req.vf_req_fwd[i] = cpu_to_le32(*data);
- data++;
- }
+ for (i = 0; i < 8; i++)
+ req.vf_req_fwd[i] = cpu_to_le32(data[i]);
+
req.enables |=
cpu_to_le32(FUNC_DRV_RGTR_REQ_ENABLES_VF_REQ_FWD);
}
@@ -2711,6 +2731,14 @@ static int bnxt_hwrm_func_drv_rgtr(struct bnxt *bp)
return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
}
+static int bnxt_hwrm_func_drv_unrgtr(struct bnxt *bp)
+{
+ struct hwrm_func_drv_unrgtr_input req = {0};
+
+ bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_DRV_UNRGTR, -1, -1);
+ return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+}
+
static int bnxt_hwrm_tunnel_dst_port_free(struct bnxt *bp, u8 tunnel_type)
{
u32 rc = 0;
@@ -2773,7 +2801,7 @@ static int bnxt_hwrm_cfa_l2_set_rx_mask(struct bnxt *bp, u16 vnic_id)
struct bnxt_vnic_info *vnic = &bp->vnic_info[vnic_id];
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_L2_SET_RX_MASK, -1, -1);
- req.dflt_vnic_id = cpu_to_le32(vnic->fw_vnic_id);
+ req.vnic_id = cpu_to_le32(vnic->fw_vnic_id);
req.num_mc_entries = cpu_to_le32(vnic->mc_list_count);
req.mc_tbl_addr = cpu_to_le64(vnic->mc_list_mapping);
@@ -2806,7 +2834,7 @@ static int bnxt_hwrm_cfa_ntuple_filter_free(struct bnxt *bp,
CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_SRC_PORT_MASK | \
CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT | \
CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT_MASK | \
- CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_VNIC_ID)
+ CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_ID)
static int bnxt_hwrm_cfa_ntuple_filter_alloc(struct bnxt *bp,
struct bnxt_ntuple_filter *fltr)
@@ -2825,7 +2853,7 @@ static int bnxt_hwrm_cfa_ntuple_filter_alloc(struct bnxt *bp,
req.ethertype = htons(ETH_P_IP);
memcpy(req.src_macaddr, fltr->src_mac_addr, ETH_ALEN);
- req.ipaddr_type = 4;
+ req.ip_addr_type = CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV4;
req.ip_protocol = keys->basic.ip_proto;
req.src_ipaddr[0] = keys->addrs.v4addrs.src;
@@ -2838,7 +2866,7 @@ static int bnxt_hwrm_cfa_ntuple_filter_alloc(struct bnxt *bp,
req.dst_port = keys->ports.dst;
req.dst_port_mask = cpu_to_be16(0xffff);
- req.dst_vnic_id = cpu_to_le16(vnic->fw_vnic_id);
+ req.dst_id = cpu_to_le16(vnic->fw_vnic_id);
mutex_lock(&bp->hwrm_cmd_lock);
rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
if (!rc)
@@ -2858,10 +2886,10 @@ static int bnxt_hwrm_set_vnic_filter(struct bnxt *bp, u16 vnic_id, u16 idx,
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_L2_FILTER_ALLOC, -1, -1);
req.flags = cpu_to_le32(CFA_L2_FILTER_ALLOC_REQ_FLAGS_PATH_RX |
CFA_L2_FILTER_ALLOC_REQ_FLAGS_OUTERMOST);
- req.dst_vnic_id = cpu_to_le16(bp->vnic_info[vnic_id].fw_vnic_id);
+ req.dst_id = cpu_to_le16(bp->vnic_info[vnic_id].fw_vnic_id);
req.enables =
cpu_to_le32(CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDR |
- CFA_L2_FILTER_ALLOC_REQ_ENABLES_DST_VNIC_ID |
+ CFA_L2_FILTER_ALLOC_REQ_ENABLES_DST_ID |
CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDR_MASK);
memcpy(req.l2_addr, mac_addr, ETH_ALEN);
req.l2_addr_mask[0] = 0xff;
@@ -2931,7 +2959,8 @@ static int bnxt_hwrm_vnic_set_tpa(struct bnxt *bp, u16 vnic_id, u32 tpa_flags)
req.enables =
cpu_to_le32(VNIC_TPA_CFG_REQ_ENABLES_MAX_AGG_SEGS |
- VNIC_TPA_CFG_REQ_ENABLES_MAX_AGGS);
+ VNIC_TPA_CFG_REQ_ENABLES_MAX_AGGS |
+ VNIC_TPA_CFG_REQ_ENABLES_MIN_AGG_LEN);
/* Number of segs are log2 units, and first packet is not
* included as part of this units.
@@ -2949,6 +2978,8 @@ static int bnxt_hwrm_vnic_set_tpa(struct bnxt *bp, u16 vnic_id, u32 tpa_flags)
segs = ilog2(nsegs);
req.max_agg_segs = cpu_to_le16(segs);
req.max_aggs = cpu_to_le16(VNIC_TPA_CFG_REQ_MAX_AGGS_MAX);
+
+ req.min_agg_len = cpu_to_le32(512);
}
req.vnic_id = cpu_to_le16(vnic->fw_vnic_id);
@@ -3059,7 +3090,7 @@ static int bnxt_hwrm_vnic_ctx_alloc(struct bnxt *bp, u16 vnic_id)
static int bnxt_hwrm_vnic_cfg(struct bnxt *bp, u16 vnic_id)
{
- int grp_idx = 0;
+ unsigned int ring = 0, grp_idx;
struct bnxt_vnic_info *vnic = &bp->vnic_info[vnic_id];
struct hwrm_vnic_cfg_input req = {0};
@@ -3070,10 +3101,11 @@ static int bnxt_hwrm_vnic_cfg(struct bnxt *bp, u16 vnic_id)
req.rss_rule = cpu_to_le16(vnic->fw_rss_cos_lb_ctx);
req.cos_rule = cpu_to_le16(0xffff);
if (vnic->flags & BNXT_VNIC_RSS_FLAG)
- grp_idx = 0;
+ ring = 0;
else if (vnic->flags & BNXT_VNIC_RFS_FLAG)
- grp_idx = vnic_id - 1;
+ ring = vnic_id - 1;
+ grp_idx = bp->rx_ring[ring].bnapi->index;
req.vnic_id = cpu_to_le16(vnic->fw_vnic_id);
req.dflt_ring_grp = cpu_to_le16(bp->grp_info[grp_idx].fw_grp_id);
@@ -3114,22 +3146,25 @@ static void bnxt_hwrm_vnic_free(struct bnxt *bp)
bnxt_hwrm_vnic_free_one(bp, i);
}
-static int bnxt_hwrm_vnic_alloc(struct bnxt *bp, u16 vnic_id, u16 start_grp_id,
- u16 end_grp_id)
+static int bnxt_hwrm_vnic_alloc(struct bnxt *bp, u16 vnic_id,
+ unsigned int start_rx_ring_idx,
+ unsigned int nr_rings)
{
- u32 rc = 0, i, j;
+ int rc = 0;
+ unsigned int i, j, grp_idx, end_idx = start_rx_ring_idx + nr_rings;
struct hwrm_vnic_alloc_input req = {0};
struct hwrm_vnic_alloc_output *resp = bp->hwrm_cmd_resp_addr;
/* map ring groups to this vnic */
- for (i = start_grp_id, j = 0; i < end_grp_id; i++, j++) {
- if (bp->grp_info[i].fw_grp_id == INVALID_HW_RING_ID) {
+ for (i = start_rx_ring_idx, j = 0; i < end_idx; i++, j++) {
+ grp_idx = bp->rx_ring[i].bnapi->index;
+ if (bp->grp_info[grp_idx].fw_grp_id == INVALID_HW_RING_ID) {
netdev_err(bp->dev, "Not enough ring groups avail:%x req:%x\n",
- j, (end_grp_id - start_grp_id));
+ j, nr_rings);
break;
}
bp->vnic_info[vnic_id].fw_grp_ids[j] =
- bp->grp_info[i].fw_grp_id;
+ bp->grp_info[grp_idx].fw_grp_id;
}
bp->vnic_info[vnic_id].fw_rss_cos_lb_ctx = INVALID_HW_RING_ID;
@@ -3156,20 +3191,22 @@ static int bnxt_hwrm_ring_grp_alloc(struct bnxt *bp)
struct hwrm_ring_grp_alloc_input req = {0};
struct hwrm_ring_grp_alloc_output *resp =
bp->hwrm_cmd_resp_addr;
+ unsigned int grp_idx = bp->rx_ring[i].bnapi->index;
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_RING_GRP_ALLOC, -1, -1);
- req.cr = cpu_to_le16(bp->grp_info[i].cp_fw_ring_id);
- req.rr = cpu_to_le16(bp->grp_info[i].rx_fw_ring_id);
- req.ar = cpu_to_le16(bp->grp_info[i].agg_fw_ring_id);
- req.sc = cpu_to_le16(bp->grp_info[i].fw_stats_ctx);
+ req.cr = cpu_to_le16(bp->grp_info[grp_idx].cp_fw_ring_id);
+ req.rr = cpu_to_le16(bp->grp_info[grp_idx].rx_fw_ring_id);
+ req.ar = cpu_to_le16(bp->grp_info[grp_idx].agg_fw_ring_id);
+ req.sc = cpu_to_le16(bp->grp_info[grp_idx].fw_stats_ctx);
rc = _hwrm_send_message(bp, &req, sizeof(req),
HWRM_CMD_TIMEOUT);
if (rc)
break;
- bp->grp_info[i].fw_grp_id = le32_to_cpu(resp->ring_group_id);
+ bp->grp_info[grp_idx].fw_grp_id =
+ le32_to_cpu(resp->ring_group_id);
}
mutex_unlock(&bp->hwrm_cmd_lock);
return rc;
@@ -3294,75 +3331,66 @@ static int bnxt_hwrm_ring_alloc(struct bnxt *bp)
{
int i, rc = 0;
- if (bp->cp_nr_rings) {
- for (i = 0; i < bp->cp_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
- struct bnxt_ring_struct *ring = &cpr->cp_ring_struct;
+ for (i = 0; i < bp->cp_nr_rings; i++) {
+ struct bnxt_napi *bnapi = bp->bnapi[i];
+ struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+ struct bnxt_ring_struct *ring = &cpr->cp_ring_struct;
- rc = hwrm_ring_alloc_send_msg(bp, ring,
- HWRM_RING_ALLOC_CMPL, i,
- INVALID_STATS_CTX_ID);
- if (rc)
- goto err_out;
- cpr->cp_doorbell = bp->bar1 + i * 0x80;
- BNXT_CP_DB(cpr->cp_doorbell, cpr->cp_raw_cons);
- bp->grp_info[i].cp_fw_ring_id = ring->fw_ring_id;
- }
+ rc = hwrm_ring_alloc_send_msg(bp, ring, HWRM_RING_ALLOC_CMPL, i,
+ INVALID_STATS_CTX_ID);
+ if (rc)
+ goto err_out;
+ cpr->cp_doorbell = bp->bar1 + i * 0x80;
+ BNXT_CP_DB(cpr->cp_doorbell, cpr->cp_raw_cons);
+ bp->grp_info[i].cp_fw_ring_id = ring->fw_ring_id;
}
- if (bp->tx_nr_rings) {
- for (i = 0; i < bp->tx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_tx_ring_info *txr = &bnapi->tx_ring;
- struct bnxt_ring_struct *ring = &txr->tx_ring_struct;
- u16 fw_stats_ctx = bp->grp_info[i].fw_stats_ctx;
+ for (i = 0; i < bp->tx_nr_rings; i++) {
+ struct bnxt_tx_ring_info *txr = &bp->tx_ring[i];
+ struct bnxt_ring_struct *ring = &txr->tx_ring_struct;
+ u32 map_idx = txr->bnapi->index;
+ u16 fw_stats_ctx = bp->grp_info[map_idx].fw_stats_ctx;
- rc = hwrm_ring_alloc_send_msg(bp, ring,
- HWRM_RING_ALLOC_TX, i,
- fw_stats_ctx);
- if (rc)
- goto err_out;
- txr->tx_doorbell = bp->bar1 + i * 0x80;
- }
+ rc = hwrm_ring_alloc_send_msg(bp, ring, HWRM_RING_ALLOC_TX,
+ map_idx, fw_stats_ctx);
+ if (rc)
+ goto err_out;
+ txr->tx_doorbell = bp->bar1 + map_idx * 0x80;
}
- if (bp->rx_nr_rings) {
- for (i = 0; i < bp->rx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
- struct bnxt_ring_struct *ring = &rxr->rx_ring_struct;
+ for (i = 0; i < bp->rx_nr_rings; i++) {
+ struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i];
+ struct bnxt_ring_struct *ring = &rxr->rx_ring_struct;
+ u32 map_idx = rxr->bnapi->index;
- rc = hwrm_ring_alloc_send_msg(bp, ring,
- HWRM_RING_ALLOC_RX, i,
- INVALID_STATS_CTX_ID);
- if (rc)
- goto err_out;
- rxr->rx_doorbell = bp->bar1 + i * 0x80;
- writel(DB_KEY_RX | rxr->rx_prod, rxr->rx_doorbell);
- bp->grp_info[i].rx_fw_ring_id = ring->fw_ring_id;
- }
+ rc = hwrm_ring_alloc_send_msg(bp, ring, HWRM_RING_ALLOC_RX,
+ map_idx, INVALID_STATS_CTX_ID);
+ if (rc)
+ goto err_out;
+ rxr->rx_doorbell = bp->bar1 + map_idx * 0x80;
+ writel(DB_KEY_RX | rxr->rx_prod, rxr->rx_doorbell);
+ bp->grp_info[map_idx].rx_fw_ring_id = ring->fw_ring_id;
}
if (bp->flags & BNXT_FLAG_AGG_RINGS) {
for (i = 0; i < bp->rx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
+ struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i];
struct bnxt_ring_struct *ring =
&rxr->rx_agg_ring_struct;
+ u32 grp_idx = rxr->bnapi->index;
+ u32 map_idx = grp_idx + bp->rx_nr_rings;
rc = hwrm_ring_alloc_send_msg(bp, ring,
HWRM_RING_ALLOC_AGG,
- bp->rx_nr_rings + i,
+ map_idx,
INVALID_STATS_CTX_ID);
if (rc)
goto err_out;
- rxr->rx_agg_doorbell =
- bp->bar1 + (bp->rx_nr_rings + i) * 0x80;
+ rxr->rx_agg_doorbell = bp->bar1 + map_idx * 0x80;
writel(DB_KEY_RX | rxr->rx_agg_prod,
rxr->rx_agg_doorbell);
- bp->grp_info[i].agg_fw_ring_id = ring->fw_ring_id;
+ bp->grp_info[grp_idx].agg_fw_ring_id = ring->fw_ring_id;
}
}
err_out:
@@ -3409,91 +3437,75 @@ static int hwrm_ring_free_send_msg(struct bnxt *bp,
return 0;
}
-static int bnxt_hwrm_ring_free(struct bnxt *bp, bool close_path)
+static void bnxt_hwrm_ring_free(struct bnxt *bp, bool close_path)
{
- int i, rc = 0;
+ int i;
if (!bp->bnapi)
- return 0;
+ return;
- if (bp->tx_nr_rings) {
- for (i = 0; i < bp->tx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_tx_ring_info *txr = &bnapi->tx_ring;
- struct bnxt_ring_struct *ring = &txr->tx_ring_struct;
- u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id;
-
- if (ring->fw_ring_id != INVALID_HW_RING_ID) {
- hwrm_ring_free_send_msg(
- bp, ring,
- RING_FREE_REQ_RING_TYPE_TX,
- close_path ? cmpl_ring_id :
- INVALID_HW_RING_ID);
- ring->fw_ring_id = INVALID_HW_RING_ID;
- }
+ for (i = 0; i < bp->tx_nr_rings; i++) {
+ struct bnxt_tx_ring_info *txr = &bp->tx_ring[i];
+ struct bnxt_ring_struct *ring = &txr->tx_ring_struct;
+ u32 grp_idx = txr->bnapi->index;
+ u32 cmpl_ring_id = bp->grp_info[grp_idx].cp_fw_ring_id;
+
+ if (ring->fw_ring_id != INVALID_HW_RING_ID) {
+ hwrm_ring_free_send_msg(bp, ring,
+ RING_FREE_REQ_RING_TYPE_TX,
+ close_path ? cmpl_ring_id :
+ INVALID_HW_RING_ID);
+ ring->fw_ring_id = INVALID_HW_RING_ID;
}
}
- if (bp->rx_nr_rings) {
- for (i = 0; i < bp->rx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
- struct bnxt_ring_struct *ring = &rxr->rx_ring_struct;
- u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id;
-
- if (ring->fw_ring_id != INVALID_HW_RING_ID) {
- hwrm_ring_free_send_msg(
- bp, ring,
- RING_FREE_REQ_RING_TYPE_RX,
- close_path ? cmpl_ring_id :
- INVALID_HW_RING_ID);
- ring->fw_ring_id = INVALID_HW_RING_ID;
- bp->grp_info[i].rx_fw_ring_id =
- INVALID_HW_RING_ID;
- }
+ for (i = 0; i < bp->rx_nr_rings; i++) {
+ struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i];
+ struct bnxt_ring_struct *ring = &rxr->rx_ring_struct;
+ u32 grp_idx = rxr->bnapi->index;
+ u32 cmpl_ring_id = bp->grp_info[grp_idx].cp_fw_ring_id;
+
+ if (ring->fw_ring_id != INVALID_HW_RING_ID) {
+ hwrm_ring_free_send_msg(bp, ring,
+ RING_FREE_REQ_RING_TYPE_RX,
+ close_path ? cmpl_ring_id :
+ INVALID_HW_RING_ID);
+ ring->fw_ring_id = INVALID_HW_RING_ID;
+ bp->grp_info[grp_idx].rx_fw_ring_id =
+ INVALID_HW_RING_ID;
}
}
- if (bp->rx_agg_nr_pages) {
- for (i = 0; i < bp->rx_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_rx_ring_info *rxr = &bnapi->rx_ring;
- struct bnxt_ring_struct *ring =
- &rxr->rx_agg_ring_struct;
- u32 cmpl_ring_id = bp->grp_info[i].cp_fw_ring_id;
-
- if (ring->fw_ring_id != INVALID_HW_RING_ID) {
- hwrm_ring_free_send_msg(
- bp, ring,
- RING_FREE_REQ_RING_TYPE_RX,
- close_path ? cmpl_ring_id :
- INVALID_HW_RING_ID);
- ring->fw_ring_id = INVALID_HW_RING_ID;
- bp->grp_info[i].agg_fw_ring_id =
- INVALID_HW_RING_ID;
- }
+ for (i = 0; i < bp->rx_nr_rings; i++) {
+ struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i];
+ struct bnxt_ring_struct *ring = &rxr->rx_agg_ring_struct;
+ u32 grp_idx = rxr->bnapi->index;
+ u32 cmpl_ring_id = bp->grp_info[grp_idx].cp_fw_ring_id;
+
+ if (ring->fw_ring_id != INVALID_HW_RING_ID) {
+ hwrm_ring_free_send_msg(bp, ring,
+ RING_FREE_REQ_RING_TYPE_RX,
+ close_path ? cmpl_ring_id :
+ INVALID_HW_RING_ID);
+ ring->fw_ring_id = INVALID_HW_RING_ID;
+ bp->grp_info[grp_idx].agg_fw_ring_id =
+ INVALID_HW_RING_ID;
}
}
- if (bp->cp_nr_rings) {
- for (i = 0; i < bp->cp_nr_rings; i++) {
- struct bnxt_napi *bnapi = bp->bnapi[i];
- struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
- struct bnxt_ring_struct *ring = &cpr->cp_ring_struct;
-
- if (ring->fw_ring_id != INVALID_HW_RING_ID) {
- hwrm_ring_free_send_msg(
- bp, ring,
- RING_FREE_REQ_RING_TYPE_CMPL,
- INVALID_HW_RING_ID);
- ring->fw_ring_id = INVALID_HW_RING_ID;
- bp->grp_info[i].cp_fw_ring_id =
- INVALID_HW_RING_ID;
- }
+ for (i = 0; i < bp->cp_nr_rings; i++) {
+ struct bnxt_napi *bnapi = bp->bnapi[i];
+ struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+ struct bnxt_ring_struct *ring = &cpr->cp_ring_struct;
+
+ if (ring->fw_ring_id != INVALID_HW_RING_ID) {
+ hwrm_ring_free_send_msg(bp, ring,
+ RING_FREE_REQ_RING_TYPE_CMPL,
+ INVALID_HW_RING_ID);
+ ring->fw_ring_id = INVALID_HW_RING_ID;
+ bp->grp_info[i].cp_fw_ring_id = INVALID_HW_RING_ID;
}
}
-
- return rc;
}
int bnxt_hwrm_set_coal(struct bnxt *bp)
@@ -3605,7 +3617,7 @@ static int bnxt_hwrm_stat_ctx_alloc(struct bnxt *bp)
return 0;
}
-static int bnxt_hwrm_func_qcaps(struct bnxt *bp)
+int bnxt_hwrm_func_qcaps(struct bnxt *bp)
{
int rc = 0;
struct hwrm_func_qcaps_input req = {0};
@@ -3625,12 +3637,14 @@ static int bnxt_hwrm_func_qcaps(struct bnxt *bp)
pf->fw_fid = le16_to_cpu(resp->fid);
pf->port_id = le16_to_cpu(resp->port_id);
memcpy(pf->mac_addr, resp->perm_mac_address, ETH_ALEN);
+ memcpy(bp->dev->dev_addr, pf->mac_addr, ETH_ALEN);
pf->max_rsscos_ctxs = le16_to_cpu(resp->max_rsscos_ctx);
pf->max_cp_rings = le16_to_cpu(resp->max_cmpl_rings);
pf->max_tx_rings = le16_to_cpu(resp->max_tx_rings);
- pf->max_pf_tx_rings = pf->max_tx_rings;
pf->max_rx_rings = le16_to_cpu(resp->max_rx_rings);
- pf->max_pf_rx_rings = pf->max_rx_rings;
+ pf->max_hw_ring_grps = le32_to_cpu(resp->max_hw_ring_grps);
+ if (!pf->max_hw_ring_grps)
+ pf->max_hw_ring_grps = pf->max_tx_rings;
pf->max_l2_ctxs = le16_to_cpu(resp->max_l2_ctxs);
pf->max_vnics = le16_to_cpu(resp->max_vnics);
pf->max_stat_ctxs = le16_to_cpu(resp->max_stat_ctx);
@@ -3648,13 +3662,19 @@ static int bnxt_hwrm_func_qcaps(struct bnxt *bp)
vf->fw_fid = le16_to_cpu(resp->fid);
memcpy(vf->mac_addr, resp->perm_mac_address, ETH_ALEN);
- if (!is_valid_ether_addr(vf->mac_addr))
- random_ether_addr(vf->mac_addr);
+ if (is_valid_ether_addr(vf->mac_addr))
+ /* overwrite netdev dev_adr with admin VF MAC */
+ memcpy(bp->dev->dev_addr, vf->mac_addr, ETH_ALEN);
+ else
+ random_ether_addr(bp->dev->dev_addr);
vf->max_rsscos_ctxs = le16_to_cpu(resp->max_rsscos_ctx);
vf->max_cp_rings = le16_to_cpu(resp->max_cmpl_rings);
vf->max_tx_rings = le16_to_cpu(resp->max_tx_rings);
vf->max_rx_rings = le16_to_cpu(resp->max_rx_rings);
+ vf->max_hw_ring_grps = le32_to_cpu(resp->max_hw_ring_grps);
+ if (!vf->max_hw_ring_grps)
+ vf->max_hw_ring_grps = vf->max_tx_rings;
vf->max_l2_ctxs = le16_to_cpu(resp->max_l2_ctxs);
vf->max_vnics = le16_to_cpu(resp->max_vnics);
vf->max_stat_ctxs = le16_to_cpu(resp->max_stat_ctx);
@@ -3731,14 +3751,11 @@ static int bnxt_hwrm_ver_get(struct bnxt *bp)
memcpy(&bp->ver_resp, resp, sizeof(struct hwrm_ver_get_output));
- if (req.hwrm_intf_maj != resp->hwrm_intf_maj ||
- req.hwrm_intf_min != resp->hwrm_intf_min ||
- req.hwrm_intf_upd != resp->hwrm_intf_upd) {
- netdev_warn(bp->dev, "HWRM interface %d.%d.%d does not match driver interface %d.%d.%d.\n",
+ if (resp->hwrm_intf_maj < 1) {
+ netdev_warn(bp->dev, "HWRM interface %d.%d.%d is older than 1.0.0.\n",
resp->hwrm_intf_maj, resp->hwrm_intf_min,
- resp->hwrm_intf_upd, req.hwrm_intf_maj,
- req.hwrm_intf_min, req.hwrm_intf_upd);
- netdev_warn(bp->dev, "Please update driver or firmware with matching interface versions.\n");
+ resp->hwrm_intf_upd);
+ netdev_warn(bp->dev, "Please update firmware with HWRM interface 1.0.0 or newer.\n");
}
snprintf(bp->fw_ver_str, BC_HWRM_STR_LEN, "bc %d.%d.%d rm %d.%d.%d",
resp->hwrm_fw_maj, resp->hwrm_fw_min, resp->hwrm_fw_bld,
@@ -3864,7 +3881,7 @@ static int bnxt_alloc_rfs_vnics(struct bnxt *bp)
break;
bp->vnic_info[vnic_id].flags |= BNXT_VNIC_RFS_FLAG;
- rc = bnxt_hwrm_vnic_alloc(bp, vnic_id, ring_id, ring_id + 1);
+ rc = bnxt_hwrm_vnic_alloc(bp, vnic_id, ring_id, 1);
if (rc) {
netdev_err(bp->dev, "hwrm vnic %d alloc failure rc: %x\n",
vnic_id, rc);
@@ -3880,6 +3897,8 @@ static int bnxt_alloc_rfs_vnics(struct bnxt *bp)
#endif
}
+static int bnxt_cfg_rx_mode(struct bnxt *);
+
static int bnxt_init_chip(struct bnxt *bp, bool irq_re_init)
{
int rc = 0;
@@ -3939,18 +3958,15 @@ static int bnxt_init_chip(struct bnxt *bp, bool irq_re_init)
}
bp->vnic_info[0].uc_filter_count = 1;
- bp->vnic_info[0].rx_mask = CFA_L2_SET_RX_MASK_REQ_MASK_UNICAST |
- CFA_L2_SET_RX_MASK_REQ_MASK_BCAST;
+ bp->vnic_info[0].rx_mask = CFA_L2_SET_RX_MASK_REQ_MASK_BCAST;
if ((bp->dev->flags & IFF_PROMISC) && BNXT_PF(bp))
bp->vnic_info[0].rx_mask |=
CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS;
- rc = bnxt_hwrm_cfa_l2_set_rx_mask(bp, 0);
- if (rc) {
- netdev_err(bp->dev, "HWRM cfa l2 rx mask failure rc: %x\n", rc);
+ rc = bnxt_cfg_rx_mode(bp);
+ if (rc)
goto err_out;
- }
rc = bnxt_hwrm_set_coal(bp);
if (rc)
@@ -4023,20 +4039,42 @@ static int bnxt_set_real_num_queues(struct bnxt *bp)
return rc;
#ifdef CONFIG_RFS_ACCEL
- if (bp->rx_nr_rings)
+ if (bp->flags & BNXT_FLAG_RFS)
dev->rx_cpu_rmap = alloc_irq_cpu_rmap(bp->rx_nr_rings);
- if (!dev->rx_cpu_rmap)
- rc = -ENOMEM;
#endif
return rc;
}
+static int bnxt_trim_rings(struct bnxt *bp, int *rx, int *tx, int max,
+ bool shared)
+{
+ int _rx = *rx, _tx = *tx;
+
+ if (shared) {
+ *rx = min_t(int, _rx, max);
+ *tx = min_t(int, _tx, max);
+ } else {
+ if (max < 2)
+ return -ENOMEM;
+
+ while (_rx + _tx > max) {
+ if (_rx > _tx && _rx > 1)
+ _rx--;
+ else if (_tx > 1)
+ _tx--;
+ }
+ *rx = _rx;
+ *tx = _tx;
+ }
+ return 0;
+}
+
static int bnxt_setup_msix(struct bnxt *bp)
{
struct msix_entry *msix_ent;
struct net_device *dev = bp->dev;
- int i, total_vecs, rc = 0;
+ int i, total_vecs, rc = 0, min = 1;
const int len = sizeof(bp->irq_tbl[0].name);
bp->flags &= ~BNXT_FLAG_USING_MSIX;
@@ -4051,7 +4089,10 @@ static int bnxt_setup_msix(struct bnxt *bp)
msix_ent[i].vector = 0;
}
- total_vecs = pci_enable_msix_range(bp->pdev, msix_ent, 1, total_vecs);
+ if (!(bp->flags & BNXT_FLAG_SHARED_RINGS))
+ min = 2;
+
+ total_vecs = pci_enable_msix_range(bp->pdev, msix_ent, min, total_vecs);
if (total_vecs < 0) {
rc = -ENODEV;
goto msix_setup_exit;
@@ -4062,8 +4103,11 @@ static int bnxt_setup_msix(struct bnxt *bp)
int tcs;
/* Trim rings based upon num of vectors allocated */
- bp->rx_nr_rings = min_t(int, total_vecs, bp->rx_nr_rings);
- bp->tx_nr_rings = min_t(int, total_vecs, bp->tx_nr_rings);
+ rc = bnxt_trim_rings(bp, &bp->rx_nr_rings, &bp->tx_nr_rings,
+ total_vecs, min == 1);
+ if (rc)
+ goto msix_setup_exit;
+
bp->tx_nr_rings_per_tc = bp->tx_nr_rings;
tcs = netdev_get_num_tc(dev);
if (tcs > 1) {
@@ -4082,12 +4126,21 @@ static int bnxt_setup_msix(struct bnxt *bp)
}
}
}
- bp->cp_nr_rings = max_t(int, bp->rx_nr_rings, bp->tx_nr_rings);
+ bp->cp_nr_rings = total_vecs;
for (i = 0; i < bp->cp_nr_rings; i++) {
+ char *attr;
+
bp->irq_tbl[i].vector = msix_ent[i].vector;
+ if (bp->flags & BNXT_FLAG_SHARED_RINGS)
+ attr = "TxRx";
+ else if (i < bp->rx_nr_rings)
+ attr = "rx";
+ else
+ attr = "tx";
+
snprintf(bp->irq_tbl[i].name, len,
- "%s-%s-%d", dev->name, "TxRx", i);
+ "%s-%s-%d", dev->name, attr, i);
bp->irq_tbl[i].handler = bnxt_msix;
}
rc = bnxt_set_real_num_queues(bp);
@@ -4125,6 +4178,7 @@ static int bnxt_setup_inta(struct bnxt *bp)
bp->tx_nr_rings = 1;
bp->cp_nr_rings = 1;
bp->tx_nr_rings_per_tc = bp->tx_nr_rings;
+ bp->flags |= BNXT_FLAG_SHARED_RINGS;
bp->irq_tbl[0].vector = bp->pdev->irq;
snprintf(bp->irq_tbl[0].name, len,
"%s-%s-%d", bp->dev->name, "TxRx", 0);
@@ -4173,7 +4227,7 @@ static void bnxt_free_irq(struct bnxt *bp)
static int bnxt_request_irq(struct bnxt *bp)
{
- int i, rc = 0;
+ int i, j, rc = 0;
unsigned long flags = 0;
#ifdef CONFIG_RFS_ACCEL
struct cpu_rmap *rmap = bp->dev->rx_cpu_rmap;
@@ -4182,14 +4236,15 @@ static int bnxt_request_irq(struct bnxt *bp)
if (!(bp->flags & BNXT_FLAG_USING_MSIX))
flags = IRQF_SHARED;
- for (i = 0; i < bp->cp_nr_rings; i++) {
+ for (i = 0, j = 0; i < bp->cp_nr_rings; i++) {
struct bnxt_irq *irq = &bp->irq_tbl[i];
#ifdef CONFIG_RFS_ACCEL
- if (rmap && (i < bp->rx_nr_rings)) {
+ if (rmap && bp->bnapi[i]->rx_ring) {
rc = irq_cpu_rmap_add(rmap, irq->vector);
if (rc)
netdev_warn(bp->dev, "failed adding irq rmap for ring %d\n",
- i);
+ j);
+ j++;
}
#endif
rc = request_irq(irq->vector, irq->handler, flags, irq->name,
@@ -4227,12 +4282,10 @@ static void bnxt_init_napi(struct bnxt *bp)
bnapi = bp->bnapi[i];
netif_napi_add(bp->dev, &bnapi->napi,
bnxt_poll, 64);
- napi_hash_add(&bnapi->napi);
}
} else {
bnapi = bp->bnapi[0];
netif_napi_add(bp->dev, &bnapi->napi, bnxt_poll, 64);
- napi_hash_add(&bnapi->napi);
}
}
@@ -4262,14 +4315,12 @@ static void bnxt_enable_napi(struct bnxt *bp)
static void bnxt_tx_disable(struct bnxt *bp)
{
int i;
- struct bnxt_napi *bnapi;
struct bnxt_tx_ring_info *txr;
struct netdev_queue *txq;
- if (bp->bnapi) {
+ if (bp->tx_ring) {
for (i = 0; i < bp->tx_nr_rings; i++) {
- bnapi = bp->bnapi[i];
- txr = &bnapi->tx_ring;
+ txr = &bp->tx_ring[i];
txq = netdev_get_tx_queue(bp->dev, i);
__netif_tx_lock(txq, smp_processor_id());
txr->dev_state = BNXT_DEV_STATE_CLOSING;
@@ -4284,13 +4335,11 @@ static void bnxt_tx_disable(struct bnxt *bp)
static void bnxt_tx_enable(struct bnxt *bp)
{
int i;
- struct bnxt_napi *bnapi;
struct bnxt_tx_ring_info *txr;
struct netdev_queue *txq;
for (i = 0; i < bp->tx_nr_rings; i++) {
- bnapi = bp->bnapi[i];
- txr = &bnapi->tx_ring;
+ txr = &bp->tx_ring[i];
txq = netdev_get_tx_queue(bp->dev, i);
txr->dev_state = 0;
}
@@ -4352,7 +4401,7 @@ static int bnxt_update_link(struct bnxt *bp, bool chng_link_state)
link_info->auto_mode = resp->auto_mode;
link_info->auto_pause_setting = resp->auto_pause;
link_info->force_pause_setting = resp->force_pause;
- link_info->duplex_setting = resp->duplex_setting;
+ link_info->duplex_setting = resp->duplex;
if (link_info->phy_link_status == BNXT_LINK_LINK)
link_info->link_speed = le16_to_cpu(resp->link_speed);
else
@@ -4599,7 +4648,7 @@ static int __bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init)
bp->nge_port_cnt = 1;
}
- bp->state = BNXT_STATE_OPEN;
+ set_bit(BNXT_STATE_OPEN, &bp->state);
bnxt_enable_int(bp);
/* Enable TX queues */
bnxt_tx_enable(bp);
@@ -4675,8 +4724,10 @@ int bnxt_close_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init)
/* Change device state to avoid TX queue wake up's */
bnxt_tx_disable(bp);
- bp->state = BNXT_STATE_CLOSED;
- cancel_work_sync(&bp->sp_task);
+ clear_bit(BNXT_STATE_OPEN, &bp->state);
+ smp_mb__after_atomic();
+ while (test_bit(BNXT_STATE_IN_SP_TASK, &bp->state))
+ msleep(20);
/* Flush rings before disabling interrupts */
bnxt_shutdown_nic(bp, irq_re_init);
@@ -4865,7 +4916,7 @@ static void bnxt_set_rx_mode(struct net_device *dev)
}
}
-static void bnxt_cfg_rx_mode(struct bnxt *bp)
+static int bnxt_cfg_rx_mode(struct bnxt *bp)
{
struct net_device *dev = bp->dev;
struct bnxt_vnic_info *vnic = &bp->vnic_info[0];
@@ -4914,6 +4965,7 @@ static void bnxt_cfg_rx_mode(struct bnxt *bp)
netdev_err(bp->dev, "HWRM vnic filter failure rc: %x\n",
rc);
vnic->uc_filter_count = i;
+ return rc;
}
}
@@ -4922,11 +4974,36 @@ skip_uc:
if (rc)
netdev_err(bp->dev, "HWRM cfa l2 rx mask failure rc: %x\n",
rc);
+
+ return rc;
+}
+
+static bool bnxt_rfs_capable(struct bnxt *bp)
+{
+#ifdef CONFIG_RFS_ACCEL
+ struct bnxt_pf_info *pf = &bp->pf;
+ int vnics;
+
+ if (BNXT_VF(bp) || !(bp->flags & BNXT_FLAG_MSIX_CAP))
+ return false;
+
+ vnics = 1 + bp->rx_nr_rings;
+ if (vnics > pf->max_rsscos_ctxs || vnics > pf->max_vnics)
+ return false;
+
+ return true;
+#else
+ return false;
+#endif
}
static netdev_features_t bnxt_fix_features(struct net_device *dev,
netdev_features_t features)
{
+ struct bnxt *bp = netdev_priv(dev);
+
+ if (!bnxt_rfs_capable(bp))
+ features &= ~NETIF_F_NTUPLE;
return features;
}
@@ -4967,7 +5044,7 @@ static int bnxt_set_features(struct net_device *dev, netdev_features_t features)
bp->flags = flags;
- if (!netif_running(dev)) {
+ if (!test_bit(BNXT_STATE_OPEN, &bp->state)) {
if (update_tpa)
bnxt_set_ring_params(bp);
return rc;
@@ -4991,31 +5068,53 @@ static int bnxt_set_features(struct net_device *dev, netdev_features_t features)
return rc;
}
+static void bnxt_dump_tx_sw_state(struct bnxt_napi *bnapi)
+{
+ struct bnxt_tx_ring_info *txr = bnapi->tx_ring;
+ int i = bnapi->index;
+
+ if (!txr)
+ return;
+
+ netdev_info(bnapi->bp->dev, "[%d]: tx{fw_ring: %d prod: %x cons: %x}\n",
+ i, txr->tx_ring_struct.fw_ring_id, txr->tx_prod,
+ txr->tx_cons);
+}
+
+static void bnxt_dump_rx_sw_state(struct bnxt_napi *bnapi)
+{
+ struct bnxt_rx_ring_info *rxr = bnapi->rx_ring;
+ int i = bnapi->index;
+
+ if (!rxr)
+ return;
+
+ netdev_info(bnapi->bp->dev, "[%d]: rx{fw_ring: %d prod: %x} rx_agg{fw_ring: %d agg_prod: %x sw_agg_prod: %x}\n",
+ i, rxr->rx_ring_struct.fw_ring_id, rxr->rx_prod,
+ rxr->rx_agg_ring_struct.fw_ring_id, rxr->rx_agg_prod,
+ rxr->rx_sw_agg_prod);
+}
+
+static void bnxt_dump_cp_sw_state(struct bnxt_napi *bnapi)
+{
+ struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
+ int i = bnapi->index;
+
+ netdev_info(bnapi->bp->dev, "[%d]: cp{fw_ring: %d raw_cons: %x}\n",
+ i, cpr->cp_ring_struct.fw_ring_id, cpr->cp_raw_cons);
+}
+
static void bnxt_dbg_dump_states(struct bnxt *bp)
{
int i;
struct bnxt_napi *bnapi;
- struct bnxt_tx_ring_info *txr;
- struct bnxt_rx_ring_info *rxr;
- struct bnxt_cp_ring_info *cpr;
for (i = 0; i < bp->cp_nr_rings; i++) {
bnapi = bp->bnapi[i];
- txr = &bnapi->tx_ring;
- rxr = &bnapi->rx_ring;
- cpr = &bnapi->cp_ring;
if (netif_msg_drv(bp)) {
- netdev_info(bp->dev, "[%d]: tx{fw_ring: %d prod: %x cons: %x}\n",
- i, txr->tx_ring_struct.fw_ring_id,
- txr->tx_prod, txr->tx_cons);
- netdev_info(bp->dev, "[%d]: rx{fw_ring: %d prod: %x} rx_agg{fw_ring: %d agg_prod: %x sw_agg_prod: %x}\n",
- i, rxr->rx_ring_struct.fw_ring_id,
- rxr->rx_prod,
- rxr->rx_agg_ring_struct.fw_ring_id,
- rxr->rx_agg_prod, rxr->rx_sw_agg_prod);
- netdev_info(bp->dev, "[%d]: cp{fw_ring: %d raw_cons: %x}\n",
- i, cpr->cp_ring_struct.fw_ring_id,
- cpr->cp_raw_cons);
+ bnxt_dump_tx_sw_state(bnapi);
+ bnxt_dump_rx_sw_state(bnapi);
+ bnxt_dump_cp_sw_state(bnapi);
}
}
}
@@ -5023,8 +5122,10 @@ static void bnxt_dbg_dump_states(struct bnxt *bp)
static void bnxt_reset_task(struct bnxt *bp)
{
bnxt_dbg_dump_states(bp);
- if (netif_running(bp->dev))
- bnxt_tx_disable(bp); /* prevent tx timout again */
+ if (netif_running(bp->dev)) {
+ bnxt_close_nic(bp, false, false);
+ bnxt_open_nic(bp, false, false);
+ }
}
static void bnxt_tx_timeout(struct net_device *dev)
@@ -5074,8 +5175,12 @@ static void bnxt_sp_task(struct work_struct *work)
struct bnxt *bp = container_of(work, struct bnxt, sp_task);
int rc;
- if (bp->state != BNXT_STATE_OPEN)
+ set_bit(BNXT_STATE_IN_SP_TASK, &bp->state);
+ smp_mb__after_atomic();
+ if (!test_bit(BNXT_STATE_OPEN, &bp->state)) {
+ clear_bit(BNXT_STATE_IN_SP_TASK, &bp->state);
return;
+ }
if (test_and_clear_bit(BNXT_RX_MASK_SP_EVENT, &bp->sp_event))
bnxt_cfg_rx_mode(bp);
@@ -5099,8 +5204,19 @@ static void bnxt_sp_task(struct work_struct *work)
bnxt_hwrm_tunnel_dst_port_free(
bp, TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_VXLAN);
}
- if (test_and_clear_bit(BNXT_RESET_TASK_SP_EVENT, &bp->sp_event))
+ if (test_and_clear_bit(BNXT_RESET_TASK_SP_EVENT, &bp->sp_event)) {
+ /* bnxt_reset_task() calls bnxt_close_nic() which waits
+ * for BNXT_STATE_IN_SP_TASK to clear.
+ */
+ clear_bit(BNXT_STATE_IN_SP_TASK, &bp->state);
+ rtnl_lock();
bnxt_reset_task(bp);
+ set_bit(BNXT_STATE_IN_SP_TASK, &bp->state);
+ rtnl_unlock();
+ }
+
+ smp_mb__before_atomic();
+ clear_bit(BNXT_STATE_IN_SP_TASK, &bp->state);
}
static int bnxt_init_board(struct pci_dev *pdev, struct net_device *dev)
@@ -5179,7 +5295,7 @@ static int bnxt_init_board(struct pci_dev *pdev, struct net_device *dev)
bp->timer.function = bnxt_timer;
bp->current_interval = BNXT_TIMER_INTERVAL;
- bp->state = BNXT_STATE_CLOSED;
+ clear_bit(BNXT_STATE_OPEN, &bp->state);
return 0;
@@ -5212,13 +5328,27 @@ init_err:
static int bnxt_change_mac_addr(struct net_device *dev, void *p)
{
struct sockaddr *addr = p;
+ struct bnxt *bp = netdev_priv(dev);
+ int rc = 0;
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
+#ifdef CONFIG_BNXT_SRIOV
+ if (BNXT_VF(bp) && is_valid_ether_addr(bp->vf.mac_addr))
+ return -EADDRNOTAVAIL;
+#endif
+
+ if (ether_addr_equal(addr->sa_data, dev->dev_addr))
+ return 0;
+
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+ if (netif_running(dev)) {
+ bnxt_close_nic(bp, false, false);
+ rc = bnxt_open_nic(bp, false, false);
+ }
- return 0;
+ return rc;
}
/* rtnl_lock held */
@@ -5255,10 +5385,14 @@ static int bnxt_setup_tc(struct net_device *dev, u8 tc)
return 0;
if (tc) {
- int max_rx_rings, max_tx_rings;
+ int max_rx_rings, max_tx_rings, rc;
+ bool sh = false;
- bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings);
- if (bp->tx_nr_rings_per_tc * tc > max_tx_rings)
+ if (bp->flags & BNXT_FLAG_SHARED_RINGS)
+ sh = true;
+
+ rc = bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, sh);
+ if (rc || bp->tx_nr_rings_per_tc * tc > max_tx_rings)
return -ENOMEM;
}
@@ -5512,6 +5646,7 @@ static void bnxt_remove_one(struct pci_dev *pdev)
cancel_work_sync(&bp->sp_task);
bp->sp_event = 0;
+ bnxt_hwrm_func_drv_unrgtr(bp);
bnxt_free_hwrm_resources(bp);
pci_iounmap(pdev, bp->bar2);
pci_iounmap(pdev, bp->bar1);
@@ -5571,28 +5706,64 @@ static int bnxt_get_max_irq(struct pci_dev *pdev)
return (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1;
}
-void bnxt_get_max_rings(struct bnxt *bp, int *max_rx, int *max_tx)
+static void _bnxt_get_max_rings(struct bnxt *bp, int *max_rx, int *max_tx,
+ int *max_cp)
{
- int max_rings = 0;
+ int max_ring_grps = 0;
- if (BNXT_PF(bp)) {
- *max_tx = bp->pf.max_pf_tx_rings;
- *max_rx = bp->pf.max_pf_rx_rings;
- max_rings = min_t(int, bp->pf.max_irqs, bp->pf.max_cp_rings);
- max_rings = min_t(int, max_rings, bp->pf.max_stat_ctxs);
- } else {
#ifdef CONFIG_BNXT_SRIOV
+ if (!BNXT_PF(bp)) {
*max_tx = bp->vf.max_tx_rings;
*max_rx = bp->vf.max_rx_rings;
- max_rings = min_t(int, bp->vf.max_irqs, bp->vf.max_cp_rings);
- max_rings = min_t(int, max_rings, bp->vf.max_stat_ctxs);
+ *max_cp = min_t(int, bp->vf.max_irqs, bp->vf.max_cp_rings);
+ *max_cp = min_t(int, *max_cp, bp->vf.max_stat_ctxs);
+ max_ring_grps = bp->vf.max_hw_ring_grps;
+ } else
#endif
+ {
+ *max_tx = bp->pf.max_tx_rings;
+ *max_rx = bp->pf.max_rx_rings;
+ *max_cp = min_t(int, bp->pf.max_irqs, bp->pf.max_cp_rings);
+ *max_cp = min_t(int, *max_cp, bp->pf.max_stat_ctxs);
+ max_ring_grps = bp->pf.max_hw_ring_grps;
}
+
if (bp->flags & BNXT_FLAG_AGG_RINGS)
*max_rx >>= 1;
+ *max_rx = min_t(int, *max_rx, max_ring_grps);
+}
+
+int bnxt_get_max_rings(struct bnxt *bp, int *max_rx, int *max_tx, bool shared)
+{
+ int rx, tx, cp;
+
+ _bnxt_get_max_rings(bp, &rx, &tx, &cp);
+ if (!rx || !tx || !cp)
+ return -ENOMEM;
+
+ *max_rx = rx;
+ *max_tx = tx;
+ return bnxt_trim_rings(bp, max_rx, max_tx, cp, shared);
+}
+
+static int bnxt_set_dflt_rings(struct bnxt *bp)
+{
+ int dflt_rings, max_rx_rings, max_tx_rings, rc;
+ bool sh = true;
- *max_rx = min_t(int, *max_rx, max_rings);
- *max_tx = min_t(int, *max_tx, max_rings);
+ if (sh)
+ bp->flags |= BNXT_FLAG_SHARED_RINGS;
+ dflt_rings = netif_get_num_default_rss_queues();
+ rc = bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, sh);
+ if (rc)
+ return rc;
+ bp->rx_nr_rings = min_t(int, dflt_rings, max_rx_rings);
+ bp->tx_nr_rings_per_tc = min_t(int, dflt_rings, max_tx_rings);
+ bp->tx_nr_rings = bp->tx_nr_rings_per_tc;
+ bp->cp_nr_rings = sh ? max_t(int, bp->tx_nr_rings, bp->rx_nr_rings) :
+ bp->tx_nr_rings + bp->rx_nr_rings;
+ bp->num_stat_ctxs = bp->cp_nr_rings;
+ return rc;
}
static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
@@ -5600,7 +5771,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
static int version_printed;
struct net_device *dev;
struct bnxt *bp;
- int rc, max_rx_rings, max_tx_rings, max_irqs, dflt_rings;
+ int rc, max_irqs;
if (version_printed++ == 0)
pr_info("%s", version);
@@ -5615,11 +5786,8 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (bnxt_vf_pciid(ent->driver_data))
bp->flags |= BNXT_FLAG_VF;
- if (pdev->msix_cap) {
+ if (pdev->msix_cap)
bp->flags |= BNXT_FLAG_MSIX_CAP;
- if (BNXT_PF(bp))
- bp->flags |= BNXT_FLAG_RFS;
- }
rc = bnxt_init_board(pdev, dev);
if (rc < 0)
@@ -5638,9 +5806,6 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
NETIF_F_RXHASH |
NETIF_F_RXCSUM | NETIF_F_LRO | NETIF_F_GRO;
- if (bp->flags & BNXT_FLAG_RFS)
- dev->hw_features |= NETIF_F_NTUPLE;
-
dev->hw_enc_features =
NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG |
NETIF_F_TSO | NETIF_F_TSO6 |
@@ -5685,22 +5850,21 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
bnxt_set_tpa_flags(bp);
bnxt_set_ring_params(bp);
- dflt_rings = netif_get_num_default_rss_queues();
- if (BNXT_PF(bp)) {
- memcpy(dev->dev_addr, bp->pf.mac_addr, ETH_ALEN);
+ if (BNXT_PF(bp))
bp->pf.max_irqs = max_irqs;
- } else {
#if defined(CONFIG_BNXT_SRIOV)
- memcpy(dev->dev_addr, bp->vf.mac_addr, ETH_ALEN);
+ else
bp->vf.max_irqs = max_irqs;
#endif
+ bnxt_set_dflt_rings(bp);
+
+ if (BNXT_PF(bp)) {
+ dev->hw_features |= NETIF_F_NTUPLE;
+ if (bnxt_rfs_capable(bp)) {
+ bp->flags |= BNXT_FLAG_RFS;
+ dev->features |= NETIF_F_NTUPLE;
+ }
}
- bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings);
- bp->rx_nr_rings = min_t(int, dflt_rings, max_rx_rings);
- bp->tx_nr_rings_per_tc = min_t(int, dflt_rings, max_tx_rings);
- bp->tx_nr_rings = bp->tx_nr_rings_per_tc;
- bp->cp_nr_rings = max_t(int, bp->rx_nr_rings, bp->tx_nr_rings);
- bp->num_stat_ctxs = bp->cp_nr_rings;
if (dev->hw_features & NETIF_F_HW_VLAN_CTAG_RX)
bp->flags |= BNXT_FLAG_STRIP_VLAN;
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index 674bc5159b91..8af3ca8efcef 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -11,11 +11,11 @@
#define BNXT_H
#define DRV_MODULE_NAME "bnxt_en"
-#define DRV_MODULE_VERSION "0.1.24"
+#define DRV_MODULE_VERSION "1.0.0"
-#define DRV_VER_MAJ 0
-#define DRV_VER_MIN 1
-#define DRV_VER_UPD 24
+#define DRV_VER_MAJ 1
+#define DRV_VER_MIN 0
+#define DRV_VER_UPD 0
struct tx_bd {
__le32 tx_bd_len_flags_type;
@@ -528,6 +528,7 @@ struct tx_push_bd {
};
struct bnxt_tx_ring_info {
+ struct bnxt_napi *bnapi;
u16 tx_prod;
u16 tx_cons;
void __iomem *tx_doorbell;
@@ -558,6 +559,7 @@ struct bnxt_tpa_info {
};
struct bnxt_rx_ring_info {
+ struct bnxt_napi *bnapi;
u16 rx_prod;
u16 rx_agg_prod;
u16 rx_sw_agg_prod;
@@ -604,8 +606,8 @@ struct bnxt_napi {
int index;
struct bnxt_cp_ring_info cp_ring;
- struct bnxt_rx_ring_info rx_ring;
- struct bnxt_tx_ring_info tx_ring;
+ struct bnxt_rx_ring_info *rx_ring;
+ struct bnxt_tx_ring_info *tx_ring;
#ifdef CONFIG_NET_RX_BUSY_POLL
atomic_t poll_state;
@@ -695,6 +697,7 @@ struct bnxt_vf_info {
u16 max_cp_rings;
u16 max_tx_rings;
u16 max_rx_rings;
+ u16 max_hw_ring_grps;
u16 max_l2_ctxs;
u16 max_irqs;
u16 max_vnics;
@@ -722,9 +725,8 @@ struct bnxt_pf_info {
u16 max_rsscos_ctxs;
u16 max_cp_rings;
u16 max_tx_rings; /* HW assigned max tx rings for this PF */
- u16 max_pf_tx_rings; /* runtime max tx rings owned by PF */
u16 max_rx_rings; /* HW assigned max rx rings for this PF */
- u16 max_pf_rx_rings; /* runtime max rx rings owned by PF */
+ u16 max_hw_ring_grps;
u16 max_irqs;
u16 max_l2_ctxs;
u16 max_vnics;
@@ -875,6 +877,8 @@ struct bnxt {
#define BNXT_FLAG_USING_MSIX 0x40
#define BNXT_FLAG_MSIX_CAP 0x80
#define BNXT_FLAG_RFS 0x100
+ #define BNXT_FLAG_SHARED_RINGS 0x200
+
#define BNXT_FLAG_ALL_CONFIG_FEATS (BNXT_FLAG_TPA | \
BNXT_FLAG_RFS | \
BNXT_FLAG_STRIP_VLAN)
@@ -884,6 +888,9 @@ struct bnxt {
struct bnxt_napi **bnapi;
+ struct bnxt_rx_ring_info *rx_ring;
+ struct bnxt_tx_ring_info *tx_ring;
+
u32 rx_buf_size;
u32 rx_buf_use_size; /* useable size */
u32 rx_ring_size;
@@ -913,6 +920,8 @@ struct bnxt {
int cp_nr_rings;
int num_stat_ctxs;
+
+ /* grp_info indexed by completion ring index */
struct bnxt_ring_grp_info *grp_info;
struct bnxt_vnic_info *vnic_info;
int nr_vnics;
@@ -925,9 +934,9 @@ struct bnxt {
struct timer_list timer;
- int state;
-#define BNXT_STATE_CLOSED 0
-#define BNXT_STATE_OPEN 1
+ unsigned long state;
+#define BNXT_STATE_OPEN 0
+#define BNXT_STATE_IN_SP_TASK 1
struct bnxt_irq *irq_tbl;
u8 mac_addr[ETH_ALEN];
@@ -1084,9 +1093,10 @@ void bnxt_hwrm_cmd_hdr_init(struct bnxt *, void *, u16, u16, u16);
int _hwrm_send_message(struct bnxt *, void *, u32, int);
int hwrm_send_message(struct bnxt *, void *, u32, int);
int bnxt_hwrm_set_coal(struct bnxt *);
+int bnxt_hwrm_func_qcaps(struct bnxt *);
int bnxt_hwrm_set_pause(struct bnxt *);
int bnxt_hwrm_set_link_setting(struct bnxt *, bool);
int bnxt_open_nic(struct bnxt *, bool, bool);
int bnxt_close_nic(struct bnxt *, bool, bool);
-void bnxt_get_max_rings(struct bnxt *, int *, int *);
+int bnxt_get_max_rings(struct bnxt *, int *, int *, bool);
#endif
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
index 45bd628eaf3a..922b898e7a32 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
@@ -211,7 +211,10 @@ static void bnxt_get_channels(struct net_device *dev,
struct bnxt *bp = netdev_priv(dev);
int max_rx_rings, max_tx_rings, tcs;
- bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings);
+ bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, true);
+ channel->max_combined = max_rx_rings;
+
+ bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, false);
tcs = netdev_get_num_tc(dev);
if (tcs > 1)
max_tx_rings /= tcs;
@@ -219,9 +222,12 @@ static void bnxt_get_channels(struct net_device *dev,
channel->max_rx = max_rx_rings;
channel->max_tx = max_tx_rings;
channel->max_other = 0;
- channel->max_combined = 0;
- channel->rx_count = bp->rx_nr_rings;
- channel->tx_count = bp->tx_nr_rings_per_tc;
+ if (bp->flags & BNXT_FLAG_SHARED_RINGS) {
+ channel->combined_count = bp->rx_nr_rings;
+ } else {
+ channel->rx_count = bp->rx_nr_rings;
+ channel->tx_count = bp->tx_nr_rings_per_tc;
+ }
}
static int bnxt_set_channels(struct net_device *dev,
@@ -230,19 +236,35 @@ static int bnxt_set_channels(struct net_device *dev,
struct bnxt *bp = netdev_priv(dev);
int max_rx_rings, max_tx_rings, tcs;
u32 rc = 0;
+ bool sh = false;
+
+ if (channel->other_count)
+ return -EINVAL;
+
+ if (!channel->combined_count &&
+ (!channel->rx_count || !channel->tx_count))
+ return -EINVAL;
- if (channel->other_count || channel->combined_count ||
- !channel->rx_count || !channel->tx_count)
+ if (channel->combined_count &&
+ (channel->rx_count || channel->tx_count))
return -EINVAL;
- bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings);
+ if (channel->combined_count)
+ sh = true;
+
+ bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, sh);
+
tcs = netdev_get_num_tc(dev);
if (tcs > 1)
max_tx_rings /= tcs;
- if (channel->rx_count > max_rx_rings ||
- channel->tx_count > max_tx_rings)
- return -EINVAL;
+ if (sh && (channel->combined_count > max_rx_rings ||
+ channel->combined_count > max_tx_rings))
+ return -ENOMEM;
+
+ if (!sh && (channel->rx_count > max_rx_rings ||
+ channel->tx_count > max_tx_rings))
+ return -ENOMEM;
if (netif_running(dev)) {
if (BNXT_PF(bp)) {
@@ -258,14 +280,27 @@ static int bnxt_set_channels(struct net_device *dev,
}
}
- bp->rx_nr_rings = channel->rx_count;
- bp->tx_nr_rings_per_tc = channel->tx_count;
+ if (sh) {
+ bp->flags |= BNXT_FLAG_SHARED_RINGS;
+ bp->rx_nr_rings = channel->combined_count;
+ bp->tx_nr_rings_per_tc = channel->combined_count;
+ } else {
+ bp->flags &= ~BNXT_FLAG_SHARED_RINGS;
+ bp->rx_nr_rings = channel->rx_count;
+ bp->tx_nr_rings_per_tc = channel->tx_count;
+ }
+
bp->tx_nr_rings = bp->tx_nr_rings_per_tc;
if (tcs > 1)
bp->tx_nr_rings = bp->tx_nr_rings_per_tc * tcs;
- bp->cp_nr_rings = max_t(int, bp->tx_nr_rings, bp->rx_nr_rings);
+
+ bp->cp_nr_rings = sh ? max_t(int, bp->tx_nr_rings, bp->rx_nr_rings) :
+ bp->tx_nr_rings + bp->rx_nr_rings;
+
bp->num_stat_ctxs = bp->cp_nr_rings;
+ /* After changing number of rx channels, update NTUPLE feature. */
+ netdev_update_features(dev);
if (netif_running(dev)) {
rc = bnxt_open_nic(bp, true, false);
if ((!rc) && BNXT_PF(bp)) {
@@ -802,6 +837,45 @@ static int bnxt_flash_nvram(struct net_device *dev,
return rc;
}
+static int bnxt_firmware_reset(struct net_device *dev,
+ u16 dir_type)
+{
+ struct bnxt *bp = netdev_priv(dev);
+ struct hwrm_fw_reset_input req = {0};
+
+ bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FW_RESET, -1, -1);
+
+ /* TODO: Support ASAP ChiMP self-reset (e.g. upon PF driver unload) */
+ /* TODO: Address self-reset of APE/KONG/BONO/TANG or ungraceful reset */
+ /* (e.g. when firmware isn't already running) */
+ switch (dir_type) {
+ case BNX_DIR_TYPE_CHIMP_PATCH:
+ case BNX_DIR_TYPE_BOOTCODE:
+ case BNX_DIR_TYPE_BOOTCODE_2:
+ req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_BOOT;
+ /* Self-reset ChiMP upon next PCIe reset: */
+ req.selfrst_status = FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST;
+ break;
+ case BNX_DIR_TYPE_APE_FW:
+ case BNX_DIR_TYPE_APE_PATCH:
+ req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_MGMT;
+ break;
+ case BNX_DIR_TYPE_KONG_FW:
+ case BNX_DIR_TYPE_KONG_PATCH:
+ req.embedded_proc_type =
+ FW_RESET_REQ_EMBEDDED_PROC_TYPE_NETCTRL;
+ break;
+ case BNX_DIR_TYPE_BONO_FW:
+ case BNX_DIR_TYPE_BONO_PATCH:
+ req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_ROCE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+}
+
static int bnxt_flash_firmware(struct net_device *dev,
u16 dir_type,
const u8 *fw_data,
@@ -818,6 +892,9 @@ static int bnxt_flash_firmware(struct net_device *dev,
case BNX_DIR_TYPE_BOOTCODE_2:
code_type = CODE_BOOT;
break;
+ case BNX_DIR_TYPE_APE_FW:
+ code_type = CODE_MCTP_PASSTHRU;
+ break;
default:
netdev_err(dev, "Unsupported directory entry type: %u\n",
dir_type);
@@ -856,10 +933,9 @@ static int bnxt_flash_firmware(struct net_device *dev,
/* TODO: Validate digital signature (RSA-encrypted SHA-256 hash) here */
rc = bnxt_flash_nvram(dev, dir_type, BNX_DIR_ORDINAL_FIRST,
0, 0, fw_data, fw_size);
- if (rc == 0) { /* Firmware update successful */
- /* TODO: Notify processor it needs to reset itself
- */
- }
+ if (rc == 0) /* Firmware update successful */
+ rc = bnxt_firmware_reset(dev, dir_type);
+
return rc;
}
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h
index 70fc8253c07f..4badbedcb421 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h
@@ -103,19 +103,22 @@ struct hwrm_async_event_cmpl {
#define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CHANGE (0x2UL << 0)
#define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_DCB_CONFIG_CHANGE (0x3UL << 0)
#define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PORT_CONN_NOT_ALLOWED (0x4UL << 0)
+ #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_LINK_SPEED_CFG_NOT_ALLOWED (0x5UL << 0)
#define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_FUNC_DRVR_UNLOAD (0x10UL << 0)
#define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_FUNC_DRVR_LOAD (0x11UL << 0)
#define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_UNLOAD (0x20UL << 0)
- #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_LOAD (0x20UL << 0)
+ #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_LOAD (0x21UL << 0)
#define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_VF_FLR (0x30UL << 0)
#define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_VF_MAC_ADDR_CHANGE (0x31UL << 0)
+ #define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_PF_VF_COMM_STATUS_CHANGE (0x32UL << 0)
#define HWRM_ASYNC_EVENT_CMPL_EVENT_ID_HWRM_ERROR (0xffUL << 0)
__le32 event_data2;
u8 opaque_v;
#define HWRM_ASYNC_EVENT_CMPL_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
};
@@ -132,9 +135,16 @@ struct hwrm_async_event_cmpl_link_status_change {
#define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
- #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_UP 0x1UL
+ #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_CHANGE 0x1UL
+ #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_CHANGE_DOWN (0x0UL << 0)
+ #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_LINK_CHANGE_UP (0x1UL << 0)
+ #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_PORT_MASK 0xeUL
+ #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_PORT_SFT 1
+ #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_PORT_ID_MASK 0xffff0UL
+ #define HWRM_ASYNC_EVENT_CMPL_LINK_STATUS_CHANGE_EVENT_DATA1_PORT_ID_SFT 4
};
/* HWRM Asynchronous Event Completion Record for link MTU change (16 bytes) */
@@ -150,7 +160,8 @@ struct hwrm_async_event_cmpl_link_mtu_change {
#define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_EVENT_DATA1_NEW_MTU_MASK 0xffffUL
#define HWRM_ASYNC_EVENT_CMPL_LINK_MTU_CHANGE_EVENT_DATA1_NEW_MTU_SFT 0
@@ -169,7 +180,8 @@ struct hwrm_async_event_cmpl_link_speed_change {
#define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_FORCE 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_LINK_SPEED_CHANGE_EVENT_DATA1_NEW_LINK_SPEED_100MBPS_MASK 0xfffeUL
@@ -200,7 +212,8 @@ struct hwrm_async_event_cmpl_dcb_config_change {
#define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_PORT_ID_MASK 0xffffUL
#define HWRM_ASYNC_EVENT_CMPL_DCB_CONFIG_CHANGE_EVENT_DATA1_PORT_ID_SFT 0
@@ -219,7 +232,8 @@ struct hwrm_async_event_cmpl_port_conn_not_allowed {
#define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_PORT_ID_MASK 0xffffUL
#define HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_PORT_ID_SFT 0
@@ -238,7 +252,8 @@ struct hwrm_async_event_cmpl_func_drvr_unload {
#define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL
#define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_SFT 0
@@ -257,7 +272,8 @@ struct hwrm_async_event_cmpl_func_drvr_load {
#define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL
#define HWRM_ASYNC_EVENT_CMPL_FUNC_DRVR_LOAD_EVENT_DATA1_FUNC_ID_SFT 0
@@ -276,10 +292,13 @@ struct hwrm_async_event_cmpl_pf_drvr_unload {
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_FUNC_ID_SFT 0
+ #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_PORT_MASK 0x70000UL
+ #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_UNLOAD_EVENT_DATA1_PORT_SFT 16
};
/* HWRM Asynchronous Event Completion Record for PF Driver load (16 bytes) */
@@ -289,16 +308,19 @@ struct hwrm_async_event_cmpl_pf_drvr_load {
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_TYPE_SFT 0
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_TYPE_HWRM_ASYNC_EVENT (0x2eUL << 0)
__le16 event_id;
- #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_ID_PF_DRVR_LOAD (0x20UL << 0)
+ #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_ID_PF_DRVR_LOAD (0x21UL << 0)
__le32 event_data2;
u8 opaque_v;
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_FUNC_ID_MASK 0xffffUL
#define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_FUNC_ID_SFT 0
+ #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_PORT_MASK 0x70000UL
+ #define HWRM_ASYNC_EVENT_CMPL_PF_DRVR_LOAD_EVENT_DATA1_PORT_SFT 16
};
/* HWRM Asynchronous Event Completion Record for VF FLR (16 bytes) */
@@ -314,7 +336,8 @@ struct hwrm_async_event_cmpl_vf_flr {
#define HWRM_ASYNC_EVENT_CMPL_VF_FLR_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_VF_FLR_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_VF_FLR_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_VF_FLR_EVENT_DATA1_VF_ID_MASK 0xffffUL
#define HWRM_ASYNC_EVENT_CMPL_VF_FLR_EVENT_DATA1_VF_ID_SFT 0
@@ -333,7 +356,8 @@ struct hwrm_async_event_cmpl_vf_mac_addr_change {
#define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_EVENT_DATA1_VF_ID_MASK 0xffffUL
#define HWRM_ASYNC_EVENT_CMPL_VF_MAC_ADDR_CHANGE_EVENT_DATA1_VF_ID_SFT 0
@@ -357,18 +381,20 @@ struct hwrm_async_event_cmpl_hwrm_error {
#define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_V 0x1UL
#define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_OPAQUE_MASK 0xfeUL
#define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_OPAQUE_SFT 1
- u8 unused_1[3];
+ u8 timestamp_lo;
+ __le16 timestamp_hi;
__le32 event_data1;
#define HWRM_ASYNC_EVENT_CMPL_HWRM_ERROR_EVENT_DATA1_TIMESTAMP 0x1UL
};
-/* HW Resource Manager Specification 0.7.8 */
-#define HWRM_VERSION_MAJOR 0
-#define HWRM_VERSION_MINOR 7
-#define HWRM_VERSION_UPDATE 8
+/* HW Resource Manager Specification 1.0.0 */
+#define HWRM_VERSION_MAJOR 1
+#define HWRM_VERSION_MINOR 0
+#define HWRM_VERSION_UPDATE 0
-#define HWRM_VERSION_STR "0.7.8"
-/* Following is the signature for HWRM message field that indicates not
+#define HWRM_VERSION_STR "1.0.0"
+/*
+ * Following is the signature for HWRM message field that indicates not
* applicable (All F's). Need to cast it the size of the field if needed.
*/
#define HWRM_NA_SIGNATURE ((__le32)(-1))
@@ -398,7 +424,9 @@ struct output {
struct cmd_nums {
__le16 req_type;
#define HWRM_VER_GET (0x0UL)
- #define HWRM_FUNC_DISABLE (0x10UL)
+ #define HWRM_FUNC_BUF_UNRGTR (0xeUL)
+ #define HWRM_FUNC_VF_CFG (0xfUL)
+ #define RESERVED1 (0x10UL)
#define HWRM_FUNC_RESET (0x11UL)
#define HWRM_FUNC_GETFID (0x12UL)
#define HWRM_FUNC_VF_ALLOC (0x13UL)
@@ -414,10 +442,9 @@ struct cmd_nums {
#define HWRM_FUNC_DRV_RGTR (0x1dUL)
#define HWRM_FUNC_DRV_QVER (0x1eUL)
#define HWRM_FUNC_BUF_RGTR (0x1fUL)
- #define HWRM_FUNC_VF_CFG (0x20UL)
#define HWRM_PORT_PHY_CFG (0x20UL)
#define HWRM_PORT_MAC_CFG (0x21UL)
- #define HWRM_PORT_ENABLE (0x22UL)
+ #define RESERVED2 (0x22UL)
#define HWRM_PORT_QSTATS (0x23UL)
#define HWRM_PORT_LPBK_QSTATS (0x24UL)
#define HWRM_PORT_CLR_STATS (0x25UL)
@@ -455,13 +482,11 @@ struct cmd_nums {
#define HWRM_RING_GRP_FREE (0x61UL)
#define HWRM_VNIC_RSS_COS_LB_CTX_ALLOC (0x70UL)
#define HWRM_VNIC_RSS_COS_LB_CTX_FREE (0x71UL)
- #define HWRM_ARB_GRP_ALLOC (0x80UL)
- #define HWRM_ARB_GRP_CFG (0x81UL)
#define HWRM_CFA_L2_FILTER_ALLOC (0x90UL)
#define HWRM_CFA_L2_FILTER_FREE (0x91UL)
#define HWRM_CFA_L2_FILTER_CFG (0x92UL)
#define HWRM_CFA_L2_SET_RX_MASK (0x93UL)
- #define HWRM_CFA_L2_SET_BCASTMCAST_MIRRORING (0x94UL)
+ #define RESERVED3 (0x94UL)
#define HWRM_CFA_TUNNEL_FILTER_ALLOC (0x95UL)
#define HWRM_CFA_TUNNEL_FILTER_FREE (0x96UL)
#define HWRM_CFA_ENCAP_RECORD_ALLOC (0x97UL)
@@ -469,6 +494,9 @@ struct cmd_nums {
#define HWRM_CFA_NTUPLE_FILTER_ALLOC (0x99UL)
#define HWRM_CFA_NTUPLE_FILTER_FREE (0x9aUL)
#define HWRM_CFA_NTUPLE_FILTER_CFG (0x9bUL)
+ #define HWRM_CFA_EM_FLOW_ALLOC (0x9cUL)
+ #define HWRM_CFA_EM_FLOW_FREE (0x9dUL)
+ #define HWRM_CFA_EM_FLOW_CFG (0x9eUL)
#define HWRM_TUNNEL_DST_PORT_QUERY (0xa0UL)
#define HWRM_TUNNEL_DST_PORT_ALLOC (0xa1UL)
#define HWRM_TUNNEL_DST_PORT_FREE (0xa2UL)
@@ -483,8 +511,6 @@ struct cmd_nums {
#define HWRM_FWD_RESP (0xd2UL)
#define HWRM_FWD_ASYNC_EVENT_CMPL (0xd3UL)
#define HWRM_TEMP_MONITOR_QUERY (0xe0UL)
- #define HWRM_MGMT_L2_FILTER_ALLOC (0x100UL)
- #define HWRM_MGMT_L2_FILTER_FREE (0x101UL)
#define HWRM_DBG_READ_DIRECT (0xff10UL)
#define HWRM_DBG_READ_INDIRECT (0xff11UL)
#define HWRM_DBG_WRITE_DIRECT (0xff12UL)
@@ -505,7 +531,6 @@ struct cmd_nums {
__le16 unused_0[3];
};
-/* Return Codes (8 bytes) */
struct ret_codes {
__le16 error_code;
#define HWRM_ERR_CODE_SUCCESS (0x0UL)
@@ -529,7 +554,7 @@ struct hwrm_err_output {
__le16 resp_len;
__le32 opaque_0;
__le16 opaque_1;
- u8 opaque_2;
+ u8 cmd_err;
u8 valid;
};
@@ -686,65 +711,38 @@ struct hwrm_ver_get_output {
u8 hwrm_fw_min;
u8 hwrm_fw_bld;
u8 hwrm_fw_rsvd;
- u8 ape_fw_maj;
- u8 ape_fw_min;
- u8 ape_fw_bld;
- u8 ape_fw_rsvd;
- u8 kong_fw_maj;
- u8 kong_fw_min;
- u8 kong_fw_bld;
- u8 kong_fw_rsvd;
- u8 tang_fw_maj;
- u8 tang_fw_min;
- u8 tang_fw_bld;
- u8 tang_fw_rsvd;
- u8 bono_fw_maj;
- u8 bono_fw_min;
- u8 bono_fw_bld;
- u8 bono_fw_rsvd;
+ u8 mgmt_fw_maj;
+ u8 mgmt_fw_min;
+ u8 mgmt_fw_bld;
+ u8 mgmt_fw_rsvd;
+ u8 netctrl_fw_maj;
+ u8 netctrl_fw_min;
+ u8 netctrl_fw_bld;
+ u8 netctrl_fw_rsvd;
+ __le32 reserved1;
+ u8 roce_fw_maj;
+ u8 roce_fw_min;
+ u8 roce_fw_bld;
+ u8 roce_fw_rsvd;
char hwrm_fw_name[16];
- char ape_fw_name[16];
- char kong_fw_name[16];
- char tang_fw_name[16];
- char bono_fw_name[16];
+ char mgmt_fw_name[16];
+ char netctrl_fw_name[16];
+ __le32 reserved2[4];
+ char roce_fw_name[16];
__le16 chip_num;
u8 chip_rev;
u8 chip_metal;
u8 chip_bond_id;
- u8 unused_0;
+ u8 chip_platform_type;
+ #define VER_GET_RESP_CHIP_PLATFORM_TYPE_ASIC (0x0UL << 0)
+ #define VER_GET_RESP_CHIP_PLATFORM_TYPE_FPGA (0x1UL << 0)
+ #define VER_GET_RESP_CHIP_PLATFORM_TYPE_PALLADIUM (0x2UL << 0)
__le16 max_req_win_len;
__le16 max_resp_len;
__le16 def_req_timeout;
+ u8 unused_0;
u8 unused_1;
u8 unused_2;
- u8 unused_3;
- u8 valid;
-};
-
-/* hwrm_func_disable */
-/* Input (24 bytes) */
-struct hwrm_func_disable_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- __le32 enables;
- #define FUNC_DISABLE_REQ_ENABLES_VF_ID_VALID 0x1UL
- __le16 vf_id;
- __le16 unused_0;
-};
-
-/* Output (16 bytes) */
-struct hwrm_func_disable_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- __le32 unused_0;
- u8 unused_1;
- u8 unused_2;
- u8 unused_3;
u8 valid;
};
@@ -759,7 +757,12 @@ struct hwrm_func_reset_input {
__le32 enables;
#define FUNC_RESET_REQ_ENABLES_VF_ID_VALID 0x1UL
__le16 vf_id;
- __le16 unused_0;
+ u8 func_reset_level;
+ #define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETALL (0x0UL << 0)
+ #define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETME (0x1UL << 0)
+ #define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETCHILDREN (0x2UL << 0)
+ #define FUNC_RESET_REQ_FUNC_RESET_LEVEL_RESETVF (0x3UL << 0)
+ u8 unused_0;
};
/* Output (16 bytes) */
@@ -861,7 +864,7 @@ struct hwrm_func_vf_free_output {
};
/* hwrm_func_vf_cfg */
-/* Input (24 bytes) */
+/* Input (32 bytes) */
struct hwrm_func_vf_cfg_input {
__le16 req_type;
__le16 cmpl_ring;
@@ -871,8 +874,11 @@ struct hwrm_func_vf_cfg_input {
__le32 enables;
#define FUNC_VF_CFG_REQ_ENABLES_MTU 0x1UL
#define FUNC_VF_CFG_REQ_ENABLES_GUEST_VLAN 0x2UL
+ #define FUNC_VF_CFG_REQ_ENABLES_ASYNC_EVENT_CR 0x4UL
__le16 mtu;
__le16 guest_vlan;
+ __le16 async_event_cr;
+ __le16 unused_0[3];
};
/* Output (16 bytes) */
@@ -944,7 +950,7 @@ struct hwrm_func_cfg_input {
__le16 seq_id;
__le16 target_id;
__le64 resp_addr;
- __le16 vf_id;
+ __le16 fid;
u8 unused_0;
u8 unused_1;
__le32 flags;
@@ -1000,10 +1006,6 @@ struct hwrm_func_cfg_input {
#define FUNC_CFG_REQ_VLAN_ANTISPOOF_MODE_INSERT_IF_VLANDNE (0x2UL << 0)
#define FUNC_CFG_REQ_VLAN_ANTISPOOF_MODE_INSERT_OR_OVERRIDE_VLAN (0x3UL << 0)
u8 allowed_vlan_pris;
- #define FUNC_CFG_REQ_ALLOWED_VLAN_PRIS_NOCHECK (0x0UL << 0)
- #define FUNC_CFG_REQ_ALLOWED_VLAN_PRIS_VALIDATE_VLAN (0x1UL << 0)
- #define FUNC_CFG_REQ_ALLOWED_VLAN_PRIS_INSERT_IF_VLANDNE (0x2UL << 0)
- #define FUNC_CFG_REQ_ALLOWED_VLAN_PRIS_INSERT_OR_OVERRIDE_VLAN (0x3UL << 0)
u8 evb_mode;
#define FUNC_CFG_REQ_EVB_MODE_NO_EVB (0x0UL << 0)
#define FUNC_CFG_REQ_EVB_MODE_VEB (0x1UL << 0)
@@ -1166,6 +1168,15 @@ struct hwrm_func_drv_rgtr_input {
#define FUNC_DRV_RGTR_REQ_ENABLES_VF_REQ_FWD 0x8UL
#define FUNC_DRV_RGTR_REQ_ENABLES_ASYNC_EVENT_FWD 0x10UL
__le16 os_type;
+ #define FUNC_DRV_RGTR_REQ_OS_TYPE_UNKNOWN (0x0UL << 0)
+ #define FUNC_DRV_RGTR_REQ_OS_TYPE_OTHER (0x1UL << 0)
+ #define FUNC_DRV_RGTR_REQ_OS_TYPE_MSDOS (0xeUL << 0)
+ #define FUNC_DRV_RGTR_REQ_OS_TYPE_SOLARIS (0x1dUL << 0)
+ #define FUNC_DRV_RGTR_REQ_OS_TYPE_LINUX (0x24UL << 0)
+ #define FUNC_DRV_RGTR_REQ_OS_TYPE_FREEBSD (0x2aUL << 0)
+ #define FUNC_DRV_RGTR_REQ_OS_TYPE_ESXI (0x68UL << 0)
+ #define FUNC_DRV_RGTR_REQ_OS_TYPE_WIN864 (0x73UL << 0)
+ #define FUNC_DRV_RGTR_REQ_OS_TYPE_WIN2012R2 (0x74UL << 0)
u8 ver_maj;
u8 ver_min;
u8 ver_upd;
@@ -1276,9 +1287,7 @@ struct hwrm_func_drv_qver_input {
__le16 seq_id;
__le16 target_id;
__le64 resp_addr;
- __le32 enables;
- #define FUNC_DRV_QVER_REQ_ENABLES_OS_TYPE_VALID 0x1UL
- #define FUNC_DRV_QVER_REQ_ENABLES_VER_VALID 0x2UL
+ __le32 reserved;
__le16 fid;
__le16 unused_0;
};
@@ -1290,6 +1299,15 @@ struct hwrm_func_drv_qver_output {
__le16 seq_id;
__le16 resp_len;
__le16 os_type;
+ #define FUNC_DRV_QVER_RESP_OS_TYPE_UNKNOWN (0x0UL << 0)
+ #define FUNC_DRV_QVER_RESP_OS_TYPE_OTHER (0x1UL << 0)
+ #define FUNC_DRV_QVER_RESP_OS_TYPE_MSDOS (0xeUL << 0)
+ #define FUNC_DRV_QVER_RESP_OS_TYPE_SOLARIS (0x1dUL << 0)
+ #define FUNC_DRV_QVER_RESP_OS_TYPE_LINUX (0x24UL << 0)
+ #define FUNC_DRV_QVER_RESP_OS_TYPE_FREEBSD (0x2aUL << 0)
+ #define FUNC_DRV_QVER_RESP_OS_TYPE_ESXI (0x68UL << 0)
+ #define FUNC_DRV_QVER_RESP_OS_TYPE_WIN864 (0x73UL << 0)
+ #define FUNC_DRV_QVER_RESP_OS_TYPE_WIN2012R2 (0x74UL << 0)
u8 ver_maj;
u8 ver_min;
u8 ver_upd;
@@ -1498,9 +1516,7 @@ struct hwrm_port_phy_qcfg_output {
u8 force_pause;
#define PORT_PHY_QCFG_RESP_FORCE_PAUSE_TX 0x1UL
#define PORT_PHY_QCFG_RESP_FORCE_PAUSE_RX 0x2UL
- u8 duplex_setting;
- #define PORT_PHY_QCFG_RESP_DUPLEX_SETTING_HALF (0x0UL << 0)
- #define PORT_PHY_QCFG_RESP_DUPLEX_SETTING_FULL (0x1UL << 0)
+ u8 reserved1;
__le32 preemphasis;
u8 phy_maj;
u8 phy_min;
@@ -1601,33 +1617,6 @@ struct hwrm_port_mac_cfg_output {
u8 valid;
};
-/* hwrm_port_enable */
-/* Input (24 bytes) */
-struct hwrm_port_enable_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- __le32 flags;
- #define PORT_ENABLE_REQ_FLAGS_FORWARD_TRAFFIC 0x1UL
- __le16 port_id;
- __le16 unused_0;
-};
-
-/* Output (16 bytes) */
-struct hwrm_port_enable_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- __le32 unused_0;
- u8 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 valid;
-};
-
/* hwrm_port_qstats */
/* Input (40 bytes) */
struct hwrm_port_qstats_input {
@@ -1651,10 +1640,11 @@ struct hwrm_port_qstats_output {
__le16 req_type;
__le16 seq_id;
__le16 resp_len;
- __le32 unused_0;
+ __le16 tx_stat_size;
+ __le16 rx_stat_size;
+ u8 unused_0;
u8 unused_1;
u8 unused_2;
- u8 unused_3;
u8 valid;
};
@@ -1668,7 +1658,7 @@ struct hwrm_port_lpbk_qstats_input {
__le64 resp_addr;
};
-/* Output (64 bytes) */
+/* Output (96 bytes) */
struct hwrm_port_lpbk_qstats_output {
__le16 error_code;
__le16 req_type;
@@ -1680,6 +1670,10 @@ struct hwrm_port_lpbk_qstats_output {
__le64 lpbk_ucast_bytes;
__le64 lpbk_mcast_bytes;
__le64 lpbk_bcast_bytes;
+ __le64 tx_stat_discard;
+ __le64 tx_stat_error;
+ __le64 rx_stat_discard;
+ __le64 rx_stat_error;
__le32 unused_0;
u8 unused_1;
u8 unused_2;
@@ -1884,12 +1878,11 @@ struct hwrm_queue_buffers_cfg_input {
__le32 enables;
#define QUEUE_BUFFERS_CFG_REQ_ENABLES_RESERVED 0x1UL
#define QUEUE_BUFFERS_CFG_REQ_ENABLES_SHARED 0x2UL
- #define QUEUE_BUFFERS_CFG_REQ_ENABLES_GROUP 0x4UL
- #define QUEUE_BUFFERS_CFG_REQ_ENABLES_XOFF 0x8UL
- #define QUEUE_BUFFERS_CFG_REQ_ENABLES_XON 0x10UL
- #define QUEUE_BUFFERS_CFG_REQ_ENABLES_FULL 0x20UL
- #define QUEUE_BUFFERS_CFG_REQ_ENABLES_NOTFULL 0x40UL
- #define QUEUE_BUFFERS_CFG_REQ_ENABLES_MAX 0x80UL
+ #define QUEUE_BUFFERS_CFG_REQ_ENABLES_XOFF 0x4UL
+ #define QUEUE_BUFFERS_CFG_REQ_ENABLES_XON 0x8UL
+ #define QUEUE_BUFFERS_CFG_REQ_ENABLES_FULL 0x10UL
+ #define QUEUE_BUFFERS_CFG_REQ_ENABLES_NOTFULL 0x20UL
+ #define QUEUE_BUFFERS_CFG_REQ_ENABLES_MAX 0x40UL
__le32 queue_id;
__le32 reserved;
__le32 shared;
@@ -1921,15 +1914,15 @@ struct hwrm_queue_pfcenable_cfg_input {
__le16 seq_id;
__le16 target_id;
__le64 resp_addr;
- __le32 enables;
- #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI0_PFC_ENABLED 0x1UL
- #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI1_PFC_ENABLED 0x2UL
- #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI2_PFC_ENABLED 0x4UL
- #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI3_PFC_ENABLED 0x8UL
- #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI4_PFC_ENABLED 0x10UL
- #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI5_PFC_ENABLED 0x20UL
- #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI6_PFC_ENABLED 0x40UL
- #define QUEUE_PFCENABLE_CFG_REQ_ENABLES_PRI7_PFC_ENABLED 0x80UL
+ __le32 flags;
+ #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI0_PFC_ENABLED 0x1UL
+ #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI1_PFC_ENABLED 0x2UL
+ #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI2_PFC_ENABLED 0x4UL
+ #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI3_PFC_ENABLED 0x8UL
+ #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI4_PFC_ENABLED 0x10UL
+ #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI5_PFC_ENABLED 0x20UL
+ #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI6_PFC_ENABLED 0x40UL
+ #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI7_PFC_ENABLED 0x80UL
__le16 port_id;
__le16 unused_0;
};
@@ -1962,14 +1955,14 @@ struct hwrm_queue_pri2cos_cfg_input {
#define QUEUE_PRI2COS_CFG_REQ_FLAGS_IVLAN 0x2UL
__le32 enables;
u8 port_id;
- u8 pri0_cos;
- u8 pri1_cos;
- u8 pri2_cos;
- u8 pri3_cos;
- u8 pri4_cos;
- u8 pri5_cos;
- u8 pri6_cos;
- u8 pri7_cos;
+ u8 pri0_cos_queue_id;
+ u8 pri1_cos_queue_id;
+ u8 pri2_cos_queue_id;
+ u8 pri3_cos_queue_id;
+ u8 pri4_cos_queue_id;
+ u8 pri5_cos_queue_id;
+ u8 pri6_cos_queue_id;
+ u8 pri7_cos_queue_id;
u8 unused_0[7];
};
@@ -2164,6 +2157,7 @@ struct hwrm_vnic_cfg_input {
__le32 flags;
#define VNIC_CFG_REQ_FLAGS_DEFAULT 0x1UL
#define VNIC_CFG_REQ_FLAGS_VLAN_STRIP_MODE 0x2UL
+ #define VNIC_CFG_REQ_FLAGS_BD_STALL_MODE 0x4UL
__le32 enables;
#define VNIC_CFG_REQ_ENABLES_DFLT_RING_GRP 0x1UL
#define VNIC_CFG_REQ_ENABLES_RSS_RULE 0x2UL
@@ -2380,18 +2374,16 @@ struct hwrm_ring_alloc_input {
__le16 target_id;
__le64 resp_addr;
__le32 enables;
- #define RING_ALLOC_REQ_ENABLES_ARB_GRP_ID_VALID 0x1UL
- #define RING_ALLOC_REQ_ENABLES_INPUT_NUM_VALID 0x2UL
- #define RING_ALLOC_REQ_ENABLES_WEIGHT_VALID 0x4UL
+ #define RING_ALLOC_REQ_ENABLES_RESERVED1 0x1UL
+ #define RING_ALLOC_REQ_ENABLES_RESERVED2 0x2UL
+ #define RING_ALLOC_REQ_ENABLES_RESERVED3 0x4UL
#define RING_ALLOC_REQ_ENABLES_STAT_CTX_ID_VALID 0x8UL
- #define RING_ALLOC_REQ_ENABLES_MIN_BW_VALID 0x10UL
+ #define RING_ALLOC_REQ_ENABLES_RESERVED4 0x10UL
#define RING_ALLOC_REQ_ENABLES_MAX_BW_VALID 0x20UL
u8 ring_type;
#define RING_ALLOC_REQ_RING_TYPE_CMPL (0x0UL << 0)
#define RING_ALLOC_REQ_RING_TYPE_TX (0x1UL << 0)
#define RING_ALLOC_REQ_RING_TYPE_RX (0x2UL << 0)
- #define RING_ALLOC_REQ_RING_TYPE_STATUS (0x3UL << 0)
- #define RING_ALLOC_REQ_RING_TYPE_CMD (0x4UL << 0)
u8 unused_0;
__le16 unused_1;
__le64 page_tbl_addr;
@@ -2406,17 +2398,17 @@ struct hwrm_ring_alloc_input {
__le16 queue_id;
u8 unused_4;
u8 unused_5;
- __le32 arb_grp_id;
- __le16 input_number;
+ __le32 reserved1;
+ __le16 reserved2;
u8 unused_6;
u8 unused_7;
- __le32 weight;
+ __le32 reserved3;
__le32 stat_ctx_id;
- __le32 min_bw;
+ __le32 reserved4;
__le32 max_bw;
u8 int_mode;
#define RING_ALLOC_REQ_INT_MODE_LEGACY (0x0UL << 0)
- #define RING_ALLOC_REQ_INT_MODE_MSI (0x1UL << 0)
+ #define RING_ALLOC_REQ_INT_MODE_RSVD (0x1UL << 0)
#define RING_ALLOC_REQ_INT_MODE_MSIX (0x2UL << 0)
#define RING_ALLOC_REQ_INT_MODE_POLL (0x3UL << 0)
u8 unused_8[3];
@@ -2448,8 +2440,6 @@ struct hwrm_ring_free_input {
#define RING_FREE_REQ_RING_TYPE_CMPL (0x0UL << 0)
#define RING_FREE_REQ_RING_TYPE_TX (0x1UL << 0)
#define RING_FREE_REQ_RING_TYPE_RX (0x2UL << 0)
- #define RING_FREE_REQ_RING_TYPE_STATUS (0x3UL << 0)
- #define RING_FREE_REQ_RING_TYPE_CMD (0x4UL << 0)
u8 unused_0;
__le16 ring_id;
__le32 unused_1;
@@ -2550,8 +2540,6 @@ struct hwrm_ring_reset_input {
#define RING_RESET_REQ_RING_TYPE_CMPL (0x0UL << 0)
#define RING_RESET_REQ_RING_TYPE_TX (0x1UL << 0)
#define RING_RESET_REQ_RING_TYPE_RX (0x2UL << 0)
- #define RING_RESET_REQ_RING_TYPE_STATUS (0x3UL << 0)
- #define RING_RESET_REQ_RING_TYPE_CMD (0x4UL << 0)
u8 unused_0;
__le16 ring_id;
__le32 unused_1;
@@ -2622,61 +2610,6 @@ struct hwrm_ring_grp_free_output {
u8 valid;
};
-/* hwrm_arb_grp_alloc */
-/* Input (24 bytes) */
-struct hwrm_arb_grp_alloc_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- __le16 input_number;
- __le16 unused_0[3];
-};
-
-/* Output (16 bytes) */
-struct hwrm_arb_grp_alloc_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- __le16 arb_grp_id;
- u8 unused_0;
- u8 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 unused_4;
- u8 valid;
-};
-
-/* hwrm_arb_grp_cfg */
-/* Input (32 bytes) */
-struct hwrm_arb_grp_cfg_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- __le32 arb_grp_id;
- __le16 input_number;
- __le16 tx_ring;
- __le32 weight;
- __le32 unused_0;
-};
-
-/* Output (16 bytes) */
-struct hwrm_arb_grp_cfg_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- __le32 unused_0;
- u8 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 valid;
-};
-
/* hwrm_cfa_l2_filter_alloc */
/* Input (96 bytes) */
struct hwrm_cfa_l2_filter_alloc_input {
@@ -2708,7 +2641,7 @@ struct hwrm_cfa_l2_filter_alloc_input {
#define CFA_L2_FILTER_ALLOC_REQ_ENABLES_SRC_TYPE 0x1000UL
#define CFA_L2_FILTER_ALLOC_REQ_ENABLES_SRC_ID 0x2000UL
#define CFA_L2_FILTER_ALLOC_REQ_ENABLES_TUNNEL_TYPE 0x4000UL
- #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_DST_VNIC_ID 0x8000UL
+ #define CFA_L2_FILTER_ALLOC_REQ_ENABLES_DST_ID 0x8000UL
#define CFA_L2_FILTER_ALLOC_REQ_ENABLES_MIRROR_VNIC_ID 0x10000UL
u8 l2_addr[6];
u8 unused_0;
@@ -2751,7 +2684,7 @@ struct hwrm_cfa_l2_filter_alloc_input {
#define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE (0x8UL << 0)
#define CFA_L2_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL (0xffUL << 0)
u8 unused_7;
- __le16 dst_vnic_id;
+ __le16 dst_id;
__le16 mirror_vnic_id;
u8 pri_hint;
#define CFA_L2_FILTER_ALLOC_REQ_PRI_HINT_NO_PREFER (0x0UL << 0)
@@ -2816,10 +2749,11 @@ struct hwrm_cfa_l2_filter_cfg_input {
#define CFA_L2_FILTER_CFG_REQ_FLAGS_PATH_RX (0x1UL << 0)
#define CFA_L2_FILTER_CFG_REQ_FLAGS_DROP 0x2UL
__le32 enables;
- #define CFA_L2_FILTER_CFG_REQ_ENABLES_DST_VNIC_ID_VALID 0x1UL
+ #define CFA_L2_FILTER_CFG_REQ_ENABLES_DST_ID 0x1UL
+ #define CFA_L2_FILTER_CFG_REQ_ENABLES_NEW_MIRROR_VNIC_ID 0x2UL
__le64 l2_filter_id;
- __le32 dst_vnic_id;
- __le32 unused_0;
+ __le32 dst_id;
+ __le32 new_mirror_vnic_id;
};
/* Output (16 bytes) */
@@ -2843,9 +2777,9 @@ struct hwrm_cfa_l2_set_rx_mask_input {
__le16 seq_id;
__le16 target_id;
__le64 resp_addr;
- __le32 dflt_vnic_id;
+ __le32 vnic_id;
__le32 mask;
- #define CFA_L2_SET_RX_MASK_REQ_MASK_UNICAST 0x1UL
+ #define CFA_L2_SET_RX_MASK_REQ_MASK_RESERVED 0x1UL
#define CFA_L2_SET_RX_MASK_REQ_MASK_MCAST 0x2UL
#define CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST 0x4UL
#define CFA_L2_SET_RX_MASK_REQ_MASK_BCAST 0x8UL
@@ -2869,46 +2803,6 @@ struct hwrm_cfa_l2_set_rx_mask_output {
u8 valid;
};
-/* hwrm_cfa_l2_set_bcastmcast_mirroring */
-/* Input (32 bytes) */
-struct hwrm_cfa_l2_set_bcastmcast_mirroring_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- __le32 dflt_vnic_id;
- __le32 mirroring_flags;
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MIRRORING_FLAGS_BCAST_MIRRORING 0x1UL
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MIRRORING_FLAGS_MCAST_MIRRORING 0x2UL
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MIRRORING_FLAGS_BCAST_SRC_KNOCKOUT 0x4UL
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MIRRORING_FLAGS_MCAST_SRC_KNOCKOUT 0x8UL
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MIRRORING_FLAGS_VLAN_ID_VALID 0x10UL
- __le16 vlan_id;
- u8 bcast_domain;
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_BCAST_DOMAIN_PFONLY (0x0UL << 0)
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_BCAST_DOMAIN_ALLPFS (0x1UL << 0)
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_BCAST_DOMAIN_ALLPFSVFS (0x2UL << 0)
- u8 mcast_domain;
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MCAST_DOMAIN_PFONLY (0x0UL << 0)
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MCAST_DOMAIN_ALLPFS (0x1UL << 0)
- #define CFA_L2_SET_BCASTMCAST_MIRRORING_REQ_MCAST_DOMAIN_ALLPFSVFS (0x2UL << 0)
- __le32 unused_0;
-};
-
-/* Output (16 bytes) */
-struct hwrm_cfa_l2_set_bcastmcast_mirroring_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- __le32 unused_0;
- u8 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 valid;
-};
-
/* hwrm_cfa_tunnel_filter_alloc */
/* Input (88 bytes) */
struct hwrm_cfa_tunnel_filter_alloc_input {
@@ -3017,17 +2911,16 @@ struct hwrm_cfa_encap_record_alloc_input {
__le32 encap_data[16];
};
-/* Output (24 bytes) */
+/* Output (16 bytes) */
struct hwrm_cfa_encap_record_alloc_output {
__le16 error_code;
__le16 req_type;
__le16 seq_id;
__le16 resp_len;
- __le64 encap_record_id;
- __le32 unused_0;
+ __le32 encap_record_id;
+ u8 unused_0;
u8 unused_1;
u8 unused_2;
- u8 unused_3;
u8 valid;
};
@@ -3039,7 +2932,8 @@ struct hwrm_cfa_encap_record_free_input {
__le16 seq_id;
__le16 target_id;
__le64 resp_addr;
- __le64 encap_record_id;
+ __le32 encap_record_id;
+ __le32 unused_0;
};
/* Output (16 bytes) */
@@ -3083,14 +2977,21 @@ struct hwrm_cfa_ntuple_filter_alloc_input {
#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_PORT_MASK 0x2000UL
#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_PRI_HINT 0x4000UL
#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_NTUPLE_FILTER_ID 0x8000UL
- #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_VNIC_ID 0x10000UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_ID 0x10000UL
#define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_MIRROR_VNIC_ID 0x20000UL
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_ENABLES_DST_MACADDR 0x40000UL
__le64 l2_filter_id;
u8 src_macaddr[6];
__be16 ethertype;
- u8 ipaddr_type;
+ u8 ip_addr_type;
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_UNKNOWN (0x0UL << 0)
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV4 (0x4UL << 0)
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV6 (0x6UL << 0)
u8 ip_protocol;
- __le16 dst_vnic_id;
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_UNKNOWN (0x0UL << 0)
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_UDP (0x6UL << 0)
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_TCP (0x11UL << 0)
+ __le16 dst_id;
__le16 mirror_vnic_id;
u8 tunnel_type;
#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL (0x0UL << 0)
@@ -3104,6 +3005,11 @@ struct hwrm_cfa_ntuple_filter_alloc_input {
#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_IPGRE (0x8UL << 0)
#define CFA_NTUPLE_FILTER_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL (0xffUL << 0)
u8 pri_hint;
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_NO_PREFER (0x0UL << 0)
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_ABOVE (0x1UL << 0)
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_BELOW (0x2UL << 0)
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_HIGHEST (0x3UL << 0)
+ #define CFA_NTUPLE_FILTER_ALLOC_REQ_PRI_HINT_LOWEST (0x4UL << 0)
__be32 src_ipaddr[4];
__be32 src_ipaddr_mask[4];
__be32 dst_ipaddr[4];
@@ -3162,11 +3068,11 @@ struct hwrm_cfa_ntuple_filter_cfg_input {
__le16 target_id;
__le64 resp_addr;
__le32 enables;
- #define CFA_NTUPLE_FILTER_CFG_REQ_ENABLES_NEW_DST_VNIC_ID_VALID 0x1UL
- #define CFA_NTUPLE_FILTER_CFG_REQ_ENABLES_NEW_MIRROR_VNIC_ID_VALID 0x2UL
+ #define CFA_NTUPLE_FILTER_CFG_REQ_ENABLES_NEW_DST_ID 0x1UL
+ #define CFA_NTUPLE_FILTER_CFG_REQ_ENABLES_NEW_MIRROR_VNIC_ID 0x2UL
__le32 unused_0;
__le64 ntuple_filter_id;
- __le32 new_dst_vnic_id;
+ __le32 new_dst_id;
__le32 new_mirror_vnic_id;
};
@@ -3192,16 +3098,8 @@ struct hwrm_tunnel_dst_port_query_input {
__le16 target_id;
__le64 resp_addr;
u8 tunnel_type;
- #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_NONTUNNEL (0x0UL << 0)
#define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_VXLAN (0x1UL << 0)
- #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_NVGRE (0x2UL << 0)
- #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_L2GRE (0x3UL << 0)
- #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_IPIP (0x4UL << 0)
#define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_GENEVE (0x5UL << 0)
- #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_MPLS (0x6UL << 0)
- #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_STT (0x7UL << 0)
- #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_IPGRE (0x8UL << 0)
- #define TUNNEL_DST_PORT_QUERY_REQ_TUNNEL_TYPE_ANYTUNNEL (0xffUL << 0)
u8 unused_0[7];
};
@@ -3228,16 +3126,8 @@ struct hwrm_tunnel_dst_port_alloc_input {
__le16 target_id;
__le64 resp_addr;
u8 tunnel_type;
- #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_NONTUNNEL (0x0UL << 0)
#define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_VXLAN (0x1UL << 0)
- #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_NVGRE (0x2UL << 0)
- #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_L2GRE (0x3UL << 0)
- #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_IPIP (0x4UL << 0)
#define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_GENEVE (0x5UL << 0)
- #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_MPLS (0x6UL << 0)
- #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_STT (0x7UL << 0)
- #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_IPGRE (0x8UL << 0)
- #define TUNNEL_DST_PORT_ALLOC_REQ_TUNNEL_TYPE_ANYTUNNEL (0xffUL << 0)
u8 unused_0;
__be16 tunnel_dst_port_val;
__le32 unused_1;
@@ -3267,16 +3157,8 @@ struct hwrm_tunnel_dst_port_free_input {
__le16 target_id;
__le64 resp_addr;
u8 tunnel_type;
- #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_NONTUNNEL (0x0UL << 0)
#define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_VXLAN (0x1UL << 0)
- #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_NVGRE (0x2UL << 0)
- #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_L2GRE (0x3UL << 0)
- #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_IPIP (0x4UL << 0)
#define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_GENEVE (0x5UL << 0)
- #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_MPLS (0x6UL << 0)
- #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_STT (0x7UL << 0)
- #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_IPGRE (0x8UL << 0)
- #define TUNNEL_DST_PORT_FREE_REQ_TUNNEL_TYPE_ANYTUNNEL (0xffUL << 0)
u8 unused_0;
__le16 tunnel_dst_port_id;
__le32 unused_1;
@@ -3416,68 +3298,145 @@ struct hwrm_stat_ctx_clr_stats_output {
u8 valid;
};
-/* hwrm_mgmt_l2_filter_alloc */
-/* Input (56 bytes) */
-struct hwrm_mgmt_l2_filter_alloc_input {
+/* hwrm_fw_reset */
+/* Input (24 bytes) */
+struct hwrm_fw_reset_input {
__le16 req_type;
__le16 cmpl_ring;
__le16 seq_id;
__le16 target_id;
__le64 resp_addr;
- __le32 flags;
- #define MGMT_L2_FILTER_ALLOC_REQ_FLAGS_PATH 0x1UL
- #define MGMT_L2_FILTER_ALLOC_REQ_FLAGS_PATH_TX (0x0UL << 0)
- #define MGMT_L2_FILTER_ALLOC_REQ_FLAGS_PATH_RX (0x1UL << 0)
- __le32 enables;
- #define MGMT_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDRESS 0x1UL
- #define MGMT_L2_FILTER_ALLOC_REQ_ENABLES_OVLAN 0x2UL
- #define MGMT_L2_FILTER_ALLOC_REQ_ENABLES_IVLAN 0x4UL
- #define MGMT_L2_FILTER_ALLOC_REQ_ENABLES_ACTION_ID 0x8UL
- u8 l2_address[6];
+ u8 embedded_proc_type;
+ #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_BOOT (0x0UL << 0)
+ #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_MGMT (0x1UL << 0)
+ #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_NETCTRL (0x2UL << 0)
+ #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_ROCE (0x3UL << 0)
+ #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_RSVD (0x4UL << 0)
+ u8 selfrst_status;
+ #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTNONE (0x0UL << 0)
+ #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTASAP (0x1UL << 0)
+ #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST (0x2UL << 0)
+ __le16 unused_0[3];
+};
+
+/* Output (16 bytes) */
+struct hwrm_fw_reset_output {
+ __le16 error_code;
+ __le16 req_type;
+ __le16 seq_id;
+ __le16 resp_len;
+ u8 selfrst_status;
+ #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTNONE (0x0UL << 0)
+ #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTASAP (0x1UL << 0)
+ #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTPCIERST (0x2UL << 0)
u8 unused_0;
+ __le16 unused_1;
+ u8 unused_2;
+ u8 unused_3;
+ u8 unused_4;
+ u8 valid;
+};
+
+/* hwrm_exec_fwd_resp */
+/* Input (128 bytes) */
+struct hwrm_exec_fwd_resp_input {
+ __le16 req_type;
+ __le16 cmpl_ring;
+ __le16 seq_id;
+ __le16 target_id;
+ __le64 resp_addr;
+ __le32 encap_request[26];
+ __le16 encap_resp_target_id;
+ __le16 unused_0[3];
+};
+
+/* Output (16 bytes) */
+struct hwrm_exec_fwd_resp_output {
+ __le16 error_code;
+ __le16 req_type;
+ __le16 seq_id;
+ __le16 resp_len;
+ __le32 unused_0;
u8 unused_1;
- u8 l2_address_mask[6];
- __le16 ovlan;
- __le16 ovlan_mask;
- __le16 ivlan;
- __le16 ivlan_mask;
u8 unused_2;
u8 unused_3;
- __le32 action_id;
- u8 action_bypass;
- #define MGMT_L2_FILTER_ALLOC_REQ_ACTION_BYPASS 0x1UL
- u8 unused_5[3];
+ u8 valid;
+};
+
+/* hwrm_reject_fwd_resp */
+/* Input (128 bytes) */
+struct hwrm_reject_fwd_resp_input {
+ __le16 req_type;
+ __le16 cmpl_ring;
+ __le16 seq_id;
+ __le16 target_id;
+ __le64 resp_addr;
+ __le32 encap_request[26];
+ __le16 encap_resp_target_id;
+ __le16 unused_0[3];
};
/* Output (16 bytes) */
-struct hwrm_mgmt_l2_filter_alloc_output {
+struct hwrm_reject_fwd_resp_output {
__le16 error_code;
__le16 req_type;
__le16 seq_id;
__le16 resp_len;
- __le16 mgmt_l2_filter_id;
+ __le32 unused_0;
+ u8 unused_1;
+ u8 unused_2;
+ u8 unused_3;
+ u8 valid;
+};
+
+/* hwrm_fwd_resp */
+/* Input (40 bytes) */
+struct hwrm_fwd_resp_input {
+ __le16 req_type;
+ __le16 cmpl_ring;
+ __le16 seq_id;
+ __le16 target_id;
+ __le64 resp_addr;
+ __le16 encap_resp_target_id;
+ __le16 encap_resp_cmpl_ring;
+ __le16 encap_resp_len;
u8 unused_0;
u8 unused_1;
+ __le64 encap_resp_addr;
+ __le32 encap_resp[24];
+};
+
+/* Output (16 bytes) */
+struct hwrm_fwd_resp_output {
+ __le16 error_code;
+ __le16 req_type;
+ __le16 seq_id;
+ __le16 resp_len;
+ __le32 unused_0;
+ u8 unused_1;
u8 unused_2;
u8 unused_3;
- u8 unused_4;
u8 valid;
};
-/* hwrm_mgmt_l2_filter_free */
-/* Input (24 bytes) */
-struct hwrm_mgmt_l2_filter_free_input {
+/* hwrm_fwd_async_event_cmpl */
+/* Input (32 bytes) */
+struct hwrm_fwd_async_event_cmpl_input {
__le16 req_type;
__le16 cmpl_ring;
__le16 seq_id;
__le16 target_id;
__le64 resp_addr;
- __le16 mgmt_l2_filter_id;
- __le16 unused_0[3];
+ __le16 encap_async_event_target_id;
+ u8 unused_0;
+ u8 unused_1;
+ u8 unused_2[3];
+ u8 unused_3;
+ __le32 encap_async_event_cmpl[4];
};
/* Output (16 bytes) */
-struct hwrm_mgmt_l2_filter_free_output {
+struct hwrm_fwd_async_event_cmpl_output {
__le16 error_code;
__le16 req_type;
__le16 seq_id;
@@ -3489,6 +3448,31 @@ struct hwrm_mgmt_l2_filter_free_output {
u8 valid;
};
+/* hwrm_temp_monitor_query */
+/* Input (16 bytes) */
+struct hwrm_temp_monitor_query_input {
+ __le16 req_type;
+ __le16 cmpl_ring;
+ __le16 seq_id;
+ __le16 target_id;
+ __le64 resp_addr;
+};
+
+/* Output (16 bytes) */
+struct hwrm_temp_monitor_query_output {
+ __le16 error_code;
+ __le16 req_type;
+ __le16 seq_id;
+ __le16 resp_len;
+ u8 temp;
+ u8 unused_0;
+ __le16 unused_1;
+ u8 unused_2;
+ u8 unused_3;
+ u8 unused_4;
+ u8 valid;
+};
+
/* hwrm_nvm_raw_write_blk */
/* Input (32 bytes) */
struct hwrm_nvm_raw_write_blk_input {
@@ -3621,7 +3605,7 @@ struct hwrm_nvm_get_dir_info_output {
};
/* hwrm_nvm_write */
-/* Input (40 bytes) */
+/* Input (48 bytes) */
struct hwrm_nvm_write_input {
__le16 req_type;
__le16 cmpl_ring;
@@ -3637,6 +3621,8 @@ struct hwrm_nvm_write_input {
__le16 option;
__le16 flags;
#define NVM_WRITE_REQ_FLAGS_KEEP_ORIG_ACTIVE_IMG 0x1UL
+ __le32 dir_item_length;
+ __le32 unused_0;
};
/* Output (16 bytes) */
@@ -3645,10 +3631,9 @@ struct hwrm_nvm_write_output {
__le16 req_type;
__le16 seq_id;
__le16 resp_len;
- __le32 unused_0;
- u8 unused_1;
- u8 unused_2;
- u8 unused_3;
+ __le32 dir_item_length;
+ __le16 dir_idx;
+ u8 unused_0;
u8 valid;
};
@@ -3833,214 +3818,4 @@ struct hwrm_nvm_verify_update_output {
u8 valid;
};
-/* hwrm_exec_fwd_resp */
-/* Input (120 bytes) */
-struct hwrm_exec_fwd_resp_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- __le32 encap_request[24];
- __le16 encap_resp_target_id;
- __le16 unused_0[3];
-};
-
-/* Output (16 bytes) */
-struct hwrm_exec_fwd_resp_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- __le32 unused_0;
- u8 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 valid;
-};
-
-/* hwrm_reject_fwd_resp */
-/* Input (120 bytes) */
-struct hwrm_reject_fwd_resp_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- __le32 encap_request[24];
- __le16 encap_resp_target_id;
- __le16 unused_0[3];
-};
-
-/* Output (16 bytes) */
-struct hwrm_reject_fwd_resp_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- __le32 unused_0;
- u8 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 valid;
-};
-
-/* hwrm_fwd_resp */
-/* Input (40 bytes) */
-struct hwrm_fwd_resp_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- __le16 encap_resp_target_id;
- __le16 encap_resp_cmpl_ring;
- __le16 encap_resp_len;
- u8 unused_0;
- u8 unused_1;
- __le64 encap_resp_addr;
- __le32 encap_resp[24];
-};
-
-/* Output (16 bytes) */
-struct hwrm_fwd_resp_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- __le32 unused_0;
- u8 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 valid;
-};
-
-/* hwrm_fwd_async_event_cmpl */
-/* Input (32 bytes) */
-struct hwrm_fwd_async_event_cmpl_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- __le16 encap_async_event_target_id;
- u8 unused_0;
- u8 unused_1;
- u8 unused_2[3];
- u8 unused_3;
- __le32 encap_async_event_cmpl[4];
-};
-
-/* Output (16 bytes) */
-struct hwrm_fwd_async_event_cmpl_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- __le32 unused_0;
- u8 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 valid;
-};
-
-/* hwrm_fw_reset */
-/* Input (24 bytes) */
-struct hwrm_fw_reset_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- u8 embedded_proc_type;
- #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_CHIMP (0x0UL << 0)
- #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_APE (0x1UL << 0)
- #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_KONG (0x2UL << 0)
- #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_BONO (0x3UL << 0)
- #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_TANG (0x4UL << 0)
- u8 selfrst_status;
- #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTNONE (0x0UL << 0)
- #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTASAP (0x1UL << 0)
- #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST (0x2UL << 0)
- __le16 unused_0[3];
-};
-
-/* Output (16 bytes) */
-struct hwrm_fw_reset_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- u8 selfrst_status;
- #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTNONE (0x0UL << 0)
- #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTASAP (0x1UL << 0)
- #define FW_RESET_RESP_SELFRST_STATUS_SELFRSTPCIERST (0x2UL << 0)
- u8 unused_0;
- __le16 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 unused_4;
- u8 valid;
-};
-
-/* hwrm_fw_qstatus */
-/* Input (24 bytes) */
-struct hwrm_fw_qstatus_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
- u8 embedded_proc_type;
- #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_CHIMP (0x0UL << 0)
- #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_APE (0x1UL << 0)
- #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_KONG (0x2UL << 0)
- #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_BONO (0x3UL << 0)
- #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_TANG (0x4UL << 0)
- u8 unused_0[7];
-};
-
-/* Output (16 bytes) */
-struct hwrm_fw_qstatus_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- u8 selfrst_status;
- #define FW_QSTATUS_RESP_SELFRST_STATUS_SELFRSTNONE (0x0UL << 0)
- #define FW_QSTATUS_RESP_SELFRST_STATUS_SELFRSTASAP (0x1UL << 0)
- #define FW_QSTATUS_RESP_SELFRST_STATUS_SELFRSTPCIERST (0x2UL << 0)
- u8 unused_0;
- __le16 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 unused_4;
- u8 valid;
-};
-
-/* hwrm_temp_monitor_query */
-/* Input (16 bytes) */
-struct hwrm_temp_monitor_query_input {
- __le16 req_type;
- __le16 cmpl_ring;
- __le16 seq_id;
- __le16 target_id;
- __le64 resp_addr;
-};
-
-/* Output (16 bytes) */
-struct hwrm_temp_monitor_query_output {
- __le16 error_code;
- __le16 req_type;
- __le16 seq_id;
- __le16 resp_len;
- u8 temp;
- u8 unused_0;
- __le16 unused_1;
- u8 unused_2;
- u8 unused_3;
- u8 unused_4;
- u8 valid;
-};
-
#endif
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
index f4cf68861069..c1cc83d7e38c 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
@@ -21,7 +21,7 @@
#ifdef CONFIG_BNXT_SRIOV
static int bnxt_vf_ndo_prep(struct bnxt *bp, int vf_id)
{
- if (bp->state != BNXT_STATE_OPEN) {
+ if (!test_bit(BNXT_STATE_OPEN, &bp->state)) {
netdev_err(bp->dev, "vf ndo called though PF is down\n");
return -EINVAL;
}
@@ -64,7 +64,7 @@ int bnxt_set_vf_spoofchk(struct net_device *dev, int vf_id, bool setting)
* the spoof check should also include vlan anti-spoofing
*/
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1);
- req.vf_id = cpu_to_le16(vf->fw_fid);
+ req.fid = cpu_to_le16(vf->fw_fid);
req.flags = cpu_to_le32(func_flags);
rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
if (!rc) {
@@ -128,7 +128,7 @@ int bnxt_set_vf_mac(struct net_device *dev, int vf_id, u8 *mac)
memcpy(vf->mac_addr, mac, ETH_ALEN);
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1);
- req.vf_id = cpu_to_le16(vf->fw_fid);
+ req.fid = cpu_to_le16(vf->fw_fid);
req.flags = cpu_to_le32(vf->func_flags);
req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_DFLT_MAC_ADDR);
memcpy(req.dflt_mac_addr, mac, ETH_ALEN);
@@ -159,7 +159,7 @@ int bnxt_set_vf_vlan(struct net_device *dev, int vf_id, u16 vlan_id, u8 qos)
return 0;
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1);
- req.vf_id = cpu_to_le16(vf->fw_fid);
+ req.fid = cpu_to_le16(vf->fw_fid);
req.flags = cpu_to_le32(vf->func_flags);
req.dflt_vlan = cpu_to_le16(vlan_tag);
req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_DFLT_VLAN);
@@ -198,7 +198,7 @@ int bnxt_set_vf_bw(struct net_device *dev, int vf_id, int min_tx_rate,
if (min_tx_rate == vf->min_tx_rate && max_tx_rate == vf->max_tx_rate)
return 0;
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1);
- req.vf_id = cpu_to_le16(vf->fw_fid);
+ req.fid = cpu_to_le16(vf->fw_fid);
req.flags = cpu_to_le32(vf->func_flags);
req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_MAX_BW);
req.max_bw = cpu_to_le32(max_tx_rate);
@@ -363,10 +363,11 @@ static int bnxt_hwrm_func_buf_rgtr(struct bnxt *bp)
}
/* only call by PF to reserve resources for VF */
-static int bnxt_hwrm_func_cfg(struct bnxt *bp, int *num_vfs)
+static int bnxt_hwrm_func_cfg(struct bnxt *bp, int num_vfs)
{
u32 rc = 0, mtu, i;
u16 vf_tx_rings, vf_rx_rings, vf_cp_rings, vf_stat_ctx, vf_vnics;
+ u16 vf_ring_grps;
struct hwrm_func_cfg_input req = {0};
struct bnxt_pf_info *pf = &bp->pf;
@@ -378,18 +379,18 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int *num_vfs)
* be removed once new HWRM provides HW ring groups capability in
* hwrm_func_qcap.
*/
- vf_cp_rings = min_t(u16, bp->pf.max_cp_rings, bp->pf.max_stat_ctxs);
- vf_cp_rings = (vf_cp_rings - bp->cp_nr_rings) / *num_vfs;
+ vf_cp_rings = min_t(u16, pf->max_cp_rings, pf->max_stat_ctxs);
+ vf_cp_rings = (vf_cp_rings - bp->cp_nr_rings) / num_vfs;
/* TODO: restore this logic below once the WA above is removed */
- /* vf_cp_rings = (bp->pf.max_cp_rings - bp->cp_nr_rings) / *num_vfs; */
- vf_stat_ctx = (bp->pf.max_stat_ctxs - bp->num_stat_ctxs) / *num_vfs;
+ /* vf_cp_rings = (pf->max_cp_rings - bp->cp_nr_rings) / num_vfs; */
+ vf_stat_ctx = (pf->max_stat_ctxs - bp->num_stat_ctxs) / num_vfs;
if (bp->flags & BNXT_FLAG_AGG_RINGS)
- vf_rx_rings = (bp->pf.max_rx_rings - bp->rx_nr_rings * 2) /
- *num_vfs;
+ vf_rx_rings = (pf->max_rx_rings - bp->rx_nr_rings * 2) /
+ num_vfs;
else
- vf_rx_rings = (bp->pf.max_rx_rings - bp->rx_nr_rings) /
- *num_vfs;
- vf_tx_rings = (bp->pf.max_tx_rings - bp->tx_nr_rings) / *num_vfs;
+ vf_rx_rings = (pf->max_rx_rings - bp->rx_nr_rings) / num_vfs;
+ vf_ring_grps = (bp->pf.max_hw_ring_grps - bp->rx_nr_rings) / num_vfs;
+ vf_tx_rings = (pf->max_tx_rings - bp->tx_nr_rings) / num_vfs;
req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_MTU |
FUNC_CFG_REQ_ENABLES_MRU |
@@ -399,7 +400,8 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int *num_vfs)
FUNC_CFG_REQ_ENABLES_NUM_TX_RINGS |
FUNC_CFG_REQ_ENABLES_NUM_RX_RINGS |
FUNC_CFG_REQ_ENABLES_NUM_L2_CTXS |
- FUNC_CFG_REQ_ENABLES_NUM_VNICS);
+ FUNC_CFG_REQ_ENABLES_NUM_VNICS |
+ FUNC_CFG_REQ_ENABLES_NUM_HW_RING_GRPS);
mtu = bp->dev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
req.mru = cpu_to_le16(mtu);
@@ -409,6 +411,7 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int *num_vfs)
req.num_cmpl_rings = cpu_to_le16(vf_cp_rings);
req.num_tx_rings = cpu_to_le16(vf_tx_rings);
req.num_rx_rings = cpu_to_le16(vf_rx_rings);
+ req.num_hw_ring_grps = cpu_to_le16(vf_ring_grps);
req.num_l2_ctxs = cpu_to_le16(4);
vf_vnics = 1;
@@ -417,22 +420,24 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int *num_vfs)
req.num_stat_ctxs = cpu_to_le16(vf_stat_ctx);
mutex_lock(&bp->hwrm_cmd_lock);
- for (i = 0; i < *num_vfs; i++) {
- req.vf_id = cpu_to_le16(pf->first_vf_id + i);
+ for (i = 0; i < num_vfs; i++) {
+ req.fid = cpu_to_le16(pf->first_vf_id + i);
rc = _hwrm_send_message(bp, &req, sizeof(req),
HWRM_CMD_TIMEOUT);
if (rc)
break;
- bp->pf.active_vfs = i + 1;
- bp->pf.vf[i].fw_fid = le16_to_cpu(req.vf_id);
+ pf->active_vfs = i + 1;
+ pf->vf[i].fw_fid = le16_to_cpu(req.fid);
}
mutex_unlock(&bp->hwrm_cmd_lock);
if (!rc) {
- bp->pf.max_pf_tx_rings = bp->tx_nr_rings;
- if (bp->flags & BNXT_FLAG_AGG_RINGS)
- bp->pf.max_pf_rx_rings = bp->rx_nr_rings * 2;
- else
- bp->pf.max_pf_rx_rings = bp->rx_nr_rings;
+ pf->max_tx_rings -= vf_tx_rings * num_vfs;
+ pf->max_rx_rings -= vf_rx_rings * num_vfs;
+ pf->max_hw_ring_grps -= vf_ring_grps * num_vfs;
+ pf->max_cp_rings -= vf_cp_rings * num_vfs;
+ pf->max_rsscos_ctxs -= num_vfs;
+ pf->max_stat_ctxs -= vf_stat_ctx * num_vfs;
+ pf->max_vnics -= vf_vnics * num_vfs;
}
return rc;
}
@@ -492,7 +497,7 @@ static int bnxt_sriov_enable(struct bnxt *bp, int *num_vfs)
goto err_out1;
/* Reserve resources for VFs */
- rc = bnxt_hwrm_func_cfg(bp, num_vfs);
+ rc = bnxt_hwrm_func_cfg(bp, *num_vfs);
if (rc)
goto err_out2;
@@ -536,8 +541,8 @@ void bnxt_sriov_disable(struct bnxt *bp)
bnxt_free_vf_resources(bp);
bp->pf.active_vfs = 0;
- bp->pf.max_pf_rx_rings = bp->pf.max_rx_rings;
- bp->pf.max_pf_tx_rings = bp->pf.max_tx_rings;
+ /* Reclaim all resources for the PF. */
+ bnxt_hwrm_func_qcaps(bp);
}
int bnxt_sriov_configure(struct pci_dev *pdev, int num_vfs)
@@ -595,6 +600,7 @@ static int bnxt_hwrm_fwd_resp(struct bnxt *bp, struct bnxt_vf_info *vf,
/* Set the new target id */
req.target_id = cpu_to_le16(vf->fw_fid);
+ req.encap_resp_target_id = cpu_to_le16(vf->fw_fid);
req.encap_resp_len = cpu_to_le16(msg_size);
req.encap_resp_addr = encap_resp_addr;
req.encap_resp_cmpl_ring = encap_resp_cpr;
@@ -629,6 +635,7 @@ static int bnxt_hwrm_fwd_err_resp(struct bnxt *bp, struct bnxt_vf_info *vf,
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_REJECT_FWD_RESP, -1, -1);
/* Set the new target id */
req.target_id = cpu_to_le16(vf->fw_fid);
+ req.encap_resp_target_id = cpu_to_le16(vf->fw_fid);
memcpy(req.encap_request, vf->hwrm_cmd_req_addr, msg_size);
mutex_lock(&bp->hwrm_cmd_lock);
@@ -660,6 +667,7 @@ static int bnxt_hwrm_exec_fwd_resp(struct bnxt *bp, struct bnxt_vf_info *vf,
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_EXEC_FWD_RESP, -1, -1);
/* Set the new target id */
req.target_id = cpu_to_le16(vf->fw_fid);
+ req.encap_resp_target_id = cpu_to_le16(vf->fw_fid);
memcpy(req.encap_request, vf->hwrm_cmd_req_addr, msg_size);
mutex_lock(&bp->hwrm_cmd_lock);
@@ -804,10 +812,9 @@ void bnxt_update_vf_mac(struct bnxt *bp)
if (!is_valid_ether_addr(resp->perm_mac_address))
goto update_vf_mac_exit;
- if (ether_addr_equal(resp->perm_mac_address, bp->vf.mac_addr))
- goto update_vf_mac_exit;
-
- memcpy(bp->vf.mac_addr, resp->perm_mac_address, ETH_ALEN);
+ if (!ether_addr_equal(resp->perm_mac_address, bp->vf.mac_addr))
+ memcpy(bp->vf.mac_addr, resp->perm_mac_address, ETH_ALEN);
+ /* overwrite netdev dev_adr with admin VF MAC */
memcpy(bp->dev->dev_addr, bp->vf.mac_addr, ETH_ALEN);
update_vf_mac_exit:
mutex_unlock(&bp->hwrm_cmd_lock);
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index 17f017ab4dac..b15a60d787c7 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -2041,11 +2041,11 @@ static void bcmgenet_init_tx_napi(struct bcmgenet_priv *priv)
for (i = 0; i < priv->hw_params->tx_queues; ++i) {
ring = &priv->tx_rings[i];
- netif_napi_add(priv->dev, &ring->napi, bcmgenet_tx_poll, 64);
+ netif_tx_napi_add(priv->dev, &ring->napi, bcmgenet_tx_poll, 64);
}
ring = &priv->tx_rings[DESC_INDEX];
- netif_napi_add(priv->dev, &ring->napi, bcmgenet_tx_poll, 64);
+ netif_tx_napi_add(priv->dev, &ring->napi, bcmgenet_tx_poll, 64);
}
static void bcmgenet_enable_tx_napi(struct bcmgenet_priv *priv)
diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c
index 8bdfe53754ba..0d775964b060 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmmii.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c
@@ -401,9 +401,7 @@ int bcmgenet_mii_probe(struct net_device *dev)
* Ethernet MAC ISRs
*/
if (priv->internal_phy)
- priv->mii_bus->irq[phydev->addr] = PHY_IGNORE_INTERRUPT;
- else
- priv->mii_bus->irq[phydev->addr] = PHY_POLL;
+ priv->mii_bus->irq[phydev->mdio.addr] = PHY_IGNORE_INTERRUPT;
return 0;
}
@@ -477,12 +475,6 @@ static int bcmgenet_mii_alloc(struct bcmgenet_priv *priv)
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d",
priv->pdev->name, priv->pdev->id);
- bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
- if (!bus->irq) {
- mdiobus_free(priv->mii_bus);
- return -ENOMEM;
- }
-
return 0;
}
@@ -581,7 +573,7 @@ static int bcmgenet_mii_pd_init(struct bcmgenet_priv *priv)
}
if (pd->phy_address >= 0 && pd->phy_address < PHY_MAX_ADDR)
- phydev = mdio->phy_map[pd->phy_address];
+ phydev = mdiobus_get_phy(mdio, pd->phy_address);
else
phydev = phy_find_first(mdio);
@@ -648,7 +640,6 @@ int bcmgenet_mii_init(struct net_device *dev)
out:
of_node_put(priv->phy_dn);
mdiobus_unregister(priv->mii_bus);
- kfree(priv->mii_bus->irq);
mdiobus_free(priv->mii_bus);
return ret;
}
@@ -659,6 +650,5 @@ void bcmgenet_mii_exit(struct net_device *dev)
of_node_put(priv->phy_dn);
mdiobus_unregister(priv->mii_bus);
- kfree(priv->mii_bus->irq);
mdiobus_free(priv->mii_bus);
}
diff --git a/drivers/net/ethernet/broadcom/sb1250-mac.c b/drivers/net/ethernet/broadcom/sb1250-mac.c
index f557a2aaec23..eacc559679bf 100644
--- a/drivers/net/ethernet/broadcom/sb1250-mac.c
+++ b/drivers/net/ethernet/broadcom/sb1250-mac.c
@@ -238,7 +238,6 @@ struct sbmac_softc {
struct napi_struct napi;
struct phy_device *phy_dev; /* the associated PHY device */
struct mii_bus *mii_bus; /* the MII bus */
- int phy_irq[PHY_MAX_ADDR];
spinlock_t sbm_lock; /* spin lock */
int sbm_devflags; /* current device flags */
@@ -2250,9 +2249,6 @@ static int sbmac_init(struct platform_device *pldev, long long base)
sc->mii_bus->priv = sc;
sc->mii_bus->read = sbmac_mii_read;
sc->mii_bus->write = sbmac_mii_write;
- sc->mii_bus->irq = sc->phy_irq;
- for (i = 0; i < PHY_MAX_ADDR; ++i)
- sc->mii_bus->irq[i] = SBMAC_PHY_INT;
sc->mii_bus->parent = &pldev->dev;
/*
@@ -2358,20 +2354,15 @@ static int sbmac_mii_probe(struct net_device *dev)
{
struct sbmac_softc *sc = netdev_priv(dev);
struct phy_device *phy_dev;
- int i;
- for (i = 0; i < PHY_MAX_ADDR; i++) {
- phy_dev = sc->mii_bus->phy_map[i];
- if (phy_dev)
- break;
- }
+ phy_dev = phy_find_first(sc->mii_bus);
if (!phy_dev) {
printk(KERN_ERR "%s: no PHY found\n", dev->name);
return -ENXIO;
}
- phy_dev = phy_connect(dev, dev_name(&phy_dev->dev), &sbmac_mii_poll,
- PHY_INTERFACE_MODE_GMII);
+ phy_dev = phy_connect(dev, dev_name(&phy_dev->mdio.dev),
+ &sbmac_mii_poll, PHY_INTERFACE_MODE_GMII);
if (IS_ERR(phy_dev)) {
printk(KERN_ERR "%s: could not attach to PHY\n", dev->name);
return PTR_ERR(phy_dev);
@@ -2388,11 +2379,10 @@ static int sbmac_mii_probe(struct net_device *dev)
SUPPORTED_MII |
SUPPORTED_Pause |
SUPPORTED_Asym_Pause;
- phy_dev->advertising = phy_dev->supported;
- pr_info("%s: attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
- dev->name, phy_dev->drv->name,
- dev_name(&phy_dev->dev), phy_dev->irq);
+ phy_attached_info(phy_dev);
+
+ phy_dev->advertising = phy_dev->supported;
sc->phy_dev = phy_dev;
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index 79789d8e52da..9293675df7ba 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -1406,7 +1406,7 @@ static void tg3_mdio_config_5785(struct tg3 *tp)
u32 val;
struct phy_device *phydev;
- phydev = tp->mdio_bus->phy_map[tp->phy_addr];
+ phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
switch (phydev->drv->phy_id & phydev->drv->phy_id_mask) {
case PHY_ID_BCM50610:
case PHY_ID_BCM50610M:
@@ -1538,10 +1538,6 @@ static int tg3_mdio_init(struct tg3 *tp)
tp->mdio_bus->read = &tg3_mdio_read;
tp->mdio_bus->write = &tg3_mdio_write;
tp->mdio_bus->phy_mask = ~(1 << tp->phy_addr);
- tp->mdio_bus->irq = &tp->mdio_irq[0];
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- tp->mdio_bus->irq[i] = PHY_POLL;
/* The bus registration will look for all the PHYs on the mdio bus.
* Unfortunately, it does not ensure the PHY is powered up before
@@ -1558,7 +1554,7 @@ static int tg3_mdio_init(struct tg3 *tp)
return i;
}
- phydev = tp->mdio_bus->phy_map[tp->phy_addr];
+ phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
if (!phydev || !phydev->drv) {
dev_warn(&tp->pdev->dev, "No PHY devices\n");
@@ -1968,7 +1964,7 @@ static void tg3_setup_flow_control(struct tg3 *tp, u32 lcladv, u32 rmtadv)
u32 old_tx_mode = tp->tx_mode;
if (tg3_flag(tp, USE_PHYLIB))
- autoneg = tp->mdio_bus->phy_map[tp->phy_addr]->autoneg;
+ autoneg = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr)->autoneg;
else
autoneg = tp->link_config.autoneg;
@@ -2004,7 +2000,7 @@ static void tg3_adjust_link(struct net_device *dev)
u8 oldflowctrl, linkmesg = 0;
u32 mac_mode, lcl_adv, rmt_adv;
struct tg3 *tp = netdev_priv(dev);
- struct phy_device *phydev = tp->mdio_bus->phy_map[tp->phy_addr];
+ struct phy_device *phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
spin_lock_bh(&tp->lock);
@@ -2093,10 +2089,10 @@ static int tg3_phy_init(struct tg3 *tp)
/* Bring the PHY back to a known state. */
tg3_bmcr_reset(tp);
- phydev = tp->mdio_bus->phy_map[tp->phy_addr];
+ phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
/* Attach the MAC to the PHY. */
- phydev = phy_connect(tp->dev, dev_name(&phydev->dev),
+ phydev = phy_connect(tp->dev, phydev_name(phydev),
tg3_adjust_link, phydev->interface);
if (IS_ERR(phydev)) {
dev_err(&tp->pdev->dev, "Could not attach to PHY\n");
@@ -2120,7 +2116,7 @@ static int tg3_phy_init(struct tg3 *tp)
SUPPORTED_Asym_Pause);
break;
default:
- phy_disconnect(tp->mdio_bus->phy_map[tp->phy_addr]);
+ phy_disconnect(mdiobus_get_phy(tp->mdio_bus, tp->phy_addr));
return -EINVAL;
}
@@ -2128,6 +2124,8 @@ static int tg3_phy_init(struct tg3 *tp)
phydev->advertising = phydev->supported;
+ phy_attached_info(phydev);
+
return 0;
}
@@ -2138,7 +2136,7 @@ static void tg3_phy_start(struct tg3 *tp)
if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
return;
- phydev = tp->mdio_bus->phy_map[tp->phy_addr];
+ phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
if (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) {
tp->phy_flags &= ~TG3_PHYFLG_IS_LOW_POWER;
@@ -2158,13 +2156,13 @@ static void tg3_phy_stop(struct tg3 *tp)
if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
return;
- phy_stop(tp->mdio_bus->phy_map[tp->phy_addr]);
+ phy_stop(mdiobus_get_phy(tp->mdio_bus, tp->phy_addr));
}
static void tg3_phy_fini(struct tg3 *tp)
{
if (tp->phy_flags & TG3_PHYFLG_IS_CONNECTED) {
- phy_disconnect(tp->mdio_bus->phy_map[tp->phy_addr]);
+ phy_disconnect(mdiobus_get_phy(tp->mdio_bus, tp->phy_addr));
tp->phy_flags &= ~TG3_PHYFLG_IS_CONNECTED;
}
}
@@ -4048,7 +4046,7 @@ static int tg3_power_down_prepare(struct tg3 *tp)
struct phy_device *phydev;
u32 phyid, advertising;
- phydev = tp->mdio_bus->phy_map[tp->phy_addr];
+ phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
tp->phy_flags |= TG3_PHYFLG_IS_LOW_POWER;
@@ -12076,7 +12074,7 @@ static int tg3_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
struct phy_device *phydev;
if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
return -EAGAIN;
- phydev = tp->mdio_bus->phy_map[tp->phy_addr];
+ phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
return phy_ethtool_gset(phydev, cmd);
}
@@ -12143,7 +12141,7 @@ static int tg3_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
struct phy_device *phydev;
if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
return -EAGAIN;
- phydev = tp->mdio_bus->phy_map[tp->phy_addr];
+ phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
return phy_ethtool_sset(phydev, cmd);
}
@@ -12298,7 +12296,7 @@ static int tg3_nway_reset(struct net_device *dev)
if (tg3_flag(tp, USE_PHYLIB)) {
if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
return -EAGAIN;
- r = phy_start_aneg(tp->mdio_bus->phy_map[tp->phy_addr]);
+ r = phy_start_aneg(mdiobus_get_phy(tp->mdio_bus, tp->phy_addr));
} else {
u32 bmcr;
@@ -12416,7 +12414,7 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam
u32 newadv;
struct phy_device *phydev;
- phydev = tp->mdio_bus->phy_map[tp->phy_addr];
+ phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
if (!(phydev->supported & SUPPORTED_Pause) ||
(!(phydev->supported & SUPPORTED_Asym_Pause) &&
@@ -13926,7 +13924,7 @@ static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
struct phy_device *phydev;
if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED))
return -EAGAIN;
- phydev = tp->mdio_bus->phy_map[tp->phy_addr];
+ phydev = mdiobus_get_phy(tp->mdio_bus, tp->phy_addr);
return phy_mii_ioctl(phydev, ifr, cmd);
}
@@ -17898,13 +17896,7 @@ static int tg3_init_one(struct pci_dev *pdev,
tg3_bus_string(tp, str),
dev->dev_addr);
- if (tp->phy_flags & TG3_PHYFLG_IS_CONNECTED) {
- struct phy_device *phydev;
- phydev = tp->mdio_bus->phy_map[tp->phy_addr];
- netdev_info(dev,
- "attached PHY driver [%s] (mii_bus:phy_addr=%s)\n",
- phydev->drv->name, dev_name(&phydev->dev));
- } else {
+ if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED)) {
char *ethtype;
if (tp->phy_flags & TG3_PHYFLG_10_100_ONLY)
diff --git a/drivers/net/ethernet/broadcom/tg3.h b/drivers/net/ethernet/broadcom/tg3.h
index 31c9f8295953..3b5e98ecba00 100644
--- a/drivers/net/ethernet/broadcom/tg3.h
+++ b/drivers/net/ethernet/broadcom/tg3.h
@@ -3254,7 +3254,6 @@ struct tg3 {
int pcie_readrq;
struct mii_bus *mdio_bus;
- int mdio_irq[PHY_MAX_ADDR];
int old_link;
u8 phy_addr;
diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index 88c1e1a834f8..c56347536f6b 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -19,6 +19,7 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -28,6 +29,7 @@
#include <linux/phy.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <linux/of_mdio.h>
#include <linux/of_net.h>
@@ -439,12 +441,6 @@ static int macb_mii_init(struct macb *bp)
bp->mii_bus->parent = &bp->dev->dev;
pdata = dev_get_platdata(&bp->pdev->dev);
- bp->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL);
- if (!bp->mii_bus->irq) {
- err = -ENOMEM;
- goto err_out_free_mdiobus;
- }
-
dev_set_drvdata(&bp->dev->dev, bp->mii_bus);
np = bp->pdev->dev.of_node;
@@ -469,9 +465,6 @@ static int macb_mii_init(struct macb *bp)
goto err_out_unregister_bus;
}
} else {
- for (i = 0; i < PHY_MAX_ADDR; i++)
- bp->mii_bus->irq[i] = PHY_POLL;
-
if (pdata)
bp->mii_bus->phy_mask = pdata->phy_mask;
@@ -479,7 +472,7 @@ static int macb_mii_init(struct macb *bp)
}
if (err)
- goto err_out_free_mdio_irq;
+ goto err_out_free_mdiobus;
err = macb_mii_probe(bp->dev);
if (err)
@@ -489,8 +482,6 @@ static int macb_mii_init(struct macb *bp)
err_out_unregister_bus:
mdiobus_unregister(bp->mii_bus);
-err_out_free_mdio_irq:
- kfree(bp->mii_bus->irq);
err_out_free_mdiobus:
mdiobus_free(bp->mii_bus);
err_out:
@@ -1682,6 +1673,8 @@ static void macb_init_hw(struct macb *bp)
macb_set_hwaddr(bp);
config = macb_mdc_clk_div(bp);
+ if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII)
+ config |= GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL);
config |= MACB_BF(RBOF, NET_IP_ALIGN); /* Make eth data aligned */
config |= MACB_BIT(PAE); /* PAuse Enable */
config |= MACB_BIT(DRFCS); /* Discard Rx FCS */
@@ -2120,7 +2113,8 @@ static void macb_get_regs(struct net_device *dev, struct ethtool_regs *regs,
regs_buff[10] = macb_tx_dma(&bp->queues[0], tail);
regs_buff[11] = macb_tx_dma(&bp->queues[0], head);
- regs_buff[12] = macb_or_gem_readl(bp, USRIO);
+ if (!(bp->caps & MACB_CAPS_USRIO_DISABLED))
+ regs_buff[12] = macb_or_gem_readl(bp, USRIO);
if (macb_is_gem(bp)) {
regs_buff[13] = gem_readl(bp, DMACFG);
}
@@ -2399,23 +2393,27 @@ static int macb_init(struct platform_device *pdev)
dev->hw_features &= ~NETIF_F_SG;
dev->features = dev->hw_features;
- val = 0;
- if (bp->phy_interface == PHY_INTERFACE_MODE_RGMII)
- val = GEM_BIT(RGMII);
- else if (bp->phy_interface == PHY_INTERFACE_MODE_RMII &&
- (bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII))
- val = MACB_BIT(RMII);
- else if (!(bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII))
- val = MACB_BIT(MII);
+ if (!(bp->caps & MACB_CAPS_USRIO_DISABLED)) {
+ val = 0;
+ if (bp->phy_interface == PHY_INTERFACE_MODE_RGMII)
+ val = GEM_BIT(RGMII);
+ else if (bp->phy_interface == PHY_INTERFACE_MODE_RMII &&
+ (bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII))
+ val = MACB_BIT(RMII);
+ else if (!(bp->caps & MACB_CAPS_USRIO_DEFAULT_IS_MII))
+ val = MACB_BIT(MII);
- if (bp->caps & MACB_CAPS_USRIO_HAS_CLKEN)
- val |= MACB_BIT(CLKEN);
+ if (bp->caps & MACB_CAPS_USRIO_HAS_CLKEN)
+ val |= MACB_BIT(CLKEN);
- macb_or_gem_writel(bp, USRIO, val);
+ macb_or_gem_writel(bp, USRIO, val);
+ }
/* Set MII management clock divider */
val = macb_mdc_clk_div(bp);
val |= macb_dbw(bp);
+ if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII)
+ val |= GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL);
macb_writel(bp, NCFGR, val);
return 0;
@@ -2772,6 +2770,11 @@ static const struct macb_config emac_config = {
.init = at91ether_init,
};
+static const struct macb_config np4_config = {
+ .caps = MACB_CAPS_USRIO_DISABLED,
+ .clk_init = macb_clk_init,
+ .init = macb_init,
+};
static const struct macb_config zynqmp_config = {
.caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_JUMBO,
@@ -2792,6 +2795,7 @@ static const struct of_device_id macb_dt_ids[] = {
{ .compatible = "cdns,at32ap7000-macb" },
{ .compatible = "cdns,at91sam9260-macb", .data = &at91sam9260_config },
{ .compatible = "cdns,macb" },
+ { .compatible = "cdns,np4-macb", .data = &np4_config },
{ .compatible = "cdns,pc302-gem", .data = &pc302gem_config },
{ .compatible = "cdns,gem", .data = &pc302gem_config },
{ .compatible = "atmel,sama5d2-gem", .data = &sama5d2_config },
@@ -2813,6 +2817,7 @@ static int macb_probe(struct platform_device *pdev)
= macb_clk_init;
int (*init)(struct platform_device *) = macb_init;
struct device_node *np = pdev->dev.of_node;
+ struct device_node *phy_node;
const struct macb_config *macb_config = NULL;
struct clk *pclk, *hclk, *tx_clk;
unsigned int queue_mask, num_queues;
@@ -2900,6 +2905,16 @@ static int macb_probe(struct platform_device *pdev)
else
macb_get_hwaddr(bp);
+ /* Power up the PHY if there is a GPIO reset */
+ phy_node = of_get_next_available_child(np, NULL);
+ if (phy_node) {
+ int gpio = of_get_named_gpio(phy_node, "reset-gpios", 0);
+ if (gpio_is_valid(gpio))
+ bp->reset_gpio = gpio_to_desc(gpio);
+ gpiod_set_value(bp->reset_gpio, GPIOD_OUT_HIGH);
+ }
+ of_node_put(phy_node);
+
err = of_get_phy_mode(np);
if (err < 0) {
pdata = dev_get_platdata(&pdev->dev);
@@ -2933,8 +2948,7 @@ static int macb_probe(struct platform_device *pdev)
dev->base_addr, dev->irq, dev->dev_addr);
phydev = bp->phy_dev;
- netdev_info(dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
- phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
+ phy_attached_info(phydev);
return 0;
@@ -2964,8 +2978,11 @@ static int macb_remove(struct platform_device *pdev)
if (bp->phy_dev)
phy_disconnect(bp->phy_dev);
mdiobus_unregister(bp->mii_bus);
- kfree(bp->mii_bus->irq);
mdiobus_free(bp->mii_bus);
+
+ /* Shutdown the PHY if there is a GPIO reset */
+ gpiod_set_value(bp->reset_gpio, GPIOD_OUT_LOW);
+
unregister_netdev(dev);
clk_disable_unprepare(bp->tx_clk);
clk_disable_unprepare(bp->hclk);
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index 6e1faea00ca8..0d4ecfcd60b7 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -215,12 +215,17 @@
/* GEM specific NCFGR bitfields. */
#define GEM_GBE_OFFSET 10 /* Gigabit mode enable */
#define GEM_GBE_SIZE 1
+#define GEM_PCSSEL_OFFSET 11
+#define GEM_PCSSEL_SIZE 1
#define GEM_CLK_OFFSET 18 /* MDC clock division */
#define GEM_CLK_SIZE 3
#define GEM_DBW_OFFSET 21 /* Data bus width */
#define GEM_DBW_SIZE 2
#define GEM_RXCOEN_OFFSET 24
#define GEM_RXCOEN_SIZE 1
+#define GEM_SGMIIEN_OFFSET 27
+#define GEM_SGMIIEN_SIZE 1
+
/* Constants for data bus width. */
#define GEM_DBW32 0 /* 32 bit AMBA AHB data bus width */
@@ -395,6 +400,7 @@
#define MACB_CAPS_USRIO_HAS_CLKEN 0x00000002
#define MACB_CAPS_USRIO_DEFAULT_IS_MII 0x00000004
#define MACB_CAPS_NO_GIGABIT_HALF 0x00000008
+#define MACB_CAPS_USRIO_DISABLED 0x00000010
#define MACB_CAPS_FIFO_MODE 0x10000000
#define MACB_CAPS_GIGABIT_MODE_AVAILABLE 0x20000000
#define MACB_CAPS_SG_DISABLED 0x40000000
@@ -824,6 +830,7 @@ struct macb {
unsigned int dma_burst_length;
phy_interface_t phy_interface;
+ struct gpio_desc *reset_gpio;
/* AT91RM9200 transmit */
struct sk_buff *skb; /* holds skb until xmit interrupt completes */
diff --git a/drivers/net/ethernet/cavium/thunder/nic.h b/drivers/net/ethernet/cavium/thunder/nic.h
index d3950b20feb9..688828865c48 100644
--- a/drivers/net/ethernet/cavium/thunder/nic.h
+++ b/drivers/net/ethernet/cavium/thunder/nic.h
@@ -120,10 +120,9 @@
* Calculated for SCLK of 700Mhz
* value written should be a 1/16th of what is expected
*
- * 1 tick per 0.05usec = value of 2.2
- * This 10% would be covered in CQ timer thresh value
+ * 1 tick per 0.025usec
*/
-#define NICPF_CLK_PER_INT_TICK 2
+#define NICPF_CLK_PER_INT_TICK 1
/* Time to wait before we decide that a SQ is stuck.
*
@@ -266,6 +265,7 @@ struct nicvf {
u8 tns_mode:1;
u8 sqs_mode:1;
u8 loopback_supported:1;
+ bool hw_tso;
u16 mtu;
struct queue_set *qs;
#define MAX_SQS_PER_VF_SINGLE_NODE 5
@@ -490,6 +490,11 @@ static inline int nic_get_node_id(struct pci_dev *pdev)
return ((addr >> NIC_NODE_ID_SHIFT) & NIC_NODE_ID_MASK);
}
+static inline bool pass1_silicon(struct pci_dev *pdev)
+{
+ return pdev->revision < 8;
+}
+
int nicvf_set_real_num_queues(struct net_device *netdev,
int tx_queues, int rx_queues);
int nicvf_open(struct net_device *netdev);
diff --git a/drivers/net/ethernet/cavium/thunder/nic_main.c b/drivers/net/ethernet/cavium/thunder/nic_main.c
index c561fdcb79a7..4dded90076c8 100644
--- a/drivers/net/ethernet/cavium/thunder/nic_main.c
+++ b/drivers/net/ethernet/cavium/thunder/nic_main.c
@@ -54,11 +54,6 @@ struct nicpf {
bool irq_allocated[NIC_PF_MSIX_VECTORS];
};
-static inline bool pass1_silicon(struct nicpf *nic)
-{
- return nic->pdev->revision < 8;
-}
-
/* Supported devices */
static const struct pci_device_id nic_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_NIC_PF) },
@@ -122,7 +117,7 @@ static void nic_send_msg_to_vf(struct nicpf *nic, int vf, union nic_mbx *mbx)
* when PF writes to MBOX(1), in next revisions when
* PF writes to MBOX(0)
*/
- if (pass1_silicon(nic)) {
+ if (pass1_silicon(nic->pdev)) {
/* see the comment for nic_reg_write()/nic_reg_read()
* functions above
*/
@@ -397,7 +392,7 @@ static void nic_config_cpi(struct nicpf *nic, struct cpi_cfg_msg *cfg)
padd = cpi % 8; /* 3 bits CS out of 6bits DSCP */
/* Leave RSS_SIZE as '0' to disable RSS */
- if (pass1_silicon(nic)) {
+ if (pass1_silicon(nic->pdev)) {
nic_reg_write(nic, NIC_PF_CPI_0_2047_CFG | (cpi << 3),
(vnic << 24) | (padd << 16) |
(rssi_base + rssi));
@@ -467,7 +462,7 @@ static void nic_config_rss(struct nicpf *nic, struct rss_cfg_msg *cfg)
}
cpi_base = nic->cpi_base[cfg->vf_id];
- if (pass1_silicon(nic))
+ if (pass1_silicon(nic->pdev))
idx_addr = NIC_PF_CPI_0_2047_CFG;
else
idx_addr = NIC_PF_MPI_0_2047_CFG;
@@ -615,6 +610,21 @@ static int nic_config_loopback(struct nicpf *nic, struct set_loopback *lbk)
return 0;
}
+static void nic_enable_vf(struct nicpf *nic, int vf, bool enable)
+{
+ int bgx, lmac;
+
+ nic->vf_enabled[vf] = enable;
+
+ if (vf >= nic->num_vf_en)
+ return;
+
+ bgx = NIC_GET_BGX_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]);
+ lmac = NIC_GET_LMAC_FROM_VF_LMAC_MAP(nic->vf_lmac_map[vf]);
+
+ bgx_lmac_rx_tx_enable(nic->node, bgx, lmac, enable);
+}
+
/* Interrupt handler to handle mailbox messages from VFs */
static void nic_handle_mbx_intr(struct nicpf *nic, int vf)
{
@@ -714,14 +724,14 @@ static void nic_handle_mbx_intr(struct nicpf *nic, int vf)
break;
case NIC_MBOX_MSG_CFG_DONE:
/* Last message of VF config msg sequence */
- nic->vf_enabled[vf] = true;
+ nic_enable_vf(nic, vf, true);
goto unlock;
case NIC_MBOX_MSG_SHUTDOWN:
/* First msg in VF teardown sequence */
- nic->vf_enabled[vf] = false;
if (vf >= nic->num_vf_en)
nic->sqs_used[vf - nic->num_vf_en] = false;
nic->pqs_vf[vf] = 0;
+ nic_enable_vf(nic, vf, false);
break;
case NIC_MBOX_MSG_ALLOC_SQS:
nic_alloc_sqs(nic, &mbx.sqs_alloc);
@@ -1074,8 +1084,7 @@ static void nic_remove(struct pci_dev *pdev)
if (nic->check_link) {
/* Destroy work Queue */
- cancel_delayed_work(&nic->dwork);
- flush_workqueue(nic->check_link);
+ cancel_delayed_work_sync(&nic->dwork);
destroy_workqueue(nic->check_link);
}
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c
index af54c10945c2..a12b2e38cf61 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c
@@ -112,6 +112,13 @@ static int nicvf_get_settings(struct net_device *netdev,
cmd->supported = 0;
cmd->transceiver = XCVR_EXTERNAL;
+
+ if (!nic->link_up) {
+ cmd->duplex = DUPLEX_UNKNOWN;
+ ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
+ return 0;
+ }
+
if (nic->speed <= 1000) {
cmd->port = PORT_MII;
cmd->autoneg = AUTONEG_ENABLE;
@@ -125,6 +132,13 @@ static int nicvf_get_settings(struct net_device *netdev,
return 0;
}
+static u32 nicvf_get_link(struct net_device *netdev)
+{
+ struct nicvf *nic = netdev_priv(netdev);
+
+ return nic->link_up;
+}
+
static void nicvf_get_drvinfo(struct net_device *netdev,
struct ethtool_drvinfo *info)
{
@@ -660,7 +674,7 @@ static int nicvf_set_channels(struct net_device *dev,
static const struct ethtool_ops nicvf_ethtool_ops = {
.get_settings = nicvf_get_settings,
- .get_link = ethtool_op_get_link,
+ .get_link = nicvf_get_link,
.get_drvinfo = nicvf_get_drvinfo,
.get_msglevel = nicvf_get_msglevel,
.set_msglevel = nicvf_set_msglevel,
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
index 7f709cbdcd87..c24cb2a86a42 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
@@ -525,14 +525,22 @@ static void nicvf_snd_pkt_handler(struct net_device *netdev,
__func__, cqe_tx->sq_qs, cqe_tx->sq_idx,
cqe_tx->sqe_ptr, hdr->subdesc_cnt);
- nicvf_put_sq_desc(sq, hdr->subdesc_cnt + 1);
nicvf_check_cqe_tx_errs(nic, cq, cqe_tx);
skb = (struct sk_buff *)sq->skbuff[cqe_tx->sqe_ptr];
- /* For TSO offloaded packets only one head SKB needs to be freed */
+ /* For TSO offloaded packets only one SQE will have a valid SKB */
if (skb) {
+ nicvf_put_sq_desc(sq, hdr->subdesc_cnt + 1);
prefetch(skb);
dev_consume_skb_any(skb);
sq->skbuff[cqe_tx->sqe_ptr] = (u64)NULL;
+ } else {
+ /* In case of HW TSO, HW sends a CQE for each segment of a TSO
+ * packet instead of a single CQE for the whole TSO packet
+ * transmitted. Each of this CQE points to the same SQE, so
+ * avoid freeing same SQE multiple times.
+ */
+ if (!nic->hw_tso)
+ nicvf_put_sq_desc(sq, hdr->subdesc_cnt + 1);
}
}
@@ -1057,6 +1065,7 @@ int nicvf_stop(struct net_device *netdev)
netif_carrier_off(netdev);
netif_tx_stop_all_queues(nic->netdev);
+ nic->link_up = false;
/* Teardown secondary qsets first */
if (!nic->sqs_mode) {
@@ -1211,9 +1220,6 @@ int nicvf_open(struct net_device *netdev)
nic->drv_stats.txq_stop = 0;
nic->drv_stats.txq_wake = 0;
- netif_carrier_on(netdev);
- netif_tx_start_all_queues(netdev);
-
return 0;
cleanup:
nicvf_disable_intr(nic, NICVF_INTR_MBOX, 0);
@@ -1551,6 +1557,9 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
netdev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
+ if (!pass1_silicon(nic->pdev))
+ nic->hw_tso = true;
+
netdev->netdev_ops = &nicvf_netdev_ops;
netdev->watchdog_timeo = NICVF_TX_TIMEOUT;
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
index e404ea837727..d0d1b5490061 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c
@@ -18,14 +18,6 @@
#include "q_struct.h"
#include "nicvf_queues.h"
-struct rbuf_info {
- struct page *page;
- void *data;
- u64 offset;
-};
-
-#define GET_RBUF_INFO(x) ((struct rbuf_info *)(x - NICVF_RCV_BUF_ALIGN_BYTES))
-
/* Poll a register for a specific value */
static int nicvf_poll_reg(struct nicvf *nic, int qidx,
u64 reg, int bit_pos, int bits, int val)
@@ -86,8 +78,6 @@ static void nicvf_free_q_desc_mem(struct nicvf *nic, struct q_desc_mem *dmem)
static inline int nicvf_alloc_rcv_buffer(struct nicvf *nic, gfp_t gfp,
u32 buf_len, u64 **rbuf)
{
- u64 data;
- struct rbuf_info *rinfo;
int order = get_order(buf_len);
/* Check if request can be accomodated in previous allocated page */
@@ -113,46 +103,28 @@ static inline int nicvf_alloc_rcv_buffer(struct nicvf *nic, gfp_t gfp,
nic->rb_page_offset = 0;
}
- data = (u64)page_address(nic->rb_page) + nic->rb_page_offset;
-
- /* Align buffer addr to cache line i.e 128 bytes */
- rinfo = (struct rbuf_info *)(data + NICVF_RCV_BUF_ALIGN_LEN(data));
- /* Save page address for reference updation */
- rinfo->page = nic->rb_page;
- /* Store start address for later retrieval */
- rinfo->data = (void *)data;
- /* Store alignment offset */
- rinfo->offset = NICVF_RCV_BUF_ALIGN_LEN(data);
+ *rbuf = (u64 *)((u64)page_address(nic->rb_page) + nic->rb_page_offset);
- data += rinfo->offset;
-
- /* Give next aligned address to hw for DMA */
- *rbuf = (u64 *)(data + NICVF_RCV_BUF_ALIGN_BYTES);
return 0;
}
-/* Retrieve actual buffer start address and build skb for received packet */
+/* Build skb around receive buffer */
static struct sk_buff *nicvf_rb_ptr_to_skb(struct nicvf *nic,
u64 rb_ptr, int len)
{
+ void *data;
struct sk_buff *skb;
- struct rbuf_info *rinfo;
- rb_ptr = (u64)phys_to_virt(rb_ptr);
- /* Get buffer start address and alignment offset */
- rinfo = GET_RBUF_INFO(rb_ptr);
+ data = phys_to_virt(rb_ptr);
/* Now build an skb to give to stack */
- skb = build_skb(rinfo->data, RCV_FRAG_LEN);
+ skb = build_skb(data, RCV_FRAG_LEN);
if (!skb) {
- put_page(rinfo->page);
+ put_page(virt_to_page(data));
return NULL;
}
- /* Set correct skb->data */
- skb_reserve(skb, rinfo->offset + NICVF_RCV_BUF_ALIGN_BYTES);
-
- prefetch((void *)rb_ptr);
+ prefetch(skb->data);
return skb;
}
@@ -196,7 +168,6 @@ static void nicvf_free_rbdr(struct nicvf *nic, struct rbdr *rbdr)
int head, tail;
u64 buf_addr;
struct rbdr_entry_t *desc;
- struct rbuf_info *rinfo;
if (!rbdr)
return;
@@ -212,16 +183,14 @@ static void nicvf_free_rbdr(struct nicvf *nic, struct rbdr *rbdr)
while (head != tail) {
desc = GET_RBDR_DESC(rbdr, head);
buf_addr = desc->buf_addr << NICVF_RCV_BUF_ALIGN;
- rinfo = GET_RBUF_INFO((u64)phys_to_virt(buf_addr));
- put_page(rinfo->page);
+ put_page(virt_to_page(phys_to_virt(buf_addr)));
head++;
head &= (rbdr->dmem.q_len - 1);
}
/* Free SKB of tail desc */
desc = GET_RBDR_DESC(rbdr, tail);
buf_addr = desc->buf_addr << NICVF_RCV_BUF_ALIGN;
- rinfo = GET_RBUF_INFO((u64)phys_to_virt(buf_addr));
- put_page(rinfo->page);
+ put_page(virt_to_page(phys_to_virt(buf_addr)));
/* Free RBDR ring */
nicvf_free_q_desc_mem(nic, &rbdr->dmem);
@@ -330,7 +299,7 @@ static int nicvf_init_cmp_queue(struct nicvf *nic,
return err;
cq->desc = cq->dmem.base;
- cq->thresh = CMP_QUEUE_CQE_THRESH;
+ cq->thresh = pass1_silicon(nic->pdev) ? 0 : CMP_QUEUE_CQE_THRESH;
nic->cq_coalesce_usecs = (CMP_QUEUE_TIMER_THRESH * 0.05) - 1;
return 0;
@@ -592,7 +561,7 @@ void nicvf_cmp_queue_config(struct nicvf *nic, struct queue_set *qs,
/* Set threshold value for interrupt generation */
nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_THRESH, qidx, cq->thresh);
nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_CFG2,
- qidx, nic->cq_coalesce_usecs);
+ qidx, CMP_QUEUE_TIMER_THRESH);
}
/* Configures transmit queue */
@@ -956,7 +925,7 @@ static int nicvf_sq_subdesc_required(struct nicvf *nic, struct sk_buff *skb)
{
int subdesc_cnt = MIN_SQ_DESC_PER_PKT_XMIT;
- if (skb_shinfo(skb)->gso_size) {
+ if (skb_shinfo(skb)->gso_size && !nic->hw_tso) {
subdesc_cnt = nicvf_tso_count_subdescs(skb);
return subdesc_cnt;
}
@@ -971,7 +940,7 @@ static int nicvf_sq_subdesc_required(struct nicvf *nic, struct sk_buff *skb)
* First subdescriptor for every send descriptor.
*/
static inline void
-nicvf_sq_add_hdr_subdesc(struct snd_queue *sq, int qentry,
+nicvf_sq_add_hdr_subdesc(struct nicvf *nic, struct snd_queue *sq, int qentry,
int subdesc_cnt, struct sk_buff *skb, int len)
{
int proto;
@@ -1007,6 +976,15 @@ nicvf_sq_add_hdr_subdesc(struct snd_queue *sq, int qentry,
break;
}
}
+
+ if (nic->hw_tso && skb_shinfo(skb)->gso_size) {
+ hdr->tso = 1;
+ hdr->tso_start = skb_transport_offset(skb) + tcp_hdrlen(skb);
+ hdr->tso_max_paysize = skb_shinfo(skb)->gso_size;
+ /* For non-tunneled pkts, point this to L2 ethertype */
+ hdr->inner_l3_offset = skb_network_offset(skb) - 2;
+ nic->drv_stats.tx_tso++;
+ }
}
/* SQ GATHER subdescriptor
@@ -1076,7 +1054,7 @@ static int nicvf_sq_append_tso(struct nicvf *nic, struct snd_queue *sq,
data_left -= size;
tso_build_data(skb, &tso, size);
}
- nicvf_sq_add_hdr_subdesc(sq, hdr_qentry,
+ nicvf_sq_add_hdr_subdesc(nic, sq, hdr_qentry,
seg_subdescs - 1, skb, seg_len);
sq->skbuff[hdr_qentry] = (u64)NULL;
qentry = nicvf_get_nxt_sqentry(sq, qentry);
@@ -1129,11 +1107,12 @@ int nicvf_sq_append_skb(struct nicvf *nic, struct sk_buff *skb)
qentry = nicvf_get_sq_desc(sq, subdesc_cnt);
/* Check if its a TSO packet */
- if (skb_shinfo(skb)->gso_size)
+ if (skb_shinfo(skb)->gso_size && !nic->hw_tso)
return nicvf_sq_append_tso(nic, sq, sq_num, qentry, skb);
/* Add SQ header subdesc */
- nicvf_sq_add_hdr_subdesc(sq, qentry, subdesc_cnt - 1, skb, skb->len);
+ nicvf_sq_add_hdr_subdesc(nic, sq, qentry, subdesc_cnt - 1,
+ skb, skb->len);
/* Add SQ gather subdescs */
qentry = nicvf_get_nxt_sqentry(sq, qentry);
@@ -1234,153 +1213,93 @@ struct sk_buff *nicvf_get_rcv_skb(struct nicvf *nic, struct cqe_rx_t *cqe_rx)
return skb;
}
-/* Enable interrupt */
-void nicvf_enable_intr(struct nicvf *nic, int int_type, int q_idx)
+static u64 nicvf_int_type_to_mask(int int_type, int q_idx)
{
u64 reg_val;
- reg_val = nicvf_reg_read(nic, NIC_VF_ENA_W1S);
-
switch (int_type) {
case NICVF_INTR_CQ:
- reg_val |= ((1ULL << q_idx) << NICVF_INTR_CQ_SHIFT);
+ reg_val = ((1ULL << q_idx) << NICVF_INTR_CQ_SHIFT);
break;
case NICVF_INTR_SQ:
- reg_val |= ((1ULL << q_idx) << NICVF_INTR_SQ_SHIFT);
+ reg_val = ((1ULL << q_idx) << NICVF_INTR_SQ_SHIFT);
break;
case NICVF_INTR_RBDR:
- reg_val |= ((1ULL << q_idx) << NICVF_INTR_RBDR_SHIFT);
+ reg_val = ((1ULL << q_idx) << NICVF_INTR_RBDR_SHIFT);
break;
case NICVF_INTR_PKT_DROP:
- reg_val |= (1ULL << NICVF_INTR_PKT_DROP_SHIFT);
+ reg_val = (1ULL << NICVF_INTR_PKT_DROP_SHIFT);
break;
case NICVF_INTR_TCP_TIMER:
- reg_val |= (1ULL << NICVF_INTR_TCP_TIMER_SHIFT);
+ reg_val = (1ULL << NICVF_INTR_TCP_TIMER_SHIFT);
break;
case NICVF_INTR_MBOX:
- reg_val |= (1ULL << NICVF_INTR_MBOX_SHIFT);
+ reg_val = (1ULL << NICVF_INTR_MBOX_SHIFT);
break;
case NICVF_INTR_QS_ERR:
- reg_val |= (1ULL << NICVF_INTR_QS_ERR_SHIFT);
+ reg_val = (1ULL << NICVF_INTR_QS_ERR_SHIFT);
break;
default:
- netdev_err(nic->netdev,
- "Failed to enable interrupt: unknown type\n");
- break;
+ reg_val = 0;
}
- nicvf_reg_write(nic, NIC_VF_ENA_W1S, reg_val);
+ return reg_val;
+}
+
+/* Enable interrupt */
+void nicvf_enable_intr(struct nicvf *nic, int int_type, int q_idx)
+{
+ u64 mask = nicvf_int_type_to_mask(int_type, q_idx);
+
+ if (!mask) {
+ netdev_dbg(nic->netdev,
+ "Failed to enable interrupt: unknown type\n");
+ return;
+ }
+ nicvf_reg_write(nic, NIC_VF_ENA_W1S,
+ nicvf_reg_read(nic, NIC_VF_ENA_W1S) | mask);
}
/* Disable interrupt */
void nicvf_disable_intr(struct nicvf *nic, int int_type, int q_idx)
{
- u64 reg_val = 0;
+ u64 mask = nicvf_int_type_to_mask(int_type, q_idx);
- switch (int_type) {
- case NICVF_INTR_CQ:
- reg_val |= ((1ULL << q_idx) << NICVF_INTR_CQ_SHIFT);
- break;
- case NICVF_INTR_SQ:
- reg_val |= ((1ULL << q_idx) << NICVF_INTR_SQ_SHIFT);
- break;
- case NICVF_INTR_RBDR:
- reg_val |= ((1ULL << q_idx) << NICVF_INTR_RBDR_SHIFT);
- break;
- case NICVF_INTR_PKT_DROP:
- reg_val |= (1ULL << NICVF_INTR_PKT_DROP_SHIFT);
- break;
- case NICVF_INTR_TCP_TIMER:
- reg_val |= (1ULL << NICVF_INTR_TCP_TIMER_SHIFT);
- break;
- case NICVF_INTR_MBOX:
- reg_val |= (1ULL << NICVF_INTR_MBOX_SHIFT);
- break;
- case NICVF_INTR_QS_ERR:
- reg_val |= (1ULL << NICVF_INTR_QS_ERR_SHIFT);
- break;
- default:
- netdev_err(nic->netdev,
+ if (!mask) {
+ netdev_dbg(nic->netdev,
"Failed to disable interrupt: unknown type\n");
- break;
+ return;
}
- nicvf_reg_write(nic, NIC_VF_ENA_W1C, reg_val);
+ nicvf_reg_write(nic, NIC_VF_ENA_W1C, mask);
}
/* Clear interrupt */
void nicvf_clear_intr(struct nicvf *nic, int int_type, int q_idx)
{
- u64 reg_val = 0;
+ u64 mask = nicvf_int_type_to_mask(int_type, q_idx);
- switch (int_type) {
- case NICVF_INTR_CQ:
- reg_val = ((1ULL << q_idx) << NICVF_INTR_CQ_SHIFT);
- break;
- case NICVF_INTR_SQ:
- reg_val = ((1ULL << q_idx) << NICVF_INTR_SQ_SHIFT);
- break;
- case NICVF_INTR_RBDR:
- reg_val = ((1ULL << q_idx) << NICVF_INTR_RBDR_SHIFT);
- break;
- case NICVF_INTR_PKT_DROP:
- reg_val = (1ULL << NICVF_INTR_PKT_DROP_SHIFT);
- break;
- case NICVF_INTR_TCP_TIMER:
- reg_val = (1ULL << NICVF_INTR_TCP_TIMER_SHIFT);
- break;
- case NICVF_INTR_MBOX:
- reg_val = (1ULL << NICVF_INTR_MBOX_SHIFT);
- break;
- case NICVF_INTR_QS_ERR:
- reg_val |= (1ULL << NICVF_INTR_QS_ERR_SHIFT);
- break;
- default:
- netdev_err(nic->netdev,
+ if (!mask) {
+ netdev_dbg(nic->netdev,
"Failed to clear interrupt: unknown type\n");
- break;
+ return;
}
- nicvf_reg_write(nic, NIC_VF_INT, reg_val);
+ nicvf_reg_write(nic, NIC_VF_INT, mask);
}
/* Check if interrupt is enabled */
int nicvf_is_intr_enabled(struct nicvf *nic, int int_type, int q_idx)
{
- u64 reg_val;
- u64 mask = 0xff;
-
- reg_val = nicvf_reg_read(nic, NIC_VF_ENA_W1S);
-
- switch (int_type) {
- case NICVF_INTR_CQ:
- mask = ((1ULL << q_idx) << NICVF_INTR_CQ_SHIFT);
- break;
- case NICVF_INTR_SQ:
- mask = ((1ULL << q_idx) << NICVF_INTR_SQ_SHIFT);
- break;
- case NICVF_INTR_RBDR:
- mask = ((1ULL << q_idx) << NICVF_INTR_RBDR_SHIFT);
- break;
- case NICVF_INTR_PKT_DROP:
- mask = NICVF_INTR_PKT_DROP_MASK;
- break;
- case NICVF_INTR_TCP_TIMER:
- mask = NICVF_INTR_TCP_TIMER_MASK;
- break;
- case NICVF_INTR_MBOX:
- mask = NICVF_INTR_MBOX_MASK;
- break;
- case NICVF_INTR_QS_ERR:
- mask = NICVF_INTR_QS_ERR_MASK;
- break;
- default:
- netdev_err(nic->netdev,
+ u64 mask = nicvf_int_type_to_mask(int_type, q_idx);
+ /* If interrupt type is unknown, we treat it disabled. */
+ if (!mask) {
+ netdev_dbg(nic->netdev,
"Failed to check interrupt enable: unknown type\n");
- break;
+ return 0;
}
- return (reg_val & mask);
+ return mask & nicvf_reg_read(nic, NIC_VF_ENA_W1S);
}
void nicvf_update_rq_stats(struct nicvf *nic, int rq_idx)
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h
index fb4957d09914..c5030a7f213a 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h
@@ -75,18 +75,16 @@
*/
#define CMP_QSIZE CMP_QUEUE_SIZE2
#define CMP_QUEUE_LEN (1ULL << (CMP_QSIZE + 10))
-#define CMP_QUEUE_CQE_THRESH 0
-#define CMP_QUEUE_TIMER_THRESH 220 /* 10usec */
+#define CMP_QUEUE_CQE_THRESH (NAPI_POLL_WEIGHT / 2)
+#define CMP_QUEUE_TIMER_THRESH 80 /* ~2usec */
#define RBDR_SIZE RBDR_SIZE0
#define RCV_BUF_COUNT (1ULL << (RBDR_SIZE + 13))
#define MAX_RCV_BUF_COUNT (1ULL << (RBDR_SIZE6 + 13))
#define RBDR_THRESH (RCV_BUF_COUNT / 2)
#define DMA_BUFFER_LEN 2048 /* In multiples of 128bytes */
-#define RCV_FRAG_LEN (SKB_DATA_ALIGN(DMA_BUFFER_LEN + NET_SKB_PAD) + \
- SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + \
- (NICVF_RCV_BUF_ALIGN_BYTES * 2))
-#define RCV_DATA_OFFSET NICVF_RCV_BUF_ALIGN_BYTES
+#define RCV_FRAG_LEN (SKB_DATA_ALIGN(DMA_BUFFER_LEN + NET_SKB_PAD) + \
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
#define MAX_CQES_FOR_TX ((SND_QUEUE_LEN / MIN_SQ_DESC_PER_PKT_XMIT) * \
MAX_CQE_PER_PKT_XMIT)
@@ -108,10 +106,6 @@
#define NICVF_SQ_BASE_ALIGN_BYTES 128 /* 7 bits */
#define NICVF_ALIGNED_ADDR(ADDR, ALIGN_BYTES) ALIGN(ADDR, ALIGN_BYTES)
-#define NICVF_ADDR_ALIGN_LEN(ADDR, BYTES)\
- (NICVF_ALIGNED_ADDR(ADDR, BYTES) - BYTES)
-#define NICVF_RCV_BUF_ALIGN_LEN(X)\
- (NICVF_ALIGNED_ADDR(X, NICVF_RCV_BUF_ALIGN_BYTES) - X)
/* Queue enable/disable */
#define NICVF_SQ_EN BIT_ULL(19)
diff --git a/drivers/net/ethernet/cavium/thunder/q_struct.h b/drivers/net/ethernet/cavium/thunder/q_struct.h
index 3c1de97b1add..9e6d9876bfd0 100644
--- a/drivers/net/ethernet/cavium/thunder/q_struct.h
+++ b/drivers/net/ethernet/cavium/thunder/q_struct.h
@@ -545,25 +545,28 @@ struct sq_hdr_subdesc {
u64 subdesc_cnt:8;
u64 csum_l4:2;
u64 csum_l3:1;
- u64 rsvd0:5;
+ u64 csum_inner_l4:2;
+ u64 csum_inner_l3:1;
+ u64 rsvd0:2;
u64 l4_offset:8;
u64 l3_offset:8;
u64 rsvd1:4;
u64 tot_len:20; /* W0 */
- u64 tso_sdc_cont:8;
- u64 tso_sdc_first:8;
- u64 tso_l4_offset:8;
- u64 tso_flags_last:12;
- u64 tso_flags_first:12;
- u64 rsvd2:2;
+ u64 rsvd2:24;
+ u64 inner_l4_offset:8;
+ u64 inner_l3_offset:8;
+ u64 tso_start:8;
+ u64 rsvd3:2;
u64 tso_max_paysize:14; /* W1 */
#elif defined(__LITTLE_ENDIAN_BITFIELD)
u64 tot_len:20;
u64 rsvd1:4;
u64 l3_offset:8;
u64 l4_offset:8;
- u64 rsvd0:5;
+ u64 rsvd0:2;
+ u64 csum_inner_l3:1;
+ u64 csum_inner_l4:2;
u64 csum_l3:1;
u64 csum_l4:2;
u64 subdesc_cnt:8;
@@ -574,12 +577,11 @@ struct sq_hdr_subdesc {
u64 subdesc_type:4; /* W0 */
u64 tso_max_paysize:14;
- u64 rsvd2:2;
- u64 tso_flags_first:12;
- u64 tso_flags_last:12;
- u64 tso_l4_offset:8;
- u64 tso_sdc_first:8;
- u64 tso_sdc_cont:8; /* W1 */
+ u64 rsvd3:2;
+ u64 tso_start:8;
+ u64 inner_l3_offset:8;
+ u64 inner_l4_offset:8;
+ u64 rsvd2:24; /* W1 */
#endif
};
diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
index 180aa9fabf48..9df26c2263bc 100644
--- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
+++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c
@@ -186,6 +186,23 @@ void bgx_set_lmac_mac(int node, int bgx_idx, int lmacid, const u8 *mac)
}
EXPORT_SYMBOL(bgx_set_lmac_mac);
+void bgx_lmac_rx_tx_enable(int node, int bgx_idx, int lmacid, bool enable)
+{
+ struct bgx *bgx = bgx_vnic[(node * MAX_BGX_PER_CN88XX) + bgx_idx];
+ u64 cfg;
+
+ if (!bgx)
+ return;
+
+ cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
+ if (enable)
+ cfg |= CMR_PKT_RX_EN | CMR_PKT_TX_EN;
+ else
+ cfg &= ~(CMR_PKT_RX_EN | CMR_PKT_TX_EN);
+ bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
+}
+EXPORT_SYMBOL(bgx_lmac_rx_tx_enable);
+
static void bgx_sgmii_change_link_state(struct lmac *lmac)
{
struct bgx *bgx = lmac->bgx;
@@ -612,6 +629,8 @@ static void bgx_poll_for_link(struct work_struct *work)
lmac->last_duplex = 1;
} else {
lmac->link_up = 0;
+ lmac->last_speed = SPEED_UNKNOWN;
+ lmac->last_duplex = DUPLEX_UNKNOWN;
}
if (lmac->last_link != lmac->link_up) {
@@ -654,8 +673,7 @@ static int bgx_lmac_enable(struct bgx *bgx, u8 lmacid)
}
/* Enable lmac */
- bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG,
- CMR_EN | CMR_PKT_RX_EN | CMR_PKT_TX_EN);
+ bgx_reg_modify(bgx, lmacid, BGX_CMRX_CFG, CMR_EN);
/* Restore default cfg, incase low level firmware changed it */
bgx_reg_write(bgx, lmacid, BGX_CMRX_RX_DMAC_CTL, 0x03);
@@ -695,8 +713,7 @@ static void bgx_lmac_disable(struct bgx *bgx, u8 lmacid)
lmac = &bgx->lmac[lmacid];
if (lmac->check_link) {
/* Destroy work queue */
- cancel_delayed_work(&lmac->dwork);
- flush_workqueue(lmac->check_link);
+ cancel_delayed_work_sync(&lmac->dwork);
destroy_workqueue(lmac->check_link);
}
@@ -1009,6 +1026,9 @@ static int bgx_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
struct bgx *bgx = NULL;
u8 lmac;
+ /* Load octeon mdio driver */
+ octeon_mdiobus_force_mod_depencency();
+
bgx = devm_kzalloc(dev, sizeof(*bgx), GFP_KERNEL);
if (!bgx)
return -ENOMEM;
diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.h b/drivers/net/ethernet/cavium/thunder/thunder_bgx.h
index 07b7ec66c60d..149e179363a1 100644
--- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.h
+++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.h
@@ -182,6 +182,8 @@ enum MCAST_MODE {
#define BCAST_ACCEPT 1
#define CAM_ACCEPT 1
+void octeon_mdiobus_force_mod_depencency(void);
+void bgx_lmac_rx_tx_enable(int node, int bgx_idx, int lmacid, bool enable);
void bgx_add_dmac_addr(u64 dmac, int node, int bgx_idx, int lmac);
unsigned bgx_get_map(int node);
int bgx_get_lmac_count(int node, int bgx);
diff --git a/drivers/net/ethernet/chelsio/Kconfig b/drivers/net/ethernet/chelsio/Kconfig
index a79813a17b6e..4d187f22c48b 100644
--- a/drivers/net/ethernet/chelsio/Kconfig
+++ b/drivers/net/ethernet/chelsio/Kconfig
@@ -65,13 +65,14 @@ config CHELSIO_T3
will be called cxgb3.
config CHELSIO_T4
- tristate "Chelsio Communications T4/T5 Ethernet support"
+ tristate "Chelsio Communications T4/T5/T6 Ethernet support"
depends on PCI && (IPV6 || IPV6=n)
select FW_LOADER
select MDIO
---help---
- This driver supports Chelsio T4 and T5 based gigabit, 10Gb Ethernet
- adapter and T5 based 40Gb Ethernet adapter.
+ This driver supports Chelsio T4, T5 & T6 based gigabit, 10Gb Ethernet
+ adapter and T5/T6 based 40Gb and T6 based 25Gb, 50Gb and 100Gb
+ Ethernet adapters.
For general information about Chelsio and our products, visit
our website at <http://www.chelsio.com>.
@@ -85,7 +86,7 @@ config CHELSIO_T4
will be called cxgb4.
config CHELSIO_T4_DCB
- bool "Data Center Bridging (DCB) Support for Chelsio T4/T5 cards"
+ bool "Data Center Bridging (DCB) Support for Chelsio T4/T5/T6 cards"
default n
depends on CHELSIO_T4 && DCB
---help---
@@ -107,12 +108,12 @@ config CHELSIO_T4_FCOE
If unsure, say N.
config CHELSIO_T4VF
- tristate "Chelsio Communications T4/T5 Virtual Function Ethernet support"
+ tristate "Chelsio Communications T4/T5/T6 Virtual Function Ethernet support"
depends on PCI
---help---
- This driver supports Chelsio T4 and T5 based gigabit, 10Gb Ethernet
- adapters and T5 based 40Gb Ethernet adapters with PCI-E SR-IOV Virtual
- Functions.
+ This driver supports Chelsio T4, T5 & T6 based gigabit, 10Gb Ethernet
+ adapters and T5/T6 based 40Gb and T6 based 25Gb, 50Gb and 100Gb
+ Ethernet adapters with PCI-E SR-IOV Virtual Functions.
For general information about Chelsio and our products, visit
our website at <http://www.chelsio.com>.
diff --git a/drivers/net/ethernet/chelsio/cxgb/cphy.h b/drivers/net/ethernet/chelsio/cxgb/cphy.h
index a4d2a4c08d3f..bf43da6c6a63 100644
--- a/drivers/net/ethernet/chelsio/cxgb/cphy.h
+++ b/drivers/net/ethernet/chelsio/cxgb/cphy.h
@@ -137,7 +137,7 @@ static inline int simple_mdio_write(struct cphy *cphy, int reg,
/* Convenience initializer */
static inline void cphy_init(struct cphy *phy, struct net_device *dev,
- int phy_addr, struct cphy_ops *phy_ops,
+ int phy_addr, const struct cphy_ops *phy_ops,
const struct mdio_ops *mdio_ops)
{
struct adapter *adapter = netdev_priv(dev);
diff --git a/drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.c b/drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.c
index 71018a4fdf15..76ce6e538326 100644
--- a/drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.c
+++ b/drivers/net/ethernet/chelsio/cxgb/mv88e1xxx.c
@@ -337,7 +337,7 @@ static void mv88e1xxx_destroy(struct cphy *cphy)
kfree(cphy);
}
-static struct cphy_ops mv88e1xxx_ops = {
+static const struct cphy_ops mv88e1xxx_ops = {
.destroy = mv88e1xxx_destroy,
.reset = mv88e1xxx_reset,
.interrupt_enable = mv88e1xxx_interrupt_enable,
diff --git a/drivers/net/ethernet/chelsio/cxgb/mv88x201x.c b/drivers/net/ethernet/chelsio/cxgb/mv88x201x.c
index d0cf611551a1..7ddb301bcba0 100644
--- a/drivers/net/ethernet/chelsio/cxgb/mv88x201x.c
+++ b/drivers/net/ethernet/chelsio/cxgb/mv88x201x.c
@@ -195,7 +195,7 @@ static void mv88x201x_destroy(struct cphy *cphy)
kfree(cphy);
}
-static struct cphy_ops mv88x201x_ops = {
+static const struct cphy_ops mv88x201x_ops = {
.destroy = mv88x201x_destroy,
.reset = mv88x201x_reset,
.interrupt_enable = mv88x201x_interrupt_enable,
diff --git a/drivers/net/ethernet/chelsio/cxgb/my3126.c b/drivers/net/ethernet/chelsio/cxgb/my3126.c
index a683fd3bb624..d546f46c8ef7 100644
--- a/drivers/net/ethernet/chelsio/cxgb/my3126.c
+++ b/drivers/net/ethernet/chelsio/cxgb/my3126.c
@@ -154,7 +154,7 @@ static void my3126_destroy(struct cphy *cphy)
kfree(cphy);
}
-static struct cphy_ops my3126_ops = {
+static const struct cphy_ops my3126_ops = {
.destroy = my3126_destroy,
.reset = my3126_reset,
.interrupt_enable = my3126_interrupt_enable,
diff --git a/drivers/net/ethernet/chelsio/cxgb/pm3393.c b/drivers/net/ethernet/chelsio/cxgb/pm3393.c
index ec5e05052d99..eb462d7db427 100644
--- a/drivers/net/ethernet/chelsio/cxgb/pm3393.c
+++ b/drivers/net/ethernet/chelsio/cxgb/pm3393.c
@@ -570,7 +570,7 @@ static void pm3393_destroy(struct cmac *cmac)
kfree(cmac);
}
-static struct cmac_ops pm3393_ops = {
+static const struct cmac_ops pm3393_ops = {
.destroy = pm3393_destroy,
.reset = pm3393_reset,
.interrupt_enable = pm3393_interrupt_enable,
diff --git a/drivers/net/ethernet/chelsio/cxgb/vsc7326.c b/drivers/net/ethernet/chelsio/cxgb/vsc7326.c
index b0cb388f5e12..6f30b6f78553 100644
--- a/drivers/net/ethernet/chelsio/cxgb/vsc7326.c
+++ b/drivers/net/ethernet/chelsio/cxgb/vsc7326.c
@@ -666,7 +666,7 @@ static void mac_destroy(struct cmac *mac)
kfree(mac);
}
-static struct cmac_ops vsc7326_ops = {
+static const struct cmac_ops vsc7326_ops = {
.destroy = mac_destroy,
.reset = mac_reset,
.interrupt_handler = mac_intr_handler,
diff --git a/drivers/net/ethernet/chelsio/cxgb3/ael1002.c b/drivers/net/ethernet/chelsio/cxgb3/ael1002.c
index 2028da95afa1..dadf11e3dddb 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/ael1002.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/ael1002.c
@@ -198,7 +198,7 @@ static int get_link_status_r(struct cphy *phy, int *link_ok, int *speed,
return 0;
}
-static struct cphy_ops ael1002_ops = {
+static const struct cphy_ops ael1002_ops = {
.reset = ael1002_reset,
.intr_enable = ael1002_intr_noop,
.intr_disable = ael1002_intr_noop,
@@ -224,7 +224,7 @@ static int ael1006_reset(struct cphy *phy, int wait)
return t3_phy_reset(phy, MDIO_MMD_PMAPMD, wait);
}
-static struct cphy_ops ael1006_ops = {
+static const struct cphy_ops ael1006_ops = {
.reset = ael1006_reset,
.intr_enable = t3_phy_lasi_intr_enable,
.intr_disable = t3_phy_lasi_intr_disable,
@@ -495,7 +495,7 @@ static int ael2005_intr_handler(struct cphy *phy)
return ret ? ret : cphy_cause_link_change;
}
-static struct cphy_ops ael2005_ops = {
+static const struct cphy_ops ael2005_ops = {
.reset = ael2005_reset,
.intr_enable = ael2005_intr_enable,
.intr_disable = ael2005_intr_disable,
@@ -801,7 +801,7 @@ static int ael2020_intr_handler(struct cphy *phy)
return ret ? ret : cphy_cause_link_change;
}
-static struct cphy_ops ael2020_ops = {
+static const struct cphy_ops ael2020_ops = {
.reset = ael2020_reset,
.intr_enable = ael2020_intr_enable,
.intr_disable = ael2020_intr_disable,
@@ -856,7 +856,7 @@ static int get_link_status_x(struct cphy *phy, int *link_ok, int *speed,
return 0;
}
-static struct cphy_ops qt2045_ops = {
+static const struct cphy_ops qt2045_ops = {
.reset = ael1006_reset,
.intr_enable = t3_phy_lasi_intr_enable,
.intr_disable = t3_phy_lasi_intr_disable,
@@ -921,7 +921,7 @@ static int xaui_direct_power_down(struct cphy *phy, int enable)
return 0;
}
-static struct cphy_ops xaui_direct_ops = {
+static const struct cphy_ops xaui_direct_ops = {
.reset = xaui_direct_reset,
.intr_enable = ael1002_intr_noop,
.intr_disable = ael1002_intr_noop,
diff --git a/drivers/net/ethernet/chelsio/cxgb3/aq100x.c b/drivers/net/ethernet/chelsio/cxgb3/aq100x.c
index 341b7ef1508f..6af5d200e44f 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/aq100x.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/aq100x.c
@@ -247,7 +247,7 @@ static int aq100x_get_link_status(struct cphy *phy, int *link_ok,
return 0;
}
-static struct cphy_ops aq100x_ops = {
+static const struct cphy_ops aq100x_ops = {
.reset = aq100x_reset,
.intr_enable = aq100x_intr_enable,
.intr_disable = aq100x_intr_disable,
diff --git a/drivers/net/ethernet/chelsio/cxgb3/common.h b/drivers/net/ethernet/chelsio/cxgb3/common.h
index 442480982d3f..1bd7d89666c4 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/common.h
+++ b/drivers/net/ethernet/chelsio/cxgb3/common.h
@@ -575,7 +575,7 @@ static inline int t3_mdio_write(struct cphy *phy, int mmd, int reg,
/* Convenience initializer */
static inline void cphy_init(struct cphy *phy, struct adapter *adapter,
- int phy_addr, struct cphy_ops *phy_ops,
+ int phy_addr, const struct cphy_ops *phy_ops,
const struct mdio_ops *mdio_ops,
unsigned int caps, const char *desc)
{
diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
index 8f7aa53a4c4b..60908eab3b3a 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
@@ -701,15 +701,16 @@ static ssize_t attr_store(struct device *d,
ssize_t(*set) (struct net_device *, unsigned int),
unsigned int min_val, unsigned int max_val)
{
- char *endp;
ssize_t ret;
unsigned int val;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- val = simple_strtoul(buf, &endp, 0);
- if (endp == buf || val < min_val || val > max_val)
+ ret = kstrtouint(buf, 0, &val);
+ if (ret)
+ return ret;
+ if (val < min_val || val > max_val)
return -EINVAL;
rtnl_lock();
@@ -829,14 +830,15 @@ static ssize_t tm_attr_store(struct device *d,
struct port_info *pi = netdev_priv(to_net_dev(d));
struct adapter *adap = pi->adapter;
unsigned int val;
- char *endp;
ssize_t ret;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- val = simple_strtoul(buf, &endp, 0);
- if (endp == buf || val > 10000000)
+ ret = kstrtouint(buf, 0, &val);
+ if (ret)
+ return ret;
+ if (val > 10000000)
return -EINVAL;
rtnl_lock();
diff --git a/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c b/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c
index a22768c94200..ee04caa6c4d8 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c
@@ -709,11 +709,21 @@ static int get_vpd_params(struct adapter *adapter, struct vpd_params *p)
return ret;
}
- p->cclk = simple_strtoul(vpd.cclk_data, NULL, 10);
- p->mclk = simple_strtoul(vpd.mclk_data, NULL, 10);
- p->uclk = simple_strtoul(vpd.uclk_data, NULL, 10);
- p->mdc = simple_strtoul(vpd.mdc_data, NULL, 10);
- p->mem_timing = simple_strtoul(vpd.mt_data, NULL, 10);
+ ret = kstrtouint(vpd.cclk_data, 10, &p->cclk);
+ if (ret)
+ return ret;
+ ret = kstrtouint(vpd.mclk_data, 10, &p->mclk);
+ if (ret)
+ return ret;
+ ret = kstrtouint(vpd.uclk_data, 10, &p->uclk);
+ if (ret)
+ return ret;
+ ret = kstrtouint(vpd.mdc_data, 10, &p->mdc);
+ if (ret)
+ return ret;
+ ret = kstrtouint(vpd.mt_data, 10, &p->mem_timing);
+ if (ret)
+ return ret;
memcpy(p->sn, vpd.sn_data, SERNUM_LEN);
/* Old eeproms didn't have port information */
@@ -723,8 +733,12 @@ static int get_vpd_params(struct adapter *adapter, struct vpd_params *p)
} else {
p->port_type[0] = hex_to_bin(vpd.port0_data[0]);
p->port_type[1] = hex_to_bin(vpd.port1_data[0]);
- p->xauicfg[0] = simple_strtoul(vpd.xaui0cfg_data, NULL, 16);
- p->xauicfg[1] = simple_strtoul(vpd.xaui1cfg_data, NULL, 16);
+ ret = kstrtou16(vpd.xaui0cfg_data, 16, &p->xauicfg[0]);
+ if (ret)
+ return ret;
+ ret = kstrtou16(vpd.xaui1cfg_data, 16, &p->xauicfg[1]);
+ if (ret)
+ return ret;
}
ret = hex2bin(p->eth_base, vpd.na_data, 6);
diff --git a/drivers/net/ethernet/chelsio/cxgb3/vsc8211.c b/drivers/net/ethernet/chelsio/cxgb3/vsc8211.c
index 4f9a1c2724f4..8638ad42bf60 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/vsc8211.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/vsc8211.c
@@ -336,7 +336,7 @@ static int vsc8211_intr_handler(struct cphy *cphy)
return cphy_cause;
}
-static struct cphy_ops vsc8211_ops = {
+static const struct cphy_ops vsc8211_ops = {
.reset = vsc8211_reset,
.intr_enable = vsc8211_intr_enable,
.intr_disable = vsc8211_intr_disable,
@@ -350,7 +350,7 @@ static struct cphy_ops vsc8211_ops = {
.power_down = vsc8211_power_down,
};
-static struct cphy_ops vsc8211_fiber_ops = {
+static const struct cphy_ops vsc8211_fiber_ops = {
.reset = vsc8211_reset,
.intr_enable = vsc8211_intr_enable,
.intr_disable = vsc8211_intr_disable,
diff --git a/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c b/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c
index c308429dd9c7..7ad43af6bde1 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c
@@ -118,6 +118,11 @@ int cxgb4_clip_get(const struct net_device *dev, const u32 *lip, u8 v6)
ret = clip6_get_mbox(dev, (const struct in6_addr *)lip);
if (ret) {
write_unlock_bh(&ctbl->lock);
+ dev_err(adap->pdev_dev,
+ "CLIP FW cmd failed with error %d, "
+ "Connections using %pI6c wont be "
+ "offloaded",
+ ret, ce->addr6.sin6_addr.s6_addr);
return ret;
}
} else {
@@ -127,6 +132,9 @@ int cxgb4_clip_get(const struct net_device *dev, const u32 *lip, u8 v6)
}
} else {
write_unlock_bh(&ctbl->lock);
+ dev_info(adap->pdev_dev, "CLIP table overflow, "
+ "Connections using %pI6c wont be offloaded",
+ (void *)lip);
return -ENOMEM;
}
write_unlock_bh(&ctbl->lock);
@@ -146,6 +154,9 @@ void cxgb4_clip_release(const struct net_device *dev, const u32 *lip, u8 v6)
int hash;
int ret = -1;
+ if (!ctbl)
+ return;
+
hash = clip_addr_hash(ctbl, addr, v6);
read_lock_bh(&ctbl->lock);
@@ -295,6 +306,10 @@ struct clip_tbl *t4_init_clip_tbl(unsigned int clipt_start,
INIT_LIST_HEAD(&ctbl->hash_list[i]);
cl_list = t4_alloc_mem(clipt_size*sizeof(struct clip_entry));
+ if (!cl_list) {
+ t4_free_mem(ctbl);
+ return NULL;
+ }
ctbl->cl_list = (void *)cl_list;
for (i = 0; i < clipt_size; i++) {
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
index 55a47de544ea..ec6e849676c1 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
@@ -301,6 +301,8 @@ struct devlog_params {
/* Stores chip specific parameters */
struct arch_specific_params {
u8 nchan;
+ u8 pm_stats_cnt;
+ u8 cng_ch_bits_log; /* congestion channel map bits width */
u16 mps_rplc_size;
u16 vfcount;
u32 sge_fl_db;
@@ -398,11 +400,10 @@ struct link_config {
enum {
MAX_ETH_QSETS = 32, /* # of Ethernet Tx/Rx queue sets */
- MAX_OFLD_QSETS = 16, /* # of offload Tx/Rx queue sets */
+ MAX_OFLD_QSETS = 16, /* # of offload Tx, iscsi Rx queue sets */
MAX_CTRL_QUEUES = NCHAN, /* # of control Tx queues */
MAX_RDMA_QUEUES = NCHAN, /* # of streaming RDMA Rx queues */
MAX_RDMA_CIQS = 32, /* # of RDMA concentrator IQs */
- MAX_ISCSI_QUEUES = NCHAN, /* # of streaming iSCSI Rx queues */
};
enum {
@@ -420,7 +421,7 @@ enum {
INGQ_EXTRAS = 2, /* firmware event queue and */
/* forwarded interrupts */
MAX_INGQ = MAX_ETH_QSETS + MAX_OFLD_QSETS + MAX_RDMA_QUEUES
- + MAX_RDMA_CIQS + MAX_ISCSI_QUEUES + INGQ_EXTRAS,
+ + MAX_RDMA_CIQS + INGQ_EXTRAS,
};
struct adapter;
@@ -483,6 +484,8 @@ struct sge_fl { /* SGE free-buffer queue state */
unsigned int pidx; /* producer index */
unsigned long alloc_failed; /* # of times buffer allocation failed */
unsigned long large_alloc_failed;
+ unsigned long mapping_err; /* # of RX Buffer DMA Mapping failures */
+ unsigned long low; /* # of times momentarily starving */
unsigned long starving;
/* RO fields */
unsigned int cntxt_id; /* SGE context id for the free list */
@@ -618,6 +621,7 @@ struct sge_ofld_txq { /* state for an SGE offload Tx queue */
struct adapter *adap;
struct sk_buff_head sendq; /* list of backpressured packets */
struct tasklet_struct qresume_tsk; /* restarts the queue */
+ bool service_ofldq_running; /* service_ofldq() is processing sendq */
u8 full; /* the Tx ring is full */
unsigned long mapping_err; /* # of I/O MMU packet mapping errors */
} ____cacheline_aligned_in_smp;
@@ -636,7 +640,7 @@ struct sge {
struct sge_ctrl_txq ctrlq[MAX_CTRL_QUEUES];
struct sge_eth_rxq ethrxq[MAX_ETH_QSETS];
- struct sge_ofld_rxq ofldrxq[MAX_OFLD_QSETS];
+ struct sge_ofld_rxq iscsirxq[MAX_OFLD_QSETS];
struct sge_ofld_rxq rdmarxq[MAX_RDMA_QUEUES];
struct sge_ofld_rxq rdmaciq[MAX_RDMA_CIQS];
struct sge_rspq fw_evtq ____cacheline_aligned_in_smp;
@@ -647,10 +651,10 @@ struct sge {
u16 max_ethqsets; /* # of available Ethernet queue sets */
u16 ethqsets; /* # of active Ethernet queue sets */
u16 ethtxq_rover; /* Tx queue to clean up next */
- u16 ofldqsets; /* # of active offload queue sets */
+ u16 iscsiqsets; /* # of active iSCSI queue sets */
u16 rdmaqs; /* # of available RDMA Rx queues */
u16 rdmaciqs; /* # of available RDMA concentrator IQs */
- u16 ofld_rxq[MAX_OFLD_QSETS];
+ u16 iscsi_rxq[MAX_OFLD_QSETS];
u16 rdma_rxq[MAX_RDMA_QUEUES];
u16 rdma_ciq[MAX_RDMA_CIQS];
u16 timer_val[SGE_NTIMERS];
@@ -676,7 +680,7 @@ struct sge {
};
#define for_each_ethrxq(sge, i) for (i = 0; i < (sge)->ethqsets; i++)
-#define for_each_ofldrxq(sge, i) for (i = 0; i < (sge)->ofldqsets; i++)
+#define for_each_iscsirxq(sge, i) for (i = 0; i < (sge)->iscsiqsets; i++)
#define for_each_rdmarxq(sge, i) for (i = 0; i < (sge)->rdmaqs; i++)
#define for_each_rdmaciq(sge, i) for (i = 0; i < (sge)->rdmaciqs; i++)
@@ -1253,6 +1257,7 @@ int t4_phy_fw_ver(struct adapter *adap, int *phy_fw_ver);
int t4_fwcache(struct adapter *adap, enum fw_params_param_dev_fwcache op);
int t4_fw_upgrade(struct adapter *adap, unsigned int mbox,
const u8 *fw_data, unsigned int size, int force);
+int t4_fl_pkt_align(struct adapter *adap);
unsigned int t4_flash_cfg_addr(struct adapter *adapter);
int t4_check_fw_version(struct adapter *adap);
int t4_get_fw_version(struct adapter *adapter, u32 *vers);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
index 4269944c5db5..e6a4072b494b 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
@@ -757,8 +757,8 @@ static int pm_stats_show(struct seq_file *seq, void *v)
};
int i;
- u32 tx_cnt[PM_NSTATS], rx_cnt[PM_NSTATS];
- u64 tx_cyc[PM_NSTATS], rx_cyc[PM_NSTATS];
+ u32 tx_cnt[T6_PM_NSTATS], rx_cnt[T6_PM_NSTATS];
+ u64 tx_cyc[T6_PM_NSTATS], rx_cyc[T6_PM_NSTATS];
struct adapter *adap = seq->private;
t4_pmtx_get_stats(adap, tx_cnt, tx_cyc);
@@ -773,6 +773,32 @@ static int pm_stats_show(struct seq_file *seq, void *v)
for (i = 0; i < PM_NSTATS - 1; i++)
seq_printf(seq, "%-13s %10u %20llu\n",
rx_pm_stats[i], rx_cnt[i], rx_cyc[i]);
+
+ if (CHELSIO_CHIP_VERSION(adap->params.chip) > CHELSIO_T5) {
+ /* In T5 the granularity of the total wait is too fine.
+ * It is not useful as it reaches the max value too fast.
+ * Hence display this Input FIFO wait for T6 onwards.
+ */
+ seq_printf(seq, "%13s %10s %20s\n",
+ " ", "Total wait", "Total Occupancy");
+ seq_printf(seq, "Tx FIFO wait %10u %20llu\n",
+ tx_cnt[i], tx_cyc[i]);
+ seq_printf(seq, "Rx FIFO wait %10u %20llu\n",
+ rx_cnt[i], rx_cyc[i]);
+
+ /* Skip index 6 as there is nothing useful ihere */
+ i += 2;
+
+ /* At index 7, a new stat for read latency (count, total wait)
+ * is added.
+ */
+ seq_printf(seq, "%13s %10s %20s\n",
+ " ", "Reads", "Total wait");
+ seq_printf(seq, "Tx latency %10u %20llu\n",
+ tx_cnt[i], tx_cyc[i]);
+ seq_printf(seq, "Rx latency %10u %20llu\n",
+ rx_cnt[i], rx_cyc[i]);
+ }
return 0;
}
@@ -1559,25 +1585,35 @@ static int mps_tcam_show(struct seq_file *seq, void *v)
{
struct adapter *adap = seq->private;
unsigned int chip_ver = CHELSIO_CHIP_VERSION(adap->params.chip);
-
if (v == SEQ_START_TOKEN) {
- if (adap->params.arch.mps_rplc_size > 128)
+ if (chip_ver > CHELSIO_T5) {
seq_puts(seq, "Idx Ethernet address Mask "
+ " VNI Mask IVLAN Vld "
+ "DIP_Hit Lookup Port "
"Vld Ports PF VF "
"Replication "
" P0 P1 P2 P3 ML\n");
- else
- seq_puts(seq, "Idx Ethernet address Mask "
- "Vld Ports PF VF Replication"
- " P0 P1 P2 P3 ML\n");
+ } else {
+ if (adap->params.arch.mps_rplc_size > 128)
+ seq_puts(seq, "Idx Ethernet address Mask "
+ "Vld Ports PF VF "
+ "Replication "
+ " P0 P1 P2 P3 ML\n");
+ else
+ seq_puts(seq, "Idx Ethernet address Mask "
+ "Vld Ports PF VF Replication"
+ " P0 P1 P2 P3 ML\n");
+ }
} else {
u64 mask;
u8 addr[ETH_ALEN];
- bool replicate;
+ bool replicate, dip_hit = false, vlan_vld = false;
unsigned int idx = (uintptr_t)v - 2;
u64 tcamy, tcamx, val;
- u32 cls_lo, cls_hi, ctl;
+ u32 cls_lo, cls_hi, ctl, data2, vnix = 0, vniy = 0;
u32 rplc[8] = {0};
+ u8 lookup_type = 0, port_num = 0;
+ u16 ivlan = 0;
if (chip_ver > CHELSIO_T5) {
/* CtlCmdType - 0: Read, 1: Write
@@ -1596,6 +1632,22 @@ static int mps_tcam_show(struct seq_file *seq, void *v)
val = t4_read_reg(adap, MPS_CLS_TCAM_DATA1_A);
tcamy = DMACH_G(val) << 32;
tcamy |= t4_read_reg(adap, MPS_CLS_TCAM_DATA0_A);
+ data2 = t4_read_reg(adap, MPS_CLS_TCAM_DATA2_CTL_A);
+ lookup_type = DATALKPTYPE_G(data2);
+ /* 0 - Outer header, 1 - Inner header
+ * [71:48] bit locations are overloaded for
+ * outer vs. inner lookup types.
+ */
+ if (lookup_type && (lookup_type != DATALKPTYPE_M)) {
+ /* Inner header VNI */
+ vniy = ((data2 & DATAVIDH2_F) << 23) |
+ (DATAVIDH1_G(data2) << 16) | VIDL_G(val);
+ dip_hit = data2 & DATADIPHIT_F;
+ } else {
+ vlan_vld = data2 & DATAVIDH2_F;
+ ivlan = VIDL_G(val);
+ }
+ port_num = DATAPORTNUM_G(data2);
/* Read tcamx. Change the control param */
ctl |= CTLXYBITSEL_V(1);
@@ -1603,6 +1655,12 @@ static int mps_tcam_show(struct seq_file *seq, void *v)
val = t4_read_reg(adap, MPS_CLS_TCAM_DATA1_A);
tcamx = DMACH_G(val) << 32;
tcamx |= t4_read_reg(adap, MPS_CLS_TCAM_DATA0_A);
+ data2 = t4_read_reg(adap, MPS_CLS_TCAM_DATA2_CTL_A);
+ if (lookup_type && (lookup_type != DATALKPTYPE_M)) {
+ /* Inner header VNI mask */
+ vnix = ((data2 & DATAVIDH2_F) << 23) |
+ (DATAVIDH1_G(data2) << 16) | VIDL_G(val);
+ }
} else {
tcamy = t4_read_reg64(adap, MPS_CLS_TCAM_Y_L(idx));
tcamx = t4_read_reg64(adap, MPS_CLS_TCAM_X_L(idx));
@@ -1662,17 +1720,47 @@ static int mps_tcam_show(struct seq_file *seq, void *v)
}
tcamxy2valmask(tcamx, tcamy, addr, &mask);
- if (chip_ver > CHELSIO_T5)
- seq_printf(seq, "%3u %02x:%02x:%02x:%02x:%02x:%02x "
- "%012llx%3c %#x%4u%4d",
- idx, addr[0], addr[1], addr[2], addr[3],
- addr[4], addr[5], (unsigned long long)mask,
- (cls_lo & T6_SRAM_VLD_F) ? 'Y' : 'N',
- PORTMAP_G(cls_hi),
- T6_PF_G(cls_lo),
- (cls_lo & T6_VF_VALID_F) ?
- T6_VF_G(cls_lo) : -1);
- else
+ if (chip_ver > CHELSIO_T5) {
+ /* Inner header lookup */
+ if (lookup_type && (lookup_type != DATALKPTYPE_M)) {
+ seq_printf(seq,
+ "%3u %02x:%02x:%02x:%02x:%02x:%02x "
+ "%012llx %06x %06x - - %3c"
+ " 'I' %4x "
+ "%3c %#x%4u%4d", idx, addr[0],
+ addr[1], addr[2], addr[3],
+ addr[4], addr[5],
+ (unsigned long long)mask,
+ vniy, vnix, dip_hit ? 'Y' : 'N',
+ port_num,
+ (cls_lo & T6_SRAM_VLD_F) ? 'Y' : 'N',
+ PORTMAP_G(cls_hi),
+ T6_PF_G(cls_lo),
+ (cls_lo & T6_VF_VALID_F) ?
+ T6_VF_G(cls_lo) : -1);
+ } else {
+ seq_printf(seq,
+ "%3u %02x:%02x:%02x:%02x:%02x:%02x "
+ "%012llx - - ",
+ idx, addr[0], addr[1], addr[2],
+ addr[3], addr[4], addr[5],
+ (unsigned long long)mask);
+
+ if (vlan_vld)
+ seq_printf(seq, "%4u Y ", ivlan);
+ else
+ seq_puts(seq, " - N ");
+
+ seq_printf(seq,
+ "- %3c %4x %3c %#x%4u%4d",
+ lookup_type ? 'I' : 'O', port_num,
+ (cls_lo & T6_SRAM_VLD_F) ? 'Y' : 'N',
+ PORTMAP_G(cls_hi),
+ T6_PF_G(cls_lo),
+ (cls_lo & T6_VF_VALID_F) ?
+ T6_VF_G(cls_lo) : -1);
+ }
+ } else
seq_printf(seq, "%3u %02x:%02x:%02x:%02x:%02x:%02x "
"%012llx%3c %#x%4u%4d",
idx, addr[0], addr[1], addr[2], addr[3],
@@ -2245,7 +2333,7 @@ static int sge_qinfo_show(struct seq_file *seq, void *v)
{
struct adapter *adap = seq->private;
int eth_entries = DIV_ROUND_UP(adap->sge.ethqsets, 4);
- int iscsi_entries = DIV_ROUND_UP(adap->sge.ofldqsets, 4);
+ int iscsi_entries = DIV_ROUND_UP(adap->sge.iscsiqsets, 4);
int rdma_entries = DIV_ROUND_UP(adap->sge.rdmaqs, 4);
int ciq_entries = DIV_ROUND_UP(adap->sge.rdmaciqs, 4);
int ctrl_entries = DIV_ROUND_UP(MAX_CTRL_QUEUES, 4);
@@ -2325,14 +2413,16 @@ do { \
TL("TxMapErr:", mapping_err);
RL("FLAllocErr:", fl.alloc_failed);
RL("FLLrgAlcErr:", fl.large_alloc_failed);
+ RL("FLMapErr:", fl.mapping_err);
+ RL("FLLow:", fl.low);
RL("FLStarving:", fl.starving);
} else if (iscsi_idx < iscsi_entries) {
const struct sge_ofld_rxq *rx =
- &adap->sge.ofldrxq[iscsi_idx * 4];
+ &adap->sge.iscsirxq[iscsi_idx * 4];
const struct sge_ofld_txq *tx =
&adap->sge.ofldtxq[iscsi_idx * 4];
- int n = min(4, adap->sge.ofldqsets - 4 * iscsi_idx);
+ int n = min(4, adap->sge.iscsiqsets - 4 * iscsi_idx);
S("QType:", "iSCSI");
T("TxQ ID:", q.cntxt_id);
@@ -2359,6 +2449,8 @@ do { \
RL("RxNoMem:", stats.nomem);
RL("FLAllocErr:", fl.alloc_failed);
RL("FLLrgAlcErr:", fl.large_alloc_failed);
+ RL("FLMapErr:", fl.mapping_err);
+ RL("FLLow:", fl.low);
RL("FLStarving:", fl.starving);
} else if (rdma_idx < rdma_entries) {
@@ -2388,6 +2480,8 @@ do { \
RL("RxNoMem:", stats.nomem);
RL("FLAllocErr:", fl.alloc_failed);
RL("FLLrgAlcErr:", fl.large_alloc_failed);
+ RL("FLMapErr:", fl.mapping_err);
+ RL("FLLow:", fl.low);
RL("FLStarving:", fl.starving);
} else if (ciq_idx < ciq_entries) {
@@ -2448,7 +2542,7 @@ do { \
static int sge_queue_entries(const struct adapter *adap)
{
return DIV_ROUND_UP(adap->sge.ethqsets, 4) +
- DIV_ROUND_UP(adap->sge.ofldqsets, 4) +
+ DIV_ROUND_UP(adap->sge.iscsiqsets, 4) +
DIV_ROUND_UP(adap->sge.rdmaqs, 4) +
DIV_ROUND_UP(adap->sge.rdmaciqs, 4) +
DIV_ROUND_UP(MAX_CTRL_QUEUES, 4) + 1;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c
index a077f9476daf..7a0b92b2f73c 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c
@@ -35,79 +35,79 @@ static void set_msglevel(struct net_device *dev, u32 val)
}
static const char stats_strings[][ETH_GSTRING_LEN] = {
- "tx_octets_ok ",
- "tx_frames_ok ",
- "tx_broadcast_frames ",
- "tx_multicast_frames ",
- "tx_unicast_frames ",
- "tx_error_frames ",
-
- "tx_frames_64 ",
- "tx_frames_65_to_127 ",
- "tx_frames_128_to_255 ",
- "tx_frames_256_to_511 ",
- "tx_frames_512_to_1023 ",
- "tx_frames_1024_to_1518 ",
- "tx_frames_1519_to_max ",
-
- "tx_frames_dropped ",
- "tx_pause_frames ",
- "tx_ppp0_frames ",
- "tx_ppp1_frames ",
- "tx_ppp2_frames ",
- "tx_ppp3_frames ",
- "tx_ppp4_frames ",
- "tx_ppp5_frames ",
- "tx_ppp6_frames ",
- "tx_ppp7_frames ",
-
- "rx_octets_ok ",
- "rx_frames_ok ",
- "rx_broadcast_frames ",
- "rx_multicast_frames ",
- "rx_unicast_frames ",
-
- "rx_frames_too_long ",
- "rx_jabber_errors ",
- "rx_fcs_errors ",
- "rx_length_errors ",
- "rx_symbol_errors ",
- "rx_runt_frames ",
-
- "rx_frames_64 ",
- "rx_frames_65_to_127 ",
- "rx_frames_128_to_255 ",
- "rx_frames_256_to_511 ",
- "rx_frames_512_to_1023 ",
- "rx_frames_1024_to_1518 ",
- "rx_frames_1519_to_max ",
-
- "rx_pause_frames ",
- "rx_ppp0_frames ",
- "rx_ppp1_frames ",
- "rx_ppp2_frames ",
- "rx_ppp3_frames ",
- "rx_ppp4_frames ",
- "rx_ppp5_frames ",
- "rx_ppp6_frames ",
- "rx_ppp7_frames ",
-
- "rx_bg0_frames_dropped ",
- "rx_bg1_frames_dropped ",
- "rx_bg2_frames_dropped ",
- "rx_bg3_frames_dropped ",
- "rx_bg0_frames_trunc ",
- "rx_bg1_frames_trunc ",
- "rx_bg2_frames_trunc ",
- "rx_bg3_frames_trunc ",
-
- "tso ",
- "tx_csum_offload ",
- "rx_csum_good ",
- "vlan_extractions ",
- "vlan_insertions ",
- "gro_packets ",
- "gro_merged ",
+ "tx_octets_ok ",
+ "tx_frames_ok ",
+ "tx_broadcast_frames ",
+ "tx_multicast_frames ",
+ "tx_unicast_frames ",
+ "tx_error_frames ",
+
+ "tx_frames_64 ",
+ "tx_frames_65_to_127 ",
+ "tx_frames_128_to_255 ",
+ "tx_frames_256_to_511 ",
+ "tx_frames_512_to_1023 ",
+ "tx_frames_1024_to_1518 ",
+ "tx_frames_1519_to_max ",
+
+ "tx_frames_dropped ",
+ "tx_pause_frames ",
+ "tx_ppp0_frames ",
+ "tx_ppp1_frames ",
+ "tx_ppp2_frames ",
+ "tx_ppp3_frames ",
+ "tx_ppp4_frames ",
+ "tx_ppp5_frames ",
+ "tx_ppp6_frames ",
+ "tx_ppp7_frames ",
+
+ "rx_octets_ok ",
+ "rx_frames_ok ",
+ "rx_broadcast_frames ",
+ "rx_multicast_frames ",
+ "rx_unicast_frames ",
+
+ "rx_frames_too_long ",
+ "rx_jabber_errors ",
+ "rx_fcs_errors ",
+ "rx_length_errors ",
+ "rx_symbol_errors ",
+ "rx_runt_frames ",
+
+ "rx_frames_64 ",
+ "rx_frames_65_to_127 ",
+ "rx_frames_128_to_255 ",
+ "rx_frames_256_to_511 ",
+ "rx_frames_512_to_1023 ",
+ "rx_frames_1024_to_1518 ",
+ "rx_frames_1519_to_max ",
+
+ "rx_pause_frames ",
+ "rx_ppp0_frames ",
+ "rx_ppp1_frames ",
+ "rx_ppp2_frames ",
+ "rx_ppp3_frames ",
+ "rx_ppp4_frames ",
+ "rx_ppp5_frames ",
+ "rx_ppp6_frames ",
+ "rx_ppp7_frames ",
+
+ "rx_bg0_frames_dropped ",
+ "rx_bg1_frames_dropped ",
+ "rx_bg2_frames_dropped ",
+ "rx_bg3_frames_dropped ",
+ "rx_bg0_frames_trunc ",
+ "rx_bg1_frames_trunc ",
+ "rx_bg2_frames_trunc ",
+ "rx_bg3_frames_trunc ",
+
+ "tso ",
+ "tx_csum_offload ",
+ "rx_csum_good ",
+ "vlan_extractions ",
+ "vlan_insertions ",
+ "gro_packets ",
+ "gro_merged ",
};
static char adapter_stats_strings[][ETH_GSTRING_LEN] = {
@@ -686,7 +686,7 @@ static int set_pauseparam(struct net_device *dev,
if (epause->tx_pause)
lc->requested_fc |= PAUSE_TX;
if (netif_running(dev))
- return t4_link_l1cfg(p->adapter, p->adapter->pf, p->tx_chan,
+ return t4_link_l1cfg(p->adapter, p->adapter->mbox, p->tx_chan,
lc);
return 0;
}
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 0d147610a06f..b8a5fb0c32d4 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -162,19 +162,8 @@ MODULE_FIRMWARE(FW6_FNAME);
static uint force_init;
module_param(force_init, uint, 0644);
-MODULE_PARM_DESC(force_init, "Forcibly become Master PF and initialize adapter");
-
-/*
- * Normally if the firmware we connect to has Configuration File support, we
- * use that and only fall back to the old Driver-based initialization if the
- * Configuration File fails for some reason. If force_old_init is set, then
- * we'll always use the old Driver-based initialization sequence.
- */
-static uint force_old_init;
-
-module_param(force_old_init, uint, 0644);
-MODULE_PARM_DESC(force_old_init, "Force old initialization sequence, deprecated"
- " parameter");
+MODULE_PARM_DESC(force_init, "Forcibly become Master PF and initialize adapter,"
+ "deprecated parameter");
static int dflt_msg_enable = DFLT_MSG_ENABLE;
@@ -196,23 +185,6 @@ module_param(msi, int, 0644);
MODULE_PARM_DESC(msi, "whether to use INTx (0), MSI (1) or MSI-X (2)");
/*
- * Queue interrupt hold-off timer values. Queues default to the first of these
- * upon creation.
- */
-static unsigned int intr_holdoff[SGE_NTIMERS - 1] = { 5, 10, 20, 50, 100 };
-
-module_param_array(intr_holdoff, uint, NULL, 0644);
-MODULE_PARM_DESC(intr_holdoff, "values for queue interrupt hold-off timers "
- "0..4 in microseconds, deprecated parameter");
-
-static unsigned int intr_cnt[SGE_NCOUNTERS - 1] = { 4, 8, 16 };
-
-module_param_array(intr_cnt, uint, NULL, 0644);
-MODULE_PARM_DESC(intr_cnt,
- "thresholds 1..3 for queue interrupt packet counters, "
- "deprecated parameter");
-
-/*
* Normally we tell the chip to deliver Ingress Packets into our DMA buffers
* offset by 2 bytes in order to have the IP headers line up on 4-byte
* boundaries. This is a requirement for many architectures which will throw
@@ -226,13 +198,7 @@ MODULE_PARM_DESC(intr_cnt,
*/
static int rx_dma_offset = 2;
-static bool vf_acls;
-
#ifdef CONFIG_PCI_IOV
-module_param(vf_acls, bool, 0644);
-MODULE_PARM_DESC(vf_acls, "if set enable virtualization L2 ACL enforcement, "
- "deprecated parameter");
-
/* Configure the number of PCI-E Virtual Function which are to be instantiated
* on SR-IOV Capable Physical Functions.
*/
@@ -253,12 +219,6 @@ module_param(select_queue, int, 0644);
MODULE_PARM_DESC(select_queue,
"Select between kernel provided method of selecting or driver method of selecting TX queue. Default is kernel method.");
-static unsigned int tp_vlan_pri_map = HW_TPL_FR_MT_PR_IV_P_FC;
-
-module_param(tp_vlan_pri_map, uint, 0644);
-MODULE_PARM_DESC(tp_vlan_pri_map, "global compressed filter configuration, "
- "deprecated parameter");
-
static struct dentry *cxgb4_debugfs_root;
static LIST_HEAD(adapter_list);
@@ -766,8 +726,8 @@ static void name_msix_vecs(struct adapter *adap)
}
/* offload queues */
- for_each_ofldrxq(&adap->sge, i)
- snprintf(adap->msix_info[msi_idx++].desc, n, "%s-ofld%d",
+ for_each_iscsirxq(&adap->sge, i)
+ snprintf(adap->msix_info[msi_idx++].desc, n, "%s-iscsi%d",
adap->port[0]->name, i);
for_each_rdmarxq(&adap->sge, i)
@@ -782,7 +742,7 @@ static void name_msix_vecs(struct adapter *adap)
static int request_msix_queue_irqs(struct adapter *adap)
{
struct sge *s = &adap->sge;
- int err, ethqidx, ofldqidx = 0, rdmaqidx = 0, rdmaciqqidx = 0;
+ int err, ethqidx, iscsiqidx = 0, rdmaqidx = 0, rdmaciqqidx = 0;
int msi_index = 2;
err = request_irq(adap->msix_info[1].vec, t4_sge_intr_msix, 0,
@@ -799,11 +759,11 @@ static int request_msix_queue_irqs(struct adapter *adap)
goto unwind;
msi_index++;
}
- for_each_ofldrxq(s, ofldqidx) {
+ for_each_iscsirxq(s, iscsiqidx) {
err = request_irq(adap->msix_info[msi_index].vec,
t4_sge_intr_msix, 0,
adap->msix_info[msi_index].desc,
- &s->ofldrxq[ofldqidx].rspq);
+ &s->iscsirxq[iscsiqidx].rspq);
if (err)
goto unwind;
msi_index++;
@@ -835,9 +795,9 @@ unwind:
while (--rdmaqidx >= 0)
free_irq(adap->msix_info[--msi_index].vec,
&s->rdmarxq[rdmaqidx].rspq);
- while (--ofldqidx >= 0)
+ while (--iscsiqidx >= 0)
free_irq(adap->msix_info[--msi_index].vec,
- &s->ofldrxq[ofldqidx].rspq);
+ &s->iscsirxq[iscsiqidx].rspq);
while (--ethqidx >= 0)
free_irq(adap->msix_info[--msi_index].vec,
&s->ethrxq[ethqidx].rspq);
@@ -853,8 +813,9 @@ static void free_msix_queue_irqs(struct adapter *adap)
free_irq(adap->msix_info[1].vec, &s->fw_evtq);
for_each_ethrxq(s, i)
free_irq(adap->msix_info[msi_index++].vec, &s->ethrxq[i].rspq);
- for_each_ofldrxq(s, i)
- free_irq(adap->msix_info[msi_index++].vec, &s->ofldrxq[i].rspq);
+ for_each_iscsirxq(s, i)
+ free_irq(adap->msix_info[msi_index++].vec,
+ &s->iscsirxq[i].rspq);
for_each_rdmarxq(s, i)
free_irq(adap->msix_info[msi_index++].vec, &s->rdmarxq[i].rspq);
for_each_rdmaciq(s, i)
@@ -1093,8 +1054,8 @@ freeout: t4_free_sge_resources(adap);
}
}
- j = s->ofldqsets / adap->params.nports; /* ofld queues per channel */
- for_each_ofldrxq(s, i) {
+ j = s->iscsiqsets / adap->params.nports; /* iscsi queues per channel */
+ for_each_iscsirxq(s, i) {
err = t4_sge_alloc_ofld_txq(adap, &s->ofldtxq[i],
adap->port[i / j],
s->fw_evtq.cntxt_id);
@@ -1110,7 +1071,7 @@ freeout: t4_free_sge_resources(adap);
msi_idx += nq; \
} while (0)
- ALLOC_OFLD_RXQS(s->ofldrxq, s->ofldqsets, j, s->ofld_rxq);
+ ALLOC_OFLD_RXQS(s->iscsirxq, s->iscsiqsets, j, s->iscsi_rxq);
ALLOC_OFLD_RXQS(s->rdmarxq, s->rdmaqs, 1, s->rdma_rxq);
j = s->rdmaciqs / adap->params.nports; /* rdmaq queues per channel */
ALLOC_OFLD_RXQS(s->rdmaciq, s->rdmaciqs, j, s->rdma_ciq);
@@ -1181,16 +1142,10 @@ static int set_filter_wr(struct adapter *adapter, int fidx)
*/
if (f->fs.newdmac || f->fs.newvlan) {
/* allocate L2T entry for new filter */
- f->l2t = t4_l2t_alloc_switching(adapter->l2t);
+ f->l2t = t4_l2t_alloc_switching(adapter, f->fs.vlan,
+ f->fs.eport, f->fs.dmac);
if (f->l2t == NULL) {
kfree_skb(skb);
- return -EAGAIN;
- }
- if (t4_l2t_set_switching(adapter, f->l2t, f->fs.vlan,
- f->fs.eport, f->fs.dmac)) {
- cxgb4_l2t_release(f->l2t);
- f->l2t = NULL;
- kfree_skb(skb);
return -ENOMEM;
}
}
@@ -1511,7 +1466,7 @@ int cxgb4_alloc_stid(struct tid_info *t, int family, void *data)
else
stid = -1;
} else {
- stid = bitmap_find_free_region(t->stid_bmap, t->nstids, 2);
+ stid = bitmap_find_free_region(t->stid_bmap, t->nstids, 1);
if (stid < 0)
stid = -1;
}
@@ -1525,7 +1480,7 @@ int cxgb4_alloc_stid(struct tid_info *t, int family, void *data)
if (family == PF_INET)
t->stids_in_use++;
else
- t->stids_in_use += 4;
+ t->stids_in_use += 2;
}
spin_unlock_bh(&t->stid_lock);
return stid;
@@ -1576,13 +1531,13 @@ void cxgb4_free_stid(struct tid_info *t, unsigned int stid, int family)
if (family == PF_INET)
__clear_bit(stid, t->stid_bmap);
else
- bitmap_release_region(t->stid_bmap, stid, 2);
+ bitmap_release_region(t->stid_bmap, stid, 1);
t->stid_tab[stid].data = NULL;
if (stid < t->nstids) {
if (family == PF_INET)
t->stids_in_use--;
else
- t->stids_in_use -= 4;
+ t->stids_in_use -= 2;
} else {
t->sftids_in_use--;
}
@@ -2283,7 +2238,7 @@ static void disable_dbs(struct adapter *adap)
for_each_ethrxq(&adap->sge, i)
disable_txq_db(&adap->sge.ethtxq[i].q);
- for_each_ofldrxq(&adap->sge, i)
+ for_each_iscsirxq(&adap->sge, i)
disable_txq_db(&adap->sge.ofldtxq[i].q);
for_each_port(adap, i)
disable_txq_db(&adap->sge.ctrlq[i].q);
@@ -2295,7 +2250,7 @@ static void enable_dbs(struct adapter *adap)
for_each_ethrxq(&adap->sge, i)
enable_txq_db(adap, &adap->sge.ethtxq[i].q);
- for_each_ofldrxq(&adap->sge, i)
+ for_each_iscsirxq(&adap->sge, i)
enable_txq_db(adap, &adap->sge.ofldtxq[i].q);
for_each_port(adap, i)
enable_txq_db(adap, &adap->sge.ctrlq[i].q);
@@ -2365,7 +2320,7 @@ static void recover_all_queues(struct adapter *adap)
for_each_ethrxq(&adap->sge, i)
sync_txq_pidx(adap, &adap->sge.ethtxq[i].q);
- for_each_ofldrxq(&adap->sge, i)
+ for_each_iscsirxq(&adap->sge, i)
sync_txq_pidx(adap, &adap->sge.ofldtxq[i].q);
for_each_port(adap, i)
sync_txq_pidx(adap, &adap->sge.ctrlq[i].q);
@@ -2449,10 +2404,10 @@ static void uld_attach(struct adapter *adap, unsigned int uld)
lli.nrxq = adap->sge.rdmaqs;
lli.nciq = adap->sge.rdmaciqs;
} else if (uld == CXGB4_ULD_ISCSI) {
- lli.rxq_ids = adap->sge.ofld_rxq;
- lli.nrxq = adap->sge.ofldqsets;
+ lli.rxq_ids = adap->sge.iscsi_rxq;
+ lli.nrxq = adap->sge.iscsiqsets;
}
- lli.ntxq = adap->sge.ofldqsets;
+ lli.ntxq = adap->sge.iscsiqsets;
lli.nchan = adap->params.nports;
lli.nports = adap->params.nports;
lli.wr_cred = adap->params.ofldq_wr_cred;
@@ -3146,16 +3101,6 @@ static int adap_init1(struct adapter *adap, struct fw_caps_config_cmd *c)
if (ret < 0)
return ret;
- /* select capabilities we'll be using */
- if (c->niccaps & htons(FW_CAPS_CONFIG_NIC_VM)) {
- if (!vf_acls)
- c->niccaps ^= htons(FW_CAPS_CONFIG_NIC_VM);
- else
- c->niccaps = htons(FW_CAPS_CONFIG_NIC_VM);
- } else if (vf_acls) {
- dev_err(adap->pdev_dev, "virtualization ACLs not supported");
- return ret;
- }
c->op_to_write = htonl(FW_CMD_OP_V(FW_CAPS_CONFIG_CMD) |
FW_CMD_REQUEST_F | FW_CMD_WRITE_F);
ret = t4_wr_mbox(adap, adap->mbox, c, sizeof(*c), NULL);
@@ -4348,11 +4293,11 @@ static void cfg_queues(struct adapter *adap)
* capped by the number of available cores.
*/
if (n10g) {
- i = min_t(int, ARRAY_SIZE(s->ofldrxq),
+ i = min_t(int, ARRAY_SIZE(s->iscsirxq),
num_online_cpus());
- s->ofldqsets = roundup(i, adap->params.nports);
+ s->iscsiqsets = roundup(i, adap->params.nports);
} else
- s->ofldqsets = adap->params.nports;
+ s->iscsiqsets = adap->params.nports;
/* For RDMA one Rx queue per channel suffices */
s->rdmaqs = adap->params.nports;
/* Try and allow at least 1 CIQ per cpu rounding down
@@ -4383,8 +4328,8 @@ static void cfg_queues(struct adapter *adap)
for (i = 0; i < ARRAY_SIZE(s->ofldtxq); i++)
s->ofldtxq[i].q.size = 1024;
- for (i = 0; i < ARRAY_SIZE(s->ofldrxq); i++) {
- struct sge_ofld_rxq *r = &s->ofldrxq[i];
+ for (i = 0; i < ARRAY_SIZE(s->iscsirxq); i++) {
+ struct sge_ofld_rxq *r = &s->iscsirxq[i];
init_rspq(adap, &r->rspq, 5, 1, 1024, 64);
r->rspq.uld = CXGB4_ULD_ISCSI;
@@ -4465,7 +4410,7 @@ static int enable_msix(struct adapter *adap)
want = s->max_ethqsets + EXTRA_VECS;
if (is_offload(adap)) {
- want += s->rdmaqs + s->rdmaciqs + s->ofldqsets;
+ want += s->rdmaqs + s->rdmaciqs + s->iscsiqsets;
/* need nchan for each possible ULD */
ofld_need = 3 * nchan;
}
@@ -4504,13 +4449,13 @@ static int enable_msix(struct adapter *adap)
/* leftovers go to OFLD */
i = allocated - EXTRA_VECS - s->max_ethqsets -
s->rdmaqs - s->rdmaciqs;
- s->ofldqsets = (i / nchan) * nchan; /* round down */
+ s->iscsiqsets = (i / nchan) * nchan; /* round down */
}
for (i = 0; i < allocated; ++i)
adap->msix_info[i].vec = entries[i].vector;
dev_info(adap->pdev_dev, "%d MSI-X vectors allocated, "
"nic %d iscsi %d rdma cpl %d rdma ciq %d\n",
- allocated, s->max_ethqsets, s->ofldqsets, s->rdmaqs,
+ allocated, s->max_ethqsets, s->iscsiqsets, s->rdmaqs,
s->rdmaciqs);
kfree(entries);
@@ -4538,6 +4483,79 @@ static int init_rss(struct adapter *adap)
return 0;
}
+static int cxgb4_get_pcie_dev_link_caps(struct adapter *adap,
+ enum pci_bus_speed *speed,
+ enum pcie_link_width *width)
+{
+ u32 lnkcap1, lnkcap2;
+ int err1, err2;
+
+#define PCIE_MLW_CAP_SHIFT 4 /* start of MLW mask in link capabilities */
+
+ *speed = PCI_SPEED_UNKNOWN;
+ *width = PCIE_LNK_WIDTH_UNKNOWN;
+
+ err1 = pcie_capability_read_dword(adap->pdev, PCI_EXP_LNKCAP,
+ &lnkcap1);
+ err2 = pcie_capability_read_dword(adap->pdev, PCI_EXP_LNKCAP2,
+ &lnkcap2);
+ if (!err2 && lnkcap2) { /* PCIe r3.0-compliant */
+ if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB)
+ *speed = PCIE_SPEED_8_0GT;
+ else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB)
+ *speed = PCIE_SPEED_5_0GT;
+ else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB)
+ *speed = PCIE_SPEED_2_5GT;
+ }
+ if (!err1) {
+ *width = (lnkcap1 & PCI_EXP_LNKCAP_MLW) >> PCIE_MLW_CAP_SHIFT;
+ if (!lnkcap2) { /* pre-r3.0 */
+ if (lnkcap1 & PCI_EXP_LNKCAP_SLS_5_0GB)
+ *speed = PCIE_SPEED_5_0GT;
+ else if (lnkcap1 & PCI_EXP_LNKCAP_SLS_2_5GB)
+ *speed = PCIE_SPEED_2_5GT;
+ }
+ }
+
+ if (*speed == PCI_SPEED_UNKNOWN || *width == PCIE_LNK_WIDTH_UNKNOWN)
+ return err1 ? err1 : err2 ? err2 : -EINVAL;
+ return 0;
+}
+
+static void cxgb4_check_pcie_caps(struct adapter *adap)
+{
+ enum pcie_link_width width, width_cap;
+ enum pci_bus_speed speed, speed_cap;
+
+#define PCIE_SPEED_STR(speed) \
+ (speed == PCIE_SPEED_8_0GT ? "8.0GT/s" : \
+ speed == PCIE_SPEED_5_0GT ? "5.0GT/s" : \
+ speed == PCIE_SPEED_2_5GT ? "2.5GT/s" : \
+ "Unknown")
+
+ if (cxgb4_get_pcie_dev_link_caps(adap, &speed_cap, &width_cap)) {
+ dev_warn(adap->pdev_dev,
+ "Unable to determine PCIe device BW capabilities\n");
+ return;
+ }
+
+ if (pcie_get_minimum_link(adap->pdev, &speed, &width) ||
+ speed == PCI_SPEED_UNKNOWN || width == PCIE_LNK_WIDTH_UNKNOWN) {
+ dev_warn(adap->pdev_dev,
+ "Unable to determine PCI Express bandwidth.\n");
+ return;
+ }
+
+ dev_info(adap->pdev_dev, "PCIe link speed is %s, device supports %s\n",
+ PCIE_SPEED_STR(speed), PCIE_SPEED_STR(speed_cap));
+ dev_info(adap->pdev_dev, "PCIe link width is x%d, device supports x%d\n",
+ width, width_cap);
+ if (speed < speed_cap || width < width_cap)
+ dev_info(adap->pdev_dev,
+ "A slot with more lanes and/or higher speed is "
+ "suggested for optimal performance.\n");
+}
+
static void print_port_info(const struct net_device *dev)
{
char buf[80];
@@ -4565,10 +4583,10 @@ static void print_port_info(const struct net_device *dev)
--bufp;
sprintf(bufp, "BASE-%s", t4_get_port_type_description(pi->port_type));
- netdev_info(dev, "Chelsio %s rev %d %s %sNIC PCIe x%d%s%s\n",
+ netdev_info(dev, "Chelsio %s rev %d %s %sNIC %s\n",
adap->params.vpd.id,
CHELSIO_CHIP_RELEASE(adap->params.chip), buf,
- is_offload(adap) ? "R" : "", adap->params.pci.width, spd,
+ is_offload(adap) ? "R" : "",
(adap->flags & USING_MSIX) ? " MSI-X" :
(adap->flags & USING_MSI) ? " MSI" : "");
netdev_info(dev, "S/N: %s, P/N: %s\n",
@@ -4787,8 +4805,9 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
/* configure SGE_STAT_CFG_A to read WC stats */
if (!is_t4(adapter->params.chip))
- t4_write_reg(adapter, SGE_STAT_CFG_A,
- STATSOURCE_T5_V(7) | STATMODE_V(0));
+ t4_write_reg(adapter, SGE_STAT_CFG_A, STATSOURCE_T5_V(7) |
+ (is_t5(adapter->params.chip) ? STATMODE_V(0) :
+ T6_STATMODE_V(0)));
for_each_port(adapter, i) {
struct net_device *netdev;
@@ -4865,15 +4884,25 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
}
#if IS_ENABLED(CONFIG_IPV6)
- adapter->clipt = t4_init_clip_tbl(adapter->clipt_start,
- adapter->clipt_end);
- if (!adapter->clipt) {
- /* We tolerate a lack of clip_table, giving up
- * some functionality
+ if ((CHELSIO_CHIP_VERSION(adapter->params.chip) <= CHELSIO_T5) &&
+ (!(t4_read_reg(adapter, LE_DB_CONFIG_A) & ASLIPCOMPEN_F))) {
+ /* CLIP functionality is not present in hardware,
+ * hence disable all offload features
*/
dev_warn(&pdev->dev,
- "could not allocate Clip table, continuing\n");
+ "CLIP not enabled in hardware, continuing\n");
adapter->params.offload = 0;
+ } else {
+ adapter->clipt = t4_init_clip_tbl(adapter->clipt_start,
+ adapter->clipt_end);
+ if (!adapter->clipt) {
+ /* We tolerate a lack of clip_table, giving up
+ * some functionality
+ */
+ dev_warn(&pdev->dev,
+ "could not allocate Clip table, continuing\n");
+ adapter->params.offload = 0;
+ }
}
#endif
if (is_offload(adapter) && tid_init(&adapter->tids) < 0) {
@@ -4904,6 +4933,9 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
else if (msi > 0 && pci_enable_msi(pdev) == 0)
adapter->flags |= USING_MSI;
+ /* check for PCI Express bandwidth capabiltites */
+ cxgb4_check_pcie_caps(adapter);
+
err = init_rss(adapter);
if (err)
goto out_free_dev;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.c b/drivers/net/ethernet/chelsio/cxgb4/l2t.c
index ac27898c6ab0..5b0f3ef348e9 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/l2t.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.c
@@ -66,7 +66,7 @@ struct l2t_data {
static inline unsigned int vlan_prio(const struct l2t_entry *e)
{
- return e->vlan >> 13;
+ return e->vlan >> VLAN_PRIO_SHIFT;
}
static inline void l2t_hold(struct l2t_data *d, struct l2t_entry *e)
@@ -161,8 +161,7 @@ static int write_l2e(struct adapter *adap, struct l2t_entry *e, int sync)
memcpy(e->dmac, e->neigh->ha, sizeof(e->dmac));
memcpy(req->dst_mac, e->dmac, sizeof(req->dst_mac));
- set_wr_txq(skb, CPL_PRIORITY_CONTROL, 0);
- t4_ofld_send(adap, skb);
+ t4_mgmt_tx(adap, skb);
if (sync && e->state != L2T_STATE_SWITCHING)
e->state = L2T_STATE_SYNC_WRITE;
@@ -175,14 +174,10 @@ static int write_l2e(struct adapter *adap, struct l2t_entry *e, int sync)
*/
static void send_pending(struct adapter *adap, struct l2t_entry *e)
{
- while (e->arpq_head) {
- struct sk_buff *skb = e->arpq_head;
+ struct sk_buff *skb;
- e->arpq_head = skb->next;
- skb->next = NULL;
+ while ((skb = __skb_dequeue(&e->arpq)) != NULL)
t4_ofld_send(adap, skb);
- }
- e->arpq_tail = NULL;
}
/*
@@ -222,12 +217,7 @@ void do_l2t_write_rpl(struct adapter *adap, const struct cpl_l2t_write_rpl *rpl)
*/
static inline void arpq_enqueue(struct l2t_entry *e, struct sk_buff *skb)
{
- skb->next = NULL;
- if (e->arpq_head)
- e->arpq_tail->next = skb;
- else
- e->arpq_head = skb;
- e->arpq_tail = skb;
+ __skb_queue_tail(&e->arpq, skb);
}
int cxgb4_l2t_send(struct net_device *dev, struct sk_buff *skb,
@@ -259,7 +249,8 @@ again:
if (e->state == L2T_STATE_RESOLVING &&
!neigh_event_send(e->neigh, NULL)) {
spin_lock_bh(&e->lock);
- if (e->state == L2T_STATE_RESOLVING && e->arpq_head)
+ if (e->state == L2T_STATE_RESOLVING &&
+ !skb_queue_empty(&e->arpq))
write_l2e(adap, e, 1);
spin_unlock_bh(&e->lock);
}
@@ -305,12 +296,82 @@ found:
return e;
}
-/*
- * Called when an L2T entry has no more users.
+static struct l2t_entry *find_or_alloc_l2e(struct l2t_data *d, u16 vlan,
+ u8 port, u8 *dmac)
+{
+ struct l2t_entry *end, *e, **p;
+ struct l2t_entry *first_free = NULL;
+
+ for (e = &d->l2tab[0], end = &d->l2tab[d->l2t_size]; e != end; ++e) {
+ if (atomic_read(&e->refcnt) == 0) {
+ if (!first_free)
+ first_free = e;
+ } else {
+ if (e->state == L2T_STATE_SWITCHING) {
+ if (ether_addr_equal(e->dmac, dmac) &&
+ (e->vlan == vlan) && (e->lport == port))
+ goto exists;
+ }
+ }
+ }
+
+ if (first_free) {
+ e = first_free;
+ goto found;
+ }
+
+ return NULL;
+
+found:
+ /* The entry we found may be an inactive entry that is
+ * presently in the hash table. We need to remove it.
+ */
+ if (e->state < L2T_STATE_SWITCHING)
+ for (p = &d->l2tab[e->hash].first; *p; p = &(*p)->next)
+ if (*p == e) {
+ *p = e->next;
+ e->next = NULL;
+ break;
+ }
+ e->state = L2T_STATE_UNUSED;
+
+exists:
+ return e;
+}
+
+/* Called when an L2T entry has no more users. The entry is left in the hash
+ * table since it is likely to be reused but we also bump nfree to indicate
+ * that the entry can be reallocated for a different neighbor. We also drop
+ * the existing neighbor reference in case the neighbor is going away and is
+ * waiting on our reference.
+ *
+ * Because entries can be reallocated to other neighbors once their ref count
+ * drops to 0 we need to take the entry's lock to avoid races with a new
+ * incarnation.
*/
+static void _t4_l2e_free(struct l2t_entry *e)
+{
+ struct l2t_data *d;
+ struct sk_buff *skb;
+
+ if (atomic_read(&e->refcnt) == 0) { /* hasn't been recycled */
+ if (e->neigh) {
+ neigh_release(e->neigh);
+ e->neigh = NULL;
+ }
+ while ((skb = __skb_dequeue(&e->arpq)) != NULL)
+ kfree_skb(skb);
+ }
+
+ d = container_of(e, struct l2t_data, l2tab[e->idx]);
+ atomic_inc(&d->nfree);
+}
+
+/* Locked version of _t4_l2e_free */
static void t4_l2e_free(struct l2t_entry *e)
{
struct l2t_data *d;
+ struct sk_buff *skb;
spin_lock_bh(&e->lock);
if (atomic_read(&e->refcnt) == 0) { /* hasn't been recycled */
@@ -318,13 +379,8 @@ static void t4_l2e_free(struct l2t_entry *e)
neigh_release(e->neigh);
e->neigh = NULL;
}
- while (e->arpq_head) {
- struct sk_buff *skb = e->arpq_head;
-
- e->arpq_head = skb->next;
+ while ((skb = __skb_dequeue(&e->arpq)) != NULL)
kfree_skb(skb);
- }
- e->arpq_tail = NULL;
}
spin_unlock_bh(&e->lock);
@@ -457,18 +513,19 @@ EXPORT_SYMBOL(cxgb4_select_ntuple);
* on the arpq head. If a packet specifies a failure handler it is invoked,
* otherwise the packet is sent to the device.
*/
-static void handle_failed_resolution(struct adapter *adap, struct sk_buff *arpq)
+static void handle_failed_resolution(struct adapter *adap, struct l2t_entry *e)
{
- while (arpq) {
- struct sk_buff *skb = arpq;
+ struct sk_buff *skb;
+
+ while ((skb = __skb_dequeue(&e->arpq)) != NULL) {
const struct l2t_skb_cb *cb = L2T_SKB_CB(skb);
- arpq = skb->next;
- skb->next = NULL;
+ spin_unlock(&e->lock);
if (cb->arp_err_handler)
cb->arp_err_handler(cb->handle, skb);
else
t4_ofld_send(adap, skb);
+ spin_lock(&e->lock);
}
}
@@ -479,7 +536,7 @@ static void handle_failed_resolution(struct adapter *adap, struct sk_buff *arpq)
void t4_l2t_update(struct adapter *adap, struct neighbour *neigh)
{
struct l2t_entry *e;
- struct sk_buff *arpq = NULL;
+ struct sk_buff_head *arpq = NULL;
struct l2t_data *d = adap->l2t;
int addr_len = neigh->tbl->key_len;
u32 *addr = (u32 *) neigh->primary_key;
@@ -506,10 +563,9 @@ void t4_l2t_update(struct adapter *adap, struct neighbour *neigh)
if (e->state == L2T_STATE_RESOLVING) {
if (neigh->nud_state & NUD_FAILED) {
- arpq = e->arpq_head;
- e->arpq_head = e->arpq_tail = NULL;
+ arpq = &e->arpq;
} else if ((neigh->nud_state & (NUD_CONNECTED | NUD_STALE)) &&
- e->arpq_head) {
+ !skb_queue_empty(&e->arpq)) {
write_l2e(adap, e, 1);
}
} else {
@@ -519,43 +575,66 @@ void t4_l2t_update(struct adapter *adap, struct neighbour *neigh)
write_l2e(adap, e, 0);
}
- spin_unlock_bh(&e->lock);
-
if (arpq)
- handle_failed_resolution(adap, arpq);
+ handle_failed_resolution(adap, e);
+ spin_unlock_bh(&e->lock);
}
/* Allocate an L2T entry for use by a switching rule. Such need to be
* explicitly freed and while busy they are not on any hash chain, so normal
* address resolution updates do not see them.
*/
-struct l2t_entry *t4_l2t_alloc_switching(struct l2t_data *d)
+struct l2t_entry *t4_l2t_alloc_switching(struct adapter *adap, u16 vlan,
+ u8 port, u8 *eth_addr)
{
+ struct l2t_data *d = adap->l2t;
struct l2t_entry *e;
+ int ret;
write_lock_bh(&d->lock);
- e = alloc_l2e(d);
+ e = find_or_alloc_l2e(d, vlan, port, eth_addr);
if (e) {
spin_lock(&e->lock); /* avoid race with t4_l2t_free */
- e->state = L2T_STATE_SWITCHING;
- atomic_set(&e->refcnt, 1);
+ if (!atomic_read(&e->refcnt)) {
+ e->state = L2T_STATE_SWITCHING;
+ e->vlan = vlan;
+ e->lport = port;
+ ether_addr_copy(e->dmac, eth_addr);
+ atomic_set(&e->refcnt, 1);
+ ret = write_l2e(adap, e, 0);
+ if (ret < 0) {
+ _t4_l2e_free(e);
+ spin_unlock(&e->lock);
+ write_unlock_bh(&d->lock);
+ return NULL;
+ }
+ } else {
+ atomic_inc(&e->refcnt);
+ }
+
spin_unlock(&e->lock);
}
write_unlock_bh(&d->lock);
return e;
}
-/* Sets/updates the contents of a switching L2T entry that has been allocated
- * with an earlier call to @t4_l2t_alloc_switching.
+/**
+ * @dev: net_device pointer
+ * @vlan: VLAN Id
+ * @port: Associated port
+ * @dmac: Destination MAC address to add to L2T
+ * Returns pointer to the allocated l2t entry
+ *
+ * Allocates an L2T entry for use by switching rule of a filter
*/
-int t4_l2t_set_switching(struct adapter *adap, struct l2t_entry *e, u16 vlan,
- u8 port, u8 *eth_addr)
+struct l2t_entry *cxgb4_l2t_alloc_switching(struct net_device *dev, u16 vlan,
+ u8 port, u8 *dmac)
{
- e->vlan = vlan;
- e->lport = port;
- memcpy(e->dmac, eth_addr, ETH_ALEN);
- return write_l2e(adap, e, 0);
+ struct adapter *adap = netdev2adap(dev);
+
+ return t4_l2t_alloc_switching(adap, vlan, port, dmac);
}
+EXPORT_SYMBOL(cxgb4_l2t_alloc_switching);
struct l2t_data *t4_init_l2t(unsigned int l2t_start, unsigned int l2t_end)
{
@@ -585,6 +664,7 @@ struct l2t_data *t4_init_l2t(unsigned int l2t_start, unsigned int l2t_end)
d->l2tab[i].state = L2T_STATE_UNUSED;
spin_lock_init(&d->l2tab[i].lock);
atomic_set(&d->l2tab[i].refcnt, 0);
+ skb_queue_head_init(&d->l2tab[i].arpq);
}
return d;
}
@@ -619,7 +699,8 @@ static char l2e_state(const struct l2t_entry *e)
case L2T_STATE_VALID: return 'V';
case L2T_STATE_STALE: return 'S';
case L2T_STATE_SYNC_WRITE: return 'W';
- case L2T_STATE_RESOLVING: return e->arpq_head ? 'A' : 'R';
+ case L2T_STATE_RESOLVING:
+ return skb_queue_empty(&e->arpq) ? 'R' : 'A';
case L2T_STATE_SWITCHING: return 'X';
default:
return 'U';
diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.h b/drivers/net/ethernet/chelsio/cxgb4/l2t.h
index b38dc526aad5..4e2d47ac102b 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/l2t.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.h
@@ -76,8 +76,7 @@ struct l2t_entry {
struct neighbour *neigh; /* associated neighbour */
struct l2t_entry *first; /* start of hash chain */
struct l2t_entry *next; /* next l2t_entry on chain */
- struct sk_buff *arpq_head; /* queue of packets awaiting resolution */
- struct sk_buff *arpq_tail;
+ struct sk_buff_head arpq; /* packet queue awaiting resolution */
spinlock_t lock;
atomic_t refcnt; /* entry reference count */
u16 hash; /* hash bucket the entry is on */
@@ -114,10 +113,11 @@ struct l2t_entry *cxgb4_l2t_get(struct l2t_data *d, struct neighbour *neigh,
unsigned int priority);
u64 cxgb4_select_ntuple(struct net_device *dev,
const struct l2t_entry *l2t);
+struct l2t_entry *cxgb4_l2t_alloc_switching(struct net_device *dev, u16 vlan,
+ u8 port, u8 *dmac);
void t4_l2t_update(struct adapter *adap, struct neighbour *neigh);
-struct l2t_entry *t4_l2t_alloc_switching(struct l2t_data *d);
-int t4_l2t_set_switching(struct adapter *adap, struct l2t_entry *e, u16 vlan,
- u8 port, u8 *eth_addr);
+struct l2t_entry *t4_l2t_alloc_switching(struct adapter *adap, u16 vlan,
+ u8 port, u8 *dmac);
struct l2t_data *t4_init_l2t(unsigned int l2t_start, unsigned int l2t_end);
void do_l2t_write_rpl(struct adapter *p, const struct cpl_l2t_write_rpl *rpl);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c
index b7b93e7a643d..b4eb4680a27c 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c
@@ -406,7 +406,7 @@ static void free_tx_desc(struct adapter *adap, struct sge_txq *q,
*/
static inline int reclaimable(const struct sge_txq *q)
{
- int hw_cidx = ntohs(q->stat->cidx);
+ int hw_cidx = ntohs(ACCESS_ONCE(q->stat->cidx));
hw_cidx -= q->cidx;
return hw_cidx < 0 ? hw_cidx + q->size : hw_cidx;
}
@@ -613,6 +613,7 @@ static unsigned int refill_fl(struct adapter *adap, struct sge_fl *q, int n,
PCI_DMA_FROMDEVICE);
if (unlikely(dma_mapping_error(adap->pdev_dev, mapping))) {
__free_pages(pg, s->fl_pg_order);
+ q->mapping_err++;
goto out; /* do not try small pages for this error */
}
mapping |= RX_LARGE_PG_BUF;
@@ -642,6 +643,7 @@ alloc_small_pages:
PCI_DMA_FROMDEVICE);
if (unlikely(dma_mapping_error(adap->pdev_dev, mapping))) {
put_page(pg);
+ q->mapping_err++;
goto out;
}
*d++ = cpu_to_be64(mapping);
@@ -663,6 +665,7 @@ out: cred = q->avail - cred;
if (unlikely(fl_starving(adap, q))) {
smp_wmb();
+ q->low++;
set_bit(q->cntxt_id - adap->sge.egr_start,
adap->sge.starving_fl);
}
@@ -1029,6 +1032,30 @@ static void inline_tx_skb(const struct sk_buff *skb, const struct sge_txq *q,
*p = 0;
}
+static void *inline_tx_skb_header(const struct sk_buff *skb,
+ const struct sge_txq *q, void *pos,
+ int length)
+{
+ u64 *p;
+ int left = (void *)q->stat - pos;
+
+ if (likely(length <= left)) {
+ memcpy(pos, skb->data, length);
+ pos += length;
+ } else {
+ memcpy(pos, skb->data, left);
+ memcpy(q->desc, skb->data + left, length - left);
+ pos = (void *)q->desc + (length - left);
+ }
+ /* 0-pad to multiple of 16 */
+ p = PTR_ALIGN(pos, 8);
+ if ((uintptr_t)p & 8) {
+ *p = 0;
+ return p + 1;
+ }
+ return p;
+}
+
/*
* Figure out what HW csum a packet wants and return the appropriate control
* bits.
@@ -1320,7 +1347,7 @@ out_free: dev_kfree_skb_any(skb);
*/
static inline void reclaim_completed_tx_imm(struct sge_txq *q)
{
- int hw_cidx = ntohs(q->stat->cidx);
+ int hw_cidx = ntohs(ACCESS_ONCE(q->stat->cidx));
int reclaim = hw_cidx - q->cidx;
if (reclaim < 0)
@@ -1542,24 +1569,50 @@ static void ofldtxq_stop(struct sge_ofld_txq *q, struct sk_buff *skb)
}
/**
- * service_ofldq - restart a suspended offload queue
+ * service_ofldq - service/restart a suspended offload queue
* @q: the offload queue
*
- * Services an offload Tx queue by moving packets from its packet queue
- * to the HW Tx ring. The function starts and ends with the queue locked.
+ * Services an offload Tx queue by moving packets from its Pending Send
+ * Queue to the Hardware TX ring. The function starts and ends with the
+ * Send Queue locked, but drops the lock while putting the skb at the
+ * head of the Send Queue onto the Hardware TX Ring. Dropping the lock
+ * allows more skbs to be added to the Send Queue by other threads.
+ * The packet being processed at the head of the Pending Send Queue is
+ * left on the queue in case we experience DMA Mapping errors, etc.
+ * and need to give up and restart later.
+ *
+ * service_ofldq() can be thought of as a task which opportunistically
+ * uses other threads execution contexts. We use the Offload Queue
+ * boolean "service_ofldq_running" to make sure that only one instance
+ * is ever running at a time ...
*/
static void service_ofldq(struct sge_ofld_txq *q)
{
- u64 *pos;
+ u64 *pos, *before, *end;
int credits;
struct sk_buff *skb;
+ struct sge_txq *txq;
+ unsigned int left;
unsigned int written = 0;
unsigned int flits, ndesc;
+ /* If another thread is currently in service_ofldq() processing the
+ * Pending Send Queue then there's nothing to do. Otherwise, flag
+ * that we're doing the work and continue. Examining/modifying
+ * the Offload Queue boolean "service_ofldq_running" must be done
+ * while holding the Pending Send Queue Lock.
+ */
+ if (q->service_ofldq_running)
+ return;
+ q->service_ofldq_running = true;
+
while ((skb = skb_peek(&q->sendq)) != NULL && !q->full) {
- /*
- * We drop the lock but leave skb on sendq, thus retaining
- * exclusive access to the state of the queue.
+ /* We drop the lock while we're working with the skb at the
+ * head of the Pending Send Queue. This allows more skbs to
+ * be added to the Pending Send Queue while we're working on
+ * this one. We don't need to lock to guard the TX Ring
+ * updates because only one thread of execution is ever
+ * allowed into service_ofldq() at a time.
*/
spin_unlock(&q->sendq.lock);
@@ -1583,9 +1636,32 @@ static void service_ofldq(struct sge_ofld_txq *q)
} else {
int last_desc, hdr_len = skb_transport_offset(skb);
- memcpy(pos, skb->data, hdr_len);
- write_sgl(skb, &q->q, (void *)pos + hdr_len,
- pos + flits, hdr_len,
+ /* The WR headers may not fit within one descriptor.
+ * So we need to deal with wrap-around here.
+ */
+ before = (u64 *)pos;
+ end = (u64 *)pos + flits;
+ txq = &q->q;
+ pos = (void *)inline_tx_skb_header(skb, &q->q,
+ (void *)pos,
+ hdr_len);
+ if (before > (u64 *)pos) {
+ left = (u8 *)end - (u8 *)txq->stat;
+ end = (void *)txq->desc + left;
+ }
+
+ /* If current position is already at the end of the
+ * ofld queue, reset the current to point to
+ * start of the queue and update the end ptr as well.
+ */
+ if (pos == (u64 *)txq->stat) {
+ left = (u8 *)end - (u8 *)txq->stat;
+ end = (void *)txq->desc + left;
+ pos = (void *)txq->desc;
+ }
+
+ write_sgl(skb, &q->q, (void *)pos,
+ end, hdr_len,
(dma_addr_t *)skb->head);
#ifdef CONFIG_NEED_DMA_MAP_STATE
skb->dev = q->adap->port[0];
@@ -1604,6 +1680,11 @@ static void service_ofldq(struct sge_ofld_txq *q)
written = 0;
}
+ /* Reacquire the Pending Send Queue Lock so we can unlink the
+ * skb we've just successfully transferred to the TX Ring and
+ * loop for the next skb which may be at the head of the
+ * Pending Send Queue.
+ */
spin_lock(&q->sendq.lock);
__skb_unlink(skb, &q->sendq);
if (is_ofld_imm(skb))
@@ -1611,6 +1692,11 @@ static void service_ofldq(struct sge_ofld_txq *q)
}
if (likely(written))
ring_tx_db(q->adap, &q->q, written);
+
+ /*Indicate that no thread is processing the Pending Send Queue
+ * currently.
+ */
+ q->service_ofldq_running = false;
}
/**
@@ -1624,9 +1710,19 @@ static int ofld_xmit(struct sge_ofld_txq *q, struct sk_buff *skb)
{
skb->priority = calc_tx_flits_ofld(skb); /* save for restart */
spin_lock(&q->sendq.lock);
+
+ /* Queue the new skb onto the Offload Queue's Pending Send Queue. If
+ * that results in this new skb being the only one on the queue, start
+ * servicing it. If there are other skbs already on the list, then
+ * either the queue is currently being processed or it's been stopped
+ * for some reason and it'll be restarted at a later time. Restart
+ * paths are triggered by events like experiencing a DMA Mapping Error
+ * or filling the Hardware TX Ring.
+ */
__skb_queue_tail(&q->sendq, skb);
if (q->sendq.qlen == 1)
service_ofldq(q);
+
spin_unlock(&q->sendq.lock);
return NET_XMIT_SUCCESS;
}
@@ -1864,7 +1960,6 @@ static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl,
skb->truesize += skb->data_len;
skb->ip_summed = CHECKSUM_UNNECESSARY;
skb_record_rx_queue(skb, rxq->rspq.idx);
- skb_mark_napi_id(skb, &rxq->rspq.napi);
pi = netdev_priv(skb->dev);
if (pi->rxtstamp)
cxgb4_sgetim_to_hwtstamp(adapter, skb_hwtstamps(skb),
@@ -2193,7 +2288,7 @@ static int napi_rx_handler(struct napi_struct *napi, int budget)
if (likely(work_done < budget)) {
int timer_index;
- napi_complete(napi);
+ napi_complete_done(napi, work_done);
timer_index = QINTR_TIMER_IDX_G(q->next_intr_params);
if (q->adaptive_rx) {
@@ -2460,7 +2555,8 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq,
iq->size = roundup(iq->size, 16);
iq->desc = alloc_ring(adap->pdev_dev, iq->size, iq->iqe_len, 0,
- &iq->phys_addr, NULL, 0, NUMA_NO_NODE);
+ &iq->phys_addr, NULL, 0,
+ dev_to_node(adap->pdev_dev));
if (!iq->desc)
return -ENOMEM;
@@ -2500,7 +2596,8 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq,
fl->size = roundup(fl->size, 8);
fl->desc = alloc_ring(adap->pdev_dev, fl->size, sizeof(__be64),
sizeof(struct rx_sw_desc), &fl->addr,
- &fl->sdesc, s->stat_len, NUMA_NO_NODE);
+ &fl->sdesc, s->stat_len,
+ dev_to_node(adap->pdev_dev));
if (!fl->desc)
goto fl_nomem;
@@ -2528,7 +2625,6 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq,
goto err;
netif_napi_add(dev, &iq->napi, napi_rx_handler, 64);
- napi_hash_add(&iq->napi);
iq->cur_desc = iq->desc;
iq->cidx = 0;
iq->gen = 1;
@@ -2574,8 +2670,9 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq,
* simple (and hopefully less wrong).
*/
if (!is_t4(adap->params.chip) && cong >= 0) {
- u32 param, val;
+ u32 param, val, ch_map = 0;
int i;
+ u16 cng_ch_bits_log = adap->params.arch.cng_ch_bits_log;
param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DMAQ) |
FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DMAQ_CONM_CTXT) |
@@ -2587,9 +2684,9 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq,
CONMCTXT_CNGTPMODE_V(CONMCTXT_CNGTPMODE_CHANNEL_X);
for (i = 0; i < 4; i++) {
if (cong & (1 << i))
- val |=
- CONMCTXT_CNGCHMAP_V(1 << (i << 2));
+ ch_map |= 1 << (i << cng_ch_bits_log);
}
+ val |= CONMCTXT_CNGCHMAP_V(ch_map);
}
ret = t4_set_params(adap, adap->mbox, adap->pf, 0, 1,
&param, &val);
@@ -2884,7 +2981,7 @@ void t4_free_sge_resources(struct adapter *adap)
}
/* clean up RDMA and iSCSI Rx queues */
- t4_free_ofld_rxqs(adap, adap->sge.ofldqsets, adap->sge.ofldrxq);
+ t4_free_ofld_rxqs(adap, adap->sge.iscsiqsets, adap->sge.iscsirxq);
t4_free_ofld_rxqs(adap, adap->sge.rdmaqs, adap->sge.rdmarxq);
t4_free_ofld_rxqs(adap, adap->sge.rdmaciqs, adap->sge.rdmaciq);
@@ -3077,8 +3174,7 @@ static int t4_sge_init_soft(struct adapter *adap)
int t4_sge_init(struct adapter *adap)
{
struct sge *s = &adap->sge;
- u32 sge_control, sge_control2, sge_conm_ctrl;
- unsigned int ingpadboundary, ingpackboundary;
+ u32 sge_control, sge_conm_ctrl;
int ret, egress_threshold;
/*
@@ -3089,35 +3185,7 @@ int t4_sge_init(struct adapter *adap)
s->pktshift = PKTSHIFT_G(sge_control);
s->stat_len = (sge_control & EGRSTATUSPAGESIZE_F) ? 128 : 64;
- /* T4 uses a single control field to specify both the PCIe Padding and
- * Packing Boundary. T5 introduced the ability to specify these
- * separately. The actual Ingress Packet Data alignment boundary
- * within Packed Buffer Mode is the maximum of these two
- * specifications. (Note that it makes no real practical sense to
- * have the Pading Boudary be larger than the Packing Boundary but you
- * could set the chip up that way and, in fact, legacy T4 code would
- * end doing this because it would initialize the Padding Boundary and
- * leave the Packing Boundary initialized to 0 (16 bytes).)
- */
- ingpadboundary = 1 << (INGPADBOUNDARY_G(sge_control) +
- INGPADBOUNDARY_SHIFT_X);
- if (is_t4(adap->params.chip)) {
- s->fl_align = ingpadboundary;
- } else {
- /* T5 has a different interpretation of one of the PCIe Packing
- * Boundary values.
- */
- sge_control2 = t4_read_reg(adap, SGE_CONTROL2_A);
- ingpackboundary = INGPACKBOUNDARY_G(sge_control2);
- if (ingpackboundary == INGPACKBOUNDARY_16B_X)
- ingpackboundary = 16;
- else
- ingpackboundary = 1 << (ingpackboundary +
- INGPACKBOUNDARY_SHIFT_X);
-
- s->fl_align = max(ingpadboundary, ingpackboundary);
- }
-
+ s->fl_align = t4_fl_pkt_align(adap);
ret = t4_sge_init_soft(adap);
if (ret < 0)
return ret;
@@ -3135,10 +3203,21 @@ int t4_sge_init(struct adapter *adap)
* buffers.
*/
sge_conm_ctrl = t4_read_reg(adap, SGE_CONM_CTRL_A);
- if (is_t4(adap->params.chip))
+ switch (CHELSIO_CHIP_VERSION(adap->params.chip)) {
+ case CHELSIO_T4:
egress_threshold = EGRTHRESHOLD_G(sge_conm_ctrl);
- else
+ break;
+ case CHELSIO_T5:
egress_threshold = EGRTHRESHOLDPACKING_G(sge_conm_ctrl);
+ break;
+ case CHELSIO_T6:
+ egress_threshold = T6_EGRTHRESHOLDPACKING_G(sge_conm_ctrl);
+ break;
+ default:
+ dev_err(adap->pdev_dev, "Unsupported Chip version %d\n",
+ CHELSIO_CHIP_VERSION(adap->params.chip));
+ return -EINVAL;
+ }
s->fl_starve_thres = 2*egress_threshold + 1;
t4_idma_monitor_init(adap, &s->idma_monitor);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
index cf61a5869c6e..636b4691f252 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
@@ -1942,8 +1942,12 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
0x1190, 0x1194,
0x11a0, 0x11a4,
0x11b0, 0x11b4,
- 0x11fc, 0x1254,
- 0x1280, 0x133c,
+ 0x11fc, 0x1258,
+ 0x1280, 0x12d4,
+ 0x12d9, 0x12d9,
+ 0x12de, 0x12de,
+ 0x12e3, 0x12e3,
+ 0x12e8, 0x133c,
0x1800, 0x18fc,
0x3000, 0x302c,
0x3060, 0x30b0,
@@ -1973,7 +1977,7 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
0x5e50, 0x5e94,
0x5ea0, 0x5eb0,
0x5ec0, 0x5ec0,
- 0x5ec8, 0x5ecc,
+ 0x5ec8, 0x5ed0,
0x6000, 0x6020,
0x6028, 0x6040,
0x6058, 0x609c,
@@ -2048,7 +2052,8 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
0x19150, 0x19194,
0x1919c, 0x191b0,
0x191d0, 0x191e8,
- 0x19238, 0x192b0,
+ 0x19238, 0x19290,
+ 0x192a4, 0x192b0,
0x192bc, 0x192bc,
0x19348, 0x1934c,
0x193f8, 0x19418,
@@ -2442,7 +2447,8 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
0x40280, 0x40280,
0x40304, 0x40304,
0x40330, 0x4033c,
- 0x41304, 0x413c8,
+ 0x41304, 0x413b8,
+ 0x413c0, 0x413c8,
0x413d0, 0x413dc,
0x413f0, 0x413f0,
0x41400, 0x4140c,
@@ -5254,7 +5260,7 @@ void t4_pmtx_get_stats(struct adapter *adap, u32 cnt[], u64 cycles[])
int i;
u32 data[2];
- for (i = 0; i < PM_NSTATS; i++) {
+ for (i = 0; i < adap->params.arch.pm_stats_cnt; i++) {
t4_write_reg(adap, PM_TX_STAT_CONFIG_A, i + 1);
cnt[i] = t4_read_reg(adap, PM_TX_STAT_COUNT_A);
if (is_t4(adap->params.chip)) {
@@ -5281,7 +5287,7 @@ void t4_pmrx_get_stats(struct adapter *adap, u32 cnt[], u64 cycles[])
int i;
u32 data[2];
- for (i = 0; i < PM_NSTATS; i++) {
+ for (i = 0; i < adap->params.arch.pm_stats_cnt; i++) {
t4_write_reg(adap, PM_RX_STAT_CONFIG_A, i + 1);
cnt[i] = t4_read_reg(adap, PM_RX_STAT_COUNT_A);
if (is_t4(adap->params.chip)) {
@@ -5310,7 +5316,14 @@ unsigned int t4_get_mps_bg_map(struct adapter *adap, int idx)
if (n == 0)
return idx == 0 ? 0xf : 0;
- if (n == 1)
+ /* In T6 (which is a 2 port card),
+ * port 0 is mapped to channel 0 and port 1 is mapped to channel 1.
+ * For 2 port T4/T5 adapter,
+ * port 0 is mapped to channel 0 and 1,
+ * port 1 is mapped to channel 2 and 3.
+ */
+ if ((n == 1) &&
+ (CHELSIO_CHIP_VERSION(adap->params.chip) <= CHELSIO_T5))
return idx < 2 ? (3 << (2 * idx)) : 0;
return 1 << idx;
}
@@ -5689,6 +5702,39 @@ void t4_sge_decode_idma_state(struct adapter *adapter, int state)
"IDMA_FL_SEND_PADDING",
"IDMA_FL_SEND_COMPLETION_TO_IMSG",
};
+ static const char * const t6_decode[] = {
+ "IDMA_IDLE",
+ "IDMA_PUSH_MORE_CPL_FIFO",
+ "IDMA_PUSH_CPL_MSG_HEADER_TO_FIFO",
+ "IDMA_SGEFLRFLUSH_SEND_PCIEHDR",
+ "IDMA_PHYSADDR_SEND_PCIEHDR",
+ "IDMA_PHYSADDR_SEND_PAYLOAD_FIRST",
+ "IDMA_PHYSADDR_SEND_PAYLOAD",
+ "IDMA_FL_REQ_DATA_FL",
+ "IDMA_FL_DROP",
+ "IDMA_FL_DROP_SEND_INC",
+ "IDMA_FL_H_REQ_HEADER_FL",
+ "IDMA_FL_H_SEND_PCIEHDR",
+ "IDMA_FL_H_PUSH_CPL_FIFO",
+ "IDMA_FL_H_SEND_CPL",
+ "IDMA_FL_H_SEND_IP_HDR_FIRST",
+ "IDMA_FL_H_SEND_IP_HDR",
+ "IDMA_FL_H_REQ_NEXT_HEADER_FL",
+ "IDMA_FL_H_SEND_NEXT_PCIEHDR",
+ "IDMA_FL_H_SEND_IP_HDR_PADDING",
+ "IDMA_FL_D_SEND_PCIEHDR",
+ "IDMA_FL_D_SEND_CPL_AND_IP_HDR",
+ "IDMA_FL_D_REQ_NEXT_DATA_FL",
+ "IDMA_FL_SEND_PCIEHDR",
+ "IDMA_FL_PUSH_CPL_FIFO",
+ "IDMA_FL_SEND_CPL",
+ "IDMA_FL_SEND_PAYLOAD_FIRST",
+ "IDMA_FL_SEND_PAYLOAD",
+ "IDMA_FL_REQ_NEXT_DATA_FL",
+ "IDMA_FL_SEND_NEXT_PCIEHDR",
+ "IDMA_FL_SEND_PADDING",
+ "IDMA_FL_SEND_COMPLETION_TO_IMSG",
+ };
static const u32 sge_regs[] = {
SGE_DEBUG_DATA_LOW_INDEX_2_A,
SGE_DEBUG_DATA_LOW_INDEX_3_A,
@@ -5697,6 +5743,32 @@ void t4_sge_decode_idma_state(struct adapter *adapter, int state)
const char **sge_idma_decode;
int sge_idma_decode_nstates;
int i;
+ unsigned int chip_version = CHELSIO_CHIP_VERSION(adapter->params.chip);
+
+ /* Select the right set of decode strings to dump depending on the
+ * adapter chip type.
+ */
+ switch (chip_version) {
+ case CHELSIO_T4:
+ sge_idma_decode = (const char **)t4_decode;
+ sge_idma_decode_nstates = ARRAY_SIZE(t4_decode);
+ break;
+
+ case CHELSIO_T5:
+ sge_idma_decode = (const char **)t5_decode;
+ sge_idma_decode_nstates = ARRAY_SIZE(t5_decode);
+ break;
+
+ case CHELSIO_T6:
+ sge_idma_decode = (const char **)t6_decode;
+ sge_idma_decode_nstates = ARRAY_SIZE(t6_decode);
+ break;
+
+ default:
+ dev_err(adapter->pdev_dev,
+ "Unsupported chip version %d\n", chip_version);
+ return;
+ }
if (is_t4(adapter->params.chip)) {
sge_idma_decode = (const char **)t4_decode;
@@ -6097,6 +6169,59 @@ int t4_fw_upgrade(struct adapter *adap, unsigned int mbox,
}
/**
+ * t4_fl_pkt_align - return the fl packet alignment
+ * @adap: the adapter
+ *
+ * T4 has a single field to specify the packing and padding boundary.
+ * T5 onwards has separate fields for this and hence the alignment for
+ * next packet offset is maximum of these two.
+ *
+ */
+int t4_fl_pkt_align(struct adapter *adap)
+{
+ u32 sge_control, sge_control2;
+ unsigned int ingpadboundary, ingpackboundary, fl_align, ingpad_shift;
+
+ sge_control = t4_read_reg(adap, SGE_CONTROL_A);
+
+ /* T4 uses a single control field to specify both the PCIe Padding and
+ * Packing Boundary. T5 introduced the ability to specify these
+ * separately. The actual Ingress Packet Data alignment boundary
+ * within Packed Buffer Mode is the maximum of these two
+ * specifications. (Note that it makes no real practical sense to
+ * have the Pading Boudary be larger than the Packing Boundary but you
+ * could set the chip up that way and, in fact, legacy T4 code would
+ * end doing this because it would initialize the Padding Boundary and
+ * leave the Packing Boundary initialized to 0 (16 bytes).)
+ * Padding Boundary values in T6 starts from 8B,
+ * where as it is 32B for T4 and T5.
+ */
+ if (CHELSIO_CHIP_VERSION(adap->params.chip) <= CHELSIO_T5)
+ ingpad_shift = INGPADBOUNDARY_SHIFT_X;
+ else
+ ingpad_shift = T6_INGPADBOUNDARY_SHIFT_X;
+
+ ingpadboundary = 1 << (INGPADBOUNDARY_G(sge_control) + ingpad_shift);
+
+ fl_align = ingpadboundary;
+ if (!is_t4(adap->params.chip)) {
+ /* T5 has a weird interpretation of one of the PCIe Packing
+ * Boundary values. No idea why ...
+ */
+ sge_control2 = t4_read_reg(adap, SGE_CONTROL2_A);
+ ingpackboundary = INGPACKBOUNDARY_G(sge_control2);
+ if (ingpackboundary == INGPACKBOUNDARY_16B_X)
+ ingpackboundary = 16;
+ else
+ ingpackboundary = 1 << (ingpackboundary +
+ INGPACKBOUNDARY_SHIFT_X);
+
+ fl_align = max(ingpadboundary, ingpackboundary);
+ }
+ return fl_align;
+}
+
+/**
* t4_fixup_host_params - fix up host-dependent parameters
* @adap: the adapter
* @page_size: the host's Base Page Size
@@ -6114,6 +6239,7 @@ int t4_fixup_host_params(struct adapter *adap, unsigned int page_size,
unsigned int stat_len = cache_line_size > 64 ? 128 : 64;
unsigned int fl_align = cache_line_size < 32 ? 32 : cache_line_size;
unsigned int fl_align_log = fls(fl_align) - 1;
+ unsigned int ingpad;
t4_write_reg(adap, SGE_HOST_PAGE_SIZE_A,
HOSTPAGESIZEPF0_V(sge_hps) |
@@ -6161,10 +6287,16 @@ int t4_fixup_host_params(struct adapter *adap, unsigned int page_size,
fl_align = 64;
fl_align_log = 6;
}
+
+ if (is_t5(adap->params.chip))
+ ingpad = INGPCIEBOUNDARY_32B_X;
+ else
+ ingpad = T6_INGPADBOUNDARY_32B_X;
+
t4_set_reg_field(adap, SGE_CONTROL_A,
INGPADBOUNDARY_V(INGPADBOUNDARY_M) |
EGRSTATUSPAGESIZE_F,
- INGPADBOUNDARY_V(INGPCIEBOUNDARY_32B_X) |
+ INGPADBOUNDARY_V(ingpad) |
EGRSTATUSPAGESIZE_V(stat_len != 64));
t4_set_reg_field(adap, SGE_CONTROL2_A,
INGPACKBOUNDARY_V(INGPACKBOUNDARY_M),
@@ -7060,7 +7192,12 @@ int t4_prep_adapter(struct adapter *adapter)
NUM_MPS_CLS_SRAM_L_INSTANCES;
adapter->params.arch.mps_rplc_size = 128;
adapter->params.arch.nchan = NCHAN;
+ adapter->params.arch.pm_stats_cnt = PM_NSTATS;
adapter->params.arch.vfcount = 128;
+ /* Congestion map is for 4 channels so that
+ * MPS can have 4 priority per port.
+ */
+ adapter->params.arch.cng_ch_bits_log = 2;
break;
case CHELSIO_T5:
adapter->params.chip |= CHELSIO_CHIP_CODE(CHELSIO_T5, pl_rev);
@@ -7069,7 +7206,9 @@ int t4_prep_adapter(struct adapter *adapter)
NUM_MPS_T5_CLS_SRAM_L_INSTANCES;
adapter->params.arch.mps_rplc_size = 128;
adapter->params.arch.nchan = NCHAN;
+ adapter->params.arch.pm_stats_cnt = PM_NSTATS;
adapter->params.arch.vfcount = 128;
+ adapter->params.arch.cng_ch_bits_log = 2;
break;
case CHELSIO_T6:
adapter->params.chip |= CHELSIO_CHIP_CODE(CHELSIO_T6, pl_rev);
@@ -7078,7 +7217,12 @@ int t4_prep_adapter(struct adapter *adapter)
NUM_MPS_T5_CLS_SRAM_L_INSTANCES;
adapter->params.arch.mps_rplc_size = 256;
adapter->params.arch.nchan = 2;
+ adapter->params.arch.pm_stats_cnt = T6_PM_NSTATS;
adapter->params.arch.vfcount = 256;
+ /* Congestion map will be for 2 channels so that
+ * MPS can have 8 priority per port.
+ */
+ adapter->params.arch.cng_ch_bits_log = 3;
break;
default:
dev_err(adapter->pdev_dev, "Device %d is not supported\n",
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h
index 13708fde1668..2fc60e83a7a1 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h
@@ -48,6 +48,7 @@ enum {
NMTUS = 16, /* size of MTU table */
NCCTRL_WIN = 32, /* # of congestion control windows */
PM_NSTATS = 5, /* # of PM stats */
+ T6_PM_NSTATS = 7, /* # of PM stats in T6 */
MBOX_LEN = 64, /* mailbox size in bytes */
TRACE_LEN = 112, /* length of trace data and mask */
FILTER_OPT_LEN = 36, /* filter tuple width for optional components */
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h b/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h
index 03ed00c49823..a8dda635456d 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h
@@ -162,6 +162,9 @@ CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN
CH_PCI_ID_TABLE_FENTRY(0x5095), /* Custom T540-CR-SO */
CH_PCI_ID_TABLE_FENTRY(0x5096), /* Custom T580-CR */
CH_PCI_ID_TABLE_FENTRY(0x5097), /* Custom T520-KR */
+ CH_PCI_ID_TABLE_FENTRY(0x5098), /* Custom 2x40G QSFP */
+ CH_PCI_ID_TABLE_FENTRY(0x5099), /* Custom 2x40G QSFP */
+ CH_PCI_ID_TABLE_FENTRY(0x509a), /* Custom T520-CR */
/* T6 adapters:
*/
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
index fc3044c8ac1c..9fea255c7e87 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
@@ -492,6 +492,9 @@
#define STATSOURCE_T5_V(x) ((x) << STATSOURCE_T5_S)
#define STATSOURCE_T5_G(x) (((x) >> STATSOURCE_T5_S) & STATSOURCE_T5_M)
+#define T6_STATMODE_S 0
+#define T6_STATMODE_V(x) ((x) << T6_STATMODE_S)
+
#define SGE_DBFIFO_STATUS2_A 0x1118
#define HP_INT_THRESH_T5_S 10
@@ -2395,6 +2398,30 @@
#define MPS_CLS_TCAM_DATA0_A 0xf000
#define MPS_CLS_TCAM_DATA1_A 0xf004
+#define VIDL_S 16
+#define VIDL_M 0xffffU
+#define VIDL_G(x) (((x) >> VIDL_S) & VIDL_M)
+
+#define DATALKPTYPE_S 10
+#define DATALKPTYPE_M 0x3U
+#define DATALKPTYPE_G(x) (((x) >> DATALKPTYPE_S) & DATALKPTYPE_M)
+
+#define DATAPORTNUM_S 12
+#define DATAPORTNUM_M 0xfU
+#define DATAPORTNUM_G(x) (((x) >> DATAPORTNUM_S) & DATAPORTNUM_M)
+
+#define DATADIPHIT_S 8
+#define DATADIPHIT_V(x) ((x) << DATADIPHIT_S)
+#define DATADIPHIT_F DATADIPHIT_V(1U)
+
+#define DATAVIDH2_S 7
+#define DATAVIDH2_V(x) ((x) << DATAVIDH2_S)
+#define DATAVIDH2_F DATAVIDH2_V(1U)
+
+#define DATAVIDH1_S 0
+#define DATAVIDH1_M 0x7fU
+#define DATAVIDH1_G(x) (((x) >> DATAVIDH1_S) & DATAVIDH1_M)
+
#define USED_S 16
#define USED_M 0x7ffU
#define USED_G(x) (((x) >> USED_S) & USED_M)
@@ -2802,6 +2829,10 @@
#define HASHEN_V(x) ((x) << HASHEN_S)
#define HASHEN_F HASHEN_V(1U)
+#define ASLIPCOMPEN_S 17
+#define ASLIPCOMPEN_V(x) ((x) << ASLIPCOMPEN_S)
+#define ASLIPCOMPEN_F ASLIPCOMPEN_V(1U)
+
#define REQQPARERR_S 16
#define REQQPARERR_V(x) ((x) << REQQPARERR_S)
#define REQQPARERR_F REQQPARERR_V(1U)
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h
index 7bdee3bf75ec..a5231fa771db 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h
@@ -53,6 +53,9 @@
#define INGPADBOUNDARY_SHIFT_X 5
+#define T6_INGPADBOUNDARY_SHIFT_X 3
+#define T6_INGPADBOUNDARY_32B_X 2
+
/* CONTROL2 register */
#define INGPACKBOUNDARY_SHIFT_X 5
#define INGPACKBOUNDARY_16B_X 0
diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
index fa3786a9d30e..6528231d8a59 100644
--- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
@@ -2607,7 +2607,7 @@ int t4vf_sge_init(struct adapter *adapter)
u32 fl0 = sge_params->sge_fl_buffer_size[0];
u32 fl1 = sge_params->sge_fl_buffer_size[1];
struct sge *s = &adapter->sge;
- unsigned int ingpadboundary, ingpackboundary;
+ unsigned int ingpadboundary, ingpackboundary, ingpad_shift;
/*
* Start by vetting the basic SGE parameters which have been set up by
@@ -2642,9 +2642,16 @@ int t4vf_sge_init(struct adapter *adapter)
* could set the chip up that way and, in fact, legacy T4 code would
* end doing this because it would initialize the Padding Boundary and
* leave the Packing Boundary initialized to 0 (16 bytes).)
+ * Padding Boundary values in T6 starts from 8B,
+ * where as it is 32B for T4 and T5.
*/
+ if (CHELSIO_CHIP_VERSION(adapter->params.chip) <= CHELSIO_T5)
+ ingpad_shift = INGPADBOUNDARY_SHIFT_X;
+ else
+ ingpad_shift = T6_INGPADBOUNDARY_SHIFT_X;
+
ingpadboundary = 1 << (INGPADBOUNDARY_G(sge_params->sge_control) +
- INGPADBOUNDARY_SHIFT_X);
+ ingpad_shift);
if (is_t4(adapter->params.chip)) {
s->fl_align = ingpadboundary;
} else {
diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h
index b516b12b1884..f859db3d254c 100644
--- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h
@@ -54,6 +54,7 @@
#define T4VF_MPS_BASE_ADDR 0x0100
#define T4VF_PL_BASE_ADDR 0x0200
#define T4VF_MBDATA_BASE_ADDR 0x0240
+#define T6VF_MBDATA_BASE_ADDR 0x0280
#define T4VF_CIM_BASE_ADDR 0x0300
#define T4VF_REGMAP_START 0x0000
diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c
index 63dd5fdac5b9..b6fa74aafe47 100644
--- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c
@@ -120,12 +120,19 @@ int t4vf_wr_mbox_core(struct adapter *adapter, const void *cmd, int size,
1, 1, 3, 5, 10, 10, 20, 50, 100
};
- u32 v;
+ u32 v, mbox_data;
int i, ms, delay_idx;
const __be64 *p;
- u32 mbox_data = T4VF_MBDATA_BASE_ADDR;
u32 mbox_ctl = T4VF_CIM_BASE_ADDR + CIM_VF_EXT_MAILBOX_CTRL;
+ /* In T6, mailbox size is changed to 128 bytes to avoid
+ * invalidating the entire prefetch buffer.
+ */
+ if (CHELSIO_CHIP_VERSION(adapter->params.chip) <= CHELSIO_T5)
+ mbox_data = T4VF_MBDATA_BASE_ADDR;
+ else
+ mbox_data = T6VF_MBDATA_BASE_ADDR;
+
/*
* Commands must be multiples of 16 bytes in length and may not be
* larger than the size of the Mailbox Data register array.
diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c
index b36643ef0593..b2182d3ba3cc 100644
--- a/drivers/net/ethernet/cisco/enic/enic_main.c
+++ b/drivers/net/ethernet/cisco/enic/enic_main.c
@@ -2458,13 +2458,11 @@ static int enic_dev_init(struct enic *enic)
switch (vnic_dev_get_intr_mode(enic->vdev)) {
default:
netif_napi_add(netdev, &enic->napi[0], enic_poll, 64);
- napi_hash_add(&enic->napi[0]);
break;
case VNIC_DEV_INTR_MODE_MSIX:
for (i = 0; i < enic->rq_count; i++) {
netif_napi_add(netdev, &enic->napi[i],
enic_poll_msix_rq, NAPI_POLL_WEIGHT);
- napi_hash_add(&enic->napi[i]);
}
for (i = 0; i < enic->wq_count; i++)
netif_napi_add(netdev, &enic->napi[enic_cq_wq(enic, i)],
diff --git a/drivers/net/ethernet/dec/tulip/de4x5.c b/drivers/net/ethernet/dec/tulip/de4x5.c
index 8966f3159bb2..3acde3b9b767 100644
--- a/drivers/net/ethernet/dec/tulip/de4x5.c
+++ b/drivers/net/ethernet/dec/tulip/de4x5.c
@@ -1990,7 +1990,7 @@ SetMulticastFilter(struct net_device *dev)
static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST;
-static int __init de4x5_eisa_probe (struct device *gendev)
+static int de4x5_eisa_probe(struct device *gendev)
{
struct eisa_device *edev;
u_long iobase;
diff --git a/drivers/net/ethernet/dec/tulip/tulip_core.c b/drivers/net/ethernet/dec/tulip/tulip_core.c
index ed41559bae77..b553409e04ad 100644
--- a/drivers/net/ethernet/dec/tulip/tulip_core.c
+++ b/drivers/net/ethernet/dec/tulip/tulip_core.c
@@ -98,8 +98,7 @@ static int csr0 = 0x01A00000 | 0x4800;
#elif defined(__mips__)
static int csr0 = 0x00200000 | 0x4000;
#else
-#warning Processor architecture undefined!
-static int csr0 = 0x00A00000 | 0x4800;
+static int csr0;
#endif
/* Operational parameters that usually are not changed. */
@@ -1982,6 +1981,12 @@ static int __init tulip_init (void)
pr_info("%s", version);
#endif
+ if (!csr0) {
+ pr_warn("tulip: unknown CPU architecture, using default csr0\n");
+ /* default to 8 longword cache line alignment */
+ csr0 = 0x00A00000 | 0x4800;
+ }
+
/* copy module parms into globals */
tulip_rx_copybreak = rx_copybreak;
tulip_max_interrupt_work = max_interrupt_work;
diff --git a/drivers/net/ethernet/dec/tulip/winbond-840.c b/drivers/net/ethernet/dec/tulip/winbond-840.c
index 9beb3d34d4ba..3c0e4d5c5fef 100644
--- a/drivers/net/ethernet/dec/tulip/winbond-840.c
+++ b/drivers/net/ethernet/dec/tulip/winbond-840.c
@@ -907,7 +907,7 @@ static void init_registers(struct net_device *dev)
#elif defined(CONFIG_SPARC) || defined (CONFIG_PARISC) || defined(CONFIG_ARM)
i |= 0x4800;
#else
-#warning Processor architecture undefined
+ dev_warn(&dev->dev, "unknown CPU architecture, using default csr0 setting\n");
i |= 0x4800;
#endif
iowrite32(i, ioaddr + PCIBusCfg);
diff --git a/drivers/net/ethernet/dlink/dl2k.c b/drivers/net/ethernet/dlink/dl2k.c
index ccca4799c27b..f92b6d948398 100644
--- a/drivers/net/ethernet/dlink/dl2k.c
+++ b/drivers/net/ethernet/dlink/dl2k.c
@@ -70,7 +70,6 @@ static const int multicast_filter_limit = 0x40;
static int rio_open (struct net_device *dev);
static void rio_timer (unsigned long data);
static void rio_tx_timeout (struct net_device *dev);
-static void alloc_list (struct net_device *dev);
static netdev_tx_t start_xmit (struct sk_buff *skb, struct net_device *dev);
static irqreturn_t rio_interrupt (int irq, void *dev_instance);
static void rio_free_tx (struct net_device *dev, int irq);
@@ -253,19 +252,6 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent)
if (err)
goto err_out_unmap_rx;
- if (np->chip_id == CHIP_IP1000A &&
- (np->pdev->revision == 0x40 || np->pdev->revision == 0x41)) {
- /* PHY magic taken from ipg driver, undocumented registers */
- mii_write(dev, np->phy_addr, 31, 0x0001);
- mii_write(dev, np->phy_addr, 27, 0x01e0);
- mii_write(dev, np->phy_addr, 31, 0x0002);
- mii_write(dev, np->phy_addr, 27, 0xeb8e);
- mii_write(dev, np->phy_addr, 31, 0x0000);
- mii_write(dev, np->phy_addr, 30, 0x005e);
- /* advertise 1000BASE-T half & full duplex, prefer MASTER */
- mii_write(dev, np->phy_addr, MII_CTRL1000, 0x0700);
- }
-
/* Fiber device? */
np->phy_media = (dr16(ASICCtrl) & PhyMedia) ? 1 : 0;
np->link_status = 0;
@@ -275,13 +261,11 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent)
if (np->an_enable == 2) {
np->an_enable = 1;
}
- mii_set_media_pcs (dev);
} else {
/* Auto-Negotiation is mandatory for 1000BASE-T,
IEEE 802.3ab Annex 28D page 14 */
if (np->speed == 1000)
np->an_enable = 1;
- mii_set_media (dev);
}
err = register_netdev (dev);
@@ -446,19 +430,106 @@ static void rio_set_led_mode(struct net_device *dev)
dw32(ASICCtrl, mode);
}
-static int
-rio_open (struct net_device *dev)
+static inline dma_addr_t desc_to_dma(struct netdev_desc *desc)
+{
+ return le64_to_cpu(desc->fraginfo) & DMA_BIT_MASK(48);
+}
+
+static void free_list(struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ struct sk_buff *skb;
+ int i;
+
+ /* Free all the skbuffs in the queue. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ skb = np->rx_skbuff[i];
+ if (skb) {
+ pci_unmap_single(np->pdev, desc_to_dma(&np->rx_ring[i]),
+ skb->len, PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(skb);
+ np->rx_skbuff[i] = NULL;
+ }
+ np->rx_ring[i].status = 0;
+ np->rx_ring[i].fraginfo = 0;
+ }
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ skb = np->tx_skbuff[i];
+ if (skb) {
+ pci_unmap_single(np->pdev, desc_to_dma(&np->tx_ring[i]),
+ skb->len, PCI_DMA_TODEVICE);
+ dev_kfree_skb(skb);
+ np->tx_skbuff[i] = NULL;
+ }
+ }
+}
+
+static void rio_reset_ring(struct netdev_private *np)
+{
+ int i;
+
+ np->cur_rx = 0;
+ np->cur_tx = 0;
+ np->old_rx = 0;
+ np->old_tx = 0;
+
+ for (i = 0; i < TX_RING_SIZE; i++)
+ np->tx_ring[i].status = cpu_to_le64(TFDDone);
+
+ for (i = 0; i < RX_RING_SIZE; i++)
+ np->rx_ring[i].status = 0;
+}
+
+ /* allocate and initialize Tx and Rx descriptors */
+static int alloc_list(struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ int i;
+
+ rio_reset_ring(np);
+ np->rx_buf_sz = (dev->mtu <= 1500 ? PACKET_SIZE : dev->mtu + 32);
+
+ /* Initialize Tx descriptors, TFDListPtr leaves in start_xmit(). */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ np->tx_skbuff[i] = NULL;
+ np->tx_ring[i].next_desc = cpu_to_le64(np->tx_ring_dma +
+ ((i + 1) % TX_RING_SIZE) *
+ sizeof(struct netdev_desc));
+ }
+
+ /* Initialize Rx descriptors & allocate buffers */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ /* Allocated fixed size of skbuff */
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb_ip_align(dev, np->rx_buf_sz);
+ np->rx_skbuff[i] = skb;
+ if (!skb) {
+ free_list(dev);
+ return -ENOMEM;
+ }
+
+ np->rx_ring[i].next_desc = cpu_to_le64(np->rx_ring_dma +
+ ((i + 1) % RX_RING_SIZE) *
+ sizeof(struct netdev_desc));
+ /* Rubicon now supports 40 bits of addressing space. */
+ np->rx_ring[i].fraginfo =
+ cpu_to_le64(pci_map_single(
+ np->pdev, skb->data, np->rx_buf_sz,
+ PCI_DMA_FROMDEVICE));
+ np->rx_ring[i].fraginfo |= cpu_to_le64((u64)np->rx_buf_sz << 48);
+ }
+
+ return 0;
+}
+
+static void rio_hw_init(struct net_device *dev)
{
struct netdev_private *np = netdev_priv(dev);
void __iomem *ioaddr = np->ioaddr;
- const int irq = np->pdev->irq;
int i;
u16 macctrl;
- i = request_irq(irq, rio_interrupt, IRQF_SHARED, dev->name, dev);
- if (i)
- return i;
-
/* Reset all logic functions */
dw16(ASICCtrl + 2,
GlobalReset | DMAReset | FIFOReset | NetworkReset | HostReset);
@@ -469,11 +540,31 @@ rio_open (struct net_device *dev)
/* DebugCtrl bit 4, 5, 9 must set */
dw32(DebugCtrl, dr32(DebugCtrl) | 0x0230);
+ if (np->chip_id == CHIP_IP1000A &&
+ (np->pdev->revision == 0x40 || np->pdev->revision == 0x41)) {
+ /* PHY magic taken from ipg driver, undocumented registers */
+ mii_write(dev, np->phy_addr, 31, 0x0001);
+ mii_write(dev, np->phy_addr, 27, 0x01e0);
+ mii_write(dev, np->phy_addr, 31, 0x0002);
+ mii_write(dev, np->phy_addr, 27, 0xeb8e);
+ mii_write(dev, np->phy_addr, 31, 0x0000);
+ mii_write(dev, np->phy_addr, 30, 0x005e);
+ /* advertise 1000BASE-T half & full duplex, prefer MASTER */
+ mii_write(dev, np->phy_addr, MII_CTRL1000, 0x0700);
+ }
+
+ if (np->phy_media)
+ mii_set_media_pcs(dev);
+ else
+ mii_set_media(dev);
+
/* Jumbo frame */
if (np->jumbo != 0)
dw16(MaxFrameSize, MAX_JUMBO+14);
- alloc_list (dev);
+ /* Set RFDListPtr */
+ dw32(RFDListPtr0, np->rx_ring_dma);
+ dw32(RFDListPtr1, 0);
/* Set station address */
/* 16 or 32-bit access is required by TC9020 datasheet but 8-bit works
@@ -509,10 +600,6 @@ rio_open (struct net_device *dev)
dw32(MACCtrl, dr32(MACCtrl) | AutoVLANuntagging);
}
- setup_timer(&np->timer, rio_timer, (unsigned long)dev);
- np->timer.expires = jiffies + 1*HZ;
- add_timer (&np->timer);
-
/* Start Tx/Rx */
dw32(MACCtrl, dr32(MACCtrl) | StatsEnable | RxEnable | TxEnable);
@@ -522,6 +609,42 @@ rio_open (struct net_device *dev)
macctrl |= (np->tx_flow) ? TxFlowControlEnable : 0;
macctrl |= (np->rx_flow) ? RxFlowControlEnable : 0;
dw16(MACCtrl, macctrl);
+}
+
+static void rio_hw_stop(struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ void __iomem *ioaddr = np->ioaddr;
+
+ /* Disable interrupts */
+ dw16(IntEnable, 0);
+
+ /* Stop Tx and Rx logics */
+ dw32(MACCtrl, TxDisable | RxDisable | StatsDisable);
+}
+
+static int rio_open(struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ const int irq = np->pdev->irq;
+ int i;
+
+ i = alloc_list(dev);
+ if (i)
+ return i;
+
+ rio_hw_init(dev);
+
+ i = request_irq(irq, rio_interrupt, IRQF_SHARED, dev->name, dev);
+ if (i) {
+ rio_hw_stop(dev);
+ free_list(dev);
+ return i;
+ }
+
+ setup_timer(&np->timer, rio_timer, (unsigned long)dev);
+ np->timer.expires = jiffies + 1 * HZ;
+ add_timer(&np->timer);
netif_start_queue (dev);
@@ -586,60 +709,6 @@ rio_tx_timeout (struct net_device *dev)
dev->trans_start = jiffies; /* prevent tx timeout */
}
- /* allocate and initialize Tx and Rx descriptors */
-static void
-alloc_list (struct net_device *dev)
-{
- struct netdev_private *np = netdev_priv(dev);
- void __iomem *ioaddr = np->ioaddr;
- int i;
-
- np->cur_rx = np->cur_tx = 0;
- np->old_rx = np->old_tx = 0;
- np->rx_buf_sz = (dev->mtu <= 1500 ? PACKET_SIZE : dev->mtu + 32);
-
- /* Initialize Tx descriptors, TFDListPtr leaves in start_xmit(). */
- for (i = 0; i < TX_RING_SIZE; i++) {
- np->tx_skbuff[i] = NULL;
- np->tx_ring[i].status = cpu_to_le64 (TFDDone);
- np->tx_ring[i].next_desc = cpu_to_le64 (np->tx_ring_dma +
- ((i+1)%TX_RING_SIZE) *
- sizeof (struct netdev_desc));
- }
-
- /* Initialize Rx descriptors */
- for (i = 0; i < RX_RING_SIZE; i++) {
- np->rx_ring[i].next_desc = cpu_to_le64 (np->rx_ring_dma +
- ((i + 1) % RX_RING_SIZE) *
- sizeof (struct netdev_desc));
- np->rx_ring[i].status = 0;
- np->rx_ring[i].fraginfo = 0;
- np->rx_skbuff[i] = NULL;
- }
-
- /* Allocate the rx buffers */
- for (i = 0; i < RX_RING_SIZE; i++) {
- /* Allocated fixed size of skbuff */
- struct sk_buff *skb;
-
- skb = netdev_alloc_skb_ip_align(dev, np->rx_buf_sz);
- np->rx_skbuff[i] = skb;
- if (skb == NULL)
- break;
-
- /* Rubicon now supports 40 bits of addressing space. */
- np->rx_ring[i].fraginfo =
- cpu_to_le64 ( pci_map_single (
- np->pdev, skb->data, np->rx_buf_sz,
- PCI_DMA_FROMDEVICE));
- np->rx_ring[i].fraginfo |= cpu_to_le64((u64)np->rx_buf_sz << 48);
- }
-
- /* Set RFDListPtr */
- dw32(RFDListPtr0, np->rx_ring_dma);
- dw32(RFDListPtr1, 0);
-}
-
static netdev_tx_t
start_xmit (struct sk_buff *skb, struct net_device *dev)
{
@@ -748,11 +817,6 @@ rio_interrupt (int irq, void *dev_instance)
return IRQ_RETVAL(handled);
}
-static inline dma_addr_t desc_to_dma(struct netdev_desc *desc)
-{
- return le64_to_cpu(desc->fraginfo) & DMA_BIT_MASK(48);
-}
-
static void
rio_free_tx (struct net_device *dev, int irq)
{
@@ -1730,44 +1794,16 @@ static int
rio_close (struct net_device *dev)
{
struct netdev_private *np = netdev_priv(dev);
- void __iomem *ioaddr = np->ioaddr;
-
struct pci_dev *pdev = np->pdev;
- struct sk_buff *skb;
- int i;
netif_stop_queue (dev);
- /* Disable interrupts */
- dw16(IntEnable, 0);
-
- /* Stop Tx and Rx logics */
- dw32(MACCtrl, TxDisable | RxDisable | StatsDisable);
+ rio_hw_stop(dev);
free_irq(pdev->irq, dev);
del_timer_sync (&np->timer);
- /* Free all the skbuffs in the queue. */
- for (i = 0; i < RX_RING_SIZE; i++) {
- skb = np->rx_skbuff[i];
- if (skb) {
- pci_unmap_single(pdev, desc_to_dma(&np->rx_ring[i]),
- skb->len, PCI_DMA_FROMDEVICE);
- dev_kfree_skb (skb);
- np->rx_skbuff[i] = NULL;
- }
- np->rx_ring[i].status = 0;
- np->rx_ring[i].fraginfo = 0;
- }
- for (i = 0; i < TX_RING_SIZE; i++) {
- skb = np->tx_skbuff[i];
- if (skb) {
- pci_unmap_single(pdev, desc_to_dma(&np->tx_ring[i]),
- skb->len, PCI_DMA_TODEVICE);
- dev_kfree_skb (skb);
- np->tx_skbuff[i] = NULL;
- }
- }
+ free_list(dev);
return 0;
}
@@ -1795,11 +1831,55 @@ rio_remove1 (struct pci_dev *pdev)
}
}
+#ifdef CONFIG_PM_SLEEP
+static int rio_suspend(struct device *device)
+{
+ struct net_device *dev = dev_get_drvdata(device);
+ struct netdev_private *np = netdev_priv(dev);
+
+ if (!netif_running(dev))
+ return 0;
+
+ netif_device_detach(dev);
+ del_timer_sync(&np->timer);
+ rio_hw_stop(dev);
+
+ return 0;
+}
+
+static int rio_resume(struct device *device)
+{
+ struct net_device *dev = dev_get_drvdata(device);
+ struct netdev_private *np = netdev_priv(dev);
+
+ if (!netif_running(dev))
+ return 0;
+
+ rio_reset_ring(np);
+ rio_hw_init(dev);
+ np->timer.expires = jiffies + 1 * HZ;
+ add_timer(&np->timer);
+ netif_device_attach(dev);
+ dl2k_enable_int(np);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(rio_pm_ops, rio_suspend, rio_resume);
+#define RIO_PM_OPS (&rio_pm_ops)
+
+#else
+
+#define RIO_PM_OPS NULL
+
+#endif /* CONFIG_PM_SLEEP */
+
static struct pci_driver rio_driver = {
.name = "dl2k",
.id_table = rio_pci_tbl,
.probe = rio_probe1,
.remove = rio_remove1,
+ .driver.pm = RIO_PM_OPS,
};
module_pci_driver(rio_driver);
diff --git a/drivers/net/ethernet/dnet.c b/drivers/net/ethernet/dnet.c
index 13d00a38a5bd..b69a9eacc531 100644
--- a/drivers/net/ethernet/dnet.c
+++ b/drivers/net/ethernet/dnet.c
@@ -255,15 +255,9 @@ static int dnet_mii_probe(struct net_device *dev)
{
struct dnet *bp = netdev_priv(dev);
struct phy_device *phydev = NULL;
- int phy_addr;
/* find the first phy */
- for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
- if (bp->mii_bus->phy_map[phy_addr]) {
- phydev = bp->mii_bus->phy_map[phy_addr];
- break;
- }
- }
+ phydev = phy_find_first(bp->mii_bus);
if (!phydev) {
printk(KERN_ERR "%s: no PHY found\n", dev->name);
@@ -274,11 +268,11 @@ static int dnet_mii_probe(struct net_device *dev)
/* attach the mac to the phy */
if (bp->capabilities & DNET_HAS_RMII) {
- phydev = phy_connect(dev, dev_name(&phydev->dev),
+ phydev = phy_connect(dev, phydev_name(phydev),
&dnet_handle_link_change,
PHY_INTERFACE_MODE_RMII);
} else {
- phydev = phy_connect(dev, dev_name(&phydev->dev),
+ phydev = phy_connect(dev, phydev_name(phydev),
&dnet_handle_link_change,
PHY_INTERFACE_MODE_MII);
}
@@ -308,7 +302,7 @@ static int dnet_mii_probe(struct net_device *dev)
static int dnet_mii_init(struct dnet *bp)
{
- int err, i;
+ int err;
bp->mii_bus = mdiobus_alloc();
if (bp->mii_bus == NULL)
@@ -323,16 +317,6 @@ static int dnet_mii_init(struct dnet *bp)
bp->mii_bus->priv = bp;
- bp->mii_bus->irq = devm_kmalloc(&bp->pdev->dev,
- sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!bp->mii_bus->irq) {
- err = -ENOMEM;
- goto err_out;
- }
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- bp->mii_bus->irq[i] = PHY_POLL;
-
if (mdiobus_register(bp->mii_bus)) {
err = -ENXIO;
goto err_out;
@@ -892,9 +876,7 @@ static int dnet_probe(struct platform_device *pdev)
(bp->capabilities & DNET_HAS_GIGABIT) ? "" : "no ",
(bp->capabilities & DNET_HAS_DMA) ? "" : "no ");
phydev = bp->phy_dev;
- dev_info(&pdev->dev, "attached PHY driver [%s] "
- "(mii_bus:phy_addr=%s, irq=%d)\n",
- phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
+ phy_attached_info(phydev);
return 0;
diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h
index d463563e1f70..cf837831304b 100644
--- a/drivers/net/ethernet/emulex/benet/be.h
+++ b/drivers/net/ethernet/emulex/benet/be.h
@@ -37,7 +37,7 @@
#include "be_hw.h"
#include "be_roce.h"
-#define DRV_VER "10.6.0.3"
+#define DRV_VER "11.0.0.0"
#define DRV_NAME "be2net"
#define BE_NAME "Emulex BladeEngine2"
#define BE3_NAME "Emulex BladeEngine3"
@@ -521,7 +521,7 @@ struct be_adapter {
struct be_drv_stats drv_stats;
struct be_aic_obj aic_obj[MAX_EVT_QS];
u8 vlan_prio_bmap; /* Available Priority BitMap */
- u16 recommended_prio; /* Recommended Priority */
+ u16 recommended_prio_bits;/* Recommended Priority bits in vlan tag */
struct be_dma_mem rx_filter; /* Cmd DMA mem for rx-filter */
struct be_dma_mem stats_cmd;
@@ -547,10 +547,6 @@ struct be_adapter {
u32 beacon_state; /* for set_phys_id */
- bool eeh_error;
- bool fw_timeout;
- bool hw_error;
-
u32 port_num;
char port_name;
u8 mc_type;
@@ -574,6 +570,8 @@ struct be_adapter {
struct be_resources pool_res; /* resources available for the port */
struct be_resources res; /* resources available for the func */
u16 num_vfs; /* Number of VFs provisioned by PF */
+ u8 pf_num; /* Numbering used by FW, starts at 0 */
+ u8 vf_num; /* Numbering used by FW, starts at 1 */
u8 virtfn;
struct be_vf_cfg *vf_cfg;
bool be3_native;
@@ -591,11 +589,10 @@ struct be_adapter {
u32 msg_enable;
int be_get_temp_freq;
struct be_hwmon hwmon_info;
- u8 pf_number;
- u8 pci_func_num;
struct rss_info rss_info;
/* Filters for packets that need to be sent to BMC */
u32 bmc_filt_mask;
+ u32 fat_dump_len;
u16 serial_num[CNTL_SERIAL_NUM_WORDS];
};
@@ -848,8 +845,6 @@ void be_roce_dev_remove(struct be_adapter *);
/*
* internal function to open-close roce device during ifup-ifdown.
*/
-void be_roce_dev_open(struct be_adapter *);
-void be_roce_dev_close(struct be_adapter *);
void be_roce_dev_shutdown(struct be_adapter *);
#endif /* BE_H */
diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c
index 1795c935ff02..b63d8ad2e115 100644
--- a/drivers/net/ethernet/emulex/benet/be_cmds.c
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.c
@@ -308,8 +308,7 @@ static void be_async_grp5_cos_priority_process(struct be_adapter *adapter,
if (evt->valid) {
adapter->vlan_prio_bmap = evt->available_priority_bmap;
- adapter->recommended_prio &= ~VLAN_PRIO_MASK;
- adapter->recommended_prio =
+ adapter->recommended_prio_bits =
evt->reco_default_priority << VLAN_PRIO_SHIFT;
}
}
@@ -1713,49 +1712,40 @@ err:
}
/* Uses synchronous mcc */
-int be_cmd_get_reg_len(struct be_adapter *adapter, u32 *log_size)
+int be_cmd_get_fat_dump_len(struct be_adapter *adapter, u32 *dump_size)
{
- struct be_mcc_wrb *wrb;
+ struct be_mcc_wrb wrb = {0};
struct be_cmd_req_get_fat *req;
int status;
- spin_lock_bh(&adapter->mcc_lock);
-
- wrb = wrb_from_mccq(adapter);
- if (!wrb) {
- status = -EBUSY;
- goto err;
- }
- req = embedded_payload(wrb);
+ req = embedded_payload(&wrb);
be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
- OPCODE_COMMON_MANAGE_FAT, sizeof(*req), wrb,
- NULL);
+ OPCODE_COMMON_MANAGE_FAT, sizeof(*req),
+ &wrb, NULL);
req->fat_operation = cpu_to_le32(QUERY_FAT);
- status = be_mcc_notify_wait(adapter);
+ status = be_cmd_notify_wait(adapter, &wrb);
if (!status) {
- struct be_cmd_resp_get_fat *resp = embedded_payload(wrb);
+ struct be_cmd_resp_get_fat *resp = embedded_payload(&wrb);
- if (log_size && resp->log_size)
- *log_size = le32_to_cpu(resp->log_size) -
+ if (dump_size && resp->log_size)
+ *dump_size = le32_to_cpu(resp->log_size) -
sizeof(u32);
}
-err:
- spin_unlock_bh(&adapter->mcc_lock);
return status;
}
-int be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
+int be_cmd_get_fat_dump(struct be_adapter *adapter, u32 buf_len, void *buf)
{
struct be_dma_mem get_fat_cmd;
struct be_mcc_wrb *wrb;
struct be_cmd_req_get_fat *req;
u32 offset = 0, total_size, buf_size,
log_offset = sizeof(u32), payload_len;
- int status = 0;
+ int status;
if (buf_len == 0)
- return -EIO;
+ return 0;
total_size = buf_len;
@@ -1763,11 +1753,8 @@ int be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
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");
+ if (!get_fat_cmd.va)
return -ENOMEM;
- }
spin_lock_bh(&adapter->mcc_lock);
@@ -2291,10 +2278,11 @@ err:
return status;
}
-int lancer_cmd_write_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
- u32 data_size, u32 data_offset,
- const char *obj_name, u32 *data_written,
- u8 *change_status, u8 *addn_status)
+static int lancer_cmd_write_object(struct be_adapter *adapter,
+ struct be_dma_mem *cmd, u32 data_size,
+ u32 data_offset, const char *obj_name,
+ u32 *data_written, u8 *change_status,
+ u8 *addn_status)
{
struct be_mcc_wrb *wrb;
struct lancer_cmd_req_write_object *req;
@@ -2410,7 +2398,8 @@ int be_cmd_query_sfp_info(struct be_adapter *adapter)
return status;
}
-int lancer_cmd_delete_object(struct be_adapter *adapter, const char *obj_name)
+static int lancer_cmd_delete_object(struct be_adapter *adapter,
+ const char *obj_name)
{
struct lancer_cmd_req_delete_object *req;
struct be_mcc_wrb *wrb;
@@ -2485,9 +2474,9 @@ err_unlock:
return status;
}
-int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd,
- u32 flash_type, u32 flash_opcode, u32 img_offset,
- u32 buf_size)
+static int be_cmd_write_flashrom(struct be_adapter *adapter,
+ struct be_dma_mem *cmd, u32 flash_type,
+ u32 flash_opcode, u32 img_offset, u32 buf_size)
{
struct be_mcc_wrb *wrb;
struct be_cmd_write_flashrom *req;
@@ -2533,8 +2522,8 @@ err_unlock:
return status;
}
-int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc,
- u16 img_optype, u32 img_offset, u32 crc_offset)
+static int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc,
+ u16 img_optype, u32 img_offset, u32 crc_offset)
{
struct be_cmd_read_flash_crc *req;
struct be_mcc_wrb *wrb;
@@ -2571,6 +2560,579 @@ err:
return status;
}
+static char flash_cookie[2][16] = {"*** SE FLAS", "H DIRECTORY *** "};
+
+static bool phy_flashing_required(struct be_adapter *adapter)
+{
+ return (adapter->phy.phy_type == PHY_TYPE_TN_8022 &&
+ adapter->phy.interface_type == PHY_TYPE_BASET_10GB);
+}
+
+static bool is_comp_in_ufi(struct be_adapter *adapter,
+ struct flash_section_info *fsec, int type)
+{
+ int i = 0, img_type = 0;
+ struct flash_section_info_g2 *fsec_g2 = NULL;
+
+ if (BE2_chip(adapter))
+ fsec_g2 = (struct flash_section_info_g2 *)fsec;
+
+ for (i = 0; i < MAX_FLASH_COMP; i++) {
+ if (fsec_g2)
+ img_type = le32_to_cpu(fsec_g2->fsec_entry[i].type);
+ else
+ img_type = le32_to_cpu(fsec->fsec_entry[i].type);
+
+ if (img_type == type)
+ return true;
+ }
+ return false;
+}
+
+static struct flash_section_info *get_fsec_info(struct be_adapter *adapter,
+ int header_size,
+ const struct firmware *fw)
+{
+ struct flash_section_info *fsec = NULL;
+ const u8 *p = fw->data;
+
+ p += header_size;
+ while (p < (fw->data + fw->size)) {
+ fsec = (struct flash_section_info *)p;
+ if (!memcmp(flash_cookie, fsec->cookie, sizeof(flash_cookie)))
+ return fsec;
+ p += 32;
+ }
+ return NULL;
+}
+
+static int be_check_flash_crc(struct be_adapter *adapter, const u8 *p,
+ u32 img_offset, u32 img_size, int hdr_size,
+ u16 img_optype, bool *crc_match)
+{
+ u32 crc_offset;
+ int status;
+ u8 crc[4];
+
+ status = be_cmd_get_flash_crc(adapter, crc, img_optype, img_offset,
+ img_size - 4);
+ if (status)
+ return status;
+
+ crc_offset = hdr_size + img_offset + img_size - 4;
+
+ /* Skip flashing, if crc of flashed region matches */
+ if (!memcmp(crc, p + crc_offset, 4))
+ *crc_match = true;
+ else
+ *crc_match = false;
+
+ return status;
+}
+
+static int be_flash(struct be_adapter *adapter, const u8 *img,
+ struct be_dma_mem *flash_cmd, int optype, int img_size,
+ u32 img_offset)
+{
+ u32 flash_op, num_bytes, total_bytes = img_size, bytes_sent = 0;
+ struct be_cmd_write_flashrom *req = flash_cmd->va;
+ int status;
+
+ while (total_bytes) {
+ num_bytes = min_t(u32, 32 * 1024, total_bytes);
+
+ total_bytes -= num_bytes;
+
+ if (!total_bytes) {
+ if (optype == OPTYPE_PHY_FW)
+ flash_op = FLASHROM_OPER_PHY_FLASH;
+ else
+ flash_op = FLASHROM_OPER_FLASH;
+ } else {
+ if (optype == OPTYPE_PHY_FW)
+ flash_op = FLASHROM_OPER_PHY_SAVE;
+ else
+ flash_op = FLASHROM_OPER_SAVE;
+ }
+
+ memcpy(req->data_buf, img, num_bytes);
+ img += num_bytes;
+ status = be_cmd_write_flashrom(adapter, flash_cmd, optype,
+ flash_op, img_offset +
+ bytes_sent, num_bytes);
+ if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST &&
+ optype == OPTYPE_PHY_FW)
+ break;
+ else if (status)
+ return status;
+
+ bytes_sent += num_bytes;
+ }
+ return 0;
+}
+
+/* For BE2, BE3 and BE3-R */
+static int be_flash_BEx(struct be_adapter *adapter,
+ const struct firmware *fw,
+ struct be_dma_mem *flash_cmd, int num_of_images)
+{
+ int img_hdrs_size = (num_of_images * sizeof(struct image_hdr));
+ struct device *dev = &adapter->pdev->dev;
+ struct flash_section_info *fsec = NULL;
+ int status, i, filehdr_size, num_comp;
+ const struct flash_comp *pflashcomp;
+ bool crc_match;
+ const u8 *p;
+
+ struct flash_comp gen3_flash_types[] = {
+ { BE3_ISCSI_PRIMARY_IMAGE_START, OPTYPE_ISCSI_ACTIVE,
+ BE3_COMP_MAX_SIZE, IMAGE_FIRMWARE_ISCSI},
+ { BE3_REDBOOT_START, OPTYPE_REDBOOT,
+ BE3_REDBOOT_COMP_MAX_SIZE, IMAGE_BOOT_CODE},
+ { BE3_ISCSI_BIOS_START, OPTYPE_BIOS,
+ BE3_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_ISCSI},
+ { BE3_PXE_BIOS_START, OPTYPE_PXE_BIOS,
+ BE3_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_PXE},
+ { BE3_FCOE_BIOS_START, OPTYPE_FCOE_BIOS,
+ BE3_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_FCOE},
+ { BE3_ISCSI_BACKUP_IMAGE_START, OPTYPE_ISCSI_BACKUP,
+ BE3_COMP_MAX_SIZE, IMAGE_FIRMWARE_BACKUP_ISCSI},
+ { BE3_FCOE_PRIMARY_IMAGE_START, OPTYPE_FCOE_FW_ACTIVE,
+ BE3_COMP_MAX_SIZE, IMAGE_FIRMWARE_FCOE},
+ { BE3_FCOE_BACKUP_IMAGE_START, OPTYPE_FCOE_FW_BACKUP,
+ BE3_COMP_MAX_SIZE, IMAGE_FIRMWARE_BACKUP_FCOE},
+ { BE3_NCSI_START, OPTYPE_NCSI_FW,
+ BE3_NCSI_COMP_MAX_SIZE, IMAGE_NCSI},
+ { BE3_PHY_FW_START, OPTYPE_PHY_FW,
+ BE3_PHY_FW_COMP_MAX_SIZE, IMAGE_FIRMWARE_PHY}
+ };
+
+ struct flash_comp gen2_flash_types[] = {
+ { BE2_ISCSI_PRIMARY_IMAGE_START, OPTYPE_ISCSI_ACTIVE,
+ BE2_COMP_MAX_SIZE, IMAGE_FIRMWARE_ISCSI},
+ { BE2_REDBOOT_START, OPTYPE_REDBOOT,
+ BE2_REDBOOT_COMP_MAX_SIZE, IMAGE_BOOT_CODE},
+ { BE2_ISCSI_BIOS_START, OPTYPE_BIOS,
+ BE2_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_ISCSI},
+ { BE2_PXE_BIOS_START, OPTYPE_PXE_BIOS,
+ BE2_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_PXE},
+ { BE2_FCOE_BIOS_START, OPTYPE_FCOE_BIOS,
+ BE2_BIOS_COMP_MAX_SIZE, IMAGE_OPTION_ROM_FCOE},
+ { BE2_ISCSI_BACKUP_IMAGE_START, OPTYPE_ISCSI_BACKUP,
+ BE2_COMP_MAX_SIZE, IMAGE_FIRMWARE_BACKUP_ISCSI},
+ { BE2_FCOE_PRIMARY_IMAGE_START, OPTYPE_FCOE_FW_ACTIVE,
+ BE2_COMP_MAX_SIZE, IMAGE_FIRMWARE_FCOE},
+ { BE2_FCOE_BACKUP_IMAGE_START, OPTYPE_FCOE_FW_BACKUP,
+ BE2_COMP_MAX_SIZE, IMAGE_FIRMWARE_BACKUP_FCOE}
+ };
+
+ if (BE3_chip(adapter)) {
+ pflashcomp = gen3_flash_types;
+ filehdr_size = sizeof(struct flash_file_hdr_g3);
+ num_comp = ARRAY_SIZE(gen3_flash_types);
+ } else {
+ pflashcomp = gen2_flash_types;
+ filehdr_size = sizeof(struct flash_file_hdr_g2);
+ num_comp = ARRAY_SIZE(gen2_flash_types);
+ img_hdrs_size = 0;
+ }
+
+ /* Get flash section info*/
+ fsec = get_fsec_info(adapter, filehdr_size + img_hdrs_size, fw);
+ if (!fsec) {
+ dev_err(dev, "Invalid Cookie. FW image may be corrupted\n");
+ return -1;
+ }
+ for (i = 0; i < num_comp; i++) {
+ if (!is_comp_in_ufi(adapter, fsec, pflashcomp[i].img_type))
+ continue;
+
+ if ((pflashcomp[i].optype == OPTYPE_NCSI_FW) &&
+ memcmp(adapter->fw_ver, "3.102.148.0", 11) < 0)
+ continue;
+
+ if (pflashcomp[i].optype == OPTYPE_PHY_FW &&
+ !phy_flashing_required(adapter))
+ continue;
+
+ if (pflashcomp[i].optype == OPTYPE_REDBOOT) {
+ status = be_check_flash_crc(adapter, fw->data,
+ pflashcomp[i].offset,
+ pflashcomp[i].size,
+ filehdr_size +
+ img_hdrs_size,
+ OPTYPE_REDBOOT, &crc_match);
+ if (status) {
+ dev_err(dev,
+ "Could not get CRC for 0x%x region\n",
+ pflashcomp[i].optype);
+ continue;
+ }
+
+ if (crc_match)
+ continue;
+ }
+
+ p = fw->data + filehdr_size + pflashcomp[i].offset +
+ img_hdrs_size;
+ if (p + pflashcomp[i].size > fw->data + fw->size)
+ return -1;
+
+ status = be_flash(adapter, p, flash_cmd, pflashcomp[i].optype,
+ pflashcomp[i].size, 0);
+ if (status) {
+ dev_err(dev, "Flashing section type 0x%x failed\n",
+ pflashcomp[i].img_type);
+ return status;
+ }
+ }
+ return 0;
+}
+
+static u16 be_get_img_optype(struct flash_section_entry fsec_entry)
+{
+ u32 img_type = le32_to_cpu(fsec_entry.type);
+ u16 img_optype = le16_to_cpu(fsec_entry.optype);
+
+ if (img_optype != 0xFFFF)
+ return img_optype;
+
+ switch (img_type) {
+ case IMAGE_FIRMWARE_ISCSI:
+ img_optype = OPTYPE_ISCSI_ACTIVE;
+ break;
+ case IMAGE_BOOT_CODE:
+ img_optype = OPTYPE_REDBOOT;
+ break;
+ case IMAGE_OPTION_ROM_ISCSI:
+ img_optype = OPTYPE_BIOS;
+ break;
+ case IMAGE_OPTION_ROM_PXE:
+ img_optype = OPTYPE_PXE_BIOS;
+ break;
+ case IMAGE_OPTION_ROM_FCOE:
+ img_optype = OPTYPE_FCOE_BIOS;
+ break;
+ case IMAGE_FIRMWARE_BACKUP_ISCSI:
+ img_optype = OPTYPE_ISCSI_BACKUP;
+ break;
+ case IMAGE_NCSI:
+ img_optype = OPTYPE_NCSI_FW;
+ break;
+ case IMAGE_FLASHISM_JUMPVECTOR:
+ img_optype = OPTYPE_FLASHISM_JUMPVECTOR;
+ break;
+ case IMAGE_FIRMWARE_PHY:
+ img_optype = OPTYPE_SH_PHY_FW;
+ break;
+ case IMAGE_REDBOOT_DIR:
+ img_optype = OPTYPE_REDBOOT_DIR;
+ break;
+ case IMAGE_REDBOOT_CONFIG:
+ img_optype = OPTYPE_REDBOOT_CONFIG;
+ break;
+ case IMAGE_UFI_DIR:
+ img_optype = OPTYPE_UFI_DIR;
+ break;
+ default:
+ break;
+ }
+
+ return img_optype;
+}
+
+static int be_flash_skyhawk(struct be_adapter *adapter,
+ const struct firmware *fw,
+ struct be_dma_mem *flash_cmd, int num_of_images)
+{
+ int img_hdrs_size = num_of_images * sizeof(struct image_hdr);
+ bool crc_match, old_fw_img, flash_offset_support = true;
+ struct device *dev = &adapter->pdev->dev;
+ struct flash_section_info *fsec = NULL;
+ u32 img_offset, img_size, img_type;
+ u16 img_optype, flash_optype;
+ int status, i, filehdr_size;
+ const u8 *p;
+
+ filehdr_size = sizeof(struct flash_file_hdr_g3);
+ fsec = get_fsec_info(adapter, filehdr_size + img_hdrs_size, fw);
+ if (!fsec) {
+ dev_err(dev, "Invalid Cookie. FW image may be corrupted\n");
+ return -EINVAL;
+ }
+
+retry_flash:
+ for (i = 0; i < le32_to_cpu(fsec->fsec_hdr.num_images); i++) {
+ img_offset = le32_to_cpu(fsec->fsec_entry[i].offset);
+ img_size = le32_to_cpu(fsec->fsec_entry[i].pad_size);
+ img_type = le32_to_cpu(fsec->fsec_entry[i].type);
+ img_optype = be_get_img_optype(fsec->fsec_entry[i]);
+ old_fw_img = fsec->fsec_entry[i].optype == 0xFFFF;
+
+ if (img_optype == 0xFFFF)
+ continue;
+
+ if (flash_offset_support)
+ flash_optype = OPTYPE_OFFSET_SPECIFIED;
+ else
+ flash_optype = img_optype;
+
+ /* Don't bother verifying CRC if an old FW image is being
+ * flashed
+ */
+ if (old_fw_img)
+ goto flash;
+
+ status = be_check_flash_crc(adapter, fw->data, img_offset,
+ img_size, filehdr_size +
+ img_hdrs_size, flash_optype,
+ &crc_match);
+ if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST ||
+ base_status(status) == MCC_STATUS_ILLEGAL_FIELD) {
+ /* The current FW image on the card does not support
+ * OFFSET based flashing. Retry using older mechanism
+ * of OPTYPE based flashing
+ */
+ if (flash_optype == OPTYPE_OFFSET_SPECIFIED) {
+ flash_offset_support = false;
+ goto retry_flash;
+ }
+
+ /* The current FW image on the card does not recognize
+ * the new FLASH op_type. The FW download is partially
+ * complete. Reboot the server now to enable FW image
+ * to recognize the new FLASH op_type. To complete the
+ * remaining process, download the same FW again after
+ * the reboot.
+ */
+ dev_err(dev, "Flash incomplete. Reset the server\n");
+ dev_err(dev, "Download FW image again after reset\n");
+ return -EAGAIN;
+ } else if (status) {
+ dev_err(dev, "Could not get CRC for 0x%x region\n",
+ img_optype);
+ return -EFAULT;
+ }
+
+ if (crc_match)
+ continue;
+
+flash:
+ p = fw->data + filehdr_size + img_offset + img_hdrs_size;
+ if (p + img_size > fw->data + fw->size)
+ return -1;
+
+ status = be_flash(adapter, p, flash_cmd, flash_optype, img_size,
+ img_offset);
+
+ /* The current FW image on the card does not support OFFSET
+ * based flashing. Retry using older mechanism of OPTYPE based
+ * flashing
+ */
+ if (base_status(status) == MCC_STATUS_ILLEGAL_FIELD &&
+ flash_optype == OPTYPE_OFFSET_SPECIFIED) {
+ flash_offset_support = false;
+ goto retry_flash;
+ }
+
+ /* For old FW images ignore ILLEGAL_FIELD error or errors on
+ * UFI_DIR region
+ */
+ if (old_fw_img &&
+ (base_status(status) == MCC_STATUS_ILLEGAL_FIELD ||
+ (img_optype == OPTYPE_UFI_DIR &&
+ base_status(status) == MCC_STATUS_FAILED))) {
+ continue;
+ } else if (status) {
+ dev_err(dev, "Flashing section type 0x%x failed\n",
+ img_type);
+
+ switch (addl_status(status)) {
+ case MCC_ADDL_STATUS_MISSING_SIGNATURE:
+ dev_err(dev,
+ "Digital signature missing in FW\n");
+ return -EINVAL;
+ case MCC_ADDL_STATUS_INVALID_SIGNATURE:
+ dev_err(dev,
+ "Invalid digital signature in FW\n");
+ return -EINVAL;
+ default:
+ return -EFAULT;
+ }
+ }
+ }
+ return 0;
+}
+
+int lancer_fw_download(struct be_adapter *adapter,
+ const struct firmware *fw)
+{
+ struct device *dev = &adapter->pdev->dev;
+ struct be_dma_mem flash_cmd;
+ const u8 *data_ptr = NULL;
+ u8 *dest_image_ptr = NULL;
+ size_t image_size = 0;
+ u32 chunk_size = 0;
+ u32 data_written = 0;
+ u32 offset = 0;
+ int status = 0;
+ u8 add_status = 0;
+ u8 change_status;
+
+ if (!IS_ALIGNED(fw->size, sizeof(u32))) {
+ dev_err(dev, "FW image size should be multiple of 4\n");
+ return -EINVAL;
+ }
+
+ flash_cmd.size = sizeof(struct lancer_cmd_req_write_object)
+ + LANCER_FW_DOWNLOAD_CHUNK;
+ flash_cmd.va = dma_zalloc_coherent(dev, flash_cmd.size,
+ &flash_cmd.dma, GFP_KERNEL);
+ if (!flash_cmd.va)
+ return -ENOMEM;
+
+ dest_image_ptr = flash_cmd.va +
+ sizeof(struct lancer_cmd_req_write_object);
+ image_size = fw->size;
+ data_ptr = fw->data;
+
+ while (image_size) {
+ chunk_size = min_t(u32, image_size, LANCER_FW_DOWNLOAD_CHUNK);
+
+ /* Copy the image chunk content. */
+ memcpy(dest_image_ptr, data_ptr, chunk_size);
+
+ status = lancer_cmd_write_object(adapter, &flash_cmd,
+ chunk_size, offset,
+ LANCER_FW_DOWNLOAD_LOCATION,
+ &data_written, &change_status,
+ &add_status);
+ if (status)
+ break;
+
+ offset += data_written;
+ data_ptr += data_written;
+ image_size -= data_written;
+ }
+
+ if (!status) {
+ /* Commit the FW written */
+ status = lancer_cmd_write_object(adapter, &flash_cmd,
+ 0, offset,
+ LANCER_FW_DOWNLOAD_LOCATION,
+ &data_written, &change_status,
+ &add_status);
+ }
+
+ dma_free_coherent(dev, flash_cmd.size, flash_cmd.va, flash_cmd.dma);
+ if (status) {
+ dev_err(dev, "Firmware load error\n");
+ return be_cmd_status(status);
+ }
+
+ dev_info(dev, "Firmware flashed successfully\n");
+
+ if (change_status == LANCER_FW_RESET_NEEDED) {
+ dev_info(dev, "Resetting adapter to activate new FW\n");
+ status = lancer_physdev_ctrl(adapter,
+ PHYSDEV_CONTROL_FW_RESET_MASK);
+ if (status) {
+ dev_err(dev, "Adapter busy, could not reset FW\n");
+ dev_err(dev, "Reboot server to activate new FW\n");
+ }
+ } else if (change_status != LANCER_NO_RESET_NEEDED) {
+ dev_info(dev, "Reboot server to activate new FW\n");
+ }
+
+ return 0;
+}
+
+/* Check if the flash image file is compatible with the adapter that
+ * is being flashed.
+ */
+static bool be_check_ufi_compatibility(struct be_adapter *adapter,
+ struct flash_file_hdr_g3 *fhdr)
+{
+ if (!fhdr) {
+ dev_err(&adapter->pdev->dev, "Invalid FW UFI file");
+ return false;
+ }
+
+ /* First letter of the build version is used to identify
+ * which chip this image file is meant for.
+ */
+ switch (fhdr->build[0]) {
+ case BLD_STR_UFI_TYPE_SH:
+ if (!skyhawk_chip(adapter))
+ return false;
+ break;
+ case BLD_STR_UFI_TYPE_BE3:
+ if (!BE3_chip(adapter))
+ return false;
+ break;
+ case BLD_STR_UFI_TYPE_BE2:
+ if (!BE2_chip(adapter))
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ /* In BE3 FW images the "asic_type_rev" field doesn't track the
+ * asic_rev of the chips it is compatible with.
+ * When asic_type_rev is 0 the image is compatible only with
+ * pre-BE3-R chips (asic_rev < 0x10)
+ */
+ if (BEx_chip(adapter) && fhdr->asic_type_rev == 0)
+ return adapter->asic_rev < 0x10;
+ else
+ return (fhdr->asic_type_rev >= adapter->asic_rev);
+}
+
+int be_fw_download(struct be_adapter *adapter, const struct firmware *fw)
+{
+ struct device *dev = &adapter->pdev->dev;
+ struct flash_file_hdr_g3 *fhdr3;
+ struct image_hdr *img_hdr_ptr;
+ int status = 0, i, num_imgs;
+ struct be_dma_mem flash_cmd;
+
+ fhdr3 = (struct flash_file_hdr_g3 *)fw->data;
+ if (!be_check_ufi_compatibility(adapter, fhdr3)) {
+ dev_err(dev, "Flash image is not compatible with adapter\n");
+ return -EINVAL;
+ }
+
+ flash_cmd.size = sizeof(struct be_cmd_write_flashrom);
+ flash_cmd.va = dma_zalloc_coherent(dev, flash_cmd.size, &flash_cmd.dma,
+ GFP_KERNEL);
+ if (!flash_cmd.va)
+ return -ENOMEM;
+
+ num_imgs = le32_to_cpu(fhdr3->num_imgs);
+ for (i = 0; i < num_imgs; i++) {
+ img_hdr_ptr = (struct image_hdr *)(fw->data +
+ (sizeof(struct flash_file_hdr_g3) +
+ i * sizeof(struct image_hdr)));
+ if (!BE2_chip(adapter) &&
+ le32_to_cpu(img_hdr_ptr->imageid) != 1)
+ continue;
+
+ if (skyhawk_chip(adapter))
+ status = be_flash_skyhawk(adapter, fw, &flash_cmd,
+ num_imgs);
+ else
+ status = be_flash_BEx(adapter, fw, &flash_cmd,
+ num_imgs);
+ }
+
+ dma_free_coherent(dev, flash_cmd.size, flash_cmd.va, flash_cmd.dma);
+ if (!status)
+ dev_info(dev, "Firmware flashed successfully\n");
+
+ return status;
+}
+
int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac,
struct be_dma_mem *nonemb_cmd)
{
@@ -2892,7 +3454,6 @@ int be_cmd_get_cntl_attributes(struct be_adapter *adapter)
if (!status) {
attribs = attribs_cmd.va + sizeof(struct be_cmd_resp_hdr);
adapter->hba_port_num = attribs->hba_attribs.phy_port;
- adapter->pci_func_num = attribs->pci_func_num;
serial_num = attribs->hba_attribs.controller_serial_number;
for (i = 0; i < CNTL_SERIAL_NUM_WORDS; i++)
adapter->serial_num[i] = le32_to_cpu(serial_num[i]) &
@@ -3575,14 +4136,16 @@ int be_cmd_query_port_name(struct be_adapter *adapter)
return status;
}
-/* Descriptor type */
-enum {
- FUNC_DESC = 1,
- VFT_DESC = 2
-};
-
+/* When more than 1 NIC descriptor is present in the descriptor list,
+ * the caller must specify the pf_num to obtain the NIC descriptor
+ * corresponding to its pci function.
+ * get_vft must be true when the caller wants the VF-template desc of the
+ * PF-pool.
+ * The pf_num should be set to PF_NUM_IGNORE when the caller knows
+ * that only it's NIC descriptor is present in the descriptor list.
+ */
static struct be_nic_res_desc *be_get_nic_desc(u8 *buf, u32 desc_count,
- int desc_type)
+ bool get_vft, u8 pf_num)
{
struct be_res_desc_hdr *hdr = (struct be_res_desc_hdr *)buf;
struct be_nic_res_desc *nic;
@@ -3592,40 +4155,42 @@ static struct be_nic_res_desc *be_get_nic_desc(u8 *buf, u32 desc_count,
if (hdr->desc_type == NIC_RESOURCE_DESC_TYPE_V0 ||
hdr->desc_type == NIC_RESOURCE_DESC_TYPE_V1) {
nic = (struct be_nic_res_desc *)hdr;
- if (desc_type == FUNC_DESC ||
- (desc_type == VFT_DESC &&
- nic->flags & (1 << VFT_SHIFT)))
+
+ if ((pf_num == PF_NUM_IGNORE ||
+ nic->pf_num == pf_num) &&
+ (!get_vft || nic->flags & BIT(VFT_SHIFT)))
return nic;
}
-
hdr->desc_len = hdr->desc_len ? : RESOURCE_DESC_SIZE_V0;
hdr = (void *)hdr + hdr->desc_len;
}
return NULL;
}
-static struct be_nic_res_desc *be_get_vft_desc(u8 *buf, u32 desc_count)
+static struct be_nic_res_desc *be_get_vft_desc(u8 *buf, u32 desc_count,
+ u8 pf_num)
{
- return be_get_nic_desc(buf, desc_count, VFT_DESC);
+ return be_get_nic_desc(buf, desc_count, true, pf_num);
}
-static struct be_nic_res_desc *be_get_func_nic_desc(u8 *buf, u32 desc_count)
+static struct be_nic_res_desc *be_get_func_nic_desc(u8 *buf, u32 desc_count,
+ u8 pf_num)
{
- return be_get_nic_desc(buf, desc_count, FUNC_DESC);
+ return be_get_nic_desc(buf, desc_count, false, pf_num);
}
-static struct be_pcie_res_desc *be_get_pcie_desc(u8 devfn, u8 *buf,
- u32 desc_count)
+static struct be_pcie_res_desc *be_get_pcie_desc(u8 *buf, u32 desc_count,
+ u8 pf_num)
{
struct be_res_desc_hdr *hdr = (struct be_res_desc_hdr *)buf;
struct be_pcie_res_desc *pcie;
int i;
for (i = 0; i < desc_count; i++) {
- if ((hdr->desc_type == PCIE_RESOURCE_DESC_TYPE_V0 ||
- hdr->desc_type == PCIE_RESOURCE_DESC_TYPE_V1)) {
- pcie = (struct be_pcie_res_desc *)hdr;
- if (pcie->pf_num == devfn)
+ if (hdr->desc_type == PCIE_RESOURCE_DESC_TYPE_V0 ||
+ hdr->desc_type == PCIE_RESOURCE_DESC_TYPE_V1) {
+ pcie = (struct be_pcie_res_desc *)hdr;
+ if (pcie->pf_num == pf_num)
return pcie;
}
@@ -3710,13 +4275,23 @@ int be_cmd_get_func_config(struct be_adapter *adapter, struct be_resources *res)
u32 desc_count = le32_to_cpu(resp->desc_count);
struct be_nic_res_desc *desc;
- desc = be_get_func_nic_desc(resp->func_param, desc_count);
+ /* GET_FUNC_CONFIG returns resource descriptors of the
+ * current function only. So, pf_num should be set to
+ * PF_NUM_IGNORE.
+ */
+ desc = be_get_func_nic_desc(resp->func_param, desc_count,
+ PF_NUM_IGNORE);
if (!desc) {
status = -EINVAL;
goto err;
}
- adapter->pf_number = desc->pf_num;
- be_copy_nic_desc(res, desc);
+
+ /* Store pf_num & vf_num for later use in GET_PROFILE_CONFIG */
+ adapter->pf_num = desc->pf_num;
+ adapter->vf_num = desc->vf_num;
+
+ if (res)
+ be_copy_nic_desc(res, desc);
}
err:
mutex_unlock(&adapter->mbox_lock);
@@ -3726,10 +4301,7 @@ err:
return status;
}
-/* Will use MBOX only if MCCQ has not been created
- * non-zero domain => a PF is querying this on behalf of a VF
- * zero domain => a PF or a VF is querying this for itself
- */
+/* Will use MBOX only if MCCQ has not been created */
int be_cmd_get_profile_config(struct be_adapter *adapter,
struct be_resources *res, u8 query, u8 domain)
{
@@ -3759,12 +4331,7 @@ int be_cmd_get_profile_config(struct be_adapter *adapter,
if (!lancer_chip(adapter))
req->hdr.version = 1;
req->type = ACTIVE_PROFILE_TYPE;
- /* When a function is querying profile information relating to
- * itself hdr.pf_number must be set to it's pci_func_num + 1
- */
req->hdr.domain = domain;
- if (domain == 0)
- req->hdr.pf_num = adapter->pci_func_num + 1;
/* When QUERY_MODIFIABLE_FIELDS_TYPE bit is set, cmd returns the
* descriptors with all bits set to "1" for the fields which can be
@@ -3780,8 +4347,8 @@ int be_cmd_get_profile_config(struct be_adapter *adapter,
resp = cmd.va;
desc_count = le16_to_cpu(resp->desc_count);
- pcie = be_get_pcie_desc(adapter->pdev->devfn, resp->func_param,
- desc_count);
+ pcie = be_get_pcie_desc(resp->func_param, desc_count,
+ adapter->pf_num);
if (pcie)
res->max_vfs = le16_to_cpu(pcie->num_vfs);
@@ -3789,11 +4356,13 @@ int be_cmd_get_profile_config(struct be_adapter *adapter,
if (port)
adapter->mc_type = port->mc_type;
- nic = be_get_func_nic_desc(resp->func_param, desc_count);
+ nic = be_get_func_nic_desc(resp->func_param, desc_count,
+ adapter->pf_num);
if (nic)
be_copy_nic_desc(res, nic);
- vf_res = be_get_vft_desc(resp->func_param, desc_count);
+ vf_res = be_get_vft_desc(resp->func_param, desc_count,
+ adapter->pf_num);
if (vf_res)
res->vf_if_cap_flags = vf_res->cap_flags;
err:
@@ -3883,7 +4452,7 @@ int be_cmd_config_qos(struct be_adapter *adapter, u32 max_rate, u16 link_speed,
return be_cmd_set_qos(adapter, max_rate / 10, domain);
be_reset_nic_desc(&nic_desc);
- nic_desc.pf_num = adapter->pf_number;
+ nic_desc.pf_num = adapter->pf_num;
nic_desc.vf_num = domain;
nic_desc.bw_min = 0;
if (lancer_chip(adapter)) {
@@ -4260,16 +4829,13 @@ err:
return status;
}
-int be_cmd_set_logical_link_config(struct be_adapter *adapter,
- int link_state, u8 domain)
+int __be_cmd_set_logical_link_config(struct be_adapter *adapter,
+ int link_state, int version, u8 domain)
{
struct be_mcc_wrb *wrb;
struct be_cmd_req_set_ll_link *req;
int status;
- if (BEx_chip(adapter) || lancer_chip(adapter))
- return -EOPNOTSUPP;
-
spin_lock_bh(&adapter->mcc_lock);
wrb = wrb_from_mccq(adapter);
@@ -4284,14 +4850,15 @@ int be_cmd_set_logical_link_config(struct be_adapter *adapter,
OPCODE_COMMON_SET_LOGICAL_LINK_CONFIG,
sizeof(*req), wrb, NULL);
- req->hdr.version = 1;
+ req->hdr.version = version;
req->hdr.domain = domain;
- if (link_state == IFLA_VF_LINK_STATE_ENABLE)
- req->link_config |= 1;
+ if (link_state == IFLA_VF_LINK_STATE_ENABLE ||
+ link_state == IFLA_VF_LINK_STATE_AUTO)
+ req->link_config |= PLINK_ENABLE;
if (link_state == IFLA_VF_LINK_STATE_AUTO)
- req->link_config |= 1 << PLINK_TRACK_SHIFT;
+ req->link_config |= PLINK_TRACK;
status = be_mcc_notify_wait(adapter);
err:
@@ -4299,6 +4866,25 @@ err:
return status;
}
+int be_cmd_set_logical_link_config(struct be_adapter *adapter,
+ int link_state, u8 domain)
+{
+ int status;
+
+ if (BEx_chip(adapter))
+ return -EOPNOTSUPP;
+
+ status = __be_cmd_set_logical_link_config(adapter, link_state,
+ 2, domain);
+
+ /* Version 2 of the command will not be recognized by older FW.
+ * On such a failure issue version 1 of the command.
+ */
+ if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST)
+ status = __be_cmd_set_logical_link_config(adapter, link_state,
+ 1, domain);
+ return status;
+}
int be_roce_mcc_cmd(void *netdev_handle, void *wrb_payload,
int wrb_payload_size, u16 *cmd_status, u16 *ext_status)
{
diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h
index 91155ea74f34..241819b36ca7 100644
--- a/drivers/net/ethernet/emulex/benet/be_cmds.h
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.h
@@ -66,7 +66,9 @@ enum mcc_addl_status {
MCC_ADDL_STATUS_INSUFFICIENT_RESOURCES = 0x16,
MCC_ADDL_STATUS_FLASH_IMAGE_CRC_MISMATCH = 0x4d,
MCC_ADDL_STATUS_TOO_MANY_INTERFACES = 0x4a,
- MCC_ADDL_STATUS_INSUFFICIENT_VLANS = 0xab
+ MCC_ADDL_STATUS_INSUFFICIENT_VLANS = 0xab,
+ MCC_ADDL_STATUS_INVALID_SIGNATURE = 0x56,
+ MCC_ADDL_STATUS_MISSING_SIGNATURE = 0x57
};
#define CQE_BASE_STATUS_MASK 0xFFFF
@@ -289,9 +291,7 @@ struct be_cmd_req_hdr {
u32 timeout; /* dword 1 */
u32 request_length; /* dword 2 */
u8 version; /* dword 3 */
- u8 rsvd1; /* dword 3 */
- u8 pf_num; /* dword 3 */
- u8 rsvd2; /* dword 3 */
+ u8 rsvd[3]; /* dword 3 */
};
#define RESP_HDR_INFO_OPCODE_SHIFT 0 /* bits 0 - 7 */
@@ -1207,68 +1207,85 @@ struct be_cmd_resp_get_beacon_state {
/* Flashrom related descriptors */
#define MAX_FLASH_COMP 32
-#define OPTYPE_ISCSI_ACTIVE 0
-#define OPTYPE_REDBOOT 1
-#define OPTYPE_BIOS 2
-#define OPTYPE_PXE_BIOS 3
-#define OPTYPE_OFFSET_SPECIFIED 7
-#define OPTYPE_FCOE_BIOS 8
-#define OPTYPE_ISCSI_BACKUP 9
-#define OPTYPE_FCOE_FW_ACTIVE 10
-#define OPTYPE_FCOE_FW_BACKUP 11
-#define OPTYPE_NCSI_FW 13
-#define OPTYPE_REDBOOT_DIR 18
-#define OPTYPE_REDBOOT_CONFIG 19
-#define OPTYPE_SH_PHY_FW 21
-#define OPTYPE_FLASHISM_JUMPVECTOR 22
-#define OPTYPE_UFI_DIR 23
-#define OPTYPE_PHY_FW 99
-
-#define FLASH_BIOS_IMAGE_MAX_SIZE_g2 262144 /* Max OPTION ROM image sz */
-#define FLASH_REDBOOT_IMAGE_MAX_SIZE_g2 262144 /* Max Redboot image sz */
-#define FLASH_IMAGE_MAX_SIZE_g2 1310720 /* Max firmware image size */
-
-#define FLASH_NCSI_IMAGE_MAX_SIZE_g3 262144
-#define FLASH_PHY_FW_IMAGE_MAX_SIZE_g3 262144
-#define FLASH_BIOS_IMAGE_MAX_SIZE_g3 524288 /* Max OPTION ROM image sz */
-#define FLASH_REDBOOT_IMAGE_MAX_SIZE_g3 1048576 /* Max Redboot image sz */
-#define FLASH_IMAGE_MAX_SIZE_g3 2097152 /* Max firmware image size */
-
-/* Offsets for components on Flash. */
-#define FLASH_REDBOOT_START_g2 0
-#define FLASH_FCoE_BIOS_START_g2 524288
-#define FLASH_iSCSI_PRIMARY_IMAGE_START_g2 1048576
-#define FLASH_iSCSI_BACKUP_IMAGE_START_g2 2359296
-#define FLASH_FCoE_PRIMARY_IMAGE_START_g2 3670016
-#define FLASH_FCoE_BACKUP_IMAGE_START_g2 4980736
-#define FLASH_iSCSI_BIOS_START_g2 7340032
-#define FLASH_PXE_BIOS_START_g2 7864320
-
-#define FLASH_REDBOOT_START_g3 262144
-#define FLASH_PHY_FW_START_g3 1310720
-#define FLASH_iSCSI_PRIMARY_IMAGE_START_g3 2097152
-#define FLASH_iSCSI_BACKUP_IMAGE_START_g3 4194304
-#define FLASH_FCoE_PRIMARY_IMAGE_START_g3 6291456
-#define FLASH_FCoE_BACKUP_IMAGE_START_g3 8388608
-#define FLASH_iSCSI_BIOS_START_g3 12582912
-#define FLASH_PXE_BIOS_START_g3 13107200
-#define FLASH_FCoE_BIOS_START_g3 13631488
-#define FLASH_NCSI_START_g3 15990784
-
-#define IMAGE_NCSI 16
-#define IMAGE_OPTION_ROM_PXE 32
-#define IMAGE_OPTION_ROM_FCoE 33
-#define IMAGE_OPTION_ROM_ISCSI 34
-#define IMAGE_FLASHISM_JUMPVECTOR 48
-#define IMAGE_FIRMWARE_iSCSI 160
-#define IMAGE_FIRMWARE_FCoE 162
-#define IMAGE_FIRMWARE_BACKUP_iSCSI 176
-#define IMAGE_FIRMWARE_BACKUP_FCoE 178
-#define IMAGE_FIRMWARE_PHY 192
-#define IMAGE_REDBOOT_DIR 208
-#define IMAGE_REDBOOT_CONFIG 209
-#define IMAGE_UFI_DIR 210
-#define IMAGE_BOOT_CODE 224
+/* Optypes of each component in the UFI */
+enum {
+ OPTYPE_ISCSI_ACTIVE = 0,
+ OPTYPE_REDBOOT = 1,
+ OPTYPE_BIOS = 2,
+ OPTYPE_PXE_BIOS = 3,
+ OPTYPE_OFFSET_SPECIFIED = 7,
+ OPTYPE_FCOE_BIOS = 8,
+ OPTYPE_ISCSI_BACKUP = 9,
+ OPTYPE_FCOE_FW_ACTIVE = 10,
+ OPTYPE_FCOE_FW_BACKUP = 11,
+ OPTYPE_NCSI_FW = 13,
+ OPTYPE_REDBOOT_DIR = 18,
+ OPTYPE_REDBOOT_CONFIG = 19,
+ OPTYPE_SH_PHY_FW = 21,
+ OPTYPE_FLASHISM_JUMPVECTOR = 22,
+ OPTYPE_UFI_DIR = 23,
+ OPTYPE_PHY_FW = 99
+};
+
+/* Maximum sizes of components in BE2 FW UFI */
+enum {
+ BE2_BIOS_COMP_MAX_SIZE = 0x40000,
+ BE2_REDBOOT_COMP_MAX_SIZE = 0x40000,
+ BE2_COMP_MAX_SIZE = 0x140000
+};
+
+/* Maximum sizes of components in BE3 FW UFI */
+enum {
+ BE3_NCSI_COMP_MAX_SIZE = 0x40000,
+ BE3_PHY_FW_COMP_MAX_SIZE = 0x40000,
+ BE3_BIOS_COMP_MAX_SIZE = 0x80000,
+ BE3_REDBOOT_COMP_MAX_SIZE = 0x100000,
+ BE3_COMP_MAX_SIZE = 0x200000
+};
+
+/* Offsets for components in BE2 FW UFI */
+enum {
+ BE2_REDBOOT_START = 0x8000,
+ BE2_FCOE_BIOS_START = 0x80000,
+ BE2_ISCSI_PRIMARY_IMAGE_START = 0x100000,
+ BE2_ISCSI_BACKUP_IMAGE_START = 0x240000,
+ BE2_FCOE_PRIMARY_IMAGE_START = 0x380000,
+ BE2_FCOE_BACKUP_IMAGE_START = 0x4c0000,
+ BE2_ISCSI_BIOS_START = 0x700000,
+ BE2_PXE_BIOS_START = 0x780000
+};
+
+/* Offsets for components in BE3 FW UFI */
+enum {
+ BE3_REDBOOT_START = 0x40000,
+ BE3_PHY_FW_START = 0x140000,
+ BE3_ISCSI_PRIMARY_IMAGE_START = 0x200000,
+ BE3_ISCSI_BACKUP_IMAGE_START = 0x400000,
+ BE3_FCOE_PRIMARY_IMAGE_START = 0x600000,
+ BE3_FCOE_BACKUP_IMAGE_START = 0x800000,
+ BE3_ISCSI_BIOS_START = 0xc00000,
+ BE3_PXE_BIOS_START = 0xc80000,
+ BE3_FCOE_BIOS_START = 0xd00000,
+ BE3_NCSI_START = 0xf40000
+};
+
+/* Component entry types */
+enum {
+ IMAGE_NCSI = 0x10,
+ IMAGE_OPTION_ROM_PXE = 0x20,
+ IMAGE_OPTION_ROM_FCOE = 0x21,
+ IMAGE_OPTION_ROM_ISCSI = 0x22,
+ IMAGE_FLASHISM_JUMPVECTOR = 0x30,
+ IMAGE_FIRMWARE_ISCSI = 0xa0,
+ IMAGE_FIRMWARE_FCOE = 0xa2,
+ IMAGE_FIRMWARE_BACKUP_ISCSI = 0xb0,
+ IMAGE_FIRMWARE_BACKUP_FCOE = 0xb2,
+ IMAGE_FIRMWARE_PHY = 0xc0,
+ IMAGE_REDBOOT_DIR = 0xd0,
+ IMAGE_REDBOOT_CONFIG = 0xd1,
+ IMAGE_UFI_DIR = 0xd2,
+ IMAGE_BOOT_CODE = 0xe2
+};
struct controller_id {
u32 vendor;
@@ -1394,6 +1411,9 @@ struct be_cmd_read_flash_crc {
} __packed;
/**************** Lancer Firmware Flash ************/
+#define LANCER_FW_DOWNLOAD_CHUNK (32 * 1024)
+#define LANCER_FW_DOWNLOAD_LOCATION "/prg"
+
struct amap_lancer_write_obj_context {
u8 write_length[24];
u8 reserved1[7];
@@ -1654,11 +1674,7 @@ struct mgmt_hba_attribs {
struct mgmt_controller_attrib {
struct mgmt_hba_attribs hba_attribs;
- u32 rsvd0[2];
- u16 rsvd1;
- u8 pci_func_num;
- u8 rsvd2;
- u32 rsvd3[7];
+ u32 rsvd0[10];
} __packed;
struct be_cmd_req_cntl_attribs {
@@ -2083,6 +2099,7 @@ struct be_port_res_desc {
#define NV_TYPE_VXLAN 3
#define SOCVID_SHIFT 2 /* Strip outer vlan */
#define RCVID_SHIFT 4 /* Report vlan */
+#define PF_NUM_IGNORE 255
u8 nv_flags;
u8 rsvd2;
__le16 nv_port; /* vxlan/gre port */
@@ -2246,7 +2263,8 @@ struct be_cmd_resp_get_iface_list {
};
/*************** Set logical link ********************/
-#define PLINK_TRACK_SHIFT 8
+#define PLINK_ENABLE BIT(0)
+#define PLINK_TRACK BIT(8)
struct be_cmd_req_set_ll_link {
struct be_cmd_req_hdr hdr;
u32 link_config; /* Bit 0: UP_DOWN, Bit 9: PLINK */
@@ -2321,19 +2339,11 @@ int be_cmd_read_port_transceiver_data(struct be_adapter *adapter,
u8 page_num, u8 *data);
int be_cmd_query_cable_type(struct be_adapter *adapter);
int be_cmd_query_sfp_info(struct be_adapter *adapter);
-int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd,
- u32 flash_oper, u32 flash_opcode, u32 img_offset,
- u32 buf_size);
-int lancer_cmd_write_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
- u32 data_size, u32 data_offset,
- const char *obj_name, u32 *data_written,
- u8 *change_status, u8 *addn_status);
int lancer_cmd_read_object(struct be_adapter *adapter, struct be_dma_mem *cmd,
u32 data_size, u32 data_offset, const char *obj_name,
u32 *data_read, u32 *eof, u8 *addn_status);
-int lancer_cmd_delete_object(struct be_adapter *adapter, const char *obj_name);
-int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc,
- u16 img_optype, u32 img_offset, u32 crc_offset);
+int lancer_fw_download(struct be_adapter *adapter, const struct firmware *fw);
+int be_fw_download(struct be_adapter *adapter, const struct firmware *fw);
int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac,
struct be_dma_mem *nonemb_cmd);
int be_cmd_fw_init(struct be_adapter *adapter);
@@ -2355,9 +2365,9 @@ int be_cmd_config_qos(struct be_adapter *adapter, u32 max_rate,
void be_detect_error(struct be_adapter *adapter);
int be_cmd_get_die_temperature(struct be_adapter *adapter);
int be_cmd_get_cntl_attributes(struct be_adapter *adapter);
+int be_cmd_get_fat_dump_len(struct be_adapter *adapter, u32 *dump_size);
+int be_cmd_get_fat_dump(struct be_adapter *adapter, u32 buf_len, void *buf);
int be_cmd_req_native_mode(struct be_adapter *adapter);
-int be_cmd_get_reg_len(struct be_adapter *adapter, u32 *log_size);
-int be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf);
int be_cmd_get_fn_privileges(struct be_adapter *adapter, u32 *privilege,
u32 domain);
int be_cmd_set_fn_privileges(struct be_adapter *adapter, u32 privileges,
diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c
index 734f655c99c1..a19ac441336f 100644
--- a/drivers/net/ethernet/emulex/benet/be_ethtool.c
+++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c
@@ -241,17 +241,28 @@ static u32 lancer_cmd_get_file_len(struct be_adapter *adapter, u8 *file_name)
u32 data_read = 0, eof;
u8 addn_status;
struct be_dma_mem data_len_cmd;
- int status;
memset(&data_len_cmd, 0, sizeof(data_len_cmd));
/* data_offset and data_size should be 0 to get reg len */
- status = lancer_cmd_read_object(adapter, &data_len_cmd, 0, 0,
- file_name, &data_read, &eof,
- &addn_status);
+ lancer_cmd_read_object(adapter, &data_len_cmd, 0, 0, file_name,
+ &data_read, &eof, &addn_status);
return data_read;
}
+static int be_get_dump_len(struct be_adapter *adapter)
+{
+ u32 dump_size = 0;
+
+ if (lancer_chip(adapter))
+ dump_size = lancer_cmd_get_file_len(adapter,
+ LANCER_FW_DUMP_FILE);
+ else
+ dump_size = adapter->fat_dump_len;
+
+ return dump_size;
+}
+
static int lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
u32 buf_len, void *buf)
{
@@ -293,37 +304,18 @@ static int lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
return status;
}
-static int be_get_reg_len(struct net_device *netdev)
+static int be_read_dump_data(struct be_adapter *adapter, u32 dump_len,
+ void *buf)
{
- struct be_adapter *adapter = netdev_priv(netdev);
- u32 log_size = 0;
-
- if (!check_privilege(adapter, MAX_PRIVILEGES))
- return 0;
-
- if (be_physfn(adapter)) {
- if (lancer_chip(adapter))
- log_size = lancer_cmd_get_file_len(adapter,
- LANCER_FW_DUMP_FILE);
- else
- be_cmd_get_reg_len(adapter, &log_size);
- }
- return log_size;
-}
+ int status = 0;
-static void
-be_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *buf)
-{
- struct be_adapter *adapter = netdev_priv(netdev);
+ if (lancer_chip(adapter))
+ status = lancer_cmd_read_file(adapter, LANCER_FW_DUMP_FILE,
+ dump_len, buf);
+ else
+ status = be_cmd_get_fat_dump(adapter, dump_len, buf);
- if (be_physfn(adapter)) {
- memset(buf, 0, regs->len);
- if (lancer_chip(adapter))
- lancer_cmd_read_file(adapter, LANCER_FW_DUMP_FILE,
- regs->len, buf);
- else
- be_cmd_get_regs(adapter, regs->len, buf);
- }
+ return status;
}
static int be_get_coalesce(struct net_device *netdev,
@@ -916,6 +908,34 @@ static int be_do_flash(struct net_device *netdev, struct ethtool_flash *efl)
return be_load_fw(adapter, efl->data);
}
+static int
+be_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump)
+{
+ struct be_adapter *adapter = netdev_priv(netdev);
+
+ if (!check_privilege(adapter, MAX_PRIVILEGES))
+ return -EOPNOTSUPP;
+
+ dump->len = be_get_dump_len(adapter);
+ dump->version = 1;
+ dump->flag = 0x1; /* FW dump is enabled */
+ return 0;
+}
+
+static int
+be_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump,
+ void *buf)
+{
+ struct be_adapter *adapter = netdev_priv(netdev);
+ int status;
+
+ if (!check_privilege(adapter, MAX_PRIVILEGES))
+ return -EOPNOTSUPP;
+
+ status = be_read_dump_data(adapter, dump->len, buf);
+ return be_cmd_status(status);
+}
+
static int be_get_eeprom_len(struct net_device *netdev)
{
struct be_adapter *adapter = netdev_priv(netdev);
@@ -1313,8 +1333,6 @@ const struct ethtool_ops be_ethtool_ops = {
.set_msglevel = be_set_msg_level,
.get_sset_count = be_get_sset_count,
.get_ethtool_stats = be_get_ethtool_stats,
- .get_regs_len = be_get_reg_len,
- .get_regs = be_get_regs,
.flash_device = be_do_flash,
.self_test = be_self_test,
.get_rxnfc = be_get_rxnfc,
@@ -1323,6 +1341,8 @@ const struct ethtool_ops be_ethtool_ops = {
.get_rxfh_key_size = be_get_rxfh_key_size,
.get_rxfh = be_get_rxfh,
.set_rxfh = be_set_rxfh,
+ .get_dump_flag = be_get_dump_flag,
+ .get_dump_data = be_get_dump_data,
.get_channels = be_get_channels,
.set_channels = be_set_channels,
.get_module_info = be_get_module_info,
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index b6ad02909d6b..f99de3657ce3 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -729,7 +729,7 @@ static inline u16 be_get_tx_vlan_tag(struct be_adapter *adapter,
/* If vlan priority provided by OS is NOT in available bmap */
if (!(adapter->vlan_prio_bmap & (1 << vlan_prio)))
vlan_tag = (vlan_tag & ~VLAN_PRIO_MASK) |
- adapter->recommended_prio;
+ adapter->recommended_prio_bits;
return vlan_tag;
}
@@ -2184,7 +2184,6 @@ static void be_rx_compl_process_gro(struct be_rx_obj *rxo,
skb_set_hash(skb, rxcp->rss_hash, PKT_HASH_TYPE_L3);
skb->csum_level = rxcp->tunneled;
- skb_mark_napi_id(skb, napi);
if (rxcp->vlanf)
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rxcp->vlan_tag);
@@ -2631,7 +2630,6 @@ static int be_evt_queues_create(struct be_adapter *adapter)
eqo->affinity_mask);
netif_napi_add(adapter->netdev, &eqo->napi, be_poll,
BE_NAPI_WEIGHT);
- napi_hash_add(&eqo->napi);
}
return 0;
}
@@ -3299,8 +3297,10 @@ static int be_msix_register(struct be_adapter *adapter)
return 0;
err_msix:
- for (i--, eqo = &adapter->eq_obj[i]; i >= 0; i--, eqo--)
+ for (i--; i >= 0; i--) {
+ eqo = &adapter->eq_obj[i];
free_irq(be_msix_vec_get(adapter, eqo), eqo);
+ }
dev_warn(&adapter->pdev->dev, "MSIX Request IRQ failed - err %d\n",
status);
be_msix_disable(adapter);
@@ -3432,8 +3432,6 @@ static int be_close(struct net_device *netdev)
be_disable_if_filters(adapter);
- be_roce_dev_close(adapter);
-
if (adapter->flags & BE_FLAGS_NAPI_ENABLED) {
for_all_evt_queues(adapter, eqo, i) {
napi_disable(&eqo->napi);
@@ -3601,8 +3599,6 @@ static int be_open(struct net_device *netdev)
be_link_status_update(adapter, link_status);
netif_tx_start_all_queues(netdev);
- be_roce_dev_open(adapter);
-
#ifdef CONFIG_BE2NET_VXLAN
if (skyhawk_chip(adapter))
vxlan_get_rx_port(netdev);
@@ -4206,10 +4202,17 @@ static int be_get_config(struct be_adapter *adapter)
int status, level;
u16 profile_id;
+ status = be_cmd_get_cntl_attributes(adapter);
+ if (status)
+ return status;
+
status = be_cmd_query_fw_cfg(adapter);
if (status)
return status;
+ if (!lancer_chip(adapter) && be_physfn(adapter))
+ be_cmd_get_fat_dump_len(adapter, &adapter->fat_dump_len);
+
if (BEx_chip(adapter)) {
level = be_cmd_get_fw_log_level(adapter);
adapter->msg_enable =
@@ -4404,10 +4407,14 @@ static int be_setup(struct be_adapter *adapter)
if (!lancer_chip(adapter))
be_cmd_req_native_mode(adapter);
- /* Need to invoke this cmd first to get the PCI Function Number */
- status = be_cmd_get_cntl_attributes(adapter);
- if (status)
- return status;
+ /* invoke this cmd first to get pf_num and vf_num which are needed
+ * for issuing profile related cmds
+ */
+ if (!BEx_chip(adapter)) {
+ status = be_cmd_get_func_config(adapter, NULL);
+ if (status)
+ return status;
+ }
if (!BE2_chip(adapter) && be_physfn(adapter))
be_alloc_sriov_res(adapter);
@@ -4492,570 +4499,6 @@ static void be_netpoll(struct net_device *netdev)
}
#endif
-static char flash_cookie[2][16] = {"*** SE FLAS", "H DIRECTORY *** "};
-
-static bool phy_flashing_required(struct be_adapter *adapter)
-{
- return (adapter->phy.phy_type == PHY_TYPE_TN_8022 &&
- adapter->phy.interface_type == PHY_TYPE_BASET_10GB);
-}
-
-static bool is_comp_in_ufi(struct be_adapter *adapter,
- struct flash_section_info *fsec, int type)
-{
- int i = 0, img_type = 0;
- struct flash_section_info_g2 *fsec_g2 = NULL;
-
- if (BE2_chip(adapter))
- fsec_g2 = (struct flash_section_info_g2 *)fsec;
-
- for (i = 0; i < MAX_FLASH_COMP; i++) {
- if (fsec_g2)
- img_type = le32_to_cpu(fsec_g2->fsec_entry[i].type);
- else
- img_type = le32_to_cpu(fsec->fsec_entry[i].type);
-
- if (img_type == type)
- return true;
- }
- return false;
-
-}
-
-static struct flash_section_info *get_fsec_info(struct be_adapter *adapter,
- int header_size,
- const struct firmware *fw)
-{
- struct flash_section_info *fsec = NULL;
- const u8 *p = fw->data;
-
- p += header_size;
- while (p < (fw->data + fw->size)) {
- fsec = (struct flash_section_info *)p;
- if (!memcmp(flash_cookie, fsec->cookie, sizeof(flash_cookie)))
- return fsec;
- p += 32;
- }
- return NULL;
-}
-
-static int be_check_flash_crc(struct be_adapter *adapter, const u8 *p,
- u32 img_offset, u32 img_size, int hdr_size,
- u16 img_optype, bool *crc_match)
-{
- u32 crc_offset;
- int status;
- u8 crc[4];
-
- status = be_cmd_get_flash_crc(adapter, crc, img_optype, img_offset,
- img_size - 4);
- if (status)
- return status;
-
- crc_offset = hdr_size + img_offset + img_size - 4;
-
- /* Skip flashing, if crc of flashed region matches */
- if (!memcmp(crc, p + crc_offset, 4))
- *crc_match = true;
- else
- *crc_match = false;
-
- return status;
-}
-
-static int be_flash(struct be_adapter *adapter, const u8 *img,
- struct be_dma_mem *flash_cmd, int optype, int img_size,
- u32 img_offset)
-{
- u32 flash_op, num_bytes, total_bytes = img_size, bytes_sent = 0;
- struct be_cmd_write_flashrom *req = flash_cmd->va;
- int status;
-
- while (total_bytes) {
- num_bytes = min_t(u32, 32*1024, total_bytes);
-
- total_bytes -= num_bytes;
-
- if (!total_bytes) {
- if (optype == OPTYPE_PHY_FW)
- flash_op = FLASHROM_OPER_PHY_FLASH;
- else
- flash_op = FLASHROM_OPER_FLASH;
- } else {
- if (optype == OPTYPE_PHY_FW)
- flash_op = FLASHROM_OPER_PHY_SAVE;
- else
- flash_op = FLASHROM_OPER_SAVE;
- }
-
- memcpy(req->data_buf, img, num_bytes);
- img += num_bytes;
- status = be_cmd_write_flashrom(adapter, flash_cmd, optype,
- flash_op, img_offset +
- bytes_sent, num_bytes);
- if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST &&
- optype == OPTYPE_PHY_FW)
- break;
- else if (status)
- return status;
-
- bytes_sent += num_bytes;
- }
- return 0;
-}
-
-/* For BE2, BE3 and BE3-R */
-static int be_flash_BEx(struct be_adapter *adapter,
- const struct firmware *fw,
- struct be_dma_mem *flash_cmd, int num_of_images)
-{
- int img_hdrs_size = (num_of_images * sizeof(struct image_hdr));
- struct device *dev = &adapter->pdev->dev;
- struct flash_section_info *fsec = NULL;
- int status, i, filehdr_size, num_comp;
- const struct flash_comp *pflashcomp;
- bool crc_match;
- const u8 *p;
-
- struct flash_comp gen3_flash_types[] = {
- { FLASH_iSCSI_PRIMARY_IMAGE_START_g3, OPTYPE_ISCSI_ACTIVE,
- FLASH_IMAGE_MAX_SIZE_g3, IMAGE_FIRMWARE_iSCSI},
- { FLASH_REDBOOT_START_g3, OPTYPE_REDBOOT,
- FLASH_REDBOOT_IMAGE_MAX_SIZE_g3, IMAGE_BOOT_CODE},
- { FLASH_iSCSI_BIOS_START_g3, OPTYPE_BIOS,
- FLASH_BIOS_IMAGE_MAX_SIZE_g3, IMAGE_OPTION_ROM_ISCSI},
- { FLASH_PXE_BIOS_START_g3, OPTYPE_PXE_BIOS,
- FLASH_BIOS_IMAGE_MAX_SIZE_g3, IMAGE_OPTION_ROM_PXE},
- { FLASH_FCoE_BIOS_START_g3, OPTYPE_FCOE_BIOS,
- FLASH_BIOS_IMAGE_MAX_SIZE_g3, IMAGE_OPTION_ROM_FCoE},
- { FLASH_iSCSI_BACKUP_IMAGE_START_g3, OPTYPE_ISCSI_BACKUP,
- FLASH_IMAGE_MAX_SIZE_g3, IMAGE_FIRMWARE_BACKUP_iSCSI},
- { FLASH_FCoE_PRIMARY_IMAGE_START_g3, OPTYPE_FCOE_FW_ACTIVE,
- FLASH_IMAGE_MAX_SIZE_g3, IMAGE_FIRMWARE_FCoE},
- { FLASH_FCoE_BACKUP_IMAGE_START_g3, OPTYPE_FCOE_FW_BACKUP,
- FLASH_IMAGE_MAX_SIZE_g3, IMAGE_FIRMWARE_BACKUP_FCoE},
- { FLASH_NCSI_START_g3, OPTYPE_NCSI_FW,
- FLASH_NCSI_IMAGE_MAX_SIZE_g3, IMAGE_NCSI},
- { FLASH_PHY_FW_START_g3, OPTYPE_PHY_FW,
- FLASH_PHY_FW_IMAGE_MAX_SIZE_g3, IMAGE_FIRMWARE_PHY}
- };
-
- struct flash_comp gen2_flash_types[] = {
- { FLASH_iSCSI_PRIMARY_IMAGE_START_g2, OPTYPE_ISCSI_ACTIVE,
- FLASH_IMAGE_MAX_SIZE_g2, IMAGE_FIRMWARE_iSCSI},
- { FLASH_REDBOOT_START_g2, OPTYPE_REDBOOT,
- FLASH_REDBOOT_IMAGE_MAX_SIZE_g2, IMAGE_BOOT_CODE},
- { FLASH_iSCSI_BIOS_START_g2, OPTYPE_BIOS,
- FLASH_BIOS_IMAGE_MAX_SIZE_g2, IMAGE_OPTION_ROM_ISCSI},
- { FLASH_PXE_BIOS_START_g2, OPTYPE_PXE_BIOS,
- FLASH_BIOS_IMAGE_MAX_SIZE_g2, IMAGE_OPTION_ROM_PXE},
- { FLASH_FCoE_BIOS_START_g2, OPTYPE_FCOE_BIOS,
- FLASH_BIOS_IMAGE_MAX_SIZE_g2, IMAGE_OPTION_ROM_FCoE},
- { FLASH_iSCSI_BACKUP_IMAGE_START_g2, OPTYPE_ISCSI_BACKUP,
- FLASH_IMAGE_MAX_SIZE_g2, IMAGE_FIRMWARE_BACKUP_iSCSI},
- { FLASH_FCoE_PRIMARY_IMAGE_START_g2, OPTYPE_FCOE_FW_ACTIVE,
- FLASH_IMAGE_MAX_SIZE_g2, IMAGE_FIRMWARE_FCoE},
- { FLASH_FCoE_BACKUP_IMAGE_START_g2, OPTYPE_FCOE_FW_BACKUP,
- FLASH_IMAGE_MAX_SIZE_g2, IMAGE_FIRMWARE_BACKUP_FCoE}
- };
-
- if (BE3_chip(adapter)) {
- pflashcomp = gen3_flash_types;
- filehdr_size = sizeof(struct flash_file_hdr_g3);
- num_comp = ARRAY_SIZE(gen3_flash_types);
- } else {
- pflashcomp = gen2_flash_types;
- filehdr_size = sizeof(struct flash_file_hdr_g2);
- num_comp = ARRAY_SIZE(gen2_flash_types);
- img_hdrs_size = 0;
- }
-
- /* Get flash section info*/
- fsec = get_fsec_info(adapter, filehdr_size + img_hdrs_size, fw);
- if (!fsec) {
- dev_err(dev, "Invalid Cookie. FW image may be corrupted\n");
- return -1;
- }
- for (i = 0; i < num_comp; i++) {
- if (!is_comp_in_ufi(adapter, fsec, pflashcomp[i].img_type))
- continue;
-
- if ((pflashcomp[i].optype == OPTYPE_NCSI_FW) &&
- memcmp(adapter->fw_ver, "3.102.148.0", 11) < 0)
- continue;
-
- if (pflashcomp[i].optype == OPTYPE_PHY_FW &&
- !phy_flashing_required(adapter))
- continue;
-
- if (pflashcomp[i].optype == OPTYPE_REDBOOT) {
- status = be_check_flash_crc(adapter, fw->data,
- pflashcomp[i].offset,
- pflashcomp[i].size,
- filehdr_size +
- img_hdrs_size,
- OPTYPE_REDBOOT, &crc_match);
- if (status) {
- dev_err(dev,
- "Could not get CRC for 0x%x region\n",
- pflashcomp[i].optype);
- continue;
- }
-
- if (crc_match)
- continue;
- }
-
- p = fw->data + filehdr_size + pflashcomp[i].offset +
- img_hdrs_size;
- if (p + pflashcomp[i].size > fw->data + fw->size)
- return -1;
-
- status = be_flash(adapter, p, flash_cmd, pflashcomp[i].optype,
- pflashcomp[i].size, 0);
- if (status) {
- dev_err(dev, "Flashing section type 0x%x failed\n",
- pflashcomp[i].img_type);
- return status;
- }
- }
- return 0;
-}
-
-static u16 be_get_img_optype(struct flash_section_entry fsec_entry)
-{
- u32 img_type = le32_to_cpu(fsec_entry.type);
- u16 img_optype = le16_to_cpu(fsec_entry.optype);
-
- if (img_optype != 0xFFFF)
- return img_optype;
-
- switch (img_type) {
- case IMAGE_FIRMWARE_iSCSI:
- img_optype = OPTYPE_ISCSI_ACTIVE;
- break;
- case IMAGE_BOOT_CODE:
- img_optype = OPTYPE_REDBOOT;
- break;
- case IMAGE_OPTION_ROM_ISCSI:
- img_optype = OPTYPE_BIOS;
- break;
- case IMAGE_OPTION_ROM_PXE:
- img_optype = OPTYPE_PXE_BIOS;
- break;
- case IMAGE_OPTION_ROM_FCoE:
- img_optype = OPTYPE_FCOE_BIOS;
- break;
- case IMAGE_FIRMWARE_BACKUP_iSCSI:
- img_optype = OPTYPE_ISCSI_BACKUP;
- break;
- case IMAGE_NCSI:
- img_optype = OPTYPE_NCSI_FW;
- break;
- case IMAGE_FLASHISM_JUMPVECTOR:
- img_optype = OPTYPE_FLASHISM_JUMPVECTOR;
- break;
- case IMAGE_FIRMWARE_PHY:
- img_optype = OPTYPE_SH_PHY_FW;
- break;
- case IMAGE_REDBOOT_DIR:
- img_optype = OPTYPE_REDBOOT_DIR;
- break;
- case IMAGE_REDBOOT_CONFIG:
- img_optype = OPTYPE_REDBOOT_CONFIG;
- break;
- case IMAGE_UFI_DIR:
- img_optype = OPTYPE_UFI_DIR;
- break;
- default:
- break;
- }
-
- return img_optype;
-}
-
-static int be_flash_skyhawk(struct be_adapter *adapter,
- const struct firmware *fw,
- struct be_dma_mem *flash_cmd, int num_of_images)
-{
- int img_hdrs_size = num_of_images * sizeof(struct image_hdr);
- bool crc_match, old_fw_img, flash_offset_support = true;
- struct device *dev = &adapter->pdev->dev;
- struct flash_section_info *fsec = NULL;
- u32 img_offset, img_size, img_type;
- u16 img_optype, flash_optype;
- int status, i, filehdr_size;
- const u8 *p;
-
- filehdr_size = sizeof(struct flash_file_hdr_g3);
- fsec = get_fsec_info(adapter, filehdr_size + img_hdrs_size, fw);
- if (!fsec) {
- dev_err(dev, "Invalid Cookie. FW image may be corrupted\n");
- return -EINVAL;
- }
-
-retry_flash:
- for (i = 0; i < le32_to_cpu(fsec->fsec_hdr.num_images); i++) {
- img_offset = le32_to_cpu(fsec->fsec_entry[i].offset);
- img_size = le32_to_cpu(fsec->fsec_entry[i].pad_size);
- img_type = le32_to_cpu(fsec->fsec_entry[i].type);
- img_optype = be_get_img_optype(fsec->fsec_entry[i]);
- old_fw_img = fsec->fsec_entry[i].optype == 0xFFFF;
-
- if (img_optype == 0xFFFF)
- continue;
-
- if (flash_offset_support)
- flash_optype = OPTYPE_OFFSET_SPECIFIED;
- else
- flash_optype = img_optype;
-
- /* Don't bother verifying CRC if an old FW image is being
- * flashed
- */
- if (old_fw_img)
- goto flash;
-
- status = be_check_flash_crc(adapter, fw->data, img_offset,
- img_size, filehdr_size +
- img_hdrs_size, flash_optype,
- &crc_match);
- if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST ||
- base_status(status) == MCC_STATUS_ILLEGAL_FIELD) {
- /* The current FW image on the card does not support
- * OFFSET based flashing. Retry using older mechanism
- * of OPTYPE based flashing
- */
- if (flash_optype == OPTYPE_OFFSET_SPECIFIED) {
- flash_offset_support = false;
- goto retry_flash;
- }
-
- /* The current FW image on the card does not recognize
- * the new FLASH op_type. The FW download is partially
- * complete. Reboot the server now to enable FW image
- * to recognize the new FLASH op_type. To complete the
- * remaining process, download the same FW again after
- * the reboot.
- */
- dev_err(dev, "Flash incomplete. Reset the server\n");
- dev_err(dev, "Download FW image again after reset\n");
- return -EAGAIN;
- } else if (status) {
- dev_err(dev, "Could not get CRC for 0x%x region\n",
- img_optype);
- return -EFAULT;
- }
-
- if (crc_match)
- continue;
-
-flash:
- p = fw->data + filehdr_size + img_offset + img_hdrs_size;
- if (p + img_size > fw->data + fw->size)
- return -1;
-
- status = be_flash(adapter, p, flash_cmd, flash_optype, img_size,
- img_offset);
-
- /* The current FW image on the card does not support OFFSET
- * based flashing. Retry using older mechanism of OPTYPE based
- * flashing
- */
- if (base_status(status) == MCC_STATUS_ILLEGAL_FIELD &&
- flash_optype == OPTYPE_OFFSET_SPECIFIED) {
- flash_offset_support = false;
- goto retry_flash;
- }
-
- /* For old FW images ignore ILLEGAL_FIELD error or errors on
- * UFI_DIR region
- */
- if (old_fw_img &&
- (base_status(status) == MCC_STATUS_ILLEGAL_FIELD ||
- (img_optype == OPTYPE_UFI_DIR &&
- base_status(status) == MCC_STATUS_FAILED))) {
- continue;
- } else if (status) {
- dev_err(dev, "Flashing section type 0x%x failed\n",
- img_type);
- return -EFAULT;
- }
- }
- return 0;
-}
-
-static int lancer_fw_download(struct be_adapter *adapter,
- const struct firmware *fw)
-{
-#define LANCER_FW_DOWNLOAD_CHUNK (32 * 1024)
-#define LANCER_FW_DOWNLOAD_LOCATION "/prg"
- struct device *dev = &adapter->pdev->dev;
- struct be_dma_mem flash_cmd;
- const u8 *data_ptr = NULL;
- u8 *dest_image_ptr = NULL;
- size_t image_size = 0;
- u32 chunk_size = 0;
- u32 data_written = 0;
- u32 offset = 0;
- int status = 0;
- u8 add_status = 0;
- u8 change_status;
-
- if (!IS_ALIGNED(fw->size, sizeof(u32))) {
- dev_err(dev, "FW image size should be multiple of 4\n");
- return -EINVAL;
- }
-
- flash_cmd.size = sizeof(struct lancer_cmd_req_write_object)
- + LANCER_FW_DOWNLOAD_CHUNK;
- flash_cmd.va = dma_zalloc_coherent(dev, flash_cmd.size,
- &flash_cmd.dma, GFP_KERNEL);
- if (!flash_cmd.va)
- return -ENOMEM;
-
- dest_image_ptr = flash_cmd.va +
- sizeof(struct lancer_cmd_req_write_object);
- image_size = fw->size;
- data_ptr = fw->data;
-
- while (image_size) {
- chunk_size = min_t(u32, image_size, LANCER_FW_DOWNLOAD_CHUNK);
-
- /* Copy the image chunk content. */
- memcpy(dest_image_ptr, data_ptr, chunk_size);
-
- status = lancer_cmd_write_object(adapter, &flash_cmd,
- chunk_size, offset,
- LANCER_FW_DOWNLOAD_LOCATION,
- &data_written, &change_status,
- &add_status);
- if (status)
- break;
-
- offset += data_written;
- data_ptr += data_written;
- image_size -= data_written;
- }
-
- if (!status) {
- /* Commit the FW written */
- status = lancer_cmd_write_object(adapter, &flash_cmd,
- 0, offset,
- LANCER_FW_DOWNLOAD_LOCATION,
- &data_written, &change_status,
- &add_status);
- }
-
- dma_free_coherent(dev, flash_cmd.size, flash_cmd.va, flash_cmd.dma);
- if (status) {
- dev_err(dev, "Firmware load error\n");
- return be_cmd_status(status);
- }
-
- dev_info(dev, "Firmware flashed successfully\n");
-
- if (change_status == LANCER_FW_RESET_NEEDED) {
- dev_info(dev, "Resetting adapter to activate new FW\n");
- status = lancer_physdev_ctrl(adapter,
- PHYSDEV_CONTROL_FW_RESET_MASK);
- if (status) {
- dev_err(dev, "Adapter busy, could not reset FW\n");
- dev_err(dev, "Reboot server to activate new FW\n");
- }
- } else if (change_status != LANCER_NO_RESET_NEEDED) {
- dev_info(dev, "Reboot server to activate new FW\n");
- }
-
- return 0;
-}
-
-/* Check if the flash image file is compatible with the adapter that
- * is being flashed.
- */
-static bool be_check_ufi_compatibility(struct be_adapter *adapter,
- struct flash_file_hdr_g3 *fhdr)
-{
- if (!fhdr) {
- dev_err(&adapter->pdev->dev, "Invalid FW UFI file");
- return false;
- }
-
- /* First letter of the build version is used to identify
- * which chip this image file is meant for.
- */
- switch (fhdr->build[0]) {
- case BLD_STR_UFI_TYPE_SH:
- if (!skyhawk_chip(adapter))
- return false;
- break;
- case BLD_STR_UFI_TYPE_BE3:
- if (!BE3_chip(adapter))
- return false;
- break;
- case BLD_STR_UFI_TYPE_BE2:
- if (!BE2_chip(adapter))
- return false;
- break;
- default:
- return false;
- }
-
- /* In BE3 FW images the "asic_type_rev" field doesn't track the
- * asic_rev of the chips it is compatible with.
- * When asic_type_rev is 0 the image is compatible only with
- * pre-BE3-R chips (asic_rev < 0x10)
- */
- if (BEx_chip(adapter) && fhdr->asic_type_rev == 0)
- return adapter->asic_rev < 0x10;
- else
- return (fhdr->asic_type_rev >= adapter->asic_rev);
-}
-
-static int be_fw_download(struct be_adapter *adapter, const struct firmware* fw)
-{
- struct device *dev = &adapter->pdev->dev;
- struct flash_file_hdr_g3 *fhdr3;
- struct image_hdr *img_hdr_ptr;
- int status = 0, i, num_imgs;
- struct be_dma_mem flash_cmd;
-
- fhdr3 = (struct flash_file_hdr_g3 *)fw->data;
- if (!be_check_ufi_compatibility(adapter, fhdr3)) {
- dev_err(dev, "Flash image is not compatible with adapter\n");
- return -EINVAL;
- }
-
- flash_cmd.size = sizeof(struct be_cmd_write_flashrom);
- flash_cmd.va = dma_zalloc_coherent(dev, flash_cmd.size, &flash_cmd.dma,
- GFP_KERNEL);
- if (!flash_cmd.va)
- return -ENOMEM;
-
- num_imgs = le32_to_cpu(fhdr3->num_imgs);
- for (i = 0; i < num_imgs; i++) {
- img_hdr_ptr = (struct image_hdr *)(fw->data +
- (sizeof(struct flash_file_hdr_g3) +
- i * sizeof(struct image_hdr)));
- if (!BE2_chip(adapter) &&
- le32_to_cpu(img_hdr_ptr->imageid) != 1)
- continue;
-
- if (skyhawk_chip(adapter))
- status = be_flash_skyhawk(adapter, fw, &flash_cmd,
- num_imgs);
- else
- status = be_flash_BEx(adapter, fw, &flash_cmd,
- num_imgs);
- }
-
- dma_free_coherent(dev, flash_cmd.size, flash_cmd.va, flash_cmd.dma);
- if (!status)
- dev_info(dev, "Firmware flashed successfully\n");
-
- return status;
-}
-
int be_load_fw(struct be_adapter *adapter, u8 *fw_file)
{
const struct firmware *fw;
@@ -5110,6 +4553,9 @@ static int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
return -EINVAL;
mode = nla_get_u16(attr);
+ if (BE3_chip(adapter) && mode == BRIDGE_MODE_VEPA)
+ return -EOPNOTSUPP;
+
if (mode != BRIDGE_MODE_VEPA && mode != BRIDGE_MODE_VEB)
return -EINVAL;
@@ -5291,7 +4737,7 @@ static netdev_features_t be_features_check(struct sk_buff *skb,
skb->inner_protocol != htons(ETH_P_TEB) ||
skb_inner_mac_header(skb) - skb_transport_header(skb) !=
sizeof(struct udphdr) + sizeof(struct vxlanhdr))
- return features & ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK);
+ return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
return features;
}
diff --git a/drivers/net/ethernet/emulex/benet/be_roce.c b/drivers/net/ethernet/emulex/benet/be_roce.c
index 60368207bf58..4089156a7f5e 100644
--- a/drivers/net/ethernet/emulex/benet/be_roce.c
+++ b/drivers/net/ethernet/emulex/benet/be_roce.c
@@ -116,40 +116,6 @@ void be_roce_dev_remove(struct be_adapter *adapter)
}
}
-static void _be_roce_dev_open(struct be_adapter *adapter)
-{
- if (ocrdma_drv && adapter->ocrdma_dev &&
- ocrdma_drv->state_change_handler)
- ocrdma_drv->state_change_handler(adapter->ocrdma_dev,
- BE_DEV_UP);
-}
-
-void be_roce_dev_open(struct be_adapter *adapter)
-{
- if (be_roce_supported(adapter)) {
- mutex_lock(&be_adapter_list_lock);
- _be_roce_dev_open(adapter);
- mutex_unlock(&be_adapter_list_lock);
- }
-}
-
-static void _be_roce_dev_close(struct be_adapter *adapter)
-{
- if (ocrdma_drv && adapter->ocrdma_dev &&
- ocrdma_drv->state_change_handler)
- ocrdma_drv->state_change_handler(adapter->ocrdma_dev,
- BE_DEV_DOWN);
-}
-
-void be_roce_dev_close(struct be_adapter *adapter)
-{
- if (be_roce_supported(adapter)) {
- mutex_lock(&be_adapter_list_lock);
- _be_roce_dev_close(adapter);
- mutex_unlock(&be_adapter_list_lock);
- }
-}
-
void be_roce_dev_shutdown(struct be_adapter *adapter)
{
if (be_roce_supported(adapter)) {
@@ -177,8 +143,6 @@ int be_roce_register_driver(struct ocrdma_driver *drv)
_be_roce_dev_add(dev);
netdev = dev->netdev;
- if (netif_running(netdev) && netif_oper_up(netdev))
- _be_roce_dev_open(dev);
}
mutex_unlock(&be_adapter_list_lock);
return 0;
diff --git a/drivers/net/ethernet/emulex/benet/be_roce.h b/drivers/net/ethernet/emulex/benet/be_roce.h
index cde6ef905ec4..fde609789483 100644
--- a/drivers/net/ethernet/emulex/benet/be_roce.h
+++ b/drivers/net/ethernet/emulex/benet/be_roce.h
@@ -60,9 +60,7 @@ struct ocrdma_driver {
void (*state_change_handler) (struct ocrdma_dev *, u32 new_state);
};
-enum {
- BE_DEV_UP = 0,
- BE_DEV_DOWN = 1,
+enum be_roce_event {
BE_DEV_SHUTDOWN = 2
};
diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c
index ff665493ca97..62fa136554ac 100644
--- a/drivers/net/ethernet/ethoc.c
+++ b/drivers/net/ethernet/ethoc.c
@@ -678,7 +678,7 @@ static int ethoc_mdio_probe(struct net_device *dev)
int err;
if (priv->phy_id != -1)
- phy = priv->mdio->phy_map[priv->phy_id];
+ phy = mdiobus_get_phy(priv->mdio, priv->phy_id);
else
phy = phy_find_first(priv->mdio);
@@ -766,7 +766,7 @@ static int ethoc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
if (mdio->phy_id >= PHY_MAX_ADDR)
return -ERANGE;
- phy = priv->mdio->phy_map[mdio->phy_id];
+ phy = mdiobus_get_phy(priv->mdio, mdio->phy_id);
if (!phy)
return -ENODEV;
} else {
@@ -1015,7 +1015,6 @@ static int ethoc_probe(struct platform_device *pdev)
struct resource *mmio = NULL;
struct resource *mem = NULL;
struct ethoc *priv = NULL;
- unsigned int phy;
int num_bd;
int ret = 0;
bool random_mac = false;
@@ -1206,19 +1205,10 @@ static int ethoc_probe(struct platform_device *pdev)
priv->mdio->write = ethoc_mdio_write;
priv->mdio->priv = priv;
- priv->mdio->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!priv->mdio->irq) {
- ret = -ENOMEM;
- goto free_mdio;
- }
-
- for (phy = 0; phy < PHY_MAX_ADDR; phy++)
- priv->mdio->irq[phy] = PHY_POLL;
-
ret = mdiobus_register(priv->mdio);
if (ret) {
dev_err(&netdev->dev, "failed to register MDIO bus\n");
- goto free_mdio;
+ goto free;
}
ret = ethoc_mdio_probe(netdev);
@@ -1250,8 +1240,6 @@ error2:
netif_napi_del(&priv->napi);
error:
mdiobus_unregister(priv->mdio);
-free_mdio:
- kfree(priv->mdio->irq);
mdiobus_free(priv->mdio);
free:
if (priv->clk)
diff --git a/drivers/net/ethernet/ezchip/nps_enet.c b/drivers/net/ethernet/ezchip/nps_enet.c
index 63c2bcf8031a..b1026689b78f 100644
--- a/drivers/net/ethernet/ezchip/nps_enet.c
+++ b/drivers/net/ethernet/ezchip/nps_enet.c
@@ -48,21 +48,15 @@ static void nps_enet_read_rx_fifo(struct net_device *ndev,
*reg = nps_enet_reg_get(priv, NPS_ENET_REG_RX_BUF);
else { /* !dst_is_aligned */
for (i = 0; i < len; i++, reg++) {
- u32 buf =
- nps_enet_reg_get(priv, NPS_ENET_REG_RX_BUF);
-
- /* to accommodate word-unaligned address of "reg"
- * we have to do memcpy_toio() instead of simple "=".
- */
- memcpy_toio((void __iomem *)reg, &buf, sizeof(buf));
+ u32 buf = nps_enet_reg_get(priv, NPS_ENET_REG_RX_BUF);
+ put_unaligned(buf, reg);
}
}
/* copy last bytes (if any) */
if (last) {
u32 buf = nps_enet_reg_get(priv, NPS_ENET_REG_RX_BUF);
-
- memcpy_toio((void __iomem *)reg, &buf, last);
+ memcpy((u8*)reg, &buf, last);
}
}
@@ -367,7 +361,7 @@ static void nps_enet_send_frame(struct net_device *ndev,
struct nps_enet_tx_ctl tx_ctrl;
short length = skb->len;
u32 i, len = DIV_ROUND_UP(length, sizeof(u32));
- u32 *src = (u32 *)virt_to_phys(skb->data);
+ u32 *src = (void *)skb->data;
bool src_is_aligned = IS_ALIGNED((unsigned long)src, sizeof(u32));
tx_ctrl.value = 0;
@@ -375,17 +369,11 @@ static void nps_enet_send_frame(struct net_device *ndev,
if (src_is_aligned)
for (i = 0; i < len; i++, src++)
nps_enet_reg_set(priv, NPS_ENET_REG_TX_BUF, *src);
- else { /* !src_is_aligned */
- for (i = 0; i < len; i++, src++) {
- u32 buf;
-
- /* to accommodate word-unaligned address of "src"
- * we have to do memcpy_fromio() instead of simple "="
- */
- memcpy_fromio(&buf, (void __iomem *)src, sizeof(buf));
- nps_enet_reg_set(priv, NPS_ENET_REG_TX_BUF, buf);
- }
- }
+ else /* !src_is_aligned */
+ for (i = 0; i < len; i++, src++)
+ nps_enet_reg_set(priv, NPS_ENET_REG_TX_BUF,
+ get_unaligned(src));
+
/* Write the length of the Frame */
tx_ctrl.nt = length;
diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index 6d0c5d5eea6d..84384e1585a5 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -71,7 +71,6 @@ struct ftgmac100 {
struct napi_struct napi;
struct mii_bus *mii_bus;
- int phy_irq[PHY_MAX_ADDR];
struct phy_device *phydev;
int old_speed;
};
@@ -835,26 +834,15 @@ static void ftgmac100_adjust_link(struct net_device *netdev)
static int ftgmac100_mii_probe(struct ftgmac100 *priv)
{
struct net_device *netdev = priv->netdev;
- struct phy_device *phydev = NULL;
- int i;
-
- /* search for connect PHY device */
- for (i = 0; i < PHY_MAX_ADDR; i++) {
- struct phy_device *tmp = priv->mii_bus->phy_map[i];
-
- if (tmp) {
- phydev = tmp;
- break;
- }
- }
+ struct phy_device *phydev;
- /* now we are supposed to have a proper phydev, to attach to... */
+ phydev = phy_find_first(priv->mii_bus);
if (!phydev) {
netdev_info(netdev, "%s: no PHY found\n", netdev->name);
return -ENODEV;
}
- phydev = phy_connect(netdev, dev_name(&phydev->dev),
+ phydev = phy_connect(netdev, phydev_name(phydev),
&ftgmac100_adjust_link, PHY_INTERFACE_MODE_GMII);
if (IS_ERR(phydev)) {
@@ -1188,7 +1176,6 @@ static int ftgmac100_probe(struct platform_device *pdev)
struct net_device *netdev;
struct ftgmac100 *priv;
int err;
- int i;
if (!pdev)
return -ENODEV;
@@ -1257,10 +1244,6 @@ static int ftgmac100_probe(struct platform_device *pdev)
priv->mii_bus->priv = netdev;
priv->mii_bus->read = ftgmac100_mdiobus_read;
priv->mii_bus->write = ftgmac100_mdiobus_write;
- priv->mii_bus->irq = priv->phy_irq;
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- priv->mii_bus->irq[i] = PHY_POLL;
err = mdiobus_register(priv->mii_bus);
if (err) {
diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig
index ff76d4e9dc1b..d1ca45fbb164 100644
--- a/drivers/net/ethernet/freescale/Kconfig
+++ b/drivers/net/ethernet/freescale/Kconfig
@@ -7,7 +7,8 @@ config NET_VENDOR_FREESCALE
default y
depends on FSL_SOC || QUICC_ENGINE || CPM1 || CPM2 || PPC_MPC512x || \
M523x || M527x || M5272 || M528x || M520x || M532x || \
- ARCH_MXC || ARCH_MXS || (PPC_MPC52xx && PPC_BESTCOMM)
+ ARCH_MXC || ARCH_MXS || (PPC_MPC52xx && PPC_BESTCOMM) || \
+ ARCH_LAYERSCAPE
---help---
If you have a network (Ethernet) card belonging to this class, say Y.
@@ -53,6 +54,7 @@ config FEC_MPC52xx_MDIO
If compiled as module, it will be called fec_mpc52xx_phy.
source "drivers/net/ethernet/freescale/fs_enet/Kconfig"
+source "drivers/net/ethernet/freescale/fman/Kconfig"
config FSL_PQ_MDIO
tristate "Freescale PQ MDIO"
diff --git a/drivers/net/ethernet/freescale/Makefile b/drivers/net/ethernet/freescale/Makefile
index 71debd1c18c9..4097c58d17a7 100644
--- a/drivers/net/ethernet/freescale/Makefile
+++ b/drivers/net/ethernet/freescale/Makefile
@@ -17,3 +17,5 @@ gianfar_driver-objs := gianfar.o \
gianfar_ethtool.o
obj-$(CONFIG_UCC_GETH) += ucc_geth_driver.o
ucc_geth_driver-objs := ucc_geth.o ucc_geth_ethtool.o
+
+obj-$(CONFIG_FSL_FMAN) += fman/
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index b2a32209ffbf..502da6f48f95 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -48,6 +48,7 @@
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
+#include <linux/mdio.h>
#include <linux/phy.h>
#include <linux/fec.h>
#include <linux/of.h>
@@ -1926,11 +1927,7 @@ static int fec_enet_mii_probe(struct net_device *ndev)
} else {
/* check for attached phy */
for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) {
- if ((fep->mii_bus->phy_mask & (1 << phy_id)))
- continue;
- if (fep->mii_bus->phy_map[phy_id] == NULL)
- continue;
- if (fep->mii_bus->phy_map[phy_id]->phy_id == 0)
+ if (!mdiobus_is_registered_device(fep->mii_bus, phy_id))
continue;
if (dev_id--)
continue;
@@ -1972,9 +1969,7 @@ static int fec_enet_mii_probe(struct net_device *ndev)
fep->link = 0;
fep->full_duplex = 0;
- netdev_info(ndev, "Freescale FEC PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
- fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev),
- fep->phy_dev->irq);
+ phy_attached_info(phy_dev);
return 0;
}
@@ -1985,7 +1980,7 @@ static int fec_enet_mii_init(struct platform_device *pdev)
struct net_device *ndev = platform_get_drvdata(pdev);
struct fec_enet_private *fep = netdev_priv(ndev);
struct device_node *node;
- int err = -ENXIO, i;
+ int err = -ENXIO;
u32 mii_speed, holdtime;
/*
@@ -2067,15 +2062,6 @@ static int fec_enet_mii_init(struct platform_device *pdev)
fep->mii_bus->priv = fep;
fep->mii_bus->parent = &pdev->dev;
- fep->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!fep->mii_bus->irq) {
- err = -ENOMEM;
- goto err_out_free_mdiobus;
- }
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- fep->mii_bus->irq[i] = PHY_POLL;
-
node = of_get_child_by_name(pdev->dev.of_node, "mdio");
if (node) {
err = of_mdiobus_register(fep->mii_bus, node);
@@ -2085,7 +2071,7 @@ static int fec_enet_mii_init(struct platform_device *pdev)
}
if (err)
- goto err_out_free_mdio_irq;
+ goto err_out_free_mdiobus;
mii_cnt++;
@@ -2095,8 +2081,6 @@ static int fec_enet_mii_init(struct platform_device *pdev)
return 0;
-err_out_free_mdio_irq:
- kfree(fep->mii_bus->irq);
err_out_free_mdiobus:
mdiobus_free(fep->mii_bus);
err_out:
@@ -2107,7 +2091,6 @@ static void fec_enet_mii_remove(struct fec_enet_private *fep)
{
if (--mii_cnt == 0) {
mdiobus_unregister(fep->mii_bus);
- kfree(fep->mii_bus->irq);
mdiobus_free(fep->mii_bus);
}
}
@@ -3277,7 +3260,6 @@ static void
fec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx)
{
struct device_node *np = pdev->dev.of_node;
- int err;
*num_tx = *num_rx = 1;
@@ -3285,13 +3267,9 @@ fec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx)
return;
/* parse the num of tx and rx queues */
- err = of_property_read_u32(np, "fsl,num-tx-queues", num_tx);
- if (err)
- *num_tx = 1;
+ of_property_read_u32(np, "fsl,num-tx-queues", num_tx);
- err = of_property_read_u32(np, "fsl,num-rx-queues", num_rx);
- if (err)
- *num_rx = 1;
+ of_property_read_u32(np, "fsl,num-rx-queues", num_rx);
if (*num_tx < 1 || *num_tx > FEC_ENET_MAX_TX_QS) {
dev_warn(&pdev->dev, "Invalid num_tx(=%d), fall back to 1\n",
diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx.c b/drivers/net/ethernet/freescale/fec_mpc52xx.c
index afe7f39cdd7c..25553ee857b4 100644
--- a/drivers/net/ethernet/freescale/fec_mpc52xx.c
+++ b/drivers/net/ethernet/freescale/fec_mpc52xx.c
@@ -1084,27 +1084,23 @@ static struct platform_driver mpc52xx_fec_driver = {
/* Module */
/* ======================================================================== */
+static struct platform_driver * const drivers[] = {
+#ifdef CONFIG_FEC_MPC52xx_MDIO
+ &mpc52xx_fec_mdio_driver,
+#endif
+ &mpc52xx_fec_driver,
+};
+
static int __init
mpc52xx_fec_init(void)
{
-#ifdef CONFIG_FEC_MPC52xx_MDIO
- int ret;
- ret = platform_driver_register(&mpc52xx_fec_mdio_driver);
- if (ret) {
- pr_err("failed to register mdio driver\n");
- return ret;
- }
-#endif
- return platform_driver_register(&mpc52xx_fec_driver);
+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
}
static void __exit
mpc52xx_fec_exit(void)
{
- platform_driver_unregister(&mpc52xx_fec_driver);
-#ifdef CONFIG_FEC_MPC52xx_MDIO
- platform_driver_unregister(&mpc52xx_fec_mdio_driver);
-#endif
+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
}
diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c b/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c
index 1e647beaf989..b5497e308302 100644
--- a/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c
+++ b/drivers/net/ethernet/freescale/fec_mpc52xx_phy.c
@@ -22,7 +22,6 @@
struct mpc52xx_fec_mdio_priv {
struct mpc52xx_fec __iomem *regs;
- int mdio_irqs[PHY_MAX_ADDR];
};
static int mpc52xx_fec_mdio_transfer(struct mii_bus *bus, int phy_id,
@@ -83,9 +82,6 @@ static int mpc52xx_fec_mdio_probe(struct platform_device *of)
bus->read = mpc52xx_fec_mdio_read;
bus->write = mpc52xx_fec_mdio_write;
- /* setup irqs */
- bus->irq = priv->mdio_irqs;
-
/* setup registers */
err = of_address_to_resource(np, 0, &res);
if (err)
diff --git a/drivers/net/ethernet/freescale/fman/Kconfig b/drivers/net/ethernet/freescale/fman/Kconfig
new file mode 100644
index 000000000000..79b7c84b7869
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/Kconfig
@@ -0,0 +1,9 @@
+config FSL_FMAN
+ tristate "FMan support"
+ depends on FSL_SOC || COMPILE_TEST
+ select GENERIC_ALLOCATOR
+ select PHYLIB
+ default n
+ help
+ Freescale Data-Path Acceleration Architecture Frame Manager
+ (FMan) support
diff --git a/drivers/net/ethernet/freescale/fman/Makefile b/drivers/net/ethernet/freescale/fman/Makefile
new file mode 100644
index 000000000000..51fd2e6c1b84
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/Makefile
@@ -0,0 +1,7 @@
+subdir-ccflags-y += -I$(srctree)/drivers/net/ethernet/freescale/fman
+
+obj-y += fsl_fman.o fsl_fman_mac.o fsl_mac.o
+
+fsl_fman-objs := fman_muram.o fman.o fman_sp.o fman_port.o
+fsl_fman_mac-objs := fman_dtsec.o fman_memac.o fman_tgec.o
+fsl_mac-objs += mac.o
diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ethernet/freescale/fman/fman.c
new file mode 100644
index 000000000000..623aa1c8ebc6
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman.c
@@ -0,0 +1,2871 @@
+/*
+ * Copyright 2008-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "fman.h"
+#include "fman_muram.h"
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/interrupt.h>
+#include <linux/libfdt_env.h>
+
+/* General defines */
+#define FMAN_LIODN_TBL 64 /* size of LIODN table */
+#define MAX_NUM_OF_MACS 10
+#define FM_NUM_OF_FMAN_CTRL_EVENT_REGS 4
+#define BASE_RX_PORTID 0x08
+#define BASE_TX_PORTID 0x28
+
+/* Modules registers offsets */
+#define BMI_OFFSET 0x00080000
+#define QMI_OFFSET 0x00080400
+#define DMA_OFFSET 0x000C2000
+#define FPM_OFFSET 0x000C3000
+#define IMEM_OFFSET 0x000C4000
+#define CGP_OFFSET 0x000DB000
+
+/* Exceptions bit map */
+#define EX_DMA_BUS_ERROR 0x80000000
+#define EX_DMA_READ_ECC 0x40000000
+#define EX_DMA_SYSTEM_WRITE_ECC 0x20000000
+#define EX_DMA_FM_WRITE_ECC 0x10000000
+#define EX_FPM_STALL_ON_TASKS 0x08000000
+#define EX_FPM_SINGLE_ECC 0x04000000
+#define EX_FPM_DOUBLE_ECC 0x02000000
+#define EX_QMI_SINGLE_ECC 0x01000000
+#define EX_QMI_DEQ_FROM_UNKNOWN_PORTID 0x00800000
+#define EX_QMI_DOUBLE_ECC 0x00400000
+#define EX_BMI_LIST_RAM_ECC 0x00200000
+#define EX_BMI_STORAGE_PROFILE_ECC 0x00100000
+#define EX_BMI_STATISTICS_RAM_ECC 0x00080000
+#define EX_IRAM_ECC 0x00040000
+#define EX_MURAM_ECC 0x00020000
+#define EX_BMI_DISPATCH_RAM_ECC 0x00010000
+#define EX_DMA_SINGLE_PORT_ECC 0x00008000
+
+/* DMA defines */
+/* masks */
+#define DMA_MODE_BER 0x00200000
+#define DMA_MODE_ECC 0x00000020
+#define DMA_MODE_SECURE_PROT 0x00000800
+#define DMA_MODE_AXI_DBG_MASK 0x0F000000
+
+#define DMA_TRANSFER_PORTID_MASK 0xFF000000
+#define DMA_TRANSFER_TNUM_MASK 0x00FF0000
+#define DMA_TRANSFER_LIODN_MASK 0x00000FFF
+
+#define DMA_STATUS_BUS_ERR 0x08000000
+#define DMA_STATUS_READ_ECC 0x04000000
+#define DMA_STATUS_SYSTEM_WRITE_ECC 0x02000000
+#define DMA_STATUS_FM_WRITE_ECC 0x01000000
+#define DMA_STATUS_FM_SPDAT_ECC 0x00080000
+
+#define DMA_MODE_CACHE_OR_SHIFT 30
+#define DMA_MODE_AXI_DBG_SHIFT 24
+#define DMA_MODE_CEN_SHIFT 13
+#define DMA_MODE_CEN_MASK 0x00000007
+#define DMA_MODE_DBG_SHIFT 7
+#define DMA_MODE_AID_MODE_SHIFT 4
+
+#define DMA_THRESH_COMMQ_SHIFT 24
+#define DMA_THRESH_READ_INT_BUF_SHIFT 16
+#define DMA_THRESH_READ_INT_BUF_MASK 0x0000003f
+#define DMA_THRESH_WRITE_INT_BUF_MASK 0x0000003f
+
+#define DMA_TRANSFER_PORTID_SHIFT 24
+#define DMA_TRANSFER_TNUM_SHIFT 16
+
+#define DMA_CAM_SIZEOF_ENTRY 0x40
+#define DMA_CAM_UNITS 8
+
+#define DMA_LIODN_SHIFT 16
+#define DMA_LIODN_BASE_MASK 0x00000FFF
+
+/* FPM defines */
+#define FPM_EV_MASK_DOUBLE_ECC 0x80000000
+#define FPM_EV_MASK_STALL 0x40000000
+#define FPM_EV_MASK_SINGLE_ECC 0x20000000
+#define FPM_EV_MASK_RELEASE_FM 0x00010000
+#define FPM_EV_MASK_DOUBLE_ECC_EN 0x00008000
+#define FPM_EV_MASK_STALL_EN 0x00004000
+#define FPM_EV_MASK_SINGLE_ECC_EN 0x00002000
+#define FPM_EV_MASK_EXTERNAL_HALT 0x00000008
+#define FPM_EV_MASK_ECC_ERR_HALT 0x00000004
+
+#define FPM_RAM_MURAM_ECC 0x00008000
+#define FPM_RAM_IRAM_ECC 0x00004000
+#define FPM_IRAM_ECC_ERR_EX_EN 0x00020000
+#define FPM_MURAM_ECC_ERR_EX_EN 0x00040000
+#define FPM_RAM_IRAM_ECC_EN 0x40000000
+#define FPM_RAM_RAMS_ECC_EN 0x80000000
+#define FPM_RAM_RAMS_ECC_EN_SRC_SEL 0x08000000
+
+#define FPM_REV1_MAJOR_MASK 0x0000FF00
+#define FPM_REV1_MINOR_MASK 0x000000FF
+
+#define FPM_DISP_LIMIT_SHIFT 24
+
+#define FPM_PRT_FM_CTL1 0x00000001
+#define FPM_PRT_FM_CTL2 0x00000002
+#define FPM_PORT_FM_CTL_PORTID_SHIFT 24
+#define FPM_PRC_ORA_FM_CTL_SEL_SHIFT 16
+
+#define FPM_THR1_PRS_SHIFT 24
+#define FPM_THR1_KG_SHIFT 16
+#define FPM_THR1_PLCR_SHIFT 8
+#define FPM_THR1_BMI_SHIFT 0
+
+#define FPM_THR2_QMI_ENQ_SHIFT 24
+#define FPM_THR2_QMI_DEQ_SHIFT 0
+#define FPM_THR2_FM_CTL1_SHIFT 16
+#define FPM_THR2_FM_CTL2_SHIFT 8
+
+#define FPM_EV_MASK_CAT_ERR_SHIFT 1
+#define FPM_EV_MASK_DMA_ERR_SHIFT 0
+
+#define FPM_REV1_MAJOR_SHIFT 8
+
+#define FPM_RSTC_FM_RESET 0x80000000
+#define FPM_RSTC_MAC0_RESET 0x40000000
+#define FPM_RSTC_MAC1_RESET 0x20000000
+#define FPM_RSTC_MAC2_RESET 0x10000000
+#define FPM_RSTC_MAC3_RESET 0x08000000
+#define FPM_RSTC_MAC8_RESET 0x04000000
+#define FPM_RSTC_MAC4_RESET 0x02000000
+#define FPM_RSTC_MAC5_RESET 0x01000000
+#define FPM_RSTC_MAC6_RESET 0x00800000
+#define FPM_RSTC_MAC7_RESET 0x00400000
+#define FPM_RSTC_MAC9_RESET 0x00200000
+
+#define FPM_TS_INT_SHIFT 16
+#define FPM_TS_CTL_EN 0x80000000
+
+/* BMI defines */
+#define BMI_INIT_START 0x80000000
+#define BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC 0x80000000
+#define BMI_ERR_INTR_EN_LIST_RAM_ECC 0x40000000
+#define BMI_ERR_INTR_EN_STATISTICS_RAM_ECC 0x20000000
+#define BMI_ERR_INTR_EN_DISPATCH_RAM_ECC 0x10000000
+#define BMI_NUM_OF_TASKS_MASK 0x3F000000
+#define BMI_NUM_OF_EXTRA_TASKS_MASK 0x000F0000
+#define BMI_NUM_OF_DMAS_MASK 0x00000F00
+#define BMI_NUM_OF_EXTRA_DMAS_MASK 0x0000000F
+#define BMI_FIFO_SIZE_MASK 0x000003FF
+#define BMI_EXTRA_FIFO_SIZE_MASK 0x03FF0000
+#define BMI_CFG2_DMAS_MASK 0x0000003F
+#define BMI_CFG2_TASKS_MASK 0x0000003F
+
+#define BMI_CFG2_TASKS_SHIFT 16
+#define BMI_CFG2_DMAS_SHIFT 0
+#define BMI_CFG1_FIFO_SIZE_SHIFT 16
+#define BMI_NUM_OF_TASKS_SHIFT 24
+#define BMI_EXTRA_NUM_OF_TASKS_SHIFT 16
+#define BMI_NUM_OF_DMAS_SHIFT 8
+#define BMI_EXTRA_NUM_OF_DMAS_SHIFT 0
+
+#define BMI_FIFO_ALIGN 0x100
+
+#define BMI_EXTRA_FIFO_SIZE_SHIFT 16
+
+/* QMI defines */
+#define QMI_CFG_ENQ_EN 0x80000000
+#define QMI_CFG_DEQ_EN 0x40000000
+#define QMI_CFG_EN_COUNTERS 0x10000000
+#define QMI_CFG_DEQ_MASK 0x0000003F
+#define QMI_CFG_ENQ_MASK 0x00003F00
+#define QMI_CFG_ENQ_SHIFT 8
+
+#define QMI_ERR_INTR_EN_DOUBLE_ECC 0x80000000
+#define QMI_ERR_INTR_EN_DEQ_FROM_DEF 0x40000000
+#define QMI_INTR_EN_SINGLE_ECC 0x80000000
+
+#define QMI_GS_HALT_NOT_BUSY 0x00000002
+
+/* IRAM defines */
+#define IRAM_IADD_AIE 0x80000000
+#define IRAM_READY 0x80000000
+
+/* Default values */
+#define DEFAULT_CATASTROPHIC_ERR 0
+#define DEFAULT_DMA_ERR 0
+#define DEFAULT_AID_MODE FMAN_DMA_AID_OUT_TNUM
+#define DEFAULT_DMA_COMM_Q_LOW 0x2A
+#define DEFAULT_DMA_COMM_Q_HIGH 0x3F
+#define DEFAULT_CACHE_OVERRIDE 0
+#define DEFAULT_DMA_CAM_NUM_OF_ENTRIES 64
+#define DEFAULT_DMA_DBG_CNT_MODE 0
+#define DEFAULT_DMA_SOS_EMERGENCY 0
+#define DEFAULT_DMA_WATCHDOG 0
+#define DEFAULT_DISP_LIMIT 0
+#define DEFAULT_PRS_DISP_TH 16
+#define DEFAULT_PLCR_DISP_TH 16
+#define DEFAULT_KG_DISP_TH 16
+#define DEFAULT_BMI_DISP_TH 16
+#define DEFAULT_QMI_ENQ_DISP_TH 16
+#define DEFAULT_QMI_DEQ_DISP_TH 16
+#define DEFAULT_FM_CTL1_DISP_TH 16
+#define DEFAULT_FM_CTL2_DISP_TH 16
+
+#define DFLT_AXI_DBG_NUM_OF_BEATS 1
+
+#define DFLT_DMA_READ_INT_BUF_LOW(dma_thresh_max_buf) \
+ ((dma_thresh_max_buf + 1) / 2)
+#define DFLT_DMA_READ_INT_BUF_HIGH(dma_thresh_max_buf) \
+ ((dma_thresh_max_buf + 1) * 3 / 4)
+#define DFLT_DMA_WRITE_INT_BUF_LOW(dma_thresh_max_buf) \
+ ((dma_thresh_max_buf + 1) / 2)
+#define DFLT_DMA_WRITE_INT_BUF_HIGH(dma_thresh_max_buf)\
+ ((dma_thresh_max_buf + 1) * 3 / 4)
+
+#define DMA_COMM_Q_LOW_FMAN_V3 0x2A
+#define DMA_COMM_Q_LOW_FMAN_V2(dma_thresh_max_commq) \
+ ((dma_thresh_max_commq + 1) / 2)
+#define DFLT_DMA_COMM_Q_LOW(major, dma_thresh_max_commq) \
+ ((major == 6) ? DMA_COMM_Q_LOW_FMAN_V3 : \
+ DMA_COMM_Q_LOW_FMAN_V2(dma_thresh_max_commq))
+
+#define DMA_COMM_Q_HIGH_FMAN_V3 0x3f
+#define DMA_COMM_Q_HIGH_FMAN_V2(dma_thresh_max_commq) \
+ ((dma_thresh_max_commq + 1) * 3 / 4)
+#define DFLT_DMA_COMM_Q_HIGH(major, dma_thresh_max_commq) \
+ ((major == 6) ? DMA_COMM_Q_HIGH_FMAN_V3 : \
+ DMA_COMM_Q_HIGH_FMAN_V2(dma_thresh_max_commq))
+
+#define TOTAL_NUM_OF_TASKS_FMAN_V3L 59
+#define TOTAL_NUM_OF_TASKS_FMAN_V3H 124
+#define DFLT_TOTAL_NUM_OF_TASKS(major, minor, bmi_max_num_of_tasks) \
+ ((major == 6) ? ((minor == 1 || minor == 4) ? \
+ TOTAL_NUM_OF_TASKS_FMAN_V3L : TOTAL_NUM_OF_TASKS_FMAN_V3H) : \
+ bmi_max_num_of_tasks)
+
+#define DMA_CAM_NUM_OF_ENTRIES_FMAN_V3 64
+#define DMA_CAM_NUM_OF_ENTRIES_FMAN_V2 32
+#define DFLT_DMA_CAM_NUM_OF_ENTRIES(major) \
+ (major == 6 ? DMA_CAM_NUM_OF_ENTRIES_FMAN_V3 : \
+ DMA_CAM_NUM_OF_ENTRIES_FMAN_V2)
+
+#define FM_TIMESTAMP_1_USEC_BIT 8
+
+/* Defines used for enabling/disabling FMan interrupts */
+#define ERR_INTR_EN_DMA 0x00010000
+#define ERR_INTR_EN_FPM 0x80000000
+#define ERR_INTR_EN_BMI 0x00800000
+#define ERR_INTR_EN_QMI 0x00400000
+#define ERR_INTR_EN_MURAM 0x00040000
+#define ERR_INTR_EN_MAC0 0x00004000
+#define ERR_INTR_EN_MAC1 0x00002000
+#define ERR_INTR_EN_MAC2 0x00001000
+#define ERR_INTR_EN_MAC3 0x00000800
+#define ERR_INTR_EN_MAC4 0x00000400
+#define ERR_INTR_EN_MAC5 0x00000200
+#define ERR_INTR_EN_MAC6 0x00000100
+#define ERR_INTR_EN_MAC7 0x00000080
+#define ERR_INTR_EN_MAC8 0x00008000
+#define ERR_INTR_EN_MAC9 0x00000040
+
+#define INTR_EN_QMI 0x40000000
+#define INTR_EN_MAC0 0x00080000
+#define INTR_EN_MAC1 0x00040000
+#define INTR_EN_MAC2 0x00020000
+#define INTR_EN_MAC3 0x00010000
+#define INTR_EN_MAC4 0x00000040
+#define INTR_EN_MAC5 0x00000020
+#define INTR_EN_MAC6 0x00000008
+#define INTR_EN_MAC7 0x00000002
+#define INTR_EN_MAC8 0x00200000
+#define INTR_EN_MAC9 0x00100000
+#define INTR_EN_REV0 0x00008000
+#define INTR_EN_REV1 0x00004000
+#define INTR_EN_REV2 0x00002000
+#define INTR_EN_REV3 0x00001000
+#define INTR_EN_TMR 0x01000000
+
+enum fman_dma_aid_mode {
+ FMAN_DMA_AID_OUT_PORT_ID = 0, /* 4 LSB of PORT_ID */
+ FMAN_DMA_AID_OUT_TNUM /* 4 LSB of TNUM */
+};
+
+struct fman_iram_regs {
+ u32 iadd; /* FM IRAM instruction address register */
+ u32 idata; /* FM IRAM instruction data register */
+ u32 itcfg; /* FM IRAM timing config register */
+ u32 iready; /* FM IRAM ready register */
+};
+
+struct fman_fpm_regs {
+ u32 fmfp_tnc; /* FPM TNUM Control 0x00 */
+ u32 fmfp_prc; /* FPM Port_ID FmCtl Association 0x04 */
+ u32 fmfp_brkc; /* FPM Breakpoint Control 0x08 */
+ u32 fmfp_mxd; /* FPM Flush Control 0x0c */
+ u32 fmfp_dist1; /* FPM Dispatch Thresholds1 0x10 */
+ u32 fmfp_dist2; /* FPM Dispatch Thresholds2 0x14 */
+ u32 fm_epi; /* FM Error Pending Interrupts 0x18 */
+ u32 fm_rie; /* FM Error Interrupt Enable 0x1c */
+ u32 fmfp_fcev[4]; /* FPM FMan-Controller Event 1-4 0x20-0x2f */
+ u32 res0030[4]; /* res 0x30 - 0x3f */
+ u32 fmfp_cee[4]; /* PM FMan-Controller Event 1-4 0x40-0x4f */
+ u32 res0050[4]; /* res 0x50-0x5f */
+ u32 fmfp_tsc1; /* FPM TimeStamp Control1 0x60 */
+ u32 fmfp_tsc2; /* FPM TimeStamp Control2 0x64 */
+ u32 fmfp_tsp; /* FPM Time Stamp 0x68 */
+ u32 fmfp_tsf; /* FPM Time Stamp Fraction 0x6c */
+ u32 fm_rcr; /* FM Rams Control 0x70 */
+ u32 fmfp_extc; /* FPM External Requests Control 0x74 */
+ u32 fmfp_ext1; /* FPM External Requests Config1 0x78 */
+ u32 fmfp_ext2; /* FPM External Requests Config2 0x7c */
+ u32 fmfp_drd[16]; /* FPM Data_Ram Data 0-15 0x80 - 0xbf */
+ u32 fmfp_dra; /* FPM Data Ram Access 0xc0 */
+ u32 fm_ip_rev_1; /* FM IP Block Revision 1 0xc4 */
+ u32 fm_ip_rev_2; /* FM IP Block Revision 2 0xc8 */
+ u32 fm_rstc; /* FM Reset Command 0xcc */
+ u32 fm_cld; /* FM Classifier Debug 0xd0 */
+ u32 fm_npi; /* FM Normal Pending Interrupts 0xd4 */
+ u32 fmfp_exte; /* FPM External Requests Enable 0xd8 */
+ u32 fmfp_ee; /* FPM Event&Mask 0xdc */
+ u32 fmfp_cev[4]; /* FPM CPU Event 1-4 0xe0-0xef */
+ u32 res00f0[4]; /* res 0xf0-0xff */
+ u32 fmfp_ps[50]; /* FPM Port Status 0x100-0x1c7 */
+ u32 res01c8[14]; /* res 0x1c8-0x1ff */
+ u32 fmfp_clfabc; /* FPM CLFABC 0x200 */
+ u32 fmfp_clfcc; /* FPM CLFCC 0x204 */
+ u32 fmfp_clfaval; /* FPM CLFAVAL 0x208 */
+ u32 fmfp_clfbval; /* FPM CLFBVAL 0x20c */
+ u32 fmfp_clfcval; /* FPM CLFCVAL 0x210 */
+ u32 fmfp_clfamsk; /* FPM CLFAMSK 0x214 */
+ u32 fmfp_clfbmsk; /* FPM CLFBMSK 0x218 */
+ u32 fmfp_clfcmsk; /* FPM CLFCMSK 0x21c */
+ u32 fmfp_clfamc; /* FPM CLFAMC 0x220 */
+ u32 fmfp_clfbmc; /* FPM CLFBMC 0x224 */
+ u32 fmfp_clfcmc; /* FPM CLFCMC 0x228 */
+ u32 fmfp_decceh; /* FPM DECCEH 0x22c */
+ u32 res0230[116]; /* res 0x230 - 0x3ff */
+ u32 fmfp_ts[128]; /* 0x400: FPM Task Status 0x400 - 0x5ff */
+ u32 res0600[0x400 - 384];
+};
+
+struct fman_bmi_regs {
+ u32 fmbm_init; /* BMI Initialization 0x00 */
+ u32 fmbm_cfg1; /* BMI Configuration 1 0x04 */
+ u32 fmbm_cfg2; /* BMI Configuration 2 0x08 */
+ u32 res000c[5]; /* 0x0c - 0x1f */
+ u32 fmbm_ievr; /* Interrupt Event Register 0x20 */
+ u32 fmbm_ier; /* Interrupt Enable Register 0x24 */
+ u32 fmbm_ifr; /* Interrupt Force Register 0x28 */
+ u32 res002c[5]; /* 0x2c - 0x3f */
+ u32 fmbm_arb[8]; /* BMI Arbitration 0x40 - 0x5f */
+ u32 res0060[12]; /* 0x60 - 0x8f */
+ u32 fmbm_dtc[3]; /* Debug Trap Counter 0x90 - 0x9b */
+ u32 res009c; /* 0x9c */
+ u32 fmbm_dcv[3][4]; /* Debug Compare val 0xa0-0xcf */
+ u32 fmbm_dcm[3][4]; /* Debug Compare Mask 0xd0-0xff */
+ u32 fmbm_gde; /* BMI Global Debug Enable 0x100 */
+ u32 fmbm_pp[63]; /* BMI Port Parameters 0x104 - 0x1ff */
+ u32 res0200; /* 0x200 */
+ u32 fmbm_pfs[63]; /* BMI Port FIFO Size 0x204 - 0x2ff */
+ u32 res0300; /* 0x300 */
+ u32 fmbm_spliodn[63]; /* Port Partition ID 0x304 - 0x3ff */
+};
+
+struct fman_qmi_regs {
+ u32 fmqm_gc; /* General Configuration Register 0x00 */
+ u32 res0004; /* 0x04 */
+ u32 fmqm_eie; /* Error Interrupt Event Register 0x08 */
+ u32 fmqm_eien; /* Error Interrupt Enable Register 0x0c */
+ u32 fmqm_eif; /* Error Interrupt Force Register 0x10 */
+ u32 fmqm_ie; /* Interrupt Event Register 0x14 */
+ u32 fmqm_ien; /* Interrupt Enable Register 0x18 */
+ u32 fmqm_if; /* Interrupt Force Register 0x1c */
+ u32 fmqm_gs; /* Global Status Register 0x20 */
+ u32 fmqm_ts; /* Task Status Register 0x24 */
+ u32 fmqm_etfc; /* Enqueue Total Frame Counter 0x28 */
+ u32 fmqm_dtfc; /* Dequeue Total Frame Counter 0x2c */
+ u32 fmqm_dc0; /* Dequeue Counter 0 0x30 */
+ u32 fmqm_dc1; /* Dequeue Counter 1 0x34 */
+ u32 fmqm_dc2; /* Dequeue Counter 2 0x38 */
+ u32 fmqm_dc3; /* Dequeue Counter 3 0x3c */
+ u32 fmqm_dfdc; /* Dequeue FQID from Default Counter 0x40 */
+ u32 fmqm_dfcc; /* Dequeue FQID from Context Counter 0x44 */
+ u32 fmqm_dffc; /* Dequeue FQID from FD Counter 0x48 */
+ u32 fmqm_dcc; /* Dequeue Confirm Counter 0x4c */
+ u32 res0050[7]; /* 0x50 - 0x6b */
+ u32 fmqm_tapc; /* Tnum Aging Period Control 0x6c */
+ u32 fmqm_dmcvc; /* Dequeue MAC Command Valid Counter 0x70 */
+ u32 fmqm_difdcc; /* Dequeue Invalid FD Command Counter 0x74 */
+ u32 fmqm_da1v; /* Dequeue A1 Valid Counter 0x78 */
+ u32 res007c; /* 0x7c */
+ u32 fmqm_dtc; /* 0x80 Debug Trap Counter 0x80 */
+ u32 fmqm_efddd; /* 0x84 Enqueue Frame desc Dynamic dbg 0x84 */
+ u32 res0088[2]; /* 0x88 - 0x8f */
+ struct {
+ u32 fmqm_dtcfg1; /* 0x90 dbg trap cfg 1 Register 0x00 */
+ u32 fmqm_dtval1; /* Debug Trap Value 1 Register 0x04 */
+ u32 fmqm_dtm1; /* Debug Trap Mask 1 Register 0x08 */
+ u32 fmqm_dtc1; /* Debug Trap Counter 1 Register 0x0c */
+ u32 fmqm_dtcfg2; /* dbg Trap cfg 2 Register 0x10 */
+ u32 fmqm_dtval2; /* Debug Trap Value 2 Register 0x14 */
+ u32 fmqm_dtm2; /* Debug Trap Mask 2 Register 0x18 */
+ u32 res001c; /* 0x1c */
+ } dbg_traps[3]; /* 0x90 - 0xef */
+ u8 res00f0[0x400 - 0xf0]; /* 0xf0 - 0x3ff */
+};
+
+struct fman_dma_regs {
+ u32 fmdmsr; /* FM DMA status register 0x00 */
+ u32 fmdmmr; /* FM DMA mode register 0x04 */
+ u32 fmdmtr; /* FM DMA bus threshold register 0x08 */
+ u32 fmdmhy; /* FM DMA bus hysteresis register 0x0c */
+ u32 fmdmsetr; /* FM DMA SOS emergency Threshold Register 0x10 */
+ u32 fmdmtah; /* FM DMA transfer bus address high reg 0x14 */
+ u32 fmdmtal; /* FM DMA transfer bus address low reg 0x18 */
+ u32 fmdmtcid; /* FM DMA transfer bus communication ID reg 0x1c */
+ u32 fmdmra; /* FM DMA bus internal ram address register 0x20 */
+ u32 fmdmrd; /* FM DMA bus internal ram data register 0x24 */
+ u32 fmdmwcr; /* FM DMA CAM watchdog counter value 0x28 */
+ u32 fmdmebcr; /* FM DMA CAM base in MURAM register 0x2c */
+ u32 fmdmccqdr; /* FM DMA CAM and CMD Queue Debug reg 0x30 */
+ u32 fmdmccqvr1; /* FM DMA CAM and CMD Queue Value reg #1 0x34 */
+ u32 fmdmccqvr2; /* FM DMA CAM and CMD Queue Value reg #2 0x38 */
+ u32 fmdmcqvr3; /* FM DMA CMD Queue Value register #3 0x3c */
+ u32 fmdmcqvr4; /* FM DMA CMD Queue Value register #4 0x40 */
+ u32 fmdmcqvr5; /* FM DMA CMD Queue Value register #5 0x44 */
+ u32 fmdmsefrc; /* FM DMA Semaphore Entry Full Reject Cntr 0x48 */
+ u32 fmdmsqfrc; /* FM DMA Semaphore Queue Full Reject Cntr 0x4c */
+ u32 fmdmssrc; /* FM DMA Semaphore SYNC Reject Counter 0x50 */
+ u32 fmdmdcr; /* FM DMA Debug Counter 0x54 */
+ u32 fmdmemsr; /* FM DMA Emergency Smoother Register 0x58 */
+ u32 res005c; /* 0x5c */
+ u32 fmdmplr[FMAN_LIODN_TBL / 2]; /* DMA LIODN regs 0x60-0xdf */
+ u32 res00e0[0x400 - 56];
+};
+
+/* Structure that holds current FMan state.
+ * Used for saving run time information.
+ */
+struct fman_state_struct {
+ u8 fm_id;
+ u16 fm_clk_freq;
+ struct fman_rev_info rev_info;
+ bool enabled_time_stamp;
+ u8 count1_micro_bit;
+ u8 total_num_of_tasks;
+ u8 accumulated_num_of_tasks;
+ u32 accumulated_fifo_size;
+ u8 accumulated_num_of_open_dmas;
+ u8 accumulated_num_of_deq_tnums;
+ u32 exceptions;
+ u32 extra_fifo_pool_size;
+ u8 extra_tasks_pool_size;
+ u8 extra_open_dmas_pool_size;
+ u16 port_mfl[MAX_NUM_OF_MACS];
+ u16 mac_mfl[MAX_NUM_OF_MACS];
+
+ /* SOC specific */
+ u32 fm_iram_size;
+ /* DMA */
+ u32 dma_thresh_max_commq;
+ u32 dma_thresh_max_buf;
+ u32 max_num_of_open_dmas;
+ /* QMI */
+ u32 qmi_max_num_of_tnums;
+ u32 qmi_def_tnums_thresh;
+ /* BMI */
+ u32 bmi_max_num_of_tasks;
+ u32 bmi_max_fifo_size;
+ /* General */
+ u32 fm_port_num_of_cg;
+ u32 num_of_rx_ports;
+ u32 total_fifo_size;
+
+ u32 qman_channel_base;
+ u32 num_of_qman_channels;
+
+ struct resource *res;
+};
+
+/* Structure that holds FMan initial configuration */
+struct fman_cfg {
+ u8 disp_limit_tsh;
+ u8 prs_disp_tsh;
+ u8 plcr_disp_tsh;
+ u8 kg_disp_tsh;
+ u8 bmi_disp_tsh;
+ u8 qmi_enq_disp_tsh;
+ u8 qmi_deq_disp_tsh;
+ u8 fm_ctl1_disp_tsh;
+ u8 fm_ctl2_disp_tsh;
+ int dma_cache_override;
+ enum fman_dma_aid_mode dma_aid_mode;
+ u32 dma_axi_dbg_num_of_beats;
+ u32 dma_cam_num_of_entries;
+ u32 dma_watchdog;
+ u8 dma_comm_qtsh_asrt_emer;
+ u32 dma_write_buf_tsh_asrt_emer;
+ u32 dma_read_buf_tsh_asrt_emer;
+ u8 dma_comm_qtsh_clr_emer;
+ u32 dma_write_buf_tsh_clr_emer;
+ u32 dma_read_buf_tsh_clr_emer;
+ u32 dma_sos_emergency;
+ int dma_dbg_cnt_mode;
+ int catastrophic_err;
+ int dma_err;
+ u32 exceptions;
+ u16 clk_freq;
+ u32 cam_base_addr;
+ u32 fifo_base_addr;
+ u32 total_fifo_size;
+ u32 total_num_of_tasks;
+ u32 qmi_def_tnums_thresh;
+};
+
+/* Structure that holds information received from device tree */
+struct fman_dts_params {
+ void __iomem *base_addr; /* FMan virtual address */
+ struct resource *res; /* FMan memory resource */
+ u8 id; /* FMan ID */
+
+ int err_irq; /* FMan Error IRQ */
+
+ u16 clk_freq; /* FMan clock freq (In Mhz) */
+
+ u32 qman_channel_base; /* QMan channels base */
+ u32 num_of_qman_channels; /* Number of QMan channels */
+
+ struct resource muram_res; /* MURAM resource */
+};
+
+/** fman_exceptions_cb
+ * fman - Pointer to FMan
+ * exception - The exception.
+ *
+ * Exceptions user callback routine, will be called upon an exception
+ * passing the exception identification.
+ *
+ * Return: irq status
+ */
+typedef irqreturn_t (fman_exceptions_cb)(struct fman *fman,
+ enum fman_exceptions exception);
+
+/** fman_bus_error_cb
+ * fman - Pointer to FMan
+ * port_id - Port id
+ * addr - Address that caused the error
+ * tnum - Owner of error
+ * liodn - Logical IO device number
+ *
+ * Bus error user callback routine, will be called upon bus error,
+ * passing parameters describing the errors and the owner.
+ *
+ * Return: IRQ status
+ */
+typedef irqreturn_t (fman_bus_error_cb)(struct fman *fman, u8 port_id,
+ u64 addr, u8 tnum, u16 liodn);
+
+struct fman {
+ struct device *dev;
+ void __iomem *base_addr;
+ struct fman_intr_src intr_mng[FMAN_EV_CNT];
+
+ struct fman_fpm_regs __iomem *fpm_regs;
+ struct fman_bmi_regs __iomem *bmi_regs;
+ struct fman_qmi_regs __iomem *qmi_regs;
+ struct fman_dma_regs __iomem *dma_regs;
+ fman_exceptions_cb *exception_cb;
+ fman_bus_error_cb *bus_error_cb;
+ /* Spinlock for FMan use */
+ spinlock_t spinlock;
+ struct fman_state_struct *state;
+
+ struct fman_cfg *cfg;
+ struct muram_info *muram;
+ /* cam section in muram */
+ int cam_offset;
+ size_t cam_size;
+ /* Fifo in MURAM */
+ int fifo_offset;
+ size_t fifo_size;
+
+ u32 liodn_base[64];
+ u32 liodn_offset[64];
+
+ struct fman_dts_params dts_params;
+};
+
+static irqreturn_t fman_exceptions(struct fman *fman,
+ enum fman_exceptions exception)
+{
+ dev_dbg(fman->dev, "%s: FMan[%d] exception %d\n",
+ __func__, fman->state->fm_id, exception);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fman_bus_error(struct fman *fman, u8 __maybe_unused port_id,
+ u64 __maybe_unused addr,
+ u8 __maybe_unused tnum,
+ u16 __maybe_unused liodn)
+{
+ dev_dbg(fman->dev, "%s: FMan[%d] bus error: port_id[%d]\n",
+ __func__, fman->state->fm_id, port_id);
+
+ return IRQ_HANDLED;
+}
+
+static inline irqreturn_t call_mac_isr(struct fman *fman, u8 id)
+{
+ if (fman->intr_mng[id].isr_cb) {
+ fman->intr_mng[id].isr_cb(fman->intr_mng[id].src_handle);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static inline u8 hw_port_id_to_sw_port_id(u8 major, u8 hw_port_id)
+{
+ u8 sw_port_id = 0;
+
+ if (hw_port_id >= BASE_TX_PORTID)
+ sw_port_id = hw_port_id - BASE_TX_PORTID;
+ else if (hw_port_id >= BASE_RX_PORTID)
+ sw_port_id = hw_port_id - BASE_RX_PORTID;
+ else
+ sw_port_id = 0;
+
+ return sw_port_id;
+}
+
+static void set_port_order_restoration(struct fman_fpm_regs __iomem *fpm_rg,
+ u8 port_id)
+{
+ u32 tmp = 0;
+
+ tmp = port_id << FPM_PORT_FM_CTL_PORTID_SHIFT;
+
+ tmp |= FPM_PRT_FM_CTL2 | FPM_PRT_FM_CTL1;
+
+ /* order restoration */
+ if (port_id % 2)
+ tmp |= FPM_PRT_FM_CTL1 << FPM_PRC_ORA_FM_CTL_SEL_SHIFT;
+ else
+ tmp |= FPM_PRT_FM_CTL2 << FPM_PRC_ORA_FM_CTL_SEL_SHIFT;
+
+ iowrite32be(tmp, &fpm_rg->fmfp_prc);
+}
+
+static void set_port_liodn(struct fman *fman, u8 port_id,
+ u32 liodn_base, u32 liodn_ofst)
+{
+ u32 tmp;
+
+ /* set LIODN base for this port */
+ tmp = ioread32be(&fman->dma_regs->fmdmplr[port_id / 2]);
+ if (port_id % 2) {
+ tmp &= ~DMA_LIODN_BASE_MASK;
+ tmp |= liodn_base;
+ } else {
+ tmp &= ~(DMA_LIODN_BASE_MASK << DMA_LIODN_SHIFT);
+ tmp |= liodn_base << DMA_LIODN_SHIFT;
+ }
+ iowrite32be(tmp, &fman->dma_regs->fmdmplr[port_id / 2]);
+ iowrite32be(liodn_ofst, &fman->bmi_regs->fmbm_spliodn[port_id - 1]);
+}
+
+static void enable_rams_ecc(struct fman_fpm_regs __iomem *fpm_rg)
+{
+ u32 tmp;
+
+ tmp = ioread32be(&fpm_rg->fm_rcr);
+ if (tmp & FPM_RAM_RAMS_ECC_EN_SRC_SEL)
+ iowrite32be(tmp | FPM_RAM_IRAM_ECC_EN, &fpm_rg->fm_rcr);
+ else
+ iowrite32be(tmp | FPM_RAM_RAMS_ECC_EN |
+ FPM_RAM_IRAM_ECC_EN, &fpm_rg->fm_rcr);
+}
+
+static void disable_rams_ecc(struct fman_fpm_regs __iomem *fpm_rg)
+{
+ u32 tmp;
+
+ tmp = ioread32be(&fpm_rg->fm_rcr);
+ if (tmp & FPM_RAM_RAMS_ECC_EN_SRC_SEL)
+ iowrite32be(tmp & ~FPM_RAM_IRAM_ECC_EN, &fpm_rg->fm_rcr);
+ else
+ iowrite32be(tmp & ~(FPM_RAM_RAMS_ECC_EN | FPM_RAM_IRAM_ECC_EN),
+ &fpm_rg->fm_rcr);
+}
+
+static void fman_defconfig(struct fman_cfg *cfg)
+{
+ memset(cfg, 0, sizeof(struct fman_cfg));
+
+ cfg->catastrophic_err = DEFAULT_CATASTROPHIC_ERR;
+ cfg->dma_err = DEFAULT_DMA_ERR;
+ cfg->dma_aid_mode = DEFAULT_AID_MODE;
+ cfg->dma_comm_qtsh_clr_emer = DEFAULT_DMA_COMM_Q_LOW;
+ cfg->dma_comm_qtsh_asrt_emer = DEFAULT_DMA_COMM_Q_HIGH;
+ cfg->dma_cache_override = DEFAULT_CACHE_OVERRIDE;
+ cfg->dma_cam_num_of_entries = DEFAULT_DMA_CAM_NUM_OF_ENTRIES;
+ cfg->dma_dbg_cnt_mode = DEFAULT_DMA_DBG_CNT_MODE;
+ cfg->dma_sos_emergency = DEFAULT_DMA_SOS_EMERGENCY;
+ cfg->dma_watchdog = DEFAULT_DMA_WATCHDOG;
+ cfg->disp_limit_tsh = DEFAULT_DISP_LIMIT;
+ cfg->prs_disp_tsh = DEFAULT_PRS_DISP_TH;
+ cfg->plcr_disp_tsh = DEFAULT_PLCR_DISP_TH;
+ cfg->kg_disp_tsh = DEFAULT_KG_DISP_TH;
+ cfg->bmi_disp_tsh = DEFAULT_BMI_DISP_TH;
+ cfg->qmi_enq_disp_tsh = DEFAULT_QMI_ENQ_DISP_TH;
+ cfg->qmi_deq_disp_tsh = DEFAULT_QMI_DEQ_DISP_TH;
+ cfg->fm_ctl1_disp_tsh = DEFAULT_FM_CTL1_DISP_TH;
+ cfg->fm_ctl2_disp_tsh = DEFAULT_FM_CTL2_DISP_TH;
+}
+
+static int dma_init(struct fman *fman)
+{
+ struct fman_dma_regs __iomem *dma_rg = fman->dma_regs;
+ struct fman_cfg *cfg = fman->cfg;
+ u32 tmp_reg;
+
+ /* Init DMA Registers */
+
+ /* clear status reg events */
+ tmp_reg = (DMA_STATUS_BUS_ERR | DMA_STATUS_READ_ECC |
+ DMA_STATUS_SYSTEM_WRITE_ECC | DMA_STATUS_FM_WRITE_ECC);
+ iowrite32be(ioread32be(&dma_rg->fmdmsr) | tmp_reg, &dma_rg->fmdmsr);
+
+ /* configure mode register */
+ tmp_reg = 0;
+ tmp_reg |= cfg->dma_cache_override << DMA_MODE_CACHE_OR_SHIFT;
+ if (cfg->exceptions & EX_DMA_BUS_ERROR)
+ tmp_reg |= DMA_MODE_BER;
+ if ((cfg->exceptions & EX_DMA_SYSTEM_WRITE_ECC) |
+ (cfg->exceptions & EX_DMA_READ_ECC) |
+ (cfg->exceptions & EX_DMA_FM_WRITE_ECC))
+ tmp_reg |= DMA_MODE_ECC;
+ if (cfg->dma_axi_dbg_num_of_beats)
+ tmp_reg |= (DMA_MODE_AXI_DBG_MASK &
+ ((cfg->dma_axi_dbg_num_of_beats - 1)
+ << DMA_MODE_AXI_DBG_SHIFT));
+
+ tmp_reg |= (((cfg->dma_cam_num_of_entries / DMA_CAM_UNITS) - 1) &
+ DMA_MODE_CEN_MASK) << DMA_MODE_CEN_SHIFT;
+ tmp_reg |= DMA_MODE_SECURE_PROT;
+ tmp_reg |= cfg->dma_dbg_cnt_mode << DMA_MODE_DBG_SHIFT;
+ tmp_reg |= cfg->dma_aid_mode << DMA_MODE_AID_MODE_SHIFT;
+
+ iowrite32be(tmp_reg, &dma_rg->fmdmmr);
+
+ /* configure thresholds register */
+ tmp_reg = ((u32)cfg->dma_comm_qtsh_asrt_emer <<
+ DMA_THRESH_COMMQ_SHIFT);
+ tmp_reg |= (cfg->dma_read_buf_tsh_asrt_emer &
+ DMA_THRESH_READ_INT_BUF_MASK) << DMA_THRESH_READ_INT_BUF_SHIFT;
+ tmp_reg |= cfg->dma_write_buf_tsh_asrt_emer &
+ DMA_THRESH_WRITE_INT_BUF_MASK;
+
+ iowrite32be(tmp_reg, &dma_rg->fmdmtr);
+
+ /* configure hysteresis register */
+ tmp_reg = ((u32)cfg->dma_comm_qtsh_clr_emer <<
+ DMA_THRESH_COMMQ_SHIFT);
+ tmp_reg |= (cfg->dma_read_buf_tsh_clr_emer &
+ DMA_THRESH_READ_INT_BUF_MASK) << DMA_THRESH_READ_INT_BUF_SHIFT;
+ tmp_reg |= cfg->dma_write_buf_tsh_clr_emer &
+ DMA_THRESH_WRITE_INT_BUF_MASK;
+
+ iowrite32be(tmp_reg, &dma_rg->fmdmhy);
+
+ /* configure emergency threshold */
+ iowrite32be(cfg->dma_sos_emergency, &dma_rg->fmdmsetr);
+
+ /* configure Watchdog */
+ iowrite32be((cfg->dma_watchdog * cfg->clk_freq), &dma_rg->fmdmwcr);
+
+ iowrite32be(cfg->cam_base_addr, &dma_rg->fmdmebcr);
+
+ /* Allocate MURAM for CAM */
+ fman->cam_size =
+ (u32)(fman->cfg->dma_cam_num_of_entries * DMA_CAM_SIZEOF_ENTRY);
+ fman->cam_offset = fman_muram_alloc(fman->muram, fman->cam_size);
+ if (IS_ERR_VALUE(fman->cam_offset)) {
+ dev_err(fman->dev, "%s: MURAM alloc for DMA CAM failed\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ if (fman->state->rev_info.major == 2) {
+ u32 __iomem *cam_base_addr;
+
+ fman_muram_free_mem(fman->muram, fman->cam_offset,
+ fman->cam_size);
+
+ fman->cam_size = fman->cfg->dma_cam_num_of_entries * 72 + 128;
+ fman->cam_offset = fman_muram_alloc(fman->muram,
+ fman->cam_size);
+ if (IS_ERR_VALUE(fman->cam_offset)) {
+ dev_err(fman->dev, "%s: MURAM alloc for DMA CAM failed\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ if (fman->cfg->dma_cam_num_of_entries % 8 ||
+ fman->cfg->dma_cam_num_of_entries > 32) {
+ dev_err(fman->dev, "%s: wrong dma_cam_num_of_entries\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ cam_base_addr = (u32 __iomem *)
+ fman_muram_offset_to_vbase(fman->muram,
+ fman->cam_offset);
+ iowrite32be(~((1 <<
+ (32 - fman->cfg->dma_cam_num_of_entries)) - 1),
+ cam_base_addr);
+ }
+
+ fman->cfg->cam_base_addr = fman->cam_offset;
+
+ return 0;
+}
+
+static void fpm_init(struct fman_fpm_regs __iomem *fpm_rg, struct fman_cfg *cfg)
+{
+ u32 tmp_reg;
+ int i;
+
+ /* Init FPM Registers */
+
+ tmp_reg = (u32)(cfg->disp_limit_tsh << FPM_DISP_LIMIT_SHIFT);
+ iowrite32be(tmp_reg, &fpm_rg->fmfp_mxd);
+
+ tmp_reg = (((u32)cfg->prs_disp_tsh << FPM_THR1_PRS_SHIFT) |
+ ((u32)cfg->kg_disp_tsh << FPM_THR1_KG_SHIFT) |
+ ((u32)cfg->plcr_disp_tsh << FPM_THR1_PLCR_SHIFT) |
+ ((u32)cfg->bmi_disp_tsh << FPM_THR1_BMI_SHIFT));
+ iowrite32be(tmp_reg, &fpm_rg->fmfp_dist1);
+
+ tmp_reg =
+ (((u32)cfg->qmi_enq_disp_tsh << FPM_THR2_QMI_ENQ_SHIFT) |
+ ((u32)cfg->qmi_deq_disp_tsh << FPM_THR2_QMI_DEQ_SHIFT) |
+ ((u32)cfg->fm_ctl1_disp_tsh << FPM_THR2_FM_CTL1_SHIFT) |
+ ((u32)cfg->fm_ctl2_disp_tsh << FPM_THR2_FM_CTL2_SHIFT));
+ iowrite32be(tmp_reg, &fpm_rg->fmfp_dist2);
+
+ /* define exceptions and error behavior */
+ tmp_reg = 0;
+ /* Clear events */
+ tmp_reg |= (FPM_EV_MASK_STALL | FPM_EV_MASK_DOUBLE_ECC |
+ FPM_EV_MASK_SINGLE_ECC);
+ /* enable interrupts */
+ if (cfg->exceptions & EX_FPM_STALL_ON_TASKS)
+ tmp_reg |= FPM_EV_MASK_STALL_EN;
+ if (cfg->exceptions & EX_FPM_SINGLE_ECC)
+ tmp_reg |= FPM_EV_MASK_SINGLE_ECC_EN;
+ if (cfg->exceptions & EX_FPM_DOUBLE_ECC)
+ tmp_reg |= FPM_EV_MASK_DOUBLE_ECC_EN;
+ tmp_reg |= (cfg->catastrophic_err << FPM_EV_MASK_CAT_ERR_SHIFT);
+ tmp_reg |= (cfg->dma_err << FPM_EV_MASK_DMA_ERR_SHIFT);
+ /* FMan is not halted upon external halt activation */
+ tmp_reg |= FPM_EV_MASK_EXTERNAL_HALT;
+ /* Man is not halted upon Unrecoverable ECC error behavior */
+ tmp_reg |= FPM_EV_MASK_ECC_ERR_HALT;
+ iowrite32be(tmp_reg, &fpm_rg->fmfp_ee);
+
+ /* clear all fmCtls event registers */
+ for (i = 0; i < FM_NUM_OF_FMAN_CTRL_EVENT_REGS; i++)
+ iowrite32be(0xFFFFFFFF, &fpm_rg->fmfp_cev[i]);
+
+ /* RAM ECC - enable and clear events */
+ /* first we need to clear all parser memory,
+ * as it is uninitialized and may cause ECC errors
+ */
+ /* event bits */
+ tmp_reg = (FPM_RAM_MURAM_ECC | FPM_RAM_IRAM_ECC);
+
+ iowrite32be(tmp_reg, &fpm_rg->fm_rcr);
+
+ tmp_reg = 0;
+ if (cfg->exceptions & EX_IRAM_ECC) {
+ tmp_reg |= FPM_IRAM_ECC_ERR_EX_EN;
+ enable_rams_ecc(fpm_rg);
+ }
+ if (cfg->exceptions & EX_MURAM_ECC) {
+ tmp_reg |= FPM_MURAM_ECC_ERR_EX_EN;
+ enable_rams_ecc(fpm_rg);
+ }
+ iowrite32be(tmp_reg, &fpm_rg->fm_rie);
+}
+
+static void bmi_init(struct fman_bmi_regs __iomem *bmi_rg,
+ struct fman_cfg *cfg)
+{
+ u32 tmp_reg;
+
+ /* Init BMI Registers */
+
+ /* define common resources */
+ tmp_reg = cfg->fifo_base_addr;
+ tmp_reg = tmp_reg / BMI_FIFO_ALIGN;
+
+ tmp_reg |= ((cfg->total_fifo_size / FMAN_BMI_FIFO_UNITS - 1) <<
+ BMI_CFG1_FIFO_SIZE_SHIFT);
+ iowrite32be(tmp_reg, &bmi_rg->fmbm_cfg1);
+
+ tmp_reg = ((cfg->total_num_of_tasks - 1) & BMI_CFG2_TASKS_MASK) <<
+ BMI_CFG2_TASKS_SHIFT;
+ /* num of DMA's will be dynamically updated when each port is set */
+ iowrite32be(tmp_reg, &bmi_rg->fmbm_cfg2);
+
+ /* define unmaskable exceptions, enable and clear events */
+ tmp_reg = 0;
+ iowrite32be(BMI_ERR_INTR_EN_LIST_RAM_ECC |
+ BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC |
+ BMI_ERR_INTR_EN_STATISTICS_RAM_ECC |
+ BMI_ERR_INTR_EN_DISPATCH_RAM_ECC, &bmi_rg->fmbm_ievr);
+
+ if (cfg->exceptions & EX_BMI_LIST_RAM_ECC)
+ tmp_reg |= BMI_ERR_INTR_EN_LIST_RAM_ECC;
+ if (cfg->exceptions & EX_BMI_STORAGE_PROFILE_ECC)
+ tmp_reg |= BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC;
+ if (cfg->exceptions & EX_BMI_STATISTICS_RAM_ECC)
+ tmp_reg |= BMI_ERR_INTR_EN_STATISTICS_RAM_ECC;
+ if (cfg->exceptions & EX_BMI_DISPATCH_RAM_ECC)
+ tmp_reg |= BMI_ERR_INTR_EN_DISPATCH_RAM_ECC;
+ iowrite32be(tmp_reg, &bmi_rg->fmbm_ier);
+}
+
+static void qmi_init(struct fman_qmi_regs __iomem *qmi_rg,
+ struct fman_cfg *cfg)
+{
+ u32 tmp_reg;
+
+ /* Init QMI Registers */
+
+ /* Clear error interrupt events */
+
+ iowrite32be(QMI_ERR_INTR_EN_DOUBLE_ECC | QMI_ERR_INTR_EN_DEQ_FROM_DEF,
+ &qmi_rg->fmqm_eie);
+ tmp_reg = 0;
+ if (cfg->exceptions & EX_QMI_DEQ_FROM_UNKNOWN_PORTID)
+ tmp_reg |= QMI_ERR_INTR_EN_DEQ_FROM_DEF;
+ if (cfg->exceptions & EX_QMI_DOUBLE_ECC)
+ tmp_reg |= QMI_ERR_INTR_EN_DOUBLE_ECC;
+ /* enable events */
+ iowrite32be(tmp_reg, &qmi_rg->fmqm_eien);
+
+ tmp_reg = 0;
+ /* Clear interrupt events */
+ iowrite32be(QMI_INTR_EN_SINGLE_ECC, &qmi_rg->fmqm_ie);
+ if (cfg->exceptions & EX_QMI_SINGLE_ECC)
+ tmp_reg |= QMI_INTR_EN_SINGLE_ECC;
+ /* enable events */
+ iowrite32be(tmp_reg, &qmi_rg->fmqm_ien);
+}
+
+static int enable(struct fman *fman, struct fman_cfg *cfg)
+{
+ u32 cfg_reg = 0;
+
+ /* Enable all modules */
+
+ /* clear&enable global counters - calculate reg and save for later,
+ * because it's the same reg for QMI enable
+ */
+ cfg_reg = QMI_CFG_EN_COUNTERS;
+
+ /* Set enqueue and dequeue thresholds */
+ cfg_reg |= (cfg->qmi_def_tnums_thresh << 8) | cfg->qmi_def_tnums_thresh;
+
+ iowrite32be(BMI_INIT_START, &fman->bmi_regs->fmbm_init);
+ iowrite32be(cfg_reg | QMI_CFG_ENQ_EN | QMI_CFG_DEQ_EN,
+ &fman->qmi_regs->fmqm_gc);
+
+ return 0;
+}
+
+static int set_exception(struct fman *fman,
+ enum fman_exceptions exception, bool enable)
+{
+ u32 tmp;
+
+ switch (exception) {
+ case FMAN_EX_DMA_BUS_ERROR:
+ tmp = ioread32be(&fman->dma_regs->fmdmmr);
+ if (enable)
+ tmp |= DMA_MODE_BER;
+ else
+ tmp &= ~DMA_MODE_BER;
+ /* disable bus error */
+ iowrite32be(tmp, &fman->dma_regs->fmdmmr);
+ break;
+ case FMAN_EX_DMA_READ_ECC:
+ case FMAN_EX_DMA_SYSTEM_WRITE_ECC:
+ case FMAN_EX_DMA_FM_WRITE_ECC:
+ tmp = ioread32be(&fman->dma_regs->fmdmmr);
+ if (enable)
+ tmp |= DMA_MODE_ECC;
+ else
+ tmp &= ~DMA_MODE_ECC;
+ iowrite32be(tmp, &fman->dma_regs->fmdmmr);
+ break;
+ case FMAN_EX_FPM_STALL_ON_TASKS:
+ tmp = ioread32be(&fman->fpm_regs->fmfp_ee);
+ if (enable)
+ tmp |= FPM_EV_MASK_STALL_EN;
+ else
+ tmp &= ~FPM_EV_MASK_STALL_EN;
+ iowrite32be(tmp, &fman->fpm_regs->fmfp_ee);
+ break;
+ case FMAN_EX_FPM_SINGLE_ECC:
+ tmp = ioread32be(&fman->fpm_regs->fmfp_ee);
+ if (enable)
+ tmp |= FPM_EV_MASK_SINGLE_ECC_EN;
+ else
+ tmp &= ~FPM_EV_MASK_SINGLE_ECC_EN;
+ iowrite32be(tmp, &fman->fpm_regs->fmfp_ee);
+ break;
+ case FMAN_EX_FPM_DOUBLE_ECC:
+ tmp = ioread32be(&fman->fpm_regs->fmfp_ee);
+ if (enable)
+ tmp |= FPM_EV_MASK_DOUBLE_ECC_EN;
+ else
+ tmp &= ~FPM_EV_MASK_DOUBLE_ECC_EN;
+ iowrite32be(tmp, &fman->fpm_regs->fmfp_ee);
+ break;
+ case FMAN_EX_QMI_SINGLE_ECC:
+ tmp = ioread32be(&fman->qmi_regs->fmqm_ien);
+ if (enable)
+ tmp |= QMI_INTR_EN_SINGLE_ECC;
+ else
+ tmp &= ~QMI_INTR_EN_SINGLE_ECC;
+ iowrite32be(tmp, &fman->qmi_regs->fmqm_ien);
+ break;
+ case FMAN_EX_QMI_DOUBLE_ECC:
+ tmp = ioread32be(&fman->qmi_regs->fmqm_eien);
+ if (enable)
+ tmp |= QMI_ERR_INTR_EN_DOUBLE_ECC;
+ else
+ tmp &= ~QMI_ERR_INTR_EN_DOUBLE_ECC;
+ iowrite32be(tmp, &fman->qmi_regs->fmqm_eien);
+ break;
+ case FMAN_EX_QMI_DEQ_FROM_UNKNOWN_PORTID:
+ tmp = ioread32be(&fman->qmi_regs->fmqm_eien);
+ if (enable)
+ tmp |= QMI_ERR_INTR_EN_DEQ_FROM_DEF;
+ else
+ tmp &= ~QMI_ERR_INTR_EN_DEQ_FROM_DEF;
+ iowrite32be(tmp, &fman->qmi_regs->fmqm_eien);
+ break;
+ case FMAN_EX_BMI_LIST_RAM_ECC:
+ tmp = ioread32be(&fman->bmi_regs->fmbm_ier);
+ if (enable)
+ tmp |= BMI_ERR_INTR_EN_LIST_RAM_ECC;
+ else
+ tmp &= ~BMI_ERR_INTR_EN_LIST_RAM_ECC;
+ iowrite32be(tmp, &fman->bmi_regs->fmbm_ier);
+ break;
+ case FMAN_EX_BMI_STORAGE_PROFILE_ECC:
+ tmp = ioread32be(&fman->bmi_regs->fmbm_ier);
+ if (enable)
+ tmp |= BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC;
+ else
+ tmp &= ~BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC;
+ iowrite32be(tmp, &fman->bmi_regs->fmbm_ier);
+ break;
+ case FMAN_EX_BMI_STATISTICS_RAM_ECC:
+ tmp = ioread32be(&fman->bmi_regs->fmbm_ier);
+ if (enable)
+ tmp |= BMI_ERR_INTR_EN_STATISTICS_RAM_ECC;
+ else
+ tmp &= ~BMI_ERR_INTR_EN_STATISTICS_RAM_ECC;
+ iowrite32be(tmp, &fman->bmi_regs->fmbm_ier);
+ break;
+ case FMAN_EX_BMI_DISPATCH_RAM_ECC:
+ tmp = ioread32be(&fman->bmi_regs->fmbm_ier);
+ if (enable)
+ tmp |= BMI_ERR_INTR_EN_DISPATCH_RAM_ECC;
+ else
+ tmp &= ~BMI_ERR_INTR_EN_DISPATCH_RAM_ECC;
+ iowrite32be(tmp, &fman->bmi_regs->fmbm_ier);
+ break;
+ case FMAN_EX_IRAM_ECC:
+ tmp = ioread32be(&fman->fpm_regs->fm_rie);
+ if (enable) {
+ /* enable ECC if not enabled */
+ enable_rams_ecc(fman->fpm_regs);
+ /* enable ECC interrupts */
+ tmp |= FPM_IRAM_ECC_ERR_EX_EN;
+ } else {
+ /* ECC mechanism may be disabled,
+ * depending on driver status
+ */
+ disable_rams_ecc(fman->fpm_regs);
+ tmp &= ~FPM_IRAM_ECC_ERR_EX_EN;
+ }
+ iowrite32be(tmp, &fman->fpm_regs->fm_rie);
+ break;
+ case FMAN_EX_MURAM_ECC:
+ tmp = ioread32be(&fman->fpm_regs->fm_rie);
+ if (enable) {
+ /* enable ECC if not enabled */
+ enable_rams_ecc(fman->fpm_regs);
+ /* enable ECC interrupts */
+ tmp |= FPM_MURAM_ECC_ERR_EX_EN;
+ } else {
+ /* ECC mechanism may be disabled,
+ * depending on driver status
+ */
+ disable_rams_ecc(fman->fpm_regs);
+ tmp &= ~FPM_MURAM_ECC_ERR_EX_EN;
+ }
+ iowrite32be(tmp, &fman->fpm_regs->fm_rie);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void resume(struct fman_fpm_regs __iomem *fpm_rg)
+{
+ u32 tmp;
+
+ tmp = ioread32be(&fpm_rg->fmfp_ee);
+ /* clear tmp_reg event bits in order not to clear standing events */
+ tmp &= ~(FPM_EV_MASK_DOUBLE_ECC |
+ FPM_EV_MASK_STALL | FPM_EV_MASK_SINGLE_ECC);
+ tmp |= FPM_EV_MASK_RELEASE_FM;
+
+ iowrite32be(tmp, &fpm_rg->fmfp_ee);
+}
+
+static int fill_soc_specific_params(struct fman_state_struct *state)
+{
+ u8 minor = state->rev_info.minor;
+ /* P4080 - Major 2
+ * P2041/P3041/P5020/P5040 - Major 3
+ * Tx/Bx - Major 6
+ */
+ switch (state->rev_info.major) {
+ case 3:
+ state->bmi_max_fifo_size = 160 * 1024;
+ state->fm_iram_size = 64 * 1024;
+ state->dma_thresh_max_commq = 31;
+ state->dma_thresh_max_buf = 127;
+ state->qmi_max_num_of_tnums = 64;
+ state->qmi_def_tnums_thresh = 48;
+ state->bmi_max_num_of_tasks = 128;
+ state->max_num_of_open_dmas = 32;
+ state->fm_port_num_of_cg = 256;
+ state->num_of_rx_ports = 6;
+ state->total_fifo_size = 122 * 1024;
+ break;
+
+ case 2:
+ state->bmi_max_fifo_size = 160 * 1024;
+ state->fm_iram_size = 64 * 1024;
+ state->dma_thresh_max_commq = 31;
+ state->dma_thresh_max_buf = 127;
+ state->qmi_max_num_of_tnums = 64;
+ state->qmi_def_tnums_thresh = 48;
+ state->bmi_max_num_of_tasks = 128;
+ state->max_num_of_open_dmas = 32;
+ state->fm_port_num_of_cg = 256;
+ state->num_of_rx_ports = 5;
+ state->total_fifo_size = 100 * 1024;
+ break;
+
+ case 6:
+ state->dma_thresh_max_commq = 83;
+ state->dma_thresh_max_buf = 127;
+ state->qmi_max_num_of_tnums = 64;
+ state->qmi_def_tnums_thresh = 32;
+ state->fm_port_num_of_cg = 256;
+
+ /* FManV3L */
+ if (minor == 1 || minor == 4) {
+ state->bmi_max_fifo_size = 192 * 1024;
+ state->bmi_max_num_of_tasks = 64;
+ state->max_num_of_open_dmas = 32;
+ state->num_of_rx_ports = 5;
+ if (minor == 1)
+ state->fm_iram_size = 32 * 1024;
+ else
+ state->fm_iram_size = 64 * 1024;
+ state->total_fifo_size = 156 * 1024;
+ }
+ /* FManV3H */
+ else if (minor == 0 || minor == 2 || minor == 3) {
+ state->bmi_max_fifo_size = 384 * 1024;
+ state->fm_iram_size = 64 * 1024;
+ state->bmi_max_num_of_tasks = 128;
+ state->max_num_of_open_dmas = 84;
+ state->num_of_rx_ports = 8;
+ state->total_fifo_size = 295 * 1024;
+ } else {
+ pr_err("Unsupported FManv3 version\n");
+ return -EINVAL;
+ }
+
+ break;
+ default:
+ pr_err("Unsupported FMan version\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static bool is_init_done(struct fman_cfg *cfg)
+{
+ /* Checks if FMan driver parameters were initialized */
+ if (!cfg)
+ return true;
+
+ return false;
+}
+
+static void free_init_resources(struct fman *fman)
+{
+ if (fman->cam_offset)
+ fman_muram_free_mem(fman->muram, fman->cam_offset,
+ fman->cam_size);
+ if (fman->fifo_offset)
+ fman_muram_free_mem(fman->muram, fman->fifo_offset,
+ fman->fifo_size);
+}
+
+static irqreturn_t bmi_err_event(struct fman *fman)
+{
+ u32 event, mask, force;
+ struct fman_bmi_regs __iomem *bmi_rg = fman->bmi_regs;
+ irqreturn_t ret = IRQ_NONE;
+
+ event = ioread32be(&bmi_rg->fmbm_ievr);
+ mask = ioread32be(&bmi_rg->fmbm_ier);
+ event &= mask;
+ /* clear the forced events */
+ force = ioread32be(&bmi_rg->fmbm_ifr);
+ if (force & event)
+ iowrite32be(force & ~event, &bmi_rg->fmbm_ifr);
+ /* clear the acknowledged events */
+ iowrite32be(event, &bmi_rg->fmbm_ievr);
+
+ if (event & BMI_ERR_INTR_EN_STORAGE_PROFILE_ECC)
+ ret = fman->exception_cb(fman, FMAN_EX_BMI_STORAGE_PROFILE_ECC);
+ if (event & BMI_ERR_INTR_EN_LIST_RAM_ECC)
+ ret = fman->exception_cb(fman, FMAN_EX_BMI_LIST_RAM_ECC);
+ if (event & BMI_ERR_INTR_EN_STATISTICS_RAM_ECC)
+ ret = fman->exception_cb(fman, FMAN_EX_BMI_STATISTICS_RAM_ECC);
+ if (event & BMI_ERR_INTR_EN_DISPATCH_RAM_ECC)
+ ret = fman->exception_cb(fman, FMAN_EX_BMI_DISPATCH_RAM_ECC);
+
+ return ret;
+}
+
+static irqreturn_t qmi_err_event(struct fman *fman)
+{
+ u32 event, mask, force;
+ struct fman_qmi_regs __iomem *qmi_rg = fman->qmi_regs;
+ irqreturn_t ret = IRQ_NONE;
+
+ event = ioread32be(&qmi_rg->fmqm_eie);
+ mask = ioread32be(&qmi_rg->fmqm_eien);
+ event &= mask;
+
+ /* clear the forced events */
+ force = ioread32be(&qmi_rg->fmqm_eif);
+ if (force & event)
+ iowrite32be(force & ~event, &qmi_rg->fmqm_eif);
+ /* clear the acknowledged events */
+ iowrite32be(event, &qmi_rg->fmqm_eie);
+
+ if (event & QMI_ERR_INTR_EN_DOUBLE_ECC)
+ ret = fman->exception_cb(fman, FMAN_EX_QMI_DOUBLE_ECC);
+ if (event & QMI_ERR_INTR_EN_DEQ_FROM_DEF)
+ ret = fman->exception_cb(fman,
+ FMAN_EX_QMI_DEQ_FROM_UNKNOWN_PORTID);
+
+ return ret;
+}
+
+static irqreturn_t dma_err_event(struct fman *fman)
+{
+ u32 status, mask, com_id;
+ u8 tnum, port_id, relative_port_id;
+ u16 liodn;
+ struct fman_dma_regs __iomem *dma_rg = fman->dma_regs;
+ irqreturn_t ret = IRQ_NONE;
+
+ status = ioread32be(&dma_rg->fmdmsr);
+ mask = ioread32be(&dma_rg->fmdmmr);
+
+ /* clear DMA_STATUS_BUS_ERR if mask has no DMA_MODE_BER */
+ if ((mask & DMA_MODE_BER) != DMA_MODE_BER)
+ status &= ~DMA_STATUS_BUS_ERR;
+
+ /* clear relevant bits if mask has no DMA_MODE_ECC */
+ if ((mask & DMA_MODE_ECC) != DMA_MODE_ECC)
+ status &= ~(DMA_STATUS_FM_SPDAT_ECC |
+ DMA_STATUS_READ_ECC |
+ DMA_STATUS_SYSTEM_WRITE_ECC |
+ DMA_STATUS_FM_WRITE_ECC);
+
+ /* clear set events */
+ iowrite32be(status, &dma_rg->fmdmsr);
+
+ if (status & DMA_STATUS_BUS_ERR) {
+ u64 addr;
+
+ addr = (u64)ioread32be(&dma_rg->fmdmtal);
+ addr |= ((u64)(ioread32be(&dma_rg->fmdmtah)) << 32);
+
+ com_id = ioread32be(&dma_rg->fmdmtcid);
+ port_id = (u8)(((com_id & DMA_TRANSFER_PORTID_MASK) >>
+ DMA_TRANSFER_PORTID_SHIFT));
+ relative_port_id =
+ hw_port_id_to_sw_port_id(fman->state->rev_info.major, port_id);
+ tnum = (u8)((com_id & DMA_TRANSFER_TNUM_MASK) >>
+ DMA_TRANSFER_TNUM_SHIFT);
+ liodn = (u16)(com_id & DMA_TRANSFER_LIODN_MASK);
+ ret = fman->bus_error_cb(fman, relative_port_id, addr, tnum,
+ liodn);
+ }
+ if (status & DMA_STATUS_FM_SPDAT_ECC)
+ ret = fman->exception_cb(fman, FMAN_EX_DMA_SINGLE_PORT_ECC);
+ if (status & DMA_STATUS_READ_ECC)
+ ret = fman->exception_cb(fman, FMAN_EX_DMA_READ_ECC);
+ if (status & DMA_STATUS_SYSTEM_WRITE_ECC)
+ ret = fman->exception_cb(fman, FMAN_EX_DMA_SYSTEM_WRITE_ECC);
+ if (status & DMA_STATUS_FM_WRITE_ECC)
+ ret = fman->exception_cb(fman, FMAN_EX_DMA_FM_WRITE_ECC);
+
+ return ret;
+}
+
+static irqreturn_t fpm_err_event(struct fman *fman)
+{
+ u32 event;
+ struct fman_fpm_regs __iomem *fpm_rg = fman->fpm_regs;
+ irqreturn_t ret = IRQ_NONE;
+
+ event = ioread32be(&fpm_rg->fmfp_ee);
+ /* clear the all occurred events */
+ iowrite32be(event, &fpm_rg->fmfp_ee);
+
+ if ((event & FPM_EV_MASK_DOUBLE_ECC) &&
+ (event & FPM_EV_MASK_DOUBLE_ECC_EN))
+ ret = fman->exception_cb(fman, FMAN_EX_FPM_DOUBLE_ECC);
+ if ((event & FPM_EV_MASK_STALL) && (event & FPM_EV_MASK_STALL_EN))
+ ret = fman->exception_cb(fman, FMAN_EX_FPM_STALL_ON_TASKS);
+ if ((event & FPM_EV_MASK_SINGLE_ECC) &&
+ (event & FPM_EV_MASK_SINGLE_ECC_EN))
+ ret = fman->exception_cb(fman, FMAN_EX_FPM_SINGLE_ECC);
+
+ return ret;
+}
+
+static irqreturn_t muram_err_intr(struct fman *fman)
+{
+ u32 event, mask;
+ struct fman_fpm_regs __iomem *fpm_rg = fman->fpm_regs;
+ irqreturn_t ret = IRQ_NONE;
+
+ event = ioread32be(&fpm_rg->fm_rcr);
+ mask = ioread32be(&fpm_rg->fm_rie);
+
+ /* clear MURAM event bit (do not clear IRAM event) */
+ iowrite32be(event & ~FPM_RAM_IRAM_ECC, &fpm_rg->fm_rcr);
+
+ if ((mask & FPM_MURAM_ECC_ERR_EX_EN) && (event & FPM_RAM_MURAM_ECC))
+ ret = fman->exception_cb(fman, FMAN_EX_MURAM_ECC);
+
+ return ret;
+}
+
+static irqreturn_t qmi_event(struct fman *fman)
+{
+ u32 event, mask, force;
+ struct fman_qmi_regs __iomem *qmi_rg = fman->qmi_regs;
+ irqreturn_t ret = IRQ_NONE;
+
+ event = ioread32be(&qmi_rg->fmqm_ie);
+ mask = ioread32be(&qmi_rg->fmqm_ien);
+ event &= mask;
+ /* clear the forced events */
+ force = ioread32be(&qmi_rg->fmqm_if);
+ if (force & event)
+ iowrite32be(force & ~event, &qmi_rg->fmqm_if);
+ /* clear the acknowledged events */
+ iowrite32be(event, &qmi_rg->fmqm_ie);
+
+ if (event & QMI_INTR_EN_SINGLE_ECC)
+ ret = fman->exception_cb(fman, FMAN_EX_QMI_SINGLE_ECC);
+
+ return ret;
+}
+
+static void enable_time_stamp(struct fman *fman)
+{
+ struct fman_fpm_regs __iomem *fpm_rg = fman->fpm_regs;
+ u16 fm_clk_freq = fman->state->fm_clk_freq;
+ u32 tmp, intgr, ts_freq;
+ u64 frac;
+
+ ts_freq = (u32)(1 << fman->state->count1_micro_bit);
+ /* configure timestamp so that bit 8 will count 1 microsecond
+ * Find effective count rate at TIMESTAMP least significant bits:
+ * Effective_Count_Rate = 1MHz x 2^8 = 256MHz
+ * Find frequency ratio between effective count rate and the clock:
+ * Effective_Count_Rate / CLK e.g. for 600 MHz clock:
+ * 256/600 = 0.4266666...
+ */
+
+ intgr = ts_freq / fm_clk_freq;
+ /* we multiply by 2^16 to keep the fraction of the division
+ * we do not div back, since we write this value as a fraction
+ * see spec
+ */
+
+ frac = ((ts_freq << 16) - (intgr << 16) * fm_clk_freq) / fm_clk_freq;
+ /* we check remainder of the division in order to round up if not int */
+ if (((ts_freq << 16) - (intgr << 16) * fm_clk_freq) % fm_clk_freq)
+ frac++;
+
+ tmp = (intgr << FPM_TS_INT_SHIFT) | (u16)frac;
+ iowrite32be(tmp, &fpm_rg->fmfp_tsc2);
+
+ /* enable timestamp with original clock */
+ iowrite32be(FPM_TS_CTL_EN, &fpm_rg->fmfp_tsc1);
+ fman->state->enabled_time_stamp = true;
+}
+
+static int clear_iram(struct fman *fman)
+{
+ struct fman_iram_regs __iomem *iram;
+ int i, count;
+
+ iram = fman->base_addr + IMEM_OFFSET;
+
+ /* Enable the auto-increment */
+ iowrite32be(IRAM_IADD_AIE, &iram->iadd);
+ count = 100;
+ do {
+ udelay(1);
+ } while ((ioread32be(&iram->iadd) != IRAM_IADD_AIE) && --count);
+ if (count == 0)
+ return -EBUSY;
+
+ for (i = 0; i < (fman->state->fm_iram_size / 4); i++)
+ iowrite32be(0xffffffff, &iram->idata);
+
+ iowrite32be(fman->state->fm_iram_size - 4, &iram->iadd);
+ count = 100;
+ do {
+ udelay(1);
+ } while ((ioread32be(&iram->idata) != 0xffffffff) && --count);
+ if (count == 0)
+ return -EBUSY;
+
+ return 0;
+}
+
+static u32 get_exception_flag(enum fman_exceptions exception)
+{
+ u32 bit_mask;
+
+ switch (exception) {
+ case FMAN_EX_DMA_BUS_ERROR:
+ bit_mask = EX_DMA_BUS_ERROR;
+ break;
+ case FMAN_EX_DMA_SINGLE_PORT_ECC:
+ bit_mask = EX_DMA_SINGLE_PORT_ECC;
+ break;
+ case FMAN_EX_DMA_READ_ECC:
+ bit_mask = EX_DMA_READ_ECC;
+ break;
+ case FMAN_EX_DMA_SYSTEM_WRITE_ECC:
+ bit_mask = EX_DMA_SYSTEM_WRITE_ECC;
+ break;
+ case FMAN_EX_DMA_FM_WRITE_ECC:
+ bit_mask = EX_DMA_FM_WRITE_ECC;
+ break;
+ case FMAN_EX_FPM_STALL_ON_TASKS:
+ bit_mask = EX_FPM_STALL_ON_TASKS;
+ break;
+ case FMAN_EX_FPM_SINGLE_ECC:
+ bit_mask = EX_FPM_SINGLE_ECC;
+ break;
+ case FMAN_EX_FPM_DOUBLE_ECC:
+ bit_mask = EX_FPM_DOUBLE_ECC;
+ break;
+ case FMAN_EX_QMI_SINGLE_ECC:
+ bit_mask = EX_QMI_SINGLE_ECC;
+ break;
+ case FMAN_EX_QMI_DOUBLE_ECC:
+ bit_mask = EX_QMI_DOUBLE_ECC;
+ break;
+ case FMAN_EX_QMI_DEQ_FROM_UNKNOWN_PORTID:
+ bit_mask = EX_QMI_DEQ_FROM_UNKNOWN_PORTID;
+ break;
+ case FMAN_EX_BMI_LIST_RAM_ECC:
+ bit_mask = EX_BMI_LIST_RAM_ECC;
+ break;
+ case FMAN_EX_BMI_STORAGE_PROFILE_ECC:
+ bit_mask = EX_BMI_STORAGE_PROFILE_ECC;
+ break;
+ case FMAN_EX_BMI_STATISTICS_RAM_ECC:
+ bit_mask = EX_BMI_STATISTICS_RAM_ECC;
+ break;
+ case FMAN_EX_BMI_DISPATCH_RAM_ECC:
+ bit_mask = EX_BMI_DISPATCH_RAM_ECC;
+ break;
+ case FMAN_EX_MURAM_ECC:
+ bit_mask = EX_MURAM_ECC;
+ break;
+ default:
+ bit_mask = 0;
+ break;
+ }
+
+ return bit_mask;
+}
+
+static int get_module_event(enum fman_event_modules module, u8 mod_id,
+ enum fman_intr_type intr_type)
+{
+ int event;
+
+ switch (module) {
+ case FMAN_MOD_MAC:
+ if (intr_type == FMAN_INTR_TYPE_ERR)
+ event = FMAN_EV_ERR_MAC0 + mod_id;
+ else
+ event = FMAN_EV_MAC0 + mod_id;
+ break;
+ case FMAN_MOD_FMAN_CTRL:
+ if (intr_type == FMAN_INTR_TYPE_ERR)
+ event = FMAN_EV_CNT;
+ else
+ event = (FMAN_EV_FMAN_CTRL_0 + mod_id);
+ break;
+ case FMAN_MOD_DUMMY_LAST:
+ event = FMAN_EV_CNT;
+ break;
+ default:
+ event = FMAN_EV_CNT;
+ break;
+ }
+
+ return event;
+}
+
+static int set_size_of_fifo(struct fman *fman, u8 port_id, u32 *size_of_fifo,
+ u32 *extra_size_of_fifo)
+{
+ struct fman_bmi_regs __iomem *bmi_rg = fman->bmi_regs;
+ u32 fifo = *size_of_fifo;
+ u32 extra_fifo = *extra_size_of_fifo;
+ u32 tmp;
+
+ /* if this is the first time a port requires extra_fifo_pool_size,
+ * the total extra_fifo_pool_size must be initialized to 1 buffer per
+ * port
+ */
+ if (extra_fifo && !fman->state->extra_fifo_pool_size)
+ fman->state->extra_fifo_pool_size =
+ fman->state->num_of_rx_ports * FMAN_BMI_FIFO_UNITS;
+
+ fman->state->extra_fifo_pool_size =
+ max(fman->state->extra_fifo_pool_size, extra_fifo);
+
+ /* check that there are enough uncommitted fifo size */
+ if ((fman->state->accumulated_fifo_size + fifo) >
+ (fman->state->total_fifo_size -
+ fman->state->extra_fifo_pool_size)) {
+ dev_err(fman->dev, "%s: Requested fifo size and extra size exceed total FIFO size.\n",
+ __func__);
+ return -EAGAIN;
+ }
+
+ /* Read, modify and write to HW */
+ tmp = (fifo / FMAN_BMI_FIFO_UNITS - 1) |
+ ((extra_fifo / FMAN_BMI_FIFO_UNITS) <<
+ BMI_EXTRA_FIFO_SIZE_SHIFT);
+ iowrite32be(tmp, &bmi_rg->fmbm_pfs[port_id - 1]);
+
+ /* update accumulated */
+ fman->state->accumulated_fifo_size += fifo;
+
+ return 0;
+}
+
+static int set_num_of_tasks(struct fman *fman, u8 port_id, u8 *num_of_tasks,
+ u8 *num_of_extra_tasks)
+{
+ struct fman_bmi_regs __iomem *bmi_rg = fman->bmi_regs;
+ u8 tasks = *num_of_tasks;
+ u8 extra_tasks = *num_of_extra_tasks;
+ u32 tmp;
+
+ if (extra_tasks)
+ fman->state->extra_tasks_pool_size =
+ max(fman->state->extra_tasks_pool_size, extra_tasks);
+
+ /* check that there are enough uncommitted tasks */
+ if ((fman->state->accumulated_num_of_tasks + tasks) >
+ (fman->state->total_num_of_tasks -
+ fman->state->extra_tasks_pool_size)) {
+ dev_err(fman->dev, "%s: Requested num_of_tasks and extra tasks pool for fm%d exceed total num_of_tasks.\n",
+ __func__, fman->state->fm_id);
+ return -EAGAIN;
+ }
+ /* update accumulated */
+ fman->state->accumulated_num_of_tasks += tasks;
+
+ /* Write to HW */
+ tmp = ioread32be(&bmi_rg->fmbm_pp[port_id - 1]) &
+ ~(BMI_NUM_OF_TASKS_MASK | BMI_NUM_OF_EXTRA_TASKS_MASK);
+ tmp |= ((u32)((tasks - 1) << BMI_NUM_OF_TASKS_SHIFT) |
+ (u32)(extra_tasks << BMI_EXTRA_NUM_OF_TASKS_SHIFT));
+ iowrite32be(tmp, &bmi_rg->fmbm_pp[port_id - 1]);
+
+ return 0;
+}
+
+static int set_num_of_open_dmas(struct fman *fman, u8 port_id,
+ u8 *num_of_open_dmas,
+ u8 *num_of_extra_open_dmas)
+{
+ struct fman_bmi_regs __iomem *bmi_rg = fman->bmi_regs;
+ u8 open_dmas = *num_of_open_dmas;
+ u8 extra_open_dmas = *num_of_extra_open_dmas;
+ u8 total_num_dmas = 0, current_val = 0, current_extra_val = 0;
+ u32 tmp;
+
+ if (!open_dmas) {
+ /* Configuration according to values in the HW.
+ * read the current number of open Dma's
+ */
+ tmp = ioread32be(&bmi_rg->fmbm_pp[port_id - 1]);
+ current_extra_val = (u8)((tmp & BMI_NUM_OF_EXTRA_DMAS_MASK) >>
+ BMI_EXTRA_NUM_OF_DMAS_SHIFT);
+
+ tmp = ioread32be(&bmi_rg->fmbm_pp[port_id - 1]);
+ current_val = (u8)(((tmp & BMI_NUM_OF_DMAS_MASK) >>
+ BMI_NUM_OF_DMAS_SHIFT) + 1);
+
+ /* This is the first configuration and user did not
+ * specify value (!open_dmas), reset values will be used
+ * and we just save these values for resource management
+ */
+ fman->state->extra_open_dmas_pool_size =
+ (u8)max(fman->state->extra_open_dmas_pool_size,
+ current_extra_val);
+ fman->state->accumulated_num_of_open_dmas += current_val;
+ *num_of_open_dmas = current_val;
+ *num_of_extra_open_dmas = current_extra_val;
+ return 0;
+ }
+
+ if (extra_open_dmas > current_extra_val)
+ fman->state->extra_open_dmas_pool_size =
+ (u8)max(fman->state->extra_open_dmas_pool_size,
+ extra_open_dmas);
+
+ if ((fman->state->rev_info.major < 6) &&
+ (fman->state->accumulated_num_of_open_dmas - current_val +
+ open_dmas > fman->state->max_num_of_open_dmas)) {
+ dev_err(fman->dev, "%s: Requested num_of_open_dmas for fm%d exceeds total num_of_open_dmas.\n",
+ __func__, fman->state->fm_id);
+ return -EAGAIN;
+ } else if ((fman->state->rev_info.major >= 6) &&
+ !((fman->state->rev_info.major == 6) &&
+ (fman->state->rev_info.minor == 0)) &&
+ (fman->state->accumulated_num_of_open_dmas -
+ current_val + open_dmas >
+ fman->state->dma_thresh_max_commq + 1)) {
+ dev_err(fman->dev, "%s: Requested num_of_open_dmas for fm%d exceeds DMA Command queue (%d)\n",
+ __func__, fman->state->fm_id,
+ fman->state->dma_thresh_max_commq + 1);
+ return -EAGAIN;
+ }
+
+ WARN_ON(fman->state->accumulated_num_of_open_dmas < current_val);
+ /* update acummulated */
+ fman->state->accumulated_num_of_open_dmas -= current_val;
+ fman->state->accumulated_num_of_open_dmas += open_dmas;
+
+ if (fman->state->rev_info.major < 6)
+ total_num_dmas =
+ (u8)(fman->state->accumulated_num_of_open_dmas +
+ fman->state->extra_open_dmas_pool_size);
+
+ /* calculate reg */
+ tmp = ioread32be(&bmi_rg->fmbm_pp[port_id - 1]) &
+ ~(BMI_NUM_OF_DMAS_MASK | BMI_NUM_OF_EXTRA_DMAS_MASK);
+ tmp |= (u32)(((open_dmas - 1) << BMI_NUM_OF_DMAS_SHIFT) |
+ (extra_open_dmas << BMI_EXTRA_NUM_OF_DMAS_SHIFT));
+ iowrite32be(tmp, &bmi_rg->fmbm_pp[port_id - 1]);
+
+ /* update total num of DMA's with committed number of open DMAS,
+ * and max uncommitted pool.
+ */
+ if (total_num_dmas) {
+ tmp = ioread32be(&bmi_rg->fmbm_cfg2) & ~BMI_CFG2_DMAS_MASK;
+ tmp |= (u32)(total_num_dmas - 1) << BMI_CFG2_DMAS_SHIFT;
+ iowrite32be(tmp, &bmi_rg->fmbm_cfg2);
+ }
+
+ return 0;
+}
+
+static int fman_config(struct fman *fman)
+{
+ void __iomem *base_addr;
+ int err;
+
+ base_addr = fman->dts_params.base_addr;
+
+ fman->state = kzalloc(sizeof(*fman->state), GFP_KERNEL);
+ if (!fman->state)
+ goto err_fm_state;
+
+ /* Allocate the FM driver's parameters structure */
+ fman->cfg = kzalloc(sizeof(*fman->cfg), GFP_KERNEL);
+ if (!fman->cfg)
+ goto err_fm_drv;
+
+ /* Initialize MURAM block */
+ fman->muram =
+ fman_muram_init(fman->dts_params.muram_res.start,
+ resource_size(&fman->dts_params.muram_res));
+ if (!fman->muram)
+ goto err_fm_soc_specific;
+
+ /* Initialize FM parameters which will be kept by the driver */
+ fman->state->fm_id = fman->dts_params.id;
+ fman->state->fm_clk_freq = fman->dts_params.clk_freq;
+ fman->state->qman_channel_base = fman->dts_params.qman_channel_base;
+ fman->state->num_of_qman_channels =
+ fman->dts_params.num_of_qman_channels;
+ fman->state->res = fman->dts_params.res;
+ fman->exception_cb = fman_exceptions;
+ fman->bus_error_cb = fman_bus_error;
+ fman->fpm_regs = base_addr + FPM_OFFSET;
+ fman->bmi_regs = base_addr + BMI_OFFSET;
+ fman->qmi_regs = base_addr + QMI_OFFSET;
+ fman->dma_regs = base_addr + DMA_OFFSET;
+ fman->base_addr = base_addr;
+
+ spin_lock_init(&fman->spinlock);
+ fman_defconfig(fman->cfg);
+
+ fman->state->extra_fifo_pool_size = 0;
+ fman->state->exceptions = (EX_DMA_BUS_ERROR |
+ EX_DMA_READ_ECC |
+ EX_DMA_SYSTEM_WRITE_ECC |
+ EX_DMA_FM_WRITE_ECC |
+ EX_FPM_STALL_ON_TASKS |
+ EX_FPM_SINGLE_ECC |
+ EX_FPM_DOUBLE_ECC |
+ EX_QMI_DEQ_FROM_UNKNOWN_PORTID |
+ EX_BMI_LIST_RAM_ECC |
+ EX_BMI_STORAGE_PROFILE_ECC |
+ EX_BMI_STATISTICS_RAM_ECC |
+ EX_MURAM_ECC |
+ EX_BMI_DISPATCH_RAM_ECC |
+ EX_QMI_DOUBLE_ECC |
+ EX_QMI_SINGLE_ECC);
+
+ /* Read FMan revision for future use*/
+ fman_get_revision(fman, &fman->state->rev_info);
+
+ err = fill_soc_specific_params(fman->state);
+ if (err)
+ goto err_fm_soc_specific;
+
+ /* FM_AID_MODE_NO_TNUM_SW005 Errata workaround */
+ if (fman->state->rev_info.major >= 6)
+ fman->cfg->dma_aid_mode = FMAN_DMA_AID_OUT_PORT_ID;
+
+ fman->cfg->qmi_def_tnums_thresh = fman->state->qmi_def_tnums_thresh;
+
+ fman->state->total_num_of_tasks =
+ (u8)DFLT_TOTAL_NUM_OF_TASKS(fman->state->rev_info.major,
+ fman->state->rev_info.minor,
+ fman->state->bmi_max_num_of_tasks);
+
+ if (fman->state->rev_info.major < 6) {
+ fman->cfg->dma_comm_qtsh_clr_emer =
+ (u8)DFLT_DMA_COMM_Q_LOW(fman->state->rev_info.major,
+ fman->state->dma_thresh_max_commq);
+
+ fman->cfg->dma_comm_qtsh_asrt_emer =
+ (u8)DFLT_DMA_COMM_Q_HIGH(fman->state->rev_info.major,
+ fman->state->dma_thresh_max_commq);
+
+ fman->cfg->dma_cam_num_of_entries =
+ DFLT_DMA_CAM_NUM_OF_ENTRIES(fman->state->rev_info.major);
+
+ fman->cfg->dma_read_buf_tsh_clr_emer =
+ DFLT_DMA_READ_INT_BUF_LOW(fman->state->dma_thresh_max_buf);
+
+ fman->cfg->dma_read_buf_tsh_asrt_emer =
+ DFLT_DMA_READ_INT_BUF_HIGH(fman->state->dma_thresh_max_buf);
+
+ fman->cfg->dma_write_buf_tsh_clr_emer =
+ DFLT_DMA_WRITE_INT_BUF_LOW(fman->state->dma_thresh_max_buf);
+
+ fman->cfg->dma_write_buf_tsh_asrt_emer =
+ DFLT_DMA_WRITE_INT_BUF_HIGH(fman->state->dma_thresh_max_buf);
+
+ fman->cfg->dma_axi_dbg_num_of_beats =
+ DFLT_AXI_DBG_NUM_OF_BEATS;
+ }
+
+ return 0;
+
+err_fm_soc_specific:
+ kfree(fman->cfg);
+err_fm_drv:
+ kfree(fman->state);
+err_fm_state:
+ kfree(fman);
+ return -EINVAL;
+}
+
+static int fman_init(struct fman *fman)
+{
+ struct fman_cfg *cfg = NULL;
+ int err = 0, i, count;
+
+ if (is_init_done(fman->cfg))
+ return -EINVAL;
+
+ fman->state->count1_micro_bit = FM_TIMESTAMP_1_USEC_BIT;
+
+ cfg = fman->cfg;
+
+ /* clear revision-dependent non existing exception */
+ if (fman->state->rev_info.major < 6)
+ fman->state->exceptions &= ~FMAN_EX_BMI_DISPATCH_RAM_ECC;
+
+ if (fman->state->rev_info.major >= 6)
+ fman->state->exceptions &= ~FMAN_EX_QMI_SINGLE_ECC;
+
+ /* clear CPG */
+ memset_io((void __iomem *)(fman->base_addr + CGP_OFFSET), 0,
+ fman->state->fm_port_num_of_cg);
+
+ /* Save LIODN info before FMan reset
+ * Skipping non-existent port 0 (i = 1)
+ */
+ for (i = 1; i < FMAN_LIODN_TBL; i++) {
+ u32 liodn_base;
+
+ fman->liodn_offset[i] =
+ ioread32be(&fman->bmi_regs->fmbm_spliodn[i - 1]);
+ liodn_base = ioread32be(&fman->dma_regs->fmdmplr[i / 2]);
+ if (i % 2) {
+ /* FMDM_PLR LSB holds LIODN base for odd ports */
+ liodn_base &= DMA_LIODN_BASE_MASK;
+ } else {
+ /* FMDM_PLR MSB holds LIODN base for even ports */
+ liodn_base >>= DMA_LIODN_SHIFT;
+ liodn_base &= DMA_LIODN_BASE_MASK;
+ }
+ fman->liodn_base[i] = liodn_base;
+ }
+
+ /* FMan Reset (supported only for FMan V2) */
+ if (fman->state->rev_info.major >= 6) {
+ /* Errata A007273 */
+ dev_dbg(fman->dev, "%s: FManV3 reset is not supported!\n",
+ __func__);
+ } else {
+ iowrite32be(FPM_RSTC_FM_RESET, &fman->fpm_regs->fm_rstc);
+ /* Wait for reset completion */
+ count = 100;
+ do {
+ udelay(1);
+ } while (((ioread32be(&fman->fpm_regs->fm_rstc)) &
+ FPM_RSTC_FM_RESET) && --count);
+ if (count == 0)
+ return -EBUSY;
+ }
+
+ if (ioread32be(&fman->qmi_regs->fmqm_gs) & QMI_GS_HALT_NOT_BUSY) {
+ resume(fman->fpm_regs);
+ /* Wait until QMI is not in halt not busy state */
+ count = 100;
+ do {
+ udelay(1);
+ } while (((ioread32be(&fman->qmi_regs->fmqm_gs)) &
+ QMI_GS_HALT_NOT_BUSY) && --count);
+ if (count == 0)
+ dev_warn(fman->dev, "%s: QMI is in halt not busy state\n",
+ __func__);
+ }
+
+ if (clear_iram(fman) != 0)
+ return -EINVAL;
+
+ cfg->exceptions = fman->state->exceptions;
+
+ /* Init DMA Registers */
+
+ err = dma_init(fman);
+ if (err != 0) {
+ free_init_resources(fman);
+ return err;
+ }
+
+ /* Init FPM Registers */
+ fpm_init(fman->fpm_regs, fman->cfg);
+
+ /* define common resources */
+ /* allocate MURAM for FIFO according to total size */
+ fman->fifo_offset = fman_muram_alloc(fman->muram,
+ fman->state->total_fifo_size);
+ if (IS_ERR_VALUE(fman->cam_offset)) {
+ free_init_resources(fman);
+ dev_err(fman->dev, "%s: MURAM alloc for BMI FIFO failed\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ cfg->fifo_base_addr = fman->fifo_offset;
+ cfg->total_fifo_size = fman->state->total_fifo_size;
+ cfg->total_num_of_tasks = fman->state->total_num_of_tasks;
+ cfg->clk_freq = fman->state->fm_clk_freq;
+
+ /* Init BMI Registers */
+ bmi_init(fman->bmi_regs, fman->cfg);
+
+ /* Init QMI Registers */
+ qmi_init(fman->qmi_regs, fman->cfg);
+
+ err = enable(fman, cfg);
+ if (err != 0)
+ return err;
+
+ enable_time_stamp(fman);
+
+ kfree(fman->cfg);
+ fman->cfg = NULL;
+
+ return 0;
+}
+
+static int fman_set_exception(struct fman *fman,
+ enum fman_exceptions exception, bool enable)
+{
+ u32 bit_mask = 0;
+
+ if (!is_init_done(fman->cfg))
+ return -EINVAL;
+
+ bit_mask = get_exception_flag(exception);
+ if (bit_mask) {
+ if (enable)
+ fman->state->exceptions |= bit_mask;
+ else
+ fman->state->exceptions &= ~bit_mask;
+ } else {
+ dev_err(fman->dev, "%s: Undefined exception (%d)\n",
+ __func__, exception);
+ return -EINVAL;
+ }
+
+ return set_exception(fman, exception, enable);
+}
+
+/**
+ * fman_register_intr
+ * @fman: A Pointer to FMan device
+ * @mod: Calling module
+ * @mod_id: Module id (if more than 1 exists, '0' if not)
+ * @intr_type: Interrupt type (error/normal) selection.
+ * @f_isr: The interrupt service routine.
+ * @h_src_arg: Argument to be passed to f_isr.
+ *
+ * Used to register an event handler to be processed by FMan
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+void fman_register_intr(struct fman *fman, enum fman_event_modules module,
+ u8 mod_id, enum fman_intr_type intr_type,
+ void (*isr_cb)(void *src_arg), void *src_arg)
+{
+ int event = 0;
+
+ event = get_module_event(module, mod_id, intr_type);
+ WARN_ON(event >= FMAN_EV_CNT);
+
+ /* register in local FM structure */
+ fman->intr_mng[event].isr_cb = isr_cb;
+ fman->intr_mng[event].src_handle = src_arg;
+}
+
+/**
+ * fman_unregister_intr
+ * @fman: A Pointer to FMan device
+ * @mod: Calling module
+ * @mod_id: Module id (if more than 1 exists, '0' if not)
+ * @intr_type: Interrupt type (error/normal) selection.
+ *
+ * Used to unregister an event handler to be processed by FMan
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+void fman_unregister_intr(struct fman *fman, enum fman_event_modules module,
+ u8 mod_id, enum fman_intr_type intr_type)
+{
+ int event = 0;
+
+ event = get_module_event(module, mod_id, intr_type);
+ WARN_ON(event >= FMAN_EV_CNT);
+
+ fman->intr_mng[event].isr_cb = NULL;
+ fman->intr_mng[event].src_handle = NULL;
+}
+
+/**
+ * fman_set_port_params
+ * @fman: A Pointer to FMan device
+ * @port_params: Port parameters
+ *
+ * Used by FMan Port to pass parameters to the FMan
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+int fman_set_port_params(struct fman *fman,
+ struct fman_port_init_params *port_params)
+{
+ int err;
+ unsigned long flags;
+ u8 port_id = port_params->port_id, mac_id;
+
+ spin_lock_irqsave(&fman->spinlock, flags);
+
+ err = set_num_of_tasks(fman, port_params->port_id,
+ &port_params->num_of_tasks,
+ &port_params->num_of_extra_tasks);
+ if (err)
+ goto return_err;
+
+ /* TX Ports */
+ if (port_params->port_type != FMAN_PORT_TYPE_RX) {
+ u32 enq_th, deq_th, reg;
+
+ /* update qmi ENQ/DEQ threshold */
+ fman->state->accumulated_num_of_deq_tnums +=
+ port_params->deq_pipeline_depth;
+ enq_th = (ioread32be(&fman->qmi_regs->fmqm_gc) &
+ QMI_CFG_ENQ_MASK) >> QMI_CFG_ENQ_SHIFT;
+ /* if enq_th is too big, we reduce it to the max value
+ * that is still 0
+ */
+ if (enq_th >= (fman->state->qmi_max_num_of_tnums -
+ fman->state->accumulated_num_of_deq_tnums)) {
+ enq_th =
+ fman->state->qmi_max_num_of_tnums -
+ fman->state->accumulated_num_of_deq_tnums - 1;
+
+ reg = ioread32be(&fman->qmi_regs->fmqm_gc);
+ reg &= ~QMI_CFG_ENQ_MASK;
+ reg |= (enq_th << QMI_CFG_ENQ_SHIFT);
+ iowrite32be(reg, &fman->qmi_regs->fmqm_gc);
+ }
+
+ deq_th = ioread32be(&fman->qmi_regs->fmqm_gc) &
+ QMI_CFG_DEQ_MASK;
+ /* if deq_th is too small, we enlarge it to the min
+ * value that is still 0.
+ * depTh may not be larger than 63
+ * (fman->state->qmi_max_num_of_tnums-1).
+ */
+ if ((deq_th <= fman->state->accumulated_num_of_deq_tnums) &&
+ (deq_th < fman->state->qmi_max_num_of_tnums - 1)) {
+ deq_th = fman->state->accumulated_num_of_deq_tnums + 1;
+ reg = ioread32be(&fman->qmi_regs->fmqm_gc);
+ reg &= ~QMI_CFG_DEQ_MASK;
+ reg |= deq_th;
+ iowrite32be(reg, &fman->qmi_regs->fmqm_gc);
+ }
+ }
+
+ err = set_size_of_fifo(fman, port_params->port_id,
+ &port_params->size_of_fifo,
+ &port_params->extra_size_of_fifo);
+ if (err)
+ goto return_err;
+
+ err = set_num_of_open_dmas(fman, port_params->port_id,
+ &port_params->num_of_open_dmas,
+ &port_params->num_of_extra_open_dmas);
+ if (err)
+ goto return_err;
+
+ set_port_liodn(fman, port_id, fman->liodn_base[port_id],
+ fman->liodn_offset[port_id]);
+
+ if (fman->state->rev_info.major < 6)
+ set_port_order_restoration(fman->fpm_regs, port_id);
+
+ mac_id = hw_port_id_to_sw_port_id(fman->state->rev_info.major, port_id);
+
+ if (port_params->max_frame_length >= fman->state->mac_mfl[mac_id]) {
+ fman->state->port_mfl[mac_id] = port_params->max_frame_length;
+ } else {
+ dev_warn(fman->dev, "%s: Port (%d) max_frame_length is smaller than MAC (%d) current MTU\n",
+ __func__, port_id, mac_id);
+ err = -EINVAL;
+ goto return_err;
+ }
+
+ spin_unlock_irqrestore(&fman->spinlock, flags);
+
+ return 0;
+
+return_err:
+ spin_unlock_irqrestore(&fman->spinlock, flags);
+ return err;
+}
+
+/**
+ * fman_reset_mac
+ * @fman: A Pointer to FMan device
+ * @mac_id: MAC id to be reset
+ *
+ * Reset a specific MAC
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+int fman_reset_mac(struct fman *fman, u8 mac_id)
+{
+ struct fman_fpm_regs __iomem *fpm_rg = fman->fpm_regs;
+ u32 msk, timeout = 100;
+
+ if (fman->state->rev_info.major >= 6) {
+ dev_err(fman->dev, "%s: FMan MAC reset no available for FMan V3!\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* Get the relevant bit mask */
+ switch (mac_id) {
+ case 0:
+ msk = FPM_RSTC_MAC0_RESET;
+ break;
+ case 1:
+ msk = FPM_RSTC_MAC1_RESET;
+ break;
+ case 2:
+ msk = FPM_RSTC_MAC2_RESET;
+ break;
+ case 3:
+ msk = FPM_RSTC_MAC3_RESET;
+ break;
+ case 4:
+ msk = FPM_RSTC_MAC4_RESET;
+ break;
+ case 5:
+ msk = FPM_RSTC_MAC5_RESET;
+ break;
+ case 6:
+ msk = FPM_RSTC_MAC6_RESET;
+ break;
+ case 7:
+ msk = FPM_RSTC_MAC7_RESET;
+ break;
+ case 8:
+ msk = FPM_RSTC_MAC8_RESET;
+ break;
+ case 9:
+ msk = FPM_RSTC_MAC9_RESET;
+ break;
+ default:
+ dev_warn(fman->dev, "%s: Illegal MAC Id [%d]\n",
+ __func__, mac_id);
+ return -EINVAL;
+ }
+
+ /* reset */
+ iowrite32be(msk, &fpm_rg->fm_rstc);
+ while ((ioread32be(&fpm_rg->fm_rstc) & msk) && --timeout)
+ udelay(10);
+
+ if (!timeout)
+ return -EIO;
+
+ return 0;
+}
+
+/**
+ * fman_set_mac_max_frame
+ * @fman: A Pointer to FMan device
+ * @mac_id: MAC id
+ * @mfl: Maximum frame length
+ *
+ * Set maximum frame length of specific MAC in FMan driver
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+int fman_set_mac_max_frame(struct fman *fman, u8 mac_id, u16 mfl)
+{
+ /* if port is already initialized, check that MaxFrameLength is smaller
+ * or equal to the port's max
+ */
+ if ((!fman->state->port_mfl[mac_id]) ||
+ (fman->state->port_mfl[mac_id] &&
+ (mfl <= fman->state->port_mfl[mac_id]))) {
+ fman->state->mac_mfl[mac_id] = mfl;
+ } else {
+ dev_warn(fman->dev, "%s: MAC max_frame_length is larger than Port max_frame_length\n",
+ __func__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * fman_get_clock_freq
+ * @fman: A Pointer to FMan device
+ *
+ * Get FMan clock frequency
+ *
+ * Return: FMan clock frequency
+ */
+u16 fman_get_clock_freq(struct fman *fman)
+{
+ return fman->state->fm_clk_freq;
+}
+
+/**
+ * fman_get_bmi_max_fifo_size
+ * @fman: A Pointer to FMan device
+ *
+ * Get FMan maximum FIFO size
+ *
+ * Return: FMan Maximum FIFO size
+ */
+u32 fman_get_bmi_max_fifo_size(struct fman *fman)
+{
+ return fman->state->bmi_max_fifo_size;
+}
+
+/**
+ * fman_get_revision
+ * @fman - Pointer to the FMan module
+ * @rev_info - A structure of revision information parameters.
+ *
+ * Returns the FM revision
+ *
+ * Allowed only following fman_init().
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+void fman_get_revision(struct fman *fman, struct fman_rev_info *rev_info)
+{
+ u32 tmp;
+
+ tmp = ioread32be(&fman->fpm_regs->fm_ip_rev_1);
+ rev_info->major = (u8)((tmp & FPM_REV1_MAJOR_MASK) >>
+ FPM_REV1_MAJOR_SHIFT);
+ rev_info->minor = tmp & FPM_REV1_MINOR_MASK;
+}
+
+/**
+ * fman_get_qman_channel_id
+ * @fman: A Pointer to FMan device
+ * @port_id: Port id
+ *
+ * Get QMan channel ID associated to the Port id
+ *
+ * Return: QMan channel ID
+ */
+u32 fman_get_qman_channel_id(struct fman *fman, u32 port_id)
+{
+ int i;
+
+ if (fman->state->rev_info.major >= 6) {
+ u32 port_ids[] = {0x30, 0x31, 0x28, 0x29, 0x2a, 0x2b,
+ 0x2c, 0x2d, 0x2, 0x3, 0x4, 0x5, 0x7, 0x7};
+ for (i = 0; i < fman->state->num_of_qman_channels; i++) {
+ if (port_ids[i] == port_id)
+ break;
+ }
+ } else {
+ u32 port_ids[] = {0x30, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x1,
+ 0x2, 0x3, 0x4, 0x5, 0x7, 0x7};
+ for (i = 0; i < fman->state->num_of_qman_channels; i++) {
+ if (port_ids[i] == port_id)
+ break;
+ }
+ }
+
+ if (i == fman->state->num_of_qman_channels)
+ return 0;
+
+ return fman->state->qman_channel_base + i;
+}
+
+/**
+ * fman_get_mem_region
+ * @fman: A Pointer to FMan device
+ *
+ * Get FMan memory region
+ *
+ * Return: A structure with FMan memory region information
+ */
+struct resource *fman_get_mem_region(struct fman *fman)
+{
+ return fman->state->res;
+}
+
+/* Bootargs defines */
+/* Extra headroom for RX buffers - Default, min and max */
+#define FSL_FM_RX_EXTRA_HEADROOM 64
+#define FSL_FM_RX_EXTRA_HEADROOM_MIN 16
+#define FSL_FM_RX_EXTRA_HEADROOM_MAX 384
+
+/* Maximum frame length */
+#define FSL_FM_MAX_FRAME_SIZE 1522
+#define FSL_FM_MAX_POSSIBLE_FRAME_SIZE 9600
+#define FSL_FM_MIN_POSSIBLE_FRAME_SIZE 64
+
+/* Extra headroom for Rx buffers.
+ * FMan is instructed to allocate, on the Rx path, this amount of
+ * space at the beginning of a data buffer, beside the DPA private
+ * data area and the IC fields.
+ * Does not impact Tx buffer layout.
+ * Configurable from bootargs. 64 by default, it's needed on
+ * particular forwarding scenarios that add extra headers to the
+ * forwarded frame.
+ */
+int fsl_fm_rx_extra_headroom = FSL_FM_RX_EXTRA_HEADROOM;
+module_param(fsl_fm_rx_extra_headroom, int, 0);
+MODULE_PARM_DESC(fsl_fm_rx_extra_headroom, "Extra headroom for Rx buffers");
+
+/* Max frame size, across all interfaces.
+ * Configurable from bootargs, to avoid allocating oversized (socket)
+ * buffers when not using jumbo frames.
+ * Must be large enough to accommodate the network MTU, but small enough
+ * to avoid wasting skb memory.
+ *
+ * Could be overridden once, at boot-time, via the
+ * fm_set_max_frm() callback.
+ */
+int fsl_fm_max_frm = FSL_FM_MAX_FRAME_SIZE;
+module_param(fsl_fm_max_frm, int, 0);
+MODULE_PARM_DESC(fsl_fm_max_frm, "Maximum frame size, across all interfaces");
+
+/**
+ * fman_get_max_frm
+ *
+ * Return: Max frame length configured in the FM driver
+ */
+u16 fman_get_max_frm(void)
+{
+ static bool fm_check_mfl;
+
+ if (!fm_check_mfl) {
+ if (fsl_fm_max_frm > FSL_FM_MAX_POSSIBLE_FRAME_SIZE ||
+ fsl_fm_max_frm < FSL_FM_MIN_POSSIBLE_FRAME_SIZE) {
+ pr_warn("Invalid fsl_fm_max_frm value (%d) in bootargs, valid range is %d-%d. Falling back to the default (%d)\n",
+ fsl_fm_max_frm,
+ FSL_FM_MIN_POSSIBLE_FRAME_SIZE,
+ FSL_FM_MAX_POSSIBLE_FRAME_SIZE,
+ FSL_FM_MAX_FRAME_SIZE);
+ fsl_fm_max_frm = FSL_FM_MAX_FRAME_SIZE;
+ }
+ fm_check_mfl = true;
+ }
+
+ return fsl_fm_max_frm;
+}
+EXPORT_SYMBOL(fman_get_max_frm);
+
+/**
+ * fman_get_rx_extra_headroom
+ *
+ * Return: Extra headroom size configured in the FM driver
+ */
+int fman_get_rx_extra_headroom(void)
+{
+ static bool fm_check_rx_extra_headroom;
+
+ if (!fm_check_rx_extra_headroom) {
+ if (fsl_fm_rx_extra_headroom > FSL_FM_RX_EXTRA_HEADROOM_MAX ||
+ fsl_fm_rx_extra_headroom < FSL_FM_RX_EXTRA_HEADROOM_MIN) {
+ pr_warn("Invalid fsl_fm_rx_extra_headroom value (%d) in bootargs, valid range is %d-%d. Falling back to the default (%d)\n",
+ fsl_fm_rx_extra_headroom,
+ FSL_FM_RX_EXTRA_HEADROOM_MIN,
+ FSL_FM_RX_EXTRA_HEADROOM_MAX,
+ FSL_FM_RX_EXTRA_HEADROOM);
+ fsl_fm_rx_extra_headroom = FSL_FM_RX_EXTRA_HEADROOM;
+ }
+
+ fm_check_rx_extra_headroom = true;
+ fsl_fm_rx_extra_headroom = ALIGN(fsl_fm_rx_extra_headroom, 16);
+ }
+
+ return fsl_fm_rx_extra_headroom;
+}
+EXPORT_SYMBOL(fman_get_rx_extra_headroom);
+
+/**
+ * fman_bind
+ * @dev: FMan OF device pointer
+ *
+ * Bind to a specific FMan device.
+ *
+ * Allowed only after the port was created.
+ *
+ * Return: A pointer to the FMan device
+ */
+struct fman *fman_bind(struct device *fm_dev)
+{
+ return (struct fman *)(dev_get_drvdata(get_device(fm_dev)));
+}
+
+static irqreturn_t fman_err_irq(int irq, void *handle)
+{
+ struct fman *fman = (struct fman *)handle;
+ u32 pending;
+ struct fman_fpm_regs __iomem *fpm_rg;
+ irqreturn_t single_ret, ret = IRQ_NONE;
+
+ if (!is_init_done(fman->cfg))
+ return IRQ_NONE;
+
+ fpm_rg = fman->fpm_regs;
+
+ /* error interrupts */
+ pending = ioread32be(&fpm_rg->fm_epi);
+ if (!pending)
+ return IRQ_NONE;
+
+ if (pending & ERR_INTR_EN_BMI) {
+ single_ret = bmi_err_event(fman);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_QMI) {
+ single_ret = qmi_err_event(fman);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_FPM) {
+ single_ret = fpm_err_event(fman);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_DMA) {
+ single_ret = dma_err_event(fman);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_MURAM) {
+ single_ret = muram_err_intr(fman);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+
+ /* MAC error interrupts */
+ if (pending & ERR_INTR_EN_MAC0) {
+ single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 0);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_MAC1) {
+ single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 1);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_MAC2) {
+ single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 2);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_MAC3) {
+ single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 3);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_MAC4) {
+ single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 4);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_MAC5) {
+ single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 5);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_MAC6) {
+ single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 6);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_MAC7) {
+ single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 7);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_MAC8) {
+ single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 8);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & ERR_INTR_EN_MAC9) {
+ single_ret = call_mac_isr(fman, FMAN_EV_ERR_MAC0 + 9);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static irqreturn_t fman_irq(int irq, void *handle)
+{
+ struct fman *fman = (struct fman *)handle;
+ u32 pending;
+ struct fman_fpm_regs __iomem *fpm_rg;
+ irqreturn_t single_ret, ret = IRQ_NONE;
+
+ if (!is_init_done(fman->cfg))
+ return IRQ_NONE;
+
+ fpm_rg = fman->fpm_regs;
+
+ /* normal interrupts */
+ pending = ioread32be(&fpm_rg->fm_npi);
+ if (!pending)
+ return IRQ_NONE;
+
+ if (pending & INTR_EN_QMI) {
+ single_ret = qmi_event(fman);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+
+ /* MAC interrupts */
+ if (pending & INTR_EN_MAC0) {
+ single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 0);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & INTR_EN_MAC1) {
+ single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 1);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & INTR_EN_MAC2) {
+ single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 2);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & INTR_EN_MAC3) {
+ single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 3);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & INTR_EN_MAC4) {
+ single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 4);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & INTR_EN_MAC5) {
+ single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 5);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & INTR_EN_MAC6) {
+ single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 6);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & INTR_EN_MAC7) {
+ single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 7);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & INTR_EN_MAC8) {
+ single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 8);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ if (pending & INTR_EN_MAC9) {
+ single_ret = call_mac_isr(fman, FMAN_EV_MAC0 + 9);
+ if (single_ret == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static const struct of_device_id fman_muram_match[] = {
+ {
+ .compatible = "fsl,fman-muram"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, fman_muram_match);
+
+static struct fman *read_dts_node(struct platform_device *of_dev)
+{
+ struct fman *fman;
+ struct device_node *fm_node, *muram_node;
+ struct resource *res;
+ const u32 *u32_prop;
+ int lenp, err, irq;
+ struct clk *clk;
+ u32 clk_rate;
+ phys_addr_t phys_base_addr;
+ resource_size_t mem_size;
+
+ fman = kzalloc(sizeof(*fman), GFP_KERNEL);
+ if (!fman)
+ return NULL;
+
+ fm_node = of_node_get(of_dev->dev.of_node);
+
+ u32_prop = (const u32 *)of_get_property(fm_node, "cell-index", &lenp);
+ if (!u32_prop) {
+ dev_err(&of_dev->dev, "%s: of_get_property(%s, cell-index) failed\n",
+ __func__, fm_node->full_name);
+ goto fman_node_put;
+ }
+ if (WARN_ON(lenp != sizeof(u32)))
+ goto fman_node_put;
+
+ fman->dts_params.id = (u8)fdt32_to_cpu(u32_prop[0]);
+
+ /* Get the FM interrupt */
+ res = platform_get_resource(of_dev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&of_dev->dev, "%s: Can't get FMan IRQ resource\n",
+ __func__);
+ goto fman_node_put;
+ }
+ irq = res->start;
+
+ /* Get the FM error interrupt */
+ res = platform_get_resource(of_dev, IORESOURCE_IRQ, 1);
+ if (!res) {
+ dev_err(&of_dev->dev, "%s: Can't get FMan Error IRQ resource\n",
+ __func__);
+ goto fman_node_put;
+ }
+ fman->dts_params.err_irq = res->start;
+
+ /* Get the FM address */
+ res = platform_get_resource(of_dev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&of_dev->dev, "%s: Can't get FMan memory resouce\n",
+ __func__);
+ goto fman_node_put;
+ }
+
+ phys_base_addr = res->start;
+ mem_size = resource_size(res);
+
+ clk = of_clk_get(fm_node, 0);
+ if (IS_ERR(clk)) {
+ dev_err(&of_dev->dev, "%s: Failed to get FM%d clock structure\n",
+ __func__, fman->dts_params.id);
+ goto fman_node_put;
+ }
+
+ clk_rate = clk_get_rate(clk);
+ if (!clk_rate) {
+ dev_err(&of_dev->dev, "%s: Failed to determine FM%d clock rate\n",
+ __func__, fman->dts_params.id);
+ goto fman_node_put;
+ }
+ /* Rounding to MHz */
+ fman->dts_params.clk_freq = DIV_ROUND_UP(clk_rate, 1000000);
+
+ u32_prop = (const u32 *)of_get_property(fm_node,
+ "fsl,qman-channel-range",
+ &lenp);
+ if (!u32_prop) {
+ dev_err(&of_dev->dev, "%s: of_get_property(%s, fsl,qman-channel-range) failed\n",
+ __func__, fm_node->full_name);
+ goto fman_node_put;
+ }
+ if (WARN_ON(lenp != sizeof(u32) * 2))
+ goto fman_node_put;
+ fman->dts_params.qman_channel_base = fdt32_to_cpu(u32_prop[0]);
+ fman->dts_params.num_of_qman_channels = fdt32_to_cpu(u32_prop[1]);
+
+ /* Get the MURAM base address and size */
+ muram_node = of_find_matching_node(fm_node, fman_muram_match);
+ if (!muram_node) {
+ dev_err(&of_dev->dev, "%s: could not find MURAM node\n",
+ __func__);
+ goto fman_node_put;
+ }
+
+ err = of_address_to_resource(muram_node, 0,
+ &fman->dts_params.muram_res);
+ if (err) {
+ of_node_put(muram_node);
+ dev_err(&of_dev->dev, "%s: of_address_to_resource() = %d\n",
+ __func__, err);
+ goto fman_node_put;
+ }
+
+ of_node_put(muram_node);
+ of_node_put(fm_node);
+
+ err = devm_request_irq(&of_dev->dev, irq, fman_irq, 0, "fman", fman);
+ if (err < 0) {
+ dev_err(&of_dev->dev, "%s: irq %d allocation failed (error = %d)\n",
+ __func__, irq, err);
+ goto fman_free;
+ }
+
+ if (fman->dts_params.err_irq != 0) {
+ err = devm_request_irq(&of_dev->dev, fman->dts_params.err_irq,
+ fman_err_irq, IRQF_SHARED,
+ "fman-err", fman);
+ if (err < 0) {
+ dev_err(&of_dev->dev, "%s: irq %d allocation failed (error = %d)\n",
+ __func__, fman->dts_params.err_irq, err);
+ goto fman_free;
+ }
+ }
+
+ fman->dts_params.res =
+ devm_request_mem_region(&of_dev->dev, phys_base_addr,
+ mem_size, "fman");
+ if (!fman->dts_params.res) {
+ dev_err(&of_dev->dev, "%s: request_mem_region() failed\n",
+ __func__);
+ goto fman_free;
+ }
+
+ fman->dts_params.base_addr =
+ devm_ioremap(&of_dev->dev, phys_base_addr, mem_size);
+ if (fman->dts_params.base_addr == 0) {
+ dev_err(&of_dev->dev, "%s: devm_ioremap() failed\n", __func__);
+ goto fman_free;
+ }
+
+ return fman;
+
+fman_node_put:
+ of_node_put(fm_node);
+fman_free:
+ kfree(fman);
+ return NULL;
+}
+
+static int fman_probe(struct platform_device *of_dev)
+{
+ struct fman *fman;
+ struct device *dev;
+ int err;
+
+ dev = &of_dev->dev;
+
+ fman = read_dts_node(of_dev);
+ if (!fman)
+ return -EIO;
+
+ err = fman_config(fman);
+ if (err) {
+ dev_err(dev, "%s: FMan config failed\n", __func__);
+ return -EINVAL;
+ }
+
+ if (fman_init(fman) != 0) {
+ dev_err(dev, "%s: FMan init failed\n", __func__);
+ return -EINVAL;
+ }
+
+ if (fman->dts_params.err_irq == 0) {
+ fman_set_exception(fman, FMAN_EX_DMA_BUS_ERROR, false);
+ fman_set_exception(fman, FMAN_EX_DMA_READ_ECC, false);
+ fman_set_exception(fman, FMAN_EX_DMA_SYSTEM_WRITE_ECC, false);
+ fman_set_exception(fman, FMAN_EX_DMA_FM_WRITE_ECC, false);
+ fman_set_exception(fman, FMAN_EX_DMA_SINGLE_PORT_ECC, false);
+ fman_set_exception(fman, FMAN_EX_FPM_STALL_ON_TASKS, false);
+ fman_set_exception(fman, FMAN_EX_FPM_SINGLE_ECC, false);
+ fman_set_exception(fman, FMAN_EX_FPM_DOUBLE_ECC, false);
+ fman_set_exception(fman, FMAN_EX_QMI_SINGLE_ECC, false);
+ fman_set_exception(fman, FMAN_EX_QMI_DOUBLE_ECC, false);
+ fman_set_exception(fman,
+ FMAN_EX_QMI_DEQ_FROM_UNKNOWN_PORTID, false);
+ fman_set_exception(fman, FMAN_EX_BMI_LIST_RAM_ECC, false);
+ fman_set_exception(fman, FMAN_EX_BMI_STORAGE_PROFILE_ECC,
+ false);
+ fman_set_exception(fman, FMAN_EX_BMI_STATISTICS_RAM_ECC, false);
+ fman_set_exception(fman, FMAN_EX_BMI_DISPATCH_RAM_ECC, false);
+ }
+
+ dev_set_drvdata(dev, fman);
+
+ fman->dev = dev;
+
+ dev_dbg(dev, "FMan%d probed\n", fman->dts_params.id);
+
+ return 0;
+}
+
+static const struct of_device_id fman_match[] = {
+ {
+ .compatible = "fsl,fman"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, fm_match);
+
+static struct platform_driver fman_driver = {
+ .driver = {
+ .name = "fsl-fman",
+ .of_match_table = fman_match,
+ },
+ .probe = fman_probe,
+};
+
+builtin_platform_driver(fman_driver);
diff --git a/drivers/net/ethernet/freescale/fman/fman.h b/drivers/net/ethernet/freescale/fman/fman.h
new file mode 100644
index 000000000000..57aae8d17d77
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman.h
@@ -0,0 +1,325 @@
+/*
+ * Copyright 2008-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __FM_H
+#define __FM_H
+
+#include <linux/io.h>
+
+/* FM Frame descriptor macros */
+/* Frame queue Context Override */
+#define FM_FD_CMD_FCO 0x80000000
+#define FM_FD_CMD_RPD 0x40000000 /* Read Prepended Data */
+#define FM_FD_CMD_DTC 0x10000000 /* Do L4 Checksum */
+
+/* TX-Port: Unsupported Format */
+#define FM_FD_ERR_UNSUPPORTED_FORMAT 0x04000000
+/* TX Port: Length Error */
+#define FM_FD_ERR_LENGTH 0x02000000
+#define FM_FD_ERR_DMA 0x01000000 /* DMA Data error */
+
+/* IPR frame (not error) */
+#define FM_FD_IPR 0x00000001
+/* IPR non-consistent-sp */
+#define FM_FD_ERR_IPR_NCSP (0x00100000 | FM_FD_IPR)
+/* IPR error */
+#define FM_FD_ERR_IPR (0x00200000 | FM_FD_IPR)
+/* IPR timeout */
+#define FM_FD_ERR_IPR_TO (0x00300000 | FM_FD_IPR)
+/* TX Port: Length Error */
+#define FM_FD_ERR_IPRE (FM_FD_ERR_IPR & ~FM_FD_IPR)
+
+/* Rx FIFO overflow, FCS error, code error, running disparity error
+ * (SGMII and TBI modes), FIFO parity error. PHY Sequence error,
+ * PHY error control character detected.
+ */
+#define FM_FD_ERR_PHYSICAL 0x00080000
+/* Frame too long OR Frame size exceeds max_length_frame */
+#define FM_FD_ERR_SIZE 0x00040000
+/* classification discard */
+#define FM_FD_ERR_CLS_DISCARD 0x00020000
+/* Extract Out of Frame */
+#define FM_FD_ERR_EXTRACTION 0x00008000
+/* No Scheme Selected */
+#define FM_FD_ERR_NO_SCHEME 0x00004000
+/* Keysize Overflow */
+#define FM_FD_ERR_KEYSIZE_OVERFLOW 0x00002000
+/* Frame color is red */
+#define FM_FD_ERR_COLOR_RED 0x00000800
+/* Frame color is yellow */
+#define FM_FD_ERR_COLOR_YELLOW 0x00000400
+/* Parser Time out Exceed */
+#define FM_FD_ERR_PRS_TIMEOUT 0x00000080
+/* Invalid Soft Parser instruction */
+#define FM_FD_ERR_PRS_ILL_INSTRUCT 0x00000040
+/* Header error was identified during parsing */
+#define FM_FD_ERR_PRS_HDR_ERR 0x00000020
+/* Frame parsed beyind 256 first bytes */
+#define FM_FD_ERR_BLOCK_LIMIT_EXCEEDED 0x00000008
+
+/* non Frame-Manager error */
+#define FM_FD_RX_STATUS_ERR_NON_FM 0x00400000
+
+/* FMan driver defines */
+#define FMAN_BMI_FIFO_UNITS 0x100
+#define OFFSET_UNITS 16
+
+/* BMan defines */
+#define BM_MAX_NUM_OF_POOLS 64 /* Buffers pools */
+#define FMAN_PORT_MAX_EXT_POOLS_NUM 8 /* External BM pools per Rx port */
+
+struct fman; /* FMan data */
+
+/* Enum for defining port types */
+enum fman_port_type {
+ FMAN_PORT_TYPE_TX = 0, /* TX Port */
+ FMAN_PORT_TYPE_RX, /* RX Port */
+};
+
+struct fman_rev_info {
+ u8 major; /* Major revision */
+ u8 minor; /* Minor revision */
+};
+
+enum fman_exceptions {
+ FMAN_EX_DMA_BUS_ERROR = 0, /* DMA bus error. */
+ FMAN_EX_DMA_READ_ECC, /* Read Buffer ECC error */
+ FMAN_EX_DMA_SYSTEM_WRITE_ECC, /* Write Buffer ECC err on sys side */
+ FMAN_EX_DMA_FM_WRITE_ECC, /* Write Buffer ECC error on FM side */
+ FMAN_EX_DMA_SINGLE_PORT_ECC, /* Single Port ECC error on FM side */
+ FMAN_EX_FPM_STALL_ON_TASKS, /* Stall of tasks on FPM */
+ FMAN_EX_FPM_SINGLE_ECC, /* Single ECC on FPM. */
+ FMAN_EX_FPM_DOUBLE_ECC, /* Double ECC error on FPM ram access */
+ FMAN_EX_QMI_SINGLE_ECC, /* Single ECC on QMI. */
+ FMAN_EX_QMI_DOUBLE_ECC, /* Double bit ECC occurred on QMI */
+ FMAN_EX_QMI_DEQ_FROM_UNKNOWN_PORTID,/* DeQ from unknown port id */
+ FMAN_EX_BMI_LIST_RAM_ECC, /* Linked List RAM ECC error */
+ FMAN_EX_BMI_STORAGE_PROFILE_ECC,/* storage profile */
+ FMAN_EX_BMI_STATISTICS_RAM_ECC,/* Statistics RAM ECC Err Enable */
+ FMAN_EX_BMI_DISPATCH_RAM_ECC, /* Dispatch RAM ECC Error Enable */
+ FMAN_EX_IRAM_ECC, /* Double bit ECC occurred on IRAM */
+ FMAN_EX_MURAM_ECC /* Double bit ECC occurred on MURAM */
+};
+
+/* Parse results memory layout */
+struct fman_prs_result {
+ u8 lpid; /* Logical port id */
+ u8 shimr; /* Shim header result */
+ u16 l2r; /* Layer 2 result */
+ u16 l3r; /* Layer 3 result */
+ u8 l4r; /* Layer 4 result */
+ u8 cplan; /* Classification plan id */
+ u16 nxthdr; /* Next Header */
+ u16 cksum; /* Running-sum */
+ /* Flags&fragment-offset field of the last IP-header */
+ u16 flags_frag_off;
+ /* Routing type field of a IPV6 routing extension header */
+ u8 route_type;
+ /* Routing Extension Header Present; last bit is IP valid */
+ u8 rhp_ip_valid;
+ u8 shim_off[2]; /* Shim offset */
+ u8 ip_pid_off; /* IP PID (last IP-proto) offset */
+ u8 eth_off; /* ETH offset */
+ u8 llc_snap_off; /* LLC_SNAP offset */
+ u8 vlan_off[2]; /* VLAN offset */
+ u8 etype_off; /* ETYPE offset */
+ u8 pppoe_off; /* PPP offset */
+ u8 mpls_off[2]; /* MPLS offset */
+ u8 ip_off[2]; /* IP offset */
+ u8 gre_off; /* GRE offset */
+ u8 l4_off; /* Layer 4 offset */
+ u8 nxthdr_off; /* Parser end point */
+};
+
+/* A structure for defining buffer prefix area content. */
+struct fman_buffer_prefix_content {
+ /* Number of bytes to be left at the beginning of the external
+ * buffer; Note that the private-area will start from the base
+ * of the buffer address.
+ */
+ u16 priv_data_size;
+ /* true to pass the parse result to/from the FM;
+ * User may use FM_PORT_GetBufferPrsResult() in
+ * order to get the parser-result from a buffer.
+ */
+ bool pass_prs_result;
+ /* true to pass the timeStamp to/from the FM User */
+ bool pass_time_stamp;
+ /* true to pass the KG hash result to/from the FM User may
+ * use FM_PORT_GetBufferHashResult() in order to get the
+ * parser-result from a buffer.
+ */
+ bool pass_hash_result;
+ /* Add all other Internal-Context information: AD,
+ * hash-result, key, etc.
+ */
+ u16 data_align;
+};
+
+/* A structure of information about each of the external
+ * buffer pools used by a port or storage-profile.
+ */
+struct fman_ext_pool_params {
+ u8 id; /* External buffer pool id */
+ u16 size; /* External buffer pool buffer size */
+};
+
+/* A structure for informing the driver about the external
+ * buffer pools allocated in the BM and used by a port or a
+ * storage-profile.
+ */
+struct fman_ext_pools {
+ u8 num_of_pools_used; /* Number of pools use by this port */
+ struct fman_ext_pool_params ext_buf_pool[FMAN_PORT_MAX_EXT_POOLS_NUM];
+ /* Parameters for each port */
+};
+
+/* A structure for defining BM pool depletion criteria */
+struct fman_buf_pool_depletion {
+ /* select mode in which pause frames will be sent after a
+ * number of pools (all together!) are depleted
+ */
+ bool pools_grp_mode_enable;
+ /* the number of depleted pools that will invoke pause
+ * frames transmission.
+ */
+ u8 num_of_pools;
+ /* For each pool, true if it should be considered for
+ * depletion (Note - this pool must be used by this port!).
+ */
+ bool pools_to_consider[BM_MAX_NUM_OF_POOLS];
+ /* select mode in which pause frames will be sent
+ * after a single-pool is depleted;
+ */
+ bool single_pool_mode_enable;
+ /* For each pool, true if it should be considered
+ * for depletion (Note - this pool must be used by this port!)
+ */
+ bool pools_to_consider_for_single_mode[BM_MAX_NUM_OF_POOLS];
+};
+
+/* Enum for inter-module interrupts registration */
+enum fman_event_modules {
+ FMAN_MOD_MAC = 0, /* MAC event */
+ FMAN_MOD_FMAN_CTRL, /* FMAN Controller */
+ FMAN_MOD_DUMMY_LAST
+};
+
+/* Enum for interrupts types */
+enum fman_intr_type {
+ FMAN_INTR_TYPE_ERR,
+ FMAN_INTR_TYPE_NORMAL
+};
+
+/* Enum for inter-module interrupts registration */
+enum fman_inter_module_event {
+ FMAN_EV_ERR_MAC0 = 0, /* MAC 0 error event */
+ FMAN_EV_ERR_MAC1, /* MAC 1 error event */
+ FMAN_EV_ERR_MAC2, /* MAC 2 error event */
+ FMAN_EV_ERR_MAC3, /* MAC 3 error event */
+ FMAN_EV_ERR_MAC4, /* MAC 4 error event */
+ FMAN_EV_ERR_MAC5, /* MAC 5 error event */
+ FMAN_EV_ERR_MAC6, /* MAC 6 error event */
+ FMAN_EV_ERR_MAC7, /* MAC 7 error event */
+ FMAN_EV_ERR_MAC8, /* MAC 8 error event */
+ FMAN_EV_ERR_MAC9, /* MAC 9 error event */
+ FMAN_EV_MAC0, /* MAC 0 event (Magic packet detection) */
+ FMAN_EV_MAC1, /* MAC 1 event (Magic packet detection) */
+ FMAN_EV_MAC2, /* MAC 2 (Magic packet detection) */
+ FMAN_EV_MAC3, /* MAC 3 (Magic packet detection) */
+ FMAN_EV_MAC4, /* MAC 4 (Magic packet detection) */
+ FMAN_EV_MAC5, /* MAC 5 (Magic packet detection) */
+ FMAN_EV_MAC6, /* MAC 6 (Magic packet detection) */
+ FMAN_EV_MAC7, /* MAC 7 (Magic packet detection) */
+ FMAN_EV_MAC8, /* MAC 8 event (Magic packet detection) */
+ FMAN_EV_MAC9, /* MAC 9 event (Magic packet detection) */
+ FMAN_EV_FMAN_CTRL_0, /* Fman controller event 0 */
+ FMAN_EV_FMAN_CTRL_1, /* Fman controller event 1 */
+ FMAN_EV_FMAN_CTRL_2, /* Fman controller event 2 */
+ FMAN_EV_FMAN_CTRL_3, /* Fman controller event 3 */
+ FMAN_EV_CNT
+};
+
+struct fman_intr_src {
+ void (*isr_cb)(void *src_arg);
+ void *src_handle;
+};
+
+/* Structure for port-FM communication during fman_port_init. */
+struct fman_port_init_params {
+ u8 port_id; /* port Id */
+ enum fman_port_type port_type; /* Port type */
+ u16 port_speed; /* Port speed */
+ u16 liodn_offset; /* Port's requested resource */
+ u8 num_of_tasks; /* Port's requested resource */
+ u8 num_of_extra_tasks; /* Port's requested resource */
+ u8 num_of_open_dmas; /* Port's requested resource */
+ u8 num_of_extra_open_dmas; /* Port's requested resource */
+ u32 size_of_fifo; /* Port's requested resource */
+ u32 extra_size_of_fifo; /* Port's requested resource */
+ u8 deq_pipeline_depth; /* Port's requested resource */
+ u16 max_frame_length; /* Port's max frame length. */
+ u16 liodn_base;
+ /* LIODN base for this port, to be used together with LIODN offset. */
+};
+
+void fman_get_revision(struct fman *fman, struct fman_rev_info *rev_info);
+
+void fman_register_intr(struct fman *fman, enum fman_event_modules mod,
+ u8 mod_id, enum fman_intr_type intr_type,
+ void (*f_isr)(void *h_src_arg), void *h_src_arg);
+
+void fman_unregister_intr(struct fman *fman, enum fman_event_modules mod,
+ u8 mod_id, enum fman_intr_type intr_type);
+
+int fman_set_port_params(struct fman *fman,
+ struct fman_port_init_params *port_params);
+
+int fman_reset_mac(struct fman *fman, u8 mac_id);
+
+u16 fman_get_clock_freq(struct fman *fman);
+
+u32 fman_get_bmi_max_fifo_size(struct fman *fman);
+
+int fman_set_mac_max_frame(struct fman *fman, u8 mac_id, u16 mfl);
+
+u32 fman_get_qman_channel_id(struct fman *fman, u32 port_id);
+
+struct resource *fman_get_mem_region(struct fman *fman);
+
+u16 fman_get_max_frm(void);
+
+int fman_get_rx_extra_headroom(void);
+
+struct fman *fman_bind(struct device *dev);
+
+#endif /* __FM_H */
diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c
new file mode 100644
index 000000000000..6b1261c0b1c2
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c
@@ -0,0 +1,1453 @@
+/*
+ * Copyright 2008-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "fman_dtsec.h"
+#include "fman.h"
+
+#include <linux/slab.h>
+#include <linux/bitrev.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/phy.h>
+#include <linux/crc32.h>
+#include <linux/of_mdio.h>
+#include <linux/mii.h>
+
+/* TBI register addresses */
+#define MII_TBICON 0x11
+
+/* TBICON register bit fields */
+#define TBICON_SOFT_RESET 0x8000 /* Soft reset */
+#define TBICON_DISABLE_RX_DIS 0x2000 /* Disable receive disparity */
+#define TBICON_DISABLE_TX_DIS 0x1000 /* Disable transmit disparity */
+#define TBICON_AN_SENSE 0x0100 /* Auto-negotiation sense enable */
+#define TBICON_CLK_SELECT 0x0020 /* Clock select */
+#define TBICON_MI_MODE 0x0010 /* GMII mode (TBI if not set) */
+
+#define TBIANA_SGMII 0x4001
+#define TBIANA_1000X 0x01a0
+
+/* Interrupt Mask Register (IMASK) */
+#define DTSEC_IMASK_BREN 0x80000000
+#define DTSEC_IMASK_RXCEN 0x40000000
+#define DTSEC_IMASK_MSROEN 0x04000000
+#define DTSEC_IMASK_GTSCEN 0x02000000
+#define DTSEC_IMASK_BTEN 0x01000000
+#define DTSEC_IMASK_TXCEN 0x00800000
+#define DTSEC_IMASK_TXEEN 0x00400000
+#define DTSEC_IMASK_LCEN 0x00040000
+#define DTSEC_IMASK_CRLEN 0x00020000
+#define DTSEC_IMASK_XFUNEN 0x00010000
+#define DTSEC_IMASK_ABRTEN 0x00008000
+#define DTSEC_IMASK_IFERREN 0x00004000
+#define DTSEC_IMASK_MAGEN 0x00000800
+#define DTSEC_IMASK_MMRDEN 0x00000400
+#define DTSEC_IMASK_MMWREN 0x00000200
+#define DTSEC_IMASK_GRSCEN 0x00000100
+#define DTSEC_IMASK_TDPEEN 0x00000002
+#define DTSEC_IMASK_RDPEEN 0x00000001
+
+#define DTSEC_EVENTS_MASK \
+ ((u32)(DTSEC_IMASK_BREN | \
+ DTSEC_IMASK_RXCEN | \
+ DTSEC_IMASK_BTEN | \
+ DTSEC_IMASK_TXCEN | \
+ DTSEC_IMASK_TXEEN | \
+ DTSEC_IMASK_ABRTEN | \
+ DTSEC_IMASK_LCEN | \
+ DTSEC_IMASK_CRLEN | \
+ DTSEC_IMASK_XFUNEN | \
+ DTSEC_IMASK_IFERREN | \
+ DTSEC_IMASK_MAGEN | \
+ DTSEC_IMASK_TDPEEN | \
+ DTSEC_IMASK_RDPEEN))
+
+/* dtsec timestamp event bits */
+#define TMR_PEMASK_TSREEN 0x00010000
+#define TMR_PEVENT_TSRE 0x00010000
+
+/* Group address bit indication */
+#define MAC_GROUP_ADDRESS 0x0000010000000000ULL
+
+/* Defaults */
+#define DEFAULT_HALFDUP_RETRANSMIT 0xf
+#define DEFAULT_HALFDUP_COLL_WINDOW 0x37
+#define DEFAULT_TX_PAUSE_TIME 0xf000
+#define DEFAULT_RX_PREPEND 0
+#define DEFAULT_PREAMBLE_LEN 7
+#define DEFAULT_TX_PAUSE_TIME_EXTD 0
+#define DEFAULT_NON_BACK_TO_BACK_IPG1 0x40
+#define DEFAULT_NON_BACK_TO_BACK_IPG2 0x60
+#define DEFAULT_MIN_IFG_ENFORCEMENT 0x50
+#define DEFAULT_BACK_TO_BACK_IPG 0x60
+#define DEFAULT_MAXIMUM_FRAME 0x600
+
+/* register related defines (bits, field offsets..) */
+#define DTSEC_ID2_INT_REDUCED_OFF 0x00010000
+
+#define DTSEC_ECNTRL_GMIIM 0x00000040
+#define DTSEC_ECNTRL_TBIM 0x00000020
+#define DTSEC_ECNTRL_SGMIIM 0x00000002
+#define DTSEC_ECNTRL_RPM 0x00000010
+#define DTSEC_ECNTRL_R100M 0x00000008
+#define DTSEC_ECNTRL_QSGMIIM 0x00000001
+
+#define DTSEC_TCTRL_GTS 0x00000020
+
+#define RCTRL_PAL_MASK 0x001f0000
+#define RCTRL_PAL_SHIFT 16
+#define RCTRL_GHTX 0x00000400
+#define RCTRL_GRS 0x00000020
+#define RCTRL_MPROM 0x00000008
+#define RCTRL_RSF 0x00000004
+#define RCTRL_UPROM 0x00000001
+
+#define MACCFG1_SOFT_RESET 0x80000000
+#define MACCFG1_RX_FLOW 0x00000020
+#define MACCFG1_TX_FLOW 0x00000010
+#define MACCFG1_TX_EN 0x00000001
+#define MACCFG1_RX_EN 0x00000004
+
+#define MACCFG2_NIBBLE_MODE 0x00000100
+#define MACCFG2_BYTE_MODE 0x00000200
+#define MACCFG2_PAD_CRC_EN 0x00000004
+#define MACCFG2_FULL_DUPLEX 0x00000001
+#define MACCFG2_PREAMBLE_LENGTH_MASK 0x0000f000
+#define MACCFG2_PREAMBLE_LENGTH_SHIFT 12
+
+#define IPGIFG_NON_BACK_TO_BACK_IPG_1_SHIFT 24
+#define IPGIFG_NON_BACK_TO_BACK_IPG_2_SHIFT 16
+#define IPGIFG_MIN_IFG_ENFORCEMENT_SHIFT 8
+
+#define IPGIFG_NON_BACK_TO_BACK_IPG_1 0x7F000000
+#define IPGIFG_NON_BACK_TO_BACK_IPG_2 0x007F0000
+#define IPGIFG_MIN_IFG_ENFORCEMENT 0x0000FF00
+#define IPGIFG_BACK_TO_BACK_IPG 0x0000007F
+
+#define HAFDUP_EXCESS_DEFER 0x00010000
+#define HAFDUP_COLLISION_WINDOW 0x000003ff
+#define HAFDUP_RETRANSMISSION_MAX_SHIFT 12
+#define HAFDUP_RETRANSMISSION_MAX 0x0000f000
+
+#define NUM_OF_HASH_REGS 8 /* Number of hash table registers */
+
+#define PTV_PTE_MASK 0xffff0000
+#define PTV_PT_MASK 0x0000ffff
+#define PTV_PTE_SHIFT 16
+
+#define MAX_PACKET_ALIGNMENT 31
+#define MAX_INTER_PACKET_GAP 0x7f
+#define MAX_RETRANSMISSION 0x0f
+#define MAX_COLLISION_WINDOW 0x03ff
+
+/* Hash table size (32 bits*8 regs) */
+#define DTSEC_HASH_TABLE_SIZE 256
+/* Extended Hash table size (32 bits*16 regs) */
+#define EXTENDED_HASH_TABLE_SIZE 512
+
+/* dTSEC Memory Map registers */
+struct dtsec_regs {
+ /* dTSEC General Control and Status Registers */
+ u32 tsec_id; /* 0x000 ETSEC_ID register */
+ u32 tsec_id2; /* 0x004 ETSEC_ID2 register */
+ u32 ievent; /* 0x008 Interrupt event register */
+ u32 imask; /* 0x00C Interrupt mask register */
+ u32 reserved0010[1];
+ u32 ecntrl; /* 0x014 E control register */
+ u32 ptv; /* 0x018 Pause time value register */
+ u32 tbipa; /* 0x01C TBI PHY address register */
+ u32 tmr_ctrl; /* 0x020 Time-stamp Control register */
+ u32 tmr_pevent; /* 0x024 Time-stamp event register */
+ u32 tmr_pemask; /* 0x028 Timer event mask register */
+ u32 reserved002c[5];
+ u32 tctrl; /* 0x040 Transmit control register */
+ u32 reserved0044[3];
+ u32 rctrl; /* 0x050 Receive control register */
+ u32 reserved0054[11];
+ u32 igaddr[8]; /* 0x080-0x09C Individual/group address */
+ u32 gaddr[8]; /* 0x0A0-0x0BC Group address registers 0-7 */
+ u32 reserved00c0[16];
+ u32 maccfg1; /* 0x100 MAC configuration #1 */
+ u32 maccfg2; /* 0x104 MAC configuration #2 */
+ u32 ipgifg; /* 0x108 IPG/IFG */
+ u32 hafdup; /* 0x10C Half-duplex */
+ u32 maxfrm; /* 0x110 Maximum frame */
+ u32 reserved0114[10];
+ u32 ifstat; /* 0x13C Interface status */
+ u32 macstnaddr1; /* 0x140 Station Address,part 1 */
+ u32 macstnaddr2; /* 0x144 Station Address,part 2 */
+ struct {
+ u32 exact_match1; /* octets 1-4 */
+ u32 exact_match2; /* octets 5-6 */
+ } macaddr[15]; /* 0x148-0x1BC mac exact match addresses 1-15 */
+ u32 reserved01c0[16];
+ u32 tr64; /* 0x200 Tx and Rx 64 byte frame counter */
+ u32 tr127; /* 0x204 Tx and Rx 65 to 127 byte frame counter */
+ u32 tr255; /* 0x208 Tx and Rx 128 to 255 byte frame counter */
+ u32 tr511; /* 0x20C Tx and Rx 256 to 511 byte frame counter */
+ u32 tr1k; /* 0x210 Tx and Rx 512 to 1023 byte frame counter */
+ u32 trmax; /* 0x214 Tx and Rx 1024 to 1518 byte frame counter */
+ u32 trmgv;
+ /* 0x218 Tx and Rx 1519 to 1522 byte good VLAN frame count */
+ u32 rbyt; /* 0x21C receive byte counter */
+ u32 rpkt; /* 0x220 receive packet counter */
+ u32 rfcs; /* 0x224 receive FCS error counter */
+ u32 rmca; /* 0x228 RMCA Rx multicast packet counter */
+ u32 rbca; /* 0x22C Rx broadcast packet counter */
+ u32 rxcf; /* 0x230 Rx control frame packet counter */
+ u32 rxpf; /* 0x234 Rx pause frame packet counter */
+ u32 rxuo; /* 0x238 Rx unknown OP code counter */
+ u32 raln; /* 0x23C Rx alignment error counter */
+ u32 rflr; /* 0x240 Rx frame length error counter */
+ u32 rcde; /* 0x244 Rx code error counter */
+ u32 rcse; /* 0x248 Rx carrier sense error counter */
+ u32 rund; /* 0x24C Rx undersize packet counter */
+ u32 rovr; /* 0x250 Rx oversize packet counter */
+ u32 rfrg; /* 0x254 Rx fragments counter */
+ u32 rjbr; /* 0x258 Rx jabber counter */
+ u32 rdrp; /* 0x25C Rx drop */
+ u32 tbyt; /* 0x260 Tx byte counter */
+ u32 tpkt; /* 0x264 Tx packet counter */
+ u32 tmca; /* 0x268 Tx multicast packet counter */
+ u32 tbca; /* 0x26C Tx broadcast packet counter */
+ u32 txpf; /* 0x270 Tx pause control frame counter */
+ u32 tdfr; /* 0x274 Tx deferral packet counter */
+ u32 tedf; /* 0x278 Tx excessive deferral packet counter */
+ u32 tscl; /* 0x27C Tx single collision packet counter */
+ u32 tmcl; /* 0x280 Tx multiple collision packet counter */
+ u32 tlcl; /* 0x284 Tx late collision packet counter */
+ u32 txcl; /* 0x288 Tx excessive collision packet counter */
+ u32 tncl; /* 0x28C Tx total collision counter */
+ u32 reserved0290[1];
+ u32 tdrp; /* 0x294 Tx drop frame counter */
+ u32 tjbr; /* 0x298 Tx jabber frame counter */
+ u32 tfcs; /* 0x29C Tx FCS error counter */
+ u32 txcf; /* 0x2A0 Tx control frame counter */
+ u32 tovr; /* 0x2A4 Tx oversize frame counter */
+ u32 tund; /* 0x2A8 Tx undersize frame counter */
+ u32 tfrg; /* 0x2AC Tx fragments frame counter */
+ u32 car1; /* 0x2B0 carry register one register* */
+ u32 car2; /* 0x2B4 carry register two register* */
+ u32 cam1; /* 0x2B8 carry register one mask register */
+ u32 cam2; /* 0x2BC carry register two mask register */
+ u32 reserved02c0[848];
+};
+
+/* struct dtsec_cfg - dTSEC configuration
+ * Transmit half-duplex flow control, under software control for 10/100-Mbps
+ * half-duplex media. If set, back pressure is applied to media by raising
+ * carrier.
+ * halfdup_retransmit:
+ * Number of retransmission attempts following a collision.
+ * If this is exceeded dTSEC aborts transmission due to excessive collisions.
+ * The standard specifies the attempt limit to be 15.
+ * halfdup_coll_window:
+ * The number of bytes of the frame during which collisions may occur.
+ * The default value of 55 corresponds to the frame byte at the end of the
+ * standard 512-bit slot time window. If collisions are detected after this
+ * byte, the late collision event is asserted and transmission of current
+ * frame is aborted.
+ * tx_pad_crc:
+ * Pad and append CRC. If set, the MAC pads all ransmitted short frames and
+ * appends a CRC to every frame regardless of padding requirement.
+ * tx_pause_time:
+ * Transmit pause time value. This pause value is used as part of the pause
+ * frame to be sent when a transmit pause frame is initiated.
+ * If set to 0 this disables transmission of pause frames.
+ * preamble_len:
+ * Length, in bytes, of the preamble field preceding each Ethernet
+ * start-of-frame delimiter byte. The default value of 0x7 should be used in
+ * order to guarantee reliable operation with IEEE 802.3 compliant hardware.
+ * rx_prepend:
+ * Packet alignment padding length. The specified number of bytes (1-31)
+ * of zero padding are inserted before the start of each received frame.
+ * For Ethernet, where optional preamble extraction is enabled, the padding
+ * appears before the preamble, otherwise the padding precedes the
+ * layer 2 header.
+ *
+ * This structure contains basic dTSEC configuration and must be passed to
+ * init() function. A default set of configuration values can be
+ * obtained by calling set_dflts().
+ */
+struct dtsec_cfg {
+ u16 halfdup_retransmit;
+ u16 halfdup_coll_window;
+ bool tx_pad_crc;
+ u16 tx_pause_time;
+ bool ptp_tsu_en;
+ bool ptp_exception_en;
+ u32 preamble_len;
+ u32 rx_prepend;
+ u16 tx_pause_time_extd;
+ u16 maximum_frame;
+ u32 non_back_to_back_ipg1;
+ u32 non_back_to_back_ipg2;
+ u32 min_ifg_enforcement;
+ u32 back_to_back_ipg;
+};
+
+struct fman_mac {
+ /* pointer to dTSEC memory mapped registers */
+ struct dtsec_regs __iomem *regs;
+ /* MAC address of device */
+ u64 addr;
+ /* Ethernet physical interface */
+ phy_interface_t phy_if;
+ u16 max_speed;
+ void *dev_id; /* device cookie used by the exception cbs */
+ fman_mac_exception_cb *exception_cb;
+ fman_mac_exception_cb *event_cb;
+ /* Number of individual addresses in registers for this station */
+ u8 num_of_ind_addr_in_regs;
+ /* pointer to driver's global address hash table */
+ struct eth_hash_t *multicast_addr_hash;
+ /* pointer to driver's individual address hash table */
+ struct eth_hash_t *unicast_addr_hash;
+ u8 mac_id;
+ u32 exceptions;
+ bool ptp_tsu_enabled;
+ bool en_tsu_err_exeption;
+ struct dtsec_cfg *dtsec_drv_param;
+ void *fm;
+ struct fman_rev_info fm_rev_info;
+ bool basex_if;
+ struct phy_device *tbiphy;
+};
+
+static void set_dflts(struct dtsec_cfg *cfg)
+{
+ cfg->halfdup_retransmit = DEFAULT_HALFDUP_RETRANSMIT;
+ cfg->halfdup_coll_window = DEFAULT_HALFDUP_COLL_WINDOW;
+ cfg->tx_pad_crc = true;
+ cfg->tx_pause_time = DEFAULT_TX_PAUSE_TIME;
+ /* PHY address 0 is reserved (DPAA RM) */
+ cfg->rx_prepend = DEFAULT_RX_PREPEND;
+ cfg->ptp_tsu_en = true;
+ cfg->ptp_exception_en = true;
+ cfg->preamble_len = DEFAULT_PREAMBLE_LEN;
+ cfg->tx_pause_time_extd = DEFAULT_TX_PAUSE_TIME_EXTD;
+ cfg->non_back_to_back_ipg1 = DEFAULT_NON_BACK_TO_BACK_IPG1;
+ cfg->non_back_to_back_ipg2 = DEFAULT_NON_BACK_TO_BACK_IPG2;
+ cfg->min_ifg_enforcement = DEFAULT_MIN_IFG_ENFORCEMENT;
+ cfg->back_to_back_ipg = DEFAULT_BACK_TO_BACK_IPG;
+ cfg->maximum_frame = DEFAULT_MAXIMUM_FRAME;
+}
+
+static int init(struct dtsec_regs __iomem *regs, struct dtsec_cfg *cfg,
+ phy_interface_t iface, u16 iface_speed, u8 *macaddr,
+ u32 exception_mask, u8 tbi_addr)
+{
+ bool is_rgmii, is_sgmii, is_qsgmii;
+ int i;
+ u32 tmp;
+
+ /* Soft reset */
+ iowrite32be(MACCFG1_SOFT_RESET, &regs->maccfg1);
+ iowrite32be(0, &regs->maccfg1);
+
+ /* dtsec_id2 */
+ tmp = ioread32be(&regs->tsec_id2);
+
+ /* check RGMII support */
+ if (iface == PHY_INTERFACE_MODE_RGMII ||
+ iface == PHY_INTERFACE_MODE_RMII)
+ if (tmp & DTSEC_ID2_INT_REDUCED_OFF)
+ return -EINVAL;
+
+ if (iface == PHY_INTERFACE_MODE_SGMII ||
+ iface == PHY_INTERFACE_MODE_MII)
+ if (tmp & DTSEC_ID2_INT_REDUCED_OFF)
+ return -EINVAL;
+
+ is_rgmii = iface == PHY_INTERFACE_MODE_RGMII;
+ is_sgmii = iface == PHY_INTERFACE_MODE_SGMII;
+ is_qsgmii = iface == PHY_INTERFACE_MODE_QSGMII;
+
+ tmp = 0;
+ if (is_rgmii || iface == PHY_INTERFACE_MODE_GMII)
+ tmp |= DTSEC_ECNTRL_GMIIM;
+ if (is_sgmii)
+ tmp |= (DTSEC_ECNTRL_SGMIIM | DTSEC_ECNTRL_TBIM);
+ if (is_qsgmii)
+ tmp |= (DTSEC_ECNTRL_SGMIIM | DTSEC_ECNTRL_TBIM |
+ DTSEC_ECNTRL_QSGMIIM);
+ if (is_rgmii)
+ tmp |= DTSEC_ECNTRL_RPM;
+ if (iface_speed == SPEED_100)
+ tmp |= DTSEC_ECNTRL_R100M;
+
+ iowrite32be(tmp, &regs->ecntrl);
+
+ tmp = 0;
+
+ if (cfg->tx_pause_time)
+ tmp |= cfg->tx_pause_time;
+ if (cfg->tx_pause_time_extd)
+ tmp |= cfg->tx_pause_time_extd << PTV_PTE_SHIFT;
+ iowrite32be(tmp, &regs->ptv);
+
+ tmp = 0;
+ tmp |= (cfg->rx_prepend << RCTRL_PAL_SHIFT) & RCTRL_PAL_MASK;
+ /* Accept short frames */
+ tmp |= RCTRL_RSF;
+
+ iowrite32be(tmp, &regs->rctrl);
+
+ /* Assign a Phy Address to the TBI (TBIPA).
+ * Done also in cases where TBI is not selected to avoid conflict with
+ * the external PHY's Physical address
+ */
+ iowrite32be(tbi_addr, &regs->tbipa);
+
+ iowrite32be(0, &regs->tmr_ctrl);
+
+ if (cfg->ptp_tsu_en) {
+ tmp = 0;
+ tmp |= TMR_PEVENT_TSRE;
+ iowrite32be(tmp, &regs->tmr_pevent);
+
+ if (cfg->ptp_exception_en) {
+ tmp = 0;
+ tmp |= TMR_PEMASK_TSREEN;
+ iowrite32be(tmp, &regs->tmr_pemask);
+ }
+ }
+
+ tmp = 0;
+ tmp |= MACCFG1_RX_FLOW;
+ tmp |= MACCFG1_TX_FLOW;
+ iowrite32be(tmp, &regs->maccfg1);
+
+ tmp = 0;
+
+ if (iface_speed < SPEED_1000)
+ tmp |= MACCFG2_NIBBLE_MODE;
+ else if (iface_speed == SPEED_1000)
+ tmp |= MACCFG2_BYTE_MODE;
+
+ tmp |= (cfg->preamble_len << MACCFG2_PREAMBLE_LENGTH_SHIFT) &
+ MACCFG2_PREAMBLE_LENGTH_MASK;
+ if (cfg->tx_pad_crc)
+ tmp |= MACCFG2_PAD_CRC_EN;
+ /* Full Duplex */
+ tmp |= MACCFG2_FULL_DUPLEX;
+ iowrite32be(tmp, &regs->maccfg2);
+
+ tmp = (((cfg->non_back_to_back_ipg1 <<
+ IPGIFG_NON_BACK_TO_BACK_IPG_1_SHIFT)
+ & IPGIFG_NON_BACK_TO_BACK_IPG_1)
+ | ((cfg->non_back_to_back_ipg2 <<
+ IPGIFG_NON_BACK_TO_BACK_IPG_2_SHIFT)
+ & IPGIFG_NON_BACK_TO_BACK_IPG_2)
+ | ((cfg->min_ifg_enforcement << IPGIFG_MIN_IFG_ENFORCEMENT_SHIFT)
+ & IPGIFG_MIN_IFG_ENFORCEMENT)
+ | (cfg->back_to_back_ipg & IPGIFG_BACK_TO_BACK_IPG));
+ iowrite32be(tmp, &regs->ipgifg);
+
+ tmp = 0;
+ tmp |= HAFDUP_EXCESS_DEFER;
+ tmp |= ((cfg->halfdup_retransmit << HAFDUP_RETRANSMISSION_MAX_SHIFT)
+ & HAFDUP_RETRANSMISSION_MAX);
+ tmp |= (cfg->halfdup_coll_window & HAFDUP_COLLISION_WINDOW);
+
+ iowrite32be(tmp, &regs->hafdup);
+
+ /* Initialize Maximum frame length */
+ iowrite32be(cfg->maximum_frame, &regs->maxfrm);
+
+ iowrite32be(0xffffffff, &regs->cam1);
+ iowrite32be(0xffffffff, &regs->cam2);
+
+ iowrite32be(exception_mask, &regs->imask);
+
+ iowrite32be(0xffffffff, &regs->ievent);
+
+ tmp = (u32)((macaddr[5] << 24) |
+ (macaddr[4] << 16) | (macaddr[3] << 8) | macaddr[2]);
+ iowrite32be(tmp, &regs->macstnaddr1);
+
+ tmp = (u32)((macaddr[1] << 24) | (macaddr[0] << 16));
+ iowrite32be(tmp, &regs->macstnaddr2);
+
+ /* HASH */
+ for (i = 0; i < NUM_OF_HASH_REGS; i++) {
+ /* Initialize IADDRx */
+ iowrite32be(0, &regs->igaddr[i]);
+ /* Initialize GADDRx */
+ iowrite32be(0, &regs->gaddr[i]);
+ }
+
+ return 0;
+}
+
+static void set_mac_address(struct dtsec_regs __iomem *regs, u8 *adr)
+{
+ u32 tmp;
+
+ tmp = (u32)((adr[5] << 24) |
+ (adr[4] << 16) | (adr[3] << 8) | adr[2]);
+ iowrite32be(tmp, &regs->macstnaddr1);
+
+ tmp = (u32)((adr[1] << 24) | (adr[0] << 16));
+ iowrite32be(tmp, &regs->macstnaddr2);
+}
+
+static void set_bucket(struct dtsec_regs __iomem *regs, int bucket,
+ bool enable)
+{
+ int reg_idx = (bucket >> 5) & 0xf;
+ int bit_idx = bucket & 0x1f;
+ u32 bit_mask = 0x80000000 >> bit_idx;
+ u32 __iomem *reg;
+
+ if (reg_idx > 7)
+ reg = &regs->gaddr[reg_idx - 8];
+ else
+ reg = &regs->igaddr[reg_idx];
+
+ if (enable)
+ iowrite32be(ioread32be(reg) | bit_mask, reg);
+ else
+ iowrite32be(ioread32be(reg) & (~bit_mask), reg);
+}
+
+static int check_init_parameters(struct fman_mac *dtsec)
+{
+ if (dtsec->max_speed >= SPEED_10000) {
+ pr_err("1G MAC driver supports 1G or lower speeds\n");
+ return -EINVAL;
+ }
+ if (dtsec->addr == 0) {
+ pr_err("Ethernet MAC Must have a valid MAC Address\n");
+ return -EINVAL;
+ }
+ if ((dtsec->dtsec_drv_param)->rx_prepend >
+ MAX_PACKET_ALIGNMENT) {
+ pr_err("packetAlignmentPadding can't be > than %d\n",
+ MAX_PACKET_ALIGNMENT);
+ return -EINVAL;
+ }
+ if (((dtsec->dtsec_drv_param)->non_back_to_back_ipg1 >
+ MAX_INTER_PACKET_GAP) ||
+ ((dtsec->dtsec_drv_param)->non_back_to_back_ipg2 >
+ MAX_INTER_PACKET_GAP) ||
+ ((dtsec->dtsec_drv_param)->back_to_back_ipg >
+ MAX_INTER_PACKET_GAP)) {
+ pr_err("Inter packet gap can't be greater than %d\n",
+ MAX_INTER_PACKET_GAP);
+ return -EINVAL;
+ }
+ if ((dtsec->dtsec_drv_param)->halfdup_retransmit >
+ MAX_RETRANSMISSION) {
+ pr_err("maxRetransmission can't be greater than %d\n",
+ MAX_RETRANSMISSION);
+ return -EINVAL;
+ }
+ if ((dtsec->dtsec_drv_param)->halfdup_coll_window >
+ MAX_COLLISION_WINDOW) {
+ pr_err("collisionWindow can't be greater than %d\n",
+ MAX_COLLISION_WINDOW);
+ return -EINVAL;
+ /* If Auto negotiation process is disabled, need to set up the PHY
+ * using the MII Management Interface
+ */
+ }
+ if (!dtsec->exception_cb) {
+ pr_err("uninitialized exception_cb\n");
+ return -EINVAL;
+ }
+ if (!dtsec->event_cb) {
+ pr_err("uninitialized event_cb\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int get_exception_flag(enum fman_mac_exceptions exception)
+{
+ u32 bit_mask;
+
+ switch (exception) {
+ case FM_MAC_EX_1G_BAB_RX:
+ bit_mask = DTSEC_IMASK_BREN;
+ break;
+ case FM_MAC_EX_1G_RX_CTL:
+ bit_mask = DTSEC_IMASK_RXCEN;
+ break;
+ case FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET:
+ bit_mask = DTSEC_IMASK_GTSCEN;
+ break;
+ case FM_MAC_EX_1G_BAB_TX:
+ bit_mask = DTSEC_IMASK_BTEN;
+ break;
+ case FM_MAC_EX_1G_TX_CTL:
+ bit_mask = DTSEC_IMASK_TXCEN;
+ break;
+ case FM_MAC_EX_1G_TX_ERR:
+ bit_mask = DTSEC_IMASK_TXEEN;
+ break;
+ case FM_MAC_EX_1G_LATE_COL:
+ bit_mask = DTSEC_IMASK_LCEN;
+ break;
+ case FM_MAC_EX_1G_COL_RET_LMT:
+ bit_mask = DTSEC_IMASK_CRLEN;
+ break;
+ case FM_MAC_EX_1G_TX_FIFO_UNDRN:
+ bit_mask = DTSEC_IMASK_XFUNEN;
+ break;
+ case FM_MAC_EX_1G_MAG_PCKT:
+ bit_mask = DTSEC_IMASK_MAGEN;
+ break;
+ case FM_MAC_EX_1G_MII_MNG_RD_COMPLET:
+ bit_mask = DTSEC_IMASK_MMRDEN;
+ break;
+ case FM_MAC_EX_1G_MII_MNG_WR_COMPLET:
+ bit_mask = DTSEC_IMASK_MMWREN;
+ break;
+ case FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET:
+ bit_mask = DTSEC_IMASK_GRSCEN;
+ break;
+ case FM_MAC_EX_1G_DATA_ERR:
+ bit_mask = DTSEC_IMASK_TDPEEN;
+ break;
+ case FM_MAC_EX_1G_RX_MIB_CNT_OVFL:
+ bit_mask = DTSEC_IMASK_MSROEN;
+ break;
+ default:
+ bit_mask = 0;
+ break;
+ }
+
+ return bit_mask;
+}
+
+static bool is_init_done(struct dtsec_cfg *dtsec_drv_params)
+{
+ /* Checks if dTSEC driver parameters were initialized */
+ if (!dtsec_drv_params)
+ return true;
+
+ return false;
+}
+
+static u16 dtsec_get_max_frame_length(struct fman_mac *dtsec)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+
+ if (is_init_done(dtsec->dtsec_drv_param))
+ return 0;
+
+ return (u16)ioread32be(&regs->maxfrm);
+}
+
+static void dtsec_isr(void *handle)
+{
+ struct fman_mac *dtsec = (struct fman_mac *)handle;
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ u32 event;
+
+ /* do not handle MDIO events */
+ event = ioread32be(&regs->ievent) &
+ (u32)(~(DTSEC_IMASK_MMRDEN | DTSEC_IMASK_MMWREN));
+
+ event &= ioread32be(&regs->imask);
+
+ iowrite32be(event, &regs->ievent);
+
+ if (event & DTSEC_IMASK_BREN)
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_BAB_RX);
+ if (event & DTSEC_IMASK_RXCEN)
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_RX_CTL);
+ if (event & DTSEC_IMASK_GTSCEN)
+ dtsec->exception_cb(dtsec->dev_id,
+ FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET);
+ if (event & DTSEC_IMASK_BTEN)
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_BAB_TX);
+ if (event & DTSEC_IMASK_TXCEN)
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_TX_CTL);
+ if (event & DTSEC_IMASK_TXEEN)
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_TX_ERR);
+ if (event & DTSEC_IMASK_LCEN)
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_LATE_COL);
+ if (event & DTSEC_IMASK_CRLEN)
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_COL_RET_LMT);
+ if (event & DTSEC_IMASK_XFUNEN) {
+ /* FM_TX_LOCKUP_ERRATA_DTSEC6 Errata workaround */
+ if (dtsec->fm_rev_info.major == 2) {
+ u32 tpkt1, tmp_reg1, tpkt2, tmp_reg2, i;
+ /* a. Write 0x00E0_0C00 to DTSEC_ID
+ * This is a read only register
+ * b. Read and save the value of TPKT
+ */
+ tpkt1 = ioread32be(&regs->tpkt);
+
+ /* c. Read the register at dTSEC address offset 0x32C */
+ tmp_reg1 = ioread32be(&regs->reserved02c0[27]);
+
+ /* d. Compare bits [9:15] to bits [25:31] of the
+ * register at address offset 0x32C.
+ */
+ if ((tmp_reg1 & 0x007F0000) !=
+ (tmp_reg1 & 0x0000007F)) {
+ /* If they are not equal, save the value of
+ * this register and wait for at least
+ * MAXFRM*16 ns
+ */
+ usleep_range((u32)(min
+ (dtsec_get_max_frame_length(dtsec) *
+ 16 / 1000, 1)), (u32)
+ (min(dtsec_get_max_frame_length
+ (dtsec) * 16 / 1000, 1) + 1));
+ }
+
+ /* e. Read and save TPKT again and read the register
+ * at dTSEC address offset 0x32C again
+ */
+ tpkt2 = ioread32be(&regs->tpkt);
+ tmp_reg2 = ioread32be(&regs->reserved02c0[27]);
+
+ /* f. Compare the value of TPKT saved in step b to
+ * value read in step e. Also compare bits [9:15] of
+ * the register at offset 0x32C saved in step d to the
+ * value of bits [9:15] saved in step e. If the two
+ * registers values are unchanged, then the transmit
+ * portion of the dTSEC controller is locked up and
+ * the user should proceed to the recover sequence.
+ */
+ if ((tpkt1 == tpkt2) && ((tmp_reg1 & 0x007F0000) ==
+ (tmp_reg2 & 0x007F0000))) {
+ /* recover sequence */
+
+ /* a.Write a 1 to RCTRL[GRS] */
+
+ iowrite32be(ioread32be(&regs->rctrl) |
+ RCTRL_GRS, &regs->rctrl);
+
+ /* b.Wait until IEVENT[GRSC]=1, or at least
+ * 100 us has elapsed.
+ */
+ for (i = 0; i < 100; i++) {
+ if (ioread32be(&regs->ievent) &
+ DTSEC_IMASK_GRSCEN)
+ break;
+ udelay(1);
+ }
+ if (ioread32be(&regs->ievent) &
+ DTSEC_IMASK_GRSCEN)
+ iowrite32be(DTSEC_IMASK_GRSCEN,
+ &regs->ievent);
+ else
+ pr_debug("Rx lockup due to Tx lockup\n");
+
+ /* c.Write a 1 to bit n of FM_RSTC
+ * (offset 0x0CC of FPM)
+ */
+ fman_reset_mac(dtsec->fm, dtsec->mac_id);
+
+ /* d.Wait 4 Tx clocks (32 ns) */
+ udelay(1);
+
+ /* e.Write a 0 to bit n of FM_RSTC. */
+ /* cleared by FMAN
+ */
+ }
+ }
+
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_TX_FIFO_UNDRN);
+ }
+ if (event & DTSEC_IMASK_MAGEN)
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_MAG_PCKT);
+ if (event & DTSEC_IMASK_GRSCEN)
+ dtsec->exception_cb(dtsec->dev_id,
+ FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET);
+ if (event & DTSEC_IMASK_TDPEEN)
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_DATA_ERR);
+ if (event & DTSEC_IMASK_RDPEEN)
+ dtsec->exception_cb(dtsec->dev_id, FM_MAC_1G_RX_DATA_ERR);
+
+ /* masked interrupts */
+ WARN_ON(event & DTSEC_IMASK_ABRTEN);
+ WARN_ON(event & DTSEC_IMASK_IFERREN);
+}
+
+static void dtsec_1588_isr(void *handle)
+{
+ struct fman_mac *dtsec = (struct fman_mac *)handle;
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ u32 event;
+
+ if (dtsec->ptp_tsu_enabled) {
+ event = ioread32be(&regs->tmr_pevent);
+ event &= ioread32be(&regs->tmr_pemask);
+
+ if (event) {
+ iowrite32be(event, &regs->tmr_pevent);
+ WARN_ON(event & TMR_PEVENT_TSRE);
+ dtsec->exception_cb(dtsec->dev_id,
+ FM_MAC_EX_1G_1588_TS_RX_ERR);
+ }
+ }
+}
+
+static void free_init_resources(struct fman_mac *dtsec)
+{
+ fman_unregister_intr(dtsec->fm, FMAN_MOD_MAC, dtsec->mac_id,
+ FMAN_INTR_TYPE_ERR);
+ fman_unregister_intr(dtsec->fm, FMAN_MOD_MAC, dtsec->mac_id,
+ FMAN_INTR_TYPE_NORMAL);
+
+ /* release the driver's group hash table */
+ free_hash_table(dtsec->multicast_addr_hash);
+ dtsec->multicast_addr_hash = NULL;
+
+ /* release the driver's individual hash table */
+ free_hash_table(dtsec->unicast_addr_hash);
+ dtsec->unicast_addr_hash = NULL;
+}
+
+int dtsec_cfg_max_frame_len(struct fman_mac *dtsec, u16 new_val)
+{
+ if (is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ dtsec->dtsec_drv_param->maximum_frame = new_val;
+
+ return 0;
+}
+
+int dtsec_cfg_pad_and_crc(struct fman_mac *dtsec, bool new_val)
+{
+ if (is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ dtsec->dtsec_drv_param->tx_pad_crc = new_val;
+
+ return 0;
+}
+
+int dtsec_enable(struct fman_mac *dtsec, enum comm_mode mode)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ u32 tmp;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ /* Enable */
+ tmp = ioread32be(&regs->maccfg1);
+ if (mode & COMM_MODE_RX)
+ tmp |= MACCFG1_RX_EN;
+ if (mode & COMM_MODE_TX)
+ tmp |= MACCFG1_TX_EN;
+
+ iowrite32be(tmp, &regs->maccfg1);
+
+ /* Graceful start - clear the graceful receive stop bit */
+ if (mode & COMM_MODE_TX)
+ iowrite32be(ioread32be(&regs->tctrl) & ~DTSEC_TCTRL_GTS,
+ &regs->tctrl);
+ if (mode & COMM_MODE_RX)
+ iowrite32be(ioread32be(&regs->rctrl) & ~RCTRL_GRS,
+ &regs->rctrl);
+
+ return 0;
+}
+
+int dtsec_disable(struct fman_mac *dtsec, enum comm_mode mode)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ u32 tmp;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ /* Gracefull stop - Assert the graceful transmit stop bit */
+ if (mode & COMM_MODE_RX) {
+ tmp = ioread32be(&regs->rctrl) | RCTRL_GRS;
+ iowrite32be(tmp, &regs->rctrl);
+
+ if (dtsec->fm_rev_info.major == 2)
+ usleep_range(100, 200);
+ else
+ udelay(10);
+ }
+
+ if (mode & COMM_MODE_TX) {
+ if (dtsec->fm_rev_info.major == 2)
+ pr_debug("GTS not supported due to DTSEC_A004 errata.\n");
+ else
+ pr_debug("GTS not supported due to DTSEC_A0014 errata.\n");
+ }
+
+ tmp = ioread32be(&regs->maccfg1);
+ if (mode & COMM_MODE_RX)
+ tmp &= ~MACCFG1_RX_EN;
+ if (mode & COMM_MODE_TX)
+ tmp &= ~MACCFG1_TX_EN;
+
+ iowrite32be(tmp, &regs->maccfg1);
+
+ return 0;
+}
+
+int dtsec_set_tx_pause_frames(struct fman_mac *dtsec,
+ u8 __maybe_unused priority,
+ u16 pause_time, u16 __maybe_unused thresh_time)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ u32 ptv = 0;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ /* FM_BAD_TX_TS_IN_B_2_B_ERRATA_DTSEC_A003 Errata workaround */
+ if (dtsec->fm_rev_info.major == 2)
+ if (pause_time <= 320) {
+ pr_warn("pause-time: %d illegal.Should be > 320\n",
+ pause_time);
+ return -EINVAL;
+ }
+
+ if (pause_time) {
+ ptv = ioread32be(&regs->ptv);
+ ptv &= PTV_PTE_MASK;
+ ptv |= pause_time & PTV_PT_MASK;
+ iowrite32be(ptv, &regs->ptv);
+
+ /* trigger the transmission of a flow-control pause frame */
+ iowrite32be(ioread32be(&regs->maccfg1) | MACCFG1_TX_FLOW,
+ &regs->maccfg1);
+ } else
+ iowrite32be(ioread32be(&regs->maccfg1) & ~MACCFG1_TX_FLOW,
+ &regs->maccfg1);
+
+ return 0;
+}
+
+int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ u32 tmp;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->maccfg1);
+ if (en)
+ tmp |= MACCFG1_RX_FLOW;
+ else
+ tmp &= ~MACCFG1_RX_FLOW;
+ iowrite32be(tmp, &regs->maccfg1);
+
+ return 0;
+}
+
+int dtsec_modify_mac_address(struct fman_mac *dtsec, enet_addr_t *enet_addr)
+{
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ /* Initialize MAC Station Address registers (1 & 2)
+ * Station address have to be swapped (big endian to little endian
+ */
+ dtsec->addr = ENET_ADDR_TO_UINT64(*enet_addr);
+ set_mac_address(dtsec->regs, (u8 *)(*enet_addr));
+
+ return 0;
+}
+
+int dtsec_add_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ struct eth_hash_entry *hash_entry;
+ u64 addr;
+ s32 bucket;
+ u32 crc = 0xFFFFFFFF;
+ bool mcast, ghtx;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ addr = ENET_ADDR_TO_UINT64(*eth_addr);
+
+ ghtx = (bool)((ioread32be(&regs->rctrl) & RCTRL_GHTX) ? true : false);
+ mcast = (bool)((addr & MAC_GROUP_ADDRESS) ? true : false);
+
+ /* Cannot handle unicast mac addr when GHTX is on */
+ if (ghtx && !mcast) {
+ pr_err("Could not compute hash bucket\n");
+ return -EINVAL;
+ }
+ crc = crc32_le(crc, (u8 *)eth_addr, ETH_ALEN);
+ crc = bitrev32(crc);
+
+ /* considering the 9 highest order bits in crc H[8:0]:
+ *if ghtx = 0 H[8:6] (highest order 3 bits) identify the hash register
+ *and H[5:1] (next 5 bits) identify the hash bit
+ *if ghts = 1 H[8:5] (highest order 4 bits) identify the hash register
+ *and H[4:0] (next 5 bits) identify the hash bit.
+ *
+ *In bucket index output the low 5 bits identify the hash register
+ *bit, while the higher 4 bits identify the hash register
+ */
+
+ if (ghtx) {
+ bucket = (s32)((crc >> 23) & 0x1ff);
+ } else {
+ bucket = (s32)((crc >> 24) & 0xff);
+ /* if !ghtx and mcast the bit must be set in gaddr instead of
+ *igaddr.
+ */
+ if (mcast)
+ bucket += 0x100;
+ }
+
+ set_bucket(dtsec->regs, bucket, true);
+
+ /* Create element to be added to the driver hash table */
+ hash_entry = kmalloc(sizeof(*hash_entry), GFP_KERNEL);
+ if (!hash_entry)
+ return -ENOMEM;
+ hash_entry->addr = addr;
+ INIT_LIST_HEAD(&hash_entry->node);
+
+ if (addr & MAC_GROUP_ADDRESS)
+ /* Group Address */
+ list_add_tail(&hash_entry->node,
+ &dtsec->multicast_addr_hash->lsts[bucket]);
+ else
+ list_add_tail(&hash_entry->node,
+ &dtsec->unicast_addr_hash->lsts[bucket]);
+
+ return 0;
+}
+
+int dtsec_del_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ struct list_head *pos;
+ struct eth_hash_entry *hash_entry = NULL;
+ u64 addr;
+ s32 bucket;
+ u32 crc = 0xFFFFFFFF;
+ bool mcast, ghtx;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ addr = ENET_ADDR_TO_UINT64(*eth_addr);
+
+ ghtx = (bool)((ioread32be(&regs->rctrl) & RCTRL_GHTX) ? true : false);
+ mcast = (bool)((addr & MAC_GROUP_ADDRESS) ? true : false);
+
+ /* Cannot handle unicast mac addr when GHTX is on */
+ if (ghtx && !mcast) {
+ pr_err("Could not compute hash bucket\n");
+ return -EINVAL;
+ }
+ crc = crc32_le(crc, (u8 *)eth_addr, ETH_ALEN);
+ crc = bitrev32(crc);
+
+ if (ghtx) {
+ bucket = (s32)((crc >> 23) & 0x1ff);
+ } else {
+ bucket = (s32)((crc >> 24) & 0xff);
+ /* if !ghtx and mcast the bit must be set
+ * in gaddr instead of igaddr.
+ */
+ if (mcast)
+ bucket += 0x100;
+ }
+
+ if (addr & MAC_GROUP_ADDRESS) {
+ /* Group Address */
+ list_for_each(pos,
+ &dtsec->multicast_addr_hash->lsts[bucket]) {
+ hash_entry = ETH_HASH_ENTRY_OBJ(pos);
+ if (hash_entry->addr == addr) {
+ list_del_init(&hash_entry->node);
+ kfree(hash_entry);
+ break;
+ }
+ }
+ if (list_empty(&dtsec->multicast_addr_hash->lsts[bucket]))
+ set_bucket(dtsec->regs, bucket, false);
+ } else {
+ /* Individual Address */
+ list_for_each(pos,
+ &dtsec->unicast_addr_hash->lsts[bucket]) {
+ hash_entry = ETH_HASH_ENTRY_OBJ(pos);
+ if (hash_entry->addr == addr) {
+ list_del_init(&hash_entry->node);
+ kfree(hash_entry);
+ break;
+ }
+ }
+ if (list_empty(&dtsec->unicast_addr_hash->lsts[bucket]))
+ set_bucket(dtsec->regs, bucket, false);
+ }
+
+ /* address does not exist */
+ WARN_ON(!hash_entry);
+
+ return 0;
+}
+
+int dtsec_set_promiscuous(struct fman_mac *dtsec, bool new_val)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ u32 tmp;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ /* Set unicast promiscuous */
+ tmp = ioread32be(&regs->rctrl);
+ if (new_val)
+ tmp |= RCTRL_UPROM;
+ else
+ tmp &= ~RCTRL_UPROM;
+
+ iowrite32be(tmp, &regs->rctrl);
+
+ /* Set multicast promiscuous */
+ tmp = ioread32be(&regs->rctrl);
+ if (new_val)
+ tmp |= RCTRL_MPROM;
+ else
+ tmp &= ~RCTRL_MPROM;
+
+ iowrite32be(tmp, &regs->rctrl);
+
+ return 0;
+}
+
+int dtsec_adjust_link(struct fman_mac *dtsec, u16 speed)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ u32 tmp;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->maccfg2);
+
+ /* Full Duplex */
+ tmp |= MACCFG2_FULL_DUPLEX;
+
+ tmp &= ~(MACCFG2_NIBBLE_MODE | MACCFG2_BYTE_MODE);
+ if (speed < SPEED_1000)
+ tmp |= MACCFG2_NIBBLE_MODE;
+ else if (speed == SPEED_1000)
+ tmp |= MACCFG2_BYTE_MODE;
+ iowrite32be(tmp, &regs->maccfg2);
+
+ tmp = ioread32be(&regs->ecntrl);
+ if (speed == SPEED_100)
+ tmp |= DTSEC_ECNTRL_R100M;
+ else
+ tmp &= ~DTSEC_ECNTRL_R100M;
+ iowrite32be(tmp, &regs->ecntrl);
+
+ return 0;
+}
+
+int dtsec_restart_autoneg(struct fman_mac *dtsec)
+{
+ u16 tmp_reg16;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ tmp_reg16 = phy_read(dtsec->tbiphy, MII_BMCR);
+
+ tmp_reg16 &= ~(BMCR_SPEED100 | BMCR_SPEED1000);
+ tmp_reg16 |= (BMCR_ANENABLE | BMCR_ANRESTART |
+ BMCR_FULLDPLX | BMCR_SPEED1000);
+
+ phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16);
+
+ return 0;
+}
+
+int dtsec_get_version(struct fman_mac *dtsec, u32 *mac_version)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ *mac_version = ioread32be(&regs->tsec_id);
+
+ return 0;
+}
+
+int dtsec_set_exception(struct fman_mac *dtsec,
+ enum fman_mac_exceptions exception, bool enable)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ u32 bit_mask = 0;
+
+ if (!is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ if (exception != FM_MAC_EX_1G_1588_TS_RX_ERR) {
+ bit_mask = get_exception_flag(exception);
+ if (bit_mask) {
+ if (enable)
+ dtsec->exceptions |= bit_mask;
+ else
+ dtsec->exceptions &= ~bit_mask;
+ } else {
+ pr_err("Undefined exception\n");
+ return -EINVAL;
+ }
+ if (enable)
+ iowrite32be(ioread32be(&regs->imask) | bit_mask,
+ &regs->imask);
+ else
+ iowrite32be(ioread32be(&regs->imask) & ~bit_mask,
+ &regs->imask);
+ } else {
+ if (!dtsec->ptp_tsu_enabled) {
+ pr_err("Exception valid for 1588 only\n");
+ return -EINVAL;
+ }
+ switch (exception) {
+ case FM_MAC_EX_1G_1588_TS_RX_ERR:
+ if (enable) {
+ dtsec->en_tsu_err_exeption = true;
+ iowrite32be(ioread32be(&regs->tmr_pemask) |
+ TMR_PEMASK_TSREEN,
+ &regs->tmr_pemask);
+ } else {
+ dtsec->en_tsu_err_exeption = false;
+ iowrite32be(ioread32be(&regs->tmr_pemask) &
+ ~TMR_PEMASK_TSREEN,
+ &regs->tmr_pemask);
+ }
+ break;
+ default:
+ pr_err("Undefined exception\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+int dtsec_init(struct fman_mac *dtsec)
+{
+ struct dtsec_regs __iomem *regs = dtsec->regs;
+ struct dtsec_cfg *dtsec_drv_param;
+ int err;
+ u16 max_frm_ln;
+ enet_addr_t eth_addr;
+
+ if (is_init_done(dtsec->dtsec_drv_param))
+ return -EINVAL;
+
+ if (DEFAULT_RESET_ON_INIT &&
+ (fman_reset_mac(dtsec->fm, dtsec->mac_id) != 0)) {
+ pr_err("Can't reset MAC!\n");
+ return -EINVAL;
+ }
+
+ err = check_init_parameters(dtsec);
+ if (err)
+ return err;
+
+ dtsec_drv_param = dtsec->dtsec_drv_param;
+
+ MAKE_ENET_ADDR_FROM_UINT64(dtsec->addr, eth_addr);
+
+ err = init(dtsec->regs, dtsec_drv_param, dtsec->phy_if,
+ dtsec->max_speed, (u8 *)eth_addr, dtsec->exceptions,
+ dtsec->tbiphy->mdio.addr);
+ if (err) {
+ free_init_resources(dtsec);
+ pr_err("DTSEC version doesn't support this i/f mode\n");
+ return err;
+ }
+
+ if (dtsec->phy_if == PHY_INTERFACE_MODE_SGMII) {
+ u16 tmp_reg16;
+
+ /* Configure the TBI PHY Control Register */
+ tmp_reg16 = TBICON_CLK_SELECT | TBICON_SOFT_RESET;
+ phy_write(dtsec->tbiphy, MII_TBICON, tmp_reg16);
+
+ tmp_reg16 = TBICON_CLK_SELECT;
+ phy_write(dtsec->tbiphy, MII_TBICON, tmp_reg16);
+
+ tmp_reg16 = (BMCR_RESET | BMCR_ANENABLE |
+ BMCR_FULLDPLX | BMCR_SPEED1000);
+ phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16);
+
+ if (dtsec->basex_if)
+ tmp_reg16 = TBIANA_1000X;
+ else
+ tmp_reg16 = TBIANA_SGMII;
+ phy_write(dtsec->tbiphy, MII_ADVERTISE, tmp_reg16);
+
+ tmp_reg16 = (BMCR_ANENABLE | BMCR_ANRESTART |
+ BMCR_FULLDPLX | BMCR_SPEED1000);
+
+ phy_write(dtsec->tbiphy, MII_BMCR, tmp_reg16);
+ }
+
+ /* Max Frame Length */
+ max_frm_ln = (u16)ioread32be(&regs->maxfrm);
+ err = fman_set_mac_max_frame(dtsec->fm, dtsec->mac_id, max_frm_ln);
+ if (err) {
+ pr_err("Setting max frame length failed\n");
+ free_init_resources(dtsec);
+ return -EINVAL;
+ }
+
+ dtsec->multicast_addr_hash =
+ alloc_hash_table(EXTENDED_HASH_TABLE_SIZE);
+ if (!dtsec->multicast_addr_hash) {
+ free_init_resources(dtsec);
+ pr_err("MC hash table is failed\n");
+ return -ENOMEM;
+ }
+
+ dtsec->unicast_addr_hash = alloc_hash_table(DTSEC_HASH_TABLE_SIZE);
+ if (!dtsec->unicast_addr_hash) {
+ free_init_resources(dtsec);
+ pr_err("UC hash table is failed\n");
+ return -ENOMEM;
+ }
+
+ /* register err intr handler for dtsec to FPM (err) */
+ fman_register_intr(dtsec->fm, FMAN_MOD_MAC, dtsec->mac_id,
+ FMAN_INTR_TYPE_ERR, dtsec_isr, dtsec);
+ /* register 1588 intr handler for TMR to FPM (normal) */
+ fman_register_intr(dtsec->fm, FMAN_MOD_MAC, dtsec->mac_id,
+ FMAN_INTR_TYPE_NORMAL, dtsec_1588_isr, dtsec);
+
+ kfree(dtsec_drv_param);
+ dtsec->dtsec_drv_param = NULL;
+
+ return 0;
+}
+
+int dtsec_free(struct fman_mac *dtsec)
+{
+ free_init_resources(dtsec);
+
+ kfree(dtsec->dtsec_drv_param);
+ dtsec->dtsec_drv_param = NULL;
+ kfree(dtsec);
+
+ return 0;
+}
+
+struct fman_mac *dtsec_config(struct fman_mac_params *params)
+{
+ struct fman_mac *dtsec;
+ struct dtsec_cfg *dtsec_drv_param;
+ void __iomem *base_addr;
+
+ base_addr = params->base_addr;
+
+ /* allocate memory for the UCC GETH data structure. */
+ dtsec = kzalloc(sizeof(*dtsec), GFP_KERNEL);
+ if (!dtsec)
+ return NULL;
+
+ /* allocate memory for the d_tsec driver parameters data structure. */
+ dtsec_drv_param = kzalloc(sizeof(*dtsec_drv_param), GFP_KERNEL);
+ if (!dtsec_drv_param)
+ goto err_dtsec;
+
+ /* Plant parameter structure pointer */
+ dtsec->dtsec_drv_param = dtsec_drv_param;
+
+ set_dflts(dtsec_drv_param);
+
+ dtsec->regs = base_addr;
+ dtsec->addr = ENET_ADDR_TO_UINT64(params->addr);
+ dtsec->max_speed = params->max_speed;
+ dtsec->phy_if = params->phy_if;
+ dtsec->mac_id = params->mac_id;
+ dtsec->exceptions = (DTSEC_IMASK_BREN |
+ DTSEC_IMASK_RXCEN |
+ DTSEC_IMASK_BTEN |
+ DTSEC_IMASK_TXCEN |
+ DTSEC_IMASK_TXEEN |
+ DTSEC_IMASK_ABRTEN |
+ DTSEC_IMASK_LCEN |
+ DTSEC_IMASK_CRLEN |
+ DTSEC_IMASK_XFUNEN |
+ DTSEC_IMASK_IFERREN |
+ DTSEC_IMASK_MAGEN |
+ DTSEC_IMASK_TDPEEN |
+ DTSEC_IMASK_RDPEEN);
+ dtsec->exception_cb = params->exception_cb;
+ dtsec->event_cb = params->event_cb;
+ dtsec->dev_id = params->dev_id;
+ dtsec->ptp_tsu_enabled = dtsec->dtsec_drv_param->ptp_tsu_en;
+ dtsec->en_tsu_err_exeption = dtsec->dtsec_drv_param->ptp_exception_en;
+
+ dtsec->fm = params->fm;
+ dtsec->basex_if = params->basex_if;
+
+ if (!params->internal_phy_node) {
+ pr_err("TBI PHY node is not available\n");
+ goto err_dtsec_drv_param;
+ }
+
+ dtsec->tbiphy = of_phy_find_device(params->internal_phy_node);
+ if (!dtsec->tbiphy) {
+ pr_err("of_phy_find_device (TBI PHY) failed\n");
+ put_device(&dtsec->tbiphy->mdio.dev);
+ goto err_dtsec_drv_param;
+ }
+
+ put_device(&dtsec->tbiphy->mdio.dev);
+
+ /* Save FMan revision */
+ fman_get_revision(dtsec->fm, &dtsec->fm_rev_info);
+
+ return dtsec;
+
+err_dtsec_drv_param:
+ kfree(dtsec_drv_param);
+err_dtsec:
+ kfree(dtsec);
+ return NULL;
+}
diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.h b/drivers/net/ethernet/freescale/fman/fman_dtsec.h
new file mode 100644
index 000000000000..c4467c072058
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2008-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DTSEC_H
+#define __DTSEC_H
+
+#include "fman_mac.h"
+
+struct fman_mac *dtsec_config(struct fman_mac_params *params);
+int dtsec_set_promiscuous(struct fman_mac *dtsec, bool new_val);
+int dtsec_modify_mac_address(struct fman_mac *dtsec, enet_addr_t *enet_addr);
+int dtsec_adjust_link(struct fman_mac *dtsec,
+ u16 speed);
+int dtsec_restart_autoneg(struct fman_mac *dtsec);
+int dtsec_cfg_max_frame_len(struct fman_mac *dtsec, u16 new_val);
+int dtsec_cfg_pad_and_crc(struct fman_mac *dtsec, bool new_val);
+int dtsec_enable(struct fman_mac *dtsec, enum comm_mode mode);
+int dtsec_disable(struct fman_mac *dtsec, enum comm_mode mode);
+int dtsec_init(struct fman_mac *dtsec);
+int dtsec_free(struct fman_mac *dtsec);
+int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en);
+int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, u8 priority,
+ u16 pause_time, u16 thresh_time);
+int dtsec_set_exception(struct fman_mac *dtsec,
+ enum fman_mac_exceptions exception, bool enable);
+int dtsec_add_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr);
+int dtsec_del_hash_mac_address(struct fman_mac *dtsec, enet_addr_t *eth_addr);
+int dtsec_get_version(struct fman_mac *dtsec, u32 *mac_version);
+
+#endif /* __DTSEC_H */
diff --git a/drivers/net/ethernet/freescale/fman/fman_mac.h b/drivers/net/ethernet/freescale/fman/fman_mac.h
new file mode 100644
index 000000000000..8ddeedbcef9c
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_mac.h
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2008-2015 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.
+ */
+
+/* FM MAC ... */
+#ifndef __FM_MAC_H
+#define __FM_MAC_H
+
+#include "fman.h"
+
+#include <linux/slab.h>
+#include <linux/phy.h>
+#include <linux/if_ether.h>
+
+struct fman_mac;
+
+/* Ethernet Address */
+typedef u8 enet_addr_t[ETH_ALEN];
+
+#define ENET_ADDR_TO_UINT64(_enet_addr) \
+ (u64)(((u64)(_enet_addr)[0] << 40) | \
+ ((u64)(_enet_addr)[1] << 32) | \
+ ((u64)(_enet_addr)[2] << 24) | \
+ ((u64)(_enet_addr)[3] << 16) | \
+ ((u64)(_enet_addr)[4] << 8) | \
+ ((u64)(_enet_addr)[5]))
+
+#define MAKE_ENET_ADDR_FROM_UINT64(_addr64, _enet_addr) \
+ do { \
+ int i; \
+ for (i = 0; i < ETH_ALEN; i++) \
+ (_enet_addr)[i] = \
+ (u8)((_addr64) >> ((5 - i) * 8)); \
+ } while (0)
+
+/* defaults */
+#define DEFAULT_RESET_ON_INIT false
+
+/* PFC defines */
+#define FSL_FM_PAUSE_TIME_ENABLE 0xf000
+#define FSL_FM_PAUSE_TIME_DISABLE 0
+#define FSL_FM_PAUSE_THRESH_DEFAULT 0
+
+#define FM_MAC_NO_PFC 0xff
+
+/* HASH defines */
+#define ETH_HASH_ENTRY_OBJ(ptr) \
+ hlist_entry_safe(ptr, struct eth_hash_entry, node)
+
+/* Enumeration (bit flags) of communication modes (Transmit,
+ * receive or both).
+ */
+enum comm_mode {
+ COMM_MODE_NONE = 0, /* No transmit/receive communication */
+ COMM_MODE_RX = 1, /* Only receive communication */
+ COMM_MODE_TX = 2, /* Only transmit communication */
+ COMM_MODE_RX_AND_TX = 3 /* Both transmit and receive communication */
+};
+
+/* FM MAC Exceptions */
+enum fman_mac_exceptions {
+ FM_MAC_EX_10G_MDIO_SCAN_EVENT = 0
+ /* 10GEC MDIO scan event interrupt */
+ , FM_MAC_EX_10G_MDIO_CMD_CMPL
+ /* 10GEC MDIO command completion interrupt */
+ , FM_MAC_EX_10G_REM_FAULT
+ /* 10GEC, mEMAC Remote fault interrupt */
+ , FM_MAC_EX_10G_LOC_FAULT
+ /* 10GEC, mEMAC Local fault interrupt */
+ , FM_MAC_EX_10G_TX_ECC_ER
+ /* 10GEC, mEMAC Transmit frame ECC error interrupt */
+ , FM_MAC_EX_10G_TX_FIFO_UNFL
+ /* 10GEC, mEMAC Transmit FIFO underflow interrupt */
+ , FM_MAC_EX_10G_TX_FIFO_OVFL
+ /* 10GEC, mEMAC Transmit FIFO overflow interrupt */
+ , FM_MAC_EX_10G_TX_ER
+ /* 10GEC Transmit frame error interrupt */
+ , FM_MAC_EX_10G_RX_FIFO_OVFL
+ /* 10GEC, mEMAC Receive FIFO overflow interrupt */
+ , FM_MAC_EX_10G_RX_ECC_ER
+ /* 10GEC, mEMAC Receive frame ECC error interrupt */
+ , FM_MAC_EX_10G_RX_JAB_FRM
+ /* 10GEC Receive jabber frame interrupt */
+ , FM_MAC_EX_10G_RX_OVRSZ_FRM
+ /* 10GEC Receive oversized frame interrupt */
+ , FM_MAC_EX_10G_RX_RUNT_FRM
+ /* 10GEC Receive runt frame interrupt */
+ , FM_MAC_EX_10G_RX_FRAG_FRM
+ /* 10GEC Receive fragment frame interrupt */
+ , FM_MAC_EX_10G_RX_LEN_ER
+ /* 10GEC Receive payload length error interrupt */
+ , FM_MAC_EX_10G_RX_CRC_ER
+ /* 10GEC Receive CRC error interrupt */
+ , FM_MAC_EX_10G_RX_ALIGN_ER
+ /* 10GEC Receive alignment error interrupt */
+ , FM_MAC_EX_1G_BAB_RX
+ /* dTSEC Babbling receive error */
+ , FM_MAC_EX_1G_RX_CTL
+ /* dTSEC Receive control (pause frame) interrupt */
+ , FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET
+ /* dTSEC Graceful transmit stop complete */
+ , FM_MAC_EX_1G_BAB_TX
+ /* dTSEC Babbling transmit error */
+ , FM_MAC_EX_1G_TX_CTL
+ /* dTSEC Transmit control (pause frame) interrupt */
+ , FM_MAC_EX_1G_TX_ERR
+ /* dTSEC Transmit error */
+ , FM_MAC_EX_1G_LATE_COL
+ /* dTSEC Late collision */
+ , FM_MAC_EX_1G_COL_RET_LMT
+ /* dTSEC Collision retry limit */
+ , FM_MAC_EX_1G_TX_FIFO_UNDRN
+ /* dTSEC Transmit FIFO underrun */
+ , FM_MAC_EX_1G_MAG_PCKT
+ /* dTSEC Magic Packet detection */
+ , FM_MAC_EX_1G_MII_MNG_RD_COMPLET
+ /* dTSEC MII management read completion */
+ , FM_MAC_EX_1G_MII_MNG_WR_COMPLET
+ /* dTSEC MII management write completion */
+ , FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET
+ /* dTSEC Graceful receive stop complete */
+ , FM_MAC_EX_1G_DATA_ERR
+ /* dTSEC Internal data error on transmit */
+ , FM_MAC_1G_RX_DATA_ERR
+ /* dTSEC Internal data error on receive */
+ , FM_MAC_EX_1G_1588_TS_RX_ERR
+ /* dTSEC Time-Stamp Receive Error */
+ , FM_MAC_EX_1G_RX_MIB_CNT_OVFL
+ /* dTSEC MIB counter overflow */
+ , FM_MAC_EX_TS_FIFO_ECC_ERR
+ /* mEMAC Time-stamp FIFO ECC error interrupt;
+ * not supported on T4240/B4860 rev1 chips
+ */
+ , FM_MAC_EX_MAGIC_PACKET_INDICATION = FM_MAC_EX_1G_MAG_PCKT
+ /* mEMAC Magic Packet Indication Interrupt */
+};
+
+struct eth_hash_entry {
+ u64 addr; /* Ethernet Address */
+ struct list_head node;
+};
+
+typedef void (fman_mac_exception_cb)(void *dev_id,
+ enum fman_mac_exceptions exceptions);
+
+/* FMan MAC config input */
+struct fman_mac_params {
+ /* Base of memory mapped FM MAC registers */
+ void __iomem *base_addr;
+ /* MAC address of device; First octet is sent first */
+ enet_addr_t addr;
+ /* MAC ID; numbering of dTSEC and 1G-mEMAC:
+ * 0 - FM_MAX_NUM_OF_1G_MACS;
+ * numbering of 10G-MAC (TGEC) and 10G-mEMAC:
+ * 0 - FM_MAX_NUM_OF_10G_MACS
+ */
+ u8 mac_id;
+ /* PHY interface */
+ phy_interface_t phy_if;
+ /* Note that the speed should indicate the maximum rate that
+ * this MAC should support rather than the actual speed;
+ */
+ u16 max_speed;
+ /* A handle to the FM object this port related to */
+ void *fm;
+ /* MDIO exceptions interrupt source - not valid for all
+ * MACs; MUST be set to 'NO_IRQ' for MACs that don't have
+ * mdio-irq, or for polling
+ */
+ void *dev_id; /* device cookie used by the exception cbs */
+ fman_mac_exception_cb *event_cb; /* MDIO Events Callback Routine */
+ fman_mac_exception_cb *exception_cb;/* Exception Callback Routine */
+ /* SGMII/QSGII interface with 1000BaseX auto-negotiation between MAC
+ * and phy or backplane; Note: 1000BaseX auto-negotiation relates only
+ * to interface between MAC and phy/backplane, SGMII phy can still
+ * synchronize with far-end phy at 10Mbps, 100Mbps or 1000Mbps
+ */
+ bool basex_if;
+ /* Pointer to TBI/PCS PHY node, used for TBI/PCS PHY access */
+ struct device_node *internal_phy_node;
+};
+
+struct eth_hash_t {
+ u16 size;
+ struct list_head *lsts;
+};
+
+static inline struct eth_hash_entry
+*dequeue_addr_from_hash_entry(struct list_head *addr_lst)
+{
+ struct eth_hash_entry *hash_entry = NULL;
+
+ if (!list_empty(addr_lst)) {
+ hash_entry = ETH_HASH_ENTRY_OBJ(addr_lst->next);
+ list_del_init(&hash_entry->node);
+ }
+ return hash_entry;
+}
+
+static inline void free_hash_table(struct eth_hash_t *hash)
+{
+ struct eth_hash_entry *hash_entry;
+ int i = 0;
+
+ if (hash) {
+ if (hash->lsts) {
+ for (i = 0; i < hash->size; i++) {
+ hash_entry =
+ dequeue_addr_from_hash_entry(&hash->lsts[i]);
+ while (hash_entry) {
+ kfree(hash_entry);
+ hash_entry =
+ dequeue_addr_from_hash_entry(&hash->
+ lsts[i]);
+ }
+ }
+
+ kfree(hash->lsts);
+ }
+
+ kfree(hash);
+ }
+}
+
+static inline struct eth_hash_t *alloc_hash_table(u16 size)
+{
+ u32 i;
+ struct eth_hash_t *hash;
+
+ /* Allocate address hash table */
+ hash = kmalloc_array(size, sizeof(struct eth_hash_t *), GFP_KERNEL);
+ if (!hash)
+ return NULL;
+
+ hash->size = size;
+
+ hash->lsts = kmalloc_array(hash->size, sizeof(struct list_head),
+ GFP_KERNEL);
+ if (!hash->lsts) {
+ kfree(hash);
+ return NULL;
+ }
+
+ for (i = 0; i < hash->size; i++)
+ INIT_LIST_HEAD(&hash->lsts[i]);
+
+ return hash;
+}
+
+#endif /* __FM_MAC_H */
diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.c b/drivers/net/ethernet/freescale/fman/fman_memac.c
new file mode 100644
index 000000000000..45e98fd8b79e
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_memac.c
@@ -0,0 +1,1170 @@
+/*
+ * Copyright 2008-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "fman_memac.h"
+#include "fman.h"
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/phy.h>
+#include <linux/of_mdio.h>
+
+/* PCS registers */
+#define MDIO_SGMII_CR 0x00
+#define MDIO_SGMII_DEV_ABIL_SGMII 0x04
+#define MDIO_SGMII_LINK_TMR_L 0x12
+#define MDIO_SGMII_LINK_TMR_H 0x13
+#define MDIO_SGMII_IF_MODE 0x14
+
+/* SGMII Control defines */
+#define SGMII_CR_AN_EN 0x1000
+#define SGMII_CR_RESTART_AN 0x0200
+#define SGMII_CR_FD 0x0100
+#define SGMII_CR_SPEED_SEL1_1G 0x0040
+#define SGMII_CR_DEF_VAL (SGMII_CR_AN_EN | SGMII_CR_FD | \
+ SGMII_CR_SPEED_SEL1_1G)
+
+/* SGMII Device Ability for SGMII defines */
+#define MDIO_SGMII_DEV_ABIL_SGMII_MODE 0x4001
+#define MDIO_SGMII_DEV_ABIL_BASEX_MODE 0x01A0
+
+/* Link timer define */
+#define LINK_TMR_L 0xa120
+#define LINK_TMR_H 0x0007
+#define LINK_TMR_L_BASEX 0xaf08
+#define LINK_TMR_H_BASEX 0x002f
+
+/* SGMII IF Mode defines */
+#define IF_MODE_USE_SGMII_AN 0x0002
+#define IF_MODE_SGMII_EN 0x0001
+#define IF_MODE_SGMII_SPEED_100M 0x0004
+#define IF_MODE_SGMII_SPEED_1G 0x0008
+#define IF_MODE_SGMII_DUPLEX_HALF 0x0010
+
+/* Num of additional exact match MAC adr regs */
+#define MEMAC_NUM_OF_PADDRS 7
+
+/* Control and Configuration Register (COMMAND_CONFIG) */
+#define CMD_CFG_REG_LOWP_RXETY 0x01000000 /* 07 Rx low power indication */
+#define CMD_CFG_TX_LOWP_ENA 0x00800000 /* 08 Tx Low Power Idle Enable */
+#define CMD_CFG_PFC_MODE 0x00080000 /* 12 Enable PFC */
+#define CMD_CFG_NO_LEN_CHK 0x00020000 /* 14 Payload length check disable */
+#define CMD_CFG_SW_RESET 0x00001000 /* 19 S/W Reset, self clearing bit */
+#define CMD_CFG_TX_PAD_EN 0x00000800 /* 20 Enable Tx padding of frames */
+#define CMD_CFG_PAUSE_IGNORE 0x00000100 /* 23 Ignore Pause frame quanta */
+#define CMD_CFG_CRC_FWD 0x00000040 /* 25 Terminate/frwd CRC of frames */
+#define CMD_CFG_PAD_EN 0x00000020 /* 26 Frame padding removal */
+#define CMD_CFG_PROMIS_EN 0x00000010 /* 27 Promiscuous operation enable */
+#define CMD_CFG_RX_EN 0x00000002 /* 30 MAC receive path enable */
+#define CMD_CFG_TX_EN 0x00000001 /* 31 MAC transmit path enable */
+
+/* Transmit FIFO Sections Register (TX_FIFO_SECTIONS) */
+#define TX_FIFO_SECTIONS_TX_EMPTY_MASK 0xFFFF0000
+#define TX_FIFO_SECTIONS_TX_AVAIL_MASK 0x0000FFFF
+#define TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G 0x00400000
+#define TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G 0x00100000
+#define TX_FIFO_SECTIONS_TX_AVAIL_10G 0x00000019
+#define TX_FIFO_SECTIONS_TX_AVAIL_1G 0x00000020
+#define TX_FIFO_SECTIONS_TX_AVAIL_SLOW_10G 0x00000060
+
+#define GET_TX_EMPTY_DEFAULT_VALUE(_val) \
+do { \
+ _val &= ~TX_FIFO_SECTIONS_TX_EMPTY_MASK; \
+ ((_val == TX_FIFO_SECTIONS_TX_AVAIL_10G) ? \
+ (_val |= TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G) :\
+ (_val |= TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G));\
+} while (0)
+
+/* Interface Mode Register (IF_MODE) */
+
+#define IF_MODE_MASK 0x00000003 /* 30-31 Mask on i/f mode bits */
+#define IF_MODE_XGMII 0x00000000 /* 30-31 XGMII (10G) interface */
+#define IF_MODE_GMII 0x00000002 /* 30-31 GMII (1G) interface */
+#define IF_MODE_RGMII 0x00000004
+#define IF_MODE_RGMII_AUTO 0x00008000
+#define IF_MODE_RGMII_1000 0x00004000 /* 10 - 1000Mbps RGMII */
+#define IF_MODE_RGMII_100 0x00000000 /* 00 - 100Mbps RGMII */
+#define IF_MODE_RGMII_10 0x00002000 /* 01 - 10Mbps RGMII */
+#define IF_MODE_RGMII_SP_MASK 0x00006000 /* Setsp mask bits */
+#define IF_MODE_RGMII_FD 0x00001000 /* Full duplex RGMII */
+#define IF_MODE_HD 0x00000040 /* Half duplex operation */
+
+/* Hash table Control Register (HASHTABLE_CTRL) */
+#define HASH_CTRL_MCAST_EN 0x00000100
+/* 26-31 Hash table address code */
+#define HASH_CTRL_ADDR_MASK 0x0000003F
+/* MAC mcast indication */
+#define GROUP_ADDRESS 0x0000010000000000LL
+#define HASH_TABLE_SIZE 64 /* Hash tbl size */
+
+/* Interrupt Mask Register (IMASK) */
+#define MEMAC_IMASK_MGI 0x40000000 /* 1 Magic pkt detect indication */
+#define MEMAC_IMASK_TSECC_ER 0x20000000 /* 2 Timestamp FIFO ECC error evnt */
+#define MEMAC_IMASK_TECC_ER 0x02000000 /* 6 Transmit frame ECC error evnt */
+#define MEMAC_IMASK_RECC_ER 0x01000000 /* 7 Receive frame ECC error evnt */
+
+#define MEMAC_ALL_ERRS_IMASK \
+ ((u32)(MEMAC_IMASK_TSECC_ER | \
+ MEMAC_IMASK_TECC_ER | \
+ MEMAC_IMASK_RECC_ER | \
+ MEMAC_IMASK_MGI))
+
+#define MEMAC_IEVNT_PCS 0x80000000 /* PCS (XG). Link sync (G) */
+#define MEMAC_IEVNT_AN 0x40000000 /* Auto-negotiation */
+#define MEMAC_IEVNT_LT 0x20000000 /* Link Training/New page */
+#define MEMAC_IEVNT_MGI 0x00004000 /* Magic pkt detection */
+#define MEMAC_IEVNT_TS_ECC_ER 0x00002000 /* Timestamp FIFO ECC error*/
+#define MEMAC_IEVNT_RX_FIFO_OVFL 0x00001000 /* Rx FIFO overflow */
+#define MEMAC_IEVNT_TX_FIFO_UNFL 0x00000800 /* Tx FIFO underflow */
+#define MEMAC_IEVNT_TX_FIFO_OVFL 0x00000400 /* Tx FIFO overflow */
+#define MEMAC_IEVNT_TX_ECC_ER 0x00000200 /* Tx frame ECC error */
+#define MEMAC_IEVNT_RX_ECC_ER 0x00000100 /* Rx frame ECC error */
+#define MEMAC_IEVNT_LI_FAULT 0x00000080 /* Link Interruption flt */
+#define MEMAC_IEVNT_RX_EMPTY 0x00000040 /* Rx FIFO empty */
+#define MEMAC_IEVNT_TX_EMPTY 0x00000020 /* Tx FIFO empty */
+#define MEMAC_IEVNT_RX_LOWP 0x00000010 /* Low Power Idle */
+#define MEMAC_IEVNT_PHY_LOS 0x00000004 /* Phy loss of signal */
+#define MEMAC_IEVNT_REM_FAULT 0x00000002 /* Remote fault (XGMII) */
+#define MEMAC_IEVNT_LOC_FAULT 0x00000001 /* Local fault (XGMII) */
+
+#define DEFAULT_PAUSE_QUANTA 0xf000
+#define DEFAULT_FRAME_LENGTH 0x600
+#define DEFAULT_TX_IPG_LENGTH 12
+
+#define CLXY_PAUSE_QUANTA_CLX_PQNT 0x0000FFFF
+#define CLXY_PAUSE_QUANTA_CLY_PQNT 0xFFFF0000
+#define CLXY_PAUSE_THRESH_CLX_QTH 0x0000FFFF
+#define CLXY_PAUSE_THRESH_CLY_QTH 0xFFFF0000
+
+struct mac_addr {
+ /* Lower 32 bits of 48-bit MAC address */
+ u32 mac_addr_l;
+ /* Upper 16 bits of 48-bit MAC address */
+ u32 mac_addr_u;
+};
+
+/* memory map */
+struct memac_regs {
+ u32 res0000[2]; /* General Control and Status */
+ u32 command_config; /* 0x008 Ctrl and cfg */
+ struct mac_addr mac_addr0; /* 0x00C-0x010 MAC_ADDR_0...1 */
+ u32 maxfrm; /* 0x014 Max frame length */
+ u32 res0018[1];
+ u32 rx_fifo_sections; /* Receive FIFO configuration reg */
+ u32 tx_fifo_sections; /* Transmit FIFO configuration reg */
+ u32 res0024[2];
+ u32 hashtable_ctrl; /* 0x02C Hash table control */
+ u32 res0030[4];
+ u32 ievent; /* 0x040 Interrupt event */
+ u32 tx_ipg_length; /* 0x044 Transmitter inter-packet-gap */
+ u32 res0048;
+ u32 imask; /* 0x04C Interrupt mask */
+ u32 res0050;
+ u32 pause_quanta[4]; /* 0x054 Pause quanta */
+ u32 pause_thresh[4]; /* 0x064 Pause quanta threshold */
+ u32 rx_pause_status; /* 0x074 Receive pause status */
+ u32 res0078[2];
+ struct mac_addr mac_addr[MEMAC_NUM_OF_PADDRS];/* 0x80-0x0B4 mac padr */
+ u32 lpwake_timer; /* 0x0B8 Low Power Wakeup Timer */
+ u32 sleep_timer; /* 0x0BC Transmit EEE Low Power Timer */
+ u32 res00c0[8];
+ u32 statn_config; /* 0x0E0 Statistics configuration */
+ u32 res00e4[7];
+ /* Rx Statistics Counter */
+ u32 reoct_l;
+ u32 reoct_u;
+ u32 roct_l;
+ u32 roct_u;
+ u32 raln_l;
+ u32 raln_u;
+ u32 rxpf_l;
+ u32 rxpf_u;
+ u32 rfrm_l;
+ u32 rfrm_u;
+ u32 rfcs_l;
+ u32 rfcs_u;
+ u32 rvlan_l;
+ u32 rvlan_u;
+ u32 rerr_l;
+ u32 rerr_u;
+ u32 ruca_l;
+ u32 ruca_u;
+ u32 rmca_l;
+ u32 rmca_u;
+ u32 rbca_l;
+ u32 rbca_u;
+ u32 rdrp_l;
+ u32 rdrp_u;
+ u32 rpkt_l;
+ u32 rpkt_u;
+ u32 rund_l;
+ u32 rund_u;
+ u32 r64_l;
+ u32 r64_u;
+ u32 r127_l;
+ u32 r127_u;
+ u32 r255_l;
+ u32 r255_u;
+ u32 r511_l;
+ u32 r511_u;
+ u32 r1023_l;
+ u32 r1023_u;
+ u32 r1518_l;
+ u32 r1518_u;
+ u32 r1519x_l;
+ u32 r1519x_u;
+ u32 rovr_l;
+ u32 rovr_u;
+ u32 rjbr_l;
+ u32 rjbr_u;
+ u32 rfrg_l;
+ u32 rfrg_u;
+ u32 rcnp_l;
+ u32 rcnp_u;
+ u32 rdrntp_l;
+ u32 rdrntp_u;
+ u32 res01d0[12];
+ /* Tx Statistics Counter */
+ u32 teoct_l;
+ u32 teoct_u;
+ u32 toct_l;
+ u32 toct_u;
+ u32 res0210[2];
+ u32 txpf_l;
+ u32 txpf_u;
+ u32 tfrm_l;
+ u32 tfrm_u;
+ u32 tfcs_l;
+ u32 tfcs_u;
+ u32 tvlan_l;
+ u32 tvlan_u;
+ u32 terr_l;
+ u32 terr_u;
+ u32 tuca_l;
+ u32 tuca_u;
+ u32 tmca_l;
+ u32 tmca_u;
+ u32 tbca_l;
+ u32 tbca_u;
+ u32 res0258[2];
+ u32 tpkt_l;
+ u32 tpkt_u;
+ u32 tund_l;
+ u32 tund_u;
+ u32 t64_l;
+ u32 t64_u;
+ u32 t127_l;
+ u32 t127_u;
+ u32 t255_l;
+ u32 t255_u;
+ u32 t511_l;
+ u32 t511_u;
+ u32 t1023_l;
+ u32 t1023_u;
+ u32 t1518_l;
+ u32 t1518_u;
+ u32 t1519x_l;
+ u32 t1519x_u;
+ u32 res02a8[6];
+ u32 tcnp_l;
+ u32 tcnp_u;
+ u32 res02c8[14];
+ /* Line Interface Control */
+ u32 if_mode; /* 0x300 Interface Mode Control */
+ u32 if_status; /* 0x304 Interface Status */
+ u32 res0308[14];
+ /* HiGig/2 */
+ u32 hg_config; /* 0x340 Control and cfg */
+ u32 res0344[3];
+ u32 hg_pause_quanta; /* 0x350 Pause quanta */
+ u32 res0354[3];
+ u32 hg_pause_thresh; /* 0x360 Pause quanta threshold */
+ u32 res0364[3];
+ u32 hgrx_pause_status; /* 0x370 Receive pause status */
+ u32 hg_fifos_status; /* 0x374 fifos status */
+ u32 rhm; /* 0x378 rx messages counter */
+ u32 thm; /* 0x37C tx messages counter */
+};
+
+struct memac_cfg {
+ bool reset_on_init;
+ bool pause_ignore;
+ bool promiscuous_mode_enable;
+ struct fixed_phy_status *fixed_link;
+ u16 max_frame_length;
+ u16 pause_quanta;
+ u32 tx_ipg_length;
+};
+
+struct fman_mac {
+ /* Pointer to MAC memory mapped registers */
+ struct memac_regs __iomem *regs;
+ /* MAC address of device */
+ u64 addr;
+ /* Ethernet physical interface */
+ phy_interface_t phy_if;
+ u16 max_speed;
+ void *dev_id; /* device cookie used by the exception cbs */
+ fman_mac_exception_cb *exception_cb;
+ fman_mac_exception_cb *event_cb;
+ /* Pointer to driver's global address hash table */
+ struct eth_hash_t *multicast_addr_hash;
+ /* Pointer to driver's individual address hash table */
+ struct eth_hash_t *unicast_addr_hash;
+ u8 mac_id;
+ u32 exceptions;
+ struct memac_cfg *memac_drv_param;
+ void *fm;
+ struct fman_rev_info fm_rev_info;
+ bool basex_if;
+ struct phy_device *pcsphy;
+};
+
+static void add_addr_in_paddr(struct memac_regs __iomem *regs, u8 *adr,
+ u8 paddr_num)
+{
+ u32 tmp0, tmp1;
+
+ tmp0 = (u32)(adr[0] | adr[1] << 8 | adr[2] << 16 | adr[3] << 24);
+ tmp1 = (u32)(adr[4] | adr[5] << 8);
+
+ if (paddr_num == 0) {
+ iowrite32be(tmp0, &regs->mac_addr0.mac_addr_l);
+ iowrite32be(tmp1, &regs->mac_addr0.mac_addr_u);
+ } else {
+ iowrite32be(tmp0, &regs->mac_addr[paddr_num - 1].mac_addr_l);
+ iowrite32be(tmp1, &regs->mac_addr[paddr_num - 1].mac_addr_u);
+ }
+}
+
+static int reset(struct memac_regs __iomem *regs)
+{
+ u32 tmp;
+ int count;
+
+ tmp = ioread32be(&regs->command_config);
+
+ tmp |= CMD_CFG_SW_RESET;
+
+ iowrite32be(tmp, &regs->command_config);
+
+ count = 100;
+ do {
+ udelay(1);
+ } while ((ioread32be(&regs->command_config) & CMD_CFG_SW_RESET) &&
+ --count);
+
+ if (count == 0)
+ return -EBUSY;
+
+ return 0;
+}
+
+static void set_exception(struct memac_regs __iomem *regs, u32 val,
+ bool enable)
+{
+ u32 tmp;
+
+ tmp = ioread32be(&regs->imask);
+ if (enable)
+ tmp |= val;
+ else
+ tmp &= ~val;
+
+ iowrite32be(tmp, &regs->imask);
+}
+
+static int init(struct memac_regs __iomem *regs, struct memac_cfg *cfg,
+ phy_interface_t phy_if, u16 speed, bool slow_10g_if,
+ u32 exceptions)
+{
+ u32 tmp;
+
+ /* Config */
+ tmp = 0;
+ if (cfg->promiscuous_mode_enable)
+ tmp |= CMD_CFG_PROMIS_EN;
+ if (cfg->pause_ignore)
+ tmp |= CMD_CFG_PAUSE_IGNORE;
+
+ /* Payload length check disable */
+ tmp |= CMD_CFG_NO_LEN_CHK;
+ /* Enable padding of frames in transmit direction */
+ tmp |= CMD_CFG_TX_PAD_EN;
+
+ tmp |= CMD_CFG_CRC_FWD;
+
+ iowrite32be(tmp, &regs->command_config);
+
+ /* Max Frame Length */
+ iowrite32be((u32)cfg->max_frame_length, &regs->maxfrm);
+
+ /* Pause Time */
+ iowrite32be((u32)cfg->pause_quanta, &regs->pause_quanta[0]);
+ iowrite32be((u32)0, &regs->pause_thresh[0]);
+
+ /* IF_MODE */
+ tmp = 0;
+ switch (phy_if) {
+ case PHY_INTERFACE_MODE_XGMII:
+ tmp |= IF_MODE_XGMII;
+ break;
+ default:
+ tmp |= IF_MODE_GMII;
+ if (phy_if == PHY_INTERFACE_MODE_RGMII)
+ tmp |= IF_MODE_RGMII | IF_MODE_RGMII_AUTO;
+ }
+ iowrite32be(tmp, &regs->if_mode);
+
+ /* TX_FIFO_SECTIONS */
+ tmp = 0;
+ if (phy_if == PHY_INTERFACE_MODE_XGMII) {
+ if (slow_10g_if) {
+ tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_SLOW_10G |
+ TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G);
+ } else {
+ tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_10G |
+ TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_10G);
+ }
+ } else {
+ tmp |= (TX_FIFO_SECTIONS_TX_AVAIL_1G |
+ TX_FIFO_SECTIONS_TX_EMPTY_DEFAULT_1G);
+ }
+ iowrite32be(tmp, &regs->tx_fifo_sections);
+
+ /* clear all pending events and set-up interrupts */
+ iowrite32be(0xffffffff, &regs->ievent);
+ set_exception(regs, exceptions, true);
+
+ return 0;
+}
+
+static void set_dflts(struct memac_cfg *cfg)
+{
+ cfg->reset_on_init = false;
+ cfg->promiscuous_mode_enable = false;
+ cfg->pause_ignore = false;
+ cfg->tx_ipg_length = DEFAULT_TX_IPG_LENGTH;
+ cfg->max_frame_length = DEFAULT_FRAME_LENGTH;
+ cfg->pause_quanta = DEFAULT_PAUSE_QUANTA;
+}
+
+static u32 get_mac_addr_hash_code(u64 eth_addr)
+{
+ u64 mask1, mask2;
+ u32 xor_val = 0;
+ u8 i, j;
+
+ for (i = 0; i < 6; i++) {
+ mask1 = eth_addr & (u64)0x01;
+ eth_addr >>= 1;
+
+ for (j = 0; j < 7; j++) {
+ mask2 = eth_addr & (u64)0x01;
+ mask1 ^= mask2;
+ eth_addr >>= 1;
+ }
+
+ xor_val |= (mask1 << (5 - i));
+ }
+
+ return xor_val;
+}
+
+static void setup_sgmii_internal_phy(struct fman_mac *memac,
+ struct fixed_phy_status *fixed_link)
+{
+ u16 tmp_reg16;
+
+ /* SGMII mode */
+ tmp_reg16 = IF_MODE_SGMII_EN;
+ if (!fixed_link)
+ /* AN enable */
+ tmp_reg16 |= IF_MODE_USE_SGMII_AN;
+ else {
+ switch (fixed_link->speed) {
+ case 10:
+ /* For 10M: IF_MODE[SPEED_10M] = 0 */
+ break;
+ case 100:
+ tmp_reg16 |= IF_MODE_SGMII_SPEED_100M;
+ break;
+ case 1000: /* fallthrough */
+ default:
+ tmp_reg16 |= IF_MODE_SGMII_SPEED_1G;
+ break;
+ }
+ if (!fixed_link->duplex)
+ tmp_reg16 |= IF_MODE_SGMII_DUPLEX_HALF;
+ }
+ phy_write(memac->pcsphy, MDIO_SGMII_IF_MODE, tmp_reg16);
+
+ /* Device ability according to SGMII specification */
+ tmp_reg16 = MDIO_SGMII_DEV_ABIL_SGMII_MODE;
+ phy_write(memac->pcsphy, MDIO_SGMII_DEV_ABIL_SGMII, tmp_reg16);
+
+ /* Adjust link timer for SGMII -
+ * According to Cisco SGMII specification the timer should be 1.6 ms.
+ * The link_timer register is configured in units of the clock.
+ * - When running as 1G SGMII, Serdes clock is 125 MHz, so
+ * unit = 1 / (125*10^6 Hz) = 8 ns.
+ * 1.6 ms in units of 8 ns = 1.6ms / 8ns = 2*10^5 = 0x30d40
+ * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so
+ * unit = 1 / (312.5*10^6 Hz) = 3.2 ns.
+ * 1.6 ms in units of 3.2 ns = 1.6ms / 3.2ns = 5*10^5 = 0x7a120.
+ * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII,
+ * we always set up here a value of 2.5 SGMII.
+ */
+ phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_H, LINK_TMR_H);
+ phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_L, LINK_TMR_L);
+
+ if (!fixed_link)
+ /* Restart AN */
+ tmp_reg16 = SGMII_CR_DEF_VAL | SGMII_CR_RESTART_AN;
+ else
+ /* AN disabled */
+ tmp_reg16 = SGMII_CR_DEF_VAL & ~SGMII_CR_AN_EN;
+ phy_write(memac->pcsphy, 0x0, tmp_reg16);
+}
+
+static void setup_sgmii_internal_phy_base_x(struct fman_mac *memac)
+{
+ u16 tmp_reg16;
+
+ /* AN Device capability */
+ tmp_reg16 = MDIO_SGMII_DEV_ABIL_BASEX_MODE;
+ phy_write(memac->pcsphy, MDIO_SGMII_DEV_ABIL_SGMII, tmp_reg16);
+
+ /* Adjust link timer for SGMII -
+ * For Serdes 1000BaseX auto-negotiation the timer should be 10 ms.
+ * The link_timer register is configured in units of the clock.
+ * - When running as 1G SGMII, Serdes clock is 125 MHz, so
+ * unit = 1 / (125*10^6 Hz) = 8 ns.
+ * 10 ms in units of 8 ns = 10ms / 8ns = 1250000 = 0x1312d0
+ * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so
+ * unit = 1 / (312.5*10^6 Hz) = 3.2 ns.
+ * 10 ms in units of 3.2 ns = 10ms / 3.2ns = 3125000 = 0x2faf08.
+ * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII,
+ * we always set up here a value of 2.5 SGMII.
+ */
+ phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_H, LINK_TMR_H_BASEX);
+ phy_write(memac->pcsphy, MDIO_SGMII_LINK_TMR_L, LINK_TMR_L_BASEX);
+
+ /* Restart AN */
+ tmp_reg16 = SGMII_CR_DEF_VAL | SGMII_CR_RESTART_AN;
+ phy_write(memac->pcsphy, 0x0, tmp_reg16);
+}
+
+static int check_init_parameters(struct fman_mac *memac)
+{
+ if (memac->addr == 0) {
+ pr_err("Ethernet MAC must have a valid MAC address\n");
+ return -EINVAL;
+ }
+ if (!memac->exception_cb) {
+ pr_err("Uninitialized exception handler\n");
+ return -EINVAL;
+ }
+ if (!memac->event_cb) {
+ pr_warn("Uninitialize event handler\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int get_exception_flag(enum fman_mac_exceptions exception)
+{
+ u32 bit_mask;
+
+ switch (exception) {
+ case FM_MAC_EX_10G_TX_ECC_ER:
+ bit_mask = MEMAC_IMASK_TECC_ER;
+ break;
+ case FM_MAC_EX_10G_RX_ECC_ER:
+ bit_mask = MEMAC_IMASK_RECC_ER;
+ break;
+ case FM_MAC_EX_TS_FIFO_ECC_ERR:
+ bit_mask = MEMAC_IMASK_TSECC_ER;
+ break;
+ case FM_MAC_EX_MAGIC_PACKET_INDICATION:
+ bit_mask = MEMAC_IMASK_MGI;
+ break;
+ default:
+ bit_mask = 0;
+ break;
+ }
+
+ return bit_mask;
+}
+
+static void memac_err_exception(void *handle)
+{
+ struct fman_mac *memac = (struct fman_mac *)handle;
+ struct memac_regs __iomem *regs = memac->regs;
+ u32 event, imask;
+
+ event = ioread32be(&regs->ievent);
+ imask = ioread32be(&regs->imask);
+
+ /* Imask include both error and notification/event bits.
+ * Leaving only error bits enabled by imask.
+ * The imask error bits are shifted by 16 bits offset from
+ * their corresponding location in the ievent - hence the >> 16
+ */
+ event &= ((imask & MEMAC_ALL_ERRS_IMASK) >> 16);
+
+ iowrite32be(event, &regs->ievent);
+
+ if (event & MEMAC_IEVNT_TS_ECC_ER)
+ memac->exception_cb(memac->dev_id, FM_MAC_EX_TS_FIFO_ECC_ERR);
+ if (event & MEMAC_IEVNT_TX_ECC_ER)
+ memac->exception_cb(memac->dev_id, FM_MAC_EX_10G_TX_ECC_ER);
+ if (event & MEMAC_IEVNT_RX_ECC_ER)
+ memac->exception_cb(memac->dev_id, FM_MAC_EX_10G_RX_ECC_ER);
+}
+
+static void memac_exception(void *handle)
+{
+ struct fman_mac *memac = (struct fman_mac *)handle;
+ struct memac_regs __iomem *regs = memac->regs;
+ u32 event, imask;
+
+ event = ioread32be(&regs->ievent);
+ imask = ioread32be(&regs->imask);
+
+ /* Imask include both error and notification/event bits.
+ * Leaving only error bits enabled by imask.
+ * The imask error bits are shifted by 16 bits offset from
+ * their corresponding location in the ievent - hence the >> 16
+ */
+ event &= ((imask & MEMAC_ALL_ERRS_IMASK) >> 16);
+
+ iowrite32be(event, &regs->ievent);
+
+ if (event & MEMAC_IEVNT_MGI)
+ memac->exception_cb(memac->dev_id,
+ FM_MAC_EX_MAGIC_PACKET_INDICATION);
+}
+
+static void free_init_resources(struct fman_mac *memac)
+{
+ fman_unregister_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id,
+ FMAN_INTR_TYPE_ERR);
+
+ fman_unregister_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id,
+ FMAN_INTR_TYPE_NORMAL);
+
+ /* release the driver's group hash table */
+ free_hash_table(memac->multicast_addr_hash);
+ memac->multicast_addr_hash = NULL;
+
+ /* release the driver's individual hash table */
+ free_hash_table(memac->unicast_addr_hash);
+ memac->unicast_addr_hash = NULL;
+}
+
+static bool is_init_done(struct memac_cfg *memac_drv_params)
+{
+ /* Checks if mEMAC driver parameters were initialized */
+ if (!memac_drv_params)
+ return true;
+
+ return false;
+}
+
+int memac_enable(struct fman_mac *memac, enum comm_mode mode)
+{
+ struct memac_regs __iomem *regs = memac->regs;
+ u32 tmp;
+
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->command_config);
+ if (mode & COMM_MODE_RX)
+ tmp |= CMD_CFG_RX_EN;
+ if (mode & COMM_MODE_TX)
+ tmp |= CMD_CFG_TX_EN;
+
+ iowrite32be(tmp, &regs->command_config);
+
+ return 0;
+}
+
+int memac_disable(struct fman_mac *memac, enum comm_mode mode)
+{
+ struct memac_regs __iomem *regs = memac->regs;
+ u32 tmp;
+
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->command_config);
+ if (mode & COMM_MODE_RX)
+ tmp &= ~CMD_CFG_RX_EN;
+ if (mode & COMM_MODE_TX)
+ tmp &= ~CMD_CFG_TX_EN;
+
+ iowrite32be(tmp, &regs->command_config);
+
+ return 0;
+}
+
+int memac_set_promiscuous(struct fman_mac *memac, bool new_val)
+{
+ struct memac_regs __iomem *regs = memac->regs;
+ u32 tmp;
+
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->command_config);
+ if (new_val)
+ tmp |= CMD_CFG_PROMIS_EN;
+ else
+ tmp &= ~CMD_CFG_PROMIS_EN;
+
+ iowrite32be(tmp, &regs->command_config);
+
+ return 0;
+}
+
+int memac_adjust_link(struct fman_mac *memac, u16 speed)
+{
+ struct memac_regs __iomem *regs = memac->regs;
+ u32 tmp;
+
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->if_mode);
+
+ /* Set full duplex */
+ tmp &= ~IF_MODE_HD;
+
+ if (memac->phy_if == PHY_INTERFACE_MODE_RGMII) {
+ /* Configure RGMII in manual mode */
+ tmp &= ~IF_MODE_RGMII_AUTO;
+ tmp &= ~IF_MODE_RGMII_SP_MASK;
+ /* Full duplex */
+ tmp |= IF_MODE_RGMII_FD;
+
+ switch (speed) {
+ case SPEED_1000:
+ tmp |= IF_MODE_RGMII_1000;
+ break;
+ case SPEED_100:
+ tmp |= IF_MODE_RGMII_100;
+ break;
+ case SPEED_10:
+ tmp |= IF_MODE_RGMII_10;
+ break;
+ default:
+ break;
+ }
+ }
+
+ iowrite32be(tmp, &regs->if_mode);
+
+ return 0;
+}
+
+int memac_cfg_max_frame_len(struct fman_mac *memac, u16 new_val)
+{
+ if (is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ memac->memac_drv_param->max_frame_length = new_val;
+
+ return 0;
+}
+
+int memac_cfg_reset_on_init(struct fman_mac *memac, bool enable)
+{
+ if (is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ memac->memac_drv_param->reset_on_init = enable;
+
+ return 0;
+}
+
+int memac_cfg_fixed_link(struct fman_mac *memac,
+ struct fixed_phy_status *fixed_link)
+{
+ if (is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ memac->memac_drv_param->fixed_link = fixed_link;
+
+ return 0;
+}
+
+int memac_set_tx_pause_frames(struct fman_mac *memac, u8 priority,
+ u16 pause_time, u16 thresh_time)
+{
+ struct memac_regs __iomem *regs = memac->regs;
+ u32 tmp;
+
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->tx_fifo_sections);
+
+ GET_TX_EMPTY_DEFAULT_VALUE(tmp);
+ iowrite32be(tmp, &regs->tx_fifo_sections);
+
+ tmp = ioread32be(&regs->command_config);
+ tmp &= ~CMD_CFG_PFC_MODE;
+ priority = 0;
+
+ iowrite32be(tmp, &regs->command_config);
+
+ tmp = ioread32be(&regs->pause_quanta[priority / 2]);
+ if (priority % 2)
+ tmp &= CLXY_PAUSE_QUANTA_CLX_PQNT;
+ else
+ tmp &= CLXY_PAUSE_QUANTA_CLY_PQNT;
+ tmp |= ((u32)pause_time << (16 * (priority % 2)));
+ iowrite32be(tmp, &regs->pause_quanta[priority / 2]);
+
+ tmp = ioread32be(&regs->pause_thresh[priority / 2]);
+ if (priority % 2)
+ tmp &= CLXY_PAUSE_THRESH_CLX_QTH;
+ else
+ tmp &= CLXY_PAUSE_THRESH_CLY_QTH;
+ tmp |= ((u32)thresh_time << (16 * (priority % 2)));
+ iowrite32be(tmp, &regs->pause_thresh[priority / 2]);
+
+ return 0;
+}
+
+int memac_accept_rx_pause_frames(struct fman_mac *memac, bool en)
+{
+ struct memac_regs __iomem *regs = memac->regs;
+ u32 tmp;
+
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->command_config);
+ if (en)
+ tmp &= ~CMD_CFG_PAUSE_IGNORE;
+ else
+ tmp |= CMD_CFG_PAUSE_IGNORE;
+
+ iowrite32be(tmp, &regs->command_config);
+
+ return 0;
+}
+
+int memac_modify_mac_address(struct fman_mac *memac, enet_addr_t *enet_addr)
+{
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ add_addr_in_paddr(memac->regs, (u8 *)(*enet_addr), 0);
+
+ return 0;
+}
+
+int memac_add_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr)
+{
+ struct memac_regs __iomem *regs = memac->regs;
+ struct eth_hash_entry *hash_entry;
+ u32 hash;
+ u64 addr;
+
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ addr = ENET_ADDR_TO_UINT64(*eth_addr);
+
+ if (!(addr & GROUP_ADDRESS)) {
+ /* Unicast addresses not supported in hash */
+ pr_err("Unicast Address\n");
+ return -EINVAL;
+ }
+ hash = get_mac_addr_hash_code(addr) & HASH_CTRL_ADDR_MASK;
+
+ /* Create element to be added to the driver hash table */
+ hash_entry = kmalloc(sizeof(*hash_entry), GFP_KERNEL);
+ if (!hash_entry)
+ return -ENOMEM;
+ hash_entry->addr = addr;
+ INIT_LIST_HEAD(&hash_entry->node);
+
+ list_add_tail(&hash_entry->node,
+ &memac->multicast_addr_hash->lsts[hash]);
+ iowrite32be(hash | HASH_CTRL_MCAST_EN, &regs->hashtable_ctrl);
+
+ return 0;
+}
+
+int memac_del_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr)
+{
+ struct memac_regs __iomem *regs = memac->regs;
+ struct eth_hash_entry *hash_entry = NULL;
+ struct list_head *pos;
+ u32 hash;
+ u64 addr;
+
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ addr = ENET_ADDR_TO_UINT64(*eth_addr);
+
+ hash = get_mac_addr_hash_code(addr) & HASH_CTRL_ADDR_MASK;
+
+ list_for_each(pos, &memac->multicast_addr_hash->lsts[hash]) {
+ hash_entry = ETH_HASH_ENTRY_OBJ(pos);
+ if (hash_entry->addr == addr) {
+ list_del_init(&hash_entry->node);
+ kfree(hash_entry);
+ break;
+ }
+ }
+ if (list_empty(&memac->multicast_addr_hash->lsts[hash]))
+ iowrite32be(hash & ~HASH_CTRL_MCAST_EN, &regs->hashtable_ctrl);
+
+ return 0;
+}
+
+int memac_set_exception(struct fman_mac *memac,
+ enum fman_mac_exceptions exception, bool enable)
+{
+ u32 bit_mask = 0;
+
+ if (!is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ bit_mask = get_exception_flag(exception);
+ if (bit_mask) {
+ if (enable)
+ memac->exceptions |= bit_mask;
+ else
+ memac->exceptions &= ~bit_mask;
+ } else {
+ pr_err("Undefined exception\n");
+ return -EINVAL;
+ }
+ set_exception(memac->regs, bit_mask, enable);
+
+ return 0;
+}
+
+int memac_init(struct fman_mac *memac)
+{
+ struct memac_cfg *memac_drv_param;
+ u8 i;
+ enet_addr_t eth_addr;
+ bool slow_10g_if = false;
+ struct fixed_phy_status *fixed_link;
+ int err;
+ u32 reg32 = 0;
+
+ if (is_init_done(memac->memac_drv_param))
+ return -EINVAL;
+
+ err = check_init_parameters(memac);
+ if (err)
+ return err;
+
+ memac_drv_param = memac->memac_drv_param;
+
+ if (memac->fm_rev_info.major == 6 && memac->fm_rev_info.minor == 4)
+ slow_10g_if = true;
+
+ /* First, reset the MAC if desired. */
+ if (memac_drv_param->reset_on_init) {
+ err = reset(memac->regs);
+ if (err) {
+ pr_err("mEMAC reset failed\n");
+ return err;
+ }
+ }
+
+ /* MAC Address */
+ MAKE_ENET_ADDR_FROM_UINT64(memac->addr, eth_addr);
+ add_addr_in_paddr(memac->regs, (u8 *)eth_addr, 0);
+
+ fixed_link = memac_drv_param->fixed_link;
+
+ init(memac->regs, memac->memac_drv_param, memac->phy_if,
+ memac->max_speed, slow_10g_if, memac->exceptions);
+
+ /* FM_RX_FIFO_CORRUPT_ERRATA_10GMAC_A006320 errata workaround
+ * Exists only in FMan 6.0 and 6.3.
+ */
+ if ((memac->fm_rev_info.major == 6) &&
+ ((memac->fm_rev_info.minor == 0) ||
+ (memac->fm_rev_info.minor == 3))) {
+ /* MAC strips CRC from received frames - this workaround
+ * should decrease the likelihood of bug appearance
+ */
+ reg32 = ioread32be(&memac->regs->command_config);
+ reg32 &= ~CMD_CFG_CRC_FWD;
+ iowrite32be(reg32, &memac->regs->command_config);
+ }
+
+ if (memac->phy_if == PHY_INTERFACE_MODE_SGMII) {
+ /* Configure internal SGMII PHY */
+ if (memac->basex_if)
+ setup_sgmii_internal_phy_base_x(memac);
+ else
+ setup_sgmii_internal_phy(memac, fixed_link);
+ } else if (memac->phy_if == PHY_INTERFACE_MODE_QSGMII) {
+ /* Configure 4 internal SGMII PHYs */
+ for (i = 0; i < 4; i++) {
+ u8 qsmgii_phy_addr, phy_addr;
+ /* QSGMII PHY address occupies 3 upper bits of 5-bit
+ * phy_address; the lower 2 bits are used to extend
+ * register address space and access each one of 4
+ * ports inside QSGMII.
+ */
+ phy_addr = memac->pcsphy->mdio.addr;
+ qsmgii_phy_addr = (u8)((phy_addr << 2) | i);
+ memac->pcsphy->mdio.addr = qsmgii_phy_addr;
+ if (memac->basex_if)
+ setup_sgmii_internal_phy_base_x(memac);
+ else
+ setup_sgmii_internal_phy(memac, fixed_link);
+
+ memac->pcsphy->mdio.addr = phy_addr;
+ }
+ }
+
+ /* Max Frame Length */
+ err = fman_set_mac_max_frame(memac->fm, memac->mac_id,
+ memac_drv_param->max_frame_length);
+ if (err) {
+ pr_err("settings Mac max frame length is FAILED\n");
+ return err;
+ }
+
+ memac->multicast_addr_hash = alloc_hash_table(HASH_TABLE_SIZE);
+ if (!memac->multicast_addr_hash) {
+ free_init_resources(memac);
+ pr_err("allocation hash table is FAILED\n");
+ return -ENOMEM;
+ }
+
+ memac->unicast_addr_hash = alloc_hash_table(HASH_TABLE_SIZE);
+ if (!memac->unicast_addr_hash) {
+ free_init_resources(memac);
+ pr_err("allocation hash table is FAILED\n");
+ return -ENOMEM;
+ }
+
+ fman_register_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id,
+ FMAN_INTR_TYPE_ERR, memac_err_exception, memac);
+
+ fman_register_intr(memac->fm, FMAN_MOD_MAC, memac->mac_id,
+ FMAN_INTR_TYPE_NORMAL, memac_exception, memac);
+
+ kfree(memac_drv_param);
+ memac->memac_drv_param = NULL;
+
+ return 0;
+}
+
+int memac_free(struct fman_mac *memac)
+{
+ free_init_resources(memac);
+
+ kfree(memac->memac_drv_param);
+ kfree(memac);
+
+ return 0;
+}
+
+struct fman_mac *memac_config(struct fman_mac_params *params)
+{
+ struct fman_mac *memac;
+ struct memac_cfg *memac_drv_param;
+ void __iomem *base_addr;
+
+ base_addr = params->base_addr;
+ /* allocate memory for the m_emac data structure */
+ memac = kzalloc(sizeof(*memac), GFP_KERNEL);
+ if (!memac)
+ return NULL;
+
+ /* allocate memory for the m_emac driver parameters data structure */
+ memac_drv_param = kzalloc(sizeof(*memac_drv_param), GFP_KERNEL);
+ if (!memac_drv_param) {
+ memac_free(memac);
+ return NULL;
+ }
+
+ /* Plant parameter structure pointer */
+ memac->memac_drv_param = memac_drv_param;
+
+ set_dflts(memac_drv_param);
+
+ memac->addr = ENET_ADDR_TO_UINT64(params->addr);
+
+ memac->regs = base_addr;
+ memac->max_speed = params->max_speed;
+ memac->phy_if = params->phy_if;
+ memac->mac_id = params->mac_id;
+ memac->exceptions = (MEMAC_IMASK_TSECC_ER | MEMAC_IMASK_TECC_ER |
+ MEMAC_IMASK_RECC_ER | MEMAC_IMASK_MGI);
+ memac->exception_cb = params->exception_cb;
+ memac->event_cb = params->event_cb;
+ memac->dev_id = params->dev_id;
+ memac->fm = params->fm;
+ memac->basex_if = params->basex_if;
+
+ /* Save FMan revision */
+ fman_get_revision(memac->fm, &memac->fm_rev_info);
+
+ if (memac->phy_if == PHY_INTERFACE_MODE_SGMII) {
+ if (!params->internal_phy_node) {
+ pr_err("PCS PHY node is not available\n");
+ memac_free(memac);
+ return NULL;
+ }
+
+ memac->pcsphy = of_phy_find_device(params->internal_phy_node);
+ if (!memac->pcsphy) {
+ pr_err("of_phy_find_device (PCS PHY) failed\n");
+ memac_free(memac);
+ return NULL;
+ }
+ }
+
+ return memac;
+}
diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.h b/drivers/net/ethernet/freescale/fman/fman_memac.h
new file mode 100644
index 000000000000..173d8e0fd716
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_memac.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2008-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MEMAC_H
+#define __MEMAC_H
+
+#include "fman_mac.h"
+
+#include <linux/netdevice.h>
+
+struct fman_mac *memac_config(struct fman_mac_params *params);
+int memac_set_promiscuous(struct fman_mac *memac, bool new_val);
+int memac_modify_mac_address(struct fman_mac *memac, enet_addr_t *enet_addr);
+int memac_adjust_link(struct fman_mac *memac, u16 speed);
+int memac_cfg_max_frame_len(struct fman_mac *memac, u16 new_val);
+int memac_cfg_reset_on_init(struct fman_mac *memac, bool enable);
+int memac_cfg_fixed_link(struct fman_mac *memac,
+ struct fixed_phy_status *fixed_link);
+int memac_enable(struct fman_mac *memac, enum comm_mode mode);
+int memac_disable(struct fman_mac *memac, enum comm_mode mode);
+int memac_init(struct fman_mac *memac);
+int memac_free(struct fman_mac *memac);
+int memac_accept_rx_pause_frames(struct fman_mac *memac, bool en);
+int memac_set_tx_pause_frames(struct fman_mac *memac, u8 priority,
+ u16 pause_time, u16 thresh_time);
+int memac_set_exception(struct fman_mac *memac,
+ enum fman_mac_exceptions exception, bool enable);
+int memac_add_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr);
+int memac_del_hash_mac_address(struct fman_mac *memac, enet_addr_t *eth_addr);
+
+#endif /* __MEMAC_H */
diff --git a/drivers/net/ethernet/freescale/fman/fman_muram.c b/drivers/net/ethernet/freescale/fman/fman_muram.c
new file mode 100644
index 000000000000..4eb0e9ac7182
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_muram.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2008-2015 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 "fman_muram.h"
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/genalloc.h>
+
+struct muram_info {
+ struct gen_pool *pool;
+ void __iomem *vbase;
+ size_t size;
+ phys_addr_t pbase;
+};
+
+static unsigned long fman_muram_vbase_to_offset(struct muram_info *muram,
+ unsigned long vaddr)
+{
+ return vaddr - (unsigned long)muram->vbase;
+}
+
+/**
+ * fman_muram_init
+ * @base: Pointer to base of memory mapped FM-MURAM.
+ * @size: Size of the FM-MURAM partition.
+ *
+ * Creates partition in the MURAM.
+ * The routine returns a pointer to the MURAM partition.
+ * This pointer must be passed as to all other FM-MURAM function calls.
+ * No actual initialization or configuration of FM_MURAM hardware is done by
+ * this routine.
+ *
+ * Return: pointer to FM-MURAM object, or NULL for Failure.
+ */
+struct muram_info *fman_muram_init(phys_addr_t base, size_t size)
+{
+ struct muram_info *muram;
+ void __iomem *vaddr;
+ int ret;
+
+ muram = kzalloc(sizeof(*muram), GFP_KERNEL);
+ if (!muram)
+ return NULL;
+
+ muram->pool = gen_pool_create(ilog2(64), -1);
+ if (!muram->pool) {
+ pr_err("%s(): MURAM pool create failed\n", __func__);
+ goto muram_free;
+ }
+
+ vaddr = ioremap(base, size);
+ if (!vaddr) {
+ pr_err("%s(): MURAM ioremap failed\n", __func__);
+ goto pool_destroy;
+ }
+
+ ret = gen_pool_add_virt(muram->pool, (unsigned long)vaddr,
+ base, size, -1);
+ if (ret < 0) {
+ pr_err("%s(): MURAM pool add failed\n", __func__);
+ iounmap(vaddr);
+ goto pool_destroy;
+ }
+
+ memset_io(vaddr, 0, (int)size);
+
+ muram->vbase = vaddr;
+ muram->pbase = base;
+ return muram;
+
+pool_destroy:
+ gen_pool_destroy(muram->pool);
+muram_free:
+ kfree(muram);
+ return NULL;
+}
+
+/**
+ * fman_muram_offset_to_vbase
+ * @muram: FM-MURAM module pointer.
+ * @offset: the offset of the memory block
+ *
+ * Gives the address of the memory region from specific offset
+ *
+ * Return: The address of the memory block
+ */
+unsigned long fman_muram_offset_to_vbase(struct muram_info *muram,
+ unsigned long offset)
+{
+ return offset + (unsigned long)muram->vbase;
+}
+
+/**
+ * fman_muram_alloc
+ * @muram: FM-MURAM module pointer.
+ * @size: Size of the memory to be allocated.
+ *
+ * Allocate some memory from FM-MURAM partition.
+ *
+ * Return: address of the allocated memory; NULL otherwise.
+ */
+int fman_muram_alloc(struct muram_info *muram, size_t size)
+{
+ unsigned long vaddr;
+
+ vaddr = gen_pool_alloc(muram->pool, size);
+ if (!vaddr)
+ return -ENOMEM;
+
+ memset_io((void __iomem *)vaddr, 0, size);
+
+ return fman_muram_vbase_to_offset(muram, vaddr);
+}
+
+/**
+ * fman_muram_free_mem
+ * muram: FM-MURAM module pointer.
+ * offset: offset of the memory region to be freed.
+ * size: size of the memory to be freed.
+ *
+ * Free an allocated memory from FM-MURAM partition.
+ */
+void fman_muram_free_mem(struct muram_info *muram, u32 offset, size_t size)
+{
+ unsigned long addr = fman_muram_offset_to_vbase(muram, offset);
+
+ gen_pool_free(muram->pool, addr, size);
+}
diff --git a/drivers/net/ethernet/freescale/fman/fman_muram.h b/drivers/net/ethernet/freescale/fman/fman_muram.h
new file mode 100644
index 000000000000..dbf0af9e5bb5
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_muram.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2008-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __FM_MURAM_EXT
+#define __FM_MURAM_EXT
+
+#include <linux/types.h>
+
+#define FM_MURAM_INVALID_ALLOCATION -1
+
+/* Structure for FM MURAM information */
+struct muram_info;
+
+struct muram_info *fman_muram_init(phys_addr_t base, size_t size);
+
+unsigned long fman_muram_offset_to_vbase(struct muram_info *muram,
+ unsigned long offset);
+
+int fman_muram_alloc(struct muram_info *muram, size_t size);
+
+void fman_muram_free_mem(struct muram_info *muram, u32 offset, size_t size);
+
+#endif /* __FM_MURAM_EXT */
diff --git a/drivers/net/ethernet/freescale/fman/fman_port.c b/drivers/net/ethernet/freescale/fman/fman_port.c
new file mode 100644
index 000000000000..70c198d072dc
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_port.c
@@ -0,0 +1,1778 @@
+/*
+ * Copyright 2008 - 2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "fman_port.h"
+#include "fman.h"
+#include "fman_sp.h"
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/delay.h>
+#include <linux/libfdt_env.h>
+
+/* Queue ID */
+#define DFLT_FQ_ID 0x00FFFFFF
+
+/* General defines */
+#define PORT_BMI_FIFO_UNITS 0x100
+
+#define MAX_PORT_FIFO_SIZE(bmi_max_fifo_size) \
+ min((u32)bmi_max_fifo_size, (u32)1024 * FMAN_BMI_FIFO_UNITS)
+
+#define PORT_CG_MAP_NUM 8
+#define PORT_PRS_RESULT_WORDS_NUM 8
+#define PORT_IC_OFFSET_UNITS 0x10
+
+#define MIN_EXT_BUF_SIZE 64
+
+#define BMI_PORT_REGS_OFFSET 0
+#define QMI_PORT_REGS_OFFSET 0x400
+
+/* Default values */
+#define DFLT_PORT_BUFFER_PREFIX_CONTEXT_DATA_ALIGN \
+ DFLT_FM_SP_BUFFER_PREFIX_CONTEXT_DATA_ALIGN
+
+#define DFLT_PORT_CUT_BYTES_FROM_END 4
+
+#define DFLT_PORT_ERRORS_TO_DISCARD FM_PORT_FRM_ERR_CLS_DISCARD
+#define DFLT_PORT_MAX_FRAME_LENGTH 9600
+
+#define DFLT_PORT_RX_FIFO_PRI_ELEVATION_LEV(bmi_max_fifo_size) \
+ MAX_PORT_FIFO_SIZE(bmi_max_fifo_size)
+
+#define DFLT_PORT_RX_FIFO_THRESHOLD(major, bmi_max_fifo_size) \
+ (major == 6 ? \
+ MAX_PORT_FIFO_SIZE(bmi_max_fifo_size) : \
+ (MAX_PORT_FIFO_SIZE(bmi_max_fifo_size) * 3 / 4)) \
+
+#define DFLT_PORT_EXTRA_NUM_OF_FIFO_BUFS 0
+
+/* QMI defines */
+#define QMI_DEQ_CFG_SUBPORTAL_MASK 0x1f
+
+#define QMI_PORT_CFG_EN 0x80000000
+#define QMI_PORT_STATUS_DEQ_FD_BSY 0x20000000
+
+#define QMI_DEQ_CFG_PRI 0x80000000
+#define QMI_DEQ_CFG_TYPE1 0x10000000
+#define QMI_DEQ_CFG_TYPE2 0x20000000
+#define QMI_DEQ_CFG_TYPE3 0x30000000
+#define QMI_DEQ_CFG_PREFETCH_PARTIAL 0x01000000
+#define QMI_DEQ_CFG_PREFETCH_FULL 0x03000000
+#define QMI_DEQ_CFG_SP_MASK 0xf
+#define QMI_DEQ_CFG_SP_SHIFT 20
+
+#define QMI_BYTE_COUNT_LEVEL_CONTROL(_type) \
+ (_type == FMAN_PORT_TYPE_TX ? 0x1400 : 0x400)
+
+/* BMI defins */
+#define BMI_EBD_EN 0x80000000
+
+#define BMI_PORT_CFG_EN 0x80000000
+
+#define BMI_PORT_STATUS_BSY 0x80000000
+
+#define BMI_DMA_ATTR_SWP_SHIFT FMAN_SP_DMA_ATTR_SWP_SHIFT
+#define BMI_DMA_ATTR_WRITE_OPTIMIZE FMAN_SP_DMA_ATTR_WRITE_OPTIMIZE
+
+#define BMI_RX_FIFO_PRI_ELEVATION_SHIFT 16
+#define BMI_RX_FIFO_THRESHOLD_ETHE 0x80000000
+
+#define BMI_FRAME_END_CS_IGNORE_SHIFT 24
+#define BMI_FRAME_END_CS_IGNORE_MASK 0x0000001f
+
+#define BMI_RX_FRAME_END_CUT_SHIFT 16
+#define BMI_RX_FRAME_END_CUT_MASK 0x0000001f
+
+#define BMI_IC_TO_EXT_SHIFT FMAN_SP_IC_TO_EXT_SHIFT
+#define BMI_IC_TO_EXT_MASK 0x0000001f
+#define BMI_IC_FROM_INT_SHIFT FMAN_SP_IC_FROM_INT_SHIFT
+#define BMI_IC_FROM_INT_MASK 0x0000000f
+#define BMI_IC_SIZE_MASK 0x0000001f
+
+#define BMI_INT_BUF_MARG_SHIFT 28
+#define BMI_INT_BUF_MARG_MASK 0x0000000f
+#define BMI_EXT_BUF_MARG_START_SHIFT FMAN_SP_EXT_BUF_MARG_START_SHIFT
+#define BMI_EXT_BUF_MARG_START_MASK 0x000001ff
+#define BMI_EXT_BUF_MARG_END_MASK 0x000001ff
+
+#define BMI_CMD_MR_LEAC 0x00200000
+#define BMI_CMD_MR_SLEAC 0x00100000
+#define BMI_CMD_MR_MA 0x00080000
+#define BMI_CMD_MR_DEAS 0x00040000
+#define BMI_CMD_RX_MR_DEF (BMI_CMD_MR_LEAC | \
+ BMI_CMD_MR_SLEAC | \
+ BMI_CMD_MR_MA | \
+ BMI_CMD_MR_DEAS)
+#define BMI_CMD_TX_MR_DEF 0
+
+#define BMI_CMD_ATTR_ORDER 0x80000000
+#define BMI_CMD_ATTR_SYNC 0x02000000
+#define BMI_CMD_ATTR_COLOR_SHIFT 26
+
+#define BMI_FIFO_PIPELINE_DEPTH_SHIFT 12
+#define BMI_FIFO_PIPELINE_DEPTH_MASK 0x0000000f
+#define BMI_NEXT_ENG_FD_BITS_SHIFT 24
+
+#define BMI_EXT_BUF_POOL_VALID FMAN_SP_EXT_BUF_POOL_VALID
+#define BMI_EXT_BUF_POOL_EN_COUNTER FMAN_SP_EXT_BUF_POOL_EN_COUNTER
+#define BMI_EXT_BUF_POOL_BACKUP FMAN_SP_EXT_BUF_POOL_BACKUP
+#define BMI_EXT_BUF_POOL_ID_SHIFT 16
+#define BMI_EXT_BUF_POOL_ID_MASK 0x003F0000
+#define BMI_POOL_DEP_NUM_OF_POOLS_SHIFT 16
+
+#define BMI_TX_FIFO_MIN_FILL_SHIFT 16
+
+#define BMI_PRIORITY_ELEVATION_LEVEL ((0x3FF + 1) * PORT_BMI_FIFO_UNITS)
+#define BMI_FIFO_THRESHOLD ((0x3FF + 1) * PORT_BMI_FIFO_UNITS)
+
+#define BMI_DEQUEUE_PIPELINE_DEPTH(_type, _speed) \
+ ((_type == FMAN_PORT_TYPE_TX && _speed == 10000) ? 4 : 1)
+
+#define RX_ERRS_TO_ENQ \
+ (FM_PORT_FRM_ERR_DMA | \
+ FM_PORT_FRM_ERR_PHYSICAL | \
+ FM_PORT_FRM_ERR_SIZE | \
+ FM_PORT_FRM_ERR_EXTRACTION | \
+ FM_PORT_FRM_ERR_NO_SCHEME | \
+ FM_PORT_FRM_ERR_PRS_TIMEOUT | \
+ FM_PORT_FRM_ERR_PRS_ILL_INSTRUCT | \
+ FM_PORT_FRM_ERR_BLOCK_LIMIT_EXCEEDED | \
+ FM_PORT_FRM_ERR_PRS_HDR_ERR | \
+ FM_PORT_FRM_ERR_KEYSIZE_OVERFLOW | \
+ FM_PORT_FRM_ERR_IPRE)
+
+/* NIA defines */
+#define NIA_ORDER_RESTOR 0x00800000
+#define NIA_ENG_BMI 0x00500000
+#define NIA_ENG_QMI_ENQ 0x00540000
+#define NIA_ENG_QMI_DEQ 0x00580000
+
+#define NIA_BMI_AC_ENQ_FRAME 0x00000002
+#define NIA_BMI_AC_TX_RELEASE 0x000002C0
+#define NIA_BMI_AC_RELEASE 0x000000C0
+#define NIA_BMI_AC_TX 0x00000274
+#define NIA_BMI_AC_FETCH_ALL_FRAME 0x0000020c
+
+/* Port IDs */
+#define TX_10G_PORT_BASE 0x30
+#define RX_10G_PORT_BASE 0x10
+
+/* BMI Rx port register map */
+struct fman_port_rx_bmi_regs {
+ u32 fmbm_rcfg; /* Rx Configuration */
+ u32 fmbm_rst; /* Rx Status */
+ u32 fmbm_rda; /* Rx DMA attributes */
+ u32 fmbm_rfp; /* Rx FIFO Parameters */
+ u32 fmbm_rfed; /* Rx Frame End Data */
+ u32 fmbm_ricp; /* Rx Internal Context Parameters */
+ u32 fmbm_rim; /* Rx Internal Buffer Margins */
+ u32 fmbm_rebm; /* Rx External Buffer Margins */
+ u32 fmbm_rfne; /* Rx Frame Next Engine */
+ u32 fmbm_rfca; /* Rx Frame Command Attributes. */
+ u32 fmbm_rfpne; /* Rx Frame Parser Next Engine */
+ u32 fmbm_rpso; /* Rx Parse Start Offset */
+ u32 fmbm_rpp; /* Rx Policer Profile */
+ u32 fmbm_rccb; /* Rx Coarse Classification Base */
+ u32 fmbm_reth; /* Rx Excessive Threshold */
+ u32 reserved003c[1]; /* (0x03C 0x03F) */
+ u32 fmbm_rprai[PORT_PRS_RESULT_WORDS_NUM];
+ /* Rx Parse Results Array Init */
+ u32 fmbm_rfqid; /* Rx Frame Queue ID */
+ u32 fmbm_refqid; /* Rx Error Frame Queue ID */
+ u32 fmbm_rfsdm; /* Rx Frame Status Discard Mask */
+ u32 fmbm_rfsem; /* Rx Frame Status Error Mask */
+ u32 fmbm_rfene; /* Rx Frame Enqueue Next Engine */
+ u32 reserved0074[0x2]; /* (0x074-0x07C) */
+ u32 fmbm_rcmne; /* Rx Frame Continuous Mode Next Engine */
+ u32 reserved0080[0x20]; /* (0x080 0x0FF) */
+ u32 fmbm_ebmpi[FMAN_PORT_MAX_EXT_POOLS_NUM];
+ /* Buffer Manager pool Information- */
+ u32 fmbm_acnt[FMAN_PORT_MAX_EXT_POOLS_NUM]; /* Allocate Counter- */
+ u32 reserved0130[8]; /* 0x130/0x140 - 0x15F reserved - */
+ u32 fmbm_rcgm[PORT_CG_MAP_NUM]; /* Congestion Group Map */
+ u32 fmbm_mpd; /* BM Pool Depletion */
+ u32 reserved0184[0x1F]; /* (0x184 0x1FF) */
+ u32 fmbm_rstc; /* Rx Statistics Counters */
+ u32 fmbm_rfrc; /* Rx Frame Counter */
+ u32 fmbm_rfbc; /* Rx Bad Frames Counter */
+ u32 fmbm_rlfc; /* Rx Large Frames Counter */
+ u32 fmbm_rffc; /* Rx Filter Frames Counter */
+ u32 fmbm_rfdc; /* Rx Frame Discard Counter */
+ u32 fmbm_rfldec; /* Rx Frames List DMA Error Counter */
+ u32 fmbm_rodc; /* Rx Out of Buffers Discard nntr */
+ u32 fmbm_rbdc; /* Rx Buffers Deallocate Counter */
+ u32 fmbm_rpec; /* RX Prepare to enqueue Counte */
+ u32 reserved0224[0x16]; /* (0x224 0x27F) */
+ u32 fmbm_rpc; /* Rx Performance Counters */
+ u32 fmbm_rpcp; /* Rx Performance Count Parameters */
+ u32 fmbm_rccn; /* Rx Cycle Counter */
+ u32 fmbm_rtuc; /* Rx Tasks Utilization Counter */
+ u32 fmbm_rrquc; /* Rx Receive Queue Utilization cntr */
+ u32 fmbm_rduc; /* Rx DMA Utilization Counter */
+ u32 fmbm_rfuc; /* Rx FIFO Utilization Counter */
+ u32 fmbm_rpac; /* Rx Pause Activation Counter */
+ u32 reserved02a0[0x18]; /* (0x2A0 0x2FF) */
+ u32 fmbm_rdcfg[0x3]; /* Rx Debug Configuration */
+ u32 fmbm_rgpr; /* Rx General Purpose Register */
+ u32 reserved0310[0x3a];
+};
+
+/* BMI Tx port register map */
+struct fman_port_tx_bmi_regs {
+ u32 fmbm_tcfg; /* Tx Configuration */
+ u32 fmbm_tst; /* Tx Status */
+ u32 fmbm_tda; /* Tx DMA attributes */
+ u32 fmbm_tfp; /* Tx FIFO Parameters */
+ u32 fmbm_tfed; /* Tx Frame End Data */
+ u32 fmbm_ticp; /* Tx Internal Context Parameters */
+ u32 fmbm_tfdne; /* Tx Frame Dequeue Next Engine. */
+ u32 fmbm_tfca; /* Tx Frame Command attribute. */
+ u32 fmbm_tcfqid; /* Tx Confirmation Frame Queue ID. */
+ u32 fmbm_tefqid; /* Tx Frame Error Queue ID */
+ u32 fmbm_tfene; /* Tx Frame Enqueue Next Engine */
+ u32 fmbm_trlmts; /* Tx Rate Limiter Scale */
+ u32 fmbm_trlmt; /* Tx Rate Limiter */
+ u32 reserved0034[0x0e]; /* (0x034-0x6c) */
+ u32 fmbm_tccb; /* Tx Coarse Classification base */
+ u32 fmbm_tfne; /* Tx Frame Next Engine */
+ u32 fmbm_tpfcm[0x02];
+ /* Tx Priority based Flow Control (PFC) Mapping */
+ u32 fmbm_tcmne; /* Tx Frame Continuous Mode Next Engine */
+ u32 reserved0080[0x60]; /* (0x080-0x200) */
+ u32 fmbm_tstc; /* Tx Statistics Counters */
+ u32 fmbm_tfrc; /* Tx Frame Counter */
+ u32 fmbm_tfdc; /* Tx Frames Discard Counter */
+ u32 fmbm_tfledc; /* Tx Frame len error discard cntr */
+ u32 fmbm_tfufdc; /* Tx Frame unsprt frmt discard cntr */
+ u32 fmbm_tbdc; /* Tx Buffers Deallocate Counter */
+ u32 reserved0218[0x1A]; /* (0x218-0x280) */
+ u32 fmbm_tpc; /* Tx Performance Counters */
+ u32 fmbm_tpcp; /* Tx Performance Count Parameters */
+ u32 fmbm_tccn; /* Tx Cycle Counter */
+ u32 fmbm_ttuc; /* Tx Tasks Utilization Counter */
+ u32 fmbm_ttcquc; /* Tx Transmit conf Q util Counter */
+ u32 fmbm_tduc; /* Tx DMA Utilization Counter */
+ u32 fmbm_tfuc; /* Tx FIFO Utilization Counter */
+ u32 reserved029c[16]; /* (0x29C-0x2FF) */
+ u32 fmbm_tdcfg[0x3]; /* Tx Debug Configuration */
+ u32 fmbm_tgpr; /* Tx General Purpose Register */
+ u32 reserved0310[0x3a]; /* (0x310-0x3FF) */
+};
+
+/* BMI port register map */
+union fman_port_bmi_regs {
+ struct fman_port_rx_bmi_regs rx;
+ struct fman_port_tx_bmi_regs tx;
+};
+
+/* QMI port register map */
+struct fman_port_qmi_regs {
+ u32 fmqm_pnc; /* PortID n Configuration Register */
+ u32 fmqm_pns; /* PortID n Status Register */
+ u32 fmqm_pnts; /* PortID n Task Status Register */
+ u32 reserved00c[4]; /* 0xn00C - 0xn01B */
+ u32 fmqm_pnen; /* PortID n Enqueue NIA Register */
+ u32 fmqm_pnetfc; /* PortID n Enq Total Frame Counter */
+ u32 reserved024[2]; /* 0xn024 - 0x02B */
+ u32 fmqm_pndn; /* PortID n Dequeue NIA Register */
+ u32 fmqm_pndc; /* PortID n Dequeue Config Register */
+ u32 fmqm_pndtfc; /* PortID n Dequeue tot Frame cntr */
+ u32 fmqm_pndfdc; /* PortID n Dequeue FQID Dflt Cntr */
+ u32 fmqm_pndcc; /* PortID n Dequeue Confirm Counter */
+};
+
+/* QMI dequeue prefetch modes */
+enum fman_port_deq_prefetch {
+ FMAN_PORT_DEQ_NO_PREFETCH, /* No prefetch mode */
+ FMAN_PORT_DEQ_PART_PREFETCH, /* Partial prefetch mode */
+ FMAN_PORT_DEQ_FULL_PREFETCH /* Full prefetch mode */
+};
+
+/* A structure for defining FM port resources */
+struct fman_port_rsrc {
+ u32 num; /* Committed required resource */
+ u32 extra; /* Extra (not committed) required resource */
+};
+
+enum fman_port_dma_swap {
+ FMAN_PORT_DMA_NO_SWAP, /* No swap, transfer data as is */
+ FMAN_PORT_DMA_SWAP_LE,
+ /* The transferred data should be swapped in PPC Little Endian mode */
+ FMAN_PORT_DMA_SWAP_BE
+ /* The transferred data should be swapped in Big Endian mode */
+};
+
+/* Default port color */
+enum fman_port_color {
+ FMAN_PORT_COLOR_GREEN, /* Default port color is green */
+ FMAN_PORT_COLOR_YELLOW, /* Default port color is yellow */
+ FMAN_PORT_COLOR_RED, /* Default port color is red */
+ FMAN_PORT_COLOR_OVERRIDE /* Ignore color */
+};
+
+/* QMI dequeue from the SP channel - types */
+enum fman_port_deq_type {
+ FMAN_PORT_DEQ_BY_PRI,
+ /* Priority precedence and Intra-Class scheduling */
+ FMAN_PORT_DEQ_ACTIVE_FQ,
+ /* Active FQ precedence and Intra-Class scheduling */
+ FMAN_PORT_DEQ_ACTIVE_FQ_NO_ICS
+ /* Active FQ precedence and override Intra-Class scheduling */
+};
+
+/* External buffer pools configuration */
+struct fman_port_bpools {
+ u8 count; /* Num of pools to set up */
+ bool counters_enable; /* Enable allocate counters */
+ u8 grp_bp_depleted_num;
+ /* Number of depleted pools - if reached the BMI indicates
+ * the MAC to send a pause frame
+ */
+ struct {
+ u8 bpid; /* BM pool ID */
+ u16 size;
+ /* Pool's size - must be in ascending order */
+ bool is_backup;
+ /* If this is a backup pool */
+ bool grp_bp_depleted;
+ /* Consider this buffer in multiple pools depletion criteria */
+ bool single_bp_depleted;
+ /* Consider this buffer in single pool depletion criteria */
+ } bpool[FMAN_PORT_MAX_EXT_POOLS_NUM];
+};
+
+struct fman_port_cfg {
+ u32 dflt_fqid;
+ u32 err_fqid;
+ u8 deq_sp;
+ bool deq_high_priority;
+ enum fman_port_deq_type deq_type;
+ enum fman_port_deq_prefetch deq_prefetch_option;
+ u16 deq_byte_cnt;
+ u8 cheksum_last_bytes_ignore;
+ u8 rx_cut_end_bytes;
+ struct fman_buf_pool_depletion buf_pool_depletion;
+ struct fman_ext_pools ext_buf_pools;
+ u32 tx_fifo_min_level;
+ u32 tx_fifo_low_comf_level;
+ u32 rx_pri_elevation;
+ u32 rx_fifo_thr;
+ struct fman_sp_buf_margins buf_margins;
+ u32 int_buf_start_margin;
+ struct fman_sp_int_context_data_copy int_context;
+ u32 discard_mask;
+ u32 err_mask;
+ struct fman_buffer_prefix_content buffer_prefix_content;
+ bool dont_release_buf;
+
+ u8 rx_fd_bits;
+ u32 tx_fifo_deq_pipeline_depth;
+ bool errata_A006320;
+ bool excessive_threshold_register;
+ bool fmbm_tfne_has_features;
+
+ enum fman_port_dma_swap dma_swap_data;
+ enum fman_port_color color;
+};
+
+struct fman_port_rx_pools_params {
+ u8 num_of_pools;
+ u16 second_largest_buf_size;
+ u16 largest_buf_size;
+};
+
+struct fman_port_dts_params {
+ void __iomem *base_addr; /* FMan port virtual memory */
+ enum fman_port_type type; /* Port type */
+ u16 speed; /* Port speed */
+ u8 id; /* HW Port Id */
+ u32 qman_channel_id; /* QMan channel id (non RX only) */
+ struct fman *fman; /* FMan Handle */
+};
+
+struct fman_port {
+ void *fm;
+ struct device *dev;
+ struct fman_rev_info rev_info;
+ u8 port_id;
+ enum fman_port_type port_type;
+ u16 port_speed;
+
+ union fman_port_bmi_regs __iomem *bmi_regs;
+ struct fman_port_qmi_regs __iomem *qmi_regs;
+
+ struct fman_sp_buffer_offsets buffer_offsets;
+
+ u8 internal_buf_offset;
+ struct fman_ext_pools ext_buf_pools;
+
+ u16 max_frame_length;
+ struct fman_port_rsrc open_dmas;
+ struct fman_port_rsrc tasks;
+ struct fman_port_rsrc fifo_bufs;
+ struct fman_port_rx_pools_params rx_pools_params;
+
+ struct fman_port_cfg *cfg;
+ struct fman_port_dts_params dts_params;
+
+ u8 ext_pools_num;
+ u32 max_port_fifo_size;
+ u32 max_num_of_ext_pools;
+ u32 max_num_of_sub_portals;
+ u32 bm_max_num_of_pools;
+};
+
+static int init_bmi_rx(struct fman_port *port)
+{
+ struct fman_port_rx_bmi_regs __iomem *regs = &port->bmi_regs->rx;
+ struct fman_port_cfg *cfg = port->cfg;
+ u32 tmp;
+
+ /* DMA attributes */
+ tmp = (u32)cfg->dma_swap_data << BMI_DMA_ATTR_SWP_SHIFT;
+ /* Enable write optimization */
+ tmp |= BMI_DMA_ATTR_WRITE_OPTIMIZE;
+ iowrite32be(tmp, &regs->fmbm_rda);
+
+ /* Rx FIFO parameters */
+ tmp = (cfg->rx_pri_elevation / PORT_BMI_FIFO_UNITS - 1) <<
+ BMI_RX_FIFO_PRI_ELEVATION_SHIFT;
+ tmp |= cfg->rx_fifo_thr / PORT_BMI_FIFO_UNITS - 1;
+ iowrite32be(tmp, &regs->fmbm_rfp);
+
+ if (cfg->excessive_threshold_register)
+ /* always allow access to the extra resources */
+ iowrite32be(BMI_RX_FIFO_THRESHOLD_ETHE, &regs->fmbm_reth);
+
+ /* Frame end data */
+ tmp = (cfg->cheksum_last_bytes_ignore & BMI_FRAME_END_CS_IGNORE_MASK) <<
+ BMI_FRAME_END_CS_IGNORE_SHIFT;
+ tmp |= (cfg->rx_cut_end_bytes & BMI_RX_FRAME_END_CUT_MASK) <<
+ BMI_RX_FRAME_END_CUT_SHIFT;
+ if (cfg->errata_A006320)
+ tmp &= 0xffe0ffff;
+ iowrite32be(tmp, &regs->fmbm_rfed);
+
+ /* Internal context parameters */
+ tmp = ((cfg->int_context.ext_buf_offset / PORT_IC_OFFSET_UNITS) &
+ BMI_IC_TO_EXT_MASK) << BMI_IC_TO_EXT_SHIFT;
+ tmp |= ((cfg->int_context.int_context_offset / PORT_IC_OFFSET_UNITS) &
+ BMI_IC_FROM_INT_MASK) << BMI_IC_FROM_INT_SHIFT;
+ tmp |= (cfg->int_context.size / PORT_IC_OFFSET_UNITS) &
+ BMI_IC_SIZE_MASK;
+ iowrite32be(tmp, &regs->fmbm_ricp);
+
+ /* Internal buffer offset */
+ tmp = ((cfg->int_buf_start_margin / PORT_IC_OFFSET_UNITS) &
+ BMI_INT_BUF_MARG_MASK) << BMI_INT_BUF_MARG_SHIFT;
+ iowrite32be(tmp, &regs->fmbm_rim);
+
+ /* External buffer margins */
+ tmp = (cfg->buf_margins.start_margins & BMI_EXT_BUF_MARG_START_MASK) <<
+ BMI_EXT_BUF_MARG_START_SHIFT;
+ tmp |= cfg->buf_margins.end_margins & BMI_EXT_BUF_MARG_END_MASK;
+ iowrite32be(tmp, &regs->fmbm_rebm);
+
+ /* Frame attributes */
+ tmp = BMI_CMD_RX_MR_DEF;
+ tmp |= BMI_CMD_ATTR_ORDER;
+ tmp |= (u32)cfg->color << BMI_CMD_ATTR_COLOR_SHIFT;
+ /* Synchronization request */
+ tmp |= BMI_CMD_ATTR_SYNC;
+
+ iowrite32be(tmp, &regs->fmbm_rfca);
+
+ /* NIA */
+ tmp = (u32)cfg->rx_fd_bits << BMI_NEXT_ENG_FD_BITS_SHIFT;
+
+ tmp |= NIA_ENG_BMI | NIA_BMI_AC_ENQ_FRAME;
+ iowrite32be(tmp, &regs->fmbm_rfne);
+
+ /* Enqueue NIA */
+ iowrite32be(NIA_ENG_QMI_ENQ | NIA_ORDER_RESTOR, &regs->fmbm_rfene);
+
+ /* Default/error queues */
+ iowrite32be((cfg->dflt_fqid & DFLT_FQ_ID), &regs->fmbm_rfqid);
+ iowrite32be((cfg->err_fqid & DFLT_FQ_ID), &regs->fmbm_refqid);
+
+ /* Discard/error masks */
+ iowrite32be(cfg->discard_mask, &regs->fmbm_rfsdm);
+ iowrite32be(cfg->err_mask, &regs->fmbm_rfsem);
+
+ return 0;
+}
+
+static int init_bmi_tx(struct fman_port *port)
+{
+ struct fman_port_tx_bmi_regs __iomem *regs = &port->bmi_regs->tx;
+ struct fman_port_cfg *cfg = port->cfg;
+ u32 tmp;
+
+ /* Tx Configuration register */
+ tmp = 0;
+ iowrite32be(tmp, &regs->fmbm_tcfg);
+
+ /* DMA attributes */
+ tmp = (u32)cfg->dma_swap_data << BMI_DMA_ATTR_SWP_SHIFT;
+ iowrite32be(tmp, &regs->fmbm_tda);
+
+ /* Tx FIFO parameters */
+ tmp = (cfg->tx_fifo_min_level / PORT_BMI_FIFO_UNITS) <<
+ BMI_TX_FIFO_MIN_FILL_SHIFT;
+ tmp |= ((cfg->tx_fifo_deq_pipeline_depth - 1) &
+ BMI_FIFO_PIPELINE_DEPTH_MASK) << BMI_FIFO_PIPELINE_DEPTH_SHIFT;
+ tmp |= (cfg->tx_fifo_low_comf_level / PORT_BMI_FIFO_UNITS) - 1;
+ iowrite32be(tmp, &regs->fmbm_tfp);
+
+ /* Frame end data */
+ tmp = (cfg->cheksum_last_bytes_ignore & BMI_FRAME_END_CS_IGNORE_MASK) <<
+ BMI_FRAME_END_CS_IGNORE_SHIFT;
+ iowrite32be(tmp, &regs->fmbm_tfed);
+
+ /* Internal context parameters */
+ tmp = ((cfg->int_context.ext_buf_offset / PORT_IC_OFFSET_UNITS) &
+ BMI_IC_TO_EXT_MASK) << BMI_IC_TO_EXT_SHIFT;
+ tmp |= ((cfg->int_context.int_context_offset / PORT_IC_OFFSET_UNITS) &
+ BMI_IC_FROM_INT_MASK) << BMI_IC_FROM_INT_SHIFT;
+ tmp |= (cfg->int_context.size / PORT_IC_OFFSET_UNITS) &
+ BMI_IC_SIZE_MASK;
+ iowrite32be(tmp, &regs->fmbm_ticp);
+
+ /* Frame attributes */
+ tmp = BMI_CMD_TX_MR_DEF;
+ tmp |= BMI_CMD_ATTR_ORDER;
+ tmp |= (u32)cfg->color << BMI_CMD_ATTR_COLOR_SHIFT;
+ iowrite32be(tmp, &regs->fmbm_tfca);
+
+ /* Dequeue NIA + enqueue NIA */
+ iowrite32be(NIA_ENG_QMI_DEQ, &regs->fmbm_tfdne);
+ iowrite32be(NIA_ENG_QMI_ENQ | NIA_ORDER_RESTOR, &regs->fmbm_tfene);
+ if (cfg->fmbm_tfne_has_features)
+ iowrite32be(!cfg->dflt_fqid ?
+ BMI_EBD_EN | NIA_BMI_AC_FETCH_ALL_FRAME :
+ NIA_BMI_AC_FETCH_ALL_FRAME, &regs->fmbm_tfne);
+ if (!cfg->dflt_fqid && cfg->dont_release_buf) {
+ iowrite32be(DFLT_FQ_ID, &regs->fmbm_tcfqid);
+ iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_TX_RELEASE,
+ &regs->fmbm_tfene);
+ if (cfg->fmbm_tfne_has_features)
+ iowrite32be(ioread32be(&regs->fmbm_tfne) & ~BMI_EBD_EN,
+ &regs->fmbm_tfne);
+ }
+
+ /* Confirmation/error queues */
+ if (cfg->dflt_fqid || !cfg->dont_release_buf)
+ iowrite32be(cfg->dflt_fqid & DFLT_FQ_ID, &regs->fmbm_tcfqid);
+ iowrite32be((cfg->err_fqid & DFLT_FQ_ID), &regs->fmbm_tefqid);
+
+ return 0;
+}
+
+static int init_qmi(struct fman_port *port)
+{
+ struct fman_port_qmi_regs __iomem *regs = port->qmi_regs;
+ struct fman_port_cfg *cfg = port->cfg;
+ u32 tmp;
+
+ /* Rx port configuration */
+ if (port->port_type == FMAN_PORT_TYPE_RX) {
+ /* Enqueue NIA */
+ iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_RELEASE, &regs->fmqm_pnen);
+ return 0;
+ }
+
+ /* Continue with Tx port configuration */
+ if (port->port_type == FMAN_PORT_TYPE_TX) {
+ /* Enqueue NIA */
+ iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_TX_RELEASE,
+ &regs->fmqm_pnen);
+ /* Dequeue NIA */
+ iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_TX, &regs->fmqm_pndn);
+ }
+
+ /* Dequeue Configuration register */
+ tmp = 0;
+ if (cfg->deq_high_priority)
+ tmp |= QMI_DEQ_CFG_PRI;
+
+ switch (cfg->deq_type) {
+ case FMAN_PORT_DEQ_BY_PRI:
+ tmp |= QMI_DEQ_CFG_TYPE1;
+ break;
+ case FMAN_PORT_DEQ_ACTIVE_FQ:
+ tmp |= QMI_DEQ_CFG_TYPE2;
+ break;
+ case FMAN_PORT_DEQ_ACTIVE_FQ_NO_ICS:
+ tmp |= QMI_DEQ_CFG_TYPE3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (cfg->deq_prefetch_option) {
+ case FMAN_PORT_DEQ_NO_PREFETCH:
+ break;
+ case FMAN_PORT_DEQ_PART_PREFETCH:
+ tmp |= QMI_DEQ_CFG_PREFETCH_PARTIAL;
+ break;
+ case FMAN_PORT_DEQ_FULL_PREFETCH:
+ tmp |= QMI_DEQ_CFG_PREFETCH_FULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tmp |= (cfg->deq_sp & QMI_DEQ_CFG_SP_MASK) << QMI_DEQ_CFG_SP_SHIFT;
+ tmp |= cfg->deq_byte_cnt;
+ iowrite32be(tmp, &regs->fmqm_pndc);
+
+ return 0;
+}
+
+static int init(struct fman_port *port)
+{
+ int err;
+
+ /* Init BMI registers */
+ switch (port->port_type) {
+ case FMAN_PORT_TYPE_RX:
+ err = init_bmi_rx(port);
+ break;
+ case FMAN_PORT_TYPE_TX:
+ err = init_bmi_tx(port);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (err)
+ return err;
+
+ /* Init QMI registers */
+ err = init_qmi(port);
+ return err;
+
+ return 0;
+}
+
+static int set_bpools(const struct fman_port *port,
+ const struct fman_port_bpools *bp)
+{
+ u32 __iomem *bp_reg, *bp_depl_reg;
+ u32 tmp;
+ u8 i, max_bp_num;
+ bool grp_depl_used = false, rx_port;
+
+ switch (port->port_type) {
+ case FMAN_PORT_TYPE_RX:
+ max_bp_num = port->ext_pools_num;
+ rx_port = true;
+ bp_reg = port->bmi_regs->rx.fmbm_ebmpi;
+ bp_depl_reg = &port->bmi_regs->rx.fmbm_mpd;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rx_port) {
+ /* Check buffers are provided in ascending order */
+ for (i = 0; (i < (bp->count - 1) &&
+ (i < FMAN_PORT_MAX_EXT_POOLS_NUM - 1)); i++) {
+ if (bp->bpool[i].size > bp->bpool[i + 1].size)
+ return -EINVAL;
+ }
+ }
+
+ /* Set up external buffers pools */
+ for (i = 0; i < bp->count; i++) {
+ tmp = BMI_EXT_BUF_POOL_VALID;
+ tmp |= ((u32)bp->bpool[i].bpid <<
+ BMI_EXT_BUF_POOL_ID_SHIFT) & BMI_EXT_BUF_POOL_ID_MASK;
+
+ if (rx_port) {
+ if (bp->counters_enable)
+ tmp |= BMI_EXT_BUF_POOL_EN_COUNTER;
+
+ if (bp->bpool[i].is_backup)
+ tmp |= BMI_EXT_BUF_POOL_BACKUP;
+
+ tmp |= (u32)bp->bpool[i].size;
+ }
+
+ iowrite32be(tmp, &bp_reg[i]);
+ }
+
+ /* Clear unused pools */
+ for (i = bp->count; i < max_bp_num; i++)
+ iowrite32be(0, &bp_reg[i]);
+
+ /* Pools depletion */
+ tmp = 0;
+ for (i = 0; i < FMAN_PORT_MAX_EXT_POOLS_NUM; i++) {
+ if (bp->bpool[i].grp_bp_depleted) {
+ grp_depl_used = true;
+ tmp |= 0x80000000 >> i;
+ }
+
+ if (bp->bpool[i].single_bp_depleted)
+ tmp |= 0x80 >> i;
+ }
+
+ if (grp_depl_used)
+ tmp |= ((u32)bp->grp_bp_depleted_num - 1) <<
+ BMI_POOL_DEP_NUM_OF_POOLS_SHIFT;
+
+ iowrite32be(tmp, bp_depl_reg);
+ return 0;
+}
+
+static bool is_init_done(struct fman_port_cfg *cfg)
+{
+ /* Checks if FMan port driver parameters were initialized */
+ if (!cfg)
+ return true;
+
+ return false;
+}
+
+static int verify_size_of_fifo(struct fman_port *port)
+{
+ u32 min_fifo_size_required = 0, opt_fifo_size_for_b2b = 0;
+
+ /* TX Ports */
+ if (port->port_type == FMAN_PORT_TYPE_TX) {
+ min_fifo_size_required = (u32)
+ (roundup(port->max_frame_length,
+ FMAN_BMI_FIFO_UNITS) + (3 * FMAN_BMI_FIFO_UNITS));
+
+ min_fifo_size_required +=
+ port->cfg->tx_fifo_deq_pipeline_depth *
+ FMAN_BMI_FIFO_UNITS;
+
+ opt_fifo_size_for_b2b = min_fifo_size_required;
+
+ /* Add some margin for back-to-back capability to improve
+ * performance, allows the hardware to pipeline new frame dma
+ * while the previous frame not yet transmitted.
+ */
+ if (port->port_speed == 10000)
+ opt_fifo_size_for_b2b += 3 * FMAN_BMI_FIFO_UNITS;
+ else
+ opt_fifo_size_for_b2b += 2 * FMAN_BMI_FIFO_UNITS;
+ }
+
+ /* RX Ports */
+ else if (port->port_type == FMAN_PORT_TYPE_RX) {
+ if (port->rev_info.major >= 6)
+ min_fifo_size_required = (u32)
+ (roundup(port->max_frame_length,
+ FMAN_BMI_FIFO_UNITS) +
+ (5 * FMAN_BMI_FIFO_UNITS));
+ /* 4 according to spec + 1 for FOF>0 */
+ else
+ min_fifo_size_required = (u32)
+ (roundup(min(port->max_frame_length,
+ port->rx_pools_params.largest_buf_size),
+ FMAN_BMI_FIFO_UNITS) +
+ (7 * FMAN_BMI_FIFO_UNITS));
+
+ opt_fifo_size_for_b2b = min_fifo_size_required;
+
+ /* Add some margin for back-to-back capability to improve
+ * performance,allows the hardware to pipeline new frame dma
+ * while the previous frame not yet transmitted.
+ */
+ if (port->port_speed == 10000)
+ opt_fifo_size_for_b2b += 8 * FMAN_BMI_FIFO_UNITS;
+ else
+ opt_fifo_size_for_b2b += 3 * FMAN_BMI_FIFO_UNITS;
+ }
+
+ WARN_ON(min_fifo_size_required <= 0);
+ WARN_ON(opt_fifo_size_for_b2b < min_fifo_size_required);
+
+ /* Verify the size */
+ if (port->fifo_bufs.num < min_fifo_size_required)
+ dev_dbg(port->dev, "%s: FIFO size should be enlarged to %d bytes\n",
+ __func__, min_fifo_size_required);
+ else if (port->fifo_bufs.num < opt_fifo_size_for_b2b)
+ dev_dbg(port->dev, "%s: For b2b processing,FIFO may be enlarged to %d bytes\n",
+ __func__, opt_fifo_size_for_b2b);
+
+ return 0;
+}
+
+static int set_ext_buffer_pools(struct fman_port *port)
+{
+ struct fman_ext_pools *ext_buf_pools = &port->cfg->ext_buf_pools;
+ struct fman_buf_pool_depletion *buf_pool_depletion =
+ &port->cfg->buf_pool_depletion;
+ u8 ordered_array[FMAN_PORT_MAX_EXT_POOLS_NUM];
+ u16 sizes_array[BM_MAX_NUM_OF_POOLS];
+ int i = 0, j = 0, err;
+ struct fman_port_bpools bpools;
+
+ memset(&ordered_array, 0, sizeof(u8) * FMAN_PORT_MAX_EXT_POOLS_NUM);
+ memset(&sizes_array, 0, sizeof(u16) * BM_MAX_NUM_OF_POOLS);
+ memcpy(&port->ext_buf_pools, ext_buf_pools,
+ sizeof(struct fman_ext_pools));
+
+ fman_sp_set_buf_pools_in_asc_order_of_buf_sizes(ext_buf_pools,
+ ordered_array,
+ sizes_array);
+
+ memset(&bpools, 0, sizeof(struct fman_port_bpools));
+ bpools.count = ext_buf_pools->num_of_pools_used;
+ bpools.counters_enable = true;
+ for (i = 0; i < ext_buf_pools->num_of_pools_used; i++) {
+ bpools.bpool[i].bpid = ordered_array[i];
+ bpools.bpool[i].size = sizes_array[ordered_array[i]];
+ }
+
+ /* save pools parameters for later use */
+ port->rx_pools_params.num_of_pools = ext_buf_pools->num_of_pools_used;
+ port->rx_pools_params.largest_buf_size =
+ sizes_array[ordered_array[ext_buf_pools->num_of_pools_used - 1]];
+ port->rx_pools_params.second_largest_buf_size =
+ sizes_array[ordered_array[ext_buf_pools->num_of_pools_used - 2]];
+
+ /* FMBM_RMPD reg. - pool depletion */
+ if (buf_pool_depletion->pools_grp_mode_enable) {
+ bpools.grp_bp_depleted_num = buf_pool_depletion->num_of_pools;
+ for (i = 0; i < port->bm_max_num_of_pools; i++) {
+ if (buf_pool_depletion->pools_to_consider[i]) {
+ for (j = 0; j < ext_buf_pools->
+ num_of_pools_used; j++) {
+ if (i == ordered_array[j]) {
+ bpools.bpool[j].
+ grp_bp_depleted = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (buf_pool_depletion->single_pool_mode_enable) {
+ for (i = 0; i < port->bm_max_num_of_pools; i++) {
+ if (buf_pool_depletion->
+ pools_to_consider_for_single_mode[i]) {
+ for (j = 0; j < ext_buf_pools->
+ num_of_pools_used; j++) {
+ if (i == ordered_array[j]) {
+ bpools.bpool[j].
+ single_bp_depleted = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ err = set_bpools(port, &bpools);
+ if (err != 0) {
+ dev_err(port->dev, "%s: set_bpools() failed\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int init_low_level_driver(struct fman_port *port)
+{
+ struct fman_port_cfg *cfg = port->cfg;
+ u32 tmp_val;
+
+ switch (port->port_type) {
+ case FMAN_PORT_TYPE_RX:
+ cfg->err_mask = (RX_ERRS_TO_ENQ & ~cfg->discard_mask);
+ break;
+ default:
+ break;
+ }
+
+ tmp_val = (u32)((port->internal_buf_offset % OFFSET_UNITS) ?
+ (port->internal_buf_offset / OFFSET_UNITS + 1) :
+ (port->internal_buf_offset / OFFSET_UNITS));
+ port->internal_buf_offset = (u8)(tmp_val * OFFSET_UNITS);
+ port->cfg->int_buf_start_margin = port->internal_buf_offset;
+
+ if (init(port) != 0) {
+ dev_err(port->dev, "%s: fman port initialization failed\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ /* The code bellow is a trick so the FM will not release the buffer
+ * to BM nor will try to enqueue the frame to QM
+ */
+ if (port->port_type == FMAN_PORT_TYPE_TX) {
+ if (!cfg->dflt_fqid && cfg->dont_release_buf) {
+ /* override fmbm_tcfqid 0 with a false non-0 value.
+ * This will force FM to act according to tfene.
+ * Otherwise, if fmbm_tcfqid is 0 the FM will release
+ * buffers to BM regardless of fmbm_tfene
+ */
+ iowrite32be(0xFFFFFF, &port->bmi_regs->tx.fmbm_tcfqid);
+ iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_TX_RELEASE,
+ &port->bmi_regs->tx.fmbm_tfene);
+ }
+ }
+
+ return 0;
+}
+
+static int fill_soc_specific_params(struct fman_port *port)
+{
+ u32 bmi_max_fifo_size;
+
+ bmi_max_fifo_size = fman_get_bmi_max_fifo_size(port->fm);
+ port->max_port_fifo_size = MAX_PORT_FIFO_SIZE(bmi_max_fifo_size);
+ port->bm_max_num_of_pools = 64;
+
+ /* P4080 - Major 2
+ * P2041/P3041/P5020/P5040 - Major 3
+ * Tx/Bx - Major 6
+ */
+ switch (port->rev_info.major) {
+ case 2:
+ case 3:
+ port->max_num_of_ext_pools = 4;
+ port->max_num_of_sub_portals = 12;
+ break;
+
+ case 6:
+ port->max_num_of_ext_pools = 8;
+ port->max_num_of_sub_portals = 16;
+ break;
+
+ default:
+ dev_err(port->dev, "%s: Unsupported FMan version\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int get_dflt_fifo_deq_pipeline_depth(u8 major, enum fman_port_type type,
+ u16 speed)
+{
+ switch (type) {
+ case FMAN_PORT_TYPE_RX:
+ case FMAN_PORT_TYPE_TX:
+ switch (speed) {
+ case 10000:
+ return 4;
+ case 1000:
+ if (major >= 6)
+ return 2;
+ else
+ return 1;
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
+static int get_dflt_num_of_tasks(u8 major, enum fman_port_type type,
+ u16 speed)
+{
+ switch (type) {
+ case FMAN_PORT_TYPE_RX:
+ case FMAN_PORT_TYPE_TX:
+ switch (speed) {
+ case 10000:
+ return 16;
+ case 1000:
+ if (major >= 6)
+ return 4;
+ else
+ return 3;
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
+static int get_dflt_extra_num_of_tasks(u8 major, enum fman_port_type type,
+ u16 speed)
+{
+ switch (type) {
+ case FMAN_PORT_TYPE_RX:
+ /* FMan V3 */
+ if (major >= 6)
+ return 0;
+
+ /* FMan V2 */
+ if (speed == 10000)
+ return 8;
+ else
+ return 2;
+ case FMAN_PORT_TYPE_TX:
+ default:
+ return 0;
+ }
+}
+
+static int get_dflt_num_of_open_dmas(u8 major, enum fman_port_type type,
+ u16 speed)
+{
+ int val;
+
+ if (major >= 6) {
+ switch (type) {
+ case FMAN_PORT_TYPE_TX:
+ if (speed == 10000)
+ val = 12;
+ else
+ val = 3;
+ break;
+ case FMAN_PORT_TYPE_RX:
+ if (speed == 10000)
+ val = 8;
+ else
+ val = 2;
+ break;
+ default:
+ return 0;
+ }
+ } else {
+ switch (type) {
+ case FMAN_PORT_TYPE_TX:
+ case FMAN_PORT_TYPE_RX:
+ if (speed == 10000)
+ val = 8;
+ else
+ val = 1;
+ break;
+ default:
+ val = 0;
+ }
+ }
+
+ return val;
+}
+
+static int get_dflt_extra_num_of_open_dmas(u8 major, enum fman_port_type type,
+ u16 speed)
+{
+ /* FMan V3 */
+ if (major >= 6)
+ return 0;
+
+ /* FMan V2 */
+ switch (type) {
+ case FMAN_PORT_TYPE_RX:
+ case FMAN_PORT_TYPE_TX:
+ if (speed == 10000)
+ return 8;
+ else
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int get_dflt_num_of_fifo_bufs(u8 major, enum fman_port_type type,
+ u16 speed)
+{
+ int val;
+
+ if (major >= 6) {
+ switch (type) {
+ case FMAN_PORT_TYPE_TX:
+ if (speed == 10000)
+ val = 64;
+ else
+ val = 50;
+ break;
+ case FMAN_PORT_TYPE_RX:
+ if (speed == 10000)
+ val = 96;
+ else
+ val = 50;
+ break;
+ default:
+ val = 0;
+ }
+ } else {
+ switch (type) {
+ case FMAN_PORT_TYPE_TX:
+ if (speed == 10000)
+ val = 48;
+ else
+ val = 44;
+ break;
+ case FMAN_PORT_TYPE_RX:
+ if (speed == 10000)
+ val = 48;
+ else
+ val = 45;
+ break;
+ default:
+ val = 0;
+ }
+ }
+
+ return val;
+}
+
+static void set_dflt_cfg(struct fman_port *port,
+ struct fman_port_params *port_params)
+{
+ struct fman_port_cfg *cfg = port->cfg;
+
+ cfg->dma_swap_data = FMAN_PORT_DMA_NO_SWAP;
+ cfg->color = FMAN_PORT_COLOR_GREEN;
+ cfg->rx_cut_end_bytes = DFLT_PORT_CUT_BYTES_FROM_END;
+ cfg->rx_pri_elevation = BMI_PRIORITY_ELEVATION_LEVEL;
+ cfg->rx_fifo_thr = BMI_FIFO_THRESHOLD;
+ cfg->tx_fifo_low_comf_level = (5 * 1024);
+ cfg->deq_type = FMAN_PORT_DEQ_BY_PRI;
+ cfg->deq_prefetch_option = FMAN_PORT_DEQ_FULL_PREFETCH;
+ cfg->tx_fifo_deq_pipeline_depth =
+ BMI_DEQUEUE_PIPELINE_DEPTH(port->port_type, port->port_speed);
+ cfg->deq_byte_cnt = QMI_BYTE_COUNT_LEVEL_CONTROL(port->port_type);
+
+ cfg->rx_pri_elevation =
+ DFLT_PORT_RX_FIFO_PRI_ELEVATION_LEV(port->max_port_fifo_size);
+ port->cfg->rx_fifo_thr =
+ DFLT_PORT_RX_FIFO_THRESHOLD(port->rev_info.major,
+ port->max_port_fifo_size);
+
+ if ((port->rev_info.major == 6) &&
+ ((port->rev_info.minor == 0) || (port->rev_info.minor == 3)))
+ cfg->errata_A006320 = true;
+
+ /* Excessive Threshold register - exists for pre-FMv3 chips only */
+ if (port->rev_info.major < 6)
+ cfg->excessive_threshold_register = true;
+ else
+ cfg->fmbm_tfne_has_features = true;
+
+ cfg->buffer_prefix_content.data_align =
+ DFLT_PORT_BUFFER_PREFIX_CONTEXT_DATA_ALIGN;
+}
+
+static void set_rx_dflt_cfg(struct fman_port *port,
+ struct fman_port_params *port_params)
+{
+ port->cfg->discard_mask = DFLT_PORT_ERRORS_TO_DISCARD;
+
+ memcpy(&port->cfg->ext_buf_pools,
+ &port_params->specific_params.rx_params.ext_buf_pools,
+ sizeof(struct fman_ext_pools));
+ port->cfg->err_fqid =
+ port_params->specific_params.rx_params.err_fqid;
+ port->cfg->dflt_fqid =
+ port_params->specific_params.rx_params.dflt_fqid;
+}
+
+static void set_tx_dflt_cfg(struct fman_port *port,
+ struct fman_port_params *port_params,
+ struct fman_port_dts_params *dts_params)
+{
+ port->cfg->tx_fifo_deq_pipeline_depth =
+ get_dflt_fifo_deq_pipeline_depth(port->rev_info.major,
+ port->port_type,
+ port->port_speed);
+ port->cfg->err_fqid =
+ port_params->specific_params.non_rx_params.err_fqid;
+ port->cfg->deq_sp =
+ (u8)(dts_params->qman_channel_id & QMI_DEQ_CFG_SUBPORTAL_MASK);
+ port->cfg->dflt_fqid =
+ port_params->specific_params.non_rx_params.dflt_fqid;
+ port->cfg->deq_high_priority = true;
+}
+
+/**
+ * fman_port_config
+ * @port: Pointer to the port structure
+ * @params: Pointer to data structure of parameters
+ *
+ * Creates a descriptor for the FM PORT module.
+ * The routine returns a pointer to the FM PORT object.
+ * This descriptor must be passed as first parameter to all other FM PORT
+ * function calls.
+ * No actual initialization or configuration of FM hardware is done by this
+ * routine.
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+int fman_port_config(struct fman_port *port, struct fman_port_params *params)
+{
+ void __iomem *base_addr = port->dts_params.base_addr;
+ int err;
+
+ /* Allocate the FM driver's parameters structure */
+ port->cfg = kzalloc(sizeof(*port->cfg), GFP_KERNEL);
+ if (!port->cfg)
+ goto err_params;
+
+ /* Initialize FM port parameters which will be kept by the driver */
+ port->port_type = port->dts_params.type;
+ port->port_speed = port->dts_params.speed;
+ port->port_id = port->dts_params.id;
+ port->fm = port->dts_params.fman;
+ port->ext_pools_num = (u8)8;
+
+ /* get FM revision */
+ fman_get_revision(port->fm, &port->rev_info);
+
+ err = fill_soc_specific_params(port);
+ if (err)
+ goto err_port_cfg;
+
+ switch (port->port_type) {
+ case FMAN_PORT_TYPE_RX:
+ set_rx_dflt_cfg(port, params);
+ case FMAN_PORT_TYPE_TX:
+ set_tx_dflt_cfg(port, params, &port->dts_params);
+ default:
+ set_dflt_cfg(port, params);
+ }
+
+ /* Continue with other parameters */
+ /* set memory map pointers */
+ port->bmi_regs = base_addr + BMI_PORT_REGS_OFFSET;
+ port->qmi_regs = base_addr + QMI_PORT_REGS_OFFSET;
+
+ port->max_frame_length = DFLT_PORT_MAX_FRAME_LENGTH;
+ /* resource distribution. */
+
+ port->fifo_bufs.num =
+ get_dflt_num_of_fifo_bufs(port->rev_info.major, port->port_type,
+ port->port_speed) * FMAN_BMI_FIFO_UNITS;
+ port->fifo_bufs.extra =
+ DFLT_PORT_EXTRA_NUM_OF_FIFO_BUFS * FMAN_BMI_FIFO_UNITS;
+
+ port->open_dmas.num =
+ get_dflt_num_of_open_dmas(port->rev_info.major,
+ port->port_type, port->port_speed);
+ port->open_dmas.extra =
+ get_dflt_extra_num_of_open_dmas(port->rev_info.major,
+ port->port_type, port->port_speed);
+ port->tasks.num =
+ get_dflt_num_of_tasks(port->rev_info.major,
+ port->port_type, port->port_speed);
+ port->tasks.extra =
+ get_dflt_extra_num_of_tasks(port->rev_info.major,
+ port->port_type, port->port_speed);
+
+ /* FM_HEAVY_TRAFFIC_SEQUENCER_HANG_ERRATA_FMAN_A006981 errata
+ * workaround
+ */
+ if ((port->rev_info.major == 6) && (port->rev_info.minor == 0) &&
+ (((port->port_type == FMAN_PORT_TYPE_TX) &&
+ (port->port_speed == 1000)))) {
+ port->open_dmas.num = 16;
+ port->open_dmas.extra = 0;
+ }
+
+ if (port->rev_info.major >= 6 &&
+ port->port_type == FMAN_PORT_TYPE_TX &&
+ port->port_speed == 1000) {
+ /* FM_WRONG_RESET_VALUES_ERRATA_FMAN_A005127 Errata
+ * workaround
+ */
+ if (port->rev_info.major >= 6) {
+ u32 reg;
+
+ reg = 0x00001013;
+ iowrite32be(reg, &port->bmi_regs->tx.fmbm_tfp);
+ }
+ }
+
+ return 0;
+
+err_port_cfg:
+ kfree(port->cfg);
+err_params:
+ kfree(port);
+ return -EINVAL;
+}
+EXPORT_SYMBOL(fman_port_config);
+
+/**
+ * fman_port_init
+ * port: A pointer to a FM Port module.
+ * Initializes the FM PORT module by defining the software structure and
+ * configuring the hardware registers.
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+int fman_port_init(struct fman_port *port)
+{
+ struct fman_port_cfg *cfg;
+ int err;
+ struct fman_port_init_params params;
+
+ if (is_init_done(port->cfg))
+ return -EINVAL;
+
+ err = fman_sp_build_buffer_struct(&port->cfg->int_context,
+ &port->cfg->buffer_prefix_content,
+ &port->cfg->buf_margins,
+ &port->buffer_offsets,
+ &port->internal_buf_offset);
+ if (err)
+ return err;
+
+ cfg = port->cfg;
+
+ if (port->port_type == FMAN_PORT_TYPE_RX) {
+ /* Call the external Buffer routine which also checks fifo
+ * size and updates it if necessary
+ */
+ /* define external buffer pools and pool depletion */
+ err = set_ext_buffer_pools(port);
+ if (err)
+ return err;
+ /* check if the largest external buffer pool is large enough */
+ if (cfg->buf_margins.start_margins + MIN_EXT_BUF_SIZE +
+ cfg->buf_margins.end_margins >
+ port->rx_pools_params.largest_buf_size) {
+ dev_err(port->dev, "%s: buf_margins.start_margins (%d) + minimum buf size (64) + buf_margins.end_margins (%d) is larger than maximum external buffer size (%d)\n",
+ __func__, cfg->buf_margins.start_margins,
+ cfg->buf_margins.end_margins,
+ port->rx_pools_params.largest_buf_size);
+ return -EINVAL;
+ }
+ }
+
+ /* Call FM module routine for communicating parameters */
+ memset(&params, 0, sizeof(params));
+ params.port_id = port->port_id;
+ params.port_type = port->port_type;
+ params.port_speed = port->port_speed;
+ params.num_of_tasks = (u8)port->tasks.num;
+ params.num_of_extra_tasks = (u8)port->tasks.extra;
+ params.num_of_open_dmas = (u8)port->open_dmas.num;
+ params.num_of_extra_open_dmas = (u8)port->open_dmas.extra;
+
+ if (port->fifo_bufs.num) {
+ err = verify_size_of_fifo(port);
+ if (err)
+ return err;
+ }
+ params.size_of_fifo = port->fifo_bufs.num;
+ params.extra_size_of_fifo = port->fifo_bufs.extra;
+ params.deq_pipeline_depth = port->cfg->tx_fifo_deq_pipeline_depth;
+ params.max_frame_length = port->max_frame_length;
+
+ err = fman_set_port_params(port->fm, &params);
+ if (err)
+ return err;
+
+ err = init_low_level_driver(port);
+ if (err)
+ return err;
+
+ kfree(port->cfg);
+ port->cfg = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL(fman_port_init);
+
+/**
+ * fman_port_cfg_buf_prefix_content
+ * @port A pointer to a FM Port module.
+ * @buffer_prefix_content A structure of parameters describing
+ * the structure of the buffer.
+ * Out parameter:
+ * Start margin - offset of data from
+ * start of external buffer.
+ * Defines the structure, size and content of the application buffer.
+ * The prefix, in Tx ports, if 'pass_prs_result', the application should set
+ * a value to their offsets in the prefix of the FM will save the first
+ * 'priv_data_size', than, depending on 'pass_prs_result' and
+ * 'pass_time_stamp', copy parse result and timeStamp, and the packet itself
+ * (in this order), to the application buffer, and to offset.
+ * Calling this routine changes the buffer margins definitions in the internal
+ * driver data base from its default configuration:
+ * Data size: [DEFAULT_PORT_BUFFER_PREFIX_CONTENT_PRIV_DATA_SIZE]
+ * Pass Parser result: [DEFAULT_PORT_BUFFER_PREFIX_CONTENT_PASS_PRS_RESULT].
+ * Pass timestamp: [DEFAULT_PORT_BUFFER_PREFIX_CONTENT_PASS_TIME_STAMP].
+ * May be used for all ports
+ *
+ * Allowed only following fman_port_config() and before fman_port_init().
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+int fman_port_cfg_buf_prefix_content(struct fman_port *port,
+ struct fman_buffer_prefix_content *
+ buffer_prefix_content)
+{
+ if (is_init_done(port->cfg))
+ return -EINVAL;
+
+ memcpy(&port->cfg->buffer_prefix_content,
+ buffer_prefix_content,
+ sizeof(struct fman_buffer_prefix_content));
+ /* if data_align was not initialized by user,
+ * we return to driver's default
+ */
+ if (!port->cfg->buffer_prefix_content.data_align)
+ port->cfg->buffer_prefix_content.data_align =
+ DFLT_PORT_BUFFER_PREFIX_CONTEXT_DATA_ALIGN;
+
+ return 0;
+}
+EXPORT_SYMBOL(fman_port_cfg_buf_prefix_content);
+
+/**
+ * fman_port_disable
+ * port: A pointer to a FM Port module.
+ *
+ * Gracefully disable an FM port. The port will not start new tasks after all
+ * tasks associated with the port are terminated.
+ *
+ * This is a blocking routine, it returns after port is gracefully stopped,
+ * i.e. the port will not except new frames, but it will finish all frames
+ * or tasks which were already began.
+ * Allowed only following fman_port_init().
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+int fman_port_disable(struct fman_port *port)
+{
+ u32 __iomem *bmi_cfg_reg, *bmi_status_reg, tmp;
+ bool rx_port, failure = false;
+ int count;
+
+ if (!is_init_done(port->cfg))
+ return -EINVAL;
+
+ switch (port->port_type) {
+ case FMAN_PORT_TYPE_RX:
+ bmi_cfg_reg = &port->bmi_regs->rx.fmbm_rcfg;
+ bmi_status_reg = &port->bmi_regs->rx.fmbm_rst;
+ rx_port = true;
+ break;
+ case FMAN_PORT_TYPE_TX:
+ bmi_cfg_reg = &port->bmi_regs->tx.fmbm_tcfg;
+ bmi_status_reg = &port->bmi_regs->tx.fmbm_tst;
+ rx_port = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Disable QMI */
+ if (!rx_port) {
+ tmp = ioread32be(&port->qmi_regs->fmqm_pnc) & ~QMI_PORT_CFG_EN;
+ iowrite32be(tmp, &port->qmi_regs->fmqm_pnc);
+
+ /* Wait for QMI to finish FD handling */
+ count = 100;
+ do {
+ udelay(10);
+ tmp = ioread32be(&port->qmi_regs->fmqm_pns);
+ } while ((tmp & QMI_PORT_STATUS_DEQ_FD_BSY) && --count);
+
+ if (count == 0) {
+ /* Timeout */
+ failure = true;
+ }
+ }
+
+ /* Disable BMI */
+ tmp = ioread32be(bmi_cfg_reg) & ~BMI_PORT_CFG_EN;
+ iowrite32be(tmp, bmi_cfg_reg);
+
+ /* Wait for graceful stop end */
+ count = 500;
+ do {
+ udelay(10);
+ tmp = ioread32be(bmi_status_reg);
+ } while ((tmp & BMI_PORT_STATUS_BSY) && --count);
+
+ if (count == 0) {
+ /* Timeout */
+ failure = true;
+ }
+
+ if (failure)
+ dev_dbg(port->dev, "%s: FMan Port[%d]: BMI or QMI is Busy. Port forced down\n",
+ __func__, port->port_id);
+
+ return 0;
+}
+EXPORT_SYMBOL(fman_port_disable);
+
+/**
+ * fman_port_enable
+ * port: A pointer to a FM Port module.
+ *
+ * A runtime routine provided to allow disable/enable of port.
+ *
+ * Allowed only following fman_port_init().
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+int fman_port_enable(struct fman_port *port)
+{
+ u32 __iomem *bmi_cfg_reg, tmp;
+ bool rx_port;
+
+ if (!is_init_done(port->cfg))
+ return -EINVAL;
+
+ switch (port->port_type) {
+ case FMAN_PORT_TYPE_RX:
+ bmi_cfg_reg = &port->bmi_regs->rx.fmbm_rcfg;
+ rx_port = true;
+ break;
+ case FMAN_PORT_TYPE_TX:
+ bmi_cfg_reg = &port->bmi_regs->tx.fmbm_tcfg;
+ rx_port = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Enable QMI */
+ if (!rx_port) {
+ tmp = ioread32be(&port->qmi_regs->fmqm_pnc) | QMI_PORT_CFG_EN;
+ iowrite32be(tmp, &port->qmi_regs->fmqm_pnc);
+ }
+
+ /* Enable BMI */
+ tmp = ioread32be(bmi_cfg_reg) | BMI_PORT_CFG_EN;
+ iowrite32be(tmp, bmi_cfg_reg);
+
+ return 0;
+}
+EXPORT_SYMBOL(fman_port_enable);
+
+/**
+ * fman_port_bind
+ * dev: FMan Port OF device pointer
+ *
+ * Bind to a specific FMan Port.
+ *
+ * Allowed only after the port was created.
+ *
+ * Return: A pointer to the FMan port device.
+ */
+struct fman_port *fman_port_bind(struct device *dev)
+{
+ return (struct fman_port *)(dev_get_drvdata(get_device(dev)));
+}
+EXPORT_SYMBOL(fman_port_bind);
+
+/**
+ * fman_port_get_qman_channel_id
+ * port: Pointer to the FMan port devuce
+ *
+ * Get the QMan channel ID for the specific port
+ *
+ * Return: QMan channel ID
+ */
+u32 fman_port_get_qman_channel_id(struct fman_port *port)
+{
+ return port->dts_params.qman_channel_id;
+}
+EXPORT_SYMBOL(fman_port_get_qman_channel_id);
+
+static int fman_port_probe(struct platform_device *of_dev)
+{
+ struct fman_port *port;
+ struct fman *fman;
+ struct device_node *fm_node, *port_node;
+ struct resource res;
+ struct resource *dev_res;
+ const u32 *u32_prop;
+ int err = 0, lenp;
+ enum fman_port_type port_type;
+ u16 port_speed;
+ u8 port_id;
+
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->dev = &of_dev->dev;
+
+ port_node = of_node_get(of_dev->dev.of_node);
+
+ /* Get the FM node */
+ fm_node = of_get_parent(port_node);
+ if (!fm_node) {
+ dev_err(port->dev, "%s: of_get_parent() failed\n", __func__);
+ err = -ENODEV;
+ goto return_err;
+ }
+
+ fman = dev_get_drvdata(&of_find_device_by_node(fm_node)->dev);
+ of_node_put(fm_node);
+ if (!fman) {
+ err = -EINVAL;
+ goto return_err;
+ }
+
+ u32_prop = (const u32 *)of_get_property(port_node, "cell-index", &lenp);
+ if (!u32_prop) {
+ dev_err(port->dev, "%s: of_get_property(%s, cell-index) failed\n",
+ __func__, port_node->full_name);
+ err = -EINVAL;
+ goto return_err;
+ }
+ if (WARN_ON(lenp != sizeof(u32))) {
+ err = -EINVAL;
+ goto return_err;
+ }
+ port_id = (u8)fdt32_to_cpu(u32_prop[0]);
+
+ port->dts_params.id = port_id;
+
+ if (of_device_is_compatible(port_node, "fsl,fman-v3-port-tx")) {
+ port_type = FMAN_PORT_TYPE_TX;
+ port_speed = 1000;
+ u32_prop = (const u32 *)of_get_property(port_node,
+ "fsl,fman-10g-port",
+ &lenp);
+ if (u32_prop)
+ port_speed = 10000;
+
+ } else if (of_device_is_compatible(port_node, "fsl,fman-v2-port-tx")) {
+ if (port_id >= TX_10G_PORT_BASE)
+ port_speed = 10000;
+ else
+ port_speed = 1000;
+ port_type = FMAN_PORT_TYPE_TX;
+
+ } else if (of_device_is_compatible(port_node, "fsl,fman-v3-port-rx")) {
+ port_type = FMAN_PORT_TYPE_RX;
+ port_speed = 1000;
+ u32_prop = (const u32 *)of_get_property(port_node,
+ "fsl,fman-10g-port", &lenp);
+ if (u32_prop)
+ port_speed = 10000;
+
+ } else if (of_device_is_compatible(port_node, "fsl,fman-v2-port-rx")) {
+ if (port_id >= RX_10G_PORT_BASE)
+ port_speed = 10000;
+ else
+ port_speed = 1000;
+ port_type = FMAN_PORT_TYPE_RX;
+
+ } else {
+ dev_err(port->dev, "%s: Illegal port type\n", __func__);
+ err = -EINVAL;
+ goto return_err;
+ }
+
+ port->dts_params.type = port_type;
+ port->dts_params.speed = port_speed;
+
+ if (port_type == FMAN_PORT_TYPE_TX) {
+ u32 qman_channel_id;
+
+ qman_channel_id = fman_get_qman_channel_id(fman, port_id);
+ if (qman_channel_id == 0) {
+ dev_err(port->dev, "%s: incorrect qman-channel-id\n",
+ __func__);
+ err = -EINVAL;
+ goto return_err;
+ }
+ port->dts_params.qman_channel_id = qman_channel_id;
+ }
+
+ err = of_address_to_resource(port_node, 0, &res);
+ if (err < 0) {
+ dev_err(port->dev, "%s: of_address_to_resource() failed\n",
+ __func__);
+ err = -ENOMEM;
+ goto return_err;
+ }
+
+ port->dts_params.fman = fman;
+
+ of_node_put(port_node);
+
+ dev_res = __devm_request_region(port->dev, &res, res.start,
+ resource_size(&res), "fman-port");
+ if (!dev_res) {
+ dev_err(port->dev, "%s: __devm_request_region() failed\n",
+ __func__);
+ err = -EINVAL;
+ goto free_port;
+ }
+
+ port->dts_params.base_addr = devm_ioremap(port->dev, res.start,
+ resource_size(&res));
+ if (port->dts_params.base_addr == 0)
+ dev_err(port->dev, "%s: devm_ioremap() failed\n", __func__);
+
+ dev_set_drvdata(&of_dev->dev, port);
+
+ return 0;
+
+return_err:
+ of_node_put(port_node);
+free_port:
+ kfree(port);
+ return err;
+}
+
+static const struct of_device_id fman_port_match[] = {
+ {.compatible = "fsl,fman-v3-port-rx"},
+ {.compatible = "fsl,fman-v2-port-rx"},
+ {.compatible = "fsl,fman-v3-port-tx"},
+ {.compatible = "fsl,fman-v2-port-tx"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, fman_port_match);
+
+static struct platform_driver fman_port_driver = {
+ .driver = {
+ .name = "fsl-fman-port",
+ .of_match_table = fman_port_match,
+ },
+ .probe = fman_port_probe,
+};
+
+builtin_platform_driver(fman_port_driver);
diff --git a/drivers/net/ethernet/freescale/fman/fman_port.h b/drivers/net/ethernet/freescale/fman/fman_port.h
new file mode 100644
index 000000000000..8ba901737048
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_port.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2008 - 2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __FMAN_PORT_H
+#define __FMAN_PORT_H
+
+#include "fman.h"
+
+/* FM Port API
+ * The FM uses a general module called "port" to represent a Tx port (MAC),
+ * an Rx port (MAC).
+ * The number of ports in an FM varies between SOCs.
+ * The SW driver manages these ports as sub-modules of the FM,i.e. after an
+ * FM is initialized, its ports may be initialized and operated upon.
+ * The port is initialized aware of its type, but other functions on a port
+ * may be indifferent to its type. When necessary, the driver verifies
+ * coherence and returns error if applicable.
+ * On initialization, user specifies the port type and it's index (relative
+ * to the port's type) - always starting at 0.
+ */
+
+/* FM Frame error */
+/* Frame Descriptor errors */
+/* Not for Rx-Port! Unsupported Format */
+#define FM_PORT_FRM_ERR_UNSUPPORTED_FORMAT FM_FD_ERR_UNSUPPORTED_FORMAT
+/* Not for Rx-Port! Length Error */
+#define FM_PORT_FRM_ERR_LENGTH FM_FD_ERR_LENGTH
+/* DMA Data error */
+#define FM_PORT_FRM_ERR_DMA FM_FD_ERR_DMA
+/* non Frame-Manager error; probably come from SEC that was chained to FM */
+#define FM_PORT_FRM_ERR_NON_FM FM_FD_RX_STATUS_ERR_NON_FM
+ /* IPR error */
+#define FM_PORT_FRM_ERR_IPRE (FM_FD_ERR_IPR & ~FM_FD_IPR)
+/* IPR non-consistent-sp */
+#define FM_PORT_FRM_ERR_IPR_NCSP (FM_FD_ERR_IPR_NCSP & \
+ ~FM_FD_IPR)
+
+/* Rx FIFO overflow, FCS error, code error, running disparity
+ * error (SGMII and TBI modes), FIFO parity error.
+ * PHY Sequence error, PHY error control character detected.
+ */
+#define FM_PORT_FRM_ERR_PHYSICAL FM_FD_ERR_PHYSICAL
+/* Frame too long OR Frame size exceeds max_length_frame */
+#define FM_PORT_FRM_ERR_SIZE FM_FD_ERR_SIZE
+/* indicates a classifier "drop" operation */
+#define FM_PORT_FRM_ERR_CLS_DISCARD FM_FD_ERR_CLS_DISCARD
+/* Extract Out of Frame */
+#define FM_PORT_FRM_ERR_EXTRACTION FM_FD_ERR_EXTRACTION
+/* No Scheme Selected */
+#define FM_PORT_FRM_ERR_NO_SCHEME FM_FD_ERR_NO_SCHEME
+/* Keysize Overflow */
+#define FM_PORT_FRM_ERR_KEYSIZE_OVERFLOW FM_FD_ERR_KEYSIZE_OVERFLOW
+/* Frame color is red */
+#define FM_PORT_FRM_ERR_COLOR_RED FM_FD_ERR_COLOR_RED
+/* Frame color is yellow */
+#define FM_PORT_FRM_ERR_COLOR_YELLOW FM_FD_ERR_COLOR_YELLOW
+/* Parser Time out Exceed */
+#define FM_PORT_FRM_ERR_PRS_TIMEOUT FM_FD_ERR_PRS_TIMEOUT
+/* Invalid Soft Parser instruction */
+#define FM_PORT_FRM_ERR_PRS_ILL_INSTRUCT FM_FD_ERR_PRS_ILL_INSTRUCT
+/* Header error was identified during parsing */
+#define FM_PORT_FRM_ERR_PRS_HDR_ERR FM_FD_ERR_PRS_HDR_ERR
+/* Frame parsed beyind 256 first bytes */
+#define FM_PORT_FRM_ERR_BLOCK_LIMIT_EXCEEDED FM_FD_ERR_BLOCK_LIMIT_EXCEEDED
+/* FPM Frame Processing Timeout Exceeded */
+#define FM_PORT_FRM_ERR_PROCESS_TIMEOUT 0x00000001
+
+struct fman_port;
+
+/* A structure for additional Rx port parameters */
+struct fman_port_rx_params {
+ u32 err_fqid; /* Error Queue Id. */
+ u32 dflt_fqid; /* Default Queue Id. */
+ /* Which external buffer pools are used
+ * (up to FMAN_PORT_MAX_EXT_POOLS_NUM), and their sizes.
+ */
+ struct fman_ext_pools ext_buf_pools;
+};
+
+/* A structure for additional non-Rx port parameters */
+struct fman_port_non_rx_params {
+ /* Error Queue Id. */
+ u32 err_fqid;
+ /* For Tx - Default Confirmation queue, 0 means no Tx confirmation
+ * for processed frames. For OP port - default Rx queue.
+ */
+ u32 dflt_fqid;
+};
+
+/* A union for additional parameters depending on port type */
+union fman_port_specific_params {
+ /* Rx port parameters structure */
+ struct fman_port_rx_params rx_params;
+ /* Non-Rx port parameters structure */
+ struct fman_port_non_rx_params non_rx_params;
+};
+
+/* A structure representing FM initialization parameters */
+struct fman_port_params {
+ /* Virtual Address of memory mapped FM Port registers. */
+ void *fm;
+ union fman_port_specific_params specific_params;
+ /* Additional parameters depending on port type. */
+};
+
+int fman_port_config(struct fman_port *port, struct fman_port_params *params);
+
+int fman_port_init(struct fman_port *port);
+
+int fman_port_cfg_buf_prefix_content(struct fman_port *port,
+ struct fman_buffer_prefix_content
+ *buffer_prefix_content);
+
+int fman_port_disable(struct fman_port *port);
+
+int fman_port_enable(struct fman_port *port);
+
+u32 fman_port_get_qman_channel_id(struct fman_port *port);
+
+struct fman_port *fman_port_bind(struct device *dev);
+
+#endif /* __FMAN_PORT_H */
diff --git a/drivers/net/ethernet/freescale/fman/fman_sp.c b/drivers/net/ethernet/freescale/fman/fman_sp.c
new file mode 100644
index 000000000000..f9e7aa385cba
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_sp.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2008 - 2015 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 "fman_sp.h"
+#include "fman.h"
+
+void fman_sp_set_buf_pools_in_asc_order_of_buf_sizes(struct fman_ext_pools
+ *fm_ext_pools,
+ u8 *ordered_array,
+ u16 *sizes_array)
+{
+ u16 buf_size = 0;
+ int i = 0, j = 0, k = 0;
+
+ /* First we copy the external buffers pools information
+ * to an ordered local array
+ */
+ for (i = 0; i < fm_ext_pools->num_of_pools_used; i++) {
+ /* get pool size */
+ buf_size = fm_ext_pools->ext_buf_pool[i].size;
+
+ /* keep sizes in an array according to poolId
+ * for direct access
+ */
+ sizes_array[fm_ext_pools->ext_buf_pool[i].id] = buf_size;
+
+ /* save poolId in an ordered array according to size */
+ for (j = 0; j <= i; j++) {
+ /* this is the next free place in the array */
+ if (j == i)
+ ordered_array[i] =
+ fm_ext_pools->ext_buf_pool[i].id;
+ else {
+ /* find the right place for this poolId */
+ if (buf_size < sizes_array[ordered_array[j]]) {
+ /* move the pool_ids one place ahead
+ * to make room for this poolId
+ */
+ for (k = i; k > j; k--)
+ ordered_array[k] =
+ ordered_array[k - 1];
+
+ /* now k==j, this is the place for
+ * the new size
+ */
+ ordered_array[k] =
+ fm_ext_pools->ext_buf_pool[i].id;
+ break;
+ }
+ }
+ }
+ }
+}
+
+int fman_sp_build_buffer_struct(struct fman_sp_int_context_data_copy *
+ int_context_data_copy,
+ struct fman_buffer_prefix_content *
+ buffer_prefix_content,
+ struct fman_sp_buf_margins *buf_margins,
+ struct fman_sp_buffer_offsets *buffer_offsets,
+ u8 *internal_buf_offset)
+{
+ u32 tmp;
+
+ /* Align start of internal context data to 16 byte */
+ int_context_data_copy->ext_buf_offset = (u16)
+ ((buffer_prefix_content->priv_data_size & (OFFSET_UNITS - 1)) ?
+ ((buffer_prefix_content->priv_data_size + OFFSET_UNITS) &
+ ~(u16)(OFFSET_UNITS - 1)) :
+ buffer_prefix_content->priv_data_size);
+
+ /* Translate margin and int_context params to FM parameters */
+ /* Initialize with illegal value. Later we'll set legal values. */
+ buffer_offsets->prs_result_offset = (u32)ILLEGAL_BASE;
+ buffer_offsets->time_stamp_offset = (u32)ILLEGAL_BASE;
+ buffer_offsets->hash_result_offset = (u32)ILLEGAL_BASE;
+
+ /* Internally the driver supports 4 options
+ * 1. prsResult/timestamp/hashResult selection (in fact 8 options,
+ * but for simplicity we'll
+ * relate to it as 1).
+ * 2. All IC context (from AD) not including debug.
+ */
+
+ /* This case covers the options under 1 */
+ /* Copy size must be in 16-byte granularity. */
+ int_context_data_copy->size =
+ (u16)((buffer_prefix_content->pass_prs_result ? 32 : 0) +
+ ((buffer_prefix_content->pass_time_stamp ||
+ buffer_prefix_content->pass_hash_result) ? 16 : 0));
+
+ /* Align start of internal context data to 16 byte */
+ int_context_data_copy->int_context_offset =
+ (u8)(buffer_prefix_content->pass_prs_result ? 32 :
+ ((buffer_prefix_content->pass_time_stamp ||
+ buffer_prefix_content->pass_hash_result) ? 64 : 0));
+
+ if (buffer_prefix_content->pass_prs_result)
+ buffer_offsets->prs_result_offset =
+ int_context_data_copy->ext_buf_offset;
+ if (buffer_prefix_content->pass_time_stamp)
+ buffer_offsets->time_stamp_offset =
+ buffer_prefix_content->pass_prs_result ?
+ (int_context_data_copy->ext_buf_offset +
+ sizeof(struct fman_prs_result)) :
+ int_context_data_copy->ext_buf_offset;
+ if (buffer_prefix_content->pass_hash_result)
+ /* If PR is not requested, whether TS is
+ * requested or not, IC will be copied from TS
+ */
+ buffer_offsets->hash_result_offset =
+ buffer_prefix_content->pass_prs_result ?
+ (int_context_data_copy->ext_buf_offset +
+ sizeof(struct fman_prs_result) + 8) :
+ int_context_data_copy->ext_buf_offset + 8;
+
+ if (int_context_data_copy->size)
+ buf_margins->start_margins =
+ (u16)(int_context_data_copy->ext_buf_offset +
+ int_context_data_copy->size);
+ else
+ /* No Internal Context passing, STartMargin is
+ * immediately after private_info
+ */
+ buf_margins->start_margins =
+ buffer_prefix_content->priv_data_size;
+
+ /* align data start */
+ tmp = (u32)(buf_margins->start_margins %
+ buffer_prefix_content->data_align);
+ if (tmp)
+ buf_margins->start_margins +=
+ (buffer_prefix_content->data_align - tmp);
+ buffer_offsets->data_offset = buf_margins->start_margins;
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/freescale/fman/fman_sp.h b/drivers/net/ethernet/freescale/fman/fman_sp.h
new file mode 100644
index 000000000000..820b7f63088f
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_sp.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2008 - 2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __FM_SP_H
+#define __FM_SP_H
+
+#include "fman.h"
+#include <linux/types.h>
+
+#define ILLEGAL_BASE (~0)
+
+/* defaults */
+#define DFLT_FM_SP_BUFFER_PREFIX_CONTEXT_DATA_ALIGN 64
+
+/* Registers bit fields */
+#define FMAN_SP_EXT_BUF_POOL_EN_COUNTER 0x40000000
+#define FMAN_SP_EXT_BUF_POOL_VALID 0x80000000
+#define FMAN_SP_EXT_BUF_POOL_BACKUP 0x20000000
+#define FMAN_SP_DMA_ATTR_WRITE_OPTIMIZE 0x00100000
+#define FMAN_SP_SG_DISABLE 0x80000000
+
+/* shifts */
+#define FMAN_SP_EXT_BUF_MARG_START_SHIFT 16
+#define FMAN_SP_DMA_ATTR_SWP_SHIFT 30
+#define FMAN_SP_IC_TO_EXT_SHIFT 16
+#define FMAN_SP_IC_FROM_INT_SHIFT 8
+
+/* structure for defining internal context copying */
+struct fman_sp_int_context_data_copy {
+ /* < Offset in External buffer to which internal
+ * context is copied to (Rx) or taken from (Tx, Op).
+ */
+ u16 ext_buf_offset;
+ /* Offset within internal context to copy from
+ * (Rx) or to copy to (Tx, Op).
+ */
+ u8 int_context_offset;
+ /* Internal offset size to be copied */
+ u16 size;
+};
+
+/* struct for defining external buffer margins */
+struct fman_sp_buf_margins {
+ /* Number of bytes to be left at the beginning
+ * of the external buffer (must be divisible by 16)
+ */
+ u16 start_margins;
+ /* number of bytes to be left at the end
+ * of the external buffer(must be divisible by 16)
+ */
+ u16 end_margins;
+};
+
+struct fman_sp_buffer_offsets {
+ u32 data_offset;
+ u32 prs_result_offset;
+ u32 time_stamp_offset;
+ u32 hash_result_offset;
+};
+
+int fman_sp_build_buffer_struct(struct fman_sp_int_context_data_copy
+ *int_context_data_copy,
+ struct fman_buffer_prefix_content
+ *buffer_prefix_content,
+ struct fman_sp_buf_margins *buf_margins,
+ struct fman_sp_buffer_offsets
+ *buffer_offsets,
+ u8 *internal_buf_offset);
+
+void fman_sp_set_buf_pools_in_asc_order_of_buf_sizes(struct fman_ext_pools
+ *fm_ext_pools,
+ u8 *ordered_array,
+ u16 *sizes_array);
+
+#endif /* __FM_SP_H */
diff --git a/drivers/net/ethernet/freescale/fman/fman_tgec.c b/drivers/net/ethernet/freescale/fman/fman_tgec.c
new file mode 100644
index 000000000000..efabb04a1ae8
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_tgec.c
@@ -0,0 +1,786 @@
+/*
+ * Copyright 2008-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "fman_tgec.h"
+#include "fman.h"
+
+#include <linux/slab.h>
+#include <linux/bitrev.h>
+#include <linux/io.h>
+#include <linux/crc32.h>
+
+/* Transmit Inter-Packet Gap Length Register (TX_IPG_LENGTH) */
+#define TGEC_TX_IPG_LENGTH_MASK 0x000003ff
+
+/* Command and Configuration Register (COMMAND_CONFIG) */
+#define CMD_CFG_NO_LEN_CHK 0x00020000
+#define CMD_CFG_PAUSE_IGNORE 0x00000100
+#define CMF_CFG_CRC_FWD 0x00000040
+#define CMD_CFG_PROMIS_EN 0x00000010
+#define CMD_CFG_RX_EN 0x00000002
+#define CMD_CFG_TX_EN 0x00000001
+
+/* Interrupt Mask Register (IMASK) */
+#define TGEC_IMASK_MDIO_SCAN_EVENT 0x00010000
+#define TGEC_IMASK_MDIO_CMD_CMPL 0x00008000
+#define TGEC_IMASK_REM_FAULT 0x00004000
+#define TGEC_IMASK_LOC_FAULT 0x00002000
+#define TGEC_IMASK_TX_ECC_ER 0x00001000
+#define TGEC_IMASK_TX_FIFO_UNFL 0x00000800
+#define TGEC_IMASK_TX_FIFO_OVFL 0x00000400
+#define TGEC_IMASK_TX_ER 0x00000200
+#define TGEC_IMASK_RX_FIFO_OVFL 0x00000100
+#define TGEC_IMASK_RX_ECC_ER 0x00000080
+#define TGEC_IMASK_RX_JAB_FRM 0x00000040
+#define TGEC_IMASK_RX_OVRSZ_FRM 0x00000020
+#define TGEC_IMASK_RX_RUNT_FRM 0x00000010
+#define TGEC_IMASK_RX_FRAG_FRM 0x00000008
+#define TGEC_IMASK_RX_LEN_ER 0x00000004
+#define TGEC_IMASK_RX_CRC_ER 0x00000002
+#define TGEC_IMASK_RX_ALIGN_ER 0x00000001
+
+/* Hashtable Control Register (HASHTABLE_CTRL) */
+#define TGEC_HASH_MCAST_SHIFT 23
+#define TGEC_HASH_MCAST_EN 0x00000200
+#define TGEC_HASH_ADR_MSK 0x000001ff
+
+#define DEFAULT_TX_IPG_LENGTH 12
+#define DEFAULT_MAX_FRAME_LENGTH 0x600
+#define DEFAULT_PAUSE_QUANT 0xf000
+
+/* number of pattern match registers (entries) */
+#define TGEC_NUM_OF_PADDRS 1
+
+/* Group address bit indication */
+#define GROUP_ADDRESS 0x0000010000000000LL
+
+/* Hash table size (= 32 bits*8 regs) */
+#define TGEC_HASH_TABLE_SIZE 512
+
+/* tGEC memory map */
+struct tgec_regs {
+ u32 tgec_id; /* 0x000 Controller ID */
+ u32 reserved001[1]; /* 0x004 */
+ u32 command_config; /* 0x008 Control and configuration */
+ u32 mac_addr_0; /* 0x00c Lower 32 bits of the MAC adr */
+ u32 mac_addr_1; /* 0x010 Upper 16 bits of the MAC adr */
+ u32 maxfrm; /* 0x014 Maximum frame length */
+ u32 pause_quant; /* 0x018 Pause quanta */
+ u32 rx_fifo_sections; /* 0x01c */
+ u32 tx_fifo_sections; /* 0x020 */
+ u32 rx_fifo_almost_f_e; /* 0x024 */
+ u32 tx_fifo_almost_f_e; /* 0x028 */
+ u32 hashtable_ctrl; /* 0x02c Hash table control */
+ u32 mdio_cfg_status; /* 0x030 */
+ u32 mdio_command; /* 0x034 */
+ u32 mdio_data; /* 0x038 */
+ u32 mdio_regaddr; /* 0x03c */
+ u32 status; /* 0x040 */
+ u32 tx_ipg_len; /* 0x044 Transmitter inter-packet-gap */
+ u32 mac_addr_2; /* 0x048 Lower 32 bits of 2nd MAC adr */
+ u32 mac_addr_3; /* 0x04c Upper 16 bits of 2nd MAC adr */
+ u32 rx_fifo_ptr_rd; /* 0x050 */
+ u32 rx_fifo_ptr_wr; /* 0x054 */
+ u32 tx_fifo_ptr_rd; /* 0x058 */
+ u32 tx_fifo_ptr_wr; /* 0x05c */
+ u32 imask; /* 0x060 Interrupt mask */
+ u32 ievent; /* 0x064 Interrupt event */
+ u32 udp_port; /* 0x068 Defines a UDP Port number */
+ u32 type_1588v2; /* 0x06c Type field for 1588v2 */
+ u32 reserved070[4]; /* 0x070 */
+ /* 10Ge Statistics Counter */
+ u32 tfrm_u; /* 80 aFramesTransmittedOK */
+ u32 tfrm_l; /* 84 aFramesTransmittedOK */
+ u32 rfrm_u; /* 88 aFramesReceivedOK */
+ u32 rfrm_l; /* 8c aFramesReceivedOK */
+ u32 rfcs_u; /* 90 aFrameCheckSequenceErrors */
+ u32 rfcs_l; /* 94 aFrameCheckSequenceErrors */
+ u32 raln_u; /* 98 aAlignmentErrors */
+ u32 raln_l; /* 9c aAlignmentErrors */
+ u32 txpf_u; /* A0 aPAUSEMACCtrlFramesTransmitted */
+ u32 txpf_l; /* A4 aPAUSEMACCtrlFramesTransmitted */
+ u32 rxpf_u; /* A8 aPAUSEMACCtrlFramesReceived */
+ u32 rxpf_l; /* Ac aPAUSEMACCtrlFramesReceived */
+ u32 rlong_u; /* B0 aFrameTooLongErrors */
+ u32 rlong_l; /* B4 aFrameTooLongErrors */
+ u32 rflr_u; /* B8 aInRangeLengthErrors */
+ u32 rflr_l; /* Bc aInRangeLengthErrors */
+ u32 tvlan_u; /* C0 VLANTransmittedOK */
+ u32 tvlan_l; /* C4 VLANTransmittedOK */
+ u32 rvlan_u; /* C8 VLANReceivedOK */
+ u32 rvlan_l; /* Cc VLANReceivedOK */
+ u32 toct_u; /* D0 if_out_octets */
+ u32 toct_l; /* D4 if_out_octets */
+ u32 roct_u; /* D8 if_in_octets */
+ u32 roct_l; /* Dc if_in_octets */
+ u32 ruca_u; /* E0 if_in_ucast_pkts */
+ u32 ruca_l; /* E4 if_in_ucast_pkts */
+ u32 rmca_u; /* E8 ifInMulticastPkts */
+ u32 rmca_l; /* Ec ifInMulticastPkts */
+ u32 rbca_u; /* F0 ifInBroadcastPkts */
+ u32 rbca_l; /* F4 ifInBroadcastPkts */
+ u32 terr_u; /* F8 if_out_errors */
+ u32 terr_l; /* Fc if_out_errors */
+ u32 reserved100[2]; /* 100-108 */
+ u32 tuca_u; /* 108 if_out_ucast_pkts */
+ u32 tuca_l; /* 10c if_out_ucast_pkts */
+ u32 tmca_u; /* 110 ifOutMulticastPkts */
+ u32 tmca_l; /* 114 ifOutMulticastPkts */
+ u32 tbca_u; /* 118 ifOutBroadcastPkts */
+ u32 tbca_l; /* 11c ifOutBroadcastPkts */
+ u32 rdrp_u; /* 120 etherStatsDropEvents */
+ u32 rdrp_l; /* 124 etherStatsDropEvents */
+ u32 reoct_u; /* 128 etherStatsOctets */
+ u32 reoct_l; /* 12c etherStatsOctets */
+ u32 rpkt_u; /* 130 etherStatsPkts */
+ u32 rpkt_l; /* 134 etherStatsPkts */
+ u32 trund_u; /* 138 etherStatsUndersizePkts */
+ u32 trund_l; /* 13c etherStatsUndersizePkts */
+ u32 r64_u; /* 140 etherStatsPkts64Octets */
+ u32 r64_l; /* 144 etherStatsPkts64Octets */
+ u32 r127_u; /* 148 etherStatsPkts65to127Octets */
+ u32 r127_l; /* 14c etherStatsPkts65to127Octets */
+ u32 r255_u; /* 150 etherStatsPkts128to255Octets */
+ u32 r255_l; /* 154 etherStatsPkts128to255Octets */
+ u32 r511_u; /* 158 etherStatsPkts256to511Octets */
+ u32 r511_l; /* 15c etherStatsPkts256to511Octets */
+ u32 r1023_u; /* 160 etherStatsPkts512to1023Octets */
+ u32 r1023_l; /* 164 etherStatsPkts512to1023Octets */
+ u32 r1518_u; /* 168 etherStatsPkts1024to1518Octets */
+ u32 r1518_l; /* 16c etherStatsPkts1024to1518Octets */
+ u32 r1519x_u; /* 170 etherStatsPkts1519toX */
+ u32 r1519x_l; /* 174 etherStatsPkts1519toX */
+ u32 trovr_u; /* 178 etherStatsOversizePkts */
+ u32 trovr_l; /* 17c etherStatsOversizePkts */
+ u32 trjbr_u; /* 180 etherStatsJabbers */
+ u32 trjbr_l; /* 184 etherStatsJabbers */
+ u32 trfrg_u; /* 188 etherStatsFragments */
+ u32 trfrg_l; /* 18C etherStatsFragments */
+ u32 rerr_u; /* 190 if_in_errors */
+ u32 rerr_l; /* 194 if_in_errors */
+};
+
+struct tgec_cfg {
+ bool pause_ignore;
+ bool promiscuous_mode_enable;
+ u16 max_frame_length;
+ u16 pause_quant;
+ u32 tx_ipg_length;
+};
+
+struct fman_mac {
+ /* Pointer to the memory mapped registers. */
+ struct tgec_regs __iomem *regs;
+ /* MAC address of device; */
+ u64 addr;
+ u16 max_speed;
+ void *dev_id; /* device cookie used by the exception cbs */
+ fman_mac_exception_cb *exception_cb;
+ fman_mac_exception_cb *event_cb;
+ /* pointer to driver's global address hash table */
+ struct eth_hash_t *multicast_addr_hash;
+ /* pointer to driver's individual address hash table */
+ struct eth_hash_t *unicast_addr_hash;
+ u8 mac_id;
+ u32 exceptions;
+ struct tgec_cfg *cfg;
+ void *fm;
+ struct fman_rev_info fm_rev_info;
+};
+
+static void set_mac_address(struct tgec_regs __iomem *regs, u8 *adr)
+{
+ u32 tmp0, tmp1;
+
+ tmp0 = (u32)(adr[0] | adr[1] << 8 | adr[2] << 16 | adr[3] << 24);
+ tmp1 = (u32)(adr[4] | adr[5] << 8);
+ iowrite32be(tmp0, &regs->mac_addr_0);
+ iowrite32be(tmp1, &regs->mac_addr_1);
+}
+
+static void set_dflts(struct tgec_cfg *cfg)
+{
+ cfg->promiscuous_mode_enable = false;
+ cfg->pause_ignore = false;
+ cfg->tx_ipg_length = DEFAULT_TX_IPG_LENGTH;
+ cfg->max_frame_length = DEFAULT_MAX_FRAME_LENGTH;
+ cfg->pause_quant = DEFAULT_PAUSE_QUANT;
+}
+
+static int init(struct tgec_regs __iomem *regs, struct tgec_cfg *cfg,
+ u32 exception_mask)
+{
+ u32 tmp;
+
+ /* Config */
+ tmp = CMF_CFG_CRC_FWD;
+ if (cfg->promiscuous_mode_enable)
+ tmp |= CMD_CFG_PROMIS_EN;
+ if (cfg->pause_ignore)
+ tmp |= CMD_CFG_PAUSE_IGNORE;
+ /* Payload length check disable */
+ tmp |= CMD_CFG_NO_LEN_CHK;
+ iowrite32be(tmp, &regs->command_config);
+
+ /* Max Frame Length */
+ iowrite32be((u32)cfg->max_frame_length, &regs->maxfrm);
+ /* Pause Time */
+ iowrite32be(cfg->pause_quant, &regs->pause_quant);
+
+ /* clear all pending events and set-up interrupts */
+ iowrite32be(0xffffffff, &regs->ievent);
+ iowrite32be(ioread32be(&regs->imask) | exception_mask, &regs->imask);
+
+ return 0;
+}
+
+static int check_init_parameters(struct fman_mac *tgec)
+{
+ if (tgec->max_speed < SPEED_10000) {
+ pr_err("10G MAC driver only support 10G speed\n");
+ return -EINVAL;
+ }
+ if (tgec->addr == 0) {
+ pr_err("Ethernet 10G MAC Must have valid MAC Address\n");
+ return -EINVAL;
+ }
+ if (!tgec->exception_cb) {
+ pr_err("uninitialized exception_cb\n");
+ return -EINVAL;
+ }
+ if (!tgec->event_cb) {
+ pr_err("uninitialized event_cb\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int get_exception_flag(enum fman_mac_exceptions exception)
+{
+ u32 bit_mask;
+
+ switch (exception) {
+ case FM_MAC_EX_10G_MDIO_SCAN_EVENT:
+ bit_mask = TGEC_IMASK_MDIO_SCAN_EVENT;
+ break;
+ case FM_MAC_EX_10G_MDIO_CMD_CMPL:
+ bit_mask = TGEC_IMASK_MDIO_CMD_CMPL;
+ break;
+ case FM_MAC_EX_10G_REM_FAULT:
+ bit_mask = TGEC_IMASK_REM_FAULT;
+ break;
+ case FM_MAC_EX_10G_LOC_FAULT:
+ bit_mask = TGEC_IMASK_LOC_FAULT;
+ break;
+ case FM_MAC_EX_10G_TX_ECC_ER:
+ bit_mask = TGEC_IMASK_TX_ECC_ER;
+ break;
+ case FM_MAC_EX_10G_TX_FIFO_UNFL:
+ bit_mask = TGEC_IMASK_TX_FIFO_UNFL;
+ break;
+ case FM_MAC_EX_10G_TX_FIFO_OVFL:
+ bit_mask = TGEC_IMASK_TX_FIFO_OVFL;
+ break;
+ case FM_MAC_EX_10G_TX_ER:
+ bit_mask = TGEC_IMASK_TX_ER;
+ break;
+ case FM_MAC_EX_10G_RX_FIFO_OVFL:
+ bit_mask = TGEC_IMASK_RX_FIFO_OVFL;
+ break;
+ case FM_MAC_EX_10G_RX_ECC_ER:
+ bit_mask = TGEC_IMASK_RX_ECC_ER;
+ break;
+ case FM_MAC_EX_10G_RX_JAB_FRM:
+ bit_mask = TGEC_IMASK_RX_JAB_FRM;
+ break;
+ case FM_MAC_EX_10G_RX_OVRSZ_FRM:
+ bit_mask = TGEC_IMASK_RX_OVRSZ_FRM;
+ break;
+ case FM_MAC_EX_10G_RX_RUNT_FRM:
+ bit_mask = TGEC_IMASK_RX_RUNT_FRM;
+ break;
+ case FM_MAC_EX_10G_RX_FRAG_FRM:
+ bit_mask = TGEC_IMASK_RX_FRAG_FRM;
+ break;
+ case FM_MAC_EX_10G_RX_LEN_ER:
+ bit_mask = TGEC_IMASK_RX_LEN_ER;
+ break;
+ case FM_MAC_EX_10G_RX_CRC_ER:
+ bit_mask = TGEC_IMASK_RX_CRC_ER;
+ break;
+ case FM_MAC_EX_10G_RX_ALIGN_ER:
+ bit_mask = TGEC_IMASK_RX_ALIGN_ER;
+ break;
+ default:
+ bit_mask = 0;
+ break;
+ }
+
+ return bit_mask;
+}
+
+static void tgec_err_exception(void *handle)
+{
+ struct fman_mac *tgec = (struct fman_mac *)handle;
+ struct tgec_regs __iomem *regs = tgec->regs;
+ u32 event;
+
+ /* do not handle MDIO events */
+ event = ioread32be(&regs->ievent) &
+ ~(TGEC_IMASK_MDIO_SCAN_EVENT |
+ TGEC_IMASK_MDIO_CMD_CMPL);
+
+ event &= ioread32be(&regs->imask);
+
+ iowrite32be(event, &regs->ievent);
+
+ if (event & TGEC_IMASK_REM_FAULT)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_REM_FAULT);
+ if (event & TGEC_IMASK_LOC_FAULT)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_LOC_FAULT);
+ if (event & TGEC_IMASK_TX_ECC_ER)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_ECC_ER);
+ if (event & TGEC_IMASK_TX_FIFO_UNFL)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_FIFO_UNFL);
+ if (event & TGEC_IMASK_TX_FIFO_OVFL)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_FIFO_OVFL);
+ if (event & TGEC_IMASK_TX_ER)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_ER);
+ if (event & TGEC_IMASK_RX_FIFO_OVFL)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_FIFO_OVFL);
+ if (event & TGEC_IMASK_RX_ECC_ER)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_ECC_ER);
+ if (event & TGEC_IMASK_RX_JAB_FRM)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_JAB_FRM);
+ if (event & TGEC_IMASK_RX_OVRSZ_FRM)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_OVRSZ_FRM);
+ if (event & TGEC_IMASK_RX_RUNT_FRM)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_RUNT_FRM);
+ if (event & TGEC_IMASK_RX_FRAG_FRM)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_FRAG_FRM);
+ if (event & TGEC_IMASK_RX_LEN_ER)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_LEN_ER);
+ if (event & TGEC_IMASK_RX_CRC_ER)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_CRC_ER);
+ if (event & TGEC_IMASK_RX_ALIGN_ER)
+ tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_ALIGN_ER);
+}
+
+static void free_init_resources(struct fman_mac *tgec)
+{
+ fman_unregister_intr(tgec->fm, FMAN_MOD_MAC, tgec->mac_id,
+ FMAN_INTR_TYPE_ERR);
+
+ /* release the driver's group hash table */
+ free_hash_table(tgec->multicast_addr_hash);
+ tgec->multicast_addr_hash = NULL;
+
+ /* release the driver's individual hash table */
+ free_hash_table(tgec->unicast_addr_hash);
+ tgec->unicast_addr_hash = NULL;
+}
+
+static bool is_init_done(struct tgec_cfg *cfg)
+{
+ /* Checks if tGEC driver parameters were initialized */
+ if (!cfg)
+ return true;
+
+ return false;
+}
+
+int tgec_enable(struct fman_mac *tgec, enum comm_mode mode)
+{
+ struct tgec_regs __iomem *regs = tgec->regs;
+ u32 tmp;
+
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->command_config);
+ if (mode & COMM_MODE_RX)
+ tmp |= CMD_CFG_RX_EN;
+ if (mode & COMM_MODE_TX)
+ tmp |= CMD_CFG_TX_EN;
+ iowrite32be(tmp, &regs->command_config);
+
+ return 0;
+}
+
+int tgec_disable(struct fman_mac *tgec, enum comm_mode mode)
+{
+ struct tgec_regs __iomem *regs = tgec->regs;
+ u32 tmp;
+
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->command_config);
+ if (mode & COMM_MODE_RX)
+ tmp &= ~CMD_CFG_RX_EN;
+ if (mode & COMM_MODE_TX)
+ tmp &= ~CMD_CFG_TX_EN;
+ iowrite32be(tmp, &regs->command_config);
+
+ return 0;
+}
+
+int tgec_set_promiscuous(struct fman_mac *tgec, bool new_val)
+{
+ struct tgec_regs __iomem *regs = tgec->regs;
+ u32 tmp;
+
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->command_config);
+ if (new_val)
+ tmp |= CMD_CFG_PROMIS_EN;
+ else
+ tmp &= ~CMD_CFG_PROMIS_EN;
+ iowrite32be(tmp, &regs->command_config);
+
+ return 0;
+}
+
+int tgec_cfg_max_frame_len(struct fman_mac *tgec, u16 new_val)
+{
+ if (is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ tgec->cfg->max_frame_length = new_val;
+
+ return 0;
+}
+
+int tgec_set_tx_pause_frames(struct fman_mac *tgec, u8 __maybe_unused priority,
+ u16 pause_time, u16 __maybe_unused thresh_time)
+{
+ struct tgec_regs __iomem *regs = tgec->regs;
+
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ iowrite32be((u32)pause_time, &regs->pause_quant);
+
+ return 0;
+}
+
+int tgec_accept_rx_pause_frames(struct fman_mac *tgec, bool en)
+{
+ struct tgec_regs __iomem *regs = tgec->regs;
+ u32 tmp;
+
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ tmp = ioread32be(&regs->command_config);
+ if (!en)
+ tmp |= CMD_CFG_PAUSE_IGNORE;
+ else
+ tmp &= ~CMD_CFG_PAUSE_IGNORE;
+ iowrite32be(tmp, &regs->command_config);
+
+ return 0;
+}
+
+int tgec_modify_mac_address(struct fman_mac *tgec, enet_addr_t *p_enet_addr)
+{
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ tgec->addr = ENET_ADDR_TO_UINT64(*p_enet_addr);
+ set_mac_address(tgec->regs, (u8 *)(*p_enet_addr));
+
+ return 0;
+}
+
+int tgec_add_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr)
+{
+ struct tgec_regs __iomem *regs = tgec->regs;
+ struct eth_hash_entry *hash_entry;
+ u32 crc = 0xFFFFFFFF, hash;
+ u64 addr;
+
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ addr = ENET_ADDR_TO_UINT64(*eth_addr);
+
+ if (!(addr & GROUP_ADDRESS)) {
+ /* Unicast addresses not supported in hash */
+ pr_err("Unicast Address\n");
+ return -EINVAL;
+ }
+ /* CRC calculation */
+ crc = crc32_le(crc, (u8 *)eth_addr, ETH_ALEN);
+ crc = bitrev32(crc);
+ /* Take 9 MSB bits */
+ hash = (crc >> TGEC_HASH_MCAST_SHIFT) & TGEC_HASH_ADR_MSK;
+
+ /* Create element to be added to the driver hash table */
+ hash_entry = kmalloc(sizeof(*hash_entry), GFP_KERNEL);
+ if (!hash_entry)
+ return -ENOMEM;
+ hash_entry->addr = addr;
+ INIT_LIST_HEAD(&hash_entry->node);
+
+ list_add_tail(&hash_entry->node,
+ &tgec->multicast_addr_hash->lsts[hash]);
+ iowrite32be((hash | TGEC_HASH_MCAST_EN), &regs->hashtable_ctrl);
+
+ return 0;
+}
+
+int tgec_del_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr)
+{
+ struct tgec_regs __iomem *regs = tgec->regs;
+ struct eth_hash_entry *hash_entry = NULL;
+ struct list_head *pos;
+ u32 crc = 0xFFFFFFFF, hash;
+ u64 addr;
+
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ addr = ((*(u64 *)eth_addr) >> 16);
+
+ /* CRC calculation */
+ crc = crc32_le(crc, (u8 *)eth_addr, ETH_ALEN);
+ crc = bitrev32(crc);
+ /* Take 9 MSB bits */
+ hash = (crc >> TGEC_HASH_MCAST_SHIFT) & TGEC_HASH_ADR_MSK;
+
+ list_for_each(pos, &tgec->multicast_addr_hash->lsts[hash]) {
+ hash_entry = ETH_HASH_ENTRY_OBJ(pos);
+ if (hash_entry->addr == addr) {
+ list_del_init(&hash_entry->node);
+ kfree(hash_entry);
+ break;
+ }
+ }
+ if (list_empty(&tgec->multicast_addr_hash->lsts[hash]))
+ iowrite32be((hash & ~TGEC_HASH_MCAST_EN),
+ &regs->hashtable_ctrl);
+
+ return 0;
+}
+
+int tgec_get_version(struct fman_mac *tgec, u32 *mac_version)
+{
+ struct tgec_regs __iomem *regs = tgec->regs;
+
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ *mac_version = ioread32be(&regs->tgec_id);
+
+ return 0;
+}
+
+int tgec_set_exception(struct fman_mac *tgec,
+ enum fman_mac_exceptions exception, bool enable)
+{
+ struct tgec_regs __iomem *regs = tgec->regs;
+ u32 bit_mask = 0;
+
+ if (!is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ bit_mask = get_exception_flag(exception);
+ if (bit_mask) {
+ if (enable)
+ tgec->exceptions |= bit_mask;
+ else
+ tgec->exceptions &= ~bit_mask;
+ } else {
+ pr_err("Undefined exception\n");
+ return -EINVAL;
+ }
+ if (enable)
+ iowrite32be(ioread32be(&regs->imask) | bit_mask, &regs->imask);
+ else
+ iowrite32be(ioread32be(&regs->imask) & ~bit_mask, &regs->imask);
+
+ return 0;
+}
+
+int tgec_init(struct fman_mac *tgec)
+{
+ struct tgec_cfg *cfg;
+ enet_addr_t eth_addr;
+ int err;
+
+ if (is_init_done(tgec->cfg))
+ return -EINVAL;
+
+ if (DEFAULT_RESET_ON_INIT &&
+ (fman_reset_mac(tgec->fm, tgec->mac_id) != 0)) {
+ pr_err("Can't reset MAC!\n");
+ return -EINVAL;
+ }
+
+ err = check_init_parameters(tgec);
+ if (err)
+ return err;
+
+ cfg = tgec->cfg;
+
+ MAKE_ENET_ADDR_FROM_UINT64(tgec->addr, eth_addr);
+ set_mac_address(tgec->regs, (u8 *)eth_addr);
+
+ /* interrupts */
+ /* FM_10G_REM_N_LCL_FLT_EX_10GMAC_ERRATA_SW005 Errata workaround */
+ if (tgec->fm_rev_info.major <= 2)
+ tgec->exceptions &= ~(TGEC_IMASK_REM_FAULT |
+ TGEC_IMASK_LOC_FAULT);
+
+ err = init(tgec->regs, cfg, tgec->exceptions);
+ if (err) {
+ free_init_resources(tgec);
+ pr_err("TGEC version doesn't support this i/f mode\n");
+ return err;
+ }
+
+ /* Max Frame Length */
+ err = fman_set_mac_max_frame(tgec->fm, tgec->mac_id,
+ cfg->max_frame_length);
+ if (err) {
+ pr_err("Setting max frame length FAILED\n");
+ free_init_resources(tgec);
+ return -EINVAL;
+ }
+
+ /* FM_TX_FIFO_CORRUPTION_ERRATA_10GMAC_A007 Errata workaround */
+ if (tgec->fm_rev_info.major == 2) {
+ struct tgec_regs __iomem *regs = tgec->regs;
+ u32 tmp;
+
+ /* restore the default tx ipg Length */
+ tmp = (ioread32be(&regs->tx_ipg_len) &
+ ~TGEC_TX_IPG_LENGTH_MASK) | 12;
+
+ iowrite32be(tmp, &regs->tx_ipg_len);
+ }
+
+ tgec->multicast_addr_hash = alloc_hash_table(TGEC_HASH_TABLE_SIZE);
+ if (!tgec->multicast_addr_hash) {
+ free_init_resources(tgec);
+ pr_err("allocation hash table is FAILED\n");
+ return -ENOMEM;
+ }
+
+ tgec->unicast_addr_hash = alloc_hash_table(TGEC_HASH_TABLE_SIZE);
+ if (!tgec->unicast_addr_hash) {
+ free_init_resources(tgec);
+ pr_err("allocation hash table is FAILED\n");
+ return -ENOMEM;
+ }
+
+ fman_register_intr(tgec->fm, FMAN_MOD_MAC, tgec->mac_id,
+ FMAN_INTR_TYPE_ERR, tgec_err_exception, tgec);
+
+ kfree(cfg);
+ tgec->cfg = NULL;
+
+ return 0;
+}
+
+int tgec_free(struct fman_mac *tgec)
+{
+ free_init_resources(tgec);
+
+ if (tgec->cfg)
+ tgec->cfg = NULL;
+
+ kfree(tgec->cfg);
+ kfree(tgec);
+
+ return 0;
+}
+
+struct fman_mac *tgec_config(struct fman_mac_params *params)
+{
+ struct fman_mac *tgec;
+ struct tgec_cfg *cfg;
+ void __iomem *base_addr;
+
+ base_addr = params->base_addr;
+ /* allocate memory for the UCC GETH data structure. */
+ tgec = kzalloc(sizeof(*tgec), GFP_KERNEL);
+ if (!tgec)
+ return NULL;
+
+ /* allocate memory for the 10G MAC driver parameters data structure. */
+ cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+ if (!cfg) {
+ tgec_free(tgec);
+ return NULL;
+ }
+
+ /* Plant parameter structure pointer */
+ tgec->cfg = cfg;
+
+ set_dflts(cfg);
+
+ tgec->regs = base_addr;
+ tgec->addr = ENET_ADDR_TO_UINT64(params->addr);
+ tgec->max_speed = params->max_speed;
+ tgec->mac_id = params->mac_id;
+ tgec->exceptions = (TGEC_IMASK_MDIO_SCAN_EVENT |
+ TGEC_IMASK_REM_FAULT |
+ TGEC_IMASK_LOC_FAULT |
+ TGEC_IMASK_TX_ECC_ER |
+ TGEC_IMASK_TX_FIFO_UNFL |
+ TGEC_IMASK_TX_FIFO_OVFL |
+ TGEC_IMASK_TX_ER |
+ TGEC_IMASK_RX_FIFO_OVFL |
+ TGEC_IMASK_RX_ECC_ER |
+ TGEC_IMASK_RX_JAB_FRM |
+ TGEC_IMASK_RX_OVRSZ_FRM |
+ TGEC_IMASK_RX_RUNT_FRM |
+ TGEC_IMASK_RX_FRAG_FRM |
+ TGEC_IMASK_RX_CRC_ER |
+ TGEC_IMASK_RX_ALIGN_ER);
+ tgec->exception_cb = params->exception_cb;
+ tgec->event_cb = params->event_cb;
+ tgec->dev_id = params->dev_id;
+ tgec->fm = params->fm;
+
+ /* Save FMan revision */
+ fman_get_revision(tgec->fm, &tgec->fm_rev_info);
+
+ return tgec;
+}
diff --git a/drivers/net/ethernet/freescale/fman/fman_tgec.h b/drivers/net/ethernet/freescale/fman/fman_tgec.h
new file mode 100644
index 000000000000..514bba9f47ce
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/fman_tgec.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2008-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __TGEC_H
+#define __TGEC_H
+
+#include "fman_mac.h"
+
+struct fman_mac *tgec_config(struct fman_mac_params *params);
+int tgec_set_promiscuous(struct fman_mac *tgec, bool new_val);
+int tgec_modify_mac_address(struct fman_mac *tgec, enet_addr_t *enet_addr);
+int tgec_cfg_max_frame_len(struct fman_mac *tgec, u16 new_val);
+int tgec_enable(struct fman_mac *tgec, enum comm_mode mode);
+int tgec_disable(struct fman_mac *tgec, enum comm_mode mode);
+int tgec_init(struct fman_mac *tgec);
+int tgec_free(struct fman_mac *tgec);
+int tgec_accept_rx_pause_frames(struct fman_mac *tgec, bool en);
+int tgec_set_tx_pause_frames(struct fman_mac *tgec, u8 priority,
+ u16 pause_time, u16 thresh_time);
+int tgec_set_exception(struct fman_mac *tgec,
+ enum fman_mac_exceptions exception, bool enable);
+int tgec_add_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr);
+int tgec_del_hash_mac_address(struct fman_mac *tgec, enet_addr_t *eth_addr);
+int tgec_get_version(struct fman_mac *tgec, u32 *mac_version);
+
+#endif /* __TGEC_H */
diff --git a/drivers/net/ethernet/freescale/fman/mac.c b/drivers/net/ethernet/freescale/fman/mac.c
new file mode 100644
index 000000000000..e33d9d24c1db
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/mac.c
@@ -0,0 +1,977 @@
+/* Copyright 2008-2015 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
+#include <linux/device.h>
+#include <linux/phy.h>
+#include <linux/netdevice.h>
+#include <linux/phy_fixed.h>
+#include <linux/etherdevice.h>
+#include <linux/libfdt_env.h>
+
+#include "mac.h"
+#include "fman_mac.h"
+#include "fman_dtsec.h"
+#include "fman_tgec.h"
+#include "fman_memac.h"
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("FSL FMan MAC API based driver");
+
+struct mac_priv_s {
+ struct device *dev;
+ void __iomem *vaddr;
+ u8 cell_index;
+ phy_interface_t phy_if;
+ struct fman *fman;
+ struct device_node *phy_node;
+ struct device_node *internal_phy_node;
+ /* List of multicast addresses */
+ struct list_head mc_addr_list;
+ struct platform_device *eth_dev;
+ struct fixed_phy_status *fixed_link;
+ u16 speed;
+ u16 max_speed;
+
+ int (*enable)(struct fman_mac *mac_dev, enum comm_mode mode);
+ int (*disable)(struct fman_mac *mac_dev, enum comm_mode mode);
+};
+
+struct mac_address {
+ u8 addr[ETH_ALEN];
+ struct list_head list;
+};
+
+static void mac_exception(void *handle, enum fman_mac_exceptions ex)
+{
+ struct mac_device *mac_dev;
+ struct mac_priv_s *priv;
+
+ mac_dev = handle;
+ priv = mac_dev->priv;
+
+ if (ex == FM_MAC_EX_10G_RX_FIFO_OVFL) {
+ /* don't flag RX FIFO after the first */
+ mac_dev->set_exception(mac_dev->fman_mac,
+ FM_MAC_EX_10G_RX_FIFO_OVFL, false);
+ dev_err(priv->dev, "10G MAC got RX FIFO Error = %x\n", ex);
+ }
+
+ dev_dbg(priv->dev, "%s:%s() -> %d\n", KBUILD_BASENAME ".c",
+ __func__, ex);
+}
+
+static void set_fman_mac_params(struct mac_device *mac_dev,
+ struct fman_mac_params *params)
+{
+ struct mac_priv_s *priv = mac_dev->priv;
+
+ params->base_addr = (typeof(params->base_addr))
+ devm_ioremap(priv->dev, mac_dev->res->start,
+ resource_size(mac_dev->res));
+ memcpy(&params->addr, mac_dev->addr, sizeof(mac_dev->addr));
+ params->max_speed = priv->max_speed;
+ params->phy_if = priv->phy_if;
+ params->basex_if = false;
+ params->mac_id = priv->cell_index;
+ params->fm = (void *)priv->fman;
+ params->exception_cb = mac_exception;
+ params->event_cb = mac_exception;
+ params->dev_id = mac_dev;
+ params->internal_phy_node = priv->internal_phy_node;
+}
+
+static int tgec_initialization(struct mac_device *mac_dev)
+{
+ int err;
+ struct mac_priv_s *priv;
+ struct fman_mac_params params;
+ u32 version;
+
+ priv = mac_dev->priv;
+
+ set_fman_mac_params(mac_dev, &params);
+
+ mac_dev->fman_mac = tgec_config(&params);
+ if (!mac_dev->fman_mac) {
+ err = -EINVAL;
+ goto _return;
+ }
+
+ err = tgec_cfg_max_frame_len(mac_dev->fman_mac, fman_get_max_frm());
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ err = tgec_init(mac_dev->fman_mac);
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ /* For 10G MAC, disable Tx ECC exception */
+ err = mac_dev->set_exception(mac_dev->fman_mac,
+ FM_MAC_EX_10G_TX_ECC_ER, false);
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ err = tgec_get_version(mac_dev->fman_mac, &version);
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ dev_info(priv->dev, "FMan XGEC version: 0x%08x\n", version);
+
+ goto _return;
+
+_return_fm_mac_free:
+ tgec_free(mac_dev->fman_mac);
+
+_return:
+ return err;
+}
+
+static int dtsec_initialization(struct mac_device *mac_dev)
+{
+ int err;
+ struct mac_priv_s *priv;
+ struct fman_mac_params params;
+ u32 version;
+
+ priv = mac_dev->priv;
+
+ set_fman_mac_params(mac_dev, &params);
+
+ mac_dev->fman_mac = dtsec_config(&params);
+ if (!mac_dev->fman_mac) {
+ err = -EINVAL;
+ goto _return;
+ }
+
+ err = dtsec_cfg_max_frame_len(mac_dev->fman_mac, fman_get_max_frm());
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ err = dtsec_cfg_pad_and_crc(mac_dev->fman_mac, true);
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ err = dtsec_init(mac_dev->fman_mac);
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ /* For 1G MAC, disable by default the MIB counters overflow interrupt */
+ err = mac_dev->set_exception(mac_dev->fman_mac,
+ FM_MAC_EX_1G_RX_MIB_CNT_OVFL, false);
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ err = dtsec_get_version(mac_dev->fman_mac, &version);
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ dev_info(priv->dev, "FMan dTSEC version: 0x%08x\n", version);
+
+ goto _return;
+
+_return_fm_mac_free:
+ dtsec_free(mac_dev->fman_mac);
+
+_return:
+ return err;
+}
+
+static int memac_initialization(struct mac_device *mac_dev)
+{
+ int err;
+ struct mac_priv_s *priv;
+ struct fman_mac_params params;
+
+ priv = mac_dev->priv;
+
+ set_fman_mac_params(mac_dev, &params);
+
+ if (priv->max_speed == SPEED_10000)
+ params.phy_if = PHY_INTERFACE_MODE_XGMII;
+
+ mac_dev->fman_mac = memac_config(&params);
+ if (!mac_dev->fman_mac) {
+ err = -EINVAL;
+ goto _return;
+ }
+
+ err = memac_cfg_max_frame_len(mac_dev->fman_mac, fman_get_max_frm());
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ err = memac_cfg_reset_on_init(mac_dev->fman_mac, true);
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ err = memac_cfg_fixed_link(mac_dev->fman_mac, priv->fixed_link);
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ err = memac_init(mac_dev->fman_mac);
+ if (err < 0)
+ goto _return_fm_mac_free;
+
+ dev_info(priv->dev, "FMan MEMAC\n");
+
+ goto _return;
+
+_return_fm_mac_free:
+ memac_free(mac_dev->fman_mac);
+
+_return:
+ return err;
+}
+
+static int start(struct mac_device *mac_dev)
+{
+ int err;
+ struct phy_device *phy_dev = mac_dev->phy_dev;
+ struct mac_priv_s *priv = mac_dev->priv;
+
+ err = priv->enable(mac_dev->fman_mac, COMM_MODE_RX_AND_TX);
+ if (!err && phy_dev)
+ phy_start(phy_dev);
+
+ return err;
+}
+
+static int stop(struct mac_device *mac_dev)
+{
+ struct mac_priv_s *priv = mac_dev->priv;
+
+ if (mac_dev->phy_dev)
+ phy_stop(mac_dev->phy_dev);
+
+ return priv->disable(mac_dev->fman_mac, COMM_MODE_RX_AND_TX);
+}
+
+static int set_multi(struct net_device *net_dev, struct mac_device *mac_dev)
+{
+ struct mac_priv_s *priv;
+ struct mac_address *old_addr, *tmp;
+ struct netdev_hw_addr *ha;
+ int err;
+ enet_addr_t *addr;
+
+ priv = mac_dev->priv;
+
+ /* Clear previous address list */
+ list_for_each_entry_safe(old_addr, tmp, &priv->mc_addr_list, list) {
+ addr = (enet_addr_t *)old_addr->addr;
+ err = mac_dev->remove_hash_mac_addr(mac_dev->fman_mac, addr);
+ if (err < 0)
+ return err;
+
+ list_del(&old_addr->list);
+ kfree(old_addr);
+ }
+
+ /* Add all the addresses from the new list */
+ netdev_for_each_mc_addr(ha, net_dev) {
+ addr = (enet_addr_t *)ha->addr;
+ err = mac_dev->add_hash_mac_addr(mac_dev->fman_mac, addr);
+ if (err < 0)
+ return err;
+
+ tmp = kmalloc(sizeof(*tmp), GFP_ATOMIC);
+ if (!tmp)
+ return -ENOMEM;
+
+ ether_addr_copy(tmp->addr, ha->addr);
+ list_add(&tmp->list, &priv->mc_addr_list);
+ }
+ return 0;
+}
+
+/**
+ * fman_set_mac_active_pause
+ * @mac_dev: A pointer to the MAC device
+ * @rx: Pause frame setting for RX
+ * @tx: Pause frame setting for TX
+ *
+ * Set the MAC RX/TX PAUSE frames settings
+ *
+ * Avoid redundant calls to FMD, if the MAC driver already contains the desired
+ * active PAUSE settings. Otherwise, the new active settings should be reflected
+ * in FMan.
+ *
+ * Return: 0 on success; Error code otherwise.
+ */
+int fman_set_mac_active_pause(struct mac_device *mac_dev, bool rx, bool tx)
+{
+ struct fman_mac *fman_mac = mac_dev->fman_mac;
+ int err = 0;
+
+ if (rx != mac_dev->rx_pause_active) {
+ err = mac_dev->set_rx_pause(fman_mac, rx);
+ if (likely(err == 0))
+ mac_dev->rx_pause_active = rx;
+ }
+
+ if (tx != mac_dev->tx_pause_active) {
+ u16 pause_time = (tx ? FSL_FM_PAUSE_TIME_ENABLE :
+ FSL_FM_PAUSE_TIME_DISABLE);
+
+ err = mac_dev->set_tx_pause(fman_mac, 0, pause_time, 0);
+
+ if (likely(err == 0))
+ mac_dev->tx_pause_active = tx;
+ }
+
+ return err;
+}
+EXPORT_SYMBOL(fman_set_mac_active_pause);
+
+/**
+ * fman_get_pause_cfg
+ * @mac_dev: A pointer to the MAC device
+ * @rx: Return value for RX setting
+ * @tx: Return value for TX setting
+ *
+ * Determine the MAC RX/TX PAUSE frames settings based on PHY
+ * autonegotiation or values set by eththool.
+ *
+ * Return: Pointer to FMan device.
+ */
+void fman_get_pause_cfg(struct mac_device *mac_dev, bool *rx_pause,
+ bool *tx_pause)
+{
+ struct phy_device *phy_dev = mac_dev->phy_dev;
+ u16 lcl_adv, rmt_adv;
+ u8 flowctrl;
+
+ *rx_pause = *tx_pause = false;
+
+ if (!phy_dev->duplex)
+ return;
+
+ /* If PAUSE autonegotiation is disabled, the TX/RX PAUSE settings
+ * are those set by ethtool.
+ */
+ if (!mac_dev->autoneg_pause) {
+ *rx_pause = mac_dev->rx_pause_req;
+ *tx_pause = mac_dev->tx_pause_req;
+ return;
+ }
+
+ /* Else if PAUSE autonegotiation is enabled, the TX/RX PAUSE
+ * settings depend on the result of the link negotiation.
+ */
+
+ /* get local capabilities */
+ lcl_adv = 0;
+ if (phy_dev->advertising & ADVERTISED_Pause)
+ lcl_adv |= ADVERTISE_PAUSE_CAP;
+ if (phy_dev->advertising & ADVERTISED_Asym_Pause)
+ lcl_adv |= ADVERTISE_PAUSE_ASYM;
+
+ /* get link partner capabilities */
+ rmt_adv = 0;
+ if (phy_dev->pause)
+ rmt_adv |= LPA_PAUSE_CAP;
+ if (phy_dev->asym_pause)
+ rmt_adv |= LPA_PAUSE_ASYM;
+
+ /* Calculate TX/RX settings based on local and peer advertised
+ * symmetric/asymmetric PAUSE capabilities.
+ */
+ flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
+ if (flowctrl & FLOW_CTRL_RX)
+ *rx_pause = true;
+ if (flowctrl & FLOW_CTRL_TX)
+ *tx_pause = true;
+}
+EXPORT_SYMBOL(fman_get_pause_cfg);
+
+static void adjust_link_void(struct net_device *net_dev)
+{
+}
+
+static void adjust_link_dtsec(struct net_device *net_dev)
+{
+ struct device *dev = net_dev->dev.parent;
+ struct dpaa_eth_data *eth_data = dev->platform_data;
+ struct mac_device *mac_dev = eth_data->mac_dev;
+ struct phy_device *phy_dev = mac_dev->phy_dev;
+ struct fman_mac *fman_mac;
+ bool rx_pause, tx_pause;
+ int err;
+
+ fman_mac = mac_dev->fman_mac;
+ if (!phy_dev->link) {
+ dtsec_restart_autoneg(fman_mac);
+
+ return;
+ }
+
+ dtsec_adjust_link(fman_mac, phy_dev->speed);
+ fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause);
+ err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause);
+ if (err < 0)
+ netdev_err(net_dev, "fman_set_mac_active_pause() = %d\n", err);
+}
+
+static void adjust_link_memac(struct net_device *net_dev)
+{
+ struct device *dev = net_dev->dev.parent;
+ struct dpaa_eth_data *eth_data = dev->platform_data;
+ struct mac_device *mac_dev = eth_data->mac_dev;
+ struct phy_device *phy_dev = mac_dev->phy_dev;
+ struct fman_mac *fman_mac;
+ bool rx_pause, tx_pause;
+ int err;
+
+ fman_mac = mac_dev->fman_mac;
+ memac_adjust_link(fman_mac, phy_dev->speed);
+
+ fman_get_pause_cfg(mac_dev, &rx_pause, &tx_pause);
+ err = fman_set_mac_active_pause(mac_dev, rx_pause, tx_pause);
+ if (err < 0)
+ netdev_err(net_dev, "fman_set_mac_active_pause() = %d\n", err);
+}
+
+/* Initializes driver's PHY state, and attaches to the PHY.
+ * Returns 0 on success.
+ */
+static int init_phy(struct net_device *net_dev,
+ struct mac_device *mac_dev,
+ void (*adj_lnk)(struct net_device *))
+{
+ struct phy_device *phy_dev;
+ struct mac_priv_s *priv = mac_dev->priv;
+
+ phy_dev = of_phy_connect(net_dev, priv->phy_node, adj_lnk, 0,
+ priv->phy_if);
+ if (!phy_dev) {
+ netdev_err(net_dev, "Could not connect to PHY\n");
+ return -ENODEV;
+ }
+
+ /* Remove any features not supported by the controller */
+ phy_dev->supported &= mac_dev->if_support;
+ /* Enable the symmetric and asymmetric PAUSE frame advertisements,
+ * as most of the PHY drivers do not enable them by default.
+ */
+ phy_dev->supported |= (SUPPORTED_Pause | SUPPORTED_Asym_Pause);
+ phy_dev->advertising = phy_dev->supported;
+
+ mac_dev->phy_dev = phy_dev;
+
+ return 0;
+}
+
+static int dtsec_init_phy(struct net_device *net_dev,
+ struct mac_device *mac_dev)
+{
+ return init_phy(net_dev, mac_dev, &adjust_link_dtsec);
+}
+
+static int tgec_init_phy(struct net_device *net_dev,
+ struct mac_device *mac_dev)
+{
+ return init_phy(net_dev, mac_dev, adjust_link_void);
+}
+
+static int memac_init_phy(struct net_device *net_dev,
+ struct mac_device *mac_dev)
+{
+ return init_phy(net_dev, mac_dev, &adjust_link_memac);
+}
+
+static void setup_dtsec(struct mac_device *mac_dev)
+{
+ mac_dev->init_phy = dtsec_init_phy;
+ mac_dev->init = dtsec_initialization;
+ mac_dev->set_promisc = dtsec_set_promiscuous;
+ mac_dev->change_addr = dtsec_modify_mac_address;
+ mac_dev->add_hash_mac_addr = dtsec_add_hash_mac_address;
+ mac_dev->remove_hash_mac_addr = dtsec_del_hash_mac_address;
+ mac_dev->set_tx_pause = dtsec_set_tx_pause_frames;
+ mac_dev->set_rx_pause = dtsec_accept_rx_pause_frames;
+ mac_dev->set_exception = dtsec_set_exception;
+ mac_dev->set_multi = set_multi;
+ mac_dev->start = start;
+ mac_dev->stop = stop;
+
+ mac_dev->priv->enable = dtsec_enable;
+ mac_dev->priv->disable = dtsec_disable;
+}
+
+static void setup_tgec(struct mac_device *mac_dev)
+{
+ mac_dev->init_phy = tgec_init_phy;
+ mac_dev->init = tgec_initialization;
+ mac_dev->set_promisc = tgec_set_promiscuous;
+ mac_dev->change_addr = tgec_modify_mac_address;
+ mac_dev->add_hash_mac_addr = tgec_add_hash_mac_address;
+ mac_dev->remove_hash_mac_addr = tgec_del_hash_mac_address;
+ mac_dev->set_tx_pause = tgec_set_tx_pause_frames;
+ mac_dev->set_rx_pause = tgec_accept_rx_pause_frames;
+ mac_dev->set_exception = tgec_set_exception;
+ mac_dev->set_multi = set_multi;
+ mac_dev->start = start;
+ mac_dev->stop = stop;
+
+ mac_dev->priv->enable = tgec_enable;
+ mac_dev->priv->disable = tgec_disable;
+}
+
+static void setup_memac(struct mac_device *mac_dev)
+{
+ mac_dev->init_phy = memac_init_phy;
+ mac_dev->init = memac_initialization;
+ mac_dev->set_promisc = memac_set_promiscuous;
+ mac_dev->change_addr = memac_modify_mac_address;
+ mac_dev->add_hash_mac_addr = memac_add_hash_mac_address;
+ mac_dev->remove_hash_mac_addr = memac_del_hash_mac_address;
+ mac_dev->set_tx_pause = memac_set_tx_pause_frames;
+ mac_dev->set_rx_pause = memac_accept_rx_pause_frames;
+ mac_dev->set_exception = memac_set_exception;
+ mac_dev->set_multi = set_multi;
+ mac_dev->start = start;
+ mac_dev->stop = stop;
+
+ mac_dev->priv->enable = memac_enable;
+ mac_dev->priv->disable = memac_disable;
+}
+
+#define DTSEC_SUPPORTED \
+ (SUPPORTED_10baseT_Half \
+ | SUPPORTED_10baseT_Full \
+ | SUPPORTED_100baseT_Half \
+ | SUPPORTED_100baseT_Full \
+ | SUPPORTED_Autoneg \
+ | SUPPORTED_Pause \
+ | SUPPORTED_Asym_Pause \
+ | SUPPORTED_MII)
+
+static DEFINE_MUTEX(eth_lock);
+
+static const char phy_str[][11] = {
+ [PHY_INTERFACE_MODE_MII] = "mii",
+ [PHY_INTERFACE_MODE_GMII] = "gmii",
+ [PHY_INTERFACE_MODE_SGMII] = "sgmii",
+ [PHY_INTERFACE_MODE_TBI] = "tbi",
+ [PHY_INTERFACE_MODE_RMII] = "rmii",
+ [PHY_INTERFACE_MODE_RGMII] = "rgmii",
+ [PHY_INTERFACE_MODE_RGMII_ID] = "rgmii-id",
+ [PHY_INTERFACE_MODE_RGMII_RXID] = "rgmii-rxid",
+ [PHY_INTERFACE_MODE_RGMII_TXID] = "rgmii-txid",
+ [PHY_INTERFACE_MODE_RTBI] = "rtbi",
+ [PHY_INTERFACE_MODE_XGMII] = "xgmii"
+};
+
+static phy_interface_t __pure __attribute__((nonnull)) str2phy(const char *str)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(phy_str); i++)
+ if (strcmp(str, phy_str[i]) == 0)
+ return (phy_interface_t)i;
+
+ return PHY_INTERFACE_MODE_MII;
+}
+
+static const u16 phy2speed[] = {
+ [PHY_INTERFACE_MODE_MII] = SPEED_100,
+ [PHY_INTERFACE_MODE_GMII] = SPEED_1000,
+ [PHY_INTERFACE_MODE_SGMII] = SPEED_1000,
+ [PHY_INTERFACE_MODE_TBI] = SPEED_1000,
+ [PHY_INTERFACE_MODE_RMII] = SPEED_100,
+ [PHY_INTERFACE_MODE_RGMII] = SPEED_1000,
+ [PHY_INTERFACE_MODE_RGMII_ID] = SPEED_1000,
+ [PHY_INTERFACE_MODE_RGMII_RXID] = SPEED_1000,
+ [PHY_INTERFACE_MODE_RGMII_TXID] = SPEED_1000,
+ [PHY_INTERFACE_MODE_RTBI] = SPEED_1000,
+ [PHY_INTERFACE_MODE_XGMII] = SPEED_10000
+};
+
+static struct platform_device *dpaa_eth_add_device(int fman_id,
+ struct mac_device *mac_dev,
+ struct device_node *node)
+{
+ struct platform_device *pdev;
+ struct dpaa_eth_data data;
+ struct mac_priv_s *priv;
+ static int dpaa_eth_dev_cnt;
+ int ret;
+
+ priv = mac_dev->priv;
+
+ data.mac_dev = mac_dev;
+ data.mac_hw_id = priv->cell_index;
+ data.fman_hw_id = fman_id;
+ data.mac_node = node;
+
+ mutex_lock(&eth_lock);
+
+ pdev = platform_device_alloc("dpaa-ethernet", dpaa_eth_dev_cnt);
+ if (!pdev) {
+ ret = -ENOMEM;
+ goto no_mem;
+ }
+
+ ret = platform_device_add_data(pdev, &data, sizeof(data));
+ if (ret)
+ goto err;
+
+ ret = platform_device_add(pdev);
+ if (ret)
+ goto err;
+
+ dpaa_eth_dev_cnt++;
+ mutex_unlock(&eth_lock);
+
+ return pdev;
+
+err:
+ platform_device_put(pdev);
+no_mem:
+ mutex_unlock(&eth_lock);
+
+ return ERR_PTR(ret);
+}
+
+static const struct of_device_id mac_match[] = {
+ { .compatible = "fsl,fman-dtsec" },
+ { .compatible = "fsl,fman-xgec" },
+ { .compatible = "fsl,fman-memac" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mac_match);
+
+static int mac_probe(struct platform_device *_of_dev)
+{
+ int err, i, lenp, nph;
+ struct device *dev;
+ struct device_node *mac_node, *dev_node;
+ struct mac_device *mac_dev;
+ struct platform_device *of_dev;
+ struct resource res;
+ struct mac_priv_s *priv;
+ const u8 *mac_addr;
+ const char *char_prop;
+ const u32 *u32_prop;
+ u8 fman_id;
+
+ dev = &_of_dev->dev;
+ mac_node = dev->of_node;
+
+ mac_dev = devm_kzalloc(dev, sizeof(*mac_dev), GFP_KERNEL);
+ if (!mac_dev) {
+ err = -ENOMEM;
+ dev_err(dev, "devm_kzalloc() = %d\n", err);
+ goto _return;
+ }
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ err = -ENOMEM;
+ goto _return;
+ }
+
+ /* Save private information */
+ mac_dev->priv = priv;
+ priv->dev = dev;
+
+ if (of_device_is_compatible(mac_node, "fsl,fman-dtsec")) {
+ setup_dtsec(mac_dev);
+ priv->internal_phy_node = of_parse_phandle(mac_node,
+ "tbi-handle", 0);
+ } else if (of_device_is_compatible(mac_node, "fsl,fman-xgec")) {
+ setup_tgec(mac_dev);
+ } else if (of_device_is_compatible(mac_node, "fsl,fman-memac")) {
+ setup_memac(mac_dev);
+ priv->internal_phy_node = of_parse_phandle(mac_node,
+ "pcsphy-handle", 0);
+ } else {
+ dev_err(dev, "MAC node (%s) contains unsupported MAC\n",
+ mac_node->full_name);
+ err = -EINVAL;
+ goto _return;
+ }
+
+ /* Register mac_dev */
+ dev_set_drvdata(dev, mac_dev);
+
+ INIT_LIST_HEAD(&priv->mc_addr_list);
+
+ /* Get the FM node */
+ dev_node = of_get_parent(mac_node);
+ if (!dev_node) {
+ dev_err(dev, "of_get_parent(%s) failed\n",
+ mac_node->full_name);
+ err = -EINVAL;
+ goto _return_dev_set_drvdata;
+ }
+
+ of_dev = of_find_device_by_node(dev_node);
+ if (!of_dev) {
+ dev_err(dev, "of_find_device_by_node(%s) failed\n",
+ dev_node->full_name);
+ err = -EINVAL;
+ goto _return_of_node_put;
+ }
+
+ /* Get the FMan cell-index */
+ u32_prop = of_get_property(dev_node, "cell-index", &lenp);
+ if (!u32_prop) {
+ dev_err(dev, "of_get_property(%s, cell-index) failed\n",
+ dev_node->full_name);
+ err = -EINVAL;
+ goto _return_of_node_put;
+ }
+ WARN_ON(lenp != sizeof(u32));
+ /* cell-index 0 => FMan id 1 */
+ fman_id = (u8)(fdt32_to_cpu(u32_prop[0]) + 1);
+
+ priv->fman = fman_bind(&of_dev->dev);
+ if (!priv->fman) {
+ dev_err(dev, "fman_bind(%s) failed\n", dev_node->full_name);
+ err = -ENODEV;
+ goto _return_of_node_put;
+ }
+
+ of_node_put(dev_node);
+
+ /* Get the address of the memory mapped registers */
+ err = of_address_to_resource(mac_node, 0, &res);
+ if (err < 0) {
+ dev_err(dev, "of_address_to_resource(%s) = %d\n",
+ mac_node->full_name, err);
+ goto _return_dev_set_drvdata;
+ }
+
+ mac_dev->res = __devm_request_region(dev,
+ fman_get_mem_region(priv->fman),
+ res.start, res.end + 1 - res.start,
+ "mac");
+ if (!mac_dev->res) {
+ dev_err(dev, "__devm_request_mem_region(mac) failed\n");
+ err = -EBUSY;
+ goto _return_dev_set_drvdata;
+ }
+
+ priv->vaddr = devm_ioremap(dev, mac_dev->res->start,
+ mac_dev->res->end + 1 - mac_dev->res->start);
+ if (!priv->vaddr) {
+ dev_err(dev, "devm_ioremap() failed\n");
+ err = -EIO;
+ goto _return_dev_set_drvdata;
+ }
+
+ if (!of_device_is_available(mac_node)) {
+ devm_iounmap(dev, priv->vaddr);
+ __devm_release_region(dev, fman_get_mem_region(priv->fman),
+ res.start, res.end + 1 - res.start);
+ devm_kfree(dev, mac_dev);
+ dev_set_drvdata(dev, NULL);
+ return -ENODEV;
+ }
+
+ /* Get the cell-index */
+ u32_prop = of_get_property(mac_node, "cell-index", &lenp);
+ if (!u32_prop) {
+ dev_err(dev, "of_get_property(%s, cell-index) failed\n",
+ mac_node->full_name);
+ err = -EINVAL;
+ goto _return_dev_set_drvdata;
+ }
+ WARN_ON(lenp != sizeof(u32));
+ priv->cell_index = (u8)fdt32_to_cpu(u32_prop[0]);
+
+ /* Get the MAC address */
+ mac_addr = of_get_mac_address(mac_node);
+ if (!mac_addr) {
+ dev_err(dev, "of_get_mac_address(%s) failed\n",
+ mac_node->full_name);
+ err = -EINVAL;
+ goto _return_dev_set_drvdata;
+ }
+ memcpy(mac_dev->addr, mac_addr, sizeof(mac_dev->addr));
+
+ /* Get the port handles */
+ nph = of_count_phandle_with_args(mac_node, "fsl,fman-ports", NULL);
+ if (unlikely(nph < 0)) {
+ dev_err(dev, "of_count_phandle_with_args(%s, fsl,fman-ports) failed\n",
+ mac_node->full_name);
+ err = nph;
+ goto _return_dev_set_drvdata;
+ }
+
+ if (nph != ARRAY_SIZE(mac_dev->port)) {
+ dev_err(dev, "Not supported number of fman-ports handles of mac node %s from device tree\n",
+ mac_node->full_name);
+ err = -EINVAL;
+ goto _return_dev_set_drvdata;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mac_dev->port); i++) {
+ /* Find the port node */
+ dev_node = of_parse_phandle(mac_node, "fsl,fman-ports", i);
+ if (!dev_node) {
+ dev_err(dev, "of_parse_phandle(%s, fsl,fman-ports) failed\n",
+ mac_node->full_name);
+ err = -EINVAL;
+ goto _return_of_node_put;
+ }
+
+ of_dev = of_find_device_by_node(dev_node);
+ if (!of_dev) {
+ dev_err(dev, "of_find_device_by_node(%s) failed\n",
+ dev_node->full_name);
+ err = -EINVAL;
+ goto _return_of_node_put;
+ }
+
+ mac_dev->port[i] = fman_port_bind(&of_dev->dev);
+ if (!mac_dev->port[i]) {
+ dev_err(dev, "dev_get_drvdata(%s) failed\n",
+ dev_node->full_name);
+ err = -EINVAL;
+ goto _return_of_node_put;
+ }
+ of_node_put(dev_node);
+ }
+
+ /* Get the PHY connection type */
+ char_prop = (const char *)of_get_property(mac_node,
+ "phy-connection-type", NULL);
+ if (!char_prop) {
+ dev_warn(dev,
+ "of_get_property(%s, phy-connection-type) failed. Defaulting to MII\n",
+ mac_node->full_name);
+ priv->phy_if = PHY_INTERFACE_MODE_MII;
+ } else {
+ priv->phy_if = str2phy(char_prop);
+ }
+
+ priv->speed = phy2speed[priv->phy_if];
+ priv->max_speed = priv->speed;
+ mac_dev->if_support = DTSEC_SUPPORTED;
+ /* We don't support half-duplex in SGMII mode */
+ if (priv->phy_if == PHY_INTERFACE_MODE_SGMII)
+ mac_dev->if_support &= ~(SUPPORTED_10baseT_Half |
+ SUPPORTED_100baseT_Half);
+
+ /* Gigabit support (no half-duplex) */
+ if (priv->max_speed == 1000)
+ mac_dev->if_support |= SUPPORTED_1000baseT_Full;
+
+ /* The 10G interface only supports one mode */
+ if (priv->phy_if == PHY_INTERFACE_MODE_XGMII)
+ mac_dev->if_support = SUPPORTED_10000baseT_Full;
+
+ /* Get the rest of the PHY information */
+ priv->phy_node = of_parse_phandle(mac_node, "phy-handle", 0);
+ if (!priv->phy_node && of_phy_is_fixed_link(mac_node)) {
+ struct phy_device *phy;
+
+ err = of_phy_register_fixed_link(mac_node);
+ if (err)
+ goto _return_dev_set_drvdata;
+
+ priv->fixed_link = kzalloc(sizeof(*priv->fixed_link),
+ GFP_KERNEL);
+ if (!priv->fixed_link)
+ goto _return_dev_set_drvdata;
+
+ priv->phy_node = of_node_get(mac_node);
+ phy = of_phy_find_device(priv->phy_node);
+ if (!phy)
+ goto _return_dev_set_drvdata;
+
+ priv->fixed_link->link = phy->link;
+ priv->fixed_link->speed = phy->speed;
+ priv->fixed_link->duplex = phy->duplex;
+ priv->fixed_link->pause = phy->pause;
+ priv->fixed_link->asym_pause = phy->asym_pause;
+ }
+
+ err = mac_dev->init(mac_dev);
+ if (err < 0) {
+ dev_err(dev, "mac_dev->init() = %d\n", err);
+ of_node_put(priv->phy_node);
+ goto _return_dev_set_drvdata;
+ }
+
+ /* pause frame autonegotiation enabled */
+ mac_dev->autoneg_pause = true;
+
+ /* By intializing the values to false, force FMD to enable PAUSE frames
+ * on RX and TX
+ */
+ mac_dev->rx_pause_req = true;
+ mac_dev->tx_pause_req = true;
+ mac_dev->rx_pause_active = false;
+ mac_dev->tx_pause_active = false;
+ err = fman_set_mac_active_pause(mac_dev, true, true);
+ if (err < 0)
+ dev_err(dev, "fman_set_mac_active_pause() = %d\n", err);
+
+ dev_info(dev, "FMan MAC address: %02hx:%02hx:%02hx:%02hx:%02hx:%02hx\n",
+ mac_dev->addr[0], mac_dev->addr[1], mac_dev->addr[2],
+ mac_dev->addr[3], mac_dev->addr[4], mac_dev->addr[5]);
+
+ priv->eth_dev = dpaa_eth_add_device(fman_id, mac_dev, mac_node);
+ if (IS_ERR(priv->eth_dev)) {
+ dev_err(dev, "failed to add Ethernet platform device for MAC %d\n",
+ priv->cell_index);
+ priv->eth_dev = NULL;
+ }
+
+ goto _return;
+
+_return_of_node_put:
+ of_node_put(dev_node);
+_return_dev_set_drvdata:
+ kfree(priv->fixed_link);
+ dev_set_drvdata(dev, NULL);
+_return:
+ return err;
+}
+
+static struct platform_driver mac_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = mac_match,
+ },
+ .probe = mac_probe,
+};
+
+builtin_platform_driver(mac_driver);
diff --git a/drivers/net/ethernet/freescale/fman/mac.h b/drivers/net/ethernet/freescale/fman/mac.h
new file mode 100644
index 000000000000..0211cc9a46d6
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fman/mac.h
@@ -0,0 +1,97 @@
+/* Copyright 2008-2015 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MAC_H
+#define __MAC_H
+
+#include <linux/device.h>
+#include <linux/if_ether.h>
+#include <linux/phy.h>
+#include <linux/list.h>
+
+#include "fman_port.h"
+#include "fman.h"
+#include "fman_mac.h"
+
+struct fman_mac;
+struct mac_priv_s;
+
+struct mac_device {
+ struct resource *res;
+ u8 addr[ETH_ALEN];
+ struct fman_port *port[2];
+ u32 if_support;
+ struct phy_device *phy_dev;
+
+ bool autoneg_pause;
+ bool rx_pause_req;
+ bool tx_pause_req;
+ bool rx_pause_active;
+ bool tx_pause_active;
+ bool promisc;
+
+ int (*init_phy)(struct net_device *net_dev, struct mac_device *mac_dev);
+ int (*init)(struct mac_device *mac_dev);
+ int (*start)(struct mac_device *mac_dev);
+ int (*stop)(struct mac_device *mac_dev);
+ int (*set_promisc)(struct fman_mac *mac_dev, bool enable);
+ int (*change_addr)(struct fman_mac *mac_dev, enet_addr_t *enet_addr);
+ int (*set_multi)(struct net_device *net_dev,
+ struct mac_device *mac_dev);
+ int (*set_rx_pause)(struct fman_mac *mac_dev, bool en);
+ int (*set_tx_pause)(struct fman_mac *mac_dev, u8 priority,
+ u16 pause_time, u16 thresh_time);
+ int (*set_exception)(struct fman_mac *mac_dev,
+ enum fman_mac_exceptions exception, bool enable);
+ int (*add_hash_mac_addr)(struct fman_mac *mac_dev,
+ enet_addr_t *eth_addr);
+ int (*remove_hash_mac_addr)(struct fman_mac *mac_dev,
+ enet_addr_t *eth_addr);
+
+ struct fman_mac *fman_mac;
+ struct mac_priv_s *priv;
+};
+
+struct dpaa_eth_data {
+ struct device_node *mac_node;
+ struct mac_device *mac_dev;
+ int mac_hw_id;
+ int fman_hw_id;
+};
+
+extern const char *mac_driver_description;
+
+int fman_set_mac_active_pause(struct mac_device *mac_dev, bool rx, bool tx);
+
+void fman_get_pause_cfg(struct mac_device *mac_dev, bool *rx_pause,
+ bool *tx_pause);
+
+#endif /* __MAC_H */
diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
index cf8e54652df9..48a9c176e0d1 100644
--- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
+++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c
@@ -1050,7 +1050,7 @@ static int fs_enet_probe(struct platform_device *ofdev)
ndev->netdev_ops = &fs_enet_netdev_ops;
ndev->watchdog_timeo = 2 * HZ;
netif_napi_add(ndev, &fep->napi, fs_enet_rx_napi, fpi->napi_weight);
- netif_napi_add(ndev, &fep->napi_tx, fs_enet_tx_napi, 2);
+ netif_tx_napi_add(ndev, &fep->napi_tx, fs_enet_tx_napi, 2);
ndev->ethtool_ops = &fs_ethtool_ops;
diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c b/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c
index 08f5b911d96b..52e0091b4fb2 100644
--- a/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c
+++ b/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c
@@ -552,7 +552,7 @@ static void tx_restart(struct net_device *dev)
cbd_t __iomem *prev_bd;
cbd_t __iomem *last_tx_bd;
- last_tx_bd = fep->tx_bd_base + (fpi->tx_ring * sizeof(cbd_t));
+ last_tx_bd = fep->tx_bd_base + ((fpi->tx_ring - 1) * sizeof(cbd_t));
/* get the current bd held in TBPTR and scan back from this point */
recheck_bd = curr_tbptr = (cbd_t __iomem *)
diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c
index 016743e355de..bade2f8f9b5c 100644
--- a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c
+++ b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c
@@ -254,7 +254,7 @@ static void restart(struct net_device *dev)
int r;
u32 addrhi, addrlo;
- struct mii_bus* mii = fep->phydev->bus;
+ struct mii_bus *mii = fep->phydev->mdio.bus;
struct fec_info* fec_inf = mii->priv;
r = whack_reset(fep->fec.fecp);
@@ -363,7 +363,7 @@ static void stop(struct net_device *dev)
const struct fs_platform_info *fpi = fep->fpi;
struct fec __iomem *fecp = fep->fec.fecp;
- struct fec_info* feci= fep->phydev->bus->priv;
+ struct fec_info *feci = fep->phydev->mdio.bus->priv;
int i;
diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c
index 68a428de0bc0..1f015edcca22 100644
--- a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c
+++ b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c
@@ -172,23 +172,16 @@ static int fs_enet_mdio_probe(struct platform_device *ofdev)
goto out_free_bus;
new_bus->phy_mask = ~0;
- new_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!new_bus->irq) {
- ret = -ENOMEM;
- goto out_unmap_regs;
- }
new_bus->parent = &ofdev->dev;
platform_set_drvdata(ofdev, new_bus);
ret = of_mdiobus_register(new_bus, ofdev->dev.of_node);
if (ret)
- goto out_free_irqs;
+ goto out_unmap_regs;
return 0;
-out_free_irqs:
- kfree(new_bus->irq);
out_unmap_regs:
iounmap(bitbang->dir);
out_free_bus:
@@ -205,7 +198,6 @@ static int fs_enet_mdio_remove(struct platform_device *ofdev)
struct bb_info *bitbang = bus->priv;
mdiobus_unregister(bus);
- kfree(bus->irq);
free_mdio_bitbang(bus);
iounmap(bitbang->dir);
kfree(bitbang);
diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c
index 2be383e6d258..a89267b94352 100644
--- a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c
+++ b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c
@@ -166,23 +166,16 @@ static int fs_enet_mdio_probe(struct platform_device *ofdev)
clrsetbits_be32(&fec->fecp->fec_mii_speed, 0x7E, fec->mii_speed);
new_bus->phy_mask = ~0;
- new_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!new_bus->irq) {
- ret = -ENOMEM;
- goto out_unmap_regs;
- }
new_bus->parent = &ofdev->dev;
platform_set_drvdata(ofdev, new_bus);
ret = of_mdiobus_register(new_bus, ofdev->dev.of_node);
if (ret)
- goto out_free_irqs;
+ goto out_unmap_regs;
return 0;
-out_free_irqs:
- kfree(new_bus->irq);
out_unmap_regs:
iounmap(fec->fecp);
out_res:
@@ -200,7 +193,6 @@ static int fs_enet_mdio_remove(struct platform_device *ofdev)
struct fec_info *fec = bus->priv;
mdiobus_unregister(bus);
- kfree(bus->irq);
iounmap(fec->fecp);
kfree(fec);
mdiobus_free(bus);
diff --git a/drivers/net/ethernet/freescale/fsl_pq_mdio.c b/drivers/net/ethernet/freescale/fsl_pq_mdio.c
index d0a6fa6d4f3e..f3c63dce1e30 100644
--- a/drivers/net/ethernet/freescale/fsl_pq_mdio.c
+++ b/drivers/net/ethernet/freescale/fsl_pq_mdio.c
@@ -69,7 +69,6 @@ struct fsl_pq_mdio {
struct fsl_pq_mdio_priv {
void __iomem *map;
struct fsl_pq_mii __iomem *regs;
- int irqs[PHY_MAX_ADDR];
};
/*
@@ -401,7 +400,6 @@ static int fsl_pq_mdio_probe(struct platform_device *pdev)
new_bus->read = &fsl_pq_mdio_read;
new_bus->write = &fsl_pq_mdio_write;
new_bus->reset = &fsl_pq_mdio_reset;
- new_bus->irq = priv->irqs;
err = of_address_to_resource(np, 0, &res);
if (err < 0) {
@@ -464,7 +462,7 @@ static int fsl_pq_mdio_probe(struct platform_device *pdev)
* address). Print error message but continue anyway.
*/
if ((void *)tbipa > priv->map + resource_size(&res) - 4)
- dev_err(&pdev->dev, "invalid register map (should be at least 0x%04x to contain TBI address)\n",
+ dev_err(&pdev->dev, "invalid register map (should be at least 0x%04zx to contain TBI address)\n",
((void *)tbipa - priv->map) + 4);
iowrite32be(be32_to_cpup(prop), tbipa);
diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index 3e6b9b437497..2aa7b401cc3b 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -647,9 +647,9 @@ static int gfar_parse_group(struct device_node *np,
if (model && strcasecmp(model, "FEC")) {
gfar_irq(grp, RX)->irq = irq_of_parse_and_map(np, 1);
gfar_irq(grp, ER)->irq = irq_of_parse_and_map(np, 2);
- if (gfar_irq(grp, TX)->irq == NO_IRQ ||
- gfar_irq(grp, RX)->irq == NO_IRQ ||
- gfar_irq(grp, ER)->irq == NO_IRQ)
+ if (!gfar_irq(grp, TX)->irq ||
+ !gfar_irq(grp, RX)->irq ||
+ !gfar_irq(grp, ER)->irq)
return -EINVAL;
}
@@ -738,7 +738,6 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
struct gfar_private *priv = NULL;
struct device_node *np = ofdev->dev.of_node;
struct device_node *child = NULL;
- struct property *stash;
u32 stash_len = 0;
u32 stash_idx = 0;
unsigned int num_tx_qs, num_rx_qs;
@@ -854,9 +853,7 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
goto err_grp_init;
}
- stash = of_find_property(np, "bd-stash", NULL);
-
- if (stash) {
+ if (of_property_read_bool(np, "bd-stash")) {
priv->device_flags |= FSL_GIANFAR_DEV_HAS_BD_STASHING;
priv->bd_stash_en = 1;
}
@@ -894,7 +891,8 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
FSL_GIANFAR_DEV_HAS_VLAN |
FSL_GIANFAR_DEV_HAS_MAGIC_PACKET |
FSL_GIANFAR_DEV_HAS_EXTENDED_HASH |
- FSL_GIANFAR_DEV_HAS_TIMER;
+ FSL_GIANFAR_DEV_HAS_TIMER |
+ FSL_GIANFAR_DEV_HAS_RX_FILER;
err = of_property_read_string(np, "phy-connection-type", &ctype);
@@ -1347,12 +1345,12 @@ static int gfar_probe(struct platform_device *ofdev)
if (priv->poll_mode == GFAR_SQ_POLLING) {
netif_napi_add(dev, &priv->gfargrp[i].napi_rx,
gfar_poll_rx_sq, GFAR_DEV_WEIGHT);
- netif_napi_add(dev, &priv->gfargrp[i].napi_tx,
+ netif_tx_napi_add(dev, &priv->gfargrp[i].napi_tx,
gfar_poll_tx_sq, 2);
} else {
netif_napi_add(dev, &priv->gfargrp[i].napi_rx,
gfar_poll_rx, GFAR_DEV_WEIGHT);
- netif_napi_add(dev, &priv->gfargrp[i].napi_tx,
+ netif_tx_napi_add(dev, &priv->gfargrp[i].napi_tx,
gfar_poll_tx, 2);
}
}
@@ -1396,8 +1394,9 @@ static int gfar_probe(struct platform_device *ofdev)
priv->rx_queue[i]->rxic = DEFAULT_RXIC;
}
- /* always enable rx filer */
- priv->rx_filer_enable = 1;
+ /* Always enable rx filer if available */
+ priv->rx_filer_enable =
+ (priv->device_flags & FSL_GIANFAR_DEV_HAS_RX_FILER) ? 1 : 0;
/* Enable most messages by default */
priv->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1;
/* use pritority h/w tx queue scheduling for single queue devices */
@@ -1835,7 +1834,7 @@ static void gfar_configure_serdes(struct net_device *dev)
* several seconds for it to come back.
*/
if (phy_read(tbiphy, MII_BMSR) & BMSR_LSTATUS) {
- put_device(&tbiphy->dev);
+ put_device(&tbiphy->mdio.dev);
return;
}
@@ -1850,7 +1849,7 @@ static void gfar_configure_serdes(struct net_device *dev)
BMCR_ANENABLE | BMCR_ANRESTART | BMCR_FULLDPLX |
BMCR_SPEED1000);
- put_device(&tbiphy->dev);
+ put_device(&tbiphy->mdio.dev);
}
static int __gfar_is_rx_idle(struct gfar_private *priv)
diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h
index f266b20f9ef5..cb77667971a7 100644
--- a/drivers/net/ethernet/freescale/gianfar.h
+++ b/drivers/net/ethernet/freescale/gianfar.h
@@ -923,6 +923,7 @@ struct gfar {
#define FSL_GIANFAR_DEV_HAS_BUF_STASHING 0x00000400
#define FSL_GIANFAR_DEV_HAS_TIMER 0x00000800
#define FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER 0x00001000
+#define FSL_GIANFAR_DEV_HAS_RX_FILER 0x00002000
#if (MAXGROUPS == 2)
#define DEFAULT_MAPPING 0xAA
diff --git a/drivers/net/ethernet/freescale/gianfar_ptp.c b/drivers/net/ethernet/freescale/gianfar_ptp.c
index 664d0c261269..b40fba929d65 100644
--- a/drivers/net/ethernet/freescale/gianfar_ptp.c
+++ b/drivers/net/ethernet/freescale/gianfar_ptp.c
@@ -467,7 +467,7 @@ static int gianfar_ptp_probe(struct platform_device *dev)
etsects->irq = platform_get_irq(dev, 0);
- if (etsects->irq == NO_IRQ) {
+ if (etsects->irq < 0) {
pr_err("irq not in device tree\n");
goto no_node;
}
diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c
index c30b72e02a1a..5bf1ade28315 100644
--- a/drivers/net/ethernet/freescale/ucc_geth.c
+++ b/drivers/net/ethernet/freescale/ucc_geth.c
@@ -1385,7 +1385,7 @@ static int adjust_enet_interface(struct ucc_geth_private *ugeth)
value &= ~0x1000; /* Turn off autonegotiation */
phy_write(tbiphy, ENET_TBI_MII_CR, value);
- put_device(&tbiphy->dev);
+ put_device(&tbiphy->mdio.dev);
}
init_check_frame_length_mode(ug_info->lengthCheckRx, &ug_regs->maccfg2);
@@ -1705,7 +1705,7 @@ static void uec_configure_serdes(struct net_device *dev)
* several seconds for it to come back.
*/
if (phy_read(tbiphy, ENET_TBI_MII_SR) & TBISR_LSTATUS) {
- put_device(&tbiphy->dev);
+ put_device(&tbiphy->mdio.dev);
return;
}
@@ -1716,7 +1716,7 @@ static void uec_configure_serdes(struct net_device *dev)
phy_write(tbiphy, ENET_TBI_MII_CR, TBICR_SETTINGS);
- put_device(&tbiphy->dev);
+ put_device(&tbiphy->mdio.dev);
}
/* Configure the PHY for dev.
diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h
index cec95ac8687d..6ca94dc3dda3 100644
--- a/drivers/net/ethernet/hisilicon/hns/hnae.h
+++ b/drivers/net/ethernet/hisilicon/hns/hnae.h
@@ -35,7 +35,7 @@
#include <linux/phy.h>
#include <linux/types.h>
-#define HNAE_DRIVER_VERSION "1.3.0"
+#define HNAE_DRIVER_VERSION "2.0"
#define HNAE_DRIVER_NAME "hns"
#define HNAE_COPYRIGHT "Copyright(c) 2015 Huawei Corporation."
#define HNAE_DRIVER_STRING "Hisilicon Network Subsystem Driver"
@@ -63,6 +63,7 @@ do { \
#define AE_VERSION_1 ('6' << 16 | '6' << 8 | '0')
#define AE_VERSION_2 ('1' << 24 | '6' << 16 | '1' << 8 | '0')
+#define AE_IS_VER1(ver) ((ver) == AE_VERSION_1)
#define AE_NAME_SIZE 16
/* some said the RX and TX RCB format should not be the same in the future. But
@@ -144,23 +145,61 @@ enum hnae_led_state {
#define HNS_RXD_ASID_S 24
#define HNS_RXD_ASID_M (0xff << HNS_RXD_ASID_S)
+#define HNSV2_TXD_BUFNUM_S 0
+#define HNSV2_TXD_BUFNUM_M (0x7 << HNSV2_TXD_BUFNUM_S)
+#define HNSV2_TXD_RI_B 1
+#define HNSV2_TXD_L4CS_B 2
+#define HNSV2_TXD_L3CS_B 3
+#define HNSV2_TXD_FE_B 4
+#define HNSV2_TXD_VLD_B 5
+
+#define HNSV2_TXD_TSE_B 0
+#define HNSV2_TXD_VLAN_EN_B 1
+#define HNSV2_TXD_SNAP_B 2
+#define HNSV2_TXD_IPV6_B 3
+#define HNSV2_TXD_SCTP_B 4
+
/* hardware spec ring buffer format */
struct __packed hnae_desc {
__le64 addr;
union {
struct {
- __le16 asid_bufnum_pid;
+ union {
+ __le16 asid_bufnum_pid;
+ __le16 asid;
+ };
__le16 send_size;
- __le32 flag_ipoffset;
- __le32 reserved_3[4];
+ union {
+ __le32 flag_ipoffset;
+ struct {
+ __u8 bn_pid;
+ __u8 ra_ri_cs_fe_vld;
+ __u8 ip_offset;
+ __u8 tse_vlan_snap_v6_sctp_nth;
+ };
+ };
+ __le16 mss;
+ __u8 l4_len;
+ __u8 reserved1;
+ __le16 paylen;
+ __u8 vmid;
+ __u8 qid;
+ __le32 reserved2[2];
} tx;
struct {
__le32 ipoff_bnum_pid_flag;
__le16 pkt_len;
__le16 size;
- __le32 vlan_pri_asid;
- __le32 reserved_2[3];
+ union {
+ __le32 vlan_pri_asid;
+ struct {
+ __le16 asid;
+ __le16 vlan_cfi_pri;
+ };
+ };
+ __le32 rss_hash;
+ __le32 reserved_1[2];
} rx;
};
};
@@ -302,7 +341,8 @@ struct hnae_queue {
void __iomem *io_base;
phys_addr_t phy_base;
struct hnae_ae_dev *dev; /* the device who use this queue */
- struct hnae_ring rx_ring, tx_ring;
+ struct hnae_ring rx_ring ____cacheline_internodealigned_in_smp;
+ struct hnae_ring tx_ring ____cacheline_internodealigned_in_smp;
struct hnae_handle *handle;
};
@@ -435,6 +475,7 @@ struct hnae_ae_ops {
int (*set_mac_addr)(struct hnae_handle *handle, void *p);
int (*set_mc_addr)(struct hnae_handle *handle, void *addr);
int (*set_mtu)(struct hnae_handle *handle, int new_mtu);
+ void (*set_tso_stats)(struct hnae_handle *handle, int enable);
void (*update_stats)(struct hnae_handle *handle,
struct net_device_stats *net_stats);
void (*get_stats)(struct hnae_handle *handle, u64 *data);
@@ -446,6 +487,12 @@ struct hnae_ae_ops {
enum hnae_led_state status);
void (*get_regs)(struct hnae_handle *handle, void *data);
int (*get_regs_len)(struct hnae_handle *handle);
+ u32 (*get_rss_key_size)(struct hnae_handle *handle);
+ u32 (*get_rss_indir_size)(struct hnae_handle *handle);
+ int (*get_rss)(struct hnae_handle *handle, u32 *indir, u8 *key,
+ u8 *hfunc);
+ int (*set_rss)(struct hnae_handle *handle, const u32 *indir,
+ const u8 *key, const u8 hfunc);
};
struct hnae_ae_dev {
@@ -551,11 +598,9 @@ static inline void hnae_replace_buffer(struct hnae_ring *ring, int i,
struct hnae_desc_cb *res_cb)
{
struct hnae_buf_ops *bops = ring->q->handle->bops;
- struct hnae_desc_cb tmp_cb = ring->desc_cb[i];
bops->unmap_buffer(ring, &ring->desc_cb[i]);
ring->desc_cb[i] = *res_cb;
- *res_cb = tmp_cb;
ring->desc[i].addr = (__le64)ring->desc_cb[i].dma;
ring->desc[i].rx.ipoff_bnum_pid_flag = 0;
}
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
index 1a16c0307b47..522b264866b4 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
@@ -252,7 +252,7 @@ static int hns_ae_set_multicast_one(struct hnae_handle *handle, void *addr)
if (mac_cb->mac_type != HNAE_PORT_SERVICE)
return 0;
- ret = hns_mac_set_multi(mac_cb, mac_cb->mac_id, mac_addr, ENABLE);
+ ret = hns_mac_set_multi(mac_cb, mac_cb->mac_id, mac_addr, true);
if (ret) {
dev_err(handle->owner_dev,
"mac add mul_mac:%pM port%d fail, ret = %#x!\n",
@@ -261,7 +261,7 @@ static int hns_ae_set_multicast_one(struct hnae_handle *handle, void *addr)
}
ret = hns_mac_set_multi(mac_cb, DSAF_BASE_INNER_PORT_NUM,
- mac_addr, ENABLE);
+ mac_addr, true);
if (ret)
dev_err(handle->owner_dev,
"mac add mul_mac:%pM port%d fail, ret = %#x!\n",
@@ -277,12 +277,19 @@ static int hns_ae_set_mtu(struct hnae_handle *handle, int new_mtu)
return hns_mac_set_mtu(mac_cb, new_mtu);
}
+static void hns_ae_set_tso_stats(struct hnae_handle *handle, int enable)
+{
+ struct hns_ppe_cb *ppe_cb = hns_get_ppe_cb(handle);
+
+ hns_ppe_set_tso_enable(ppe_cb, enable);
+}
+
static int hns_ae_start(struct hnae_handle *handle)
{
int ret;
struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle);
- ret = hns_mac_vm_config_bc_en(mac_cb, 0, ENABLE);
+ ret = hns_mac_vm_config_bc_en(mac_cb, 0, true);
if (ret)
return ret;
@@ -309,7 +316,7 @@ void hns_ae_stop(struct hnae_handle *handle)
hns_ae_ring_enable_all(handle, 0);
- (void)hns_mac_vm_config_bc_en(mac_cb, 0, DISABLE);
+ (void)hns_mac_vm_config_bc_en(mac_cb, 0, false);
}
static void hns_ae_reset(struct hnae_handle *handle)
@@ -334,12 +341,30 @@ void hns_ae_toggle_ring_irq(struct hnae_ring *ring, u32 mask)
else
flag = RCB_INT_FLAG_RX;
- hns_rcb_int_clr_hw(ring->q, flag);
hns_rcb_int_ctrl_hw(ring->q, flag, mask);
}
+static void hns_aev2_toggle_ring_irq(struct hnae_ring *ring, u32 mask)
+{
+ u32 flag;
+
+ if (is_tx_ring(ring))
+ flag = RCB_INT_FLAG_TX;
+ else
+ flag = RCB_INT_FLAG_RX;
+
+ hns_rcbv2_int_ctrl_hw(ring->q, flag, mask);
+}
+
static void hns_ae_toggle_queue_status(struct hnae_queue *queue, u32 val)
{
+ struct dsaf_device *dsaf_dev = hns_ae_get_dsaf_dev(queue->dev);
+
+ if (AE_IS_VER1(dsaf_dev->dsaf_ver))
+ hns_rcb_int_clr_hw(queue, RCB_INT_FLAG_TX | RCB_INT_FLAG_RX);
+ else
+ hns_rcbv2_int_clr_hw(queue, RCB_INT_FLAG_TX | RCB_INT_FLAG_RX);
+
hns_rcb_start(queue, val);
}
@@ -730,6 +755,53 @@ int hns_ae_get_regs_len(struct hnae_handle *handle)
return total_num;
}
+static u32 hns_ae_get_rss_key_size(struct hnae_handle *handle)
+{
+ return HNS_PPEV2_RSS_KEY_SIZE;
+}
+
+static u32 hns_ae_get_rss_indir_size(struct hnae_handle *handle)
+{
+ return HNS_PPEV2_RSS_IND_TBL_SIZE;
+}
+
+static int hns_ae_get_rss(struct hnae_handle *handle, u32 *indir, u8 *key,
+ u8 *hfunc)
+{
+ struct hns_ppe_cb *ppe_cb = hns_get_ppe_cb(handle);
+
+ /* currently we support only one type of hash function i.e. Toep hash */
+ if (hfunc)
+ *hfunc = ETH_RSS_HASH_TOP;
+
+ /* get the RSS Key required by the user */
+ if (key)
+ memcpy(key, ppe_cb->rss_key, HNS_PPEV2_RSS_KEY_SIZE);
+
+ /* update the current hash->queue mappings from the shadow RSS table */
+ memcpy(indir, ppe_cb->rss_indir_table, HNS_PPEV2_RSS_IND_TBL_SIZE);
+
+ return 0;
+}
+
+static int hns_ae_set_rss(struct hnae_handle *handle, const u32 *indir,
+ const u8 *key, const u8 hfunc)
+{
+ struct hns_ppe_cb *ppe_cb = hns_get_ppe_cb(handle);
+
+ /* set the RSS Hash Key if specififed by the user */
+ if (key)
+ hns_ppe_set_rss_key(ppe_cb, (int *)key);
+
+ /* update the shadow RSS table with user specified qids */
+ memcpy(ppe_cb->rss_indir_table, indir, HNS_PPEV2_RSS_IND_TBL_SIZE);
+
+ /* now update the hardware */
+ hns_ppe_set_indir_table(ppe_cb, ppe_cb->rss_indir_table);
+
+ return 0;
+}
+
static struct hnae_ae_ops hns_dsaf_ops = {
.get_handle = hns_ae_get_handle,
.put_handle = hns_ae_put_handle,
@@ -758,19 +830,34 @@ static struct hnae_ae_ops hns_dsaf_ops = {
.set_mc_addr = hns_ae_set_multicast_one,
.set_mtu = hns_ae_set_mtu,
.update_stats = hns_ae_update_stats,
+ .set_tso_stats = hns_ae_set_tso_stats,
.get_stats = hns_ae_get_stats,
.get_strings = hns_ae_get_strings,
.get_sset_count = hns_ae_get_sset_count,
.update_led_status = hns_ae_update_led_status,
.set_led_id = hns_ae_cpld_set_led_id,
.get_regs = hns_ae_get_regs,
- .get_regs_len = hns_ae_get_regs_len
+ .get_regs_len = hns_ae_get_regs_len,
+ .get_rss_key_size = hns_ae_get_rss_key_size,
+ .get_rss_indir_size = hns_ae_get_rss_indir_size,
+ .get_rss = hns_ae_get_rss,
+ .set_rss = hns_ae_set_rss
};
int hns_dsaf_ae_init(struct dsaf_device *dsaf_dev)
{
struct hnae_ae_dev *ae_dev = &dsaf_dev->ae_dev;
+ switch (dsaf_dev->dsaf_ver) {
+ case AE_VERSION_1:
+ hns_dsaf_ops.toggle_ring_irq = hns_ae_toggle_ring_irq;
+ break;
+ case AE_VERSION_2:
+ hns_dsaf_ops.toggle_ring_irq = hns_aev2_toggle_ring_irq;
+ break;
+ default:
+ break;
+ }
ae_dev->ops = &hns_dsaf_ops;
ae_dev->dev = dsaf_dev->dev;
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c
index 026b38676cba..5ef0e96e918a 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c
@@ -283,7 +283,7 @@ int hns_mac_change_vf_addr(struct hns_mac_cb *mac_cb,
}
int hns_mac_set_multi(struct hns_mac_cb *mac_cb,
- u32 port_num, char *addr, u8 en)
+ u32 port_num, char *addr, bool enable)
{
int ret;
struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev;
@@ -295,7 +295,7 @@ int hns_mac_set_multi(struct hns_mac_cb *mac_cb,
mac_entry.in_port_num = mac_cb->mac_id;
mac_entry.port_num = port_num;
- if (en == DISABLE)
+ if (!enable)
ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry);
else
ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry);
@@ -368,7 +368,7 @@ static void hns_mac_param_get(struct mac_params *param,
*retuen 0 - success , negative --fail
*/
static int hns_mac_port_config_bc_en(struct hns_mac_cb *mac_cb,
- u32 port_num, u16 vlan_id, u8 en)
+ u32 port_num, u16 vlan_id, bool enable)
{
int ret;
struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev;
@@ -386,7 +386,7 @@ static int hns_mac_port_config_bc_en(struct hns_mac_cb *mac_cb,
mac_entry.in_port_num = mac_cb->mac_id;
mac_entry.port_num = port_num;
- if (en == DISABLE)
+ if (!enable)
ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry);
else
ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry);
@@ -403,7 +403,7 @@ static int hns_mac_port_config_bc_en(struct hns_mac_cb *mac_cb,
*@en:enable
*retuen 0 - success , negative --fail
*/
-int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vmid, u8 en)
+int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vmid, bool enable)
{
int ret;
struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev;
@@ -427,7 +427,7 @@ int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vmid, u8 en)
return ret;
mac_entry.port_num = port_num;
- if (en == DISABLE)
+ if (!enable)
ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry);
else
ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry);
@@ -648,7 +648,7 @@ static int hns_mac_init_ex(struct hns_mac_cb *mac_cb)
hns_mac_adjust_link(mac_cb, mac_cb->speed, !mac_cb->half_duplex);
- ret = hns_mac_port_config_bc_en(mac_cb, mac_cb->mac_id, 0, ENABLE);
+ ret = hns_mac_port_config_bc_en(mac_cb, mac_cb->mac_id, 0, true);
if (ret)
goto free_mac_drv;
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h
index 7da95a7581f9..0b052191d751 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h
@@ -425,8 +425,8 @@ void mac_adjust_link(struct net_device *net_dev);
void hns_mac_get_link_status(struct hns_mac_cb *mac_cb, u32 *link_status);
int hns_mac_change_vf_addr(struct hns_mac_cb *mac_cb, u32 vmid, char *addr);
int hns_mac_set_multi(struct hns_mac_cb *mac_cb,
- u32 port_num, char *addr, u8 en);
-int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vm, u8 en);
+ u32 port_num, char *addr, bool enable);
+int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vm, bool enable);
void hns_mac_start(struct hns_mac_cb *mac_cb);
void hns_mac_stop(struct hns_mac_cb *mac_cb);
int hns_mac_del_mac(struct hns_mac_cb *mac_cb, u32 vfn, char *mac);
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c
index 2a98eba660c0..1c33bd06bd5c 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c
@@ -38,10 +38,10 @@ int hns_dsaf_get_cfg(struct dsaf_device *dsaf_dev)
const char *name, *mode_str;
struct device_node *np = dsaf_dev->dev->of_node;
- if (of_device_is_compatible(np, "hisilicon,hns-dsaf-v2"))
- dsaf_dev->dsaf_ver = AE_VERSION_2;
- else
+ if (of_device_is_compatible(np, "hisilicon,hns-dsaf-v1"))
dsaf_dev->dsaf_ver = AE_VERSION_1;
+ else
+ dsaf_dev->dsaf_ver = AE_VERSION_2;
ret = of_property_read_string(np, "dsa_name", &name);
if (ret) {
@@ -274,6 +274,8 @@ static void hns_dsaf_stp_port_type_cfg(struct dsaf_device *dsaf_dev,
}
}
+#define HNS_DSAF_SBM_NUM(dev) \
+ (AE_IS_VER1((dev)->dsaf_ver) ? DSAF_SBM_NUM : DSAFV2_SBM_NUM)
/**
* hns_dsaf_sbm_cfg - config sbm
* @dsaf_id: dsa fabric id
@@ -283,7 +285,7 @@ static void hns_dsaf_sbm_cfg(struct dsaf_device *dsaf_dev)
u32 o_sbm_cfg;
u32 i;
- for (i = 0; i < DSAF_SBM_NUM; i++) {
+ for (i = 0; i < HNS_DSAF_SBM_NUM(dsaf_dev); i++) {
o_sbm_cfg = dsaf_read_dev(dsaf_dev,
DSAF_SBM_CFG_REG_0_REG + 0x80 * i);
dsaf_set_bit(o_sbm_cfg, DSAF_SBM_CFG_EN_S, 1);
@@ -304,13 +306,19 @@ static int hns_dsaf_sbm_cfg_mib_en(struct dsaf_device *dsaf_dev)
u32 reg;
u32 read_cnt;
- for (i = 0; i < DSAF_SBM_NUM; i++) {
+ /* validate configure by setting SBM_CFG_MIB_EN bit from 0 to 1. */
+ for (i = 0; i < HNS_DSAF_SBM_NUM(dsaf_dev); i++) {
+ reg = DSAF_SBM_CFG_REG_0_REG + 0x80 * i;
+ dsaf_set_dev_bit(dsaf_dev, reg, DSAF_SBM_CFG_MIB_EN_S, 0);
+ }
+
+ for (i = 0; i < HNS_DSAF_SBM_NUM(dsaf_dev); i++) {
reg = DSAF_SBM_CFG_REG_0_REG + 0x80 * i;
dsaf_set_dev_bit(dsaf_dev, reg, DSAF_SBM_CFG_MIB_EN_S, 1);
}
/* waitint for all sbm enable finished */
- for (i = 0; i < DSAF_SBM_NUM; i++) {
+ for (i = 0; i < HNS_DSAF_SBM_NUM(dsaf_dev); i++) {
read_cnt = 0;
reg = DSAF_SBM_CFG_REG_0_REG + 0x80 * i;
do {
@@ -338,83 +346,156 @@ static int hns_dsaf_sbm_cfg_mib_en(struct dsaf_device *dsaf_dev)
*/
static void hns_dsaf_sbm_bp_wl_cfg(struct dsaf_device *dsaf_dev)
{
- u32 o_sbm_bp_cfg0;
- u32 o_sbm_bp_cfg1;
- u32 o_sbm_bp_cfg2;
- u32 o_sbm_bp_cfg3;
+ u32 o_sbm_bp_cfg;
u32 reg;
u32 i;
/* XGE */
for (i = 0; i < DSAF_XGE_NUM; i++) {
reg = DSAF_SBM_BP_CFG_0_XGE_REG_0_REG + 0x80 * i;
- o_sbm_bp_cfg0 = dsaf_read_dev(dsaf_dev, reg);
- dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_COM_MAX_BUF_NUM_M,
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG0_COM_MAX_BUF_NUM_M,
DSAF_SBM_CFG0_COM_MAX_BUF_NUM_S, 512);
- dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_M,
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_M,
DSAF_SBM_CFG0_VC0_MAX_BUF_NUM_S, 0);
- dsaf_set_field(o_sbm_bp_cfg0, DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_M,
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_M,
DSAF_SBM_CFG0_VC1_MAX_BUF_NUM_S, 0);
- dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg0);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
reg = DSAF_SBM_BP_CFG_1_REG_0_REG + 0x80 * i;
- o_sbm_bp_cfg1 = dsaf_read_dev(dsaf_dev, reg);
- dsaf_set_field(o_sbm_bp_cfg1, DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_M,
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_M,
DSAF_SBM_CFG1_TC4_MAX_BUF_NUM_S, 0);
- dsaf_set_field(o_sbm_bp_cfg1, DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_M,
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_M,
DSAF_SBM_CFG1_TC0_MAX_BUF_NUM_S, 0);
- dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg1);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
reg = DSAF_SBM_BP_CFG_2_XGE_REG_0_REG + 0x80 * i;
- o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg);
- dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M,
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG2_SET_BUF_NUM_M,
DSAF_SBM_CFG2_SET_BUF_NUM_S, 104);
- dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M,
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG2_RESET_BUF_NUM_M,
DSAF_SBM_CFG2_RESET_BUF_NUM_S, 128);
- dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
reg = DSAF_SBM_BP_CFG_3_REG_0_REG + 0x80 * i;
- o_sbm_bp_cfg3 = dsaf_read_dev(dsaf_dev, reg);
- dsaf_set_field(o_sbm_bp_cfg3,
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg,
DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_M,
DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_S, 110);
- dsaf_set_field(o_sbm_bp_cfg3,
+ dsaf_set_field(o_sbm_bp_cfg,
DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M,
DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S, 160);
- dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg3);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
/* for no enable pfc mode */
reg = DSAF_SBM_BP_CFG_4_REG_0_REG + 0x80 * i;
- o_sbm_bp_cfg3 = dsaf_read_dev(dsaf_dev, reg);
- dsaf_set_field(o_sbm_bp_cfg3,
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg,
DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_M,
DSAF_SBM_CFG3_SET_BUF_NUM_NO_PFC_S, 128);
- dsaf_set_field(o_sbm_bp_cfg3,
+ dsaf_set_field(o_sbm_bp_cfg,
DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M,
DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S, 192);
- dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg3);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
}
/* PPE */
for (i = 0; i < DSAF_COMM_CHN; i++) {
reg = DSAF_SBM_BP_CFG_2_PPE_REG_0_REG + 0x80 * i;
- o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg);
- dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M,
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG2_SET_BUF_NUM_M,
DSAF_SBM_CFG2_SET_BUF_NUM_S, 10);
- dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M,
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG2_RESET_BUF_NUM_M,
DSAF_SBM_CFG2_RESET_BUF_NUM_S, 12);
- dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
}
/* RoCEE */
for (i = 0; i < DSAF_COMM_CHN; i++) {
reg = DSAF_SBM_BP_CFG_2_ROCEE_REG_0_REG + 0x80 * i;
- o_sbm_bp_cfg2 = dsaf_read_dev(dsaf_dev, reg);
- dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_SET_BUF_NUM_M,
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG2_SET_BUF_NUM_M,
DSAF_SBM_CFG2_SET_BUF_NUM_S, 2);
- dsaf_set_field(o_sbm_bp_cfg2, DSAF_SBM_CFG2_RESET_BUF_NUM_M,
+ dsaf_set_field(o_sbm_bp_cfg, DSAF_SBM_CFG2_RESET_BUF_NUM_M,
DSAF_SBM_CFG2_RESET_BUF_NUM_S, 4);
- dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg2);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
+ }
+}
+
+static void hns_dsafv2_sbm_bp_wl_cfg(struct dsaf_device *dsaf_dev)
+{
+ u32 o_sbm_bp_cfg;
+ u32 reg;
+ u32 i;
+
+ /* XGE */
+ for (i = 0; i < DSAFV2_SBM_XGE_CHN; i++) {
+ reg = DSAF_SBM_BP_CFG_0_XGE_REG_0_REG + 0x80 * i;
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG0_COM_MAX_BUF_NUM_M,
+ DSAFV2_SBM_CFG0_COM_MAX_BUF_NUM_S, 256);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG0_VC0_MAX_BUF_NUM_M,
+ DSAFV2_SBM_CFG0_VC0_MAX_BUF_NUM_S, 0);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG0_VC1_MAX_BUF_NUM_M,
+ DSAFV2_SBM_CFG0_VC1_MAX_BUF_NUM_S, 0);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
+
+ reg = DSAF_SBM_BP_CFG_1_REG_0_REG + 0x80 * i;
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG1_TC4_MAX_BUF_NUM_M,
+ DSAFV2_SBM_CFG1_TC4_MAX_BUF_NUM_S, 0);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG1_TC0_MAX_BUF_NUM_M,
+ DSAFV2_SBM_CFG1_TC0_MAX_BUF_NUM_S, 0);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
+
+ reg = DSAF_SBM_BP_CFG_2_XGE_REG_0_REG + 0x80 * i;
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG2_SET_BUF_NUM_M,
+ DSAFV2_SBM_CFG2_SET_BUF_NUM_S, 104);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG2_RESET_BUF_NUM_M,
+ DSAFV2_SBM_CFG2_RESET_BUF_NUM_S, 128);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
+
+ reg = DSAF_SBM_BP_CFG_3_REG_0_REG + 0x80 * i;
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg,
+ DSAFV2_SBM_CFG3_SET_BUF_NUM_NO_PFC_M,
+ DSAFV2_SBM_CFG3_SET_BUF_NUM_NO_PFC_S, 110);
+ dsaf_set_field(o_sbm_bp_cfg,
+ DSAFV2_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M,
+ DSAFV2_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S, 160);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
+
+ /* for no enable pfc mode */
+ reg = DSAF_SBM_BP_CFG_4_REG_0_REG + 0x80 * i;
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg,
+ DSAFV2_SBM_CFG4_SET_BUF_NUM_NO_PFC_M,
+ DSAFV2_SBM_CFG4_SET_BUF_NUM_NO_PFC_S, 128);
+ dsaf_set_field(o_sbm_bp_cfg,
+ DSAFV2_SBM_CFG4_RESET_BUF_NUM_NO_PFC_M,
+ DSAFV2_SBM_CFG4_RESET_BUF_NUM_NO_PFC_S, 192);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
+ }
+
+ /* PPE */
+ reg = DSAF_SBM_BP_CFG_2_PPE_REG_0_REG + 0x80 * i;
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG2_SET_BUF_NUM_M,
+ DSAFV2_SBM_CFG2_SET_BUF_NUM_S, 10);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG2_RESET_BUF_NUM_M,
+ DSAFV2_SBM_CFG2_RESET_BUF_NUM_S, 12);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
+ /* RoCEE */
+ for (i = 0; i < DASFV2_ROCEE_CRD_NUM; i++) {
+ reg = DSAFV2_SBM_BP_CFG_2_ROCEE_REG_0_REG + 0x80 * i;
+ o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG2_SET_BUF_NUM_M,
+ DSAFV2_SBM_CFG2_SET_BUF_NUM_S, 2);
+ dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG2_RESET_BUF_NUM_M,
+ DSAFV2_SBM_CFG2_RESET_BUF_NUM_S, 4);
+ dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg);
}
}
@@ -985,11 +1066,38 @@ static void hns_dsaf_inode_init(struct dsaf_device *dsaf_dev)
else
tc_cfg = HNS_DSAF_I8TC_CFG;
+ if (AE_IS_VER1(dsaf_dev->dsaf_ver)) {
+ for (i = 0; i < DSAF_INODE_NUM; i++) {
+ reg = DSAF_INODE_IN_PORT_NUM_0_REG + 0x80 * i;
+ dsaf_set_dev_field(dsaf_dev, reg,
+ DSAF_INODE_IN_PORT_NUM_M,
+ DSAF_INODE_IN_PORT_NUM_S,
+ i % DSAF_XGE_NUM);
+ }
+ } else {
+ for (i = 0; i < DSAF_PORT_TYPE_NUM; i++) {
+ reg = DSAF_INODE_IN_PORT_NUM_0_REG + 0x80 * i;
+ dsaf_set_dev_field(dsaf_dev, reg,
+ DSAF_INODE_IN_PORT_NUM_M,
+ DSAF_INODE_IN_PORT_NUM_S, 0);
+ dsaf_set_dev_field(dsaf_dev, reg,
+ DSAFV2_INODE_IN_PORT1_NUM_M,
+ DSAFV2_INODE_IN_PORT1_NUM_S, 1);
+ dsaf_set_dev_field(dsaf_dev, reg,
+ DSAFV2_INODE_IN_PORT2_NUM_M,
+ DSAFV2_INODE_IN_PORT2_NUM_S, 2);
+ dsaf_set_dev_field(dsaf_dev, reg,
+ DSAFV2_INODE_IN_PORT3_NUM_M,
+ DSAFV2_INODE_IN_PORT3_NUM_S, 3);
+ dsaf_set_dev_field(dsaf_dev, reg,
+ DSAFV2_INODE_IN_PORT4_NUM_M,
+ DSAFV2_INODE_IN_PORT4_NUM_S, 4);
+ dsaf_set_dev_field(dsaf_dev, reg,
+ DSAFV2_INODE_IN_PORT5_NUM_M,
+ DSAFV2_INODE_IN_PORT5_NUM_S, 5);
+ }
+ }
for (i = 0; i < DSAF_INODE_NUM; i++) {
- reg = DSAF_INODE_IN_PORT_NUM_0_REG + 0x80 * i;
- dsaf_set_dev_field(dsaf_dev, reg, DSAF_INODE_IN_PORT_NUM_M,
- DSAF_INODE_IN_PORT_NUM_S, i % DSAF_XGE_NUM);
-
reg = DSAF_INODE_PRI_TC_CFG_0_REG + 0x80 * i;
dsaf_write_dev(dsaf_dev, reg, tc_cfg);
}
@@ -1002,10 +1110,17 @@ static void hns_dsaf_inode_init(struct dsaf_device *dsaf_dev)
static int hns_dsaf_sbm_init(struct dsaf_device *dsaf_dev)
{
u32 flag;
+ u32 finish_msk;
u32 cnt = 0;
int ret;
- hns_dsaf_sbm_bp_wl_cfg(dsaf_dev);
+ if (AE_IS_VER1(dsaf_dev->dsaf_ver)) {
+ hns_dsaf_sbm_bp_wl_cfg(dsaf_dev);
+ finish_msk = DSAF_SRAM_INIT_OVER_M;
+ } else {
+ hns_dsafv2_sbm_bp_wl_cfg(dsaf_dev);
+ finish_msk = DSAFV2_SRAM_INIT_OVER_M;
+ }
/* enable sbm chanel, disable sbm chanel shcut function*/
hns_dsaf_sbm_cfg(dsaf_dev);
@@ -1024,11 +1139,13 @@ static int hns_dsaf_sbm_init(struct dsaf_device *dsaf_dev)
do {
usleep_range(200, 210);/*udelay(200);*/
- flag = dsaf_read_dev(dsaf_dev, DSAF_SRAM_INIT_OVER_0_REG);
+ flag = dsaf_get_dev_field(dsaf_dev, DSAF_SRAM_INIT_OVER_0_REG,
+ finish_msk, DSAF_SRAM_INIT_OVER_S);
cnt++;
- } while (flag != DSAF_SRAM_INIT_FINISH_FLAG && cnt < DSAF_CFG_READ_CNT);
+ } while (flag != (finish_msk >> DSAF_SRAM_INIT_OVER_S) &&
+ cnt < DSAF_CFG_READ_CNT);
- if (flag != DSAF_SRAM_INIT_FINISH_FLAG) {
+ if (flag != (finish_msk >> DSAF_SRAM_INIT_OVER_S)) {
dev_err(dsaf_dev->dev,
"hns_dsaf_sbm_init fail %s, flag=%d, cnt=%d\n",
dsaf_dev->ae_dev.name, flag, cnt);
@@ -1259,12 +1376,8 @@ int hns_dsaf_set_mac_uc_entry(
if (MAC_IS_ALL_ZEROS(mac_entry->addr) ||
MAC_IS_BROADCAST(mac_entry->addr) ||
MAC_IS_MULTICAST(mac_entry->addr)) {
- dev_err(dsaf_dev->dev,
- "set_uc %s Mac %02x:%02x:%02x:%02x:%02x:%02x err!\n",
- dsaf_dev->ae_dev.name, mac_entry->addr[0],
- mac_entry->addr[1], mac_entry->addr[2],
- mac_entry->addr[3], mac_entry->addr[4],
- mac_entry->addr[5]);
+ dev_err(dsaf_dev->dev, "set_uc %s Mac %pM err!\n",
+ dsaf_dev->ae_dev.name, mac_entry->addr);
return -EINVAL;
}
@@ -1331,12 +1444,8 @@ int hns_dsaf_set_mac_mc_entry(
/* mac addr check */
if (MAC_IS_ALL_ZEROS(mac_entry->addr)) {
- dev_err(dsaf_dev->dev,
- "set uc %s Mac %02x:%02x:%02x:%02x:%02x:%02x err!\n",
- dsaf_dev->ae_dev.name, mac_entry->addr[0],
- mac_entry->addr[1], mac_entry->addr[2],
- mac_entry->addr[3],
- mac_entry->addr[4], mac_entry->addr[5]);
+ dev_err(dsaf_dev->dev, "set uc %s Mac %pM err!\n",
+ dsaf_dev->ae_dev.name, mac_entry->addr);
return -EINVAL;
}
@@ -1410,11 +1519,8 @@ int hns_dsaf_add_mac_mc_port(struct dsaf_device *dsaf_dev,
/*chechk mac addr */
if (MAC_IS_ALL_ZEROS(mac_entry->addr)) {
- dev_err(dsaf_dev->dev,
- "set_entry failed,addr %02x:%02x:%02x:%02x:%02x:%02x!\n",
- mac_entry->addr[0], mac_entry->addr[1],
- mac_entry->addr[2], mac_entry->addr[3],
- mac_entry->addr[4], mac_entry->addr[5]);
+ dev_err(dsaf_dev->dev, "set_entry failed,addr %pM!\n",
+ mac_entry->addr);
return -EINVAL;
}
@@ -1497,9 +1603,8 @@ int hns_dsaf_del_mac_entry(struct dsaf_device *dsaf_dev, u16 vlan_id,
/*check mac addr */
if (MAC_IS_ALL_ZEROS(addr) || MAC_IS_BROADCAST(addr)) {
- dev_err(dsaf_dev->dev,
- "del_entry failed,addr %02x:%02x:%02x:%02x:%02x:%02x!\n",
- addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+ dev_err(dsaf_dev->dev, "del_entry failed,addr %pM!\n",
+ addr);
return -EINVAL;
}
@@ -1563,11 +1668,8 @@ int hns_dsaf_del_mac_mc_port(struct dsaf_device *dsaf_dev,
/*check mac addr */
if (MAC_IS_ALL_ZEROS(mac_entry->addr)) {
- dev_err(dsaf_dev->dev,
- "del_port failed, addr %02x:%02x:%02x:%02x:%02x:%02x!\n",
- mac_entry->addr[0], mac_entry->addr[1],
- mac_entry->addr[2], mac_entry->addr[3],
- mac_entry->addr[4], mac_entry->addr[5]);
+ dev_err(dsaf_dev->dev, "del_port failed, addr %pM!\n",
+ mac_entry->addr);
return -EINVAL;
}
@@ -1644,11 +1746,8 @@ int hns_dsaf_get_mac_uc_entry(struct dsaf_device *dsaf_dev,
/* check macaddr */
if (MAC_IS_ALL_ZEROS(mac_entry->addr) ||
MAC_IS_BROADCAST(mac_entry->addr)) {
- dev_err(dsaf_dev->dev,
- "get_entry failed,addr %02x:%02x:%02x:%02x:%02x:%02x\n",
- mac_entry->addr[0], mac_entry->addr[1],
- mac_entry->addr[2], mac_entry->addr[3],
- mac_entry->addr[4], mac_entry->addr[5]);
+ dev_err(dsaf_dev->dev, "get_entry failed,addr %pM\n",
+ mac_entry->addr);
return -EINVAL;
}
@@ -1695,11 +1794,8 @@ int hns_dsaf_get_mac_mc_entry(struct dsaf_device *dsaf_dev,
/*check mac addr */
if (MAC_IS_ALL_ZEROS(mac_entry->addr) ||
MAC_IS_BROADCAST(mac_entry->addr)) {
- dev_err(dsaf_dev->dev,
- "get_entry failed,addr %02x:%02x:%02x:%02x:%02x:%02x\n",
- mac_entry->addr[0], mac_entry->addr[1],
- mac_entry->addr[2], mac_entry->addr[3],
- mac_entry->addr[4], mac_entry->addr[5]);
+ dev_err(dsaf_dev->dev, "get_entry failed,addr %pM\n",
+ mac_entry->addr);
return -EINVAL;
}
@@ -2032,7 +2128,7 @@ void hns_dsaf_get_regs(struct dsaf_device *ddev, u32 port, void *data)
DSAF_INODE_VC1_IN_PKT_NUM_0_REG + port * 4);
/* dsaf inode registers */
- for (i = 0; i < DSAF_SBM_NUM / DSAF_COMM_CHN; i++) {
+ for (i = 0; i < HNS_DSAF_SBM_NUM(ddev) / DSAF_COMM_CHN; i++) {
j = i * DSAF_COMM_CHN + port;
p[232 + i] = dsaf_read_dev(ddev,
DSAF_SBM_CFG_REG_0_REG + j * 0x80);
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h
index b2b93484995c..31c312f9826e 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h
@@ -19,24 +19,20 @@ struct hns_mac_cb;
#define DSAF_DRV_NAME "hns_dsaf"
#define DSAF_MOD_VERSION "v1.0"
-#define ENABLE (0x1)
-#define DISABLE (0x0)
+#define HNS_DSAF_DEBUG_NW_REG_OFFSET 0x100000
-#define HNS_DSAF_DEBUG_NW_REG_OFFSET (0x100000)
+#define DSAF_BASE_INNER_PORT_NUM 127/* mac tbl qid*/
-#define DSAF_BASE_INNER_PORT_NUM (127) /* mac tbl qid*/
+#define DSAF_MAX_CHIP_NUM 2 /*max 2 chips */
-#define DSAF_MAX_CHIP_NUM (2) /*max 2 chips */
+#define DSAF_DEFAUTL_QUEUE_NUM_PER_PPE 22
-#define DSAF_DEFAUTL_QUEUE_NUM_PER_PPE (22)
+#define HNS_DSAF_MAX_DESC_CNT 1024
+#define HNS_DSAF_MIN_DESC_CNT 16
-#define HNS_DSAF_MAX_DESC_CNT (1024)
-#define HNS_DSAF_MIN_DESC_CNT (16)
+#define DSAF_INVALID_ENTRY_IDX 0xffff
-#define DSAF_INVALID_ENTRY_IDX (0xffff)
-
-#define DSAF_CFG_READ_CNT (30)
-#define DSAF_SRAM_INIT_FINISH_FLAG (0xff)
+#define DSAF_CFG_READ_CNT 30
#define MAC_NUM_OCTETS_PER_ADDR 6
@@ -274,10 +270,6 @@ struct dsaf_device {
struct device *dev;
struct hnae_ae_dev ae_dev;
- void *priv;
-
- int virq[DSAF_IRQ_NUM];
-
u8 __iomem *sc_base;
u8 __iomem *sds_base;
u8 __iomem *ppe_base;
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c
index 523e9b83d304..607c3be42241 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c
@@ -149,7 +149,11 @@ void hns_dsaf_ge_srst_by_port(struct dsaf_device *dsaf_dev, u32 port, u32 val)
if (port < DSAF_SERVICE_NW_NUM) {
reg_val_1 = 0x1 << port;
- reg_val_2 = 0x1041041 << port;
+ /* there is difference between V1 and V2 in register.*/
+ if (AE_IS_VER1(dsaf_dev->dsaf_ver))
+ reg_val_2 = 0x1041041 << port;
+ else
+ reg_val_2 = 0x2082082 << port;
if (val == 0) {
dsaf_write_reg(dsaf_dev->sc_base,
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c
index 67f33f185a44..f302ef9073c6 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c
@@ -19,6 +19,48 @@
#include "hns_dsaf_ppe.h"
+void hns_ppe_set_tso_enable(struct hns_ppe_cb *ppe_cb, u32 value)
+{
+ dsaf_set_dev_bit(ppe_cb, PPEV2_CFG_TSO_EN_REG, 0, !!value);
+}
+
+void hns_ppe_set_rss_key(struct hns_ppe_cb *ppe_cb,
+ const u32 rss_key[HNS_PPEV2_RSS_KEY_NUM])
+{
+ int key_item = 0;
+
+ for (key_item = 0; key_item < HNS_PPEV2_RSS_KEY_NUM; key_item++)
+ dsaf_write_dev(ppe_cb, PPEV2_RSS_KEY_REG + key_item * 0x4,
+ rss_key[key_item]);
+}
+
+void hns_ppe_set_indir_table(struct hns_ppe_cb *ppe_cb,
+ const u32 rss_tab[HNS_PPEV2_RSS_IND_TBL_SIZE])
+{
+ int i;
+ int reg_value;
+
+ for (i = 0; i < (HNS_PPEV2_RSS_IND_TBL_SIZE / 4); i++) {
+ reg_value = dsaf_read_dev(ppe_cb,
+ PPEV2_INDRECTION_TBL_REG + i * 0x4);
+
+ dsaf_set_field(reg_value, PPEV2_CFG_RSS_TBL_4N0_M,
+ PPEV2_CFG_RSS_TBL_4N0_S,
+ rss_tab[i * 4 + 0] & 0x1F);
+ dsaf_set_field(reg_value, PPEV2_CFG_RSS_TBL_4N1_M,
+ PPEV2_CFG_RSS_TBL_4N1_S,
+ rss_tab[i * 4 + 1] & 0x1F);
+ dsaf_set_field(reg_value, PPEV2_CFG_RSS_TBL_4N2_M,
+ PPEV2_CFG_RSS_TBL_4N2_S,
+ rss_tab[i * 4 + 2] & 0x1F);
+ dsaf_set_field(reg_value, PPEV2_CFG_RSS_TBL_4N3_M,
+ PPEV2_CFG_RSS_TBL_4N3_S,
+ rss_tab[i * 4 + 3] & 0x1F);
+ dsaf_write_dev(
+ ppe_cb, PPEV2_INDRECTION_TBL_REG + i * 0x4, reg_value);
+ }
+}
+
static void __iomem *hns_ppe_common_get_ioaddr(
struct ppe_common_cb *ppe_common)
{
@@ -134,6 +176,11 @@ static void hns_ppe_cnt_clr_ce(struct hns_ppe_cb *ppe_cb)
PPE_CNT_CLR_CE_B, 1);
}
+static void hns_ppe_set_vlan_strip(struct hns_ppe_cb *ppe_cb, int en)
+{
+ dsaf_write_dev(ppe_cb, PPEV2_VLAN_STRIP_EN_REG, en);
+}
+
/**
* hns_ppe_checksum_hw - set ppe checksum caculate
* @ppe_device: ppe device
@@ -266,13 +313,17 @@ static void hns_ppe_exc_irq_en(struct hns_ppe_cb *ppe_cb, int en)
/**
* ppe_init_hw - init ppe
- * @ppe_device: ppe device
+ * @ppe_cb: ppe device
*/
static void hns_ppe_init_hw(struct hns_ppe_cb *ppe_cb)
{
struct ppe_common_cb *ppe_common_cb = ppe_cb->ppe_common_cb;
u32 port = ppe_cb->port;
struct dsaf_device *dsaf_dev = ppe_common_cb->dsaf_dev;
+ int i;
+
+ /* get default RSS key */
+ netdev_rss_key_fill(ppe_cb->rss_key, HNS_PPEV2_RSS_KEY_SIZE);
hns_ppe_srst_by_port(dsaf_dev, port, 0);
mdelay(10);
@@ -285,8 +336,21 @@ static void hns_ppe_init_hw(struct hns_ppe_cb *ppe_cb)
hns_ppe_set_port_mode(ppe_cb, PPE_MODE_GE);
else
hns_ppe_set_port_mode(ppe_cb, PPE_MODE_XGE);
+
hns_ppe_checksum_hw(ppe_cb, 0xffffffff);
hns_ppe_cnt_clr_ce(ppe_cb);
+
+ if (!AE_IS_VER1(dsaf_dev->dsaf_ver)) {
+ hns_ppe_set_vlan_strip(ppe_cb, 0);
+
+ /* set default RSS key in h/w */
+ hns_ppe_set_rss_key(ppe_cb, ppe_cb->rss_key);
+
+ /* Set default indrection table in h/w */
+ for (i = 0; i < HNS_PPEV2_RSS_IND_TBL_SIZE; i++)
+ ppe_cb->rss_indir_table[i] = i;
+ hns_ppe_set_indir_table(ppe_cb, ppe_cb->rss_indir_table);
+ }
}
/**
@@ -341,13 +405,13 @@ void hns_ppe_reset_common(struct dsaf_device *dsaf_dev, u8 ppe_common_index)
if (ret)
return;
+ for (i = 0; i < ppe_common->ppe_num; i++)
+ hns_ppe_init_hw(&ppe_common->ppe_cb[i]);
+
ret = hns_rcb_common_init_hw(dsaf_dev->rcb_common[ppe_common_index]);
if (ret)
return;
- for (i = 0; i < ppe_common->ppe_num; i++)
- hns_ppe_init_hw(&ppe_common->ppe_cb[i]);
-
hns_rcb_common_init_commit_hw(dsaf_dev->rcb_common[ppe_common_index]);
}
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h
index 4894f9a0d39f..0f5cb6962acf 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h
@@ -25,15 +25,24 @@
#define ETH_PPE_DUMP_NUM 576
#define ETH_PPE_STATIC_NUM 12
+
+#define HNS_PPEV2_RSS_IND_TBL_SIZE 256
+#define HNS_PPEV2_RSS_KEY_SIZE 40 /* in bytes or 320 bits */
+#define HNS_PPEV2_RSS_KEY_NUM (HNS_PPEV2_RSS_KEY_SIZE / sizeof(u32))
+
enum ppe_qid_mode {
- PPE_QID_MODE0 = 0, /* fixed queue id mode */
- PPE_QID_MODE1, /* switch:128VM non switch:6Port/4VM/4TC */
- PPE_QID_MODE2, /* switch:32VM/4TC non switch:6Port/16VM */
- PPE_QID_MODE3, /* switch:4TC/8TAG non switch:2Port/64VM */
- PPE_QID_MODE4, /* switch:8VM/16TAG non switch:2Port/16VM/4TC */
- PPE_QID_MODE5, /* non switch:6Port/16TAG */
- PPE_QID_MODE6, /* non switch:6Port/2VM/8TC */
- PPE_QID_MODE7, /* non switch:2Port/8VM/8TC */
+ PPE_QID_MODE0 = 0, /* fixed queue id mode */
+ PPE_QID_MODE1, /* switch:128VM non switch:6Port/4VM/4TC */
+ PPE_QID_MODE2, /* switch:32VM/4TC non switch:6Port/16VM */
+ PPE_QID_MODE3, /* switch:4TC/8RSS non switch:2Port/64VM */
+ PPE_QID_MODE4, /* switch:8VM/16RSS non switch:2Port/16VM/4TC */
+ PPE_QID_MODE5, /* switch:16VM/8TC non switch:6Port/16RSS */
+ PPE_QID_MODE6, /* switch:32VM/4RSS non switch:6Port/2VM/8TC */
+ PPE_QID_MODE7, /* switch:32RSS non switch:2Port/8VM/8TC */
+ PPE_QID_MODE8, /* switch:6VM/4TC/4RSS non switch:2Port/16VM/4RSS */
+ PPE_QID_MODE9, /* non switch:2Port/32VM/2RSS */
+ PPE_QID_MODE10, /* non switch:2Port/32RSS */
+ PPE_QID_MODE11, /* non switch:2Port/4TC/16RSS */
};
enum ppe_port_mode {
@@ -72,6 +81,8 @@ struct hns_ppe_cb {
u8 port; /* port id in dsaf */
void __iomem *io_base;
int virq;
+ u32 rss_indir_table[HNS_PPEV2_RSS_IND_TBL_SIZE]; /*shadow indir tab */
+ u32 rss_key[HNS_PPEV2_RSS_KEY_NUM]; /* rss hash key */
};
struct ppe_common_cb {
@@ -102,4 +113,9 @@ void hns_ppe_get_regs(struct hns_ppe_cb *ppe_cb, void *data);
void hns_ppe_get_strings(struct hns_ppe_cb *ppe_cb, int stringset, u8 *data);
void hns_ppe_get_stats(struct hns_ppe_cb *ppe_cb, u64 *data);
+void hns_ppe_set_tso_enable(struct hns_ppe_cb *ppe_cb, u32 value);
+void hns_ppe_set_rss_key(struct hns_ppe_cb *ppe_cb,
+ const u32 rss_key[HNS_PPEV2_RSS_KEY_NUM]);
+void hns_ppe_set_indir_table(struct hns_ppe_cb *ppe_cb,
+ const u32 rss_tab[HNS_PPEV2_RSS_IND_TBL_SIZE]);
#endif /* _HNS_DSAF_PPE_H */
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c
index 4db32c62f062..d2263c72bd8a 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c
@@ -136,19 +136,37 @@ void hns_rcb_int_ctrl_hw(struct hnae_queue *q, u32 flag, u32 mask)
void hns_rcb_int_clr_hw(struct hnae_queue *q, u32 flag)
{
- u32 clr = 1;
-
if (flag & RCB_INT_FLAG_TX) {
- dsaf_write_dev(q, RCB_RING_INTSTS_TX_RING_REG, clr);
- dsaf_write_dev(q, RCB_RING_INTSTS_TX_OVERTIME_REG, clr);
+ dsaf_write_dev(q, RCB_RING_INTSTS_TX_RING_REG, 1);
+ dsaf_write_dev(q, RCB_RING_INTSTS_TX_OVERTIME_REG, 1);
}
if (flag & RCB_INT_FLAG_RX) {
- dsaf_write_dev(q, RCB_RING_INTSTS_RX_RING_REG, clr);
- dsaf_write_dev(q, RCB_RING_INTSTS_RX_OVERTIME_REG, clr);
+ dsaf_write_dev(q, RCB_RING_INTSTS_RX_RING_REG, 1);
+ dsaf_write_dev(q, RCB_RING_INTSTS_RX_OVERTIME_REG, 1);
}
}
+void hns_rcbv2_int_ctrl_hw(struct hnae_queue *q, u32 flag, u32 mask)
+{
+ u32 int_mask_en = !!mask;
+
+ if (flag & RCB_INT_FLAG_TX)
+ dsaf_write_dev(q, RCB_RING_INTMSK_TXWL_REG, int_mask_en);
+
+ if (flag & RCB_INT_FLAG_RX)
+ dsaf_write_dev(q, RCB_RING_INTMSK_RXWL_REG, int_mask_en);
+}
+
+void hns_rcbv2_int_clr_hw(struct hnae_queue *q, u32 flag)
+{
+ if (flag & RCB_INT_FLAG_TX)
+ dsaf_write_dev(q, RCBV2_TX_RING_INT_STS_REG, 1);
+
+ if (flag & RCB_INT_FLAG_RX)
+ dsaf_write_dev(q, RCBV2_RX_RING_INT_STS_REG, 1);
+}
+
/**
*hns_rcb_ring_enable_hw - enable ring
*@ring: rcb ring
@@ -193,6 +211,7 @@ static void hns_rcb_ring_init(struct ring_pair_cb *ring_pair, int ring_type)
(u32)dma);
dsaf_write_dev(q, RCB_RING_RX_RING_BASEADDR_H_REG,
(u32)((dma >> 31) >> 1));
+
dsaf_write_dev(q, RCB_RING_RX_RING_BD_LEN_REG,
bd_size_type);
dsaf_write_dev(q, RCB_RING_RX_RING_BD_NUM_REG,
@@ -204,6 +223,7 @@ static void hns_rcb_ring_init(struct ring_pair_cb *ring_pair, int ring_type)
(u32)dma);
dsaf_write_dev(q, RCB_RING_TX_RING_BASEADDR_H_REG,
(u32)((dma >> 31) >> 1));
+
dsaf_write_dev(q, RCB_RING_TX_RING_BD_LEN_REG,
bd_size_type);
dsaf_write_dev(q, RCB_RING_TX_RING_BD_NUM_REG,
@@ -232,9 +252,6 @@ void hns_rcb_init_hw(struct ring_pair_cb *ring)
static void hns_rcb_set_port_desc_cnt(struct rcb_common_cb *rcb_common,
u32 port_idx, u32 desc_cnt)
{
- if (port_idx >= HNS_RCB_SERVICE_NW_ENGINE_NUM)
- port_idx = 0;
-
dsaf_write_dev(rcb_common, RCB_CFG_BD_NUM_REG + port_idx * 4,
desc_cnt);
}
@@ -249,8 +266,6 @@ static int hns_rcb_set_port_coalesced_frames(struct rcb_common_cb *rcb_common,
u32 port_idx,
u32 coalesced_frames)
{
- if (port_idx >= HNS_RCB_SERVICE_NW_ENGINE_NUM)
- port_idx = 0;
if (coalesced_frames >= rcb_common->desc_num ||
coalesced_frames > HNS_RCB_MAX_COALESCED_FRAMES)
return -EINVAL;
@@ -354,6 +369,9 @@ int hns_rcb_common_init_hw(struct rcb_common_cb *rcb_common)
dsaf_write_dev(rcb_common, RCB_COM_CFG_ENDIAN_REG,
HNS_RCB_COMMON_ENDIAN);
+ dsaf_write_dev(rcb_common, RCB_COM_CFG_FNA_REG, 0x0);
+ dsaf_write_dev(rcb_common, RCB_COM_CFG_FA_REG, 0x1);
+
return 0;
}
@@ -387,19 +405,23 @@ static void hns_rcb_ring_get_cfg(struct hnae_queue *q, int ring_type)
struct rcb_common_cb *rcb_common;
struct ring_pair_cb *ring_pair_cb;
u32 buf_size;
- u16 desc_num;
- int irq_idx;
+ u16 desc_num, mdnum_ppkt;
+ bool irq_idx, is_ver1;
ring_pair_cb = container_of(q, struct ring_pair_cb, q);
+ is_ver1 = AE_IS_VER1(ring_pair_cb->rcb_common->dsaf_dev->dsaf_ver);
if (ring_type == RX_RING) {
ring = &q->rx_ring;
ring->io_base = ring_pair_cb->q.io_base;
irq_idx = HNS_RCB_IRQ_IDX_RX;
+ mdnum_ppkt = HNS_RCB_RING_MAX_BD_PER_PKT;
} else {
ring = &q->tx_ring;
ring->io_base = (u8 __iomem *)ring_pair_cb->q.io_base +
HNS_RCB_TX_REG_OFFSET;
irq_idx = HNS_RCB_IRQ_IDX_TX;
+ mdnum_ppkt = is_ver1 ? HNS_RCB_RING_MAX_TXBD_PER_PKT :
+ HNS_RCBV2_RING_MAX_TXBD_PER_PKT;
}
rcb_common = ring_pair_cb->rcb_common;
@@ -414,7 +436,7 @@ static void hns_rcb_ring_get_cfg(struct hnae_queue *q, int ring_type)
ring->buf_size = buf_size;
ring->desc_num = desc_num;
- ring->max_desc_num_per_pkt = HNS_RCB_RING_MAX_BD_PER_PKT;
+ ring->max_desc_num_per_pkt = mdnum_ppkt;
ring->max_raw_data_sz_per_desc = HNS_RCB_MAX_PKT_SIZE;
ring->max_pkt_size = HNS_RCB_MAX_PKT_SIZE;
ring->next_to_use = 0;
@@ -445,14 +467,22 @@ static int hns_rcb_get_port(struct rcb_common_cb *rcb_common, int ring_idx)
return port;
}
+#define SERVICE_RING_IRQ_IDX(v1) \
+ ((v1) ? HNS_SERVICE_RING_IRQ_IDX : HNSV2_SERVICE_RING_IRQ_IDX)
+#define DEBUG_RING_IRQ_IDX(v1) \
+ ((v1) ? HNS_DEBUG_RING_IRQ_IDX : HNSV2_DEBUG_RING_IRQ_IDX)
+#define DEBUG_RING_IRQ_OFFSET(v1) \
+ ((v1) ? HNS_DEBUG_RING_IRQ_OFFSET : HNSV2_DEBUG_RING_IRQ_OFFSET)
static int hns_rcb_get_base_irq_idx(struct rcb_common_cb *rcb_common)
{
int comm_index = rcb_common->comm_index;
+ bool is_ver1 = AE_IS_VER1(rcb_common->dsaf_dev->dsaf_ver);
if (comm_index == HNS_DSAF_COMM_SERVICE_NW_IDX)
- return HNS_SERVICE_RING_IRQ_IDX;
+ return SERVICE_RING_IRQ_IDX(is_ver1);
else
- return HNS_DEBUG_RING_IRQ_IDX + (comm_index - 1) * 2;
+ return DEBUG_RING_IRQ_IDX(is_ver1) +
+ (comm_index - 1) * DEBUG_RING_IRQ_OFFSET(is_ver1);
}
#define RCB_COMM_BASE_TO_RING_BASE(base, ringid)\
@@ -468,6 +498,9 @@ void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common)
u32 ring_num = rcb_common->ring_num;
int base_irq_idx = hns_rcb_get_base_irq_idx(rcb_common);
struct device_node *np = rcb_common->dsaf_dev->dev->of_node;
+ struct platform_device *pdev =
+ to_platform_device(rcb_common->dsaf_dev->dev);
+ bool is_ver1 = AE_IS_VER1(rcb_common->dsaf_dev->dsaf_ver);
for (i = 0; i < ring_num; i++) {
ring_pair_cb = &rcb_common->ring_pair_cb[i];
@@ -477,10 +510,12 @@ void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common)
ring_pair_cb->q.io_base =
RCB_COMM_BASE_TO_RING_BASE(rcb_common->io_base, i);
ring_pair_cb->port_id_in_dsa = hns_rcb_get_port(rcb_common, i);
- ring_pair_cb->virq[HNS_RCB_IRQ_IDX_TX]
- = irq_of_parse_and_map(np, base_irq_idx + i * 2);
- ring_pair_cb->virq[HNS_RCB_IRQ_IDX_RX]
- = irq_of_parse_and_map(np, base_irq_idx + i * 2 + 1);
+ ring_pair_cb->virq[HNS_RCB_IRQ_IDX_TX] =
+ is_ver1 ? irq_of_parse_and_map(np, base_irq_idx + i * 2) :
+ platform_get_irq(pdev, base_irq_idx + i * 3 + 1);
+ ring_pair_cb->virq[HNS_RCB_IRQ_IDX_RX] =
+ is_ver1 ? irq_of_parse_and_map(np, base_irq_idx + i * 2 + 1) :
+ platform_get_irq(pdev, base_irq_idx + i * 3);
ring_pair_cb->q.phy_base =
RCB_COMM_BASE_TO_RING_BASE(rcb_common->phy_base, i);
hns_rcb_ring_pair_get_cfg(ring_pair_cb);
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h
index 3a2afe2dd8bb..29041b18741a 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h
@@ -26,6 +26,8 @@ struct rcb_common_cb;
#define HNS_RCB_SERVICE_NW_ENGINE_NUM DSAF_COMM_CHN
#define HNS_RCB_DEBUG_NW_ENGINE_NUM 1
#define HNS_RCB_RING_MAX_BD_PER_PKT 3
+#define HNS_RCB_RING_MAX_TXBD_PER_PKT 3
+#define HNS_RCBV2_RING_MAX_TXBD_PER_PKT 8
#define HNS_RCB_MAX_PKT_SIZE MAC_MAX_MTU
#define HNS_RCB_RING_MAX_PENDING_BD 1024
@@ -106,13 +108,17 @@ void hns_rcb_common_free_cfg(struct dsaf_device *dsaf_dev, u32 comm_index);
int hns_rcb_common_init_hw(struct rcb_common_cb *rcb_common);
void hns_rcb_start(struct hnae_queue *q, u32 val);
void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common);
-void hns_rcb_common_init_commit_hw(struct rcb_common_cb *rcb_common);
void hns_rcb_get_queue_mode(enum dsaf_mode dsaf_mode, int comm_index,
u16 *max_vfn, u16 *max_q_per_vf);
+void hns_rcb_common_init_commit_hw(struct rcb_common_cb *rcb_common);
+
void hns_rcb_ring_enable_hw(struct hnae_queue *q, u32 val);
void hns_rcb_int_clr_hw(struct hnae_queue *q, u32 flag);
void hns_rcb_int_ctrl_hw(struct hnae_queue *q, u32 flag, u32 enable);
+void hns_rcbv2_int_ctrl_hw(struct hnae_queue *q, u32 flag, u32 mask);
+void hns_rcbv2_int_clr_hw(struct hnae_queue *q, u32 flag);
+
void hns_rcb_init_hw(struct ring_pair_cb *ring);
void hns_rcb_reset_ring_hw(struct hnae_queue *q);
void hns_rcb_wait_fbd_clean(struct hnae_queue **qs, int q_num, u32 flag);
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h
index b475e1bf2e6f..5d1b746e141d 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h
+++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h
@@ -10,21 +10,12 @@
#ifndef _DSAF_REG_H_
#define _DSAF_REG_H_
-#define HNS_GE_FIFO_ERR_INTNUM 8
-#define HNS_XGE_ERR_INTNUM 6
-#define HNS_RCB_COMM_ERR_INTNUM 12
-#define HNS_PPE_TNL_ERR_INTNUM 8
-#define HNS_DSAF_EVENT_INTNUM 21
-#define HNS_DEBUG_RING_INTNUM 4
-#define HNS_SERVICE_RING_INTNUM 256
-
-#define HNS_DEBUG_RING_IRQ_IDX (HNS_GE_FIFO_ERR_INTNUM + HNS_XGE_ERR_INTNUM +\
- HNS_RCB_COMM_ERR_INTNUM + HNS_PPE_TNL_ERR_INTNUM +\
- HNS_DSAF_EVENT_INTNUM)
-#define HNS_SERVICE_RING_IRQ_IDX (HNS_DEBUG_RING_IRQ_IDX +\
- HNS_DEBUG_RING_INTNUM)
-
-#define DSAF_IRQ_NUM 18
+#define HNS_DEBUG_RING_IRQ_IDX 55
+#define HNS_SERVICE_RING_IRQ_IDX 59
+#define HNS_DEBUG_RING_IRQ_OFFSET 2
+#define HNSV2_DEBUG_RING_IRQ_IDX 409
+#define HNSV2_SERVICE_RING_IRQ_IDX 25
+#define HNSV2_DEBUG_RING_IRQ_OFFSET 9
#define DSAF_MAX_PORT_NUM_PER_CHIP 8
#define DSAF_SERVICE_PORT_NUM_PER_DSAF 6
@@ -39,9 +30,15 @@
#define DSAF_GE_NUM ((DSAF_SERVICE_NW_NUM) + (DSAF_DEBUG_NW_NUM))
#define DSAF_PORT_NUM ((DSAF_SERVICE_NW_NUM) + (DSAF_DEBUG_NW_NUM))
#define DSAF_XGE_NUM DSAF_SERVICE_NW_NUM
+#define DSAF_PORT_TYPE_NUM 3
#define DSAF_NODE_NUM 18
#define DSAF_XOD_BIG_NUM DSAF_NODE_NUM
#define DSAF_SBM_NUM DSAF_NODE_NUM
+#define DSAFV2_SBM_NUM 8
+#define DSAFV2_SBM_XGE_CHN 6
+#define DSAFV2_SBM_PPE_CHN 1
+#define DASFV2_ROCEE_CRD_NUM 8
+
#define DSAF_VOQ_NUM DSAF_NODE_NUM
#define DSAF_INODE_NUM DSAF_NODE_NUM
#define DSAF_XOD_NUM 8
@@ -52,56 +49,56 @@
#define DSAF_TCAM_SUM 512
#define DSAF_LINE_SUM (2048 * 14)
-#define DSAF_SUB_SC_NT_SRAM_CLK_SEL_REG 0x100
-#define DSAF_SUB_SC_HILINK3_CRG_CTRL0_REG 0x180
-#define DSAF_SUB_SC_HILINK3_CRG_CTRL1_REG 0x184
-#define DSAF_SUB_SC_HILINK3_CRG_CTRL2_REG 0x188
-#define DSAF_SUB_SC_HILINK3_CRG_CTRL3_REG 0x18C
-#define DSAF_SUB_SC_HILINK4_CRG_CTRL0_REG 0x190
-#define DSAF_SUB_SC_HILINK4_CRG_CTRL1_REG 0x194
-#define DSAF_SUB_SC_DSAF_CLK_EN_REG 0x300
-#define DSAF_SUB_SC_DSAF_CLK_DIS_REG 0x304
-#define DSAF_SUB_SC_NT_CLK_EN_REG 0x308
-#define DSAF_SUB_SC_NT_CLK_DIS_REG 0x30C
-#define DSAF_SUB_SC_XGE_CLK_EN_REG 0x310
-#define DSAF_SUB_SC_XGE_CLK_DIS_REG 0x314
-#define DSAF_SUB_SC_GE_CLK_EN_REG 0x318
-#define DSAF_SUB_SC_GE_CLK_DIS_REG 0x31C
-#define DSAF_SUB_SC_PPE_CLK_EN_REG 0x320
-#define DSAF_SUB_SC_PPE_CLK_DIS_REG 0x324
-#define DSAF_SUB_SC_RCB_PPE_COM_CLK_EN_REG 0x350
-#define DSAF_SUB_SC_RCB_PPE_COM_CLK_DIS_REG 0x354
-#define DSAF_SUB_SC_XBAR_RESET_REQ_REG 0xA00
-#define DSAF_SUB_SC_XBAR_RESET_DREQ_REG 0xA04
-#define DSAF_SUB_SC_NT_RESET_REQ_REG 0xA08
-#define DSAF_SUB_SC_NT_RESET_DREQ_REG 0xA0C
-#define DSAF_SUB_SC_XGE_RESET_REQ_REG 0xA10
-#define DSAF_SUB_SC_XGE_RESET_DREQ_REG 0xA14
-#define DSAF_SUB_SC_GE_RESET_REQ0_REG 0xA18
-#define DSAF_SUB_SC_GE_RESET_DREQ0_REG 0xA1C
-#define DSAF_SUB_SC_GE_RESET_REQ1_REG 0xA20
-#define DSAF_SUB_SC_GE_RESET_DREQ1_REG 0xA24
-#define DSAF_SUB_SC_PPE_RESET_REQ_REG 0xA48
-#define DSAF_SUB_SC_PPE_RESET_DREQ_REG 0xA4C
-#define DSAF_SUB_SC_RCB_PPE_COM_RESET_REQ_REG 0xA88
-#define DSAF_SUB_SC_RCB_PPE_COM_RESET_DREQ_REG 0xA8C
-#define DSAF_SUB_SC_LIGHT_MODULE_DETECT_EN_REG 0x2060
-#define DSAF_SUB_SC_TCAM_MBIST_EN_REG 0x2300
-#define DSAF_SUB_SC_DSAF_CLK_ST_REG 0x5300
-#define DSAF_SUB_SC_NT_CLK_ST_REG 0x5304
-#define DSAF_SUB_SC_XGE_CLK_ST_REG 0x5308
-#define DSAF_SUB_SC_GE_CLK_ST_REG 0x530C
-#define DSAF_SUB_SC_PPE_CLK_ST_REG 0x5310
-#define DSAF_SUB_SC_ROCEE_CLK_ST_REG 0x5314
-#define DSAF_SUB_SC_CPU_CLK_ST_REG 0x5318
-#define DSAF_SUB_SC_RCB_PPE_COM_CLK_ST_REG 0x5328
-#define DSAF_SUB_SC_XBAR_RESET_ST_REG 0x5A00
-#define DSAF_SUB_SC_NT_RESET_ST_REG 0x5A04
-#define DSAF_SUB_SC_XGE_RESET_ST_REG 0x5A08
-#define DSAF_SUB_SC_GE_RESET_ST0_REG 0x5A0C
-#define DSAF_SUB_SC_GE_RESET_ST1_REG 0x5A10
-#define DSAF_SUB_SC_PPE_RESET_ST_REG 0x5A24
-#define DSAF_SUB_SC_RCB_PPE_COM_RESET_ST_REG 0x5A44
+#define DSAF_SUB_SC_NT_SRAM_CLK_SEL_REG 0x100
+#define DSAF_SUB_SC_HILINK3_CRG_CTRL0_REG 0x180
+#define DSAF_SUB_SC_HILINK3_CRG_CTRL1_REG 0x184
+#define DSAF_SUB_SC_HILINK3_CRG_CTRL2_REG 0x188
+#define DSAF_SUB_SC_HILINK3_CRG_CTRL3_REG 0x18C
+#define DSAF_SUB_SC_HILINK4_CRG_CTRL0_REG 0x190
+#define DSAF_SUB_SC_HILINK4_CRG_CTRL1_REG 0x194
+#define DSAF_SUB_SC_DSAF_CLK_EN_REG 0x300
+#define DSAF_SUB_SC_DSAF_CLK_DIS_REG 0x304
+#define DSAF_SUB_SC_NT_CLK_EN_REG 0x308
+#define DSAF_SUB_SC_NT_CLK_DIS_REG 0x30C
+#define DSAF_SUB_SC_XGE_CLK_EN_REG 0x310
+#define DSAF_SUB_SC_XGE_CLK_DIS_REG 0x314
+#define DSAF_SUB_SC_GE_CLK_EN_REG 0x318
+#define DSAF_SUB_SC_GE_CLK_DIS_REG 0x31C
+#define DSAF_SUB_SC_PPE_CLK_EN_REG 0x320
+#define DSAF_SUB_SC_PPE_CLK_DIS_REG 0x324
+#define DSAF_SUB_SC_RCB_PPE_COM_CLK_EN_REG 0x350
+#define DSAF_SUB_SC_RCB_PPE_COM_CLK_DIS_REG 0x354
+#define DSAF_SUB_SC_XBAR_RESET_REQ_REG 0xA00
+#define DSAF_SUB_SC_XBAR_RESET_DREQ_REG 0xA04
+#define DSAF_SUB_SC_NT_RESET_REQ_REG 0xA08
+#define DSAF_SUB_SC_NT_RESET_DREQ_REG 0xA0C
+#define DSAF_SUB_SC_XGE_RESET_REQ_REG 0xA10
+#define DSAF_SUB_SC_XGE_RESET_DREQ_REG 0xA14
+#define DSAF_SUB_SC_GE_RESET_REQ0_REG 0xA18
+#define DSAF_SUB_SC_GE_RESET_DREQ0_REG 0xA1C
+#define DSAF_SUB_SC_GE_RESET_REQ1_REG 0xA20
+#define DSAF_SUB_SC_GE_RESET_DREQ1_REG 0xA24
+#define DSAF_SUB_SC_PPE_RESET_REQ_REG 0xA48
+#define DSAF_SUB_SC_PPE_RESET_DREQ_REG 0xA4C
+#define DSAF_SUB_SC_RCB_PPE_COM_RESET_REQ_REG 0xA88
+#define DSAF_SUB_SC_RCB_PPE_COM_RESET_DREQ_REG 0xA8C
+#define DSAF_SUB_SC_LIGHT_MODULE_DETECT_EN_REG 0x2060
+#define DSAF_SUB_SC_TCAM_MBIST_EN_REG 0x2300
+#define DSAF_SUB_SC_DSAF_CLK_ST_REG 0x5300
+#define DSAF_SUB_SC_NT_CLK_ST_REG 0x5304
+#define DSAF_SUB_SC_XGE_CLK_ST_REG 0x5308
+#define DSAF_SUB_SC_GE_CLK_ST_REG 0x530C
+#define DSAF_SUB_SC_PPE_CLK_ST_REG 0x5310
+#define DSAF_SUB_SC_ROCEE_CLK_ST_REG 0x5314
+#define DSAF_SUB_SC_CPU_CLK_ST_REG 0x5318
+#define DSAF_SUB_SC_RCB_PPE_COM_CLK_ST_REG 0x5328
+#define DSAF_SUB_SC_XBAR_RESET_ST_REG 0x5A00
+#define DSAF_SUB_SC_NT_RESET_ST_REG 0x5A04
+#define DSAF_SUB_SC_XGE_RESET_ST_REG 0x5A08
+#define DSAF_SUB_SC_GE_RESET_ST0_REG 0x5A0C
+#define DSAF_SUB_SC_GE_RESET_ST1_REG 0x5A10
+#define DSAF_SUB_SC_PPE_RESET_ST_REG 0x5A24
+#define DSAF_SUB_SC_RCB_PPE_COM_RESET_ST_REG 0x5A44
/*serdes offset**/
#define HNS_MAC_HILINK3_REG DSAF_SUB_SC_HILINK3_CRG_CTRL0_REG
@@ -178,6 +175,7 @@
#define DSAF_SBM_BP_CFG_2_XGE_REG_0_REG 0x200C
#define DSAF_SBM_BP_CFG_2_PPE_REG_0_REG 0x230C
#define DSAF_SBM_BP_CFG_2_ROCEE_REG_0_REG 0x260C
+#define DSAFV2_SBM_BP_CFG_2_ROCEE_REG_0_REG 0x238C
#define DSAF_SBM_FREE_CNT_0_0_REG 0x2010
#define DSAF_SBM_FREE_CNT_1_0_REG 0x2014
#define DSAF_SBM_BP_CNT_0_0_REG 0x2018
@@ -319,6 +317,8 @@
#define PPE_CFG_TAG_GEN_REG 0x90
#define PPE_CFG_PARSE_TAG_REG 0x94
#define PPE_CFG_PRO_CHECK_EN_REG 0x98
+#define PPEV2_CFG_TSO_EN_REG 0xA0
+#define PPEV2_VLAN_STRIP_EN_REG 0xAC
#define PPE_INTEN_REG 0x100
#define PPE_RINT_REG 0x104
#define PPE_INTSTS_REG 0x108
@@ -351,6 +351,8 @@
#define PPE_ECO0_REG 0x32C
#define PPE_ECO1_REG 0x330
#define PPE_ECO2_REG 0x334
+#define PPEV2_INDRECTION_TBL_REG 0x800
+#define PPEV2_RSS_KEY_REG 0x900
#define RCB_COM_CFG_ENDIAN_REG 0x0
#define RCB_COM_CFG_SYS_FSH_REG 0xC
@@ -431,8 +433,10 @@
#define RCB_RING_INTMSK_RXWL_REG 0x000A0
#define RCB_RING_INTSTS_RX_RING_REG 0x000A4
+#define RCBV2_RX_RING_INT_STS_REG 0x000A8
#define RCB_RING_INTMSK_TXWL_REG 0x000AC
#define RCB_RING_INTSTS_TX_RING_REG 0x000B0
+#define RCBV2_TX_RING_INT_STS_REG 0x000B4
#define RCB_RING_INTMSK_RX_OVERTIME_REG 0x000B8
#define RCB_RING_INTSTS_RX_OVERTIME_REG 0x000BC
#define RCB_RING_INTMSK_TX_OVERTIME_REG 0x000C4
@@ -678,6 +682,10 @@
#define XGMAC_TRX_CORE_SRST_M 0x2080
+#define DSAF_SRAM_INIT_OVER_M 0xff
+#define DSAFV2_SRAM_INIT_OVER_M 0x3ff
+#define DSAF_SRAM_INIT_OVER_S 0
+
#define DSAF_CFG_EN_S 0
#define DSAF_CFG_TC_MODE_S 1
#define DSAF_CFG_CRC_EN_S 2
@@ -685,6 +693,7 @@
#define DSAF_CFG_MIX_MODE_S 4
#define DSAF_CFG_STP_MODE_S 5
#define DSAF_CFG_LOCA_ADDR_EN_S 6
+#define DSAFV2_CFG_VLAN_TAG_MODE_S 17
#define DSAF_CNT_CLR_CE_S 0
#define DSAF_SNAP_EN_S 1
@@ -707,6 +716,16 @@
#define DSAF_INODE_IN_PORT_NUM_M 7
#define DSAF_INODE_IN_PORT_NUM_S 0
+#define DSAFV2_INODE_IN_PORT1_NUM_M (7ULL << 3)
+#define DSAFV2_INODE_IN_PORT1_NUM_S 3
+#define DSAFV2_INODE_IN_PORT2_NUM_M (7ULL << 6)
+#define DSAFV2_INODE_IN_PORT2_NUM_S 6
+#define DSAFV2_INODE_IN_PORT3_NUM_M (7ULL << 9)
+#define DSAFV2_INODE_IN_PORT3_NUM_S 9
+#define DSAFV2_INODE_IN_PORT4_NUM_M (7ULL << 12)
+#define DSAFV2_INODE_IN_PORT4_NUM_S 12
+#define DSAFV2_INODE_IN_PORT5_NUM_M (7ULL << 15)
+#define DSAFV2_INODE_IN_PORT5_NUM_S 15
#define HNS_DSAF_I4TC_CFG 0x18688688
#define HNS_DSAF_I8TC_CFG 0x18FAC688
@@ -738,6 +757,33 @@
#define DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S 10
#define DSAF_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M (((1ULL << 10) - 1) << 10)
+#define DSAFV2_SBM_CFG0_VC1_MAX_BUF_NUM_S 0
+#define DSAFV2_SBM_CFG0_VC1_MAX_BUF_NUM_M (((1ULL << 9) - 1) << 0)
+#define DSAFV2_SBM_CFG0_VC0_MAX_BUF_NUM_S 9
+#define DSAFV2_SBM_CFG0_VC0_MAX_BUF_NUM_M (((1ULL << 9) - 1) << 9)
+#define DSAFV2_SBM_CFG0_COM_MAX_BUF_NUM_S 18
+#define DSAFV2_SBM_CFG0_COM_MAX_BUF_NUM_M (((1ULL << 10) - 1) << 18)
+
+#define DSAFV2_SBM_CFG1_TC4_MAX_BUF_NUM_S 0
+#define DSAFV2_SBM_CFG1_TC4_MAX_BUF_NUM_M (((1ULL << 9) - 1) << 0)
+#define DSAFV2_SBM_CFG1_TC0_MAX_BUF_NUM_S 9
+#define DSAFV2_SBM_CFG1_TC0_MAX_BUF_NUM_M (((1ULL << 9) - 1) << 9)
+
+#define DSAFV2_SBM_CFG2_SET_BUF_NUM_S 0
+#define DSAFV2_SBM_CFG2_SET_BUF_NUM_M (((1ULL << 9) - 1) << 0)
+#define DSAFV2_SBM_CFG2_RESET_BUF_NUM_S 9
+#define DSAFV2_SBM_CFG2_RESET_BUF_NUM_M (((1ULL << 9) - 1) << 9)
+
+#define DSAFV2_SBM_CFG3_SET_BUF_NUM_NO_PFC_S 0
+#define DSAFV2_SBM_CFG3_SET_BUF_NUM_NO_PFC_M (((1ULL << 9) - 1) << 0)
+#define DSAFV2_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S 9
+#define DSAFV2_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M (((1ULL << 9) - 1) << 9)
+
+#define DSAFV2_SBM_CFG4_SET_BUF_NUM_NO_PFC_S 0
+#define DSAFV2_SBM_CFG4_SET_BUF_NUM_NO_PFC_M (((1ULL << 9) - 1) << 0)
+#define DSAFV2_SBM_CFG4_RESET_BUF_NUM_NO_PFC_S 9
+#define DSAFV2_SBM_CFG4_RESET_BUF_NUM_NO_PFC_M (((1ULL << 9) - 1) << 9)
+
#define DSAF_TBL_TCAM_ADDR_S 0
#define DSAF_TBL_TCAM_ADDR_M ((1ULL << 9) - 1)
@@ -797,6 +843,18 @@
#define PPE_CFG_QID_MODE_CF_QID_MODE_S 8
#define PPE_CFG_QID_MODE_CF_QID_MODE_M (0x7 << PPE_CFG_QID_MODE_CF_QID_MODE_S)
+#define PPEV2_CFG_RSS_TBL_4N0_S 0
+#define PPEV2_CFG_RSS_TBL_4N0_M (((1UL << 5) - 1) << PPEV2_CFG_RSS_TBL_4N0_S)
+
+#define PPEV2_CFG_RSS_TBL_4N1_S 8
+#define PPEV2_CFG_RSS_TBL_4N1_M (((1UL << 5) - 1) << PPEV2_CFG_RSS_TBL_4N1_S)
+
+#define PPEV2_CFG_RSS_TBL_4N2_S 16
+#define PPEV2_CFG_RSS_TBL_4N2_M (((1UL << 5) - 1) << PPEV2_CFG_RSS_TBL_4N2_S)
+
+#define PPEV2_CFG_RSS_TBL_4N3_S 24
+#define PPEV2_CFG_RSS_TBL_4N3_M (((1UL << 5) - 1) << PPEV2_CFG_RSS_TBL_4N3_S)
+
#define PPE_CNT_CLR_CE_B 0
#define PPE_CNT_CLR_SNAP_EN_B 1
@@ -898,7 +956,7 @@
#define XGMAC_PAUSE_CTL_RSP_MODE_B 2
#define XGMAC_PAUSE_CTL_TX_XOFF_B 3
-static inline void dsaf_write_reg(void *base, u32 reg, u32 value)
+static inline void dsaf_write_reg(void __iomem *base, u32 reg, u32 value)
{
u8 __iomem *reg_addr = ACCESS_ONCE(base);
@@ -908,7 +966,7 @@ static inline void dsaf_write_reg(void *base, u32 reg, u32 value)
#define dsaf_write_dev(a, reg, value) \
dsaf_write_reg((a)->io_base, (reg), (value))
-static inline u32 dsaf_read_reg(u8 *base, u32 reg)
+static inline u32 dsaf_read_reg(u8 __iomem *base, u32 reg)
{
u8 __iomem *reg_addr = ACCESS_ONCE(base);
@@ -927,8 +985,8 @@ static inline u32 dsaf_read_reg(u8 *base, u32 reg)
#define dsaf_set_bit(origin, shift, val) \
dsaf_set_field((origin), (1ull << (shift)), (shift), (val))
-static inline void dsaf_set_reg_field(void *base, u32 reg, u32 mask, u32 shift,
- u32 val)
+static inline void dsaf_set_reg_field(void __iomem *base, u32 reg, u32 mask,
+ u32 shift, u32 val)
{
u32 origin = dsaf_read_reg(base, reg);
@@ -947,7 +1005,8 @@ static inline void dsaf_set_reg_field(void *base, u32 reg, u32 mask, u32 shift,
#define dsaf_get_bit(origin, shift) \
dsaf_get_field((origin), (1ull << (shift)), (shift))
-static inline u32 dsaf_get_reg_field(void *base, u32 reg, u32 mask, u32 shift)
+static inline u32 dsaf_get_reg_field(void __iomem *base, u32 reg, u32 mask,
+ u32 shift)
{
u32 origin;
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c
index 08cef0dfb5db..0e30846a24f8 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c
@@ -33,10 +33,105 @@
#define RCB_IRQ_NOT_INITED 0
#define RCB_IRQ_INITED 1
+#define HNS_BUFFER_SIZE_2048 2048
+
+#define BD_MAX_SEND_SIZE 8191
+#define SKB_TMP_LEN(SKB) \
+ (((SKB)->transport_header - (SKB)->mac_header) + tcp_hdrlen(SKB))
+
+static void fill_v2_desc(struct hnae_ring *ring, void *priv,
+ int size, dma_addr_t dma, int frag_end,
+ int buf_num, enum hns_desc_type type, int mtu)
+{
+ struct hnae_desc *desc = &ring->desc[ring->next_to_use];
+ struct hnae_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_use];
+ struct iphdr *iphdr;
+ struct ipv6hdr *ipv6hdr;
+ struct sk_buff *skb;
+ int skb_tmp_len;
+ __be16 protocol;
+ u8 bn_pid = 0;
+ u8 rrcfv = 0;
+ u8 ip_offset = 0;
+ u8 tvsvsn = 0;
+ u16 mss = 0;
+ u8 l4_len = 0;
+ u16 paylen = 0;
+
+ desc_cb->priv = priv;
+ desc_cb->length = size;
+ desc_cb->dma = dma;
+ desc_cb->type = type;
+
+ desc->addr = cpu_to_le64(dma);
+ desc->tx.send_size = cpu_to_le16((u16)size);
+
+ /*config bd buffer end */
+ hnae_set_bit(rrcfv, HNSV2_TXD_VLD_B, 1);
+ hnae_set_field(bn_pid, HNSV2_TXD_BUFNUM_M, 0, buf_num - 1);
+
+ if (type == DESC_TYPE_SKB) {
+ skb = (struct sk_buff *)priv;
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ skb_reset_mac_len(skb);
+ protocol = skb->protocol;
+ ip_offset = ETH_HLEN;
+
+ if (protocol == htons(ETH_P_8021Q)) {
+ ip_offset += VLAN_HLEN;
+ protocol = vlan_get_protocol(skb);
+ skb->protocol = protocol;
+ }
+
+ if (skb->protocol == htons(ETH_P_IP)) {
+ iphdr = ip_hdr(skb);
+ hnae_set_bit(rrcfv, HNSV2_TXD_L3CS_B, 1);
+ hnae_set_bit(rrcfv, HNSV2_TXD_L4CS_B, 1);
+
+ /* check for tcp/udp header */
+ if (iphdr->protocol == IPPROTO_TCP) {
+ hnae_set_bit(tvsvsn,
+ HNSV2_TXD_TSE_B, 1);
+ skb_tmp_len = SKB_TMP_LEN(skb);
+ l4_len = tcp_hdrlen(skb);
+ mss = mtu - skb_tmp_len - ETH_FCS_LEN;
+ paylen = skb->len - skb_tmp_len;
+ }
+ } else if (skb->protocol == htons(ETH_P_IPV6)) {
+ hnae_set_bit(tvsvsn, HNSV2_TXD_IPV6_B, 1);
+ ipv6hdr = ipv6_hdr(skb);
+ hnae_set_bit(rrcfv, HNSV2_TXD_L4CS_B, 1);
+
+ /* check for tcp/udp header */
+ if (ipv6hdr->nexthdr == IPPROTO_TCP) {
+ hnae_set_bit(tvsvsn,
+ HNSV2_TXD_TSE_B, 1);
+ skb_tmp_len = SKB_TMP_LEN(skb);
+ l4_len = tcp_hdrlen(skb);
+ mss = mtu - skb_tmp_len - ETH_FCS_LEN;
+ paylen = skb->len - skb_tmp_len;
+ }
+ }
+ desc->tx.ip_offset = ip_offset;
+ desc->tx.tse_vlan_snap_v6_sctp_nth = tvsvsn;
+ desc->tx.mss = cpu_to_le16(mss);
+ desc->tx.l4_len = l4_len;
+ desc->tx.paylen = cpu_to_le16(paylen);
+ }
+ }
+
+ hnae_set_bit(rrcfv, HNSV2_TXD_FE_B, frag_end);
+
+ desc->tx.bn_pid = bn_pid;
+ desc->tx.ra_ri_cs_fe_vld = rrcfv;
+
+ ring_ptr_move_fw(ring, next_to_use);
+}
static void fill_desc(struct hnae_ring *ring, void *priv,
int size, dma_addr_t dma, int frag_end,
- int buf_num, enum hns_desc_type type)
+ int buf_num, enum hns_desc_type type, int mtu)
{
struct hnae_desc *desc = &ring->desc[ring->next_to_use];
struct hnae_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_use];
@@ -100,47 +195,129 @@ static void unfill_desc(struct hnae_ring *ring)
ring_ptr_move_bw(ring, next_to_use);
}
-int hns_nic_net_xmit_hw(struct net_device *ndev,
- struct sk_buff *skb,
- struct hns_nic_ring_data *ring_data)
+static int hns_nic_maybe_stop_tx(
+ struct sk_buff **out_skb, int *bnum, struct hnae_ring *ring)
{
- struct hns_nic_priv *priv = netdev_priv(ndev);
- struct device *dev = priv->dev;
- struct hnae_ring *ring = ring_data->ring;
- struct netdev_queue *dev_queue;
- struct skb_frag_struct *frag;
+ struct sk_buff *skb = *out_skb;
+ struct sk_buff *new_skb = NULL;
int buf_num;
- dma_addr_t dma;
- int size, next_to_use;
- int i, j;
- struct sk_buff *new_skb;
-
- assert(ring->max_desc_num_per_pkt <= ring->desc_num);
/* no. of segments (plus a header) */
buf_num = skb_shinfo(skb)->nr_frags + 1;
if (unlikely(buf_num > ring->max_desc_num_per_pkt)) {
- if (ring_space(ring) < 1) {
- ring->stats.tx_busy++;
- goto out_net_tx_busy;
- }
+ if (ring_space(ring) < 1)
+ return -EBUSY;
new_skb = skb_copy(skb, GFP_ATOMIC);
- if (!new_skb) {
- ring->stats.sw_err_cnt++;
- netdev_err(ndev, "no memory to xmit!\n");
- goto out_err_tx_ok;
- }
+ if (!new_skb)
+ return -ENOMEM;
dev_kfree_skb_any(skb);
- skb = new_skb;
+ *out_skb = new_skb;
buf_num = 1;
- assert(skb_shinfo(skb)->nr_frags == 1);
} else if (buf_num > ring_space(ring)) {
+ return -EBUSY;
+ }
+
+ *bnum = buf_num;
+ return 0;
+}
+
+static int hns_nic_maybe_stop_tso(
+ struct sk_buff **out_skb, int *bnum, struct hnae_ring *ring)
+{
+ int i;
+ int size;
+ int buf_num;
+ int frag_num;
+ struct sk_buff *skb = *out_skb;
+ struct sk_buff *new_skb = NULL;
+ struct skb_frag_struct *frag;
+
+ size = skb_headlen(skb);
+ buf_num = (size + BD_MAX_SEND_SIZE - 1) / BD_MAX_SEND_SIZE;
+
+ frag_num = skb_shinfo(skb)->nr_frags;
+ for (i = 0; i < frag_num; i++) {
+ frag = &skb_shinfo(skb)->frags[i];
+ size = skb_frag_size(frag);
+ buf_num += (size + BD_MAX_SEND_SIZE - 1) / BD_MAX_SEND_SIZE;
+ }
+
+ if (unlikely(buf_num > ring->max_desc_num_per_pkt)) {
+ buf_num = (skb->len + BD_MAX_SEND_SIZE - 1) / BD_MAX_SEND_SIZE;
+ if (ring_space(ring) < buf_num)
+ return -EBUSY;
+ /* manual split the send packet */
+ new_skb = skb_copy(skb, GFP_ATOMIC);
+ if (!new_skb)
+ return -ENOMEM;
+ dev_kfree_skb_any(skb);
+ *out_skb = new_skb;
+
+ } else if (ring_space(ring) < buf_num) {
+ return -EBUSY;
+ }
+
+ *bnum = buf_num;
+ return 0;
+}
+
+static void fill_tso_desc(struct hnae_ring *ring, void *priv,
+ int size, dma_addr_t dma, int frag_end,
+ int buf_num, enum hns_desc_type type, int mtu)
+{
+ int frag_buf_num;
+ int sizeoflast;
+ int k;
+
+ frag_buf_num = (size + BD_MAX_SEND_SIZE - 1) / BD_MAX_SEND_SIZE;
+ sizeoflast = size % BD_MAX_SEND_SIZE;
+ sizeoflast = sizeoflast ? sizeoflast : BD_MAX_SEND_SIZE;
+
+ /* when the frag size is bigger than hardware, split this frag */
+ for (k = 0; k < frag_buf_num; k++)
+ fill_v2_desc(ring, priv,
+ (k == frag_buf_num - 1) ?
+ sizeoflast : BD_MAX_SEND_SIZE,
+ dma + BD_MAX_SEND_SIZE * k,
+ frag_end && (k == frag_buf_num - 1) ? 1 : 0,
+ buf_num,
+ (type == DESC_TYPE_SKB && !k) ?
+ DESC_TYPE_SKB : DESC_TYPE_PAGE,
+ mtu);
+}
+
+int hns_nic_net_xmit_hw(struct net_device *ndev,
+ struct sk_buff *skb,
+ struct hns_nic_ring_data *ring_data)
+{
+ struct hns_nic_priv *priv = netdev_priv(ndev);
+ struct device *dev = priv->dev;
+ struct hnae_ring *ring = ring_data->ring;
+ struct netdev_queue *dev_queue;
+ struct skb_frag_struct *frag;
+ int buf_num;
+ int seg_num;
+ dma_addr_t dma;
+ int size, next_to_use;
+ int i;
+
+ switch (priv->ops.maybe_stop_tx(&skb, &buf_num, ring)) {
+ case -EBUSY:
ring->stats.tx_busy++;
goto out_net_tx_busy;
+ case -ENOMEM:
+ ring->stats.sw_err_cnt++;
+ netdev_err(ndev, "no memory to xmit!\n");
+ goto out_err_tx_ok;
+ default:
+ break;
}
+
+ /* no. of segments (plus a header) */
+ seg_num = skb_shinfo(skb)->nr_frags + 1;
next_to_use = ring->next_to_use;
/* fill the first part */
@@ -151,11 +328,11 @@ int hns_nic_net_xmit_hw(struct net_device *ndev,
ring->stats.sw_err_cnt++;
goto out_err_tx_ok;
}
- fill_desc(ring, skb, size, dma, buf_num == 1 ? 1 : 0, buf_num,
- DESC_TYPE_SKB);
+ priv->ops.fill_desc(ring, skb, size, dma, seg_num == 1 ? 1 : 0,
+ buf_num, DESC_TYPE_SKB, ndev->mtu);
/* fill the fragments */
- for (i = 1; i < buf_num; i++) {
+ for (i = 1; i < seg_num; i++) {
frag = &skb_shinfo(skb)->frags[i - 1];
size = skb_frag_size(frag);
dma = skb_frag_dma_map(dev, frag, 0, size, DMA_TO_DEVICE);
@@ -164,8 +341,9 @@ int hns_nic_net_xmit_hw(struct net_device *ndev,
ring->stats.sw_err_cnt++;
goto out_map_frag_fail;
}
- fill_desc(ring, skb_frag_page(frag), size, dma,
- buf_num - 1 == i ? 1 : 0, buf_num, DESC_TYPE_PAGE);
+ priv->ops.fill_desc(ring, skb_frag_page(frag), size, dma,
+ seg_num - 1 == i ? 1 : 0, buf_num,
+ DESC_TYPE_PAGE, ndev->mtu);
}
/*complete translate all packets*/
@@ -182,19 +360,20 @@ int hns_nic_net_xmit_hw(struct net_device *ndev,
out_map_frag_fail:
- for (j = i - 1; j > 0; j--) {
+ while (ring->next_to_use != next_to_use) {
unfill_desc(ring);
- next_to_use = ring->next_to_use;
- dma_unmap_page(dev, ring->desc_cb[next_to_use].dma,
- ring->desc_cb[next_to_use].length,
- DMA_TO_DEVICE);
+ if (ring->next_to_use != next_to_use)
+ dma_unmap_page(dev,
+ ring->desc_cb[ring->next_to_use].dma,
+ ring->desc_cb[ring->next_to_use].length,
+ DMA_TO_DEVICE);
+ else
+ dma_unmap_single(dev,
+ ring->desc_cb[next_to_use].dma,
+ ring->desc_cb[next_to_use].length,
+ DMA_TO_DEVICE);
}
- unfill_desc(ring);
- next_to_use = ring->next_to_use;
- dma_unmap_single(dev, ring->desc_cb[next_to_use].dma,
- ring->desc_cb[next_to_use].length, DMA_TO_DEVICE);
-
out_err_tx_ok:
dev_kfree_skb_any(skb);
@@ -313,51 +492,110 @@ static unsigned int hns_nic_get_headlen(unsigned char *data, u32 flag,
return max_size;
}
-static void
-hns_nic_reuse_page(struct hnae_desc_cb *desc_cb, int tsize, int last_offset)
+static void hns_nic_reuse_page(struct sk_buff *skb, int i,
+ struct hnae_ring *ring, int pull_len,
+ struct hnae_desc_cb *desc_cb)
{
+ struct hnae_desc *desc;
+ int truesize, size;
+ int last_offset;
+ bool twobufs;
+
+ twobufs = ((PAGE_SIZE < 8192) && hnae_buf_size(ring) == HNS_BUFFER_SIZE_2048);
+
+ desc = &ring->desc[ring->next_to_clean];
+ size = le16_to_cpu(desc->rx.size);
+
+ if (twobufs) {
+ truesize = hnae_buf_size(ring);
+ } else {
+ truesize = ALIGN(size, L1_CACHE_BYTES);
+ last_offset = hnae_page_size(ring) - hnae_buf_size(ring);
+ }
+
+ skb_add_rx_frag(skb, i, desc_cb->priv, desc_cb->page_offset + pull_len,
+ size - pull_len, truesize - pull_len);
+
/* avoid re-using remote pages,flag default unreuse */
- if (likely(page_to_nid(desc_cb->priv) == numa_node_id())) {
- /* move offset up to the next cache line */
- desc_cb->page_offset += tsize;
+ if (unlikely(page_to_nid(desc_cb->priv) != numa_node_id()))
+ return;
+
+ if (twobufs) {
+ /* if we are only owner of page we can reuse it */
+ if (likely(page_count(desc_cb->priv) == 1)) {
+ /* flip page offset to other buffer */
+ desc_cb->page_offset ^= truesize;
- if (desc_cb->page_offset <= last_offset) {
desc_cb->reuse_flag = 1;
/* bump ref count on page before it is given*/
get_page(desc_cb->priv);
}
+ return;
+ }
+
+ /* move offset up to the next cache line */
+ desc_cb->page_offset += truesize;
+
+ if (desc_cb->page_offset <= last_offset) {
+ desc_cb->reuse_flag = 1;
+ /* bump ref count on page before it is given*/
+ get_page(desc_cb->priv);
}
}
+static void get_v2rx_desc_bnum(u32 bnum_flag, int *out_bnum)
+{
+ *out_bnum = hnae_get_field(bnum_flag,
+ HNS_RXD_BUFNUM_M, HNS_RXD_BUFNUM_S) + 1;
+}
+
+static void get_rx_desc_bnum(u32 bnum_flag, int *out_bnum)
+{
+ *out_bnum = hnae_get_field(bnum_flag,
+ HNS_RXD_BUFNUM_M, HNS_RXD_BUFNUM_S);
+}
+
static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data,
struct sk_buff **out_skb, int *out_bnum)
{
struct hnae_ring *ring = ring_data->ring;
struct net_device *ndev = ring_data->napi.dev;
+ struct hns_nic_priv *priv = netdev_priv(ndev);
struct sk_buff *skb;
struct hnae_desc *desc;
struct hnae_desc_cb *desc_cb;
unsigned char *va;
- int bnum, length, size, i, truesize, last_offset;
+ int bnum, length, i;
int pull_len;
u32 bnum_flag;
- last_offset = hnae_page_size(ring) - hnae_buf_size(ring);
desc = &ring->desc[ring->next_to_clean];
desc_cb = &ring->desc_cb[ring->next_to_clean];
- length = le16_to_cpu(desc->rx.pkt_len);
- bnum_flag = le32_to_cpu(desc->rx.ipoff_bnum_pid_flag);
- bnum = hnae_get_field(bnum_flag, HNS_RXD_BUFNUM_M, HNS_RXD_BUFNUM_S);
- *out_bnum = bnum;
+
+ prefetch(desc);
+
va = (unsigned char *)desc_cb->buf + desc_cb->page_offset;
- skb = *out_skb = napi_alloc_skb(&ring_data->napi, HNS_RX_HEAD_SIZE);
+ /* prefetch first cache line of first page */
+ prefetch(va);
+#if L1_CACHE_BYTES < 128
+ prefetch(va + L1_CACHE_BYTES);
+#endif
+
+ skb = *out_skb = napi_alloc_skb(&ring_data->napi,
+ HNS_RX_HEAD_SIZE);
if (unlikely(!skb)) {
netdev_err(ndev, "alloc rx skb fail\n");
ring->stats.sw_err_cnt++;
return -ENOMEM;
}
+ prefetchw(skb->data);
+ length = le16_to_cpu(desc->rx.pkt_len);
+ bnum_flag = le32_to_cpu(desc->rx.ipoff_bnum_pid_flag);
+ priv->ops.get_rxd_bnum(bnum_flag, &bnum);
+ *out_bnum = bnum;
+
if (length <= HNS_RX_HEAD_SIZE) {
memcpy(__skb_put(skb, length), va, ALIGN(length, sizeof(long)));
@@ -380,13 +618,7 @@ static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data,
memcpy(__skb_put(skb, pull_len), va,
ALIGN(pull_len, sizeof(long)));
- size = le16_to_cpu(desc->rx.size);
- truesize = ALIGN(size, L1_CACHE_BYTES);
- skb_add_rx_frag(skb, 0, desc_cb->priv,
- desc_cb->page_offset + pull_len,
- size - pull_len, truesize - pull_len);
-
- hns_nic_reuse_page(desc_cb, truesize, last_offset);
+ hns_nic_reuse_page(skb, 0, ring, pull_len, desc_cb);
ring_ptr_move_fw(ring, next_to_clean);
if (unlikely(bnum >= (int)MAX_SKB_FRAGS)) { /* check err*/
@@ -396,13 +628,8 @@ static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data,
for (i = 1; i < bnum; i++) {
desc = &ring->desc[ring->next_to_clean];
desc_cb = &ring->desc_cb[ring->next_to_clean];
- size = le16_to_cpu(desc->rx.size);
- truesize = ALIGN(size, L1_CACHE_BYTES);
- skb_add_rx_frag(skb, i, desc_cb->priv,
- desc_cb->page_offset,
- size, truesize);
- hns_nic_reuse_page(desc_cb, truesize, last_offset);
+ hns_nic_reuse_page(skb, i, ring, 0, desc_cb);
ring_ptr_move_fw(ring, next_to_clean);
}
}
@@ -540,20 +767,20 @@ recv:
}
/* make all data has been write before submit */
- if (clean_count > 0) {
- hns_nic_alloc_rx_buffers(ring_data, clean_count);
- clean_count = 0;
- }
-
if (recv_pkts < budget) {
ex_num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM);
- rmb(); /*complete read rx ring bd number*/
- if (ex_num > 0) {
- num += ex_num;
+
+ if (ex_num > clean_count) {
+ num += ex_num - clean_count;
+ rmb(); /*complete read rx ring bd number*/
goto recv;
}
}
+ /* make all data has been write before submit */
+ if (clean_count > 0)
+ hns_nic_alloc_rx_buffers(ring_data, clean_count);
+
return recv_pkts;
}
@@ -642,14 +869,20 @@ static int hns_nic_tx_poll_one(struct hns_nic_ring_data *ring_data,
bytes = 0;
pkts = 0;
- while (head != ring->next_to_clean)
+ while (head != ring->next_to_clean) {
hns_nic_reclaim_one_desc(ring, &bytes, &pkts);
+ /* issue prefetch for next Tx descriptor */
+ prefetch(&ring->desc_cb[ring->next_to_clean]);
+ }
NETIF_TX_UNLOCK(ndev);
dev_queue = netdev_get_tx_queue(ndev, ring_data->queue_index);
netdev_tx_completed_queue(dev_queue, pkts, bytes);
+ if (unlikely(priv->link && !netif_carrier_ok(ndev)))
+ netif_carrier_on(ndev);
+
if (unlikely(pkts && netif_carrier_ok(ndev) &&
(ring_space(ring) >= ring->max_desc_num_per_pkt * 2))) {
/* Make sure that anybody stopping the queue after this
@@ -716,6 +949,7 @@ static int hns_nic_common_poll(struct napi_struct *napi, int budget)
ring_data->ring, 0);
ring_data->fini_process(ring_data);
+ return 0;
}
return clean_complete;
@@ -848,15 +1082,58 @@ static void hns_nic_ring_close(struct net_device *netdev, int idx)
napi_disable(&priv->ring_data[idx].napi);
}
-static int hns_nic_init_irq(struct hns_nic_priv *priv)
+static void hns_set_irq_affinity(struct hns_nic_priv *priv)
{
struct hnae_handle *h = priv->ae_handle;
struct hns_nic_ring_data *rd;
int i;
- int ret;
int cpu;
cpumask_t mask;
+ /*diffrent irq banlance for 16core and 32core*/
+ if (h->q_num == num_possible_cpus()) {
+ for (i = 0; i < h->q_num * 2; i++) {
+ rd = &priv->ring_data[i];
+ if (cpu_online(rd->queue_index)) {
+ cpumask_clear(&mask);
+ cpu = rd->queue_index;
+ cpumask_set_cpu(cpu, &mask);
+ (void)irq_set_affinity_hint(rd->ring->irq,
+ &mask);
+ }
+ }
+ } else {
+ for (i = 0; i < h->q_num; i++) {
+ rd = &priv->ring_data[i];
+ if (cpu_online(rd->queue_index * 2)) {
+ cpumask_clear(&mask);
+ cpu = rd->queue_index * 2;
+ cpumask_set_cpu(cpu, &mask);
+ (void)irq_set_affinity_hint(rd->ring->irq,
+ &mask);
+ }
+ }
+
+ for (i = h->q_num; i < h->q_num * 2; i++) {
+ rd = &priv->ring_data[i];
+ if (cpu_online(rd->queue_index * 2 + 1)) {
+ cpumask_clear(&mask);
+ cpu = rd->queue_index * 2 + 1;
+ cpumask_set_cpu(cpu, &mask);
+ (void)irq_set_affinity_hint(rd->ring->irq,
+ &mask);
+ }
+ }
+ }
+}
+
+static int hns_nic_init_irq(struct hns_nic_priv *priv)
+{
+ struct hnae_handle *h = priv->ae_handle;
+ struct hns_nic_ring_data *rd;
+ int i;
+ int ret;
+
for (i = 0; i < h->q_num * 2; i++) {
rd = &priv->ring_data[i];
@@ -878,16 +1155,11 @@ static int hns_nic_init_irq(struct hns_nic_priv *priv)
}
disable_irq(rd->ring->irq);
rd->ring->irq_init_flag = RCB_IRQ_INITED;
-
- /*set cpu affinity*/
- if (cpu_online(rd->queue_index)) {
- cpumask_clear(&mask);
- cpu = rd->queue_index;
- cpumask_set_cpu(cpu, &mask);
- irq_set_affinity_hint(rd->ring->irq, &mask);
- }
}
+ /*set cpu affinity*/
+ hns_set_irq_affinity(priv);
+
return 0;
}
@@ -1136,6 +1408,51 @@ static int hns_nic_change_mtu(struct net_device *ndev, int new_mtu)
return ret;
}
+static int hns_nic_set_features(struct net_device *netdev,
+ netdev_features_t features)
+{
+ struct hns_nic_priv *priv = netdev_priv(netdev);
+ struct hnae_handle *h = priv->ae_handle;
+
+ switch (priv->enet_ver) {
+ case AE_VERSION_1:
+ if (features & (NETIF_F_TSO | NETIF_F_TSO6))
+ netdev_info(netdev, "enet v1 do not support tso!\n");
+ break;
+ default:
+ if (features & (NETIF_F_TSO | NETIF_F_TSO6)) {
+ priv->ops.fill_desc = fill_tso_desc;
+ priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tso;
+ /* The chip only support 7*4096 */
+ netif_set_gso_max_size(netdev, 7 * 4096);
+ h->dev->ops->set_tso_stats(h, 1);
+ } else {
+ priv->ops.fill_desc = fill_v2_desc;
+ priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tx;
+ h->dev->ops->set_tso_stats(h, 0);
+ }
+ break;
+ }
+ netdev->features = features;
+ return 0;
+}
+
+static netdev_features_t hns_nic_fix_features(
+ struct net_device *netdev, netdev_features_t features)
+{
+ struct hns_nic_priv *priv = netdev_priv(netdev);
+
+ switch (priv->enet_ver) {
+ case AE_VERSION_1:
+ features &= ~(NETIF_F_TSO | NETIF_F_TSO6 |
+ NETIF_F_HW_VLAN_CTAG_FILTER);
+ break;
+ default:
+ break;
+ }
+ return features;
+}
+
/**
* nic_set_multicast_list - set mutl mac address
* @netdev: net device
@@ -1231,6 +1548,8 @@ static const struct net_device_ops hns_nic_netdev_ops = {
.ndo_set_mac_address = hns_nic_net_set_mac_address,
.ndo_change_mtu = hns_nic_change_mtu,
.ndo_do_ioctl = hns_nic_do_ioctl,
+ .ndo_set_features = hns_nic_set_features,
+ .ndo_fix_features = hns_nic_fix_features,
.ndo_get_stats64 = hns_nic_get_stats64,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = hns_nic_poll_controller,
@@ -1315,22 +1634,26 @@ static void hns_nic_reset_subtask(struct hns_nic_priv *priv)
return;
hns_nic_dump(priv);
- netdev_info(priv->netdev, "Reset %s port\n",
- (type == HNAE_PORT_DEBUG ? "debug" : "business"));
+ netdev_info(priv->netdev, "try to reset %s port!\n",
+ (type == HNAE_PORT_DEBUG ? "debug" : "service"));
rtnl_lock();
/* put off any impending NetWatchDogTimeout */
priv->netdev->trans_start = jiffies;
- if (type == HNAE_PORT_DEBUG)
+ if (type == HNAE_PORT_DEBUG) {
hns_nic_net_reinit(priv->netdev);
+ } else {
+ netif_carrier_off(priv->netdev);
+ netif_tx_disable(priv->netdev);
+ }
rtnl_unlock();
}
/* for doing service complete*/
static void hns_nic_service_event_complete(struct hns_nic_priv *priv)
{
- assert(!test_bit(NIC_STATE_SERVICE_SCHED, &priv->state));
+ WARN_ON(!test_bit(NIC_STATE_SERVICE_SCHED, &priv->state));
smp_mb__before_atomic();
clear_bit(NIC_STATE_SERVICE_SCHED, &priv->state);
@@ -1435,8 +1758,9 @@ static void hns_nic_uninit_ring_data(struct hns_nic_priv *priv)
for (i = 0; i < h->q_num * 2; i++) {
netif_napi_del(&priv->ring_data[i].napi);
if (priv->ring_data[i].ring->irq_init_flag == RCB_IRQ_INITED) {
- irq_set_affinity_hint(priv->ring_data[i].ring->irq,
- NULL);
+ (void)irq_set_affinity_hint(
+ priv->ring_data[i].ring->irq,
+ NULL);
free_irq(priv->ring_data[i].ring->irq,
&priv->ring_data[i]);
}
@@ -1446,6 +1770,31 @@ static void hns_nic_uninit_ring_data(struct hns_nic_priv *priv)
kfree(priv->ring_data);
}
+static void hns_nic_set_priv_ops(struct net_device *netdev)
+{
+ struct hns_nic_priv *priv = netdev_priv(netdev);
+ struct hnae_handle *h = priv->ae_handle;
+
+ if (AE_IS_VER1(priv->enet_ver)) {
+ priv->ops.fill_desc = fill_desc;
+ priv->ops.get_rxd_bnum = get_rx_desc_bnum;
+ priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tx;
+ } else {
+ priv->ops.get_rxd_bnum = get_v2rx_desc_bnum;
+ if ((netdev->features & NETIF_F_TSO) ||
+ (netdev->features & NETIF_F_TSO6)) {
+ priv->ops.fill_desc = fill_tso_desc;
+ priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tso;
+ /* This chip only support 7*4096 */
+ netif_set_gso_max_size(netdev, 7 * 4096);
+ h->dev->ops->set_tso_stats(h, 1);
+ } else {
+ priv->ops.fill_desc = fill_v2_desc;
+ priv->ops.maybe_stop_tx = hns_nic_maybe_stop_tx;
+ }
+ }
+}
+
static int hns_nic_try_get_ae(struct net_device *ndev)
{
struct hns_nic_priv *priv = netdev_priv(ndev);
@@ -1473,6 +1822,8 @@ static int hns_nic_try_get_ae(struct net_device *ndev)
goto out_init_ring_data;
}
+ hns_nic_set_priv_ops(ndev);
+
ret = register_netdev(ndev);
if (ret) {
dev_err(priv->dev, "probe register netdev fail!\n");
@@ -1524,10 +1875,10 @@ static int hns_nic_dev_probe(struct platform_device *pdev)
priv->dev = dev;
priv->netdev = ndev;
- if (of_device_is_compatible(node, "hisilicon,hns-nic-v2"))
- priv->enet_ver = AE_VERSION_2;
- else
+ if (of_device_is_compatible(node, "hisilicon,hns-nic-v1"))
priv->enet_ver = AE_VERSION_1;
+ else
+ priv->enet_ver = AE_VERSION_2;
ret = of_property_read_string(node, "ae-name", &priv->ae_name);
if (ret)
@@ -1543,6 +1894,7 @@ static int hns_nic_dev_probe(struct platform_device *pdev)
ndev->priv_flags |= IFF_UNICAST_FLT;
ndev->netdev_ops = &hns_nic_netdev_ops;
hns_ethtool_set_ops(ndev);
+
ndev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO |
NETIF_F_GRO;
@@ -1550,6 +1902,17 @@ static int hns_nic_dev_probe(struct platform_device *pdev)
NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM;
ndev->vlan_features |= NETIF_F_SG | NETIF_F_GSO | NETIF_F_GRO;
+ switch (priv->enet_ver) {
+ case AE_VERSION_2:
+ ndev->features |= NETIF_F_TSO | NETIF_F_TSO6;
+ ndev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+ NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO |
+ NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6;
+ break;
+ default:
+ break;
+ }
+
SET_NETDEV_DEV(ndev, dev);
if (!dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)))
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.h b/drivers/net/ethernet/hisilicon/hns/hns_enet.h
index dae0ed19ac6d..4b75270f014e 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_enet.h
+++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.h
@@ -40,6 +40,16 @@ struct hns_nic_ring_data {
void (*fini_process)(struct hns_nic_ring_data *);
};
+/* compatible the difference between two versions */
+struct hns_nic_ops {
+ void (*fill_desc)(struct hnae_ring *ring, void *priv,
+ int size, dma_addr_t dma, int frag_end,
+ int buf_num, enum hns_desc_type type, int mtu);
+ int (*maybe_stop_tx)(struct sk_buff **out_skb,
+ int *bnum, struct hnae_ring *ring);
+ void (*get_rxd_bnum)(u32 bnum_flag, int *out_bnum);
+};
+
struct hns_nic_priv {
const char *ae_name;
u32 enet_ver;
@@ -51,6 +61,8 @@ struct hns_nic_priv {
struct device *dev;
struct hnae_handle *ae_handle;
+ struct hns_nic_ops ops;
+
/* the cb for nic to manage the ring buffer, the first half of the
* array is for tx_ring and vice versa for the second half
*/
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
index a0332129970b..3df22840fcd1 100644
--- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
+++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
@@ -11,7 +11,6 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-
#include "hns_enet.h"
#define HNS_PHY_PAGE_MDIX 0
@@ -72,24 +71,22 @@ static void hns_get_mdix_mode(struct net_device *net_dev,
struct hns_nic_priv *priv = netdev_priv(net_dev);
struct phy_device *phy_dev = priv->phy;
- if (!phy_dev || !phy_dev->bus) {
+ if (!phy_dev || !phy_dev->mdio.bus) {
cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID;
cmd->eth_tp_mdix = ETH_TP_MDI_INVALID;
return;
}
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_PHY_PAGE_REG,
- HNS_PHY_PAGE_MDIX);
+ phy_write(phy_dev, HNS_PHY_PAGE_REG, HNS_PHY_PAGE_MDIX);
- retval = mdiobus_read(phy_dev->bus, phy_dev->addr, HNS_PHY_CSC_REG);
+ retval = phy_read(phy_dev, HNS_PHY_CSC_REG);
mdix_ctrl = hnae_get_field(retval, PHY_MDIX_CTRL_M, PHY_MDIX_CTRL_S);
- retval = mdiobus_read(phy_dev->bus, phy_dev->addr, HNS_PHY_CSS_REG);
+ retval = phy_read(phy_dev, HNS_PHY_CSS_REG);
mdix = hnae_get_bit(retval, PHY_MDIX_STATUS_B);
is_resolved = hnae_get_bit(retval, PHY_SPEED_DUP_RESOLVE_B);
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_PHY_PAGE_REG,
- HNS_PHY_PAGE_COPPER);
+ phy_write(phy_dev, HNS_PHY_PAGE_REG, HNS_PHY_PAGE_COPPER);
switch (mdix_ctrl) {
case 0x0:
@@ -254,53 +251,36 @@ static int hns_nic_config_phy_loopback(struct phy_device *phy_dev, u8 en)
if (en) {
/* speed : 1000M */
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- HNS_PHY_PAGE_REG, 2);
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 21, 0x1046);
+ phy_write(phy_dev, HNS_PHY_PAGE_REG, 2);
+ phy_write(phy_dev, 21, 0x1046);
/* Force Master */
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 9, 0x1F00);
+ phy_write(phy_dev, 9, 0x1F00);
/* Soft-reset */
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 0, 0x9140);
+ phy_write(phy_dev, 0, 0x9140);
/* If autoneg disabled,two soft-reset operations */
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 0, 0x9140);
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 22, 0xFA);
+ phy_write(phy_dev, 0, 0x9140);
+ phy_write(phy_dev, 22, 0xFA);
/* Default is 0x0400 */
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 1, 0x418);
+ phy_write(phy_dev, 1, 0x418);
/* Force 1000M Link, Default is 0x0200 */
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 7, 0x20C);
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 22, 0);
+ phy_write(phy_dev, 7, 0x20C);
+ phy_write(phy_dev, 22, 0);
/* Enable MAC loop-back */
- val = (u16)mdiobus_read(phy_dev->bus, phy_dev->addr,
- COPPER_CONTROL_REG);
+ val = phy_read(phy_dev, COPPER_CONTROL_REG);
val |= PHY_LOOP_BACK;
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- COPPER_CONTROL_REG, val);
+ phy_write(phy_dev, COPPER_CONTROL_REG, val);
} else {
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 22, 0xFA);
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 1, 0x400);
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 7, 0x200);
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- 22, 0);
-
- val = (u16)mdiobus_read(phy_dev->bus, phy_dev->addr,
- COPPER_CONTROL_REG);
+ phy_write(phy_dev, 22, 0xFA);
+ phy_write(phy_dev, 1, 0x400);
+ phy_write(phy_dev, 7, 0x200);
+ phy_write(phy_dev, 22, 0);
+
+ val = phy_read(phy_dev, COPPER_CONTROL_REG);
val &= ~PHY_LOOP_BACK;
- (void)mdiobus_write(phy_dev->bus, phy_dev->addr,
- COPPER_CONTROL_REG, val);
+ phy_write(phy_dev, COPPER_CONTROL_REG, val);
}
return 0;
}
@@ -667,6 +647,7 @@ static void hns_nic_get_drvinfo(struct net_device *net_dev,
drvinfo->bus_info[ETHTOOL_BUSINFO_LEN - 1] = '\0';
strncpy(drvinfo->fw_version, "N/A", ETHTOOL_FWVERS_LEN);
+ drvinfo->eedump_len = 0;
}
/**
@@ -1018,16 +999,9 @@ int hns_phy_led_set(struct net_device *netdev, int value)
struct hns_nic_priv *priv = netdev_priv(netdev);
struct phy_device *phy_dev = priv->phy;
- if (!phy_dev->bus) {
- netdev_err(netdev, "phy_dev->bus is null!\n");
- return -EINVAL;
- }
- retval = mdiobus_write(phy_dev->bus, phy_dev->addr,
- HNS_PHY_PAGE_REG, HNS_PHY_PAGE_LED);
- retval = mdiobus_write(phy_dev->bus, phy_dev->addr, HNS_LED_FC_REG,
- value);
- retval = mdiobus_write(phy_dev->bus, phy_dev->addr,
- HNS_PHY_PAGE_REG, HNS_PHY_PAGE_COPPER);
+ retval = phy_write(phy_dev, HNS_PHY_PAGE_REG, HNS_PHY_PAGE_LED);
+ retval = phy_write(phy_dev, HNS_LED_FC_REG, value);
+ retval = phy_write(phy_dev, HNS_PHY_PAGE_REG, HNS_PHY_PAGE_COPPER);
if (retval) {
netdev_err(netdev, "mdiobus_write fail !\n");
return retval;
@@ -1052,19 +1026,15 @@ int hns_set_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state)
if (phy_dev)
switch (state) {
case ETHTOOL_ID_ACTIVE:
- ret = mdiobus_write(phy_dev->bus, phy_dev->addr,
- HNS_PHY_PAGE_REG,
- HNS_PHY_PAGE_LED);
+ ret = phy_write(phy_dev, HNS_PHY_PAGE_REG,
+ HNS_PHY_PAGE_LED);
if (ret)
return ret;
- priv->phy_led_val = (u16)mdiobus_read(phy_dev->bus,
- phy_dev->addr,
- HNS_LED_FC_REG);
+ priv->phy_led_val = phy_read(phy_dev, HNS_LED_FC_REG);
- ret = mdiobus_write(phy_dev->bus, phy_dev->addr,
- HNS_PHY_PAGE_REG,
- HNS_PHY_PAGE_COPPER);
+ ret = phy_write(phy_dev, HNS_PHY_PAGE_REG,
+ HNS_PHY_PAGE_COPPER);
if (ret)
return ret;
return 2;
@@ -1079,20 +1049,18 @@ int hns_set_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state)
return ret;
break;
case ETHTOOL_ID_INACTIVE:
- ret = mdiobus_write(phy_dev->bus, phy_dev->addr,
- HNS_PHY_PAGE_REG,
- HNS_PHY_PAGE_LED);
+ ret = phy_write(phy_dev, HNS_PHY_PAGE_REG,
+ HNS_PHY_PAGE_LED);
if (ret)
return ret;
- ret = mdiobus_write(phy_dev->bus, phy_dev->addr,
- HNS_LED_FC_REG, priv->phy_led_val);
+ ret = phy_write(phy_dev, HNS_LED_FC_REG,
+ priv->phy_led_val);
if (ret)
return ret;
- ret = mdiobus_write(phy_dev->bus, phy_dev->addr,
- HNS_PHY_PAGE_REG,
- HNS_PHY_PAGE_COPPER);
+ ret = phy_write(phy_dev, HNS_PHY_PAGE_REG,
+ HNS_PHY_PAGE_COPPER);
if (ret)
return ret;
break;
@@ -1187,6 +1155,95 @@ static int hns_nic_nway_reset(struct net_device *netdev)
return ret;
}
+static u32
+hns_get_rss_key_size(struct net_device *netdev)
+{
+ struct hns_nic_priv *priv = netdev_priv(netdev);
+ struct hnae_ae_ops *ops;
+ u32 ret;
+
+ if (AE_IS_VER1(priv->enet_ver)) {
+ netdev_err(netdev,
+ "RSS feature is not supported on this hardware\n");
+ return -EOPNOTSUPP;
+ }
+
+ ops = priv->ae_handle->dev->ops;
+ ret = ops->get_rss_key_size(priv->ae_handle);
+
+ return ret;
+}
+
+static u32
+hns_get_rss_indir_size(struct net_device *netdev)
+{
+ struct hns_nic_priv *priv = netdev_priv(netdev);
+ struct hnae_ae_ops *ops;
+ u32 ret;
+
+ if (AE_IS_VER1(priv->enet_ver)) {
+ netdev_err(netdev,
+ "RSS feature is not supported on this hardware\n");
+ return -EOPNOTSUPP;
+ }
+
+ ops = priv->ae_handle->dev->ops;
+ ret = ops->get_rss_indir_size(priv->ae_handle);
+
+ return ret;
+}
+
+static int
+hns_get_rss(struct net_device *netdev, u32 *indir, u8 *key, u8 *hfunc)
+{
+ struct hns_nic_priv *priv = netdev_priv(netdev);
+ struct hnae_ae_ops *ops;
+ int ret;
+
+ if (AE_IS_VER1(priv->enet_ver)) {
+ netdev_err(netdev,
+ "RSS feature is not supported on this hardware\n");
+ return -EOPNOTSUPP;
+ }
+
+ ops = priv->ae_handle->dev->ops;
+
+ if (!indir)
+ return 0;
+
+ ret = ops->get_rss(priv->ae_handle, indir, key, hfunc);
+
+ return 0;
+}
+
+static int
+hns_set_rss(struct net_device *netdev, const u32 *indir, const u8 *key,
+ const u8 hfunc)
+{
+ struct hns_nic_priv *priv = netdev_priv(netdev);
+ struct hnae_ae_ops *ops;
+ int ret;
+
+ if (AE_IS_VER1(priv->enet_ver)) {
+ netdev_err(netdev,
+ "RSS feature is not supported on this hardware\n");
+ return -EOPNOTSUPP;
+ }
+
+ ops = priv->ae_handle->dev->ops;
+
+ /* currently hfunc can only be Toeplitz hash */
+ if (key ||
+ (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
+ return -EOPNOTSUPP;
+ if (!indir)
+ return 0;
+
+ ret = ops->set_rss(priv->ae_handle, indir, key, hfunc);
+
+ return 0;
+}
+
static struct ethtool_ops hns_ethtool_ops = {
.get_drvinfo = hns_nic_get_drvinfo,
.get_link = hns_nic_get_link,
@@ -1206,6 +1263,10 @@ static struct ethtool_ops hns_ethtool_ops = {
.get_regs_len = hns_get_regs_len,
.get_regs = hns_get_regs,
.nway_reset = hns_nic_nway_reset,
+ .get_rxfh_key_size = hns_get_rss_key_size,
+ .get_rxfh_indir_size = hns_get_rss_indir_size,
+ .get_rxfh = hns_get_rss,
+ .set_rxfh = hns_set_rss,
};
void hns_ethtool_set_ops(struct net_device *ndev)
diff --git a/drivers/net/ethernet/hisilicon/hns_mdio.c b/drivers/net/ethernet/hisilicon/hns_mdio.c
index 37491c85bc42..58c96c412fe8 100644
--- a/drivers/net/ethernet/hisilicon/hns_mdio.c
+++ b/drivers/net/ethernet/hisilicon/hns_mdio.c
@@ -463,11 +463,6 @@ static int hns_mdio_probe(struct platform_device *pdev)
dev_warn(&pdev->dev, "no syscon hisilicon,peri-c-subctrl\n");
mdio_dev->subctrl_vbase = NULL;
}
- new_bus->irq = devm_kcalloc(&pdev->dev, PHY_MAX_ADDR,
- sizeof(int), GFP_KERNEL);
- if (!new_bus->irq)
- return -ENOMEM;
-
new_bus->parent = &pdev->dev;
platform_set_drvdata(pdev, new_bus);
diff --git a/drivers/net/ethernet/hp/hp100.c b/drivers/net/ethernet/hp/hp100.c
index ae6e30d39f0f..1d5c3e16d8f4 100644
--- a/drivers/net/ethernet/hp/hp100.c
+++ b/drivers/net/ethernet/hp/hp100.c
@@ -2843,7 +2843,7 @@ static void cleanup_dev(struct net_device *d)
}
#ifdef CONFIG_EISA
-static int __init hp100_eisa_probe (struct device *gendev)
+static int hp100_eisa_probe(struct device *gendev)
{
struct net_device *dev = alloc_etherdev(sizeof(struct hp100_private));
struct eisa_device *edev = to_eisa_device(gendev);
diff --git a/drivers/net/ethernet/ibm/Kconfig b/drivers/net/ethernet/ibm/Kconfig
index 99c1cebd002d..37dceabf8861 100644
--- a/drivers/net/ethernet/ibm/Kconfig
+++ b/drivers/net/ethernet/ibm/Kconfig
@@ -37,4 +37,14 @@ config EHEA
To compile the driver as a module, choose M here. The module
will be called ehea.
+config IBMVNIC
+ tristate "IBM Virtual NIC support"
+ depends on PPC_PSERIES
+ ---help---
+ This driver supports Virtual NIC adapters on IBM i and IBM System p
+ systems.
+
+ To compile this driver as a module, choose M here. The module will
+ be called ibmvnic.
+
endif # NET_VENDOR_IBM
diff --git a/drivers/net/ethernet/ibm/Makefile b/drivers/net/ethernet/ibm/Makefile
index 2f04e71a5926..447865c8b632 100644
--- a/drivers/net/ethernet/ibm/Makefile
+++ b/drivers/net/ethernet/ibm/Makefile
@@ -3,5 +3,6 @@
#
obj-$(CONFIG_IBMVETH) += ibmveth.o
+obj-$(CONFIG_IBMVNIC) += ibmvnic.o
obj-$(CONFIG_IBM_EMAC) += emac/
obj-$(CONFIG_EHEA) += ehea/
diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index 7af870a3c549..335417b4756b 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -169,7 +169,7 @@ static int ibmveth_alloc_buffer_pool(struct ibmveth_buff_pool *pool)
if (!pool->free_map)
return -1;
- pool->dma_addr = kmalloc(sizeof(dma_addr_t) * pool->size, GFP_KERNEL);
+ pool->dma_addr = kcalloc(pool->size, sizeof(dma_addr_t), GFP_KERNEL);
if (!pool->dma_addr) {
kfree(pool->free_map);
pool->free_map = NULL;
@@ -187,8 +187,6 @@ static int ibmveth_alloc_buffer_pool(struct ibmveth_buff_pool *pool)
return -1;
}
- memset(pool->dma_addr, 0, sizeof(dma_addr_t) * pool->size);
-
for (i = 0; i < pool->size; ++i)
pool->free_map[i] = i;
@@ -763,7 +761,7 @@ static netdev_features_t ibmveth_fix_features(struct net_device *dev,
*/
if (!(features & NETIF_F_RXCSUM))
- features &= ~NETIF_F_ALL_CSUM;
+ features &= ~NETIF_F_CSUM_MASK;
return features;
}
@@ -928,7 +926,8 @@ static int ibmveth_set_features(struct net_device *dev,
rc1 = ibmveth_set_csum_offload(dev, rx_csum);
if (rc1 && !adapter->rx_csum)
dev->features =
- features & ~(NETIF_F_ALL_CSUM | NETIF_F_RXCSUM);
+ features & ~(NETIF_F_CSUM_MASK |
+ NETIF_F_RXCSUM);
}
if (large_send != adapter->large_send) {
diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c
new file mode 100644
index 000000000000..7d6570843723
--- /dev/null
+++ b/drivers/net/ethernet/ibm/ibmvnic.c
@@ -0,0 +1,3585 @@
+/**************************************************************************/
+/* */
+/* IBM System i and System p Virtual NIC Device Driver */
+/* Copyright (C) 2014 IBM Corp. */
+/* Santiago Leon (santi_leon@yahoo.com) */
+/* Thomas Falcon (tlfalcon@linux.vnet.ibm.com) */
+/* John Allen (jallen@linux.vnet.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 */
+/* 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. */
+/* */
+/* This module contains the implementation of a virtual ethernet device */
+/* for use with IBM i/p Series LPAR Linux. It utilizes the logical LAN */
+/* option of the RS/6000 Platform Architecture to interface with virtual */
+/* ethernet NICs that are presented to the partition by the hypervisor. */
+/* */
+/* Messages are passed between the VNIC driver and the VNIC server using */
+/* Command/Response Queues (CRQs) and sub CRQs (sCRQs). CRQs are used to */
+/* issue and receive commands that initiate communication with the server */
+/* on driver initialization. Sub CRQs (sCRQs) are similar to CRQs, but */
+/* are used by the driver to notify the server that a packet is */
+/* ready for transmission or that a buffer has been added to receive a */
+/* packet. Subsequently, sCRQs are used by the server to notify the */
+/* driver that a packet transmission has been completed or that a packet */
+/* has been received and placed in a waiting buffer. */
+/* */
+/* In lieu of a more conventional "on-the-fly" DMA mapping strategy in */
+/* which skbs are DMA mapped and immediately unmapped when the transmit */
+/* or receive has been completed, the VNIC driver is required to use */
+/* "long term mapping". This entails that large, continuous DMA mapped */
+/* buffers are allocated on driver initialization and these buffers are */
+/* then continuously reused to pass skbs to and from the VNIC server. */
+/* */
+/**************************************************************************/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/completion.h>
+#include <linux/ioport.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/ethtool.h>
+#include <linux/proc_fs.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/irq.h>
+#include <linux/kthread.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/interrupt.h>
+#include <net/net_namespace.h>
+#include <asm/hvcall.h>
+#include <linux/atomic.h>
+#include <asm/vio.h>
+#include <asm/iommu.h>
+#include <linux/uaccess.h>
+#include <asm/firmware.h>
+#include <linux/seq_file.h>
+
+#include "ibmvnic.h"
+
+static const char ibmvnic_driver_name[] = "ibmvnic";
+static const char ibmvnic_driver_string[] = "IBM System i/p Virtual NIC Driver";
+
+MODULE_AUTHOR("Santiago Leon <santi_leon@yahoo.com>");
+MODULE_DESCRIPTION("IBM System i/p Virtual NIC Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(IBMVNIC_DRIVER_VERSION);
+
+static int ibmvnic_version = IBMVNIC_INITIAL_VERSION;
+static int ibmvnic_remove(struct vio_dev *);
+static void release_sub_crqs(struct ibmvnic_adapter *);
+static int ibmvnic_reset_crq(struct ibmvnic_adapter *);
+static int ibmvnic_send_crq_init(struct ibmvnic_adapter *);
+static int ibmvnic_reenable_crq_queue(struct ibmvnic_adapter *);
+static int ibmvnic_send_crq(struct ibmvnic_adapter *, union ibmvnic_crq *);
+static int send_subcrq(struct ibmvnic_adapter *adapter, u64 remote_handle,
+ union sub_crq *sub_crq);
+static irqreturn_t ibmvnic_interrupt_rx(int irq, void *instance);
+static int enable_scrq_irq(struct ibmvnic_adapter *,
+ struct ibmvnic_sub_crq_queue *);
+static int disable_scrq_irq(struct ibmvnic_adapter *,
+ struct ibmvnic_sub_crq_queue *);
+static int pending_scrq(struct ibmvnic_adapter *,
+ struct ibmvnic_sub_crq_queue *);
+static union sub_crq *ibmvnic_next_scrq(struct ibmvnic_adapter *,
+ struct ibmvnic_sub_crq_queue *);
+static int ibmvnic_poll(struct napi_struct *napi, int data);
+static void send_map_query(struct ibmvnic_adapter *adapter);
+static void send_request_map(struct ibmvnic_adapter *, dma_addr_t, __be32, u8);
+static void send_request_unmap(struct ibmvnic_adapter *, u8);
+
+struct ibmvnic_stat {
+ char name[ETH_GSTRING_LEN];
+ int offset;
+};
+
+#define IBMVNIC_STAT_OFF(stat) (offsetof(struct ibmvnic_adapter, stats) + \
+ offsetof(struct ibmvnic_statistics, stat))
+#define IBMVNIC_GET_STAT(a, off) (*((u64 *)(((unsigned long)(a)) + off)))
+
+static const struct ibmvnic_stat ibmvnic_stats[] = {
+ {"rx_packets", IBMVNIC_STAT_OFF(rx_packets)},
+ {"rx_bytes", IBMVNIC_STAT_OFF(rx_bytes)},
+ {"tx_packets", IBMVNIC_STAT_OFF(tx_packets)},
+ {"tx_bytes", IBMVNIC_STAT_OFF(tx_bytes)},
+ {"ucast_tx_packets", IBMVNIC_STAT_OFF(ucast_tx_packets)},
+ {"ucast_rx_packets", IBMVNIC_STAT_OFF(ucast_rx_packets)},
+ {"mcast_tx_packets", IBMVNIC_STAT_OFF(mcast_tx_packets)},
+ {"mcast_rx_packets", IBMVNIC_STAT_OFF(mcast_rx_packets)},
+ {"bcast_tx_packets", IBMVNIC_STAT_OFF(bcast_tx_packets)},
+ {"bcast_rx_packets", IBMVNIC_STAT_OFF(bcast_rx_packets)},
+ {"align_errors", IBMVNIC_STAT_OFF(align_errors)},
+ {"fcs_errors", IBMVNIC_STAT_OFF(fcs_errors)},
+ {"single_collision_frames", IBMVNIC_STAT_OFF(single_collision_frames)},
+ {"multi_collision_frames", IBMVNIC_STAT_OFF(multi_collision_frames)},
+ {"sqe_test_errors", IBMVNIC_STAT_OFF(sqe_test_errors)},
+ {"deferred_tx", IBMVNIC_STAT_OFF(deferred_tx)},
+ {"late_collisions", IBMVNIC_STAT_OFF(late_collisions)},
+ {"excess_collisions", IBMVNIC_STAT_OFF(excess_collisions)},
+ {"internal_mac_tx_errors", IBMVNIC_STAT_OFF(internal_mac_tx_errors)},
+ {"carrier_sense", IBMVNIC_STAT_OFF(carrier_sense)},
+ {"too_long_frames", IBMVNIC_STAT_OFF(too_long_frames)},
+ {"internal_mac_rx_errors", IBMVNIC_STAT_OFF(internal_mac_rx_errors)},
+};
+
+static long h_reg_sub_crq(unsigned long unit_address, unsigned long token,
+ unsigned long length, unsigned long *number,
+ unsigned long *irq)
+{
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+ long rc;
+
+ rc = plpar_hcall(H_REG_SUB_CRQ, retbuf, unit_address, token, length);
+ *number = retbuf[0];
+ *irq = retbuf[1];
+
+ return rc;
+}
+
+/* net_device_ops functions */
+
+static void init_rx_pool(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_rx_pool *rx_pool, int num, int index,
+ int buff_size, int active)
+{
+ netdev_dbg(adapter->netdev,
+ "Initializing rx_pool %d, %d buffs, %d bytes each\n",
+ index, num, buff_size);
+ rx_pool->size = num;
+ rx_pool->index = index;
+ rx_pool->buff_size = buff_size;
+ rx_pool->active = active;
+}
+
+static int alloc_long_term_buff(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_long_term_buff *ltb, int size)
+{
+ struct device *dev = &adapter->vdev->dev;
+
+ ltb->size = size;
+ ltb->buff = dma_alloc_coherent(dev, ltb->size, &ltb->addr,
+ GFP_KERNEL);
+
+ if (!ltb->buff) {
+ dev_err(dev, "Couldn't alloc long term buffer\n");
+ return -ENOMEM;
+ }
+ ltb->map_id = adapter->map_id;
+ adapter->map_id++;
+ send_request_map(adapter, ltb->addr,
+ ltb->size, ltb->map_id);
+ init_completion(&adapter->fw_done);
+ wait_for_completion(&adapter->fw_done);
+ return 0;
+}
+
+static void free_long_term_buff(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_long_term_buff *ltb)
+{
+ struct device *dev = &adapter->vdev->dev;
+
+ dma_free_coherent(dev, ltb->size, ltb->buff, ltb->addr);
+ send_request_unmap(adapter, ltb->map_id);
+}
+
+static int alloc_rx_pool(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_rx_pool *pool)
+{
+ struct device *dev = &adapter->vdev->dev;
+ int i;
+
+ pool->free_map = kcalloc(pool->size, sizeof(int), GFP_KERNEL);
+ if (!pool->free_map)
+ return -ENOMEM;
+
+ pool->rx_buff = kcalloc(pool->size, sizeof(struct ibmvnic_rx_buff),
+ GFP_KERNEL);
+
+ if (!pool->rx_buff) {
+ dev_err(dev, "Couldn't alloc rx buffers\n");
+ kfree(pool->free_map);
+ return -ENOMEM;
+ }
+
+ if (alloc_long_term_buff(adapter, &pool->long_term_buff,
+ pool->size * pool->buff_size)) {
+ kfree(pool->free_map);
+ kfree(pool->rx_buff);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < pool->size; ++i)
+ pool->free_map[i] = i;
+
+ atomic_set(&pool->available, 0);
+ pool->next_alloc = 0;
+ pool->next_free = 0;
+
+ return 0;
+}
+
+static void replenish_rx_pool(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_rx_pool *pool)
+{
+ int count = pool->size - atomic_read(&pool->available);
+ struct device *dev = &adapter->vdev->dev;
+ int buffers_added = 0;
+ unsigned long lpar_rc;
+ union sub_crq sub_crq;
+ struct sk_buff *skb;
+ unsigned int offset;
+ dma_addr_t dma_addr;
+ unsigned char *dst;
+ u64 *handle_array;
+ int shift = 0;
+ int index;
+ int i;
+
+ handle_array = (u64 *)((u8 *)(adapter->login_rsp_buf) +
+ be32_to_cpu(adapter->login_rsp_buf->
+ off_rxadd_subcrqs));
+
+ for (i = 0; i < count; ++i) {
+ skb = alloc_skb(pool->buff_size, GFP_ATOMIC);
+ if (!skb) {
+ dev_err(dev, "Couldn't replenish rx buff\n");
+ adapter->replenish_no_mem++;
+ break;
+ }
+
+ index = pool->free_map[pool->next_free];
+
+ if (pool->rx_buff[index].skb)
+ dev_err(dev, "Inconsistent free_map!\n");
+
+ /* Copy the skb to the long term mapped DMA buffer */
+ offset = index * pool->buff_size;
+ dst = pool->long_term_buff.buff + offset;
+ memset(dst, 0, pool->buff_size);
+ dma_addr = pool->long_term_buff.addr + offset;
+ pool->rx_buff[index].data = dst;
+
+ pool->free_map[pool->next_free] = IBMVNIC_INVALID_MAP;
+ pool->rx_buff[index].dma = dma_addr;
+ pool->rx_buff[index].skb = skb;
+ pool->rx_buff[index].pool_index = pool->index;
+ pool->rx_buff[index].size = pool->buff_size;
+
+ memset(&sub_crq, 0, sizeof(sub_crq));
+ sub_crq.rx_add.first = IBMVNIC_CRQ_CMD;
+ sub_crq.rx_add.correlator =
+ cpu_to_be64((u64)&pool->rx_buff[index]);
+ sub_crq.rx_add.ioba = cpu_to_be32(dma_addr);
+ sub_crq.rx_add.map_id = pool->long_term_buff.map_id;
+
+ /* The length field of the sCRQ is defined to be 24 bits so the
+ * buffer size needs to be left shifted by a byte before it is
+ * converted to big endian to prevent the last byte from being
+ * truncated.
+ */
+#ifdef __LITTLE_ENDIAN__
+ shift = 8;
+#endif
+ sub_crq.rx_add.len = cpu_to_be32(pool->buff_size << shift);
+
+ lpar_rc = send_subcrq(adapter, handle_array[pool->index],
+ &sub_crq);
+ if (lpar_rc != H_SUCCESS)
+ goto failure;
+
+ buffers_added++;
+ adapter->replenish_add_buff_success++;
+ pool->next_free = (pool->next_free + 1) % pool->size;
+ }
+ atomic_add(buffers_added, &pool->available);
+ return;
+
+failure:
+ dev_info(dev, "replenish pools failure\n");
+ pool->free_map[pool->next_free] = index;
+ pool->rx_buff[index].skb = NULL;
+ if (!dma_mapping_error(dev, dma_addr))
+ dma_unmap_single(dev, dma_addr, pool->buff_size,
+ DMA_FROM_DEVICE);
+
+ dev_kfree_skb_any(skb);
+ adapter->replenish_add_buff_failure++;
+ atomic_add(buffers_added, &pool->available);
+}
+
+static void replenish_pools(struct ibmvnic_adapter *adapter)
+{
+ int i;
+
+ if (adapter->migrated)
+ return;
+
+ adapter->replenish_task_cycles++;
+ for (i = 0; i < be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs);
+ i++) {
+ if (adapter->rx_pool[i].active)
+ replenish_rx_pool(adapter, &adapter->rx_pool[i]);
+ }
+}
+
+static void free_rx_pool(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_rx_pool *pool)
+{
+ int i;
+
+ kfree(pool->free_map);
+ pool->free_map = NULL;
+
+ if (!pool->rx_buff)
+ return;
+
+ for (i = 0; i < pool->size; i++) {
+ if (pool->rx_buff[i].skb) {
+ dev_kfree_skb_any(pool->rx_buff[i].skb);
+ pool->rx_buff[i].skb = NULL;
+ }
+ }
+ kfree(pool->rx_buff);
+ pool->rx_buff = NULL;
+}
+
+static int ibmvnic_open(struct net_device *netdev)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_tx_pool *tx_pool;
+ union ibmvnic_crq crq;
+ int rxadd_subcrqs;
+ u64 *size_array;
+ int tx_subcrqs;
+ int i, j;
+
+ rxadd_subcrqs =
+ be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs);
+ tx_subcrqs =
+ be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs);
+ size_array = (u64 *)((u8 *)(adapter->login_rsp_buf) +
+ be32_to_cpu(adapter->login_rsp_buf->
+ off_rxadd_buff_size));
+ adapter->map_id = 1;
+ adapter->napi = kcalloc(adapter->req_rx_queues,
+ sizeof(struct napi_struct), GFP_KERNEL);
+ if (!adapter->napi)
+ goto alloc_napi_failed;
+ for (i = 0; i < adapter->req_rx_queues; i++) {
+ netif_napi_add(netdev, &adapter->napi[i], ibmvnic_poll,
+ NAPI_POLL_WEIGHT);
+ napi_enable(&adapter->napi[i]);
+ }
+ adapter->rx_pool =
+ kcalloc(rxadd_subcrqs, sizeof(struct ibmvnic_rx_pool), GFP_KERNEL);
+
+ if (!adapter->rx_pool)
+ goto rx_pool_arr_alloc_failed;
+ send_map_query(adapter);
+ for (i = 0; i < rxadd_subcrqs; i++) {
+ init_rx_pool(adapter, &adapter->rx_pool[i],
+ IBMVNIC_BUFFS_PER_POOL, i,
+ be64_to_cpu(size_array[i]), 1);
+ if (alloc_rx_pool(adapter, &adapter->rx_pool[i])) {
+ dev_err(dev, "Couldn't alloc rx pool\n");
+ goto rx_pool_alloc_failed;
+ }
+ }
+ adapter->tx_pool =
+ kcalloc(tx_subcrqs, sizeof(struct ibmvnic_tx_pool), GFP_KERNEL);
+
+ if (!adapter->tx_pool)
+ goto tx_pool_arr_alloc_failed;
+ for (i = 0; i < tx_subcrqs; i++) {
+ tx_pool = &adapter->tx_pool[i];
+ tx_pool->tx_buff =
+ kcalloc(adapter->max_tx_entries_per_subcrq,
+ sizeof(struct ibmvnic_tx_buff), GFP_KERNEL);
+ if (!tx_pool->tx_buff)
+ goto tx_pool_alloc_failed;
+
+ if (alloc_long_term_buff(adapter, &tx_pool->long_term_buff,
+ adapter->max_tx_entries_per_subcrq *
+ adapter->req_mtu))
+ goto tx_ltb_alloc_failed;
+
+ tx_pool->free_map =
+ kcalloc(adapter->max_tx_entries_per_subcrq,
+ sizeof(int), GFP_KERNEL);
+ if (!tx_pool->free_map)
+ goto tx_fm_alloc_failed;
+
+ for (j = 0; j < adapter->max_tx_entries_per_subcrq; j++)
+ tx_pool->free_map[j] = j;
+
+ tx_pool->consumer_index = 0;
+ tx_pool->producer_index = 0;
+ }
+ adapter->bounce_buffer_size =
+ (netdev->mtu + ETH_HLEN - 1) / PAGE_SIZE + 1;
+ adapter->bounce_buffer = kmalloc(adapter->bounce_buffer_size,
+ GFP_KERNEL);
+ if (!adapter->bounce_buffer)
+ goto bounce_alloc_failed;
+
+ adapter->bounce_buffer_dma = dma_map_single(dev, adapter->bounce_buffer,
+ adapter->bounce_buffer_size,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, adapter->bounce_buffer_dma)) {
+ dev_err(dev, "Couldn't map tx bounce buffer\n");
+ goto bounce_map_failed;
+ }
+ replenish_pools(adapter);
+
+ /* We're ready to receive frames, enable the sub-crq interrupts and
+ * set the logical link state to up
+ */
+ for (i = 0; i < adapter->req_rx_queues; i++)
+ enable_scrq_irq(adapter, adapter->rx_scrq[i]);
+
+ for (i = 0; i < adapter->req_tx_queues; i++)
+ enable_scrq_irq(adapter, adapter->tx_scrq[i]);
+
+ memset(&crq, 0, sizeof(crq));
+ crq.logical_link_state.first = IBMVNIC_CRQ_CMD;
+ crq.logical_link_state.cmd = LOGICAL_LINK_STATE;
+ crq.logical_link_state.link_state = IBMVNIC_LOGICAL_LNK_UP;
+ ibmvnic_send_crq(adapter, &crq);
+
+ netif_start_queue(netdev);
+ return 0;
+
+bounce_map_failed:
+ kfree(adapter->bounce_buffer);
+bounce_alloc_failed:
+ i = tx_subcrqs - 1;
+ kfree(adapter->tx_pool[i].free_map);
+tx_fm_alloc_failed:
+ free_long_term_buff(adapter, &adapter->tx_pool[i].long_term_buff);
+tx_ltb_alloc_failed:
+ kfree(adapter->tx_pool[i].tx_buff);
+tx_pool_alloc_failed:
+ for (j = 0; j < i; j++) {
+ kfree(adapter->tx_pool[j].tx_buff);
+ free_long_term_buff(adapter,
+ &adapter->tx_pool[j].long_term_buff);
+ kfree(adapter->tx_pool[j].free_map);
+ }
+ kfree(adapter->tx_pool);
+ adapter->tx_pool = NULL;
+tx_pool_arr_alloc_failed:
+ i = rxadd_subcrqs;
+rx_pool_alloc_failed:
+ for (j = 0; j < i; j++) {
+ free_rx_pool(adapter, &adapter->rx_pool[j]);
+ free_long_term_buff(adapter,
+ &adapter->rx_pool[j].long_term_buff);
+ }
+ kfree(adapter->rx_pool);
+ adapter->rx_pool = NULL;
+rx_pool_arr_alloc_failed:
+ for (i = 0; i < adapter->req_rx_queues; i++)
+ napi_enable(&adapter->napi[i]);
+alloc_napi_failed:
+ return -ENOMEM;
+}
+
+static int ibmvnic_close(struct net_device *netdev)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+ struct device *dev = &adapter->vdev->dev;
+ union ibmvnic_crq crq;
+ int i;
+
+ adapter->closing = true;
+
+ for (i = 0; i < adapter->req_rx_queues; i++)
+ napi_disable(&adapter->napi[i]);
+
+ netif_stop_queue(netdev);
+
+ if (adapter->bounce_buffer) {
+ if (!dma_mapping_error(dev, adapter->bounce_buffer_dma)) {
+ dma_unmap_single(&adapter->vdev->dev,
+ adapter->bounce_buffer_dma,
+ adapter->bounce_buffer_size,
+ DMA_BIDIRECTIONAL);
+ adapter->bounce_buffer_dma = DMA_ERROR_CODE;
+ }
+ kfree(adapter->bounce_buffer);
+ adapter->bounce_buffer = NULL;
+ }
+
+ memset(&crq, 0, sizeof(crq));
+ crq.logical_link_state.first = IBMVNIC_CRQ_CMD;
+ crq.logical_link_state.cmd = LOGICAL_LINK_STATE;
+ crq.logical_link_state.link_state = IBMVNIC_LOGICAL_LNK_DN;
+ ibmvnic_send_crq(adapter, &crq);
+
+ for (i = 0; i < be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs);
+ i++) {
+ kfree(adapter->tx_pool[i].tx_buff);
+ free_long_term_buff(adapter,
+ &adapter->tx_pool[i].long_term_buff);
+ kfree(adapter->tx_pool[i].free_map);
+ }
+ kfree(adapter->tx_pool);
+ adapter->tx_pool = NULL;
+
+ for (i = 0; i < be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs);
+ i++) {
+ free_rx_pool(adapter, &adapter->rx_pool[i]);
+ free_long_term_buff(adapter,
+ &adapter->rx_pool[i].long_term_buff);
+ }
+ kfree(adapter->rx_pool);
+ adapter->rx_pool = NULL;
+
+ adapter->closing = false;
+
+ return 0;
+}
+
+static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+ int queue_num = skb_get_queue_mapping(skb);
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_tx_buff *tx_buff = NULL;
+ struct ibmvnic_tx_pool *tx_pool;
+ unsigned int tx_send_failed = 0;
+ unsigned int tx_map_failed = 0;
+ unsigned int tx_dropped = 0;
+ unsigned int tx_packets = 0;
+ unsigned int tx_bytes = 0;
+ dma_addr_t data_dma_addr;
+ struct netdev_queue *txq;
+ bool used_bounce = false;
+ unsigned long lpar_rc;
+ union sub_crq tx_crq;
+ unsigned int offset;
+ unsigned char *dst;
+ u64 *handle_array;
+ int index = 0;
+ int ret = 0;
+
+ tx_pool = &adapter->tx_pool[queue_num];
+ txq = netdev_get_tx_queue(netdev, skb_get_queue_mapping(skb));
+ handle_array = (u64 *)((u8 *)(adapter->login_rsp_buf) +
+ be32_to_cpu(adapter->login_rsp_buf->
+ off_txsubm_subcrqs));
+ if (adapter->migrated) {
+ tx_send_failed++;
+ tx_dropped++;
+ ret = NETDEV_TX_BUSY;
+ goto out;
+ }
+
+ index = tx_pool->free_map[tx_pool->consumer_index];
+ offset = index * adapter->req_mtu;
+ dst = tx_pool->long_term_buff.buff + offset;
+ memset(dst, 0, adapter->req_mtu);
+ skb_copy_from_linear_data(skb, dst, skb->len);
+ data_dma_addr = tx_pool->long_term_buff.addr + offset;
+
+ tx_pool->consumer_index =
+ (tx_pool->consumer_index + 1) %
+ adapter->max_tx_entries_per_subcrq;
+
+ tx_buff = &tx_pool->tx_buff[index];
+ tx_buff->skb = skb;
+ tx_buff->data_dma[0] = data_dma_addr;
+ tx_buff->data_len[0] = skb->len;
+ tx_buff->index = index;
+ tx_buff->pool_index = queue_num;
+ tx_buff->last_frag = true;
+ tx_buff->used_bounce = used_bounce;
+
+ memset(&tx_crq, 0, sizeof(tx_crq));
+ tx_crq.v1.first = IBMVNIC_CRQ_CMD;
+ tx_crq.v1.type = IBMVNIC_TX_DESC;
+ tx_crq.v1.n_crq_elem = 1;
+ tx_crq.v1.n_sge = 1;
+ tx_crq.v1.flags1 = IBMVNIC_TX_COMP_NEEDED;
+ tx_crq.v1.correlator = cpu_to_be32(index);
+ tx_crq.v1.dma_reg = cpu_to_be16(tx_pool->long_term_buff.map_id);
+ tx_crq.v1.sge_len = cpu_to_be32(skb->len);
+ tx_crq.v1.ioba = cpu_to_be64(data_dma_addr);
+
+ if (adapter->vlan_header_insertion) {
+ tx_crq.v1.flags2 |= IBMVNIC_TX_VLAN_INSERT;
+ tx_crq.v1.vlan_id = cpu_to_be16(skb->vlan_tci);
+ }
+
+ if (skb->protocol == htons(ETH_P_IP)) {
+ if (ip_hdr(skb)->version == 4)
+ tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_IPV4;
+ else if (ip_hdr(skb)->version == 6)
+ tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_IPV6;
+
+ if (ip_hdr(skb)->protocol == IPPROTO_TCP)
+ tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_TCP;
+ else if (ip_hdr(skb)->protocol != IPPROTO_TCP)
+ tx_crq.v1.flags1 |= IBMVNIC_TX_PROT_UDP;
+ }
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ tx_crq.v1.flags1 |= IBMVNIC_TX_CHKSUM_OFFLOAD;
+
+ lpar_rc = send_subcrq(adapter, handle_array[0], &tx_crq);
+
+ if (lpar_rc != H_SUCCESS) {
+ dev_err(dev, "tx failed with code %ld\n", lpar_rc);
+
+ if (tx_pool->consumer_index == 0)
+ tx_pool->consumer_index =
+ adapter->max_tx_entries_per_subcrq - 1;
+ else
+ tx_pool->consumer_index--;
+
+ tx_send_failed++;
+ tx_dropped++;
+ ret = NETDEV_TX_BUSY;
+ goto out;
+ }
+ tx_packets++;
+ tx_bytes += skb->len;
+ txq->trans_start = jiffies;
+ ret = NETDEV_TX_OK;
+
+out:
+ netdev->stats.tx_dropped += tx_dropped;
+ netdev->stats.tx_bytes += tx_bytes;
+ netdev->stats.tx_packets += tx_packets;
+ adapter->tx_send_failed += tx_send_failed;
+ adapter->tx_map_failed += tx_map_failed;
+
+ return ret;
+}
+
+static void ibmvnic_set_multi(struct net_device *netdev)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+ struct netdev_hw_addr *ha;
+ union ibmvnic_crq crq;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.request_capability.first = IBMVNIC_CRQ_CMD;
+ crq.request_capability.cmd = REQUEST_CAPABILITY;
+
+ if (netdev->flags & IFF_PROMISC) {
+ if (!adapter->promisc_supported)
+ return;
+ } else {
+ if (netdev->flags & IFF_ALLMULTI) {
+ /* Accept all multicast */
+ memset(&crq, 0, sizeof(crq));
+ crq.multicast_ctrl.first = IBMVNIC_CRQ_CMD;
+ crq.multicast_ctrl.cmd = MULTICAST_CTRL;
+ crq.multicast_ctrl.flags = IBMVNIC_ENABLE_ALL;
+ ibmvnic_send_crq(adapter, &crq);
+ } else if (netdev_mc_empty(netdev)) {
+ /* Reject all multicast */
+ memset(&crq, 0, sizeof(crq));
+ crq.multicast_ctrl.first = IBMVNIC_CRQ_CMD;
+ crq.multicast_ctrl.cmd = MULTICAST_CTRL;
+ crq.multicast_ctrl.flags = IBMVNIC_DISABLE_ALL;
+ ibmvnic_send_crq(adapter, &crq);
+ } else {
+ /* Accept one or more multicast(s) */
+ netdev_for_each_mc_addr(ha, netdev) {
+ memset(&crq, 0, sizeof(crq));
+ crq.multicast_ctrl.first = IBMVNIC_CRQ_CMD;
+ crq.multicast_ctrl.cmd = MULTICAST_CTRL;
+ crq.multicast_ctrl.flags = IBMVNIC_ENABLE_MC;
+ ether_addr_copy(&crq.multicast_ctrl.mac_addr[0],
+ ha->addr);
+ ibmvnic_send_crq(adapter, &crq);
+ }
+ }
+ }
+}
+
+static int ibmvnic_set_mac(struct net_device *netdev, void *p)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+ struct sockaddr *addr = p;
+ union ibmvnic_crq crq;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.change_mac_addr.first = IBMVNIC_CRQ_CMD;
+ crq.change_mac_addr.cmd = CHANGE_MAC_ADDR;
+ ether_addr_copy(&crq.change_mac_addr.mac_addr[0], addr->sa_data);
+ ibmvnic_send_crq(adapter, &crq);
+ /* netdev->dev_addr is changed in handle_change_mac_rsp function */
+ return 0;
+}
+
+static int ibmvnic_change_mtu(struct net_device *netdev, int new_mtu)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+
+ if (new_mtu > adapter->req_mtu || new_mtu < adapter->min_mtu)
+ return -EINVAL;
+
+ netdev->mtu = new_mtu;
+ return 0;
+}
+
+static void ibmvnic_tx_timeout(struct net_device *dev)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(dev);
+ int rc;
+
+ /* Adapter timed out, resetting it */
+ release_sub_crqs(adapter);
+ rc = ibmvnic_reset_crq(adapter);
+ if (rc)
+ dev_err(&adapter->vdev->dev, "Adapter timeout, reset failed\n");
+ else
+ ibmvnic_send_crq_init(adapter);
+}
+
+static void remove_buff_from_pool(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_rx_buff *rx_buff)
+{
+ struct ibmvnic_rx_pool *pool = &adapter->rx_pool[rx_buff->pool_index];
+
+ rx_buff->skb = NULL;
+
+ pool->free_map[pool->next_alloc] = (int)(rx_buff - pool->rx_buff);
+ pool->next_alloc = (pool->next_alloc + 1) % pool->size;
+
+ atomic_dec(&pool->available);
+}
+
+static int ibmvnic_poll(struct napi_struct *napi, int budget)
+{
+ struct net_device *netdev = napi->dev;
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+ int scrq_num = (int)(napi - adapter->napi);
+ int frames_processed = 0;
+restart_poll:
+ while (frames_processed < budget) {
+ struct sk_buff *skb;
+ struct ibmvnic_rx_buff *rx_buff;
+ union sub_crq *next;
+ u32 length;
+ u16 offset;
+ u8 flags = 0;
+
+ if (!pending_scrq(adapter, adapter->rx_scrq[scrq_num]))
+ break;
+ next = ibmvnic_next_scrq(adapter, adapter->rx_scrq[scrq_num]);
+ rx_buff =
+ (struct ibmvnic_rx_buff *)be64_to_cpu(next->
+ rx_comp.correlator);
+ /* do error checking */
+ if (next->rx_comp.rc) {
+ netdev_err(netdev, "rx error %x\n", next->rx_comp.rc);
+ /* free the entry */
+ next->rx_comp.first = 0;
+ remove_buff_from_pool(adapter, rx_buff);
+ break;
+ }
+
+ length = be32_to_cpu(next->rx_comp.len);
+ offset = be16_to_cpu(next->rx_comp.off_frame_data);
+ flags = next->rx_comp.flags;
+ skb = rx_buff->skb;
+ skb_copy_to_linear_data(skb, rx_buff->data + offset,
+ length);
+ skb->vlan_tci = be16_to_cpu(next->rx_comp.vlan_tci);
+ /* free the entry */
+ next->rx_comp.first = 0;
+ remove_buff_from_pool(adapter, rx_buff);
+
+ skb_put(skb, length);
+ skb->protocol = eth_type_trans(skb, netdev);
+
+ if (flags & IBMVNIC_IP_CHKSUM_GOOD &&
+ flags & IBMVNIC_TCP_UDP_CHKSUM_GOOD) {
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ }
+
+ length = skb->len;
+ napi_gro_receive(napi, skb); /* send it up */
+ netdev->stats.rx_packets++;
+ netdev->stats.rx_bytes += length;
+ frames_processed++;
+ }
+ replenish_pools(adapter);
+
+ if (frames_processed < budget) {
+ enable_scrq_irq(adapter, adapter->rx_scrq[scrq_num]);
+ napi_complete(napi);
+ if (pending_scrq(adapter, adapter->rx_scrq[scrq_num]) &&
+ napi_reschedule(napi)) {
+ disable_scrq_irq(adapter, adapter->rx_scrq[scrq_num]);
+ goto restart_poll;
+ }
+ }
+ return frames_processed;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void ibmvnic_netpoll_controller(struct net_device *dev)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(dev);
+ int i;
+
+ replenish_pools(netdev_priv(dev));
+ for (i = 0; i < adapter->req_rx_queues; i++)
+ ibmvnic_interrupt_rx(adapter->rx_scrq[i]->irq,
+ adapter->rx_scrq[i]);
+}
+#endif
+
+static const struct net_device_ops ibmvnic_netdev_ops = {
+ .ndo_open = ibmvnic_open,
+ .ndo_stop = ibmvnic_close,
+ .ndo_start_xmit = ibmvnic_xmit,
+ .ndo_set_rx_mode = ibmvnic_set_multi,
+ .ndo_set_mac_address = ibmvnic_set_mac,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = ibmvnic_change_mtu,
+ .ndo_tx_timeout = ibmvnic_tx_timeout,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = ibmvnic_netpoll_controller,
+#endif
+};
+
+/* ethtool functions */
+
+static int ibmvnic_get_settings(struct net_device *netdev,
+ struct ethtool_cmd *cmd)
+{
+ cmd->supported = (SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
+ SUPPORTED_FIBRE);
+ cmd->advertising = (ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg |
+ ADVERTISED_FIBRE);
+ ethtool_cmd_speed_set(cmd, SPEED_1000);
+ cmd->duplex = DUPLEX_FULL;
+ cmd->port = PORT_FIBRE;
+ cmd->phy_address = 0;
+ cmd->transceiver = XCVR_INTERNAL;
+ cmd->autoneg = AUTONEG_ENABLE;
+ cmd->maxtxpkt = 0;
+ cmd->maxrxpkt = 1;
+ return 0;
+}
+
+static void ibmvnic_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, ibmvnic_driver_name, sizeof(info->driver));
+ strlcpy(info->version, IBMVNIC_DRIVER_VERSION, sizeof(info->version));
+}
+
+static u32 ibmvnic_get_msglevel(struct net_device *netdev)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+
+ return adapter->msg_enable;
+}
+
+static void ibmvnic_set_msglevel(struct net_device *netdev, u32 data)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+
+ adapter->msg_enable = data;
+}
+
+static u32 ibmvnic_get_link(struct net_device *netdev)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+
+ /* Don't need to send a query because we request a logical link up at
+ * init and then we wait for link state indications
+ */
+ return adapter->logical_link_state;
+}
+
+static void ibmvnic_get_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *ring)
+{
+ ring->rx_max_pending = 0;
+ ring->tx_max_pending = 0;
+ ring->rx_mini_max_pending = 0;
+ ring->rx_jumbo_max_pending = 0;
+ ring->rx_pending = 0;
+ ring->tx_pending = 0;
+ ring->rx_mini_pending = 0;
+ ring->rx_jumbo_pending = 0;
+}
+
+static void ibmvnic_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+ int i;
+
+ if (stringset != ETH_SS_STATS)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(ibmvnic_stats); i++, data += ETH_GSTRING_LEN)
+ memcpy(data, ibmvnic_stats[i].name, ETH_GSTRING_LEN);
+}
+
+static int ibmvnic_get_sset_count(struct net_device *dev, int sset)
+{
+ switch (sset) {
+ case ETH_SS_STATS:
+ return ARRAY_SIZE(ibmvnic_stats);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void ibmvnic_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct ibmvnic_adapter *adapter = netdev_priv(dev);
+ union ibmvnic_crq crq;
+ int i;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.request_statistics.first = IBMVNIC_CRQ_CMD;
+ crq.request_statistics.cmd = REQUEST_STATISTICS;
+ crq.request_statistics.ioba = cpu_to_be32(adapter->stats_token);
+ crq.request_statistics.len =
+ cpu_to_be32(sizeof(struct ibmvnic_statistics));
+ ibmvnic_send_crq(adapter, &crq);
+
+ /* Wait for data to be written */
+ init_completion(&adapter->stats_done);
+ wait_for_completion(&adapter->stats_done);
+
+ for (i = 0; i < ARRAY_SIZE(ibmvnic_stats); i++)
+ data[i] = IBMVNIC_GET_STAT(adapter, ibmvnic_stats[i].offset);
+}
+
+static const struct ethtool_ops ibmvnic_ethtool_ops = {
+ .get_settings = ibmvnic_get_settings,
+ .get_drvinfo = ibmvnic_get_drvinfo,
+ .get_msglevel = ibmvnic_get_msglevel,
+ .set_msglevel = ibmvnic_set_msglevel,
+ .get_link = ibmvnic_get_link,
+ .get_ringparam = ibmvnic_get_ringparam,
+ .get_strings = ibmvnic_get_strings,
+ .get_sset_count = ibmvnic_get_sset_count,
+ .get_ethtool_stats = ibmvnic_get_ethtool_stats,
+};
+
+/* Routines for managing CRQs/sCRQs */
+
+static void release_sub_crq_queue(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_sub_crq_queue *scrq)
+{
+ struct device *dev = &adapter->vdev->dev;
+ long rc;
+
+ netdev_dbg(adapter->netdev, "Releasing sub-CRQ\n");
+
+ /* Close the sub-crqs */
+ do {
+ rc = plpar_hcall_norets(H_FREE_SUB_CRQ,
+ adapter->vdev->unit_address,
+ scrq->crq_num);
+ } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+
+ dma_unmap_single(dev, scrq->msg_token, 4 * PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ free_pages((unsigned long)scrq->msgs, 2);
+ kfree(scrq);
+}
+
+static struct ibmvnic_sub_crq_queue *init_sub_crq_queue(struct ibmvnic_adapter
+ *adapter)
+{
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_sub_crq_queue *scrq;
+ int rc;
+
+ scrq = kmalloc(sizeof(*scrq), GFP_ATOMIC);
+ if (!scrq)
+ return NULL;
+
+ scrq->msgs = (union sub_crq *)__get_free_pages(GFP_KERNEL, 2);
+ memset(scrq->msgs, 0, 4 * PAGE_SIZE);
+ if (!scrq->msgs) {
+ dev_warn(dev, "Couldn't allocate crq queue messages page\n");
+ goto zero_page_failed;
+ }
+
+ scrq->msg_token = dma_map_single(dev, scrq->msgs, 4 * PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dev, scrq->msg_token)) {
+ dev_warn(dev, "Couldn't map crq queue messages page\n");
+ goto map_failed;
+ }
+
+ rc = h_reg_sub_crq(adapter->vdev->unit_address, scrq->msg_token,
+ 4 * PAGE_SIZE, &scrq->crq_num, &scrq->hw_irq);
+
+ if (rc == H_RESOURCE)
+ rc = ibmvnic_reset_crq(adapter);
+
+ if (rc == H_CLOSED) {
+ dev_warn(dev, "Partner adapter not ready, waiting.\n");
+ } else if (rc) {
+ dev_warn(dev, "Error %d registering sub-crq\n", rc);
+ goto reg_failed;
+ }
+
+ scrq->irq = irq_create_mapping(NULL, scrq->hw_irq);
+ if (scrq->irq == NO_IRQ) {
+ dev_err(dev, "Error mapping irq\n");
+ goto map_irq_failed;
+ }
+
+ scrq->adapter = adapter;
+ scrq->size = 4 * PAGE_SIZE / sizeof(*scrq->msgs);
+ scrq->cur = 0;
+ scrq->rx_skb_top = NULL;
+ spin_lock_init(&scrq->lock);
+
+ netdev_dbg(adapter->netdev,
+ "sub-crq initialized, num %lx, hw_irq=%lx, irq=%x\n",
+ scrq->crq_num, scrq->hw_irq, scrq->irq);
+
+ return scrq;
+
+map_irq_failed:
+ do {
+ rc = plpar_hcall_norets(H_FREE_SUB_CRQ,
+ adapter->vdev->unit_address,
+ scrq->crq_num);
+ } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+reg_failed:
+ dma_unmap_single(dev, scrq->msg_token, 4 * PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+map_failed:
+ free_pages((unsigned long)scrq->msgs, 2);
+zero_page_failed:
+ kfree(scrq);
+
+ return NULL;
+}
+
+static void release_sub_crqs(struct ibmvnic_adapter *adapter)
+{
+ int i;
+
+ if (adapter->tx_scrq) {
+ for (i = 0; i < adapter->req_tx_queues; i++)
+ if (adapter->tx_scrq[i]) {
+ free_irq(adapter->tx_scrq[i]->irq,
+ adapter->tx_scrq[i]);
+ release_sub_crq_queue(adapter,
+ adapter->tx_scrq[i]);
+ }
+ adapter->tx_scrq = NULL;
+ }
+
+ if (adapter->rx_scrq) {
+ for (i = 0; i < adapter->req_rx_queues; i++)
+ if (adapter->rx_scrq[i]) {
+ free_irq(adapter->rx_scrq[i]->irq,
+ adapter->rx_scrq[i]);
+ release_sub_crq_queue(adapter,
+ adapter->rx_scrq[i]);
+ }
+ adapter->rx_scrq = NULL;
+ }
+
+ adapter->requested_caps = 0;
+}
+
+static int disable_scrq_irq(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_sub_crq_queue *scrq)
+{
+ struct device *dev = &adapter->vdev->dev;
+ unsigned long rc;
+
+ rc = plpar_hcall_norets(H_VIOCTL, adapter->vdev->unit_address,
+ H_DISABLE_VIO_INTERRUPT, scrq->hw_irq, 0, 0);
+ if (rc)
+ dev_err(dev, "Couldn't disable scrq irq 0x%lx. rc=%ld\n",
+ scrq->hw_irq, rc);
+ return rc;
+}
+
+static int enable_scrq_irq(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_sub_crq_queue *scrq)
+{
+ struct device *dev = &adapter->vdev->dev;
+ unsigned long rc;
+
+ if (scrq->hw_irq > 0x100000000ULL) {
+ dev_err(dev, "bad hw_irq = %lx\n", scrq->hw_irq);
+ return 1;
+ }
+
+ rc = plpar_hcall_norets(H_VIOCTL, adapter->vdev->unit_address,
+ H_ENABLE_VIO_INTERRUPT, scrq->hw_irq, 0, 0);
+ if (rc)
+ dev_err(dev, "Couldn't enable scrq irq 0x%lx. rc=%ld\n",
+ scrq->hw_irq, rc);
+ return rc;
+}
+
+static int ibmvnic_complete_tx(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_sub_crq_queue *scrq)
+{
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_tx_buff *txbuff;
+ union sub_crq *next;
+ int index;
+ int i, j;
+
+restart_loop:
+ while (pending_scrq(adapter, scrq)) {
+ unsigned int pool = scrq->pool_index;
+
+ next = ibmvnic_next_scrq(adapter, scrq);
+ for (i = 0; i < next->tx_comp.num_comps; i++) {
+ if (next->tx_comp.rcs[i]) {
+ dev_err(dev, "tx error %x\n",
+ next->tx_comp.rcs[i]);
+ continue;
+ }
+ index = be32_to_cpu(next->tx_comp.correlators[i]);
+ txbuff = &adapter->tx_pool[pool].tx_buff[index];
+
+ for (j = 0; j < IBMVNIC_MAX_FRAGS_PER_CRQ; j++) {
+ if (!txbuff->data_dma[j])
+ continue;
+
+ txbuff->data_dma[j] = 0;
+ txbuff->used_bounce = false;
+ }
+
+ if (txbuff->last_frag)
+ dev_kfree_skb_any(txbuff->skb);
+
+ adapter->tx_pool[pool].free_map[adapter->tx_pool[pool].
+ producer_index] = index;
+ adapter->tx_pool[pool].producer_index =
+ (adapter->tx_pool[pool].producer_index + 1) %
+ adapter->max_tx_entries_per_subcrq;
+ }
+ /* remove tx_comp scrq*/
+ next->tx_comp.first = 0;
+ }
+
+ enable_scrq_irq(adapter, scrq);
+
+ if (pending_scrq(adapter, scrq)) {
+ disable_scrq_irq(adapter, scrq);
+ goto restart_loop;
+ }
+
+ return 0;
+}
+
+static irqreturn_t ibmvnic_interrupt_tx(int irq, void *instance)
+{
+ struct ibmvnic_sub_crq_queue *scrq = instance;
+ struct ibmvnic_adapter *adapter = scrq->adapter;
+
+ disable_scrq_irq(adapter, scrq);
+ ibmvnic_complete_tx(adapter, scrq);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ibmvnic_interrupt_rx(int irq, void *instance)
+{
+ struct ibmvnic_sub_crq_queue *scrq = instance;
+ struct ibmvnic_adapter *adapter = scrq->adapter;
+
+ if (napi_schedule_prep(&adapter->napi[scrq->scrq_num])) {
+ disable_scrq_irq(adapter, scrq);
+ __napi_schedule(&adapter->napi[scrq->scrq_num]);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void init_sub_crqs(struct ibmvnic_adapter *adapter, int retry)
+{
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_sub_crq_queue **allqueues;
+ int registered_queues = 0;
+ union ibmvnic_crq crq;
+ int total_queues;
+ int more = 0;
+ int i, j;
+ int rc;
+
+ if (!retry) {
+ /* Sub-CRQ entries are 32 byte long */
+ int entries_page = 4 * PAGE_SIZE / (sizeof(u64) * 4);
+
+ if (adapter->min_tx_entries_per_subcrq > entries_page ||
+ adapter->min_rx_add_entries_per_subcrq > entries_page) {
+ dev_err(dev, "Fatal, invalid entries per sub-crq\n");
+ goto allqueues_failed;
+ }
+
+ /* Get the minimum between the queried max and the entries
+ * that fit in our PAGE_SIZE
+ */
+ adapter->req_tx_entries_per_subcrq =
+ adapter->max_tx_entries_per_subcrq > entries_page ?
+ entries_page : adapter->max_tx_entries_per_subcrq;
+ adapter->req_rx_add_entries_per_subcrq =
+ adapter->max_rx_add_entries_per_subcrq > entries_page ?
+ entries_page : adapter->max_rx_add_entries_per_subcrq;
+
+ /* Choosing the maximum number of queues supported by firmware*/
+ adapter->req_tx_queues = adapter->min_tx_queues;
+ adapter->req_rx_queues = adapter->min_rx_queues;
+ adapter->req_rx_add_queues = adapter->min_rx_add_queues;
+
+ adapter->req_mtu = adapter->max_mtu;
+ }
+
+ total_queues = adapter->req_tx_queues + adapter->req_rx_queues;
+
+ allqueues = kcalloc(total_queues, sizeof(*allqueues), GFP_ATOMIC);
+ if (!allqueues)
+ goto allqueues_failed;
+
+ for (i = 0; i < total_queues; i++) {
+ allqueues[i] = init_sub_crq_queue(adapter);
+ if (!allqueues[i]) {
+ dev_warn(dev, "Couldn't allocate all sub-crqs\n");
+ break;
+ }
+ registered_queues++;
+ }
+
+ /* Make sure we were able to register the minimum number of queues */
+ if (registered_queues <
+ adapter->min_tx_queues + adapter->min_rx_queues) {
+ dev_err(dev, "Fatal: Couldn't init min number of sub-crqs\n");
+ goto tx_failed;
+ }
+
+ /* Distribute the failed allocated queues*/
+ for (i = 0; i < total_queues - registered_queues + more ; i++) {
+ netdev_dbg(adapter->netdev, "Reducing number of queues\n");
+ switch (i % 3) {
+ case 0:
+ if (adapter->req_rx_queues > adapter->min_rx_queues)
+ adapter->req_rx_queues--;
+ else
+ more++;
+ break;
+ case 1:
+ if (adapter->req_tx_queues > adapter->min_tx_queues)
+ adapter->req_tx_queues--;
+ else
+ more++;
+ break;
+ }
+ }
+
+ adapter->tx_scrq = kcalloc(adapter->req_tx_queues,
+ sizeof(*adapter->tx_scrq), GFP_ATOMIC);
+ if (!adapter->tx_scrq)
+ goto tx_failed;
+
+ for (i = 0; i < adapter->req_tx_queues; i++) {
+ adapter->tx_scrq[i] = allqueues[i];
+ adapter->tx_scrq[i]->pool_index = i;
+ rc = request_irq(adapter->tx_scrq[i]->irq, ibmvnic_interrupt_tx,
+ 0, "ibmvnic_tx", adapter->tx_scrq[i]);
+ if (rc) {
+ dev_err(dev, "Couldn't register tx irq 0x%x. rc=%d\n",
+ adapter->tx_scrq[i]->irq, rc);
+ goto req_tx_irq_failed;
+ }
+ }
+
+ adapter->rx_scrq = kcalloc(adapter->req_rx_queues,
+ sizeof(*adapter->rx_scrq), GFP_ATOMIC);
+ if (!adapter->rx_scrq)
+ goto rx_failed;
+
+ for (i = 0; i < adapter->req_rx_queues; i++) {
+ adapter->rx_scrq[i] = allqueues[i + adapter->req_tx_queues];
+ adapter->rx_scrq[i]->scrq_num = i;
+ rc = request_irq(adapter->rx_scrq[i]->irq, ibmvnic_interrupt_rx,
+ 0, "ibmvnic_rx", adapter->rx_scrq[i]);
+ if (rc) {
+ dev_err(dev, "Couldn't register rx irq 0x%x. rc=%d\n",
+ adapter->rx_scrq[i]->irq, rc);
+ goto req_rx_irq_failed;
+ }
+ }
+
+ memset(&crq, 0, sizeof(crq));
+ crq.request_capability.first = IBMVNIC_CRQ_CMD;
+ crq.request_capability.cmd = REQUEST_CAPABILITY;
+
+ crq.request_capability.capability = cpu_to_be16(REQ_TX_QUEUES);
+ crq.request_capability.number = cpu_to_be32(adapter->req_tx_queues);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.request_capability.capability = cpu_to_be16(REQ_RX_QUEUES);
+ crq.request_capability.number = cpu_to_be32(adapter->req_rx_queues);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.request_capability.capability = cpu_to_be16(REQ_RX_ADD_QUEUES);
+ crq.request_capability.number = cpu_to_be32(adapter->req_rx_add_queues);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.request_capability.capability =
+ cpu_to_be16(REQ_TX_ENTRIES_PER_SUBCRQ);
+ crq.request_capability.number =
+ cpu_to_be32(adapter->req_tx_entries_per_subcrq);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.request_capability.capability =
+ cpu_to_be16(REQ_RX_ADD_ENTRIES_PER_SUBCRQ);
+ crq.request_capability.number =
+ cpu_to_be32(adapter->req_rx_add_entries_per_subcrq);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.request_capability.capability = cpu_to_be16(REQ_MTU);
+ crq.request_capability.number = cpu_to_be32(adapter->req_mtu);
+ ibmvnic_send_crq(adapter, &crq);
+
+ if (adapter->netdev->flags & IFF_PROMISC) {
+ if (adapter->promisc_supported) {
+ crq.request_capability.capability =
+ cpu_to_be16(PROMISC_REQUESTED);
+ crq.request_capability.number = cpu_to_be32(1);
+ ibmvnic_send_crq(adapter, &crq);
+ }
+ } else {
+ crq.request_capability.capability =
+ cpu_to_be16(PROMISC_REQUESTED);
+ crq.request_capability.number = cpu_to_be32(0);
+ ibmvnic_send_crq(adapter, &crq);
+ }
+
+ kfree(allqueues);
+
+ return;
+
+req_rx_irq_failed:
+ for (j = 0; j < i; j++)
+ free_irq(adapter->rx_scrq[j]->irq, adapter->rx_scrq[j]);
+ i = adapter->req_tx_queues;
+req_tx_irq_failed:
+ for (j = 0; j < i; j++)
+ free_irq(adapter->tx_scrq[j]->irq, adapter->tx_scrq[j]);
+ kfree(adapter->rx_scrq);
+ adapter->rx_scrq = NULL;
+rx_failed:
+ kfree(adapter->tx_scrq);
+ adapter->tx_scrq = NULL;
+tx_failed:
+ for (i = 0; i < registered_queues; i++)
+ release_sub_crq_queue(adapter, allqueues[i]);
+ kfree(allqueues);
+allqueues_failed:
+ ibmvnic_remove(adapter->vdev);
+}
+
+static int pending_scrq(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_sub_crq_queue *scrq)
+{
+ union sub_crq *entry = &scrq->msgs[scrq->cur];
+
+ if (entry->generic.first & IBMVNIC_CRQ_CMD_RSP || adapter->closing)
+ return 1;
+ else
+ return 0;
+}
+
+static union sub_crq *ibmvnic_next_scrq(struct ibmvnic_adapter *adapter,
+ struct ibmvnic_sub_crq_queue *scrq)
+{
+ union sub_crq *entry;
+ unsigned long flags;
+
+ spin_lock_irqsave(&scrq->lock, flags);
+ entry = &scrq->msgs[scrq->cur];
+ if (entry->generic.first & IBMVNIC_CRQ_CMD_RSP) {
+ if (++scrq->cur == scrq->size)
+ scrq->cur = 0;
+ } else {
+ entry = NULL;
+ }
+ spin_unlock_irqrestore(&scrq->lock, flags);
+
+ return entry;
+}
+
+static union ibmvnic_crq *ibmvnic_next_crq(struct ibmvnic_adapter *adapter)
+{
+ struct ibmvnic_crq_queue *queue = &adapter->crq;
+ union ibmvnic_crq *crq;
+
+ crq = &queue->msgs[queue->cur];
+ if (crq->generic.first & IBMVNIC_CRQ_CMD_RSP) {
+ if (++queue->cur == queue->size)
+ queue->cur = 0;
+ } else {
+ crq = NULL;
+ }
+
+ return crq;
+}
+
+static int send_subcrq(struct ibmvnic_adapter *adapter, u64 remote_handle,
+ union sub_crq *sub_crq)
+{
+ unsigned int ua = adapter->vdev->unit_address;
+ struct device *dev = &adapter->vdev->dev;
+ u64 *u64_crq = (u64 *)sub_crq;
+ int rc;
+
+ netdev_dbg(adapter->netdev,
+ "Sending sCRQ %016lx: %016lx %016lx %016lx %016lx\n",
+ (unsigned long int)cpu_to_be64(remote_handle),
+ (unsigned long int)cpu_to_be64(u64_crq[0]),
+ (unsigned long int)cpu_to_be64(u64_crq[1]),
+ (unsigned long int)cpu_to_be64(u64_crq[2]),
+ (unsigned long int)cpu_to_be64(u64_crq[3]));
+
+ /* Make sure the hypervisor sees the complete request */
+ mb();
+
+ rc = plpar_hcall_norets(H_SEND_SUB_CRQ, ua,
+ cpu_to_be64(remote_handle),
+ cpu_to_be64(u64_crq[0]),
+ cpu_to_be64(u64_crq[1]),
+ cpu_to_be64(u64_crq[2]),
+ cpu_to_be64(u64_crq[3]));
+
+ if (rc) {
+ if (rc == H_CLOSED)
+ dev_warn(dev, "CRQ Queue closed\n");
+ dev_err(dev, "Send error (rc=%d)\n", rc);
+ }
+
+ return rc;
+}
+
+static int ibmvnic_send_crq(struct ibmvnic_adapter *adapter,
+ union ibmvnic_crq *crq)
+{
+ unsigned int ua = adapter->vdev->unit_address;
+ struct device *dev = &adapter->vdev->dev;
+ u64 *u64_crq = (u64 *)crq;
+ int rc;
+
+ netdev_dbg(adapter->netdev, "Sending CRQ: %016lx %016lx\n",
+ (unsigned long int)cpu_to_be64(u64_crq[0]),
+ (unsigned long int)cpu_to_be64(u64_crq[1]));
+
+ /* Make sure the hypervisor sees the complete request */
+ mb();
+
+ rc = plpar_hcall_norets(H_SEND_CRQ, ua,
+ cpu_to_be64(u64_crq[0]),
+ cpu_to_be64(u64_crq[1]));
+
+ if (rc) {
+ if (rc == H_CLOSED)
+ dev_warn(dev, "CRQ Queue closed\n");
+ dev_warn(dev, "Send error (rc=%d)\n", rc);
+ }
+
+ return rc;
+}
+
+static int ibmvnic_send_crq_init(struct ibmvnic_adapter *adapter)
+{
+ union ibmvnic_crq crq;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.generic.first = IBMVNIC_CRQ_INIT_CMD;
+ crq.generic.cmd = IBMVNIC_CRQ_INIT;
+ netdev_dbg(adapter->netdev, "Sending CRQ init\n");
+
+ return ibmvnic_send_crq(adapter, &crq);
+}
+
+static int ibmvnic_send_crq_init_complete(struct ibmvnic_adapter *adapter)
+{
+ union ibmvnic_crq crq;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.generic.first = IBMVNIC_CRQ_INIT_CMD;
+ crq.generic.cmd = IBMVNIC_CRQ_INIT_COMPLETE;
+ netdev_dbg(adapter->netdev, "Sending CRQ init complete\n");
+
+ return ibmvnic_send_crq(adapter, &crq);
+}
+
+static int send_version_xchg(struct ibmvnic_adapter *adapter)
+{
+ union ibmvnic_crq crq;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.version_exchange.first = IBMVNIC_CRQ_CMD;
+ crq.version_exchange.cmd = VERSION_EXCHANGE;
+ crq.version_exchange.version = cpu_to_be16(ibmvnic_version);
+
+ return ibmvnic_send_crq(adapter, &crq);
+}
+
+static void send_login(struct ibmvnic_adapter *adapter)
+{
+ struct ibmvnic_login_rsp_buffer *login_rsp_buffer;
+ struct ibmvnic_login_buffer *login_buffer;
+ struct ibmvnic_inflight_cmd *inflight_cmd;
+ struct device *dev = &adapter->vdev->dev;
+ dma_addr_t rsp_buffer_token;
+ dma_addr_t buffer_token;
+ size_t rsp_buffer_size;
+ union ibmvnic_crq crq;
+ unsigned long flags;
+ size_t buffer_size;
+ __be64 *tx_list_p;
+ __be64 *rx_list_p;
+ int i;
+
+ buffer_size =
+ sizeof(struct ibmvnic_login_buffer) +
+ sizeof(u64) * (adapter->req_tx_queues + adapter->req_rx_queues);
+
+ login_buffer = kmalloc(buffer_size, GFP_ATOMIC);
+ if (!login_buffer)
+ goto buf_alloc_failed;
+
+ buffer_token = dma_map_single(dev, login_buffer, buffer_size,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, buffer_token)) {
+ dev_err(dev, "Couldn't map login buffer\n");
+ goto buf_map_failed;
+ }
+
+ rsp_buffer_size =
+ sizeof(struct ibmvnic_login_rsp_buffer) +
+ sizeof(u64) * (adapter->req_tx_queues +
+ adapter->req_rx_queues *
+ adapter->req_rx_add_queues + adapter->
+ req_rx_add_queues) +
+ sizeof(u8) * (IBMVNIC_TX_DESC_VERSIONS);
+
+ login_rsp_buffer = kmalloc(rsp_buffer_size, GFP_ATOMIC);
+ if (!login_rsp_buffer)
+ goto buf_rsp_alloc_failed;
+
+ rsp_buffer_token = dma_map_single(dev, login_rsp_buffer,
+ rsp_buffer_size, DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, rsp_buffer_token)) {
+ dev_err(dev, "Couldn't map login rsp buffer\n");
+ goto buf_rsp_map_failed;
+ }
+ inflight_cmd = kmalloc(sizeof(*inflight_cmd), GFP_ATOMIC);
+ if (!inflight_cmd) {
+ dev_err(dev, "Couldn't allocate inflight_cmd\n");
+ goto inflight_alloc_failed;
+ }
+ adapter->login_buf = login_buffer;
+ adapter->login_buf_token = buffer_token;
+ adapter->login_buf_sz = buffer_size;
+ adapter->login_rsp_buf = login_rsp_buffer;
+ adapter->login_rsp_buf_token = rsp_buffer_token;
+ adapter->login_rsp_buf_sz = rsp_buffer_size;
+
+ login_buffer->len = cpu_to_be32(buffer_size);
+ login_buffer->version = cpu_to_be32(INITIAL_VERSION_LB);
+ login_buffer->num_txcomp_subcrqs = cpu_to_be32(adapter->req_tx_queues);
+ login_buffer->off_txcomp_subcrqs =
+ cpu_to_be32(sizeof(struct ibmvnic_login_buffer));
+ login_buffer->num_rxcomp_subcrqs = cpu_to_be32(adapter->req_rx_queues);
+ login_buffer->off_rxcomp_subcrqs =
+ cpu_to_be32(sizeof(struct ibmvnic_login_buffer) +
+ sizeof(u64) * adapter->req_tx_queues);
+ login_buffer->login_rsp_ioba = cpu_to_be32(rsp_buffer_token);
+ login_buffer->login_rsp_len = cpu_to_be32(rsp_buffer_size);
+
+ tx_list_p = (__be64 *)((char *)login_buffer +
+ sizeof(struct ibmvnic_login_buffer));
+ rx_list_p = (__be64 *)((char *)login_buffer +
+ sizeof(struct ibmvnic_login_buffer) +
+ sizeof(u64) * adapter->req_tx_queues);
+
+ for (i = 0; i < adapter->req_tx_queues; i++) {
+ if (adapter->tx_scrq[i]) {
+ tx_list_p[i] = cpu_to_be64(adapter->tx_scrq[i]->
+ crq_num);
+ }
+ }
+
+ for (i = 0; i < adapter->req_rx_queues; i++) {
+ if (adapter->rx_scrq[i]) {
+ rx_list_p[i] = cpu_to_be64(adapter->rx_scrq[i]->
+ crq_num);
+ }
+ }
+
+ netdev_dbg(adapter->netdev, "Login Buffer:\n");
+ for (i = 0; i < (adapter->login_buf_sz - 1) / 8 + 1; i++) {
+ netdev_dbg(adapter->netdev, "%016lx\n",
+ ((unsigned long int *)(adapter->login_buf))[i]);
+ }
+
+ memset(&crq, 0, sizeof(crq));
+ crq.login.first = IBMVNIC_CRQ_CMD;
+ crq.login.cmd = LOGIN;
+ crq.login.ioba = cpu_to_be32(buffer_token);
+ crq.login.len = cpu_to_be32(buffer_size);
+
+ memcpy(&inflight_cmd->crq, &crq, sizeof(crq));
+
+ spin_lock_irqsave(&adapter->inflight_lock, flags);
+ list_add_tail(&inflight_cmd->list, &adapter->inflight);
+ spin_unlock_irqrestore(&adapter->inflight_lock, flags);
+
+ ibmvnic_send_crq(adapter, &crq);
+
+ return;
+
+inflight_alloc_failed:
+ dma_unmap_single(dev, rsp_buffer_token, rsp_buffer_size,
+ DMA_FROM_DEVICE);
+buf_rsp_map_failed:
+ kfree(login_rsp_buffer);
+buf_rsp_alloc_failed:
+ dma_unmap_single(dev, buffer_token, buffer_size, DMA_TO_DEVICE);
+buf_map_failed:
+ kfree(login_buffer);
+buf_alloc_failed:
+ return;
+}
+
+static void send_request_map(struct ibmvnic_adapter *adapter, dma_addr_t addr,
+ u32 len, u8 map_id)
+{
+ union ibmvnic_crq crq;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.request_map.first = IBMVNIC_CRQ_CMD;
+ crq.request_map.cmd = REQUEST_MAP;
+ crq.request_map.map_id = map_id;
+ crq.request_map.ioba = cpu_to_be32(addr);
+ crq.request_map.len = cpu_to_be32(len);
+ ibmvnic_send_crq(adapter, &crq);
+}
+
+static void send_request_unmap(struct ibmvnic_adapter *adapter, u8 map_id)
+{
+ union ibmvnic_crq crq;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.request_unmap.first = IBMVNIC_CRQ_CMD;
+ crq.request_unmap.cmd = REQUEST_UNMAP;
+ crq.request_unmap.map_id = map_id;
+ ibmvnic_send_crq(adapter, &crq);
+}
+
+static void send_map_query(struct ibmvnic_adapter *adapter)
+{
+ union ibmvnic_crq crq;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.query_map.first = IBMVNIC_CRQ_CMD;
+ crq.query_map.cmd = QUERY_MAP;
+ ibmvnic_send_crq(adapter, &crq);
+}
+
+/* Send a series of CRQs requesting various capabilities of the VNIC server */
+static void send_cap_queries(struct ibmvnic_adapter *adapter)
+{
+ union ibmvnic_crq crq;
+
+ atomic_set(&adapter->running_cap_queries, 0);
+ memset(&crq, 0, sizeof(crq));
+ crq.query_capability.first = IBMVNIC_CRQ_CMD;
+ crq.query_capability.cmd = QUERY_CAPABILITY;
+
+ crq.query_capability.capability = cpu_to_be16(MIN_TX_QUEUES);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(MIN_RX_QUEUES);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(MIN_RX_ADD_QUEUES);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(MAX_TX_QUEUES);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(MAX_RX_QUEUES);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(MAX_RX_ADD_QUEUES);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability =
+ cpu_to_be16(MIN_TX_ENTRIES_PER_SUBCRQ);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability =
+ cpu_to_be16(MIN_RX_ADD_ENTRIES_PER_SUBCRQ);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability =
+ cpu_to_be16(MAX_TX_ENTRIES_PER_SUBCRQ);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability =
+ cpu_to_be16(MAX_RX_ADD_ENTRIES_PER_SUBCRQ);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(TCP_IP_OFFLOAD);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(PROMISC_SUPPORTED);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(MIN_MTU);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(MAX_MTU);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(MAX_MULTICAST_FILTERS);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(VLAN_HEADER_INSERTION);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(MAX_TX_SG_ENTRIES);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(RX_SG_SUPPORTED);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(OPT_TX_COMP_SUB_QUEUES);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(OPT_RX_COMP_QUEUES);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability =
+ cpu_to_be16(OPT_RX_BUFADD_Q_PER_RX_COMP_Q);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability =
+ cpu_to_be16(OPT_TX_ENTRIES_PER_SUBCRQ);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability =
+ cpu_to_be16(OPT_RXBA_ENTRIES_PER_SUBCRQ);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+
+ crq.query_capability.capability = cpu_to_be16(TX_RX_DESC_REQ);
+ atomic_inc(&adapter->running_cap_queries);
+ ibmvnic_send_crq(adapter, &crq);
+}
+
+static void handle_query_ip_offload_rsp(struct ibmvnic_adapter *adapter)
+{
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_query_ip_offload_buffer *buf = &adapter->ip_offload_buf;
+ union ibmvnic_crq crq;
+ int i;
+
+ dma_unmap_single(dev, adapter->ip_offload_tok,
+ sizeof(adapter->ip_offload_buf), DMA_FROM_DEVICE);
+
+ netdev_dbg(adapter->netdev, "Query IP Offload Buffer:\n");
+ for (i = 0; i < (sizeof(adapter->ip_offload_buf) - 1) / 8 + 1; i++)
+ netdev_dbg(adapter->netdev, "%016lx\n",
+ ((unsigned long int *)(buf))[i]);
+
+ netdev_dbg(adapter->netdev, "ipv4_chksum = %d\n", buf->ipv4_chksum);
+ netdev_dbg(adapter->netdev, "ipv6_chksum = %d\n", buf->ipv6_chksum);
+ netdev_dbg(adapter->netdev, "tcp_ipv4_chksum = %d\n",
+ buf->tcp_ipv4_chksum);
+ netdev_dbg(adapter->netdev, "tcp_ipv6_chksum = %d\n",
+ buf->tcp_ipv6_chksum);
+ netdev_dbg(adapter->netdev, "udp_ipv4_chksum = %d\n",
+ buf->udp_ipv4_chksum);
+ netdev_dbg(adapter->netdev, "udp_ipv6_chksum = %d\n",
+ buf->udp_ipv6_chksum);
+ netdev_dbg(adapter->netdev, "large_tx_ipv4 = %d\n",
+ buf->large_tx_ipv4);
+ netdev_dbg(adapter->netdev, "large_tx_ipv6 = %d\n",
+ buf->large_tx_ipv6);
+ netdev_dbg(adapter->netdev, "large_rx_ipv4 = %d\n",
+ buf->large_rx_ipv4);
+ netdev_dbg(adapter->netdev, "large_rx_ipv6 = %d\n",
+ buf->large_rx_ipv6);
+ netdev_dbg(adapter->netdev, "max_ipv4_hdr_sz = %d\n",
+ buf->max_ipv4_header_size);
+ netdev_dbg(adapter->netdev, "max_ipv6_hdr_sz = %d\n",
+ buf->max_ipv6_header_size);
+ netdev_dbg(adapter->netdev, "max_tcp_hdr_size = %d\n",
+ buf->max_tcp_header_size);
+ netdev_dbg(adapter->netdev, "max_udp_hdr_size = %d\n",
+ buf->max_udp_header_size);
+ netdev_dbg(adapter->netdev, "max_large_tx_size = %d\n",
+ buf->max_large_tx_size);
+ netdev_dbg(adapter->netdev, "max_large_rx_size = %d\n",
+ buf->max_large_rx_size);
+ netdev_dbg(adapter->netdev, "ipv6_ext_hdr = %d\n",
+ buf->ipv6_extension_header);
+ netdev_dbg(adapter->netdev, "tcp_pseudosum_req = %d\n",
+ buf->tcp_pseudosum_req);
+ netdev_dbg(adapter->netdev, "num_ipv6_ext_hd = %d\n",
+ buf->num_ipv6_ext_headers);
+ netdev_dbg(adapter->netdev, "off_ipv6_ext_hd = %d\n",
+ buf->off_ipv6_ext_headers);
+
+ adapter->ip_offload_ctrl_tok =
+ dma_map_single(dev, &adapter->ip_offload_ctrl,
+ sizeof(adapter->ip_offload_ctrl), DMA_TO_DEVICE);
+
+ if (dma_mapping_error(dev, adapter->ip_offload_ctrl_tok)) {
+ dev_err(dev, "Couldn't map ip offload control buffer\n");
+ return;
+ }
+
+ adapter->ip_offload_ctrl.version = cpu_to_be32(INITIAL_VERSION_IOB);
+ adapter->ip_offload_ctrl.tcp_ipv4_chksum = buf->tcp_ipv4_chksum;
+ adapter->ip_offload_ctrl.udp_ipv4_chksum = buf->udp_ipv4_chksum;
+ adapter->ip_offload_ctrl.tcp_ipv6_chksum = buf->tcp_ipv6_chksum;
+ adapter->ip_offload_ctrl.udp_ipv6_chksum = buf->udp_ipv6_chksum;
+
+ /* large_tx/rx disabled for now, additional features needed */
+ adapter->ip_offload_ctrl.large_tx_ipv4 = 0;
+ adapter->ip_offload_ctrl.large_tx_ipv6 = 0;
+ adapter->ip_offload_ctrl.large_rx_ipv4 = 0;
+ adapter->ip_offload_ctrl.large_rx_ipv6 = 0;
+
+ adapter->netdev->features = NETIF_F_GSO;
+
+ if (buf->tcp_ipv4_chksum || buf->udp_ipv4_chksum)
+ adapter->netdev->features |= NETIF_F_IP_CSUM;
+
+ if (buf->tcp_ipv6_chksum || buf->udp_ipv6_chksum)
+ adapter->netdev->features |= NETIF_F_IPV6_CSUM;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.control_ip_offload.first = IBMVNIC_CRQ_CMD;
+ crq.control_ip_offload.cmd = CONTROL_IP_OFFLOAD;
+ crq.control_ip_offload.len =
+ cpu_to_be32(sizeof(adapter->ip_offload_ctrl));
+ crq.control_ip_offload.ioba = cpu_to_be32(adapter->ip_offload_ctrl_tok);
+ ibmvnic_send_crq(adapter, &crq);
+}
+
+static void handle_error_info_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_error_buff *error_buff;
+ unsigned long flags;
+ bool found = false;
+ int i;
+
+ if (!crq->request_error_rsp.rc.code) {
+ dev_info(dev, "Request Error Rsp returned with rc=%x\n",
+ crq->request_error_rsp.rc.code);
+ return;
+ }
+
+ spin_lock_irqsave(&adapter->error_list_lock, flags);
+ list_for_each_entry(error_buff, &adapter->errors, list)
+ if (error_buff->error_id == crq->request_error_rsp.error_id) {
+ found = true;
+ list_del(&error_buff->list);
+ break;
+ }
+ spin_unlock_irqrestore(&adapter->error_list_lock, flags);
+
+ if (!found) {
+ dev_err(dev, "Couldn't find error id %x\n",
+ crq->request_error_rsp.error_id);
+ return;
+ }
+
+ dev_err(dev, "Detailed info for error id %x:",
+ crq->request_error_rsp.error_id);
+
+ for (i = 0; i < error_buff->len; i++) {
+ pr_cont("%02x", (int)error_buff->buff[i]);
+ if (i % 8 == 7)
+ pr_cont(" ");
+ }
+ pr_cont("\n");
+
+ dma_unmap_single(dev, error_buff->dma, error_buff->len,
+ DMA_FROM_DEVICE);
+ kfree(error_buff->buff);
+ kfree(error_buff);
+}
+
+static void handle_dump_size_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ int len = be32_to_cpu(crq->request_dump_size_rsp.len);
+ struct ibmvnic_inflight_cmd *inflight_cmd;
+ struct device *dev = &adapter->vdev->dev;
+ union ibmvnic_crq newcrq;
+ unsigned long flags;
+
+ /* allocate and map buffer */
+ adapter->dump_data = kmalloc(len, GFP_KERNEL);
+ if (!adapter->dump_data) {
+ complete(&adapter->fw_done);
+ return;
+ }
+
+ adapter->dump_data_token = dma_map_single(dev, adapter->dump_data, len,
+ DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(dev, adapter->dump_data_token)) {
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ dev_err(dev, "Couldn't map dump data\n");
+ kfree(adapter->dump_data);
+ complete(&adapter->fw_done);
+ return;
+ }
+
+ inflight_cmd = kmalloc(sizeof(*inflight_cmd), GFP_ATOMIC);
+ if (!inflight_cmd) {
+ dma_unmap_single(dev, adapter->dump_data_token, len,
+ DMA_FROM_DEVICE);
+ kfree(adapter->dump_data);
+ complete(&adapter->fw_done);
+ return;
+ }
+
+ memset(&newcrq, 0, sizeof(newcrq));
+ newcrq.request_dump.first = IBMVNIC_CRQ_CMD;
+ newcrq.request_dump.cmd = REQUEST_DUMP;
+ newcrq.request_dump.ioba = cpu_to_be32(adapter->dump_data_token);
+ newcrq.request_dump.len = cpu_to_be32(adapter->dump_data_size);
+
+ memcpy(&inflight_cmd->crq, &newcrq, sizeof(newcrq));
+
+ spin_lock_irqsave(&adapter->inflight_lock, flags);
+ list_add_tail(&inflight_cmd->list, &adapter->inflight);
+ spin_unlock_irqrestore(&adapter->inflight_lock, flags);
+
+ ibmvnic_send_crq(adapter, &newcrq);
+}
+
+static void handle_error_indication(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ int detail_len = be32_to_cpu(crq->error_indication.detail_error_sz);
+ struct ibmvnic_inflight_cmd *inflight_cmd;
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_error_buff *error_buff;
+ union ibmvnic_crq new_crq;
+ unsigned long flags;
+
+ dev_err(dev, "Firmware reports %serror id %x, cause %d\n",
+ crq->error_indication.
+ flags & IBMVNIC_FATAL_ERROR ? "FATAL " : "",
+ crq->error_indication.error_id,
+ crq->error_indication.error_cause);
+
+ error_buff = kmalloc(sizeof(*error_buff), GFP_ATOMIC);
+ if (!error_buff)
+ return;
+
+ error_buff->buff = kmalloc(detail_len, GFP_ATOMIC);
+ if (!error_buff->buff) {
+ kfree(error_buff);
+ return;
+ }
+
+ error_buff->dma = dma_map_single(dev, error_buff->buff, detail_len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, error_buff->dma)) {
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ dev_err(dev, "Couldn't map error buffer\n");
+ kfree(error_buff->buff);
+ kfree(error_buff);
+ return;
+ }
+
+ inflight_cmd = kmalloc(sizeof(*inflight_cmd), GFP_ATOMIC);
+ if (!inflight_cmd) {
+ dma_unmap_single(dev, error_buff->dma, detail_len,
+ DMA_FROM_DEVICE);
+ kfree(error_buff->buff);
+ kfree(error_buff);
+ return;
+ }
+
+ error_buff->len = detail_len;
+ error_buff->error_id = crq->error_indication.error_id;
+
+ spin_lock_irqsave(&adapter->error_list_lock, flags);
+ list_add_tail(&error_buff->list, &adapter->errors);
+ spin_unlock_irqrestore(&adapter->error_list_lock, flags);
+
+ memset(&new_crq, 0, sizeof(new_crq));
+ new_crq.request_error_info.first = IBMVNIC_CRQ_CMD;
+ new_crq.request_error_info.cmd = REQUEST_ERROR_INFO;
+ new_crq.request_error_info.ioba = cpu_to_be32(error_buff->dma);
+ new_crq.request_error_info.len = cpu_to_be32(detail_len);
+ new_crq.request_error_info.error_id = crq->error_indication.error_id;
+
+ memcpy(&inflight_cmd->crq, &crq, sizeof(crq));
+
+ spin_lock_irqsave(&adapter->inflight_lock, flags);
+ list_add_tail(&inflight_cmd->list, &adapter->inflight);
+ spin_unlock_irqrestore(&adapter->inflight_lock, flags);
+
+ ibmvnic_send_crq(adapter, &new_crq);
+}
+
+static void handle_change_mac_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ struct device *dev = &adapter->vdev->dev;
+ long rc;
+
+ rc = crq->change_mac_addr_rsp.rc.code;
+ if (rc) {
+ dev_err(dev, "Error %ld in CHANGE_MAC_ADDR_RSP\n", rc);
+ return;
+ }
+ memcpy(netdev->dev_addr, &crq->change_mac_addr_rsp.mac_addr[0],
+ ETH_ALEN);
+}
+
+static void handle_request_cap_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ struct device *dev = &adapter->vdev->dev;
+ u64 *req_value;
+ char *name;
+
+ switch (be16_to_cpu(crq->request_capability_rsp.capability)) {
+ case REQ_TX_QUEUES:
+ req_value = &adapter->req_tx_queues;
+ name = "tx";
+ break;
+ case REQ_RX_QUEUES:
+ req_value = &adapter->req_rx_queues;
+ name = "rx";
+ break;
+ case REQ_RX_ADD_QUEUES:
+ req_value = &adapter->req_rx_add_queues;
+ name = "rx_add";
+ break;
+ case REQ_TX_ENTRIES_PER_SUBCRQ:
+ req_value = &adapter->req_tx_entries_per_subcrq;
+ name = "tx_entries_per_subcrq";
+ break;
+ case REQ_RX_ADD_ENTRIES_PER_SUBCRQ:
+ req_value = &adapter->req_rx_add_entries_per_subcrq;
+ name = "rx_add_entries_per_subcrq";
+ break;
+ case REQ_MTU:
+ req_value = &adapter->req_mtu;
+ name = "mtu";
+ break;
+ case PROMISC_REQUESTED:
+ req_value = &adapter->promisc;
+ name = "promisc";
+ break;
+ default:
+ dev_err(dev, "Got invalid cap request rsp %d\n",
+ crq->request_capability.capability);
+ return;
+ }
+
+ switch (crq->request_capability_rsp.rc.code) {
+ case SUCCESS:
+ break;
+ case PARTIALSUCCESS:
+ dev_info(dev, "req=%lld, rsp=%ld in %s queue, retrying.\n",
+ *req_value,
+ (long int)be32_to_cpu(crq->request_capability_rsp.
+ number), name);
+ release_sub_crqs(adapter);
+ *req_value = be32_to_cpu(crq->request_capability_rsp.number);
+ complete(&adapter->init_done);
+ return;
+ default:
+ dev_err(dev, "Error %d in request cap rsp\n",
+ crq->request_capability_rsp.rc.code);
+ return;
+ }
+
+ /* Done receiving requested capabilities, query IP offload support */
+ if (++adapter->requested_caps == 7) {
+ union ibmvnic_crq newcrq;
+ int buf_sz = sizeof(struct ibmvnic_query_ip_offload_buffer);
+ struct ibmvnic_query_ip_offload_buffer *ip_offload_buf =
+ &adapter->ip_offload_buf;
+
+ adapter->ip_offload_tok = dma_map_single(dev, ip_offload_buf,
+ buf_sz,
+ DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(dev, adapter->ip_offload_tok)) {
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ dev_err(dev, "Couldn't map offload buffer\n");
+ return;
+ }
+
+ memset(&newcrq, 0, sizeof(newcrq));
+ newcrq.query_ip_offload.first = IBMVNIC_CRQ_CMD;
+ newcrq.query_ip_offload.cmd = QUERY_IP_OFFLOAD;
+ newcrq.query_ip_offload.len = cpu_to_be32(buf_sz);
+ newcrq.query_ip_offload.ioba =
+ cpu_to_be32(adapter->ip_offload_tok);
+
+ ibmvnic_send_crq(adapter, &newcrq);
+ }
+}
+
+static int handle_login_rsp(union ibmvnic_crq *login_rsp_crq,
+ struct ibmvnic_adapter *adapter)
+{
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_login_rsp_buffer *login_rsp = adapter->login_rsp_buf;
+ struct ibmvnic_login_buffer *login = adapter->login_buf;
+ union ibmvnic_crq crq;
+ int i;
+
+ dma_unmap_single(dev, adapter->login_buf_token, adapter->login_buf_sz,
+ DMA_BIDIRECTIONAL);
+ dma_unmap_single(dev, adapter->login_rsp_buf_token,
+ adapter->login_rsp_buf_sz, DMA_BIDIRECTIONAL);
+
+ netdev_dbg(adapter->netdev, "Login Response Buffer:\n");
+ for (i = 0; i < (adapter->login_rsp_buf_sz - 1) / 8 + 1; i++) {
+ netdev_dbg(adapter->netdev, "%016lx\n",
+ ((unsigned long int *)(adapter->login_rsp_buf))[i]);
+ }
+
+ /* Sanity checks */
+ if (login->num_txcomp_subcrqs != login_rsp->num_txsubm_subcrqs ||
+ (be32_to_cpu(login->num_rxcomp_subcrqs) *
+ adapter->req_rx_add_queues !=
+ be32_to_cpu(login_rsp->num_rxadd_subcrqs))) {
+ dev_err(dev, "FATAL: Inconsistent login and login rsp\n");
+ ibmvnic_remove(adapter->vdev);
+ return -EIO;
+ }
+ complete(&adapter->init_done);
+
+ memset(&crq, 0, sizeof(crq));
+ crq.request_ras_comp_num.first = IBMVNIC_CRQ_CMD;
+ crq.request_ras_comp_num.cmd = REQUEST_RAS_COMP_NUM;
+ ibmvnic_send_crq(adapter, &crq);
+
+ return 0;
+}
+
+static void handle_request_map_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ struct device *dev = &adapter->vdev->dev;
+ u8 map_id = crq->request_map_rsp.map_id;
+ int tx_subcrqs;
+ int rx_subcrqs;
+ long rc;
+ int i;
+
+ tx_subcrqs = be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs);
+ rx_subcrqs = be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs);
+
+ rc = crq->request_map_rsp.rc.code;
+ if (rc) {
+ dev_err(dev, "Error %ld in REQUEST_MAP_RSP\n", rc);
+ adapter->map_id--;
+ /* need to find and zero tx/rx_pool map_id */
+ for (i = 0; i < tx_subcrqs; i++) {
+ if (adapter->tx_pool[i].long_term_buff.map_id == map_id)
+ adapter->tx_pool[i].long_term_buff.map_id = 0;
+ }
+ for (i = 0; i < rx_subcrqs; i++) {
+ if (adapter->rx_pool[i].long_term_buff.map_id == map_id)
+ adapter->rx_pool[i].long_term_buff.map_id = 0;
+ }
+ }
+ complete(&adapter->fw_done);
+}
+
+static void handle_request_unmap_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ struct device *dev = &adapter->vdev->dev;
+ long rc;
+
+ rc = crq->request_unmap_rsp.rc.code;
+ if (rc)
+ dev_err(dev, "Error %ld in REQUEST_UNMAP_RSP\n", rc);
+}
+
+static void handle_query_map_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ struct device *dev = &adapter->vdev->dev;
+ long rc;
+
+ rc = crq->query_map_rsp.rc.code;
+ if (rc) {
+ dev_err(dev, "Error %ld in QUERY_MAP_RSP\n", rc);
+ return;
+ }
+ netdev_dbg(netdev, "page_size = %d\ntot_pages = %d\nfree_pages = %d\n",
+ crq->query_map_rsp.page_size, crq->query_map_rsp.tot_pages,
+ crq->query_map_rsp.free_pages);
+}
+
+static void handle_query_cap_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ struct device *dev = &adapter->vdev->dev;
+ long rc;
+
+ atomic_dec(&adapter->running_cap_queries);
+ netdev_dbg(netdev, "Outstanding queries: %d\n",
+ atomic_read(&adapter->running_cap_queries));
+ rc = crq->query_capability.rc.code;
+ if (rc) {
+ dev_err(dev, "Error %ld in QUERY_CAP_RSP\n", rc);
+ goto out;
+ }
+
+ switch (be16_to_cpu(crq->query_capability.capability)) {
+ case MIN_TX_QUEUES:
+ adapter->min_tx_queues =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "min_tx_queues = %lld\n",
+ adapter->min_tx_queues);
+ break;
+ case MIN_RX_QUEUES:
+ adapter->min_rx_queues =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "min_rx_queues = %lld\n",
+ adapter->min_rx_queues);
+ break;
+ case MIN_RX_ADD_QUEUES:
+ adapter->min_rx_add_queues =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "min_rx_add_queues = %lld\n",
+ adapter->min_rx_add_queues);
+ break;
+ case MAX_TX_QUEUES:
+ adapter->max_tx_queues =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "max_tx_queues = %lld\n",
+ adapter->max_tx_queues);
+ break;
+ case MAX_RX_QUEUES:
+ adapter->max_rx_queues =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "max_rx_queues = %lld\n",
+ adapter->max_rx_queues);
+ break;
+ case MAX_RX_ADD_QUEUES:
+ adapter->max_rx_add_queues =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "max_rx_add_queues = %lld\n",
+ adapter->max_rx_add_queues);
+ break;
+ case MIN_TX_ENTRIES_PER_SUBCRQ:
+ adapter->min_tx_entries_per_subcrq =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "min_tx_entries_per_subcrq = %lld\n",
+ adapter->min_tx_entries_per_subcrq);
+ break;
+ case MIN_RX_ADD_ENTRIES_PER_SUBCRQ:
+ adapter->min_rx_add_entries_per_subcrq =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "min_rx_add_entrs_per_subcrq = %lld\n",
+ adapter->min_rx_add_entries_per_subcrq);
+ break;
+ case MAX_TX_ENTRIES_PER_SUBCRQ:
+ adapter->max_tx_entries_per_subcrq =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "max_tx_entries_per_subcrq = %lld\n",
+ adapter->max_tx_entries_per_subcrq);
+ break;
+ case MAX_RX_ADD_ENTRIES_PER_SUBCRQ:
+ adapter->max_rx_add_entries_per_subcrq =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "max_rx_add_entrs_per_subcrq = %lld\n",
+ adapter->max_rx_add_entries_per_subcrq);
+ break;
+ case TCP_IP_OFFLOAD:
+ adapter->tcp_ip_offload =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "tcp_ip_offload = %lld\n",
+ adapter->tcp_ip_offload);
+ break;
+ case PROMISC_SUPPORTED:
+ adapter->promisc_supported =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "promisc_supported = %lld\n",
+ adapter->promisc_supported);
+ break;
+ case MIN_MTU:
+ adapter->min_mtu = be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "min_mtu = %lld\n", adapter->min_mtu);
+ break;
+ case MAX_MTU:
+ adapter->max_mtu = be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "max_mtu = %lld\n", adapter->max_mtu);
+ break;
+ case MAX_MULTICAST_FILTERS:
+ adapter->max_multicast_filters =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "max_multicast_filters = %lld\n",
+ adapter->max_multicast_filters);
+ break;
+ case VLAN_HEADER_INSERTION:
+ adapter->vlan_header_insertion =
+ be32_to_cpu(crq->query_capability.number);
+ if (adapter->vlan_header_insertion)
+ netdev->features |= NETIF_F_HW_VLAN_STAG_TX;
+ netdev_dbg(netdev, "vlan_header_insertion = %lld\n",
+ adapter->vlan_header_insertion);
+ break;
+ case MAX_TX_SG_ENTRIES:
+ adapter->max_tx_sg_entries =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "max_tx_sg_entries = %lld\n",
+ adapter->max_tx_sg_entries);
+ break;
+ case RX_SG_SUPPORTED:
+ adapter->rx_sg_supported =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "rx_sg_supported = %lld\n",
+ adapter->rx_sg_supported);
+ break;
+ case OPT_TX_COMP_SUB_QUEUES:
+ adapter->opt_tx_comp_sub_queues =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "opt_tx_comp_sub_queues = %lld\n",
+ adapter->opt_tx_comp_sub_queues);
+ break;
+ case OPT_RX_COMP_QUEUES:
+ adapter->opt_rx_comp_queues =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "opt_rx_comp_queues = %lld\n",
+ adapter->opt_rx_comp_queues);
+ break;
+ case OPT_RX_BUFADD_Q_PER_RX_COMP_Q:
+ adapter->opt_rx_bufadd_q_per_rx_comp_q =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "opt_rx_bufadd_q_per_rx_comp_q = %lld\n",
+ adapter->opt_rx_bufadd_q_per_rx_comp_q);
+ break;
+ case OPT_TX_ENTRIES_PER_SUBCRQ:
+ adapter->opt_tx_entries_per_subcrq =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "opt_tx_entries_per_subcrq = %lld\n",
+ adapter->opt_tx_entries_per_subcrq);
+ break;
+ case OPT_RXBA_ENTRIES_PER_SUBCRQ:
+ adapter->opt_rxba_entries_per_subcrq =
+ be32_to_cpu(crq->query_capability.number);
+ netdev_dbg(netdev, "opt_rxba_entries_per_subcrq = %lld\n",
+ adapter->opt_rxba_entries_per_subcrq);
+ break;
+ case TX_RX_DESC_REQ:
+ adapter->tx_rx_desc_req = crq->query_capability.number;
+ netdev_dbg(netdev, "tx_rx_desc_req = %llx\n",
+ adapter->tx_rx_desc_req);
+ break;
+
+ default:
+ netdev_err(netdev, "Got invalid cap rsp %d\n",
+ crq->query_capability.capability);
+ }
+
+out:
+ if (atomic_read(&adapter->running_cap_queries) == 0)
+ complete(&adapter->init_done);
+ /* We're done querying the capabilities, initialize sub-crqs */
+}
+
+static void handle_control_ras_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ u8 correlator = crq->control_ras_rsp.correlator;
+ struct device *dev = &adapter->vdev->dev;
+ bool found = false;
+ int i;
+
+ if (crq->control_ras_rsp.rc.code) {
+ dev_warn(dev, "Control ras failed rc=%d\n",
+ crq->control_ras_rsp.rc.code);
+ return;
+ }
+
+ for (i = 0; i < adapter->ras_comp_num; i++) {
+ if (adapter->ras_comps[i].correlator == correlator) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ dev_warn(dev, "Correlator not found on control_ras_rsp\n");
+ return;
+ }
+
+ switch (crq->control_ras_rsp.op) {
+ case IBMVNIC_TRACE_LEVEL:
+ adapter->ras_comps[i].trace_level = crq->control_ras.level;
+ break;
+ case IBMVNIC_ERROR_LEVEL:
+ adapter->ras_comps[i].error_check_level =
+ crq->control_ras.level;
+ break;
+ case IBMVNIC_TRACE_PAUSE:
+ adapter->ras_comp_int[i].paused = 1;
+ break;
+ case IBMVNIC_TRACE_RESUME:
+ adapter->ras_comp_int[i].paused = 0;
+ break;
+ case IBMVNIC_TRACE_ON:
+ adapter->ras_comps[i].trace_on = 1;
+ break;
+ case IBMVNIC_TRACE_OFF:
+ adapter->ras_comps[i].trace_on = 0;
+ break;
+ case IBMVNIC_CHG_TRACE_BUFF_SZ:
+ /* trace_buff_sz is 3 bytes, stuff it into an int */
+ ((u8 *)(&adapter->ras_comps[i].trace_buff_size))[0] = 0;
+ ((u8 *)(&adapter->ras_comps[i].trace_buff_size))[1] =
+ crq->control_ras_rsp.trace_buff_sz[0];
+ ((u8 *)(&adapter->ras_comps[i].trace_buff_size))[2] =
+ crq->control_ras_rsp.trace_buff_sz[1];
+ ((u8 *)(&adapter->ras_comps[i].trace_buff_size))[3] =
+ crq->control_ras_rsp.trace_buff_sz[2];
+ break;
+ default:
+ dev_err(dev, "invalid op %d on control_ras_rsp",
+ crq->control_ras_rsp.op);
+ }
+}
+
+static int ibmvnic_fw_comp_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t trace_read(struct file *file, char __user *user_buf, size_t len,
+ loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_fw_trace_entry *trace;
+ int num = ras_comp_int->num;
+ union ibmvnic_crq crq;
+ dma_addr_t trace_tok;
+
+ if (*ppos >= be32_to_cpu(adapter->ras_comps[num].trace_buff_size))
+ return 0;
+
+ trace =
+ dma_alloc_coherent(dev,
+ be32_to_cpu(adapter->ras_comps[num].
+ trace_buff_size), &trace_tok,
+ GFP_KERNEL);
+ if (!trace) {
+ dev_err(dev, "Couldn't alloc trace buffer\n");
+ return 0;
+ }
+
+ memset(&crq, 0, sizeof(crq));
+ crq.collect_fw_trace.first = IBMVNIC_CRQ_CMD;
+ crq.collect_fw_trace.cmd = COLLECT_FW_TRACE;
+ crq.collect_fw_trace.correlator = adapter->ras_comps[num].correlator;
+ crq.collect_fw_trace.ioba = cpu_to_be32(trace_tok);
+ crq.collect_fw_trace.len = adapter->ras_comps[num].trace_buff_size;
+ ibmvnic_send_crq(adapter, &crq);
+
+ init_completion(&adapter->fw_done);
+ wait_for_completion(&adapter->fw_done);
+
+ if (*ppos + len > be32_to_cpu(adapter->ras_comps[num].trace_buff_size))
+ len =
+ be32_to_cpu(adapter->ras_comps[num].trace_buff_size) -
+ *ppos;
+
+ copy_to_user(user_buf, &((u8 *)trace)[*ppos], len);
+
+ dma_free_coherent(dev,
+ be32_to_cpu(adapter->ras_comps[num].trace_buff_size),
+ trace, trace_tok);
+ *ppos += len;
+ return len;
+}
+
+static const struct file_operations trace_ops = {
+ .owner = THIS_MODULE,
+ .open = ibmvnic_fw_comp_open,
+ .read = trace_read,
+};
+
+static ssize_t paused_read(struct file *file, char __user *user_buf, size_t len,
+ loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ int num = ras_comp_int->num;
+ char buff[5]; /* 1 or 0 plus \n and \0 */
+ int size;
+
+ size = sprintf(buff, "%d\n", adapter->ras_comp_int[num].paused);
+
+ if (*ppos >= size)
+ return 0;
+
+ copy_to_user(user_buf, buff, size);
+ *ppos += size;
+ return size;
+}
+
+static ssize_t paused_write(struct file *file, const char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ int num = ras_comp_int->num;
+ union ibmvnic_crq crq;
+ unsigned long val;
+ char buff[9]; /* decimal max int plus \n and \0 */
+
+ copy_from_user(buff, user_buf, sizeof(buff));
+ val = kstrtoul(buff, 10, NULL);
+
+ adapter->ras_comp_int[num].paused = val ? 1 : 0;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.control_ras.first = IBMVNIC_CRQ_CMD;
+ crq.control_ras.cmd = CONTROL_RAS;
+ crq.control_ras.correlator = adapter->ras_comps[num].correlator;
+ crq.control_ras.op = val ? IBMVNIC_TRACE_PAUSE : IBMVNIC_TRACE_RESUME;
+ ibmvnic_send_crq(adapter, &crq);
+
+ return len;
+}
+
+static const struct file_operations paused_ops = {
+ .owner = THIS_MODULE,
+ .open = ibmvnic_fw_comp_open,
+ .read = paused_read,
+ .write = paused_write,
+};
+
+static ssize_t tracing_read(struct file *file, char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ int num = ras_comp_int->num;
+ char buff[5]; /* 1 or 0 plus \n and \0 */
+ int size;
+
+ size = sprintf(buff, "%d\n", adapter->ras_comps[num].trace_on);
+
+ if (*ppos >= size)
+ return 0;
+
+ copy_to_user(user_buf, buff, size);
+ *ppos += size;
+ return size;
+}
+
+static ssize_t tracing_write(struct file *file, const char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ int num = ras_comp_int->num;
+ union ibmvnic_crq crq;
+ unsigned long val;
+ char buff[9]; /* decimal max int plus \n and \0 */
+
+ copy_from_user(buff, user_buf, sizeof(buff));
+ val = kstrtoul(buff, 10, NULL);
+
+ memset(&crq, 0, sizeof(crq));
+ crq.control_ras.first = IBMVNIC_CRQ_CMD;
+ crq.control_ras.cmd = CONTROL_RAS;
+ crq.control_ras.correlator = adapter->ras_comps[num].correlator;
+ crq.control_ras.op = val ? IBMVNIC_TRACE_ON : IBMVNIC_TRACE_OFF;
+
+ return len;
+}
+
+static const struct file_operations tracing_ops = {
+ .owner = THIS_MODULE,
+ .open = ibmvnic_fw_comp_open,
+ .read = tracing_read,
+ .write = tracing_write,
+};
+
+static ssize_t error_level_read(struct file *file, char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ int num = ras_comp_int->num;
+ char buff[5]; /* decimal max char plus \n and \0 */
+ int size;
+
+ size = sprintf(buff, "%d\n", adapter->ras_comps[num].error_check_level);
+
+ if (*ppos >= size)
+ return 0;
+
+ copy_to_user(user_buf, buff, size);
+ *ppos += size;
+ return size;
+}
+
+static ssize_t error_level_write(struct file *file, const char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ int num = ras_comp_int->num;
+ union ibmvnic_crq crq;
+ unsigned long val;
+ char buff[9]; /* decimal max int plus \n and \0 */
+
+ copy_from_user(buff, user_buf, sizeof(buff));
+ val = kstrtoul(buff, 10, NULL);
+
+ if (val > 9)
+ val = 9;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.control_ras.first = IBMVNIC_CRQ_CMD;
+ crq.control_ras.cmd = CONTROL_RAS;
+ crq.control_ras.correlator = adapter->ras_comps[num].correlator;
+ crq.control_ras.op = IBMVNIC_ERROR_LEVEL;
+ crq.control_ras.level = val;
+ ibmvnic_send_crq(adapter, &crq);
+
+ return len;
+}
+
+static const struct file_operations error_level_ops = {
+ .owner = THIS_MODULE,
+ .open = ibmvnic_fw_comp_open,
+ .read = error_level_read,
+ .write = error_level_write,
+};
+
+static ssize_t trace_level_read(struct file *file, char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ int num = ras_comp_int->num;
+ char buff[5]; /* decimal max char plus \n and \0 */
+ int size;
+
+ size = sprintf(buff, "%d\n", adapter->ras_comps[num].trace_level);
+ if (*ppos >= size)
+ return 0;
+
+ copy_to_user(user_buf, buff, size);
+ *ppos += size;
+ return size;
+}
+
+static ssize_t trace_level_write(struct file *file, const char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ union ibmvnic_crq crq;
+ unsigned long val;
+ char buff[9]; /* decimal max int plus \n and \0 */
+
+ copy_from_user(buff, user_buf, sizeof(buff));
+ val = kstrtoul(buff, 10, NULL);
+ if (val > 9)
+ val = 9;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.control_ras.first = IBMVNIC_CRQ_CMD;
+ crq.control_ras.cmd = CONTROL_RAS;
+ crq.control_ras.correlator =
+ adapter->ras_comps[ras_comp_int->num].correlator;
+ crq.control_ras.op = IBMVNIC_TRACE_LEVEL;
+ crq.control_ras.level = val;
+ ibmvnic_send_crq(adapter, &crq);
+
+ return len;
+}
+
+static const struct file_operations trace_level_ops = {
+ .owner = THIS_MODULE,
+ .open = ibmvnic_fw_comp_open,
+ .read = trace_level_read,
+ .write = trace_level_write,
+};
+
+static ssize_t trace_buff_size_read(struct file *file, char __user *user_buf,
+ size_t len, loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ int num = ras_comp_int->num;
+ char buff[9]; /* decimal max int plus \n and \0 */
+ int size;
+
+ size = sprintf(buff, "%d\n", adapter->ras_comps[num].trace_buff_size);
+ if (*ppos >= size)
+ return 0;
+
+ copy_to_user(user_buf, buff, size);
+ *ppos += size;
+ return size;
+}
+
+static ssize_t trace_buff_size_write(struct file *file,
+ const char __user *user_buf, size_t len,
+ loff_t *ppos)
+{
+ struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data;
+ struct ibmvnic_adapter *adapter = ras_comp_int->adapter;
+ union ibmvnic_crq crq;
+ unsigned long val;
+ char buff[9]; /* decimal max int plus \n and \0 */
+
+ copy_from_user(buff, user_buf, sizeof(buff));
+ val = kstrtoul(buff, 10, NULL);
+
+ memset(&crq, 0, sizeof(crq));
+ crq.control_ras.first = IBMVNIC_CRQ_CMD;
+ crq.control_ras.cmd = CONTROL_RAS;
+ crq.control_ras.correlator =
+ adapter->ras_comps[ras_comp_int->num].correlator;
+ crq.control_ras.op = IBMVNIC_CHG_TRACE_BUFF_SZ;
+ /* trace_buff_sz is 3 bytes, stuff an int into it */
+ crq.control_ras.trace_buff_sz[0] = ((u8 *)(&val))[5];
+ crq.control_ras.trace_buff_sz[1] = ((u8 *)(&val))[6];
+ crq.control_ras.trace_buff_sz[2] = ((u8 *)(&val))[7];
+ ibmvnic_send_crq(adapter, &crq);
+
+ return len;
+}
+
+static const struct file_operations trace_size_ops = {
+ .owner = THIS_MODULE,
+ .open = ibmvnic_fw_comp_open,
+ .read = trace_buff_size_read,
+ .write = trace_buff_size_write,
+};
+
+static void handle_request_ras_comps_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ struct device *dev = &adapter->vdev->dev;
+ struct dentry *dir_ent;
+ struct dentry *ent;
+ int i;
+
+ debugfs_remove_recursive(adapter->ras_comps_ent);
+
+ adapter->ras_comps_ent = debugfs_create_dir("ras_comps",
+ adapter->debugfs_dir);
+ if (!adapter->ras_comps_ent || IS_ERR(adapter->ras_comps_ent)) {
+ dev_info(dev, "debugfs create ras_comps dir failed\n");
+ return;
+ }
+
+ for (i = 0; i < adapter->ras_comp_num; i++) {
+ dir_ent = debugfs_create_dir(adapter->ras_comps[i].name,
+ adapter->ras_comps_ent);
+ if (!dir_ent || IS_ERR(dir_ent)) {
+ dev_info(dev, "debugfs create %s dir failed\n",
+ adapter->ras_comps[i].name);
+ continue;
+ }
+
+ adapter->ras_comp_int[i].adapter = adapter;
+ adapter->ras_comp_int[i].num = i;
+ adapter->ras_comp_int[i].desc_blob.data =
+ &adapter->ras_comps[i].description;
+ adapter->ras_comp_int[i].desc_blob.size =
+ sizeof(adapter->ras_comps[i].description);
+
+ /* Don't need to remember the dentry's because the debugfs dir
+ * gets removed recursively
+ */
+ ent = debugfs_create_blob("description", S_IRUGO, dir_ent,
+ &adapter->ras_comp_int[i].desc_blob);
+ ent = debugfs_create_file("trace_buf_size", S_IRUGO | S_IWUSR,
+ dir_ent, &adapter->ras_comp_int[i],
+ &trace_size_ops);
+ ent = debugfs_create_file("trace_level",
+ S_IRUGO |
+ (adapter->ras_comps[i].trace_level !=
+ 0xFF ? S_IWUSR : 0),
+ dir_ent, &adapter->ras_comp_int[i],
+ &trace_level_ops);
+ ent = debugfs_create_file("error_level",
+ S_IRUGO |
+ (adapter->
+ ras_comps[i].error_check_level !=
+ 0xFF ? S_IWUSR : 0),
+ dir_ent, &adapter->ras_comp_int[i],
+ &trace_level_ops);
+ ent = debugfs_create_file("tracing", S_IRUGO | S_IWUSR,
+ dir_ent, &adapter->ras_comp_int[i],
+ &tracing_ops);
+ ent = debugfs_create_file("paused", S_IRUGO | S_IWUSR,
+ dir_ent, &adapter->ras_comp_int[i],
+ &paused_ops);
+ ent = debugfs_create_file("trace", S_IRUGO, dir_ent,
+ &adapter->ras_comp_int[i],
+ &trace_ops);
+ }
+}
+
+static void handle_request_ras_comp_num_rsp(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ int len = adapter->ras_comp_num * sizeof(struct ibmvnic_fw_component);
+ struct device *dev = &adapter->vdev->dev;
+ union ibmvnic_crq newcrq;
+
+ adapter->ras_comps = dma_alloc_coherent(dev, len,
+ &adapter->ras_comps_tok,
+ GFP_KERNEL);
+ if (!adapter->ras_comps) {
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ dev_err(dev, "Couldn't alloc fw comps buffer\n");
+ return;
+ }
+
+ adapter->ras_comp_int = kmalloc(adapter->ras_comp_num *
+ sizeof(struct ibmvnic_fw_comp_internal),
+ GFP_KERNEL);
+ if (!adapter->ras_comp_int)
+ dma_free_coherent(dev, len, adapter->ras_comps,
+ adapter->ras_comps_tok);
+
+ memset(&newcrq, 0, sizeof(newcrq));
+ newcrq.request_ras_comps.first = IBMVNIC_CRQ_CMD;
+ newcrq.request_ras_comps.cmd = REQUEST_RAS_COMPS;
+ newcrq.request_ras_comps.ioba = cpu_to_be32(adapter->ras_comps_tok);
+ newcrq.request_ras_comps.len = cpu_to_be32(len);
+ ibmvnic_send_crq(adapter, &newcrq);
+}
+
+static void ibmvnic_free_inflight(struct ibmvnic_adapter *adapter)
+{
+ struct ibmvnic_inflight_cmd *inflight_cmd;
+ struct device *dev = &adapter->vdev->dev;
+ struct ibmvnic_error_buff *error_buff;
+ unsigned long flags;
+ unsigned long flags2;
+
+ spin_lock_irqsave(&adapter->inflight_lock, flags);
+ list_for_each_entry(inflight_cmd, &adapter->inflight, list) {
+ switch (inflight_cmd->crq.generic.cmd) {
+ case LOGIN:
+ dma_unmap_single(dev, adapter->login_buf_token,
+ adapter->login_buf_sz,
+ DMA_BIDIRECTIONAL);
+ dma_unmap_single(dev, adapter->login_rsp_buf_token,
+ adapter->login_rsp_buf_sz,
+ DMA_BIDIRECTIONAL);
+ kfree(adapter->login_rsp_buf);
+ kfree(adapter->login_buf);
+ break;
+ case REQUEST_DUMP:
+ complete(&adapter->fw_done);
+ break;
+ case REQUEST_ERROR_INFO:
+ spin_lock_irqsave(&adapter->error_list_lock, flags2);
+ list_for_each_entry(error_buff, &adapter->errors,
+ list) {
+ dma_unmap_single(dev, error_buff->dma,
+ error_buff->len,
+ DMA_FROM_DEVICE);
+ kfree(error_buff->buff);
+ list_del(&error_buff->list);
+ kfree(error_buff);
+ }
+ spin_unlock_irqrestore(&adapter->error_list_lock,
+ flags2);
+ break;
+ }
+ list_del(&inflight_cmd->list);
+ kfree(inflight_cmd);
+ }
+ spin_unlock_irqrestore(&adapter->inflight_lock, flags);
+}
+
+static void ibmvnic_handle_crq(union ibmvnic_crq *crq,
+ struct ibmvnic_adapter *adapter)
+{
+ struct ibmvnic_generic_crq *gen_crq = &crq->generic;
+ struct net_device *netdev = adapter->netdev;
+ struct device *dev = &adapter->vdev->dev;
+ long rc;
+
+ netdev_dbg(netdev, "Handling CRQ: %016lx %016lx\n",
+ ((unsigned long int *)crq)[0],
+ ((unsigned long int *)crq)[1]);
+ switch (gen_crq->first) {
+ case IBMVNIC_CRQ_INIT_RSP:
+ switch (gen_crq->cmd) {
+ case IBMVNIC_CRQ_INIT:
+ dev_info(dev, "Partner initialized\n");
+ /* Send back a response */
+ rc = ibmvnic_send_crq_init_complete(adapter);
+ if (rc == 0)
+ send_version_xchg(adapter);
+ else
+ dev_err(dev, "Can't send initrsp rc=%ld\n", rc);
+ break;
+ case IBMVNIC_CRQ_INIT_COMPLETE:
+ dev_info(dev, "Partner initialization complete\n");
+ send_version_xchg(adapter);
+ break;
+ default:
+ dev_err(dev, "Unknown crq cmd: %d\n", gen_crq->cmd);
+ }
+ return;
+ case IBMVNIC_CRQ_XPORT_EVENT:
+ if (gen_crq->cmd == IBMVNIC_PARTITION_MIGRATED) {
+ dev_info(dev, "Re-enabling adapter\n");
+ adapter->migrated = true;
+ ibmvnic_free_inflight(adapter);
+ release_sub_crqs(adapter);
+ rc = ibmvnic_reenable_crq_queue(adapter);
+ if (rc)
+ dev_err(dev, "Error after enable rc=%ld\n", rc);
+ adapter->migrated = false;
+ rc = ibmvnic_send_crq_init(adapter);
+ if (rc)
+ dev_err(dev, "Error sending init rc=%ld\n", rc);
+ } else {
+ /* The adapter lost the connection */
+ dev_err(dev, "Virtual Adapter failed (rc=%d)\n",
+ gen_crq->cmd);
+ ibmvnic_free_inflight(adapter);
+ release_sub_crqs(adapter);
+ }
+ return;
+ case IBMVNIC_CRQ_CMD_RSP:
+ break;
+ default:
+ dev_err(dev, "Got an invalid msg type 0x%02x\n",
+ gen_crq->first);
+ return;
+ }
+
+ switch (gen_crq->cmd) {
+ case VERSION_EXCHANGE_RSP:
+ rc = crq->version_exchange_rsp.rc.code;
+ if (rc) {
+ dev_err(dev, "Error %ld in VERSION_EXCHG_RSP\n", rc);
+ break;
+ }
+ dev_info(dev, "Partner protocol version is %d\n",
+ crq->version_exchange_rsp.version);
+ if (be16_to_cpu(crq->version_exchange_rsp.version) <
+ ibmvnic_version)
+ ibmvnic_version =
+ be16_to_cpu(crq->version_exchange_rsp.version);
+ send_cap_queries(adapter);
+ break;
+ case QUERY_CAPABILITY_RSP:
+ handle_query_cap_rsp(crq, adapter);
+ break;
+ case QUERY_MAP_RSP:
+ handle_query_map_rsp(crq, adapter);
+ break;
+ case REQUEST_MAP_RSP:
+ handle_request_map_rsp(crq, adapter);
+ break;
+ case REQUEST_UNMAP_RSP:
+ handle_request_unmap_rsp(crq, adapter);
+ break;
+ case REQUEST_CAPABILITY_RSP:
+ handle_request_cap_rsp(crq, adapter);
+ break;
+ case LOGIN_RSP:
+ netdev_dbg(netdev, "Got Login Response\n");
+ handle_login_rsp(crq, adapter);
+ break;
+ case LOGICAL_LINK_STATE_RSP:
+ netdev_dbg(netdev, "Got Logical Link State Response\n");
+ adapter->logical_link_state =
+ crq->logical_link_state_rsp.link_state;
+ break;
+ case LINK_STATE_INDICATION:
+ netdev_dbg(netdev, "Got Logical Link State Indication\n");
+ adapter->phys_link_state =
+ crq->link_state_indication.phys_link_state;
+ adapter->logical_link_state =
+ crq->link_state_indication.logical_link_state;
+ break;
+ case CHANGE_MAC_ADDR_RSP:
+ netdev_dbg(netdev, "Got MAC address change Response\n");
+ handle_change_mac_rsp(crq, adapter);
+ break;
+ case ERROR_INDICATION:
+ netdev_dbg(netdev, "Got Error Indication\n");
+ handle_error_indication(crq, adapter);
+ break;
+ case REQUEST_ERROR_RSP:
+ netdev_dbg(netdev, "Got Error Detail Response\n");
+ handle_error_info_rsp(crq, adapter);
+ break;
+ case REQUEST_STATISTICS_RSP:
+ netdev_dbg(netdev, "Got Statistics Response\n");
+ complete(&adapter->stats_done);
+ break;
+ case REQUEST_DUMP_SIZE_RSP:
+ netdev_dbg(netdev, "Got Request Dump Size Response\n");
+ handle_dump_size_rsp(crq, adapter);
+ break;
+ case REQUEST_DUMP_RSP:
+ netdev_dbg(netdev, "Got Request Dump Response\n");
+ complete(&adapter->fw_done);
+ break;
+ case QUERY_IP_OFFLOAD_RSP:
+ netdev_dbg(netdev, "Got Query IP offload Response\n");
+ handle_query_ip_offload_rsp(adapter);
+ break;
+ case MULTICAST_CTRL_RSP:
+ netdev_dbg(netdev, "Got multicast control Response\n");
+ break;
+ case CONTROL_IP_OFFLOAD_RSP:
+ netdev_dbg(netdev, "Got Control IP offload Response\n");
+ dma_unmap_single(dev, adapter->ip_offload_ctrl_tok,
+ sizeof(adapter->ip_offload_ctrl),
+ DMA_TO_DEVICE);
+ /* We're done with the queries, perform the login */
+ send_login(adapter);
+ break;
+ case REQUEST_RAS_COMP_NUM_RSP:
+ netdev_dbg(netdev, "Got Request RAS Comp Num Response\n");
+ if (crq->request_ras_comp_num_rsp.rc.code == 10) {
+ netdev_dbg(netdev, "Request RAS Comp Num not supported\n");
+ break;
+ }
+ adapter->ras_comp_num =
+ be32_to_cpu(crq->request_ras_comp_num_rsp.num_components);
+ handle_request_ras_comp_num_rsp(crq, adapter);
+ break;
+ case REQUEST_RAS_COMPS_RSP:
+ netdev_dbg(netdev, "Got Request RAS Comps Response\n");
+ handle_request_ras_comps_rsp(crq, adapter);
+ break;
+ case CONTROL_RAS_RSP:
+ netdev_dbg(netdev, "Got Control RAS Response\n");
+ handle_control_ras_rsp(crq, adapter);
+ break;
+ case COLLECT_FW_TRACE_RSP:
+ netdev_dbg(netdev, "Got Collect firmware trace Response\n");
+ complete(&adapter->fw_done);
+ break;
+ default:
+ netdev_err(netdev, "Got an invalid cmd type 0x%02x\n",
+ gen_crq->cmd);
+ }
+}
+
+static irqreturn_t ibmvnic_interrupt(int irq, void *instance)
+{
+ struct ibmvnic_adapter *adapter = instance;
+ struct ibmvnic_crq_queue *queue = &adapter->crq;
+ struct vio_dev *vdev = adapter->vdev;
+ union ibmvnic_crq *crq;
+ unsigned long flags;
+ bool done = false;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ vio_disable_interrupts(vdev);
+ while (!done) {
+ /* Pull all the valid messages off the CRQ */
+ while ((crq = ibmvnic_next_crq(adapter)) != NULL) {
+ ibmvnic_handle_crq(crq, adapter);
+ crq->generic.first = 0;
+ }
+ vio_enable_interrupts(vdev);
+ crq = ibmvnic_next_crq(adapter);
+ if (crq) {
+ vio_disable_interrupts(vdev);
+ ibmvnic_handle_crq(crq, adapter);
+ crq->generic.first = 0;
+ } else {
+ done = true;
+ }
+ }
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static int ibmvnic_reenable_crq_queue(struct ibmvnic_adapter *adapter)
+{
+ struct vio_dev *vdev = adapter->vdev;
+ int rc;
+
+ do {
+ rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address);
+ } while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc));
+
+ if (rc)
+ dev_err(&vdev->dev, "Error enabling adapter (rc=%d)\n", rc);
+
+ return rc;
+}
+
+static int ibmvnic_reset_crq(struct ibmvnic_adapter *adapter)
+{
+ struct ibmvnic_crq_queue *crq = &adapter->crq;
+ struct device *dev = &adapter->vdev->dev;
+ struct vio_dev *vdev = adapter->vdev;
+ int rc;
+
+ /* Close the CRQ */
+ do {
+ rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
+ } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+
+ /* Clean out the queue */
+ memset(crq->msgs, 0, PAGE_SIZE);
+ crq->cur = 0;
+
+ /* And re-open it again */
+ rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address,
+ crq->msg_token, PAGE_SIZE);
+
+ if (rc == H_CLOSED)
+ /* Adapter is good, but other end is not ready */
+ dev_warn(dev, "Partner adapter not ready\n");
+ else if (rc != 0)
+ dev_warn(dev, "Couldn't register crq (rc=%d)\n", rc);
+
+ return rc;
+}
+
+static void ibmvnic_release_crq_queue(struct ibmvnic_adapter *adapter)
+{
+ struct ibmvnic_crq_queue *crq = &adapter->crq;
+ struct vio_dev *vdev = adapter->vdev;
+ long rc;
+
+ netdev_dbg(adapter->netdev, "Releasing CRQ\n");
+ free_irq(vdev->irq, adapter);
+ do {
+ rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
+ } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+
+ dma_unmap_single(&vdev->dev, crq->msg_token, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ free_page((unsigned long)crq->msgs);
+}
+
+static int ibmvnic_init_crq_queue(struct ibmvnic_adapter *adapter)
+{
+ struct ibmvnic_crq_queue *crq = &adapter->crq;
+ struct device *dev = &adapter->vdev->dev;
+ struct vio_dev *vdev = adapter->vdev;
+ int rc, retrc = -ENOMEM;
+
+ crq->msgs = (union ibmvnic_crq *)get_zeroed_page(GFP_KERNEL);
+ /* Should we allocate more than one page? */
+
+ if (!crq->msgs)
+ return -ENOMEM;
+
+ crq->size = PAGE_SIZE / sizeof(*crq->msgs);
+ crq->msg_token = dma_map_single(dev, crq->msgs, PAGE_SIZE,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dev, crq->msg_token))
+ goto map_failed;
+
+ rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address,
+ crq->msg_token, PAGE_SIZE);
+
+ if (rc == H_RESOURCE)
+ /* maybe kexecing and resource is busy. try a reset */
+ rc = ibmvnic_reset_crq(adapter);
+ retrc = rc;
+
+ if (rc == H_CLOSED) {
+ dev_warn(dev, "Partner adapter not ready\n");
+ } else if (rc) {
+ dev_warn(dev, "Error %d opening adapter\n", rc);
+ goto reg_crq_failed;
+ }
+
+ retrc = 0;
+
+ netdev_dbg(adapter->netdev, "registering irq 0x%x\n", vdev->irq);
+ rc = request_irq(vdev->irq, ibmvnic_interrupt, 0, IBMVNIC_NAME,
+ adapter);
+ if (rc) {
+ dev_err(dev, "Couldn't register irq 0x%x. rc=%d\n",
+ vdev->irq, rc);
+ goto req_irq_failed;
+ }
+
+ rc = vio_enable_interrupts(vdev);
+ if (rc) {
+ dev_err(dev, "Error %d enabling interrupts\n", rc);
+ goto req_irq_failed;
+ }
+
+ crq->cur = 0;
+ spin_lock_init(&crq->lock);
+
+ return retrc;
+
+req_irq_failed:
+ do {
+ rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
+ } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+reg_crq_failed:
+ dma_unmap_single(dev, crq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL);
+map_failed:
+ free_page((unsigned long)crq->msgs);
+ return retrc;
+}
+
+/* debugfs for dump */
+static int ibmvnic_dump_show(struct seq_file *seq, void *v)
+{
+ struct net_device *netdev = seq->private;
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+ struct device *dev = &adapter->vdev->dev;
+ union ibmvnic_crq crq;
+
+ memset(&crq, 0, sizeof(crq));
+ crq.request_dump_size.first = IBMVNIC_CRQ_CMD;
+ crq.request_dump_size.cmd = REQUEST_DUMP_SIZE;
+ ibmvnic_send_crq(adapter, &crq);
+
+ init_completion(&adapter->fw_done);
+ wait_for_completion(&adapter->fw_done);
+
+ seq_write(seq, adapter->dump_data, adapter->dump_data_size);
+
+ dma_unmap_single(dev, adapter->dump_data_token, adapter->dump_data_size,
+ DMA_BIDIRECTIONAL);
+
+ kfree(adapter->dump_data);
+
+ return 0;
+}
+
+static int ibmvnic_dump_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ibmvnic_dump_show, inode->i_private);
+}
+
+static const struct file_operations ibmvnic_dump_ops = {
+ .owner = THIS_MODULE,
+ .open = ibmvnic_dump_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
+{
+ struct ibmvnic_adapter *adapter;
+ struct net_device *netdev;
+ unsigned char *mac_addr_p;
+ struct dentry *ent;
+ char buf[16]; /* debugfs name buf */
+ int rc;
+
+ dev_dbg(&dev->dev, "entering ibmvnic_probe for UA 0x%x\n",
+ dev->unit_address);
+
+ mac_addr_p = (unsigned char *)vio_get_attribute(dev,
+ VETH_MAC_ADDR, NULL);
+ if (!mac_addr_p) {
+ dev_err(&dev->dev,
+ "(%s:%3.3d) ERROR: Can't find MAC_ADDR attribute\n",
+ __FILE__, __LINE__);
+ return 0;
+ }
+
+ netdev = alloc_etherdev_mq(sizeof(struct ibmvnic_adapter),
+ IBMVNIC_MAX_TX_QUEUES);
+ if (!netdev)
+ return -ENOMEM;
+
+ adapter = netdev_priv(netdev);
+ dev_set_drvdata(&dev->dev, netdev);
+ adapter->vdev = dev;
+ adapter->netdev = netdev;
+
+ ether_addr_copy(adapter->mac_addr, mac_addr_p);
+ ether_addr_copy(netdev->dev_addr, adapter->mac_addr);
+ netdev->irq = dev->irq;
+ netdev->netdev_ops = &ibmvnic_netdev_ops;
+ netdev->ethtool_ops = &ibmvnic_ethtool_ops;
+ SET_NETDEV_DEV(netdev, &dev->dev);
+
+ spin_lock_init(&adapter->stats_lock);
+
+ rc = ibmvnic_init_crq_queue(adapter);
+ if (rc) {
+ dev_err(&dev->dev, "Couldn't initialize crq. rc=%d\n", rc);
+ goto free_netdev;
+ }
+
+ INIT_LIST_HEAD(&adapter->errors);
+ INIT_LIST_HEAD(&adapter->inflight);
+ spin_lock_init(&adapter->error_list_lock);
+ spin_lock_init(&adapter->inflight_lock);
+
+ adapter->stats_token = dma_map_single(&dev->dev, &adapter->stats,
+ sizeof(struct ibmvnic_statistics),
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&dev->dev, adapter->stats_token)) {
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ dev_err(&dev->dev, "Couldn't map stats buffer\n");
+ goto free_crq;
+ }
+
+ snprintf(buf, sizeof(buf), "ibmvnic_%x", dev->unit_address);
+ ent = debugfs_create_dir(buf, NULL);
+ if (!ent || IS_ERR(ent)) {
+ dev_info(&dev->dev, "debugfs create directory failed\n");
+ adapter->debugfs_dir = NULL;
+ } else {
+ adapter->debugfs_dir = ent;
+ ent = debugfs_create_file("dump", S_IRUGO, adapter->debugfs_dir,
+ netdev, &ibmvnic_dump_ops);
+ if (!ent || IS_ERR(ent)) {
+ dev_info(&dev->dev,
+ "debugfs create dump file failed\n");
+ adapter->debugfs_dump = NULL;
+ } else {
+ adapter->debugfs_dump = ent;
+ }
+ }
+ ibmvnic_send_crq_init(adapter);
+
+ init_completion(&adapter->init_done);
+ wait_for_completion(&adapter->init_done);
+
+ /* needed to pull init_sub_crqs outside of an interrupt context
+ * because it creates IRQ mappings for the subCRQ queues, causing
+ * a kernel warning
+ */
+ init_sub_crqs(adapter, 0);
+
+ reinit_completion(&adapter->init_done);
+ wait_for_completion(&adapter->init_done);
+
+ /* if init_sub_crqs is partially successful, retry */
+ while (!adapter->tx_scrq || !adapter->rx_scrq) {
+ init_sub_crqs(adapter, 1);
+
+ reinit_completion(&adapter->init_done);
+ wait_for_completion(&adapter->init_done);
+ }
+
+ netdev->real_num_tx_queues = adapter->req_tx_queues;
+
+ rc = register_netdev(netdev);
+ if (rc) {
+ dev_err(&dev->dev, "failed to register netdev rc=%d\n", rc);
+ goto free_debugfs;
+ }
+ dev_info(&dev->dev, "ibmvnic registered\n");
+
+ return 0;
+
+free_debugfs:
+ if (adapter->debugfs_dir && !IS_ERR(adapter->debugfs_dir))
+ debugfs_remove_recursive(adapter->debugfs_dir);
+free_crq:
+ ibmvnic_release_crq_queue(adapter);
+free_netdev:
+ free_netdev(netdev);
+ return rc;
+}
+
+static int ibmvnic_remove(struct vio_dev *dev)
+{
+ struct net_device *netdev = dev_get_drvdata(&dev->dev);
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+
+ unregister_netdev(netdev);
+
+ release_sub_crqs(adapter);
+
+ ibmvnic_release_crq_queue(adapter);
+
+ if (adapter->debugfs_dir && !IS_ERR(adapter->debugfs_dir))
+ debugfs_remove_recursive(adapter->debugfs_dir);
+
+ if (adapter->ras_comps)
+ dma_free_coherent(&dev->dev,
+ adapter->ras_comp_num *
+ sizeof(struct ibmvnic_fw_component),
+ adapter->ras_comps, adapter->ras_comps_tok);
+
+ kfree(adapter->ras_comp_int);
+
+ free_netdev(netdev);
+ dev_set_drvdata(&dev->dev, NULL);
+
+ return 0;
+}
+
+static unsigned long ibmvnic_get_desired_dma(struct vio_dev *vdev)
+{
+ struct net_device *netdev = dev_get_drvdata(&vdev->dev);
+ struct ibmvnic_adapter *adapter;
+ struct iommu_table *tbl;
+ unsigned long ret = 0;
+ int i;
+
+ tbl = get_iommu_table_base(&vdev->dev);
+
+ /* netdev inits at probe time along with the structures we need below*/
+ if (!netdev)
+ return IOMMU_PAGE_ALIGN(IBMVNIC_IO_ENTITLEMENT_DEFAULT, tbl);
+
+ adapter = netdev_priv(netdev);
+
+ ret += PAGE_SIZE; /* the crq message queue */
+ ret += adapter->bounce_buffer_size;
+ ret += IOMMU_PAGE_ALIGN(sizeof(struct ibmvnic_statistics), tbl);
+
+ for (i = 0; i < adapter->req_tx_queues + adapter->req_rx_queues; i++)
+ ret += 4 * PAGE_SIZE; /* the scrq message queue */
+
+ for (i = 0; i < be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs);
+ i++)
+ ret += adapter->rx_pool[i].size *
+ IOMMU_PAGE_ALIGN(adapter->rx_pool[i].buff_size, tbl);
+
+ return ret;
+}
+
+static int ibmvnic_resume(struct device *dev)
+{
+ struct net_device *netdev = dev_get_drvdata(dev);
+ struct ibmvnic_adapter *adapter = netdev_priv(netdev);
+ int i;
+
+ /* kick the interrupt handlers just in case we lost an interrupt */
+ for (i = 0; i < adapter->req_rx_queues; i++)
+ ibmvnic_interrupt_rx(adapter->rx_scrq[i]->irq,
+ adapter->rx_scrq[i]);
+
+ return 0;
+}
+
+static struct vio_device_id ibmvnic_device_table[] = {
+ {"network", "IBM,vnic"},
+ {"", "" }
+};
+MODULE_DEVICE_TABLE(vio, ibmvnic_device_table);
+
+static const struct dev_pm_ops ibmvnic_pm_ops = {
+ .resume = ibmvnic_resume
+};
+
+static struct vio_driver ibmvnic_driver = {
+ .id_table = ibmvnic_device_table,
+ .probe = ibmvnic_probe,
+ .remove = ibmvnic_remove,
+ .get_desired_dma = ibmvnic_get_desired_dma,
+ .name = ibmvnic_driver_name,
+ .pm = &ibmvnic_pm_ops,
+};
+
+/* module functions */
+static int __init ibmvnic_module_init(void)
+{
+ pr_info("%s: %s %s\n", ibmvnic_driver_name, ibmvnic_driver_string,
+ IBMVNIC_DRIVER_VERSION);
+
+ return vio_register_driver(&ibmvnic_driver);
+}
+
+static void __exit ibmvnic_module_exit(void)
+{
+ vio_unregister_driver(&ibmvnic_driver);
+}
+
+module_init(ibmvnic_module_init);
+module_exit(ibmvnic_module_exit);
diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h
new file mode 100644
index 000000000000..1242925ad34c
--- /dev/null
+++ b/drivers/net/ethernet/ibm/ibmvnic.h
@@ -0,0 +1,1046 @@
+/**************************************************************************/
+/* */
+/* IBM System i and System p Virtual NIC Device Driver */
+/* Copyright (C) 2014 IBM Corp. */
+/* Santiago Leon (santi_leon@yahoo.com) */
+/* Thomas Falcon (tlfalcon@linux.vnet.ibm.com) */
+/* John Allen (jallen@linux.vnet.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 */
+/* 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. */
+/* */
+/* This module contains the implementation of a virtual ethernet device */
+/* for use with IBM i/pSeries LPAR Linux. It utilizes the logical LAN */
+/* option of the RS/6000 Platform Architecture to interface with virtual */
+/* ethernet NICs that are presented to the partition by the hypervisor. */
+/* */
+/**************************************************************************/
+
+#define IBMVNIC_NAME "ibmvnic"
+#define IBMVNIC_DRIVER_VERSION "1.0"
+#define IBMVNIC_INVALID_MAP -1
+#define IBMVNIC_STATS_TIMEOUT 1
+/* basic structures plus 100 2k buffers */
+#define IBMVNIC_IO_ENTITLEMENT_DEFAULT 610305
+
+/* Initial module_parameters */
+#define IBMVNIC_RX_WEIGHT 16
+/* when changing this, update IBMVNIC_IO_ENTITLEMENT_DEFAULT */
+#define IBMVNIC_BUFFS_PER_POOL 100
+#define IBMVNIC_MAX_TX_QUEUES 5
+
+struct ibmvnic_login_buffer {
+ __be32 len;
+ __be32 version;
+#define INITIAL_VERSION_LB 1
+ __be32 num_txcomp_subcrqs;
+ __be32 off_txcomp_subcrqs;
+ __be32 num_rxcomp_subcrqs;
+ __be32 off_rxcomp_subcrqs;
+ __be32 login_rsp_ioba;
+ __be32 login_rsp_len;
+} __packed __aligned(8);
+
+struct ibmvnic_login_rsp_buffer {
+ __be32 len;
+ __be32 version;
+#define INITIAL_VERSION_LRB 1
+ __be32 num_txsubm_subcrqs;
+ __be32 off_txsubm_subcrqs;
+ __be32 num_rxadd_subcrqs;
+ __be32 off_rxadd_subcrqs;
+ __be32 off_rxadd_buff_size;
+ __be32 num_supp_tx_desc;
+ __be32 off_supp_tx_desc;
+} __packed __aligned(8);
+
+struct ibmvnic_query_ip_offload_buffer {
+ __be32 len;
+ __be32 version;
+#define INITIAL_VERSION_IOB 1
+ u8 ipv4_chksum;
+ u8 ipv6_chksum;
+ u8 tcp_ipv4_chksum;
+ u8 tcp_ipv6_chksum;
+ u8 udp_ipv4_chksum;
+ u8 udp_ipv6_chksum;
+ u8 large_tx_ipv4;
+ u8 large_tx_ipv6;
+ u8 large_rx_ipv4;
+ u8 large_rx_ipv6;
+ u8 reserved1[14];
+ __be16 max_ipv4_header_size;
+ __be16 max_ipv6_header_size;
+ __be16 max_tcp_header_size;
+ __be16 max_udp_header_size;
+ __be32 max_large_tx_size;
+ __be32 max_large_rx_size;
+ u8 reserved2[16];
+ u8 ipv6_extension_header;
+#define IPV6_EH_NOT_SUPPORTED 0x00
+#define IPV6_EH_SUPPORTED_LIM 0x01
+#define IPV6_EH_SUPPORTED 0xFF
+ u8 tcp_pseudosum_req;
+#define TCP_PS_NOT_REQUIRED 0x00
+#define TCP_PS_REQUIRED 0x01
+ u8 reserved3[30];
+ __be16 num_ipv6_ext_headers;
+ __be32 off_ipv6_ext_headers;
+ u8 reserved4[154];
+} __packed __aligned(8);
+
+struct ibmvnic_control_ip_offload_buffer {
+ __be32 len;
+ __be32 version;
+#define INITIAL_VERSION_IOB 1
+ u8 ipv4_chksum;
+ u8 ipv6_chksum;
+ u8 tcp_ipv4_chksum;
+ u8 tcp_ipv6_chksum;
+ u8 udp_ipv4_chksum;
+ u8 udp_ipv6_chksum;
+ u8 large_tx_ipv4;
+ u8 large_tx_ipv6;
+ u8 bad_packet_rx;
+ u8 large_rx_ipv4;
+ u8 large_rx_ipv6;
+ u8 reserved4[111];
+} __packed __aligned(8);
+
+struct ibmvnic_fw_component {
+ u8 name[48];
+ __be32 trace_buff_size;
+ u8 correlator;
+ u8 trace_level;
+ u8 parent_correlator;
+ u8 error_check_level;
+ u8 trace_on;
+ u8 reserved[7];
+ u8 description[192];
+} __packed __aligned(8);
+
+struct ibmvnic_fw_trace_entry {
+ __be32 trace_id;
+ u8 num_valid_data;
+ u8 reserved[3];
+ __be64 pmc_registers;
+ __be64 timebase;
+ __be64 trace_data[5];
+} __packed __aligned(8);
+
+struct ibmvnic_statistics {
+ __be32 version;
+ __be32 promiscuous;
+ __be64 rx_packets;
+ __be64 rx_bytes;
+ __be64 tx_packets;
+ __be64 tx_bytes;
+ __be64 ucast_tx_packets;
+ __be64 ucast_rx_packets;
+ __be64 mcast_tx_packets;
+ __be64 mcast_rx_packets;
+ __be64 bcast_tx_packets;
+ __be64 bcast_rx_packets;
+ __be64 align_errors;
+ __be64 fcs_errors;
+ __be64 single_collision_frames;
+ __be64 multi_collision_frames;
+ __be64 sqe_test_errors;
+ __be64 deferred_tx;
+ __be64 late_collisions;
+ __be64 excess_collisions;
+ __be64 internal_mac_tx_errors;
+ __be64 carrier_sense;
+ __be64 too_long_frames;
+ __be64 internal_mac_rx_errors;
+ u8 reserved[72];
+} __packed __aligned(8);
+
+struct ibmvnic_acl_buffer {
+ __be32 len;
+ __be32 version;
+#define INITIAL_VERSION_IOB 1
+ u8 mac_acls_restrict;
+ u8 vlan_acls_restrict;
+ u8 reserved1[22];
+ __be32 num_mac_addrs;
+ __be32 offset_mac_addrs;
+ __be32 num_vlan_ids;
+ __be32 offset_vlan_ids;
+ u8 reserved2[80];
+} __packed __aligned(8);
+
+/* descriptors have been changed, how should this be defined? 1? 4? */
+
+#define IBMVNIC_TX_DESC_VERSIONS 3
+
+/* is this still needed? */
+struct ibmvnic_tx_comp_desc {
+ u8 first;
+ u8 num_comps;
+ __be16 rcs[5];
+ __be32 correlators[5];
+} __packed __aligned(8);
+
+/* some flags that included in v0 descriptor, which is gone
+ * only used for IBMVNIC_TCP_CHKSUM and IBMVNIC_UDP_CHKSUM
+ * and only in some offload_flags variable that doesn't seem
+ * to be used anywhere, can probably be removed?
+ */
+
+#define IBMVNIC_TCP_CHKSUM 0x20
+#define IBMVNIC_UDP_CHKSUM 0x08
+
+#define IBMVNIC_MAX_FRAGS_PER_CRQ 3
+
+struct ibmvnic_tx_desc {
+ u8 first;
+ u8 type;
+
+#define IBMVNIC_TX_DESC 0x10
+ u8 n_crq_elem;
+ u8 n_sge;
+ u8 flags1;
+#define IBMVNIC_TX_COMP_NEEDED 0x80
+#define IBMVNIC_TX_CHKSUM_OFFLOAD 0x40
+#define IBMVNIC_TX_LSO 0x20
+#define IBMVNIC_TX_PROT_TCP 0x10
+#define IBMVNIC_TX_PROT_UDP 0x08
+#define IBMVNIC_TX_PROT_IPV4 0x04
+#define IBMVNIC_TX_PROT_IPV6 0x02
+#define IBMVNIC_TX_VLAN_PRESENT 0x01
+ u8 flags2;
+#define IBMVNIC_TX_VLAN_INSERT 0x80
+ __be16 mss;
+ u8 reserved[4];
+ __be32 correlator;
+ __be16 vlan_id;
+ __be16 dma_reg;
+ __be32 sge_len;
+ __be64 ioba;
+} __packed __aligned(8);
+
+struct ibmvnic_hdr_desc {
+ u8 first;
+ u8 type;
+#define IBMVNIC_HDR_DESC 0x11
+ u8 len;
+ u8 l2_len;
+ __be16 l3_len;
+ u8 l4_len;
+ u8 flag;
+ u8 data[24];
+} __packed __aligned(8);
+
+struct ibmvnic_hdr_ext_desc {
+ u8 first;
+ u8 type;
+#define IBMVNIC_HDR_EXT_DESC 0x12
+ u8 len;
+ u8 data[29];
+} __packed __aligned(8);
+
+struct ibmvnic_sge_desc {
+ u8 first;
+ u8 type;
+#define IBMVNIC_SGE_DESC 0x30
+ __be16 sge1_dma_reg;
+ __be32 sge1_len;
+ __be64 sge1_ioba;
+ __be16 reserved;
+ __be16 sge2_dma_reg;
+ __be32 sge2_len;
+ __be64 sge2_ioba;
+} __packed __aligned(8);
+
+struct ibmvnic_rx_comp_desc {
+ u8 first;
+ u8 flags;
+#define IBMVNIC_IP_CHKSUM_GOOD 0x80
+#define IBMVNIC_TCP_UDP_CHKSUM_GOOD 0x40
+#define IBMVNIC_END_FRAME 0x20
+#define IBMVNIC_EXACT_MC 0x10
+#define IBMVNIC_VLAN_STRIPPED 0x08
+ __be16 off_frame_data;
+ __be32 len;
+ __be64 correlator;
+ __be16 vlan_tci;
+ __be16 rc;
+ u8 reserved[12];
+} __packed __aligned(8);
+
+struct ibmvnic_generic_scrq {
+ u8 first;
+ u8 reserved[31];
+} __packed __aligned(8);
+
+struct ibmvnic_rx_buff_add_desc {
+ u8 first;
+ u8 reserved[7];
+ __be64 correlator;
+ __be32 ioba;
+ u8 map_id;
+ __be32 len:24;
+ u8 reserved2[8];
+} __packed __aligned(8);
+
+struct ibmvnic_rc {
+ u8 code; /* one of enum ibmvnic_rc_codes */
+ u8 detailed_data[3];
+} __packed __aligned(4);
+
+struct ibmvnic_generic_crq {
+ u8 first;
+ u8 cmd;
+ u8 params[10];
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_version_exchange {
+ u8 first;
+ u8 cmd;
+ __be16 version;
+#define IBMVNIC_INITIAL_VERSION 1
+ u8 reserved[8];
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_capability {
+ u8 first;
+ u8 cmd;
+ __be16 capability; /* one of ibmvnic_capabilities */
+ struct ibmvnic_rc rc;
+ __be32 number; /*FIX: should be __be64, but I'm getting the least
+ * significant word first
+ */
+} __packed __aligned(8);
+
+struct ibmvnic_login {
+ u8 first;
+ u8 cmd;
+ u8 reserved[6];
+ __be32 ioba;
+ __be32 len;
+} __packed __aligned(8);
+
+struct ibmvnic_phys_parms {
+ u8 first;
+ u8 cmd;
+ u8 flags1;
+#define IBMVNIC_EXTERNAL_LOOPBACK 0x80
+#define IBMVNIC_INTERNAL_LOOPBACK 0x40
+#define IBMVNIC_PROMISC 0x20
+#define IBMVNIC_PHYS_LINK_ACTIVE 0x10
+#define IBMVNIC_AUTONEG_DUPLEX 0x08
+#define IBMVNIC_FULL_DUPLEX 0x04
+#define IBMVNIC_HALF_DUPLEX 0x02
+#define IBMVNIC_CAN_CHG_PHYS_PARMS 0x01
+ u8 flags2;
+#define IBMVNIC_LOGICAL_LNK_ACTIVE 0x80
+ __be32 speed;
+#define IBMVNIC_AUTONEG 0x80
+#define IBMVNIC_10MBPS 0x40
+#define IBMVNIC_100MBPS 0x20
+#define IBMVNIC_1GBPS 0x10
+#define IBMVNIC_10GBPS 0x08
+ __be32 mtu;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_logical_link_state {
+ u8 first;
+ u8 cmd;
+ u8 link_state;
+#define IBMVNIC_LOGICAL_LNK_DN 0x00
+#define IBMVNIC_LOGICAL_LNK_UP 0x01
+#define IBMVNIC_LOGICAL_LNK_QUERY 0xff
+ u8 reserved[9];
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_query_ip_offload {
+ u8 first;
+ u8 cmd;
+ u8 reserved[2];
+ __be32 len;
+ __be32 ioba;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_control_ip_offload {
+ u8 first;
+ u8 cmd;
+ u8 reserved[2];
+ __be32 ioba;
+ __be32 len;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_request_dump_size {
+ u8 first;
+ u8 cmd;
+ u8 reserved[6];
+ __be32 len;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_request_dump {
+ u8 first;
+ u8 cmd;
+ u8 reserved1[2];
+ __be32 ioba;
+ __be32 len;
+ u8 reserved2[4];
+} __packed __aligned(8);
+
+struct ibmvnic_request_dump_rsp {
+ u8 first;
+ u8 cmd;
+ u8 reserved[6];
+ __be32 dumped_len;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_request_ras_comp_num {
+ u8 first;
+ u8 cmd;
+ u8 reserved1[2];
+ __be32 num_components;
+ u8 reserved2[4];
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_request_ras_comps {
+ u8 first;
+ u8 cmd;
+ u8 reserved[2];
+ __be32 ioba;
+ __be32 len;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_control_ras {
+ u8 first;
+ u8 cmd;
+ u8 correlator;
+ u8 level;
+ u8 op;
+#define IBMVNIC_TRACE_LEVEL 1
+#define IBMVNIC_ERROR_LEVEL 2
+#define IBMVNIC_TRACE_PAUSE 3
+#define IBMVNIC_TRACE_RESUME 4
+#define IBMVNIC_TRACE_ON 5
+#define IBMVNIC_TRACE_OFF 6
+#define IBMVNIC_CHG_TRACE_BUFF_SZ 7
+ u8 trace_buff_sz[3];
+ u8 reserved[4];
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_collect_fw_trace {
+ u8 first;
+ u8 cmd;
+ u8 correlator;
+ u8 reserved;
+ __be32 ioba;
+ __be32 len;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_request_statistics {
+ u8 first;
+ u8 cmd;
+ u8 flags;
+#define IBMVNIC_PHYSICAL_PORT 0x80
+ u8 reserved1;
+ __be32 ioba;
+ __be32 len;
+ u8 reserved[4];
+} __packed __aligned(8);
+
+struct ibmvnic_request_debug_stats {
+ u8 first;
+ u8 cmd;
+ u8 reserved[2];
+ __be32 ioba;
+ __be32 len;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_error_indication {
+ u8 first;
+ u8 cmd;
+ u8 flags;
+#define IBMVNIC_FATAL_ERROR 0x80
+ u8 reserved1;
+ __be32 error_id;
+ __be32 detail_error_sz;
+ __be16 error_cause;
+ u8 reserved2[2];
+} __packed __aligned(8);
+
+struct ibmvnic_request_error_info {
+ u8 first;
+ u8 cmd;
+ u8 reserved[2];
+ __be32 ioba;
+ __be32 len;
+ __be32 error_id;
+} __packed __aligned(8);
+
+struct ibmvnic_request_error_rsp {
+ u8 first;
+ u8 cmd;
+ u8 reserved[2];
+ __be32 error_id;
+ __be32 len;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_link_state_indication {
+ u8 first;
+ u8 cmd;
+ u8 reserved1[2];
+ u8 phys_link_state;
+ u8 logical_link_state;
+ u8 reserved2[10];
+} __packed __aligned(8);
+
+struct ibmvnic_change_mac_addr {
+ u8 first;
+ u8 cmd;
+ u8 mac_addr[6];
+ struct ibmvnic_rc rc;
+ u8 reserved[4];
+} __packed __aligned(8);
+
+struct ibmvnic_multicast_ctrl {
+ u8 first;
+ u8 cmd;
+ u8 mac_addr[6];
+ u8 flags;
+#define IBMVNIC_ENABLE_MC 0x80
+#define IBMVNIC_DISABLE_MC 0x40
+#define IBMVNIC_ENABLE_ALL 0x20
+#define IBMVNIC_DISABLE_ALL 0x10
+ u8 reserved1;
+ __be16 reserved2; /* was num_enabled_mc_addr; */
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_get_vpd_size_rsp {
+ u8 first;
+ u8 cmd;
+ u8 reserved[2];
+ __be64 len;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_get_vpd {
+ u8 first;
+ u8 cmd;
+ u8 reserved1[2];
+ __be32 ioba;
+ __be32 len;
+ u8 reserved[4];
+} __packed __aligned(8);
+
+struct ibmvnic_acl_change_indication {
+ u8 first;
+ u8 cmd;
+ __be16 change_type;
+#define IBMVNIC_MAC_ACL 0
+#define IBMVNIC_VLAN_ACL 1
+ u8 reserved[12];
+} __packed __aligned(8);
+
+struct ibmvnic_acl_query {
+ u8 first;
+ u8 cmd;
+ u8 reserved1[2];
+ __be32 ioba;
+ __be32 len;
+ u8 reserved2[4];
+} __packed __aligned(8);
+
+struct ibmvnic_tune {
+ u8 first;
+ u8 cmd;
+ u8 reserved1[2];
+ __be32 ioba;
+ __be32 len;
+ u8 reserved2[4];
+} __packed __aligned(8);
+
+struct ibmvnic_request_map {
+ u8 first;
+ u8 cmd;
+ u8 reserved1;
+ u8 map_id;
+ __be32 ioba;
+ __be32 len;
+ u8 reserved2[4];
+} __packed __aligned(8);
+
+struct ibmvnic_request_map_rsp {
+ u8 first;
+ u8 cmd;
+ u8 reserved1;
+ u8 map_id;
+ u8 reserved2[4];
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_request_unmap {
+ u8 first;
+ u8 cmd;
+ u8 reserved1;
+ u8 map_id;
+ u8 reserved2[12];
+} __packed __aligned(8);
+
+struct ibmvnic_request_unmap_rsp {
+ u8 first;
+ u8 cmd;
+ u8 reserved1;
+ u8 map_id;
+ u8 reserved2[8];
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+struct ibmvnic_query_map {
+ u8 first;
+ u8 cmd;
+ u8 reserved[14];
+} __packed __aligned(8);
+
+struct ibmvnic_query_map_rsp {
+ u8 first;
+ u8 cmd;
+ u8 reserved;
+ u8 page_size;
+ __be32 tot_pages;
+ __be32 free_pages;
+ struct ibmvnic_rc rc;
+} __packed __aligned(8);
+
+union ibmvnic_crq {
+ struct ibmvnic_generic_crq generic;
+ struct ibmvnic_version_exchange version_exchange;
+ struct ibmvnic_version_exchange version_exchange_rsp;
+ struct ibmvnic_capability query_capability;
+ struct ibmvnic_capability query_capability_rsp;
+ struct ibmvnic_capability request_capability;
+ struct ibmvnic_capability request_capability_rsp;
+ struct ibmvnic_login login;
+ struct ibmvnic_generic_crq login_rsp;
+ struct ibmvnic_phys_parms query_phys_parms;
+ struct ibmvnic_phys_parms query_phys_parms_rsp;
+ struct ibmvnic_phys_parms query_phys_capabilities;
+ struct ibmvnic_phys_parms query_phys_capabilities_rsp;
+ struct ibmvnic_phys_parms set_phys_parms;
+ struct ibmvnic_phys_parms set_phys_parms_rsp;
+ struct ibmvnic_logical_link_state logical_link_state;
+ struct ibmvnic_logical_link_state logical_link_state_rsp;
+ struct ibmvnic_query_ip_offload query_ip_offload;
+ struct ibmvnic_query_ip_offload query_ip_offload_rsp;
+ struct ibmvnic_control_ip_offload control_ip_offload;
+ struct ibmvnic_control_ip_offload control_ip_offload_rsp;
+ struct ibmvnic_request_dump_size request_dump_size;
+ struct ibmvnic_request_dump_size request_dump_size_rsp;
+ struct ibmvnic_request_dump request_dump;
+ struct ibmvnic_request_dump_rsp request_dump_rsp;
+ struct ibmvnic_request_ras_comp_num request_ras_comp_num;
+ struct ibmvnic_request_ras_comp_num request_ras_comp_num_rsp;
+ struct ibmvnic_request_ras_comps request_ras_comps;
+ struct ibmvnic_request_ras_comps request_ras_comps_rsp;
+ struct ibmvnic_control_ras control_ras;
+ struct ibmvnic_control_ras control_ras_rsp;
+ struct ibmvnic_collect_fw_trace collect_fw_trace;
+ struct ibmvnic_collect_fw_trace collect_fw_trace_rsp;
+ struct ibmvnic_request_statistics request_statistics;
+ struct ibmvnic_generic_crq request_statistics_rsp;
+ struct ibmvnic_request_debug_stats request_debug_stats;
+ struct ibmvnic_request_debug_stats request_debug_stats_rsp;
+ struct ibmvnic_error_indication error_indication;
+ struct ibmvnic_request_error_info request_error_info;
+ struct ibmvnic_request_error_rsp request_error_rsp;
+ struct ibmvnic_link_state_indication link_state_indication;
+ struct ibmvnic_change_mac_addr change_mac_addr;
+ struct ibmvnic_change_mac_addr change_mac_addr_rsp;
+ struct ibmvnic_multicast_ctrl multicast_ctrl;
+ struct ibmvnic_multicast_ctrl multicast_ctrl_rsp;
+ struct ibmvnic_generic_crq get_vpd_size;
+ struct ibmvnic_get_vpd_size_rsp get_vpd_size_rsp;
+ struct ibmvnic_get_vpd get_vpd;
+ struct ibmvnic_generic_crq get_vpd_rsp;
+ struct ibmvnic_acl_change_indication acl_change_indication;
+ struct ibmvnic_acl_query acl_query;
+ struct ibmvnic_generic_crq acl_query_rsp;
+ struct ibmvnic_tune tune;
+ struct ibmvnic_generic_crq tune_rsp;
+ struct ibmvnic_request_map request_map;
+ struct ibmvnic_request_map_rsp request_map_rsp;
+ struct ibmvnic_request_unmap request_unmap;
+ struct ibmvnic_request_unmap_rsp request_unmap_rsp;
+ struct ibmvnic_query_map query_map;
+ struct ibmvnic_query_map_rsp query_map_rsp;
+};
+
+enum ibmvnic_rc_codes {
+ SUCCESS = 0,
+ PARTIALSUCCESS = 1,
+ PERMISSION = 2,
+ NOMEMORY = 3,
+ PARAMETER = 4,
+ UNKNOWNCOMMAND = 5,
+ ABORTED = 6,
+ INVALIDSTATE = 7,
+ INVALIDIOBA = 8,
+ INVALIDLENGTH = 9,
+ UNSUPPORTEDOPTION = 10,
+};
+
+enum ibmvnic_capabilities {
+ MIN_TX_QUEUES = 1,
+ MIN_RX_QUEUES = 2,
+ MIN_RX_ADD_QUEUES = 3,
+ MAX_TX_QUEUES = 4,
+ MAX_RX_QUEUES = 5,
+ MAX_RX_ADD_QUEUES = 6,
+ REQ_TX_QUEUES = 7,
+ REQ_RX_QUEUES = 8,
+ REQ_RX_ADD_QUEUES = 9,
+ MIN_TX_ENTRIES_PER_SUBCRQ = 10,
+ MIN_RX_ADD_ENTRIES_PER_SUBCRQ = 11,
+ MAX_TX_ENTRIES_PER_SUBCRQ = 12,
+ MAX_RX_ADD_ENTRIES_PER_SUBCRQ = 13,
+ REQ_TX_ENTRIES_PER_SUBCRQ = 14,
+ REQ_RX_ADD_ENTRIES_PER_SUBCRQ = 15,
+ TCP_IP_OFFLOAD = 16,
+ PROMISC_REQUESTED = 17,
+ PROMISC_SUPPORTED = 18,
+ MIN_MTU = 19,
+ MAX_MTU = 20,
+ REQ_MTU = 21,
+ MAX_MULTICAST_FILTERS = 22,
+ VLAN_HEADER_INSERTION = 23,
+ MAX_TX_SG_ENTRIES = 25,
+ RX_SG_SUPPORTED = 26,
+ RX_SG_REQUESTED = 27,
+ OPT_TX_COMP_SUB_QUEUES = 28,
+ OPT_RX_COMP_QUEUES = 29,
+ OPT_RX_BUFADD_Q_PER_RX_COMP_Q = 30,
+ OPT_TX_ENTRIES_PER_SUBCRQ = 31,
+ OPT_RXBA_ENTRIES_PER_SUBCRQ = 32,
+ TX_RX_DESC_REQ = 33,
+};
+
+enum ibmvnic_error_cause {
+ ADAPTER_PROBLEM = 0,
+ BUS_PROBLEM = 1,
+ FW_PROBLEM = 2,
+ DD_PROBLEM = 3,
+ EEH_RECOVERY = 4,
+ FW_UPDATED = 5,
+ LOW_MEMORY = 6,
+};
+
+enum ibmvnic_commands {
+ VERSION_EXCHANGE = 0x01,
+ VERSION_EXCHANGE_RSP = 0x81,
+ QUERY_CAPABILITY = 0x02,
+ QUERY_CAPABILITY_RSP = 0x82,
+ REQUEST_CAPABILITY = 0x03,
+ REQUEST_CAPABILITY_RSP = 0x83,
+ LOGIN = 0x04,
+ LOGIN_RSP = 0x84,
+ QUERY_PHYS_PARMS = 0x05,
+ QUERY_PHYS_PARMS_RSP = 0x85,
+ QUERY_PHYS_CAPABILITIES = 0x06,
+ QUERY_PHYS_CAPABILITIES_RSP = 0x86,
+ SET_PHYS_PARMS = 0x07,
+ SET_PHYS_PARMS_RSP = 0x87,
+ ERROR_INDICATION = 0x08,
+ REQUEST_ERROR_INFO = 0x09,
+ REQUEST_ERROR_RSP = 0x89,
+ REQUEST_DUMP_SIZE = 0x0A,
+ REQUEST_DUMP_SIZE_RSP = 0x8A,
+ REQUEST_DUMP = 0x0B,
+ REQUEST_DUMP_RSP = 0x8B,
+ LOGICAL_LINK_STATE = 0x0C,
+ LOGICAL_LINK_STATE_RSP = 0x8C,
+ REQUEST_STATISTICS = 0x0D,
+ REQUEST_STATISTICS_RSP = 0x8D,
+ REQUEST_RAS_COMP_NUM = 0x0E,
+ REQUEST_RAS_COMP_NUM_RSP = 0x8E,
+ REQUEST_RAS_COMPS = 0x0F,
+ REQUEST_RAS_COMPS_RSP = 0x8F,
+ CONTROL_RAS = 0x10,
+ CONTROL_RAS_RSP = 0x90,
+ COLLECT_FW_TRACE = 0x11,
+ COLLECT_FW_TRACE_RSP = 0x91,
+ LINK_STATE_INDICATION = 0x12,
+ CHANGE_MAC_ADDR = 0x13,
+ CHANGE_MAC_ADDR_RSP = 0x93,
+ MULTICAST_CTRL = 0x14,
+ MULTICAST_CTRL_RSP = 0x94,
+ GET_VPD_SIZE = 0x15,
+ GET_VPD_SIZE_RSP = 0x95,
+ GET_VPD = 0x16,
+ GET_VPD_RSP = 0x96,
+ TUNE = 0x17,
+ TUNE_RSP = 0x97,
+ QUERY_IP_OFFLOAD = 0x18,
+ QUERY_IP_OFFLOAD_RSP = 0x98,
+ CONTROL_IP_OFFLOAD = 0x19,
+ CONTROL_IP_OFFLOAD_RSP = 0x99,
+ ACL_CHANGE_INDICATION = 0x1A,
+ ACL_QUERY = 0x1B,
+ ACL_QUERY_RSP = 0x9B,
+ REQUEST_DEBUG_STATS = 0x1C,
+ REQUEST_DEBUG_STATS_RSP = 0x9C,
+ QUERY_MAP = 0x1D,
+ QUERY_MAP_RSP = 0x9D,
+ REQUEST_MAP = 0x1E,
+ REQUEST_MAP_RSP = 0x9E,
+ REQUEST_UNMAP = 0x1F,
+ REQUEST_UNMAP_RSP = 0x9F,
+ VLAN_CTRL = 0x20,
+ VLAN_CTRL_RSP = 0xA0,
+};
+
+enum ibmvnic_crq_type {
+ IBMVNIC_CRQ_CMD = 0x80,
+ IBMVNIC_CRQ_CMD_RSP = 0x80,
+ IBMVNIC_CRQ_INIT_CMD = 0xC0,
+ IBMVNIC_CRQ_INIT_RSP = 0xC0,
+ IBMVNIC_CRQ_XPORT_EVENT = 0xFF,
+};
+
+enum ibmvfc_crq_format {
+ IBMVNIC_CRQ_INIT = 0x01,
+ IBMVNIC_CRQ_INIT_COMPLETE = 0x02,
+ IBMVNIC_PARTITION_MIGRATED = 0x06,
+};
+
+struct ibmvnic_crq_queue {
+ union ibmvnic_crq *msgs;
+ int size, cur;
+ dma_addr_t msg_token;
+ spinlock_t lock;
+};
+
+union sub_crq {
+ struct ibmvnic_generic_scrq generic;
+ struct ibmvnic_tx_comp_desc tx_comp;
+ struct ibmvnic_tx_desc v1;
+ struct ibmvnic_hdr_desc hdr;
+ struct ibmvnic_hdr_ext_desc hdr_ext;
+ struct ibmvnic_sge_desc sge;
+ struct ibmvnic_rx_comp_desc rx_comp;
+ struct ibmvnic_rx_buff_add_desc rx_add;
+};
+
+struct ibmvnic_sub_crq_queue {
+ union sub_crq *msgs;
+ int size, cur;
+ dma_addr_t msg_token;
+ unsigned long crq_num;
+ unsigned long hw_irq;
+ unsigned int irq;
+ unsigned int pool_index;
+ int scrq_num;
+ spinlock_t lock;
+ struct sk_buff *rx_skb_top;
+ struct ibmvnic_adapter *adapter;
+};
+
+struct ibmvnic_long_term_buff {
+ unsigned char *buff;
+ dma_addr_t addr;
+ u64 size;
+ u8 map_id;
+};
+
+struct ibmvnic_tx_buff {
+ struct sk_buff *skb;
+ dma_addr_t data_dma[IBMVNIC_MAX_FRAGS_PER_CRQ];
+ unsigned int data_len[IBMVNIC_MAX_FRAGS_PER_CRQ];
+ int index;
+ int pool_index;
+ bool last_frag;
+ bool used_bounce;
+};
+
+struct ibmvnic_tx_pool {
+ struct ibmvnic_tx_buff *tx_buff;
+ int *free_map;
+ int consumer_index;
+ int producer_index;
+ wait_queue_head_t ibmvnic_tx_comp_q;
+ struct task_struct *work_thread;
+ struct ibmvnic_long_term_buff long_term_buff;
+};
+
+struct ibmvnic_rx_buff {
+ struct sk_buff *skb;
+ dma_addr_t dma;
+ unsigned char *data;
+ int size;
+ int pool_index;
+};
+
+struct ibmvnic_rx_pool {
+ struct ibmvnic_rx_buff *rx_buff;
+ int size;
+ int index;
+ int buff_size;
+ atomic_t available;
+ int *free_map;
+ int next_free;
+ int next_alloc;
+ int active;
+ struct ibmvnic_long_term_buff long_term_buff;
+};
+
+struct ibmvnic_error_buff {
+ char *buff;
+ dma_addr_t dma;
+ int len;
+ struct list_head list;
+ __be32 error_id;
+};
+
+struct ibmvnic_fw_comp_internal {
+ struct ibmvnic_adapter *adapter;
+ int num;
+ struct debugfs_blob_wrapper desc_blob;
+ int paused;
+};
+
+struct ibmvnic_inflight_cmd {
+ union ibmvnic_crq crq;
+ struct list_head list;
+};
+
+struct ibmvnic_adapter {
+ struct vio_dev *vdev;
+ struct net_device *netdev;
+ struct ibmvnic_crq_queue crq;
+ u8 mac_addr[ETH_ALEN];
+ struct ibmvnic_query_ip_offload_buffer ip_offload_buf;
+ dma_addr_t ip_offload_tok;
+ struct ibmvnic_control_ip_offload_buffer ip_offload_ctrl;
+ dma_addr_t ip_offload_ctrl_tok;
+ bool migrated;
+ u32 msg_enable;
+ void *bounce_buffer;
+ int bounce_buffer_size;
+ dma_addr_t bounce_buffer_dma;
+
+ /* Statistics */
+ struct net_device_stats net_stats;
+ struct ibmvnic_statistics stats;
+ dma_addr_t stats_token;
+ struct completion stats_done;
+ spinlock_t stats_lock;
+ int replenish_no_mem;
+ int replenish_add_buff_success;
+ int replenish_add_buff_failure;
+ int replenish_task_cycles;
+ int tx_send_failed;
+ int tx_map_failed;
+
+ int phys_link_state;
+ int logical_link_state;
+
+ /* login data */
+ struct ibmvnic_login_buffer *login_buf;
+ dma_addr_t login_buf_token;
+ int login_buf_sz;
+
+ struct ibmvnic_login_rsp_buffer *login_rsp_buf;
+ dma_addr_t login_rsp_buf_token;
+ int login_rsp_buf_sz;
+
+ atomic_t running_cap_queries;
+
+ struct ibmvnic_sub_crq_queue **tx_scrq;
+ struct ibmvnic_sub_crq_queue **rx_scrq;
+ int requested_caps;
+
+ /* rx structs */
+ struct napi_struct *napi;
+ struct ibmvnic_rx_pool *rx_pool;
+ u64 promisc;
+
+ struct ibmvnic_tx_pool *tx_pool;
+ bool closing;
+ struct completion init_done;
+
+ struct list_head errors;
+ spinlock_t error_list_lock;
+
+ /* debugfs */
+ struct dentry *debugfs_dir;
+ struct dentry *debugfs_dump;
+ struct completion fw_done;
+ char *dump_data;
+ dma_addr_t dump_data_token;
+ int dump_data_size;
+ int ras_comp_num;
+ struct ibmvnic_fw_component *ras_comps;
+ struct ibmvnic_fw_comp_internal *ras_comp_int;
+ dma_addr_t ras_comps_tok;
+ struct dentry *ras_comps_ent;
+
+ /* in-flight commands that allocate and/or map memory*/
+ struct list_head inflight;
+ spinlock_t inflight_lock;
+
+ /* partner capabilities */
+ u64 min_tx_queues;
+ u64 min_rx_queues;
+ u64 min_rx_add_queues;
+ u64 max_tx_queues;
+ u64 max_rx_queues;
+ u64 max_rx_add_queues;
+ u64 req_tx_queues;
+ u64 req_rx_queues;
+ u64 req_rx_add_queues;
+ u64 min_tx_entries_per_subcrq;
+ u64 min_rx_add_entries_per_subcrq;
+ u64 max_tx_entries_per_subcrq;
+ u64 max_rx_add_entries_per_subcrq;
+ u64 req_tx_entries_per_subcrq;
+ u64 req_rx_add_entries_per_subcrq;
+ u64 tcp_ip_offload;
+ u64 promisc_requested;
+ u64 promisc_supported;
+ u64 min_mtu;
+ u64 max_mtu;
+ u64 req_mtu;
+ u64 max_multicast_filters;
+ u64 vlan_header_insertion;
+ u64 max_tx_sg_entries;
+ u64 rx_sg_supported;
+ u64 rx_sg_requested;
+ u64 opt_tx_comp_sub_queues;
+ u64 opt_rx_comp_queues;
+ u64 opt_rx_bufadd_q_per_rx_comp_q;
+ u64 opt_tx_entries_per_subcrq;
+ u64 opt_rxba_entries_per_subcrq;
+ __be64 tx_rx_desc_req;
+ u8 map_id;
+};
diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
index 4163b16489b3..fa593dd3efe1 100644
--- a/drivers/net/ethernet/intel/Kconfig
+++ b/drivers/net/ethernet/intel/Kconfig
@@ -280,6 +280,16 @@ config I40E_VXLAN
Say Y here if you want to use Virtual eXtensible Local Area Network
(VXLAN) in the driver.
+config I40E_GENEVE
+ bool "Generic Network Virtualization Encapsulation (GENEVE) Support"
+ depends on I40E && GENEVE && !(I40E=y && GENEVE=m)
+ default n
+ ---help---
+ This allows one to create GENEVE virtual interfaces that provide
+ Layer 2 Networks over Layer 3 Networks. GENEVE is often used
+ to tunnel virtual network infrastructure in virtualized environments.
+ Say Y here if you want to use GENEVE in the driver.
+
config I40E_DCB
bool "Data Center Bridging (DCB) Support"
default n
diff --git a/drivers/net/ethernet/intel/e1000/e1000.h b/drivers/net/ethernet/intel/e1000/e1000.h
index 69707108d23c..98fe5a2cd6e3 100644
--- a/drivers/net/ethernet/intel/e1000/e1000.h
+++ b/drivers/net/ethernet/intel/e1000/e1000.h
@@ -213,8 +213,11 @@ struct e1000_rx_ring {
};
#define E1000_DESC_UNUSED(R) \
- ((((R)->next_to_clean > (R)->next_to_use) \
- ? 0 : (R)->count) + (R)->next_to_clean - (R)->next_to_use - 1)
+({ \
+ unsigned int clean = smp_load_acquire(&(R)->next_to_clean); \
+ unsigned int use = READ_ONCE((R)->next_to_use); \
+ (clean > use ? 0 : (R)->count) + clean - use - 1; \
+})
#define E1000_RX_DESC_EXT(R, i) \
(&(((union e1000_rx_desc_extended *)((R).desc))[i]))
diff --git a/drivers/net/ethernet/intel/e1000/e1000_hw.c b/drivers/net/ethernet/intel/e1000/e1000_hw.c
index b1af0d613caa..8172cf08cc33 100644
--- a/drivers/net/ethernet/intel/e1000/e1000_hw.c
+++ b/drivers/net/ethernet/intel/e1000/e1000_hw.c
@@ -1,5 +1,5 @@
/*******************************************************************************
-
+*
Intel PRO/1000 Linux driver
Copyright(c) 1999 - 2006 Intel Corporation.
@@ -106,7 +106,7 @@ u16 e1000_igp_cable_length_table[IGP01E1000_AGC_LENGTH_TABLE_SIZE] = {
120, 120
};
-static DEFINE_SPINLOCK(e1000_eeprom_lock);
+static DEFINE_MUTEX(e1000_eeprom_lock);
static DEFINE_SPINLOCK(e1000_phy_lock);
/**
@@ -624,8 +624,8 @@ s32 e1000_init_hw(struct e1000_hw *hw)
/* Workaround for PCI-X problem when BIOS sets MMRBC
* incorrectly.
*/
- if (hw->bus_type == e1000_bus_type_pcix
- && e1000_pcix_get_mmrbc(hw) > 2048)
+ if (hw->bus_type == e1000_bus_type_pcix &&
+ e1000_pcix_get_mmrbc(hw) > 2048)
e1000_pcix_set_mmrbc(hw, 2048);
break;
}
@@ -683,10 +683,9 @@ static s32 e1000_adjust_serdes_amplitude(struct e1000_hw *hw)
}
ret_val = e1000_read_eeprom(hw, EEPROM_SERDES_AMPLITUDE, 1,
- &eeprom_data);
- if (ret_val) {
+ &eeprom_data);
+ if (ret_val)
return ret_val;
- }
if (eeprom_data != EEPROM_RESERVED_WORD) {
/* Adjust SERDES output amplitude only. */
@@ -1074,8 +1073,8 @@ static s32 e1000_copper_link_preconfig(struct e1000_hw *hw)
if (hw->mac_type <= e1000_82543 ||
hw->mac_type == e1000_82541 || hw->mac_type == e1000_82547 ||
- hw->mac_type == e1000_82541_rev_2
- || hw->mac_type == e1000_82547_rev_2)
+ hw->mac_type == e1000_82541_rev_2 ||
+ hw->mac_type == e1000_82547_rev_2)
hw->phy_reset_disable = false;
return E1000_SUCCESS;
@@ -1652,7 +1651,7 @@ s32 e1000_phy_setup_autoneg(struct e1000_hw *hw)
mii_1000t_ctrl_reg = 0;
} else {
ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL,
- mii_1000t_ctrl_reg);
+ mii_1000t_ctrl_reg);
if (ret_val)
return ret_val;
}
@@ -1881,10 +1880,11 @@ static s32 e1000_phy_force_speed_duplex(struct e1000_hw *hw)
if (ret_val)
return ret_val;
- if ((hw->mac_type == e1000_82544 || hw->mac_type == e1000_82543)
- && (!hw->autoneg)
- && (hw->forced_speed_duplex == e1000_10_full
- || hw->forced_speed_duplex == e1000_10_half)) {
+ if ((hw->mac_type == e1000_82544 ||
+ hw->mac_type == e1000_82543) &&
+ (!hw->autoneg) &&
+ (hw->forced_speed_duplex == e1000_10_full ||
+ hw->forced_speed_duplex == e1000_10_half)) {
ret_val = e1000_polarity_reversal_workaround(hw);
if (ret_val)
return ret_val;
@@ -2084,11 +2084,12 @@ static s32 e1000_config_fc_after_link_up(struct e1000_hw *hw)
* so we had to force link. In this case, we need to force the
* configuration of the MAC to match the "fc" parameter.
*/
- if (((hw->media_type == e1000_media_type_fiber) && (hw->autoneg_failed))
- || ((hw->media_type == e1000_media_type_internal_serdes)
- && (hw->autoneg_failed))
- || ((hw->media_type == e1000_media_type_copper)
- && (!hw->autoneg))) {
+ if (((hw->media_type == e1000_media_type_fiber) &&
+ (hw->autoneg_failed)) ||
+ ((hw->media_type == e1000_media_type_internal_serdes) &&
+ (hw->autoneg_failed)) ||
+ ((hw->media_type == e1000_media_type_copper) &&
+ (!hw->autoneg))) {
ret_val = e1000_force_mac_fc(hw);
if (ret_val) {
e_dbg("Error forcing flow control settings\n");
@@ -2193,8 +2194,7 @@ static s32 e1000_config_fc_after_link_up(struct e1000_hw *hw)
else if (!(mii_nway_adv_reg & NWAY_AR_PAUSE) &&
(mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
- (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR))
- {
+ (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
hw->fc = E1000_FC_TX_PAUSE;
e_dbg
("Flow Control = TX PAUSE frames only.\n");
@@ -2210,8 +2210,7 @@ static s32 e1000_config_fc_after_link_up(struct e1000_hw *hw)
else if ((mii_nway_adv_reg & NWAY_AR_PAUSE) &&
(mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
!(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
- (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR))
- {
+ (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
hw->fc = E1000_FC_RX_PAUSE;
e_dbg
("Flow Control = RX PAUSE frames only.\n");
@@ -2460,10 +2459,11 @@ s32 e1000_check_for_link(struct e1000_hw *hw)
* happen due to the execution of this workaround.
*/
- if ((hw->mac_type == e1000_82544
- || hw->mac_type == e1000_82543) && (!hw->autoneg)
- && (hw->forced_speed_duplex == e1000_10_full
- || hw->forced_speed_duplex == e1000_10_half)) {
+ if ((hw->mac_type == e1000_82544 ||
+ hw->mac_type == e1000_82543) &&
+ (!hw->autoneg) &&
+ (hw->forced_speed_duplex == e1000_10_full ||
+ hw->forced_speed_duplex == e1000_10_half)) {
ew32(IMC, 0xffffffff);
ret_val =
e1000_polarity_reversal_workaround(hw);
@@ -2528,8 +2528,10 @@ s32 e1000_check_for_link(struct e1000_hw *hw)
*/
if (hw->tbi_compatibility_en) {
u16 speed, duplex;
+
ret_val =
e1000_get_speed_and_duplex(hw, &speed, &duplex);
+
if (ret_val) {
e_dbg
("Error getting link speed and duplex\n");
@@ -2628,10 +2630,10 @@ s32 e1000_get_speed_and_duplex(struct e1000_hw *hw, u16 *speed, u16 *duplex)
e1000_read_phy_reg(hw, PHY_LP_ABILITY, &phy_data);
if (ret_val)
return ret_val;
- if ((*speed == SPEED_100
- && !(phy_data & NWAY_LPAR_100TX_FD_CAPS))
- || (*speed == SPEED_10
- && !(phy_data & NWAY_LPAR_10T_FD_CAPS)))
+ if ((*speed == SPEED_100 &&
+ !(phy_data & NWAY_LPAR_100TX_FD_CAPS)) ||
+ (*speed == SPEED_10 &&
+ !(phy_data & NWAY_LPAR_10T_FD_CAPS)))
*duplex = HALF_DUPLEX;
}
}
@@ -2664,9 +2666,9 @@ static s32 e1000_wait_autoneg(struct e1000_hw *hw)
ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data);
if (ret_val)
return ret_val;
- if (phy_data & MII_SR_AUTONEG_COMPLETE) {
+ if (phy_data & MII_SR_AUTONEG_COMPLETE)
return E1000_SUCCESS;
- }
+
msleep(100);
}
return E1000_SUCCESS;
@@ -2803,11 +2805,11 @@ static u16 e1000_shift_in_mdi_bits(struct e1000_hw *hw)
return data;
}
-
/**
* e1000_read_phy_reg - read a phy register
* @hw: Struct containing variables accessed by shared code
* @reg_addr: address of the PHY register to read
+ * @phy_data: pointer to the value on the PHY register
*
* Reads the value from a PHY register, if the value is on a specific non zero
* page, sets the page first.
@@ -2823,14 +2825,13 @@ s32 e1000_read_phy_reg(struct e1000_hw *hw, u32 reg_addr, u16 *phy_data)
(reg_addr > MAX_PHY_MULTI_PAGE_REG)) {
ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT,
(u16) reg_addr);
- if (ret_val) {
- spin_unlock_irqrestore(&e1000_phy_lock, flags);
- return ret_val;
- }
+ if (ret_val)
+ goto out;
}
ret_val = e1000_read_phy_reg_ex(hw, MAX_PHY_REG_ADDRESS & reg_addr,
phy_data);
+out:
spin_unlock_irqrestore(&e1000_phy_lock, flags);
return ret_val;
@@ -2881,7 +2882,7 @@ static s32 e1000_read_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr,
e_dbg("MDI Read Error\n");
return -E1000_ERR_PHY;
}
- *phy_data = (u16) mdic;
+ *phy_data = (u16)mdic;
} else {
mdic = ((reg_addr << E1000_MDIC_REG_SHIFT) |
(phy_addr << E1000_MDIC_PHY_SHIFT) |
@@ -2906,7 +2907,7 @@ static s32 e1000_read_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr,
e_dbg("MDI Error\n");
return -E1000_ERR_PHY;
}
- *phy_data = (u16) mdic;
+ *phy_data = (u16)mdic;
}
} else {
/* We must first send a preamble through the MDIO pin to signal
@@ -2960,7 +2961,7 @@ s32 e1000_write_phy_reg(struct e1000_hw *hw, u32 reg_addr, u16 phy_data)
if ((hw->phy_type == e1000_phy_igp) &&
(reg_addr > MAX_PHY_MULTI_PAGE_REG)) {
ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT,
- (u16) reg_addr);
+ (u16)reg_addr);
if (ret_val) {
spin_unlock_irqrestore(&e1000_phy_lock, flags);
return ret_val;
@@ -2993,7 +2994,7 @@ static s32 e1000_write_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr,
* the desired data.
*/
if (hw->mac_type == e1000_ce4100) {
- mdic = (((u32) phy_data) |
+ mdic = (((u32)phy_data) |
(reg_addr << E1000_MDIC_REG_SHIFT) |
(phy_addr << E1000_MDIC_PHY_SHIFT) |
(INTEL_CE_GBE_MDIC_OP_WRITE) |
@@ -3015,7 +3016,7 @@ static s32 e1000_write_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr,
return -E1000_ERR_PHY;
}
} else {
- mdic = (((u32) phy_data) |
+ mdic = (((u32)phy_data) |
(reg_addr << E1000_MDIC_REG_SHIFT) |
(phy_addr << E1000_MDIC_PHY_SHIFT) |
(E1000_MDIC_OP_WRITE));
@@ -3053,7 +3054,7 @@ static s32 e1000_write_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr,
mdic = ((PHY_TURNAROUND) | (reg_addr << 2) | (phy_addr << 7) |
(PHY_OP_WRITE << 12) | (PHY_SOF << 14));
mdic <<= 16;
- mdic |= (u32) phy_data;
+ mdic |= (u32)phy_data;
e1000_shift_out_mdi_bits(hw, mdic, 32);
}
@@ -3176,14 +3177,14 @@ static s32 e1000_detect_gig_phy(struct e1000_hw *hw)
if (ret_val)
return ret_val;
- hw->phy_id = (u32) (phy_id_high << 16);
+ hw->phy_id = (u32)(phy_id_high << 16);
udelay(20);
ret_val = e1000_read_phy_reg(hw, PHY_ID2, &phy_id_low);
if (ret_val)
return ret_val;
- hw->phy_id |= (u32) (phy_id_low & PHY_REVISION_MASK);
- hw->phy_revision = (u32) phy_id_low & ~PHY_REVISION_MASK;
+ hw->phy_id |= (u32)(phy_id_low & PHY_REVISION_MASK);
+ hw->phy_revision = (u32)phy_id_low & ~PHY_REVISION_MASK;
switch (hw->mac_type) {
case e1000_82543:
@@ -3401,7 +3402,6 @@ static s32 e1000_phy_m88_get_info(struct e1000_hw *hw,
phy_info->remote_rx = ((phy_data & SR_1000T_REMOTE_RX_STATUS) >>
SR_1000T_REMOTE_RX_STATUS_SHIFT) ?
e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok;
-
}
return E1000_SUCCESS;
@@ -3449,7 +3449,7 @@ s32 e1000_phy_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info)
if (hw->phy_type == e1000_phy_igp)
return e1000_phy_igp_get_info(hw, phy_info);
else if ((hw->phy_type == e1000_phy_8211) ||
- (hw->phy_type == e1000_phy_8201))
+ (hw->phy_type == e1000_phy_8201))
return E1000_SUCCESS;
else
return e1000_phy_m88_get_info(hw, phy_info);
@@ -3611,11 +3611,11 @@ static void e1000_shift_out_ee_bits(struct e1000_hw *hw, u16 data, u16 count)
*/
mask = 0x01 << (count - 1);
eecd = er32(EECD);
- if (eeprom->type == e1000_eeprom_microwire) {
+ if (eeprom->type == e1000_eeprom_microwire)
eecd &= ~E1000_EECD_DO;
- } else if (eeprom->type == e1000_eeprom_spi) {
+ else if (eeprom->type == e1000_eeprom_spi)
eecd |= E1000_EECD_DO;
- }
+
do {
/* A "1" is shifted out to the EEPROM by setting bit "DI" to a
* "1", and then raising and then lowering the clock (the SK bit
@@ -3851,7 +3851,7 @@ static s32 e1000_spi_eeprom_ready(struct e1000_hw *hw)
do {
e1000_shift_out_ee_bits(hw, EEPROM_RDSR_OPCODE_SPI,
hw->eeprom.opcode_bits);
- spi_stat_reg = (u8) e1000_shift_in_ee_bits(hw, 8);
+ spi_stat_reg = (u8)e1000_shift_in_ee_bits(hw, 8);
if (!(spi_stat_reg & EEPROM_STATUS_RDY_SPI))
break;
@@ -3882,9 +3882,10 @@ static s32 e1000_spi_eeprom_ready(struct e1000_hw *hw)
s32 e1000_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
{
s32 ret;
- spin_lock(&e1000_eeprom_lock);
+
+ mutex_lock(&e1000_eeprom_lock);
ret = e1000_do_read_eeprom(hw, offset, words, data);
- spin_unlock(&e1000_eeprom_lock);
+ mutex_unlock(&e1000_eeprom_lock);
return ret;
}
@@ -3896,15 +3897,16 @@ static s32 e1000_do_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words,
if (hw->mac_type == e1000_ce4100) {
GBE_CONFIG_FLASH_READ(GBE_CONFIG_BASE_VIRT, offset, words,
- data);
+ data);
return E1000_SUCCESS;
}
/* A check for invalid values: offset too large, too many words, and
* not enough words.
*/
- if ((offset >= eeprom->word_size)
- || (words > eeprom->word_size - offset) || (words == 0)) {
+ if ((offset >= eeprom->word_size) ||
+ (words > eeprom->word_size - offset) ||
+ (words == 0)) {
e_dbg("\"words\" parameter out of bounds. Words = %d,"
"size = %d\n", offset, eeprom->word_size);
return -E1000_ERR_EEPROM;
@@ -3940,7 +3942,7 @@ static s32 e1000_do_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words,
/* Send the READ command (opcode + addr) */
e1000_shift_out_ee_bits(hw, read_opcode, eeprom->opcode_bits);
- e1000_shift_out_ee_bits(hw, (u16) (offset * 2),
+ e1000_shift_out_ee_bits(hw, (u16)(offset * 2),
eeprom->address_bits);
/* Read the data. The address of the eeprom internally
@@ -3960,7 +3962,7 @@ static s32 e1000_do_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words,
e1000_shift_out_ee_bits(hw,
EEPROM_READ_OPCODE_MICROWIRE,
eeprom->opcode_bits);
- e1000_shift_out_ee_bits(hw, (u16) (offset + i),
+ e1000_shift_out_ee_bits(hw, (u16)(offset + i),
eeprom->address_bits);
/* Read the data. For microwire, each word requires the
@@ -3968,6 +3970,7 @@ static s32 e1000_do_read_eeprom(struct e1000_hw *hw, u16 offset, u16 words,
*/
data[i] = e1000_shift_in_ee_bits(hw, 16);
e1000_standby_eeprom(hw);
+ cond_resched();
}
}
@@ -4004,7 +4007,7 @@ s32 e1000_validate_eeprom_checksum(struct e1000_hw *hw)
return E1000_SUCCESS;
#endif
- if (checksum == (u16) EEPROM_SUM)
+ if (checksum == (u16)EEPROM_SUM)
return E1000_SUCCESS;
else {
e_dbg("EEPROM Checksum Invalid\n");
@@ -4031,7 +4034,7 @@ s32 e1000_update_eeprom_checksum(struct e1000_hw *hw)
}
checksum += eeprom_data;
}
- checksum = (u16) EEPROM_SUM - checksum;
+ checksum = (u16)EEPROM_SUM - checksum;
if (e1000_write_eeprom(hw, EEPROM_CHECKSUM_REG, 1, &checksum) < 0) {
e_dbg("EEPROM Write Error\n");
return -E1000_ERR_EEPROM;
@@ -4052,9 +4055,10 @@ s32 e1000_update_eeprom_checksum(struct e1000_hw *hw)
s32 e1000_write_eeprom(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
{
s32 ret;
- spin_lock(&e1000_eeprom_lock);
+
+ mutex_lock(&e1000_eeprom_lock);
ret = e1000_do_write_eeprom(hw, offset, words, data);
- spin_unlock(&e1000_eeprom_lock);
+ mutex_unlock(&e1000_eeprom_lock);
return ret;
}
@@ -4066,15 +4070,16 @@ static s32 e1000_do_write_eeprom(struct e1000_hw *hw, u16 offset, u16 words,
if (hw->mac_type == e1000_ce4100) {
GBE_CONFIG_FLASH_WRITE(GBE_CONFIG_BASE_VIRT, offset, words,
- data);
+ data);
return E1000_SUCCESS;
}
/* A check for invalid values: offset too large, too many words, and
* not enough words.
*/
- if ((offset >= eeprom->word_size)
- || (words > eeprom->word_size - offset) || (words == 0)) {
+ if ((offset >= eeprom->word_size) ||
+ (words > eeprom->word_size - offset) ||
+ (words == 0)) {
e_dbg("\"words\" parameter out of bounds\n");
return -E1000_ERR_EEPROM;
}
@@ -4116,6 +4121,7 @@ static s32 e1000_write_eeprom_spi(struct e1000_hw *hw, u16 offset, u16 words,
return -E1000_ERR_EEPROM;
e1000_standby_eeprom(hw);
+ cond_resched();
/* Send the WRITE ENABLE command (8 bit opcode ) */
e1000_shift_out_ee_bits(hw, EEPROM_WREN_OPCODE_SPI,
@@ -4132,7 +4138,7 @@ static s32 e1000_write_eeprom_spi(struct e1000_hw *hw, u16 offset, u16 words,
/* Send the Write command (8-bit opcode + addr) */
e1000_shift_out_ee_bits(hw, write_opcode, eeprom->opcode_bits);
- e1000_shift_out_ee_bits(hw, (u16) ((offset + widx) * 2),
+ e1000_shift_out_ee_bits(hw, (u16)((offset + widx) * 2),
eeprom->address_bits);
/* Send the data */
@@ -4142,6 +4148,7 @@ static s32 e1000_write_eeprom_spi(struct e1000_hw *hw, u16 offset, u16 words,
*/
while (widx < words) {
u16 word_out = data[widx];
+
word_out = (word_out >> 8) | (word_out << 8);
e1000_shift_out_ee_bits(hw, word_out, 16);
widx++;
@@ -4183,9 +4190,9 @@ static s32 e1000_write_eeprom_microwire(struct e1000_hw *hw, u16 offset,
* EEPROM into write/erase mode.
*/
e1000_shift_out_ee_bits(hw, EEPROM_EWEN_OPCODE_MICROWIRE,
- (u16) (eeprom->opcode_bits + 2));
+ (u16)(eeprom->opcode_bits + 2));
- e1000_shift_out_ee_bits(hw, 0, (u16) (eeprom->address_bits - 2));
+ e1000_shift_out_ee_bits(hw, 0, (u16)(eeprom->address_bits - 2));
/* Prepare the EEPROM */
e1000_standby_eeprom(hw);
@@ -4195,7 +4202,7 @@ static s32 e1000_write_eeprom_microwire(struct e1000_hw *hw, u16 offset,
e1000_shift_out_ee_bits(hw, EEPROM_WRITE_OPCODE_MICROWIRE,
eeprom->opcode_bits);
- e1000_shift_out_ee_bits(hw, (u16) (offset + words_written),
+ e1000_shift_out_ee_bits(hw, (u16)(offset + words_written),
eeprom->address_bits);
/* Send the data */
@@ -4224,6 +4231,7 @@ static s32 e1000_write_eeprom_microwire(struct e1000_hw *hw, u16 offset,
/* Recover from write */
e1000_standby_eeprom(hw);
+ cond_resched();
words_written++;
}
@@ -4235,9 +4243,9 @@ static s32 e1000_write_eeprom_microwire(struct e1000_hw *hw, u16 offset,
* EEPROM out of write/erase mode.
*/
e1000_shift_out_ee_bits(hw, EEPROM_EWDS_OPCODE_MICROWIRE,
- (u16) (eeprom->opcode_bits + 2));
+ (u16)(eeprom->opcode_bits + 2));
- e1000_shift_out_ee_bits(hw, 0, (u16) (eeprom->address_bits - 2));
+ e1000_shift_out_ee_bits(hw, 0, (u16)(eeprom->address_bits - 2));
return E1000_SUCCESS;
}
@@ -4260,8 +4268,8 @@ s32 e1000_read_mac_addr(struct e1000_hw *hw)
e_dbg("EEPROM Read Error\n");
return -E1000_ERR_EEPROM;
}
- hw->perm_mac_addr[i] = (u8) (eeprom_data & 0x00FF);
- hw->perm_mac_addr[i + 1] = (u8) (eeprom_data >> 8);
+ hw->perm_mac_addr[i] = (u8)(eeprom_data & 0x00FF);
+ hw->perm_mac_addr[i + 1] = (u8)(eeprom_data >> 8);
}
switch (hw->mac_type) {
@@ -4328,19 +4336,19 @@ u32 e1000_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr)
*/
case 0:
/* [47:36] i.e. 0x563 for above example address */
- hash_value = ((mc_addr[4] >> 4) | (((u16) mc_addr[5]) << 4));
+ hash_value = ((mc_addr[4] >> 4) | (((u16)mc_addr[5]) << 4));
break;
case 1:
/* [46:35] i.e. 0xAC6 for above example address */
- hash_value = ((mc_addr[4] >> 3) | (((u16) mc_addr[5]) << 5));
+ hash_value = ((mc_addr[4] >> 3) | (((u16)mc_addr[5]) << 5));
break;
case 2:
/* [45:34] i.e. 0x5D8 for above example address */
- hash_value = ((mc_addr[4] >> 2) | (((u16) mc_addr[5]) << 6));
+ hash_value = ((mc_addr[4] >> 2) | (((u16)mc_addr[5]) << 6));
break;
case 3:
/* [43:32] i.e. 0x634 for above example address */
- hash_value = ((mc_addr[4]) | (((u16) mc_addr[5]) << 8));
+ hash_value = ((mc_addr[4]) | (((u16)mc_addr[5]) << 8));
break;
}
@@ -4361,9 +4369,9 @@ void e1000_rar_set(struct e1000_hw *hw, u8 *addr, u32 index)
/* HW expects these in little endian so we reverse the byte order
* from network order (big endian) to little endian
*/
- rar_low = ((u32) addr[0] | ((u32) addr[1] << 8) |
- ((u32) addr[2] << 16) | ((u32) addr[3] << 24));
- rar_high = ((u32) addr[4] | ((u32) addr[5] << 8));
+ rar_low = ((u32)addr[0] | ((u32)addr[1] << 8) |
+ ((u32)addr[2] << 16) | ((u32)addr[3] << 24));
+ rar_high = ((u32)addr[4] | ((u32)addr[5] << 8));
/* Disable Rx and flush all Rx frames before enabling RSS to avoid Rx
* unit hang.
@@ -4537,7 +4545,7 @@ s32 e1000_setup_led(struct e1000_hw *hw)
if (ret_val)
return ret_val;
ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO,
- (u16) (hw->phy_spd_default &
+ (u16)(hw->phy_spd_default &
~IGP01E1000_GMII_SPD));
if (ret_val)
return ret_val;
@@ -4802,7 +4810,7 @@ void e1000_reset_adaptive(struct e1000_hw *hw)
void e1000_update_adaptive(struct e1000_hw *hw)
{
if (hw->adaptive_ifs) {
- if ((hw->collision_delta *hw->ifs_ratio) > hw->tx_packet_delta) {
+ if ((hw->collision_delta * hw->ifs_ratio) > hw->tx_packet_delta) {
if (hw->tx_packet_delta > MIN_NUM_XMITS) {
hw->in_ifs_mode = true;
if (hw->current_ifs_val < hw->ifs_max_val) {
@@ -4816,8 +4824,8 @@ void e1000_update_adaptive(struct e1000_hw *hw)
}
}
} else {
- if (hw->in_ifs_mode
- && (hw->tx_packet_delta <= MIN_NUM_XMITS)) {
+ if (hw->in_ifs_mode &&
+ (hw->tx_packet_delta <= MIN_NUM_XMITS)) {
hw->current_ifs_val = 0;
hw->in_ifs_mode = false;
ew32(AIT, 0);
@@ -4922,7 +4930,6 @@ static s32 e1000_get_cable_length(struct e1000_hw *hw, u16 *min_length,
/* Use old method for Phy older than IGP */
if (hw->phy_type == e1000_phy_m88) {
-
ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS,
&phy_data);
if (ret_val)
@@ -4966,7 +4973,6 @@ static s32 e1000_get_cable_length(struct e1000_hw *hw, u16 *min_length,
};
/* Read the AGC registers for all channels */
for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) {
-
ret_val =
e1000_read_phy_reg(hw, agc_reg_array[i], &phy_data);
if (ret_val)
@@ -4976,8 +4982,8 @@ static s32 e1000_get_cable_length(struct e1000_hw *hw, u16 *min_length,
/* Value bound check. */
if ((cur_agc_value >=
- IGP01E1000_AGC_LENGTH_TABLE_SIZE - 1)
- || (cur_agc_value == 0))
+ IGP01E1000_AGC_LENGTH_TABLE_SIZE - 1) ||
+ (cur_agc_value == 0))
return -E1000_ERR_PHY;
agc_value += cur_agc_value;
@@ -5054,7 +5060,6 @@ static s32 e1000_check_polarity(struct e1000_hw *hw,
*/
if ((phy_data & IGP01E1000_PSSR_SPEED_MASK) ==
IGP01E1000_PSSR_SPEED_1000MBPS) {
-
/* Read the GIG initialization PCS register (0x00B4) */
ret_val =
e1000_read_phy_reg(hw, IGP01E1000_PHY_PCS_INIT_REG,
@@ -5175,8 +5180,8 @@ static s32 e1000_1000Mb_check_cable_length(struct e1000_hw *hw)
hw->ffe_config_state = e1000_ffe_config_active;
ret_val = e1000_write_phy_reg(hw,
- IGP01E1000_PHY_DSP_FFE,
- IGP01E1000_PHY_DSP_FFE_CM_CP);
+ IGP01E1000_PHY_DSP_FFE,
+ IGP01E1000_PHY_DSP_FFE_CM_CP);
if (ret_val)
return ret_val;
break;
@@ -5243,7 +5248,7 @@ static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up)
msleep(20);
ret_val = e1000_write_phy_reg(hw, 0x0000,
- IGP01E1000_IEEE_FORCE_GIGA);
+ IGP01E1000_IEEE_FORCE_GIGA);
if (ret_val)
return ret_val;
for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) {
@@ -5264,7 +5269,7 @@ static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up)
}
ret_val = e1000_write_phy_reg(hw, 0x0000,
- IGP01E1000_IEEE_RESTART_AUTONEG);
+ IGP01E1000_IEEE_RESTART_AUTONEG);
if (ret_val)
return ret_val;
@@ -5299,7 +5304,7 @@ static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up)
msleep(20);
ret_val = e1000_write_phy_reg(hw, 0x0000,
- IGP01E1000_IEEE_FORCE_GIGA);
+ IGP01E1000_IEEE_FORCE_GIGA);
if (ret_val)
return ret_val;
ret_val =
@@ -5309,7 +5314,7 @@ static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up)
return ret_val;
ret_val = e1000_write_phy_reg(hw, 0x0000,
- IGP01E1000_IEEE_RESTART_AUTONEG);
+ IGP01E1000_IEEE_RESTART_AUTONEG);
if (ret_val)
return ret_val;
@@ -5346,9 +5351,8 @@ static s32 e1000_set_phy_mode(struct e1000_hw *hw)
ret_val =
e1000_read_eeprom(hw, EEPROM_PHY_CLASS_WORD, 1,
&eeprom_data);
- if (ret_val) {
+ if (ret_val)
return ret_val;
- }
if ((eeprom_data != EEPROM_RESERVED_WORD) &&
(eeprom_data & EEPROM_PHY_CLASS_A)) {
@@ -5395,8 +5399,8 @@ static s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active)
* from the lowest speeds starting from 10Mbps. The capability is used
* for Dx transitions and states
*/
- if (hw->mac_type == e1000_82541_rev_2
- || hw->mac_type == e1000_82547_rev_2) {
+ if (hw->mac_type == e1000_82541_rev_2 ||
+ hw->mac_type == e1000_82547_rev_2) {
ret_val =
e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO, &phy_data);
if (ret_val)
@@ -5446,11 +5450,9 @@ static s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active)
if (ret_val)
return ret_val;
}
- } else if ((hw->autoneg_advertised == AUTONEG_ADVERTISE_SPEED_DEFAULT)
- || (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_ALL)
- || (hw->autoneg_advertised ==
- AUTONEG_ADVERTISE_10_100_ALL)) {
-
+ } else if ((hw->autoneg_advertised == AUTONEG_ADVERTISE_SPEED_DEFAULT) ||
+ (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_ALL) ||
+ (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_100_ALL)) {
if (hw->mac_type == e1000_82541_rev_2 ||
hw->mac_type == e1000_82547_rev_2) {
phy_data |= IGP01E1000_GMII_FLEX_SPD;
@@ -5474,7 +5476,6 @@ static s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active)
phy_data);
if (ret_val)
return ret_val;
-
}
return E1000_SUCCESS;
}
@@ -5542,7 +5543,6 @@ static s32 e1000_set_vco_speed(struct e1000_hw *hw)
return E1000_SUCCESS;
}
-
/**
* e1000_enable_mng_pass_thru - check for bmc pass through
* @hw: Struct containing variables accessed by shared code
diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c
index fd7be860c201..3fc7bde699ba 100644
--- a/drivers/net/ethernet/intel/e1000/e1000_main.c
+++ b/drivers/net/ethernet/intel/e1000/e1000_main.c
@@ -99,13 +99,13 @@ int e1000_setup_all_rx_resources(struct e1000_adapter *adapter);
void e1000_free_all_tx_resources(struct e1000_adapter *adapter);
void e1000_free_all_rx_resources(struct e1000_adapter *adapter);
static int e1000_setup_tx_resources(struct e1000_adapter *adapter,
- struct e1000_tx_ring *txdr);
+ struct e1000_tx_ring *txdr);
static int e1000_setup_rx_resources(struct e1000_adapter *adapter,
- struct e1000_rx_ring *rxdr);
+ struct e1000_rx_ring *rxdr);
static void e1000_free_tx_resources(struct e1000_adapter *adapter,
- struct e1000_tx_ring *tx_ring);
+ struct e1000_tx_ring *tx_ring);
static void e1000_free_rx_resources(struct e1000_adapter *adapter,
- struct e1000_rx_ring *rx_ring);
+ struct e1000_rx_ring *rx_ring);
void e1000_update_stats(struct e1000_adapter *adapter);
static int e1000_init_module(void);
@@ -122,16 +122,16 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter);
static void e1000_clean_all_tx_rings(struct e1000_adapter *adapter);
static void e1000_clean_all_rx_rings(struct e1000_adapter *adapter);
static void e1000_clean_tx_ring(struct e1000_adapter *adapter,
- struct e1000_tx_ring *tx_ring);
+ struct e1000_tx_ring *tx_ring);
static void e1000_clean_rx_ring(struct e1000_adapter *adapter,
- struct e1000_rx_ring *rx_ring);
+ struct e1000_rx_ring *rx_ring);
static void e1000_set_rx_mode(struct net_device *netdev);
static void e1000_update_phy_info_task(struct work_struct *work);
static void e1000_watchdog(struct work_struct *work);
static void e1000_82547_tx_fifo_stall_task(struct work_struct *work);
static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
struct net_device *netdev);
-static struct net_device_stats * e1000_get_stats(struct net_device *netdev);
+static struct net_device_stats *e1000_get_stats(struct net_device *netdev);
static int e1000_change_mtu(struct net_device *netdev, int new_mtu);
static int e1000_set_mac(struct net_device *netdev, void *p);
static irqreturn_t e1000_intr(int irq, void *data);
@@ -164,7 +164,7 @@ static void e1000_tx_timeout(struct net_device *dev);
static void e1000_reset_task(struct work_struct *work);
static void e1000_smartspeed(struct e1000_adapter *adapter);
static int e1000_82547_fifo_workaround(struct e1000_adapter *adapter,
- struct sk_buff *skb);
+ struct sk_buff *skb);
static bool e1000_vlan_used(struct e1000_adapter *adapter);
static void e1000_vlan_mode(struct net_device *netdev,
@@ -195,7 +195,7 @@ MODULE_PARM_DESC(copybreak,
"Maximum size of packet that is copied to a new buffer on receive");
static pci_ers_result_t e1000_io_error_detected(struct pci_dev *pdev,
- pci_channel_state_t state);
+ pci_channel_state_t state);
static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev);
static void e1000_io_resume(struct pci_dev *pdev);
@@ -287,7 +287,7 @@ static int e1000_request_irq(struct e1000_adapter *adapter)
int err;
err = request_irq(adapter->pdev->irq, handler, irq_flags, netdev->name,
- netdev);
+ netdev);
if (err) {
e_err(probe, "Unable to allocate interrupt Error: %d\n", err);
}
@@ -636,8 +636,8 @@ void e1000_reset(struct e1000_adapter *adapter)
* but don't include ethernet FCS because hardware appends it
*/
min_tx_space = (hw->max_frame_size +
- sizeof(struct e1000_tx_desc) -
- ETH_FCS_LEN) * 2;
+ sizeof(struct e1000_tx_desc) -
+ ETH_FCS_LEN) * 2;
min_tx_space = ALIGN(min_tx_space, 1024);
min_tx_space >>= 10;
/* software strips receive CRC, so leave room for it */
@@ -943,8 +943,8 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
struct e1000_adapter *adapter;
struct e1000_hw *hw;
- static int cards_found = 0;
- static int global_quad_port_a = 0; /* global ksp3 port a indication */
+ static int cards_found;
+ static int global_quad_port_a; /* global ksp3 port a indication */
int i, err, pci_using_dac;
u16 eeprom_data = 0;
u16 tmp = 0;
@@ -1046,7 +1046,7 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (hw->mac_type == e1000_ce4100) {
hw->ce4100_gbe_mdio_base_virt =
ioremap(pci_resource_start(pdev, BAR_1),
- pci_resource_len(pdev, BAR_1));
+ pci_resource_len(pdev, BAR_1));
if (!hw->ce4100_gbe_mdio_base_virt)
goto err_mdio_ioremap;
@@ -1148,7 +1148,7 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
break;
case e1000_82546:
case e1000_82546_rev_3:
- if (er32(STATUS) & E1000_STATUS_FUNC_1){
+ if (er32(STATUS) & E1000_STATUS_FUNC_1) {
e1000_read_eeprom(hw,
EEPROM_INIT_CONTROL3_PORT_B, 1, &eeprom_data);
break;
@@ -1199,13 +1199,13 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
for (i = 0; i < 32; i++) {
hw->phy_addr = i;
e1000_read_phy_reg(hw, PHY_ID2, &tmp);
- if (tmp == 0 || tmp == 0xFF) {
- if (i == 31)
- goto err_eeprom;
- continue;
- } else
+
+ if (tmp != 0 && tmp != 0xFF)
break;
}
+
+ if (i >= 32)
+ goto err_eeprom;
}
/* reset the hardware with the new settings */
@@ -1263,7 +1263,7 @@ err_pci_reg:
* @pdev: PCI device information struct
*
* e1000_remove is called by the PCI subsystem to alert the driver
- * that it should release a PCI device. The could be caused by a
+ * that it should release a PCI device. That could be caused by a
* Hot-Plug event, or because the driver is going to be removed from
* memory.
**/
@@ -1334,12 +1334,12 @@ static int e1000_sw_init(struct e1000_adapter *adapter)
static int e1000_alloc_queues(struct e1000_adapter *adapter)
{
adapter->tx_ring = kcalloc(adapter->num_tx_queues,
- sizeof(struct e1000_tx_ring), GFP_KERNEL);
+ sizeof(struct e1000_tx_ring), GFP_KERNEL);
if (!adapter->tx_ring)
return -ENOMEM;
adapter->rx_ring = kcalloc(adapter->num_rx_queues,
- sizeof(struct e1000_rx_ring), GFP_KERNEL);
+ sizeof(struct e1000_rx_ring), GFP_KERNEL);
if (!adapter->rx_ring) {
kfree(adapter->tx_ring);
return -ENOMEM;
@@ -1811,20 +1811,20 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter)
rctl &= ~E1000_RCTL_SZ_4096;
rctl |= E1000_RCTL_BSEX;
switch (adapter->rx_buffer_len) {
- case E1000_RXBUFFER_2048:
- default:
- rctl |= E1000_RCTL_SZ_2048;
- rctl &= ~E1000_RCTL_BSEX;
- break;
- case E1000_RXBUFFER_4096:
- rctl |= E1000_RCTL_SZ_4096;
- break;
- case E1000_RXBUFFER_8192:
- rctl |= E1000_RCTL_SZ_8192;
- break;
- case E1000_RXBUFFER_16384:
- rctl |= E1000_RCTL_SZ_16384;
- break;
+ case E1000_RXBUFFER_2048:
+ default:
+ rctl |= E1000_RCTL_SZ_2048;
+ rctl &= ~E1000_RCTL_BSEX;
+ break;
+ case E1000_RXBUFFER_4096:
+ rctl |= E1000_RCTL_SZ_4096;
+ break;
+ case E1000_RXBUFFER_8192:
+ rctl |= E1000_RCTL_SZ_8192;
+ break;
+ case E1000_RXBUFFER_16384:
+ rctl |= E1000_RCTL_SZ_16384;
+ break;
}
/* This is useful for sniffing bad packets. */
@@ -1861,12 +1861,12 @@ static void e1000_configure_rx(struct e1000_adapter *adapter)
if (adapter->netdev->mtu > ETH_DATA_LEN) {
rdlen = adapter->rx_ring[0].count *
- sizeof(struct e1000_rx_desc);
+ sizeof(struct e1000_rx_desc);
adapter->clean_rx = e1000_clean_jumbo_rx_irq;
adapter->alloc_rx_buf = e1000_alloc_jumbo_rx_buffers;
} else {
rdlen = adapter->rx_ring[0].count *
- sizeof(struct e1000_rx_desc);
+ sizeof(struct e1000_rx_desc);
adapter->clean_rx = e1000_clean_rx_irq;
adapter->alloc_rx_buf = e1000_alloc_rx_buffers;
}
@@ -2761,7 +2761,9 @@ static int e1000_tso(struct e1000_adapter *adapter,
buffer_info->time_stamp = jiffies;
buffer_info->next_to_watch = i;
- if (++i == tx_ring->count) i = 0;
+ if (++i == tx_ring->count)
+ i = 0;
+
tx_ring->next_to_use = i;
return true;
@@ -2816,7 +2818,9 @@ static bool e1000_tx_csum(struct e1000_adapter *adapter,
buffer_info->time_stamp = jiffies;
buffer_info->next_to_watch = i;
- if (unlikely(++i == tx_ring->count)) i = 0;
+ if (unlikely(++i == tx_ring->count))
+ i = 0;
+
tx_ring->next_to_use = i;
return true;
@@ -2865,8 +2869,8 @@ static int e1000_tx_map(struct e1000_adapter *adapter,
* packet is smaller than 2048 - 16 - 16 (or 2016) bytes
*/
if (unlikely((hw->bus_type == e1000_bus_type_pcix) &&
- (size > 2015) && count == 0))
- size = 2015;
+ (size > 2015) && count == 0))
+ size = 2015;
/* Workaround for potential 82544 hang in PCI-X. Avoid
* terminating buffers within evenly-aligned dwords.
@@ -2963,7 +2967,7 @@ dma_error:
count--;
while (count--) {
- if (i==0)
+ if (i == 0)
i += tx_ring->count;
i--;
buffer_info = &tx_ring->buffer_info[i];
@@ -3013,7 +3017,8 @@ static void e1000_tx_queue(struct e1000_adapter *adapter,
tx_desc->lower.data =
cpu_to_le32(txd_lower | buffer_info->length);
tx_desc->upper.data = cpu_to_le32(txd_upper);
- if (unlikely(++i == tx_ring->count)) i = 0;
+ if (unlikely(++i == tx_ring->count))
+ i = 0;
}
tx_desc->lower.data |= cpu_to_le32(adapter->txd_cmd);
@@ -3101,7 +3106,7 @@ static int e1000_maybe_stop_tx(struct net_device *netdev,
return __e1000_maybe_stop_tx(netdev, size);
}
-#define TXD_USE_COUNT(S, X) (((S) >> (X)) + 1 )
+#define TXD_USE_COUNT(S, X) (((S) >> (X)) + 1)
static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
struct net_device *netdev)
{
@@ -3841,7 +3846,7 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter,
struct e1000_tx_buffer *buffer_info;
unsigned int i, eop;
unsigned int count = 0;
- unsigned int total_tx_bytes=0, total_tx_packets=0;
+ unsigned int total_tx_bytes = 0, total_tx_packets = 0;
unsigned int bytes_compl = 0, pkts_compl = 0;
i = tx_ring->next_to_clean;
@@ -3869,14 +3874,18 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter,
e1000_unmap_and_free_tx_resource(adapter, buffer_info);
tx_desc->upper.data = 0;
- if (unlikely(++i == tx_ring->count)) i = 0;
+ if (unlikely(++i == tx_ring->count))
+ i = 0;
}
eop = tx_ring->buffer_info[i].next_to_watch;
eop_desc = E1000_TX_DESC(*tx_ring, eop);
}
- tx_ring->next_to_clean = i;
+ /* Synchronize with E1000_DESC_UNUSED called from e1000_xmit_frame,
+ * which will reuse the cleaned buffers.
+ */
+ smp_store_release(&tx_ring->next_to_clean, i);
netdev_completed_queue(netdev, pkts_compl, bytes_compl);
@@ -3954,9 +3963,11 @@ static void e1000_rx_checksum(struct e1000_adapter *adapter, u32 status_err,
skb_checksum_none_assert(skb);
/* 82543 or newer only */
- if (unlikely(hw->mac_type < e1000_82543)) return;
+ if (unlikely(hw->mac_type < e1000_82543))
+ return;
/* Ignore Checksum bit is set */
- if (unlikely(status & E1000_RXD_STAT_IXSM)) return;
+ if (unlikely(status & E1000_RXD_STAT_IXSM))
+ return;
/* TCP/UDP checksum error bit is set */
if (unlikely(errors & E1000_RXD_ERR_TCPE)) {
/* let the stack verify checksum errors */
@@ -4136,7 +4147,7 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_adapter *adapter,
unsigned int i;
int cleaned_count = 0;
bool cleaned = false;
- unsigned int total_rx_bytes=0, total_rx_packets=0;
+ unsigned int total_rx_bytes = 0, total_rx_packets = 0;
i = rx_ring->next_to_clean;
rx_desc = E1000_RX_DESC(*rx_ring, i);
@@ -4153,7 +4164,9 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_adapter *adapter,
status = rx_desc->status;
- if (++i == rx_ring->count) i = 0;
+ if (++i == rx_ring->count)
+ i = 0;
+
next_rxd = E1000_RX_DESC(*rx_ring, i);
prefetch(next_rxd);
@@ -4356,7 +4369,7 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
unsigned int i;
int cleaned_count = 0;
bool cleaned = false;
- unsigned int total_rx_bytes=0, total_rx_packets=0;
+ unsigned int total_rx_bytes = 0, total_rx_packets = 0;
i = rx_ring->next_to_clean;
rx_desc = E1000_RX_DESC(*rx_ring, i);
@@ -4395,7 +4408,9 @@ static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
buffer_info->rxbuf.data = NULL;
}
- if (++i == rx_ring->count) i = 0;
+ if (++i == rx_ring->count)
+ i = 0;
+
next_rxd = E1000_RX_DESC(*rx_ring, i);
prefetch(next_rxd);
@@ -4683,9 +4698,11 @@ static void e1000_smartspeed(struct e1000_adapter *adapter)
* we assume back-to-back
*/
e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_status);
- if (!(phy_status & SR_1000T_MS_CONFIG_FAULT)) return;
+ if (!(phy_status & SR_1000T_MS_CONFIG_FAULT))
+ return;
e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_status);
- if (!(phy_status & SR_1000T_MS_CONFIG_FAULT)) return;
+ if (!(phy_status & SR_1000T_MS_CONFIG_FAULT))
+ return;
e1000_read_phy_reg(hw, PHY_1000T_CTRL, &phy_ctrl);
if (phy_ctrl & CR_1000T_MS_ENABLE) {
phy_ctrl &= ~CR_1000T_MS_ENABLE;
diff --git a/drivers/net/ethernet/intel/e1000e/defines.h b/drivers/net/ethernet/intel/e1000e/defines.h
index 133d4074dbe4..f7c7804d79e5 100644
--- a/drivers/net/ethernet/intel/e1000e/defines.h
+++ b/drivers/net/ethernet/intel/e1000e/defines.h
@@ -441,12 +441,13 @@
#define E1000_IMS_RXQ1 E1000_ICR_RXQ1 /* Rx Queue 1 Interrupt */
#define E1000_IMS_TXQ0 E1000_ICR_TXQ0 /* Tx Queue 0 Interrupt */
#define E1000_IMS_TXQ1 E1000_ICR_TXQ1 /* Tx Queue 1 Interrupt */
-#define E1000_IMS_OTHER E1000_ICR_OTHER /* Other Interrupts */
+#define E1000_IMS_OTHER E1000_ICR_OTHER /* Other Interrupt */
/* Interrupt Cause Set */
#define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */
#define E1000_ICS_RXSEQ E1000_ICR_RXSEQ /* Rx sequence error */
#define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* Rx desc min. threshold */
+#define E1000_ICS_OTHER E1000_ICR_OTHER /* Other Interrupt */
/* Transmit Descriptor Control */
#define E1000_TXDCTL_PTHRESH 0x0000003F /* TXDCTL Prefetch Threshold */
diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h
index 0b748d1959d9..1dc293bad87b 100644
--- a/drivers/net/ethernet/intel/e1000e/e1000.h
+++ b/drivers/net/ethernet/intel/e1000e/e1000.h
@@ -480,7 +480,7 @@ extern const char e1000e_driver_version[];
void e1000e_check_options(struct e1000_adapter *adapter);
void e1000e_set_ethtool_ops(struct net_device *netdev);
-int e1000e_up(struct e1000_adapter *adapter);
+void e1000e_up(struct e1000_adapter *adapter);
void e1000e_down(struct e1000_adapter *adapter, bool reset);
void e1000e_reinit_locked(struct e1000_adapter *adapter);
void e1000e_reset(struct e1000_adapter *adapter);
diff --git a/drivers/net/ethernet/intel/e1000e/hw.h b/drivers/net/ethernet/intel/e1000e/hw.h
index c9da4654e9ca..b3949d5bef5c 100644
--- a/drivers/net/ethernet/intel/e1000e/hw.h
+++ b/drivers/net/ethernet/intel/e1000e/hw.h
@@ -91,6 +91,7 @@ struct e1000_hw;
#define E1000_DEV_ID_PCH_SPT_I219_V 0x1570 /* SPT PCH */
#define E1000_DEV_ID_PCH_SPT_I219_LM2 0x15B7 /* SPT-H PCH */
#define E1000_DEV_ID_PCH_SPT_I219_V2 0x15B8 /* SPT-H PCH */
+#define E1000_DEV_ID_PCH_LBG_I219_LM3 0x15B9 /* LBG PCH */
#define E1000_REVISION_4 4
diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c
index 91a5a0ae9cd7..a049e30639a1 100644
--- a/drivers/net/ethernet/intel/e1000e/ich8lan.c
+++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c
@@ -1984,7 +1984,7 @@ static s32 e1000_check_reset_block_ich8lan(struct e1000_hw *hw)
int i = 0;
while ((blocked = !(er32(FWSM) & E1000_ICH_FWSM_RSPCIPHY)) &&
- (i++ < 10))
+ (i++ < 30))
usleep_range(10000, 20000);
return blocked ? E1000_BLK_PHY_RESET : 0;
}
@@ -3093,24 +3093,45 @@ static s32 e1000_valid_nvm_bank_detect_ich8lan(struct e1000_hw *hw, u32 *bank)
struct e1000_nvm_info *nvm = &hw->nvm;
u32 bank1_offset = nvm->flash_bank_size * sizeof(u16);
u32 act_offset = E1000_ICH_NVM_SIG_WORD * 2 + 1;
+ u32 nvm_dword = 0;
u8 sig_byte = 0;
s32 ret_val;
switch (hw->mac.type) {
- /* In SPT, read from the CTRL_EXT reg instead of
- * accessing the sector valid bits from the nvm
- */
case e1000_pch_spt:
- *bank = er32(CTRL_EXT)
- & E1000_CTRL_EXT_NVMVS;
- if ((*bank == 0) || (*bank == 1)) {
- e_dbg("ERROR: No valid NVM bank present\n");
- return -E1000_ERR_NVM;
- } else {
- *bank = *bank - 2;
+ bank1_offset = nvm->flash_bank_size;
+ act_offset = E1000_ICH_NVM_SIG_WORD;
+
+ /* set bank to 0 in case flash read fails */
+ *bank = 0;
+
+ /* Check bank 0 */
+ ret_val = e1000_read_flash_dword_ich8lan(hw, act_offset,
+ &nvm_dword);
+ if (ret_val)
+ return ret_val;
+ sig_byte = (u8)((nvm_dword & 0xFF00) >> 8);
+ if ((sig_byte & E1000_ICH_NVM_VALID_SIG_MASK) ==
+ E1000_ICH_NVM_SIG_VALUE) {
+ *bank = 0;
return 0;
}
- break;
+
+ /* Check bank 1 */
+ ret_val = e1000_read_flash_dword_ich8lan(hw, act_offset +
+ bank1_offset,
+ &nvm_dword);
+ if (ret_val)
+ return ret_val;
+ sig_byte = (u8)((nvm_dword & 0xFF00) >> 8);
+ if ((sig_byte & E1000_ICH_NVM_VALID_SIG_MASK) ==
+ E1000_ICH_NVM_SIG_VALUE) {
+ *bank = 1;
+ return 0;
+ }
+
+ e_dbg("ERROR: No valid NVM bank present\n");
+ return -E1000_ERR_NVM;
case e1000_ich8lan:
case e1000_ich9lan:
eecd = er32(EECD);
diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
index 0a854a47d31a..c71ba1bfc1ec 100644
--- a/drivers/net/ethernet/intel/e1000e/netdev.c
+++ b/drivers/net/ethernet/intel/e1000e/netdev.c
@@ -1905,30 +1905,15 @@ static irqreturn_t e1000_msix_other(int __always_unused irq, void *data)
struct net_device *netdev = data;
struct e1000_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
- u32 icr = er32(ICR);
-
- if (!(icr & E1000_ICR_INT_ASSERTED)) {
- if (!test_bit(__E1000_DOWN, &adapter->state))
- ew32(IMS, E1000_IMS_OTHER);
- return IRQ_NONE;
- }
- if (icr & adapter->eiac_mask)
- ew32(ICS, (icr & adapter->eiac_mask));
+ hw->mac.get_link_status = true;
- if (icr & E1000_ICR_OTHER) {
- if (!(icr & E1000_ICR_LSC))
- goto no_link_interrupt;
- hw->mac.get_link_status = true;
- /* guard against interrupt when we're going down */
- if (!test_bit(__E1000_DOWN, &adapter->state))
- mod_timer(&adapter->watchdog_timer, jiffies + 1);
+ /* guard against interrupt when we're going down */
+ if (!test_bit(__E1000_DOWN, &adapter->state)) {
+ mod_timer(&adapter->watchdog_timer, jiffies + 1);
+ ew32(IMS, E1000_IMS_OTHER);
}
-no_link_interrupt:
- if (!test_bit(__E1000_DOWN, &adapter->state))
- ew32(IMS, E1000_IMS_LSC | E1000_IMS_OTHER);
-
return IRQ_HANDLED;
}
@@ -1946,6 +1931,9 @@ static irqreturn_t e1000_intr_msix_tx(int __always_unused irq, void *data)
/* Ring was not completely cleaned, so fire another interrupt */
ew32(ICS, tx_ring->ims_val);
+ if (!test_bit(__E1000_DOWN, &adapter->state))
+ ew32(IMS, adapter->tx_ring->ims_val);
+
return IRQ_HANDLED;
}
@@ -1959,8 +1947,10 @@ static irqreturn_t e1000_intr_msix_rx(int __always_unused irq, void *data)
* previous interrupt.
*/
if (rx_ring->set_itr) {
- writel(1000000000 / (rx_ring->itr_val * 256),
- rx_ring->itr_register);
+ u32 itr = rx_ring->itr_val ?
+ 1000000000 / (rx_ring->itr_val * 256) : 0;
+
+ writel(itr, rx_ring->itr_register);
rx_ring->set_itr = 0;
}
@@ -2025,6 +2015,7 @@ static void e1000_configure_msix(struct e1000_adapter *adapter)
hw->hw_addr + E1000_EITR_82574(vector));
else
writel(1, hw->hw_addr + E1000_EITR_82574(vector));
+ adapter->eiac_mask |= E1000_IMS_OTHER;
/* Cause Tx interrupts on every write back */
ivar |= (1 << 31);
@@ -2032,12 +2023,8 @@ static void e1000_configure_msix(struct e1000_adapter *adapter)
ew32(IVAR, ivar);
/* enable MSI-X PBA support */
- ctrl_ext = er32(CTRL_EXT);
- ctrl_ext |= E1000_CTRL_EXT_PBA_CLR;
-
- /* Auto-Mask Other interrupts upon ICR read */
- ew32(IAM, ~E1000_EIAC_MASK_82574 | E1000_IMS_OTHER);
- ctrl_ext |= E1000_CTRL_EXT_EIAME;
+ ctrl_ext = er32(CTRL_EXT) & ~E1000_CTRL_EXT_IAME;
+ ctrl_ext |= E1000_CTRL_EXT_PBA_CLR | E1000_CTRL_EXT_EIAME;
ew32(CTRL_EXT, ctrl_ext);
e1e_flush();
}
@@ -2253,7 +2240,7 @@ static void e1000_irq_enable(struct e1000_adapter *adapter)
if (adapter->msix_entries) {
ew32(EIAC_82574, adapter->eiac_mask & E1000_EIAC_MASK_82574);
- ew32(IMS, adapter->eiac_mask | E1000_IMS_OTHER | E1000_IMS_LSC);
+ ew32(IMS, adapter->eiac_mask | E1000_IMS_LSC);
} else if ((hw->mac.type == e1000_pch_lpt) ||
(hw->mac.type == e1000_pch_spt)) {
ew32(IMS, IMS_ENABLE_MASK | E1000_IMS_ECCER);
@@ -4144,10 +4131,24 @@ void e1000e_reset(struct e1000_adapter *adapter)
}
-int e1000e_up(struct e1000_adapter *adapter)
+/**
+ * e1000e_trigger_lsc - trigger an LSC interrupt
+ * @adapter:
+ *
+ * Fire a link status change interrupt to start the watchdog.
+ **/
+static void e1000e_trigger_lsc(struct e1000_adapter *adapter)
{
struct e1000_hw *hw = &adapter->hw;
+ if (adapter->msix_entries)
+ ew32(ICS, E1000_ICS_OTHER);
+ else
+ ew32(ICS, E1000_ICS_LSC);
+}
+
+void e1000e_up(struct e1000_adapter *adapter)
+{
/* hardware has been reset, we need to reload some things */
e1000_configure(adapter);
@@ -4159,13 +4160,7 @@ int e1000e_up(struct e1000_adapter *adapter)
netif_start_queue(adapter->netdev);
- /* fire a link change interrupt to start the watchdog */
- if (adapter->msix_entries)
- ew32(ICS, E1000_ICS_LSC | E1000_ICR_OTHER);
- else
- ew32(ICS, E1000_ICS_LSC);
-
- return 0;
+ e1000e_trigger_lsc(adapter);
}
static void e1000e_flush_descriptors(struct e1000_adapter *adapter)
@@ -4590,11 +4585,7 @@ static int e1000_open(struct net_device *netdev)
hw->mac.get_link_status = true;
pm_runtime_put(&pdev->dev);
- /* fire a link status change interrupt to start the watchdog */
- if (adapter->msix_entries)
- ew32(ICS, E1000_ICS_LSC | E1000_ICR_OTHER);
- else
- ew32(ICS, E1000_ICS_LSC);
+ e1000e_trigger_lsc(adapter);
return 0;
@@ -6631,7 +6622,7 @@ static int e1000e_pm_runtime_resume(struct device *dev)
return rc;
if (netdev->flags & IFF_UP)
- rc = e1000e_up(adapter);
+ e1000e_up(adapter);
return rc;
}
@@ -6822,13 +6813,8 @@ static void e1000_io_resume(struct pci_dev *pdev)
e1000_init_manageability_pt(adapter);
- if (netif_running(netdev)) {
- if (e1000e_up(adapter)) {
- dev_err(&pdev->dev,
- "can't bring device back up after reset\n");
- return;
- }
- }
+ if (netif_running(netdev))
+ e1000e_up(adapter);
netif_device_attach(netdev);
@@ -7465,6 +7451,7 @@ static const struct pci_device_id e1000_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_V), board_pch_spt },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_LM2), board_pch_spt },
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_V2), board_pch_spt },
+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_LBG_I219_LM3), board_pch_spt },
{ 0, 0, 0, 0, 0, 0, 0 } /* terminate list */
};
@@ -7504,14 +7491,11 @@ static struct pci_driver e1000_driver = {
**/
static int __init e1000_init_module(void)
{
- int ret;
-
pr_info("Intel(R) PRO/1000 Network Driver - %s\n",
e1000e_driver_version);
pr_info("Copyright(c) 1999 - 2015 Intel Corporation.\n");
- ret = pci_register_driver(&e1000_driver);
- return ret;
+ return pci_register_driver(&e1000_driver);
}
module_init(e1000_init_module);
diff --git a/drivers/net/ethernet/intel/fm10k/Makefile b/drivers/net/ethernet/intel/fm10k/Makefile
index 08859dd220a8..b006ff66d028 100644
--- a/drivers/net/ethernet/intel/fm10k/Makefile
+++ b/drivers/net/ethernet/intel/fm10k/Makefile
@@ -1,7 +1,7 @@
################################################################################
#
# Intel Ethernet Switch Host Interface Driver
-# Copyright(c) 2013 - 2014 Intel Corporation.
+# 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,
@@ -27,7 +27,17 @@
obj-$(CONFIG_FM10K) += fm10k.o
-fm10k-objs := fm10k_main.o fm10k_common.o fm10k_pci.o \
- fm10k_netdev.o fm10k_ethtool.o fm10k_pf.o fm10k_vf.o \
- fm10k_mbx.o fm10k_iov.o fm10k_tlv.o \
- fm10k_debugfs.o fm10k_ptp.o fm10k_dcbnl.o
+fm10k-y := fm10k_main.o \
+ fm10k_common.o \
+ fm10k_pci.o \
+ fm10k_ptp.o \
+ fm10k_netdev.o \
+ fm10k_ethtool.o \
+ fm10k_pf.o \
+ fm10k_vf.o \
+ fm10k_mbx.o \
+ fm10k_iov.o \
+ fm10k_tlv.o
+
+fm10k-$(CONFIG_DEBUG_FS) += fm10k_debugfs.o
+fm10k-$(CONFIG_DCB) += fm10k_dcbnl.o
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h
index 14440200499b..b34bb008b104 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k.h
@@ -23,6 +23,7 @@
#include <linux/types.h>
#include <linux/etherdevice.h>
+#include <linux/cpumask.h>
#include <linux/rtnetlink.h>
#include <linux/if_vlan.h>
#include <linux/pci.h>
@@ -33,7 +34,7 @@
#include "fm10k_pf.h"
#include "fm10k_vf.h"
-#define FM10K_MAX_JUMBO_FRAME_SIZE 15358 /* Maximum supported size 15K */
+#define FM10K_MAX_JUMBO_FRAME_SIZE 15342 /* Maximum supported size 15K */
#define MAX_QUEUES FM10K_MAX_QUEUES_PF
@@ -66,6 +67,7 @@ struct fm10k_l2_accel {
enum fm10k_ring_state_t {
__FM10K_TX_DETECT_HANG,
__FM10K_HANG_CHECK_ARMED,
+ __FM10K_TX_XPS_INIT_DONE,
};
#define check_for_tx_hang(ring) \
@@ -138,7 +140,7 @@ struct fm10k_ring {
* different for DCB and RSS modes
*/
u8 qos_pc; /* priority class of queue */
- u16 vid; /* default vlan ID of queue */
+ u16 vid; /* default VLAN ID of queue */
u16 count; /* amount of descriptors */
u16 next_to_alloc;
@@ -164,14 +166,20 @@ struct fm10k_ring_container {
unsigned int total_packets; /* total packets processed this int */
u16 work_limit; /* total work allowed per interrupt */
u16 itr; /* interrupt throttle rate value */
+ u8 itr_scale; /* ITR adjustment based on PCI speed */
u8 count; /* total number of rings in vector */
};
#define FM10K_ITR_MAX 0x0FFF /* maximum value for ITR */
#define FM10K_ITR_10K 100 /* 100us */
#define FM10K_ITR_20K 50 /* 50us */
+#define FM10K_ITR_40K 25 /* 25us */
#define FM10K_ITR_ADAPTIVE 0x8000 /* adaptive interrupt moderation flag */
+#define ITR_IS_ADAPTIVE(itr) (!!(itr & FM10K_ITR_ADAPTIVE))
+
+#define FM10K_TX_ITR_DEFAULT FM10K_ITR_40K
+#define FM10K_RX_ITR_DEFAULT FM10K_ITR_20K
#define FM10K_ITR_ENABLE (FM10K_ITR_AUTOMASK | FM10K_ITR_MASK_CLEAR)
static inline struct netdev_queue *txring_txq(const struct fm10k_ring *ring)
@@ -203,6 +211,7 @@ struct fm10k_q_vector {
struct fm10k_ring_container rx, tx;
struct napi_struct napi;
+ cpumask_t affinity_mask;
char name[IFNAMSIZ + 9];
#ifdef CONFIG_DEBUG_FS
@@ -413,7 +422,7 @@ static inline u16 fm10k_desc_unused(struct fm10k_ring *ring)
(&(((union fm10k_rx_desc *)((R)->desc))[i]))
#define FM10K_MAX_TXD_PWR 14
-#define FM10K_MAX_DATA_PER_TXD (1 << FM10K_MAX_TXD_PWR)
+#define FM10K_MAX_DATA_PER_TXD BIT(FM10K_MAX_TXD_PWR)
/* Tx Descriptors needed, worst case */
#define TXD_USE_COUNT(S) DIV_ROUND_UP((S), FM10K_MAX_DATA_PER_TXD)
@@ -434,7 +443,7 @@ union fm10k_ftag_info {
struct {
/* dglort and sglort combined into a single 32bit desc read */
__le32 glort;
- /* upper 16 bits of vlan are reserved 0 for swpri_type_user */
+ /* upper 16 bits of VLAN are reserved 0 for swpri_type_user */
__le32 vlan;
} d;
struct {
@@ -484,7 +493,7 @@ void fm10k_netpoll(struct net_device *netdev);
#endif
/* Netdev */
-struct net_device *fm10k_alloc_netdev(void);
+struct net_device *fm10k_alloc_netdev(const struct fm10k_info *info);
int fm10k_setup_rx_resources(struct fm10k_ring *);
int fm10k_setup_tx_resources(struct fm10k_ring *);
void fm10k_free_rx_resources(struct fm10k_ring *);
@@ -551,5 +560,9 @@ int fm10k_get_ts_config(struct net_device *netdev, struct ifreq *ifr);
int fm10k_set_ts_config(struct net_device *netdev, struct ifreq *ifr);
/* DCB */
+#ifdef CONFIG_DCB
void fm10k_dcbnl_set_ops(struct net_device *dev);
+#else
+static inline void fm10k_dcbnl_set_ops(struct net_device *dev) {}
+#endif
#endif /* _FM10K_H_ */
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c b/drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c
index 5c7a4d7662d8..2be4361839db 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c
@@ -20,7 +20,6 @@
#include "fm10k.h"
-#ifdef CONFIG_DCB
/**
* fm10k_dcbnl_ieee_getets - get the ETS configuration for the device
* @dev: netdev interface for the device
@@ -155,7 +154,6 @@ static const struct dcbnl_rtnl_ops fm10k_dcbnl_ops = {
.setdcbx = fm10k_dcbnl_setdcbx,
};
-#endif /* CONFIG_DCB */
/**
* fm10k_dcbnl_set_ops - Configures dcbnl ops pointer for netdev
* @dev: netdev interface for the device
@@ -164,11 +162,9 @@ static const struct dcbnl_rtnl_ops fm10k_dcbnl_ops = {
**/
void fm10k_dcbnl_set_ops(struct net_device *dev)
{
-#ifdef CONFIG_DCB
struct fm10k_intfc *interface = netdev_priv(dev);
struct fm10k_hw *hw = &interface->hw;
if (hw->mac.type == fm10k_mac_pf)
dev->dcbnl_ops = &fm10k_dcbnl_ops;
-#endif /* CONFIG_DCB */
}
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c b/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c
index 5304bc1fbecd..5d6137faf7d1 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c
@@ -18,8 +18,6 @@
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*/
-#ifdef CONFIG_DEBUG_FS
-
#include "fm10k.h"
#include <linux/debugfs.h>
@@ -258,5 +256,3 @@ void fm10k_dbg_exit(void)
debugfs_remove_recursive(dbg_root);
dbg_root = NULL;
}
-
-#endif /* CONFIG_DEBUG_FS */
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
index 2ce0eba5e040..2f6a05b57228 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
@@ -111,12 +111,14 @@ static const struct fm10k_stats fm10k_gstrings_pf_stats[] = {
static const struct fm10k_stats fm10k_gstrings_mbx_stats[] = {
FM10K_MBX_STAT("mbx_tx_busy", tx_busy),
- FM10K_MBX_STAT("mbx_tx_oversized", tx_dropped),
+ FM10K_MBX_STAT("mbx_tx_dropped", tx_dropped),
FM10K_MBX_STAT("mbx_tx_messages", tx_messages),
FM10K_MBX_STAT("mbx_tx_dwords", tx_dwords),
+ FM10K_MBX_STAT("mbx_tx_mbmem_pulled", tx_mbmem_pulled),
FM10K_MBX_STAT("mbx_rx_messages", rx_messages),
FM10K_MBX_STAT("mbx_rx_dwords", rx_dwords),
FM10K_MBX_STAT("mbx_rx_parse_err", rx_parse_err),
+ FM10K_MBX_STAT("mbx_rx_mbmem_pushed", rx_mbmem_pushed),
};
#define FM10K_GLOBAL_STATS_LEN ARRAY_SIZE(fm10k_gstrings_global_stats)
@@ -125,7 +127,7 @@ static const struct fm10k_stats fm10k_gstrings_mbx_stats[] = {
#define FM10K_MBX_STATS_LEN ARRAY_SIZE(fm10k_gstrings_mbx_stats)
#define FM10K_QUEUE_STATS_LEN(_n) \
- ( (_n) * 2 * (sizeof(struct fm10k_queue_stats) / sizeof(u64)))
+ ((_n) * 2 * (sizeof(struct fm10k_queue_stats) / sizeof(u64)))
#define FM10K_STATIC_STATS_LEN (FM10K_GLOBAL_STATS_LEN + \
FM10K_NETDEV_STATS_LEN + \
@@ -257,7 +259,8 @@ static int fm10k_get_sset_count(struct net_device *dev, int sset)
stats_len += FM10K_DEBUG_STATS_LEN;
if (iov_data)
- stats_len += FM10K_MBX_STATS_LEN * iov_data->num_vfs;
+ stats_len += FM10K_MBX_STATS_LEN *
+ iov_data->num_vfs;
}
return stats_len;
@@ -296,14 +299,16 @@ static void fm10k_get_ethtool_stats(struct net_device *netdev,
if (interface->flags & FM10K_FLAG_DEBUG_STATS) {
for (i = 0; i < FM10K_DEBUG_STATS_LEN; i++) {
- p = (char *)interface + fm10k_gstrings_debug_stats[i].stat_offset;
+ p = (char *)interface +
+ fm10k_gstrings_debug_stats[i].stat_offset;
*(data++) = (fm10k_gstrings_debug_stats[i].sizeof_stat ==
sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
}
}
for (i = 0; i < FM10K_MBX_STATS_LEN; i++) {
- p = (char *)&interface->hw.mbx + fm10k_gstrings_mbx_stats[i].stat_offset;
+ p = (char *)&interface->hw.mbx +
+ fm10k_gstrings_mbx_stats[i].stat_offset;
*(data++) = (fm10k_gstrings_mbx_stats[i].sizeof_stat ==
sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
}
@@ -320,6 +325,7 @@ static void fm10k_get_ethtool_stats(struct net_device *netdev,
if ((interface->flags & FM10K_FLAG_DEBUG_STATS) && iov_data) {
for (i = 0; i < iov_data->num_vfs; i++) {
struct fm10k_vf_info *vf_info;
+
vf_info = &iov_data->vf_info[i];
/* skip stats if we don't have a vf info */
@@ -329,7 +335,8 @@ static void fm10k_get_ethtool_stats(struct net_device *netdev,
}
for (j = 0; j < FM10K_MBX_STATS_LEN; j++) {
- p = (char *)&vf_info->mbx + fm10k_gstrings_mbx_stats[j].stat_offset;
+ p = (char *)&vf_info->mbx +
+ fm10k_gstrings_mbx_stats[j].stat_offset;
*(data++) = (fm10k_gstrings_mbx_stats[j].sizeof_stat ==
sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
}
@@ -699,12 +706,10 @@ static int fm10k_get_coalesce(struct net_device *dev,
{
struct fm10k_intfc *interface = netdev_priv(dev);
- ec->use_adaptive_tx_coalesce =
- !!(interface->tx_itr & FM10K_ITR_ADAPTIVE);
+ ec->use_adaptive_tx_coalesce = ITR_IS_ADAPTIVE(interface->tx_itr);
ec->tx_coalesce_usecs = interface->tx_itr & ~FM10K_ITR_ADAPTIVE;
- ec->use_adaptive_rx_coalesce =
- !!(interface->rx_itr & FM10K_ITR_ADAPTIVE);
+ ec->use_adaptive_rx_coalesce = ITR_IS_ADAPTIVE(interface->rx_itr);
ec->rx_coalesce_usecs = interface->rx_itr & ~FM10K_ITR_ADAPTIVE;
return 0;
@@ -729,10 +734,10 @@ static int fm10k_set_coalesce(struct net_device *dev,
/* set initial values for adaptive ITR */
if (ec->use_adaptive_tx_coalesce)
- tx_itr = FM10K_ITR_ADAPTIVE | FM10K_ITR_10K;
+ tx_itr = FM10K_ITR_ADAPTIVE | FM10K_TX_ITR_DEFAULT;
if (ec->use_adaptive_rx_coalesce)
- rx_itr = FM10K_ITR_ADAPTIVE | FM10K_ITR_20K;
+ rx_itr = FM10K_ITR_ADAPTIVE | FM10K_RX_ITR_DEFAULT;
/* update interface */
interface->tx_itr = tx_itr;
@@ -1020,7 +1025,6 @@ static int fm10k_set_priv_flags(struct net_device *netdev, u32 priv_flags)
return 0;
}
-
static u32 fm10k_get_reta_size(struct net_device __always_unused *netdev)
{
return FM10K_RETA_SIZE * FM10K_RETA_ENTRIES_PER_REG;
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c
index e76a44cf330c..b243c3cbe68f 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c
@@ -28,7 +28,7 @@
#include "fm10k.h"
-#define DRV_VERSION "0.15.2-k"
+#define DRV_VERSION "0.19.3-k"
const char fm10k_driver_version[] = DRV_VERSION;
char fm10k_driver_name[] = "fm10k";
static const char fm10k_driver_string[] =
@@ -42,7 +42,7 @@ MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
/* single workqueue for entire fm10k driver */
-struct workqueue_struct *fm10k_workqueue = NULL;
+struct workqueue_struct *fm10k_workqueue;
/**
* fm10k_init_module - Driver Registration Routine
@@ -56,8 +56,7 @@ static int __init fm10k_init_module(void)
pr_info("%s\n", fm10k_copyright);
/* create driver workqueue */
- if (!fm10k_workqueue)
- fm10k_workqueue = create_workqueue("fm10k");
+ fm10k_workqueue = create_workqueue("fm10k");
fm10k_dbg_init();
@@ -80,7 +79,6 @@ static void __exit fm10k_exit_module(void)
/* destroy driver workqueue */
flush_workqueue(fm10k_workqueue);
destroy_workqueue(fm10k_workqueue);
- fm10k_workqueue = NULL;
}
module_exit(fm10k_exit_module);
@@ -917,7 +915,7 @@ static u8 fm10k_tx_desc_flags(struct sk_buff *skb, u32 tx_flags)
/* set timestamping bits */
if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
likely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS))
- desc_flags |= FM10K_TXD_FLAG_TIME;
+ desc_flags |= FM10K_TXD_FLAG_TIME;
/* set checksum offload bits */
desc_flags |= FM10K_SET_FLAG(tx_flags, FM10K_TX_FLAGS_CSUM,
@@ -1094,11 +1092,11 @@ dma_error:
netdev_tx_t fm10k_xmit_frame_ring(struct sk_buff *skb,
struct fm10k_ring *tx_ring)
{
+ u16 count = TXD_USE_COUNT(skb_headlen(skb));
struct fm10k_tx_buffer *first;
- int tso;
- u32 tx_flags = 0;
unsigned short f;
- u16 count = TXD_USE_COUNT(skb_headlen(skb));
+ u32 tx_flags = 0;
+ int tso;
/* need: 1 descriptor per page * PAGE_SIZE/FM10K_MAX_DATA_PER_TXD,
* + 1 desc for skb_headlen/FM10K_MAX_DATA_PER_TXD,
@@ -1363,10 +1361,10 @@ static bool fm10k_clean_tx_irq(struct fm10k_q_vector *q_vector,
**/
static void fm10k_update_itr(struct fm10k_ring_container *ring_container)
{
- unsigned int avg_wire_size, packets;
+ unsigned int avg_wire_size, packets, itr_round;
/* Only update ITR if we are using adaptive setting */
- if (!(ring_container->itr & FM10K_ITR_ADAPTIVE))
+ if (!ITR_IS_ADAPTIVE(ring_container->itr))
goto clear_counts;
packets = ring_container->total_packets;
@@ -1375,18 +1373,44 @@ static void fm10k_update_itr(struct fm10k_ring_container *ring_container)
avg_wire_size = ring_container->total_bytes / packets;
- /* Add 24 bytes to size to account for CRC, preamble, and gap */
- avg_wire_size += 24;
-
- /* Don't starve jumbo frames */
- if (avg_wire_size > 3000)
- avg_wire_size = 3000;
+ /* The following is a crude approximation of:
+ * wmem_default / (size + overhead) = desired_pkts_per_int
+ * rate / bits_per_byte / (size + ethernet overhead) = pkt_rate
+ * (desired_pkt_rate / pkt_rate) * usecs_per_sec = ITR value
+ *
+ * Assuming wmem_default is 212992 and overhead is 640 bytes per
+ * packet, (256 skb, 64 headroom, 320 shared info), we can reduce the
+ * formula down to
+ *
+ * (34 * (size + 24)) / (size + 640) = ITR
+ *
+ * We first do some math on the packet size and then finally bitshift
+ * by 8 after rounding up. We also have to account for PCIe link speed
+ * difference as ITR scales based on this.
+ */
+ if (avg_wire_size <= 360) {
+ /* Start at 250K ints/sec and gradually drop to 77K ints/sec */
+ avg_wire_size *= 8;
+ avg_wire_size += 376;
+ } else if (avg_wire_size <= 1152) {
+ /* 77K ints/sec to 45K ints/sec */
+ avg_wire_size *= 3;
+ avg_wire_size += 2176;
+ } else if (avg_wire_size <= 1920) {
+ /* 45K ints/sec to 38K ints/sec */
+ avg_wire_size += 4480;
+ } else {
+ /* plateau at a limit of 38K ints/sec */
+ avg_wire_size = 6656;
+ }
- /* Give a little boost to mid-size frames */
- if ((avg_wire_size > 300) && (avg_wire_size < 1200))
- avg_wire_size /= 3;
- else
- avg_wire_size /= 2;
+ /* Perform final bitshift for division after rounding up to ensure
+ * that the calculation will never get below a 1. The bit shift
+ * accounts for changes in the ITR due to PCIe link speed.
+ */
+ itr_round = ACCESS_ONCE(ring_container->itr_scale) + 8;
+ avg_wire_size += (1 << itr_round) - 1;
+ avg_wire_size >>= itr_round;
/* write back value and retain adaptive flag */
ring_container->itr = avg_wire_size | FM10K_ITR_ADAPTIVE;
@@ -1428,11 +1452,15 @@ static int fm10k_poll(struct napi_struct *napi, int budget)
fm10k_for_each_ring(ring, q_vector->tx)
clean_complete &= fm10k_clean_tx_irq(q_vector, ring);
+ /* Handle case where we are called by netpoll with a budget of 0 */
+ if (budget <= 0)
+ return budget;
+
/* attempt to distribute budget to each queue fairly, but don't
* allow the budget to go below 1 because we'll exit polling
*/
if (q_vector->rx.count > 1)
- per_ring_budget = max(budget/q_vector->rx.count, 1);
+ per_ring_budget = max(budget / q_vector->rx.count, 1);
else
per_ring_budget = budget;
@@ -1600,6 +1628,7 @@ static int fm10k_alloc_q_vector(struct fm10k_intfc *interface,
q_vector->tx.ring = ring;
q_vector->tx.work_limit = FM10K_DEFAULT_TX_WORK;
q_vector->tx.itr = interface->tx_itr;
+ q_vector->tx.itr_scale = interface->hw.mac.itr_scale;
q_vector->tx.count = txr_count;
while (txr_count) {
@@ -1628,6 +1657,7 @@ static int fm10k_alloc_q_vector(struct fm10k_intfc *interface,
/* save Rx ring container info */
q_vector->rx.ring = ring;
q_vector->rx.itr = interface->rx_itr;
+ q_vector->rx.itr_scale = interface->hw.mac.itr_scale;
q_vector->rx.count = rxr_count;
while (rxr_count) {
@@ -1966,8 +1996,10 @@ int fm10k_init_queueing_scheme(struct fm10k_intfc *interface)
/* Allocate memory for queues */
err = fm10k_alloc_q_vectors(interface);
- if (err)
+ if (err) {
+ fm10k_reset_msix_capability(interface);
return err;
+ }
/* Map rings to devices, and map devices to physical queues */
fm10k_assign_rings(interface);
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c
index af09a1b272e6..98202c3d591c 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c
@@ -57,7 +57,7 @@ static u16 fm10k_fifo_unused(struct fm10k_mbx_fifo *fifo)
}
/**
- * fm10k_fifo_empty - Test to verify if fifo is empty
+ * fm10k_fifo_empty - Test to verify if FIFO is empty
* @fifo: pointer to FIFO
*
* This function returns true if the FIFO is empty, else false
@@ -72,7 +72,7 @@ static bool fm10k_fifo_empty(struct fm10k_mbx_fifo *fifo)
* @fifo: pointer to FIFO
* @offset: offset to add to head
*
- * This function returns the indices into the fifo based on head + offset
+ * This function returns the indices into the FIFO based on head + offset
**/
static u16 fm10k_fifo_head_offset(struct fm10k_mbx_fifo *fifo, u16 offset)
{
@@ -84,7 +84,7 @@ static u16 fm10k_fifo_head_offset(struct fm10k_mbx_fifo *fifo, u16 offset)
* @fifo: pointer to FIFO
* @offset: offset to add to tail
*
- * This function returns the indices into the fifo based on tail + offset
+ * This function returns the indices into the FIFO based on tail + offset
**/
static u16 fm10k_fifo_tail_offset(struct fm10k_mbx_fifo *fifo, u16 offset)
{
@@ -160,7 +160,7 @@ static u16 fm10k_mbx_index_len(struct fm10k_mbx_info *mbx, u16 head, u16 tail)
/**
* fm10k_mbx_tail_add - Determine new tail value with added offset
* @mbx: pointer to mailbox
- * @offset: length to add to head offset
+ * @offset: length to add to tail offset
*
* This function takes the local tail index and recomputes it for
* a given length added as an offset.
@@ -176,7 +176,7 @@ static u16 fm10k_mbx_tail_add(struct fm10k_mbx_info *mbx, u16 offset)
/**
* fm10k_mbx_tail_sub - Determine new tail value with subtracted offset
* @mbx: pointer to mailbox
- * @offset: length to add to head offset
+ * @offset: length to add to tail offset
*
* This function takes the local tail index and recomputes it for
* a given length added as an offset.
@@ -240,7 +240,7 @@ static u16 fm10k_mbx_pushed_tail_len(struct fm10k_mbx_info *mbx)
}
/**
- * fm10k_fifo_write_copy - pulls data off of msg and places it in fifo
+ * fm10k_fifo_write_copy - pulls data off of msg and places it in FIFO
* @fifo: pointer to FIFO
* @msg: message array to populate
* @tail_offset: additional offset to add to tail pointer
@@ -336,6 +336,7 @@ static u16 fm10k_mbx_validate_msg_size(struct fm10k_mbx_info *mbx, u16 len)
/**
* fm10k_mbx_write_copy - pulls data off of Tx FIFO and places it in mbmem
+ * @hw: pointer to hardware structure
* @mbx: pointer to mailbox
*
* This function will take a section of the Tx FIFO and copy it into the
@@ -375,6 +376,8 @@ static void fm10k_mbx_write_copy(struct fm10k_hw *hw,
if (!tail)
tail++;
+ mbx->tx_mbmem_pulled++;
+
/* write message to hardware FIFO */
fm10k_write_reg(hw, mbmem + tail++, *(head++));
} while (--len && --end);
@@ -459,6 +462,8 @@ static void fm10k_mbx_read_copy(struct fm10k_hw *hw,
if (!head)
head++;
+ mbx->rx_mbmem_pushed++;
+
/* read message from hardware FIFO */
*(tail++) = fm10k_read_reg(hw, mbmem + head++);
} while (--len && --end);
@@ -707,7 +712,7 @@ static bool fm10k_mbx_tx_complete(struct fm10k_mbx_info *mbx)
* @hw: pointer to hardware structure
* @mbx: pointer to mailbox
*
- * This function dequeues messages and hands them off to the tlv parser.
+ * This function dequeues messages and hands them off to the TLV parser.
* It will return the number of messages processed when called.
**/
static u16 fm10k_mbx_dequeue_rx(struct fm10k_hw *hw,
@@ -899,7 +904,7 @@ static void fm10k_mbx_create_disconnect_hdr(struct fm10k_mbx_info *mbx)
}
/**
- * fm10k_mbx_create_fake_disconnect_hdr - Generate a false disconnect mailbox header
+ * fm10k_mbx_create_fake_disconnect_hdr - Generate a false disconnect mbox hdr
* @mbx: pointer to mailbox
*
* This function creates a fake disconnect header for loading into remote
@@ -920,7 +925,7 @@ static void fm10k_mbx_create_fake_disconnect_hdr(struct fm10k_mbx_info *mbx)
}
/**
- * fm10k_mbx_create_error_msg - Generate a error message
+ * fm10k_mbx_create_error_msg - Generate an error message
* @mbx: pointer to mailbox
* @err: local error encountered
*
@@ -953,7 +958,6 @@ static void fm10k_mbx_create_error_msg(struct fm10k_mbx_info *mbx, s32 err)
/**
* fm10k_mbx_validate_msg_hdr - Validate common fields in the message header
* @mbx: pointer to mailbox
- * @msg: message array to read
*
* This function will parse up the fields in the mailbox header and return
* an error if the header contains any of a number of invalid configurations
@@ -1017,11 +1021,12 @@ static s32 fm10k_mbx_validate_msg_hdr(struct fm10k_mbx_info *mbx)
/**
* fm10k_mbx_create_reply - Generate reply based on state and remote head
+ * @hw: pointer to hardware structure
* @mbx: pointer to mailbox
* @head: acknowledgement number
*
* This function will generate an outgoing message based on the current
- * mailbox state and the remote fifo head. It will return the length
+ * mailbox state and the remote FIFO head. It will return the length
* of the outgoing message excluding header on success, and a negative value
* on error.
**/
@@ -1147,8 +1152,8 @@ static void fm10k_mbx_connect_reset(struct fm10k_mbx_info *mbx)
/**
* fm10k_mbx_process_connect - Process connect header
+ * @hw: pointer to hardware structure
* @mbx: pointer to mailbox
- * @msg: message array to process
*
* This function will read an incoming connect header and reply with the
* appropriate message. It will return a value indicating the number of
@@ -1194,6 +1199,7 @@ static s32 fm10k_mbx_process_connect(struct fm10k_hw *hw,
/**
* fm10k_mbx_process_data - Process data header
+ * @hw: pointer to hardware structure
* @mbx: pointer to mailbox
*
* This function will read an incoming data header and reply with the
@@ -1235,6 +1241,7 @@ static s32 fm10k_mbx_process_data(struct fm10k_hw *hw,
/**
* fm10k_mbx_process_disconnect - Process disconnect header
+ * @hw: pointer to hardware structure
* @mbx: pointer to mailbox
*
* This function will read an incoming disconnect header and reply with the
@@ -1287,6 +1294,7 @@ static s32 fm10k_mbx_process_disconnect(struct fm10k_hw *hw,
/**
* fm10k_mbx_process_error - Process error header
+ * @hw: pointer to hardware structure
* @mbx: pointer to mailbox
*
* This function will read an incoming error header and reply with the
@@ -1556,7 +1564,7 @@ static s32 fm10k_mbx_register_handlers(struct fm10k_mbx_info *mbx,
* @id: ID reference for PF as it supports up to 64 PF/VF mailboxes
*
* This function initializes the mailbox for use. It will split the
- * buffer provided an use that th populate both the Tx and Rx FIFO by
+ * buffer provided and use that to populate both the Tx and Rx FIFO by
* evenly splitting it. In order to allow for easy masking of head/tail
* the value reported in size must be a power of 2 and is reported in
* DWORDs, not bytes. Any invalid values will cause the mailbox to return
@@ -1633,7 +1641,7 @@ s32 fm10k_pfvf_mbx_init(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx,
* fm10k_sm_mbx_create_data_hdr - Generate a mailbox header for local FIFO
* @mbx: pointer to mailbox
*
- * This function returns a connection mailbox header
+ * This function returns a data mailbox header
**/
static void fm10k_sm_mbx_create_data_hdr(struct fm10k_mbx_info *mbx)
{
@@ -1726,8 +1734,6 @@ static s32 fm10k_sm_mbx_connect(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx)
fm10k_sm_mbx_create_connect_hdr(mbx, 0);
fm10k_mbx_write(hw, mbx);
- /* enable interrupt and notify other party of new message */
-
return 0;
}
@@ -1771,7 +1777,7 @@ static void fm10k_sm_mbx_disconnect(struct fm10k_hw *hw,
}
/**
- * fm10k_mbx_validate_fifo_hdr - Validate fields in the remote FIFO header
+ * fm10k_sm_mbx_validate_fifo_hdr - Validate fields in the remote FIFO header
* @mbx: pointer to mailbox
*
* This function will parse up the fields in the mailbox header and return
@@ -1849,7 +1855,7 @@ static void fm10k_sm_mbx_process_error(struct fm10k_mbx_info *mbx)
}
/**
- * fm10k_sm_mbx_create_error_message - Process an error in FIFO hdr
+ * fm10k_sm_mbx_create_error_msg - Process an error in FIFO header
* @mbx: pointer to mailbox
* @err: local error encountered
*
@@ -1879,6 +1885,7 @@ static void fm10k_sm_mbx_create_error_msg(struct fm10k_mbx_info *mbx, s32 err)
* fm10k_sm_mbx_receive - Take message from Rx mailbox FIFO and put it in Rx
* @hw: pointer to hardware structure
* @mbx: pointer to mailbox
+ * @tail: tail index of message
*
* This function will dequeue one message from the Rx switch manager mailbox
* FIFO and place it in the Rx mailbox FIFO for processing by software.
@@ -1918,6 +1925,7 @@ static s32 fm10k_sm_mbx_receive(struct fm10k_hw *hw,
* fm10k_sm_mbx_transmit - Take message from Tx and put it in Tx mailbox FIFO
* @hw: pointer to hardware structure
* @mbx: pointer to mailbox
+ * @head: head index of message
*
* This function will dequeue one message from the Tx mailbox FIFO and place
* it in the Tx switch manager mailbox FIFO for processing by hardware.
@@ -1957,11 +1965,12 @@ static void fm10k_sm_mbx_transmit(struct fm10k_hw *hw,
/**
* fm10k_sm_mbx_create_reply - Generate reply based on state and remote head
+ * @hw: pointer to hardware structure
* @mbx: pointer to mailbox
* @head: acknowledgement number
*
* This function will generate an outgoing message based on the current
- * mailbox state and the remote fifo head. It will return the length
+ * mailbox state and the remote FIFO head. It will return the length
* of the outgoing message excluding header on success, and a negative value
* on error.
**/
@@ -2073,7 +2082,7 @@ send_reply:
}
/**
- * fm10k_sm_mbx_process - Process mailbox switch mailbox interrupt
+ * fm10k_sm_mbx_process - Process switch manager mailbox interrupt
* @hw: pointer to hardware structure
* @mbx: pointer to mailbox
*
@@ -2129,13 +2138,19 @@ fifo_err:
* @mbx: pointer to mailbox
* @msg_data: handlers for mailbox events
*
- * This function for now is used to stub out the PF/SM mailbox
+ * This function initializes the PF/SM mailbox for use. It will split the
+ * buffer provided and use that to populate both the Tx and Rx FIFO by
+ * evenly splitting it. In order to allow for easy masking of head/tail
+ * the value reported in size must be a power of 2 and is reported in
+ * DWORDs, not bytes. Any invalid values will cause the mailbox to return
+ * error.
**/
s32 fm10k_sm_mbx_init(struct fm10k_hw *hw, struct fm10k_mbx_info *mbx,
const struct fm10k_msg_data *msg_data)
{
mbx->mbx_reg = FM10K_GMBX;
mbx->mbmem_reg = FM10K_MBMEM_PF(0);
+
/* start out in closed state */
mbx->state = FM10K_STATE_CLOSED;
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.h b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.h
index 0419a7f0035e..245a0a3dc32e 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.h
@@ -1,5 +1,5 @@
/* Intel Ethernet Switch Host Interface Driver
- * Copyright(c) 2013 - 2014 Intel Corporation.
+ * 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,
@@ -128,11 +128,11 @@ enum fm10k_mbx_state {
* The maximum message size is provided during connect to avoid
* jamming the mailbox with messages that do not fit.
* Err_no: Error number - Applies only to error headers
- * The error number provides a indication of the type of error
+ * The error number provides an indication of the type of error
* experienced.
*/
-/* macros for retriving and setting header values */
+/* macros for retrieving and setting header values */
#define FM10K_MSG_HDR_MASK(name) \
((0x1u << FM10K_MSG_##name##_SIZE) - 1)
#define FM10K_MSG_HDR_FIELD_SET(value, name) \
@@ -291,8 +291,10 @@ struct fm10k_mbx_info {
u64 tx_dropped;
u64 tx_messages;
u64 tx_dwords;
+ u64 tx_mbmem_pulled;
u64 rx_messages;
u64 rx_dwords;
+ u64 rx_mbmem_pushed;
u64 rx_parse_err;
/* Buffer to store messages */
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c
index 639263d5e833..662569d5b7c0 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c
@@ -20,7 +20,7 @@
#include "fm10k.h"
#include <linux/vmalloc.h>
-#if IS_ENABLED(CONFIG_FM10K_VXLAN)
+#ifdef CONFIG_FM10K_VXLAN
#include <net/vxlan.h>
#endif /* CONFIG_FM10K_VXLAN */
@@ -556,11 +556,11 @@ int fm10k_open(struct net_device *netdev)
if (err)
goto err_set_queues;
-#if IS_ENABLED(CONFIG_FM10K_VXLAN)
+#ifdef CONFIG_FM10K_VXLAN
/* update VXLAN port configuration */
vxlan_get_rx_port(netdev);
-
#endif
+
fm10k_up(interface);
return 0;
@@ -608,7 +608,7 @@ static netdev_tx_t fm10k_xmit_frame(struct sk_buff *skb, struct net_device *dev)
unsigned int r_idx = skb->queue_mapping;
int err;
- if ((skb->protocol == htons(ETH_P_8021Q)) &&
+ if ((skb->protocol == htons(ETH_P_8021Q)) &&
!skb_vlan_tag_present(skb)) {
/* FM10K only supports hardware tagging, any tags in frame
* are considered 2nd level or "outer" tags
@@ -627,10 +627,12 @@ static netdev_tx_t fm10k_xmit_frame(struct sk_buff *skb, struct net_device *dev)
/* verify the skb head is not shared */
err = skb_cow_head(skb, 0);
- if (err)
+ if (err) {
+ dev_kfree_skb(skb);
return NETDEV_TX_OK;
+ }
- /* locate vlan header */
+ /* locate VLAN header */
vhdr = (struct vlan_hdr *)(skb->data + ETH_HLEN);
/* pull the 2 key pieces of data out of it */
@@ -703,7 +705,7 @@ static void fm10k_tx_timeout(struct net_device *netdev)
} else {
netif_info(interface, drv, netdev,
"Fake Tx hang detected with timeout of %d seconds\n",
- netdev->watchdog_timeo/HZ);
+ netdev->watchdog_timeo / HZ);
/* fake Tx hang - increase the kernel timeout */
if (netdev->watchdog_timeo < TX_TIMEO_LIMIT)
@@ -776,7 +778,7 @@ static int fm10k_update_vid(struct net_device *netdev, u16 vid, bool set)
if (!set)
clear_bit(vid, interface->active_vlans);
- /* disable the default VID on ring if we have an active VLAN */
+ /* disable the default VLAN ID on ring if we have an active VLAN */
for (i = 0; i < interface->num_rx_queues; i++) {
struct fm10k_ring *rx_ring = interface->rx_ring[i];
u16 rx_vid = rx_ring->vid & (VLAN_N_VID - 1);
@@ -787,7 +789,9 @@ static int fm10k_update_vid(struct net_device *netdev, u16 vid, bool set)
rx_ring->vid &= ~FM10K_VLAN_CLEAR;
}
- /* Do not remove default VID related entries from VLAN and MAC tables */
+ /* Do not remove default VLAN ID related entries from VLAN and MAC
+ * tables
+ */
if (!set && vid == hw->mac.default_vid)
return 0;
@@ -812,7 +816,7 @@ static int fm10k_update_vid(struct net_device *netdev, u16 vid, bool set)
if (err)
goto err_out;
- /* set vid prior to syncing/unsyncing the VLAN */
+ /* set VLAN ID prior to syncing/unsyncing the VLAN */
interface->vid = vid + (set ? VLAN_N_VID : 0);
/* Update the unicast and multicast address list to add/drop VLAN */
@@ -1149,6 +1153,7 @@ static struct rtnl_link_stats64 *fm10k_get_stats64(struct net_device *netdev,
int fm10k_setup_tc(struct net_device *dev, u8 tc)
{
struct fm10k_intfc *interface = netdev_priv(dev);
+ int err;
/* Currently only the PF supports priority classes */
if (tc && (interface->hw.mac.type != fm10k_mac_pf))
@@ -1173,17 +1178,30 @@ int fm10k_setup_tc(struct net_device *dev, u8 tc)
netdev_reset_tc(dev);
netdev_set_num_tc(dev, tc);
- fm10k_init_queueing_scheme(interface);
+ err = fm10k_init_queueing_scheme(interface);
+ if (err)
+ goto err_queueing_scheme;
- fm10k_mbx_request_irq(interface);
+ err = fm10k_mbx_request_irq(interface);
+ if (err)
+ goto err_mbx_irq;
- if (netif_running(dev))
- fm10k_open(dev);
+ err = netif_running(dev) ? fm10k_open(dev) : 0;
+ if (err)
+ goto err_open;
/* flag to indicate SWPRI has yet to be updated */
interface->flags |= FM10K_FLAG_SWPRI_CONFIG;
return 0;
+err_open:
+ fm10k_mbx_free_irq(interface);
+err_mbx_irq:
+ fm10k_clear_queueing_scheme(interface);
+err_queueing_scheme:
+ netif_device_detach(dev);
+
+ return err;
}
static int fm10k_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
@@ -1353,7 +1371,7 @@ static netdev_features_t fm10k_features_check(struct sk_buff *skb,
if (!skb->encapsulation || fm10k_tx_encap_offload(skb))
return features;
- return features & ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK);
+ return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
}
static const struct net_device_ops fm10k_netdev_ops = {
@@ -1386,8 +1404,9 @@ static const struct net_device_ops fm10k_netdev_ops = {
#define DEFAULT_DEBUG_LEVEL_SHIFT 3
-struct net_device *fm10k_alloc_netdev(void)
+struct net_device *fm10k_alloc_netdev(const struct fm10k_info *info)
{
+ netdev_features_t hw_features;
struct fm10k_intfc *interface;
struct net_device *dev;
@@ -1410,27 +1429,31 @@ struct net_device *fm10k_alloc_netdev(void)
NETIF_F_TSO |
NETIF_F_TSO6 |
NETIF_F_TSO_ECN |
- NETIF_F_GSO_UDP_TUNNEL |
NETIF_F_RXHASH |
NETIF_F_RXCSUM;
+ /* Only the PF can support VXLAN and NVGRE tunnel offloads */
+ if (info->mac == fm10k_mac_pf) {
+ dev->hw_enc_features = NETIF_F_IP_CSUM |
+ NETIF_F_TSO |
+ NETIF_F_TSO6 |
+ NETIF_F_TSO_ECN |
+ NETIF_F_GSO_UDP_TUNNEL |
+ NETIF_F_IPV6_CSUM |
+ NETIF_F_SG;
+
+ dev->features |= NETIF_F_GSO_UDP_TUNNEL;
+ }
+
/* all features defined to this point should be changeable */
- dev->hw_features |= dev->features;
+ hw_features = dev->features;
/* allow user to enable L2 forwarding acceleration */
- dev->hw_features |= NETIF_F_HW_L2FW_DOFFLOAD;
+ hw_features |= NETIF_F_HW_L2FW_DOFFLOAD;
/* configure VLAN features */
dev->vlan_features |= dev->features;
- /* configure tunnel offloads */
- dev->hw_enc_features |= NETIF_F_IP_CSUM |
- NETIF_F_TSO |
- NETIF_F_TSO6 |
- NETIF_F_TSO_ECN |
- NETIF_F_GSO_UDP_TUNNEL |
- NETIF_F_IPV6_CSUM;
-
/* we want to leave these both on as we cannot disable VLAN tag
* insertion or stripping on the hardware since it is contained
* in the FTAG and not in the frame itself.
@@ -1441,5 +1464,7 @@ struct net_device *fm10k_alloc_netdev(void)
dev->priv_flags |= IFF_UNICAST_FLT;
+ dev->hw_features |= hw_features;
+
return dev;
}
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c
index 74be792f3f1b..4eb7a6fa6b0d 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c
@@ -159,16 +159,40 @@ static void fm10k_reinit(struct fm10k_intfc *interface)
fm10k_mbx_free_irq(interface);
+ /* free interrupts */
+ fm10k_clear_queueing_scheme(interface);
+
/* delay any future reset requests */
interface->last_reset = jiffies + (10 * HZ);
/* reset and initialize the hardware so it is in a known state */
- err = hw->mac.ops.reset_hw(hw) ? : hw->mac.ops.init_hw(hw);
- if (err)
+ err = hw->mac.ops.reset_hw(hw);
+ if (err) {
+ dev_err(&interface->pdev->dev, "reset_hw failed: %d\n", err);
+ goto reinit_err;
+ }
+
+ err = hw->mac.ops.init_hw(hw);
+ if (err) {
dev_err(&interface->pdev->dev, "init_hw failed: %d\n", err);
+ goto reinit_err;
+ }
+
+ err = fm10k_init_queueing_scheme(interface);
+ if (err) {
+ dev_err(&interface->pdev->dev,
+ "init_queueing_scheme failed: %d\n", err);
+ goto reinit_err;
+ }
/* reassociate interrupts */
- fm10k_mbx_request_irq(interface);
+ err = fm10k_mbx_request_irq(interface);
+ if (err)
+ goto err_mbx_irq;
+
+ err = fm10k_hw_ready(interface);
+ if (err)
+ goto err_open;
/* update hardware address for VFs if perm_addr has changed */
if (hw->mac.type == fm10k_mac_vf) {
@@ -188,14 +212,27 @@ static void fm10k_reinit(struct fm10k_intfc *interface)
/* reset clock */
fm10k_ts_reset(interface);
- if (netif_running(netdev))
- fm10k_open(netdev);
+ err = netif_running(netdev) ? fm10k_open(netdev) : 0;
+ if (err)
+ goto err_open;
fm10k_iov_resume(interface->pdev);
rtnl_unlock();
clear_bit(__FM10K_RESETTING, &interface->state);
+
+ return;
+err_open:
+ fm10k_mbx_free_irq(interface);
+err_mbx_irq:
+ fm10k_clear_queueing_scheme(interface);
+reinit_err:
+ netif_device_detach(netdev);
+
+ rtnl_unlock();
+
+ clear_bit(__FM10K_RESETTING, &interface->state);
}
static void fm10k_reset_subtask(struct fm10k_intfc *interface)
@@ -563,7 +600,7 @@ static void fm10k_configure_tx_ring(struct fm10k_intfc *interface,
/* store tail pointer */
ring->tail = &interface->uc_addr[FM10K_TDT(reg_idx)];
- /* reset ntu and ntc to place SW in sync with hardwdare */
+ /* reset ntu and ntc to place SW in sync with hardware */
ring->next_to_clean = 0;
ring->next_to_use = 0;
@@ -579,6 +616,13 @@ static void fm10k_configure_tx_ring(struct fm10k_intfc *interface,
fm10k_write_reg(hw, FM10K_PFVTCTL(reg_idx),
FM10K_PFVTCTL_FTAG_DESC_ENABLE);
+ /* Initialize XPS */
+ if (!test_and_set_bit(__FM10K_TX_XPS_INIT_DONE, &ring->state) &&
+ ring->q_vector)
+ netif_set_xps_queue(ring->netdev,
+ &ring->q_vector->affinity_mask,
+ ring->queue_index);
+
/* enable queue */
fm10k_write_reg(hw, FM10K_TXDCTL(reg_idx), txdctl);
}
@@ -669,7 +713,7 @@ static void fm10k_configure_rx_ring(struct fm10k_intfc *interface,
/* store tail pointer */
ring->tail = &interface->uc_addr[FM10K_RDT(reg_idx)];
- /* reset ntu and ntc to place SW in sync with hardwdare */
+ /* reset ntu and ntc to place SW in sync with hardware */
ring->next_to_clean = 0;
ring->next_to_use = 0;
ring->next_to_alloc = 0;
@@ -694,7 +738,7 @@ static void fm10k_configure_rx_ring(struct fm10k_intfc *interface,
/* assign default VLAN to queue */
ring->vid = hw->mac.default_vid;
- /* if we have an active VLAN, disable default VID */
+ /* if we have an active VLAN, disable default VLAN ID */
if (test_bit(hw->mac.default_vid, interface->active_vlans))
ring->vid |= FM10K_VLAN_CLEAR;
@@ -846,7 +890,7 @@ static irqreturn_t fm10k_msix_clean_rings(int __always_unused irq, void *data)
struct fm10k_q_vector *q_vector = data;
if (q_vector->rx.count || q_vector->tx.count)
- napi_schedule(&q_vector->napi);
+ napi_schedule_irqoff(&q_vector->napi);
return IRQ_HANDLED;
}
@@ -859,7 +903,8 @@ static irqreturn_t fm10k_msix_mbx_vf(int __always_unused irq, void *data)
/* re-enable mailbox interrupt and indicate 20us delay */
fm10k_write_reg(hw, FM10K_VFITR(FM10K_MBX_VECTOR),
- FM10K_ITR_ENABLE | FM10K_MBX_INT_DELAY);
+ FM10K_ITR_ENABLE | (FM10K_MBX_INT_DELAY >>
+ hw->mac.itr_scale));
/* service upstream mailbox */
if (fm10k_mbx_trylock(interface)) {
@@ -867,7 +912,7 @@ static irqreturn_t fm10k_msix_mbx_vf(int __always_unused irq, void *data)
fm10k_mbx_unlock(interface);
}
- hw->mac.get_host_state = 1;
+ hw->mac.get_host_state = true;
fm10k_service_event_schedule(interface);
return IRQ_HANDLED;
@@ -897,7 +942,7 @@ void fm10k_netpoll(struct net_device *netdev)
#endif
#define FM10K_ERR_MSG(type) case (type): error = #type; break
static void fm10k_handle_fault(struct fm10k_intfc *interface, int type,
- struct fm10k_fault *fault)
+ struct fm10k_fault *fault)
{
struct pci_dev *pdev = interface->pdev;
struct fm10k_hw *hw = &interface->hw;
@@ -1083,14 +1128,15 @@ static irqreturn_t fm10k_msix_mbx_pf(int __always_unused irq, void *data)
}
/* we should validate host state after interrupt event */
- hw->mac.get_host_state = 1;
+ hw->mac.get_host_state = true;
/* validate host state, and handle VF mailboxes in the service task */
fm10k_service_event_schedule(interface);
/* re-enable mailbox interrupt and indicate 20us delay */
fm10k_write_reg(hw, FM10K_ITR(FM10K_MBX_VECTOR),
- FM10K_ITR_ENABLE | FM10K_MBX_INT_DELAY);
+ FM10K_ITR_ENABLE | (FM10K_MBX_INT_DELAY >>
+ hw->mac.itr_scale));
return IRQ_HANDLED;
}
@@ -1101,6 +1147,10 @@ void fm10k_mbx_free_irq(struct fm10k_intfc *interface)
struct fm10k_hw *hw = &interface->hw;
int itr_reg;
+ /* no mailbox IRQ to free if MSI-X is not enabled */
+ if (!interface->msix_entries)
+ return;
+
/* disconnect the mailbox */
hw->mbx.ops.disconnect(hw, &hw->mbx);
@@ -1141,7 +1191,7 @@ static s32 fm10k_mbx_mac_addr(struct fm10k_hw *hw, u32 **results,
/* MAC was changed so we need reset */
if (is_valid_ether_addr(hw->mac.perm_addr) &&
- memcmp(hw->mac.perm_addr, hw->mac.addr, ETH_ALEN))
+ !ether_addr_equal(hw->mac.perm_addr, hw->mac.addr))
interface->flags |= FM10K_FLAG_RESET_REQUESTED;
/* VLAN override was changed, or default VLAN changed */
@@ -1269,7 +1319,7 @@ static s32 fm10k_update_pvid(struct fm10k_hw *hw, u32 **results,
if (!fm10k_glort_valid_pf(hw, glort))
return FM10K_ERR_PARAM;
- /* verify VID is valid */
+ /* verify VLAN ID is valid */
if (pvid >= FM10K_VLAN_TABLE_VID_MAX)
return FM10K_ERR_PARAM;
@@ -1388,14 +1438,14 @@ static int fm10k_mbx_request_irq_pf(struct fm10k_intfc *interface)
}
/* Enable interrupts w/ no moderation for "other" interrupts */
- fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_PCIeFault), other_itr);
- fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_SwitchUpDown), other_itr);
- fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_SRAM), other_itr);
- fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_MaxHoldTime), other_itr);
- fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_VFLR), other_itr);
+ fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_pcie_fault), other_itr);
+ fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_switch_up_down), other_itr);
+ fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_sram), other_itr);
+ fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_max_hold_time), other_itr);
+ fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_vflr), other_itr);
/* Enable interrupts w/ moderation for mailbox */
- fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_Mailbox), mbx_itr);
+ fm10k_write_reg(hw, FM10K_INT_MAP(fm10k_int_mailbox), mbx_itr);
/* Enable individual interrupt causes */
fm10k_write_reg(hw, FM10K_EIMR, FM10K_EIMR_ENABLE(PCA_FAULT) |
@@ -1423,10 +1473,15 @@ int fm10k_mbx_request_irq(struct fm10k_intfc *interface)
err = fm10k_mbx_request_irq_pf(interface);
else
err = fm10k_mbx_request_irq_vf(interface);
+ if (err)
+ return err;
/* connect mailbox */
- if (!err)
- err = hw->mbx.ops.connect(hw, &hw->mbx);
+ err = hw->mbx.ops.connect(hw, &hw->mbx);
+
+ /* if the mailbox failed to connect, then free IRQ */
+ if (err)
+ fm10k_mbx_free_irq(interface);
return err;
}
@@ -1455,8 +1510,10 @@ void fm10k_qv_free_irq(struct fm10k_intfc *interface)
if (!q_vector->tx.count && !q_vector->rx.count)
continue;
- /* disable interrupts */
+ /* clear the affinity_mask in the IRQ descriptor */
+ irq_set_affinity_hint(entry->vector, NULL);
+ /* disable interrupts */
writel(FM10K_ITR_MASK_SET, q_vector->itr);
free_irq(entry->vector, q_vector);
@@ -1514,6 +1571,9 @@ int fm10k_qv_request_irq(struct fm10k_intfc *interface)
goto err_out;
}
+ /* assign the mask for this irq */
+ irq_set_affinity_hint(entry->vector, &q_vector->affinity_mask);
+
/* Enable q_vector */
writel(FM10K_ITR_ENABLE, q_vector->itr);
@@ -1534,8 +1594,10 @@ err_out:
if (!q_vector->tx.count && !q_vector->rx.count)
continue;
- /* disable interrupts */
+ /* clear the affinity_mask in the IRQ descriptor */
+ irq_set_affinity_hint(entry->vector, NULL);
+ /* disable interrupts */
writel(FM10K_ITR_MASK_SET, q_vector->itr);
free_irq(entry->vector, q_vector);
@@ -1573,7 +1635,7 @@ void fm10k_up(struct fm10k_intfc *interface)
netif_tx_start_all_queues(interface->netdev);
/* kick off the service timer now */
- hw->mac.get_host_state = 1;
+ hw->mac.get_host_state = true;
mod_timer(&interface->service_timer, jiffies);
}
@@ -1684,7 +1746,13 @@ static int fm10k_sw_init(struct fm10k_intfc *interface,
interface->last_reset = jiffies + (10 * HZ);
/* reset and initialize the hardware so it is in a known state */
- err = hw->mac.ops.reset_hw(hw) ? : hw->mac.ops.init_hw(hw);
+ err = hw->mac.ops.reset_hw(hw);
+ if (err) {
+ dev_err(&pdev->dev, "reset_hw failed: %d\n", err);
+ return err;
+ }
+
+ err = hw->mac.ops.init_hw(hw);
if (err) {
dev_err(&pdev->dev, "init_hw failed: %d\n", err);
return err;
@@ -1722,13 +1790,6 @@ static int fm10k_sw_init(struct fm10k_intfc *interface,
pci_resource_len(pdev, 4));
hw->sw_addr = interface->sw_addr;
- /* Only the PF can support VXLAN and NVGRE offloads */
- if (hw->mac.type != fm10k_mac_pf) {
- netdev->hw_enc_features = 0;
- netdev->features &= ~NETIF_F_GSO_UDP_TUNNEL;
- netdev->hw_features &= ~NETIF_F_GSO_UDP_TUNNEL;
- }
-
/* initialize DCBNL interface */
fm10k_dcbnl_set_ops(netdev);
@@ -1749,8 +1810,8 @@ static int fm10k_sw_init(struct fm10k_intfc *interface,
interface->rx_ring_count = FM10K_DEFAULT_RXD;
/* set default interrupt moderation */
- interface->tx_itr = FM10K_ITR_10K;
- interface->rx_itr = FM10K_ITR_ADAPTIVE | FM10K_ITR_20K;
+ interface->tx_itr = FM10K_TX_ITR_DEFAULT;
+ interface->rx_itr = FM10K_ITR_ADAPTIVE | FM10K_RX_ITR_DEFAULT;
/* initialize vxlan_port list */
INIT_LIST_HEAD(&interface->vxlan_port);
@@ -1835,17 +1896,18 @@ static void fm10k_slot_warn(struct fm10k_intfc *interface)
return;
}
- if (max_gts < expected_gts) {
- dev_warn(&interface->pdev->dev,
- "This device requires %dGT/s of bandwidth for optimal performance.\n",
- expected_gts);
- dev_warn(&interface->pdev->dev,
- "A %sslot with x%d lanes is suggested.\n",
- (hw->bus_caps.speed == fm10k_bus_speed_2500 ? "2.5GT/s " :
- hw->bus_caps.speed == fm10k_bus_speed_5000 ? "5.0GT/s " :
- hw->bus_caps.speed == fm10k_bus_speed_8000 ? "8.0GT/s " : ""),
- hw->bus_caps.width);
- }
+ if (max_gts >= expected_gts)
+ return;
+
+ dev_warn(&interface->pdev->dev,
+ "This device requires %dGT/s of bandwidth for optimal performance.\n",
+ expected_gts);
+ dev_warn(&interface->pdev->dev,
+ "A %sslot with x%d lanes is suggested.\n",
+ (hw->bus_caps.speed == fm10k_bus_speed_2500 ? "2.5GT/s " :
+ hw->bus_caps.speed == fm10k_bus_speed_5000 ? "5.0GT/s " :
+ hw->bus_caps.speed == fm10k_bus_speed_8000 ? "8.0GT/s " : ""),
+ hw->bus_caps.width);
}
/**
@@ -1859,8 +1921,7 @@ static void fm10k_slot_warn(struct fm10k_intfc *interface)
* The OS initialization, configuring of the interface private structure,
* and a hardware reset occur.
**/
-static int fm10k_probe(struct pci_dev *pdev,
- const struct pci_device_id *ent)
+static int fm10k_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct net_device *netdev;
struct fm10k_intfc *interface;
@@ -1894,7 +1955,7 @@ static int fm10k_probe(struct pci_dev *pdev,
pci_set_master(pdev);
pci_save_state(pdev);
- netdev = fm10k_alloc_netdev();
+ netdev = fm10k_alloc_netdev(fm10k_info_tbl[ent->driver_data]);
if (!netdev) {
err = -ENOMEM;
goto err_alloc_netdev;
@@ -2071,8 +2132,10 @@ static int fm10k_resume(struct pci_dev *pdev)
/* reset hardware to known state */
err = hw->mac.ops.init_hw(&interface->hw);
- if (err)
+ if (err) {
+ dev_err(&pdev->dev, "init_hw failed: %d\n", err);
return err;
+ }
/* reset statistics starting values */
hw->mac.ops.rebind_hw_stats(hw, &interface->stats);
@@ -2083,16 +2146,22 @@ static int fm10k_resume(struct pci_dev *pdev)
rtnl_lock();
err = fm10k_init_queueing_scheme(interface);
- if (!err) {
- fm10k_mbx_request_irq(interface);
- if (netif_running(netdev))
- err = fm10k_open(netdev);
- }
+ if (err)
+ goto err_queueing_scheme;
- rtnl_unlock();
+ err = fm10k_mbx_request_irq(interface);
+ if (err)
+ goto err_mbx_irq;
+ err = fm10k_hw_ready(interface);
if (err)
- return err;
+ goto err_open;
+
+ err = netif_running(netdev) ? fm10k_open(netdev) : 0;
+ if (err)
+ goto err_open;
+
+ rtnl_unlock();
/* assume host is not ready, to prevent race with watchdog in case we
* actually don't have connection to the switch
@@ -2110,6 +2179,14 @@ static int fm10k_resume(struct pci_dev *pdev)
netif_device_attach(netdev);
return 0;
+err_open:
+ fm10k_mbx_free_irq(interface);
+err_mbx_irq:
+ fm10k_clear_queueing_scheme(interface);
+err_queueing_scheme:
+ rtnl_unlock();
+
+ return err;
}
/**
@@ -2185,6 +2262,9 @@ static pci_ers_result_t fm10k_io_error_detected(struct pci_dev *pdev,
if (netif_running(netdev))
fm10k_close(netdev);
+ /* free interrupts */
+ fm10k_clear_queueing_scheme(interface);
+
fm10k_mbx_free_irq(interface);
pci_disable_device(pdev);
@@ -2248,11 +2328,22 @@ static void fm10k_io_resume(struct pci_dev *pdev)
int err = 0;
/* reset hardware to known state */
- hw->mac.ops.init_hw(&interface->hw);
+ err = hw->mac.ops.init_hw(&interface->hw);
+ if (err) {
+ dev_err(&pdev->dev, "init_hw failed: %d\n", err);
+ return;
+ }
/* reset statistics starting values */
hw->mac.ops.rebind_hw_stats(hw, &interface->stats);
+ err = fm10k_init_queueing_scheme(interface);
+ if (err) {
+ dev_err(&interface->pdev->dev,
+ "init_queueing_scheme failed: %d\n", err);
+ return;
+ }
+
/* reassociate interrupts */
fm10k_mbx_request_irq(interface);
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c
index 8c0bdc4e4edd..62ccebc5f728 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c
@@ -1,5 +1,5 @@
/* Intel Ethernet Switch Host Interface Driver
- * Copyright(c) 2013 - 2014 Intel Corporation.
+ * 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,
@@ -150,19 +150,26 @@ static s32 fm10k_init_hw_pf(struct fm10k_hw *hw)
FM10K_TPH_RXCTRL_HDR_WROEN);
}
- /* set max hold interval to align with 1.024 usec in all modes */
+ /* set max hold interval to align with 1.024 usec in all modes and
+ * store ITR scale
+ */
switch (hw->bus.speed) {
case fm10k_bus_speed_2500:
dma_ctrl = FM10K_DMA_CTRL_MAX_HOLD_1US_GEN1;
+ hw->mac.itr_scale = FM10K_TDLEN_ITR_SCALE_GEN1;
break;
case fm10k_bus_speed_5000:
dma_ctrl = FM10K_DMA_CTRL_MAX_HOLD_1US_GEN2;
+ hw->mac.itr_scale = FM10K_TDLEN_ITR_SCALE_GEN2;
break;
case fm10k_bus_speed_8000:
dma_ctrl = FM10K_DMA_CTRL_MAX_HOLD_1US_GEN3;
+ hw->mac.itr_scale = FM10K_TDLEN_ITR_SCALE_GEN3;
break;
default:
dma_ctrl = 0;
+ /* just in case, assume Gen3 ITR scale */
+ hw->mac.itr_scale = FM10K_TDLEN_ITR_SCALE_GEN3;
break;
}
@@ -259,7 +266,6 @@ static s32 fm10k_read_mac_addr_pf(struct fm10k_hw *hw)
{
u8 perm_addr[ETH_ALEN];
u32 serial_num;
- int i;
serial_num = fm10k_read_reg(hw, FM10K_SM_AREA(1));
@@ -281,10 +287,8 @@ static s32 fm10k_read_mac_addr_pf(struct fm10k_hw *hw)
perm_addr[4] = (u8)(serial_num >> 8);
perm_addr[5] = (u8)(serial_num);
- for (i = 0; i < ETH_ALEN; i++) {
- hw->mac.perm_addr[i] = perm_addr[i];
- hw->mac.addr[i] = perm_addr[i];
- }
+ ether_addr_copy(hw->mac.perm_addr, perm_addr);
+ ether_addr_copy(hw->mac.addr, perm_addr);
return 0;
}
@@ -325,7 +329,7 @@ static s32 fm10k_update_xc_addr_pf(struct fm10k_hw *hw, u16 glort,
/* clear set bit from VLAN ID */
vid &= ~FM10K_VLAN_CLEAR;
- /* if glort or vlan are not valid return error */
+ /* if glort or VLAN are not valid return error */
if (!fm10k_glort_valid_pf(hw, glort) || vid >= FM10K_VLAN_TABLE_VID_MAX)
return FM10K_ERR_PARAM;
@@ -334,8 +338,8 @@ static s32 fm10k_update_xc_addr_pf(struct fm10k_hw *hw, u16 glort,
((u32)mac[3] << 16) |
((u32)mac[4] << 8) |
((u32)mac[5]));
- mac_update.mac_upper = cpu_to_le16(((u32)mac[0] << 8) |
- ((u32)mac[1]));
+ mac_update.mac_upper = cpu_to_le16(((u16)mac[0] << 8) |
+ ((u16)mac[1]));
mac_update.vlan = cpu_to_le16(vid);
mac_update.glort = cpu_to_le16(glort);
mac_update.action = add ? 0 : 1;
@@ -410,6 +414,7 @@ static s32 fm10k_update_xcast_mode_pf(struct fm10k_hw *hw, u16 glort, u8 mode)
if (mode > FM10K_XCAST_MODE_NONE)
return FM10K_ERR_PARAM;
+
/* if glort is not valid return error */
if (!fm10k_glort_valid_pf(hw, glort))
return FM10K_ERR_PARAM;
@@ -903,6 +908,13 @@ static s32 fm10k_iov_assign_default_mac_vlan_pf(struct fm10k_hw *hw,
fm10k_write_reg(hw, FM10K_TDBAL(vf_q_idx), tdbal);
fm10k_write_reg(hw, FM10K_TDBAH(vf_q_idx), tdbah);
+ /* Provide the VF the ITR scale, using software-defined fields in TDLEN
+ * to pass the information during VF initialization. See definition of
+ * FM10K_TDLEN_ITR_SCALE_SHIFT for more details.
+ */
+ fm10k_write_reg(hw, FM10K_TDLEN(vf_q_idx), hw->mac.itr_scale <<
+ FM10K_TDLEN_ITR_SCALE_SHIFT);
+
err_out:
/* configure Queue control register */
txqctl = ((u32)vf_vid << FM10K_TXQCTL_VID_SHIFT) &
@@ -910,7 +922,7 @@ err_out:
txqctl |= (vf_idx << FM10K_TXQCTL_TC_SHIFT) |
FM10K_TXQCTL_VF | vf_idx;
- /* assign VID */
+ /* assign VLAN ID */
for (i = 0; i < queues_per_pool; i++)
fm10k_write_reg(hw, FM10K_TXQCTL(vf_q_idx + i), txqctl);
@@ -1035,6 +1047,12 @@ static s32 fm10k_iov_reset_resources_pf(struct fm10k_hw *hw,
for (i = queues_per_pool; i--;) {
fm10k_write_reg(hw, FM10K_TDBAL(vf_q_idx + i), tdbal);
fm10k_write_reg(hw, FM10K_TDBAH(vf_q_idx + i), tdbah);
+ /* See definition of FM10K_TDLEN_ITR_SCALE_SHIFT for an
+ * explanation of how TDLEN is used.
+ */
+ fm10k_write_reg(hw, FM10K_TDLEN(vf_q_idx + i),
+ hw->mac.itr_scale <<
+ FM10K_TDLEN_ITR_SCALE_SHIFT);
fm10k_write_reg(hw, FM10K_TQMAP(qmap_idx + i), vf_q_idx + i);
fm10k_write_reg(hw, FM10K_RQMAP(qmap_idx + i), vf_q_idx + i);
}
@@ -1155,14 +1173,14 @@ s32 fm10k_iov_msg_msix_pf(struct fm10k_hw *hw, u32 **results,
}
/**
- * fm10k_iov_select_vid - Select correct default VID
+ * fm10k_iov_select_vid - Select correct default VLAN ID
* @hw: Pointer to hardware structure
- * @vid: VID to correct
+ * @vid: VLAN ID to correct
*
- * Will report an error if VID is out of range. For VID = 0, it will return
- * either the pf_vid or sw_vid depending on which one is set.
+ * Will report an error if the VLAN ID is out of range. For VID = 0, it will
+ * return either the pf_vid or sw_vid depending on which one is set.
*/
-static inline s32 fm10k_iov_select_vid(struct fm10k_vf_info *vf_info, u16 vid)
+static s32 fm10k_iov_select_vid(struct fm10k_vf_info *vf_info, u16 vid)
{
if (!vid)
return vf_info->pf_vid ? vf_info->pf_vid : vf_info->sw_vid;
@@ -1212,11 +1230,11 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results,
set = !(vid & FM10K_VLAN_CLEAR);
vid &= ~FM10K_VLAN_CLEAR;
- err = fm10k_iov_select_vid(vf_info, vid);
+ err = fm10k_iov_select_vid(vf_info, (u16)vid);
if (err < 0)
return err;
- else
- vid = err;
+
+ vid = err;
/* update VSI info for VF in regards to VLAN table */
err = hw->mac.ops.update_vlan(hw, vid, vf_info->vsi, set);
@@ -1232,7 +1250,7 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results,
/* block attempts to set MAC for a locked device */
if (is_valid_ether_addr(vf_info->mac) &&
- memcmp(mac, vf_info->mac, ETH_ALEN))
+ !ether_addr_equal(mac, vf_info->mac))
return FM10K_ERR_PARAM;
set = !(vlan & FM10K_VLAN_CLEAR);
@@ -1241,8 +1259,8 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results,
err = fm10k_iov_select_vid(vf_info, vlan);
if (err < 0)
return err;
- else
- vlan = err;
+
+ vlan = (u16)err;
/* notify switch of request for new unicast address */
err = hw->mac.ops.update_uc_addr(hw, vf_info->glort,
@@ -1267,8 +1285,8 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *hw, u32 **results,
err = fm10k_iov_select_vid(vf_info, vlan);
if (err < 0)
return err;
- else
- vlan = err;
+
+ vlan = (u16)err;
/* notify switch of request for new multicast address */
err = hw->mac.ops.update_mc_addr(hw, vf_info->glort,
@@ -1396,14 +1414,6 @@ s32 fm10k_iov_msg_lport_state_pf(struct fm10k_hw *hw, u32 **results,
return err;
}
-const struct fm10k_msg_data fm10k_iov_msg_data_pf[] = {
- FM10K_TLV_MSG_TEST_HANDLER(fm10k_tlv_msg_test),
- FM10K_VF_MSG_MSIX_HANDLER(fm10k_iov_msg_msix_pf),
- FM10K_VF_MSG_MAC_VLAN_HANDLER(fm10k_iov_msg_mac_vlan_pf),
- FM10K_VF_MSG_LPORT_STATE_HANDLER(fm10k_iov_msg_lport_state_pf),
- FM10K_TLV_MSG_ERROR_HANDLER(fm10k_tlv_msg_error),
-};
-
/**
* fm10k_update_stats_hw_pf - Updates hardware related statistics of PF
* @hw: pointer to hardware structure
@@ -1431,9 +1441,10 @@ static void fm10k_update_hw_stats_pf(struct fm10k_hw *hw,
xec = fm10k_read_hw_stats_32b(hw, FM10K_STATS_XEC, &stats->xec);
vlan_drop = fm10k_read_hw_stats_32b(hw, FM10K_STATS_VLAN_DROP,
&stats->vlan_drop);
- loopback_drop = fm10k_read_hw_stats_32b(hw,
- FM10K_STATS_LOOPBACK_DROP,
- &stats->loopback_drop);
+ loopback_drop =
+ fm10k_read_hw_stats_32b(hw,
+ FM10K_STATS_LOOPBACK_DROP,
+ &stats->loopback_drop);
nodesc_drop = fm10k_read_hw_stats_32b(hw,
FM10K_STATS_NODESC_DROP,
&stats->nodesc_drop);
@@ -1678,8 +1689,8 @@ const struct fm10k_tlv_attr fm10k_update_pvid_msg_attr[] = {
*
* This handler configures the default VLAN for the PF
**/
-s32 fm10k_msg_update_pvid_pf(struct fm10k_hw *hw, u32 **results,
- struct fm10k_mbx_info *mbx)
+static s32 fm10k_msg_update_pvid_pf(struct fm10k_hw *hw, u32 **results,
+ struct fm10k_mbx_info *mbx)
{
u16 glort, pvid;
u32 pvid_update;
@@ -1698,7 +1709,7 @@ s32 fm10k_msg_update_pvid_pf(struct fm10k_hw *hw, u32 **results,
if (!fm10k_glort_valid_pf(hw, glort))
return FM10K_ERR_PARAM;
- /* verify VID is valid */
+ /* verify VLAN ID is valid */
if (pvid >= FM10K_VLAN_TABLE_VID_MAX)
return FM10K_ERR_PARAM;
@@ -1855,39 +1866,39 @@ static const struct fm10k_msg_data fm10k_msg_data_pf[] = {
FM10K_TLV_MSG_ERROR_HANDLER(fm10k_tlv_msg_error),
};
-static struct fm10k_mac_ops mac_ops_pf = {
- .get_bus_info = &fm10k_get_bus_info_generic,
- .reset_hw = &fm10k_reset_hw_pf,
- .init_hw = &fm10k_init_hw_pf,
- .start_hw = &fm10k_start_hw_generic,
- .stop_hw = &fm10k_stop_hw_generic,
- .update_vlan = &fm10k_update_vlan_pf,
- .read_mac_addr = &fm10k_read_mac_addr_pf,
- .update_uc_addr = &fm10k_update_uc_addr_pf,
- .update_mc_addr = &fm10k_update_mc_addr_pf,
- .update_xcast_mode = &fm10k_update_xcast_mode_pf,
- .update_int_moderator = &fm10k_update_int_moderator_pf,
- .update_lport_state = &fm10k_update_lport_state_pf,
- .update_hw_stats = &fm10k_update_hw_stats_pf,
- .rebind_hw_stats = &fm10k_rebind_hw_stats_pf,
- .configure_dglort_map = &fm10k_configure_dglort_map_pf,
- .set_dma_mask = &fm10k_set_dma_mask_pf,
- .get_fault = &fm10k_get_fault_pf,
- .get_host_state = &fm10k_get_host_state_pf,
- .adjust_systime = &fm10k_adjust_systime_pf,
- .read_systime = &fm10k_read_systime_pf,
+static const struct fm10k_mac_ops mac_ops_pf = {
+ .get_bus_info = fm10k_get_bus_info_generic,
+ .reset_hw = fm10k_reset_hw_pf,
+ .init_hw = fm10k_init_hw_pf,
+ .start_hw = fm10k_start_hw_generic,
+ .stop_hw = fm10k_stop_hw_generic,
+ .update_vlan = fm10k_update_vlan_pf,
+ .read_mac_addr = fm10k_read_mac_addr_pf,
+ .update_uc_addr = fm10k_update_uc_addr_pf,
+ .update_mc_addr = fm10k_update_mc_addr_pf,
+ .update_xcast_mode = fm10k_update_xcast_mode_pf,
+ .update_int_moderator = fm10k_update_int_moderator_pf,
+ .update_lport_state = fm10k_update_lport_state_pf,
+ .update_hw_stats = fm10k_update_hw_stats_pf,
+ .rebind_hw_stats = fm10k_rebind_hw_stats_pf,
+ .configure_dglort_map = fm10k_configure_dglort_map_pf,
+ .set_dma_mask = fm10k_set_dma_mask_pf,
+ .get_fault = fm10k_get_fault_pf,
+ .get_host_state = fm10k_get_host_state_pf,
+ .adjust_systime = fm10k_adjust_systime_pf,
+ .read_systime = fm10k_read_systime_pf,
};
-static struct fm10k_iov_ops iov_ops_pf = {
- .assign_resources = &fm10k_iov_assign_resources_pf,
- .configure_tc = &fm10k_iov_configure_tc_pf,
- .assign_int_moderator = &fm10k_iov_assign_int_moderator_pf,
+static const struct fm10k_iov_ops iov_ops_pf = {
+ .assign_resources = fm10k_iov_assign_resources_pf,
+ .configure_tc = fm10k_iov_configure_tc_pf,
+ .assign_int_moderator = fm10k_iov_assign_int_moderator_pf,
.assign_default_mac_vlan = fm10k_iov_assign_default_mac_vlan_pf,
- .reset_resources = &fm10k_iov_reset_resources_pf,
- .set_lport = &fm10k_iov_set_lport_pf,
- .reset_lport = &fm10k_iov_reset_lport_pf,
- .update_stats = &fm10k_iov_update_stats_pf,
- .report_timestamp = &fm10k_iov_report_timestamp_pf,
+ .reset_resources = fm10k_iov_reset_resources_pf,
+ .set_lport = fm10k_iov_set_lport_pf,
+ .reset_lport = fm10k_iov_reset_lport_pf,
+ .update_stats = fm10k_iov_update_stats_pf,
+ .report_timestamp = fm10k_iov_report_timestamp_pf,
};
static s32 fm10k_get_invariants_pf(struct fm10k_hw *hw)
@@ -1897,9 +1908,9 @@ static s32 fm10k_get_invariants_pf(struct fm10k_hw *hw)
return fm10k_sm_mbx_init(hw, &hw->mbx, fm10k_msg_data_pf);
}
-struct fm10k_info fm10k_pf_info = {
+const struct fm10k_info fm10k_pf_info = {
.mac = fm10k_mac_pf,
- .get_invariants = &fm10k_get_invariants_pf,
+ .get_invariants = fm10k_get_invariants_pf,
.mac_ops = &mac_ops_pf,
.iov_ops = &iov_ops_pf,
};
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.h b/drivers/net/ethernet/intel/fm10k/fm10k_pf.h
index 40a0dbc62a04..b2d96b45ca3c 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.h
@@ -1,5 +1,5 @@
/* Intel Ethernet Switch Host Interface Driver
- * Copyright(c) 2013 - 2014 Intel Corporation.
+ * 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,
@@ -74,6 +74,11 @@ enum fm10k_pf_tlv_attr_id_v1 {
#define FM10K_MSG_UPDATE_PVID_PVID_SHIFT 16
#define FM10K_MSG_UPDATE_PVID_PVID_SIZE 16
+/* The following data structures are overlayed directly onto TLV mailbox
+ * messages, and must not break 4 byte alignment. Ensure the structures line
+ * up correctly as per their TLV definition.
+ */
+
struct fm10k_mac_update {
__le32 mac_lower;
__le16 mac_upper;
@@ -81,34 +86,32 @@ struct fm10k_mac_update {
__le16 glort;
u8 flags;
u8 action;
-} __packed;
+} __aligned(4) __packed;
struct fm10k_global_table_data {
__le32 used;
__le32 avail;
-} __packed;
+} __aligned(4) __packed;
struct fm10k_swapi_error {
__le32 status;
struct fm10k_global_table_data mac;
struct fm10k_global_table_data nexthop;
struct fm10k_global_table_data ffu;
-} __packed;
+} __aligned(4) __packed;
struct fm10k_swapi_1588_timestamp {
__le64 egress;
__le64 ingress;
__le16 dglort;
__le16 sglort;
-} __packed;
+} __aligned(4) __packed;
s32 fm10k_msg_lport_map_pf(struct fm10k_hw *, u32 **, struct fm10k_mbx_info *);
extern const struct fm10k_tlv_attr fm10k_lport_map_msg_attr[];
#define FM10K_PF_MSG_LPORT_MAP_HANDLER(func) \
FM10K_MSG_HANDLER(FM10K_PF_MSG_ID_LPORT_MAP, \
fm10k_lport_map_msg_attr, func)
-s32 fm10k_msg_update_pvid_pf(struct fm10k_hw *, u32 **,
- struct fm10k_mbx_info *);
extern const struct fm10k_tlv_attr fm10k_update_pvid_msg_attr[];
#define FM10K_PF_MSG_UPDATE_PVID_HANDLER(func) \
FM10K_MSG_HANDLER(FM10K_PF_MSG_ID_UPDATE_PVID, \
@@ -129,7 +132,6 @@ s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *, u32 **,
struct fm10k_mbx_info *);
s32 fm10k_iov_msg_lport_state_pf(struct fm10k_hw *, u32 **,
struct fm10k_mbx_info *);
-extern const struct fm10k_msg_data fm10k_iov_msg_data_pf[];
-extern struct fm10k_info fm10k_pf_info;
+extern const struct fm10k_info fm10k_pf_info;
#endif /* _FM10K_PF_H */
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c b/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c
index 9b29d7b0377a..ab01bb30752f 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_tlv.c
@@ -1,5 +1,5 @@
/* Intel Ethernet Switch Host Interface Driver
- * Copyright(c) 2013 - 2014 Intel Corporation.
+ * 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,
@@ -48,8 +48,8 @@ s32 fm10k_tlv_msg_init(u32 *msg, u16 msg_id)
* the attribute buffer. It will return success if provided with a valid
* pointers.
**/
-s32 fm10k_tlv_attr_put_null_string(u32 *msg, u16 attr_id,
- const unsigned char *string)
+static s32 fm10k_tlv_attr_put_null_string(u32 *msg, u16 attr_id,
+ const unsigned char *string)
{
u32 attr_data = 0, len = 0;
u32 *attr;
@@ -98,7 +98,7 @@ s32 fm10k_tlv_attr_put_null_string(u32 *msg, u16 attr_id,
* it in the array pointed by by string. It will return success if provided
* with a valid pointers.
**/
-s32 fm10k_tlv_attr_get_null_string(u32 *attr, unsigned char *string)
+static s32 fm10k_tlv_attr_get_null_string(u32 *attr, unsigned char *string)
{
u32 len;
@@ -353,7 +353,7 @@ s32 fm10k_tlv_attr_get_le_struct(u32 *attr, void *le_struct, u32 len)
* function will return NULL on failure, and a pointer to the start
* of the nested attributes on success.
**/
-u32 *fm10k_tlv_attr_nest_start(u32 *msg, u16 attr_id)
+static u32 *fm10k_tlv_attr_nest_start(u32 *msg, u16 attr_id)
{
u32 *attr;
@@ -370,7 +370,7 @@ u32 *fm10k_tlv_attr_nest_start(u32 *msg, u16 attr_id)
}
/**
- * fm10k_tlv_attr_nest_start - Start a set of nested attributes
+ * fm10k_tlv_attr_nest_stop - Stop a set of nested attributes
* @msg: Pointer to message block
*
* This function closes off an existing set of nested attributes. The
@@ -378,7 +378,7 @@ u32 *fm10k_tlv_attr_nest_start(u32 *msg, u16 attr_id)
* the case of a nest within the nest this would be the outer nest pointer.
* This function will return success provided all pointers are valid.
**/
-s32 fm10k_tlv_attr_nest_stop(u32 *msg)
+static s32 fm10k_tlv_attr_nest_stop(u32 *msg)
{
u32 *attr;
u32 len;
@@ -483,8 +483,8 @@ static s32 fm10k_tlv_attr_validate(u32 *attr,
* FM10K_NOT_IMPLEMENTED for any attribute that is outside of the array
* and 0 on success.
**/
-s32 fm10k_tlv_attr_parse(u32 *attr, u32 **results,
- const struct fm10k_tlv_attr *tlv_attr)
+static s32 fm10k_tlv_attr_parse(u32 *attr, u32 **results,
+ const struct fm10k_tlv_attr *tlv_attr)
{
u32 i, attr_id, offset = 0;
s32 err = 0;
@@ -755,7 +755,7 @@ parse_nested:
err = fm10k_tlv_attr_get_mac_vlan(
results[FM10K_TEST_MSG_MAC_ADDR],
result_mac, &result_vlan);
- if (!err && memcmp(test_mac, result_mac, ETH_ALEN))
+ if (!err && !ether_addr_equal(test_mac, result_mac))
err = FM10K_ERR_INVALID_VALUE;
if (!err && test_vlan != result_vlan)
err = FM10K_ERR_INVALID_VALUE;
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_tlv.h b/drivers/net/ethernet/intel/fm10k/fm10k_tlv.h
index 7e045e8bf1eb..e1845e0a17d8 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_tlv.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_tlv.h
@@ -1,5 +1,5 @@
/* Intel Ethernet Switch Host Interface Driver
- * Copyright(c) 2013 - 2014 Intel Corporation.
+ * 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,
@@ -38,9 +38,9 @@ struct fm10k_msg_data;
* mailbox size we will provide a message with the above header and it
* will be segmented and transported to the mailbox to the other side where
* it is reassembled. It contains the following fields:
- * Len: Length of the message in bytes excluding the message header
+ * Length: Length of the message in bytes excluding the message header
* Flags: TBD
- * Rule: These will be the message/argument types we pass
+ * Type/ID: These will be the message/argument types we pass
*/
/* message data header */
#define FM10K_TLV_ID_SHIFT 0
@@ -106,8 +106,6 @@ struct fm10k_msg_data {
#define FM10K_MSG_HANDLER(id, attr, func) { id, attr, func }
s32 fm10k_tlv_msg_init(u32 *, u16);
-s32 fm10k_tlv_attr_put_null_string(u32 *, u16, const unsigned char *);
-s32 fm10k_tlv_attr_get_null_string(u32 *, unsigned char *);
s32 fm10k_tlv_attr_put_mac_vlan(u32 *, u16, const u8 *, u16);
s32 fm10k_tlv_attr_get_mac_vlan(u32 *, u8 *, u16 *);
s32 fm10k_tlv_attr_put_bool(u32 *, u16);
@@ -147,9 +145,6 @@ s32 fm10k_tlv_attr_get_value(u32 *, void *, u32);
fm10k_tlv_attr_get_value(attr, ptr, sizeof(s64))
s32 fm10k_tlv_attr_put_le_struct(u32 *, u16, const void *, u32);
s32 fm10k_tlv_attr_get_le_struct(u32 *, void *, u32);
-u32 *fm10k_tlv_attr_nest_start(u32 *, u16);
-s32 fm10k_tlv_attr_nest_stop(u32 *);
-s32 fm10k_tlv_attr_parse(u32 *, u32 **, const struct fm10k_tlv_attr *);
s32 fm10k_tlv_msg_parse(struct fm10k_hw *, u32 *, struct fm10k_mbx_info *,
const struct fm10k_msg_data *);
s32 fm10k_tlv_msg_error(struct fm10k_hw *hw, u32 **results,
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_type.h b/drivers/net/ethernet/intel/fm10k/fm10k_type.h
index 318a212f0a78..854ebb1906bf 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_type.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_type.h
@@ -1,5 +1,5 @@
/* Intel Ethernet Switch Host Interface Driver
- * Copyright(c) 2013 - 2014 Intel Corporation.
+ * 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,
@@ -77,6 +77,7 @@ struct fm10k_hw;
#define FM10K_PCIE_SRIOV_CTRL_VFARI 0x10
#define FM10K_ERR_PARAM -2
+#define FM10K_ERR_NO_RESOURCES -3
#define FM10K_ERR_REQUESTS_PENDING -4
#define FM10K_ERR_RESET_REQUESTED -5
#define FM10K_ERR_DMA_PENDING -6
@@ -271,6 +272,20 @@ struct fm10k_hw;
#define FM10K_TDBAL(_n) ((0x40 * (_n)) + 0x8000)
#define FM10K_TDBAH(_n) ((0x40 * (_n)) + 0x8001)
#define FM10K_TDLEN(_n) ((0x40 * (_n)) + 0x8002)
+/* When fist initialized, VFs need to know the Interrupt Throttle Rate (ITR)
+ * scale which is based on the PCIe speed but the speed information in the PCI
+ * configuration space may not be accurate. The PF already knows the ITR scale
+ * but there is no defined method to pass that information from the PF to the
+ * VF. This is accomplished during VF initialization by temporarily co-opting
+ * the yet-to-be-used TDLEN register to have the PF store the ITR shift for
+ * the VF to retrieve before the VF needs to use the TDLEN register for its
+ * intended purpose, i.e. before the Tx resources are allocated.
+ */
+#define FM10K_TDLEN_ITR_SCALE_SHIFT 9
+#define FM10K_TDLEN_ITR_SCALE_MASK 0x00000E00
+#define FM10K_TDLEN_ITR_SCALE_GEN1 2
+#define FM10K_TDLEN_ITR_SCALE_GEN2 1
+#define FM10K_TDLEN_ITR_SCALE_GEN3 0
#define FM10K_TPH_TXCTRL(_n) ((0x40 * (_n)) + 0x8003)
#define FM10K_TPH_TXCTRL_DESC_TPHEN 0x00000020
#define FM10K_TPH_TXCTRL_DESC_RROEN 0x00000200
@@ -339,7 +354,7 @@ struct fm10k_hw;
#define FM10K_VLAN_TABLE_VID_MAX 4096
#define FM10K_VLAN_TABLE_VSI_MAX 64
#define FM10K_VLAN_LENGTH_SHIFT 16
-#define FM10K_VLAN_CLEAR (1 << 15)
+#define FM10K_VLAN_CLEAR BIT(15)
#define FM10K_VLAN_ALL \
((FM10K_VLAN_TABLE_VID_MAX - 1) << FM10K_VLAN_LENGTH_SHIFT)
@@ -373,13 +388,13 @@ struct fm10k_hw;
#define FM10K_SW_SYSTIME_PULSE(_n) ((_n) + 0x02252)
enum fm10k_int_source {
- fm10k_int_Mailbox = 0,
- fm10k_int_PCIeFault = 1,
- fm10k_int_SwitchUpDown = 2,
- fm10k_int_SwitchEvent = 3,
- fm10k_int_SRAM = 4,
- fm10k_int_VFLR = 5,
- fm10k_int_MaxHoldTime = 6,
+ fm10k_int_mailbox = 0,
+ fm10k_int_pcie_fault = 1,
+ fm10k_int_switch_up_down = 2,
+ fm10k_int_switch_event = 3,
+ fm10k_int_sram = 4,
+ fm10k_int_vflr = 5,
+ fm10k_int_max_hold_time = 6,
fm10k_int_sources_max_pf
};
@@ -535,7 +550,6 @@ struct fm10k_mac_ops {
struct fm10k_dglort_cfg *);
void (*set_dma_mask)(struct fm10k_hw *, u64);
s32 (*get_fault)(struct fm10k_hw *, int, struct fm10k_fault *);
- void (*request_lport_map)(struct fm10k_hw *);
s32 (*adjust_systime)(struct fm10k_hw *, s32 ppb);
u64 (*read_systime)(struct fm10k_hw *);
};
@@ -559,6 +573,7 @@ struct fm10k_mac_info {
bool get_host_state;
bool tx_ready;
u32 dglort_map;
+ u8 itr_scale;
};
struct fm10k_swapi_table_info {
@@ -644,10 +659,10 @@ enum fm10k_devices {
};
struct fm10k_info {
- enum fm10k_mac_type mac;
- s32 (*get_invariants)(struct fm10k_hw *);
- struct fm10k_mac_ops *mac_ops;
- struct fm10k_iov_ops *iov_ops;
+ enum fm10k_mac_type mac;
+ s32 (*get_invariants)(struct fm10k_hw *);
+ const struct fm10k_mac_ops *mac_ops;
+ const struct fm10k_iov_ops *iov_ops;
};
struct fm10k_hw {
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c
index 36c8b0aa08fd..91f8d7311f3b 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c
@@ -28,7 +28,7 @@
static s32 fm10k_stop_hw_vf(struct fm10k_hw *hw)
{
u8 *perm_addr = hw->mac.perm_addr;
- u32 bal = 0, bah = 0;
+ u32 bal = 0, bah = 0, tdlen;
s32 err;
u16 i;
@@ -48,6 +48,9 @@ static s32 fm10k_stop_hw_vf(struct fm10k_hw *hw)
((u32)perm_addr[2]);
}
+ /* restore default itr_scale for next VF initialization */
+ tdlen = hw->mac.itr_scale << FM10K_TDLEN_ITR_SCALE_SHIFT;
+
/* The queues have already been disabled so we just need to
* update their base address registers
*/
@@ -56,6 +59,12 @@ static s32 fm10k_stop_hw_vf(struct fm10k_hw *hw)
fm10k_write_reg(hw, FM10K_TDBAH(i), bah);
fm10k_write_reg(hw, FM10K_RDBAL(i), bal);
fm10k_write_reg(hw, FM10K_RDBAH(i), bah);
+ /* Restore ITR scale in software-defined mechanism in TDLEN
+ * for next VF initialization. See definition of
+ * FM10K_TDLEN_ITR_SCALE_SHIFT for more details on the use of
+ * TDLEN here.
+ */
+ fm10k_write_reg(hw, FM10K_TDLEN(i), tdlen);
}
return 0;
@@ -103,7 +112,14 @@ static s32 fm10k_init_hw_vf(struct fm10k_hw *hw)
s32 err;
u16 i;
- /* assume we always have at least 1 queue */
+ /* verify we have at least 1 queue */
+ if (!~fm10k_read_reg(hw, FM10K_TXQCTL(0)) ||
+ !~fm10k_read_reg(hw, FM10K_RXQCTL(0))) {
+ err = FM10K_ERR_NO_RESOURCES;
+ goto reset_max_queues;
+ }
+
+ /* determine how many queues we have */
for (i = 1; tqdloc0 && (i < FM10K_MAX_QUEUES_POOL); i++) {
/* verify the Descriptor cache offsets are increasing */
tqdloc = ~fm10k_read_reg(hw, FM10K_TQDLOC(i));
@@ -119,16 +135,28 @@ static s32 fm10k_init_hw_vf(struct fm10k_hw *hw)
/* shut down queues we own and reset DMA configuration */
err = fm10k_disable_queues_generic(hw, i);
if (err)
- return err;
+ goto reset_max_queues;
/* record maximum queue count */
hw->mac.max_queues = i;
- /* fetch default VLAN */
+ /* fetch default VLAN and ITR scale */
hw->mac.default_vid = (fm10k_read_reg(hw, FM10K_TXQCTL(0)) &
FM10K_TXQCTL_VID_MASK) >> FM10K_TXQCTL_VID_SHIFT;
+ /* Read the ITR scale from TDLEN. See the definition of
+ * FM10K_TDLEN_ITR_SCALE_SHIFT for more information about how TDLEN is
+ * used here.
+ */
+ hw->mac.itr_scale = (fm10k_read_reg(hw, FM10K_TDLEN(0)) &
+ FM10K_TDLEN_ITR_SCALE_MASK) >>
+ FM10K_TDLEN_ITR_SCALE_SHIFT;
return 0;
+
+reset_max_queues:
+ hw->mac.max_queues = 0;
+
+ return err;
}
/* This structure defines the attibutes to be parsed below */
@@ -270,7 +298,7 @@ static s32 fm10k_update_uc_addr_vf(struct fm10k_hw *hw, u16 glort,
/* verify we are not locked down on the MAC address */
if (is_valid_ether_addr(hw->mac.perm_addr) &&
- memcmp(hw->mac.perm_addr, mac, ETH_ALEN))
+ !ether_addr_equal(hw->mac.perm_addr, mac))
return FM10K_ERR_PARAM;
/* add bit to notify us if this is a set or clear operation */
@@ -414,6 +442,7 @@ static s32 fm10k_update_xcast_mode_vf(struct fm10k_hw *hw, u16 glort, u8 mode)
if (mode > FM10K_XCAST_MODE_NONE)
return FM10K_ERR_PARAM;
+
/* generate message requesting to change xcast mode */
fm10k_tlv_msg_init(msg, FM10K_VF_MSG_ID_LPORT_STATE);
fm10k_tlv_attr_put_u8(msg, FM10K_LPORT_STATE_MSG_XCAST_MODE, mode);
@@ -533,25 +562,25 @@ static const struct fm10k_msg_data fm10k_msg_data_vf[] = {
FM10K_TLV_MSG_ERROR_HANDLER(fm10k_tlv_msg_error),
};
-static struct fm10k_mac_ops mac_ops_vf = {
- .get_bus_info = &fm10k_get_bus_info_generic,
- .reset_hw = &fm10k_reset_hw_vf,
- .init_hw = &fm10k_init_hw_vf,
- .start_hw = &fm10k_start_hw_generic,
- .stop_hw = &fm10k_stop_hw_vf,
- .update_vlan = &fm10k_update_vlan_vf,
- .read_mac_addr = &fm10k_read_mac_addr_vf,
- .update_uc_addr = &fm10k_update_uc_addr_vf,
- .update_mc_addr = &fm10k_update_mc_addr_vf,
- .update_xcast_mode = &fm10k_update_xcast_mode_vf,
- .update_int_moderator = &fm10k_update_int_moderator_vf,
- .update_lport_state = &fm10k_update_lport_state_vf,
- .update_hw_stats = &fm10k_update_hw_stats_vf,
- .rebind_hw_stats = &fm10k_rebind_hw_stats_vf,
- .configure_dglort_map = &fm10k_configure_dglort_map_vf,
- .get_host_state = &fm10k_get_host_state_generic,
- .adjust_systime = &fm10k_adjust_systime_vf,
- .read_systime = &fm10k_read_systime_vf,
+static const struct fm10k_mac_ops mac_ops_vf = {
+ .get_bus_info = fm10k_get_bus_info_generic,
+ .reset_hw = fm10k_reset_hw_vf,
+ .init_hw = fm10k_init_hw_vf,
+ .start_hw = fm10k_start_hw_generic,
+ .stop_hw = fm10k_stop_hw_vf,
+ .update_vlan = fm10k_update_vlan_vf,
+ .read_mac_addr = fm10k_read_mac_addr_vf,
+ .update_uc_addr = fm10k_update_uc_addr_vf,
+ .update_mc_addr = fm10k_update_mc_addr_vf,
+ .update_xcast_mode = fm10k_update_xcast_mode_vf,
+ .update_int_moderator = fm10k_update_int_moderator_vf,
+ .update_lport_state = fm10k_update_lport_state_vf,
+ .update_hw_stats = fm10k_update_hw_stats_vf,
+ .rebind_hw_stats = fm10k_rebind_hw_stats_vf,
+ .configure_dglort_map = fm10k_configure_dglort_map_vf,
+ .get_host_state = fm10k_get_host_state_generic,
+ .adjust_systime = fm10k_adjust_systime_vf,
+ .read_systime = fm10k_read_systime_vf,
};
static s32 fm10k_get_invariants_vf(struct fm10k_hw *hw)
@@ -561,8 +590,8 @@ static s32 fm10k_get_invariants_vf(struct fm10k_hw *hw)
return fm10k_pfvf_mbx_init(hw, &hw->mbx, fm10k_msg_data_vf, 0);
}
-struct fm10k_info fm10k_vf_info = {
+const struct fm10k_info fm10k_vf_info = {
.mac = fm10k_mac_vf,
- .get_invariants = &fm10k_get_invariants_vf,
+ .get_invariants = fm10k_get_invariants_vf,
.mac_ops = &mac_ops_vf,
};
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_vf.h b/drivers/net/ethernet/intel/fm10k/fm10k_vf.h
index 06a99d794c99..c4439f1313a0 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_vf.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_vf.h
@@ -74,5 +74,5 @@ extern const struct fm10k_tlv_attr fm10k_1588_msg_attr[];
#define FM10K_VF_MSG_1588_HANDLER(func) \
FM10K_MSG_HANDLER(FM10K_VF_MSG_ID_1588, fm10k_1588_msg_attr, func)
-extern struct fm10k_info fm10k_vf_info;
+extern const struct fm10k_info fm10k_vf_info;
#endif /* _FM10K_VF_H */
diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 4dd3e26129b4..68f2204ec6f3 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -42,7 +42,6 @@
#include <linux/string.h>
#include <linux/in.h>
#include <linux/ip.h>
-#include <linux/tcp.h>
#include <linux/sctp.h>
#include <linux/pkt_sched.h>
#include <linux/ipv6.h>
@@ -104,6 +103,7 @@
#define I40E_PRIV_FLAGS_LINKPOLL_FLAG BIT(1)
#define I40E_PRIV_FLAGS_FD_ATR BIT(2)
#define I40E_PRIV_FLAGS_VEB_STATS BIT(3)
+#define I40E_PRIV_FLAGS_PS BIT(4)
#define I40E_NVM_VERSION_LO_SHIFT 0
#define I40E_NVM_VERSION_LO_MASK (0xff << I40E_NVM_VERSION_LO_SHIFT)
@@ -187,6 +187,7 @@ struct i40e_lump_tracking {
#define I40E_FDIR_BUFFER_HEAD_ROOM_FOR_ATR (I40E_FDIR_BUFFER_HEAD_ROOM * 4)
#define I40E_HKEY_ARRAY_SIZE ((I40E_PFQF_HKEY_MAX_INDEX + 1) * 4)
+#define I40E_HLUT_ARRAY_SIZE ((I40E_PFQF_HLUT_MAX_INDEX + 1) * 4)
enum i40e_fd_stat_idx {
I40E_FD_STAT_ATR,
@@ -244,6 +245,11 @@ struct i40e_tc_configuration {
struct i40e_tc_info tc_info[I40E_MAX_TRAFFIC_CLASS];
};
+struct i40e_udp_port_config {
+ __be16 index;
+ u8 type;
+};
+
/* struct that defines the Ethernet device */
struct i40e_pf {
struct pci_dev *pdev;
@@ -265,7 +271,7 @@ struct i40e_pf {
u16 num_lan_qps; /* num lan queues this PF has set up */
u16 num_lan_msix; /* num queue vectors for the base PF vsi */
int queues_left; /* queues left unclaimed */
- u16 rss_size; /* num queues in the RSS array */
+ u16 alloc_rss_size; /* allocated RSS queues */
u16 rss_size_max; /* HW defined max RSS queues */
u16 fdir_pf_filter_count; /* num of guaranteed filters for this PF */
u16 num_alloc_vsi; /* num VSIs this driver supports */
@@ -280,11 +286,9 @@ struct i40e_pf {
u32 fd_atr_cnt;
u32 fd_tcp_rule;
-#ifdef CONFIG_I40E_VXLAN
- __be16 vxlan_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS];
- u16 pending_vxlan_bitmap;
+ struct i40e_udp_port_config udp_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS];
+ u16 pending_udp_bitmap;
-#endif
enum i40e_interrupt_policy int_policy;
u16 rx_itr_default;
u16 tx_itr_default;
@@ -321,9 +325,7 @@ struct i40e_pf {
#define I40E_FLAG_FD_ATR_ENABLED BIT_ULL(22)
#define I40E_FLAG_PTP BIT_ULL(25)
#define I40E_FLAG_MFP_ENABLED BIT_ULL(26)
-#ifdef CONFIG_I40E_VXLAN
-#define I40E_FLAG_VXLAN_FILTER_SYNC BIT_ULL(27)
-#endif
+#define I40E_FLAG_UDP_FILTER_SYNC BIT_ULL(27)
#define I40E_FLAG_PORT_ID_VALID BIT_ULL(28)
#define I40E_FLAG_DCB_CAPABLE BIT_ULL(29)
#define I40E_FLAG_RSS_AQ_CAPABLE BIT_ULL(31)
@@ -335,7 +337,9 @@ struct i40e_pf {
#define I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE BIT_ULL(38)
#define I40E_FLAG_LINK_POLLING_ENABLED BIT_ULL(39)
#define I40E_FLAG_VEB_MODE_ENABLED BIT_ULL(40)
+#define I40E_FLAG_GENEVE_OFFLOAD_CAPABLE BIT_ULL(41)
#define I40E_FLAG_NO_PCI_LINK_CHECK BIT_ULL(42)
+#define I40E_FLAG_PF_MAC BIT_ULL(50)
/* tracks features that get auto disabled by errors */
u64 auto_disable_flags;
@@ -412,7 +416,7 @@ struct i40e_pf {
u32 rx_hwtstamp_cleared;
bool ptp_tx;
bool ptp_rx;
- u16 rss_table_size;
+ u16 rss_table_size; /* HW RSS table size */
/* These are only valid in NPAR modes */
u32 npar_max_bw;
u32 npar_min_bw;
@@ -487,6 +491,7 @@ struct i40e_vsi {
u32 tx_restart;
u32 tx_busy;
u64 tx_linearize;
+ u64 tx_force_wb;
u32 rx_buf_failed;
u32 rx_page_failed;
@@ -504,8 +509,10 @@ struct i40e_vsi {
u16 tx_itr_setting;
u16 int_rate_limit; /* value in usecs */
- u16 rss_table_size;
- u16 rss_size;
+ u16 rss_table_size; /* HW RSS table size */
+ u16 rss_size; /* Allocated RSS queues */
+ u8 *rss_hkey_user; /* User configured hash keys */
+ u8 *rss_lut_user; /* User configured lookup table entries */
u16 max_frame;
u16 rx_hdr_len;
@@ -575,6 +582,9 @@ struct i40e_q_vector {
u8 num_ringpairs; /* total number of ring pairs in vector */
+#define I40E_Q_VECTOR_HUNG_DETECT 0 /* Bit Index for hung detection logic */
+ unsigned long hung_detected; /* Set/Reset for hung_detection logic */
+
cpumask_t affinity_mask;
struct rcu_head rcu; /* to avoid race with update stats on free */
char name[I40E_INT_NAME_STR_LEN];
@@ -602,8 +612,8 @@ static inline char *i40e_nvm_version_str(struct i40e_hw *hw)
full_ver = hw->nvm.oem_ver;
ver = (u8)(full_ver >> I40E_OEM_VER_SHIFT);
- build = (u16)((full_ver >> I40E_OEM_VER_BUILD_SHIFT)
- & I40E_OEM_VER_BUILD_MASK);
+ build = (u16)((full_ver >> I40E_OEM_VER_BUILD_SHIFT) &
+ I40E_OEM_VER_BUILD_MASK);
patch = (u8)(full_ver & I40E_OEM_VER_PATCH_MASK);
snprintf(buf, sizeof(buf),
@@ -668,6 +678,8 @@ extern const char i40e_driver_name[];
extern const char i40e_driver_version_str[];
void i40e_do_reset_safe(struct i40e_pf *pf, u32 reset_flags);
void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags);
+int i40e_config_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size);
+int i40e_get_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size);
struct i40e_vsi *i40e_find_vsi_from_id(struct i40e_pf *pf, u16 id);
void i40e_update_stats(struct i40e_vsi *vsi);
void i40e_update_eth_stats(struct i40e_vsi *vsi);
@@ -691,7 +703,7 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi,
bool is_vf, bool is_netdev);
void i40e_del_filter(struct i40e_vsi *vsi, u8 *macaddr, s16 vlan,
bool is_vf, bool is_netdev);
-int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl);
+int i40e_sync_vsi_filters(struct i40e_vsi *vsi);
struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type,
u16 uplink, u32 param1);
int i40e_vsi_release(struct i40e_vsi *vsi);
@@ -709,7 +721,7 @@ struct i40e_veb *i40e_veb_setup(struct i40e_pf *pf, u16 flags, u16 uplink_seid,
void i40e_veb_release(struct i40e_veb *veb);
int i40e_veb_config_tc(struct i40e_veb *veb, u8 enabled_tc);
-i40e_status i40e_vsi_add_pvid(struct i40e_vsi *vsi, u16 vid);
+int i40e_vsi_add_pvid(struct i40e_vsi *vsi, u16 vid);
void i40e_vsi_remove_pvid(struct i40e_vsi *vsi);
void i40e_vsi_reset_stats(struct i40e_vsi *vsi);
void i40e_pf_reset_stats(struct i40e_pf *pf);
@@ -767,6 +779,8 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid);
int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid);
struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, u8 *macaddr,
bool is_vf, bool is_netdev);
+int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, u8 *macaddr,
+ bool is_vf, bool is_netdev);
bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi);
struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, u8 *macaddr,
bool is_vf, bool is_netdev);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.c b/drivers/net/ethernet/intel/i40e/i40e_adminq.c
index 0ff8f01e57ee..1fd5ea82a9bc 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_adminq.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.c
@@ -567,10 +567,6 @@ i40e_status i40e_init_adminq(struct i40e_hw *hw)
goto init_adminq_exit;
}
- /* initialize locks */
- mutex_init(&hw->aq.asq_mutex);
- mutex_init(&hw->aq.arq_mutex);
-
/* Set up register offsets */
i40e_adminq_init_regs(hw);
@@ -664,8 +660,6 @@ i40e_status i40e_shutdown_adminq(struct i40e_hw *hw)
i40e_shutdown_asq(hw);
i40e_shutdown_arq(hw);
- /* destroy the locks */
-
if (hw->nvm_buff.va)
i40e_free_virt_mem(hw, &hw->nvm_buff);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
index 6584b6cd73fd..b22012a446a6 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
@@ -227,6 +227,7 @@ enum i40e_admin_queue_opc {
i40e_aqc_opc_nvm_update = 0x0703,
i40e_aqc_opc_nvm_config_read = 0x0704,
i40e_aqc_opc_nvm_config_write = 0x0705,
+ i40e_aqc_opc_oem_post_update = 0x0720,
/* virtualization commands */
i40e_aqc_opc_send_msg_to_pf = 0x0801,
@@ -1891,6 +1892,26 @@ struct i40e_aqc_nvm_config_data_immediate_field {
I40E_CHECK_STRUCT_LEN(0xc, i40e_aqc_nvm_config_data_immediate_field);
+/* OEM Post Update (indirect 0x0720)
+ * no command data struct used
+ */
+struct i40e_aqc_nvm_oem_post_update {
+#define I40E_AQ_NVM_OEM_POST_UPDATE_EXTERNAL_DATA 0x01
+ u8 sel_data;
+ u8 reserved[7];
+};
+
+I40E_CHECK_STRUCT_LEN(0x8, i40e_aqc_nvm_oem_post_update);
+
+struct i40e_aqc_nvm_oem_post_update_buffer {
+ u8 str_len;
+ u8 dev_addr;
+ __le16 eeprom_addr;
+ u8 data[36];
+};
+
+I40E_CHECK_STRUCT_LEN(0x28, i40e_aqc_nvm_oem_post_update_buffer);
+
/* Send to PF command (indirect 0x0801) id is only used by PF
* Send to VF command (indirect 0x0802) id is only used by PF
* Send to Peer PF command (indirect 0x0803)
@@ -2403,4 +2424,4 @@ struct i40e_aqc_debug_modify_internals {
I40E_CHECK_CMD_LENGTH(i40e_aqc_debug_modify_internals);
-#endif
+#endif /* _I40E_ADMINQ_CMD_H_ */
diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
index 2d74c6e4d7b6..6a034ddac36a 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_common.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
@@ -44,7 +44,6 @@ static i40e_status i40e_set_mac_type(struct i40e_hw *hw)
switch (hw->device_id) {
case I40E_DEV_ID_SFP_XL710:
case I40E_DEV_ID_QEMU:
- case I40E_DEV_ID_KX_A:
case I40E_DEV_ID_KX_B:
case I40E_DEV_ID_KX_C:
case I40E_DEV_ID_QSFP_A:
diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
index d4b7af9a2fc8..10744a698d6f 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
@@ -103,8 +103,8 @@ static ssize_t i40e_dbg_dump_read(struct file *filp, char __user *buffer,
len = min_t(int, count, (i40e_dbg_dump_data_len - *ppos));
bytes_not_copied = copy_to_user(buffer, &i40e_dbg_dump_buf[*ppos], len);
- if (bytes_not_copied < 0)
- return bytes_not_copied;
+ if (bytes_not_copied)
+ return -EFAULT;
*ppos += len;
return len;
@@ -353,8 +353,8 @@ static ssize_t i40e_dbg_command_read(struct file *filp, char __user *buffer,
bytes_not_copied = copy_to_user(buffer, buf, len);
kfree(buf);
- if (bytes_not_copied < 0)
- return bytes_not_copied;
+ if (bytes_not_copied)
+ return -EFAULT;
*ppos = len;
return len;
@@ -981,12 +981,10 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
if (!cmd_buf)
return count;
bytes_not_copied = copy_from_user(cmd_buf, buffer, count);
- if (bytes_not_copied < 0) {
+ if (bytes_not_copied) {
kfree(cmd_buf);
- return bytes_not_copied;
+ return -EFAULT;
}
- if (bytes_not_copied > 0)
- count -= bytes_not_copied;
cmd_buf[count] = '\0';
cmd_buf_tmp = strchr(cmd_buf, '\n');
@@ -1140,7 +1138,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
spin_lock_bh(&vsi->mac_filter_list_lock);
f = i40e_add_filter(vsi, ma, vlan, false, false);
spin_unlock_bh(&vsi->mac_filter_list_lock);
- ret = i40e_sync_vsi_filters(vsi, true);
+ ret = i40e_sync_vsi_filters(vsi);
if (f && !ret)
dev_info(&pf->pdev->dev,
"add macaddr: %pM vlan=%d added to VSI %d\n",
@@ -1179,7 +1177,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
spin_lock_bh(&vsi->mac_filter_list_lock);
i40e_del_filter(vsi, ma, vlan, false, false);
spin_unlock_bh(&vsi->mac_filter_list_lock);
- ret = i40e_sync_vsi_filters(vsi, true);
+ ret = i40e_sync_vsi_filters(vsi);
if (!ret)
dev_info(&pf->pdev->dev,
"del macaddr: %pM vlan=%d removed from VSI %d\n",
@@ -2034,8 +2032,8 @@ static ssize_t i40e_dbg_netdev_ops_read(struct file *filp, char __user *buffer,
bytes_not_copied = copy_to_user(buffer, buf, len);
kfree(buf);
- if (bytes_not_copied < 0)
- return bytes_not_copied;
+ if (bytes_not_copied)
+ return -EFAULT;
*ppos = len;
return len;
@@ -2068,10 +2066,8 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp,
memset(i40e_dbg_netdev_ops_buf, 0, sizeof(i40e_dbg_netdev_ops_buf));
bytes_not_copied = copy_from_user(i40e_dbg_netdev_ops_buf,
buffer, count);
- if (bytes_not_copied < 0)
- return bytes_not_copied;
- else if (bytes_not_copied > 0)
- count -= bytes_not_copied;
+ if (bytes_not_copied)
+ return -EFAULT;
i40e_dbg_netdev_ops_buf[count] = '\0';
buf_tmp = strchr(i40e_dbg_netdev_ops_buf, '\n');
diff --git a/drivers/net/ethernet/intel/i40e/i40e_devids.h b/drivers/net/ethernet/intel/i40e/i40e_devids.h
index c601ca4a610c..448ef4c17efb 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_devids.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_devids.h
@@ -30,7 +30,6 @@
/* Device IDs */
#define I40E_DEV_ID_SFP_XL710 0x1572
#define I40E_DEV_ID_QEMU 0x1574
-#define I40E_DEV_ID_KX_A 0x157F
#define I40E_DEV_ID_KX_B 0x1580
#define I40E_DEV_ID_KX_C 0x1581
#define I40E_DEV_ID_QSFP_A 0x1583
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
index 3f385ffe420f..29d5833e24a3 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
@@ -88,6 +88,7 @@ static const struct i40e_stats i40e_gstrings_misc_stats[] = {
I40E_VSI_STAT("tx_broadcast", eth_stats.tx_broadcast),
I40E_VSI_STAT("rx_unknown_protocol", eth_stats.rx_unknown_protocol),
I40E_VSI_STAT("tx_linearize", tx_linearize),
+ I40E_VSI_STAT("tx_force_wb", tx_force_wb),
};
/* These PF_STATs might look like duplicates of some NETDEV_STATs,
@@ -230,6 +231,7 @@ static const char i40e_priv_flags_strings[][ETH_GSTRING_LEN] = {
"LinkPolling",
"flow-director-atr",
"veb-stats",
+ "packet-split",
};
#define I40E_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_priv_flags_strings)
@@ -2110,7 +2112,7 @@ static int i40e_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
switch (cmd->cmd) {
case ETHTOOL_GRXRINGS:
- cmd->data = vsi->alloc_queue_pairs;
+ cmd->data = vsi->num_queue_pairs;
ret = 0;
break;
case ETHTOOL_GRXFH:
@@ -2583,7 +2585,6 @@ static int i40e_set_channels(struct net_device *dev,
return -EINVAL;
}
-#define I40E_HLUT_ARRAY_SIZE ((I40E_PFQF_HLUT_MAX_INDEX + 1) * 4)
/**
* i40e_get_rxfh_key_size - get the RSS hash key size
* @netdev: network interface device structure
@@ -2611,10 +2612,9 @@ static int i40e_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
- struct i40e_pf *pf = vsi->back;
- struct i40e_hw *hw = &pf->hw;
- u32 reg_val;
- int i, j;
+ u8 *lut, *seed = NULL;
+ int ret;
+ u16 i;
if (hfunc)
*hfunc = ETH_RSS_HASH_TOP;
@@ -2622,24 +2622,20 @@ static int i40e_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
if (!indir)
return 0;
- for (i = 0, j = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++) {
- reg_val = rd32(hw, I40E_PFQF_HLUT(i));
- indir[j++] = reg_val & 0xff;
- indir[j++] = (reg_val >> 8) & 0xff;
- indir[j++] = (reg_val >> 16) & 0xff;
- indir[j++] = (reg_val >> 24) & 0xff;
- }
+ seed = key;
+ lut = kzalloc(I40E_HLUT_ARRAY_SIZE, GFP_KERNEL);
+ if (!lut)
+ return -ENOMEM;
+ ret = i40e_get_rss(vsi, seed, lut, I40E_HLUT_ARRAY_SIZE);
+ if (ret)
+ goto out;
+ for (i = 0; i < I40E_HLUT_ARRAY_SIZE; i++)
+ indir[i] = (u32)(lut[i]);
- if (key) {
- for (i = 0, j = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++) {
- reg_val = rd32(hw, I40E_PFQF_HKEY(i));
- key[j++] = (u8)(reg_val & 0xff);
- key[j++] = (u8)((reg_val >> 8) & 0xff);
- key[j++] = (u8)((reg_val >> 16) & 0xff);
- key[j++] = (u8)((reg_val >> 24) & 0xff);
- }
- }
- return 0;
+out:
+ kfree(lut);
+
+ return ret;
}
/**
@@ -2656,10 +2652,8 @@ static int i40e_set_rxfh(struct net_device *netdev, const u32 *indir,
{
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
- struct i40e_pf *pf = vsi->back;
- struct i40e_hw *hw = &pf->hw;
- u32 reg_val;
- int i, j;
+ u8 *seed = NULL;
+ u16 i;
if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
return -EOPNOTSUPP;
@@ -2667,24 +2661,28 @@ static int i40e_set_rxfh(struct net_device *netdev, const u32 *indir,
if (!indir)
return 0;
- for (i = 0, j = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++) {
- reg_val = indir[j++];
- reg_val |= indir[j++] << 8;
- reg_val |= indir[j++] << 16;
- reg_val |= indir[j++] << 24;
- wr32(hw, I40E_PFQF_HLUT(i), reg_val);
- }
-
if (key) {
- for (i = 0, j = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++) {
- reg_val = key[j++];
- reg_val |= key[j++] << 8;
- reg_val |= key[j++] << 16;
- reg_val |= key[j++] << 24;
- wr32(hw, I40E_PFQF_HKEY(i), reg_val);
+ if (!vsi->rss_hkey_user) {
+ vsi->rss_hkey_user = kzalloc(I40E_HKEY_ARRAY_SIZE,
+ GFP_KERNEL);
+ if (!vsi->rss_hkey_user)
+ return -ENOMEM;
}
+ memcpy(vsi->rss_hkey_user, key, I40E_HKEY_ARRAY_SIZE);
+ seed = vsi->rss_hkey_user;
}
- return 0;
+ if (!vsi->rss_lut_user) {
+ vsi->rss_lut_user = kzalloc(I40E_HLUT_ARRAY_SIZE, GFP_KERNEL);
+ if (!vsi->rss_lut_user)
+ return -ENOMEM;
+ }
+
+ /* Each 32 bits pointed by 'indir' is stored with a lut entry */
+ for (i = 0; i < I40E_HLUT_ARRAY_SIZE; i++)
+ vsi->rss_lut_user[i] = (u8)(indir[i]);
+
+ return i40e_config_rss(vsi, seed, vsi->rss_lut_user,
+ I40E_HLUT_ARRAY_SIZE);
}
/**
@@ -2712,6 +2710,8 @@ static u32 i40e_get_priv_flags(struct net_device *dev)
I40E_PRIV_FLAGS_FD_ATR : 0;
ret_flags |= pf->flags & I40E_FLAG_VEB_STATS_ENABLED ?
I40E_PRIV_FLAGS_VEB_STATS : 0;
+ ret_flags |= pf->flags & I40E_FLAG_RX_PS_ENABLED ?
+ I40E_PRIV_FLAGS_PS : 0;
return ret_flags;
}
@@ -2726,6 +2726,26 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags)
struct i40e_netdev_priv *np = netdev_priv(dev);
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
+ bool reset_required = false;
+
+ /* NOTE: MFP is not settable */
+
+ /* allow the user to control the method of receive
+ * buffer DMA, whether the packet is split at header
+ * boundaries into two separate buffers. In some cases
+ * one routine or the other will perform better.
+ */
+ if ((flags & I40E_PRIV_FLAGS_PS) &&
+ !(pf->flags & I40E_FLAG_RX_PS_ENABLED)) {
+ pf->flags |= I40E_FLAG_RX_PS_ENABLED;
+ pf->flags &= ~I40E_FLAG_RX_1BUF_ENABLED;
+ reset_required = true;
+ } else if (!(flags & I40E_PRIV_FLAGS_PS) &&
+ (pf->flags & I40E_FLAG_RX_PS_ENABLED)) {
+ pf->flags &= ~I40E_FLAG_RX_PS_ENABLED;
+ pf->flags |= I40E_FLAG_RX_1BUF_ENABLED;
+ reset_required = true;
+ }
if (flags & I40E_PRIV_FLAGS_LINKPOLL_FLAG)
pf->flags |= I40E_FLAG_LINK_POLLING_ENABLED;
@@ -2748,6 +2768,10 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags)
else
pf->flags &= ~I40E_FLAG_VEB_STATS_ENABLED;
+ /* if needed, issue reset to cause things to take effect */
+ if (reset_required)
+ i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED));
+
return 0;
}
diff --git a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c
index fe5d9bf3ed6d..579a46ca82df 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c
@@ -1544,8 +1544,6 @@ void i40e_fcoe_vsi_setup(struct i40e_pf *pf)
if (!(pf->flags & I40E_FLAG_FCOE_ENABLED))
return;
- BUG_ON(!pf->vsi[pf->lan_vsi]);
-
for (i = 0; i < pf->num_alloc_vsi; i++) {
vsi = pf->vsi[i];
if (vsi && vsi->type == I40E_VSI_FCOE) {
diff --git a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c
index 79ae7beeafe5..daa9204426d4 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c
@@ -762,7 +762,7 @@ static void i40e_write_byte(u8 *hmc_bits,
/* prepare the bits and mask */
shift_width = ce_info->lsb % 8;
- mask = BIT(ce_info->width) - 1;
+ mask = (u8)(BIT(ce_info->width) - 1);
src_byte = *from;
src_byte &= mask;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index b825f978d441..bb4612c159fd 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -24,12 +24,24 @@
*
******************************************************************************/
+#include <linux/etherdevice.h>
+#include <linux/of_net.h>
+#include <linux/pci.h>
+
+#ifdef CONFIG_SPARC
+#include <asm/idprom.h>
+#include <asm/prom.h>
+#endif
+
/* Local includes */
#include "i40e.h"
#include "i40e_diag.h"
-#ifdef CONFIG_I40E_VXLAN
+#if IS_ENABLED(CONFIG_VXLAN)
#include <net/vxlan.h>
#endif
+#if IS_ENABLED(CONFIG_GENEVE)
+#include <net/geneve.h>
+#endif
const char i40e_driver_name[] = "i40e";
static const char i40e_driver_string[] =
@@ -38,8 +50,8 @@ static const char i40e_driver_string[] =
#define DRV_KERN "-k"
#define DRV_VERSION_MAJOR 1
-#define DRV_VERSION_MINOR 3
-#define DRV_VERSION_BUILD 46
+#define DRV_VERSION_MINOR 4
+#define DRV_VERSION_BUILD 8
#define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \
__stringify(DRV_VERSION_MINOR) "." \
__stringify(DRV_VERSION_BUILD) DRV_KERN
@@ -55,6 +67,8 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit);
static int i40e_setup_misc_vector(struct i40e_pf *pf);
static void i40e_determine_queue_usage(struct i40e_pf *pf);
static int i40e_setup_pf_filter_control(struct i40e_pf *pf);
+static void i40e_fill_rss_lut(struct i40e_pf *pf, u8 *lut,
+ u16 rss_table_size, u16 rss_size);
static void i40e_fdir_sb_setup(struct i40e_pf *pf);
static int i40e_veb_get_bw_info(struct i40e_veb *veb);
@@ -68,7 +82,6 @@ static int i40e_veb_get_bw_info(struct i40e_veb *veb);
static const struct pci_device_id i40e_pci_tbl[] = {
{PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_XL710), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_QEMU), 0},
- {PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_A), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_B), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_C), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_A), 0},
@@ -790,75 +803,6 @@ static void i40e_update_fcoe_stats(struct i40e_vsi *vsi)
#endif
/**
- * i40e_update_link_xoff_rx - Update XOFF received in link flow control mode
- * @pf: the corresponding PF
- *
- * Update the Rx XOFF counter (PAUSE frames) in link flow control mode
- **/
-static void i40e_update_link_xoff_rx(struct i40e_pf *pf)
-{
- struct i40e_hw_port_stats *osd = &pf->stats_offsets;
- struct i40e_hw_port_stats *nsd = &pf->stats;
- struct i40e_hw *hw = &pf->hw;
- u64 xoff = 0;
-
- if ((hw->fc.current_mode != I40E_FC_FULL) &&
- (hw->fc.current_mode != I40E_FC_RX_PAUSE))
- return;
-
- xoff = nsd->link_xoff_rx;
- i40e_stat_update32(hw, I40E_GLPRT_LXOFFRXC(hw->port),
- pf->stat_offsets_loaded,
- &osd->link_xoff_rx, &nsd->link_xoff_rx);
-
- /* No new LFC xoff rx */
- if (!(nsd->link_xoff_rx - xoff))
- return;
-
-}
-
-/**
- * i40e_update_prio_xoff_rx - Update XOFF received in PFC mode
- * @pf: the corresponding PF
- *
- * Update the Rx XOFF counter (PAUSE frames) in PFC mode
- **/
-static void i40e_update_prio_xoff_rx(struct i40e_pf *pf)
-{
- struct i40e_hw_port_stats *osd = &pf->stats_offsets;
- struct i40e_hw_port_stats *nsd = &pf->stats;
- bool xoff[I40E_MAX_TRAFFIC_CLASS] = {false};
- struct i40e_dcbx_config *dcb_cfg;
- struct i40e_hw *hw = &pf->hw;
- u16 i;
- u8 tc;
-
- dcb_cfg = &hw->local_dcbx_config;
-
- /* Collect Link XOFF stats when PFC is disabled */
- if (!dcb_cfg->pfc.pfcenable) {
- i40e_update_link_xoff_rx(pf);
- return;
- }
-
- for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) {
- u64 prio_xoff = nsd->priority_xoff_rx[i];
-
- i40e_stat_update32(hw, I40E_GLPRT_PXOFFRXC(hw->port, i),
- pf->stat_offsets_loaded,
- &osd->priority_xoff_rx[i],
- &nsd->priority_xoff_rx[i]);
-
- /* No new PFC xoff rx */
- if (!(nsd->priority_xoff_rx[i] - prio_xoff))
- continue;
- /* Get the TC for given priority */
- tc = dcb_cfg->etscfg.prioritytable[i];
- xoff[tc] = true;
- }
-}
-
-/**
* i40e_update_vsi_stats - Update the vsi statistics counters.
* @vsi: the VSI to be updated
*
@@ -881,6 +825,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi)
u64 bytes, packets;
unsigned int start;
u64 tx_linearize;
+ u64 tx_force_wb;
u64 rx_p, rx_b;
u64 tx_p, tx_b;
u16 q;
@@ -899,7 +844,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi)
*/
rx_b = rx_p = 0;
tx_b = tx_p = 0;
- tx_restart = tx_busy = tx_linearize = 0;
+ tx_restart = tx_busy = tx_linearize = tx_force_wb = 0;
rx_page = 0;
rx_buf = 0;
rcu_read_lock();
@@ -917,6 +862,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi)
tx_restart += p->tx_stats.restart_queue;
tx_busy += p->tx_stats.tx_busy;
tx_linearize += p->tx_stats.tx_linearize;
+ tx_force_wb += p->tx_stats.tx_force_wb;
/* Rx queue is part of the same block as Tx queue */
p = &p[1];
@@ -934,6 +880,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi)
vsi->tx_restart = tx_restart;
vsi->tx_busy = tx_busy;
vsi->tx_linearize = tx_linearize;
+ vsi->tx_force_wb = tx_force_wb;
vsi->rx_page_failed = rx_page;
vsi->rx_buf_failed = rx_buf;
@@ -1049,12 +996,18 @@ static void i40e_update_pf_stats(struct i40e_pf *pf)
i40e_stat_update32(hw, I40E_GLPRT_LXONTXC(hw->port),
pf->stat_offsets_loaded,
&osd->link_xon_tx, &nsd->link_xon_tx);
- i40e_update_prio_xoff_rx(pf); /* handles I40E_GLPRT_LXOFFRXC */
+ i40e_stat_update32(hw, I40E_GLPRT_LXOFFRXC(hw->port),
+ pf->stat_offsets_loaded,
+ &osd->link_xoff_rx, &nsd->link_xoff_rx);
i40e_stat_update32(hw, I40E_GLPRT_LXOFFTXC(hw->port),
pf->stat_offsets_loaded,
&osd->link_xoff_tx, &nsd->link_xoff_tx);
for (i = 0; i < 8; i++) {
+ i40e_stat_update32(hw, I40E_GLPRT_PXOFFRXC(hw->port, i),
+ pf->stat_offsets_loaded,
+ &osd->priority_xoff_rx[i],
+ &nsd->priority_xoff_rx[i]);
i40e_stat_update32(hw, I40E_GLPRT_PXONRXC(hw->port, i),
pf->stat_offsets_loaded,
&osd->priority_xon_rx[i],
@@ -1317,6 +1270,42 @@ struct i40e_mac_filter *i40e_put_mac_in_vlan(struct i40e_vsi *vsi, u8 *macaddr,
}
/**
+ * i40e_del_mac_all_vlan - Remove a MAC filter from all VLANS
+ * @vsi: the VSI to be searched
+ * @macaddr: the mac address to be removed
+ * @is_vf: true if it is a VF
+ * @is_netdev: true if it is a netdev
+ *
+ * Removes a given MAC address from a VSI, regardless of VLAN
+ *
+ * Returns 0 for success, or error
+ **/
+int i40e_del_mac_all_vlan(struct i40e_vsi *vsi, u8 *macaddr,
+ bool is_vf, bool is_netdev)
+{
+ struct i40e_mac_filter *f = NULL;
+ int changed = 0;
+
+ WARN(!spin_is_locked(&vsi->mac_filter_list_lock),
+ "Missing mac_filter_list_lock\n");
+ list_for_each_entry(f, &vsi->mac_filter_list, list) {
+ if ((ether_addr_equal(macaddr, f->macaddr)) &&
+ (is_vf == f->is_vf) &&
+ (is_netdev == f->is_netdev)) {
+ f->counter--;
+ f->changed = true;
+ changed = 1;
+ }
+ }
+ if (changed) {
+ vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED;
+ vsi->back->flags |= I40E_FLAG_FILTER_SYNC;
+ return 0;
+ }
+ return -ENOENT;
+}
+
+/**
* i40e_rm_default_mac_filter - Remove the default MAC filter set by NVM
* @vsi: the PF Main VSI - inappropriate for any other VSI
* @macaddr: the MAC address
@@ -1547,10 +1536,9 @@ static int i40e_set_mac(struct net_device *netdev, void *p)
spin_unlock_bh(&vsi->mac_filter_list_lock);
}
- i40e_sync_vsi_filters(vsi, false);
ether_addr_copy(netdev->dev_addr, addr->sa_data);
- return 0;
+ return i40e_sync_vsi_filters(vsi);
}
/**
@@ -1590,7 +1578,7 @@ static void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi,
if (enabled_tc && (vsi->back->flags & I40E_FLAG_DCB_ENABLED)) {
/* Find numtc from enabled TC bitmap */
for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
- if (enabled_tc & BIT_ULL(i)) /* TC is enabled */
+ if (enabled_tc & BIT(i)) /* TC is enabled */
numtc++;
}
if (!numtc) {
@@ -1619,13 +1607,14 @@ static void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi,
/* Setup queue offset/count for all TCs for given VSI */
for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
/* See if the given TC is enabled for the given VSI */
- if (vsi->tc_config.enabled_tc & BIT_ULL(i)) {
+ if (vsi->tc_config.enabled_tc & BIT(i)) {
/* TC is enabled */
int pow, num_qps;
switch (vsi->type) {
case I40E_VSI_MAIN:
- qcount = min_t(int, pf->rss_size, num_tc_qps);
+ qcount = min_t(int, pf->alloc_rss_size,
+ num_tc_qps);
break;
#ifdef I40E_FCOE
case I40E_VSI_FCOE:
@@ -1851,13 +1840,12 @@ static void i40e_cleanup_add_list(struct list_head *add_list)
/**
* i40e_sync_vsi_filters - Update the VSI filter list to the HW
* @vsi: ptr to the VSI
- * @grab_rtnl: whether RTNL needs to be grabbed
*
* Push any outstanding VSI filter changes through the AdminQ.
*
* Returns 0 or error value
**/
-int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
+int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
{
struct list_head tmp_del_list, tmp_add_list;
struct i40e_mac_filter *f, *ftmp, *fclone;
@@ -1865,8 +1853,9 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
bool add_happened = false;
int filter_list_len = 0;
u32 changed_flags = 0;
+ i40e_status aq_ret = 0;
bool err_cond = false;
- i40e_status ret = 0;
+ int retval = 0;
struct i40e_pf *pf;
int num_add = 0;
int num_del = 0;
@@ -1929,17 +1918,22 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
}
spin_unlock_bh(&vsi->mac_filter_list_lock);
- if (err_cond)
+ if (err_cond) {
i40e_cleanup_add_list(&tmp_add_list);
+ retval = -ENOMEM;
+ goto out;
+ }
}
/* Now process 'del_list' outside the lock */
if (!list_empty(&tmp_del_list)) {
+ int del_list_size;
+
filter_list_len = pf->hw.aq.asq_buf_size /
sizeof(struct i40e_aqc_remove_macvlan_element_data);
- del_list = kcalloc(filter_list_len,
- sizeof(struct i40e_aqc_remove_macvlan_element_data),
- GFP_KERNEL);
+ del_list_size = filter_list_len *
+ sizeof(struct i40e_aqc_remove_macvlan_element_data);
+ del_list = kzalloc(del_list_size, GFP_KERNEL);
if (!del_list) {
i40e_cleanup_add_list(&tmp_add_list);
@@ -1948,7 +1942,8 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
i40e_undo_del_filter_entries(vsi, &tmp_del_list);
i40e_undo_add_filter_entries(vsi);
spin_unlock_bh(&vsi->mac_filter_list_lock);
- return -ENOMEM;
+ retval = -ENOMEM;
+ goto out;
}
list_for_each_entry_safe(f, ftmp, &tmp_del_list, list) {
@@ -1966,18 +1961,22 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
/* flush a full buffer */
if (num_del == filter_list_len) {
- ret = i40e_aq_remove_macvlan(&pf->hw,
- vsi->seid, del_list, num_del,
- NULL);
+ aq_ret = i40e_aq_remove_macvlan(&pf->hw,
+ vsi->seid,
+ del_list,
+ num_del,
+ NULL);
aq_err = pf->hw.aq.asq_last_status;
num_del = 0;
- memset(del_list, 0, sizeof(*del_list));
+ memset(del_list, 0, del_list_size);
- if (ret && aq_err != I40E_AQ_RC_ENOENT)
+ if (aq_ret && aq_err != I40E_AQ_RC_ENOENT) {
+ retval = -EIO;
dev_err(&pf->pdev->dev,
"ignoring delete macvlan error, err %s, aq_err %s while flushing a full buffer\n",
- i40e_stat_str(&pf->hw, ret),
+ i40e_stat_str(&pf->hw, aq_ret),
i40e_aq_str(&pf->hw, aq_err));
+ }
}
/* Release memory for MAC filter entries which were
* synced up with HW.
@@ -1987,15 +1986,16 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
}
if (num_del) {
- ret = i40e_aq_remove_macvlan(&pf->hw, vsi->seid,
- del_list, num_del, NULL);
+ aq_ret = i40e_aq_remove_macvlan(&pf->hw, vsi->seid,
+ del_list, num_del,
+ NULL);
aq_err = pf->hw.aq.asq_last_status;
num_del = 0;
- if (ret && aq_err != I40E_AQ_RC_ENOENT)
+ if (aq_ret && aq_err != I40E_AQ_RC_ENOENT)
dev_info(&pf->pdev->dev,
"ignoring delete macvlan error, err %s aq_err %s\n",
- i40e_stat_str(&pf->hw, ret),
+ i40e_stat_str(&pf->hw, aq_ret),
i40e_aq_str(&pf->hw, aq_err));
}
@@ -2004,13 +2004,14 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
}
if (!list_empty(&tmp_add_list)) {
+ int add_list_size;
/* do all the adds now */
filter_list_len = pf->hw.aq.asq_buf_size /
sizeof(struct i40e_aqc_add_macvlan_element_data),
- add_list = kcalloc(filter_list_len,
- sizeof(struct i40e_aqc_add_macvlan_element_data),
- GFP_KERNEL);
+ add_list_size = filter_list_len *
+ sizeof(struct i40e_aqc_add_macvlan_element_data);
+ add_list = kzalloc(add_list_size, GFP_KERNEL);
if (!add_list) {
/* Purge element from temporary lists */
i40e_cleanup_add_list(&tmp_add_list);
@@ -2019,7 +2020,8 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
spin_lock_bh(&vsi->mac_filter_list_lock);
i40e_undo_add_filter_entries(vsi);
spin_unlock_bh(&vsi->mac_filter_list_lock);
- return -ENOMEM;
+ retval = -ENOMEM;
+ goto out;
}
list_for_each_entry_safe(f, ftmp, &tmp_add_list, list) {
@@ -2040,15 +2042,15 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
/* flush a full buffer */
if (num_add == filter_list_len) {
- ret = i40e_aq_add_macvlan(&pf->hw, vsi->seid,
- add_list, num_add,
- NULL);
+ aq_ret = i40e_aq_add_macvlan(&pf->hw, vsi->seid,
+ add_list, num_add,
+ NULL);
aq_err = pf->hw.aq.asq_last_status;
num_add = 0;
- if (ret)
+ if (aq_ret)
break;
- memset(add_list, 0, sizeof(*add_list));
+ memset(add_list, 0, add_list_size);
}
/* Entries from tmp_add_list were cloned from MAC
* filter list, hence clean those cloned entries
@@ -2058,18 +2060,19 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
}
if (num_add) {
- ret = i40e_aq_add_macvlan(&pf->hw, vsi->seid,
- add_list, num_add, NULL);
+ aq_ret = i40e_aq_add_macvlan(&pf->hw, vsi->seid,
+ add_list, num_add, NULL);
aq_err = pf->hw.aq.asq_last_status;
num_add = 0;
}
kfree(add_list);
add_list = NULL;
- if (add_happened && ret && aq_err != I40E_AQ_RC_EINVAL) {
+ if (add_happened && aq_ret && aq_err != I40E_AQ_RC_EINVAL) {
+ retval = i40e_aq_rc_to_posix(aq_ret, aq_err);
dev_info(&pf->pdev->dev,
"add filter failed, err %s aq_err %s\n",
- i40e_stat_str(&pf->hw, ret),
+ i40e_stat_str(&pf->hw, aq_ret),
i40e_aq_str(&pf->hw, aq_err));
if ((pf->hw.aq.asq_last_status == I40E_AQ_RC_ENOSPC) &&
!test_bit(__I40E_FILTER_OVERFLOW_PROMISC,
@@ -2087,16 +2090,19 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
bool cur_multipromisc;
cur_multipromisc = !!(vsi->current_netdev_flags & IFF_ALLMULTI);
- ret = i40e_aq_set_vsi_multicast_promiscuous(&vsi->back->hw,
- vsi->seid,
- cur_multipromisc,
- NULL);
- if (ret)
+ aq_ret = i40e_aq_set_vsi_multicast_promiscuous(&vsi->back->hw,
+ vsi->seid,
+ cur_multipromisc,
+ NULL);
+ if (aq_ret) {
+ retval = i40e_aq_rc_to_posix(aq_ret,
+ pf->hw.aq.asq_last_status);
dev_info(&pf->pdev->dev,
"set multi promisc failed, err %s aq_err %s\n",
- i40e_stat_str(&pf->hw, ret),
+ i40e_stat_str(&pf->hw, aq_ret),
i40e_aq_str(&pf->hw,
pf->hw.aq.asq_last_status));
+ }
}
if ((changed_flags & IFF_PROMISC) || promisc_forced_on) {
bool cur_promisc;
@@ -2112,44 +2118,50 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi, bool grab_rtnl)
*/
if (pf->cur_promisc != cur_promisc) {
pf->cur_promisc = cur_promisc;
- if (grab_rtnl)
- i40e_do_reset_safe(pf,
- BIT(__I40E_PF_RESET_REQUESTED));
- else
- i40e_do_reset(pf,
- BIT(__I40E_PF_RESET_REQUESTED));
+ set_bit(__I40E_PF_RESET_REQUESTED, &pf->state);
}
} else {
- ret = i40e_aq_set_vsi_unicast_promiscuous(
+ aq_ret = i40e_aq_set_vsi_unicast_promiscuous(
&vsi->back->hw,
vsi->seid,
cur_promisc, NULL);
- if (ret)
+ if (aq_ret) {
+ retval =
+ i40e_aq_rc_to_posix(aq_ret,
+ pf->hw.aq.asq_last_status);
dev_info(&pf->pdev->dev,
"set unicast promisc failed, err %d, aq_err %d\n",
- ret, pf->hw.aq.asq_last_status);
- ret = i40e_aq_set_vsi_multicast_promiscuous(
+ aq_ret, pf->hw.aq.asq_last_status);
+ }
+ aq_ret = i40e_aq_set_vsi_multicast_promiscuous(
&vsi->back->hw,
vsi->seid,
cur_promisc, NULL);
- if (ret)
+ if (aq_ret) {
+ retval =
+ i40e_aq_rc_to_posix(aq_ret,
+ pf->hw.aq.asq_last_status);
dev_info(&pf->pdev->dev,
"set multicast promisc failed, err %d, aq_err %d\n",
- ret, pf->hw.aq.asq_last_status);
+ aq_ret, pf->hw.aq.asq_last_status);
+ }
}
- ret = i40e_aq_set_vsi_broadcast(&vsi->back->hw,
- vsi->seid,
- cur_promisc, NULL);
- if (ret)
+ aq_ret = i40e_aq_set_vsi_broadcast(&vsi->back->hw,
+ vsi->seid,
+ cur_promisc, NULL);
+ if (aq_ret) {
+ retval = i40e_aq_rc_to_posix(aq_ret,
+ pf->hw.aq.asq_last_status);
dev_info(&pf->pdev->dev,
"set brdcast promisc failed, err %s, aq_err %s\n",
- i40e_stat_str(&pf->hw, ret),
+ i40e_stat_str(&pf->hw, aq_ret),
i40e_aq_str(&pf->hw,
pf->hw.aq.asq_last_status));
+ }
}
-
+out:
clear_bit(__I40E_CONFIG_BUSY, &vsi->state);
- return 0;
+ return retval;
}
/**
@@ -2166,8 +2178,15 @@ static void i40e_sync_filters_subtask(struct i40e_pf *pf)
for (v = 0; v < pf->num_alloc_vsi; v++) {
if (pf->vsi[v] &&
- (pf->vsi[v]->flags & I40E_VSI_FLAG_FILTER_CHANGED))
- i40e_sync_vsi_filters(pf->vsi[v], true);
+ (pf->vsi[v]->flags & I40E_VSI_FLAG_FILTER_CHANGED)) {
+ int ret = i40e_sync_vsi_filters(pf->vsi[v]);
+
+ if (ret) {
+ /* come back and try again later */
+ pf->flags |= I40E_FLAG_FILTER_SYNC;
+ break;
+ }
+ }
}
}
@@ -2377,16 +2396,13 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid)
}
}
- /* Make sure to release before sync_vsi_filter because that
- * function will lock/unlock as necessary
- */
spin_unlock_bh(&vsi->mac_filter_list_lock);
- if (test_bit(__I40E_DOWN, &vsi->back->state) ||
- test_bit(__I40E_RESET_RECOVERY_PENDING, &vsi->back->state))
- return 0;
-
- return i40e_sync_vsi_filters(vsi, false);
+ /* schedule our worker thread which will take care of
+ * applying the new filter changes
+ */
+ i40e_service_event_schedule(vsi->back);
+ return 0;
}
/**
@@ -2459,16 +2475,13 @@ int i40e_vsi_kill_vlan(struct i40e_vsi *vsi, s16 vid)
}
}
- /* Make sure to release before sync_vsi_filter because that
- * function with lock/unlock as necessary
- */
spin_unlock_bh(&vsi->mac_filter_list_lock);
- if (test_bit(__I40E_DOWN, &vsi->back->state) ||
- test_bit(__I40E_RESET_RECOVERY_PENDING, &vsi->back->state))
- return 0;
-
- return i40e_sync_vsi_filters(vsi, false);
+ /* schedule our worker thread which will take care of
+ * applying the new filter changes
+ */
+ i40e_service_event_schedule(vsi->back);
+ return 0;
}
/**
@@ -2711,6 +2724,11 @@ static void i40e_config_xps_tx_ring(struct i40e_ring *ring)
netif_set_xps_queue(ring->netdev, mask, ring->queue_index);
free_cpumask_var(mask);
}
+
+ /* schedule our worker thread which will take care of
+ * applying the new filter changes
+ */
+ i40e_service_event_schedule(vsi->back);
}
/**
@@ -4360,17 +4378,41 @@ static void i40e_detect_recover_hung_queue(int q_idx, struct i40e_vsi *vsi)
else
val = rd32(&pf->hw, I40E_PFINT_DYN_CTL0);
+ /* Bail out if interrupts are disabled because napi_poll
+ * execution in-progress or will get scheduled soon.
+ * napi_poll cleans TX and RX queues and updates 'next_to_clean'.
+ */
+ if (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK))
+ return;
+
head = i40e_get_head(tx_ring);
tx_pending = i40e_get_tx_pending(tx_ring);
- /* Interrupts are disabled and TX pending is non-zero,
- * trigger the SW interrupt (don't wait). Worst case
- * there will be one extra interrupt which may result
- * into not cleaning any queues because queues are cleaned.
+ /* HW is done executing descriptors, updated HEAD write back,
+ * but SW hasn't processed those descriptors. If interrupt is
+ * not generated from this point ON, it could result into
+ * dev_watchdog detecting timeout on those netdev_queue,
+ * hence proactively trigger SW interrupt.
*/
- if (tx_pending && (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK)))
- i40e_force_wb(vsi, tx_ring->q_vector);
+ if (tx_pending) {
+ /* NAPI Poll didn't run and clear since it was set */
+ if (test_and_clear_bit(I40E_Q_VECTOR_HUNG_DETECT,
+ &tx_ring->q_vector->hung_detected)) {
+ netdev_info(vsi->netdev, "VSI_seid %d, Hung TX queue %d, tx_pending: %d, NTC:0x%x, HWB: 0x%x, NTU: 0x%x, TAIL: 0x%x\n",
+ vsi->seid, q_idx, tx_pending,
+ tx_ring->next_to_clean, head,
+ tx_ring->next_to_use,
+ readl(tx_ring->tail));
+ netdev_info(vsi->netdev, "VSI_seid %d, Issuing force_wb for TX queue %d, Interrupt Reg: 0x%x\n",
+ vsi->seid, q_idx, val);
+ i40e_force_wb(vsi, tx_ring->q_vector);
+ } else {
+ /* First Chance - detected possible hung */
+ set_bit(I40E_Q_VECTOR_HUNG_DETECT,
+ &tx_ring->q_vector->hung_detected);
+ }
+ }
}
/**
@@ -4441,7 +4483,7 @@ static u8 i40e_get_iscsi_tc_map(struct i40e_pf *pf)
if (app.selector == I40E_APP_SEL_TCPIP &&
app.protocolid == I40E_APP_PROTOID_ISCSI) {
tc = dcbcfg->etscfg.prioritytable[app.priority];
- enabled_tc |= BIT_ULL(tc);
+ enabled_tc |= BIT(tc);
break;
}
}
@@ -4525,7 +4567,7 @@ static u8 i40e_pf_get_num_tc(struct i40e_pf *pf)
/* At least have TC0 */
enabled_tc = (enabled_tc ? enabled_tc : 0x1);
for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
- if (enabled_tc & BIT_ULL(i))
+ if (enabled_tc & BIT(i))
num_tc++;
}
return num_tc;
@@ -4547,7 +4589,7 @@ static u8 i40e_pf_get_default_tc(struct i40e_pf *pf)
/* Find the first enabled TC */
for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
- if (enabled_tc & BIT_ULL(i))
+ if (enabled_tc & BIT(i))
break;
}
@@ -4707,7 +4749,7 @@ static void i40e_vsi_config_netdev_tc(struct i40e_vsi *vsi, u8 enabled_tc)
* will set the numtc for netdev as 2 that will be
* referenced by the netdev layer as TC 0 and 1.
*/
- if (vsi->tc_config.enabled_tc & BIT_ULL(i))
+ if (vsi->tc_config.enabled_tc & BIT(i))
netdev_set_tc_queue(netdev,
vsi->tc_config.tc_info[i].netdev_tc,
vsi->tc_config.tc_info[i].qcount,
@@ -4769,7 +4811,7 @@ static int i40e_vsi_config_tc(struct i40e_vsi *vsi, u8 enabled_tc)
/* Enable ETS TCs with equal BW Share for now across all VSIs */
for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
- if (enabled_tc & BIT_ULL(i))
+ if (enabled_tc & BIT(i))
bw_share[i] = 1;
}
@@ -4843,7 +4885,7 @@ int i40e_veb_config_tc(struct i40e_veb *veb, u8 enabled_tc)
/* Enable ETS TCs with equal BW Share for now */
for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
- if (enabled_tc & BIT_ULL(i))
+ if (enabled_tc & BIT(i))
bw_data.tc_bw_share_credits[i] = 1;
}
@@ -5240,7 +5282,7 @@ static int i40e_setup_tc(struct net_device *netdev, u8 tc)
/* Generate TC map for number of tc requested */
for (i = 0; i < tc; i++)
- enabled_tc |= BIT_ULL(i);
+ enabled_tc |= BIT(i);
/* Requesting same TC configuration as already enabled */
if (enabled_tc == vsi->tc_config.enabled_tc)
@@ -5305,6 +5347,9 @@ int i40e_open(struct net_device *netdev)
#ifdef CONFIG_I40E_VXLAN
vxlan_get_rx_port(netdev);
#endif
+#ifdef CONFIG_I40E_GENEVE
+ geneve_get_rx_port(netdev);
+#endif
return 0;
}
@@ -5738,7 +5783,7 @@ static void i40e_handle_lan_overflow_event(struct i40e_pf *pf,
**/
static void i40e_service_event_complete(struct i40e_pf *pf)
{
- BUG_ON(!test_bit(__I40E_SERVICE_SCHED, &pf->state));
+ WARN_ON(!test_bit(__I40E_SERVICE_SCHED, &pf->state));
/* flush memory to make sure state is correct before next watchog */
smp_mb__before_atomic();
@@ -6013,6 +6058,9 @@ static void i40e_link_event(struct i40e_pf *pf)
i40e_status status;
bool new_link, old_link;
+ /* save off old link status information */
+ pf->hw.phy.link_info_old = pf->hw.phy.link_info;
+
/* set this to force the get_link_status call to refresh state */
pf->hw.phy.get_link_info = true;
@@ -6101,23 +6149,23 @@ static void i40e_reset_subtask(struct i40e_pf *pf)
rtnl_lock();
if (test_bit(__I40E_REINIT_REQUESTED, &pf->state)) {
- reset_flags |= BIT_ULL(__I40E_REINIT_REQUESTED);
+ reset_flags |= BIT(__I40E_REINIT_REQUESTED);
clear_bit(__I40E_REINIT_REQUESTED, &pf->state);
}
if (test_bit(__I40E_PF_RESET_REQUESTED, &pf->state)) {
- reset_flags |= BIT_ULL(__I40E_PF_RESET_REQUESTED);
+ reset_flags |= BIT(__I40E_PF_RESET_REQUESTED);
clear_bit(__I40E_PF_RESET_REQUESTED, &pf->state);
}
if (test_bit(__I40E_CORE_RESET_REQUESTED, &pf->state)) {
- reset_flags |= BIT_ULL(__I40E_CORE_RESET_REQUESTED);
+ reset_flags |= BIT(__I40E_CORE_RESET_REQUESTED);
clear_bit(__I40E_CORE_RESET_REQUESTED, &pf->state);
}
if (test_bit(__I40E_GLOBAL_RESET_REQUESTED, &pf->state)) {
- reset_flags |= BIT_ULL(__I40E_GLOBAL_RESET_REQUESTED);
+ reset_flags |= BIT(__I40E_GLOBAL_RESET_REQUESTED);
clear_bit(__I40E_GLOBAL_RESET_REQUESTED, &pf->state);
}
if (test_bit(__I40E_DOWN_REQUESTED, &pf->state)) {
- reset_flags |= BIT_ULL(__I40E_DOWN_REQUESTED);
+ reset_flags |= BIT(__I40E_DOWN_REQUESTED);
clear_bit(__I40E_DOWN_REQUESTED, &pf->state);
}
@@ -6147,13 +6195,9 @@ unlock:
static void i40e_handle_link_event(struct i40e_pf *pf,
struct i40e_arq_event_info *e)
{
- struct i40e_hw *hw = &pf->hw;
struct i40e_aqc_get_link_status *status =
(struct i40e_aqc_get_link_status *)&e->desc.params.raw;
- /* save off old link status information */
- hw->phy.link_info_old = hw->phy.link_info;
-
/* Do a new status request to re-enable LSE reporting
* and load new status information into the hw struct
* This completely ignores any state information
@@ -6192,15 +6236,18 @@ static void i40e_clean_adminq_subtask(struct i40e_pf *pf)
val = rd32(&pf->hw, pf->hw.aq.arq.len);
oldval = val;
if (val & I40E_PF_ARQLEN_ARQVFE_MASK) {
- dev_info(&pf->pdev->dev, "ARQ VF Error detected\n");
+ if (hw->debug_mask & I40E_DEBUG_AQ)
+ dev_info(&pf->pdev->dev, "ARQ VF Error detected\n");
val &= ~I40E_PF_ARQLEN_ARQVFE_MASK;
}
if (val & I40E_PF_ARQLEN_ARQOVFL_MASK) {
- dev_info(&pf->pdev->dev, "ARQ Overflow Error detected\n");
+ if (hw->debug_mask & I40E_DEBUG_AQ)
+ dev_info(&pf->pdev->dev, "ARQ Overflow Error detected\n");
val &= ~I40E_PF_ARQLEN_ARQOVFL_MASK;
}
if (val & I40E_PF_ARQLEN_ARQCRIT_MASK) {
- dev_info(&pf->pdev->dev, "ARQ Critical Error detected\n");
+ if (hw->debug_mask & I40E_DEBUG_AQ)
+ dev_info(&pf->pdev->dev, "ARQ Critical Error detected\n");
val &= ~I40E_PF_ARQLEN_ARQCRIT_MASK;
}
if (oldval != val)
@@ -6209,15 +6256,18 @@ static void i40e_clean_adminq_subtask(struct i40e_pf *pf)
val = rd32(&pf->hw, pf->hw.aq.asq.len);
oldval = val;
if (val & I40E_PF_ATQLEN_ATQVFE_MASK) {
- dev_info(&pf->pdev->dev, "ASQ VF Error detected\n");
+ if (pf->hw.debug_mask & I40E_DEBUG_AQ)
+ dev_info(&pf->pdev->dev, "ASQ VF Error detected\n");
val &= ~I40E_PF_ATQLEN_ATQVFE_MASK;
}
if (val & I40E_PF_ATQLEN_ATQOVFL_MASK) {
- dev_info(&pf->pdev->dev, "ASQ Overflow Error detected\n");
+ if (pf->hw.debug_mask & I40E_DEBUG_AQ)
+ dev_info(&pf->pdev->dev, "ASQ Overflow Error detected\n");
val &= ~I40E_PF_ATQLEN_ATQOVFL_MASK;
}
if (val & I40E_PF_ATQLEN_ATQCRIT_MASK) {
- dev_info(&pf->pdev->dev, "ASQ Critical Error detected\n");
+ if (pf->hw.debug_mask & I40E_DEBUG_AQ)
+ dev_info(&pf->pdev->dev, "ASQ Critical Error detected\n");
val &= ~I40E_PF_ATQLEN_ATQCRIT_MASK;
}
if (oldval != val)
@@ -6268,6 +6318,7 @@ static void i40e_clean_adminq_subtask(struct i40e_pf *pf)
break;
case i40e_aqc_opc_nvm_erase:
case i40e_aqc_opc_nvm_update:
+ case i40e_aqc_opc_oem_post_update:
i40e_debug(&pf->hw, I40E_DEBUG_NVM, "ARQ NVM operation completed\n");
break;
default:
@@ -6685,6 +6736,7 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit)
struct i40e_hw *hw = &pf->hw;
u8 set_fc_aq_fail = 0;
i40e_status ret;
+ u32 val;
u32 v;
/* Now we wait for GRST to settle out.
@@ -6823,6 +6875,20 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit)
}
}
+ /* Reconfigure hardware for allowing smaller MSS in the case
+ * of TSO, so that we avoid the MDD being fired and causing
+ * a reset in the case of small MSS+TSO.
+ */
+#define I40E_REG_MSS 0x000E64DC
+#define I40E_REG_MSS_MIN_MASK 0x3FF0000
+#define I40E_64BYTE_MSS 0x400000
+ val = rd32(hw, I40E_REG_MSS);
+ if ((val & I40E_REG_MSS_MIN_MASK) > I40E_64BYTE_MSS) {
+ val &= ~I40E_REG_MSS_MIN_MASK;
+ val |= I40E_64BYTE_MSS;
+ wr32(hw, I40E_REG_MSS, val);
+ }
+
if (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver < 33)) ||
(pf->hw.aq.fw_maj_ver < 4)) {
msleep(75);
@@ -6984,30 +7050,30 @@ static void i40e_handle_mdd_event(struct i40e_pf *pf)
i40e_flush(hw);
}
-#ifdef CONFIG_I40E_VXLAN
/**
- * i40e_sync_vxlan_filters_subtask - Sync the VSI filter list with HW
+ * i40e_sync_udp_filters_subtask - Sync the VSI filter list with HW
* @pf: board private structure
**/
-static void i40e_sync_vxlan_filters_subtask(struct i40e_pf *pf)
+static void i40e_sync_udp_filters_subtask(struct i40e_pf *pf)
{
+#if IS_ENABLED(CONFIG_VXLAN) || IS_ENABLED(CONFIG_GENEVE)
struct i40e_hw *hw = &pf->hw;
i40e_status ret;
__be16 port;
int i;
- if (!(pf->flags & I40E_FLAG_VXLAN_FILTER_SYNC))
+ if (!(pf->flags & I40E_FLAG_UDP_FILTER_SYNC))
return;
- pf->flags &= ~I40E_FLAG_VXLAN_FILTER_SYNC;
+ pf->flags &= ~I40E_FLAG_UDP_FILTER_SYNC;
for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) {
- if (pf->pending_vxlan_bitmap & BIT_ULL(i)) {
- pf->pending_vxlan_bitmap &= ~BIT_ULL(i);
- port = pf->vxlan_ports[i];
+ if (pf->pending_udp_bitmap & BIT_ULL(i)) {
+ pf->pending_udp_bitmap &= ~BIT_ULL(i);
+ port = pf->udp_ports[i].index;
if (port)
ret = i40e_aq_add_udp_tunnel(hw, ntohs(port),
- I40E_AQC_TUNNEL_TYPE_VXLAN,
+ pf->udp_ports[i].type,
NULL, NULL);
else
ret = i40e_aq_del_udp_tunnel(hw, i, NULL);
@@ -7020,13 +7086,13 @@ static void i40e_sync_vxlan_filters_subtask(struct i40e_pf *pf)
i40e_stat_str(&pf->hw, ret),
i40e_aq_str(&pf->hw,
pf->hw.aq.asq_last_status));
- pf->vxlan_ports[i] = 0;
+ pf->udp_ports[i].index = 0;
}
}
}
+#endif
}
-#endif
/**
* i40e_service_task - Run the driver's async subtasks
* @work: pointer to work_struct containing our data
@@ -7051,8 +7117,8 @@ static void i40e_service_task(struct work_struct *work)
i40e_watchdog_subtask(pf);
i40e_fdir_reinit_subtask(pf);
i40e_sync_filters_subtask(pf);
-#ifdef CONFIG_I40E_VXLAN
- i40e_sync_vxlan_filters_subtask(pf);
+#if IS_ENABLED(CONFIG_VXLAN) || IS_ENABLED(CONFIG_GENEVE)
+ i40e_sync_udp_filters_subtask(pf);
#endif
i40e_clean_adminq_subtask(pf);
@@ -7282,6 +7348,23 @@ static void i40e_vsi_free_arrays(struct i40e_vsi *vsi, bool free_qvectors)
}
/**
+ * i40e_clear_rss_config_user - clear the user configured RSS hash keys
+ * and lookup table
+ * @vsi: Pointer to VSI structure
+ */
+static void i40e_clear_rss_config_user(struct i40e_vsi *vsi)
+{
+ if (!vsi)
+ return;
+
+ kfree(vsi->rss_hkey_user);
+ vsi->rss_hkey_user = NULL;
+
+ kfree(vsi->rss_lut_user);
+ vsi->rss_lut_user = NULL;
+}
+
+/**
* i40e_vsi_clear - Deallocate the VSI provided
* @vsi: the VSI being un-configured
**/
@@ -7318,6 +7401,7 @@ static int i40e_vsi_clear(struct i40e_vsi *vsi)
i40e_put_lump(pf->irq_pile, vsi->base_vector, vsi->idx);
i40e_vsi_free_arrays(vsi, true);
+ i40e_clear_rss_config_user(vsi);
pf->vsi[vsi->idx] = NULL;
if (vsi->idx < pf->next_vsi)
@@ -7780,7 +7864,8 @@ static int i40e_setup_misc_vector(struct i40e_pf *pf)
* @vsi: vsi structure
* @seed: RSS hash seed
**/
-static int i40e_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed)
+static int i40e_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed,
+ u8 *lut, u16 lut_size)
{
struct i40e_aqc_get_set_rss_key_data rss_key;
struct i40e_pf *pf = vsi->back;
@@ -7833,43 +7918,57 @@ static int i40e_vsi_config_rss(struct i40e_vsi *vsi)
{
u8 seed[I40E_HKEY_ARRAY_SIZE];
struct i40e_pf *pf = vsi->back;
+ u8 *lut;
+ int ret;
- netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE);
- vsi->rss_size = min_t(int, pf->rss_size, vsi->num_queue_pairs);
+ if (!(pf->flags & I40E_FLAG_RSS_AQ_CAPABLE))
+ return 0;
- if (pf->flags & I40E_FLAG_RSS_AQ_CAPABLE)
- return i40e_config_rss_aq(vsi, seed);
+ lut = kzalloc(vsi->rss_table_size, GFP_KERNEL);
+ if (!lut)
+ return -ENOMEM;
- return 0;
+ i40e_fill_rss_lut(pf, lut, vsi->rss_table_size, vsi->rss_size);
+ netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE);
+ vsi->rss_size = min_t(int, pf->alloc_rss_size, vsi->num_queue_pairs);
+ ret = i40e_config_rss_aq(vsi, seed, lut, vsi->rss_table_size);
+ kfree(lut);
+
+ return ret;
}
/**
- * i40e_config_rss_reg - Prepare for RSS if used
- * @pf: board private structure
+ * i40e_config_rss_reg - Configure RSS keys and lut by writing registers
+ * @vsi: Pointer to vsi structure
* @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Returns 0 on success, negative on failure
**/
-static int i40e_config_rss_reg(struct i40e_pf *pf, const u8 *seed)
+static int i40e_config_rss_reg(struct i40e_vsi *vsi, const u8 *seed,
+ const u8 *lut, u16 lut_size)
{
- struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi];
+ struct i40e_pf *pf = vsi->back;
struct i40e_hw *hw = &pf->hw;
- u32 *seed_dw = (u32 *)seed;
- u32 current_queue = 0;
- u32 lut = 0;
- int i, j;
+ u8 i;
/* Fill out hash function seed */
- for (i = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++)
- wr32(hw, I40E_PFQF_HKEY(i), seed_dw[i]);
+ if (seed) {
+ u32 *seed_dw = (u32 *)seed;
- for (i = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++) {
- lut = 0;
- for (j = 0; j < 4; j++) {
- if (current_queue == vsi->rss_size)
- current_queue = 0;
- lut |= ((current_queue) << (8 * j));
- current_queue++;
- }
- wr32(&pf->hw, I40E_PFQF_HLUT(i), lut);
+ for (i = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++)
+ wr32(hw, I40E_PFQF_HKEY(i), seed_dw[i]);
+ }
+
+ if (lut) {
+ u32 *lut_dw = (u32 *)lut;
+
+ if (lut_size != I40E_HLUT_ARRAY_SIZE)
+ return -EINVAL;
+
+ for (i = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++)
+ wr32(hw, I40E_PFQF_HLUT(i), lut_dw[i]);
}
i40e_flush(hw);
@@ -7877,18 +7976,101 @@ static int i40e_config_rss_reg(struct i40e_pf *pf, const u8 *seed)
}
/**
- * i40e_config_rss - Prepare for RSS if used
+ * i40e_get_rss_reg - Get the RSS keys and lut by reading registers
+ * @vsi: Pointer to VSI structure
+ * @seed: Buffer to store the keys
+ * @lut: Buffer to store the lookup table entries
+ * @lut_size: Size of buffer to store the lookup table entries
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int i40e_get_rss_reg(struct i40e_vsi *vsi, u8 *seed,
+ u8 *lut, u16 lut_size)
+{
+ struct i40e_pf *pf = vsi->back;
+ struct i40e_hw *hw = &pf->hw;
+ u16 i;
+
+ if (seed) {
+ u32 *seed_dw = (u32 *)seed;
+
+ for (i = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++)
+ seed_dw[i] = rd32(hw, I40E_PFQF_HKEY(i));
+ }
+ if (lut) {
+ u32 *lut_dw = (u32 *)lut;
+
+ if (lut_size != I40E_HLUT_ARRAY_SIZE)
+ return -EINVAL;
+ for (i = 0; i <= I40E_PFQF_HLUT_MAX_INDEX; i++)
+ lut_dw[i] = rd32(hw, I40E_PFQF_HLUT(i));
+ }
+
+ return 0;
+}
+
+/**
+ * i40e_config_rss - Configure RSS keys and lut
+ * @vsi: Pointer to VSI structure
+ * @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Returns 0 on success, negative on failure
+ */
+int i40e_config_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size)
+{
+ struct i40e_pf *pf = vsi->back;
+
+ if (pf->flags & I40E_FLAG_RSS_AQ_CAPABLE)
+ return i40e_config_rss_aq(vsi, seed, lut, lut_size);
+ else
+ return i40e_config_rss_reg(vsi, seed, lut, lut_size);
+}
+
+/**
+ * i40e_get_rss - Get RSS keys and lut
+ * @vsi: Pointer to VSI structure
+ * @seed: Buffer to store the keys
+ * @lut: Buffer to store the lookup table entries
+ * lut_size: Size of buffer to store the lookup table entries
+ *
+ * Returns 0 on success, negative on failure
+ */
+int i40e_get_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size)
+{
+ return i40e_get_rss_reg(vsi, seed, lut, lut_size);
+}
+
+/**
+ * i40e_fill_rss_lut - Fill the RSS lookup table with default values
+ * @pf: Pointer to board private structure
+ * @lut: Lookup table
+ * @rss_table_size: Lookup table size
+ * @rss_size: Range of queue number for hashing
+ */
+static void i40e_fill_rss_lut(struct i40e_pf *pf, u8 *lut,
+ u16 rss_table_size, u16 rss_size)
+{
+ u16 i;
+
+ for (i = 0; i < rss_table_size; i++)
+ lut[i] = i % rss_size;
+}
+
+/**
+ * i40e_pf_config_rss - Prepare for RSS if used
* @pf: board private structure
**/
-static int i40e_config_rss(struct i40e_pf *pf)
+static int i40e_pf_config_rss(struct i40e_pf *pf)
{
struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi];
u8 seed[I40E_HKEY_ARRAY_SIZE];
+ u8 *lut;
struct i40e_hw *hw = &pf->hw;
u32 reg_val;
u64 hena;
-
- netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE);
+ int ret;
/* By default we enable TCP/UDP with IPv4/IPv6 ptypes */
hena = (u64)rd32(hw, I40E_PFQF_HENA(0)) |
@@ -7898,8 +8080,6 @@ static int i40e_config_rss(struct i40e_pf *pf)
wr32(hw, I40E_PFQF_HENA(0), (u32)hena);
wr32(hw, I40E_PFQF_HENA(1), (u32)(hena >> 32));
- vsi->rss_size = min_t(int, pf->rss_size, vsi->num_queue_pairs);
-
/* Determine the RSS table size based on the hardware capabilities */
reg_val = rd32(hw, I40E_PFQF_CTL_0);
reg_val = (pf->rss_table_size == 512) ?
@@ -7907,10 +8087,32 @@ static int i40e_config_rss(struct i40e_pf *pf)
(reg_val & ~I40E_PFQF_CTL_0_HASHLUTSIZE_512);
wr32(hw, I40E_PFQF_CTL_0, reg_val);
- if (pf->flags & I40E_FLAG_RSS_AQ_CAPABLE)
- return i40e_config_rss_aq(pf->vsi[pf->lan_vsi], seed);
+ /* Determine the RSS size of the VSI */
+ if (!vsi->rss_size)
+ vsi->rss_size = min_t(int, pf->alloc_rss_size,
+ vsi->num_queue_pairs);
+
+ lut = kzalloc(vsi->rss_table_size, GFP_KERNEL);
+ if (!lut)
+ return -ENOMEM;
+
+ /* Use user configured lut if there is one, otherwise use default */
+ if (vsi->rss_lut_user)
+ memcpy(lut, vsi->rss_lut_user, vsi->rss_table_size);
else
- return i40e_config_rss_reg(pf, seed);
+ i40e_fill_rss_lut(pf, lut, vsi->rss_table_size, vsi->rss_size);
+
+ /* Use user configured hash key if there is one, otherwise
+ * use default.
+ */
+ if (vsi->rss_hkey_user)
+ memcpy(seed, vsi->rss_hkey_user, I40E_HKEY_ARRAY_SIZE);
+ else
+ netdev_rss_key_fill((void *)seed, I40E_HKEY_ARRAY_SIZE);
+ ret = i40e_config_rss(vsi, seed, lut, vsi->rss_table_size);
+ kfree(lut);
+
+ return ret;
}
/**
@@ -7935,13 +8137,28 @@ int i40e_reconfig_rss_queues(struct i40e_pf *pf, int queue_count)
vsi->req_queue_pairs = queue_count;
i40e_prep_for_reset(pf);
- pf->rss_size = new_rss_size;
+ pf->alloc_rss_size = new_rss_size;
i40e_reset_and_rebuild(pf, true);
- i40e_config_rss(pf);
+
+ /* Discard the user configured hash keys and lut, if less
+ * queues are enabled.
+ */
+ if (queue_count < vsi->rss_size) {
+ i40e_clear_rss_config_user(vsi);
+ dev_dbg(&pf->pdev->dev,
+ "discard user configured hash keys and lut\n");
+ }
+
+ /* Reset vsi->rss_size, as number of enabled queues changed */
+ vsi->rss_size = min_t(int, pf->alloc_rss_size,
+ vsi->num_queue_pairs);
+
+ i40e_pf_config_rss(pf);
}
- dev_info(&pf->pdev->dev, "RSS count: %d\n", pf->rss_size);
- return pf->rss_size;
+ dev_info(&pf->pdev->dev, "RSS count/HW max RSS count: %d/%d\n",
+ pf->alloc_rss_size, pf->rss_size_max);
+ return pf->alloc_rss_size;
}
/**
@@ -8112,13 +8329,14 @@ static int i40e_sw_init(struct i40e_pf *pf)
* maximum might end up larger than the available queues
*/
pf->rss_size_max = BIT(pf->hw.func_caps.rss_table_entry_width);
- pf->rss_size = 1;
+ pf->alloc_rss_size = 1;
pf->rss_table_size = pf->hw.func_caps.rss_table_size;
pf->rss_size_max = min_t(int, pf->rss_size_max,
pf->hw.func_caps.num_tx_qp);
if (pf->hw.func_caps.rss) {
pf->flags |= I40E_FLAG_RSS_ENABLED;
- pf->rss_size = min_t(int, pf->rss_size_max, num_online_cpus());
+ pf->alloc_rss_size = min_t(int, pf->rss_size_max,
+ num_online_cpus());
}
/* MFP mode enabled */
@@ -8176,7 +8394,8 @@ static int i40e_sw_init(struct i40e_pf *pf)
I40E_FLAG_HW_ATR_EVICT_CAPABLE |
I40E_FLAG_OUTER_UDP_CSUM_CAPABLE |
I40E_FLAG_WB_ON_ITR_CAPABLE |
- I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE;
+ I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE |
+ I40E_FLAG_GENEVE_OFFLOAD_CAPABLE;
}
pf->eeprom_version = 0xDEAD;
pf->lan_veb = I40E_NO_VEB;
@@ -8275,26 +8494,27 @@ static int i40e_set_features(struct net_device *netdev,
return 0;
}
-#ifdef CONFIG_I40E_VXLAN
+#if IS_ENABLED(CONFIG_VXLAN) || IS_ENABLED(CONFIG_GENEVE)
/**
- * i40e_get_vxlan_port_idx - Lookup a possibly offloaded for Rx UDP port
+ * i40e_get_udp_port_idx - Lookup a possibly offloaded for Rx UDP port
* @pf: board private structure
* @port: The UDP port to look up
*
* Returns the index number or I40E_MAX_PF_UDP_OFFLOAD_PORTS if port not found
**/
-static u8 i40e_get_vxlan_port_idx(struct i40e_pf *pf, __be16 port)
+static u8 i40e_get_udp_port_idx(struct i40e_pf *pf, __be16 port)
{
u8 i;
for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) {
- if (pf->vxlan_ports[i] == port)
+ if (pf->udp_ports[i].index == port)
return i;
}
return i;
}
+#endif
/**
* i40e_add_vxlan_port - Get notifications about VXLAN ports that come up
* @netdev: This physical port's netdev
@@ -8304,6 +8524,7 @@ static u8 i40e_get_vxlan_port_idx(struct i40e_pf *pf, __be16 port)
static void i40e_add_vxlan_port(struct net_device *netdev,
sa_family_t sa_family, __be16 port)
{
+#if IS_ENABLED(CONFIG_VXLAN)
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
@@ -8313,7 +8534,7 @@ static void i40e_add_vxlan_port(struct net_device *netdev,
if (sa_family == AF_INET6)
return;
- idx = i40e_get_vxlan_port_idx(pf, port);
+ idx = i40e_get_udp_port_idx(pf, port);
/* Check if port already exists */
if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) {
@@ -8323,7 +8544,7 @@ static void i40e_add_vxlan_port(struct net_device *netdev,
}
/* Now check if there is space to add the new port */
- next_idx = i40e_get_vxlan_port_idx(pf, 0);
+ next_idx = i40e_get_udp_port_idx(pf, 0);
if (next_idx == I40E_MAX_PF_UDP_OFFLOAD_PORTS) {
netdev_info(netdev, "maximum number of vxlan UDP ports reached, not adding port %d\n",
@@ -8332,9 +8553,11 @@ static void i40e_add_vxlan_port(struct net_device *netdev,
}
/* New port: add it and mark its index in the bitmap */
- pf->vxlan_ports[next_idx] = port;
- pf->pending_vxlan_bitmap |= BIT_ULL(next_idx);
- pf->flags |= I40E_FLAG_VXLAN_FILTER_SYNC;
+ pf->udp_ports[next_idx].index = port;
+ pf->udp_ports[next_idx].type = I40E_AQC_TUNNEL_TYPE_VXLAN;
+ pf->pending_udp_bitmap |= BIT_ULL(next_idx);
+ pf->flags |= I40E_FLAG_UDP_FILTER_SYNC;
+#endif
}
/**
@@ -8346,6 +8569,7 @@ static void i40e_add_vxlan_port(struct net_device *netdev,
static void i40e_del_vxlan_port(struct net_device *netdev,
sa_family_t sa_family, __be16 port)
{
+#if IS_ENABLED(CONFIG_VXLAN)
struct i40e_netdev_priv *np = netdev_priv(netdev);
struct i40e_vsi *vsi = np->vsi;
struct i40e_pf *pf = vsi->back;
@@ -8354,23 +8578,108 @@ static void i40e_del_vxlan_port(struct net_device *netdev,
if (sa_family == AF_INET6)
return;
- idx = i40e_get_vxlan_port_idx(pf, port);
+ idx = i40e_get_udp_port_idx(pf, port);
/* Check if port already exists */
if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) {
/* if port exists, set it to 0 (mark for deletion)
* and make it pending
*/
- pf->vxlan_ports[idx] = 0;
- pf->pending_vxlan_bitmap |= BIT_ULL(idx);
- pf->flags |= I40E_FLAG_VXLAN_FILTER_SYNC;
+ pf->udp_ports[idx].index = 0;
+ pf->pending_udp_bitmap |= BIT_ULL(idx);
+ pf->flags |= I40E_FLAG_UDP_FILTER_SYNC;
} else {
netdev_warn(netdev, "vxlan port %d was not found, not deleting\n",
ntohs(port));
}
+#endif
+}
+
+/**
+ * i40e_add_geneve_port - Get notifications about GENEVE ports that come up
+ * @netdev: This physical port's netdev
+ * @sa_family: Socket Family that GENEVE is notifying us about
+ * @port: New UDP port number that GENEVE started listening to
+ **/
+static void i40e_add_geneve_port(struct net_device *netdev,
+ sa_family_t sa_family, __be16 port)
+{
+#if IS_ENABLED(CONFIG_GENEVE)
+ struct i40e_netdev_priv *np = netdev_priv(netdev);
+ struct i40e_vsi *vsi = np->vsi;
+ struct i40e_pf *pf = vsi->back;
+ u8 next_idx;
+ u8 idx;
+
+ if (sa_family == AF_INET6)
+ return;
+
+ idx = i40e_get_udp_port_idx(pf, port);
+
+ /* Check if port already exists */
+ if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) {
+ netdev_info(netdev, "udp port %d already offloaded\n",
+ ntohs(port));
+ return;
+ }
+
+ /* Now check if there is space to add the new port */
+ next_idx = i40e_get_udp_port_idx(pf, 0);
+
+ if (next_idx == I40E_MAX_PF_UDP_OFFLOAD_PORTS) {
+ netdev_info(netdev, "maximum number of UDP ports reached, not adding port %d\n",
+ ntohs(port));
+ return;
+ }
+
+ /* New port: add it and mark its index in the bitmap */
+ pf->udp_ports[next_idx].index = port;
+ pf->udp_ports[next_idx].type = I40E_AQC_TUNNEL_TYPE_NGE;
+ pf->pending_udp_bitmap |= BIT_ULL(next_idx);
+ pf->flags |= I40E_FLAG_UDP_FILTER_SYNC;
+
+ dev_info(&pf->pdev->dev, "adding geneve port %d\n", ntohs(port));
+#endif
}
+/**
+ * i40e_del_geneve_port - Get notifications about GENEVE ports that go away
+ * @netdev: This physical port's netdev
+ * @sa_family: Socket Family that GENEVE is notifying us about
+ * @port: UDP port number that GENEVE stopped listening to
+ **/
+static void i40e_del_geneve_port(struct net_device *netdev,
+ sa_family_t sa_family, __be16 port)
+{
+#if IS_ENABLED(CONFIG_GENEVE)
+ struct i40e_netdev_priv *np = netdev_priv(netdev);
+ struct i40e_vsi *vsi = np->vsi;
+ struct i40e_pf *pf = vsi->back;
+ u8 idx;
+
+ if (sa_family == AF_INET6)
+ return;
+
+ idx = i40e_get_udp_port_idx(pf, port);
+
+ /* Check if port already exists */
+ if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) {
+ /* if port exists, set it to 0 (mark for deletion)
+ * and make it pending
+ */
+ pf->udp_ports[idx].index = 0;
+ pf->pending_udp_bitmap |= BIT_ULL(idx);
+ pf->flags |= I40E_FLAG_UDP_FILTER_SYNC;
+
+ dev_info(&pf->pdev->dev, "deleting geneve port %d\n",
+ ntohs(port));
+ } else {
+ netdev_warn(netdev, "geneve port %d was not found, not deleting\n",
+ ntohs(port));
+ }
#endif
+}
+
static int i40e_get_phys_port_id(struct net_device *netdev,
struct netdev_phys_item_id *ppid)
{
@@ -8548,7 +8857,10 @@ static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
nlflags, 0, 0, filter_mask, NULL);
}
-#define I40E_MAX_TUNNEL_HDR_LEN 80
+/* Hardware supports L4 tunnel length of 128B (=2^7) which includes
+ * inner mac plus all inner ethertypes.
+ */
+#define I40E_MAX_TUNNEL_HDR_LEN 128
/**
* i40e_features_check - Validate encapsulated packet conforms to limits
* @skb: skb buff
@@ -8560,9 +8872,9 @@ static netdev_features_t i40e_features_check(struct sk_buff *skb,
netdev_features_t features)
{
if (skb->encapsulation &&
- (skb_inner_mac_header(skb) - skb_transport_header(skb) >
+ ((skb_inner_network_header(skb) - skb_transport_header(skb)) >
I40E_MAX_TUNNEL_HDR_LEN))
- return features & ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK);
+ return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
return features;
}
@@ -8595,10 +8907,14 @@ static const struct net_device_ops i40e_netdev_ops = {
.ndo_get_vf_config = i40e_ndo_get_vf_config,
.ndo_set_vf_link_state = i40e_ndo_set_vf_link_state,
.ndo_set_vf_spoofchk = i40e_ndo_set_vf_spoofchk,
-#ifdef CONFIG_I40E_VXLAN
+#if IS_ENABLED(CONFIG_VXLAN)
.ndo_add_vxlan_port = i40e_add_vxlan_port,
.ndo_del_vxlan_port = i40e_del_vxlan_port,
#endif
+#if IS_ENABLED(CONFIG_GENEVE)
+ .ndo_add_geneve_port = i40e_add_geneve_port,
+ .ndo_del_geneve_port = i40e_del_geneve_port,
+#endif
.ndo_get_phys_port_id = i40e_get_phys_port_id,
.ndo_fdb_add = i40e_ndo_fdb_add,
.ndo_features_check = i40e_features_check,
@@ -8632,13 +8948,14 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
np->vsi = vsi;
netdev->hw_enc_features |= NETIF_F_IP_CSUM |
+ NETIF_F_RXCSUM |
NETIF_F_GSO_UDP_TUNNEL |
NETIF_F_GSO_GRE |
NETIF_F_TSO;
netdev->features = NETIF_F_SG |
NETIF_F_IP_CSUM |
- NETIF_F_SCTP_CSUM |
+ NETIF_F_SCTP_CRC |
NETIF_F_HIGHDMA |
NETIF_F_GSO_UDP_TUNNEL |
NETIF_F_GSO_GRE |
@@ -9051,7 +9368,7 @@ int i40e_vsi_release(struct i40e_vsi *vsi)
f->is_vf, f->is_netdev);
spin_unlock_bh(&vsi->mac_filter_list_lock);
- i40e_sync_vsi_filters(vsi, false);
+ i40e_sync_vsi_filters(vsi);
i40e_vsi_delete(vsi);
i40e_vsi_free_q_vectors(vsi);
@@ -9213,6 +9530,44 @@ err_vsi:
}
/**
+ * i40e_macaddr_init - explicitly write the mac address filters.
+ *
+ * @vsi: pointer to the vsi.
+ * @macaddr: the MAC address
+ *
+ * This is needed when the macaddr has been obtained by other
+ * means than the default, e.g., from Open Firmware or IDPROM.
+ * Returns 0 on success, negative on failure
+ **/
+static int i40e_macaddr_init(struct i40e_vsi *vsi, u8 *macaddr)
+{
+ int ret;
+ struct i40e_aqc_add_macvlan_element_data element;
+
+ ret = i40e_aq_mac_address_write(&vsi->back->hw,
+ I40E_AQC_WRITE_TYPE_LAA_WOL,
+ macaddr, NULL);
+ if (ret) {
+ dev_info(&vsi->back->pdev->dev,
+ "Addr change for VSI failed: %d\n", ret);
+ return -EADDRNOTAVAIL;
+ }
+
+ memset(&element, 0, sizeof(element));
+ ether_addr_copy(element.mac_addr, macaddr);
+ element.flags = cpu_to_le16(I40E_AQC_MACVLAN_ADD_PERFECT_MATCH);
+ ret = i40e_aq_add_macvlan(&vsi->back->hw, vsi->seid, &element, 1, NULL);
+ if (ret) {
+ dev_info(&vsi->back->pdev->dev,
+ "add filter failed err %s aq_err %s\n",
+ i40e_stat_str(&vsi->back->hw, ret),
+ i40e_aq_str(&vsi->back->hw,
+ vsi->back->hw.aq.asq_last_status));
+ }
+ return ret;
+}
+
+/**
* i40e_vsi_setup - Set up a VSI by a given type
* @pf: board private structure
* @type: VSI type
@@ -9336,6 +9691,17 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type,
switch (vsi->type) {
/* setup the netdev if needed */
case I40E_VSI_MAIN:
+ /* Apply relevant filters if a platform-specific mac
+ * address was selected.
+ */
+ if (!!(pf->flags & I40E_FLAG_PF_MAC)) {
+ ret = i40e_macaddr_init(vsi, pf->hw.mac.addr);
+ if (ret) {
+ dev_warn(&pf->pdev->dev,
+ "could not set up macaddr; err %d\n",
+ ret);
+ }
+ }
case I40E_VSI_VMDQ2:
case I40E_VSI_FCOE:
ret = i40e_config_netdev(vsi);
@@ -9947,7 +10313,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
* the hash
*/
if ((pf->flags & I40E_FLAG_RSS_ENABLED))
- i40e_config_rss(pf);
+ i40e_pf_config_rss(pf);
/* fill in link information and enable LSE reporting */
i40e_update_link_info(&pf->hw);
@@ -9985,7 +10351,7 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf)
!(pf->flags & I40E_FLAG_MSIX_ENABLED)) {
/* one qp for PF, no queues for anything else */
queues_left = 0;
- pf->rss_size = pf->num_lan_qps = 1;
+ pf->alloc_rss_size = pf->num_lan_qps = 1;
/* make sure all the fancies are disabled */
pf->flags &= ~(I40E_FLAG_RSS_ENABLED |
@@ -10002,7 +10368,7 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf)
I40E_FLAG_FD_ATR_ENABLED |
I40E_FLAG_DCB_CAPABLE))) {
/* one qp for PF */
- pf->rss_size = pf->num_lan_qps = 1;
+ pf->alloc_rss_size = pf->num_lan_qps = 1;
queues_left -= pf->num_lan_qps;
pf->flags &= ~(I40E_FLAG_RSS_ENABLED |
@@ -10072,8 +10438,9 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf)
"qs_avail=%d FD SB=%d lan_qs=%d lan_tc0=%d vf=%d*%d vmdq=%d*%d, remaining=%d\n",
pf->hw.func_caps.num_tx_qp,
!!(pf->flags & I40E_FLAG_FD_SB_ENABLED),
- pf->num_lan_qps, pf->rss_size, pf->num_req_vfs, pf->num_vf_qps,
- pf->num_vmdq_vsis, pf->num_vmdq_qps, queues_left);
+ pf->num_lan_qps, pf->alloc_rss_size, pf->num_req_vfs,
+ pf->num_vf_qps, pf->num_vmdq_vsis, pf->num_vmdq_qps,
+ queues_left);
#ifdef I40E_FCOE
dev_dbg(&pf->pdev->dev, "fcoe queues = %d\n", pf->num_fcoe_qps);
#endif
@@ -10111,55 +10478,86 @@ static int i40e_setup_pf_filter_control(struct i40e_pf *pf)
}
#define INFO_STRING_LEN 255
+#define REMAIN(__x) (INFO_STRING_LEN - (__x))
static void i40e_print_features(struct i40e_pf *pf)
{
struct i40e_hw *hw = &pf->hw;
- char *buf, *string;
+ char *buf;
+ int i;
- string = kzalloc(INFO_STRING_LEN, GFP_KERNEL);
- if (!string) {
- dev_err(&pf->pdev->dev, "Features string allocation failed\n");
+ buf = kmalloc(INFO_STRING_LEN, GFP_KERNEL);
+ if (!buf)
return;
- }
- buf = string;
-
- buf += sprintf(string, "Features: PF-id[%d] ", hw->pf_id);
+ i = snprintf(buf, INFO_STRING_LEN, "Features: PF-id[%d]", hw->pf_id);
#ifdef CONFIG_PCI_IOV
- buf += sprintf(buf, "VFs: %d ", pf->num_req_vfs);
+ i += snprintf(&buf[i], REMAIN(i), " VFs: %d", pf->num_req_vfs);
#endif
- buf += sprintf(buf, "VSIs: %d QP: %d RX: %s ",
- pf->hw.func_caps.num_vsis,
- pf->vsi[pf->lan_vsi]->num_queue_pairs,
- pf->flags & I40E_FLAG_RX_PS_ENABLED ? "PS" : "1BUF");
+ i += snprintf(&buf[i], REMAIN(i), " VSIs: %d QP: %d RX: %s",
+ pf->hw.func_caps.num_vsis,
+ pf->vsi[pf->lan_vsi]->num_queue_pairs,
+ pf->flags & I40E_FLAG_RX_PS_ENABLED ? "PS" : "1BUF");
if (pf->flags & I40E_FLAG_RSS_ENABLED)
- buf += sprintf(buf, "RSS ");
+ i += snprintf(&buf[i], REMAIN(i), " RSS");
if (pf->flags & I40E_FLAG_FD_ATR_ENABLED)
- buf += sprintf(buf, "FD_ATR ");
+ i += snprintf(&buf[i], REMAIN(i), " FD_ATR");
if (pf->flags & I40E_FLAG_FD_SB_ENABLED) {
- buf += sprintf(buf, "FD_SB ");
- buf += sprintf(buf, "NTUPLE ");
+ i += snprintf(&buf[i], REMAIN(i), " FD_SB");
+ i += snprintf(&buf[i], REMAIN(i), " NTUPLE");
}
if (pf->flags & I40E_FLAG_DCB_CAPABLE)
- buf += sprintf(buf, "DCB ");
+ i += snprintf(&buf[i], REMAIN(i), " DCB");
#if IS_ENABLED(CONFIG_VXLAN)
- buf += sprintf(buf, "VxLAN ");
+ i += snprintf(&buf[i], REMAIN(i), " VxLAN");
+#endif
+#if IS_ENABLED(CONFIG_GENEVE)
+ i += snprintf(&buf[i], REMAIN(i), " Geneve");
#endif
if (pf->flags & I40E_FLAG_PTP)
- buf += sprintf(buf, "PTP ");
+ i += snprintf(&buf[i], REMAIN(i), " PTP");
#ifdef I40E_FCOE
if (pf->flags & I40E_FLAG_FCOE_ENABLED)
- buf += sprintf(buf, "FCOE ");
+ i += snprintf(&buf[i], REMAIN(i), " FCOE");
#endif
if (pf->flags & I40E_FLAG_VEB_MODE_ENABLED)
- buf += sprintf(buf, "VEB ");
+ i += snprintf(&buf[i], REMAIN(i), " VEB");
else
- buf += sprintf(buf, "VEPA ");
+ i += snprintf(&buf[i], REMAIN(i), " VEPA");
+
+ dev_info(&pf->pdev->dev, "%s\n", buf);
+ kfree(buf);
+ WARN_ON(i > INFO_STRING_LEN);
+}
+
+/**
+ * i40e_get_platform_mac_addr - get platform-specific MAC address
+ *
+ * @pdev: PCI device information struct
+ * @pf: board private structure
+ *
+ * Look up the MAC address in Open Firmware on systems that support it,
+ * and use IDPROM on SPARC if no OF address is found. On return, the
+ * I40E_FLAG_PF_MAC will be wset in pf->flags if a platform-specific value
+ * has been selected.
+ **/
+static void i40e_get_platform_mac_addr(struct pci_dev *pdev, struct i40e_pf *pf)
+{
+ struct device_node *dp = pci_device_to_OF_node(pdev);
+ const unsigned char *addr;
+ u8 *mac_addr = pf->hw.mac.addr;
- BUG_ON(buf > (string + INFO_STRING_LEN));
- dev_info(&pf->pdev->dev, "%s\n", string);
- kfree(string);
+ pf->flags &= ~I40E_FLAG_PF_MAC;
+ addr = of_get_mac_address(dp);
+ if (addr) {
+ ether_addr_copy(mac_addr, addr);
+ pf->flags |= I40E_FLAG_PF_MAC;
+#ifdef CONFIG_SPARC
+ } else {
+ ether_addr_copy(mac_addr, idprom->id_ethaddr);
+ pf->flags |= I40E_FLAG_PF_MAC;
+#endif /* CONFIG_SPARC */
+ }
}
/**
@@ -10183,6 +10581,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
u16 link_status;
int err;
u32 len;
+ u32 val;
u32 i;
u8 set_fc_aq_fail;
@@ -10295,7 +10694,23 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* set up a default setting for link flow control */
pf->hw.fc.requested_mode = I40E_FC_NONE;
+ /* set up the locks for the AQ, do this only once in probe
+ * and destroy them only once in remove
+ */
+ mutex_init(&hw->aq.asq_mutex);
+ mutex_init(&hw->aq.arq_mutex);
+
err = i40e_init_adminq(hw);
+ if (err) {
+ if (err == I40E_ERR_FIRMWARE_API_VERSION)
+ dev_info(&pdev->dev,
+ "The driver for the device stopped because the NVM image is newer than expected. You must install the most recent version of the network driver.\n");
+ else
+ dev_info(&pdev->dev,
+ "The driver for the device stopped because the device firmware failed to init. Try updating your NVM image.\n");
+
+ goto err_pf_reset;
+ }
/* provide nvm, fw, api versions */
dev_info(&pdev->dev, "fw %d.%d.%05d api %d.%d nvm %s\n",
@@ -10303,12 +10718,6 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
hw->aq.api_maj_ver, hw->aq.api_min_ver,
i40e_nvm_version_str(hw));
- if (err) {
- dev_info(&pdev->dev,
- "The driver for the device stopped because the NVM image is newer than expected. You must install the most recent version of the network driver.\n");
- goto err_pf_reset;
- }
-
if (hw->aq.api_maj_ver == I40E_FW_API_VERSION_MAJOR &&
hw->aq.api_min_ver > I40E_FW_API_VERSION_MINOR)
dev_info(&pdev->dev,
@@ -10361,6 +10770,8 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
}
i40e_get_mac_addr(hw, hw->mac.addr);
+ /* allow a platform config to override the HW addr */
+ i40e_get_platform_mac_addr(pdev, pf);
if (!is_valid_ether_addr(hw->mac.addr)) {
dev_info(&pdev->dev, "invalid MAC address %pM\n", hw->mac.addr);
err = -EIO;
@@ -10405,7 +10816,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* NVM bit on means WoL disabled for the port */
i40e_read_nvm_word(hw, I40E_SR_NVM_WAKE_ON_LAN, &wol_nvm_bits);
- if ((1 << hw->port) & wol_nvm_bits || hw->partition_id != 1)
+ if (BIT (hw->port) & wol_nvm_bits || hw->partition_id != 1)
pf->wol_en = false;
else
pf->wol_en = true;
@@ -10487,6 +10898,17 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
i40e_stat_str(&pf->hw, err),
i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status));
+ /* Reconfigure hardware for allowing smaller MSS in the case
+ * of TSO, so that we avoid the MDD being fired and causing
+ * a reset in the case of small MSS+TSO.
+ */
+ val = rd32(hw, I40E_REG_MSS);
+ if ((val & I40E_REG_MSS_MIN_MASK) > I40E_64BYTE_MSS) {
+ val &= ~I40E_REG_MSS_MIN_MASK;
+ val |= I40E_64BYTE_MSS;
+ wr32(hw, I40E_REG_MSS, val);
+ }
+
if (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver < 33)) ||
(pf->hw.aq.fw_maj_ver < 4)) {
msleep(75);
@@ -10697,7 +11119,6 @@ static void i40e_remove(struct pci_dev *pdev)
set_bit(__I40E_DOWN, &pf->state);
del_timer_sync(&pf->service_timer);
cancel_work_sync(&pf->service_task);
- i40e_fdir_teardown(pf);
if (pf->flags & I40E_FLAG_SRIOV_ENABLED) {
i40e_free_vfs(pf);
@@ -10740,6 +11161,10 @@ static void i40e_remove(struct pci_dev *pdev)
"Failed to destroy the Admin Queue resources: %d\n",
ret_code);
+ /* destroy the locks only once, here */
+ mutex_destroy(&hw->aq.arq_mutex);
+ mutex_destroy(&hw->aq.asq_mutex);
+
/* Clear all dynamic memory lists of rings, q_vectors, and VSIs */
i40e_clear_interrupt_scheme(pf);
for (i = 0; i < pf->num_alloc_vsi; i++) {
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
index 635b3ac17877..720516b0e8ee 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
@@ -235,6 +235,9 @@ static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi,
"Filter deleted for PCTYPE %d loc = %d\n",
fd_data->pctype, fd_data->fd_id);
}
+ if (err)
+ kfree(raw_packet);
+
return err ? -EOPNOTSUPP : 0;
}
@@ -312,6 +315,9 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi,
fd_data->pctype, fd_data->fd_id);
}
+ if (err)
+ kfree(raw_packet);
+
return err ? -EOPNOTSUPP : 0;
}
@@ -322,7 +328,7 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi,
* @fd_data: the flow director data required for the FDir descriptor
* @add: true adds a filter, false removes it
*
- * Always returns -EOPNOTSUPP
+ * Returns 0 if the filters were successfully added or removed
**/
static int i40e_add_del_fdir_sctpv4(struct i40e_vsi *vsi,
struct i40e_fdir_filter *fd_data,
@@ -387,6 +393,9 @@ static int i40e_add_del_fdir_ipv4(struct i40e_vsi *vsi,
}
}
+ if (err)
+ kfree(raw_packet);
+
return err ? -EOPNOTSUPP : 0;
}
@@ -506,9 +515,6 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring,
pf->auto_disable_flags |=
I40E_FLAG_FD_SB_ENABLED;
}
- } else {
- dev_info(&pdev->dev,
- "FD filter programming failed due to incorrect filter parameters\n");
}
} else if (error == BIT(I40E_RX_PROG_STATUS_DESC_NO_FD_ENTRY_SHIFT)) {
if (I40E_DEBUG_FD & pf->hw.debug_mask)
@@ -526,11 +532,7 @@ static void i40e_unmap_and_free_tx_resource(struct i40e_ring *ring,
struct i40e_tx_buffer *tx_buffer)
{
if (tx_buffer->skb) {
- if (tx_buffer->tx_flags & I40E_TX_FLAGS_FD_SB)
- kfree(tx_buffer->raw_buf);
- else
- dev_kfree_skb_any(tx_buffer->skb);
-
+ dev_kfree_skb_any(tx_buffer->skb);
if (dma_unmap_len(tx_buffer, len))
dma_unmap_single(ring->dev,
dma_unmap_addr(tx_buffer, dma),
@@ -542,6 +544,10 @@ static void i40e_unmap_and_free_tx_resource(struct i40e_ring *ring,
dma_unmap_len(tx_buffer, len),
DMA_TO_DEVICE);
}
+
+ if (tx_buffer->tx_flags & I40E_TX_FLAGS_FD_SB)
+ kfree(tx_buffer->raw_buf);
+
tx_buffer->next_to_watch = NULL;
tx_buffer->skb = NULL;
dma_unmap_len_set(tx_buffer, len, 0);
@@ -1374,7 +1380,7 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
if (rx_error & BIT(I40E_RX_DESC_ERROR_PPRS_SHIFT))
return;
- /* If VXLAN traffic has an outer UDPv4 checksum we need to check
+ /* If VXLAN/GENEVE traffic has an outer UDPv4 checksum we need to check
* it in the driver, hardware does not do it for us.
* Since L3L4P bit was set we assume a valid IHL value (>=5)
* so the total length of IPv4 header is IHL*4 bytes
@@ -1416,31 +1422,12 @@ checksum_fail:
}
/**
- * i40e_rx_hash - returns the hash value from the Rx descriptor
- * @ring: descriptor ring
- * @rx_desc: specific descriptor
- **/
-static inline u32 i40e_rx_hash(struct i40e_ring *ring,
- union i40e_rx_desc *rx_desc)
-{
- const __le64 rss_mask =
- cpu_to_le64((u64)I40E_RX_DESC_FLTSTAT_RSS_HASH <<
- I40E_RX_DESC_STATUS_FLTSTAT_SHIFT);
-
- if ((ring->netdev->features & NETIF_F_RXHASH) &&
- (rx_desc->wb.qword1.status_error_len & rss_mask) == rss_mask)
- return le32_to_cpu(rx_desc->wb.qword0.hi_dword.rss);
- else
- return 0;
-}
-
-/**
- * i40e_ptype_to_hash - get a hash type
+ * i40e_ptype_to_htype - get a hash type
* @ptype: the ptype value from the descriptor
*
* Returns a hash type to be used by skb_set_hash
**/
-static inline enum pkt_hash_types i40e_ptype_to_hash(u8 ptype)
+static inline enum pkt_hash_types i40e_ptype_to_htype(u8 ptype)
{
struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(ptype);
@@ -1458,6 +1445,30 @@ static inline enum pkt_hash_types i40e_ptype_to_hash(u8 ptype)
}
/**
+ * i40e_rx_hash - set the hash value in the skb
+ * @ring: descriptor ring
+ * @rx_desc: specific descriptor
+ **/
+static inline void i40e_rx_hash(struct i40e_ring *ring,
+ union i40e_rx_desc *rx_desc,
+ struct sk_buff *skb,
+ u8 rx_ptype)
+{
+ u32 hash;
+ const __le64 rss_mask =
+ cpu_to_le64((u64)I40E_RX_DESC_FLTSTAT_RSS_HASH <<
+ I40E_RX_DESC_STATUS_FLTSTAT_SHIFT);
+
+ if (ring->netdev->features & NETIF_F_RXHASH)
+ return;
+
+ if ((rx_desc->wb.qword1.status_error_len & rss_mask) == rss_mask) {
+ hash = le32_to_cpu(rx_desc->wb.qword0.hi_dword.rss);
+ skb_set_hash(skb, hash, i40e_ptype_to_htype(rx_ptype));
+ }
+}
+
+/**
* i40e_clean_rx_irq_ps - Reclaim resources after receive; packet split
* @rx_ring: rx ring to clean
* @budget: how many cleans we're allowed
@@ -1606,8 +1617,8 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget)
continue;
}
- skb_set_hash(skb, i40e_rx_hash(rx_ring, rx_desc),
- i40e_ptype_to_hash(rx_ptype));
+ i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype);
+
if (unlikely(rx_status & I40E_RXD_QW1_STATUS_TSYNVALID_MASK)) {
i40e_ptp_rx_hwtstamp(vsi->back, skb, (rx_status &
I40E_RXD_QW1_STATUS_TSYNINDX_MASK) >>
@@ -1632,7 +1643,6 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget)
continue;
}
#endif
- skb_mark_napi_id(skb, &rx_ring->q_vector->napi);
i40e_receive_skb(rx_ring, skb, vlan_tag);
rx_desc->wb.qword1.status_error_len = 0;
@@ -1736,8 +1746,7 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget)
continue;
}
- skb_set_hash(skb, i40e_rx_hash(rx_ring, rx_desc),
- i40e_ptype_to_hash(rx_ptype));
+ i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype);
if (unlikely(rx_status & I40E_RXD_QW1_STATUS_TSYNVALID_MASK)) {
i40e_ptp_rx_hwtstamp(vsi->back, skb, (rx_status &
I40E_RXD_QW1_STATUS_TSYNINDX_MASK) >>
@@ -1864,7 +1873,6 @@ enable_int:
q_vector->itr_countdown--;
else
q_vector->itr_countdown = ITR_COUNTDOWN_START;
-
}
/**
@@ -1892,12 +1900,14 @@ int i40e_napi_poll(struct napi_struct *napi, int budget)
return 0;
}
+ /* Clear hung_detected bit */
+ clear_bit(I40E_Q_VECTOR_HUNG_DETECT, &q_vector->hung_detected);
/* Since the actual Tx work is minimal, we can give the Tx a larger
* budget and be more aggressive about cleaning up the Tx descriptors.
*/
i40e_for_each_ring(ring, q_vector->tx) {
clean_complete &= i40e_clean_tx_irq(ring, vsi->work_limit);
- arm_wb |= ring->arm_wb;
+ arm_wb = arm_wb || ring->arm_wb;
ring->arm_wb = false;
}
@@ -1926,8 +1936,10 @@ int i40e_napi_poll(struct napi_struct *napi, int budget)
/* If work not completed, return budget and polling will return */
if (!clean_complete) {
tx_only:
- if (arm_wb)
+ if (arm_wb) {
+ q_vector->tx.ring[0].tx_stats.tx_force_wb++;
i40e_force_wb(vsi, q_vector);
+ }
return budget;
}
@@ -1993,7 +2005,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
if (!(tx_flags & (I40E_TX_FLAGS_IPV4 | I40E_TX_FLAGS_IPV6)))
return;
- if (!(tx_flags & I40E_TX_FLAGS_VXLAN_TUNNEL)) {
+ if (!(tx_flags & I40E_TX_FLAGS_UDP_TUNNEL)) {
/* snag network header to get L4 type and address */
hdr.network = skb_network_header(skb);
@@ -2078,7 +2090,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
I40E_TXD_FLTR_QW1_FD_STATUS_SHIFT;
dtype_cmd |= I40E_TXD_FLTR_QW1_CNT_ENA_MASK;
- if (!(tx_flags & I40E_TX_FLAGS_VXLAN_TUNNEL))
+ if (!(tx_flags & I40E_TX_FLAGS_UDP_TUNNEL))
dtype_cmd |=
((u32)I40E_FD_ATR_STAT_IDX(pf->hw.pf_id) <<
I40E_TXD_FLTR_QW1_CNTINDEX_SHIFT) &
@@ -2187,14 +2199,12 @@ out:
* @tx_ring: ptr to the ring to send
* @skb: ptr to the skb we're sending
* @hdr_len: ptr to the size of the packet header
- * @cd_type_cmd_tso_mss: ptr to u64 object
- * @cd_tunneling: ptr to context descriptor bits
+ * @cd_type_cmd_tso_mss: Quad Word 1
*
* Returns 0 if no TSO can happen, 1 if tso is going, or error
**/
static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb,
- u8 *hdr_len, u64 *cd_type_cmd_tso_mss,
- u32 *cd_tunneling)
+ u8 *hdr_len, u64 *cd_type_cmd_tso_mss)
{
u32 cd_cmd, cd_tso_len, cd_mss;
struct ipv6hdr *ipv6h;
@@ -2247,7 +2257,7 @@ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb,
* @tx_ring: ptr to the ring to send
* @skb: ptr to the skb we're sending
* @tx_flags: the collected send information
- * @cd_type_cmd_tso_mss: ptr to u64 object
+ * @cd_type_cmd_tso_mss: Quad Word 1
*
* Returns 0 if no Tx timestamp can happen and 1 if the timestamp will happen
**/
@@ -2313,7 +2323,7 @@ static void i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags,
oudph = udp_hdr(skb);
oiph = ip_hdr(skb);
l4_tunnel = I40E_TXD_CTX_UDP_TUNNELING;
- *tx_flags |= I40E_TX_FLAGS_VXLAN_TUNNEL;
+ *tx_flags |= I40E_TX_FLAGS_UDP_TUNNEL;
break;
case IPPROTO_GRE:
l4_tunnel = I40E_TXD_CTX_GRE_TUNNELING;
@@ -2807,6 +2817,9 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
int tsyn;
int tso;
+ /* prefetch the data, we'll need it later */
+ prefetch(skb->data);
+
if (0 == i40e_xmit_descriptor_count(skb, tx_ring))
return NETDEV_TX_BUSY;
@@ -2826,8 +2839,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
else if (protocol == htons(ETH_P_IPV6))
tx_flags |= I40E_TX_FLAGS_IPV6;
- tso = i40e_tso(tx_ring, skb, &hdr_len,
- &cd_type_cmd_tso_mss, &cd_tunneling);
+ tso = i40e_tso(tx_ring, skb, &hdr_len, &cd_type_cmd_tso_mss);
if (tso < 0)
goto out_drop;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
index 6779fb771d6a..3f081e25e097 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h
@@ -163,7 +163,7 @@ enum i40e_dyn_idx_t {
#define I40E_TX_FLAGS_FSO BIT(7)
#define I40E_TX_FLAGS_TSYN BIT(8)
#define I40E_TX_FLAGS_FD_SB BIT(9)
-#define I40E_TX_FLAGS_VXLAN_TUNNEL BIT(10)
+#define I40E_TX_FLAGS_UDP_TUNNEL BIT(10)
#define I40E_TX_FLAGS_VLAN_MASK 0xffff0000
#define I40E_TX_FLAGS_VLAN_PRIO_MASK 0xe0000000
#define I40E_TX_FLAGS_VLAN_PRIO_SHIFT 29
@@ -202,6 +202,7 @@ struct i40e_tx_queue_stats {
u64 tx_busy;
u64 tx_done_old;
u64 tx_linearize;
+ u64 tx_force_wb;
};
struct i40e_rx_queue_stats {
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h
index ae879826084b..3226946bf3d4 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h
@@ -153,6 +153,7 @@ struct i40e_virtchnl_vsi_resource {
#define I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR 0x00000020
#define I40E_VIRTCHNL_VF_OFFLOAD_VLAN 0x00010000
#define I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING 0x00020000
+#define I40E_VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2 0x00040000
struct i40e_virtchnl_vf_resource {
u16 num_vsis;
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
index 44462b40f2d7..63e62f9aec6e 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
@@ -290,8 +290,8 @@ static void i40e_config_irq_link_list(struct i40e_vf *vf, u16 vsi_id,
next_q = find_first_bit(&linklistmap,
(I40E_MAX_VSI_QP *
I40E_VIRTCHNL_SUPPORTED_QTYPES));
- vsi_queue_id = next_q/I40E_VIRTCHNL_SUPPORTED_QTYPES;
- qtype = next_q%I40E_VIRTCHNL_SUPPORTED_QTYPES;
+ vsi_queue_id = next_q / I40E_VIRTCHNL_SUPPORTED_QTYPES;
+ qtype = next_q % I40E_VIRTCHNL_SUPPORTED_QTYPES;
pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_id, vsi_queue_id);
reg = ((qtype << I40E_VPINT_LNKLSTN_FIRSTQ_TYPE_SHIFT) | pf_queue_id);
@@ -549,12 +549,15 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type)
i40e_vsi_add_pvid(vsi, vf->port_vlan_id);
spin_lock_bh(&vsi->mac_filter_list_lock);
- f = i40e_add_filter(vsi, vf->default_lan_addr.addr,
- vf->port_vlan_id ? vf->port_vlan_id : -1,
- true, false);
- if (!f)
- dev_info(&pf->pdev->dev,
- "Could not allocate VF MAC addr\n");
+ if (is_valid_ether_addr(vf->default_lan_addr.addr)) {
+ f = i40e_add_filter(vsi, vf->default_lan_addr.addr,
+ vf->port_vlan_id ? vf->port_vlan_id : -1,
+ true, false);
+ if (!f)
+ dev_info(&pf->pdev->dev,
+ "Could not add MAC filter %pM for VF %d\n",
+ vf->default_lan_addr.addr, vf->vf_id);
+ }
f = i40e_add_filter(vsi, brdcast,
vf->port_vlan_id ? vf->port_vlan_id : -1,
true, false);
@@ -565,7 +568,7 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type)
}
/* program mac filter */
- ret = i40e_sync_vsi_filters(vsi, false);
+ ret = i40e_sync_vsi_filters(vsi);
if (ret)
dev_err(&pf->pdev->dev, "Unable to program ucast filters\n");
@@ -1094,8 +1097,8 @@ static int i40e_vc_send_msg_to_vf(struct i40e_vf *vf, u32 v_opcode,
/* single place to detect unsuccessful return values */
if (v_retval) {
vf->num_invalid_msgs++;
- dev_err(&pf->pdev->dev, "Failed opcode %d Error: %d\n",
- v_opcode, v_retval);
+ dev_err(&pf->pdev->dev, "VF %d failed opcode %d, error: %d\n",
+ vf->vf_id, v_opcode, v_retval);
if (vf->num_invalid_msgs >
I40E_DEFAULT_NUM_INVALID_MSGS_ALLOWED) {
dev_err(&pf->pdev->dev,
@@ -1623,7 +1626,8 @@ static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
if (!f) {
dev_err(&pf->pdev->dev,
- "Unable to add VF MAC filter\n");
+ "Unable to add MAC filter %pM for VF %d\n",
+ al->list[i].addr, vf->vf_id);
ret = I40E_ERR_PARAM;
spin_unlock_bh(&vsi->mac_filter_list_lock);
goto error_param;
@@ -1632,8 +1636,10 @@ static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
spin_unlock_bh(&vsi->mac_filter_list_lock);
/* program the updated filter list */
- if (i40e_sync_vsi_filters(vsi, false))
- dev_err(&pf->pdev->dev, "Unable to program VF MAC filters\n");
+ ret = i40e_sync_vsi_filters(vsi);
+ if (ret)
+ dev_err(&pf->pdev->dev, "Unable to program VF %d MAC filters, error %d\n",
+ vf->vf_id, ret);
error_param:
/* send the response to the VF */
@@ -1669,8 +1675,8 @@ static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
for (i = 0; i < al->num_elements; i++) {
if (is_broadcast_ether_addr(al->list[i].addr) ||
is_zero_ether_addr(al->list[i].addr)) {
- dev_err(&pf->pdev->dev, "invalid VF MAC addr %pM\n",
- al->list[i].addr);
+ dev_err(&pf->pdev->dev, "Invalid MAC addr %pM for VF %d\n",
+ al->list[i].addr, vf->vf_id);
ret = I40E_ERR_INVALID_MAC_ADDR;
goto error_param;
}
@@ -1680,13 +1686,19 @@ static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
spin_lock_bh(&vsi->mac_filter_list_lock);
/* delete addresses from the list */
for (i = 0; i < al->num_elements; i++)
- i40e_del_filter(vsi, al->list[i].addr,
- I40E_VLAN_ANY, true, false);
+ if (i40e_del_mac_all_vlan(vsi, al->list[i].addr, true, false)) {
+ ret = I40E_ERR_INVALID_MAC_ADDR;
+ spin_unlock_bh(&vsi->mac_filter_list_lock);
+ goto error_param;
+ }
+
spin_unlock_bh(&vsi->mac_filter_list_lock);
/* program the updated filter list */
- if (i40e_sync_vsi_filters(vsi, false))
- dev_err(&pf->pdev->dev, "Unable to program VF MAC filters\n");
+ ret = i40e_sync_vsi_filters(vsi);
+ if (ret)
+ dev_err(&pf->pdev->dev, "Unable to program VF %d MAC filters, error %d\n",
+ vf->vf_id, ret);
error_param:
/* send the response to the VF */
@@ -1740,8 +1752,8 @@ static int i40e_vc_add_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
if (ret)
dev_err(&pf->pdev->dev,
- "Unable to add VF vlan filter %d, error %d\n",
- vfl->vlan_id[i], ret);
+ "Unable to add VLAN filter %d for VF %d, error %d\n",
+ vfl->vlan_id[i], vf->vf_id, ret);
}
error_param:
@@ -1792,8 +1804,8 @@ static int i40e_vc_remove_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen)
if (ret)
dev_err(&pf->pdev->dev,
- "Unable to delete VF vlan filter %d, error %d\n",
- vfl->vlan_id[i], ret);
+ "Unable to delete VLAN filter %d for VF %d, error %d\n",
+ vfl->vlan_id[i], vf->vf_id, ret);
}
error_param:
@@ -2066,15 +2078,15 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
vf = &(pf->vf[vf_id]);
vsi = pf->vsi[vf->lan_vsi_idx];
if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) {
- dev_err(&pf->pdev->dev,
- "Uninitialized VF %d\n", vf_id);
- ret = -EINVAL;
+ dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n",
+ vf_id);
+ ret = -EAGAIN;
goto error_param;
}
- if (!is_valid_ether_addr(mac)) {
+ if (is_multicast_ether_addr(mac)) {
dev_err(&pf->pdev->dev,
- "Invalid VF ethernet address\n");
+ "Invalid Ethernet address %pM for VF %d\n", mac, vf_id);
ret = -EINVAL;
goto error_param;
}
@@ -2085,9 +2097,10 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
spin_lock_bh(&vsi->mac_filter_list_lock);
/* delete the temporary mac address */
- i40e_del_filter(vsi, vf->default_lan_addr.addr,
- vf->port_vlan_id ? vf->port_vlan_id : -1,
- true, false);
+ if (!is_zero_ether_addr(vf->default_lan_addr.addr))
+ i40e_del_filter(vsi, vf->default_lan_addr.addr,
+ vf->port_vlan_id ? vf->port_vlan_id : -1,
+ true, false);
/* Delete all the filters for this VSI - we're going to kill it
* anyway.
@@ -2099,7 +2112,7 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
dev_info(&pf->pdev->dev, "Setting MAC %pM on VF %d\n", mac, vf_id);
/* program mac filter */
- if (i40e_sync_vsi_filters(vsi, false)) {
+ if (i40e_sync_vsi_filters(vsi)) {
dev_err(&pf->pdev->dev, "Unable to program ucast filters\n");
ret = -EIO;
goto error_param;
@@ -2150,8 +2163,9 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev,
vf = &(pf->vf[vf_id]);
vsi = pf->vsi[vf->lan_vsi_idx];
if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) {
- dev_err(&pf->pdev->dev, "Uninitialized VF %d\n", vf_id);
- ret = -EINVAL;
+ dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n",
+ vf_id);
+ ret = -EAGAIN;
goto error_pvid;
}
@@ -2270,8 +2284,9 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate,
vf = &(pf->vf[vf_id]);
vsi = pf->vsi[vf->lan_vsi_idx];
if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) {
- dev_err(&pf->pdev->dev, "Uninitialized VF %d.\n", vf_id);
- ret = -EINVAL;
+ dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n",
+ vf_id);
+ ret = -EAGAIN;
goto error;
}
@@ -2344,8 +2359,9 @@ int i40e_ndo_get_vf_config(struct net_device *netdev,
/* first vsi is always the LAN vsi */
vsi = pf->vsi[vf->lan_vsi_idx];
if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) {
- dev_err(&pf->pdev->dev, "Uninitialized VF %d\n", vf_id);
- ret = -EINVAL;
+ dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n",
+ vf_id);
+ ret = -EAGAIN;
goto error_param;
}
@@ -2460,6 +2476,12 @@ int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable)
}
vf = &(pf->vf[vf_id]);
+ if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) {
+ dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n",
+ vf_id);
+ ret = -EAGAIN;
+ goto out;
+ }
if (enable == vf->spoofchk)
goto out;
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c
index fd123ca60761..3f65e39b3fe4 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c
+++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c
@@ -551,10 +551,6 @@ i40e_status i40evf_init_adminq(struct i40e_hw *hw)
goto init_adminq_exit;
}
- /* initialize locks */
- mutex_init(&hw->aq.asq_mutex);
- mutex_init(&hw->aq.arq_mutex);
-
/* Set up register offsets */
i40e_adminq_init_regs(hw);
@@ -596,8 +592,6 @@ i40e_status i40evf_shutdown_adminq(struct i40e_hw *hw)
i40e_shutdown_asq(hw);
i40e_shutdown_arq(hw);
- /* destroy the locks */
-
if (hw->nvm_buff.va)
i40e_free_virt_mem(hw, &hw->nvm_buff);
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
index fcb9ef34cc7a..f5b2b369dc7c 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
@@ -227,6 +227,7 @@ enum i40e_admin_queue_opc {
i40e_aqc_opc_nvm_update = 0x0703,
i40e_aqc_opc_nvm_config_read = 0x0704,
i40e_aqc_opc_nvm_config_write = 0x0705,
+ i40e_aqc_opc_oem_post_update = 0x0720,
/* virtualization commands */
i40e_aqc_opc_send_msg_to_pf = 0x0801,
@@ -1888,6 +1889,26 @@ struct i40e_aqc_nvm_config_data_immediate_field {
I40E_CHECK_STRUCT_LEN(0xc, i40e_aqc_nvm_config_data_immediate_field);
+/* OEM Post Update (indirect 0x0720)
+ * no command data struct used
+ */
+ struct i40e_aqc_nvm_oem_post_update {
+#define I40E_AQ_NVM_OEM_POST_UPDATE_EXTERNAL_DATA 0x01
+ u8 sel_data;
+ u8 reserved[7];
+};
+
+I40E_CHECK_STRUCT_LEN(0x8, i40e_aqc_nvm_oem_post_update);
+
+struct i40e_aqc_nvm_oem_post_update_buffer {
+ u8 str_len;
+ u8 dev_addr;
+ __le16 eeprom_addr;
+ u8 data[36];
+};
+
+I40E_CHECK_STRUCT_LEN(0x28, i40e_aqc_nvm_oem_post_update_buffer);
+
/* Send to PF command (indirect 0x0801) id is only used by PF
* Send to VF command (indirect 0x0802) id is only used by PF
* Send to Peer PF command (indirect 0x0803)
@@ -2311,4 +2332,4 @@ struct i40e_aqc_debug_modify_internals {
I40E_CHECK_CMD_LENGTH(i40e_aqc_debug_modify_internals);
-#endif
+#endif /* _I40E_ADMINQ_CMD_H_ */
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_common.c b/drivers/net/ethernet/intel/i40evf/i40e_common.c
index 72b1942a94aa..938783e0baac 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_common.c
+++ b/drivers/net/ethernet/intel/i40evf/i40e_common.c
@@ -44,7 +44,6 @@ i40e_status i40e_set_mac_type(struct i40e_hw *hw)
switch (hw->device_id) {
case I40E_DEV_ID_SFP_XL710:
case I40E_DEV_ID_QEMU:
- case I40E_DEV_ID_KX_A:
case I40E_DEV_ID_KX_B:
case I40E_DEV_ID_KX_C:
case I40E_DEV_ID_QSFP_A:
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_devids.h b/drivers/net/ethernet/intel/i40evf/i40e_devids.h
index e6a39c9862e8..ca8b58c3d1f5 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_devids.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_devids.h
@@ -30,7 +30,6 @@
/* Device IDs */
#define I40E_DEV_ID_SFP_XL710 0x1572
#define I40E_DEV_ID_QEMU 0x1574
-#define I40E_DEV_ID_KX_A 0x157F
#define I40E_DEV_ID_KX_B 0x1580
#define I40E_DEV_ID_KX_C 0x1581
#define I40E_DEV_ID_QSFP_A 0x1583
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
index 47e9a90d6b10..7a00657dacda 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
@@ -51,11 +51,7 @@ static void i40e_unmap_and_free_tx_resource(struct i40e_ring *ring,
struct i40e_tx_buffer *tx_buffer)
{
if (tx_buffer->skb) {
- if (tx_buffer->tx_flags & I40E_TX_FLAGS_FD_SB)
- kfree(tx_buffer->raw_buf);
- else
- dev_kfree_skb_any(tx_buffer->skb);
-
+ dev_kfree_skb_any(tx_buffer->skb);
if (dma_unmap_len(tx_buffer, len))
dma_unmap_single(ring->dev,
dma_unmap_addr(tx_buffer, dma),
@@ -67,6 +63,10 @@ static void i40e_unmap_and_free_tx_resource(struct i40e_ring *ring,
dma_unmap_len(tx_buffer, len),
DMA_TO_DEVICE);
}
+
+ if (tx_buffer->tx_flags & I40E_TX_FLAGS_FD_SB)
+ kfree(tx_buffer->raw_buf);
+
tx_buffer->next_to_watch = NULL;
tx_buffer->skb = NULL;
dma_unmap_len_set(tx_buffer, len, 0);
@@ -127,17 +127,24 @@ void i40evf_free_tx_resources(struct i40e_ring *tx_ring)
}
/**
- * i40e_get_head - Retrieve head from head writeback
- * @tx_ring: tx ring to fetch head of
+ * i40evf_get_tx_pending - how many Tx descriptors not processed
+ * @tx_ring: the ring of descriptors
*
- * Returns value of Tx ring head based on value stored
- * in head write-back location
+ * Since there is no access to the ring head register
+ * in XL710, we need to use our local copies
**/
-static inline u32 i40e_get_head(struct i40e_ring *tx_ring)
+u32 i40evf_get_tx_pending(struct i40e_ring *ring)
{
- void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count;
+ u32 head, tail;
+
+ head = i40e_get_head(ring);
+ tail = readl(ring->tail);
- return le32_to_cpu(*(volatile __le32 *)head);
+ if (head != tail)
+ return (head < tail) ?
+ tail - head : (tail + ring->count - head);
+
+ return 0;
}
#define WB_STRIDE 0x3
@@ -245,16 +252,6 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget)
tx_ring->q_vector->tx.total_bytes += total_bytes;
tx_ring->q_vector->tx.total_packets += total_packets;
- /* check to see if there are any non-cache aligned descriptors
- * waiting to be written back, and kick the hardware to force
- * them to be written back in case of napi polling
- */
- if (budget &&
- !((i & WB_STRIDE) == WB_STRIDE) &&
- !test_bit(__I40E_DOWN, &tx_ring->vsi->state) &&
- (I40E_DESC_UNUSED(tx_ring) != tx_ring->count))
- tx_ring->arm_wb = true;
-
netdev_tx_completed_queue(netdev_get_tx_queue(tx_ring->netdev,
tx_ring->queue_index),
total_packets, total_bytes);
@@ -414,7 +411,7 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc)
return false;
}
-/*
+/**
* i40evf_setup_tx_descriptors - Allocate the Tx descriptors
* @tx_ring: the tx ring to set up
*
@@ -889,31 +886,12 @@ checksum_fail:
}
/**
- * i40e_rx_hash - returns the hash value from the Rx descriptor
- * @ring: descriptor ring
- * @rx_desc: specific descriptor
- **/
-static inline u32 i40e_rx_hash(struct i40e_ring *ring,
- union i40e_rx_desc *rx_desc)
-{
- const __le64 rss_mask =
- cpu_to_le64((u64)I40E_RX_DESC_FLTSTAT_RSS_HASH <<
- I40E_RX_DESC_STATUS_FLTSTAT_SHIFT);
-
- if ((ring->netdev->features & NETIF_F_RXHASH) &&
- (rx_desc->wb.qword1.status_error_len & rss_mask) == rss_mask)
- return le32_to_cpu(rx_desc->wb.qword0.hi_dword.rss);
- else
- return 0;
-}
-
-/**
- * i40e_ptype_to_hash - get a hash type
+ * i40e_ptype_to_htype - get a hash type
* @ptype: the ptype value from the descriptor
*
* Returns a hash type to be used by skb_set_hash
**/
-static inline enum pkt_hash_types i40e_ptype_to_hash(u8 ptype)
+static inline enum pkt_hash_types i40e_ptype_to_htype(u8 ptype)
{
struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(ptype);
@@ -931,6 +909,30 @@ static inline enum pkt_hash_types i40e_ptype_to_hash(u8 ptype)
}
/**
+ * i40e_rx_hash - set the hash value in the skb
+ * @ring: descriptor ring
+ * @rx_desc: specific descriptor
+ **/
+static inline void i40e_rx_hash(struct i40e_ring *ring,
+ union i40e_rx_desc *rx_desc,
+ struct sk_buff *skb,
+ u8 rx_ptype)
+{
+ u32 hash;
+ const __le64 rss_mask =
+ cpu_to_le64((u64)I40E_RX_DESC_FLTSTAT_RSS_HASH <<
+ I40E_RX_DESC_STATUS_FLTSTAT_SHIFT);
+
+ if (ring->netdev->features & NETIF_F_RXHASH)
+ return;
+
+ if ((rx_desc->wb.qword1.status_error_len & rss_mask) == rss_mask) {
+ hash = le32_to_cpu(rx_desc->wb.qword0.hi_dword.rss);
+ skb_set_hash(skb, hash, i40e_ptype_to_htype(rx_ptype));
+ }
+}
+
+/**
* i40e_clean_rx_irq_ps - Reclaim resources after receive; packet split
* @rx_ring: rx ring to clean
* @budget: how many cleans we're allowed
@@ -1071,8 +1073,8 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget)
continue;
}
- skb_set_hash(skb, i40e_rx_hash(rx_ring, rx_desc),
- i40e_ptype_to_hash(rx_ptype));
+ i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype);
+
/* probably a little skewed due to removing CRC */
total_rx_bytes += skb->len;
total_rx_packets++;
@@ -1090,7 +1092,6 @@ static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, int budget)
continue;
}
#endif
- skb_mark_napi_id(skb, &rx_ring->q_vector->napi);
i40e_receive_skb(rx_ring, skb, vlan_tag);
rx_desc->wb.qword1.status_error_len = 0;
@@ -1189,8 +1190,7 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget)
continue;
}
- skb_set_hash(skb, i40e_rx_hash(rx_ring, rx_desc),
- i40e_ptype_to_hash(rx_ptype));
+ i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype);
/* probably a little skewed due to removing CRC */
total_rx_bytes += skb->len;
total_rx_packets++;
@@ -1263,10 +1263,12 @@ static inline void i40e_update_enable_itr(struct i40e_vsi *vsi,
rx = i40e_set_new_dynamic_itr(&q_vector->rx);
rxval = i40e_buildreg_itr(I40E_RX_ITR, q_vector->rx.itr);
}
+
if (ITR_IS_DYNAMIC(vsi->tx_itr_setting)) {
tx = i40e_set_new_dynamic_itr(&q_vector->tx);
txval = i40e_buildreg_itr(I40E_TX_ITR, q_vector->tx.itr);
}
+
if (rx || tx) {
/* get the higher of the two ITR adjustments and
* use the same value for both ITR registers
@@ -1302,7 +1304,6 @@ enable_int:
q_vector->itr_countdown--;
else
q_vector->itr_countdown = ITR_COUNTDOWN_START;
-
}
/**
@@ -1335,7 +1336,7 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget)
*/
i40e_for_each_ring(ring, q_vector->tx) {
clean_complete &= i40e_clean_tx_irq(ring, vsi->work_limit);
- arm_wb |= ring->arm_wb;
+ arm_wb = arm_wb || ring->arm_wb;
ring->arm_wb = false;
}
@@ -1364,8 +1365,10 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget)
/* If work not completed, return budget and polling will return */
if (!clean_complete) {
tx_only:
- if (arm_wb)
+ if (arm_wb) {
+ q_vector->tx.ring[0].tx_stats.tx_force_wb++;
i40evf_force_wb(vsi, q_vector);
+ }
return budget;
}
@@ -1437,13 +1440,12 @@ out:
* @tx_ring: ptr to the ring to send
* @skb: ptr to the skb we're sending
* @hdr_len: ptr to the size of the packet header
- * @cd_tunneling: ptr to context descriptor bits
+ * @cd_type_cmd_tso_mss: Quad Word 1
*
* Returns 0 if no TSO can happen, 1 if tso is going, or error
**/
static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb,
- u8 *hdr_len, u64 *cd_type_cmd_tso_mss,
- u32 *cd_tunneling)
+ u8 *hdr_len, u64 *cd_type_cmd_tso_mss)
{
u32 cd_cmd, cd_tso_len, cd_mss;
struct ipv6hdr *ipv6h;
@@ -1555,7 +1557,6 @@ static void i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags,
*tx_flags |= I40E_TX_FLAGS_IPV6;
}
-
if ((tx_ring->flags & I40E_TXR_FLAGS_OUTER_UDP_CSUM) &&
(l4_tunnel == I40E_TXD_CTX_UDP_TUNNELING) &&
(*cd_tunneling & I40E_TXD_CTX_QW0_EXT_IP_MASK)) {
@@ -1654,7 +1655,7 @@ static void i40e_create_tx_ctx(struct i40e_ring *tx_ring,
context_desc->type_cmd_tso_mss = cpu_to_le64(cd_type_cmd_tso_mss);
}
- /**
+/**
* i40e_chk_linearize - Check if there are more than 8 fragments per packet
* @skb: send buffer
* @tx_flags: collected send information
@@ -1770,6 +1771,9 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
u32 td_tag = 0;
dma_addr_t dma;
u16 gso_segs;
+ u16 desc_count = 0;
+ bool tail_bump = true;
+ bool do_rs = false;
if (tx_flags & I40E_TX_FLAGS_HW_VLAN) {
td_cmd |= I40E_TX_DESC_CMD_IL2TAG1;
@@ -1810,6 +1814,8 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
tx_desc++;
i++;
+ desc_count++;
+
if (i == tx_ring->count) {
tx_desc = I40E_TX_DESC(tx_ring, 0);
i = 0;
@@ -1829,6 +1835,8 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
tx_desc++;
i++;
+ desc_count++;
+
if (i == tx_ring->count) {
tx_desc = I40E_TX_DESC(tx_ring, 0);
i = 0;
@@ -1843,35 +1851,6 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
tx_bi = &tx_ring->tx_bi[i];
}
- /* Place RS bit on last descriptor of any packet that spans across the
- * 4th descriptor (WB_STRIDE aka 0x3) in a 64B cacheline.
- */
-#define WB_STRIDE 0x3
- if (((i & WB_STRIDE) != WB_STRIDE) &&
- (first <= &tx_ring->tx_bi[i]) &&
- (first >= &tx_ring->tx_bi[i & ~WB_STRIDE])) {
- tx_desc->cmd_type_offset_bsz =
- build_ctob(td_cmd, td_offset, size, td_tag) |
- cpu_to_le64((u64)I40E_TX_DESC_CMD_EOP <<
- I40E_TXD_QW1_CMD_SHIFT);
- } else {
- tx_desc->cmd_type_offset_bsz =
- build_ctob(td_cmd, td_offset, size, td_tag) |
- cpu_to_le64((u64)I40E_TXD_CMD <<
- I40E_TXD_QW1_CMD_SHIFT);
- }
-
- netdev_tx_sent_queue(netdev_get_tx_queue(tx_ring->netdev,
- tx_ring->queue_index),
- first->bytecount);
-
- /* Force memory writes to complete before letting h/w
- * know there are new descriptors to fetch. (Only
- * applicable for weak-ordered memory model archs,
- * such as IA-64).
- */
- wmb();
-
/* set next_to_watch value indicating a packet is present */
first->next_to_watch = tx_desc;
@@ -1881,15 +1860,72 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb,
tx_ring->next_to_use = i;
+ netdev_tx_sent_queue(netdev_get_tx_queue(tx_ring->netdev,
+ tx_ring->queue_index),
+ first->bytecount);
i40evf_maybe_stop_tx(tx_ring, DESC_NEEDED);
+
+ /* Algorithm to optimize tail and RS bit setting:
+ * if xmit_more is supported
+ * if xmit_more is true
+ * do not update tail and do not mark RS bit.
+ * if xmit_more is false and last xmit_more was false
+ * if every packet spanned less than 4 desc
+ * then set RS bit on 4th packet and update tail
+ * on every packet
+ * else
+ * update tail and set RS bit on every packet.
+ * if xmit_more is false and last_xmit_more was true
+ * update tail and set RS bit.
+ *
+ * Optimization: wmb to be issued only in case of tail update.
+ * Also optimize the Descriptor WB path for RS bit with the same
+ * algorithm.
+ *
+ * Note: If there are less than 4 packets
+ * pending and interrupts were disabled the service task will
+ * trigger a force WB.
+ */
+ if (skb->xmit_more &&
+ !netif_xmit_stopped(netdev_get_tx_queue(tx_ring->netdev,
+ tx_ring->queue_index))) {
+ tx_ring->flags |= I40E_TXR_FLAGS_LAST_XMIT_MORE_SET;
+ tail_bump = false;
+ } else if (!skb->xmit_more &&
+ !netif_xmit_stopped(netdev_get_tx_queue(tx_ring->netdev,
+ tx_ring->queue_index)) &&
+ (!(tx_ring->flags & I40E_TXR_FLAGS_LAST_XMIT_MORE_SET)) &&
+ (tx_ring->packet_stride < WB_STRIDE) &&
+ (desc_count < WB_STRIDE)) {
+ tx_ring->packet_stride++;
+ } else {
+ tx_ring->packet_stride = 0;
+ tx_ring->flags &= ~I40E_TXR_FLAGS_LAST_XMIT_MORE_SET;
+ do_rs = true;
+ }
+ if (do_rs)
+ tx_ring->packet_stride = 0;
+
+ tx_desc->cmd_type_offset_bsz =
+ build_ctob(td_cmd, td_offset, size, td_tag) |
+ cpu_to_le64((u64)(do_rs ? I40E_TXD_CMD :
+ I40E_TX_DESC_CMD_EOP) <<
+ I40E_TXD_QW1_CMD_SHIFT);
+
/* notify HW of packet */
- if (!skb->xmit_more ||
- netif_xmit_stopped(netdev_get_tx_queue(tx_ring->netdev,
- tx_ring->queue_index)))
- writel(i, tx_ring->tail);
- else
+ if (!tail_bump)
prefetchw(tx_desc + 1);
+ if (tail_bump) {
+ /* Force memory writes to complete before letting h/w
+ * know there are new descriptors to fetch. (Only
+ * applicable for weak-ordered memory model archs,
+ * such as IA-64).
+ */
+ wmb();
+ writel(i, tx_ring->tail);
+ }
+
return;
dma_error:
@@ -1961,6 +1997,9 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
u8 hdr_len = 0;
int tso;
+ /* prefetch the data, we'll need it later */
+ prefetch(skb->data);
+
if (0 == i40evf_xmit_descriptor_count(skb, tx_ring))
return NETDEV_TX_BUSY;
@@ -1980,8 +2019,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
else if (protocol == htons(ETH_P_IPV6))
tx_flags |= I40E_TX_FLAGS_IPV6;
- tso = i40e_tso(tx_ring, skb, &hdr_len,
- &cd_type_cmd_tso_mss, &cd_tunneling);
+ tso = i40e_tso(tx_ring, skb, &hdr_len, &cd_type_cmd_tso_mss);
if (tso < 0)
goto out_drop;
@@ -2029,7 +2067,7 @@ out_drop:
netdev_tx_t i40evf_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
{
struct i40evf_adapter *adapter = netdev_priv(netdev);
- struct i40e_ring *tx_ring = adapter->tx_rings[skb->queue_mapping];
+ struct i40e_ring *tx_ring = &adapter->tx_rings[skb->queue_mapping];
/* hardware can't handle really short frames, hardware padding works
* beyond this point
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h
index ebc1bf77f036..e29bb3e86cfd 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h
@@ -201,6 +201,7 @@ struct i40e_tx_queue_stats {
u64 tx_busy;
u64 tx_done_old;
u64 tx_linearize;
+ u64 tx_force_wb;
};
struct i40e_rx_queue_stats {
@@ -267,6 +268,8 @@ struct i40e_ring {
bool ring_active; /* is ring online or not */
bool arm_wb; /* do something to arm write back */
+ u8 packet_stride;
+#define I40E_TXR_FLAGS_LAST_XMIT_MORE_SET BIT(2)
u16 flags;
#define I40E_TXR_FLAGS_WB_ON_ITR BIT(0)
@@ -321,4 +324,19 @@ int i40evf_setup_rx_descriptors(struct i40e_ring *rx_ring);
void i40evf_free_tx_resources(struct i40e_ring *tx_ring);
void i40evf_free_rx_resources(struct i40e_ring *rx_ring);
int i40evf_napi_poll(struct napi_struct *napi, int budget);
+u32 i40evf_get_tx_pending(struct i40e_ring *ring);
+
+/**
+ * i40e_get_head - Retrieve head from head writeback
+ * @tx_ring: Tx ring to fetch head of
+ *
+ * Returns value of Tx ring head based on value stored
+ * in head write-back location
+ **/
+static inline u32 i40e_get_head(struct i40e_ring *tx_ring)
+{
+ void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count;
+
+ return le32_to_cpu(*(volatile __le32 *)head);
+}
#endif /* _I40E_TXRX_H_ */
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h
index 9f7b279b9d9c..3b9d2037456c 100644
--- a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h
+++ b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h
@@ -153,6 +153,7 @@ struct i40e_virtchnl_vsi_resource {
#define I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR 0x00000020
#define I40E_VIRTCHNL_VF_OFFLOAD_VLAN 0x00010000
#define I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING 0x00020000
+#define I40E_VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2 0x00040000
struct i40e_virtchnl_vf_resource {
u16 num_vsis;
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h
index 22fc3d49c4b9..be1b72b93888 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf.h
+++ b/drivers/net/ethernet/intel/i40evf/i40evf.h
@@ -67,6 +67,8 @@ struct i40e_vsi {
u16 rx_itr_setting;
u16 tx_itr_setting;
u16 qs_handle;
+ u8 *rss_hkey_user; /* User configured hash keys */
+ u8 *rss_lut_user; /* User configured lookup table entries */
};
/* How many Rx Buffers do we bundle into one write to the hardware ? */
@@ -95,10 +97,10 @@ struct i40e_vsi {
#define I40E_TX_DESC(R, i) (&(((struct i40e_tx_desc *)((R)->desc))[i]))
#define I40E_TX_CTXTDESC(R, i) \
(&(((struct i40e_tx_context_desc *)((R)->desc))[i]))
-#define MAX_RX_QUEUES 8
-#define MAX_TX_QUEUES MAX_RX_QUEUES
+#define MAX_QUEUES 16
#define I40EVF_HKEY_ARRAY_SIZE ((I40E_VFQF_HKEY_MAX_INDEX + 1) * 4)
+#define I40EVF_HLUT_ARRAY_SIZE ((I40E_VFQF_HLUT_MAX_INDEX + 1) * 4)
/* MAX_MSIX_Q_VECTORS of these are allocated,
* but we only use one per queue-specific vector.
@@ -142,9 +144,6 @@ struct i40e_q_vector {
#define OTHER_VECTOR 1
#define NONQ_VECS (OTHER_VECTOR)
-#define MAX_MSIX_Q_VECTORS 4
-#define MAX_MSIX_COUNT 5
-
#define MIN_MSIX_Q_VECTORS 1
#define MIN_MSIX_COUNT (MIN_MSIX_Q_VECTORS + NONQ_VECS)
@@ -190,19 +189,19 @@ struct i40evf_adapter {
struct work_struct reset_task;
struct work_struct adminq_task;
struct delayed_work init_task;
- struct i40e_q_vector *q_vector[MAX_MSIX_Q_VECTORS];
+ struct i40e_q_vector *q_vectors;
struct list_head vlan_filter_list;
char misc_vector_name[IFNAMSIZ + 9];
int num_active_queues;
/* TX */
- struct i40e_ring *tx_rings[I40E_MAX_VSI_QP];
+ struct i40e_ring *tx_rings;
u32 tx_timeout_count;
struct list_head mac_filter_list;
u32 tx_desc_count;
/* RX */
- struct i40e_ring *rx_rings[I40E_MAX_VSI_QP];
+ struct i40e_ring *rx_rings;
u64 hw_csum_rx_error;
u32 rx_desc_count;
int num_msix_vectors;
@@ -313,4 +312,8 @@ void i40evf_request_reset(struct i40evf_adapter *adapter);
void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
enum i40e_virtchnl_ops v_opcode,
i40e_status v_retval, u8 *msg, u16 msglen);
+int i40evf_config_rss(struct i40e_vsi *vsi, const u8 *seed, u8 *lut,
+ u16 lut_size);
+int i40evf_get_rss(struct i40e_vsi *vsi, const u8 *seed, u8 *lut,
+ u16 lut_size);
#endif /* _I40EVF_H_ */
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c
index 4790437a50ac..a4c9feb589e7 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c
@@ -121,12 +121,12 @@ static void i40evf_get_ethtool_stats(struct net_device *netdev,
data[i] = *(u64 *)p;
}
for (j = 0; j < adapter->num_active_queues; j++) {
- data[i++] = adapter->tx_rings[j]->stats.packets;
- data[i++] = adapter->tx_rings[j]->stats.bytes;
+ data[i++] = adapter->tx_rings[j].stats.packets;
+ data[i++] = adapter->tx_rings[j].stats.bytes;
}
for (j = 0; j < adapter->num_active_queues; j++) {
- data[i++] = adapter->rx_rings[j]->stats.packets;
- data[i++] = adapter->rx_rings[j]->stats.bytes;
+ data[i++] = adapter->rx_rings[j].stats.packets;
+ data[i++] = adapter->rx_rings[j].stats.bytes;
}
}
@@ -351,7 +351,7 @@ static int i40evf_set_coalesce(struct net_device *netdev,
vsi->tx_itr_setting &= ~I40E_ITR_DYNAMIC;
for (i = 0; i < adapter->num_msix_vectors - NONQ_VECS; i++) {
- q_vector = adapter->q_vector[i];
+ q_vector = &adapter->q_vectors[i];
q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting);
wr32(hw, I40E_VFINT_ITRN1(0, i), q_vector->rx.itr);
q_vector->tx.itr = ITR_TO_REG(vsi->tx_itr_setting);
@@ -634,25 +634,34 @@ static int i40evf_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
u8 *hfunc)
{
struct i40evf_adapter *adapter = netdev_priv(netdev);
- struct i40e_hw *hw = &adapter->hw;
- u32 hlut_val;
- int i, j;
+ struct i40e_vsi *vsi = &adapter->vsi;
+ u8 *seed = NULL, *lut;
+ int ret;
+ u16 i;
if (hfunc)
*hfunc = ETH_RSS_HASH_TOP;
if (!indir)
return 0;
- if (indir) {
- for (i = 0, j = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) {
- hlut_val = rd32(hw, I40E_VFQF_HLUT(i));
- indir[j++] = hlut_val & 0xff;
- indir[j++] = (hlut_val >> 8) & 0xff;
- indir[j++] = (hlut_val >> 16) & 0xff;
- indir[j++] = (hlut_val >> 24) & 0xff;
- }
- }
- return 0;
+ seed = key;
+
+ lut = kzalloc(I40EVF_HLUT_ARRAY_SIZE, GFP_KERNEL);
+ if (!lut)
+ return -ENOMEM;
+
+ ret = i40evf_get_rss(vsi, seed, lut, I40EVF_HLUT_ARRAY_SIZE);
+ if (ret)
+ goto out;
+
+ /* Each 32 bits pointed by 'indir' is stored with a lut entry */
+ for (i = 0; i < I40EVF_HLUT_ARRAY_SIZE; i++)
+ indir[i] = (u32)lut[i];
+
+out:
+ kfree(lut);
+
+ return ret;
}
/**
@@ -668,9 +677,9 @@ static int i40evf_set_rxfh(struct net_device *netdev, const u32 *indir,
const u8 *key, const u8 hfunc)
{
struct i40evf_adapter *adapter = netdev_priv(netdev);
- struct i40e_hw *hw = &adapter->hw;
- u32 hlut_val;
- int i, j;
+ struct i40e_vsi *vsi = &adapter->vsi;
+ u8 *seed = NULL;
+ u16 i;
/* We do not allow change in unsupported parameters */
if (key ||
@@ -679,15 +688,29 @@ static int i40evf_set_rxfh(struct net_device *netdev, const u32 *indir,
if (!indir)
return 0;
- for (i = 0, j = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) {
- hlut_val = indir[j++];
- hlut_val |= indir[j++] << 8;
- hlut_val |= indir[j++] << 16;
- hlut_val |= indir[j++] << 24;
- wr32(hw, I40E_VFQF_HLUT(i), hlut_val);
+ if (key) {
+ if (!vsi->rss_hkey_user) {
+ vsi->rss_hkey_user = kzalloc(I40EVF_HKEY_ARRAY_SIZE,
+ GFP_KERNEL);
+ if (!vsi->rss_hkey_user)
+ return -ENOMEM;
+ }
+ memcpy(vsi->rss_hkey_user, key, I40EVF_HKEY_ARRAY_SIZE);
+ seed = vsi->rss_hkey_user;
+ }
+ if (!vsi->rss_lut_user) {
+ vsi->rss_lut_user = kzalloc(I40EVF_HLUT_ARRAY_SIZE,
+ GFP_KERNEL);
+ if (!vsi->rss_lut_user)
+ return -ENOMEM;
}
- return 0;
+ /* Each 32 bits pointed by 'indir' is stored with a lut entry */
+ for (i = 0; i < I40EVF_HLUT_ARRAY_SIZE; i++)
+ vsi->rss_lut_user[i] = (u8)(indir[i]);
+
+ return i40evf_config_rss(vsi, seed, vsi->rss_lut_user,
+ I40EVF_HLUT_ARRAY_SIZE);
}
static const struct ethtool_ops i40evf_ethtool_ops = {
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
index d962164dfb0f..94da913b151d 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c
@@ -34,7 +34,15 @@ char i40evf_driver_name[] = "i40evf";
static const char i40evf_driver_string[] =
"Intel(R) XL710/X710 Virtual Function Network Driver";
-#define DRV_VERSION "1.3.33"
+#define DRV_KERN "-k"
+
+#define DRV_VERSION_MAJOR 1
+#define DRV_VERSION_MINOR 4
+#define DRV_VERSION_BUILD 4
+#define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \
+ __stringify(DRV_VERSION_MINOR) "." \
+ __stringify(DRV_VERSION_BUILD) \
+ DRV_KERN
const char i40evf_driver_version[] = DRV_VERSION;
static const char i40evf_copyright[] =
"Copyright (c) 2013 - 2015 Intel Corporation.";
@@ -259,7 +267,7 @@ static void i40evf_fire_sw_int(struct i40evf_adapter *adapter, u32 mask)
{
struct i40e_hw *hw = &adapter->hw;
int i;
- uint32_t dyn_ctl;
+ u32 dyn_ctl;
if (mask & 1) {
dyn_ctl = rd32(hw, I40E_VFINT_DYN_CTL01);
@@ -307,10 +315,9 @@ static irqreturn_t i40evf_msix_aq(int irq, void *data)
struct i40e_hw *hw = &adapter->hw;
u32 val;
- /* handle non-queue interrupts */
- rd32(hw, I40E_VFINT_ICR01);
- rd32(hw, I40E_VFINT_ICR0_ENA1);
-
+ /* handle non-queue interrupts, these reads clear the registers */
+ val = rd32(hw, I40E_VFINT_ICR01);
+ val = rd32(hw, I40E_VFINT_ICR0_ENA1);
val = rd32(hw, I40E_VFINT_DYN_CTL01) |
I40E_VFINT_DYN_CTL01_CLEARPBA_MASK;
@@ -348,8 +355,8 @@ static irqreturn_t i40evf_msix_clean_rings(int irq, void *data)
static void
i40evf_map_vector_to_rxq(struct i40evf_adapter *adapter, int v_idx, int r_idx)
{
- struct i40e_q_vector *q_vector = adapter->q_vector[v_idx];
- struct i40e_ring *rx_ring = adapter->rx_rings[r_idx];
+ struct i40e_q_vector *q_vector = &adapter->q_vectors[v_idx];
+ struct i40e_ring *rx_ring = &adapter->rx_rings[r_idx];
rx_ring->q_vector = q_vector;
rx_ring->next = q_vector->rx.ring;
@@ -369,8 +376,8 @@ i40evf_map_vector_to_rxq(struct i40evf_adapter *adapter, int v_idx, int r_idx)
static void
i40evf_map_vector_to_txq(struct i40evf_adapter *adapter, int v_idx, int t_idx)
{
- struct i40e_q_vector *q_vector = adapter->q_vector[v_idx];
- struct i40e_ring *tx_ring = adapter->tx_rings[t_idx];
+ struct i40e_q_vector *q_vector = &adapter->q_vectors[v_idx];
+ struct i40e_ring *tx_ring = &adapter->tx_rings[t_idx];
tx_ring->q_vector = q_vector;
tx_ring->next = q_vector->tx.ring;
@@ -465,7 +472,7 @@ static void i40evf_netpoll(struct net_device *netdev)
return;
for (i = 0; i < q_vectors; i++)
- i40evf_msix_clean_rings(0, adapter->q_vector[i]);
+ i40evf_msix_clean_rings(0, &adapter->q_vectors[i]);
}
#endif
@@ -487,7 +494,7 @@ i40evf_request_traffic_irqs(struct i40evf_adapter *adapter, char *basename)
q_vectors = adapter->num_msix_vectors - NONQ_VECS;
for (vector = 0; vector < q_vectors; vector++) {
- struct i40e_q_vector *q_vector = adapter->q_vector[vector];
+ struct i40e_q_vector *q_vector = &adapter->q_vectors[vector];
if (q_vector->tx.ring && q_vector->rx.ring) {
snprintf(q_vector->name, sizeof(q_vector->name) - 1,
@@ -532,7 +539,7 @@ free_queue_irqs:
adapter->msix_entries[vector + NONQ_VECS].vector,
NULL);
free_irq(adapter->msix_entries[vector + NONQ_VECS].vector,
- adapter->q_vector[vector]);
+ &adapter->q_vectors[vector]);
}
return err;
}
@@ -582,7 +589,7 @@ static void i40evf_free_traffic_irqs(struct i40evf_adapter *adapter)
irq_set_affinity_hint(adapter->msix_entries[i+1].vector,
NULL);
free_irq(adapter->msix_entries[i+1].vector,
- adapter->q_vector[i]);
+ &adapter->q_vectors[i]);
}
}
@@ -611,7 +618,7 @@ static void i40evf_configure_tx(struct i40evf_adapter *adapter)
int i;
for (i = 0; i < adapter->num_active_queues; i++)
- adapter->tx_rings[i]->tail = hw->hw_addr + I40E_QTX_TAIL1(i);
+ adapter->tx_rings[i].tail = hw->hw_addr + I40E_QTX_TAIL1(i);
}
/**
@@ -656,8 +663,8 @@ static void i40evf_configure_rx(struct i40evf_adapter *adapter)
}
for (i = 0; i < adapter->num_active_queues; i++) {
- adapter->rx_rings[i]->tail = hw->hw_addr + I40E_QRX_TAIL1(i);
- adapter->rx_rings[i]->rx_buf_len = rx_buf_len;
+ adapter->rx_rings[i].tail = hw->hw_addr + I40E_QRX_TAIL1(i);
+ adapter->rx_rings[i].rx_buf_len = rx_buf_len;
}
}
@@ -954,7 +961,7 @@ static void i40evf_napi_enable_all(struct i40evf_adapter *adapter)
for (q_idx = 0; q_idx < q_vectors; q_idx++) {
struct napi_struct *napi;
- q_vector = adapter->q_vector[q_idx];
+ q_vector = &adapter->q_vectors[q_idx];
napi = &q_vector->napi;
napi_enable(napi);
}
@@ -971,7 +978,7 @@ static void i40evf_napi_disable_all(struct i40evf_adapter *adapter)
int q_vectors = adapter->num_msix_vectors - NONQ_VECS;
for (q_idx = 0; q_idx < q_vectors; q_idx++) {
- q_vector = adapter->q_vector[q_idx];
+ q_vector = &adapter->q_vectors[q_idx];
napi_disable(&q_vector->napi);
}
}
@@ -992,7 +999,7 @@ static void i40evf_configure(struct i40evf_adapter *adapter)
adapter->aq_required |= I40EVF_FLAG_AQ_CONFIGURE_QUEUES;
for (i = 0; i < adapter->num_active_queues; i++) {
- struct i40e_ring *ring = adapter->rx_rings[i];
+ struct i40e_ring *ring = &adapter->rx_rings[i];
i40evf_alloc_rx_buffers_1buf(ring, ring->count);
ring->next_to_use = ring->count - 1;
@@ -1112,16 +1119,10 @@ i40evf_acquire_msix_vectors(struct i40evf_adapter *adapter, int vectors)
**/
static void i40evf_free_queues(struct i40evf_adapter *adapter)
{
- int i;
-
if (!adapter->vsi_res)
return;
- for (i = 0; i < adapter->num_active_queues; i++) {
- if (adapter->tx_rings[i])
- kfree_rcu(adapter->tx_rings[i], rcu);
- adapter->tx_rings[i] = NULL;
- adapter->rx_rings[i] = NULL;
- }
+ kfree(adapter->tx_rings);
+ kfree(adapter->rx_rings);
}
/**
@@ -1136,13 +1137,20 @@ static int i40evf_alloc_queues(struct i40evf_adapter *adapter)
{
int i;
+ adapter->tx_rings = kcalloc(adapter->num_active_queues,
+ sizeof(struct i40e_ring), GFP_KERNEL);
+ if (!adapter->tx_rings)
+ goto err_out;
+ adapter->rx_rings = kcalloc(adapter->num_active_queues,
+ sizeof(struct i40e_ring), GFP_KERNEL);
+ if (!adapter->rx_rings)
+ goto err_out;
+
for (i = 0; i < adapter->num_active_queues; i++) {
struct i40e_ring *tx_ring;
struct i40e_ring *rx_ring;
- tx_ring = kzalloc(sizeof(*tx_ring) * 2, GFP_KERNEL);
- if (!tx_ring)
- goto err_out;
+ tx_ring = &adapter->tx_rings[i];
tx_ring->queue_index = i;
tx_ring->netdev = adapter->netdev;
@@ -1150,14 +1158,12 @@ static int i40evf_alloc_queues(struct i40evf_adapter *adapter)
tx_ring->count = adapter->tx_desc_count;
if (adapter->flags & I40E_FLAG_WB_ON_ITR_CAPABLE)
tx_ring->flags |= I40E_TXR_FLAGS_WB_ON_ITR;
- adapter->tx_rings[i] = tx_ring;
- rx_ring = &tx_ring[1];
+ rx_ring = &adapter->rx_rings[i];
rx_ring->queue_index = i;
rx_ring->netdev = adapter->netdev;
rx_ring->dev = &adapter->pdev->dev;
rx_ring->count = adapter->rx_desc_count;
- adapter->rx_rings[i] = rx_ring;
}
return 0;
@@ -1207,115 +1213,273 @@ static int i40evf_set_interrupt_capability(struct i40evf_adapter *adapter)
err = i40evf_acquire_msix_vectors(adapter, v_budget);
out:
- adapter->netdev->real_num_tx_queues = pairs;
+ netif_set_real_num_rx_queues(adapter->netdev, pairs);
+ netif_set_real_num_tx_queues(adapter->netdev, pairs);
return err;
}
/**
- * i40e_configure_rss_aq - Prepare for RSS using AQ commands
+ * i40e_config_rss_aq - Prepare for RSS using AQ commands
* @vsi: vsi structure
* @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Return 0 on success, negative on failure
**/
-static void i40evf_configure_rss_aq(struct i40e_vsi *vsi, const u8 *seed)
+static int i40evf_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed,
+ u8 *lut, u16 lut_size)
{
- struct i40e_aqc_get_set_rss_key_data rss_key;
struct i40evf_adapter *adapter = vsi->back;
struct i40e_hw *hw = &adapter->hw;
- int ret = 0, i;
- u8 *rss_lut;
+ int ret = 0;
if (!vsi->id)
- return;
+ return -EINVAL;
if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) {
/* bail because we already have a command pending */
- dev_err(&adapter->pdev->dev, "Cannot confiure RSS, command %d pending\n",
+ dev_err(&adapter->pdev->dev, "Cannot configure RSS, command %d pending\n",
adapter->current_op);
- return;
+ return -EBUSY;
}
- memset(&rss_key, 0, sizeof(rss_key));
- memcpy(&rss_key, seed, sizeof(rss_key));
+ if (seed) {
+ struct i40e_aqc_get_set_rss_key_data *rss_key =
+ (struct i40e_aqc_get_set_rss_key_data *)seed;
+ ret = i40evf_aq_set_rss_key(hw, vsi->id, rss_key);
+ if (ret) {
+ dev_err(&adapter->pdev->dev, "Cannot set RSS key, err %s aq_err %s\n",
+ i40evf_stat_str(hw, ret),
+ i40evf_aq_str(hw, hw->aq.asq_last_status));
+ return ret;
+ }
+ }
- rss_lut = kzalloc(((I40E_VFQF_HLUT_MAX_INDEX + 1) * 4), GFP_KERNEL);
- if (!rss_lut)
- return;
+ if (lut) {
+ ret = i40evf_aq_set_rss_lut(hw, vsi->id, false, lut, lut_size);
+ if (ret) {
+ dev_err(&adapter->pdev->dev,
+ "Cannot set RSS lut, err %s aq_err %s\n",
+ i40evf_stat_str(hw, ret),
+ i40evf_aq_str(hw, hw->aq.asq_last_status));
+ return ret;
+ }
+ }
- /* Populate the LUT with max no. PF queues in round robin fashion */
- for (i = 0; i <= (I40E_VFQF_HLUT_MAX_INDEX * 4); i++)
- rss_lut[i] = i % adapter->num_active_queues;
+ return ret;
+}
- ret = i40evf_aq_set_rss_key(hw, vsi->id, &rss_key);
- if (ret) {
- dev_err(&adapter->pdev->dev,
- "Cannot set RSS key, err %s aq_err %s\n",
- i40evf_stat_str(hw, ret),
- i40evf_aq_str(hw, hw->aq.asq_last_status));
- return;
+/**
+ * i40evf_config_rss_reg - Configure RSS keys and lut by writing registers
+ * @vsi: Pointer to vsi structure
+ * @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Returns 0 on success, negative on failure
+ **/
+static int i40evf_config_rss_reg(struct i40e_vsi *vsi, const u8 *seed,
+ const u8 *lut, u16 lut_size)
+{
+ struct i40evf_adapter *adapter = vsi->back;
+ struct i40e_hw *hw = &adapter->hw;
+ u16 i;
+
+ if (seed) {
+ u32 *seed_dw = (u32 *)seed;
+
+ for (i = 0; i <= I40E_VFQF_HKEY_MAX_INDEX; i++)
+ wr32(hw, I40E_VFQF_HKEY(i), seed_dw[i]);
}
- ret = i40evf_aq_set_rss_lut(hw, vsi->id, false, rss_lut,
- (I40E_VFQF_HLUT_MAX_INDEX + 1) * 4);
- if (ret)
- dev_err(&adapter->pdev->dev,
- "Cannot set RSS lut, err %s aq_err %s\n",
- i40evf_stat_str(hw, ret),
- i40evf_aq_str(hw, hw->aq.asq_last_status));
+ if (lut) {
+ u32 *lut_dw = (u32 *)lut;
+
+ if (lut_size != I40EVF_HLUT_ARRAY_SIZE)
+ return -EINVAL;
+
+ for (i = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++)
+ wr32(hw, I40E_VFQF_HLUT(i), lut_dw[i]);
+ }
+ i40e_flush(hw);
+
+ return 0;
}
/**
- * i40e_configure_rss_reg - Prepare for RSS if used
- * @adapter: board private structure
- * @seed: RSS hash seed
+ * * i40evf_get_rss_aq - Get RSS keys and lut by using AQ commands
+ * @vsi: Pointer to vsi structure
+ * @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Return 0 on success, negative on failure
**/
-static void i40evf_configure_rss_reg(struct i40evf_adapter *adapter,
- const u8 *seed)
+static int i40evf_get_rss_aq(struct i40e_vsi *vsi, const u8 *seed,
+ u8 *lut, u16 lut_size)
{
+ struct i40evf_adapter *adapter = vsi->back;
struct i40e_hw *hw = &adapter->hw;
- u32 *seed_dw = (u32 *)seed;
- u32 cqueue = 0;
- u32 lut = 0;
- int i, j;
+ int ret = 0;
- /* Fill out hash function seed */
- for (i = 0; i <= I40E_VFQF_HKEY_MAX_INDEX; i++)
- wr32(hw, I40E_VFQF_HKEY(i), seed_dw[i]);
-
- /* Populate the LUT with max no. PF queues in round robin fashion */
- for (i = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) {
- lut = 0;
- for (j = 0; j < 4; j++) {
- if (cqueue == adapter->num_active_queues)
- cqueue = 0;
- lut |= ((cqueue) << (8 * j));
- cqueue++;
+ if (seed) {
+ ret = i40evf_aq_get_rss_key(hw, vsi->id,
+ (struct i40e_aqc_get_set_rss_key_data *)seed);
+ if (ret) {
+ dev_err(&adapter->pdev->dev,
+ "Cannot get RSS key, err %s aq_err %s\n",
+ i40evf_stat_str(hw, ret),
+ i40evf_aq_str(hw, hw->aq.asq_last_status));
+ return ret;
}
- wr32(hw, I40E_VFQF_HLUT(i), lut);
}
- i40e_flush(hw);
+
+ if (lut) {
+ ret = i40evf_aq_get_rss_lut(hw, vsi->id, seed, lut, lut_size);
+ if (ret) {
+ dev_err(&adapter->pdev->dev,
+ "Cannot get RSS lut, err %s aq_err %s\n",
+ i40evf_stat_str(hw, ret),
+ i40evf_aq_str(hw, hw->aq.asq_last_status));
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * * i40evf_get_rss_reg - Get RSS keys and lut by reading registers
+ * @vsi: Pointer to vsi structure
+ * @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Returns 0 on success, negative on failure
+ **/
+static int i40evf_get_rss_reg(struct i40e_vsi *vsi, const u8 *seed,
+ const u8 *lut, u16 lut_size)
+{
+ struct i40evf_adapter *adapter = vsi->back;
+ struct i40e_hw *hw = &adapter->hw;
+ u16 i;
+
+ if (seed) {
+ u32 *seed_dw = (u32 *)seed;
+
+ for (i = 0; i <= I40E_VFQF_HKEY_MAX_INDEX; i++)
+ seed_dw[i] = rd32(hw, I40E_VFQF_HKEY(i));
+ }
+
+ if (lut) {
+ u32 *lut_dw = (u32 *)lut;
+
+ if (lut_size != I40EVF_HLUT_ARRAY_SIZE)
+ return -EINVAL;
+
+ for (i = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++)
+ lut_dw[i] = rd32(hw, I40E_VFQF_HLUT(i));
+ }
+
+ return 0;
+}
+
+/**
+ * i40evf_config_rss - Configure RSS keys and lut
+ * @vsi: Pointer to vsi structure
+ * @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Returns 0 on success, negative on failure
+ **/
+int i40evf_config_rss(struct i40e_vsi *vsi, const u8 *seed,
+ u8 *lut, u16 lut_size)
+{
+ struct i40evf_adapter *adapter = vsi->back;
+
+ if (RSS_AQ(adapter))
+ return i40evf_config_rss_aq(vsi, seed, lut, lut_size);
+ else
+ return i40evf_config_rss_reg(vsi, seed, lut, lut_size);
+}
+
+/**
+ * i40evf_get_rss - Get RSS keys and lut
+ * @vsi: Pointer to vsi structure
+ * @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Returns 0 on success, negative on failure
+ **/
+int i40evf_get_rss(struct i40e_vsi *vsi, const u8 *seed, u8 *lut, u16 lut_size)
+{
+ struct i40evf_adapter *adapter = vsi->back;
+
+ if (RSS_AQ(adapter))
+ return i40evf_get_rss_aq(vsi, seed, lut, lut_size);
+ else
+ return i40evf_get_rss_reg(vsi, seed, lut, lut_size);
+}
+
+/**
+ * i40evf_fill_rss_lut - Fill the lut with default values
+ * @lut: Lookup table to be filled with
+ * @rss_table_size: Lookup table size
+ * @rss_size: Range of queue number for hashing
+ **/
+static void i40evf_fill_rss_lut(u8 *lut, u16 rss_table_size, u16 rss_size)
+{
+ u16 i;
+
+ for (i = 0; i < rss_table_size; i++)
+ lut[i] = i % rss_size;
}
/**
- * i40evf_configure_rss - Prepare for RSS
+ * i40evf_init_rss - Prepare for RSS
* @adapter: board private structure
+ *
+ * Return 0 on success, negative on failure
**/
-static void i40evf_configure_rss(struct i40evf_adapter *adapter)
+static int i40evf_init_rss(struct i40evf_adapter *adapter)
{
+ struct i40e_vsi *vsi = &adapter->vsi;
struct i40e_hw *hw = &adapter->hw;
u8 seed[I40EVF_HKEY_ARRAY_SIZE];
u64 hena;
-
- netdev_rss_key_fill((void *)seed, I40EVF_HKEY_ARRAY_SIZE);
+ u8 *lut;
+ int ret;
/* Enable PCTYPES for RSS, TCP/UDP with IPv4/IPv6 */
hena = I40E_DEFAULT_RSS_HENA;
wr32(hw, I40E_VFQF_HENA(0), (u32)hena);
wr32(hw, I40E_VFQF_HENA(1), (u32)(hena >> 32));
- if (RSS_AQ(adapter))
- i40evf_configure_rss_aq(&adapter->vsi, seed);
+ lut = kzalloc(I40EVF_HLUT_ARRAY_SIZE, GFP_KERNEL);
+ if (!lut)
+ return -ENOMEM;
+
+ /* Use user configured lut if there is one, otherwise use default */
+ if (vsi->rss_lut_user)
+ memcpy(lut, vsi->rss_lut_user, I40EVF_HLUT_ARRAY_SIZE);
else
- i40evf_configure_rss_reg(adapter, seed);
+ i40evf_fill_rss_lut(lut, I40EVF_HLUT_ARRAY_SIZE,
+ adapter->num_active_queues);
+
+ /* Use user configured hash key if there is one, otherwise
+ * user default.
+ */
+ if (vsi->rss_hkey_user)
+ memcpy(seed, vsi->rss_hkey_user, I40EVF_HKEY_ARRAY_SIZE);
+ else
+ netdev_rss_key_fill((void *)seed, I40EVF_HKEY_ARRAY_SIZE);
+ ret = i40evf_config_rss(vsi, seed, lut, I40EVF_HLUT_ARRAY_SIZE);
+ kfree(lut);
+
+ return ret;
}
/**
@@ -1327,21 +1491,22 @@ static void i40evf_configure_rss(struct i40evf_adapter *adapter)
**/
static int i40evf_alloc_q_vectors(struct i40evf_adapter *adapter)
{
- int q_idx, num_q_vectors;
+ int q_idx = 0, num_q_vectors;
struct i40e_q_vector *q_vector;
num_q_vectors = adapter->num_msix_vectors - NONQ_VECS;
+ adapter->q_vectors = kcalloc(num_q_vectors, sizeof(*q_vector),
+ GFP_KERNEL);
+ if (!adapter->q_vectors)
+ goto err_out;
for (q_idx = 0; q_idx < num_q_vectors; q_idx++) {
- q_vector = kzalloc(sizeof(*q_vector), GFP_KERNEL);
- if (!q_vector)
- goto err_out;
+ q_vector = &adapter->q_vectors[q_idx];
q_vector->adapter = adapter;
q_vector->vsi = &adapter->vsi;
q_vector->v_idx = q_idx;
netif_napi_add(adapter->netdev, &q_vector->napi,
i40evf_napi_poll, NAPI_POLL_WEIGHT);
- adapter->q_vector[q_idx] = q_vector;
}
return 0;
@@ -1349,11 +1514,10 @@ static int i40evf_alloc_q_vectors(struct i40evf_adapter *adapter)
err_out:
while (q_idx) {
q_idx--;
- q_vector = adapter->q_vector[q_idx];
+ q_vector = &adapter->q_vectors[q_idx];
netif_napi_del(&q_vector->napi);
- kfree(q_vector);
- adapter->q_vector[q_idx] = NULL;
}
+ kfree(adapter->q_vectors);
return -ENOMEM;
}
@@ -1374,13 +1538,11 @@ static void i40evf_free_q_vectors(struct i40evf_adapter *adapter)
napi_vectors = adapter->num_active_queues;
for (q_idx = 0; q_idx < num_q_vectors; q_idx++) {
- struct i40e_q_vector *q_vector = adapter->q_vector[q_idx];
-
- adapter->q_vector[q_idx] = NULL;
+ struct i40e_q_vector *q_vector = &adapter->q_vectors[q_idx];
if (q_idx < napi_vectors)
netif_napi_del(&q_vector->napi);
- kfree(q_vector);
}
+ kfree(adapter->q_vectors);
}
/**
@@ -1439,6 +1601,22 @@ err_set_interrupt:
}
/**
+ * i40evf_clear_rss_config_user - Clear user configurations of RSS
+ * @vsi: Pointer to VSI structure
+ **/
+static void i40evf_clear_rss_config_user(struct i40e_vsi *vsi)
+{
+ if (!vsi)
+ return;
+
+ kfree(vsi->rss_hkey_user);
+ vsi->rss_hkey_user = NULL;
+
+ kfree(vsi->rss_lut_user);
+ vsi->rss_lut_user = NULL;
+}
+
+/**
* i40evf_watchdog_timer - Periodic call-back timer
* @data: pointer to adapter disguised as unsigned long
**/
@@ -1565,7 +1743,7 @@ static void i40evf_watchdog_task(struct work_struct *work)
* PF, so we don't have to set current_op as we will
* not get a response through the ARQ.
*/
- i40evf_configure_rss(adapter);
+ i40evf_init_rss(adapter);
adapter->aq_required &= ~I40EVF_FLAG_AQ_CONFIGURE_RSS;
goto watchdog_done;
}
@@ -1864,9 +2042,12 @@ void i40evf_free_all_tx_resources(struct i40evf_adapter *adapter)
{
int i;
+ if (!adapter->tx_rings)
+ return;
+
for (i = 0; i < adapter->num_active_queues; i++)
- if (adapter->tx_rings[i]->desc)
- i40evf_free_tx_resources(adapter->tx_rings[i]);
+ if (adapter->tx_rings[i].desc)
+ i40evf_free_tx_resources(&adapter->tx_rings[i]);
}
/**
@@ -1884,8 +2065,8 @@ static int i40evf_setup_all_tx_resources(struct i40evf_adapter *adapter)
int i, err = 0;
for (i = 0; i < adapter->num_active_queues; i++) {
- adapter->tx_rings[i]->count = adapter->tx_desc_count;
- err = i40evf_setup_tx_descriptors(adapter->tx_rings[i]);
+ adapter->tx_rings[i].count = adapter->tx_desc_count;
+ err = i40evf_setup_tx_descriptors(&adapter->tx_rings[i]);
if (!err)
continue;
dev_err(&adapter->pdev->dev,
@@ -1911,8 +2092,8 @@ static int i40evf_setup_all_rx_resources(struct i40evf_adapter *adapter)
int i, err = 0;
for (i = 0; i < adapter->num_active_queues; i++) {
- adapter->rx_rings[i]->count = adapter->rx_desc_count;
- err = i40evf_setup_rx_descriptors(adapter->rx_rings[i]);
+ adapter->rx_rings[i].count = adapter->rx_desc_count;
+ err = i40evf_setup_rx_descriptors(&adapter->rx_rings[i]);
if (!err)
continue;
dev_err(&adapter->pdev->dev,
@@ -1932,9 +2113,12 @@ void i40evf_free_all_rx_resources(struct i40evf_adapter *adapter)
{
int i;
+ if (!adapter->rx_rings)
+ return;
+
for (i = 0; i < adapter->num_active_queues; i++)
- if (adapter->rx_rings[i]->desc)
- i40evf_free_rx_resources(adapter->rx_rings[i]);
+ if (adapter->rx_rings[i].desc)
+ i40evf_free_rx_resources(&adapter->rx_rings[i]);
}
/**
@@ -2137,7 +2321,7 @@ int i40evf_process_config(struct i40evf_adapter *adapter)
netdev->features |= NETIF_F_HIGHDMA |
NETIF_F_SG |
NETIF_F_IP_CSUM |
- NETIF_F_SCTP_CSUM |
+ NETIF_F_SCTP_CRC |
NETIF_F_IPV6_CSUM |
NETIF_F_TSO |
NETIF_F_TSO6 |
@@ -2263,6 +2447,14 @@ static void i40evf_init_task(struct work_struct *work)
if (err == I40E_ERR_ADMIN_QUEUE_NO_WORK) {
err = i40evf_send_vf_config_msg(adapter);
goto err;
+ } else if (err == I40E_ERR_PARAM) {
+ /* We only get ERR_PARAM if the device is in a very bad
+ * state or if we've been disabled for previous bad
+ * behavior. Either way, we're done now.
+ */
+ i40evf_shutdown_adminq(hw);
+ dev_err(&pdev->dev, "Unable to get VF config due to PF error condition, not retrying\n");
+ return;
}
if (err) {
dev_err(&pdev->dev, "Unable to get VF config (%d)\n",
@@ -2313,7 +2505,7 @@ static void i40evf_init_task(struct work_struct *work)
I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR)
adapter->flags |= I40EVF_FLAG_WB_ON_ITR_CAPABLE;
if (!RSS_AQ(adapter))
- i40evf_configure_rss(adapter);
+ i40evf_init_rss(adapter);
err = i40evf_request_misc_irq(adapter);
if (err)
goto err_sw_init;
@@ -2334,7 +2526,6 @@ static void i40evf_init_task(struct work_struct *work)
if (netdev->features & NETIF_F_GRO)
dev_info(&pdev->dev, "GRO is enabled\n");
- dev_info(&pdev->dev, "%s\n", i40evf_driver_string);
adapter->state = __I40EVF_DOWN;
set_bit(__I40E_DOWN, &adapter->vsi.state);
i40evf_misc_irq_enable(adapter);
@@ -2343,7 +2534,7 @@ static void i40evf_init_task(struct work_struct *work)
adapter->aq_required |= I40EVF_FLAG_AQ_CONFIGURE_RSS;
mod_timer_pending(&adapter->watchdog_timer, jiffies + 1);
} else {
- i40evf_configure_rss(adapter);
+ i40evf_init_rss(adapter);
}
return;
restart:
@@ -2438,8 +2629,7 @@ static int i40evf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_set_master(pdev);
- netdev = alloc_etherdev_mq(sizeof(struct i40evf_adapter),
- MAX_TX_QUEUES);
+ netdev = alloc_etherdev_mq(sizeof(struct i40evf_adapter), MAX_QUEUES);
if (!netdev) {
err = -ENOMEM;
goto err_alloc_etherdev;
@@ -2476,6 +2666,12 @@ static int i40evf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
hw->bus.device = PCI_SLOT(pdev->devfn);
hw->bus.func = PCI_FUNC(pdev->devfn);
+ /* set up the locks for the AQ, do this only once in probe
+ * and destroy them only once in remove
+ */
+ mutex_init(&hw->aq.asq_mutex);
+ mutex_init(&hw->aq.arq_mutex);
+
INIT_LIST_HEAD(&adapter->mac_filter_list);
INIT_LIST_HEAD(&adapter->vlan_filter_list);
@@ -2626,9 +2822,16 @@ static void i40evf_remove(struct pci_dev *pdev)
flush_scheduled_work();
+ /* Clear user configurations for RSS */
+ i40evf_clear_rss_config_user(&adapter->vsi);
+
if (hw->aq.asq.count)
i40evf_shutdown_adminq(hw);
+ /* destroy the locks only once, here */
+ mutex_destroy(&hw->aq.arq_mutex);
+ mutex_destroy(&hw->aq.asq_mutex);
+
iounmap(hw->hw_addr);
pci_release_regions(pdev);
diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
index 32e620e1eb5c..c1c526283757 100644
--- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
+++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c
@@ -157,7 +157,9 @@ int i40evf_send_vf_config_msg(struct i40evf_adapter *adapter)
I40E_VIRTCHNL_VF_OFFLOAD_RSS_AQ |
I40E_VIRTCHNL_VF_OFFLOAD_RSS_REG |
I40E_VIRTCHNL_VF_OFFLOAD_VLAN |
- I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR;
+ I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR |
+ I40E_VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2;
+
adapter->current_op = I40E_VIRTCHNL_OP_GET_VF_RESOURCES;
adapter->aq_required &= ~I40EVF_FLAG_AQ_GET_CONFIG;
if (PF_IS_V11(adapter))
@@ -242,7 +244,7 @@ void i40evf_configure_queues(struct i40evf_adapter *adapter)
adapter->current_op = I40E_VIRTCHNL_OP_CONFIG_VSI_QUEUES;
len = sizeof(struct i40e_virtchnl_vsi_queue_config_info) +
(sizeof(struct i40e_virtchnl_queue_pair_info) * pairs);
- vqci = kzalloc(len, GFP_ATOMIC);
+ vqci = kzalloc(len, GFP_KERNEL);
if (!vqci)
return;
@@ -255,19 +257,19 @@ void i40evf_configure_queues(struct i40evf_adapter *adapter)
for (i = 0; i < pairs; i++) {
vqpi->txq.vsi_id = vqci->vsi_id;
vqpi->txq.queue_id = i;
- vqpi->txq.ring_len = adapter->tx_rings[i]->count;
- vqpi->txq.dma_ring_addr = adapter->tx_rings[i]->dma;
+ vqpi->txq.ring_len = adapter->tx_rings[i].count;
+ vqpi->txq.dma_ring_addr = adapter->tx_rings[i].dma;
vqpi->txq.headwb_enabled = 1;
vqpi->txq.dma_headwb_addr = vqpi->txq.dma_ring_addr +
(vqpi->txq.ring_len * sizeof(struct i40e_tx_desc));
vqpi->rxq.vsi_id = vqci->vsi_id;
vqpi->rxq.queue_id = i;
- vqpi->rxq.ring_len = adapter->rx_rings[i]->count;
- vqpi->rxq.dma_ring_addr = adapter->rx_rings[i]->dma;
+ vqpi->rxq.ring_len = adapter->rx_rings[i].count;
+ vqpi->rxq.dma_ring_addr = adapter->rx_rings[i].dma;
vqpi->rxq.max_pkt_size = adapter->netdev->mtu
+ ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN;
- vqpi->rxq.databuffer_size = adapter->rx_rings[i]->rx_buf_len;
+ vqpi->rxq.databuffer_size = adapter->rx_rings[i].rx_buf_len;
vqpi++;
}
@@ -353,14 +355,14 @@ void i40evf_map_queues(struct i40evf_adapter *adapter)
len = sizeof(struct i40e_virtchnl_irq_map_info) +
(adapter->num_msix_vectors *
sizeof(struct i40e_virtchnl_vector_map));
- vimi = kzalloc(len, GFP_ATOMIC);
+ vimi = kzalloc(len, GFP_KERNEL);
if (!vimi)
return;
vimi->num_vectors = adapter->num_msix_vectors;
/* Queue vectors first */
for (v_idx = 0; v_idx < q_vectors; v_idx++) {
- q_vector = adapter->q_vector[v_idx];
+ q_vector = adapter->q_vectors + v_idx;
vimi->vecmap[v_idx].vsi_id = adapter->vsi_res->vsi_id;
vimi->vecmap[v_idx].vector_id = v_idx + NONQ_VECS;
vimi->vecmap[v_idx].txq_map = q_vector->ring_mask;
@@ -391,6 +393,7 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter)
struct i40e_virtchnl_ether_addr_list *veal;
int len, i = 0, count = 0;
struct i40evf_mac_filter *f;
+ bool more = false;
if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) {
/* bail because we already have a command pending */
@@ -415,10 +418,12 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter)
count = (I40EVF_MAX_AQ_BUF_SIZE -
sizeof(struct i40e_virtchnl_ether_addr_list)) /
sizeof(struct i40e_virtchnl_ether_addr);
- len = I40EVF_MAX_AQ_BUF_SIZE;
+ len = sizeof(struct i40e_virtchnl_ether_addr_list) +
+ (count * sizeof(struct i40e_virtchnl_ether_addr));
+ more = true;
}
- veal = kzalloc(len, GFP_ATOMIC);
+ veal = kzalloc(len, GFP_KERNEL);
if (!veal)
return;
@@ -431,7 +436,8 @@ void i40evf_add_ether_addrs(struct i40evf_adapter *adapter)
f->add = false;
}
}
- adapter->aq_required &= ~I40EVF_FLAG_AQ_ADD_MAC_FILTER;
+ if (!more)
+ adapter->aq_required &= ~I40EVF_FLAG_AQ_ADD_MAC_FILTER;
i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_ADD_ETHER_ADDRESS,
(u8 *)veal, len);
kfree(veal);
@@ -450,6 +456,7 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter)
struct i40e_virtchnl_ether_addr_list *veal;
struct i40evf_mac_filter *f, *ftmp;
int len, i = 0, count = 0;
+ bool more = false;
if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) {
/* bail because we already have a command pending */
@@ -474,9 +481,11 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter)
count = (I40EVF_MAX_AQ_BUF_SIZE -
sizeof(struct i40e_virtchnl_ether_addr_list)) /
sizeof(struct i40e_virtchnl_ether_addr);
- len = I40EVF_MAX_AQ_BUF_SIZE;
+ len = sizeof(struct i40e_virtchnl_ether_addr_list) +
+ (count * sizeof(struct i40e_virtchnl_ether_addr));
+ more = true;
}
- veal = kzalloc(len, GFP_ATOMIC);
+ veal = kzalloc(len, GFP_KERNEL);
if (!veal)
return;
@@ -490,7 +499,8 @@ void i40evf_del_ether_addrs(struct i40evf_adapter *adapter)
kfree(f);
}
}
- adapter->aq_required &= ~I40EVF_FLAG_AQ_DEL_MAC_FILTER;
+ if (!more)
+ adapter->aq_required &= ~I40EVF_FLAG_AQ_DEL_MAC_FILTER;
i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_DEL_ETHER_ADDRESS,
(u8 *)veal, len);
kfree(veal);
@@ -509,6 +519,7 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter)
struct i40e_virtchnl_vlan_filter_list *vvfl;
int len, i = 0, count = 0;
struct i40evf_vlan_filter *f;
+ bool more = false;
if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) {
/* bail because we already have a command pending */
@@ -534,9 +545,11 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter)
count = (I40EVF_MAX_AQ_BUF_SIZE -
sizeof(struct i40e_virtchnl_vlan_filter_list)) /
sizeof(u16);
- len = I40EVF_MAX_AQ_BUF_SIZE;
+ len = sizeof(struct i40e_virtchnl_vlan_filter_list) +
+ (count * sizeof(u16));
+ more = true;
}
- vvfl = kzalloc(len, GFP_ATOMIC);
+ vvfl = kzalloc(len, GFP_KERNEL);
if (!vvfl)
return;
@@ -549,7 +562,8 @@ void i40evf_add_vlans(struct i40evf_adapter *adapter)
f->add = false;
}
}
- adapter->aq_required &= ~I40EVF_FLAG_AQ_ADD_VLAN_FILTER;
+ if (!more)
+ adapter->aq_required &= ~I40EVF_FLAG_AQ_ADD_VLAN_FILTER;
i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_ADD_VLAN, (u8 *)vvfl, len);
kfree(vvfl);
}
@@ -567,6 +581,7 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter)
struct i40e_virtchnl_vlan_filter_list *vvfl;
struct i40evf_vlan_filter *f, *ftmp;
int len, i = 0, count = 0;
+ bool more = false;
if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) {
/* bail because we already have a command pending */
@@ -592,9 +607,11 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter)
count = (I40EVF_MAX_AQ_BUF_SIZE -
sizeof(struct i40e_virtchnl_vlan_filter_list)) /
sizeof(u16);
- len = I40EVF_MAX_AQ_BUF_SIZE;
+ len = sizeof(struct i40e_virtchnl_vlan_filter_list) +
+ (count * sizeof(u16));
+ more = true;
}
- vvfl = kzalloc(len, GFP_ATOMIC);
+ vvfl = kzalloc(len, GFP_KERNEL);
if (!vvfl)
return;
@@ -608,7 +625,8 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter)
kfree(f);
}
}
- adapter->aq_required &= ~I40EVF_FLAG_AQ_DEL_VLAN_FILTER;
+ if (!more)
+ adapter->aq_required &= ~I40EVF_FLAG_AQ_DEL_VLAN_FILTER;
i40evf_send_pf_msg(adapter, I40E_VIRTCHNL_OP_DEL_VLAN, (u8 *)vvfl, len);
kfree(vvfl);
}
@@ -724,9 +742,29 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter,
return;
}
if (v_retval) {
- dev_err(&adapter->pdev->dev, "PF returned error %d (%s) to our request %d\n",
- v_retval, i40evf_stat_str(&adapter->hw, v_retval),
- v_opcode);
+ switch (v_opcode) {
+ case I40E_VIRTCHNL_OP_ADD_VLAN:
+ dev_err(&adapter->pdev->dev, "Failed to add VLAN filter, error %s\n",
+ i40evf_stat_str(&adapter->hw, v_retval));
+ break;
+ case I40E_VIRTCHNL_OP_ADD_ETHER_ADDRESS:
+ dev_err(&adapter->pdev->dev, "Failed to add MAC filter, error %s\n",
+ i40evf_stat_str(&adapter->hw, v_retval));
+ break;
+ case I40E_VIRTCHNL_OP_DEL_VLAN:
+ dev_err(&adapter->pdev->dev, "Failed to delete VLAN filter, error %s\n",
+ i40evf_stat_str(&adapter->hw, v_retval));
+ break;
+ case I40E_VIRTCHNL_OP_DEL_ETHER_ADDRESS:
+ dev_err(&adapter->pdev->dev, "Failed to delete MAC filter, error %s\n",
+ i40evf_stat_str(&adapter->hw, v_retval));
+ break;
+ default:
+ dev_err(&adapter->pdev->dev, "PF returned error %d (%s) to our request %d\n",
+ v_retval,
+ i40evf_stat_str(&adapter->hw, v_retval),
+ v_opcode);
+ }
}
switch (v_opcode) {
case I40E_VIRTCHNL_OP_GET_STATS: {
diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c
index 7a73510e547c..adb33e2a0137 100644
--- a/drivers/net/ethernet/intel/igb/e1000_82575.c
+++ b/drivers/net/ethernet/intel/igb/e1000_82575.c
@@ -45,8 +45,6 @@ static s32 igb_get_cfg_done_82575(struct e1000_hw *);
static s32 igb_init_hw_82575(struct e1000_hw *);
static s32 igb_phy_hw_reset_sgmii_82575(struct e1000_hw *);
static s32 igb_read_phy_reg_sgmii_82575(struct e1000_hw *, u32, u16 *);
-static s32 igb_read_phy_reg_82580(struct e1000_hw *, u32, u16 *);
-static s32 igb_write_phy_reg_82580(struct e1000_hw *, u32, u16);
static s32 igb_reset_hw_82575(struct e1000_hw *);
static s32 igb_reset_hw_82580(struct e1000_hw *);
static s32 igb_set_d0_lplu_state_82575(struct e1000_hw *, bool);
@@ -205,13 +203,10 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw)
case e1000_82580:
case e1000_i350:
case e1000_i354:
- phy->ops.read_reg = igb_read_phy_reg_82580;
- phy->ops.write_reg = igb_write_phy_reg_82580;
- break;
case e1000_i210:
case e1000_i211:
- phy->ops.read_reg = igb_read_phy_reg_gs40g;
- phy->ops.write_reg = igb_write_phy_reg_gs40g;
+ phy->ops.read_reg = igb_read_phy_reg_82580;
+ phy->ops.write_reg = igb_write_phy_reg_82580;
break;
default:
phy->ops.read_reg = igb_read_phy_reg_igp;
@@ -272,6 +267,11 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw)
if (ret_val)
goto out;
}
+ if (phy->id == M88E1543_E_PHY_ID) {
+ ret_val = igb_initialize_M88E1543_phy(hw);
+ if (ret_val)
+ goto out;
+ }
break;
case IGP03E1000_E_PHY_ID:
phy->type = e1000_phy_igp_3;
@@ -294,6 +294,7 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw)
case I210_I_PHY_ID:
phy->type = e1000_phy_i210;
phy->ops.check_polarity = igb_check_polarity_m88;
+ phy->ops.get_cfg_done = igb_get_cfg_done_i210;
phy->ops.get_phy_info = igb_get_phy_info_m88;
phy->ops.get_cable_length = igb_get_cable_length_m88_gen2;
phy->ops.set_d0_lplu_state = igb_set_d0_lplu_state_82580;
@@ -925,6 +926,8 @@ static s32 igb_phy_hw_reset_sgmii_82575(struct e1000_hw *hw)
if (phy->id == M88E1512_E_PHY_ID)
ret_val = igb_initialize_M88E1512_phy(hw);
+ if (phy->id == M88E1543_E_PHY_ID)
+ ret_val = igb_initialize_M88E1543_phy(hw);
out:
return ret_val;
}
@@ -2145,7 +2148,7 @@ void igb_vmdq_set_replication_pf(struct e1000_hw *hw, bool enable)
* Reads the MDI control register in the PHY at offset and stores the
* information read to data.
**/
-static s32 igb_read_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 *data)
+s32 igb_read_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 *data)
{
s32 ret_val;
@@ -2169,7 +2172,7 @@ out:
*
* Writes data to MDI control register in the PHY at offset.
**/
-static s32 igb_write_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 data)
+s32 igb_write_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 data)
{
s32 ret_val;
diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h
index b1915043bc0c..c3c598c347a9 100644
--- a/drivers/net/ethernet/intel/igb/e1000_defines.h
+++ b/drivers/net/ethernet/intel/igb/e1000_defines.h
@@ -927,7 +927,10 @@
/* Intel i347-AT4 Registers */
-#define I347AT4_PCDL 0x10 /* PHY Cable Diagnostics Length */
+#define I347AT4_PCDL0 0x10 /* Pair 0 PHY Cable Diagnostics Length */
+#define I347AT4_PCDL1 0x11 /* Pair 1 PHY Cable Diagnostics Length */
+#define I347AT4_PCDL2 0x12 /* Pair 2 PHY Cable Diagnostics Length */
+#define I347AT4_PCDL3 0x13 /* Pair 3 PHY Cable Diagnostics Length */
#define I347AT4_PCDC 0x15 /* PHY Cable Diagnostics Control */
#define I347AT4_PAGE_SELECT 0x16
@@ -990,6 +993,7 @@
#define E1000_M88E1543_PAGE_ADDR 0x16 /* Page Offset Register */
#define E1000_M88E1543_EEE_CTRL_1 0x0
#define E1000_M88E1543_EEE_CTRL_1_MS 0x0001 /* EEE Master/Slave */
+#define E1000_M88E1543_FIBER_CTRL 0x0
#define E1000_EEE_ADV_DEV_I354 7
#define E1000_EEE_ADV_ADDR_I354 60
#define E1000_EEE_ADV_100_SUPPORTED (1 << 1) /* 100BaseTx EEE Supported */
diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h
index 2003b3756ba2..4034207eb5cc 100644
--- a/drivers/net/ethernet/intel/igb/e1000_hw.h
+++ b/drivers/net/ethernet/intel/igb/e1000_hw.h
@@ -441,6 +441,7 @@ struct e1000_phy_info {
u16 cable_length;
u16 max_cable_length;
u16 min_cable_length;
+ u16 pair_length[4];
u8 mdix;
diff --git a/drivers/net/ethernet/intel/igb/e1000_i210.c b/drivers/net/ethernet/intel/igb/e1000_i210.c
index 65d931669f81..8aa798737d4d 100644
--- a/drivers/net/ethernet/intel/igb/e1000_i210.c
+++ b/drivers/net/ethernet/intel/igb/e1000_i210.c
@@ -861,10 +861,10 @@ s32 igb_pll_workaround_i210(struct e1000_hw *hw)
if (ret_val)
nvm_word = E1000_INVM_DEFAULT_AL;
tmp_nvm = nvm_word | E1000_INVM_PLL_WO_VAL;
+ igb_write_phy_reg_82580(hw, I347AT4_PAGE_SELECT, E1000_PHY_PLL_FREQ_PAGE);
for (i = 0; i < E1000_MAX_PLL_TRIES; i++) {
/* check current state directly from internal PHY */
- igb_read_phy_reg_gs40g(hw, (E1000_PHY_PLL_FREQ_PAGE |
- E1000_PHY_PLL_FREQ_REG), &phy_word);
+ igb_read_phy_reg_82580(hw, E1000_PHY_PLL_FREQ_REG, &phy_word);
if ((phy_word & E1000_PHY_PLL_UNCONF)
!= E1000_PHY_PLL_UNCONF) {
ret_val = 0;
@@ -896,7 +896,35 @@ s32 igb_pll_workaround_i210(struct e1000_hw *hw)
/* restore WUC register */
wr32(E1000_WUC, wuc);
}
+ igb_write_phy_reg_82580(hw, I347AT4_PAGE_SELECT, 0);
/* restore MDICNFG setting */
wr32(E1000_MDICNFG, mdicnfg);
return ret_val;
}
+
+/**
+ * igb_get_cfg_done_i210 - Read config done bit
+ * @hw: pointer to the HW structure
+ *
+ * Read the management control register for the config done bit for
+ * completion status. NOTE: silicon which is EEPROM-less will fail trying
+ * to read the config done bit, so an error is *ONLY* logged and returns
+ * 0. If we were to return with error, EEPROM-less silicon
+ * would not be able to be reset or change link.
+ **/
+s32 igb_get_cfg_done_i210(struct e1000_hw *hw)
+{
+ s32 timeout = PHY_CFG_TIMEOUT;
+ u32 mask = E1000_NVM_CFG_DONE_PORT_0;
+
+ while (timeout) {
+ if (rd32(E1000_EEMNGCTL_I210) & mask)
+ break;
+ usleep_range(1000, 2000);
+ timeout--;
+ }
+ if (!timeout)
+ hw_dbg("MNG configuration cycle has not completed.\n");
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/intel/igb/e1000_i210.h b/drivers/net/ethernet/intel/igb/e1000_i210.h
index 3442b6357d01..b2964a2a60b1 100644
--- a/drivers/net/ethernet/intel/igb/e1000_i210.h
+++ b/drivers/net/ethernet/intel/igb/e1000_i210.h
@@ -34,6 +34,7 @@ s32 igb_write_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, u16 data);
s32 igb_init_nvm_params_i210(struct e1000_hw *hw);
bool igb_get_flash_presence_i210(struct e1000_hw *hw);
s32 igb_pll_workaround_i210(struct e1000_hw *hw);
+s32 igb_get_cfg_done_i210(struct e1000_hw *hw);
#define E1000_STM_OPCODE 0xDB00
#define E1000_EEPROM_FLASH_SIZE_WORD 0x11
@@ -84,7 +85,7 @@ enum E1000_INVM_STRUCTURE_TYPE {
#define E1000_PCI_PMCSR_D3 0x03
#define E1000_MAX_PLL_TRIES 5
#define E1000_PHY_PLL_UNCONF 0xFF
-#define E1000_PHY_PLL_FREQ_PAGE 0xFC0000
+#define E1000_PHY_PLL_FREQ_PAGE 0xFC
#define E1000_PHY_PLL_FREQ_REG 0x000E
#define E1000_INVM_DEFAULT_AL 0x202F
#define E1000_INVM_AUTOLOAD 0x0A
diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c
index 23ec28f43f6d..5b54254aed4f 100644
--- a/drivers/net/ethernet/intel/igb/e1000_phy.c
+++ b/drivers/net/ethernet/intel/igb/e1000_phy.c
@@ -1717,59 +1717,76 @@ s32 igb_get_cable_length_m88_gen2(struct e1000_hw *hw)
struct e1000_phy_info *phy = &hw->phy;
s32 ret_val;
u16 phy_data, phy_data2, index, default_page, is_cm;
+ int len_tot = 0;
+ u16 len_min;
+ u16 len_max;
switch (hw->phy.id) {
+ case M88E1543_E_PHY_ID:
+ case M88E1512_E_PHY_ID:
+ case I347AT4_E_PHY_ID:
case I210_I_PHY_ID:
- /* Get cable length from PHY Cable Diagnostics Control Reg */
- ret_val = phy->ops.read_reg(hw, (0x7 << GS40G_PAGE_SHIFT) +
- (I347AT4_PCDL + phy->addr),
- &phy_data);
+ /* Remember the original page select and set it to 7 */
+ ret_val = phy->ops.read_reg(hw, I347AT4_PAGE_SELECT,
+ &default_page);
if (ret_val)
- return ret_val;
+ goto out;
+
+ ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, 0x07);
+ if (ret_val)
+ goto out;
/* Check if the unit of cable length is meters or cm */
- ret_val = phy->ops.read_reg(hw, (0x7 << GS40G_PAGE_SHIFT) +
- I347AT4_PCDC, &phy_data2);
+ ret_val = phy->ops.read_reg(hw, I347AT4_PCDC, &phy_data2);
if (ret_val)
- return ret_val;
+ goto out;
is_cm = !(phy_data2 & I347AT4_PCDC_CABLE_LENGTH_UNIT);
- /* Populate the phy structure with cable length in meters */
- phy->min_cable_length = phy_data / (is_cm ? 100 : 1);
- phy->max_cable_length = phy_data / (is_cm ? 100 : 1);
- phy->cable_length = phy_data / (is_cm ? 100 : 1);
- break;
- case M88E1543_E_PHY_ID:
- case M88E1512_E_PHY_ID:
- case I347AT4_E_PHY_ID:
- /* Remember the original page select and set it to 7 */
- ret_val = phy->ops.read_reg(hw, I347AT4_PAGE_SELECT,
- &default_page);
+ /* Get cable length from Pair 0 length Regs */
+ ret_val = phy->ops.read_reg(hw, I347AT4_PCDL0, &phy_data);
if (ret_val)
goto out;
- ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, 0x07);
+ phy->pair_length[0] = phy_data / (is_cm ? 100 : 1);
+ len_tot = phy->pair_length[0];
+ len_min = phy->pair_length[0];
+ len_max = phy->pair_length[0];
+
+ /* Get cable length from Pair 1 length Regs */
+ ret_val = phy->ops.read_reg(hw, I347AT4_PCDL1, &phy_data);
if (ret_val)
goto out;
- /* Get cable length from PHY Cable Diagnostics Control Reg */
- ret_val = phy->ops.read_reg(hw, (I347AT4_PCDL + phy->addr),
- &phy_data);
+ phy->pair_length[1] = phy_data / (is_cm ? 100 : 1);
+ len_tot += phy->pair_length[1];
+ len_min = min(len_min, phy->pair_length[1]);
+ len_max = max(len_max, phy->pair_length[1]);
+
+ /* Get cable length from Pair 2 length Regs */
+ ret_val = phy->ops.read_reg(hw, I347AT4_PCDL2, &phy_data);
if (ret_val)
goto out;
- /* Check if the unit of cable length is meters or cm */
- ret_val = phy->ops.read_reg(hw, I347AT4_PCDC, &phy_data2);
+ phy->pair_length[2] = phy_data / (is_cm ? 100 : 1);
+ len_tot += phy->pair_length[2];
+ len_min = min(len_min, phy->pair_length[2]);
+ len_max = max(len_max, phy->pair_length[2]);
+
+ /* Get cable length from Pair 3 length Regs */
+ ret_val = phy->ops.read_reg(hw, I347AT4_PCDL3, &phy_data);
if (ret_val)
goto out;
- is_cm = !(phy_data2 & I347AT4_PCDC_CABLE_LENGTH_UNIT);
+ phy->pair_length[3] = phy_data / (is_cm ? 100 : 1);
+ len_tot += phy->pair_length[3];
+ len_min = min(len_min, phy->pair_length[3]);
+ len_max = max(len_max, phy->pair_length[3]);
/* Populate the phy structure with cable length in meters */
- phy->min_cable_length = phy_data / (is_cm ? 100 : 1);
- phy->max_cable_length = phy_data / (is_cm ? 100 : 1);
- phy->cable_length = phy_data / (is_cm ? 100 : 1);
+ phy->min_cable_length = len_min;
+ phy->max_cable_length = len_max;
+ phy->cable_length = len_tot / 4;
/* Reset the page selec to its original value */
ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT,
@@ -2278,6 +2295,100 @@ out:
}
/**
+ * igb_initialize_M88E1543_phy - Initialize M88E1512 PHY
+ * @hw: pointer to the HW structure
+ *
+ * Initialize Marvell 1543 to work correctly with Avoton.
+ **/
+s32 igb_initialize_M88E1543_phy(struct e1000_hw *hw)
+{
+ struct e1000_phy_info *phy = &hw->phy;
+ s32 ret_val = 0;
+
+ /* Switch to PHY page 0xFF. */
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0x00FF);
+ if (ret_val)
+ goto out;
+
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_2, 0x214B);
+ if (ret_val)
+ goto out;
+
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_1, 0x2144);
+ if (ret_val)
+ goto out;
+
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_2, 0x0C28);
+ if (ret_val)
+ goto out;
+
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_1, 0x2146);
+ if (ret_val)
+ goto out;
+
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_2, 0xB233);
+ if (ret_val)
+ goto out;
+
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_1, 0x214D);
+ if (ret_val)
+ goto out;
+
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_2, 0xDC0C);
+ if (ret_val)
+ goto out;
+
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_1, 0x2159);
+ if (ret_val)
+ goto out;
+
+ /* Switch to PHY page 0xFB. */
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0x00FB);
+ if (ret_val)
+ goto out;
+
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_3, 0x0C0D);
+ if (ret_val)
+ goto out;
+
+ /* Switch to PHY page 0x12. */
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0x12);
+ if (ret_val)
+ goto out;
+
+ /* Change mode to SGMII-to-Copper */
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1512_MODE, 0x8001);
+ if (ret_val)
+ goto out;
+
+ /* Switch to PHY page 1. */
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0x1);
+ if (ret_val)
+ goto out;
+
+ /* Change mode to 1000BASE-X/SGMII and autoneg enable */
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1543_FIBER_CTRL, 0x9140);
+ if (ret_val)
+ goto out;
+
+ /* Return the PHY to page 0. */
+ ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0);
+ if (ret_val)
+ goto out;
+
+ ret_val = igb_phy_sw_reset(hw);
+ if (ret_val) {
+ hw_dbg("Error committing the PHY changes\n");
+ return ret_val;
+ }
+
+ /* msec_delay(1000); */
+ usleep_range(1000, 2000);
+out:
+ return ret_val;
+}
+
+/**
* igb_power_up_phy_copper - Restore copper link in case of PHY power down
* @hw: pointer to the HW structure
*
@@ -2494,66 +2605,6 @@ out:
}
/**
- * igb_write_phy_reg_gs40g - Write GS40G PHY register
- * @hw: pointer to the HW structure
- * @offset: lower half is register offset to write to
- * upper half is page to use.
- * @data: data to write at register offset
- *
- * Acquires semaphore, if necessary, then writes the data to PHY register
- * at the offset. Release any acquired semaphores before exiting.
- **/
-s32 igb_write_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 data)
-{
- s32 ret_val;
- u16 page = offset >> GS40G_PAGE_SHIFT;
-
- offset = offset & GS40G_OFFSET_MASK;
- ret_val = hw->phy.ops.acquire(hw);
- if (ret_val)
- return ret_val;
-
- ret_val = igb_write_phy_reg_mdic(hw, GS40G_PAGE_SELECT, page);
- if (ret_val)
- goto release;
- ret_val = igb_write_phy_reg_mdic(hw, offset, data);
-
-release:
- hw->phy.ops.release(hw);
- return ret_val;
-}
-
-/**
- * igb_read_phy_reg_gs40g - Read GS40G PHY register
- * @hw: pointer to the HW structure
- * @offset: lower half is register offset to read to
- * upper half is page to use.
- * @data: data to read at register offset
- *
- * Acquires semaphore, if necessary, then reads the data in the PHY register
- * at the offset. Release any acquired semaphores before exiting.
- **/
-s32 igb_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data)
-{
- s32 ret_val;
- u16 page = offset >> GS40G_PAGE_SHIFT;
-
- offset = offset & GS40G_OFFSET_MASK;
- ret_val = hw->phy.ops.acquire(hw);
- if (ret_val)
- return ret_val;
-
- ret_val = igb_write_phy_reg_mdic(hw, GS40G_PAGE_SELECT, page);
- if (ret_val)
- goto release;
- ret_val = igb_read_phy_reg_mdic(hw, offset, data);
-
-release:
- hw->phy.ops.release(hw);
- return ret_val;
-}
-
-/**
* igb_set_master_slave_mode - Setup PHY for Master/slave mode
* @hw: pointer to the HW structure
*
diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.h b/drivers/net/ethernet/intel/igb/e1000_phy.h
index 24d55edbb0e3..969a6ddafa3b 100644
--- a/drivers/net/ethernet/intel/igb/e1000_phy.h
+++ b/drivers/net/ethernet/intel/igb/e1000_phy.h
@@ -62,6 +62,7 @@ void igb_power_up_phy_copper(struct e1000_hw *hw);
void igb_power_down_phy_copper(struct e1000_hw *hw);
s32 igb_phy_init_script_igp3(struct e1000_hw *hw);
s32 igb_initialize_M88E1512_phy(struct e1000_hw *hw);
+s32 igb_initialize_M88E1543_phy(struct e1000_hw *hw);
s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data);
s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data);
s32 igb_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data);
@@ -71,8 +72,8 @@ s32 igb_copper_link_setup_82580(struct e1000_hw *hw);
s32 igb_get_phy_info_82580(struct e1000_hw *hw);
s32 igb_phy_force_speed_duplex_82580(struct e1000_hw *hw);
s32 igb_get_cable_length_82580(struct e1000_hw *hw);
-s32 igb_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data);
-s32 igb_write_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 data);
+s32 igb_read_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 *data);
+s32 igb_write_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 data);
s32 igb_check_polarity_m88(struct e1000_hw *hw);
/* IGP01E1000 Specific Registers */
@@ -143,17 +144,6 @@ s32 igb_check_polarity_m88(struct e1000_hw *hw);
#define E1000_CABLE_LENGTH_UNDEFINED 0xFF
-/* GS40G - I210 PHY defines */
-#define GS40G_PAGE_SELECT 0x16
-#define GS40G_PAGE_SHIFT 16
-#define GS40G_OFFSET_MASK 0xFFFF
-#define GS40G_PAGE_2 0x20000
-#define GS40G_MAC_REG2 0x15
-#define GS40G_MAC_LB 0x4140
-#define GS40G_MAC_SPEED_1G 0X0006
-#define GS40G_COPPER_SPEC 0x0010
-#define GS40G_LINE_LB 0x4000
-
/* SFP modules ID memory locations */
#define E1000_SFF_IDENTIFIER_OFFSET 0x00
#define E1000_SFF_IDENTIFIER_SFF 0x02
diff --git a/drivers/net/ethernet/intel/igb/e1000_regs.h b/drivers/net/ethernet/intel/igb/e1000_regs.h
index 4af2870e49f8..21d9d02885cb 100644
--- a/drivers/net/ethernet/intel/igb/e1000_regs.h
+++ b/drivers/net/ethernet/intel/igb/e1000_regs.h
@@ -66,6 +66,7 @@
#define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */
#define E1000_PBS 0x01008 /* Packet Buffer Size */
#define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */
+#define E1000_EEMNGCTL_I210 0x12030 /* MNG EEprom Control */
#define E1000_EEARBC_I210 0x12024 /* EEPROM Auto Read Bus Control */
#define E1000_EEWR 0x0102C /* EEPROM Write Register - RW */
#define E1000_I2CCMD 0x01028 /* SFPI2C Command Register - RW */
@@ -385,8 +386,7 @@ do { \
#define array_wr32(reg, offset, value) \
wr32((reg) + ((offset) << 2), (value))
-#define array_rd32(reg, offset) \
- (readl(hw->hw_addr + reg + ((offset) << 2)))
+#define array_rd32(reg, offset) (igb_rd32(hw, reg + ((offset) << 2)))
/* DMA Coalescing registers */
#define E1000_PCIEMISC 0x05BB8 /* PCIE misc config register */
diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h
index 1a2f1cc44b28..e3cb93bdb21a 100644
--- a/drivers/net/ethernet/intel/igb/igb.h
+++ b/drivers/net/ethernet/intel/igb/igb.h
@@ -389,6 +389,8 @@ struct igb_adapter {
u16 link_speed;
u16 link_duplex;
+ u8 __iomem *io_addr; /* Mainly for iounmap use */
+
struct work_struct reset_task;
struct work_struct watchdog_task;
bool fc_autoneg;
diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c
index 2529bc625de4..1d329f1d047b 100644
--- a/drivers/net/ethernet/intel/igb/igb_ethtool.c
+++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c
@@ -127,10 +127,20 @@ static const struct igb_stats igb_gstrings_net_stats[] = {
#define IGB_STATS_LEN \
(IGB_GLOBAL_STATS_LEN + IGB_NETDEV_STATS_LEN + IGB_QUEUE_STATS_LEN)
+enum igb_diagnostics_results {
+ TEST_REG = 0,
+ TEST_EEP,
+ TEST_IRQ,
+ TEST_LOOP,
+ TEST_LINK
+};
+
static const char igb_gstrings_test[][ETH_GSTRING_LEN] = {
- "Register test (offline)", "Eeprom test (offline)",
- "Interrupt test (offline)", "Loopback test (offline)",
- "Link test (on/offline)"
+ [TEST_REG] = "Register test (offline)",
+ [TEST_EEP] = "Eeprom test (offline)",
+ [TEST_IRQ] = "Interrupt test (offline)",
+ [TEST_LOOP] = "Loopback test (offline)",
+ [TEST_LINK] = "Link test (on/offline)"
};
#define IGB_TEST_LEN (sizeof(igb_gstrings_test) / ETH_GSTRING_LEN)
@@ -2002,7 +2012,7 @@ static void igb_diag_test(struct net_device *netdev,
/* Link test performed before hardware reset so autoneg doesn't
* interfere with test result
*/
- if (igb_link_test(adapter, &data[4]))
+ if (igb_link_test(adapter, &data[TEST_LINK]))
eth_test->flags |= ETH_TEST_FL_FAILED;
if (if_running)
@@ -2011,21 +2021,21 @@ static void igb_diag_test(struct net_device *netdev,
else
igb_reset(adapter);
- if (igb_reg_test(adapter, &data[0]))
+ if (igb_reg_test(adapter, &data[TEST_REG]))
eth_test->flags |= ETH_TEST_FL_FAILED;
igb_reset(adapter);
- if (igb_eeprom_test(adapter, &data[1]))
+ if (igb_eeprom_test(adapter, &data[TEST_EEP]))
eth_test->flags |= ETH_TEST_FL_FAILED;
igb_reset(adapter);
- if (igb_intr_test(adapter, &data[2]))
+ if (igb_intr_test(adapter, &data[TEST_IRQ]))
eth_test->flags |= ETH_TEST_FL_FAILED;
igb_reset(adapter);
/* power up link for loopback test */
igb_power_up_link(adapter);
- if (igb_loopback_test(adapter, &data[3]))
+ if (igb_loopback_test(adapter, &data[TEST_LOOP]))
eth_test->flags |= ETH_TEST_FL_FAILED;
/* restore speed, duplex, autoneg settings */
@@ -2045,16 +2055,16 @@ static void igb_diag_test(struct net_device *netdev,
dev_info(&adapter->pdev->dev, "online testing starting\n");
/* PHY is powered down when interface is down */
- if (if_running && igb_link_test(adapter, &data[4]))
+ if (if_running && igb_link_test(adapter, &data[TEST_LINK]))
eth_test->flags |= ETH_TEST_FL_FAILED;
else
- data[4] = 0;
+ data[TEST_LINK] = 0;
/* Online tests aren't run; pass by default */
- data[0] = 0;
- data[1] = 0;
- data[2] = 0;
- data[3] = 0;
+ data[TEST_REG] = 0;
+ data[TEST_EEP] = 0;
+ data[TEST_IRQ] = 0;
+ data[TEST_LOOP] = 0;
clear_bit(__IGB_TESTING, &adapter->state);
}
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index ea7b09887245..31e5f3942839 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -946,7 +946,6 @@ static void igb_configure_msix(struct igb_adapter *adapter)
static int igb_request_msix(struct igb_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
- struct e1000_hw *hw = &adapter->hw;
int i, err = 0, vector = 0, free_vector = 0;
err = request_irq(adapter->msix_entries[vector].vector,
@@ -959,7 +958,7 @@ static int igb_request_msix(struct igb_adapter *adapter)
vector++;
- q_vector->itr_register = hw->hw_addr + E1000_EITR(vector);
+ q_vector->itr_register = adapter->io_addr + E1000_EITR(vector);
if (q_vector->rx.ring && q_vector->tx.ring)
sprintf(q_vector->name, "%s-TxRx-%u", netdev->name,
@@ -1230,7 +1229,7 @@ static int igb_alloc_q_vector(struct igb_adapter *adapter,
q_vector->tx.work_limit = adapter->tx_work_limit;
/* initialize ITR configuration */
- q_vector->itr_register = adapter->hw.hw_addr + E1000_EITR(0);
+ q_vector->itr_register = adapter->io_addr + E1000_EITR(0);
q_vector->itr_val = IGB_START_ITR;
/* initialize pointer to rings */
@@ -2294,9 +2293,11 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
adapter->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE);
err = -EIO;
- hw->hw_addr = pci_iomap(pdev, 0, 0);
- if (!hw->hw_addr)
+ adapter->io_addr = pci_iomap(pdev, 0, 0);
+ if (!adapter->io_addr)
goto err_ioremap;
+ /* hw->hw_addr can be altered, we'll use adapter->io_addr for unmap */
+ hw->hw_addr = adapter->io_addr;
netdev->netdev_ops = &igb_netdev_ops;
igb_set_ethtool_ops(netdev);
@@ -2378,8 +2379,8 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
}
if (hw->mac.type >= e1000_82576) {
- netdev->hw_features |= NETIF_F_SCTP_CSUM;
- netdev->features |= NETIF_F_SCTP_CSUM;
+ netdev->hw_features |= NETIF_F_SCTP_CRC;
+ netdev->features |= NETIF_F_SCTP_CRC;
}
netdev->priv_flags |= IFF_UNICAST_FLT;
@@ -2656,7 +2657,7 @@ err_sw_init:
#ifdef CONFIG_PCI_IOV
igb_disable_sriov(pdev);
#endif
- pci_iounmap(pdev, hw->hw_addr);
+ pci_iounmap(pdev, adapter->io_addr);
err_ioremap:
free_netdev(netdev);
err_alloc_etherdev:
@@ -2823,7 +2824,7 @@ static void igb_remove(struct pci_dev *pdev)
igb_clear_interrupt_scheme(adapter);
- pci_iounmap(pdev, hw->hw_addr);
+ pci_iounmap(pdev, adapter->io_addr);
if (hw->flash_address)
iounmap(hw->flash_address);
pci_release_selected_regions(pdev,
@@ -2856,6 +2857,13 @@ static void igb_probe_vfs(struct igb_adapter *adapter)
if ((hw->mac.type == e1000_i210) || (hw->mac.type == e1000_i211))
return;
+ /* Of the below we really only want the effect of getting
+ * IGB_FLAG_HAS_MSIX set (if available), without which
+ * igb_enable_sriov() has no effect.
+ */
+ igb_set_interrupt_capability(adapter, true);
+ igb_reset_interrupt_capability(adapter);
+
pci_sriov_set_totalvfs(pdev, 7);
igb_enable_sriov(pdev, max_vfs);
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
index 1d2174526a4c..4b9156cd8b93 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
@@ -139,6 +139,7 @@ enum ixgbe_tx_flags {
#define IXGBE_X540_VF_DEVICE_ID 0x1515
struct vf_data_storage {
+ struct pci_dev *vfdev;
unsigned char vf_mac_addresses[ETH_ALEN];
u16 vf_mc_hashes[IXGBE_MAX_VF_MC_ENTRIES];
u16 num_vf_mc_hashes;
@@ -224,6 +225,8 @@ struct ixgbe_rx_queue_stats {
u64 csum_err;
};
+#define IXGBE_TS_HDR_LEN 8
+
enum ixgbe_ring_state_t {
__IXGBE_TX_FDIR_INIT_DONE,
__IXGBE_TX_XPS_INIT_DONE,
@@ -282,6 +285,8 @@ struct ixgbe_ring {
u16 next_to_use;
u16 next_to_clean;
+ unsigned long last_rx_timestamp;
+
union {
u16 next_to_alloc;
struct {
@@ -312,7 +317,7 @@ enum ixgbe_ring_f_enum {
};
#define IXGBE_MAX_RSS_INDICES 16
-#define IXGBE_MAX_RSS_INDICES_X550 64
+#define IXGBE_MAX_RSS_INDICES_X550 63
#define IXGBE_MAX_VMDQ_INDICES 64
#define IXGBE_MAX_FDIR_INDICES 63 /* based on q_vector limit */
#define IXGBE_MAX_FCOE_INDICES 8
@@ -587,9 +592,10 @@ static inline u16 ixgbe_desc_unused(struct ixgbe_ring *ring)
struct ixgbe_mac_addr {
u8 addr[ETH_ALEN];
- u16 queue;
+ u16 pool;
u16 state; /* bitmask */
};
+
#define IXGBE_MAC_STATE_DEFAULT 0x1
#define IXGBE_MAC_STATE_MODIFIED 0x2
#define IXGBE_MAC_STATE_IN_USE 0x4
@@ -639,6 +645,8 @@ struct ixgbe_adapter {
#define IXGBE_FLAG_SRIOV_CAPABLE (u32)(1 << 22)
#define IXGBE_FLAG_SRIOV_ENABLED (u32)(1 << 23)
#define IXGBE_FLAG_VXLAN_OFFLOAD_CAPABLE BIT(24)
+#define IXGBE_FLAG_RX_HWTSTAMP_ENABLED BIT(25)
+#define IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER BIT(26)
u32 flags2;
#define IXGBE_FLAG2_RSC_CAPABLE (u32)(1 << 0)
@@ -656,6 +664,7 @@ struct ixgbe_adapter {
#ifdef CONFIG_IXGBE_VXLAN
#define IXGBE_FLAG2_VXLAN_REREG_NEEDED BIT(12)
#endif
+#define IXGBE_FLAG2_VLAN_PROMISC BIT(13)
/* Tx fast path data */
int num_tx_queues;
@@ -755,9 +764,12 @@ struct ixgbe_adapter {
unsigned long last_rx_ptp_check;
unsigned long last_rx_timestamp;
spinlock_t tmreg_lock;
- struct cyclecounter cc;
- struct timecounter tc;
+ struct cyclecounter hw_cc;
+ struct timecounter hw_tc;
u32 base_incval;
+ u32 tx_hwtstamp_timeouts;
+ u32 rx_hwtstamp_cleared;
+ void (*ptp_setup_sdp)(struct ixgbe_adapter *);
/* SR-IOV */
DECLARE_BITMAP(active_vfs, IXGBE_MAX_VF_FUNCTIONS);
@@ -883,9 +895,10 @@ int ixgbe_wol_supported(struct ixgbe_adapter *adapter, u16 device_id,
void ixgbe_full_sync_mac_table(struct ixgbe_adapter *adapter);
#endif
int ixgbe_add_mac_filter(struct ixgbe_adapter *adapter,
- u8 *addr, u16 queue);
+ const u8 *addr, u16 queue);
int ixgbe_del_mac_filter(struct ixgbe_adapter *adapter,
- u8 *addr, u16 queue);
+ const u8 *addr, u16 queue);
+void ixgbe_update_pf_promisc_vlvf(struct ixgbe_adapter *adapter, u32 vid);
void ixgbe_clear_interrupt_scheme(struct ixgbe_adapter *adapter);
netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *, struct ixgbe_adapter *,
struct ixgbe_ring *);
@@ -968,12 +981,33 @@ void ixgbe_ptp_suspend(struct ixgbe_adapter *adapter);
void ixgbe_ptp_stop(struct ixgbe_adapter *adapter);
void ixgbe_ptp_overflow_check(struct ixgbe_adapter *adapter);
void ixgbe_ptp_rx_hang(struct ixgbe_adapter *adapter);
-void ixgbe_ptp_rx_hwtstamp(struct ixgbe_adapter *adapter, struct sk_buff *skb);
+void ixgbe_ptp_rx_pktstamp(struct ixgbe_q_vector *, struct sk_buff *);
+void ixgbe_ptp_rx_rgtstamp(struct ixgbe_q_vector *, struct sk_buff *skb);
+static inline void ixgbe_ptp_rx_hwtstamp(struct ixgbe_ring *rx_ring,
+ union ixgbe_adv_rx_desc *rx_desc,
+ struct sk_buff *skb)
+{
+ if (unlikely(ixgbe_test_staterr(rx_desc, IXGBE_RXD_STAT_TSIP))) {
+ ixgbe_ptp_rx_pktstamp(rx_ring->q_vector, skb);
+ return;
+ }
+
+ if (unlikely(!ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_STAT_TS)))
+ return;
+
+ ixgbe_ptp_rx_rgtstamp(rx_ring->q_vector, skb);
+
+ /* Update the last_rx_timestamp timer in order to enable watchdog check
+ * for error case of latched timestamp on a dropped packet.
+ */
+ rx_ring->last_rx_timestamp = jiffies;
+}
+
int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr);
int ixgbe_ptp_get_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr);
void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter);
void ixgbe_ptp_reset(struct ixgbe_adapter *adapter);
-void ixgbe_ptp_check_pps_event(struct ixgbe_adapter *adapter, u32 eicr);
+void ixgbe_ptp_check_pps_event(struct ixgbe_adapter *adapter);
#ifdef CONFIG_PCI_IOV
void ixgbe_sriov_reinit(struct ixgbe_adapter *adapter);
#endif
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c
index 65db69b862fb..d8a9fb8a59e2 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82598.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
- Copyright(c) 1999 - 2014 Intel Corporation.
+ Copyright(c) 1999 - 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,
@@ -765,13 +765,14 @@ mac_reset_top:
ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL) | IXGBE_CTRL_RST;
IXGBE_WRITE_REG(hw, IXGBE_CTRL, ctrl);
IXGBE_WRITE_FLUSH(hw);
+ usleep_range(1000, 1200);
/* Poll for reset bit to self-clear indicating reset is complete */
for (i = 0; i < 10; i++) {
- udelay(1);
ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL);
if (!(ctrl & IXGBE_CTRL_RST))
break;
+ udelay(1);
}
if (ctrl & IXGBE_CTRL_RST) {
status = IXGBE_ERR_RESET_FAILED;
@@ -879,11 +880,12 @@ static s32 ixgbe_clear_vmdq_82598(struct ixgbe_hw *hw, u32 rar, u32 vmdq)
* @vlan: VLAN id to write to VLAN filter
* @vind: VMDq output index that maps queue to VLAN id in VFTA
* @vlan_on: boolean flag to turn on/off VLAN in VFTA
+ * @vlvf_bypass: boolean flag - unused
*
* Turn on/off specified VLAN in the VLAN filter table.
**/
static s32 ixgbe_set_vfta_82598(struct ixgbe_hw *hw, u32 vlan, u32 vind,
- bool vlan_on)
+ bool vlan_on, bool vlvf_bypass)
{
u32 regindex;
u32 bitindex;
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
index a39afcf03e2c..fa8d4f40ac2a 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
@@ -990,13 +990,14 @@ mac_reset_top:
ctrl |= IXGBE_READ_REG(hw, IXGBE_CTRL);
IXGBE_WRITE_REG(hw, IXGBE_CTRL, ctrl);
IXGBE_WRITE_FLUSH(hw);
+ usleep_range(1000, 1200);
/* Poll for reset bit to self-clear indicating reset is complete */
for (i = 0; i < 10; i++) {
- udelay(1);
ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL);
if (!(ctrl & IXGBE_CTRL_RST_MASK))
break;
+ udelay(1);
}
if (ctrl & IXGBE_CTRL_RST_MASK) {
@@ -1082,12 +1083,16 @@ mac_reset_top:
/* Add the SAN MAC address to the RAR only if it's a valid address */
if (is_valid_ether_addr(hw->mac.san_addr)) {
- hw->mac.ops.set_rar(hw, hw->mac.num_rar_entries - 1,
- hw->mac.san_addr, 0, IXGBE_RAH_AV);
-
/* Save the SAN MAC RAR index */
hw->mac.san_mac_rar_index = hw->mac.num_rar_entries - 1;
+ hw->mac.ops.set_rar(hw, hw->mac.san_mac_rar_index,
+ hw->mac.san_addr, 0, IXGBE_RAH_AV);
+
+ /* clear VMDq pool/queue selection for this RAR */
+ hw->mac.ops.clear_vmdq(hw, hw->mac.san_mac_rar_index,
+ IXGBE_CLEAR_VMDQ_ALL);
+
/* Reserve the last RAR for the SAN MAC address */
hw->mac.num_rar_entries--;
}
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
index ce61b36b94f1..64045053e874 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
- Copyright(c) 1999 - 2014 Intel Corporation.
+ Copyright(c) 1999 - 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,
@@ -1884,10 +1884,11 @@ s32 ixgbe_init_rx_addrs_generic(struct ixgbe_hw *hw)
hw_dbg(hw, " New MAC Addr =%pM\n", hw->mac.addr);
hw->mac.ops.set_rar(hw, 0, hw->mac.addr, 0, IXGBE_RAH_AV);
-
- /* clear VMDq pool/queue selection for RAR 0 */
- hw->mac.ops.clear_vmdq(hw, 0, IXGBE_CLEAR_VMDQ_ALL);
}
+
+ /* clear VMDq pool/queue selection for RAR 0 */
+ hw->mac.ops.clear_vmdq(hw, 0, IXGBE_CLEAR_VMDQ_ALL);
+
hw->addr_ctrl.overflow_promisc = 0;
hw->addr_ctrl.rar_used_count = 1;
@@ -2454,6 +2455,17 @@ static s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw)
/* Always set this bit to ensure any future transactions are blocked */
IXGBE_WRITE_REG(hw, IXGBE_CTRL, IXGBE_CTRL_GIO_DIS);
+ /* Poll for bit to read as set */
+ for (i = 0; i < IXGBE_PCI_MASTER_DISABLE_TIMEOUT; i++) {
+ if (IXGBE_READ_REG(hw, IXGBE_CTRL) & IXGBE_CTRL_GIO_DIS)
+ break;
+ usleep_range(100, 120);
+ }
+ if (i >= IXGBE_PCI_MASTER_DISABLE_TIMEOUT) {
+ hw_dbg(hw, "GIO disable did not set - requesting resets\n");
+ goto gio_disable_fail;
+ }
+
/* Exit if master requests are blocked */
if (!(IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_GIO) ||
ixgbe_removed(hw->hw_addr))
@@ -2475,6 +2487,7 @@ static s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw)
* again to clear out any effects they may have had on our device.
*/
hw_dbg(hw, "GIO Master Disable bit didn't clear - requesting resets\n");
+gio_disable_fail:
hw->mac.flags |= IXGBE_FLAGS_DOUBLE_RESET_REQUIRED;
if (hw->mac.type >= ixgbe_mac_X550)
@@ -2987,43 +3000,44 @@ s32 ixgbe_init_uta_tables_generic(struct ixgbe_hw *hw)
* return the VLVF index where this VLAN id should be placed
*
**/
-static s32 ixgbe_find_vlvf_slot(struct ixgbe_hw *hw, u32 vlan)
+static s32 ixgbe_find_vlvf_slot(struct ixgbe_hw *hw, u32 vlan, bool vlvf_bypass)
{
- u32 bits = 0;
- u32 first_empty_slot = 0;
- s32 regindex;
+ s32 regindex, first_empty_slot;
+ u32 bits;
/* short cut the special case */
if (vlan == 0)
return 0;
- /*
- * Search for the vlan id in the VLVF entries. Save off the first empty
- * slot found along the way
- */
- for (regindex = 1; regindex < IXGBE_VLVF_ENTRIES; regindex++) {
+ /* if vlvf_bypass is set we don't want to use an empty slot, we
+ * will simply bypass the VLVF if there are no entries present in the
+ * VLVF that contain our VLAN
+ */
+ first_empty_slot = vlvf_bypass ? IXGBE_ERR_NO_SPACE : 0;
+
+ /* add VLAN enable bit for comparison */
+ vlan |= IXGBE_VLVF_VIEN;
+
+ /* Search for the vlan id in the VLVF entries. Save off the first empty
+ * slot found along the way.
+ *
+ * pre-decrement loop covering (IXGBE_VLVF_ENTRIES - 1) .. 1
+ */
+ for (regindex = IXGBE_VLVF_ENTRIES; --regindex;) {
bits = IXGBE_READ_REG(hw, IXGBE_VLVF(regindex));
- if (!bits && !(first_empty_slot))
+ if (bits == vlan)
+ return regindex;
+ if (!first_empty_slot && !bits)
first_empty_slot = regindex;
- else if ((bits & 0x0FFF) == vlan)
- break;
}
- /*
- * If regindex is less than IXGBE_VLVF_ENTRIES, then we found the vlan
- * in the VLVF. Else use the first empty VLVF register for this
- * vlan id.
- */
- if (regindex >= IXGBE_VLVF_ENTRIES) {
- if (first_empty_slot)
- regindex = first_empty_slot;
- else {
- hw_dbg(hw, "No space in VLVF.\n");
- regindex = IXGBE_ERR_NO_SPACE;
- }
- }
+ /* If we are here then we didn't find the VLAN. Return first empty
+ * slot we found during our search, else error.
+ */
+ if (!first_empty_slot)
+ hw_dbg(hw, "No space in VLVF.\n");
- return regindex;
+ return first_empty_slot ? : IXGBE_ERR_NO_SPACE;
}
/**
@@ -3032,21 +3046,17 @@ static s32 ixgbe_find_vlvf_slot(struct ixgbe_hw *hw, u32 vlan)
* @vlan: VLAN id to write to VLAN filter
* @vind: VMDq output index that maps queue to VLAN id in VFVFB
* @vlan_on: boolean flag to turn on/off VLAN in VFVF
+ * @vlvf_bypass: boolean flag indicating updating default pool is okay
*
* Turn on/off specified VLAN in the VLAN filter table.
**/
s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind,
- bool vlan_on)
+ bool vlan_on, bool vlvf_bypass)
{
- s32 regindex;
- u32 bitindex;
- u32 vfta;
- u32 bits;
- u32 vt;
- u32 targetbit;
- bool vfta_changed = false;
+ u32 regidx, vfta_delta, vfta, bits;
+ s32 vlvf_index;
- if (vlan > 4095)
+ if ((vlan > 4095) || (vind > 63))
return IXGBE_ERR_PARAM;
/*
@@ -3061,22 +3071,16 @@ s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind,
* bits[11-5]: which register
* bits[4-0]: which bit in the register
*/
- regindex = (vlan >> 5) & 0x7F;
- bitindex = vlan & 0x1F;
- targetbit = (1 << bitindex);
- vfta = IXGBE_READ_REG(hw, IXGBE_VFTA(regindex));
-
- if (vlan_on) {
- if (!(vfta & targetbit)) {
- vfta |= targetbit;
- vfta_changed = true;
- }
- } else {
- if ((vfta & targetbit)) {
- vfta &= ~targetbit;
- vfta_changed = true;
- }
- }
+ regidx = vlan / 32;
+ vfta_delta = 1 << (vlan % 32);
+ vfta = IXGBE_READ_REG(hw, IXGBE_VFTA(regidx));
+
+ /* vfta_delta represents the difference between the current value
+ * of vfta and the value we want in the register. Since the diff
+ * is an XOR mask we can just update vfta using an XOR.
+ */
+ vfta_delta &= vlan_on ? ~vfta : vfta;
+ vfta ^= vfta_delta;
/* Part 2
* If VT Mode is set
@@ -3086,85 +3090,67 @@ s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind,
* Or !vlan_on
* clear the pool bit and possibly the vind
*/
- vt = IXGBE_READ_REG(hw, IXGBE_VT_CTL);
- if (vt & IXGBE_VT_CTL_VT_ENABLE) {
- s32 vlvf_index;
-
- vlvf_index = ixgbe_find_vlvf_slot(hw, vlan);
- if (vlvf_index < 0)
- return vlvf_index;
-
- if (vlan_on) {
- /* set the pool bit */
- if (vind < 32) {
- bits = IXGBE_READ_REG(hw,
- IXGBE_VLVFB(vlvf_index*2));
- bits |= (1 << vind);
- IXGBE_WRITE_REG(hw,
- IXGBE_VLVFB(vlvf_index*2),
- bits);
- } else {
- bits = IXGBE_READ_REG(hw,
- IXGBE_VLVFB((vlvf_index*2)+1));
- bits |= (1 << (vind-32));
- IXGBE_WRITE_REG(hw,
- IXGBE_VLVFB((vlvf_index*2)+1),
- bits);
- }
- } else {
- /* clear the pool bit */
- if (vind < 32) {
- bits = IXGBE_READ_REG(hw,
- IXGBE_VLVFB(vlvf_index*2));
- bits &= ~(1 << vind);
- IXGBE_WRITE_REG(hw,
- IXGBE_VLVFB(vlvf_index*2),
- bits);
- bits |= IXGBE_READ_REG(hw,
- IXGBE_VLVFB((vlvf_index*2)+1));
- } else {
- bits = IXGBE_READ_REG(hw,
- IXGBE_VLVFB((vlvf_index*2)+1));
- bits &= ~(1 << (vind-32));
- IXGBE_WRITE_REG(hw,
- IXGBE_VLVFB((vlvf_index*2)+1),
- bits);
- bits |= IXGBE_READ_REG(hw,
- IXGBE_VLVFB(vlvf_index*2));
- }
- }
+ if (!(IXGBE_READ_REG(hw, IXGBE_VT_CTL) & IXGBE_VT_CTL_VT_ENABLE))
+ goto vfta_update;
+
+ vlvf_index = ixgbe_find_vlvf_slot(hw, vlan, vlvf_bypass);
+ if (vlvf_index < 0) {
+ if (vlvf_bypass)
+ goto vfta_update;
+ return vlvf_index;
+ }
- /*
- * If there are still bits set in the VLVFB registers
- * for the VLAN ID indicated we need to see if the
- * caller is requesting that we clear the VFTA entry bit.
- * If the caller has requested that we clear the VFTA
- * entry bit but there are still pools/VFs using this VLAN
- * ID entry then ignore the request. We're not worried
- * about the case where we're turning the VFTA VLAN ID
- * entry bit on, only when requested to turn it off as
- * there may be multiple pools and/or VFs using the
- * VLAN ID entry. In that case we cannot clear the
- * VFTA bit until all pools/VFs using that VLAN ID have also
- * been cleared. This will be indicated by "bits" being
- * zero.
+ bits = IXGBE_READ_REG(hw, IXGBE_VLVFB(vlvf_index * 2 + vind / 32));
+
+ /* set the pool bit */
+ bits |= 1 << (vind % 32);
+ if (vlan_on)
+ goto vlvf_update;
+
+ /* clear the pool bit */
+ bits ^= 1 << (vind % 32);
+
+ if (!bits &&
+ !IXGBE_READ_REG(hw, IXGBE_VLVFB(vlvf_index * 2 + 1 - vind / 32))) {
+ /* Clear VFTA first, then disable VLVF. Otherwise
+ * we run the risk of stray packets leaking into
+ * the PF via the default pool
*/
- if (bits) {
- IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index),
- (IXGBE_VLVF_VIEN | vlan));
- if (!vlan_on) {
- /* someone wants to clear the vfta entry
- * but some pools/VFs are still using it.
- * Ignore it. */
- vfta_changed = false;
- }
- } else {
- IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index), 0);
- }
+ if (vfta_delta)
+ IXGBE_WRITE_REG(hw, IXGBE_VFTA(regidx), vfta);
+
+ /* disable VLVF and clear remaining bit from pool */
+ IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_VLVFB(vlvf_index * 2 + vind / 32), 0);
+
+ return 0;
}
- if (vfta_changed)
- IXGBE_WRITE_REG(hw, IXGBE_VFTA(regindex), vfta);
+ /* If there are still bits set in the VLVFB registers
+ * for the VLAN ID indicated we need to see if the
+ * caller is requesting that we clear the VFTA entry bit.
+ * If the caller has requested that we clear the VFTA
+ * entry bit but there are still pools/VFs using this VLAN
+ * ID entry then ignore the request. We're not worried
+ * about the case where we're turning the VFTA VLAN ID
+ * entry bit on, only when requested to turn it off as
+ * there may be multiple pools and/or VFs using the
+ * VLAN ID entry. In that case we cannot clear the
+ * VFTA bit until all pools/VFs using that VLAN ID have also
+ * been cleared. This will be indicated by "bits" being
+ * zero.
+ */
+ vfta_delta = 0;
+
+vlvf_update:
+ /* record pool change and enable VLAN ID if not already enabled */
+ IXGBE_WRITE_REG(hw, IXGBE_VLVFB(vlvf_index * 2 + vind / 32), bits);
+ IXGBE_WRITE_REG(hw, IXGBE_VLVF(vlvf_index), IXGBE_VLVF_VIEN | vlan);
+
+vfta_update:
+ /* Update VFTA now that we are ready for traffic */
+ if (vfta_delta)
+ IXGBE_WRITE_REG(hw, IXGBE_VFTA(regidx), vfta);
return 0;
}
@@ -3184,8 +3170,8 @@ s32 ixgbe_clear_vfta_generic(struct ixgbe_hw *hw)
for (offset = 0; offset < IXGBE_VLVF_ENTRIES; offset++) {
IXGBE_WRITE_REG(hw, IXGBE_VLVF(offset), 0);
- IXGBE_WRITE_REG(hw, IXGBE_VLVFB(offset*2), 0);
- IXGBE_WRITE_REG(hw, IXGBE_VLVFB((offset*2)+1), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_VLVFB(offset * 2), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_VLVFB(offset * 2 + 1), 0);
}
return 0;
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h
index a0044e4a8b90..2b9563137fd8 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.h
@@ -92,7 +92,7 @@ s32 ixgbe_set_vmdq_san_mac_generic(struct ixgbe_hw *hw, u32 vmdq);
s32 ixgbe_clear_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq);
s32 ixgbe_init_uta_tables_generic(struct ixgbe_hw *hw);
s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan,
- u32 vind, bool vlan_on);
+ u32 vind, bool vlan_on, bool vlvf_bypass);
s32 ixgbe_clear_vfta_generic(struct ixgbe_hw *hw);
s32 ixgbe_check_mac_link_generic(struct ixgbe_hw *hw,
ixgbe_link_speed *speed,
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c
index a507a6fe3624..02c7333a9c83 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c
@@ -139,6 +139,11 @@ s32 ixgbe_dcb_calculate_tc_credits(struct ixgbe_hw *hw,
/* Calculate credit refill ratio using multiplier */
credit_refill = min(link_percentage * min_multiplier,
MAX_CREDIT_REFILL);
+
+ /* Refill at least minimum credit */
+ if (credit_refill < min_credit)
+ credit_refill = min_credit;
+
p->data_credits_refill = (u16)credit_refill;
/* Calculate maximum credit for the TC */
@@ -149,7 +154,7 @@ s32 ixgbe_dcb_calculate_tc_credits(struct ixgbe_hw *hw,
* of a TC is too small, the maximum credit may not be
* enough to send out a jumbo frame in data plane arbitration.
*/
- if (credit_max && (credit_max < min_credit))
+ if (credit_max < min_credit)
credit_max = min_credit;
if (direction == DCB_TX_CONFIG) {
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c
index 23277ab153b6..b5cc989a3d23 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.c
@@ -223,13 +223,13 @@ s32 ixgbe_dcb_config_pfc_82599(struct ixgbe_hw *hw, u8 pfc_en, u8 *prio_tc)
reg |= IXGBE_MFLCN_DPF;
/*
- * X540 supports per TC Rx priority flow control. So
- * clear all TCs and only enable those that should be
+ * X540 & X550 supports per TC Rx priority flow control.
+ * So clear all TCs and only enable those that should be
* enabled.
*/
reg &= ~(IXGBE_MFLCN_RPFCE_MASK | IXGBE_MFLCN_RFCE);
- if (hw->mac.type == ixgbe_mac_X540)
+ if (hw->mac.type >= ixgbe_mac_X540)
reg |= pfc_en << IXGBE_MFLCN_RPFCE_SHIFT;
if (pfc_en)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
index d681273bd39d..bea96b3bc90c 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
@@ -151,47 +151,70 @@ static const char ixgbe_gstrings_test[][ETH_GSTRING_LEN] = {
};
#define IXGBE_TEST_LEN sizeof(ixgbe_gstrings_test) / ETH_GSTRING_LEN
+/* currently supported speeds for 10G */
+#define ADVRTSD_MSK_10G (SUPPORTED_10000baseT_Full | \
+ SUPPORTED_10000baseKX4_Full | \
+ SUPPORTED_10000baseKR_Full)
+
+#define ixgbe_isbackplane(type) ((type) == ixgbe_media_type_backplane)
+
+static u32 ixgbe_get_supported_10gtypes(struct ixgbe_hw *hw)
+{
+ if (!ixgbe_isbackplane(hw->phy.media_type))
+ return SUPPORTED_10000baseT_Full;
+
+ switch (hw->device_id) {
+ case IXGBE_DEV_ID_82598:
+ case IXGBE_DEV_ID_82599_KX4:
+ case IXGBE_DEV_ID_82599_KX4_MEZZ:
+ case IXGBE_DEV_ID_X550EM_X_KX4:
+ return SUPPORTED_10000baseKX4_Full;
+ case IXGBE_DEV_ID_82598_BX:
+ case IXGBE_DEV_ID_82599_KR:
+ case IXGBE_DEV_ID_X550EM_X_KR:
+ return SUPPORTED_10000baseKR_Full;
+ default:
+ return SUPPORTED_10000baseKX4_Full |
+ SUPPORTED_10000baseKR_Full;
+ }
+}
+
static int ixgbe_get_settings(struct net_device *netdev,
struct ethtool_cmd *ecmd)
{
struct ixgbe_adapter *adapter = netdev_priv(netdev);
struct ixgbe_hw *hw = &adapter->hw;
ixgbe_link_speed supported_link;
- u32 link_speed = 0;
bool autoneg = false;
- bool link_up;
hw->mac.ops.get_link_capabilities(hw, &supported_link, &autoneg);
/* set the supported link speeds */
if (supported_link & IXGBE_LINK_SPEED_10GB_FULL)
- ecmd->supported |= SUPPORTED_10000baseT_Full;
- if (supported_link & IXGBE_LINK_SPEED_2_5GB_FULL)
- ecmd->supported |= SUPPORTED_2500baseX_Full;
+ ecmd->supported |= ixgbe_get_supported_10gtypes(hw);
if (supported_link & IXGBE_LINK_SPEED_1GB_FULL)
ecmd->supported |= SUPPORTED_1000baseT_Full;
if (supported_link & IXGBE_LINK_SPEED_100_FULL)
- ecmd->supported |= SUPPORTED_100baseT_Full;
+ ecmd->supported |= ixgbe_isbackplane(hw->phy.media_type) ?
+ SUPPORTED_1000baseKX_Full :
+ SUPPORTED_1000baseT_Full;
+ /* default advertised speed if phy.autoneg_advertised isn't set */
+ ecmd->advertising = ecmd->supported;
/* set the advertised speeds */
if (hw->phy.autoneg_advertised) {
+ ecmd->advertising = 0;
if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_100_FULL)
ecmd->advertising |= ADVERTISED_100baseT_Full;
if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL)
- ecmd->advertising |= ADVERTISED_10000baseT_Full;
- if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_2_5GB_FULL)
- ecmd->advertising |= ADVERTISED_2500baseX_Full;
- if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL)
- ecmd->advertising |= ADVERTISED_1000baseT_Full;
+ ecmd->advertising |= ecmd->supported & ADVRTSD_MSK_10G;
+ if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL) {
+ if (ecmd->supported & SUPPORTED_1000baseKX_Full)
+ ecmd->advertising |= ADVERTISED_1000baseKX_Full;
+ else
+ ecmd->advertising |= ADVERTISED_1000baseT_Full;
+ }
} else {
- /* default modes in case phy.autoneg_advertised isn't set */
- if (supported_link & IXGBE_LINK_SPEED_10GB_FULL)
- ecmd->advertising |= ADVERTISED_10000baseT_Full;
- if (supported_link & IXGBE_LINK_SPEED_1GB_FULL)
- ecmd->advertising |= ADVERTISED_1000baseT_Full;
- if (supported_link & IXGBE_LINK_SPEED_100_FULL)
- ecmd->advertising |= ADVERTISED_100baseT_Full;
-
if (hw->phy.multispeed_fiber && !autoneg) {
if (supported_link & IXGBE_LINK_SPEED_10GB_FULL)
ecmd->advertising = ADVERTISED_10000baseT_Full;
@@ -229,6 +252,10 @@ static int ixgbe_get_settings(struct net_device *netdev,
case ixgbe_phy_sfp_avago:
case ixgbe_phy_sfp_intel:
case ixgbe_phy_sfp_unknown:
+ case ixgbe_phy_qsfp_passive_unknown:
+ case ixgbe_phy_qsfp_active_unknown:
+ case ixgbe_phy_qsfp_intel:
+ case ixgbe_phy_qsfp_unknown:
/* SFP+ devices, further checking needed */
switch (adapter->hw.phy.sfp_type) {
case ixgbe_sfp_type_da_cu:
@@ -284,9 +311,8 @@ static int ixgbe_get_settings(struct net_device *netdev,
break;
}
- hw->mac.ops.check_link(hw, &link_speed, &link_up, false);
- if (link_up) {
- switch (link_speed) {
+ if (netif_carrier_ok(netdev)) {
+ switch (adapter->link_speed) {
case IXGBE_LINK_SPEED_10GB_FULL:
ethtool_cmd_speed_set(ecmd, SPEED_10000);
break;
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c
index 631c603fc966..2a653ec954f5 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_fcoe.c
@@ -77,7 +77,7 @@ int ixgbe_fcoe_ddp_put(struct net_device *netdev, u16 xid)
if (!netdev)
return 0;
- if (xid >= IXGBE_FCOE_DDP_MAX)
+ if (xid >= netdev->fcoe_ddp_xid)
return 0;
adapter = netdev_priv(netdev);
@@ -177,7 +177,7 @@ static int ixgbe_fcoe_ddp_setup(struct net_device *netdev, u16 xid,
return 0;
adapter = netdev_priv(netdev);
- if (xid >= IXGBE_FCOE_DDP_MAX) {
+ if (xid >= netdev->fcoe_ddp_xid) {
e_warn(drv, "xid=0x%x out-of-range\n", xid);
return 0;
}
@@ -517,6 +517,7 @@ int ixgbe_fso(struct ixgbe_ring *tx_ring,
u32 vlan_macip_lens;
u32 fcoe_sof_eof = 0;
u32 mss_l4len_idx;
+ u32 type_tucmd = IXGBE_ADVTXT_TUCMD_FCOE;
u8 sof, eof;
if (skb_is_gso(skb) && (skb_shinfo(skb)->gso_type != SKB_GSO_FCOE)) {
@@ -593,6 +594,8 @@ int ixgbe_fso(struct ixgbe_ring *tx_ring,
skb_shinfo(skb)->gso_size);
first->bytecount += (first->gso_segs - 1) * *hdr_len;
first->tx_flags |= IXGBE_TX_FLAGS_TSO;
+ /* Hardware expects L4T to be RSV for FCoE TSO */
+ type_tucmd |= IXGBE_ADVTXD_TUCMD_L4T_RSV;
}
/* set flag indicating FCOE to ixgbe_tx_map call */
@@ -610,7 +613,7 @@ int ixgbe_fso(struct ixgbe_ring *tx_ring,
/* write context desc */
ixgbe_tx_ctxtdesc(tx_ring, vlan_macip_lens, fcoe_sof_eof,
- IXGBE_ADVTXT_TUCMD_FCOE, mss_l4len_idx);
+ type_tucmd, mss_l4len_idx);
return 0;
}
@@ -620,8 +623,7 @@ static void ixgbe_fcoe_dma_pool_free(struct ixgbe_fcoe *fcoe, unsigned int cpu)
struct ixgbe_fcoe_ddp_pool *ddp_pool;
ddp_pool = per_cpu_ptr(fcoe->ddp_pool, cpu);
- if (ddp_pool->pool)
- dma_pool_destroy(ddp_pool->pool);
+ dma_pool_destroy(ddp_pool->pool);
ddp_pool->pool = NULL;
}
@@ -997,8 +999,7 @@ int ixgbe_fcoe_get_hbainfo(struct net_device *netdev,
return -EINVAL;
/* Don't return information on unsupported devices */
- if (hw->mac.type != ixgbe_mac_82599EB &&
- hw->mac.type != ixgbe_mac_X540)
+ if (!(adapter->flags & IXGBE_FLAG_FCOE_ENABLED))
return -EINVAL;
/* Manufacturer */
@@ -1044,6 +1045,10 @@ int ixgbe_fcoe_get_hbainfo(struct net_device *netdev,
snprintf(info->model,
sizeof(info->model),
"Intel 82599");
+ } else if (hw->mac.type == ixgbe_mac_X550) {
+ snprintf(info->model,
+ sizeof(info->model),
+ "Intel X550");
} else {
snprintf(info->model,
sizeof(info->model),
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
index f3168bcc7d87..e771e764daa3 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
@@ -844,7 +844,6 @@ static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter,
/* initialize NAPI */
netif_napi_add(adapter->netdev, &q_vector->napi,
ixgbe_poll, 64);
- napi_hash_add(&q_vector->napi);
#ifdef CONFIG_NET_RX_BUSY_POLL
/* initialize busy poll */
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 47395ff5d908..c4003a88bbf6 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -65,9 +65,6 @@
#include "ixgbe_common.h"
#include "ixgbe_dcb_82599.h"
#include "ixgbe_sriov.h"
-#ifdef CONFIG_IXGBE_VXLAN
-#include <net/vxlan.h>
-#endif
char ixgbe_driver_name[] = "ixgbe";
static const char ixgbe_driver_string[] =
@@ -175,6 +172,8 @@ MODULE_DESCRIPTION("Intel(R) 10 Gigabit PCI Express Network Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
+static struct workqueue_struct *ixgbe_wq;
+
static bool ixgbe_check_cfg_remove(struct ixgbe_hw *hw, struct pci_dev *pdev);
static int ixgbe_read_pci_cfg_word_parent(struct ixgbe_adapter *adapter,
@@ -316,7 +315,7 @@ static void ixgbe_service_event_schedule(struct ixgbe_adapter *adapter)
if (!test_bit(__IXGBE_DOWN, &adapter->state) &&
!test_bit(__IXGBE_REMOVING, &adapter->state) &&
!test_and_set_bit(__IXGBE_SERVICE_SCHED, &adapter->state))
- schedule_work(&adapter->service_task);
+ queue_work(ixgbe_wq, &adapter->service_task);
}
static void ixgbe_remove_adapter(struct ixgbe_hw *hw)
@@ -1484,7 +1483,7 @@ static inline void ixgbe_rx_checksum(struct ixgbe_ring *ring,
return;
if (ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_ERR_OUTERIPER)) {
- ring->rx_stats.csum_err++;
+ skb->ip_summed = CHECKSUM_NONE;
return;
}
/* If we checked the outer header let the stack know */
@@ -1635,6 +1634,7 @@ static void ixgbe_process_skb_fields(struct ixgbe_ring *rx_ring,
struct sk_buff *skb)
{
struct net_device *dev = rx_ring->netdev;
+ u32 flags = rx_ring->q_vector->adapter->flags;
ixgbe_update_rsc_stats(rx_ring, skb);
@@ -1642,8 +1642,8 @@ static void ixgbe_process_skb_fields(struct ixgbe_ring *rx_ring,
ixgbe_rx_checksum(rx_ring, rx_desc, skb);
- if (unlikely(ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_STAT_TS)))
- ixgbe_ptp_rx_hwtstamp(rx_ring->q_vector->adapter, skb);
+ if (unlikely(flags & IXGBE_FLAG_RX_HWTSTAMP_ENABLED))
+ ixgbe_ptp_rx_hwtstamp(rx_ring, rx_desc, skb);
if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
ixgbe_test_staterr(rx_desc, IXGBE_RXD_STAT_VP)) {
@@ -1659,6 +1659,7 @@ static void ixgbe_process_skb_fields(struct ixgbe_ring *rx_ring,
static void ixgbe_rx_skb(struct ixgbe_q_vector *q_vector,
struct sk_buff *skb)
{
+ skb_mark_napi_id(skb, &q_vector->napi);
if (ixgbe_qv_busy_polling(q_vector))
netif_receive_skb(skb);
else
@@ -2123,7 +2124,6 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
}
#endif /* IXGBE_FCOE */
- skb_mark_napi_id(skb, &q_vector->napi);
ixgbe_rx_skb(q_vector, skb);
/* update budget accounting */
@@ -2741,7 +2741,7 @@ static irqreturn_t ixgbe_msix_other(int irq, void *data)
ixgbe_check_fan_failure(adapter, eicr);
if (unlikely(eicr & IXGBE_EICR_TIMESYNC))
- ixgbe_ptp_check_pps_event(adapter, eicr);
+ ixgbe_ptp_check_pps_event(adapter);
/* re-enable the original interrupt state, no lsc, no queues */
if (!test_bit(__IXGBE_DOWN, &adapter->state))
@@ -2757,7 +2757,7 @@ static irqreturn_t ixgbe_msix_clean_rings(int irq, void *data)
/* EIAM disabled interrupts (on this vector) for us */
if (q_vector->rx.ring || q_vector->tx.ring)
- napi_schedule(&q_vector->napi);
+ napi_schedule_irqoff(&q_vector->napi);
return IRQ_HANDLED;
}
@@ -2786,7 +2786,8 @@ int ixgbe_poll(struct napi_struct *napi, int budget)
ixgbe_for_each_ring(ring, q_vector->tx)
clean_complete &= !!ixgbe_clean_tx_irq(q_vector, ring);
- if (!ixgbe_qv_lock_napi(q_vector))
+ /* Exit if we are called by netpoll or busy polling is active */
+ if ((budget <= 0) || !ixgbe_qv_lock_napi(q_vector))
return budget;
/* attempt to distribute budget to each queue fairly, but don't allow
@@ -2947,10 +2948,10 @@ static irqreturn_t ixgbe_intr(int irq, void *data)
ixgbe_check_fan_failure(adapter, eicr);
if (unlikely(eicr & IXGBE_EICR_TIMESYNC))
- ixgbe_ptp_check_pps_event(adapter, eicr);
+ ixgbe_ptp_check_pps_event(adapter);
/* would disable interrupts here but EIAM disabled it */
- napi_schedule(&q_vector->napi);
+ napi_schedule_irqoff(&q_vector->napi);
/*
* re-enable link(maybe) and non-queue interrupts, no flush.
@@ -3315,8 +3316,7 @@ static void ixgbe_configure_srrctl(struct ixgbe_adapter *adapter,
}
/**
- * Return a number of entries in the RSS indirection table
- *
+ * ixgbe_rss_indir_tbl_entries - Return RSS indirection table entries
* @adapter: device handle
*
* - 82598/82599/X540: 128
@@ -3334,8 +3334,7 @@ u32 ixgbe_rss_indir_tbl_entries(struct ixgbe_adapter *adapter)
}
/**
- * Write the RETA table to HW
- *
+ * ixgbe_store_reta - Write the RETA table to HW
* @adapter: device handle
*
* Write the RSS redirection table stored in adapter.rss_indir_tbl[] to HW.
@@ -3374,8 +3373,7 @@ void ixgbe_store_reta(struct ixgbe_adapter *adapter)
}
/**
- * Write the RETA table to HW (for x550 devices in SRIOV mode)
- *
+ * ixgbe_store_vfreta - Write the RETA table to HW (x550 devices in SRIOV mode)
* @adapter: device handle
*
* Write the RSS redirection table stored in adapter.rss_indir_tbl[] to HW.
@@ -3621,6 +3619,9 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter,
IXGBE_WRITE_REG(hw, IXGBE_RDBAH(reg_idx), (rdba >> 32));
IXGBE_WRITE_REG(hw, IXGBE_RDLEN(reg_idx),
ring->count * sizeof(union ixgbe_adv_rx_desc));
+ /* Force flushing of IXGBE_RDLEN to prevent MDD */
+ IXGBE_WRITE_FLUSH(hw);
+
IXGBE_WRITE_REG(hw, IXGBE_RDH(reg_idx), 0);
IXGBE_WRITE_REG(hw, IXGBE_RDT(reg_idx), 0);
ring->tail = adapter->io_addr + IXGBE_RDT(reg_idx);
@@ -3704,6 +3705,9 @@ static void ixgbe_configure_virtualization(struct ixgbe_adapter *adapter)
/* Map PF MAC address in RAR Entry 0 to first pool following VFs */
hw->mac.ops.set_vmdq(hw, 0, VMDQ_P(0));
+ /* clear VLAN promisc flag so VFTA will be updated if necessary */
+ adapter->flags2 &= ~IXGBE_FLAG2_VLAN_PROMISC;
+
/*
* Set up VF register offsets for selected VT Mode,
* i.e. 32 or 64 VFs for SR-IOV
@@ -3901,12 +3905,56 @@ static int ixgbe_vlan_rx_add_vid(struct net_device *netdev,
struct ixgbe_hw *hw = &adapter->hw;
/* add VID to filter table */
- hw->mac.ops.set_vfta(&adapter->hw, vid, VMDQ_P(0), true);
+ hw->mac.ops.set_vfta(&adapter->hw, vid, VMDQ_P(0), true, true);
set_bit(vid, adapter->active_vlans);
return 0;
}
+static int ixgbe_find_vlvf_entry(struct ixgbe_hw *hw, u32 vlan)
+{
+ u32 vlvf;
+ int idx;
+
+ /* short cut the special case */
+ if (vlan == 0)
+ return 0;
+
+ /* Search for the vlan id in the VLVF entries */
+ for (idx = IXGBE_VLVF_ENTRIES; --idx;) {
+ vlvf = IXGBE_READ_REG(hw, IXGBE_VLVF(idx));
+ if ((vlvf & VLAN_VID_MASK) == vlan)
+ break;
+ }
+
+ return idx;
+}
+
+void ixgbe_update_pf_promisc_vlvf(struct ixgbe_adapter *adapter, u32 vid)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ u32 bits, word;
+ int idx;
+
+ idx = ixgbe_find_vlvf_entry(hw, vid);
+ if (!idx)
+ return;
+
+ /* See if any other pools are set for this VLAN filter
+ * entry other than the PF.
+ */
+ word = idx * 2 + (VMDQ_P(0) / 32);
+ bits = ~(1 << (VMDQ_P(0)) % 32);
+ bits &= IXGBE_READ_REG(hw, IXGBE_VLVFB(word));
+
+ /* Disable the filter so this falls into the default pool. */
+ if (!bits && !IXGBE_READ_REG(hw, IXGBE_VLVFB(word ^ 1))) {
+ if (!(adapter->flags2 & IXGBE_FLAG2_VLAN_PROMISC))
+ IXGBE_WRITE_REG(hw, IXGBE_VLVFB(word), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_VLVF(idx), 0);
+ }
+}
+
static int ixgbe_vlan_rx_kill_vid(struct net_device *netdev,
__be16 proto, u16 vid)
{
@@ -3914,7 +3962,11 @@ static int ixgbe_vlan_rx_kill_vid(struct net_device *netdev,
struct ixgbe_hw *hw = &adapter->hw;
/* remove VID from filter table */
- hw->mac.ops.set_vfta(&adapter->hw, vid, VMDQ_P(0), false);
+ if (adapter->flags2 & IXGBE_FLAG2_VLAN_PROMISC)
+ ixgbe_update_pf_promisc_vlvf(adapter, vid);
+ else
+ hw->mac.ops.set_vfta(hw, vid, VMDQ_P(0), false, true);
+
clear_bit(vid, adapter->active_vlans);
return 0;
@@ -3992,6 +4044,129 @@ static void ixgbe_vlan_strip_enable(struct ixgbe_adapter *adapter)
}
}
+static void ixgbe_vlan_promisc_enable(struct ixgbe_adapter *adapter)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ u32 vlnctrl, i;
+
+ switch (hw->mac.type) {
+ case ixgbe_mac_82599EB:
+ case ixgbe_mac_X540:
+ case ixgbe_mac_X550:
+ case ixgbe_mac_X550EM_x:
+ default:
+ if (adapter->flags & IXGBE_FLAG_VMDQ_ENABLED)
+ break;
+ /* fall through */
+ case ixgbe_mac_82598EB:
+ /* legacy case, we can just disable VLAN filtering */
+ vlnctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL);
+ vlnctrl &= ~(IXGBE_VLNCTRL_VFE | IXGBE_VLNCTRL_CFIEN);
+ IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, vlnctrl);
+ return;
+ }
+
+ /* We are already in VLAN promisc, nothing to do */
+ if (adapter->flags2 & IXGBE_FLAG2_VLAN_PROMISC)
+ return;
+
+ /* Set flag so we don't redo unnecessary work */
+ adapter->flags2 |= IXGBE_FLAG2_VLAN_PROMISC;
+
+ /* Add PF to all active pools */
+ for (i = IXGBE_VLVF_ENTRIES; --i;) {
+ u32 reg_offset = IXGBE_VLVFB(i * 2 + VMDQ_P(0) / 32);
+ u32 vlvfb = IXGBE_READ_REG(hw, reg_offset);
+
+ vlvfb |= 1 << (VMDQ_P(0) % 32);
+ IXGBE_WRITE_REG(hw, reg_offset, vlvfb);
+ }
+
+ /* Set all bits in the VLAN filter table array */
+ for (i = hw->mac.vft_size; i--;)
+ IXGBE_WRITE_REG(hw, IXGBE_VFTA(i), ~0U);
+}
+
+#define VFTA_BLOCK_SIZE 8
+static void ixgbe_scrub_vfta(struct ixgbe_adapter *adapter, u32 vfta_offset)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ u32 vfta[VFTA_BLOCK_SIZE] = { 0 };
+ u32 vid_start = vfta_offset * 32;
+ u32 vid_end = vid_start + (VFTA_BLOCK_SIZE * 32);
+ u32 i, vid, word, bits;
+
+ for (i = IXGBE_VLVF_ENTRIES; --i;) {
+ u32 vlvf = IXGBE_READ_REG(hw, IXGBE_VLVF(i));
+
+ /* pull VLAN ID from VLVF */
+ vid = vlvf & VLAN_VID_MASK;
+
+ /* only concern outselves with a certain range */
+ if (vid < vid_start || vid >= vid_end)
+ continue;
+
+ if (vlvf) {
+ /* record VLAN ID in VFTA */
+ vfta[(vid - vid_start) / 32] |= 1 << (vid % 32);
+
+ /* if PF is part of this then continue */
+ if (test_bit(vid, adapter->active_vlans))
+ continue;
+ }
+
+ /* remove PF from the pool */
+ word = i * 2 + VMDQ_P(0) / 32;
+ bits = ~(1 << (VMDQ_P(0) % 32));
+ bits &= IXGBE_READ_REG(hw, IXGBE_VLVFB(word));
+ IXGBE_WRITE_REG(hw, IXGBE_VLVFB(word), bits);
+ }
+
+ /* extract values from active_vlans and write back to VFTA */
+ for (i = VFTA_BLOCK_SIZE; i--;) {
+ vid = (vfta_offset + i) * 32;
+ word = vid / BITS_PER_LONG;
+ bits = vid % BITS_PER_LONG;
+
+ vfta[i] |= adapter->active_vlans[word] >> bits;
+
+ IXGBE_WRITE_REG(hw, IXGBE_VFTA(vfta_offset + i), vfta[i]);
+ }
+}
+
+static void ixgbe_vlan_promisc_disable(struct ixgbe_adapter *adapter)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ u32 vlnctrl, i;
+
+ switch (hw->mac.type) {
+ case ixgbe_mac_82599EB:
+ case ixgbe_mac_X540:
+ case ixgbe_mac_X550:
+ case ixgbe_mac_X550EM_x:
+ default:
+ if (adapter->flags & IXGBE_FLAG_VMDQ_ENABLED)
+ break;
+ /* fall through */
+ case ixgbe_mac_82598EB:
+ vlnctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL);
+ vlnctrl &= ~IXGBE_VLNCTRL_CFIEN;
+ vlnctrl |= IXGBE_VLNCTRL_VFE;
+ IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, vlnctrl);
+ return;
+ }
+
+ /* We are not in VLAN promisc, nothing to do */
+ if (!(adapter->flags2 & IXGBE_FLAG2_VLAN_PROMISC))
+ return;
+
+ /* Set flag so we don't redo unnecessary work */
+ adapter->flags2 &= ~IXGBE_FLAG2_VLAN_PROMISC;
+
+ for (i = 0; i < hw->mac.vft_size; i += VFTA_BLOCK_SIZE)
+ ixgbe_scrub_vfta(adapter, i);
+}
+
static void ixgbe_restore_vlan(struct ixgbe_adapter *adapter)
{
u16 vid;
@@ -4034,124 +4209,156 @@ static int ixgbe_write_mc_addr_list(struct net_device *netdev)
#ifdef CONFIG_PCI_IOV
void ixgbe_full_sync_mac_table(struct ixgbe_adapter *adapter)
{
+ struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
struct ixgbe_hw *hw = &adapter->hw;
int i;
- for (i = 0; i < hw->mac.num_rar_entries; i++) {
- if (adapter->mac_table[i].state & IXGBE_MAC_STATE_IN_USE)
- hw->mac.ops.set_rar(hw, i, adapter->mac_table[i].addr,
- adapter->mac_table[i].queue,
+
+ for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) {
+ mac_table->state &= ~IXGBE_MAC_STATE_MODIFIED;
+
+ if (mac_table->state & IXGBE_MAC_STATE_IN_USE)
+ hw->mac.ops.set_rar(hw, i,
+ mac_table->addr,
+ mac_table->pool,
IXGBE_RAH_AV);
else
hw->mac.ops.clear_rar(hw, i);
-
- adapter->mac_table[i].state &= ~(IXGBE_MAC_STATE_MODIFIED);
}
}
-#endif
+#endif
static void ixgbe_sync_mac_table(struct ixgbe_adapter *adapter)
{
+ struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
struct ixgbe_hw *hw = &adapter->hw;
int i;
- for (i = 0; i < hw->mac.num_rar_entries; i++) {
- if (adapter->mac_table[i].state & IXGBE_MAC_STATE_MODIFIED) {
- if (adapter->mac_table[i].state &
- IXGBE_MAC_STATE_IN_USE)
- hw->mac.ops.set_rar(hw, i,
- adapter->mac_table[i].addr,
- adapter->mac_table[i].queue,
- IXGBE_RAH_AV);
- else
- hw->mac.ops.clear_rar(hw, i);
- adapter->mac_table[i].state &=
- ~(IXGBE_MAC_STATE_MODIFIED);
- }
+ for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) {
+ if (!(mac_table->state & IXGBE_MAC_STATE_MODIFIED))
+ continue;
+
+ mac_table->state &= ~IXGBE_MAC_STATE_MODIFIED;
+
+ if (mac_table->state & IXGBE_MAC_STATE_IN_USE)
+ hw->mac.ops.set_rar(hw, i,
+ mac_table->addr,
+ mac_table->pool,
+ IXGBE_RAH_AV);
+ else
+ hw->mac.ops.clear_rar(hw, i);
}
}
static void ixgbe_flush_sw_mac_table(struct ixgbe_adapter *adapter)
{
- int i;
+ struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
struct ixgbe_hw *hw = &adapter->hw;
+ int i;
- for (i = 0; i < hw->mac.num_rar_entries; i++) {
- adapter->mac_table[i].state |= IXGBE_MAC_STATE_MODIFIED;
- adapter->mac_table[i].state &= ~IXGBE_MAC_STATE_IN_USE;
- eth_zero_addr(adapter->mac_table[i].addr);
- adapter->mac_table[i].queue = 0;
+ for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) {
+ mac_table->state |= IXGBE_MAC_STATE_MODIFIED;
+ mac_table->state &= ~IXGBE_MAC_STATE_IN_USE;
}
+
ixgbe_sync_mac_table(adapter);
}
-static int ixgbe_available_rars(struct ixgbe_adapter *adapter)
+static int ixgbe_available_rars(struct ixgbe_adapter *adapter, u16 pool)
{
+ struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
struct ixgbe_hw *hw = &adapter->hw;
int i, count = 0;
- for (i = 0; i < hw->mac.num_rar_entries; i++) {
- if (adapter->mac_table[i].state == 0)
- count++;
+ for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) {
+ /* do not count default RAR as available */
+ if (mac_table->state & IXGBE_MAC_STATE_DEFAULT)
+ continue;
+
+ /* only count unused and addresses that belong to us */
+ if (mac_table->state & IXGBE_MAC_STATE_IN_USE) {
+ if (mac_table->pool != pool)
+ continue;
+ }
+
+ count++;
}
+
return count;
}
/* this function destroys the first RAR entry */
-static void ixgbe_mac_set_default_filter(struct ixgbe_adapter *adapter,
- u8 *addr)
+static void ixgbe_mac_set_default_filter(struct ixgbe_adapter *adapter)
{
+ struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
struct ixgbe_hw *hw = &adapter->hw;
- memcpy(&adapter->mac_table[0].addr, addr, ETH_ALEN);
- adapter->mac_table[0].queue = VMDQ_P(0);
- adapter->mac_table[0].state = (IXGBE_MAC_STATE_DEFAULT |
- IXGBE_MAC_STATE_IN_USE);
- hw->mac.ops.set_rar(hw, 0, adapter->mac_table[0].addr,
- adapter->mac_table[0].queue,
+ memcpy(&mac_table->addr, hw->mac.addr, ETH_ALEN);
+ mac_table->pool = VMDQ_P(0);
+
+ mac_table->state = IXGBE_MAC_STATE_DEFAULT | IXGBE_MAC_STATE_IN_USE;
+
+ hw->mac.ops.set_rar(hw, 0, mac_table->addr, mac_table->pool,
IXGBE_RAH_AV);
}
-int ixgbe_add_mac_filter(struct ixgbe_adapter *adapter, u8 *addr, u16 queue)
+int ixgbe_add_mac_filter(struct ixgbe_adapter *adapter,
+ const u8 *addr, u16 pool)
{
+ struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
struct ixgbe_hw *hw = &adapter->hw;
int i;
if (is_zero_ether_addr(addr))
return -EINVAL;
- for (i = 0; i < hw->mac.num_rar_entries; i++) {
- if (adapter->mac_table[i].state & IXGBE_MAC_STATE_IN_USE)
+ for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) {
+ if (mac_table->state & IXGBE_MAC_STATE_IN_USE)
continue;
- adapter->mac_table[i].state |= (IXGBE_MAC_STATE_MODIFIED |
- IXGBE_MAC_STATE_IN_USE);
- ether_addr_copy(adapter->mac_table[i].addr, addr);
- adapter->mac_table[i].queue = queue;
+
+ ether_addr_copy(mac_table->addr, addr);
+ mac_table->pool = pool;
+
+ mac_table->state |= IXGBE_MAC_STATE_MODIFIED |
+ IXGBE_MAC_STATE_IN_USE;
+
ixgbe_sync_mac_table(adapter);
+
return i;
}
+
return -ENOMEM;
}
-int ixgbe_del_mac_filter(struct ixgbe_adapter *adapter, u8 *addr, u16 queue)
+int ixgbe_del_mac_filter(struct ixgbe_adapter *adapter,
+ const u8 *addr, u16 pool)
{
- /* search table for addr, if found, set to 0 and sync */
- int i;
+ struct ixgbe_mac_addr *mac_table = &adapter->mac_table[0];
struct ixgbe_hw *hw = &adapter->hw;
+ int i;
if (is_zero_ether_addr(addr))
return -EINVAL;
- for (i = 0; i < hw->mac.num_rar_entries; i++) {
- if (ether_addr_equal(addr, adapter->mac_table[i].addr) &&
- adapter->mac_table[i].queue == queue) {
- adapter->mac_table[i].state |= IXGBE_MAC_STATE_MODIFIED;
- adapter->mac_table[i].state &= ~IXGBE_MAC_STATE_IN_USE;
- eth_zero_addr(adapter->mac_table[i].addr);
- adapter->mac_table[i].queue = 0;
- ixgbe_sync_mac_table(adapter);
- return 0;
- }
+ /* search table for addr, if found clear IN_USE flag and sync */
+ for (i = 0; i < hw->mac.num_rar_entries; i++, mac_table++) {
+ /* we can only delete an entry if it is in use */
+ if (!(mac_table->state & IXGBE_MAC_STATE_IN_USE))
+ continue;
+ /* we only care about entries that belong to the given pool */
+ if (mac_table->pool != pool)
+ continue;
+ /* we only care about a specific MAC address */
+ if (!ether_addr_equal(addr, mac_table->addr))
+ continue;
+
+ mac_table->state |= IXGBE_MAC_STATE_MODIFIED;
+ mac_table->state &= ~IXGBE_MAC_STATE_IN_USE;
+
+ ixgbe_sync_mac_table(adapter);
+
+ return 0;
}
+
return -ENOMEM;
}
/**
@@ -4169,7 +4376,7 @@ static int ixgbe_write_uc_addr_list(struct net_device *netdev, int vfn)
int count = 0;
/* return ENOMEM indicating insufficient memory for addresses */
- if (netdev_uc_count(netdev) > ixgbe_available_rars(adapter))
+ if (netdev_uc_count(netdev) > ixgbe_available_rars(adapter, vfn))
return -ENOMEM;
if (!netdev_uc_empty(netdev)) {
@@ -4183,6 +4390,25 @@ static int ixgbe_write_uc_addr_list(struct net_device *netdev, int vfn)
return count;
}
+static int ixgbe_uc_sync(struct net_device *netdev, const unsigned char *addr)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ int ret;
+
+ ret = ixgbe_add_mac_filter(adapter, addr, VMDQ_P(0));
+
+ return min_t(int, ret, 0);
+}
+
+static int ixgbe_uc_unsync(struct net_device *netdev, const unsigned char *addr)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ ixgbe_del_mac_filter(adapter, addr, VMDQ_P(0));
+
+ return 0;
+}
+
/**
* ixgbe_set_rx_mode - Unicast, Multicast and Promiscuous mode set
* @netdev: network interface device structure
@@ -4197,12 +4423,10 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
struct ixgbe_adapter *adapter = netdev_priv(netdev);
struct ixgbe_hw *hw = &adapter->hw;
u32 fctrl, vmolr = IXGBE_VMOLR_BAM | IXGBE_VMOLR_AUPE;
- u32 vlnctrl;
int count;
/* Check for Promiscuous and All Multicast modes */
fctrl = IXGBE_READ_REG(hw, IXGBE_FCTRL);
- vlnctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL);
/* set all bits that we expect to always be set */
fctrl &= ~IXGBE_FCTRL_SBP; /* disable store-bad-packets */
@@ -4212,25 +4436,18 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
/* clear the bits we are changing the status of */
fctrl &= ~(IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE);
- vlnctrl &= ~(IXGBE_VLNCTRL_VFE | IXGBE_VLNCTRL_CFIEN);
if (netdev->flags & IFF_PROMISC) {
hw->addr_ctrl.user_set_promisc = true;
fctrl |= (IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE);
vmolr |= IXGBE_VMOLR_MPE;
- /* Only disable hardware filter vlans in promiscuous mode
- * if SR-IOV and VMDQ are disabled - otherwise ensure
- * that hardware VLAN filters remain enabled.
- */
- if (adapter->flags & (IXGBE_FLAG_VMDQ_ENABLED |
- IXGBE_FLAG_SRIOV_ENABLED))
- vlnctrl |= (IXGBE_VLNCTRL_VFE | IXGBE_VLNCTRL_CFIEN);
+ ixgbe_vlan_promisc_enable(adapter);
} else {
if (netdev->flags & IFF_ALLMULTI) {
fctrl |= IXGBE_FCTRL_MPE;
vmolr |= IXGBE_VMOLR_MPE;
}
- vlnctrl |= IXGBE_VLNCTRL_VFE;
hw->addr_ctrl.user_set_promisc = false;
+ ixgbe_vlan_promisc_disable(adapter);
}
/*
@@ -4238,8 +4455,7 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
* sufficient space to store all the addresses then enable
* unicast promiscuous mode
*/
- count = ixgbe_write_uc_addr_list(netdev, VMDQ_P(0));
- if (count < 0) {
+ if (__dev_uc_sync(netdev, ixgbe_uc_sync, ixgbe_uc_unsync)) {
fctrl |= IXGBE_FCTRL_UPE;
vmolr |= IXGBE_VMOLR_ROPE;
}
@@ -4275,7 +4491,6 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
/* NOTE: VLAN filtering is disabled by setting PROMISC */
}
- IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, vlnctrl);
IXGBE_WRITE_REG(hw, IXGBE_FCTRL, fctrl);
if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX)
@@ -5042,7 +5257,6 @@ void ixgbe_reset(struct ixgbe_adapter *adapter)
struct ixgbe_hw *hw = &adapter->hw;
struct net_device *netdev = adapter->netdev;
int err;
- u8 old_addr[ETH_ALEN];
if (ixgbe_removed(hw->hw_addr))
return;
@@ -5078,10 +5292,13 @@ void ixgbe_reset(struct ixgbe_adapter *adapter)
}
clear_bit(__IXGBE_IN_SFP_INIT, &adapter->state);
- /* do not flush user set addresses */
- memcpy(old_addr, &adapter->mac_table[0].addr, netdev->addr_len);
+
+ /* flush entries out of MAC table */
ixgbe_flush_sw_mac_table(adapter);
- ixgbe_mac_set_default_filter(adapter, old_addr);
+ __dev_uc_unsync(netdev, NULL);
+
+ /* do not flush user set addresses */
+ ixgbe_mac_set_default_filter(adapter);
/* update SAN MAC vmdq pool selection */
if (hw->mac.san_mac_rar_index)
@@ -5331,6 +5548,8 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter)
adapter->mac_table = kzalloc(sizeof(struct ixgbe_mac_addr) *
hw->mac.num_rar_entries,
GFP_ATOMIC);
+ if (!adapter->mac_table)
+ return -ENOMEM;
/* Set MAC specific capability flags and exceptions */
switch (hw->mac.type) {
@@ -6616,10 +6835,8 @@ static void ixgbe_check_for_bad_vf(struct ixgbe_adapter *adapter)
{
struct ixgbe_hw *hw = &adapter->hw;
struct pci_dev *pdev = adapter->pdev;
- struct pci_dev *vfdev;
+ unsigned int vf;
u32 gpc;
- int pos;
- unsigned short vf_id;
if (!(netif_carrier_ok(adapter->netdev)))
return;
@@ -6636,26 +6853,17 @@ static void ixgbe_check_for_bad_vf(struct ixgbe_adapter *adapter)
if (!pdev)
return;
- pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
- if (!pos)
- return;
-
- /* get the device ID for the VF */
- pci_read_config_word(pdev, pos + PCI_SRIOV_VF_DID, &vf_id);
-
/* check status reg for all VFs owned by this PF */
- vfdev = pci_get_device(pdev->vendor, vf_id, NULL);
- while (vfdev) {
- if (vfdev->is_virtfn && (vfdev->physfn == pdev)) {
- u16 status_reg;
-
- pci_read_config_word(vfdev, PCI_STATUS, &status_reg);
- if (status_reg & PCI_STATUS_REC_MASTER_ABORT)
- /* issue VFLR */
- ixgbe_issue_vf_flr(adapter, vfdev);
- }
+ for (vf = 0; vf < adapter->num_vfs; ++vf) {
+ struct pci_dev *vfdev = adapter->vfinfo[vf].vfdev;
+ u16 status_reg;
- vfdev = pci_get_device(pdev->vendor, vf_id, vfdev);
+ if (!vfdev)
+ continue;
+ pci_read_config_word(vfdev, PCI_STATUS, &status_reg);
+ if (status_reg != IXGBE_FAILED_READ_CFG_WORD &&
+ status_reg & PCI_STATUS_REC_MASTER_ABORT)
+ ixgbe_issue_vf_flr(adapter, vfdev);
}
}
@@ -7024,6 +7232,7 @@ static void ixgbe_tx_csum(struct ixgbe_ring *tx_ring,
struct tcphdr *tcphdr;
u8 *raw;
} transport_hdr;
+ __be16 frag_off;
if (skb->encapsulation) {
network_hdr.raw = skb_inner_network_header(skb);
@@ -7047,13 +7256,17 @@ static void ixgbe_tx_csum(struct ixgbe_ring *tx_ring,
case 6:
vlan_macip_lens |= transport_hdr.raw - network_hdr.raw;
l4_hdr = network_hdr.ipv6->nexthdr;
+ if (likely((transport_hdr.raw - network_hdr.raw) ==
+ sizeof(struct ipv6hdr)))
+ break;
+ ipv6_skip_exthdr(skb, network_hdr.raw - skb->data +
+ sizeof(struct ipv6hdr),
+ &l4_hdr, &frag_off);
+ if (unlikely(frag_off))
+ l4_hdr = NEXTHDR_FRAGMENT;
break;
default:
- if (unlikely(net_ratelimit())) {
- dev_warn(tx_ring->dev,
- "partial checksum but version=%d\n",
- network_hdr.ipv4->version);
- }
+ break;
}
switch (l4_hdr) {
@@ -7074,16 +7287,18 @@ static void ixgbe_tx_csum(struct ixgbe_ring *tx_ring,
default:
if (unlikely(net_ratelimit())) {
dev_warn(tx_ring->dev,
- "partial checksum but l4 proto=%x!\n",
- l4_hdr);
+ "partial checksum, version=%d, l4 proto=%x\n",
+ network_hdr.ipv4->version, l4_hdr);
}
- break;
+ skb_checksum_help(skb);
+ goto no_csum;
}
/* update TX checksum flag */
first->tx_flags |= IXGBE_TX_FLAGS_CSUM;
}
+no_csum:
/* vlan_macip_lens: MACLEN, VLAN tag */
vlan_macip_lens |= first->tx_flags & IXGBE_TX_FLAGS_VLAN_MASK;
@@ -7358,7 +7573,9 @@ static void ixgbe_atr(struct ixgbe_ring *ring,
/* snag network header to get L4 type and address */
skb = first->skb;
hdr.network = skb_network_header(skb);
- if (skb->encapsulation) {
+ if (!skb->encapsulation) {
+ th = tcp_hdr(skb);
+ } else {
#ifdef CONFIG_IXGBE_VXLAN
struct ixgbe_adapter *adapter = q_vector->adapter;
@@ -7377,14 +7594,34 @@ static void ixgbe_atr(struct ixgbe_ring *ring,
#else
return;
#endif /* CONFIG_IXGBE_VXLAN */
- } else {
- /* Currently only IPv4/IPv6 with TCP is supported */
- if ((first->protocol != htons(ETH_P_IPV6) ||
- hdr.ipv6->nexthdr != IPPROTO_TCP) &&
- (first->protocol != htons(ETH_P_IP) ||
- hdr.ipv4->protocol != IPPROTO_TCP))
+ }
+
+ /* Currently only IPv4/IPv6 with TCP is supported */
+ switch (hdr.ipv4->version) {
+ case IPVERSION:
+ if (hdr.ipv4->protocol != IPPROTO_TCP)
return;
- th = tcp_hdr(skb);
+ break;
+ case 6:
+ if (likely((unsigned char *)th - hdr.network ==
+ sizeof(struct ipv6hdr))) {
+ if (hdr.ipv6->nexthdr != IPPROTO_TCP)
+ return;
+ } else {
+ __be16 frag_off;
+ u8 l4_hdr;
+
+ ipv6_skip_exthdr(skb, hdr.network - skb->data +
+ sizeof(struct ipv6hdr),
+ &l4_hdr, &frag_off);
+ if (unlikely(frag_off))
+ return;
+ if (l4_hdr != IPPROTO_TCP)
+ return;
+ }
+ break;
+ default:
+ return;
}
/* skip this packet since it is invalid or the socket is closing */
@@ -7419,10 +7656,12 @@ static void ixgbe_atr(struct ixgbe_ring *ring,
common.port.src ^= th->dest ^ first->protocol;
common.port.dst ^= th->source;
- if (first->protocol == htons(ETH_P_IP)) {
+ switch (hdr.ipv4->version) {
+ case IPVERSION:
input.formatted.flow_type = IXGBE_ATR_FLOW_TYPE_TCPV4;
common.ip ^= hdr.ipv4->saddr ^ hdr.ipv4->daddr;
- } else {
+ break;
+ case 6:
input.formatted.flow_type = IXGBE_ATR_FLOW_TYPE_TCPV6;
common.ip ^= hdr.ipv6->saddr.s6_addr32[0] ^
hdr.ipv6->saddr.s6_addr32[1] ^
@@ -7432,6 +7671,9 @@ static void ixgbe_atr(struct ixgbe_ring *ring,
hdr.ipv6->daddr.s6_addr32[1] ^
hdr.ipv6->daddr.s6_addr32[2] ^
hdr.ipv6->daddr.s6_addr32[3];
+ break;
+ default:
+ break;
}
#ifdef CONFIG_IXGBE_VXLAN
@@ -7659,17 +7901,16 @@ static int ixgbe_set_mac(struct net_device *netdev, void *p)
struct ixgbe_adapter *adapter = netdev_priv(netdev);
struct ixgbe_hw *hw = &adapter->hw;
struct sockaddr *addr = p;
- int ret;
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
- ixgbe_del_mac_filter(adapter, hw->mac.addr, VMDQ_P(0));
memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
memcpy(hw->mac.addr, addr->sa_data, netdev->addr_len);
- ret = ixgbe_add_mac_filter(adapter, hw->mac.addr, VMDQ_P(0));
- return ret > 0 ? 0 : ret;
+ ixgbe_mac_set_default_filter(adapter);
+
+ return 0;
}
static int
@@ -7920,6 +8161,9 @@ int ixgbe_setup_tc(struct net_device *dev, u8 tc)
*/
if (netif_running(dev))
ixgbe_close(dev);
+ else
+ ixgbe_reset(adapter);
+
ixgbe_clear_interrupt_scheme(adapter);
#ifdef CONFIG_IXGBE_DCB
@@ -8152,7 +8396,10 @@ static int ixgbe_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
{
/* guarantee we can provide a unique filter for the unicast address */
if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr)) {
- if (IXGBE_MAX_PF_MACVLANS <= netdev_uc_count(dev))
+ struct ixgbe_adapter *adapter = netdev_priv(dev);
+ u16 pool = VMDQ_P(0);
+
+ if (netdev_uc_count(dev) >= ixgbe_available_rars(adapter, pool))
return -ENOMEM;
}
@@ -8384,7 +8631,7 @@ ixgbe_features_check(struct sk_buff *skb, struct net_device *dev,
if (unlikely(skb_inner_mac_header(skb) - skb_transport_header(skb) >
IXGBE_MAX_TUNNEL_HDR_LEN))
- return features & ~NETIF_F_ALL_CSUM;
+ return features & ~NETIF_F_CSUM_MASK;
return features;
}
@@ -8781,8 +9028,8 @@ skip_sriov:
case ixgbe_mac_X540:
case ixgbe_mac_X550:
case ixgbe_mac_X550EM_x:
- netdev->features |= NETIF_F_SCTP_CSUM;
- netdev->hw_features |= NETIF_F_SCTP_CSUM |
+ netdev->features |= NETIF_F_SCTP_CRC;
+ netdev->hw_features |= NETIF_F_SCTP_CRC |
NETIF_F_NTUPLE;
break;
default:
@@ -8798,8 +9045,7 @@ skip_sriov:
netdev->vlan_features |= NETIF_F_IPV6_CSUM;
netdev->vlan_features |= NETIF_F_SG;
- netdev->hw_enc_features |= NETIF_F_SG | NETIF_F_IP_CSUM |
- NETIF_F_IPV6_CSUM;
+ netdev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
netdev->priv_flags |= IFF_UNICAST_FLT;
netdev->priv_flags |= IFF_SUPP_NOFCS;
@@ -8808,9 +9054,7 @@ skip_sriov:
switch (adapter->hw.mac.type) {
case ixgbe_mac_X550:
case ixgbe_mac_X550EM_x:
- netdev->hw_enc_features |= NETIF_F_RXCSUM |
- NETIF_F_IP_CSUM |
- NETIF_F_IPV6_CSUM;
+ netdev->hw_enc_features |= NETIF_F_RXCSUM;
break;
default:
break;
@@ -8870,7 +9114,7 @@ skip_sriov:
goto err_sw_init;
}
- ixgbe_mac_set_default_filter(adapter, hw->mac.perm_addr);
+ ixgbe_mac_set_default_filter(adapter);
setup_timer(&adapter->service_timer, &ixgbe_service_timer,
(unsigned long) adapter);
@@ -9325,6 +9569,12 @@ static int __init ixgbe_init_module(void)
pr_info("%s - version %s\n", ixgbe_driver_string, ixgbe_driver_version);
pr_info("%s\n", ixgbe_copyright);
+ ixgbe_wq = create_singlethread_workqueue(ixgbe_driver_name);
+ if (!ixgbe_wq) {
+ pr_err("%s: Failed to create workqueue\n", ixgbe_driver_name);
+ return -ENOMEM;
+ }
+
ixgbe_dbg_init();
ret = pci_register_driver(&ixgbe_driver);
@@ -9356,6 +9606,10 @@ static void __exit ixgbe_exit_module(void)
pci_unregister_driver(&ixgbe_driver);
ixgbe_dbg_exit();
+ if (ixgbe_wq) {
+ destroy_workqueue(ixgbe_wq);
+ ixgbe_wq = NULL;
+ }
}
#ifdef CONFIG_IXGBE_DCA
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
index fb8673d63806..db0731e05401 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
@@ -2393,6 +2393,9 @@ s32 ixgbe_set_copper_phy_power(struct ixgbe_hw *hw, bool on)
if (hw->mac.ops.get_media_type(hw) != ixgbe_media_type_copper)
return 0;
+ if (!on && ixgbe_mng_present(hw))
+ return 0;
+
status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_VENDOR_SPECIFIC_1_CONTROL,
IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
&reg);
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
index e5ba04025e2b..ef1504d41890 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
- Copyright(c) 1999 - 2013 Intel Corporation.
+ Copyright(c) 1999 - 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,
@@ -27,6 +27,7 @@
*******************************************************************************/
#include "ixgbe.h"
#include <linux/ptp_classify.h>
+#include <linux/clocksource.h>
/*
* The 82599 and the X540 do not have true 64bit nanosecond scale
@@ -93,7 +94,6 @@
#define IXGBE_INCVAL_SHIFT_82599 7
#define IXGBE_INCPER_SHIFT_82599 24
-#define IXGBE_MAX_TIMEADJ_VALUE 0x7FFFFFFFFFFFFFFFULL
#define IXGBE_OVERFLOW_PERIOD (HZ * 30)
#define IXGBE_PTP_TX_TIMEOUT (HZ * 15)
@@ -104,8 +104,68 @@
*/
#define IXGBE_PTP_PPS_HALF_SECOND 500000000ULL
+/* In contrast, the X550 controller has two registers, SYSTIMEH and SYSTIMEL
+ * which contain measurements of seconds and nanoseconds respectively. This
+ * matches the standard linux representation of time in the kernel. In addition,
+ * the X550 also has a SYSTIMER register which represents residue, or
+ * subnanosecond overflow adjustments. To control clock adjustment, the TIMINCA
+ * register is used, but it is unlike the X540 and 82599 devices. TIMINCA
+ * represents units of 2^-32 nanoseconds, and uses 31 bits for this, with the
+ * high bit representing whether the adjustent is positive or negative. Every
+ * clock cycle, the X550 will add 12.5 ns + TIMINCA which can result in a range
+ * of 12 to 13 nanoseconds adjustment. Unlike the 82599 and X540 devices, the
+ * X550's clock for purposes of SYSTIME generation is constant and not dependent
+ * on the link speed.
+ *
+ * SYSTIMEH SYSTIMEL SYSTIMER
+ * +--------------+ +--------------+ +-------------+
+ * X550 | 32 | | 32 | | 32 |
+ * *--------------+ +--------------+ +-------------+
+ * \____seconds___/ \_nanoseconds_/ \__2^-32 ns__/
+ *
+ * This results in a full 96 bits to represent the clock, with 32 bits for
+ * seconds, 32 bits for nanoseconds (largest value is 0d999999999 or just under
+ * 1 second) and an additional 32 bits to measure sub nanosecond adjustments for
+ * underflow of adjustments.
+ *
+ * The 32 bits of seconds for the X550 overflows every
+ * 2^32 / ( 365.25 * 24 * 60 * 60 ) = ~136 years.
+ *
+ * In order to adjust the clock frequency for the X550, the TIMINCA register is
+ * provided. This register represents a + or minus nearly 0.5 ns adjustment to
+ * the base frequency. It is measured in 2^-32 ns units, with the high bit being
+ * the sign bit. This register enables software to calculate frequency
+ * adjustments and apply them directly to the clock rate.
+ *
+ * The math for converting ppb into TIMINCA values is fairly straightforward.
+ * TIMINCA value = ( Base_Frequency * ppb ) / 1000000000ULL
+ *
+ * This assumes that ppb is never high enough to create a value bigger than
+ * TIMINCA's 31 bits can store. This is ensured by the stack. Calculating this
+ * value is also simple.
+ * Max ppb = ( Max Adjustment / Base Frequency ) / 1000000000ULL
+ *
+ * For the X550, the Max adjustment is +/- 0.5 ns, and the base frequency is
+ * 12.5 nanoseconds. This means that the Max ppb is 39999999
+ * Note: We subtract one in order to ensure no overflow, because the TIMINCA
+ * register can only hold slightly under 0.5 nanoseconds.
+ *
+ * Because TIMINCA is measured in 2^-32 ns units, we have to convert 12.5 ns
+ * into 2^-32 units, which is
+ *
+ * 12.5 * 2^32 = C80000000
+ *
+ * Some revisions of hardware have a faster base frequency than the registers
+ * were defined for. To fix this, we use a timecounter structure with the
+ * proper mult and shift to convert the cycles into nanoseconds of time.
+ */
+#define IXGBE_X550_BASE_PERIOD 0xC80000000ULL
+#define INCVALUE_MASK 0x7FFFFFFF
+#define ISGN 0x80000000
+#define MAX_TIMADJ 0x7FFFFFFF
+
/**
- * ixgbe_ptp_setup_sdp
+ * ixgbe_ptp_setup_sdp_x540
* @hw: the hardware private structure
*
* this function enables or disables the clock out feature on SDP0 for
@@ -116,83 +176,116 @@
* aligns the start of the PPS signal to that value. The shift is
* necessary because it can change based on the link speed.
*/
-static void ixgbe_ptp_setup_sdp(struct ixgbe_adapter *adapter)
+static void ixgbe_ptp_setup_sdp_x540(struct ixgbe_adapter *adapter)
{
struct ixgbe_hw *hw = &adapter->hw;
- int shift = adapter->cc.shift;
+ int shift = adapter->hw_cc.shift;
u32 esdp, tsauxc, clktiml, clktimh, trgttiml, trgttimh, rem;
u64 ns = 0, clock_edge = 0;
- if ((adapter->flags2 & IXGBE_FLAG2_PTP_PPS_ENABLED) &&
- (hw->mac.type == ixgbe_mac_X540)) {
+ /* disable the pin first */
+ IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, 0x0);
+ IXGBE_WRITE_FLUSH(hw);
- /* disable the pin first */
- IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, 0x0);
- IXGBE_WRITE_FLUSH(hw);
+ if (!(adapter->flags2 & IXGBE_FLAG2_PTP_PPS_ENABLED))
+ return;
- esdp = IXGBE_READ_REG(hw, IXGBE_ESDP);
+ esdp = IXGBE_READ_REG(hw, IXGBE_ESDP);
- /*
- * enable the SDP0 pin as output, and connected to the
- * native function for Timesync (ClockOut)
- */
- esdp |= (IXGBE_ESDP_SDP0_DIR |
- IXGBE_ESDP_SDP0_NATIVE);
+ /* enable the SDP0 pin as output, and connected to the
+ * native function for Timesync (ClockOut)
+ */
+ esdp |= IXGBE_ESDP_SDP0_DIR |
+ IXGBE_ESDP_SDP0_NATIVE;
- /*
- * enable the Clock Out feature on SDP0, and allow
- * interrupts to occur when the pin changes
- */
- tsauxc = (IXGBE_TSAUXC_EN_CLK |
- IXGBE_TSAUXC_SYNCLK |
- IXGBE_TSAUXC_SDP0_INT);
+ /* enable the Clock Out feature on SDP0, and allow
+ * interrupts to occur when the pin changes
+ */
+ tsauxc = IXGBE_TSAUXC_EN_CLK |
+ IXGBE_TSAUXC_SYNCLK |
+ IXGBE_TSAUXC_SDP0_INT;
- /* clock period (or pulse length) */
- clktiml = (u32)(IXGBE_PTP_PPS_HALF_SECOND << shift);
- clktimh = (u32)((IXGBE_PTP_PPS_HALF_SECOND << shift) >> 32);
+ /* clock period (or pulse length) */
+ clktiml = (u32)(IXGBE_PTP_PPS_HALF_SECOND << shift);
+ clktimh = (u32)((IXGBE_PTP_PPS_HALF_SECOND << shift) >> 32);
- /*
- * Account for the cyclecounter wrap-around value by
- * using the converted ns value of the current time to
- * check for when the next aligned second would occur.
- */
- clock_edge |= (u64)IXGBE_READ_REG(hw, IXGBE_SYSTIML);
- clock_edge |= (u64)IXGBE_READ_REG(hw, IXGBE_SYSTIMH) << 32;
- ns = timecounter_cyc2time(&adapter->tc, clock_edge);
+ /* Account for the cyclecounter wrap-around value by
+ * using the converted ns value of the current time to
+ * check for when the next aligned second would occur.
+ */
+ clock_edge |= (u64)IXGBE_READ_REG(hw, IXGBE_SYSTIML);
+ clock_edge |= (u64)IXGBE_READ_REG(hw, IXGBE_SYSTIMH) << 32;
+ ns = timecounter_cyc2time(&adapter->hw_tc, clock_edge);
- div_u64_rem(ns, IXGBE_PTP_PPS_HALF_SECOND, &rem);
- clock_edge += ((IXGBE_PTP_PPS_HALF_SECOND - (u64)rem) << shift);
+ div_u64_rem(ns, IXGBE_PTP_PPS_HALF_SECOND, &rem);
+ clock_edge += ((IXGBE_PTP_PPS_HALF_SECOND - (u64)rem) << shift);
- /* specify the initial clock start time */
- trgttiml = (u32)clock_edge;
- trgttimh = (u32)(clock_edge >> 32);
+ /* specify the initial clock start time */
+ trgttiml = (u32)clock_edge;
+ trgttimh = (u32)(clock_edge >> 32);
- IXGBE_WRITE_REG(hw, IXGBE_CLKTIML, clktiml);
- IXGBE_WRITE_REG(hw, IXGBE_CLKTIMH, clktimh);
- IXGBE_WRITE_REG(hw, IXGBE_TRGTTIML0, trgttiml);
- IXGBE_WRITE_REG(hw, IXGBE_TRGTTIMH0, trgttimh);
+ IXGBE_WRITE_REG(hw, IXGBE_CLKTIML, clktiml);
+ IXGBE_WRITE_REG(hw, IXGBE_CLKTIMH, clktimh);
+ IXGBE_WRITE_REG(hw, IXGBE_TRGTTIML0, trgttiml);
+ IXGBE_WRITE_REG(hw, IXGBE_TRGTTIMH0, trgttimh);
- IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp);
- IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc);
- } else {
- IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, 0x0);
- }
+ IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp);
+ IXGBE_WRITE_REG(hw, IXGBE_TSAUXC, tsauxc);
IXGBE_WRITE_FLUSH(hw);
}
/**
- * ixgbe_ptp_read - read raw cycle counter (to be used by time counter)
+ * ixgbe_ptp_read_X550 - read cycle counter value
+ * @hw_cc: cyclecounter structure
+ *
+ * This function reads SYSTIME registers. It is called by the cyclecounter
+ * structure to convert from internal representation into nanoseconds. We need
+ * this for X550 since some skews do not have expected clock frequency and
+ * result of SYSTIME is 32bits of "billions of cycles" and 32 bits of
+ * "cycles", rather than seconds and nanoseconds.
+ */
+static cycle_t ixgbe_ptp_read_X550(const struct cyclecounter *hw_cc)
+{
+ struct ixgbe_adapter *adapter =
+ container_of(hw_cc, struct ixgbe_adapter, hw_cc);
+ struct ixgbe_hw *hw = &adapter->hw;
+ struct timespec64 ts;
+
+ /* storage is 32 bits of 'billions of cycles' and 32 bits of 'cycles'.
+ * Some revisions of hardware run at a higher frequency and so the
+ * cycles are not guaranteed to be nanoseconds. The timespec64 created
+ * here is used for its math/conversions but does not necessarily
+ * represent nominal time.
+ *
+ * It should be noted that this cyclecounter will overflow at a
+ * non-bitmask field since we have to convert our billions of cycles
+ * into an actual cycles count. This results in some possible weird
+ * situations at high cycle counter stamps. However given that 32 bits
+ * of "seconds" is ~138 years this isn't a problem. Even at the
+ * increased frequency of some revisions, this is still ~103 years.
+ * Since the SYSTIME values start at 0 and we never write them, it is
+ * highly unlikely for the cyclecounter to overflow in practice.
+ */
+ IXGBE_READ_REG(hw, IXGBE_SYSTIMR);
+ ts.tv_nsec = IXGBE_READ_REG(hw, IXGBE_SYSTIML);
+ ts.tv_sec = IXGBE_READ_REG(hw, IXGBE_SYSTIMH);
+
+ return (u64)timespec64_to_ns(&ts);
+}
+
+/**
+ * ixgbe_ptp_read_82599 - read raw cycle counter (to be used by time counter)
* @cc: the cyclecounter structure
*
* this function reads the cyclecounter registers and is called by the
* cyclecounter structure used to construct a ns counter from the
* arbitrary fixed point registers
*/
-static cycle_t ixgbe_ptp_read(const struct cyclecounter *cc)
+static cycle_t ixgbe_ptp_read_82599(const struct cyclecounter *cc)
{
struct ixgbe_adapter *adapter =
- container_of(cc, struct ixgbe_adapter, cc);
+ container_of(cc, struct ixgbe_adapter, hw_cc);
struct ixgbe_hw *hw = &adapter->hw;
u64 stamp = 0;
@@ -203,20 +296,79 @@ static cycle_t ixgbe_ptp_read(const struct cyclecounter *cc)
}
/**
- * ixgbe_ptp_adjfreq
+ * ixgbe_ptp_convert_to_hwtstamp - convert register value to hw timestamp
+ * @adapter: private adapter structure
+ * @hwtstamp: stack timestamp structure
+ * @systim: unsigned 64bit system time value
+ *
+ * We need to convert the adapter's RX/TXSTMP registers into a hwtstamp value
+ * which can be used by the stack's ptp functions.
+ *
+ * The lock is used to protect consistency of the cyclecounter and the SYSTIME
+ * registers. However, it does not need to protect against the Rx or Tx
+ * timestamp registers, as there can't be a new timestamp until the old one is
+ * unlatched by reading.
+ *
+ * In addition to the timestamp in hardware, some controllers need a software
+ * overflow cyclecounter, and this function takes this into account as well.
+ **/
+static void ixgbe_ptp_convert_to_hwtstamp(struct ixgbe_adapter *adapter,
+ struct skb_shared_hwtstamps *hwtstamp,
+ u64 timestamp)
+{
+ unsigned long flags;
+ struct timespec64 systime;
+ u64 ns;
+
+ memset(hwtstamp, 0, sizeof(*hwtstamp));
+
+ switch (adapter->hw.mac.type) {
+ /* X550 and later hardware supposedly represent time using a seconds
+ * and nanoseconds counter, instead of raw 64bits nanoseconds. We need
+ * to convert the timestamp into cycles before it can be fed to the
+ * cyclecounter. We need an actual cyclecounter because some revisions
+ * of hardware run at a higher frequency and thus the counter does
+ * not represent seconds/nanoseconds. Instead it can be thought of as
+ * cycles and billions of cycles.
+ */
+ case ixgbe_mac_X550:
+ case ixgbe_mac_X550EM_x:
+ /* Upper 32 bits represent billions of cycles, lower 32 bits
+ * represent cycles. However, we use timespec64_to_ns for the
+ * correct math even though the units haven't been corrected
+ * yet.
+ */
+ systime.tv_sec = timestamp >> 32;
+ systime.tv_nsec = timestamp & 0xFFFFFFFF;
+
+ timestamp = timespec64_to_ns(&systime);
+ break;
+ default:
+ break;
+ }
+
+ spin_lock_irqsave(&adapter->tmreg_lock, flags);
+ ns = timecounter_cyc2time(&adapter->hw_tc, timestamp);
+ spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
+
+ hwtstamp->hwtstamp = ns_to_ktime(ns);
+}
+
+/**
+ * ixgbe_ptp_adjfreq_82599
* @ptp: the ptp clock structure
* @ppb: parts per billion adjustment from base
*
* adjust the frequency of the ptp cycle counter by the
* indicated ppb from the base frequency.
*/
-static int ixgbe_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
+static int ixgbe_ptp_adjfreq_82599(struct ptp_clock_info *ptp, s32 ppb)
{
struct ixgbe_adapter *adapter =
container_of(ptp, struct ixgbe_adapter, ptp_caps);
struct ixgbe_hw *hw = &adapter->hw;
- u64 freq;
- u32 diff, incval;
+ u64 freq, incval;
+ u32 diff;
int neg_adj = 0;
if (ppb < 0) {
@@ -235,12 +387,16 @@ static int ixgbe_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
switch (hw->mac.type) {
case ixgbe_mac_X540:
- IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, incval);
+ if (incval > 0xFFFFFFFFULL)
+ e_dev_warn("PTP ppb adjusted SYSTIME rate overflowed!\n");
+ IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, (u32)incval);
break;
case ixgbe_mac_82599EB:
+ if (incval > 0x00FFFFFFULL)
+ e_dev_warn("PTP ppb adjusted SYSTIME rate overflowed!\n");
IXGBE_WRITE_REG(hw, IXGBE_TIMINCA,
(1 << IXGBE_INCPER_SHIFT_82599) |
- incval);
+ ((u32)incval & 0x00FFFFFFUL));
break;
default:
break;
@@ -250,6 +406,43 @@ static int ixgbe_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
}
/**
+ * ixgbe_ptp_adjfreq_X550
+ * @ptp: the ptp clock structure
+ * @ppb: parts per billion adjustment from base
+ *
+ * adjust the frequency of the SYSTIME registers by the indicated ppb from base
+ * frequency
+ */
+static int ixgbe_ptp_adjfreq_X550(struct ptp_clock_info *ptp, s32 ppb)
+{
+ struct ixgbe_adapter *adapter =
+ container_of(ptp, struct ixgbe_adapter, ptp_caps);
+ struct ixgbe_hw *hw = &adapter->hw;
+ int neg_adj = 0;
+ u64 rate = IXGBE_X550_BASE_PERIOD;
+ u32 inca;
+
+ if (ppb < 0) {
+ neg_adj = 1;
+ ppb = -ppb;
+ }
+ rate *= ppb;
+ rate = div_u64(rate, 1000000000ULL);
+
+ /* warn if rate is too large */
+ if (rate >= INCVALUE_MASK)
+ e_dev_warn("PTP ppb adjusted SYSTIME rate overflowed!\n");
+
+ inca = rate & INCVALUE_MASK;
+ if (neg_adj)
+ inca |= ISGN;
+
+ IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, inca);
+
+ return 0;
+}
+
+/**
* ixgbe_ptp_adjtime
* @ptp: the ptp clock structure
* @delta: offset to adjust the cycle counter by
@@ -263,10 +456,11 @@ static int ixgbe_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
unsigned long flags;
spin_lock_irqsave(&adapter->tmreg_lock, flags);
- timecounter_adjtime(&adapter->tc, delta);
+ timecounter_adjtime(&adapter->hw_tc, delta);
spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
- ixgbe_ptp_setup_sdp(adapter);
+ if (adapter->ptp_setup_sdp)
+ adapter->ptp_setup_sdp(adapter);
return 0;
}
@@ -283,11 +477,11 @@ static int ixgbe_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
{
struct ixgbe_adapter *adapter =
container_of(ptp, struct ixgbe_adapter, ptp_caps);
- u64 ns;
unsigned long flags;
+ u64 ns;
spin_lock_irqsave(&adapter->tmreg_lock, flags);
- ns = timecounter_read(&adapter->tc);
+ ns = timecounter_read(&adapter->hw_tc);
spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
*ts = ns_to_timespec64(ns);
@@ -308,17 +502,16 @@ static int ixgbe_ptp_settime(struct ptp_clock_info *ptp,
{
struct ixgbe_adapter *adapter =
container_of(ptp, struct ixgbe_adapter, ptp_caps);
- u64 ns;
unsigned long flags;
-
- ns = timespec64_to_ns(ts);
+ u64 ns = timespec64_to_ns(ts);
/* reset the timecounter */
spin_lock_irqsave(&adapter->tmreg_lock, flags);
- timecounter_init(&adapter->tc, &adapter->cc, ns);
+ timecounter_init(&adapter->hw_tc, &adapter->hw_cc, ns);
spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
- ixgbe_ptp_setup_sdp(adapter);
+ if (adapter->ptp_setup_sdp)
+ adapter->ptp_setup_sdp(adapter);
return 0;
}
@@ -343,33 +536,26 @@ static int ixgbe_ptp_feature_enable(struct ptp_clock_info *ptp,
* event when the clock SDP triggers. Clear mask when PPS is
* disabled
*/
- if (rq->type == PTP_CLK_REQ_PPS) {
- switch (adapter->hw.mac.type) {
- case ixgbe_mac_X540:
- if (on)
- adapter->flags2 |= IXGBE_FLAG2_PTP_PPS_ENABLED;
- else
- adapter->flags2 &= ~IXGBE_FLAG2_PTP_PPS_ENABLED;
-
- ixgbe_ptp_setup_sdp(adapter);
- return 0;
- default:
- break;
- }
- }
+ if (rq->type != PTP_CLK_REQ_PPS || !adapter->ptp_setup_sdp)
+ return -ENOTSUPP;
+
+ if (on)
+ adapter->flags2 |= IXGBE_FLAG2_PTP_PPS_ENABLED;
+ else
+ adapter->flags2 &= ~IXGBE_FLAG2_PTP_PPS_ENABLED;
- return -ENOTSUPP;
+ adapter->ptp_setup_sdp(adapter);
+ return 0;
}
/**
* ixgbe_ptp_check_pps_event
* @adapter: the private adapter structure
- * @eicr: the interrupt cause register value
*
* This function is called by the interrupt routine when checking for
* interrupts. It will check and handle a pps event.
*/
-void ixgbe_ptp_check_pps_event(struct ixgbe_adapter *adapter, u32 eicr)
+void ixgbe_ptp_check_pps_event(struct ixgbe_adapter *adapter)
{
struct ixgbe_hw *hw = &adapter->hw;
struct ptp_clock_event event;
@@ -425,7 +611,9 @@ void ixgbe_ptp_rx_hang(struct ixgbe_adapter *adapter)
{
struct ixgbe_hw *hw = &adapter->hw;
u32 tsyncrxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCRXCTL);
+ struct ixgbe_ring *rx_ring;
unsigned long rx_event;
+ int n;
/* if we don't have a valid timestamp in the registers, just update the
* timeout counter and exit
@@ -437,19 +625,43 @@ void ixgbe_ptp_rx_hang(struct ixgbe_adapter *adapter)
/* determine the most recent watchdog or rx_timestamp event */
rx_event = adapter->last_rx_ptp_check;
- if (time_after(adapter->last_rx_timestamp, rx_event))
- rx_event = adapter->last_rx_timestamp;
+ for (n = 0; n < adapter->num_rx_queues; n++) {
+ rx_ring = adapter->rx_ring[n];
+ if (time_after(rx_ring->last_rx_timestamp, rx_event))
+ rx_event = rx_ring->last_rx_timestamp;
+ }
/* only need to read the high RXSTMP register to clear the lock */
- if (time_is_before_jiffies(rx_event + 5*HZ)) {
+ if (time_is_before_jiffies(rx_event + 5 * HZ)) {
IXGBE_READ_REG(hw, IXGBE_RXSTMPH);
adapter->last_rx_ptp_check = jiffies;
+ adapter->rx_hwtstamp_cleared++;
e_warn(drv, "clearing RX Timestamp hang\n");
}
}
/**
+ * ixgbe_ptp_clear_tx_timestamp - utility function to clear Tx timestamp state
+ * @adapter: the private adapter structure
+ *
+ * This function should be called whenever the state related to a Tx timestamp
+ * needs to be cleared. This helps ensure that all related bits are reset for
+ * the next Tx timestamp event.
+ */
+static void ixgbe_ptp_clear_tx_timestamp(struct ixgbe_adapter *adapter)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+
+ IXGBE_READ_REG(hw, IXGBE_TXSTMPH);
+ if (adapter->ptp_tx_skb) {
+ dev_kfree_skb_any(adapter->ptp_tx_skb);
+ adapter->ptp_tx_skb = NULL;
+ }
+ clear_bit_unlock(__IXGBE_PTP_TX_IN_PROGRESS, &adapter->state);
+}
+
+/**
* ixgbe_ptp_tx_hwtstamp - utility function which checks for TX time stamp
* @adapter: the private adapter struct
*
@@ -461,23 +673,15 @@ static void ixgbe_ptp_tx_hwtstamp(struct ixgbe_adapter *adapter)
{
struct ixgbe_hw *hw = &adapter->hw;
struct skb_shared_hwtstamps shhwtstamps;
- u64 regval = 0, ns;
- unsigned long flags;
+ u64 regval = 0;
regval |= (u64)IXGBE_READ_REG(hw, IXGBE_TXSTMPL);
regval |= (u64)IXGBE_READ_REG(hw, IXGBE_TXSTMPH) << 32;
- spin_lock_irqsave(&adapter->tmreg_lock, flags);
- ns = timecounter_cyc2time(&adapter->tc, regval);
- spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
-
- memset(&shhwtstamps, 0, sizeof(shhwtstamps));
- shhwtstamps.hwtstamp = ns_to_ktime(ns);
+ ixgbe_ptp_convert_to_hwtstamp(adapter, &shhwtstamps, regval);
skb_tstamp_tx(adapter->ptp_tx_skb, &shhwtstamps);
- dev_kfree_skb_any(adapter->ptp_tx_skb);
- adapter->ptp_tx_skb = NULL;
- clear_bit_unlock(__IXGBE_PTP_TX_IN_PROGRESS, &adapter->state);
+ ixgbe_ptp_clear_tx_timestamp(adapter);
}
/**
@@ -497,38 +701,85 @@ static void ixgbe_ptp_tx_hwtstamp_work(struct work_struct *work)
IXGBE_PTP_TX_TIMEOUT);
u32 tsynctxctl;
- if (timeout) {
- dev_kfree_skb_any(adapter->ptp_tx_skb);
- adapter->ptp_tx_skb = NULL;
- clear_bit_unlock(__IXGBE_PTP_TX_IN_PROGRESS, &adapter->state);
- e_warn(drv, "clearing Tx Timestamp hang\n");
+ /* we have to have a valid skb to poll for a timestamp */
+ if (!adapter->ptp_tx_skb) {
+ ixgbe_ptp_clear_tx_timestamp(adapter);
return;
}
+ /* stop polling once we have a valid timestamp */
tsynctxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCTXCTL);
- if (tsynctxctl & IXGBE_TSYNCTXCTL_VALID)
+ if (tsynctxctl & IXGBE_TSYNCTXCTL_VALID) {
ixgbe_ptp_tx_hwtstamp(adapter);
- else
+ return;
+ }
+
+ if (timeout) {
+ ixgbe_ptp_clear_tx_timestamp(adapter);
+ adapter->tx_hwtstamp_timeouts++;
+ e_warn(drv, "clearing Tx Timestamp hang\n");
+ } else {
/* reschedule to keep checking if it's not available yet */
schedule_work(&adapter->ptp_tx_work);
+ }
}
/**
- * ixgbe_ptp_rx_hwtstamp - utility function which checks for RX time stamp
- * @adapter: pointer to adapter struct
+ * ixgbe_ptp_rx_pktstamp - utility function to get RX time stamp from buffer
+ * @q_vector: structure containing interrupt and ring information
+ * @skb: the packet
+ *
+ * This function will be called by the Rx routine of the timestamp for this
+ * packet is stored in the buffer. The value is stored in little endian format
+ * starting at the end of the packet data.
+ */
+void ixgbe_ptp_rx_pktstamp(struct ixgbe_q_vector *q_vector,
+ struct sk_buff *skb)
+{
+ __le64 regval;
+
+ /* copy the bits out of the skb, and then trim the skb length */
+ skb_copy_bits(skb, skb->len - IXGBE_TS_HDR_LEN, &regval,
+ IXGBE_TS_HDR_LEN);
+ __pskb_trim(skb, skb->len - IXGBE_TS_HDR_LEN);
+
+ /* The timestamp is recorded in little endian format, and is stored at
+ * the end of the packet.
+ *
+ * DWORD: N N + 1 N + 2
+ * Field: End of Packet SYSTIMH SYSTIML
+ */
+ ixgbe_ptp_convert_to_hwtstamp(q_vector->adapter, skb_hwtstamps(skb),
+ le64_to_cpu(regval));
+}
+
+/**
+ * ixgbe_ptp_rx_rgtstamp - utility function which checks for RX time stamp
+ * @q_vector: structure containing interrupt and ring information
* @skb: particular skb to send timestamp with
*
* if the timestamp is valid, we convert it into the timecounter ns
* value, then store that result into the shhwtstamps structure which
* is passed up the network stack
*/
-void ixgbe_ptp_rx_hwtstamp(struct ixgbe_adapter *adapter, struct sk_buff *skb)
+void ixgbe_ptp_rx_rgtstamp(struct ixgbe_q_vector *q_vector,
+ struct sk_buff *skb)
{
- struct ixgbe_hw *hw = &adapter->hw;
- struct skb_shared_hwtstamps *shhwtstamps;
- u64 regval = 0, ns;
+ struct ixgbe_adapter *adapter;
+ struct ixgbe_hw *hw;
+ u64 regval = 0;
u32 tsyncrxctl;
- unsigned long flags;
+
+ /* we cannot process timestamps on a ring without a q_vector */
+ if (!q_vector || !q_vector->adapter)
+ return;
+
+ adapter = q_vector->adapter;
+ hw = &adapter->hw;
+
+ /* Read the tsyncrxctl register afterwards in order to prevent taking an
+ * I/O hit on every packet.
+ */
tsyncrxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCRXCTL);
if (!(tsyncrxctl & IXGBE_TSYNCRXCTL_VALID))
@@ -537,17 +788,7 @@ void ixgbe_ptp_rx_hwtstamp(struct ixgbe_adapter *adapter, struct sk_buff *skb)
regval |= (u64)IXGBE_READ_REG(hw, IXGBE_RXSTMPL);
regval |= (u64)IXGBE_READ_REG(hw, IXGBE_RXSTMPH) << 32;
- spin_lock_irqsave(&adapter->tmreg_lock, flags);
- ns = timecounter_cyc2time(&adapter->tc, regval);
- spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
-
- shhwtstamps = skb_hwtstamps(skb);
- shhwtstamps->hwtstamp = ns_to_ktime(ns);
-
- /* Update the last_rx_timestamp timer in order to enable watchdog check
- * for error case of latched timestamp on a dropped packet.
- */
- adapter->last_rx_timestamp = jiffies;
+ ixgbe_ptp_convert_to_hwtstamp(adapter, skb_hwtstamps(skb), regval);
}
int ixgbe_ptp_get_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
@@ -610,14 +851,20 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter,
case HWTSTAMP_FILTER_NONE:
tsync_rx_ctl = 0;
tsync_rx_mtrl = 0;
+ adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED |
+ IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER);
break;
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_L4_V1;
tsync_rx_mtrl |= IXGBE_RXMTRL_V1_SYNC_MSG;
+ adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED |
+ IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER);
break;
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_L4_V1;
tsync_rx_mtrl |= IXGBE_RXMTRL_V1_DELAY_REQ_MSG;
+ adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED |
+ IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER);
break;
case HWTSTAMP_FILTER_PTP_V2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
@@ -631,9 +878,21 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter,
tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_EVENT_V2;
is_l2 = true;
config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+ adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED |
+ IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER);
break;
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
case HWTSTAMP_FILTER_ALL:
+ /* The X550 controller is capable of timestamping all packets,
+ * which allows it to accept any filter.
+ */
+ if (hw->mac.type >= ixgbe_mac_X550) {
+ tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_ALL;
+ config->rx_filter = HWTSTAMP_FILTER_ALL;
+ adapter->flags |= IXGBE_FLAG_RX_HWTSTAMP_ENABLED;
+ break;
+ }
+ /* fall through */
default:
/*
* register RXMTRL must be set in order to do V1 packets,
@@ -641,16 +900,46 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter,
* Delay_Req messages and hardware does not support
* timestamping all packets => return error
*/
+ adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED |
+ IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER);
config->rx_filter = HWTSTAMP_FILTER_NONE;
return -ERANGE;
}
if (hw->mac.type == ixgbe_mac_82598EB) {
+ adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED |
+ IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER);
if (tsync_rx_ctl | tsync_tx_ctl)
return -ERANGE;
return 0;
}
+ /* Per-packet timestamping only works if the filter is set to all
+ * packets. Since this is desired, always timestamp all packets as long
+ * as any Rx filter was configured.
+ */
+ switch (hw->mac.type) {
+ case ixgbe_mac_X550:
+ case ixgbe_mac_X550EM_x:
+ /* enable timestamping all packets only if at least some
+ * packets were requested. Otherwise, play nice and disable
+ * timestamping
+ */
+ if (config->rx_filter == HWTSTAMP_FILTER_NONE)
+ break;
+
+ tsync_rx_ctl = IXGBE_TSYNCRXCTL_ENABLED |
+ IXGBE_TSYNCRXCTL_TYPE_ALL |
+ IXGBE_TSYNCRXCTL_TSIP_UT_EN;
+ config->rx_filter = HWTSTAMP_FILTER_ALL;
+ adapter->flags |= IXGBE_FLAG_RX_HWTSTAMP_ENABLED;
+ adapter->flags &= ~IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER;
+ is_l2 = true;
+ break;
+ default:
+ break;
+ }
+
/* define ethertype filter for timestamping L2 packets */
if (is_l2)
IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_1588),
@@ -678,8 +967,8 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter,
IXGBE_WRITE_FLUSH(hw);
/* clear TX/RX time stamp registers, just to be sure */
- regval = IXGBE_READ_REG(hw, IXGBE_TXSTMPH);
- regval = IXGBE_READ_REG(hw, IXGBE_RXSTMPH);
+ ixgbe_ptp_clear_tx_timestamp(adapter);
+ IXGBE_READ_REG(hw, IXGBE_RXSTMPH);
return 0;
}
@@ -712,23 +1001,9 @@ int ixgbe_ptp_set_ts_config(struct ixgbe_adapter *adapter, struct ifreq *ifr)
-EFAULT : 0;
}
-/**
- * ixgbe_ptp_start_cyclecounter - create the cycle counter from hw
- * @adapter: pointer to the adapter structure
- *
- * This function should be called to set the proper values for the TIMINCA
- * register and tell the cyclecounter structure what the tick rate of SYSTIME
- * is. It does not directly modify SYSTIME registers or the timecounter
- * structure. It should be called whenever a new TIMINCA value is necessary,
- * such as during initialization or when the link speed changes.
- */
-void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter)
+static void ixgbe_ptp_link_speed_adjust(struct ixgbe_adapter *adapter,
+ u32 *shift, u32 *incval)
{
- struct ixgbe_hw *hw = &adapter->hw;
- u32 incval = 0;
- u32 shift = 0;
- unsigned long flags;
-
/**
* Scale the NIC cycle counter by a large factor so that
* relatively small corrections to the frequency can be added
@@ -745,36 +1020,98 @@ void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter)
*/
switch (adapter->link_speed) {
case IXGBE_LINK_SPEED_100_FULL:
- incval = IXGBE_INCVAL_100;
- shift = IXGBE_INCVAL_SHIFT_100;
+ *shift = IXGBE_INCVAL_SHIFT_100;
+ *incval = IXGBE_INCVAL_100;
break;
case IXGBE_LINK_SPEED_1GB_FULL:
- incval = IXGBE_INCVAL_1GB;
- shift = IXGBE_INCVAL_SHIFT_1GB;
+ *shift = IXGBE_INCVAL_SHIFT_1GB;
+ *incval = IXGBE_INCVAL_1GB;
break;
case IXGBE_LINK_SPEED_10GB_FULL:
default:
- incval = IXGBE_INCVAL_10GB;
- shift = IXGBE_INCVAL_SHIFT_10GB;
+ *shift = IXGBE_INCVAL_SHIFT_10GB;
+ *incval = IXGBE_INCVAL_10GB;
break;
}
+}
- /**
- * Modify the calculated values to fit within the correct
- * number of bits specified by the hardware. The 82599 doesn't
- * have the same space as the X540, so bitshift the calculated
- * values to fit.
+/**
+ * ixgbe_ptp_start_cyclecounter - create the cycle counter from hw
+ * @adapter: pointer to the adapter structure
+ *
+ * This function should be called to set the proper values for the TIMINCA
+ * register and tell the cyclecounter structure what the tick rate of SYSTIME
+ * is. It does not directly modify SYSTIME registers or the timecounter
+ * structure. It should be called whenever a new TIMINCA value is necessary,
+ * such as during initialization or when the link speed changes.
+ */
+void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ struct cyclecounter cc;
+ unsigned long flags;
+ u32 incval = 0;
+ u32 tsauxc = 0;
+ u32 fuse0 = 0;
+
+ /* For some of the boards below this mask is technically incorrect.
+ * The timestamp mask overflows at approximately 61bits. However the
+ * particular hardware does not overflow on an even bitmask value.
+ * Instead, it overflows due to conversion of upper 32bits billions of
+ * cycles. Timecounters are not really intended for this purpose so
+ * they do not properly function if the overflow point isn't 2^N-1.
+ * However, the actual SYSTIME values in question take ~138 years to
+ * overflow. In practice this means they won't actually overflow. A
+ * proper fix to this problem would require modification of the
+ * timecounter delta calculations.
*/
+ cc.mask = CLOCKSOURCE_MASK(64);
+ cc.mult = 1;
+ cc.shift = 0;
+
switch (hw->mac.type) {
+ case ixgbe_mac_X550EM_x:
+ /* SYSTIME assumes X550EM_x board frequency is 300Mhz, and is
+ * designed to represent seconds and nanoseconds when this is
+ * the case. However, some revisions of hardware have a 400Mhz
+ * clock and we have to compensate for this frequency
+ * variation using corrected mult and shift values.
+ */
+ fuse0 = IXGBE_READ_REG(hw, IXGBE_FUSES0_GROUP(0));
+ if (!(fuse0 & IXGBE_FUSES0_300MHZ)) {
+ cc.mult = 3;
+ cc.shift = 2;
+ }
+ /* fallthrough */
+ case ixgbe_mac_X550:
+ cc.read = ixgbe_ptp_read_X550;
+
+ /* enable SYSTIME counter */
+ IXGBE_WRITE_REG(hw, IXGBE_SYSTIMR, 0);
+ IXGBE_WRITE_REG(hw, IXGBE_SYSTIML, 0);
+ IXGBE_WRITE_REG(hw, IXGBE_SYSTIMH, 0);
+ tsauxc = IXGBE_READ_REG(hw, IXGBE_TSAUXC);
+ IXGBE_WRITE_REG(hw, IXGBE_TSAUXC,
+ tsauxc & ~IXGBE_TSAUXC_DISABLE_SYSTIME);
+ IXGBE_WRITE_REG(hw, IXGBE_TSIM, IXGBE_TSIM_TXTS);
+ IXGBE_WRITE_REG(hw, IXGBE_EIMS, IXGBE_EIMS_TIMESYNC);
+
+ IXGBE_WRITE_FLUSH(hw);
+ break;
case ixgbe_mac_X540:
+ cc.read = ixgbe_ptp_read_82599;
+
+ ixgbe_ptp_link_speed_adjust(adapter, &cc.shift, &incval);
IXGBE_WRITE_REG(hw, IXGBE_TIMINCA, incval);
break;
case ixgbe_mac_82599EB:
+ cc.read = ixgbe_ptp_read_82599;
+
+ ixgbe_ptp_link_speed_adjust(adapter, &cc.shift, &incval);
incval >>= IXGBE_INCVAL_SHIFT_82599;
- shift -= IXGBE_INCVAL_SHIFT_82599;
+ cc.shift -= IXGBE_INCVAL_SHIFT_82599;
IXGBE_WRITE_REG(hw, IXGBE_TIMINCA,
- (1 << IXGBE_INCPER_SHIFT_82599) |
- incval);
+ (1 << IXGBE_INCPER_SHIFT_82599) | incval);
break;
default:
/* other devices aren't supported */
@@ -787,13 +1124,7 @@ void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter)
/* need lock to prevent incorrect read while modifying cyclecounter */
spin_lock_irqsave(&adapter->tmreg_lock, flags);
-
- memset(&adapter->cc, 0, sizeof(adapter->cc));
- adapter->cc.read = ixgbe_ptp_read;
- adapter->cc.mask = CYCLECOUNTER_MASK(64);
- adapter->cc.shift = shift;
- adapter->cc.mult = 1;
-
+ memcpy(&adapter->hw_cc, &cc, sizeof(adapter->hw_cc));
spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
}
@@ -814,29 +1145,27 @@ void ixgbe_ptp_reset(struct ixgbe_adapter *adapter)
struct ixgbe_hw *hw = &adapter->hw;
unsigned long flags;
- /* set SYSTIME registers to 0 just in case */
- IXGBE_WRITE_REG(hw, IXGBE_SYSTIML, 0x00000000);
- IXGBE_WRITE_REG(hw, IXGBE_SYSTIMH, 0x00000000);
- IXGBE_WRITE_FLUSH(hw);
-
/* reset the hardware timestamping mode */
ixgbe_ptp_set_timestamp_mode(adapter, &adapter->tstamp_config);
+ /* 82598 does not support PTP */
+ if (hw->mac.type == ixgbe_mac_82598EB)
+ return;
+
ixgbe_ptp_start_cyclecounter(adapter);
spin_lock_irqsave(&adapter->tmreg_lock, flags);
-
- /* reset the ns time counter */
- timecounter_init(&adapter->tc, &adapter->cc,
+ timecounter_init(&adapter->hw_tc, &adapter->hw_cc,
ktime_to_ns(ktime_get_real()));
-
spin_unlock_irqrestore(&adapter->tmreg_lock, flags);
- /*
- * Now that the shift has been calculated and the systime
+ adapter->last_overflow_check = jiffies;
+
+ /* Now that the shift has been calculated and the systime
* registers reset, (re-)enable the Clock out feature
*/
- ixgbe_ptp_setup_sdp(adapter);
+ if (adapter->ptp_setup_sdp)
+ adapter->ptp_setup_sdp(adapter);
}
/**
@@ -845,11 +1174,11 @@ void ixgbe_ptp_reset(struct ixgbe_adapter *adapter)
*
* This function performs setup of the user entry point function table and
* initializes the PTP clock device, which is used to access the clock-like
- * features of the PTP core. It will be called by ixgbe_ptp_init, only if
- * there isn't already a clock device (such as after a suspend/resume cycle,
- * where the clock device wasn't destroyed).
+ * features of the PTP core. It will be called by ixgbe_ptp_init, and may
+ * reuse a previously initialized clock (such as during a suspend/resume
+ * cycle).
*/
-static int ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter)
+static long ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
long err;
@@ -869,11 +1198,12 @@ static int ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter)
adapter->ptp_caps.n_ext_ts = 0;
adapter->ptp_caps.n_per_out = 0;
adapter->ptp_caps.pps = 1;
- adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq;
+ adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq_82599;
adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime;
adapter->ptp_caps.gettime64 = ixgbe_ptp_gettime;
adapter->ptp_caps.settime64 = ixgbe_ptp_settime;
adapter->ptp_caps.enable = ixgbe_ptp_feature_enable;
+ adapter->ptp_setup_sdp = ixgbe_ptp_setup_sdp_x540;
break;
case ixgbe_mac_82599EB:
snprintf(adapter->ptp_caps.name,
@@ -885,14 +1215,31 @@ static int ixgbe_ptp_create_clock(struct ixgbe_adapter *adapter)
adapter->ptp_caps.n_ext_ts = 0;
adapter->ptp_caps.n_per_out = 0;
adapter->ptp_caps.pps = 0;
- adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq;
+ adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq_82599;
+ adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime;
+ adapter->ptp_caps.gettime64 = ixgbe_ptp_gettime;
+ adapter->ptp_caps.settime64 = ixgbe_ptp_settime;
+ adapter->ptp_caps.enable = ixgbe_ptp_feature_enable;
+ break;
+ case ixgbe_mac_X550:
+ case ixgbe_mac_X550EM_x:
+ snprintf(adapter->ptp_caps.name, 16, "%s", netdev->name);
+ adapter->ptp_caps.owner = THIS_MODULE;
+ adapter->ptp_caps.max_adj = 30000000;
+ adapter->ptp_caps.n_alarm = 0;
+ adapter->ptp_caps.n_ext_ts = 0;
+ adapter->ptp_caps.n_per_out = 0;
+ adapter->ptp_caps.pps = 0;
+ adapter->ptp_caps.adjfreq = ixgbe_ptp_adjfreq_X550;
adapter->ptp_caps.adjtime = ixgbe_ptp_adjtime;
adapter->ptp_caps.gettime64 = ixgbe_ptp_gettime;
adapter->ptp_caps.settime64 = ixgbe_ptp_settime;
adapter->ptp_caps.enable = ixgbe_ptp_feature_enable;
+ adapter->ptp_setup_sdp = NULL;
break;
default:
adapter->ptp_clock = NULL;
+ adapter->ptp_setup_sdp = NULL;
return -EOPNOTSUPP;
}
@@ -961,18 +1308,13 @@ void ixgbe_ptp_suspend(struct ixgbe_adapter *adapter)
if (!test_and_clear_bit(__IXGBE_PTP_RUNNING, &adapter->state))
return;
- /* since this might be called in suspend, we don't clear the state,
- * but simply reset the auxiliary PPS signal control register
- */
- IXGBE_WRITE_REG(&adapter->hw, IXGBE_TSAUXC, 0x0);
+ adapter->flags2 &= ~IXGBE_FLAG2_PTP_PPS_ENABLED;
+ if (adapter->ptp_setup_sdp)
+ adapter->ptp_setup_sdp(adapter);
/* ensure that we cancel any pending PTP Tx work item in progress */
cancel_work_sync(&adapter->ptp_tx_work);
- if (adapter->ptp_tx_skb) {
- dev_kfree_skb_any(adapter->ptp_tx_skb);
- adapter->ptp_tx_skb = NULL;
- clear_bit_unlock(__IXGBE_PTP_TX_IN_PROGRESS, &adapter->state);
- }
+ ixgbe_ptp_clear_tx_timestamp(adapter);
}
/**
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
index fcd8b27a0ccb..8025a3f93598 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel 10 Gigabit PCI Express Linux driver
- Copyright(c) 1999 - 2014 Intel Corporation.
+ Copyright(c) 1999 - 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,
@@ -130,6 +130,38 @@ static int __ixgbe_enable_sriov(struct ixgbe_adapter *adapter)
return -ENOMEM;
}
+/**
+ * ixgbe_get_vfs - Find and take references to all vf devices
+ * @adapter: Pointer to adapter struct
+ */
+static void ixgbe_get_vfs(struct ixgbe_adapter *adapter)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ u16 vendor = pdev->vendor;
+ struct pci_dev *vfdev;
+ int vf = 0;
+ u16 vf_id;
+ int pos;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
+ if (!pos)
+ return;
+ pci_read_config_word(pdev, pos + PCI_SRIOV_VF_DID, &vf_id);
+
+ vfdev = pci_get_device(vendor, vf_id, NULL);
+ for (; vfdev; vfdev = pci_get_device(vendor, vf_id, vfdev)) {
+ if (!vfdev->is_virtfn)
+ continue;
+ if (vfdev->physfn != pdev)
+ continue;
+ if (vf >= adapter->num_vfs)
+ continue;
+ pci_dev_get(vfdev);
+ adapter->vfinfo[vf].vfdev = vfdev;
+ ++vf;
+ }
+}
+
/* Note this function is called when the user wants to enable SR-IOV
* VFs using the now deprecated module parameter
*/
@@ -170,8 +202,10 @@ void ixgbe_enable_sriov(struct ixgbe_adapter *adapter)
}
}
- if (!__ixgbe_enable_sriov(adapter))
+ if (!__ixgbe_enable_sriov(adapter)) {
+ ixgbe_get_vfs(adapter);
return;
+ }
/* If we have gotten to this point then there is no memory available
* to manage the VF devices - print message and bail.
@@ -184,6 +218,7 @@ void ixgbe_enable_sriov(struct ixgbe_adapter *adapter)
#endif /* #ifdef CONFIG_PCI_IOV */
int ixgbe_disable_sriov(struct ixgbe_adapter *adapter)
{
+ unsigned int num_vfs = adapter->num_vfs, vf;
struct ixgbe_hw *hw = &adapter->hw;
u32 gpie;
u32 vmdctl;
@@ -192,6 +227,16 @@ int ixgbe_disable_sriov(struct ixgbe_adapter *adapter)
/* set num VFs to 0 to prevent access to vfinfo */
adapter->num_vfs = 0;
+ /* put the reference to all of the vf devices */
+ for (vf = 0; vf < num_vfs; ++vf) {
+ struct pci_dev *vfdev = adapter->vfinfo[vf].vfdev;
+
+ if (!vfdev)
+ continue;
+ adapter->vfinfo[vf].vfdev = NULL;
+ pci_dev_put(vfdev);
+ }
+
/* free VF control structures */
kfree(adapter->vfinfo);
adapter->vfinfo = NULL;
@@ -289,6 +334,7 @@ static int ixgbe_pci_sriov_enable(struct pci_dev *dev, int num_vfs)
e_dev_warn("Failed to enable PCI sriov: %d\n", err);
return err;
}
+ ixgbe_get_vfs(adapter);
ixgbe_sriov_reinit(adapter);
return num_vfs;
@@ -406,11 +452,34 @@ void ixgbe_restore_vf_multicasts(struct ixgbe_adapter *adapter)
static int ixgbe_set_vf_vlan(struct ixgbe_adapter *adapter, int add, int vid,
u32 vf)
{
- /* VLAN 0 is a special case, don't allow it to be removed */
- if (!vid && !add)
- return 0;
+ struct ixgbe_hw *hw = &adapter->hw;
+ int err;
+
+ /* If VLAN overlaps with one the PF is currently monitoring make
+ * sure that we are able to allocate a VLVF entry. This may be
+ * redundant but it guarantees PF will maintain visibility to
+ * the VLAN.
+ */
+ if (add && test_bit(vid, adapter->active_vlans)) {
+ err = hw->mac.ops.set_vfta(hw, vid, VMDQ_P(0), true, false);
+ if (err)
+ return err;
+ }
+
+ err = hw->mac.ops.set_vfta(hw, vid, vf, !!add, false);
- return adapter->hw.mac.ops.set_vfta(&adapter->hw, vid, vf, (bool)add);
+ if (add && !err)
+ return err;
+
+ /* If we failed to add the VF VLAN or we are removing the VF VLAN
+ * we may need to drop the PF pool bit in order to allow us to free
+ * up the VLVF resources.
+ */
+ if (test_bit(vid, adapter->active_vlans) ||
+ (adapter->flags2 & IXGBE_FLAG2_VLAN_PROMISC))
+ ixgbe_update_pf_promisc_vlvf(adapter, vid);
+
+ return err;
}
static s32 ixgbe_set_vf_lpe(struct ixgbe_adapter *adapter, u32 *msgbuf, u32 vf)
@@ -516,13 +585,75 @@ static void ixgbe_clear_vmvir(struct ixgbe_adapter *adapter, u32 vf)
IXGBE_WRITE_REG(hw, IXGBE_VMVIR(vf), 0);
}
+
+static void ixgbe_clear_vf_vlans(struct ixgbe_adapter *adapter, u32 vf)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ u32 i;
+
+ /* post increment loop, covers VLVF_ENTRIES - 1 to 0 */
+ for (i = IXGBE_VLVF_ENTRIES; i--;) {
+ u32 bits[2], vlvfb, vid, vfta, vlvf;
+ u32 word = i * 2 + vf / 32;
+ u32 mask = 1 << (vf % 32);
+
+ vlvfb = IXGBE_READ_REG(hw, IXGBE_VLVFB(word));
+
+ /* if our bit isn't set we can skip it */
+ if (!(vlvfb & mask))
+ continue;
+
+ /* clear our bit from vlvfb */
+ vlvfb ^= mask;
+
+ /* create 64b mask to chedk to see if we should clear VLVF */
+ bits[word % 2] = vlvfb;
+ bits[~word % 2] = IXGBE_READ_REG(hw, IXGBE_VLVFB(word ^ 1));
+
+ /* if promisc is enabled, PF will be present, leave VFTA */
+ if (adapter->flags2 & IXGBE_FLAG2_VLAN_PROMISC) {
+ bits[VMDQ_P(0) / 32] &= ~(1 << (VMDQ_P(0) % 32));
+
+ if (bits[0] || bits[1])
+ goto update_vlvfb;
+ goto update_vlvf;
+ }
+
+ /* if other pools are present, just remove ourselves */
+ if (bits[0] || bits[1])
+ goto update_vlvfb;
+
+ /* if we cannot determine VLAN just remove ourselves */
+ vlvf = IXGBE_READ_REG(hw, IXGBE_VLVF(i));
+ if (!vlvf)
+ goto update_vlvfb;
+
+ vid = vlvf & VLAN_VID_MASK;
+ mask = 1 << (vid % 32);
+
+ /* clear bit from VFTA */
+ vfta = IXGBE_READ_REG(hw, IXGBE_VFTA(vid / 32));
+ if (vfta & mask)
+ IXGBE_WRITE_REG(hw, IXGBE_VFTA(vid / 32), vfta ^ mask);
+update_vlvf:
+ /* clear POOL selection enable */
+ IXGBE_WRITE_REG(hw, IXGBE_VLVF(i), 0);
+update_vlvfb:
+ /* clear pool bits */
+ IXGBE_WRITE_REG(hw, IXGBE_VLVFB(word), vlvfb);
+ }
+}
+
static inline void ixgbe_vf_reset_event(struct ixgbe_adapter *adapter, u32 vf)
{
struct ixgbe_hw *hw = &adapter->hw;
struct vf_data_storage *vfinfo = &adapter->vfinfo[vf];
u8 num_tcs = netdev_get_num_tc(adapter->netdev);
- /* add PF assigned VLAN or VLAN 0 */
+ /* remove VLAN filters beloning to this VF */
+ ixgbe_clear_vf_vlans(adapter, vf);
+
+ /* add back PF assigned VLAN or VLAN 0 */
ixgbe_set_vf_vlan(adapter, true, vfinfo->pf_vlan, vf);
/* reset offloads to defaults */
@@ -768,40 +899,14 @@ static int ixgbe_set_vf_mac_addr(struct ixgbe_adapter *adapter,
return ixgbe_set_vf_mac(adapter, vf, new_mac) < 0;
}
-static int ixgbe_find_vlvf_entry(struct ixgbe_hw *hw, u32 vlan)
-{
- u32 vlvf;
- s32 regindex;
-
- /* short cut the special case */
- if (vlan == 0)
- return 0;
-
- /* Search for the vlan id in the VLVF entries */
- for (regindex = 1; regindex < IXGBE_VLVF_ENTRIES; regindex++) {
- vlvf = IXGBE_READ_REG(hw, IXGBE_VLVF(regindex));
- if ((vlvf & VLAN_VID_MASK) == vlan)
- break;
- }
-
- /* Return a negative value if not found */
- if (regindex >= IXGBE_VLVF_ENTRIES)
- regindex = -1;
-
- return regindex;
-}
-
static int ixgbe_set_vf_vlan_msg(struct ixgbe_adapter *adapter,
u32 *msgbuf, u32 vf)
{
+ u32 add = (msgbuf[0] & IXGBE_VT_MSGINFO_MASK) >> IXGBE_VT_MSGINFO_SHIFT;
+ u32 vid = (msgbuf[1] & IXGBE_VLVF_VLANID_MASK);
+ u8 tcs = netdev_get_num_tc(adapter->netdev);
struct ixgbe_hw *hw = &adapter->hw;
- int add = (msgbuf[0] & IXGBE_VT_MSGINFO_MASK) >> IXGBE_VT_MSGINFO_SHIFT;
- int vid = (msgbuf[1] & IXGBE_VLVF_VLANID_MASK);
int err;
- s32 reg_ndx;
- u32 vlvf;
- u32 bits;
- u8 tcs = netdev_get_num_tc(adapter->netdev);
if (adapter->vfinfo[vf].pf_vlan || tcs) {
e_warn(drv,
@@ -811,54 +916,23 @@ static int ixgbe_set_vf_vlan_msg(struct ixgbe_adapter *adapter,
return -1;
}
- if (add)
- adapter->vfinfo[vf].vlan_count++;
- else if (adapter->vfinfo[vf].vlan_count)
- adapter->vfinfo[vf].vlan_count--;
-
- /* in case of promiscuous mode any VLAN filter set for a VF must
- * also have the PF pool added to it.
- */
- if (add && adapter->netdev->flags & IFF_PROMISC)
- err = ixgbe_set_vf_vlan(adapter, add, vid, VMDQ_P(0));
+ /* VLAN 0 is a special case, don't allow it to be removed */
+ if (!vid && !add)
+ return 0;
err = ixgbe_set_vf_vlan(adapter, add, vid, vf);
- if (!err && adapter->vfinfo[vf].spoofchk_enabled)
- hw->mac.ops.set_vlan_anti_spoofing(hw, true, vf);
+ if (err)
+ return err;
- /* Go through all the checks to see if the VLAN filter should
- * be wiped completely.
- */
- if (!add && adapter->netdev->flags & IFF_PROMISC) {
- reg_ndx = ixgbe_find_vlvf_entry(hw, vid);
- if (reg_ndx < 0)
- return err;
- vlvf = IXGBE_READ_REG(hw, IXGBE_VLVF(reg_ndx));
- /* See if any other pools are set for this VLAN filter
- * entry other than the PF.
- */
- if (VMDQ_P(0) < 32) {
- bits = IXGBE_READ_REG(hw, IXGBE_VLVFB(reg_ndx * 2));
- bits &= ~(1 << VMDQ_P(0));
- bits |= IXGBE_READ_REG(hw,
- IXGBE_VLVFB(reg_ndx * 2) + 1);
- } else {
- bits = IXGBE_READ_REG(hw,
- IXGBE_VLVFB(reg_ndx * 2) + 1);
- bits &= ~(1 << (VMDQ_P(0) - 32));
- bits |= IXGBE_READ_REG(hw, IXGBE_VLVFB(reg_ndx * 2));
- }
+ if (adapter->vfinfo[vf].spoofchk_enabled)
+ hw->mac.ops.set_vlan_anti_spoofing(hw, true, vf);
- /* If the filter was removed then ensure PF pool bit
- * is cleared if the PF only added itself to the pool
- * because the PF is in promiscuous mode.
- */
- if ((vlvf & VLAN_VID_MASK) == vid &&
- !test_bit(vid, adapter->active_vlans) && !bits)
- ixgbe_set_vf_vlan(adapter, add, vid, VMDQ_P(0));
- }
+ if (add)
+ adapter->vfinfo[vf].vlan_count++;
+ else if (adapter->vfinfo[vf].vlan_count)
+ adapter->vfinfo[vf].vlan_count--;
- return err;
+ return 0;
}
static int ixgbe_set_vf_macvlan_msg(struct ixgbe_adapter *adapter,
@@ -1239,6 +1313,9 @@ static int ixgbe_enable_port_vlan(struct ixgbe_adapter *adapter, int vf,
if (err)
goto out;
+ /* Revoke tagless access via VLAN 0 */
+ ixgbe_set_vf_vlan(adapter, false, 0, vf);
+
ixgbe_set_vmvir(adapter, vlan, qos, vf);
ixgbe_set_vmolr(hw, vf, false);
if (adapter->vfinfo[vf].spoofchk_enabled)
@@ -1272,6 +1349,8 @@ static int ixgbe_disable_port_vlan(struct ixgbe_adapter *adapter, int vf)
err = ixgbe_set_vf_vlan(adapter, false,
adapter->vfinfo[vf].pf_vlan, vf);
+ /* Restore tagless access via VLAN 0 */
+ ixgbe_set_vf_vlan(adapter, true, 0, vf);
ixgbe_clear_vmvir(adapter, vf);
ixgbe_set_vmolr(hw, vf, true);
hw->mac.ops.set_vlan_anti_spoofing(hw, false, vf);
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
index 995f03107eac..bf7367a08716 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h
@@ -1020,6 +1020,7 @@ struct ixgbe_thermal_sensor_data {
#define IXGBE_TXSTMPH 0x08C08 /* Tx timestamp value High - RO */
#define IXGBE_SYSTIML 0x08C0C /* System time register Low - RO */
#define IXGBE_SYSTIMH 0x08C10 /* System time register High - RO */
+#define IXGBE_SYSTIMR 0x08C58 /* System time register Residue - RO */
#define IXGBE_TIMINCA 0x08C14 /* Increment attributes register - RW */
#define IXGBE_TIMADJL 0x08C18 /* Time Adjustment Offset register Low - RW */
#define IXGBE_TIMADJH 0x08C1C /* Time Adjustment Offset register High - RW */
@@ -1036,6 +1037,7 @@ struct ixgbe_thermal_sensor_data {
#define IXGBE_AUXSTMPH0 0x08C40 /* Auxiliary Time Stamp 0 register High - RO */
#define IXGBE_AUXSTMPL1 0x08C44 /* Auxiliary Time Stamp 1 register Low - RO */
#define IXGBE_AUXSTMPH1 0x08C48 /* Auxiliary Time Stamp 1 register High - RO */
+#define IXGBE_TSIM 0x08C68 /* TimeSync Interrupt Mask Register - RW */
/* Diagnostic Registers */
#define IXGBE_RDSTATCTL 0x02C20
@@ -1345,7 +1347,10 @@ struct ixgbe_thermal_sensor_data {
#define IXGBE_MDIO_GLOBAL_INT_CHIP_VEN_MASK 0xFF01 /* int chip-wide mask */
#define IXGBE_MDIO_GLOBAL_INT_CHIP_VEN_FLAG 0xFC01 /* int chip-wide mask */
#define IXGBE_MDIO_GLOBAL_ALARM_1 0xCC00 /* Global alarm 1 */
+#define IXGBE_MDIO_GLOBAL_ALM_1_DEV_FAULT 0x0010 /* device fault */
#define IXGBE_MDIO_GLOBAL_ALM_1_HI_TMP_FAIL 0x4000 /* high temp failure */
+#define IXGBE_MDIO_GLOBAL_FAULT_MSG 0xC850 /* global fault msg */
+#define IXGBE_MDIO_GLOBAL_FAULT_MSG_HI_TMP 0x8007 /* high temp failure */
#define IXGBE_MDIO_GLOBAL_INT_MASK 0xD400 /* Global int mask */
/* autoneg vendor alarm int enable */
#define IXGBE_MDIO_GLOBAL_AN_VEN_ALM_INT_EN 0x1000
@@ -1353,6 +1358,7 @@ struct ixgbe_thermal_sensor_data {
#define IXGBE_MDIO_GLOBAL_VEN_ALM_INT_EN 0x1 /* vendor alarm int enable */
#define IXGBE_MDIO_GLOBAL_STD_ALM2_INT 0x200 /* vendor alarm2 int mask */
#define IXGBE_MDIO_GLOBAL_INT_HI_TEMP_EN 0x4000 /* int high temp enable */
+#define IXGBE_MDIO_GLOBAL_INT_DEV_FAULT_EN 0x0010 /*int dev fault enable */
#define IXGBE_MDIO_PMA_PMD_SDA_SCL_ADDR 0xC30A /* PHY_XS SDA/SCL Addr Reg */
#define IXGBE_MDIO_PMA_PMD_SDA_SCL_DATA 0xC30B /* PHY_XS SDA/SCL Data Reg */
@@ -2209,6 +2215,7 @@ enum {
#define IXGBE_TSAUXC_EN_CLK 0x00000004
#define IXGBE_TSAUXC_SYNCLK 0x00000008
#define IXGBE_TSAUXC_SDP0_INT 0x00000040
+#define IXGBE_TSAUXC_DISABLE_SYSTIME 0x80000000
#define IXGBE_TSYNCTXCTL_VALID 0x00000001 /* Tx timestamp valid */
#define IXGBE_TSYNCTXCTL_ENABLED 0x00000010 /* Tx timestamping enabled */
@@ -2218,8 +2225,12 @@ enum {
#define IXGBE_TSYNCRXCTL_TYPE_L2_V2 0x00
#define IXGBE_TSYNCRXCTL_TYPE_L4_V1 0x02
#define IXGBE_TSYNCRXCTL_TYPE_L2_L4_V2 0x04
+#define IXGBE_TSYNCRXCTL_TYPE_ALL 0x08
#define IXGBE_TSYNCRXCTL_TYPE_EVENT_V2 0x0A
#define IXGBE_TSYNCRXCTL_ENABLED 0x00000010 /* Rx Timestamping enabled */
+#define IXGBE_TSYNCRXCTL_TSIP_UT_EN 0x00800000 /* Rx Timestamp in Packet */
+
+#define IXGBE_TSIM_TXTS 0x00000002
#define IXGBE_RXMTRL_V1_CTRLT_MASK 0x000000FF
#define IXGBE_RXMTRL_V1_SYNC_MSG 0x00
@@ -2332,6 +2343,7 @@ enum {
#define IXGBE_RXD_STAT_UDPV 0x400 /* Valid UDP checksum */
#define IXGBE_RXD_STAT_DYNINT 0x800 /* Pkt caused INT via DYNINT */
#define IXGBE_RXD_STAT_LLINT 0x800 /* Pkt caused Low Latency Interrupt */
+#define IXGBE_RXD_STAT_TSIP 0x08000 /* Time Stamp in packet buffer */
#define IXGBE_RXD_STAT_TS 0x10000 /* Time Stamp */
#define IXGBE_RXD_STAT_SECP 0x20000 /* Security Processing */
#define IXGBE_RXD_STAT_LB 0x40000 /* Loopback Status */
@@ -2768,6 +2780,7 @@ struct ixgbe_adv_tx_context_desc {
#define IXGBE_ADVTXD_TUCMD_L4T_UDP 0x00000000 /* L4 Packet TYPE of UDP */
#define IXGBE_ADVTXD_TUCMD_L4T_TCP 0x00000800 /* L4 Packet TYPE of TCP */
#define IXGBE_ADVTXD_TUCMD_L4T_SCTP 0x00001000 /* L4 Packet TYPE of SCTP */
+#define IXGBE_ADVTXD_TUCMD_L4T_RSV 0x00001800 /* RSV L4 Packet TYPE */
#define IXGBE_ADVTXD_TUCMD_MKRREQ 0x00002000 /*Req requires Markers and CRC*/
#define IXGBE_ADVTXD_POPTS_IPSEC 0x00000400 /* IPSec offload request */
#define IXGBE_ADVTXD_TUCMD_IPSEC_TYPE_ESP 0x00002000 /* IPSec Type ESP */
@@ -3288,7 +3301,7 @@ struct ixgbe_mac_operations {
s32 (*enable_mc)(struct ixgbe_hw *);
s32 (*disable_mc)(struct ixgbe_hw *);
s32 (*clear_vfta)(struct ixgbe_hw *);
- s32 (*set_vfta)(struct ixgbe_hw *, u32, u32, bool);
+ s32 (*set_vfta)(struct ixgbe_hw *, u32, u32, bool, bool);
s32 (*init_uta_tables)(struct ixgbe_hw *);
void (*set_mac_anti_spoofing)(struct ixgbe_hw *, bool, int);
void (*set_vlan_anti_spoofing)(struct ixgbe_hw *, bool, int);
@@ -3508,7 +3521,7 @@ struct ixgbe_info {
#define IXGBE_FUSES0_GROUP(_i) (0x11158 + ((_i) * 4))
#define IXGBE_FUSES0_300MHZ BIT(5)
-#define IXGBE_FUSES0_REV1 BIT(6)
+#define IXGBE_FUSES0_REV_MASK (3 << 6)
#define IXGBE_KRM_PORT_CAR_GEN_CTRL(P) ((P) ? 0x8010 : 0x4010)
#define IXGBE_KRM_LINK_CTRL_1(P) ((P) ? 0x820C : 0x420C)
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c
index c1d4584f6469..2358c1b7d586 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c
@@ -57,8 +57,7 @@ s32 ixgbe_get_invariants_X540(struct ixgbe_hw *hw)
struct ixgbe_phy_info *phy = &hw->phy;
/* set_phy_power was set by default to NULL */
- if (!ixgbe_mng_present(hw))
- phy->ops.set_phy_power = ixgbe_set_copper_phy_power;
+ phy->ops.set_phy_power = ixgbe_set_copper_phy_power;
mac->mcft_size = IXGBE_X540_MC_TBL_SIZE;
mac->vft_size = IXGBE_X540_VFT_TBL_SIZE;
@@ -110,13 +109,14 @@ mac_reset_top:
ctrl |= IXGBE_READ_REG(hw, IXGBE_CTRL);
IXGBE_WRITE_REG(hw, IXGBE_CTRL, ctrl);
IXGBE_WRITE_FLUSH(hw);
+ usleep_range(1000, 1200);
/* Poll for reset bit to self-clear indicating reset is complete */
for (i = 0; i < 10; i++) {
- udelay(1);
ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL);
if (!(ctrl & IXGBE_CTRL_RST_MASK))
break;
+ udelay(1);
}
if (ctrl & IXGBE_CTRL_RST_MASK) {
@@ -154,12 +154,16 @@ mac_reset_top:
/* Add the SAN MAC address to the RAR only if it's a valid address */
if (is_valid_ether_addr(hw->mac.san_addr)) {
- hw->mac.ops.set_rar(hw, hw->mac.num_rar_entries - 1,
- hw->mac.san_addr, 0, IXGBE_RAH_AV);
-
/* Save the SAN MAC RAR index */
hw->mac.san_mac_rar_index = hw->mac.num_rar_entries - 1;
+ hw->mac.ops.set_rar(hw, hw->mac.san_mac_rar_index,
+ hw->mac.san_addr, 0, IXGBE_RAH_AV);
+
+ /* clear VMDq pool/queue selection for this RAR */
+ hw->mac.ops.clear_vmdq(hw, hw->mac.san_mac_rar_index,
+ IXGBE_CLEAR_VMDQ_ALL);
+
/* Reserve the last RAR for the SAN MAC address */
hw->mac.num_rar_entries--;
}
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c
index ebe0ac950b14..87aca3f7c3de 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c
@@ -26,6 +26,8 @@
#include "ixgbe_common.h"
#include "ixgbe_phy.h"
+static s32 ixgbe_setup_kr_speed_x550em(struct ixgbe_hw *, ixgbe_link_speed);
+
static s32 ixgbe_get_invariants_X550_x(struct ixgbe_hw *hw)
{
struct ixgbe_mac_info *mac = &hw->mac;
@@ -85,79 +87,6 @@ static s32 ixgbe_write_cs4227(struct ixgbe_hw *hw, u16 reg, u16 value)
}
/**
- * ixgbe_check_cs4227_reg - Perform diag on a CS4227 register
- * @hw: pointer to hardware structure
- * @reg: the register to check
- *
- * Performs a diagnostic on a register in the CS4227 chip. Returns an error
- * if it is not operating correctly.
- * This function assumes that the caller has acquired the proper semaphore.
- */
-static s32 ixgbe_check_cs4227_reg(struct ixgbe_hw *hw, u16 reg)
-{
- s32 status;
- u32 retry;
- u16 reg_val;
-
- reg_val = (IXGBE_CS4227_EDC_MODE_DIAG << 1) | 1;
- status = ixgbe_write_cs4227(hw, reg, reg_val);
- if (status)
- return status;
- for (retry = 0; retry < IXGBE_CS4227_RETRIES; retry++) {
- msleep(IXGBE_CS4227_CHECK_DELAY);
- reg_val = 0xFFFF;
- ixgbe_read_cs4227(hw, reg, &reg_val);
- if (!reg_val)
- break;
- }
- if (reg_val) {
- hw_err(hw, "CS4227 reg 0x%04X failed diagnostic\n", reg);
- return status;
- }
-
- return 0;
-}
-
-/**
- * ixgbe_get_cs4227_status - Return CS4227 status
- * @hw: pointer to hardware structure
- *
- * Performs a diagnostic on the CS4227 chip. Returns an error if it is
- * not operating correctly.
- * This function assumes that the caller has acquired the proper semaphore.
- */
-static s32 ixgbe_get_cs4227_status(struct ixgbe_hw *hw)
-{
- s32 status;
- u16 value = 0;
-
- /* Exit if the diagnostic has already been performed. */
- status = ixgbe_read_cs4227(hw, IXGBE_CS4227_SCRATCH, &value);
- if (status)
- return status;
- if (value == IXGBE_CS4227_RESET_COMPLETE)
- return 0;
-
- /* Check port 0. */
- status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_LINE_SPARE24_LSB);
- if (status)
- return status;
-
- status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_HOST_SPARE24_LSB);
- if (status)
- return status;
-
- /* Check port 1. */
- status = ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_LINE_SPARE24_LSB +
- (1 << 12));
- if (status)
- return status;
-
- return ixgbe_check_cs4227_reg(hw, IXGBE_CS4227_HOST_SPARE24_LSB +
- (1 << 12));
-}
-
-/**
* ixgbe_read_pe - Read register from port expander
* @hw: pointer to hardware structure
* @reg: register number to read
@@ -326,13 +255,6 @@ static void ixgbe_check_cs4227(struct ixgbe_hw *hw)
return;
}
- /* Is the CS4227 working correctly? */
- status = ixgbe_get_cs4227_status(hw);
- if (status) {
- hw_err(hw, "CS4227 status failed: %d", status);
- goto out;
- }
-
/* Record completion for next time. */
status = ixgbe_write_cs4227(hw, IXGBE_CS4227_SCRATCH,
IXGBE_CS4227_RESET_COMPLETE);
@@ -1257,31 +1179,71 @@ ixgbe_setup_mac_link_sfp_x550em(struct ixgbe_hw *hw,
if (status)
return status;
- /* Configure CS4227 LINE side to 10G SR. */
- slice = IXGBE_CS4227_LINE_SPARE22_MSB + (hw->bus.lan_id << 12);
- value = IXGBE_CS4227_SPEED_10G;
- status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice,
- value);
-
- /* Configure CS4227 for HOST connection rate then type. */
- slice = IXGBE_CS4227_HOST_SPARE22_MSB + (hw->bus.lan_id << 12);
- value = speed & IXGBE_LINK_SPEED_10GB_FULL ?
- IXGBE_CS4227_SPEED_10G : IXGBE_CS4227_SPEED_1G;
- status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice,
- value);
+ if (!(hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE)) {
+ /* Configure CS4227 LINE side to 10G SR. */
+ slice = IXGBE_CS4227_LINE_SPARE22_MSB + (hw->bus.lan_id << 12);
+ value = IXGBE_CS4227_SPEED_10G;
+ status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227,
+ slice, value);
+ if (status)
+ goto i2c_err;
- slice = IXGBE_CS4227_HOST_SPARE24_LSB + (hw->bus.lan_id << 12);
- if (setup_linear)
- value = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 1;
- else
+ slice = IXGBE_CS4227_LINE_SPARE24_LSB + (hw->bus.lan_id << 12);
value = (IXGBE_CS4227_EDC_MODE_SR << 1) | 1;
- status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227, slice,
- value);
+ status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227,
+ slice, value);
+ if (status)
+ goto i2c_err;
+
+ /* Configure CS4227 for HOST connection rate then type. */
+ slice = IXGBE_CS4227_HOST_SPARE22_MSB + (hw->bus.lan_id << 12);
+ value = speed & IXGBE_LINK_SPEED_10GB_FULL ?
+ IXGBE_CS4227_SPEED_10G : IXGBE_CS4227_SPEED_1G;
+ status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227,
+ slice, value);
+ if (status)
+ goto i2c_err;
- /* If internal link mode is XFI, then setup XFI internal link. */
- if (!(hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE))
+ slice = IXGBE_CS4227_HOST_SPARE24_LSB + (hw->bus.lan_id << 12);
+ if (setup_linear)
+ value = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 1;
+ else
+ value = (IXGBE_CS4227_EDC_MODE_SR << 1) | 1;
+ status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227,
+ slice, value);
+ if (status)
+ goto i2c_err;
+
+ /* Setup XFI internal link. */
status = ixgbe_setup_ixfi_x550em(hw, &speed);
+ if (status) {
+ hw_dbg(hw, "setup_ixfi failed with %d\n", status);
+ return status;
+ }
+ } else {
+ /* Configure internal PHY for KR/KX. */
+ status = ixgbe_setup_kr_speed_x550em(hw, speed);
+ if (status) {
+ hw_dbg(hw, "setup_kr_speed failed with %d\n", status);
+ return status;
+ }
+ /* Configure CS4227 LINE side to proper mode. */
+ slice = IXGBE_CS4227_LINE_SPARE24_LSB + (hw->bus.lan_id << 12);
+ if (setup_linear)
+ value = (IXGBE_CS4227_EDC_MODE_CX1 << 1) | 1;
+ else
+ value = (IXGBE_CS4227_EDC_MODE_SR << 1) | 1;
+ status = ixgbe_write_i2c_combined_generic(hw, IXGBE_CS4227,
+ slice, value);
+ if (status)
+ goto i2c_err;
+ }
+
+ return 0;
+
+i2c_err:
+ hw_dbg(hw, "combined i2c access failed with %d\n", status);
return status;
}
@@ -1482,7 +1444,7 @@ static s32 ixgbe_get_lasi_ext_t_x550em(struct ixgbe_hw *hw, bool *lsc)
IXGBE_MDIO_GLOBAL_ALARM_1_INT)))
return status;
- /* High temperature failure alarm triggered */
+ /* Global alarm triggered */
status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_ALARM_1,
IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
&reg);
@@ -1496,6 +1458,21 @@ static s32 ixgbe_get_lasi_ext_t_x550em(struct ixgbe_hw *hw, bool *lsc)
ixgbe_set_copper_phy_power(hw, false);
return IXGBE_ERR_OVERTEMP;
}
+ if (reg & IXGBE_MDIO_GLOBAL_ALM_1_DEV_FAULT) {
+ /* device fault alarm triggered */
+ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_FAULT_MSG,
+ IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
+ &reg);
+ if (status)
+ return status;
+
+ /* if device fault was due to high temp alarm handle and exit */
+ if (reg == IXGBE_MDIO_GLOBAL_FAULT_MSG_HI_TMP) {
+ /* power down the PHY in case the PHY FW didn't */
+ ixgbe_set_copper_phy_power(hw, false);
+ return IXGBE_ERR_OVERTEMP;
+ }
+ }
/* Vendor alarm 2 triggered */
status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_CHIP_STD_INT_FLAG,
@@ -1549,14 +1526,15 @@ static s32 ixgbe_enable_lasi_ext_t_x550em(struct ixgbe_hw *hw)
if (status)
return status;
- /* Enables high temperature failure alarm */
+ /* Enable high temperature failure and global fault alarms */
status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_INT_MASK,
IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
&reg);
if (status)
return status;
- reg |= IXGBE_MDIO_GLOBAL_INT_HI_TEMP_EN;
+ reg |= (IXGBE_MDIO_GLOBAL_INT_HI_TEMP_EN |
+ IXGBE_MDIO_GLOBAL_INT_DEV_FAULT_EN);
status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_GLOBAL_INT_MASK,
IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
@@ -1765,6 +1743,12 @@ static s32 ixgbe_setup_internal_phy_t_x550em(struct ixgbe_hw *hw)
if (hw->mac.ops.get_media_type(hw) != ixgbe_media_type_copper)
return IXGBE_ERR_CONFIG;
+ if (hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE) {
+ speed = IXGBE_LINK_SPEED_10GB_FULL |
+ IXGBE_LINK_SPEED_1GB_FULL;
+ return ixgbe_setup_kr_speed_x550em(hw, speed);
+ }
+
/* If link is not up, then there is no setup necessary so return */
status = ixgbe_ext_phy_t_x550em_get_link(hw, &link_up);
if (status)
@@ -1873,10 +1857,6 @@ static s32 ixgbe_enter_lplu_t_x550em(struct ixgbe_hw *hw)
u32 save_autoneg;
bool link_up;
- /* SW LPLU not required on later HW revisions. */
- if (IXGBE_FUSES0_REV1 & IXGBE_READ_REG(hw, IXGBE_FUSES0_GROUP(0)))
- return 0;
-
/* If blocked by MNG FW, then don't restart AN */
if (ixgbe_check_reset_blocked(hw))
return 0;
@@ -1969,7 +1949,6 @@ static s32 ixgbe_enter_lplu_t_x550em(struct ixgbe_hw *hw)
static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw)
{
struct ixgbe_phy_info *phy = &hw->phy;
- ixgbe_link_speed speed;
s32 ret_val;
hw->mac.ops.set_lan_id(hw);
@@ -1982,13 +1961,6 @@ static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw)
* to determine internal PHY mode.
*/
phy->nw_mng_if_sel = IXGBE_READ_REG(hw, IXGBE_NW_MNG_IF_SEL);
-
- /* If internal PHY mode is KR, then initialize KR link */
- if (phy->nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE) {
- speed = IXGBE_LINK_SPEED_10GB_FULL |
- IXGBE_LINK_SPEED_1GB_FULL;
- ret_val = ixgbe_setup_kr_speed_x550em(hw, speed);
- }
}
/* Identify the PHY or SFP module */
@@ -2020,18 +1992,13 @@ static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw)
/* If internal link mode is XFI, then setup iXFI internal link,
* else setup KR now.
*/
- if (!(phy->nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE)) {
- phy->ops.setup_internal_link =
- ixgbe_setup_internal_phy_t_x550em;
- } else {
- speed = IXGBE_LINK_SPEED_10GB_FULL |
- IXGBE_LINK_SPEED_1GB_FULL;
- ret_val = ixgbe_setup_kr_speed_x550em(hw, speed);
- }
+ phy->ops.setup_internal_link =
+ ixgbe_setup_internal_phy_t_x550em;
/* setup SW LPLU only for first revision */
- if (!(IXGBE_FUSES0_REV1 & IXGBE_READ_REG(hw,
- IXGBE_FUSES0_GROUP(0))))
+ if (hw->mac.type == ixgbe_mac_X550EM_x &&
+ !(IXGBE_READ_REG(hw, IXGBE_FUSES0_GROUP(0)) &
+ IXGBE_FUSES0_REV_MASK))
phy->ops.enter_lplu = ixgbe_enter_lplu_t_x550em;
phy->ops.handle_lasi = ixgbe_handle_lasi_ext_t_x550em;
@@ -2176,13 +2143,14 @@ mac_reset_top:
ctrl |= IXGBE_READ_REG(hw, IXGBE_CTRL);
IXGBE_WRITE_REG(hw, IXGBE_CTRL, ctrl);
IXGBE_WRITE_FLUSH(hw);
+ usleep_range(1000, 1200);
/* Poll for reset bit to self-clear meaning reset is complete */
for (i = 0; i < 10; i++) {
- udelay(1);
ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL);
if (!(ctrl & IXGBE_CTRL_RST_MASK))
break;
+ udelay(1);
}
if (ctrl & IXGBE_CTRL_RST_MASK) {
diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c
index d3e5f5b37999..c48aef613b0a 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c
@@ -774,7 +774,7 @@ static int ixgbevf_set_coalesce(struct net_device *netdev,
adapter->tx_itr_setting = ec->tx_coalesce_usecs;
if (adapter->tx_itr_setting == 1)
- tx_itr_param = IXGBE_10K_ITR;
+ tx_itr_param = IXGBE_12K_ITR;
else
tx_itr_param = adapter->tx_itr_setting;
diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
index ec3147279621..68ec7daa04fd 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
+++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
@@ -326,8 +326,7 @@ static inline bool ixgbevf_qv_disable(struct ixgbevf_q_vector *q_vector)
#define IXGBE_MIN_RSC_ITR 24
#define IXGBE_100K_ITR 40
#define IXGBE_20K_ITR 200
-#define IXGBE_10K_ITR 400
-#define IXGBE_8K_ITR 500
+#define IXGBE_12K_ITR 336
/* Helper macros to switch between ints/sec and what the register uses.
* And yes, it's the same math going both ways. The lowest value
diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
index 592ff237d692..3558f019b631 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
@@ -59,7 +59,7 @@ static const char ixgbevf_driver_string[] =
#define DRV_VERSION "2.12.1-k"
const char ixgbevf_driver_version[] = DRV_VERSION;
static char ixgbevf_copyright[] =
- "Copyright (c) 2009 - 2012 Intel Corporation.";
+ "Copyright (c) 2009 - 2015 Intel Corporation.";
static const struct ixgbevf_info *ixgbevf_info_tbl[] = {
[board_82599_vf] = &ixgbevf_82599_vf_info,
@@ -96,12 +96,14 @@ static int debug = -1;
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
+static struct workqueue_struct *ixgbevf_wq;
+
static void ixgbevf_service_event_schedule(struct ixgbevf_adapter *adapter)
{
if (!test_bit(__IXGBEVF_DOWN, &adapter->state) &&
!test_bit(__IXGBEVF_REMOVING, &adapter->state) &&
!test_and_set_bit(__IXGBEVF_SERVICE_SCHED, &adapter->state))
- schedule_work(&adapter->service_task);
+ queue_work(ixgbevf_wq, &adapter->service_task);
}
static void ixgbevf_service_event_complete(struct ixgbevf_adapter *adapter)
@@ -1014,6 +1016,8 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget)
ixgbevf_for_each_ring(ring, q_vector->tx)
clean_complete &= ixgbevf_clean_tx_irq(q_vector, ring);
+ if (budget <= 0)
+ return budget;
#ifdef CONFIG_NET_RX_BUSY_POLL
if (!ixgbevf_qv_lock_napi(q_vector))
return budget;
@@ -1043,7 +1047,7 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget)
return budget;
/* all work done, exit the polling mode */
napi_complete_done(napi, work_done);
- if (adapter->rx_itr_setting & 1)
+ if (adapter->rx_itr_setting == 1)
ixgbevf_set_itr(q_vector);
if (!test_bit(__IXGBEVF_DOWN, &adapter->state) &&
!test_bit(__IXGBEVF_REMOVING, &adapter->state))
@@ -1138,7 +1142,7 @@ static void ixgbevf_configure_msix(struct ixgbevf_adapter *adapter)
if (q_vector->tx.ring && !q_vector->rx.ring) {
/* Tx only vector */
if (adapter->tx_itr_setting == 1)
- q_vector->itr = IXGBE_10K_ITR;
+ q_vector->itr = IXGBE_12K_ITR;
else
q_vector->itr = adapter->tx_itr_setting;
} else {
@@ -1196,7 +1200,7 @@ static void ixgbevf_update_itr(struct ixgbevf_q_vector *q_vector,
/* simple throttle rate management
* 0-20MB/s lowest (100000 ints/s)
* 20-100MB/s low (20000 ints/s)
- * 100-1249MB/s bulk (8000 ints/s)
+ * 100-1249MB/s bulk (12000 ints/s)
*/
/* what was last interrupt timeslice? */
timepassed_us = q_vector->itr >> 2;
@@ -1246,8 +1250,9 @@ static void ixgbevf_set_itr(struct ixgbevf_q_vector *q_vector)
new_itr = IXGBE_20K_ITR;
break;
case bulk_latency:
+ new_itr = IXGBE_12K_ITR;
+ break;
default:
- new_itr = IXGBE_8K_ITR;
break;
}
@@ -1288,7 +1293,7 @@ static irqreturn_t ixgbevf_msix_clean_rings(int irq, void *data)
/* EIAM disabled interrupts (on this vector) for us */
if (q_vector->rx.ring || q_vector->tx.ring)
- napi_schedule(&q_vector->napi);
+ napi_schedule_irqoff(&q_vector->napi);
return IRQ_HANDLED;
}
@@ -1332,7 +1337,6 @@ static int ixgbevf_map_rings_to_vectors(struct ixgbevf_adapter *adapter)
int txr_remaining = adapter->num_tx_queues;
int i, j;
int rqpv, tqpv;
- int err = 0;
q_vectors = adapter->num_msix_vectors - NON_Q_VECTORS;
@@ -1345,7 +1349,7 @@ static int ixgbevf_map_rings_to_vectors(struct ixgbevf_adapter *adapter)
for (; txr_idx < txr_remaining; v_start++, txr_idx++)
map_vector_to_txq(adapter, v_start, txr_idx);
- goto out;
+ return 0;
}
/* If we don't have enough vectors for a 1-to-1
@@ -1370,8 +1374,7 @@ static int ixgbevf_map_rings_to_vectors(struct ixgbevf_adapter *adapter)
}
}
-out:
- return err;
+ return 0;
}
/**
@@ -1469,9 +1472,7 @@ static inline void ixgbevf_reset_q_vectors(struct ixgbevf_adapter *adapter)
**/
static int ixgbevf_request_irq(struct ixgbevf_adapter *adapter)
{
- int err = 0;
-
- err = ixgbevf_request_msix_irqs(adapter);
+ int err = ixgbevf_request_msix_irqs(adapter);
if (err)
hw_dbg(&adapter->hw, "request_irq failed, Error %d\n", err);
@@ -1830,7 +1831,7 @@ static int ixgbevf_vlan_rx_kill_vid(struct net_device *netdev,
{
struct ixgbevf_adapter *adapter = netdev_priv(netdev);
struct ixgbe_hw *hw = &adapter->hw;
- int err = -EOPNOTSUPP;
+ int err;
spin_lock_bh(&adapter->mbx_lock);
@@ -2046,7 +2047,7 @@ static void ixgbevf_negotiate_api(struct ixgbevf_adapter *adapter)
ixgbe_mbox_api_11,
ixgbe_mbox_api_10,
ixgbe_mbox_api_unknown };
- int err = 0, idx = 0;
+ int err, idx = 0;
spin_lock_bh(&adapter->mbx_lock);
@@ -2260,10 +2261,8 @@ void ixgbevf_reset(struct ixgbevf_adapter *adapter)
}
if (is_valid_ether_addr(adapter->hw.mac.addr)) {
- memcpy(netdev->dev_addr, adapter->hw.mac.addr,
- netdev->addr_len);
- memcpy(netdev->perm_addr, adapter->hw.mac.addr,
- netdev->addr_len);
+ ether_addr_copy(netdev->dev_addr, adapter->hw.mac.addr);
+ ether_addr_copy(netdev->perm_addr, adapter->hw.mac.addr);
}
adapter->last_reset = jiffies;
@@ -2421,7 +2420,7 @@ err_allocation:
static int ixgbevf_set_interrupt_capability(struct ixgbevf_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
- int err = 0;
+ int err;
int vector, v_budget;
/* It's easy to be greedy for MSI-X vectors, but it really
@@ -2439,26 +2438,21 @@ static int ixgbevf_set_interrupt_capability(struct ixgbevf_adapter *adapter)
*/
adapter->msix_entries = kcalloc(v_budget,
sizeof(struct msix_entry), GFP_KERNEL);
- if (!adapter->msix_entries) {
- err = -ENOMEM;
- goto out;
- }
+ if (!adapter->msix_entries)
+ return -ENOMEM;
for (vector = 0; vector < v_budget; vector++)
adapter->msix_entries[vector].entry = vector;
err = ixgbevf_acquire_msix_vectors(adapter, v_budget);
if (err)
- goto out;
+ return err;
err = netif_set_real_num_tx_queues(netdev, adapter->num_tx_queues);
if (err)
- goto out;
-
- err = netif_set_real_num_rx_queues(netdev, adapter->num_rx_queues);
+ return err;
-out:
- return err;
+ return netif_set_real_num_rx_queues(netdev, adapter->num_rx_queues);
}
/**
@@ -2483,9 +2477,6 @@ static int ixgbevf_alloc_q_vectors(struct ixgbevf_adapter *adapter)
q_vector->v_idx = q_idx;
netif_napi_add(adapter->netdev, &q_vector->napi,
ixgbevf_poll, 64);
-#ifdef CONFIG_NET_RX_BUSY_POLL
- napi_hash_add(&q_vector->napi);
-#endif
adapter->q_vector[q_idx] = q_vector;
}
@@ -2662,13 +2653,14 @@ static int ixgbevf_sw_init(struct ixgbevf_adapter *adapter)
else if (is_zero_ether_addr(adapter->hw.mac.addr))
dev_info(&pdev->dev,
"MAC address not assigned by administrator.\n");
- memcpy(netdev->dev_addr, hw->mac.addr, netdev->addr_len);
+ ether_addr_copy(netdev->dev_addr, hw->mac.addr);
}
if (!is_valid_ether_addr(netdev->dev_addr)) {
dev_info(&pdev->dev, "Assigning random MAC address\n");
eth_hw_addr_random(netdev);
- memcpy(hw->mac.addr, netdev->dev_addr, netdev->addr_len);
+ ether_addr_copy(hw->mac.addr, netdev->dev_addr);
+ ether_addr_copy(hw->mac.perm_addr, netdev->dev_addr);
}
/* Enable dynamic interrupt throttling rates */
@@ -3355,6 +3347,7 @@ static void ixgbevf_tx_csum(struct ixgbevf_ring *tx_ring,
if (skb->ip_summed == CHECKSUM_PARTIAL) {
u8 l4_hdr = 0;
+ __be16 frag_off;
switch (first->protocol) {
case htons(ETH_P_IP):
@@ -3365,13 +3358,16 @@ static void ixgbevf_tx_csum(struct ixgbevf_ring *tx_ring,
case htons(ETH_P_IPV6):
vlan_macip_lens |= skb_network_header_len(skb);
l4_hdr = ipv6_hdr(skb)->nexthdr;
+ if (likely(skb_network_header_len(skb) ==
+ sizeof(struct ipv6hdr)))
+ break;
+ ipv6_skip_exthdr(skb, skb_network_offset(skb) +
+ sizeof(struct ipv6hdr),
+ &l4_hdr, &frag_off);
+ if (unlikely(frag_off))
+ l4_hdr = NEXTHDR_FRAGMENT;
break;
default:
- if (unlikely(net_ratelimit())) {
- dev_warn(tx_ring->dev,
- "partial checksum but proto=%x!\n",
- first->protocol);
- }
break;
}
@@ -3393,16 +3389,18 @@ static void ixgbevf_tx_csum(struct ixgbevf_ring *tx_ring,
default:
if (unlikely(net_ratelimit())) {
dev_warn(tx_ring->dev,
- "partial checksum but l4 proto=%x!\n",
- l4_hdr);
+ "partial checksum, l3 proto=%x, l4 proto=%x\n",
+ first->protocol, l4_hdr);
}
- break;
+ skb_checksum_help(skb);
+ goto no_csum;
}
/* update TX checksum flag */
first->tx_flags |= IXGBE_TX_FLAGS_CSUM;
}
+no_csum:
/* vlan_macip_lens: MACLEN, VLAN tag */
vlan_macip_lens |= skb_network_offset(skb) << IXGBE_ADVTXD_MACLEN_SHIFT;
vlan_macip_lens |= first->tx_flags & IXGBE_TX_FLAGS_VLAN_MASK;
@@ -3698,8 +3696,8 @@ static int ixgbevf_set_mac(struct net_device *netdev, void *p)
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
- memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
- memcpy(hw->mac.addr, addr->sa_data, netdev->addr_len);
+ ether_addr_copy(netdev->dev_addr, addr->sa_data);
+ ether_addr_copy(hw->mac.addr, addr->sa_data);
spin_lock_bh(&adapter->mbx_lock);
@@ -4248,15 +4246,17 @@ static struct pci_driver ixgbevf_driver = {
**/
static int __init ixgbevf_init_module(void)
{
- int ret;
-
pr_info("%s - version %s\n", ixgbevf_driver_string,
ixgbevf_driver_version);
pr_info("%s\n", ixgbevf_copyright);
+ ixgbevf_wq = create_singlethread_workqueue(ixgbevf_driver_name);
+ if (!ixgbevf_wq) {
+ pr_err("%s: Failed to create workqueue\n", ixgbevf_driver_name);
+ return -ENOMEM;
+ }
- ret = pci_register_driver(&ixgbevf_driver);
- return ret;
+ return pci_register_driver(&ixgbevf_driver);
}
module_init(ixgbevf_init_module);
@@ -4270,6 +4270,10 @@ module_init(ixgbevf_init_module);
static void __exit ixgbevf_exit_module(void)
{
pci_unregister_driver(&ixgbevf_driver);
+ if (ixgbevf_wq) {
+ destroy_workqueue(ixgbevf_wq);
+ ixgbevf_wq = NULL;
+ }
}
#ifdef DEBUG
diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.c b/drivers/net/ethernet/intel/ixgbevf/vf.c
index 427f3605cbfc..61a98f4c5746 100644
--- a/drivers/net/ethernet/intel/ixgbevf/vf.c
+++ b/drivers/net/ethernet/intel/ixgbevf/vf.c
@@ -117,7 +117,9 @@ static s32 ixgbevf_reset_hw_vf(struct ixgbe_hw *hw)
msgbuf[0] != (IXGBE_VF_RESET | IXGBE_VT_MSGTYPE_NACK))
return IXGBE_ERR_INVALID_MAC_ADDR;
- ether_addr_copy(hw->mac.perm_addr, addr);
+ if (msgbuf[0] == (IXGBE_VF_RESET | IXGBE_VT_MSGTYPE_ACK))
+ ether_addr_copy(hw->mac.perm_addr, addr);
+
hw->mac.mc_filter_type = msgbuf[IXGBE_VF_MC_TYPE_WORD];
return 0;
diff --git a/drivers/net/ethernet/jme.c b/drivers/net/ethernet/jme.c
index 060dd3922974..b1de7afd4116 100644
--- a/drivers/net/ethernet/jme.c
+++ b/drivers/net/ethernet/jme.c
@@ -2753,7 +2753,7 @@ static netdev_features_t
jme_fix_features(struct net_device *netdev, netdev_features_t features)
{
if (netdev->mtu > 1900)
- features &= ~(NETIF_F_ALL_TSO | NETIF_F_ALL_CSUM);
+ features &= ~(NETIF_F_ALL_TSO | NETIF_F_CSUM_MASK);
return features;
}
diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c
index 581928c068f2..b630ef1e9646 100644
--- a/drivers/net/ethernet/lantiq_etop.c
+++ b/drivers/net/ethernet/lantiq_etop.c
@@ -375,22 +375,16 @@ static int
ltq_etop_mdio_probe(struct net_device *dev)
{
struct ltq_etop_priv *priv = netdev_priv(dev);
- struct phy_device *phydev = NULL;
- int phy_addr;
+ struct phy_device *phydev;
- for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
- if (priv->mii_bus->phy_map[phy_addr]) {
- phydev = priv->mii_bus->phy_map[phy_addr];
- break;
- }
- }
+ phydev = phy_find_first(priv->mii_bus);
if (!phydev) {
netdev_err(dev, "no PHY found\n");
return -ENODEV;
}
- phydev = phy_connect(dev, dev_name(&phydev->dev),
+ phydev = phy_connect(dev, phydev_name(phydev),
&ltq_etop_mdio_link, priv->pldata->mii_mode);
if (IS_ERR(phydev)) {
@@ -408,9 +402,7 @@ ltq_etop_mdio_probe(struct net_device *dev)
phydev->advertising = phydev->supported;
priv->phydev = phydev;
- pr_info("%s: attached PHY [%s] (phy_addr=%s, irq=%d)\n",
- dev->name, phydev->drv->name,
- dev_name(&phydev->dev), phydev->irq);
+ phy_attached_info(phydev);
return 0;
}
@@ -435,18 +427,9 @@ ltq_etop_mdio_init(struct net_device *dev)
priv->mii_bus->name = "ltq_mii";
snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
priv->pdev->name, priv->pdev->id);
- priv->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!priv->mii_bus->irq) {
- err = -ENOMEM;
- goto err_out_free_mdiobus;
- }
-
- for (i = 0; i < PHY_MAX_ADDR; ++i)
- priv->mii_bus->irq[i] = PHY_POLL;
-
if (mdiobus_register(priv->mii_bus)) {
err = -ENXIO;
- goto err_out_free_mdio_irq;
+ goto err_out_free_mdiobus;
}
if (ltq_etop_mdio_probe(dev)) {
@@ -457,8 +440,6 @@ ltq_etop_mdio_init(struct net_device *dev)
err_out_unregister_bus:
mdiobus_unregister(priv->mii_bus);
-err_out_free_mdio_irq:
- kfree(priv->mii_bus->irq);
err_out_free_mdiobus:
mdiobus_free(priv->mii_bus);
err_out:
@@ -472,7 +453,6 @@ ltq_etop_mdio_cleanup(struct net_device *dev)
phy_disconnect(priv->phydev);
mdiobus_unregister(priv->mii_bus);
- kfree(priv->mii_bus->irq);
mdiobus_free(priv->mii_bus);
}
diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c
index 4182290fdbcf..a0c03834a2f7 100644
--- a/drivers/net/ethernet/marvell/mv643xx_eth.c
+++ b/drivers/net/ethernet/marvell/mv643xx_eth.c
@@ -3133,7 +3133,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
if (!mp->phy)
err = -ENODEV;
else
- phy_addr_set(mp, mp->phy->addr);
+ phy_addr_set(mp, mp->phy->mdio.addr);
} else if (pd->phy_addr != MV643XX_ETH_PHY_NONE) {
mp->phy = phy_scan(mp, pd->phy_addr);
@@ -3257,25 +3257,20 @@ static struct platform_driver mv643xx_eth_driver = {
},
};
+static struct platform_driver * const drivers[] = {
+ &mv643xx_eth_shared_driver,
+ &mv643xx_eth_driver,
+};
+
static int __init mv643xx_eth_init_module(void)
{
- int rc;
-
- rc = platform_driver_register(&mv643xx_eth_shared_driver);
- if (!rc) {
- rc = platform_driver_register(&mv643xx_eth_driver);
- if (rc)
- platform_driver_unregister(&mv643xx_eth_shared_driver);
- }
-
- return rc;
+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
}
module_init(mv643xx_eth_init_module);
static void __exit mv643xx_eth_cleanup_module(void)
{
- platform_driver_unregister(&mv643xx_eth_driver);
- platform_driver_unregister(&mv643xx_eth_shared_driver);
+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
}
module_exit(mv643xx_eth_cleanup_module);
diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c
index fc2fb25343f4..8982c882af1b 100644
--- a/drivers/net/ethernet/marvell/mvmdio.c
+++ b/drivers/net/ethernet/marvell/mvmdio.c
@@ -187,7 +187,7 @@ static int orion_mdio_probe(struct platform_device *pdev)
struct resource *r;
struct mii_bus *bus;
struct orion_mdio_dev *dev;
- int i, ret;
+ int ret;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
@@ -207,14 +207,6 @@ static int orion_mdio_probe(struct platform_device *pdev)
dev_name(&pdev->dev));
bus->parent = &pdev->dev;
- bus->irq = devm_kmalloc_array(&pdev->dev, PHY_MAX_ADDR, sizeof(int),
- GFP_KERNEL);
- if (!bus->irq)
- return -ENOMEM;
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- bus->irq[i] = PHY_POLL;
-
dev = bus->priv;
dev->regs = devm_ioremap(&pdev->dev, r->start, resource_size(r));
if (!dev->regs) {
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index e84c7f2634d3..fabc8df40392 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -36,7 +36,7 @@
/* Registers */
#define MVNETA_RXQ_CONFIG_REG(q) (0x1400 + ((q) << 2))
-#define MVNETA_RXQ_HW_BUF_ALLOC BIT(1)
+#define MVNETA_RXQ_HW_BUF_ALLOC BIT(0)
#define MVNETA_RXQ_PKT_OFFSET_ALL_MASK (0xf << 8)
#define MVNETA_RXQ_PKT_OFFSET_MASK(offs) ((offs) << 8)
#define MVNETA_RXQ_THRESHOLD_REG(q) (0x14c0 + ((q) << 2))
@@ -62,6 +62,7 @@
#define MVNETA_WIN_SIZE(w) (0x2204 + ((w) << 3))
#define MVNETA_WIN_REMAP(w) (0x2280 + ((w) << 2))
#define MVNETA_BASE_ADDR_ENABLE 0x2290
+#define MVNETA_ACCESS_PROTECT_ENABLE 0x2294
#define MVNETA_PORT_CONFIG 0x2400
#define MVNETA_UNI_PROMISC_MODE BIT(0)
#define MVNETA_DEF_RXQ(q) ((q) << 1)
@@ -109,9 +110,17 @@
#define MVNETA_CPU_MAP(cpu) (0x2540 + ((cpu) << 2))
#define MVNETA_CPU_RXQ_ACCESS_ALL_MASK 0x000000ff
#define MVNETA_CPU_TXQ_ACCESS_ALL_MASK 0x0000ff00
+#define MVNETA_CPU_RXQ_ACCESS(rxq) BIT(rxq)
+#define MVNETA_CPU_TXQ_ACCESS(txq) BIT(txq + 8)
#define MVNETA_RXQ_TIME_COAL_REG(q) (0x2580 + ((q) << 2))
-/* Exception Interrupt Port/Queue Cause register */
+/* Exception Interrupt Port/Queue Cause register
+ *
+ * Their behavior depend of the mapping done using the PCPX2Q
+ * registers. For a given CPU if the bit associated to a queue is not
+ * set, then for the register a read from this CPU will always return
+ * 0 and a write won't do anything
+ */
#define MVNETA_INTR_NEW_CAUSE 0x25a0
#define MVNETA_INTR_NEW_MASK 0x25a4
@@ -159,7 +168,7 @@
#define MVNETA_INTR_ENABLE 0x25b8
#define MVNETA_TXQ_INTR_ENABLE_ALL_MASK 0x0000ff00
-#define MVNETA_RXQ_INTR_ENABLE_ALL_MASK 0xff000000 // note: neta says it's 0x000000FF
+#define MVNETA_RXQ_INTR_ENABLE_ALL_MASK 0x000000ff
#define MVNETA_RXQ_CMD 0x2680
#define MVNETA_RXQ_DISABLE_SHIFT 8
@@ -242,6 +251,7 @@
#define MVNETA_VLAN_TAG_LEN 4
#define MVNETA_CPU_D_CACHE_LINE_SIZE 32
+#define MVNETA_TX_CSUM_DEF_SIZE 1600
#define MVNETA_TX_CSUM_MAX_SIZE 9800
#define MVNETA_ACC_MODE_EXT 1
@@ -252,6 +262,11 @@
#define MVNETA_TX_MTU_MAX 0x3ffff
+/* The RSS lookup table actually has 256 entries but we do not use
+ * them yet
+ */
+#define MVNETA_RSS_LU_TABLE_SIZE 1
+
/* TSO header size */
#define TSO_HEADER_SIZE 128
@@ -354,6 +369,7 @@ struct mvneta_port {
struct mvneta_tx_queue *txqs;
struct net_device *dev;
struct notifier_block cpu_notifier;
+ int rxq_def;
/* Core clock */
struct clk *clk;
@@ -369,9 +385,11 @@ struct mvneta_port {
unsigned int duplex;
unsigned int speed;
unsigned int tx_csum_limit;
- int use_inband_status:1;
+ unsigned int use_inband_status:1;
u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)];
+
+ u32 indir[MVNETA_RSS_LU_TABLE_SIZE];
};
/* The mvneta_tx_desc and mvneta_rx_desc structures describe the
@@ -497,6 +515,9 @@ struct mvneta_tx_queue {
/* DMA address of TSO headers */
dma_addr_t tso_hdrs_phys;
+
+ /* Affinity mask for CPUs*/
+ cpumask_t affinity_mask;
};
struct mvneta_rx_queue {
@@ -817,7 +838,13 @@ static void mvneta_port_up(struct mvneta_port *pp)
mvreg_write(pp, MVNETA_TXQ_CMD, q_map);
/* Enable all initialized RXQs. */
- mvreg_write(pp, MVNETA_RXQ_CMD, BIT(rxq_def));
+ for (queue = 0; queue < rxq_number; queue++) {
+ struct mvneta_rx_queue *rxq = &pp->rxqs[queue];
+
+ if (rxq->descs != NULL)
+ q_map |= (1 << queue);
+ }
+ mvreg_write(pp, MVNETA_RXQ_CMD, q_map);
}
/* Stop the Ethernet port activity */
@@ -971,6 +998,44 @@ static void mvneta_set_other_mcast_table(struct mvneta_port *pp, int queue)
mvreg_write(pp, MVNETA_DA_FILT_OTH_MCAST + offset, val);
}
+static void mvneta_set_autoneg(struct mvneta_port *pp, int enable)
+{
+ u32 val;
+
+ if (enable) {
+ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
+ val &= ~(MVNETA_GMAC_FORCE_LINK_PASS |
+ MVNETA_GMAC_FORCE_LINK_DOWN |
+ MVNETA_GMAC_AN_FLOW_CTRL_EN);
+ val |= MVNETA_GMAC_INBAND_AN_ENABLE |
+ MVNETA_GMAC_AN_SPEED_EN |
+ MVNETA_GMAC_AN_DUPLEX_EN;
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+
+ val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
+ val |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
+ mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
+
+ val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
+ val |= MVNETA_GMAC2_INBAND_AN_ENABLE;
+ mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
+ } else {
+ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
+ val &= ~(MVNETA_GMAC_INBAND_AN_ENABLE |
+ MVNETA_GMAC_AN_SPEED_EN |
+ MVNETA_GMAC_AN_DUPLEX_EN);
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+
+ val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
+ val &= ~MVNETA_GMAC_1MS_CLOCK_ENABLE;
+ mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
+
+ val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
+ val &= ~MVNETA_GMAC2_INBAND_AN_ENABLE;
+ mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
+ }
+}
+
/* This method sets defaults to the NETA port:
* Clears interrupt Cause and Mask registers.
* Clears all MAC tables.
@@ -985,6 +1050,7 @@ static void mvneta_defaults_set(struct mvneta_port *pp)
int cpu;
int queue;
u32 val;
+ int max_cpu = num_present_cpus();
/* Clear all Cause registers */
mvreg_write(pp, MVNETA_INTR_NEW_CAUSE, 0);
@@ -1000,13 +1066,33 @@ static void mvneta_defaults_set(struct mvneta_port *pp)
/* Enable MBUS Retry bit16 */
mvreg_write(pp, MVNETA_MBUS_RETRY, 0x20);
- /* Set CPU queue access map - all CPUs have access to all RX
- * queues and to all TX queues
+ /* Set CPU queue access map. CPUs are assigned to the RX and
+ * TX queues modulo their number. If there is only one TX
+ * queue then it is assigned to the CPU associated to the
+ * default RX queue.
*/
- for_each_present_cpu(cpu)
- mvreg_write(pp, MVNETA_CPU_MAP(cpu),
- (MVNETA_CPU_RXQ_ACCESS_ALL_MASK |
- MVNETA_CPU_TXQ_ACCESS_ALL_MASK));
+ for_each_present_cpu(cpu) {
+ int rxq_map = 0, txq_map = 0;
+ int rxq, txq;
+
+ for (rxq = 0; rxq < rxq_number; rxq++)
+ if ((rxq % max_cpu) == cpu)
+ rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq);
+
+ for (txq = 0; txq < txq_number; txq++)
+ if ((txq % max_cpu) == cpu)
+ txq_map |= MVNETA_CPU_TXQ_ACCESS(txq);
+
+ /* With only one TX queue we configure a special case
+ * which will allow to get all the irq on a single
+ * CPU
+ */
+ if (txq_number == 1)
+ txq_map = (cpu == pp->rxq_def) ?
+ MVNETA_CPU_TXQ_ACCESS(1) : 0;
+
+ mvreg_write(pp, MVNETA_CPU_MAP(cpu), rxq_map | txq_map);
+ }
/* Reset RX and TX DMAs */
mvreg_write(pp, MVNETA_PORT_RX_RESET, MVNETA_PORT_RX_DMA_RESET);
@@ -1027,7 +1113,7 @@ static void mvneta_defaults_set(struct mvneta_port *pp)
mvreg_write(pp, MVNETA_ACC_MODE, val);
/* Update val of portCfg register accordingly with all RxQueue types */
- val = MVNETA_PORT_CONFIG_DEFL_VALUE(rxq_def);
+ val = MVNETA_PORT_CONFIG_DEFL_VALUE(pp->rxq_def);
mvreg_write(pp, MVNETA_PORT_CONFIG, val);
val = 0;
@@ -1056,26 +1142,7 @@ static void mvneta_defaults_set(struct mvneta_port *pp)
val &= ~MVNETA_PHY_POLLING_ENABLE;
mvreg_write(pp, MVNETA_UNIT_CONTROL, val);
- if (pp->use_inband_status) {
- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
- val &= ~(MVNETA_GMAC_FORCE_LINK_PASS |
- MVNETA_GMAC_FORCE_LINK_DOWN |
- MVNETA_GMAC_AN_FLOW_CTRL_EN);
- val |= MVNETA_GMAC_INBAND_AN_ENABLE |
- MVNETA_GMAC_AN_SPEED_EN |
- MVNETA_GMAC_AN_DUPLEX_EN;
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
- val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
- val |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
- mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
- } else {
- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
- val &= ~(MVNETA_GMAC_INBAND_AN_ENABLE |
- MVNETA_GMAC_AN_SPEED_EN |
- MVNETA_GMAC_AN_DUPLEX_EN);
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
- }
-
+ mvneta_set_autoneg(pp, pp->use_inband_status);
mvneta_set_ucast_table(pp, -1);
mvneta_set_special_mcast_table(pp, -1);
mvneta_set_other_mcast_table(pp, -1);
@@ -1579,12 +1646,16 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo,
}
skb = build_skb(data, pp->frag_size > PAGE_SIZE ? 0 : pp->frag_size);
- if (!skb)
- goto err_drop_frame;
+ /* After refill old buffer has to be unmapped regardless
+ * the skb is successfully built or not.
+ */
dma_unmap_single(dev->dev.parent, phys_addr,
MVNETA_RX_BUF_SIZE(pp->pkt_size), DMA_FROM_DEVICE);
+ if (!skb)
+ goto err_drop_frame;
+
rcvd_pkts++;
rcvd_bytes += rx_bytes;
@@ -2076,19 +2147,19 @@ static void mvneta_set_rx_mode(struct net_device *dev)
if (dev->flags & IFF_PROMISC) {
/* Accept all: Multicast + Unicast */
mvneta_rx_unicast_promisc_set(pp, 1);
- mvneta_set_ucast_table(pp, rxq_def);
- mvneta_set_special_mcast_table(pp, rxq_def);
- mvneta_set_other_mcast_table(pp, rxq_def);
+ mvneta_set_ucast_table(pp, pp->rxq_def);
+ mvneta_set_special_mcast_table(pp, pp->rxq_def);
+ mvneta_set_other_mcast_table(pp, pp->rxq_def);
} else {
/* Accept single Unicast */
mvneta_rx_unicast_promisc_set(pp, 0);
mvneta_set_ucast_table(pp, -1);
- mvneta_mac_addr_set(pp, dev->dev_addr, rxq_def);
+ mvneta_mac_addr_set(pp, dev->dev_addr, pp->rxq_def);
if (dev->flags & IFF_ALLMULTI) {
/* Accept all multicast */
- mvneta_set_special_mcast_table(pp, rxq_def);
- mvneta_set_other_mcast_table(pp, rxq_def);
+ mvneta_set_special_mcast_table(pp, pp->rxq_def);
+ mvneta_set_other_mcast_table(pp, pp->rxq_def);
} else {
/* Accept only initialized multicast */
mvneta_set_special_mcast_table(pp, -1);
@@ -2097,7 +2168,7 @@ static void mvneta_set_rx_mode(struct net_device *dev)
if (!netdev_mc_empty(dev)) {
netdev_for_each_mc_addr(ha, dev) {
mvneta_mcast_addr_set(pp, ha->addr,
- rxq_def);
+ pp->rxq_def);
}
}
}
@@ -2148,6 +2219,7 @@ static int mvneta_poll(struct napi_struct *napi, int budget)
{
int rx_done = 0;
u32 cause_rx_tx;
+ int rx_queue;
struct mvneta_port *pp = netdev_priv(napi->dev);
struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports);
@@ -2179,8 +2251,15 @@ static int mvneta_poll(struct napi_struct *napi, int budget)
/* For the case where the last mvneta_poll did not process all
* RX packets
*/
+ rx_queue = fls(((cause_rx_tx >> 8) & 0xff));
+
cause_rx_tx |= port->cause_rx_tx;
- rx_done = mvneta_rx(pp, budget, &pp->rxqs[rxq_def]);
+
+ if (rx_queue) {
+ rx_queue = rx_queue - 1;
+ rx_done = mvneta_rx(pp, budget, &pp->rxqs[rx_queue]);
+ }
+
budget -= rx_done;
if (budget > 0) {
@@ -2297,6 +2376,8 @@ static void mvneta_rxq_deinit(struct mvneta_port *pp,
static int mvneta_txq_init(struct mvneta_port *pp,
struct mvneta_tx_queue *txq)
{
+ int cpu;
+
txq->size = pp->tx_ring_size;
/* A queue must always have room for at least one skb.
@@ -2349,6 +2430,14 @@ static int mvneta_txq_init(struct mvneta_port *pp,
}
mvneta_tx_done_pkts_coal_set(pp, txq, txq->done_pkts_coal);
+ /* Setup XPS mapping */
+ if (txq_number > 1)
+ cpu = txq->id % num_present_cpus();
+ else
+ cpu = pp->rxq_def % num_present_cpus();
+ cpumask_set_cpu(cpu, &txq->affinity_mask);
+ netif_set_xps_queue(pp->dev, &txq->affinity_mask, txq->id);
+
return 0;
}
@@ -2393,19 +2482,27 @@ static void mvneta_cleanup_txqs(struct mvneta_port *pp)
/* Cleanup all Rx queues */
static void mvneta_cleanup_rxqs(struct mvneta_port *pp)
{
- mvneta_rxq_deinit(pp, &pp->rxqs[rxq_def]);
+ int queue;
+
+ for (queue = 0; queue < txq_number; queue++)
+ mvneta_rxq_deinit(pp, &pp->rxqs[queue]);
}
/* Init all Rx queues */
static int mvneta_setup_rxqs(struct mvneta_port *pp)
{
- int err = mvneta_rxq_init(pp, &pp->rxqs[rxq_def]);
- if (err) {
- netdev_err(pp->dev, "%s: can't create rxq=%d\n",
- __func__, rxq_def);
- mvneta_cleanup_rxqs(pp);
- return err;
+ int queue;
+
+ for (queue = 0; queue < rxq_number; queue++) {
+ int err = mvneta_rxq_init(pp, &pp->rxqs[queue]);
+
+ if (err) {
+ netdev_err(pp->dev, "%s: can't create rxq=%d\n",
+ __func__, queue);
+ mvneta_cleanup_rxqs(pp);
+ return err;
+ }
}
return 0;
@@ -2429,6 +2526,31 @@ static int mvneta_setup_txqs(struct mvneta_port *pp)
return 0;
}
+static void mvneta_percpu_unmask_interrupt(void *arg)
+{
+ struct mvneta_port *pp = arg;
+
+ /* All the queue are unmasked, but actually only the ones
+ * maped to this CPU will be unmasked
+ */
+ mvreg_write(pp, MVNETA_INTR_NEW_MASK,
+ MVNETA_RX_INTR_MASK_ALL |
+ MVNETA_TX_INTR_MASK_ALL |
+ MVNETA_MISCINTR_INTR_MASK);
+}
+
+static void mvneta_percpu_mask_interrupt(void *arg)
+{
+ struct mvneta_port *pp = arg;
+
+ /* All the queue are masked, but actually only the ones
+ * maped to this CPU will be masked
+ */
+ mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
+ mvreg_write(pp, MVNETA_INTR_OLD_MASK, 0);
+ mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0);
+}
+
static void mvneta_start_dev(struct mvneta_port *pp)
{
unsigned int cpu;
@@ -2446,11 +2568,10 @@ static void mvneta_start_dev(struct mvneta_port *pp)
napi_enable(&port->napi);
}
- /* Unmask interrupts */
- mvreg_write(pp, MVNETA_INTR_NEW_MASK,
- MVNETA_RX_INTR_MASK(rxq_number) |
- MVNETA_TX_INTR_MASK(txq_number) |
- MVNETA_MISCINTR_INTR_MASK);
+ /* Unmask interrupts. It has to be done from each CPU */
+ for_each_online_cpu(cpu)
+ smp_call_function_single(cpu, mvneta_percpu_unmask_interrupt,
+ pp, true);
mvreg_write(pp, MVNETA_INTR_MISC_MASK,
MVNETA_CAUSE_PHY_STATUS_CHANGE |
MVNETA_CAUSE_LINK_CHANGE |
@@ -2609,7 +2730,7 @@ static int mvneta_set_mac_addr(struct net_device *dev, void *addr)
mvneta_mac_addr_set(pp, dev->dev_addr, -1);
/* Set new addr in hw */
- mvneta_mac_addr_set(pp, sockaddr->sa_data, rxq_def);
+ mvneta_mac_addr_set(pp, sockaddr->sa_data, pp->rxq_def);
eth_commit_mac_addr_change(dev, addr);
return 0;
@@ -2726,22 +2847,45 @@ static void mvneta_percpu_disable(void *arg)
static void mvneta_percpu_elect(struct mvneta_port *pp)
{
- int online_cpu_idx, cpu, i = 0;
+ int online_cpu_idx, max_cpu, cpu, i = 0;
- online_cpu_idx = rxq_def % num_online_cpus();
+ online_cpu_idx = pp->rxq_def % num_online_cpus();
+ max_cpu = num_present_cpus();
for_each_online_cpu(cpu) {
+ int rxq_map = 0, txq_map = 0;
+ int rxq;
+
+ for (rxq = 0; rxq < rxq_number; rxq++)
+ if ((rxq % max_cpu) == cpu)
+ rxq_map |= MVNETA_CPU_RXQ_ACCESS(rxq);
+
if (i == online_cpu_idx)
- /* Enable per-CPU interrupt on the one CPU we
- * just elected
+ /* Map the default receive queue queue to the
+ * elected CPU
*/
- smp_call_function_single(cpu, mvneta_percpu_enable,
- pp, true);
+ rxq_map |= MVNETA_CPU_RXQ_ACCESS(pp->rxq_def);
+
+ /* We update the TX queue map only if we have one
+ * queue. In this case we associate the TX queue to
+ * the CPU bound to the default RX queue
+ */
+ if (txq_number == 1)
+ txq_map = (i == online_cpu_idx) ?
+ MVNETA_CPU_TXQ_ACCESS(1) : 0;
else
- /* Disable per-CPU interrupt on all the other CPU */
- smp_call_function_single(cpu, mvneta_percpu_disable,
- pp, true);
+ txq_map = mvreg_read(pp, MVNETA_CPU_MAP(cpu)) &
+ MVNETA_CPU_TXQ_ACCESS_ALL_MASK;
+
+ mvreg_write(pp, MVNETA_CPU_MAP(cpu), rxq_map | txq_map);
+
+ /* Update the interrupt mask on each CPU according the
+ * new mapping
+ */
+ smp_call_function_single(cpu, mvneta_percpu_unmask_interrupt,
+ pp, true);
i++;
+
}
};
@@ -2776,12 +2920,22 @@ static int mvneta_percpu_notifier(struct notifier_block *nfb,
mvreg_write(pp, MVNETA_INTR_MISC_MASK, 0);
napi_enable(&port->napi);
+
+ /* Enable per-CPU interrupts on the CPU that is
+ * brought up.
+ */
+ smp_call_function_single(cpu, mvneta_percpu_enable,
+ pp, true);
+
/* Enable per-CPU interrupt on the one CPU we care
* about.
*/
mvneta_percpu_elect(pp);
- /* Unmask all ethernet port interrupts */
+ /* Unmask all ethernet port interrupts, as this
+ * notifier is called for each CPU then the CPU to
+ * Queue mapping is applied
+ */
mvreg_write(pp, MVNETA_INTR_NEW_MASK,
MVNETA_RX_INTR_MASK(rxq_number) |
MVNETA_TX_INTR_MASK(txq_number) |
@@ -2832,7 +2986,7 @@ static int mvneta_percpu_notifier(struct notifier_block *nfb,
static int mvneta_open(struct net_device *dev)
{
struct mvneta_port *pp = netdev_priv(dev);
- int ret;
+ int ret, cpu;
pp->pkt_size = MVNETA_RX_PKT_SIZE(pp->dev->mtu);
pp->frag_size = SKB_DATA_ALIGN(MVNETA_RX_BUF_SIZE(pp->pkt_size)) +
@@ -2862,8 +3016,13 @@ static int mvneta_open(struct net_device *dev)
*/
mvneta_percpu_disable(pp);
- /* Elect a CPU to handle our RX queue interrupt */
- mvneta_percpu_elect(pp);
+ /* Enable per-CPU interrupt on all the CPU to handle our RX
+ * queue interrupts
+ */
+ for_each_online_cpu(cpu)
+ smp_call_function_single(cpu, mvneta_percpu_enable,
+ pp, true);
+
/* Register a CPU notifier to handle the case where our CPU
* might be taken offline.
@@ -2937,10 +3096,43 @@ int mvneta_ethtool_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
int mvneta_ethtool_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct mvneta_port *pp = netdev_priv(dev);
+ struct phy_device *phydev = pp->phy_dev;
- if (!pp->phy_dev)
+ if (!phydev)
return -ENODEV;
+ if ((cmd->autoneg == AUTONEG_ENABLE) != pp->use_inband_status) {
+ u32 val;
+
+ mvneta_set_autoneg(pp, cmd->autoneg == AUTONEG_ENABLE);
+
+ if (cmd->autoneg == AUTONEG_DISABLE) {
+ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
+ val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED |
+ MVNETA_GMAC_CONFIG_GMII_SPEED |
+ MVNETA_GMAC_CONFIG_FULL_DUPLEX);
+
+ if (phydev->duplex)
+ val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
+
+ if (phydev->speed == SPEED_1000)
+ val |= MVNETA_GMAC_CONFIG_GMII_SPEED;
+ else if (phydev->speed == SPEED_100)
+ val |= MVNETA_GMAC_CONFIG_MII_SPEED;
+
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+ }
+
+ pp->use_inband_status = (cmd->autoneg == AUTONEG_ENABLE);
+ netdev_info(pp->dev, "autoneg status set to %i\n",
+ pp->use_inband_status);
+
+ if (netif_running(dev)) {
+ mvneta_port_down(pp);
+ mvneta_port_up(pp);
+ }
+ }
+
return phy_ethtool_sset(pp->phy_dev, cmd);
}
@@ -3092,6 +3284,106 @@ static int mvneta_ethtool_get_sset_count(struct net_device *dev, int sset)
return -EOPNOTSUPP;
}
+static u32 mvneta_ethtool_get_rxfh_indir_size(struct net_device *dev)
+{
+ return MVNETA_RSS_LU_TABLE_SIZE;
+}
+
+static int mvneta_ethtool_get_rxnfc(struct net_device *dev,
+ struct ethtool_rxnfc *info,
+ u32 *rules __always_unused)
+{
+ switch (info->cmd) {
+ case ETHTOOL_GRXRINGS:
+ info->data = rxq_number;
+ return 0;
+ case ETHTOOL_GRXFH:
+ return -EOPNOTSUPP;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int mvneta_config_rss(struct mvneta_port *pp)
+{
+ int cpu;
+ u32 val;
+
+ netif_tx_stop_all_queues(pp->dev);
+
+ for_each_online_cpu(cpu)
+ smp_call_function_single(cpu, mvneta_percpu_mask_interrupt,
+ pp, true);
+
+ /* We have to synchronise on the napi of each CPU */
+ for_each_online_cpu(cpu) {
+ struct mvneta_pcpu_port *pcpu_port =
+ per_cpu_ptr(pp->ports, cpu);
+
+ napi_synchronize(&pcpu_port->napi);
+ napi_disable(&pcpu_port->napi);
+ }
+
+ pp->rxq_def = pp->indir[0];
+
+ /* Update unicast mapping */
+ mvneta_set_rx_mode(pp->dev);
+
+ /* Update val of portCfg register accordingly with all RxQueue types */
+ val = MVNETA_PORT_CONFIG_DEFL_VALUE(pp->rxq_def);
+ mvreg_write(pp, MVNETA_PORT_CONFIG, val);
+
+ /* Update the elected CPU matching the new rxq_def */
+ mvneta_percpu_elect(pp);
+
+ /* We have to synchronise on the napi of each CPU */
+ for_each_online_cpu(cpu) {
+ struct mvneta_pcpu_port *pcpu_port =
+ per_cpu_ptr(pp->ports, cpu);
+
+ napi_enable(&pcpu_port->napi);
+ }
+
+ netif_tx_start_all_queues(pp->dev);
+
+ return 0;
+}
+
+static int mvneta_ethtool_set_rxfh(struct net_device *dev, const u32 *indir,
+ const u8 *key, const u8 hfunc)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+ /* We require at least one supported parameter to be changed
+ * and no change in any of the unsupported parameters
+ */
+ if (key ||
+ (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
+ return -EOPNOTSUPP;
+
+ if (!indir)
+ return 0;
+
+ memcpy(pp->indir, indir, MVNETA_RSS_LU_TABLE_SIZE);
+
+ return mvneta_config_rss(pp);
+}
+
+static int mvneta_ethtool_get_rxfh(struct net_device *dev, u32 *indir, u8 *key,
+ u8 *hfunc)
+{
+ struct mvneta_port *pp = netdev_priv(dev);
+
+ if (hfunc)
+ *hfunc = ETH_RSS_HASH_TOP;
+
+ if (!indir)
+ return 0;
+
+ memcpy(indir, pp->indir, MVNETA_RSS_LU_TABLE_SIZE);
+
+ return 0;
+}
+
static const struct net_device_ops mvneta_netdev_ops = {
.ndo_open = mvneta_open,
.ndo_stop = mvneta_stop,
@@ -3116,6 +3408,10 @@ const struct ethtool_ops mvneta_eth_tool_ops = {
.get_strings = mvneta_ethtool_get_strings,
.get_ethtool_stats = mvneta_ethtool_get_stats,
.get_sset_count = mvneta_ethtool_get_sset_count,
+ .get_rxfh_indir_size = mvneta_ethtool_get_rxfh_indir_size,
+ .get_rxnfc = mvneta_ethtool_get_rxnfc,
+ .get_rxfh = mvneta_ethtool_get_rxfh,
+ .set_rxfh = mvneta_ethtool_set_rxfh,
};
/* Initialize hw */
@@ -3191,6 +3487,7 @@ static void mvneta_conf_mbus_windows(struct mvneta_port *pp,
}
mvreg_write(pp, MVNETA_BASE_ADDR_ENABLE, win_enable);
+ mvreg_write(pp, MVNETA_ACCESS_PROTECT_ENABLE, win_protect);
}
/* Power up the port */
@@ -3223,9 +3520,6 @@ static int mvneta_port_power_up(struct mvneta_port *pp, int phy_mode)
return -EINVAL;
}
- if (pp->use_inband_status)
- ctrl |= MVNETA_GMAC2_INBAND_AN_ENABLE;
-
/* Cancel Port Reset */
ctrl &= ~MVNETA_GMAC2_PORT_RESET;
mvreg_write(pp, MVNETA_GMAC_CTRL_2, ctrl);
@@ -3250,6 +3544,7 @@ static int mvneta_probe(struct platform_device *pdev)
char hw_mac_addr[ETH_ALEN];
const char *mac_from;
const char *managed;
+ int tx_csum_limit;
int phy_mode;
int err;
int cpu;
@@ -3306,6 +3601,10 @@ static int mvneta_probe(struct platform_device *pdev)
strcmp(managed, "in-band-status") == 0);
pp->cpu_notifier.notifier_call = mvneta_percpu_notifier;
+ pp->rxq_def = rxq_def;
+
+ pp->indir[0] = rxq_def;
+
pp->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(pp->clk)) {
err = PTR_ERR(pp->clk);
@@ -3350,8 +3649,21 @@ static int mvneta_probe(struct platform_device *pdev)
}
}
- if (of_device_is_compatible(dn, "marvell,armada-370-neta"))
- pp->tx_csum_limit = 1600;
+ if (!of_property_read_u32(dn, "tx-csum-limit", &tx_csum_limit)) {
+ if (tx_csum_limit < 0 ||
+ tx_csum_limit > MVNETA_TX_CSUM_MAX_SIZE) {
+ tx_csum_limit = MVNETA_TX_CSUM_DEF_SIZE;
+ dev_info(&pdev->dev,
+ "Wrong TX csum limit in DT, set to %dB\n",
+ MVNETA_TX_CSUM_DEF_SIZE);
+ }
+ } else if (of_device_is_compatible(dn, "marvell,armada-370-neta")) {
+ tx_csum_limit = MVNETA_TX_CSUM_DEF_SIZE;
+ } else {
+ tx_csum_limit = MVNETA_TX_CSUM_MAX_SIZE;
+ }
+
+ pp->tx_csum_limit = tx_csum_limit;
pp->tx_ring_size = MVNETA_MAX_TXD;
pp->rx_ring_size = MVNETA_MAX_RXD;
@@ -3402,7 +3714,7 @@ static int mvneta_probe(struct platform_device *pdev)
mvneta_fixed_link_update(pp, phy);
- put_device(&phy->dev);
+ put_device(&phy->mdio.dev);
}
return 0;
diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
index d9884fd15b45..a4beccf1fd46 100644
--- a/drivers/net/ethernet/marvell/mvpp2.c
+++ b/drivers/net/ethernet/marvell/mvpp2.c
@@ -3413,16 +3413,23 @@ static void mvpp2_bm_pool_bufsize_set(struct mvpp2 *priv,
}
/* Free all buffers from the pool */
-static void mvpp2_bm_bufs_free(struct mvpp2 *priv, struct mvpp2_bm_pool *bm_pool)
+static void mvpp2_bm_bufs_free(struct device *dev, struct mvpp2 *priv,
+ struct mvpp2_bm_pool *bm_pool)
{
int i;
for (i = 0; i < bm_pool->buf_num; i++) {
+ dma_addr_t buf_phys_addr;
u32 vaddr;
/* Get buffer virtual address (indirect access) */
- mvpp2_read(priv, MVPP2_BM_PHY_ALLOC_REG(bm_pool->id));
+ buf_phys_addr = mvpp2_read(priv,
+ MVPP2_BM_PHY_ALLOC_REG(bm_pool->id));
vaddr = mvpp2_read(priv, MVPP2_BM_VIRT_ALLOC_REG);
+
+ dma_unmap_single(dev, buf_phys_addr,
+ bm_pool->buf_size, DMA_FROM_DEVICE);
+
if (!vaddr)
break;
dev_kfree_skb_any((struct sk_buff *)vaddr);
@@ -3439,7 +3446,7 @@ static int mvpp2_bm_pool_destroy(struct platform_device *pdev,
{
u32 val;
- mvpp2_bm_bufs_free(priv, bm_pool);
+ mvpp2_bm_bufs_free(&pdev->dev, priv, bm_pool);
if (bm_pool->buf_num) {
WARN(1, "cannot free all buffers in pool %d\n", bm_pool->id);
return 0;
@@ -3692,7 +3699,8 @@ mvpp2_bm_pool_use(struct mvpp2_port *port, int pool, enum mvpp2_bm_type type,
MVPP2_BM_LONG_BUF_NUM :
MVPP2_BM_SHORT_BUF_NUM;
else
- mvpp2_bm_bufs_free(port->priv, new_pool);
+ mvpp2_bm_bufs_free(port->dev->dev.parent,
+ port->priv, new_pool);
new_pool->pkt_size = pkt_size;
@@ -3756,7 +3764,7 @@ static int mvpp2_bm_update_mtu(struct net_device *dev, int mtu)
int pkt_size = MVPP2_RX_PKT_SIZE(mtu);
/* Update BM pool with new buffer size */
- mvpp2_bm_bufs_free(port->priv, port_pool);
+ mvpp2_bm_bufs_free(dev->dev.parent, port->priv, port_pool);
if (port_pool->buf_num) {
WARN(1, "cannot free all buffers in pool %d\n", port_pool->id);
return -EIO;
@@ -4401,11 +4409,10 @@ static void mvpp2_txq_bufs_free(struct mvpp2_port *port,
mvpp2_txq_inc_get(txq_pcpu);
- if (!skb)
- continue;
-
dma_unmap_single(port->dev->dev.parent, buf_phys_addr,
skb_headlen(skb), DMA_TO_DEVICE);
+ if (!skb)
+ continue;
dev_kfree_skb_any(skb);
}
}
@@ -5092,7 +5099,8 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo,
struct mvpp2_rx_queue *rxq)
{
struct net_device *dev = port->dev;
- int rx_received, rx_filled, i;
+ int rx_received;
+ int rx_done = 0;
u32 rcvd_pkts = 0;
u32 rcvd_bytes = 0;
@@ -5101,17 +5109,18 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo,
if (rx_todo > rx_received)
rx_todo = rx_received;
- rx_filled = 0;
- for (i = 0; i < rx_todo; i++) {
+ while (rx_done < rx_todo) {
struct mvpp2_rx_desc *rx_desc = mvpp2_rxq_next_desc_get(rxq);
struct mvpp2_bm_pool *bm_pool;
struct sk_buff *skb;
+ dma_addr_t phys_addr;
u32 bm, rx_status;
int pool, rx_bytes, err;
- rx_filled++;
+ rx_done++;
rx_status = rx_desc->status;
rx_bytes = rx_desc->data_size - MVPP2_MH_SIZE;
+ phys_addr = rx_desc->buf_phys_addr;
bm = mvpp2_bm_cookie_build(rx_desc);
pool = mvpp2_bm_cookie_pool_get(bm);
@@ -5128,8 +5137,10 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo,
* comprised by the RX descriptor.
*/
if (rx_status & MVPP2_RXD_ERR_SUMMARY) {
+ err_drop_frame:
dev->stats.rx_errors++;
mvpp2_rx_error(port, rx_desc);
+ /* Return the buffer to the pool */
mvpp2_pool_refill(port, bm, rx_desc->buf_phys_addr,
rx_desc->buf_cookie);
continue;
@@ -5137,6 +5148,15 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo,
skb = (struct sk_buff *)rx_desc->buf_cookie;
+ err = mvpp2_rx_refill(port, bm_pool, bm, 0);
+ if (err) {
+ netdev_err(port->dev, "failed to refill BM pools\n");
+ goto err_drop_frame;
+ }
+
+ dma_unmap_single(dev->dev.parent, phys_addr,
+ bm_pool->buf_size, DMA_FROM_DEVICE);
+
rcvd_pkts++;
rcvd_bytes += rx_bytes;
atomic_inc(&bm_pool->in_use);
@@ -5147,12 +5167,6 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo,
mvpp2_rx_csum(port, rx_status, skb);
napi_gro_receive(&port->napi, skb);
-
- err = mvpp2_rx_refill(port, bm_pool, bm, 0);
- if (err) {
- netdev_err(port->dev, "failed to refill BM pools\n");
- rx_filled--;
- }
}
if (rcvd_pkts) {
@@ -5166,7 +5180,7 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo,
/* Update Rx queue management counters */
wmb();
- mvpp2_rxq_status_update(port, rxq->id, rx_todo, rx_filled);
+ mvpp2_rxq_status_update(port, rxq->id, rx_done, rx_done);
return rx_todo;
}
diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c
index 5606a043063e..ec0a22119e09 100644
--- a/drivers/net/ethernet/marvell/sky2.c
+++ b/drivers/net/ethernet/marvell/sky2.c
@@ -4380,7 +4380,7 @@ static netdev_features_t sky2_fix_features(struct net_device *dev,
*/
if (dev->mtu > ETH_DATA_LEN && hw->chip_id == CHIP_ID_YUKON_EC_U) {
netdev_info(dev, "checksum offload not possible with jumbo frames\n");
- features &= ~(NETIF_F_TSO|NETIF_F_SG|NETIF_F_ALL_CSUM);
+ features &= ~(NETIF_F_TSO | NETIF_F_SG | NETIF_F_CSUM_MASK);
}
/* Some hardware requires receive checksum for RSS to work. */
diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c
index 2177e56ed0be..d48d5793407d 100644
--- a/drivers/net/ethernet/mellanox/mlx4/cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c
@@ -1010,7 +1010,7 @@ static int mlx4_MAD_IFC_wrapper(struct mlx4_dev *dev, int slave,
if (!(smp->mgmt_class == IB_MGMT_CLASS_SUBN_LID_ROUTED &&
smp->method == IB_MGMT_METHOD_GET) || network_view) {
mlx4_err(dev, "Unprivileged slave %d is trying to execute a Subnet MGMT MAD, class 0x%x, method 0x%x, view=%s for attr 0x%x. Rejecting\n",
- slave, smp->method, smp->mgmt_class,
+ slave, smp->mgmt_class, smp->method,
network_view ? "Network" : "Host",
be16_to_cpu(smp->attr_id));
return -EPERM;
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_clock.c b/drivers/net/ethernet/mellanox/mlx4/en_clock.c
index 8a083d73efdb..038f9ce391e6 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_clock.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_clock.c
@@ -242,6 +242,13 @@ void mlx4_en_init_timestamp(struct mlx4_en_dev *mdev)
unsigned long flags;
u64 ns, zero = 0;
+ /* mlx4_en_init_timestamp is called for each netdev.
+ * mdev->ptp_clock is common for all ports, skip initialization if
+ * was done for other port.
+ */
+ if (mdev->ptp_clock)
+ return;
+
rwlock_init(&mdev->clock_lock);
memset(&mdev->cycles, 0, sizeof(mdev->cycles));
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c
index eb8a4988de63..af975a2b74c6 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_cq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c
@@ -155,13 +155,11 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq,
cq->mcq.comp = cq->is_tx ? mlx4_en_tx_irq : mlx4_en_rx_irq;
cq->mcq.event = mlx4_en_cq_event;
- if (cq->is_tx) {
- netif_napi_add(cq->dev, &cq->napi, mlx4_en_poll_tx_cq,
- NAPI_POLL_WEIGHT);
- } else {
+ if (cq->is_tx)
+ netif_tx_napi_add(cq->dev, &cq->napi, mlx4_en_poll_tx_cq,
+ NAPI_POLL_WEIGHT);
+ else
netif_napi_add(cq->dev, &cq->napi, mlx4_en_poll_rx_cq, 64);
- napi_hash_add(&cq->napi);
- }
napi_enable(&cq->napi);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
index ddb5541882f5..dd84cabb2a51 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
@@ -337,11 +337,7 @@ static int mlx4_en_get_sset_count(struct net_device *dev, int sset)
case ETH_SS_STATS:
return bitmap_iterator_count(&it) +
(priv->tx_ring_num * 2) +
-#ifdef CONFIG_NET_RX_BUSY_POLL
- (priv->rx_ring_num * 5);
-#else
(priv->rx_ring_num * 2);
-#endif
case ETH_SS_TEST:
return MLX4_EN_NUM_SELF_TEST - !(priv->mdev->dev->caps.flags
& MLX4_DEV_CAP_FLAG_UC_LOOPBACK) * 2;
@@ -408,11 +404,6 @@ static void mlx4_en_get_ethtool_stats(struct net_device *dev,
for (i = 0; i < priv->rx_ring_num; i++) {
data[index++] = priv->rx_ring[i]->packets;
data[index++] = priv->rx_ring[i]->bytes;
-#ifdef CONFIG_NET_RX_BUSY_POLL
- data[index++] = priv->rx_ring[i]->yields;
- data[index++] = priv->rx_ring[i]->misses;
- data[index++] = priv->rx_ring[i]->cleaned;
-#endif
}
spin_unlock_bh(&priv->stats_lock);
@@ -486,14 +477,6 @@ static void mlx4_en_get_strings(struct net_device *dev,
"rx%d_packets", i);
sprintf(data + (index++) * ETH_GSTRING_LEN,
"rx%d_bytes", i);
-#ifdef CONFIG_NET_RX_BUSY_POLL
- sprintf(data + (index++) * ETH_GSTRING_LEN,
- "rx%d_napi_yield", i);
- sprintf(data + (index++) * ETH_GSTRING_LEN,
- "rx%d_misses", i);
- sprintf(data + (index++) * ETH_GSTRING_LEN,
- "rx%d_cleaned", i);
-#endif
}
break;
case ETH_SS_PRIV_FLAGS:
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_main.c b/drivers/net/ethernet/mellanox/mlx4/en_main.c
index 005f910ec955..e0ec280a7fa1 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_main.c
@@ -232,9 +232,6 @@ static void mlx4_en_remove(struct mlx4_dev *dev, void *endev_ptr)
if (mdev->pndev[i])
mlx4_en_destroy_netdev(mdev->pndev[i]);
- if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS)
- mlx4_en_remove_timestamp(mdev);
-
flush_workqueue(mdev->workqueue);
destroy_workqueue(mdev->workqueue);
(void) mlx4_mr_free(dev, &mdev->mr);
@@ -320,10 +317,6 @@ static void *mlx4_en_add(struct mlx4_dev *dev)
mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_ETH)
mdev->port_cnt++;
- /* Initialize time stamp mechanism */
- if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS)
- mlx4_en_init_timestamp(mdev);
-
/* Set default number of RX rings*/
mlx4_en_set_num_rx_rings(mdev);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 886e1bc86374..0c7e3f69a73b 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -69,34 +69,6 @@ int mlx4_en_setup_tc(struct net_device *dev, u8 up)
return 0;
}
-#ifdef CONFIG_NET_RX_BUSY_POLL
-/* must be called with local_bh_disable()d */
-static int mlx4_en_low_latency_recv(struct napi_struct *napi)
-{
- struct mlx4_en_cq *cq = container_of(napi, struct mlx4_en_cq, napi);
- struct net_device *dev = cq->dev;
- struct mlx4_en_priv *priv = netdev_priv(dev);
- struct mlx4_en_rx_ring *rx_ring = priv->rx_ring[cq->ring];
- int done;
-
- if (!priv->port_up)
- return LL_FLUSH_FAILED;
-
- if (!mlx4_en_cq_lock_poll(cq))
- return LL_FLUSH_BUSY;
-
- done = mlx4_en_process_rx_cq(dev, cq, 4);
- if (likely(done))
- rx_ring->cleaned += done;
- else
- rx_ring->misses++;
-
- mlx4_en_cq_unlock_poll(cq);
-
- return done;
-}
-#endif /* CONFIG_NET_RX_BUSY_POLL */
-
#ifdef CONFIG_RFS_ACCEL
struct mlx4_en_filter {
@@ -1561,8 +1533,6 @@ int mlx4_en_start_port(struct net_device *dev)
for (i = 0; i < priv->rx_ring_num; i++) {
cq = priv->rx_cq[i];
- mlx4_en_cq_init_lock(cq);
-
err = mlx4_en_init_affinity_hint(priv, i);
if (err) {
en_err(priv, "Failed preparing IRQ affinity hint\n");
@@ -1859,13 +1829,6 @@ void mlx4_en_stop_port(struct net_device *dev, int detach)
for (i = 0; i < priv->rx_ring_num; i++) {
struct mlx4_en_cq *cq = priv->rx_cq[i];
- local_bh_disable();
- while (!mlx4_en_cq_lock_napi(cq)) {
- pr_info("CQ %d locked\n", i);
- mdelay(1);
- }
- local_bh_enable();
-
napi_synchronize(&cq->napi);
mlx4_en_deactivate_rx_ring(priv, priv->rx_ring[i]);
mlx4_en_deactivate_cq(priv, cq);
@@ -2072,6 +2035,9 @@ void mlx4_en_destroy_netdev(struct net_device *dev)
/* flush any pending task for this netdev */
flush_workqueue(mdev->workqueue);
+ if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS)
+ mlx4_en_remove_timestamp(mdev);
+
/* Detach the netdev so tasks would not attempt to access it */
mutex_lock(&mdev->state_lock);
mdev->pndev[priv->port] = NULL;
@@ -2504,9 +2470,6 @@ static const struct net_device_ops mlx4_netdev_ops = {
#ifdef CONFIG_RFS_ACCEL
.ndo_rx_flow_steer = mlx4_en_filter_rfs,
#endif
-#ifdef CONFIG_NET_RX_BUSY_POLL
- .ndo_busy_poll = mlx4_en_low_latency_recv,
-#endif
.ndo_get_phys_port_id = mlx4_en_get_phys_port_id,
#ifdef CONFIG_MLX4_EN_VXLAN
.ndo_add_vxlan_port = mlx4_en_add_vxlan_port,
@@ -3058,9 +3021,12 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
}
queue_delayed_work(mdev->workqueue, &priv->stats_task, STATS_DELAY);
+ /* Initialize time stamp mechanism */
if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS)
- queue_delayed_work(mdev->workqueue, &priv->service_task,
- SERVICE_TASK_DELAY);
+ mlx4_en_init_timestamp(mdev);
+
+ queue_delayed_work(mdev->workqueue, &priv->service_task,
+ SERVICE_TASK_DELAY);
mlx4_en_set_stats_bitmap(mdev->dev, &priv->stats_bitmap,
mdev->profile.prof[priv->port].rx_ppp,
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index e7a5000aa12c..41440b2b20a3 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -873,10 +873,8 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
* - TCP/IP (v4)
* - without IP options
* - not an IP fragment
- * - no LLS polling in progress
*/
- if (!mlx4_en_cq_busy_polling(cq) &&
- (dev->features & NETIF_F_GRO)) {
+ if (dev->features & NETIF_F_GRO) {
struct sk_buff *gro_skb = napi_get_frags(&cq->napi);
if (!gro_skb)
goto next;
@@ -927,7 +925,6 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
PKT_HASH_TYPE_L3);
skb_record_rx_queue(gro_skb, cq->ring);
- skb_mark_napi_id(gro_skb, &cq->napi);
if (ring->hwtstamp_rx_filter == HWTSTAMP_FILTER_ALL) {
timestamp = mlx4_en_get_cqe_ts(cqe);
@@ -990,13 +987,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
timestamp);
}
- skb_mark_napi_id(skb, &cq->napi);
-
- if (!mlx4_en_cq_busy_polling(cq))
- napi_gro_receive(&cq->napi, skb);
- else
- netif_receive_skb(skb);
-
+ napi_gro_receive(&cq->napi, skb);
next:
for (nr = 0; nr < priv->num_frags; nr++)
mlx4_en_free_frag(priv, frags, nr);
@@ -1038,13 +1029,8 @@ int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget)
struct mlx4_en_priv *priv = netdev_priv(dev);
int done;
- if (!mlx4_en_cq_lock_napi(cq))
- return budget;
-
done = mlx4_en_process_rx_cq(dev, cq, budget);
- mlx4_en_cq_unlock_napi(cq);
-
/* If we used up all the quota - we're probably not done yet... */
if (done == budget) {
const struct cpumask *aff;
diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c
index 603d1c3d3b2e..4696053165f8 100644
--- a/drivers/net/ethernet/mellanox/mlx4/eq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/eq.c
@@ -151,6 +151,17 @@ void mlx4_gen_slave_eqe(struct work_struct *work)
eqe = next_slave_event_eqe(slave_eq)) {
slave = eqe->slave_id;
+ if (eqe->type == MLX4_EVENT_TYPE_PORT_CHANGE &&
+ eqe->subtype == MLX4_PORT_CHANGE_SUBTYPE_DOWN &&
+ mlx4_is_bonded(dev)) {
+ struct mlx4_port_cap port_cap;
+
+ if (!mlx4_QUERY_PORT(dev, 1, &port_cap) && port_cap.link_state)
+ goto consume;
+
+ if (!mlx4_QUERY_PORT(dev, 2, &port_cap) && port_cap.link_state)
+ goto consume;
+ }
/* All active slaves need to receive the event */
if (slave == ALL_SLAVES) {
for (i = 0; i <= dev->persist->num_vfs; i++) {
@@ -174,6 +185,7 @@ void mlx4_gen_slave_eqe(struct work_struct *work)
mlx4_warn(dev, "Failed to generate event for slave %d\n",
slave);
}
+consume:
++slave_eq->cons;
}
}
@@ -594,7 +606,9 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
break;
for (i = 0; i < dev->persist->num_vfs + 1;
i++) {
- if (!test_bit(i, slaves_port.slaves))
+ int reported_port = mlx4_is_bonded(dev) ? 1 : mlx4_phys_to_slave_port(dev, i, port);
+
+ if (!test_bit(i, slaves_port.slaves) && !mlx4_is_bonded(dev))
continue;
if (dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH) {
if (i == mlx4_master_func_num(dev))
@@ -606,7 +620,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
eqe->event.port_change.port =
cpu_to_be32(
(be32_to_cpu(eqe->event.port_change.port) & 0xFFFFFFF)
- | (mlx4_phys_to_slave_port(dev, i, port) << 28));
+ | (reported_port << 28));
mlx4_slave_event(dev, i, eqe);
}
} else { /* IB port */
@@ -636,7 +650,9 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
for (i = 0;
i < dev->persist->num_vfs + 1;
i++) {
- if (!test_bit(i, slaves_port.slaves))
+ int reported_port = mlx4_is_bonded(dev) ? 1 : mlx4_phys_to_slave_port(dev, i, port);
+
+ if (!test_bit(i, slaves_port.slaves) && !mlx4_is_bonded(dev))
continue;
if (i == mlx4_master_func_num(dev))
continue;
@@ -645,7 +661,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq)
eqe->event.port_change.port =
cpu_to_be32(
(be32_to_cpu(eqe->event.port_change.port) & 0xFFFFFFF)
- | (mlx4_phys_to_slave_port(dev, i, port) << 28));
+ | (reported_port << 28));
mlx4_slave_event(dev, i, eqe);
}
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c
index 90db94e83fde..2c2baab9d880 100644
--- a/drivers/net/ethernet/mellanox/mlx4/fw.c
+++ b/drivers/net/ethernet/mellanox/mlx4/fw.c
@@ -1104,6 +1104,7 @@ int mlx4_QUERY_PORT(struct mlx4_dev *dev, int port, struct mlx4_port_cap *port_c
goto out;
MLX4_GET(field, outbox, QUERY_PORT_SUPPORTED_TYPE_OFFSET);
+ port_cap->link_state = (field & 0x80) >> 7;
port_cap->supported_port_types = field & 3;
port_cap->suggested_type = (field >> 3) & 1;
port_cap->default_sense = (field >> 4) & 1;
@@ -1310,6 +1311,15 @@ int mlx4_QUERY_PORT_wrapper(struct mlx4_dev *dev, int slave,
port_type |= MLX4_PORT_LINK_UP_MASK;
else if (IFLA_VF_LINK_STATE_DISABLE == admin_link_state)
port_type &= ~MLX4_PORT_LINK_UP_MASK;
+ else if (IFLA_VF_LINK_STATE_AUTO == admin_link_state && mlx4_is_bonded(dev)) {
+ int other_port = (port == 1) ? 2 : 1;
+ struct mlx4_port_cap port_cap;
+
+ err = mlx4_QUERY_PORT(dev, other_port, &port_cap);
+ if (err)
+ goto out;
+ port_type |= (port_cap.link_state << 7);
+ }
MLX4_PUT(outbox->buf, port_type,
QUERY_PORT_SUPPORTED_TYPE_OFFSET);
@@ -1325,7 +1335,7 @@ int mlx4_QUERY_PORT_wrapper(struct mlx4_dev *dev, int slave,
MLX4_PUT(outbox->buf, short_field,
QUERY_PORT_CUR_MAX_PKEY_OFFSET);
}
-
+out:
return err;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.h b/drivers/net/ethernet/mellanox/mlx4/fw.h
index 08de5555c2f4..7ea258af636a 100644
--- a/drivers/net/ethernet/mellanox/mlx4/fw.h
+++ b/drivers/net/ethernet/mellanox/mlx4/fw.h
@@ -44,6 +44,7 @@ struct mlx4_mod_stat_cfg {
};
struct mlx4_port_cap {
+ u8 link_state;
u8 supported_port_types;
u8 suggested_type;
u8 default_sense;
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index 31c491e02e69..f1b6d219e445 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -1221,6 +1221,76 @@ err_set_port:
return err ? err : count;
}
+/* bond for multi-function device */
+#define MAX_MF_BOND_ALLOWED_SLAVES 63
+static int mlx4_mf_bond(struct mlx4_dev *dev)
+{
+ int err = 0;
+ struct mlx4_slaves_pport slaves_port1;
+ struct mlx4_slaves_pport slaves_port2;
+ DECLARE_BITMAP(slaves_port_1_2, MLX4_MFUNC_MAX);
+
+ slaves_port1 = mlx4_phys_to_slaves_pport(dev, 1);
+ slaves_port2 = mlx4_phys_to_slaves_pport(dev, 2);
+ bitmap_and(slaves_port_1_2,
+ slaves_port1.slaves, slaves_port2.slaves,
+ dev->persist->num_vfs + 1);
+
+ /* only single port vfs are allowed */
+ if (bitmap_weight(slaves_port_1_2, dev->persist->num_vfs + 1) > 1) {
+ mlx4_warn(dev, "HA mode unsupported for dual ported VFs\n");
+ return -EINVAL;
+ }
+
+ /* limit on maximum allowed VFs */
+ if ((bitmap_weight(slaves_port1.slaves, dev->persist->num_vfs + 1) +
+ bitmap_weight(slaves_port2.slaves, dev->persist->num_vfs + 1)) >
+ MAX_MF_BOND_ALLOWED_SLAVES)
+ return -EINVAL;
+
+ if (dev->caps.steering_mode != MLX4_STEERING_MODE_DEVICE_MANAGED) {
+ mlx4_warn(dev, "HA mode unsupported for NON DMFS steering\n");
+ return -EINVAL;
+ }
+
+ err = mlx4_bond_mac_table(dev);
+ if (err)
+ return err;
+ err = mlx4_bond_vlan_table(dev);
+ if (err)
+ goto err1;
+ err = mlx4_bond_fs_rules(dev);
+ if (err)
+ goto err2;
+
+ return 0;
+err2:
+ (void)mlx4_unbond_vlan_table(dev);
+err1:
+ (void)mlx4_unbond_mac_table(dev);
+ return err;
+}
+
+static int mlx4_mf_unbond(struct mlx4_dev *dev)
+{
+ int ret, ret1;
+
+ ret = mlx4_unbond_fs_rules(dev);
+ if (ret)
+ mlx4_warn(dev, "multifunction unbond for flow rules failedi (%d)\n", ret);
+ ret1 = mlx4_unbond_mac_table(dev);
+ if (ret1) {
+ mlx4_warn(dev, "multifunction unbond for MAC table failed (%d)\n", ret1);
+ ret = ret1;
+ }
+ ret1 = mlx4_unbond_vlan_table(dev);
+ if (ret1) {
+ mlx4_warn(dev, "multifunction unbond for VLAN table failed (%d)\n", ret1);
+ ret = ret1;
+ }
+ return ret;
+}
+
int mlx4_bond(struct mlx4_dev *dev)
{
int ret = 0;
@@ -1228,16 +1298,23 @@ int mlx4_bond(struct mlx4_dev *dev)
mutex_lock(&priv->bond_mutex);
- if (!mlx4_is_bonded(dev))
+ if (!mlx4_is_bonded(dev)) {
ret = mlx4_do_bond(dev, true);
- else
- ret = 0;
+ if (ret)
+ mlx4_err(dev, "Failed to bond device: %d\n", ret);
+ if (!ret && mlx4_is_master(dev)) {
+ ret = mlx4_mf_bond(dev);
+ if (ret) {
+ mlx4_err(dev, "bond for multifunction failed\n");
+ mlx4_do_bond(dev, false);
+ }
+ }
+ }
mutex_unlock(&priv->bond_mutex);
- if (ret)
- mlx4_err(dev, "Failed to bond device: %d\n", ret);
- else
+ if (!ret)
mlx4_dbg(dev, "Device is bonded\n");
+
return ret;
}
EXPORT_SYMBOL_GPL(mlx4_bond);
@@ -1249,14 +1326,24 @@ int mlx4_unbond(struct mlx4_dev *dev)
mutex_lock(&priv->bond_mutex);
- if (mlx4_is_bonded(dev))
+ if (mlx4_is_bonded(dev)) {
+ int ret2 = 0;
+
ret = mlx4_do_bond(dev, false);
+ if (ret)
+ mlx4_err(dev, "Failed to unbond device: %d\n", ret);
+ if (mlx4_is_master(dev))
+ ret2 = mlx4_mf_unbond(dev);
+ if (ret2) {
+ mlx4_warn(dev, "Failed to unbond device for multifunction (%d)\n", ret2);
+ ret = ret2;
+ }
+ }
mutex_unlock(&priv->bond_mutex);
- if (ret)
- mlx4_err(dev, "Failed to unbond device: %d\n", ret);
- else
+ if (!ret)
mlx4_dbg(dev, "Device is unbonded\n");
+
return ret;
}
EXPORT_SYMBOL_GPL(mlx4_unbond);
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h
index e1cf9036af22..2404c22ad2b2 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h
@@ -736,6 +736,7 @@ struct mlx4_catas_err {
struct mlx4_mac_table {
__be64 entries[MLX4_MAX_MAC_NUM];
int refs[MLX4_MAX_MAC_NUM];
+ bool is_dup[MLX4_MAX_MAC_NUM];
struct mutex mutex;
int total;
int max;
@@ -758,6 +759,7 @@ struct mlx4_roce_gid_table {
struct mlx4_vlan_table {
__be32 entries[MLX4_MAX_VLAN_NUM];
int refs[MLX4_MAX_VLAN_NUM];
+ int is_dup[MLX4_MAX_VLAN_NUM];
struct mutex mutex;
int total;
int max;
@@ -1225,6 +1227,10 @@ void mlx4_init_roce_gid_table(struct mlx4_dev *dev,
struct mlx4_roce_gid_table *table);
void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan);
int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index);
+int mlx4_bond_vlan_table(struct mlx4_dev *dev);
+int mlx4_unbond_vlan_table(struct mlx4_dev *dev);
+int mlx4_bond_mac_table(struct mlx4_dev *dev);
+int mlx4_unbond_mac_table(struct mlx4_dev *dev);
int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port, int pkey_tbl_sz);
/* resource tracker functions*/
@@ -1385,6 +1391,8 @@ int mlx4_get_slave_num_gids(struct mlx4_dev *dev, int slave, int port);
int mlx4_get_vf_indx(struct mlx4_dev *dev, int slave);
int mlx4_config_mad_demux(struct mlx4_dev *dev);
int mlx4_do_bond(struct mlx4_dev *dev, bool enable);
+int mlx4_bond_fs_rules(struct mlx4_dev *dev);
+int mlx4_unbond_fs_rules(struct mlx4_dev *dev);
enum mlx4_zone_flags {
MLX4_ZONE_ALLOW_ALLOC_FROM_LOWER_PRIO = 1UL << 0,
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index c41f15102ae0..35de7d2e6b34 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -320,11 +320,6 @@ struct mlx4_en_rx_ring {
void *rx_info;
unsigned long bytes;
unsigned long packets;
-#ifdef CONFIG_NET_RX_BUSY_POLL
- unsigned long yields;
- unsigned long misses;
- unsigned long cleaned;
-#endif
unsigned long csum_ok;
unsigned long csum_none;
unsigned long csum_complete;
@@ -347,18 +342,6 @@ struct mlx4_en_cq {
struct mlx4_cqe *buf;
#define MLX4_EN_OPCODE_ERROR 0x1e
-#ifdef CONFIG_NET_RX_BUSY_POLL
- unsigned int state;
-#define MLX4_EN_CQ_STATE_IDLE 0
-#define MLX4_EN_CQ_STATE_NAPI 1 /* NAPI owns this CQ */
-#define MLX4_EN_CQ_STATE_POLL 2 /* poll owns this CQ */
-#define MLX4_CQ_LOCKED (MLX4_EN_CQ_STATE_NAPI | MLX4_EN_CQ_STATE_POLL)
-#define MLX4_EN_CQ_STATE_NAPI_YIELD 4 /* NAPI yielded this CQ */
-#define MLX4_EN_CQ_STATE_POLL_YIELD 8 /* poll yielded this CQ */
-#define CQ_YIELD (MLX4_EN_CQ_STATE_NAPI_YIELD | MLX4_EN_CQ_STATE_POLL_YIELD)
-#define CQ_USER_PEND (MLX4_EN_CQ_STATE_POLL | MLX4_EN_CQ_STATE_POLL_YIELD)
- spinlock_t poll_lock; /* protects from LLS/napi conflicts */
-#endif /* CONFIG_NET_RX_BUSY_POLL */
struct irq_desc *irq_desc;
};
@@ -622,115 +605,6 @@ static inline struct mlx4_cqe *mlx4_en_get_cqe(void *buf, int idx, int cqe_sz)
return buf + idx * cqe_sz;
}
-#ifdef CONFIG_NET_RX_BUSY_POLL
-static inline void mlx4_en_cq_init_lock(struct mlx4_en_cq *cq)
-{
- spin_lock_init(&cq->poll_lock);
- cq->state = MLX4_EN_CQ_STATE_IDLE;
-}
-
-/* called from the device poll rutine to get ownership of a cq */
-static inline bool mlx4_en_cq_lock_napi(struct mlx4_en_cq *cq)
-{
- int rc = true;
- spin_lock(&cq->poll_lock);
- if (cq->state & MLX4_CQ_LOCKED) {
- WARN_ON(cq->state & MLX4_EN_CQ_STATE_NAPI);
- cq->state |= MLX4_EN_CQ_STATE_NAPI_YIELD;
- rc = false;
- } else
- /* we don't care if someone yielded */
- cq->state = MLX4_EN_CQ_STATE_NAPI;
- spin_unlock(&cq->poll_lock);
- return rc;
-}
-
-/* returns true is someone tried to get the cq while napi had it */
-static inline bool mlx4_en_cq_unlock_napi(struct mlx4_en_cq *cq)
-{
- int rc = false;
- spin_lock(&cq->poll_lock);
- WARN_ON(cq->state & (MLX4_EN_CQ_STATE_POLL |
- MLX4_EN_CQ_STATE_NAPI_YIELD));
-
- if (cq->state & MLX4_EN_CQ_STATE_POLL_YIELD)
- rc = true;
- cq->state = MLX4_EN_CQ_STATE_IDLE;
- spin_unlock(&cq->poll_lock);
- return rc;
-}
-
-/* called from mlx4_en_low_latency_poll() */
-static inline bool mlx4_en_cq_lock_poll(struct mlx4_en_cq *cq)
-{
- int rc = true;
- spin_lock_bh(&cq->poll_lock);
- if ((cq->state & MLX4_CQ_LOCKED)) {
- struct net_device *dev = cq->dev;
- struct mlx4_en_priv *priv = netdev_priv(dev);
- struct mlx4_en_rx_ring *rx_ring = priv->rx_ring[cq->ring];
-
- cq->state |= MLX4_EN_CQ_STATE_POLL_YIELD;
- rc = false;
- rx_ring->yields++;
- } else
- /* preserve yield marks */
- cq->state |= MLX4_EN_CQ_STATE_POLL;
- spin_unlock_bh(&cq->poll_lock);
- return rc;
-}
-
-/* returns true if someone tried to get the cq while it was locked */
-static inline bool mlx4_en_cq_unlock_poll(struct mlx4_en_cq *cq)
-{
- int rc = false;
- spin_lock_bh(&cq->poll_lock);
- WARN_ON(cq->state & (MLX4_EN_CQ_STATE_NAPI));
-
- if (cq->state & MLX4_EN_CQ_STATE_POLL_YIELD)
- rc = true;
- cq->state = MLX4_EN_CQ_STATE_IDLE;
- spin_unlock_bh(&cq->poll_lock);
- return rc;
-}
-
-/* true if a socket is polling, even if it did not get the lock */
-static inline bool mlx4_en_cq_busy_polling(struct mlx4_en_cq *cq)
-{
- WARN_ON(!(cq->state & MLX4_CQ_LOCKED));
- return cq->state & CQ_USER_PEND;
-}
-#else
-static inline void mlx4_en_cq_init_lock(struct mlx4_en_cq *cq)
-{
-}
-
-static inline bool mlx4_en_cq_lock_napi(struct mlx4_en_cq *cq)
-{
- return true;
-}
-
-static inline bool mlx4_en_cq_unlock_napi(struct mlx4_en_cq *cq)
-{
- return false;
-}
-
-static inline bool mlx4_en_cq_lock_poll(struct mlx4_en_cq *cq)
-{
- return false;
-}
-
-static inline bool mlx4_en_cq_unlock_poll(struct mlx4_en_cq *cq)
-{
- return false;
-}
-
-static inline bool mlx4_en_cq_busy_polling(struct mlx4_en_cq *cq)
-{
- return false;
-}
-#endif /* CONFIG_NET_RX_BUSY_POLL */
-
#define MLX4_EN_WOL_DO_MODIFY (1ULL << 63)
void mlx4_en_update_loopback_state(struct net_device *dev,
diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c
index c2b21313dba7..f2550425c251 100644
--- a/drivers/net/ethernet/mellanox/mlx4/port.c
+++ b/drivers/net/ethernet/mellanox/mlx4/port.c
@@ -61,6 +61,7 @@ void mlx4_init_mac_table(struct mlx4_dev *dev, struct mlx4_mac_table *table)
for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
table->entries[i] = 0;
table->refs[i] = 0;
+ table->is_dup[i] = false;
}
table->max = 1 << dev->caps.log_num_macs;
table->total = 0;
@@ -74,6 +75,7 @@ void mlx4_init_vlan_table(struct mlx4_dev *dev, struct mlx4_vlan_table *table)
for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) {
table->entries[i] = 0;
table->refs[i] = 0;
+ table->is_dup[i] = false;
}
table->max = (1 << dev->caps.log_num_vlans) - MLX4_VLAN_REGULAR;
table->total = 0;
@@ -159,21 +161,94 @@ int mlx4_find_cached_mac(struct mlx4_dev *dev, u8 port, u64 mac, int *idx)
}
EXPORT_SYMBOL_GPL(mlx4_find_cached_mac);
+static bool mlx4_need_mf_bond(struct mlx4_dev *dev)
+{
+ int i, num_eth_ports = 0;
+
+ if (!mlx4_is_mfunc(dev))
+ return false;
+ mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_ETH)
+ ++num_eth_ports;
+
+ return (num_eth_ports == 2) ? true : false;
+}
+
int __mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac)
{
struct mlx4_port_info *info = &mlx4_priv(dev)->port[port];
struct mlx4_mac_table *table = &info->mac_table;
int i, err = 0;
int free = -1;
+ int free_for_dup = -1;
+ bool dup = mlx4_is_mf_bonded(dev);
+ u8 dup_port = (port == 1) ? 2 : 1;
+ struct mlx4_mac_table *dup_table = &mlx4_priv(dev)->port[dup_port].mac_table;
+ bool need_mf_bond = mlx4_need_mf_bond(dev);
+ bool can_mf_bond = true;
+
+ mlx4_dbg(dev, "Registering MAC: 0x%llx for port %d %s duplicate\n",
+ (unsigned long long)mac, port,
+ dup ? "with" : "without");
+
+ if (need_mf_bond) {
+ if (port == 1) {
+ mutex_lock(&table->mutex);
+ mutex_lock(&dup_table->mutex);
+ } else {
+ mutex_lock(&dup_table->mutex);
+ mutex_lock(&table->mutex);
+ }
+ } else {
+ mutex_lock(&table->mutex);
+ }
+
+ if (need_mf_bond) {
+ int index_at_port = -1;
+ int index_at_dup_port = -1;
- mlx4_dbg(dev, "Registering MAC: 0x%llx for port %d\n",
- (unsigned long long) mac, port);
+ for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
+ if (((MLX4_MAC_MASK & mac) == (MLX4_MAC_MASK & be64_to_cpu(table->entries[i]))))
+ index_at_port = i;
+ if (((MLX4_MAC_MASK & mac) == (MLX4_MAC_MASK & be64_to_cpu(dup_table->entries[i]))))
+ index_at_dup_port = i;
+ }
+
+ /* check that same mac is not in the tables at different indices */
+ if ((index_at_port != index_at_dup_port) &&
+ (index_at_port >= 0) &&
+ (index_at_dup_port >= 0))
+ can_mf_bond = false;
+
+ /* If the mac is already in the primary table, the slot must be
+ * available in the duplicate table as well.
+ */
+ if (index_at_port >= 0 && index_at_dup_port < 0 &&
+ dup_table->refs[index_at_port]) {
+ can_mf_bond = false;
+ }
+ /* If the mac is already in the duplicate table, check that the
+ * corresponding index is not occupied in the primary table, or
+ * the primary table already contains the mac at the same index.
+ * Otherwise, you cannot bond (primary contains a different mac
+ * at that index).
+ */
+ if (index_at_dup_port >= 0) {
+ if (!table->refs[index_at_dup_port] ||
+ ((MLX4_MAC_MASK & mac) == (MLX4_MAC_MASK & be64_to_cpu(table->entries[index_at_dup_port]))))
+ free_for_dup = index_at_dup_port;
+ else
+ can_mf_bond = false;
+ }
+ }
- mutex_lock(&table->mutex);
for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
if (!table->refs[i]) {
if (free < 0)
free = i;
+ if (free_for_dup < 0 && need_mf_bond && can_mf_bond) {
+ if (!dup_table->refs[i])
+ free_for_dup = i;
+ }
continue;
}
@@ -182,10 +257,30 @@ int __mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac)
/* MAC already registered, increment ref count */
err = i;
++table->refs[i];
+ if (dup) {
+ u64 dup_mac = MLX4_MAC_MASK & be64_to_cpu(dup_table->entries[i]);
+
+ if (dup_mac != mac || !dup_table->is_dup[i]) {
+ mlx4_warn(dev, "register mac: expect duplicate mac 0x%llx on port %d index %d\n",
+ mac, dup_port, i);
+ }
+ }
goto out;
}
}
+ if (need_mf_bond && (free_for_dup < 0)) {
+ if (dup) {
+ mlx4_warn(dev, "Fail to allocate duplicate MAC table entry\n");
+ mlx4_warn(dev, "High Availability for virtual functions may not work as expected\n");
+ dup = false;
+ }
+ can_mf_bond = false;
+ }
+
+ if (need_mf_bond && can_mf_bond)
+ free = free_for_dup;
+
mlx4_dbg(dev, "Free MAC index is %d\n", free);
if (table->total == table->max) {
@@ -205,10 +300,35 @@ int __mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac)
goto out;
}
table->refs[free] = 1;
- err = free;
+ table->is_dup[free] = false;
++table->total;
+ if (dup) {
+ dup_table->refs[free] = 0;
+ dup_table->is_dup[free] = true;
+ dup_table->entries[free] = cpu_to_be64(mac | MLX4_MAC_VALID);
+
+ err = mlx4_set_port_mac_table(dev, dup_port, dup_table->entries);
+ if (unlikely(err)) {
+ mlx4_warn(dev, "Failed adding duplicate mac: 0x%llx\n", mac);
+ dup_table->is_dup[free] = false;
+ dup_table->entries[free] = 0;
+ goto out;
+ }
+ ++dup_table->total;
+ }
+ err = free;
out:
- mutex_unlock(&table->mutex);
+ if (need_mf_bond) {
+ if (port == 2) {
+ mutex_unlock(&table->mutex);
+ mutex_unlock(&dup_table->mutex);
+ } else {
+ mutex_unlock(&dup_table->mutex);
+ mutex_unlock(&table->mutex);
+ }
+ } else {
+ mutex_unlock(&table->mutex);
+ }
return err;
}
EXPORT_SYMBOL_GPL(__mlx4_register_mac);
@@ -255,6 +375,9 @@ void __mlx4_unregister_mac(struct mlx4_dev *dev, u8 port, u64 mac)
struct mlx4_port_info *info;
struct mlx4_mac_table *table;
int index;
+ bool dup = mlx4_is_mf_bonded(dev);
+ u8 dup_port = (port == 1) ? 2 : 1;
+ struct mlx4_mac_table *dup_table = &mlx4_priv(dev)->port[dup_port].mac_table;
if (port < 1 || port > dev->caps.num_ports) {
mlx4_warn(dev, "invalid port number (%d), aborting...\n", port);
@@ -262,22 +385,59 @@ void __mlx4_unregister_mac(struct mlx4_dev *dev, u8 port, u64 mac)
}
info = &mlx4_priv(dev)->port[port];
table = &info->mac_table;
- mutex_lock(&table->mutex);
+
+ if (dup) {
+ if (port == 1) {
+ mutex_lock(&table->mutex);
+ mutex_lock(&dup_table->mutex);
+ } else {
+ mutex_lock(&dup_table->mutex);
+ mutex_lock(&table->mutex);
+ }
+ } else {
+ mutex_lock(&table->mutex);
+ }
+
index = find_index(dev, table, mac);
if (validate_index(dev, table, index))
goto out;
- if (--table->refs[index]) {
+
+ if (--table->refs[index] || table->is_dup[index]) {
mlx4_dbg(dev, "Have more references for index %d, no need to modify mac table\n",
index);
+ if (!table->refs[index])
+ dup_table->is_dup[index] = false;
goto out;
}
table->entries[index] = 0;
- mlx4_set_port_mac_table(dev, port, table->entries);
+ if (mlx4_set_port_mac_table(dev, port, table->entries))
+ mlx4_warn(dev, "Fail to set mac in port %d during unregister\n", port);
--table->total;
+
+ if (dup) {
+ dup_table->is_dup[index] = false;
+ if (dup_table->refs[index])
+ goto out;
+ dup_table->entries[index] = 0;
+ if (mlx4_set_port_mac_table(dev, dup_port, dup_table->entries))
+ mlx4_warn(dev, "Fail to set mac in duplicate port %d during unregister\n", dup_port);
+
+ --table->total;
+ }
out:
- mutex_unlock(&table->mutex);
+ if (dup) {
+ if (port == 2) {
+ mutex_unlock(&table->mutex);
+ mutex_unlock(&dup_table->mutex);
+ } else {
+ mutex_unlock(&dup_table->mutex);
+ mutex_unlock(&table->mutex);
+ }
+ } else {
+ mutex_unlock(&table->mutex);
+ }
}
EXPORT_SYMBOL_GPL(__mlx4_unregister_mac);
@@ -311,9 +471,22 @@ int __mlx4_replace_mac(struct mlx4_dev *dev, u8 port, int qpn, u64 new_mac)
struct mlx4_mac_table *table = &info->mac_table;
int index = qpn - info->base_qpn;
int err = 0;
+ bool dup = mlx4_is_mf_bonded(dev);
+ u8 dup_port = (port == 1) ? 2 : 1;
+ struct mlx4_mac_table *dup_table = &mlx4_priv(dev)->port[dup_port].mac_table;
/* CX1 doesn't support multi-functions */
- mutex_lock(&table->mutex);
+ if (dup) {
+ if (port == 1) {
+ mutex_lock(&table->mutex);
+ mutex_lock(&dup_table->mutex);
+ } else {
+ mutex_lock(&dup_table->mutex);
+ mutex_lock(&table->mutex);
+ }
+ } else {
+ mutex_lock(&table->mutex);
+ }
err = validate_index(dev, table, index);
if (err)
@@ -326,9 +499,30 @@ int __mlx4_replace_mac(struct mlx4_dev *dev, u8 port, int qpn, u64 new_mac)
mlx4_err(dev, "Failed adding MAC: 0x%llx\n",
(unsigned long long) new_mac);
table->entries[index] = 0;
+ } else {
+ if (dup) {
+ dup_table->entries[index] = cpu_to_be64(new_mac | MLX4_MAC_VALID);
+
+ err = mlx4_set_port_mac_table(dev, dup_port, dup_table->entries);
+ if (unlikely(err)) {
+ mlx4_err(dev, "Failed adding duplicate MAC: 0x%llx\n",
+ (unsigned long long)new_mac);
+ dup_table->entries[index] = 0;
+ }
+ }
}
out:
- mutex_unlock(&table->mutex);
+ if (dup) {
+ if (port == 2) {
+ mutex_unlock(&table->mutex);
+ mutex_unlock(&dup_table->mutex);
+ } else {
+ mutex_unlock(&dup_table->mutex);
+ mutex_unlock(&table->mutex);
+ }
+ } else {
+ mutex_unlock(&table->mutex);
+ }
return err;
}
EXPORT_SYMBOL_GPL(__mlx4_replace_mac);
@@ -380,8 +574,28 @@ int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan,
struct mlx4_vlan_table *table = &mlx4_priv(dev)->port[port].vlan_table;
int i, err = 0;
int free = -1;
-
- mutex_lock(&table->mutex);
+ int free_for_dup = -1;
+ bool dup = mlx4_is_mf_bonded(dev);
+ u8 dup_port = (port == 1) ? 2 : 1;
+ struct mlx4_vlan_table *dup_table = &mlx4_priv(dev)->port[dup_port].vlan_table;
+ bool need_mf_bond = mlx4_need_mf_bond(dev);
+ bool can_mf_bond = true;
+
+ mlx4_dbg(dev, "Registering VLAN: %d for port %d %s duplicate\n",
+ vlan, port,
+ dup ? "with" : "without");
+
+ if (need_mf_bond) {
+ if (port == 1) {
+ mutex_lock(&table->mutex);
+ mutex_lock(&dup_table->mutex);
+ } else {
+ mutex_lock(&dup_table->mutex);
+ mutex_lock(&table->mutex);
+ }
+ } else {
+ mutex_lock(&table->mutex);
+ }
if (table->total == table->max) {
/* No free vlan entries */
@@ -389,22 +603,85 @@ int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan,
goto out;
}
+ if (need_mf_bond) {
+ int index_at_port = -1;
+ int index_at_dup_port = -1;
+
+ for (i = MLX4_VLAN_REGULAR; i < MLX4_MAX_VLAN_NUM; i++) {
+ if ((vlan == (MLX4_VLAN_MASK & be32_to_cpu(table->entries[i]))))
+ index_at_port = i;
+ if ((vlan == (MLX4_VLAN_MASK & be32_to_cpu(dup_table->entries[i]))))
+ index_at_dup_port = i;
+ }
+ /* check that same vlan is not in the tables at different indices */
+ if ((index_at_port != index_at_dup_port) &&
+ (index_at_port >= 0) &&
+ (index_at_dup_port >= 0))
+ can_mf_bond = false;
+
+ /* If the vlan is already in the primary table, the slot must be
+ * available in the duplicate table as well.
+ */
+ if (index_at_port >= 0 && index_at_dup_port < 0 &&
+ dup_table->refs[index_at_port]) {
+ can_mf_bond = false;
+ }
+ /* If the vlan is already in the duplicate table, check that the
+ * corresponding index is not occupied in the primary table, or
+ * the primary table already contains the vlan at the same index.
+ * Otherwise, you cannot bond (primary contains a different vlan
+ * at that index).
+ */
+ if (index_at_dup_port >= 0) {
+ if (!table->refs[index_at_dup_port] ||
+ (vlan == (MLX4_VLAN_MASK & be32_to_cpu(dup_table->entries[index_at_dup_port]))))
+ free_for_dup = index_at_dup_port;
+ else
+ can_mf_bond = false;
+ }
+ }
+
for (i = MLX4_VLAN_REGULAR; i < MLX4_MAX_VLAN_NUM; i++) {
- if (free < 0 && (table->refs[i] == 0)) {
- free = i;
- continue;
+ if (!table->refs[i]) {
+ if (free < 0)
+ free = i;
+ if (free_for_dup < 0 && need_mf_bond && can_mf_bond) {
+ if (!dup_table->refs[i])
+ free_for_dup = i;
+ }
}
- if (table->refs[i] &&
+ if ((table->refs[i] || table->is_dup[i]) &&
(vlan == (MLX4_VLAN_MASK &
be32_to_cpu(table->entries[i])))) {
/* Vlan already registered, increase references count */
+ mlx4_dbg(dev, "vlan %u is already registered.\n", vlan);
*index = i;
++table->refs[i];
+ if (dup) {
+ u16 dup_vlan = MLX4_VLAN_MASK & be32_to_cpu(dup_table->entries[i]);
+
+ if (dup_vlan != vlan || !dup_table->is_dup[i]) {
+ mlx4_warn(dev, "register vlan: expected duplicate vlan %u on port %d index %d\n",
+ vlan, dup_port, i);
+ }
+ }
goto out;
}
}
+ if (need_mf_bond && (free_for_dup < 0)) {
+ if (dup) {
+ mlx4_warn(dev, "Fail to allocate duplicate VLAN table entry\n");
+ mlx4_warn(dev, "High Availability for virtual functions may not work as expected\n");
+ dup = false;
+ }
+ can_mf_bond = false;
+ }
+
+ if (need_mf_bond && can_mf_bond)
+ free = free_for_dup;
+
if (free < 0) {
err = -ENOMEM;
goto out;
@@ -412,6 +689,7 @@ int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan,
/* Register new VLAN */
table->refs[free] = 1;
+ table->is_dup[free] = false;
table->entries[free] = cpu_to_be32(vlan | MLX4_VLAN_VALID);
err = mlx4_set_port_vlan_table(dev, port, table->entries);
@@ -421,11 +699,35 @@ int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan,
table->entries[free] = 0;
goto out;
}
+ ++table->total;
+ if (dup) {
+ dup_table->refs[free] = 0;
+ dup_table->is_dup[free] = true;
+ dup_table->entries[free] = cpu_to_be32(vlan | MLX4_VLAN_VALID);
+
+ err = mlx4_set_port_vlan_table(dev, dup_port, dup_table->entries);
+ if (unlikely(err)) {
+ mlx4_warn(dev, "Failed adding duplicate vlan: %u\n", vlan);
+ dup_table->is_dup[free] = false;
+ dup_table->entries[free] = 0;
+ goto out;
+ }
+ ++dup_table->total;
+ }
*index = free;
- ++table->total;
out:
- mutex_unlock(&table->mutex);
+ if (need_mf_bond) {
+ if (port == 2) {
+ mutex_unlock(&table->mutex);
+ mutex_unlock(&dup_table->mutex);
+ } else {
+ mutex_unlock(&dup_table->mutex);
+ mutex_unlock(&table->mutex);
+ }
+ } else {
+ mutex_unlock(&table->mutex);
+ }
return err;
}
@@ -455,8 +757,22 @@ void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan)
{
struct mlx4_vlan_table *table = &mlx4_priv(dev)->port[port].vlan_table;
int index;
+ bool dup = mlx4_is_mf_bonded(dev);
+ u8 dup_port = (port == 1) ? 2 : 1;
+ struct mlx4_vlan_table *dup_table = &mlx4_priv(dev)->port[dup_port].vlan_table;
+
+ if (dup) {
+ if (port == 1) {
+ mutex_lock(&table->mutex);
+ mutex_lock(&dup_table->mutex);
+ } else {
+ mutex_lock(&dup_table->mutex);
+ mutex_lock(&table->mutex);
+ }
+ } else {
+ mutex_lock(&table->mutex);
+ }
- mutex_lock(&table->mutex);
if (mlx4_find_cached_vlan(dev, port, vlan, &index)) {
mlx4_warn(dev, "vlan 0x%x is not in the vlan table\n", vlan);
goto out;
@@ -467,16 +783,38 @@ void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan)
goto out;
}
- if (--table->refs[index]) {
+ if (--table->refs[index] || table->is_dup[index]) {
mlx4_dbg(dev, "Have %d more references for index %d, no need to modify vlan table\n",
table->refs[index], index);
+ if (!table->refs[index])
+ dup_table->is_dup[index] = false;
goto out;
}
table->entries[index] = 0;
- mlx4_set_port_vlan_table(dev, port, table->entries);
+ if (mlx4_set_port_vlan_table(dev, port, table->entries))
+ mlx4_warn(dev, "Fail to set vlan in port %d during unregister\n", port);
--table->total;
+ if (dup) {
+ dup_table->is_dup[index] = false;
+ if (dup_table->refs[index])
+ goto out;
+ dup_table->entries[index] = 0;
+ if (mlx4_set_port_vlan_table(dev, dup_port, dup_table->entries))
+ mlx4_warn(dev, "Fail to set vlan in duplicate port %d during unregister\n", dup_port);
+ --dup_table->total;
+ }
out:
- mutex_unlock(&table->mutex);
+ if (dup) {
+ if (port == 2) {
+ mutex_unlock(&table->mutex);
+ mutex_unlock(&dup_table->mutex);
+ } else {
+ mutex_unlock(&dup_table->mutex);
+ mutex_unlock(&table->mutex);
+ }
+ } else {
+ mutex_unlock(&table->mutex);
+ }
}
void mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan)
@@ -495,6 +833,220 @@ void mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan)
}
EXPORT_SYMBOL_GPL(mlx4_unregister_vlan);
+int mlx4_bond_mac_table(struct mlx4_dev *dev)
+{
+ struct mlx4_mac_table *t1 = &mlx4_priv(dev)->port[1].mac_table;
+ struct mlx4_mac_table *t2 = &mlx4_priv(dev)->port[2].mac_table;
+ int ret = 0;
+ int i;
+ bool update1 = false;
+ bool update2 = false;
+
+ mutex_lock(&t1->mutex);
+ mutex_lock(&t2->mutex);
+ for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
+ if ((t1->entries[i] != t2->entries[i]) &&
+ t1->entries[i] && t2->entries[i]) {
+ mlx4_warn(dev, "can't duplicate entry %d in mac table\n", i);
+ ret = -EINVAL;
+ goto unlock;
+ }
+ }
+
+ for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
+ if (t1->entries[i] && !t2->entries[i]) {
+ t2->entries[i] = t1->entries[i];
+ t2->is_dup[i] = true;
+ update2 = true;
+ } else if (!t1->entries[i] && t2->entries[i]) {
+ t1->entries[i] = t2->entries[i];
+ t1->is_dup[i] = true;
+ update1 = true;
+ } else if (t1->entries[i] && t2->entries[i]) {
+ t1->is_dup[i] = true;
+ t2->is_dup[i] = true;
+ }
+ }
+
+ if (update1) {
+ ret = mlx4_set_port_mac_table(dev, 1, t1->entries);
+ if (ret)
+ mlx4_warn(dev, "failed to set MAC table for port 1 (%d)\n", ret);
+ }
+ if (!ret && update2) {
+ ret = mlx4_set_port_mac_table(dev, 2, t2->entries);
+ if (ret)
+ mlx4_warn(dev, "failed to set MAC table for port 2 (%d)\n", ret);
+ }
+
+ if (ret)
+ mlx4_warn(dev, "failed to create mirror MAC tables\n");
+unlock:
+ mutex_unlock(&t2->mutex);
+ mutex_unlock(&t1->mutex);
+ return ret;
+}
+
+int mlx4_unbond_mac_table(struct mlx4_dev *dev)
+{
+ struct mlx4_mac_table *t1 = &mlx4_priv(dev)->port[1].mac_table;
+ struct mlx4_mac_table *t2 = &mlx4_priv(dev)->port[2].mac_table;
+ int ret = 0;
+ int ret1;
+ int i;
+ bool update1 = false;
+ bool update2 = false;
+
+ mutex_lock(&t1->mutex);
+ mutex_lock(&t2->mutex);
+ for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
+ if (t1->entries[i] != t2->entries[i]) {
+ mlx4_warn(dev, "mac table is in an unexpected state when trying to unbond\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+ }
+
+ for (i = 0; i < MLX4_MAX_MAC_NUM; i++) {
+ if (!t1->entries[i])
+ continue;
+ t1->is_dup[i] = false;
+ if (!t1->refs[i]) {
+ t1->entries[i] = 0;
+ update1 = true;
+ }
+ t2->is_dup[i] = false;
+ if (!t2->refs[i]) {
+ t2->entries[i] = 0;
+ update2 = true;
+ }
+ }
+
+ if (update1) {
+ ret = mlx4_set_port_mac_table(dev, 1, t1->entries);
+ if (ret)
+ mlx4_warn(dev, "failed to unmirror MAC tables for port 1(%d)\n", ret);
+ }
+ if (update2) {
+ ret1 = mlx4_set_port_mac_table(dev, 2, t2->entries);
+ if (ret1) {
+ mlx4_warn(dev, "failed to unmirror MAC tables for port 2(%d)\n", ret1);
+ ret = ret1;
+ }
+ }
+unlock:
+ mutex_unlock(&t2->mutex);
+ mutex_unlock(&t1->mutex);
+ return ret;
+}
+
+int mlx4_bond_vlan_table(struct mlx4_dev *dev)
+{
+ struct mlx4_vlan_table *t1 = &mlx4_priv(dev)->port[1].vlan_table;
+ struct mlx4_vlan_table *t2 = &mlx4_priv(dev)->port[2].vlan_table;
+ int ret = 0;
+ int i;
+ bool update1 = false;
+ bool update2 = false;
+
+ mutex_lock(&t1->mutex);
+ mutex_lock(&t2->mutex);
+ for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) {
+ if ((t1->entries[i] != t2->entries[i]) &&
+ t1->entries[i] && t2->entries[i]) {
+ mlx4_warn(dev, "can't duplicate entry %d in vlan table\n", i);
+ ret = -EINVAL;
+ goto unlock;
+ }
+ }
+
+ for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) {
+ if (t1->entries[i] && !t2->entries[i]) {
+ t2->entries[i] = t1->entries[i];
+ t2->is_dup[i] = true;
+ update2 = true;
+ } else if (!t1->entries[i] && t2->entries[i]) {
+ t1->entries[i] = t2->entries[i];
+ t1->is_dup[i] = true;
+ update1 = true;
+ } else if (t1->entries[i] && t2->entries[i]) {
+ t1->is_dup[i] = true;
+ t2->is_dup[i] = true;
+ }
+ }
+
+ if (update1) {
+ ret = mlx4_set_port_vlan_table(dev, 1, t1->entries);
+ if (ret)
+ mlx4_warn(dev, "failed to set VLAN table for port 1 (%d)\n", ret);
+ }
+ if (!ret && update2) {
+ ret = mlx4_set_port_vlan_table(dev, 2, t2->entries);
+ if (ret)
+ mlx4_warn(dev, "failed to set VLAN table for port 2 (%d)\n", ret);
+ }
+
+ if (ret)
+ mlx4_warn(dev, "failed to create mirror VLAN tables\n");
+unlock:
+ mutex_unlock(&t2->mutex);
+ mutex_unlock(&t1->mutex);
+ return ret;
+}
+
+int mlx4_unbond_vlan_table(struct mlx4_dev *dev)
+{
+ struct mlx4_vlan_table *t1 = &mlx4_priv(dev)->port[1].vlan_table;
+ struct mlx4_vlan_table *t2 = &mlx4_priv(dev)->port[2].vlan_table;
+ int ret = 0;
+ int ret1;
+ int i;
+ bool update1 = false;
+ bool update2 = false;
+
+ mutex_lock(&t1->mutex);
+ mutex_lock(&t2->mutex);
+ for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) {
+ if (t1->entries[i] != t2->entries[i]) {
+ mlx4_warn(dev, "vlan table is in an unexpected state when trying to unbond\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+ }
+
+ for (i = 0; i < MLX4_MAX_VLAN_NUM; i++) {
+ if (!t1->entries[i])
+ continue;
+ t1->is_dup[i] = false;
+ if (!t1->refs[i]) {
+ t1->entries[i] = 0;
+ update1 = true;
+ }
+ t2->is_dup[i] = false;
+ if (!t2->refs[i]) {
+ t2->entries[i] = 0;
+ update2 = true;
+ }
+ }
+
+ if (update1) {
+ ret = mlx4_set_port_vlan_table(dev, 1, t1->entries);
+ if (ret)
+ mlx4_warn(dev, "failed to unmirror VLAN tables for port 1(%d)\n", ret);
+ }
+ if (update2) {
+ ret1 = mlx4_set_port_vlan_table(dev, 2, t2->entries);
+ if (ret1) {
+ mlx4_warn(dev, "failed to unmirror VLAN tables for port 2(%d)\n", ret1);
+ ret = ret1;
+ }
+ }
+unlock:
+ mutex_unlock(&t2->mutex);
+ mutex_unlock(&t1->mutex);
+ return ret;
+}
+
int mlx4_get_port_ib_caps(struct mlx4_dev *dev, u8 port, __be32 *caps)
{
struct mlx4_cmd_mailbox *inmailbox, *outmailbox;
diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
index 6fec3e993d02..b46dbe29ef6c 100644
--- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
+++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
@@ -222,6 +222,13 @@ enum res_fs_rule_states {
struct res_fs_rule {
struct res_common com;
int qpn;
+ /* VF DMFS mbox with port flipped */
+ void *mirr_mbox;
+ /* > 0 --> apply mirror when getting into HA mode */
+ /* = 0 --> un-apply mirror when getting out of HA mode */
+ u32 mirr_mbox_size;
+ struct list_head mirr_list;
+ u64 mirr_rule_id;
};
static void *res_tracker_lookup(struct rb_root *root, u64 res_id)
@@ -4284,6 +4291,22 @@ err_mac:
return err;
}
+static u32 qp_attach_mbox_size(void *mbox)
+{
+ u32 size = sizeof(struct mlx4_net_trans_rule_hw_ctrl);
+ struct _rule_hw *rule_header;
+
+ rule_header = (struct _rule_hw *)(mbox + size);
+
+ while (rule_header->size) {
+ size += rule_header->size * sizeof(u32);
+ rule_header += 1;
+ }
+ return size;
+}
+
+static int mlx4_do_mirror_rule(struct mlx4_dev *dev, struct res_fs_rule *fs_rule);
+
int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_vhcr *vhcr,
struct mlx4_cmd_mailbox *inbox,
@@ -4300,15 +4323,18 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_net_trans_rule_hw_ctrl *ctrl;
struct _rule_hw *rule_header;
int header_id;
+ struct res_fs_rule *rrule;
+ u32 mbox_size;
if (dev->caps.steering_mode !=
MLX4_STEERING_MODE_DEVICE_MANAGED)
return -EOPNOTSUPP;
ctrl = (struct mlx4_net_trans_rule_hw_ctrl *)inbox->buf;
- ctrl->port = mlx4_slave_convert_port(dev, slave, ctrl->port);
- if (ctrl->port <= 0)
+ err = mlx4_slave_convert_port(dev, slave, ctrl->port);
+ if (err <= 0)
return -EINVAL;
+ ctrl->port = err;
qpn = be32_to_cpu(ctrl->qpn) & 0xffffff;
err = get_res(dev, slave, qpn, RES_QP, &rqp);
if (err) {
@@ -4328,7 +4354,7 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
case MLX4_NET_TRANS_RULE_ID_ETH:
if (validate_eth_header_mac(slave, rule_header, rlist)) {
err = -EINVAL;
- goto err_put;
+ goto err_put_qp;
}
break;
case MLX4_NET_TRANS_RULE_ID_IB:
@@ -4339,7 +4365,7 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
pr_warn("Can't attach FS rule without L2 headers, adding L2 header\n");
if (add_eth_header(dev, slave, inbox, rlist, header_id)) {
err = -EINVAL;
- goto err_put;
+ goto err_put_qp;
}
vhcr->in_modifier +=
sizeof(struct mlx4_net_trans_rule_hw_eth) >> 2;
@@ -4347,7 +4373,7 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
default:
pr_err("Corrupted mailbox\n");
err = -EINVAL;
- goto err_put;
+ goto err_put_qp;
}
execute:
@@ -4356,23 +4382,69 @@ execute:
MLX4_QP_FLOW_STEERING_ATTACH, MLX4_CMD_TIME_CLASS_A,
MLX4_CMD_NATIVE);
if (err)
- goto err_put;
+ goto err_put_qp;
+
err = add_res_range(dev, slave, vhcr->out_param, 1, RES_FS_RULE, qpn);
if (err) {
mlx4_err(dev, "Fail to add flow steering resources\n");
- /* detach rule*/
+ goto err_detach;
+ }
+
+ err = get_res(dev, slave, vhcr->out_param, RES_FS_RULE, &rrule);
+ if (err)
+ goto err_detach;
+
+ mbox_size = qp_attach_mbox_size(inbox->buf);
+ rrule->mirr_mbox = kmalloc(mbox_size, GFP_KERNEL);
+ if (!rrule->mirr_mbox) {
+ err = -ENOMEM;
+ goto err_put_rule;
+ }
+ rrule->mirr_mbox_size = mbox_size;
+ rrule->mirr_rule_id = 0;
+ memcpy(rrule->mirr_mbox, inbox->buf, mbox_size);
+
+ /* set different port */
+ ctrl = (struct mlx4_net_trans_rule_hw_ctrl *)rrule->mirr_mbox;
+ if (ctrl->port == 1)
+ ctrl->port = 2;
+ else
+ ctrl->port = 1;
+
+ if (mlx4_is_bonded(dev))
+ mlx4_do_mirror_rule(dev, rrule);
+
+ atomic_inc(&rqp->ref_count);
+
+err_put_rule:
+ put_res(dev, slave, vhcr->out_param, RES_FS_RULE);
+err_detach:
+ /* detach rule on error */
+ if (err)
mlx4_cmd(dev, vhcr->out_param, 0, 0,
MLX4_QP_FLOW_STEERING_DETACH, MLX4_CMD_TIME_CLASS_A,
MLX4_CMD_NATIVE);
- goto err_put;
- }
- atomic_inc(&rqp->ref_count);
-err_put:
+err_put_qp:
put_res(dev, slave, qpn, RES_QP);
return err;
}
+static int mlx4_undo_mirror_rule(struct mlx4_dev *dev, struct res_fs_rule *fs_rule)
+{
+ int err;
+
+ err = rem_res_range(dev, fs_rule->com.owner, fs_rule->com.res_id, 1, RES_FS_RULE, 0);
+ if (err) {
+ mlx4_err(dev, "Fail to remove flow steering resources\n");
+ return err;
+ }
+
+ mlx4_cmd(dev, fs_rule->com.res_id, 0, 0, MLX4_QP_FLOW_STEERING_DETACH,
+ MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE);
+ return 0;
+}
+
int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_vhcr *vhcr,
struct mlx4_cmd_mailbox *inbox,
@@ -4382,6 +4454,7 @@ int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave,
int err;
struct res_qp *rqp;
struct res_fs_rule *rrule;
+ u64 mirr_reg_id;
if (dev->caps.steering_mode !=
MLX4_STEERING_MODE_DEVICE_MANAGED)
@@ -4390,12 +4463,30 @@ int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave,
err = get_res(dev, slave, vhcr->in_param, RES_FS_RULE, &rrule);
if (err)
return err;
+
+ if (!rrule->mirr_mbox) {
+ mlx4_err(dev, "Mirror rules cannot be removed explicitly\n");
+ put_res(dev, slave, vhcr->in_param, RES_FS_RULE);
+ return -EINVAL;
+ }
+ mirr_reg_id = rrule->mirr_rule_id;
+ kfree(rrule->mirr_mbox);
+
/* Release the rule form busy state before removal */
put_res(dev, slave, vhcr->in_param, RES_FS_RULE);
err = get_res(dev, slave, rrule->qpn, RES_QP, &rqp);
if (err)
return err;
+ if (mirr_reg_id && mlx4_is_bonded(dev)) {
+ err = get_res(dev, slave, mirr_reg_id, RES_FS_RULE, &rrule);
+ if (err) {
+ mlx4_err(dev, "Fail to get resource of mirror rule\n");
+ } else {
+ put_res(dev, slave, mirr_reg_id, RES_FS_RULE);
+ mlx4_undo_mirror_rule(dev, rrule);
+ }
+ }
err = rem_res_range(dev, slave, vhcr->in_param, 1, RES_FS_RULE, 0);
if (err) {
mlx4_err(dev, "Fail to remove flow steering resources\n");
@@ -4833,6 +4924,91 @@ static void rem_slave_mtts(struct mlx4_dev *dev, int slave)
spin_unlock_irq(mlx4_tlock(dev));
}
+static int mlx4_do_mirror_rule(struct mlx4_dev *dev, struct res_fs_rule *fs_rule)
+{
+ struct mlx4_cmd_mailbox *mailbox;
+ int err;
+ struct res_fs_rule *mirr_rule;
+ u64 reg_id;
+
+ mailbox = mlx4_alloc_cmd_mailbox(dev);
+ if (IS_ERR(mailbox))
+ return PTR_ERR(mailbox);
+
+ if (!fs_rule->mirr_mbox) {
+ mlx4_err(dev, "rule mirroring mailbox is null\n");
+ return -EINVAL;
+ }
+ memcpy(mailbox->buf, fs_rule->mirr_mbox, fs_rule->mirr_mbox_size);
+ err = mlx4_cmd_imm(dev, mailbox->dma, &reg_id, fs_rule->mirr_mbox_size >> 2, 0,
+ MLX4_QP_FLOW_STEERING_ATTACH, MLX4_CMD_TIME_CLASS_A,
+ MLX4_CMD_NATIVE);
+ mlx4_free_cmd_mailbox(dev, mailbox);
+
+ if (err)
+ goto err;
+
+ err = add_res_range(dev, fs_rule->com.owner, reg_id, 1, RES_FS_RULE, fs_rule->qpn);
+ if (err)
+ goto err_detach;
+
+ err = get_res(dev, fs_rule->com.owner, reg_id, RES_FS_RULE, &mirr_rule);
+ if (err)
+ goto err_rem;
+
+ fs_rule->mirr_rule_id = reg_id;
+ mirr_rule->mirr_rule_id = 0;
+ mirr_rule->mirr_mbox_size = 0;
+ mirr_rule->mirr_mbox = NULL;
+ put_res(dev, fs_rule->com.owner, reg_id, RES_FS_RULE);
+
+ return 0;
+err_rem:
+ rem_res_range(dev, fs_rule->com.owner, reg_id, 1, RES_FS_RULE, 0);
+err_detach:
+ mlx4_cmd(dev, reg_id, 0, 0, MLX4_QP_FLOW_STEERING_DETACH,
+ MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE);
+err:
+ return err;
+}
+
+static int mlx4_mirror_fs_rules(struct mlx4_dev *dev, bool bond)
+{
+ struct mlx4_priv *priv = mlx4_priv(dev);
+ struct mlx4_resource_tracker *tracker =
+ &priv->mfunc.master.res_tracker;
+ struct rb_root *root = &tracker->res_tree[RES_FS_RULE];
+ struct rb_node *p;
+ struct res_fs_rule *fs_rule;
+ int err = 0;
+ LIST_HEAD(mirr_list);
+
+ for (p = rb_first(root); p; p = rb_next(p)) {
+ fs_rule = rb_entry(p, struct res_fs_rule, com.node);
+ if ((bond && fs_rule->mirr_mbox_size) ||
+ (!bond && !fs_rule->mirr_mbox_size))
+ list_add_tail(&fs_rule->mirr_list, &mirr_list);
+ }
+
+ list_for_each_entry(fs_rule, &mirr_list, mirr_list) {
+ if (bond)
+ err += mlx4_do_mirror_rule(dev, fs_rule);
+ else
+ err += mlx4_undo_mirror_rule(dev, fs_rule);
+ }
+ return err;
+}
+
+int mlx4_bond_fs_rules(struct mlx4_dev *dev)
+{
+ return mlx4_mirror_fs_rules(dev, true);
+}
+
+int mlx4_unbond_fs_rules(struct mlx4_dev *dev)
+{
+ return mlx4_mirror_fs_rules(dev, false);
+}
+
static void rem_slave_fs_rule(struct mlx4_dev *dev, int slave)
{
struct mlx4_priv *priv = mlx4_priv(dev);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
index 158c88c69ef9..c503ea05e742 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
+++ b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
@@ -13,6 +13,7 @@ config MLX5_CORE
config MLX5_CORE_EN
bool "Mellanox Technologies ConnectX-4 Ethernet support"
depends on NETDEVICES && ETHERNET && PCI && MLX5_CORE
+ select PTP_1588_CLOCK
default n
---help---
Ethernet support in Mellanox Technologies ConnectX-4 NIC.
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile
index 26a68b8af2c5..01c0256effb8 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile
+++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile
@@ -2,7 +2,7 @@ obj-$(CONFIG_MLX5_CORE) += mlx5_core.o
mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \
health.o mcg.o cq.o srq.o alloc.o qp.o port.o mr.o pd.o \
- mad.o transobj.o vport.o
-mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o flow_table.o \
- en_main.o en_flow_table.o en_ethtool.o en_tx.o en_rx.o \
- en_txrx.o
+ mad.o transobj.o vport.o sriov.o fs_cmd.o fs_core.o
+mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o eswitch.o \
+ en_main.o en_fs.o en_ethtool.o en_tx.o en_rx.o \
+ en_txrx.o en_clock.o
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h
index 22e72bf1ae48..9ea49a893323 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h
@@ -32,6 +32,9 @@
#include <linux/if_vlan.h>
#include <linux/etherdevice.h>
+#include <linux/timecounter.h>
+#include <linux/net_tstamp.h>
+#include <linux/ptp_clock_kernel.h>
#include <linux/mlx5/driver.h>
#include <linux/mlx5/qp.h>
#include <linux/mlx5/cq.h>
@@ -64,6 +67,8 @@
#define MLX5E_UPDATE_STATS_INTERVAL 200 /* msecs */
#define MLX5E_SQ_BF_BUDGET 16
+#define MLX5E_NUM_MAIN_GROUPS 9
+
static const char vport_strings[][ETH_GSTRING_LEN] = {
/* vport statistics */
"rx_packets",
@@ -282,6 +287,19 @@ struct mlx5e_params {
u32 indirection_rqt[MLX5E_INDIR_RQT_SIZE];
};
+struct mlx5e_tstamp {
+ rwlock_t lock;
+ struct cyclecounter cycles;
+ struct timecounter clock;
+ struct hwtstamp_config hwtstamp_config;
+ u32 nominal_c_mult;
+ unsigned long overflow_period;
+ struct delayed_work overflow_work;
+ struct mlx5_core_dev *mdev;
+ struct ptp_clock *ptp;
+ struct ptp_clock_info ptp_info;
+};
+
enum {
MLX5E_RQ_STATE_POST_WQES_ENABLE,
};
@@ -313,6 +331,7 @@ struct mlx5e_rq {
struct device *pdev;
struct net_device *netdev;
+ struct mlx5e_tstamp *tstamp;
struct mlx5e_rq_stats stats;
struct mlx5e_cq cq;
@@ -326,14 +345,12 @@ struct mlx5e_rq {
struct mlx5e_priv *priv;
} ____cacheline_aligned_in_smp;
-struct mlx5e_tx_skb_cb {
+struct mlx5e_tx_wqe_info {
u32 num_bytes;
u8 num_wqebbs;
u8 num_dma;
};
-#define MLX5E_TX_SKB_CB(__skb) ((struct mlx5e_tx_skb_cb *)__skb->cb)
-
enum mlx5e_dma_map_type {
MLX5E_DMA_MAP_SINGLE,
MLX5E_DMA_MAP_PAGE
@@ -369,6 +386,7 @@ struct mlx5e_sq {
/* pointers to per packet info: write@xmit, read@completion */
struct sk_buff **skb;
struct mlx5e_sq_dma *dma_fifo;
+ struct mlx5e_tx_wqe_info *wqe_info;
/* read only */
struct mlx5_wq_cyc wq;
@@ -381,6 +399,7 @@ struct mlx5e_sq {
u16 max_inline;
u16 edge;
struct device *pdev;
+ struct mlx5e_tstamp *tstamp;
__be32 mkey_be;
unsigned long state;
@@ -442,7 +461,7 @@ enum mlx5e_rqt_ix {
struct mlx5e_eth_addr_info {
u8 addr[ETH_ALEN + 2];
u32 tt_vec;
- u32 ft_ix[MLX5E_NUM_TT]; /* flow table index per traffic type */
+ struct mlx5_flow_rule *ft_rule[MLX5E_NUM_TT];
};
#define MLX5E_ETH_ADDR_HASH_SIZE (1 << BITS_PER_BYTE)
@@ -465,15 +484,23 @@ enum {
};
struct mlx5e_vlan_db {
- u32 active_vlans_ft_ix[VLAN_N_VID];
- u32 untagged_rule_ft_ix;
- u32 any_vlan_rule_ft_ix;
+ unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
+ struct mlx5_flow_rule *active_vlans_rule[VLAN_N_VID];
+ struct mlx5_flow_rule *untagged_rule;
+ struct mlx5_flow_rule *any_vlan_rule;
bool filter_disabled;
};
struct mlx5e_flow_table {
- void *vlan;
- void *main;
+ int num_groups;
+ struct mlx5_flow_table *t;
+ struct mlx5_flow_group **g;
+};
+
+struct mlx5e_flow_tables {
+ struct mlx5_flow_namespace *ns;
+ struct mlx5e_flow_table vlan;
+ struct mlx5e_flow_table main;
};
struct mlx5e_priv {
@@ -496,7 +523,7 @@ struct mlx5e_priv {
u32 rqtn[MLX5E_NUM_RQT];
u32 tirn[MLX5E_NUM_TT];
- struct mlx5e_flow_table ft;
+ struct mlx5e_flow_tables fts;
struct mlx5e_eth_addr_db eth_addr;
struct mlx5e_vlan_db vlan;
@@ -509,6 +536,7 @@ struct mlx5e_priv {
struct mlx5_core_dev *mdev;
struct net_device *netdev;
struct mlx5e_stats stats;
+ struct mlx5e_tstamp tstamp;
};
#define MLX5E_NET_IP_ALIGN 2
@@ -564,7 +592,7 @@ void mlx5e_completion_event(struct mlx5_core_cq *mcq);
void mlx5e_cq_error_event(struct mlx5_core_cq *mcq, enum mlx5_event event);
int mlx5e_napi_poll(struct napi_struct *napi, int budget);
bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq);
-bool mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget);
+int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget);
bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq);
struct mlx5_cqe64 *mlx5e_get_cqe(struct mlx5e_cq *cq);
@@ -575,6 +603,13 @@ void mlx5e_destroy_flow_tables(struct mlx5e_priv *priv);
void mlx5e_init_eth_addr(struct mlx5e_priv *priv);
void mlx5e_set_rx_mode_work(struct work_struct *work);
+void mlx5e_fill_hwstamp(struct mlx5e_tstamp *clock, u64 timestamp,
+ struct skb_shared_hwtstamps *hwts);
+void mlx5e_timestamp_init(struct mlx5e_priv *priv);
+void mlx5e_timestamp_cleanup(struct mlx5e_priv *priv);
+int mlx5e_hwstamp_set(struct net_device *dev, struct ifreq *ifr);
+int mlx5e_hwstamp_get(struct net_device *dev, struct ifreq *ifr);
+
int mlx5e_vlan_rx_add_vid(struct net_device *dev, __always_unused __be16 proto,
u16 vid);
int mlx5e_vlan_rx_kill_vid(struct net_device *dev, __always_unused __be16 proto,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c
new file mode 100644
index 000000000000..be6543570b2b
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2015, Mellanox Technologies. 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.
+ */
+
+#include <linux/clocksource.h>
+#include "en.h"
+
+enum {
+ MLX5E_CYCLES_SHIFT = 23
+};
+
+void mlx5e_fill_hwstamp(struct mlx5e_tstamp *tstamp, u64 timestamp,
+ struct skb_shared_hwtstamps *hwts)
+{
+ u64 nsec;
+
+ read_lock(&tstamp->lock);
+ nsec = timecounter_cyc2time(&tstamp->clock, timestamp);
+ read_unlock(&tstamp->lock);
+
+ hwts->hwtstamp = ns_to_ktime(nsec);
+}
+
+static cycle_t mlx5e_read_internal_timer(const struct cyclecounter *cc)
+{
+ struct mlx5e_tstamp *tstamp = container_of(cc, struct mlx5e_tstamp,
+ cycles);
+
+ return mlx5_read_internal_timer(tstamp->mdev) & cc->mask;
+}
+
+static void mlx5e_timestamp_overflow(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct mlx5e_tstamp *tstamp = container_of(dwork, struct mlx5e_tstamp,
+ overflow_work);
+
+ write_lock(&tstamp->lock);
+ timecounter_read(&tstamp->clock);
+ write_unlock(&tstamp->lock);
+ schedule_delayed_work(&tstamp->overflow_work, tstamp->overflow_period);
+}
+
+int mlx5e_hwstamp_set(struct net_device *dev, struct ifreq *ifr)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+ struct hwtstamp_config config;
+
+ if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz))
+ return -EOPNOTSUPP;
+
+ if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
+ return -EFAULT;
+
+ /* TX HW timestamp */
+ switch (config.tx_type) {
+ case HWTSTAMP_TX_OFF:
+ case HWTSTAMP_TX_ON:
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ /* RX HW timestamp */
+ switch (config.rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ break;
+ case HWTSTAMP_FILTER_ALL:
+ case HWTSTAMP_FILTER_SOME:
+ case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+ config.rx_filter = HWTSTAMP_FILTER_ALL;
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ memcpy(&priv->tstamp.hwtstamp_config, &config, sizeof(config));
+
+ return copy_to_user(ifr->ifr_data, &config,
+ sizeof(config)) ? -EFAULT : 0;
+}
+
+int mlx5e_hwstamp_get(struct net_device *dev, struct ifreq *ifr)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+ struct hwtstamp_config *cfg = &priv->tstamp.hwtstamp_config;
+
+ if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz))
+ return -EOPNOTSUPP;
+
+ return copy_to_user(ifr->ifr_data, cfg, sizeof(*cfg)) ? -EFAULT : 0;
+}
+
+static int mlx5e_ptp_settime(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
+{
+ struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp,
+ ptp_info);
+ u64 ns = timespec64_to_ns(ts);
+
+ write_lock(&tstamp->lock);
+ timecounter_init(&tstamp->clock, &tstamp->cycles, ns);
+ write_unlock(&tstamp->lock);
+
+ return 0;
+}
+
+static int mlx5e_ptp_gettime(struct ptp_clock_info *ptp,
+ struct timespec64 *ts)
+{
+ struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp,
+ ptp_info);
+ u64 ns;
+
+ write_lock(&tstamp->lock);
+ ns = timecounter_read(&tstamp->clock);
+ write_unlock(&tstamp->lock);
+
+ *ts = ns_to_timespec64(ns);
+
+ return 0;
+}
+
+static int mlx5e_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp,
+ ptp_info);
+
+ write_lock(&tstamp->lock);
+ timecounter_adjtime(&tstamp->clock, delta);
+ write_unlock(&tstamp->lock);
+
+ return 0;
+}
+
+static int mlx5e_ptp_adjfreq(struct ptp_clock_info *ptp, s32 delta)
+{
+ u64 adj;
+ u32 diff;
+ int neg_adj = 0;
+ struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp,
+ ptp_info);
+
+ if (delta < 0) {
+ neg_adj = 1;
+ delta = -delta;
+ }
+
+ adj = tstamp->nominal_c_mult;
+ adj *= delta;
+ diff = div_u64(adj, 1000000000ULL);
+
+ write_lock(&tstamp->lock);
+ timecounter_read(&tstamp->clock);
+ tstamp->cycles.mult = neg_adj ? tstamp->nominal_c_mult - diff :
+ tstamp->nominal_c_mult + diff;
+ write_unlock(&tstamp->lock);
+
+ return 0;
+}
+
+static const struct ptp_clock_info mlx5e_ptp_clock_info = {
+ .owner = THIS_MODULE,
+ .max_adj = 100000000,
+ .n_alarm = 0,
+ .n_ext_ts = 0,
+ .n_per_out = 0,
+ .n_pins = 0,
+ .pps = 0,
+ .adjfreq = mlx5e_ptp_adjfreq,
+ .adjtime = mlx5e_ptp_adjtime,
+ .gettime64 = mlx5e_ptp_gettime,
+ .settime64 = mlx5e_ptp_settime,
+ .enable = NULL,
+};
+
+static void mlx5e_timestamp_init_config(struct mlx5e_tstamp *tstamp)
+{
+ tstamp->hwtstamp_config.tx_type = HWTSTAMP_TX_OFF;
+ tstamp->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
+}
+
+void mlx5e_timestamp_init(struct mlx5e_priv *priv)
+{
+ struct mlx5e_tstamp *tstamp = &priv->tstamp;
+ u64 ns;
+ u64 frac = 0;
+ u32 dev_freq;
+
+ mlx5e_timestamp_init_config(tstamp);
+ dev_freq = MLX5_CAP_GEN(priv->mdev, device_frequency_khz);
+ if (!dev_freq) {
+ mlx5_core_warn(priv->mdev, "invalid device_frequency_khz, aborting HW clock init\n");
+ return;
+ }
+ rwlock_init(&tstamp->lock);
+ tstamp->cycles.read = mlx5e_read_internal_timer;
+ tstamp->cycles.shift = MLX5E_CYCLES_SHIFT;
+ tstamp->cycles.mult = clocksource_khz2mult(dev_freq,
+ tstamp->cycles.shift);
+ tstamp->nominal_c_mult = tstamp->cycles.mult;
+ tstamp->cycles.mask = CLOCKSOURCE_MASK(41);
+ tstamp->mdev = priv->mdev;
+
+ timecounter_init(&tstamp->clock, &tstamp->cycles,
+ ktime_to_ns(ktime_get_real()));
+
+ /* Calculate period in seconds to call the overflow watchdog - to make
+ * sure counter is checked at least once every wrap around.
+ */
+ ns = cyclecounter_cyc2ns(&tstamp->cycles, tstamp->cycles.mask,
+ frac, &frac);
+ do_div(ns, NSEC_PER_SEC / 2 / HZ);
+ tstamp->overflow_period = ns;
+
+ INIT_DELAYED_WORK(&tstamp->overflow_work, mlx5e_timestamp_overflow);
+ if (tstamp->overflow_period)
+ schedule_delayed_work(&tstamp->overflow_work, 0);
+ else
+ mlx5_core_warn(priv->mdev, "invalid overflow period, overflow_work is not scheduled\n");
+
+ /* Configure the PHC */
+ tstamp->ptp_info = mlx5e_ptp_clock_info;
+ snprintf(tstamp->ptp_info.name, 16, "mlx5 ptp");
+
+ tstamp->ptp = ptp_clock_register(&tstamp->ptp_info,
+ &priv->mdev->pdev->dev);
+ if (IS_ERR_OR_NULL(tstamp->ptp)) {
+ mlx5_core_warn(priv->mdev, "ptp_clock_register failed %ld\n",
+ PTR_ERR(tstamp->ptp));
+ tstamp->ptp = NULL;
+ }
+}
+
+void mlx5e_timestamp_cleanup(struct mlx5e_priv *priv)
+{
+ struct mlx5e_tstamp *tstamp = &priv->tstamp;
+
+ if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz))
+ return;
+
+ if (priv->tstamp.ptp) {
+ ptp_clock_unregister(priv->tstamp.ptp);
+ priv->tstamp.ptp = NULL;
+ }
+
+ cancel_delayed_work_sync(&tstamp->overflow_work);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
index 2e022e900939..65624ac65b4c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
@@ -855,6 +855,35 @@ static int mlx5e_set_pauseparam(struct net_device *netdev,
return err;
}
+static int mlx5e_get_ts_info(struct net_device *dev,
+ struct ethtool_ts_info *info)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+ int ret;
+
+ ret = ethtool_op_get_ts_info(dev, info);
+ if (ret)
+ return ret;
+
+ info->phc_index = priv->tstamp.ptp ?
+ ptp_clock_index(priv->tstamp.ptp) : -1;
+
+ if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz))
+ return 0;
+
+ info->so_timestamping |= SOF_TIMESTAMPING_TX_HARDWARE |
+ SOF_TIMESTAMPING_RX_HARDWARE |
+ SOF_TIMESTAMPING_RAW_HARDWARE;
+
+ info->tx_types = (BIT(1) << HWTSTAMP_TX_OFF) |
+ (BIT(1) << HWTSTAMP_TX_ON);
+
+ info->rx_filters = (BIT(1) << HWTSTAMP_FILTER_NONE) |
+ (BIT(1) << HWTSTAMP_FILTER_ALL);
+
+ return 0;
+}
+
const struct ethtool_ops mlx5e_ethtool_ops = {
.get_drvinfo = mlx5e_get_drvinfo,
.get_link = ethtool_op_get_link,
@@ -878,4 +907,5 @@ const struct ethtool_ops mlx5e_ethtool_ops = {
.set_tunable = mlx5e_set_tunable,
.get_pauseparam = mlx5e_get_pauseparam,
.set_pauseparam = mlx5e_set_pauseparam,
+ .get_ts_info = mlx5e_get_ts_info,
};
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c b/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c
deleted file mode 100644
index 22d603f78273..000000000000
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_flow_table.c
+++ /dev/null
@@ -1,907 +0,0 @@
-/*
- * Copyright (c) 2015, Mellanox Technologies. 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.
- */
-
-#include <linux/list.h>
-#include <linux/ip.h>
-#include <linux/ipv6.h>
-#include <linux/tcp.h>
-#include <linux/mlx5/flow_table.h>
-#include "en.h"
-
-enum {
- MLX5E_FULLMATCH = 0,
- MLX5E_ALLMULTI = 1,
- MLX5E_PROMISC = 2,
-};
-
-enum {
- MLX5E_UC = 0,
- MLX5E_MC_IPV4 = 1,
- MLX5E_MC_IPV6 = 2,
- MLX5E_MC_OTHER = 3,
-};
-
-enum {
- MLX5E_ACTION_NONE = 0,
- MLX5E_ACTION_ADD = 1,
- MLX5E_ACTION_DEL = 2,
-};
-
-struct mlx5e_eth_addr_hash_node {
- struct hlist_node hlist;
- u8 action;
- struct mlx5e_eth_addr_info ai;
-};
-
-static inline int mlx5e_hash_eth_addr(u8 *addr)
-{
- return addr[5];
-}
-
-static void mlx5e_add_eth_addr_to_hash(struct hlist_head *hash, u8 *addr)
-{
- struct mlx5e_eth_addr_hash_node *hn;
- int ix = mlx5e_hash_eth_addr(addr);
- int found = 0;
-
- hlist_for_each_entry(hn, &hash[ix], hlist)
- if (ether_addr_equal_64bits(hn->ai.addr, addr)) {
- found = 1;
- break;
- }
-
- if (found) {
- hn->action = MLX5E_ACTION_NONE;
- return;
- }
-
- hn = kzalloc(sizeof(*hn), GFP_ATOMIC);
- if (!hn)
- return;
-
- ether_addr_copy(hn->ai.addr, addr);
- hn->action = MLX5E_ACTION_ADD;
-
- hlist_add_head(&hn->hlist, &hash[ix]);
-}
-
-static void mlx5e_del_eth_addr_from_hash(struct mlx5e_eth_addr_hash_node *hn)
-{
- hlist_del(&hn->hlist);
- kfree(hn);
-}
-
-static void mlx5e_del_eth_addr_from_flow_table(struct mlx5e_priv *priv,
- struct mlx5e_eth_addr_info *ai)
-{
- void *ft = priv->ft.main;
-
- if (ai->tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_ESP))
- mlx5_del_flow_table_entry(ft,
- ai->ft_ix[MLX5E_TT_IPV6_IPSEC_ESP]);
-
- if (ai->tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_ESP))
- mlx5_del_flow_table_entry(ft,
- ai->ft_ix[MLX5E_TT_IPV4_IPSEC_ESP]);
-
- if (ai->tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_AH))
- mlx5_del_flow_table_entry(ft,
- ai->ft_ix[MLX5E_TT_IPV6_IPSEC_AH]);
-
- if (ai->tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_AH))
- mlx5_del_flow_table_entry(ft,
- ai->ft_ix[MLX5E_TT_IPV4_IPSEC_AH]);
-
- if (ai->tt_vec & BIT(MLX5E_TT_IPV6_TCP))
- mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_IPV6_TCP]);
-
- if (ai->tt_vec & BIT(MLX5E_TT_IPV4_TCP))
- mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_IPV4_TCP]);
-
- if (ai->tt_vec & BIT(MLX5E_TT_IPV6_UDP))
- mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_IPV6_UDP]);
-
- if (ai->tt_vec & BIT(MLX5E_TT_IPV4_UDP))
- mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_IPV4_UDP]);
-
- if (ai->tt_vec & BIT(MLX5E_TT_IPV6))
- mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_IPV6]);
-
- if (ai->tt_vec & BIT(MLX5E_TT_IPV4))
- mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_IPV4]);
-
- if (ai->tt_vec & BIT(MLX5E_TT_ANY))
- mlx5_del_flow_table_entry(ft, ai->ft_ix[MLX5E_TT_ANY]);
-}
-
-static int mlx5e_get_eth_addr_type(u8 *addr)
-{
- if (is_unicast_ether_addr(addr))
- return MLX5E_UC;
-
- if ((addr[0] == 0x01) &&
- (addr[1] == 0x00) &&
- (addr[2] == 0x5e) &&
- !(addr[3] & 0x80))
- return MLX5E_MC_IPV4;
-
- if ((addr[0] == 0x33) &&
- (addr[1] == 0x33))
- return MLX5E_MC_IPV6;
-
- return MLX5E_MC_OTHER;
-}
-
-static u32 mlx5e_get_tt_vec(struct mlx5e_eth_addr_info *ai, int type)
-{
- int eth_addr_type;
- u32 ret;
-
- switch (type) {
- case MLX5E_FULLMATCH:
- eth_addr_type = mlx5e_get_eth_addr_type(ai->addr);
- switch (eth_addr_type) {
- case MLX5E_UC:
- ret =
- BIT(MLX5E_TT_IPV4_TCP) |
- BIT(MLX5E_TT_IPV6_TCP) |
- BIT(MLX5E_TT_IPV4_UDP) |
- BIT(MLX5E_TT_IPV6_UDP) |
- BIT(MLX5E_TT_IPV4_IPSEC_AH) |
- BIT(MLX5E_TT_IPV6_IPSEC_AH) |
- BIT(MLX5E_TT_IPV4_IPSEC_ESP) |
- BIT(MLX5E_TT_IPV6_IPSEC_ESP) |
- BIT(MLX5E_TT_IPV4) |
- BIT(MLX5E_TT_IPV6) |
- BIT(MLX5E_TT_ANY) |
- 0;
- break;
-
- case MLX5E_MC_IPV4:
- ret =
- BIT(MLX5E_TT_IPV4_UDP) |
- BIT(MLX5E_TT_IPV4) |
- 0;
- break;
-
- case MLX5E_MC_IPV6:
- ret =
- BIT(MLX5E_TT_IPV6_UDP) |
- BIT(MLX5E_TT_IPV6) |
- 0;
- break;
-
- case MLX5E_MC_OTHER:
- ret =
- BIT(MLX5E_TT_ANY) |
- 0;
- break;
- }
-
- break;
-
- case MLX5E_ALLMULTI:
- ret =
- BIT(MLX5E_TT_IPV4_UDP) |
- BIT(MLX5E_TT_IPV6_UDP) |
- BIT(MLX5E_TT_IPV4) |
- BIT(MLX5E_TT_IPV6) |
- BIT(MLX5E_TT_ANY) |
- 0;
- break;
-
- default: /* MLX5E_PROMISC */
- ret =
- BIT(MLX5E_TT_IPV4_TCP) |
- BIT(MLX5E_TT_IPV6_TCP) |
- BIT(MLX5E_TT_IPV4_UDP) |
- BIT(MLX5E_TT_IPV6_UDP) |
- BIT(MLX5E_TT_IPV4_IPSEC_AH) |
- BIT(MLX5E_TT_IPV6_IPSEC_AH) |
- BIT(MLX5E_TT_IPV4_IPSEC_ESP) |
- BIT(MLX5E_TT_IPV6_IPSEC_ESP) |
- BIT(MLX5E_TT_IPV4) |
- BIT(MLX5E_TT_IPV6) |
- BIT(MLX5E_TT_ANY) |
- 0;
- break;
- }
-
- return ret;
-}
-
-static int __mlx5e_add_eth_addr_rule(struct mlx5e_priv *priv,
- struct mlx5e_eth_addr_info *ai, int type,
- void *flow_context, void *match_criteria)
-{
- u8 match_criteria_enable = 0;
- void *match_value;
- void *dest;
- u8 *dmac;
- u8 *match_criteria_dmac;
- void *ft = priv->ft.main;
- u32 *tirn = priv->tirn;
- u32 *ft_ix;
- u32 tt_vec;
- int err;
-
- match_value = MLX5_ADDR_OF(flow_context, flow_context, match_value);
- dmac = MLX5_ADDR_OF(fte_match_param, match_value,
- outer_headers.dmac_47_16);
- match_criteria_dmac = MLX5_ADDR_OF(fte_match_param, match_criteria,
- outer_headers.dmac_47_16);
- dest = MLX5_ADDR_OF(flow_context, flow_context, destination);
-
- MLX5_SET(flow_context, flow_context, action,
- MLX5_FLOW_CONTEXT_ACTION_FWD_DEST);
- MLX5_SET(flow_context, flow_context, destination_list_size, 1);
- MLX5_SET(dest_format_struct, dest, destination_type,
- MLX5_FLOW_CONTEXT_DEST_TYPE_TIR);
-
- switch (type) {
- case MLX5E_FULLMATCH:
- match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- memset(match_criteria_dmac, 0xff, ETH_ALEN);
- ether_addr_copy(dmac, ai->addr);
- break;
-
- case MLX5E_ALLMULTI:
- match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- match_criteria_dmac[0] = 0x01;
- dmac[0] = 0x01;
- break;
-
- case MLX5E_PROMISC:
- break;
- }
-
- tt_vec = mlx5e_get_tt_vec(ai, type);
-
- ft_ix = &ai->ft_ix[MLX5E_TT_ANY];
- if (tt_vec & BIT(MLX5E_TT_ANY)) {
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_ANY]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_ANY);
- }
-
- match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- MLX5_SET_TO_ONES(fte_match_param, match_criteria,
- outer_headers.ethertype);
-
- ft_ix = &ai->ft_ix[MLX5E_TT_IPV4];
- if (tt_vec & BIT(MLX5E_TT_IPV4)) {
- MLX5_SET(fte_match_param, match_value, outer_headers.ethertype,
- ETH_P_IP);
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_IPV4]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_IPV4);
- }
-
- ft_ix = &ai->ft_ix[MLX5E_TT_IPV6];
- if (tt_vec & BIT(MLX5E_TT_IPV6)) {
- MLX5_SET(fte_match_param, match_value, outer_headers.ethertype,
- ETH_P_IPV6);
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_IPV6]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_IPV6);
- }
-
- MLX5_SET_TO_ONES(fte_match_param, match_criteria,
- outer_headers.ip_protocol);
- MLX5_SET(fte_match_param, match_value, outer_headers.ip_protocol,
- IPPROTO_UDP);
-
- ft_ix = &ai->ft_ix[MLX5E_TT_IPV4_UDP];
- if (tt_vec & BIT(MLX5E_TT_IPV4_UDP)) {
- MLX5_SET(fte_match_param, match_value, outer_headers.ethertype,
- ETH_P_IP);
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_IPV4_UDP]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_IPV4_UDP);
- }
-
- ft_ix = &ai->ft_ix[MLX5E_TT_IPV6_UDP];
- if (tt_vec & BIT(MLX5E_TT_IPV6_UDP)) {
- MLX5_SET(fte_match_param, match_value, outer_headers.ethertype,
- ETH_P_IPV6);
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_IPV6_UDP]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_IPV6_UDP);
- }
-
- MLX5_SET(fte_match_param, match_value, outer_headers.ip_protocol,
- IPPROTO_TCP);
-
- ft_ix = &ai->ft_ix[MLX5E_TT_IPV4_TCP];
- if (tt_vec & BIT(MLX5E_TT_IPV4_TCP)) {
- MLX5_SET(fte_match_param, match_value, outer_headers.ethertype,
- ETH_P_IP);
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_IPV4_TCP]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_IPV4_TCP);
- }
-
- ft_ix = &ai->ft_ix[MLX5E_TT_IPV6_TCP];
- if (tt_vec & BIT(MLX5E_TT_IPV6_TCP)) {
- MLX5_SET(fte_match_param, match_value, outer_headers.ethertype,
- ETH_P_IPV6);
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_IPV6_TCP]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_IPV6_TCP);
- }
-
- MLX5_SET(fte_match_param, match_value, outer_headers.ip_protocol,
- IPPROTO_AH);
-
- ft_ix = &ai->ft_ix[MLX5E_TT_IPV4_IPSEC_AH];
- if (tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_AH)) {
- MLX5_SET(fte_match_param, match_value, outer_headers.ethertype,
- ETH_P_IP);
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_IPV4_IPSEC_AH]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_IPV4_IPSEC_AH);
- }
-
- ft_ix = &ai->ft_ix[MLX5E_TT_IPV6_IPSEC_AH];
- if (tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_AH)) {
- MLX5_SET(fte_match_param, match_value, outer_headers.ethertype,
- ETH_P_IPV6);
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_IPV6_IPSEC_AH]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_IPV6_IPSEC_AH);
- }
-
- MLX5_SET(fte_match_param, match_value, outer_headers.ip_protocol,
- IPPROTO_ESP);
-
- ft_ix = &ai->ft_ix[MLX5E_TT_IPV4_IPSEC_ESP];
- if (tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_ESP)) {
- MLX5_SET(fte_match_param, match_value, outer_headers.ethertype,
- ETH_P_IP);
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_IPV4_IPSEC_ESP]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_IPV4_IPSEC_ESP);
- }
-
- ft_ix = &ai->ft_ix[MLX5E_TT_IPV6_IPSEC_ESP];
- if (tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_ESP)) {
- MLX5_SET(fte_match_param, match_value, outer_headers.ethertype,
- ETH_P_IPV6);
- MLX5_SET(dest_format_struct, dest, destination_id,
- tirn[MLX5E_TT_IPV6_IPSEC_ESP]);
- err = mlx5_add_flow_table_entry(ft, match_criteria_enable,
- match_criteria, flow_context,
- ft_ix);
- if (err)
- goto err_del_ai;
-
- ai->tt_vec |= BIT(MLX5E_TT_IPV6_IPSEC_ESP);
- }
-
- return 0;
-
-err_del_ai:
- mlx5e_del_eth_addr_from_flow_table(priv, ai);
-
- return err;
-}
-
-static int mlx5e_add_eth_addr_rule(struct mlx5e_priv *priv,
- struct mlx5e_eth_addr_info *ai, int type)
-{
- u32 *flow_context;
- u32 *match_criteria;
- int err;
-
- flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context) +
- MLX5_ST_SZ_BYTES(dest_format_struct));
- match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
- if (!flow_context || !match_criteria) {
- netdev_err(priv->netdev, "%s: alloc failed\n", __func__);
- err = -ENOMEM;
- goto add_eth_addr_rule_out;
- }
-
- err = __mlx5e_add_eth_addr_rule(priv, ai, type, flow_context,
- match_criteria);
- if (err)
- netdev_err(priv->netdev, "%s: failed\n", __func__);
-
-add_eth_addr_rule_out:
- kvfree(match_criteria);
- kvfree(flow_context);
- return err;
-}
-
-enum mlx5e_vlan_rule_type {
- MLX5E_VLAN_RULE_TYPE_UNTAGGED,
- MLX5E_VLAN_RULE_TYPE_ANY_VID,
- MLX5E_VLAN_RULE_TYPE_MATCH_VID,
-};
-
-static int mlx5e_add_vlan_rule(struct mlx5e_priv *priv,
- enum mlx5e_vlan_rule_type rule_type, u16 vid)
-{
- u8 match_criteria_enable = 0;
- u32 *flow_context;
- void *match_value;
- void *dest;
- u32 *match_criteria;
- u32 *ft_ix;
- int err;
-
- flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context) +
- MLX5_ST_SZ_BYTES(dest_format_struct));
- match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
- if (!flow_context || !match_criteria) {
- netdev_err(priv->netdev, "%s: alloc failed\n", __func__);
- err = -ENOMEM;
- goto add_vlan_rule_out;
- }
- match_value = MLX5_ADDR_OF(flow_context, flow_context, match_value);
- dest = MLX5_ADDR_OF(flow_context, flow_context, destination);
-
- MLX5_SET(flow_context, flow_context, action,
- MLX5_FLOW_CONTEXT_ACTION_FWD_DEST);
- MLX5_SET(flow_context, flow_context, destination_list_size, 1);
- MLX5_SET(dest_format_struct, dest, destination_type,
- MLX5_FLOW_CONTEXT_DEST_TYPE_FLOW_TABLE);
- MLX5_SET(dest_format_struct, dest, destination_id,
- mlx5_get_flow_table_id(priv->ft.main));
-
- match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- MLX5_SET_TO_ONES(fte_match_param, match_criteria,
- outer_headers.vlan_tag);
-
- switch (rule_type) {
- case MLX5E_VLAN_RULE_TYPE_UNTAGGED:
- ft_ix = &priv->vlan.untagged_rule_ft_ix;
- break;
- case MLX5E_VLAN_RULE_TYPE_ANY_VID:
- ft_ix = &priv->vlan.any_vlan_rule_ft_ix;
- MLX5_SET(fte_match_param, match_value, outer_headers.vlan_tag,
- 1);
- break;
- default: /* MLX5E_VLAN_RULE_TYPE_MATCH_VID */
- ft_ix = &priv->vlan.active_vlans_ft_ix[vid];
- MLX5_SET(fte_match_param, match_value, outer_headers.vlan_tag,
- 1);
- MLX5_SET_TO_ONES(fte_match_param, match_criteria,
- outer_headers.first_vid);
- MLX5_SET(fte_match_param, match_value, outer_headers.first_vid,
- vid);
- break;
- }
-
- err = mlx5_add_flow_table_entry(priv->ft.vlan, match_criteria_enable,
- match_criteria, flow_context, ft_ix);
- if (err)
- netdev_err(priv->netdev, "%s: failed\n", __func__);
-
-add_vlan_rule_out:
- kvfree(match_criteria);
- kvfree(flow_context);
- return err;
-}
-
-static void mlx5e_del_vlan_rule(struct mlx5e_priv *priv,
- enum mlx5e_vlan_rule_type rule_type, u16 vid)
-{
- switch (rule_type) {
- case MLX5E_VLAN_RULE_TYPE_UNTAGGED:
- mlx5_del_flow_table_entry(priv->ft.vlan,
- priv->vlan.untagged_rule_ft_ix);
- break;
- case MLX5E_VLAN_RULE_TYPE_ANY_VID:
- mlx5_del_flow_table_entry(priv->ft.vlan,
- priv->vlan.any_vlan_rule_ft_ix);
- break;
- case MLX5E_VLAN_RULE_TYPE_MATCH_VID:
- mlx5_del_flow_table_entry(priv->ft.vlan,
- priv->vlan.active_vlans_ft_ix[vid]);
- break;
- }
-}
-
-void mlx5e_enable_vlan_filter(struct mlx5e_priv *priv)
-{
- if (!priv->vlan.filter_disabled)
- return;
-
- priv->vlan.filter_disabled = false;
- if (priv->netdev->flags & IFF_PROMISC)
- return;
- mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID, 0);
-}
-
-void mlx5e_disable_vlan_filter(struct mlx5e_priv *priv)
-{
- if (priv->vlan.filter_disabled)
- return;
-
- priv->vlan.filter_disabled = true;
- if (priv->netdev->flags & IFF_PROMISC)
- return;
- mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID, 0);
-}
-
-int mlx5e_vlan_rx_add_vid(struct net_device *dev, __always_unused __be16 proto,
- u16 vid)
-{
- struct mlx5e_priv *priv = netdev_priv(dev);
-
- return mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_MATCH_VID, vid);
-}
-
-int mlx5e_vlan_rx_kill_vid(struct net_device *dev, __always_unused __be16 proto,
- u16 vid)
-{
- struct mlx5e_priv *priv = netdev_priv(dev);
-
- mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_MATCH_VID, vid);
-
- return 0;
-}
-
-#define mlx5e_for_each_hash_node(hn, tmp, hash, i) \
- for (i = 0; i < MLX5E_ETH_ADDR_HASH_SIZE; i++) \
- hlist_for_each_entry_safe(hn, tmp, &hash[i], hlist)
-
-static void mlx5e_execute_action(struct mlx5e_priv *priv,
- struct mlx5e_eth_addr_hash_node *hn)
-{
- switch (hn->action) {
- case MLX5E_ACTION_ADD:
- mlx5e_add_eth_addr_rule(priv, &hn->ai, MLX5E_FULLMATCH);
- hn->action = MLX5E_ACTION_NONE;
- break;
-
- case MLX5E_ACTION_DEL:
- mlx5e_del_eth_addr_from_flow_table(priv, &hn->ai);
- mlx5e_del_eth_addr_from_hash(hn);
- break;
- }
-}
-
-static void mlx5e_sync_netdev_addr(struct mlx5e_priv *priv)
-{
- struct net_device *netdev = priv->netdev;
- struct netdev_hw_addr *ha;
-
- netif_addr_lock_bh(netdev);
-
- mlx5e_add_eth_addr_to_hash(priv->eth_addr.netdev_uc,
- priv->netdev->dev_addr);
-
- netdev_for_each_uc_addr(ha, netdev)
- mlx5e_add_eth_addr_to_hash(priv->eth_addr.netdev_uc, ha->addr);
-
- netdev_for_each_mc_addr(ha, netdev)
- mlx5e_add_eth_addr_to_hash(priv->eth_addr.netdev_mc, ha->addr);
-
- netif_addr_unlock_bh(netdev);
-}
-
-static void mlx5e_apply_netdev_addr(struct mlx5e_priv *priv)
-{
- struct mlx5e_eth_addr_hash_node *hn;
- struct hlist_node *tmp;
- int i;
-
- mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_uc, i)
- mlx5e_execute_action(priv, hn);
-
- mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_mc, i)
- mlx5e_execute_action(priv, hn);
-}
-
-static void mlx5e_handle_netdev_addr(struct mlx5e_priv *priv)
-{
- struct mlx5e_eth_addr_hash_node *hn;
- struct hlist_node *tmp;
- int i;
-
- mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_uc, i)
- hn->action = MLX5E_ACTION_DEL;
- mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_mc, i)
- hn->action = MLX5E_ACTION_DEL;
-
- if (!test_bit(MLX5E_STATE_DESTROYING, &priv->state))
- mlx5e_sync_netdev_addr(priv);
-
- mlx5e_apply_netdev_addr(priv);
-}
-
-void mlx5e_set_rx_mode_work(struct work_struct *work)
-{
- struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv,
- set_rx_mode_work);
-
- struct mlx5e_eth_addr_db *ea = &priv->eth_addr;
- struct net_device *ndev = priv->netdev;
-
- bool rx_mode_enable = !test_bit(MLX5E_STATE_DESTROYING, &priv->state);
- bool promisc_enabled = rx_mode_enable && (ndev->flags & IFF_PROMISC);
- bool allmulti_enabled = rx_mode_enable && (ndev->flags & IFF_ALLMULTI);
- bool broadcast_enabled = rx_mode_enable;
-
- bool enable_promisc = !ea->promisc_enabled && promisc_enabled;
- bool disable_promisc = ea->promisc_enabled && !promisc_enabled;
- bool enable_allmulti = !ea->allmulti_enabled && allmulti_enabled;
- bool disable_allmulti = ea->allmulti_enabled && !allmulti_enabled;
- bool enable_broadcast = !ea->broadcast_enabled && broadcast_enabled;
- bool disable_broadcast = ea->broadcast_enabled && !broadcast_enabled;
-
- if (enable_promisc) {
- mlx5e_add_eth_addr_rule(priv, &ea->promisc, MLX5E_PROMISC);
- if (!priv->vlan.filter_disabled)
- mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID,
- 0);
- }
- if (enable_allmulti)
- mlx5e_add_eth_addr_rule(priv, &ea->allmulti, MLX5E_ALLMULTI);
- if (enable_broadcast)
- mlx5e_add_eth_addr_rule(priv, &ea->broadcast, MLX5E_FULLMATCH);
-
- mlx5e_handle_netdev_addr(priv);
-
- if (disable_broadcast)
- mlx5e_del_eth_addr_from_flow_table(priv, &ea->broadcast);
- if (disable_allmulti)
- mlx5e_del_eth_addr_from_flow_table(priv, &ea->allmulti);
- if (disable_promisc) {
- if (!priv->vlan.filter_disabled)
- mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID,
- 0);
- mlx5e_del_eth_addr_from_flow_table(priv, &ea->promisc);
- }
-
- ea->promisc_enabled = promisc_enabled;
- ea->allmulti_enabled = allmulti_enabled;
- ea->broadcast_enabled = broadcast_enabled;
-}
-
-void mlx5e_init_eth_addr(struct mlx5e_priv *priv)
-{
- ether_addr_copy(priv->eth_addr.broadcast.addr, priv->netdev->broadcast);
-}
-
-static int mlx5e_create_main_flow_table(struct mlx5e_priv *priv)
-{
- struct mlx5_flow_table_group *g;
- u8 *dmac;
-
- g = kcalloc(9, sizeof(*g), GFP_KERNEL);
- if (!g)
- return -ENOMEM;
-
- g[0].log_sz = 3;
- g[0].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- MLX5_SET_TO_ONES(fte_match_param, g[0].match_criteria,
- outer_headers.ethertype);
- MLX5_SET_TO_ONES(fte_match_param, g[0].match_criteria,
- outer_headers.ip_protocol);
-
- g[1].log_sz = 1;
- g[1].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- MLX5_SET_TO_ONES(fte_match_param, g[1].match_criteria,
- outer_headers.ethertype);
-
- g[2].log_sz = 0;
-
- g[3].log_sz = 14;
- g[3].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- dmac = MLX5_ADDR_OF(fte_match_param, g[3].match_criteria,
- outer_headers.dmac_47_16);
- memset(dmac, 0xff, ETH_ALEN);
- MLX5_SET_TO_ONES(fte_match_param, g[3].match_criteria,
- outer_headers.ethertype);
- MLX5_SET_TO_ONES(fte_match_param, g[3].match_criteria,
- outer_headers.ip_protocol);
-
- g[4].log_sz = 13;
- g[4].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- dmac = MLX5_ADDR_OF(fte_match_param, g[4].match_criteria,
- outer_headers.dmac_47_16);
- memset(dmac, 0xff, ETH_ALEN);
- MLX5_SET_TO_ONES(fte_match_param, g[4].match_criteria,
- outer_headers.ethertype);
-
- g[5].log_sz = 11;
- g[5].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- dmac = MLX5_ADDR_OF(fte_match_param, g[5].match_criteria,
- outer_headers.dmac_47_16);
- memset(dmac, 0xff, ETH_ALEN);
-
- g[6].log_sz = 2;
- g[6].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- dmac = MLX5_ADDR_OF(fte_match_param, g[6].match_criteria,
- outer_headers.dmac_47_16);
- dmac[0] = 0x01;
- MLX5_SET_TO_ONES(fte_match_param, g[6].match_criteria,
- outer_headers.ethertype);
- MLX5_SET_TO_ONES(fte_match_param, g[6].match_criteria,
- outer_headers.ip_protocol);
-
- g[7].log_sz = 1;
- g[7].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- dmac = MLX5_ADDR_OF(fte_match_param, g[7].match_criteria,
- outer_headers.dmac_47_16);
- dmac[0] = 0x01;
- MLX5_SET_TO_ONES(fte_match_param, g[7].match_criteria,
- outer_headers.ethertype);
-
- g[8].log_sz = 0;
- g[8].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- dmac = MLX5_ADDR_OF(fte_match_param, g[8].match_criteria,
- outer_headers.dmac_47_16);
- dmac[0] = 0x01;
- priv->ft.main = mlx5_create_flow_table(priv->mdev, 1,
- MLX5_FLOW_TABLE_TYPE_NIC_RCV,
- 9, g);
- kfree(g);
-
- return priv->ft.main ? 0 : -ENOMEM;
-}
-
-static void mlx5e_destroy_main_flow_table(struct mlx5e_priv *priv)
-{
- mlx5_destroy_flow_table(priv->ft.main);
-}
-
-static int mlx5e_create_vlan_flow_table(struct mlx5e_priv *priv)
-{
- struct mlx5_flow_table_group *g;
-
- g = kcalloc(2, sizeof(*g), GFP_KERNEL);
- if (!g)
- return -ENOMEM;
-
- g[0].log_sz = 12;
- g[0].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- MLX5_SET_TO_ONES(fte_match_param, g[0].match_criteria,
- outer_headers.vlan_tag);
- MLX5_SET_TO_ONES(fte_match_param, g[0].match_criteria,
- outer_headers.first_vid);
-
- /* untagged + any vlan id */
- g[1].log_sz = 1;
- g[1].match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- MLX5_SET_TO_ONES(fte_match_param, g[1].match_criteria,
- outer_headers.vlan_tag);
-
- priv->ft.vlan = mlx5_create_flow_table(priv->mdev, 0,
- MLX5_FLOW_TABLE_TYPE_NIC_RCV,
- 2, g);
-
- kfree(g);
- return priv->ft.vlan ? 0 : -ENOMEM;
-}
-
-static void mlx5e_destroy_vlan_flow_table(struct mlx5e_priv *priv)
-{
- mlx5_destroy_flow_table(priv->ft.vlan);
-}
-
-int mlx5e_create_flow_tables(struct mlx5e_priv *priv)
-{
- int err;
-
- err = mlx5e_create_main_flow_table(priv);
- if (err)
- return err;
-
- err = mlx5e_create_vlan_flow_table(priv);
- if (err)
- goto err_destroy_main_flow_table;
-
- err = mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_UNTAGGED, 0);
- if (err)
- goto err_destroy_vlan_flow_table;
-
- return 0;
-
-err_destroy_vlan_flow_table:
- mlx5e_destroy_vlan_flow_table(priv);
-
-err_destroy_main_flow_table:
- mlx5e_destroy_main_flow_table(priv);
-
- return err;
-}
-
-void mlx5e_destroy_flow_tables(struct mlx5e_priv *priv)
-{
- mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_UNTAGGED, 0);
- mlx5e_destroy_vlan_flow_table(priv);
- mlx5e_destroy_main_flow_table(priv);
-}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
new file mode 100644
index 000000000000..80d81abc4820
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
@@ -0,0 +1,1224 @@
+/*
+ * Copyright (c) 2015, Mellanox Technologies. 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.
+ */
+
+#include <linux/list.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/tcp.h>
+#include <linux/mlx5/fs.h>
+#include "en.h"
+
+#define MLX5_SET_CFG(p, f, v) MLX5_SET(create_flow_group_in, p, f, v)
+
+enum {
+ MLX5E_FULLMATCH = 0,
+ MLX5E_ALLMULTI = 1,
+ MLX5E_PROMISC = 2,
+};
+
+enum {
+ MLX5E_UC = 0,
+ MLX5E_MC_IPV4 = 1,
+ MLX5E_MC_IPV6 = 2,
+ MLX5E_MC_OTHER = 3,
+};
+
+enum {
+ MLX5E_ACTION_NONE = 0,
+ MLX5E_ACTION_ADD = 1,
+ MLX5E_ACTION_DEL = 2,
+};
+
+struct mlx5e_eth_addr_hash_node {
+ struct hlist_node hlist;
+ u8 action;
+ struct mlx5e_eth_addr_info ai;
+};
+
+static inline int mlx5e_hash_eth_addr(u8 *addr)
+{
+ return addr[5];
+}
+
+static void mlx5e_add_eth_addr_to_hash(struct hlist_head *hash, u8 *addr)
+{
+ struct mlx5e_eth_addr_hash_node *hn;
+ int ix = mlx5e_hash_eth_addr(addr);
+ int found = 0;
+
+ hlist_for_each_entry(hn, &hash[ix], hlist)
+ if (ether_addr_equal_64bits(hn->ai.addr, addr)) {
+ found = 1;
+ break;
+ }
+
+ if (found) {
+ hn->action = MLX5E_ACTION_NONE;
+ return;
+ }
+
+ hn = kzalloc(sizeof(*hn), GFP_ATOMIC);
+ if (!hn)
+ return;
+
+ ether_addr_copy(hn->ai.addr, addr);
+ hn->action = MLX5E_ACTION_ADD;
+
+ hlist_add_head(&hn->hlist, &hash[ix]);
+}
+
+static void mlx5e_del_eth_addr_from_hash(struct mlx5e_eth_addr_hash_node *hn)
+{
+ hlist_del(&hn->hlist);
+ kfree(hn);
+}
+
+static void mlx5e_del_eth_addr_from_flow_table(struct mlx5e_priv *priv,
+ struct mlx5e_eth_addr_info *ai)
+{
+ if (ai->tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_ESP))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6_IPSEC_ESP]);
+
+ if (ai->tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_ESP))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4_IPSEC_ESP]);
+
+ if (ai->tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_AH))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6_IPSEC_AH]);
+
+ if (ai->tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_AH))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4_IPSEC_AH]);
+
+ if (ai->tt_vec & BIT(MLX5E_TT_IPV6_TCP))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6_TCP]);
+
+ if (ai->tt_vec & BIT(MLX5E_TT_IPV4_TCP))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4_TCP]);
+
+ if (ai->tt_vec & BIT(MLX5E_TT_IPV6_UDP))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6_UDP]);
+
+ if (ai->tt_vec & BIT(MLX5E_TT_IPV4_UDP))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4_UDP]);
+
+ if (ai->tt_vec & BIT(MLX5E_TT_IPV6))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV6]);
+
+ if (ai->tt_vec & BIT(MLX5E_TT_IPV4))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_IPV4]);
+
+ if (ai->tt_vec & BIT(MLX5E_TT_ANY))
+ mlx5_del_flow_rule(ai->ft_rule[MLX5E_TT_ANY]);
+}
+
+static int mlx5e_get_eth_addr_type(u8 *addr)
+{
+ if (is_unicast_ether_addr(addr))
+ return MLX5E_UC;
+
+ if ((addr[0] == 0x01) &&
+ (addr[1] == 0x00) &&
+ (addr[2] == 0x5e) &&
+ !(addr[3] & 0x80))
+ return MLX5E_MC_IPV4;
+
+ if ((addr[0] == 0x33) &&
+ (addr[1] == 0x33))
+ return MLX5E_MC_IPV6;
+
+ return MLX5E_MC_OTHER;
+}
+
+static u32 mlx5e_get_tt_vec(struct mlx5e_eth_addr_info *ai, int type)
+{
+ int eth_addr_type;
+ u32 ret;
+
+ switch (type) {
+ case MLX5E_FULLMATCH:
+ eth_addr_type = mlx5e_get_eth_addr_type(ai->addr);
+ switch (eth_addr_type) {
+ case MLX5E_UC:
+ ret =
+ BIT(MLX5E_TT_IPV4_TCP) |
+ BIT(MLX5E_TT_IPV6_TCP) |
+ BIT(MLX5E_TT_IPV4_UDP) |
+ BIT(MLX5E_TT_IPV6_UDP) |
+ BIT(MLX5E_TT_IPV4_IPSEC_AH) |
+ BIT(MLX5E_TT_IPV6_IPSEC_AH) |
+ BIT(MLX5E_TT_IPV4_IPSEC_ESP) |
+ BIT(MLX5E_TT_IPV6_IPSEC_ESP) |
+ BIT(MLX5E_TT_IPV4) |
+ BIT(MLX5E_TT_IPV6) |
+ BIT(MLX5E_TT_ANY) |
+ 0;
+ break;
+
+ case MLX5E_MC_IPV4:
+ ret =
+ BIT(MLX5E_TT_IPV4_UDP) |
+ BIT(MLX5E_TT_IPV4) |
+ 0;
+ break;
+
+ case MLX5E_MC_IPV6:
+ ret =
+ BIT(MLX5E_TT_IPV6_UDP) |
+ BIT(MLX5E_TT_IPV6) |
+ 0;
+ break;
+
+ case MLX5E_MC_OTHER:
+ ret =
+ BIT(MLX5E_TT_ANY) |
+ 0;
+ break;
+ }
+
+ break;
+
+ case MLX5E_ALLMULTI:
+ ret =
+ BIT(MLX5E_TT_IPV4_UDP) |
+ BIT(MLX5E_TT_IPV6_UDP) |
+ BIT(MLX5E_TT_IPV4) |
+ BIT(MLX5E_TT_IPV6) |
+ BIT(MLX5E_TT_ANY) |
+ 0;
+ break;
+
+ default: /* MLX5E_PROMISC */
+ ret =
+ BIT(MLX5E_TT_IPV4_TCP) |
+ BIT(MLX5E_TT_IPV6_TCP) |
+ BIT(MLX5E_TT_IPV4_UDP) |
+ BIT(MLX5E_TT_IPV6_UDP) |
+ BIT(MLX5E_TT_IPV4_IPSEC_AH) |
+ BIT(MLX5E_TT_IPV6_IPSEC_AH) |
+ BIT(MLX5E_TT_IPV4_IPSEC_ESP) |
+ BIT(MLX5E_TT_IPV6_IPSEC_ESP) |
+ BIT(MLX5E_TT_IPV4) |
+ BIT(MLX5E_TT_IPV6) |
+ BIT(MLX5E_TT_ANY) |
+ 0;
+ break;
+ }
+
+ return ret;
+}
+
+static int __mlx5e_add_eth_addr_rule(struct mlx5e_priv *priv,
+ struct mlx5e_eth_addr_info *ai,
+ int type, u32 *mc, u32 *mv)
+{
+ struct mlx5_flow_destination dest;
+ u8 match_criteria_enable = 0;
+ struct mlx5_flow_rule **rule_p;
+ struct mlx5_flow_table *ft = priv->fts.main.t;
+ u8 *mc_dmac = MLX5_ADDR_OF(fte_match_param, mc,
+ outer_headers.dmac_47_16);
+ u8 *mv_dmac = MLX5_ADDR_OF(fte_match_param, mv,
+ outer_headers.dmac_47_16);
+ u32 *tirn = priv->tirn;
+ u32 tt_vec;
+ int err = 0;
+
+ dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR;
+
+ switch (type) {
+ case MLX5E_FULLMATCH:
+ match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
+ eth_broadcast_addr(mc_dmac);
+ ether_addr_copy(mv_dmac, ai->addr);
+ break;
+
+ case MLX5E_ALLMULTI:
+ match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
+ mc_dmac[0] = 0x01;
+ mv_dmac[0] = 0x01;
+ break;
+
+ case MLX5E_PROMISC:
+ break;
+ }
+
+ tt_vec = mlx5e_get_tt_vec(ai, type);
+
+ if (tt_vec & BIT(MLX5E_TT_ANY)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_ANY];
+ dest.tir_num = tirn[MLX5E_TT_ANY];
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+ ai->tt_vec |= BIT(MLX5E_TT_ANY);
+ }
+
+ match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype);
+
+ if (tt_vec & BIT(MLX5E_TT_IPV4)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_IPV4];
+ dest.tir_num = tirn[MLX5E_TT_IPV4];
+ MLX5_SET(fte_match_param, mv, outer_headers.ethertype,
+ ETH_P_IP);
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+ ai->tt_vec |= BIT(MLX5E_TT_IPV4);
+ }
+
+ if (tt_vec & BIT(MLX5E_TT_IPV6)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_IPV6];
+ dest.tir_num = tirn[MLX5E_TT_IPV6];
+ MLX5_SET(fte_match_param, mv, outer_headers.ethertype,
+ ETH_P_IPV6);
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+ ai->tt_vec |= BIT(MLX5E_TT_IPV6);
+ }
+
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol);
+ MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_UDP);
+
+ if (tt_vec & BIT(MLX5E_TT_IPV4_UDP)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_IPV4_UDP];
+ dest.tir_num = tirn[MLX5E_TT_IPV4_UDP];
+ MLX5_SET(fte_match_param, mv, outer_headers.ethertype,
+ ETH_P_IP);
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+ ai->tt_vec |= BIT(MLX5E_TT_IPV4_UDP);
+ }
+
+ if (tt_vec & BIT(MLX5E_TT_IPV6_UDP)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_IPV6_UDP];
+ dest.tir_num = tirn[MLX5E_TT_IPV6_UDP];
+ MLX5_SET(fte_match_param, mv, outer_headers.ethertype,
+ ETH_P_IPV6);
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+ ai->tt_vec |= BIT(MLX5E_TT_IPV6_UDP);
+ }
+
+ MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_TCP);
+
+ if (tt_vec & BIT(MLX5E_TT_IPV4_TCP)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_IPV4_TCP];
+ dest.tir_num = tirn[MLX5E_TT_IPV4_TCP];
+ MLX5_SET(fte_match_param, mv, outer_headers.ethertype,
+ ETH_P_IP);
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+ ai->tt_vec |= BIT(MLX5E_TT_IPV4_TCP);
+ }
+
+ if (tt_vec & BIT(MLX5E_TT_IPV6_TCP)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_IPV6_TCP];
+ dest.tir_num = tirn[MLX5E_TT_IPV6_TCP];
+ MLX5_SET(fte_match_param, mv, outer_headers.ethertype,
+ ETH_P_IPV6);
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+
+ ai->tt_vec |= BIT(MLX5E_TT_IPV6_TCP);
+ }
+
+ MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_AH);
+
+ if (tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_AH)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_IPV4_IPSEC_AH];
+ dest.tir_num = tirn[MLX5E_TT_IPV4_IPSEC_AH];
+ MLX5_SET(fte_match_param, mv, outer_headers.ethertype,
+ ETH_P_IP);
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+ ai->tt_vec |= BIT(MLX5E_TT_IPV4_IPSEC_AH);
+ }
+
+ if (tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_AH)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_IPV6_IPSEC_AH];
+ dest.tir_num = tirn[MLX5E_TT_IPV6_IPSEC_AH];
+ MLX5_SET(fte_match_param, mv, outer_headers.ethertype,
+ ETH_P_IPV6);
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+ ai->tt_vec |= BIT(MLX5E_TT_IPV6_IPSEC_AH);
+ }
+
+ MLX5_SET(fte_match_param, mv, outer_headers.ip_protocol, IPPROTO_ESP);
+
+ if (tt_vec & BIT(MLX5E_TT_IPV4_IPSEC_ESP)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_IPV4_IPSEC_ESP];
+ dest.tir_num = tirn[MLX5E_TT_IPV4_IPSEC_ESP];
+ MLX5_SET(fte_match_param, mv, outer_headers.ethertype,
+ ETH_P_IP);
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+ ai->tt_vec |= BIT(MLX5E_TT_IPV4_IPSEC_ESP);
+ }
+
+ if (tt_vec & BIT(MLX5E_TT_IPV6_IPSEC_ESP)) {
+ rule_p = &ai->ft_rule[MLX5E_TT_IPV6_IPSEC_ESP];
+ dest.tir_num = tirn[MLX5E_TT_IPV6_IPSEC_ESP];
+ MLX5_SET(fte_match_param, mv, outer_headers.ethertype,
+ ETH_P_IPV6);
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG, &dest);
+ if (IS_ERR_OR_NULL(*rule_p))
+ goto err_del_ai;
+ ai->tt_vec |= BIT(MLX5E_TT_IPV6_IPSEC_ESP);
+ }
+
+ return 0;
+
+err_del_ai:
+ err = PTR_ERR(*rule_p);
+ *rule_p = NULL;
+ mlx5e_del_eth_addr_from_flow_table(priv, ai);
+
+ return err;
+}
+
+static int mlx5e_add_eth_addr_rule(struct mlx5e_priv *priv,
+ struct mlx5e_eth_addr_info *ai, int type)
+{
+ u32 *match_criteria;
+ u32 *match_value;
+ int err = 0;
+
+ match_value = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
+ match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
+ if (!match_value || !match_criteria) {
+ netdev_err(priv->netdev, "%s: alloc failed\n", __func__);
+ err = -ENOMEM;
+ goto add_eth_addr_rule_out;
+ }
+
+ err = __mlx5e_add_eth_addr_rule(priv, ai, type, match_criteria,
+ match_value);
+
+add_eth_addr_rule_out:
+ kvfree(match_criteria);
+ kvfree(match_value);
+
+ return err;
+}
+
+static int mlx5e_vport_context_update_vlans(struct mlx5e_priv *priv)
+{
+ struct net_device *ndev = priv->netdev;
+ int max_list_size;
+ int list_size;
+ u16 *vlans;
+ int vlan;
+ int err;
+ int i;
+
+ list_size = 0;
+ for_each_set_bit(vlan, priv->vlan.active_vlans, VLAN_N_VID)
+ list_size++;
+
+ max_list_size = 1 << MLX5_CAP_GEN(priv->mdev, log_max_vlan_list);
+
+ if (list_size > max_list_size) {
+ netdev_warn(ndev,
+ "netdev vlans list size (%d) > (%d) max vport list size, some vlans will be dropped\n",
+ list_size, max_list_size);
+ list_size = max_list_size;
+ }
+
+ vlans = kcalloc(list_size, sizeof(*vlans), GFP_KERNEL);
+ if (!vlans)
+ return -ENOMEM;
+
+ i = 0;
+ for_each_set_bit(vlan, priv->vlan.active_vlans, VLAN_N_VID) {
+ if (i >= list_size)
+ break;
+ vlans[i++] = vlan;
+ }
+
+ err = mlx5_modify_nic_vport_vlans(priv->mdev, vlans, list_size);
+ if (err)
+ netdev_err(ndev, "Failed to modify vport vlans list err(%d)\n",
+ err);
+
+ kfree(vlans);
+ return err;
+}
+
+enum mlx5e_vlan_rule_type {
+ MLX5E_VLAN_RULE_TYPE_UNTAGGED,
+ MLX5E_VLAN_RULE_TYPE_ANY_VID,
+ MLX5E_VLAN_RULE_TYPE_MATCH_VID,
+};
+
+static int __mlx5e_add_vlan_rule(struct mlx5e_priv *priv,
+ enum mlx5e_vlan_rule_type rule_type,
+ u16 vid, u32 *mc, u32 *mv)
+{
+ struct mlx5_flow_table *ft = priv->fts.vlan.t;
+ struct mlx5_flow_destination dest;
+ u8 match_criteria_enable = 0;
+ struct mlx5_flow_rule **rule_p;
+ int err = 0;
+
+ dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
+ dest.ft = priv->fts.main.t;
+
+ match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.vlan_tag);
+
+ switch (rule_type) {
+ case MLX5E_VLAN_RULE_TYPE_UNTAGGED:
+ rule_p = &priv->vlan.untagged_rule;
+ break;
+ case MLX5E_VLAN_RULE_TYPE_ANY_VID:
+ rule_p = &priv->vlan.any_vlan_rule;
+ MLX5_SET(fte_match_param, mv, outer_headers.vlan_tag, 1);
+ break;
+ default: /* MLX5E_VLAN_RULE_TYPE_MATCH_VID */
+ rule_p = &priv->vlan.active_vlans_rule[vid];
+ MLX5_SET(fte_match_param, mv, outer_headers.vlan_tag, 1);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.first_vid);
+ MLX5_SET(fte_match_param, mv, outer_headers.first_vid, vid);
+ break;
+ }
+
+ *rule_p = mlx5_add_flow_rule(ft, match_criteria_enable, mc, mv,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ MLX5_FS_DEFAULT_FLOW_TAG,
+ &dest);
+
+ if (IS_ERR(*rule_p)) {
+ err = PTR_ERR(*rule_p);
+ *rule_p = NULL;
+ netdev_err(priv->netdev, "%s: add rule failed\n", __func__);
+ }
+
+ return err;
+}
+
+static int mlx5e_add_vlan_rule(struct mlx5e_priv *priv,
+ enum mlx5e_vlan_rule_type rule_type, u16 vid)
+{
+ u32 *match_criteria;
+ u32 *match_value;
+ int err = 0;
+
+ match_value = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
+ match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
+ if (!match_value || !match_criteria) {
+ netdev_err(priv->netdev, "%s: alloc failed\n", __func__);
+ err = -ENOMEM;
+ goto add_vlan_rule_out;
+ }
+
+ if (rule_type == MLX5E_VLAN_RULE_TYPE_MATCH_VID)
+ mlx5e_vport_context_update_vlans(priv);
+
+ err = __mlx5e_add_vlan_rule(priv, rule_type, vid, match_criteria,
+ match_value);
+
+add_vlan_rule_out:
+ kvfree(match_criteria);
+ kvfree(match_value);
+
+ return err;
+}
+
+static void mlx5e_del_vlan_rule(struct mlx5e_priv *priv,
+ enum mlx5e_vlan_rule_type rule_type, u16 vid)
+{
+ switch (rule_type) {
+ case MLX5E_VLAN_RULE_TYPE_UNTAGGED:
+ if (priv->vlan.untagged_rule) {
+ mlx5_del_flow_rule(priv->vlan.untagged_rule);
+ priv->vlan.untagged_rule = NULL;
+ }
+ break;
+ case MLX5E_VLAN_RULE_TYPE_ANY_VID:
+ if (priv->vlan.any_vlan_rule) {
+ mlx5_del_flow_rule(priv->vlan.any_vlan_rule);
+ priv->vlan.any_vlan_rule = NULL;
+ }
+ break;
+ case MLX5E_VLAN_RULE_TYPE_MATCH_VID:
+ mlx5e_vport_context_update_vlans(priv);
+ if (priv->vlan.active_vlans_rule[vid]) {
+ mlx5_del_flow_rule(priv->vlan.active_vlans_rule[vid]);
+ priv->vlan.active_vlans_rule[vid] = NULL;
+ }
+ mlx5e_vport_context_update_vlans(priv);
+ break;
+ }
+}
+
+void mlx5e_enable_vlan_filter(struct mlx5e_priv *priv)
+{
+ if (!priv->vlan.filter_disabled)
+ return;
+
+ priv->vlan.filter_disabled = false;
+ if (priv->netdev->flags & IFF_PROMISC)
+ return;
+ mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID, 0);
+}
+
+void mlx5e_disable_vlan_filter(struct mlx5e_priv *priv)
+{
+ if (priv->vlan.filter_disabled)
+ return;
+
+ priv->vlan.filter_disabled = true;
+ if (priv->netdev->flags & IFF_PROMISC)
+ return;
+ mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID, 0);
+}
+
+int mlx5e_vlan_rx_add_vid(struct net_device *dev, __always_unused __be16 proto,
+ u16 vid)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+
+ set_bit(vid, priv->vlan.active_vlans);
+
+ return mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_MATCH_VID, vid);
+}
+
+int mlx5e_vlan_rx_kill_vid(struct net_device *dev, __always_unused __be16 proto,
+ u16 vid)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+
+ clear_bit(vid, priv->vlan.active_vlans);
+
+ mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_MATCH_VID, vid);
+
+ return 0;
+}
+
+#define mlx5e_for_each_hash_node(hn, tmp, hash, i) \
+ for (i = 0; i < MLX5E_ETH_ADDR_HASH_SIZE; i++) \
+ hlist_for_each_entry_safe(hn, tmp, &hash[i], hlist)
+
+static void mlx5e_execute_action(struct mlx5e_priv *priv,
+ struct mlx5e_eth_addr_hash_node *hn)
+{
+ switch (hn->action) {
+ case MLX5E_ACTION_ADD:
+ mlx5e_add_eth_addr_rule(priv, &hn->ai, MLX5E_FULLMATCH);
+ hn->action = MLX5E_ACTION_NONE;
+ break;
+
+ case MLX5E_ACTION_DEL:
+ mlx5e_del_eth_addr_from_flow_table(priv, &hn->ai);
+ mlx5e_del_eth_addr_from_hash(hn);
+ break;
+ }
+}
+
+static void mlx5e_sync_netdev_addr(struct mlx5e_priv *priv)
+{
+ struct net_device *netdev = priv->netdev;
+ struct netdev_hw_addr *ha;
+
+ netif_addr_lock_bh(netdev);
+
+ mlx5e_add_eth_addr_to_hash(priv->eth_addr.netdev_uc,
+ priv->netdev->dev_addr);
+
+ netdev_for_each_uc_addr(ha, netdev)
+ mlx5e_add_eth_addr_to_hash(priv->eth_addr.netdev_uc, ha->addr);
+
+ netdev_for_each_mc_addr(ha, netdev)
+ mlx5e_add_eth_addr_to_hash(priv->eth_addr.netdev_mc, ha->addr);
+
+ netif_addr_unlock_bh(netdev);
+}
+
+static void mlx5e_fill_addr_array(struct mlx5e_priv *priv, int list_type,
+ u8 addr_array[][ETH_ALEN], int size)
+{
+ bool is_uc = (list_type == MLX5_NVPRT_LIST_TYPE_UC);
+ struct net_device *ndev = priv->netdev;
+ struct mlx5e_eth_addr_hash_node *hn;
+ struct hlist_head *addr_list;
+ struct hlist_node *tmp;
+ int i = 0;
+ int hi;
+
+ addr_list = is_uc ? priv->eth_addr.netdev_uc : priv->eth_addr.netdev_mc;
+
+ if (is_uc) /* Make sure our own address is pushed first */
+ ether_addr_copy(addr_array[i++], ndev->dev_addr);
+ else if (priv->eth_addr.broadcast_enabled)
+ ether_addr_copy(addr_array[i++], ndev->broadcast);
+
+ mlx5e_for_each_hash_node(hn, tmp, addr_list, hi) {
+ if (ether_addr_equal(ndev->dev_addr, hn->ai.addr))
+ continue;
+ if (i >= size)
+ break;
+ ether_addr_copy(addr_array[i++], hn->ai.addr);
+ }
+}
+
+static void mlx5e_vport_context_update_addr_list(struct mlx5e_priv *priv,
+ int list_type)
+{
+ bool is_uc = (list_type == MLX5_NVPRT_LIST_TYPE_UC);
+ struct mlx5e_eth_addr_hash_node *hn;
+ u8 (*addr_array)[ETH_ALEN] = NULL;
+ struct hlist_head *addr_list;
+ struct hlist_node *tmp;
+ int max_size;
+ int size;
+ int err;
+ int hi;
+
+ size = is_uc ? 0 : (priv->eth_addr.broadcast_enabled ? 1 : 0);
+ max_size = is_uc ?
+ 1 << MLX5_CAP_GEN(priv->mdev, log_max_current_uc_list) :
+ 1 << MLX5_CAP_GEN(priv->mdev, log_max_current_mc_list);
+
+ addr_list = is_uc ? priv->eth_addr.netdev_uc : priv->eth_addr.netdev_mc;
+ mlx5e_for_each_hash_node(hn, tmp, addr_list, hi)
+ size++;
+
+ if (size > max_size) {
+ netdev_warn(priv->netdev,
+ "netdev %s list size (%d) > (%d) max vport list size, some addresses will be dropped\n",
+ is_uc ? "UC" : "MC", size, max_size);
+ size = max_size;
+ }
+
+ if (size) {
+ addr_array = kcalloc(size, ETH_ALEN, GFP_KERNEL);
+ if (!addr_array) {
+ err = -ENOMEM;
+ goto out;
+ }
+ mlx5e_fill_addr_array(priv, list_type, addr_array, size);
+ }
+
+ err = mlx5_modify_nic_vport_mac_list(priv->mdev, list_type, addr_array, size);
+out:
+ if (err)
+ netdev_err(priv->netdev,
+ "Failed to modify vport %s list err(%d)\n",
+ is_uc ? "UC" : "MC", err);
+ kfree(addr_array);
+}
+
+static void mlx5e_vport_context_update(struct mlx5e_priv *priv)
+{
+ struct mlx5e_eth_addr_db *ea = &priv->eth_addr;
+
+ mlx5e_vport_context_update_addr_list(priv, MLX5_NVPRT_LIST_TYPE_UC);
+ mlx5e_vport_context_update_addr_list(priv, MLX5_NVPRT_LIST_TYPE_MC);
+ mlx5_modify_nic_vport_promisc(priv->mdev, 0,
+ ea->allmulti_enabled,
+ ea->promisc_enabled);
+}
+
+static void mlx5e_apply_netdev_addr(struct mlx5e_priv *priv)
+{
+ struct mlx5e_eth_addr_hash_node *hn;
+ struct hlist_node *tmp;
+ int i;
+
+ mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_uc, i)
+ mlx5e_execute_action(priv, hn);
+
+ mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_mc, i)
+ mlx5e_execute_action(priv, hn);
+}
+
+static void mlx5e_handle_netdev_addr(struct mlx5e_priv *priv)
+{
+ struct mlx5e_eth_addr_hash_node *hn;
+ struct hlist_node *tmp;
+ int i;
+
+ mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_uc, i)
+ hn->action = MLX5E_ACTION_DEL;
+ mlx5e_for_each_hash_node(hn, tmp, priv->eth_addr.netdev_mc, i)
+ hn->action = MLX5E_ACTION_DEL;
+
+ if (!test_bit(MLX5E_STATE_DESTROYING, &priv->state))
+ mlx5e_sync_netdev_addr(priv);
+
+ mlx5e_apply_netdev_addr(priv);
+}
+
+void mlx5e_set_rx_mode_work(struct work_struct *work)
+{
+ struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv,
+ set_rx_mode_work);
+
+ struct mlx5e_eth_addr_db *ea = &priv->eth_addr;
+ struct net_device *ndev = priv->netdev;
+
+ bool rx_mode_enable = !test_bit(MLX5E_STATE_DESTROYING, &priv->state);
+ bool promisc_enabled = rx_mode_enable && (ndev->flags & IFF_PROMISC);
+ bool allmulti_enabled = rx_mode_enable && (ndev->flags & IFF_ALLMULTI);
+ bool broadcast_enabled = rx_mode_enable;
+
+ bool enable_promisc = !ea->promisc_enabled && promisc_enabled;
+ bool disable_promisc = ea->promisc_enabled && !promisc_enabled;
+ bool enable_allmulti = !ea->allmulti_enabled && allmulti_enabled;
+ bool disable_allmulti = ea->allmulti_enabled && !allmulti_enabled;
+ bool enable_broadcast = !ea->broadcast_enabled && broadcast_enabled;
+ bool disable_broadcast = ea->broadcast_enabled && !broadcast_enabled;
+
+ if (enable_promisc) {
+ mlx5e_add_eth_addr_rule(priv, &ea->promisc, MLX5E_PROMISC);
+ if (!priv->vlan.filter_disabled)
+ mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID,
+ 0);
+ }
+ if (enable_allmulti)
+ mlx5e_add_eth_addr_rule(priv, &ea->allmulti, MLX5E_ALLMULTI);
+ if (enable_broadcast)
+ mlx5e_add_eth_addr_rule(priv, &ea->broadcast, MLX5E_FULLMATCH);
+
+ mlx5e_handle_netdev_addr(priv);
+
+ if (disable_broadcast)
+ mlx5e_del_eth_addr_from_flow_table(priv, &ea->broadcast);
+ if (disable_allmulti)
+ mlx5e_del_eth_addr_from_flow_table(priv, &ea->allmulti);
+ if (disable_promisc) {
+ if (!priv->vlan.filter_disabled)
+ mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_VID,
+ 0);
+ mlx5e_del_eth_addr_from_flow_table(priv, &ea->promisc);
+ }
+
+ ea->promisc_enabled = promisc_enabled;
+ ea->allmulti_enabled = allmulti_enabled;
+ ea->broadcast_enabled = broadcast_enabled;
+
+ mlx5e_vport_context_update(priv);
+}
+
+static void mlx5e_destroy_groups(struct mlx5e_flow_table *ft)
+{
+ int i;
+
+ for (i = ft->num_groups - 1; i >= 0; i--) {
+ if (!IS_ERR_OR_NULL(ft->g[i]))
+ mlx5_destroy_flow_group(ft->g[i]);
+ ft->g[i] = NULL;
+ }
+ ft->num_groups = 0;
+}
+
+void mlx5e_init_eth_addr(struct mlx5e_priv *priv)
+{
+ ether_addr_copy(priv->eth_addr.broadcast.addr, priv->netdev->broadcast);
+}
+
+#define MLX5E_MAIN_GROUP0_SIZE BIT(3)
+#define MLX5E_MAIN_GROUP1_SIZE BIT(1)
+#define MLX5E_MAIN_GROUP2_SIZE BIT(0)
+#define MLX5E_MAIN_GROUP3_SIZE BIT(14)
+#define MLX5E_MAIN_GROUP4_SIZE BIT(13)
+#define MLX5E_MAIN_GROUP5_SIZE BIT(11)
+#define MLX5E_MAIN_GROUP6_SIZE BIT(2)
+#define MLX5E_MAIN_GROUP7_SIZE BIT(1)
+#define MLX5E_MAIN_GROUP8_SIZE BIT(0)
+#define MLX5E_MAIN_TABLE_SIZE (MLX5E_MAIN_GROUP0_SIZE +\
+ MLX5E_MAIN_GROUP1_SIZE +\
+ MLX5E_MAIN_GROUP2_SIZE +\
+ MLX5E_MAIN_GROUP3_SIZE +\
+ MLX5E_MAIN_GROUP4_SIZE +\
+ MLX5E_MAIN_GROUP5_SIZE +\
+ MLX5E_MAIN_GROUP6_SIZE +\
+ MLX5E_MAIN_GROUP7_SIZE +\
+ MLX5E_MAIN_GROUP8_SIZE)
+
+static int __mlx5e_create_main_groups(struct mlx5e_flow_table *ft, u32 *in,
+ int inlen)
+{
+ u8 *mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
+ u8 *dmac = MLX5_ADDR_OF(create_flow_group_in, in,
+ match_criteria.outer_headers.dmac_47_16);
+ int err;
+ int ix = 0;
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol);
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_MAIN_GROUP0_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype);
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_MAIN_GROUP1_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_MAIN_GROUP2_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol);
+ eth_broadcast_addr(dmac);
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_MAIN_GROUP3_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype);
+ eth_broadcast_addr(dmac);
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_MAIN_GROUP4_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ eth_broadcast_addr(dmac);
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_MAIN_GROUP5_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol);
+ dmac[0] = 0x01;
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_MAIN_GROUP6_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype);
+ dmac[0] = 0x01;
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_MAIN_GROUP7_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ dmac[0] = 0x01;
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_MAIN_GROUP8_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ return 0;
+
+err_destroy_groups:
+ err = PTR_ERR(ft->g[ft->num_groups]);
+ ft->g[ft->num_groups] = NULL;
+ mlx5e_destroy_groups(ft);
+
+ return err;
+}
+
+static int mlx5e_create_main_groups(struct mlx5e_flow_table *ft)
+{
+ u32 *in;
+ int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
+ int err;
+
+ in = mlx5_vzalloc(inlen);
+ if (!in)
+ return -ENOMEM;
+
+ err = __mlx5e_create_main_groups(ft, in, inlen);
+
+ kvfree(in);
+ return err;
+}
+
+static int mlx5e_create_main_flow_table(struct mlx5e_priv *priv)
+{
+ struct mlx5e_flow_table *ft = &priv->fts.main;
+ int err;
+
+ ft->num_groups = 0;
+ ft->t = mlx5_create_flow_table(priv->fts.ns, 0, MLX5E_MAIN_TABLE_SIZE);
+
+ if (IS_ERR(ft->t)) {
+ err = PTR_ERR(ft->t);
+ ft->t = NULL;
+ return err;
+ }
+ ft->g = kcalloc(MLX5E_NUM_MAIN_GROUPS, sizeof(*ft->g), GFP_KERNEL);
+ if (!ft->g) {
+ err = -ENOMEM;
+ goto err_destroy_main_flow_table;
+ }
+
+ err = mlx5e_create_main_groups(ft);
+ if (err)
+ goto err_free_g;
+ return 0;
+
+err_free_g:
+ kfree(ft->g);
+
+err_destroy_main_flow_table:
+ mlx5_destroy_flow_table(ft->t);
+ ft->t = NULL;
+
+ return err;
+}
+
+static void mlx5e_destroy_flow_table(struct mlx5e_flow_table *ft)
+{
+ mlx5e_destroy_groups(ft);
+ kfree(ft->g);
+ mlx5_destroy_flow_table(ft->t);
+ ft->t = NULL;
+}
+
+static void mlx5e_destroy_main_flow_table(struct mlx5e_priv *priv)
+{
+ mlx5e_destroy_flow_table(&priv->fts.main);
+}
+
+#define MLX5E_NUM_VLAN_GROUPS 2
+#define MLX5E_VLAN_GROUP0_SIZE BIT(12)
+#define MLX5E_VLAN_GROUP1_SIZE BIT(1)
+#define MLX5E_VLAN_TABLE_SIZE (MLX5E_VLAN_GROUP0_SIZE +\
+ MLX5E_VLAN_GROUP1_SIZE)
+
+static int __mlx5e_create_vlan_groups(struct mlx5e_flow_table *ft, u32 *in,
+ int inlen)
+{
+ int err;
+ int ix = 0;
+ u8 *mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.vlan_tag);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.first_vid);
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_VLAN_GROUP0_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ memset(in, 0, inlen);
+ MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+ MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.vlan_tag);
+ MLX5_SET_CFG(in, start_flow_index, ix);
+ ix += MLX5E_VLAN_GROUP1_SIZE;
+ MLX5_SET_CFG(in, end_flow_index, ix - 1);
+ ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+ if (IS_ERR(ft->g[ft->num_groups]))
+ goto err_destroy_groups;
+ ft->num_groups++;
+
+ return 0;
+
+err_destroy_groups:
+ err = PTR_ERR(ft->g[ft->num_groups]);
+ ft->g[ft->num_groups] = NULL;
+ mlx5e_destroy_groups(ft);
+
+ return err;
+}
+
+static int mlx5e_create_vlan_groups(struct mlx5e_flow_table *ft)
+{
+ u32 *in;
+ int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
+ int err;
+
+ in = mlx5_vzalloc(inlen);
+ if (!in)
+ return -ENOMEM;
+
+ err = __mlx5e_create_vlan_groups(ft, in, inlen);
+
+ kvfree(in);
+ return err;
+}
+
+static int mlx5e_create_vlan_flow_table(struct mlx5e_priv *priv)
+{
+ struct mlx5e_flow_table *ft = &priv->fts.vlan;
+ int err;
+
+ ft->num_groups = 0;
+ ft->t = mlx5_create_flow_table(priv->fts.ns, 0, MLX5E_VLAN_TABLE_SIZE);
+
+ if (IS_ERR(ft->t)) {
+ err = PTR_ERR(ft->t);
+ ft->t = NULL;
+ return err;
+ }
+ ft->g = kcalloc(MLX5E_NUM_VLAN_GROUPS, sizeof(*ft->g), GFP_KERNEL);
+ if (!ft->g) {
+ err = -ENOMEM;
+ goto err_destroy_vlan_flow_table;
+ }
+
+ err = mlx5e_create_vlan_groups(ft);
+ if (err)
+ goto err_free_g;
+
+ return 0;
+
+err_free_g:
+ kfree(ft->g);
+
+err_destroy_vlan_flow_table:
+ mlx5_destroy_flow_table(ft->t);
+ ft->t = NULL;
+
+ return err;
+}
+
+static void mlx5e_destroy_vlan_flow_table(struct mlx5e_priv *priv)
+{
+ mlx5e_destroy_flow_table(&priv->fts.vlan);
+}
+
+int mlx5e_create_flow_tables(struct mlx5e_priv *priv)
+{
+ int err;
+
+ priv->fts.ns = mlx5_get_flow_namespace(priv->mdev,
+ MLX5_FLOW_NAMESPACE_KERNEL);
+
+ if (!priv->fts.ns)
+ return -EINVAL;
+
+ err = mlx5e_create_vlan_flow_table(priv);
+ if (err)
+ return err;
+
+ err = mlx5e_create_main_flow_table(priv);
+ if (err)
+ goto err_destroy_vlan_flow_table;
+
+ err = mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_UNTAGGED, 0);
+ if (err)
+ goto err_destroy_main_flow_table;
+
+ return 0;
+
+err_destroy_main_flow_table:
+ mlx5e_destroy_main_flow_table(priv);
+err_destroy_vlan_flow_table:
+ mlx5e_destroy_vlan_flow_table(priv);
+
+ return err;
+}
+
+void mlx5e_destroy_flow_tables(struct mlx5e_priv *priv)
+{
+ mlx5e_del_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_UNTAGGED, 0);
+ mlx5e_destroy_main_flow_table(priv);
+ mlx5e_destroy_vlan_flow_table(priv);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 1e52db32c73d..5c74a734f158 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -30,8 +30,9 @@
* SOFTWARE.
*/
-#include <linux/mlx5/flow_table.h>
+#include <linux/mlx5/fs.h>
#include "en.h"
+#include "eswitch.h"
struct mlx5e_rq_param {
u32 rqc[MLX5_ST_SZ_DW(rqc)];
@@ -63,7 +64,7 @@ static void mlx5e_update_carrier(struct mlx5e_priv *priv)
u8 port_state;
port_state = mlx5_query_vport_state(mdev,
- MLX5_QUERY_VPORT_STATE_IN_OP_MOD_VNIC_VPORT);
+ MLX5_QUERY_VPORT_STATE_IN_OP_MOD_VNIC_VPORT, 0);
if (port_state == VPORT_STATE_UP)
netif_carrier_on(priv->netdev);
@@ -350,6 +351,7 @@ static int mlx5e_create_rq(struct mlx5e_channel *c,
rq->pdev = c->pdev;
rq->netdev = c->netdev;
+ rq->tstamp = &priv->tstamp;
rq->channel = c;
rq->ix = c->ix;
rq->priv = c->priv;
@@ -506,6 +508,7 @@ static void mlx5e_close_rq(struct mlx5e_rq *rq)
static void mlx5e_free_sq_db(struct mlx5e_sq *sq)
{
+ kfree(sq->wqe_info);
kfree(sq->dma_fifo);
kfree(sq->skb);
}
@@ -518,8 +521,10 @@ static int mlx5e_alloc_sq_db(struct mlx5e_sq *sq, int numa)
sq->skb = kzalloc_node(wq_sz * sizeof(*sq->skb), GFP_KERNEL, numa);
sq->dma_fifo = kzalloc_node(df_sz * sizeof(*sq->dma_fifo), GFP_KERNEL,
numa);
+ sq->wqe_info = kzalloc_node(wq_sz * sizeof(*sq->wqe_info), GFP_KERNEL,
+ numa);
- if (!sq->skb || !sq->dma_fifo) {
+ if (!sq->skb || !sq->dma_fifo || !sq->wqe_info) {
mlx5e_free_sq_db(sq);
return -ENOMEM;
}
@@ -567,6 +572,7 @@ static int mlx5e_create_sq(struct mlx5e_channel *c,
sq->txq = netdev_get_tx_queue(priv->netdev, txq_ix);
sq->pdev = c->pdev;
+ sq->tstamp = &priv->tstamp;
sq->mkey_be = c->mkey_be;
sq->channel = c;
sq->tc = tc;
@@ -1020,6 +1026,7 @@ err_close_tx_cqs:
err_napi_del:
netif_napi_del(&c->napi);
+ napi_hash_del(&c->napi);
kfree(c);
return err;
@@ -1033,6 +1040,10 @@ static void mlx5e_close_channel(struct mlx5e_channel *c)
mlx5e_close_cq(&c->rq.cq);
mlx5e_close_tx_cqs(c);
netif_napi_del(&c->napi);
+
+ napi_hash_del(&c->napi);
+ synchronize_rcu();
+
kfree(c);
}
@@ -1421,6 +1432,7 @@ int mlx5e_open_locked(struct net_device *netdev)
mlx5e_update_carrier(priv);
mlx5e_redirect_rqts(priv);
+ mlx5e_timestamp_init(priv);
schedule_delayed_work(&priv->update_stats_work, 0);
@@ -1457,6 +1469,7 @@ int mlx5e_close_locked(struct net_device *netdev)
clear_bit(MLX5E_STATE_OPENED, &priv->state);
+ mlx5e_timestamp_cleanup(priv);
mlx5e_redirect_rqts(priv);
netif_carrier_off(priv->netdev);
mlx5e_close_channels(priv);
@@ -1926,6 +1939,91 @@ static int mlx5e_change_mtu(struct net_device *netdev, int new_mtu)
return err;
}
+static int mlx5e_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ switch (cmd) {
+ case SIOCSHWTSTAMP:
+ return mlx5e_hwstamp_set(dev, ifr);
+ case SIOCGHWTSTAMP:
+ return mlx5e_hwstamp_get(dev, ifr);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int mlx5e_set_vf_mac(struct net_device *dev, int vf, u8 *mac)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+ struct mlx5_core_dev *mdev = priv->mdev;
+
+ return mlx5_eswitch_set_vport_mac(mdev->priv.eswitch, vf + 1, mac);
+}
+
+static int mlx5e_set_vf_vlan(struct net_device *dev, int vf, u16 vlan, u8 qos)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+ struct mlx5_core_dev *mdev = priv->mdev;
+
+ return mlx5_eswitch_set_vport_vlan(mdev->priv.eswitch, vf + 1,
+ vlan, qos);
+}
+
+static int mlx5_vport_link2ifla(u8 esw_link)
+{
+ switch (esw_link) {
+ case MLX5_ESW_VPORT_ADMIN_STATE_DOWN:
+ return IFLA_VF_LINK_STATE_DISABLE;
+ case MLX5_ESW_VPORT_ADMIN_STATE_UP:
+ return IFLA_VF_LINK_STATE_ENABLE;
+ }
+ return IFLA_VF_LINK_STATE_AUTO;
+}
+
+static int mlx5_ifla_link2vport(u8 ifla_link)
+{
+ switch (ifla_link) {
+ case IFLA_VF_LINK_STATE_DISABLE:
+ return MLX5_ESW_VPORT_ADMIN_STATE_DOWN;
+ case IFLA_VF_LINK_STATE_ENABLE:
+ return MLX5_ESW_VPORT_ADMIN_STATE_UP;
+ }
+ return MLX5_ESW_VPORT_ADMIN_STATE_AUTO;
+}
+
+static int mlx5e_set_vf_link_state(struct net_device *dev, int vf,
+ int link_state)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+ struct mlx5_core_dev *mdev = priv->mdev;
+
+ return mlx5_eswitch_set_vport_state(mdev->priv.eswitch, vf + 1,
+ mlx5_ifla_link2vport(link_state));
+}
+
+static int mlx5e_get_vf_config(struct net_device *dev,
+ int vf, struct ifla_vf_info *ivi)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+ struct mlx5_core_dev *mdev = priv->mdev;
+ int err;
+
+ err = mlx5_eswitch_get_vport_config(mdev->priv.eswitch, vf + 1, ivi);
+ if (err)
+ return err;
+ ivi->linkstate = mlx5_vport_link2ifla(ivi->linkstate);
+ return 0;
+}
+
+static int mlx5e_get_vf_stats(struct net_device *dev,
+ int vf, struct ifla_vf_stats *vf_stats)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+ struct mlx5_core_dev *mdev = priv->mdev;
+
+ return mlx5_eswitch_get_vport_stats(mdev->priv.eswitch, vf + 1,
+ vf_stats);
+}
+
static struct net_device_ops mlx5e_netdev_ops = {
.ndo_open = mlx5e_open,
.ndo_stop = mlx5e_close,
@@ -1937,6 +2035,7 @@ static struct net_device_ops mlx5e_netdev_ops = {
.ndo_vlan_rx_kill_vid = mlx5e_vlan_rx_kill_vid,
.ndo_set_features = mlx5e_set_features,
.ndo_change_mtu = mlx5e_change_mtu,
+ .ndo_do_ioctl = mlx5e_ioctl,
};
static int mlx5e_check_required_hca_cap(struct mlx5_core_dev *mdev)
@@ -2023,7 +2122,12 @@ static void mlx5e_set_netdev_dev_addr(struct net_device *netdev)
{
struct mlx5e_priv *priv = netdev_priv(netdev);
- mlx5_query_nic_vport_mac_address(priv->mdev, netdev->dev_addr);
+ mlx5_query_nic_vport_mac_address(priv->mdev, 0, netdev->dev_addr);
+ if (is_zero_ether_addr(netdev->dev_addr) &&
+ !MLX5_CAP_GEN(priv->mdev, vport_group_manager)) {
+ eth_hw_addr_random(netdev);
+ mlx5_core_info(priv->mdev, "Assigned random MAC address %pM\n", netdev->dev_addr);
+ }
}
static void mlx5e_build_netdev(struct net_device *netdev)
@@ -2036,6 +2140,14 @@ static void mlx5e_build_netdev(struct net_device *netdev)
if (priv->params.num_tc > 1)
mlx5e_netdev_ops.ndo_select_queue = mlx5e_select_queue;
+ if (MLX5_CAP_GEN(mdev, vport_group_manager)) {
+ mlx5e_netdev_ops.ndo_set_vf_mac = mlx5e_set_vf_mac;
+ mlx5e_netdev_ops.ndo_set_vf_vlan = mlx5e_set_vf_vlan;
+ mlx5e_netdev_ops.ndo_get_vf_config = mlx5e_get_vf_config;
+ mlx5e_netdev_ops.ndo_set_vf_link_state = mlx5e_set_vf_link_state;
+ mlx5e_netdev_ops.ndo_get_vf_stats = mlx5e_get_vf_stats;
+ }
+
netdev->netdev_ops = &mlx5e_netdev_ops;
netdev->watchdog_timeo = 15 * HZ;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
index cf0098596e85..dd959d929aad 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
@@ -33,8 +33,14 @@
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
+#include <net/busy_poll.h>
#include "en.h"
+static inline bool mlx5e_rx_hw_stamp(struct mlx5e_tstamp *tstamp)
+{
+ return tstamp->hwtstamp_config.rx_filter == HWTSTAMP_FILTER_ALL;
+}
+
static inline int mlx5e_alloc_rx_wqe(struct mlx5e_rq *rq,
struct mlx5e_rx_wqe *wqe, u16 ix)
{
@@ -189,6 +195,7 @@ static inline void mlx5e_build_rx_skb(struct mlx5_cqe64 *cqe,
{
struct net_device *netdev = rq->netdev;
u32 cqe_bcnt = be32_to_cpu(cqe->byte_cnt);
+ struct mlx5e_tstamp *tstamp = rq->tstamp;
int lro_num_seg;
skb_put(skb, cqe_bcnt);
@@ -201,6 +208,9 @@ static inline void mlx5e_build_rx_skb(struct mlx5_cqe64 *cqe,
rq->stats.lro_bytes += cqe_bcnt;
}
+ if (unlikely(mlx5e_rx_hw_stamp(tstamp)))
+ mlx5e_fill_hwstamp(tstamp, get_cqe_ts(cqe), skb_hwtstamps(skb));
+
mlx5e_handle_csum(netdev, cqe, rq, skb);
skb->protocol = eth_type_trans(skb, netdev);
@@ -215,16 +225,16 @@ static inline void mlx5e_build_rx_skb(struct mlx5_cqe64 *cqe,
be16_to_cpu(cqe->vlan_info));
}
-bool mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget)
+int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget)
{
struct mlx5e_rq *rq = container_of(cq, struct mlx5e_rq, cq);
- int i;
+ int work_done;
/* avoid accessing cq (dma coherent memory) if not needed */
if (!test_and_clear_bit(MLX5E_CQ_HAS_CQES, &cq->flags))
- return false;
+ return 0;
- for (i = 0; i < budget; i++) {
+ for (work_done = 0; work_done < budget; work_done++) {
struct mlx5e_rx_wqe *wqe;
struct mlx5_cqe64 *cqe;
struct sk_buff *skb;
@@ -269,10 +279,8 @@ wq_ll_pop:
/* ensure cq space is freed before enabling more cqes */
wmb();
- if (i == budget) {
+ if (work_done == budget)
set_bit(MLX5E_CQ_HAS_CQES, &cq->flags);
- return true;
- }
- return false;
+ return work_done;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
index 1341b1d3c421..2c3fba0fff54 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
@@ -92,11 +92,11 @@ static inline struct mlx5e_sq_dma *mlx5e_dma_get(struct mlx5e_sq *sq, u32 i)
return &sq->dma_fifo[i & sq->dma_fifo_mask];
}
-static void mlx5e_dma_unmap_wqe_err(struct mlx5e_sq *sq, struct sk_buff *skb)
+static void mlx5e_dma_unmap_wqe_err(struct mlx5e_sq *sq, u8 num_dma)
{
int i;
- for (i = 0; i < MLX5E_TX_SKB_CB(skb)->num_dma; i++) {
+ for (i = 0; i < num_dma; i++) {
struct mlx5e_sq_dma *last_pushed_dma =
mlx5e_dma_get(sq, --sq->dma_fifo_pc);
@@ -139,19 +139,28 @@ static inline u16 mlx5e_get_inline_hdr_size(struct mlx5e_sq *sq,
return MLX5E_MIN_INLINE;
}
-static inline void mlx5e_insert_vlan(void *start, struct sk_buff *skb, u16 ihs)
+static inline void mlx5e_tx_skb_pull_inline(unsigned char **skb_data,
+ unsigned int *skb_len,
+ unsigned int len)
+{
+ *skb_len -= len;
+ *skb_data += len;
+}
+
+static inline void mlx5e_insert_vlan(void *start, struct sk_buff *skb, u16 ihs,
+ unsigned char **skb_data,
+ unsigned int *skb_len)
{
struct vlan_ethhdr *vhdr = (struct vlan_ethhdr *)start;
int cpy1_sz = 2 * ETH_ALEN;
int cpy2_sz = ihs - cpy1_sz;
- skb_copy_from_linear_data(skb, vhdr, cpy1_sz);
- skb_pull_inline(skb, cpy1_sz);
+ memcpy(vhdr, *skb_data, cpy1_sz);
+ mlx5e_tx_skb_pull_inline(skb_data, skb_len, cpy1_sz);
vhdr->h_vlan_proto = skb->vlan_proto;
vhdr->h_vlan_TCI = cpu_to_be16(skb_vlan_tag_get(skb));
- skb_copy_from_linear_data(skb, &vhdr->h_vlan_encapsulated_proto,
- cpy2_sz);
- skb_pull_inline(skb, cpy2_sz);
+ memcpy(&vhdr->h_vlan_encapsulated_proto, *skb_data, cpy2_sz);
+ mlx5e_tx_skb_pull_inline(skb_data, skb_len, cpy2_sz);
}
static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
@@ -160,11 +169,14 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
u16 pi = sq->pc & wq->sz_m1;
struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(wq, pi);
+ struct mlx5e_tx_wqe_info *wi = &sq->wqe_info[pi];
struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl;
struct mlx5_wqe_eth_seg *eseg = &wqe->eth;
struct mlx5_wqe_data_seg *dseg;
+ unsigned char *skb_data = skb->data;
+ unsigned int skb_len = skb->len;
u8 opcode = MLX5_OPCODE_SEND;
dma_addr_t dma_addr = 0;
bool bf = false;
@@ -192,8 +204,8 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
opcode = MLX5_OPCODE_LSO;
ihs = skb_transport_offset(skb) + tcp_hdrlen(skb);
payload_len = skb->len - ihs;
- MLX5E_TX_SKB_CB(skb)->num_bytes = skb->len +
- (skb_shinfo(skb)->gso_segs - 1) * ihs;
+ wi->num_bytes = skb->len +
+ (skb_shinfo(skb)->gso_segs - 1) * ihs;
sq->stats.tso_packets++;
sq->stats.tso_bytes += payload_len;
} else {
@@ -201,16 +213,16 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
!skb->xmit_more &&
!skb_shinfo(skb)->nr_frags;
ihs = mlx5e_get_inline_hdr_size(sq, skb, bf);
- MLX5E_TX_SKB_CB(skb)->num_bytes = max_t(unsigned int, skb->len,
- ETH_ZLEN);
+ wi->num_bytes = max_t(unsigned int, skb->len, ETH_ZLEN);
}
if (skb_vlan_tag_present(skb)) {
- mlx5e_insert_vlan(eseg->inline_hdr_start, skb, ihs);
+ mlx5e_insert_vlan(eseg->inline_hdr_start, skb, ihs, &skb_data,
+ &skb_len);
ihs += VLAN_HLEN;
} else {
- skb_copy_from_linear_data(skb, eseg->inline_hdr_start, ihs);
- skb_pull_inline(skb, ihs);
+ memcpy(eseg->inline_hdr_start, skb_data, ihs);
+ mlx5e_tx_skb_pull_inline(&skb_data, &skb_len, ihs);
}
eseg->inline_hdr_sz = cpu_to_be16(ihs);
@@ -220,11 +232,11 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
MLX5_SEND_WQE_DS);
dseg = (struct mlx5_wqe_data_seg *)cseg + ds_cnt;
- MLX5E_TX_SKB_CB(skb)->num_dma = 0;
+ wi->num_dma = 0;
- headlen = skb_headlen(skb);
+ headlen = skb_len - skb->data_len;
if (headlen) {
- dma_addr = dma_map_single(sq->pdev, skb->data, headlen,
+ dma_addr = dma_map_single(sq->pdev, skb_data, headlen,
DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(sq->pdev, dma_addr)))
goto dma_unmap_wqe_err;
@@ -234,7 +246,7 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
dseg->byte_count = cpu_to_be32(headlen);
mlx5e_dma_push(sq, dma_addr, headlen, MLX5E_DMA_MAP_SINGLE);
- MLX5E_TX_SKB_CB(skb)->num_dma++;
+ wi->num_dma++;
dseg++;
}
@@ -253,23 +265,25 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
dseg->byte_count = cpu_to_be32(fsz);
mlx5e_dma_push(sq, dma_addr, fsz, MLX5E_DMA_MAP_PAGE);
- MLX5E_TX_SKB_CB(skb)->num_dma++;
+ wi->num_dma++;
dseg++;
}
- ds_cnt += MLX5E_TX_SKB_CB(skb)->num_dma;
+ ds_cnt += wi->num_dma;
cseg->opmod_idx_opcode = cpu_to_be32((sq->pc << 8) | opcode);
cseg->qpn_ds = cpu_to_be32((sq->sqn << 8) | ds_cnt);
sq->skb[pi] = skb;
- MLX5E_TX_SKB_CB(skb)->num_wqebbs = DIV_ROUND_UP(ds_cnt,
- MLX5_SEND_WQEBB_NUM_DS);
- sq->pc += MLX5E_TX_SKB_CB(skb)->num_wqebbs;
+ wi->num_wqebbs = DIV_ROUND_UP(ds_cnt, MLX5_SEND_WQEBB_NUM_DS);
+ sq->pc += wi->num_wqebbs;
+
+ netdev_tx_sent_queue(sq->txq, wi->num_bytes);
- netdev_tx_sent_queue(sq->txq, MLX5E_TX_SKB_CB(skb)->num_bytes);
+ if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))
+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
if (unlikely(!mlx5e_sq_has_room_for(sq, MLX5E_SQ_STOP_ROOM))) {
netif_tx_stop_queue(sq->txq);
@@ -280,7 +294,7 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
int bf_sz = 0;
if (bf && sq->uar_bf_map)
- bf_sz = MLX5E_TX_SKB_CB(skb)->num_wqebbs << 3;
+ bf_sz = wi->num_wqebbs << 3;
cseg->fm_ce_se = MLX5_WQE_CTRL_CQ_UPDATE;
mlx5e_tx_notify_hw(sq, wqe, bf_sz);
@@ -297,7 +311,7 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
dma_unmap_wqe_err:
sq->stats.dropped++;
- mlx5e_dma_unmap_wqe_err(sq, skb);
+ mlx5e_dma_unmap_wqe_err(sq, wi->num_dma);
dev_kfree_skb_any(skb);
@@ -352,6 +366,7 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq)
wqe_counter = be16_to_cpu(cqe->wqe_counter);
do {
+ struct mlx5e_tx_wqe_info *wi;
struct sk_buff *skb;
u16 ci;
int j;
@@ -360,6 +375,7 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq)
ci = sqcc & sq->wq.sz_m1;
skb = sq->skb[ci];
+ wi = &sq->wqe_info[ci];
if (unlikely(!skb)) { /* nop */
sq->stats.nop++;
@@ -367,7 +383,16 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq)
continue;
}
- for (j = 0; j < MLX5E_TX_SKB_CB(skb)->num_dma; j++) {
+ if (unlikely(skb_shinfo(skb)->tx_flags &
+ SKBTX_HW_TSTAMP)) {
+ struct skb_shared_hwtstamps hwts = {};
+
+ mlx5e_fill_hwstamp(sq->tstamp,
+ get_cqe_ts(cqe), &hwts);
+ skb_tstamp_tx(skb, &hwts);
+ }
+
+ for (j = 0; j < wi->num_dma; j++) {
struct mlx5e_sq_dma *dma =
mlx5e_dma_get(sq, dma_fifo_cc++);
@@ -375,8 +400,8 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq)
}
npkts++;
- nbytes += MLX5E_TX_SKB_CB(skb)->num_bytes;
- sqcc += MLX5E_TX_SKB_CB(skb)->num_wqebbs;
+ nbytes += wi->num_bytes;
+ sqcc += wi->num_wqebbs;
dev_kfree_skb(skb);
} while (!last_wqe);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c
index 2c7cb6755d1d..4ac8d716dbdd 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c
@@ -54,6 +54,7 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget)
struct mlx5e_channel *c = container_of(napi, struct mlx5e_channel,
napi);
bool busy = false;
+ int work_done;
int i;
clear_bit(MLX5E_CHANNEL_NAPI_SCHED, &c->flags);
@@ -61,26 +62,26 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget)
for (i = 0; i < c->num_tc; i++)
busy |= mlx5e_poll_tx_cq(&c->sq[i].cq);
- busy |= mlx5e_poll_rx_cq(&c->rq.cq, budget);
-
+ work_done = mlx5e_poll_rx_cq(&c->rq.cq, budget);
+ busy |= work_done == budget;
busy |= mlx5e_post_rx_wqes(&c->rq);
if (busy)
return budget;
- napi_complete(napi);
+ napi_complete_done(napi, work_done);
/* avoid losing completion event during/after polling cqs */
if (test_bit(MLX5E_CHANNEL_NAPI_SCHED, &c->flags)) {
napi_schedule(napi);
- return 0;
+ return work_done;
}
for (i = 0; i < c->num_tc; i++)
mlx5e_cq_arm(&c->sq[i].cq);
mlx5e_cq_arm(&c->rq.cq);
- return 0;
+ return work_done;
}
void mlx5e_completion_event(struct mlx5_core_cq *mcq)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c
index 713ead583347..23c244a7e5d7 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c
@@ -35,6 +35,9 @@
#include <linux/mlx5/driver.h>
#include <linux/mlx5/cmd.h>
#include "mlx5_core.h"
+#ifdef CONFIG_MLX5_CORE_EN
+#include "eswitch.h"
+#endif
enum {
MLX5_EQE_SIZE = sizeof(struct mlx5_eqe),
@@ -287,6 +290,11 @@ static int mlx5_eq_int(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
break;
#endif
+#ifdef CONFIG_MLX5_CORE_EN
+ case MLX5_EVENT_TYPE_NIC_VPORT_CHANGE:
+ mlx5_eswitch_vport_event(dev->priv.eswitch, eqe);
+ break;
+#endif
default:
mlx5_core_warn(dev, "Unhandled event 0x%x on EQ 0x%x\n",
eqe->type, eq->eqn);
@@ -459,6 +467,11 @@ int mlx5_start_eqs(struct mlx5_core_dev *dev)
if (MLX5_CAP_GEN(dev, pg))
async_event_mask |= (1ull << MLX5_EVENT_TYPE_PAGE_FAULT);
+ if (MLX5_CAP_GEN(dev, port_type) == MLX5_CAP_PORT_TYPE_ETH &&
+ MLX5_CAP_GEN(dev, vport_group_manager) &&
+ mlx5_core_is_pf(dev))
+ async_event_mask |= (1ull << MLX5_EVENT_TYPE_NIC_VPORT_CHANGE);
+
err = mlx5_create_map_eq(dev, &table->cmd_eq, MLX5_EQ_VEC_CMD,
MLX5_NUM_CMD_EQE, 1ull << MLX5_EVENT_TYPE_CMD,
"mlx5_cmd_eq", &dev->priv.uuari.uars[0]);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
new file mode 100644
index 000000000000..bc3d9f8a75c1
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
@@ -0,0 +1,1097 @@
+/*
+ * Copyright (c) 2015, Mellanox Technologies. 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.
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/mlx5_ifc.h>
+#include <linux/mlx5/vport.h>
+#include <linux/mlx5/fs.h>
+#include "mlx5_core.h"
+#include "eswitch.h"
+
+#define UPLINK_VPORT 0xFFFF
+
+#define MLX5_DEBUG_ESWITCH_MASK BIT(3)
+
+#define esw_info(dev, format, ...) \
+ pr_info("(%s): E-Switch: " format, (dev)->priv.name, ##__VA_ARGS__)
+
+#define esw_warn(dev, format, ...) \
+ pr_warn("(%s): E-Switch: " format, (dev)->priv.name, ##__VA_ARGS__)
+
+#define esw_debug(dev, format, ...) \
+ mlx5_core_dbg_mask(dev, MLX5_DEBUG_ESWITCH_MASK, format, ##__VA_ARGS__)
+
+enum {
+ MLX5_ACTION_NONE = 0,
+ MLX5_ACTION_ADD = 1,
+ MLX5_ACTION_DEL = 2,
+};
+
+/* E-Switch UC L2 table hash node */
+struct esw_uc_addr {
+ struct l2addr_node node;
+ u32 table_index;
+ u32 vport;
+};
+
+/* E-Switch MC FDB table hash node */
+struct esw_mc_addr { /* SRIOV only */
+ struct l2addr_node node;
+ struct mlx5_flow_rule *uplink_rule; /* Forward to uplink rule */
+ u32 refcnt;
+};
+
+/* Vport UC/MC hash node */
+struct vport_addr {
+ struct l2addr_node node;
+ u8 action;
+ u32 vport;
+ struct mlx5_flow_rule *flow_rule; /* SRIOV only */
+};
+
+enum {
+ UC_ADDR_CHANGE = BIT(0),
+ MC_ADDR_CHANGE = BIT(1),
+};
+
+/* Vport context events */
+#define SRIOV_VPORT_EVENTS (UC_ADDR_CHANGE | \
+ MC_ADDR_CHANGE)
+
+static int arm_vport_context_events_cmd(struct mlx5_core_dev *dev, u16 vport,
+ u32 events_mask)
+{
+ int in[MLX5_ST_SZ_DW(modify_nic_vport_context_in)];
+ int out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)];
+ void *nic_vport_ctx;
+ int err;
+
+ memset(out, 0, sizeof(out));
+ memset(in, 0, sizeof(in));
+
+ MLX5_SET(modify_nic_vport_context_in, in,
+ opcode, MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
+ MLX5_SET(modify_nic_vport_context_in, in, field_select.change_event, 1);
+ MLX5_SET(modify_nic_vport_context_in, in, vport_number, vport);
+ if (vport)
+ MLX5_SET(modify_nic_vport_context_in, in, other_vport, 1);
+ nic_vport_ctx = MLX5_ADDR_OF(modify_nic_vport_context_in,
+ in, nic_vport_context);
+
+ MLX5_SET(nic_vport_context, nic_vport_ctx, arm_change_event, 1);
+
+ if (events_mask & UC_ADDR_CHANGE)
+ MLX5_SET(nic_vport_context, nic_vport_ctx,
+ event_on_uc_address_change, 1);
+ if (events_mask & MC_ADDR_CHANGE)
+ MLX5_SET(nic_vport_context, nic_vport_ctx,
+ event_on_mc_address_change, 1);
+
+ err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+ if (err)
+ goto ex;
+ err = mlx5_cmd_status_to_err_v2(out);
+ if (err)
+ goto ex;
+ return 0;
+ex:
+ return err;
+}
+
+/* E-Switch vport context HW commands */
+static int query_esw_vport_context_cmd(struct mlx5_core_dev *mdev, u32 vport,
+ u32 *out, int outlen)
+{
+ u32 in[MLX5_ST_SZ_DW(query_esw_vport_context_in)];
+
+ memset(in, 0, sizeof(in));
+
+ MLX5_SET(query_nic_vport_context_in, in, opcode,
+ MLX5_CMD_OP_QUERY_ESW_VPORT_CONTEXT);
+
+ MLX5_SET(query_esw_vport_context_in, in, vport_number, vport);
+ if (vport)
+ MLX5_SET(query_esw_vport_context_in, in, other_vport, 1);
+
+ return mlx5_cmd_exec_check_status(mdev, in, sizeof(in), out, outlen);
+}
+
+static int query_esw_vport_cvlan(struct mlx5_core_dev *dev, u32 vport,
+ u16 *vlan, u8 *qos)
+{
+ u32 out[MLX5_ST_SZ_DW(query_esw_vport_context_out)];
+ int err;
+ bool cvlan_strip;
+ bool cvlan_insert;
+
+ memset(out, 0, sizeof(out));
+
+ *vlan = 0;
+ *qos = 0;
+
+ if (!MLX5_CAP_ESW(dev, vport_cvlan_strip) ||
+ !MLX5_CAP_ESW(dev, vport_cvlan_insert_if_not_exist))
+ return -ENOTSUPP;
+
+ err = query_esw_vport_context_cmd(dev, vport, out, sizeof(out));
+ if (err)
+ goto out;
+
+ cvlan_strip = MLX5_GET(query_esw_vport_context_out, out,
+ esw_vport_context.vport_cvlan_strip);
+
+ cvlan_insert = MLX5_GET(query_esw_vport_context_out, out,
+ esw_vport_context.vport_cvlan_insert);
+
+ if (cvlan_strip || cvlan_insert) {
+ *vlan = MLX5_GET(query_esw_vport_context_out, out,
+ esw_vport_context.cvlan_id);
+ *qos = MLX5_GET(query_esw_vport_context_out, out,
+ esw_vport_context.cvlan_pcp);
+ }
+
+ esw_debug(dev, "Query Vport[%d] cvlan: VLAN %d qos=%d\n",
+ vport, *vlan, *qos);
+out:
+ return err;
+}
+
+static int modify_esw_vport_context_cmd(struct mlx5_core_dev *dev, u16 vport,
+ void *in, int inlen)
+{
+ u32 out[MLX5_ST_SZ_DW(modify_esw_vport_context_out)];
+
+ memset(out, 0, sizeof(out));
+
+ MLX5_SET(modify_esw_vport_context_in, in, vport_number, vport);
+ if (vport)
+ MLX5_SET(modify_esw_vport_context_in, in, other_vport, 1);
+
+ MLX5_SET(modify_esw_vport_context_in, in, opcode,
+ MLX5_CMD_OP_MODIFY_ESW_VPORT_CONTEXT);
+
+ return mlx5_cmd_exec_check_status(dev, in, inlen,
+ out, sizeof(out));
+}
+
+static int modify_esw_vport_cvlan(struct mlx5_core_dev *dev, u32 vport,
+ u16 vlan, u8 qos, bool set)
+{
+ u32 in[MLX5_ST_SZ_DW(modify_esw_vport_context_in)];
+
+ memset(in, 0, sizeof(in));
+
+ if (!MLX5_CAP_ESW(dev, vport_cvlan_strip) ||
+ !MLX5_CAP_ESW(dev, vport_cvlan_insert_if_not_exist))
+ return -ENOTSUPP;
+
+ esw_debug(dev, "Set Vport[%d] VLAN %d qos %d set=%d\n",
+ vport, vlan, qos, set);
+
+ if (set) {
+ MLX5_SET(modify_esw_vport_context_in, in,
+ esw_vport_context.vport_cvlan_strip, 1);
+ /* insert only if no vlan in packet */
+ MLX5_SET(modify_esw_vport_context_in, in,
+ esw_vport_context.vport_cvlan_insert, 1);
+ MLX5_SET(modify_esw_vport_context_in, in,
+ esw_vport_context.cvlan_pcp, qos);
+ MLX5_SET(modify_esw_vport_context_in, in,
+ esw_vport_context.cvlan_id, vlan);
+ }
+
+ MLX5_SET(modify_esw_vport_context_in, in,
+ field_select.vport_cvlan_strip, 1);
+ MLX5_SET(modify_esw_vport_context_in, in,
+ field_select.vport_cvlan_insert, 1);
+
+ return modify_esw_vport_context_cmd(dev, vport, in, sizeof(in));
+}
+
+/* HW L2 Table (MPFS) management */
+static int set_l2_table_entry_cmd(struct mlx5_core_dev *dev, u32 index,
+ u8 *mac, u8 vlan_valid, u16 vlan)
+{
+ u32 in[MLX5_ST_SZ_DW(set_l2_table_entry_in)];
+ u32 out[MLX5_ST_SZ_DW(set_l2_table_entry_out)];
+ u8 *in_mac_addr;
+
+ memset(in, 0, sizeof(in));
+ memset(out, 0, sizeof(out));
+
+ MLX5_SET(set_l2_table_entry_in, in, opcode,
+ MLX5_CMD_OP_SET_L2_TABLE_ENTRY);
+ MLX5_SET(set_l2_table_entry_in, in, table_index, index);
+ MLX5_SET(set_l2_table_entry_in, in, vlan_valid, vlan_valid);
+ MLX5_SET(set_l2_table_entry_in, in, vlan, vlan);
+
+ in_mac_addr = MLX5_ADDR_OF(set_l2_table_entry_in, in, mac_address);
+ ether_addr_copy(&in_mac_addr[2], mac);
+
+ return mlx5_cmd_exec_check_status(dev, in, sizeof(in),
+ out, sizeof(out));
+}
+
+static int del_l2_table_entry_cmd(struct mlx5_core_dev *dev, u32 index)
+{
+ u32 in[MLX5_ST_SZ_DW(delete_l2_table_entry_in)];
+ u32 out[MLX5_ST_SZ_DW(delete_l2_table_entry_out)];
+
+ memset(in, 0, sizeof(in));
+ memset(out, 0, sizeof(out));
+
+ MLX5_SET(delete_l2_table_entry_in, in, opcode,
+ MLX5_CMD_OP_DELETE_L2_TABLE_ENTRY);
+ MLX5_SET(delete_l2_table_entry_in, in, table_index, index);
+ return mlx5_cmd_exec_check_status(dev, in, sizeof(in),
+ out, sizeof(out));
+}
+
+static int alloc_l2_table_index(struct mlx5_l2_table *l2_table, u32 *ix)
+{
+ int err = 0;
+
+ *ix = find_first_zero_bit(l2_table->bitmap, l2_table->size);
+ if (*ix >= l2_table->size)
+ err = -ENOSPC;
+ else
+ __set_bit(*ix, l2_table->bitmap);
+
+ return err;
+}
+
+static void free_l2_table_index(struct mlx5_l2_table *l2_table, u32 ix)
+{
+ __clear_bit(ix, l2_table->bitmap);
+}
+
+static int set_l2_table_entry(struct mlx5_core_dev *dev, u8 *mac,
+ u8 vlan_valid, u16 vlan,
+ u32 *index)
+{
+ struct mlx5_l2_table *l2_table = &dev->priv.eswitch->l2_table;
+ int err;
+
+ err = alloc_l2_table_index(l2_table, index);
+ if (err)
+ return err;
+
+ err = set_l2_table_entry_cmd(dev, *index, mac, vlan_valid, vlan);
+ if (err)
+ free_l2_table_index(l2_table, *index);
+
+ return err;
+}
+
+static void del_l2_table_entry(struct mlx5_core_dev *dev, u32 index)
+{
+ struct mlx5_l2_table *l2_table = &dev->priv.eswitch->l2_table;
+
+ del_l2_table_entry_cmd(dev, index);
+ free_l2_table_index(l2_table, index);
+}
+
+/* E-Switch FDB */
+static struct mlx5_flow_rule *
+esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u8 mac[ETH_ALEN], u32 vport)
+{
+ int match_header = MLX5_MATCH_OUTER_HEADERS;
+ struct mlx5_flow_destination dest;
+ struct mlx5_flow_rule *flow_rule = NULL;
+ u32 *match_v;
+ u32 *match_c;
+ u8 *dmac_v;
+ u8 *dmac_c;
+
+ match_v = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
+ match_c = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
+ if (!match_v || !match_c) {
+ pr_warn("FDB: Failed to alloc match parameters\n");
+ goto out;
+ }
+ dmac_v = MLX5_ADDR_OF(fte_match_param, match_v,
+ outer_headers.dmac_47_16);
+ dmac_c = MLX5_ADDR_OF(fte_match_param, match_c,
+ outer_headers.dmac_47_16);
+
+ ether_addr_copy(dmac_v, mac);
+ /* Match criteria mask */
+ memset(dmac_c, 0xff, 6);
+
+ dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
+ dest.vport_num = vport;
+
+ esw_debug(esw->dev,
+ "\tFDB add rule dmac_v(%pM) dmac_c(%pM) -> vport(%d)\n",
+ dmac_v, dmac_c, vport);
+ flow_rule =
+ mlx5_add_flow_rule(esw->fdb_table.fdb,
+ match_header,
+ match_c,
+ match_v,
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+ 0, &dest);
+ if (IS_ERR_OR_NULL(flow_rule)) {
+ pr_warn(
+ "FDB: Failed to add flow rule: dmac_v(%pM) dmac_c(%pM) -> vport(%d), err(%ld)\n",
+ dmac_v, dmac_c, vport, PTR_ERR(flow_rule));
+ flow_rule = NULL;
+ }
+out:
+ kfree(match_v);
+ kfree(match_c);
+ return flow_rule;
+}
+
+static int esw_create_fdb_table(struct mlx5_eswitch *esw, int nvports)
+{
+ int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
+ struct mlx5_core_dev *dev = esw->dev;
+ struct mlx5_flow_namespace *root_ns;
+ struct mlx5_flow_table *fdb;
+ struct mlx5_flow_group *g;
+ void *match_criteria;
+ int table_size;
+ u32 *flow_group_in;
+ u8 *dmac;
+ int err = 0;
+
+ esw_debug(dev, "Create FDB log_max_size(%d)\n",
+ MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size));
+
+ root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_FDB);
+ if (!root_ns) {
+ esw_warn(dev, "Failed to get FDB flow namespace\n");
+ return -ENOMEM;
+ }
+
+ flow_group_in = mlx5_vzalloc(inlen);
+ if (!flow_group_in)
+ return -ENOMEM;
+ memset(flow_group_in, 0, inlen);
+
+ table_size = BIT(MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size));
+ fdb = mlx5_create_flow_table(root_ns, 0, table_size);
+ if (IS_ERR_OR_NULL(fdb)) {
+ err = PTR_ERR(fdb);
+ esw_warn(dev, "Failed to create FDB Table err %d\n", err);
+ goto out;
+ }
+
+ MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,
+ MLX5_MATCH_OUTER_HEADERS);
+ match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria);
+ dmac = MLX5_ADDR_OF(fte_match_param, match_criteria, outer_headers.dmac_47_16);
+ MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
+ MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, table_size - 1);
+ eth_broadcast_addr(dmac);
+
+ g = mlx5_create_flow_group(fdb, flow_group_in);
+ if (IS_ERR_OR_NULL(g)) {
+ err = PTR_ERR(g);
+ esw_warn(dev, "Failed to create flow group err(%d)\n", err);
+ goto out;
+ }
+
+ esw->fdb_table.addr_grp = g;
+ esw->fdb_table.fdb = fdb;
+out:
+ kfree(flow_group_in);
+ if (err && !IS_ERR_OR_NULL(fdb))
+ mlx5_destroy_flow_table(fdb);
+ return err;
+}
+
+static void esw_destroy_fdb_table(struct mlx5_eswitch *esw)
+{
+ if (!esw->fdb_table.fdb)
+ return;
+
+ esw_debug(esw->dev, "Destroy FDB Table\n");
+ mlx5_destroy_flow_group(esw->fdb_table.addr_grp);
+ mlx5_destroy_flow_table(esw->fdb_table.fdb);
+ esw->fdb_table.fdb = NULL;
+ esw->fdb_table.addr_grp = NULL;
+}
+
+/* E-Switch vport UC/MC lists management */
+typedef int (*vport_addr_action)(struct mlx5_eswitch *esw,
+ struct vport_addr *vaddr);
+
+static int esw_add_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
+{
+ struct hlist_head *hash = esw->l2_table.l2_hash;
+ struct esw_uc_addr *esw_uc;
+ u8 *mac = vaddr->node.addr;
+ u32 vport = vaddr->vport;
+ int err;
+
+ esw_uc = l2addr_hash_find(hash, mac, struct esw_uc_addr);
+ if (esw_uc) {
+ esw_warn(esw->dev,
+ "Failed to set L2 mac(%pM) for vport(%d), mac is already in use by vport(%d)\n",
+ mac, vport, esw_uc->vport);
+ return -EEXIST;
+ }
+
+ esw_uc = l2addr_hash_add(hash, mac, struct esw_uc_addr, GFP_KERNEL);
+ if (!esw_uc)
+ return -ENOMEM;
+ esw_uc->vport = vport;
+
+ err = set_l2_table_entry(esw->dev, mac, 0, 0, &esw_uc->table_index);
+ if (err)
+ goto abort;
+
+ if (esw->fdb_table.fdb) /* SRIOV is enabled: Forward UC MAC to vport */
+ vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport);
+
+ esw_debug(esw->dev, "\tADDED UC MAC: vport[%d] %pM index:%d fr(%p)\n",
+ vport, mac, esw_uc->table_index, vaddr->flow_rule);
+ return err;
+abort:
+ l2addr_hash_del(esw_uc);
+ return err;
+}
+
+static int esw_del_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
+{
+ struct hlist_head *hash = esw->l2_table.l2_hash;
+ struct esw_uc_addr *esw_uc;
+ u8 *mac = vaddr->node.addr;
+ u32 vport = vaddr->vport;
+
+ esw_uc = l2addr_hash_find(hash, mac, struct esw_uc_addr);
+ if (!esw_uc || esw_uc->vport != vport) {
+ esw_debug(esw->dev,
+ "MAC(%pM) doesn't belong to vport (%d)\n",
+ mac, vport);
+ return -EINVAL;
+ }
+ esw_debug(esw->dev, "\tDELETE UC MAC: vport[%d] %pM index:%d fr(%p)\n",
+ vport, mac, esw_uc->table_index, vaddr->flow_rule);
+
+ del_l2_table_entry(esw->dev, esw_uc->table_index);
+
+ if (vaddr->flow_rule)
+ mlx5_del_flow_rule(vaddr->flow_rule);
+ vaddr->flow_rule = NULL;
+
+ l2addr_hash_del(esw_uc);
+ return 0;
+}
+
+static int esw_add_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
+{
+ struct hlist_head *hash = esw->mc_table;
+ struct esw_mc_addr *esw_mc;
+ u8 *mac = vaddr->node.addr;
+ u32 vport = vaddr->vport;
+
+ if (!esw->fdb_table.fdb)
+ return 0;
+
+ esw_mc = l2addr_hash_find(hash, mac, struct esw_mc_addr);
+ if (esw_mc)
+ goto add;
+
+ esw_mc = l2addr_hash_add(hash, mac, struct esw_mc_addr, GFP_KERNEL);
+ if (!esw_mc)
+ return -ENOMEM;
+
+ esw_mc->uplink_rule = /* Forward MC MAC to Uplink */
+ esw_fdb_set_vport_rule(esw, mac, UPLINK_VPORT);
+add:
+ esw_mc->refcnt++;
+ /* Forward MC MAC to vport */
+ vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport);
+ esw_debug(esw->dev,
+ "\tADDED MC MAC: vport[%d] %pM fr(%p) refcnt(%d) uplinkfr(%p)\n",
+ vport, mac, vaddr->flow_rule,
+ esw_mc->refcnt, esw_mc->uplink_rule);
+ return 0;
+}
+
+static int esw_del_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
+{
+ struct hlist_head *hash = esw->mc_table;
+ struct esw_mc_addr *esw_mc;
+ u8 *mac = vaddr->node.addr;
+ u32 vport = vaddr->vport;
+
+ if (!esw->fdb_table.fdb)
+ return 0;
+
+ esw_mc = l2addr_hash_find(hash, mac, struct esw_mc_addr);
+ if (!esw_mc) {
+ esw_warn(esw->dev,
+ "Failed to find eswitch MC addr for MAC(%pM) vport(%d)",
+ mac, vport);
+ return -EINVAL;
+ }
+ esw_debug(esw->dev,
+ "\tDELETE MC MAC: vport[%d] %pM fr(%p) refcnt(%d) uplinkfr(%p)\n",
+ vport, mac, vaddr->flow_rule, esw_mc->refcnt,
+ esw_mc->uplink_rule);
+
+ if (vaddr->flow_rule)
+ mlx5_del_flow_rule(vaddr->flow_rule);
+ vaddr->flow_rule = NULL;
+
+ if (--esw_mc->refcnt)
+ return 0;
+
+ if (esw_mc->uplink_rule)
+ mlx5_del_flow_rule(esw_mc->uplink_rule);
+
+ l2addr_hash_del(esw_mc);
+ return 0;
+}
+
+/* Apply vport UC/MC list to HW l2 table and FDB table */
+static void esw_apply_vport_addr_list(struct mlx5_eswitch *esw,
+ u32 vport_num, int list_type)
+{
+ struct mlx5_vport *vport = &esw->vports[vport_num];
+ bool is_uc = list_type == MLX5_NVPRT_LIST_TYPE_UC;
+ vport_addr_action vport_addr_add;
+ vport_addr_action vport_addr_del;
+ struct vport_addr *addr;
+ struct l2addr_node *node;
+ struct hlist_head *hash;
+ struct hlist_node *tmp;
+ int hi;
+
+ vport_addr_add = is_uc ? esw_add_uc_addr :
+ esw_add_mc_addr;
+ vport_addr_del = is_uc ? esw_del_uc_addr :
+ esw_del_mc_addr;
+
+ hash = is_uc ? vport->uc_list : vport->mc_list;
+ for_each_l2hash_node(node, tmp, hash, hi) {
+ addr = container_of(node, struct vport_addr, node);
+ switch (addr->action) {
+ case MLX5_ACTION_ADD:
+ vport_addr_add(esw, addr);
+ addr->action = MLX5_ACTION_NONE;
+ break;
+ case MLX5_ACTION_DEL:
+ vport_addr_del(esw, addr);
+ l2addr_hash_del(addr);
+ break;
+ }
+ }
+}
+
+/* Sync vport UC/MC list from vport context */
+static void esw_update_vport_addr_list(struct mlx5_eswitch *esw,
+ u32 vport_num, int list_type)
+{
+ struct mlx5_vport *vport = &esw->vports[vport_num];
+ bool is_uc = list_type == MLX5_NVPRT_LIST_TYPE_UC;
+ u8 (*mac_list)[ETH_ALEN];
+ struct l2addr_node *node;
+ struct vport_addr *addr;
+ struct hlist_head *hash;
+ struct hlist_node *tmp;
+ int size;
+ int err;
+ int hi;
+ int i;
+
+ size = is_uc ? MLX5_MAX_UC_PER_VPORT(esw->dev) :
+ MLX5_MAX_MC_PER_VPORT(esw->dev);
+
+ mac_list = kcalloc(size, ETH_ALEN, GFP_KERNEL);
+ if (!mac_list)
+ return;
+
+ hash = is_uc ? vport->uc_list : vport->mc_list;
+
+ for_each_l2hash_node(node, tmp, hash, hi) {
+ addr = container_of(node, struct vport_addr, node);
+ addr->action = MLX5_ACTION_DEL;
+ }
+
+ err = mlx5_query_nic_vport_mac_list(esw->dev, vport_num, list_type,
+ mac_list, &size);
+ if (err)
+ return;
+ esw_debug(esw->dev, "vport[%d] context update %s list size (%d)\n",
+ vport_num, is_uc ? "UC" : "MC", size);
+
+ for (i = 0; i < size; i++) {
+ if (is_uc && !is_valid_ether_addr(mac_list[i]))
+ continue;
+
+ if (!is_uc && !is_multicast_ether_addr(mac_list[i]))
+ continue;
+
+ addr = l2addr_hash_find(hash, mac_list[i], struct vport_addr);
+ if (addr) {
+ addr->action = MLX5_ACTION_NONE;
+ continue;
+ }
+
+ addr = l2addr_hash_add(hash, mac_list[i], struct vport_addr,
+ GFP_KERNEL);
+ if (!addr) {
+ esw_warn(esw->dev,
+ "Failed to add MAC(%pM) to vport[%d] DB\n",
+ mac_list[i], vport_num);
+ continue;
+ }
+ addr->vport = vport_num;
+ addr->action = MLX5_ACTION_ADD;
+ }
+ kfree(mac_list);
+}
+
+static void esw_vport_change_handler(struct work_struct *work)
+{
+ struct mlx5_vport *vport =
+ container_of(work, struct mlx5_vport, vport_change_handler);
+ struct mlx5_core_dev *dev = vport->dev;
+ struct mlx5_eswitch *esw = dev->priv.eswitch;
+ u8 mac[ETH_ALEN];
+
+ mlx5_query_nic_vport_mac_address(dev, vport->vport, mac);
+ esw_debug(dev, "vport[%d] Context Changed: perm mac: %pM\n",
+ vport->vport, mac);
+
+ if (vport->enabled_events & UC_ADDR_CHANGE) {
+ esw_update_vport_addr_list(esw, vport->vport,
+ MLX5_NVPRT_LIST_TYPE_UC);
+ esw_apply_vport_addr_list(esw, vport->vport,
+ MLX5_NVPRT_LIST_TYPE_UC);
+ }
+
+ if (vport->enabled_events & MC_ADDR_CHANGE) {
+ esw_update_vport_addr_list(esw, vport->vport,
+ MLX5_NVPRT_LIST_TYPE_MC);
+ esw_apply_vport_addr_list(esw, vport->vport,
+ MLX5_NVPRT_LIST_TYPE_MC);
+ }
+
+ esw_debug(esw->dev, "vport[%d] Context Changed: Done\n", vport->vport);
+ if (vport->enabled)
+ arm_vport_context_events_cmd(dev, vport->vport,
+ vport->enabled_events);
+}
+
+static void esw_enable_vport(struct mlx5_eswitch *esw, int vport_num,
+ int enable_events)
+{
+ struct mlx5_vport *vport = &esw->vports[vport_num];
+ unsigned long flags;
+
+ WARN_ON(vport->enabled);
+
+ esw_debug(esw->dev, "Enabling VPORT(%d)\n", vport_num);
+ mlx5_modify_vport_admin_state(esw->dev,
+ MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
+ vport_num,
+ MLX5_ESW_VPORT_ADMIN_STATE_AUTO);
+
+ /* Sync with current vport context */
+ vport->enabled_events = enable_events;
+ esw_vport_change_handler(&vport->vport_change_handler);
+
+ spin_lock_irqsave(&vport->lock, flags);
+ vport->enabled = true;
+ spin_unlock_irqrestore(&vport->lock, flags);
+
+ arm_vport_context_events_cmd(esw->dev, vport_num, enable_events);
+
+ esw->enabled_vports++;
+ esw_debug(esw->dev, "Enabled VPORT(%d)\n", vport_num);
+}
+
+static void esw_cleanup_vport(struct mlx5_eswitch *esw, u16 vport_num)
+{
+ struct mlx5_vport *vport = &esw->vports[vport_num];
+ struct l2addr_node *node;
+ struct vport_addr *addr;
+ struct hlist_node *tmp;
+ int hi;
+
+ for_each_l2hash_node(node, tmp, vport->uc_list, hi) {
+ addr = container_of(node, struct vport_addr, node);
+ addr->action = MLX5_ACTION_DEL;
+ }
+ esw_apply_vport_addr_list(esw, vport_num, MLX5_NVPRT_LIST_TYPE_UC);
+
+ for_each_l2hash_node(node, tmp, vport->mc_list, hi) {
+ addr = container_of(node, struct vport_addr, node);
+ addr->action = MLX5_ACTION_DEL;
+ }
+ esw_apply_vport_addr_list(esw, vport_num, MLX5_NVPRT_LIST_TYPE_MC);
+}
+
+static void esw_disable_vport(struct mlx5_eswitch *esw, int vport_num)
+{
+ struct mlx5_vport *vport = &esw->vports[vport_num];
+ unsigned long flags;
+
+ if (!vport->enabled)
+ return;
+
+ esw_debug(esw->dev, "Disabling vport(%d)\n", vport_num);
+ /* Mark this vport as disabled to discard new events */
+ spin_lock_irqsave(&vport->lock, flags);
+ vport->enabled = false;
+ vport->enabled_events = 0;
+ spin_unlock_irqrestore(&vport->lock, flags);
+
+ mlx5_modify_vport_admin_state(esw->dev,
+ MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
+ vport_num,
+ MLX5_ESW_VPORT_ADMIN_STATE_DOWN);
+ /* Wait for current already scheduled events to complete */
+ flush_workqueue(esw->work_queue);
+ /* Disable events from this vport */
+ arm_vport_context_events_cmd(esw->dev, vport->vport, 0);
+ /* We don't assume VFs will cleanup after themselves */
+ esw_cleanup_vport(esw, vport_num);
+ esw->enabled_vports--;
+}
+
+/* Public E-Switch API */
+int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs)
+{
+ int err;
+ int i;
+
+ if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) ||
+ MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
+ return 0;
+
+ if (!MLX5_CAP_GEN(esw->dev, eswitch_flow_table) ||
+ !MLX5_CAP_ESW_FLOWTABLE_FDB(esw->dev, ft_support)) {
+ esw_warn(esw->dev, "E-Switch FDB is not supported, aborting ...\n");
+ return -ENOTSUPP;
+ }
+
+ esw_info(esw->dev, "E-Switch enable SRIOV: nvfs(%d)\n", nvfs);
+
+ esw_disable_vport(esw, 0);
+
+ err = esw_create_fdb_table(esw, nvfs + 1);
+ if (err)
+ goto abort;
+
+ for (i = 0; i <= nvfs; i++)
+ esw_enable_vport(esw, i, SRIOV_VPORT_EVENTS);
+
+ esw_info(esw->dev, "SRIOV enabled: active vports(%d)\n",
+ esw->enabled_vports);
+ return 0;
+
+abort:
+ esw_enable_vport(esw, 0, UC_ADDR_CHANGE);
+ return err;
+}
+
+void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw)
+{
+ int i;
+
+ if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) ||
+ MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
+ return;
+
+ esw_info(esw->dev, "disable SRIOV: active vports(%d)\n",
+ esw->enabled_vports);
+
+ for (i = 0; i < esw->total_vports; i++)
+ esw_disable_vport(esw, i);
+
+ esw_destroy_fdb_table(esw);
+
+ /* VPORT 0 (PF) must be enabled back with non-sriov configuration */
+ esw_enable_vport(esw, 0, UC_ADDR_CHANGE);
+}
+
+int mlx5_eswitch_init(struct mlx5_core_dev *dev)
+{
+ int l2_table_size = 1 << MLX5_CAP_GEN(dev, log_max_l2_table);
+ int total_vports = 1 + pci_sriov_get_totalvfs(dev->pdev);
+ struct mlx5_eswitch *esw;
+ int vport_num;
+ int err;
+
+ if (!MLX5_CAP_GEN(dev, vport_group_manager) ||
+ MLX5_CAP_GEN(dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
+ return 0;
+
+ esw_info(dev,
+ "Total vports %d, l2 table size(%d), per vport: max uc(%d) max mc(%d)\n",
+ total_vports, l2_table_size,
+ MLX5_MAX_UC_PER_VPORT(dev),
+ MLX5_MAX_MC_PER_VPORT(dev));
+
+ esw = kzalloc(sizeof(*esw), GFP_KERNEL);
+ if (!esw)
+ return -ENOMEM;
+
+ esw->dev = dev;
+
+ esw->l2_table.bitmap = kcalloc(BITS_TO_LONGS(l2_table_size),
+ sizeof(uintptr_t), GFP_KERNEL);
+ if (!esw->l2_table.bitmap) {
+ err = -ENOMEM;
+ goto abort;
+ }
+ esw->l2_table.size = l2_table_size;
+
+ esw->work_queue = create_singlethread_workqueue("mlx5_esw_wq");
+ if (!esw->work_queue) {
+ err = -ENOMEM;
+ goto abort;
+ }
+
+ esw->vports = kcalloc(total_vports, sizeof(struct mlx5_vport),
+ GFP_KERNEL);
+ if (!esw->vports) {
+ err = -ENOMEM;
+ goto abort;
+ }
+
+ for (vport_num = 0; vport_num < total_vports; vport_num++) {
+ struct mlx5_vport *vport = &esw->vports[vport_num];
+
+ vport->vport = vport_num;
+ vport->dev = dev;
+ INIT_WORK(&vport->vport_change_handler,
+ esw_vport_change_handler);
+ spin_lock_init(&vport->lock);
+ }
+
+ esw->total_vports = total_vports;
+ esw->enabled_vports = 0;
+
+ dev->priv.eswitch = esw;
+ esw_enable_vport(esw, 0, UC_ADDR_CHANGE);
+ /* VF Vports will be enabled when SRIOV is enabled */
+ return 0;
+abort:
+ if (esw->work_queue)
+ destroy_workqueue(esw->work_queue);
+ kfree(esw->l2_table.bitmap);
+ kfree(esw->vports);
+ kfree(esw);
+ return err;
+}
+
+void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw)
+{
+ if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) ||
+ MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
+ return;
+
+ esw_info(esw->dev, "cleanup\n");
+ esw_disable_vport(esw, 0);
+
+ esw->dev->priv.eswitch = NULL;
+ destroy_workqueue(esw->work_queue);
+ kfree(esw->l2_table.bitmap);
+ kfree(esw->vports);
+ kfree(esw);
+}
+
+void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe)
+{
+ struct mlx5_eqe_vport_change *vc_eqe = &eqe->data.vport_change;
+ u16 vport_num = be16_to_cpu(vc_eqe->vport_num);
+ struct mlx5_vport *vport;
+
+ if (!esw) {
+ pr_warn("MLX5 E-Switch: vport %d got an event while eswitch is not initialized\n",
+ vport_num);
+ return;
+ }
+
+ vport = &esw->vports[vport_num];
+ spin_lock(&vport->lock);
+ if (vport->enabled)
+ queue_work(esw->work_queue, &vport->vport_change_handler);
+ spin_unlock(&vport->lock);
+}
+
+/* Vport Administration */
+#define ESW_ALLOWED(esw) \
+ (esw && MLX5_CAP_GEN(esw->dev, vport_group_manager) && mlx5_core_is_pf(esw->dev))
+#define LEGAL_VPORT(esw, vport) (vport >= 0 && vport < esw->total_vports)
+
+int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw,
+ int vport, u8 mac[ETH_ALEN])
+{
+ int err = 0;
+
+ if (!ESW_ALLOWED(esw))
+ return -EPERM;
+ if (!LEGAL_VPORT(esw, vport))
+ return -EINVAL;
+
+ err = mlx5_modify_nic_vport_mac_address(esw->dev, vport, mac);
+ if (err) {
+ mlx5_core_warn(esw->dev,
+ "Failed to mlx5_modify_nic_vport_mac vport(%d) err=(%d)\n",
+ vport, err);
+ return err;
+ }
+
+ return err;
+}
+
+int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw,
+ int vport, int link_state)
+{
+ if (!ESW_ALLOWED(esw))
+ return -EPERM;
+ if (!LEGAL_VPORT(esw, vport))
+ return -EINVAL;
+
+ return mlx5_modify_vport_admin_state(esw->dev,
+ MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
+ vport, link_state);
+}
+
+int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw,
+ int vport, struct ifla_vf_info *ivi)
+{
+ u16 vlan;
+ u8 qos;
+
+ if (!ESW_ALLOWED(esw))
+ return -EPERM;
+ if (!LEGAL_VPORT(esw, vport))
+ return -EINVAL;
+
+ memset(ivi, 0, sizeof(*ivi));
+ ivi->vf = vport - 1;
+
+ mlx5_query_nic_vport_mac_address(esw->dev, vport, ivi->mac);
+ ivi->linkstate = mlx5_query_vport_admin_state(esw->dev,
+ MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
+ vport);
+ query_esw_vport_cvlan(esw->dev, vport, &vlan, &qos);
+ ivi->vlan = vlan;
+ ivi->qos = qos;
+ ivi->spoofchk = 0;
+
+ return 0;
+}
+
+int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw,
+ int vport, u16 vlan, u8 qos)
+{
+ int set = 0;
+
+ if (!ESW_ALLOWED(esw))
+ return -EPERM;
+ if (!LEGAL_VPORT(esw, vport) || (vlan > 4095) || (qos > 7))
+ return -EINVAL;
+
+ if (vlan || qos)
+ set = 1;
+
+ return modify_esw_vport_cvlan(esw->dev, vport, vlan, qos, set);
+}
+
+int mlx5_eswitch_get_vport_stats(struct mlx5_eswitch *esw,
+ int vport,
+ struct ifla_vf_stats *vf_stats)
+{
+ int outlen = MLX5_ST_SZ_BYTES(query_vport_counter_out);
+ u32 in[MLX5_ST_SZ_DW(query_vport_counter_in)];
+ int err = 0;
+ u32 *out;
+
+ if (!ESW_ALLOWED(esw))
+ return -EPERM;
+ if (!LEGAL_VPORT(esw, vport))
+ return -EINVAL;
+
+ out = mlx5_vzalloc(outlen);
+ if (!out)
+ return -ENOMEM;
+
+ memset(in, 0, sizeof(in));
+
+ MLX5_SET(query_vport_counter_in, in, opcode,
+ MLX5_CMD_OP_QUERY_VPORT_COUNTER);
+ MLX5_SET(query_vport_counter_in, in, op_mod, 0);
+ MLX5_SET(query_vport_counter_in, in, vport_number, vport);
+ if (vport)
+ MLX5_SET(query_vport_counter_in, in, other_vport, 1);
+
+ memset(out, 0, outlen);
+ err = mlx5_cmd_exec(esw->dev, in, sizeof(in), out, outlen);
+ if (err)
+ goto free_out;
+
+ #define MLX5_GET_CTR(p, x) \
+ MLX5_GET64(query_vport_counter_out, p, x)
+
+ memset(vf_stats, 0, sizeof(*vf_stats));
+ vf_stats->rx_packets =
+ MLX5_GET_CTR(out, received_eth_unicast.packets) +
+ MLX5_GET_CTR(out, received_eth_multicast.packets) +
+ MLX5_GET_CTR(out, received_eth_broadcast.packets);
+
+ vf_stats->rx_bytes =
+ MLX5_GET_CTR(out, received_eth_unicast.octets) +
+ MLX5_GET_CTR(out, received_eth_multicast.octets) +
+ MLX5_GET_CTR(out, received_eth_broadcast.octets);
+
+ vf_stats->tx_packets =
+ MLX5_GET_CTR(out, transmitted_eth_unicast.packets) +
+ MLX5_GET_CTR(out, transmitted_eth_multicast.packets) +
+ MLX5_GET_CTR(out, transmitted_eth_broadcast.packets);
+
+ vf_stats->tx_bytes =
+ MLX5_GET_CTR(out, transmitted_eth_unicast.octets) +
+ MLX5_GET_CTR(out, transmitted_eth_multicast.octets) +
+ MLX5_GET_CTR(out, transmitted_eth_broadcast.octets);
+
+ vf_stats->multicast =
+ MLX5_GET_CTR(out, received_eth_multicast.packets);
+
+ vf_stats->broadcast =
+ MLX5_GET_CTR(out, received_eth_broadcast.packets);
+
+free_out:
+ kvfree(out);
+ return err;
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
new file mode 100644
index 000000000000..3416a428f70f
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2015, Mellanox Technologies, Ltd. 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 __MLX5_ESWITCH_H__
+#define __MLX5_ESWITCH_H__
+
+#include <linux/if_ether.h>
+#include <linux/if_link.h>
+#include <linux/mlx5/device.h>
+
+#define MLX5_MAX_UC_PER_VPORT(dev) \
+ (1 << MLX5_CAP_GEN(dev, log_max_current_uc_list))
+
+#define MLX5_MAX_MC_PER_VPORT(dev) \
+ (1 << MLX5_CAP_GEN(dev, log_max_current_mc_list))
+
+#define MLX5_L2_ADDR_HASH_SIZE (BIT(BITS_PER_BYTE))
+#define MLX5_L2_ADDR_HASH(addr) (addr[5])
+
+/* L2 -mac address based- hash helpers */
+struct l2addr_node {
+ struct hlist_node hlist;
+ u8 addr[ETH_ALEN];
+};
+
+#define for_each_l2hash_node(hn, tmp, hash, i) \
+ for (i = 0; i < MLX5_L2_ADDR_HASH_SIZE; i++) \
+ hlist_for_each_entry_safe(hn, tmp, &hash[i], hlist)
+
+#define l2addr_hash_find(hash, mac, type) ({ \
+ int ix = MLX5_L2_ADDR_HASH(mac); \
+ bool found = false; \
+ type *ptr = NULL; \
+ \
+ hlist_for_each_entry(ptr, &hash[ix], node.hlist) \
+ if (ether_addr_equal(ptr->node.addr, mac)) {\
+ found = true; \
+ break; \
+ } \
+ if (!found) \
+ ptr = NULL; \
+ ptr; \
+})
+
+#define l2addr_hash_add(hash, mac, type, gfp) ({ \
+ int ix = MLX5_L2_ADDR_HASH(mac); \
+ type *ptr = NULL; \
+ \
+ ptr = kzalloc(sizeof(type), gfp); \
+ if (ptr) { \
+ ether_addr_copy(ptr->node.addr, mac); \
+ hlist_add_head(&ptr->node.hlist, &hash[ix]);\
+ } \
+ ptr; \
+})
+
+#define l2addr_hash_del(ptr) ({ \
+ hlist_del(&ptr->node.hlist); \
+ kfree(ptr); \
+})
+
+struct mlx5_vport {
+ struct mlx5_core_dev *dev;
+ int vport;
+ struct hlist_head uc_list[MLX5_L2_ADDR_HASH_SIZE];
+ struct hlist_head mc_list[MLX5_L2_ADDR_HASH_SIZE];
+ struct work_struct vport_change_handler;
+
+ /* This spinlock protects access to vport data, between
+ * "esw_vport_disable" and ongoing interrupt "mlx5_eswitch_vport_event"
+ * once vport marked as disabled new interrupts are discarded.
+ */
+ spinlock_t lock; /* vport events sync */
+ bool enabled;
+ u16 enabled_events;
+};
+
+struct mlx5_l2_table {
+ struct hlist_head l2_hash[MLX5_L2_ADDR_HASH_SIZE];
+ u32 size;
+ unsigned long *bitmap;
+};
+
+struct mlx5_eswitch_fdb {
+ void *fdb;
+ struct mlx5_flow_group *addr_grp;
+};
+
+struct mlx5_eswitch {
+ struct mlx5_core_dev *dev;
+ struct mlx5_l2_table l2_table;
+ struct mlx5_eswitch_fdb fdb_table;
+ struct hlist_head mc_table[MLX5_L2_ADDR_HASH_SIZE];
+ struct workqueue_struct *work_queue;
+ struct mlx5_vport *vports;
+ int total_vports;
+ int enabled_vports;
+};
+
+/* E-Switch API */
+int mlx5_eswitch_init(struct mlx5_core_dev *dev);
+void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw);
+void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe);
+int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs);
+void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw);
+int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw,
+ int vport, u8 mac[ETH_ALEN]);
+int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw,
+ int vport, int link_state);
+int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw,
+ int vport, u16 vlan, u8 qos);
+int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw,
+ int vport, struct ifla_vf_info *ivi);
+int mlx5_eswitch_get_vport_stats(struct mlx5_eswitch *esw,
+ int vport,
+ struct ifla_vf_stats *vf_stats);
+
+#endif /* __MLX5_ESWITCH_H__ */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/flow_table.c b/drivers/net/ethernet/mellanox/mlx5/core/flow_table.c
deleted file mode 100644
index ca90b9bc3b95..000000000000
--- a/drivers/net/ethernet/mellanox/mlx5/core/flow_table.c
+++ /dev/null
@@ -1,422 +0,0 @@
-/*
- * Copyright (c) 2013-2015, Mellanox Technologies, Ltd. 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.
- */
-
-#include <linux/export.h>
-#include <linux/mlx5/driver.h>
-#include <linux/mlx5/flow_table.h>
-#include "mlx5_core.h"
-
-struct mlx5_ftg {
- struct mlx5_flow_table_group g;
- u32 id;
- u32 start_ix;
-};
-
-struct mlx5_flow_table {
- struct mlx5_core_dev *dev;
- u8 level;
- u8 type;
- u32 id;
- struct mutex mutex; /* sync bitmap alloc */
- u16 num_groups;
- struct mlx5_ftg *group;
- unsigned long *bitmap;
- u32 size;
-};
-
-static int mlx5_set_flow_entry_cmd(struct mlx5_flow_table *ft, u32 group_ix,
- u32 flow_index, void *flow_context)
-{
- u32 out[MLX5_ST_SZ_DW(set_fte_out)];
- u32 *in;
- void *in_flow_context;
- int fcdls =
- MLX5_GET(flow_context, flow_context, destination_list_size) *
- MLX5_ST_SZ_BYTES(dest_format_struct);
- int inlen = MLX5_ST_SZ_BYTES(set_fte_in) + fcdls;
- int err;
-
- in = mlx5_vzalloc(inlen);
- if (!in) {
- mlx5_core_warn(ft->dev, "failed to allocate inbox\n");
- return -ENOMEM;
- }
-
- MLX5_SET(set_fte_in, in, table_type, ft->type);
- MLX5_SET(set_fte_in, in, table_id, ft->id);
- MLX5_SET(set_fte_in, in, flow_index, flow_index);
- MLX5_SET(set_fte_in, in, opcode, MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY);
-
- in_flow_context = MLX5_ADDR_OF(set_fte_in, in, flow_context);
- memcpy(in_flow_context, flow_context,
- MLX5_ST_SZ_BYTES(flow_context) + fcdls);
-
- MLX5_SET(flow_context, in_flow_context, group_id,
- ft->group[group_ix].id);
-
- memset(out, 0, sizeof(out));
- err = mlx5_cmd_exec_check_status(ft->dev, in, inlen, out,
- sizeof(out));
- kvfree(in);
-
- return err;
-}
-
-static void mlx5_del_flow_entry_cmd(struct mlx5_flow_table *ft, u32 flow_index)
-{
- u32 in[MLX5_ST_SZ_DW(delete_fte_in)];
- u32 out[MLX5_ST_SZ_DW(delete_fte_out)];
-
- memset(in, 0, sizeof(in));
- memset(out, 0, sizeof(out));
-
-#define MLX5_SET_DFTEI(p, x, v) MLX5_SET(delete_fte_in, p, x, v)
- MLX5_SET_DFTEI(in, table_type, ft->type);
- MLX5_SET_DFTEI(in, table_id, ft->id);
- MLX5_SET_DFTEI(in, flow_index, flow_index);
- MLX5_SET_DFTEI(in, opcode, MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY);
-
- mlx5_cmd_exec_check_status(ft->dev, in, sizeof(in), out, sizeof(out));
-}
-
-static void mlx5_destroy_flow_group_cmd(struct mlx5_flow_table *ft, int i)
-{
- u32 in[MLX5_ST_SZ_DW(destroy_flow_group_in)];
- u32 out[MLX5_ST_SZ_DW(destroy_flow_group_out)];
-
- memset(in, 0, sizeof(in));
- memset(out, 0, sizeof(out));
-
-#define MLX5_SET_DFGI(p, x, v) MLX5_SET(destroy_flow_group_in, p, x, v)
- MLX5_SET_DFGI(in, table_type, ft->type);
- MLX5_SET_DFGI(in, table_id, ft->id);
- MLX5_SET_DFGI(in, opcode, MLX5_CMD_OP_DESTROY_FLOW_GROUP);
- MLX5_SET_DFGI(in, group_id, ft->group[i].id);
- mlx5_cmd_exec_check_status(ft->dev, in, sizeof(in), out, sizeof(out));
-}
-
-static int mlx5_create_flow_group_cmd(struct mlx5_flow_table *ft, int i)
-{
- u32 out[MLX5_ST_SZ_DW(create_flow_group_out)];
- u32 *in;
- void *in_match_criteria;
- int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
- struct mlx5_flow_table_group *g = &ft->group[i].g;
- u32 start_ix = ft->group[i].start_ix;
- u32 end_ix = start_ix + (1 << g->log_sz) - 1;
- int err;
-
- in = mlx5_vzalloc(inlen);
- if (!in) {
- mlx5_core_warn(ft->dev, "failed to allocate inbox\n");
- return -ENOMEM;
- }
- in_match_criteria = MLX5_ADDR_OF(create_flow_group_in, in,
- match_criteria);
-
- memset(out, 0, sizeof(out));
-
-#define MLX5_SET_CFGI(p, x, v) MLX5_SET(create_flow_group_in, p, x, v)
- MLX5_SET_CFGI(in, table_type, ft->type);
- MLX5_SET_CFGI(in, table_id, ft->id);
- MLX5_SET_CFGI(in, opcode, MLX5_CMD_OP_CREATE_FLOW_GROUP);
- MLX5_SET_CFGI(in, start_flow_index, start_ix);
- MLX5_SET_CFGI(in, end_flow_index, end_ix);
- MLX5_SET_CFGI(in, match_criteria_enable, g->match_criteria_enable);
-
- memcpy(in_match_criteria, g->match_criteria,
- MLX5_ST_SZ_BYTES(fte_match_param));
-
- err = mlx5_cmd_exec_check_status(ft->dev, in, inlen, out,
- sizeof(out));
- if (!err)
- ft->group[i].id = MLX5_GET(create_flow_group_out, out,
- group_id);
-
- kvfree(in);
-
- return err;
-}
-
-static void mlx5_destroy_flow_table_groups(struct mlx5_flow_table *ft)
-{
- int i;
-
- for (i = 0; i < ft->num_groups; i++)
- mlx5_destroy_flow_group_cmd(ft, i);
-}
-
-static int mlx5_create_flow_table_groups(struct mlx5_flow_table *ft)
-{
- int err;
- int i;
-
- for (i = 0; i < ft->num_groups; i++) {
- err = mlx5_create_flow_group_cmd(ft, i);
- if (err)
- goto err_destroy_flow_table_groups;
- }
-
- return 0;
-
-err_destroy_flow_table_groups:
- for (i--; i >= 0; i--)
- mlx5_destroy_flow_group_cmd(ft, i);
-
- return err;
-}
-
-static int mlx5_create_flow_table_cmd(struct mlx5_flow_table *ft)
-{
- u32 in[MLX5_ST_SZ_DW(create_flow_table_in)];
- u32 out[MLX5_ST_SZ_DW(create_flow_table_out)];
- int err;
-
- memset(in, 0, sizeof(in));
-
- MLX5_SET(create_flow_table_in, in, table_type, ft->type);
- MLX5_SET(create_flow_table_in, in, level, ft->level);
- MLX5_SET(create_flow_table_in, in, log_size, order_base_2(ft->size));
-
- MLX5_SET(create_flow_table_in, in, opcode,
- MLX5_CMD_OP_CREATE_FLOW_TABLE);
-
- memset(out, 0, sizeof(out));
- err = mlx5_cmd_exec_check_status(ft->dev, in, sizeof(in), out,
- sizeof(out));
- if (err)
- return err;
-
- ft->id = MLX5_GET(create_flow_table_out, out, table_id);
-
- return 0;
-}
-
-static void mlx5_destroy_flow_table_cmd(struct mlx5_flow_table *ft)
-{
- u32 in[MLX5_ST_SZ_DW(destroy_flow_table_in)];
- u32 out[MLX5_ST_SZ_DW(destroy_flow_table_out)];
-
- memset(in, 0, sizeof(in));
- memset(out, 0, sizeof(out));
-
-#define MLX5_SET_DFTI(p, x, v) MLX5_SET(destroy_flow_table_in, p, x, v)
- MLX5_SET_DFTI(in, table_type, ft->type);
- MLX5_SET_DFTI(in, table_id, ft->id);
- MLX5_SET_DFTI(in, opcode, MLX5_CMD_OP_DESTROY_FLOW_TABLE);
-
- mlx5_cmd_exec_check_status(ft->dev, in, sizeof(in), out, sizeof(out));
-}
-
-static int mlx5_find_group(struct mlx5_flow_table *ft, u8 match_criteria_enable,
- u32 *match_criteria, int *group_ix)
-{
- void *mc_outer = MLX5_ADDR_OF(fte_match_param, match_criteria,
- outer_headers);
- void *mc_misc = MLX5_ADDR_OF(fte_match_param, match_criteria,
- misc_parameters);
- void *mc_inner = MLX5_ADDR_OF(fte_match_param, match_criteria,
- inner_headers);
- int mc_outer_sz = MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4);
- int mc_misc_sz = MLX5_ST_SZ_BYTES(fte_match_set_misc);
- int mc_inner_sz = MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4);
- int i;
-
- for (i = 0; i < ft->num_groups; i++) {
- struct mlx5_flow_table_group *g = &ft->group[i].g;
- void *gmc_outer = MLX5_ADDR_OF(fte_match_param,
- g->match_criteria,
- outer_headers);
- void *gmc_misc = MLX5_ADDR_OF(fte_match_param,
- g->match_criteria,
- misc_parameters);
- void *gmc_inner = MLX5_ADDR_OF(fte_match_param,
- g->match_criteria,
- inner_headers);
-
- if (g->match_criteria_enable != match_criteria_enable)
- continue;
-
- if (match_criteria_enable & MLX5_MATCH_OUTER_HEADERS)
- if (memcmp(mc_outer, gmc_outer, mc_outer_sz))
- continue;
-
- if (match_criteria_enable & MLX5_MATCH_MISC_PARAMETERS)
- if (memcmp(mc_misc, gmc_misc, mc_misc_sz))
- continue;
-
- if (match_criteria_enable & MLX5_MATCH_INNER_HEADERS)
- if (memcmp(mc_inner, gmc_inner, mc_inner_sz))
- continue;
-
- *group_ix = i;
- return 0;
- }
-
- return -EINVAL;
-}
-
-static int alloc_flow_index(struct mlx5_flow_table *ft, int group_ix, u32 *ix)
-{
- struct mlx5_ftg *g = &ft->group[group_ix];
- int err = 0;
-
- mutex_lock(&ft->mutex);
-
- *ix = find_next_zero_bit(ft->bitmap, ft->size, g->start_ix);
- if (*ix >= (g->start_ix + (1 << g->g.log_sz)))
- err = -ENOSPC;
- else
- __set_bit(*ix, ft->bitmap);
-
- mutex_unlock(&ft->mutex);
-
- return err;
-}
-
-static void mlx5_free_flow_index(struct mlx5_flow_table *ft, u32 ix)
-{
- __clear_bit(ix, ft->bitmap);
-}
-
-int mlx5_add_flow_table_entry(void *flow_table, u8 match_criteria_enable,
- void *match_criteria, void *flow_context,
- u32 *flow_index)
-{
- struct mlx5_flow_table *ft = flow_table;
- int group_ix;
- int err;
-
- err = mlx5_find_group(ft, match_criteria_enable, match_criteria,
- &group_ix);
- if (err) {
- mlx5_core_warn(ft->dev, "mlx5_find_group failed\n");
- return err;
- }
-
- err = alloc_flow_index(ft, group_ix, flow_index);
- if (err) {
- mlx5_core_warn(ft->dev, "alloc_flow_index failed\n");
- return err;
- }
-
- return mlx5_set_flow_entry_cmd(ft, group_ix, *flow_index, flow_context);
-}
-EXPORT_SYMBOL(mlx5_add_flow_table_entry);
-
-void mlx5_del_flow_table_entry(void *flow_table, u32 flow_index)
-{
- struct mlx5_flow_table *ft = flow_table;
-
- mlx5_del_flow_entry_cmd(ft, flow_index);
- mlx5_free_flow_index(ft, flow_index);
-}
-EXPORT_SYMBOL(mlx5_del_flow_table_entry);
-
-void *mlx5_create_flow_table(struct mlx5_core_dev *dev, u8 level, u8 table_type,
- u16 num_groups,
- struct mlx5_flow_table_group *group)
-{
- struct mlx5_flow_table *ft;
- u32 start_ix = 0;
- u32 ft_size = 0;
- void *gr;
- void *bm;
- int err;
- int i;
-
- for (i = 0; i < num_groups; i++)
- ft_size += (1 << group[i].log_sz);
-
- ft = kzalloc(sizeof(*ft), GFP_KERNEL);
- gr = kcalloc(num_groups, sizeof(struct mlx5_ftg), GFP_KERNEL);
- bm = kcalloc(BITS_TO_LONGS(ft_size), sizeof(uintptr_t), GFP_KERNEL);
- if (!ft || !gr || !bm)
- goto err_free_ft;
-
- ft->group = gr;
- ft->bitmap = bm;
- ft->num_groups = num_groups;
- ft->level = level;
- ft->type = table_type;
- ft->size = ft_size;
- ft->dev = dev;
- mutex_init(&ft->mutex);
-
- for (i = 0; i < ft->num_groups; i++) {
- memcpy(&ft->group[i].g, &group[i], sizeof(*group));
- ft->group[i].start_ix = start_ix;
- start_ix += 1 << group[i].log_sz;
- }
-
- err = mlx5_create_flow_table_cmd(ft);
- if (err)
- goto err_free_ft;
-
- err = mlx5_create_flow_table_groups(ft);
- if (err)
- goto err_destroy_flow_table_cmd;
-
- return ft;
-
-err_destroy_flow_table_cmd:
- mlx5_destroy_flow_table_cmd(ft);
-
-err_free_ft:
- mlx5_core_warn(dev, "failed to alloc flow table\n");
- kfree(bm);
- kfree(gr);
- kfree(ft);
-
- return NULL;
-}
-EXPORT_SYMBOL(mlx5_create_flow_table);
-
-void mlx5_destroy_flow_table(void *flow_table)
-{
- struct mlx5_flow_table *ft = flow_table;
-
- mlx5_destroy_flow_table_groups(ft);
- mlx5_destroy_flow_table_cmd(ft);
- kfree(ft->bitmap);
- kfree(ft->group);
- kfree(ft);
-}
-EXPORT_SYMBOL(mlx5_destroy_flow_table);
-
-u32 mlx5_get_flow_table_id(void *flow_table)
-{
- struct mlx5_flow_table *ft = flow_table;
-
- return ft->id;
-}
-EXPORT_SYMBOL(mlx5_get_flow_table_id);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c
new file mode 100644
index 000000000000..a9894d2e8e26
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2015, Mellanox Technologies. 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.
+ */
+
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/device.h>
+#include <linux/mlx5/mlx5_ifc.h>
+
+#include "fs_core.h"
+#include "fs_cmd.h"
+#include "mlx5_core.h"
+
+int mlx5_cmd_update_root_ft(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft)
+{
+ u32 in[MLX5_ST_SZ_DW(set_flow_table_root_in)];
+ u32 out[MLX5_ST_SZ_DW(set_flow_table_root_out)];
+
+ memset(in, 0, sizeof(in));
+
+ MLX5_SET(set_flow_table_root_in, in, opcode,
+ MLX5_CMD_OP_SET_FLOW_TABLE_ROOT);
+ MLX5_SET(set_flow_table_root_in, in, table_type, ft->type);
+ MLX5_SET(set_flow_table_root_in, in, table_id, ft->id);
+
+ memset(out, 0, sizeof(out));
+ return mlx5_cmd_exec_check_status(dev, in, sizeof(in), out,
+ sizeof(out));
+}
+
+int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev,
+ enum fs_flow_table_type type, unsigned int level,
+ unsigned int log_size, struct mlx5_flow_table
+ *next_ft, unsigned int *table_id)
+{
+ u32 out[MLX5_ST_SZ_DW(create_flow_table_out)];
+ u32 in[MLX5_ST_SZ_DW(create_flow_table_in)];
+ int err;
+
+ memset(in, 0, sizeof(in));
+
+ MLX5_SET(create_flow_table_in, in, opcode,
+ MLX5_CMD_OP_CREATE_FLOW_TABLE);
+
+ if (next_ft) {
+ MLX5_SET(create_flow_table_in, in, table_miss_mode, 1);
+ MLX5_SET(create_flow_table_in, in, table_miss_id, next_ft->id);
+ }
+ MLX5_SET(create_flow_table_in, in, table_type, type);
+ MLX5_SET(create_flow_table_in, in, level, level);
+ MLX5_SET(create_flow_table_in, in, log_size, log_size);
+
+ memset(out, 0, sizeof(out));
+ err = mlx5_cmd_exec_check_status(dev, in, sizeof(in), out,
+ sizeof(out));
+
+ if (!err)
+ *table_id = MLX5_GET(create_flow_table_out, out,
+ table_id);
+ return err;
+}
+
+int mlx5_cmd_destroy_flow_table(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft)
+{
+ u32 in[MLX5_ST_SZ_DW(destroy_flow_table_in)];
+ u32 out[MLX5_ST_SZ_DW(destroy_flow_table_out)];
+
+ memset(in, 0, sizeof(in));
+ memset(out, 0, sizeof(out));
+
+ MLX5_SET(destroy_flow_table_in, in, opcode,
+ MLX5_CMD_OP_DESTROY_FLOW_TABLE);
+ MLX5_SET(destroy_flow_table_in, in, table_type, ft->type);
+ MLX5_SET(destroy_flow_table_in, in, table_id, ft->id);
+
+ return mlx5_cmd_exec_check_status(dev, in, sizeof(in), out,
+ sizeof(out));
+}
+
+int mlx5_cmd_modify_flow_table(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct mlx5_flow_table *next_ft)
+{
+ u32 in[MLX5_ST_SZ_DW(modify_flow_table_in)];
+ u32 out[MLX5_ST_SZ_DW(modify_flow_table_out)];
+
+ memset(in, 0, sizeof(in));
+ memset(out, 0, sizeof(out));
+
+ MLX5_SET(modify_flow_table_in, in, opcode,
+ MLX5_CMD_OP_MODIFY_FLOW_TABLE);
+ MLX5_SET(modify_flow_table_in, in, table_type, ft->type);
+ MLX5_SET(modify_flow_table_in, in, table_id, ft->id);
+ MLX5_SET(modify_flow_table_in, in, modify_field_select,
+ MLX5_MODIFY_FLOW_TABLE_MISS_TABLE_ID);
+ if (next_ft) {
+ MLX5_SET(modify_flow_table_in, in, table_miss_mode, 1);
+ MLX5_SET(modify_flow_table_in, in, table_miss_id, next_ft->id);
+ } else {
+ MLX5_SET(modify_flow_table_in, in, table_miss_mode, 0);
+ }
+
+ return mlx5_cmd_exec_check_status(dev, in, sizeof(in), out,
+ sizeof(out));
+}
+
+int mlx5_cmd_create_flow_group(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ u32 *in,
+ unsigned int *group_id)
+{
+ int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
+ u32 out[MLX5_ST_SZ_DW(create_flow_group_out)];
+ int err;
+
+ memset(out, 0, sizeof(out));
+
+ MLX5_SET(create_flow_group_in, in, opcode,
+ MLX5_CMD_OP_CREATE_FLOW_GROUP);
+ MLX5_SET(create_flow_group_in, in, table_type, ft->type);
+ MLX5_SET(create_flow_group_in, in, table_id, ft->id);
+
+ err = mlx5_cmd_exec_check_status(dev, in,
+ inlen, out,
+ sizeof(out));
+ if (!err)
+ *group_id = MLX5_GET(create_flow_group_out, out,
+ group_id);
+
+ return err;
+}
+
+int mlx5_cmd_destroy_flow_group(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned int group_id)
+{
+ u32 out[MLX5_ST_SZ_DW(destroy_flow_group_out)];
+ u32 in[MLX5_ST_SZ_DW(destroy_flow_group_in)];
+
+ memset(in, 0, sizeof(in));
+ memset(out, 0, sizeof(out));
+
+ MLX5_SET(destroy_flow_group_in, in, opcode,
+ MLX5_CMD_OP_DESTROY_FLOW_GROUP);
+ MLX5_SET(destroy_flow_group_in, in, table_type, ft->type);
+ MLX5_SET(destroy_flow_group_in, in, table_id, ft->id);
+ MLX5_SET(destroy_flow_group_in, in, group_id, group_id);
+
+ return mlx5_cmd_exec_check_status(dev, in, sizeof(in), out,
+ sizeof(out));
+}
+
+static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev,
+ int opmod, int modify_mask,
+ struct mlx5_flow_table *ft,
+ unsigned group_id,
+ struct fs_fte *fte)
+{
+ unsigned int inlen = MLX5_ST_SZ_BYTES(set_fte_in) +
+ fte->dests_size * MLX5_ST_SZ_BYTES(dest_format_struct);
+ u32 out[MLX5_ST_SZ_DW(set_fte_out)];
+ struct mlx5_flow_rule *dst;
+ void *in_flow_context;
+ void *in_match_value;
+ void *in_dests;
+ u32 *in;
+ int err;
+
+ in = mlx5_vzalloc(inlen);
+ if (!in) {
+ mlx5_core_warn(dev, "failed to allocate inbox\n");
+ return -ENOMEM;
+ }
+
+ MLX5_SET(set_fte_in, in, opcode, MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY);
+ MLX5_SET(set_fte_in, in, op_mod, opmod);
+ MLX5_SET(set_fte_in, in, modify_enable_mask, modify_mask);
+ MLX5_SET(set_fte_in, in, table_type, ft->type);
+ MLX5_SET(set_fte_in, in, table_id, ft->id);
+ MLX5_SET(set_fte_in, in, flow_index, fte->index);
+
+ in_flow_context = MLX5_ADDR_OF(set_fte_in, in, flow_context);
+ MLX5_SET(flow_context, in_flow_context, group_id, group_id);
+ MLX5_SET(flow_context, in_flow_context, flow_tag, fte->flow_tag);
+ MLX5_SET(flow_context, in_flow_context, action, fte->action);
+ MLX5_SET(flow_context, in_flow_context, destination_list_size,
+ fte->dests_size);
+ in_match_value = MLX5_ADDR_OF(flow_context, in_flow_context,
+ match_value);
+ memcpy(in_match_value, &fte->val, MLX5_ST_SZ_BYTES(fte_match_param));
+
+ in_dests = MLX5_ADDR_OF(flow_context, in_flow_context, destination);
+ list_for_each_entry(dst, &fte->node.children, node.list) {
+ unsigned int id;
+
+ MLX5_SET(dest_format_struct, in_dests, destination_type,
+ dst->dest_attr.type);
+ if (dst->dest_attr.type ==
+ MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE)
+ id = dst->dest_attr.ft->id;
+ else
+ id = dst->dest_attr.tir_num;
+ MLX5_SET(dest_format_struct, in_dests, destination_id, id);
+ in_dests += MLX5_ST_SZ_BYTES(dest_format_struct);
+ }
+ memset(out, 0, sizeof(out));
+ err = mlx5_cmd_exec_check_status(dev, in, inlen, out,
+ sizeof(out));
+ kvfree(in);
+
+ return err;
+}
+
+int mlx5_cmd_create_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned group_id,
+ struct fs_fte *fte)
+{
+ return mlx5_cmd_set_fte(dev, 0, 0, ft, group_id, fte);
+}
+
+int mlx5_cmd_update_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned group_id,
+ struct fs_fte *fte)
+{
+ int opmod;
+ int modify_mask;
+ int atomic_mod_cap = MLX5_CAP_FLOWTABLE(dev,
+ flow_table_properties_nic_receive.
+ flow_modify_en);
+ if (!atomic_mod_cap)
+ return -ENOTSUPP;
+ opmod = 1;
+ modify_mask = 1 <<
+ MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST;
+
+ return mlx5_cmd_set_fte(dev, opmod, modify_mask, ft, group_id, fte);
+}
+
+int mlx5_cmd_delete_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned int index)
+{
+ u32 out[MLX5_ST_SZ_DW(delete_fte_out)];
+ u32 in[MLX5_ST_SZ_DW(delete_fte_in)];
+ int err;
+
+ memset(in, 0, sizeof(in));
+ memset(out, 0, sizeof(out));
+
+ MLX5_SET(delete_fte_in, in, opcode, MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY);
+ MLX5_SET(delete_fte_in, in, table_type, ft->type);
+ MLX5_SET(delete_fte_in, in, table_id, ft->id);
+ MLX5_SET(delete_fte_in, in, flow_index, index);
+
+ err = mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, sizeof(out));
+
+ return err;
+}
diff --git a/include/linux/mlx5/flow_table.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h
index 5f922c6d4fc2..9814d4784803 100644
--- a/include/linux/mlx5/flow_table.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2015, Mellanox Technologies, Ltd. All rights reserved.
+ * Copyright (c) 2015, Mellanox Technologies. 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
@@ -30,25 +30,43 @@
* SOFTWARE.
*/
-#ifndef MLX5_FLOW_TABLE_H
-#define MLX5_FLOW_TABLE_H
+#ifndef _MLX5_FS_CMD_
+#define _MLX5_FS_CMD_
-#include <linux/mlx5/driver.h>
+int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev,
+ enum fs_flow_table_type type, unsigned int level,
+ unsigned int log_size, struct mlx5_flow_table
+ *next_ft, unsigned int *table_id);
-struct mlx5_flow_table_group {
- u8 log_sz;
- u8 match_criteria_enable;
- u32 match_criteria[MLX5_ST_SZ_DW(fte_match_param)];
-};
+int mlx5_cmd_destroy_flow_table(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft);
-void *mlx5_create_flow_table(struct mlx5_core_dev *dev, u8 level, u8 table_type,
- u16 num_groups,
- struct mlx5_flow_table_group *group);
-void mlx5_destroy_flow_table(void *flow_table);
-int mlx5_add_flow_table_entry(void *flow_table, u8 match_criteria_enable,
- void *match_criteria, void *flow_context,
- u32 *flow_index);
-void mlx5_del_flow_table_entry(void *flow_table, u32 flow_index);
-u32 mlx5_get_flow_table_id(void *flow_table);
+int mlx5_cmd_modify_flow_table(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct mlx5_flow_table *next_ft);
-#endif /* MLX5_FLOW_TABLE_H */
+int mlx5_cmd_create_flow_group(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ u32 *in, unsigned int *group_id);
+
+int mlx5_cmd_destroy_flow_group(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned int group_id);
+
+int mlx5_cmd_create_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned group_id,
+ struct fs_fte *fte);
+
+int mlx5_cmd_update_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned group_id,
+ struct fs_fte *fte);
+
+int mlx5_cmd_delete_fte(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ unsigned int index);
+
+int mlx5_cmd_update_root_ft(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft);
+#endif
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
new file mode 100644
index 000000000000..6f68dba8d7ed
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
@@ -0,0 +1,1514 @@
+/*
+ * Copyright (c) 2015, Mellanox Technologies. 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.
+ */
+
+#include <linux/mutex.h>
+#include <linux/mlx5/driver.h>
+
+#include "mlx5_core.h"
+#include "fs_core.h"
+#include "fs_cmd.h"
+
+#define INIT_TREE_NODE_ARRAY_SIZE(...) (sizeof((struct init_tree_node[]){__VA_ARGS__}) /\
+ sizeof(struct init_tree_node))
+
+#define ADD_PRIO(num_prios_val, min_level_val, max_ft_val, caps_val,\
+ ...) {.type = FS_TYPE_PRIO,\
+ .min_ft_level = min_level_val,\
+ .max_ft = max_ft_val,\
+ .num_leaf_prios = num_prios_val,\
+ .caps = caps_val,\
+ .children = (struct init_tree_node[]) {__VA_ARGS__},\
+ .ar_size = INIT_TREE_NODE_ARRAY_SIZE(__VA_ARGS__) \
+}
+
+#define ADD_MULTIPLE_PRIO(num_prios_val, max_ft_val, ...)\
+ ADD_PRIO(num_prios_val, 0, max_ft_val, {},\
+ __VA_ARGS__)\
+
+#define ADD_NS(...) {.type = FS_TYPE_NAMESPACE,\
+ .children = (struct init_tree_node[]) {__VA_ARGS__},\
+ .ar_size = INIT_TREE_NODE_ARRAY_SIZE(__VA_ARGS__) \
+}
+
+#define INIT_CAPS_ARRAY_SIZE(...) (sizeof((long[]){__VA_ARGS__}) /\
+ sizeof(long))
+
+#define FS_CAP(cap) (__mlx5_bit_off(flow_table_nic_cap, cap))
+
+#define FS_REQUIRED_CAPS(...) {.arr_sz = INIT_CAPS_ARRAY_SIZE(__VA_ARGS__), \
+ .caps = (long[]) {__VA_ARGS__} }
+
+#define LEFTOVERS_MAX_FT 1
+#define LEFTOVERS_NUM_PRIOS 1
+#define BY_PASS_PRIO_MAX_FT 1
+#define BY_PASS_MIN_LEVEL (KENREL_MIN_LEVEL + MLX5_BY_PASS_NUM_PRIOS +\
+ LEFTOVERS_MAX_FT)
+
+#define KERNEL_MAX_FT 2
+#define KERNEL_NUM_PRIOS 1
+#define KENREL_MIN_LEVEL 2
+
+struct node_caps {
+ size_t arr_sz;
+ long *caps;
+};
+static struct init_tree_node {
+ enum fs_node_type type;
+ struct init_tree_node *children;
+ int ar_size;
+ struct node_caps caps;
+ int min_ft_level;
+ int num_leaf_prios;
+ int prio;
+ int max_ft;
+} root_fs = {
+ .type = FS_TYPE_NAMESPACE,
+ .ar_size = 3,
+ .children = (struct init_tree_node[]) {
+ ADD_PRIO(0, BY_PASS_MIN_LEVEL, 0,
+ FS_REQUIRED_CAPS(FS_CAP(flow_table_properties_nic_receive.flow_modify_en),
+ FS_CAP(flow_table_properties_nic_receive.modify_root),
+ FS_CAP(flow_table_properties_nic_receive.identified_miss_table_mode),
+ FS_CAP(flow_table_properties_nic_receive.flow_table_modify)),
+ ADD_NS(ADD_MULTIPLE_PRIO(MLX5_BY_PASS_NUM_PRIOS, BY_PASS_PRIO_MAX_FT))),
+ ADD_PRIO(0, KENREL_MIN_LEVEL, 0, {},
+ ADD_NS(ADD_MULTIPLE_PRIO(KERNEL_NUM_PRIOS, KERNEL_MAX_FT))),
+ ADD_PRIO(0, BY_PASS_MIN_LEVEL, 0,
+ FS_REQUIRED_CAPS(FS_CAP(flow_table_properties_nic_receive.flow_modify_en),
+ FS_CAP(flow_table_properties_nic_receive.modify_root),
+ FS_CAP(flow_table_properties_nic_receive.identified_miss_table_mode),
+ FS_CAP(flow_table_properties_nic_receive.flow_table_modify)),
+ ADD_NS(ADD_MULTIPLE_PRIO(LEFTOVERS_NUM_PRIOS, LEFTOVERS_MAX_FT))),
+ }
+};
+
+enum fs_i_mutex_lock_class {
+ FS_MUTEX_GRANDPARENT,
+ FS_MUTEX_PARENT,
+ FS_MUTEX_CHILD
+};
+
+static void del_rule(struct fs_node *node);
+static void del_flow_table(struct fs_node *node);
+static void del_flow_group(struct fs_node *node);
+static void del_fte(struct fs_node *node);
+
+static void tree_init_node(struct fs_node *node,
+ unsigned int refcount,
+ void (*remove_func)(struct fs_node *))
+{
+ atomic_set(&node->refcount, refcount);
+ INIT_LIST_HEAD(&node->list);
+ INIT_LIST_HEAD(&node->children);
+ mutex_init(&node->lock);
+ node->remove_func = remove_func;
+}
+
+static void tree_add_node(struct fs_node *node, struct fs_node *parent)
+{
+ if (parent)
+ atomic_inc(&parent->refcount);
+ node->parent = parent;
+
+ /* Parent is the root */
+ if (!parent)
+ node->root = node;
+ else
+ node->root = parent->root;
+}
+
+static void tree_get_node(struct fs_node *node)
+{
+ atomic_inc(&node->refcount);
+}
+
+static void nested_lock_ref_node(struct fs_node *node,
+ enum fs_i_mutex_lock_class class)
+{
+ if (node) {
+ mutex_lock_nested(&node->lock, class);
+ atomic_inc(&node->refcount);
+ }
+}
+
+static void lock_ref_node(struct fs_node *node)
+{
+ if (node) {
+ mutex_lock(&node->lock);
+ atomic_inc(&node->refcount);
+ }
+}
+
+static void unlock_ref_node(struct fs_node *node)
+{
+ if (node) {
+ atomic_dec(&node->refcount);
+ mutex_unlock(&node->lock);
+ }
+}
+
+static void tree_put_node(struct fs_node *node)
+{
+ struct fs_node *parent_node = node->parent;
+
+ lock_ref_node(parent_node);
+ if (atomic_dec_and_test(&node->refcount)) {
+ if (parent_node)
+ list_del_init(&node->list);
+ if (node->remove_func)
+ node->remove_func(node);
+ kfree(node);
+ node = NULL;
+ }
+ unlock_ref_node(parent_node);
+ if (!node && parent_node)
+ tree_put_node(parent_node);
+}
+
+static int tree_remove_node(struct fs_node *node)
+{
+ if (atomic_read(&node->refcount) > 1)
+ return -EPERM;
+ tree_put_node(node);
+ return 0;
+}
+
+static struct fs_prio *find_prio(struct mlx5_flow_namespace *ns,
+ unsigned int prio)
+{
+ struct fs_prio *iter_prio;
+
+ fs_for_each_prio(iter_prio, ns) {
+ if (iter_prio->prio == prio)
+ return iter_prio;
+ }
+
+ return NULL;
+}
+
+static unsigned int find_next_free_level(struct fs_prio *prio)
+{
+ if (!list_empty(&prio->node.children)) {
+ struct mlx5_flow_table *ft;
+
+ ft = list_last_entry(&prio->node.children,
+ struct mlx5_flow_table,
+ node.list);
+ return ft->level + 1;
+ }
+ return prio->start_level;
+}
+
+static bool masked_memcmp(void *mask, void *val1, void *val2, size_t size)
+{
+ unsigned int i;
+
+ for (i = 0; i < size; i++, mask++, val1++, val2++)
+ if ((*((u8 *)val1) & (*(u8 *)mask)) !=
+ ((*(u8 *)val2) & (*(u8 *)mask)))
+ return false;
+
+ return true;
+}
+
+static bool compare_match_value(struct mlx5_flow_group_mask *mask,
+ void *fte_param1, void *fte_param2)
+{
+ if (mask->match_criteria_enable &
+ 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_OUTER_HEADERS) {
+ void *fte_match1 = MLX5_ADDR_OF(fte_match_param,
+ fte_param1, outer_headers);
+ void *fte_match2 = MLX5_ADDR_OF(fte_match_param,
+ fte_param2, outer_headers);
+ void *fte_mask = MLX5_ADDR_OF(fte_match_param,
+ mask->match_criteria, outer_headers);
+
+ if (!masked_memcmp(fte_mask, fte_match1, fte_match2,
+ MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4)))
+ return false;
+ }
+
+ if (mask->match_criteria_enable &
+ 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS) {
+ void *fte_match1 = MLX5_ADDR_OF(fte_match_param,
+ fte_param1, misc_parameters);
+ void *fte_match2 = MLX5_ADDR_OF(fte_match_param,
+ fte_param2, misc_parameters);
+ void *fte_mask = MLX5_ADDR_OF(fte_match_param,
+ mask->match_criteria, misc_parameters);
+
+ if (!masked_memcmp(fte_mask, fte_match1, fte_match2,
+ MLX5_ST_SZ_BYTES(fte_match_set_misc)))
+ return false;
+ }
+
+ if (mask->match_criteria_enable &
+ 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_INNER_HEADERS) {
+ void *fte_match1 = MLX5_ADDR_OF(fte_match_param,
+ fte_param1, inner_headers);
+ void *fte_match2 = MLX5_ADDR_OF(fte_match_param,
+ fte_param2, inner_headers);
+ void *fte_mask = MLX5_ADDR_OF(fte_match_param,
+ mask->match_criteria, inner_headers);
+
+ if (!masked_memcmp(fte_mask, fte_match1, fte_match2,
+ MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4)))
+ return false;
+ }
+ return true;
+}
+
+static bool compare_match_criteria(u8 match_criteria_enable1,
+ u8 match_criteria_enable2,
+ void *mask1, void *mask2)
+{
+ return match_criteria_enable1 == match_criteria_enable2 &&
+ !memcmp(mask1, mask2, MLX5_ST_SZ_BYTES(fte_match_param));
+}
+
+static struct mlx5_flow_root_namespace *find_root(struct fs_node *node)
+{
+ struct fs_node *root;
+ struct mlx5_flow_namespace *ns;
+
+ root = node->root;
+
+ if (WARN_ON(root->type != FS_TYPE_NAMESPACE)) {
+ pr_warn("mlx5: flow steering node is not in tree or garbaged\n");
+ return NULL;
+ }
+
+ ns = container_of(root, struct mlx5_flow_namespace, node);
+ return container_of(ns, struct mlx5_flow_root_namespace, ns);
+}
+
+static inline struct mlx5_core_dev *get_dev(struct fs_node *node)
+{
+ struct mlx5_flow_root_namespace *root = find_root(node);
+
+ if (root)
+ return root->dev;
+ return NULL;
+}
+
+static void del_flow_table(struct fs_node *node)
+{
+ struct mlx5_flow_table *ft;
+ struct mlx5_core_dev *dev;
+ struct fs_prio *prio;
+ int err;
+
+ fs_get_obj(ft, node);
+ dev = get_dev(&ft->node);
+
+ err = mlx5_cmd_destroy_flow_table(dev, ft);
+ if (err)
+ pr_warn("flow steering can't destroy ft\n");
+ fs_get_obj(prio, ft->node.parent);
+ prio->num_ft--;
+}
+
+static void del_rule(struct fs_node *node)
+{
+ struct mlx5_flow_rule *rule;
+ struct mlx5_flow_table *ft;
+ struct mlx5_flow_group *fg;
+ struct fs_fte *fte;
+ u32 *match_value;
+ struct mlx5_core_dev *dev = get_dev(node);
+ int match_len = MLX5_ST_SZ_BYTES(fte_match_param);
+ int err;
+
+ match_value = mlx5_vzalloc(match_len);
+ if (!match_value) {
+ pr_warn("failed to allocate inbox\n");
+ return;
+ }
+
+ fs_get_obj(rule, node);
+ fs_get_obj(fte, rule->node.parent);
+ fs_get_obj(fg, fte->node.parent);
+ memcpy(match_value, fte->val, sizeof(fte->val));
+ fs_get_obj(ft, fg->node.parent);
+ list_del(&rule->node.list);
+ fte->dests_size--;
+ if (fte->dests_size) {
+ err = mlx5_cmd_update_fte(dev, ft,
+ fg->id, fte);
+ if (err)
+ pr_warn("%s can't del rule fg id=%d fte_index=%d\n",
+ __func__, fg->id, fte->index);
+ }
+ kvfree(match_value);
+}
+
+static void del_fte(struct fs_node *node)
+{
+ struct mlx5_flow_table *ft;
+ struct mlx5_flow_group *fg;
+ struct mlx5_core_dev *dev;
+ struct fs_fte *fte;
+ int err;
+
+ fs_get_obj(fte, node);
+ fs_get_obj(fg, fte->node.parent);
+ fs_get_obj(ft, fg->node.parent);
+
+ dev = get_dev(&ft->node);
+ err = mlx5_cmd_delete_fte(dev, ft,
+ fte->index);
+ if (err)
+ pr_warn("flow steering can't delete fte in index %d of flow group id %d\n",
+ fte->index, fg->id);
+
+ fte->status = 0;
+ fg->num_ftes--;
+}
+
+static void del_flow_group(struct fs_node *node)
+{
+ struct mlx5_flow_group *fg;
+ struct mlx5_flow_table *ft;
+ struct mlx5_core_dev *dev;
+
+ fs_get_obj(fg, node);
+ fs_get_obj(ft, fg->node.parent);
+ dev = get_dev(&ft->node);
+
+ if (mlx5_cmd_destroy_flow_group(dev, ft, fg->id))
+ pr_warn("flow steering can't destroy fg %d of ft %d\n",
+ fg->id, ft->id);
+}
+
+static struct fs_fte *alloc_fte(u8 action,
+ u32 flow_tag,
+ u32 *match_value,
+ unsigned int index)
+{
+ struct fs_fte *fte;
+
+ fte = kzalloc(sizeof(*fte), GFP_KERNEL);
+ if (!fte)
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(fte->val, match_value, sizeof(fte->val));
+ fte->node.type = FS_TYPE_FLOW_ENTRY;
+ fte->flow_tag = flow_tag;
+ fte->index = index;
+ fte->action = action;
+
+ return fte;
+}
+
+static struct mlx5_flow_group *alloc_flow_group(u32 *create_fg_in)
+{
+ struct mlx5_flow_group *fg;
+ void *match_criteria = MLX5_ADDR_OF(create_flow_group_in,
+ create_fg_in, match_criteria);
+ u8 match_criteria_enable = MLX5_GET(create_flow_group_in,
+ create_fg_in,
+ match_criteria_enable);
+ fg = kzalloc(sizeof(*fg), GFP_KERNEL);
+ if (!fg)
+ return ERR_PTR(-ENOMEM);
+
+ fg->mask.match_criteria_enable = match_criteria_enable;
+ memcpy(&fg->mask.match_criteria, match_criteria,
+ sizeof(fg->mask.match_criteria));
+ fg->node.type = FS_TYPE_FLOW_GROUP;
+ fg->start_index = MLX5_GET(create_flow_group_in, create_fg_in,
+ start_flow_index);
+ fg->max_ftes = MLX5_GET(create_flow_group_in, create_fg_in,
+ end_flow_index) - fg->start_index + 1;
+ return fg;
+}
+
+static struct mlx5_flow_table *alloc_flow_table(int level, int max_fte,
+ enum fs_flow_table_type table_type)
+{
+ struct mlx5_flow_table *ft;
+
+ ft = kzalloc(sizeof(*ft), GFP_KERNEL);
+ if (!ft)
+ return NULL;
+
+ ft->level = level;
+ ft->node.type = FS_TYPE_FLOW_TABLE;
+ ft->type = table_type;
+ ft->max_fte = max_fte;
+
+ return ft;
+}
+
+/* If reverse is false, then we search for the first flow table in the
+ * root sub-tree from start(closest from right), else we search for the
+ * last flow table in the root sub-tree till start(closest from left).
+ */
+static struct mlx5_flow_table *find_closest_ft_recursive(struct fs_node *root,
+ struct list_head *start,
+ bool reverse)
+{
+#define list_advance_entry(pos, reverse) \
+ ((reverse) ? list_prev_entry(pos, list) : list_next_entry(pos, list))
+
+#define list_for_each_advance_continue(pos, head, reverse) \
+ for (pos = list_advance_entry(pos, reverse); \
+ &pos->list != (head); \
+ pos = list_advance_entry(pos, reverse))
+
+ struct fs_node *iter = list_entry(start, struct fs_node, list);
+ struct mlx5_flow_table *ft = NULL;
+
+ if (!root)
+ return NULL;
+
+ list_for_each_advance_continue(iter, &root->children, reverse) {
+ if (iter->type == FS_TYPE_FLOW_TABLE) {
+ fs_get_obj(ft, iter);
+ return ft;
+ }
+ ft = find_closest_ft_recursive(iter, &iter->children, reverse);
+ if (ft)
+ return ft;
+ }
+
+ return ft;
+}
+
+/* If reverse if false then return the first flow table in next priority of
+ * prio in the tree, else return the last flow table in the previous priority
+ * of prio in the tree.
+ */
+static struct mlx5_flow_table *find_closest_ft(struct fs_prio *prio, bool reverse)
+{
+ struct mlx5_flow_table *ft = NULL;
+ struct fs_node *curr_node;
+ struct fs_node *parent;
+
+ parent = prio->node.parent;
+ curr_node = &prio->node;
+ while (!ft && parent) {
+ ft = find_closest_ft_recursive(parent, &curr_node->list, reverse);
+ curr_node = parent;
+ parent = curr_node->parent;
+ }
+ return ft;
+}
+
+/* Assuming all the tree is locked by mutex chain lock */
+static struct mlx5_flow_table *find_next_chained_ft(struct fs_prio *prio)
+{
+ return find_closest_ft(prio, false);
+}
+
+/* Assuming all the tree is locked by mutex chain lock */
+static struct mlx5_flow_table *find_prev_chained_ft(struct fs_prio *prio)
+{
+ return find_closest_ft(prio, true);
+}
+
+static int connect_fts_in_prio(struct mlx5_core_dev *dev,
+ struct fs_prio *prio,
+ struct mlx5_flow_table *ft)
+{
+ struct mlx5_flow_table *iter;
+ int i = 0;
+ int err;
+
+ fs_for_each_ft(iter, prio) {
+ i++;
+ err = mlx5_cmd_modify_flow_table(dev,
+ iter,
+ ft);
+ if (err) {
+ mlx5_core_warn(dev, "Failed to modify flow table %d\n",
+ iter->id);
+ /* The driver is out of sync with the FW */
+ if (i > 1)
+ WARN_ON(true);
+ return err;
+ }
+ }
+ return 0;
+}
+
+/* Connect flow tables from previous priority of prio to ft */
+static int connect_prev_fts(struct mlx5_core_dev *dev,
+ struct mlx5_flow_table *ft,
+ struct fs_prio *prio)
+{
+ struct mlx5_flow_table *prev_ft;
+
+ prev_ft = find_prev_chained_ft(prio);
+ if (prev_ft) {
+ struct fs_prio *prev_prio;
+
+ fs_get_obj(prev_prio, prev_ft->node.parent);
+ return connect_fts_in_prio(dev, prev_prio, ft);
+ }
+ return 0;
+}
+
+static int update_root_ft_create(struct mlx5_flow_table *ft, struct fs_prio
+ *prio)
+{
+ struct mlx5_flow_root_namespace *root = find_root(&prio->node);
+ int min_level = INT_MAX;
+ int err;
+
+ if (root->root_ft)
+ min_level = root->root_ft->level;
+
+ if (ft->level >= min_level)
+ return 0;
+
+ err = mlx5_cmd_update_root_ft(root->dev, ft);
+ if (err)
+ mlx5_core_warn(root->dev, "Update root flow table of id=%u failed\n",
+ ft->id);
+ else
+ root->root_ft = ft;
+
+ return err;
+}
+
+static int connect_flow_table(struct mlx5_core_dev *dev, struct mlx5_flow_table *ft,
+ struct fs_prio *prio)
+{
+ int err = 0;
+
+ /* Connect_prev_fts and update_root_ft_create are mutually exclusive */
+
+ if (list_empty(&prio->node.children)) {
+ err = connect_prev_fts(dev, ft, prio);
+ if (err)
+ return err;
+ }
+
+ if (MLX5_CAP_FLOWTABLE(dev,
+ flow_table_properties_nic_receive.modify_root))
+ err = update_root_ft_create(ft, prio);
+ return err;
+}
+
+struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns,
+ int prio,
+ int max_fte)
+{
+ struct mlx5_flow_table *next_ft = NULL;
+ struct mlx5_flow_table *ft;
+ int err;
+ int log_table_sz;
+ struct mlx5_flow_root_namespace *root =
+ find_root(&ns->node);
+ struct fs_prio *fs_prio = NULL;
+
+ if (!root) {
+ pr_err("mlx5: flow steering failed to find root of namespace\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ mutex_lock(&root->chain_lock);
+ fs_prio = find_prio(ns, prio);
+ if (!fs_prio) {
+ err = -EINVAL;
+ goto unlock_root;
+ }
+ if (fs_prio->num_ft == fs_prio->max_ft) {
+ err = -ENOSPC;
+ goto unlock_root;
+ }
+
+ ft = alloc_flow_table(find_next_free_level(fs_prio),
+ roundup_pow_of_two(max_fte),
+ root->table_type);
+ if (!ft) {
+ err = -ENOMEM;
+ goto unlock_root;
+ }
+
+ tree_init_node(&ft->node, 1, del_flow_table);
+ log_table_sz = ilog2(ft->max_fte);
+ next_ft = find_next_chained_ft(fs_prio);
+ err = mlx5_cmd_create_flow_table(root->dev, ft->type, ft->level,
+ log_table_sz, next_ft, &ft->id);
+ if (err)
+ goto free_ft;
+
+ err = connect_flow_table(root->dev, ft, fs_prio);
+ if (err)
+ goto destroy_ft;
+ lock_ref_node(&fs_prio->node);
+ tree_add_node(&ft->node, &fs_prio->node);
+ list_add_tail(&ft->node.list, &fs_prio->node.children);
+ fs_prio->num_ft++;
+ unlock_ref_node(&fs_prio->node);
+ mutex_unlock(&root->chain_lock);
+ return ft;
+destroy_ft:
+ mlx5_cmd_destroy_flow_table(root->dev, ft);
+free_ft:
+ kfree(ft);
+unlock_root:
+ mutex_unlock(&root->chain_lock);
+ return ERR_PTR(err);
+}
+
+struct mlx5_flow_table *mlx5_create_auto_grouped_flow_table(struct mlx5_flow_namespace *ns,
+ int prio,
+ int num_flow_table_entries,
+ int max_num_groups)
+{
+ struct mlx5_flow_table *ft;
+
+ if (max_num_groups > num_flow_table_entries)
+ return ERR_PTR(-EINVAL);
+
+ ft = mlx5_create_flow_table(ns, prio, num_flow_table_entries);
+ if (IS_ERR(ft))
+ return ft;
+
+ ft->autogroup.active = true;
+ ft->autogroup.required_groups = max_num_groups;
+
+ return ft;
+}
+EXPORT_SYMBOL(mlx5_create_auto_grouped_flow_table);
+
+/* Flow table should be locked */
+static struct mlx5_flow_group *create_flow_group_common(struct mlx5_flow_table *ft,
+ u32 *fg_in,
+ struct list_head
+ *prev_fg,
+ bool is_auto_fg)
+{
+ struct mlx5_flow_group *fg;
+ struct mlx5_core_dev *dev = get_dev(&ft->node);
+ int err;
+
+ if (!dev)
+ return ERR_PTR(-ENODEV);
+
+ fg = alloc_flow_group(fg_in);
+ if (IS_ERR(fg))
+ return fg;
+
+ err = mlx5_cmd_create_flow_group(dev, ft, fg_in, &fg->id);
+ if (err) {
+ kfree(fg);
+ return ERR_PTR(err);
+ }
+
+ if (ft->autogroup.active)
+ ft->autogroup.num_groups++;
+ /* Add node to tree */
+ tree_init_node(&fg->node, !is_auto_fg, del_flow_group);
+ tree_add_node(&fg->node, &ft->node);
+ /* Add node to group list */
+ list_add(&fg->node.list, ft->node.children.prev);
+
+ return fg;
+}
+
+struct mlx5_flow_group *mlx5_create_flow_group(struct mlx5_flow_table *ft,
+ u32 *fg_in)
+{
+ struct mlx5_flow_group *fg;
+
+ if (ft->autogroup.active)
+ return ERR_PTR(-EPERM);
+
+ lock_ref_node(&ft->node);
+ fg = create_flow_group_common(ft, fg_in, &ft->node.children, false);
+ unlock_ref_node(&ft->node);
+
+ return fg;
+}
+
+static struct mlx5_flow_rule *alloc_rule(struct mlx5_flow_destination *dest)
+{
+ struct mlx5_flow_rule *rule;
+
+ rule = kzalloc(sizeof(*rule), GFP_KERNEL);
+ if (!rule)
+ return NULL;
+
+ rule->node.type = FS_TYPE_FLOW_DEST;
+ memcpy(&rule->dest_attr, dest, sizeof(*dest));
+
+ return rule;
+}
+
+/* fte should not be deleted while calling this function */
+static struct mlx5_flow_rule *add_rule_fte(struct fs_fte *fte,
+ struct mlx5_flow_group *fg,
+ struct mlx5_flow_destination *dest)
+{
+ struct mlx5_flow_table *ft;
+ struct mlx5_flow_rule *rule;
+ int err;
+
+ rule = alloc_rule(dest);
+ if (!rule)
+ return ERR_PTR(-ENOMEM);
+
+ fs_get_obj(ft, fg->node.parent);
+ /* Add dest to dests list- added as first element after the head */
+ tree_init_node(&rule->node, 1, del_rule);
+ list_add_tail(&rule->node.list, &fte->node.children);
+ fte->dests_size++;
+ if (fte->dests_size == 1)
+ err = mlx5_cmd_create_fte(get_dev(&ft->node),
+ ft, fg->id, fte);
+ else
+ err = mlx5_cmd_update_fte(get_dev(&ft->node),
+ ft, fg->id, fte);
+ if (err)
+ goto free_rule;
+
+ fte->status |= FS_FTE_STATUS_EXISTING;
+
+ return rule;
+
+free_rule:
+ list_del(&rule->node.list);
+ kfree(rule);
+ fte->dests_size--;
+ return ERR_PTR(err);
+}
+
+/* Assumed fg is locked */
+static unsigned int get_free_fte_index(struct mlx5_flow_group *fg,
+ struct list_head **prev)
+{
+ struct fs_fte *fte;
+ unsigned int start = fg->start_index;
+
+ if (prev)
+ *prev = &fg->node.children;
+
+ /* assumed list is sorted by index */
+ fs_for_each_fte(fte, fg) {
+ if (fte->index != start)
+ return start;
+ start++;
+ if (prev)
+ *prev = &fte->node.list;
+ }
+
+ return start;
+}
+
+/* prev is output, prev->next = new_fte */
+static struct fs_fte *create_fte(struct mlx5_flow_group *fg,
+ u32 *match_value,
+ u8 action,
+ u32 flow_tag,
+ struct list_head **prev)
+{
+ struct fs_fte *fte;
+ int index;
+
+ index = get_free_fte_index(fg, prev);
+ fte = alloc_fte(action, flow_tag, match_value, index);
+ if (IS_ERR(fte))
+ return fte;
+
+ return fte;
+}
+
+static struct mlx5_flow_group *create_autogroup(struct mlx5_flow_table *ft,
+ u8 match_criteria_enable,
+ u32 *match_criteria)
+{
+ int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
+ struct list_head *prev = &ft->node.children;
+ unsigned int candidate_index = 0;
+ struct mlx5_flow_group *fg;
+ void *match_criteria_addr;
+ unsigned int group_size = 0;
+ u32 *in;
+
+ if (!ft->autogroup.active)
+ return ERR_PTR(-ENOENT);
+
+ in = mlx5_vzalloc(inlen);
+ if (!in)
+ return ERR_PTR(-ENOMEM);
+
+ if (ft->autogroup.num_groups < ft->autogroup.required_groups)
+ /* We save place for flow groups in addition to max types */
+ group_size = ft->max_fte / (ft->autogroup.required_groups + 1);
+
+ /* ft->max_fte == ft->autogroup.max_types */
+ if (group_size == 0)
+ group_size = 1;
+
+ /* sorted by start_index */
+ fs_for_each_fg(fg, ft) {
+ if (candidate_index + group_size > fg->start_index)
+ candidate_index = fg->start_index + fg->max_ftes;
+ else
+ break;
+ prev = &fg->node.list;
+ }
+
+ if (candidate_index + group_size > ft->max_fte) {
+ fg = ERR_PTR(-ENOSPC);
+ goto out;
+ }
+
+ MLX5_SET(create_flow_group_in, in, match_criteria_enable,
+ match_criteria_enable);
+ MLX5_SET(create_flow_group_in, in, start_flow_index, candidate_index);
+ MLX5_SET(create_flow_group_in, in, end_flow_index, candidate_index +
+ group_size - 1);
+ match_criteria_addr = MLX5_ADDR_OF(create_flow_group_in,
+ in, match_criteria);
+ memcpy(match_criteria_addr, match_criteria,
+ MLX5_ST_SZ_BYTES(fte_match_param));
+
+ fg = create_flow_group_common(ft, in, prev, true);
+out:
+ kvfree(in);
+ return fg;
+}
+
+static struct mlx5_flow_rule *add_rule_fg(struct mlx5_flow_group *fg,
+ u32 *match_value,
+ u8 action,
+ u32 flow_tag,
+ struct mlx5_flow_destination *dest)
+{
+ struct fs_fte *fte;
+ struct mlx5_flow_rule *rule;
+ struct mlx5_flow_table *ft;
+ struct list_head *prev;
+
+ nested_lock_ref_node(&fg->node, FS_MUTEX_PARENT);
+ fs_for_each_fte(fte, fg) {
+ nested_lock_ref_node(&fte->node, FS_MUTEX_CHILD);
+ if (compare_match_value(&fg->mask, match_value, &fte->val) &&
+ action == fte->action && flow_tag == fte->flow_tag) {
+ rule = add_rule_fte(fte, fg, dest);
+ unlock_ref_node(&fte->node);
+ if (IS_ERR(rule))
+ goto unlock_fg;
+ else
+ goto add_rule;
+ }
+ unlock_ref_node(&fte->node);
+ }
+ fs_get_obj(ft, fg->node.parent);
+ if (fg->num_ftes >= fg->max_ftes) {
+ rule = ERR_PTR(-ENOSPC);
+ goto unlock_fg;
+ }
+
+ fte = create_fte(fg, match_value, action, flow_tag, &prev);
+ if (IS_ERR(fte)) {
+ rule = (void *)fte;
+ goto unlock_fg;
+ }
+ tree_init_node(&fte->node, 0, del_fte);
+ rule = add_rule_fte(fte, fg, dest);
+ if (IS_ERR(rule)) {
+ kfree(fte);
+ goto unlock_fg;
+ }
+
+ fg->num_ftes++;
+
+ tree_add_node(&fte->node, &fg->node);
+ list_add(&fte->node.list, prev);
+add_rule:
+ tree_add_node(&rule->node, &fte->node);
+unlock_fg:
+ unlock_ref_node(&fg->node);
+ return rule;
+}
+
+static struct mlx5_flow_rule *add_rule_to_auto_fg(struct mlx5_flow_table *ft,
+ u8 match_criteria_enable,
+ u32 *match_criteria,
+ u32 *match_value,
+ u8 action,
+ u32 flow_tag,
+ struct mlx5_flow_destination *dest)
+{
+ struct mlx5_flow_rule *rule;
+ struct mlx5_flow_group *g;
+
+ g = create_autogroup(ft, match_criteria_enable, match_criteria);
+ if (IS_ERR(g))
+ return (void *)g;
+
+ rule = add_rule_fg(g, match_value,
+ action, flow_tag, dest);
+ if (IS_ERR(rule)) {
+ /* Remove assumes refcount > 0 and autogroup creates a group
+ * with a refcount = 0.
+ */
+ tree_get_node(&g->node);
+ tree_remove_node(&g->node);
+ }
+ return rule;
+}
+
+struct mlx5_flow_rule *
+mlx5_add_flow_rule(struct mlx5_flow_table *ft,
+ u8 match_criteria_enable,
+ u32 *match_criteria,
+ u32 *match_value,
+ u32 action,
+ u32 flow_tag,
+ struct mlx5_flow_destination *dest)
+{
+ struct mlx5_flow_group *g;
+ struct mlx5_flow_rule *rule;
+
+ nested_lock_ref_node(&ft->node, FS_MUTEX_GRANDPARENT);
+ fs_for_each_fg(g, ft)
+ if (compare_match_criteria(g->mask.match_criteria_enable,
+ match_criteria_enable,
+ g->mask.match_criteria,
+ match_criteria)) {
+ rule = add_rule_fg(g, match_value,
+ action, flow_tag, dest);
+ if (!IS_ERR(rule) || PTR_ERR(rule) != -ENOSPC)
+ goto unlock;
+ }
+
+ rule = add_rule_to_auto_fg(ft, match_criteria_enable, match_criteria,
+ match_value, action, flow_tag, dest);
+unlock:
+ unlock_ref_node(&ft->node);
+ return rule;
+}
+EXPORT_SYMBOL(mlx5_add_flow_rule);
+
+void mlx5_del_flow_rule(struct mlx5_flow_rule *rule)
+{
+ tree_remove_node(&rule->node);
+}
+EXPORT_SYMBOL(mlx5_del_flow_rule);
+
+/* Assuming prio->node.children(flow tables) is sorted by level */
+static struct mlx5_flow_table *find_next_ft(struct mlx5_flow_table *ft)
+{
+ struct fs_prio *prio;
+
+ fs_get_obj(prio, ft->node.parent);
+
+ if (!list_is_last(&ft->node.list, &prio->node.children))
+ return list_next_entry(ft, node.list);
+ return find_next_chained_ft(prio);
+}
+
+static int update_root_ft_destroy(struct mlx5_flow_table *ft)
+{
+ struct mlx5_flow_root_namespace *root = find_root(&ft->node);
+ struct mlx5_flow_table *new_root_ft = NULL;
+
+ if (root->root_ft != ft)
+ return 0;
+
+ new_root_ft = find_next_ft(ft);
+ if (new_root_ft) {
+ int err = mlx5_cmd_update_root_ft(root->dev, new_root_ft);
+
+ if (err) {
+ mlx5_core_warn(root->dev, "Update root flow table of id=%u failed\n",
+ ft->id);
+ return err;
+ }
+ root->root_ft = new_root_ft;
+ }
+ return 0;
+}
+
+/* Connect flow table from previous priority to
+ * the next flow table.
+ */
+static int disconnect_flow_table(struct mlx5_flow_table *ft)
+{
+ struct mlx5_core_dev *dev = get_dev(&ft->node);
+ struct mlx5_flow_table *next_ft;
+ struct fs_prio *prio;
+ int err = 0;
+
+ err = update_root_ft_destroy(ft);
+ if (err)
+ return err;
+
+ fs_get_obj(prio, ft->node.parent);
+ if (!(list_first_entry(&prio->node.children,
+ struct mlx5_flow_table,
+ node.list) == ft))
+ return 0;
+
+ next_ft = find_next_chained_ft(prio);
+ err = connect_prev_fts(dev, next_ft, prio);
+ if (err)
+ mlx5_core_warn(dev, "Failed to disconnect flow table %d\n",
+ ft->id);
+ return err;
+}
+
+int mlx5_destroy_flow_table(struct mlx5_flow_table *ft)
+{
+ struct mlx5_flow_root_namespace *root = find_root(&ft->node);
+ int err = 0;
+
+ mutex_lock(&root->chain_lock);
+ err = disconnect_flow_table(ft);
+ if (err) {
+ mutex_unlock(&root->chain_lock);
+ return err;
+ }
+ if (tree_remove_node(&ft->node))
+ mlx5_core_warn(get_dev(&ft->node), "Flow table %d wasn't destroyed, refcount > 1\n",
+ ft->id);
+ mutex_unlock(&root->chain_lock);
+
+ return err;
+}
+EXPORT_SYMBOL(mlx5_destroy_flow_table);
+
+void mlx5_destroy_flow_group(struct mlx5_flow_group *fg)
+{
+ if (tree_remove_node(&fg->node))
+ mlx5_core_warn(get_dev(&fg->node), "Flow group %d wasn't destroyed, refcount > 1\n",
+ fg->id);
+}
+
+struct mlx5_flow_namespace *mlx5_get_flow_namespace(struct mlx5_core_dev *dev,
+ enum mlx5_flow_namespace_type type)
+{
+ struct mlx5_flow_root_namespace *root_ns = dev->priv.root_ns;
+ int prio;
+ static struct fs_prio *fs_prio;
+ struct mlx5_flow_namespace *ns;
+
+ if (!root_ns)
+ return NULL;
+
+ switch (type) {
+ case MLX5_FLOW_NAMESPACE_BYPASS:
+ case MLX5_FLOW_NAMESPACE_KERNEL:
+ case MLX5_FLOW_NAMESPACE_LEFTOVERS:
+ prio = type;
+ break;
+ case MLX5_FLOW_NAMESPACE_FDB:
+ if (dev->priv.fdb_root_ns)
+ return &dev->priv.fdb_root_ns->ns;
+ else
+ return NULL;
+ default:
+ return NULL;
+ }
+
+ fs_prio = find_prio(&root_ns->ns, prio);
+ if (!fs_prio)
+ return NULL;
+
+ ns = list_first_entry(&fs_prio->node.children,
+ typeof(*ns),
+ node.list);
+
+ return ns;
+}
+EXPORT_SYMBOL(mlx5_get_flow_namespace);
+
+static struct fs_prio *fs_create_prio(struct mlx5_flow_namespace *ns,
+ unsigned prio, int max_ft)
+{
+ struct fs_prio *fs_prio;
+
+ fs_prio = kzalloc(sizeof(*fs_prio), GFP_KERNEL);
+ if (!fs_prio)
+ return ERR_PTR(-ENOMEM);
+
+ fs_prio->node.type = FS_TYPE_PRIO;
+ tree_init_node(&fs_prio->node, 1, NULL);
+ tree_add_node(&fs_prio->node, &ns->node);
+ fs_prio->max_ft = max_ft;
+ fs_prio->prio = prio;
+ list_add_tail(&fs_prio->node.list, &ns->node.children);
+
+ return fs_prio;
+}
+
+static struct mlx5_flow_namespace *fs_init_namespace(struct mlx5_flow_namespace
+ *ns)
+{
+ ns->node.type = FS_TYPE_NAMESPACE;
+
+ return ns;
+}
+
+static struct mlx5_flow_namespace *fs_create_namespace(struct fs_prio *prio)
+{
+ struct mlx5_flow_namespace *ns;
+
+ ns = kzalloc(sizeof(*ns), GFP_KERNEL);
+ if (!ns)
+ return ERR_PTR(-ENOMEM);
+
+ fs_init_namespace(ns);
+ tree_init_node(&ns->node, 1, NULL);
+ tree_add_node(&ns->node, &prio->node);
+ list_add_tail(&ns->node.list, &prio->node.children);
+
+ return ns;
+}
+
+static int create_leaf_prios(struct mlx5_flow_namespace *ns, struct init_tree_node
+ *prio_metadata)
+{
+ struct fs_prio *fs_prio;
+ int i;
+
+ for (i = 0; i < prio_metadata->num_leaf_prios; i++) {
+ fs_prio = fs_create_prio(ns, i, prio_metadata->max_ft);
+ if (IS_ERR(fs_prio))
+ return PTR_ERR(fs_prio);
+ }
+ return 0;
+}
+
+#define FLOW_TABLE_BIT_SZ 1
+#define GET_FLOW_TABLE_CAP(dev, offset) \
+ ((be32_to_cpu(*((__be32 *)(dev->hca_caps_cur[MLX5_CAP_FLOW_TABLE]) + \
+ offset / 32)) >> \
+ (32 - FLOW_TABLE_BIT_SZ - (offset & 0x1f))) & FLOW_TABLE_BIT_SZ)
+static bool has_required_caps(struct mlx5_core_dev *dev, struct node_caps *caps)
+{
+ int i;
+
+ for (i = 0; i < caps->arr_sz; i++) {
+ if (!GET_FLOW_TABLE_CAP(dev, caps->caps[i]))
+ return false;
+ }
+ return true;
+}
+
+static int init_root_tree_recursive(struct mlx5_core_dev *dev,
+ struct init_tree_node *init_node,
+ struct fs_node *fs_parent_node,
+ struct init_tree_node *init_parent_node,
+ int index)
+{
+ int max_ft_level = MLX5_CAP_FLOWTABLE(dev,
+ flow_table_properties_nic_receive.
+ max_ft_level);
+ struct mlx5_flow_namespace *fs_ns;
+ struct fs_prio *fs_prio;
+ struct fs_node *base;
+ int i;
+ int err;
+
+ if (init_node->type == FS_TYPE_PRIO) {
+ if ((init_node->min_ft_level > max_ft_level) ||
+ !has_required_caps(dev, &init_node->caps))
+ return 0;
+
+ fs_get_obj(fs_ns, fs_parent_node);
+ if (init_node->num_leaf_prios)
+ return create_leaf_prios(fs_ns, init_node);
+ fs_prio = fs_create_prio(fs_ns, index, init_node->max_ft);
+ if (IS_ERR(fs_prio))
+ return PTR_ERR(fs_prio);
+ base = &fs_prio->node;
+ } else if (init_node->type == FS_TYPE_NAMESPACE) {
+ fs_get_obj(fs_prio, fs_parent_node);
+ fs_ns = fs_create_namespace(fs_prio);
+ if (IS_ERR(fs_ns))
+ return PTR_ERR(fs_ns);
+ base = &fs_ns->node;
+ } else {
+ return -EINVAL;
+ }
+ for (i = 0; i < init_node->ar_size; i++) {
+ err = init_root_tree_recursive(dev, &init_node->children[i],
+ base, init_node, i);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int init_root_tree(struct mlx5_core_dev *dev,
+ struct init_tree_node *init_node,
+ struct fs_node *fs_parent_node)
+{
+ int i;
+ struct mlx5_flow_namespace *fs_ns;
+ int err;
+
+ fs_get_obj(fs_ns, fs_parent_node);
+ for (i = 0; i < init_node->ar_size; i++) {
+ err = init_root_tree_recursive(dev, &init_node->children[i],
+ &fs_ns->node,
+ init_node, i);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+static struct mlx5_flow_root_namespace *create_root_ns(struct mlx5_core_dev *dev,
+ enum fs_flow_table_type
+ table_type)
+{
+ struct mlx5_flow_root_namespace *root_ns;
+ struct mlx5_flow_namespace *ns;
+
+ /* Create the root namespace */
+ root_ns = mlx5_vzalloc(sizeof(*root_ns));
+ if (!root_ns)
+ return NULL;
+
+ root_ns->dev = dev;
+ root_ns->table_type = table_type;
+
+ ns = &root_ns->ns;
+ fs_init_namespace(ns);
+ mutex_init(&root_ns->chain_lock);
+ tree_init_node(&ns->node, 1, NULL);
+ tree_add_node(&ns->node, NULL);
+
+ return root_ns;
+}
+
+static void set_prio_attrs_in_prio(struct fs_prio *prio, int acc_level);
+
+static int set_prio_attrs_in_ns(struct mlx5_flow_namespace *ns, int acc_level)
+{
+ struct fs_prio *prio;
+
+ fs_for_each_prio(prio, ns) {
+ /* This updates prio start_level and max_ft */
+ set_prio_attrs_in_prio(prio, acc_level);
+ acc_level += prio->max_ft;
+ }
+ return acc_level;
+}
+
+static void set_prio_attrs_in_prio(struct fs_prio *prio, int acc_level)
+{
+ struct mlx5_flow_namespace *ns;
+ int acc_level_ns = acc_level;
+
+ prio->start_level = acc_level;
+ fs_for_each_ns(ns, prio)
+ /* This updates start_level and max_ft of ns's priority descendants */
+ acc_level_ns = set_prio_attrs_in_ns(ns, acc_level);
+ if (!prio->max_ft)
+ prio->max_ft = acc_level_ns - prio->start_level;
+ WARN_ON(prio->max_ft < acc_level_ns - prio->start_level);
+}
+
+static void set_prio_attrs(struct mlx5_flow_root_namespace *root_ns)
+{
+ struct mlx5_flow_namespace *ns = &root_ns->ns;
+ struct fs_prio *prio;
+ int start_level = 0;
+
+ fs_for_each_prio(prio, ns) {
+ set_prio_attrs_in_prio(prio, start_level);
+ start_level += prio->max_ft;
+ }
+}
+
+static int init_root_ns(struct mlx5_core_dev *dev)
+{
+
+ dev->priv.root_ns = create_root_ns(dev, FS_FT_NIC_RX);
+ if (IS_ERR_OR_NULL(dev->priv.root_ns))
+ goto cleanup;
+
+ if (init_root_tree(dev, &root_fs, &dev->priv.root_ns->ns.node))
+ goto cleanup;
+
+ set_prio_attrs(dev->priv.root_ns);
+
+ return 0;
+
+cleanup:
+ mlx5_cleanup_fs(dev);
+ return -ENOMEM;
+}
+
+static void cleanup_single_prio_root_ns(struct mlx5_core_dev *dev,
+ struct mlx5_flow_root_namespace *root_ns)
+{
+ struct fs_node *prio;
+
+ if (!root_ns)
+ return;
+
+ if (!list_empty(&root_ns->ns.node.children)) {
+ prio = list_first_entry(&root_ns->ns.node.children,
+ struct fs_node,
+ list);
+ if (tree_remove_node(prio))
+ mlx5_core_warn(dev,
+ "Flow steering priority wasn't destroyed, refcount > 1\n");
+ }
+ if (tree_remove_node(&root_ns->ns.node))
+ mlx5_core_warn(dev,
+ "Flow steering namespace wasn't destroyed, refcount > 1\n");
+ root_ns = NULL;
+}
+
+static void cleanup_root_ns(struct mlx5_core_dev *dev)
+{
+ struct mlx5_flow_root_namespace *root_ns = dev->priv.root_ns;
+ struct fs_prio *iter_prio;
+
+ if (!MLX5_CAP_GEN(dev, nic_flow_table))
+ return;
+
+ if (!root_ns)
+ return;
+
+ /* stage 1 */
+ fs_for_each_prio(iter_prio, &root_ns->ns) {
+ struct fs_node *node;
+ struct mlx5_flow_namespace *iter_ns;
+
+ fs_for_each_ns_or_ft(node, iter_prio) {
+ if (node->type == FS_TYPE_FLOW_TABLE)
+ continue;
+ fs_get_obj(iter_ns, node);
+ while (!list_empty(&iter_ns->node.children)) {
+ struct fs_prio *obj_iter_prio2;
+ struct fs_node *iter_prio2 =
+ list_first_entry(&iter_ns->node.children,
+ struct fs_node,
+ list);
+
+ fs_get_obj(obj_iter_prio2, iter_prio2);
+ if (tree_remove_node(iter_prio2)) {
+ mlx5_core_warn(dev,
+ "Priority %d wasn't destroyed, refcount > 1\n",
+ obj_iter_prio2->prio);
+ return;
+ }
+ }
+ }
+ }
+
+ /* stage 2 */
+ fs_for_each_prio(iter_prio, &root_ns->ns) {
+ while (!list_empty(&iter_prio->node.children)) {
+ struct fs_node *iter_ns =
+ list_first_entry(&iter_prio->node.children,
+ struct fs_node,
+ list);
+ if (tree_remove_node(iter_ns)) {
+ mlx5_core_warn(dev,
+ "Namespace wasn't destroyed, refcount > 1\n");
+ return;
+ }
+ }
+ }
+
+ /* stage 3 */
+ while (!list_empty(&root_ns->ns.node.children)) {
+ struct fs_prio *obj_prio_node;
+ struct fs_node *prio_node =
+ list_first_entry(&root_ns->ns.node.children,
+ struct fs_node,
+ list);
+
+ fs_get_obj(obj_prio_node, prio_node);
+ if (tree_remove_node(prio_node)) {
+ mlx5_core_warn(dev,
+ "Priority %d wasn't destroyed, refcount > 1\n",
+ obj_prio_node->prio);
+ return;
+ }
+ }
+
+ if (tree_remove_node(&root_ns->ns.node)) {
+ mlx5_core_warn(dev,
+ "root namespace wasn't destroyed, refcount > 1\n");
+ return;
+ }
+
+ dev->priv.root_ns = NULL;
+}
+
+void mlx5_cleanup_fs(struct mlx5_core_dev *dev)
+{
+ cleanup_root_ns(dev);
+ cleanup_single_prio_root_ns(dev, dev->priv.fdb_root_ns);
+}
+
+static int init_fdb_root_ns(struct mlx5_core_dev *dev)
+{
+ struct fs_prio *prio;
+
+ dev->priv.fdb_root_ns = create_root_ns(dev, FS_FT_FDB);
+ if (!dev->priv.fdb_root_ns)
+ return -ENOMEM;
+
+ /* Create single prio */
+ prio = fs_create_prio(&dev->priv.fdb_root_ns->ns, 0, 1);
+ if (IS_ERR(prio)) {
+ cleanup_single_prio_root_ns(dev, dev->priv.fdb_root_ns);
+ return PTR_ERR(prio);
+ } else {
+ return 0;
+ }
+}
+
+int mlx5_init_fs(struct mlx5_core_dev *dev)
+{
+ int err = 0;
+
+ if (MLX5_CAP_GEN(dev, nic_flow_table)) {
+ err = init_root_ns(dev);
+ if (err)
+ return err;
+ }
+ if (MLX5_CAP_GEN(dev, eswitch_flow_table)) {
+ err = init_fdb_root_ns(dev);
+ if (err)
+ cleanup_root_ns(dev);
+ }
+
+ return err;
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
new file mode 100644
index 000000000000..00245fd7e4bc
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2015, Mellanox Technologies. 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 _MLX5_FS_CORE_
+#define _MLX5_FS_CORE_
+
+#include <linux/mlx5/fs.h>
+
+enum fs_node_type {
+ FS_TYPE_NAMESPACE,
+ FS_TYPE_PRIO,
+ FS_TYPE_FLOW_TABLE,
+ FS_TYPE_FLOW_GROUP,
+ FS_TYPE_FLOW_ENTRY,
+ FS_TYPE_FLOW_DEST
+};
+
+enum fs_flow_table_type {
+ FS_FT_NIC_RX = 0x0,
+ FS_FT_FDB = 0X4,
+};
+
+enum fs_fte_status {
+ FS_FTE_STATUS_EXISTING = 1UL << 0,
+};
+
+struct fs_node {
+ struct list_head list;
+ struct list_head children;
+ enum fs_node_type type;
+ struct fs_node *parent;
+ struct fs_node *root;
+ /* lock the node for writing and traversing */
+ struct mutex lock;
+ atomic_t refcount;
+ void (*remove_func)(struct fs_node *);
+};
+
+struct mlx5_flow_rule {
+ struct fs_node node;
+ struct mlx5_flow_destination dest_attr;
+};
+
+/* Type of children is mlx5_flow_group */
+struct mlx5_flow_table {
+ struct fs_node node;
+ u32 id;
+ unsigned int max_fte;
+ unsigned int level;
+ enum fs_flow_table_type type;
+ struct {
+ bool active;
+ unsigned int required_groups;
+ unsigned int num_groups;
+ } autogroup;
+};
+
+/* Type of children is mlx5_flow_rule */
+struct fs_fte {
+ struct fs_node node;
+ u32 val[MLX5_ST_SZ_DW(fte_match_param)];
+ u32 dests_size;
+ u32 flow_tag;
+ u32 index;
+ u32 action;
+ enum fs_fte_status status;
+};
+
+/* Type of children is mlx5_flow_table/namespace */
+struct fs_prio {
+ struct fs_node node;
+ unsigned int max_ft;
+ unsigned int start_level;
+ unsigned int prio;
+ unsigned int num_ft;
+};
+
+/* Type of children is fs_prio */
+struct mlx5_flow_namespace {
+ /* parent == NULL => root ns */
+ struct fs_node node;
+};
+
+struct mlx5_flow_group_mask {
+ u8 match_criteria_enable;
+ u32 match_criteria[MLX5_ST_SZ_DW(fte_match_param)];
+};
+
+/* Type of children is fs_fte */
+struct mlx5_flow_group {
+ struct fs_node node;
+ struct mlx5_flow_group_mask mask;
+ u32 start_index;
+ u32 max_ftes;
+ u32 num_ftes;
+ u32 id;
+};
+
+struct mlx5_flow_root_namespace {
+ struct mlx5_flow_namespace ns;
+ enum fs_flow_table_type table_type;
+ struct mlx5_core_dev *dev;
+ struct mlx5_flow_table *root_ft;
+ /* Should be held when chaining flow tables */
+ struct mutex chain_lock;
+};
+
+int mlx5_init_fs(struct mlx5_core_dev *dev);
+void mlx5_cleanup_fs(struct mlx5_core_dev *dev);
+
+#define fs_get_obj(v, _node) {v = container_of((_node), typeof(*v), node); }
+
+#define fs_list_for_each_entry(pos, root) \
+ list_for_each_entry(pos, root, node.list)
+
+#define fs_for_each_ns_or_ft_reverse(pos, prio) \
+ list_for_each_entry_reverse(pos, &(prio)->node.children, list)
+
+#define fs_for_each_ns_or_ft(pos, prio) \
+ list_for_each_entry(pos, (&(prio)->node.children), list)
+
+#define fs_for_each_prio(pos, ns) \
+ fs_list_for_each_entry(pos, &(ns)->node.children)
+
+#define fs_for_each_ns(pos, prio) \
+ fs_list_for_each_entry(pos, &(prio)->node.children)
+
+#define fs_for_each_ft(pos, prio) \
+ fs_list_for_each_entry(pos, &(prio)->node.children)
+
+#define fs_for_each_fg(pos, ft) \
+ fs_list_for_each_entry(pos, &(ft)->node.children)
+
+#define fs_for_each_fte(pos, fg) \
+ fs_list_for_each_entry(pos, &(fg)->node.children)
+
+#define fs_for_each_dst(pos, fte) \
+ fs_list_for_each_entry(pos, &(fte)->node.children)
+
+#endif
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c
index 9335e5ae18cc..aa1ab4702385 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c
@@ -160,6 +160,30 @@ int mlx5_query_hca_caps(struct mlx5_core_dev *dev)
if (err)
return err;
}
+
+ if (MLX5_CAP_GEN(dev, vport_group_manager) &&
+ MLX5_CAP_GEN(dev, eswitch_flow_table)) {
+ err = mlx5_core_get_caps(dev, MLX5_CAP_ESWITCH_FLOW_TABLE,
+ HCA_CAP_OPMOD_GET_CUR);
+ if (err)
+ return err;
+ err = mlx5_core_get_caps(dev, MLX5_CAP_ESWITCH_FLOW_TABLE,
+ HCA_CAP_OPMOD_GET_MAX);
+ if (err)
+ return err;
+ }
+
+ if (MLX5_CAP_GEN(dev, eswitch_flow_table)) {
+ err = mlx5_core_get_caps(dev, MLX5_CAP_ESWITCH,
+ HCA_CAP_OPMOD_GET_CUR);
+ if (err)
+ return err;
+ err = mlx5_core_get_caps(dev, MLX5_CAP_ESWITCH,
+ HCA_CAP_OPMOD_GET_MAX);
+ if (err)
+ return err;
+ }
+
return 0;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index 4ac8d4cc4973..67676cf0d507 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -49,6 +49,10 @@
#include <linux/delay.h>
#include <linux/mlx5/mlx5_ifc.h>
#include "mlx5_core.h"
+#include "fs_core.h"
+#ifdef CONFIG_MLX5_CORE_EN
+#include "eswitch.h"
+#endif
MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>");
MODULE_DESCRIPTION("Mellanox Connect-IB, ConnectX-4 core driver");
@@ -454,6 +458,9 @@ static int set_hca_ctrl(struct mlx5_core_dev *dev)
struct mlx5_reg_host_endianess he_out;
int err;
+ if (!mlx5_core_is_pf(dev))
+ return 0;
+
memset(&he_in, 0, sizeof(he_in));
he_in.he = MLX5_SET_HOST_ENDIANNESS;
err = mlx5_core_access_reg(dev, &he_in, sizeof(he_in),
@@ -462,42 +469,52 @@ static int set_hca_ctrl(struct mlx5_core_dev *dev)
return err;
}
-static int mlx5_core_enable_hca(struct mlx5_core_dev *dev)
+int mlx5_core_enable_hca(struct mlx5_core_dev *dev, u16 func_id)
{
+ u32 out[MLX5_ST_SZ_DW(enable_hca_out)];
+ u32 in[MLX5_ST_SZ_DW(enable_hca_in)];
int err;
- struct mlx5_enable_hca_mbox_in in;
- struct mlx5_enable_hca_mbox_out out;
- memset(&in, 0, sizeof(in));
- memset(&out, 0, sizeof(out));
- in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_ENABLE_HCA);
+ memset(in, 0, sizeof(in));
+ MLX5_SET(enable_hca_in, in, opcode, MLX5_CMD_OP_ENABLE_HCA);
+ MLX5_SET(enable_hca_in, in, function_id, func_id);
+ memset(out, 0, sizeof(out));
+
err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
if (err)
return err;
- if (out.hdr.status)
- return mlx5_cmd_status_to_err(&out.hdr);
-
- return 0;
+ return mlx5_cmd_status_to_err_v2(out);
}
-static int mlx5_core_disable_hca(struct mlx5_core_dev *dev)
+int mlx5_core_disable_hca(struct mlx5_core_dev *dev, u16 func_id)
{
+ u32 out[MLX5_ST_SZ_DW(disable_hca_out)];
+ u32 in[MLX5_ST_SZ_DW(disable_hca_in)];
int err;
- struct mlx5_disable_hca_mbox_in in;
- struct mlx5_disable_hca_mbox_out out;
- memset(&in, 0, sizeof(in));
- memset(&out, 0, sizeof(out));
- in.hdr.opcode = cpu_to_be16(MLX5_CMD_OP_DISABLE_HCA);
- err = mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+ memset(in, 0, sizeof(in));
+ MLX5_SET(disable_hca_in, in, opcode, MLX5_CMD_OP_DISABLE_HCA);
+ MLX5_SET(disable_hca_in, in, function_id, func_id);
+ memset(out, 0, sizeof(out));
+ err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
if (err)
return err;
- if (out.hdr.status)
- return mlx5_cmd_status_to_err(&out.hdr);
+ return mlx5_cmd_status_to_err_v2(out);
+}
- return 0;
+cycle_t mlx5_read_internal_timer(struct mlx5_core_dev *dev)
+{
+ u32 timer_h, timer_h1, timer_l;
+
+ timer_h = ioread32be(&dev->iseg->internal_timer_h);
+ timer_l = ioread32be(&dev->iseg->internal_timer_l);
+ timer_h1 = ioread32be(&dev->iseg->internal_timer_h);
+ if (timer_h != timer_h1) /* wrap around */
+ timer_l = ioread32be(&dev->iseg->internal_timer_l);
+
+ return (cycle_t)timer_l | (cycle_t)timer_h1 << 32;
}
static int mlx5_irq_set_affinity_hint(struct mlx5_core_dev *mdev, int i)
@@ -942,7 +959,7 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv)
mlx5_pagealloc_init(dev);
- err = mlx5_core_enable_hca(dev);
+ err = mlx5_core_enable_hca(dev, 0);
if (err) {
dev_err(&pdev->dev, "enable hca failed\n");
goto err_pagealloc_cleanup;
@@ -1052,6 +1069,25 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv)
mlx5_init_srq_table(dev);
mlx5_init_mr_table(dev);
+ err = mlx5_init_fs(dev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to init flow steering\n");
+ goto err_fs;
+ }
+#ifdef CONFIG_MLX5_CORE_EN
+ err = mlx5_eswitch_init(dev);
+ if (err) {
+ dev_err(&pdev->dev, "eswitch init failed %d\n", err);
+ goto err_reg_dev;
+ }
+#endif
+
+ err = mlx5_sriov_init(dev);
+ if (err) {
+ dev_err(&pdev->dev, "sriov init failed %d\n", err);
+ goto err_sriov;
+ }
+
err = mlx5_register_device(dev);
if (err) {
dev_err(&pdev->dev, "mlx5_register_device failed %d\n", err);
@@ -1068,7 +1104,16 @@ out:
return 0;
+err_sriov:
+ if (mlx5_sriov_cleanup(dev))
+ dev_err(&dev->pdev->dev, "sriov cleanup failed\n");
+
+#ifdef CONFIG_MLX5_CORE_EN
+ mlx5_eswitch_cleanup(dev->priv.eswitch);
+#endif
err_reg_dev:
+ mlx5_cleanup_fs(dev);
+err_fs:
mlx5_cleanup_mr_table(dev);
mlx5_cleanup_srq_table(dev);
mlx5_cleanup_qp_table(dev);
@@ -1106,7 +1151,7 @@ reclaim_boot_pages:
mlx5_reclaim_startup_pages(dev);
err_disable_hca:
- mlx5_core_disable_hca(dev);
+ mlx5_core_disable_hca(dev, 0);
err_pagealloc_cleanup:
mlx5_pagealloc_cleanup(dev);
@@ -1123,6 +1168,13 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv)
{
int err = 0;
+ err = mlx5_sriov_cleanup(dev);
+ if (err) {
+ dev_warn(&dev->pdev->dev, "%s: sriov cleanup failed - abort\n",
+ __func__);
+ return err;
+ }
+
mutex_lock(&dev->intf_state_mutex);
if (dev->interface_state == MLX5_INTERFACE_STATE_DOWN) {
dev_warn(&dev->pdev->dev, "%s: interface is down, NOP\n",
@@ -1130,6 +1182,11 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv)
goto out;
}
mlx5_unregister_device(dev);
+#ifdef CONFIG_MLX5_CORE_EN
+ mlx5_eswitch_cleanup(dev->priv.eswitch);
+#endif
+
+ mlx5_cleanup_fs(dev);
mlx5_cleanup_mr_table(dev);
mlx5_cleanup_srq_table(dev);
mlx5_cleanup_qp_table(dev);
@@ -1149,7 +1206,7 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv)
}
mlx5_pagealloc_stop(dev);
mlx5_reclaim_startup_pages(dev);
- mlx5_core_disable_hca(dev);
+ mlx5_core_disable_hca(dev, 0);
mlx5_pagealloc_cleanup(dev);
mlx5_cmd_cleanup(dev);
@@ -1195,6 +1252,7 @@ static int init_one(struct pci_dev *pdev,
return -ENOMEM;
}
priv = &dev->priv;
+ priv->pci_dev_data = id->driver_data;
pci_set_drvdata(pdev, dev);
@@ -1365,12 +1423,12 @@ static const struct pci_error_handlers mlx5_err_handler = {
};
static const struct pci_device_id mlx5_core_pci_table[] = {
- { PCI_VDEVICE(MELLANOX, 0x1011) }, /* Connect-IB */
- { PCI_VDEVICE(MELLANOX, 0x1012) }, /* Connect-IB VF */
- { PCI_VDEVICE(MELLANOX, 0x1013) }, /* ConnectX-4 */
- { PCI_VDEVICE(MELLANOX, 0x1014) }, /* ConnectX-4 VF */
- { PCI_VDEVICE(MELLANOX, 0x1015) }, /* ConnectX-4LX */
- { PCI_VDEVICE(MELLANOX, 0x1016) }, /* ConnectX-4LX VF */
+ { PCI_VDEVICE(MELLANOX, 0x1011) }, /* Connect-IB */
+ { PCI_VDEVICE(MELLANOX, 0x1012), MLX5_PCI_DEV_IS_VF}, /* Connect-IB VF */
+ { PCI_VDEVICE(MELLANOX, 0x1013) }, /* ConnectX-4 */
+ { PCI_VDEVICE(MELLANOX, 0x1014), MLX5_PCI_DEV_IS_VF}, /* ConnectX-4 VF */
+ { PCI_VDEVICE(MELLANOX, 0x1015) }, /* ConnectX-4LX */
+ { PCI_VDEVICE(MELLANOX, 0x1016), MLX5_PCI_DEV_IS_VF}, /* ConnectX-4LX VF */
{ 0, }
};
@@ -1381,7 +1439,8 @@ static struct pci_driver mlx5_core_driver = {
.id_table = mlx5_core_pci_table,
.probe = init_one,
.remove = remove_one,
- .err_handler = &mlx5_err_handler
+ .err_handler = &mlx5_err_handler,
+ .sriov_configure = mlx5_core_sriov_configure,
};
static int __init init(void)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
index cee5b7a839bc..0336847ec9a1 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
@@ -36,6 +36,7 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/if_link.h>
#define DRIVER_NAME "mlx5_core"
#define DRIVER_VERSION "3.0-1"
@@ -64,6 +65,9 @@ do { \
(__dev)->priv.name, __func__, __LINE__, current->pid, \
##__VA_ARGS__)
+#define mlx5_core_info(__dev, format, ...) \
+ dev_info(&(__dev)->pdev->dev, format, ##__VA_ARGS__)
+
enum {
MLX5_CMD_DATA, /* print command payload only */
MLX5_CMD_TIME, /* print command execution time */
@@ -90,6 +94,11 @@ void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event,
unsigned long param);
void mlx5_enter_error_state(struct mlx5_core_dev *dev);
void mlx5_disable_device(struct mlx5_core_dev *dev);
+int mlx5_core_sriov_configure(struct pci_dev *dev, int num_vfs);
+int mlx5_core_enable_hca(struct mlx5_core_dev *dev, u16 func_id);
+int mlx5_core_disable_hca(struct mlx5_core_dev *dev, u16 func_id);
+int mlx5_wait_for_vf_pages(struct mlx5_core_dev *dev);
+cycle_t mlx5_read_internal_timer(struct mlx5_core_dev *dev);
void mlx5e_init(void);
void mlx5e_cleanup(void);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
index 4d3377b12657..9eeee0545f1c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
@@ -33,6 +33,7 @@
#include <linux/highmem.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/delay.h>
#include <linux/mlx5/driver.h>
#include <linux/mlx5/cmd.h>
#include "mlx5_core.h"
@@ -95,6 +96,7 @@ struct mlx5_manage_pages_outbox {
enum {
MAX_RECLAIM_TIME_MSECS = 5000,
+ MAX_RECLAIM_VFS_PAGES_TIME_MSECS = 2 * 1000 * 60,
};
enum {
@@ -352,6 +354,10 @@ retry:
goto out_4k;
}
+ dev->priv.fw_pages += npages;
+ if (func_id)
+ dev->priv.vfs_pages += npages;
+
mlx5_core_dbg(dev, "err %d\n", err);
kvfree(in);
@@ -405,6 +411,12 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages,
}
num_claimed = be32_to_cpu(out->num_entries);
+ if (num_claimed > npages) {
+ mlx5_core_warn(dev, "fw returned %d, driver asked %d => corruption\n",
+ num_claimed, npages);
+ err = -EINVAL;
+ goto out_free;
+ }
if (nclaimed)
*nclaimed = num_claimed;
@@ -412,6 +424,9 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages,
addr = be64_to_cpu(out->pas[i]);
free_4k(dev, addr);
}
+ dev->priv.fw_pages -= num_claimed;
+ if (func_id)
+ dev->priv.vfs_pages -= num_claimed;
out_free:
kvfree(out);
@@ -548,3 +563,26 @@ void mlx5_pagealloc_stop(struct mlx5_core_dev *dev)
{
destroy_workqueue(dev->priv.pg_wq);
}
+
+int mlx5_wait_for_vf_pages(struct mlx5_core_dev *dev)
+{
+ unsigned long end = jiffies + msecs_to_jiffies(MAX_RECLAIM_VFS_PAGES_TIME_MSECS);
+ int prev_vfs_pages = dev->priv.vfs_pages;
+
+ mlx5_core_dbg(dev, "Waiting for %d pages from %s\n", prev_vfs_pages,
+ dev->priv.name);
+ while (dev->priv.vfs_pages) {
+ if (time_after(jiffies, end)) {
+ mlx5_core_warn(dev, "aborting while there are %d pending pages\n", dev->priv.vfs_pages);
+ return -ETIMEDOUT;
+ }
+ if (dev->priv.vfs_pages < prev_vfs_pages) {
+ end = jiffies + msecs_to_jiffies(MAX_RECLAIM_VFS_PAGES_TIME_MSECS);
+ prev_vfs_pages = dev->priv.vfs_pages;
+ }
+ msleep(50);
+ }
+
+ mlx5_core_dbg(dev, "All pages received from %s\n", dev->priv.name);
+ return 0;
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c
new file mode 100644
index 000000000000..7b24386794f9
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2014, Mellanox Technologies inc. 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.
+ */
+
+#include <linux/pci.h>
+#include <linux/mlx5/driver.h>
+#include "mlx5_core.h"
+#ifdef CONFIG_MLX5_CORE_EN
+#include "eswitch.h"
+#endif
+
+static void enable_vfs(struct mlx5_core_dev *dev, int num_vfs)
+{
+ struct mlx5_core_sriov *sriov = &dev->priv.sriov;
+ int err;
+ int vf;
+
+ for (vf = 1; vf <= num_vfs; vf++) {
+ err = mlx5_core_enable_hca(dev, vf);
+ if (err) {
+ mlx5_core_warn(dev, "failed to enable VF %d\n", vf - 1);
+ } else {
+ sriov->vfs_ctx[vf - 1].enabled = 1;
+ mlx5_core_dbg(dev, "successfully enabled VF %d\n", vf - 1);
+ }
+ }
+}
+
+static void disable_vfs(struct mlx5_core_dev *dev, int num_vfs)
+{
+ struct mlx5_core_sriov *sriov = &dev->priv.sriov;
+ int vf;
+
+ for (vf = 1; vf <= num_vfs; vf++) {
+ if (sriov->vfs_ctx[vf - 1].enabled) {
+ if (mlx5_core_disable_hca(dev, vf))
+ mlx5_core_warn(dev, "failed to disable VF %d\n", vf - 1);
+ else
+ sriov->vfs_ctx[vf - 1].enabled = 0;
+ }
+ }
+}
+
+static int mlx5_core_create_vfs(struct pci_dev *pdev, int num_vfs)
+{
+ struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
+ int err;
+
+ if (pci_num_vf(pdev))
+ pci_disable_sriov(pdev);
+
+ enable_vfs(dev, num_vfs);
+
+ err = pci_enable_sriov(pdev, num_vfs);
+ if (err) {
+ dev_warn(&pdev->dev, "enable sriov failed %d\n", err);
+ goto ex;
+ }
+
+ return 0;
+
+ex:
+ disable_vfs(dev, num_vfs);
+ return err;
+}
+
+static int mlx5_core_sriov_enable(struct pci_dev *pdev, int num_vfs)
+{
+ struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
+ struct mlx5_core_sriov *sriov = &dev->priv.sriov;
+ int err;
+
+ kfree(sriov->vfs_ctx);
+ sriov->vfs_ctx = kcalloc(num_vfs, sizeof(*sriov->vfs_ctx), GFP_ATOMIC);
+ if (!sriov->vfs_ctx)
+ return -ENOMEM;
+
+ sriov->enabled_vfs = num_vfs;
+ err = mlx5_core_create_vfs(pdev, num_vfs);
+ if (err) {
+ kfree(sriov->vfs_ctx);
+ sriov->vfs_ctx = NULL;
+ return err;
+ }
+
+ return 0;
+}
+
+static void mlx5_core_init_vfs(struct mlx5_core_dev *dev, int num_vfs)
+{
+ struct mlx5_core_sriov *sriov = &dev->priv.sriov;
+
+ sriov->num_vfs = num_vfs;
+}
+
+static void mlx5_core_cleanup_vfs(struct mlx5_core_dev *dev)
+{
+ struct mlx5_core_sriov *sriov;
+
+ sriov = &dev->priv.sriov;
+ disable_vfs(dev, sriov->num_vfs);
+
+ if (mlx5_wait_for_vf_pages(dev))
+ mlx5_core_warn(dev, "timeout claiming VFs pages\n");
+
+ sriov->num_vfs = 0;
+}
+
+int mlx5_core_sriov_configure(struct pci_dev *pdev, int num_vfs)
+{
+ struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
+ struct mlx5_core_sriov *sriov = &dev->priv.sriov;
+ int err;
+
+ mlx5_core_dbg(dev, "requsted num_vfs %d\n", num_vfs);
+ if (!mlx5_core_is_pf(dev))
+ return -EPERM;
+
+ mlx5_core_cleanup_vfs(dev);
+
+ if (!num_vfs) {
+#ifdef CONFIG_MLX5_CORE_EN
+ mlx5_eswitch_disable_sriov(dev->priv.eswitch);
+#endif
+ kfree(sriov->vfs_ctx);
+ sriov->vfs_ctx = NULL;
+ if (!pci_vfs_assigned(pdev))
+ pci_disable_sriov(pdev);
+ else
+ pr_info("unloading PF driver while leaving orphan VFs\n");
+ return 0;
+ }
+
+ err = mlx5_core_sriov_enable(pdev, num_vfs);
+ if (err) {
+ dev_warn(&pdev->dev, "mlx5_core_sriov_enable failed %d\n", err);
+ return err;
+ }
+
+ mlx5_core_init_vfs(dev, num_vfs);
+#ifdef CONFIG_MLX5_CORE_EN
+ mlx5_eswitch_enable_sriov(dev->priv.eswitch, num_vfs);
+#endif
+
+ return num_vfs;
+}
+
+static int sync_required(struct pci_dev *pdev)
+{
+ struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
+ struct mlx5_core_sriov *sriov = &dev->priv.sriov;
+ int cur_vfs = pci_num_vf(pdev);
+
+ if (cur_vfs != sriov->num_vfs) {
+ pr_info("current VFs %d, registered %d - sync needed\n", cur_vfs, sriov->num_vfs);
+ return 1;
+ }
+
+ return 0;
+}
+
+int mlx5_sriov_init(struct mlx5_core_dev *dev)
+{
+ struct mlx5_core_sriov *sriov = &dev->priv.sriov;
+ struct pci_dev *pdev = dev->pdev;
+ int cur_vfs;
+
+ if (!mlx5_core_is_pf(dev))
+ return 0;
+
+ if (!sync_required(dev->pdev))
+ return 0;
+
+ cur_vfs = pci_num_vf(pdev);
+ sriov->vfs_ctx = kcalloc(cur_vfs, sizeof(*sriov->vfs_ctx), GFP_KERNEL);
+ if (!sriov->vfs_ctx)
+ return -ENOMEM;
+
+ sriov->enabled_vfs = cur_vfs;
+
+ mlx5_core_init_vfs(dev, cur_vfs);
+#ifdef CONFIG_MLX5_CORE_EN
+ if (cur_vfs)
+ mlx5_eswitch_enable_sriov(dev->priv.eswitch, cur_vfs);
+#endif
+
+ enable_vfs(dev, cur_vfs);
+
+ return 0;
+}
+
+int mlx5_sriov_cleanup(struct mlx5_core_dev *dev)
+{
+ struct pci_dev *pdev = dev->pdev;
+ int err;
+
+ if (!mlx5_core_is_pf(dev))
+ return 0;
+
+ err = mlx5_core_sriov_configure(pdev, 0);
+ if (err)
+ return err;
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c
index b94177ebcf3a..076197efea9b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c
@@ -36,54 +36,399 @@
#include <linux/mlx5/vport.h>
#include "mlx5_core.h"
-u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod)
+static int _mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod,
+ u16 vport, u32 *out, int outlen)
{
- u32 in[MLX5_ST_SZ_DW(query_vport_state_in)];
- u32 out[MLX5_ST_SZ_DW(query_vport_state_out)];
int err;
+ u32 in[MLX5_ST_SZ_DW(query_vport_state_in)];
memset(in, 0, sizeof(in));
MLX5_SET(query_vport_state_in, in, opcode,
MLX5_CMD_OP_QUERY_VPORT_STATE);
MLX5_SET(query_vport_state_in, in, op_mod, opmod);
+ MLX5_SET(query_vport_state_in, in, vport_number, vport);
+ if (vport)
+ MLX5_SET(query_vport_state_in, in, other_vport, 1);
- err = mlx5_cmd_exec_check_status(mdev, in, sizeof(in), out,
- sizeof(out));
+ err = mlx5_cmd_exec_check_status(mdev, in, sizeof(in), out, outlen);
if (err)
mlx5_core_warn(mdev, "MLX5_CMD_OP_QUERY_VPORT_STATE failed\n");
+ return err;
+}
+
+u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport)
+{
+ u32 out[MLX5_ST_SZ_DW(query_vport_state_out)] = {0};
+
+ _mlx5_query_vport_state(mdev, opmod, vport, out, sizeof(out));
+
return MLX5_GET(query_vport_state_out, out, state);
}
-EXPORT_SYMBOL(mlx5_query_vport_state);
+EXPORT_SYMBOL_GPL(mlx5_query_vport_state);
+
+u8 mlx5_query_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport)
+{
+ u32 out[MLX5_ST_SZ_DW(query_vport_state_out)] = {0};
+
+ _mlx5_query_vport_state(mdev, opmod, vport, out, sizeof(out));
+
+ return MLX5_GET(query_vport_state_out, out, admin_state);
+}
+EXPORT_SYMBOL(mlx5_query_vport_admin_state);
-void mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev, u8 *addr)
+int mlx5_modify_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod,
+ u16 vport, u8 state)
+{
+ u32 in[MLX5_ST_SZ_DW(modify_vport_state_in)];
+ u32 out[MLX5_ST_SZ_DW(modify_vport_state_out)];
+ int err;
+
+ memset(in, 0, sizeof(in));
+
+ MLX5_SET(modify_vport_state_in, in, opcode,
+ MLX5_CMD_OP_MODIFY_VPORT_STATE);
+ MLX5_SET(modify_vport_state_in, in, op_mod, opmod);
+ MLX5_SET(modify_vport_state_in, in, vport_number, vport);
+
+ if (vport)
+ MLX5_SET(modify_vport_state_in, in, other_vport, 1);
+
+ MLX5_SET(modify_vport_state_in, in, admin_state, state);
+
+ err = mlx5_cmd_exec_check_status(mdev, in, sizeof(in), out,
+ sizeof(out));
+ if (err)
+ mlx5_core_warn(mdev, "MLX5_CMD_OP_MODIFY_VPORT_STATE failed\n");
+
+ return err;
+}
+EXPORT_SYMBOL(mlx5_modify_vport_admin_state);
+
+static int mlx5_query_nic_vport_context(struct mlx5_core_dev *mdev, u16 vport,
+ u32 *out, int outlen)
+{
+ u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)];
+
+ memset(in, 0, sizeof(in));
+
+ MLX5_SET(query_nic_vport_context_in, in, opcode,
+ MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT);
+
+ MLX5_SET(query_nic_vport_context_in, in, vport_number, vport);
+ if (vport)
+ MLX5_SET(query_nic_vport_context_in, in, other_vport, 1);
+
+ return mlx5_cmd_exec_check_status(mdev, in, sizeof(in), out, outlen);
+}
+
+static int mlx5_modify_nic_vport_context(struct mlx5_core_dev *mdev, void *in,
+ int inlen)
+{
+ u32 out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)];
+
+ MLX5_SET(modify_nic_vport_context_in, in, opcode,
+ MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
+
+ memset(out, 0, sizeof(out));
+ return mlx5_cmd_exec_check_status(mdev, in, inlen, out, sizeof(out));
+}
+
+int mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev,
+ u16 vport, u8 *addr)
{
- u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)];
u32 *out;
int outlen = MLX5_ST_SZ_BYTES(query_nic_vport_context_out);
u8 *out_addr;
+ int err;
out = mlx5_vzalloc(outlen);
if (!out)
- return;
+ return -ENOMEM;
out_addr = MLX5_ADDR_OF(query_nic_vport_context_out, out,
nic_vport_context.permanent_address);
+ err = mlx5_query_nic_vport_context(mdev, vport, out, outlen);
+ if (err)
+ goto out;
+
+ ether_addr_copy(addr, &out_addr[2]);
+
+out:
+ kvfree(out);
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_mac_address);
+
+int mlx5_modify_nic_vport_mac_address(struct mlx5_core_dev *mdev,
+ u16 vport, u8 *addr)
+{
+ void *in;
+ int inlen = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in);
+ int err;
+ void *nic_vport_ctx;
+ u8 *perm_mac;
+
+ in = mlx5_vzalloc(inlen);
+ if (!in) {
+ mlx5_core_warn(mdev, "failed to allocate inbox\n");
+ return -ENOMEM;
+ }
+
+ MLX5_SET(modify_nic_vport_context_in, in,
+ field_select.permanent_address, 1);
+ MLX5_SET(modify_nic_vport_context_in, in, vport_number, vport);
+
+ if (vport)
+ MLX5_SET(modify_nic_vport_context_in, in, other_vport, 1);
+
+ nic_vport_ctx = MLX5_ADDR_OF(modify_nic_vport_context_in,
+ in, nic_vport_context);
+ perm_mac = MLX5_ADDR_OF(nic_vport_context, nic_vport_ctx,
+ permanent_address);
+
+ ether_addr_copy(&perm_mac[2], addr);
+
+ err = mlx5_modify_nic_vport_context(mdev, in, inlen);
+
+ kvfree(in);
+
+ return err;
+}
+EXPORT_SYMBOL(mlx5_modify_nic_vport_mac_address);
+
+int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev,
+ u32 vport,
+ enum mlx5_list_type list_type,
+ u8 addr_list[][ETH_ALEN],
+ int *list_size)
+{
+ u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)];
+ void *nic_vport_ctx;
+ int max_list_size;
+ int req_list_size;
+ int out_sz;
+ void *out;
+ int err;
+ int i;
+
+ req_list_size = *list_size;
+
+ max_list_size = list_type == MLX5_NVPRT_LIST_TYPE_UC ?
+ 1 << MLX5_CAP_GEN(dev, log_max_current_uc_list) :
+ 1 << MLX5_CAP_GEN(dev, log_max_current_mc_list);
+
+ if (req_list_size > max_list_size) {
+ mlx5_core_warn(dev, "Requested list size (%d) > (%d) max_list_size\n",
+ req_list_size, max_list_size);
+ req_list_size = max_list_size;
+ }
+
+ out_sz = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in) +
+ req_list_size * MLX5_ST_SZ_BYTES(mac_address_layout);
+
memset(in, 0, sizeof(in));
+ out = kzalloc(out_sz, GFP_KERNEL);
+ if (!out)
+ return -ENOMEM;
MLX5_SET(query_nic_vport_context_in, in, opcode,
MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT);
+ MLX5_SET(query_nic_vport_context_in, in, allowed_list_type, list_type);
+ MLX5_SET(query_nic_vport_context_in, in, vport_number, vport);
- memset(out, 0, outlen);
- mlx5_cmd_exec_check_status(mdev, in, sizeof(in), out, outlen);
+ if (vport)
+ MLX5_SET(query_nic_vport_context_in, in, other_vport, 1);
- ether_addr_copy(addr, &out_addr[2]);
+ err = mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, out_sz);
+ if (err)
+ goto out;
- kvfree(out);
+ nic_vport_ctx = MLX5_ADDR_OF(query_nic_vport_context_out, out,
+ nic_vport_context);
+ req_list_size = MLX5_GET(nic_vport_context, nic_vport_ctx,
+ allowed_list_size);
+
+ *list_size = req_list_size;
+ for (i = 0; i < req_list_size; i++) {
+ u8 *mac_addr = MLX5_ADDR_OF(nic_vport_context,
+ nic_vport_ctx,
+ current_uc_mac_address[i]) + 2;
+ ether_addr_copy(addr_list[i], mac_addr);
+ }
+out:
+ kfree(out);
+ return err;
}
-EXPORT_SYMBOL(mlx5_query_nic_vport_mac_address);
+EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_mac_list);
+
+int mlx5_modify_nic_vport_mac_list(struct mlx5_core_dev *dev,
+ enum mlx5_list_type list_type,
+ u8 addr_list[][ETH_ALEN],
+ int list_size)
+{
+ u32 out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)];
+ void *nic_vport_ctx;
+ int max_list_size;
+ int in_sz;
+ void *in;
+ int err;
+ int i;
+
+ max_list_size = list_type == MLX5_NVPRT_LIST_TYPE_UC ?
+ 1 << MLX5_CAP_GEN(dev, log_max_current_uc_list) :
+ 1 << MLX5_CAP_GEN(dev, log_max_current_mc_list);
+
+ if (list_size > max_list_size)
+ return -ENOSPC;
+
+ in_sz = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in) +
+ list_size * MLX5_ST_SZ_BYTES(mac_address_layout);
+
+ memset(out, 0, sizeof(out));
+ in = kzalloc(in_sz, GFP_KERNEL);
+ if (!in)
+ return -ENOMEM;
+
+ MLX5_SET(modify_nic_vport_context_in, in, opcode,
+ MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
+ MLX5_SET(modify_nic_vport_context_in, in,
+ field_select.addresses_list, 1);
+
+ nic_vport_ctx = MLX5_ADDR_OF(modify_nic_vport_context_in, in,
+ nic_vport_context);
+
+ MLX5_SET(nic_vport_context, nic_vport_ctx,
+ allowed_list_type, list_type);
+ MLX5_SET(nic_vport_context, nic_vport_ctx,
+ allowed_list_size, list_size);
+
+ for (i = 0; i < list_size; i++) {
+ u8 *curr_mac = MLX5_ADDR_OF(nic_vport_context,
+ nic_vport_ctx,
+ current_uc_mac_address[i]) + 2;
+ ether_addr_copy(curr_mac, addr_list[i]);
+ }
+
+ err = mlx5_cmd_exec_check_status(dev, in, in_sz, out, sizeof(out));
+ kfree(in);
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_mac_list);
+
+int mlx5_query_nic_vport_vlans(struct mlx5_core_dev *dev,
+ u32 vport,
+ u16 vlans[],
+ int *size)
+{
+ u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)];
+ void *nic_vport_ctx;
+ int req_list_size;
+ int max_list_size;
+ int out_sz;
+ void *out;
+ int err;
+ int i;
+
+ req_list_size = *size;
+ max_list_size = 1 << MLX5_CAP_GEN(dev, log_max_vlan_list);
+ if (req_list_size > max_list_size) {
+ mlx5_core_warn(dev, "Requested list size (%d) > (%d) max list size\n",
+ req_list_size, max_list_size);
+ req_list_size = max_list_size;
+ }
+
+ out_sz = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in) +
+ req_list_size * MLX5_ST_SZ_BYTES(vlan_layout);
+
+ memset(in, 0, sizeof(in));
+ out = kzalloc(out_sz, GFP_KERNEL);
+ if (!out)
+ return -ENOMEM;
+
+ MLX5_SET(query_nic_vport_context_in, in, opcode,
+ MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT);
+ MLX5_SET(query_nic_vport_context_in, in, allowed_list_type,
+ MLX5_NVPRT_LIST_TYPE_VLAN);
+ MLX5_SET(query_nic_vport_context_in, in, vport_number, vport);
+
+ if (vport)
+ MLX5_SET(query_nic_vport_context_in, in, other_vport, 1);
+
+ err = mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, out_sz);
+ if (err)
+ goto out;
+
+ nic_vport_ctx = MLX5_ADDR_OF(query_nic_vport_context_out, out,
+ nic_vport_context);
+ req_list_size = MLX5_GET(nic_vport_context, nic_vport_ctx,
+ allowed_list_size);
+
+ *size = req_list_size;
+ for (i = 0; i < req_list_size; i++) {
+ void *vlan_addr = MLX5_ADDR_OF(nic_vport_context,
+ nic_vport_ctx,
+ current_uc_mac_address[i]);
+ vlans[i] = MLX5_GET(vlan_layout, vlan_addr, vlan);
+ }
+out:
+ kfree(out);
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_vlans);
+
+int mlx5_modify_nic_vport_vlans(struct mlx5_core_dev *dev,
+ u16 vlans[],
+ int list_size)
+{
+ u32 out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)];
+ void *nic_vport_ctx;
+ int max_list_size;
+ int in_sz;
+ void *in;
+ int err;
+ int i;
+
+ max_list_size = 1 << MLX5_CAP_GEN(dev, log_max_vlan_list);
+
+ if (list_size > max_list_size)
+ return -ENOSPC;
+
+ in_sz = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in) +
+ list_size * MLX5_ST_SZ_BYTES(vlan_layout);
+
+ memset(out, 0, sizeof(out));
+ in = kzalloc(in_sz, GFP_KERNEL);
+ if (!in)
+ return -ENOMEM;
+
+ MLX5_SET(modify_nic_vport_context_in, in, opcode,
+ MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
+ MLX5_SET(modify_nic_vport_context_in, in,
+ field_select.addresses_list, 1);
+
+ nic_vport_ctx = MLX5_ADDR_OF(modify_nic_vport_context_in, in,
+ nic_vport_context);
+
+ MLX5_SET(nic_vport_context, nic_vport_ctx,
+ allowed_list_type, MLX5_NVPRT_LIST_TYPE_VLAN);
+ MLX5_SET(nic_vport_context, nic_vport_ctx,
+ allowed_list_size, list_size);
+
+ for (i = 0; i < list_size; i++) {
+ void *vlan_addr = MLX5_ADDR_OF(nic_vport_context,
+ nic_vport_ctx,
+ current_uc_mac_address[i]);
+ MLX5_SET(vlan_layout, vlan_addr, vlan, vlans[i]);
+ }
+
+ err = mlx5_cmd_exec_check_status(dev, in, in_sz, out, sizeof(out));
+ kfree(in);
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_vlans);
int mlx5_query_hca_vport_gid(struct mlx5_core_dev *dev, u8 other_vport,
u8 port_num, u16 vf_num, u16 gid_index,
@@ -343,3 +688,65 @@ int mlx5_query_hca_vport_node_guid(struct mlx5_core_dev *dev,
return err;
}
EXPORT_SYMBOL_GPL(mlx5_query_hca_vport_node_guid);
+
+int mlx5_query_nic_vport_promisc(struct mlx5_core_dev *mdev,
+ u32 vport,
+ int *promisc_uc,
+ int *promisc_mc,
+ int *promisc_all)
+{
+ u32 *out;
+ int outlen = MLX5_ST_SZ_BYTES(query_nic_vport_context_out);
+ int err;
+
+ out = kzalloc(outlen, GFP_KERNEL);
+ if (!out)
+ return -ENOMEM;
+
+ err = mlx5_query_nic_vport_context(mdev, vport, out, outlen);
+ if (err)
+ goto out;
+
+ *promisc_uc = MLX5_GET(query_nic_vport_context_out, out,
+ nic_vport_context.promisc_uc);
+ *promisc_mc = MLX5_GET(query_nic_vport_context_out, out,
+ nic_vport_context.promisc_mc);
+ *promisc_all = MLX5_GET(query_nic_vport_context_out, out,
+ nic_vport_context.promisc_all);
+
+out:
+ kfree(out);
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_promisc);
+
+int mlx5_modify_nic_vport_promisc(struct mlx5_core_dev *mdev,
+ int promisc_uc,
+ int promisc_mc,
+ int promisc_all)
+{
+ void *in;
+ int inlen = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in);
+ int err;
+
+ in = mlx5_vzalloc(inlen);
+ if (!in) {
+ mlx5_core_err(mdev, "failed to allocate inbox\n");
+ return -ENOMEM;
+ }
+
+ MLX5_SET(modify_nic_vport_context_in, in, field_select.promisc, 1);
+ MLX5_SET(modify_nic_vport_context_in, in,
+ nic_vport_context.promisc_uc, promisc_uc);
+ MLX5_SET(modify_nic_vport_context_in, in,
+ nic_vport_context.promisc_mc, promisc_mc);
+ MLX5_SET(modify_nic_vport_context_in, in,
+ nic_vport_context.promisc_all, promisc_all);
+
+ err = mlx5_modify_nic_vport_context(mdev, in, inlen);
+
+ kvfree(in);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(mlx5_modify_nic_vport_promisc);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig
index e36e12219c9b..ce26adcb4988 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig
+++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig
@@ -10,6 +10,14 @@ config MLXSW_CORE
To compile this driver as a module, choose M here: the
module will be called mlxsw_core.
+config MLXSW_CORE_HWMON
+ bool "HWMON support for Mellanox Technologies Switch ASICs"
+ depends on MLXSW_CORE && HWMON
+ depends on !(MLXSW_CORE=y && HWMON=m)
+ default y
+ ---help---
+ Say Y here if you want to expose HWMON interface on mlxsw devices.
+
config MLXSW_PCI
tristate "PCI bus implementation for Mellanox Technologies Switch ASICs"
depends on PCI && HAS_DMA && HAS_IOMEM && MLXSW_CORE
@@ -33,7 +41,7 @@ config MLXSW_SWITCHX2
config MLXSW_SPECTRUM
tristate "Mellanox Technologies Spectrum support"
- depends on MLXSW_CORE && NET_SWITCHDEV
+ depends on MLXSW_CORE && NET_SWITCHDEV && VLAN_8021Q
default m
---help---
This driver supports Mellanox Technologies Spectrum Ethernet
diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile
index af015818fd19..584cac444852 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/Makefile
+++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_MLXSW_CORE) += mlxsw_core.o
mlxsw_core-objs := core.o
+mlxsw_core-$(CONFIG_MLXSW_CORE_HWMON) += core_hwmon.o
obj-$(CONFIG_MLXSW_PCI) += mlxsw_pci.o
mlxsw_pci-objs := pci.o
obj-$(CONFIG_MLXSW_SWITCHX2) += mlxsw_switchx2.o
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c
index 97f0d93caf99..22379eb8e924 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core.c
@@ -105,6 +105,10 @@ struct mlxsw_core {
struct debugfs_blob_wrapper vsd_blob;
struct debugfs_blob_wrapper psid_blob;
} dbg;
+ struct {
+ u8 *mapping; /* lag_id+port_index to local_port mapping */
+ } lag;
+ struct mlxsw_hwmon *hwmon;
unsigned long driver_priv[0];
/* driver_priv has to be always the last item */
};
@@ -814,6 +818,17 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
goto err_alloc_stats;
}
+ if (mlxsw_driver->profile->used_max_lag &&
+ mlxsw_driver->profile->used_max_port_per_lag) {
+ alloc_size = sizeof(u8) * mlxsw_driver->profile->max_lag *
+ mlxsw_driver->profile->max_port_per_lag;
+ mlxsw_core->lag.mapping = kzalloc(alloc_size, GFP_KERNEL);
+ if (!mlxsw_core->lag.mapping) {
+ err = -ENOMEM;
+ goto err_alloc_lag_mapping;
+ }
+ }
+
err = mlxsw_bus->init(bus_priv, mlxsw_core, mlxsw_driver->profile);
if (err)
goto err_bus_init;
@@ -822,6 +837,10 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
if (err)
goto err_emad_init;
+ err = mlxsw_hwmon_init(mlxsw_core, mlxsw_bus_info, &mlxsw_core->hwmon);
+ if (err)
+ goto err_hwmon_init;
+
err = mlxsw_driver->init(mlxsw_core->driver_priv, mlxsw_core,
mlxsw_bus_info);
if (err)
@@ -836,10 +855,13 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
err_debugfs_init:
mlxsw_core->driver->fini(mlxsw_core->driver_priv);
err_driver_init:
+err_hwmon_init:
mlxsw_emad_fini(mlxsw_core);
err_emad_init:
mlxsw_bus->fini(bus_priv);
err_bus_init:
+ kfree(mlxsw_core->lag.mapping);
+err_alloc_lag_mapping:
free_percpu(mlxsw_core->pcpu_stats);
err_alloc_stats:
kfree(mlxsw_core);
@@ -857,6 +879,7 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core)
mlxsw_core->driver->fini(mlxsw_core->driver_priv);
mlxsw_emad_fini(mlxsw_core);
mlxsw_core->bus->fini(mlxsw_core->bus_priv);
+ kfree(mlxsw_core->lag.mapping);
free_percpu(mlxsw_core->pcpu_stats);
kfree(mlxsw_core);
mlxsw_core_driver_put(device_kind);
@@ -1188,11 +1211,25 @@ void mlxsw_core_skb_receive(struct mlxsw_core *mlxsw_core, struct sk_buff *skb,
struct mlxsw_rx_listener_item *rxl_item;
const struct mlxsw_rx_listener *rxl;
struct mlxsw_core_pcpu_stats *pcpu_stats;
- u8 local_port = rx_info->sys_port;
+ u8 local_port;
bool found = false;
- dev_dbg_ratelimited(mlxsw_core->bus_info->dev, "%s: sys_port = %d, trap_id = 0x%x\n",
- __func__, rx_info->sys_port, rx_info->trap_id);
+ if (rx_info->is_lag) {
+ dev_dbg_ratelimited(mlxsw_core->bus_info->dev, "%s: lag_id = %d, lag_port_index = 0x%x\n",
+ __func__, rx_info->u.lag_id,
+ rx_info->trap_id);
+ /* Upper layer does not care if the skb came from LAG or not,
+ * so just get the local_port for the lag port and push it up.
+ */
+ local_port = mlxsw_core_lag_mapping_get(mlxsw_core,
+ rx_info->u.lag_id,
+ rx_info->lag_port_index);
+ } else {
+ local_port = rx_info->u.sys_port;
+ }
+
+ dev_dbg_ratelimited(mlxsw_core->bus_info->dev, "%s: local_port = %d, trap_id = 0x%x\n",
+ __func__, local_port, rx_info->trap_id);
if ((rx_info->trap_id >= MLXSW_TRAP_ID_MAX) ||
(local_port >= MLXSW_PORT_MAX_PORTS))
@@ -1236,6 +1273,48 @@ drop:
}
EXPORT_SYMBOL(mlxsw_core_skb_receive);
+static int mlxsw_core_lag_mapping_index(struct mlxsw_core *mlxsw_core,
+ u16 lag_id, u8 port_index)
+{
+ return mlxsw_core->driver->profile->max_port_per_lag * lag_id +
+ port_index;
+}
+
+void mlxsw_core_lag_mapping_set(struct mlxsw_core *mlxsw_core,
+ u16 lag_id, u8 port_index, u8 local_port)
+{
+ int index = mlxsw_core_lag_mapping_index(mlxsw_core,
+ lag_id, port_index);
+
+ mlxsw_core->lag.mapping[index] = local_port;
+}
+EXPORT_SYMBOL(mlxsw_core_lag_mapping_set);
+
+u8 mlxsw_core_lag_mapping_get(struct mlxsw_core *mlxsw_core,
+ u16 lag_id, u8 port_index)
+{
+ int index = mlxsw_core_lag_mapping_index(mlxsw_core,
+ lag_id, port_index);
+
+ return mlxsw_core->lag.mapping[index];
+}
+EXPORT_SYMBOL(mlxsw_core_lag_mapping_get);
+
+void mlxsw_core_lag_mapping_clear(struct mlxsw_core *mlxsw_core,
+ u16 lag_id, u8 local_port)
+{
+ int i;
+
+ for (i = 0; i < mlxsw_core->driver->profile->max_port_per_lag; i++) {
+ int index = mlxsw_core_lag_mapping_index(mlxsw_core,
+ lag_id, i);
+
+ if (mlxsw_core->lag.mapping[index] == local_port)
+ mlxsw_core->lag.mapping[index] = 0;
+ }
+}
+EXPORT_SYMBOL(mlxsw_core_lag_mapping_clear);
+
int mlxsw_cmd_exec(struct mlxsw_core *mlxsw_core, u16 opcode, u8 opcode_mod,
u32 in_mod, bool out_mbox_direct,
char *in_mbox, size_t in_mbox_size,
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h
index 807827350a89..a01723600f0a 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/core.h
@@ -112,13 +112,25 @@ int mlxsw_reg_write(struct mlxsw_core *mlxsw_core,
const struct mlxsw_reg_info *reg, char *payload);
struct mlxsw_rx_info {
- u16 sys_port;
+ bool is_lag;
+ union {
+ u16 sys_port;
+ u16 lag_id;
+ } u;
+ u8 lag_port_index;
int trap_id;
};
void mlxsw_core_skb_receive(struct mlxsw_core *mlxsw_core, struct sk_buff *skb,
struct mlxsw_rx_info *rx_info);
+void mlxsw_core_lag_mapping_set(struct mlxsw_core *mlxsw_core,
+ u16 lag_id, u8 port_index, u8 local_port);
+u8 mlxsw_core_lag_mapping_get(struct mlxsw_core *mlxsw_core,
+ u16 lag_id, u8 port_index);
+void mlxsw_core_lag_mapping_clear(struct mlxsw_core *mlxsw_core,
+ u16 lag_id, u8 local_port);
+
#define MLXSW_CONFIG_PROFILE_SWID_COUNT 8
struct mlxsw_swid_config {
@@ -209,4 +221,24 @@ struct mlxsw_bus_info {
u8 psid[MLXSW_CMD_BOARDINFO_PSID_LEN];
};
+struct mlxsw_hwmon;
+
+#ifdef CONFIG_MLXSW_CORE_HWMON
+
+int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
+ const struct mlxsw_bus_info *mlxsw_bus_info,
+ struct mlxsw_hwmon **p_hwmon);
+void mlxsw_hwmon_fini(struct mlxsw_hwmon *mlxsw_hwmon);
+
+#else
+
+static inline int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
+ const struct mlxsw_bus_info *mlxsw_bus_info,
+ struct mlxsw_hwmon **p_hwmon)
+{
+ return 0;
+}
+
+#endif
+
#endif
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c
new file mode 100644
index 000000000000..1ac8bf187168
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c
@@ -0,0 +1,372 @@
+/*
+ * drivers/net/ethernet/mellanox/mlxsw/core_hwmon.c
+ * Copyright (c) 2015 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2015 Jiri Pirko <jiri@mellanox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/err.h>
+
+#include "core.h"
+
+#define MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT 127
+#define MLXSW_HWMON_ATTR_COUNT (MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT * 4 + \
+ MLXSW_MFCR_TACHOS_MAX + MLXSW_MFCR_PWMS_MAX)
+
+struct mlxsw_hwmon_attr {
+ struct device_attribute dev_attr;
+ struct mlxsw_hwmon *hwmon;
+ unsigned int type_index;
+ char name[32];
+};
+
+struct mlxsw_hwmon {
+ struct mlxsw_core *core;
+ const struct mlxsw_bus_info *bus_info;
+ struct device *hwmon_dev;
+ struct attribute_group group;
+ const struct attribute_group *groups[2];
+ struct attribute *attrs[MLXSW_HWMON_ATTR_COUNT + 1];
+ struct mlxsw_hwmon_attr hwmon_attrs[MLXSW_HWMON_ATTR_COUNT];
+ unsigned int attrs_count;
+};
+
+static ssize_t mlxsw_hwmon_temp_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+ container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+ struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+ char mtmp_pl[MLXSW_REG_MTMP_LEN];
+ unsigned int temp;
+ int err;
+
+ mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index,
+ false, false);
+ err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
+ if (err) {
+ dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query temp sensor\n");
+ return err;
+ }
+ mlxsw_reg_mtmp_unpack(mtmp_pl, &temp, NULL, NULL);
+ return sprintf(buf, "%u\n", temp);
+}
+
+static ssize_t mlxsw_hwmon_temp_max_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+ container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+ struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+ char mtmp_pl[MLXSW_REG_MTMP_LEN];
+ unsigned int temp_max;
+ int err;
+
+ mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index,
+ false, false);
+ err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
+ if (err) {
+ dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query temp sensor\n");
+ return err;
+ }
+ mlxsw_reg_mtmp_unpack(mtmp_pl, NULL, &temp_max, NULL);
+ return sprintf(buf, "%u\n", temp_max);
+}
+
+static ssize_t mlxsw_hwmon_temp_rst_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+ container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+ struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+ char mtmp_pl[MLXSW_REG_MTMP_LEN];
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+ if (val != 1)
+ return -EINVAL;
+
+ mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index, true, true);
+ err = mlxsw_reg_write(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
+ if (err) {
+ dev_err(mlxsw_hwmon->bus_info->dev, "Failed to reset temp sensor history\n");
+ return err;
+ }
+ return len;
+}
+
+static ssize_t mlxsw_hwmon_fan_rpm_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+ container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+ struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+ char mfsm_pl[MLXSW_REG_MFSM_LEN];
+ int err;
+
+ mlxsw_reg_mfsm_pack(mfsm_pl, mlwsw_hwmon_attr->type_index);
+ err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfsm), mfsm_pl);
+ if (err) {
+ dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query fan\n");
+ return err;
+ }
+ return sprintf(buf, "%u\n", mlxsw_reg_mfsm_rpm_get(mfsm_pl));
+}
+
+static ssize_t mlxsw_hwmon_pwm_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+ container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+ struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+ char mfsc_pl[MLXSW_REG_MFSC_LEN];
+ int err;
+
+ mlxsw_reg_mfsc_pack(mfsc_pl, mlwsw_hwmon_attr->type_index, 0);
+ err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfsc), mfsc_pl);
+ if (err) {
+ dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query PWM\n");
+ return err;
+ }
+ return sprintf(buf, "%u\n",
+ mlxsw_reg_mfsc_pwm_duty_cycle_get(mfsc_pl));
+}
+
+static ssize_t mlxsw_hwmon_pwm_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
+ container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
+ struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
+ char mfsc_pl[MLXSW_REG_MFSC_LEN];
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+ if (val > 255)
+ return -EINVAL;
+
+ mlxsw_reg_mfsc_pack(mfsc_pl, mlwsw_hwmon_attr->type_index, val);
+ err = mlxsw_reg_write(mlxsw_hwmon->core, MLXSW_REG(mfsc), mfsc_pl);
+ if (err) {
+ dev_err(mlxsw_hwmon->bus_info->dev, "Failed to write PWM\n");
+ return err;
+ }
+ return len;
+}
+
+enum mlxsw_hwmon_attr_type {
+ MLXSW_HWMON_ATTR_TYPE_TEMP,
+ MLXSW_HWMON_ATTR_TYPE_TEMP_MAX,
+ MLXSW_HWMON_ATTR_TYPE_TEMP_RST,
+ MLXSW_HWMON_ATTR_TYPE_FAN_RPM,
+ MLXSW_HWMON_ATTR_TYPE_PWM,
+};
+
+static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon,
+ enum mlxsw_hwmon_attr_type attr_type,
+ unsigned int type_index, unsigned int num) {
+ struct mlxsw_hwmon_attr *mlxsw_hwmon_attr;
+ unsigned int attr_index;
+
+ attr_index = mlxsw_hwmon->attrs_count;
+ mlxsw_hwmon_attr = &mlxsw_hwmon->hwmon_attrs[attr_index];
+
+ switch (attr_type) {
+ case MLXSW_HWMON_ATTR_TYPE_TEMP:
+ mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_temp_show;
+ mlxsw_hwmon_attr->dev_attr.attr.mode = S_IRUGO;
+ snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
+ "temp%u_input", num + 1);
+ break;
+ case MLXSW_HWMON_ATTR_TYPE_TEMP_MAX:
+ mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_temp_max_show;
+ mlxsw_hwmon_attr->dev_attr.attr.mode = S_IRUGO;
+ snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
+ "temp%u_highest", num + 1);
+ break;
+ case MLXSW_HWMON_ATTR_TYPE_TEMP_RST:
+ mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_temp_rst_store;
+ mlxsw_hwmon_attr->dev_attr.attr.mode = S_IWUSR;
+ snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
+ "temp%u_reset_history", num + 1);
+ break;
+ case MLXSW_HWMON_ATTR_TYPE_FAN_RPM:
+ mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_fan_rpm_show;
+ mlxsw_hwmon_attr->dev_attr.attr.mode = S_IRUGO;
+ snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
+ "fan%u_input", num + 1);
+ break;
+ case MLXSW_HWMON_ATTR_TYPE_PWM:
+ mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_pwm_show;
+ mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_pwm_store;
+ mlxsw_hwmon_attr->dev_attr.attr.mode = S_IWUSR | S_IRUGO;
+ snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
+ "pwm%u", num + 1);
+ break;
+ default:
+ WARN_ON(1);
+ }
+
+ mlxsw_hwmon_attr->type_index = type_index;
+ mlxsw_hwmon_attr->hwmon = mlxsw_hwmon;
+ mlxsw_hwmon_attr->dev_attr.attr.name = mlxsw_hwmon_attr->name;
+ sysfs_attr_init(&mlxsw_hwmon_attr->dev_attr.attr);
+
+ mlxsw_hwmon->attrs[attr_index] = &mlxsw_hwmon_attr->dev_attr.attr;
+ mlxsw_hwmon->attrs_count++;
+}
+
+static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon *mlxsw_hwmon)
+{
+ char mtcap_pl[MLXSW_REG_MTCAP_LEN];
+ char mtmp_pl[MLXSW_REG_MTMP_LEN];
+ u8 sensor_count;
+ int i;
+ int err;
+
+ err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtcap), mtcap_pl);
+ if (err) {
+ dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get number of temp sensors\n");
+ return err;
+ }
+ sensor_count = mlxsw_reg_mtcap_sensor_count_get(mtcap_pl);
+ for (i = 0; i < sensor_count; i++) {
+ mlxsw_reg_mtmp_pack(mtmp_pl, i, true, true);
+ err = mlxsw_reg_write(mlxsw_hwmon->core,
+ MLXSW_REG(mtmp), mtmp_pl);
+ if (err) {
+ dev_err(mlxsw_hwmon->bus_info->dev, "Failed to setup temp sensor number %d\n",
+ i);
+ return err;
+ }
+ mlxsw_hwmon_attr_add(mlxsw_hwmon,
+ MLXSW_HWMON_ATTR_TYPE_TEMP, i, i);
+ mlxsw_hwmon_attr_add(mlxsw_hwmon,
+ MLXSW_HWMON_ATTR_TYPE_TEMP_MAX, i, i);
+ mlxsw_hwmon_attr_add(mlxsw_hwmon,
+ MLXSW_HWMON_ATTR_TYPE_TEMP_RST, i, i);
+ }
+ return 0;
+}
+
+static int mlxsw_hwmon_fans_init(struct mlxsw_hwmon *mlxsw_hwmon)
+{
+ char mfcr_pl[MLXSW_REG_MFCR_LEN];
+ enum mlxsw_reg_mfcr_pwm_frequency freq;
+ unsigned int type_index;
+ unsigned int num;
+ u16 tacho_active;
+ u8 pwm_active;
+ int err;
+
+ err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfcr), mfcr_pl);
+ if (err) {
+ dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get to probe PWMs and Tachometers\n");
+ return err;
+ }
+ mlxsw_reg_mfcr_unpack(mfcr_pl, &freq, &tacho_active, &pwm_active);
+ num = 0;
+ for (type_index = 0; type_index < MLXSW_MFCR_TACHOS_MAX; type_index++) {
+ if (tacho_active & BIT(type_index))
+ mlxsw_hwmon_attr_add(mlxsw_hwmon,
+ MLXSW_HWMON_ATTR_TYPE_FAN_RPM,
+ type_index, num++);
+ }
+ num = 0;
+ for (type_index = 0; type_index < MLXSW_MFCR_PWMS_MAX; type_index++) {
+ if (pwm_active & BIT(type_index))
+ mlxsw_hwmon_attr_add(mlxsw_hwmon,
+ MLXSW_HWMON_ATTR_TYPE_PWM,
+ type_index, num++);
+ }
+ return 0;
+}
+
+int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
+ const struct mlxsw_bus_info *mlxsw_bus_info,
+ struct mlxsw_hwmon **p_hwmon)
+{
+ struct mlxsw_hwmon *mlxsw_hwmon;
+ struct device *hwmon_dev;
+ int err;
+
+ mlxsw_hwmon = devm_kzalloc(mlxsw_bus_info->dev, sizeof(*mlxsw_hwmon),
+ GFP_KERNEL);
+ if (!mlxsw_hwmon)
+ return -ENOMEM;
+ mlxsw_hwmon->core = mlxsw_core;
+ mlxsw_hwmon->bus_info = mlxsw_bus_info;
+
+ err = mlxsw_hwmon_temp_init(mlxsw_hwmon);
+ if (err)
+ goto err_temp_init;
+
+ err = mlxsw_hwmon_fans_init(mlxsw_hwmon);
+ if (err)
+ goto err_fans_init;
+
+ mlxsw_hwmon->groups[0] = &mlxsw_hwmon->group;
+ mlxsw_hwmon->group.attrs = mlxsw_hwmon->attrs;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(mlxsw_bus_info->dev,
+ "mlxsw",
+ mlxsw_hwmon,
+ mlxsw_hwmon->groups);
+ if (IS_ERR(hwmon_dev)) {
+ err = PTR_ERR(hwmon_dev);
+ goto err_hwmon_register;
+ }
+
+ mlxsw_hwmon->hwmon_dev = hwmon_dev;
+ *p_hwmon = mlxsw_hwmon;
+ return 0;
+
+err_hwmon_register:
+err_fans_init:
+err_temp_init:
+ return err;
+}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c
index de69e719dc9d..c071077aafbd 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/pci.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c
@@ -384,7 +384,7 @@ static int mlxsw_pci_sdq_init(struct mlxsw_pci *mlxsw_pci, char *mbox,
/* Set CQ of same number of this SDQ. */
mlxsw_cmd_mbox_sw2hw_dq_cq_set(mbox, q->num);
- mlxsw_cmd_mbox_sw2hw_dq_sdq_tclass_set(mbox, 7);
+ mlxsw_cmd_mbox_sw2hw_dq_sdq_tclass_set(mbox, 3);
mlxsw_cmd_mbox_sw2hw_dq_log2_dq_sz_set(mbox, 3); /* 8 pages */
for (i = 0; i < MLXSW_PCI_AQ_PAGES; i++) {
dma_addr_t mapaddr = __mlxsw_pci_queue_page_get(q, i);
@@ -686,11 +686,15 @@ static void mlxsw_pci_cqe_rdq_handle(struct mlxsw_pci *mlxsw_pci,
if (q->consumer_counter++ != consumer_counter_limit)
dev_dbg_ratelimited(&pdev->dev, "Consumer counter does not match limit in RDQ\n");
- /* We do not support lag now */
- if (mlxsw_pci_cqe_lag_get(cqe))
- goto drop;
+ if (mlxsw_pci_cqe_lag_get(cqe)) {
+ rx_info.is_lag = true;
+ rx_info.u.lag_id = mlxsw_pci_cqe_lag_id_get(cqe);
+ rx_info.lag_port_index = mlxsw_pci_cqe_lag_port_index_get(cqe);
+ } else {
+ rx_info.is_lag = false;
+ rx_info.u.sys_port = mlxsw_pci_cqe_system_port_get(cqe);
+ }
- rx_info.sys_port = mlxsw_pci_cqe_system_port_get(cqe);
rx_info.trap_id = mlxsw_pci_cqe_trap_id_get(cqe);
byte_count = mlxsw_pci_cqe_byte_count_get(cqe);
@@ -699,7 +703,6 @@ static void mlxsw_pci_cqe_rdq_handle(struct mlxsw_pci *mlxsw_pci,
skb_put(skb, byte_count);
mlxsw_core_skb_receive(mlxsw_pci->core, skb, &rx_info);
-put_new_skb:
memset(wqe, 0, q->elem_size);
err = mlxsw_pci_rdq_skb_alloc(mlxsw_pci, elem_info);
if (err)
@@ -708,10 +711,6 @@ put_new_skb:
q->producer_counter++;
mlxsw_pci_queue_doorbell_producer_ring(mlxsw_pci, q);
return;
-
-drop:
- dev_kfree_skb_any(skb);
- goto put_new_skb;
}
static char *mlxsw_pci_cq_sw_cqe_get(struct mlxsw_pci_queue *q)
diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.h b/drivers/net/ethernet/mellanox/mlxsw/pci.h
index 142f33d978c5..912106054ff2 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/pci.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/pci.h
@@ -129,13 +129,15 @@ MLXSW_ITEM64_INDEXED(pci, wqe, address, 0x08, 0, 64, 0x8, 0x0, false);
*/
MLXSW_ITEM32(pci, cqe, lag, 0x00, 23, 1);
-/* pci_cqe_system_port
+/* pci_cqe_system_port/lag_id
* When lag=0: System port on which the packet was received
* When lag=1:
* bits [15:4] LAG ID on which the packet was received
* bits [3:0] sub_port on which the packet was received
*/
MLXSW_ITEM32(pci, cqe, system_port, 0x00, 0, 16);
+MLXSW_ITEM32(pci, cqe, lag_id, 0x00, 4, 12);
+MLXSW_ITEM32(pci, cqe, lag_port_index, 0x00, 0, 4);
/* pci_cqe_wqe_counter
* WQE count of the WQEs completed on the associated dqn
diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h
index 236fb5d2ad69..0c5237264e3e 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/reg.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h
@@ -99,6 +99,55 @@ static const struct mlxsw_reg_info mlxsw_reg_spad = {
*/
MLXSW_ITEM_BUF(reg, spad, base_mac, 0x02, 6);
+/* SMID - Switch Multicast ID
+ * --------------------------
+ * The MID record maps from a MID (Multicast ID), which is a unique identifier
+ * of the multicast group within the stacking domain, into a list of local
+ * ports into which the packet is replicated.
+ */
+#define MLXSW_REG_SMID_ID 0x2007
+#define MLXSW_REG_SMID_LEN 0x240
+
+static const struct mlxsw_reg_info mlxsw_reg_smid = {
+ .id = MLXSW_REG_SMID_ID,
+ .len = MLXSW_REG_SMID_LEN,
+};
+
+/* reg_smid_swid
+ * Switch partition ID.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, smid, swid, 0x00, 24, 8);
+
+/* reg_smid_mid
+ * Multicast identifier - global identifier that represents the multicast group
+ * across all devices.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, smid, mid, 0x00, 0, 16);
+
+/* reg_smid_port
+ * Local port memebership (1 bit per port).
+ * Access: RW
+ */
+MLXSW_ITEM_BIT_ARRAY(reg, smid, port, 0x20, 0x20, 1);
+
+/* reg_smid_port_mask
+ * Local port mask (1 bit per port).
+ * Access: W
+ */
+MLXSW_ITEM_BIT_ARRAY(reg, smid, port_mask, 0x220, 0x20, 1);
+
+static inline void mlxsw_reg_smid_pack(char *payload, u16 mid,
+ u8 port, bool set)
+{
+ MLXSW_REG_ZERO(smid, payload);
+ mlxsw_reg_smid_swid_set(payload, 0);
+ mlxsw_reg_smid_mid_set(payload, mid);
+ mlxsw_reg_smid_port_set(payload, port, set);
+ mlxsw_reg_smid_port_mask_set(payload, port, 1);
+}
+
/* SSPR - Switch System Port Record Register
* -----------------------------------------
* Configures the system port to local port mapping.
@@ -286,6 +335,8 @@ MLXSW_ITEM32_INDEXED(reg, sfd, rec_swid, MLXSW_REG_SFD_BASE_LEN, 24, 8,
enum mlxsw_reg_sfd_rec_type {
MLXSW_REG_SFD_REC_TYPE_UNICAST = 0x0,
+ MLXSW_REG_SFD_REC_TYPE_UNICAST_LAG = 0x1,
+ MLXSW_REG_SFD_REC_TYPE_MULTICAST = 0x2,
};
/* reg_sfd_rec_type
@@ -376,36 +427,144 @@ MLXSW_ITEM32_INDEXED(reg, sfd, uc_fid_vid, MLXSW_REG_SFD_BASE_LEN, 0, 16,
MLXSW_ITEM32_INDEXED(reg, sfd, uc_system_port, MLXSW_REG_SFD_BASE_LEN, 0, 16,
MLXSW_REG_SFD_REC_LEN, 0x0C, false);
-static inline void mlxsw_reg_sfd_uc_pack(char *payload, int rec_index,
- enum mlxsw_reg_sfd_rec_policy policy,
- const char *mac, u16 vid,
- enum mlxsw_reg_sfd_rec_action action,
- u8 local_port)
+static inline void mlxsw_reg_sfd_rec_pack(char *payload, int rec_index,
+ enum mlxsw_reg_sfd_rec_type rec_type,
+ const char *mac,
+ enum mlxsw_reg_sfd_rec_action action)
{
u8 num_rec = mlxsw_reg_sfd_num_rec_get(payload);
if (rec_index >= num_rec)
mlxsw_reg_sfd_num_rec_set(payload, rec_index + 1);
mlxsw_reg_sfd_rec_swid_set(payload, rec_index, 0);
- mlxsw_reg_sfd_rec_type_set(payload, rec_index,
- MLXSW_REG_SFD_REC_TYPE_UNICAST);
- mlxsw_reg_sfd_rec_policy_set(payload, rec_index, policy);
+ mlxsw_reg_sfd_rec_type_set(payload, rec_index, rec_type);
mlxsw_reg_sfd_rec_mac_memcpy_to(payload, rec_index, mac);
- mlxsw_reg_sfd_uc_sub_port_set(payload, rec_index, 0);
- mlxsw_reg_sfd_uc_fid_vid_set(payload, rec_index, vid);
mlxsw_reg_sfd_rec_action_set(payload, rec_index, action);
+}
+
+static inline void mlxsw_reg_sfd_uc_pack(char *payload, int rec_index,
+ enum mlxsw_reg_sfd_rec_policy policy,
+ const char *mac, u16 fid_vid,
+ enum mlxsw_reg_sfd_rec_action action,
+ u8 local_port)
+{
+ mlxsw_reg_sfd_rec_pack(payload, rec_index,
+ MLXSW_REG_SFD_REC_TYPE_UNICAST, mac, action);
+ mlxsw_reg_sfd_rec_policy_set(payload, rec_index, policy);
+ mlxsw_reg_sfd_uc_sub_port_set(payload, rec_index, 0);
+ mlxsw_reg_sfd_uc_fid_vid_set(payload, rec_index, fid_vid);
mlxsw_reg_sfd_uc_system_port_set(payload, rec_index, local_port);
}
static inline void mlxsw_reg_sfd_uc_unpack(char *payload, int rec_index,
- char *mac, u16 *p_vid,
+ char *mac, u16 *p_fid_vid,
u8 *p_local_port)
{
mlxsw_reg_sfd_rec_mac_memcpy_from(payload, rec_index, mac);
- *p_vid = mlxsw_reg_sfd_uc_fid_vid_get(payload, rec_index);
+ *p_fid_vid = mlxsw_reg_sfd_uc_fid_vid_get(payload, rec_index);
*p_local_port = mlxsw_reg_sfd_uc_system_port_get(payload, rec_index);
}
+/* reg_sfd_uc_lag_sub_port
+ * LAG sub port.
+ * Must be 0 if multichannel VEPA is not enabled.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, uc_lag_sub_port, MLXSW_REG_SFD_BASE_LEN, 16, 8,
+ MLXSW_REG_SFD_REC_LEN, 0x08, false);
+
+/* reg_sfd_uc_lag_fid_vid
+ * Filtering ID or VLAN ID
+ * For SwitchX and SwitchX-2:
+ * - Dynamic entries (policy 2,3) use FID
+ * - Static entries (policy 0) use VID
+ * - When independent learning is configured, VID=FID
+ * For Spectrum: use FID for both Dynamic and Static entries.
+ * VID should not be used.
+ * Access: Index
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, uc_lag_fid_vid, MLXSW_REG_SFD_BASE_LEN, 0, 16,
+ MLXSW_REG_SFD_REC_LEN, 0x08, false);
+
+/* reg_sfd_uc_lag_lag_vid
+ * Indicates VID in case of vFIDs. Reserved for FIDs.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, uc_lag_lag_vid, MLXSW_REG_SFD_BASE_LEN, 16, 12,
+ MLXSW_REG_SFD_REC_LEN, 0x0C, false);
+
+/* reg_sfd_uc_lag_lag_id
+ * LAG Identifier - pointer into the LAG descriptor table.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, uc_lag_lag_id, MLXSW_REG_SFD_BASE_LEN, 0, 10,
+ MLXSW_REG_SFD_REC_LEN, 0x0C, false);
+
+static inline void
+mlxsw_reg_sfd_uc_lag_pack(char *payload, int rec_index,
+ enum mlxsw_reg_sfd_rec_policy policy,
+ const char *mac, u16 fid_vid,
+ enum mlxsw_reg_sfd_rec_action action, u16 lag_vid,
+ u16 lag_id)
+{
+ mlxsw_reg_sfd_rec_pack(payload, rec_index,
+ MLXSW_REG_SFD_REC_TYPE_UNICAST_LAG,
+ mac, action);
+ mlxsw_reg_sfd_rec_policy_set(payload, rec_index, policy);
+ mlxsw_reg_sfd_uc_lag_sub_port_set(payload, rec_index, 0);
+ mlxsw_reg_sfd_uc_lag_fid_vid_set(payload, rec_index, fid_vid);
+ mlxsw_reg_sfd_uc_lag_lag_vid_set(payload, rec_index, lag_vid);
+ mlxsw_reg_sfd_uc_lag_lag_id_set(payload, rec_index, lag_id);
+}
+
+static inline void mlxsw_reg_sfd_uc_lag_unpack(char *payload, int rec_index,
+ char *mac, u16 *p_vid,
+ u16 *p_lag_id)
+{
+ mlxsw_reg_sfd_rec_mac_memcpy_from(payload, rec_index, mac);
+ *p_vid = mlxsw_reg_sfd_uc_lag_fid_vid_get(payload, rec_index);
+ *p_lag_id = mlxsw_reg_sfd_uc_lag_lag_id_get(payload, rec_index);
+}
+
+/* reg_sfd_mc_pgi
+ *
+ * Multicast port group index - index into the port group table.
+ * Value 0x1FFF indicates the pgi should point to the MID entry.
+ * For Spectrum this value must be set to 0x1FFF
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, mc_pgi, MLXSW_REG_SFD_BASE_LEN, 16, 13,
+ MLXSW_REG_SFD_REC_LEN, 0x08, false);
+
+/* reg_sfd_mc_fid_vid
+ *
+ * Filtering ID or VLAN ID
+ * Access: Index
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, mc_fid_vid, MLXSW_REG_SFD_BASE_LEN, 0, 16,
+ MLXSW_REG_SFD_REC_LEN, 0x08, false);
+
+/* reg_sfd_mc_mid
+ *
+ * Multicast identifier - global identifier that represents the multicast
+ * group across all devices.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, sfd, mc_mid, MLXSW_REG_SFD_BASE_LEN, 0, 16,
+ MLXSW_REG_SFD_REC_LEN, 0x0C, false);
+
+static inline void
+mlxsw_reg_sfd_mc_pack(char *payload, int rec_index,
+ const char *mac, u16 fid_vid,
+ enum mlxsw_reg_sfd_rec_action action, u16 mid)
+{
+ mlxsw_reg_sfd_rec_pack(payload, rec_index,
+ MLXSW_REG_SFD_REC_TYPE_MULTICAST, mac, action);
+ mlxsw_reg_sfd_mc_pgi_set(payload, rec_index, 0x1FFF);
+ mlxsw_reg_sfd_mc_fid_vid_set(payload, rec_index, fid_vid);
+ mlxsw_reg_sfd_mc_mid_set(payload, rec_index, mid);
+}
+
/* SFN - Switch FDB Notification Register
* -------------------------------------------
* The switch provides notifications on newly learned FDB entries and
@@ -456,8 +615,12 @@ MLXSW_ITEM32_INDEXED(reg, sfn, rec_swid, MLXSW_REG_SFN_BASE_LEN, 24, 8,
enum mlxsw_reg_sfn_rec_type {
/* MAC addresses learned on a regular port. */
MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC = 0x5,
- /* Aged-out MAC address on a regular port */
+ /* MAC addresses learned on a LAG port. */
+ MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC_LAG = 0x6,
+ /* Aged-out MAC address on a regular port. */
MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC = 0x7,
+ /* Aged-out MAC address on a LAG port. */
+ MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC_LAG = 0x8,
};
/* reg_sfn_rec_type
@@ -505,6 +668,22 @@ static inline void mlxsw_reg_sfn_mac_unpack(char *payload, int rec_index,
*p_local_port = mlxsw_reg_sfn_mac_system_port_get(payload, rec_index);
}
+/* reg_sfn_mac_lag_lag_id
+ * LAG ID (pointer into the LAG descriptor table).
+ * Access: RO
+ */
+MLXSW_ITEM32_INDEXED(reg, sfn, mac_lag_lag_id, MLXSW_REG_SFN_BASE_LEN, 0, 10,
+ MLXSW_REG_SFN_REC_LEN, 0x0C, false);
+
+static inline void mlxsw_reg_sfn_mac_lag_unpack(char *payload, int rec_index,
+ char *mac, u16 *p_vid,
+ u16 *p_lag_id)
+{
+ mlxsw_reg_sfn_rec_mac_memcpy_from(payload, rec_index, mac);
+ *p_vid = mlxsw_reg_sfn_mac_fid_get(payload, rec_index);
+ *p_lag_id = mlxsw_reg_sfn_mac_lag_lag_id_get(payload, rec_index);
+}
+
/* SPMS - Switch Port MSTP/RSTP State Register
* -------------------------------------------
* Configures the spanning tree state of a physical port.
@@ -865,6 +1044,293 @@ static inline void mlxsw_reg_sftr_pack(char *payload,
mlxsw_reg_sftr_port_mask_set(payload, port, 1);
}
+/* SLDR - Switch LAG Descriptor Register
+ * -----------------------------------------
+ * The switch LAG descriptor register is populated by LAG descriptors.
+ * Each LAG descriptor is indexed by lag_id. The LAG ID runs from 0 to
+ * max_lag-1.
+ */
+#define MLXSW_REG_SLDR_ID 0x2014
+#define MLXSW_REG_SLDR_LEN 0x0C /* counting in only one port in list */
+
+static const struct mlxsw_reg_info mlxsw_reg_sldr = {
+ .id = MLXSW_REG_SLDR_ID,
+ .len = MLXSW_REG_SLDR_LEN,
+};
+
+enum mlxsw_reg_sldr_op {
+ /* Indicates a creation of a new LAG-ID, lag_id must be valid */
+ MLXSW_REG_SLDR_OP_LAG_CREATE,
+ MLXSW_REG_SLDR_OP_LAG_DESTROY,
+ /* Ports that appear in the list have the Distributor enabled */
+ MLXSW_REG_SLDR_OP_LAG_ADD_PORT_LIST,
+ /* Removes ports from the disributor list */
+ MLXSW_REG_SLDR_OP_LAG_REMOVE_PORT_LIST,
+};
+
+/* reg_sldr_op
+ * Operation.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sldr, op, 0x00, 29, 3);
+
+/* reg_sldr_lag_id
+ * LAG identifier. The lag_id is the index into the LAG descriptor table.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, sldr, lag_id, 0x00, 0, 10);
+
+static inline void mlxsw_reg_sldr_lag_create_pack(char *payload, u8 lag_id)
+{
+ MLXSW_REG_ZERO(sldr, payload);
+ mlxsw_reg_sldr_op_set(payload, MLXSW_REG_SLDR_OP_LAG_CREATE);
+ mlxsw_reg_sldr_lag_id_set(payload, lag_id);
+}
+
+static inline void mlxsw_reg_sldr_lag_destroy_pack(char *payload, u8 lag_id)
+{
+ MLXSW_REG_ZERO(sldr, payload);
+ mlxsw_reg_sldr_op_set(payload, MLXSW_REG_SLDR_OP_LAG_DESTROY);
+ mlxsw_reg_sldr_lag_id_set(payload, lag_id);
+}
+
+/* reg_sldr_num_ports
+ * The number of member ports of the LAG.
+ * Reserved for Create / Destroy operations
+ * For Add / Remove operations - indicates the number of ports in the list.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, sldr, num_ports, 0x04, 24, 8);
+
+/* reg_sldr_system_port
+ * System port.
+ * Access: RW
+ */
+MLXSW_ITEM32_INDEXED(reg, sldr, system_port, 0x08, 0, 16, 4, 0, false);
+
+static inline void mlxsw_reg_sldr_lag_add_port_pack(char *payload, u8 lag_id,
+ u8 local_port)
+{
+ MLXSW_REG_ZERO(sldr, payload);
+ mlxsw_reg_sldr_op_set(payload, MLXSW_REG_SLDR_OP_LAG_ADD_PORT_LIST);
+ mlxsw_reg_sldr_lag_id_set(payload, lag_id);
+ mlxsw_reg_sldr_num_ports_set(payload, 1);
+ mlxsw_reg_sldr_system_port_set(payload, 0, local_port);
+}
+
+static inline void mlxsw_reg_sldr_lag_remove_port_pack(char *payload, u8 lag_id,
+ u8 local_port)
+{
+ MLXSW_REG_ZERO(sldr, payload);
+ mlxsw_reg_sldr_op_set(payload, MLXSW_REG_SLDR_OP_LAG_REMOVE_PORT_LIST);
+ mlxsw_reg_sldr_lag_id_set(payload, lag_id);
+ mlxsw_reg_sldr_num_ports_set(payload, 1);
+ mlxsw_reg_sldr_system_port_set(payload, 0, local_port);
+}
+
+/* SLCR - Switch LAG Configuration 2 Register
+ * -------------------------------------------
+ * The Switch LAG Configuration register is used for configuring the
+ * LAG properties of the switch.
+ */
+#define MLXSW_REG_SLCR_ID 0x2015
+#define MLXSW_REG_SLCR_LEN 0x10
+
+static const struct mlxsw_reg_info mlxsw_reg_slcr = {
+ .id = MLXSW_REG_SLCR_ID,
+ .len = MLXSW_REG_SLCR_LEN,
+};
+
+enum mlxsw_reg_slcr_pp {
+ /* Global Configuration (for all ports) */
+ MLXSW_REG_SLCR_PP_GLOBAL,
+ /* Per port configuration, based on local_port field */
+ MLXSW_REG_SLCR_PP_PER_PORT,
+};
+
+/* reg_slcr_pp
+ * Per Port Configuration
+ * Note: Reading at Global mode results in reading port 1 configuration.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, slcr, pp, 0x00, 24, 1);
+
+/* reg_slcr_local_port
+ * Local port number
+ * Supported from CPU port
+ * Not supported from router port
+ * Reserved when pp = Global Configuration
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, slcr, local_port, 0x00, 16, 8);
+
+enum mlxsw_reg_slcr_type {
+ MLXSW_REG_SLCR_TYPE_CRC, /* default */
+ MLXSW_REG_SLCR_TYPE_XOR,
+ MLXSW_REG_SLCR_TYPE_RANDOM,
+};
+
+/* reg_slcr_type
+ * Hash type
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, slcr, type, 0x00, 0, 4);
+
+/* Ingress port */
+#define MLXSW_REG_SLCR_LAG_HASH_IN_PORT BIT(0)
+/* SMAC - for IPv4 and IPv6 packets */
+#define MLXSW_REG_SLCR_LAG_HASH_SMAC_IP BIT(1)
+/* SMAC - for non-IP packets */
+#define MLXSW_REG_SLCR_LAG_HASH_SMAC_NONIP BIT(2)
+#define MLXSW_REG_SLCR_LAG_HASH_SMAC \
+ (MLXSW_REG_SLCR_LAG_HASH_SMAC_IP | \
+ MLXSW_REG_SLCR_LAG_HASH_SMAC_NONIP)
+/* DMAC - for IPv4 and IPv6 packets */
+#define MLXSW_REG_SLCR_LAG_HASH_DMAC_IP BIT(3)
+/* DMAC - for non-IP packets */
+#define MLXSW_REG_SLCR_LAG_HASH_DMAC_NONIP BIT(4)
+#define MLXSW_REG_SLCR_LAG_HASH_DMAC \
+ (MLXSW_REG_SLCR_LAG_HASH_DMAC_IP | \
+ MLXSW_REG_SLCR_LAG_HASH_DMAC_NONIP)
+/* Ethertype - for IPv4 and IPv6 packets */
+#define MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE_IP BIT(5)
+/* Ethertype - for non-IP packets */
+#define MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE_NONIP BIT(6)
+#define MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE \
+ (MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE_IP | \
+ MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE_NONIP)
+/* VLAN ID - for IPv4 and IPv6 packets */
+#define MLXSW_REG_SLCR_LAG_HASH_VLANID_IP BIT(7)
+/* VLAN ID - for non-IP packets */
+#define MLXSW_REG_SLCR_LAG_HASH_VLANID_NONIP BIT(8)
+#define MLXSW_REG_SLCR_LAG_HASH_VLANID \
+ (MLXSW_REG_SLCR_LAG_HASH_VLANID_IP | \
+ MLXSW_REG_SLCR_LAG_HASH_VLANID_NONIP)
+/* Source IP address (can be IPv4 or IPv6) */
+#define MLXSW_REG_SLCR_LAG_HASH_SIP BIT(9)
+/* Destination IP address (can be IPv4 or IPv6) */
+#define MLXSW_REG_SLCR_LAG_HASH_DIP BIT(10)
+/* TCP/UDP source port */
+#define MLXSW_REG_SLCR_LAG_HASH_SPORT BIT(11)
+/* TCP/UDP destination port*/
+#define MLXSW_REG_SLCR_LAG_HASH_DPORT BIT(12)
+/* IPv4 Protocol/IPv6 Next Header */
+#define MLXSW_REG_SLCR_LAG_HASH_IPPROTO BIT(13)
+/* IPv6 Flow label */
+#define MLXSW_REG_SLCR_LAG_HASH_FLOWLABEL BIT(14)
+/* SID - FCoE source ID */
+#define MLXSW_REG_SLCR_LAG_HASH_FCOE_SID BIT(15)
+/* DID - FCoE destination ID */
+#define MLXSW_REG_SLCR_LAG_HASH_FCOE_DID BIT(16)
+/* OXID - FCoE originator exchange ID */
+#define MLXSW_REG_SLCR_LAG_HASH_FCOE_OXID BIT(17)
+/* Destination QP number - for RoCE packets */
+#define MLXSW_REG_SLCR_LAG_HASH_ROCE_DQP BIT(19)
+
+/* reg_slcr_lag_hash
+ * LAG hashing configuration. This is a bitmask, in which each set
+ * bit includes the corresponding item in the LAG hash calculation.
+ * The default lag_hash contains SMAC, DMAC, VLANID and
+ * Ethertype (for all packet types).
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, slcr, lag_hash, 0x04, 0, 20);
+
+static inline void mlxsw_reg_slcr_pack(char *payload, u16 lag_hash)
+{
+ MLXSW_REG_ZERO(slcr, payload);
+ mlxsw_reg_slcr_pp_set(payload, MLXSW_REG_SLCR_PP_GLOBAL);
+ mlxsw_reg_slcr_type_set(payload, MLXSW_REG_SLCR_TYPE_XOR);
+ mlxsw_reg_slcr_lag_hash_set(payload, lag_hash);
+}
+
+/* SLCOR - Switch LAG Collector Register
+ * -------------------------------------
+ * The Switch LAG Collector register controls the Local Port membership
+ * in a LAG and enablement of the collector.
+ */
+#define MLXSW_REG_SLCOR_ID 0x2016
+#define MLXSW_REG_SLCOR_LEN 0x10
+
+static const struct mlxsw_reg_info mlxsw_reg_slcor = {
+ .id = MLXSW_REG_SLCOR_ID,
+ .len = MLXSW_REG_SLCOR_LEN,
+};
+
+enum mlxsw_reg_slcor_col {
+ /* Port is added with collector disabled */
+ MLXSW_REG_SLCOR_COL_LAG_ADD_PORT,
+ MLXSW_REG_SLCOR_COL_LAG_COLLECTOR_ENABLED,
+ MLXSW_REG_SLCOR_COL_LAG_COLLECTOR_DISABLED,
+ MLXSW_REG_SLCOR_COL_LAG_REMOVE_PORT,
+};
+
+/* reg_slcor_col
+ * Collector configuration
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, slcor, col, 0x00, 30, 2);
+
+/* reg_slcor_local_port
+ * Local port number
+ * Not supported for CPU port
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, slcor, local_port, 0x00, 16, 8);
+
+/* reg_slcor_lag_id
+ * LAG Identifier. Index into the LAG descriptor table.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, slcor, lag_id, 0x00, 0, 10);
+
+/* reg_slcor_port_index
+ * Port index in the LAG list. Only valid on Add Port to LAG col.
+ * Valid range is from 0 to cap_max_lag_members-1
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, slcor, port_index, 0x04, 0, 10);
+
+static inline void mlxsw_reg_slcor_pack(char *payload,
+ u8 local_port, u16 lag_id,
+ enum mlxsw_reg_slcor_col col)
+{
+ MLXSW_REG_ZERO(slcor, payload);
+ mlxsw_reg_slcor_col_set(payload, col);
+ mlxsw_reg_slcor_local_port_set(payload, local_port);
+ mlxsw_reg_slcor_lag_id_set(payload, lag_id);
+}
+
+static inline void mlxsw_reg_slcor_port_add_pack(char *payload,
+ u8 local_port, u16 lag_id,
+ u8 port_index)
+{
+ mlxsw_reg_slcor_pack(payload, local_port, lag_id,
+ MLXSW_REG_SLCOR_COL_LAG_ADD_PORT);
+ mlxsw_reg_slcor_port_index_set(payload, port_index);
+}
+
+static inline void mlxsw_reg_slcor_port_remove_pack(char *payload,
+ u8 local_port, u16 lag_id)
+{
+ mlxsw_reg_slcor_pack(payload, local_port, lag_id,
+ MLXSW_REG_SLCOR_COL_LAG_REMOVE_PORT);
+}
+
+static inline void mlxsw_reg_slcor_col_enable_pack(char *payload,
+ u8 local_port, u16 lag_id)
+{
+ mlxsw_reg_slcor_pack(payload, local_port, lag_id,
+ MLXSW_REG_SLCOR_COL_LAG_COLLECTOR_ENABLED);
+}
+
+static inline void mlxsw_reg_slcor_col_disable_pack(char *payload,
+ u8 local_port, u16 lag_id)
+{
+ mlxsw_reg_slcor_pack(payload, local_port, lag_id,
+ MLXSW_REG_SLCOR_COL_LAG_COLLECTOR_ENABLED);
+}
+
/* SPMLR - Switch Port MAC Learning Register
* -----------------------------------------
* Controls the Switch MAC learning policy per port.
@@ -2087,6 +2553,284 @@ static inline void mlxsw_reg_hpkt_pack(char *payload, u8 action, u16 trap_id)
mlxsw_reg_hpkt_ctrl_set(payload, MLXSW_REG_HPKT_CTRL_PACKET_DEFAULT);
}
+/* MFCR - Management Fan Control Register
+ * --------------------------------------
+ * This register controls the settings of the Fan Speed PWM mechanism.
+ */
+#define MLXSW_REG_MFCR_ID 0x9001
+#define MLXSW_REG_MFCR_LEN 0x08
+
+static const struct mlxsw_reg_info mlxsw_reg_mfcr = {
+ .id = MLXSW_REG_MFCR_ID,
+ .len = MLXSW_REG_MFCR_LEN,
+};
+
+enum mlxsw_reg_mfcr_pwm_frequency {
+ MLXSW_REG_MFCR_PWM_FEQ_11HZ = 0x00,
+ MLXSW_REG_MFCR_PWM_FEQ_14_7HZ = 0x01,
+ MLXSW_REG_MFCR_PWM_FEQ_22_1HZ = 0x02,
+ MLXSW_REG_MFCR_PWM_FEQ_1_4KHZ = 0x40,
+ MLXSW_REG_MFCR_PWM_FEQ_5KHZ = 0x41,
+ MLXSW_REG_MFCR_PWM_FEQ_20KHZ = 0x42,
+ MLXSW_REG_MFCR_PWM_FEQ_22_5KHZ = 0x43,
+ MLXSW_REG_MFCR_PWM_FEQ_25KHZ = 0x44,
+};
+
+/* reg_mfcr_pwm_frequency
+ * Controls the frequency of the PWM signal.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mfcr, pwm_frequency, 0x00, 0, 6);
+
+#define MLXSW_MFCR_TACHOS_MAX 10
+
+/* reg_mfcr_tacho_active
+ * Indicates which of the tachometer is active (bit per tachometer).
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, mfcr, tacho_active, 0x04, 16, MLXSW_MFCR_TACHOS_MAX);
+
+#define MLXSW_MFCR_PWMS_MAX 5
+
+/* reg_mfcr_pwm_active
+ * Indicates which of the PWM control is active (bit per PWM).
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, mfcr, pwm_active, 0x04, 0, MLXSW_MFCR_PWMS_MAX);
+
+static inline void
+mlxsw_reg_mfcr_pack(char *payload,
+ enum mlxsw_reg_mfcr_pwm_frequency pwm_frequency)
+{
+ MLXSW_REG_ZERO(mfcr, payload);
+ mlxsw_reg_mfcr_pwm_frequency_set(payload, pwm_frequency);
+}
+
+static inline void
+mlxsw_reg_mfcr_unpack(char *payload,
+ enum mlxsw_reg_mfcr_pwm_frequency *p_pwm_frequency,
+ u16 *p_tacho_active, u8 *p_pwm_active)
+{
+ *p_pwm_frequency = mlxsw_reg_mfcr_pwm_frequency_get(payload);
+ *p_tacho_active = mlxsw_reg_mfcr_tacho_active_get(payload);
+ *p_pwm_active = mlxsw_reg_mfcr_pwm_active_get(payload);
+}
+
+/* MFSC - Management Fan Speed Control Register
+ * --------------------------------------------
+ * This register controls the settings of the Fan Speed PWM mechanism.
+ */
+#define MLXSW_REG_MFSC_ID 0x9002
+#define MLXSW_REG_MFSC_LEN 0x08
+
+static const struct mlxsw_reg_info mlxsw_reg_mfsc = {
+ .id = MLXSW_REG_MFSC_ID,
+ .len = MLXSW_REG_MFSC_LEN,
+};
+
+/* reg_mfsc_pwm
+ * Fan pwm to control / monitor.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, mfsc, pwm, 0x00, 24, 3);
+
+/* reg_mfsc_pwm_duty_cycle
+ * Controls the duty cycle of the PWM. Value range from 0..255 to
+ * represent duty cycle of 0%...100%.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mfsc, pwm_duty_cycle, 0x04, 0, 8);
+
+static inline void mlxsw_reg_mfsc_pack(char *payload, u8 pwm,
+ u8 pwm_duty_cycle)
+{
+ MLXSW_REG_ZERO(mfsc, payload);
+ mlxsw_reg_mfsc_pwm_set(payload, pwm);
+ mlxsw_reg_mfsc_pwm_duty_cycle_set(payload, pwm_duty_cycle);
+}
+
+/* MFSM - Management Fan Speed Measurement
+ * ---------------------------------------
+ * This register controls the settings of the Tacho measurements and
+ * enables reading the Tachometer measurements.
+ */
+#define MLXSW_REG_MFSM_ID 0x9003
+#define MLXSW_REG_MFSM_LEN 0x08
+
+static const struct mlxsw_reg_info mlxsw_reg_mfsm = {
+ .id = MLXSW_REG_MFSM_ID,
+ .len = MLXSW_REG_MFSM_LEN,
+};
+
+/* reg_mfsm_tacho
+ * Fan tachometer index.
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, mfsm, tacho, 0x00, 24, 4);
+
+/* reg_mfsm_rpm
+ * Fan speed (round per minute).
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, mfsm, rpm, 0x04, 0, 16);
+
+static inline void mlxsw_reg_mfsm_pack(char *payload, u8 tacho)
+{
+ MLXSW_REG_ZERO(mfsm, payload);
+ mlxsw_reg_mfsm_tacho_set(payload, tacho);
+}
+
+/* MTCAP - Management Temperature Capabilities
+ * -------------------------------------------
+ * This register exposes the capabilities of the device and
+ * system temperature sensing.
+ */
+#define MLXSW_REG_MTCAP_ID 0x9009
+#define MLXSW_REG_MTCAP_LEN 0x08
+
+static const struct mlxsw_reg_info mlxsw_reg_mtcap = {
+ .id = MLXSW_REG_MTCAP_ID,
+ .len = MLXSW_REG_MTCAP_LEN,
+};
+
+/* reg_mtcap_sensor_count
+ * Number of sensors supported by the device.
+ * This includes the QSFP module sensors (if exists in the QSFP module).
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, mtcap, sensor_count, 0x00, 0, 7);
+
+/* MTMP - Management Temperature
+ * -----------------------------
+ * This register controls the settings of the temperature measurements
+ * and enables reading the temperature measurements. Note that temperature
+ * is in 0.125 degrees Celsius.
+ */
+#define MLXSW_REG_MTMP_ID 0x900A
+#define MLXSW_REG_MTMP_LEN 0x20
+
+static const struct mlxsw_reg_info mlxsw_reg_mtmp = {
+ .id = MLXSW_REG_MTMP_ID,
+ .len = MLXSW_REG_MTMP_LEN,
+};
+
+/* reg_mtmp_sensor_index
+ * Sensors index to access.
+ * 64-127 of sensor_index are mapped to the SFP+/QSFP modules sequentially
+ * (module 0 is mapped to sensor_index 64).
+ * Access: Index
+ */
+MLXSW_ITEM32(reg, mtmp, sensor_index, 0x00, 0, 7);
+
+/* Convert to milli degrees Celsius */
+#define MLXSW_REG_MTMP_TEMP_TO_MC(val) (val * 125)
+
+/* reg_mtmp_temperature
+ * Temperature reading from the sensor. Reading is in 0.125 Celsius
+ * degrees units.
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, mtmp, temperature, 0x04, 0, 16);
+
+/* reg_mtmp_mte
+ * Max Temperature Enable - enables measuring the max temperature on a sensor.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mtmp, mte, 0x08, 31, 1);
+
+/* reg_mtmp_mtr
+ * Max Temperature Reset - clears the value of the max temperature register.
+ * Access: WO
+ */
+MLXSW_ITEM32(reg, mtmp, mtr, 0x08, 30, 1);
+
+/* reg_mtmp_max_temperature
+ * The highest measured temperature from the sensor.
+ * When the bit mte is cleared, the field max_temperature is reserved.
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, mtmp, max_temperature, 0x08, 0, 16);
+
+#define MLXSW_REG_MTMP_SENSOR_NAME_SIZE 8
+
+/* reg_mtmp_sensor_name
+ * Sensor Name
+ * Access: RO
+ */
+MLXSW_ITEM_BUF(reg, mtmp, sensor_name, 0x18, MLXSW_REG_MTMP_SENSOR_NAME_SIZE);
+
+static inline void mlxsw_reg_mtmp_pack(char *payload, u8 sensor_index,
+ bool max_temp_enable,
+ bool max_temp_reset)
+{
+ MLXSW_REG_ZERO(mtmp, payload);
+ mlxsw_reg_mtmp_sensor_index_set(payload, sensor_index);
+ mlxsw_reg_mtmp_mte_set(payload, max_temp_enable);
+ mlxsw_reg_mtmp_mtr_set(payload, max_temp_reset);
+}
+
+static inline void mlxsw_reg_mtmp_unpack(char *payload, unsigned int *p_temp,
+ unsigned int *p_max_temp,
+ char *sensor_name)
+{
+ u16 temp;
+
+ if (p_temp) {
+ temp = mlxsw_reg_mtmp_temperature_get(payload);
+ *p_temp = MLXSW_REG_MTMP_TEMP_TO_MC(temp);
+ }
+ if (p_max_temp) {
+ temp = mlxsw_reg_mtmp_max_temperature_get(payload);
+ *p_max_temp = MLXSW_REG_MTMP_TEMP_TO_MC(temp);
+ }
+ if (sensor_name)
+ mlxsw_reg_mtmp_sensor_name_memcpy_from(payload, sensor_name);
+}
+
+/* MLCR - Management LED Control Register
+ * --------------------------------------
+ * Controls the system LEDs.
+ */
+#define MLXSW_REG_MLCR_ID 0x902B
+#define MLXSW_REG_MLCR_LEN 0x0C
+
+static const struct mlxsw_reg_info mlxsw_reg_mlcr = {
+ .id = MLXSW_REG_MLCR_ID,
+ .len = MLXSW_REG_MLCR_LEN,
+};
+
+/* reg_mlcr_local_port
+ * Local port number.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mlcr, local_port, 0x00, 16, 8);
+
+#define MLXSW_REG_MLCR_DURATION_MAX 0xFFFF
+
+/* reg_mlcr_beacon_duration
+ * Duration of the beacon to be active, in seconds.
+ * 0x0 - Will turn off the beacon.
+ * 0xFFFF - Will turn on the beacon until explicitly turned off.
+ * Access: RW
+ */
+MLXSW_ITEM32(reg, mlcr, beacon_duration, 0x04, 0, 16);
+
+/* reg_mlcr_beacon_remain
+ * Remaining duration of the beacon, in seconds.
+ * 0xFFFF indicates an infinite amount of time.
+ * Access: RO
+ */
+MLXSW_ITEM32(reg, mlcr, beacon_remain, 0x08, 0, 16);
+
+static inline void mlxsw_reg_mlcr_pack(char *payload, u8 local_port,
+ bool active)
+{
+ MLXSW_REG_ZERO(mlcr, payload);
+ mlxsw_reg_mlcr_local_port_set(payload, local_port);
+ mlxsw_reg_mlcr_beacon_duration_set(payload, active ?
+ MLXSW_REG_MLCR_DURATION_MAX : 0);
+}
+
/* SBPR - Shared Buffer Pools Register
* -----------------------------------
* The SBPR configures and retrieves the shared buffer pools and configuration.
@@ -2357,6 +3101,8 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id)
return "SGCR";
case MLXSW_REG_SPAD_ID:
return "SPAD";
+ case MLXSW_REG_SMID_ID:
+ return "SMID";
case MLXSW_REG_SSPR_ID:
return "SSPR";
case MLXSW_REG_SFDAT_ID:
@@ -2375,6 +3121,12 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id)
return "SFGC";
case MLXSW_REG_SFTR_ID:
return "SFTR";
+ case MLXSW_REG_SLDR_ID:
+ return "SLDR";
+ case MLXSW_REG_SLCR_ID:
+ return "SLCR";
+ case MLXSW_REG_SLCOR_ID:
+ return "SLCOR";
case MLXSW_REG_SPMLR_ID:
return "SPMLR";
case MLXSW_REG_SVFA_ID:
@@ -2405,6 +3157,18 @@ static inline const char *mlxsw_reg_id_str(u16 reg_id)
return "HTGT";
case MLXSW_REG_HPKT_ID:
return "HPKT";
+ case MLXSW_REG_MFCR_ID:
+ return "MFCR";
+ case MLXSW_REG_MFSC_ID:
+ return "MFSC";
+ case MLXSW_REG_MFSM_ID:
+ return "MFSM";
+ case MLXSW_REG_MTCAP_ID:
+ return "MTCAP";
+ case MLXSW_REG_MTMP_ID:
+ return "MTMP";
+ case MLXSW_REG_MLCR_ID:
+ return "MLCR";
case MLXSW_REG_SBPR_ID:
return "SBPR";
case MLXSW_REG_SBCM_ID:
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index 3be4a2355ead..ce6845d534a8 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -48,6 +48,7 @@
#include <linux/workqueue.h>
#include <linux/jiffies.h>
#include <linux/bitops.h>
+#include <linux/list.h>
#include <net/switchdev.h>
#include <generated/utsrelease.h>
@@ -186,33 +187,6 @@ static int mlxsw_sp_port_oper_status_get(struct mlxsw_sp_port *mlxsw_sp_port,
return 0;
}
-static int mlxsw_sp_vfid_create(struct mlxsw_sp *mlxsw_sp, u16 vfid)
-{
- char sfmr_pl[MLXSW_REG_SFMR_LEN];
- int err;
-
- mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_CREATE_FID,
- MLXSW_SP_VFID_BASE + vfid, 0);
- err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl);
-
- if (err)
- return err;
-
- set_bit(vfid, mlxsw_sp->active_vfids);
- return 0;
-}
-
-static void mlxsw_sp_vfid_destroy(struct mlxsw_sp *mlxsw_sp, u16 vfid)
-{
- char sfmr_pl[MLXSW_REG_SFMR_LEN];
-
- clear_bit(vfid, mlxsw_sp->active_vfids);
-
- mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_DESTROY_FID,
- MLXSW_SP_VFID_BASE + vfid, 0);
- mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl);
-}
-
static int mlxsw_sp_port_dev_addr_set(struct mlxsw_sp_port *mlxsw_sp_port,
unsigned char *addr)
{
@@ -417,6 +391,10 @@ static netdev_tx_t mlxsw_sp_port_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
}
+static void mlxsw_sp_set_rx_mode(struct net_device *dev)
+{
+}
+
static int mlxsw_sp_port_set_mac_address(struct net_device *dev, void *p)
{
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
@@ -545,12 +523,132 @@ static int mlxsw_sp_port_vlan_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port)
return 0;
}
+static struct mlxsw_sp_vfid *
+mlxsw_sp_vfid_find(const struct mlxsw_sp *mlxsw_sp, u16 vid)
+{
+ struct mlxsw_sp_vfid *vfid;
+
+ list_for_each_entry(vfid, &mlxsw_sp->port_vfids.list, list) {
+ if (vfid->vid == vid)
+ return vfid;
+ }
+
+ return NULL;
+}
+
+static u16 mlxsw_sp_avail_vfid_get(const struct mlxsw_sp *mlxsw_sp)
+{
+ return find_first_zero_bit(mlxsw_sp->port_vfids.mapped,
+ MLXSW_SP_VFID_PORT_MAX);
+}
+
+static int __mlxsw_sp_vfid_create(struct mlxsw_sp *mlxsw_sp, u16 vfid)
+{
+ u16 fid = mlxsw_sp_vfid_to_fid(vfid);
+ char sfmr_pl[MLXSW_REG_SFMR_LEN];
+
+ mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_CREATE_FID, fid, 0);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl);
+}
+
+static void __mlxsw_sp_vfid_destroy(struct mlxsw_sp *mlxsw_sp, u16 vfid)
+{
+ u16 fid = mlxsw_sp_vfid_to_fid(vfid);
+ char sfmr_pl[MLXSW_REG_SFMR_LEN];
+
+ mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_DESTROY_FID, fid, 0);
+ mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl);
+}
+
+static struct mlxsw_sp_vfid *mlxsw_sp_vfid_create(struct mlxsw_sp *mlxsw_sp,
+ u16 vid)
+{
+ struct device *dev = mlxsw_sp->bus_info->dev;
+ struct mlxsw_sp_vfid *vfid;
+ u16 n_vfid;
+ int err;
+
+ n_vfid = mlxsw_sp_avail_vfid_get(mlxsw_sp);
+ if (n_vfid == MLXSW_SP_VFID_PORT_MAX) {
+ dev_err(dev, "No available vFIDs\n");
+ return ERR_PTR(-ERANGE);
+ }
+
+ err = __mlxsw_sp_vfid_create(mlxsw_sp, n_vfid);
+ if (err) {
+ dev_err(dev, "Failed to create vFID=%d\n", n_vfid);
+ return ERR_PTR(err);
+ }
+
+ vfid = kzalloc(sizeof(*vfid), GFP_KERNEL);
+ if (!vfid)
+ goto err_allocate_vfid;
+
+ vfid->vfid = n_vfid;
+ vfid->vid = vid;
+
+ list_add(&vfid->list, &mlxsw_sp->port_vfids.list);
+ set_bit(n_vfid, mlxsw_sp->port_vfids.mapped);
+
+ return vfid;
+
+err_allocate_vfid:
+ __mlxsw_sp_vfid_destroy(mlxsw_sp, n_vfid);
+ return ERR_PTR(-ENOMEM);
+}
+
+static void mlxsw_sp_vfid_destroy(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_vfid *vfid)
+{
+ clear_bit(vfid->vfid, mlxsw_sp->port_vfids.mapped);
+ list_del(&vfid->list);
+
+ __mlxsw_sp_vfid_destroy(mlxsw_sp, vfid->vfid);
+
+ kfree(vfid);
+}
+
+static struct mlxsw_sp_port *
+mlxsw_sp_port_vport_create(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct mlxsw_sp_vfid *vfid)
+{
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+
+ mlxsw_sp_vport = kzalloc(sizeof(*mlxsw_sp_vport), GFP_KERNEL);
+ if (!mlxsw_sp_vport)
+ return NULL;
+
+ /* dev will be set correctly after the VLAN device is linked
+ * with the real device. In case of bridge SELF invocation, dev
+ * will remain as is.
+ */
+ mlxsw_sp_vport->dev = mlxsw_sp_port->dev;
+ mlxsw_sp_vport->mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ mlxsw_sp_vport->local_port = mlxsw_sp_port->local_port;
+ mlxsw_sp_vport->stp_state = BR_STATE_FORWARDING;
+ mlxsw_sp_vport->lagged = mlxsw_sp_port->lagged;
+ mlxsw_sp_vport->lag_id = mlxsw_sp_port->lag_id;
+ mlxsw_sp_vport->vport.vfid = vfid;
+ mlxsw_sp_vport->vport.vid = vfid->vid;
+
+ list_add(&mlxsw_sp_vport->vport.list, &mlxsw_sp_port->vports_list);
+
+ return mlxsw_sp_vport;
+}
+
+static void mlxsw_sp_port_vport_destroy(struct mlxsw_sp_port *mlxsw_sp_vport)
+{
+ list_del(&mlxsw_sp_vport->vport.list);
+ kfree(mlxsw_sp_vport);
+}
+
int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto,
u16 vid)
{
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
- char *sftr_pl;
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+ struct mlxsw_sp_vfid *vfid;
int err;
/* VLAN 0 is added to HW filter when device goes up, but it is
@@ -559,100 +657,105 @@ int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto,
if (!vid)
return 0;
- if (test_bit(vid, mlxsw_sp_port->active_vfids)) {
+ if (mlxsw_sp_port_vport_find(mlxsw_sp_port, vid)) {
netdev_warn(dev, "VID=%d already configured\n", vid);
return 0;
}
- if (!test_bit(vid, mlxsw_sp->active_vfids)) {
- err = mlxsw_sp_vfid_create(mlxsw_sp, vid);
- if (err) {
- netdev_err(dev, "Failed to create vFID=%d\n",
- MLXSW_SP_VFID_BASE + vid);
- return err;
+ vfid = mlxsw_sp_vfid_find(mlxsw_sp, vid);
+ if (!vfid) {
+ vfid = mlxsw_sp_vfid_create(mlxsw_sp, vid);
+ if (IS_ERR(vfid)) {
+ netdev_err(dev, "Failed to create vFID for VID=%d\n",
+ vid);
+ return PTR_ERR(vfid);
}
+ }
- sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL);
- if (!sftr_pl) {
- err = -ENOMEM;
- goto err_flood_table_alloc;
- }
- mlxsw_reg_sftr_pack(sftr_pl, 0, vid,
- MLXSW_REG_SFGC_TABLE_TYPE_FID, 0,
- MLXSW_PORT_CPU_PORT, true);
- err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
- kfree(sftr_pl);
+ mlxsw_sp_vport = mlxsw_sp_port_vport_create(mlxsw_sp_port, vfid);
+ if (!mlxsw_sp_vport) {
+ netdev_err(dev, "Failed to create vPort for VID=%d\n", vid);
+ err = -ENOMEM;
+ goto err_port_vport_create;
+ }
+
+ if (!vfid->nr_vports) {
+ err = mlxsw_sp_vport_flood_set(mlxsw_sp_vport, vfid->vfid,
+ true, false);
if (err) {
- netdev_err(dev, "Failed to configure flood table\n");
- goto err_flood_table_config;
+ netdev_err(dev, "Failed to setup flooding for vFID=%d\n",
+ vfid->vfid);
+ goto err_vport_flood_set;
}
}
- /* In case we fail in the following steps, we intentionally do not
- * destroy the associated vFID.
- */
-
/* When adding the first VLAN interface on a bridged port we need to
* transition all the active 802.1Q bridge VLANs to use explicit
* {Port, VID} to FID mappings and set the port's mode to Virtual mode.
*/
- if (!mlxsw_sp_port->nr_vfids) {
+ if (list_is_singular(&mlxsw_sp_port->vports_list)) {
err = mlxsw_sp_port_vp_mode_trans(mlxsw_sp_port);
if (err) {
netdev_err(dev, "Failed to set to Virtual mode\n");
- return err;
+ goto err_port_vp_mode_trans;
}
}
- err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port,
+ err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport,
MLXSW_REG_SVFA_MT_PORT_VID_TO_FID,
- true, MLXSW_SP_VFID_BASE + vid, vid);
+ true,
+ mlxsw_sp_vfid_to_fid(vfid->vfid),
+ vid);
if (err) {
netdev_err(dev, "Failed to map {Port, VID=%d} to vFID=%d\n",
- vid, MLXSW_SP_VFID_BASE + vid);
+ vid, vfid->vfid);
goto err_port_vid_to_fid_set;
}
- err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
+ err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, false);
if (err) {
netdev_err(dev, "Failed to disable learning for VID=%d\n", vid);
goto err_port_vid_learning_set;
}
- err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true, false);
+ err = mlxsw_sp_port_vlan_set(mlxsw_sp_vport, vid, vid, true, false);
if (err) {
netdev_err(dev, "Failed to set VLAN membership for VID=%d\n",
vid);
goto err_port_add_vid;
}
- err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port, vid,
+ err = mlxsw_sp_port_stp_state_set(mlxsw_sp_vport, vid,
MLXSW_REG_SPMS_STATE_FORWARDING);
if (err) {
netdev_err(dev, "Failed to set STP state for VID=%d\n", vid);
goto err_port_stp_state_set;
}
- mlxsw_sp_port->nr_vfids++;
- set_bit(vid, mlxsw_sp_port->active_vfids);
+ vfid->nr_vports++;
return 0;
-err_flood_table_config:
-err_flood_table_alloc:
- mlxsw_sp_vfid_destroy(mlxsw_sp, vid);
- return err;
-
err_port_stp_state_set:
- mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
+ mlxsw_sp_port_vlan_set(mlxsw_sp_vport, vid, vid, false, false);
err_port_add_vid:
- mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
+ mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, true);
err_port_vid_learning_set:
- mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port,
+ mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport,
MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, false,
- MLXSW_SP_VFID_BASE + vid, vid);
+ mlxsw_sp_vfid_to_fid(vfid->vfid), vid);
err_port_vid_to_fid_set:
- mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port);
+ if (list_is_singular(&mlxsw_sp_port->vports_list))
+ mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port);
+err_port_vp_mode_trans:
+ if (!vfid->nr_vports)
+ mlxsw_sp_vport_flood_set(mlxsw_sp_vport, vfid->vfid, false,
+ false);
+err_vport_flood_set:
+ mlxsw_sp_port_vport_destroy(mlxsw_sp_vport);
+err_port_vport_create:
+ if (!vfid->nr_vports)
+ mlxsw_sp_vfid_destroy(mlxsw_sp, vfid);
return err;
}
@@ -660,6 +763,8 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev,
__be16 __always_unused proto, u16 vid)
{
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+ struct mlxsw_sp_vfid *vfid;
int err;
/* VLAN 0 is removed from HW filter when device goes down, but
@@ -668,38 +773,42 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev,
if (!vid)
return 0;
- if (!test_bit(vid, mlxsw_sp_port->active_vfids)) {
+ mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid);
+ if (!mlxsw_sp_vport) {
netdev_warn(dev, "VID=%d does not exist\n", vid);
return 0;
}
- err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port, vid,
+ vfid = mlxsw_sp_vport->vport.vfid;
+
+ err = mlxsw_sp_port_stp_state_set(mlxsw_sp_vport, vid,
MLXSW_REG_SPMS_STATE_DISCARDING);
if (err) {
netdev_err(dev, "Failed to set STP state for VID=%d\n", vid);
return err;
}
- err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
+ err = mlxsw_sp_port_vlan_set(mlxsw_sp_vport, vid, vid, false, false);
if (err) {
netdev_err(dev, "Failed to set VLAN membership for VID=%d\n",
vid);
return err;
}
- err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, true);
+ err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, true);
if (err) {
netdev_err(dev, "Failed to enable learning for VID=%d\n", vid);
return err;
}
- err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port,
+ err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport,
MLXSW_REG_SVFA_MT_PORT_VID_TO_FID,
- false, MLXSW_SP_VFID_BASE + vid,
+ false,
+ mlxsw_sp_vfid_to_fid(vfid->vfid),
vid);
if (err) {
netdev_err(dev, "Failed to invalidate {Port, VID=%d} to vFID=%d mapping\n",
- vid, MLXSW_SP_VFID_BASE + vid);
+ vid, vfid->vfid);
return err;
}
@@ -707,7 +816,7 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev,
* transition all active 802.1Q bridge VLANs to use VID to FID
* mappings and set port's mode to VLAN mode.
*/
- if (mlxsw_sp_port->nr_vfids == 1) {
+ if (list_is_singular(&mlxsw_sp_port->vports_list)) {
err = mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port);
if (err) {
netdev_err(dev, "Failed to set to VLAN mode\n");
@@ -715,8 +824,12 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev,
}
}
- mlxsw_sp_port->nr_vfids--;
- clear_bit(vid, mlxsw_sp_port->active_vfids);
+ vfid->nr_vports--;
+ mlxsw_sp_port_vport_destroy(mlxsw_sp_vport);
+
+ /* Destroy the vFID if no vPorts are assigned to it anymore. */
+ if (!vfid->nr_vports)
+ mlxsw_sp_vfid_destroy(mlxsw_sp_port->mlxsw_sp, vfid);
return 0;
}
@@ -725,6 +838,7 @@ static const struct net_device_ops mlxsw_sp_port_netdev_ops = {
.ndo_open = mlxsw_sp_port_open,
.ndo_stop = mlxsw_sp_port_stop,
.ndo_start_xmit = mlxsw_sp_port_xmit,
+ .ndo_set_rx_mode = mlxsw_sp_set_rx_mode,
.ndo_set_mac_address = mlxsw_sp_port_set_mac_address,
.ndo_change_mtu = mlxsw_sp_port_change_mtu,
.ndo_get_stats64 = mlxsw_sp_port_get_stats64,
@@ -859,6 +973,29 @@ static void mlxsw_sp_port_get_strings(struct net_device *dev,
}
}
+static int mlxsw_sp_port_set_phys_id(struct net_device *dev,
+ enum ethtool_phys_id_state state)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ char mlcr_pl[MLXSW_REG_MLCR_LEN];
+ bool active;
+
+ switch (state) {
+ case ETHTOOL_ID_ACTIVE:
+ active = true;
+ break;
+ case ETHTOOL_ID_INACTIVE:
+ active = false;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ mlxsw_reg_mlcr_pack(mlcr_pl, mlxsw_sp_port->local_port, active);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mlcr), mlcr_pl);
+}
+
static void mlxsw_sp_port_get_stats(struct net_device *dev,
struct ethtool_stats *stats, u64 *data)
{
@@ -1205,6 +1342,7 @@ static const struct ethtool_ops mlxsw_sp_port_ethtool_ops = {
.get_drvinfo = mlxsw_sp_port_get_drvinfo,
.get_link = ethtool_op_get_link,
.get_strings = mlxsw_sp_port_get_strings,
+ .set_phys_id = mlxsw_sp_port_set_phys_id,
.get_ethtool_stats = mlxsw_sp_port_get_stats,
.get_sset_count = mlxsw_sp_port_get_sset_count,
.get_settings = mlxsw_sp_port_get_settings,
@@ -1216,6 +1354,7 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
struct mlxsw_sp_port *mlxsw_sp_port;
struct net_device *dev;
bool usable;
+ size_t bytes;
int err;
dev = alloc_etherdev(sizeof(struct mlxsw_sp_port));
@@ -1225,10 +1364,18 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port)
mlxsw_sp_port->dev = dev;
mlxsw_sp_port->mlxsw_sp = mlxsw_sp;
mlxsw_sp_port->local_port = local_port;
- mlxsw_sp_port->learning = 1;
- mlxsw_sp_port->learning_sync = 1;
- mlxsw_sp_port->uc_flood = 1;
- mlxsw_sp_port->pvid = 1;
+ bytes = DIV_ROUND_UP(VLAN_N_VID, BITS_PER_BYTE);
+ mlxsw_sp_port->active_vlans = kzalloc(bytes, GFP_KERNEL);
+ if (!mlxsw_sp_port->active_vlans) {
+ err = -ENOMEM;
+ goto err_port_active_vlans_alloc;
+ }
+ mlxsw_sp_port->untagged_vlans = kzalloc(bytes, GFP_KERNEL);
+ if (!mlxsw_sp_port->untagged_vlans) {
+ err = -ENOMEM;
+ goto err_port_untagged_vlans_alloc;
+ }
+ INIT_LIST_HEAD(&mlxsw_sp_port->vports_list);
mlxsw_sp_port->pcpu_stats =
netdev_alloc_pcpu_stats(struct mlxsw_sp_port_pcpu_stats);
@@ -1330,16 +1477,29 @@ err_port_module_check:
err_dev_addr_init:
free_percpu(mlxsw_sp_port->pcpu_stats);
err_alloc_stats:
+ kfree(mlxsw_sp_port->untagged_vlans);
+err_port_untagged_vlans_alloc:
+ kfree(mlxsw_sp_port->active_vlans);
+err_port_active_vlans_alloc:
free_netdev(dev);
return err;
}
-static void mlxsw_sp_vfids_fini(struct mlxsw_sp *mlxsw_sp)
+static void mlxsw_sp_port_vports_fini(struct mlxsw_sp_port *mlxsw_sp_port)
{
- u16 vfid;
+ struct net_device *dev = mlxsw_sp_port->dev;
+ struct mlxsw_sp_port *mlxsw_sp_vport, *tmp;
- for_each_set_bit(vfid, mlxsw_sp->active_vfids, VLAN_N_VID)
- mlxsw_sp_vfid_destroy(mlxsw_sp, vfid);
+ list_for_each_entry_safe(mlxsw_sp_vport, tmp,
+ &mlxsw_sp_port->vports_list, vport.list) {
+ u16 vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport);
+
+ /* vPorts created for VLAN devices should already be gone
+ * by now, since we unregistered the port netdev.
+ */
+ WARN_ON(is_vlan_dev(mlxsw_sp_vport->dev));
+ mlxsw_sp_port_kill_vid(dev, 0, vid);
+ }
}
static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port)
@@ -1348,10 +1508,12 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port)
if (!mlxsw_sp_port)
return;
- mlxsw_sp_port_kill_vid(mlxsw_sp_port->dev, 0, 1);
unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */
+ mlxsw_sp_port_vports_fini(mlxsw_sp_port);
mlxsw_sp_port_switchdev_fini(mlxsw_sp_port);
free_percpu(mlxsw_sp_port->pcpu_stats);
+ kfree(mlxsw_sp_port->untagged_vlans);
+ kfree(mlxsw_sp_port->active_vlans);
free_netdev(mlxsw_sp_port->dev);
}
@@ -1633,16 +1795,15 @@ static int __mlxsw_sp_flood_init(struct mlxsw_core *mlxsw_core,
enum mlxsw_sp_flood_table flood_table;
char sfgc_pl[MLXSW_REG_SFGC_LEN];
- if (bridge_type == MLXSW_REG_SFGC_BRIDGE_TYPE_VFID) {
+ if (bridge_type == MLXSW_REG_SFGC_BRIDGE_TYPE_VFID)
table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID;
- flood_table = 0;
- } else {
+ else
table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST;
- if (type == MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST)
- flood_table = MLXSW_SP_FLOOD_TABLE_UC;
- else
- flood_table = MLXSW_SP_FLOOD_TABLE_BM;
- }
+
+ if (type == MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST)
+ flood_table = MLXSW_SP_FLOOD_TABLE_UC;
+ else
+ flood_table = MLXSW_SP_FLOOD_TABLE_BM;
mlxsw_reg_sfgc_pack(sfgc_pl, type, bridge_type, table_type,
flood_table);
@@ -1653,9 +1814,6 @@ static int mlxsw_sp_flood_init(struct mlxsw_sp *mlxsw_sp)
{
int type, err;
- /* For non-offloaded netdevs, flood all traffic types to CPU
- * port.
- */
for (type = 0; type < MLXSW_REG_SFGC_TYPE_MAX; type++) {
if (type == MLXSW_REG_SFGC_TYPE_RESERVED)
continue;
@@ -1664,15 +1822,6 @@ static int mlxsw_sp_flood_init(struct mlxsw_sp *mlxsw_sp)
MLXSW_REG_SFGC_BRIDGE_TYPE_VFID);
if (err)
return err;
- }
-
- /* For bridged ports, use one flooding table for unknown unicast
- * traffic and a second table for unregistered multicast and
- * broadcast.
- */
- for (type = 0; type < MLXSW_REG_SFGC_TYPE_MAX; type++) {
- if (type == MLXSW_REG_SFGC_TYPE_RESERVED)
- continue;
err = __mlxsw_sp_flood_init(mlxsw_sp->core, type,
MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID);
@@ -1683,6 +1832,22 @@ static int mlxsw_sp_flood_init(struct mlxsw_sp *mlxsw_sp)
return 0;
}
+static int mlxsw_sp_lag_init(struct mlxsw_sp *mlxsw_sp)
+{
+ char slcr_pl[MLXSW_REG_SLCR_LEN];
+
+ mlxsw_reg_slcr_pack(slcr_pl, MLXSW_REG_SLCR_LAG_HASH_SMAC |
+ MLXSW_REG_SLCR_LAG_HASH_DMAC |
+ MLXSW_REG_SLCR_LAG_HASH_ETHERTYPE |
+ MLXSW_REG_SLCR_LAG_HASH_VLANID |
+ MLXSW_REG_SLCR_LAG_HASH_SIP |
+ MLXSW_REG_SLCR_LAG_HASH_DIP |
+ MLXSW_REG_SLCR_LAG_HASH_SPORT |
+ MLXSW_REG_SLCR_LAG_HASH_DPORT |
+ MLXSW_REG_SLCR_LAG_HASH_IPPROTO);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcr), slcr_pl);
+}
+
static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core,
const struct mlxsw_bus_info *mlxsw_bus_info)
{
@@ -1691,6 +1856,9 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core,
mlxsw_sp->core = mlxsw_core;
mlxsw_sp->bus_info = mlxsw_bus_info;
+ INIT_LIST_HEAD(&mlxsw_sp->port_vfids.list);
+ INIT_LIST_HEAD(&mlxsw_sp->br_vfids.list);
+ INIT_LIST_HEAD(&mlxsw_sp->br_mids.list);
err = mlxsw_sp_base_mac_get(mlxsw_sp);
if (err) {
@@ -1701,7 +1869,7 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core,
err = mlxsw_sp_ports_create(mlxsw_sp);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to create ports\n");
- goto err_ports_create;
+ return err;
}
err = mlxsw_sp_event_register(mlxsw_sp, MLXSW_TRAP_ID_PUDE);
@@ -1728,6 +1896,12 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core,
goto err_buffers_init;
}
+ err = mlxsw_sp_lag_init(mlxsw_sp);
+ if (err) {
+ dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize LAG\n");
+ goto err_lag_init;
+ }
+
err = mlxsw_sp_switchdev_init(mlxsw_sp);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize switchdev\n");
@@ -1737,6 +1911,7 @@ static int mlxsw_sp_init(void *priv, struct mlxsw_core *mlxsw_core,
return 0;
err_switchdev_init:
+err_lag_init:
err_buffers_init:
err_flood_init:
mlxsw_sp_traps_fini(mlxsw_sp);
@@ -1744,8 +1919,6 @@ err_rx_listener_register:
mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE);
err_event_register:
mlxsw_sp_ports_remove(mlxsw_sp);
-err_ports_create:
- mlxsw_sp_vfids_fini(mlxsw_sp);
return err;
}
@@ -1757,18 +1930,17 @@ static void mlxsw_sp_fini(void *priv)
mlxsw_sp_traps_fini(mlxsw_sp);
mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE);
mlxsw_sp_ports_remove(mlxsw_sp);
- mlxsw_sp_vfids_fini(mlxsw_sp);
}
static struct mlxsw_config_profile mlxsw_sp_config_profile = {
.used_max_vepa_channels = 1,
.max_vepa_channels = 0,
.used_max_lag = 1,
- .max_lag = 64,
+ .max_lag = MLXSW_SP_LAG_MAX,
.used_max_port_per_lag = 1,
- .max_port_per_lag = 16,
+ .max_port_per_lag = MLXSW_SP_PORT_PER_LAG_MAX,
.used_max_mid = 1,
- .max_mid = 7000,
+ .max_mid = MLXSW_SP_MID_MAX,
.used_max_pgt = 1,
.max_pgt = 0,
.used_max_system_port = 1,
@@ -1782,8 +1954,8 @@ static struct mlxsw_config_profile mlxsw_sp_config_profile = {
.flood_mode = 3,
.max_fid_offset_flood_tables = 2,
.fid_offset_flood_table_size = VLAN_N_VID - 1,
- .max_fid_flood_tables = 1,
- .fid_flood_table_size = VLAN_N_VID,
+ .max_fid_flood_tables = 2,
+ .fid_flood_table_size = MLXSW_SP_VFID_MAX,
.used_max_ib_mc = 1,
.max_ib_mc = 0,
.used_max_pkey = 1,
@@ -1824,24 +1996,29 @@ static int mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port)
*/
err = mlxsw_sp_port_kill_vid(dev, 0, 1);
if (err)
- netdev_err(dev, "Failed to remove VID 1\n");
+ return err;
- return err;
+ mlxsw_sp_port->learning = 1;
+ mlxsw_sp_port->learning_sync = 1;
+ mlxsw_sp_port->uc_flood = 1;
+ mlxsw_sp_port->bridged = 1;
+
+ return 0;
}
static int mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port)
{
struct net_device *dev = mlxsw_sp_port->dev;
- int err;
+
+ mlxsw_sp_port->learning = 0;
+ mlxsw_sp_port->learning_sync = 0;
+ mlxsw_sp_port->uc_flood = 0;
+ mlxsw_sp_port->bridged = 0;
/* Add implicit VLAN interface in the device, so that untagged
* packets will be classified to the default vFID.
*/
- err = mlxsw_sp_port_add_vid(dev, 0, 1);
- if (err)
- netdev_err(dev, "Failed to add VID 1\n");
-
- return err;
+ return mlxsw_sp_port_add_vid(dev, 0, 1);
}
static bool mlxsw_sp_master_bridge_check(struct mlxsw_sp *mlxsw_sp,
@@ -1865,19 +2042,293 @@ static void mlxsw_sp_master_bridge_dec(struct mlxsw_sp *mlxsw_sp,
mlxsw_sp->master_bridge.dev = NULL;
}
-static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
- unsigned long event, void *ptr)
+static int mlxsw_sp_lag_create(struct mlxsw_sp *mlxsw_sp, u16 lag_id)
+{
+ char sldr_pl[MLXSW_REG_SLDR_LEN];
+
+ mlxsw_reg_sldr_lag_create_pack(sldr_pl, lag_id);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl);
+}
+
+static int mlxsw_sp_lag_destroy(struct mlxsw_sp *mlxsw_sp, u16 lag_id)
+{
+ char sldr_pl[MLXSW_REG_SLDR_LEN];
+
+ mlxsw_reg_sldr_lag_destroy_pack(sldr_pl, lag_id);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl);
+}
+
+static int mlxsw_sp_lag_col_port_add(struct mlxsw_sp_port *mlxsw_sp_port,
+ u16 lag_id, u8 port_index)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ char slcor_pl[MLXSW_REG_SLCOR_LEN];
+
+ mlxsw_reg_slcor_port_add_pack(slcor_pl, mlxsw_sp_port->local_port,
+ lag_id, port_index);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl);
+}
+
+static int mlxsw_sp_lag_col_port_remove(struct mlxsw_sp_port *mlxsw_sp_port,
+ u16 lag_id)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ char slcor_pl[MLXSW_REG_SLCOR_LEN];
+
+ mlxsw_reg_slcor_port_remove_pack(slcor_pl, mlxsw_sp_port->local_port,
+ lag_id);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl);
+}
+
+static int mlxsw_sp_lag_col_port_enable(struct mlxsw_sp_port *mlxsw_sp_port,
+ u16 lag_id)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ char slcor_pl[MLXSW_REG_SLCOR_LEN];
+
+ mlxsw_reg_slcor_col_enable_pack(slcor_pl, mlxsw_sp_port->local_port,
+ lag_id);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl);
+}
+
+static int mlxsw_sp_lag_col_port_disable(struct mlxsw_sp_port *mlxsw_sp_port,
+ u16 lag_id)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ char slcor_pl[MLXSW_REG_SLCOR_LEN];
+
+ mlxsw_reg_slcor_col_disable_pack(slcor_pl, mlxsw_sp_port->local_port,
+ lag_id);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(slcor), slcor_pl);
+}
+
+static int mlxsw_sp_lag_index_get(struct mlxsw_sp *mlxsw_sp,
+ struct net_device *lag_dev,
+ u16 *p_lag_id)
+{
+ struct mlxsw_sp_upper *lag;
+ int free_lag_id = -1;
+ int i;
+
+ for (i = 0; i < MLXSW_SP_LAG_MAX; i++) {
+ lag = mlxsw_sp_lag_get(mlxsw_sp, i);
+ if (lag->ref_count) {
+ if (lag->dev == lag_dev) {
+ *p_lag_id = i;
+ return 0;
+ }
+ } else if (free_lag_id < 0) {
+ free_lag_id = i;
+ }
+ }
+ if (free_lag_id < 0)
+ return -EBUSY;
+ *p_lag_id = free_lag_id;
+ return 0;
+}
+
+static bool
+mlxsw_sp_master_lag_check(struct mlxsw_sp *mlxsw_sp,
+ struct net_device *lag_dev,
+ struct netdev_lag_upper_info *lag_upper_info)
+{
+ u16 lag_id;
+
+ if (mlxsw_sp_lag_index_get(mlxsw_sp, lag_dev, &lag_id) != 0)
+ return false;
+ if (lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
+ return false;
+ return true;
+}
+
+static int mlxsw_sp_port_lag_index_get(struct mlxsw_sp *mlxsw_sp,
+ u16 lag_id, u8 *p_port_index)
+{
+ int i;
+
+ for (i = 0; i < MLXSW_SP_PORT_PER_LAG_MAX; i++) {
+ if (!mlxsw_sp_port_lagged_get(mlxsw_sp, lag_id, i)) {
+ *p_port_index = i;
+ return 0;
+ }
+ }
+ return -EBUSY;
+}
+
+static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct net_device *lag_dev)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ struct mlxsw_sp_upper *lag;
+ u16 lag_id;
+ u8 port_index;
+ int err;
+
+ err = mlxsw_sp_lag_index_get(mlxsw_sp, lag_dev, &lag_id);
+ if (err)
+ return err;
+ lag = mlxsw_sp_lag_get(mlxsw_sp, lag_id);
+ if (!lag->ref_count) {
+ err = mlxsw_sp_lag_create(mlxsw_sp, lag_id);
+ if (err)
+ return err;
+ lag->dev = lag_dev;
+ }
+
+ err = mlxsw_sp_port_lag_index_get(mlxsw_sp, lag_id, &port_index);
+ if (err)
+ return err;
+ err = mlxsw_sp_lag_col_port_add(mlxsw_sp_port, lag_id, port_index);
+ if (err)
+ goto err_col_port_add;
+ err = mlxsw_sp_lag_col_port_enable(mlxsw_sp_port, lag_id);
+ if (err)
+ goto err_col_port_enable;
+
+ mlxsw_core_lag_mapping_set(mlxsw_sp->core, lag_id, port_index,
+ mlxsw_sp_port->local_port);
+ mlxsw_sp_port->lag_id = lag_id;
+ mlxsw_sp_port->lagged = 1;
+ lag->ref_count++;
+ return 0;
+
+err_col_port_add:
+ if (!lag->ref_count)
+ mlxsw_sp_lag_destroy(mlxsw_sp, lag_id);
+err_col_port_enable:
+ mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id);
+ return err;
+}
+
+static int mlxsw_sp_port_lag_leave(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct net_device *lag_dev)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ struct mlxsw_sp_upper *lag;
+ u16 lag_id = mlxsw_sp_port->lag_id;
+ int err;
+
+ if (!mlxsw_sp_port->lagged)
+ return 0;
+ lag = mlxsw_sp_lag_get(mlxsw_sp, lag_id);
+ WARN_ON(lag->ref_count == 0);
+
+ err = mlxsw_sp_lag_col_port_disable(mlxsw_sp_port, lag_id);
+ if (err)
+ return err;
+ err = mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id);
+ if (err)
+ return err;
+
+ if (lag->ref_count == 1) {
+ err = mlxsw_sp_lag_destroy(mlxsw_sp, lag_id);
+ if (err)
+ return err;
+ }
+
+ mlxsw_core_lag_mapping_clear(mlxsw_sp->core, lag_id,
+ mlxsw_sp_port->local_port);
+ mlxsw_sp_port->lagged = 0;
+ lag->ref_count--;
+ return 0;
+}
+
+static int mlxsw_sp_lag_dist_port_add(struct mlxsw_sp_port *mlxsw_sp_port,
+ u16 lag_id)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ char sldr_pl[MLXSW_REG_SLDR_LEN];
+
+ mlxsw_reg_sldr_lag_add_port_pack(sldr_pl, lag_id,
+ mlxsw_sp_port->local_port);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl);
+}
+
+static int mlxsw_sp_lag_dist_port_remove(struct mlxsw_sp_port *mlxsw_sp_port,
+ u16 lag_id)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ char sldr_pl[MLXSW_REG_SLDR_LEN];
+
+ mlxsw_reg_sldr_lag_remove_port_pack(sldr_pl, lag_id,
+ mlxsw_sp_port->local_port);
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sldr), sldr_pl);
+}
+
+static int mlxsw_sp_port_lag_tx_en_set(struct mlxsw_sp_port *mlxsw_sp_port,
+ bool lag_tx_enabled)
+{
+ if (lag_tx_enabled)
+ return mlxsw_sp_lag_dist_port_add(mlxsw_sp_port,
+ mlxsw_sp_port->lag_id);
+ else
+ return mlxsw_sp_lag_dist_port_remove(mlxsw_sp_port,
+ mlxsw_sp_port->lag_id);
+}
+
+static int mlxsw_sp_port_lag_changed(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct netdev_lag_lower_state_info *info)
+{
+ return mlxsw_sp_port_lag_tx_en_set(mlxsw_sp_port, info->tx_enabled);
+}
+
+static int mlxsw_sp_vport_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_vport,
+ struct net_device *br_dev);
+
+static int mlxsw_sp_port_vlan_link(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct net_device *vlan_dev)
+{
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+ u16 vid = vlan_dev_vlan_id(vlan_dev);
+
+ mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid);
+ if (!mlxsw_sp_vport) {
+ WARN_ON(!mlxsw_sp_vport);
+ return -EINVAL;
+ }
+
+ mlxsw_sp_vport->dev = vlan_dev;
+
+ return 0;
+}
+
+static int mlxsw_sp_port_vlan_unlink(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct net_device *vlan_dev)
+{
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+ u16 vid = vlan_dev_vlan_id(vlan_dev);
+
+ mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid);
+ if (!mlxsw_sp_vport) {
+ WARN_ON(!mlxsw_sp_vport);
+ return -EINVAL;
+ }
+
+ /* When removing a VLAN device while still bridged we should first
+ * remove it from the bridge, as we receive the bridge's notification
+ * when the vPort is already gone.
+ */
+ if (mlxsw_sp_vport->bridged) {
+ struct net_device *br_dev;
+
+ br_dev = mlxsw_sp_vport_br_get(mlxsw_sp_vport);
+ mlxsw_sp_vport_bridge_leave(mlxsw_sp_vport, br_dev);
+ }
+
+ mlxsw_sp_vport->dev = mlxsw_sp_port->dev;
+
+ return 0;
+}
+
+static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev,
+ unsigned long event, void *ptr)
{
- struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct netdev_notifier_changeupper_info *info;
struct mlxsw_sp_port *mlxsw_sp_port;
struct net_device *upper_dev;
struct mlxsw_sp *mlxsw_sp;
int err;
- if (!mlxsw_sp_port_dev_check(dev))
- return NOTIFY_DONE;
-
mlxsw_sp_port = netdev_priv(dev);
mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
info = ptr;
@@ -1885,28 +2336,66 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
switch (event) {
case NETDEV_PRECHANGEUPPER:
upper_dev = info->upper_dev;
+ if (!info->master || !info->linking)
+ break;
/* HW limitation forbids to put ports to multiple bridges. */
- if (info->master && info->linking &&
- netif_is_bridge_master(upper_dev) &&
+ if (netif_is_bridge_master(upper_dev) &&
!mlxsw_sp_master_bridge_check(mlxsw_sp, upper_dev))
return NOTIFY_BAD;
+ if (netif_is_lag_master(upper_dev) &&
+ !mlxsw_sp_master_lag_check(mlxsw_sp, upper_dev,
+ info->upper_info))
+ return NOTIFY_BAD;
break;
case NETDEV_CHANGEUPPER:
upper_dev = info->upper_dev;
- if (info->master &&
- netif_is_bridge_master(upper_dev)) {
+ if (is_vlan_dev(upper_dev)) {
+ if (info->linking) {
+ err = mlxsw_sp_port_vlan_link(mlxsw_sp_port,
+ upper_dev);
+ if (err) {
+ netdev_err(dev, "Failed to link VLAN device\n");
+ return NOTIFY_BAD;
+ }
+ } else {
+ err = mlxsw_sp_port_vlan_unlink(mlxsw_sp_port,
+ upper_dev);
+ if (err) {
+ netdev_err(dev, "Failed to unlink VLAN device\n");
+ return NOTIFY_BAD;
+ }
+ }
+ } else if (netif_is_bridge_master(upper_dev)) {
if (info->linking) {
err = mlxsw_sp_port_bridge_join(mlxsw_sp_port);
- if (err)
+ if (err) {
netdev_err(dev, "Failed to join bridge\n");
+ return NOTIFY_BAD;
+ }
mlxsw_sp_master_bridge_inc(mlxsw_sp, upper_dev);
- mlxsw_sp_port->bridged = 1;
} else {
err = mlxsw_sp_port_bridge_leave(mlxsw_sp_port);
- if (err)
- netdev_err(dev, "Failed to leave bridge\n");
- mlxsw_sp_port->bridged = 0;
mlxsw_sp_master_bridge_dec(mlxsw_sp, upper_dev);
+ if (err) {
+ netdev_err(dev, "Failed to leave bridge\n");
+ return NOTIFY_BAD;
+ }
+ }
+ } else if (netif_is_lag_master(upper_dev)) {
+ if (info->linking) {
+ err = mlxsw_sp_port_lag_join(mlxsw_sp_port,
+ upper_dev);
+ if (err) {
+ netdev_err(dev, "Failed to join link aggregation\n");
+ return NOTIFY_BAD;
+ }
+ } else {
+ err = mlxsw_sp_port_lag_leave(mlxsw_sp_port,
+ upper_dev);
+ if (err) {
+ netdev_err(dev, "Failed to leave link aggregation\n");
+ return NOTIFY_BAD;
+ }
}
}
break;
@@ -1915,6 +2404,443 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
return NOTIFY_DONE;
}
+static int mlxsw_sp_netdevice_port_lower_event(struct net_device *dev,
+ unsigned long event, void *ptr)
+{
+ struct netdev_notifier_changelowerstate_info *info;
+ struct mlxsw_sp_port *mlxsw_sp_port;
+ int err;
+
+ mlxsw_sp_port = netdev_priv(dev);
+ info = ptr;
+
+ switch (event) {
+ case NETDEV_CHANGELOWERSTATE:
+ if (netif_is_lag_port(dev) && mlxsw_sp_port->lagged) {
+ err = mlxsw_sp_port_lag_changed(mlxsw_sp_port,
+ info->lower_state_info);
+ if (err)
+ netdev_err(dev, "Failed to reflect link aggregation lower state change\n");
+ }
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int mlxsw_sp_netdevice_port_event(struct net_device *dev,
+ unsigned long event, void *ptr)
+{
+ switch (event) {
+ case NETDEV_PRECHANGEUPPER:
+ case NETDEV_CHANGEUPPER:
+ return mlxsw_sp_netdevice_port_upper_event(dev, event, ptr);
+ case NETDEV_CHANGELOWERSTATE:
+ return mlxsw_sp_netdevice_port_lower_event(dev, event, ptr);
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int mlxsw_sp_netdevice_lag_event(struct net_device *lag_dev,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev;
+ struct list_head *iter;
+ int ret;
+
+ netdev_for_each_lower_dev(lag_dev, dev, iter) {
+ if (mlxsw_sp_port_dev_check(dev)) {
+ ret = mlxsw_sp_netdevice_port_event(dev, event, ptr);
+ if (ret == NOTIFY_BAD)
+ return ret;
+ }
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct mlxsw_sp_vfid *
+mlxsw_sp_br_vfid_find(const struct mlxsw_sp *mlxsw_sp,
+ const struct net_device *br_dev)
+{
+ struct mlxsw_sp_vfid *vfid;
+
+ list_for_each_entry(vfid, &mlxsw_sp->br_vfids.list, list) {
+ if (vfid->br_dev == br_dev)
+ return vfid;
+ }
+
+ return NULL;
+}
+
+static u16 mlxsw_sp_vfid_to_br_vfid(u16 vfid)
+{
+ return vfid - MLXSW_SP_VFID_PORT_MAX;
+}
+
+static u16 mlxsw_sp_br_vfid_to_vfid(u16 br_vfid)
+{
+ return MLXSW_SP_VFID_PORT_MAX + br_vfid;
+}
+
+static u16 mlxsw_sp_avail_br_vfid_get(const struct mlxsw_sp *mlxsw_sp)
+{
+ return find_first_zero_bit(mlxsw_sp->br_vfids.mapped,
+ MLXSW_SP_VFID_BR_MAX);
+}
+
+static struct mlxsw_sp_vfid *mlxsw_sp_br_vfid_create(struct mlxsw_sp *mlxsw_sp,
+ struct net_device *br_dev)
+{
+ struct device *dev = mlxsw_sp->bus_info->dev;
+ struct mlxsw_sp_vfid *vfid;
+ u16 n_vfid;
+ int err;
+
+ n_vfid = mlxsw_sp_br_vfid_to_vfid(mlxsw_sp_avail_br_vfid_get(mlxsw_sp));
+ if (n_vfid == MLXSW_SP_VFID_MAX) {
+ dev_err(dev, "No available vFIDs\n");
+ return ERR_PTR(-ERANGE);
+ }
+
+ err = __mlxsw_sp_vfid_create(mlxsw_sp, n_vfid);
+ if (err) {
+ dev_err(dev, "Failed to create vFID=%d\n", n_vfid);
+ return ERR_PTR(err);
+ }
+
+ vfid = kzalloc(sizeof(*vfid), GFP_KERNEL);
+ if (!vfid)
+ goto err_allocate_vfid;
+
+ vfid->vfid = n_vfid;
+ vfid->br_dev = br_dev;
+
+ list_add(&vfid->list, &mlxsw_sp->br_vfids.list);
+ set_bit(mlxsw_sp_vfid_to_br_vfid(n_vfid), mlxsw_sp->br_vfids.mapped);
+
+ return vfid;
+
+err_allocate_vfid:
+ __mlxsw_sp_vfid_destroy(mlxsw_sp, n_vfid);
+ return ERR_PTR(-ENOMEM);
+}
+
+static void mlxsw_sp_br_vfid_destroy(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_vfid *vfid)
+{
+ u16 br_vfid = mlxsw_sp_vfid_to_br_vfid(vfid->vfid);
+
+ clear_bit(br_vfid, mlxsw_sp->br_vfids.mapped);
+ list_del(&vfid->list);
+
+ __mlxsw_sp_vfid_destroy(mlxsw_sp, vfid->vfid);
+
+ kfree(vfid);
+}
+
+static int mlxsw_sp_vport_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_vport,
+ struct net_device *br_dev)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp;
+ u16 vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport);
+ struct net_device *dev = mlxsw_sp_vport->dev;
+ struct mlxsw_sp_vfid *vfid, *new_vfid;
+ int err;
+
+ vfid = mlxsw_sp_br_vfid_find(mlxsw_sp, br_dev);
+ if (!vfid) {
+ WARN_ON(!vfid);
+ return -EINVAL;
+ }
+
+ /* We need a vFID to go back to after leaving the bridge's vFID. */
+ new_vfid = mlxsw_sp_vfid_find(mlxsw_sp, vid);
+ if (!new_vfid) {
+ new_vfid = mlxsw_sp_vfid_create(mlxsw_sp, vid);
+ if (IS_ERR(new_vfid)) {
+ netdev_err(dev, "Failed to create vFID for VID=%d\n",
+ vid);
+ return PTR_ERR(new_vfid);
+ }
+ }
+
+ /* Invalidate existing {Port, VID} to vFID mapping and create a new
+ * one for the new vFID.
+ */
+ err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport,
+ MLXSW_REG_SVFA_MT_PORT_VID_TO_FID,
+ false,
+ mlxsw_sp_vfid_to_fid(vfid->vfid),
+ vid);
+ if (err) {
+ netdev_err(dev, "Failed to invalidate {Port, VID} to vFID=%d mapping\n",
+ vfid->vfid);
+ goto err_port_vid_to_fid_invalidate;
+ }
+
+ err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport,
+ MLXSW_REG_SVFA_MT_PORT_VID_TO_FID,
+ true,
+ mlxsw_sp_vfid_to_fid(new_vfid->vfid),
+ vid);
+ if (err) {
+ netdev_err(dev, "Failed to map {Port, VID} to vFID=%d\n",
+ new_vfid->vfid);
+ goto err_port_vid_to_fid_validate;
+ }
+
+ err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, false);
+ if (err) {
+ netdev_err(dev, "Failed to disable learning\n");
+ goto err_port_vid_learning_set;
+ }
+
+ err = mlxsw_sp_vport_flood_set(mlxsw_sp_vport, vfid->vfid, false,
+ false);
+ if (err) {
+ netdev_err(dev, "Failed clear to clear flooding\n");
+ goto err_vport_flood_set;
+ }
+
+ /* Switch between the vFIDs and destroy the old one if needed. */
+ new_vfid->nr_vports++;
+ mlxsw_sp_vport->vport.vfid = new_vfid;
+ vfid->nr_vports--;
+ if (!vfid->nr_vports)
+ mlxsw_sp_br_vfid_destroy(mlxsw_sp, vfid);
+
+ mlxsw_sp_vport->learning = 0;
+ mlxsw_sp_vport->learning_sync = 0;
+ mlxsw_sp_vport->uc_flood = 0;
+ mlxsw_sp_vport->bridged = 0;
+
+ return 0;
+
+err_vport_flood_set:
+err_port_vid_learning_set:
+err_port_vid_to_fid_validate:
+err_port_vid_to_fid_invalidate:
+ /* Rollback vFID only if new. */
+ if (!new_vfid->nr_vports)
+ mlxsw_sp_vfid_destroy(mlxsw_sp, new_vfid);
+ return err;
+}
+
+static int mlxsw_sp_vport_bridge_join(struct mlxsw_sp_port *mlxsw_sp_vport,
+ struct net_device *br_dev)
+{
+ struct mlxsw_sp_vfid *old_vfid = mlxsw_sp_vport->vport.vfid;
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp;
+ u16 vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport);
+ struct net_device *dev = mlxsw_sp_vport->dev;
+ struct mlxsw_sp_vfid *vfid;
+ int err;
+
+ vfid = mlxsw_sp_br_vfid_find(mlxsw_sp, br_dev);
+ if (!vfid) {
+ vfid = mlxsw_sp_br_vfid_create(mlxsw_sp, br_dev);
+ if (IS_ERR(vfid)) {
+ netdev_err(dev, "Failed to create bridge vFID\n");
+ return PTR_ERR(vfid);
+ }
+ }
+
+ err = mlxsw_sp_vport_flood_set(mlxsw_sp_vport, vfid->vfid, true, false);
+ if (err) {
+ netdev_err(dev, "Failed to setup flooding for vFID=%d\n",
+ vfid->vfid);
+ goto err_port_flood_set;
+ }
+
+ err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, true);
+ if (err) {
+ netdev_err(dev, "Failed to enable learning\n");
+ goto err_port_vid_learning_set;
+ }
+
+ /* We need to invalidate existing {Port, VID} to vFID mapping and
+ * create a new one for the bridge's vFID.
+ */
+ err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport,
+ MLXSW_REG_SVFA_MT_PORT_VID_TO_FID,
+ false,
+ mlxsw_sp_vfid_to_fid(old_vfid->vfid),
+ vid);
+ if (err) {
+ netdev_err(dev, "Failed to invalidate {Port, VID} to vFID=%d mapping\n",
+ old_vfid->vfid);
+ goto err_port_vid_to_fid_invalidate;
+ }
+
+ err = mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport,
+ MLXSW_REG_SVFA_MT_PORT_VID_TO_FID,
+ true,
+ mlxsw_sp_vfid_to_fid(vfid->vfid),
+ vid);
+ if (err) {
+ netdev_err(dev, "Failed to map {Port, VID} to vFID=%d\n",
+ vfid->vfid);
+ goto err_port_vid_to_fid_validate;
+ }
+
+ /* Switch between the vFIDs and destroy the old one if needed. */
+ vfid->nr_vports++;
+ mlxsw_sp_vport->vport.vfid = vfid;
+ old_vfid->nr_vports--;
+ if (!old_vfid->nr_vports)
+ mlxsw_sp_vfid_destroy(mlxsw_sp, old_vfid);
+
+ mlxsw_sp_vport->learning = 1;
+ mlxsw_sp_vport->learning_sync = 1;
+ mlxsw_sp_vport->uc_flood = 1;
+ mlxsw_sp_vport->bridged = 1;
+
+ return 0;
+
+err_port_vid_to_fid_validate:
+ mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_vport,
+ MLXSW_REG_SVFA_MT_PORT_VID_TO_FID, false,
+ mlxsw_sp_vfid_to_fid(old_vfid->vfid), vid);
+err_port_vid_to_fid_invalidate:
+ mlxsw_sp_port_vid_learning_set(mlxsw_sp_vport, vid, false);
+err_port_vid_learning_set:
+ mlxsw_sp_vport_flood_set(mlxsw_sp_vport, vfid->vfid, false, false);
+err_port_flood_set:
+ if (!vfid->nr_vports)
+ mlxsw_sp_br_vfid_destroy(mlxsw_sp, vfid);
+ return err;
+}
+
+static bool
+mlxsw_sp_port_master_bridge_check(const struct mlxsw_sp_port *mlxsw_sp_port,
+ const struct net_device *br_dev)
+{
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+
+ list_for_each_entry(mlxsw_sp_vport, &mlxsw_sp_port->vports_list,
+ vport.list) {
+ if (mlxsw_sp_vport_br_get(mlxsw_sp_vport) == br_dev)
+ return false;
+ }
+
+ return true;
+}
+
+static int mlxsw_sp_netdevice_vport_event(struct net_device *dev,
+ unsigned long event, void *ptr,
+ u16 vid)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
+ struct netdev_notifier_changeupper_info *info = ptr;
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+ struct net_device *upper_dev;
+ int err;
+
+ mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid);
+
+ switch (event) {
+ case NETDEV_PRECHANGEUPPER:
+ upper_dev = info->upper_dev;
+ if (!info->master || !info->linking)
+ break;
+ if (!netif_is_bridge_master(upper_dev))
+ return NOTIFY_BAD;
+ /* We can't have multiple VLAN interfaces configured on
+ * the same port and being members in the same bridge.
+ */
+ if (!mlxsw_sp_port_master_bridge_check(mlxsw_sp_port,
+ upper_dev))
+ return NOTIFY_BAD;
+ break;
+ case NETDEV_CHANGEUPPER:
+ upper_dev = info->upper_dev;
+ if (!info->master)
+ break;
+ if (info->linking) {
+ if (!mlxsw_sp_vport) {
+ WARN_ON(!mlxsw_sp_vport);
+ return NOTIFY_BAD;
+ }
+ err = mlxsw_sp_vport_bridge_join(mlxsw_sp_vport,
+ upper_dev);
+ if (err) {
+ netdev_err(dev, "Failed to join bridge\n");
+ return NOTIFY_BAD;
+ }
+ } else {
+ /* We ignore bridge's unlinking notifications if vPort
+ * is gone, since we already left the bridge when the
+ * VLAN device was unlinked from the real device.
+ */
+ if (!mlxsw_sp_vport)
+ return NOTIFY_DONE;
+ err = mlxsw_sp_vport_bridge_leave(mlxsw_sp_vport,
+ upper_dev);
+ if (err) {
+ netdev_err(dev, "Failed to leave bridge\n");
+ return NOTIFY_BAD;
+ }
+ }
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int mlxsw_sp_netdevice_lag_vport_event(struct net_device *lag_dev,
+ unsigned long event, void *ptr,
+ u16 vid)
+{
+ struct net_device *dev;
+ struct list_head *iter;
+ int ret;
+
+ netdev_for_each_lower_dev(lag_dev, dev, iter) {
+ if (mlxsw_sp_port_dev_check(dev)) {
+ ret = mlxsw_sp_netdevice_vport_event(dev, event, ptr,
+ vid);
+ if (ret == NOTIFY_BAD)
+ return ret;
+ }
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int mlxsw_sp_netdevice_vlan_event(struct net_device *vlan_dev,
+ unsigned long event, void *ptr)
+{
+ struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
+ u16 vid = vlan_dev_vlan_id(vlan_dev);
+
+ if (mlxsw_sp_port_dev_check(real_dev))
+ return mlxsw_sp_netdevice_vport_event(real_dev, event, ptr,
+ vid);
+ else if (netif_is_lag_master(real_dev))
+ return mlxsw_sp_netdevice_lag_vport_event(real_dev, event, ptr,
+ vid);
+
+ return NOTIFY_DONE;
+}
+
+static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+
+ if (mlxsw_sp_port_dev_check(dev))
+ return mlxsw_sp_netdevice_port_event(dev, event, ptr);
+
+ if (netif_is_lag_master(dev))
+ return mlxsw_sp_netdevice_lag_event(dev, event, ptr);
+
+ if (is_vlan_dev(dev))
+ return mlxsw_sp_netdevice_vlan_event(dev, event, ptr);
+
+ return NOTIFY_DONE;
+}
+
static struct notifier_block mlxsw_sp_netdevice_nb __read_mostly = {
.notifier_call = mlxsw_sp_netdevice_event,
};
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
index 4365c8bccc6d..a23dc610d259 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h
@@ -41,16 +41,73 @@
#include <linux/netdevice.h>
#include <linux/bitops.h>
#include <linux/if_vlan.h>
+#include <linux/list.h>
#include <net/switchdev.h>
+#include "port.h"
#include "core.h"
#define MLXSW_SP_VFID_BASE VLAN_N_VID
+#define MLXSW_SP_VFID_PORT_MAX 512 /* Non-bridged VLAN interfaces */
+#define MLXSW_SP_VFID_BR_MAX 8192 /* Bridged VLAN interfaces */
+#define MLXSW_SP_VFID_MAX (MLXSW_SP_VFID_PORT_MAX + MLXSW_SP_VFID_BR_MAX)
+
+#define MLXSW_SP_LAG_MAX 64
+#define MLXSW_SP_PORT_PER_LAG_MAX 16
+
+#define MLXSW_SP_MID_MAX 7000
struct mlxsw_sp_port;
+struct mlxsw_sp_upper {
+ struct net_device *dev;
+ unsigned int ref_count;
+};
+
+struct mlxsw_sp_vfid {
+ struct list_head list;
+ u16 nr_vports;
+ u16 vfid; /* Starting at 0 */
+ struct net_device *br_dev;
+ u16 vid;
+};
+
+struct mlxsw_sp_mid {
+ struct list_head list;
+ unsigned char addr[ETH_ALEN];
+ u16 vid;
+ u16 mid;
+ unsigned int ref_count;
+};
+
+static inline u16 mlxsw_sp_vfid_to_fid(u16 vfid)
+{
+ return MLXSW_SP_VFID_BASE + vfid;
+}
+
+static inline u16 mlxsw_sp_fid_to_vfid(u16 fid)
+{
+ return fid - MLXSW_SP_VFID_BASE;
+}
+
+static inline bool mlxsw_sp_fid_is_vfid(u16 fid)
+{
+ return fid >= MLXSW_SP_VFID_BASE;
+}
+
struct mlxsw_sp {
- unsigned long active_vfids[BITS_TO_LONGS(VLAN_N_VID)];
+ struct {
+ struct list_head list;
+ unsigned long mapped[BITS_TO_LONGS(MLXSW_SP_VFID_PORT_MAX)];
+ } port_vfids;
+ struct {
+ struct list_head list;
+ unsigned long mapped[BITS_TO_LONGS(MLXSW_SP_VFID_BR_MAX)];
+ } br_vfids;
+ struct {
+ struct list_head list;
+ unsigned long mapped[BITS_TO_LONGS(MLXSW_SP_MID_MAX)];
+ } br_mids;
unsigned long active_fids[BITS_TO_LONGS(VLAN_N_VID)];
struct mlxsw_sp_port **ports;
struct mlxsw_core *core;
@@ -63,12 +120,17 @@ struct mlxsw_sp {
} fdb_notify;
#define MLXSW_SP_DEFAULT_AGEING_TIME 300
u32 ageing_time;
- struct {
- struct net_device *dev;
- unsigned int ref_count;
- } master_bridge;
+ struct mutex fdb_lock; /* Make sure FDB sessions are atomic. */
+ struct mlxsw_sp_upper master_bridge;
+ struct mlxsw_sp_upper lags[MLXSW_SP_LAG_MAX];
};
+static inline struct mlxsw_sp_upper *
+mlxsw_sp_lag_get(struct mlxsw_sp *mlxsw_sp, u16 lag_id)
+{
+ return &mlxsw_sp->lags[lag_id];
+}
+
struct mlxsw_sp_port_pcpu_stats {
u64 rx_packets;
u64 rx_bytes;
@@ -87,15 +149,87 @@ struct mlxsw_sp_port {
u8 learning:1,
learning_sync:1,
uc_flood:1,
- bridged:1;
+ bridged:1,
+ lagged:1;
u16 pvid;
+ u16 lag_id;
+ struct {
+ struct list_head list;
+ struct mlxsw_sp_vfid *vfid;
+ u16 vid;
+ } vport;
/* 802.1Q bridge VLANs */
- unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
+ unsigned long *active_vlans;
+ unsigned long *untagged_vlans;
/* VLAN interfaces */
- unsigned long active_vfids[BITS_TO_LONGS(VLAN_N_VID)];
- u16 nr_vfids;
+ struct list_head vports_list;
};
+static inline struct mlxsw_sp_port *
+mlxsw_sp_port_lagged_get(struct mlxsw_sp *mlxsw_sp, u16 lag_id, u8 port_index)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port;
+ u8 local_port;
+
+ local_port = mlxsw_core_lag_mapping_get(mlxsw_sp->core,
+ lag_id, port_index);
+ mlxsw_sp_port = mlxsw_sp->ports[local_port];
+ return mlxsw_sp_port && mlxsw_sp_port->lagged ? mlxsw_sp_port : NULL;
+}
+
+static inline bool
+mlxsw_sp_port_is_vport(const struct mlxsw_sp_port *mlxsw_sp_port)
+{
+ return mlxsw_sp_port->vport.vfid;
+}
+
+static inline struct net_device *
+mlxsw_sp_vport_br_get(const struct mlxsw_sp_port *mlxsw_sp_vport)
+{
+ return mlxsw_sp_vport->vport.vfid->br_dev;
+}
+
+static inline u16
+mlxsw_sp_vport_vid_get(const struct mlxsw_sp_port *mlxsw_sp_vport)
+{
+ return mlxsw_sp_vport->vport.vid;
+}
+
+static inline u16
+mlxsw_sp_vport_vfid_get(const struct mlxsw_sp_port *mlxsw_sp_vport)
+{
+ return mlxsw_sp_vport->vport.vfid->vfid;
+}
+
+static inline struct mlxsw_sp_port *
+mlxsw_sp_port_vport_find(const struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
+{
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+
+ list_for_each_entry(mlxsw_sp_vport, &mlxsw_sp_port->vports_list,
+ vport.list) {
+ if (mlxsw_sp_vport_vid_get(mlxsw_sp_vport) == vid)
+ return mlxsw_sp_vport;
+ }
+
+ return NULL;
+}
+
+static inline struct mlxsw_sp_port *
+mlxsw_sp_port_vport_find_by_vfid(const struct mlxsw_sp_port *mlxsw_sp_port,
+ u16 vfid)
+{
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+
+ list_for_each_entry(mlxsw_sp_vport, &mlxsw_sp_port->vports_list,
+ vport.list) {
+ if (mlxsw_sp_vport_vfid_get(mlxsw_sp_vport) == vfid)
+ return mlxsw_sp_vport;
+ }
+
+ return NULL;
+}
+
enum mlxsw_sp_flood_table {
MLXSW_SP_FLOOD_TABLE_UC,
MLXSW_SP_FLOOD_TABLE_BM,
@@ -118,5 +252,7 @@ int mlxsw_sp_port_add_vid(struct net_device *dev, __be16 __always_unused proto,
u16 vid);
int mlxsw_sp_port_kill_vid(struct net_device *dev,
__be16 __always_unused proto, u16 vid);
+int mlxsw_sp_vport_flood_set(struct mlxsw_sp_port *mlxsw_sp_vport, u16 vfid,
+ bool set, bool only_uc);
#endif
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
index 617fb22b5d81..ffe894e6d287 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
@@ -51,12 +51,50 @@
#include "core.h"
#include "reg.h"
+static u16 mlxsw_sp_port_vid_to_fid_get(struct mlxsw_sp_port *mlxsw_sp_port,
+ u16 vid)
+{
+ u16 fid = vid;
+
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
+ u16 vfid = mlxsw_sp_vport_vfid_get(mlxsw_sp_port);
+
+ fid = mlxsw_sp_vfid_to_fid(vfid);
+ }
+
+ if (!fid)
+ fid = mlxsw_sp_port->pvid;
+
+ return fid;
+}
+
+static struct mlxsw_sp_port *
+mlxsw_sp_port_orig_get(struct net_device *dev,
+ struct mlxsw_sp_port *mlxsw_sp_port)
+{
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+ u16 vid;
+
+ if (!is_vlan_dev(dev))
+ return mlxsw_sp_port;
+
+ vid = vlan_dev_vlan_id(dev);
+ mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid);
+ WARN_ON(!mlxsw_sp_vport);
+
+ return mlxsw_sp_vport;
+}
+
static int mlxsw_sp_port_attr_get(struct net_device *dev,
struct switchdev_attr *attr)
{
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ mlxsw_sp_port = mlxsw_sp_port_orig_get(attr->orig_dev, mlxsw_sp_port);
+ if (!mlxsw_sp_port)
+ return -EINVAL;
+
switch (attr->id) {
case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
attr->u.ppid.id_len = sizeof(mlxsw_sp->base_mac);
@@ -105,8 +143,14 @@ static int mlxsw_sp_port_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port,
if (!spms_pl)
return -ENOMEM;
mlxsw_reg_spms_pack(spms_pl, mlxsw_sp_port->local_port);
- for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID)
+
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
+ vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
mlxsw_reg_spms_vid_pack(spms_pl, vid, spms_state);
+ } else {
+ for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID)
+ mlxsw_reg_spms_vid_pack(spms_pl, vid, spms_state);
+ }
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spms), spms_pl);
kfree(spms_pl);
@@ -124,22 +168,38 @@ static int mlxsw_sp_port_attr_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port,
return mlxsw_sp_port_stp_state_set(mlxsw_sp_port, state);
}
+static bool mlxsw_sp_vfid_is_vport_br(u16 vfid)
+{
+ return vfid >= MLXSW_SP_VFID_PORT_MAX;
+}
+
static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
- u16 fid_begin, u16 fid_end, bool set,
+ u16 idx_begin, u16 idx_end, bool set,
bool only_uc)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
- u16 range = fid_end - fid_begin + 1;
+ u16 local_port = mlxsw_sp_port->local_port;
+ enum mlxsw_flood_table_type table_type;
+ u16 range = idx_end - idx_begin + 1;
char *sftr_pl;
int err;
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
+ table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID;
+ if (mlxsw_sp_vfid_is_vport_br(idx_begin))
+ local_port = mlxsw_sp_port->local_port;
+ else
+ local_port = MLXSW_PORT_CPU_PORT;
+ } else {
+ table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST;
+ }
+
sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL);
if (!sftr_pl)
return -ENOMEM;
- mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_UC, fid_begin,
- MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST, range,
- mlxsw_sp_port->local_port, set);
+ mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_UC, idx_begin,
+ table_type, range, local_port, set);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
if (err)
goto buffer_out;
@@ -150,9 +210,8 @@ static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
if (only_uc)
goto buffer_out;
- mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BM, fid_begin,
- MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST, range,
- mlxsw_sp_port->local_port, set);
+ mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BM, idx_begin,
+ table_type, range, local_port, set);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
buffer_out:
@@ -167,6 +226,13 @@ static int mlxsw_sp_port_uc_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
u16 vid, last_visited_vid;
int err;
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
+ u16 vfid = mlxsw_sp_vport_vfid_get(mlxsw_sp_port);
+
+ return __mlxsw_sp_port_flood_set(mlxsw_sp_port, vfid, vfid,
+ set, true);
+ }
+
for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) {
err = __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid, vid, set,
true);
@@ -185,6 +251,16 @@ err_port_flood_set:
return err;
}
+int mlxsw_sp_vport_flood_set(struct mlxsw_sp_port *mlxsw_sp_vport, u16 vfid,
+ bool set, bool only_uc)
+{
+ /* In case of vFIDs, index into the flooding table is relative to
+ * the start of the vFIDs range.
+ */
+ return __mlxsw_sp_port_flood_set(mlxsw_sp_vport, vfid, vfid, set,
+ only_uc);
+}
+
static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
struct switchdev_trans *trans,
unsigned long brport_flags)
@@ -193,6 +269,9 @@ static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
bool set;
int err;
+ if (!mlxsw_sp_port->bridged)
+ return -EINVAL;
+
if (switchdev_trans_ph_prepare(trans))
return 0;
@@ -237,6 +316,22 @@ static int mlxsw_sp_port_attr_br_ageing_set(struct mlxsw_sp_port *mlxsw_sp_port,
return mlxsw_sp_ageing_set(mlxsw_sp, ageing_time);
}
+static int mlxsw_sp_port_attr_br_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port,
+ struct switchdev_trans *trans,
+ struct net_device *orig_dev,
+ bool vlan_enabled)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+
+ /* SWITCHDEV_TRANS_PREPARE phase */
+ if ((!vlan_enabled) && (mlxsw_sp->master_bridge.dev == orig_dev)) {
+ netdev_err(mlxsw_sp_port->dev, "Bridge must be vlan-aware\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int mlxsw_sp_port_attr_set(struct net_device *dev,
const struct switchdev_attr *attr,
struct switchdev_trans *trans)
@@ -244,6 +339,10 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev,
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
int err = 0;
+ mlxsw_sp_port = mlxsw_sp_port_orig_get(attr->orig_dev, mlxsw_sp_port);
+ if (!mlxsw_sp_port)
+ return -EINVAL;
+
switch (attr->id) {
case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
err = mlxsw_sp_port_attr_stp_state_set(mlxsw_sp_port, trans,
@@ -257,6 +356,11 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev,
err = mlxsw_sp_port_attr_br_ageing_set(mlxsw_sp_port, trans,
attr->u.ageing_time);
break;
+ case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
+ err = mlxsw_sp_port_attr_br_vlan_set(mlxsw_sp_port, trans,
+ attr->orig_dev,
+ attr->u.vlan_filtering);
+ break;
default:
err = -EOPNOTSUPP;
break;
@@ -304,7 +408,7 @@ static int mlxsw_sp_port_fid_map(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid)
{
enum mlxsw_reg_svfa_mt mt;
- if (mlxsw_sp_port->nr_vfids)
+ if (!list_empty(&mlxsw_sp_port->vports_list))
mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID;
else
mt = MLXSW_REG_SVFA_MT_VID_TO_FID;
@@ -316,7 +420,7 @@ static int mlxsw_sp_port_fid_unmap(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid)
{
enum mlxsw_reg_svfa_mt mt;
- if (!mlxsw_sp_port->nr_vfids)
+ if (list_empty(&mlxsw_sp_port->vports_list))
return 0;
mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID;
@@ -342,14 +446,35 @@ err_port_add_vid:
return err;
}
+static int __mlxsw_sp_port_vlans_set(struct mlxsw_sp_port *mlxsw_sp_port,
+ u16 vid_begin, u16 vid_end, bool is_member,
+ bool untagged)
+{
+ u16 vid, vid_e;
+ int err;
+
+ for (vid = vid_begin; vid <= vid_end;
+ vid += MLXSW_REG_SPVM_REC_MAX_COUNT) {
+ vid_e = min((u16) (vid + MLXSW_REG_SPVM_REC_MAX_COUNT - 1),
+ vid_end);
+
+ err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e,
+ is_member, untagged);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
u16 vid_begin, u16 vid_end,
bool flag_untagged, bool flag_pvid)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
struct net_device *dev = mlxsw_sp_port->dev;
+ u16 vid, last_visited_vid, old_pvid;
enum mlxsw_reg_svfa_mt mt;
- u16 vid, vid_e;
int err;
/* In case this is invoked with BRIDGE_FLAGS_SELF and port is
@@ -377,15 +502,18 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
if (err) {
netdev_err(dev, "Failed to create FID=VID=%d mapping\n",
vid);
- return err;
+ goto err_port_vid_to_fid_set;
}
}
+ }
- /* Set FID mapping according to port's mode */
+ /* Set FID mapping according to port's mode */
+ for (vid = vid_begin; vid <= vid_end; vid++) {
err = mlxsw_sp_port_fid_map(mlxsw_sp_port, vid);
if (err) {
netdev_err(dev, "Failed to map FID=%d", vid);
- return err;
+ last_visited_vid = --vid;
+ goto err_port_fid_map;
}
}
@@ -393,83 +521,133 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
true, false);
if (err) {
netdev_err(dev, "Failed to configure flooding\n");
- return err;
+ goto err_port_flood_set;
}
- for (vid = vid_begin; vid <= vid_end;
- vid += MLXSW_REG_SPVM_REC_MAX_COUNT) {
- vid_e = min((u16) (vid + MLXSW_REG_SPVM_REC_MAX_COUNT - 1),
- vid_end);
-
- err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e, true,
- flag_untagged);
- if (err) {
- netdev_err(mlxsw_sp_port->dev, "Unable to add VIDs %d-%d\n",
- vid, vid_e);
- return err;
- }
+ err = __mlxsw_sp_port_vlans_set(mlxsw_sp_port, vid_begin, vid_end,
+ true, flag_untagged);
+ if (err) {
+ netdev_err(dev, "Unable to add VIDs %d-%d\n", vid_begin,
+ vid_end);
+ goto err_port_vlans_set;
}
- vid = vid_begin;
- if (flag_pvid && mlxsw_sp_port->pvid != vid) {
- err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, vid);
+ old_pvid = mlxsw_sp_port->pvid;
+ if (flag_pvid && old_pvid != vid_begin) {
+ err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, vid_begin);
if (err) {
- netdev_err(mlxsw_sp_port->dev, "Unable to add PVID %d\n",
- vid);
- return err;
+ netdev_err(dev, "Unable to add PVID %d\n", vid_begin);
+ goto err_port_pvid_set;
}
- mlxsw_sp_port->pvid = vid;
+ mlxsw_sp_port->pvid = vid_begin;
}
/* Changing activity bits only if HW operation succeded */
- for (vid = vid_begin; vid <= vid_end; vid++)
+ for (vid = vid_begin; vid <= vid_end; vid++) {
set_bit(vid, mlxsw_sp_port->active_vlans);
+ if (flag_untagged)
+ set_bit(vid, mlxsw_sp_port->untagged_vlans);
+ else
+ clear_bit(vid, mlxsw_sp_port->untagged_vlans);
+ }
- return mlxsw_sp_port_stp_state_set(mlxsw_sp_port,
- mlxsw_sp_port->stp_state);
+ /* STP state change must be done after we set active VLANs */
+ err = mlxsw_sp_port_stp_state_set(mlxsw_sp_port,
+ mlxsw_sp_port->stp_state);
+ if (err) {
+ netdev_err(dev, "Failed to set STP state\n");
+ goto err_port_stp_state_set;
+ }
+
+ return 0;
+
+err_port_vid_to_fid_set:
+ mlxsw_sp_fid_destroy(mlxsw_sp, vid);
+ return err;
+
+err_port_stp_state_set:
+ for (vid = vid_begin; vid <= vid_end; vid++)
+ clear_bit(vid, mlxsw_sp_port->active_vlans);
+ if (old_pvid != mlxsw_sp_port->pvid)
+ mlxsw_sp_port_pvid_set(mlxsw_sp_port, old_pvid);
+err_port_pvid_set:
+ __mlxsw_sp_port_vlans_set(mlxsw_sp_port, vid_begin, vid_end, false,
+ false);
+err_port_vlans_set:
+ __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid_begin, vid_end, false,
+ false);
+err_port_flood_set:
+ last_visited_vid = vid_end;
+err_port_fid_map:
+ for (vid = last_visited_vid; vid >= vid_begin; vid--)
+ mlxsw_sp_port_fid_unmap(mlxsw_sp_port, vid);
+ return err;
}
static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
const struct switchdev_obj_port_vlan *vlan,
struct switchdev_trans *trans)
{
- bool untagged_flag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
- bool pvid_flag = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+ bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+ bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
if (switchdev_trans_ph_prepare(trans))
return 0;
return __mlxsw_sp_port_vlans_add(mlxsw_sp_port,
vlan->vid_begin, vlan->vid_end,
- untagged_flag, pvid_flag);
+ flag_untagged, flag_pvid);
+}
+
+static enum mlxsw_reg_sfd_rec_policy mlxsw_sp_sfd_rec_policy(bool dynamic)
+{
+ return dynamic ? MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS :
+ MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY;
+}
+
+static enum mlxsw_reg_sfd_op mlxsw_sp_sfd_op(bool adding)
+{
+ return adding ? MLXSW_REG_SFD_OP_WRITE_EDIT :
+ MLXSW_REG_SFD_OP_WRITE_REMOVE;
}
-static int mlxsw_sp_port_fdb_op(struct mlxsw_sp_port *mlxsw_sp_port,
- const char *mac, u16 vid, bool adding,
- bool dynamic)
+static int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp *mlxsw_sp, u8 local_port,
+ const char *mac, u16 fid, bool adding,
+ bool dynamic)
{
- enum mlxsw_reg_sfd_rec_policy policy;
- enum mlxsw_reg_sfd_op op;
char *sfd_pl;
int err;
- if (!vid)
- vid = mlxsw_sp_port->pvid;
+ sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
+ if (!sfd_pl)
+ return -ENOMEM;
+
+ mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
+ mlxsw_reg_sfd_uc_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic),
+ mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP,
+ local_port);
+ err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
+ kfree(sfd_pl);
+
+ return err;
+}
+
+static int mlxsw_sp_port_fdb_uc_lag_op(struct mlxsw_sp *mlxsw_sp, u16 lag_id,
+ const char *mac, u16 fid, u16 lag_vid,
+ bool adding, bool dynamic)
+{
+ char *sfd_pl;
+ int err;
sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
if (!sfd_pl)
return -ENOMEM;
- policy = dynamic ? MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS :
- MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY;
- op = adding ? MLXSW_REG_SFD_OP_WRITE_EDIT :
- MLXSW_REG_SFD_OP_WRITE_REMOVE;
- mlxsw_reg_sfd_pack(sfd_pl, op, 0);
- mlxsw_reg_sfd_uc_pack(sfd_pl, 0, policy,
- mac, vid, MLXSW_REG_SFD_REC_ACTION_NOP,
- mlxsw_sp_port->local_port);
- err = mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(sfd),
- sfd_pl);
+ mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
+ mlxsw_reg_sfd_uc_lag_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic),
+ mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP,
+ lag_vid, lag_id);
+ err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
kfree(sfd_pl);
return err;
@@ -480,11 +658,162 @@ mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port,
const struct switchdev_obj_port_fdb *fdb,
struct switchdev_trans *trans)
{
+ u16 fid = mlxsw_sp_port_vid_to_fid_get(mlxsw_sp_port, fdb->vid);
+ u16 lag_vid = 0;
+
+ if (switchdev_trans_ph_prepare(trans))
+ return 0;
+
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
+ lag_vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
+ }
+
+ if (!mlxsw_sp_port->lagged)
+ return mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port->mlxsw_sp,
+ mlxsw_sp_port->local_port,
+ fdb->addr, fid, true, false);
+ else
+ return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp_port->mlxsw_sp,
+ mlxsw_sp_port->lag_id,
+ fdb->addr, fid, lag_vid,
+ true, false);
+}
+
+static int mlxsw_sp_port_mdb_op(struct mlxsw_sp *mlxsw_sp, const char *addr,
+ u16 fid, u16 mid, bool adding)
+{
+ char *sfd_pl;
+ int err;
+
+ sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
+ if (!sfd_pl)
+ return -ENOMEM;
+
+ mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
+ mlxsw_reg_sfd_mc_pack(sfd_pl, 0, addr, fid,
+ MLXSW_REG_SFD_REC_ACTION_NOP, mid);
+ err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
+ kfree(sfd_pl);
+ return err;
+}
+
+static int mlxsw_sp_port_smid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 mid,
+ bool add, bool clear_all_ports)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ char *smid_pl;
+ int err, i;
+
+ smid_pl = kmalloc(MLXSW_REG_SMID_LEN, GFP_KERNEL);
+ if (!smid_pl)
+ return -ENOMEM;
+
+ mlxsw_reg_smid_pack(smid_pl, mid, mlxsw_sp_port->local_port, add);
+ if (clear_all_ports) {
+ for (i = 1; i < MLXSW_PORT_MAX_PORTS; i++)
+ if (mlxsw_sp->ports[i])
+ mlxsw_reg_smid_port_mask_set(smid_pl, i, 1);
+ }
+ err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smid), smid_pl);
+ kfree(smid_pl);
+ return err;
+}
+
+static struct mlxsw_sp_mid *__mlxsw_sp_mc_get(struct mlxsw_sp *mlxsw_sp,
+ const unsigned char *addr,
+ u16 vid)
+{
+ struct mlxsw_sp_mid *mid;
+
+ list_for_each_entry(mid, &mlxsw_sp->br_mids.list, list) {
+ if (ether_addr_equal(mid->addr, addr) && mid->vid == vid)
+ return mid;
+ }
+ return NULL;
+}
+
+static struct mlxsw_sp_mid *__mlxsw_sp_mc_alloc(struct mlxsw_sp *mlxsw_sp,
+ const unsigned char *addr,
+ u16 vid)
+{
+ struct mlxsw_sp_mid *mid;
+ u16 mid_idx;
+
+ mid_idx = find_first_zero_bit(mlxsw_sp->br_mids.mapped,
+ MLXSW_SP_MID_MAX);
+ if (mid_idx == MLXSW_SP_MID_MAX)
+ return NULL;
+
+ mid = kzalloc(sizeof(*mid), GFP_KERNEL);
+ if (!mid)
+ return NULL;
+
+ set_bit(mid_idx, mlxsw_sp->br_mids.mapped);
+ ether_addr_copy(mid->addr, addr);
+ mid->vid = vid;
+ mid->mid = mid_idx;
+ mid->ref_count = 0;
+ list_add_tail(&mid->list, &mlxsw_sp->br_mids.list);
+
+ return mid;
+}
+
+static int __mlxsw_sp_mc_dec_ref(struct mlxsw_sp *mlxsw_sp,
+ struct mlxsw_sp_mid *mid)
+{
+ if (--mid->ref_count == 0) {
+ list_del(&mid->list);
+ clear_bit(mid->mid, mlxsw_sp->br_mids.mapped);
+ kfree(mid);
+ return 1;
+ }
+ return 0;
+}
+
+static int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port,
+ const struct switchdev_obj_port_mdb *mdb,
+ struct switchdev_trans *trans)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ struct net_device *dev = mlxsw_sp_port->dev;
+ struct mlxsw_sp_mid *mid;
+ u16 fid = mlxsw_sp_port_vid_to_fid_get(mlxsw_sp_port, mdb->vid);
+ int err = 0;
+
if (switchdev_trans_ph_prepare(trans))
return 0;
- return mlxsw_sp_port_fdb_op(mlxsw_sp_port, fdb->addr, fdb->vid,
- true, false);
+ mid = __mlxsw_sp_mc_get(mlxsw_sp, mdb->addr, mdb->vid);
+ if (!mid) {
+ mid = __mlxsw_sp_mc_alloc(mlxsw_sp, mdb->addr, mdb->vid);
+ if (!mid) {
+ netdev_err(dev, "Unable to allocate MC group\n");
+ return -ENOMEM;
+ }
+ }
+ mid->ref_count++;
+
+ err = mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, true,
+ mid->ref_count == 1);
+ if (err) {
+ netdev_err(dev, "Unable to set SMID\n");
+ goto err_out;
+ }
+
+ if (mid->ref_count == 1) {
+ err = mlxsw_sp_port_mdb_op(mlxsw_sp, mdb->addr, fid, mid->mid,
+ true);
+ if (err) {
+ netdev_err(dev, "Unable to set MC SFD\n");
+ goto err_out;
+ }
+ }
+
+ return 0;
+
+err_out:
+ __mlxsw_sp_mc_dec_ref(mlxsw_sp, mid);
+ return err;
}
static int mlxsw_sp_port_obj_add(struct net_device *dev,
@@ -494,8 +823,15 @@ static int mlxsw_sp_port_obj_add(struct net_device *dev,
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
int err = 0;
+ mlxsw_sp_port = mlxsw_sp_port_orig_get(obj->orig_dev, mlxsw_sp_port);
+ if (!mlxsw_sp_port)
+ return -EINVAL;
+
switch (obj->id) {
case SWITCHDEV_OBJ_ID_PORT_VLAN:
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port))
+ return 0;
+
err = mlxsw_sp_port_vlans_add(mlxsw_sp_port,
SWITCHDEV_OBJ_PORT_VLAN(obj),
trans);
@@ -505,6 +841,11 @@ static int mlxsw_sp_port_obj_add(struct net_device *dev,
SWITCHDEV_OBJ_PORT_FDB(obj),
trans);
break;
+ case SWITCHDEV_OBJ_ID_PORT_MDB:
+ err = mlxsw_sp_port_mdb_add(mlxsw_sp_port,
+ SWITCHDEV_OBJ_PORT_MDB(obj),
+ trans);
+ break;
default:
err = -EOPNOTSUPP;
break;
@@ -532,7 +873,7 @@ static int __mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
u16 vid_begin, u16 vid_end, bool init)
{
struct net_device *dev = mlxsw_sp_port->dev;
- u16 vid, vid_e;
+ u16 vid, pvid;
int err;
/* In case this is invoked with BRIDGE_FLAGS_SELF and port is
@@ -542,30 +883,23 @@ static int __mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
if (!init && !mlxsw_sp_port->bridged)
return mlxsw_sp_port_kill_vids(dev, vid_begin, vid_end);
- for (vid = vid_begin; vid <= vid_end;
- vid += MLXSW_REG_SPVM_REC_MAX_COUNT) {
- vid_e = min((u16) (vid + MLXSW_REG_SPVM_REC_MAX_COUNT - 1),
- vid_end);
- err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid_e, false,
- false);
- if (err) {
- netdev_err(mlxsw_sp_port->dev, "Unable to del VIDs %d-%d\n",
- vid, vid_e);
- return err;
- }
+ err = __mlxsw_sp_port_vlans_set(mlxsw_sp_port, vid_begin, vid_end,
+ false, false);
+ if (err) {
+ netdev_err(dev, "Unable to del VIDs %d-%d\n", vid_begin,
+ vid_end);
+ return err;
}
- if ((mlxsw_sp_port->pvid >= vid_begin) &&
- (mlxsw_sp_port->pvid <= vid_end)) {
+ pvid = mlxsw_sp_port->pvid;
+ if (pvid >= vid_begin && pvid <= vid_end && pvid != 1) {
/* Default VLAN is always 1 */
- mlxsw_sp_port->pvid = 1;
- err = mlxsw_sp_port_pvid_set(mlxsw_sp_port,
- mlxsw_sp_port->pvid);
+ err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, 1);
if (err) {
- netdev_err(mlxsw_sp_port->dev, "Unable to del PVID %d\n",
- vid);
+ netdev_err(dev, "Unable to del PVID %d\n", pvid);
return err;
}
+ mlxsw_sp_port->pvid = 1;
}
if (init)
@@ -606,8 +940,54 @@ static int
mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port,
const struct switchdev_obj_port_fdb *fdb)
{
- return mlxsw_sp_port_fdb_op(mlxsw_sp_port, fdb->addr, fdb->vid,
- false, false);
+ u16 fid = mlxsw_sp_port_vid_to_fid_get(mlxsw_sp_port, fdb->vid);
+ u16 lag_vid = 0;
+
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
+ lag_vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
+ }
+
+ if (!mlxsw_sp_port->lagged)
+ return mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port->mlxsw_sp,
+ mlxsw_sp_port->local_port,
+ fdb->addr, fid,
+ false, false);
+ else
+ return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp_port->mlxsw_sp,
+ mlxsw_sp_port->lag_id,
+ fdb->addr, fid, lag_vid,
+ false, false);
+}
+
+static int mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port,
+ const struct switchdev_obj_port_mdb *mdb)
+{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ struct net_device *dev = mlxsw_sp_port->dev;
+ struct mlxsw_sp_mid *mid;
+ u16 fid = mlxsw_sp_port_vid_to_fid_get(mlxsw_sp_port, mdb->vid);
+ u16 mid_idx;
+ int err = 0;
+
+ mid = __mlxsw_sp_mc_get(mlxsw_sp, mdb->addr, mdb->vid);
+ if (!mid) {
+ netdev_err(dev, "Unable to remove port from MC DB\n");
+ return -EINVAL;
+ }
+
+ err = mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, false, false);
+ if (err)
+ netdev_err(dev, "Unable to remove port from SMID\n");
+
+ mid_idx = mid->mid;
+ if (__mlxsw_sp_mc_dec_ref(mlxsw_sp, mid)) {
+ err = mlxsw_sp_port_mdb_op(mlxsw_sp, mdb->addr, fid, mid_idx,
+ false);
+ if (err)
+ netdev_err(dev, "Unable to remove MC SFD\n");
+ }
+
+ return err;
}
static int mlxsw_sp_port_obj_del(struct net_device *dev,
@@ -616,8 +996,15 @@ static int mlxsw_sp_port_obj_del(struct net_device *dev,
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
int err = 0;
+ mlxsw_sp_port = mlxsw_sp_port_orig_get(obj->orig_dev, mlxsw_sp_port);
+ if (!mlxsw_sp_port)
+ return -EINVAL;
+
switch (obj->id) {
case SWITCHDEV_OBJ_ID_PORT_VLAN:
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port))
+ return 0;
+
err = mlxsw_sp_port_vlans_del(mlxsw_sp_port,
SWITCHDEV_OBJ_PORT_VLAN(obj));
break;
@@ -625,6 +1012,9 @@ static int mlxsw_sp_port_obj_del(struct net_device *dev,
err = mlxsw_sp_port_fdb_static_del(mlxsw_sp_port,
SWITCHDEV_OBJ_PORT_FDB(obj));
break;
+ case SWITCHDEV_OBJ_ID_PORT_MDB:
+ err = mlxsw_sp_port_mdb_del(mlxsw_sp_port,
+ SWITCHDEV_OBJ_PORT_MDB(obj));
default:
err = -EOPNOTSUPP;
break;
@@ -633,14 +1023,31 @@ static int mlxsw_sp_port_obj_del(struct net_device *dev,
return err;
}
+static struct mlxsw_sp_port *mlxsw_sp_lag_rep_port(struct mlxsw_sp *mlxsw_sp,
+ u16 lag_id)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port;
+ int i;
+
+ for (i = 0; i < MLXSW_SP_PORT_PER_LAG_MAX; i++) {
+ mlxsw_sp_port = mlxsw_sp_port_lagged_get(mlxsw_sp, lag_id, i);
+ if (mlxsw_sp_port)
+ return mlxsw_sp_port;
+ }
+ return NULL;
+}
+
static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port,
struct switchdev_obj_port_fdb *fdb,
switchdev_obj_dump_cb_t *cb)
{
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+ u16 vport_vid = 0, vport_fid = 0;
char *sfd_pl;
char mac[ETH_ALEN];
- u16 vid;
+ u16 fid;
u8 local_port;
+ u16 lag_id;
u8 num_rec;
int stored_err = 0;
int i;
@@ -650,11 +1057,19 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port,
if (!sfd_pl)
return -ENOMEM;
+ mutex_lock(&mlxsw_sp_port->mlxsw_sp->fdb_lock);
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
+ u16 tmp;
+
+ tmp = mlxsw_sp_vport_vfid_get(mlxsw_sp_port);
+ vport_fid = mlxsw_sp_vfid_to_fid(tmp);
+ vport_vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
+ }
+
mlxsw_reg_sfd_pack(sfd_pl, MLXSW_REG_SFD_OP_QUERY_DUMP, 0);
do {
mlxsw_reg_sfd_num_rec_set(sfd_pl, MLXSW_REG_SFD_REC_MAX_COUNT);
- err = mlxsw_reg_query(mlxsw_sp_port->mlxsw_sp->core,
- MLXSW_REG(sfd), sfd_pl);
+ err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
if (err)
goto out;
@@ -669,21 +1084,46 @@ static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port,
for (i = 0; i < num_rec; i++) {
switch (mlxsw_reg_sfd_rec_type_get(sfd_pl, i)) {
case MLXSW_REG_SFD_REC_TYPE_UNICAST:
- mlxsw_reg_sfd_uc_unpack(sfd_pl, i, mac, &vid,
+ mlxsw_reg_sfd_uc_unpack(sfd_pl, i, mac, &fid,
&local_port);
if (local_port == mlxsw_sp_port->local_port) {
+ if (vport_fid && vport_fid != fid)
+ continue;
+ else if (vport_fid)
+ fdb->vid = vport_vid;
+ else
+ fdb->vid = fid;
+ ether_addr_copy(fdb->addr, mac);
+ fdb->ndm_state = NUD_REACHABLE;
+ err = cb(&fdb->obj);
+ if (err)
+ stored_err = err;
+ }
+ break;
+ case MLXSW_REG_SFD_REC_TYPE_UNICAST_LAG:
+ mlxsw_reg_sfd_uc_lag_unpack(sfd_pl, i,
+ mac, &fid, &lag_id);
+ if (mlxsw_sp_port ==
+ mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id)) {
+ if (vport_fid && vport_fid != fid)
+ continue;
+ else if (vport_fid)
+ fdb->vid = vport_vid;
+ else
+ fdb->vid = fid;
ether_addr_copy(fdb->addr, mac);
fdb->ndm_state = NUD_REACHABLE;
- fdb->vid = vid;
err = cb(&fdb->obj);
if (err)
stored_err = err;
}
+ break;
}
}
} while (num_rec == MLXSW_REG_SFD_REC_MAX_COUNT);
out:
+ mutex_unlock(&mlxsw_sp_port->mlxsw_sp->fdb_lock);
kfree(sfd_pl);
return stored_err ? stored_err : err;
}
@@ -695,10 +1135,19 @@ static int mlxsw_sp_port_vlan_dump(struct mlxsw_sp_port *mlxsw_sp_port,
u16 vid;
int err = 0;
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
+ vlan->flags = 0;
+ vlan->vid_begin = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
+ vlan->vid_end = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
+ return cb(&vlan->obj);
+ }
+
for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) {
vlan->flags = 0;
if (vid == mlxsw_sp_port->pvid)
vlan->flags |= BRIDGE_VLAN_INFO_PVID;
+ if (test_bit(vid, mlxsw_sp_port->untagged_vlans))
+ vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
vlan->vid_begin = vid;
vlan->vid_end = vid;
err = cb(&vlan->obj);
@@ -715,6 +1164,10 @@ static int mlxsw_sp_port_obj_dump(struct net_device *dev,
struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
int err = 0;
+ mlxsw_sp_port = mlxsw_sp_port_orig_get(obj->orig_dev, mlxsw_sp_port);
+ if (!mlxsw_sp_port)
+ return -EINVAL;
+
switch (obj->id) {
case SWITCHDEV_OBJ_ID_PORT_VLAN:
err = mlxsw_sp_port_vlan_dump(mlxsw_sp_port,
@@ -740,6 +1193,21 @@ static const struct switchdev_ops mlxsw_sp_port_switchdev_ops = {
.switchdev_port_obj_dump = mlxsw_sp_port_obj_dump,
};
+static void mlxsw_sp_fdb_call_notifiers(bool learning, bool learning_sync,
+ bool adding, char *mac, u16 vid,
+ struct net_device *dev)
+{
+ struct switchdev_notifier_fdb_info info;
+ unsigned long notifier_type;
+
+ if (learning && learning_sync) {
+ info.addr = mac;
+ info.vid = vid;
+ notifier_type = adding ? SWITCHDEV_FDB_ADD : SWITCHDEV_FDB_DEL;
+ call_switchdev_notifiers(notifier_type, dev, &info.info);
+ }
+}
+
static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp,
char *sfn_pl, int rec_index,
bool adding)
@@ -747,34 +1215,119 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_port *mlxsw_sp_port;
char mac[ETH_ALEN];
u8 local_port;
- u16 vid;
+ u16 vid, fid;
+ bool do_notification = true;
int err;
- mlxsw_reg_sfn_mac_unpack(sfn_pl, rec_index, mac, &vid, &local_port);
+ mlxsw_reg_sfn_mac_unpack(sfn_pl, rec_index, mac, &fid, &local_port);
mlxsw_sp_port = mlxsw_sp->ports[local_port];
if (!mlxsw_sp_port) {
dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect local port in FDB notification\n");
- return;
+ goto just_remove;
+ }
+
+ if (mlxsw_sp_fid_is_vfid(fid)) {
+ u16 vfid = mlxsw_sp_fid_to_vfid(fid);
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+
+ mlxsw_sp_vport = mlxsw_sp_port_vport_find_by_vfid(mlxsw_sp_port,
+ vfid);
+ if (!mlxsw_sp_vport) {
+ netdev_err(mlxsw_sp_port->dev, "Failed to find a matching vPort following FDB notification\n");
+ goto just_remove;
+ }
+ vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport);
+ /* Override the physical port with the vPort. */
+ mlxsw_sp_port = mlxsw_sp_vport;
+ } else {
+ vid = fid;
}
- err = mlxsw_sp_port_fdb_op(mlxsw_sp_port, mac, vid,
- adding && mlxsw_sp_port->learning, true);
+ adding = adding && mlxsw_sp_port->learning;
+
+do_fdb_op:
+ err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp, local_port, mac, fid,
+ adding, true);
if (err) {
if (net_ratelimit())
netdev_err(mlxsw_sp_port->dev, "Failed to set FDB entry\n");
return;
}
- if (mlxsw_sp_port->learning && mlxsw_sp_port->learning_sync) {
- struct switchdev_notifier_fdb_info info;
- unsigned long notifier_type;
+ if (!do_notification)
+ return;
+ mlxsw_sp_fdb_call_notifiers(mlxsw_sp_port->learning,
+ mlxsw_sp_port->learning_sync,
+ adding, mac, vid, mlxsw_sp_port->dev);
+ return;
+
+just_remove:
+ adding = false;
+ do_notification = false;
+ goto do_fdb_op;
+}
- info.addr = mac;
- info.vid = vid;
- notifier_type = adding ? SWITCHDEV_FDB_ADD : SWITCHDEV_FDB_DEL;
- call_switchdev_notifiers(notifier_type, mlxsw_sp_port->dev,
- &info.info);
+static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp,
+ char *sfn_pl, int rec_index,
+ bool adding)
+{
+ struct mlxsw_sp_port *mlxsw_sp_port;
+ char mac[ETH_ALEN];
+ u16 lag_vid = 0;
+ u16 lag_id;
+ u16 vid, fid;
+ bool do_notification = true;
+ int err;
+
+ mlxsw_reg_sfn_mac_lag_unpack(sfn_pl, rec_index, mac, &fid, &lag_id);
+ mlxsw_sp_port = mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id);
+ if (!mlxsw_sp_port) {
+ dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Cannot find port representor for LAG\n");
+ goto just_remove;
}
+
+ if (mlxsw_sp_fid_is_vfid(fid)) {
+ u16 vfid = mlxsw_sp_fid_to_vfid(fid);
+ struct mlxsw_sp_port *mlxsw_sp_vport;
+
+ mlxsw_sp_vport = mlxsw_sp_port_vport_find_by_vfid(mlxsw_sp_port,
+ vfid);
+ if (!mlxsw_sp_vport) {
+ netdev_err(mlxsw_sp_port->dev, "Failed to find a matching vPort following FDB notification\n");
+ goto just_remove;
+ }
+
+ vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport);
+ lag_vid = vid;
+ /* Override the physical port with the vPort. */
+ mlxsw_sp_port = mlxsw_sp_vport;
+ } else {
+ vid = fid;
+ }
+
+ adding = adding && mlxsw_sp_port->learning;
+
+do_fdb_op:
+ err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, lag_vid,
+ adding, true);
+ if (err) {
+ if (net_ratelimit())
+ netdev_err(mlxsw_sp_port->dev, "Failed to set FDB entry\n");
+ return;
+ }
+
+ if (!do_notification)
+ return;
+ mlxsw_sp_fdb_call_notifiers(mlxsw_sp_port->learning,
+ mlxsw_sp_port->learning_sync,
+ adding, mac, vid,
+ mlxsw_sp_lag_get(mlxsw_sp, lag_id)->dev);
+ return;
+
+just_remove:
+ adding = false;
+ do_notification = false;
+ goto do_fdb_op;
}
static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp,
@@ -789,6 +1342,14 @@ static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp,
mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl,
rec_index, false);
break;
+ case MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC_LAG:
+ mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl,
+ rec_index, true);
+ break;
+ case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC_LAG:
+ mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl,
+ rec_index, false);
+ break;
}
}
@@ -812,6 +1373,7 @@ static void mlxsw_sp_fdb_notify_work(struct work_struct *work)
mlxsw_sp = container_of(work, struct mlxsw_sp, fdb_notify.dw.work);
+ mutex_lock(&mlxsw_sp->fdb_lock);
do {
mlxsw_reg_sfn_pack(sfn_pl);
err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfn), sfn_pl);
@@ -824,6 +1386,7 @@ static void mlxsw_sp_fdb_notify_work(struct work_struct *work)
mlxsw_sp_fdb_notify_rec_process(mlxsw_sp, sfn_pl, i);
} while (num_rec);
+ mutex_unlock(&mlxsw_sp->fdb_lock);
kfree(sfn_pl);
mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp);
@@ -838,6 +1401,7 @@ static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp)
dev_err(mlxsw_sp->bus_info->dev, "Failed to set default ageing time\n");
return err;
}
+ mutex_init(&mlxsw_sp->fdb_lock);
INIT_DELAYED_WORK(&mlxsw_sp->fdb_notify.dw, mlxsw_sp_fdb_notify_work);
mlxsw_sp->fdb_notify.interval = MLXSW_SP_DEFAULT_LEARNING_INTERVAL;
mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp);
@@ -877,7 +1441,8 @@ int mlxsw_sp_port_vlan_init(struct mlxsw_sp_port *mlxsw_sp_port)
* with VID 1.
*/
mlxsw_sp_port->pvid = 1;
- err = __mlxsw_sp_port_vlans_del(mlxsw_sp_port, 0, VLAN_N_VID, true);
+ err = __mlxsw_sp_port_vlans_del(mlxsw_sp_port, 0, VLAN_N_VID - 1,
+ true);
if (err) {
netdev_err(dev, "Unable to init VLANs\n");
return err;
diff --git a/drivers/net/ethernet/microchip/encx24j600.c b/drivers/net/ethernet/microchip/encx24j600.c
index 2056b719c262..7df318346b05 100644
--- a/drivers/net/ethernet/microchip/encx24j600.c
+++ b/drivers/net/ethernet/microchip/encx24j600.c
@@ -600,22 +600,11 @@ static void encx24j600_set_rxfilter_mode(struct encx24j600_priv *priv)
static int encx24j600_hw_init(struct encx24j600_priv *priv)
{
- struct net_device *dev = priv->ndev;
int ret = 0;
- u16 eidled;
u16 macon2;
priv->hw_enabled = false;
- eidled = encx24j600_read_reg(priv, EIDLED);
- if (((eidled & DEVID_MASK) >> DEVID_SHIFT) != ENCX24J600_DEV_ID) {
- ret = -EINVAL;
- goto err_out;
- }
-
- netif_info(priv, drv, dev, "Silicon rev ID: 0x%02x\n",
- (eidled & REVID_MASK) >> REVID_SHIFT);
-
/* PHY Leds: link status,
* LEDA: Link State + collision events
* LEDB: Link State + transmit/receive events
@@ -655,7 +644,6 @@ static int encx24j600_hw_init(struct encx24j600_priv *priv)
if (netif_msg_hw(priv))
encx24j600_dump_config(priv, "Hw is initialized");
-err_out:
return ret;
}
@@ -1004,6 +992,7 @@ static int encx24j600_spi_probe(struct spi_device *spi)
struct net_device *ndev;
struct encx24j600_priv *priv;
+ u16 eidled;
ndev = alloc_etherdev(sizeof(struct encx24j600_priv));
@@ -1072,10 +1061,21 @@ static int encx24j600_spi_probe(struct spi_device *spi)
goto out_free;
}
+ eidled = encx24j600_read_reg(priv, EIDLED);
+ if (((eidled & DEVID_MASK) >> DEVID_SHIFT) != ENCX24J600_DEV_ID) {
+ ret = -EINVAL;
+ goto out_unregister;
+ }
+
+ netif_info(priv, probe, ndev, "Silicon rev ID: 0x%02x\n",
+ (eidled & REVID_MASK) >> REVID_SHIFT);
+
netif_info(priv, drv, priv->ndev, "MAC address %pM\n", ndev->dev_addr);
return ret;
+out_unregister:
+ unregister_netdev(priv->ndev);
out_free:
free_netdev(ndev);
diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
index 83651ac8ddb9..270c9eeb7ab6 100644
--- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
+++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
@@ -1488,7 +1488,6 @@ myri10ge_rx_done(struct myri10ge_slice_state *ss, int len, __wsum csum)
}
myri10ge_vlan_rx(mgp->dev, va, skb);
skb_record_rx_queue(skb, ss - &mgp->ss[0]);
- skb_mark_napi_id(skb, &ss->napi);
if (polling) {
int hlen;
@@ -1506,6 +1505,7 @@ myri10ge_rx_done(struct myri10ge_slice_state *ss, int len, __wsum csum)
skb->data_len -= hlen;
skb->tail += hlen;
skb->protocol = eth_type_trans(skb, dev);
+ skb_mark_napi_id(skb, &ss->napi);
netif_receive_skb(skb);
}
else
@@ -3814,7 +3814,6 @@ static int myri10ge_alloc_slices(struct myri10ge_priv *mgp)
ss->dev = mgp->dev;
netif_napi_add(ss->dev, &ss->napi, myri10ge_poll,
myri10ge_napi_weight);
- napi_hash_add(&ss->napi);
}
return 0;
abort:
diff --git a/drivers/net/ethernet/natsemi/natsemi.c b/drivers/net/ethernet/natsemi/natsemi.c
index b83f7c0fcf99..122c2ee3dfe2 100644
--- a/drivers/net/ethernet/natsemi/natsemi.c
+++ b/drivers/net/ethernet/natsemi/natsemi.c
@@ -1937,6 +1937,12 @@ static void refill_rx(struct net_device *dev)
break; /* Better luck next round. */
np->rx_dma[entry] = pci_map_single(np->pci_dev,
skb->data, buflen, PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(np->pci_dev,
+ np->rx_dma[entry])) {
+ dev_kfree_skb_any(skb);
+ np->rx_skbuff[entry] = NULL;
+ break; /* Better luck next round. */
+ }
np->rx_ring[entry].addr = cpu_to_le32(np->rx_dma[entry]);
}
np->rx_ring[entry].cmd_status = cpu_to_le32(np->rx_buf_sz);
@@ -2093,6 +2099,12 @@ static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev)
np->tx_skbuff[entry] = skb;
np->tx_dma[entry] = pci_map_single(np->pci_dev,
skb->data,skb->len, PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(np->pci_dev, np->tx_dma[entry])) {
+ np->tx_skbuff[entry] = NULL;
+ dev_kfree_skb_irq(skb);
+ dev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+ }
np->tx_ring[entry].addr = cpu_to_le32(np->tx_dma[entry]);
diff --git a/drivers/net/ethernet/netronome/Kconfig b/drivers/net/ethernet/netronome/Kconfig
new file mode 100644
index 000000000000..9508ad782c30
--- /dev/null
+++ b/drivers/net/ethernet/netronome/Kconfig
@@ -0,0 +1,36 @@
+#
+# Netronome device configuration
+#
+
+config NET_VENDOR_NETRONOME
+ bool "Netronome(R) devices"
+ default y
+ ---help---
+ If you have a Netronome(R) network (Ethernet) card or device, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Netronome(R) cards. If you say Y, you will be
+ asked for your specific card in the following questions.
+
+if NET_VENDOR_NETRONOME
+
+config NFP_NETVF
+ tristate "Netronome(R) NFP4000/NFP6000 VF NIC driver"
+ depends on PCI && PCI_MSI
+ depends on VXLAN || VXLAN=n
+ ---help---
+ This driver supports SR-IOV virtual functions of
+ the Netronome(R) NFP4000/NFP6000 cards working as
+ a advanced Ethernet NIC.
+
+config NFP_NET_DEBUG
+ bool "Debug support for Netronome(R) NFP3200/NFP6000 NIC drivers"
+ depends on NFP_NET || NFP_NETVF
+ ---help---
+ Enable extra sanity checks and debugfs support in
+ Netronome(R) NFP3200/NFP6000 NIC PF and VF drivers.
+ Note: selecting this option may adversely impact
+ performance.
+
+endif
diff --git a/drivers/net/ethernet/netronome/Makefile b/drivers/net/ethernet/netronome/Makefile
new file mode 100644
index 000000000000..dcb7b383f634
--- /dev/null
+++ b/drivers/net/ethernet/netronome/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Netronome network device drivers
+#
+
+obj-$(CONFIG_NFP_NETVF) += nfp/
diff --git a/drivers/net/ethernet/netronome/nfp/Makefile b/drivers/net/ethernet/netronome/nfp/Makefile
new file mode 100644
index 000000000000..68178819ff12
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/Makefile
@@ -0,0 +1,8 @@
+obj-$(CONFIG_NFP_NETVF) += nfp_netvf.o
+
+nfp_netvf-objs := \
+ nfp_net_common.o \
+ nfp_net_ethtool.o \
+ nfp_netvf_main.o
+
+nfp_netvf-$(CONFIG_NFP_NET_DEBUG) += nfp_net_debugfs.o
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h
new file mode 100644
index 000000000000..ab264e1bccd0
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h
@@ -0,0 +1,748 @@
+/*
+ * Copyright (C) 2015 Netronome Systems, Inc.
+ *
+ * This software is dual licensed under the GNU General License Version 2,
+ * June 1991 as shown in the file COPYING in the top-level directory of this
+ * source tree or the BSD 2-Clause License provided below. You have the
+ * option to license this software under the complete terms of either license.
+ *
+ * The BSD 2-Clause 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, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 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.
+ */
+
+/*
+ * nfp_net.h
+ * Declarations for Netronome network device driver.
+ * Authors: Jakub Kicinski <jakub.kicinski@netronome.com>
+ * Jason McMullan <jason.mcmullan@netronome.com>
+ * Rolf Neugebauer <rolf.neugebauer@netronome.com>
+ */
+
+#ifndef _NFP_NET_H_
+#define _NFP_NET_H_
+
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <asm-generic/io-64-nonatomic-hi-lo.h>
+
+#include "nfp_net_ctrl.h"
+
+#define nn_err(nn, fmt, args...) netdev_err((nn)->netdev, fmt, ## args)
+#define nn_warn(nn, fmt, args...) netdev_warn((nn)->netdev, fmt, ## args)
+#define nn_info(nn, fmt, args...) netdev_info((nn)->netdev, fmt, ## args)
+#define nn_dbg(nn, fmt, args...) netdev_dbg((nn)->netdev, fmt, ## args)
+#define nn_warn_ratelimit(nn, fmt, args...) \
+ do { \
+ if (unlikely(net_ratelimit())) \
+ netdev_warn((nn)->netdev, fmt, ## args); \
+ } while (0)
+
+/* Max time to wait for NFP to respond on updates (in ms) */
+#define NFP_NET_POLL_TIMEOUT 5000
+
+/* Bar allocation */
+#define NFP_NET_CRTL_BAR 0
+#define NFP_NET_Q0_BAR 2
+#define NFP_NET_Q1_BAR 4 /* OBSOLETE */
+
+/* Max bits in DMA address */
+#define NFP_NET_MAX_DMA_BITS 40
+
+/* Default size for MTU and freelist buffer sizes */
+#define NFP_NET_DEFAULT_MTU 1500
+#define NFP_NET_DEFAULT_RX_BUFSZ 2048
+
+/* Maximum number of bytes prepended to a packet */
+#define NFP_NET_MAX_PREPEND 64
+
+/* Interrupt definitions */
+#define NFP_NET_NON_Q_VECTORS 2
+#define NFP_NET_IRQ_LSC_IDX 0
+#define NFP_NET_IRQ_EXN_IDX 1
+
+/* Queue/Ring definitions */
+#define NFP_NET_MAX_TX_RINGS 64 /* Max. # of Tx rings per device */
+#define NFP_NET_MAX_RX_RINGS 64 /* Max. # of Rx rings per device */
+
+#define NFP_NET_MIN_TX_DESCS 256 /* Min. # of Tx descs per ring */
+#define NFP_NET_MIN_RX_DESCS 256 /* Min. # of Rx descs per ring */
+#define NFP_NET_MAX_TX_DESCS (256 * 1024) /* Max. # of Tx descs per ring */
+#define NFP_NET_MAX_RX_DESCS (256 * 1024) /* Max. # of Rx descs per ring */
+
+#define NFP_NET_TX_DESCS_DEFAULT 4096 /* Default # of Tx descs per ring */
+#define NFP_NET_RX_DESCS_DEFAULT 4096 /* Default # of Rx descs per ring */
+
+#define NFP_NET_FL_BATCH 16 /* Add freelist in this Batch size */
+
+/* Offload definitions */
+#define NFP_NET_N_VXLAN_PORTS (NFP_NET_CFG_VXLAN_SZ / sizeof(__be16))
+
+/* Forward declarations */
+struct nfp_net;
+struct nfp_net_r_vector;
+
+/* Convenience macro for writing dma address into RX/TX descriptors */
+#define nfp_desc_set_dma_addr(desc, dma_addr) \
+ do { \
+ __typeof(desc) __d = (desc); \
+ dma_addr_t __addr = (dma_addr); \
+ \
+ __d->dma_addr_lo = cpu_to_le32(lower_32_bits(__addr)); \
+ __d->dma_addr_hi = upper_32_bits(__addr) & 0xff; \
+ } while (0)
+
+/* TX descriptor format */
+
+#define PCIE_DESC_TX_EOP BIT(7)
+#define PCIE_DESC_TX_OFFSET_MASK GENMASK(6, 0)
+#define PCIE_DESC_TX_MSS_MASK GENMASK(13, 0)
+
+/* Flags in the host TX descriptor */
+#define PCIE_DESC_TX_CSUM BIT(7)
+#define PCIE_DESC_TX_IP4_CSUM BIT(6)
+#define PCIE_DESC_TX_TCP_CSUM BIT(5)
+#define PCIE_DESC_TX_UDP_CSUM BIT(4)
+#define PCIE_DESC_TX_VLAN BIT(3)
+#define PCIE_DESC_TX_LSO BIT(2)
+#define PCIE_DESC_TX_ENCAP BIT(1)
+#define PCIE_DESC_TX_O_IP4_CSUM BIT(0)
+
+struct nfp_net_tx_desc {
+ union {
+ struct {
+ u8 dma_addr_hi; /* High bits of host buf address */
+ __le16 dma_len; /* Length to DMA for this desc */
+ u8 offset_eop; /* Offset in buf where pkt starts +
+ * highest bit is eop flag.
+ */
+ __le32 dma_addr_lo; /* Low 32bit of host buf addr */
+
+ __le16 mss; /* MSS to be used for LSO */
+ u8 l4_offset; /* LSO, where the L4 data starts */
+ u8 flags; /* TX Flags, see @PCIE_DESC_TX_* */
+
+ __le16 vlan; /* VLAN tag to add if indicated */
+ __le16 data_len; /* Length of frame + meta data */
+ } __packed;
+ __le32 vals[4];
+ };
+};
+
+/**
+ * struct nfp_net_tx_buf - software TX buffer descriptor
+ * @skb: sk_buff associated with this buffer
+ * @dma_addr: DMA mapping address of the buffer
+ * @fidx: Fragment index (-1 for the head and [0..nr_frags-1] for frags)
+ * @pkt_cnt: Number of packets to be produced out of the skb associated
+ * with this buffer (valid only on the head's buffer).
+ * Will be 1 for all non-TSO packets.
+ * @real_len: Number of bytes which to be produced out of the skb (valid only
+ * on the head's buffer). Equal to skb->len for non-TSO packets.
+ */
+struct nfp_net_tx_buf {
+ struct sk_buff *skb;
+ dma_addr_t dma_addr;
+ short int fidx;
+ u16 pkt_cnt;
+ u32 real_len;
+};
+
+/**
+ * struct nfp_net_tx_ring - TX ring structure
+ * @r_vec: Back pointer to ring vector structure
+ * @idx: Ring index from Linux's perspective
+ * @qcidx: Queue Controller Peripheral (QCP) queue index for the TX queue
+ * @qcp_q: Pointer to base of the QCP TX queue
+ * @cnt: Size of the queue in number of descriptors
+ * @wr_p: TX ring write pointer (free running)
+ * @rd_p: TX ring read pointer (free running)
+ * @qcp_rd_p: Local copy of QCP TX queue read pointer
+ * @wr_ptr_add: Accumulated number of buffers to add to QCP write pointer
+ * (used for .xmit_more delayed kick)
+ * @txbufs: Array of transmitted TX buffers, to free on transmit
+ * @txds: Virtual address of TX ring in host memory
+ * @dma: DMA address of the TX ring
+ * @size: Size, in bytes, of the TX ring (needed to free)
+ */
+struct nfp_net_tx_ring {
+ struct nfp_net_r_vector *r_vec;
+
+ u32 idx;
+ int qcidx;
+ u8 __iomem *qcp_q;
+
+ u32 cnt;
+ u32 wr_p;
+ u32 rd_p;
+ u32 qcp_rd_p;
+
+ u32 wr_ptr_add;
+
+ struct nfp_net_tx_buf *txbufs;
+ struct nfp_net_tx_desc *txds;
+
+ dma_addr_t dma;
+ unsigned int size;
+} ____cacheline_aligned;
+
+/* RX and freelist descriptor format */
+
+#define PCIE_DESC_RX_DD BIT(7)
+#define PCIE_DESC_RX_META_LEN_MASK GENMASK(6, 0)
+
+/* Flags in the RX descriptor */
+#define PCIE_DESC_RX_RSS cpu_to_le16(BIT(15))
+#define PCIE_DESC_RX_I_IP4_CSUM cpu_to_le16(BIT(14))
+#define PCIE_DESC_RX_I_IP4_CSUM_OK cpu_to_le16(BIT(13))
+#define PCIE_DESC_RX_I_TCP_CSUM cpu_to_le16(BIT(12))
+#define PCIE_DESC_RX_I_TCP_CSUM_OK cpu_to_le16(BIT(11))
+#define PCIE_DESC_RX_I_UDP_CSUM cpu_to_le16(BIT(10))
+#define PCIE_DESC_RX_I_UDP_CSUM_OK cpu_to_le16(BIT(9))
+#define PCIE_DESC_RX_SPARE cpu_to_le16(BIT(8))
+#define PCIE_DESC_RX_EOP cpu_to_le16(BIT(7))
+#define PCIE_DESC_RX_IP4_CSUM cpu_to_le16(BIT(6))
+#define PCIE_DESC_RX_IP4_CSUM_OK cpu_to_le16(BIT(5))
+#define PCIE_DESC_RX_TCP_CSUM cpu_to_le16(BIT(4))
+#define PCIE_DESC_RX_TCP_CSUM_OK cpu_to_le16(BIT(3))
+#define PCIE_DESC_RX_UDP_CSUM cpu_to_le16(BIT(2))
+#define PCIE_DESC_RX_UDP_CSUM_OK cpu_to_le16(BIT(1))
+#define PCIE_DESC_RX_VLAN cpu_to_le16(BIT(0))
+
+#define PCIE_DESC_RX_CSUM_ALL (PCIE_DESC_RX_IP4_CSUM | \
+ PCIE_DESC_RX_TCP_CSUM | \
+ PCIE_DESC_RX_UDP_CSUM | \
+ PCIE_DESC_RX_I_IP4_CSUM | \
+ PCIE_DESC_RX_I_TCP_CSUM | \
+ PCIE_DESC_RX_I_UDP_CSUM)
+#define PCIE_DESC_RX_CSUM_OK_SHIFT 1
+#define __PCIE_DESC_RX_CSUM_ALL le16_to_cpu(PCIE_DESC_RX_CSUM_ALL)
+#define __PCIE_DESC_RX_CSUM_ALL_OK (__PCIE_DESC_RX_CSUM_ALL >> \
+ PCIE_DESC_RX_CSUM_OK_SHIFT)
+
+struct nfp_net_rx_desc {
+ union {
+ struct {
+ u8 dma_addr_hi; /* High bits of the buf address */
+ __le16 reserved; /* Must be zero */
+ u8 meta_len_dd; /* Must be zero */
+
+ __le32 dma_addr_lo; /* Low bits of the buffer address */
+ } __packed fld;
+
+ struct {
+ __le16 data_len; /* Length of the frame + meta data */
+ u8 reserved;
+ u8 meta_len_dd; /* Length of meta data prepended +
+ * descriptor done flag.
+ */
+
+ __le16 flags; /* RX flags. See @PCIE_DESC_RX_* */
+ __le16 vlan; /* VLAN if stripped */
+ } __packed rxd;
+
+ __le32 vals[2];
+ };
+};
+
+struct nfp_net_rx_hash {
+ __be32 hash_type;
+ __be32 hash;
+};
+
+/**
+ * struct nfp_net_rx_buf - software RX buffer descriptor
+ * @skb: sk_buff associated with this buffer
+ * @dma_addr: DMA mapping address of the buffer
+ */
+struct nfp_net_rx_buf {
+ struct sk_buff *skb;
+ dma_addr_t dma_addr;
+};
+
+/**
+ * struct nfp_net_rx_ring - RX ring structure
+ * @r_vec: Back pointer to ring vector structure
+ * @cnt: Size of the queue in number of descriptors
+ * @wr_p: FL/RX ring write pointer (free running)
+ * @rd_p: FL/RX ring read pointer (free running)
+ * @idx: Ring index from Linux's perspective
+ * @fl_qcidx: Queue Controller Peripheral (QCP) queue index for the freelist
+ * @rx_qcidx: Queue Controller Peripheral (QCP) queue index for the RX queue
+ * @qcp_fl: Pointer to base of the QCP freelist queue
+ * @qcp_rx: Pointer to base of the QCP RX queue
+ * @wr_ptr_add: Accumulated number of buffers to add to QCP write pointer
+ * (used for free list batching)
+ * @rxbufs: Array of transmitted FL/RX buffers
+ * @rxds: Virtual address of FL/RX ring in host memory
+ * @dma: DMA address of the FL/RX ring
+ * @size: Size, in bytes, of the FL/RX ring (needed to free)
+ */
+struct nfp_net_rx_ring {
+ struct nfp_net_r_vector *r_vec;
+
+ u32 cnt;
+ u32 wr_p;
+ u32 rd_p;
+
+ u16 idx;
+ u16 wr_ptr_add;
+
+ int fl_qcidx;
+ int rx_qcidx;
+ u8 __iomem *qcp_fl;
+ u8 __iomem *qcp_rx;
+
+ struct nfp_net_rx_buf *rxbufs;
+ struct nfp_net_rx_desc *rxds;
+
+ dma_addr_t dma;
+ unsigned int size;
+} ____cacheline_aligned;
+
+/**
+ * struct nfp_net_r_vector - Per ring interrupt vector configuration
+ * @nfp_net: Backpointer to nfp_net structure
+ * @napi: NAPI structure for this ring vec
+ * @tx_ring: Pointer to TX ring
+ * @rx_ring: Pointer to RX ring
+ * @irq_idx: Index into MSI-X table
+ * @rx_sync: Seqlock for atomic updates of RX stats
+ * @rx_pkts: Number of received packets
+ * @rx_bytes: Number of received bytes
+ * @rx_drops: Number of packets dropped on RX due to lack of resources
+ * @hw_csum_rx_ok: Counter of packets where the HW checksum was OK
+ * @hw_csum_rx_inner_ok: Counter of packets where the inner HW checksum was OK
+ * @hw_csum_rx_error: Counter of packets with bad checksums
+ * @tx_sync: Seqlock for atomic updates of TX stats
+ * @tx_pkts: Number of Transmitted packets
+ * @tx_bytes: Number of Transmitted bytes
+ * @hw_csum_tx: Counter of packets with TX checksum offload requested
+ * @hw_csum_tx_inner: Counter of inner TX checksum offload requests
+ * @tx_gather: Counter of packets with Gather DMA
+ * @tx_lso: Counter of LSO packets sent
+ * @tx_errors: How many TX errors were encountered
+ * @tx_busy: How often was TX busy (no space)?
+ * @handler: Interrupt handler for this ring vector
+ * @name: Name of the interrupt vector
+ * @affinity_mask: SMP affinity mask for this vector
+ *
+ * This structure ties RX and TX rings to interrupt vectors and a NAPI
+ * context. This currently only supports one RX and TX ring per
+ * interrupt vector but might be extended in the future to allow
+ * association of multiple rings per vector.
+ */
+struct nfp_net_r_vector {
+ struct nfp_net *nfp_net;
+ struct napi_struct napi;
+
+ struct nfp_net_tx_ring *tx_ring;
+ struct nfp_net_rx_ring *rx_ring;
+
+ int irq_idx;
+
+ struct u64_stats_sync rx_sync;
+ u64 rx_pkts;
+ u64 rx_bytes;
+ u64 rx_drops;
+ u64 hw_csum_rx_ok;
+ u64 hw_csum_rx_inner_ok;
+ u64 hw_csum_rx_error;
+
+ struct u64_stats_sync tx_sync;
+ u64 tx_pkts;
+ u64 tx_bytes;
+ u64 hw_csum_tx;
+ u64 hw_csum_tx_inner;
+ u64 tx_gather;
+ u64 tx_lso;
+ u64 tx_errors;
+ u64 tx_busy;
+
+ irq_handler_t handler;
+ char name[IFNAMSIZ + 8];
+ cpumask_t affinity_mask;
+} ____cacheline_aligned;
+
+/* Firmware version as it is written in the 32bit value in the BAR */
+struct nfp_net_fw_version {
+ u8 minor;
+ u8 major;
+ u8 class;
+ u8 resv;
+} __packed;
+
+static inline bool nfp_net_fw_ver_eq(struct nfp_net_fw_version *fw_ver,
+ u8 resv, u8 class, u8 major, u8 minor)
+{
+ return fw_ver->resv == resv &&
+ fw_ver->class == class &&
+ fw_ver->major == major &&
+ fw_ver->minor == minor;
+}
+
+/**
+ * struct nfp_net - NFP network device structure
+ * @pdev: Backpointer to PCI device
+ * @netdev: Backpointer to net_device structure
+ * @nfp_fallback: Is the driver used in fallback mode?
+ * @is_vf: Is the driver attached to a VF?
+ * @is_nfp3200: Is the driver for a NFP-3200 card?
+ * @fw_loaded: Is the firmware loaded?
+ * @ctrl: Local copy of the control register/word.
+ * @fl_bufsz: Currently configured size of the freelist buffers
+ * @rx_offset: Offset in the RX buffers where packet data starts
+ * @cpp: Pointer to the CPP handle
+ * @nfp_dev_cpp: Pointer to the NFP Device handle
+ * @ctrl_area: Pointer to the CPP area for the control BAR
+ * @tx_area: Pointer to the CPP area for the TX queues
+ * @rx_area: Pointer to the CPP area for the FL/RX queues
+ * @fw_ver: Firmware version
+ * @cap: Capabilities advertised by the Firmware
+ * @max_mtu: Maximum support MTU advertised by the Firmware
+ * @rss_cfg: RSS configuration
+ * @rss_key: RSS secret key
+ * @rss_itbl: RSS indirection table
+ * @max_tx_rings: Maximum number of TX rings supported by the Firmware
+ * @max_rx_rings: Maximum number of RX rings supported by the Firmware
+ * @num_tx_rings: Currently configured number of TX rings
+ * @num_rx_rings: Currently configured number of RX rings
+ * @txd_cnt: Size of the TX ring in number of descriptors
+ * @rxd_cnt: Size of the RX ring in number of descriptors
+ * @tx_rings: Array of pre-allocated TX ring structures
+ * @rx_rings: Array of pre-allocated RX ring structures
+ * @num_irqs: Number of allocated interrupt vectors
+ * @num_r_vecs: Number of used ring vectors
+ * @r_vecs: Pre-allocated array of ring vectors
+ * @irq_entries: Pre-allocated array of MSI-X entries
+ * @lsc_handler: Handler for Link State Change interrupt
+ * @lsc_name: Name for Link State Change interrupt
+ * @exn_handler: Handler for Exception interrupt
+ * @exn_name: Name for Exception interrupt
+ * @shared_handler: Handler for shared interrupts
+ * @shared_name: Name for shared interrupt
+ * @me_freq_mhz: ME clock_freq (MHz)
+ * @reconfig_lock: Protects HW reconfiguration request regs/machinery
+ * @link_up: Is the link up?
+ * @link_status_lock: Protects @link_up and ensures atomicity with BAR reading
+ * @rx_coalesce_usecs: RX interrupt moderation usecs delay parameter
+ * @rx_coalesce_max_frames: RX interrupt moderation frame count parameter
+ * @tx_coalesce_usecs: TX interrupt moderation usecs delay parameter
+ * @tx_coalesce_max_frames: TX interrupt moderation frame count parameter
+ * @vxlan_ports: VXLAN ports for RX inner csum offload communicated to HW
+ * @vxlan_usecnt: IPv4/IPv6 VXLAN port use counts
+ * @qcp_cfg: Pointer to QCP queue used for configuration notification
+ * @ctrl_bar: Pointer to mapped control BAR
+ * @tx_bar: Pointer to mapped TX queues
+ * @rx_bar: Pointer to mapped FL/RX queues
+ * @debugfs_dir: Device directory in debugfs
+ */
+struct nfp_net {
+ struct pci_dev *pdev;
+ struct net_device *netdev;
+
+ unsigned nfp_fallback:1;
+ unsigned is_vf:1;
+ unsigned is_nfp3200:1;
+ unsigned fw_loaded:1;
+
+ u32 ctrl;
+ u32 fl_bufsz;
+
+ u32 rx_offset;
+
+#ifdef CONFIG_PCI_IOV
+ unsigned int num_vfs;
+ struct vf_data_storage *vfinfo;
+ int vf_rate_link_speed;
+#endif
+
+ struct nfp_cpp *cpp;
+ struct platform_device *nfp_dev_cpp;
+ struct nfp_cpp_area *ctrl_area;
+ struct nfp_cpp_area *tx_area;
+ struct nfp_cpp_area *rx_area;
+
+ struct nfp_net_fw_version fw_ver;
+ u32 cap;
+ u32 max_mtu;
+
+ u32 rss_cfg;
+ u8 rss_key[NFP_NET_CFG_RSS_KEY_SZ];
+ u8 rss_itbl[NFP_NET_CFG_RSS_ITBL_SZ];
+
+ int max_tx_rings;
+ int max_rx_rings;
+
+ int num_tx_rings;
+ int num_rx_rings;
+
+ int stride_tx;
+ int stride_rx;
+
+ int txd_cnt;
+ int rxd_cnt;
+
+ struct nfp_net_tx_ring tx_rings[NFP_NET_MAX_TX_RINGS];
+ struct nfp_net_rx_ring rx_rings[NFP_NET_MAX_RX_RINGS];
+
+ u8 num_irqs;
+ u8 num_r_vecs;
+ struct nfp_net_r_vector r_vecs[NFP_NET_MAX_TX_RINGS];
+ struct msix_entry irq_entries[NFP_NET_NON_Q_VECTORS +
+ NFP_NET_MAX_TX_RINGS];
+
+ irq_handler_t lsc_handler;
+ char lsc_name[IFNAMSIZ + 8];
+
+ irq_handler_t exn_handler;
+ char exn_name[IFNAMSIZ + 8];
+
+ irq_handler_t shared_handler;
+ char shared_name[IFNAMSIZ + 8];
+
+ u32 me_freq_mhz;
+
+ bool link_up;
+ spinlock_t link_status_lock;
+
+ spinlock_t reconfig_lock;
+
+ u32 rx_coalesce_usecs;
+ u32 rx_coalesce_max_frames;
+ u32 tx_coalesce_usecs;
+ u32 tx_coalesce_max_frames;
+
+ __be16 vxlan_ports[NFP_NET_N_VXLAN_PORTS];
+ u8 vxlan_usecnt[NFP_NET_N_VXLAN_PORTS];
+
+ u8 __iomem *qcp_cfg;
+
+ u8 __iomem *ctrl_bar;
+ u8 __iomem *q_bar;
+ u8 __iomem *tx_bar;
+ u8 __iomem *rx_bar;
+
+ struct dentry *debugfs_dir;
+};
+
+/* Functions to read/write from/to a BAR
+ * Performs any endian conversion necessary.
+ */
+static inline void nn_writeb(struct nfp_net *nn, int off, u8 val)
+{
+ writeb(val, nn->ctrl_bar + off);
+}
+
+/* NFP-3200 can't handle 16-bit accesses too well - hence no readw/writew */
+
+static inline u32 nn_readl(struct nfp_net *nn, int off)
+{
+ return readl(nn->ctrl_bar + off);
+}
+
+static inline void nn_writel(struct nfp_net *nn, int off, u32 val)
+{
+ writel(val, nn->ctrl_bar + off);
+}
+
+static inline u64 nn_readq(struct nfp_net *nn, int off)
+{
+ return readq(nn->ctrl_bar + off);
+}
+
+static inline void nn_writeq(struct nfp_net *nn, int off, u64 val)
+{
+ writeq(val, nn->ctrl_bar + off);
+}
+
+/* Flush posted PCI writes by reading something without side effects */
+static inline void nn_pci_flush(struct nfp_net *nn)
+{
+ nn_readl(nn, NFP_NET_CFG_VERSION);
+}
+
+/* Queue Controller Peripheral access functions and definitions.
+ *
+ * Some of the BARs of the NFP are mapped to portions of the Queue
+ * Controller Peripheral (QCP) address space on the NFP. A QCP queue
+ * has a read and a write pointer (as well as a size and flags,
+ * indicating overflow etc). The QCP offers a number of different
+ * operation on queue pointers, but here we only offer function to
+ * either add to a pointer or to read the pointer value.
+ */
+#define NFP_QCP_QUEUE_ADDR_SZ 0x800
+#define NFP_QCP_QUEUE_OFF(_x) ((_x) * NFP_QCP_QUEUE_ADDR_SZ)
+#define NFP_QCP_QUEUE_ADD_RPTR 0x0000
+#define NFP_QCP_QUEUE_ADD_WPTR 0x0004
+#define NFP_QCP_QUEUE_STS_LO 0x0008
+#define NFP_QCP_QUEUE_STS_LO_READPTR_mask 0x3ffff
+#define NFP_QCP_QUEUE_STS_HI 0x000c
+#define NFP_QCP_QUEUE_STS_HI_WRITEPTR_mask 0x3ffff
+
+/* The offset of a QCP queues in the PCIe Target (same on NFP3200 and NFP6000 */
+#define NFP_PCIE_QUEUE(_q) (0x80000 + (NFP_QCP_QUEUE_ADDR_SZ * ((_q) & 0xff)))
+
+/* nfp_qcp_ptr - Read or Write Pointer of a queue */
+enum nfp_qcp_ptr {
+ NFP_QCP_READ_PTR = 0,
+ NFP_QCP_WRITE_PTR
+};
+
+/* There appear to be an *undocumented* upper limit on the value which
+ * one can add to a queue and that value is either 0x3f or 0x7f. We
+ * go with 0x3f as a conservative measure.
+ */
+#define NFP_QCP_MAX_ADD 0x3f
+
+static inline void _nfp_qcp_ptr_add(u8 __iomem *q,
+ enum nfp_qcp_ptr ptr, u32 val)
+{
+ u32 off;
+
+ if (ptr == NFP_QCP_READ_PTR)
+ off = NFP_QCP_QUEUE_ADD_RPTR;
+ else
+ off = NFP_QCP_QUEUE_ADD_WPTR;
+
+ while (val > NFP_QCP_MAX_ADD) {
+ writel(NFP_QCP_MAX_ADD, q + off);
+ val -= NFP_QCP_MAX_ADD;
+ }
+
+ writel(val, q + off);
+}
+
+/**
+ * nfp_qcp_rd_ptr_add() - Add the value to the read pointer of a queue
+ *
+ * @q: Base address for queue structure
+ * @val: Value to add to the queue pointer
+ *
+ * If @val is greater than @NFP_QCP_MAX_ADD multiple writes are performed.
+ */
+static inline void nfp_qcp_rd_ptr_add(u8 __iomem *q, u32 val)
+{
+ _nfp_qcp_ptr_add(q, NFP_QCP_READ_PTR, val);
+}
+
+/**
+ * nfp_qcp_wr_ptr_add() - Add the value to the write pointer of a queue
+ *
+ * @q: Base address for queue structure
+ * @val: Value to add to the queue pointer
+ *
+ * If @val is greater than @NFP_QCP_MAX_ADD multiple writes are performed.
+ */
+static inline void nfp_qcp_wr_ptr_add(u8 __iomem *q, u32 val)
+{
+ _nfp_qcp_ptr_add(q, NFP_QCP_WRITE_PTR, val);
+}
+
+static inline u32 _nfp_qcp_read(u8 __iomem *q, enum nfp_qcp_ptr ptr)
+{
+ u32 off;
+ u32 val;
+
+ if (ptr == NFP_QCP_READ_PTR)
+ off = NFP_QCP_QUEUE_STS_LO;
+ else
+ off = NFP_QCP_QUEUE_STS_HI;
+
+ val = readl(q + off);
+
+ if (ptr == NFP_QCP_READ_PTR)
+ return val & NFP_QCP_QUEUE_STS_LO_READPTR_mask;
+ else
+ return val & NFP_QCP_QUEUE_STS_HI_WRITEPTR_mask;
+}
+
+/**
+ * nfp_qcp_rd_ptr_read() - Read the current read pointer value for a queue
+ * @q: Base address for queue structure
+ *
+ * Return: Value read.
+ */
+static inline u32 nfp_qcp_rd_ptr_read(u8 __iomem *q)
+{
+ return _nfp_qcp_read(q, NFP_QCP_READ_PTR);
+}
+
+/**
+ * nfp_qcp_wr_ptr_read() - Read the current write pointer value for a queue
+ * @q: Base address for queue structure
+ *
+ * Return: Value read.
+ */
+static inline u32 nfp_qcp_wr_ptr_read(u8 __iomem *q)
+{
+ return _nfp_qcp_read(q, NFP_QCP_WRITE_PTR);
+}
+
+/* Globals */
+extern const char nfp_net_driver_name[];
+extern const char nfp_net_driver_version[];
+
+/* Prototypes */
+void nfp_net_get_fw_version(struct nfp_net_fw_version *fw_ver,
+ void __iomem *ctrl_bar);
+
+struct nfp_net *nfp_net_netdev_alloc(struct pci_dev *pdev,
+ int max_tx_rings, int max_rx_rings);
+void nfp_net_netdev_free(struct nfp_net *nn);
+int nfp_net_netdev_init(struct net_device *netdev);
+void nfp_net_netdev_clean(struct net_device *netdev);
+void nfp_net_set_ethtool_ops(struct net_device *netdev);
+void nfp_net_info(struct nfp_net *nn);
+int nfp_net_reconfig(struct nfp_net *nn, u32 update);
+void nfp_net_rss_write_itbl(struct nfp_net *nn);
+void nfp_net_rss_write_key(struct nfp_net *nn);
+void nfp_net_coalesce_write_cfg(struct nfp_net *nn);
+int nfp_net_irqs_alloc(struct nfp_net *nn);
+void nfp_net_irqs_disable(struct nfp_net *nn);
+
+#ifdef CONFIG_NFP_NET_DEBUG
+void nfp_net_debugfs_create(void);
+void nfp_net_debugfs_destroy(void);
+void nfp_net_debugfs_adapter_add(struct nfp_net *nn);
+void nfp_net_debugfs_adapter_del(struct nfp_net *nn);
+#else
+static inline void nfp_net_debugfs_create(void)
+{
+}
+
+static inline void nfp_net_debugfs_destroy(void)
+{
+}
+
+static inline void nfp_net_debugfs_adapter_add(struct nfp_net *nn)
+{
+}
+
+static inline void nfp_net_debugfs_adapter_del(struct nfp_net *nn)
+{
+}
+#endif /* CONFIG_NFP_NET_DEBUG */
+
+#endif /* _NFP_NET_H_ */
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
new file mode 100644
index 000000000000..43c618bafdb6
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
@@ -0,0 +1,2435 @@
+/*
+ * Copyright (C) 2015 Netronome Systems, Inc.
+ *
+ * This software is dual licensed under the GNU General License Version 2,
+ * June 1991 as shown in the file COPYING in the top-level directory of this
+ * source tree or the BSD 2-Clause License provided below. You have the
+ * option to license this software under the complete terms of either license.
+ *
+ * The BSD 2-Clause 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, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 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.
+ */
+
+/*
+ * nfp_net_common.c
+ * Netronome network device driver: Common functions between PF and VF
+ * Authors: Jakub Kicinski <jakub.kicinski@netronome.com>
+ * Jason McMullan <jason.mcmullan@netronome.com>
+ * Rolf Neugebauer <rolf.neugebauer@netronome.com>
+ * Brad Petrus <brad.petrus@netronome.com>
+ * Chris Telfer <chris.telfer@netronome.com>
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/pci.h>
+#include <linux/pci_regs.h>
+#include <linux/msi.h>
+#include <linux/ethtool.h>
+#include <linux/log2.h>
+#include <linux/if_vlan.h>
+#include <linux/random.h>
+
+#include <linux/ktime.h>
+
+#include <net/vxlan.h>
+
+#include "nfp_net_ctrl.h"
+#include "nfp_net.h"
+
+/**
+ * nfp_net_get_fw_version() - Read and parse the FW version
+ * @fw_ver: Output fw_version structure to read to
+ * @ctrl_bar: Mapped address of the control BAR
+ */
+void nfp_net_get_fw_version(struct nfp_net_fw_version *fw_ver,
+ void __iomem *ctrl_bar)
+{
+ u32 reg;
+
+ reg = readl(ctrl_bar + NFP_NET_CFG_VERSION);
+ put_unaligned_le32(reg, fw_ver);
+}
+
+/**
+ * nfp_net_reconfig() - Reconfigure the firmware
+ * @nn: NFP Net device to reconfigure
+ * @update: The value for the update field in the BAR config
+ *
+ * Write the update word to the BAR and ping the reconfig queue. The
+ * poll until the firmware has acknowledged the update by zeroing the
+ * update word.
+ *
+ * Return: Negative errno on error, 0 on success
+ */
+int nfp_net_reconfig(struct nfp_net *nn, u32 update)
+{
+ int cnt, ret = 0;
+ u32 new;
+
+ spin_lock_bh(&nn->reconfig_lock);
+
+ nn_writel(nn, NFP_NET_CFG_UPDATE, update);
+ /* ensure update is written before pinging HW */
+ nn_pci_flush(nn);
+ nfp_qcp_wr_ptr_add(nn->qcp_cfg, 1);
+
+ /* Poll update field, waiting for NFP to ack the config */
+ for (cnt = 0; ; cnt++) {
+ new = nn_readl(nn, NFP_NET_CFG_UPDATE);
+ if (new == 0)
+ break;
+ if (new & NFP_NET_CFG_UPDATE_ERR) {
+ nn_err(nn, "Reconfig error: 0x%08x\n", new);
+ ret = -EIO;
+ break;
+ } else if (cnt >= NFP_NET_POLL_TIMEOUT) {
+ nn_err(nn, "Reconfig timeout for 0x%08x after %dms\n",
+ update, cnt);
+ ret = -EIO;
+ break;
+ }
+ mdelay(1);
+ }
+
+ spin_unlock_bh(&nn->reconfig_lock);
+ return ret;
+}
+
+/* Interrupt configuration and handling
+ */
+
+/**
+ * nfp_net_irq_unmask_msix() - Unmask MSI-X after automasking
+ * @nn: NFP Network structure
+ * @entry_nr: MSI-X table entry
+ *
+ * Clear the MSI-X table mask bit for the given entry bypassing Linux irq
+ * handling subsystem. Use *only* to reenable automasked vectors.
+ */
+static void nfp_net_irq_unmask_msix(struct nfp_net *nn, unsigned int entry_nr)
+{
+ struct list_head *msi_head = &nn->pdev->dev.msi_list;
+ struct msi_desc *entry;
+ u32 off;
+
+ /* All MSI-Xs have the same mask_base */
+ entry = list_first_entry(msi_head, struct msi_desc, list);
+
+ off = (PCI_MSIX_ENTRY_SIZE * entry_nr) +
+ PCI_MSIX_ENTRY_VECTOR_CTRL;
+ writel(0, entry->mask_base + off);
+ readl(entry->mask_base);
+}
+
+/**
+ * nfp_net_irq_unmask() - Unmask automasked interrupt
+ * @nn: NFP Network structure
+ * @entry_nr: MSI-X table entry
+ *
+ * If MSI-X auto-masking is enabled clear the mask bit, otherwise
+ * clear the ICR for the entry.
+ */
+static void nfp_net_irq_unmask(struct nfp_net *nn, unsigned int entry_nr)
+{
+ if (nn->ctrl & NFP_NET_CFG_CTRL_MSIXAUTO) {
+ nfp_net_irq_unmask_msix(nn, entry_nr);
+ return;
+ }
+
+ nn_writeb(nn, NFP_NET_CFG_ICR(entry_nr), NFP_NET_CFG_ICR_UNMASKED);
+ nn_pci_flush(nn);
+}
+
+/**
+ * nfp_net_msix_alloc() - Try to allocate MSI-X irqs
+ * @nn: NFP Network structure
+ * @nr_vecs: Number of MSI-X vectors to allocate
+ *
+ * For MSI-X we want at least NFP_NET_NON_Q_VECTORS + 1 vectors.
+ *
+ * Return: Number of MSI-X vectors obtained or 0 on error.
+ */
+static int nfp_net_msix_alloc(struct nfp_net *nn, int nr_vecs)
+{
+ struct pci_dev *pdev = nn->pdev;
+ int nvecs;
+ int i;
+
+ for (i = 0; i < nr_vecs; i++)
+ nn->irq_entries[i].entry = i;
+
+ nvecs = pci_enable_msix_range(pdev, nn->irq_entries,
+ NFP_NET_NON_Q_VECTORS + 1, nr_vecs);
+ if (nvecs < 0) {
+ nn_warn(nn, "Failed to enable MSI-X. Wanted %d-%d (err=%d)\n",
+ NFP_NET_NON_Q_VECTORS + 1, nr_vecs, nvecs);
+ return 0;
+ }
+
+ return nvecs;
+}
+
+/**
+ * nfp_net_irqs_wanted() - Work out how many interrupt vectors we want
+ * @nn: NFP Network structure
+ *
+ * We want a vector per CPU (or ring), whatever is smaller plus
+ * NFP_NET_NON_Q_VECTORS for LSC etc.
+ *
+ * Return: Number of interrupts wanted
+ */
+static int nfp_net_irqs_wanted(struct nfp_net *nn)
+{
+ int ncpus;
+ int vecs;
+
+ ncpus = num_online_cpus();
+
+ vecs = max_t(int, nn->num_tx_rings, nn->num_rx_rings);
+ vecs = min_t(int, vecs, ncpus);
+
+ return vecs + NFP_NET_NON_Q_VECTORS;
+}
+
+/**
+ * nfp_net_irqs_alloc() - allocates MSI-X irqs
+ * @nn: NFP Network structure
+ *
+ * Return: Number of irqs obtained or 0 on error.
+ */
+int nfp_net_irqs_alloc(struct nfp_net *nn)
+{
+ int wanted_irqs;
+
+ wanted_irqs = nfp_net_irqs_wanted(nn);
+
+ nn->num_irqs = nfp_net_msix_alloc(nn, wanted_irqs);
+ if (nn->num_irqs == 0) {
+ nn_err(nn, "Failed to allocate MSI-X IRQs\n");
+ return 0;
+ }
+
+ nn->num_r_vecs = nn->num_irqs - NFP_NET_NON_Q_VECTORS;
+
+ if (nn->num_irqs < wanted_irqs)
+ nn_warn(nn, "Unable to allocate %d vectors. Got %d instead\n",
+ wanted_irqs, nn->num_irqs);
+
+ return nn->num_irqs;
+}
+
+/**
+ * nfp_net_irqs_disable() - Disable interrupts
+ * @nn: NFP Network structure
+ *
+ * Undoes what @nfp_net_irqs_alloc() does.
+ */
+void nfp_net_irqs_disable(struct nfp_net *nn)
+{
+ pci_disable_msix(nn->pdev);
+}
+
+/**
+ * nfp_net_irq_rxtx() - Interrupt service routine for RX/TX rings.
+ * @irq: Interrupt
+ * @data: Opaque data structure
+ *
+ * Return: Indicate if the interrupt has been handled.
+ */
+static irqreturn_t nfp_net_irq_rxtx(int irq, void *data)
+{
+ struct nfp_net_r_vector *r_vec = data;
+
+ napi_schedule_irqoff(&r_vec->napi);
+
+ /* The FW auto-masks any interrupt, either via the MASK bit in
+ * the MSI-X table or via the per entry ICR field. So there
+ * is no need to disable interrupts here.
+ */
+ return IRQ_HANDLED;
+}
+
+/**
+ * nfp_net_read_link_status() - Reread link status from control BAR
+ * @nn: NFP Network structure
+ */
+static void nfp_net_read_link_status(struct nfp_net *nn)
+{
+ unsigned long flags;
+ bool link_up;
+ u32 sts;
+
+ spin_lock_irqsave(&nn->link_status_lock, flags);
+
+ sts = nn_readl(nn, NFP_NET_CFG_STS);
+ link_up = !!(sts & NFP_NET_CFG_STS_LINK);
+
+ if (nn->link_up == link_up)
+ goto out;
+
+ nn->link_up = link_up;
+
+ if (nn->link_up) {
+ netif_carrier_on(nn->netdev);
+ netdev_info(nn->netdev, "NIC Link is Up\n");
+ } else {
+ netif_carrier_off(nn->netdev);
+ netdev_info(nn->netdev, "NIC Link is Down\n");
+ }
+out:
+ spin_unlock_irqrestore(&nn->link_status_lock, flags);
+}
+
+/**
+ * nfp_net_irq_lsc() - Interrupt service routine for link state changes
+ * @irq: Interrupt
+ * @data: Opaque data structure
+ *
+ * Return: Indicate if the interrupt has been handled.
+ */
+static irqreturn_t nfp_net_irq_lsc(int irq, void *data)
+{
+ struct nfp_net *nn = data;
+
+ nfp_net_read_link_status(nn);
+
+ nfp_net_irq_unmask(nn, NFP_NET_IRQ_LSC_IDX);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * nfp_net_irq_exn() - Interrupt service routine for exceptions
+ * @irq: Interrupt
+ * @data: Opaque data structure
+ *
+ * Return: Indicate if the interrupt has been handled.
+ */
+static irqreturn_t nfp_net_irq_exn(int irq, void *data)
+{
+ struct nfp_net *nn = data;
+
+ nn_err(nn, "%s: UNIMPLEMENTED.\n", __func__);
+ /* XXX TO BE IMPLEMENTED */
+ return IRQ_HANDLED;
+}
+
+/**
+ * nfp_net_tx_ring_init() - Fill in the boilerplate for a TX ring
+ * @tx_ring: TX ring structure
+ */
+static void nfp_net_tx_ring_init(struct nfp_net_tx_ring *tx_ring)
+{
+ struct nfp_net_r_vector *r_vec = tx_ring->r_vec;
+ struct nfp_net *nn = r_vec->nfp_net;
+
+ tx_ring->qcidx = tx_ring->idx * nn->stride_tx;
+ tx_ring->qcp_q = nn->tx_bar + NFP_QCP_QUEUE_OFF(tx_ring->qcidx);
+}
+
+/**
+ * nfp_net_rx_ring_init() - Fill in the boilerplate for a RX ring
+ * @rx_ring: RX ring structure
+ */
+static void nfp_net_rx_ring_init(struct nfp_net_rx_ring *rx_ring)
+{
+ struct nfp_net_r_vector *r_vec = rx_ring->r_vec;
+ struct nfp_net *nn = r_vec->nfp_net;
+
+ rx_ring->fl_qcidx = rx_ring->idx * nn->stride_rx;
+ rx_ring->rx_qcidx = rx_ring->fl_qcidx + (nn->stride_rx - 1);
+
+ rx_ring->qcp_fl = nn->rx_bar + NFP_QCP_QUEUE_OFF(rx_ring->fl_qcidx);
+ rx_ring->qcp_rx = nn->rx_bar + NFP_QCP_QUEUE_OFF(rx_ring->rx_qcidx);
+}
+
+/**
+ * nfp_net_irqs_assign() - Assign IRQs and setup rvecs.
+ * @netdev: netdev structure
+ */
+static void nfp_net_irqs_assign(struct net_device *netdev)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ struct nfp_net_r_vector *r_vec;
+ int r;
+
+ /* Assumes nn->num_tx_rings == nn->num_rx_rings */
+ if (nn->num_tx_rings > nn->num_r_vecs) {
+ nn_warn(nn, "More rings (%d) than vectors (%d).\n",
+ nn->num_tx_rings, nn->num_r_vecs);
+ nn->num_tx_rings = nn->num_r_vecs;
+ nn->num_rx_rings = nn->num_r_vecs;
+ }
+
+ nn->lsc_handler = nfp_net_irq_lsc;
+ nn->exn_handler = nfp_net_irq_exn;
+
+ for (r = 0; r < nn->num_r_vecs; r++) {
+ r_vec = &nn->r_vecs[r];
+ r_vec->nfp_net = nn;
+ r_vec->handler = nfp_net_irq_rxtx;
+ r_vec->irq_idx = NFP_NET_NON_Q_VECTORS + r;
+
+ cpumask_set_cpu(r, &r_vec->affinity_mask);
+
+ r_vec->tx_ring = &nn->tx_rings[r];
+ nn->tx_rings[r].idx = r;
+ nn->tx_rings[r].r_vec = r_vec;
+ nfp_net_tx_ring_init(r_vec->tx_ring);
+
+ r_vec->rx_ring = &nn->rx_rings[r];
+ nn->rx_rings[r].idx = r;
+ nn->rx_rings[r].r_vec = r_vec;
+ nfp_net_rx_ring_init(r_vec->rx_ring);
+ }
+}
+
+/**
+ * nfp_net_aux_irq_request() - Request an auxiliary interrupt (LSC or EXN)
+ * @nn: NFP Network structure
+ * @ctrl_offset: Control BAR offset where IRQ configuration should be written
+ * @format: printf-style format to construct the interrupt name
+ * @name: Pointer to allocated space for interrupt name
+ * @name_sz: Size of space for interrupt name
+ * @vector_idx: Index of MSI-X vector used for this interrupt
+ * @handler: IRQ handler to register for this interrupt
+ */
+static int
+nfp_net_aux_irq_request(struct nfp_net *nn, u32 ctrl_offset,
+ const char *format, char *name, size_t name_sz,
+ unsigned int vector_idx, irq_handler_t handler)
+{
+ struct msix_entry *entry;
+ int err;
+
+ entry = &nn->irq_entries[vector_idx];
+
+ snprintf(name, name_sz, format, netdev_name(nn->netdev));
+ err = request_irq(entry->vector, handler, 0, name, nn);
+ if (err) {
+ nn_err(nn, "Failed to request IRQ %d (err=%d).\n",
+ entry->vector, err);
+ return err;
+ }
+ nn_writeb(nn, ctrl_offset, vector_idx);
+
+ return 0;
+}
+
+/**
+ * nfp_net_aux_irq_free() - Free an auxiliary interrupt (LSC or EXN)
+ * @nn: NFP Network structure
+ * @ctrl_offset: Control BAR offset where IRQ configuration should be written
+ * @vector_idx: Index of MSI-X vector used for this interrupt
+ */
+static void nfp_net_aux_irq_free(struct nfp_net *nn, u32 ctrl_offset,
+ unsigned int vector_idx)
+{
+ nn_writeb(nn, ctrl_offset, 0xff);
+ free_irq(nn->irq_entries[vector_idx].vector, nn);
+}
+
+/* Transmit
+ *
+ * One queue controller peripheral queue is used for transmit. The
+ * driver en-queues packets for transmit by advancing the write
+ * pointer. The device indicates that packets have transmitted by
+ * advancing the read pointer. The driver maintains a local copy of
+ * the read and write pointer in @struct nfp_net_tx_ring. The driver
+ * keeps @wr_p in sync with the queue controller write pointer and can
+ * determine how many packets have been transmitted by comparing its
+ * copy of the read pointer @rd_p with the read pointer maintained by
+ * the queue controller peripheral.
+ */
+
+/**
+ * nfp_net_tx_full() - Check if the TX ring is full
+ * @tx_ring: TX ring to check
+ * @dcnt: Number of descriptors that need to be enqueued (must be >= 1)
+ *
+ * This function checks, based on the *host copy* of read/write
+ * pointer if a given TX ring is full. The real TX queue may have
+ * some newly made available slots.
+ *
+ * Return: True if the ring is full.
+ */
+static inline int nfp_net_tx_full(struct nfp_net_tx_ring *tx_ring, int dcnt)
+{
+ return (tx_ring->wr_p - tx_ring->rd_p) >= (tx_ring->cnt - dcnt);
+}
+
+/* Wrappers for deciding when to stop and restart TX queues */
+static int nfp_net_tx_ring_should_wake(struct nfp_net_tx_ring *tx_ring)
+{
+ return !nfp_net_tx_full(tx_ring, MAX_SKB_FRAGS * 4);
+}
+
+static int nfp_net_tx_ring_should_stop(struct nfp_net_tx_ring *tx_ring)
+{
+ return nfp_net_tx_full(tx_ring, MAX_SKB_FRAGS + 1);
+}
+
+/**
+ * nfp_net_tx_ring_stop() - stop tx ring
+ * @nd_q: netdev queue
+ * @tx_ring: driver tx queue structure
+ *
+ * Safely stop TX ring. Remember that while we are running .start_xmit()
+ * someone else may be cleaning the TX ring completions so we need to be
+ * extra careful here.
+ */
+static void nfp_net_tx_ring_stop(struct netdev_queue *nd_q,
+ struct nfp_net_tx_ring *tx_ring)
+{
+ netif_tx_stop_queue(nd_q);
+
+ /* We can race with the TX completion out of NAPI so recheck */
+ smp_mb();
+ if (unlikely(nfp_net_tx_ring_should_wake(tx_ring)))
+ netif_tx_start_queue(nd_q);
+}
+
+/**
+ * nfp_net_tx_tso() - Set up Tx descriptor for LSO
+ * @nn: NFP Net device
+ * @r_vec: per-ring structure
+ * @txbuf: Pointer to driver soft TX descriptor
+ * @txd: Pointer to HW TX descriptor
+ * @skb: Pointer to SKB
+ *
+ * Set up Tx descriptor for LSO, do nothing for non-LSO skbs.
+ * Return error on packet header greater than maximum supported LSO header size.
+ */
+static void nfp_net_tx_tso(struct nfp_net *nn, struct nfp_net_r_vector *r_vec,
+ struct nfp_net_tx_buf *txbuf,
+ struct nfp_net_tx_desc *txd, struct sk_buff *skb)
+{
+ u32 hdrlen;
+ u16 mss;
+
+ if (!skb_is_gso(skb))
+ return;
+
+ if (!skb->encapsulation)
+ hdrlen = skb_transport_offset(skb) + tcp_hdrlen(skb);
+ else
+ hdrlen = skb_inner_transport_header(skb) - skb->data +
+ inner_tcp_hdrlen(skb);
+
+ txbuf->pkt_cnt = skb_shinfo(skb)->gso_segs;
+ txbuf->real_len += hdrlen * (txbuf->pkt_cnt - 1);
+
+ mss = skb_shinfo(skb)->gso_size & PCIE_DESC_TX_MSS_MASK;
+ txd->l4_offset = hdrlen;
+ txd->mss = cpu_to_le16(mss);
+ txd->flags |= PCIE_DESC_TX_LSO;
+
+ u64_stats_update_begin(&r_vec->tx_sync);
+ r_vec->tx_lso++;
+ u64_stats_update_end(&r_vec->tx_sync);
+}
+
+/**
+ * nfp_net_tx_csum() - Set TX CSUM offload flags in TX descriptor
+ * @nn: NFP Net device
+ * @r_vec: per-ring structure
+ * @txbuf: Pointer to driver soft TX descriptor
+ * @txd: Pointer to TX descriptor
+ * @skb: Pointer to SKB
+ *
+ * This function sets the TX checksum flags in the TX descriptor based
+ * on the configuration and the protocol of the packet to be transmitted.
+ */
+static void nfp_net_tx_csum(struct nfp_net *nn, struct nfp_net_r_vector *r_vec,
+ struct nfp_net_tx_buf *txbuf,
+ struct nfp_net_tx_desc *txd, struct sk_buff *skb)
+{
+ struct ipv6hdr *ipv6h;
+ struct iphdr *iph;
+ u8 l4_hdr;
+
+ if (!(nn->ctrl & NFP_NET_CFG_CTRL_TXCSUM))
+ return;
+
+ if (skb->ip_summed != CHECKSUM_PARTIAL)
+ return;
+
+ txd->flags |= PCIE_DESC_TX_CSUM;
+ if (skb->encapsulation)
+ txd->flags |= PCIE_DESC_TX_ENCAP;
+
+ iph = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb);
+ ipv6h = skb->encapsulation ? inner_ipv6_hdr(skb) : ipv6_hdr(skb);
+
+ if (iph->version == 4) {
+ txd->flags |= PCIE_DESC_TX_IP4_CSUM;
+ l4_hdr = iph->protocol;
+ } else if (ipv6h->version == 6) {
+ l4_hdr = ipv6h->nexthdr;
+ } else {
+ nn_warn_ratelimit(nn, "partial checksum but ipv=%x!\n",
+ iph->version);
+ return;
+ }
+
+ switch (l4_hdr) {
+ case IPPROTO_TCP:
+ txd->flags |= PCIE_DESC_TX_TCP_CSUM;
+ break;
+ case IPPROTO_UDP:
+ txd->flags |= PCIE_DESC_TX_UDP_CSUM;
+ break;
+ default:
+ nn_warn_ratelimit(nn, "partial checksum but l4 proto=%x!\n",
+ l4_hdr);
+ return;
+ }
+
+ u64_stats_update_begin(&r_vec->tx_sync);
+ if (skb->encapsulation)
+ r_vec->hw_csum_tx_inner += txbuf->pkt_cnt;
+ else
+ r_vec->hw_csum_tx += txbuf->pkt_cnt;
+ u64_stats_update_end(&r_vec->tx_sync);
+}
+
+/**
+ * nfp_net_tx() - Main transmit entry point
+ * @skb: SKB to transmit
+ * @netdev: netdev structure
+ *
+ * Return: NETDEV_TX_OK on success.
+ */
+static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ const struct skb_frag_struct *frag;
+ struct nfp_net_r_vector *r_vec;
+ struct nfp_net_tx_desc *txd, txdg;
+ struct nfp_net_tx_buf *txbuf;
+ struct nfp_net_tx_ring *tx_ring;
+ struct netdev_queue *nd_q;
+ dma_addr_t dma_addr;
+ unsigned int fsize;
+ int f, nr_frags;
+ int wr_idx;
+ u16 qidx;
+
+ qidx = skb_get_queue_mapping(skb);
+ tx_ring = &nn->tx_rings[qidx];
+ r_vec = tx_ring->r_vec;
+ nd_q = netdev_get_tx_queue(nn->netdev, qidx);
+
+ nr_frags = skb_shinfo(skb)->nr_frags;
+
+ if (unlikely(nfp_net_tx_full(tx_ring, nr_frags + 1))) {
+ nn_warn_ratelimit(nn, "TX ring %d busy. wrp=%u rdp=%u\n",
+ qidx, tx_ring->wr_p, tx_ring->rd_p);
+ netif_tx_stop_queue(nd_q);
+ u64_stats_update_begin(&r_vec->tx_sync);
+ r_vec->tx_busy++;
+ u64_stats_update_end(&r_vec->tx_sync);
+ return NETDEV_TX_BUSY;
+ }
+
+ /* Start with the head skbuf */
+ dma_addr = dma_map_single(&nn->pdev->dev, skb->data, skb_headlen(skb),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(&nn->pdev->dev, dma_addr))
+ goto err_free;
+
+ wr_idx = tx_ring->wr_p % tx_ring->cnt;
+
+ /* Stash the soft descriptor of the head then initialize it */
+ txbuf = &tx_ring->txbufs[wr_idx];
+ txbuf->skb = skb;
+ txbuf->dma_addr = dma_addr;
+ txbuf->fidx = -1;
+ txbuf->pkt_cnt = 1;
+ txbuf->real_len = skb->len;
+
+ /* Build TX descriptor */
+ txd = &tx_ring->txds[wr_idx];
+ txd->offset_eop = (nr_frags == 0) ? PCIE_DESC_TX_EOP : 0;
+ txd->dma_len = cpu_to_le16(skb_headlen(skb));
+ nfp_desc_set_dma_addr(txd, dma_addr);
+ txd->data_len = cpu_to_le16(skb->len);
+
+ txd->flags = 0;
+ txd->mss = 0;
+ txd->l4_offset = 0;
+
+ nfp_net_tx_tso(nn, r_vec, txbuf, txd, skb);
+
+ nfp_net_tx_csum(nn, r_vec, txbuf, txd, skb);
+
+ if (skb_vlan_tag_present(skb) && nn->ctrl & NFP_NET_CFG_CTRL_TXVLAN) {
+ txd->flags |= PCIE_DESC_TX_VLAN;
+ txd->vlan = cpu_to_le16(skb_vlan_tag_get(skb));
+ }
+
+ /* Gather DMA */
+ if (nr_frags > 0) {
+ /* all descs must match except for in addr, length and eop */
+ txdg = *txd;
+
+ for (f = 0; f < nr_frags; f++) {
+ frag = &skb_shinfo(skb)->frags[f];
+ fsize = skb_frag_size(frag);
+
+ dma_addr = skb_frag_dma_map(&nn->pdev->dev, frag, 0,
+ fsize, DMA_TO_DEVICE);
+ if (dma_mapping_error(&nn->pdev->dev, dma_addr))
+ goto err_unmap;
+
+ wr_idx = (wr_idx + 1) % tx_ring->cnt;
+ tx_ring->txbufs[wr_idx].skb = skb;
+ tx_ring->txbufs[wr_idx].dma_addr = dma_addr;
+ tx_ring->txbufs[wr_idx].fidx = f;
+
+ txd = &tx_ring->txds[wr_idx];
+ *txd = txdg;
+ txd->dma_len = cpu_to_le16(fsize);
+ nfp_desc_set_dma_addr(txd, dma_addr);
+ txd->offset_eop =
+ (f == nr_frags - 1) ? PCIE_DESC_TX_EOP : 0;
+ }
+
+ u64_stats_update_begin(&r_vec->tx_sync);
+ r_vec->tx_gather++;
+ u64_stats_update_end(&r_vec->tx_sync);
+ }
+
+ netdev_tx_sent_queue(nd_q, txbuf->real_len);
+
+ tx_ring->wr_p += nr_frags + 1;
+ if (nfp_net_tx_ring_should_stop(tx_ring))
+ nfp_net_tx_ring_stop(nd_q, tx_ring);
+
+ tx_ring->wr_ptr_add += nr_frags + 1;
+ if (!skb->xmit_more || netif_xmit_stopped(nd_q)) {
+ /* force memory write before we let HW know */
+ wmb();
+ nfp_qcp_wr_ptr_add(tx_ring->qcp_q, tx_ring->wr_ptr_add);
+ tx_ring->wr_ptr_add = 0;
+ }
+
+ skb_tx_timestamp(skb);
+
+ return NETDEV_TX_OK;
+
+err_unmap:
+ --f;
+ while (f >= 0) {
+ frag = &skb_shinfo(skb)->frags[f];
+ dma_unmap_page(&nn->pdev->dev,
+ tx_ring->txbufs[wr_idx].dma_addr,
+ skb_frag_size(frag), DMA_TO_DEVICE);
+ tx_ring->txbufs[wr_idx].skb = NULL;
+ tx_ring->txbufs[wr_idx].dma_addr = 0;
+ tx_ring->txbufs[wr_idx].fidx = -2;
+ wr_idx = wr_idx - 1;
+ if (wr_idx < 0)
+ wr_idx += tx_ring->cnt;
+ }
+ dma_unmap_single(&nn->pdev->dev, tx_ring->txbufs[wr_idx].dma_addr,
+ skb_headlen(skb), DMA_TO_DEVICE);
+ tx_ring->txbufs[wr_idx].skb = NULL;
+ tx_ring->txbufs[wr_idx].dma_addr = 0;
+ tx_ring->txbufs[wr_idx].fidx = -2;
+err_free:
+ nn_warn_ratelimit(nn, "Failed to map DMA TX buffer\n");
+ u64_stats_update_begin(&r_vec->tx_sync);
+ r_vec->tx_errors++;
+ u64_stats_update_end(&r_vec->tx_sync);
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+}
+
+/**
+ * nfp_net_tx_complete() - Handled completed TX packets
+ * @tx_ring: TX ring structure
+ *
+ * Return: Number of completed TX descriptors
+ */
+static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring)
+{
+ struct nfp_net_r_vector *r_vec = tx_ring->r_vec;
+ struct nfp_net *nn = r_vec->nfp_net;
+ const struct skb_frag_struct *frag;
+ struct netdev_queue *nd_q;
+ u32 done_pkts = 0, done_bytes = 0;
+ struct sk_buff *skb;
+ int todo, nr_frags;
+ u32 qcp_rd_p;
+ int fidx;
+ int idx;
+
+ /* Work out how many descriptors have been transmitted */
+ qcp_rd_p = nfp_qcp_rd_ptr_read(tx_ring->qcp_q);
+
+ if (qcp_rd_p == tx_ring->qcp_rd_p)
+ return;
+
+ if (qcp_rd_p > tx_ring->qcp_rd_p)
+ todo = qcp_rd_p - tx_ring->qcp_rd_p;
+ else
+ todo = qcp_rd_p + tx_ring->cnt - tx_ring->qcp_rd_p;
+
+ while (todo--) {
+ idx = tx_ring->rd_p % tx_ring->cnt;
+ tx_ring->rd_p++;
+
+ skb = tx_ring->txbufs[idx].skb;
+ if (!skb)
+ continue;
+
+ nr_frags = skb_shinfo(skb)->nr_frags;
+ fidx = tx_ring->txbufs[idx].fidx;
+
+ if (fidx == -1) {
+ /* unmap head */
+ dma_unmap_single(&nn->pdev->dev,
+ tx_ring->txbufs[idx].dma_addr,
+ skb_headlen(skb), DMA_TO_DEVICE);
+
+ done_pkts += tx_ring->txbufs[idx].pkt_cnt;
+ done_bytes += tx_ring->txbufs[idx].real_len;
+ } else {
+ /* unmap fragment */
+ frag = &skb_shinfo(skb)->frags[fidx];
+ dma_unmap_page(&nn->pdev->dev,
+ tx_ring->txbufs[idx].dma_addr,
+ skb_frag_size(frag), DMA_TO_DEVICE);
+ }
+
+ /* check for last gather fragment */
+ if (fidx == nr_frags - 1)
+ dev_kfree_skb_any(skb);
+
+ tx_ring->txbufs[idx].dma_addr = 0;
+ tx_ring->txbufs[idx].skb = NULL;
+ tx_ring->txbufs[idx].fidx = -2;
+ }
+
+ tx_ring->qcp_rd_p = qcp_rd_p;
+
+ u64_stats_update_begin(&r_vec->tx_sync);
+ r_vec->tx_bytes += done_bytes;
+ r_vec->tx_pkts += done_pkts;
+ u64_stats_update_end(&r_vec->tx_sync);
+
+ nd_q = netdev_get_tx_queue(nn->netdev, tx_ring->idx);
+ netdev_tx_completed_queue(nd_q, done_pkts, done_bytes);
+ if (nfp_net_tx_ring_should_wake(tx_ring)) {
+ /* Make sure TX thread will see updated tx_ring->rd_p */
+ smp_mb();
+
+ if (unlikely(netif_tx_queue_stopped(nd_q)))
+ netif_tx_wake_queue(nd_q);
+ }
+
+ WARN_ONCE(tx_ring->wr_p - tx_ring->rd_p > tx_ring->cnt,
+ "TX ring corruption rd_p=%u wr_p=%u cnt=%u\n",
+ tx_ring->rd_p, tx_ring->wr_p, tx_ring->cnt);
+}
+
+/**
+ * nfp_net_tx_flush() - Free any untransmitted buffers currently on the TX ring
+ * @tx_ring: TX ring structure
+ *
+ * Assumes that the device is stopped
+ */
+static void nfp_net_tx_flush(struct nfp_net_tx_ring *tx_ring)
+{
+ struct nfp_net_r_vector *r_vec = tx_ring->r_vec;
+ struct nfp_net *nn = r_vec->nfp_net;
+ struct pci_dev *pdev = nn->pdev;
+ const struct skb_frag_struct *frag;
+ struct netdev_queue *nd_q;
+ struct sk_buff *skb;
+ int nr_frags;
+ int fidx;
+ int idx;
+
+ while (tx_ring->rd_p != tx_ring->wr_p) {
+ idx = tx_ring->rd_p % tx_ring->cnt;
+
+ skb = tx_ring->txbufs[idx].skb;
+ if (skb) {
+ nr_frags = skb_shinfo(skb)->nr_frags;
+ fidx = tx_ring->txbufs[idx].fidx;
+
+ if (fidx == -1) {
+ /* unmap head */
+ dma_unmap_single(&pdev->dev,
+ tx_ring->txbufs[idx].dma_addr,
+ skb_headlen(skb),
+ DMA_TO_DEVICE);
+ } else {
+ /* unmap fragment */
+ frag = &skb_shinfo(skb)->frags[fidx];
+ dma_unmap_page(&pdev->dev,
+ tx_ring->txbufs[idx].dma_addr,
+ skb_frag_size(frag),
+ DMA_TO_DEVICE);
+ }
+
+ /* check for last gather fragment */
+ if (fidx == nr_frags - 1)
+ dev_kfree_skb_any(skb);
+
+ tx_ring->txbufs[idx].dma_addr = 0;
+ tx_ring->txbufs[idx].skb = NULL;
+ tx_ring->txbufs[idx].fidx = -2;
+ }
+
+ memset(&tx_ring->txds[idx], 0, sizeof(tx_ring->txds[idx]));
+
+ tx_ring->qcp_rd_p++;
+ tx_ring->rd_p++;
+ }
+
+ nd_q = netdev_get_tx_queue(nn->netdev, tx_ring->idx);
+ netdev_tx_reset_queue(nd_q);
+}
+
+static void nfp_net_tx_timeout(struct net_device *netdev)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ int i;
+
+ for (i = 0; i < nn->num_tx_rings; i++) {
+ if (!netif_tx_queue_stopped(netdev_get_tx_queue(netdev, i)))
+ continue;
+ nn_warn(nn, "TX timeout on ring: %d\n", i);
+ }
+ nn_warn(nn, "TX watchdog timeout\n");
+}
+
+/* Receive processing
+ */
+
+/**
+ * nfp_net_rx_space() - return the number of free slots on the RX ring
+ * @rx_ring: RX ring structure
+ *
+ * Make sure we leave at least one slot free.
+ *
+ * Return: True if there is space on the RX ring
+ */
+static inline int nfp_net_rx_space(struct nfp_net_rx_ring *rx_ring)
+{
+ return (rx_ring->cnt - 1) - (rx_ring->wr_p - rx_ring->rd_p);
+}
+
+/**
+ * nfp_net_rx_alloc_one() - Allocate and map skb for RX
+ * @rx_ring: RX ring structure of the skb
+ * @dma_addr: Pointer to storage for DMA address (output param)
+ *
+ * This function will allcate a new skb, map it for DMA.
+ *
+ * Return: allocated skb or NULL on failure.
+ */
+static struct sk_buff *
+nfp_net_rx_alloc_one(struct nfp_net_rx_ring *rx_ring, dma_addr_t *dma_addr)
+{
+ struct nfp_net *nn = rx_ring->r_vec->nfp_net;
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb(nn->netdev, nn->fl_bufsz);
+ if (!skb) {
+ nn_warn_ratelimit(nn, "Failed to alloc receive SKB\n");
+ return NULL;
+ }
+
+ *dma_addr = dma_map_single(&nn->pdev->dev, skb->data,
+ nn->fl_bufsz, DMA_FROM_DEVICE);
+ if (dma_mapping_error(&nn->pdev->dev, *dma_addr)) {
+ dev_kfree_skb_any(skb);
+ nn_warn_ratelimit(nn, "Failed to map DMA RX buffer\n");
+ return NULL;
+ }
+
+ return skb;
+}
+
+/**
+ * nfp_net_rx_give_one() - Put mapped skb on the software and hardware rings
+ * @rx_ring: RX ring structure
+ * @skb: Skb to put on rings
+ * @dma_addr: DMA address of skb mapping
+ */
+static void nfp_net_rx_give_one(struct nfp_net_rx_ring *rx_ring,
+ struct sk_buff *skb, dma_addr_t dma_addr)
+{
+ unsigned int wr_idx;
+
+ wr_idx = rx_ring->wr_p % rx_ring->cnt;
+
+ /* Stash SKB and DMA address away */
+ rx_ring->rxbufs[wr_idx].skb = skb;
+ rx_ring->rxbufs[wr_idx].dma_addr = dma_addr;
+
+ /* Fill freelist descriptor */
+ rx_ring->rxds[wr_idx].fld.reserved = 0;
+ rx_ring->rxds[wr_idx].fld.meta_len_dd = 0;
+ nfp_desc_set_dma_addr(&rx_ring->rxds[wr_idx].fld, dma_addr);
+
+ rx_ring->wr_p++;
+ rx_ring->wr_ptr_add++;
+ if (rx_ring->wr_ptr_add >= NFP_NET_FL_BATCH) {
+ /* Update write pointer of the freelist queue. Make
+ * sure all writes are flushed before telling the hardware.
+ */
+ wmb();
+ nfp_qcp_wr_ptr_add(rx_ring->qcp_fl, rx_ring->wr_ptr_add);
+ rx_ring->wr_ptr_add = 0;
+ }
+}
+
+/**
+ * nfp_net_rx_flush() - Free any buffers currently on the RX ring
+ * @rx_ring: RX ring to remove buffers from
+ *
+ * Assumes that the device is stopped
+ */
+static void nfp_net_rx_flush(struct nfp_net_rx_ring *rx_ring)
+{
+ struct nfp_net *nn = rx_ring->r_vec->nfp_net;
+ struct pci_dev *pdev = nn->pdev;
+ int idx;
+
+ while (rx_ring->rd_p != rx_ring->wr_p) {
+ idx = rx_ring->rd_p % rx_ring->cnt;
+
+ if (rx_ring->rxbufs[idx].skb) {
+ dma_unmap_single(&pdev->dev,
+ rx_ring->rxbufs[idx].dma_addr,
+ nn->fl_bufsz, DMA_FROM_DEVICE);
+ dev_kfree_skb_any(rx_ring->rxbufs[idx].skb);
+ rx_ring->rxbufs[idx].dma_addr = 0;
+ rx_ring->rxbufs[idx].skb = NULL;
+ }
+
+ memset(&rx_ring->rxds[idx], 0, sizeof(rx_ring->rxds[idx]));
+
+ rx_ring->rd_p++;
+ }
+}
+
+/**
+ * nfp_net_rx_fill_freelist() - Attempt filling freelist with RX buffers
+ * @rx_ring: RX ring to fill
+ *
+ * Try to fill as many buffers as possible into freelist. Return
+ * number of buffers added.
+ *
+ * Return: Number of freelist buffers added.
+ */
+static int nfp_net_rx_fill_freelist(struct nfp_net_rx_ring *rx_ring)
+{
+ struct sk_buff *skb;
+ dma_addr_t dma_addr;
+
+ while (nfp_net_rx_space(rx_ring)) {
+ skb = nfp_net_rx_alloc_one(rx_ring, &dma_addr);
+ if (!skb) {
+ nfp_net_rx_flush(rx_ring);
+ return -ENOMEM;
+ }
+ nfp_net_rx_give_one(rx_ring, skb, dma_addr);
+ }
+
+ return 0;
+}
+
+/**
+ * nfp_net_rx_csum_has_errors() - group check if rxd has any csum errors
+ * @flags: RX descriptor flags field in CPU byte order
+ */
+static int nfp_net_rx_csum_has_errors(u16 flags)
+{
+ u16 csum_all_checked, csum_all_ok;
+
+ csum_all_checked = flags & __PCIE_DESC_RX_CSUM_ALL;
+ csum_all_ok = flags & __PCIE_DESC_RX_CSUM_ALL_OK;
+
+ return csum_all_checked != (csum_all_ok << PCIE_DESC_RX_CSUM_OK_SHIFT);
+}
+
+/**
+ * nfp_net_rx_csum() - set SKB checksum field based on RX descriptor flags
+ * @nn: NFP Net device
+ * @r_vec: per-ring structure
+ * @rxd: Pointer to RX descriptor
+ * @skb: Pointer to SKB
+ */
+static void nfp_net_rx_csum(struct nfp_net *nn, struct nfp_net_r_vector *r_vec,
+ struct nfp_net_rx_desc *rxd, struct sk_buff *skb)
+{
+ skb_checksum_none_assert(skb);
+
+ if (!(nn->netdev->features & NETIF_F_RXCSUM))
+ return;
+
+ if (nfp_net_rx_csum_has_errors(le16_to_cpu(rxd->rxd.flags))) {
+ u64_stats_update_begin(&r_vec->rx_sync);
+ r_vec->hw_csum_rx_error++;
+ u64_stats_update_end(&r_vec->rx_sync);
+ return;
+ }
+
+ /* Assume that the firmware will never report inner CSUM_OK unless outer
+ * L4 headers were successfully parsed. FW will always report zero UDP
+ * checksum as CSUM_OK.
+ */
+ if (rxd->rxd.flags & PCIE_DESC_RX_TCP_CSUM_OK ||
+ rxd->rxd.flags & PCIE_DESC_RX_UDP_CSUM_OK) {
+ __skb_incr_checksum_unnecessary(skb);
+ u64_stats_update_begin(&r_vec->rx_sync);
+ r_vec->hw_csum_rx_ok++;
+ u64_stats_update_end(&r_vec->rx_sync);
+ }
+
+ if (rxd->rxd.flags & PCIE_DESC_RX_I_TCP_CSUM_OK ||
+ rxd->rxd.flags & PCIE_DESC_RX_I_UDP_CSUM_OK) {
+ __skb_incr_checksum_unnecessary(skb);
+ u64_stats_update_begin(&r_vec->rx_sync);
+ r_vec->hw_csum_rx_inner_ok++;
+ u64_stats_update_end(&r_vec->rx_sync);
+ }
+}
+
+/**
+ * nfp_net_set_hash() - Set SKB hash data
+ * @netdev: adapter's net_device structure
+ * @skb: SKB to set the hash data on
+ * @rxd: RX descriptor
+ *
+ * The RSS hash and hash-type are pre-pended to the packet data.
+ * Extract and decode it and set the skb fields.
+ */
+static void nfp_net_set_hash(struct net_device *netdev, struct sk_buff *skb,
+ struct nfp_net_rx_desc *rxd)
+{
+ struct nfp_net_rx_hash *rx_hash;
+
+ if (!(rxd->rxd.flags & PCIE_DESC_RX_RSS) ||
+ !(netdev->features & NETIF_F_RXHASH))
+ return;
+
+ rx_hash = (struct nfp_net_rx_hash *)(skb->data - sizeof(*rx_hash));
+
+ switch (be32_to_cpu(rx_hash->hash_type)) {
+ case NFP_NET_RSS_IPV4:
+ case NFP_NET_RSS_IPV6:
+ case NFP_NET_RSS_IPV6_EX:
+ skb_set_hash(skb, be32_to_cpu(rx_hash->hash), PKT_HASH_TYPE_L3);
+ break;
+ default:
+ skb_set_hash(skb, be32_to_cpu(rx_hash->hash), PKT_HASH_TYPE_L4);
+ break;
+ }
+}
+
+/**
+ * nfp_net_rx() - receive up to @budget packets on @rx_ring
+ * @rx_ring: RX ring to receive from
+ * @budget: NAPI budget
+ *
+ * Note, this function is separated out from the napi poll function to
+ * more cleanly separate packet receive code from other bookkeeping
+ * functions performed in the napi poll function.
+ *
+ * There are differences between the NFP-3200 firmware and the
+ * NFP-6000 firmware. The NFP-3200 firmware uses a dedicated RX queue
+ * to indicate that new packets have arrived. The NFP-6000 does not
+ * have this queue and uses the DD bit in the RX descriptor. This
+ * method cannot be used on the NFP-3200 as it causes a race
+ * condition: The RX ring write pointer on the NFP-3200 is updated
+ * after packets (and descriptors) have been DMAed. If the DD bit is
+ * used and subsequently the read pointer is updated this may lead to
+ * the RX queue to underflow (if the firmware has not yet update the
+ * write pointer). Therefore we use slightly ugly conditional code
+ * below to handle the differences. We may, in the future update the
+ * NFP-3200 firmware to behave the same as the firmware on the
+ * NFP-6000.
+ *
+ * Return: Number of packets received.
+ */
+static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget)
+{
+ struct nfp_net_r_vector *r_vec = rx_ring->r_vec;
+ struct nfp_net *nn = r_vec->nfp_net;
+ unsigned int data_len, meta_len;
+ int avail = 0, pkts_polled = 0;
+ struct sk_buff *skb, *new_skb;
+ struct nfp_net_rx_desc *rxd;
+ dma_addr_t new_dma_addr;
+ u32 qcp_wr_p;
+ int idx;
+
+ if (nn->is_nfp3200) {
+ /* Work out how many packets arrived */
+ qcp_wr_p = nfp_qcp_wr_ptr_read(rx_ring->qcp_rx);
+ idx = rx_ring->rd_p % rx_ring->cnt;
+
+ if (qcp_wr_p == idx)
+ /* No new packets */
+ return 0;
+
+ if (qcp_wr_p > idx)
+ avail = qcp_wr_p - idx;
+ else
+ avail = qcp_wr_p + rx_ring->cnt - idx;
+ } else {
+ avail = budget + 1;
+ }
+
+ while (avail > 0 && pkts_polled < budget) {
+ idx = rx_ring->rd_p % rx_ring->cnt;
+
+ rxd = &rx_ring->rxds[idx];
+ if (!(rxd->rxd.meta_len_dd & PCIE_DESC_RX_DD)) {
+ if (nn->is_nfp3200)
+ nn_dbg(nn, "RX descriptor not valid (DD)%d:%u rxd[0]=%#x rxd[1]=%#x\n",
+ rx_ring->idx, idx,
+ rxd->vals[0], rxd->vals[1]);
+ break;
+ }
+ /* Memory barrier to ensure that we won't do other reads
+ * before the DD bit.
+ */
+ dma_rmb();
+
+ rx_ring->rd_p++;
+ pkts_polled++;
+ avail--;
+
+ skb = rx_ring->rxbufs[idx].skb;
+
+ new_skb = nfp_net_rx_alloc_one(rx_ring, &new_dma_addr);
+ if (!new_skb) {
+ nfp_net_rx_give_one(rx_ring, rx_ring->rxbufs[idx].skb,
+ rx_ring->rxbufs[idx].dma_addr);
+ u64_stats_update_begin(&r_vec->rx_sync);
+ r_vec->rx_drops++;
+ u64_stats_update_end(&r_vec->rx_sync);
+ continue;
+ }
+
+ dma_unmap_single(&nn->pdev->dev,
+ rx_ring->rxbufs[idx].dma_addr,
+ nn->fl_bufsz, DMA_FROM_DEVICE);
+
+ nfp_net_rx_give_one(rx_ring, new_skb, new_dma_addr);
+
+ meta_len = rxd->rxd.meta_len_dd & PCIE_DESC_RX_META_LEN_MASK;
+ data_len = le16_to_cpu(rxd->rxd.data_len);
+
+ if (WARN_ON_ONCE(data_len > nn->fl_bufsz)) {
+ dev_kfree_skb_any(skb);
+ continue;
+ }
+
+ if (nn->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) {
+ /* The packet data starts after the metadata */
+ skb_reserve(skb, meta_len);
+ } else {
+ /* The packet data starts at a fixed offset */
+ skb_reserve(skb, nn->rx_offset);
+ }
+
+ /* Adjust the SKB for the dynamic meta data pre-pended */
+ skb_put(skb, data_len - meta_len);
+
+ nfp_net_set_hash(nn->netdev, skb, rxd);
+
+ /* Pad small frames to minimum */
+ if (skb_put_padto(skb, 60))
+ break;
+
+ /* Stats update */
+ u64_stats_update_begin(&r_vec->rx_sync);
+ r_vec->rx_pkts++;
+ r_vec->rx_bytes += skb->len;
+ u64_stats_update_end(&r_vec->rx_sync);
+
+ skb_record_rx_queue(skb, rx_ring->idx);
+ skb->protocol = eth_type_trans(skb, nn->netdev);
+
+ nfp_net_rx_csum(nn, r_vec, rxd, skb);
+
+ if (rxd->rxd.flags & PCIE_DESC_RX_VLAN)
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+ le16_to_cpu(rxd->rxd.vlan));
+
+ napi_gro_receive(&rx_ring->r_vec->napi, skb);
+ }
+
+ if (nn->is_nfp3200)
+ nfp_qcp_rd_ptr_add(rx_ring->qcp_rx, pkts_polled);
+
+ return pkts_polled;
+}
+
+/**
+ * nfp_net_poll() - napi poll function
+ * @napi: NAPI structure
+ * @budget: NAPI budget
+ *
+ * Return: number of packets polled.
+ */
+static int nfp_net_poll(struct napi_struct *napi, int budget)
+{
+ struct nfp_net_r_vector *r_vec =
+ container_of(napi, struct nfp_net_r_vector, napi);
+ struct nfp_net_rx_ring *rx_ring = r_vec->rx_ring;
+ struct nfp_net_tx_ring *tx_ring = r_vec->tx_ring;
+ struct nfp_net *nn = r_vec->nfp_net;
+ struct netdev_queue *txq;
+ unsigned int pkts_polled;
+
+ tx_ring = &nn->tx_rings[rx_ring->idx];
+ txq = netdev_get_tx_queue(nn->netdev, tx_ring->idx);
+ nfp_net_tx_complete(tx_ring);
+
+ pkts_polled = nfp_net_rx(rx_ring, budget);
+
+ if (pkts_polled < budget) {
+ napi_complete_done(napi, pkts_polled);
+ nfp_net_irq_unmask(nn, r_vec->irq_idx);
+ }
+
+ return pkts_polled;
+}
+
+/* Setup and Configuration
+ */
+
+/**
+ * nfp_net_tx_ring_free() - Free resources allocated to a TX ring
+ * @tx_ring: TX ring to free
+ */
+static void nfp_net_tx_ring_free(struct nfp_net_tx_ring *tx_ring)
+{
+ struct nfp_net_r_vector *r_vec = tx_ring->r_vec;
+ struct nfp_net *nn = r_vec->nfp_net;
+ struct pci_dev *pdev = nn->pdev;
+
+ nn_writeq(nn, NFP_NET_CFG_TXR_ADDR(tx_ring->idx), 0);
+ nn_writeb(nn, NFP_NET_CFG_TXR_SZ(tx_ring->idx), 0);
+ nn_writeb(nn, NFP_NET_CFG_TXR_VEC(tx_ring->idx), 0);
+
+ kfree(tx_ring->txbufs);
+
+ if (tx_ring->txds)
+ dma_free_coherent(&pdev->dev, tx_ring->size,
+ tx_ring->txds, tx_ring->dma);
+
+ tx_ring->cnt = 0;
+ tx_ring->wr_p = 0;
+ tx_ring->rd_p = 0;
+ tx_ring->qcp_rd_p = 0;
+ tx_ring->wr_ptr_add = 0;
+
+ tx_ring->txbufs = NULL;
+ tx_ring->txds = NULL;
+ tx_ring->dma = 0;
+ tx_ring->size = 0;
+}
+
+/**
+ * nfp_net_tx_ring_alloc() - Allocate resource for a TX ring
+ * @tx_ring: TX Ring structure to allocate
+ *
+ * Return: 0 on success, negative errno otherwise.
+ */
+static int nfp_net_tx_ring_alloc(struct nfp_net_tx_ring *tx_ring)
+{
+ struct nfp_net_r_vector *r_vec = tx_ring->r_vec;
+ struct nfp_net *nn = r_vec->nfp_net;
+ struct pci_dev *pdev = nn->pdev;
+ int sz;
+
+ tx_ring->cnt = nn->txd_cnt;
+
+ tx_ring->size = sizeof(*tx_ring->txds) * tx_ring->cnt;
+ tx_ring->txds = dma_zalloc_coherent(&pdev->dev, tx_ring->size,
+ &tx_ring->dma, GFP_KERNEL);
+ if (!tx_ring->txds)
+ goto err_alloc;
+
+ sz = sizeof(*tx_ring->txbufs) * tx_ring->cnt;
+ tx_ring->txbufs = kzalloc(sz, GFP_KERNEL);
+ if (!tx_ring->txbufs)
+ goto err_alloc;
+
+ /* Write the DMA address, size and MSI-X info to the device */
+ nn_writeq(nn, NFP_NET_CFG_TXR_ADDR(tx_ring->idx), tx_ring->dma);
+ nn_writeb(nn, NFP_NET_CFG_TXR_SZ(tx_ring->idx), ilog2(tx_ring->cnt));
+ nn_writeb(nn, NFP_NET_CFG_TXR_VEC(tx_ring->idx), r_vec->irq_idx);
+
+ netif_set_xps_queue(nn->netdev, &r_vec->affinity_mask, tx_ring->idx);
+
+ nn_dbg(nn, "TxQ%02d: QCidx=%02d cnt=%d dma=%#llx host=%p\n",
+ tx_ring->idx, tx_ring->qcidx,
+ tx_ring->cnt, (unsigned long long)tx_ring->dma, tx_ring->txds);
+
+ return 0;
+
+err_alloc:
+ nfp_net_tx_ring_free(tx_ring);
+ return -ENOMEM;
+}
+
+/**
+ * nfp_net_rx_ring_free() - Free resources allocated to a RX ring
+ * @rx_ring: RX ring to free
+ */
+static void nfp_net_rx_ring_free(struct nfp_net_rx_ring *rx_ring)
+{
+ struct nfp_net_r_vector *r_vec = rx_ring->r_vec;
+ struct nfp_net *nn = r_vec->nfp_net;
+ struct pci_dev *pdev = nn->pdev;
+
+ nn_writeq(nn, NFP_NET_CFG_RXR_ADDR(rx_ring->idx), 0);
+ nn_writeb(nn, NFP_NET_CFG_RXR_SZ(rx_ring->idx), 0);
+ nn_writeb(nn, NFP_NET_CFG_RXR_VEC(rx_ring->idx), 0);
+
+ kfree(rx_ring->rxbufs);
+
+ if (rx_ring->rxds)
+ dma_free_coherent(&pdev->dev, rx_ring->size,
+ rx_ring->rxds, rx_ring->dma);
+
+ rx_ring->cnt = 0;
+ rx_ring->wr_p = 0;
+ rx_ring->rd_p = 0;
+ rx_ring->wr_ptr_add = 0;
+
+ rx_ring->rxbufs = NULL;
+ rx_ring->rxds = NULL;
+ rx_ring->dma = 0;
+ rx_ring->size = 0;
+}
+
+/**
+ * nfp_net_rx_ring_alloc() - Allocate resource for a RX ring
+ * @rx_ring: RX ring to allocate
+ *
+ * Return: 0 on success, negative errno otherwise.
+ */
+static int nfp_net_rx_ring_alloc(struct nfp_net_rx_ring *rx_ring)
+{
+ struct nfp_net_r_vector *r_vec = rx_ring->r_vec;
+ struct nfp_net *nn = r_vec->nfp_net;
+ struct pci_dev *pdev = nn->pdev;
+ int sz;
+
+ rx_ring->cnt = nn->rxd_cnt;
+
+ rx_ring->size = sizeof(*rx_ring->rxds) * rx_ring->cnt;
+ rx_ring->rxds = dma_zalloc_coherent(&pdev->dev, rx_ring->size,
+ &rx_ring->dma, GFP_KERNEL);
+ if (!rx_ring->rxds)
+ goto err_alloc;
+
+ sz = sizeof(*rx_ring->rxbufs) * rx_ring->cnt;
+ rx_ring->rxbufs = kzalloc(sz, GFP_KERNEL);
+ if (!rx_ring->rxbufs)
+ goto err_alloc;
+
+ /* Write the DMA address, size and MSI-X info to the device */
+ nn_writeq(nn, NFP_NET_CFG_RXR_ADDR(rx_ring->idx), rx_ring->dma);
+ nn_writeb(nn, NFP_NET_CFG_RXR_SZ(rx_ring->idx), ilog2(rx_ring->cnt));
+ nn_writeb(nn, NFP_NET_CFG_RXR_VEC(rx_ring->idx), r_vec->irq_idx);
+
+ nn_dbg(nn, "RxQ%02d: FlQCidx=%02d RxQCidx=%02d cnt=%d dma=%#llx host=%p\n",
+ rx_ring->idx, rx_ring->fl_qcidx, rx_ring->rx_qcidx,
+ rx_ring->cnt, (unsigned long long)rx_ring->dma, rx_ring->rxds);
+
+ return 0;
+
+err_alloc:
+ nfp_net_rx_ring_free(rx_ring);
+ return -ENOMEM;
+}
+
+static void __nfp_net_free_rings(struct nfp_net *nn, unsigned int n_free)
+{
+ struct nfp_net_r_vector *r_vec;
+ struct msix_entry *entry;
+
+ while (n_free--) {
+ r_vec = &nn->r_vecs[n_free];
+ entry = &nn->irq_entries[r_vec->irq_idx];
+
+ nfp_net_rx_ring_free(r_vec->rx_ring);
+ nfp_net_tx_ring_free(r_vec->tx_ring);
+
+ irq_set_affinity_hint(entry->vector, NULL);
+ free_irq(entry->vector, r_vec);
+
+ netif_napi_del(&r_vec->napi);
+ }
+}
+
+/**
+ * nfp_net_free_rings() - Free all ring resources
+ * @nn: NFP Net device to reconfigure
+ */
+static void nfp_net_free_rings(struct nfp_net *nn)
+{
+ __nfp_net_free_rings(nn, nn->num_r_vecs);
+}
+
+/**
+ * nfp_net_alloc_rings() - Allocate resources for RX and TX rings
+ * @nn: NFP Net device to reconfigure
+ *
+ * Return: 0 on success or negative errno on error.
+ */
+static int nfp_net_alloc_rings(struct nfp_net *nn)
+{
+ struct nfp_net_r_vector *r_vec;
+ struct msix_entry *entry;
+ int err;
+ int r;
+
+ for (r = 0; r < nn->num_r_vecs; r++) {
+ r_vec = &nn->r_vecs[r];
+ entry = &nn->irq_entries[r_vec->irq_idx];
+
+ /* Setup NAPI */
+ netif_napi_add(nn->netdev, &r_vec->napi,
+ nfp_net_poll, NAPI_POLL_WEIGHT);
+
+ snprintf(r_vec->name, sizeof(r_vec->name),
+ "%s-rxtx-%d", nn->netdev->name, r);
+ err = request_irq(entry->vector, r_vec->handler, 0,
+ r_vec->name, r_vec);
+ if (err) {
+ nn_dbg(nn, "Error requesting IRQ %d\n", entry->vector);
+ goto err_napi_del;
+ }
+
+ irq_set_affinity_hint(entry->vector, &r_vec->affinity_mask);
+
+ nn_dbg(nn, "RV%02d: irq=%03d/%03d\n",
+ r, entry->vector, entry->entry);
+
+ /* Allocate TX ring resources */
+ err = nfp_net_tx_ring_alloc(r_vec->tx_ring);
+ if (err)
+ goto err_free_irq;
+
+ /* Allocate RX ring resources */
+ err = nfp_net_rx_ring_alloc(r_vec->rx_ring);
+ if (err)
+ goto err_free_tx;
+ }
+
+ return 0;
+
+err_free_tx:
+ nfp_net_tx_ring_free(r_vec->tx_ring);
+err_free_irq:
+ irq_set_affinity_hint(entry->vector, NULL);
+ free_irq(entry->vector, r_vec);
+err_napi_del:
+ netif_napi_del(&r_vec->napi);
+ __nfp_net_free_rings(nn, r);
+ return err;
+}
+
+/**
+ * nfp_net_rss_write_itbl() - Write RSS indirection table to device
+ * @nn: NFP Net device to reconfigure
+ */
+void nfp_net_rss_write_itbl(struct nfp_net *nn)
+{
+ int i;
+
+ for (i = 0; i < NFP_NET_CFG_RSS_ITBL_SZ; i += 4)
+ nn_writel(nn, NFP_NET_CFG_RSS_ITBL + i,
+ get_unaligned_le32(nn->rss_itbl + i));
+}
+
+/**
+ * nfp_net_rss_write_key() - Write RSS hash key to device
+ * @nn: NFP Net device to reconfigure
+ */
+void nfp_net_rss_write_key(struct nfp_net *nn)
+{
+ int i;
+
+ for (i = 0; i < NFP_NET_CFG_RSS_KEY_SZ; i += 4)
+ nn_writel(nn, NFP_NET_CFG_RSS_KEY + i,
+ get_unaligned_le32(nn->rss_key + i));
+}
+
+/**
+ * nfp_net_coalesce_write_cfg() - Write irq coalescence configuration to HW
+ * @nn: NFP Net device to reconfigure
+ */
+void nfp_net_coalesce_write_cfg(struct nfp_net *nn)
+{
+ u8 i;
+ u32 factor;
+ u32 value;
+
+ /* Compute factor used to convert coalesce '_usecs' parameters to
+ * ME timestamp ticks. There are 16 ME clock cycles for each timestamp
+ * count.
+ */
+ factor = nn->me_freq_mhz / 16;
+
+ /* copy RX interrupt coalesce parameters */
+ value = (nn->rx_coalesce_max_frames << 16) |
+ (factor * nn->rx_coalesce_usecs);
+ for (i = 0; i < nn->num_r_vecs; i++)
+ nn_writel(nn, NFP_NET_CFG_RXR_IRQ_MOD(i), value);
+
+ /* copy TX interrupt coalesce parameters */
+ value = (nn->tx_coalesce_max_frames << 16) |
+ (factor * nn->tx_coalesce_usecs);
+ for (i = 0; i < nn->num_r_vecs; i++)
+ nn_writel(nn, NFP_NET_CFG_TXR_IRQ_MOD(i), value);
+}
+
+/**
+ * nfp_net_write_mac_addr() - Write mac address to device registers
+ * @nn: NFP Net device to reconfigure
+ * @mac: Six-byte MAC address to be written
+ *
+ * We do a bit of byte swapping dance because firmware is LE.
+ */
+static void nfp_net_write_mac_addr(struct nfp_net *nn, const u8 *mac)
+{
+ nn_writel(nn, NFP_NET_CFG_MACADDR + 0,
+ get_unaligned_be32(nn->netdev->dev_addr));
+ /* We can't do writew for NFP-3200 compatibility */
+ nn_writel(nn, NFP_NET_CFG_MACADDR + 4,
+ get_unaligned_be16(nn->netdev->dev_addr + 4) << 16);
+}
+
+/**
+ * nfp_net_clear_config_and_disable() - Clear control BAR and disable NFP
+ * @nn: NFP Net device to reconfigure
+ */
+static void nfp_net_clear_config_and_disable(struct nfp_net *nn)
+{
+ u32 new_ctrl, update;
+ int err;
+
+ new_ctrl = nn->ctrl;
+ new_ctrl &= ~NFP_NET_CFG_CTRL_ENABLE;
+ update = NFP_NET_CFG_UPDATE_GEN;
+ update |= NFP_NET_CFG_UPDATE_MSIX;
+ update |= NFP_NET_CFG_UPDATE_RING;
+
+ if (nn->cap & NFP_NET_CFG_CTRL_RINGCFG)
+ new_ctrl &= ~NFP_NET_CFG_CTRL_RINGCFG;
+
+ nn_writeq(nn, NFP_NET_CFG_TXRS_ENABLE, 0);
+ nn_writeq(nn, NFP_NET_CFG_RXRS_ENABLE, 0);
+
+ nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl);
+ err = nfp_net_reconfig(nn, update);
+ if (err) {
+ nn_err(nn, "Could not disable device: %d\n", err);
+ return;
+ }
+
+ nn->ctrl = new_ctrl;
+}
+
+/**
+ * nfp_net_start_vec() - Start ring vector
+ * @nn: NFP Net device structure
+ * @r_vec: Ring vector to be started
+ */
+static int nfp_net_start_vec(struct nfp_net *nn, struct nfp_net_r_vector *r_vec)
+{
+ unsigned int irq_vec;
+ int err = 0;
+
+ irq_vec = nn->irq_entries[r_vec->irq_idx].vector;
+
+ disable_irq(irq_vec);
+
+ err = nfp_net_rx_fill_freelist(r_vec->rx_ring);
+ if (err) {
+ nn_err(nn, "RV%02d: couldn't allocate enough buffers\n",
+ r_vec->irq_idx);
+ goto out;
+ }
+
+ napi_enable(&r_vec->napi);
+out:
+ enable_irq(irq_vec);
+
+ return err;
+}
+
+static int nfp_net_netdev_open(struct net_device *netdev)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ int err, r;
+ u32 update = 0;
+ u32 new_ctrl;
+
+ if (nn->ctrl & NFP_NET_CFG_CTRL_ENABLE) {
+ nn_err(nn, "Dev is already enabled: 0x%08x\n", nn->ctrl);
+ return -EBUSY;
+ }
+
+ new_ctrl = nn->ctrl;
+
+ /* Step 1: Allocate resources for rings and the like
+ * - Request interrupts
+ * - Allocate RX and TX ring resources
+ * - Setup initial RSS table
+ */
+ err = nfp_net_aux_irq_request(nn, NFP_NET_CFG_EXN, "%s-exn",
+ nn->exn_name, sizeof(nn->exn_name),
+ NFP_NET_IRQ_EXN_IDX, nn->exn_handler);
+ if (err)
+ return err;
+
+ err = nfp_net_alloc_rings(nn);
+ if (err)
+ goto err_free_exn;
+
+ err = netif_set_real_num_tx_queues(netdev, nn->num_tx_rings);
+ if (err)
+ goto err_free_rings;
+
+ err = netif_set_real_num_rx_queues(netdev, nn->num_rx_rings);
+ if (err)
+ goto err_free_rings;
+
+ if (nn->cap & NFP_NET_CFG_CTRL_RSS) {
+ nfp_net_rss_write_key(nn);
+ nfp_net_rss_write_itbl(nn);
+ nn_writel(nn, NFP_NET_CFG_RSS_CTRL, nn->rss_cfg);
+ update |= NFP_NET_CFG_UPDATE_RSS;
+ }
+
+ if (nn->cap & NFP_NET_CFG_CTRL_IRQMOD) {
+ nfp_net_coalesce_write_cfg(nn);
+
+ new_ctrl |= NFP_NET_CFG_CTRL_IRQMOD;
+ update |= NFP_NET_CFG_UPDATE_IRQMOD;
+ }
+
+ /* Step 2: Configure the NFP
+ * - Enable rings from 0 to tx_rings/rx_rings - 1.
+ * - Write MAC address (in case it changed)
+ * - Set the MTU
+ * - Set the Freelist buffer size
+ * - Enable the FW
+ */
+ nn_writeq(nn, NFP_NET_CFG_TXRS_ENABLE, nn->num_tx_rings == 64 ?
+ 0xffffffffffffffffULL : ((u64)1 << nn->num_tx_rings) - 1);
+
+ nn_writeq(nn, NFP_NET_CFG_RXRS_ENABLE, nn->num_rx_rings == 64 ?
+ 0xffffffffffffffffULL : ((u64)1 << nn->num_rx_rings) - 1);
+
+ nfp_net_write_mac_addr(nn, netdev->dev_addr);
+
+ nn_writel(nn, NFP_NET_CFG_MTU, netdev->mtu);
+ nn_writel(nn, NFP_NET_CFG_FLBUFSZ, nn->fl_bufsz);
+
+ /* Enable device */
+ new_ctrl |= NFP_NET_CFG_CTRL_ENABLE;
+ update |= NFP_NET_CFG_UPDATE_GEN;
+ update |= NFP_NET_CFG_UPDATE_MSIX;
+ update |= NFP_NET_CFG_UPDATE_RING;
+ if (nn->cap & NFP_NET_CFG_CTRL_RINGCFG)
+ new_ctrl |= NFP_NET_CFG_CTRL_RINGCFG;
+
+ nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl);
+ err = nfp_net_reconfig(nn, update);
+ if (err)
+ goto err_clear_config;
+
+ nn->ctrl = new_ctrl;
+
+ /* Since reconfiguration requests while NFP is down are ignored we
+ * have to wipe the entire VXLAN configuration and reinitialize it.
+ */
+ if (nn->ctrl & NFP_NET_CFG_CTRL_VXLAN) {
+ memset(&nn->vxlan_ports, 0, sizeof(nn->vxlan_ports));
+ memset(&nn->vxlan_usecnt, 0, sizeof(nn->vxlan_usecnt));
+ vxlan_get_rx_port(netdev);
+ }
+
+ /* Step 3: Enable for kernel
+ * - put some freelist descriptors on each RX ring
+ * - enable NAPI on each ring
+ * - enable all TX queues
+ * - set link state
+ */
+ for (r = 0; r < nn->num_r_vecs; r++) {
+ err = nfp_net_start_vec(nn, &nn->r_vecs[r]);
+ if (err)
+ goto err_disable_napi;
+ }
+
+ netif_tx_wake_all_queues(netdev);
+
+ err = nfp_net_aux_irq_request(nn, NFP_NET_CFG_LSC, "%s-lsc",
+ nn->lsc_name, sizeof(nn->lsc_name),
+ NFP_NET_IRQ_LSC_IDX, nn->lsc_handler);
+ if (err)
+ goto err_stop_tx;
+ nfp_net_read_link_status(nn);
+
+ return 0;
+
+err_stop_tx:
+ netif_tx_disable(netdev);
+ for (r = 0; r < nn->num_r_vecs; r++)
+ nfp_net_tx_flush(nn->r_vecs[r].tx_ring);
+err_disable_napi:
+ while (r--) {
+ napi_disable(&nn->r_vecs[r].napi);
+ nfp_net_rx_flush(nn->r_vecs[r].rx_ring);
+ }
+err_clear_config:
+ nfp_net_clear_config_and_disable(nn);
+err_free_rings:
+ nfp_net_free_rings(nn);
+err_free_exn:
+ nfp_net_aux_irq_free(nn, NFP_NET_CFG_EXN, NFP_NET_IRQ_EXN_IDX);
+ return err;
+}
+
+/**
+ * nfp_net_netdev_close() - Called when the device is downed
+ * @netdev: netdev structure
+ */
+static int nfp_net_netdev_close(struct net_device *netdev)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ int r;
+
+ if (!(nn->ctrl & NFP_NET_CFG_CTRL_ENABLE)) {
+ nn_err(nn, "Dev is not up: 0x%08x\n", nn->ctrl);
+ return 0;
+ }
+
+ /* Step 1: Disable RX and TX rings from the Linux kernel perspective
+ */
+ nfp_net_aux_irq_free(nn, NFP_NET_CFG_LSC, NFP_NET_IRQ_LSC_IDX);
+ netif_carrier_off(netdev);
+ nn->link_up = false;
+
+ for (r = 0; r < nn->num_r_vecs; r++)
+ napi_disable(&nn->r_vecs[r].napi);
+
+ netif_tx_disable(netdev);
+
+ /* Step 2: Tell NFP
+ */
+ nfp_net_clear_config_and_disable(nn);
+
+ /* Step 3: Free resources
+ */
+ for (r = 0; r < nn->num_r_vecs; r++) {
+ nfp_net_rx_flush(nn->r_vecs[r].rx_ring);
+ nfp_net_tx_flush(nn->r_vecs[r].tx_ring);
+ }
+
+ nfp_net_free_rings(nn);
+ nfp_net_aux_irq_free(nn, NFP_NET_CFG_EXN, NFP_NET_IRQ_EXN_IDX);
+
+ nn_dbg(nn, "%s down", netdev->name);
+ return 0;
+}
+
+static void nfp_net_set_rx_mode(struct net_device *netdev)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ u32 new_ctrl;
+
+ new_ctrl = nn->ctrl;
+
+ if (netdev->flags & IFF_PROMISC) {
+ if (nn->cap & NFP_NET_CFG_CTRL_PROMISC)
+ new_ctrl |= NFP_NET_CFG_CTRL_PROMISC;
+ else
+ nn_warn(nn, "FW does not support promiscuous mode\n");
+ } else {
+ new_ctrl &= ~NFP_NET_CFG_CTRL_PROMISC;
+ }
+
+ if (new_ctrl == nn->ctrl)
+ return;
+
+ nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl);
+ if (nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_GEN))
+ return;
+
+ nn->ctrl = new_ctrl;
+}
+
+static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ u32 tmp;
+
+ nn_dbg(nn, "New MTU = %d\n", new_mtu);
+
+ if (new_mtu < 68 || new_mtu > nn->max_mtu) {
+ nn_err(nn, "New MTU (%d) is not valid\n", new_mtu);
+ return -EINVAL;
+ }
+
+ netdev->mtu = new_mtu;
+
+ /* Freelist buffer size rounded up to the nearest 1K */
+ tmp = new_mtu + ETH_HLEN + VLAN_HLEN + NFP_NET_MAX_PREPEND;
+ nn->fl_bufsz = roundup(tmp, 1024);
+
+ /* restart if running */
+ if (netif_running(netdev)) {
+ nfp_net_netdev_close(netdev);
+ nfp_net_netdev_open(netdev);
+ }
+
+ return 0;
+}
+
+static struct rtnl_link_stats64 *nfp_net_stat64(struct net_device *netdev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ int r;
+
+ for (r = 0; r < nn->num_r_vecs; r++) {
+ struct nfp_net_r_vector *r_vec = &nn->r_vecs[r];
+ u64 data[3];
+ unsigned int start;
+
+ do {
+ start = u64_stats_fetch_begin(&r_vec->rx_sync);
+ data[0] = r_vec->rx_pkts;
+ data[1] = r_vec->rx_bytes;
+ data[2] = r_vec->rx_drops;
+ } while (u64_stats_fetch_retry(&r_vec->rx_sync, start));
+ stats->rx_packets += data[0];
+ stats->rx_bytes += data[1];
+ stats->rx_dropped += data[2];
+
+ do {
+ start = u64_stats_fetch_begin(&r_vec->tx_sync);
+ data[0] = r_vec->tx_pkts;
+ data[1] = r_vec->tx_bytes;
+ data[2] = r_vec->tx_errors;
+ } while (u64_stats_fetch_retry(&r_vec->tx_sync, start));
+ stats->tx_packets += data[0];
+ stats->tx_bytes += data[1];
+ stats->tx_errors += data[2];
+ }
+
+ return stats;
+}
+
+static int nfp_net_set_features(struct net_device *netdev,
+ netdev_features_t features)
+{
+ netdev_features_t changed = netdev->features ^ features;
+ struct nfp_net *nn = netdev_priv(netdev);
+ u32 new_ctrl;
+ int err;
+
+ /* Assume this is not called with features we have not advertised */
+
+ new_ctrl = nn->ctrl;
+
+ if (changed & NETIF_F_RXCSUM) {
+ if (features & NETIF_F_RXCSUM)
+ new_ctrl |= NFP_NET_CFG_CTRL_RXCSUM;
+ else
+ new_ctrl &= ~NFP_NET_CFG_CTRL_RXCSUM;
+ }
+
+ if (changed & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) {
+ if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))
+ new_ctrl |= NFP_NET_CFG_CTRL_TXCSUM;
+ else
+ new_ctrl &= ~NFP_NET_CFG_CTRL_TXCSUM;
+ }
+
+ if (changed & (NETIF_F_TSO | NETIF_F_TSO6)) {
+ if (features & (NETIF_F_TSO | NETIF_F_TSO6))
+ new_ctrl |= NFP_NET_CFG_CTRL_LSO;
+ else
+ new_ctrl &= ~NFP_NET_CFG_CTRL_LSO;
+ }
+
+ if (changed & NETIF_F_HW_VLAN_CTAG_RX) {
+ if (features & NETIF_F_HW_VLAN_CTAG_RX)
+ new_ctrl |= NFP_NET_CFG_CTRL_RXVLAN;
+ else
+ new_ctrl &= ~NFP_NET_CFG_CTRL_RXVLAN;
+ }
+
+ if (changed & NETIF_F_HW_VLAN_CTAG_TX) {
+ if (features & NETIF_F_HW_VLAN_CTAG_TX)
+ new_ctrl |= NFP_NET_CFG_CTRL_TXVLAN;
+ else
+ new_ctrl &= ~NFP_NET_CFG_CTRL_TXVLAN;
+ }
+
+ if (changed & NETIF_F_SG) {
+ if (features & NETIF_F_SG)
+ new_ctrl |= NFP_NET_CFG_CTRL_GATHER;
+ else
+ new_ctrl &= ~NFP_NET_CFG_CTRL_GATHER;
+ }
+
+ nn_dbg(nn, "Feature change 0x%llx -> 0x%llx (changed=0x%llx)\n",
+ netdev->features, features, changed);
+
+ if (new_ctrl == nn->ctrl)
+ return 0;
+
+ nn_dbg(nn, "NIC ctrl: 0x%x -> 0x%x\n", nn->ctrl, new_ctrl);
+ nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl);
+ err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_GEN);
+ if (err)
+ return err;
+
+ nn->ctrl = new_ctrl;
+
+ return 0;
+}
+
+static netdev_features_t
+nfp_net_features_check(struct sk_buff *skb, struct net_device *dev,
+ netdev_features_t features)
+{
+ u8 l4_hdr;
+
+ /* We can't do TSO over double tagged packets (802.1AD) */
+ features &= vlan_features_check(skb, features);
+
+ if (!skb->encapsulation)
+ return features;
+
+ /* Ensure that inner L4 header offset fits into TX descriptor field */
+ if (skb_is_gso(skb)) {
+ u32 hdrlen;
+
+ hdrlen = skb_inner_transport_header(skb) - skb->data +
+ inner_tcp_hdrlen(skb);
+
+ if (unlikely(hdrlen > NFP_NET_LSO_MAX_HDR_SZ))
+ features &= ~NETIF_F_GSO_MASK;
+ }
+
+ /* VXLAN/GRE check */
+ switch (vlan_get_protocol(skb)) {
+ case htons(ETH_P_IP):
+ l4_hdr = ip_hdr(skb)->protocol;
+ break;
+ case htons(ETH_P_IPV6):
+ l4_hdr = ipv6_hdr(skb)->nexthdr;
+ break;
+ default:
+ return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
+ }
+
+ if (skb->inner_protocol_type != ENCAP_TYPE_ETHER ||
+ skb->inner_protocol != htons(ETH_P_TEB) ||
+ (l4_hdr != IPPROTO_UDP && l4_hdr != IPPROTO_GRE) ||
+ (l4_hdr == IPPROTO_UDP &&
+ (skb_inner_mac_header(skb) - skb_transport_header(skb) !=
+ sizeof(struct udphdr) + sizeof(struct vxlanhdr))))
+ return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
+
+ return features;
+}
+
+/**
+ * nfp_net_set_vxlan_port() - set vxlan port in SW and reconfigure HW
+ * @nn: NFP Net device to reconfigure
+ * @idx: Index into the port table where new port should be written
+ * @port: UDP port to configure (pass zero to remove VXLAN port)
+ */
+static void nfp_net_set_vxlan_port(struct nfp_net *nn, int idx, __be16 port)
+{
+ int i;
+
+ nn->vxlan_ports[idx] = port;
+
+ if (!(nn->ctrl & NFP_NET_CFG_CTRL_VXLAN))
+ return;
+
+ BUILD_BUG_ON(NFP_NET_N_VXLAN_PORTS & 1);
+ for (i = 0; i < NFP_NET_N_VXLAN_PORTS; i += 2)
+ nn_writel(nn, NFP_NET_CFG_VXLAN_PORT + i * sizeof(port),
+ be16_to_cpu(nn->vxlan_ports[i + 1]) << 16 |
+ be16_to_cpu(nn->vxlan_ports[i]));
+
+ nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_VXLAN);
+}
+
+/**
+ * nfp_net_find_vxlan_idx() - find table entry of the port or a free one
+ * @nn: NFP Network structure
+ * @port: UDP port to look for
+ *
+ * Return: if the port is already in the table -- it's position;
+ * if the port is not in the table -- free position to use;
+ * if the table is full -- -ENOSPC.
+ */
+static int nfp_net_find_vxlan_idx(struct nfp_net *nn, __be16 port)
+{
+ int i, free_idx = -ENOSPC;
+
+ for (i = 0; i < NFP_NET_N_VXLAN_PORTS; i++) {
+ if (nn->vxlan_ports[i] == port)
+ return i;
+ if (!nn->vxlan_usecnt[i])
+ free_idx = i;
+ }
+
+ return free_idx;
+}
+
+static void nfp_net_add_vxlan_port(struct net_device *netdev,
+ sa_family_t sa_family, __be16 port)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ int idx;
+
+ idx = nfp_net_find_vxlan_idx(nn, port);
+ if (idx == -ENOSPC)
+ return;
+
+ if (!nn->vxlan_usecnt[idx]++)
+ nfp_net_set_vxlan_port(nn, idx, port);
+}
+
+static void nfp_net_del_vxlan_port(struct net_device *netdev,
+ sa_family_t sa_family, __be16 port)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ int idx;
+
+ idx = nfp_net_find_vxlan_idx(nn, port);
+ if (!nn->vxlan_usecnt[idx] || idx == -ENOSPC)
+ return;
+
+ if (!--nn->vxlan_usecnt[idx])
+ nfp_net_set_vxlan_port(nn, idx, 0);
+}
+
+static const struct net_device_ops nfp_net_netdev_ops = {
+ .ndo_open = nfp_net_netdev_open,
+ .ndo_stop = nfp_net_netdev_close,
+ .ndo_start_xmit = nfp_net_tx,
+ .ndo_get_stats64 = nfp_net_stat64,
+ .ndo_tx_timeout = nfp_net_tx_timeout,
+ .ndo_set_rx_mode = nfp_net_set_rx_mode,
+ .ndo_change_mtu = nfp_net_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_set_features = nfp_net_set_features,
+ .ndo_features_check = nfp_net_features_check,
+ .ndo_add_vxlan_port = nfp_net_add_vxlan_port,
+ .ndo_del_vxlan_port = nfp_net_del_vxlan_port,
+};
+
+/**
+ * nfp_net_info() - Print general info about the NIC
+ * @nn: NFP Net device to reconfigure
+ */
+void nfp_net_info(struct nfp_net *nn)
+{
+ nn_info(nn, "Netronome %s %sNetdev: TxQs=%d/%d RxQs=%d/%d\n",
+ nn->is_nfp3200 ? "NFP-32xx" : "NFP-6xxx",
+ nn->is_vf ? "VF " : "",
+ nn->num_tx_rings, nn->max_tx_rings,
+ nn->num_rx_rings, nn->max_rx_rings);
+ nn_info(nn, "VER: %d.%d.%d.%d, Maximum supported MTU: %d\n",
+ nn->fw_ver.resv, nn->fw_ver.class,
+ nn->fw_ver.major, nn->fw_ver.minor,
+ nn->max_mtu);
+ nn_info(nn, "CAP: %#x %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+ nn->cap,
+ nn->cap & NFP_NET_CFG_CTRL_PROMISC ? "PROMISC " : "",
+ nn->cap & NFP_NET_CFG_CTRL_L2BC ? "L2BCFILT " : "",
+ nn->cap & NFP_NET_CFG_CTRL_L2MC ? "L2MCFILT " : "",
+ nn->cap & NFP_NET_CFG_CTRL_RXCSUM ? "RXCSUM " : "",
+ nn->cap & NFP_NET_CFG_CTRL_TXCSUM ? "TXCSUM " : "",
+ nn->cap & NFP_NET_CFG_CTRL_RXVLAN ? "RXVLAN " : "",
+ nn->cap & NFP_NET_CFG_CTRL_TXVLAN ? "TXVLAN " : "",
+ nn->cap & NFP_NET_CFG_CTRL_SCATTER ? "SCATTER " : "",
+ nn->cap & NFP_NET_CFG_CTRL_GATHER ? "GATHER " : "",
+ nn->cap & NFP_NET_CFG_CTRL_LSO ? "TSO " : "",
+ nn->cap & NFP_NET_CFG_CTRL_RSS ? "RSS " : "",
+ nn->cap & NFP_NET_CFG_CTRL_L2SWITCH ? "L2SWITCH " : "",
+ nn->cap & NFP_NET_CFG_CTRL_MSIXAUTO ? "AUTOMASK " : "",
+ nn->cap & NFP_NET_CFG_CTRL_IRQMOD ? "IRQMOD " : "",
+ nn->cap & NFP_NET_CFG_CTRL_VXLAN ? "VXLAN " : "",
+ nn->cap & NFP_NET_CFG_CTRL_NVGRE ? "NVGRE " : "");
+}
+
+/**
+ * nfp_net_netdev_alloc() - Allocate netdev and related structure
+ * @pdev: PCI device
+ * @max_tx_rings: Maximum number of TX rings supported by device
+ * @max_rx_rings: Maximum number of RX rings supported by device
+ *
+ * This function allocates a netdev device and fills in the initial
+ * part of the @struct nfp_net structure.
+ *
+ * Return: NFP Net device structure, or ERR_PTR on error.
+ */
+struct nfp_net *nfp_net_netdev_alloc(struct pci_dev *pdev,
+ int max_tx_rings, int max_rx_rings)
+{
+ struct net_device *netdev;
+ struct nfp_net *nn;
+ int nqs;
+
+ netdev = alloc_etherdev_mqs(sizeof(struct nfp_net),
+ max_tx_rings, max_rx_rings);
+ if (!netdev)
+ return ERR_PTR(-ENOMEM);
+
+ SET_NETDEV_DEV(netdev, &pdev->dev);
+ nn = netdev_priv(netdev);
+
+ nn->netdev = netdev;
+ nn->pdev = pdev;
+
+ nn->max_tx_rings = max_tx_rings;
+ nn->max_rx_rings = max_rx_rings;
+
+ nqs = netif_get_num_default_rss_queues();
+ nn->num_tx_rings = min_t(int, nqs, max_tx_rings);
+ nn->num_rx_rings = min_t(int, nqs, max_rx_rings);
+
+ nn->txd_cnt = NFP_NET_TX_DESCS_DEFAULT;
+ nn->rxd_cnt = NFP_NET_RX_DESCS_DEFAULT;
+
+ spin_lock_init(&nn->reconfig_lock);
+ spin_lock_init(&nn->link_status_lock);
+
+ return nn;
+}
+
+/**
+ * nfp_net_netdev_free() - Undo what @nfp_net_netdev_alloc() did
+ * @nn: NFP Net device to reconfigure
+ */
+void nfp_net_netdev_free(struct nfp_net *nn)
+{
+ free_netdev(nn->netdev);
+}
+
+/**
+ * nfp_net_rss_init() - Set the initial RSS parameters
+ * @nn: NFP Net device to reconfigure
+ */
+static void nfp_net_rss_init(struct nfp_net *nn)
+{
+ int i;
+
+ netdev_rss_key_fill(nn->rss_key, NFP_NET_CFG_RSS_KEY_SZ);
+
+ for (i = 0; i < sizeof(nn->rss_itbl); i++)
+ nn->rss_itbl[i] =
+ ethtool_rxfh_indir_default(i, nn->num_rx_rings);
+
+ /* Enable IPv4/IPv6 TCP by default */
+ nn->rss_cfg = NFP_NET_CFG_RSS_IPV4_TCP |
+ NFP_NET_CFG_RSS_IPV6_TCP |
+ NFP_NET_CFG_RSS_TOEPLITZ |
+ NFP_NET_CFG_RSS_MASK;
+}
+
+/**
+ * nfp_net_irqmod_init() - Set the initial IRQ moderation parameters
+ * @nn: NFP Net device to reconfigure
+ */
+static void nfp_net_irqmod_init(struct nfp_net *nn)
+{
+ nn->rx_coalesce_usecs = 50;
+ nn->rx_coalesce_max_frames = 64;
+ nn->tx_coalesce_usecs = 50;
+ nn->tx_coalesce_max_frames = 64;
+}
+
+/**
+ * nfp_net_netdev_init() - Initialise/finalise the netdev structure
+ * @netdev: netdev structure
+ *
+ * Return: 0 on success or negative errno on error.
+ */
+int nfp_net_netdev_init(struct net_device *netdev)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ int err;
+
+ /* Get some of the read-only fields from the BAR */
+ nn->cap = nn_readl(nn, NFP_NET_CFG_CAP);
+ nn->max_mtu = nn_readl(nn, NFP_NET_CFG_MAX_MTU);
+
+ nfp_net_write_mac_addr(nn, nn->netdev->dev_addr);
+
+ /* Set default MTU and Freelist buffer size */
+ if (nn->max_mtu < NFP_NET_DEFAULT_MTU)
+ netdev->mtu = nn->max_mtu;
+ else
+ netdev->mtu = NFP_NET_DEFAULT_MTU;
+ nn->fl_bufsz = NFP_NET_DEFAULT_RX_BUFSZ;
+
+ /* Advertise/enable offloads based on capabilities
+ *
+ * Note: netdev->features show the currently enabled features
+ * and netdev->hw_features advertises which features are
+ * supported. By default we enable most features.
+ */
+ netdev->hw_features = NETIF_F_HIGHDMA;
+ if (nn->cap & NFP_NET_CFG_CTRL_RXCSUM) {
+ netdev->hw_features |= NETIF_F_RXCSUM;
+ nn->ctrl |= NFP_NET_CFG_CTRL_RXCSUM;
+ }
+ if (nn->cap & NFP_NET_CFG_CTRL_TXCSUM) {
+ netdev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
+ nn->ctrl |= NFP_NET_CFG_CTRL_TXCSUM;
+ }
+ if (nn->cap & NFP_NET_CFG_CTRL_GATHER) {
+ netdev->hw_features |= NETIF_F_SG;
+ nn->ctrl |= NFP_NET_CFG_CTRL_GATHER;
+ }
+ if ((nn->cap & NFP_NET_CFG_CTRL_LSO) && nn->fw_ver.major > 2) {
+ netdev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6;
+ nn->ctrl |= NFP_NET_CFG_CTRL_LSO;
+ }
+ if (nn->cap & NFP_NET_CFG_CTRL_RSS) {
+ netdev->hw_features |= NETIF_F_RXHASH;
+ nfp_net_rss_init(nn);
+ nn->ctrl |= NFP_NET_CFG_CTRL_RSS;
+ }
+ if (nn->cap & NFP_NET_CFG_CTRL_VXLAN &&
+ nn->cap & NFP_NET_CFG_CTRL_NVGRE) {
+ if (nn->cap & NFP_NET_CFG_CTRL_LSO)
+ netdev->hw_features |= NETIF_F_GSO_GRE |
+ NETIF_F_GSO_UDP_TUNNEL;
+ nn->ctrl |= NFP_NET_CFG_CTRL_VXLAN | NFP_NET_CFG_CTRL_NVGRE;
+
+ netdev->hw_enc_features = netdev->hw_features;
+ }
+
+ netdev->vlan_features = netdev->hw_features;
+
+ if (nn->cap & NFP_NET_CFG_CTRL_RXVLAN) {
+ netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
+ nn->ctrl |= NFP_NET_CFG_CTRL_RXVLAN;
+ }
+ if (nn->cap & NFP_NET_CFG_CTRL_TXVLAN) {
+ netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX;
+ nn->ctrl |= NFP_NET_CFG_CTRL_TXVLAN;
+ }
+
+ netdev->features = netdev->hw_features;
+
+ /* Advertise but disable TSO by default. */
+ netdev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6);
+
+ /* Allow L2 Broadcast and Multicast through by default, if supported */
+ if (nn->cap & NFP_NET_CFG_CTRL_L2BC)
+ nn->ctrl |= NFP_NET_CFG_CTRL_L2BC;
+ if (nn->cap & NFP_NET_CFG_CTRL_L2MC)
+ nn->ctrl |= NFP_NET_CFG_CTRL_L2MC;
+
+ /* Allow IRQ moderation, if supported */
+ if (nn->cap & NFP_NET_CFG_CTRL_IRQMOD) {
+ nfp_net_irqmod_init(nn);
+ nn->ctrl |= NFP_NET_CFG_CTRL_IRQMOD;
+ }
+
+ /* On NFP-3200 enable MSI-X auto-masking, if supported and the
+ * interrupts are not shared.
+ */
+ if (nn->is_nfp3200 && nn->cap & NFP_NET_CFG_CTRL_MSIXAUTO)
+ nn->ctrl |= NFP_NET_CFG_CTRL_MSIXAUTO;
+
+ /* On NFP4000/NFP6000, determine RX packet/metadata boundary offset */
+ if (nn->fw_ver.major >= 2)
+ nn->rx_offset = nn_readl(nn, NFP_NET_CFG_RX_OFFSET);
+ else
+ nn->rx_offset = NFP_NET_RX_OFFSET;
+
+ /* Stash the re-configuration queue away. First odd queue in TX Bar */
+ nn->qcp_cfg = nn->tx_bar + NFP_QCP_QUEUE_ADDR_SZ;
+
+ /* Make sure the FW knows the netdev is supposed to be disabled here */
+ nn_writel(nn, NFP_NET_CFG_CTRL, 0);
+ nn_writeq(nn, NFP_NET_CFG_TXRS_ENABLE, 0);
+ nn_writeq(nn, NFP_NET_CFG_RXRS_ENABLE, 0);
+ err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_RING |
+ NFP_NET_CFG_UPDATE_GEN);
+ if (err)
+ return err;
+
+ /* Finalise the netdev setup */
+ ether_setup(netdev);
+ netdev->netdev_ops = &nfp_net_netdev_ops;
+ netdev->watchdog_timeo = msecs_to_jiffies(5 * 1000);
+ netif_carrier_off(netdev);
+
+ nfp_net_set_ethtool_ops(netdev);
+ nfp_net_irqs_assign(netdev);
+
+ return register_netdev(netdev);
+}
+
+/**
+ * nfp_net_netdev_clean() - Undo what nfp_net_netdev_init() did.
+ * @netdev: netdev structure
+ */
+void nfp_net_netdev_clean(struct net_device *netdev)
+{
+ unregister_netdev(netdev);
+}
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h
new file mode 100644
index 000000000000..8692003aeed8
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2015 Netronome Systems, Inc.
+ *
+ * This software is dual licensed under the GNU General License Version 2,
+ * June 1991 as shown in the file COPYING in the top-level directory of this
+ * source tree or the BSD 2-Clause License provided below. You have the
+ * option to license this software under the complete terms of either license.
+ *
+ * The BSD 2-Clause 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, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 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.
+ */
+
+/*
+ * nfp_net_ctrl.h
+ * Netronome network device driver: Control BAR layout
+ * Authors: Jakub Kicinski <jakub.kicinski@netronome.com>
+ * Jason McMullan <jason.mcmullan@netronome.com>
+ * Rolf Neugebauer <rolf.neugebauer@netronome.com>
+ * Brad Petrus <brad.petrus@netronome.com>
+ */
+
+#ifndef _NFP_NET_CTRL_H_
+#define _NFP_NET_CTRL_H_
+
+/* IMPORTANT: This header file is shared with the FW,
+ * no OS specific constructs, please!
+ */
+
+/**
+ * Configuration BAR size.
+ *
+ * The configuration BAR is 8K in size, but on the NFP6000, due to
+ * THB-350, 32k needs to be reserved.
+ */
+#define NFP_NET_CFG_BAR_SZ (32 * 1024)
+
+/**
+ * Offset in Freelist buffer where packet starts on RX
+ */
+#define NFP_NET_RX_OFFSET 32
+
+/**
+ * Maximum header size supported for LSO frames
+ */
+#define NFP_NET_LSO_MAX_HDR_SZ 255
+
+/**
+ * Hash type pre-pended when a RSS hash was computed
+ */
+#define NFP_NET_RSS_NONE 0
+#define NFP_NET_RSS_IPV4 1
+#define NFP_NET_RSS_IPV6 2
+#define NFP_NET_RSS_IPV6_EX 3
+#define NFP_NET_RSS_IPV4_TCP 4
+#define NFP_NET_RSS_IPV6_TCP 5
+#define NFP_NET_RSS_IPV6_EX_TCP 6
+#define NFP_NET_RSS_IPV4_UDP 7
+#define NFP_NET_RSS_IPV6_UDP 8
+#define NFP_NET_RSS_IPV6_EX_UDP 9
+
+/**
+ * @NFP_NET_TXR_MAX: Maximum number of TX rings
+ * @NFP_NET_TXR_MASK: Mask for TX rings
+ * @NFP_NET_RXR_MAX: Maximum number of RX rings
+ * @NFP_NET_RXR_MASK: Mask for RX rings
+ */
+#define NFP_NET_TXR_MAX 64
+#define NFP_NET_TXR_MASK (NFP_NET_TXR_MAX - 1)
+#define NFP_NET_RXR_MAX 64
+#define NFP_NET_RXR_MASK (NFP_NET_RXR_MAX - 1)
+
+/**
+ * Read/Write config words (0x0000 - 0x002c)
+ * @NFP_NET_CFG_CTRL: Global control
+ * @NFP_NET_CFG_UPDATE: Indicate which fields are updated
+ * @NFP_NET_CFG_TXRS_ENABLE: Bitmask of enabled TX rings
+ * @NFP_NET_CFG_RXRS_ENABLE: Bitmask of enabled RX rings
+ * @NFP_NET_CFG_MTU: Set MTU size
+ * @NFP_NET_CFG_FLBUFSZ: Set freelist buffer size (must be larger than MTU)
+ * @NFP_NET_CFG_EXN: MSI-X table entry for exceptions
+ * @NFP_NET_CFG_LSC: MSI-X table entry for link state changes
+ * @NFP_NET_CFG_MACADDR: MAC address
+ *
+ * TODO:
+ * - define Error details in UPDATE
+ */
+#define NFP_NET_CFG_CTRL 0x0000
+#define NFP_NET_CFG_CTRL_ENABLE (0x1 << 0) /* Global enable */
+#define NFP_NET_CFG_CTRL_PROMISC (0x1 << 1) /* Enable Promisc mode */
+#define NFP_NET_CFG_CTRL_L2BC (0x1 << 2) /* Allow L2 Broadcast */
+#define NFP_NET_CFG_CTRL_L2MC (0x1 << 3) /* Allow L2 Multicast */
+#define NFP_NET_CFG_CTRL_RXCSUM (0x1 << 4) /* Enable RX Checksum */
+#define NFP_NET_CFG_CTRL_TXCSUM (0x1 << 5) /* Enable TX Checksum */
+#define NFP_NET_CFG_CTRL_RXVLAN (0x1 << 6) /* Enable VLAN strip */
+#define NFP_NET_CFG_CTRL_TXVLAN (0x1 << 7) /* Enable VLAN insert */
+#define NFP_NET_CFG_CTRL_SCATTER (0x1 << 8) /* Scatter DMA */
+#define NFP_NET_CFG_CTRL_GATHER (0x1 << 9) /* Gather DMA */
+#define NFP_NET_CFG_CTRL_LSO (0x1 << 10) /* LSO/TSO */
+#define NFP_NET_CFG_CTRL_RINGCFG (0x1 << 16) /* Ring runtime changes */
+#define NFP_NET_CFG_CTRL_RSS (0x1 << 17) /* RSS */
+#define NFP_NET_CFG_CTRL_IRQMOD (0x1 << 18) /* Interrupt moderation */
+#define NFP_NET_CFG_CTRL_RINGPRIO (0x1 << 19) /* Ring priorities */
+#define NFP_NET_CFG_CTRL_MSIXAUTO (0x1 << 20) /* MSI-X auto-masking */
+#define NFP_NET_CFG_CTRL_TXRWB (0x1 << 21) /* Write-back of TX ring*/
+#define NFP_NET_CFG_CTRL_L2SWITCH (0x1 << 22) /* L2 Switch */
+#define NFP_NET_CFG_CTRL_L2SWITCH_LOCAL (0x1 << 23) /* Switch to local */
+#define NFP_NET_CFG_CTRL_VXLAN (0x1 << 24) /* VXLAN tunnel support */
+#define NFP_NET_CFG_CTRL_NVGRE (0x1 << 25) /* NVGRE tunnel support */
+#define NFP_NET_CFG_UPDATE 0x0004
+#define NFP_NET_CFG_UPDATE_GEN (0x1 << 0) /* General update */
+#define NFP_NET_CFG_UPDATE_RING (0x1 << 1) /* Ring config change */
+#define NFP_NET_CFG_UPDATE_RSS (0x1 << 2) /* RSS config change */
+#define NFP_NET_CFG_UPDATE_TXRPRIO (0x1 << 3) /* TX Ring prio change */
+#define NFP_NET_CFG_UPDATE_RXRPRIO (0x1 << 4) /* RX Ring prio change */
+#define NFP_NET_CFG_UPDATE_MSIX (0x1 << 5) /* MSI-X change */
+#define NFP_NET_CFG_UPDATE_L2SWITCH (0x1 << 6) /* Switch changes */
+#define NFP_NET_CFG_UPDATE_RESET (0x1 << 7) /* Update due to FLR */
+#define NFP_NET_CFG_UPDATE_IRQMOD (0x1 << 8) /* IRQ mod change */
+#define NFP_NET_CFG_UPDATE_VXLAN (0x1 << 9) /* VXLAN port change */
+#define NFP_NET_CFG_UPDATE_ERR (0x1 << 31) /* A error occurred */
+#define NFP_NET_CFG_TXRS_ENABLE 0x0008
+#define NFP_NET_CFG_RXRS_ENABLE 0x0010
+#define NFP_NET_CFG_MTU 0x0018
+#define NFP_NET_CFG_FLBUFSZ 0x001c
+#define NFP_NET_CFG_EXN 0x001f
+#define NFP_NET_CFG_LSC 0x0020
+#define NFP_NET_CFG_MACADDR 0x0024
+
+/**
+ * Read-only words (0x0030 - 0x0050):
+ * @NFP_NET_CFG_VERSION: Firmware version number
+ * @NFP_NET_CFG_STS: Status
+ * @NFP_NET_CFG_CAP: Capabilities (same bits as @NFP_NET_CFG_CTRL)
+ * @NFP_NET_MAX_TXRINGS: Maximum number of TX rings
+ * @NFP_NET_MAX_RXRINGS: Maximum number of RX rings
+ * @NFP_NET_MAX_MTU: Maximum support MTU
+ * @NFP_NET_CFG_START_TXQ: Start Queue Control Queue to use for TX (PF only)
+ * @NFP_NET_CFG_START_RXQ: Start Queue Control Queue to use for RX (PF only)
+ *
+ * TODO:
+ * - define more STS bits
+ */
+#define NFP_NET_CFG_VERSION 0x0030
+#define NFP_NET_CFG_VERSION_RESERVED_MASK (0xff << 24)
+#define NFP_NET_CFG_VERSION_CLASS_MASK (0xff << 16)
+#define NFP_NET_CFG_VERSION_CLASS(x) (((x) & 0xff) << 16)
+#define NFP_NET_CFG_VERSION_CLASS_GENERIC 0
+#define NFP_NET_CFG_VERSION_MAJOR_MASK (0xff << 8)
+#define NFP_NET_CFG_VERSION_MAJOR(x) (((x) & 0xff) << 8)
+#define NFP_NET_CFG_VERSION_MINOR_MASK (0xff << 0)
+#define NFP_NET_CFG_VERSION_MINOR(x) (((x) & 0xff) << 0)
+#define NFP_NET_CFG_STS 0x0034
+#define NFP_NET_CFG_STS_LINK (0x1 << 0) /* Link up or down */
+#define NFP_NET_CFG_CAP 0x0038
+#define NFP_NET_CFG_MAX_TXRINGS 0x003c
+#define NFP_NET_CFG_MAX_RXRINGS 0x0040
+#define NFP_NET_CFG_MAX_MTU 0x0044
+/* Next two words are being used by VFs for solving THB350 issue */
+#define NFP_NET_CFG_START_TXQ 0x0048
+#define NFP_NET_CFG_START_RXQ 0x004c
+
+/**
+ * NFP-3200 workaround (0x0050 - 0x0058)
+ * @NFP_NET_CFG_SPARE_ADDR: DMA address for ME code to use (e.g. YDS-155 fix)
+ */
+#define NFP_NET_CFG_SPARE_ADDR 0x0050
+/**
+ * NFP6000/NFP4000 - Prepend configuration
+ */
+#define NFP_NET_CFG_RX_OFFSET 0x0050
+#define NFP_NET_CFG_RX_OFFSET_DYNAMIC 0 /* Prepend mode */
+
+/**
+ * NFP6000/NFP4000 - VXLAN/UDP encap configuration
+ * @NFP_NET_CFG_VXLAN_PORT: Base address of table of tunnels' UDP dst ports
+ * @NFP_NET_CFG_VXLAN_SZ: Size of the UDP port table in bytes
+ */
+#define NFP_NET_CFG_VXLAN_PORT 0x0060
+#define NFP_NET_CFG_VXLAN_SZ 0x0008
+
+/**
+ * 64B reserved for future use (0x0080 - 0x00c0)
+ */
+#define NFP_NET_CFG_RESERVED 0x0080
+#define NFP_NET_CFG_RESERVED_SZ 0x0040
+
+/**
+ * RSS configuration (0x0100 - 0x01ac):
+ * Used only when NFP_NET_CFG_CTRL_RSS is enabled
+ * @NFP_NET_CFG_RSS_CFG: RSS configuration word
+ * @NFP_NET_CFG_RSS_KEY: RSS "secret" key
+ * @NFP_NET_CFG_RSS_ITBL: RSS indirection table
+ */
+#define NFP_NET_CFG_RSS_BASE 0x0100
+#define NFP_NET_CFG_RSS_CTRL NFP_NET_CFG_RSS_BASE
+#define NFP_NET_CFG_RSS_MASK (0x7f)
+#define NFP_NET_CFG_RSS_MASK_of(_x) ((_x) & 0x7f)
+#define NFP_NET_CFG_RSS_IPV4 (1 << 8) /* RSS for IPv4 */
+#define NFP_NET_CFG_RSS_IPV6 (1 << 9) /* RSS for IPv6 */
+#define NFP_NET_CFG_RSS_IPV4_TCP (1 << 10) /* RSS for IPv4/TCP */
+#define NFP_NET_CFG_RSS_IPV4_UDP (1 << 11) /* RSS for IPv4/UDP */
+#define NFP_NET_CFG_RSS_IPV6_TCP (1 << 12) /* RSS for IPv6/TCP */
+#define NFP_NET_CFG_RSS_IPV6_UDP (1 << 13) /* RSS for IPv6/UDP */
+#define NFP_NET_CFG_RSS_TOEPLITZ (1 << 24) /* Use Toeplitz hash */
+#define NFP_NET_CFG_RSS_KEY (NFP_NET_CFG_RSS_BASE + 0x4)
+#define NFP_NET_CFG_RSS_KEY_SZ 0x28
+#define NFP_NET_CFG_RSS_ITBL (NFP_NET_CFG_RSS_BASE + 0x4 + \
+ NFP_NET_CFG_RSS_KEY_SZ)
+#define NFP_NET_CFG_RSS_ITBL_SZ 0x80
+
+/**
+ * TX ring configuration (0x200 - 0x800)
+ * @NFP_NET_CFG_TXR_BASE: Base offset for TX ring configuration
+ * @NFP_NET_CFG_TXR_ADDR: Per TX ring DMA address (8B entries)
+ * @NFP_NET_CFG_TXR_WB_ADDR: Per TX ring write back DMA address (8B entries)
+ * @NFP_NET_CFG_TXR_SZ: Per TX ring ring size (1B entries)
+ * @NFP_NET_CFG_TXR_VEC: Per TX ring MSI-X table entry (1B entries)
+ * @NFP_NET_CFG_TXR_PRIO: Per TX ring priority (1B entries)
+ * @NFP_NET_CFG_TXR_IRQ_MOD: Per TX ring interrupt moderation packet
+ */
+#define NFP_NET_CFG_TXR_BASE 0x0200
+#define NFP_NET_CFG_TXR_ADDR(_x) (NFP_NET_CFG_TXR_BASE + ((_x) * 0x8))
+#define NFP_NET_CFG_TXR_WB_ADDR(_x) (NFP_NET_CFG_TXR_BASE + 0x200 + \
+ ((_x) * 0x8))
+#define NFP_NET_CFG_TXR_SZ(_x) (NFP_NET_CFG_TXR_BASE + 0x400 + (_x))
+#define NFP_NET_CFG_TXR_VEC(_x) (NFP_NET_CFG_TXR_BASE + 0x440 + (_x))
+#define NFP_NET_CFG_TXR_PRIO(_x) (NFP_NET_CFG_TXR_BASE + 0x480 + (_x))
+#define NFP_NET_CFG_TXR_IRQ_MOD(_x) (NFP_NET_CFG_TXR_BASE + 0x500 + \
+ ((_x) * 0x4))
+
+/**
+ * RX ring configuration (0x0800 - 0x0c00)
+ * @NFP_NET_CFG_RXR_BASE: Base offset for RX ring configuration
+ * @NFP_NET_CFG_RXR_ADDR: Per RX ring DMA address (8B entries)
+ * @NFP_NET_CFG_RXR_SZ: Per RX ring ring size (1B entries)
+ * @NFP_NET_CFG_RXR_VEC: Per RX ring MSI-X table entry (1B entries)
+ * @NFP_NET_CFG_RXR_PRIO: Per RX ring priority (1B entries)
+ * @NFP_NET_CFG_RXR_IRQ_MOD: Per RX ring interrupt moderation (4B entries)
+ */
+#define NFP_NET_CFG_RXR_BASE 0x0800
+#define NFP_NET_CFG_RXR_ADDR(_x) (NFP_NET_CFG_RXR_BASE + ((_x) * 0x8))
+#define NFP_NET_CFG_RXR_SZ(_x) (NFP_NET_CFG_RXR_BASE + 0x200 + (_x))
+#define NFP_NET_CFG_RXR_VEC(_x) (NFP_NET_CFG_RXR_BASE + 0x240 + (_x))
+#define NFP_NET_CFG_RXR_PRIO(_x) (NFP_NET_CFG_RXR_BASE + 0x280 + (_x))
+#define NFP_NET_CFG_RXR_IRQ_MOD(_x) (NFP_NET_CFG_RXR_BASE + 0x300 + \
+ ((_x) * 0x4))
+
+/**
+ * Interrupt Control/Cause registers (0x0c00 - 0x0d00)
+ * These registers are only used when MSI-X auto-masking is not
+ * enabled (@NFP_NET_CFG_CTRL_MSIXAUTO not set). The array is index
+ * by MSI-X entry and are 1B in size. If an entry is zero, the
+ * corresponding entry is enabled. If the FW generates an interrupt,
+ * it writes a cause into the corresponding field. This also masks
+ * the MSI-X entry and the host driver must clear the register to
+ * re-enable the interrupt.
+ */
+#define NFP_NET_CFG_ICR_BASE 0x0c00
+#define NFP_NET_CFG_ICR(_x) (NFP_NET_CFG_ICR_BASE + (_x))
+#define NFP_NET_CFG_ICR_UNMASKED 0x0
+#define NFP_NET_CFG_ICR_RXTX 0x1
+#define NFP_NET_CFG_ICR_LSC 0x2
+
+/**
+ * General device stats (0x0d00 - 0x0d90)
+ * all counters are 64bit.
+ */
+#define NFP_NET_CFG_STATS_BASE 0x0d00
+#define NFP_NET_CFG_STATS_RX_DISCARDS (NFP_NET_CFG_STATS_BASE + 0x00)
+#define NFP_NET_CFG_STATS_RX_ERRORS (NFP_NET_CFG_STATS_BASE + 0x08)
+#define NFP_NET_CFG_STATS_RX_OCTETS (NFP_NET_CFG_STATS_BASE + 0x10)
+#define NFP_NET_CFG_STATS_RX_UC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x18)
+#define NFP_NET_CFG_STATS_RX_MC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x20)
+#define NFP_NET_CFG_STATS_RX_BC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x28)
+#define NFP_NET_CFG_STATS_RX_FRAMES (NFP_NET_CFG_STATS_BASE + 0x30)
+#define NFP_NET_CFG_STATS_RX_MC_FRAMES (NFP_NET_CFG_STATS_BASE + 0x38)
+#define NFP_NET_CFG_STATS_RX_BC_FRAMES (NFP_NET_CFG_STATS_BASE + 0x40)
+
+#define NFP_NET_CFG_STATS_TX_DISCARDS (NFP_NET_CFG_STATS_BASE + 0x48)
+#define NFP_NET_CFG_STATS_TX_ERRORS (NFP_NET_CFG_STATS_BASE + 0x50)
+#define NFP_NET_CFG_STATS_TX_OCTETS (NFP_NET_CFG_STATS_BASE + 0x58)
+#define NFP_NET_CFG_STATS_TX_UC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x60)
+#define NFP_NET_CFG_STATS_TX_MC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x68)
+#define NFP_NET_CFG_STATS_TX_BC_OCTETS (NFP_NET_CFG_STATS_BASE + 0x70)
+#define NFP_NET_CFG_STATS_TX_FRAMES (NFP_NET_CFG_STATS_BASE + 0x78)
+#define NFP_NET_CFG_STATS_TX_MC_FRAMES (NFP_NET_CFG_STATS_BASE + 0x80)
+#define NFP_NET_CFG_STATS_TX_BC_FRAMES (NFP_NET_CFG_STATS_BASE + 0x88)
+
+/**
+ * Per ring stats (0x1000 - 0x1800)
+ * options, 64bit per entry
+ * @NFP_NET_CFG_TXR_STATS: TX ring statistics (Packet and Byte count)
+ * @NFP_NET_CFG_RXR_STATS: RX ring statistics (Packet and Byte count)
+ */
+#define NFP_NET_CFG_TXR_STATS_BASE 0x1000
+#define NFP_NET_CFG_TXR_STATS(_x) (NFP_NET_CFG_TXR_STATS_BASE + \
+ ((_x) * 0x10))
+#define NFP_NET_CFG_RXR_STATS_BASE 0x1400
+#define NFP_NET_CFG_RXR_STATS(_x) (NFP_NET_CFG_RXR_STATS_BASE + \
+ ((_x) * 0x10))
+
+#endif /* _NFP_NET_CTRL_H_ */
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c
new file mode 100644
index 000000000000..4c97c713121c
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2015 Netronome Systems, Inc.
+ *
+ * This software is dual licensed under the GNU General License Version 2,
+ * June 1991 as shown in the file COPYING in the top-level directory of this
+ * source tree or the BSD 2-Clause License provided below. You have the
+ * option to license this software under the complete terms of either license.
+ *
+ * The BSD 2-Clause 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, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 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/debugfs.h>
+#include <linux/module.h>
+#include <linux/rtnetlink.h>
+
+#include "nfp_net.h"
+
+static struct dentry *nfp_dir;
+
+static int nfp_net_debugfs_rx_q_read(struct seq_file *file, void *data)
+{
+ struct nfp_net_rx_ring *rx_ring = file->private;
+ int fl_rd_p, fl_wr_p, rx_rd_p, rx_wr_p, rxd_cnt;
+ struct nfp_net_rx_desc *rxd;
+ struct sk_buff *skb;
+ struct nfp_net *nn;
+ int i;
+
+ rtnl_lock();
+
+ if (!rx_ring->r_vec || !rx_ring->r_vec->nfp_net)
+ goto out;
+ nn = rx_ring->r_vec->nfp_net;
+ if (!netif_running(nn->netdev))
+ goto out;
+
+ rxd_cnt = rx_ring->cnt;
+
+ fl_rd_p = nfp_qcp_rd_ptr_read(rx_ring->qcp_fl);
+ fl_wr_p = nfp_qcp_wr_ptr_read(rx_ring->qcp_fl);
+ rx_rd_p = nfp_qcp_rd_ptr_read(rx_ring->qcp_rx);
+ rx_wr_p = nfp_qcp_wr_ptr_read(rx_ring->qcp_rx);
+
+ seq_printf(file, "RX[%02d]: H_RD=%d H_WR=%d FL_RD=%d FL_WR=%d RX_RD=%d RX_WR=%d\n",
+ rx_ring->idx, rx_ring->rd_p, rx_ring->wr_p,
+ fl_rd_p, fl_wr_p, rx_rd_p, rx_wr_p);
+
+ for (i = 0; i < rxd_cnt; i++) {
+ rxd = &rx_ring->rxds[i];
+ seq_printf(file, "%04d: 0x%08x 0x%08x", i,
+ rxd->vals[0], rxd->vals[1]);
+
+ skb = READ_ONCE(rx_ring->rxbufs[i].skb);
+ if (skb)
+ seq_printf(file, " skb->head=%p skb->data=%p",
+ skb->head, skb->data);
+
+ if (rx_ring->rxbufs[i].dma_addr)
+ seq_printf(file, " dma_addr=%pad",
+ &rx_ring->rxbufs[i].dma_addr);
+
+ if (i == rx_ring->rd_p % rxd_cnt)
+ seq_puts(file, " H_RD ");
+ if (i == rx_ring->wr_p % rxd_cnt)
+ seq_puts(file, " H_WR ");
+ if (i == fl_rd_p % rxd_cnt)
+ seq_puts(file, " FL_RD");
+ if (i == fl_wr_p % rxd_cnt)
+ seq_puts(file, " FL_WR");
+ if (i == rx_rd_p % rxd_cnt)
+ seq_puts(file, " RX_RD");
+ if (i == rx_wr_p % rxd_cnt)
+ seq_puts(file, " RX_WR");
+
+ seq_putc(file, '\n');
+ }
+out:
+ rtnl_unlock();
+ return 0;
+}
+
+static int nfp_net_debugfs_rx_q_open(struct inode *inode, struct file *f)
+{
+ return single_open(f, nfp_net_debugfs_rx_q_read, inode->i_private);
+}
+
+static const struct file_operations nfp_rx_q_fops = {
+ .owner = THIS_MODULE,
+ .open = nfp_net_debugfs_rx_q_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek
+};
+
+static int nfp_net_debugfs_tx_q_read(struct seq_file *file, void *data)
+{
+ struct nfp_net_tx_ring *tx_ring = file->private;
+ struct nfp_net_tx_desc *txd;
+ int d_rd_p, d_wr_p, txd_cnt;
+ struct sk_buff *skb;
+ struct nfp_net *nn;
+ int i;
+
+ rtnl_lock();
+
+ if (!tx_ring->r_vec || !tx_ring->r_vec->nfp_net)
+ goto out;
+ nn = tx_ring->r_vec->nfp_net;
+ if (!netif_running(nn->netdev))
+ goto out;
+
+ txd_cnt = tx_ring->cnt;
+
+ d_rd_p = nfp_qcp_rd_ptr_read(tx_ring->qcp_q);
+ d_wr_p = nfp_qcp_wr_ptr_read(tx_ring->qcp_q);
+
+ seq_printf(file, "TX[%02d]: H_RD=%d H_WR=%d D_RD=%d D_WR=%d\n",
+ tx_ring->idx, tx_ring->rd_p, tx_ring->wr_p, d_rd_p, d_wr_p);
+
+ for (i = 0; i < txd_cnt; i++) {
+ txd = &tx_ring->txds[i];
+ seq_printf(file, "%04d: 0x%08x 0x%08x 0x%08x 0x%08x", i,
+ txd->vals[0], txd->vals[1],
+ txd->vals[2], txd->vals[3]);
+
+ skb = READ_ONCE(tx_ring->txbufs[i].skb);
+ if (skb)
+ seq_printf(file, " skb->head=%p skb->data=%p",
+ skb->head, skb->data);
+ if (tx_ring->txbufs[i].dma_addr)
+ seq_printf(file, " dma_addr=%pad",
+ &tx_ring->txbufs[i].dma_addr);
+
+ if (i == tx_ring->rd_p % txd_cnt)
+ seq_puts(file, " H_RD");
+ if (i == tx_ring->wr_p % txd_cnt)
+ seq_puts(file, " H_WR");
+ if (i == d_rd_p % txd_cnt)
+ seq_puts(file, " D_RD");
+ if (i == d_wr_p % txd_cnt)
+ seq_puts(file, " D_WR");
+
+ seq_putc(file, '\n');
+ }
+out:
+ rtnl_unlock();
+ return 0;
+}
+
+static int nfp_net_debugfs_tx_q_open(struct inode *inode, struct file *f)
+{
+ return single_open(f, nfp_net_debugfs_tx_q_read, inode->i_private);
+}
+
+static const struct file_operations nfp_tx_q_fops = {
+ .owner = THIS_MODULE,
+ .open = nfp_net_debugfs_tx_q_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek
+};
+
+void nfp_net_debugfs_adapter_add(struct nfp_net *nn)
+{
+ static struct dentry *queues, *tx, *rx;
+ char int_name[16];
+ int i;
+
+ if (IS_ERR_OR_NULL(nfp_dir))
+ return;
+
+ nn->debugfs_dir = debugfs_create_dir(pci_name(nn->pdev), nfp_dir);
+ if (IS_ERR_OR_NULL(nn->debugfs_dir))
+ return;
+
+ /* Create queue debugging sub-tree */
+ queues = debugfs_create_dir("queue", nn->debugfs_dir);
+ if (IS_ERR_OR_NULL(nn->debugfs_dir))
+ return;
+
+ rx = debugfs_create_dir("rx", queues);
+ tx = debugfs_create_dir("tx", queues);
+ if (IS_ERR_OR_NULL(rx) || IS_ERR_OR_NULL(tx))
+ return;
+
+ for (i = 0; i < nn->num_rx_rings; i++) {
+ sprintf(int_name, "%d", i);
+ debugfs_create_file(int_name, S_IRUSR, rx,
+ &nn->rx_rings[i], &nfp_rx_q_fops);
+ }
+
+ for (i = 0; i < nn->num_tx_rings; i++) {
+ sprintf(int_name, "%d", i);
+ debugfs_create_file(int_name, S_IRUSR, tx,
+ &nn->tx_rings[i], &nfp_tx_q_fops);
+ }
+}
+
+void nfp_net_debugfs_adapter_del(struct nfp_net *nn)
+{
+ debugfs_remove_recursive(nn->debugfs_dir);
+ nn->debugfs_dir = NULL;
+}
+
+void nfp_net_debugfs_create(void)
+{
+ nfp_dir = debugfs_create_dir("nfp_net", NULL);
+}
+
+void nfp_net_debugfs_destroy(void)
+{
+ debugfs_remove_recursive(nfp_dir);
+ nfp_dir = NULL;
+}
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
new file mode 100644
index 000000000000..9a4084a68db5
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
@@ -0,0 +1,640 @@
+/*
+ * Copyright (C) 2015 Netronome Systems, Inc.
+ *
+ * This software is dual licensed under the GNU General License Version 2,
+ * June 1991 as shown in the file COPYING in the top-level directory of this
+ * source tree or the BSD 2-Clause License provided below. You have the
+ * option to license this software under the complete terms of either license.
+ *
+ * The BSD 2-Clause 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, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 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.
+ */
+
+/*
+ * nfp_net_ethtool.c
+ * Netronome network device driver: ethtool support
+ * Authors: Jakub Kicinski <jakub.kicinski@netronome.com>
+ * Jason McMullan <jason.mcmullan@netronome.com>
+ * Rolf Neugebauer <rolf.neugebauer@netronome.com>
+ * Brad Petrus <brad.petrus@netronome.com>
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/ethtool.h>
+
+#include "nfp_net_ctrl.h"
+#include "nfp_net.h"
+
+/* Support for stats. Returns netdev, driver, and device stats */
+enum { NETDEV_ET_STATS, NFP_NET_DRV_ET_STATS, NFP_NET_DEV_ET_STATS };
+struct _nfp_net_et_stats {
+ char name[ETH_GSTRING_LEN];
+ int type;
+ int sz;
+ int off;
+};
+
+#define NN_ET_NETDEV_STAT(m) NETDEV_ET_STATS, \
+ FIELD_SIZEOF(struct net_device_stats, m), \
+ offsetof(struct net_device_stats, m)
+/* For stats in the control BAR (other than Q stats) */
+#define NN_ET_DEV_STAT(m) NFP_NET_DEV_ET_STATS, \
+ sizeof(u64), \
+ (m)
+static const struct _nfp_net_et_stats nfp_net_et_stats[] = {
+ /* netdev stats */
+ {"rx_packets", NN_ET_NETDEV_STAT(rx_packets)},
+ {"tx_packets", NN_ET_NETDEV_STAT(tx_packets)},
+ {"rx_bytes", NN_ET_NETDEV_STAT(rx_bytes)},
+ {"tx_bytes", NN_ET_NETDEV_STAT(tx_bytes)},
+ {"rx_errors", NN_ET_NETDEV_STAT(rx_errors)},
+ {"tx_errors", NN_ET_NETDEV_STAT(tx_errors)},
+ {"rx_dropped", NN_ET_NETDEV_STAT(rx_dropped)},
+ {"tx_dropped", NN_ET_NETDEV_STAT(tx_dropped)},
+ {"multicast", NN_ET_NETDEV_STAT(multicast)},
+ {"collisions", NN_ET_NETDEV_STAT(collisions)},
+ {"rx_over_errors", NN_ET_NETDEV_STAT(rx_over_errors)},
+ {"rx_crc_errors", NN_ET_NETDEV_STAT(rx_crc_errors)},
+ {"rx_frame_errors", NN_ET_NETDEV_STAT(rx_frame_errors)},
+ {"rx_fifo_errors", NN_ET_NETDEV_STAT(rx_fifo_errors)},
+ {"rx_missed_errors", NN_ET_NETDEV_STAT(rx_missed_errors)},
+ {"tx_aborted_errors", NN_ET_NETDEV_STAT(tx_aborted_errors)},
+ {"tx_carrier_errors", NN_ET_NETDEV_STAT(tx_carrier_errors)},
+ {"tx_fifo_errors", NN_ET_NETDEV_STAT(tx_fifo_errors)},
+ /* Stats from the device */
+ {"dev_rx_discards", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_DISCARDS)},
+ {"dev_rx_errors", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_ERRORS)},
+ {"dev_rx_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_OCTETS)},
+ {"dev_rx_uc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_UC_OCTETS)},
+ {"dev_rx_mc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_MC_OCTETS)},
+ {"dev_rx_bc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_BC_OCTETS)},
+ {"dev_rx_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_FRAMES)},
+ {"dev_rx_mc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_MC_FRAMES)},
+ {"dev_rx_bc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_BC_FRAMES)},
+
+ {"dev_tx_discards", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_DISCARDS)},
+ {"dev_tx_errors", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_ERRORS)},
+ {"dev_tx_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_OCTETS)},
+ {"dev_tx_uc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_UC_OCTETS)},
+ {"dev_tx_mc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_MC_OCTETS)},
+ {"dev_tx_bc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_BC_OCTETS)},
+ {"dev_tx_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_FRAMES)},
+ {"dev_tx_mc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_MC_FRAMES)},
+ {"dev_tx_bc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_BC_FRAMES)},
+};
+
+#define NN_ET_GLOBAL_STATS_LEN ARRAY_SIZE(nfp_net_et_stats)
+#define NN_ET_RVEC_STATS_LEN (nn->num_r_vecs * 3)
+#define NN_ET_RVEC_GATHER_STATS 7
+#define NN_ET_QUEUE_STATS_LEN ((nn->num_tx_rings + nn->num_rx_rings) * 2)
+#define NN_ET_STATS_LEN (NN_ET_GLOBAL_STATS_LEN + NN_ET_RVEC_GATHER_STATS + \
+ NN_ET_RVEC_STATS_LEN + NN_ET_QUEUE_STATS_LEN)
+
+static void nfp_net_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+
+ strlcpy(drvinfo->driver, nfp_net_driver_name, sizeof(drvinfo->driver));
+ strlcpy(drvinfo->version, nfp_net_driver_version,
+ sizeof(drvinfo->version));
+
+ snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
+ "%d.%d.%d.%d",
+ nn->fw_ver.resv, nn->fw_ver.class,
+ nn->fw_ver.major, nn->fw_ver.minor);
+ strlcpy(drvinfo->bus_info, pci_name(nn->pdev),
+ sizeof(drvinfo->bus_info));
+
+ drvinfo->n_stats = NN_ET_STATS_LEN;
+ drvinfo->regdump_len = NFP_NET_CFG_BAR_SZ;
+}
+
+static void nfp_net_get_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *ring)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+
+ ring->rx_max_pending = NFP_NET_MAX_RX_DESCS;
+ ring->tx_max_pending = NFP_NET_MAX_TX_DESCS;
+ ring->rx_pending = nn->rxd_cnt;
+ ring->tx_pending = nn->txd_cnt;
+}
+
+static int nfp_net_set_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *ring)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ u32 rxd_cnt, txd_cnt;
+
+ if (netif_running(netdev)) {
+ /* Some NIC drivers allow reconfiguration on the fly,
+ * some down the interface, change and then up it
+ * again. For now we don't allow changes when the
+ * device is up.
+ */
+ nn_warn(nn, "Can't change rings while device is up\n");
+ return -EBUSY;
+ }
+
+ /* We don't have separate queues/rings for small/large frames. */
+ if (ring->rx_mini_pending || ring->rx_jumbo_pending)
+ return -EINVAL;
+
+ /* Round up to supported values */
+ rxd_cnt = roundup_pow_of_two(ring->rx_pending);
+ rxd_cnt = max_t(u32, rxd_cnt, NFP_NET_MIN_RX_DESCS);
+ rxd_cnt = min_t(u32, rxd_cnt, NFP_NET_MAX_RX_DESCS);
+
+ txd_cnt = roundup_pow_of_two(ring->tx_pending);
+ txd_cnt = max_t(u32, txd_cnt, NFP_NET_MIN_TX_DESCS);
+ txd_cnt = min_t(u32, txd_cnt, NFP_NET_MAX_TX_DESCS);
+
+ if (nn->rxd_cnt != rxd_cnt || nn->txd_cnt != txd_cnt)
+ nn_dbg(nn, "Change ring size: RxQ %u->%u, TxQ %u->%u\n",
+ nn->rxd_cnt, rxd_cnt, nn->txd_cnt, txd_cnt);
+
+ nn->rxd_cnt = rxd_cnt;
+ nn->txd_cnt = txd_cnt;
+
+ return 0;
+}
+
+static void nfp_net_get_strings(struct net_device *netdev,
+ u32 stringset, u8 *data)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ u8 *p = data;
+ int i;
+
+ switch (stringset) {
+ case ETH_SS_STATS:
+ for (i = 0; i < NN_ET_GLOBAL_STATS_LEN; i++) {
+ memcpy(p, nfp_net_et_stats[i].name, ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < nn->num_r_vecs; i++) {
+ sprintf(p, "rvec_%u_rx_pkts", i);
+ p += ETH_GSTRING_LEN;
+ sprintf(p, "rvec_%u_tx_pkts", i);
+ p += ETH_GSTRING_LEN;
+ sprintf(p, "rvec_%u_tx_busy", i);
+ p += ETH_GSTRING_LEN;
+ }
+ strncpy(p, "hw_rx_csum_ok", ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ strncpy(p, "hw_rx_csum_inner_ok", ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ strncpy(p, "hw_rx_csum_err", ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ strncpy(p, "hw_tx_csum", ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ strncpy(p, "hw_tx_inner_csum", ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ strncpy(p, "tx_gather", ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ strncpy(p, "tx_lso", ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ for (i = 0; i < nn->num_tx_rings; i++) {
+ sprintf(p, "txq_%u_pkts", i);
+ p += ETH_GSTRING_LEN;
+ sprintf(p, "txq_%u_bytes", i);
+ p += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < nn->num_rx_rings; i++) {
+ sprintf(p, "rxq_%u_pkts", i);
+ p += ETH_GSTRING_LEN;
+ sprintf(p, "rxq_%u_bytes", i);
+ p += ETH_GSTRING_LEN;
+ }
+ break;
+ }
+}
+
+static void nfp_net_get_stats(struct net_device *netdev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ u64 gathered_stats[NN_ET_RVEC_GATHER_STATS] = {};
+ struct nfp_net *nn = netdev_priv(netdev);
+ struct rtnl_link_stats64 *netdev_stats;
+ struct rtnl_link_stats64 temp = {};
+ u64 tmp[NN_ET_RVEC_GATHER_STATS];
+ u8 __iomem *io_p;
+ int i, j, k;
+ u8 *p;
+
+ netdev_stats = dev_get_stats(netdev, &temp);
+
+ for (i = 0; i < NN_ET_GLOBAL_STATS_LEN; i++) {
+ switch (nfp_net_et_stats[i].type) {
+ case NETDEV_ET_STATS:
+ p = (char *)netdev_stats + nfp_net_et_stats[i].off;
+ data[i] = nfp_net_et_stats[i].sz == sizeof(u64) ?
+ *(u64 *)p : *(u32 *)p;
+ break;
+
+ case NFP_NET_DEV_ET_STATS:
+ io_p = nn->ctrl_bar + nfp_net_et_stats[i].off;
+ data[i] = readq(io_p);
+ break;
+ }
+ }
+ for (j = 0; j < nn->num_r_vecs; j++) {
+ unsigned int start;
+
+ do {
+ start = u64_stats_fetch_begin(&nn->r_vecs[j].rx_sync);
+ data[i++] = nn->r_vecs[j].rx_pkts;
+ tmp[0] = nn->r_vecs[j].hw_csum_rx_ok;
+ tmp[1] = nn->r_vecs[j].hw_csum_rx_inner_ok;
+ tmp[2] = nn->r_vecs[j].hw_csum_rx_error;
+ } while (u64_stats_fetch_retry(&nn->r_vecs[j].rx_sync, start));
+
+ do {
+ start = u64_stats_fetch_begin(&nn->r_vecs[j].tx_sync);
+ data[i++] = nn->r_vecs[j].tx_pkts;
+ data[i++] = nn->r_vecs[j].tx_busy;
+ tmp[3] = nn->r_vecs[j].hw_csum_tx;
+ tmp[4] = nn->r_vecs[j].hw_csum_tx_inner;
+ tmp[5] = nn->r_vecs[j].tx_gather;
+ tmp[6] = nn->r_vecs[j].tx_lso;
+ } while (u64_stats_fetch_retry(&nn->r_vecs[j].tx_sync, start));
+
+ for (k = 0; k < NN_ET_RVEC_GATHER_STATS; k++)
+ gathered_stats[k] += tmp[k];
+ }
+ for (j = 0; j < NN_ET_RVEC_GATHER_STATS; j++)
+ data[i++] = gathered_stats[j];
+ for (j = 0; j < nn->num_tx_rings; j++) {
+ io_p = nn->ctrl_bar + NFP_NET_CFG_TXR_STATS(j);
+ data[i++] = readq(io_p);
+ io_p = nn->ctrl_bar + NFP_NET_CFG_TXR_STATS(j) + 8;
+ data[i++] = readq(io_p);
+ }
+ for (j = 0; j < nn->num_rx_rings; j++) {
+ io_p = nn->ctrl_bar + NFP_NET_CFG_RXR_STATS(j);
+ data[i++] = readq(io_p);
+ io_p = nn->ctrl_bar + NFP_NET_CFG_RXR_STATS(j) + 8;
+ data[i++] = readq(io_p);
+ }
+}
+
+static int nfp_net_get_sset_count(struct net_device *netdev, int sset)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+
+ switch (sset) {
+ case ETH_SS_STATS:
+ return NN_ET_STATS_LEN;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+/* RX network flow classification (RSS, filters, etc)
+ */
+static u32 ethtool_flow_to_nfp_flag(u32 flow_type)
+{
+ static const u32 xlate_ethtool_to_nfp[IPV6_FLOW + 1] = {
+ [TCP_V4_FLOW] = NFP_NET_CFG_RSS_IPV4_TCP,
+ [TCP_V6_FLOW] = NFP_NET_CFG_RSS_IPV6_TCP,
+ [UDP_V4_FLOW] = NFP_NET_CFG_RSS_IPV4_UDP,
+ [UDP_V6_FLOW] = NFP_NET_CFG_RSS_IPV6_UDP,
+ [IPV4_FLOW] = NFP_NET_CFG_RSS_IPV4,
+ [IPV6_FLOW] = NFP_NET_CFG_RSS_IPV6,
+ };
+
+ if (flow_type >= ARRAY_SIZE(xlate_ethtool_to_nfp))
+ return 0;
+
+ return xlate_ethtool_to_nfp[flow_type];
+}
+
+static int nfp_net_get_rss_hash_opts(struct nfp_net *nn,
+ struct ethtool_rxnfc *cmd)
+{
+ u32 nfp_rss_flag;
+
+ cmd->data = 0;
+
+ if (!(nn->cap & NFP_NET_CFG_CTRL_RSS))
+ return -EOPNOTSUPP;
+
+ nfp_rss_flag = ethtool_flow_to_nfp_flag(cmd->flow_type);
+ if (!nfp_rss_flag)
+ return -EINVAL;
+
+ cmd->data |= RXH_IP_SRC | RXH_IP_DST;
+ if (nn->rss_cfg & nfp_rss_flag)
+ cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+
+ return 0;
+}
+
+static int nfp_net_get_rxnfc(struct net_device *netdev,
+ struct ethtool_rxnfc *cmd, u32 *rule_locs)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+
+ switch (cmd->cmd) {
+ case ETHTOOL_GRXRINGS:
+ cmd->data = nn->num_rx_rings;
+ return 0;
+ case ETHTOOL_GRXFH:
+ return nfp_net_get_rss_hash_opts(nn, cmd);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int nfp_net_set_rss_hash_opt(struct nfp_net *nn,
+ struct ethtool_rxnfc *nfc)
+{
+ u32 new_rss_cfg = nn->rss_cfg;
+ u32 nfp_rss_flag;
+ int err;
+
+ if (!(nn->cap & NFP_NET_CFG_CTRL_RSS))
+ return -EOPNOTSUPP;
+
+ /* RSS only supports IP SA/DA and L4 src/dst ports */
+ if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST |
+ RXH_L4_B_0_1 | RXH_L4_B_2_3))
+ return -EINVAL;
+
+ /* We need at least the IP SA/DA fields for hashing */
+ if (!(nfc->data & RXH_IP_SRC) ||
+ !(nfc->data & RXH_IP_DST))
+ return -EINVAL;
+
+ nfp_rss_flag = ethtool_flow_to_nfp_flag(nfc->flow_type);
+ if (!nfp_rss_flag)
+ return -EINVAL;
+
+ switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
+ case 0:
+ new_rss_cfg &= ~nfp_rss_flag;
+ break;
+ case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
+ new_rss_cfg |= nfp_rss_flag;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ new_rss_cfg |= NFP_NET_CFG_RSS_TOEPLITZ;
+ new_rss_cfg |= NFP_NET_CFG_RSS_MASK;
+
+ if (new_rss_cfg == nn->rss_cfg)
+ return 0;
+
+ writel(new_rss_cfg, nn->ctrl_bar + NFP_NET_CFG_RSS_CTRL);
+ err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_RSS);
+ if (err)
+ return err;
+
+ nn->rss_cfg = new_rss_cfg;
+
+ nn_dbg(nn, "Changed RSS config to 0x%x\n", nn->rss_cfg);
+ return 0;
+}
+
+static int nfp_net_set_rxnfc(struct net_device *netdev,
+ struct ethtool_rxnfc *cmd)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+
+ switch (cmd->cmd) {
+ case ETHTOOL_SRXFH:
+ return nfp_net_set_rss_hash_opt(nn, cmd);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static u32 nfp_net_get_rxfh_indir_size(struct net_device *netdev)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+
+ if (!(nn->cap & NFP_NET_CFG_CTRL_RSS))
+ return 0;
+
+ return ARRAY_SIZE(nn->rss_itbl);
+}
+
+static u32 nfp_net_get_rxfh_key_size(struct net_device *netdev)
+{
+ return NFP_NET_CFG_RSS_KEY_SZ;
+}
+
+static int nfp_net_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
+ u8 *hfunc)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ int i;
+
+ if (!(nn->cap & NFP_NET_CFG_CTRL_RSS))
+ return -EOPNOTSUPP;
+
+ if (indir)
+ for (i = 0; i < ARRAY_SIZE(nn->rss_itbl); i++)
+ indir[i] = nn->rss_itbl[i];
+ if (key)
+ memcpy(key, nn->rss_key, NFP_NET_CFG_RSS_KEY_SZ);
+ if (hfunc)
+ *hfunc = ETH_RSS_HASH_TOP;
+
+ return 0;
+}
+
+static int nfp_net_set_rxfh(struct net_device *netdev,
+ const u32 *indir, const u8 *key,
+ const u8 hfunc)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ int i;
+
+ if (!(nn->cap & NFP_NET_CFG_CTRL_RSS) ||
+ !(hfunc == ETH_RSS_HASH_NO_CHANGE || hfunc == ETH_RSS_HASH_TOP))
+ return -EOPNOTSUPP;
+
+ if (!key && !indir)
+ return 0;
+
+ if (key) {
+ memcpy(nn->rss_key, key, NFP_NET_CFG_RSS_KEY_SZ);
+ nfp_net_rss_write_key(nn);
+ }
+ if (indir) {
+ for (i = 0; i < ARRAY_SIZE(nn->rss_itbl); i++)
+ nn->rss_itbl[i] = indir[i];
+
+ nfp_net_rss_write_itbl(nn);
+ }
+
+ return nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_RSS);
+}
+
+/* Dump BAR registers
+ */
+static int nfp_net_get_regs_len(struct net_device *netdev)
+{
+ return NFP_NET_CFG_BAR_SZ;
+}
+
+static void nfp_net_get_regs(struct net_device *netdev,
+ struct ethtool_regs *regs, void *p)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ u32 *regs_buf = p;
+ int i;
+
+ regs->version = nn_readl(nn, NFP_NET_CFG_VERSION);
+
+ for (i = 0; i < NFP_NET_CFG_BAR_SZ / sizeof(u32); i++)
+ regs_buf[i] = readl(nn->ctrl_bar + (i * sizeof(u32)));
+}
+
+static int nfp_net_get_coalesce(struct net_device *netdev,
+ struct ethtool_coalesce *ec)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+
+ if (!(nn->cap & NFP_NET_CFG_CTRL_IRQMOD))
+ return -EINVAL;
+
+ ec->rx_coalesce_usecs = nn->rx_coalesce_usecs;
+ ec->rx_max_coalesced_frames = nn->rx_coalesce_max_frames;
+ ec->tx_coalesce_usecs = nn->tx_coalesce_usecs;
+ ec->tx_max_coalesced_frames = nn->tx_coalesce_max_frames;
+
+ return 0;
+}
+
+static int nfp_net_set_coalesce(struct net_device *netdev,
+ struct ethtool_coalesce *ec)
+{
+ struct nfp_net *nn = netdev_priv(netdev);
+ unsigned int factor;
+
+ if (ec->rx_coalesce_usecs_irq ||
+ ec->rx_max_coalesced_frames_irq ||
+ ec->tx_coalesce_usecs_irq ||
+ ec->tx_max_coalesced_frames_irq ||
+ ec->stats_block_coalesce_usecs ||
+ ec->use_adaptive_rx_coalesce ||
+ ec->use_adaptive_tx_coalesce ||
+ ec->pkt_rate_low ||
+ ec->rx_coalesce_usecs_low ||
+ ec->rx_max_coalesced_frames_low ||
+ ec->tx_coalesce_usecs_low ||
+ ec->tx_max_coalesced_frames_low ||
+ ec->pkt_rate_high ||
+ ec->rx_coalesce_usecs_high ||
+ ec->rx_max_coalesced_frames_high ||
+ ec->tx_coalesce_usecs_high ||
+ ec->tx_max_coalesced_frames_high ||
+ ec->rate_sample_interval)
+ return -ENOTSUPP;
+
+ /* Compute factor used to convert coalesce '_usecs' parameters to
+ * ME timestamp ticks. There are 16 ME clock cycles for each timestamp
+ * count.
+ */
+ factor = nn->me_freq_mhz / 16;
+
+ /* Each pair of (usecs, max_frames) fields specifies that interrupts
+ * should be coalesced until
+ * (usecs > 0 && time_since_first_completion >= usecs) ||
+ * (max_frames > 0 && completed_frames >= max_frames)
+ *
+ * It is illegal to set both usecs and max_frames to zero as this would
+ * cause interrupts to never be generated. To disable coalescing, set
+ * usecs = 0 and max_frames = 1.
+ *
+ * Some implementations ignore the value of max_frames and use the
+ * condition time_since_first_completion >= usecs
+ */
+
+ if (!(nn->cap & NFP_NET_CFG_CTRL_IRQMOD))
+ return -EINVAL;
+
+ /* ensure valid configuration */
+ if (!ec->rx_coalesce_usecs && !ec->rx_max_coalesced_frames)
+ return -EINVAL;
+
+ if (!ec->tx_coalesce_usecs && !ec->tx_max_coalesced_frames)
+ return -EINVAL;
+
+ if (ec->rx_coalesce_usecs * factor >= ((1 << 16) - 1))
+ return -EINVAL;
+
+ if (ec->tx_coalesce_usecs * factor >= ((1 << 16) - 1))
+ return -EINVAL;
+
+ if (ec->rx_max_coalesced_frames >= ((1 << 16) - 1))
+ return -EINVAL;
+
+ if (ec->tx_max_coalesced_frames >= ((1 << 16) - 1))
+ return -EINVAL;
+
+ /* configuration is valid */
+ nn->rx_coalesce_usecs = ec->rx_coalesce_usecs;
+ nn->rx_coalesce_max_frames = ec->rx_max_coalesced_frames;
+ nn->tx_coalesce_usecs = ec->tx_coalesce_usecs;
+ nn->tx_coalesce_max_frames = ec->tx_max_coalesced_frames;
+
+ /* write configuration to device */
+ nfp_net_coalesce_write_cfg(nn);
+ return nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_IRQMOD);
+}
+
+static const struct ethtool_ops nfp_net_ethtool_ops = {
+ .get_drvinfo = nfp_net_get_drvinfo,
+ .get_ringparam = nfp_net_get_ringparam,
+ .set_ringparam = nfp_net_set_ringparam,
+ .get_strings = nfp_net_get_strings,
+ .get_ethtool_stats = nfp_net_get_stats,
+ .get_sset_count = nfp_net_get_sset_count,
+ .get_rxnfc = nfp_net_get_rxnfc,
+ .set_rxnfc = nfp_net_set_rxnfc,
+ .get_rxfh_indir_size = nfp_net_get_rxfh_indir_size,
+ .get_rxfh_key_size = nfp_net_get_rxfh_key_size,
+ .get_rxfh = nfp_net_get_rxfh,
+ .set_rxfh = nfp_net_set_rxfh,
+ .get_regs_len = nfp_net_get_regs_len,
+ .get_regs = nfp_net_get_regs,
+ .get_coalesce = nfp_net_get_coalesce,
+ .set_coalesce = nfp_net_set_coalesce,
+};
+
+void nfp_net_set_ethtool_ops(struct net_device *netdev)
+{
+ netdev->ethtool_ops = &nfp_net_ethtool_ops;
+}
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c
new file mode 100644
index 000000000000..e2b22b8a20f1
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2015 Netronome Systems, Inc.
+ *
+ * This software is dual licensed under the GNU General License Version 2,
+ * June 1991 as shown in the file COPYING in the top-level directory of this
+ * source tree or the BSD 2-Clause License provided below. You have the
+ * option to license this software under the complete terms of either license.
+ *
+ * The BSD 2-Clause 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, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 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.
+ */
+
+/*
+ * nfp_netvf_main.c
+ * Netronome virtual function network device driver: Main entry point
+ * Author: Jason McMullan <jason.mcmullan@netronome.com>
+ * Rolf Neugebauer <rolf.neugebauer@netronome.com>
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/etherdevice.h>
+
+#include "nfp_net_ctrl.h"
+#include "nfp_net.h"
+
+const char nfp_net_driver_name[] = "nfp_netvf";
+const char nfp_net_driver_version[] = "0.1";
+#define PCI_DEVICE_NFP6000VF 0x6003
+static const struct pci_device_id nfp_netvf_pci_device_ids[] = {
+ { PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_NFP6000VF,
+ PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID,
+ PCI_ANY_ID, 0,
+ },
+ { 0, } /* Required last entry. */
+};
+MODULE_DEVICE_TABLE(pci, nfp_netvf_pci_device_ids);
+
+static void nfp_netvf_get_mac_addr(struct nfp_net *nn)
+{
+ u8 mac_addr[ETH_ALEN];
+
+ put_unaligned_be32(nn_readl(nn, NFP_NET_CFG_MACADDR + 0), &mac_addr[0]);
+ /* We can't do readw for NFP-3200 compatibility */
+ put_unaligned_be16(nn_readl(nn, NFP_NET_CFG_MACADDR + 4) >> 16,
+ &mac_addr[4]);
+
+ if (!is_valid_ether_addr(mac_addr)) {
+ eth_hw_addr_random(nn->netdev);
+ return;
+ }
+
+ ether_addr_copy(nn->netdev->dev_addr, mac_addr);
+ ether_addr_copy(nn->netdev->perm_addr, mac_addr);
+}
+
+static int nfp_netvf_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *pci_id)
+{
+ struct nfp_net_fw_version fw_ver;
+ int max_tx_rings, max_rx_rings;
+ u32 tx_bar_off, rx_bar_off;
+ u32 tx_bar_sz, rx_bar_sz;
+ int tx_bar_no, rx_bar_no;
+ u8 __iomem *ctrl_bar;
+ struct nfp_net *nn;
+ int is_nfp3200;
+ u32 startq;
+ int stride;
+ int err;
+
+ err = pci_enable_device_mem(pdev);
+ if (err)
+ return err;
+
+ err = pci_request_regions(pdev, nfp_net_driver_name);
+ if (err) {
+ dev_err(&pdev->dev, "Unable to allocate device memory.\n");
+ goto err_pci_disable;
+ }
+
+ switch (pdev->device) {
+ case PCI_DEVICE_NFP6000VF:
+ is_nfp3200 = 0;
+ break;
+ default:
+ err = -ENODEV;
+ goto err_pci_regions;
+ }
+
+ pci_set_master(pdev);
+
+ err = dma_set_mask_and_coherent(&pdev->dev,
+ DMA_BIT_MASK(NFP_NET_MAX_DMA_BITS));
+ if (err)
+ goto err_pci_regions;
+
+ /* Map the Control BAR.
+ *
+ * Irrespective of the advertised BAR size we only map the
+ * first NFP_NET_CFG_BAR_SZ of the BAR. This keeps the code
+ * the identical for PF and VF drivers.
+ */
+ ctrl_bar = ioremap_nocache(pci_resource_start(pdev, NFP_NET_CRTL_BAR),
+ NFP_NET_CFG_BAR_SZ);
+ if (!ctrl_bar) {
+ dev_err(&pdev->dev,
+ "Failed to map resource %d\n", NFP_NET_CRTL_BAR);
+ err = -EIO;
+ goto err_pci_regions;
+ }
+
+ nfp_net_get_fw_version(&fw_ver, ctrl_bar);
+ if (fw_ver.class != NFP_NET_CFG_VERSION_CLASS_GENERIC) {
+ dev_err(&pdev->dev, "Unknown Firmware ABI %d.%d.%d.%d\n",
+ fw_ver.resv, fw_ver.class, fw_ver.major, fw_ver.minor);
+ err = -EINVAL;
+ goto err_ctrl_unmap;
+ }
+
+ /* Determine stride */
+ if (nfp_net_fw_ver_eq(&fw_ver, 0, 0, 0, 0) ||
+ nfp_net_fw_ver_eq(&fw_ver, 0, 0, 0, 1) ||
+ nfp_net_fw_ver_eq(&fw_ver, 0, 0, 0x12, 0x48)) {
+ stride = 2;
+ tx_bar_no = NFP_NET_Q0_BAR;
+ rx_bar_no = NFP_NET_Q1_BAR;
+ dev_warn(&pdev->dev, "OBSOLETE Firmware detected - VF isolation not available\n");
+ } else {
+ switch (fw_ver.major) {
+ case 1 ... 3:
+ if (is_nfp3200) {
+ stride = 2;
+ tx_bar_no = NFP_NET_Q0_BAR;
+ rx_bar_no = NFP_NET_Q1_BAR;
+ } else {
+ stride = 4;
+ tx_bar_no = NFP_NET_Q0_BAR;
+ rx_bar_no = tx_bar_no;
+ }
+ break;
+ default:
+ dev_err(&pdev->dev, "Unsupported Firmware ABI %d.%d.%d.%d\n",
+ fw_ver.resv, fw_ver.class,
+ fw_ver.major, fw_ver.minor);
+ err = -EINVAL;
+ goto err_ctrl_unmap;
+ }
+ }
+
+ /* Find out how many rings are supported */
+ max_tx_rings = readl(ctrl_bar + NFP_NET_CFG_MAX_TXRINGS);
+ max_rx_rings = readl(ctrl_bar + NFP_NET_CFG_MAX_RXRINGS);
+
+ tx_bar_sz = NFP_QCP_QUEUE_ADDR_SZ * max_tx_rings * stride;
+ rx_bar_sz = NFP_QCP_QUEUE_ADDR_SZ * max_rx_rings * stride;
+
+ /* Sanity checks */
+ if (tx_bar_sz > pci_resource_len(pdev, tx_bar_no)) {
+ dev_err(&pdev->dev,
+ "TX BAR too small for number of TX rings. Adjusting\n");
+ tx_bar_sz = pci_resource_len(pdev, tx_bar_no);
+ max_tx_rings = (tx_bar_sz / NFP_QCP_QUEUE_ADDR_SZ) / 2;
+ }
+ if (rx_bar_sz > pci_resource_len(pdev, rx_bar_no)) {
+ dev_err(&pdev->dev,
+ "RX BAR too small for number of RX rings. Adjusting\n");
+ rx_bar_sz = pci_resource_len(pdev, rx_bar_no);
+ max_rx_rings = (rx_bar_sz / NFP_QCP_QUEUE_ADDR_SZ) / 2;
+ }
+
+ /* XXX Implement a workaround for THB-350 here. Ideally, we
+ * have a different PCI ID for A rev VFs.
+ */
+ switch (pdev->device) {
+ case PCI_DEVICE_NFP6000VF:
+ startq = readl(ctrl_bar + NFP_NET_CFG_START_TXQ);
+ tx_bar_off = NFP_PCIE_QUEUE(startq);
+ startq = readl(ctrl_bar + NFP_NET_CFG_START_RXQ);
+ rx_bar_off = NFP_PCIE_QUEUE(startq);
+ break;
+ default:
+ err = -ENODEV;
+ goto err_ctrl_unmap;
+ }
+
+ /* Allocate and initialise the netdev */
+ nn = nfp_net_netdev_alloc(pdev, max_tx_rings, max_rx_rings);
+ if (IS_ERR(nn)) {
+ err = PTR_ERR(nn);
+ goto err_ctrl_unmap;
+ }
+
+ nn->fw_ver = fw_ver;
+ nn->ctrl_bar = ctrl_bar;
+ nn->is_vf = 1;
+ nn->is_nfp3200 = is_nfp3200;
+ nn->stride_tx = stride;
+ nn->stride_rx = stride;
+
+ if (rx_bar_no == tx_bar_no) {
+ u32 bar_off, bar_sz;
+ resource_size_t map_addr;
+
+ /* Make a single overlapping BAR mapping */
+ if (tx_bar_off < rx_bar_off)
+ bar_off = tx_bar_off;
+ else
+ bar_off = rx_bar_off;
+
+ if ((tx_bar_off + tx_bar_sz) > (rx_bar_off + rx_bar_sz))
+ bar_sz = (tx_bar_off + tx_bar_sz) - bar_off;
+ else
+ bar_sz = (rx_bar_off + rx_bar_sz) - bar_off;
+
+ map_addr = pci_resource_start(pdev, tx_bar_no) + bar_off;
+ nn->q_bar = ioremap_nocache(map_addr, bar_sz);
+ if (!nn->q_bar) {
+ nn_err(nn, "Failed to map resource %d\n", tx_bar_no);
+ err = -EIO;
+ goto err_netdev_free;
+ }
+
+ /* TX queues */
+ nn->tx_bar = nn->q_bar + (tx_bar_off - bar_off);
+ /* RX queues */
+ nn->rx_bar = nn->q_bar + (rx_bar_off - bar_off);
+ } else {
+ resource_size_t map_addr;
+
+ /* TX queues */
+ map_addr = pci_resource_start(pdev, tx_bar_no) + tx_bar_off;
+ nn->tx_bar = ioremap_nocache(map_addr, tx_bar_sz);
+ if (!nn->tx_bar) {
+ nn_err(nn, "Failed to map resource %d\n", tx_bar_no);
+ err = -EIO;
+ goto err_netdev_free;
+ }
+
+ /* RX queues */
+ map_addr = pci_resource_start(pdev, rx_bar_no) + rx_bar_off;
+ nn->rx_bar = ioremap_nocache(map_addr, rx_bar_sz);
+ if (!nn->rx_bar) {
+ nn_err(nn, "Failed to map resource %d\n", rx_bar_no);
+ err = -EIO;
+ goto err_unmap_tx;
+ }
+ }
+
+ nfp_netvf_get_mac_addr(nn);
+
+ err = nfp_net_irqs_alloc(nn);
+ if (!err) {
+ nn_warn(nn, "Unable to allocate MSI-X Vectors. Exiting\n");
+ err = -EIO;
+ goto err_unmap_rx;
+ }
+
+ /* Get ME clock frequency from ctrl BAR
+ * XXX for now frequency is hardcoded until we figure out how
+ * to get the value from nfp-hwinfo into ctrl bar
+ */
+ nn->me_freq_mhz = 1200;
+
+ err = nfp_net_netdev_init(nn->netdev);
+ if (err)
+ goto err_irqs_disable;
+
+ pci_set_drvdata(pdev, nn);
+
+ nfp_net_info(nn);
+ nfp_net_debugfs_adapter_add(nn);
+
+ return 0;
+
+err_irqs_disable:
+ nfp_net_irqs_disable(nn);
+err_unmap_rx:
+ if (!nn->q_bar)
+ iounmap(nn->rx_bar);
+err_unmap_tx:
+ if (!nn->q_bar)
+ iounmap(nn->tx_bar);
+ else
+ iounmap(nn->q_bar);
+err_netdev_free:
+ pci_set_drvdata(pdev, NULL);
+ nfp_net_netdev_free(nn);
+err_ctrl_unmap:
+ iounmap(ctrl_bar);
+err_pci_regions:
+ pci_release_regions(pdev);
+err_pci_disable:
+ pci_disable_device(pdev);
+ return err;
+}
+
+static void nfp_netvf_pci_remove(struct pci_dev *pdev)
+{
+ struct nfp_net *nn = pci_get_drvdata(pdev);
+
+ /* Note, the order is slightly different from above as we need
+ * to keep the nn pointer around till we have freed everything.
+ */
+ nfp_net_debugfs_adapter_del(nn);
+
+ nfp_net_netdev_clean(nn->netdev);
+
+ nfp_net_irqs_disable(nn);
+
+ if (!nn->q_bar) {
+ iounmap(nn->rx_bar);
+ iounmap(nn->tx_bar);
+ } else {
+ iounmap(nn->q_bar);
+ }
+ iounmap(nn->ctrl_bar);
+
+ pci_set_drvdata(pdev, NULL);
+
+ nfp_net_netdev_free(nn);
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+}
+
+static struct pci_driver nfp_netvf_pci_driver = {
+ .name = nfp_net_driver_name,
+ .id_table = nfp_netvf_pci_device_ids,
+ .probe = nfp_netvf_pci_probe,
+ .remove = nfp_netvf_pci_remove,
+};
+
+static int __init nfp_netvf_init(void)
+{
+ int err;
+
+ pr_info("%s: NFP VF Network driver, Copyright (C) 2014-2015 Netronome Systems\n",
+ nfp_net_driver_name);
+
+ nfp_net_debugfs_create();
+ err = pci_register_driver(&nfp_netvf_pci_driver);
+ if (err) {
+ nfp_net_debugfs_destroy();
+ return err;
+ }
+
+ return 0;
+}
+
+static void __exit nfp_netvf_exit(void)
+{
+ pci_unregister_driver(&nfp_netvf_pci_driver);
+ nfp_net_debugfs_destroy();
+}
+
+module_init(nfp_netvf_init);
+module_exit(nfp_netvf_exit);
+
+MODULE_AUTHOR("Netronome Systems <oss-drivers@netronome.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("NFP VF network device driver");
diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c
index b159ef8303cc..b1ce7aaa8f8b 100644
--- a/drivers/net/ethernet/nxp/lpc_eth.c
+++ b/drivers/net/ethernet/nxp/lpc_eth.c
@@ -797,7 +797,7 @@ static int lpc_mii_probe(struct net_device *ndev)
netdev_info(ndev, "using MII interface\n");
else
netdev_info(ndev, "using RMII interface\n");
- phydev = phy_connect(ndev, dev_name(&phydev->dev),
+ phydev = phy_connect(ndev, phydev_name(phydev),
&lpc_handle_link_change,
lpc_phy_interface_mode(&pldat->pdev->dev));
@@ -816,15 +816,14 @@ static int lpc_mii_probe(struct net_device *ndev)
pldat->duplex = -1;
pldat->phy_dev = phydev;
- netdev_info(ndev,
- "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
- phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
+ phy_attached_info(phydev);
+
return 0;
}
static int lpc_mii_init(struct netdata_local *pldat)
{
- int err = -ENXIO, i;
+ int err = -ENXIO;
pldat->mii_bus = mdiobus_alloc();
if (!pldat->mii_bus) {
@@ -851,19 +850,10 @@ static int lpc_mii_init(struct netdata_local *pldat)
pldat->mii_bus->priv = pldat;
pldat->mii_bus->parent = &pldat->pdev->dev;
- pldat->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!pldat->mii_bus->irq) {
- err = -ENOMEM;
- goto err_out_1;
- }
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- pldat->mii_bus->irq[i] = PHY_POLL;
-
platform_set_drvdata(pldat->pdev, pldat->mii_bus);
if (mdiobus_register(pldat->mii_bus))
- goto err_out_free_mdio_irq;
+ goto err_out_unregister_bus;
if (lpc_mii_probe(pldat->ndev) != 0)
goto err_out_unregister_bus;
@@ -872,9 +862,6 @@ static int lpc_mii_init(struct netdata_local *pldat)
err_out_unregister_bus:
mdiobus_unregister(pldat->mii_bus);
-err_out_free_mdio_irq:
- kfree(pldat->mii_bus->irq);
-err_out_1:
mdiobus_free(pldat->mii_bus);
err_out:
return err;
@@ -1326,7 +1313,7 @@ static int lpc_eth_drv_probe(struct platform_device *pdev)
/* Get platform resources */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
- if ((!res) || (irq < 0) || (irq >= NR_IRQS)) {
+ if (!res || irq < 0) {
dev_err(&pdev->dev, "error getting resources.\n");
ret = -ENXIO;
goto err_exit;
diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c
index 08d4be616064..e097e6baaac4 100644
--- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c
+++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c
@@ -500,7 +500,7 @@ void pch_gbe_check_options(struct pch_gbe_adapter *adapter)
val = XsumTX;
pch_gbe_validate_option(&val, &opt, adapter);
if (!val)
- dev->features &= ~NETIF_F_ALL_CSUM;
+ dev->features &= ~NETIF_F_CSUM_MASK;
}
{ /* Flow Control */
static const struct pch_gbe_option opt = {
diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h
index ac17d8669b1a..1292c360390c 100644
--- a/drivers/net/ethernet/qlogic/qed/qed.h
+++ b/drivers/net/ethernet/qlogic/qed/qed.h
@@ -299,6 +299,7 @@ struct qed_hwfn {
/* Flag indicating whether interrupts are enabled or not*/
bool b_int_enabled;
+ bool b_int_requested;
struct qed_mcp_info *mcp_info;
@@ -491,6 +492,8 @@ u32 qed_unzip_data(struct qed_hwfn *p_hwfn,
u32 input_len, u8 *input_buf,
u32 max_size, u8 *unzip_buf);
+int qed_slowpath_irq_req(struct qed_hwfn *hwfn);
+
#define QED_ETH_INTERFACE_VERSION 300
#endif /* _QED_H */
diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c
index 803b190ccada..817bbd5476ff 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_dev.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c
@@ -1385,52 +1385,63 @@ err0:
return rc;
}
-static u32 qed_hw_bar_size(struct qed_dev *cdev,
- u8 bar_id)
+static u32 qed_hw_bar_size(struct qed_hwfn *p_hwfn,
+ u8 bar_id)
{
- u32 size = pci_resource_len(cdev->pdev, (bar_id > 0) ? 2 : 0);
+ u32 bar_reg = (bar_id == 0 ? PGLUE_B_REG_PF_BAR0_SIZE
+ : PGLUE_B_REG_PF_BAR1_SIZE);
+ u32 val = qed_rd(p_hwfn, p_hwfn->p_main_ptt, bar_reg);
- return size / cdev->num_hwfns;
+ /* Get the BAR size(in KB) from hardware given val */
+ return 1 << (val + 15);
}
int qed_hw_prepare(struct qed_dev *cdev,
int personality)
{
- int rc, i;
+ struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
+ int rc;
/* Store the precompiled init data ptrs */
qed_init_iro_array(cdev);
/* Initialize the first hwfn - will learn number of hwfns */
- rc = qed_hw_prepare_single(&cdev->hwfns[0], cdev->regview,
+ rc = qed_hw_prepare_single(p_hwfn,
+ cdev->regview,
cdev->doorbells, personality);
if (rc)
return rc;
- personality = cdev->hwfns[0].hw_info.personality;
+ personality = p_hwfn->hw_info.personality;
/* Initialize the rest of the hwfns */
- for (i = 1; i < cdev->num_hwfns; i++) {
+ if (cdev->num_hwfns > 1) {
void __iomem *p_regview, *p_doorbell;
+ u8 __iomem *addr;
+
+ /* adjust bar offset for second engine */
+ addr = cdev->regview + qed_hw_bar_size(p_hwfn, 0) / 2;
+ p_regview = addr;
- p_regview = cdev->regview +
- i * qed_hw_bar_size(cdev, 0);
- p_doorbell = cdev->doorbells +
- i * qed_hw_bar_size(cdev, 1);
- rc = qed_hw_prepare_single(&cdev->hwfns[i], p_regview,
+ /* adjust doorbell bar offset for second engine */
+ addr = cdev->doorbells + qed_hw_bar_size(p_hwfn, 1) / 2;
+ p_doorbell = addr;
+
+ /* prepare second hw function */
+ rc = qed_hw_prepare_single(&cdev->hwfns[1], p_regview,
p_doorbell, personality);
+
+ /* in case of error, need to free the previously
+ * initiliazed hwfn 0.
+ */
if (rc) {
- /* Cleanup previously initialized hwfns */
- while (--i >= 0) {
- qed_init_free(&cdev->hwfns[i]);
- qed_mcp_free(&cdev->hwfns[i]);
- qed_hw_hwfn_free(&cdev->hwfns[i]);
- }
- return rc;
+ qed_init_free(p_hwfn);
+ qed_mcp_free(p_hwfn);
+ qed_hw_hwfn_free(p_hwfn);
}
}
- return 0;
+ return rc;
}
void qed_hw_remove(struct qed_dev *cdev)
diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
index b2f8e854dfd1..264e954675d1 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h
@@ -3993,6 +3993,8 @@ struct public_drv_mb {
#define DRV_MSG_CODE_PHY_CORE_WRITE 0x000e0000
#define DRV_MSG_CODE_SET_VERSION 0x000f0000
+#define DRV_MSG_CODE_SET_LED_MODE 0x00200000
+
#define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff
u32 drv_mb_param;
@@ -4044,6 +4046,10 @@ struct public_drv_mb {
#define DRV_MB_PARAM_CFG_VF_MSIX_SB_NUM_SHIFT 8
#define DRV_MB_PARAM_CFG_VF_MSIX_SB_NUM_MASK 0x0000FF00
+#define DRV_MB_PARAM_SET_LED_MODE_OPER 0x0
+#define DRV_MB_PARAM_SET_LED_MODE_ON 0x1
+#define DRV_MB_PARAM_SET_LED_MODE_OFF 0x2
+
u32 fw_mb_header;
#define FW_MSG_CODE_MASK 0xffff0000
#define FW_MSG_CODE_DRV_LOAD_ENGINE 0x10100000
diff --git a/drivers/net/ethernet/qlogic/qed/qed_int.c b/drivers/net/ethernet/qlogic/qed/qed_int.c
index de50e84902af..9cc9d62c1fec 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_int.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_int.c
@@ -783,22 +783,16 @@ void qed_int_igu_enable_int(struct qed_hwfn *p_hwfn,
qed_wr(p_hwfn, p_ptt, IGU_REG_PF_CONFIGURATION, igu_pf_conf);
}
-void qed_int_igu_enable(struct qed_hwfn *p_hwfn,
- struct qed_ptt *p_ptt,
- enum qed_int_mode int_mode)
+int qed_int_igu_enable(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt,
+ enum qed_int_mode int_mode)
{
- int i;
-
- p_hwfn->b_int_enabled = 1;
+ int rc, i;
/* Mask non-link attentions */
for (i = 0; i < 9; i++)
qed_wr(p_hwfn, p_ptt,
MISC_REG_AEU_ENABLE1_IGU_OUT_0 + (i << 2), 0);
- /* Enable interrupt Generation */
- qed_int_igu_enable_int(p_hwfn, p_ptt, int_mode);
-
/* Configure AEU signal change to produce attentions for link */
qed_wr(p_hwfn, p_ptt, IGU_REG_LEADING_EDGE_LATCH, 0xfff);
qed_wr(p_hwfn, p_ptt, IGU_REG_TRAILING_EDGE_LATCH, 0xfff);
@@ -808,6 +802,19 @@ void qed_int_igu_enable(struct qed_hwfn *p_hwfn,
/* Unmask AEU signals toward IGU */
qed_wr(p_hwfn, p_ptt, MISC_REG_AEU_MASK_ATTN_IGU, 0xff);
+ if ((int_mode != QED_INT_MODE_INTA) || IS_LEAD_HWFN(p_hwfn)) {
+ rc = qed_slowpath_irq_req(p_hwfn);
+ if (rc != 0) {
+ DP_NOTICE(p_hwfn, "Slowpath IRQ request failed\n");
+ return -EINVAL;
+ }
+ p_hwfn->b_int_requested = true;
+ }
+ /* Enable interrupt Generation */
+ qed_int_igu_enable_int(p_hwfn, p_ptt, int_mode);
+ p_hwfn->b_int_enabled = 1;
+
+ return rc;
}
void qed_int_igu_disable_int(struct qed_hwfn *p_hwfn,
@@ -1127,3 +1134,11 @@ int qed_int_get_num_sbs(struct qed_hwfn *p_hwfn,
return info->igu_sb_cnt;
}
+
+void qed_int_disable_post_isr_release(struct qed_dev *cdev)
+{
+ int i;
+
+ for_each_hwfn(cdev, i)
+ cdev->hwfns[i].b_int_requested = false;
+}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_int.h b/drivers/net/ethernet/qlogic/qed/qed_int.h
index 16b57518e706..51e0b09a7f47 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_int.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_int.h
@@ -169,10 +169,14 @@ int qed_int_get_num_sbs(struct qed_hwfn *p_hwfn,
int *p_iov_blks);
/**
- * @file
+ * @brief qed_int_disable_post_isr_release - performs the cleanup post ISR
+ * release. The API need to be called after releasing all slowpath IRQs
+ * of the device.
+ *
+ * @param cdev
*
- * @brief Interrupt handler
*/
+void qed_int_disable_post_isr_release(struct qed_dev *cdev);
#define QED_CAU_DEF_RX_TIMER_RES 0
#define QED_CAU_DEF_TX_TIMER_RES 0
@@ -366,10 +370,11 @@ void qed_int_setup(struct qed_hwfn *p_hwfn,
* @param p_hwfn
* @param p_ptt
* @param int_mode
+ *
+ * @return int
*/
-void qed_int_igu_enable(struct qed_hwfn *p_hwfn,
- struct qed_ptt *p_ptt,
- enum qed_int_mode int_mode);
+int qed_int_igu_enable(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt,
+ enum qed_int_mode int_mode);
/**
* @brief - Initialize CAU status block entry
diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c
index 947c7af72b25..9d76ce249277 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_main.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_main.c
@@ -476,41 +476,22 @@ static irqreturn_t qed_single_int(int irq, void *dev_instance)
return rc;
}
-static int qed_slowpath_irq_req(struct qed_dev *cdev)
+int qed_slowpath_irq_req(struct qed_hwfn *hwfn)
{
- int i = 0, rc = 0;
+ struct qed_dev *cdev = hwfn->cdev;
+ int rc = 0;
+ u8 id;
if (cdev->int_params.out.int_mode == QED_INT_MODE_MSIX) {
- /* Request all the slowpath MSI-X vectors */
- for (i = 0; i < cdev->num_hwfns; i++) {
- snprintf(cdev->hwfns[i].name, NAME_SIZE,
- "sp-%d-%02x:%02x.%02x",
- i, cdev->pdev->bus->number,
- PCI_SLOT(cdev->pdev->devfn),
- cdev->hwfns[i].abs_pf_id);
-
- rc = request_irq(cdev->int_params.msix_table[i].vector,
- qed_msix_sp_int, 0,
- cdev->hwfns[i].name,
- cdev->hwfns[i].sp_dpc);
- if (rc)
- break;
-
- DP_VERBOSE(&cdev->hwfns[i],
- (NETIF_MSG_INTR | QED_MSG_SP),
+ id = hwfn->my_id;
+ snprintf(hwfn->name, NAME_SIZE, "sp-%d-%02x:%02x.%02x",
+ id, cdev->pdev->bus->number,
+ PCI_SLOT(cdev->pdev->devfn), hwfn->abs_pf_id);
+ rc = request_irq(cdev->int_params.msix_table[id].vector,
+ qed_msix_sp_int, 0, hwfn->name, hwfn->sp_dpc);
+ if (!rc)
+ DP_VERBOSE(hwfn, (NETIF_MSG_INTR | QED_MSG_SP),
"Requested slowpath MSI-X\n");
- }
-
- if (i != cdev->num_hwfns) {
- /* Free already request MSI-X vectors */
- for (i--; i >= 0; i--) {
- unsigned int vec =
- cdev->int_params.msix_table[i].vector;
- synchronize_irq(vec);
- free_irq(cdev->int_params.msix_table[i].vector,
- cdev->hwfns[i].sp_dpc);
- }
- }
} else {
unsigned long flags = 0;
@@ -534,13 +515,17 @@ static void qed_slowpath_irq_free(struct qed_dev *cdev)
if (cdev->int_params.out.int_mode == QED_INT_MODE_MSIX) {
for_each_hwfn(cdev, i) {
+ if (!cdev->hwfns[i].b_int_requested)
+ break;
synchronize_irq(cdev->int_params.msix_table[i].vector);
free_irq(cdev->int_params.msix_table[i].vector,
cdev->hwfns[i].sp_dpc);
}
} else {
- free_irq(cdev->pdev->irq, cdev);
+ if (QED_LEADING_HWFN(cdev)->b_int_requested)
+ free_irq(cdev->pdev->irq, cdev);
}
+ qed_int_disable_post_isr_release(cdev);
}
static int qed_nic_stop(struct qed_dev *cdev)
@@ -765,16 +750,11 @@ static int qed_slowpath_start(struct qed_dev *cdev,
if (rc)
goto err1;
- /* Request the slowpath IRQ */
- rc = qed_slowpath_irq_req(cdev);
- if (rc)
- goto err2;
-
/* Allocate stream for unzipping */
rc = qed_alloc_stream_mem(cdev);
if (rc) {
DP_NOTICE(cdev, "Failed to allocate stream memory\n");
- goto err3;
+ goto err2;
}
/* Start the slowpath */
@@ -1135,6 +1115,23 @@ static int qed_drain(struct qed_dev *cdev)
return 0;
}
+static int qed_set_led(struct qed_dev *cdev, enum qed_led_mode mode)
+{
+ struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev);
+ struct qed_ptt *ptt;
+ int status = 0;
+
+ ptt = qed_ptt_acquire(hwfn);
+ if (!ptt)
+ return -EAGAIN;
+
+ status = qed_mcp_set_led(hwfn, ptt, mode);
+
+ qed_ptt_release(hwfn, ptt);
+
+ return status;
+}
+
const struct qed_common_ops qed_common_ops_pass = {
.probe = &qed_probe,
.remove = &qed_remove,
@@ -1155,6 +1152,7 @@ const struct qed_common_ops qed_common_ops_pass = {
.update_msglvl = &qed_init_dp,
.chain_alloc = &qed_chain_alloc,
.chain_free = &qed_chain_free,
+ .set_led = &qed_set_led,
};
u32 qed_get_protocol_version(enum qed_protocol protocol)
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
index 20d048cdcb88..ba1b1f1ef789 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
@@ -858,3 +858,30 @@ qed_mcp_send_drv_version(struct qed_hwfn *p_hwfn,
return 0;
}
+
+int qed_mcp_set_led(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt,
+ enum qed_led_mode mode)
+{
+ u32 resp = 0, param = 0, drv_mb_param;
+ int rc;
+
+ switch (mode) {
+ case QED_LED_MODE_ON:
+ drv_mb_param = DRV_MB_PARAM_SET_LED_MODE_ON;
+ break;
+ case QED_LED_MODE_OFF:
+ drv_mb_param = DRV_MB_PARAM_SET_LED_MODE_OFF;
+ break;
+ case QED_LED_MODE_RESTORE:
+ drv_mb_param = DRV_MB_PARAM_SET_LED_MODE_OPER;
+ break;
+ default:
+ DP_NOTICE(p_hwfn, "Invalid LED mode %d\n", mode);
+ return -EINVAL;
+ }
+
+ rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_SET_LED_MODE,
+ drv_mb_param, &resp, &param);
+
+ return rc;
+}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h
index dbaae586b4a7..506197d5c3dd 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h
@@ -224,6 +224,19 @@ qed_mcp_send_drv_version(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct qed_mcp_drv_version *p_ver);
+/**
+ * @brief Set LED status
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param mode - LED mode
+ *
+ * @return int - 0 - operation was successful.
+ */
+int qed_mcp_set_led(struct qed_hwfn *p_hwfn,
+ struct qed_ptt *p_ptt,
+ enum qed_led_mode mode);
+
/* Using hwfn number (and not pf_num) is required since in CMT mode,
* same pf_num may be used by two different hwfn
* TODO - this shouldn't really be in .h file, but until all fields
diff --git a/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h b/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h
index 7a5ce5914ace..e8df12335a97 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h
@@ -363,4 +363,8 @@
0x7 << 0)
#define MCP_REG_NVM_CFG4_FLASH_SIZE_SHIFT \
0
+#define PGLUE_B_REG_PF_BAR0_SIZE \
+ 0x2aae60UL
+#define PGLUE_B_REG_PF_BAR1_SIZE \
+ 0x2aae64UL
#endif
diff --git a/drivers/net/ethernet/qlogic/qed/qed_sp.h b/drivers/net/ethernet/qlogic/qed/qed_sp.h
index 31a1f1eb4f56..287fadfab52d 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_sp.h
+++ b/drivers/net/ethernet/qlogic/qed/qed_sp.h
@@ -124,8 +124,12 @@ struct qed_spq {
dma_addr_t p_phys;
struct qed_spq_entry *p_virt;
- /* Used as index for completions (returns on EQ by FW) */
- u16 echo_idx;
+#define SPQ_RING_SIZE \
+ (CORE_SPQE_PAGE_SIZE_BYTES / sizeof(struct slow_path_element))
+
+ /* Bitmap for handling out-of-order completions */
+ DECLARE_BITMAP(p_comp_bitmap, SPQ_RING_SIZE);
+ u8 comp_bitmap_idx;
/* Statistics */
u32 unlimited_pending_count;
diff --git a/drivers/net/ethernet/qlogic/qed/qed_spq.c b/drivers/net/ethernet/qlogic/qed/qed_spq.c
index 7c0b8459666e..3dd548ab8df1 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_spq.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_spq.c
@@ -112,8 +112,6 @@ static int
qed_spq_fill_entry(struct qed_hwfn *p_hwfn,
struct qed_spq_entry *p_ent)
{
- p_ent->elem.hdr.echo = 0;
- p_hwfn->p_spq->echo_idx++;
p_ent->flags = 0;
switch (p_ent->comp_mode) {
@@ -195,10 +193,12 @@ static int qed_spq_hw_post(struct qed_hwfn *p_hwfn,
struct qed_spq *p_spq,
struct qed_spq_entry *p_ent)
{
- struct qed_chain *p_chain = &p_hwfn->p_spq->chain;
+ struct qed_chain *p_chain = &p_hwfn->p_spq->chain;
+ u16 echo = qed_chain_get_prod_idx(p_chain);
struct slow_path_element *elem;
struct core_db_data db;
+ p_ent->elem.hdr.echo = cpu_to_le16(echo);
elem = qed_chain_produce(p_chain);
if (!elem) {
DP_NOTICE(p_hwfn, "Failed to produce from SPQ chain\n");
@@ -437,7 +437,9 @@ void qed_spq_setup(struct qed_hwfn *p_hwfn)
p_spq->comp_count = 0;
p_spq->comp_sent_count = 0;
p_spq->unlimited_pending_count = 0;
- p_spq->echo_idx = 0;
+
+ bitmap_zero(p_spq->p_comp_bitmap, SPQ_RING_SIZE);
+ p_spq->comp_bitmap_idx = 0;
/* SPQ cid, cannot fail */
qed_cxt_acquire_cid(p_hwfn, PROTOCOLID_CORE, &p_spq->cid);
@@ -582,26 +584,32 @@ qed_spq_add_entry(struct qed_hwfn *p_hwfn,
struct qed_spq *p_spq = p_hwfn->p_spq;
if (p_ent->queue == &p_spq->unlimited_pending) {
- struct qed_spq_entry *p_en2;
if (list_empty(&p_spq->free_pool)) {
list_add_tail(&p_ent->list, &p_spq->unlimited_pending);
p_spq->unlimited_pending_count++;
return 0;
- }
+ } else {
+ struct qed_spq_entry *p_en2;
- p_en2 = list_first_entry(&p_spq->free_pool,
- struct qed_spq_entry,
- list);
- list_del(&p_en2->list);
+ p_en2 = list_first_entry(&p_spq->free_pool,
+ struct qed_spq_entry,
+ list);
+ list_del(&p_en2->list);
+
+ /* Copy the ring element physical pointer to the new
+ * entry, since we are about to override the entire ring
+ * entry and don't want to lose the pointer.
+ */
+ p_ent->elem.data_ptr = p_en2->elem.data_ptr;
- /* Strcut assignment */
- *p_en2 = *p_ent;
+ *p_en2 = *p_ent;
- kfree(p_ent);
+ kfree(p_ent);
- p_ent = p_en2;
+ p_ent = p_en2;
+ }
}
/* entry is to be placed in 'pending' queue */
@@ -777,13 +785,38 @@ int qed_spq_completion(struct qed_hwfn *p_hwfn,
list_for_each_entry_safe(p_ent, tmp, &p_spq->completion_pending,
list) {
if (p_ent->elem.hdr.echo == echo) {
+ u16 pos = le16_to_cpu(echo) % SPQ_RING_SIZE;
+
list_del(&p_ent->list);
- qed_chain_return_produced(&p_spq->chain);
+ /* Avoid overriding of SPQ entries when getting
+ * out-of-order completions, by marking the completions
+ * in a bitmap and increasing the chain consumer only
+ * for the first successive completed entries.
+ */
+ bitmap_set(p_spq->p_comp_bitmap, pos, SPQ_RING_SIZE);
+
+ while (test_bit(p_spq->comp_bitmap_idx,
+ p_spq->p_comp_bitmap)) {
+ bitmap_clear(p_spq->p_comp_bitmap,
+ p_spq->comp_bitmap_idx,
+ SPQ_RING_SIZE);
+ p_spq->comp_bitmap_idx++;
+ qed_chain_return_produced(&p_spq->chain);
+ }
+
p_spq->comp_count++;
found = p_ent;
break;
}
+
+ /* This is relatively uncommon - depends on scenarios
+ * which have mutliple per-PF sent ramrods.
+ */
+ DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
+ "Got completion for echo %04x - doesn't match echo %04x in completion pending list\n",
+ le16_to_cpu(echo),
+ le16_to_cpu(p_ent->elem.hdr.echo));
}
/* Release lock before callback, as callback may post
diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h
index ea00d5f3bab4..7c6caf7f6612 100644
--- a/drivers/net/ethernet/qlogic/qede/qede.h
+++ b/drivers/net/ethernet/qlogic/qede/qede.h
@@ -116,6 +116,7 @@ struct qede_dev {
(edev)->dev_info.num_tc)
struct qede_fastpath *fp_array;
+ u16 req_rss;
u16 num_rss;
u8 num_tc;
#define QEDE_RSS_CNT(edev) ((edev)->num_rss)
@@ -269,13 +270,13 @@ int qede_change_mtu(struct net_device *dev, int new_mtu);
void qede_fill_by_demand_stats(struct qede_dev *edev);
#define RX_RING_SIZE_POW 13
-#define RX_RING_SIZE BIT(RX_RING_SIZE_POW)
+#define RX_RING_SIZE ((u16)BIT(RX_RING_SIZE_POW))
#define NUM_RX_BDS_MAX (RX_RING_SIZE - 1)
#define NUM_RX_BDS_MIN 128
#define NUM_RX_BDS_DEF NUM_RX_BDS_MAX
#define TX_RING_SIZE_POW 13
-#define TX_RING_SIZE BIT(TX_RING_SIZE_POW)
+#define TX_RING_SIZE ((u16)BIT(TX_RING_SIZE_POW))
#define NUM_TX_BDS_MAX (TX_RING_SIZE - 1)
#define NUM_TX_BDS_MIN 128
#define NUM_TX_BDS_DEF NUM_TX_BDS_MAX
diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
index 3a362476a22c..e442b85c9a5e 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
@@ -322,6 +322,30 @@ static void qede_set_msglevel(struct net_device *ndev, u32 level)
dp_module, dp_level);
}
+static int qede_nway_reset(struct net_device *dev)
+{
+ struct qede_dev *edev = netdev_priv(dev);
+ struct qed_link_output current_link;
+ struct qed_link_params link_params;
+
+ if (!netif_running(dev))
+ return 0;
+
+ memset(&current_link, 0, sizeof(current_link));
+ edev->ops->common->get_link(edev->cdev, &current_link);
+ if (!current_link.link_up)
+ return 0;
+
+ /* Toggle the link */
+ memset(&link_params, 0, sizeof(link_params));
+ link_params.link_up = false;
+ edev->ops->common->set_link(edev->cdev, &link_params);
+ link_params.link_up = true;
+ edev->ops->common->set_link(edev->cdev, &link_params);
+
+ return 0;
+}
+
static u32 qede_get_link(struct net_device *dev)
{
struct qede_dev *edev = netdev_priv(dev);
@@ -333,6 +357,106 @@ static u32 qede_get_link(struct net_device *dev)
return current_link.link_up;
}
+static void qede_get_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *ering)
+{
+ struct qede_dev *edev = netdev_priv(dev);
+
+ ering->rx_max_pending = NUM_RX_BDS_MAX;
+ ering->rx_pending = edev->q_num_rx_buffers;
+ ering->tx_max_pending = NUM_TX_BDS_MAX;
+ ering->tx_pending = edev->q_num_tx_buffers;
+}
+
+static int qede_set_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *ering)
+{
+ struct qede_dev *edev = netdev_priv(dev);
+
+ DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
+ "Set ring params command parameters: rx_pending = %d, tx_pending = %d\n",
+ ering->rx_pending, ering->tx_pending);
+
+ /* Validate legality of configuration */
+ if (ering->rx_pending > NUM_RX_BDS_MAX ||
+ ering->rx_pending < NUM_RX_BDS_MIN ||
+ ering->tx_pending > NUM_TX_BDS_MAX ||
+ ering->tx_pending < NUM_TX_BDS_MIN) {
+ DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
+ "Can only support Rx Buffer size [0%08x,...,0x%08x] and Tx Buffer size [0x%08x,...,0x%08x]\n",
+ NUM_RX_BDS_MIN, NUM_RX_BDS_MAX,
+ NUM_TX_BDS_MIN, NUM_TX_BDS_MAX);
+ return -EINVAL;
+ }
+
+ /* Change ring size and re-load */
+ edev->q_num_rx_buffers = ering->rx_pending;
+ edev->q_num_tx_buffers = ering->tx_pending;
+
+ if (netif_running(edev->ndev))
+ qede_reload(edev, NULL, NULL);
+
+ return 0;
+}
+
+static void qede_get_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *epause)
+{
+ struct qede_dev *edev = netdev_priv(dev);
+ struct qed_link_output current_link;
+
+ memset(&current_link, 0, sizeof(current_link));
+ edev->ops->common->get_link(edev->cdev, &current_link);
+
+ if (current_link.pause_config & QED_LINK_PAUSE_AUTONEG_ENABLE)
+ epause->autoneg = true;
+ if (current_link.pause_config & QED_LINK_PAUSE_RX_ENABLE)
+ epause->rx_pause = true;
+ if (current_link.pause_config & QED_LINK_PAUSE_TX_ENABLE)
+ epause->tx_pause = true;
+
+ DP_VERBOSE(edev, QED_MSG_DEBUG,
+ "ethtool_pauseparam: cmd %d autoneg %d rx_pause %d tx_pause %d\n",
+ epause->cmd, epause->autoneg, epause->rx_pause,
+ epause->tx_pause);
+}
+
+static int qede_set_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *epause)
+{
+ struct qede_dev *edev = netdev_priv(dev);
+ struct qed_link_params params;
+ struct qed_link_output current_link;
+
+ if (!edev->dev_info.common.is_mf) {
+ DP_INFO(edev,
+ "Pause parameters can not be updated in non-default mode\n");
+ return -EOPNOTSUPP;
+ }
+
+ memset(&current_link, 0, sizeof(current_link));
+ edev->ops->common->get_link(edev->cdev, &current_link);
+
+ memset(&params, 0, sizeof(params));
+ params.override_flags |= QED_LINK_OVERRIDE_PAUSE_CONFIG;
+ if (epause->autoneg) {
+ if (!(current_link.supported_caps & SUPPORTED_Autoneg)) {
+ DP_INFO(edev, "autoneg not supported\n");
+ return -EINVAL;
+ }
+ params.pause_config |= QED_LINK_PAUSE_AUTONEG_ENABLE;
+ }
+ if (epause->rx_pause)
+ params.pause_config |= QED_LINK_PAUSE_RX_ENABLE;
+ if (epause->tx_pause)
+ params.pause_config |= QED_LINK_PAUSE_TX_ENABLE;
+
+ params.link_up = true;
+ edev->ops->common->set_link(edev->cdev, &params);
+
+ return 0;
+}
+
static void qede_update_mtu(struct qede_dev *edev, union qede_reload_args *args)
{
edev->ndev->mtu = args->mtu;
@@ -366,17 +490,104 @@ int qede_change_mtu(struct net_device *ndev, int new_mtu)
return 0;
}
+static void qede_get_channels(struct net_device *dev,
+ struct ethtool_channels *channels)
+{
+ struct qede_dev *edev = netdev_priv(dev);
+
+ channels->max_combined = QEDE_MAX_RSS_CNT(edev);
+ channels->combined_count = QEDE_RSS_CNT(edev);
+}
+
+static int qede_set_channels(struct net_device *dev,
+ struct ethtool_channels *channels)
+{
+ struct qede_dev *edev = netdev_priv(dev);
+
+ DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
+ "set-channels command parameters: rx = %d, tx = %d, other = %d, combined = %d\n",
+ channels->rx_count, channels->tx_count,
+ channels->other_count, channels->combined_count);
+
+ /* We don't support separate rx / tx, nor `other' channels. */
+ if (channels->rx_count || channels->tx_count ||
+ channels->other_count || (channels->combined_count == 0) ||
+ (channels->combined_count > QEDE_MAX_RSS_CNT(edev))) {
+ DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
+ "command parameters not supported\n");
+ return -EINVAL;
+ }
+
+ /* Check if there was a change in the active parameters */
+ if (channels->combined_count == QEDE_RSS_CNT(edev)) {
+ DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
+ "No change in active parameters\n");
+ return 0;
+ }
+
+ /* We need the number of queues to be divisible between the hwfns */
+ if (channels->combined_count % edev->dev_info.common.num_hwfns) {
+ DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
+ "Number of channels must be divisable by %04x\n",
+ edev->dev_info.common.num_hwfns);
+ return -EINVAL;
+ }
+
+ /* Set number of queues and reload if necessary */
+ edev->req_rss = channels->combined_count;
+ if (netif_running(dev))
+ qede_reload(edev, NULL, NULL);
+
+ return 0;
+}
+
+static int qede_set_phys_id(struct net_device *dev,
+ enum ethtool_phys_id_state state)
+{
+ struct qede_dev *edev = netdev_priv(dev);
+ u8 led_state = 0;
+
+ switch (state) {
+ case ETHTOOL_ID_ACTIVE:
+ return 1; /* cycle on/off once per second */
+
+ case ETHTOOL_ID_ON:
+ led_state = QED_LED_MODE_ON;
+ break;
+
+ case ETHTOOL_ID_OFF:
+ led_state = QED_LED_MODE_OFF;
+ break;
+
+ case ETHTOOL_ID_INACTIVE:
+ led_state = QED_LED_MODE_RESTORE;
+ break;
+ }
+
+ edev->ops->common->set_led(edev->cdev, led_state);
+
+ return 0;
+}
+
static const struct ethtool_ops qede_ethtool_ops = {
.get_settings = qede_get_settings,
.set_settings = qede_set_settings,
.get_drvinfo = qede_get_drvinfo,
.get_msglevel = qede_get_msglevel,
.set_msglevel = qede_set_msglevel,
+ .nway_reset = qede_nway_reset,
.get_link = qede_get_link,
+ .get_ringparam = qede_get_ringparam,
+ .set_ringparam = qede_set_ringparam,
+ .get_pauseparam = qede_get_pauseparam,
+ .set_pauseparam = qede_set_pauseparam,
.get_strings = qede_get_strings,
+ .set_phys_id = qede_set_phys_id,
.get_ethtool_stats = qede_get_ethtool_stats,
.get_sset_count = qede_get_sset_count,
+ .get_channels = qede_get_channels,
+ .set_channels = qede_set_channels,
};
void qede_set_ethtool_ops(struct net_device *dev)
diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c
index f4657a2e730a..6237f10b5119 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_main.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_main.c
@@ -1502,8 +1502,11 @@ static int qede_set_num_queues(struct qede_dev *edev)
u16 rss_num;
/* Setup queues according to possible resources*/
- rss_num = netif_get_num_default_rss_queues() *
- edev->dev_info.common.num_hwfns;
+ if (edev->req_rss)
+ rss_num = edev->req_rss;
+ else
+ rss_num = netif_get_num_default_rss_queues() *
+ edev->dev_info.common.num_hwfns;
rss_num = min_t(u16, QEDE_MAX_RSS_CNT(edev), rss_num);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c
index be7d7a62cc0d..34906750b7e7 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c
@@ -246,12 +246,13 @@ int qlcnic_83xx_check_vnic_state(struct qlcnic_adapter *adapter)
u32 state;
state = QLCRDX(ahw, QLC_83XX_VNIC_STATE);
- while (state != QLCNIC_DEV_NPAR_OPER && idc->vnic_wait_limit--) {
+ while (state != QLCNIC_DEV_NPAR_OPER && idc->vnic_wait_limit) {
+ idc->vnic_wait_limit--;
msleep(1000);
state = QLCRDX(ahw, QLC_83XX_VNIC_STATE);
}
- if (!idc->vnic_wait_limit) {
+ if (state != QLCNIC_DEV_NPAR_OPER) {
dev_err(&adapter->pdev->dev,
"vNIC mode not operational, state check timed out.\n");
return -EIO;
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
index a5f422f26cb4..daf05155b732 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
@@ -772,8 +772,10 @@ int qlcnic_82xx_config_intrpt(struct qlcnic_adapter *adapter, u8 op_type)
int i, err = 0;
for (i = 0; i < ahw->num_msix; i++) {
- qlcnic_alloc_mbx_args(&cmd, adapter,
- QLCNIC_CMD_MQ_TX_CONFIG_INTR);
+ err = qlcnic_alloc_mbx_args(&cmd, adapter,
+ QLCNIC_CMD_MQ_TX_CONFIG_INTR);
+ if (err)
+ return err;
type = op_type ? QLCNIC_INTRPT_ADD : QLCNIC_INTRPT_DEL;
val = type | (ahw->intr_tbl[i].type << 4);
if (ahw->intr_tbl[i].type == QLCNIC_INTRPT_MSIX)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
index a72bcddf160a..4b76c69fe86d 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c
@@ -167,7 +167,7 @@ struct qlcnic_dcb_cfg {
u32 version;
};
-static struct qlcnic_dcb_ops qlcnic_83xx_dcb_ops = {
+static const struct qlcnic_dcb_ops qlcnic_83xx_dcb_ops = {
.init_dcbnl_ops = __qlcnic_init_dcbnl_ops,
.free = __qlcnic_dcb_free,
.attach = __qlcnic_dcb_attach,
@@ -180,7 +180,7 @@ static struct qlcnic_dcb_ops qlcnic_83xx_dcb_ops = {
.aen_handler = qlcnic_83xx_dcb_aen_handler,
};
-static struct qlcnic_dcb_ops qlcnic_82xx_dcb_ops = {
+static const struct qlcnic_dcb_ops qlcnic_82xx_dcb_ops = {
.init_dcbnl_ops = __qlcnic_init_dcbnl_ops,
.free = __qlcnic_dcb_free,
.attach = __qlcnic_dcb_attach,
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
index 3cf4a10fbe1e..9777e5713525 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
@@ -37,7 +37,7 @@ struct qlcnic_dcb {
struct qlcnic_adapter *adapter;
struct delayed_work aen_work;
struct workqueue_struct *wq;
- struct qlcnic_dcb_ops *ops;
+ const struct qlcnic_dcb_ops *ops;
struct qlcnic_dcb_cfg *cfg;
unsigned long state;
};
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
index d4b5085a21fa..7bd6f25b4625 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
@@ -1604,7 +1604,7 @@ int qlcnic_82xx_napi_add(struct qlcnic_adapter *adapter,
if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test) {
for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
- netif_napi_add(netdev, &tx_ring->napi, qlcnic_tx_poll,
+ netif_tx_napi_add(netdev, &tx_ring->napi, qlcnic_tx_poll,
NAPI_POLL_WEIGHT);
}
}
@@ -2135,7 +2135,7 @@ int qlcnic_83xx_napi_add(struct qlcnic_adapter *adapter,
!(adapter->flags & QLCNIC_TX_INTR_SHARED)) {
for (ring = 0; ring < adapter->drv_tx_rings; ring++) {
tx_ring = &adapter->tx_ring[ring];
- netif_napi_add(netdev, &tx_ring->napi,
+ netif_tx_napi_add(netdev, &tx_ring->napi,
qlcnic_83xx_msix_tx_poll,
NAPI_POLL_WEIGHT);
}
diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c
index 02b7115b6aaa..997976426799 100644
--- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c
+++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c
@@ -4211,8 +4211,9 @@ static int ql_change_rx_buffers(struct ql_adapter *qdev)
/* Wait for an outstanding reset to complete. */
if (!test_bit(QL_ADAPTER_UP, &qdev->flags)) {
- int i = 3;
- while (i-- && !test_bit(QL_ADAPTER_UP, &qdev->flags)) {
+ int i = 4;
+
+ while (--i && !test_bit(QL_ADAPTER_UP, &qdev->flags)) {
netif_err(qdev, ifup, qdev->ndev,
"Waiting for adapter UP...\n");
ssleep(1);
diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c
index ddb2c6c6ec94..689a4a5c8dcf 100644
--- a/drivers/net/ethernet/qualcomm/qca_spi.c
+++ b/drivers/net/ethernet/qualcomm/qca_spi.c
@@ -736,9 +736,8 @@ qcaspi_netdev_tx_timeout(struct net_device *dev)
netdev_info(qca->net_dev, "Transmit timeout at %ld, latency %ld\n",
jiffies, jiffies - dev->trans_start);
qca->net_dev->stats.tx_errors++;
- /* wake the queue if there is room */
- if (qcaspi_tx_ring_has_space(&qca->txr))
- netif_wake_queue(dev);
+ /* Trigger tx queue flush and QCA7000 reset */
+ qca->sync = QCASPI_SYNC_UNKNOWN;
}
static int
diff --git a/drivers/net/ethernet/rdc/r6040.c b/drivers/net/ethernet/rdc/r6040.c
index 9a37247cf4b8..6b541e57c96a 100644
--- a/drivers/net/ethernet/rdc/r6040.c
+++ b/drivers/net/ethernet/rdc/r6040.c
@@ -1039,7 +1039,7 @@ static int r6040_mii_probe(struct net_device *dev)
return -ENODEV;
}
- phydev = phy_connect(dev, dev_name(&phydev->dev), &r6040_adjust_link,
+ phydev = phy_connect(dev, phydev_name(phydev), &r6040_adjust_link,
PHY_INTERFACE_MODE_MII);
if (IS_ERR(phydev)) {
@@ -1061,9 +1061,7 @@ static int r6040_mii_probe(struct net_device *dev)
lp->old_link = 0;
lp->old_duplex = -1;
- dev_info(&lp->pdev->dev, "attached PHY driver [%s] "
- "(mii_bus:phy_addr=%s)\n",
- phydev->drv->name, dev_name(&phydev->dev));
+ phy_attached_info(phydev);
return 0;
}
@@ -1077,7 +1075,6 @@ static int r6040_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
static int card_idx = -1;
int bar = 0;
u16 *adrp;
- int i;
pr_info("%s\n", version);
@@ -1189,19 +1186,11 @@ static int r6040_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
lp->mii_bus->name = "r6040_eth_mii";
snprintf(lp->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
dev_name(&pdev->dev), card_idx);
- lp->mii_bus->irq = kmalloc_array(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
- if (!lp->mii_bus->irq) {
- err = -ENOMEM;
- goto err_out_mdio;
- }
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- lp->mii_bus->irq[i] = PHY_POLL;
err = mdiobus_register(lp->mii_bus);
if (err) {
dev_err(&pdev->dev, "failed to register MII bus\n");
- goto err_out_mdio_irq;
+ goto err_out_mdio;
}
err = r6040_mii_probe(dev);
@@ -1220,8 +1209,6 @@ static int r6040_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
err_out_mdio_unregister:
mdiobus_unregister(lp->mii_bus);
-err_out_mdio_irq:
- kfree(lp->mii_bus->irq);
err_out_mdio:
mdiobus_free(lp->mii_bus);
err_out_unmap:
@@ -1244,7 +1231,6 @@ static void r6040_remove_one(struct pci_dev *pdev)
unregister_netdev(dev);
mdiobus_unregister(lp->mii_bus);
- kfree(lp->mii_bus->irq);
mdiobus_free(lp->mii_bus);
netif_napi_del(&lp->napi);
pci_iounmap(pdev, lp->base);
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index 79ef799f88ab..17d5571d0432 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -3894,7 +3894,7 @@ static void rtl8168h_1_hw_phy_config(struct rtl8169_private *tp)
/* disable phy pfm mode */
rtl_writephy(tp, 0x1f, 0x0a44);
- rtl_w0w1_phy(tp, 0x14, 0x0000, 0x0080);
+ rtl_w0w1_phy(tp, 0x11, 0x0000, 0x0080);
rtl_writephy(tp, 0x1f, 0x0000);
/* Check ALDPS bit, disable it if enabled */
@@ -3947,7 +3947,7 @@ static void rtl8168h_2_hw_phy_config(struct rtl8169_private *tp)
data = (ioffset_p3<<12)|(ioffset_p2<<8)|(ioffset_p1<<4)|(ioffset_p0);
if ((ioffset_p3 != 0x0f) || (ioffset_p2 != 0x0f) ||
- (ioffset_p1 != 0x0f) || (ioffset_p0 == 0x0f)) {
+ (ioffset_p1 != 0x0f) || (ioffset_p0 != 0x0f)) {
rtl_writephy(tp, 0x1f, 0x0bcf);
rtl_writephy(tp, 0x16, data);
rtl_writephy(tp, 0x1f, 0x0000);
@@ -3967,7 +3967,7 @@ static void rtl8168h_2_hw_phy_config(struct rtl8169_private *tp)
/* disable phy pfm mode */
rtl_writephy(tp, 0x1f, 0x0a44);
- rtl_w0w1_phy(tp, 0x14, 0x0000, 0x0080);
+ rtl_w0w1_phy(tp, 0x11, 0x0000, 0x0080);
rtl_writephy(tp, 0x1f, 0x0000);
/* Check ALDPS bit, disable it if enabled */
@@ -5818,11 +5818,10 @@ static void rtl_hw_start_8168d_4(struct rtl8169_private *tp)
void __iomem *ioaddr = tp->mmio_addr;
struct pci_dev *pdev = tp->pci_dev;
static const struct ephy_info e_info_8168d_4[] = {
- { 0x0b, ~0, 0x48 },
- { 0x19, 0x20, 0x50 },
- { 0x0c, ~0, 0x20 }
+ { 0x0b, 0x0000, 0x0048 },
+ { 0x19, 0x0020, 0x0050 },
+ { 0x0c, 0x0100, 0x0020 }
};
- int i;
rtl_csi_access_enable_1(tp);
@@ -5830,13 +5829,7 @@ static void rtl_hw_start_8168d_4(struct rtl8169_private *tp)
RTL_W8(MaxTxPacketSize, TxPacketMax);
- for (i = 0; i < ARRAY_SIZE(e_info_8168d_4); i++) {
- const struct ephy_info *e = e_info_8168d_4 + i;
- u16 w;
-
- w = rtl_ephy_read(tp, e->offset);
- rtl_ephy_write(tp, 0x03, (w & e->mask) | e->bits);
- }
+ rtl_ephy_init(tp, e_info_8168d_4, ARRAY_SIZE(e_info_8168d_4));
rtl_enable_clock_request(pdev);
}
@@ -6127,7 +6120,7 @@ static void rtl_hw_start_8168h_1(struct rtl8169_private *tp)
RTL_W8(EEE_LED, RTL_R8(EEE_LED) & ~0x07);
RTL_W8(DLLPR, RTL_R8(DLLPR) & ~PFM_EN);
- RTL_W8(DLLPR, RTL_R8(MISC_1) & ~PFM_D3COLD_EN);
+ RTL_W8(MISC_1, RTL_R8(MISC_1) & ~PFM_D3COLD_EN);
RTL_W8(DLLPR, RTL_R8(DLLPR) & ~TX_10M_PS_EN);
@@ -6136,7 +6129,7 @@ static void rtl_hw_start_8168h_1(struct rtl8169_private *tp)
rtl_pcie_state_l2l3_enable(tp, false);
rtl_writephy(tp, 0x1f, 0x0c42);
- rg_saw_cnt = rtl_readphy(tp, 0x13);
+ rg_saw_cnt = (rtl_readphy(tp, 0x13) & 0x3fff);
rtl_writephy(tp, 0x1f, 0x0000);
if (rg_saw_cnt > 0) {
u16 sw_cnt_1ms_ini;
@@ -6252,7 +6245,7 @@ static void rtl_hw_start_8168ep_2(struct rtl8169_private *tp)
rtl_hw_start_8168ep(tp);
RTL_W8(DLLPR, RTL_R8(DLLPR) & ~PFM_EN);
- RTL_W8(DLLPR, RTL_R8(MISC_1) & ~PFM_D3COLD_EN);
+ RTL_W8(MISC_1, RTL_R8(MISC_1) & ~PFM_D3COLD_EN);
}
static void rtl_hw_start_8168ep_3(struct rtl8169_private *tp)
@@ -6274,7 +6267,7 @@ static void rtl_hw_start_8168ep_3(struct rtl8169_private *tp)
rtl_hw_start_8168ep(tp);
RTL_W8(DLLPR, RTL_R8(DLLPR) & ~PFM_EN);
- RTL_W8(DLLPR, RTL_R8(MISC_1) & ~PFM_D3COLD_EN);
+ RTL_W8(MISC_1, RTL_R8(MISC_1) & ~PFM_D3COLD_EN);
data = r8168_mac_ocp_read(tp, 0xd3e2);
data &= 0xf000;
diff --git a/drivers/net/ethernet/renesas/ravb.h b/drivers/net/ethernet/renesas/ravb.h
index 0623fff932e4..9fbe92ac225b 100644
--- a/drivers/net/ethernet/renesas/ravb.h
+++ b/drivers/net/ethernet/renesas/ravb.h
@@ -206,6 +206,7 @@ enum CCC_BIT {
CCC_OPC_RESET = 0x00000000,
CCC_OPC_CONFIG = 0x00000001,
CCC_OPC_OPERATION = 0x00000002,
+ CCC_GAC = 0x00000080,
CCC_DTSR = 0x00000100,
CCC_CSEL = 0x00030000,
CCC_CSEL_HPB = 0x00010000,
@@ -576,6 +577,9 @@ enum GTI_BIT {
GTI_TIV = 0x0FFFFFFF,
};
+#define GTI_TIV_MAX GTI_TIV
+#define GTI_TIV_MIN 0x20
+
/* GIC */
enum GIC_BIT {
GIC_PTCE = 0x00000001, /* Undocumented? */
diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c
index ee8d1ec61fab..ac43ed914fcf 100644
--- a/drivers/net/ethernet/renesas/ravb_main.c
+++ b/drivers/net/ethernet/renesas/ravb_main.c
@@ -32,6 +32,8 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <asm/div64.h>
+
#include "ravb.h"
#define RAVB_DEF_MSG_ENABLE \
@@ -113,12 +115,15 @@ static void ravb_read_mac_address(struct net_device *ndev, const u8 *mac)
if (mac) {
ether_addr_copy(ndev->dev_addr, mac);
} else {
- ndev->dev_addr[0] = (ravb_read(ndev, MAHR) >> 24);
- ndev->dev_addr[1] = (ravb_read(ndev, MAHR) >> 16) & 0xFF;
- ndev->dev_addr[2] = (ravb_read(ndev, MAHR) >> 8) & 0xFF;
- ndev->dev_addr[3] = (ravb_read(ndev, MAHR) >> 0) & 0xFF;
- ndev->dev_addr[4] = (ravb_read(ndev, MALR) >> 8) & 0xFF;
- ndev->dev_addr[5] = (ravb_read(ndev, MALR) >> 0) & 0xFF;
+ u32 mahr = ravb_read(ndev, MAHR);
+ u32 malr = ravb_read(ndev, MALR);
+
+ ndev->dev_addr[0] = (mahr >> 24) & 0xFF;
+ ndev->dev_addr[1] = (mahr >> 16) & 0xFF;
+ ndev->dev_addr[2] = (mahr >> 8) & 0xFF;
+ ndev->dev_addr[3] = (mahr >> 0) & 0xFF;
+ ndev->dev_addr[4] = (malr >> 8) & 0xFF;
+ ndev->dev_addr[5] = (malr >> 0) & 0xFF;
}
}
@@ -338,16 +343,13 @@ error:
static void ravb_emac_init(struct net_device *ndev)
{
struct ravb_private *priv = netdev_priv(ndev);
- u32 ecmr;
/* Receive frame limit set register */
ravb_write(ndev, ndev->mtu + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN, RFLR);
/* PAUSE prohibition */
- ecmr = ravb_read(ndev, ECMR);
- ecmr &= ECMR_DM;
- ecmr |= ECMR_ZPF | (priv->duplex ? ECMR_DM : 0) | ECMR_TE | ECMR_RE;
- ravb_write(ndev, ecmr, ECMR);
+ ravb_write(ndev, ECMR_ZPF | (priv->duplex ? ECMR_DM : 0) |
+ ECMR_TE | ECMR_RE, ECMR);
ravb_set_rate(ndev);
@@ -405,9 +407,11 @@ static int ravb_dmac_init(struct net_device *ndev)
/* Timestamp enable */
ravb_write(ndev, TCCR_TFEN, TCCR);
- /* Interrupt enable: */
+ /* Interrupt init: */
/* Frame receive */
ravb_write(ndev, RIC0_FRE0 | RIC0_FRE1, RIC0);
+ /* Disable FIFO full warning */
+ ravb_write(ndev, 0, RIC1);
/* Receive FIFO full error, descriptor empty */
ravb_write(ndev, RIC2_QFE0 | RIC2_QFE1 | RIC2_RFFE, RIC2);
/* Frame transmitted, timestamp FIFO updated */
@@ -875,6 +879,7 @@ static int ravb_phy_init(struct net_device *ndev)
struct ravb_private *priv = netdev_priv(ndev);
struct phy_device *phydev;
struct device_node *pn;
+ int err;
priv->link = 0;
priv->speed = 0;
@@ -882,6 +887,17 @@ static int ravb_phy_init(struct net_device *ndev)
/* Try connecting to PHY */
pn = of_parse_phandle(np, "phy-handle", 0);
+ if (!pn) {
+ /* In the case of a fixed PHY, the DT node associated
+ * to the PHY is the Ethernet MAC DT node.
+ */
+ if (of_phy_is_fixed_link(np)) {
+ err = of_phy_register_fixed_link(np);
+ if (err)
+ return err;
+ }
+ pn = of_node_get(np);
+ }
phydev = of_phy_connect(ndev, pn, ravb_adjust_link, 0,
priv->phy_interface);
if (!phydev) {
@@ -905,8 +921,10 @@ static int ravb_phy_init(struct net_device *ndev)
netdev_info(ndev, "limited PHY to 100Mbit/s\n");
}
- netdev_info(ndev, "attached PHY %d (IRQ %d) to driver %s\n",
- phydev->addr, phydev->irq, phydev->drv->name);
+ /* 10BASE is not supported */
+ phydev->supported &= ~PHY_10BT_FEATURES;
+
+ phy_attached_info(phydev);
priv->phydev = phydev;
@@ -1037,7 +1055,7 @@ static const char ravb_gstrings_stats[][ETH_GSTRING_LEN] = {
"rx_queue_1_mcast_packets",
"rx_queue_1_errors",
"rx_queue_1_crc_errors",
- "rx_queue_1_frame_errors_",
+ "rx_queue_1_frame_errors",
"rx_queue_1_length_errors",
"rx_queue_1_missed_errors",
"rx_queue_1_over_errors",
@@ -1225,11 +1243,12 @@ static int ravb_open(struct net_device *ndev)
/* Device init */
error = ravb_dmac_init(ndev);
if (error)
- goto out_free_irq;
+ goto out_free_irq2;
ravb_emac_init(ndev);
/* Initialise PTP Clock driver */
- ravb_ptp_init(ndev, priv->pdev);
+ if (priv->chip_id == RCAR_GEN2)
+ ravb_ptp_init(ndev, priv->pdev);
netif_tx_start_all_queues(ndev);
@@ -1242,10 +1261,13 @@ static int ravb_open(struct net_device *ndev)
out_ptp_stop:
/* Stop PTP Clock driver */
- ravb_ptp_stop(ndev);
+ if (priv->chip_id == RCAR_GEN2)
+ ravb_ptp_stop(ndev);
+out_free_irq2:
+ if (priv->chip_id == RCAR_GEN3)
+ free_irq(priv->emac_irq, ndev);
out_free_irq:
free_irq(ndev->irq, ndev);
- free_irq(priv->emac_irq, ndev);
out_napi_off:
napi_disable(&priv->napi[RAVB_NC]);
napi_disable(&priv->napi[RAVB_BE]);
@@ -1469,12 +1491,12 @@ static int ravb_close(struct net_device *ndev)
/* Disable interrupts by clearing the interrupt masks. */
ravb_write(ndev, 0, RIC0);
- ravb_write(ndev, 0, RIC1);
ravb_write(ndev, 0, RIC2);
ravb_write(ndev, 0, TIC);
/* Stop PTP Clock driver */
- ravb_ptp_stop(ndev);
+ if (priv->chip_id == RCAR_GEN2)
+ ravb_ptp_stop(ndev);
/* Set the config mode to stop the AVB-DMAC's processes */
if (ravb_stop_dma(ndev) < 0)
@@ -1654,11 +1676,45 @@ static int ravb_mdio_release(struct ravb_private *priv)
static const struct of_device_id ravb_match_table[] = {
{ .compatible = "renesas,etheravb-r8a7790", .data = (void *)RCAR_GEN2 },
{ .compatible = "renesas,etheravb-r8a7794", .data = (void *)RCAR_GEN2 },
+ { .compatible = "renesas,etheravb-rcar-gen2", .data = (void *)RCAR_GEN2 },
{ .compatible = "renesas,etheravb-r8a7795", .data = (void *)RCAR_GEN3 },
+ { .compatible = "renesas,etheravb-rcar-gen3", .data = (void *)RCAR_GEN3 },
{ }
};
MODULE_DEVICE_TABLE(of, ravb_match_table);
+static int ravb_set_gti(struct net_device *ndev)
+{
+
+ struct device *dev = ndev->dev.parent;
+ struct device_node *np = dev->of_node;
+ unsigned long rate;
+ struct clk *clk;
+ uint64_t inc;
+
+ clk = of_clk_get(np, 0);
+ if (IS_ERR(clk)) {
+ dev_err(dev, "could not get clock\n");
+ return PTR_ERR(clk);
+ }
+
+ rate = clk_get_rate(clk);
+ clk_put(clk);
+
+ inc = 1000000000ULL << 20;
+ do_div(inc, rate);
+
+ if (inc < GTI_TIV_MIN || inc > GTI_TIV_MAX) {
+ dev_err(dev, "gti.tiv increment 0x%llx is outside the range 0x%x - 0x%x\n",
+ inc, GTI_TIV_MIN, GTI_TIV_MAX);
+ return -EINVAL;
+ }
+
+ ravb_write(ndev, inc, GTI);
+
+ return 0;
+}
+
static int ravb_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -1747,15 +1803,25 @@ static int ravb_probe(struct platform_device *pdev)
ndev->ethtool_ops = &ravb_ethtool_ops;
/* Set AVB config mode */
- ravb_write(ndev, (ravb_read(ndev, CCC) & ~CCC_OPC) | CCC_OPC_CONFIG,
- CCC);
+ if (chip_id == RCAR_GEN2) {
+ ravb_write(ndev, (ravb_read(ndev, CCC) & ~CCC_OPC) |
+ CCC_OPC_CONFIG, CCC);
+ /* Set CSEL value */
+ ravb_write(ndev, (ravb_read(ndev, CCC) & ~CCC_CSEL) |
+ CCC_CSEL_HPB, CCC);
+ } else {
+ ravb_write(ndev, (ravb_read(ndev, CCC) & ~CCC_OPC) |
+ CCC_OPC_CONFIG | CCC_GAC | CCC_CSEL_HPB, CCC);
+ }
/* Set CSEL value */
ravb_write(ndev, (ravb_read(ndev, CCC) & ~CCC_CSEL) | CCC_CSEL_HPB,
CCC);
/* Set GTI value */
- ravb_write(ndev, ((1000 << 20) / 130) & GTI_TIV, GTI);
+ error = ravb_set_gti(ndev);
+ if (error)
+ goto out_release;
/* Request GTI loading */
ravb_write(ndev, ravb_read(ndev, GCCR) | GCCR_LTI, GCCR);
@@ -1778,6 +1844,10 @@ static int ravb_probe(struct platform_device *pdev)
/* Initialise HW timestamp list */
INIT_LIST_HEAD(&priv->ts_skb_list);
+ /* Initialise PTP Clock driver */
+ if (chip_id != RCAR_GEN2)
+ ravb_ptp_init(ndev, pdev);
+
/* Debug message level */
priv->msg_enable = RAVB_DEF_MSG_ENABLE;
@@ -1819,6 +1889,10 @@ out_napi_del:
out_dma_free:
dma_free_coherent(ndev->dev.parent, priv->desc_bat_size, priv->desc_bat,
priv->desc_bat_dma);
+
+ /* Stop PTP Clock driver */
+ if (chip_id != RCAR_GEN2)
+ ravb_ptp_stop(ndev);
out_release:
if (ndev)
free_netdev(ndev);
@@ -1833,6 +1907,10 @@ static int ravb_remove(struct platform_device *pdev)
struct net_device *ndev = platform_get_drvdata(pdev);
struct ravb_private *priv = netdev_priv(ndev);
+ /* Stop PTP Clock driver */
+ if (priv->chip_id != RCAR_GEN2)
+ ravb_ptp_stop(ndev);
+
dma_free_coherent(ndev->dev.parent, priv->desc_bat_size, priv->desc_bat,
priv->desc_bat_dma);
/* Set reset mode */
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index e7bab7909ed9..dfa9e59c9442 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -52,6 +52,8 @@
NETIF_MSG_RX_ERR| \
NETIF_MSG_TX_ERR)
+#define SH_ETH_OFFSET_INVALID ((u16)~0)
+
#define SH_ETH_OFFSET_DEFAULTS \
[0 ... SH_ETH_MAX_REGISTER_OFFSET - 1] = SH_ETH_OFFSET_INVALID
@@ -404,6 +406,28 @@ static const u16 sh_eth_offset_fast_sh3_sh2[SH_ETH_MAX_REGISTER_OFFSET] = {
static void sh_eth_rcv_snd_disable(struct net_device *ndev);
static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev);
+static void sh_eth_write(struct net_device *ndev, u32 data, int enum_index)
+{
+ struct sh_eth_private *mdp = netdev_priv(ndev);
+ u16 offset = mdp->reg_offset[enum_index];
+
+ if (WARN_ON(offset == SH_ETH_OFFSET_INVALID))
+ return;
+
+ iowrite32(data, mdp->addr + offset);
+}
+
+static u32 sh_eth_read(struct net_device *ndev, int enum_index)
+{
+ struct sh_eth_private *mdp = netdev_priv(ndev);
+ u16 offset = mdp->reg_offset[enum_index];
+
+ if (WARN_ON(offset == SH_ETH_OFFSET_INVALID))
+ return ~0U;
+
+ return ioread32(mdp->addr + offset);
+}
+
static bool sh_eth_is_gether(struct sh_eth_private *mdp)
{
return mdp->reg_offset == sh_eth_offset_gigabit;
@@ -449,6 +473,109 @@ static void sh_eth_set_duplex(struct net_device *ndev)
sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR);
}
+static void sh_eth_chip_reset(struct net_device *ndev)
+{
+ struct sh_eth_private *mdp = netdev_priv(ndev);
+
+ /* reset device */
+ sh_eth_tsu_write(mdp, ARSTR_ARSTR, ARSTR);
+ mdelay(1);
+}
+
+static void sh_eth_set_rate_gether(struct net_device *ndev)
+{
+ struct sh_eth_private *mdp = netdev_priv(ndev);
+
+ switch (mdp->speed) {
+ case 10: /* 10BASE */
+ sh_eth_write(ndev, GECMR_10, GECMR);
+ break;
+ case 100:/* 100BASE */
+ sh_eth_write(ndev, GECMR_100, GECMR);
+ break;
+ case 1000: /* 1000BASE */
+ sh_eth_write(ndev, GECMR_1000, GECMR);
+ break;
+ default:
+ break;
+ }
+}
+
+#ifdef CONFIG_OF
+/* R7S72100 */
+static struct sh_eth_cpu_data r7s72100_data = {
+ .chip_reset = sh_eth_chip_reset,
+ .set_duplex = sh_eth_set_duplex,
+
+ .register_type = SH_ETH_REG_FAST_RZ,
+
+ .ecsr_value = ECSR_ICD,
+ .ecsipr_value = ECSIPR_ICDIP,
+ .eesipr_value = 0xff7f009f,
+
+ .tx_check = EESR_TC1 | EESR_FTC,
+ .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT |
+ EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE |
+ EESR_TDE | EESR_ECI,
+ .fdr_value = 0x0000070f,
+
+ .no_psr = 1,
+ .apr = 1,
+ .mpr = 1,
+ .tpauser = 1,
+ .hw_swap = 1,
+ .rpadir = 1,
+ .rpadir_value = 2 << 16,
+ .no_trimd = 1,
+ .no_ade = 1,
+ .hw_crc = 1,
+ .tsu = 1,
+ .shift_rd0 = 1,
+};
+
+static void sh_eth_chip_reset_r8a7740(struct net_device *ndev)
+{
+ struct sh_eth_private *mdp = netdev_priv(ndev);
+
+ /* reset device */
+ sh_eth_tsu_write(mdp, ARSTR_ARSTR, ARSTR);
+ mdelay(1);
+
+ sh_eth_select_mii(ndev);
+}
+
+/* R8A7740 */
+static struct sh_eth_cpu_data r8a7740_data = {
+ .chip_reset = sh_eth_chip_reset_r8a7740,
+ .set_duplex = sh_eth_set_duplex,
+ .set_rate = sh_eth_set_rate_gether,
+
+ .register_type = SH_ETH_REG_GIGABIT,
+
+ .ecsr_value = ECSR_ICD | ECSR_MPD,
+ .ecsipr_value = ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP,
+ .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff,
+
+ .tx_check = EESR_TC1 | EESR_FTC,
+ .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT |
+ EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE |
+ EESR_TDE | EESR_ECI,
+ .fdr_value = 0x0000070f,
+
+ .apr = 1,
+ .mpr = 1,
+ .tpauser = 1,
+ .bculr = 1,
+ .hw_swap = 1,
+ .rpadir = 1,
+ .rpadir_value = 2 << 16,
+ .no_trimd = 1,
+ .no_ade = 1,
+ .tsu = 1,
+ .select_mii = 1,
+ .shift_rd0 = 1,
+};
+
/* There is CPU dependent code */
static void sh_eth_set_rate_r8a777x(struct net_device *ndev)
{
@@ -514,6 +641,7 @@ static struct sh_eth_cpu_data r8a779x_data = {
.hw_swap = 1,
.rmiimode = 1,
};
+#endif /* CONFIG_OF */
static void sh_eth_set_rate_sh7724(struct net_device *ndev)
{
@@ -671,34 +799,6 @@ static struct sh_eth_cpu_data sh7757_data_giga = {
.tsu = 1,
};
-static void sh_eth_chip_reset(struct net_device *ndev)
-{
- struct sh_eth_private *mdp = netdev_priv(ndev);
-
- /* reset device */
- sh_eth_tsu_write(mdp, ARSTR_ARSTR, ARSTR);
- mdelay(1);
-}
-
-static void sh_eth_set_rate_gether(struct net_device *ndev)
-{
- struct sh_eth_private *mdp = netdev_priv(ndev);
-
- switch (mdp->speed) {
- case 10: /* 10BASE */
- sh_eth_write(ndev, GECMR_10, GECMR);
- break;
- case 100:/* 100BASE */
- sh_eth_write(ndev, GECMR_100, GECMR);
- break;
- case 1000: /* 1000BASE */
- sh_eth_write(ndev, GECMR_1000, GECMR);
- break;
- default:
- break;
- }
-}
-
/* SH7734 */
static struct sh_eth_cpu_data sh7734_data = {
.chip_reset = sh_eth_chip_reset,
@@ -756,80 +856,6 @@ static struct sh_eth_cpu_data sh7763_data = {
.irq_flags = IRQF_SHARED,
};
-static void sh_eth_chip_reset_r8a7740(struct net_device *ndev)
-{
- struct sh_eth_private *mdp = netdev_priv(ndev);
-
- /* reset device */
- sh_eth_tsu_write(mdp, ARSTR_ARSTR, ARSTR);
- mdelay(1);
-
- sh_eth_select_mii(ndev);
-}
-
-/* R8A7740 */
-static struct sh_eth_cpu_data r8a7740_data = {
- .chip_reset = sh_eth_chip_reset_r8a7740,
- .set_duplex = sh_eth_set_duplex,
- .set_rate = sh_eth_set_rate_gether,
-
- .register_type = SH_ETH_REG_GIGABIT,
-
- .ecsr_value = ECSR_ICD | ECSR_MPD,
- .ecsipr_value = ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP,
- .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff,
-
- .tx_check = EESR_TC1 | EESR_FTC,
- .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT |
- EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE |
- EESR_TDE | EESR_ECI,
- .fdr_value = 0x0000070f,
-
- .apr = 1,
- .mpr = 1,
- .tpauser = 1,
- .bculr = 1,
- .hw_swap = 1,
- .rpadir = 1,
- .rpadir_value = 2 << 16,
- .no_trimd = 1,
- .no_ade = 1,
- .tsu = 1,
- .select_mii = 1,
- .shift_rd0 = 1,
-};
-
-/* R7S72100 */
-static struct sh_eth_cpu_data r7s72100_data = {
- .chip_reset = sh_eth_chip_reset,
- .set_duplex = sh_eth_set_duplex,
-
- .register_type = SH_ETH_REG_FAST_RZ,
-
- .ecsr_value = ECSR_ICD,
- .ecsipr_value = ECSIPR_ICDIP,
- .eesipr_value = 0xff7f009f,
-
- .tx_check = EESR_TC1 | EESR_FTC,
- .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT |
- EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE |
- EESR_TDE | EESR_ECI,
- .fdr_value = 0x0000070f,
-
- .no_psr = 1,
- .apr = 1,
- .mpr = 1,
- .tpauser = 1,
- .hw_swap = 1,
- .rpadir = 1,
- .rpadir_value = 2 << 16,
- .no_trimd = 1,
- .no_ade = 1,
- .hw_crc = 1,
- .tsu = 1,
- .shift_rd0 = 1,
-};
-
static struct sh_eth_cpu_data sh7619_data = {
.register_type = SH_ETH_REG_FAST_SH3_SH2,
@@ -941,30 +967,6 @@ static void sh_eth_set_receive_align(struct sk_buff *skb)
skb_reserve(skb, SH_ETH_RX_ALIGN - reserve);
}
-
-/* CPU <-> EDMAC endian convert */
-static inline __u32 cpu_to_edmac(struct sh_eth_private *mdp, u32 x)
-{
- switch (mdp->edmac_endian) {
- case EDMAC_LITTLE_ENDIAN:
- return cpu_to_le32(x);
- case EDMAC_BIG_ENDIAN:
- return cpu_to_be32(x);
- }
- return x;
-}
-
-static inline __u32 edmac_to_cpu(struct sh_eth_private *mdp, u32 x)
-{
- switch (mdp->edmac_endian) {
- case EDMAC_LITTLE_ENDIAN:
- return le32_to_cpu(x);
- case EDMAC_BIG_ENDIAN:
- return be32_to_cpu(x);
- }
- return x;
-}
-
/* Program the hardware MAC address from dev->dev_addr. */
static void update_mac_address(struct net_device *ndev)
{
@@ -987,12 +989,15 @@ static void read_mac_address(struct net_device *ndev, unsigned char *mac)
if (mac[0] || mac[1] || mac[2] || mac[3] || mac[4] || mac[5]) {
memcpy(ndev->dev_addr, mac, ETH_ALEN);
} else {
- ndev->dev_addr[0] = (sh_eth_read(ndev, MAHR) >> 24);
- ndev->dev_addr[1] = (sh_eth_read(ndev, MAHR) >> 16) & 0xFF;
- ndev->dev_addr[2] = (sh_eth_read(ndev, MAHR) >> 8) & 0xFF;
- ndev->dev_addr[3] = (sh_eth_read(ndev, MAHR) & 0xFF);
- ndev->dev_addr[4] = (sh_eth_read(ndev, MALR) >> 8) & 0xFF;
- ndev->dev_addr[5] = (sh_eth_read(ndev, MALR) & 0xFF);
+ u32 mahr = sh_eth_read(ndev, MAHR);
+ u32 malr = sh_eth_read(ndev, MALR);
+
+ ndev->dev_addr[0] = (mahr >> 24) & 0xFF;
+ ndev->dev_addr[1] = (mahr >> 16) & 0xFF;
+ ndev->dev_addr[2] = (mahr >> 8) & 0xFF;
+ ndev->dev_addr[3] = (mahr >> 0) & 0xFF;
+ ndev->dev_addr[4] = (malr >> 8) & 0xFF;
+ ndev->dev_addr[5] = (malr >> 0) & 0xFF;
}
}
@@ -1008,56 +1013,34 @@ struct bb_info {
void (*set_gate)(void *addr);
struct mdiobb_ctrl ctrl;
void *addr;
- u32 mmd_msk;/* MMD */
- u32 mdo_msk;
- u32 mdi_msk;
- u32 mdc_msk;
};
-/* PHY bit set */
-static void bb_set(void *addr, u32 msk)
+static void sh_mdio_ctrl(struct mdiobb_ctrl *ctrl, u32 mask, int set)
{
- iowrite32(ioread32(addr) | msk, addr);
-}
+ struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
+ u32 pir;
-/* PHY bit clear */
-static void bb_clr(void *addr, u32 msk)
-{
- iowrite32((ioread32(addr) & ~msk), addr);
-}
+ if (bitbang->set_gate)
+ bitbang->set_gate(bitbang->addr);
-/* PHY bit read */
-static int bb_read(void *addr, u32 msk)
-{
- return (ioread32(addr) & msk) != 0;
+ pir = ioread32(bitbang->addr);
+ if (set)
+ pir |= mask;
+ else
+ pir &= ~mask;
+ iowrite32(pir, bitbang->addr);
}
/* Data I/O pin control */
static void sh_mmd_ctrl(struct mdiobb_ctrl *ctrl, int bit)
{
- struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
-
- if (bitbang->set_gate)
- bitbang->set_gate(bitbang->addr);
-
- if (bit)
- bb_set(bitbang->addr, bitbang->mmd_msk);
- else
- bb_clr(bitbang->addr, bitbang->mmd_msk);
+ sh_mdio_ctrl(ctrl, PIR_MMD, bit);
}
/* Set bit data*/
static void sh_set_mdio(struct mdiobb_ctrl *ctrl, int bit)
{
- struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
-
- if (bitbang->set_gate)
- bitbang->set_gate(bitbang->addr);
-
- if (bit)
- bb_set(bitbang->addr, bitbang->mdo_msk);
- else
- bb_clr(bitbang->addr, bitbang->mdo_msk);
+ sh_mdio_ctrl(ctrl, PIR_MDO, bit);
}
/* Get bit data*/
@@ -1068,21 +1051,13 @@ static int sh_get_mdio(struct mdiobb_ctrl *ctrl)
if (bitbang->set_gate)
bitbang->set_gate(bitbang->addr);
- return bb_read(bitbang->addr, bitbang->mdi_msk);
+ return (ioread32(bitbang->addr) & PIR_MDI) != 0;
}
/* MDC pin control */
static void sh_mdc_ctrl(struct mdiobb_ctrl *ctrl, int bit)
{
- struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
-
- if (bitbang->set_gate)
- bitbang->set_gate(bitbang->addr);
-
- if (bit)
- bb_set(bitbang->addr, bitbang->mdc_msk);
- else
- bb_clr(bitbang->addr, bitbang->mdc_msk);
+ sh_mdio_ctrl(ctrl, PIR_MDC, bit);
}
/* mdio bus control struct */
@@ -1143,6 +1118,7 @@ static void sh_eth_ring_format(struct net_device *ndev)
int tx_ringsize = sizeof(*txdesc) * mdp->num_tx_ring;
int skbuff_size = mdp->rx_buf_sz + SH_ETH_RX_ALIGN + 32 - 1;
dma_addr_t dma_addr;
+ u32 buf_len;
mdp->cur_rx = 0;
mdp->cur_tx = 0;
@@ -1163,17 +1139,17 @@ static void sh_eth_ring_format(struct net_device *ndev)
/* RX descriptor */
rxdesc = &mdp->rx_ring[i];
/* The size of the buffer is a multiple of 32 bytes. */
- rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 32);
- dma_addr = dma_map_single(&ndev->dev, skb->data,
- rxdesc->buffer_length,
+ buf_len = ALIGN(mdp->rx_buf_sz, 32);
+ rxdesc->len = cpu_to_le32(buf_len << 16);
+ dma_addr = dma_map_single(&ndev->dev, skb->data, buf_len,
DMA_FROM_DEVICE);
if (dma_mapping_error(&ndev->dev, dma_addr)) {
kfree_skb(skb);
break;
}
mdp->rx_skbuff[i] = skb;
- rxdesc->addr = dma_addr;
- rxdesc->status = cpu_to_edmac(mdp, RD_RACT | RD_RFP);
+ rxdesc->addr = cpu_to_le32(dma_addr);
+ rxdesc->status = cpu_to_le32(RD_RACT | RD_RFP);
/* Rx descriptor address set */
if (i == 0) {
@@ -1187,7 +1163,7 @@ static void sh_eth_ring_format(struct net_device *ndev)
mdp->dirty_rx = (u32) (i - mdp->num_rx_ring);
/* Mark the last entry as wrapping the ring. */
- rxdesc->status |= cpu_to_edmac(mdp, RD_RDLE);
+ rxdesc->status |= cpu_to_le32(RD_RDLE);
memset(mdp->tx_ring, 0, tx_ringsize);
@@ -1195,8 +1171,8 @@ static void sh_eth_ring_format(struct net_device *ndev)
for (i = 0; i < mdp->num_tx_ring; i++) {
mdp->tx_skbuff[i] = NULL;
txdesc = &mdp->tx_ring[i];
- txdesc->status = cpu_to_edmac(mdp, TD_TFP);
- txdesc->buffer_length = 0;
+ txdesc->status = cpu_to_le32(TD_TFP);
+ txdesc->len = cpu_to_le32(0);
if (i == 0) {
/* Tx descriptor address set */
sh_eth_write(ndev, mdp->tx_desc_dma, TDLAR);
@@ -1206,7 +1182,7 @@ static void sh_eth_ring_format(struct net_device *ndev)
}
}
- txdesc->status |= cpu_to_edmac(mdp, TD_TDLE);
+ txdesc->status |= cpu_to_le32(TD_TDLE);
}
/* Get skb and descriptor buffer */
@@ -1264,7 +1240,6 @@ static int sh_eth_dev_init(struct net_device *ndev, bool start)
{
int ret = 0;
struct sh_eth_private *mdp = netdev_priv(ndev);
- u32 val;
/* Soft Reset */
ret = sh_eth_reset(ndev);
@@ -1317,10 +1292,8 @@ static int sh_eth_dev_init(struct net_device *ndev, bool start)
}
/* PAUSE Prohibition */
- val = (sh_eth_read(ndev, ECMR) & ECMR_DM) |
- ECMR_ZPF | (mdp->duplex ? ECMR_DM : 0) | ECMR_TE | ECMR_RE;
-
- sh_eth_write(ndev, val, ECMR);
+ sh_eth_write(ndev, ECMR_ZPF | (mdp->duplex ? ECMR_DM : 0) |
+ ECMR_TE | ECMR_RE, ECMR);
if (mdp->cd->set_rate)
mdp->cd->set_rate(ndev);
@@ -1362,7 +1335,7 @@ static void sh_eth_dev_exit(struct net_device *ndev)
* packet boundary if it's currently running
*/
for (i = 0; i < mdp->num_tx_ring; i++)
- mdp->tx_ring[i].status &= ~cpu_to_edmac(mdp, TD_TACT);
+ mdp->tx_ring[i].status &= ~cpu_to_le32(TD_TACT);
/* Disable TX FIFO egress to MAC */
sh_eth_rcv_snd_disable(ndev);
@@ -1394,27 +1367,28 @@ static int sh_eth_txfree(struct net_device *ndev)
for (; mdp->cur_tx - mdp->dirty_tx > 0; mdp->dirty_tx++) {
entry = mdp->dirty_tx % mdp->num_tx_ring;
txdesc = &mdp->tx_ring[entry];
- if (txdesc->status & cpu_to_edmac(mdp, TD_TACT))
+ if (txdesc->status & cpu_to_le32(TD_TACT))
break;
/* TACT bit must be checked before all the following reads */
dma_rmb();
netif_info(mdp, tx_done, ndev,
"tx entry %d status 0x%08x\n",
- entry, edmac_to_cpu(mdp, txdesc->status));
+ entry, le32_to_cpu(txdesc->status));
/* Free the original skb. */
if (mdp->tx_skbuff[entry]) {
- dma_unmap_single(&ndev->dev, txdesc->addr,
- txdesc->buffer_length, DMA_TO_DEVICE);
+ dma_unmap_single(&ndev->dev, le32_to_cpu(txdesc->addr),
+ le32_to_cpu(txdesc->len) >> 16,
+ DMA_TO_DEVICE);
dev_kfree_skb_irq(mdp->tx_skbuff[entry]);
mdp->tx_skbuff[entry] = NULL;
free_num++;
}
- txdesc->status = cpu_to_edmac(mdp, TD_TFP);
+ txdesc->status = cpu_to_le32(TD_TFP);
if (entry >= mdp->num_tx_ring - 1)
- txdesc->status |= cpu_to_edmac(mdp, TD_TDLE);
+ txdesc->status |= cpu_to_le32(TD_TDLE);
ndev->stats.tx_packets++;
- ndev->stats.tx_bytes += txdesc->buffer_length;
+ ndev->stats.tx_bytes += le32_to_cpu(txdesc->len) >> 16;
}
return free_num;
}
@@ -1433,15 +1407,16 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
u32 desc_status;
int skbuff_size = mdp->rx_buf_sz + SH_ETH_RX_ALIGN + 32 - 1;
dma_addr_t dma_addr;
+ u32 buf_len;
boguscnt = min(boguscnt, *quota);
limit = boguscnt;
rxdesc = &mdp->rx_ring[entry];
- while (!(rxdesc->status & cpu_to_edmac(mdp, RD_RACT))) {
+ while (!(rxdesc->status & cpu_to_le32(RD_RACT))) {
/* RACT bit must be checked before all the following reads */
dma_rmb();
- desc_status = edmac_to_cpu(mdp, rxdesc->status);
- pkt_len = rxdesc->frame_length;
+ desc_status = le32_to_cpu(rxdesc->status);
+ pkt_len = le32_to_cpu(rxdesc->len) & RD_RFL;
if (--boguscnt < 0)
break;
@@ -1462,6 +1437,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
if (mdp->cd->shift_rd0)
desc_status >>= 16;
+ skb = mdp->rx_skbuff[entry];
if (desc_status & (RD_RFS1 | RD_RFS2 | RD_RFS3 | RD_RFS4 |
RD_RFS5 | RD_RFS6 | RD_RFS10)) {
ndev->stats.rx_errors++;
@@ -1477,16 +1453,16 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
ndev->stats.rx_missed_errors++;
if (desc_status & RD_RFS10)
ndev->stats.rx_over_errors++;
- } else {
+ } else if (skb) {
+ dma_addr = le32_to_cpu(rxdesc->addr);
if (!mdp->cd->hw_swap)
sh_eth_soft_swap(
- phys_to_virt(ALIGN(rxdesc->addr, 4)),
+ phys_to_virt(ALIGN(dma_addr, 4)),
pkt_len + 2);
- skb = mdp->rx_skbuff[entry];
mdp->rx_skbuff[entry] = NULL;
if (mdp->cd->rpadir)
skb_reserve(skb, NET_IP_ALIGN);
- dma_unmap_single(&ndev->dev, rxdesc->addr,
+ dma_unmap_single(&ndev->dev, dma_addr,
ALIGN(mdp->rx_buf_sz, 32),
DMA_FROM_DEVICE);
skb_put(skb, pkt_len);
@@ -1506,7 +1482,8 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
entry = mdp->dirty_rx % mdp->num_rx_ring;
rxdesc = &mdp->rx_ring[entry];
/* The size of the buffer is 32 byte boundary. */
- rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 32);
+ buf_len = ALIGN(mdp->rx_buf_sz, 32);
+ rxdesc->len = cpu_to_le32(buf_len << 16);
if (mdp->rx_skbuff[entry] == NULL) {
skb = netdev_alloc_skb(ndev, skbuff_size);
@@ -1514,8 +1491,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
break; /* Better luck next round. */
sh_eth_set_receive_align(skb);
dma_addr = dma_map_single(&ndev->dev, skb->data,
- rxdesc->buffer_length,
- DMA_FROM_DEVICE);
+ buf_len, DMA_FROM_DEVICE);
if (dma_mapping_error(&ndev->dev, dma_addr)) {
kfree_skb(skb);
break;
@@ -1523,15 +1499,14 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
mdp->rx_skbuff[entry] = skb;
skb_checksum_none_assert(skb);
- rxdesc->addr = dma_addr;
+ rxdesc->addr = cpu_to_le32(dma_addr);
}
dma_wmb(); /* RACT bit must be set after all the above writes */
if (entry >= mdp->num_rx_ring - 1)
rxdesc->status |=
- cpu_to_edmac(mdp, RD_RACT | RD_RFP | RD_RDLE);
+ cpu_to_le32(RD_RACT | RD_RFP | RD_RDLE);
else
- rxdesc->status |=
- cpu_to_edmac(mdp, RD_RACT | RD_RFP);
+ rxdesc->status |= cpu_to_le32(RD_RACT | RD_RFP);
}
/* Restart Rx engine if stopped. */
@@ -1848,8 +1823,7 @@ static int sh_eth_phy_init(struct net_device *ndev)
return PTR_ERR(phydev);
}
- netdev_info(ndev, "attached PHY %d (IRQ %d) to driver %s\n",
- phydev->addr, phydev->irq, phydev->drv->name);
+ phy_attached_info(phydev);
mdp->phydev = phydev;
@@ -2331,8 +2305,8 @@ static void sh_eth_tx_timeout(struct net_device *ndev)
/* Free all the skbuffs in the Rx queue. */
for (i = 0; i < mdp->num_rx_ring; i++) {
rxdesc = &mdp->rx_ring[i];
- rxdesc->status = 0;
- rxdesc->addr = 0xBADF00D0;
+ rxdesc->status = cpu_to_le32(0);
+ rxdesc->addr = cpu_to_le32(0xBADF00D0);
dev_kfree_skb(mdp->rx_skbuff[i]);
mdp->rx_skbuff[i] = NULL;
}
@@ -2350,6 +2324,7 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct sh_eth_private *mdp = netdev_priv(ndev);
struct sh_eth_txdesc *txdesc;
+ dma_addr_t dma_addr;
u32 entry;
unsigned long flags;
@@ -2372,21 +2347,21 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev)
txdesc = &mdp->tx_ring[entry];
/* soft swap. */
if (!mdp->cd->hw_swap)
- sh_eth_soft_swap(phys_to_virt(ALIGN(txdesc->addr, 4)),
- skb->len + 2);
- txdesc->addr = dma_map_single(&ndev->dev, skb->data, skb->len,
- DMA_TO_DEVICE);
- if (dma_mapping_error(&ndev->dev, txdesc->addr)) {
+ sh_eth_soft_swap(PTR_ALIGN(skb->data, 4), skb->len + 2);
+ dma_addr = dma_map_single(&ndev->dev, skb->data, skb->len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(&ndev->dev, dma_addr)) {
kfree_skb(skb);
return NETDEV_TX_OK;
}
- txdesc->buffer_length = skb->len;
+ txdesc->addr = cpu_to_le32(dma_addr);
+ txdesc->len = cpu_to_le32(skb->len << 16);
dma_wmb(); /* TACT bit must be set after all the above writes */
if (entry >= mdp->num_tx_ring - 1)
- txdesc->status |= cpu_to_edmac(mdp, TD_TACT | TD_TDLE);
+ txdesc->status |= cpu_to_le32(TD_TACT | TD_TDLE);
else
- txdesc->status |= cpu_to_edmac(mdp, TD_TACT);
+ txdesc->status |= cpu_to_le32(TD_TACT);
mdp->cur_tx++;
@@ -2881,7 +2856,7 @@ static int sh_mdio_release(struct sh_eth_private *mdp)
static int sh_mdio_init(struct sh_eth_private *mdp,
struct sh_eth_plat_data *pd)
{
- int ret, i;
+ int ret;
struct bb_info *bitbang;
struct platform_device *pdev = mdp->pdev;
struct device *dev = &mdp->pdev->dev;
@@ -2894,10 +2869,6 @@ static int sh_mdio_init(struct sh_eth_private *mdp,
/* bitbang init */
bitbang->addr = mdp->addr + mdp->reg_offset[PIR];
bitbang->set_gate = pd->set_mdio_gate;
- bitbang->mdi_msk = PIR_MDI;
- bitbang->mdo_msk = PIR_MDO;
- bitbang->mmd_msk = PIR_MMD;
- bitbang->mdc_msk = PIR_MDC;
bitbang->ctrl.ops = &bb_ops;
/* MII controller setting */
@@ -2911,20 +2882,10 @@ static int sh_mdio_init(struct sh_eth_private *mdp,
snprintf(mdp->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
pdev->name, pdev->id);
- /* PHY IRQ */
- mdp->mii_bus->irq = devm_kmalloc_array(dev, PHY_MAX_ADDR, sizeof(int),
- GFP_KERNEL);
- if (!mdp->mii_bus->irq) {
- ret = -ENOMEM;
- goto out_free_bus;
- }
-
/* register MDIO bus */
if (dev->of_node) {
ret = of_mdiobus_register(mdp->mii_bus, dev->of_node);
} else {
- for (i = 0; i < PHY_MAX_ADDR; i++)
- mdp->mii_bus->irq[i] = PHY_POLL;
if (pd->phy_irq > 0)
mdp->mii_bus->irq[pd->phy] = pd->phy_irq;
@@ -3096,8 +3057,6 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
/* get PHY ID */
mdp->phy_id = pd->phy;
mdp->phy_interface = pd->phy_interface;
- /* EDMAC endian */
- mdp->edmac_endian = pd->edmac_endian;
mdp->no_ether_link = pd->no_ether_link;
mdp->ether_link_active_low = pd->ether_link_active_low;
@@ -3277,13 +3236,6 @@ static struct platform_device_id sh_eth_id_table[] = {
{ "sh7757-ether", (kernel_ulong_t)&sh7757_data },
{ "sh7757-gether", (kernel_ulong_t)&sh7757_data_giga },
{ "sh7763-gether", (kernel_ulong_t)&sh7763_data },
- { "r7s72100-ether", (kernel_ulong_t)&r7s72100_data },
- { "r8a7740-gether", (kernel_ulong_t)&r8a7740_data },
- { "r8a777x-ether", (kernel_ulong_t)&r8a777x_data },
- { "r8a7790-ether", (kernel_ulong_t)&r8a779x_data },
- { "r8a7791-ether", (kernel_ulong_t)&r8a779x_data },
- { "r8a7793-ether", (kernel_ulong_t)&r8a779x_data },
- { "r8a7794-ether", (kernel_ulong_t)&r8a779x_data },
{ }
};
MODULE_DEVICE_TABLE(platform, sh_eth_id_table);
diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h
index 50382b1c9ddc..8fa4ef3a7fdd 100644
--- a/drivers/net/ethernet/renesas/sh_eth.h
+++ b/drivers/net/ethernet/renesas/sh_eth.h
@@ -283,7 +283,7 @@ enum DMAC_IM_BIT {
DMAC_M_RINT1 = 0x00000001,
};
-/* Receive descriptor bit */
+/* Receive descriptor 0 bits */
enum RD_STS_BIT {
RD_RACT = 0x80000000, RD_RDLE = 0x40000000,
RD_RFP1 = 0x20000000, RD_RFP0 = 0x10000000,
@@ -298,6 +298,12 @@ enum RD_STS_BIT {
#define RDFEND RD_RFP0
#define RD_RFP (RD_RFP1|RD_RFP0)
+/* Receive descriptor 1 bits */
+enum RD_LEN_BIT {
+ RD_RFL = 0x0000ffff, /* receive frame length */
+ RD_RBL = 0xffff0000, /* receive buffer length */
+};
+
/* FCFTR */
enum FCFTR_BIT {
FCFTR_RFF2 = 0x00040000, FCFTR_RFF1 = 0x00020000,
@@ -307,7 +313,7 @@ enum FCFTR_BIT {
#define DEFAULT_FIFO_F_D_RFF (FCFTR_RFF2 | FCFTR_RFF1 | FCFTR_RFF0)
#define DEFAULT_FIFO_F_D_RFD (FCFTR_RFD2 | FCFTR_RFD1 | FCFTR_RFD0)
-/* Transmit descriptor bit */
+/* Transmit descriptor 0 bits */
enum TD_STS_BIT {
TD_TACT = 0x80000000, TD_TDLE = 0x40000000,
TD_TFP1 = 0x20000000, TD_TFP0 = 0x10000000,
@@ -317,6 +323,11 @@ enum TD_STS_BIT {
#define TDFEND TD_TFP0
#define TD_TFP (TD_TFP1|TD_TFP0)
+/* Transmit descriptor 1 bits */
+enum TD_LEN_BIT {
+ TD_TBL = 0xffff0000, /* transmit buffer length */
+};
+
/* RMCR */
enum RMCR_BIT {
RMCR_RNC = 0x00000001,
@@ -425,15 +436,9 @@ enum TSU_FWSLC_BIT {
*/
struct sh_eth_txdesc {
u32 status; /* TD0 */
-#if defined(__LITTLE_ENDIAN)
- u16 pad0; /* TD1 */
- u16 buffer_length; /* TD1 */
-#else
- u16 buffer_length; /* TD1 */
- u16 pad0; /* TD1 */
-#endif
+ u32 len; /* TD1 */
u32 addr; /* TD2 */
- u32 pad1; /* padding data */
+ u32 pad0; /* padding data */
} __aligned(2) __packed;
/* The sh ether Rx buffer descriptors.
@@ -441,13 +446,7 @@ struct sh_eth_txdesc {
*/
struct sh_eth_rxdesc {
u32 status; /* RD0 */
-#if defined(__LITTLE_ENDIAN)
- u16 frame_length; /* RD1 */
- u16 buffer_length; /* RD1 */
-#else
- u16 buffer_length; /* RD1 */
- u16 frame_length; /* RD1 */
-#endif
+ u32 len; /* RD1 */
u32 addr; /* RD2 */
u32 pad0; /* padding data */
} __aligned(2) __packed;
@@ -514,7 +513,6 @@ struct sh_eth_private {
u32 cur_rx, dirty_rx; /* Producer/consumer ring indices */
u32 cur_tx, dirty_tx;
u32 rx_buf_sz; /* Based on MTU+slack. */
- int edmac_endian;
struct napi_struct napi;
bool irq_enabled;
/* MII transceiver section. */
@@ -546,31 +544,6 @@ static inline void sh_eth_soft_swap(char *src, int len)
#endif
}
-#define SH_ETH_OFFSET_INVALID ((u16) ~0)
-
-static inline void sh_eth_write(struct net_device *ndev, u32 data,
- int enum_index)
-{
- struct sh_eth_private *mdp = netdev_priv(ndev);
- u16 offset = mdp->reg_offset[enum_index];
-
- if (WARN_ON(offset == SH_ETH_OFFSET_INVALID))
- return;
-
- iowrite32(data, mdp->addr + offset);
-}
-
-static inline u32 sh_eth_read(struct net_device *ndev, int enum_index)
-{
- struct sh_eth_private *mdp = netdev_priv(ndev);
- u16 offset = mdp->reg_offset[enum_index];
-
- if (WARN_ON(offset == SH_ETH_OFFSET_INVALID))
- return ~0U;
-
- return ioread32(mdp->addr + offset);
-}
-
static inline void *sh_eth_tsu_get_offset(struct sh_eth_private *mdp,
int enum_index)
{
diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c
index e9f2349e98bc..a4ab71d43e4e 100644
--- a/drivers/net/ethernet/rocker/rocker.c
+++ b/drivers/net/ethernet/rocker/rocker.c
@@ -4998,7 +4998,7 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number)
dev->netdev_ops = &rocker_port_netdev_ops;
dev->ethtool_ops = &rocker_port_ethtool_ops;
dev->switchdev_ops = &rocker_port_switchdev_ops;
- netif_napi_add(dev, &rocker_port->napi_tx, rocker_port_poll_tx,
+ netif_tx_napi_add(dev, &rocker_port->napi_tx, rocker_port_poll_tx,
NAPI_POLL_WEIGHT);
netif_napi_add(dev, &rocker_port->napi_rx, rocker_port_poll_rx,
NAPI_POLL_WEIGHT);
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c
index 43ccb4a6de15..467ff7033606 100644
--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_mdio.c
@@ -180,7 +180,7 @@ int sxgbe_mdio_register(struct net_device *ndev)
}
for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
- struct phy_device *phy = mdio_bus->phy_map[phy_addr];
+ struct phy_device *phy = mdiobus_get_phy(mdio_bus, phy_addr);
if (phy) {
char irq_num[4];
@@ -216,7 +216,7 @@ int sxgbe_mdio_register(struct net_device *ndev)
}
netdev_info(ndev, "PHY ID %08x at %d IRQ %s (%s)%s\n",
phy->phy_id, phy_addr, irq_str,
- dev_name(&phy->dev), act ? " active" : "");
+ phydev_name(phy), act ? " active" : "");
phy_found = true;
}
}
diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c
index bc6d21b471be..98d33d462c6c 100644
--- a/drivers/net/ethernet/sfc/ef10.c
+++ b/drivers/net/ethernet/sfc/ef10.c
@@ -181,13 +181,6 @@ static int efx_ef10_init_datapath_caps(struct efx_nic *efx)
MCDI_WORD(outbuf, GET_CAPABILITIES_OUT_TX_DPCPU_FW_ID);
if (!(nic_data->datapath_caps &
- (1 << MC_CMD_GET_CAPABILITIES_OUT_TX_TSO_LBN))) {
- netif_err(efx, drv, efx->net_dev,
- "current firmware does not support TSO\n");
- return -ENODEV;
- }
-
- if (!(nic_data->datapath_caps &
(1 << MC_CMD_GET_CAPABILITIES_OUT_RX_PREFIX_LEN_14_LBN))) {
netif_err(efx, probe, efx->net_dev,
"current firmware does not support an RX prefix\n");
@@ -493,10 +486,17 @@ static int efx_ef10_alloc_piobufs(struct efx_nic *efx, unsigned int n)
BUILD_BUG_ON(MC_CMD_ALLOC_PIOBUF_IN_LEN != 0);
for (i = 0; i < n; i++) {
- rc = efx_mcdi_rpc(efx, MC_CMD_ALLOC_PIOBUF, NULL, 0,
- outbuf, sizeof(outbuf), &outlen);
- if (rc)
+ rc = efx_mcdi_rpc_quiet(efx, MC_CMD_ALLOC_PIOBUF, NULL, 0,
+ outbuf, sizeof(outbuf), &outlen);
+ if (rc) {
+ /* Don't display the MC error if we didn't have space
+ * for a VF.
+ */
+ if (!(efx_ef10_is_vf(efx) && rc == -ENOSPC))
+ efx_mcdi_display_error(efx, MC_CMD_ALLOC_PIOBUF,
+ 0, outbuf, outlen, rc);
break;
+ }
if (outlen < MC_CMD_ALLOC_PIOBUF_OUT_LEN) {
rc = -EIO;
break;
@@ -1797,6 +1797,12 @@ static void efx_ef10_tx_init(struct efx_tx_queue *tx_queue)
ESF_DZ_TX_OPTION_UDP_TCP_CSUM, csum_offload,
ESF_DZ_TX_OPTION_IP_CSUM, csum_offload);
tx_queue->write_count = 1;
+
+ if (nic_data->datapath_caps &
+ (1 << MC_CMD_GET_CAPABILITIES_OUT_TX_TSO_LBN)) {
+ tx_queue->tso_version = 1;
+ }
+
wmb();
efx_ef10_push_tx_desc(tx_queue, txd);
@@ -2375,8 +2381,19 @@ static int efx_ef10_ev_init(struct efx_channel *channel)
1 << MC_CMD_WORKAROUND_EXT_OUT_FLR_DONE_LBN) {
netif_info(efx, drv, efx->net_dev,
"other functions on NIC have been reset\n");
- /* MC's boot count has incremented */
- ++nic_data->warm_boot_count;
+
+ /* With MCFW v4.6.x and earlier, the
+ * boot count will have incremented,
+ * so re-read the warm_boot_count
+ * value now to ensure this function
+ * doesn't think it has changed next
+ * time it checks.
+ */
+ rc = efx_ef10_get_warm_boot_count(efx);
+ if (rc >= 0) {
+ nic_data->warm_boot_count = rc;
+ rc = 0;
+ }
}
nic_data->workaround_26807 = true;
} else if (rc == -EPERM) {
@@ -3299,7 +3316,8 @@ static int efx_ef10_filter_remove_internal(struct efx_nic *efx,
new_spec.priority = EFX_FILTER_PRI_AUTO;
new_spec.flags = (EFX_FILTER_FLAG_RX |
- EFX_FILTER_FLAG_RX_RSS);
+ (efx_rss_enabled(efx) ?
+ EFX_FILTER_FLAG_RX_RSS : 0));
new_spec.dmaq_id = 0;
new_spec.rss_context = EFX_FILTER_RSS_CONTEXT_DEFAULT;
rc = efx_ef10_filter_push(efx, &new_spec,
@@ -3822,13 +3840,12 @@ static void efx_ef10_filter_table_remove(struct efx_nic *efx)
MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE);
MCDI_SET_QWORD(inbuf, FILTER_OP_IN_HANDLE,
table->entry[filter_idx].handle);
- rc = efx_mcdi_rpc(efx, MC_CMD_FILTER_OP, inbuf, sizeof(inbuf),
- NULL, 0, NULL);
+ rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FILTER_OP, inbuf,
+ sizeof(inbuf), NULL, 0, NULL);
if (rc)
- netdev_WARN(efx->net_dev,
- "filter_idx=%#x handle=%#llx\n",
- filter_idx,
- table->entry[filter_idx].handle);
+ netif_info(efx, drv, efx->net_dev,
+ "%s: filter %04x remove failed\n",
+ __func__, filter_idx);
kfree(spec);
}
@@ -3837,11 +3854,14 @@ static void efx_ef10_filter_table_remove(struct efx_nic *efx)
}
#define EFX_EF10_FILTER_DO_MARK_OLD(id) \
- if (id != EFX_EF10_FILTER_ID_INVALID) { \
- filter_idx = efx_ef10_filter_get_unsafe_id(efx, id); \
- WARN_ON(!table->entry[filter_idx].spec); \
- table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD; \
- }
+ if (id != EFX_EF10_FILTER_ID_INVALID) { \
+ filter_idx = efx_ef10_filter_get_unsafe_id(efx, id); \
+ if (!table->entry[filter_idx].spec) \
+ netif_dbg(efx, drv, efx->net_dev, \
+ "%s: marked null spec old %04x:%04x\n", \
+ __func__, id, filter_idx); \
+ table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD;\
+ }
static void efx_ef10_filter_mark_old(struct efx_nic *efx)
{
struct efx_ef10_filter_table *table = efx->filter_state;
@@ -3921,6 +3941,7 @@ static int efx_ef10_filter_insert_addr_list(struct efx_nic *efx,
{
struct efx_ef10_filter_table *table = efx->filter_state;
struct efx_ef10_dev_addr *addr_list;
+ enum efx_filter_flags filter_flags;
struct efx_filter_spec spec;
u8 baddr[ETH_ALEN];
unsigned int i, j;
@@ -3935,11 +3956,11 @@ static int efx_ef10_filter_insert_addr_list(struct efx_nic *efx,
addr_count = table->dev_uc_count;
}
+ filter_flags = efx_rss_enabled(efx) ? EFX_FILTER_FLAG_RX_RSS : 0;
+
/* Insert/renew filters */
for (i = 0; i < addr_count; i++) {
- efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO,
- EFX_FILTER_FLAG_RX_RSS,
- 0);
+ efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO, filter_flags, 0);
efx_filter_set_eth_local(&spec, EFX_FILTER_VID_UNSPEC,
addr_list[i].addr);
rc = efx_ef10_filter_insert(efx, &spec, true);
@@ -3968,9 +3989,7 @@ static int efx_ef10_filter_insert_addr_list(struct efx_nic *efx,
if (multicast && rollback) {
/* Also need an Ethernet broadcast filter */
- efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO,
- EFX_FILTER_FLAG_RX_RSS,
- 0);
+ efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO, filter_flags, 0);
eth_broadcast_addr(baddr);
efx_filter_set_eth_local(&spec, EFX_FILTER_VID_UNSPEC, baddr);
rc = efx_ef10_filter_insert(efx, &spec, true);
@@ -4000,13 +4019,14 @@ static int efx_ef10_filter_insert_def(struct efx_nic *efx, bool multicast,
{
struct efx_ef10_filter_table *table = efx->filter_state;
struct efx_ef10_nic_data *nic_data = efx->nic_data;
+ enum efx_filter_flags filter_flags;
struct efx_filter_spec spec;
u8 baddr[ETH_ALEN];
int rc;
- efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO,
- EFX_FILTER_FLAG_RX_RSS,
- 0);
+ filter_flags = efx_rss_enabled(efx) ? EFX_FILTER_FLAG_RX_RSS : 0;
+
+ efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO, filter_flags, 0);
if (multicast)
efx_filter_set_mc_def(&spec);
@@ -4015,16 +4035,16 @@ static int efx_ef10_filter_insert_def(struct efx_nic *efx, bool multicast,
rc = efx_ef10_filter_insert(efx, &spec, true);
if (rc < 0) {
- netif_warn(efx, drv, efx->net_dev,
- "%scast mismatch filter insert failed rc=%d\n",
- multicast ? "Multi" : "Uni", rc);
+ netif_printk(efx, drv, rc == -EPERM ? KERN_DEBUG : KERN_WARNING,
+ efx->net_dev,
+ "%scast mismatch filter insert failed rc=%d\n",
+ multicast ? "Multi" : "Uni", rc);
} else if (multicast) {
table->mcdef_id = efx_ef10_filter_get_unsafe_id(efx, rc);
if (!nic_data->workaround_26807) {
/* Also need an Ethernet broadcast filter */
efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO,
- EFX_FILTER_FLAG_RX_RSS,
- 0);
+ filter_flags, 0);
eth_broadcast_addr(baddr);
efx_filter_set_eth_local(&spec, EFX_FILTER_VID_UNSPEC,
baddr);
@@ -4060,19 +4080,31 @@ static int efx_ef10_filter_insert_def(struct efx_nic *efx, bool multicast,
static void efx_ef10_filter_remove_old(struct efx_nic *efx)
{
struct efx_ef10_filter_table *table = efx->filter_state;
- bool remove_failed = false;
+ int remove_failed = 0;
+ int remove_noent = 0;
+ int rc;
int i;
for (i = 0; i < HUNT_FILTER_TBL_ROWS; i++) {
if (ACCESS_ONCE(table->entry[i].spec) &
EFX_EF10_FILTER_FLAG_AUTO_OLD) {
- if (efx_ef10_filter_remove_internal(
- efx, 1U << EFX_FILTER_PRI_AUTO,
- i, true) < 0)
- remove_failed = true;
+ rc = efx_ef10_filter_remove_internal(efx,
+ 1U << EFX_FILTER_PRI_AUTO, i, true);
+ if (rc == -ENOENT)
+ remove_noent++;
+ else if (rc)
+ remove_failed++;
}
}
- WARN_ON(remove_failed);
+
+ if (remove_failed)
+ netif_info(efx, drv, efx->net_dev,
+ "%s: failed to remove %d filters\n",
+ __func__, remove_failed);
+ if (remove_noent)
+ netif_info(efx, drv, efx->net_dev,
+ "%s: failed to remove %d non-existent filters\n",
+ __func__, remove_noent);
}
static int efx_ef10_vport_set_mac_address(struct efx_nic *efx)
diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c
index a3c42a376741..0705ec869487 100644
--- a/drivers/net/ethernet/sfc/efx.c
+++ b/drivers/net/ethernet/sfc/efx.c
@@ -2059,7 +2059,6 @@ static void efx_init_napi_channel(struct efx_channel *channel)
channel->napi_dev = efx->net_dev;
netif_napi_add(channel->napi_dev, &channel->napi_str,
efx_poll, napi_weight);
- napi_hash_add(&channel->napi_str);
efx_channel_busy_poll_init(channel);
}
@@ -2785,6 +2784,12 @@ static const struct pci_device_id efx_pci_table[] = {
.driver_data = (unsigned long) &efx_hunt_a0_vf_nic_type},
{PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, 0x0923), /* SFC9140 PF */
.driver_data = (unsigned long) &efx_hunt_a0_nic_type},
+ {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, 0x1923), /* SFC9140 VF */
+ .driver_data = (unsigned long) &efx_hunt_a0_vf_nic_type},
+ {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, 0x0a03), /* SFC9220 PF */
+ .driver_data = (unsigned long) &efx_hunt_a0_nic_type},
+ {PCI_DEVICE(PCI_VENDOR_ID_SOLARFLARE, 0x1a03), /* SFC9220 VF */
+ .driver_data = (unsigned long) &efx_hunt_a0_vf_nic_type},
{0} /* end of list */
};
@@ -3123,10 +3128,10 @@ static int efx_pci_probe(struct pci_dev *pci_dev,
net_dev->features |= (efx->type->offload_features | NETIF_F_SG |
NETIF_F_HIGHDMA | NETIF_F_TSO |
NETIF_F_RXCSUM);
- if (efx->type->offload_features & NETIF_F_V6_CSUM)
+ if (efx->type->offload_features & (NETIF_F_IPV6_CSUM | NETIF_F_HW_CSUM))
net_dev->features |= NETIF_F_TSO6;
/* Mask for features that also apply to VLAN devices */
- net_dev->vlan_features |= (NETIF_F_ALL_CSUM | NETIF_F_SG |
+ net_dev->vlan_features |= (NETIF_F_HW_CSUM | NETIF_F_SG |
NETIF_F_HIGHDMA | NETIF_F_ALL_TSO |
NETIF_F_RXCSUM);
/* All offloads can be toggled */
@@ -3169,14 +3174,15 @@ static int efx_pci_probe(struct pci_dev *pci_dev,
rtnl_lock();
rc = efx_mtd_probe(efx);
rtnl_unlock();
- if (rc)
+ if (rc && rc != -EPERM)
netif_warn(efx, probe, efx->net_dev,
"failed to create MTDs (%d)\n", rc);
rc = pci_enable_pcie_error_reporting(pci_dev);
if (rc && rc != -EINVAL)
- netif_warn(efx, probe, efx->net_dev,
- "pci_enable_pcie_error_reporting failed (%d)\n", rc);
+ netif_notice(efx, probe, efx->net_dev,
+ "PCIE error reporting unavailable (%d).\n",
+ rc);
return 0;
diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h
index 1aaf76c1ace8..10827476bc0b 100644
--- a/drivers/net/ethernet/sfc/efx.h
+++ b/drivers/net/ethernet/sfc/efx.h
@@ -76,6 +76,11 @@ void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue);
#define EFX_TXQ_MAX_ENT(efx) (EFX_WORKAROUND_35388(efx) ? \
EFX_MAX_DMAQ_SIZE / 2 : EFX_MAX_DMAQ_SIZE)
+static inline bool efx_rss_enabled(struct efx_nic *efx)
+{
+ return efx->rss_spread > 1;
+}
+
/* Filters */
void efx_mac_reconfigure(struct efx_nic *efx);
diff --git a/drivers/net/ethernet/sfc/farch.c b/drivers/net/ethernet/sfc/farch.c
index 5a1c5a8f278a..133e9e35be9e 100644
--- a/drivers/net/ethernet/sfc/farch.c
+++ b/drivers/net/ethernet/sfc/farch.c
@@ -2242,7 +2242,7 @@ efx_farch_filter_init_rx_auto(struct efx_nic *efx,
*/
spec->priority = EFX_FILTER_PRI_AUTO;
spec->flags = (EFX_FILTER_FLAG_RX |
- (efx->n_rx_channels > 1 ? EFX_FILTER_FLAG_RX_RSS : 0) |
+ (efx_rss_enabled(efx) ? EFX_FILTER_FLAG_RX_RSS : 0) |
(efx->rx_scatter ? EFX_FILTER_FLAG_RX_SCATTER : 0));
spec->dmaq_id = 0;
}
diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c
index 41fb6b60a3f0..d28e7dd8fa3c 100644
--- a/drivers/net/ethernet/sfc/mcdi.c
+++ b/drivers/net/ethernet/sfc/mcdi.c
@@ -82,6 +82,7 @@ int efx_mcdi_init(struct efx_nic *efx)
mcdi->logging_enabled = mcdi_logging_default;
#endif
init_waitqueue_head(&mcdi->wq);
+ init_waitqueue_head(&mcdi->proxy_rx_wq);
spin_lock_init(&mcdi->iface_lock);
mcdi->state = MCDI_STATE_QUIESCENT;
mcdi->mode = MCDI_MODE_POLL;
@@ -315,6 +316,7 @@ static void efx_mcdi_read_response_header(struct efx_nic *efx)
}
#endif
+ mcdi->resprc_raw = 0;
if (error && mcdi->resp_data_len == 0) {
netif_err(efx, hw, efx->net_dev, "MC rebooted\n");
mcdi->resprc = -EIO;
@@ -325,8 +327,8 @@ static void efx_mcdi_read_response_header(struct efx_nic *efx)
mcdi->resprc = -EIO;
} else if (error) {
efx->type->mcdi_read_response(efx, &hdr, mcdi->resp_hdr_len, 4);
- mcdi->resprc =
- efx_mcdi_errno(EFX_DWORD_FIELD(hdr, EFX_DWORD_0));
+ mcdi->resprc_raw = EFX_DWORD_FIELD(hdr, EFX_DWORD_0);
+ mcdi->resprc = efx_mcdi_errno(mcdi->resprc_raw);
} else {
mcdi->resprc = 0;
}
@@ -621,9 +623,30 @@ efx_mcdi_check_supported(struct efx_nic *efx, unsigned int cmd, size_t inlen)
return 0;
}
-static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
+static bool efx_mcdi_get_proxy_handle(struct efx_nic *efx,
+ size_t hdr_len, size_t data_len,
+ u32 *proxy_handle)
+{
+ MCDI_DECLARE_BUF_ERR(testbuf);
+ const size_t buflen = sizeof(testbuf);
+
+ if (!proxy_handle || data_len < buflen)
+ return false;
+
+ efx->type->mcdi_read_response(efx, testbuf, hdr_len, buflen);
+ if (MCDI_DWORD(testbuf, ERR_CODE) == MC_CMD_ERR_PROXY_PENDING) {
+ *proxy_handle = MCDI_DWORD(testbuf, ERR_PROXY_PENDING_HANDLE);
+ return true;
+ }
+
+ return false;
+}
+
+static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned int cmd,
+ size_t inlen,
efx_dword_t *outbuf, size_t outlen,
- size_t *outlen_actual, bool quiet)
+ size_t *outlen_actual, bool quiet,
+ u32 *proxy_handle, int *raw_rc)
{
struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
MCDI_DECLARE_BUF_ERR(errbuf);
@@ -657,6 +680,9 @@ static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
spin_unlock_bh(&mcdi->iface_lock);
}
+ if (proxy_handle)
+ *proxy_handle = 0;
+
if (rc != 0) {
if (outlen_actual)
*outlen_actual = 0;
@@ -669,6 +695,8 @@ static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
* acquiring the iface_lock. */
spin_lock_bh(&mcdi->iface_lock);
rc = mcdi->resprc;
+ if (raw_rc)
+ *raw_rc = mcdi->resprc_raw;
hdr_len = mcdi->resp_hdr_len;
data_len = mcdi->resp_data_len;
err_len = min(sizeof(errbuf), data_len);
@@ -689,6 +717,12 @@ static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
netif_err(efx, hw, efx->net_dev, "MC fatal error %d\n",
-rc);
efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE);
+ } else if (proxy_handle && (rc == -EPROTO) &&
+ efx_mcdi_get_proxy_handle(efx, hdr_len, data_len,
+ proxy_handle)) {
+ mcdi->proxy_rx_status = 0;
+ mcdi->proxy_rx_handle = 0;
+ mcdi->state = MCDI_STATE_PROXY_WAIT;
} else if (rc && !quiet) {
efx_mcdi_display_error(efx, cmd, inlen, errbuf, err_len,
rc);
@@ -701,34 +735,195 @@ static int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
}
}
- efx_mcdi_release(mcdi);
+ if (!proxy_handle || !*proxy_handle)
+ efx_mcdi_release(mcdi);
return rc;
}
-static int _efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd,
+static void efx_mcdi_proxy_abort(struct efx_mcdi_iface *mcdi)
+{
+ if (mcdi->state == MCDI_STATE_PROXY_WAIT) {
+ /* Interrupt the proxy wait. */
+ mcdi->proxy_rx_status = -EINTR;
+ wake_up(&mcdi->proxy_rx_wq);
+ }
+}
+
+static void efx_mcdi_ev_proxy_response(struct efx_nic *efx,
+ u32 handle, int status)
+{
+ struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
+
+ WARN_ON(mcdi->state != MCDI_STATE_PROXY_WAIT);
+
+ mcdi->proxy_rx_status = efx_mcdi_errno(status);
+ /* Ensure the status is written before we update the handle, since the
+ * latter is used to check if we've finished.
+ */
+ wmb();
+ mcdi->proxy_rx_handle = handle;
+ wake_up(&mcdi->proxy_rx_wq);
+}
+
+static int efx_mcdi_proxy_wait(struct efx_nic *efx, u32 handle, bool quiet)
+{
+ struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
+ int rc;
+
+ /* Wait for a proxy event, or timeout. */
+ rc = wait_event_timeout(mcdi->proxy_rx_wq,
+ mcdi->proxy_rx_handle != 0 ||
+ mcdi->proxy_rx_status == -EINTR,
+ MCDI_RPC_TIMEOUT);
+
+ if (rc <= 0) {
+ netif_dbg(efx, hw, efx->net_dev,
+ "MCDI proxy timeout %d\n", handle);
+ return -ETIMEDOUT;
+ } else if (mcdi->proxy_rx_handle != handle) {
+ netif_warn(efx, hw, efx->net_dev,
+ "MCDI proxy unexpected handle %d (expected %d)\n",
+ mcdi->proxy_rx_handle, handle);
+ return -EINVAL;
+ }
+
+ return mcdi->proxy_rx_status;
+}
+
+static int _efx_mcdi_rpc(struct efx_nic *efx, unsigned int cmd,
const efx_dword_t *inbuf, size_t inlen,
efx_dword_t *outbuf, size_t outlen,
- size_t *outlen_actual, bool quiet)
+ size_t *outlen_actual, bool quiet, int *raw_rc)
{
+ u32 proxy_handle = 0; /* Zero is an invalid proxy handle. */
int rc;
+ if (inbuf && inlen && (inbuf == outbuf)) {
+ /* The input buffer can't be aliased with the output. */
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
rc = efx_mcdi_rpc_start(efx, cmd, inbuf, inlen);
- if (rc) {
- if (outlen_actual)
- *outlen_actual = 0;
+ if (rc)
return rc;
+
+ rc = _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen,
+ outlen_actual, quiet, &proxy_handle, raw_rc);
+
+ if (proxy_handle) {
+ /* Handle proxy authorisation. This allows approval of MCDI
+ * operations to be delegated to the admin function, allowing
+ * fine control over (eg) multicast subscriptions.
+ */
+ struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
+
+ netif_dbg(efx, hw, efx->net_dev,
+ "MCDI waiting for proxy auth %d\n",
+ proxy_handle);
+ rc = efx_mcdi_proxy_wait(efx, proxy_handle, quiet);
+
+ if (rc == 0) {
+ netif_dbg(efx, hw, efx->net_dev,
+ "MCDI proxy retry %d\n", proxy_handle);
+
+ /* We now retry the original request. */
+ mcdi->state = MCDI_STATE_RUNNING_SYNC;
+ efx_mcdi_send_request(efx, cmd, inbuf, inlen);
+
+ rc = _efx_mcdi_rpc_finish(efx, cmd, inlen,
+ outbuf, outlen, outlen_actual,
+ quiet, NULL, raw_rc);
+ } else {
+ netif_printk(efx, hw,
+ rc == -EPERM ? KERN_DEBUG : KERN_ERR,
+ efx->net_dev,
+ "MC command 0x%x failed after proxy auth rc=%d\n",
+ cmd, rc);
+
+ if (rc == -EINTR || rc == -EIO)
+ efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE);
+ efx_mcdi_release(mcdi);
+ }
}
- return _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen,
- outlen_actual, quiet);
+
+ return rc;
}
+static int _efx_mcdi_rpc_evb_retry(struct efx_nic *efx, unsigned cmd,
+ const efx_dword_t *inbuf, size_t inlen,
+ efx_dword_t *outbuf, size_t outlen,
+ size_t *outlen_actual, bool quiet)
+{
+ int raw_rc = 0;
+ int rc;
+
+ rc = _efx_mcdi_rpc(efx, cmd, inbuf, inlen,
+ outbuf, outlen, outlen_actual, true, &raw_rc);
+
+ if ((rc == -EPROTO) && (raw_rc == MC_CMD_ERR_NO_EVB_PORT) &&
+ efx->type->is_vf) {
+ /* If the EVB port isn't available within a VF this may
+ * mean the PF is still bringing the switch up. We should
+ * retry our request shortly.
+ */
+ unsigned long abort_time = jiffies + MCDI_RPC_TIMEOUT;
+ unsigned int delay_us = 10000;
+
+ netif_dbg(efx, hw, efx->net_dev,
+ "%s: NO_EVB_PORT; will retry request\n",
+ __func__);
+
+ do {
+ usleep_range(delay_us, delay_us + 10000);
+ rc = _efx_mcdi_rpc(efx, cmd, inbuf, inlen,
+ outbuf, outlen, outlen_actual,
+ true, &raw_rc);
+ if (delay_us < 100000)
+ delay_us <<= 1;
+ } while ((rc == -EPROTO) &&
+ (raw_rc == MC_CMD_ERR_NO_EVB_PORT) &&
+ time_before(jiffies, abort_time));
+ }
+
+ if (rc && !quiet && !(cmd == MC_CMD_REBOOT && rc == -EIO))
+ efx_mcdi_display_error(efx, cmd, inlen,
+ outbuf, outlen, rc);
+
+ return rc;
+}
+
+/**
+ * efx_mcdi_rpc - Issue an MCDI command and wait for completion
+ * @efx: NIC through which to issue the command
+ * @cmd: Command type number
+ * @inbuf: Command parameters
+ * @inlen: Length of command parameters, in bytes. Must be a multiple
+ * of 4 and no greater than %MCDI_CTL_SDU_LEN_MAX_V1.
+ * @outbuf: Response buffer. May be %NULL if @outlen is 0.
+ * @outlen: Length of response buffer, in bytes. If the actual
+ * response is longer than @outlen & ~3, it will be truncated
+ * to that length.
+ * @outlen_actual: Pointer through which to return the actual response
+ * length. May be %NULL if this is not needed.
+ *
+ * This function may sleep and therefore must be called in an appropriate
+ * context.
+ *
+ * Return: A negative error code, or zero if successful. The error
+ * code may come from the MCDI response or may indicate a failure
+ * to communicate with the MC. In the former case, the response
+ * will still be copied to @outbuf and *@outlen_actual will be
+ * set accordingly. In the latter case, *@outlen_actual will be
+ * set to zero.
+ */
int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd,
const efx_dword_t *inbuf, size_t inlen,
efx_dword_t *outbuf, size_t outlen,
size_t *outlen_actual)
{
- return _efx_mcdi_rpc(efx, cmd, inbuf, inlen, outbuf, outlen,
- outlen_actual, false);
+ return _efx_mcdi_rpc_evb_retry(efx, cmd, inbuf, inlen, outbuf, outlen,
+ outlen_actual, false);
}
/* Normally, on receiving an error code in the MCDI response,
@@ -744,8 +939,8 @@ int efx_mcdi_rpc_quiet(struct efx_nic *efx, unsigned cmd,
efx_dword_t *outbuf, size_t outlen,
size_t *outlen_actual)
{
- return _efx_mcdi_rpc(efx, cmd, inbuf, inlen, outbuf, outlen,
- outlen_actual, true);
+ return _efx_mcdi_rpc_evb_retry(efx, cmd, inbuf, inlen, outbuf, outlen,
+ outlen_actual, true);
}
int efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd,
@@ -866,7 +1061,7 @@ int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
size_t *outlen_actual)
{
return _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen,
- outlen_actual, false);
+ outlen_actual, false, NULL, NULL);
}
int efx_mcdi_rpc_finish_quiet(struct efx_nic *efx, unsigned cmd, size_t inlen,
@@ -874,7 +1069,7 @@ int efx_mcdi_rpc_finish_quiet(struct efx_nic *efx, unsigned cmd, size_t inlen,
size_t *outlen_actual)
{
return _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen,
- outlen_actual, true);
+ outlen_actual, true, NULL, NULL);
}
void efx_mcdi_display_error(struct efx_nic *efx, unsigned cmd,
@@ -887,9 +1082,10 @@ void efx_mcdi_display_error(struct efx_nic *efx, unsigned cmd,
code = MCDI_DWORD(outbuf, ERR_CODE);
if (outlen >= MC_CMD_ERR_ARG_OFST + 4)
err_arg = MCDI_DWORD(outbuf, ERR_ARG);
- netif_err(efx, hw, efx->net_dev,
- "MC command 0x%x inlen %d failed rc=%d (raw=%d) arg=%d\n",
- cmd, (int)inlen, rc, code, err_arg);
+ netif_printk(efx, hw, rc == -EPERM ? KERN_DEBUG : KERN_ERR,
+ efx->net_dev,
+ "MC command 0x%x inlen %zu failed rc=%d (raw=%d) arg=%d\n",
+ cmd, inlen, rc, code, err_arg);
}
/* Switch to polled MCDI completions. This can be called in various
@@ -1014,8 +1210,13 @@ static void efx_mcdi_ev_death(struct efx_nic *efx, int rc)
* receiving a REBOOT event after posting the MCDI
* request. Did the mc reboot before or after the copyout? The
* best we can do always is just return failure.
+ *
+ * If there is an outstanding proxy response expected it is not going
+ * to arrive. We should thus abort it.
*/
spin_lock(&mcdi->iface_lock);
+ efx_mcdi_proxy_abort(mcdi);
+
if (efx_mcdi_complete_sync(mcdi)) {
if (mcdi->mode == MCDI_MODE_EVENTS) {
mcdi->resprc = rc;
@@ -1063,6 +1264,8 @@ static void efx_mcdi_ev_bist(struct efx_nic *efx)
spin_lock(&mcdi->iface_lock);
efx->mc_bist_for_other_fn = true;
+ efx_mcdi_proxy_abort(mcdi);
+
if (efx_mcdi_complete_sync(mcdi)) {
if (mcdi->mode == MCDI_MODE_EVENTS) {
mcdi->resprc = -EIO;
@@ -1171,6 +1374,11 @@ void efx_mcdi_process_event(struct efx_channel *channel,
EFX_QWORD_VAL(*event));
efx_schedule_reset(efx, RESET_TYPE_DMA_ERROR);
break;
+ case MCDI_EVENT_CODE_PROXY_RESPONSE:
+ efx_mcdi_ev_proxy_response(efx,
+ MCDI_EVENT_FIELD(*event, PROXY_RESPONSE_HANDLE),
+ MCDI_EVENT_FIELD(*event, PROXY_RESPONSE_RC));
+ break;
default:
netif_err(efx, hw, efx->net_dev, "Unknown MCDI event 0x%x\n",
code);
diff --git a/drivers/net/ethernet/sfc/mcdi.h b/drivers/net/ethernet/sfc/mcdi.h
index 025d504c472b..c9aeb0701c9a 100644
--- a/drivers/net/ethernet/sfc/mcdi.h
+++ b/drivers/net/ethernet/sfc/mcdi.h
@@ -17,6 +17,8 @@
* @MCDI_STATE_RUNNING_SYNC: There is a synchronous MCDI request pending.
* Only the thread that moved into this state is allowed to move out of it.
* @MCDI_STATE_RUNNING_ASYNC: There is an asynchronous MCDI request pending.
+ * @MCDI_STATE_PROXY_WAIT: An MCDI request has completed with a response that
+ * indicates we must wait for a proxy try again message.
* @MCDI_STATE_COMPLETED: An MCDI request has completed, but the owning thread
* has not yet consumed the result. For all other threads, equivalent to
* %MCDI_STATE_RUNNING.
@@ -25,6 +27,7 @@ enum efx_mcdi_state {
MCDI_STATE_QUIESCENT,
MCDI_STATE_RUNNING_SYNC,
MCDI_STATE_RUNNING_ASYNC,
+ MCDI_STATE_PROXY_WAIT,
MCDI_STATE_COMPLETED,
};
@@ -60,6 +63,9 @@ enum efx_mcdi_mode {
* @async_timer: Timer for asynchronous request timeout
* @logging_buffer: buffer that may be used to build MCDI tracing messages
* @logging_enabled: whether to trace MCDI
+ * @proxy_rx_handle: Most recently received proxy authorisation handle
+ * @proxy_rx_status: Status of most recent proxy authorisation
+ * @proxy_rx_wq: Wait queue for updates to proxy_rx_handle
*/
struct efx_mcdi_iface {
struct efx_nic *efx;
@@ -71,6 +77,7 @@ struct efx_mcdi_iface {
unsigned int credits;
unsigned int seqno;
int resprc;
+ int resprc_raw;
size_t resp_hdr_len;
size_t resp_data_len;
spinlock_t async_lock;
@@ -80,6 +87,9 @@ struct efx_mcdi_iface {
char *logging_buffer;
bool logging_enabled;
#endif
+ unsigned int proxy_rx_handle;
+ int proxy_rx_status;
+ wait_queue_head_t proxy_rx_wq;
};
struct efx_mcdi_mon {
diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h
index a8ddd122f685..38c422321cda 100644
--- a/drivers/net/ethernet/sfc/net_driver.h
+++ b/drivers/net/ethernet/sfc/net_driver.h
@@ -182,6 +182,7 @@ struct efx_tx_buffer {
*
* @efx: The associated Efx NIC
* @queue: DMA queue number
+ * @tso_version: Version of TSO in use for this queue.
* @channel: The associated channel
* @core_txq: The networking core TX queue structure
* @buffer: The software buffer ring
@@ -228,6 +229,7 @@ struct efx_tx_queue {
/* Members which don't change on the fast path */
struct efx_nic *efx ____cacheline_aligned_in_smp;
unsigned queue;
+ unsigned int tso_version;
struct efx_channel *channel;
struct netdev_queue *core_txq;
struct efx_tx_buffer *buffer;
@@ -1502,8 +1504,9 @@ static inline struct efx_rx_buffer *efx_rx_buffer(struct efx_rx_queue *rx_queue,
* same cycle, the XMAC can miss the IPG altogether. We work around
* this by adding a further 16 bytes.
*/
+#define EFX_FRAME_PAD 16
#define EFX_MAX_FRAME_LEN(mtu) \
- ((((mtu) + ETH_HLEN + VLAN_HLEN + 4/* FCS */ + 7) & ~7) + 16)
+ (ALIGN(((mtu) + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN + EFX_FRAME_PAD), 8))
static inline bool efx_xmit_with_hwtstamp(struct sk_buff *skb)
{
diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c
index 809ea4610a77..8956995b2fe7 100644
--- a/drivers/net/ethernet/sfc/rx.c
+++ b/drivers/net/ethernet/sfc/rx.c
@@ -463,7 +463,6 @@ efx_rx_packet_gro(struct efx_channel *channel, struct efx_rx_buffer *rx_buf,
skb_record_rx_queue(skb, channel->rx_queue.core_index);
- skb_mark_napi_id(skb, &channel->napi_str);
gro_result = napi_gro_frags(napi);
if (gro_result != GRO_DROP)
channel->irq_mod_score += 2;
diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c
index 67f6afaa022f..f7a0ec1bca97 100644
--- a/drivers/net/ethernet/sfc/tx.c
+++ b/drivers/net/ethernet/sfc/tx.c
@@ -1010,13 +1010,17 @@ static void efx_enqueue_unwind(struct efx_tx_queue *tx_queue,
/* Parse the SKB header and initialise state. */
static int tso_start(struct tso_state *st, struct efx_nic *efx,
+ struct efx_tx_queue *tx_queue,
const struct sk_buff *skb)
{
- bool use_opt_desc = efx_nic_rev(efx) >= EFX_REV_HUNT_A0;
struct device *dma_dev = &efx->pci_dev->dev;
unsigned int header_len, in_len;
+ bool use_opt_desc = false;
dma_addr_t dma_addr;
+ if (tx_queue->tso_version == 1)
+ use_opt_desc = true;
+
st->ip_off = skb_network_header(skb) - skb->data;
st->tcp_off = skb_transport_header(skb) - skb->data;
header_len = st->tcp_off + (tcp_hdr(skb)->doff << 2u);
@@ -1271,7 +1275,7 @@ static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue,
/* Find the packet protocol and sanity-check it */
state.protocol = efx_tso_check_protocol(skb);
- rc = tso_start(&state, efx, skb);
+ rc = tso_start(&state, efx, tx_queue, skb);
if (rc)
goto mem_err;
diff --git a/drivers/net/ethernet/sfc/txc43128_phy.c b/drivers/net/ethernet/sfc/txc43128_phy.c
index 3d5ee3259885..194f67d9f3bf 100644
--- a/drivers/net/ethernet/sfc/txc43128_phy.c
+++ b/drivers/net/ethernet/sfc/txc43128_phy.c
@@ -418,7 +418,7 @@ static void txc_reset_logic_mmd(struct efx_nic *efx, int mmd)
val |= (1 << TXC_GLCMD_LMTSWRST_LBN);
efx_mdio_write(efx, mmd, TXC_GLRGS_GLCMD, val);
- while (tries--) {
+ while (--tries) {
val = efx_mdio_read(efx, mmd, TXC_GLRGS_GLCMD);
if (!(val & (1 << TXC_GLCMD_LMTSWRST_LBN)))
break;
diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c
index 219a99b7a631..8af25563f627 100644
--- a/drivers/net/ethernet/smsc/smsc911x.c
+++ b/drivers/net/ethernet/smsc/smsc911x.c
@@ -864,8 +864,8 @@ static int smsc911x_phy_loopbacktest(struct net_device *dev)
for (i = 0; i < 10; i++) {
/* Set PHY to 10/FD, no ANEG, and loopback mode */
- smsc911x_mii_write(phy_dev->bus, phy_dev->addr, MII_BMCR,
- BMCR_LOOPBACK | BMCR_FULLDPLX);
+ smsc911x_mii_write(phy_dev->mdio.bus, phy_dev->mdio.addr,
+ MII_BMCR, BMCR_LOOPBACK | BMCR_FULLDPLX);
/* Enable MAC tx/rx, FD */
spin_lock_irqsave(&pdata->mac_lock, flags);
@@ -893,7 +893,7 @@ static int smsc911x_phy_loopbacktest(struct net_device *dev)
spin_unlock_irqrestore(&pdata->mac_lock, flags);
/* Cancel PHY loopback mode */
- smsc911x_mii_write(phy_dev->bus, phy_dev->addr, MII_BMCR, 0);
+ smsc911x_mii_write(phy_dev->mdio.bus, phy_dev->mdio.addr, MII_BMCR, 0);
smsc911x_reg_write(pdata, TX_CFG, 0);
smsc911x_reg_write(pdata, RX_CFG, 0);
@@ -1021,7 +1021,7 @@ static int smsc911x_mii_probe(struct net_device *dev)
}
SMSC_TRACE(pdata, probe, "PHY: addr %d, phy_id 0x%08X",
- phydev->addr, phydev->phy_id);
+ phydev->mdio.addr, phydev->phy_id);
ret = phy_connect_direct(dev, phydev, &smsc911x_phy_adjust_link,
pdata->config.phy_interface);
@@ -1031,9 +1031,7 @@ static int smsc911x_mii_probe(struct net_device *dev)
return ret;
}
- netdev_info(dev,
- "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
- phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
+ phy_attached_info(phydev);
/* mask with MAC supported features */
phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause |
@@ -1061,7 +1059,7 @@ static int smsc911x_mii_init(struct platform_device *pdev,
struct net_device *dev)
{
struct smsc911x_data *pdata = netdev_priv(dev);
- int err = -ENXIO, i;
+ int err = -ENXIO;
pdata->mii_bus = mdiobus_alloc();
if (!pdata->mii_bus) {
@@ -1075,9 +1073,7 @@ static int smsc911x_mii_init(struct platform_device *pdev,
pdata->mii_bus->priv = pdata;
pdata->mii_bus->read = smsc911x_mii_read;
pdata->mii_bus->write = smsc911x_mii_write;
- pdata->mii_bus->irq = pdata->phy_irq;
- for (i = 0; i < PHY_MAX_ADDR; ++i)
- pdata->mii_bus->irq[i] = PHY_POLL;
+ memcpy(pdata->mii_bus->irq, pdata->phy_irq, sizeof(pdata->mii_bus));
pdata->mii_bus->parent = &pdev->dev;
@@ -1992,7 +1988,8 @@ smsc911x_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs,
}
for (i = 0; i <= 31; i++)
- data[j++] = smsc911x_mii_read(phy_dev->bus, phy_dev->addr, i);
+ data[j++] = smsc911x_mii_read(phy_dev->mdio.bus,
+ phy_dev->mdio.addr, i);
}
static void smsc911x_eeprom_enable_access(struct smsc911x_data *pdata)
diff --git a/drivers/net/ethernet/smsc/smsc9420.c b/drivers/net/ethernet/smsc/smsc9420.c
index 4a90cdae5444..8594b9e8b28b 100644
--- a/drivers/net/ethernet/smsc/smsc9420.c
+++ b/drivers/net/ethernet/smsc/smsc9420.c
@@ -78,7 +78,6 @@ struct smsc9420_pdata {
struct phy_device *phy_dev;
struct mii_bus *mii_bus;
- int phy_irq[PHY_MAX_ADDR];
int last_duplex;
int last_carrier;
};
@@ -316,7 +315,8 @@ smsc9420_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs,
return;
for (i = 0; i <= 31; i++)
- data[j++] = smsc9420_mii_read(phy_dev->bus, phy_dev->addr, i);
+ data[j++] = smsc9420_mii_read(phy_dev->mdio.bus,
+ phy_dev->mdio.addr, i);
}
static void smsc9420_eeprom_enable_access(struct smsc9420_pdata *pd)
@@ -1158,16 +1158,13 @@ static int smsc9420_mii_probe(struct net_device *dev)
BUG_ON(pd->phy_dev);
/* Device only supports internal PHY at address 1 */
- if (!pd->mii_bus->phy_map[1]) {
+ phydev = mdiobus_get_phy(pd->mii_bus, 1);
+ if (!phydev) {
netdev_err(dev, "no PHY found at address 1\n");
return -ENODEV;
}
- phydev = pd->mii_bus->phy_map[1];
- netif_info(pd, probe, pd->dev, "PHY addr %d, phy_id 0x%08X\n",
- phydev->addr, phydev->phy_id);
-
- phydev = phy_connect(dev, dev_name(&phydev->dev),
+ phydev = phy_connect(dev, phydev_name(phydev),
smsc9420_phy_adjust_link, PHY_INTERFACE_MODE_MII);
if (IS_ERR(phydev)) {
@@ -1175,14 +1172,13 @@ static int smsc9420_mii_probe(struct net_device *dev)
return PTR_ERR(phydev);
}
- netdev_info(dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
- phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
-
/* mask with MAC supported features */
phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause |
SUPPORTED_Asym_Pause);
phydev->advertising = phydev->supported;
+ phy_attached_info(phydev);
+
pd->phy_dev = phydev;
pd->last_duplex = -1;
pd->last_carrier = -1;
@@ -1193,7 +1189,7 @@ static int smsc9420_mii_probe(struct net_device *dev)
static int smsc9420_mii_init(struct net_device *dev)
{
struct smsc9420_pdata *pd = netdev_priv(dev);
- int err = -ENXIO, i;
+ int err = -ENXIO;
pd->mii_bus = mdiobus_alloc();
if (!pd->mii_bus) {
@@ -1206,9 +1202,6 @@ static int smsc9420_mii_init(struct net_device *dev)
pd->mii_bus->priv = pd;
pd->mii_bus->read = smsc9420_mii_read;
pd->mii_bus->write = smsc9420_mii_write;
- pd->mii_bus->irq = pd->phy_irq;
- for (i = 0; i < PHY_MAX_ADDR; ++i)
- pd->mii_bus->irq[i] = PHY_POLL;
/* Mask all PHYs except ID 1 (internal) */
pd->mii_bus->phy_mask = ~(1 << 1);
diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
index 623c6ed8764a..1e19c8fd8b82 100644
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
@@ -137,6 +137,31 @@ struct stmmac_extra_stats {
unsigned long pcs_link;
unsigned long pcs_duplex;
unsigned long pcs_speed;
+ /* debug register */
+ unsigned long mtl_tx_status_fifo_full;
+ unsigned long mtl_tx_fifo_not_empty;
+ unsigned long mmtl_fifo_ctrl;
+ unsigned long mtl_tx_fifo_read_ctrl_write;
+ unsigned long mtl_tx_fifo_read_ctrl_wait;
+ unsigned long mtl_tx_fifo_read_ctrl_read;
+ unsigned long mtl_tx_fifo_read_ctrl_idle;
+ unsigned long mac_tx_in_pause;
+ unsigned long mac_tx_frame_ctrl_xfer;
+ unsigned long mac_tx_frame_ctrl_idle;
+ unsigned long mac_tx_frame_ctrl_wait;
+ unsigned long mac_tx_frame_ctrl_pause;
+ unsigned long mac_gmii_tx_proto_engine;
+ unsigned long mtl_rx_fifo_fill_level_full;
+ unsigned long mtl_rx_fifo_fill_above_thresh;
+ unsigned long mtl_rx_fifo_fill_below_thresh;
+ unsigned long mtl_rx_fifo_fill_level_empty;
+ unsigned long mtl_rx_fifo_read_ctrl_flush;
+ unsigned long mtl_rx_fifo_read_ctrl_read_data;
+ unsigned long mtl_rx_fifo_read_ctrl_status;
+ unsigned long mtl_rx_fifo_read_ctrl_idle;
+ unsigned long mtl_rx_fifo_ctrl_active;
+ unsigned long mac_rx_frame_ctrl_fifo;
+ unsigned long mac_gmii_rx_proto_engine;
};
/* CSR Frequency Access Defines*/
@@ -408,12 +433,13 @@ struct stmmac_ops {
void (*set_eee_pls)(struct mac_device_info *hw, int link);
void (*ctrl_ane)(struct mac_device_info *hw, bool restart);
void (*get_adv)(struct mac_device_info *hw, struct rgmii_adv *adv);
+ void (*debug)(void __iomem *ioaddr, struct stmmac_extra_stats *x);
};
/* PTP and HW Timer helpers */
struct stmmac_hwtimestamp {
void (*config_hw_tstamping) (void __iomem *ioaddr, u32 data);
- void (*config_sub_second_increment) (void __iomem *ioaddr);
+ u32 (*config_sub_second_increment) (void __iomem *ioaddr, u32 clk_rate);
int (*init_systime) (void __iomem *ioaddr, u32 sec, u32 nsec);
int (*config_addend) (void __iomem *ioaddr, u32 addend);
int (*adjust_systime) (void __iomem *ioaddr, u32 sec, u32 nsec,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
index 82de68b1a452..36d3355f2fb0 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
@@ -198,19 +198,19 @@ static int ipq806x_gmac_set_speed(struct ipq806x_gmac *gmac, unsigned int speed)
return 0;
}
-static void *ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac)
+static int ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac)
{
struct device *dev = &gmac->pdev->dev;
gmac->phy_mode = of_get_phy_mode(dev->of_node);
if (gmac->phy_mode < 0) {
dev_err(dev, "missing phy mode property\n");
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
}
if (of_property_read_u32(dev->of_node, "qcom,id", &gmac->id) < 0) {
dev_err(dev, "missing qcom id property\n");
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
}
/* The GMACs are called 1 to 4 in the documentation, but to simplify the
@@ -219,13 +219,13 @@ static void *ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac)
*/
if (gmac->id < 0 || gmac->id > 3) {
dev_err(dev, "invalid gmac id\n");
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
}
gmac->core_clk = devm_clk_get(dev, "stmmaceth");
if (IS_ERR(gmac->core_clk)) {
dev_err(dev, "missing stmmaceth clk property\n");
- return gmac->core_clk;
+ return PTR_ERR(gmac->core_clk);
}
clk_set_rate(gmac->core_clk, 266000000);
@@ -234,18 +234,16 @@ static void *ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac)
"qcom,nss-common");
if (IS_ERR(gmac->nss_common)) {
dev_err(dev, "missing nss-common node\n");
- return gmac->nss_common;
+ return PTR_ERR(gmac->nss_common);
}
/* Setup the register map for the qsgmii csr registers */
gmac->qsgmii_csr = syscon_regmap_lookup_by_phandle(dev->of_node,
"qcom,qsgmii-csr");
- if (IS_ERR(gmac->qsgmii_csr)) {
+ if (IS_ERR(gmac->qsgmii_csr))
dev_err(dev, "missing qsgmii-csr node\n");
- return gmac->qsgmii_csr;
- }
- return NULL;
+ return PTR_ERR_OR_ZERO(gmac->qsgmii_csr);
}
static void ipq806x_gmac_fix_mac_speed(void *priv, unsigned int speed)
@@ -262,7 +260,7 @@ static int ipq806x_gmac_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct ipq806x_gmac *gmac;
int val;
- void *err;
+ int err;
val = stmmac_get_platform_resources(pdev, &stmmac_res);
if (val)
@@ -279,9 +277,9 @@ static int ipq806x_gmac_probe(struct platform_device *pdev)
gmac->pdev = pdev;
err = ipq806x_gmac_of_parse(gmac);
- if (IS_ERR(err)) {
+ if (err) {
dev_err(dev, "device tree parsing error\n");
- return PTR_ERR(err);
+ return err;
}
regmap_write(gmac->qsgmii_csr, QSGMII_PCS_CAL_LCKDT_CTL,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
index 401383b252a8..f0d797ab74d8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
@@ -32,6 +32,7 @@
#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII 0x2
#define SYSMGR_EMACGRP_CTRL_PHYSEL_WIDTH 2
#define SYSMGR_EMACGRP_CTRL_PHYSEL_MASK 0x00000003
+#define SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK 0x00000010
#define EMAC_SPLITTER_CTRL_REG 0x0
#define EMAC_SPLITTER_CTRL_SPEED_MASK 0x3
@@ -47,6 +48,7 @@ struct socfpga_dwmac {
struct regmap *sys_mgr_base_addr;
struct reset_control *stmmac_rst;
void __iomem *splitter_base;
+ bool f2h_ptp_ref_clk;
};
static void socfpga_dwmac_fix_mac_speed(void *priv, unsigned int speed)
@@ -116,6 +118,8 @@ static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device *
return -EINVAL;
}
+ dwmac->f2h_ptp_ref_clk = of_property_read_bool(np, "altr,f2h_ptp_ref_clk");
+
np_splitter = of_parse_phandle(np, "altr,emac-splitter", 0);
if (np_splitter) {
if (of_address_to_resource(np_splitter, 0, &res_splitter)) {
@@ -171,6 +175,11 @@ static int socfpga_dwmac_setup(struct socfpga_dwmac *dwmac)
ctrl &= ~(SYSMGR_EMACGRP_CTRL_PHYSEL_MASK << reg_shift);
ctrl |= val << reg_shift;
+ if (dwmac->f2h_ptp_ref_clk)
+ ctrl |= SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK << (reg_shift / 2);
+ else
+ ctrl &= ~(SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK << (reg_shift / 2));
+
regmap_write(sys_mgr_base_addr, reg_offset, ctrl);
return 0;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
index 7f6f4a4fcc70..58c05acc2aab 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
@@ -299,16 +299,17 @@ static int sti_dwmac_parse_data(struct sti_dwmac *dwmac,
if (IS_PHY_IF_MODE_GBIT(dwmac->interface)) {
const char *rs;
+ dwmac->tx_retime_src = TX_RETIME_SRC_CLKGEN;
+
err = of_property_read_string(np, "st,tx-retime-src", &rs);
if (err < 0) {
dev_warn(dev, "Use internal clock source\n");
- dwmac->tx_retime_src = TX_RETIME_SRC_CLKGEN;
- } else if (!strcasecmp(rs, "clk_125")) {
- dwmac->tx_retime_src = TX_RETIME_SRC_CLK_125;
- } else if (!strcasecmp(rs, "txclk")) {
- dwmac->tx_retime_src = TX_RETIME_SRC_TXCLK;
+ } else {
+ if (!strcasecmp(rs, "clk_125"))
+ dwmac->tx_retime_src = TX_RETIME_SRC_CLK_125;
+ else if (!strcasecmp(rs, "txclk"))
+ dwmac->tx_retime_src = TX_RETIME_SRC_TXCLK;
}
-
dwmac->speed = SPEED_1000;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
index 52b8ed9bd87c..adff46375a32 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
@@ -153,7 +153,11 @@ static int sun7i_gmac_probe(struct platform_device *pdev)
if (ret)
return ret;
- return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+ ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+ if (ret)
+ sun7i_gmac_exit(pdev, plat_dat->bsp_priv);
+
+ return ret;
}
static const struct of_device_id sun7i_dwmac_match[] = {
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
index b3fe0575ff6b..8831a053ac13 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
@@ -34,6 +34,7 @@
#define GMAC_FLOW_CTRL 0x00000018 /* Flow Control */
#define GMAC_VLAN_TAG 0x0000001c /* VLAN Tag */
#define GMAC_VERSION 0x00000020 /* GMAC CORE Version */
+#define GMAC_DEBUG 0x00000024 /* GMAC debug register */
#define GMAC_WAKEUP_FILTER 0x00000028 /* Wake-up Frame Filter */
#define GMAC_INT_STATUS 0x00000038 /* interrupt status register */
@@ -177,6 +178,47 @@ enum inter_frame_gap {
#define GMAC_FLOW_CTRL_TFE 0x00000002 /* Tx Flow Control Enable */
#define GMAC_FLOW_CTRL_FCB_BPA 0x00000001 /* Flow Control Busy ... */
+/* DEBUG Register defines */
+/* MTL TxStatus FIFO */
+#define GMAC_DEBUG_TXSTSFSTS BIT(25) /* MTL TxStatus FIFO Full Status */
+#define GMAC_DEBUG_TXFSTS BIT(24) /* MTL Tx FIFO Not Empty Status */
+#define GMAC_DEBUG_TWCSTS BIT(22) /* MTL Tx FIFO Write Controller */
+/* MTL Tx FIFO Read Controller Status */
+#define GMAC_DEBUG_TRCSTS_MASK GENMASK(21, 20)
+#define GMAC_DEBUG_TRCSTS_SHIFT 20
+#define GMAC_DEBUG_TRCSTS_IDLE 0
+#define GMAC_DEBUG_TRCSTS_READ 1
+#define GMAC_DEBUG_TRCSTS_TXW 2
+#define GMAC_DEBUG_TRCSTS_WRITE 3
+#define GMAC_DEBUG_TXPAUSED BIT(19) /* MAC Transmitter in PAUSE */
+/* MAC Transmit Frame Controller Status */
+#define GMAC_DEBUG_TFCSTS_MASK GENMASK(18, 17)
+#define GMAC_DEBUG_TFCSTS_SHIFT 17
+#define GMAC_DEBUG_TFCSTS_IDLE 0
+#define GMAC_DEBUG_TFCSTS_WAIT 1
+#define GMAC_DEBUG_TFCSTS_GEN_PAUSE 2
+#define GMAC_DEBUG_TFCSTS_XFER 3
+/* MAC GMII or MII Transmit Protocol Engine Status */
+#define GMAC_DEBUG_TPESTS BIT(16)
+#define GMAC_DEBUG_RXFSTS_MASK GENMASK(9, 8) /* MTL Rx FIFO Fill-level */
+#define GMAC_DEBUG_RXFSTS_SHIFT 8
+#define GMAC_DEBUG_RXFSTS_EMPTY 0
+#define GMAC_DEBUG_RXFSTS_BT 1
+#define GMAC_DEBUG_RXFSTS_AT 2
+#define GMAC_DEBUG_RXFSTS_FULL 3
+#define GMAC_DEBUG_RRCSTS_MASK GENMASK(6, 5) /* MTL Rx FIFO Read Controller */
+#define GMAC_DEBUG_RRCSTS_SHIFT 5
+#define GMAC_DEBUG_RRCSTS_IDLE 0
+#define GMAC_DEBUG_RRCSTS_RDATA 1
+#define GMAC_DEBUG_RRCSTS_RSTAT 2
+#define GMAC_DEBUG_RRCSTS_FLUSH 3
+#define GMAC_DEBUG_RWCSTS BIT(4) /* MTL Rx FIFO Write Controller Active */
+/* MAC Receive Frame Controller FIFO Status */
+#define GMAC_DEBUG_RFCFCSTS_MASK GENMASK(2, 1)
+#define GMAC_DEBUG_RFCFCSTS_SHIFT 1
+/* MAC GMII or MII Receive Protocol Engine Status */
+#define GMAC_DEBUG_RPESTS BIT(0)
+
/*--- DMA BLOCK defines ---*/
/* DMA Bus Mode register defines */
#define DMA_BUS_MODE_SFT_RESET 0x00000001 /* Software Reset */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
index 371a669d69fd..c2941172f6d1 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
@@ -397,6 +397,80 @@ static void dwmac1000_get_adv(struct mac_device_info *hw, struct rgmii_adv *adv)
adv->lp_pause = (value & GMAC_ANE_PSE) >> GMAC_ANE_PSE_SHIFT;
}
+static void dwmac1000_debug(void __iomem *ioaddr, struct stmmac_extra_stats *x)
+{
+ u32 value = readl(ioaddr + GMAC_DEBUG);
+
+ if (value & GMAC_DEBUG_TXSTSFSTS)
+ x->mtl_tx_status_fifo_full++;
+ if (value & GMAC_DEBUG_TXFSTS)
+ x->mtl_tx_fifo_not_empty++;
+ if (value & GMAC_DEBUG_TWCSTS)
+ x->mmtl_fifo_ctrl++;
+ if (value & GMAC_DEBUG_TRCSTS_MASK) {
+ u32 trcsts = (value & GMAC_DEBUG_TRCSTS_MASK)
+ >> GMAC_DEBUG_TRCSTS_SHIFT;
+ if (trcsts == GMAC_DEBUG_TRCSTS_WRITE)
+ x->mtl_tx_fifo_read_ctrl_write++;
+ else if (trcsts == GMAC_DEBUG_TRCSTS_TXW)
+ x->mtl_tx_fifo_read_ctrl_wait++;
+ else if (trcsts == GMAC_DEBUG_TRCSTS_READ)
+ x->mtl_tx_fifo_read_ctrl_read++;
+ else
+ x->mtl_tx_fifo_read_ctrl_idle++;
+ }
+ if (value & GMAC_DEBUG_TXPAUSED)
+ x->mac_tx_in_pause++;
+ if (value & GMAC_DEBUG_TFCSTS_MASK) {
+ u32 tfcsts = (value & GMAC_DEBUG_TFCSTS_MASK)
+ >> GMAC_DEBUG_TFCSTS_SHIFT;
+
+ if (tfcsts == GMAC_DEBUG_TFCSTS_XFER)
+ x->mac_tx_frame_ctrl_xfer++;
+ else if (tfcsts == GMAC_DEBUG_TFCSTS_GEN_PAUSE)
+ x->mac_tx_frame_ctrl_pause++;
+ else if (tfcsts == GMAC_DEBUG_TFCSTS_WAIT)
+ x->mac_tx_frame_ctrl_wait++;
+ else
+ x->mac_tx_frame_ctrl_idle++;
+ }
+ if (value & GMAC_DEBUG_TPESTS)
+ x->mac_gmii_tx_proto_engine++;
+ if (value & GMAC_DEBUG_RXFSTS_MASK) {
+ u32 rxfsts = (value & GMAC_DEBUG_RXFSTS_MASK)
+ >> GMAC_DEBUG_RRCSTS_SHIFT;
+
+ if (rxfsts == GMAC_DEBUG_RXFSTS_FULL)
+ x->mtl_rx_fifo_fill_level_full++;
+ else if (rxfsts == GMAC_DEBUG_RXFSTS_AT)
+ x->mtl_rx_fifo_fill_above_thresh++;
+ else if (rxfsts == GMAC_DEBUG_RXFSTS_BT)
+ x->mtl_rx_fifo_fill_below_thresh++;
+ else
+ x->mtl_rx_fifo_fill_level_empty++;
+ }
+ if (value & GMAC_DEBUG_RRCSTS_MASK) {
+ u32 rrcsts = (value & GMAC_DEBUG_RRCSTS_MASK) >>
+ GMAC_DEBUG_RRCSTS_SHIFT;
+
+ if (rrcsts == GMAC_DEBUG_RRCSTS_FLUSH)
+ x->mtl_rx_fifo_read_ctrl_flush++;
+ else if (rrcsts == GMAC_DEBUG_RRCSTS_RSTAT)
+ x->mtl_rx_fifo_read_ctrl_read_data++;
+ else if (rrcsts == GMAC_DEBUG_RRCSTS_RDATA)
+ x->mtl_rx_fifo_read_ctrl_status++;
+ else
+ x->mtl_rx_fifo_read_ctrl_idle++;
+ }
+ if (value & GMAC_DEBUG_RWCSTS)
+ x->mtl_rx_fifo_ctrl_active++;
+ if (value & GMAC_DEBUG_RFCFCSTS_MASK)
+ x->mac_rx_frame_ctrl_fifo = (value & GMAC_DEBUG_RFCFCSTS_MASK)
+ >> GMAC_DEBUG_RFCFCSTS_SHIFT;
+ if (value & GMAC_DEBUG_RPESTS)
+ x->mac_gmii_rx_proto_engine++;
+}
+
static const struct stmmac_ops dwmac1000_ops = {
.core_init = dwmac1000_core_init,
.rx_ipc = dwmac1000_rx_ipc_enable,
@@ -413,6 +487,7 @@ static const struct stmmac_ops dwmac1000_ops = {
.set_eee_pls = dwmac1000_set_eee_pls,
.ctrl_ane = dwmac1000_ctrl_ane,
.get_adv = dwmac1000_get_adv,
+ .debug = dwmac1000_debug,
};
struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr, int mcbins,
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
index 2e51b816a7e8..4c6486cc80fb 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
@@ -136,6 +136,31 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
STMMAC_STAT(irq_pcs_ane_n),
STMMAC_STAT(irq_pcs_link_n),
STMMAC_STAT(irq_rgmii_n),
+ /* DEBUG */
+ STMMAC_STAT(mtl_tx_status_fifo_full),
+ STMMAC_STAT(mtl_tx_fifo_not_empty),
+ STMMAC_STAT(mmtl_fifo_ctrl),
+ STMMAC_STAT(mtl_tx_fifo_read_ctrl_write),
+ STMMAC_STAT(mtl_tx_fifo_read_ctrl_wait),
+ STMMAC_STAT(mtl_tx_fifo_read_ctrl_read),
+ STMMAC_STAT(mtl_tx_fifo_read_ctrl_idle),
+ STMMAC_STAT(mac_tx_in_pause),
+ STMMAC_STAT(mac_tx_frame_ctrl_xfer),
+ STMMAC_STAT(mac_tx_frame_ctrl_idle),
+ STMMAC_STAT(mac_tx_frame_ctrl_wait),
+ STMMAC_STAT(mac_tx_frame_ctrl_pause),
+ STMMAC_STAT(mac_gmii_tx_proto_engine),
+ STMMAC_STAT(mtl_rx_fifo_fill_level_full),
+ STMMAC_STAT(mtl_rx_fifo_fill_above_thresh),
+ STMMAC_STAT(mtl_rx_fifo_fill_below_thresh),
+ STMMAC_STAT(mtl_rx_fifo_fill_level_empty),
+ STMMAC_STAT(mtl_rx_fifo_read_ctrl_flush),
+ STMMAC_STAT(mtl_rx_fifo_read_ctrl_read_data),
+ STMMAC_STAT(mtl_rx_fifo_read_ctrl_status),
+ STMMAC_STAT(mtl_rx_fifo_read_ctrl_idle),
+ STMMAC_STAT(mtl_rx_fifo_ctrl_active),
+ STMMAC_STAT(mac_rx_frame_ctrl_fifo),
+ STMMAC_STAT(mac_gmii_rx_proto_engine),
};
#define STMMAC_STATS_LEN ARRAY_SIZE(stmmac_gstrings_stats)
@@ -497,6 +522,11 @@ static void stmmac_get_ethtool_stats(struct net_device *dev,
if (val)
priv->xstats.phy_eee_wakeup_error_n = val;
}
+
+ if ((priv->hw->mac->debug) &&
+ (priv->synopsys_id >= DWMAC_CORE_3_50))
+ priv->hw->mac->debug(priv->ioaddr,
+ (void *)&priv->xstats);
}
for (i = 0; i < STMMAC_STATS_LEN; i++) {
char *p = (char *)priv + stmmac_gstrings_stats[i].stat_offset;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
index 76ad214b4036..a77f68918010 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
@@ -33,22 +33,25 @@ static void stmmac_config_hw_tstamping(void __iomem *ioaddr, u32 data)
writel(data, ioaddr + PTP_TCR);
}
-static void stmmac_config_sub_second_increment(void __iomem *ioaddr)
+static u32 stmmac_config_sub_second_increment(void __iomem *ioaddr,
+ u32 ptp_clock)
{
u32 value = readl(ioaddr + PTP_TCR);
unsigned long data;
/* Convert the ptp_clock to nano second
- * formula = (1/ptp_clock) * 1000000000
+ * formula = (2/ptp_clock) * 1000000000
* where, ptp_clock = 50MHz.
*/
- data = (1000000000ULL / 50000000);
+ data = (2000000000ULL / ptp_clock);
/* 0.465ns accuracy */
if (!(value & PTP_TCR_TSCTRLSSR))
data = (data * 1000) / 465;
writel(data, ioaddr + PTP_SSIR);
+
+ return data;
}
static int stmmac_init_systime(void __iomem *ioaddr, u32 sec, u32 nsec)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 64d8aa4e0cad..c21015b68097 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -53,6 +53,7 @@
#include "stmmac.h"
#include <linux/reset.h>
#include <linux/of_mdio.h>
+#include "dwmac1000.h"
#define STMMAC_ALIGN(x) L1_CACHE_ALIGN(x)
@@ -435,6 +436,7 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
u32 ts_master_en = 0;
u32 ts_event_en = 0;
u32 value = 0;
+ u32 sec_inc;
if (!(priv->dma_cap.time_stamp || priv->adv_ts)) {
netdev_alert(priv->dev, "No support for HW time stamping\n");
@@ -598,24 +600,19 @@ static int stmmac_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
tstamp_all | ptp_v2 | ptp_over_ethernet |
ptp_over_ipv6_udp | ptp_over_ipv4_udp | ts_event_en |
ts_master_en | snap_type_sel);
-
priv->hw->ptp->config_hw_tstamping(priv->ioaddr, value);
/* program Sub Second Increment reg */
- priv->hw->ptp->config_sub_second_increment(priv->ioaddr);
+ sec_inc = priv->hw->ptp->config_sub_second_increment(
+ priv->ioaddr, priv->clk_ptp_rate);
+ temp = div_u64(1000000000ULL, sec_inc);
/* calculate default added value:
* formula is :
* addend = (2^32)/freq_div_ratio;
- * where, freq_div_ratio = clk_ptp_ref_i/50MHz
- * hence, addend = ((2^32) * 50MHz)/clk_ptp_ref_i;
- * NOTE: clk_ptp_ref_i should be >= 50MHz to
- * achieve 20ns accuracy.
- *
- * 2^x * y == (y << x), hence
- * 2^32 * 50000000 ==> (50000000 << 32)
+ * where, freq_div_ratio = 1e9ns/sec_inc
*/
- temp = (u64) (50000000ULL << 32);
+ temp = (u64)(temp << 32);
priv->default_addend = div_u64(temp, priv->clk_ptp_rate);
priv->hw->ptp->config_addend(priv->ioaddr,
priv->default_addend);
@@ -2232,6 +2229,12 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit)
frame_len = priv->hw->desc->get_rx_frame_len(p, coe);
+ /* check if frame_len fits the preallocated memory */
+ if (frame_len > priv->dma_buf_sz) {
+ priv->dev->stats.rx_length_errors++;
+ break;
+ }
+
/* ACS is set; GMAC core strips PAD/FCS for IEEE 802.3
* Type frames (LLC/LLC-SNAP)
*/
@@ -2396,7 +2399,7 @@ static netdev_features_t stmmac_fix_features(struct net_device *dev,
features &= ~NETIF_F_RXCSUM;
if (!priv->plat->tx_coe)
- features &= ~NETIF_F_ALL_CSUM;
+ features &= ~NETIF_F_CSUM_MASK;
/* Some GMAC devices have a bugged Jumbo frame support that
* needs to have the Tx COE disabled for oversized frames
@@ -2404,7 +2407,7 @@ static netdev_features_t stmmac_fix_features(struct net_device *dev,
* the TX csum insertionin the TDES and not use SF.
*/
if (priv->plat->bugged_jumbo && (dev->mtu > ETH_DATA_LEN))
- features &= ~NETIF_F_ALL_CSUM;
+ features &= ~NETIF_F_CSUM_MASK;
return features;
}
@@ -3040,8 +3043,6 @@ int stmmac_suspend(struct net_device *ndev)
priv->hw->dma->stop_tx(priv->ioaddr);
priv->hw->dma->stop_rx(priv->ioaddr);
- stmmac_clear_descriptors(priv);
-
/* Enable Power down mode by programming the PMT regs */
if (device_may_wakeup(priv->device)) {
priv->hw->mac->pmt(priv->hw, priv->wolopts);
@@ -3099,9 +3100,15 @@ int stmmac_resume(struct net_device *ndev)
netif_device_attach(ndev);
- init_dma_desc_rings(ndev, GFP_ATOMIC);
+ priv->cur_rx = 0;
+ priv->dirty_rx = 0;
+ priv->dirty_tx = 0;
+ priv->cur_tx = 0;
+ stmmac_clear_descriptors(priv);
+
stmmac_hw_setup(ndev, false);
stmmac_init_tx_coalesce(priv);
+ stmmac_set_rx_mode(ndev);
napi_enable(&priv->napi);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
index ebf6abc4853f..0faf16336035 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
@@ -29,7 +29,7 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
-
+#include <linux/of_mdio.h>
#include <asm/io.h>
#include "stmmac.h"
@@ -138,7 +138,6 @@ int stmmac_mdio_reset(struct mii_bus *bus)
#ifdef CONFIG_OF
if (priv->device->of_node) {
- int reset_gpio, active_low;
if (data->reset_gpio < 0) {
struct device_node *np = priv->device->of_node;
@@ -154,24 +153,23 @@ int stmmac_mdio_reset(struct mii_bus *bus)
"snps,reset-active-low");
of_property_read_u32_array(np,
"snps,reset-delays-us", data->delays, 3);
- }
- reset_gpio = data->reset_gpio;
- active_low = data->active_low;
+ if (gpio_request(data->reset_gpio, "mdio-reset"))
+ return 0;
+ }
- if (!gpio_request(reset_gpio, "mdio-reset")) {
- gpio_direction_output(reset_gpio, active_low ? 1 : 0);
- if (data->delays[0])
- msleep(DIV_ROUND_UP(data->delays[0], 1000));
+ gpio_direction_output(data->reset_gpio,
+ data->active_low ? 1 : 0);
+ if (data->delays[0])
+ msleep(DIV_ROUND_UP(data->delays[0], 1000));
- gpio_set_value(reset_gpio, active_low ? 0 : 1);
- if (data->delays[1])
- msleep(DIV_ROUND_UP(data->delays[1], 1000));
+ gpio_set_value(data->reset_gpio, data->active_low ? 0 : 1);
+ if (data->delays[1])
+ msleep(DIV_ROUND_UP(data->delays[1], 1000));
- gpio_set_value(reset_gpio, active_low ? 1 : 0);
- if (data->delays[2])
- msleep(DIV_ROUND_UP(data->delays[2], 1000));
- }
+ gpio_set_value(data->reset_gpio, data->active_low ? 1 : 0);
+ if (data->delays[2])
+ msleep(DIV_ROUND_UP(data->delays[2], 1000));
}
#endif
@@ -198,25 +196,37 @@ int stmmac_mdio_register(struct net_device *ndev)
{
int err = 0;
struct mii_bus *new_bus;
- int *irqlist;
struct stmmac_priv *priv = netdev_priv(ndev);
struct stmmac_mdio_bus_data *mdio_bus_data = priv->plat->mdio_bus_data;
int addr, found;
+ struct device_node *mdio_node = NULL;
+ struct device_node *child_node = NULL;
if (!mdio_bus_data)
return 0;
+ if (IS_ENABLED(CONFIG_OF)) {
+ for_each_child_of_node(priv->device->of_node, child_node) {
+ if (of_device_is_compatible(child_node,
+ "snps,dwmac-mdio")) {
+ mdio_node = child_node;
+ break;
+ }
+ }
+
+ if (mdio_node) {
+ netdev_dbg(ndev, "FOUND MDIO subnode\n");
+ } else {
+ netdev_warn(ndev, "No MDIO subnode found\n");
+ }
+ }
+
new_bus = mdiobus_alloc();
if (new_bus == NULL)
return -ENOMEM;
- if (mdio_bus_data->irqs) {
- irqlist = mdio_bus_data->irqs;
- } else {
- for (addr = 0; addr < PHY_MAX_ADDR; addr++)
- priv->mii_irq[addr] = PHY_POLL;
- irqlist = priv->mii_irq;
- }
+ if (mdio_bus_data->irqs)
+ memcpy(new_bus->irq, mdio_bus_data, sizeof(new_bus->irq));
#ifdef CONFIG_OF
if (priv->device->of_node)
@@ -230,10 +240,13 @@ int stmmac_mdio_register(struct net_device *ndev)
snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s-%x",
new_bus->name, priv->plat->bus_id);
new_bus->priv = ndev;
- new_bus->irq = irqlist;
new_bus->phy_mask = mdio_bus_data->phy_mask;
new_bus->parent = priv->device;
- err = mdiobus_register(new_bus);
+
+ if (mdio_node)
+ err = of_mdiobus_register(new_bus, mdio_node);
+ else
+ err = mdiobus_register(new_bus);
if (err != 0) {
pr_err("%s: Cannot register as MDIO bus\n", new_bus->name);
goto bus_register_fail;
@@ -241,7 +254,7 @@ int stmmac_mdio_register(struct net_device *ndev)
found = 0;
for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
- struct phy_device *phydev = new_bus->phy_map[addr];
+ struct phy_device *phydev = mdiobus_get_phy(new_bus, addr);
if (phydev) {
int act = 0;
char irq_num[4];
@@ -253,7 +266,8 @@ int stmmac_mdio_register(struct net_device *ndev)
*/
if ((mdio_bus_data->irqs == NULL) &&
(mdio_bus_data->probed_phy_irq > 0)) {
- irqlist[addr] = mdio_bus_data->probed_phy_irq;
+ new_bus->irq[addr] =
+ mdio_bus_data->probed_phy_irq;
phydev->irq = mdio_bus_data->probed_phy_irq;
}
@@ -280,13 +294,13 @@ int stmmac_mdio_register(struct net_device *ndev)
}
pr_info("%s: PHY ID %08x at %d IRQ %s (%s)%s\n",
ndev->name, phydev->phy_id, addr,
- irq_str, dev_name(&phydev->dev),
+ irq_str, phydev_name(phydev),
act ? " active" : "");
found = 1;
}
}
- if (!found) {
+ if (!found && !mdio_node) {
pr_warn("%s: No PHY found\n", ndev->name);
mdiobus_unregister(new_bus);
mdiobus_free(new_bus);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index d02691ba3d7f..6a52fa18cbf2 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -146,7 +146,7 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
if (of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr) == 0)
dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n");
- if (plat->phy_node || plat->phy_bus_name)
+ if ((plat->phy_node && !of_phy_is_fixed_link(np)) || plat->phy_bus_name)
plat->mdio_bus_data = NULL;
else
plat->mdio_bus_data =
diff --git a/drivers/net/ethernet/synopsys/dwc_eth_qos.c b/drivers/net/ethernet/synopsys/dwc_eth_qos.c
index 9066d7a8483c..70814b7386b3 100644
--- a/drivers/net/ethernet/synopsys/dwc_eth_qos.c
+++ b/drivers/net/ethernet/synopsys/dwc_eth_qos.c
@@ -972,9 +972,7 @@ static int dwceqos_mii_probe(struct net_device *ndev)
}
if (netif_msg_probe(lp))
- netdev_dbg(lp->ndev,
- "phydev %p, phydev->phy_id 0xa%x, phydev->addr 0x%x\n",
- phydev, phydev->phy_id, phydev->addr);
+ phy_attached_info(phydev);
phydev->supported &= PHY_GBIT_FEATURES;
@@ -983,14 +981,6 @@ static int dwceqos_mii_probe(struct net_device *ndev)
lp->duplex = DUPLEX_UNKNOWN;
lp->phy_dev = phydev;
- if (netif_msg_probe(lp)) {
- netdev_dbg(lp->ndev, "phy_addr 0x%x, phy_id 0x%08x\n",
- lp->phy_dev->addr, lp->phy_dev->phy_id);
-
- netdev_dbg(lp->ndev, "attach [%s] phy driver\n",
- lp->phy_dev->drv->name);
- }
-
return 0;
}
@@ -1230,7 +1220,7 @@ static void dwceqos_enable_mmc_interrupt(struct net_local *lp)
static int dwceqos_mii_init(struct net_local *lp)
{
- int ret = -ENXIO, i;
+ int ret = -ENXIO;
struct resource res;
struct device_node *mdionode;
@@ -1251,24 +1241,14 @@ static int dwceqos_mii_init(struct net_local *lp)
lp->mii_bus->priv = lp;
lp->mii_bus->parent = &lp->ndev->dev;
- lp->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!lp->mii_bus->irq) {
- ret = -ENOMEM;
- goto err_out_free_mdiobus;
- }
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- lp->mii_bus->irq[i] = PHY_POLL;
of_address_to_resource(lp->pdev->dev.of_node, 0, &res);
snprintf(lp->mii_bus->id, MII_BUS_ID_SIZE, "%.8llx",
(unsigned long long)res.start);
if (of_mdiobus_register(lp->mii_bus, mdionode))
- goto err_out_free_mdio_irq;
+ goto err_out_free_mdiobus;
return 0;
-err_out_free_mdio_irq:
- kfree(lp->mii_bus->irq);
err_out_free_mdiobus:
mdiobus_free(lp->mii_bus);
err_out:
@@ -2107,7 +2087,7 @@ static int dwceqos_tx_frags(struct sk_buff *skb, struct net_local *lp,
dd = &lp->tx_descs[lp->tx_next];
/* Set DMA Descriptor fields */
- dd->des0 = dma_handle;
+ dd->des0 = dma_handle + consumed_size;
dd->des1 = 0;
dd->des2 = dma_size;
@@ -2987,7 +2967,6 @@ static int dwceqos_remove(struct platform_device *pdev)
if (lp->phy_dev)
phy_disconnect(lp->phy_dev);
mdiobus_unregister(lp->mii_bus);
- kfree(lp->mii_bus->irq);
mdiobus_free(lp->mii_bus);
unregister_netdev(ndev);
diff --git a/drivers/net/ethernet/ti/cpmac.c b/drivers/net/ethernet/ti/cpmac.c
index 77d26fe286c0..7eef45e6d70a 100644
--- a/drivers/net/ethernet/ti/cpmac.c
+++ b/drivers/net/ethernet/ti/cpmac.c
@@ -316,8 +316,6 @@ static int cpmac_mdio_reset(struct mii_bus *bus)
return 0;
}
-static int mii_irqs[PHY_MAX_ADDR] = { PHY_POLL, };
-
static struct mii_bus *cpmac_mii;
static void cpmac_set_multicast_list(struct net_device *dev)
@@ -1118,7 +1116,7 @@ static int cpmac_probe(struct platform_device *pdev)
for (phy_id = 0; phy_id < PHY_MAX_ADDR; phy_id++) {
if (!(pdata->phy_mask & (1 << phy_id)))
continue;
- if (!cpmac_mii->phy_map[phy_id])
+ if (!mdiobus_get_phy(cpmac_mii, phy_id))
continue;
strncpy(mdio_bus_id, cpmac_mii->id, MII_BUS_ID_SIZE);
break;
@@ -1226,7 +1224,6 @@ int cpmac_init(void)
cpmac_mii->read = cpmac_mdio_read;
cpmac_mii->write = cpmac_mdio_write;
cpmac_mii->reset = cpmac_mdio_reset;
- cpmac_mii->irq = mii_irqs;
cpmac_mii->priv = ioremap(AR7_REGS_MDIO, 256);
diff --git a/drivers/net/ethernet/ti/cpsw-common.c b/drivers/net/ethernet/ti/cpsw-common.c
index c08be62bceba..1562ab4151e1 100644
--- a/drivers/net/ethernet/ti/cpsw-common.c
+++ b/drivers/net/ethernet/ti/cpsw-common.c
@@ -78,6 +78,9 @@ static int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave,
int ti_cm_get_macid(struct device *dev, int slave, u8 *mac_addr)
{
+ if (of_machine_is_compatible("ti,dm8148"))
+ return cpsw_am33xx_cm_get_macid(dev, 0x630, slave, mac_addr);
+
if (of_machine_is_compatible("ti,am33xx"))
return cpsw_am33xx_cm_get_macid(dev, 0x630, slave, mac_addr);
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 48b92c9de12a..42fdfd4d9d4f 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -1159,8 +1159,8 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)
slave->data->phy_id, slave->slave_num);
slave->phy = NULL;
} else {
- dev_info(priv->dev, "phy found : id is : 0x%x\n",
- slave->phy->phy_id);
+ phy_attached_info(slave->phy);
+
phy_start(slave->phy);
/* Configure GMII_SEL register */
@@ -2026,45 +2026,55 @@ static int cpsw_probe_dt(struct cpsw_priv *priv,
for_each_child_of_node(node, slave_node) {
struct cpsw_slave_data *slave_data = data->slave_data + i;
const void *mac_addr = NULL;
- u32 phyid;
int lenp;
const __be32 *parp;
- struct device_node *mdio_node;
- struct platform_device *mdio;
/* This is no slave child node, continue */
if (strcmp(slave_node->name, "slave"))
continue;
priv->phy_node = of_parse_phandle(slave_node, "phy-handle", 0);
+ parp = of_get_property(slave_node, "phy_id", &lenp);
if (of_phy_is_fixed_link(slave_node)) {
- struct phy_device *pd;
+ struct device_node *phy_node;
+ struct phy_device *phy_dev;
+ /* In the case of a fixed PHY, the DT node associated
+ * to the PHY is the Ethernet MAC DT node.
+ */
ret = of_phy_register_fixed_link(slave_node);
if (ret)
return ret;
- pd = of_phy_find_device(slave_node);
- if (!pd)
+ phy_node = of_node_get(slave_node);
+ phy_dev = of_phy_find_device(phy_node);
+ if (!phy_dev)
return -ENODEV;
snprintf(slave_data->phy_id, sizeof(slave_data->phy_id),
- PHY_ID_FMT, pd->bus->id, pd->phy_id);
- goto no_phy_slave;
- }
- parp = of_get_property(slave_node, "phy_id", &lenp);
- if ((parp == NULL) || (lenp != (sizeof(void *) * 2))) {
- dev_err(&pdev->dev, "Missing slave[%d] phy_id property\n", i);
+ PHY_ID_FMT, phy_dev->mdio.bus->id,
+ phy_dev->mdio.addr);
+ } else if (parp) {
+ u32 phyid;
+ struct device_node *mdio_node;
+ struct platform_device *mdio;
+
+ if (lenp != (sizeof(__be32) * 2)) {
+ dev_err(&pdev->dev, "Invalid slave[%d] phy_id property\n", i);
+ goto no_phy_slave;
+ }
+ mdio_node = of_find_node_by_phandle(be32_to_cpup(parp));
+ phyid = be32_to_cpup(parp+1);
+ mdio = of_find_device_by_node(mdio_node);
+ of_node_put(mdio_node);
+ if (!mdio) {
+ dev_err(&pdev->dev, "Missing mdio platform device\n");
+ return -EINVAL;
+ }
+ snprintf(slave_data->phy_id, sizeof(slave_data->phy_id),
+ PHY_ID_FMT, mdio->name, phyid);
+ } else {
+ dev_err(&pdev->dev, "No slave[%d] phy_id or fixed-link property\n", i);
goto no_phy_slave;
}
- mdio_node = of_find_node_by_phandle(be32_to_cpup(parp));
- phyid = be32_to_cpup(parp+1);
- mdio = of_find_device_by_node(mdio_node);
- of_node_put(mdio_node);
- if (!mdio) {
- dev_err(&pdev->dev, "Missing mdio platform device\n");
- return -EINVAL;
- }
- snprintf(slave_data->phy_id, sizeof(slave_data->phy_id),
- PHY_ID_FMT, mdio->name, phyid);
slave_data->phy_if = of_get_phy_mode(slave_node);
if (slave_data->phy_if < 0) {
dev_err(&pdev->dev, "Missing or malformed slave[%d] phy-mode property\n",
@@ -2418,7 +2428,7 @@ static int cpsw_probe(struct platform_device *pdev)
ndev->irq = platform_get_irq(pdev, 1);
if (ndev->irq < 0) {
dev_err(priv->dev, "error getting irq resource\n");
- ret = -ENOENT;
+ ret = ndev->irq;
goto clean_ale_ret;
}
@@ -2439,8 +2449,10 @@ static int cpsw_probe(struct platform_device *pdev)
/* RX IRQ */
irq = platform_get_irq(pdev, 1);
- if (irq < 0)
+ if (irq < 0) {
+ ret = irq;
goto clean_ale_ret;
+ }
priv->irqs_table[0] = irq;
ret = devm_request_irq(&pdev->dev, irq, cpsw_rx_interrupt,
@@ -2452,8 +2464,10 @@ static int cpsw_probe(struct platform_device *pdev)
/* TX IRQ */
irq = platform_get_irq(pdev, 2);
- if (irq < 0)
+ if (irq < 0) {
+ ret = irq;
goto clean_ale_ret;
+ }
priv->irqs_table[1] = irq;
ret = devm_request_irq(&pdev->dev, irq, cpsw_tx_interrupt,
@@ -2469,7 +2483,7 @@ static int cpsw_probe(struct platform_device *pdev)
ndev->netdev_ops = &cpsw_netdev_ops;
ndev->ethtool_ops = &cpsw_ethtool_ops;
netif_napi_add(ndev, &priv->napi_rx, cpsw_rx_poll, CPSW_POLL_WEIGHT);
- netif_napi_add(ndev, &priv->napi_tx, cpsw_tx_poll, CPSW_POLL_WEIGHT);
+ netif_tx_napi_add(ndev, &priv->napi_tx, cpsw_tx_poll, CPSW_POLL_WEIGHT);
/* register the network device */
SET_NETDEV_DEV(ndev, &pdev->dev);
diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c
index 33bd3b902304..5d9abedd6b75 100644
--- a/drivers/net/ethernet/ti/davinci_emac.c
+++ b/drivers/net/ethernet/ti/davinci_emac.c
@@ -1644,10 +1644,7 @@ static int emac_dev_open(struct net_device *ndev)
priv->speed = 0;
priv->duplex = ~0;
- dev_info(emac_dev, "attached PHY driver [%s] "
- "(mii_bus:phy_addr=%s, id=%x)\n",
- priv->phydev->drv->name, dev_name(&priv->phydev->dev),
- priv->phydev->phy_id);
+ phy_attached_info(priv->phydev);
}
if (!priv->phydev) {
diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c
index c00084d689f3..4e7c9b9b042a 100644
--- a/drivers/net/ethernet/ti/davinci_mdio.c
+++ b/drivers/net/ethernet/ti/davinci_mdio.c
@@ -393,10 +393,10 @@ static int davinci_mdio_probe(struct platform_device *pdev)
/* scan and dump the bus */
for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
- phy = data->bus->phy_map[addr];
+ phy = mdiobus_get_phy(data->bus, addr);
if (phy) {
dev_info(dev, "phy[%d]: device %s, driver %s\n",
- phy->addr, dev_name(&phy->dev),
+ phy->mdio.addr, phydev_name(phy),
phy->drv ? phy->drv->name : "unknown");
}
}
diff --git a/drivers/net/ethernet/ti/netcp.h b/drivers/net/ethernet/ti/netcp.h
index bb1bb72121c0..17a26a429b71 100644
--- a/drivers/net/ethernet/ti/netcp.h
+++ b/drivers/net/ethernet/ti/netcp.h
@@ -113,7 +113,7 @@ struct netcp_intf {
#define NETCP_PSDATA_LEN KNAV_DMA_NUM_PS_WORDS
struct netcp_packet {
struct sk_buff *skb;
- u32 *epib;
+ __le32 *epib;
u32 *psdata;
unsigned int psdata_len;
struct netcp_intf *netcp;
diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c
index 37b9b39192ec..c61d66d38634 100644
--- a/drivers/net/ethernet/ti/netcp_core.c
+++ b/drivers/net/ethernet/ti/netcp_core.c
@@ -109,69 +109,80 @@ module_param(netcp_debug_level, int, 0);
MODULE_PARM_DESC(netcp_debug_level, "Netcp debug level (NETIF_MSG bits) (0=none,...,16=all)");
/* Helper functions - Get/Set */
-static void get_pkt_info(u32 *buff, u32 *buff_len, u32 *ndesc,
+static void get_pkt_info(dma_addr_t *buff, u32 *buff_len, dma_addr_t *ndesc,
struct knav_dma_desc *desc)
{
- *buff_len = desc->buff_len;
- *buff = desc->buff;
- *ndesc = desc->next_desc;
+ *buff_len = le32_to_cpu(desc->buff_len);
+ *buff = le32_to_cpu(desc->buff);
+ *ndesc = le32_to_cpu(desc->next_desc);
}
-static void get_pad_info(u32 *pad0, u32 *pad1, struct knav_dma_desc *desc)
+static void get_pad_info(u32 *pad0, u32 *pad1, u32 *pad2, struct knav_dma_desc *desc)
{
- *pad0 = desc->pad[0];
- *pad1 = desc->pad[1];
+ *pad0 = le32_to_cpu(desc->pad[0]);
+ *pad1 = le32_to_cpu(desc->pad[1]);
+ *pad2 = le32_to_cpu(desc->pad[2]);
}
-static void get_org_pkt_info(u32 *buff, u32 *buff_len,
+static void get_pad_ptr(void **padptr, struct knav_dma_desc *desc)
+{
+ u64 pad64;
+
+ pad64 = le32_to_cpu(desc->pad[0]) +
+ ((u64)le32_to_cpu(desc->pad[1]) << 32);
+ *padptr = (void *)(uintptr_t)pad64;
+}
+
+static void get_org_pkt_info(dma_addr_t *buff, u32 *buff_len,
struct knav_dma_desc *desc)
{
- *buff = desc->orig_buff;
- *buff_len = desc->orig_len;
+ *buff = le32_to_cpu(desc->orig_buff);
+ *buff_len = le32_to_cpu(desc->orig_len);
}
-static void get_words(u32 *words, int num_words, u32 *desc)
+static void get_words(dma_addr_t *words, int num_words, __le32 *desc)
{
int i;
for (i = 0; i < num_words; i++)
- words[i] = desc[i];
+ words[i] = le32_to_cpu(desc[i]);
}
-static void set_pkt_info(u32 buff, u32 buff_len, u32 ndesc,
+static void set_pkt_info(dma_addr_t buff, u32 buff_len, u32 ndesc,
struct knav_dma_desc *desc)
{
- desc->buff_len = buff_len;
- desc->buff = buff;
- desc->next_desc = ndesc;
+ desc->buff_len = cpu_to_le32(buff_len);
+ desc->buff = cpu_to_le32(buff);
+ desc->next_desc = cpu_to_le32(ndesc);
}
static void set_desc_info(u32 desc_info, u32 pkt_info,
struct knav_dma_desc *desc)
{
- desc->desc_info = desc_info;
- desc->packet_info = pkt_info;
+ desc->desc_info = cpu_to_le32(desc_info);
+ desc->packet_info = cpu_to_le32(pkt_info);
}
-static void set_pad_info(u32 pad0, u32 pad1, struct knav_dma_desc *desc)
+static void set_pad_info(u32 pad0, u32 pad1, u32 pad2, struct knav_dma_desc *desc)
{
- desc->pad[0] = pad0;
- desc->pad[1] = pad1;
+ desc->pad[0] = cpu_to_le32(pad0);
+ desc->pad[1] = cpu_to_le32(pad1);
+ desc->pad[2] = cpu_to_le32(pad1);
}
-static void set_org_pkt_info(u32 buff, u32 buff_len,
+static void set_org_pkt_info(dma_addr_t buff, u32 buff_len,
struct knav_dma_desc *desc)
{
- desc->orig_buff = buff;
- desc->orig_len = buff_len;
+ desc->orig_buff = cpu_to_le32(buff);
+ desc->orig_len = cpu_to_le32(buff_len);
}
-static void set_words(u32 *words, int num_words, u32 *desc)
+static void set_words(u32 *words, int num_words, __le32 *desc)
{
int i;
for (i = 0; i < num_words; i++)
- desc[i] = words[i];
+ desc[i] = cpu_to_le32(words[i]);
}
/* Read the e-fuse value as 32 bit values to be endian independent */
@@ -570,6 +581,7 @@ static void netcp_free_rx_desc_chain(struct netcp_intf *netcp,
dma_addr_t dma_desc, dma_buf;
unsigned int buf_len, dma_sz = sizeof(*ndesc);
void *buf_ptr;
+ u32 pad[2];
u32 tmp;
get_words(&dma_desc, 1, &desc->next_desc);
@@ -581,13 +593,15 @@ static void netcp_free_rx_desc_chain(struct netcp_intf *netcp,
break;
}
get_pkt_info(&dma_buf, &tmp, &dma_desc, ndesc);
- get_pad_info((u32 *)&buf_ptr, &tmp, ndesc);
+ get_pad_ptr(&buf_ptr, ndesc);
dma_unmap_page(netcp->dev, dma_buf, PAGE_SIZE, DMA_FROM_DEVICE);
__free_page(buf_ptr);
knav_pool_desc_put(netcp->rx_pool, desc);
}
- get_pad_info((u32 *)&buf_ptr, &buf_len, desc);
+ get_pad_info(&pad[0], &pad[1], &buf_len, desc);
+ buf_ptr = (void *)(uintptr_t)(pad[0] + ((u64)pad[1] << 32));
+
if (buf_ptr)
netcp_frag_free(buf_len <= PAGE_SIZE, buf_ptr);
knav_pool_desc_put(netcp->rx_pool, desc);
@@ -625,8 +639,8 @@ static int netcp_process_one_rx_packet(struct netcp_intf *netcp)
dma_addr_t dma_desc, dma_buff;
struct netcp_packet p_info;
struct sk_buff *skb;
+ u32 pad[2];
void *org_buf_ptr;
- u32 tmp;
dma_desc = knav_queue_pop(netcp->rx_queue, &dma_sz);
if (!dma_desc)
@@ -639,7 +653,8 @@ static int netcp_process_one_rx_packet(struct netcp_intf *netcp)
}
get_pkt_info(&dma_buff, &buf_len, &dma_desc, desc);
- get_pad_info((u32 *)&org_buf_ptr, &org_buf_len, desc);
+ get_pad_info(&pad[0], &pad[1], &org_buf_len, desc);
+ org_buf_ptr = (void *)(uintptr_t)(pad[0] + ((u64)pad[1] << 32));
if (unlikely(!org_buf_ptr)) {
dev_err(netcp->ndev_dev, "NULL bufptr in desc\n");
@@ -664,6 +679,7 @@ static int netcp_process_one_rx_packet(struct netcp_intf *netcp)
/* Fill in the page fragment list */
while (dma_desc) {
struct page *page;
+ void *ptr;
ndesc = knav_pool_desc_unmap(netcp->rx_pool, dma_desc, dma_sz);
if (unlikely(!ndesc)) {
@@ -672,14 +688,15 @@ static int netcp_process_one_rx_packet(struct netcp_intf *netcp)
}
get_pkt_info(&dma_buff, &buf_len, &dma_desc, ndesc);
- get_pad_info((u32 *)&page, &tmp, ndesc);
+ get_pad_ptr(&ptr, ndesc);
+ page = ptr;
if (likely(dma_buff && buf_len && page)) {
dma_unmap_page(netcp->dev, dma_buff, PAGE_SIZE,
DMA_FROM_DEVICE);
} else {
- dev_err(netcp->ndev_dev, "Bad Rx desc dma_buff(%p), len(%d), page(%p)\n",
- (void *)dma_buff, buf_len, page);
+ dev_err(netcp->ndev_dev, "Bad Rx desc dma_buff(%pad), len(%d), page(%p)\n",
+ &dma_buff, buf_len, page);
goto free_desc;
}
@@ -750,7 +767,6 @@ static void netcp_free_rx_buf(struct netcp_intf *netcp, int fdq)
unsigned int buf_len, dma_sz;
dma_addr_t dma;
void *buf_ptr;
- u32 tmp;
/* Allocate descriptor */
while ((dma = knav_queue_pop(netcp->rx_fdq[fdq], &dma_sz))) {
@@ -761,7 +777,7 @@ static void netcp_free_rx_buf(struct netcp_intf *netcp, int fdq)
}
get_org_pkt_info(&dma, &buf_len, desc);
- get_pad_info((u32 *)&buf_ptr, &tmp, desc);
+ get_pad_ptr(&buf_ptr, desc);
if (unlikely(!dma)) {
dev_err(netcp->ndev_dev, "NULL orig_buff in desc\n");
@@ -813,7 +829,7 @@ static int netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq)
struct page *page;
dma_addr_t dma;
void *bufptr;
- u32 pad[2];
+ u32 pad[3];
/* Allocate descriptor */
hwdesc = knav_pool_desc_get(netcp->rx_pool);
@@ -830,7 +846,7 @@ static int netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq)
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
bufptr = netdev_alloc_frag(primary_buf_len);
- pad[1] = primary_buf_len;
+ pad[2] = primary_buf_len;
if (unlikely(!bufptr)) {
dev_warn_ratelimited(netcp->ndev_dev,
@@ -842,7 +858,8 @@ static int netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq)
if (unlikely(dma_mapping_error(netcp->dev, dma)))
goto fail;
- pad[0] = (u32)bufptr;
+ pad[0] = lower_32_bits((uintptr_t)bufptr);
+ pad[1] = upper_32_bits((uintptr_t)bufptr);
} else {
/* Allocate a secondary receive queue entry */
@@ -853,8 +870,9 @@ static int netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq)
}
buf_len = PAGE_SIZE;
dma = dma_map_page(netcp->dev, page, 0, buf_len, DMA_TO_DEVICE);
- pad[0] = (u32)page;
- pad[1] = 0;
+ pad[0] = lower_32_bits(dma);
+ pad[1] = upper_32_bits(dma);
+ pad[2] = 0;
}
desc_info = KNAV_DMA_DESC_PS_INFO_IN_DESC;
@@ -864,7 +882,7 @@ static int netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq)
pkt_info |= (netcp->rx_queue_id & KNAV_DMA_DESC_RETQ_MASK) <<
KNAV_DMA_DESC_RETQ_SHIFT;
set_org_pkt_info(dma, buf_len, hwdesc);
- set_pad_info(pad[0], pad[1], hwdesc);
+ set_pad_info(pad[0], pad[1], pad[2], hwdesc);
set_desc_info(desc_info, pkt_info, hwdesc);
/* Push to FDQs */
@@ -935,8 +953,8 @@ static void netcp_free_tx_desc_chain(struct netcp_intf *netcp,
dma_unmap_single(netcp->dev, dma_buf, buf_len,
DMA_TO_DEVICE);
else
- dev_warn(netcp->ndev_dev, "bad Tx desc buf(%p), len(%d)\n",
- (void *)dma_buf, buf_len);
+ dev_warn(netcp->ndev_dev, "bad Tx desc buf(%pad), len(%d)\n",
+ &dma_buf, buf_len);
knav_pool_desc_put(netcp->tx_pool, ndesc);
ndesc = NULL;
@@ -953,11 +971,11 @@ static int netcp_process_tx_compl_packets(struct netcp_intf *netcp,
unsigned int budget)
{
struct knav_dma_desc *desc;
+ void *ptr;
struct sk_buff *skb;
unsigned int dma_sz;
dma_addr_t dma;
int pkts = 0;
- u32 tmp;
while (budget--) {
dma = knav_queue_pop(netcp->tx_compl_q, &dma_sz);
@@ -970,7 +988,8 @@ static int netcp_process_tx_compl_packets(struct netcp_intf *netcp,
continue;
}
- get_pad_info((u32 *)&skb, &tmp, desc);
+ get_pad_ptr(&ptr, desc);
+ skb = ptr;
netcp_free_tx_desc_chain(netcp, desc, dma_sz);
if (!skb) {
dev_err(netcp->ndev_dev, "No skb in Tx desc\n");
@@ -1059,6 +1078,7 @@ netcp_tx_map_skb(struct sk_buff *skb, struct netcp_intf *netcp)
u32 page_offset = frag->page_offset;
u32 buf_len = skb_frag_size(frag);
dma_addr_t desc_dma;
+ u32 desc_dma_32;
u32 pkt_info;
dma_addr = dma_map_page(dev, page, page_offset, buf_len,
@@ -1075,13 +1095,13 @@ netcp_tx_map_skb(struct sk_buff *skb, struct netcp_intf *netcp)
goto free_descs;
}
- desc_dma = knav_pool_desc_virt_to_dma(netcp->tx_pool,
- (void *)ndesc);
+ desc_dma = knav_pool_desc_virt_to_dma(netcp->tx_pool, ndesc);
pkt_info =
(netcp->tx_compl_qid & KNAV_DMA_DESC_RETQ_MASK) <<
KNAV_DMA_DESC_RETQ_SHIFT;
set_pkt_info(dma_addr, buf_len, 0, ndesc);
- set_words(&desc_dma, 1, &pdesc->next_desc);
+ desc_dma_32 = (u32)desc_dma;
+ set_words(&desc_dma_32, 1, &pdesc->next_desc);
pkt_len += buf_len;
if (pdesc != desc)
knav_pool_desc_map(netcp->tx_pool, pdesc,
@@ -1129,8 +1149,8 @@ static int netcp_tx_submit_skb(struct netcp_intf *netcp,
p_info.ts_context = NULL;
p_info.txtstamp_complete = NULL;
p_info.epib = desc->epib;
- p_info.psdata = desc->psdata;
- memset(p_info.epib, 0, KNAV_DMA_NUM_EPIB_WORDS * sizeof(u32));
+ p_info.psdata = (u32 __force *)desc->psdata;
+ memset(p_info.epib, 0, KNAV_DMA_NUM_EPIB_WORDS * sizeof(__le32));
/* Find out where to inject the packet for transmission */
list_for_each_entry(tx_hook, &netcp->txhook_list_head, list) {
@@ -1154,11 +1174,12 @@ static int netcp_tx_submit_skb(struct netcp_intf *netcp,
/* update descriptor */
if (p_info.psdata_len) {
- u32 *psdata = p_info.psdata;
+ /* psdata points to both native-endian and device-endian data */
+ __le32 *psdata = (void __force *)p_info.psdata;
memmove(p_info.psdata, p_info.psdata + p_info.psdata_len,
p_info.psdata_len);
- set_words(psdata, p_info.psdata_len, psdata);
+ set_words(p_info.psdata, p_info.psdata_len, psdata);
tmp |= (p_info.psdata_len & KNAV_DMA_DESC_PSLEN_MASK) <<
KNAV_DMA_DESC_PSLEN_SHIFT;
}
@@ -1173,11 +1194,14 @@ static int netcp_tx_submit_skb(struct netcp_intf *netcp,
}
set_words(&tmp, 1, &desc->packet_info);
- set_words((u32 *)&skb, 1, &desc->pad[0]);
+ tmp = lower_32_bits((uintptr_t)&skb);
+ set_words(&tmp, 1, &desc->pad[0]);
+ tmp = upper_32_bits((uintptr_t)&skb);
+ set_words(&tmp, 1, &desc->pad[1]);
if (tx_pipe->flags & SWITCH_TO_PORT_IN_TAGINFO) {
tmp = tx_pipe->switch_to_port;
- set_words((u32 *)&tmp, 1, &desc->tag_info);
+ set_words(&tmp, 1, &desc->tag_info);
}
/* submit packet descriptor */
@@ -1990,7 +2014,7 @@ static int netcp_create_interface(struct netcp_device *netcp_device,
/* NAPI register */
netif_napi_add(ndev, &netcp->rx_napi, netcp_rx_poll, NETCP_NAPI_WEIGHT);
- netif_napi_add(ndev, &netcp->tx_napi, netcp_tx_poll, NETCP_NAPI_WEIGHT);
+ netif_tx_napi_add(ndev, &netcp->tx_napi, netcp_tx_poll, NETCP_NAPI_WEIGHT);
/* Register the network device */
ndev->dev_id = 0;
diff --git a/drivers/net/ethernet/ti/netcp_ethss.c b/drivers/net/ethernet/ti/netcp_ethss.c
index 4e70e7586a09..d543298d6750 100644
--- a/drivers/net/ethernet/ti/netcp_ethss.c
+++ b/drivers/net/ethernet/ti/netcp_ethss.c
@@ -2178,7 +2178,7 @@ static int gbe_slave_open(struct gbe_intf *gbe_intf)
return -ENODEV;
}
dev_dbg(priv->dev, "phy found: id is: 0x%s\n",
- dev_name(&slave->phy->dev));
+ phydev_name(slave->phy));
phy_start(slave->phy);
phy_read_status(slave->phy);
}
@@ -2681,7 +2681,7 @@ static void init_secondary_ports(struct gbe_priv *gbe_dev,
slave->phy = NULL;
} else {
dev_dbg(dev, "phy found: id is: 0x%s\n",
- dev_name(&slave->phy->dev));
+ phydev_name(slave->phy));
phy_start(slave->phy);
phy_read_status(slave->phy);
}
diff --git a/drivers/net/ethernet/tile/tilepro.c b/drivers/net/ethernet/tile/tilepro.c
index 6f0a4495c7f3..298e059d0498 100644
--- a/drivers/net/ethernet/tile/tilepro.c
+++ b/drivers/net/ethernet/tile/tilepro.c
@@ -1349,8 +1349,7 @@ static int tile_net_open_inner(struct net_device *dev)
*/
static void tile_net_open_retry(struct work_struct *w)
{
- struct delayed_work *dw =
- container_of(w, struct delayed_work, work);
+ struct delayed_work *dw = to_delayed_work(w);
struct tile_net_priv *priv =
container_of(dw, struct tile_net_priv, retry_work);
diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c
index 45ac38d29ed8..54874783476a 100644
--- a/drivers/net/ethernet/toshiba/tc35815.c
+++ b/drivers/net/ethernet/toshiba/tc35815.c
@@ -608,40 +608,25 @@ static void tc_handle_link_change(struct net_device *dev)
static int tc_mii_probe(struct net_device *dev)
{
struct tc35815_local *lp = netdev_priv(dev);
- struct phy_device *phydev = NULL;
- int phy_addr;
+ struct phy_device *phydev;
u32 dropmask;
- /* find the first phy */
- for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
- if (lp->mii_bus->phy_map[phy_addr]) {
- if (phydev) {
- printk(KERN_ERR "%s: multiple PHYs found\n",
- dev->name);
- return -EINVAL;
- }
- phydev = lp->mii_bus->phy_map[phy_addr];
- break;
- }
- }
-
+ phydev = phy_find_first(lp->mii_bus);
if (!phydev) {
printk(KERN_ERR "%s: no PHY found\n", dev->name);
return -ENODEV;
}
/* attach the mac to the phy */
- phydev = phy_connect(dev, dev_name(&phydev->dev),
+ phydev = phy_connect(dev, phydev_name(phydev),
&tc_handle_link_change,
lp->chiptype == TC35815_TX4939 ? PHY_INTERFACE_MODE_RMII : PHY_INTERFACE_MODE_MII);
if (IS_ERR(phydev)) {
printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
return PTR_ERR(phydev);
}
- printk(KERN_INFO "%s: attached PHY driver [%s] "
- "(mii_bus:phy_addr=%s, id=%x)\n",
- dev->name, phydev->drv->name, dev_name(&phydev->dev),
- phydev->phy_id);
+
+ phy_attached_info(phydev);
/* mask with MAC supported features */
phydev->supported &= PHY_BASIC_FEATURES;
@@ -669,7 +654,6 @@ static int tc_mii_init(struct net_device *dev)
{
struct tc35815_local *lp = netdev_priv(dev);
int err;
- int i;
lp->mii_bus = mdiobus_alloc();
if (lp->mii_bus == NULL) {
@@ -684,18 +668,9 @@ static int tc_mii_init(struct net_device *dev)
(lp->pci_dev->bus->number << 8) | lp->pci_dev->devfn);
lp->mii_bus->priv = dev;
lp->mii_bus->parent = &lp->pci_dev->dev;
- lp->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!lp->mii_bus->irq) {
- err = -ENOMEM;
- goto err_out_free_mii_bus;
- }
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- lp->mii_bus->irq[i] = PHY_POLL;
-
err = mdiobus_register(lp->mii_bus);
if (err)
- goto err_out_free_mdio_irq;
+ goto err_out_free_mii_bus;
err = tc_mii_probe(dev);
if (err)
goto err_out_unregister_bus;
@@ -703,8 +678,6 @@ static int tc_mii_init(struct net_device *dev)
err_out_unregister_bus:
mdiobus_unregister(lp->mii_bus);
-err_out_free_mdio_irq:
- kfree(lp->mii_bus->irq);
err_out_free_mii_bus:
mdiobus_free(lp->mii_bus);
err_out:
@@ -882,7 +855,6 @@ static void tc35815_remove_one(struct pci_dev *pdev)
phy_disconnect(lp->phy_dev);
mdiobus_unregister(lp->mii_bus);
- kfree(lp->mii_bus->irq);
mdiobus_free(lp->mii_bus);
unregister_netdev(dev);
free_netdev(dev);
diff --git a/drivers/net/ethernet/xilinx/ll_temac.h b/drivers/net/ethernet/xilinx/ll_temac.h
index 522abe2ff25a..902457e43628 100644
--- a/drivers/net/ethernet/xilinx/ll_temac.h
+++ b/drivers/net/ethernet/xilinx/ll_temac.h
@@ -337,7 +337,6 @@ struct temac_local {
/* MDIO bus data */
struct mii_bus *mii_bus; /* MII bus reference */
- int mdio_irqs[PHY_MAX_ADDR]; /* IRQs table for MDIO bus */
/* IO registers, dma functions and IRQs */
void __iomem *regs;
diff --git a/drivers/net/ethernet/xilinx/ll_temac_mdio.c b/drivers/net/ethernet/xilinx/ll_temac_mdio.c
index 415de1eaf641..7714aff78b7d 100644
--- a/drivers/net/ethernet/xilinx/ll_temac_mdio.c
+++ b/drivers/net/ethernet/xilinx/ll_temac_mdio.c
@@ -92,7 +92,6 @@ int temac_mdio_setup(struct temac_local *lp, struct device_node *np)
bus->read = temac_mdio_read;
bus->write = temac_mdio_write;
bus->parent = lp->dev;
- bus->irq = lp->mdio_irqs; /* preallocated IRQ table */
lp->mii_bus = bus;
@@ -114,7 +113,6 @@ int temac_mdio_setup(struct temac_local *lp, struct device_node *np)
void temac_mdio_teardown(struct temac_local *lp)
{
mdiobus_unregister(lp->mii_bus);
- kfree(lp->mii_bus->irq);
mdiobus_free(lp->mii_bus);
lp->mii_bus = NULL;
}
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h
index 7cb9abac95c8..9ead4e269409 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
@@ -385,7 +385,6 @@ struct axidma_bd {
* @phy_dev: Pointer to PHY device structure attached to the axienet_local
* @phy_node: Pointer to device node structure
* @mii_bus: Pointer to MII bus structure
- * @mdio_irqs: IRQs table for MDIO bus required in mii_bus structure
* @regs: Base address for the axienet_local device address space
* @dma_regs: Base address for the axidma device address space
* @dma_err_tasklet: Tasklet structure to process Axi DMA errors
@@ -426,7 +425,6 @@ struct axienet_local {
/* MDIO bus data */
struct mii_bus *mii_bus; /* MII bus reference */
- int mdio_irqs[PHY_MAX_ADDR]; /* IRQs table for MDIO bus */
/* IO registers, dma functions and IRQs */
void __iomem *regs;
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
index 507bbb0355c2..63307ea97846 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
@@ -212,7 +212,6 @@ issue:
bus->read = axienet_mdio_read;
bus->write = axienet_mdio_write;
bus->parent = lp->dev;
- bus->irq = lp->mdio_irqs; /* preallocated IRQ table */
lp->mii_bus = bus;
ret = of_mdiobus_register(bus, np1);
@@ -232,7 +231,6 @@ issue:
void axienet_mdio_teardown(struct axienet_local *lp)
{
mdiobus_unregister(lp->mii_bus);
- kfree(lp->mii_bus->irq);
mdiobus_free(lp->mii_bus);
lp->mii_bus = NULL;
}
diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
index cf468c87ce57..e324b3092380 100644
--- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c
+++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
@@ -114,7 +114,6 @@
* @phy_dev: pointer to the PHY device
* @phy_node: pointer to the PHY device node
* @mii_bus: pointer to the MII bus
- * @mdio_irqs: IRQs table for MDIO bus
* @last_link: last link status
* @has_mdio: indicates whether MDIO is included in the HW
*/
@@ -135,7 +134,6 @@ struct net_local {
struct device_node *phy_node;
struct mii_bus *mii_bus;
- int mdio_irqs[PHY_MAX_ADDR];
int last_link;
bool has_mdio;
@@ -829,7 +827,7 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev)
dev_info(dev,
"MDIO of the phy is not registered yet\n");
else
- put_device(&phydev->dev);
+ put_device(&phydev->mdio.dev);
return 0;
}
@@ -852,7 +850,6 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev)
bus->read = xemaclite_mdio_read;
bus->write = xemaclite_mdio_write;
bus->parent = dev;
- bus->irq = lp->mdio_irqs; /* preallocated IRQ table */
lp->mii_bus = bus;
@@ -1196,7 +1193,6 @@ static int xemaclite_of_remove(struct platform_device *of_dev)
/* Un-register the mii_bus, if configured */
if (lp->has_mdio) {
mdiobus_unregister(lp->mii_bus);
- kfree(lp->mii_bus->irq);
mdiobus_free(lp->mii_bus);
lp->mii_bus = NULL;
}
diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index de5c30c9f059..7456569f53c1 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -71,8 +71,14 @@ struct geneve_dev {
__be16 dst_port;
bool collect_md;
struct gro_cells gro_cells;
+ u32 flags;
};
+/* Geneve device flags */
+#define GENEVE_F_UDP_CSUM BIT(0)
+#define GENEVE_F_UDP_ZERO_CSUM6_TX BIT(1)
+#define GENEVE_F_UDP_ZERO_CSUM6_RX BIT(2)
+
struct geneve_sock {
bool collect_md;
struct list_head list;
@@ -81,6 +87,7 @@ struct geneve_sock {
int refcnt;
struct udp_offload udp_offloads;
struct hlist_head vni_list[VNI_HASH_SIZE];
+ u32 flags;
};
static inline __u32 geneve_net_vni_hash(u8 vni[3])
@@ -343,7 +350,7 @@ error:
}
static struct socket *geneve_create_sock(struct net *net, bool ipv6,
- __be16 port)
+ __be16 port, u32 flags)
{
struct socket *sock;
struct udp_port_cfg udp_conf;
@@ -354,6 +361,8 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6,
if (ipv6) {
udp_conf.family = AF_INET6;
udp_conf.ipv6_v6only = 1;
+ udp_conf.use_udp6_rx_checksums =
+ !(flags & GENEVE_F_UDP_ZERO_CSUM6_RX);
} else {
udp_conf.family = AF_INET;
udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
@@ -371,16 +380,27 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6,
static void geneve_notify_add_rx_port(struct geneve_sock *gs)
{
+ struct net_device *dev;
struct sock *sk = gs->sock->sk;
+ struct net *net = sock_net(sk);
sa_family_t sa_family = sk->sk_family;
+ __be16 port = inet_sk(sk)->inet_sport;
int err;
if (sa_family == AF_INET) {
- err = udp_add_offload(&gs->udp_offloads);
+ err = udp_add_offload(sock_net(sk), &gs->udp_offloads);
if (err)
pr_warn("geneve: udp_add_offload failed with status %d\n",
err);
}
+
+ rcu_read_lock();
+ for_each_netdev_rcu(net, dev) {
+ if (dev->netdev_ops->ndo_add_geneve_port)
+ dev->netdev_ops->ndo_add_geneve_port(dev, sa_family,
+ port);
+ }
+ rcu_read_unlock();
}
static int geneve_hlen(struct genevehdr *gh)
@@ -480,7 +500,7 @@ static int geneve_gro_complete(struct sk_buff *skb, int nhoff,
/* Create new listen socket if needed */
static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port,
- bool ipv6)
+ bool ipv6, u32 flags)
{
struct geneve_net *gn = net_generic(net, geneve_net_id);
struct geneve_sock *gs;
@@ -492,7 +512,7 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port,
if (!gs)
return ERR_PTR(-ENOMEM);
- sock = geneve_create_sock(net, ipv6, port);
+ sock = geneve_create_sock(net, ipv6, port, flags);
if (IS_ERR(sock)) {
kfree(gs);
return ERR_CAST(sock);
@@ -521,8 +541,20 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port,
static void geneve_notify_del_rx_port(struct geneve_sock *gs)
{
+ struct net_device *dev;
struct sock *sk = gs->sock->sk;
+ struct net *net = sock_net(sk);
sa_family_t sa_family = sk->sk_family;
+ __be16 port = inet_sk(sk)->inet_sport;
+
+ rcu_read_lock();
+ for_each_netdev_rcu(net, dev) {
+ if (dev->netdev_ops->ndo_del_geneve_port)
+ dev->netdev_ops->ndo_del_geneve_port(dev, sa_family,
+ port);
+ }
+
+ rcu_read_unlock();
if (sa_family == AF_INET)
udp_del_offload(&gs->udp_offloads);
@@ -575,12 +607,13 @@ static int geneve_sock_add(struct geneve_dev *geneve, bool ipv6)
goto out;
}
- gs = geneve_socket_create(net, geneve->dst_port, ipv6);
+ gs = geneve_socket_create(net, geneve->dst_port, ipv6, geneve->flags);
if (IS_ERR(gs))
return PTR_ERR(gs);
out:
gs->collect_md = geneve->collect_md;
+ gs->flags = geneve->flags;
#if IS_ENABLED(CONFIG_IPV6)
if (ipv6)
geneve->sock6 = gs;
@@ -642,11 +675,12 @@ static void geneve_build_header(struct genevehdr *geneveh,
static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
__be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
- bool csum, bool xnet)
+ u32 flags, bool xnet)
{
struct genevehdr *gnvh;
int min_headroom;
int err;
+ bool udp_sum = !!(flags & GENEVE_F_UDP_CSUM);
skb_scrub_packet(skb, xnet);
@@ -658,7 +692,7 @@ static int geneve_build_skb(struct rtable *rt, struct sk_buff *skb,
goto free_rt;
}
- skb = udp_tunnel_handle_offloads(skb, csum);
+ skb = udp_tunnel_handle_offloads(skb, udp_sum);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
goto free_rt;
@@ -678,11 +712,12 @@ free_rt:
#if IS_ENABLED(CONFIG_IPV6)
static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
__be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
- bool csum, bool xnet)
+ u32 flags, bool xnet)
{
struct genevehdr *gnvh;
int min_headroom;
int err;
+ bool udp_sum = !(flags & GENEVE_F_UDP_ZERO_CSUM6_TX);
skb_scrub_packet(skb, xnet);
@@ -694,7 +729,7 @@ static int geneve6_build_skb(struct dst_entry *dst, struct sk_buff *skb,
goto free_dst;
}
- skb = udp_tunnel_handle_offloads(skb, csum);
+ skb = udp_tunnel_handle_offloads(skb, udp_sum);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
goto free_dst;
@@ -824,9 +859,9 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
struct flowi4 fl4;
__u8 tos, ttl;
__be16 sport;
- bool udp_csum;
__be16 df;
bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
+ u32 flags = geneve->flags;
if (geneve->collect_md) {
if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
@@ -857,9 +892,13 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
if (key->tun_flags & TUNNEL_GENEVE_OPT)
opts = ip_tunnel_info_opts(info);
- udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
+ if (key->tun_flags & TUNNEL_CSUM)
+ flags |= GENEVE_F_UDP_CSUM;
+ else
+ flags &= ~GENEVE_F_UDP_CSUM;
+
err = geneve_build_skb(rt, skb, key->tun_flags, vni,
- info->options_len, opts, udp_csum, xnet);
+ info->options_len, opts, flags, xnet);
if (unlikely(err))
goto err;
@@ -867,9 +906,8 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
ttl = key->ttl;
df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0;
} else {
- udp_csum = false;
err = geneve_build_skb(rt, skb, 0, geneve->vni,
- 0, NULL, udp_csum, xnet);
+ 0, NULL, flags, xnet);
if (unlikely(err))
goto err;
@@ -880,12 +918,11 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
df = 0;
}
- err = udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, fl4.saddr, fl4.daddr,
- tos, ttl, df, sport, geneve->dst_port,
- !net_eq(geneve->net, dev_net(geneve->dev)),
- !udp_csum);
+ udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, fl4.saddr, fl4.daddr,
+ tos, ttl, df, sport, geneve->dst_port,
+ !net_eq(geneve->net, dev_net(geneve->dev)),
+ !(flags & GENEVE_F_UDP_CSUM));
- iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
return NETDEV_TX_OK;
tx_error:
@@ -912,8 +949,8 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
struct flowi6 fl6;
__u8 prio, ttl;
__be16 sport;
- bool udp_csum;
bool xnet = !net_eq(geneve->net, dev_net(geneve->dev));
+ u32 flags = geneve->flags;
if (geneve->collect_md) {
if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
@@ -942,19 +979,22 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
if (key->tun_flags & TUNNEL_GENEVE_OPT)
opts = ip_tunnel_info_opts(info);
- udp_csum = !!(key->tun_flags & TUNNEL_CSUM);
+ if (key->tun_flags & TUNNEL_CSUM)
+ flags |= GENEVE_F_UDP_CSUM;
+ else
+ flags &= ~GENEVE_F_UDP_CSUM;
+
err = geneve6_build_skb(dst, skb, key->tun_flags, vni,
info->options_len, opts,
- udp_csum, xnet);
+ flags, xnet);
if (unlikely(err))
goto err;
prio = ip_tunnel_ecn_encap(key->tos, iip, skb);
ttl = key->ttl;
} else {
- udp_csum = false;
err = geneve6_build_skb(dst, skb, 0, geneve->vni,
- 0, NULL, udp_csum, xnet);
+ 0, NULL, flags, xnet);
if (unlikely(err))
goto err;
@@ -964,11 +1004,10 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
ttl = 1;
ttl = ttl ? : ip6_dst_hoplimit(dst);
}
- err = udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
- &fl6.saddr, &fl6.daddr, prio, ttl,
- sport, geneve->dst_port, !udp_csum);
-
- iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
+ udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
+ &fl6.saddr, &fl6.daddr, prio, ttl,
+ sport, geneve->dst_port,
+ !!(flags & GENEVE_F_UDP_ZERO_CSUM6_TX));
return NETDEV_TX_OK;
tx_error:
@@ -1067,6 +1106,30 @@ static struct device_type geneve_type = {
.name = "geneve",
};
+/* Calls the ndo_add_geneve_port of the caller in order to
+ * supply the listening GENEVE udp ports. Callers are expected
+ * to implement the ndo_add_geneve_port.
+ */
+void geneve_get_rx_port(struct net_device *dev)
+{
+ struct net *net = dev_net(dev);
+ struct geneve_net *gn = net_generic(net, geneve_net_id);
+ struct geneve_sock *gs;
+ sa_family_t sa_family;
+ struct sock *sk;
+ __be16 port;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(gs, &gn->sock_list, list) {
+ sk = gs->sock->sk;
+ sa_family = sk->sk_family;
+ port = inet_sk(sk)->inet_sport;
+ dev->netdev_ops->ndo_add_geneve_port(dev, sa_family, port);
+ }
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(geneve_get_rx_port);
+
/* Initialize the device structure. */
static void geneve_setup(struct net_device *dev)
{
@@ -1099,6 +1162,9 @@ static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = {
[IFLA_GENEVE_TOS] = { .type = NLA_U8 },
[IFLA_GENEVE_PORT] = { .type = NLA_U16 },
[IFLA_GENEVE_COLLECT_METADATA] = { .type = NLA_FLAG },
+ [IFLA_GENEVE_UDP_CSUM] = { .type = NLA_U8 },
+ [IFLA_GENEVE_UDP_ZERO_CSUM6_TX] = { .type = NLA_U8 },
+ [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NLA_U8 },
};
static int geneve_validate(struct nlattr *tb[], struct nlattr *data[])
@@ -1152,12 +1218,12 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
static int geneve_configure(struct net *net, struct net_device *dev,
union geneve_addr *remote,
__u32 vni, __u8 ttl, __u8 tos, __be16 dst_port,
- bool metadata)
+ bool metadata, u32 flags)
{
struct geneve_net *gn = net_generic(net, geneve_net_id);
struct geneve_dev *t, *geneve = netdev_priv(dev);
bool tun_collect_md, tun_on_same_port;
- int err;
+ int err, encap_len;
if (!remote)
return -EINVAL;
@@ -1183,12 +1249,21 @@ static int geneve_configure(struct net *net, struct net_device *dev,
geneve->tos = tos;
geneve->dst_port = dst_port;
geneve->collect_md = metadata;
+ geneve->flags = flags;
t = geneve_find_dev(gn, dst_port, remote, geneve->vni,
&tun_on_same_port, &tun_collect_md);
if (t)
return -EBUSY;
+ /* make enough headroom for basic scenario */
+ encap_len = GENEVE_BASE_HLEN + ETH_HLEN;
+ if (remote->sa.sa_family == AF_INET)
+ encap_len += sizeof(struct iphdr);
+ else
+ encap_len += sizeof(struct ipv6hdr);
+ dev->needed_headroom = encap_len + ETH_HLEN;
+
if (metadata) {
if (tun_on_same_port)
return -EPERM;
@@ -1213,6 +1288,7 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
bool metadata = false;
union geneve_addr remote = geneve_remote_unspec;
__u32 vni = 0;
+ u32 flags = 0;
if (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6])
return -EINVAL;
@@ -1253,8 +1329,20 @@ static int geneve_newlink(struct net *net, struct net_device *dev,
if (data[IFLA_GENEVE_COLLECT_METADATA])
metadata = true;
+ if (data[IFLA_GENEVE_UDP_CSUM] &&
+ nla_get_u8(data[IFLA_GENEVE_UDP_CSUM]))
+ flags |= GENEVE_F_UDP_CSUM;
+
+ if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX] &&
+ nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]))
+ flags |= GENEVE_F_UDP_ZERO_CSUM6_TX;
+
+ if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX] &&
+ nla_get_u8(data[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]))
+ flags |= GENEVE_F_UDP_ZERO_CSUM6_RX;
+
return geneve_configure(net, dev, &remote, vni, ttl, tos, dst_port,
- metadata);
+ metadata, flags);
}
static void geneve_dellink(struct net_device *dev, struct list_head *head)
@@ -1273,6 +1361,9 @@ static size_t geneve_get_size(const struct net_device *dev)
nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_TOS */
nla_total_size(sizeof(__be16)) + /* IFLA_GENEVE_PORT */
nla_total_size(0) + /* IFLA_GENEVE_COLLECT_METADATA */
+ nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_UDP_CSUM */
+ nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_UDP_ZERO_CSUM6_TX */
+ nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_UDP_ZERO_CSUM6_RX */
0;
}
@@ -1309,6 +1400,14 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev)
goto nla_put_failure;
}
+ if (nla_put_u8(skb, IFLA_GENEVE_UDP_CSUM,
+ !!(geneve->flags & GENEVE_F_UDP_CSUM)) ||
+ nla_put_u8(skb, IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
+ !!(geneve->flags & GENEVE_F_UDP_ZERO_CSUM6_TX)) ||
+ nla_put_u8(skb, IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
+ !!(geneve->flags & GENEVE_F_UDP_ZERO_CSUM6_RX)))
+ goto nla_put_failure;
+
return 0;
nla_put_failure:
@@ -1342,7 +1441,7 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
return dev;
err = geneve_configure(net, dev, &geneve_remote_unspec,
- 0, 0, 0, htons(dst_port), true);
+ 0, 0, 0, htons(dst_port), true, 0);
if (err) {
free_netdev(dev);
return ERR_PTR(err);
diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c
index 7c4a4151ef0f..5a1e98547031 100644
--- a/drivers/net/hamradio/6pack.c
+++ b/drivers/net/hamradio/6pack.c
@@ -683,14 +683,20 @@ static void sixpack_close(struct tty_struct *tty)
if (!atomic_dec_and_test(&sp->refcnt))
down(&sp->dead_sem);
- unregister_netdev(sp->dev);
+ /* We must stop the queue to avoid potentially scribbling
+ * on the free buffers. The sp->dead_sem is not sufficient
+ * to protect us from sp->xbuff access.
+ */
+ netif_stop_queue(sp->dev);
- del_timer(&sp->tx_t);
- del_timer(&sp->resync_t);
+ del_timer_sync(&sp->tx_t);
+ del_timer_sync(&sp->resync_t);
/* Free all 6pack frame buffers. */
kfree(sp->rbuff);
kfree(sp->xbuff);
+
+ unregister_netdev(sp->dev);
}
/* Perform I/O control on an active 6pack channel. */
diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c
index 216bfd350169..85828f153445 100644
--- a/drivers/net/hamradio/mkiss.c
+++ b/drivers/net/hamradio/mkiss.c
@@ -797,14 +797,19 @@ static void mkiss_close(struct tty_struct *tty)
*/
if (!atomic_dec_and_test(&ax->refcnt))
down(&ax->dead_sem);
-
- unregister_netdev(ax->dev);
+ /*
+ * Halt the transmit queue so that a new transmit cannot scribble
+ * on our buffers
+ */
+ netif_stop_queue(ax->dev);
/* Free all AX25 frame buffers. */
kfree(ax->rbuff);
kfree(ax->xbuff);
ax->tty = NULL;
+
+ unregister_netdev(ax->dev);
}
/* Perform I/O control on an active ax25 channel. */
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
index 5fa98f599b3d..f4130af09244 100644
--- a/drivers/net/hyperv/hyperv_net.h
+++ b/drivers/net/hyperv/hyperv_net.h
@@ -124,37 +124,22 @@ struct ndis_tcp_ip_checksum_info;
/*
* Represent netvsc packet which contains 1 RNDIS and 1 ethernet frame
* within the RNDIS
+ *
+ * The size of this structure is less than 48 bytes and we can now
+ * place this structure in the skb->cb field.
*/
struct hv_netvsc_packet {
/* Bookkeeping stuff */
- u32 status;
-
- bool is_data_pkt;
- bool xmit_more; /* from skb */
- bool cp_partial; /* partial copy into send buffer */
+ u8 cp_partial; /* partial copy into send buffer */
- u16 vlan_tci;
+ u8 rmsg_size; /* RNDIS header and PPI size */
+ u8 rmsg_pgcnt; /* page count of RNDIS header and PPI */
+ u8 page_buf_cnt;
u16 q_idx;
- struct vmbus_channel *channel;
-
- u64 send_completion_tid;
- void *send_completion_ctx;
- void (*send_completion)(void *context);
-
u32 send_buf_index;
- /* This points to the memory after page_buf */
- struct rndis_message *rndis_msg;
-
- u32 rmsg_size; /* RNDIS header and PPI size */
- u32 rmsg_pgcnt; /* page count of RNDIS header and PPI */
-
u32 total_data_buflen;
- /* Points to the send/receive buffer where the ethernet frame is */
- void *data;
- u32 page_buf_cnt;
- struct hv_page_buffer *page_buf;
};
struct netvsc_device_info {
@@ -177,7 +162,6 @@ struct rndis_device {
enum rndis_device_state state;
bool link_state;
- bool link_change;
atomic_t new_req_id;
spinlock_t request_lock;
@@ -188,16 +172,22 @@ struct rndis_device {
/* Interface */
+struct rndis_message;
int netvsc_device_add(struct hv_device *device, void *additional_info);
int netvsc_device_remove(struct hv_device *device);
int netvsc_send(struct hv_device *device,
- struct hv_netvsc_packet *packet);
+ struct hv_netvsc_packet *packet,
+ struct rndis_message *rndis_msg,
+ struct hv_page_buffer **page_buffer,
+ struct sk_buff *skb);
void netvsc_linkstatus_callback(struct hv_device *device_obj,
struct rndis_message *resp);
-void netvsc_xmit_completion(void *context);
int netvsc_recv_callback(struct hv_device *device_obj,
struct hv_netvsc_packet *packet,
- struct ndis_tcp_ip_checksum_info *csum_info);
+ void **data,
+ struct ndis_tcp_ip_checksum_info *csum_info,
+ struct vmbus_channel *channel,
+ u16 vlan_tci);
void netvsc_channel_cb(void *context);
int rndis_filter_open(struct hv_device *dev);
int rndis_filter_close(struct hv_device *dev);
@@ -205,12 +195,13 @@ int rndis_filter_device_add(struct hv_device *dev,
void *additional_info);
void rndis_filter_device_remove(struct hv_device *dev);
int rndis_filter_receive(struct hv_device *dev,
- struct hv_netvsc_packet *pkt);
+ struct hv_netvsc_packet *pkt,
+ void **data,
+ struct vmbus_channel *channel);
int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter);
int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac);
-
#define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF)
#define NVSP_PROTOCOL_VERSION_1 2
@@ -633,7 +624,6 @@ struct nvsp_message {
#define RNDIS_PKT_ALIGN_DEFAULT 8
struct multi_send_data {
- spinlock_t lock; /* protect struct multi_send_data */
struct hv_netvsc_packet *pkt; /* netvsc pkt pending */
u32 count; /* counter of batched packets */
};
@@ -644,11 +634,24 @@ struct netvsc_stats {
struct u64_stats_sync syncp;
};
+struct netvsc_reconfig {
+ struct list_head list;
+ u32 event;
+};
+
/* The context of the netvsc device */
struct net_device_context {
/* point back to our device context */
struct hv_device *device_ctx;
+ /* reconfigure work */
struct delayed_work dwork;
+ /* last reconfig time */
+ unsigned long last_reconfig;
+ /* reconfig events */
+ struct list_head reconfig_events;
+ /* list protection */
+ spinlock_t lock;
+
struct work_struct work;
u32 msg_enable; /* debug level */
@@ -1260,5 +1263,4 @@ struct rndis_message {
#define TRANSPORT_INFO_IPV6_TCP ((INFO_IPV6 << 16) | INFO_TCP)
#define TRANSPORT_INFO_IPV6_UDP ((INFO_IPV6 << 16) | INFO_UDP)
-
#endif /* _HYPERV_NET_H */
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 51e4c0fd0a74..059fc5231601 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -38,7 +38,6 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device)
{
struct netvsc_device *net_device;
struct net_device *ndev = hv_get_drvdata(device);
- int i;
net_device = kzalloc(sizeof(struct netvsc_device), GFP_KERNEL);
if (!net_device)
@@ -58,9 +57,6 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device)
net_device->max_pkt = RNDIS_MAX_PKT_DEFAULT;
net_device->pkt_align = RNDIS_PKT_ALIGN_DEFAULT;
- for (i = 0; i < num_online_cpus(); i++)
- spin_lock_init(&net_device->msd[i].lock);
-
hv_set_drvdata(device, net_device);
return net_device;
}
@@ -610,6 +606,7 @@ static inline void netvsc_free_send_slot(struct netvsc_device *net_device,
}
static void netvsc_send_completion(struct netvsc_device *net_device,
+ struct vmbus_channel *incoming_channel,
struct hv_device *device,
struct vmpacket_descriptor *packet)
{
@@ -617,6 +614,7 @@ static void netvsc_send_completion(struct netvsc_device *net_device,
struct hv_netvsc_packet *nvsc_packet;
struct net_device *ndev;
u32 send_index;
+ struct sk_buff *skb;
ndev = net_device->ndev;
@@ -642,18 +640,17 @@ static void netvsc_send_completion(struct netvsc_device *net_device,
int queue_sends;
/* Get the send context */
- nvsc_packet = (struct hv_netvsc_packet *)(unsigned long)
- packet->trans_id;
+ skb = (struct sk_buff *)(unsigned long)packet->trans_id;
/* Notify the layer above us */
- if (nvsc_packet) {
+ if (skb) {
+ nvsc_packet = (struct hv_netvsc_packet *) skb->cb;
send_index = nvsc_packet->send_buf_index;
if (send_index != NETVSC_INVALID_INDEX)
netvsc_free_send_slot(net_device, send_index);
q_idx = nvsc_packet->q_idx;
- channel = nvsc_packet->channel;
- nvsc_packet->send_completion(nvsc_packet->
- send_completion_ctx);
+ channel = incoming_channel;
+ dev_kfree_skb_any(skb);
}
num_outstanding_sends =
@@ -705,12 +702,17 @@ static u32 netvsc_get_next_send_section(struct netvsc_device *net_device)
static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device,
unsigned int section_index,
u32 pend_size,
- struct hv_netvsc_packet *packet)
+ struct hv_netvsc_packet *packet,
+ struct rndis_message *rndis_msg,
+ struct hv_page_buffer **pb,
+ struct sk_buff *skb)
{
char *start = net_device->send_buf;
char *dest = start + (section_index * net_device->send_section_size)
+ pend_size;
int i;
+ bool is_data_pkt = (skb != NULL) ? true : false;
+ bool xmit_more = (skb != NULL) ? skb->xmit_more : false;
u32 msg_size = 0;
u32 padding = 0;
u32 remain = packet->total_data_buflen % net_device->pkt_align;
@@ -718,17 +720,17 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device,
packet->page_buf_cnt;
/* Add padding */
- if (packet->is_data_pkt && packet->xmit_more && remain &&
+ if (is_data_pkt && xmit_more && remain &&
!packet->cp_partial) {
padding = net_device->pkt_align - remain;
- packet->rndis_msg->msg_len += padding;
+ rndis_msg->msg_len += padding;
packet->total_data_buflen += padding;
}
for (i = 0; i < page_count; i++) {
- char *src = phys_to_virt(packet->page_buf[i].pfn << PAGE_SHIFT);
- u32 offset = packet->page_buf[i].offset;
- u32 len = packet->page_buf[i].len;
+ char *src = phys_to_virt((*pb)[i].pfn << PAGE_SHIFT);
+ u32 offset = (*pb)[i].offset;
+ u32 len = (*pb)[i].len;
memcpy(dest, (src + offset), len);
msg_size += len;
@@ -745,19 +747,22 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device,
static inline int netvsc_send_pkt(
struct hv_netvsc_packet *packet,
- struct netvsc_device *net_device)
+ struct netvsc_device *net_device,
+ struct hv_page_buffer **pb,
+ struct sk_buff *skb)
{
struct nvsp_message nvmsg;
- struct vmbus_channel *out_channel = packet->channel;
u16 q_idx = packet->q_idx;
+ struct vmbus_channel *out_channel = net_device->chn_table[q_idx];
struct net_device *ndev = net_device->ndev;
u64 req_id;
int ret;
struct hv_page_buffer *pgbuf;
u32 ring_avail = hv_ringbuf_avail_percent(&out_channel->outbound);
+ bool xmit_more = (skb != NULL) ? skb->xmit_more : false;
nvmsg.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT;
- if (packet->is_data_pkt) {
+ if (skb != NULL) {
/* 0 is RMC_DATA; */
nvmsg.msg.v1_msg.send_rndis_pkt.channel_type = 0;
} else {
@@ -773,10 +778,7 @@ static inline int netvsc_send_pkt(
nvmsg.msg.v1_msg.send_rndis_pkt.send_buf_section_size =
packet->total_data_buflen;
- if (packet->send_completion)
- req_id = (ulong)packet;
- else
- req_id = 0;
+ req_id = (ulong)skb;
if (out_channel->rescind)
return -ENODEV;
@@ -789,11 +791,11 @@ static inline int netvsc_send_pkt(
* unnecessarily.
*/
if (ring_avail < (RING_AVAIL_PERCENT_LOWATER + 1))
- packet->xmit_more = false;
+ xmit_more = false;
if (packet->page_buf_cnt) {
- pgbuf = packet->cp_partial ? packet->page_buf +
- packet->rmsg_pgcnt : packet->page_buf;
+ pgbuf = packet->cp_partial ? (*pb) +
+ packet->rmsg_pgcnt : (*pb);
ret = vmbus_sendpacket_pagebuffer_ctl(out_channel,
pgbuf,
packet->page_buf_cnt,
@@ -801,14 +803,14 @@ static inline int netvsc_send_pkt(
sizeof(struct nvsp_message),
req_id,
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED,
- !packet->xmit_more);
+ !xmit_more);
} else {
ret = vmbus_sendpacket_ctl(out_channel, &nvmsg,
sizeof(struct nvsp_message),
req_id,
VM_PKT_DATA_INBAND,
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED,
- !packet->xmit_more);
+ !xmit_more);
}
if (ret == 0) {
@@ -840,7 +842,10 @@ static inline int netvsc_send_pkt(
}
int netvsc_send(struct hv_device *device,
- struct hv_netvsc_packet *packet)
+ struct hv_netvsc_packet *packet,
+ struct rndis_message *rndis_msg,
+ struct hv_page_buffer **pb,
+ struct sk_buff *skb)
{
struct netvsc_device *net_device;
int ret = 0, m_ret = 0;
@@ -848,33 +853,35 @@ int netvsc_send(struct hv_device *device,
u16 q_idx = packet->q_idx;
u32 pktlen = packet->total_data_buflen, msd_len = 0;
unsigned int section_index = NETVSC_INVALID_INDEX;
- unsigned long flag;
struct multi_send_data *msdp;
struct hv_netvsc_packet *msd_send = NULL, *cur_send = NULL;
bool try_batch;
+ bool xmit_more = (skb != NULL) ? skb->xmit_more : false;
net_device = get_outbound_net_device(device);
if (!net_device)
return -ENODEV;
out_channel = net_device->chn_table[q_idx];
- if (!out_channel) {
- out_channel = device->channel;
- q_idx = 0;
- packet->q_idx = 0;
- }
- packet->channel = out_channel;
+
packet->send_buf_index = NETVSC_INVALID_INDEX;
packet->cp_partial = false;
+ /* Send control message directly without accessing msd (Multi-Send
+ * Data) field which may be changed during data packet processing.
+ */
+ if (!skb) {
+ cur_send = packet;
+ goto send_now;
+ }
+
msdp = &net_device->msd[q_idx];
/* batch packets in send buffer if possible */
- spin_lock_irqsave(&msdp->lock, flag);
if (msdp->pkt)
msd_len = msdp->pkt->total_data_buflen;
- try_batch = packet->is_data_pkt && msd_len > 0 && msdp->count <
+ try_batch = (skb != NULL) && msd_len > 0 && msdp->count <
net_device->max_pkt;
if (try_batch && msd_len + pktlen + net_device->pkt_align <
@@ -886,7 +893,7 @@ int netvsc_send(struct hv_device *device,
section_index = msdp->pkt->send_buf_index;
packet->cp_partial = true;
- } else if (packet->is_data_pkt && pktlen + net_device->pkt_align <
+ } else if ((skb != NULL) && pktlen + net_device->pkt_align <
net_device->send_section_size) {
section_index = netvsc_get_next_send_section(net_device);
if (section_index != NETVSC_INVALID_INDEX) {
@@ -900,7 +907,7 @@ int netvsc_send(struct hv_device *device,
if (section_index != NETVSC_INVALID_INDEX) {
netvsc_copy_to_send_buf(net_device,
section_index, msd_len,
- packet);
+ packet, rndis_msg, pb, skb);
packet->send_buf_index = section_index;
@@ -913,9 +920,9 @@ int netvsc_send(struct hv_device *device,
}
if (msdp->pkt)
- netvsc_xmit_completion(msdp->pkt);
+ dev_kfree_skb_any(skb);
- if (packet->xmit_more && !packet->cp_partial) {
+ if (xmit_more && !packet->cp_partial) {
msdp->pkt = packet;
msdp->count++;
} else {
@@ -930,20 +937,19 @@ int netvsc_send(struct hv_device *device,
cur_send = packet;
}
- spin_unlock_irqrestore(&msdp->lock, flag);
-
if (msd_send) {
- m_ret = netvsc_send_pkt(msd_send, net_device);
+ m_ret = netvsc_send_pkt(msd_send, net_device, pb, skb);
if (m_ret != 0) {
netvsc_free_send_slot(net_device,
msd_send->send_buf_index);
- netvsc_xmit_completion(msd_send);
+ dev_kfree_skb_any(skb);
}
}
+send_now:
if (cur_send)
- ret = netvsc_send_pkt(cur_send, net_device);
+ ret = netvsc_send_pkt(cur_send, net_device, pb, skb);
if (ret != 0 && section_index != NETVSC_INVALID_INDEX)
netvsc_free_send_slot(net_device, section_index);
@@ -1009,6 +1015,7 @@ static void netvsc_receive(struct netvsc_device *net_device,
int i;
int count = 0;
struct net_device *ndev;
+ void *data;
ndev = net_device->ndev;
@@ -1043,22 +1050,19 @@ static void netvsc_receive(struct netvsc_device *net_device,
}
count = vmxferpage_packet->range_cnt;
- netvsc_packet->channel = channel;
/* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */
for (i = 0; i < count; i++) {
/* Initialize the netvsc packet */
- netvsc_packet->status = NVSP_STAT_SUCCESS;
- netvsc_packet->data = (void *)((unsigned long)net_device->
+ data = (void *)((unsigned long)net_device->
recv_buf + vmxferpage_packet->ranges[i].byte_offset);
netvsc_packet->total_data_buflen =
vmxferpage_packet->ranges[i].byte_count;
/* Pass it to the upper layer */
- rndis_filter_receive(device, netvsc_packet);
+ status = rndis_filter_receive(device, netvsc_packet, &data,
+ channel);
- if (netvsc_packet->status != NVSP_STAT_SUCCESS)
- status = NVSP_STAT_FAIL;
}
netvsc_send_recv_completion(device, channel, net_device,
@@ -1150,6 +1154,7 @@ void netvsc_channel_cb(void *context)
switch (desc->type) {
case VM_PKT_COMP:
netvsc_send_completion(net_device,
+ channel,
device, desc);
break;
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index 409b48e1e589..1c8db9afdcda 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -42,6 +42,7 @@
#define RING_SIZE_MIN 64
+#define LINKCHANGE_INT (2 * HZ)
static int ring_size = 128;
module_param(ring_size, int, S_IRUGO);
MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)");
@@ -272,17 +273,10 @@ static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb,
skb_set_hash(skb, hash, PKT_HASH_TYPE_L3);
}
- return q_idx;
-}
-
-void netvsc_xmit_completion(void *context)
-{
- struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context;
- struct sk_buff *skb = (struct sk_buff *)
- (unsigned long)packet->send_completion_tid;
+ if (!nvsc_dev->chn_table[q_idx])
+ q_idx = 0;
- if (skb)
- dev_kfree_skb_any(skb);
+ return q_idx;
}
static u32 fill_pg_buf(struct page *page, u32 offset, u32 len,
@@ -320,9 +314,10 @@ static u32 fill_pg_buf(struct page *page, u32 offset, u32 len,
}
static u32 init_page_array(void *hdr, u32 len, struct sk_buff *skb,
- struct hv_netvsc_packet *packet)
+ struct hv_netvsc_packet *packet,
+ struct hv_page_buffer **page_buf)
{
- struct hv_page_buffer *pb = packet->page_buf;
+ struct hv_page_buffer *pb = *page_buf;
u32 slots_used = 0;
char *data = skb->data;
int frags = skb_shinfo(skb)->nr_frags;
@@ -432,8 +427,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
u32 net_trans_info;
u32 hash;
u32 skb_length;
- u32 pkt_sz;
struct hv_page_buffer page_buf[MAX_PAGE_BUFFER_COUNT];
+ struct hv_page_buffer *pb = page_buf;
struct netvsc_stats *tx_stats = this_cpu_ptr(net_device_ctx->tx_stats);
/* We will atmost need two pages to describe the rndis
@@ -460,42 +455,34 @@ check_size:
goto check_size;
}
- pkt_sz = sizeof(struct hv_netvsc_packet) + RNDIS_AND_PPI_SIZE;
-
- ret = skb_cow_head(skb, pkt_sz);
+ /*
+ * Place the rndis header in the skb head room and
+ * the skb->cb will be used for hv_netvsc_packet
+ * structure.
+ */
+ ret = skb_cow_head(skb, RNDIS_AND_PPI_SIZE);
if (ret) {
netdev_err(net, "unable to alloc hv_netvsc_packet\n");
ret = -ENOMEM;
goto drop;
}
- /* Use the headroom for building up the packet */
- packet = (struct hv_netvsc_packet *)skb->head;
+ /* Use the skb control buffer for building up the packet */
+ BUILD_BUG_ON(sizeof(struct hv_netvsc_packet) >
+ FIELD_SIZEOF(struct sk_buff, cb));
+ packet = (struct hv_netvsc_packet *)skb->cb;
- packet->status = 0;
- packet->xmit_more = skb->xmit_more;
-
- packet->vlan_tci = skb->vlan_tci;
- packet->page_buf = page_buf;
packet->q_idx = skb_get_queue_mapping(skb);
- packet->is_data_pkt = true;
packet->total_data_buflen = skb->len;
- packet->rndis_msg = (struct rndis_message *)((unsigned long)packet +
- sizeof(struct hv_netvsc_packet));
-
- memset(packet->rndis_msg, 0, RNDIS_AND_PPI_SIZE);
+ rndis_msg = (struct rndis_message *)skb->head;
- /* Set the completion routine */
- packet->send_completion = netvsc_xmit_completion;
- packet->send_completion_ctx = packet;
- packet->send_completion_tid = (unsigned long)skb;
+ memset(rndis_msg, 0, RNDIS_AND_PPI_SIZE);
- isvlan = packet->vlan_tci & VLAN_TAG_PRESENT;
+ isvlan = skb->vlan_tci & VLAN_TAG_PRESENT;
/* Add the rndis header */
- rndis_msg = packet->rndis_msg;
rndis_msg->ndis_msg_type = RNDIS_MSG_PACKET;
rndis_msg->msg_len = packet->total_data_buflen;
rndis_pkt = &rndis_msg->msg.pkt;
@@ -521,8 +508,8 @@ check_size:
IEEE_8021Q_INFO);
vlan = (struct ndis_pkt_8021q_info *)((void *)ppi +
ppi->ppi_offset);
- vlan->vlanid = packet->vlan_tci & VLAN_VID_MASK;
- vlan->pri = (packet->vlan_tci & VLAN_PRIO_MASK) >>
+ vlan->vlanid = skb->vlan_tci & VLAN_VID_MASK;
+ vlan->pri = (skb->vlan_tci & VLAN_PRIO_MASK) >>
VLAN_PRIO_SHIFT;
}
@@ -617,9 +604,10 @@ do_send:
rndis_msg->msg_len += rndis_msg_size;
packet->total_data_buflen = rndis_msg->msg_len;
packet->page_buf_cnt = init_page_array(rndis_msg, rndis_msg_size,
- skb, packet);
+ skb, packet, &pb);
- ret = netvsc_send(net_device_ctx->device_ctx, packet);
+ ret = netvsc_send(net_device_ctx->device_ctx, packet,
+ rndis_msg, &pb, skb);
drop:
if (ret == 0) {
@@ -647,37 +635,33 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj,
struct net_device *net;
struct net_device_context *ndev_ctx;
struct netvsc_device *net_device;
- struct rndis_device *rdev;
-
- net_device = hv_get_drvdata(device_obj);
- rdev = net_device->extension;
+ struct netvsc_reconfig *event;
+ unsigned long flags;
- switch (indicate->status) {
- case RNDIS_STATUS_MEDIA_CONNECT:
- rdev->link_state = false;
- break;
- case RNDIS_STATUS_MEDIA_DISCONNECT:
- rdev->link_state = true;
- break;
- case RNDIS_STATUS_NETWORK_CHANGE:
- rdev->link_change = true;
- break;
- default:
+ /* Handle link change statuses only */
+ if (indicate->status != RNDIS_STATUS_NETWORK_CHANGE &&
+ indicate->status != RNDIS_STATUS_MEDIA_CONNECT &&
+ indicate->status != RNDIS_STATUS_MEDIA_DISCONNECT)
return;
- }
+ net_device = hv_get_drvdata(device_obj);
net = net_device->ndev;
if (!net || net->reg_state != NETREG_REGISTERED)
return;
ndev_ctx = netdev_priv(net);
- if (!rdev->link_state) {
- schedule_delayed_work(&ndev_ctx->dwork, 0);
- schedule_delayed_work(&ndev_ctx->dwork, msecs_to_jiffies(20));
- } else {
- schedule_delayed_work(&ndev_ctx->dwork, 0);
- }
+
+ event = kzalloc(sizeof(*event), GFP_ATOMIC);
+ if (!event)
+ return;
+ event->event = indicate->status;
+
+ spin_lock_irqsave(&ndev_ctx->lock, flags);
+ list_add_tail(&event->list, &ndev_ctx->reconfig_events);
+ spin_unlock_irqrestore(&ndev_ctx->lock, flags);
+
+ schedule_delayed_work(&ndev_ctx->dwork, 0);
}
/*
@@ -686,7 +670,10 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj,
*/
int netvsc_recv_callback(struct hv_device *device_obj,
struct hv_netvsc_packet *packet,
- struct ndis_tcp_ip_checksum_info *csum_info)
+ void **data,
+ struct ndis_tcp_ip_checksum_info *csum_info,
+ struct vmbus_channel *channel,
+ u16 vlan_tci)
{
struct net_device *net;
struct net_device_context *net_device_ctx;
@@ -695,8 +682,7 @@ int netvsc_recv_callback(struct hv_device *device_obj,
net = ((struct netvsc_device *)hv_get_drvdata(device_obj))->ndev;
if (!net || net->reg_state != NETREG_REGISTERED) {
- packet->status = NVSP_STAT_FAIL;
- return 0;
+ return NVSP_STAT_FAIL;
}
net_device_ctx = netdev_priv(net);
rx_stats = this_cpu_ptr(net_device_ctx->rx_stats);
@@ -705,15 +691,14 @@ int netvsc_recv_callback(struct hv_device *device_obj,
skb = netdev_alloc_skb_ip_align(net, packet->total_data_buflen);
if (unlikely(!skb)) {
++net->stats.rx_dropped;
- packet->status = NVSP_STAT_FAIL;
- return 0;
+ return NVSP_STAT_FAIL;
}
/*
* Copy to skb. This copy is needed here since the memory pointed by
* hv_netvsc_packet cannot be deallocated
*/
- memcpy(skb_put(skb, packet->total_data_buflen), packet->data,
+ memcpy(skb_put(skb, packet->total_data_buflen), *data,
packet->total_data_buflen);
skb->protocol = eth_type_trans(skb, net);
@@ -728,11 +713,11 @@ int netvsc_recv_callback(struct hv_device *device_obj,
skb->ip_summed = CHECKSUM_NONE;
}
- if (packet->vlan_tci & VLAN_TAG_PRESENT)
+ if (vlan_tci & VLAN_TAG_PRESENT)
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
- packet->vlan_tci);
+ vlan_tci);
- skb_record_rx_queue(skb, packet->channel->
+ skb_record_rx_queue(skb, channel->
offermsg.offer.sub_channel_index);
u64_stats_update_begin(&rx_stats->syncp);
@@ -1009,12 +994,9 @@ static const struct net_device_ops device_ops = {
};
/*
- * Send GARP packet to network peers after migrations.
- * After Quick Migration, the network is not immediately operational in the
- * current context when receiving RNDIS_STATUS_MEDIA_CONNECT event. So, add
- * another netif_notify_peers() into a delayed work, otherwise GARP packet
- * will not be sent after quick migration, and cause network disconnection.
- * Also, we update the carrier status here.
+ * Handle link status changes. For RNDIS_STATUS_NETWORK_CHANGE emulate link
+ * down/up sequence. In case of RNDIS_STATUS_MEDIA_CONNECT when carrier is
+ * present send GARP packet to network peers with netif_notify_peers().
*/
static void netvsc_link_change(struct work_struct *w)
{
@@ -1022,36 +1004,89 @@ static void netvsc_link_change(struct work_struct *w)
struct net_device *net;
struct netvsc_device *net_device;
struct rndis_device *rdev;
- bool notify, refresh = false;
- char *argv[] = { "/etc/init.d/network", "restart", NULL };
- char *envp[] = { "HOME=/", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
-
- rtnl_lock();
+ struct netvsc_reconfig *event = NULL;
+ bool notify = false, reschedule = false;
+ unsigned long flags, next_reconfig, delay;
ndev_ctx = container_of(w, struct net_device_context, dwork.work);
net_device = hv_get_drvdata(ndev_ctx->device_ctx);
rdev = net_device->extension;
net = net_device->ndev;
- if (rdev->link_state) {
- netif_carrier_off(net);
- notify = false;
- } else {
- netif_carrier_on(net);
- notify = true;
- if (rdev->link_change) {
- rdev->link_change = false;
- refresh = true;
+ next_reconfig = ndev_ctx->last_reconfig + LINKCHANGE_INT;
+ if (time_is_after_jiffies(next_reconfig)) {
+ /* link_watch only sends one notification with current state
+ * per second, avoid doing reconfig more frequently. Handle
+ * wrap around.
+ */
+ delay = next_reconfig - jiffies;
+ delay = delay < LINKCHANGE_INT ? delay : LINKCHANGE_INT;
+ schedule_delayed_work(&ndev_ctx->dwork, delay);
+ return;
+ }
+ ndev_ctx->last_reconfig = jiffies;
+
+ spin_lock_irqsave(&ndev_ctx->lock, flags);
+ if (!list_empty(&ndev_ctx->reconfig_events)) {
+ event = list_first_entry(&ndev_ctx->reconfig_events,
+ struct netvsc_reconfig, list);
+ list_del(&event->list);
+ reschedule = !list_empty(&ndev_ctx->reconfig_events);
+ }
+ spin_unlock_irqrestore(&ndev_ctx->lock, flags);
+
+ if (!event)
+ return;
+
+ rtnl_lock();
+
+ switch (event->event) {
+ /* Only the following events are possible due to the check in
+ * netvsc_linkstatus_callback()
+ */
+ case RNDIS_STATUS_MEDIA_CONNECT:
+ if (rdev->link_state) {
+ rdev->link_state = false;
+ netif_carrier_on(net);
+ netif_tx_wake_all_queues(net);
+ } else {
+ notify = true;
+ }
+ kfree(event);
+ break;
+ case RNDIS_STATUS_MEDIA_DISCONNECT:
+ if (!rdev->link_state) {
+ rdev->link_state = true;
+ netif_carrier_off(net);
+ netif_tx_stop_all_queues(net);
+ }
+ kfree(event);
+ break;
+ case RNDIS_STATUS_NETWORK_CHANGE:
+ /* Only makes sense if carrier is present */
+ if (!rdev->link_state) {
+ rdev->link_state = true;
+ netif_carrier_off(net);
+ netif_tx_stop_all_queues(net);
+ event->event = RNDIS_STATUS_MEDIA_CONNECT;
+ spin_lock_irqsave(&ndev_ctx->lock, flags);
+ list_add_tail(&event->list, &ndev_ctx->reconfig_events);
+ spin_unlock_irqrestore(&ndev_ctx->lock, flags);
+ reschedule = true;
}
+ break;
}
rtnl_unlock();
- if (refresh)
- call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
-
if (notify)
netdev_notify_peers(net);
+
+ /* link_watch only sends one notification with current state per
+ * second, handle next reconfig event in 2 seconds.
+ */
+ if (reschedule)
+ schedule_delayed_work(&ndev_ctx->dwork, LINKCHANGE_INT);
}
static void netvsc_free_netdev(struct net_device *netdev)
@@ -1071,16 +1106,12 @@ static int netvsc_probe(struct hv_device *dev,
struct netvsc_device_info device_info;
struct netvsc_device *nvdev;
int ret;
- u32 max_needed_headroom;
net = alloc_etherdev_mq(sizeof(struct net_device_context),
num_online_cpus());
if (!net)
return -ENOMEM;
- max_needed_headroom = sizeof(struct hv_netvsc_packet) +
- RNDIS_AND_PPI_SIZE;
-
netif_carrier_off(net);
net_device_ctx = netdev_priv(net);
@@ -1106,6 +1137,9 @@ static int netvsc_probe(struct hv_device *dev,
INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_link_change);
INIT_WORK(&net_device_ctx->work, do_set_multicast);
+ spin_lock_init(&net_device_ctx->lock);
+ INIT_LIST_HEAD(&net_device_ctx->reconfig_events);
+
net->netdev_ops = &device_ops;
net->hw_features = NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_IP_CSUM |
@@ -1116,13 +1150,6 @@ static int netvsc_probe(struct hv_device *dev,
net->ethtool_ops = &ethtool_ops;
SET_NETDEV_DEV(net, &dev->device);
- /*
- * Request additional head room in the skb.
- * We will use this space to build the rndis
- * heaser and other state we need to maintain.
- */
- net->needed_headroom = max_needed_headroom;
-
/* Notify the netvsc driver of the new device */
memset(&device_info, 0, sizeof(device_info));
device_info.ring_size = ring_size;
@@ -1145,8 +1172,6 @@ static int netvsc_probe(struct hv_device *dev,
pr_err("Unable to register netdev.\n");
rndis_filter_device_remove(dev);
netvsc_free_netdev(net);
- } else {
- schedule_delayed_work(&net_device_ctx->dwork, 0);
}
return ret;
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c
index 5931a799aa17..a37bbda37ffa 100644
--- a/drivers/net/hyperv/rndis_filter.c
+++ b/drivers/net/hyperv/rndis_filter.c
@@ -210,37 +210,33 @@ static int rndis_filter_send_request(struct rndis_device *dev,
int ret;
struct hv_netvsc_packet *packet;
struct hv_page_buffer page_buf[2];
+ struct hv_page_buffer *pb = page_buf;
/* Setup the packet to send it */
packet = &req->pkt;
- packet->is_data_pkt = false;
packet->total_data_buflen = req->request_msg.msg_len;
packet->page_buf_cnt = 1;
- packet->page_buf = page_buf;
- packet->page_buf[0].pfn = virt_to_phys(&req->request_msg) >>
+ pb[0].pfn = virt_to_phys(&req->request_msg) >>
PAGE_SHIFT;
- packet->page_buf[0].len = req->request_msg.msg_len;
- packet->page_buf[0].offset =
+ pb[0].len = req->request_msg.msg_len;
+ pb[0].offset =
(unsigned long)&req->request_msg & (PAGE_SIZE - 1);
/* Add one page_buf when request_msg crossing page boundary */
- if (packet->page_buf[0].offset + packet->page_buf[0].len > PAGE_SIZE) {
+ if (pb[0].offset + pb[0].len > PAGE_SIZE) {
packet->page_buf_cnt++;
- packet->page_buf[0].len = PAGE_SIZE -
- packet->page_buf[0].offset;
- packet->page_buf[1].pfn = virt_to_phys((void *)&req->request_msg
- + packet->page_buf[0].len) >> PAGE_SHIFT;
- packet->page_buf[1].offset = 0;
- packet->page_buf[1].len = req->request_msg.msg_len -
- packet->page_buf[0].len;
+ pb[0].len = PAGE_SIZE -
+ pb[0].offset;
+ pb[1].pfn = virt_to_phys((void *)&req->request_msg
+ + pb[0].len) >> PAGE_SHIFT;
+ pb[1].offset = 0;
+ pb[1].len = req->request_msg.msg_len -
+ pb[0].len;
}
- packet->send_completion = NULL;
- packet->xmit_more = false;
-
- ret = netvsc_send(dev->net_dev->dev, packet);
+ ret = netvsc_send(dev->net_dev->dev, packet, NULL, &pb, NULL);
return ret;
}
@@ -348,14 +344,17 @@ static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type)
return NULL;
}
-static void rndis_filter_receive_data(struct rndis_device *dev,
+static int rndis_filter_receive_data(struct rndis_device *dev,
struct rndis_message *msg,
- struct hv_netvsc_packet *pkt)
+ struct hv_netvsc_packet *pkt,
+ void **data,
+ struct vmbus_channel *channel)
{
struct rndis_packet *rndis_pkt;
u32 data_offset;
struct ndis_pkt_8021q_info *vlan;
struct ndis_tcp_ip_checksum_info *csum_info;
+ u16 vlan_tci = 0;
rndis_pkt = &msg->msg.pkt;
@@ -373,7 +372,7 @@ static void rndis_filter_receive_data(struct rndis_device *dev,
"overflow detected (got %u, min %u)"
"...dropping this message!\n",
pkt->total_data_buflen, rndis_pkt->data_len);
- return;
+ return NVSP_STAT_FAIL;
}
/*
@@ -382,22 +381,23 @@ static void rndis_filter_receive_data(struct rndis_device *dev,
* the data packet to the stack, without the rndis trailer padding
*/
pkt->total_data_buflen = rndis_pkt->data_len;
- pkt->data = (void *)((unsigned long)pkt->data + data_offset);
+ *data = (void *)((unsigned long)(*data) + data_offset);
vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO);
if (vlan) {
- pkt->vlan_tci = VLAN_TAG_PRESENT | vlan->vlanid |
+ vlan_tci = VLAN_TAG_PRESENT | vlan->vlanid |
(vlan->pri << VLAN_PRIO_SHIFT);
- } else {
- pkt->vlan_tci = 0;
}
csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO);
- netvsc_recv_callback(dev->net_dev->dev, pkt, csum_info);
+ return netvsc_recv_callback(dev->net_dev->dev, pkt, data,
+ csum_info, channel, vlan_tci);
}
int rndis_filter_receive(struct hv_device *dev,
- struct hv_netvsc_packet *pkt)
+ struct hv_netvsc_packet *pkt,
+ void **data,
+ struct vmbus_channel *channel)
{
struct netvsc_device *net_dev = hv_get_drvdata(dev);
struct rndis_device *rndis_dev;
@@ -406,7 +406,7 @@ int rndis_filter_receive(struct hv_device *dev,
int ret = 0;
if (!net_dev) {
- ret = -EINVAL;
+ ret = NVSP_STAT_FAIL;
goto exit;
}
@@ -416,7 +416,7 @@ int rndis_filter_receive(struct hv_device *dev,
if (!net_dev->extension) {
netdev_err(ndev, "got rndis message but no rndis device - "
"dropping this message!\n");
- ret = -ENODEV;
+ ret = NVSP_STAT_FAIL;
goto exit;
}
@@ -424,11 +424,11 @@ int rndis_filter_receive(struct hv_device *dev,
if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED) {
netdev_err(ndev, "got rndis message but rndis device "
"uninitialized...dropping this message!\n");
- ret = -ENODEV;
+ ret = NVSP_STAT_FAIL;
goto exit;
}
- rndis_msg = pkt->data;
+ rndis_msg = *data;
if (netif_msg_rx_err(net_dev->nd_ctx))
dump_rndis_message(dev, rndis_msg);
@@ -436,7 +436,8 @@ int rndis_filter_receive(struct hv_device *dev,
switch (rndis_msg->ndis_msg_type) {
case RNDIS_MSG_PACKET:
/* data msg */
- rndis_filter_receive_data(rndis_dev, rndis_msg, pkt);
+ ret = rndis_filter_receive_data(rndis_dev, rndis_msg, pkt,
+ data, channel);
break;
case RNDIS_MSG_INIT_C:
@@ -459,9 +460,6 @@ int rndis_filter_receive(struct hv_device *dev,
}
exit:
- if (ret != 0)
- pkt->status = NVSP_STAT_FAIL;
-
return ret;
}
diff --git a/drivers/net/ieee802154/Kconfig b/drivers/net/ieee802154/Kconfig
index ce5f1a21e6d7..3057a8df4ce9 100644
--- a/drivers/net/ieee802154/Kconfig
+++ b/drivers/net/ieee802154/Kconfig
@@ -71,3 +71,14 @@ config IEEE802154_ATUSB
This driver can also be built as a module. To do so say M here.
The module will be called 'atusb'.
+
+config IEEE802154_ADF7242
+ tristate "ADF7242 transceiver driver"
+ depends on IEEE802154_DRIVERS && MAC802154
+ depends on SPI
+ ---help---
+ Say Y here to enable the ADF7242 SPI 802.15.4 wireless
+ controller.
+
+ This driver can also be built as a module. To do so, say M here.
+ the module will be called 'adf7242'.
diff --git a/drivers/net/ieee802154/Makefile b/drivers/net/ieee802154/Makefile
index cf1d2a6db023..3a923d339497 100644
--- a/drivers/net/ieee802154/Makefile
+++ b/drivers/net/ieee802154/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_IEEE802154_AT86RF230) += at86rf230.o
obj-$(CONFIG_IEEE802154_MRF24J40) += mrf24j40.o
obj-$(CONFIG_IEEE802154_CC2520) += cc2520.o
obj-$(CONFIG_IEEE802154_ATUSB) += atusb.o
+obj-$(CONFIG_IEEE802154_ADF7242) += adf7242.o
diff --git a/drivers/net/ieee802154/adf7242.c b/drivers/net/ieee802154/adf7242.c
new file mode 100644
index 000000000000..89154c079788
--- /dev/null
+++ b/drivers/net/ieee802154/adf7242.c
@@ -0,0 +1,1285 @@
+/*
+ * Analog Devices ADF7242 Low-Power IEEE 802.15.4 Transceiver
+ *
+ * Copyright 2009-2015 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ *
+ * http://www.analog.com/ADF7242
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/firmware.h>
+#include <linux/spi/spi.h>
+#include <linux/skbuff.h>
+#include <linux/of.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/bitops.h>
+#include <linux/ieee802154.h>
+#include <net/mac802154.h>
+#include <net/cfg802154.h>
+
+#define FIRMWARE "adf7242_firmware.bin"
+#define MAX_POLL_LOOPS 200
+
+/* All Registers */
+
+#define REG_EXT_CTRL 0x100 /* RW External LNA/PA and internal PA control */
+#define REG_TX_FSK_TEST 0x101 /* RW TX FSK test mode configuration */
+#define REG_CCA1 0x105 /* RW RSSI threshold for CCA */
+#define REG_CCA2 0x106 /* RW CCA mode configuration */
+#define REG_BUFFERCFG 0x107 /* RW RX_BUFFER overwrite control */
+#define REG_PKT_CFG 0x108 /* RW FCS evaluation configuration */
+#define REG_DELAYCFG0 0x109 /* RW RC_RX command to SFD or sync word delay */
+#define REG_DELAYCFG1 0x10A /* RW RC_TX command to TX state */
+#define REG_DELAYCFG2 0x10B /* RW Mac delay extension */
+#define REG_SYNC_WORD0 0x10C /* RW sync word bits [7:0] of [23:0] */
+#define REG_SYNC_WORD1 0x10D /* RW sync word bits [15:8] of [23:0] */
+#define REG_SYNC_WORD2 0x10E /* RW sync word bits [23:16] of [23:0] */
+#define REG_SYNC_CONFIG 0x10F /* RW sync word configuration */
+#define REG_RC_CFG 0x13E /* RW RX / TX packet configuration */
+#define REG_RC_VAR44 0x13F /* RW RESERVED */
+#define REG_CH_FREQ0 0x300 /* RW Channel Frequency Settings - Low */
+#define REG_CH_FREQ1 0x301 /* RW Channel Frequency Settings - Middle */
+#define REG_CH_FREQ2 0x302 /* RW Channel Frequency Settings - High */
+#define REG_TX_FD 0x304 /* RW TX Frequency Deviation Register */
+#define REG_DM_CFG0 0x305 /* RW RX Discriminator BW Register */
+#define REG_TX_M 0x306 /* RW TX Mode Register */
+#define REG_RX_M 0x307 /* RW RX Mode Register */
+#define REG_RRB 0x30C /* R RSSI Readback Register */
+#define REG_LRB 0x30D /* R Link Quality Readback Register */
+#define REG_DR0 0x30E /* RW bits [15:8] of [15:0] data rate setting */
+#define REG_DR1 0x30F /* RW bits [7:0] of [15:0] data rate setting */
+#define REG_PRAMPG 0x313 /* RW RESERVED */
+#define REG_TXPB 0x314 /* RW TX Packet Storage Base Address */
+#define REG_RXPB 0x315 /* RW RX Packet Storage Base Address */
+#define REG_TMR_CFG0 0x316 /* RW Wake up Timer Conf Register - High */
+#define REG_TMR_CFG1 0x317 /* RW Wake up Timer Conf Register - Low */
+#define REG_TMR_RLD0 0x318 /* RW Wake up Timer Value Register - High */
+#define REG_TMR_RLD1 0x319 /* RW Wake up Timer Value Register - Low */
+#define REG_TMR_CTRL 0x31A /* RW Wake up Timer Timeout flag */
+#define REG_PD_AUX 0x31E /* RW Battmon enable */
+#define REG_GP_CFG 0x32C /* RW GPIO Configuration */
+#define REG_GP_OUT 0x32D /* RW GPIO Configuration */
+#define REG_GP_IN 0x32E /* R GPIO Configuration */
+#define REG_SYNT 0x335 /* RW bandwidth calibration timers */
+#define REG_CAL_CFG 0x33D /* RW Calibration Settings */
+#define REG_PA_BIAS 0x36E /* RW PA BIAS */
+#define REG_SYNT_CAL 0x371 /* RW Oscillator and Doubler Configuration */
+#define REG_IIRF_CFG 0x389 /* RW BB Filter Decimation Rate */
+#define REG_CDR_CFG 0x38A /* RW CDR kVCO */
+#define REG_DM_CFG1 0x38B /* RW Postdemodulator Filter */
+#define REG_AGCSTAT 0x38E /* R RXBB Ref Osc Calibration Engine Readback */
+#define REG_RXCAL0 0x395 /* RW RX BB filter tuning, LSB */
+#define REG_RXCAL1 0x396 /* RW RX BB filter tuning, MSB */
+#define REG_RXFE_CFG 0x39B /* RW RXBB Ref Osc & RXFE Calibration */
+#define REG_PA_RR 0x3A7 /* RW Set PA ramp rate */
+#define REG_PA_CFG 0x3A8 /* RW PA enable */
+#define REG_EXTPA_CFG 0x3A9 /* RW External PA BIAS DAC */
+#define REG_EXTPA_MSC 0x3AA /* RW PA Bias Mode */
+#define REG_ADC_RBK 0x3AE /* R Readback temp */
+#define REG_AGC_CFG1 0x3B2 /* RW GC Parameters */
+#define REG_AGC_MAX 0x3B4 /* RW Slew rate */
+#define REG_AGC_CFG2 0x3B6 /* RW RSSI Parameters */
+#define REG_AGC_CFG3 0x3B7 /* RW RSSI Parameters */
+#define REG_AGC_CFG4 0x3B8 /* RW RSSI Parameters */
+#define REG_AGC_CFG5 0x3B9 /* RW RSSI & NDEC Parameters */
+#define REG_AGC_CFG6 0x3BA /* RW NDEC Parameters */
+#define REG_OCL_CFG1 0x3C4 /* RW OCL System Parameters */
+#define REG_IRQ1_EN0 0x3C7 /* RW Interrupt Mask set bits for IRQ1 */
+#define REG_IRQ1_EN1 0x3C8 /* RW Interrupt Mask set bits for IRQ1 */
+#define REG_IRQ2_EN0 0x3C9 /* RW Interrupt Mask set bits for IRQ2 */
+#define REG_IRQ2_EN1 0x3CA /* RW Interrupt Mask set bits for IRQ2 */
+#define REG_IRQ1_SRC0 0x3CB /* RW Interrupt Source bits for IRQ */
+#define REG_IRQ1_SRC1 0x3CC /* RW Interrupt Source bits for IRQ */
+#define REG_OCL_BW0 0x3D2 /* RW OCL System Parameters */
+#define REG_OCL_BW1 0x3D3 /* RW OCL System Parameters */
+#define REG_OCL_BW2 0x3D4 /* RW OCL System Parameters */
+#define REG_OCL_BW3 0x3D5 /* RW OCL System Parameters */
+#define REG_OCL_BW4 0x3D6 /* RW OCL System Parameters */
+#define REG_OCL_BWS 0x3D7 /* RW OCL System Parameters */
+#define REG_OCL_CFG13 0x3E0 /* RW OCL System Parameters */
+#define REG_GP_DRV 0x3E3 /* RW I/O pads Configuration and bg trim */
+#define REG_BM_CFG 0x3E6 /* RW Batt. Monitor Threshold Voltage setting */
+#define REG_SFD_15_4 0x3F4 /* RW Option to set non standard SFD */
+#define REG_AFC_CFG 0x3F7 /* RW AFC mode and polarity */
+#define REG_AFC_KI_KP 0x3F8 /* RW AFC ki and kp */
+#define REG_AFC_RANGE 0x3F9 /* RW AFC range */
+#define REG_AFC_READ 0x3FA /* RW Readback frequency error */
+
+/* REG_EXTPA_MSC */
+#define PA_PWR(x) (((x) & 0xF) << 4)
+#define EXTPA_BIAS_SRC BIT(3)
+#define EXTPA_BIAS_MODE(x) (((x) & 0x7) << 0)
+
+/* REG_PA_CFG */
+#define PA_BRIDGE_DBIAS(x) (((x) & 0x1F) << 0)
+#define PA_DBIAS_HIGH_POWER 21
+#define PA_DBIAS_LOW_POWER 13
+
+/* REG_PA_BIAS */
+#define PA_BIAS_CTRL(x) (((x) & 0x1F) << 1)
+#define REG_PA_BIAS_DFL BIT(0)
+#define PA_BIAS_HIGH_POWER 63
+#define PA_BIAS_LOW_POWER 55
+
+#define REG_PAN_ID0 0x112
+#define REG_PAN_ID1 0x113
+#define REG_SHORT_ADDR_0 0x114
+#define REG_SHORT_ADDR_1 0x115
+#define REG_IEEE_ADDR_0 0x116
+#define REG_IEEE_ADDR_1 0x117
+#define REG_IEEE_ADDR_2 0x118
+#define REG_IEEE_ADDR_3 0x119
+#define REG_IEEE_ADDR_4 0x11A
+#define REG_IEEE_ADDR_5 0x11B
+#define REG_IEEE_ADDR_6 0x11C
+#define REG_IEEE_ADDR_7 0x11D
+#define REG_FFILT_CFG 0x11E
+#define REG_AUTO_CFG 0x11F
+#define REG_AUTO_TX1 0x120
+#define REG_AUTO_TX2 0x121
+#define REG_AUTO_STATUS 0x122
+
+/* REG_FFILT_CFG */
+#define ACCEPT_BEACON_FRAMES BIT(0)
+#define ACCEPT_DATA_FRAMES BIT(1)
+#define ACCEPT_ACK_FRAMES BIT(2)
+#define ACCEPT_MACCMD_FRAMES BIT(3)
+#define ACCEPT_RESERVED_FRAMES BIT(4)
+#define ACCEPT_ALL_ADDRESS BIT(5)
+
+/* REG_AUTO_CFG */
+#define AUTO_ACK_FRAMEPEND BIT(0)
+#define IS_PANCOORD BIT(1)
+#define RX_AUTO_ACK_EN BIT(3)
+#define CSMA_CA_RX_TURNAROUND BIT(4)
+
+/* REG_AUTO_TX1 */
+#define MAX_FRAME_RETRIES(x) ((x) & 0xF)
+#define MAX_CCA_RETRIES(x) (((x) & 0x7) << 4)
+
+/* REG_AUTO_TX2 */
+#define CSMA_MAX_BE(x) ((x) & 0xF)
+#define CSMA_MIN_BE(x) (((x) & 0xF) << 4)
+
+#define CMD_SPI_NOP 0xFF /* No operation. Use for dummy writes */
+#define CMD_SPI_PKT_WR 0x10 /* Write telegram to the Packet RAM
+ * starting from the TX packet base address
+ * pointer tx_packet_base
+ */
+#define CMD_SPI_PKT_RD 0x30 /* Read telegram from the Packet RAM
+ * starting from RX packet base address
+ * pointer rxpb.rx_packet_base
+ */
+#define CMD_SPI_MEM_WR(x) (0x18 + (x >> 8)) /* Write data to MCR or
+ * Packet RAM sequentially
+ */
+#define CMD_SPI_MEM_RD(x) (0x38 + (x >> 8)) /* Read data from MCR or
+ * Packet RAM sequentially
+ */
+#define CMD_SPI_MEMR_WR(x) (0x08 + (x >> 8)) /* Write data to MCR or Packet
+ * RAM as random block
+ */
+#define CMD_SPI_MEMR_RD(x) (0x28 + (x >> 8)) /* Read data from MCR or
+ * Packet RAM random block
+ */
+#define CMD_SPI_PRAM_WR 0x1E /* Write data sequentially to current
+ * PRAM page selected
+ */
+#define CMD_SPI_PRAM_RD 0x3E /* Read data sequentially from current
+ * PRAM page selected
+ */
+#define CMD_RC_SLEEP 0xB1 /* Invoke transition of radio controller
+ * into SLEEP state
+ */
+#define CMD_RC_IDLE 0xB2 /* Invoke transition of radio controller
+ * into IDLE state
+ */
+#define CMD_RC_PHY_RDY 0xB3 /* Invoke transition of radio controller
+ * into PHY_RDY state
+ */
+#define CMD_RC_RX 0xB4 /* Invoke transition of radio controller
+ * into RX state
+ */
+#define CMD_RC_TX 0xB5 /* Invoke transition of radio controller
+ * into TX state
+ */
+#define CMD_RC_MEAS 0xB6 /* Invoke transition of radio controller
+ * into MEAS state
+ */
+#define CMD_RC_CCA 0xB7 /* Invoke Clear channel assessment */
+#define CMD_RC_CSMACA 0xC1 /* initiates CSMA-CA channel access
+ * sequence and frame transmission
+ */
+#define CMD_RC_PC_RESET 0xC7 /* Program counter reset */
+#define CMD_RC_RESET 0xC8 /* Resets the ADF7242 and puts it in
+ * the sleep state
+ */
+#define CMD_RC_PC_RESET_NO_WAIT (CMD_RC_PC_RESET | BIT(31))
+
+/* STATUS */
+
+#define STAT_SPI_READY BIT(7)
+#define STAT_IRQ_STATUS BIT(6)
+#define STAT_RC_READY BIT(5)
+#define STAT_CCA_RESULT BIT(4)
+#define RC_STATUS_IDLE 1
+#define RC_STATUS_MEAS 2
+#define RC_STATUS_PHY_RDY 3
+#define RC_STATUS_RX 4
+#define RC_STATUS_TX 5
+#define RC_STATUS_MASK 0xF
+
+/* AUTO_STATUS */
+
+#define SUCCESS 0
+#define SUCCESS_DATPEND 1
+#define FAILURE_CSMACA 2
+#define FAILURE_NOACK 3
+#define AUTO_STATUS_MASK 0x3
+
+#define PRAM_PAGESIZE 256
+
+/* IRQ1 */
+
+#define IRQ_CCA_COMPLETE BIT(0)
+#define IRQ_SFD_RX BIT(1)
+#define IRQ_SFD_TX BIT(2)
+#define IRQ_RX_PKT_RCVD BIT(3)
+#define IRQ_TX_PKT_SENT BIT(4)
+#define IRQ_FRAME_VALID BIT(5)
+#define IRQ_ADDRESS_VALID BIT(6)
+#define IRQ_CSMA_CA BIT(7)
+
+#define AUTO_TX_TURNAROUND BIT(3)
+#define ADDON_EN BIT(4)
+
+#define FLAG_XMIT 0
+#define FLAG_START 1
+
+#define ADF7242_REPORT_CSMA_CA_STAT 0 /* framework doesn't handle yet */
+
+struct adf7242_local {
+ struct spi_device *spi;
+ struct completion tx_complete;
+ struct ieee802154_hw *hw;
+ struct mutex bmux; /* protect SPI messages */
+ struct spi_message stat_msg;
+ struct spi_transfer stat_xfer;
+ struct dentry *debugfs_root;
+ unsigned long flags;
+ int tx_stat;
+ bool promiscuous;
+ s8 rssi;
+ u8 max_frame_retries;
+ u8 max_cca_retries;
+ u8 max_be;
+ u8 min_be;
+
+ /* DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+
+ u8 buf[3] ____cacheline_aligned;
+ u8 buf_reg_tx[3];
+ u8 buf_read_tx[4];
+ u8 buf_read_rx[4];
+ u8 buf_stat_rx;
+ u8 buf_stat_tx;
+ u8 buf_cmd;
+};
+
+static int adf7242_soft_reset(struct adf7242_local *lp, int line);
+
+static int adf7242_status(struct adf7242_local *lp, u8 *stat)
+{
+ int status;
+
+ mutex_lock(&lp->bmux);
+ status = spi_sync(lp->spi, &lp->stat_msg);
+ *stat = lp->buf_stat_rx;
+ mutex_unlock(&lp->bmux);
+
+ return status;
+}
+
+static int adf7242_wait_status(struct adf7242_local *lp, unsigned status,
+ unsigned mask, int line)
+{
+ int cnt = 0, ret = 0;
+ u8 stat;
+
+ do {
+ adf7242_status(lp, &stat);
+ cnt++;
+ } while (((stat & mask) != status) && (cnt < MAX_POLL_LOOPS));
+
+ if (cnt >= MAX_POLL_LOOPS) {
+ ret = -ETIMEDOUT;
+
+ if (!(stat & STAT_RC_READY)) {
+ adf7242_soft_reset(lp, line);
+ adf7242_status(lp, &stat);
+
+ if ((stat & mask) == status)
+ ret = 0;
+ }
+
+ if (ret < 0)
+ dev_warn(&lp->spi->dev,
+ "%s:line %d Timeout status 0x%x (%d)\n",
+ __func__, line, stat, cnt);
+ }
+
+ dev_vdbg(&lp->spi->dev, "%s : loops=%d line %d\n", __func__, cnt, line);
+
+ return ret;
+}
+
+static int adf7242_wait_ready(struct adf7242_local *lp, int line)
+{
+ return adf7242_wait_status(lp, STAT_RC_READY | STAT_SPI_READY,
+ STAT_RC_READY | STAT_SPI_READY, line);
+}
+
+static int adf7242_write_fbuf(struct adf7242_local *lp, u8 *data, u8 len)
+{
+ u8 *buf = lp->buf;
+ int status;
+ struct spi_message msg;
+ struct spi_transfer xfer_head = {
+ .len = 2,
+ .tx_buf = buf,
+
+ };
+ struct spi_transfer xfer_buf = {
+ .len = len,
+ .tx_buf = data,
+ };
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer_head, &msg);
+ spi_message_add_tail(&xfer_buf, &msg);
+
+ adf7242_wait_ready(lp, __LINE__);
+
+ mutex_lock(&lp->bmux);
+ buf[0] = CMD_SPI_PKT_WR;
+ buf[1] = len + 2;
+
+ status = spi_sync(lp->spi, &msg);
+ mutex_unlock(&lp->bmux);
+
+ return status;
+}
+
+static int adf7242_read_fbuf(struct adf7242_local *lp,
+ u8 *data, size_t len, bool packet_read)
+{
+ u8 *buf = lp->buf;
+ int status;
+ struct spi_message msg;
+ struct spi_transfer xfer_head = {
+ .len = 3,
+ .tx_buf = buf,
+ .rx_buf = buf,
+ };
+ struct spi_transfer xfer_buf = {
+ .len = len,
+ .rx_buf = data,
+ };
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer_head, &msg);
+ spi_message_add_tail(&xfer_buf, &msg);
+
+ adf7242_wait_ready(lp, __LINE__);
+
+ mutex_lock(&lp->bmux);
+ if (packet_read) {
+ buf[0] = CMD_SPI_PKT_RD;
+ buf[1] = CMD_SPI_NOP;
+ buf[2] = 0; /* PHR */
+ } else {
+ buf[0] = CMD_SPI_PRAM_RD;
+ buf[1] = 0;
+ buf[2] = CMD_SPI_NOP;
+ }
+
+ status = spi_sync(lp->spi, &msg);
+
+ mutex_unlock(&lp->bmux);
+
+ return status;
+}
+
+static int adf7242_read_reg(struct adf7242_local *lp, u16 addr, u8 *data)
+{
+ int status;
+ struct spi_message msg;
+
+ struct spi_transfer xfer = {
+ .len = 4,
+ .tx_buf = lp->buf_read_tx,
+ .rx_buf = lp->buf_read_rx,
+ };
+
+ adf7242_wait_ready(lp, __LINE__);
+
+ mutex_lock(&lp->bmux);
+ lp->buf_read_tx[0] = CMD_SPI_MEM_RD(addr);
+ lp->buf_read_tx[1] = addr;
+ lp->buf_read_tx[2] = CMD_SPI_NOP;
+ lp->buf_read_tx[3] = CMD_SPI_NOP;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ status = spi_sync(lp->spi, &msg);
+ if (msg.status)
+ status = msg.status;
+
+ if (!status)
+ *data = lp->buf_read_rx[3];
+
+ mutex_unlock(&lp->bmux);
+
+ dev_vdbg(&lp->spi->dev, "%s : REG 0x%X, VAL 0x%X\n", __func__,
+ addr, *data);
+
+ return status;
+}
+
+static int adf7242_write_reg(struct adf7242_local *lp, u16 addr, u8 data)
+{
+ int status;
+
+ adf7242_wait_ready(lp, __LINE__);
+
+ mutex_lock(&lp->bmux);
+ lp->buf_reg_tx[0] = CMD_SPI_MEM_WR(addr);
+ lp->buf_reg_tx[1] = addr;
+ lp->buf_reg_tx[2] = data;
+ status = spi_write(lp->spi, lp->buf_reg_tx, 3);
+ mutex_unlock(&lp->bmux);
+
+ dev_vdbg(&lp->spi->dev, "%s : REG 0x%X, VAL 0x%X\n",
+ __func__, addr, data);
+
+ return status;
+}
+
+static int adf7242_cmd(struct adf7242_local *lp, unsigned cmd)
+{
+ int status;
+
+ dev_vdbg(&lp->spi->dev, "%s : CMD=0x%X\n", __func__, cmd);
+
+ if (cmd != CMD_RC_PC_RESET_NO_WAIT)
+ adf7242_wait_ready(lp, __LINE__);
+
+ mutex_lock(&lp->bmux);
+ lp->buf_cmd = cmd;
+ status = spi_write(lp->spi, &lp->buf_cmd, 1);
+ mutex_unlock(&lp->bmux);
+
+ return status;
+}
+
+static int adf7242_upload_firmware(struct adf7242_local *lp, u8 *data, u16 len)
+{
+ struct spi_message msg;
+ struct spi_transfer xfer_buf = { };
+ int status, i, page = 0;
+ u8 *buf = lp->buf;
+
+ struct spi_transfer xfer_head = {
+ .len = 2,
+ .tx_buf = buf,
+ };
+
+ buf[0] = CMD_SPI_PRAM_WR;
+ buf[1] = 0;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer_head, &msg);
+ spi_message_add_tail(&xfer_buf, &msg);
+
+ for (i = len; i >= 0; i -= PRAM_PAGESIZE) {
+ adf7242_write_reg(lp, REG_PRAMPG, page);
+
+ xfer_buf.len = (i >= PRAM_PAGESIZE) ? PRAM_PAGESIZE : i;
+ xfer_buf.tx_buf = &data[page * PRAM_PAGESIZE];
+
+ mutex_lock(&lp->bmux);
+ status = spi_sync(lp->spi, &msg);
+ mutex_unlock(&lp->bmux);
+ page++;
+ }
+
+ return status;
+}
+
+static int adf7242_verify_firmware(struct adf7242_local *lp,
+ const u8 *data, size_t len)
+{
+#ifdef DEBUG
+ int i, j;
+ unsigned int page;
+ u8 *buf = kmalloc(PRAM_PAGESIZE, GFP_KERNEL);
+
+ if (!buf)
+ return -ENOMEM;
+
+ for (page = 0, i = len; i >= 0; i -= PRAM_PAGESIZE, page++) {
+ size_t nb = (i >= PRAM_PAGESIZE) ? PRAM_PAGESIZE : i;
+
+ adf7242_write_reg(lp, REG_PRAMPG, page);
+ adf7242_read_fbuf(lp, buf, nb, false);
+
+ for (j = 0; j < nb; j++) {
+ if (buf[j] != data[page * PRAM_PAGESIZE + j]) {
+ kfree(buf);
+ return -EIO;
+ }
+ }
+ }
+ kfree(buf);
+#endif
+ return 0;
+}
+
+static int adf7242_set_txpower(struct ieee802154_hw *hw, int mbm)
+{
+ struct adf7242_local *lp = hw->priv;
+ u8 pwr, bias_ctrl, dbias, tmp;
+ int db = mbm / 100;
+
+ dev_vdbg(&lp->spi->dev, "%s : Power %d dB\n", __func__, db);
+
+ if (db > 5 || db < -26)
+ return -EINVAL;
+
+ db = DIV_ROUND_CLOSEST(db + 29, 2);
+
+ if (db > 15) {
+ dbias = PA_DBIAS_HIGH_POWER;
+ bias_ctrl = PA_BIAS_HIGH_POWER;
+ } else {
+ dbias = PA_DBIAS_LOW_POWER;
+ bias_ctrl = PA_BIAS_LOW_POWER;
+ }
+
+ pwr = clamp_t(u8, db, 3, 15);
+
+ adf7242_read_reg(lp, REG_PA_CFG, &tmp);
+ tmp &= ~PA_BRIDGE_DBIAS(~0);
+ tmp |= PA_BRIDGE_DBIAS(dbias);
+ adf7242_write_reg(lp, REG_PA_CFG, tmp);
+
+ adf7242_read_reg(lp, REG_PA_BIAS, &tmp);
+ tmp &= ~PA_BIAS_CTRL(~0);
+ tmp |= PA_BIAS_CTRL(bias_ctrl);
+ adf7242_write_reg(lp, REG_PA_BIAS, tmp);
+
+ adf7242_read_reg(lp, REG_EXTPA_MSC, &tmp);
+ tmp &= ~PA_PWR(~0);
+ tmp |= PA_PWR(pwr);
+
+ return adf7242_write_reg(lp, REG_EXTPA_MSC, tmp);
+}
+
+static int adf7242_set_csma_params(struct ieee802154_hw *hw, u8 min_be,
+ u8 max_be, u8 retries)
+{
+ struct adf7242_local *lp = hw->priv;
+ int ret;
+
+ dev_vdbg(&lp->spi->dev, "%s : min_be=%d max_be=%d retries=%d\n",
+ __func__, min_be, max_be, retries);
+
+ if (min_be > max_be || max_be > 8 || retries > 5)
+ return -EINVAL;
+
+ ret = adf7242_write_reg(lp, REG_AUTO_TX1,
+ MAX_FRAME_RETRIES(lp->max_frame_retries) |
+ MAX_CCA_RETRIES(retries));
+ if (ret)
+ return ret;
+
+ lp->max_cca_retries = retries;
+ lp->max_be = max_be;
+ lp->min_be = min_be;
+
+ return adf7242_write_reg(lp, REG_AUTO_TX2, CSMA_MAX_BE(max_be) |
+ CSMA_MIN_BE(min_be));
+}
+
+static int adf7242_set_frame_retries(struct ieee802154_hw *hw, s8 retries)
+{
+ struct adf7242_local *lp = hw->priv;
+ int ret = 0;
+
+ dev_vdbg(&lp->spi->dev, "%s : Retries = %d\n", __func__, retries);
+
+ if (retries < -1 || retries > 15)
+ return -EINVAL;
+
+ if (retries >= 0)
+ ret = adf7242_write_reg(lp, REG_AUTO_TX1,
+ MAX_FRAME_RETRIES(retries) |
+ MAX_CCA_RETRIES(lp->max_cca_retries));
+
+ lp->max_frame_retries = retries;
+
+ return ret;
+}
+
+static int adf7242_ed(struct ieee802154_hw *hw, u8 *level)
+{
+ struct adf7242_local *lp = hw->priv;
+
+ *level = lp->rssi;
+
+ dev_vdbg(&lp->spi->dev, "%s :Exit level=%d\n",
+ __func__, *level);
+
+ return 0;
+}
+
+static int adf7242_start(struct ieee802154_hw *hw)
+{
+ struct adf7242_local *lp = hw->priv;
+
+ adf7242_cmd(lp, CMD_RC_PHY_RDY);
+ adf7242_write_reg(lp, REG_IRQ1_SRC1, 0xFF);
+ enable_irq(lp->spi->irq);
+ set_bit(FLAG_START, &lp->flags);
+
+ return adf7242_cmd(lp, CMD_RC_RX);
+}
+
+static void adf7242_stop(struct ieee802154_hw *hw)
+{
+ struct adf7242_local *lp = hw->priv;
+
+ adf7242_cmd(lp, CMD_RC_IDLE);
+ clear_bit(FLAG_START, &lp->flags);
+ disable_irq(lp->spi->irq);
+ adf7242_write_reg(lp, REG_IRQ1_SRC1, 0xFF);
+}
+
+static int adf7242_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
+{
+ struct adf7242_local *lp = hw->priv;
+ unsigned long freq;
+
+ dev_dbg(&lp->spi->dev, "%s :Channel=%d\n", __func__, channel);
+
+ might_sleep();
+
+ WARN_ON(page != 0);
+ WARN_ON(channel < 11);
+ WARN_ON(channel > 26);
+
+ freq = (2405 + 5 * (channel - 11)) * 100;
+ adf7242_cmd(lp, CMD_RC_PHY_RDY);
+
+ adf7242_write_reg(lp, REG_CH_FREQ0, freq);
+ adf7242_write_reg(lp, REG_CH_FREQ1, freq >> 8);
+ adf7242_write_reg(lp, REG_CH_FREQ2, freq >> 16);
+
+ return adf7242_cmd(lp, CMD_RC_RX);
+}
+
+static int adf7242_set_hw_addr_filt(struct ieee802154_hw *hw,
+ struct ieee802154_hw_addr_filt *filt,
+ unsigned long changed)
+{
+ struct adf7242_local *lp = hw->priv;
+ u8 reg;
+
+ dev_dbg(&lp->spi->dev, "%s :Changed=0x%lX\n", __func__, changed);
+
+ might_sleep();
+
+ if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) {
+ u8 addr[8], i;
+
+ memcpy(addr, &filt->ieee_addr, 8);
+
+ for (i = 0; i < 8; i++)
+ adf7242_write_reg(lp, REG_IEEE_ADDR_0 + i, addr[i]);
+ }
+
+ if (changed & IEEE802154_AFILT_SADDR_CHANGED) {
+ u16 saddr = le16_to_cpu(filt->short_addr);
+
+ adf7242_write_reg(lp, REG_SHORT_ADDR_0, saddr);
+ adf7242_write_reg(lp, REG_SHORT_ADDR_1, saddr >> 8);
+ }
+
+ if (changed & IEEE802154_AFILT_PANID_CHANGED) {
+ u16 pan_id = le16_to_cpu(filt->pan_id);
+
+ adf7242_write_reg(lp, REG_PAN_ID0, pan_id);
+ adf7242_write_reg(lp, REG_PAN_ID1, pan_id >> 8);
+ }
+
+ if (changed & IEEE802154_AFILT_PANC_CHANGED) {
+ adf7242_read_reg(lp, REG_AUTO_CFG, &reg);
+ if (filt->pan_coord)
+ reg |= IS_PANCOORD;
+ else
+ reg &= ~IS_PANCOORD;
+ adf7242_write_reg(lp, REG_AUTO_CFG, reg);
+ }
+
+ return 0;
+}
+
+static int adf7242_set_promiscuous_mode(struct ieee802154_hw *hw, bool on)
+{
+ struct adf7242_local *lp = hw->priv;
+
+ dev_dbg(&lp->spi->dev, "%s : mode %d\n", __func__, on);
+
+ lp->promiscuous = on;
+
+ if (on) {
+ adf7242_write_reg(lp, REG_AUTO_CFG, 0);
+ return adf7242_write_reg(lp, REG_FFILT_CFG,
+ ACCEPT_BEACON_FRAMES |
+ ACCEPT_DATA_FRAMES |
+ ACCEPT_MACCMD_FRAMES |
+ ACCEPT_ALL_ADDRESS |
+ ACCEPT_ACK_FRAMES |
+ ACCEPT_RESERVED_FRAMES);
+ } else {
+ adf7242_write_reg(lp, REG_FFILT_CFG,
+ ACCEPT_BEACON_FRAMES |
+ ACCEPT_DATA_FRAMES |
+ ACCEPT_MACCMD_FRAMES |
+ ACCEPT_RESERVED_FRAMES);
+
+ return adf7242_write_reg(lp, REG_AUTO_CFG, RX_AUTO_ACK_EN);
+ }
+}
+
+static int adf7242_set_cca_ed_level(struct ieee802154_hw *hw, s32 mbm)
+{
+ struct adf7242_local *lp = hw->priv;
+ s8 level = clamp_t(s8, mbm / 100, S8_MIN, S8_MAX);
+
+ dev_dbg(&lp->spi->dev, "%s : level %d\n", __func__, level);
+
+ return adf7242_write_reg(lp, REG_CCA1, level);
+}
+
+static int adf7242_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
+{
+ struct adf7242_local *lp = hw->priv;
+ int ret;
+
+ set_bit(FLAG_XMIT, &lp->flags);
+ reinit_completion(&lp->tx_complete);
+ adf7242_cmd(lp, CMD_RC_PHY_RDY);
+
+ ret = adf7242_write_fbuf(lp, skb->data, skb->len);
+ if (ret)
+ goto err;
+
+ ret = adf7242_cmd(lp, CMD_RC_CSMACA);
+ if (ret)
+ goto err;
+
+ ret = wait_for_completion_interruptible_timeout(&lp->tx_complete,
+ HZ / 10);
+ if (ret < 0)
+ goto err;
+ if (ret == 0) {
+ dev_dbg(&lp->spi->dev, "Timeout waiting for TX interrupt\n");
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ if (lp->tx_stat != SUCCESS) {
+ dev_dbg(&lp->spi->dev,
+ "Error xmit: Retry count exceeded Status=0x%x\n",
+ lp->tx_stat);
+ ret = -ECOMM;
+ } else {
+ ret = 0;
+ }
+
+err:
+ clear_bit(FLAG_XMIT, &lp->flags);
+ adf7242_cmd(lp, CMD_RC_RX);
+
+ return ret;
+}
+
+static int adf7242_rx(struct adf7242_local *lp)
+{
+ struct sk_buff *skb;
+ size_t len;
+ int ret;
+ u8 lqi, len_u8, *data;
+
+ adf7242_read_reg(lp, 0, &len_u8);
+
+ len = len_u8;
+
+ if (!ieee802154_is_valid_psdu_len(len)) {
+ dev_dbg(&lp->spi->dev,
+ "corrupted frame received len %d\n", (int)len);
+ len = IEEE802154_MTU;
+ }
+
+ skb = dev_alloc_skb(len);
+ if (!skb) {
+ adf7242_cmd(lp, CMD_RC_RX);
+ return -ENOMEM;
+ }
+
+ data = skb_put(skb, len);
+ ret = adf7242_read_fbuf(lp, data, len, true);
+ if (ret < 0) {
+ kfree_skb(skb);
+ adf7242_cmd(lp, CMD_RC_RX);
+ return ret;
+ }
+
+ lqi = data[len - 2];
+ lp->rssi = data[len - 1];
+
+ adf7242_cmd(lp, CMD_RC_RX);
+
+ skb_trim(skb, len - 2); /* Don't put RSSI/LQI or CRC into the frame */
+
+ ieee802154_rx_irqsafe(lp->hw, skb, lqi);
+
+ dev_dbg(&lp->spi->dev, "%s: ret=%d len=%d lqi=%d rssi=%d\n",
+ __func__, ret, (int)len, (int)lqi, lp->rssi);
+
+ return 0;
+}
+
+static struct ieee802154_ops adf7242_ops = {
+ .owner = THIS_MODULE,
+ .xmit_sync = adf7242_xmit,
+ .ed = adf7242_ed,
+ .set_channel = adf7242_channel,
+ .set_hw_addr_filt = adf7242_set_hw_addr_filt,
+ .start = adf7242_start,
+ .stop = adf7242_stop,
+ .set_csma_params = adf7242_set_csma_params,
+ .set_frame_retries = adf7242_set_frame_retries,
+ .set_txpower = adf7242_set_txpower,
+ .set_promiscuous_mode = adf7242_set_promiscuous_mode,
+ .set_cca_ed_level = adf7242_set_cca_ed_level,
+};
+
+static void adf7242_debug(u8 irq1)
+{
+#ifdef DEBUG
+ u8 stat;
+
+ adf7242_status(lp, &stat);
+
+ dev_dbg(&lp->spi->dev, "%s IRQ1 = %X:\n%s%s%s%s%s%s%s%s\n",
+ __func__, irq1,
+ irq1 & IRQ_CCA_COMPLETE ? "IRQ_CCA_COMPLETE\n" : "",
+ irq1 & IRQ_SFD_RX ? "IRQ_SFD_RX\n" : "",
+ irq1 & IRQ_SFD_TX ? "IRQ_SFD_TX\n" : "",
+ irq1 & IRQ_RX_PKT_RCVD ? "IRQ_RX_PKT_RCVD\n" : "",
+ irq1 & IRQ_TX_PKT_SENT ? "IRQ_TX_PKT_SENT\n" : "",
+ irq1 & IRQ_CSMA_CA ? "IRQ_CSMA_CA\n" : "",
+ irq1 & IRQ_FRAME_VALID ? "IRQ_FRAME_VALID\n" : "",
+ irq1 & IRQ_ADDRESS_VALID ? "IRQ_ADDRESS_VALID\n" : "");
+
+ dev_dbg(&lp->spi->dev, "%s STATUS = %X:\n%s\n%s%s%s%s%s\n",
+ __func__, stat,
+ stat & STAT_RC_READY ? "RC_READY" : "RC_BUSY",
+ (stat & 0xf) == RC_STATUS_IDLE ? "RC_STATUS_IDLE" : "",
+ (stat & 0xf) == RC_STATUS_MEAS ? "RC_STATUS_MEAS" : "",
+ (stat & 0xf) == RC_STATUS_PHY_RDY ? "RC_STATUS_PHY_RDY" : "",
+ (stat & 0xf) == RC_STATUS_RX ? "RC_STATUS_RX" : "",
+ (stat & 0xf) == RC_STATUS_TX ? "RC_STATUS_TX" : "");
+ }
+#endif
+}
+
+static irqreturn_t adf7242_isr(int irq, void *data)
+{
+ struct adf7242_local *lp = data;
+ unsigned xmit;
+ u8 irq1;
+
+ adf7242_wait_status(lp, RC_STATUS_PHY_RDY, RC_STATUS_MASK, __LINE__);
+
+ adf7242_read_reg(lp, REG_IRQ1_SRC1, &irq1);
+ adf7242_write_reg(lp, REG_IRQ1_SRC1, irq1);
+
+ if (!(irq1 & (IRQ_RX_PKT_RCVD | IRQ_CSMA_CA)))
+ dev_err(&lp->spi->dev, "%s :ERROR IRQ1 = 0x%X\n",
+ __func__, irq1);
+
+ adf7242_debug(irq1);
+
+ xmit = test_bit(FLAG_XMIT, &lp->flags);
+
+ if (xmit && (irq1 & IRQ_CSMA_CA)) {
+ if (ADF7242_REPORT_CSMA_CA_STAT) {
+ u8 astat;
+
+ adf7242_read_reg(lp, REG_AUTO_STATUS, &astat);
+ astat &= AUTO_STATUS_MASK;
+
+ dev_dbg(&lp->spi->dev, "AUTO_STATUS = %X:\n%s%s%s%s\n",
+ astat,
+ astat == SUCCESS ? "SUCCESS" : "",
+ astat ==
+ SUCCESS_DATPEND ? "SUCCESS_DATPEND" : "",
+ astat == FAILURE_CSMACA ? "FAILURE_CSMACA" : "",
+ astat == FAILURE_NOACK ? "FAILURE_NOACK" : "");
+
+ /* save CSMA-CA completion status */
+ lp->tx_stat = astat;
+ } else {
+ lp->tx_stat = SUCCESS;
+ }
+ complete(&lp->tx_complete);
+ } else if (!xmit && (irq1 & IRQ_RX_PKT_RCVD) &&
+ (irq1 & IRQ_FRAME_VALID)) {
+ adf7242_rx(lp);
+ } else if (!xmit && test_bit(FLAG_START, &lp->flags)) {
+ /* Invalid packet received - drop it and restart */
+ dev_dbg(&lp->spi->dev, "%s:%d : ERROR IRQ1 = 0x%X\n",
+ __func__, __LINE__, irq1);
+ adf7242_cmd(lp, CMD_RC_PHY_RDY);
+ adf7242_write_reg(lp, REG_IRQ1_SRC1, 0xFF);
+ adf7242_cmd(lp, CMD_RC_RX);
+ } else {
+ /* This can only be xmit without IRQ, likely a RX packet.
+ * we get an TX IRQ shortly - do nothing or let the xmit
+ * timeout handle this
+ */
+ dev_dbg(&lp->spi->dev, "%s:%d : ERROR IRQ1 = 0x%X, xmit %d\n",
+ __func__, __LINE__, irq1, xmit);
+ complete(&lp->tx_complete);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int adf7242_soft_reset(struct adf7242_local *lp, int line)
+{
+ dev_warn(&lp->spi->dev, "%s (line %d)\n", __func__, line);
+
+ if (test_bit(FLAG_START, &lp->flags))
+ disable_irq_nosync(lp->spi->irq);
+
+ adf7242_cmd(lp, CMD_RC_PC_RESET_NO_WAIT);
+ usleep_range(200, 250);
+ adf7242_write_reg(lp, REG_PKT_CFG, ADDON_EN | BIT(2));
+ adf7242_cmd(lp, CMD_RC_PHY_RDY);
+ adf7242_set_promiscuous_mode(lp->hw, lp->promiscuous);
+ adf7242_set_csma_params(lp->hw, lp->min_be, lp->max_be,
+ lp->max_cca_retries);
+ adf7242_write_reg(lp, REG_IRQ1_SRC1, 0xFF);
+
+ if (test_bit(FLAG_START, &lp->flags)) {
+ enable_irq(lp->spi->irq);
+ return adf7242_cmd(lp, CMD_RC_RX);
+ }
+
+ return 0;
+}
+
+static int adf7242_hw_init(struct adf7242_local *lp)
+{
+ int ret;
+ const struct firmware *fw;
+
+ adf7242_cmd(lp, CMD_RC_RESET);
+ adf7242_cmd(lp, CMD_RC_IDLE);
+
+ /* get ADF7242 addon firmware
+ * build this driver as module
+ * and place under /lib/firmware/adf7242_firmware.bin
+ * or compile firmware into the kernel.
+ */
+ ret = request_firmware(&fw, FIRMWARE, &lp->spi->dev);
+ if (ret) {
+ dev_err(&lp->spi->dev,
+ "request_firmware() failed with %d\n", ret);
+ return ret;
+ }
+
+ ret = adf7242_upload_firmware(lp, (u8 *)fw->data, fw->size);
+ if (ret) {
+ dev_err(&lp->spi->dev,
+ "upload firmware failed with %d\n", ret);
+ return ret;
+ }
+
+ ret = adf7242_verify_firmware(lp, (u8 *)fw->data, fw->size);
+ if (ret) {
+ dev_err(&lp->spi->dev,
+ "verify firmware failed with %d\n", ret);
+ return ret;
+ }
+
+ adf7242_cmd(lp, CMD_RC_PC_RESET);
+
+ release_firmware(fw);
+
+ adf7242_write_reg(lp, REG_FFILT_CFG,
+ ACCEPT_BEACON_FRAMES |
+ ACCEPT_DATA_FRAMES |
+ ACCEPT_MACCMD_FRAMES |
+ ACCEPT_RESERVED_FRAMES);
+
+ adf7242_write_reg(lp, REG_AUTO_CFG, RX_AUTO_ACK_EN);
+
+ adf7242_write_reg(lp, REG_PKT_CFG, ADDON_EN | BIT(2));
+
+ adf7242_write_reg(lp, REG_EXTPA_MSC, 0xF1);
+ adf7242_write_reg(lp, REG_RXFE_CFG, 0x1D);
+
+ adf7242_write_reg(lp, REG_IRQ1_EN0, 0);
+ adf7242_write_reg(lp, REG_IRQ1_EN1, IRQ_RX_PKT_RCVD | IRQ_CSMA_CA);
+
+ adf7242_write_reg(lp, REG_IRQ1_SRC1, 0xFF);
+ adf7242_write_reg(lp, REG_IRQ1_SRC0, 0xFF);
+
+ adf7242_cmd(lp, CMD_RC_IDLE);
+
+ return 0;
+}
+
+static int adf7242_stats_show(struct seq_file *file, void *offset)
+{
+ struct adf7242_local *lp = spi_get_drvdata(file->private);
+ u8 stat, irq1;
+
+ adf7242_status(lp, &stat);
+ adf7242_read_reg(lp, REG_IRQ1_SRC1, &irq1);
+
+ seq_printf(file, "IRQ1 = %X:\n%s%s%s%s%s%s%s%s\n", irq1,
+ irq1 & IRQ_CCA_COMPLETE ? "IRQ_CCA_COMPLETE\n" : "",
+ irq1 & IRQ_SFD_RX ? "IRQ_SFD_RX\n" : "",
+ irq1 & IRQ_SFD_TX ? "IRQ_SFD_TX\n" : "",
+ irq1 & IRQ_RX_PKT_RCVD ? "IRQ_RX_PKT_RCVD\n" : "",
+ irq1 & IRQ_TX_PKT_SENT ? "IRQ_TX_PKT_SENT\n" : "",
+ irq1 & IRQ_CSMA_CA ? "IRQ_CSMA_CA\n" : "",
+ irq1 & IRQ_FRAME_VALID ? "IRQ_FRAME_VALID\n" : "",
+ irq1 & IRQ_ADDRESS_VALID ? "IRQ_ADDRESS_VALID\n" : "");
+
+ seq_printf(file, "STATUS = %X:\n%s\n%s%s%s%s%s\n", stat,
+ stat & STAT_RC_READY ? "RC_READY" : "RC_BUSY",
+ (stat & 0xf) == RC_STATUS_IDLE ? "RC_STATUS_IDLE" : "",
+ (stat & 0xf) == RC_STATUS_MEAS ? "RC_STATUS_MEAS" : "",
+ (stat & 0xf) == RC_STATUS_PHY_RDY ? "RC_STATUS_PHY_RDY" : "",
+ (stat & 0xf) == RC_STATUS_RX ? "RC_STATUS_RX" : "",
+ (stat & 0xf) == RC_STATUS_TX ? "RC_STATUS_TX" : "");
+
+ seq_printf(file, "RSSI = %d\n", lp->rssi);
+
+ return 0;
+}
+
+static int adf7242_debugfs_init(struct adf7242_local *lp)
+{
+ char debugfs_dir_name[DNAME_INLINE_LEN + 1] = "adf7242-";
+ struct dentry *stats;
+
+ strncat(debugfs_dir_name, dev_name(&lp->spi->dev), DNAME_INLINE_LEN);
+
+ lp->debugfs_root = debugfs_create_dir(debugfs_dir_name, NULL);
+ if (IS_ERR_OR_NULL(lp->debugfs_root))
+ return PTR_ERR_OR_ZERO(lp->debugfs_root);
+
+ stats = debugfs_create_devm_seqfile(&lp->spi->dev, "status",
+ lp->debugfs_root,
+ adf7242_stats_show);
+ return PTR_ERR_OR_ZERO(stats);
+
+ return 0;
+}
+
+static const s32 adf7242_powers[] = {
+ 500, 400, 300, 200, 100, 0, -100, -200, -300, -400, -500, -600, -700,
+ -800, -900, -1000, -1100, -1200, -1300, -1400, -1500, -1600, -1700,
+ -1800, -1900, -2000, -2100, -2200, -2300, -2400, -2500, -2600,
+};
+
+static const s32 adf7242_ed_levels[] = {
+ -9000, -8900, -8800, -8700, -8600, -8500, -8400, -8300, -8200, -8100,
+ -8000, -7900, -7800, -7700, -7600, -7500, -7400, -7300, -7200, -7100,
+ -7000, -6900, -6800, -6700, -6600, -6500, -6400, -6300, -6200, -6100,
+ -6000, -5900, -5800, -5700, -5600, -5500, -5400, -5300, -5200, -5100,
+ -5000, -4900, -4800, -4700, -4600, -4500, -4400, -4300, -4200, -4100,
+ -4000, -3900, -3800, -3700, -3600, -3500, -3400, -3200, -3100, -3000
+};
+
+static int adf7242_probe(struct spi_device *spi)
+{
+ struct ieee802154_hw *hw;
+ struct adf7242_local *lp;
+ int ret, irq_type;
+
+ if (!spi->irq) {
+ dev_err(&spi->dev, "no IRQ specified\n");
+ return -EINVAL;
+ }
+
+ hw = ieee802154_alloc_hw(sizeof(*lp), &adf7242_ops);
+ if (!hw)
+ return -ENOMEM;
+
+ lp = hw->priv;
+ lp->hw = hw;
+ lp->spi = spi;
+
+ hw->priv = lp;
+ hw->parent = &spi->dev;
+ hw->extra_tx_headroom = 0;
+
+ /* We support only 2.4 Ghz */
+ hw->phy->supported.channels[0] = 0x7FFF800;
+
+ hw->flags = IEEE802154_HW_OMIT_CKSUM |
+ IEEE802154_HW_CSMA_PARAMS |
+ IEEE802154_HW_FRAME_RETRIES | IEEE802154_HW_AFILT |
+ IEEE802154_HW_PROMISCUOUS;
+
+ hw->phy->flags = WPAN_PHY_FLAG_TXPOWER |
+ WPAN_PHY_FLAG_CCA_ED_LEVEL |
+ WPAN_PHY_FLAG_CCA_MODE;
+
+ hw->phy->supported.cca_modes = BIT(NL802154_CCA_ENERGY);
+
+ hw->phy->supported.cca_ed_levels = adf7242_ed_levels;
+ hw->phy->supported.cca_ed_levels_size = ARRAY_SIZE(adf7242_ed_levels);
+
+ hw->phy->cca.mode = NL802154_CCA_ENERGY;
+
+ hw->phy->supported.tx_powers = adf7242_powers;
+ hw->phy->supported.tx_powers_size = ARRAY_SIZE(adf7242_powers);
+
+ hw->phy->supported.min_minbe = 0;
+ hw->phy->supported.max_minbe = 8;
+
+ hw->phy->supported.min_maxbe = 3;
+ hw->phy->supported.max_maxbe = 8;
+
+ hw->phy->supported.min_frame_retries = 0;
+ hw->phy->supported.max_frame_retries = 15;
+
+ hw->phy->supported.min_csma_backoffs = 0;
+ hw->phy->supported.max_csma_backoffs = 5;
+
+ ieee802154_random_extended_addr(&hw->phy->perm_extended_addr);
+
+ mutex_init(&lp->bmux);
+ init_completion(&lp->tx_complete);
+
+ /* Setup Status Message */
+ lp->stat_xfer.len = 1;
+ lp->stat_xfer.tx_buf = &lp->buf_stat_tx;
+ lp->stat_xfer.rx_buf = &lp->buf_stat_rx;
+ lp->buf_stat_tx = CMD_SPI_NOP;
+
+ spi_message_init(&lp->stat_msg);
+ spi_message_add_tail(&lp->stat_xfer, &lp->stat_msg);
+
+ spi_set_drvdata(spi, lp);
+
+ ret = adf7242_hw_init(lp);
+ if (ret)
+ goto err_hw_init;
+
+ irq_type = irq_get_trigger_type(spi->irq);
+ if (!irq_type)
+ irq_type = IRQF_TRIGGER_HIGH;
+
+ ret = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, adf7242_isr,
+ irq_type | IRQF_ONESHOT,
+ dev_name(&spi->dev), lp);
+ if (ret)
+ goto err_hw_init;
+
+ disable_irq(spi->irq);
+
+ ret = ieee802154_register_hw(lp->hw);
+ if (ret)
+ goto err_hw_init;
+
+ dev_set_drvdata(&spi->dev, lp);
+
+ adf7242_debugfs_init(lp);
+
+ dev_info(&spi->dev, "mac802154 IRQ-%d registered\n", spi->irq);
+
+ return ret;
+
+err_hw_init:
+ mutex_destroy(&lp->bmux);
+ ieee802154_free_hw(lp->hw);
+
+ return ret;
+}
+
+static int adf7242_remove(struct spi_device *spi)
+{
+ struct adf7242_local *lp = spi_get_drvdata(spi);
+
+ if (!IS_ERR_OR_NULL(lp->debugfs_root))
+ debugfs_remove_recursive(lp->debugfs_root);
+
+ ieee802154_unregister_hw(lp->hw);
+ mutex_destroy(&lp->bmux);
+ ieee802154_free_hw(lp->hw);
+
+ return 0;
+}
+
+static const struct of_device_id adf7242_of_match[] = {
+ { .compatible = "adi,adf7242", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adf7242_of_match);
+
+static const struct spi_device_id adf7242_device_id[] = {
+ { .name = "adf7242", },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, adf7242_device_id);
+
+static struct spi_driver adf7242_driver = {
+ .id_table = adf7242_device_id,
+ .driver = {
+ .of_match_table = of_match_ptr(adf7242_of_match),
+ .name = "adf7242",
+ .owner = THIS_MODULE,
+ },
+ .probe = adf7242_probe,
+ .remove = adf7242_remove,
+};
+
+module_spi_driver(adf7242_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("ADF7242 IEEE802.15.4 Transceiver Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ieee802154/atusb.c b/drivers/net/ieee802154/atusb.c
index 199a94a9c8bc..b1cd865ade2e 100644
--- a/drivers/net/ieee802154/atusb.c
+++ b/drivers/net/ieee802154/atusb.c
@@ -310,8 +310,7 @@ static void atusb_free_urbs(struct atusb *atusb)
urb = usb_get_from_anchor(&atusb->idle_urbs);
if (!urb)
break;
- if (urb->context)
- kfree_skb(urb->context);
+ kfree_skb(urb->context);
usb_free_urb(urb);
}
}
diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c
index e65b60591317..d50add705a79 100644
--- a/drivers/net/ieee802154/cc2520.c
+++ b/drivers/net/ieee802154/cc2520.c
@@ -21,6 +21,8 @@
#include <linux/skbuff.h>
#include <linux/of_gpio.h>
#include <linux/ieee802154.h>
+#include <linux/crc-ccitt.h>
+#include <asm/unaligned.h>
#include <net/mac802154.h>
#include <net/cfg802154.h>
@@ -189,6 +191,18 @@
#define CC2520_RXFIFOCNT 0x3E
#define CC2520_TXFIFOCNT 0x3F
+/* CC2520_FRMFILT0 */
+#define FRMFILT0_FRAME_FILTER_EN BIT(0)
+#define FRMFILT0_PAN_COORDINATOR BIT(1)
+
+/* CC2520_FRMCTRL0 */
+#define FRMCTRL0_AUTOACK BIT(5)
+#define FRMCTRL0_AUTOCRC BIT(6)
+
+/* CC2520_FRMCTRL1 */
+#define FRMCTRL1_SET_RXENMASK_ON_TX BIT(0)
+#define FRMCTRL1_IGNORE_TX_UNDERF BIT(1)
+
/* Driver private information */
struct cc2520_private {
struct spi_device *spi; /* SPI device structure */
@@ -201,6 +215,7 @@ struct cc2520_private {
struct work_struct fifop_irqwork;/* Workqueue for FIFOP */
spinlock_t lock; /* Lock for is_tx*/
struct completion tx_complete; /* Work completion for Tx */
+ bool promiscuous; /* Flag for promiscuous mode */
};
/* Generic Functions */
@@ -367,14 +382,14 @@ cc2520_read_register(struct cc2520_private *priv, u8 reg, u8 *data)
}
static int
-cc2520_write_txfifo(struct cc2520_private *priv, u8 *data, u8 len)
+cc2520_write_txfifo(struct cc2520_private *priv, u8 pkt_len, u8 *data, u8 len)
{
int status;
/* length byte must include FCS even
* if it is calculated in the hardware
*/
- int len_byte = len + 2;
+ int len_byte = pkt_len;
struct spi_message msg;
@@ -414,7 +429,7 @@ cc2520_write_txfifo(struct cc2520_private *priv, u8 *data, u8 len)
}
static int
-cc2520_read_rxfifo(struct cc2520_private *priv, u8 *data, u8 len, u8 *lqi)
+cc2520_read_rxfifo(struct cc2520_private *priv, u8 *data, u8 len)
{
int status;
struct spi_message msg;
@@ -470,12 +485,25 @@ cc2520_tx(struct ieee802154_hw *hw, struct sk_buff *skb)
unsigned long flags;
int rc;
u8 status = 0;
+ u8 pkt_len;
+
+ /* In promiscuous mode we disable AUTOCRC so we can get the raw CRC
+ * values on RX. This means we need to manually add the CRC on TX.
+ */
+ if (priv->promiscuous) {
+ u16 crc = crc_ccitt(0, skb->data, skb->len);
+
+ put_unaligned_le16(crc, skb_put(skb, 2));
+ pkt_len = skb->len;
+ } else {
+ pkt_len = skb->len + 2;
+ }
rc = cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHTX);
if (rc)
goto err_tx;
- rc = cc2520_write_txfifo(priv, skb->data, skb->len);
+ rc = cc2520_write_txfifo(priv, pkt_len, skb->data, skb->len);
if (rc)
goto err_tx;
@@ -518,22 +546,62 @@ static int cc2520_rx(struct cc2520_private *priv)
u8 len = 0, lqi = 0, bytes = 1;
struct sk_buff *skb;
- cc2520_read_rxfifo(priv, &len, bytes, &lqi);
+ /* Read single length byte from the radio. */
+ cc2520_read_rxfifo(priv, &len, bytes);
- if (len < 2 || len > IEEE802154_MTU)
- return -EINVAL;
+ if (!ieee802154_is_valid_psdu_len(len)) {
+ /* Corrupted frame received, clear frame buffer by
+ * reading entire buffer.
+ */
+ dev_dbg(&priv->spi->dev, "corrupted frame received\n");
+ len = IEEE802154_MTU;
+ }
skb = dev_alloc_skb(len);
if (!skb)
return -ENOMEM;
- if (cc2520_read_rxfifo(priv, skb_put(skb, len), len, &lqi)) {
+ if (cc2520_read_rxfifo(priv, skb_put(skb, len), len)) {
dev_dbg(&priv->spi->dev, "frame reception failed\n");
kfree_skb(skb);
return -EINVAL;
}
- skb_trim(skb, skb->len - 2);
+ /* In promiscuous mode, we configure the radio to include the
+ * CRC (AUTOCRC==0) and we pass on the packet unconditionally. If not
+ * in promiscuous mode, we check the CRC here, but leave the
+ * RSSI/LQI/CRC_OK bytes as they will get removed in the mac layer.
+ */
+ if (!priv->promiscuous) {
+ bool crc_ok;
+
+ /* Check if the CRC is valid. With AUTOCRC set, the most
+ * significant bit of the last byte returned from the CC2520
+ * is CRC_OK flag. See section 20.3.4 of the datasheet.
+ */
+ crc_ok = skb->data[len - 1] & BIT(7);
+
+ /* If we failed CRC drop the packet in the driver layer. */
+ if (!crc_ok) {
+ dev_dbg(&priv->spi->dev, "CRC check failed\n");
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ /* To calculate LQI, the lower 7 bits of the last byte (the
+ * correlation value provided by the radio) must be scaled to
+ * the range 0-255. According to section 20.6, the correlation
+ * value ranges from 50-110. Ideally this would be calibrated
+ * per hardware design, but we use roughly the datasheet values
+ * to get close enough while avoiding floating point.
+ */
+ lqi = skb->data[len - 1] & 0x7f;
+ if (lqi < 50)
+ lqi = 50;
+ else if (lqi > 113)
+ lqi = 113;
+ lqi = (lqi - 50) * 4;
+ }
ieee802154_rx_irqsafe(priv->hw, skb, lqi);
@@ -619,14 +687,19 @@ cc2520_filter(struct ieee802154_hw *hw,
}
if (changed & IEEE802154_AFILT_PANC_CHANGED) {
+ u8 frmfilt0;
+
dev_vdbg(&priv->spi->dev,
"cc2520_filter called for panc change\n");
+
+ cc2520_read_register(priv, CC2520_FRMFILT0, &frmfilt0);
+
if (filt->pan_coord)
- ret = cc2520_write_register(priv, CC2520_FRMFILT0,
- 0x02);
+ frmfilt0 |= FRMFILT0_PAN_COORDINATOR;
else
- ret = cc2520_write_register(priv, CC2520_FRMFILT0,
- 0x00);
+ frmfilt0 &= ~FRMFILT0_PAN_COORDINATOR;
+
+ ret = cc2520_write_register(priv, CC2520_FRMFILT0, frmfilt0);
}
return ret;
@@ -723,6 +796,30 @@ cc2520_set_txpower(struct ieee802154_hw *hw, s32 mbm)
return cc2520_cc2591_set_tx_power(priv, mbm);
}
+static int
+cc2520_set_promiscuous_mode(struct ieee802154_hw *hw, bool on)
+{
+ struct cc2520_private *priv = hw->priv;
+ u8 frmfilt0;
+
+ dev_dbg(&priv->spi->dev, "%s : mode %d\n", __func__, on);
+
+ priv->promiscuous = on;
+
+ cc2520_read_register(priv, CC2520_FRMFILT0, &frmfilt0);
+
+ if (on) {
+ /* Disable automatic ACK, automatic CRC, and frame filtering. */
+ cc2520_write_register(priv, CC2520_FRMCTRL0, 0);
+ frmfilt0 &= ~FRMFILT0_FRAME_FILTER_EN;
+ } else {
+ cc2520_write_register(priv, CC2520_FRMCTRL0, FRMCTRL0_AUTOACK |
+ FRMCTRL0_AUTOCRC);
+ frmfilt0 |= FRMFILT0_FRAME_FILTER_EN;
+ }
+ return cc2520_write_register(priv, CC2520_FRMFILT0, frmfilt0);
+}
+
static const struct ieee802154_ops cc2520_ops = {
.owner = THIS_MODULE,
.start = cc2520_start,
@@ -732,6 +829,7 @@ static const struct ieee802154_ops cc2520_ops = {
.set_channel = cc2520_set_channel,
.set_hw_addr_filt = cc2520_filter,
.set_txpower = cc2520_set_txpower,
+ .set_promiscuous_mode = cc2520_set_promiscuous_mode,
};
static int cc2520_register(struct cc2520_private *priv)
@@ -749,7 +847,8 @@ static int cc2520_register(struct cc2520_private *priv)
/* We do support only 2.4 Ghz */
priv->hw->phy->supported.channels[0] = 0x7FFF800;
- priv->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AFILT;
+ priv->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT |
+ IEEE802154_HW_PROMISCUOUS;
priv->hw->phy->flags = WPAN_PHY_FLAG_TXPOWER;
@@ -919,6 +1018,11 @@ static int cc2520_hw_init(struct cc2520_private *priv)
}
/* Registers default value: section 28.1 in Datasheet */
+
+ /* Set the CCA threshold to -50 dBm. This seems to have been copied
+ * from the TinyOS CC2520 driver and is much higher than the -84 dBm
+ * threshold suggested in the datasheet.
+ */
ret = cc2520_write_register(priv, CC2520_CCACTRL0, 0x1A);
if (ret)
goto err_ret;
@@ -955,15 +1059,10 @@ static int cc2520_hw_init(struct cc2520_private *priv)
if (ret)
goto err_ret;
- ret = cc2520_write_register(priv, CC2520_FRMCTRL0, 0x60);
- if (ret)
- goto err_ret;
-
- ret = cc2520_write_register(priv, CC2520_FRMCTRL1, 0x03);
- if (ret)
- goto err_ret;
-
- ret = cc2520_write_register(priv, CC2520_FRMFILT0, 0x00);
+ /* Configure registers correctly for this driver. */
+ ret = cc2520_write_register(priv, CC2520_FRMCTRL1,
+ FRMCTRL1_SET_RXENMASK_ON_TX |
+ FRMCTRL1_IGNORE_TX_UNDERF);
if (ret)
goto err_ret;
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c
index a9268db4e349..f94392d07126 100644
--- a/drivers/net/ipvlan/ipvlan_main.c
+++ b/drivers/net/ipvlan/ipvlan_main.c
@@ -88,7 +88,7 @@ static struct lock_class_key ipvlan_netdev_xmit_lock_key;
static struct lock_class_key ipvlan_netdev_addr_lock_key;
#define IPVLAN_FEATURES \
- (NETIF_F_SG | NETIF_F_ALL_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \
+ (NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \
NETIF_F_GSO | NETIF_F_TSO | NETIF_F_UFO | NETIF_F_GSO_ROBUST | \
NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_GRO | NETIF_F_RXCSUM | \
NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_STAG_FILTER)
diff --git a/drivers/net/irda/toim3232-sir.c b/drivers/net/irda/toim3232-sir.c
index 6d2f55959c49..b977d6d33e74 100644
--- a/drivers/net/irda/toim3232-sir.c
+++ b/drivers/net/irda/toim3232-sir.c
@@ -130,16 +130,6 @@ static int toim3232delay = 150; /* default is 150 ms */
module_param(toim3232delay, int, 0);
MODULE_PARM_DESC(toim3232delay, "toim3232 dongle write complete delay");
-#if 0
-static int toim3232flipdtr = 0; /* default is DTR high to reset */
-module_param(toim3232flipdtr, int, 0);
-MODULE_PARM_DESC(toim3232flipdtr, "toim3232 dongle invert DTR (Reset)");
-
-static int toim3232fliprts = 0; /* default is RTS high for baud change */
-module_param(toim3232fliptrs, int, 0);
-MODULE_PARM_DESC(toim3232fliprts, "toim3232 dongle invert RTS (BR/D)");
-#endif
-
static int toim3232_open(struct sir_dev *);
static int toim3232_close(struct sir_dev *);
static int toim3232_change_speed(struct sir_dev *, unsigned);
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
index dc7d970bd1c0..a400288cb37b 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -175,7 +175,7 @@ static void loopback_setup(struct net_device *dev)
| NETIF_F_UFO
| NETIF_F_HW_CSUM
| NETIF_F_RXCSUM
- | NETIF_F_SCTP_CSUM
+ | NETIF_F_SCTP_CRC
| NETIF_F_HIGHDMA
| NETIF_F_LLTX
| NETIF_F_NETNS_LOCAL
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 06c8bfeaccd6..6a57a005e0ca 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -758,11 +758,11 @@ static struct lock_class_key macvlan_netdev_xmit_lock_key;
static struct lock_class_key macvlan_netdev_addr_lock_key;
#define ALWAYS_ON_FEATURES \
- (NETIF_F_SG | NETIF_F_GEN_CSUM | NETIF_F_GSO_SOFTWARE | NETIF_F_LLTX | \
+ (NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_GSO_SOFTWARE | NETIF_F_LLTX | \
NETIF_F_GSO_ROBUST)
#define MACVLAN_FEATURES \
- (NETIF_F_SG | NETIF_F_ALL_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \
+ (NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \
NETIF_F_GSO | NETIF_F_TSO | NETIF_F_UFO | NETIF_F_LRO | \
NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_GRO | NETIF_F_RXCSUM | \
NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_STAG_FILTER)
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index 54036ae0a388..d636d051fac8 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -388,7 +388,7 @@ static rx_handler_result_t macvtap_handle_frame(struct sk_buff **pskb)
* check, we either support them all or none.
*/
if (skb->ip_summed == CHECKSUM_PARTIAL &&
- !(features & NETIF_F_ALL_CSUM) &&
+ !(features & NETIF_F_CSUM_MASK) &&
skb_checksum_help(skb))
goto drop;
skb_queue_tail(&q->sk.sk_receive_queue, skb);
@@ -498,7 +498,7 @@ static void macvtap_sock_write_space(struct sock *sk)
wait_queue_head_t *wqueue;
if (!sock_writeable(sk) ||
- !test_and_clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags))
+ !test_and_clear_bit(SOCKWQ_ASYNC_NOSPACE, &sk->sk_socket->flags))
return;
wqueue = sk_sleep(sk);
@@ -585,7 +585,7 @@ static unsigned int macvtap_poll(struct file *file, poll_table * wait)
mask |= POLLIN | POLLRDNORM;
if (sock_writeable(&q->sk) ||
- (!test_and_set_bit(SOCK_ASYNC_NOSPACE, &q->sock.flags) &&
+ (!test_and_set_bit(SOCKWQ_ASYNC_NOSPACE, &q->sock.flags) &&
sock_writeable(&q->sk)))
mask |= POLLOUT | POLLWRNORM;
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index f31a4e25cf15..680e88f9915a 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -1,6 +1,6 @@
# Makefile for Linux PHY drivers
-libphy-objs := phy.o phy_device.o mdio_bus.o
+libphy-objs := phy.o phy_device.o mdio_bus.o mdio_device.o
obj-$(CONFIG_PHYLIB) += libphy.o
obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o
diff --git a/drivers/net/phy/amd.c b/drivers/net/phy/amd.c
index 65a488f82eb8..18141c022b13 100644
--- a/drivers/net/phy/amd.c
+++ b/drivers/net/phy/amd.c
@@ -72,7 +72,6 @@ static struct phy_driver am79c_driver[] = { {
.read_status = genphy_read_status,
.ack_interrupt = am79c_ack_interrupt,
.config_intr = am79c_config_intr,
- .driver = { .owner = THIS_MODULE,},
} };
module_phy_driver(am79c_driver);
diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c
index f1936b7a7af6..09b0b0aa8d68 100644
--- a/drivers/net/phy/aquantia.c
+++ b/drivers/net/phy/aquantia.c
@@ -128,7 +128,6 @@ static struct phy_driver aquantia_driver[] = {
.config_intr = aquantia_config_intr,
.ack_interrupt = aquantia_ack_interrupt,
.read_status = aquantia_read_status,
- .driver = { .owner = THIS_MODULE,},
},
{
.phy_id = PHY_ID_AQ2104,
@@ -141,7 +140,6 @@ static struct phy_driver aquantia_driver[] = {
.config_intr = aquantia_config_intr,
.ack_interrupt = aquantia_ack_interrupt,
.read_status = aquantia_read_status,
- .driver = { .owner = THIS_MODULE,},
},
{
.phy_id = PHY_ID_AQR105,
@@ -154,7 +152,6 @@ static struct phy_driver aquantia_driver[] = {
.config_intr = aquantia_config_intr,
.ack_interrupt = aquantia_ack_interrupt,
.read_status = aquantia_read_status,
- .driver = { .owner = THIS_MODULE,},
},
{
.phy_id = PHY_ID_AQR405,
@@ -167,7 +164,6 @@ static struct phy_driver aquantia_driver[] = {
.config_intr = aquantia_config_intr,
.ack_interrupt = aquantia_ack_interrupt,
.read_status = aquantia_read_status,
- .driver = { .owner = THIS_MODULE,},
},
};
diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c
index 2d020a3ec0b5..8a8f6fb2880d 100644
--- a/drivers/net/phy/at803x.c
+++ b/drivers/net/phy/at803x.c
@@ -190,7 +190,7 @@ static int at803x_resume(struct phy_device *phydev)
static int at803x_probe(struct phy_device *phydev)
{
- struct device *dev = &phydev->dev;
+ struct device *dev = &phydev->mdio.dev;
struct at803x_priv *priv;
struct gpio_desc *gpiod_reset;
@@ -281,8 +281,8 @@ static void at803x_link_change_notify(struct phy_device *phydev)
at803x_context_restore(phydev, &context);
- dev_dbg(&phydev->dev, "%s(): phy was reset\n",
- __func__);
+ phydev_dbg(phydev, "%s(): phy was reset\n",
+ __func__);
priv->phy_reset = true;
}
} else {
@@ -310,9 +310,6 @@ static struct phy_driver at803x_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = at803x_ack_interrupt,
.config_intr = at803x_config_intr,
- .driver = {
- .owner = THIS_MODULE,
- },
}, {
/* ATHEROS 8030 */
.phy_id = ATH8030_PHY_ID,
@@ -331,9 +328,6 @@ static struct phy_driver at803x_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = at803x_ack_interrupt,
.config_intr = at803x_config_intr,
- .driver = {
- .owner = THIS_MODULE,
- },
}, {
/* ATHEROS 8031 */
.phy_id = ATH8031_PHY_ID,
@@ -352,9 +346,6 @@ static struct phy_driver at803x_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = &at803x_ack_interrupt,
.config_intr = &at803x_config_intr,
- .driver = {
- .owner = THIS_MODULE,
- },
} };
module_phy_driver(at803x_driver);
diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c
index ddb377e53633..df0416db0b88 100644
--- a/drivers/net/phy/bcm-phy-lib.c
+++ b/drivers/net/phy/bcm-phy-lib.c
@@ -184,25 +184,25 @@ int bcm_phy_enable_eee(struct phy_device *phydev)
/* Enable EEE at PHY level */
val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
- MDIO_MMD_AN, phydev->addr);
+ MDIO_MMD_AN);
if (val < 0)
return val;
val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X;
phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
- MDIO_MMD_AN, phydev->addr, (u32)val);
+ MDIO_MMD_AN, (u32)val);
/* Advertise EEE */
val = phy_read_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV,
- MDIO_MMD_AN, phydev->addr);
+ MDIO_MMD_AN);
if (val < 0)
return val;
val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T);
phy_write_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV,
- MDIO_MMD_AN, phydev->addr, (u32)val);
+ MDIO_MMD_AN, (u32)val);
return 0;
}
diff --git a/drivers/net/phy/bcm63xx.c b/drivers/net/phy/bcm63xx.c
index 86b28052bf06..e741bf614c4e 100644
--- a/drivers/net/phy/bcm63xx.c
+++ b/drivers/net/phy/bcm63xx.c
@@ -56,7 +56,6 @@ static struct phy_driver bcm63xx_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
/* same phy as above, with just a different OUI */
.phy_id = 0x002bdc00,
@@ -69,7 +68,6 @@ static struct phy_driver bcm63xx_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
} };
module_phy_driver(bcm63xx_driver);
diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c
index 03d4809a9126..bf241a3ec5e5 100644
--- a/drivers/net/phy/bcm7xxx.c
+++ b/drivers/net/phy/bcm7xxx.c
@@ -170,7 +170,7 @@ static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
int ret = 0;
pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n",
- dev_name(&phydev->dev), phydev->drv->name, rev, patch);
+ phydev_name(phydev), phydev->drv->name, rev, patch);
/* Dummy read to a register to workaround an issue upon reset where the
* internal inverter may not allow the first MDIO transaction to pass
@@ -324,7 +324,6 @@ static int bcm7xxx_dummy_config_init(struct phy_device *phydev)
.config_aneg = genphy_config_aneg, \
.read_status = genphy_read_status, \
.resume = bcm7xxx_28nm_resume, \
- .driver = { .owner = THIS_MODULE }, \
}
static struct phy_driver bcm7xxx_driver[] = {
@@ -346,7 +345,6 @@ static struct phy_driver bcm7xxx_driver[] = {
.read_status = genphy_read_status,
.suspend = bcm7xxx_suspend,
.resume = bcm7xxx_config_init,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM7429,
.phy_id_mask = 0xfffffff0,
@@ -359,7 +357,18 @@ static struct phy_driver bcm7xxx_driver[] = {
.read_status = genphy_read_status,
.suspend = bcm7xxx_suspend,
.resume = bcm7xxx_config_init,
- .driver = { .owner = THIS_MODULE },
+}, {
+ .phy_id = PHY_ID_BCM7435,
+ .phy_id_mask = 0xfffffff0,
+ .name = "Broadcom BCM7435",
+ .features = PHY_GBIT_FEATURES |
+ SUPPORTED_Pause | SUPPORTED_Asym_Pause,
+ .flags = PHY_IS_INTERNAL,
+ .config_init = bcm7xxx_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .suspend = bcm7xxx_suspend,
+ .resume = bcm7xxx_config_init,
}, {
.phy_id = PHY_BCM_OUI_4,
.phy_id_mask = 0xffff0000,
@@ -372,7 +381,6 @@ static struct phy_driver bcm7xxx_driver[] = {
.read_status = genphy_read_status,
.suspend = bcm7xxx_suspend,
.resume = bcm7xxx_config_init,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_BCM_OUI_5,
.phy_id_mask = 0xffffff00,
@@ -385,7 +393,6 @@ static struct phy_driver bcm7xxx_driver[] = {
.read_status = genphy_read_status,
.suspend = bcm7xxx_suspend,
.resume = bcm7xxx_config_init,
- .driver = { .owner = THIS_MODULE },
} };
static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {
@@ -395,6 +402,7 @@ static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {
{ PHY_ID_BCM7425, 0xfffffff0, },
{ PHY_ID_BCM7429, 0xfffffff0, },
{ PHY_ID_BCM7439, 0xfffffff0, },
+ { PHY_ID_BCM7435, 0xfffffff0, },
{ PHY_ID_BCM7445, 0xfffffff0, },
{ PHY_BCM_OUI_4, 0xffff0000 },
{ PHY_BCM_OUI_5, 0xffffff00 },
diff --git a/drivers/net/phy/bcm87xx.c b/drivers/net/phy/bcm87xx.c
index 1eca20452f03..f7ebdcff53e4 100644
--- a/drivers/net/phy/bcm87xx.c
+++ b/drivers/net/phy/bcm87xx.c
@@ -40,10 +40,10 @@ static int bcm87xx_of_reg_init(struct phy_device *phydev)
const __be32 *paddr_end;
int len, ret;
- if (!phydev->dev.of_node)
+ if (!phydev->mdio.dev.of_node)
return 0;
- paddr = of_get_property(phydev->dev.of_node,
+ paddr = of_get_property(phydev->mdio.dev.of_node,
"broadcom,c45-reg-init", &len);
if (!paddr)
return 0;
@@ -163,8 +163,9 @@ static int bcm87xx_did_interrupt(struct phy_device *phydev)
reg = phy_read(phydev, BCM87XX_LASI_STATUS);
if (reg < 0) {
- dev_err(&phydev->dev,
- "Error: Read of BCM87XX_LASI_STATUS failed: %d\n", reg);
+ phydev_err(phydev,
+ "Error: Read of BCM87XX_LASI_STATUS failed: %d\n",
+ reg);
return 0;
}
return (reg & 1) != 0;
@@ -200,7 +201,6 @@ static struct phy_driver bcm87xx_driver[] = {
.config_intr = bcm87xx_config_intr,
.did_interrupt = bcm87xx_did_interrupt,
.match_phy_device = bcm8706_match_phy_device,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM8727,
.phy_id_mask = 0xffffffff,
@@ -213,7 +213,6 @@ static struct phy_driver bcm87xx_driver[] = {
.config_intr = bcm87xx_config_intr,
.did_interrupt = bcm87xx_did_interrupt,
.match_phy_device = bcm8727_match_phy_device,
- .driver = { .owner = THIS_MODULE },
} };
module_phy_driver(bcm87xx_driver);
diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
index 07a6119121c3..870327efccf7 100644
--- a/drivers/net/phy/broadcom.c
+++ b/drivers/net/phy/broadcom.c
@@ -460,7 +460,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM5421,
.phy_id_mask = 0xfffffff0,
@@ -473,7 +472,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM5461,
.phy_id_mask = 0xfffffff0,
@@ -486,7 +484,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM54616S,
.phy_id_mask = 0xfffffff0,
@@ -499,7 +496,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM5464,
.phy_id_mask = 0xfffffff0,
@@ -512,7 +508,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM5481,
.phy_id_mask = 0xfffffff0,
@@ -525,7 +520,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM5482,
.phy_id_mask = 0xfffffff0,
@@ -538,7 +532,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = bcm5482_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM50610,
.phy_id_mask = 0xfffffff0,
@@ -551,7 +544,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM50610M,
.phy_id_mask = 0xfffffff0,
@@ -564,7 +556,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM57780,
.phy_id_mask = 0xfffffff0,
@@ -577,7 +568,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = bcm_phy_ack_intr,
.config_intr = bcm_phy_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCMAC131,
.phy_id_mask = 0xfffffff0,
@@ -590,7 +580,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = brcm_fet_ack_interrupt,
.config_intr = brcm_fet_config_intr,
- .driver = { .owner = THIS_MODULE },
}, {
.phy_id = PHY_ID_BCM5241,
.phy_id_mask = 0xfffffff0,
@@ -603,7 +592,6 @@ static struct phy_driver broadcom_drivers[] = {
.read_status = genphy_read_status,
.ack_interrupt = brcm_fet_ack_interrupt,
.config_intr = brcm_fet_config_intr,
- .driver = { .owner = THIS_MODULE },
} };
module_phy_driver(broadcom_drivers);
@@ -614,7 +602,7 @@ static struct mdio_device_id __maybe_unused broadcom_tbl[] = {
{ PHY_ID_BCM5461, 0xfffffff0 },
{ PHY_ID_BCM54616S, 0xfffffff0 },
{ PHY_ID_BCM5464, 0xfffffff0 },
- { PHY_ID_BCM5482, 0xfffffff0 },
+ { PHY_ID_BCM5481, 0xfffffff0 },
{ PHY_ID_BCM5482, 0xfffffff0 },
{ PHY_ID_BCM50610, 0xfffffff0 },
{ PHY_ID_BCM50610M, 0xfffffff0 },
diff --git a/drivers/net/phy/cicada.c b/drivers/net/phy/cicada.c
index 27f5464899d4..d339c1afea77 100644
--- a/drivers/net/phy/cicada.c
+++ b/drivers/net/phy/cicada.c
@@ -114,7 +114,6 @@ static struct phy_driver cis820x_driver[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &cis820x_ack_interrupt,
.config_intr = &cis820x_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x000fc440,
.name = "Cicada Cis8204",
@@ -126,7 +125,6 @@ static struct phy_driver cis820x_driver[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &cis820x_ack_interrupt,
.config_intr = &cis820x_config_intr,
- .driver = { .owner = THIS_MODULE,},
} };
module_phy_driver(cis820x_driver);
diff --git a/drivers/net/phy/davicom.c b/drivers/net/phy/davicom.c
index 2a328703b4ae..36e3e2033eca 100644
--- a/drivers/net/phy/davicom.c
+++ b/drivers/net/phy/davicom.c
@@ -156,7 +156,6 @@ static struct phy_driver dm91xx_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = dm9161_ack_interrupt,
.config_intr = dm9161_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x0181b8b0,
.name = "Davicom DM9161B/C",
@@ -168,7 +167,6 @@ static struct phy_driver dm91xx_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = dm9161_ack_interrupt,
.config_intr = dm9161_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x0181b8a0,
.name = "Davicom DM9161A",
@@ -180,7 +178,6 @@ static struct phy_driver dm91xx_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = dm9161_ack_interrupt,
.config_intr = dm9161_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x00181b80,
.name = "Davicom DM9131",
@@ -191,7 +188,6 @@ static struct phy_driver dm91xx_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = dm9161_ack_interrupt,
.config_intr = dm9161_config_intr,
- .driver = { .owner = THIS_MODULE,},
} };
module_phy_driver(dm91xx_driver);
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index 47b711739ba9..180f69952779 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -220,9 +220,10 @@ static void rx_timestamp_work(struct work_struct *work);
#define BROADCAST_ADDR 31
-static inline int broadcast_write(struct mii_bus *bus, u32 regnum, u16 val)
+static inline int broadcast_write(struct phy_device *phydev, u32 regnum,
+ u16 val)
{
- return mdiobus_write(bus, BROADCAST_ADDR, regnum, val);
+ return mdiobus_write(phydev->mdio.bus, BROADCAST_ADDR, regnum, val);
}
/* Caller must hold extreg_lock. */
@@ -232,7 +233,7 @@ static int ext_read(struct phy_device *phydev, int page, u32 regnum)
int val;
if (dp83640->clock->page != page) {
- broadcast_write(phydev->bus, PAGESEL, page);
+ broadcast_write(phydev, PAGESEL, page);
dp83640->clock->page = page;
}
val = phy_read(phydev, regnum);
@@ -247,11 +248,11 @@ static void ext_write(int broadcast, struct phy_device *phydev,
struct dp83640_private *dp83640 = phydev->priv;
if (dp83640->clock->page != page) {
- broadcast_write(phydev->bus, PAGESEL, page);
+ broadcast_write(phydev, PAGESEL, page);
dp83640->clock->page = page;
}
if (broadcast)
- broadcast_write(phydev->bus, regnum, val);
+ broadcast_write(phydev, regnum, val);
else
phy_write(phydev, regnum, val);
}
@@ -1039,7 +1040,7 @@ static int choose_this_phy(struct dp83640_clock *clock,
if (chosen_phy == -1 && !clock->chosen)
return 1;
- if (chosen_phy == phydev->addr)
+ if (chosen_phy == phydev->mdio.addr)
return 1;
return 0;
@@ -1103,10 +1104,10 @@ static int dp83640_probe(struct phy_device *phydev)
struct dp83640_private *dp83640;
int err = -ENOMEM, i;
- if (phydev->addr == BROADCAST_ADDR)
+ if (phydev->mdio.addr == BROADCAST_ADDR)
return 0;
- clock = dp83640_clock_get_bus(phydev->bus);
+ clock = dp83640_clock_get_bus(phydev->mdio.bus);
if (!clock)
goto no_clock;
@@ -1132,7 +1133,8 @@ static int dp83640_probe(struct phy_device *phydev)
if (choose_this_phy(clock, phydev)) {
clock->chosen = dp83640;
- clock->ptp_clock = ptp_clock_register(&clock->caps, &phydev->dev);
+ clock->ptp_clock = ptp_clock_register(&clock->caps,
+ &phydev->mdio.dev);
if (IS_ERR(clock->ptp_clock)) {
err = PTR_ERR(clock->ptp_clock);
goto no_register;
@@ -1158,7 +1160,7 @@ static void dp83640_remove(struct phy_device *phydev)
struct list_head *this, *next;
struct dp83640_private *tmp, *dp83640 = phydev->priv;
- if (phydev->addr == BROADCAST_ADDR)
+ if (phydev->mdio.addr == BROADCAST_ADDR)
return;
enable_status_frames(phydev, false);
@@ -1490,12 +1492,11 @@ static struct phy_driver dp83640_driver = {
.hwtstamp = dp83640_hwtstamp,
.rxtstamp = dp83640_rxtstamp,
.txtstamp = dp83640_txtstamp,
- .driver = {.owner = THIS_MODULE,}
};
static int __init dp83640_init(void)
{
- return phy_driver_register(&dp83640_driver);
+ return phy_driver_register(&dp83640_driver, THIS_MODULE);
}
static void __exit dp83640_exit(void)
diff --git a/drivers/net/phy/dp83848.c b/drivers/net/phy/dp83848.c
index 5ce9bef54468..5e14e629c597 100644
--- a/drivers/net/phy/dp83848.c
+++ b/drivers/net/phy/dp83848.c
@@ -88,8 +88,6 @@ static struct phy_driver dp83848_driver[] = {
/* IRQ related */
.ack_interrupt = dp83848_ack_interrupt,
.config_intr = dp83848_config_intr,
-
- .driver = { .owner = THIS_MODULE, },
},
};
module_phy_driver(dp83848_driver);
diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c
index 32f10662f4ac..2afa61b51d41 100644
--- a/drivers/net/phy/dp83867.c
+++ b/drivers/net/phy/dp83867.c
@@ -103,14 +103,11 @@ static int dp83867_config_intr(struct phy_device *phydev)
static int dp83867_of_init(struct phy_device *phydev)
{
struct dp83867_private *dp83867 = phydev->priv;
- struct device *dev = &phydev->dev;
+ struct device *dev = &phydev->mdio.dev;
struct device_node *of_node = dev->of_node;
int ret;
- if (!of_node && dev->parent->of_node)
- of_node = dev->parent->of_node;
-
- if (!phydev->dev.of_node)
+ if (!of_node)
return -ENODEV;
ret = of_property_read_u32(of_node, "ti,rx-internal-delay",
@@ -140,7 +137,7 @@ static int dp83867_config_init(struct phy_device *phydev)
u16 val, delay;
if (!phydev->priv) {
- dp83867 = devm_kzalloc(&phydev->dev, sizeof(*dp83867),
+ dp83867 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83867),
GFP_KERNEL);
if (!dp83867)
return -ENOMEM;
@@ -163,7 +160,7 @@ static int dp83867_config_init(struct phy_device *phydev)
if ((phydev->interface >= PHY_INTERFACE_MODE_RGMII_ID) &&
(phydev->interface <= PHY_INTERFACE_MODE_RGMII_RXID)) {
val = phy_read_mmd_indirect(phydev, DP83867_RGMIICTL,
- DP83867_DEVADDR, phydev->addr);
+ DP83867_DEVADDR);
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
val |= (DP83867_RGMII_TX_CLK_DELAY_EN | DP83867_RGMII_RX_CLK_DELAY_EN);
@@ -175,13 +172,13 @@ static int dp83867_config_init(struct phy_device *phydev)
val |= DP83867_RGMII_RX_CLK_DELAY_EN;
phy_write_mmd_indirect(phydev, DP83867_RGMIICTL,
- DP83867_DEVADDR, phydev->addr, val);
+ DP83867_DEVADDR, val);
delay = (dp83867->rx_id_delay |
(dp83867->tx_id_delay << DP83867_RGMII_TX_CLK_DELAY_SHIFT));
phy_write_mmd_indirect(phydev, DP83867_RGMIIDCTL,
- DP83867_DEVADDR, phydev->addr, delay);
+ DP83867_DEVADDR, delay);
}
return 0;
@@ -217,8 +214,6 @@ static struct phy_driver dp83867_driver[] = {
.read_status = genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
-
- .driver = {.owner = THIS_MODULE,}
},
};
module_phy_driver(dp83867_driver);
diff --git a/drivers/net/phy/et1011c.c b/drivers/net/phy/et1011c.c
index a907743816a8..a9a4edfa23c8 100644
--- a/drivers/net/phy/et1011c.c
+++ b/drivers/net/phy/et1011c.c
@@ -95,7 +95,6 @@ static struct phy_driver et1011c_driver[] = { {
.flags = PHY_POLL,
.config_aneg = et1011c_config_aneg,
.read_status = et1011c_read_status,
- .driver = { .owner = THIS_MODULE,},
} };
module_phy_driver(et1011c_driver);
diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c
index e23bf5b90e17..ab9c473d75ea 100644
--- a/drivers/net/phy/fixed_phy.c
+++ b/drivers/net/phy/fixed_phy.c
@@ -27,7 +27,6 @@
#define MII_REGS_NUM 29
struct fixed_mdio_bus {
- int irqs[PHY_MAX_ADDR];
struct mii_bus *mii_bus;
struct list_head phys;
};
@@ -198,11 +197,11 @@ int fixed_phy_set_link_update(struct phy_device *phydev,
struct fixed_mdio_bus *fmb = &platform_fmb;
struct fixed_phy *fp;
- if (!phydev || !phydev->bus)
+ if (!phydev || !phydev->mdio.bus)
return -EINVAL;
list_for_each_entry(fp, &fmb->phys, node) {
- if (fp->addr == phydev->addr) {
+ if (fp->addr == phydev->mdio.addr) {
fp->link_update = link_update;
fp->phydev = phydev;
return 0;
@@ -220,11 +219,11 @@ int fixed_phy_update_state(struct phy_device *phydev,
struct fixed_mdio_bus *fmb = &platform_fmb;
struct fixed_phy *fp;
- if (!phydev || phydev->bus != fmb->mii_bus)
+ if (!phydev || phydev->mdio.bus != fmb->mii_bus)
return -EINVAL;
list_for_each_entry(fp, &fmb->phys, node) {
- if (fp->addr == phydev->addr) {
+ if (fp->addr == phydev->mdio.addr) {
#define _UPD(x) if (changed->x) \
fp->status.x = status->x
_UPD(link);
@@ -256,7 +255,7 @@ int fixed_phy_add(unsigned int irq, int phy_addr,
memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM);
- fmb->irqs[phy_addr] = irq;
+ fmb->mii_bus->irq[phy_addr] = irq;
fp->addr = phy_addr;
fp->status = *status;
@@ -345,7 +344,7 @@ struct phy_device *fixed_phy_register(unsigned int irq,
}
of_node_get(np);
- phy->dev.of_node = np;
+ phy->mdio.dev.of_node = np;
phy->is_pseudo_fixed_link = true;
switch (status->speed) {
@@ -395,7 +394,6 @@ static int __init fixed_mdio_bus_init(void)
fmb->mii_bus->parent = &pdev->dev;
fmb->mii_bus->read = &fixed_mdio_read;
fmb->mii_bus->write = &fixed_mdio_write;
- fmb->mii_bus->irq = fmb->irqs;
ret = mdiobus_register(fmb->mii_bus);
if (ret)
diff --git a/drivers/net/phy/icplus.c b/drivers/net/phy/icplus.c
index 0dbc445a5fa0..e5f251b91578 100644
--- a/drivers/net/phy/icplus.c
+++ b/drivers/net/phy/icplus.c
@@ -53,43 +53,43 @@ static int ip175c_config_init(struct phy_device *phydev)
if (full_reset_performed == 0) {
/* master reset */
- err = mdiobus_write(phydev->bus, 30, 0, 0x175c);
+ err = mdiobus_write(phydev->mdio.bus, 30, 0, 0x175c);
if (err < 0)
return err;
/* ensure no bus delays overlap reset period */
- err = mdiobus_read(phydev->bus, 30, 0);
+ err = mdiobus_read(phydev->mdio.bus, 30, 0);
/* data sheet specifies reset period is 2 msec */
mdelay(2);
/* enable IP175C mode */
- err = mdiobus_write(phydev->bus, 29, 31, 0x175c);
+ err = mdiobus_write(phydev->mdio.bus, 29, 31, 0x175c);
if (err < 0)
return err;
/* Set MII0 speed and duplex (in PHY mode) */
- err = mdiobus_write(phydev->bus, 29, 22, 0x420);
+ err = mdiobus_write(phydev->mdio.bus, 29, 22, 0x420);
if (err < 0)
return err;
/* reset switch ports */
for (i = 0; i < 5; i++) {
- err = mdiobus_write(phydev->bus, i,
+ err = mdiobus_write(phydev->mdio.bus, i,
MII_BMCR, BMCR_RESET);
if (err < 0)
return err;
}
for (i = 0; i < 5; i++)
- err = mdiobus_read(phydev->bus, i, MII_BMCR);
+ err = mdiobus_read(phydev->mdio.bus, i, MII_BMCR);
mdelay(2);
full_reset_performed = 1;
}
- if (phydev->addr != 4) {
+ if (phydev->mdio.addr != 4) {
phydev->state = PHY_RUNNING;
phydev->speed = SPEED_100;
phydev->duplex = DUPLEX_FULL;
@@ -184,7 +184,7 @@ static int ip101a_g_config_init(struct phy_device *phydev)
static int ip175c_read_status(struct phy_device *phydev)
{
- if (phydev->addr == 4) /* WAN port */
+ if (phydev->mdio.addr == 4) /* WAN port */
genphy_read_status(phydev);
else
/* Don't need to read status for switch ports */
@@ -195,7 +195,7 @@ static int ip175c_read_status(struct phy_device *phydev)
static int ip175c_config_aneg(struct phy_device *phydev)
{
- if (phydev->addr == 4) /* WAN port */
+ if (phydev->mdio.addr == 4) /* WAN port */
genphy_config_aneg(phydev);
return 0;
@@ -221,7 +221,6 @@ static struct phy_driver icplus_driver[] = {
.read_status = &ip175c_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x02430d90,
.name = "ICPlus IP1001",
@@ -233,7 +232,6 @@ static struct phy_driver icplus_driver[] = {
.read_status = &genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x02430c54,
.name = "ICPlus IP101A/G",
@@ -247,7 +245,6 @@ static struct phy_driver icplus_driver[] = {
.read_status = &genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
} };
module_phy_driver(icplus_driver);
diff --git a/drivers/net/phy/lxt.c b/drivers/net/phy/lxt.c
index a3a5a703635b..f6078376ef50 100644
--- a/drivers/net/phy/lxt.c
+++ b/drivers/net/phy/lxt.c
@@ -278,7 +278,6 @@ static struct phy_driver lxt97x_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = lxt970_ack_interrupt,
.config_intr = lxt970_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x001378e0,
.name = "LXT971",
@@ -289,7 +288,6 @@ static struct phy_driver lxt97x_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = lxt971_ack_interrupt,
.config_intr = lxt971_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x00137a10,
.name = "LXT973-A2",
@@ -299,7 +297,6 @@ static struct phy_driver lxt97x_driver[] = {
.probe = lxt973_probe,
.config_aneg = lxt973_config_aneg,
.read_status = lxt973a2_read_status,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x00137a10,
.name = "LXT973",
@@ -309,7 +306,6 @@ static struct phy_driver lxt97x_driver[] = {
.probe = lxt973_probe,
.config_aneg = lxt973_config_aneg,
.read_status = genphy_read_status,
- .driver = { .owner = THIS_MODULE,},
} };
module_phy_driver(lxt97x_driver);
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index 0240552b50f3..e3eb96443c97 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -137,6 +137,22 @@ MODULE_DESCRIPTION("Marvell PHY driver");
MODULE_AUTHOR("Andy Fleming");
MODULE_LICENSE("GPL");
+struct marvell_hw_stat {
+ const char *string;
+ u8 page;
+ u8 reg;
+ u8 bits;
+};
+
+static struct marvell_hw_stat marvell_hw_stats[] = {
+ { "phy_receive_errors", 0, 21, 16},
+ { "phy_idle_errors", 0, 10, 8 },
+};
+
+struct marvell_priv {
+ u64 stats[ARRAY_SIZE(marvell_hw_stats)];
+};
+
static int marvell_ack_interrupt(struct phy_device *phydev)
{
int err;
@@ -284,10 +300,11 @@ static int marvell_of_reg_init(struct phy_device *phydev)
const __be32 *paddr;
int len, i, saved_page, current_page, page_changed, ret;
- if (!phydev->dev.of_node)
+ if (!phydev->mdio.dev.of_node)
return 0;
- paddr = of_get_property(phydev->dev.of_node, "marvell,reg-init", &len);
+ paddr = of_get_property(phydev->mdio.dev.of_node,
+ "marvell,reg-init", &len);
if (!paddr || len < (4 * sizeof(*paddr)))
return 0;
@@ -986,12 +1003,80 @@ static int m88e1318_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *w
return 0;
}
+static int marvell_get_sset_count(struct phy_device *phydev)
+{
+ return ARRAY_SIZE(marvell_hw_stats);
+}
+
+static void marvell_get_strings(struct phy_device *phydev, u8 *data)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++) {
+ memcpy(data + i * ETH_GSTRING_LEN,
+ marvell_hw_stats[i].string, ETH_GSTRING_LEN);
+ }
+}
+
+#ifndef UINT64_MAX
+#define UINT64_MAX (u64)(~((u64)0))
+#endif
+static u64 marvell_get_stat(struct phy_device *phydev, int i)
+{
+ struct marvell_hw_stat stat = marvell_hw_stats[i];
+ struct marvell_priv *priv = phydev->priv;
+ int err, oldpage;
+ u64 val;
+
+ oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
+ err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
+ stat.page);
+ if (err < 0)
+ return UINT64_MAX;
+
+ val = phy_read(phydev, stat.reg);
+ if (val < 0) {
+ val = UINT64_MAX;
+ } else {
+ val = val & ((1 << stat.bits) - 1);
+ priv->stats[i] += val;
+ val = priv->stats[i];
+ }
+
+ phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
+
+ return val;
+}
+
+static void marvell_get_stats(struct phy_device *phydev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(marvell_hw_stats); i++)
+ data[i] = marvell_get_stat(phydev, i);
+}
+
+static int marvell_probe(struct phy_device *phydev)
+{
+ struct marvell_priv *priv;
+
+ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ phydev->priv = priv;
+
+ return 0;
+}
+
static struct phy_driver marvell_drivers[] = {
{
.phy_id = MARVELL_PHY_ID_88E1101,
.phy_id_mask = MARVELL_PHY_ID_MASK,
.name = "Marvell 88E1101",
.features = PHY_GBIT_FEATURES,
+ .probe = marvell_probe,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = &marvell_config_aneg,
.read_status = &genphy_read_status,
@@ -999,7 +1084,9 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1112,
@@ -1007,6 +1094,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1112",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_init = &m88e1111_config_init,
.config_aneg = &marvell_config_aneg,
.read_status = &genphy_read_status,
@@ -1014,7 +1102,9 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1111,
@@ -1022,6 +1112,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1111",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_init = &m88e1111_config_init,
.config_aneg = &marvell_config_aneg,
.read_status = &marvell_read_status,
@@ -1029,7 +1120,9 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1118,
@@ -1037,6 +1130,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1118",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_init = &m88e1118_config_init,
.config_aneg = &m88e1118_config_aneg,
.read_status = &genphy_read_status,
@@ -1044,7 +1138,9 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = {.owner = THIS_MODULE,},
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1121R,
@@ -1052,6 +1148,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1121R",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_aneg = &m88e1121_config_aneg,
.read_status = &marvell_read_status,
.ack_interrupt = &marvell_ack_interrupt,
@@ -1059,7 +1156,9 @@ static struct phy_driver marvell_drivers[] = {
.did_interrupt = &m88e1121_did_interrupt,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1318S,
@@ -1067,6 +1166,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1318S",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_aneg = &m88e1318_config_aneg,
.read_status = &marvell_read_status,
.ack_interrupt = &marvell_ack_interrupt,
@@ -1076,7 +1176,9 @@ static struct phy_driver marvell_drivers[] = {
.set_wol = &m88e1318_set_wol,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1145,
@@ -1084,6 +1186,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1145",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_init = &m88e1145_config_init,
.config_aneg = &marvell_config_aneg,
.read_status = &genphy_read_status,
@@ -1091,7 +1194,9 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1149R,
@@ -1099,6 +1204,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1149R",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_init = &m88e1149_config_init,
.config_aneg = &m88e1118_config_aneg,
.read_status = &genphy_read_status,
@@ -1106,7 +1212,9 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1240,
@@ -1114,6 +1222,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1240",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_init = &m88e1111_config_init,
.config_aneg = &marvell_config_aneg,
.read_status = &genphy_read_status,
@@ -1121,7 +1230,9 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1116R,
@@ -1129,6 +1240,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1116R",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_init = &m88e1116r_config_init,
.config_aneg = &genphy_config_aneg,
.read_status = &genphy_read_status,
@@ -1136,7 +1248,9 @@ static struct phy_driver marvell_drivers[] = {
.config_intr = &marvell_config_intr,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1510,
@@ -1144,6 +1258,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1510",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_aneg = &m88e1510_config_aneg,
.read_status = &marvell_read_status,
.ack_interrupt = &marvell_ack_interrupt,
@@ -1151,7 +1266,9 @@ static struct phy_driver marvell_drivers[] = {
.did_interrupt = &m88e1121_did_interrupt,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E1540,
@@ -1159,6 +1276,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1540",
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_aneg = &m88e1510_config_aneg,
.read_status = &marvell_read_status,
.ack_interrupt = &marvell_ack_interrupt,
@@ -1166,7 +1284,9 @@ static struct phy_driver marvell_drivers[] = {
.did_interrupt = &m88e1121_did_interrupt,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
{
.phy_id = MARVELL_PHY_ID_88E3016,
@@ -1174,6 +1294,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E3016",
.features = PHY_BASIC_FEATURES,
.flags = PHY_HAS_INTERRUPT,
+ .probe = marvell_probe,
.config_aneg = &genphy_config_aneg,
.config_init = &m88e3016_config_init,
.aneg_done = &marvell_aneg_done,
@@ -1183,7 +1304,9 @@ static struct phy_driver marvell_drivers[] = {
.did_interrupt = &m88e1121_did_interrupt,
.resume = &genphy_resume,
.suspend = &genphy_suspend,
- .driver = { .owner = THIS_MODULE },
+ .get_sset_count = marvell_get_sset_count,
+ .get_strings = marvell_get_strings,
+ .get_stats = marvell_get_stats,
},
};
diff --git a/drivers/net/phy/mdio-bcm-unimac.c b/drivers/net/phy/mdio-bcm-unimac.c
index 4bde5e728fe0..8c73b2e771dd 100644
--- a/drivers/net/phy/mdio-bcm-unimac.c
+++ b/drivers/net/phy/mdio-bcm-unimac.c
@@ -200,16 +200,10 @@ static int unimac_mdio_probe(struct platform_device *pdev)
bus->reset = unimac_mdio_reset;
snprintf(bus->id, MII_BUS_ID_SIZE, "%s", pdev->name);
- bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
- if (!bus->irq) {
- ret = -ENOMEM;
- goto out_mdio_free;
- }
-
ret = of_mdiobus_register(bus, np);
if (ret) {
dev_err(&pdev->dev, "MDIO bus registration failed\n");
- goto out_mdio_irq;
+ goto out_mdio_free;
}
platform_set_drvdata(pdev, priv);
@@ -218,8 +212,6 @@ static int unimac_mdio_probe(struct platform_device *pdev)
return 0;
-out_mdio_irq:
- kfree(bus->irq);
out_mdio_free:
mdiobus_free(bus);
return ret;
@@ -230,7 +222,6 @@ static int unimac_mdio_remove(struct platform_device *pdev)
struct unimac_mdio_priv *priv = platform_get_drvdata(pdev);
mdiobus_unregister(priv->mii_bus);
- kfree(priv->mii_bus->irq);
mdiobus_free(priv->mii_bus);
return 0;
diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c
index 95f51d7267b3..27ab63064f95 100644
--- a/drivers/net/phy/mdio-gpio.c
+++ b/drivers/net/phy/mdio-gpio.c
@@ -159,7 +159,7 @@ static struct mii_bus *mdio_gpio_bus_init(struct device *dev,
new_bus->phy_mask = pdata->phy_mask;
new_bus->phy_ignore_ta_mask = pdata->phy_ignore_ta_mask;
- new_bus->irq = pdata->irqs;
+ memcpy(new_bus->irq, pdata->irqs, sizeof(new_bus->irq));
new_bus->parent = dev;
if (new_bus->phy_mask == ~0)
diff --git a/drivers/net/phy/mdio-moxart.c b/drivers/net/phy/mdio-moxart.c
index f1fc51f655d9..5bb56d126693 100644
--- a/drivers/net/phy/mdio-moxart.c
+++ b/drivers/net/phy/mdio-moxart.c
@@ -130,13 +130,6 @@ static int moxart_mdio_probe(struct platform_device *pdev)
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d-mii", pdev->name, pdev->id);
bus->parent = &pdev->dev;
- bus->irq = devm_kzalloc(&pdev->dev, sizeof(int) * PHY_MAX_ADDR,
- GFP_KERNEL);
- if (!bus->irq) {
- ret = -ENOMEM;
- goto err_out_free_mdiobus;
- }
-
/* Setting PHY_IGNORE_INTERRUPT here even if it has no effect,
* of_mdiobus_register() sets these PHY_POLL.
* Ideally, the interrupt from MAC controller could be used to
diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c
index 908e8d486342..308ade0eb1b6 100644
--- a/drivers/net/phy/mdio-mux.c
+++ b/drivers/net/phy/mdio-mux.c
@@ -34,7 +34,6 @@ struct mdio_mux_child_bus {
struct mdio_mux_parent_bus *parent;
struct mdio_mux_child_bus *next;
int bus_number;
- int phy_irq[PHY_MAX_ADDR];
};
/*
@@ -149,10 +148,15 @@ int mdio_mux_init(struct device *dev,
}
cb->bus_number = v;
cb->parent = pb;
+
cb->mii_bus = mdiobus_alloc();
+ if (!cb->mii_bus) {
+ ret_val = -ENOMEM;
+ of_node_put(child_bus_node);
+ break;
+ }
cb->mii_bus->priv = cb;
- cb->mii_bus->irq = cb->phy_irq;
cb->mii_bus->name = "mdio_mux";
snprintf(cb->mii_bus->id, MII_BUS_ID_SIZE, "%x.%x",
pb->parent_id, v);
diff --git a/drivers/net/phy/mdio-octeon.c b/drivers/net/phy/mdio-octeon.c
index fcf4e4df7cc8..47d4f2f263d1 100644
--- a/drivers/net/phy/mdio-octeon.c
+++ b/drivers/net/phy/mdio-octeon.c
@@ -113,7 +113,6 @@ struct octeon_mdiobus {
resource_size_t mdio_phys;
resource_size_t regsize;
enum octeon_mdiobus_mode mode;
- int phy_irq[PHY_MAX_ADDR];
};
#ifdef CONFIG_CAVIUM_OCTEON_SOC
@@ -268,12 +267,13 @@ static int octeon_mdiobus_write(struct mii_bus *bus, int phy_id,
static int octeon_mdiobus_probe(struct platform_device *pdev)
{
struct octeon_mdiobus *bus;
+ struct mii_bus *mii_bus;
struct resource *res_mem;
union cvmx_smix_en smi_en;
int err = -ENOENT;
- bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
- if (!bus)
+ mii_bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*bus));
+ if (!mii_bus)
return -ENOMEM;
res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -282,6 +282,8 @@ static int octeon_mdiobus_probe(struct platform_device *pdev)
return -ENXIO;
}
+ bus = mii_bus->priv;
+ bus->mii_bus = mii_bus;
bus->mdio_phys = res_mem->start;
bus->regsize = resource_size(res_mem);
@@ -298,16 +300,11 @@ static int octeon_mdiobus_probe(struct platform_device *pdev)
return -ENOMEM;
}
- bus->mii_bus = mdiobus_alloc();
- if (!bus->mii_bus)
- goto fail;
-
smi_en.u64 = 0;
smi_en.s.en = 1;
oct_mdio_writeq(smi_en.u64, bus->register_base + SMI_EN);
bus->mii_bus->priv = bus;
- bus->mii_bus->irq = bus->phy_irq;
bus->mii_bus->name = "mdio-octeon";
snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%llx", bus->register_base);
bus->mii_bus->parent = &pdev->dev;
@@ -326,7 +323,6 @@ static int octeon_mdiobus_probe(struct platform_device *pdev)
return 0;
fail_register:
mdiobus_free(bus->mii_bus);
-fail:
smi_en.u64 = 0;
oct_mdio_writeq(smi_en.u64, bus->register_base + SMI_EN);
return err;
diff --git a/drivers/net/phy/mdio-sun4i.c b/drivers/net/phy/mdio-sun4i.c
index 15bc7f9ea224..f70522c35163 100644
--- a/drivers/net/phy/mdio-sun4i.c
+++ b/drivers/net/phy/mdio-sun4i.c
@@ -96,7 +96,7 @@ static int sun4i_mdio_probe(struct platform_device *pdev)
struct mii_bus *bus;
struct sun4i_mdio_data *data;
struct resource *res;
- int ret, i;
+ int ret;
bus = mdiobus_alloc_size(sizeof(*data));
if (!bus)
@@ -108,16 +108,6 @@ static int sun4i_mdio_probe(struct platform_device *pdev)
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
bus->parent = &pdev->dev;
- bus->irq = devm_kzalloc(&pdev->dev, sizeof(int) * PHY_MAX_ADDR,
- GFP_KERNEL);
- if (!bus->irq) {
- ret = -ENOMEM;
- goto err_out_free_mdiobus;
- }
-
- for (i = 0; i < PHY_MAX_ADDR; i++)
- bus->irq[i] = PHY_POLL;
-
data = bus->priv;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
data->membase = devm_ioremap_resource(&pdev->dev, res);
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 88cb4592b6fb..0cba64f1ecf4 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -38,6 +38,48 @@
#include <asm/irq.h>
+int mdiobus_register_device(struct mdio_device *mdiodev)
+{
+ if (mdiodev->bus->mdio_map[mdiodev->addr])
+ return -EBUSY;
+
+ mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev;
+
+ return 0;
+}
+EXPORT_SYMBOL(mdiobus_register_device);
+
+int mdiobus_unregister_device(struct mdio_device *mdiodev)
+{
+ if (mdiodev->bus->mdio_map[mdiodev->addr] != mdiodev)
+ return -EINVAL;
+
+ mdiodev->bus->mdio_map[mdiodev->addr] = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL(mdiobus_unregister_device);
+
+struct phy_device *mdiobus_get_phy(struct mii_bus *bus, int addr)
+{
+ struct mdio_device *mdiodev = bus->mdio_map[addr];
+
+ if (!mdiodev)
+ return NULL;
+
+ if (!(mdiodev->flags & MDIO_DEVICE_FLAG_PHY))
+ return NULL;
+
+ return container_of(mdiodev, struct phy_device, mdio);
+}
+EXPORT_SYMBOL(mdiobus_get_phy);
+
+bool mdiobus_is_registered_device(struct mii_bus *bus, int addr)
+{
+ return bus->mdio_map[addr];
+}
+EXPORT_SYMBOL(mdiobus_is_registered_device);
+
/**
* mdiobus_alloc_size - allocate a mii_bus structure
* @size: extra amount of memory to allocate for private storage.
@@ -51,6 +93,7 @@ struct mii_bus *mdiobus_alloc_size(size_t size)
struct mii_bus *bus;
size_t aligned_size = ALIGN(sizeof(*bus), NETDEV_ALIGN);
size_t alloc_size;
+ int i;
/* If we alloc extra space, it should be aligned */
if (size)
@@ -59,11 +102,16 @@ struct mii_bus *mdiobus_alloc_size(size_t size)
alloc_size = sizeof(*bus);
bus = kzalloc(alloc_size, GFP_KERNEL);
- if (bus) {
- bus->state = MDIOBUS_ALLOCATED;
- if (size)
- bus->priv = (void *)bus + aligned_size;
- }
+ if (!bus)
+ return NULL;
+
+ bus->state = MDIOBUS_ALLOCATED;
+ if (size)
+ bus->priv = (void *)bus + aligned_size;
+
+ /* Initialise the interrupts to polling */
+ for (i = 0; i < PHY_MAX_ADDR; i++)
+ bus->irq[i] = PHY_POLL;
return bus;
}
@@ -190,47 +238,48 @@ struct mii_bus *of_mdio_find_bus(struct device_node *mdio_bus_np)
}
EXPORT_SYMBOL(of_mdio_find_bus);
-/* Walk the list of subnodes of a mdio bus and look for a node that matches the
- * phy's address with its 'reg' property. If found, set the of_node pointer for
- * the phy. This allows auto-probed pyh devices to be supplied with information
- * passed in via DT.
+/* Walk the list of subnodes of a mdio bus and look for a node that
+ * matches the mdio device's address with its 'reg' property. If
+ * found, set the of_node pointer for the mdio device. This allows
+ * auto-probed phy devices to be supplied with information passed in
+ * via DT.
*/
-static void of_mdiobus_link_phydev(struct mii_bus *mdio,
- struct phy_device *phydev)
+static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
+ struct mdio_device *mdiodev)
{
- struct device *dev = &phydev->dev;
+ struct device *dev = &mdiodev->dev;
struct device_node *child;
- if (dev->of_node || !mdio->dev.of_node)
+ if (dev->of_node || !bus->dev.of_node)
return;
- for_each_available_child_of_node(mdio->dev.of_node, child) {
+ for_each_available_child_of_node(bus->dev.of_node, child) {
int addr;
int ret;
ret = of_property_read_u32(child, "reg", &addr);
if (ret < 0) {
- dev_err(dev, "%s has invalid PHY address\n",
+ dev_err(dev, "%s has invalid MDIO address\n",
child->full_name);
continue;
}
- /* A PHY must have a reg property in the range [0-31] */
+ /* A MDIO device must have a reg property in the range [0-31] */
if (addr >= PHY_MAX_ADDR) {
- dev_err(dev, "%s PHY address %i is too large\n",
+ dev_err(dev, "%s MDIO address %i is too large\n",
child->full_name, addr);
continue;
}
- if (addr == phydev->addr) {
+ if (addr == mdiodev->addr) {
dev->of_node = child;
return;
}
}
}
#else /* !IS_ENABLED(CONFIG_OF_MDIO) */
-static inline void of_mdiobus_link_phydev(struct mii_bus *mdio,
- struct phy_device *phydev)
+static inline void of_mdiobus_link_mdiodev(struct mii_bus *mdio,
+ struct mdio_device *mdiodev)
{
}
#endif
@@ -243,12 +292,15 @@ static inline void of_mdiobus_link_phydev(struct mii_bus *mdio,
* Description: Called by a bus driver to bring up all the PHYs
* on a given bus, and attach them to the bus. Drivers should use
* mdiobus_register() rather than __mdiobus_register() unless they
- * need to pass a specific owner module.
+ * need to pass a specific owner module. MDIO devices which are not
+ * PHYs will not be brought up by this function. They are expected to
+ * to be explicitly listed in DT and instantiated by of_mdiobus_register().
*
* Returns 0 on success or < 0 on error.
*/
int __mdiobus_register(struct mii_bus *bus, struct module *owner)
{
+ struct mdio_device *mdiodev;
int i, err;
if (NULL == bus || NULL == bus->name ||
@@ -294,11 +346,12 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner)
error:
while (--i >= 0) {
- struct phy_device *phydev = bus->phy_map[i];
- if (phydev) {
- phy_device_remove(phydev);
- phy_device_free(phydev);
- }
+ mdiodev = bus->mdio_map[i];
+ if (!mdiodev)
+ continue;
+
+ mdiodev->device_remove(mdiodev);
+ mdiodev->device_free(mdiodev);
}
device_del(&bus->dev);
return err;
@@ -307,17 +360,19 @@ EXPORT_SYMBOL(__mdiobus_register);
void mdiobus_unregister(struct mii_bus *bus)
{
+ struct mdio_device *mdiodev;
int i;
BUG_ON(bus->state != MDIOBUS_REGISTERED);
bus->state = MDIOBUS_UNREGISTERED;
for (i = 0; i < PHY_MAX_ADDR; i++) {
- struct phy_device *phydev = bus->phy_map[i];
- if (phydev) {
- phy_device_remove(phydev);
- phy_device_free(phydev);
- }
+ mdiodev = bus->mdio_map[i];
+ if (!mdiodev)
+ continue;
+
+ mdiodev->device_remove(mdiodev);
+ mdiodev->device_free(mdiodev);
}
device_del(&bus->dev);
}
@@ -346,6 +401,18 @@ void mdiobus_free(struct mii_bus *bus)
}
EXPORT_SYMBOL(mdiobus_free);
+/**
+ * mdiobus_scan - scan a bus for MDIO devices.
+ * @bus: mii_bus to scan
+ * @addr: address on bus to scan
+ *
+ * This function scans the MDIO bus, looking for devices which can be
+ * identified using a vendor/product ID in registers 2 and 3. Not all
+ * MDIO devices have such registers, but PHY devices typically
+ * do. Hence this function assumes anything found is a PHY, or can be
+ * treated as a PHY. Other MDIO devices, such as switches, will
+ * probably not be found during the scan.
+ */
struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)
{
struct phy_device *phydev;
@@ -359,7 +426,7 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)
* For DT, see if the auto-probed phy has a correspoding child
* in the bus node, and set the of_node pointer in this case.
*/
- of_mdiobus_link_phydev(bus, phydev);
+ of_mdiobus_link_mdiodev(bus, &phydev->mdio);
err = phy_device_register(phydev);
if (err) {
@@ -476,133 +543,56 @@ int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val)
EXPORT_SYMBOL(mdiobus_write);
/**
- * mdio_bus_match - determine if given PHY driver supports the given PHY device
- * @dev: target PHY device
- * @drv: given PHY driver
+ * mdio_bus_match - determine if given MDIO driver supports the given
+ * MDIO device
+ * @dev: target MDIO device
+ * @drv: given MDIO driver
*
- * Description: Given a PHY device, and a PHY driver, return 1 if
- * the driver supports the device. Otherwise, return 0.
+ * Description: Given a MDIO device, and a MDIO driver, return 1 if
+ * the driver supports the device. Otherwise, return 0. This may
+ * require calling the devices own match function, since different classes
+ * of MDIO devices have different match criteria.
*/
static int mdio_bus_match(struct device *dev, struct device_driver *drv)
{
- struct phy_device *phydev = to_phy_device(dev);
- struct phy_driver *phydrv = to_phy_driver(drv);
- const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids);
- int i;
+ struct mdio_device *mdio = to_mdio_device(dev);
if (of_driver_match_device(dev, drv))
return 1;
- if (phydrv->match_phy_device)
- return phydrv->match_phy_device(phydev);
-
- if (phydev->is_c45) {
- for (i = 1; i < num_ids; i++) {
- if (!(phydev->c45_ids.devices_in_package & (1 << i)))
- continue;
+ if (mdio->bus_match)
+ return mdio->bus_match(dev, drv);
- if ((phydrv->phy_id & phydrv->phy_id_mask) ==
- (phydev->c45_ids.device_ids[i] &
- phydrv->phy_id_mask))
- return 1;
- }
- return 0;
- } else {
- return (phydrv->phy_id & phydrv->phy_id_mask) ==
- (phydev->phy_id & phydrv->phy_id_mask);
- }
+ return 0;
}
#ifdef CONFIG_PM
-
-static bool mdio_bus_phy_may_suspend(struct phy_device *phydev)
-{
- struct device_driver *drv = phydev->dev.driver;
- struct phy_driver *phydrv = to_phy_driver(drv);
- struct net_device *netdev = phydev->attached_dev;
-
- if (!drv || !phydrv->suspend)
- return false;
-
- /* PHY not attached? May suspend if the PHY has not already been
- * suspended as part of a prior call to phy_disconnect() ->
- * phy_detach() -> phy_suspend() because the parent netdev might be the
- * MDIO bus driver and clock gated at this point.
- */
- if (!netdev)
- return !phydev->suspended;
-
- /* Don't suspend PHY if the attched netdev parent may wakeup.
- * The parent may point to a PCI device, as in tg3 driver.
- */
- if (netdev->dev.parent && device_may_wakeup(netdev->dev.parent))
- return false;
-
- /* Also don't suspend PHY if the netdev itself may wakeup. This
- * is the case for devices w/o underlaying pwr. mgmt. aware bus,
- * e.g. SoC devices.
- */
- if (device_may_wakeup(&netdev->dev))
- return false;
-
- return true;
-}
-
static int mdio_bus_suspend(struct device *dev)
{
- struct phy_device *phydev = to_phy_device(dev);
+ struct mdio_device *mdio = to_mdio_device(dev);
- /* We must stop the state machine manually, otherwise it stops out of
- * control, possibly with the phydev->lock held. Upon resume, netdev
- * may call phy routines that try to grab the same lock, and that may
- * lead to a deadlock.
- */
- if (phydev->attached_dev && phydev->adjust_link)
- phy_stop_machine(phydev);
+ if (mdio->pm_ops && mdio->pm_ops->suspend)
+ return mdio->pm_ops->suspend(dev);
- if (!mdio_bus_phy_may_suspend(phydev))
- return 0;
-
- return phy_suspend(phydev);
+ return 0;
}
static int mdio_bus_resume(struct device *dev)
{
- struct phy_device *phydev = to_phy_device(dev);
- int ret;
-
- if (!mdio_bus_phy_may_suspend(phydev))
- goto no_resume;
-
- ret = phy_resume(phydev);
- if (ret < 0)
- return ret;
+ struct mdio_device *mdio = to_mdio_device(dev);
-no_resume:
- if (phydev->attached_dev && phydev->adjust_link)
- phy_start_machine(phydev);
+ if (mdio->pm_ops && mdio->pm_ops->resume)
+ return mdio->pm_ops->resume(dev);
return 0;
}
static int mdio_bus_restore(struct device *dev)
{
- struct phy_device *phydev = to_phy_device(dev);
- struct net_device *netdev = phydev->attached_dev;
- int ret;
-
- if (!netdev)
- return 0;
+ struct mdio_device *mdio = to_mdio_device(dev);
- ret = phy_init_hw(phydev);
- if (ret < 0)
- return ret;
-
- /* The PHY needs to renegotiate. */
- phydev->link = 0;
- phydev->state = PHY_UP;
-
- phy_start_machine(phydev);
+ if (mdio->pm_ops && mdio->pm_ops->restore)
+ return mdio->pm_ops->restore(dev);
return 0;
}
@@ -623,52 +613,10 @@ static const struct dev_pm_ops mdio_bus_pm_ops = {
#endif /* CONFIG_PM */
-static ssize_t
-phy_id_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct phy_device *phydev = to_phy_device(dev);
-
- return sprintf(buf, "0x%.8lx\n", (unsigned long)phydev->phy_id);
-}
-static DEVICE_ATTR_RO(phy_id);
-
-static ssize_t
-phy_interface_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct phy_device *phydev = to_phy_device(dev);
- const char *mode = NULL;
-
- if (phy_is_internal(phydev))
- mode = "internal";
- else
- mode = phy_modes(phydev->interface);
-
- return sprintf(buf, "%s\n", mode);
-}
-static DEVICE_ATTR_RO(phy_interface);
-
-static ssize_t
-phy_has_fixups_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct phy_device *phydev = to_phy_device(dev);
-
- return sprintf(buf, "%d\n", phydev->has_fixups);
-}
-static DEVICE_ATTR_RO(phy_has_fixups);
-
-static struct attribute *mdio_dev_attrs[] = {
- &dev_attr_phy_id.attr,
- &dev_attr_phy_interface.attr,
- &dev_attr_phy_has_fixups.attr,
- NULL,
-};
-ATTRIBUTE_GROUPS(mdio_dev);
-
struct bus_type mdio_bus_type = {
.name = "mdio_bus",
.match = mdio_bus_match,
.pm = MDIO_BUS_PM_OPS,
- .dev_groups = mdio_dev_groups,
};
EXPORT_SYMBOL(mdio_bus_type);
diff --git a/drivers/net/phy/mdio_device.c b/drivers/net/phy/mdio_device.c
new file mode 100644
index 000000000000..9c88e6749b9a
--- /dev/null
+++ b/drivers/net/phy/mdio_device.c
@@ -0,0 +1,171 @@
+/* Framework for MDIO devices, other than PHYs.
+ *
+ * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mdio.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/phy.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/unistd.h>
+
+void mdio_device_free(struct mdio_device *mdiodev)
+{
+ put_device(&mdiodev->dev);
+}
+EXPORT_SYMBOL(mdio_device_free);
+
+static void mdio_device_release(struct device *dev)
+{
+ kfree(to_mdio_device(dev));
+}
+
+struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr)
+{
+ struct mdio_device *mdiodev;
+
+ /* We allocate the device, and initialize the default values */
+ mdiodev = kzalloc(sizeof(*mdiodev), GFP_KERNEL);
+ if (!mdiodev)
+ return ERR_PTR(-ENOMEM);
+
+ mdiodev->dev.release = mdio_device_release;
+ mdiodev->dev.parent = &bus->dev;
+ mdiodev->dev.bus = &mdio_bus_type;
+ mdiodev->device_free = mdio_device_free;
+ mdiodev->device_remove = mdio_device_remove;
+ mdiodev->bus = bus;
+ mdiodev->addr = addr;
+
+ dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr);
+
+ device_initialize(&mdiodev->dev);
+
+ return mdiodev;
+}
+EXPORT_SYMBOL(mdio_device_create);
+
+/**
+ * mdio_device_register - Register the mdio device on the MDIO bus
+ * @mdiodev: mdio_device structure to be added to the MDIO bus
+ */
+int mdio_device_register(struct mdio_device *mdiodev)
+{
+ int err;
+
+ dev_info(&mdiodev->dev, "mdio_device_register\n");
+
+ err = mdiobus_register_device(mdiodev);
+ if (err)
+ return err;
+
+ err = device_add(&mdiodev->dev);
+ if (err) {
+ pr_err("MDIO %d failed to add\n", mdiodev->addr);
+ goto out;
+ }
+
+ return 0;
+
+ out:
+ mdiobus_unregister_device(mdiodev);
+ return err;
+}
+EXPORT_SYMBOL(mdio_device_register);
+
+/**
+ * mdio_device_remove - Remove a previously registered mdio device from the
+ * MDIO bus
+ * @mdiodev: mdio_device structure to remove
+ *
+ * This doesn't free the mdio_device itself, it merely reverses the effects
+ * of mdio_device_register(). Use mdio_device_free() to free the device
+ * after calling this function.
+ */
+void mdio_device_remove(struct mdio_device *mdiodev)
+{
+ device_del(&mdiodev->dev);
+ mdiobus_unregister_device(mdiodev);
+}
+EXPORT_SYMBOL(mdio_device_remove);
+
+/**
+ * mdio_probe - probe an MDIO device
+ * @dev: device to probe
+ *
+ * Description: Take care of setting up the mdio_device structure
+ * and calling the driver to probe the device.
+ */
+static int mdio_probe(struct device *dev)
+{
+ struct mdio_device *mdiodev = to_mdio_device(dev);
+ struct device_driver *drv = mdiodev->dev.driver;
+ struct mdio_driver *mdiodrv = to_mdio_driver(drv);
+ int err = 0;
+
+ if (mdiodrv->probe)
+ err = mdiodrv->probe(mdiodev);
+
+ return err;
+}
+
+static int mdio_remove(struct device *dev)
+{
+ struct mdio_device *mdiodev = to_mdio_device(dev);
+ struct device_driver *drv = mdiodev->dev.driver;
+ struct mdio_driver *mdiodrv = to_mdio_driver(drv);
+
+ if (mdiodrv->remove)
+ mdiodrv->remove(mdiodev);
+
+ return 0;
+}
+
+/**
+ * mdio_driver_register - register an mdio_driver with the MDIO layer
+ * @new_driver: new mdio_driver to register
+ */
+int mdio_driver_register(struct mdio_driver *drv)
+{
+ struct mdio_driver_common *mdiodrv = &drv->mdiodrv;
+ int retval;
+
+ pr_info("mdio_driver_register: %s\n", mdiodrv->driver.name);
+
+ mdiodrv->driver.bus = &mdio_bus_type;
+ mdiodrv->driver.probe = mdio_probe;
+ mdiodrv->driver.remove = mdio_remove;
+
+ retval = driver_register(&mdiodrv->driver);
+ if (retval) {
+ pr_err("%s: Error %d in registering driver\n",
+ mdiodrv->driver.name, retval);
+
+ return retval;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(mdio_driver_register);
+
+void mdio_driver_unregister(struct mdio_driver *drv)
+{
+ struct mdio_driver_common *mdiodrv = &drv->mdiodrv;
+
+ driver_unregister(&mdiodrv->driver);
+}
+EXPORT_SYMBOL(mdio_driver_unregister);
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index cf6312fafea5..03833dbfca67 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -73,6 +73,17 @@
#define PS_TO_REG 200
+struct kszphy_hw_stat {
+ const char *string;
+ u8 reg;
+ u8 bits;
+};
+
+static struct kszphy_hw_stat kszphy_hw_stats[] = {
+ { "phy_receive_errors", 21, 16},
+ { "phy_idle_errors", 10, 8 },
+};
+
struct kszphy_type {
u32 led_mode_reg;
u16 interrupt_level_mask;
@@ -86,6 +97,7 @@ struct kszphy_priv {
int led_mode;
bool rmii_ref_clk_sel;
bool rmii_ref_clk_sel_val;
+ u64 stats[ARRAY_SIZE(kszphy_hw_stats)];
};
static const struct kszphy_type ksz8021_type = {
@@ -212,7 +224,7 @@ static int kszphy_setup_led(struct phy_device *phydev, u32 reg, int val)
rc = phy_write(phydev, reg, temp);
out:
if (rc < 0)
- dev_err(&phydev->dev, "failed to set led mode\n");
+ phydev_err(phydev, "failed to set led mode\n");
return rc;
}
@@ -231,7 +243,7 @@ static int kszphy_broadcast_disable(struct phy_device *phydev)
ret = phy_write(phydev, MII_KSZPHY_OMSO, ret | KSZPHY_OMSO_B_CAST_OFF);
out:
if (ret)
- dev_err(&phydev->dev, "failed to disable broadcast address\n");
+ phydev_err(phydev, "failed to disable broadcast address\n");
return ret;
}
@@ -251,7 +263,7 @@ static int kszphy_nand_tree_disable(struct phy_device *phydev)
ret & ~KSZPHY_OMSO_NAND_TREE_ON);
out:
if (ret)
- dev_err(&phydev->dev, "failed to disable NAND tree mode\n");
+ phydev_err(phydev, "failed to disable NAND tree mode\n");
return ret;
}
@@ -276,7 +288,8 @@ static int kszphy_config_init(struct phy_device *phydev)
if (priv->rmii_ref_clk_sel) {
ret = kszphy_rmii_clk_sel(phydev, priv->rmii_ref_clk_sel_val);
if (ret) {
- dev_err(&phydev->dev, "failed to set rmii reference clock\n");
+ phydev_err(phydev,
+ "failed to set rmii reference clock\n");
return ret;
}
}
@@ -337,11 +350,20 @@ static int ksz9021_load_values_from_of(struct phy_device *phydev,
static int ksz9021_config_init(struct phy_device *phydev)
{
- const struct device *dev = &phydev->dev;
+ const struct device *dev = &phydev->mdio.dev;
const struct device_node *of_node = dev->of_node;
+ const struct device *dev_walker;
- if (!of_node && dev->parent->of_node)
- of_node = dev->parent->of_node;
+ /* The Micrel driver has a deprecated option to place phy OF
+ * properties in the MAC node. Walk up the tree of devices to
+ * find a device with an OF node.
+ */
+ dev_walker = &phydev->mdio.dev;
+ do {
+ of_node = dev_walker->of_node;
+ dev_walker = dev_walker->parent;
+
+ } while (!of_node && dev_walker);
if (of_node) {
ksz9021_load_values_from_of(phydev, of_node,
@@ -449,7 +471,7 @@ static int ksz9031_center_flp_timing(struct phy_device *phydev)
static int ksz9031_config_init(struct phy_device *phydev)
{
- const struct device *dev = &phydev->dev;
+ const struct device *dev = &phydev->mdio.dev;
const struct device_node *of_node = dev->of_node;
static const char *clk_skews[2] = {"rxc-skew-ps", "txc-skew-ps"};
static const char *rx_data_skews[4] = {
@@ -461,9 +483,17 @@ static int ksz9031_config_init(struct phy_device *phydev)
"txd2-skew-ps", "txd3-skew-ps"
};
static const char *control_skews[2] = {"txen-skew-ps", "rxdv-skew-ps"};
+ const struct device *dev_walker;
- if (!of_node && dev->parent->of_node)
- of_node = dev->parent->of_node;
+ /* The Micrel driver has a deprecated option to place phy OF
+ * properties in the MAC node. Walk up the tree of devices to
+ * find a device with an OF node.
+ */
+ dev_walker = &phydev->mdio.dev;
+ do {
+ of_node = dev_walker->of_node;
+ dev_walker = dev_walker->parent;
+ } while (!of_node && dev_walker);
if (of_node) {
ksz9031_of_load_skew_values(phydev, of_node,
@@ -560,15 +590,60 @@ ksz9021_wr_mmd_phyreg(struct phy_device *phydev, int ptrad, int devnum,
{
}
+static int kszphy_get_sset_count(struct phy_device *phydev)
+{
+ return ARRAY_SIZE(kszphy_hw_stats);
+}
+
+static void kszphy_get_strings(struct phy_device *phydev, u8 *data)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(kszphy_hw_stats); i++) {
+ memcpy(data + i * ETH_GSTRING_LEN,
+ kszphy_hw_stats[i].string, ETH_GSTRING_LEN);
+ }
+}
+
+#ifndef UINT64_MAX
+#define UINT64_MAX (u64)(~((u64)0))
+#endif
+static u64 kszphy_get_stat(struct phy_device *phydev, int i)
+{
+ struct kszphy_hw_stat stat = kszphy_hw_stats[i];
+ struct kszphy_priv *priv = phydev->priv;
+ u64 val;
+
+ val = phy_read(phydev, stat.reg);
+ if (val < 0) {
+ val = UINT64_MAX;
+ } else {
+ val = val & ((1 << stat.bits) - 1);
+ priv->stats[i] += val;
+ val = priv->stats[i];
+ }
+
+ return val;
+}
+
+static void kszphy_get_stats(struct phy_device *phydev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(kszphy_hw_stats); i++)
+ data[i] = kszphy_get_stat(phydev, i);
+}
+
static int kszphy_probe(struct phy_device *phydev)
{
const struct kszphy_type *type = phydev->drv->driver_data;
- const struct device_node *np = phydev->dev.of_node;
+ const struct device_node *np = phydev->mdio.dev.of_node;
struct kszphy_priv *priv;
struct clk *clk;
int ret;
- priv = devm_kzalloc(&phydev->dev, sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -583,15 +658,15 @@ static int kszphy_probe(struct phy_device *phydev)
priv->led_mode = -1;
if (priv->led_mode > 3) {
- dev_err(&phydev->dev, "invalid led mode: 0x%02x\n",
- priv->led_mode);
+ phydev_err(phydev, "invalid led mode: 0x%02x\n",
+ priv->led_mode);
priv->led_mode = -1;
}
} else {
priv->led_mode = -1;
}
- clk = devm_clk_get(&phydev->dev, "rmii-ref");
+ clk = devm_clk_get(&phydev->mdio.dev, "rmii-ref");
/* NOTE: clk may be NULL if building without CONFIG_HAVE_CLK */
if (!IS_ERR_OR_NULL(clk)) {
unsigned long rate = clk_get_rate(clk);
@@ -606,7 +681,8 @@ static int kszphy_probe(struct phy_device *phydev)
} else if (rate > 49500000 && rate < 50500000) {
priv->rmii_ref_clk_sel_val = !rmii_ref_clk_sel_25_mhz;
} else {
- dev_err(&phydev->dev, "Clock rate out of range: %ld\n", rate);
+ phydev_err(phydev, "Clock rate out of range: %ld\n",
+ rate);
return -EINVAL;
}
}
@@ -633,9 +709,11 @@ static struct phy_driver ksphy_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_KSZ8021,
.phy_id_mask = 0x00ffffff,
@@ -650,9 +728,11 @@ static struct phy_driver ksphy_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_KSZ8031,
.phy_id_mask = 0x00ffffff,
@@ -667,9 +747,11 @@ static struct phy_driver ksphy_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_KSZ8041,
.phy_id_mask = 0x00fffff0,
@@ -684,9 +766,11 @@ static struct phy_driver ksphy_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_KSZ8041RNLI,
.phy_id_mask = 0x00fffff0,
@@ -701,9 +785,11 @@ static struct phy_driver ksphy_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_KSZ8051,
.phy_id_mask = 0x00fffff0,
@@ -718,9 +804,11 @@ static struct phy_driver ksphy_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_KSZ8001,
.name = "Micrel KSZ8001 or KS8721",
@@ -734,9 +822,11 @@ static struct phy_driver ksphy_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_KSZ8081,
.name = "Micrel KSZ8081 or KSZ8091",
@@ -750,9 +840,11 @@ static struct phy_driver ksphy_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_KSZ8061,
.name = "Micrel KSZ8061",
@@ -764,9 +856,11 @@ static struct phy_driver ksphy_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_KSZ9021,
.phy_id_mask = 0x000ffffe,
@@ -779,11 +873,13 @@ static struct phy_driver ksphy_driver[] = {
.read_status = genphy_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
.read_mmd_indirect = ksz9021_rd_mmd_phyreg,
.write_mmd_indirect = ksz9021_wr_mmd_phyreg,
- .driver = { .owner = THIS_MODULE, },
}, {
.phy_id = PHY_ID_KSZ9031,
.phy_id_mask = 0x00fffff0,
@@ -796,9 +892,11 @@ static struct phy_driver ksphy_driver[] = {
.read_status = ksz9031_read_status,
.ack_interrupt = kszphy_ack_interrupt,
.config_intr = kszphy_config_intr,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE, },
}, {
.phy_id = PHY_ID_KSZ8873MLL,
.phy_id_mask = 0x00fffff0,
@@ -808,9 +906,11 @@ static struct phy_driver ksphy_driver[] = {
.config_init = kszphy_config_init,
.config_aneg = ksz8873mll_config_aneg,
.read_status = ksz8873mll_read_status,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE, },
}, {
.phy_id = PHY_ID_KSZ886X,
.phy_id_mask = 0x00fffff0,
@@ -820,9 +920,11 @@ static struct phy_driver ksphy_driver[] = {
.config_init = kszphy_config_init,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
+ .get_sset_count = kszphy_get_sset_count,
+ .get_strings = kszphy_get_strings,
+ .get_stats = kszphy_get_stats,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE, },
} };
module_phy_driver(ksphy_driver);
diff --git a/drivers/net/phy/microchip.c b/drivers/net/phy/microchip.c
index c0a20ebd083b..15f820648f82 100644
--- a/drivers/net/phy/microchip.c
+++ b/drivers/net/phy/microchip.c
@@ -68,7 +68,7 @@ int lan88xx_suspend(struct phy_device *phydev)
static int lan88xx_probe(struct phy_device *phydev)
{
- struct device *dev = &phydev->dev;
+ struct device *dev = &phydev->mdio.dev;
struct lan88xx_priv *priv;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -78,10 +78,9 @@ static int lan88xx_probe(struct phy_device *phydev)
priv->wolopts = 0;
/* these values can be used to identify internal PHY */
- priv->chip_id = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_ID,
- 3, phydev->addr);
+ priv->chip_id = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_ID, 3);
priv->chip_rev = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_REV,
- 3, phydev->addr);
+ 3);
phydev->priv = priv;
@@ -90,7 +89,7 @@ static int lan88xx_probe(struct phy_device *phydev)
static void lan88xx_remove(struct phy_device *phydev)
{
- struct device *dev = &phydev->dev;
+ struct device *dev = &phydev->mdio.dev;
struct lan88xx_priv *priv = phydev->priv;
if (priv)
@@ -130,8 +129,6 @@ static struct phy_driver microchip_phy_driver[] = {
.suspend = lan88xx_suspend,
.resume = genphy_resume,
.set_wol = lan88xx_set_wol,
-
- .driver = { .owner = THIS_MODULE, }
} };
module_phy_driver(microchip_phy_driver);
diff --git a/drivers/net/phy/national.c b/drivers/net/phy/national.c
index 0a7b9c7f09a2..2a1b490bc587 100644
--- a/drivers/net/phy/national.c
+++ b/drivers/net/phy/national.c
@@ -140,7 +140,6 @@ static struct phy_driver dp83865_driver[] = { {
.read_status = genphy_read_status,
.ack_interrupt = ns_ack_interrupt,
.config_intr = ns_config_intr,
- .driver = {.owner = THIS_MODULE,}
} };
module_phy_driver(dp83865_driver);
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 48ce6ef400fe..8763bb20988a 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -319,7 +319,7 @@ int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd)
{
u32 speed = ethtool_cmd_speed(cmd);
- if (cmd->phy_address != phydev->addr)
+ if (cmd->phy_address != phydev->mdio.addr)
return -EINVAL;
/* We make sure that we don't pass unsupported values in to the PHY */
@@ -375,7 +375,7 @@ int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd)
cmd->port = PORT_BNC;
else
cmd->port = PORT_MII;
- cmd->phy_address = phydev->addr;
+ cmd->phy_address = phydev->mdio.addr;
cmd->transceiver = phy_is_internal(phydev) ?
XCVR_INTERNAL : XCVR_EXTERNAL;
cmd->autoneg = phydev->autoneg;
@@ -403,16 +403,17 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
switch (cmd) {
case SIOCGMIIPHY:
- mii_data->phy_id = phydev->addr;
+ mii_data->phy_id = phydev->mdio.addr;
/* fall through */
case SIOCGMIIREG:
- mii_data->val_out = mdiobus_read(phydev->bus, mii_data->phy_id,
+ mii_data->val_out = mdiobus_read(phydev->mdio.bus,
+ mii_data->phy_id,
mii_data->reg_num);
return 0;
case SIOCSMIIREG:
- if (mii_data->phy_id == phydev->addr) {
+ if (mii_data->phy_id == phydev->mdio.addr) {
switch (mii_data->reg_num) {
case MII_BMCR:
if ((val & (BMCR_RESET | BMCR_ANENABLE)) == 0) {
@@ -445,10 +446,11 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
}
}
- mdiobus_write(phydev->bus, mii_data->phy_id,
+ mdiobus_write(phydev->mdio.bus, mii_data->phy_id,
mii_data->reg_num, val);
- if (mii_data->reg_num == MII_BMCR &&
+ if (mii_data->phy_id == phydev->mdio.addr &&
+ mii_data->reg_num == MII_BMCR &&
val & BMCR_RESET)
return phy_init_hw(phydev);
@@ -642,7 +644,7 @@ int phy_start_interrupts(struct phy_device *phydev)
if (request_irq(phydev->irq, phy_interrupt, 0, "phy_interrupt",
phydev) < 0) {
pr_warn("%s: Can't get IRQ %d (PHY)\n",
- phydev->bus->name, phydev->irq);
+ phydev->mdio.bus->name, phydev->irq);
phydev->irq = PHY_POLL;
return 0;
}
@@ -994,8 +996,9 @@ void phy_state_machine(struct work_struct *work)
if (err < 0)
phy_error(phydev);
- dev_dbg(&phydev->dev, "PHY state change %s -> %s\n",
- phy_state_to_str(old_state), phy_state_to_str(phydev->state));
+ phydev_dbg(phydev, "PHY state change %s -> %s\n",
+ phy_state_to_str(old_state),
+ phy_state_to_str(phydev->state));
queue_delayed_work(system_power_efficient_wq, &phydev->state_queue,
PHY_STATE_TIME * HZ);
@@ -1027,7 +1030,6 @@ static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad,
* @phydev: The PHY device bus
* @prtad: MMD Address
* @devad: MMD DEVAD
- * @addr: PHY address on the MII bus
*
* Description: it reads data from the MMD registers (clause 22 to access to
* clause 45) of the specified phy address.
@@ -1037,14 +1039,14 @@ static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad,
* 3) Write reg 13 // MMD Data Command for MMD DEVAD
* 3) Read reg 14 // Read MMD data
*/
-int phy_read_mmd_indirect(struct phy_device *phydev, int prtad,
- int devad, int addr)
+int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad)
{
struct phy_driver *phydrv = phydev->drv;
+ int addr = phydev->mdio.addr;
int value = -1;
if (!phydrv->read_mmd_indirect) {
- struct mii_bus *bus = phydev->bus;
+ struct mii_bus *bus = phydev->mdio.bus;
mutex_lock(&bus->mdio_lock);
mmd_phy_indirect(bus, prtad, devad, addr);
@@ -1064,7 +1066,6 @@ EXPORT_SYMBOL(phy_read_mmd_indirect);
* @phydev: The PHY device
* @prtad: MMD Address
* @devad: MMD DEVAD
- * @addr: PHY address on the MII bus
* @data: data to write in the MMD register
*
* Description: Write data from the MMD registers of the specified
@@ -1076,12 +1077,13 @@ EXPORT_SYMBOL(phy_read_mmd_indirect);
* 3) Write reg 14 // Write MMD data
*/
void phy_write_mmd_indirect(struct phy_device *phydev, int prtad,
- int devad, int addr, u32 data)
+ int devad, u32 data)
{
struct phy_driver *phydrv = phydev->drv;
+ int addr = phydev->mdio.addr;
if (!phydrv->write_mmd_indirect) {
- struct mii_bus *bus = phydev->bus;
+ struct mii_bus *bus = phydev->mdio.bus;
mutex_lock(&bus->mdio_lock);
mmd_phy_indirect(bus, prtad, devad, addr);
@@ -1128,7 +1130,7 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable)
/* First check if the EEE ability is supported */
eee_cap = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE,
- MDIO_MMD_PCS, phydev->addr);
+ MDIO_MMD_PCS);
if (eee_cap <= 0)
goto eee_exit_err;
@@ -1140,12 +1142,12 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable)
* the EEE advertising registers.
*/
eee_lp = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_LPABLE,
- MDIO_MMD_AN, phydev->addr);
+ MDIO_MMD_AN);
if (eee_lp <= 0)
goto eee_exit_err;
eee_adv = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV,
- MDIO_MMD_AN, phydev->addr);
+ MDIO_MMD_AN);
if (eee_adv <= 0)
goto eee_exit_err;
@@ -1159,15 +1161,13 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable)
* clock while it is signaling LPI.
*/
int val = phy_read_mmd_indirect(phydev, MDIO_CTRL1,
- MDIO_MMD_PCS,
- phydev->addr);
+ MDIO_MMD_PCS);
if (val < 0)
return val;
val |= MDIO_PCS_CTRL1_CLKSTOP_EN;
phy_write_mmd_indirect(phydev, MDIO_CTRL1,
- MDIO_MMD_PCS, phydev->addr,
- val);
+ MDIO_MMD_PCS, val);
}
return 0; /* EEE supported */
@@ -1186,8 +1186,7 @@ EXPORT_SYMBOL(phy_init_eee);
*/
int phy_get_eee_err(struct phy_device *phydev)
{
- return phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_WK_ERR,
- MDIO_MMD_PCS, phydev->addr);
+ return phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_WK_ERR, MDIO_MMD_PCS);
}
EXPORT_SYMBOL(phy_get_eee_err);
@@ -1204,22 +1203,19 @@ int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data)
int val;
/* Get Supported EEE */
- val = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE,
- MDIO_MMD_PCS, phydev->addr);
+ val = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE, MDIO_MMD_PCS);
if (val < 0)
return val;
data->supported = mmd_eee_cap_to_ethtool_sup_t(val);
/* Get advertisement EEE */
- val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV,
- MDIO_MMD_AN, phydev->addr);
+ val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN);
if (val < 0)
return val;
data->advertised = mmd_eee_adv_to_ethtool_adv_t(val);
/* Get LP advertisement EEE */
- val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_LPABLE,
- MDIO_MMD_AN, phydev->addr);
+ val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_LPABLE, MDIO_MMD_AN);
if (val < 0)
return val;
data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val);
@@ -1239,8 +1235,7 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
{
int val = ethtool_adv_to_mmd_eee_adv_t(data->advertised);
- phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN,
- phydev->addr, val);
+ phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, val);
return 0;
}
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 0bfbabad4431..903737adfc01 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -43,15 +43,31 @@ MODULE_LICENSE("GPL");
void phy_device_free(struct phy_device *phydev)
{
- put_device(&phydev->dev);
+ put_device(&phydev->mdio.dev);
}
EXPORT_SYMBOL(phy_device_free);
+static void phy_mdio_device_free(struct mdio_device *mdiodev)
+{
+ struct phy_device *phydev;
+
+ phydev = container_of(mdiodev, struct phy_device, mdio);
+ phy_device_free(phydev);
+}
+
static void phy_device_release(struct device *dev)
{
kfree(to_phy_device(dev));
}
+static void phy_mdio_device_remove(struct mdio_device *mdiodev)
+{
+ struct phy_device *phydev;
+
+ phydev = container_of(mdiodev, struct phy_device, mdio);
+ phy_device_remove(phydev);
+}
+
enum genphy_driver {
GENPHY_DRV_1G,
GENPHY_DRV_10G,
@@ -63,9 +79,118 @@ static struct phy_driver genphy_driver[GENPHY_DRV_MAX];
static LIST_HEAD(phy_fixup_list);
static DEFINE_MUTEX(phy_fixup_lock);
+#ifdef CONFIG_PM
+static bool mdio_bus_phy_may_suspend(struct phy_device *phydev)
+{
+ struct device_driver *drv = phydev->mdio.dev.driver;
+ struct phy_driver *phydrv = to_phy_driver(drv);
+ struct net_device *netdev = phydev->attached_dev;
+
+ if (!drv || !phydrv->suspend)
+ return false;
+
+ /* PHY not attached? May suspend if the PHY has not already been
+ * suspended as part of a prior call to phy_disconnect() ->
+ * phy_detach() -> phy_suspend() because the parent netdev might be the
+ * MDIO bus driver and clock gated at this point.
+ */
+ if (!netdev)
+ return !phydev->suspended;
+
+ /* Don't suspend PHY if the attached netdev parent may wakeup.
+ * The parent may point to a PCI device, as in tg3 driver.
+ */
+ if (netdev->dev.parent && device_may_wakeup(netdev->dev.parent))
+ return false;
+
+ /* Also don't suspend PHY if the netdev itself may wakeup. This
+ * is the case for devices w/o underlaying pwr. mgmt. aware bus,
+ * e.g. SoC devices.
+ */
+ if (device_may_wakeup(&netdev->dev))
+ return false;
+
+ return true;
+}
+
+static int mdio_bus_phy_suspend(struct device *dev)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+
+ /* We must stop the state machine manually, otherwise it stops out of
+ * control, possibly with the phydev->lock held. Upon resume, netdev
+ * may call phy routines that try to grab the same lock, and that may
+ * lead to a deadlock.
+ */
+ if (phydev->attached_dev && phydev->adjust_link)
+ phy_stop_machine(phydev);
+
+ if (!mdio_bus_phy_may_suspend(phydev))
+ return 0;
+
+ return phy_suspend(phydev);
+}
+
+static int mdio_bus_phy_resume(struct device *dev)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+ int ret;
+
+ if (!mdio_bus_phy_may_suspend(phydev))
+ goto no_resume;
+
+ ret = phy_resume(phydev);
+ if (ret < 0)
+ return ret;
+
+no_resume:
+ if (phydev->attached_dev && phydev->adjust_link)
+ phy_start_machine(phydev);
+
+ return 0;
+}
+
+static int mdio_bus_phy_restore(struct device *dev)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+ struct net_device *netdev = phydev->attached_dev;
+ int ret;
+
+ if (!netdev)
+ return 0;
+
+ ret = phy_init_hw(phydev);
+ if (ret < 0)
+ return ret;
+
+ /* The PHY needs to renegotiate. */
+ phydev->link = 0;
+ phydev->state = PHY_UP;
+
+ phy_start_machine(phydev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops mdio_bus_phy_pm_ops = {
+ .suspend = mdio_bus_phy_suspend,
+ .resume = mdio_bus_phy_resume,
+ .freeze = mdio_bus_phy_suspend,
+ .thaw = mdio_bus_phy_resume,
+ .restore = mdio_bus_phy_restore,
+};
+
+#define MDIO_BUS_PHY_PM_OPS (&mdio_bus_phy_pm_ops)
+
+#else
+
+#define MDIO_BUS_PHY_PM_OPS NULL
+
+#endif /* CONFIG_PM */
+
/**
* phy_register_fixup - creates a new phy_fixup and adds it to the list
- * @bus_id: A string which matches phydev->dev.bus_id (or PHY_ANY_ID)
+ * @bus_id: A string which matches phydev->mdio.dev.bus_id (or PHY_ANY_ID)
* @phy_uid: Used to match against phydev->phy_id (the UID of the PHY)
* It can also be PHY_ANY_UID
* @phy_uid_mask: Applied to phydev->phy_id and fixup->phy_uid before
@@ -114,7 +239,7 @@ EXPORT_SYMBOL(phy_register_fixup_for_id);
*/
static int phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup)
{
- if (strcmp(fixup->bus_id, dev_name(&phydev->dev)) != 0)
+ if (strcmp(fixup->bus_id, phydev_name(phydev)) != 0)
if (strcmp(fixup->bus_id, PHY_ANY_ID) != 0)
return 0;
@@ -148,18 +273,59 @@ static int phy_scan_fixups(struct phy_device *phydev)
return 0;
}
+static int phy_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+ struct phy_driver *phydrv = to_phy_driver(drv);
+ const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids);
+ int i;
+
+ if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY))
+ return 0;
+
+ if (phydrv->match_phy_device)
+ return phydrv->match_phy_device(phydev);
+
+ if (phydev->is_c45) {
+ for (i = 1; i < num_ids; i++) {
+ if (!(phydev->c45_ids.devices_in_package & (1 << i)))
+ continue;
+
+ if ((phydrv->phy_id & phydrv->phy_id_mask) ==
+ (phydev->c45_ids.device_ids[i] &
+ phydrv->phy_id_mask))
+ return 1;
+ }
+ return 0;
+ } else {
+ return (phydrv->phy_id & phydrv->phy_id_mask) ==
+ (phydev->phy_id & phydrv->phy_id_mask);
+ }
+}
+
struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
bool is_c45,
struct phy_c45_device_ids *c45_ids)
{
struct phy_device *dev;
+ struct mdio_device *mdiodev;
/* We allocate the device, and initialize the default values */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return ERR_PTR(-ENOMEM);
- dev->dev.release = phy_device_release;
+ mdiodev = &dev->mdio;
+ mdiodev->dev.release = phy_device_release;
+ mdiodev->dev.parent = &bus->dev;
+ mdiodev->dev.bus = &mdio_bus_type;
+ mdiodev->bus = bus;
+ mdiodev->pm_ops = MDIO_BUS_PHY_PM_OPS;
+ mdiodev->bus_match = phy_bus_match;
+ mdiodev->addr = addr;
+ mdiodev->flags = MDIO_DEVICE_FLAG_PHY;
+ mdiodev->device_free = phy_mdio_device_free;
+ mdiodev->device_remove = phy_mdio_device_remove;
dev->speed = 0;
dev->duplex = -1;
@@ -171,15 +337,11 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
dev->autoneg = AUTONEG_ENABLE;
dev->is_c45 = is_c45;
- dev->addr = addr;
dev->phy_id = phy_id;
if (c45_ids)
dev->c45_ids = *c45_ids;
- dev->bus = bus;
- dev->dev.parent = &bus->dev;
- dev->dev.bus = &mdio_bus_type;
- dev->irq = bus->irq ? bus->irq[addr] : PHY_POLL;
- dev_set_name(&dev->dev, PHY_ID_FMT, bus->id, addr);
+ dev->irq = bus->irq[addr];
+ dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr);
dev->state = PHY_DOWN;
@@ -199,7 +361,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
*/
request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phy_id));
- device_initialize(&dev->dev);
+ device_initialize(&mdiodev->dev);
return dev;
}
@@ -373,6 +535,48 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45)
}
EXPORT_SYMBOL(get_phy_device);
+static ssize_t
+phy_id_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+
+ return sprintf(buf, "0x%.8lx\n", (unsigned long)phydev->phy_id);
+}
+static DEVICE_ATTR_RO(phy_id);
+
+static ssize_t
+phy_interface_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+ const char *mode = NULL;
+
+ if (phy_is_internal(phydev))
+ mode = "internal";
+ else
+ mode = phy_modes(phydev->interface);
+
+ return sprintf(buf, "%s\n", mode);
+}
+static DEVICE_ATTR_RO(phy_interface);
+
+static ssize_t
+phy_has_fixups_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+
+ return sprintf(buf, "%d\n", phydev->has_fixups);
+}
+static DEVICE_ATTR_RO(phy_has_fixups);
+
+static struct attribute *phy_dev_attrs[] = {
+ &dev_attr_phy_id.attr,
+ &dev_attr_phy_interface.attr,
+ &dev_attr_phy_has_fixups.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(phy_dev);
+
/**
* phy_device_register - Register the phy device on the MDIO bus
* @phydev: phy_device structure to be added to the MDIO bus
@@ -381,28 +585,29 @@ int phy_device_register(struct phy_device *phydev)
{
int err;
- /* Don't register a phy if one is already registered at this address */
- if (phydev->bus->phy_map[phydev->addr])
- return -EINVAL;
- phydev->bus->phy_map[phydev->addr] = phydev;
+ err = mdiobus_register_device(&phydev->mdio);
+ if (err)
+ return err;
/* Run all of the fixups for this PHY */
err = phy_scan_fixups(phydev);
if (err) {
- pr_err("PHY %d failed to initialize\n", phydev->addr);
+ pr_err("PHY %d failed to initialize\n", phydev->mdio.addr);
goto out;
}
- err = device_add(&phydev->dev);
+ phydev->mdio.dev.groups = phy_dev_groups;
+
+ err = device_add(&phydev->mdio.dev);
if (err) {
- pr_err("PHY %d failed to add\n", phydev->addr);
+ pr_err("PHY %d failed to add\n", phydev->mdio.addr);
goto out;
}
return 0;
out:
- phydev->bus->phy_map[phydev->addr] = NULL;
+ mdiobus_unregister_device(&phydev->mdio);
return err;
}
EXPORT_SYMBOL(phy_device_register);
@@ -417,11 +622,8 @@ EXPORT_SYMBOL(phy_device_register);
*/
void phy_device_remove(struct phy_device *phydev)
{
- struct mii_bus *bus = phydev->bus;
- int addr = phydev->addr;
-
- device_del(&phydev->dev);
- bus->phy_map[addr] = NULL;
+ device_del(&phydev->mdio.dev);
+ mdiobus_unregister_device(&phydev->mdio);
}
EXPORT_SYMBOL(phy_device_remove);
@@ -431,11 +633,13 @@ EXPORT_SYMBOL(phy_device_remove);
*/
struct phy_device *phy_find_first(struct mii_bus *bus)
{
+ struct phy_device *phydev;
int addr;
for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
- if (bus->phy_map[addr])
- return bus->phy_map[addr];
+ phydev = mdiobus_get_phy(bus, addr);
+ if (phydev)
+ return phydev;
}
return NULL;
}
@@ -607,6 +811,33 @@ int phy_init_hw(struct phy_device *phydev)
}
EXPORT_SYMBOL(phy_init_hw);
+void phy_attached_info(struct phy_device *phydev)
+{
+ phy_attached_print(phydev, NULL);
+}
+EXPORT_SYMBOL(phy_attached_info);
+
+#define ATTACHED_FMT "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)"
+void phy_attached_print(struct phy_device *phydev, const char *fmt, ...)
+{
+ if (!fmt) {
+ dev_info(&phydev->mdio.dev, ATTACHED_FMT "\n",
+ phydev->drv->name, phydev_name(phydev),
+ phydev->irq);
+ } else {
+ va_list ap;
+
+ dev_info(&phydev->mdio.dev, ATTACHED_FMT,
+ phydev->drv->name, phydev_name(phydev),
+ phydev->irq);
+
+ va_start(ap, fmt);
+ vprintk(fmt, ap);
+ va_end(ap);
+ }
+}
+EXPORT_SYMBOL(phy_attached_print);
+
/**
* phy_attach_direct - attach a network device to a given PHY device pointer
* @dev: network device to attach
@@ -625,8 +856,8 @@ EXPORT_SYMBOL(phy_init_hw);
int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
u32 flags, phy_interface_t interface)
{
- struct mii_bus *bus = phydev->bus;
- struct device *d = &phydev->dev;
+ struct mii_bus *bus = phydev->mdio.bus;
+ struct device *d = &phydev->mdio.dev;
int err;
if (!try_module_get(bus->owner)) {
@@ -641,9 +872,11 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
*/
if (!d->driver) {
if (phydev->is_c45)
- d->driver = &genphy_driver[GENPHY_DRV_10G].driver;
+ d->driver =
+ &genphy_driver[GENPHY_DRV_10G].mdiodrv.driver;
else
- d->driver = &genphy_driver[GENPHY_DRV_1G].driver;
+ d->driver =
+ &genphy_driver[GENPHY_DRV_1G].mdiodrv.driver;
err = d->driver->probe(d);
if (err >= 0)
@@ -744,8 +977,9 @@ void phy_detach(struct phy_device *phydev)
* real driver could be loaded
*/
for (i = 0; i < ARRAY_SIZE(genphy_driver); i++) {
- if (phydev->dev.driver == &genphy_driver[i].driver) {
- device_release_driver(&phydev->dev);
+ if (phydev->mdio.dev.driver ==
+ &genphy_driver[i].mdiodrv.driver) {
+ device_release_driver(&phydev->mdio.dev);
break;
}
}
@@ -754,16 +988,16 @@ void phy_detach(struct phy_device *phydev)
* The phydev might go away on the put_device() below, so avoid
* a use-after-free bug by reading the underlying bus first.
*/
- bus = phydev->bus;
+ bus = phydev->mdio.bus;
- put_device(&phydev->dev);
+ put_device(&phydev->mdio.dev);
module_put(bus->owner);
}
EXPORT_SYMBOL(phy_detach);
int phy_suspend(struct phy_device *phydev)
{
- struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver);
+ struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver);
struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
int ret = 0;
@@ -786,7 +1020,7 @@ EXPORT_SYMBOL(phy_suspend);
int phy_resume(struct phy_device *phydev)
{
- struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver);
+ struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver);
int ret = 0;
if (phydrv->resume)
@@ -1303,7 +1537,7 @@ EXPORT_SYMBOL(phy_set_max_speed);
static void of_set_phy_supported(struct phy_device *phydev)
{
- struct device_node *node = phydev->dev.of_node;
+ struct device_node *node = phydev->mdio.dev.of_node;
u32 max_speed;
if (!IS_ENABLED(CONFIG_OF_MDIO))
@@ -1327,7 +1561,7 @@ static void of_set_phy_supported(struct phy_device *phydev)
static int phy_probe(struct device *dev)
{
struct phy_device *phydev = to_phy_device(dev);
- struct device_driver *drv = phydev->dev.driver;
+ struct device_driver *drv = phydev->mdio.dev.driver;
struct phy_driver *phydrv = to_phy_driver(drv);
int err = 0;
@@ -1382,17 +1616,20 @@ static int phy_remove(struct device *dev)
/**
* phy_driver_register - register a phy_driver with the PHY layer
* @new_driver: new phy_driver to register
+ * @owner: module owning this PHY
*/
-int phy_driver_register(struct phy_driver *new_driver)
+int phy_driver_register(struct phy_driver *new_driver, struct module *owner)
{
int retval;
- new_driver->driver.name = new_driver->name;
- new_driver->driver.bus = &mdio_bus_type;
- new_driver->driver.probe = phy_probe;
- new_driver->driver.remove = phy_remove;
+ new_driver->mdiodrv.flags |= MDIO_DEVICE_IS_PHY;
+ new_driver->mdiodrv.driver.name = new_driver->name;
+ new_driver->mdiodrv.driver.bus = &mdio_bus_type;
+ new_driver->mdiodrv.driver.probe = phy_probe;
+ new_driver->mdiodrv.driver.remove = phy_remove;
+ new_driver->mdiodrv.driver.owner = owner;
- retval = driver_register(&new_driver->driver);
+ retval = driver_register(&new_driver->mdiodrv.driver);
if (retval) {
pr_err("%s: Error %d in registering driver\n",
new_driver->name, retval);
@@ -1406,12 +1643,13 @@ int phy_driver_register(struct phy_driver *new_driver)
}
EXPORT_SYMBOL(phy_driver_register);
-int phy_drivers_register(struct phy_driver *new_driver, int n)
+int phy_drivers_register(struct phy_driver *new_driver, int n,
+ struct module *owner)
{
int i, ret = 0;
for (i = 0; i < n; i++) {
- ret = phy_driver_register(new_driver + i);
+ ret = phy_driver_register(new_driver + i, owner);
if (ret) {
while (i-- > 0)
phy_driver_unregister(new_driver + i);
@@ -1424,7 +1662,7 @@ EXPORT_SYMBOL(phy_drivers_register);
void phy_driver_unregister(struct phy_driver *drv)
{
- driver_unregister(&drv->driver);
+ driver_unregister(&drv->mdiodrv.driver);
}
EXPORT_SYMBOL(phy_driver_unregister);
@@ -1452,7 +1690,6 @@ static struct phy_driver genphy_driver[] = {
.read_status = genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE, },
}, {
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
@@ -1464,7 +1701,6 @@ static struct phy_driver genphy_driver[] = {
.read_status = gen10g_read_status,
.suspend = gen10g_suspend,
.resume = gen10g_resume,
- .driver = {.owner = THIS_MODULE, },
} };
static int __init phy_init(void)
@@ -1476,7 +1712,7 @@ static int __init phy_init(void)
return rc;
rc = phy_drivers_register(genphy_driver,
- ARRAY_SIZE(genphy_driver));
+ ARRAY_SIZE(genphy_driver), THIS_MODULE);
if (rc)
mdio_bus_exit();
diff --git a/drivers/net/phy/qsemi.c b/drivers/net/phy/qsemi.c
index be4c6f7c3645..d470db89e8dd 100644
--- a/drivers/net/phy/qsemi.c
+++ b/drivers/net/phy/qsemi.c
@@ -122,7 +122,6 @@ static struct phy_driver qs6612_driver[] = { {
.read_status = genphy_read_status,
.ack_interrupt = qs6612_ack_interrupt,
.config_intr = qs6612_config_intr,
- .driver = { .owner = THIS_MODULE,},
} };
module_phy_driver(qs6612_driver);
diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c
index 43ab691362d4..aadd6e9f54ad 100644
--- a/drivers/net/phy/realtek.c
+++ b/drivers/net/phy/realtek.c
@@ -124,7 +124,6 @@ static struct phy_driver realtek_drvs[] = {
.flags = PHY_HAS_INTERRUPT,
.config_aneg = &genphy_config_aneg,
.read_status = &genphy_read_status,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x001cc912,
.name = "RTL8211B Gigabit Ethernet",
@@ -135,7 +134,6 @@ static struct phy_driver realtek_drvs[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &rtl821x_ack_interrupt,
.config_intr = &rtl8211b_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x001cc914,
.name = "RTL8211DN Gigabit Ethernet",
@@ -148,7 +146,6 @@ static struct phy_driver realtek_drvs[] = {
.config_intr = rtl8211e_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x001cc915,
.name = "RTL8211E Gigabit Ethernet",
@@ -161,7 +158,6 @@ static struct phy_driver realtek_drvs[] = {
.config_intr = &rtl8211e_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = 0x001cc916,
.name = "RTL8211F Gigabit Ethernet",
@@ -175,7 +171,6 @@ static struct phy_driver realtek_drvs[] = {
.config_intr = &rtl8211f_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = { .owner = THIS_MODULE },
},
};
diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c
index dc2da8770918..e485f2653c82 100644
--- a/drivers/net/phy/smsc.c
+++ b/drivers/net/phy/smsc.c
@@ -44,7 +44,7 @@ static int smsc_phy_ack_interrupt(struct phy_device *phydev)
static int smsc_phy_config_init(struct phy_device *phydev)
{
int __maybe_unused len;
- struct device *dev __maybe_unused = &phydev->dev;
+ struct device *dev __maybe_unused = &phydev->mdio.dev;
struct device_node *of_node __maybe_unused = dev->of_node;
int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
int enable_energy = 1;
@@ -171,8 +171,6 @@ static struct phy_driver smsc_phy_driver[] = {
.suspend = genphy_suspend,
.resume = genphy_resume,
-
- .driver = { .owner = THIS_MODULE, }
}, {
.phy_id = 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */
.phy_id_mask = 0xfffffff0,
@@ -194,8 +192,6 @@ static struct phy_driver smsc_phy_driver[] = {
.suspend = genphy_suspend,
.resume = genphy_resume,
-
- .driver = { .owner = THIS_MODULE, }
}, {
.phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */
.phy_id_mask = 0xfffffff0,
@@ -217,8 +213,6 @@ static struct phy_driver smsc_phy_driver[] = {
.suspend = genphy_suspend,
.resume = genphy_resume,
-
- .driver = { .owner = THIS_MODULE, }
}, {
.phy_id = 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */
.phy_id_mask = 0xfffffff0,
@@ -239,8 +233,6 @@ static struct phy_driver smsc_phy_driver[] = {
.suspend = genphy_suspend,
.resume = genphy_resume,
-
- .driver = { .owner = THIS_MODULE, }
}, {
.phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */
.phy_id_mask = 0xfffffff0,
@@ -262,8 +254,27 @@ static struct phy_driver smsc_phy_driver[] = {
.suspend = genphy_suspend,
.resume = genphy_resume,
+}, {
+ .phy_id = 0x0007c110,
+ .phy_id_mask = 0xfffffff0,
+ .name = "SMSC LAN8740",
+
+ .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
+ | SUPPORTED_Asym_Pause),
+ .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG,
+
+ /* basic functions */
+ .config_aneg = genphy_config_aneg,
+ .read_status = lan87xx_read_status,
+ .config_init = smsc_phy_config_init,
+ .soft_reset = smsc_phy_reset,
- .driver = { .owner = THIS_MODULE, }
+ /* IRQ related */
+ .ack_interrupt = smsc_phy_ack_interrupt,
+ .config_intr = smsc_phy_config_intr,
+
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
} };
module_phy_driver(smsc_phy_driver);
@@ -278,6 +289,7 @@ static struct mdio_device_id __maybe_unused smsc_tbl[] = {
{ 0x0007c0c0, 0xfffffff0 },
{ 0x0007c0d0, 0xfffffff0 },
{ 0x0007c0f0, 0xfffffff0 },
+ { 0x0007c110, 0xfffffff0 },
{ }
};
diff --git a/drivers/net/phy/ste10Xp.c b/drivers/net/phy/ste10Xp.c
index 3fc199b773e6..d00cfb64529e 100644
--- a/drivers/net/phy/ste10Xp.c
+++ b/drivers/net/phy/ste10Xp.c
@@ -95,7 +95,6 @@ static struct phy_driver ste10xp_pdriver[] = {
.config_intr = ste10Xp_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = {.owner = THIS_MODULE,}
}, {
.phy_id = STE100P_PHY_ID,
.phy_id_mask = 0xffffffff,
@@ -109,7 +108,6 @@ static struct phy_driver ste10xp_pdriver[] = {
.config_intr = ste10Xp_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
- .driver = {.owner = THIS_MODULE,}
} };
module_phy_driver(ste10xp_pdriver);
diff --git a/drivers/net/phy/teranetics.c b/drivers/net/phy/teranetics.c
index 07463fcca212..fb2cef764e9a 100644
--- a/drivers/net/phy/teranetics.c
+++ b/drivers/net/phy/teranetics.c
@@ -108,7 +108,6 @@ static struct phy_driver teranetics_driver[] = {
.config_aneg = teranetics_config_aneg,
.read_status = teranetics_read_status,
.match_phy_device = teranetics_match_phy_device,
- .driver = { .owner = THIS_MODULE,},
},
};
diff --git a/drivers/net/phy/vitesse.c b/drivers/net/phy/vitesse.c
index dd295dbaa074..2e37eb337d48 100644
--- a/drivers/net/phy/vitesse.c
+++ b/drivers/net/phy/vitesse.c
@@ -236,7 +236,6 @@ static struct phy_driver vsc82xx_driver[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_VSC8244,
.name = "Vitesse VSC8244",
@@ -248,7 +247,6 @@ static struct phy_driver vsc82xx_driver[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_VSC8514,
.name = "Vitesse VSC8514",
@@ -260,7 +258,6 @@ static struct phy_driver vsc82xx_driver[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_VSC8574,
.name = "Vitesse VSC8574",
@@ -272,7 +269,6 @@ static struct phy_driver vsc82xx_driver[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_VSC8601,
.name = "Vitesse VSC8601",
@@ -284,7 +280,6 @@ static struct phy_driver vsc82xx_driver[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
.phy_id = PHY_ID_VSC8662,
.name = "Vitesse VSC8662",
@@ -296,7 +291,6 @@ static struct phy_driver vsc82xx_driver[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
/* Vitesse 8221 */
.phy_id = PHY_ID_VSC8221,
@@ -309,7 +303,6 @@ static struct phy_driver vsc82xx_driver[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
- .driver = { .owner = THIS_MODULE,},
}, {
/* Vitesse 8211 */
.phy_id = PHY_ID_VSC8211,
@@ -322,7 +315,6 @@ static struct phy_driver vsc82xx_driver[] = {
.read_status = &genphy_read_status,
.ack_interrupt = &vsc824x_ack_interrupt,
.config_intr = &vsc82xx_config_intr,
- .driver = { .owner = THIS_MODULE,},
} };
module_phy_driver(vsc82xx_driver);
diff --git a/drivers/net/plip/plip.c b/drivers/net/plip/plip.c
index 040b8978d6ca..9c4b41a4df7d 100644
--- a/drivers/net/plip/plip.c
+++ b/drivers/net/plip/plip.c
@@ -1249,6 +1249,7 @@ static void plip_attach (struct parport *port)
struct net_device *dev;
struct net_local *nl;
char name[IFNAMSIZ];
+ struct pardev_cb plip_cb;
if ((parport[0] == -1 && (!timid || !port->devices)) ||
plip_searchfor(parport, port->number)) {
@@ -1273,9 +1274,15 @@ static void plip_attach (struct parport *port)
nl = netdev_priv(dev);
nl->dev = dev;
- nl->pardev = parport_register_device(port, dev->name, plip_preempt,
- plip_wakeup, plip_interrupt,
- 0, dev);
+
+ memset(&plip_cb, 0, sizeof(plip_cb));
+ plip_cb.private = dev;
+ plip_cb.preempt = plip_preempt;
+ plip_cb.wakeup = plip_wakeup;
+ plip_cb.irq_func = plip_interrupt;
+
+ nl->pardev = parport_register_dev_model(port, dev->name,
+ &plip_cb, unit);
if (!nl->pardev) {
printk(KERN_ERR "%s: parport_register failed\n", name);
@@ -1315,10 +1322,23 @@ static void plip_detach (struct parport *port)
/* Nothing to do */
}
+static int plip_probe(struct pardevice *par_dev)
+{
+ struct device_driver *drv = par_dev->dev.driver;
+ int len = strlen(drv->name);
+
+ if (strncmp(par_dev->name, drv->name, len))
+ return -ENODEV;
+
+ return 0;
+}
+
static struct parport_driver plip_driver = {
- .name = "plip",
- .attach = plip_attach,
- .detach = plip_detach
+ .name = "plip",
+ .probe = plip_probe,
+ .match_port = plip_attach,
+ .detach = plip_detach,
+ .devmodel = true,
};
static void __exit plip_cleanup_module (void)
@@ -1326,8 +1346,6 @@ static void __exit plip_cleanup_module (void)
struct net_device *dev;
int i;
- parport_unregister_driver (&plip_driver);
-
for (i=0; i < PLIP_MAX; i++) {
if ((dev = dev_plip[i])) {
struct net_local *nl = netdev_priv(dev);
@@ -1339,6 +1357,8 @@ static void __exit plip_cleanup_module (void)
dev_plip[i] = NULL;
}
}
+
+ parport_unregister_driver(&plip_driver);
}
#ifndef MODULE
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index 9a863c6a6a33..fc8ad001bc94 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -1138,9 +1138,15 @@ static const struct net_device_ops ppp_netdev_ops = {
.ndo_get_stats64 = ppp_get_stats64,
};
+static struct device_type ppp_type = {
+ .name = "ppp",
+};
+
static void ppp_setup(struct net_device *dev)
{
dev->netdev_ops = &ppp_netdev_ops;
+ SET_NETDEV_DEVTYPE(dev, &ppp_type);
+
dev->hard_header_len = PPP_HDRLEN;
dev->mtu = PPP_MRU;
dev->addr_len = 0;
@@ -2720,8 +2726,7 @@ static struct ppp *ppp_create_interface(struct net *net, int unit,
int ret = -ENOMEM;
int i;
- dev = alloc_netdev(sizeof(struct ppp), "", NET_NAME_UNKNOWN,
- ppp_setup);
+ dev = alloc_netdev(sizeof(struct ppp), "", NET_NAME_ENUM, ppp_setup);
if (!dev)
goto out1;
diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
index 5e0b43283bce..f3c63022eb3c 100644
--- a/drivers/net/ppp/pppoe.c
+++ b/drivers/net/ppp/pppoe.c
@@ -311,7 +311,7 @@ static void pppoe_flush_dev(struct net_device *dev)
lock_sock(sk);
if (po->pppoe_dev == dev &&
- sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND | PPPOX_ZOMBIE)) {
+ sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) {
pppox_unbind_sock(sk);
sk->sk_state_change(sk);
po->pppoe_dev = NULL;
@@ -500,27 +500,9 @@ static int pppoe_disc_rcv(struct sk_buff *skb, struct net_device *dev,
pn = pppoe_pernet(dev_net(dev));
po = get_item(pn, ph->sid, eth_hdr(skb)->h_source, dev->ifindex);
- if (po) {
- struct sock *sk = sk_pppox(po);
-
- bh_lock_sock(sk);
-
- /* If the user has locked the socket, just ignore
- * the packet. With the way two rcv protocols hook into
- * one socket family type, we cannot (easily) distinguish
- * what kind of SKB it is during backlog rcv.
- */
- if (sock_owned_by_user(sk) == 0) {
- /* We're no longer connect at the PPPOE layer,
- * and must wait for ppp channel to disconnect us.
- */
- sk->sk_state = PPPOX_ZOMBIE;
- }
-
- bh_unlock_sock(sk);
+ if (po)
if (!schedule_work(&po->proto.pppoe.padt_work))
- sock_put(sk);
- }
+ sock_put(sk_pppox(po));
abort:
kfree_skb(skb);
@@ -568,6 +550,9 @@ static int pppoe_create(struct net *net, struct socket *sock, int kern)
sk->sk_family = PF_PPPOX;
sk->sk_protocol = PX_PROTO_OE;
+ INIT_WORK(&pppox_sk(sk)->proto.pppoe.padt_work,
+ pppoe_unbind_sock_work);
+
return 0;
}
@@ -632,8 +617,6 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr,
lock_sock(sk);
- INIT_WORK(&po->proto.pppoe.padt_work, pppoe_unbind_sock_work);
-
error = -EINVAL;
if (sp->sa_protocol != PX_PROTO_OE)
goto end;
@@ -663,8 +646,13 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr,
po->pppoe_dev = NULL;
}
- memset(sk_pppox(po) + 1, 0,
- sizeof(struct pppox_sock) - sizeof(struct sock));
+ po->pppoe_ifindex = 0;
+ memset(&po->pppoe_pa, 0, sizeof(po->pppoe_pa));
+ memset(&po->pppoe_relay, 0, sizeof(po->pppoe_relay));
+ memset(&po->chan, 0, sizeof(po->chan));
+ po->next = NULL;
+ po->num = 0;
+
sk->sk_state = PPPOX_NONE;
}
@@ -793,7 +781,7 @@ static int pppoe_ioctl(struct socket *sock, unsigned int cmd,
struct pppox_sock *relay_po;
err = -EBUSY;
- if (sk->sk_state & (PPPOX_BOUND | PPPOX_ZOMBIE | PPPOX_DEAD))
+ if (sk->sk_state & (PPPOX_BOUND | PPPOX_DEAD))
break;
err = -ENOTCONN;
@@ -1220,4 +1208,4 @@ module_exit(pppoe_exit);
MODULE_AUTHOR("Michal Ostrowski <mostrows@speakeasy.net>");
MODULE_DESCRIPTION("PPP over Ethernet driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_NETPROTO(PF_PPPOX);
+MODULE_ALIAS_NET_PF_PROTO(PF_PPPOX, PX_PROTO_OE);
diff --git a/drivers/net/ppp/pppox.c b/drivers/net/ppp/pppox.c
index 0e1b30622477..b9c8be6283d3 100644
--- a/drivers/net/ppp/pppox.c
+++ b/drivers/net/ppp/pppox.c
@@ -58,7 +58,7 @@ void pppox_unbind_sock(struct sock *sk)
{
/* Clear connection to ppp device, if attached. */
- if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED | PPPOX_ZOMBIE)) {
+ if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED)) {
ppp_unregister_channel(&pppox_sk(sk)->chan);
sk->sk_state = PPPOX_DEAD;
}
@@ -113,7 +113,7 @@ static int pppox_create(struct net *net, struct socket *sock, int protocol,
rc = -EPROTONOSUPPORT;
if (!pppox_protos[protocol])
- request_module("pppox-proto-%d", protocol);
+ request_module("net-pf-%d-proto-%d", PF_PPPOX, protocol);
if (!pppox_protos[protocol] ||
!try_module_get(pppox_protos[protocol]->owner))
goto out;
@@ -147,3 +147,4 @@ module_exit(pppox_exit);
MODULE_AUTHOR("Michal Ostrowski <mostrows@speakeasy.net>");
MODULE_DESCRIPTION("PPP over Ethernet driver (generic socket layer)");
MODULE_LICENSE("GPL");
+MODULE_ALIAS_NETPROTO(PF_PPPOX);
diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c
index fc69e41d0950..90868ca5e341 100644
--- a/drivers/net/ppp/pptp.c
+++ b/drivers/net/ppp/pptp.c
@@ -419,6 +419,9 @@ static int pptp_bind(struct socket *sock, struct sockaddr *uservaddr,
struct pptp_opt *opt = &po->proto.pptp;
int error = 0;
+ if (sockaddr_len < sizeof(struct sockaddr_pppox))
+ return -EINVAL;
+
lock_sock(sk);
opt->src_addr = sp->sa_addr.pptp;
@@ -440,6 +443,9 @@ static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr,
struct flowi4 fl4;
int error = 0;
+ if (sockaddr_len < sizeof(struct sockaddr_pppox))
+ return -EINVAL;
+
if (sp->sa_protocol != PX_PROTO_PPTP)
return -EINVAL;
@@ -718,3 +724,4 @@ module_exit(pptp_exit_module);
MODULE_DESCRIPTION("Point-to-Point Tunneling Protocol");
MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
MODULE_LICENSE("GPL");
+MODULE_ALIAS_NET_PF_PROTO(PF_PPPOX, PX_PROTO_PPTP);
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 651d35ea22c5..2528331de193 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -91,10 +91,24 @@ void team_modeop_port_change_dev_addr(struct team *team,
}
EXPORT_SYMBOL(team_modeop_port_change_dev_addr);
+static void team_lower_state_changed(struct team_port *port)
+{
+ struct netdev_lag_lower_state_info info;
+
+ info.link_up = port->linkup;
+ info.tx_enabled = team_port_enabled(port);
+ netdev_lower_state_changed(port->dev, &info);
+}
+
static void team_refresh_port_linkup(struct team_port *port)
{
- port->linkup = port->user.linkup_enabled ? port->user.linkup :
- port->state.linkup;
+ bool new_linkup = port->user.linkup_enabled ? port->user.linkup :
+ port->state.linkup;
+
+ if (port->linkup != new_linkup) {
+ port->linkup = new_linkup;
+ team_lower_state_changed(port);
+ }
}
@@ -932,6 +946,7 @@ static void team_port_enable(struct team *team,
team->ops.port_enabled(team, port);
team_notify_peers(team);
team_mcast_rejoin(team);
+ team_lower_state_changed(port);
}
static void __reconstruct_port_hlist(struct team *team, int rm_index)
@@ -963,16 +978,21 @@ static void team_port_disable(struct team *team,
team_adjust_ops(team);
team_notify_peers(team);
team_mcast_rejoin(team);
+ team_lower_state_changed(port);
}
-#define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \
+#define TEAM_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \
NETIF_F_HIGHDMA | NETIF_F_LRO)
+#define TEAM_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
+ NETIF_F_RXCSUM | NETIF_F_ALL_TSO)
+
static void __team_compute_features(struct team *team)
{
struct team_port *port;
u32 vlan_features = TEAM_VLAN_FEATURES & NETIF_F_ALL_FOR_ALL;
+ netdev_features_t enc_features = TEAM_ENC_FEATURES;
unsigned short max_hard_header_len = ETH_HLEN;
unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE |
IFF_XMIT_DST_RELEASE_PERM;
@@ -981,6 +1001,11 @@ static void __team_compute_features(struct team *team)
vlan_features = netdev_increment_features(vlan_features,
port->dev->vlan_features,
TEAM_VLAN_FEATURES);
+ enc_features =
+ netdev_increment_features(enc_features,
+ port->dev->hw_enc_features,
+ TEAM_ENC_FEATURES);
+
dst_release_flag &= port->dev->priv_flags;
if (port->dev->hard_header_len > max_hard_header_len)
@@ -988,6 +1013,7 @@ static void __team_compute_features(struct team *team)
}
team->dev->vlan_features = vlan_features;
+ team->dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL;
team->dev->hard_header_len = max_hard_header_len;
team->dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
@@ -1078,23 +1104,24 @@ static void team_port_disable_netpoll(struct team_port *port)
}
#endif
-static int team_upper_dev_link(struct net_device *dev,
- struct net_device *port_dev)
+static int team_upper_dev_link(struct team *team, struct team_port *port)
{
+ struct netdev_lag_upper_info lag_upper_info;
int err;
- err = netdev_master_upper_dev_link(port_dev, dev);
+ lag_upper_info.tx_type = team->mode->lag_tx_type;
+ err = netdev_master_upper_dev_link(port->dev, team->dev, NULL,
+ &lag_upper_info);
if (err)
return err;
- port_dev->priv_flags |= IFF_TEAM_PORT;
+ port->dev->priv_flags |= IFF_TEAM_PORT;
return 0;
}
-static void team_upper_dev_unlink(struct net_device *dev,
- struct net_device *port_dev)
+static void team_upper_dev_unlink(struct team *team, struct team_port *port)
{
- netdev_upper_dev_unlink(port_dev, dev);
- port_dev->priv_flags &= ~IFF_TEAM_PORT;
+ netdev_upper_dev_unlink(port->dev, team->dev);
+ port->dev->priv_flags &= ~IFF_TEAM_PORT;
}
static void __team_port_change_port_added(struct team_port *port, bool linkup);
@@ -1194,7 +1221,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
goto err_handler_register;
}
- err = team_upper_dev_link(dev, port_dev);
+ err = team_upper_dev_link(team, port);
if (err) {
netdev_err(dev, "Device %s failed to set upper link\n",
portname);
@@ -1220,7 +1247,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
return 0;
err_option_port_add:
- team_upper_dev_unlink(dev, port_dev);
+ team_upper_dev_unlink(team, port);
err_set_upper_link:
netdev_rx_handler_unregister(port_dev);
@@ -1264,7 +1291,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
team_port_disable(team, port);
list_del_rcu(&port->list);
- team_upper_dev_unlink(dev, port_dev);
+ team_upper_dev_unlink(team, port);
netdev_rx_handler_unregister(port_dev);
team_port_disable_netpoll(port);
vlan_vids_del_by_dev(port_dev, dev);
@@ -2054,6 +2081,7 @@ static void team_setup(struct net_device *dev)
dev->flags |= IFF_MULTICAST;
dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
dev->priv_flags |= IFF_NO_QUEUE;
+ dev->priv_flags |= IFF_TEAM;
/*
* Indicate we support unicast address filtering. That way core won't
@@ -2073,7 +2101,7 @@ static void team_setup(struct net_device *dev)
NETIF_F_HW_VLAN_CTAG_RX |
NETIF_F_HW_VLAN_CTAG_FILTER;
- dev->hw_features &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_HW_CSUM);
+ dev->hw_features |= NETIF_F_GSO_ENCAP_ALL;
dev->features |= dev->hw_features;
}
@@ -2420,9 +2448,13 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
struct nlattr *nl_option;
LIST_HEAD(opt_inst_list);
+ rtnl_lock();
+
team = team_nl_team_get(info);
- if (!team)
- return -EINVAL;
+ if (!team) {
+ err = -EINVAL;
+ goto rtnl_unlock;
+ }
err = -EINVAL;
if (!info->attrs[TEAM_ATTR_LIST_OPTION]) {
@@ -2549,7 +2581,8 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
team_put:
team_nl_team_put(team);
-
+rtnl_unlock:
+ rtnl_unlock();
return err;
}
diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c
index 40fd3381b693..3f189823ba3b 100644
--- a/drivers/net/team/team_mode_activebackup.c
+++ b/drivers/net/team/team_mode_activebackup.c
@@ -127,6 +127,7 @@ static const struct team_mode ab_mode = {
.owner = THIS_MODULE,
.priv_size = sizeof(struct ab_priv),
.ops = &ab_mode_ops,
+ .lag_tx_type = NETDEV_LAG_TX_TYPE_ACTIVEBACKUP,
};
static int __init ab_init_module(void)
diff --git a/drivers/net/team/team_mode_broadcast.c b/drivers/net/team/team_mode_broadcast.c
index c366cd299c06..302ff35b0cbc 100644
--- a/drivers/net/team/team_mode_broadcast.c
+++ b/drivers/net/team/team_mode_broadcast.c
@@ -56,6 +56,7 @@ static const struct team_mode bc_mode = {
.kind = "broadcast",
.owner = THIS_MODULE,
.ops = &bc_mode_ops,
+ .lag_tx_type = NETDEV_LAG_TX_TYPE_BROADCAST,
};
static int __init bc_init_module(void)
diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c
index a1536d0d83a9..cdb19b385d42 100644
--- a/drivers/net/team/team_mode_loadbalance.c
+++ b/drivers/net/team/team_mode_loadbalance.c
@@ -661,6 +661,7 @@ static const struct team_mode lb_mode = {
.priv_size = sizeof(struct lb_priv),
.port_priv_size = sizeof(struct lb_port_priv),
.ops = &lb_mode_ops,
+ .lag_tx_type = NETDEV_LAG_TX_TYPE_HASH,
};
static int __init lb_init_module(void)
diff --git a/drivers/net/team/team_mode_random.c b/drivers/net/team/team_mode_random.c
index cd2f692b8074..215f845782db 100644
--- a/drivers/net/team/team_mode_random.c
+++ b/drivers/net/team/team_mode_random.c
@@ -46,6 +46,7 @@ static const struct team_mode rnd_mode = {
.kind = "random",
.owner = THIS_MODULE,
.ops = &rnd_mode_ops,
+ .lag_tx_type = NETDEV_LAG_TX_TYPE_RANDOM,
};
static int __init rnd_init_module(void)
diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c
index 53665850b59e..0aa234118c03 100644
--- a/drivers/net/team/team_mode_roundrobin.c
+++ b/drivers/net/team/team_mode_roundrobin.c
@@ -58,6 +58,7 @@ static const struct team_mode rr_mode = {
.owner = THIS_MODULE,
.priv_size = sizeof(struct rr_priv),
.ops = &rr_mode_ops,
+ .lag_tx_type = NETDEV_LAG_TX_TYPE_ROUNDROBIN,
};
static int __init rr_init_module(void)
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index b1878faea397..88bb8cc3555b 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1040,7 +1040,7 @@ static unsigned int tun_chr_poll(struct file *file, poll_table *wait)
mask |= POLLIN | POLLRDNORM;
if (sock_writeable(sk) ||
- (!test_and_set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags) &&
+ (!test_and_set_bit(SOCKWQ_ASYNC_NOSPACE, &sk->sk_socket->flags) &&
sock_writeable(sk)))
mask |= POLLOUT | POLLWRNORM;
@@ -1095,6 +1095,9 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
u32 rxhash;
ssize_t n;
+ if (!(tun->dev->flags & IFF_UP))
+ return -EIO;
+
if (!(tun->flags & IFF_NO_PI)) {
if (len < sizeof(pi))
return -EINVAL;
@@ -1488,7 +1491,7 @@ static void tun_sock_write_space(struct sock *sk)
if (!sock_writeable(sk))
return;
- if (!test_and_clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags))
+ if (!test_and_clear_bit(SOCKWQ_ASYNC_NOSPACE, &sk->sk_socket->flags))
return;
wqueue = sk_sleep(sk);
diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c
index bd9acff1eb7b..0c5c22b84da8 100644
--- a/drivers/net/usb/asix_common.c
+++ b/drivers/net/usb/asix_common.c
@@ -118,7 +118,7 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb,
return 0;
}
if (size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) {
- netdev_err(dev->net, "asix_rx_fixup() Bad RX Length %d\n",
+ netdev_dbg(dev->net, "asix_rx_fixup() Bad RX Length %d\n",
size);
return 0;
}
diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c
index 5f18fcb8dcc7..224e7d82de6d 100644
--- a/drivers/net/usb/ax88172a.c
+++ b/drivers/net/usb/ax88172a.c
@@ -98,7 +98,7 @@ static void ax88172a_status(struct usbnet *dev, struct urb *urb)
static int ax88172a_init_mdio(struct usbnet *dev)
{
struct ax88172a_private *priv = dev->driver_priv;
- int ret, i;
+ int ret;
priv->mdio = mdiobus_alloc();
if (!priv->mdio) {
@@ -114,25 +114,15 @@ static int ax88172a_init_mdio(struct usbnet *dev)
snprintf(priv->mdio->id, MII_BUS_ID_SIZE, "usb-%03d:%03d",
dev->udev->bus->busnum, dev->udev->devnum);
- priv->mdio->irq = kzalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!priv->mdio->irq) {
- ret = -ENOMEM;
- goto mfree;
- }
- for (i = 0; i < PHY_MAX_ADDR; i++)
- priv->mdio->irq[i] = PHY_POLL;
-
ret = mdiobus_register(priv->mdio);
if (ret) {
netdev_err(dev->net, "Could not register MDIO bus\n");
- goto ifree;
+ goto mfree;
}
netdev_info(dev->net, "registered mdio bus %s\n", priv->mdio->id);
return 0;
-ifree:
- kfree(priv->mdio->irq);
mfree:
mdiobus_free(priv->mdio);
return ret;
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 3da70bf9936a..7cba2c3759df 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -160,6 +160,12 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
info->u = header.usb_cdc_union_desc;
info->header = header.usb_cdc_header_desc;
info->ether = header.usb_cdc_ether_desc;
+ if (!info->u) {
+ if (rndis)
+ goto skip;
+ else /* in that case a quirk is mandatory */
+ goto bad_desc;
+ }
/* we need a master/control interface (what we're
* probed with) and a slave/data interface; union
* descriptors sort this all out.
@@ -256,7 +262,7 @@ skip:
goto bad_desc;
}
- } else if (!info->header || !info->u || (!rndis && !info->ether)) {
+ } else if (!info->header || (!rndis && !info->ether)) {
dev_dbg(&intf->dev, "missing cdc %s%s%sdescriptor\n",
info->header ? "" : "header ",
info->u ? "" : "union ",
diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c
index bbde9884ab8a..bdd83d95ec0a 100644
--- a/drivers/net/usb/cdc_mbim.c
+++ b/drivers/net/usb/cdc_mbim.c
@@ -100,7 +100,7 @@ static const struct net_device_ops cdc_mbim_netdev_ops = {
.ndo_stop = usbnet_stop,
.ndo_start_xmit = usbnet_start_xmit,
.ndo_tx_timeout = usbnet_tx_timeout,
- .ndo_change_mtu = usbnet_change_mtu,
+ .ndo_change_mtu = cdc_ncm_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
.ndo_vlan_rx_add_vid = cdc_mbim_rx_add_vid,
@@ -158,7 +158,7 @@ static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf)
if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
goto err;
- ret = cdc_ncm_bind_common(dev, intf, data_altsetting, 0);
+ ret = cdc_ncm_bind_common(dev, intf, data_altsetting, dev->driver_info->data);
if (ret)
goto err;
@@ -582,6 +582,26 @@ static const struct driver_info cdc_mbim_info_zlp = {
.tx_fixup = cdc_mbim_tx_fixup,
};
+/* The spefication explicitly allows NDPs to be placed anywhere in the
+ * frame, but some devices fail unless the NDP is placed after the IP
+ * packets. Using the CDC_NCM_FLAG_NDP_TO_END flags to force this
+ * behaviour.
+ *
+ * Note: The current implementation of this feature restricts each NTB
+ * to a single NDP, implying that multiplexed sessions cannot share an
+ * NTB. This might affect performace for multiplexed sessions.
+ */
+static const struct driver_info cdc_mbim_info_ndp_to_end = {
+ .description = "CDC MBIM",
+ .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN,
+ .bind = cdc_mbim_bind,
+ .unbind = cdc_mbim_unbind,
+ .manage_power = cdc_mbim_manage_power,
+ .rx_fixup = cdc_mbim_rx_fixup,
+ .tx_fixup = cdc_mbim_tx_fixup,
+ .data = CDC_NCM_FLAG_NDP_TO_END,
+};
+
static const struct usb_device_id mbim_devs[] = {
/* This duplicate NCM entry is intentional. MBIM devices can
* be disguised as NCM by default, and this is necessary to
@@ -597,6 +617,10 @@ static const struct usb_device_id mbim_devs[] = {
{ USB_VENDOR_AND_INTERFACE_INFO(0x0bdb, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
.driver_info = (unsigned long)&cdc_mbim_info,
},
+ /* Huawei E3372 fails unless NDP comes after the IP packets */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x12d1, 0x157d, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
+ .driver_info = (unsigned long)&cdc_mbim_info_ndp_to_end,
+ },
/* default entry */
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
.driver_info = (unsigned long)&cdc_mbim_info_zlp,
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index a187f08113ec..dc0212c3cc28 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -41,6 +41,7 @@
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/ctype.h>
+#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/workqueue.h>
#include <linux/mii.h>
@@ -283,6 +284,48 @@ static DEVICE_ATTR(rx_max, S_IRUGO | S_IWUSR, cdc_ncm_show_rx_max, cdc_ncm_store
static DEVICE_ATTR(tx_max, S_IRUGO | S_IWUSR, cdc_ncm_show_tx_max, cdc_ncm_store_tx_max);
static DEVICE_ATTR(tx_timer_usecs, S_IRUGO | S_IWUSR, cdc_ncm_show_tx_timer_usecs, cdc_ncm_store_tx_timer_usecs);
+static ssize_t ndp_to_end_show(struct device *d, struct device_attribute *attr, char *buf)
+{
+ struct usbnet *dev = netdev_priv(to_net_dev(d));
+ struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+
+ return sprintf(buf, "%c\n", ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END ? 'Y' : 'N');
+}
+
+static ssize_t ndp_to_end_store(struct device *d, struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct usbnet *dev = netdev_priv(to_net_dev(d));
+ struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+ bool enable;
+
+ if (strtobool(buf, &enable))
+ return -EINVAL;
+
+ /* no change? */
+ if (enable == (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
+ return len;
+
+ if (enable && !ctx->delayed_ndp16) {
+ ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL);
+ if (!ctx->delayed_ndp16)
+ return -ENOMEM;
+ }
+
+ /* flush pending data before changing flag */
+ netif_tx_lock_bh(dev->net);
+ usbnet_start_xmit(NULL, dev->net);
+ spin_lock_bh(&ctx->mtx);
+ if (enable)
+ ctx->drvflags |= CDC_NCM_FLAG_NDP_TO_END;
+ else
+ ctx->drvflags &= ~CDC_NCM_FLAG_NDP_TO_END;
+ spin_unlock_bh(&ctx->mtx);
+ netif_tx_unlock_bh(dev->net);
+
+ return len;
+}
+static DEVICE_ATTR_RW(ndp_to_end);
+
#define NCM_PARM_ATTR(name, format, tocpu) \
static ssize_t cdc_ncm_show_##name(struct device *d, struct device_attribute *attr, char *buf) \
{ \
@@ -305,6 +348,7 @@ NCM_PARM_ATTR(wNtbOutMaxDatagrams, "%u", le16_to_cpu);
static struct attribute *cdc_ncm_sysfs_attrs[] = {
&dev_attr_min_tx_pkt.attr,
+ &dev_attr_ndp_to_end.attr,
&dev_attr_rx_max.attr,
&dev_attr_tx_max.attr,
&dev_attr_tx_timer_usecs.attr,
@@ -689,9 +733,35 @@ static void cdc_ncm_free(struct cdc_ncm_ctx *ctx)
kfree(ctx);
}
+/* we need to override the usbnet change_mtu ndo for two reasons:
+ * - respect the negotiated maximum datagram size
+ * - avoid unwanted changes to rx and tx buffers
+ */
+int cdc_ncm_change_mtu(struct net_device *net, int new_mtu)
+{
+ struct usbnet *dev = netdev_priv(net);
+ struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+ int maxmtu = ctx->max_datagram_size - cdc_ncm_eth_hlen(dev);
+
+ if (new_mtu <= 0 || new_mtu > maxmtu)
+ return -EINVAL;
+ net->mtu = new_mtu;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cdc_ncm_change_mtu);
+
+static const struct net_device_ops cdc_ncm_netdev_ops = {
+ .ndo_open = usbnet_open,
+ .ndo_stop = usbnet_stop,
+ .ndo_start_xmit = usbnet_start_xmit,
+ .ndo_tx_timeout = usbnet_tx_timeout,
+ .ndo_change_mtu = cdc_ncm_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting, int drvflags)
{
- const struct usb_cdc_union_desc *union_desc = NULL;
struct cdc_ncm_ctx *ctx;
struct usb_driver *driver;
u8 *buf;
@@ -725,15 +795,16 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_
/* parse through descriptors associated with control interface */
cdc_parse_cdc_header(&hdr, intf, buf, len);
- ctx->data = usb_ifnum_to_if(dev->udev,
- hdr.usb_cdc_union_desc->bSlaveInterface0);
+ if (hdr.usb_cdc_union_desc)
+ ctx->data = usb_ifnum_to_if(dev->udev,
+ hdr.usb_cdc_union_desc->bSlaveInterface0);
ctx->ether_desc = hdr.usb_cdc_ether_desc;
ctx->func_desc = hdr.usb_cdc_ncm_desc;
ctx->mbim_desc = hdr.usb_cdc_mbim_desc;
ctx->mbim_extended_desc = hdr.usb_cdc_mbim_extended_desc;
/* some buggy devices have an IAD but no CDC Union */
- if (!union_desc && intf->intf_assoc && intf->intf_assoc->bInterfaceCount == 2) {
+ if (!hdr.usb_cdc_union_desc && intf->intf_assoc && intf->intf_assoc->bInterfaceCount == 2) {
ctx->data = usb_ifnum_to_if(dev->udev, intf->cur_altsetting->desc.bInterfaceNumber + 1);
dev_dbg(&intf->dev, "CDC Union missing - got slave from IAD\n");
}
@@ -823,6 +894,9 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_
/* add our sysfs attrs */
dev->net->sysfs_groups[0] = &cdc_ncm_sysfs_attr_group;
+ /* must handle MTU changes */
+ dev->net->netdev_ops = &cdc_ncm_netdev_ops;
+
return 0;
error2:
@@ -955,10 +1029,18 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_
* NTH16 header as we would normally do. NDP isn't written to the SKB yet, and
* the wNdpIndex field in the header is actually not consistent with reality. It will be later.
*/
- if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END)
+ if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) {
if (ctx->delayed_ndp16->dwSignature == sign)
return ctx->delayed_ndp16;
+ /* We can only push a single NDP to the end. Return
+ * NULL to send what we've already got and queue this
+ * skb for later.
+ */
+ else if (ctx->delayed_ndp16->dwSignature)
+ return NULL;
+ }
+
/* follow the chain of NDPs, looking for a match */
while (ndpoffset) {
ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb->data + ndpoffset);
@@ -1550,6 +1632,24 @@ static const struct usb_device_id cdc_devs[] = {
.driver_info = (unsigned long) &wwan_info,
},
+ /* DW5812 LTE Verizon Mobile Broadband Card
+ * Unlike DW5550 this device requires FLAG_NOARP
+ */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x81bb,
+ USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
+ .driver_info = (unsigned long)&wwan_noarp_info,
+ },
+
+ /* DW5813 LTE AT&T Mobile Broadband Card
+ * Unlike DW5550 this device requires FLAG_NOARP
+ */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x413c, 0x81bc,
+ USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
+ .driver_info = (unsigned long)&wwan_noarp_info,
+ },
+
/* Dell branded MBM devices like DW5550 */
{ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
| USB_DEVICE_ID_MATCH_VENDOR,
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index 226668ead0d8..2ed53331bfb2 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -603,6 +603,59 @@ static int lan78xx_read_raw_otp(struct lan78xx_net *dev, u32 offset,
return 0;
}
+static int lan78xx_write_raw_otp(struct lan78xx_net *dev, u32 offset,
+ u32 length, u8 *data)
+{
+ int i;
+ int ret;
+ u32 buf;
+ unsigned long timeout;
+
+ ret = lan78xx_read_reg(dev, OTP_PWR_DN, &buf);
+
+ if (buf & OTP_PWR_DN_PWRDN_N_) {
+ /* clear it and wait to be cleared */
+ ret = lan78xx_write_reg(dev, OTP_PWR_DN, 0);
+
+ timeout = jiffies + HZ;
+ do {
+ udelay(1);
+ ret = lan78xx_read_reg(dev, OTP_PWR_DN, &buf);
+ if (time_after(jiffies, timeout)) {
+ netdev_warn(dev->net,
+ "timeout on OTP_PWR_DN completion");
+ return -EIO;
+ }
+ } while (buf & OTP_PWR_DN_PWRDN_N_);
+ }
+
+ /* set to BYTE program mode */
+ ret = lan78xx_write_reg(dev, OTP_PRGM_MODE, OTP_PRGM_MODE_BYTE_);
+
+ for (i = 0; i < length; i++) {
+ ret = lan78xx_write_reg(dev, OTP_ADDR1,
+ ((offset + i) >> 8) & OTP_ADDR1_15_11);
+ ret = lan78xx_write_reg(dev, OTP_ADDR2,
+ ((offset + i) & OTP_ADDR2_10_3));
+ ret = lan78xx_write_reg(dev, OTP_PRGM_DATA, data[i]);
+ ret = lan78xx_write_reg(dev, OTP_TST_CMD, OTP_TST_CMD_PRGVRFY_);
+ ret = lan78xx_write_reg(dev, OTP_CMD_GO, OTP_CMD_GO_GO_);
+
+ timeout = jiffies + HZ;
+ do {
+ udelay(1);
+ ret = lan78xx_read_reg(dev, OTP_STATUS, &buf);
+ if (time_after(jiffies, timeout)) {
+ netdev_warn(dev->net,
+ "Timeout on OTP_STATUS completion");
+ return -EIO;
+ }
+ } while (buf & OTP_STATUS_BUSY_);
+ }
+
+ return 0;
+}
+
static int lan78xx_read_otp(struct lan78xx_net *dev, u32 offset,
u32 length, u8 *data)
{
@@ -969,7 +1022,7 @@ static int lan78xx_ethtool_set_eeprom(struct net_device *netdev,
(ee->offset == 0) &&
(ee->len == 512) &&
(data[0] == OTP_INDICATOR_1))
- return lan78xx_write_raw_eeprom(dev, ee->offset, ee->len, data);
+ return lan78xx_write_raw_otp(dev, ee->offset, ee->len, data);
return -EINVAL;
}
@@ -1458,12 +1511,6 @@ static int lan78xx_mdio_init(struct lan78xx_net *dev)
snprintf(dev->mdiobus->id, MII_BUS_ID_SIZE, "usb-%03d:%03d",
dev->udev->bus->busnum, dev->udev->devnum);
- dev->mdiobus->irq = kzalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!dev->mdiobus->irq) {
- ret = -ENOMEM;
- goto exit1;
- }
-
/* handle our own interrupt */
for (i = 0; i < PHY_MAX_ADDR; i++)
dev->mdiobus->irq[i] = PHY_IGNORE_INTERRUPT;
@@ -1479,13 +1526,11 @@ static int lan78xx_mdio_init(struct lan78xx_net *dev)
ret = mdiobus_register(dev->mdiobus);
if (ret) {
netdev_err(dev->net, "can't register MDIO bus\n");
- goto exit2;
+ goto exit1;
}
netdev_dbg(dev->net, "registered mdiobus bus %s\n", dev->mdiobus->id);
return 0;
-exit2:
- kfree(dev->mdiobus->irq);
exit1:
mdiobus_free(dev->mdiobus);
return ret;
@@ -1494,7 +1539,6 @@ exit1:
static void lan78xx_remove_mdio(struct lan78xx_net *dev)
{
mdiobus_unregister(dev->mdiobus);
- kfree(dev->mdiobus->irq);
mdiobus_free(dev->mdiobus);
}
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 34799eaace41..23e9880791fc 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -14,7 +14,9 @@
#include <linux/netdevice.h>
#include <linux/ethtool.h>
#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
#include <linux/mii.h>
+#include <linux/rtnetlink.h>
#include <linux/usb.h>
#include <linux/usb/cdc.h>
#include <linux/usb/usbnet.h>
@@ -48,11 +50,100 @@
struct qmi_wwan_state {
struct usb_driver *subdriver;
atomic_t pmcount;
- unsigned long unused;
+ unsigned long flags;
struct usb_interface *control;
struct usb_interface *data;
};
+enum qmi_wwan_flags {
+ QMI_WWAN_FLAG_RAWIP = 1 << 0,
+};
+
+static void qmi_wwan_netdev_setup(struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+ struct qmi_wwan_state *info = (void *)&dev->data;
+
+ if (info->flags & QMI_WWAN_FLAG_RAWIP) {
+ net->header_ops = NULL; /* No header */
+ net->type = ARPHRD_NONE;
+ net->hard_header_len = 0;
+ net->addr_len = 0;
+ net->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ netdev_dbg(net, "mode: raw IP\n");
+ } else if (!net->header_ops) { /* don't bother if already set */
+ ether_setup(net);
+ netdev_dbg(net, "mode: Ethernet\n");
+ }
+
+ /* recalculate buffers after changing hard_header_len */
+ usbnet_change_mtu(net, net->mtu);
+}
+
+static ssize_t raw_ip_show(struct device *d, struct device_attribute *attr, char *buf)
+{
+ struct usbnet *dev = netdev_priv(to_net_dev(d));
+ struct qmi_wwan_state *info = (void *)&dev->data;
+
+ return sprintf(buf, "%c\n", info->flags & QMI_WWAN_FLAG_RAWIP ? 'Y' : 'N');
+}
+
+static ssize_t raw_ip_store(struct device *d, struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct usbnet *dev = netdev_priv(to_net_dev(d));
+ struct qmi_wwan_state *info = (void *)&dev->data;
+ bool enable;
+ int ret;
+
+ if (strtobool(buf, &enable))
+ return -EINVAL;
+
+ /* no change? */
+ if (enable == (info->flags & QMI_WWAN_FLAG_RAWIP))
+ return len;
+
+ if (!rtnl_trylock())
+ return restart_syscall();
+
+ /* we don't want to modify a running netdev */
+ if (netif_running(dev->net)) {
+ netdev_err(dev->net, "Cannot change a running device\n");
+ ret = -EBUSY;
+ goto err;
+ }
+
+ /* let other drivers deny the change */
+ ret = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev->net);
+ ret = notifier_to_errno(ret);
+ if (ret) {
+ netdev_err(dev->net, "Type change was refused\n");
+ goto err;
+ }
+
+ if (enable)
+ info->flags |= QMI_WWAN_FLAG_RAWIP;
+ else
+ info->flags &= ~QMI_WWAN_FLAG_RAWIP;
+ qmi_wwan_netdev_setup(dev->net);
+ call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev->net);
+ ret = len;
+err:
+ rtnl_unlock();
+ return ret;
+}
+
+static DEVICE_ATTR_RW(raw_ip);
+
+static struct attribute *qmi_wwan_sysfs_attrs[] = {
+ &dev_attr_raw_ip.attr,
+ NULL,
+};
+
+static struct attribute_group qmi_wwan_sysfs_attr_group = {
+ .name = "qmi",
+ .attrs = qmi_wwan_sysfs_attrs,
+};
+
/* default ethernet address used by the modem */
static const u8 default_modem_addr[ETH_ALEN] = {0x02, 0x50, 0xf3};
@@ -80,6 +171,8 @@ static const u8 buggy_fw_addr[ETH_ALEN] = {0x00, 0xa0, 0xc6, 0x00, 0x00, 0x00};
*/
static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{
+ struct qmi_wwan_state *info = (void *)&dev->data;
+ bool rawip = info->flags & QMI_WWAN_FLAG_RAWIP;
__be16 proto;
/* This check is no longer done by usbnet */
@@ -94,15 +187,25 @@ static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
proto = htons(ETH_P_IPV6);
break;
case 0x00:
+ if (rawip)
+ return 0;
if (is_multicast_ether_addr(skb->data))
return 1;
/* possibly bogus destination - rewrite just in case */
skb_reset_mac_header(skb);
goto fix_dest;
default:
+ if (rawip)
+ return 0;
/* pass along other packets without modifications */
return 1;
}
+ if (rawip) {
+ skb->dev = dev->net; /* normally set by eth_type_trans */
+ skb->protocol = proto;
+ return 1;
+ }
+
if (skb_headroom(skb) < ETH_HLEN)
return 0;
skb_push(skb, ETH_HLEN);
@@ -223,6 +326,20 @@ err:
return rv;
}
+/* Send CDC SetControlLineState request, setting or clearing the DTR.
+ * "Required for Autoconnect and 9x30 to wake up" according to the
+ * GobiNet driver. The requirement has been verified on an MDM9230
+ * based Sierra Wireless MC7455
+ */
+static int qmi_wwan_change_dtr(struct usbnet *dev, bool on)
+{
+ u8 intf = dev->intf->cur_altsetting->desc.bInterfaceNumber;
+
+ return usbnet_write_cmd(dev, USB_CDC_REQ_SET_CONTROL_LINE_STATE,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ on ? 0x01 : 0x00, intf, NULL, 0);
+}
+
static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
{
int status = -1;
@@ -257,7 +374,10 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
"bogus CDC Union: master=%u, slave=%u\n",
cdc_union->bMasterInterface0,
cdc_union->bSlaveInterface0);
- goto err;
+
+ /* ignore and continue... */
+ cdc_union = NULL;
+ info->data = intf;
}
}
@@ -280,6 +400,24 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
usb_driver_release_interface(driver, info->data);
}
+ /* disabling remote wakeup on MDM9x30 devices has the same
+ * effect as clearing DTR. The device will not respond to QMI
+ * requests until we set DTR again. This is similar to a
+ * QMI_CTL SYNC request, clearing a lot of firmware state
+ * including the client ID allocations.
+ *
+ * Our usage model allows a session to span multiple
+ * open/close events, so we must prevent the firmware from
+ * clearing out state the clients might need.
+ *
+ * MDM9x30 is the first QMI chipset with USB3 support. Abuse
+ * this fact to enable the quirk.
+ */
+ if (le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) {
+ qmi_wwan_manage_power(dev, 1);
+ qmi_wwan_change_dtr(dev, true);
+ }
+
/* Never use the same address on both ends of the link, even if the
* buggy firmware told us to. Or, if device is assigned the well-known
* buggy firmware MAC address, replace it with a random address,
@@ -294,6 +432,7 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
dev->net->dev_addr[0] &= 0xbf; /* clear "IP" bit */
}
dev->net->netdev_ops = &qmi_wwan_netdev_ops;
+ dev->net->sysfs_groups[0] = &qmi_wwan_sysfs_attr_group;
err:
return status;
}
@@ -307,6 +446,12 @@ static void qmi_wwan_unbind(struct usbnet *dev, struct usb_interface *intf)
if (info->subdriver && info->subdriver->disconnect)
info->subdriver->disconnect(info->control);
+ /* disable MDM9x30 quirk */
+ if (le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) {
+ qmi_wwan_change_dtr(dev, false);
+ qmi_wwan_manage_power(dev, 0);
+ }
+
/* allow user to unbind using either control or data */
if (intf == info->control)
other = info->data;
@@ -715,8 +860,6 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x1199, 0x9056, 8)}, /* Sierra Wireless Modem */
{QMI_FIXED_INTF(0x1199, 0x9057, 8)},
{QMI_FIXED_INTF(0x1199, 0x9061, 8)}, /* Sierra Wireless Modem */
- {QMI_FIXED_INTF(0x1199, 0x9070, 8)}, /* Sierra Wireless MC74xx/EM74xx */
- {QMI_FIXED_INTF(0x1199, 0x9070, 10)}, /* Sierra Wireless MC74xx/EM74xx */
{QMI_FIXED_INTF(0x1199, 0x9071, 8)}, /* Sierra Wireless MC74xx/EM74xx */
{QMI_FIXED_INTF(0x1199, 0x9071, 10)}, /* Sierra Wireless MC74xx/EM74xx */
{QMI_FIXED_INTF(0x1bbb, 0x011e, 4)}, /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */
@@ -725,6 +868,7 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x2357, 0x9000, 4)}, /* TP-LINK MA260 */
{QMI_FIXED_INTF(0x1bc7, 0x1200, 5)}, /* Telit LE920 */
{QMI_FIXED_INTF(0x1bc7, 0x1201, 2)}, /* Telit LE920 */
+ {QMI_FIXED_INTF(0x1c9e, 0x9b01, 3)}, /* XS Stick W100-2 from 4G Systems */
{QMI_FIXED_INTF(0x0b3c, 0xc000, 4)}, /* Olivetti Olicard 100 */
{QMI_FIXED_INTF(0x0b3c, 0xc001, 4)}, /* Olivetti Olicard 120 */
{QMI_FIXED_INTF(0x0b3c, 0xc002, 4)}, /* Olivetti Olicard 140 */
@@ -741,6 +885,8 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x413c, 0x81a9, 8)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */
{QMI_FIXED_INTF(0x413c, 0x81b1, 8)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */
{QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */
+ {QMI_FIXED_INTF(0x22de, 0x9061, 3)}, /* WeTelecom WPD-600N */
+ {QMI_FIXED_INTF(0x1e0e, 0x9001, 5)}, /* SIMCom 7230E */
/* 4. Gobi 1000 devices */
{QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index d9427ca3dba7..d1f78c2c97aa 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -25,12 +25,13 @@
#include <uapi/linux/mdio.h>
#include <linux/mdio.h>
#include <linux/usb/cdc.h>
+#include <linux/suspend.h>
/* Information for net-next */
#define NETNEXT_VERSION "08"
/* Information for net */
-#define NET_VERSION "2"
+#define NET_VERSION "3"
#define DRIVER_VERSION "v1." NETNEXT_VERSION "." NET_VERSION
#define DRIVER_AUTHOR "Realtek linux nic maintainers <nic_swsd@realtek.com>"
@@ -604,6 +605,9 @@ struct r8152 {
struct delayed_work schedule;
struct mii_if_info mii;
struct mutex control; /* use for hw setting */
+#ifdef CONFIG_PM_SLEEP
+ struct notifier_block pm_notifier;
+#endif
struct rtl_ops {
void (*init)(struct r8152 *);
@@ -1938,7 +1942,6 @@ static void _rtl8152_set_rx_mode(struct net_device *netdev)
__le32 tmp[2];
u32 ocp_data;
- clear_bit(RTL8152_SET_RX_MODE, &tp->flags);
netif_stop_queue(netdev);
ocp_data = ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR);
ocp_data &= ~RCR_ACPT_ALL;
@@ -1986,7 +1989,7 @@ rtl8152_features_check(struct sk_buff *skb, struct net_device *dev,
int offset = skb_transport_offset(skb);
if ((mss || skb->ip_summed == CHECKSUM_PARTIAL) && offset > max_offset)
- features &= ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK);
+ features &= ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
else if ((skb->len + sizeof(struct tx_desc)) > agg_buf_sz)
features &= ~NETIF_F_GSO_MASK;
@@ -2424,8 +2427,6 @@ static void rtl_phy_reset(struct r8152 *tp)
u16 data;
int i;
- clear_bit(PHY_RESET, &tp->flags);
-
data = r8152_mdio_read(tp, MII_BMCR);
/* don't reset again before the previous one complete */
@@ -2455,23 +2456,23 @@ static void r8153_teredo_off(struct r8152 *tp)
ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TEREDO_TIMER, 0);
}
-static void r8152b_disable_aldps(struct r8152 *tp)
-{
- ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPDNPS | LINKENA | DIS_SDSAVE);
- msleep(20);
-}
-
-static inline void r8152b_enable_aldps(struct r8152 *tp)
+static void r8152_aldps_en(struct r8152 *tp, bool enable)
{
- ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPWRSAVE | ENPDNPS |
- LINKENA | DIS_SDSAVE);
+ if (enable) {
+ ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPWRSAVE | ENPDNPS |
+ LINKENA | DIS_SDSAVE);
+ } else {
+ ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPDNPS | LINKENA |
+ DIS_SDSAVE);
+ msleep(20);
+ }
}
static void rtl8152_disable(struct r8152 *tp)
{
- r8152b_disable_aldps(tp);
+ r8152_aldps_en(tp, false);
rtl_disable(tp);
- r8152b_enable_aldps(tp);
+ r8152_aldps_en(tp, true);
}
static void r8152b_hw_phy_cfg(struct r8152 *tp)
@@ -2783,30 +2784,26 @@ static void r8153_enter_oob(struct r8152 *tp)
ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
}
-static void r8153_disable_aldps(struct r8152 *tp)
+static void r8153_aldps_en(struct r8152 *tp, bool enable)
{
u16 data;
data = ocp_reg_read(tp, OCP_POWER_CFG);
- data &= ~EN_ALDPS;
- ocp_reg_write(tp, OCP_POWER_CFG, data);
- msleep(20);
-}
-
-static void r8153_enable_aldps(struct r8152 *tp)
-{
- u16 data;
-
- data = ocp_reg_read(tp, OCP_POWER_CFG);
- data |= EN_ALDPS;
- ocp_reg_write(tp, OCP_POWER_CFG, data);
+ if (enable) {
+ data |= EN_ALDPS;
+ ocp_reg_write(tp, OCP_POWER_CFG, data);
+ } else {
+ data &= ~EN_ALDPS;
+ ocp_reg_write(tp, OCP_POWER_CFG, data);
+ msleep(20);
+ }
}
static void rtl8153_disable(struct r8152 *tp)
{
- r8153_disable_aldps(tp);
+ r8153_aldps_en(tp, false);
rtl_disable(tp);
- r8153_enable_aldps(tp);
+ r8153_aldps_en(tp, true);
usb_enable_lpm(tp->udev);
}
@@ -2884,10 +2881,9 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex)
r8152_mdio_write(tp, MII_ADVERTISE, anar);
r8152_mdio_write(tp, MII_BMCR, bmcr);
- if (test_bit(PHY_RESET, &tp->flags)) {
+ if (test_and_clear_bit(PHY_RESET, &tp->flags)) {
int i;
- clear_bit(PHY_RESET, &tp->flags);
for (i = 0; i < 50; i++) {
msleep(20);
if ((r8152_mdio_read(tp, MII_BMCR) & BMCR_RESET) == 0)
@@ -2896,7 +2892,6 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex)
}
out:
-
return ret;
}
@@ -2905,9 +2900,9 @@ static void rtl8152_up(struct r8152 *tp)
if (test_bit(RTL8152_UNPLUG, &tp->flags))
return;
- r8152b_disable_aldps(tp);
+ r8152_aldps_en(tp, false);
r8152b_exit_oob(tp);
- r8152b_enable_aldps(tp);
+ r8152_aldps_en(tp, true);
}
static void rtl8152_down(struct r8152 *tp)
@@ -2918,9 +2913,9 @@ static void rtl8152_down(struct r8152 *tp)
}
r8152_power_cut_en(tp, false);
- r8152b_disable_aldps(tp);
+ r8152_aldps_en(tp, false);
r8152b_enter_oob(tp);
- r8152b_enable_aldps(tp);
+ r8152_aldps_en(tp, true);
}
static void rtl8153_up(struct r8152 *tp)
@@ -2929,9 +2924,9 @@ static void rtl8153_up(struct r8152 *tp)
return;
r8153_u1u2en(tp, false);
- r8153_disable_aldps(tp);
+ r8153_aldps_en(tp, false);
r8153_first_init(tp);
- r8153_enable_aldps(tp);
+ r8153_aldps_en(tp, true);
r8153_u2p3en(tp, true);
r8153_u1u2en(tp, true);
usb_enable_lpm(tp->udev);
@@ -2947,9 +2942,9 @@ static void rtl8153_down(struct r8152 *tp)
r8153_u1u2en(tp, false);
r8153_u2p3en(tp, false);
r8153_power_cut_en(tp, false);
- r8153_disable_aldps(tp);
+ r8153_aldps_en(tp, false);
r8153_enter_oob(tp);
- r8153_enable_aldps(tp);
+ r8153_aldps_en(tp, true);
}
static bool rtl8152_in_nway(struct r8152 *tp)
@@ -2983,7 +2978,6 @@ static void set_carrier(struct r8152 *tp)
struct net_device *netdev = tp->netdev;
u8 speed;
- clear_bit(RTL8152_LINK_CHG, &tp->flags);
speed = rtl8152_get_speed(tp);
if (speed & LINK_STATUS) {
@@ -3026,20 +3020,18 @@ static void rtl_work_func_t(struct work_struct *work)
goto out1;
}
- if (test_bit(RTL8152_LINK_CHG, &tp->flags))
+ if (test_and_clear_bit(RTL8152_LINK_CHG, &tp->flags))
set_carrier(tp);
- if (test_bit(RTL8152_SET_RX_MODE, &tp->flags))
+ if (test_and_clear_bit(RTL8152_SET_RX_MODE, &tp->flags))
_rtl8152_set_rx_mode(tp->netdev);
/* don't schedule napi before linking */
- if (test_bit(SCHEDULE_NAPI, &tp->flags) &&
- netif_carrier_ok(tp->netdev)) {
- clear_bit(SCHEDULE_NAPI, &tp->flags);
+ if (test_and_clear_bit(SCHEDULE_NAPI, &tp->flags) &&
+ netif_carrier_ok(tp->netdev))
napi_schedule(&tp->napi);
- }
- if (test_bit(PHY_RESET, &tp->flags))
+ if (test_and_clear_bit(PHY_RESET, &tp->flags))
rtl_phy_reset(tp);
mutex_unlock(&tp->control);
@@ -3048,6 +3040,33 @@ out1:
usb_autopm_put_interface(tp->intf);
}
+#ifdef CONFIG_PM_SLEEP
+static int rtl_notifier(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct r8152 *tp = container_of(nb, struct r8152, pm_notifier);
+
+ switch (action) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ usb_autopm_get_interface(tp->intf);
+ break;
+
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ usb_autopm_put_interface(tp->intf);
+ break;
+
+ case PM_POST_RESTORE:
+ case PM_RESTORE_PREPARE:
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+#endif
+
static int rtl8152_open(struct net_device *netdev)
{
struct r8152 *tp = netdev_priv(netdev);
@@ -3067,17 +3086,6 @@ static int rtl8152_open(struct net_device *netdev)
mutex_lock(&tp->control);
- /* The WORK_ENABLE may be set when autoresume occurs */
- if (test_bit(WORK_ENABLE, &tp->flags)) {
- clear_bit(WORK_ENABLE, &tp->flags);
- usb_kill_urb(tp->intr_urb);
- cancel_delayed_work_sync(&tp->schedule);
-
- /* disable the tx/rx, if the workqueue has enabled them. */
- if (netif_carrier_ok(netdev))
- tp->rtl_ops.disable(tp);
- }
-
tp->rtl_ops.up(tp);
rtl8152_set_speed(tp, AUTONEG_ENABLE,
@@ -3101,6 +3109,10 @@ static int rtl8152_open(struct net_device *netdev)
mutex_unlock(&tp->control);
usb_autopm_put_interface(tp->intf);
+#ifdef CONFIG_PM_SLEEP
+ tp->pm_notifier.notifier_call = rtl_notifier;
+ register_pm_notifier(&tp->pm_notifier);
+#endif
out:
return res;
@@ -3111,6 +3123,9 @@ static int rtl8152_close(struct net_device *netdev)
struct r8152 *tp = netdev_priv(netdev);
int res = 0;
+#ifdef CONFIG_PM_SLEEP
+ unregister_pm_notifier(&tp->pm_notifier);
+#endif
napi_disable(&tp->napi);
clear_bit(WORK_ENABLE, &tp->flags);
usb_kill_urb(tp->intr_urb);
@@ -3124,12 +3139,6 @@ static int rtl8152_close(struct net_device *netdev)
} else {
mutex_lock(&tp->control);
- /* The autosuspend may have been enabled and wouldn't
- * be disable when autoresume occurs, because the
- * netif_running() would be false.
- */
- rtl_runtime_suspend_enable(tp, false);
-
tp->rtl_ops.down(tp);
mutex_unlock(&tp->control);
@@ -3255,7 +3264,7 @@ static void r8152b_init(struct r8152 *tp)
if (test_bit(RTL8152_UNPLUG, &tp->flags))
return;
- r8152b_disable_aldps(tp);
+ r8152_aldps_en(tp, false);
if (tp->version == RTL_VER_01) {
ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE);
@@ -3277,7 +3286,7 @@ static void r8152b_init(struct r8152 *tp)
ocp_write_word(tp, MCU_TYPE_PLA, PLA_GPHY_INTR_IMR, ocp_data);
r8152b_enable_eee(tp);
- r8152b_enable_aldps(tp);
+ r8152_aldps_en(tp, true);
r8152b_enable_fc(tp);
rtl_tally_reset(tp);
@@ -3295,7 +3304,7 @@ static void r8153_init(struct r8152 *tp)
if (test_bit(RTL8152_UNPLUG, &tp->flags))
return;
- r8153_disable_aldps(tp);
+ r8153_aldps_en(tp, false);
r8153_u1u2en(tp, false);
for (i = 0; i < 500; i++) {
@@ -3384,7 +3393,7 @@ static void r8153_init(struct r8152 *tp)
EEE_SPDWN_EN);
r8153_enable_eee(tp);
- r8153_enable_aldps(tp);
+ r8153_aldps_en(tp, true);
r8152b_enable_fc(tp);
rtl_tally_reset(tp);
r8153_u2p3en(tp, true);
@@ -3512,7 +3521,7 @@ static int rtl8152_resume(struct usb_interface *intf)
netif_device_attach(tp->netdev);
}
- if (netif_running(tp->netdev)) {
+ if (netif_running(tp->netdev) && tp->netdev->flags & IFF_UP) {
if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) {
rtl_runtime_suspend_enable(tp, false);
clear_bit(SELECTIVE_SUSPEND, &tp->flags);
@@ -3532,6 +3541,8 @@ static int rtl8152_resume(struct usb_interface *intf)
}
usb_submit_urb(tp->intr_urb, GFP_KERNEL);
} else if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) {
+ if (tp->netdev->flags & IFF_UP)
+ rtl_runtime_suspend_enable(tp, false);
clear_bit(SELECTIVE_SUSPEND, &tp->flags);
}
@@ -3540,6 +3551,14 @@ static int rtl8152_resume(struct usb_interface *intf)
return 0;
}
+static int rtl8152_reset_resume(struct usb_interface *intf)
+{
+ struct r8152 *tp = usb_get_intfdata(intf);
+
+ clear_bit(SELECTIVE_SUSPEND, &tp->flags);
+ return rtl8152_resume(intf);
+}
+
static void rtl8152_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct r8152 *tp = netdev_priv(dev);
@@ -4291,7 +4310,7 @@ static struct usb_driver rtl8152_driver = {
.disconnect = rtl8152_disconnect,
.suspend = rtl8152_suspend,
.resume = rtl8152_resume,
- .reset_resume = rtl8152_resume,
+ .reset_resume = rtl8152_reset_resume,
.pre_reset = rtl8152_pre_reset,
.post_reset = rtl8152_post_reset,
.supports_autosuspend = 1,
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 0744bf2ef2d6..0b0ba7ef14e4 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -324,7 +324,10 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
return;
}
- skb->protocol = eth_type_trans (skb, dev->net);
+ /* only update if unset to allow minidriver rx_fixup override */
+ if (skb->protocol == 0)
+ skb->protocol = eth_type_trans (skb, dev->net);
+
dev->net->stats.rx_packets++;
dev->net->stats.rx_bytes += skb->len;
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 0ef4a5ad5557..ba21d072be31 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -117,12 +117,6 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
kfree_skb(skb);
goto drop;
}
- /* don't change ip_summed == CHECKSUM_PARTIAL, as that
- * will cause bad checksum on forwarded packets
- */
- if (skb->ip_summed == CHECKSUM_NONE &&
- rcv->features & NETIF_F_RXCSUM)
- skb->ip_summed = CHECKSUM_UNNECESSARY;
if (likely(dev_forward_skb(rcv, skb) == NET_RX_SUCCESS)) {
struct pcpu_vstats *stats = this_cpu_ptr(dev->vstats);
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index d8838dedb7a4..767ab11a6e9f 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -140,6 +140,12 @@ struct virtnet_info {
/* CPU hot plug notifier */
struct notifier_block nb;
+
+ /* Control VQ buffers: protected by the rtnl lock */
+ struct virtio_net_ctrl_hdr ctrl_hdr;
+ virtio_net_ctrl_ack ctrl_status;
+ u8 ctrl_promisc;
+ u8 ctrl_allmulti;
};
struct padded_vnet_hdr {
@@ -516,8 +522,6 @@ static void receive_buf(struct virtnet_info *vi, struct receive_queue *rq,
skb_shinfo(skb)->gso_segs = 0;
}
- skb_mark_napi_id(skb, &rq->napi);
-
napi_gro_receive(&rq->napi, skb);
return;
@@ -976,31 +980,30 @@ static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd,
struct scatterlist *out)
{
struct scatterlist *sgs[4], hdr, stat;
- struct virtio_net_ctrl_hdr ctrl;
- virtio_net_ctrl_ack status = ~0;
unsigned out_num = 0, tmp;
/* Caller should know better */
BUG_ON(!virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ));
- ctrl.class = class;
- ctrl.cmd = cmd;
+ vi->ctrl_status = ~0;
+ vi->ctrl_hdr.class = class;
+ vi->ctrl_hdr.cmd = cmd;
/* Add header */
- sg_init_one(&hdr, &ctrl, sizeof(ctrl));
+ sg_init_one(&hdr, &vi->ctrl_hdr, sizeof(vi->ctrl_hdr));
sgs[out_num++] = &hdr;
if (out)
sgs[out_num++] = out;
/* Add return status. */
- sg_init_one(&stat, &status, sizeof(status));
+ sg_init_one(&stat, &vi->ctrl_status, sizeof(vi->ctrl_status));
sgs[out_num] = &stat;
BUG_ON(out_num + 1 > ARRAY_SIZE(sgs));
virtqueue_add_sgs(vi->cvq, sgs, out_num, 1, vi, GFP_ATOMIC);
if (unlikely(!virtqueue_kick(vi->cvq)))
- return status == VIRTIO_NET_OK;
+ return vi->ctrl_status == VIRTIO_NET_OK;
/* Spin for a response, the kick causes an ioport write, trapping
* into the hypervisor, so the request should be handled immediately.
@@ -1009,7 +1012,7 @@ static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd,
!virtqueue_is_broken(vi->cvq))
cpu_relax();
- return status == VIRTIO_NET_OK;
+ return vi->ctrl_status == VIRTIO_NET_OK;
}
static int virtnet_set_mac_address(struct net_device *dev, void *p)
@@ -1151,7 +1154,6 @@ static void virtnet_set_rx_mode(struct net_device *dev)
{
struct virtnet_info *vi = netdev_priv(dev);
struct scatterlist sg[2];
- u8 promisc, allmulti;
struct virtio_net_ctrl_mac *mac_data;
struct netdev_hw_addr *ha;
int uc_count;
@@ -1163,22 +1165,22 @@ static void virtnet_set_rx_mode(struct net_device *dev)
if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_RX))
return;
- promisc = ((dev->flags & IFF_PROMISC) != 0);
- allmulti = ((dev->flags & IFF_ALLMULTI) != 0);
+ vi->ctrl_promisc = ((dev->flags & IFF_PROMISC) != 0);
+ vi->ctrl_allmulti = ((dev->flags & IFF_ALLMULTI) != 0);
- sg_init_one(sg, &promisc, sizeof(promisc));
+ sg_init_one(sg, &vi->ctrl_promisc, sizeof(vi->ctrl_promisc));
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_RX,
VIRTIO_NET_CTRL_RX_PROMISC, sg))
dev_warn(&dev->dev, "Failed to %sable promisc mode.\n",
- promisc ? "en" : "dis");
+ vi->ctrl_promisc ? "en" : "dis");
- sg_init_one(sg, &allmulti, sizeof(allmulti));
+ sg_init_one(sg, &vi->ctrl_allmulti, sizeof(vi->ctrl_allmulti));
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_RX,
VIRTIO_NET_CTRL_RX_ALLMULTI, sg))
dev_warn(&dev->dev, "Failed to %sable allmulti mode.\n",
- allmulti ? "en" : "dis");
+ vi->ctrl_allmulti ? "en" : "dis");
uc_count = netdev_uc_count(dev);
mc_count = netdev_mc_count(dev);
@@ -1612,7 +1614,6 @@ static int virtnet_alloc_queues(struct virtnet_info *vi)
vi->rq[i].pages = NULL;
netif_napi_add(vi->dev, &vi->rq[i].napi, virtnet_poll,
napi_weight);
- napi_hash_add(&vi->rq[i].napi);
sg_init_table(vi->rq[i].sg, ARRAY_SIZE(vi->rq[i].sg));
ewma_pkt_len_init(&vi->rq[i].mrg_avg_pkt_len);
diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c
index 899ea4288197..0cbf520cea77 100644
--- a/drivers/net/vmxnet3/vmxnet3_drv.c
+++ b/drivers/net/vmxnet3/vmxnet3_drv.c
@@ -587,6 +587,12 @@ vmxnet3_rq_alloc_rx_buf(struct vmxnet3_rx_queue *rq, u32 ring_idx,
&adapter->pdev->dev,
rbi->skb->data, rbi->len,
PCI_DMA_FROMDEVICE);
+ if (dma_mapping_error(&adapter->pdev->dev,
+ rbi->dma_addr)) {
+ dev_kfree_skb_any(rbi->skb);
+ rq->stats.rx_buf_alloc_failure++;
+ break;
+ }
} else {
/* rx buffer skipped by the device */
}
@@ -605,13 +611,18 @@ vmxnet3_rq_alloc_rx_buf(struct vmxnet3_rx_queue *rq, u32 ring_idx,
&adapter->pdev->dev,
rbi->page, 0, PAGE_SIZE,
PCI_DMA_FROMDEVICE);
+ if (dma_mapping_error(&adapter->pdev->dev,
+ rbi->dma_addr)) {
+ put_page(rbi->page);
+ rq->stats.rx_buf_alloc_failure++;
+ break;
+ }
} else {
/* rx buffers skipped by the device */
}
val = VMXNET3_RXD_BTYPE_BODY << VMXNET3_RXD_BTYPE_SHIFT;
}
- BUG_ON(rbi->dma_addr == 0);
gd->rxd.addr = cpu_to_le64(rbi->dma_addr);
gd->dword[2] = cpu_to_le32((!ring->gen << VMXNET3_RXD_GEN_SHIFT)
| val | rbi->len);
@@ -655,7 +666,7 @@ vmxnet3_append_frag(struct sk_buff *skb, struct Vmxnet3_RxCompDesc *rcd,
}
-static void
+static int
vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx,
struct vmxnet3_tx_queue *tq, struct pci_dev *pdev,
struct vmxnet3_adapter *adapter)
@@ -715,6 +726,8 @@ vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx,
tbi->dma_addr = dma_map_single(&adapter->pdev->dev,
skb->data + buf_offset, buf_size,
PCI_DMA_TODEVICE);
+ if (dma_mapping_error(&adapter->pdev->dev, tbi->dma_addr))
+ return -EFAULT;
tbi->len = buf_size;
@@ -755,6 +768,8 @@ vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx,
tbi->dma_addr = skb_frag_dma_map(&adapter->pdev->dev, frag,
buf_offset, buf_size,
DMA_TO_DEVICE);
+ if (dma_mapping_error(&adapter->pdev->dev, tbi->dma_addr))
+ return -EFAULT;
tbi->len = buf_size;
@@ -782,6 +797,8 @@ vmxnet3_map_pkt(struct sk_buff *skb, struct vmxnet3_tx_ctx *ctx,
/* set the last buf_info for the pkt */
tbi->skb = skb;
tbi->sop_idx = ctx->sop_txd - tq->tx_ring.base;
+
+ return 0;
}
@@ -1020,7 +1037,8 @@ vmxnet3_tq_xmit(struct sk_buff *skb, struct vmxnet3_tx_queue *tq,
}
/* fill tx descs related to addr & len */
- vmxnet3_map_pkt(skb, &ctx, tq, adapter->pdev, adapter);
+ if (vmxnet3_map_pkt(skb, &ctx, tq, adapter->pdev, adapter))
+ goto unlock_drop_pkt;
/* setup the EOP desc */
ctx.eop_txd->dword[3] = cpu_to_le32(VMXNET3_TXD_CQ | VMXNET3_TXD_EOP);
@@ -1231,6 +1249,7 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
struct vmxnet3_rx_buf_info *rbi;
struct sk_buff *skb, *new_skb = NULL;
struct page *new_page = NULL;
+ dma_addr_t new_dma_addr;
int num_to_alloc;
struct Vmxnet3_RxDesc *rxd;
u32 idx, ring_idx;
@@ -1287,6 +1306,21 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
skip_page_frags = true;
goto rcd_done;
}
+ new_dma_addr = dma_map_single(&adapter->pdev->dev,
+ new_skb->data, rbi->len,
+ PCI_DMA_FROMDEVICE);
+ if (dma_mapping_error(&adapter->pdev->dev,
+ new_dma_addr)) {
+ dev_kfree_skb(new_skb);
+ /* Skb allocation failed, do not handover this
+ * skb to stack. Reuse it. Drop the existing pkt
+ */
+ rq->stats.rx_buf_alloc_failure++;
+ ctx->skb = NULL;
+ rq->stats.drop_total++;
+ skip_page_frags = true;
+ goto rcd_done;
+ }
dma_unmap_single(&adapter->pdev->dev, rbi->dma_addr,
rbi->len,
@@ -1303,9 +1337,7 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
/* Immediate refill */
rbi->skb = new_skb;
- rbi->dma_addr = dma_map_single(&adapter->pdev->dev,
- rbi->skb->data, rbi->len,
- PCI_DMA_FROMDEVICE);
+ rbi->dma_addr = new_dma_addr;
rxd->addr = cpu_to_le64(rbi->dma_addr);
rxd->len = rbi->len;
if (adapter->version == 2 &&
@@ -1348,6 +1380,19 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
skip_page_frags = true;
goto rcd_done;
}
+ new_dma_addr = dma_map_page(&adapter->pdev->dev,
+ new_page,
+ 0, PAGE_SIZE,
+ PCI_DMA_FROMDEVICE);
+ if (dma_mapping_error(&adapter->pdev->dev,
+ new_dma_addr)) {
+ put_page(new_page);
+ rq->stats.rx_buf_alloc_failure++;
+ dev_kfree_skb(ctx->skb);
+ ctx->skb = NULL;
+ skip_page_frags = true;
+ goto rcd_done;
+ }
dma_unmap_page(&adapter->pdev->dev,
rbi->dma_addr, rbi->len,
@@ -1357,10 +1402,7 @@ vmxnet3_rq_rx_complete(struct vmxnet3_rx_queue *rq,
/* Immediate refill */
rbi->page = new_page;
- rbi->dma_addr = dma_map_page(&adapter->pdev->dev
- , rbi->page,
- 0, PAGE_SIZE,
- PCI_DMA_FROMDEVICE);
+ rbi->dma_addr = new_dma_addr;
rxd->addr = cpu_to_le64(rbi->dma_addr);
rxd->len = rbi->len;
}
@@ -2167,7 +2209,8 @@ vmxnet3_set_mc(struct net_device *netdev)
PCI_DMA_TODEVICE);
}
- if (new_table_pa) {
+ if (!dma_mapping_error(&adapter->pdev->dev,
+ new_table_pa)) {
new_mode |= VMXNET3_RXM_MCAST;
rxConf->mfTablePA = cpu_to_le64(new_table_pa);
} else {
@@ -3075,6 +3118,11 @@ vmxnet3_probe_device(struct pci_dev *pdev,
adapter->adapter_pa = dma_map_single(&adapter->pdev->dev, adapter,
sizeof(struct vmxnet3_adapter),
PCI_DMA_TODEVICE);
+ if (dma_mapping_error(&adapter->pdev->dev, adapter->adapter_pa)) {
+ dev_err(&pdev->dev, "Failed to map dma\n");
+ err = -EFAULT;
+ goto err_dma_map;
+ }
adapter->shared = dma_alloc_coherent(
&adapter->pdev->dev,
sizeof(struct Vmxnet3_DriverShared),
@@ -3233,6 +3281,7 @@ err_alloc_queue_desc:
err_alloc_shared:
dma_unmap_single(&adapter->pdev->dev, adapter->adapter_pa,
sizeof(struct vmxnet3_adapter), PCI_DMA_TODEVICE);
+err_dma_map:
free_netdev(netdev);
return err;
}
diff --git a/drivers/net/vmxnet3/vmxnet3_int.h b/drivers/net/vmxnet3/vmxnet3_int.h
index 4c58c83dc225..bdb8a6c0f8aa 100644
--- a/drivers/net/vmxnet3/vmxnet3_int.h
+++ b/drivers/net/vmxnet3/vmxnet3_int.h
@@ -69,10 +69,10 @@
/*
* Version numbers
*/
-#define VMXNET3_DRIVER_VERSION_STRING "1.4.4.0-k"
+#define VMXNET3_DRIVER_VERSION_STRING "1.4.5.0-k"
/* a 32-bit int, each byte encode a verion number in VMXNET3_DRIVER_VERSION */
-#define VMXNET3_DRIVER_VERSION_NUM 0x01040400
+#define VMXNET3_DRIVER_VERSION_NUM 0x01040500
#if defined(CONFIG_PCI_MSI)
/* RSS only makes sense if MSI-X is supported. */
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index 92fa3e1ea65c..66addb7a7911 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -46,17 +46,7 @@
#define vrf_master_get_rcu(dev) \
((struct net_device *)rcu_dereference(dev->rx_handler_data))
-struct slave {
- struct list_head list;
- struct net_device *dev;
-};
-
-struct slave_queue {
- struct list_head all_slaves;
-};
-
struct net_vrf {
- struct slave_queue queue;
struct rtable *rth;
struct rt6_info *rt6;
u32 tb_id;
@@ -621,42 +611,9 @@ static void cycle_netdev(struct net_device *dev)
}
}
-static struct slave *__vrf_find_slave_dev(struct slave_queue *queue,
- struct net_device *dev)
-{
- struct list_head *head = &queue->all_slaves;
- struct slave *slave;
-
- list_for_each_entry(slave, head, list) {
- if (slave->dev == dev)
- return slave;
- }
-
- return NULL;
-}
-
-/* inverse of __vrf_insert_slave */
-static void __vrf_remove_slave(struct slave_queue *queue, struct slave *slave)
-{
- list_del(&slave->list);
-}
-
-static void __vrf_insert_slave(struct slave_queue *queue, struct slave *slave)
-{
- list_add(&slave->list, &queue->all_slaves);
-}
-
static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
{
- struct slave *slave = kzalloc(sizeof(*slave), GFP_KERNEL);
- struct net_vrf *vrf = netdev_priv(dev);
- struct slave_queue *queue = &vrf->queue;
- int ret = -ENOMEM;
-
- if (!slave)
- goto out_fail;
-
- slave->dev = port_dev;
+ int ret;
/* register the packet handler for slave ports */
ret = netdev_rx_handler_register(port_dev, vrf_handle_frame, dev);
@@ -667,12 +624,11 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
goto out_fail;
}
- ret = netdev_master_upper_dev_link(port_dev, dev);
+ ret = netdev_master_upper_dev_link(port_dev, dev, NULL, NULL);
if (ret < 0)
goto out_unregister;
port_dev->priv_flags |= IFF_L3MDEV_SLAVE;
- __vrf_insert_slave(queue, slave);
cycle_netdev(port_dev);
return 0;
@@ -680,7 +636,6 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
out_unregister:
netdev_rx_handler_unregister(port_dev);
out_fail:
- kfree(slave);
return ret;
}
@@ -695,10 +650,6 @@ static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
/* inverse of do_vrf_add_slave */
static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
{
- struct net_vrf *vrf = netdev_priv(dev);
- struct slave_queue *queue = &vrf->queue;
- struct slave *slave;
-
netdev_upper_dev_unlink(port_dev, dev);
port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE;
@@ -706,12 +657,6 @@ static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
cycle_netdev(port_dev);
- slave = __vrf_find_slave_dev(queue, port_dev);
- if (slave)
- __vrf_remove_slave(queue, slave);
-
- kfree(slave);
-
return 0;
}
@@ -723,15 +668,14 @@ static int vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
static void vrf_dev_uninit(struct net_device *dev)
{
struct net_vrf *vrf = netdev_priv(dev);
- struct slave_queue *queue = &vrf->queue;
- struct list_head *head = &queue->all_slaves;
- struct slave *slave, *next;
+ struct net_device *port_dev;
+ struct list_head *iter;
vrf_rtable_destroy(vrf);
vrf_rt6_destroy(vrf);
- list_for_each_entry_safe(slave, next, head, list)
- vrf_del_slave(dev, slave->dev);
+ netdev_for_each_lower_dev(dev, port_dev, iter)
+ vrf_del_slave(dev, port_dev);
free_percpu(dev->dstats);
dev->dstats = NULL;
@@ -741,8 +685,6 @@ static int vrf_dev_init(struct net_device *dev)
{
struct net_vrf *vrf = netdev_priv(dev);
- INIT_LIST_HEAD(&vrf->queue.all_slaves);
-
dev->dstats = netdev_alloc_pcpu_stats(struct pcpu_dstats);
if (!dev->dstats)
goto out_nomem;
@@ -800,7 +742,7 @@ static struct rtable *vrf_get_rtable(const struct net_device *dev,
}
/* called under rcu_read_lock */
-static void vrf_get_saddr(struct net_device *dev, struct flowi4 *fl4)
+static int vrf_get_saddr(struct net_device *dev, struct flowi4 *fl4)
{
struct fib_result res = { .tclassid = 0 };
struct net *net = dev_net(dev);
@@ -808,9 +750,10 @@ static void vrf_get_saddr(struct net_device *dev, struct flowi4 *fl4)
u8 flags = fl4->flowi4_flags;
u8 scope = fl4->flowi4_scope;
u8 tos = RT_FL_TOS(fl4);
+ int rc;
if (unlikely(!fl4->daddr))
- return;
+ return 0;
fl4->flowi4_flags |= FLOWI_FLAG_SKIP_NH_OIF;
fl4->flowi4_iif = LOOPBACK_IFINDEX;
@@ -818,7 +761,8 @@ static void vrf_get_saddr(struct net_device *dev, struct flowi4 *fl4)
fl4->flowi4_scope = ((tos & RTO_ONLINK) ?
RT_SCOPE_LINK : RT_SCOPE_UNIVERSE);
- if (!fib_lookup(net, fl4, &res, 0)) {
+ rc = fib_lookup(net, fl4, &res, 0);
+ if (!rc) {
if (res.type == RTN_LOCAL)
fl4->saddr = res.fi->fib_prefsrc ? : fl4->daddr;
else
@@ -828,6 +772,8 @@ static void vrf_get_saddr(struct net_device *dev, struct flowi4 *fl4)
fl4->flowi4_flags = flags;
fl4->flowi4_tos = orig_tos;
fl4->flowi4_scope = scope;
+
+ return rc;
}
#if IS_ENABLED(CONFIG_IPV6)
@@ -907,7 +853,6 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[])
{
struct net_vrf *vrf = netdev_priv(dev);
- int err;
if (!data || !data[IFLA_VRF_TABLE])
return -EINVAL;
@@ -916,15 +861,7 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev,
dev->priv_flags |= IFF_L3MDEV_MASTER;
- err = register_netdevice(dev);
- if (err < 0)
- goto out_fail;
-
- return 0;
-
-out_fail:
- free_netdev(dev);
- return err;
+ return register_netdevice(dev);
}
static size_t vrf_nl_getsize(const struct net_device *dev)
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 6369a5734d4c..2d88c799d2ac 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -621,7 +621,7 @@ static void vxlan_notify_add_rx_port(struct vxlan_sock *vs)
int err;
if (sa_family == AF_INET) {
- err = udp_add_offload(&vs->udp_offloads);
+ err = udp_add_offload(net, &vs->udp_offloads);
if (err)
pr_warn("vxlan: udp_add_offload failed with status %d\n", err);
}
@@ -1158,7 +1158,6 @@ static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb,
struct pcpu_sw_netstats *stats;
union vxlan_addr saddr;
int err = 0;
- union vxlan_addr *remote_ip;
/* For flow based devices, map all packets to VNI 0 */
if (vs->flags & VXLAN_F_COLLECT_METADATA)
@@ -1169,7 +1168,6 @@ static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb,
if (!vxlan)
goto drop;
- remote_ip = &vxlan->default_dst.remote_ip;
skb_reset_mac_header(skb);
skb_scrub_packet(skb, !net_eq(vxlan->net, dev_net(vxlan->dev)));
skb->protocol = eth_type_trans(skb, vxlan->dev);
@@ -1179,8 +1177,8 @@ static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb,
if (ether_addr_equal(eth_hdr(skb)->h_source, vxlan->dev->dev_addr))
goto drop;
- /* Re-examine inner Ethernet packet */
- if (remote_ip->sa.sa_family == AF_INET) {
+ /* Get data from the outer IP header */
+ if (vxlan_get_sk_family(vs) == AF_INET) {
oip = ip_hdr(skb);
saddr.sin.sin_addr.s_addr = oip->saddr;
saddr.sa.sa_family = AF_INET;
@@ -1843,11 +1841,40 @@ static int vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *sk
skb_set_inner_protocol(skb, htons(ETH_P_TEB));
- return udp_tunnel_xmit_skb(rt, sk, skb, src, dst, tos,
- ttl, df, src_port, dst_port, xnet,
- !(vxflags & VXLAN_F_UDP_CSUM));
+ udp_tunnel_xmit_skb(rt, sk, skb, src, dst, tos, ttl, df,
+ src_port, dst_port, xnet,
+ !(vxflags & VXLAN_F_UDP_CSUM));
+ return 0;
}
+#if IS_ENABLED(CONFIG_IPV6)
+static struct dst_entry *vxlan6_get_route(struct vxlan_dev *vxlan,
+ struct sk_buff *skb, int oif,
+ const struct in6_addr *daddr,
+ struct in6_addr *saddr)
+{
+ struct dst_entry *ndst;
+ struct flowi6 fl6;
+ int err;
+
+ memset(&fl6, 0, sizeof(fl6));
+ fl6.flowi6_oif = oif;
+ fl6.daddr = *daddr;
+ fl6.saddr = vxlan->cfg.saddr.sin6.sin6_addr;
+ fl6.flowi6_mark = skb->mark;
+ fl6.flowi6_proto = IPPROTO_UDP;
+
+ err = ipv6_stub->ipv6_dst_lookup(vxlan->net,
+ vxlan->vn6_sock->sock->sk,
+ &ndst, &fl6);
+ if (err < 0)
+ return ERR_PTR(err);
+
+ *saddr = fl6.saddr;
+ return ndst;
+}
+#endif
+
/* Bypass encapsulation if the destination is local */
static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
struct vxlan_dev *dst_vxlan)
@@ -2030,26 +2057,20 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
skb = NULL;
goto rt_tx_error;
}
-
- iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
#if IS_ENABLED(CONFIG_IPV6)
} else {
struct dst_entry *ndst;
- struct flowi6 fl6;
+ struct in6_addr saddr;
u32 rt6i_flags;
if (!vxlan->vn6_sock)
goto drop;
sk = vxlan->vn6_sock->sock->sk;
- memset(&fl6, 0, sizeof(fl6));
- fl6.flowi6_oif = rdst ? rdst->remote_ifindex : 0;
- fl6.daddr = dst->sin6.sin6_addr;
- fl6.saddr = vxlan->cfg.saddr.sin6.sin6_addr;
- fl6.flowi6_mark = skb->mark;
- fl6.flowi6_proto = IPPROTO_UDP;
-
- if (ipv6_stub->ipv6_dst_lookup(vxlan->net, sk, &ndst, &fl6)) {
+ ndst = vxlan6_get_route(vxlan, skb,
+ rdst ? rdst->remote_ifindex : 0,
+ &dst->sin6.sin6_addr, &saddr);
+ if (IS_ERR(ndst)) {
netdev_dbg(dev, "no route to %pI6\n",
&dst->sin6.sin6_addr);
dev->stats.tx_carrier_errors++;
@@ -2081,7 +2102,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
}
ttl = ttl ? : ip6_dst_hoplimit(ndst);
- err = vxlan6_xmit_skb(ndst, sk, skb, dev, &fl6.saddr, &fl6.daddr,
+ err = vxlan6_xmit_skb(ndst, sk, skb, dev, &saddr, &dst->sin6.sin6_addr,
0, ttl, src_port, dst_port, htonl(vni << 8), md,
!net_eq(vxlan->net, dev_net(vxlan->dev)),
flags);
@@ -2395,9 +2416,30 @@ static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
vxlan->cfg.port_max, true);
dport = info->key.tp_dst ? : vxlan->cfg.dst_port;
- if (ip_tunnel_info_af(info) == AF_INET)
+ if (ip_tunnel_info_af(info) == AF_INET) {
+ if (!vxlan->vn4_sock)
+ return -EINVAL;
return egress_ipv4_tun_info(dev, skb, info, sport, dport);
- return -EINVAL;
+ } else {
+#if IS_ENABLED(CONFIG_IPV6)
+ struct dst_entry *ndst;
+
+ if (!vxlan->vn6_sock)
+ return -EINVAL;
+ ndst = vxlan6_get_route(vxlan, skb, 0,
+ &info->key.u.ipv6.dst,
+ &info->key.u.ipv6.src);
+ if (IS_ERR(ndst))
+ return PTR_ERR(ndst);
+ dst_release(ndst);
+
+ info->key.tp_src = sport;
+ info->key.tp_dst = dport;
+#else /* !CONFIG_IPV6 */
+ return -EPFNOSUPPORT;
+#endif
+ }
+ return 0;
}
static const struct net_device_ops vxlan_netdev_ops = {
@@ -2708,7 +2750,7 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
struct vxlan_config *conf)
{
struct vxlan_net *vn = net_generic(src_net, vxlan_net_id);
- struct vxlan_dev *vxlan = netdev_priv(dev);
+ struct vxlan_dev *vxlan = netdev_priv(dev), *tmp;
struct vxlan_rdst *dst = &vxlan->default_dst;
unsigned short needed_headroom = ETH_HLEN;
int err;
@@ -2774,9 +2816,15 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
if (!vxlan->cfg.age_interval)
vxlan->cfg.age_interval = FDB_AGE_DEFAULT;
- if (vxlan_find_vni(src_net, conf->vni, use_ipv6 ? AF_INET6 : AF_INET,
- vxlan->cfg.dst_port, vxlan->flags))
+ list_for_each_entry(tmp, &vn->vxlan_list, next) {
+ if (tmp->cfg.vni == conf->vni &&
+ (tmp->default_dst.remote_ip.sa.sa_family == AF_INET6 ||
+ tmp->cfg.saddr.sa.sa_family == AF_INET6) == use_ipv6 &&
+ tmp->cfg.dst_port == vxlan->cfg.dst_port &&
+ (tmp->flags & VXLAN_F_RCV_FLAGS) ==
+ (vxlan->flags & VXLAN_F_RCV_FLAGS))
return -EEXIST;
+ }
dev->ethtool_ops = &vxlan_ethtool_ops;
diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c
index 51f6cee8aab2..9bd4aa8083ce 100644
--- a/drivers/net/wan/hdlc.c
+++ b/drivers/net/wan/hdlc.c
@@ -266,8 +266,8 @@ struct net_device *alloc_hdlcdev(void *priv)
void unregister_hdlc_device(struct net_device *dev)
{
rtnl_lock();
- unregister_netdevice(dev);
detach_hdlc_protocol(dev);
+ unregister_netdevice(dev);
rtnl_unlock();
}
@@ -276,7 +276,11 @@ void unregister_hdlc_device(struct net_device *dev)
int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto,
size_t size)
{
- detach_hdlc_protocol(dev);
+ int err;
+
+ err = detach_hdlc_protocol(dev);
+ if (err)
+ return err;
if (!try_module_get(proto->module))
return -ENOSYS;
@@ -289,15 +293,24 @@ int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto,
}
}
dev_to_hdlc(dev)->proto = proto;
+
return 0;
}
-void detach_hdlc_protocol(struct net_device *dev)
+int detach_hdlc_protocol(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
+ int err;
if (hdlc->proto) {
+ err = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev);
+ err = notifier_to_errno(err);
+ if (err) {
+ netdev_err(dev, "Refused to change device type\n");
+ return err;
+ }
+
if (hdlc->proto->detach)
hdlc->proto->detach(dev);
module_put(hdlc->proto->module);
@@ -306,6 +319,8 @@ void detach_hdlc_protocol(struct net_device *dev)
kfree(hdlc->state);
hdlc->state = NULL;
hdlc_setup_dev(dev);
+
+ return 0;
}
diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c
index 3f20808b5ff8..a408abc25512 100644
--- a/drivers/net/wan/hdlc_cisco.c
+++ b/drivers/net/wan/hdlc_cisco.c
@@ -378,6 +378,7 @@ static int cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
spin_lock_init(&state(hdlc)->lock);
dev->header_ops = &cisco_header_ops;
dev->type = ARPHRD_CISCO;
+ call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
netif_dormant_on(dev);
return 0;
}
diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c
index e92aaf615901..b6e0cfb095d3 100644
--- a/drivers/net/wan/hdlc_fr.c
+++ b/drivers/net/wan/hdlc_fr.c
@@ -1075,11 +1075,10 @@ static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type)
used = pvc_is_used(pvc);
- if (type == ARPHRD_ETHER) {
+ if (type == ARPHRD_ETHER)
dev = alloc_netdev(0, "pvceth%d", NET_NAME_UNKNOWN,
ether_setup);
- dev->priv_flags &= ~IFF_TX_SKB_SHARING;
- } else
+ else
dev = alloc_netdev(0, "pvc%d", NET_NAME_UNKNOWN, pvc_setup);
if (!dev) {
@@ -1088,9 +1087,10 @@ static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type)
return -ENOBUFS;
}
- if (type == ARPHRD_ETHER)
+ if (type == ARPHRD_ETHER) {
+ dev->priv_flags &= ~IFF_TX_SKB_SHARING;
eth_hw_addr_random(dev);
- else {
+ } else {
*(__be16*)dev->dev_addr = htons(dlci);
dlci_to_q922(dev->broadcast, dlci);
}
@@ -1240,6 +1240,7 @@ static int fr_ioctl(struct net_device *dev, struct ifreq *ifr)
}
memcpy(&state(hdlc)->settings, &new_settings, size);
dev->type = ARPHRD_FRAD;
+ call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
return 0;
case IF_PROTO_FR_ADD_PVC:
diff --git a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c
index 0d7645581f91..47fdb87d3567 100644
--- a/drivers/net/wan/hdlc_ppp.c
+++ b/drivers/net/wan/hdlc_ppp.c
@@ -687,6 +687,7 @@ static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
dev->hard_header_len = sizeof(struct hdlc_header);
dev->header_ops = &ppp_header_ops;
dev->type = ARPHRD_PPP;
+ call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
netif_dormant_on(dev);
return 0;
}
diff --git a/drivers/net/wan/hdlc_raw.c b/drivers/net/wan/hdlc_raw.c
index 5dc153e8a29d..4feb45001aac 100644
--- a/drivers/net/wan/hdlc_raw.c
+++ b/drivers/net/wan/hdlc_raw.c
@@ -84,6 +84,7 @@ static int raw_ioctl(struct net_device *dev, struct ifreq *ifr)
return result;
memcpy(hdlc->state, &new_settings, size);
dev->type = ARPHRD_RAWHDLC;
+ call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
netif_dormant_off(dev);
return 0;
}
diff --git a/drivers/net/wan/hdlc_raw_eth.c b/drivers/net/wan/hdlc_raw_eth.c
index 3ab72b3082de..2f11836078ab 100644
--- a/drivers/net/wan/hdlc_raw_eth.c
+++ b/drivers/net/wan/hdlc_raw_eth.c
@@ -102,6 +102,7 @@ static int raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr)
ether_setup(dev);
dev->tx_queue_len = old_qlen;
eth_hw_addr_random(dev);
+ call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
netif_dormant_off(dev);
return 0;
}
diff --git a/drivers/net/wan/hdlc_x25.c b/drivers/net/wan/hdlc_x25.c
index a49aec5efd20..e867638067a6 100644
--- a/drivers/net/wan/hdlc_x25.c
+++ b/drivers/net/wan/hdlc_x25.c
@@ -213,6 +213,7 @@ static int x25_ioctl(struct net_device *dev, struct ifreq *ifr)
if ((result = attach_hdlc_protocol(dev, &proto, 0)))
return result;
dev->type = ARPHRD_X25;
+ call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
netif_dormant_off(dev);
return 0;
}
diff --git a/drivers/net/wan/wanxl.c b/drivers/net/wan/wanxl.c
index e73f13857846..a20d688d2595 100644
--- a/drivers/net/wan/wanxl.c
+++ b/drivers/net/wan/wanxl.c
@@ -586,6 +586,7 @@ static int wanxl_pci_init_one(struct pci_dev *pdev,
if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(28)) ||
pci_set_dma_mask(pdev, DMA_BIT_MASK(28))) {
pr_err("No usable DMA configuration\n");
+ pci_disable_device(pdev);
return -EIO;
}
diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c
index 5c47b011a9d7..cd39025d2abf 100644
--- a/drivers/net/wan/x25_asy.c
+++ b/drivers/net/wan/x25_asy.c
@@ -549,16 +549,12 @@ static void x25_asy_receive_buf(struct tty_struct *tty,
static int x25_asy_open_tty(struct tty_struct *tty)
{
- struct x25_asy *sl = tty->disc_data;
+ struct x25_asy *sl;
int err;
if (tty->ops->write == NULL)
return -EOPNOTSUPP;
- /* First make sure we're not already connected. */
- if (sl && sl->magic == X25_ASY_MAGIC)
- return -EEXIST;
-
/* OK. Find a free X.25 channel to use. */
sl = x25_asy_alloc();
if (sl == NULL)
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index f9f94229bf1b..8c8edaf1bba6 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -17,6 +17,22 @@ menuconfig WLAN
if WLAN
+source "drivers/net/wireless/admtek/Kconfig"
+source "drivers/net/wireless/ath/Kconfig"
+source "drivers/net/wireless/atmel/Kconfig"
+source "drivers/net/wireless/broadcom/Kconfig"
+source "drivers/net/wireless/cisco/Kconfig"
+source "drivers/net/wireless/intel/Kconfig"
+source "drivers/net/wireless/intersil/Kconfig"
+source "drivers/net/wireless/marvell/Kconfig"
+source "drivers/net/wireless/mediatek/Kconfig"
+source "drivers/net/wireless/ralink/Kconfig"
+source "drivers/net/wireless/realtek/Kconfig"
+source "drivers/net/wireless/rsi/Kconfig"
+source "drivers/net/wireless/st/Kconfig"
+source "drivers/net/wireless/ti/Kconfig"
+source "drivers/net/wireless/zydas/Kconfig"
+
config PCMCIA_RAYCS
tristate "Aviator/Raytheon 2.4GHz wireless support"
depends on PCMCIA
@@ -32,110 +48,6 @@ config PCMCIA_RAYCS
To compile this driver as a module, choose M here: the module will be
called ray_cs. If unsure, say N.
-config LIBERTAS_THINFIRM
- tristate "Marvell 8xxx Libertas WLAN driver support with thin firmware"
- depends on MAC80211
- select FW_LOADER
- ---help---
- A library for Marvell Libertas 8xxx devices using thinfirm.
-
-config LIBERTAS_THINFIRM_DEBUG
- bool "Enable full debugging output in the Libertas thin firmware module."
- depends on LIBERTAS_THINFIRM
- ---help---
- Debugging support.
-
-config LIBERTAS_THINFIRM_USB
- tristate "Marvell Libertas 8388 USB 802.11b/g cards with thin firmware"
- depends on LIBERTAS_THINFIRM && USB
- ---help---
- A driver for Marvell Libertas 8388 USB devices using thinfirm.
-
-config AIRO
- tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards"
- depends on CFG80211 && ISA_DMA_API && (PCI || BROKEN)
- select WIRELESS_EXT
- select CRYPTO
- select WEXT_SPY
- select WEXT_PRIV
- ---help---
- This is the standard Linux driver to support Cisco/Aironet ISA and
- PCI 802.11 wireless cards.
- It supports the new 802.11b cards from Cisco (Cisco 34X, Cisco 35X
- - with or without encryption) as well as card before the Cisco
- acquisition (Aironet 4500, Aironet 4800, Aironet 4800B).
-
- This driver support both the standard Linux Wireless Extensions
- and Cisco proprietary API, so both the Linux Wireless Tools and the
- Cisco Linux utilities can be used to configure the card.
-
- The driver can be compiled as a module and will be named "airo".
-
-config ATMEL
- tristate "Atmel at76c50x chipset 802.11b support"
- depends on CFG80211 && (PCI || PCMCIA)
- select WIRELESS_EXT
- select WEXT_PRIV
- select FW_LOADER
- select CRC32
- ---help---
- A driver 802.11b wireless cards based on the Atmel fast-vnet
- chips. This driver supports standard Linux wireless extensions.
-
- Many cards based on this chipset do not have flash memory
- and need their firmware loaded at start-up. If yours is
- one of these, you will need to provide a firmware image
- to be loaded into the card by the driver. The Atmel
- firmware package can be downloaded from
- <http://www.thekelleys.org.uk/atmel>
-
-config PCI_ATMEL
- tristate "Atmel at76c506 PCI cards"
- depends on ATMEL && PCI
- ---help---
- Enable support for PCI and mini-PCI cards containing the
- Atmel at76c506 chip.
-
-config PCMCIA_ATMEL
- tristate "Atmel at76c502/at76c504 PCMCIA cards"
- depends on ATMEL && PCMCIA
- select WIRELESS_EXT
- select FW_LOADER
- select CRC32
- ---help---
- Enable support for PCMCIA cards containing the
- Atmel at76c502 and at76c504 chips.
-
-config AT76C50X_USB
- tristate "Atmel at76c503/at76c505/at76c505a USB cards"
- depends on MAC80211 && USB
- select FW_LOADER
- ---help---
- Enable support for USB Wireless devices using Atmel at76c503,
- at76c505 or at76c505a chips.
-
-config AIRO_CS
- tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards"
- depends on CFG80211 && PCMCIA && (BROKEN || !M32R)
- select WIRELESS_EXT
- select WEXT_SPY
- select WEXT_PRIV
- select CRYPTO
- select CRYPTO_AES
- ---help---
- This is the standard Linux driver to support Cisco/Aironet PCMCIA
- 802.11 wireless cards. This driver is the same as the Aironet
- driver part of the Linux Pcmcia package.
- It supports the new 802.11b cards from Cisco (Cisco 34X, Cisco 35X
- - with or without encryption) as well as card before the Cisco
- acquisition (Aironet 4500, Aironet 4800, Aironet 4800B). It also
- supports OEM of Cisco such as the DELL TrueMobile 4800 and Xircom
- 802.11b cards.
-
- This driver support both the standard Linux Wireless Extensions
- and Cisco proprietary API, so both the Linux Wireless Tools and the
- Cisco Linux utilities can be used to configure the card.
-
config PCMCIA_WL3501
tristate "Planet WL3501 PCMCIA cards"
depends on CFG80211 && PCMCIA
@@ -146,44 +58,18 @@ config PCMCIA_WL3501
It has basic support for Linux wireless extensions and initial
micro support for ethtool.
-config PRISM54
- tristate 'Intersil Prism GT/Duette/Indigo PCI/Cardbus (DEPRECATED)'
- depends on PCI
- select WIRELESS_EXT
- select WEXT_SPY
- select WEXT_PRIV
- select FW_LOADER
- ---help---
- This enables support for FullMAC PCI/Cardbus prism54 devices. This
- driver is now deprecated in favor for the SoftMAC driver, p54pci.
- p54pci supports FullMAC PCI/Cardbus devices as well.
-
- For more information refer to the p54 wiki:
-
- http://wireless.kernel.org/en/users/Drivers/p54
-
- Note: You need a motherboard with DMA support to use any of these cards
-
- When built as module you get the module prism54
-
-config USB_ZD1201
- tristate "USB ZD1201 based Wireless device support"
- depends on CFG80211 && USB
- select WIRELESS_EXT
- select WEXT_PRIV
- select FW_LOADER
+config MAC80211_HWSIM
+ tristate "Simulated radio testing tool for mac80211"
+ depends on MAC80211
---help---
- Say Y if you want to use wireless LAN adapters based on the ZyDAS
- ZD1201 chip.
-
- This driver makes the adapter appear as a normal Ethernet interface,
- typically on wlan0.
-
- The zd1201 device requires external firmware to be loaded.
- This can be found at http://linux-lc100020.sourceforge.net/
+ This driver is a developer testing tool that can be used to test
+ IEEE 802.11 networking stack (mac80211) functionality. This is not
+ needed for normal wireless LAN usage and is only for testing. See
+ Documentation/networking/mac80211_hwsim for more information on how
+ to use this tool.
- To compile this driver as a module, choose M here: the
- module will be called zd1201.
+ To compile this driver as a module, choose M here: the module will be
+ called mac80211_hwsim. If unsure, say N.
config USB_NET_RNDIS_WLAN
tristate "Wireless RNDIS USB support"
@@ -214,76 +100,4 @@ config USB_NET_RNDIS_WLAN
If you choose to build a module, it'll be called rndis_wlan.
-config ADM8211
- tristate "ADMtek ADM8211 support"
- depends on MAC80211 && PCI
- select CRC32
- select EEPROM_93CX6
- ---help---
- This driver is for ADM8211A, ADM8211B, and ADM8211C based cards.
- These are PCI/mini-PCI/Cardbus 802.11b chips found in cards such as:
-
- Xterasys Cardbus XN-2411b
- Blitz NetWave Point PC
- TrendNet 221pc
- Belkin F5D6001
- SMC 2635W
- Linksys WPC11 v1
- Fiberline FL-WL-200X
- 3com Office Connect (3CRSHPW796)
- Corega WLPCIB-11
- SMC 2602W V2 EU
- D-Link DWL-520 Revision C
-
- However, some of these cards have been replaced with other chips
- like the RTL8180L (Xterasys Cardbus XN-2411b, Belkin F5D6001) or
- the Ralink RT2400 (SMC2635W) without a model number change.
-
- Thanks to Infineon-ADMtek for their support of this driver.
-
-source "drivers/net/wireless/realtek/rtl818x/Kconfig"
-
-config MAC80211_HWSIM
- tristate "Simulated radio testing tool for mac80211"
- depends on MAC80211
- ---help---
- This driver is a developer testing tool that can be used to test
- IEEE 802.11 networking stack (mac80211) functionality. This is not
- needed for normal wireless LAN usage and is only for testing. See
- Documentation/networking/mac80211_hwsim for more information on how
- to use this tool.
-
- To compile this driver as a module, choose M here: the module will be
- called mac80211_hwsim. If unsure, say N.
-
-config MWL8K
- tristate "Marvell 88W8xxx PCI/PCIe Wireless support"
- depends on MAC80211 && PCI
- ---help---
- This driver supports Marvell TOPDOG 802.11 wireless cards.
-
- To compile this driver as a module, choose M here: the module
- will be called mwl8k. If unsure, say N.
-
-source "drivers/net/wireless/ath/Kconfig"
-source "drivers/net/wireless/b43/Kconfig"
-source "drivers/net/wireless/b43legacy/Kconfig"
-source "drivers/net/wireless/brcm80211/Kconfig"
-source "drivers/net/wireless/hostap/Kconfig"
-source "drivers/net/wireless/ipw2x00/Kconfig"
-source "drivers/net/wireless/iwlwifi/Kconfig"
-source "drivers/net/wireless/iwlegacy/Kconfig"
-source "drivers/net/wireless/libertas/Kconfig"
-source "drivers/net/wireless/orinoco/Kconfig"
-source "drivers/net/wireless/p54/Kconfig"
-source "drivers/net/wireless/rt2x00/Kconfig"
-source "drivers/net/wireless/mediatek/Kconfig"
-source "drivers/net/wireless/realtek/rtlwifi/Kconfig"
-source "drivers/net/wireless/realtek/rtl8xxxu/Kconfig"
-source "drivers/net/wireless/ti/Kconfig"
-source "drivers/net/wireless/zd1211rw/Kconfig"
-source "drivers/net/wireless/mwifiex/Kconfig"
-source "drivers/net/wireless/cw1200/Kconfig"
-source "drivers/net/wireless/rsi/Kconfig"
-
endif # WLAN
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 740fdd353c5d..f00d42953fb8 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -2,27 +2,21 @@
# Makefile for the Linux Wireless network device drivers.
#
-obj-$(CONFIG_IPW2100) += ipw2x00/
-obj-$(CONFIG_IPW2200) += ipw2x00/
-
-obj-$(CONFIG_HERMES) += orinoco/
-
-obj-$(CONFIG_AIRO) += airo.o
-obj-$(CONFIG_AIRO_CS) += airo_cs.o airo.o
-
-obj-$(CONFIG_ATMEL) += atmel.o
-obj-$(CONFIG_PCI_ATMEL) += atmel_pci.o
-obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o
-
-obj-$(CONFIG_AT76C50X_USB) += at76c50x-usb.o
-
-obj-$(CONFIG_PRISM54) += prism54/
-
-obj-$(CONFIG_HOSTAP) += hostap/
-obj-$(CONFIG_B43) += b43/
-obj-$(CONFIG_B43LEGACY) += b43legacy/
-obj-$(CONFIG_ZD1211RW) += zd1211rw/
-obj-$(CONFIG_WLAN) += realtek/
+obj-$(CONFIG_WLAN_VENDOR_ADMTEK) += admtek/
+obj-$(CONFIG_WLAN_VENDOR_ATH) += ath/
+obj-$(CONFIG_WLAN_VENDOR_ATMEL) += atmel/
+obj-$(CONFIG_WLAN_VENDOR_BROADCOM) += broadcom/
+obj-$(CONFIG_WLAN_VENDOR_CISCO) += cisco/
+obj-$(CONFIG_WLAN_VENDOR_INTEL) += intel/
+obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/
+obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/
+obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/
+obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/
+obj-$(CONFIG_WLAN_VENDOR_REALTEK) += realtek/
+obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/
+obj-$(CONFIG_WLAN_VENDOR_ST) += st/
+obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
+obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
# 16-bit wireless PCMCIA client drivers
obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o
@@ -30,33 +24,4 @@ obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o
obj-$(CONFIG_USB_NET_RNDIS_WLAN) += rndis_wlan.o
-obj-$(CONFIG_USB_ZD1201) += zd1201.o
-obj-$(CONFIG_LIBERTAS) += libertas/
-
-obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/
-
-obj-$(CONFIG_ADM8211) += adm8211.o
-
-obj-$(CONFIG_MWL8K) += mwl8k.o
-
-obj-$(CONFIG_IWLWIFI) += iwlwifi/
-obj-$(CONFIG_IWLEGACY) += iwlegacy/
-obj-$(CONFIG_RT2X00) += rt2x00/
-
-obj-$(CONFIG_WL_MEDIATEK) += mediatek/
-
-obj-$(CONFIG_P54_COMMON) += p54/
-
-obj-$(CONFIG_ATH_CARDS) += ath/
-
obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o
-
-obj-$(CONFIG_WL_TI) += ti/
-
-obj-$(CONFIG_MWIFIEX) += mwifiex/
-
-obj-$(CONFIG_BRCMFMAC) += brcm80211/
-obj-$(CONFIG_BRCMSMAC) += brcm80211/
-
-obj-$(CONFIG_CW1200) += cw1200/
-obj-$(CONFIG_RSI_91X) += rsi/
diff --git a/drivers/net/wireless/admtek/Kconfig b/drivers/net/wireless/admtek/Kconfig
new file mode 100644
index 000000000000..d5a2dc728078
--- /dev/null
+++ b/drivers/net/wireless/admtek/Kconfig
@@ -0,0 +1,41 @@
+config WLAN_VENDOR_ADMTEK
+ bool "ADMtek devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_ADMTEK
+
+config ADM8211
+ tristate "ADMtek ADM8211 support"
+ depends on MAC80211 && PCI
+ select CRC32
+ select EEPROM_93CX6
+ ---help---
+ This driver is for ADM8211A, ADM8211B, and ADM8211C based cards.
+ These are PCI/mini-PCI/Cardbus 802.11b chips found in cards such as:
+
+ Xterasys Cardbus XN-2411b
+ Blitz NetWave Point PC
+ TrendNet 221pc
+ Belkin F5D6001
+ SMC 2635W
+ Linksys WPC11 v1
+ Fiberline FL-WL-200X
+ 3com Office Connect (3CRSHPW796)
+ Corega WLPCIB-11
+ SMC 2602W V2 EU
+ D-Link DWL-520 Revision C
+
+ However, some of these cards have been replaced with other chips
+ like the RTL8180L (Xterasys Cardbus XN-2411b, Belkin F5D6001) or
+ the Ralink RT2400 (SMC2635W) without a model number change.
+
+ Thanks to Infineon-ADMtek for their support of this driver.
+
+endif # WLAN_VENDOR_ADMTEK
diff --git a/drivers/net/wireless/admtek/Makefile b/drivers/net/wireless/admtek/Makefile
new file mode 100644
index 000000000000..9cca7e571cdd
--- /dev/null
+++ b/drivers/net/wireless/admtek/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_ADM8211) += adm8211.o
diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/admtek/adm8211.c
index 15f057ed41ad..15f057ed41ad 100644
--- a/drivers/net/wireless/adm8211.c
+++ b/drivers/net/wireless/admtek/adm8211.c
diff --git a/drivers/net/wireless/adm8211.h b/drivers/net/wireless/admtek/adm8211.h
index bbc10b1cde87..bbc10b1cde87 100644
--- a/drivers/net/wireless/adm8211.h
+++ b/drivers/net/wireless/admtek/adm8211.h
diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig
index ce7826009eeb..44b2470af81d 100644
--- a/drivers/net/wireless/ath/Kconfig
+++ b/drivers/net/wireless/ath/Kconfig
@@ -1,13 +1,16 @@
config ATH_COMMON
tristate
-menuconfig ATH_CARDS
- tristate "Atheros Wireless Cards"
- depends on CFG80211 && (!UML || BROKEN)
+config WLAN_VENDOR_ATH
+ bool "Atheros/Qualcomm devices"
+ default y
---help---
- This will enable the support for the Atheros wireless drivers.
- ath5k, ath9k, ath9k_htc and ar9170 drivers share some common code, this option
- enables the common ath.ko module which shares common helpers.
+ If you have a wireless card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about cards. If you say Y, you will be asked for
+ your specific card in the following questions.
For more information and documentation on this module you can visit:
@@ -17,7 +20,7 @@ menuconfig ATH_CARDS
http://wireless.kernel.org/en/users/Drivers/Atheros
-if ATH_CARDS
+if WLAN_VENDOR_ATH
config ATH_DEBUG
bool "Atheros wireless debugging"
diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig
index 72acb822bb11..03aa35f999a1 100644
--- a/drivers/net/wireless/ath/ath10k/Kconfig
+++ b/drivers/net/wireless/ath/ath10k/Kconfig
@@ -2,6 +2,7 @@ config ATH10K
tristate "Atheros 802.11ac wireless cards support"
depends on MAC80211 && HAS_DMA
select ATH_COMMON
+ select CRC32
---help---
This module adds support for wireless adapters based on
Atheros IEEE 802.11ac family of chipsets.
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index aa9bd92ac4ed..b41eb3f4ee56 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -51,6 +51,7 @@ MODULE_PARM_DESC(rawmode, "Use raw 802.11 frame datapath");
static const struct ath10k_hw_params ath10k_hw_params_list[] = {
{
.id = QCA988X_HW_2_0_VERSION,
+ .dev_id = QCA988X_2_0_DEVICE_ID,
.name = "qca988x hw2.0",
.patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR,
.uart_pin = 7,
@@ -58,6 +59,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.otp_exe_param = 0,
.channel_counters_freq_hz = 88000,
.max_probe_resp_desc_thres = 0,
+ .hw_4addr_pad = ATH10K_HW_4ADDR_PAD_AFTER,
.fw = {
.dir = QCA988X_HW_2_0_FW_DIR,
.fw = QCA988X_HW_2_0_FW_FILE,
@@ -69,12 +71,32 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
},
{
.id = QCA6174_HW_2_1_VERSION,
+ .dev_id = QCA6164_2_1_DEVICE_ID,
+ .name = "qca6164 hw2.1",
+ .patch_load_addr = QCA6174_HW_2_1_PATCH_LOAD_ADDR,
+ .uart_pin = 6,
+ .otp_exe_param = 0,
+ .channel_counters_freq_hz = 88000,
+ .max_probe_resp_desc_thres = 0,
+ .fw = {
+ .dir = QCA6174_HW_2_1_FW_DIR,
+ .fw = QCA6174_HW_2_1_FW_FILE,
+ .otp = QCA6174_HW_2_1_OTP_FILE,
+ .board = QCA6174_HW_2_1_BOARD_DATA_FILE,
+ .board_size = QCA6174_BOARD_DATA_SZ,
+ .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ,
+ },
+ },
+ {
+ .id = QCA6174_HW_2_1_VERSION,
+ .dev_id = QCA6174_2_1_DEVICE_ID,
.name = "qca6174 hw2.1",
.patch_load_addr = QCA6174_HW_2_1_PATCH_LOAD_ADDR,
.uart_pin = 6,
.otp_exe_param = 0,
.channel_counters_freq_hz = 88000,
.max_probe_resp_desc_thres = 0,
+ .hw_4addr_pad = ATH10K_HW_4ADDR_PAD_AFTER,
.fw = {
.dir = QCA6174_HW_2_1_FW_DIR,
.fw = QCA6174_HW_2_1_FW_FILE,
@@ -86,12 +108,14 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
},
{
.id = QCA6174_HW_3_0_VERSION,
+ .dev_id = QCA6174_2_1_DEVICE_ID,
.name = "qca6174 hw3.0",
.patch_load_addr = QCA6174_HW_3_0_PATCH_LOAD_ADDR,
.uart_pin = 6,
.otp_exe_param = 0,
.channel_counters_freq_hz = 88000,
.max_probe_resp_desc_thres = 0,
+ .hw_4addr_pad = ATH10K_HW_4ADDR_PAD_AFTER,
.fw = {
.dir = QCA6174_HW_3_0_FW_DIR,
.fw = QCA6174_HW_3_0_FW_FILE,
@@ -103,12 +127,14 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
},
{
.id = QCA6174_HW_3_2_VERSION,
+ .dev_id = QCA6174_2_1_DEVICE_ID,
.name = "qca6174 hw3.2",
.patch_load_addr = QCA6174_HW_3_0_PATCH_LOAD_ADDR,
.uart_pin = 6,
.otp_exe_param = 0,
.channel_counters_freq_hz = 88000,
.max_probe_resp_desc_thres = 0,
+ .hw_4addr_pad = ATH10K_HW_4ADDR_PAD_AFTER,
.fw = {
/* uses same binaries as hw3.0 */
.dir = QCA6174_HW_3_0_FW_DIR,
@@ -121,6 +147,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
},
{
.id = QCA99X0_HW_2_0_DEV_VERSION,
+ .dev_id = QCA99X0_2_0_DEVICE_ID,
.name = "qca99x0 hw2.0",
.patch_load_addr = QCA99X0_HW_2_0_PATCH_LOAD_ADDR,
.uart_pin = 7,
@@ -128,6 +155,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.continuous_frag_desc = true,
.channel_counters_freq_hz = 150000,
.max_probe_resp_desc_thres = 24,
+ .hw_4addr_pad = ATH10K_HW_4ADDR_PAD_BEFORE,
.fw = {
.dir = QCA99X0_HW_2_0_FW_DIR,
.fw = QCA99X0_HW_2_0_FW_FILE,
@@ -139,10 +167,31 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
},
{
.id = QCA9377_HW_1_0_DEV_VERSION,
+ .dev_id = QCA9377_1_0_DEVICE_ID,
.name = "qca9377 hw1.0",
.patch_load_addr = QCA9377_HW_1_0_PATCH_LOAD_ADDR,
- .uart_pin = 7,
+ .uart_pin = 6,
.otp_exe_param = 0,
+ .channel_counters_freq_hz = 88000,
+ .max_probe_resp_desc_thres = 0,
+ .fw = {
+ .dir = QCA9377_HW_1_0_FW_DIR,
+ .fw = QCA9377_HW_1_0_FW_FILE,
+ .otp = QCA9377_HW_1_0_OTP_FILE,
+ .board = QCA9377_HW_1_0_BOARD_DATA_FILE,
+ .board_size = QCA9377_BOARD_DATA_SZ,
+ .board_ext_size = QCA9377_BOARD_EXT_DATA_SZ,
+ },
+ },
+ {
+ .id = QCA9377_HW_1_1_DEV_VERSION,
+ .dev_id = QCA9377_1_0_DEVICE_ID,
+ .name = "qca9377 hw1.1",
+ .patch_load_addr = QCA9377_HW_1_0_PATCH_LOAD_ADDR,
+ .uart_pin = 6,
+ .otp_exe_param = 0,
+ .channel_counters_freq_hz = 88000,
+ .max_probe_resp_desc_thres = 0,
.fw = {
.dir = QCA9377_HW_1_0_FW_DIR,
.fw = QCA9377_HW_1_0_FW_FILE,
@@ -167,6 +216,7 @@ static const char *const ath10k_core_fw_feature_str[] = {
[ATH10K_FW_FEATURE_SUPPORTS_SKIP_CLOCK_INIT] = "skip-clock-init",
[ATH10K_FW_FEATURE_RAW_MODE_SUPPORT] = "raw-mode",
[ATH10K_FW_FEATURE_SUPPORTS_ADAPTIVE_CCA] = "adaptive-cca",
+ [ATH10K_FW_FEATURE_MFP_SUPPORT] = "mfp",
};
static unsigned int ath10k_core_get_fw_feature_str(char *buf,
@@ -843,7 +893,7 @@ out:
if (!ar->board_data || !ar->board_len) {
ath10k_err(ar,
"failed to fetch board data for %s from %s/%s\n",
- ar->hw_params.fw.dir, boardname, filename);
+ boardname, ar->hw_params.fw.dir, filename);
ret = -ENODATA;
goto err;
}
@@ -1263,7 +1313,8 @@ static int ath10k_init_hw_params(struct ath10k *ar)
for (i = 0; i < ARRAY_SIZE(ath10k_hw_params_list); i++) {
hw_params = &ath10k_hw_params_list[i];
- if (hw_params->id == ar->target_version)
+ if (hw_params->id == ar->target_version &&
+ hw_params->dev_id == ar->dev_id)
break;
}
@@ -1745,9 +1796,11 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
goto err_power_down;
}
+ ath10k_debug_print_hwfw_info(ar);
+
ret = ath10k_core_get_board_id_from_otp(ar);
if (ret && ret != -EOPNOTSUPP) {
- ath10k_err(ar, "failed to get board id from otp for qca99x0: %d\n",
+ ath10k_err(ar, "failed to get board id from otp: %d\n",
ret);
return ret;
}
@@ -1758,6 +1811,8 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
goto err_free_firmware_files;
}
+ ath10k_debug_print_board_info(ar);
+
ret = ath10k_core_init_firmware_features(ar);
if (ret) {
ath10k_err(ar, "fatal problem with firmware features: %d\n",
@@ -1780,7 +1835,7 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
goto err_unlock;
}
- ath10k_print_driver_info(ar);
+ ath10k_debug_print_boot_info(ar);
ath10k_core_stop(ar);
mutex_unlock(&ar->conf_mutex);
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 018c64f4fd25..7840cf3ef7a6 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -81,26 +81,20 @@ static inline const char *ath10k_bus_str(enum ath10k_bus bus)
return "unknown";
}
+enum ath10k_skb_flags {
+ ATH10K_SKB_F_NO_HWCRYPT = BIT(0),
+ ATH10K_SKB_F_DTIM_ZERO = BIT(1),
+ ATH10K_SKB_F_DELIVER_CAB = BIT(2),
+ ATH10K_SKB_F_MGMT = BIT(3),
+ ATH10K_SKB_F_QOS = BIT(4),
+};
+
struct ath10k_skb_cb {
dma_addr_t paddr;
+ u8 flags;
u8 eid;
- u8 vdev_id;
- enum ath10k_hw_txrx_mode txmode;
- bool is_protected;
-
- struct {
- u8 tid;
- u16 freq;
- bool is_offchan;
- bool nohwcrypt;
- struct ath10k_htt_txbuf *txbuf;
- u32 txbuf_paddr;
- } __packed htt;
-
- struct {
- bool dtim_zero;
- bool deliver_cab;
- } bcn;
+ u16 msdu_id;
+ struct ieee80211_vif *vif;
} __packed;
struct ath10k_skb_rxcb {
@@ -151,6 +145,7 @@ struct ath10k_wmi {
struct wmi_vdev_param_map *vdev_param;
struct wmi_pdev_param_map *pdev_param;
const struct wmi_ops *ops;
+ const struct wmi_peer_flags_map *peer_flags;
u32 num_mem_chunks;
u32 rx_decap_mode;
@@ -512,6 +507,9 @@ enum ath10k_fw_features {
/* Firmware Supports Adaptive CCA*/
ATH10K_FW_FEATURE_SUPPORTS_ADAPTIVE_CCA = 11,
+ /* Firmware supports management frame protection */
+ ATH10K_FW_FEATURE_MFP_SUPPORT = 12,
+
/* keep last */
ATH10K_FW_FEATURE_COUNT,
};
@@ -534,6 +532,9 @@ enum ath10k_dev_flags {
/* Disable HW crypto engine */
ATH10K_FLAG_HW_CRYPTO_DISABLED,
+
+ /* Bluetooth coexistance enabled */
+ ATH10K_FLAG_BTCOEX,
};
enum ath10k_cal_mode {
@@ -636,6 +637,7 @@ struct ath10k {
struct ath10k_hw_params {
u32 id;
+ u16 dev_id;
const char *name;
u32 patch_load_addr;
int uart_pin;
@@ -661,6 +663,9 @@ struct ath10k {
*/
u32 max_probe_resp_desc_thres;
+ /* The padding bytes's location is different on various chips */
+ enum ath10k_hw_4addr_pad hw_4addr_pad;
+
struct ath10k_hw_params_fw {
const char *dir;
const char *fw;
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index 6cc1aa3449c8..2bdf5408b0d9 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -19,6 +19,8 @@
#include <linux/debugfs.h>
#include <linux/vmalloc.h>
#include <linux/utsname.h>
+#include <linux/crc32.h>
+#include <linux/firmware.h>
#include "core.h"
#include "debug.h"
@@ -122,28 +124,51 @@ void ath10k_info(struct ath10k *ar, const char *fmt, ...)
}
EXPORT_SYMBOL(ath10k_info);
-void ath10k_print_driver_info(struct ath10k *ar)
+void ath10k_debug_print_hwfw_info(struct ath10k *ar)
{
char fw_features[128] = {};
- char boardinfo[100];
ath10k_core_get_fw_features_str(ar, fw_features, sizeof(fw_features));
- if (ar->id.bmi_ids_valid)
- scnprintf(boardinfo, sizeof(boardinfo), "bmi %d:%d",
- ar->id.bmi_chip_id, ar->id.bmi_board_id);
- else
- scnprintf(boardinfo, sizeof(boardinfo), "sub %04x:%04x",
- ar->id.subsystem_vendor, ar->id.subsystem_device);
-
- ath10k_info(ar, "%s (0x%08x, 0x%08x %s) fw %s fwapi %d bdapi %d htt-ver %d.%d wmi-op %d htt-op %d cal %s max-sta %d raw %d hwcrypto %d features %s\n",
+ ath10k_info(ar, "%s target 0x%08x chip_id 0x%08x sub %04x:%04x",
ar->hw_params.name,
ar->target_version,
ar->chip_id,
- boardinfo,
+ ar->id.subsystem_vendor, ar->id.subsystem_device);
+
+ ath10k_info(ar, "kconfig debug %d debugfs %d tracing %d dfs %d testmode %d\n",
+ config_enabled(CONFIG_ATH10K_DEBUG),
+ config_enabled(CONFIG_ATH10K_DEBUGFS),
+ config_enabled(CONFIG_ATH10K_TRACING),
+ config_enabled(CONFIG_ATH10K_DFS_CERTIFIED),
+ config_enabled(CONFIG_NL80211_TESTMODE));
+
+ ath10k_info(ar, "firmware ver %s api %d features %s crc32 %08x\n",
ar->hw->wiphy->fw_version,
ar->fw_api,
+ fw_features,
+ crc32_le(0, ar->firmware->data, ar->firmware->size));
+}
+
+void ath10k_debug_print_board_info(struct ath10k *ar)
+{
+ char boardinfo[100];
+
+ if (ar->id.bmi_ids_valid)
+ scnprintf(boardinfo, sizeof(boardinfo), "%d:%d",
+ ar->id.bmi_chip_id, ar->id.bmi_board_id);
+ else
+ scnprintf(boardinfo, sizeof(boardinfo), "N/A");
+
+ ath10k_info(ar, "board_file api %d bmi_id %s crc32 %08x",
ar->bd_api,
+ boardinfo,
+ crc32_le(0, ar->board->data, ar->board->size));
+}
+
+void ath10k_debug_print_boot_info(struct ath10k *ar)
+{
+ ath10k_info(ar, "htt-ver %d.%d wmi-op %d htt-op %d cal %s max-sta %d raw %d hwcrypto %d\n",
ar->htt.target_version_major,
ar->htt.target_version_minor,
ar->wmi.op_version,
@@ -151,14 +176,14 @@ void ath10k_print_driver_info(struct ath10k *ar)
ath10k_cal_mode_str(ar->cal_mode),
ar->max_num_stations,
test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags),
- !test_bit(ATH10K_FLAG_HW_CRYPTO_DISABLED, &ar->dev_flags),
- fw_features);
- ath10k_info(ar, "debug %d debugfs %d tracing %d dfs %d testmode %d\n",
- config_enabled(CONFIG_ATH10K_DEBUG),
- config_enabled(CONFIG_ATH10K_DEBUGFS),
- config_enabled(CONFIG_ATH10K_TRACING),
- config_enabled(CONFIG_ATH10K_DFS_CERTIFIED),
- config_enabled(CONFIG_NL80211_TESTMODE));
+ !test_bit(ATH10K_FLAG_HW_CRYPTO_DISABLED, &ar->dev_flags));
+}
+
+void ath10k_print_driver_info(struct ath10k *ar)
+{
+ ath10k_debug_print_hwfw_info(ar);
+ ath10k_debug_print_board_info(ar);
+ ath10k_debug_print_boot_info(ar);
}
EXPORT_SYMBOL(ath10k_print_driver_info);
@@ -1114,7 +1139,7 @@ static ssize_t ath10k_read_htt_max_amsdu_ampdu(struct file *file,
{
struct ath10k *ar = file->private_data;
char buf[64];
- u8 amsdu = 3, ampdu = 64;
+ u8 amsdu, ampdu;
unsigned int len;
mutex_lock(&ar->conf_mutex);
@@ -2074,6 +2099,121 @@ static const struct file_operations fops_quiet_period = {
.open = simple_open
};
+static ssize_t ath10k_write_btcoex(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ char buf[32];
+ size_t buf_size;
+ bool val;
+
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, ubuf, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+
+ if (strtobool(buf, &val) != 0)
+ return -EINVAL;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (!(test_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags) ^ val))
+ goto exit;
+
+ if (val)
+ set_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags);
+ else
+ clear_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags);
+
+ if (ar->state != ATH10K_STATE_ON)
+ goto exit;
+
+ ath10k_info(ar, "restarting firmware due to btcoex change");
+
+ queue_work(ar->workqueue, &ar->restart_work);
+
+exit:
+ mutex_unlock(&ar->conf_mutex);
+
+ return count;
+}
+
+static ssize_t ath10k_read_btcoex(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ char buf[32];
+ struct ath10k *ar = file->private_data;
+ int len = 0;
+
+ mutex_lock(&ar->conf_mutex);
+ len = scnprintf(buf, sizeof(buf) - len, "%d\n",
+ test_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags));
+ mutex_unlock(&ar->conf_mutex);
+
+ return simple_read_from_buffer(ubuf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_btcoex = {
+ .read = ath10k_read_btcoex,
+ .write = ath10k_write_btcoex,
+ .open = simple_open
+};
+
+static ssize_t ath10k_debug_fw_checksums_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ unsigned int len = 0, buf_len = 4096;
+ ssize_t ret_cnt;
+ char *buf;
+
+ buf = kzalloc(buf_len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (len > buf_len)
+ len = buf_len;
+
+ len += scnprintf(buf + len, buf_len - len,
+ "firmware-N.bin\t\t%08x\n",
+ crc32_le(0, ar->firmware->data, ar->firmware->size));
+ len += scnprintf(buf + len, buf_len - len,
+ "athwlan\t\t\t%08x\n",
+ crc32_le(0, ar->firmware_data, ar->firmware_len));
+ len += scnprintf(buf + len, buf_len - len,
+ "otp\t\t\t%08x\n",
+ crc32_le(0, ar->otp_data, ar->otp_len));
+ len += scnprintf(buf + len, buf_len - len,
+ "codeswap\t\t%08x\n",
+ crc32_le(0, ar->swap.firmware_codeswap_data,
+ ar->swap.firmware_codeswap_len));
+ len += scnprintf(buf + len, buf_len - len,
+ "board-N.bin\t\t%08x\n",
+ crc32_le(0, ar->board->data, ar->board->size));
+ len += scnprintf(buf + len, buf_len - len,
+ "board\t\t\t%08x\n",
+ crc32_le(0, ar->board_data, ar->board_len));
+
+ ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ mutex_unlock(&ar->conf_mutex);
+
+ kfree(buf);
+ return ret_cnt;
+}
+
+static const struct file_operations fops_fw_checksums = {
+ .read = ath10k_debug_fw_checksums_read,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
int ath10k_debug_create(struct ath10k *ar)
{
ar->debug.fw_crash_data = vzalloc(sizeof(*ar->debug.fw_crash_data));
@@ -2123,8 +2263,8 @@ int ath10k_debug_register(struct ath10k *ar)
debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
&fops_wmi_services);
- debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy,
- ar, &fops_simulate_fw_crash);
+ debugfs_create_file("simulate_fw_crash", S_IRUSR | S_IWUSR,
+ ar->debug.debugfs_phy, ar, &fops_simulate_fw_crash);
debugfs_create_file("fw_crash_dump", S_IRUSR, ar->debug.debugfs_phy,
ar, &fops_fw_crash_dump);
@@ -2141,15 +2281,15 @@ int ath10k_debug_register(struct ath10k *ar)
debugfs_create_file("chip_id", S_IRUSR, ar->debug.debugfs_phy,
ar, &fops_chip_id);
- debugfs_create_file("htt_stats_mask", S_IRUSR, ar->debug.debugfs_phy,
- ar, &fops_htt_stats_mask);
+ debugfs_create_file("htt_stats_mask", S_IRUSR | S_IWUSR,
+ ar->debug.debugfs_phy, ar, &fops_htt_stats_mask);
debugfs_create_file("htt_max_amsdu_ampdu", S_IRUSR | S_IWUSR,
ar->debug.debugfs_phy, ar,
&fops_htt_max_amsdu_ampdu);
- debugfs_create_file("fw_dbglog", S_IRUSR, ar->debug.debugfs_phy,
- ar, &fops_fw_dbglog);
+ debugfs_create_file("fw_dbglog", S_IRUSR | S_IWUSR,
+ ar->debug.debugfs_phy, ar, &fops_fw_dbglog);
debugfs_create_file("cal_data", S_IRUSR, ar->debug.debugfs_phy,
ar, &fops_cal_data);
@@ -2183,6 +2323,13 @@ int ath10k_debug_register(struct ath10k *ar)
debugfs_create_file("tpc_stats", S_IRUSR,
ar->debug.debugfs_phy, ar, &fops_tpc_stats);
+ if (test_bit(WMI_SERVICE_COEX_GPIO, ar->wmi.svc_map))
+ debugfs_create_file("btcoex", S_IRUGO | S_IWUSR,
+ ar->debug.debugfs_phy, ar, &fops_btcoex);
+
+ debugfs_create_file("fw_checksums", S_IRUSR,
+ ar->debug.debugfs_phy, ar, &fops_fw_checksums);
+
return 0;
}
diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h
index 7de780c4ec8d..814719cf4f22 100644
--- a/drivers/net/wireless/ath/ath10k/debug.h
+++ b/drivers/net/wireless/ath/ath10k/debug.h
@@ -63,6 +63,10 @@ extern unsigned int ath10k_debug_mask;
__printf(2, 3) void ath10k_info(struct ath10k *ar, const char *fmt, ...);
__printf(2, 3) void ath10k_err(struct ath10k *ar, const char *fmt, ...);
__printf(2, 3) void ath10k_warn(struct ath10k *ar, const char *fmt, ...);
+
+void ath10k_debug_print_hwfw_info(struct ath10k *ar);
+void ath10k_debug_print_board_info(struct ath10k *ar);
+void ath10k_debug_print_boot_info(struct ath10k *ar);
void ath10k_print_driver_info(struct ath10k *ar);
#ifdef CONFIG_ATH10K_DEBUGFS
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index 2bad50e520b5..47ca048feaf0 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -166,8 +166,13 @@ struct htt_data_tx_desc {
__le16 len;
__le16 id;
__le32 frags_paddr;
- __le16 peerid;
- __le16 freq;
+ union {
+ __le32 peerid;
+ struct {
+ __le16 peerid;
+ __le16 freq;
+ } __packed offchan_tx;
+ } __packed;
u8 prefetch[0]; /* start of frame, for FW classification engine */
} __packed;
@@ -1597,6 +1602,10 @@ void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc);
int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb);
void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id);
int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *);
-int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *);
+int ath10k_htt_tx(struct ath10k_htt *htt,
+ enum ath10k_hw_txrx_mode txmode,
+ struct sk_buff *msdu);
+void ath10k_htt_rx_pktlog_completion_handler(struct ath10k *ar,
+ struct sk_buff *skb);
#endif
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 6060dda4e910..91afa3ae414c 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -536,7 +536,7 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
size = htt->rx_ring.size * sizeof(htt->rx_ring.paddrs_ring);
- vaddr = dma_alloc_coherent(htt->ar->dev, size, &paddr, GFP_DMA);
+ vaddr = dma_alloc_coherent(htt->ar->dev, size, &paddr, GFP_KERNEL);
if (!vaddr)
goto err_dma_ring;
@@ -545,7 +545,7 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
vaddr = dma_alloc_coherent(htt->ar->dev,
sizeof(*htt->rx_ring.alloc_idx.vaddr),
- &paddr, GFP_DMA);
+ &paddr, GFP_KERNEL);
if (!vaddr)
goto err_dma_idx;
@@ -674,7 +674,7 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar,
rate &= ~RX_PPDU_START_RATE_FLAG;
sband = &ar->mac.sbands[status->band];
- status->rate_idx = ath10k_mac_hw_rate_to_idx(sband, rate);
+ status->rate_idx = ath10k_mac_hw_rate_to_idx(sband, rate, cck);
break;
case HTT_RX_HT:
case HTT_RX_HT_WITH_TXBF:
@@ -1114,7 +1114,20 @@ static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
*/
/* pull decapped header and copy SA & DA */
- hdr = (struct ieee80211_hdr *)msdu->data;
+ if ((ar->hw_params.hw_4addr_pad == ATH10K_HW_4ADDR_PAD_BEFORE) &&
+ ieee80211_has_a4(((struct ieee80211_hdr *)first_hdr)->frame_control)) {
+ /* The QCA99X0 4 address mode pad 2 bytes at the
+ * beginning of MSDU
+ */
+ hdr = (struct ieee80211_hdr *)(msdu->data + 2);
+ /* The skb length need be extended 2 as the 2 bytes at the tail
+ * be excluded due to the padding
+ */
+ skb_put(msdu, 2);
+ } else {
+ hdr = (struct ieee80211_hdr *)(msdu->data);
+ }
+
hdr_len = ath10k_htt_rx_nwifi_hdrlen(ar, hdr);
ether_addr_copy(da, ieee80211_get_DA(hdr));
ether_addr_copy(sa, ieee80211_get_SA(hdr));
@@ -2127,6 +2140,18 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
}
EXPORT_SYMBOL(ath10k_htt_t2h_msg_handler);
+void ath10k_htt_rx_pktlog_completion_handler(struct ath10k *ar,
+ struct sk_buff *skb)
+{
+ struct ath10k_pktlog_10_4_hdr *hdr =
+ (struct ath10k_pktlog_10_4_hdr *)skb->data;
+
+ trace_ath10k_htt_pktlog(ar, hdr->payload,
+ sizeof(*hdr) + __le16_to_cpu(hdr->size));
+ dev_kfree_skb_any(skb);
+}
+EXPORT_SYMBOL(ath10k_htt_rx_pktlog_completion_handler);
+
static void ath10k_htt_txrx_compl_task(unsigned long ptr)
{
struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index 16823970dbfd..b3adadb5f824 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -111,7 +111,7 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt)
size = htt->max_num_pending_tx * sizeof(struct ath10k_htt_txbuf);
htt->txbuf.vaddr = dma_alloc_coherent(ar->dev, size,
&htt->txbuf.paddr,
- GFP_DMA);
+ GFP_KERNEL);
if (!htt->txbuf.vaddr) {
ath10k_err(ar, "failed to alloc tx buffer\n");
ret = -ENOMEM;
@@ -124,7 +124,7 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt)
size = htt->max_num_pending_tx * sizeof(struct htt_msdu_ext_desc);
htt->frag_desc.vaddr = dma_alloc_coherent(ar->dev, size,
&htt->frag_desc.paddr,
- GFP_DMA);
+ GFP_KERNEL);
if (!htt->frag_desc.vaddr) {
ath10k_warn(ar, "failed to alloc fragment desc memory\n");
ret = -ENOMEM;
@@ -439,6 +439,35 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
return 0;
}
+static u8 ath10k_htt_tx_get_vdev_id(struct ath10k *ar, struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
+ struct ath10k_vif *arvif = (void *)cb->vif->drv_priv;
+
+ if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
+ return ar->scan.vdev_id;
+ else if (cb->vif)
+ return arvif->vdev_id;
+ else if (ar->monitor_started)
+ return ar->monitor_vdev_id;
+ else
+ return 0;
+}
+
+static u8 ath10k_htt_tx_get_tid(struct sk_buff *skb, bool is_eth)
+{
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
+
+ if (!is_eth && ieee80211_is_mgmt(hdr->frame_control))
+ return HTT_DATA_TX_EXT_TID_MGMT;
+ else if (cb->flags & ATH10K_SKB_F_QOS)
+ return skb->priority % IEEE80211_QOS_CTL_TID_MASK;
+ else
+ return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
+}
+
int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
{
struct ath10k *ar = htt->ar;
@@ -446,7 +475,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
struct sk_buff *txdesc = NULL;
struct htt_cmd *cmd;
struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
- u8 vdev_id = skb_cb->vdev_id;
+ u8 vdev_id = ath10k_htt_tx_get_vdev_id(ar, msdu);
int len = 0;
int msdu_id = -1;
int res;
@@ -477,6 +506,13 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
msdu_id = res;
+ if ((ieee80211_is_action(hdr->frame_control) ||
+ ieee80211_is_deauth(hdr->frame_control) ||
+ ieee80211_is_disassoc(hdr->frame_control)) &&
+ ieee80211_has_protected(hdr->frame_control)) {
+ skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
+ }
+
txdesc = ath10k_htc_alloc_skb(ar, len);
if (!txdesc) {
res = -ENOMEM;
@@ -503,8 +539,6 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
memcpy(cmd->mgmt_tx.hdr, msdu->data,
min_t(int, msdu->len, HTT_MGMT_FRM_HDR_DOWNLOAD_LEN));
- skb_cb->htt.txbuf = NULL;
-
res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc);
if (res)
goto err_unmap_msdu;
@@ -525,21 +559,27 @@ err:
return res;
}
-int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
+int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode,
+ struct sk_buff *msdu)
{
struct ath10k *ar = htt->ar;
struct device *dev = ar->dev;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(msdu);
struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu);
struct ath10k_hif_sg_item sg_items[2];
+ struct ath10k_htt_txbuf *txbuf;
struct htt_data_tx_desc_frag *frags;
- u8 vdev_id = skb_cb->vdev_id;
- u8 tid = skb_cb->htt.tid;
+ bool is_eth = (txmode == ATH10K_HW_TXRX_ETHERNET);
+ u8 vdev_id = ath10k_htt_tx_get_vdev_id(ar, msdu);
+ u8 tid = ath10k_htt_tx_get_tid(msdu, is_eth);
int prefetch_len;
int res;
u8 flags0 = 0;
u16 msdu_id, flags1 = 0;
+ u16 freq = 0;
u32 frags_paddr = 0;
+ u32 txbuf_paddr;
struct htt_msdu_ext_desc *ext_desc = NULL;
bool limit_mgmt_desc = false;
bool is_probe_resp = false;
@@ -567,17 +607,17 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
prefetch_len = min(htt->prefetch_len, msdu->len);
prefetch_len = roundup(prefetch_len, 4);
- skb_cb->htt.txbuf = &htt->txbuf.vaddr[msdu_id];
- skb_cb->htt.txbuf_paddr = htt->txbuf.paddr +
- (sizeof(struct ath10k_htt_txbuf) * msdu_id);
+ txbuf = &htt->txbuf.vaddr[msdu_id];
+ txbuf_paddr = htt->txbuf.paddr +
+ (sizeof(struct ath10k_htt_txbuf) * msdu_id);
if ((ieee80211_is_action(hdr->frame_control) ||
ieee80211_is_deauth(hdr->frame_control) ||
ieee80211_is_disassoc(hdr->frame_control)) &&
ieee80211_has_protected(hdr->frame_control)) {
skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
- } else if (!skb_cb->htt.nohwcrypt &&
- skb_cb->txmode == ATH10K_HW_TXRX_RAW &&
+ } else if (!(skb_cb->flags & ATH10K_SKB_F_NO_HWCRYPT) &&
+ txmode == ATH10K_HW_TXRX_RAW &&
ieee80211_has_protected(hdr->frame_control)) {
skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
}
@@ -590,7 +630,10 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
goto err_free_msdu_id;
}
- switch (skb_cb->txmode) {
+ if (unlikely(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN))
+ freq = ar->scan.roc_freq;
+
+ switch (txmode) {
case ATH10K_HW_TXRX_RAW:
case ATH10K_HW_TXRX_NATIVE_WIFI:
flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT;
@@ -610,16 +653,16 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
frags_paddr = htt->frag_desc.paddr +
(sizeof(struct htt_msdu_ext_desc) * msdu_id);
} else {
- frags = skb_cb->htt.txbuf->frags;
+ frags = txbuf->frags;
frags[0].dword_addr.paddr =
__cpu_to_le32(skb_cb->paddr);
frags[0].dword_addr.len = __cpu_to_le32(msdu->len);
frags[1].dword_addr.paddr = 0;
frags[1].dword_addr.len = 0;
- frags_paddr = skb_cb->htt.txbuf_paddr;
+ frags_paddr = txbuf_paddr;
}
- flags0 |= SM(skb_cb->txmode, HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
+ flags0 |= SM(txmode, HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
break;
case ATH10K_HW_TXRX_MGMT:
flags0 |= SM(ATH10K_HW_TXRX_MGMT,
@@ -646,17 +689,13 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
* avoid extra memory allocations, compress data structures and thus
* improve performance. */
- skb_cb->htt.txbuf->htc_hdr.eid = htt->eid;
- skb_cb->htt.txbuf->htc_hdr.len = __cpu_to_le16(
- sizeof(skb_cb->htt.txbuf->cmd_hdr) +
- sizeof(skb_cb->htt.txbuf->cmd_tx) +
- prefetch_len);
- skb_cb->htt.txbuf->htc_hdr.flags = 0;
-
- if (skb_cb->htt.nohwcrypt)
- flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT;
+ txbuf->htc_hdr.eid = htt->eid;
+ txbuf->htc_hdr.len = __cpu_to_le16(sizeof(txbuf->cmd_hdr) +
+ sizeof(txbuf->cmd_tx) +
+ prefetch_len);
+ txbuf->htc_hdr.flags = 0;
- if (!skb_cb->is_protected)
+ if (skb_cb->flags & ATH10K_SKB_F_NO_HWCRYPT)
flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT;
flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID);
@@ -675,20 +714,27 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
*/
flags1 |= HTT_DATA_TX_DESC_FLAGS1_POSTPONED;
- skb_cb->htt.txbuf->cmd_hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FRM;
- skb_cb->htt.txbuf->cmd_tx.flags0 = flags0;
- skb_cb->htt.txbuf->cmd_tx.flags1 = __cpu_to_le16(flags1);
- skb_cb->htt.txbuf->cmd_tx.len = __cpu_to_le16(msdu->len);
- skb_cb->htt.txbuf->cmd_tx.id = __cpu_to_le16(msdu_id);
- skb_cb->htt.txbuf->cmd_tx.frags_paddr = __cpu_to_le32(frags_paddr);
- skb_cb->htt.txbuf->cmd_tx.peerid = __cpu_to_le16(HTT_INVALID_PEERID);
- skb_cb->htt.txbuf->cmd_tx.freq = __cpu_to_le16(skb_cb->htt.freq);
+ txbuf->cmd_hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FRM;
+ txbuf->cmd_tx.flags0 = flags0;
+ txbuf->cmd_tx.flags1 = __cpu_to_le16(flags1);
+ txbuf->cmd_tx.len = __cpu_to_le16(msdu->len);
+ txbuf->cmd_tx.id = __cpu_to_le16(msdu_id);
+ txbuf->cmd_tx.frags_paddr = __cpu_to_le32(frags_paddr);
+ if (ath10k_mac_tx_frm_has_freq(ar)) {
+ txbuf->cmd_tx.offchan_tx.peerid =
+ __cpu_to_le16(HTT_INVALID_PEERID);
+ txbuf->cmd_tx.offchan_tx.freq =
+ __cpu_to_le16(freq);
+ } else {
+ txbuf->cmd_tx.peerid =
+ __cpu_to_le32(HTT_INVALID_PEERID);
+ }
trace_ath10k_htt_tx(ar, msdu_id, msdu->len, vdev_id, tid);
ath10k_dbg(ar, ATH10K_DBG_HTT,
"htt tx flags0 %hhu flags1 %hu len %d id %hu frags_paddr %08x, msdu_paddr %08x vdev %hhu tid %hhu freq %hu\n",
flags0, flags1, msdu->len, msdu_id, frags_paddr,
- (u32)skb_cb->paddr, vdev_id, tid, skb_cb->htt.freq);
+ (u32)skb_cb->paddr, vdev_id, tid, freq);
ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt tx msdu: ",
msdu->data, msdu->len);
trace_ath10k_tx_hdr(ar, msdu->data, msdu->len);
@@ -696,12 +742,12 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
sg_items[0].transfer_id = 0;
sg_items[0].transfer_context = NULL;
- sg_items[0].vaddr = &skb_cb->htt.txbuf->htc_hdr;
- sg_items[0].paddr = skb_cb->htt.txbuf_paddr +
- sizeof(skb_cb->htt.txbuf->frags);
- sg_items[0].len = sizeof(skb_cb->htt.txbuf->htc_hdr) +
- sizeof(skb_cb->htt.txbuf->cmd_hdr) +
- sizeof(skb_cb->htt.txbuf->cmd_tx);
+ sg_items[0].vaddr = &txbuf->htc_hdr;
+ sg_items[0].paddr = txbuf_paddr +
+ sizeof(txbuf->frags);
+ sg_items[0].len = sizeof(txbuf->htc_hdr) +
+ sizeof(txbuf->cmd_hdr) +
+ sizeof(txbuf->cmd_tx);
sg_items[1].transfer_id = 0;
sg_items[1].transfer_context = NULL;
diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
index 39966a05c1cc..0678831e8671 100644
--- a/drivers/net/wireless/ath/ath10k/hw.h
+++ b/drivers/net/wireless/ath/ath10k/hw.h
@@ -22,6 +22,12 @@
#define ATH10K_FW_DIR "ath10k"
+#define QCA988X_2_0_DEVICE_ID (0x003c)
+#define QCA6164_2_1_DEVICE_ID (0x0041)
+#define QCA6174_2_1_DEVICE_ID (0x003e)
+#define QCA99X0_2_0_DEVICE_ID (0x0040)
+#define QCA9377_1_0_DEVICE_ID (0x0042)
+
/* QCA988X 1.0 definitions (unsupported) */
#define QCA988X_HW_1_0_CHIP_ID_REV 0x0
@@ -42,6 +48,10 @@
#define QCA6174_HW_3_0_VERSION 0x05020000
#define QCA6174_HW_3_2_VERSION 0x05030000
+/* QCA9377 target BMI version signatures */
+#define QCA9377_HW_1_0_DEV_VERSION 0x05020000
+#define QCA9377_HW_1_1_DEV_VERSION 0x05020001
+
enum qca6174_pci_rev {
QCA6174_PCI_REV_1_1 = 0x11,
QCA6174_PCI_REV_1_3 = 0x13,
@@ -60,6 +70,11 @@ enum qca6174_chip_id_rev {
QCA6174_HW_3_2_CHIP_ID_REV = 10,
};
+enum qca9377_chip_id_rev {
+ QCA9377_HW_1_0_CHIP_ID_REV = 0x0,
+ QCA9377_HW_1_1_CHIP_ID_REV = 0x1,
+};
+
#define QCA6174_HW_2_1_FW_DIR "ath10k/QCA6174/hw2.1"
#define QCA6174_HW_2_1_FW_FILE "firmware.bin"
#define QCA6174_HW_2_1_OTP_FILE "otp.bin"
@@ -85,8 +100,6 @@ enum qca6174_chip_id_rev {
#define QCA99X0_HW_2_0_PATCH_LOAD_ADDR 0x1234
/* QCA9377 1.0 definitions */
-#define QCA9377_HW_1_0_DEV_VERSION 0x05020001
-#define QCA9377_HW_1_0_CHIP_ID_REV 0x1
#define QCA9377_HW_1_0_FW_DIR ATH10K_FW_DIR "/QCA9377/hw1.0"
#define QCA9377_HW_1_0_FW_FILE "firmware.bin"
#define QCA9377_HW_1_0_OTP_FILE "otp.bin"
@@ -273,6 +286,16 @@ struct ath10k_pktlog_hdr {
u8 payload[0];
} __packed;
+struct ath10k_pktlog_10_4_hdr {
+ __le16 flags;
+ __le16 missed_cnt;
+ __le16 log_type;
+ __le16 size;
+ __le32 timestamp;
+ __le32 type_specific_data;
+ u8 payload[0];
+} __packed;
+
enum ath10k_hw_rate_ofdm {
ATH10K_HW_RATE_OFDM_48M = 0,
ATH10K_HW_RATE_OFDM_24M,
@@ -294,6 +317,11 @@ enum ath10k_hw_rate_cck {
ATH10K_HW_RATE_CCK_SP_2M,
};
+enum ath10k_hw_4addr_pad {
+ ATH10K_HW_4ADDR_PAD_AFTER,
+ ATH10K_HW_4ADDR_PAD_BEFORE,
+};
+
/* Target specific defines for MAIN firmware */
#define TARGET_NUM_VDEVS 8
#define TARGET_NUM_PEER_AST 2
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index a7411fe90cc4..6146a293601a 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -90,7 +90,7 @@ static u8 ath10k_mac_bitrate_to_rate(int bitrate)
}
u8 ath10k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband,
- u8 hw_rate)
+ u8 hw_rate, bool cck)
{
const struct ieee80211_rate *rate;
int i;
@@ -98,6 +98,9 @@ u8 ath10k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband,
for (i = 0; i < sband->n_bitrates; i++) {
rate = &sband->bitrates[i];
+ if (ath10k_mac_bitrate_is_cck(rate->bitrate) != cck)
+ continue;
+
if (rate->hw_value == hw_rate)
return i;
else if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE &&
@@ -247,7 +250,8 @@ static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif,
lockdep_assert_held(&ar->conf_mutex);
if (WARN_ON(arvif->vif->type != NL80211_IFTYPE_AP &&
- arvif->vif->type != NL80211_IFTYPE_ADHOC))
+ arvif->vif->type != NL80211_IFTYPE_ADHOC &&
+ arvif->vif->type != NL80211_IFTYPE_MESH_POINT))
return -EINVAL;
spin_lock_bh(&ar->data_lock);
@@ -1960,7 +1964,7 @@ static void ath10k_peer_assoc_h_basic(struct ath10k *ar,
ether_addr_copy(arg->addr, sta->addr);
arg->vdev_id = arvif->vdev_id;
arg->peer_aid = aid;
- arg->peer_flags |= WMI_PEER_AUTH;
+ arg->peer_flags |= arvif->ar->wmi.peer_flags->auth;
arg->peer_listen_intval = ath10k_peer_assoc_h_listen_intval(ar, vif);
arg->peer_num_spatial_streams = 1;
arg->peer_caps = vif->bss_conf.assoc_capability;
@@ -1968,6 +1972,7 @@ static void ath10k_peer_assoc_h_basic(struct ath10k *ar,
static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
struct wmi_peer_assoc_complete_arg *arg)
{
struct ieee80211_bss_conf *info = &vif->bss_conf;
@@ -2002,12 +2007,17 @@ static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
/* FIXME: base on RSN IE/WPA IE is a correct idea? */
if (rsnie || wpaie) {
ath10k_dbg(ar, ATH10K_DBG_WMI, "%s: rsn ie found\n", __func__);
- arg->peer_flags |= WMI_PEER_NEED_PTK_4_WAY;
+ arg->peer_flags |= ar->wmi.peer_flags->need_ptk_4_way;
}
if (wpaie) {
ath10k_dbg(ar, ATH10K_DBG_WMI, "%s: wpa ie found\n", __func__);
- arg->peer_flags |= WMI_PEER_NEED_GTK_2_WAY;
+ arg->peer_flags |= ar->wmi.peer_flags->need_gtk_2_way;
+ }
+
+ if (sta->mfp &&
+ test_bit(ATH10K_FW_FEATURE_MFP_SUPPORT, ar->fw_features)) {
+ arg->peer_flags |= ar->wmi.peer_flags->pmf;
}
}
@@ -2104,7 +2114,7 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
ath10k_peer_assoc_h_vht_masked(vht_mcs_mask))
return;
- arg->peer_flags |= WMI_PEER_HT;
+ arg->peer_flags |= ar->wmi.peer_flags->ht;
arg->peer_max_mpdu = (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
ht_cap->ampdu_factor)) - 1;
@@ -2115,10 +2125,10 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
arg->peer_rate_caps |= WMI_RC_HT_FLAG;
if (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)
- arg->peer_flags |= WMI_PEER_LDPC;
+ arg->peer_flags |= ar->wmi.peer_flags->ldbc;
if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) {
- arg->peer_flags |= WMI_PEER_40MHZ;
+ arg->peer_flags |= ar->wmi.peer_flags->bw40;
arg->peer_rate_caps |= WMI_RC_CW40_FLAG;
}
@@ -2132,7 +2142,7 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
if (ht_cap->cap & IEEE80211_HT_CAP_TX_STBC) {
arg->peer_rate_caps |= WMI_RC_TX_STBC_FLAG;
- arg->peer_flags |= WMI_PEER_STBC;
+ arg->peer_flags |= ar->wmi.peer_flags->stbc;
}
if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) {
@@ -2140,7 +2150,7 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
stbc = stbc >> IEEE80211_HT_CAP_RX_STBC_SHIFT;
stbc = stbc << WMI_RC_RX_STBC_FLAG_S;
arg->peer_rate_caps |= stbc;
- arg->peer_flags |= WMI_PEER_STBC;
+ arg->peer_flags |= ar->wmi.peer_flags->stbc;
}
if (ht_cap->mcs.rx_mask[1] && ht_cap->mcs.rx_mask[2])
@@ -2321,10 +2331,10 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
if (ath10k_peer_assoc_h_vht_masked(vht_mcs_mask))
return;
- arg->peer_flags |= WMI_PEER_VHT;
+ arg->peer_flags |= ar->wmi.peer_flags->vht;
if (def.chan->band == IEEE80211_BAND_2GHZ)
- arg->peer_flags |= WMI_PEER_VHT_2G;
+ arg->peer_flags |= ar->wmi.peer_flags->vht_2g;
arg->peer_vht_caps = vht_cap->cap;
@@ -2341,7 +2351,7 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
ampdu_factor)) - 1);
if (sta->bandwidth == IEEE80211_STA_RX_BW_80)
- arg->peer_flags |= WMI_PEER_80MHZ;
+ arg->peer_flags |= ar->wmi.peer_flags->bw80;
arg->peer_vht_rates.rx_max_rate =
__le16_to_cpu(vht_cap->vht_mcs.rx_highest);
@@ -2366,27 +2376,28 @@ static void ath10k_peer_assoc_h_qos(struct ath10k *ar,
switch (arvif->vdev_type) {
case WMI_VDEV_TYPE_AP:
if (sta->wme)
- arg->peer_flags |= WMI_PEER_QOS;
+ arg->peer_flags |= arvif->ar->wmi.peer_flags->qos;
if (sta->wme && sta->uapsd_queues) {
- arg->peer_flags |= WMI_PEER_APSD;
+ arg->peer_flags |= arvif->ar->wmi.peer_flags->apsd;
arg->peer_rate_caps |= WMI_RC_UAPSD_FLAG;
}
break;
case WMI_VDEV_TYPE_STA:
if (vif->bss_conf.qos)
- arg->peer_flags |= WMI_PEER_QOS;
+ arg->peer_flags |= arvif->ar->wmi.peer_flags->qos;
break;
case WMI_VDEV_TYPE_IBSS:
if (sta->wme)
- arg->peer_flags |= WMI_PEER_QOS;
+ arg->peer_flags |= arvif->ar->wmi.peer_flags->qos;
break;
default:
break;
}
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac peer %pM qos %d\n",
- sta->addr, !!(arg->peer_flags & WMI_PEER_QOS));
+ sta->addr, !!(arg->peer_flags &
+ arvif->ar->wmi.peer_flags->qos));
}
static bool ath10k_mac_sta_has_ofdm_only(struct ieee80211_sta *sta)
@@ -2479,7 +2490,7 @@ static int ath10k_peer_assoc_prepare(struct ath10k *ar,
memset(arg, 0, sizeof(*arg));
ath10k_peer_assoc_h_basic(ar, vif, sta, arg);
- ath10k_peer_assoc_h_crypto(ar, vif, arg);
+ ath10k_peer_assoc_h_crypto(ar, vif, sta, arg);
ath10k_peer_assoc_h_rates(ar, vif, sta, arg);
ath10k_peer_assoc_h_ht(ar, vif, sta, arg);
ath10k_peer_assoc_h_vht(ar, vif, sta, arg);
@@ -3112,35 +3123,11 @@ void ath10k_mac_handle_tx_pause_vdev(struct ath10k *ar, u32 vdev_id,
spin_unlock_bh(&ar->htt.tx_lock);
}
-static u8 ath10k_tx_h_get_tid(struct ieee80211_hdr *hdr)
-{
- if (ieee80211_is_mgmt(hdr->frame_control))
- return HTT_DATA_TX_EXT_TID_MGMT;
-
- if (!ieee80211_is_data_qos(hdr->frame_control))
- return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
-
- if (!is_unicast_ether_addr(ieee80211_get_DA(hdr)))
- return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
-
- return ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK;
-}
-
-static u8 ath10k_tx_h_get_vdev_id(struct ath10k *ar, struct ieee80211_vif *vif)
-{
- if (vif)
- return ath10k_vif_to_arvif(vif)->vdev_id;
-
- if (ar->monitor_started)
- return ar->monitor_vdev_id;
-
- ath10k_warn(ar, "failed to resolve vdev id\n");
- return 0;
-}
-
static enum ath10k_hw_txrx_mode
-ath10k_tx_h_get_txmode(struct ath10k *ar, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, struct sk_buff *skb)
+ath10k_mac_tx_h_get_txmode(struct ath10k *ar,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct sk_buff *skb)
{
const struct ieee80211_hdr *hdr = (void *)skb->data;
__le16 fc = hdr->frame_control;
@@ -3190,14 +3177,22 @@ ath10k_tx_h_get_txmode(struct ath10k *ar, struct ieee80211_vif *vif,
}
static bool ath10k_tx_h_use_hwcrypto(struct ieee80211_vif *vif,
- struct sk_buff *skb) {
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct sk_buff *skb)
+{
+ const struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ const struct ieee80211_hdr *hdr = (void *)skb->data;
const u32 mask = IEEE80211_TX_INTFL_DONT_ENCRYPT |
IEEE80211_TX_CTL_INJECTED;
+
+ if (!ieee80211_has_protected(hdr->frame_control))
+ return false;
+
if ((info->flags & mask) == mask)
return false;
+
if (vif)
return !ath10k_vif_to_arvif(vif)->nohwcrypt;
+
return true;
}
@@ -3224,7 +3219,7 @@ static void ath10k_tx_h_nwifi(struct ieee80211_hw *hw, struct sk_buff *skb)
*/
hdr = (void *)skb->data;
if (ieee80211_is_qos_nullfunc(hdr->frame_control))
- cb->htt.tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST;
+ cb->flags &= ~ATH10K_SKB_F_QOS;
hdr->frame_control &= ~__cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
}
@@ -3280,7 +3275,7 @@ static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar,
}
}
-static bool ath10k_mac_need_offchan_tx_work(struct ath10k *ar)
+bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar)
{
/* FIXME: Not really sure since when the behaviour changed. At some
* point new firmware stopped requiring creation of peer entries for
@@ -3288,8 +3283,9 @@ static bool ath10k_mac_need_offchan_tx_work(struct ath10k *ar)
* tx credit replenishment and reliability). Assuming it's at least 3.4
* because that's when the `freq` was introduced to TX_FRM HTT command.
*/
- return !(ar->htt.target_version_major >= 3 &&
- ar->htt.target_version_minor >= 4);
+ return (ar->htt.target_version_major >= 3 &&
+ ar->htt.target_version_minor >= 4 &&
+ ar->htt.op_version == ATH10K_FW_HTT_OP_VERSION_TLV);
}
static int ath10k_mac_tx_wmi_mgmt(struct ath10k *ar, struct sk_buff *skb)
@@ -3314,24 +3310,24 @@ unlock:
return ret;
}
-static void ath10k_mac_tx(struct ath10k *ar, struct sk_buff *skb)
+static void ath10k_mac_tx(struct ath10k *ar, enum ath10k_hw_txrx_mode txmode,
+ struct sk_buff *skb)
{
- struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
struct ath10k_htt *htt = &ar->htt;
int ret = 0;
- switch (cb->txmode) {
+ switch (txmode) {
case ATH10K_HW_TXRX_RAW:
case ATH10K_HW_TXRX_NATIVE_WIFI:
case ATH10K_HW_TXRX_ETHERNET:
- ret = ath10k_htt_tx(htt, skb);
+ ret = ath10k_htt_tx(htt, txmode, skb);
break;
case ATH10K_HW_TXRX_MGMT:
if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
ar->fw_features))
ret = ath10k_mac_tx_wmi_mgmt(ar, skb);
else if (ar->htt.target_version_major >= 3)
- ret = ath10k_htt_tx(htt, skb);
+ ret = ath10k_htt_tx(htt, txmode, skb);
else
ret = ath10k_htt_mgmt_tx(htt, skb);
break;
@@ -3361,9 +3357,13 @@ void ath10k_offchan_tx_work(struct work_struct *work)
{
struct ath10k *ar = container_of(work, struct ath10k, offchan_tx_work);
struct ath10k_peer *peer;
+ struct ath10k_vif *arvif;
struct ieee80211_hdr *hdr;
+ struct ieee80211_vif *vif;
+ struct ieee80211_sta *sta;
struct sk_buff *skb;
const u8 *peer_addr;
+ enum ath10k_hw_txrx_mode txmode;
int vdev_id;
int ret;
unsigned long time_left;
@@ -3388,9 +3388,9 @@ void ath10k_offchan_tx_work(struct work_struct *work)
hdr = (struct ieee80211_hdr *)skb->data;
peer_addr = ieee80211_get_DA(hdr);
- vdev_id = ATH10K_SKB_CB(skb)->vdev_id;
spin_lock_bh(&ar->data_lock);
+ vdev_id = ar->scan.vdev_id;
peer = ath10k_peer_find(ar, vdev_id, peer_addr);
spin_unlock_bh(&ar->data_lock);
@@ -3413,7 +3413,22 @@ void ath10k_offchan_tx_work(struct work_struct *work)
ar->offchan_tx_skb = skb;
spin_unlock_bh(&ar->data_lock);
- ath10k_mac_tx(ar, skb);
+ /* It's safe to access vif and sta - conf_mutex guarantees that
+ * sta_state() and remove_interface() are locked exclusively
+ * out wrt to this offchannel worker.
+ */
+ arvif = ath10k_get_arvif(ar, vdev_id);
+ if (arvif) {
+ vif = arvif->vif;
+ sta = ieee80211_find_sta(vif, peer_addr);
+ } else {
+ vif = NULL;
+ sta = NULL;
+ }
+
+ txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
+
+ ath10k_mac_tx(ar, txmode, skb);
time_left =
wait_for_completion_timeout(&ar->offchan_tx_completed, 3 * HZ);
@@ -3488,6 +3503,7 @@ void __ath10k_scan_finish(struct ath10k *ar)
case ATH10K_SCAN_STARTING:
ar->scan.state = ATH10K_SCAN_IDLE;
ar->scan_channel = NULL;
+ ar->scan.roc_freq = 0;
ath10k_offchan_tx_purge(ar);
cancel_delayed_work(&ar->scan.timeout);
complete_all(&ar->scan.completed);
@@ -3631,25 +3647,32 @@ static void ath10k_tx(struct ieee80211_hw *hw,
struct sk_buff *skb)
{
struct ath10k *ar = hw->priv;
+ struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_vif *vif = info->control.vif;
struct ieee80211_sta *sta = control->sta;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- __le16 fc = hdr->frame_control;
+ enum ath10k_hw_txrx_mode txmode;
/* We should disable CCK RATE due to P2P */
if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
ath10k_dbg(ar, ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
- ATH10K_SKB_CB(skb)->htt.is_offchan = false;
- ATH10K_SKB_CB(skb)->htt.freq = 0;
- ATH10K_SKB_CB(skb)->htt.tid = ath10k_tx_h_get_tid(hdr);
- ATH10K_SKB_CB(skb)->htt.nohwcrypt = !ath10k_tx_h_use_hwcrypto(vif, skb);
- ATH10K_SKB_CB(skb)->vdev_id = ath10k_tx_h_get_vdev_id(ar, vif);
- ATH10K_SKB_CB(skb)->txmode = ath10k_tx_h_get_txmode(ar, vif, sta, skb);
- ATH10K_SKB_CB(skb)->is_protected = ieee80211_has_protected(fc);
+ txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
+
+ skb_cb->flags = 0;
+ if (!ath10k_tx_h_use_hwcrypto(vif, skb))
+ skb_cb->flags |= ATH10K_SKB_F_NO_HWCRYPT;
+
+ if (ieee80211_is_mgmt(hdr->frame_control))
+ skb_cb->flags |= ATH10K_SKB_F_MGMT;
- switch (ATH10K_SKB_CB(skb)->txmode) {
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ skb_cb->flags |= ATH10K_SKB_F_QOS;
+
+ skb_cb->vif = vif;
+
+ switch (txmode) {
case ATH10K_HW_TXRX_MGMT:
case ATH10K_HW_TXRX_NATIVE_WIFI:
ath10k_tx_h_nwifi(hw, skb);
@@ -3668,15 +3691,7 @@ static void ath10k_tx(struct ieee80211_hw *hw,
}
if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
- spin_lock_bh(&ar->data_lock);
- ATH10K_SKB_CB(skb)->htt.freq = ar->scan.roc_freq;
- ATH10K_SKB_CB(skb)->vdev_id = ar->scan.vdev_id;
- spin_unlock_bh(&ar->data_lock);
-
- if (ath10k_mac_need_offchan_tx_work(ar)) {
- ATH10K_SKB_CB(skb)->htt.freq = 0;
- ATH10K_SKB_CB(skb)->htt.is_offchan = true;
-
+ if (!ath10k_mac_tx_frm_has_freq(ar)) {
ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n",
skb);
@@ -3686,7 +3701,7 @@ static void ath10k_tx(struct ieee80211_hw *hw,
}
}
- ath10k_mac_tx(ar, skb);
+ ath10k_mac_tx(ar, txmode, skb);
}
/* Must not be called with conf_mutex held as workers can use that also. */
@@ -3845,7 +3860,8 @@ static struct ieee80211_sta_ht_cap ath10k_get_ht_cap(struct ath10k *ar)
ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8;
ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
- ht_cap.cap |= WLAN_HT_CAP_SM_PS_STATIC << IEEE80211_HT_CAP_SM_PS_SHIFT;
+ ht_cap.cap |=
+ WLAN_HT_CAP_SM_PS_DISABLED << IEEE80211_HT_CAP_SM_PS_SHIFT;
if (ar->ht_cap_info & WMI_HT_CAP_HT20_SGI)
ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
@@ -4225,7 +4241,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
static u32 get_nss_from_chainmask(u16 chain_mask)
{
- if ((chain_mask & 0x15) == 0x15)
+ if ((chain_mask & 0xf) == 0xf)
return 4;
else if ((chain_mask & 0x7) == 0x7)
return 3;
@@ -4350,7 +4366,9 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
arvif->vdev_type = WMI_VDEV_TYPE_IBSS;
break;
case NL80211_IFTYPE_MESH_POINT:
- if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
+ if (test_bit(WMI_SERVICE_MESH, ar->wmi.svc_map)) {
+ arvif->vdev_subtype = WMI_VDEV_SUBTYPE_MESH;
+ } else if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
ret = -EINVAL;
ath10k_warn(ar, "must load driver with rawmode=1 to add mesh interfaces\n");
goto err;
@@ -6907,35 +6925,39 @@ void ath10k_mac_destroy(struct ath10k *ar)
static const struct ieee80211_iface_limit ath10k_if_limits[] = {
{
- .max = 8,
- .types = BIT(NL80211_IFTYPE_STATION)
- | BIT(NL80211_IFTYPE_P2P_CLIENT)
+ .max = 8,
+ .types = BIT(NL80211_IFTYPE_STATION)
+ | BIT(NL80211_IFTYPE_P2P_CLIENT)
},
{
- .max = 3,
- .types = BIT(NL80211_IFTYPE_P2P_GO)
+ .max = 3,
+ .types = BIT(NL80211_IFTYPE_P2P_GO)
},
{
- .max = 1,
- .types = BIT(NL80211_IFTYPE_P2P_DEVICE)
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_P2P_DEVICE)
},
{
- .max = 7,
- .types = BIT(NL80211_IFTYPE_AP)
+ .max = 7,
+ .types = BIT(NL80211_IFTYPE_AP)
#ifdef CONFIG_MAC80211_MESH
- | BIT(NL80211_IFTYPE_MESH_POINT)
+ | BIT(NL80211_IFTYPE_MESH_POINT)
#endif
},
};
static const struct ieee80211_iface_limit ath10k_10x_if_limits[] = {
{
- .max = 8,
- .types = BIT(NL80211_IFTYPE_AP)
+ .max = 8,
+ .types = BIT(NL80211_IFTYPE_AP)
#ifdef CONFIG_MAC80211_MESH
- | BIT(NL80211_IFTYPE_MESH_POINT)
+ | BIT(NL80211_IFTYPE_MESH_POINT)
#endif
},
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_STATION)
+ },
};
static const struct ieee80211_iface_combination ath10k_if_comb[] = {
diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h
index e3cefe4c7cfd..53091588090d 100644
--- a/drivers/net/wireless/ath/ath10k/mac.h
+++ b/drivers/net/wireless/ath/ath10k/mac.h
@@ -66,7 +66,7 @@ void ath10k_mac_handle_tx_pause_vdev(struct ath10k *ar, u32 vdev_id,
enum wmi_tlv_tx_pause_action action);
u8 ath10k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband,
- u8 hw_rate);
+ u8 hw_rate, bool cck);
u8 ath10k_mac_bitrate_to_idx(const struct ieee80211_supported_band *sband,
u32 bitrate);
@@ -74,6 +74,7 @@ void ath10k_mac_tx_lock(struct ath10k *ar, int reason);
void ath10k_mac_tx_unlock(struct ath10k *ar, int reason);
void ath10k_mac_vif_tx_lock(struct ath10k_vif *arvif, int reason);
void ath10k_mac_vif_tx_unlock(struct ath10k_vif *arvif, int reason);
+bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar);
static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
{
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 3fca200b986c..ee925c618535 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -57,12 +57,6 @@ MODULE_PARM_DESC(reset_mode, "0: auto, 1: warm only (default: 0)");
#define ATH10K_PCI_TARGET_WAIT 3000
#define ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS 3
-#define QCA988X_2_0_DEVICE_ID (0x003c)
-#define QCA6164_2_1_DEVICE_ID (0x0041)
-#define QCA6174_2_1_DEVICE_ID (0x003e)
-#define QCA99X0_2_0_DEVICE_ID (0x0040)
-#define QCA9377_1_0_DEVICE_ID (0x0042)
-
static const struct pci_device_id ath10k_pci_id_table[] = {
{ PCI_VDEVICE(ATHEROS, QCA988X_2_0_DEVICE_ID) }, /* PCI-E QCA988X V2 */
{ PCI_VDEVICE(ATHEROS, QCA6164_2_1_DEVICE_ID) }, /* PCI-E QCA6164 V2.1 */
@@ -92,7 +86,9 @@ static const struct ath10k_pci_supp_chip ath10k_pci_supp_chips[] = {
{ QCA6174_2_1_DEVICE_ID, QCA6174_HW_3_2_CHIP_ID_REV },
{ QCA99X0_2_0_DEVICE_ID, QCA99X0_HW_2_0_CHIP_ID_REV },
+
{ QCA9377_1_0_DEVICE_ID, QCA9377_HW_1_0_CHIP_ID_REV },
+ { QCA9377_1_0_DEVICE_ID, QCA9377_HW_1_1_CHIP_ID_REV },
};
static void ath10k_pci_buffer_cleanup(struct ath10k *ar);
@@ -111,8 +107,10 @@ static void ath10k_pci_htc_tx_cb(struct ath10k_ce_pipe *ce_state);
static void ath10k_pci_htc_rx_cb(struct ath10k_ce_pipe *ce_state);
static void ath10k_pci_htt_tx_cb(struct ath10k_ce_pipe *ce_state);
static void ath10k_pci_htt_rx_cb(struct ath10k_ce_pipe *ce_state);
+static void ath10k_pci_htt_htc_rx_cb(struct ath10k_ce_pipe *ce_state);
+static void ath10k_pci_pktlog_rx_cb(struct ath10k_ce_pipe *ce_state);
-static const struct ce_attr host_ce_config_wlan[] = {
+static struct ce_attr host_ce_config_wlan[] = {
/* CE0: host->target HTC control and raw streams */
{
.flags = CE_ATTR_FLAGS,
@@ -128,7 +126,7 @@ static const struct ce_attr host_ce_config_wlan[] = {
.src_nentries = 0,
.src_sz_max = 2048,
.dest_nentries = 512,
- .recv_cb = ath10k_pci_htc_rx_cb,
+ .recv_cb = ath10k_pci_htt_htc_rx_cb,
},
/* CE2: target->host WMI */
@@ -189,6 +187,7 @@ static const struct ce_attr host_ce_config_wlan[] = {
.src_nentries = 0,
.src_sz_max = 2048,
.dest_nentries = 128,
+ .recv_cb = ath10k_pci_pktlog_rx_cb,
},
/* CE9 target autonomous qcache memcpy */
@@ -217,7 +216,7 @@ static const struct ce_attr host_ce_config_wlan[] = {
};
/* Target firmware's Copy Engine configuration. */
-static const struct ce_pipe_config target_ce_config_wlan[] = {
+static struct ce_pipe_config target_ce_config_wlan[] = {
/* CE0: host->target HTC control and raw streams */
{
.pipenum = __cpu_to_le32(0),
@@ -330,7 +329,7 @@ static const struct ce_pipe_config target_ce_config_wlan[] = {
* This table is derived from the CE_PCI TABLE, above.
* It is passed to the Target at startup for use by firmware.
*/
-static const struct service_to_pipe target_service_to_ce_map_wlan[] = {
+static struct service_to_pipe target_service_to_ce_map_wlan[] = {
{
__cpu_to_le32(ATH10K_HTC_SVC_ID_WMI_DATA_VO),
__cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */
@@ -488,6 +487,9 @@ static int ath10k_pci_force_wake(struct ath10k *ar)
unsigned long flags;
int ret = 0;
+ if (ar_pci->pci_ps)
+ return ret;
+
spin_lock_irqsave(&ar_pci->ps_lock, flags);
if (!ar_pci->ps_awake) {
@@ -1208,6 +1210,25 @@ static void ath10k_pci_htc_rx_cb(struct ath10k_ce_pipe *ce_state)
ath10k_pci_process_rx_cb(ce_state, ath10k_htc_rx_completion_handler);
}
+static void ath10k_pci_htt_htc_rx_cb(struct ath10k_ce_pipe *ce_state)
+{
+ /* CE4 polling needs to be done whenever CE pipe which transports
+ * HTT Rx (target->host) is processed.
+ */
+ ath10k_ce_per_engine_service(ce_state->ar, 4);
+
+ ath10k_pci_process_rx_cb(ce_state, ath10k_htc_rx_completion_handler);
+}
+
+/* Called by lower (CE) layer when data is received from the Target.
+ * Only 10.4 firmware uses separate CE to transfer pktlog data.
+ */
+static void ath10k_pci_pktlog_rx_cb(struct ath10k_ce_pipe *ce_state)
+{
+ ath10k_pci_process_rx_cb(ce_state,
+ ath10k_htt_rx_pktlog_completion_handler);
+}
+
/* Called by lower (CE) layer when a send to HTT Target completes. */
static void ath10k_pci_htt_tx_cb(struct ath10k_ce_pipe *ce_state)
{
@@ -2027,6 +2048,29 @@ static int ath10k_pci_init_config(struct ath10k *ar)
return 0;
}
+static void ath10k_pci_override_ce_config(struct ath10k *ar)
+{
+ struct ce_attr *attr;
+ struct ce_pipe_config *config;
+
+ /* For QCA6174 we're overriding the Copy Engine 5 configuration,
+ * since it is currently used for other feature.
+ */
+
+ /* Override Host's Copy Engine 5 configuration */
+ attr = &host_ce_config_wlan[5];
+ attr->src_sz_max = 0;
+ attr->dest_nentries = 0;
+
+ /* Override Target firmware's Copy Engine configuration */
+ config = &target_ce_config_wlan[5];
+ config->pipedir = __cpu_to_le32(PIPEDIR_OUT);
+ config->nbytes_max = __cpu_to_le32(2048);
+
+ /* Map from service/endpoint to Copy Engine */
+ target_service_to_ce_map_wlan[15].pipenum = __cpu_to_le32(1);
+}
+
static int ath10k_pci_alloc_pipes(struct ath10k *ar)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
@@ -2439,12 +2483,10 @@ static int ath10k_pci_hif_resume(struct ath10k *ar)
u32 val;
int ret = 0;
- if (ar_pci->pci_ps == 0) {
- ret = ath10k_pci_force_wake(ar);
- if (ret) {
- ath10k_err(ar, "failed to wake up target: %d\n", ret);
- return ret;
- }
+ ret = ath10k_pci_force_wake(ar);
+ if (ret) {
+ ath10k_err(ar, "failed to wake up target: %d\n", ret);
+ return ret;
}
/* Suspend/Resume resets the PCI configuration space, so we have to
@@ -2551,13 +2593,10 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg)
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
int ret;
- if (ar_pci->pci_ps == 0) {
- ret = ath10k_pci_force_wake(ar);
- if (ret) {
- ath10k_warn(ar, "failed to wake device up on irq: %d\n",
- ret);
- return IRQ_NONE;
- }
+ ret = ath10k_pci_force_wake(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to wake device up on irq: %d\n", ret);
+ return IRQ_NONE;
}
if (ar_pci->num_msi_intrs == 0) {
@@ -3020,6 +3059,9 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
goto err_core_destroy;
}
+ if (QCA_REV_6174(ar))
+ ath10k_pci_override_ce_config(ar);
+
ret = ath10k_pci_alloc_pipes(ar);
if (ret) {
ath10k_err(ar, "failed to allocate copy engine pipes: %d\n",
@@ -3027,17 +3069,15 @@ static int ath10k_pci_probe(struct pci_dev *pdev,
goto err_sleep;
}
+ ret = ath10k_pci_force_wake(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to wake up device : %d\n", ret);
+ goto err_free_pipes;
+ }
+
ath10k_pci_ce_deinit(ar);
ath10k_pci_irq_disable(ar);
- if (ar_pci->pci_ps == 0) {
- ret = ath10k_pci_force_wake(ar);
- if (ret) {
- ath10k_warn(ar, "failed to wake up device : %d\n", ret);
- goto err_free_pipes;
- }
- }
-
ret = ath10k_pci_init_irq(ar);
if (ret) {
ath10k_err(ar, "failed to init irqs: %d\n", ret);
diff --git a/drivers/net/wireless/ath/ath10k/thermal.c b/drivers/net/wireless/ath/ath10k/thermal.c
index 60fe562e3041..444b52c7e4f3 100644
--- a/drivers/net/wireless/ath/ath10k/thermal.c
+++ b/drivers/net/wireless/ath/ath10k/thermal.c
@@ -187,7 +187,7 @@ int ath10k_thermal_register(struct ath10k *ar)
/* Do not register hwmon device when temperature reading is not
* supported by firmware
*/
- if (ar->wmi.op_version != ATH10K_FW_WMI_OP_VERSION_10_2_4)
+ if (!(ar->wmi.ops->gen_pdev_get_temperature))
return 0;
/* Avoid linking error on devm_hwmon_device_register_with_groups, I
diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
index 6d1105ab4592..fbfb608e48ab 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -23,7 +23,12 @@
static void ath10k_report_offchan_tx(struct ath10k *ar, struct sk_buff *skb)
{
- if (!ATH10K_SKB_CB(skb)->htt.is_offchan)
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ if (likely(!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)))
+ return;
+
+ if (ath10k_mac_tx_frm_has_freq(ar))
return;
/* If the original wait_for_completion() timed out before
@@ -52,8 +57,6 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
struct ieee80211_tx_info *info;
struct ath10k_skb_cb *skb_cb;
struct sk_buff *msdu;
- struct ieee80211_hdr *hdr;
- __le16 fc;
bool limit_mgmt_desc = false;
ath10k_dbg(ar, ATH10K_DBG_HTT,
@@ -76,10 +79,9 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
return;
}
- hdr = (struct ieee80211_hdr *)msdu->data;
- fc = hdr->frame_control;
+ skb_cb = ATH10K_SKB_CB(msdu);
- if (unlikely(ieee80211_is_mgmt(fc)) &&
+ if (unlikely(skb_cb->flags & ATH10K_SKB_F_MGMT) &&
ar->hw_params.max_probe_resp_desc_thres)
limit_mgmt_desc = true;
@@ -89,7 +91,6 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
wake_up(&htt->empty_tx_wq);
spin_unlock_bh(&htt->tx_lock);
- skb_cb = ATH10K_SKB_CB(msdu);
dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
ath10k_report_offchan_tx(htt->ar, msdu);
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
index 6fbd17b69469..3b3a27b859f3 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
@@ -3485,6 +3485,24 @@ static const struct wmi_ops wmi_tlv_ops = {
.fw_stats_fill = ath10k_wmi_main_op_fw_stats_fill,
};
+static const struct wmi_peer_flags_map wmi_tlv_peer_flags_map = {
+ .auth = WMI_TLV_PEER_AUTH,
+ .qos = WMI_TLV_PEER_QOS,
+ .need_ptk_4_way = WMI_TLV_PEER_NEED_PTK_4_WAY,
+ .need_gtk_2_way = WMI_TLV_PEER_NEED_GTK_2_WAY,
+ .apsd = WMI_TLV_PEER_APSD,
+ .ht = WMI_TLV_PEER_HT,
+ .bw40 = WMI_TLV_PEER_40MHZ,
+ .stbc = WMI_TLV_PEER_STBC,
+ .ldbc = WMI_TLV_PEER_LDPC,
+ .dyn_mimops = WMI_TLV_PEER_DYN_MIMOPS,
+ .static_mimops = WMI_TLV_PEER_STATIC_MIMOPS,
+ .spatial_mux = WMI_TLV_PEER_SPATIAL_MUX,
+ .vht = WMI_TLV_PEER_VHT,
+ .bw80 = WMI_TLV_PEER_80MHZ,
+ .pmf = WMI_TLV_PEER_PMF,
+};
+
/************/
/* TLV init */
/************/
@@ -3495,4 +3513,5 @@ void ath10k_wmi_tlv_attach(struct ath10k *ar)
ar->wmi.vdev_param = &wmi_tlv_vdev_param_map;
ar->wmi.pdev_param = &wmi_tlv_pdev_param_map;
ar->wmi.ops = &wmi_tlv_ops;
+ ar->wmi.peer_flags = &wmi_tlv_peer_flags_map;
}
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h
index ad655c44afdb..dd678590531a 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h
@@ -527,6 +527,24 @@ enum wmi_tlv_vdev_param {
WMI_TLV_VDEV_PARAM_IBSS_PS_1RX_CHAIN_IN_ATIM_WINDOW_ENABLE,
};
+enum wmi_tlv_peer_flags {
+ WMI_TLV_PEER_AUTH = 0x00000001,
+ WMI_TLV_PEER_QOS = 0x00000002,
+ WMI_TLV_PEER_NEED_PTK_4_WAY = 0x00000004,
+ WMI_TLV_PEER_NEED_GTK_2_WAY = 0x00000010,
+ WMI_TLV_PEER_APSD = 0x00000800,
+ WMI_TLV_PEER_HT = 0x00001000,
+ WMI_TLV_PEER_40MHZ = 0x00002000,
+ WMI_TLV_PEER_STBC = 0x00008000,
+ WMI_TLV_PEER_LDPC = 0x00010000,
+ WMI_TLV_PEER_DYN_MIMOPS = 0x00020000,
+ WMI_TLV_PEER_STATIC_MIMOPS = 0x00040000,
+ WMI_TLV_PEER_SPATIAL_MUX = 0x00200000,
+ WMI_TLV_PEER_VHT = 0x02000000,
+ WMI_TLV_PEER_80MHZ = 0x04000000,
+ WMI_TLV_PEER_PMF = 0x08000000,
+};
+
enum wmi_tlv_tag {
WMI_TLV_TAG_LAST_RESERVED = 15,
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 7569db0f69b5..a7c3d299639b 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -1546,6 +1546,61 @@ static struct wmi_pdev_param_map wmi_10_4_pdev_param_map = {
.arp_dstaddr = WMI_10_4_PDEV_PARAM_ARP_DSTADDR,
};
+static const struct wmi_peer_flags_map wmi_peer_flags_map = {
+ .auth = WMI_PEER_AUTH,
+ .qos = WMI_PEER_QOS,
+ .need_ptk_4_way = WMI_PEER_NEED_PTK_4_WAY,
+ .need_gtk_2_way = WMI_PEER_NEED_GTK_2_WAY,
+ .apsd = WMI_PEER_APSD,
+ .ht = WMI_PEER_HT,
+ .bw40 = WMI_PEER_40MHZ,
+ .stbc = WMI_PEER_STBC,
+ .ldbc = WMI_PEER_LDPC,
+ .dyn_mimops = WMI_PEER_DYN_MIMOPS,
+ .static_mimops = WMI_PEER_STATIC_MIMOPS,
+ .spatial_mux = WMI_PEER_SPATIAL_MUX,
+ .vht = WMI_PEER_VHT,
+ .bw80 = WMI_PEER_80MHZ,
+ .vht_2g = WMI_PEER_VHT_2G,
+ .pmf = WMI_PEER_PMF,
+};
+
+static const struct wmi_peer_flags_map wmi_10x_peer_flags_map = {
+ .auth = WMI_10X_PEER_AUTH,
+ .qos = WMI_10X_PEER_QOS,
+ .need_ptk_4_way = WMI_10X_PEER_NEED_PTK_4_WAY,
+ .need_gtk_2_way = WMI_10X_PEER_NEED_GTK_2_WAY,
+ .apsd = WMI_10X_PEER_APSD,
+ .ht = WMI_10X_PEER_HT,
+ .bw40 = WMI_10X_PEER_40MHZ,
+ .stbc = WMI_10X_PEER_STBC,
+ .ldbc = WMI_10X_PEER_LDPC,
+ .dyn_mimops = WMI_10X_PEER_DYN_MIMOPS,
+ .static_mimops = WMI_10X_PEER_STATIC_MIMOPS,
+ .spatial_mux = WMI_10X_PEER_SPATIAL_MUX,
+ .vht = WMI_10X_PEER_VHT,
+ .bw80 = WMI_10X_PEER_80MHZ,
+};
+
+static const struct wmi_peer_flags_map wmi_10_2_peer_flags_map = {
+ .auth = WMI_10_2_PEER_AUTH,
+ .qos = WMI_10_2_PEER_QOS,
+ .need_ptk_4_way = WMI_10_2_PEER_NEED_PTK_4_WAY,
+ .need_gtk_2_way = WMI_10_2_PEER_NEED_GTK_2_WAY,
+ .apsd = WMI_10_2_PEER_APSD,
+ .ht = WMI_10_2_PEER_HT,
+ .bw40 = WMI_10_2_PEER_40MHZ,
+ .stbc = WMI_10_2_PEER_STBC,
+ .ldbc = WMI_10_2_PEER_LDPC,
+ .dyn_mimops = WMI_10_2_PEER_DYN_MIMOPS,
+ .static_mimops = WMI_10_2_PEER_STATIC_MIMOPS,
+ .spatial_mux = WMI_10_2_PEER_SPATIAL_MUX,
+ .vht = WMI_10_2_PEER_VHT,
+ .bw80 = WMI_10_2_PEER_80MHZ,
+ .vht_2g = WMI_10_2_PEER_VHT_2G,
+ .pmf = WMI_10_2_PEER_PMF,
+};
+
void ath10k_wmi_put_wmi_channel(struct wmi_channel *ch,
const struct wmi_channel_arg *arg)
{
@@ -1660,6 +1715,8 @@ static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif)
struct ath10k *ar = arvif->ar;
struct ath10k_skb_cb *cb;
struct sk_buff *bcn;
+ bool dtim_zero;
+ bool deliver_cab;
int ret;
spin_lock_bh(&ar->data_lock);
@@ -1679,12 +1736,14 @@ static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif)
arvif->beacon_state = ATH10K_BEACON_SENDING;
spin_unlock_bh(&ar->data_lock);
+ dtim_zero = !!(cb->flags & ATH10K_SKB_F_DTIM_ZERO);
+ deliver_cab = !!(cb->flags & ATH10K_SKB_F_DELIVER_CAB);
ret = ath10k_wmi_beacon_send_ref_nowait(arvif->ar,
arvif->vdev_id,
bcn->data, bcn->len,
cb->paddr,
- cb->bcn.dtim_zero,
- cb->bcn.deliver_cab);
+ dtim_zero,
+ deliver_cab);
spin_lock_bh(&ar->data_lock);
@@ -1755,16 +1814,24 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id)
static struct sk_buff *
ath10k_wmi_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu)
{
+ struct ath10k_skb_cb *cb = ATH10K_SKB_CB(msdu);
+ struct ath10k_vif *arvif = (void *)cb->vif->drv_priv;
struct wmi_mgmt_tx_cmd *cmd;
struct ieee80211_hdr *hdr;
struct sk_buff *skb;
int len;
+ u32 vdev_id;
u32 buf_len = msdu->len;
u16 fc;
hdr = (struct ieee80211_hdr *)msdu->data;
fc = le16_to_cpu(hdr->frame_control);
+ if (cb->vif)
+ vdev_id = arvif->vdev_id;
+ else
+ vdev_id = 0;
+
if (WARN_ON_ONCE(!ieee80211_is_mgmt(hdr->frame_control)))
return ERR_PTR(-EINVAL);
@@ -1786,7 +1853,7 @@ ath10k_wmi_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu)
cmd = (struct wmi_mgmt_tx_cmd *)skb->data;
- cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(msdu)->vdev_id);
+ cmd->hdr.vdev_id = __cpu_to_le32(vdev_id);
cmd->hdr.tx_rate = 0;
cmd->hdr.tx_power = 0;
cmd->hdr.buf_len = __cpu_to_le32(buf_len);
@@ -2204,22 +2271,9 @@ int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
ath10k_dbg(ar, ATH10K_DBG_MGMT,
"event mgmt rx status %08x\n", rx_status);
- if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) {
- dev_kfree_skb(skb);
- return 0;
- }
-
- if (rx_status & WMI_RX_STATUS_ERR_DECRYPT) {
- dev_kfree_skb(skb);
- return 0;
- }
-
- if (rx_status & WMI_RX_STATUS_ERR_KEY_CACHE_MISS) {
- dev_kfree_skb(skb);
- return 0;
- }
-
- if (rx_status & WMI_RX_STATUS_ERR_CRC) {
+ if ((test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) ||
+ (rx_status & (WMI_RX_STATUS_ERR_DECRYPT |
+ WMI_RX_STATUS_ERR_KEY_CACHE_MISS | WMI_RX_STATUS_ERR_CRC))) {
dev_kfree_skb(skb);
return 0;
}
@@ -3115,10 +3169,10 @@ static void ath10k_wmi_update_tim(struct ath10k *ar,
memcpy(tim->virtual_map, arvif->u.ap.tim_bitmap, pvm_len);
if (tim->dtim_count == 0) {
- ATH10K_SKB_CB(bcn)->bcn.dtim_zero = true;
+ ATH10K_SKB_CB(bcn)->flags |= ATH10K_SKB_F_DTIM_ZERO;
if (__le32_to_cpu(tim_info->tim_mcast) == 1)
- ATH10K_SKB_CB(bcn)->bcn.deliver_cab = true;
+ ATH10K_SKB_CB(bcn)->flags |= ATH10K_SKB_F_DELIVER_CAB;
}
ath10k_dbg(ar, ATH10K_DBG_MGMT, "dtim %d/%d mcast %d pvmlen %d\n",
@@ -4258,34 +4312,58 @@ void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, struct sk_buff *skb)
ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n");
}
-static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id,
- u32 num_units, u32 unit_len)
+static int ath10k_wmi_alloc_chunk(struct ath10k *ar, u32 req_id,
+ u32 num_units, u32 unit_len)
{
dma_addr_t paddr;
- u32 pool_size;
+ u32 pool_size = 0;
int idx = ar->wmi.num_mem_chunks;
+ void *vaddr = NULL;
- pool_size = num_units * round_up(unit_len, 4);
+ if (ar->wmi.num_mem_chunks == ARRAY_SIZE(ar->wmi.mem_chunks))
+ return -ENOMEM;
- if (!pool_size)
- return -EINVAL;
+ while (!vaddr && num_units) {
+ pool_size = num_units * round_up(unit_len, 4);
+ if (!pool_size)
+ return -EINVAL;
- ar->wmi.mem_chunks[idx].vaddr = dma_alloc_coherent(ar->dev,
- pool_size,
- &paddr,
- GFP_KERNEL);
- if (!ar->wmi.mem_chunks[idx].vaddr) {
- ath10k_warn(ar, "failed to allocate memory chunk\n");
- return -ENOMEM;
+ vaddr = kzalloc(pool_size, GFP_KERNEL | __GFP_NOWARN);
+ if (!vaddr)
+ num_units /= 2;
}
- memset(ar->wmi.mem_chunks[idx].vaddr, 0, pool_size);
+ if (!num_units)
+ return -ENOMEM;
+ paddr = dma_map_single(ar->dev, vaddr, pool_size, DMA_TO_DEVICE);
+ if (dma_mapping_error(ar->dev, paddr)) {
+ kfree(vaddr);
+ return -ENOMEM;
+ }
+
+ ar->wmi.mem_chunks[idx].vaddr = vaddr;
ar->wmi.mem_chunks[idx].paddr = paddr;
ar->wmi.mem_chunks[idx].len = pool_size;
ar->wmi.mem_chunks[idx].req_id = req_id;
ar->wmi.num_mem_chunks++;
+ return num_units;
+}
+
+static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id,
+ u32 num_units, u32 unit_len)
+{
+ int ret;
+
+ while (num_units) {
+ ret = ath10k_wmi_alloc_chunk(ar, req_id, num_units, unit_len);
+ if (ret < 0)
+ return ret;
+
+ num_units -= ret;
+ }
+
return 0;
}
@@ -5061,6 +5139,9 @@ static void ath10k_wmi_10_4_op_rx(struct ath10k *ar, struct sk_buff *skb)
case WMI_10_4_UPDATE_STATS_EVENTID:
ath10k_wmi_event_update_stats(ar, skb);
break;
+ case WMI_10_4_PDEV_TEMPERATURE_EVENTID:
+ ath10k_wmi_event_temperature(ar, skb);
+ break;
default:
ath10k_warn(ar, "Unknown eventid: %d\n", id);
break;
@@ -5431,8 +5512,11 @@ static struct sk_buff *ath10k_wmi_10_2_op_gen_init(struct ath10k *ar)
cmd = (struct wmi_init_cmd_10_2 *)buf->data;
features = WMI_10_2_RX_BATCH_MODE;
- if (test_bit(WMI_SERVICE_COEX_GPIO, ar->wmi.svc_map))
+
+ if (test_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags) &&
+ test_bit(WMI_SERVICE_COEX_GPIO, ar->wmi.svc_map))
features |= WMI_10_2_COEX_GPIO;
+
cmd->resource_config.feature_mask = __cpu_to_le32(features);
memcpy(&cmd->resource_config.common, &config, sizeof(config));
@@ -6328,6 +6412,16 @@ ath10k_wmi_peer_assoc_fill_10_2(struct ath10k *ar, void *buf,
cmd->info0 = __cpu_to_le32(info0);
}
+static void
+ath10k_wmi_peer_assoc_fill_10_4(struct ath10k *ar, void *buf,
+ const struct wmi_peer_assoc_complete_arg *arg)
+{
+ struct wmi_10_4_peer_assoc_complete_cmd *cmd = buf;
+
+ ath10k_wmi_peer_assoc_fill_10_2(ar, buf, arg);
+ cmd->peer_bw_rxnss_override = 0;
+}
+
static int
ath10k_wmi_peer_assoc_check_arg(const struct wmi_peer_assoc_complete_arg *arg)
{
@@ -6417,6 +6511,31 @@ ath10k_wmi_10_2_op_gen_peer_assoc(struct ath10k *ar,
}
static struct sk_buff *
+ath10k_wmi_10_4_op_gen_peer_assoc(struct ath10k *ar,
+ const struct wmi_peer_assoc_complete_arg *arg)
+{
+ size_t len = sizeof(struct wmi_10_4_peer_assoc_complete_cmd);
+ struct sk_buff *skb;
+ int ret;
+
+ ret = ath10k_wmi_peer_assoc_check_arg(arg);
+ if (ret)
+ return ERR_PTR(ret);
+
+ skb = ath10k_wmi_alloc_skb(ar, len);
+ if (!skb)
+ return ERR_PTR(-ENOMEM);
+
+ ath10k_wmi_peer_assoc_fill_10_4(ar, skb->data, arg);
+
+ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi peer assoc vdev %d addr %pM (%s)\n",
+ arg->vdev_id, arg->addr,
+ arg->peer_reassoc ? "reassociate" : "new");
+ return skb;
+}
+
+static struct sk_buff *
ath10k_wmi_10_2_op_gen_pdev_get_temperature(struct ath10k *ar)
{
struct sk_buff *skb;
@@ -7536,6 +7655,7 @@ static const struct wmi_ops wmi_10_4_ops = {
.gen_peer_delete = ath10k_wmi_op_gen_peer_delete,
.gen_peer_flush = ath10k_wmi_op_gen_peer_flush,
.gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param,
+ .gen_peer_assoc = ath10k_wmi_10_4_op_gen_peer_assoc,
.gen_set_psmode = ath10k_wmi_op_gen_set_psmode,
.gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps,
.gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps,
@@ -7555,8 +7675,8 @@ static const struct wmi_ops wmi_10_4_ops = {
.fw_stats_fill = ath10k_wmi_10_4_op_fw_stats_fill,
/* shared with 10.2 */
- .gen_peer_assoc = ath10k_wmi_10_2_op_gen_peer_assoc,
.gen_request_stats = ath10k_wmi_op_gen_request_stats,
+ .gen_pdev_get_temperature = ath10k_wmi_10_2_op_gen_pdev_get_temperature,
};
int ath10k_wmi_attach(struct ath10k *ar)
@@ -7567,30 +7687,35 @@ int ath10k_wmi_attach(struct ath10k *ar)
ar->wmi.cmd = &wmi_10_4_cmd_map;
ar->wmi.vdev_param = &wmi_10_4_vdev_param_map;
ar->wmi.pdev_param = &wmi_10_4_pdev_param_map;
+ ar->wmi.peer_flags = &wmi_10_2_peer_flags_map;
break;
case ATH10K_FW_WMI_OP_VERSION_10_2_4:
ar->wmi.cmd = &wmi_10_2_4_cmd_map;
ar->wmi.ops = &wmi_10_2_4_ops;
ar->wmi.vdev_param = &wmi_10_2_4_vdev_param_map;
ar->wmi.pdev_param = &wmi_10_2_4_pdev_param_map;
+ ar->wmi.peer_flags = &wmi_10_2_peer_flags_map;
break;
case ATH10K_FW_WMI_OP_VERSION_10_2:
ar->wmi.cmd = &wmi_10_2_cmd_map;
ar->wmi.ops = &wmi_10_2_ops;
ar->wmi.vdev_param = &wmi_10x_vdev_param_map;
ar->wmi.pdev_param = &wmi_10x_pdev_param_map;
+ ar->wmi.peer_flags = &wmi_10_2_peer_flags_map;
break;
case ATH10K_FW_WMI_OP_VERSION_10_1:
ar->wmi.cmd = &wmi_10x_cmd_map;
ar->wmi.ops = &wmi_10_1_ops;
ar->wmi.vdev_param = &wmi_10x_vdev_param_map;
ar->wmi.pdev_param = &wmi_10x_pdev_param_map;
+ ar->wmi.peer_flags = &wmi_10x_peer_flags_map;
break;
case ATH10K_FW_WMI_OP_VERSION_MAIN:
ar->wmi.cmd = &wmi_cmd_map;
ar->wmi.ops = &wmi_ops;
ar->wmi.vdev_param = &wmi_vdev_param_map;
ar->wmi.pdev_param = &wmi_pdev_param_map;
+ ar->wmi.peer_flags = &wmi_peer_flags_map;
break;
case ATH10K_FW_WMI_OP_VERSION_TLV:
ath10k_wmi_tlv_attach(ar);
@@ -7616,10 +7741,11 @@ void ath10k_wmi_free_host_mem(struct ath10k *ar)
/* free the host memory chunks requested by firmware */
for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
- dma_free_coherent(ar->dev,
- ar->wmi.mem_chunks[i].len,
- ar->wmi.mem_chunks[i].vaddr,
- ar->wmi.mem_chunks[i].paddr);
+ dma_unmap_single(ar->dev,
+ ar->wmi.mem_chunks[i].paddr,
+ ar->wmi.mem_chunks[i].len,
+ DMA_TO_DEVICE);
+ kfree(ar->wmi.mem_chunks[i].vaddr);
}
ar->wmi.num_mem_chunks = 0;
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 72a4ef709577..d85ad7855d20 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -175,6 +175,8 @@ enum wmi_service {
WMI_SERVICE_AUX_SPECTRAL_INTF,
WMI_SERVICE_AUX_CHAN_LOAD_INTF,
WMI_SERVICE_BSS_CHANNEL_INFO_64,
+ WMI_SERVICE_EXT_RES_CFG_SUPPORT,
+ WMI_SERVICE_MESH,
/* keep last */
WMI_SERVICE_MAX,
@@ -206,6 +208,11 @@ enum wmi_10x_service {
WMI_10X_SERVICE_SMART_ANTENNA_HW_SUPPORT,
WMI_10X_SERVICE_ATF,
WMI_10X_SERVICE_COEX_GPIO,
+ WMI_10X_SERVICE_AUX_SPECTRAL_INTF,
+ WMI_10X_SERVICE_AUX_CHAN_LOAD_INTF,
+ WMI_10X_SERVICE_BSS_CHANNEL_INFO_64,
+ WMI_10X_SERVICE_MESH,
+ WMI_10X_SERVICE_EXT_RES_CFG_SUPPORT,
};
enum wmi_main_service {
@@ -286,6 +293,8 @@ enum wmi_10_4_service {
WMI_10_4_SERVICE_AUX_SPECTRAL_INTF,
WMI_10_4_SERVICE_AUX_CHAN_LOAD_INTF,
WMI_10_4_SERVICE_BSS_CHANNEL_INFO_64,
+ WMI_10_4_SERVICE_EXT_RES_CFG_SUPPORT,
+ WMI_10_4_SERVICE_MESH,
};
static inline char *wmi_service_name(int service_id)
@@ -375,6 +384,8 @@ static inline char *wmi_service_name(int service_id)
SVCSTR(WMI_SERVICE_AUX_SPECTRAL_INTF);
SVCSTR(WMI_SERVICE_AUX_CHAN_LOAD_INTF);
SVCSTR(WMI_SERVICE_BSS_CHANNEL_INFO_64);
+ SVCSTR(WMI_SERVICE_EXT_RES_CFG_SUPPORT);
+ SVCSTR(WMI_SERVICE_MESH);
default:
return NULL;
}
@@ -442,6 +453,16 @@ static inline void wmi_10x_svc_map(const __le32 *in, unsigned long *out,
WMI_SERVICE_ATF, len);
SVCMAP(WMI_10X_SERVICE_COEX_GPIO,
WMI_SERVICE_COEX_GPIO, len);
+ SVCMAP(WMI_10X_SERVICE_AUX_SPECTRAL_INTF,
+ WMI_SERVICE_AUX_SPECTRAL_INTF, len);
+ SVCMAP(WMI_10X_SERVICE_AUX_CHAN_LOAD_INTF,
+ WMI_SERVICE_AUX_CHAN_LOAD_INTF, len);
+ SVCMAP(WMI_10X_SERVICE_BSS_CHANNEL_INFO_64,
+ WMI_SERVICE_BSS_CHANNEL_INFO_64, len);
+ SVCMAP(WMI_10X_SERVICE_MESH,
+ WMI_SERVICE_MESH, len);
+ SVCMAP(WMI_10X_SERVICE_EXT_RES_CFG_SUPPORT,
+ WMI_SERVICE_EXT_RES_CFG_SUPPORT, len);
}
static inline void wmi_main_svc_map(const __le32 *in, unsigned long *out,
@@ -600,6 +621,10 @@ static inline void wmi_10_4_svc_map(const __le32 *in, unsigned long *out,
WMI_SERVICE_AUX_CHAN_LOAD_INTF, len);
SVCMAP(WMI_10_4_SERVICE_BSS_CHANNEL_INFO_64,
WMI_SERVICE_BSS_CHANNEL_INFO_64, len);
+ SVCMAP(WMI_10_4_SERVICE_EXT_RES_CFG_SUPPORT,
+ WMI_SERVICE_EXT_RES_CFG_SUPPORT, len);
+ SVCMAP(WMI_10_4_SERVICE_MESH,
+ WMI_SERVICE_MESH, len);
}
#undef SVCMAP
@@ -1576,6 +1601,9 @@ enum wmi_10_4_cmd_id {
WMI_10_4_MU_CAL_START_CMDID,
WMI_10_4_SET_CCA_PARAMS_CMDID,
WMI_10_4_PDEV_BSS_CHAN_INFO_REQUEST_CMDID,
+ WMI_10_4_EXT_RESOURCE_CFG_CMDID,
+ WMI_10_4_VDEV_SET_IE_CMDID,
+ WMI_10_4_SET_LTEU_CONFIG_CMDID,
WMI_10_4_PDEV_UTF_CMDID = WMI_10_4_END_CMDID - 1,
};
@@ -1638,6 +1666,7 @@ enum wmi_10_4_event_id {
WMI_10_4_PDEV_TEMPERATURE_EVENTID,
WMI_10_4_PDEV_NFCAL_POWER_ALL_CHANNELS_EVENTID,
WMI_10_4_PDEV_BSS_CHAN_INFO_EVENTID,
+ WMI_10_4_MU_REPORT_EVENTID,
WMI_10_4_PDEV_UTF_EVENTID = WMI_10_4_END_EVENTID - 1,
};
@@ -3650,6 +3679,12 @@ enum wmi_10_4_pdev_param {
WMI_10_4_PDEV_PARAM_WAPI_MBSSID_OFFSET,
WMI_10_4_PDEV_PARAM_ARP_SRCADDR,
WMI_10_4_PDEV_PARAM_ARP_DSTADDR,
+ WMI_10_4_PDEV_PARAM_TXPOWER_DECR_DB,
+ WMI_10_4_PDEV_PARAM_RX_BATCHMODE,
+ WMI_10_4_PDEV_PARAM_PACKET_AGGR_DELAY,
+ WMI_10_4_PDEV_PARAM_ATF_OBSS_NOISE_SCH,
+ WMI_10_4_PDEV_PARAM_ATF_OBSS_NOISE_SCALING_FACTOR,
+ WMI_10_4_PDEV_PARAM_CUST_TXPOWER_SCALE,
};
struct wmi_pdev_set_param_cmd {
@@ -4239,6 +4274,8 @@ enum wmi_vdev_subtype {
WMI_VDEV_SUBTYPE_P2P_DEVICE = 1,
WMI_VDEV_SUBTYPE_P2P_CLIENT = 2,
WMI_VDEV_SUBTYPE_P2P_GO = 3,
+ WMI_VDEV_SUBTYPE_PROXY_STA = 4,
+ WMI_VDEV_SUBTYPE_MESH = 5,
};
/* values for vdev_subtype */
@@ -5641,21 +5678,79 @@ struct wmi_peer_set_q_empty_callback_cmd {
__le32 callback_enable;
} __packed;
-#define WMI_PEER_AUTH 0x00000001
-#define WMI_PEER_QOS 0x00000002
-#define WMI_PEER_NEED_PTK_4_WAY 0x00000004
-#define WMI_PEER_NEED_GTK_2_WAY 0x00000010
-#define WMI_PEER_APSD 0x00000800
-#define WMI_PEER_HT 0x00001000
-#define WMI_PEER_40MHZ 0x00002000
-#define WMI_PEER_STBC 0x00008000
-#define WMI_PEER_LDPC 0x00010000
-#define WMI_PEER_DYN_MIMOPS 0x00020000
-#define WMI_PEER_STATIC_MIMOPS 0x00040000
-#define WMI_PEER_SPATIAL_MUX 0x00200000
-#define WMI_PEER_VHT 0x02000000
-#define WMI_PEER_80MHZ 0x04000000
-#define WMI_PEER_VHT_2G 0x08000000
+struct wmi_peer_flags_map {
+ u32 auth;
+ u32 qos;
+ u32 need_ptk_4_way;
+ u32 need_gtk_2_way;
+ u32 apsd;
+ u32 ht;
+ u32 bw40;
+ u32 stbc;
+ u32 ldbc;
+ u32 dyn_mimops;
+ u32 static_mimops;
+ u32 spatial_mux;
+ u32 vht;
+ u32 bw80;
+ u32 vht_2g;
+ u32 pmf;
+};
+
+enum wmi_peer_flags {
+ WMI_PEER_AUTH = 0x00000001,
+ WMI_PEER_QOS = 0x00000002,
+ WMI_PEER_NEED_PTK_4_WAY = 0x00000004,
+ WMI_PEER_NEED_GTK_2_WAY = 0x00000010,
+ WMI_PEER_APSD = 0x00000800,
+ WMI_PEER_HT = 0x00001000,
+ WMI_PEER_40MHZ = 0x00002000,
+ WMI_PEER_STBC = 0x00008000,
+ WMI_PEER_LDPC = 0x00010000,
+ WMI_PEER_DYN_MIMOPS = 0x00020000,
+ WMI_PEER_STATIC_MIMOPS = 0x00040000,
+ WMI_PEER_SPATIAL_MUX = 0x00200000,
+ WMI_PEER_VHT = 0x02000000,
+ WMI_PEER_80MHZ = 0x04000000,
+ WMI_PEER_VHT_2G = 0x08000000,
+ WMI_PEER_PMF = 0x10000000,
+};
+
+enum wmi_10x_peer_flags {
+ WMI_10X_PEER_AUTH = 0x00000001,
+ WMI_10X_PEER_QOS = 0x00000002,
+ WMI_10X_PEER_NEED_PTK_4_WAY = 0x00000004,
+ WMI_10X_PEER_NEED_GTK_2_WAY = 0x00000010,
+ WMI_10X_PEER_APSD = 0x00000800,
+ WMI_10X_PEER_HT = 0x00001000,
+ WMI_10X_PEER_40MHZ = 0x00002000,
+ WMI_10X_PEER_STBC = 0x00008000,
+ WMI_10X_PEER_LDPC = 0x00010000,
+ WMI_10X_PEER_DYN_MIMOPS = 0x00020000,
+ WMI_10X_PEER_STATIC_MIMOPS = 0x00040000,
+ WMI_10X_PEER_SPATIAL_MUX = 0x00200000,
+ WMI_10X_PEER_VHT = 0x02000000,
+ WMI_10X_PEER_80MHZ = 0x04000000,
+};
+
+enum wmi_10_2_peer_flags {
+ WMI_10_2_PEER_AUTH = 0x00000001,
+ WMI_10_2_PEER_QOS = 0x00000002,
+ WMI_10_2_PEER_NEED_PTK_4_WAY = 0x00000004,
+ WMI_10_2_PEER_NEED_GTK_2_WAY = 0x00000010,
+ WMI_10_2_PEER_APSD = 0x00000800,
+ WMI_10_2_PEER_HT = 0x00001000,
+ WMI_10_2_PEER_40MHZ = 0x00002000,
+ WMI_10_2_PEER_STBC = 0x00008000,
+ WMI_10_2_PEER_LDPC = 0x00010000,
+ WMI_10_2_PEER_DYN_MIMOPS = 0x00020000,
+ WMI_10_2_PEER_STATIC_MIMOPS = 0x00040000,
+ WMI_10_2_PEER_SPATIAL_MUX = 0x00200000,
+ WMI_10_2_PEER_VHT = 0x02000000,
+ WMI_10_2_PEER_80MHZ = 0x04000000,
+ WMI_10_2_PEER_VHT_2G = 0x08000000,
+ WMI_10_2_PEER_PMF = 0x10000000,
+};
/*
* Peer rate capabilities.
@@ -5721,6 +5816,11 @@ struct wmi_10_2_peer_assoc_complete_cmd {
__le32 info0; /* WMI_PEER_ASSOC_INFO0_ */
} __packed;
+struct wmi_10_4_peer_assoc_complete_cmd {
+ struct wmi_10_2_peer_assoc_complete_cmd cmd;
+ __le32 peer_bw_rxnss_override;
+} __packed;
+
struct wmi_peer_assoc_complete_arg {
u8 addr[ETH_ALEN];
u32 vdev_id;
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 342563a3706f..3d946d8b2db2 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -767,7 +767,7 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf,
if (info->flags & IEEE80211_TX_CTL_NO_ACK)
flags |= AR5K_TXDESC_NOACK;
- rc_flags = info->control.rates[0].flags;
+ rc_flags = bf->rates[0].flags;
hw_rate = ath5k_get_rate_hw_value(ah->hw, info, bf, 0);
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 81ac8c59f0ec..7f3f94fbf157 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -3930,8 +3930,8 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff;
ath6kl_band_5ghz.ht_cap.mcs.rx_mask[1] = 0xff;
- ar->hw.tx_ant = 2;
- ar->hw.rx_ant = 2;
+ ar->hw.tx_ant = 0x3; /* mask, 2 antenna */
+ ar->hw.rx_ant = 0x3;
} else {
ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
diff --git a/drivers/net/wireless/ath/ath6kl/htc_mbox.c b/drivers/net/wireless/ath/ath6kl/htc_mbox.c
index fffb65b3e652..65c31da43c47 100644
--- a/drivers/net/wireless/ath/ath6kl/htc_mbox.c
+++ b/drivers/net/wireless/ath/ath6kl/htc_mbox.c
@@ -2222,8 +2222,9 @@ int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target,
}
if (status) {
- ath6kl_err("failed to get pending recv messages: %d\n",
- status);
+ if (status != -ECANCELED)
+ ath6kl_err("failed to get pending recv messages: %d\n",
+ status);
/* cleanup any packets in sync completion queue */
list_for_each_entry_safe(packets, tmp_pkt, &comp_pktq, list) {
diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c
index 6ae0734f86e0..da557dc742e6 100644
--- a/drivers/net/wireless/ath/ath6kl/init.c
+++ b/drivers/net/wireless/ath/ath6kl/init.c
@@ -954,8 +954,10 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name)
snprintf(filename, sizeof(filename), "%s/%s", ar->hw.fw.dir, name);
ret = request_firmware(&fw, filename, ar->dev);
- if (ret)
+ if (ret) {
+ ath6kl_err("Failed request firmware, rv: %d\n", ret);
return ret;
+ }
data = fw->data;
len = fw->size;
@@ -964,11 +966,15 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name)
magic_len = strlen(ATH6KL_FIRMWARE_MAGIC) + 1;
if (len < magic_len) {
+ ath6kl_err("Magic length is invalid, len: %zd magic_len: %zd\n",
+ len, magic_len);
ret = -EINVAL;
goto out;
}
if (memcmp(data, ATH6KL_FIRMWARE_MAGIC, magic_len) != 0) {
+ ath6kl_err("Magic is invalid, magic_len: %zd\n",
+ magic_len);
ret = -EINVAL;
goto out;
}
@@ -987,7 +993,12 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name)
len -= sizeof(*hdr);
data += sizeof(*hdr);
+ ath6kl_dbg(ATH6KL_DBG_BOOT, "ie-id: %d len: %zd (0x%zx)\n",
+ ie_id, ie_len, ie_len);
+
if (len < ie_len) {
+ ath6kl_err("IE len is invalid, len: %zd ie_len: %zd ie-id: %d\n",
+ len, ie_len, ie_id);
ret = -EINVAL;
goto out;
}
@@ -1008,6 +1019,7 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name)
ar->fw_otp = kmemdup(data, ie_len, GFP_KERNEL);
if (ar->fw_otp == NULL) {
+ ath6kl_err("fw_otp cannot be allocated\n");
ret = -ENOMEM;
goto out;
}
@@ -1025,6 +1037,7 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name)
ar->fw = vmalloc(ie_len);
if (ar->fw == NULL) {
+ ath6kl_err("fw storage cannot be allocated, len: %zd\n", ie_len);
ret = -ENOMEM;
goto out;
}
@@ -1039,6 +1052,7 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name)
ar->fw_patch = kmemdup(data, ie_len, GFP_KERNEL);
if (ar->fw_patch == NULL) {
+ ath6kl_err("fw_patch storage cannot be allocated, len: %zd\n", ie_len);
ret = -ENOMEM;
goto out;
}
diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig
index fee0cadb0f5e..40fa915d6f35 100644
--- a/drivers/net/wireless/ath/ath9k/Kconfig
+++ b/drivers/net/wireless/ath/ath9k/Kconfig
@@ -176,3 +176,14 @@ config ATH9K_HTC_DEBUGFS
depends on ATH9K_HTC && DEBUG_FS
---help---
Say Y, if you need access to ath9k_htc's statistics.
+
+config ATH9K_HWRNG
+ bool "Random number generator support"
+ depends on ATH9K && (HW_RANDOM = y || HW_RANDOM = ATH9K)
+ default y
+ ---help---
+ This option incorporates the ADC register output as a source of
+ randomness into Linux entropy pool (/dev/urandom and /dev/random)
+
+ Say Y, feeds the entropy directly from the WiFi driver to the input
+ pool.
diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
index ecda613c2d54..76f9dc37500b 100644
--- a/drivers/net/wireless/ath/ath9k/Makefile
+++ b/drivers/net/wireless/ath/ath9k/Makefile
@@ -15,6 +15,7 @@ ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o
ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += dfs.o
ath9k-$(CONFIG_ATH9K_TX99) += tx99.o
ath9k-$(CONFIG_ATH9K_WOW) += wow.o
+ath9k-$(CONFIG_ATH9K_HWRNG) += rng.o
ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index b42f4a963ef4..5294595da5a7 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -23,6 +23,7 @@
#include <linux/leds.h>
#include <linux/completion.h>
#include <linux/time.h>
+#include <linux/hw_random.h>
#include "common.h"
#include "debug.h"
@@ -981,6 +982,7 @@ struct ath_softc {
struct ath_offchannel offchannel;
struct ath_chanctx *next_chan;
struct completion go_beacon;
+ struct timespec last_event_time;
#endif
unsigned long driver_data;
@@ -1040,6 +1042,11 @@ struct ath_softc {
u32 wow_intr_before_sleep;
bool force_wow;
#endif
+
+#ifdef CONFIG_ATH9K_HWRNG
+ u32 rng_last;
+ struct task_struct *rng_task;
+#endif
};
/********/
@@ -1062,6 +1069,22 @@ static inline int ath9k_tx99_send(struct ath_softc *sc,
}
#endif /* CONFIG_ATH9K_TX99 */
+/***************************/
+/* Random Number Generator */
+/***************************/
+#ifdef CONFIG_ATH9K_HWRNG
+void ath9k_rng_start(struct ath_softc *sc);
+void ath9k_rng_stop(struct ath_softc *sc);
+#else
+static inline void ath9k_rng_start(struct ath_softc *sc)
+{
+}
+
+static inline void ath9k_rng_stop(struct ath_softc *sc)
+{
+}
+#endif
+
static inline void ath_read_cachesize(struct ath_common *common, int *csz)
{
common->bus_ops->read_cachesize(common, csz);
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index f50a6bc5d06e..5cf0cd7cb2d1 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -148,7 +148,8 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
ath_assign_seq(common, skb);
- if (vif->p2p)
+ /* Always assign NOA attr when MCC enabled */
+ if (ath9k_is_chanctx_enabled())
ath9k_beacon_add_noa(sc, avp, skb);
bf->bf_buf_addr = dma_map_single(sc->dev, skb->data,
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index 90f5773a1a61..50e614b915f1 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -226,6 +226,20 @@ static const char *chanctx_state_string(enum ath_chanctx_state state)
}
}
+static const u32 chanctx_event_delta(struct ath_softc *sc)
+{
+ u64 ms;
+ struct timespec ts, *old;
+
+ getrawmonotonic(&ts);
+ old = &sc->last_event_time;
+ ms = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+ ms -= old->tv_sec * 1000 + old->tv_nsec / 1000000;
+ sc->last_event_time = ts;
+
+ return (u32)ms;
+}
+
void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
@@ -356,14 +370,16 @@ static void ath_chanctx_setup_timer(struct ath_softc *sc, u32 tsf_time)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_hw *ah = sc->sc_ah;
+ unsigned long timeout;
ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, tsf_time, 1000000);
tsf_time -= ath9k_hw_gettsf32(ah);
- tsf_time = msecs_to_jiffies(tsf_time / 1000) + 1;
- mod_timer(&sc->sched.timer, jiffies + tsf_time);
+ timeout = msecs_to_jiffies(tsf_time / 1000) + 1;
+ mod_timer(&sc->sched.timer, jiffies + timeout);
ath_dbg(common, CHAN_CTX,
- "Setup chanctx timer with timeout: %d ms\n", jiffies_to_msecs(tsf_time));
+ "Setup chanctx timer with timeout: %d (%d) ms\n",
+ tsf_time / 1000, jiffies_to_msecs(timeout));
}
static void ath_chanctx_handle_bmiss(struct ath_softc *sc,
@@ -403,7 +419,7 @@ static void ath_chanctx_offchannel_noa(struct ath_softc *sc,
avp->offchannel_duration = sc->sched.offchannel_duration;
ath_dbg(common, CHAN_CTX,
- "offchannel noa_duration: %d, noa_start: %d, noa_index: %d\n",
+ "offchannel noa_duration: %d, noa_start: %u, noa_index: %d\n",
avp->offchannel_duration,
avp->offchannel_start,
avp->noa_index);
@@ -443,7 +459,7 @@ static void ath_chanctx_set_periodic_noa(struct ath_softc *sc,
avp->periodic_noa = true;
ath_dbg(common, CHAN_CTX,
- "noa_duration: %d, noa_start: %d, noa_index: %d, periodic: %d\n",
+ "noa_duration: %d, noa_start: %u, noa_index: %d, periodic: %d\n",
avp->noa_duration,
avp->noa_start,
avp->noa_index,
@@ -464,7 +480,7 @@ static void ath_chanctx_set_oneshot_noa(struct ath_softc *sc,
avp->noa_duration = duration + sc->sched.channel_switch_time;
ath_dbg(common, CHAN_CTX,
- "oneshot noa_duration: %d, noa_start: %d, noa_index: %d, periodic: %d\n",
+ "oneshot noa_duration: %d, noa_start: %u, noa_index: %d, periodic: %d\n",
avp->noa_duration,
avp->noa_start,
avp->noa_index,
@@ -487,10 +503,11 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
spin_lock_bh(&sc->chan_lock);
- ath_dbg(common, CHAN_CTX, "cur_chan: %d MHz, event: %s, state: %s\n",
+ ath_dbg(common, CHAN_CTX, "cur_chan: %d MHz, event: %s, state: %s, delta: %u ms\n",
sc->cur_chan->chandef.center_freq1,
chanctx_event_string(ev),
- chanctx_state_string(sc->sched.state));
+ chanctx_state_string(sc->sched.state),
+ chanctx_event_delta(sc));
switch (ev) {
case ATH_CHANCTX_EVENT_BEACON_PREPARE:
@@ -1099,6 +1116,7 @@ ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp,
nullfunc->frame_control |=
cpu_to_le16(IEEE80211_FCTL_PM);
+ skb->priority = 7;
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta)) {
dev_kfree_skb_any(skb);
@@ -1401,8 +1419,9 @@ void ath9k_chanctx_wake_queues(struct ath_softc *sc, struct ath_chanctx *ctx)
static void ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_hw *ah = sc->sc_ah;
- s32 tsf, target_tsf;
+ u32 tsf, target_tsf;
if (!avp || !avp->noa.has_next_tsf)
return;
@@ -1414,11 +1433,17 @@ static void ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp)
target_tsf = avp->noa.next_tsf;
if (!avp->noa.absent)
target_tsf -= ATH_P2P_PS_STOP_TIME;
+ else
+ target_tsf += ATH_P2P_PS_STOP_TIME;
if (target_tsf - tsf < ATH_P2P_PS_STOP_TIME)
target_tsf = tsf + ATH_P2P_PS_STOP_TIME;
- ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, (u32) target_tsf, 1000000);
+ ath_dbg(common, CHAN_CTX, "%s absent %d tsf 0x%08X next_tsf 0x%08X (%dms)\n",
+ __func__, avp->noa.absent, tsf, target_tsf,
+ (target_tsf - tsf) / 1000);
+
+ ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, target_tsf, 1000000);
}
static void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif)
@@ -1433,6 +1458,10 @@ static void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif)
return;
sc->p2p_ps_vif = avp;
+
+ if (sc->ps_flags & PS_BEACON_SYNC)
+ return;
+
tsf = ath9k_hw_gettsf32(sc->sc_ah);
ieee80211_parse_p2p_noa(&vif->bss_conf.p2p_noa_attr, &avp->noa, tsf);
ath9k_update_p2p_ps_timer(sc, avp);
@@ -1495,6 +1524,8 @@ void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp,
noa->index = avp->noa_index;
noa->oppps_ctwindow = ath9k_get_ctwin(sc, avp);
+ if (noa->oppps_ctwindow)
+ noa->oppps_ctwindow |= BIT(7);
if (avp->noa_duration) {
if (avp->periodic_noa) {
@@ -1536,6 +1567,8 @@ void ath9k_p2p_ps_timer(void *priv)
tsf = ath9k_hw_gettsf32(sc->sc_ah);
if (!avp->noa.absent)
tsf += ATH_P2P_PS_STOP_TIME;
+ else
+ tsf -= ATH_P2P_PS_STOP_TIME;
if (!avp->noa.has_next_tsf ||
avp->noa.next_tsf - tsf > BIT(31))
@@ -1571,8 +1604,7 @@ void ath9k_p2p_bss_info_changed(struct ath_softc *sc,
spin_lock_bh(&sc->sc_pcu_lock);
spin_lock_irqsave(&sc->sc_pm_lock, flags);
- if (!(sc->ps_flags & PS_BEACON_SYNC))
- ath9k_update_p2p_ps(sc, vif);
+ ath9k_update_p2p_ps(sc, vif);
spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
spin_unlock_bh(&sc->sc_pcu_lock);
}
diff --git a/drivers/net/wireless/ath/ath9k/common-beacon.c b/drivers/net/wireless/ath/ath9k/common-beacon.c
index 6ad44470d0f2..01d6d3205a65 100644
--- a/drivers/net/wireless/ath/ath9k/common-beacon.c
+++ b/drivers/net/wireless/ath/ath9k/common-beacon.c
@@ -18,30 +18,16 @@
#define FUDGE 2
-/* Calculate the modulo of a 64 bit TSF snapshot with a TU divisor */
-static u32 ath9k_mod_tsf64_tu(u64 tsf, u32 div_tu)
-{
- u32 tsf_mod, tsf_hi, tsf_lo, mod_hi, mod_lo;
-
- tsf_mod = tsf & (BIT(10) - 1);
- tsf_hi = tsf >> 32;
- tsf_lo = ((u32) tsf) >> 10;
-
- mod_hi = tsf_hi % div_tu;
- mod_lo = ((mod_hi << 22) + tsf_lo) % div_tu;
-
- return (mod_lo << 10) | tsf_mod;
-}
-
static u32 ath9k_get_next_tbtt(struct ath_hw *ah, u64 tsf,
unsigned int interval)
{
- unsigned int offset;
+ unsigned int offset, divisor;
tsf += TU_TO_USEC(FUDGE + ah->config.sw_beacon_response_time);
- offset = ath9k_mod_tsf64_tu(tsf, interval);
+ divisor = TU_TO_USEC(interval);
+ div_u64_rem(tsf, divisor, &offset);
- return (u32) tsf + TU_TO_USEC(interval) - offset;
+ return (u32) tsf + divisor - offset;
}
/*
diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c
index cc81482c934d..a7afdeee698c 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom.c
@@ -138,6 +138,80 @@ bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data)
return ret;
}
+int ath9k_hw_nvram_swap_data(struct ath_hw *ah, bool *swap_needed, int size)
+{
+ u16 magic;
+ u16 *eepdata;
+ int i;
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) {
+ ath_err(common, "Reading Magic # failed\n");
+ return -EIO;
+ }
+
+ if (magic == AR5416_EEPROM_MAGIC) {
+ *swap_needed = false;
+ } else if (swab16(magic) == AR5416_EEPROM_MAGIC) {
+ if (ah->ah_flags & AH_NO_EEP_SWAP) {
+ ath_info(common,
+ "Ignoring endianness difference in EEPROM magic bytes.\n");
+
+ *swap_needed = false;
+ } else {
+ *swap_needed = true;
+ }
+ } else {
+ ath_err(common,
+ "Invalid EEPROM Magic (0x%04x).\n", magic);
+ return -EINVAL;
+ }
+
+ eepdata = (u16 *)(&ah->eeprom);
+
+ if (*swap_needed) {
+ ath_dbg(common, EEPROM,
+ "EEPROM Endianness is not native.. Changing.\n");
+
+ for (i = 0; i < size; i++)
+ eepdata[i] = swab16(eepdata[i]);
+ }
+
+ return 0;
+}
+
+bool ath9k_hw_nvram_validate_checksum(struct ath_hw *ah, int size)
+{
+ u32 i, sum = 0;
+ u16 *eepdata = (u16 *)(&ah->eeprom);
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ for (i = 0; i < size; i++)
+ sum ^= eepdata[i];
+
+ if (sum != 0xffff) {
+ ath_err(common, "Bad EEPROM checksum 0x%x\n", sum);
+ return false;
+ }
+
+ return true;
+}
+
+bool ath9k_hw_nvram_check_version(struct ath_hw *ah, int version, int minrev)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ if (ah->eep_ops->get_eeprom_ver(ah) != version ||
+ ah->eep_ops->get_eeprom_rev(ah) < minrev) {
+ ath_err(common, "Bad EEPROM VER 0x%04x or REV 0x%04x\n",
+ ah->eep_ops->get_eeprom_ver(ah),
+ ah->eep_ops->get_eeprom_rev(ah));
+ return false;
+ }
+
+ return true;
+}
+
void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList,
u8 *pVpdList, u16 numIntercepts,
u8 *pRetVpdList)
diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h
index 40d4f62d0f16..4465c6566f20 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom.h
+++ b/drivers/net/wireless/ath/ath9k/eeprom.h
@@ -664,6 +664,9 @@ int16_t ath9k_hw_interpolate(u16 target, u16 srcLeft, u16 srcRight,
bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, u16 listSize,
u16 *indexL, u16 *indexR);
bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data);
+int ath9k_hw_nvram_swap_data(struct ath_hw *ah, bool *swap_needed, int size);
+bool ath9k_hw_nvram_validate_checksum(struct ath_hw *ah, int size);
+bool ath9k_hw_nvram_check_version(struct ath_hw *ah, int version, int minrev);
void ath9k_hw_usb_gen_fill_eeprom(struct ath_hw *ah, u16 *eep_data,
int eep_start_loc, int size);
void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList,
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
index 4773da6dc6f2..5da0826bf1be 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
@@ -177,74 +177,30 @@ static u32 ath9k_hw_4k_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
}
#endif
-
-#undef SIZE_EEPROM_4K
-
static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
{
-#define EEPROM_4K_SIZE (sizeof(struct ar5416_eeprom_4k) / sizeof(u16))
- struct ath_common *common = ath9k_hw_common(ah);
struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k;
- u16 *eepdata, temp, magic, magic2;
- u32 sum = 0, el;
- bool need_swap = false;
- int i, addr;
+ u32 el;
+ bool need_swap;
+ int i, err;
-
- if (!ath9k_hw_use_flash(ah)) {
- if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET,
- &magic)) {
- ath_err(common, "Reading Magic # failed\n");
- return false;
- }
-
- ath_dbg(common, EEPROM, "Read Magic = 0x%04X\n", magic);
-
- if (magic != AR5416_EEPROM_MAGIC) {
- magic2 = swab16(magic);
-
- if (magic2 == AR5416_EEPROM_MAGIC) {
- need_swap = true;
- eepdata = (u16 *) (&ah->eeprom);
-
- for (addr = 0; addr < EEPROM_4K_SIZE; addr++) {
- temp = swab16(*eepdata);
- *eepdata = temp;
- eepdata++;
- }
- } else {
- ath_err(common,
- "Invalid EEPROM Magic. Endianness mismatch.\n");
- return -EINVAL;
- }
- }
- }
-
- ath_dbg(common, EEPROM, "need_swap = %s\n",
- need_swap ? "True" : "False");
+ err = ath9k_hw_nvram_swap_data(ah, &need_swap, SIZE_EEPROM_4K);
+ if (err)
+ return err;
if (need_swap)
- el = swab16(ah->eeprom.map4k.baseEepHeader.length);
- else
- el = ah->eeprom.map4k.baseEepHeader.length;
-
- if (el > sizeof(struct ar5416_eeprom_4k))
- el = sizeof(struct ar5416_eeprom_4k) / sizeof(u16);
+ el = swab16(eep->baseEepHeader.length);
else
- el = el / sizeof(u16);
+ el = eep->baseEepHeader.length;
- eepdata = (u16 *)(&ah->eeprom);
-
- for (i = 0; i < el; i++)
- sum ^= *eepdata++;
+ el = min(el / sizeof(u16), SIZE_EEPROM_4K);
+ if (!ath9k_hw_nvram_validate_checksum(ah, el))
+ return -EINVAL;
if (need_swap) {
u32 integer;
u16 word;
- ath_dbg(common, EEPROM,
- "EEPROM Endianness is not native.. Changing\n");
-
word = swab16(eep->baseEepHeader.length);
eep->baseEepHeader.length = word;
@@ -283,17 +239,15 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah)
}
}
- if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER ||
- ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) {
- ath_err(common, "Bad EEPROM checksum 0x%x or revision 0x%04x\n",
- sum, ah->eep_ops->get_eeprom_ver(ah));
+ if (!ath9k_hw_nvram_check_version(ah, AR5416_EEP_VER,
+ AR5416_EEP_NO_BACK_VER))
return -EINVAL;
- }
return 0;
-#undef EEPROM_4K_SIZE
}
+#undef SIZE_EEPROM_4K
+
static u32 ath9k_hw_4k_get_eeprom(struct ath_hw *ah,
enum eeprom_param param)
{
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c
index 6ca33dfde1fd..1a019a39eda1 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c
@@ -177,59 +177,24 @@ static u32 ath9k_hw_ar9287_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
static int ath9k_hw_ar9287_check_eeprom(struct ath_hw *ah)
{
- u32 sum = 0, el, integer;
- u16 temp, word, magic, magic2, *eepdata;
- int i, addr;
- bool need_swap = false;
+ u32 el, integer;
+ u16 word;
+ int i, err;
+ bool need_swap;
struct ar9287_eeprom *eep = &ah->eeprom.map9287;
- struct ath_common *common = ath9k_hw_common(ah);
-
- if (!ath9k_hw_use_flash(ah)) {
- if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET,
- &magic)) {
- ath_err(common, "Reading Magic # failed\n");
- return false;
- }
-
- ath_dbg(common, EEPROM, "Read Magic = 0x%04X\n", magic);
-
- if (magic != AR5416_EEPROM_MAGIC) {
- magic2 = swab16(magic);
-
- if (magic2 == AR5416_EEPROM_MAGIC) {
- need_swap = true;
- eepdata = (u16 *)(&ah->eeprom);
-
- for (addr = 0; addr < SIZE_EEPROM_AR9287; addr++) {
- temp = swab16(*eepdata);
- *eepdata = temp;
- eepdata++;
- }
- } else {
- ath_err(common,
- "Invalid EEPROM Magic. Endianness mismatch.\n");
- return -EINVAL;
- }
- }
- }
- ath_dbg(common, EEPROM, "need_swap = %s\n",
- need_swap ? "True" : "False");
+ err = ath9k_hw_nvram_swap_data(ah, &need_swap, SIZE_EEPROM_AR9287);
+ if (err)
+ return err;
if (need_swap)
- el = swab16(ah->eeprom.map9287.baseEepHeader.length);
- else
- el = ah->eeprom.map9287.baseEepHeader.length;
-
- if (el > sizeof(struct ar9287_eeprom))
- el = sizeof(struct ar9287_eeprom) / sizeof(u16);
+ el = swab16(eep->baseEepHeader.length);
else
- el = el / sizeof(u16);
-
- eepdata = (u16 *)(&ah->eeprom);
+ el = eep->baseEepHeader.length;
- for (i = 0; i < el; i++)
- sum ^= *eepdata++;
+ el = min(el / sizeof(u16), SIZE_EEPROM_AR9287);
+ if (!ath9k_hw_nvram_validate_checksum(ah, el))
+ return -EINVAL;
if (need_swap) {
word = swab16(eep->baseEepHeader.length);
@@ -270,16 +235,15 @@ static int ath9k_hw_ar9287_check_eeprom(struct ath_hw *ah)
}
}
- if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR9287_EEP_VER
- || ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) {
- ath_err(common, "Bad EEPROM checksum 0x%x or revision 0x%04x\n",
- sum, ah->eep_ops->get_eeprom_ver(ah));
+ if (!ath9k_hw_nvram_check_version(ah, AR9287_EEP_VER,
+ AR5416_EEP_NO_BACK_VER))
return -EINVAL;
- }
return 0;
}
+#undef SIZE_EEPROM_AR9287
+
static u32 ath9k_hw_ar9287_get_eeprom(struct ath_hw *ah,
enum eeprom_param param)
{
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c
index 056f516bf017..959682f7909c 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_def.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c
@@ -126,8 +126,6 @@ static bool ath9k_hw_def_fill_eeprom(struct ath_hw *ah)
return __ath9k_hw_def_fill_eeprom(ah);
}
-#undef SIZE_EEPROM_DEF
-
#if defined(CONFIG_ATH9K_DEBUGFS) || defined(CONFIG_ATH9K_HTC_DEBUGFS)
static u32 ath9k_def_dump_modal_eeprom(char *buf, u32 len, u32 size,
struct modal_eep_header *modal_hdr)
@@ -257,59 +255,31 @@ static u32 ath9k_hw_def_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
}
#endif
-
static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
{
struct ar5416_eeprom_def *eep = &ah->eeprom.def;
struct ath_common *common = ath9k_hw_common(ah);
- u16 *eepdata, temp, magic;
- u32 sum = 0, el;
- bool need_swap = false;
- int i, addr, size;
-
- if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) {
- ath_err(common, "Reading Magic # failed\n");
- return false;
- }
-
- if (swab16(magic) == AR5416_EEPROM_MAGIC &&
- !(ah->ah_flags & AH_NO_EEP_SWAP)) {
- size = sizeof(struct ar5416_eeprom_def);
- need_swap = true;
- eepdata = (u16 *) (&ah->eeprom);
-
- for (addr = 0; addr < size / sizeof(u16); addr++) {
- temp = swab16(*eepdata);
- *eepdata = temp;
- eepdata++;
- }
- }
+ u32 el;
+ bool need_swap;
+ int i, err;
- ath_dbg(common, EEPROM, "need_swap = %s\n",
- need_swap ? "True" : "False");
+ err = ath9k_hw_nvram_swap_data(ah, &need_swap, SIZE_EEPROM_DEF);
+ if (err)
+ return err;
if (need_swap)
- el = swab16(ah->eeprom.def.baseEepHeader.length);
+ el = swab16(eep->baseEepHeader.length);
else
- el = ah->eeprom.def.baseEepHeader.length;
+ el = eep->baseEepHeader.length;
- if (el > sizeof(struct ar5416_eeprom_def))
- el = sizeof(struct ar5416_eeprom_def) / sizeof(u16);
- else
- el = el / sizeof(u16);
-
- eepdata = (u16 *)(&ah->eeprom);
-
- for (i = 0; i < el; i++)
- sum ^= *eepdata++;
+ el = min(el / sizeof(u16), SIZE_EEPROM_DEF);
+ if (!ath9k_hw_nvram_validate_checksum(ah, el))
+ return -EINVAL;
if (need_swap) {
u32 integer, j;
u16 word;
- ath_dbg(common, EEPROM,
- "EEPROM Endianness is not native.. Changing.\n");
-
word = swab16(eep->baseEepHeader.length);
eep->baseEepHeader.length = word;
@@ -356,12 +326,9 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
}
}
- if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER ||
- ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) {
- ath_err(common, "Bad EEPROM checksum 0x%x or revision 0x%04x\n",
- sum, ah->eep_ops->get_eeprom_ver(ah));
+ if (!ath9k_hw_nvram_check_version(ah, AR5416_EEP_VER,
+ AR5416_EEP_NO_BACK_VER))
return -EINVAL;
- }
/* Enable fixup for AR_AN_TOP2 if necessary */
if ((ah->hw_version.devid == AR9280_DEVID_PCI) &&
@@ -376,6 +343,8 @@ static int ath9k_hw_def_check_eeprom(struct ath_hw *ah)
return 0;
}
+#undef SIZE_EEPROM_DEF
+
static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah,
enum eeprom_param param)
{
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index a680a970b7f7..fe1fd1a5ae15 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -834,7 +834,7 @@ void ath9k_htc_ani_work(struct work_struct *work)
if (longcal || shortcal)
common->ani.caldone =
ath9k_hw_calibrate(ah, ah->curchan,
- ah->rxchainmask, longcal);
+ ah->rxchainmask, longcal) > 0;
ath9k_htc_ps_restore(priv);
}
diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c
index 2294709ee8b0..fd85f996c554 100644
--- a/drivers/net/wireless/ath/ath9k/htc_hst.c
+++ b/drivers/net/wireless/ath/ath9k/htc_hst.c
@@ -414,7 +414,7 @@ void ath9k_htc_rx_msg(struct htc_target *htc_handle,
return;
}
- if (epid >= ENDPOINT_MAX) {
+ if (epid < 0 || epid >= ENDPOINT_MAX) {
if (pipe_id != USB_REG_IN_PIPE)
dev_kfree_skb_any(skb);
else
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 41382f89abe1..257f46ed4a04 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -2299,10 +2299,10 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
else
nextTbtt = bs->bs_nexttbtt;
- ath_dbg(common, BEACON, "next DTIM %d\n", bs->bs_nextdtim);
- ath_dbg(common, BEACON, "next beacon %d\n", nextTbtt);
- ath_dbg(common, BEACON, "beacon period %d\n", beaconintval);
- ath_dbg(common, BEACON, "DTIM period %d\n", dtimperiod);
+ ath_dbg(common, BEACON, "next DTIM %u\n", bs->bs_nextdtim);
+ ath_dbg(common, BEACON, "next beacon %u\n", nextTbtt);
+ ath_dbg(common, BEACON, "beacon period %u\n", beaconintval);
+ ath_dbg(common, BEACON, "DTIM period %u\n", dtimperiod);
ENABLE_REGWRITE_BUFFER(ah);
@@ -2761,9 +2761,6 @@ void ath9k_hw_setrxfilter(struct ath_hw *ah, u32 bits)
ENABLE_REGWRITE_BUFFER(ah);
- if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
- bits |= ATH9K_RX_FILTER_CONTROL_WRAPPER;
-
REG_WRITE(ah, AR_RX_FILTER, bits);
phybits = 0;
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 2e2b92ba96b8..ab7a1ac37849 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -828,6 +828,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
ieee80211_hw_set(hw, RX_INCLUDES_FCS);
ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
+ ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);
if (ath9k_ps_enable)
ieee80211_hw_set(hw, SUPPORTS_PS);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index d184e682e636..c1b33fdcca08 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -739,6 +739,8 @@ static int ath9k_start(struct ieee80211_hw *hw)
ath9k_ps_restore(sc);
+ ath9k_rng_start(sc);
+
return 0;
}
@@ -828,6 +830,8 @@ static void ath9k_stop(struct ieee80211_hw *hw)
ath9k_deinit_channel_context(sc);
+ ath9k_rng_stop(sc);
+
mutex_lock(&sc->mutex);
ath_cancel_work(sc);
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 994daf6c6297..32160fca876a 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -424,6 +424,9 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
AR_SREV_9561(sc->sc_ah))
rfilt |= ATH9K_RX_FILTER_4ADDRESS;
+ if (AR_SREV_9462(sc->sc_ah) || AR_SREV_9565(sc->sc_ah))
+ rfilt |= ATH9K_RX_FILTER_CONTROL_WRAPPER;
+
if (ath9k_is_chanctx_enabled() &&
test_bit(ATH_OP_SCANNING, &common->op_flags))
rfilt |= ATH9K_RX_FILTER_BEACON;
diff --git a/drivers/net/wireless/ath/ath9k/rng.c b/drivers/net/wireless/ath/ath9k/rng.c
new file mode 100644
index 000000000000..c9cb2aad7b6f
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/rng.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2015 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/hw_random.h>
+#include <linux/kthread.h>
+
+#include "ath9k.h"
+#include "hw.h"
+#include "ar9003_phy.h"
+
+#define ATH9K_RNG_BUF_SIZE 320
+#define ATH9K_RNG_ENTROPY(x) (((x) * 8 * 320) >> 10) /* quality: 320/1024 */
+
+static int ath9k_rng_data_read(struct ath_softc *sc, u32 *buf, u32 buf_size)
+{
+ int i, j;
+ u32 v1, v2, rng_last = sc->rng_last;
+ struct ath_hw *ah = sc->sc_ah;
+
+ ath9k_ps_wakeup(sc);
+
+ REG_RMW_FIELD(ah, AR_PHY_TEST, AR_PHY_TEST_BBB_OBS_SEL, 1);
+ REG_CLR_BIT(ah, AR_PHY_TEST, AR_PHY_TEST_RX_OBS_SEL_BIT5);
+ REG_RMW_FIELD(ah, AR_PHY_TEST_CTL_STATUS, AR_PHY_TEST_CTL_RX_OBS_SEL, 0);
+
+ for (i = 0, j = 0; i < buf_size; i++) {
+ v1 = REG_READ(ah, AR_PHY_TST_ADC) & 0xffff;
+ v2 = REG_READ(ah, AR_PHY_TST_ADC) & 0xffff;
+
+ /* wait for data ready */
+ if (v1 && v2 && rng_last != v1 && v1 != v2 && v1 != 0xffff &&
+ v2 != 0xffff)
+ buf[j++] = (v1 << 16) | v2;
+
+ rng_last = v2;
+ }
+
+ ath9k_ps_restore(sc);
+
+ sc->rng_last = rng_last;
+
+ return j << 2;
+}
+
+static int ath9k_rng_kthread(void *data)
+{
+ int bytes_read;
+ struct ath_softc *sc = data;
+ u32 *rng_buf;
+
+ rng_buf = kmalloc_array(ATH9K_RNG_BUF_SIZE, sizeof(u32), GFP_KERNEL);
+ if (!rng_buf)
+ goto out;
+
+ while (!kthread_should_stop()) {
+ bytes_read = ath9k_rng_data_read(sc, rng_buf,
+ ATH9K_RNG_BUF_SIZE);
+ if (unlikely(!bytes_read)) {
+ msleep_interruptible(10);
+ continue;
+ }
+
+ /* sleep until entropy bits under write_wakeup_threshold */
+ add_hwgenerator_randomness((void *)rng_buf, bytes_read,
+ ATH9K_RNG_ENTROPY(bytes_read));
+ }
+
+ kfree(rng_buf);
+out:
+ sc->rng_task = NULL;
+
+ return 0;
+}
+
+void ath9k_rng_start(struct ath_softc *sc)
+{
+ struct ath_hw *ah = sc->sc_ah;
+
+ if (sc->rng_task)
+ return;
+
+ if (!AR_SREV_9300_20_OR_LATER(ah))
+ return;
+
+ sc->rng_task = kthread_run(ath9k_rng_kthread, sc, "ath9k-hwrng");
+ if (IS_ERR(sc->rng_task))
+ sc->rng_task = NULL;
+}
+
+void ath9k_rng_stop(struct ath_softc *sc)
+{
+ if (sc->rng_task)
+ kthread_stop(sc->rng_task);
+}
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 3e3dac3d7060..fe795fc5288c 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1473,11 +1473,14 @@ static bool ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
u16 tid, u16 *ssn)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_atx_tid *txtid;
struct ath_txq *txq;
struct ath_node *an;
u8 density;
+ ath_dbg(common, XMIT, "%s called\n", __func__);
+
an = (struct ath_node *)sta->drv_priv;
txtid = ATH_AN_2_TID(an, tid);
txq = txtid->txq;
@@ -1512,10 +1515,13 @@ int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_node *an = (struct ath_node *)sta->drv_priv;
struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid);
struct ath_txq *txq = txtid->txq;
+ ath_dbg(common, XMIT, "%s called\n", __func__);
+
ath_txq_lock(sc, txq);
txtid->active = false;
ath_tx_flush_tid(sc, txtid);
@@ -1526,11 +1532,14 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc,
struct ath_node *an)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_atx_tid *tid;
struct ath_txq *txq;
bool buffered;
int tidno;
+ ath_dbg(common, XMIT, "%s called\n", __func__);
+
for (tidno = 0, tid = &an->tid[tidno];
tidno < IEEE80211_NUM_TIDS; tidno++, tid++) {
@@ -1555,10 +1564,13 @@ void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc,
void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_atx_tid *tid;
struct ath_txq *txq;
int tidno;
+ ath_dbg(common, XMIT, "%s called\n", __func__);
+
for (tidno = 0, tid = &an->tid[tidno];
tidno < IEEE80211_NUM_TIDS; tidno++, tid++) {
@@ -1579,10 +1591,13 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an)
void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta,
u16 tidno)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_atx_tid *tid;
struct ath_node *an;
struct ath_txq *txq;
+ ath_dbg(common, XMIT, "%s called\n", __func__);
+
an = (struct ath_node *)sta->drv_priv;
tid = ATH_AN_2_TID(an, tidno);
txq = tid->txq;
@@ -2316,6 +2331,12 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
queue = ieee80211_is_data_present(hdr->frame_control);
+ /* If chanctx, queue all null frames while NOA could be there */
+ if (ath9k_is_chanctx_enabled() &&
+ ieee80211_is_nullfunc(hdr->frame_control) &&
+ !txctl->force_channel)
+ queue = true;
+
/* Force queueing of all frames that belong to a virtual interface on
* a different channel context, to ensure that they are sent on the
* correct channel.
@@ -2894,7 +2915,7 @@ int ath9k_tx99_send(struct ath_softc *sc, struct sk_buff *skb,
if (skb_headroom(skb) < padsize) {
ath_dbg(common, XMIT,
"tx99 padding failed\n");
- return -EINVAL;
+ return -EINVAL;
}
skb_push(skb, padsize);
diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c
index f8dfa05b290a..8643801f31b6 100644
--- a/drivers/net/wireless/ath/wcn36xx/dxe.c
+++ b/drivers/net/wireless/ath/wcn36xx/dxe.c
@@ -474,36 +474,37 @@ static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn,
struct wcn36xx_dxe_desc *dxe = ctl->desc;
dma_addr_t dma_addr;
struct sk_buff *skb;
+ int ret = 0, int_mask;
+ u32 value;
+
+ if (ch->ch_type == WCN36XX_DXE_CH_RX_L) {
+ value = WCN36XX_DXE_CTRL_RX_L;
+ int_mask = WCN36XX_DXE_INT_CH1_MASK;
+ } else {
+ value = WCN36XX_DXE_CTRL_RX_H;
+ int_mask = WCN36XX_DXE_INT_CH3_MASK;
+ }
while (!(dxe->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)) {
skb = ctl->skb;
dma_addr = dxe->dst_addr_l;
- wcn36xx_dxe_fill_skb(wcn->dev, ctl);
-
- switch (ch->ch_type) {
- case WCN36XX_DXE_CH_RX_L:
- dxe->ctrl = WCN36XX_DXE_CTRL_RX_L;
- wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR,
- WCN36XX_DXE_INT_CH1_MASK);
- break;
- case WCN36XX_DXE_CH_RX_H:
- dxe->ctrl = WCN36XX_DXE_CTRL_RX_H;
- wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR,
- WCN36XX_DXE_INT_CH3_MASK);
- break;
- default:
- wcn36xx_warn("Unknown channel\n");
- }
-
- dma_unmap_single(wcn->dev, dma_addr, WCN36XX_PKT_SIZE,
- DMA_FROM_DEVICE);
- wcn36xx_rx_skb(wcn, skb);
+ ret = wcn36xx_dxe_fill_skb(wcn->dev, ctl);
+ if (0 == ret) {
+ /* new skb allocation ok. Use the new one and queue
+ * the old one to network system.
+ */
+ dma_unmap_single(wcn->dev, dma_addr, WCN36XX_PKT_SIZE,
+ DMA_FROM_DEVICE);
+ wcn36xx_rx_skb(wcn, skb);
+ } /* else keep old skb not submitted and use it for rx DMA */
+
+ dxe->ctrl = value;
ctl = ctl->next;
dxe = ctl->desc;
}
+ wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, int_mask);
ch->head_blk_ctl = ctl;
-
return 0;
}
diff --git a/drivers/net/wireless/ath/wcn36xx/hal.h b/drivers/net/wireless/ath/wcn36xx/hal.h
index a1f1127d7808..b947de0fb2e5 100644
--- a/drivers/net/wireless/ath/wcn36xx/hal.h
+++ b/drivers/net/wireless/ath/wcn36xx/hal.h
@@ -345,6 +345,8 @@ enum wcn36xx_hal_host_msg_type {
WCN36XX_HAL_DHCP_START_IND = 189,
WCN36XX_HAL_DHCP_STOP_IND = 190,
+ WCN36XX_HAL_AVOID_FREQ_RANGE_IND = 233,
+
WCN36XX_HAL_MSG_MAX = WCN36XX_HAL_MSG_TYPE_MAX_ENUM_SIZE
};
diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c
index c9263e1c75d4..74f56a81ad9a 100644
--- a/drivers/net/wireless/ath/wcn36xx/smd.c
+++ b/drivers/net/wireless/ath/wcn36xx/smd.c
@@ -302,6 +302,22 @@ static int wcn36xx_smd_rsp_status_check(void *buf, size_t len)
return 0;
}
+static int wcn36xx_smd_rsp_status_check_v2(struct wcn36xx *wcn, void *buf,
+ size_t len)
+{
+ struct wcn36xx_fw_msg_status_rsp_v2 *rsp;
+
+ if (len < sizeof(struct wcn36xx_hal_msg_header) + sizeof(*rsp))
+ return wcn36xx_smd_rsp_status_check(buf, len);
+
+ rsp = buf + sizeof(struct wcn36xx_hal_msg_header);
+
+ if (WCN36XX_FW_MSG_RESULT_SUCCESS != rsp->status)
+ return rsp->status;
+
+ return 0;
+}
+
int wcn36xx_smd_load_nv(struct wcn36xx *wcn)
{
struct nv_data *nv_d;
@@ -1582,7 +1598,8 @@ int wcn36xx_smd_remove_bsskey(struct wcn36xx *wcn,
wcn36xx_err("Sending hal_remove_bsskey failed\n");
goto out;
}
- ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+ ret = wcn36xx_smd_rsp_status_check_v2(wcn, wcn->hal_buf,
+ wcn->hal_rsp_len);
if (ret) {
wcn36xx_err("hal_remove_bsskey response failed err=%d\n", ret);
goto out;
@@ -1951,7 +1968,8 @@ int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index)
wcn36xx_err("Sending hal_trigger_ba failed\n");
goto out;
}
- ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+ ret = wcn36xx_smd_rsp_status_check_v2(wcn, wcn->hal_buf,
+ wcn->hal_rsp_len);
if (ret) {
wcn36xx_err("hal_trigger_ba response failed err=%d\n", ret);
goto out;
@@ -2128,6 +2146,8 @@ static void wcn36xx_smd_rsp_process(struct wcn36xx *wcn, void *buf, size_t len)
complete(&wcn->hal_rsp_compl);
break;
+ case WCN36XX_HAL_COEX_IND:
+ case WCN36XX_HAL_AVOID_FREQ_RANGE_IND:
case WCN36XX_HAL_OTA_TX_COMPL_IND:
case WCN36XX_HAL_MISSED_BEACON_IND:
case WCN36XX_HAL_DELETE_STA_CONTEXT_IND:
@@ -2174,6 +2194,9 @@ static void wcn36xx_ind_smd_work(struct work_struct *work)
msg_header = (struct wcn36xx_hal_msg_header *)hal_ind_msg->msg;
switch (msg_header->msg_type) {
+ case WCN36XX_HAL_COEX_IND:
+ case WCN36XX_HAL_AVOID_FREQ_RANGE_IND:
+ break;
case WCN36XX_HAL_OTA_TX_COMPL_IND:
wcn36xx_smd_tx_compl_ind(wcn,
hal_ind_msg->msg,
diff --git a/drivers/net/wireless/ath/wcn36xx/smd.h b/drivers/net/wireless/ath/wcn36xx/smd.h
index 008d03423dbf..8361f9e3995b 100644
--- a/drivers/net/wireless/ath/wcn36xx/smd.h
+++ b/drivers/net/wireless/ath/wcn36xx/smd.h
@@ -44,6 +44,15 @@ struct wcn36xx_fw_msg_status_rsp {
u32 status;
} __packed;
+/* wcn3620 returns this for tigger_ba */
+
+struct wcn36xx_fw_msg_status_rsp_v2 {
+ u8 bss_id[6];
+ u32 status __packed;
+ u16 count_following_candidates __packed;
+ /* candidate list follows */
+};
+
struct wcn36xx_hal_ind_msg {
struct list_head list;
u8 *msg;
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index 97bc186f9728..a1d10b85989f 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -580,16 +580,10 @@ static ssize_t wil_write_file_rxon(struct file *file, const char __user *buf,
long channel;
bool on;
- char *kbuf = kmalloc(len + 1, GFP_KERNEL);
-
- if (!kbuf)
- return -ENOMEM;
- if (copy_from_user(kbuf, buf, len)) {
- kfree(kbuf);
- return -EIO;
- }
+ char *kbuf = memdup_user_nul(buf, len);
- kbuf[len] = '\0';
+ if (IS_ERR(kbuf))
+ return PTR_ERR(kbuf);
rc = kstrtol(kbuf, 0, &channel);
kfree(kbuf);
if (rc)
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index 50c136e843c4..4f2ffa5c6e17 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -394,9 +394,13 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
wil_fw_core_dump(wil);
wil_notify_fw_error(wil);
isr &= ~ISR_MISC_FW_ERROR;
- wil_fw_error_recovery(wil);
+ if (wil->platform_ops.notify_crash) {
+ wil_err(wil, "notify platform driver about FW crash");
+ wil->platform_ops.notify_crash(wil->platform_handle);
+ } else {
+ wil_fw_error_recovery(wil);
+ }
}
-
if (isr & ISR_MISC_MBOX_EVT) {
wil_dbg_irq(wil, "MBOX event\n");
wmi_recv_cmd(wil);
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index bb69a5949aea..b39f0bfc591e 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -401,20 +401,26 @@ void wil_bcast_fini(struct wil6210_priv *wil)
static void wil_connect_worker(struct work_struct *work)
{
- int rc;
+ int rc, cid, ringid;
struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
connect_worker);
struct net_device *ndev = wil_to_ndev(wil);
- int cid = wil->pending_connect_cid;
- int ringid = wil_find_free_vring(wil);
+ mutex_lock(&wil->mutex);
+ cid = wil->pending_connect_cid;
if (cid < 0) {
wil_err(wil, "No connection pending\n");
- return;
+ goto out;
+ }
+ ringid = wil_find_free_vring(wil);
+ if (ringid < 0) {
+ wil_err(wil, "No free vring found\n");
+ goto out;
}
- wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid);
+ wil_dbg_wmi(wil, "Configure for connection CID %d vring %d\n",
+ cid, ringid);
rc = wil_vring_init_tx(wil, ringid, 1 << tx_ring_order, cid, 0);
wil->pending_connect_cid = -1;
@@ -424,6 +430,8 @@ static void wil_connect_worker(struct work_struct *work)
} else {
wil_disconnect_cid(wil, cid, WLAN_REASON_UNSPECIFIED, true);
}
+out:
+ mutex_unlock(&wil->mutex);
}
int wil_priv_init(struct wil6210_priv *wil)
@@ -773,8 +781,10 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
wil_bcast_fini(wil);
- /* prevent NAPI from being scheduled */
+ /* prevent NAPI from being scheduled and prevent wmi commands */
+ mutex_lock(&wil->wmi_mutex);
bitmap_zero(wil->status, wil_status_last);
+ mutex_unlock(&wil->wmi_mutex);
if (wil->scan_request) {
wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
@@ -977,7 +987,7 @@ int __wil_down(struct wil6210_priv *wil)
}
mutex_lock(&wil->mutex);
- if (!iter)
+ if (iter < 0)
wil_err(wil, "timeout waiting for idle FW/HW\n");
wil_reset(wil, false);
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index e3b3c8fb4605..56aaa2d4fb0e 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -183,7 +183,7 @@ void *wil_if_alloc(struct device *dev)
netif_napi_add(ndev, &wil->napi_rx, wil6210_netdev_poll_rx,
WIL6210_NAPI_BUDGET);
- netif_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx,
+ netif_tx_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx,
WIL6210_NAPI_BUDGET);
netif_tx_stop_all_queues(ndev);
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index 1a3142c332e1..e36f2a0c8cb6 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2014 Qualcomm Atheros, Inc.
+ * Copyright (c) 2012-2015 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -125,11 +125,37 @@ static int wil_if_pcie_disable(struct wil6210_priv *wil)
return 0;
}
+static int wil_platform_rop_ramdump(void *wil_handle, void *buf, uint32_t size)
+{
+ struct wil6210_priv *wil = wil_handle;
+
+ if (!wil)
+ return -EINVAL;
+
+ return wil_fw_copy_crash_dump(wil, buf, size);
+}
+
+static int wil_platform_rop_fw_recovery(void *wil_handle)
+{
+ struct wil6210_priv *wil = wil_handle;
+
+ if (!wil)
+ return -EINVAL;
+
+ wil_fw_error_recovery(wil);
+
+ return 0;
+}
+
static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct wil6210_priv *wil;
struct device *dev = &pdev->dev;
int rc;
+ const struct wil_platform_rops rops = {
+ .ramdump = wil_platform_rop_ramdump,
+ .fw_recovery = wil_platform_rop_fw_recovery,
+ };
/* check HW */
dev_info(&pdev->dev, WIL_NAME
@@ -154,7 +180,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
/* rollback to if_free */
wil->platform_handle =
- wil_platform_init(&pdev->dev, &wil->platform_ops);
+ wil_platform_init(&pdev->dev, &wil->platform_ops, &rops, wil);
if (!wil->platform_handle) {
rc = -ENODEV;
wil_err(wil, "wil_platform_init failed\n");
diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c
index e3d1be82f314..32031e7a11d5 100644
--- a/drivers/net/wireless/ath/wil6210/rx_reorder.c
+++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c
@@ -261,9 +261,19 @@ struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
void wil_tid_ampdu_rx_free(struct wil6210_priv *wil,
struct wil_tid_ampdu_rx *r)
{
+ int i;
+
if (!r)
return;
- wil_release_reorder_frames(wil, r, r->head_seq_num + r->buf_size);
+
+ /* Do not pass remaining frames to the network stack - it may be
+ * not expecting to get any more Rx. Rx from here may lead to
+ * kernel OOPS since some per-socket accounting info was already
+ * released.
+ */
+ for (i = 0; i < r->buf_size; i++)
+ kfree_skb(r->reorder_buf[i]);
+
kfree(r->reorder_buf);
kfree(r->reorder_time);
kfree(r);
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 3bc9bc0efbac..7887e6cfd817 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -160,6 +160,7 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,
struct device *dev = wil_to_dev(wil);
size_t sz = vring->size * sizeof(vring->va[0]);
+ lockdep_assert_held(&wil->mutex);
if (tx) {
int vring_index = vring - wil->vring_tx;
@@ -749,6 +750,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
wil_dbg_misc(wil, "%s() max_mpdu_size %d\n", __func__,
cmd.vring_cfg.tx_sw_ring.max_mpdu_size);
+ lockdep_assert_held(&wil->mutex);
if (vring->va) {
wil_err(wil, "Tx ring [%d] already allocated\n", id);
@@ -821,6 +823,7 @@ int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size)
wil_dbg_misc(wil, "%s() max_mpdu_size %d\n", __func__,
cmd.vring_cfg.tx_sw_ring.max_mpdu_size);
+ lockdep_assert_held(&wil->mutex);
if (vring->va) {
wil_err(wil, "Tx ring [%d] already allocated\n", id);
@@ -872,7 +875,7 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id)
struct vring *vring = &wil->vring_tx[id];
struct vring_tx_data *txdata = &wil->vring_tx_data[id];
- WARN_ON(!mutex_is_locked(&wil->mutex));
+ lockdep_assert_held(&wil->mutex);
if (!vring->va)
return;
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index ade5f3b8274b..235e205ce2bc 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -828,6 +828,7 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime);
int wil_suspend(struct wil6210_priv *wil, bool is_runtime);
int wil_resume(struct wil6210_priv *wil, bool is_runtime);
+int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size);
void wil_fw_core_dump(struct wil6210_priv *wil);
#endif /* __WIL6210_H__ */
diff --git a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c
index 7e70934990ae..b57d280946e0 100644
--- a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c
+++ b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c
@@ -51,8 +51,7 @@ static int wil_fw_get_crash_dump_bounds(struct wil6210_priv *wil,
return 0;
}
-static int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest,
- u32 size)
+int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size)
{
int i;
const struct fw_map *map;
diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.c b/drivers/net/wireless/ath/wil6210/wil_platform.c
index 2e831bf20117..4eed05bddb60 100644
--- a/drivers/net/wireless/ath/wil6210/wil_platform.c
+++ b/drivers/net/wireless/ath/wil6210/wil_platform.c
@@ -33,7 +33,8 @@ void wil_platform_modexit(void)
* It returns a handle which is used with the rest of the API
*
*/
-void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops)
+void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops,
+ const struct wil_platform_rops *rops, void *wil_handle)
{
void *handle = ops; /* to return some non-NULL for 'void' impl. */
diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.h b/drivers/net/wireless/ath/wil6210/wil_platform.h
index d7fa19b7886d..9a949d910343 100644
--- a/drivers/net/wireless/ath/wil6210/wil_platform.h
+++ b/drivers/net/wireless/ath/wil6210/wil_platform.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014 Qualcomm Atheros, Inc.
+ * Copyright (c) 2014-2015 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -20,16 +20,48 @@
struct device;
/**
- * struct wil_platform_ops - wil platform module callbacks
+ * struct wil_platform_ops - wil platform module calls from this
+ * driver to platform driver
*/
struct wil_platform_ops {
int (*bus_request)(void *handle, uint32_t kbps /* KBytes/Sec */);
int (*suspend)(void *handle);
int (*resume)(void *handle);
void (*uninit)(void *handle);
+ int (*notify_crash)(void *handle);
};
-void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops);
+/**
+ * struct wil_platform_rops - wil platform module callbacks from
+ * platform driver to this driver
+ * @ramdump: store a ramdump from the wil firmware. The platform
+ * driver may add additional data to the ramdump to
+ * generate the final crash dump.
+ * @fw_recovery: start a firmware recovery process. Called as
+ * part of a crash recovery process which may include other
+ * related platform subsystems.
+ */
+struct wil_platform_rops {
+ int (*ramdump)(void *wil_handle, void *buf, uint32_t size);
+ int (*fw_recovery)(void *wil_handle);
+};
+
+/**
+ * wil_platform_init - initialize the platform driver
+ *
+ * @dev - pointer to the wil6210 device
+ * @ops - structure with platform driver operations. Platform
+ * driver will fill this structure with function pointers.
+ * @rops - structure with callbacks from platform driver to
+ * this driver. The platform driver copies the structure to
+ * its own storage. Can be NULL if this driver does not
+ * support crash recovery.
+ * @wil_handle - context for this driver that will be passed
+ * when platform driver invokes one of the callbacks in
+ * rops. May be NULL if rops is NULL.
+ */
+void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops,
+ const struct wil_platform_rops *rops, void *wil_handle);
int __init wil_platform_modinit(void);
void wil_platform_modexit(void);
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 6ed26baca0e5..e3ea74cdd4aa 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -228,6 +228,10 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
wil_dbg_wmi(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head);
/* wait till FW finish with previous command */
for (retry = 5; retry > 0; retry--) {
+ if (!test_bit(wil_status_fwready, wil->status)) {
+ wil_err(wil, "WMI: cannot send command while FW not ready\n");
+ return -EAGAIN;
+ }
r->tail = wil_r(wil, RGF_MBOX +
offsetof(struct wil6210_mbox_ctl, tx.tail));
if (next_head != r->tail)
diff --git a/drivers/net/wireless/atmel/Kconfig b/drivers/net/wireless/atmel/Kconfig
new file mode 100644
index 000000000000..a43cfd163254
--- /dev/null
+++ b/drivers/net/wireless/atmel/Kconfig
@@ -0,0 +1,57 @@
+config WLAN_VENDOR_ATMEL
+ bool "Atmel devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_ATMEL
+
+config ATMEL
+ tristate "Atmel at76c50x chipset 802.11b support"
+ depends on CFG80211 && (PCI || PCMCIA)
+ select WIRELESS_EXT
+ select WEXT_PRIV
+ select FW_LOADER
+ select CRC32
+ ---help---
+ A driver 802.11b wireless cards based on the Atmel fast-vnet
+ chips. This driver supports standard Linux wireless extensions.
+
+ Many cards based on this chipset do not have flash memory
+ and need their firmware loaded at start-up. If yours is
+ one of these, you will need to provide a firmware image
+ to be loaded into the card by the driver. The Atmel
+ firmware package can be downloaded from
+ <http://www.thekelleys.org.uk/atmel>
+
+config PCI_ATMEL
+ tristate "Atmel at76c506 PCI cards"
+ depends on ATMEL && PCI
+ ---help---
+ Enable support for PCI and mini-PCI cards containing the
+ Atmel at76c506 chip.
+
+config PCMCIA_ATMEL
+ tristate "Atmel at76c502/at76c504 PCMCIA cards"
+ depends on ATMEL && PCMCIA
+ select WIRELESS_EXT
+ select FW_LOADER
+ select CRC32
+ ---help---
+ Enable support for PCMCIA cards containing the
+ Atmel at76c502 and at76c504 chips.
+
+config AT76C50X_USB
+ tristate "Atmel at76c503/at76c505/at76c505a USB cards"
+ depends on MAC80211 && USB
+ select FW_LOADER
+ ---help---
+ Enable support for USB Wireless devices using Atmel at76c503,
+ at76c505 or at76c505a chips.
+
+endif # WLAN_VENDOR_ATMEL
diff --git a/drivers/net/wireless/atmel/Makefile b/drivers/net/wireless/atmel/Makefile
new file mode 100644
index 000000000000..e62e345f7af6
--- /dev/null
+++ b/drivers/net/wireless/atmel/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_ATMEL) += atmel.o
+obj-$(CONFIG_PCI_ATMEL) += atmel_pci.o
+obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o
+
+obj-$(CONFIG_AT76C50X_USB) += at76c50x-usb.o
diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/atmel/at76c50x-usb.c
index dab25136214a..dab25136214a 100644
--- a/drivers/net/wireless/at76c50x-usb.c
+++ b/drivers/net/wireless/atmel/at76c50x-usb.c
diff --git a/drivers/net/wireless/at76c50x-usb.h b/drivers/net/wireless/atmel/at76c50x-usb.h
index ae03271f878e..ae03271f878e 100644
--- a/drivers/net/wireless/at76c50x-usb.h
+++ b/drivers/net/wireless/atmel/at76c50x-usb.h
diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel/atmel.c
index 6a1f03c271c1..6a1f03c271c1 100644
--- a/drivers/net/wireless/atmel.c
+++ b/drivers/net/wireless/atmel/atmel.c
diff --git a/drivers/net/wireless/atmel.h b/drivers/net/wireless/atmel/atmel.h
index 96f7318cbb04..96f7318cbb04 100644
--- a/drivers/net/wireless/atmel.h
+++ b/drivers/net/wireless/atmel/atmel.h
diff --git a/drivers/net/wireless/atmel_cs.c b/drivers/net/wireless/atmel/atmel_cs.c
index 7afc9c5329fb..7afc9c5329fb 100644
--- a/drivers/net/wireless/atmel_cs.c
+++ b/drivers/net/wireless/atmel/atmel_cs.c
diff --git a/drivers/net/wireless/atmel_pci.c b/drivers/net/wireless/atmel/atmel_pci.c
index bcf1f274a251..bcf1f274a251 100644
--- a/drivers/net/wireless/atmel_pci.c
+++ b/drivers/net/wireless/atmel/atmel_pci.c
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.h b/drivers/net/wireless/brcm80211/brcmfmac/common.h
deleted file mode 100644
index 21c7488b4732..000000000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/common.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Copyright (c) 2014 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-#ifndef BRCMFMAC_COMMON_H
-#define BRCMFMAC_COMMON_H
-
-extern const u8 ALLFFMAC[ETH_ALEN];
-
-/* Sets dongle media info (drv_version, mac address). */
-int brcmf_c_preinit_dcmds(struct brcmf_if *ifp);
-
-#endif /* BRCMFMAC_COMMON_H */
diff --git a/drivers/net/wireless/broadcom/Kconfig b/drivers/net/wireless/broadcom/Kconfig
new file mode 100644
index 000000000000..d3651ceb5046
--- /dev/null
+++ b/drivers/net/wireless/broadcom/Kconfig
@@ -0,0 +1,18 @@
+config WLAN_VENDOR_BROADCOM
+ bool "Broadcom devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_BROADCOM
+
+source "drivers/net/wireless/broadcom/b43/Kconfig"
+source "drivers/net/wireless/broadcom/b43legacy/Kconfig"
+source "drivers/net/wireless/broadcom/brcm80211/Kconfig"
+
+endif # WLAN_VENDOR_BROADCOM
diff --git a/drivers/net/wireless/broadcom/Makefile b/drivers/net/wireless/broadcom/Makefile
new file mode 100644
index 000000000000..9d5ac95710c3
--- /dev/null
+++ b/drivers/net/wireless/broadcom/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_B43) += b43/
+obj-$(CONFIG_B43LEGACY) += b43legacy/
+
+obj-$(CONFIG_BRCMFMAC) += brcm80211/
+obj-$(CONFIG_BRCMSMAC) += brcm80211/
diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/broadcom/b43/Kconfig
index fba856032ca5..fba856032ca5 100644
--- a/drivers/net/wireless/b43/Kconfig
+++ b/drivers/net/wireless/broadcom/b43/Kconfig
diff --git a/drivers/net/wireless/b43/Makefile b/drivers/net/wireless/broadcom/b43/Makefile
index ddc4df46656f..ddc4df46656f 100644
--- a/drivers/net/wireless/b43/Makefile
+++ b/drivers/net/wireless/broadcom/b43/Makefile
diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/broadcom/b43/b43.h
index 036552439816..036552439816 100644
--- a/drivers/net/wireless/b43/b43.h
+++ b/drivers/net/wireless/broadcom/b43/b43.h
diff --git a/drivers/net/wireless/b43/bus.c b/drivers/net/wireless/broadcom/b43/bus.c
index 17d16a391fe6..17d16a391fe6 100644
--- a/drivers/net/wireless/b43/bus.c
+++ b/drivers/net/wireless/broadcom/b43/bus.c
diff --git a/drivers/net/wireless/b43/bus.h b/drivers/net/wireless/broadcom/b43/bus.h
index 256c2c17939a..256c2c17939a 100644
--- a/drivers/net/wireless/b43/bus.h
+++ b/drivers/net/wireless/broadcom/b43/bus.h
diff --git a/drivers/net/wireless/b43/debugfs.c b/drivers/net/wireless/broadcom/b43/debugfs.c
index b4bcd94aff6c..b4bcd94aff6c 100644
--- a/drivers/net/wireless/b43/debugfs.c
+++ b/drivers/net/wireless/broadcom/b43/debugfs.c
diff --git a/drivers/net/wireless/b43/debugfs.h b/drivers/net/wireless/broadcom/b43/debugfs.h
index d05377745011..d05377745011 100644
--- a/drivers/net/wireless/b43/debugfs.h
+++ b/drivers/net/wireless/broadcom/b43/debugfs.h
diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/broadcom/b43/dma.c
index 6837064908be..6837064908be 100644
--- a/drivers/net/wireless/b43/dma.c
+++ b/drivers/net/wireless/broadcom/b43/dma.c
diff --git a/drivers/net/wireless/b43/dma.h b/drivers/net/wireless/broadcom/b43/dma.h
index df8c8cdcbdb5..df8c8cdcbdb5 100644
--- a/drivers/net/wireless/b43/dma.h
+++ b/drivers/net/wireless/broadcom/b43/dma.h
diff --git a/drivers/net/wireless/b43/leds.c b/drivers/net/wireless/broadcom/b43/leds.c
index d79ab2a227e1..d79ab2a227e1 100644
--- a/drivers/net/wireless/b43/leds.c
+++ b/drivers/net/wireless/broadcom/b43/leds.c
diff --git a/drivers/net/wireless/b43/leds.h b/drivers/net/wireless/broadcom/b43/leds.h
index 32b66d53cdac..32b66d53cdac 100644
--- a/drivers/net/wireless/b43/leds.h
+++ b/drivers/net/wireless/broadcom/b43/leds.h
diff --git a/drivers/net/wireless/b43/lo.c b/drivers/net/wireless/broadcom/b43/lo.c
index a335f94c72ff..a335f94c72ff 100644
--- a/drivers/net/wireless/b43/lo.c
+++ b/drivers/net/wireless/broadcom/b43/lo.c
diff --git a/drivers/net/wireless/b43/lo.h b/drivers/net/wireless/broadcom/b43/lo.h
index 7b4df3883bc2..7b4df3883bc2 100644
--- a/drivers/net/wireless/b43/lo.h
+++ b/drivers/net/wireless/broadcom/b43/lo.h
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c
index ec013fbd6a81..ec013fbd6a81 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/broadcom/b43/main.c
diff --git a/drivers/net/wireless/b43/main.h b/drivers/net/wireless/broadcom/b43/main.h
index c46430cc725c..c46430cc725c 100644
--- a/drivers/net/wireless/b43/main.h
+++ b/drivers/net/wireless/broadcom/b43/main.h
diff --git a/drivers/net/wireless/b43/phy_a.c b/drivers/net/wireless/broadcom/b43/phy_a.c
index 99c036f5ecb7..99c036f5ecb7 100644
--- a/drivers/net/wireless/b43/phy_a.c
+++ b/drivers/net/wireless/broadcom/b43/phy_a.c
diff --git a/drivers/net/wireless/b43/phy_a.h b/drivers/net/wireless/broadcom/b43/phy_a.h
index f7d0d929a374..f7d0d929a374 100644
--- a/drivers/net/wireless/b43/phy_a.h
+++ b/drivers/net/wireless/broadcom/b43/phy_a.h
diff --git a/drivers/net/wireless/b43/phy_ac.c b/drivers/net/wireless/broadcom/b43/phy_ac.c
index e75633d67938..e75633d67938 100644
--- a/drivers/net/wireless/b43/phy_ac.c
+++ b/drivers/net/wireless/broadcom/b43/phy_ac.c
diff --git a/drivers/net/wireless/b43/phy_ac.h b/drivers/net/wireless/broadcom/b43/phy_ac.h
index d1ca79e0eb24..d1ca79e0eb24 100644
--- a/drivers/net/wireless/b43/phy_ac.h
+++ b/drivers/net/wireless/broadcom/b43/phy_ac.h
diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/broadcom/b43/phy_common.c
index ec2b9c577b90..ec2b9c577b90 100644
--- a/drivers/net/wireless/b43/phy_common.c
+++ b/drivers/net/wireless/broadcom/b43/phy_common.c
diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/broadcom/b43/phy_common.h
index 78d86526799e..78d86526799e 100644
--- a/drivers/net/wireless/b43/phy_common.h
+++ b/drivers/net/wireless/broadcom/b43/phy_common.h
diff --git a/drivers/net/wireless/b43/phy_g.c b/drivers/net/wireless/broadcom/b43/phy_g.c
index 462310e6e88f..462310e6e88f 100644
--- a/drivers/net/wireless/b43/phy_g.c
+++ b/drivers/net/wireless/broadcom/b43/phy_g.c
diff --git a/drivers/net/wireless/b43/phy_g.h b/drivers/net/wireless/broadcom/b43/phy_g.h
index 5413c906a3e7..5413c906a3e7 100644
--- a/drivers/net/wireless/b43/phy_g.h
+++ b/drivers/net/wireless/broadcom/b43/phy_g.h
diff --git a/drivers/net/wireless/b43/phy_ht.c b/drivers/net/wireless/broadcom/b43/phy_ht.c
index bd68945965d6..bd68945965d6 100644
--- a/drivers/net/wireless/b43/phy_ht.c
+++ b/drivers/net/wireless/broadcom/b43/phy_ht.c
diff --git a/drivers/net/wireless/b43/phy_ht.h b/drivers/net/wireless/broadcom/b43/phy_ht.h
index c086f56ce478..c086f56ce478 100644
--- a/drivers/net/wireless/b43/phy_ht.h
+++ b/drivers/net/wireless/broadcom/b43/phy_ht.h
diff --git a/drivers/net/wireless/b43/phy_lcn.c b/drivers/net/wireless/broadcom/b43/phy_lcn.c
index 97461ccf3e1e..97461ccf3e1e 100644
--- a/drivers/net/wireless/b43/phy_lcn.c
+++ b/drivers/net/wireless/broadcom/b43/phy_lcn.c
diff --git a/drivers/net/wireless/b43/phy_lcn.h b/drivers/net/wireless/broadcom/b43/phy_lcn.h
index 6a7092e13fff..6a7092e13fff 100644
--- a/drivers/net/wireless/b43/phy_lcn.h
+++ b/drivers/net/wireless/broadcom/b43/phy_lcn.h
diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/broadcom/b43/phy_lp.c
index 058a9f232050..058a9f232050 100644
--- a/drivers/net/wireless/b43/phy_lp.c
+++ b/drivers/net/wireless/broadcom/b43/phy_lp.c
diff --git a/drivers/net/wireless/b43/phy_lp.h b/drivers/net/wireless/broadcom/b43/phy_lp.h
index 62737f700cbc..62737f700cbc 100644
--- a/drivers/net/wireless/b43/phy_lp.h
+++ b/drivers/net/wireless/broadcom/b43/phy_lp.h
diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/broadcom/b43/phy_n.c
index 9f0bcf3b8414..9f0bcf3b8414 100644
--- a/drivers/net/wireless/b43/phy_n.c
+++ b/drivers/net/wireless/broadcom/b43/phy_n.c
diff --git a/drivers/net/wireless/b43/phy_n.h b/drivers/net/wireless/broadcom/b43/phy_n.h
index a6da2c31a99c..a6da2c31a99c 100644
--- a/drivers/net/wireless/b43/phy_n.h
+++ b/drivers/net/wireless/broadcom/b43/phy_n.h
diff --git a/drivers/net/wireless/b43/pio.c b/drivers/net/wireless/broadcom/b43/pio.c
index a4ff5e2a42b9..a4ff5e2a42b9 100644
--- a/drivers/net/wireless/b43/pio.c
+++ b/drivers/net/wireless/broadcom/b43/pio.c
diff --git a/drivers/net/wireless/b43/pio.h b/drivers/net/wireless/broadcom/b43/pio.h
index 1e516147424f..1e516147424f 100644
--- a/drivers/net/wireless/b43/pio.h
+++ b/drivers/net/wireless/broadcom/b43/pio.h
diff --git a/drivers/net/wireless/b43/ppr.c b/drivers/net/wireless/broadcom/b43/ppr.c
index 9a770279c415..9a770279c415 100644
--- a/drivers/net/wireless/b43/ppr.c
+++ b/drivers/net/wireless/broadcom/b43/ppr.c
diff --git a/drivers/net/wireless/b43/ppr.h b/drivers/net/wireless/broadcom/b43/ppr.h
index 24d7447e9f01..24d7447e9f01 100644
--- a/drivers/net/wireless/b43/ppr.h
+++ b/drivers/net/wireless/broadcom/b43/ppr.h
diff --git a/drivers/net/wireless/b43/radio_2055.c b/drivers/net/wireless/broadcom/b43/radio_2055.c
index 5289a18ddd8c..5289a18ddd8c 100644
--- a/drivers/net/wireless/b43/radio_2055.c
+++ b/drivers/net/wireless/broadcom/b43/radio_2055.c
diff --git a/drivers/net/wireless/b43/radio_2055.h b/drivers/net/wireless/broadcom/b43/radio_2055.h
index 67f96122f8d8..67f96122f8d8 100644
--- a/drivers/net/wireless/b43/radio_2055.h
+++ b/drivers/net/wireless/broadcom/b43/radio_2055.h
diff --git a/drivers/net/wireless/b43/radio_2056.c b/drivers/net/wireless/broadcom/b43/radio_2056.c
index 2ce25607c60d..2ce25607c60d 100644
--- a/drivers/net/wireless/b43/radio_2056.c
+++ b/drivers/net/wireless/broadcom/b43/radio_2056.c
diff --git a/drivers/net/wireless/b43/radio_2056.h b/drivers/net/wireless/broadcom/b43/radio_2056.h
index 5b86673459fa..5b86673459fa 100644
--- a/drivers/net/wireless/b43/radio_2056.h
+++ b/drivers/net/wireless/broadcom/b43/radio_2056.h
diff --git a/drivers/net/wireless/b43/radio_2057.c b/drivers/net/wireless/broadcom/b43/radio_2057.c
index ff1e026a61a1..ff1e026a61a1 100644
--- a/drivers/net/wireless/b43/radio_2057.c
+++ b/drivers/net/wireless/broadcom/b43/radio_2057.c
diff --git a/drivers/net/wireless/b43/radio_2057.h b/drivers/net/wireless/broadcom/b43/radio_2057.h
index 220d080238ff..220d080238ff 100644
--- a/drivers/net/wireless/b43/radio_2057.h
+++ b/drivers/net/wireless/broadcom/b43/radio_2057.h
diff --git a/drivers/net/wireless/b43/radio_2059.c b/drivers/net/wireless/broadcom/b43/radio_2059.c
index a3cf9efd7e21..a3cf9efd7e21 100644
--- a/drivers/net/wireless/b43/radio_2059.c
+++ b/drivers/net/wireless/broadcom/b43/radio_2059.c
diff --git a/drivers/net/wireless/b43/radio_2059.h b/drivers/net/wireless/broadcom/b43/radio_2059.h
index 9e22fb60588b..9e22fb60588b 100644
--- a/drivers/net/wireless/b43/radio_2059.h
+++ b/drivers/net/wireless/broadcom/b43/radio_2059.h
diff --git a/drivers/net/wireless/b43/rfkill.c b/drivers/net/wireless/broadcom/b43/rfkill.c
index 70c2fcedd1bb..70c2fcedd1bb 100644
--- a/drivers/net/wireless/b43/rfkill.c
+++ b/drivers/net/wireless/broadcom/b43/rfkill.c
diff --git a/drivers/net/wireless/b43/rfkill.h b/drivers/net/wireless/broadcom/b43/rfkill.h
index f046c3ca0519..f046c3ca0519 100644
--- a/drivers/net/wireless/b43/rfkill.h
+++ b/drivers/net/wireless/broadcom/b43/rfkill.h
diff --git a/drivers/net/wireless/b43/sdio.c b/drivers/net/wireless/broadcom/b43/sdio.c
index 59a521800694..59a521800694 100644
--- a/drivers/net/wireless/b43/sdio.c
+++ b/drivers/net/wireless/broadcom/b43/sdio.c
diff --git a/drivers/net/wireless/b43/sdio.h b/drivers/net/wireless/broadcom/b43/sdio.h
index 1e93926f388f..1e93926f388f 100644
--- a/drivers/net/wireless/b43/sdio.h
+++ b/drivers/net/wireless/broadcom/b43/sdio.h
diff --git a/drivers/net/wireless/b43/sysfs.c b/drivers/net/wireless/broadcom/b43/sysfs.c
index 3190493bd07f..3190493bd07f 100644
--- a/drivers/net/wireless/b43/sysfs.c
+++ b/drivers/net/wireless/broadcom/b43/sysfs.c
diff --git a/drivers/net/wireless/b43/sysfs.h b/drivers/net/wireless/broadcom/b43/sysfs.h
index 12bda9ef1a85..12bda9ef1a85 100644
--- a/drivers/net/wireless/b43/sysfs.h
+++ b/drivers/net/wireless/broadcom/b43/sysfs.h
diff --git a/drivers/net/wireless/b43/tables.c b/drivers/net/wireless/broadcom/b43/tables.c
index ea288df8aee9..ea288df8aee9 100644
--- a/drivers/net/wireless/b43/tables.c
+++ b/drivers/net/wireless/broadcom/b43/tables.c
diff --git a/drivers/net/wireless/b43/tables.h b/drivers/net/wireless/broadcom/b43/tables.h
index 80e73c7cbac5..80e73c7cbac5 100644
--- a/drivers/net/wireless/b43/tables.h
+++ b/drivers/net/wireless/broadcom/b43/tables.h
diff --git a/drivers/net/wireless/b43/tables_lpphy.c b/drivers/net/wireless/broadcom/b43/tables_lpphy.c
index cff187c5616d..cff187c5616d 100644
--- a/drivers/net/wireless/b43/tables_lpphy.c
+++ b/drivers/net/wireless/broadcom/b43/tables_lpphy.c
diff --git a/drivers/net/wireless/b43/tables_lpphy.h b/drivers/net/wireless/broadcom/b43/tables_lpphy.h
index 84f1d265f657..84f1d265f657 100644
--- a/drivers/net/wireless/b43/tables_lpphy.h
+++ b/drivers/net/wireless/broadcom/b43/tables_lpphy.h
diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/broadcom/b43/tables_nphy.c
index b2f0d245bcf3..b2f0d245bcf3 100644
--- a/drivers/net/wireless/b43/tables_nphy.c
+++ b/drivers/net/wireless/broadcom/b43/tables_nphy.c
diff --git a/drivers/net/wireless/b43/tables_nphy.h b/drivers/net/wireless/broadcom/b43/tables_nphy.h
index b51f386db02f..b51f386db02f 100644
--- a/drivers/net/wireless/b43/tables_nphy.h
+++ b/drivers/net/wireless/broadcom/b43/tables_nphy.h
diff --git a/drivers/net/wireless/b43/tables_phy_ht.c b/drivers/net/wireless/broadcom/b43/tables_phy_ht.c
index 176c49d74ef4..176c49d74ef4 100644
--- a/drivers/net/wireless/b43/tables_phy_ht.c
+++ b/drivers/net/wireless/broadcom/b43/tables_phy_ht.c
diff --git a/drivers/net/wireless/b43/tables_phy_ht.h b/drivers/net/wireless/broadcom/b43/tables_phy_ht.h
index 1b5ef2bc770c..1b5ef2bc770c 100644
--- a/drivers/net/wireless/b43/tables_phy_ht.h
+++ b/drivers/net/wireless/broadcom/b43/tables_phy_ht.h
diff --git a/drivers/net/wireless/b43/tables_phy_lcn.c b/drivers/net/wireless/broadcom/b43/tables_phy_lcn.c
index e347b8d80ea4..e347b8d80ea4 100644
--- a/drivers/net/wireless/b43/tables_phy_lcn.c
+++ b/drivers/net/wireless/broadcom/b43/tables_phy_lcn.c
diff --git a/drivers/net/wireless/b43/tables_phy_lcn.h b/drivers/net/wireless/broadcom/b43/tables_phy_lcn.h
index caff9db6831f..caff9db6831f 100644
--- a/drivers/net/wireless/b43/tables_phy_lcn.h
+++ b/drivers/net/wireless/broadcom/b43/tables_phy_lcn.h
diff --git a/drivers/net/wireless/b43/wa.c b/drivers/net/wireless/broadcom/b43/wa.c
index c218c08fb2f5..c218c08fb2f5 100644
--- a/drivers/net/wireless/b43/wa.c
+++ b/drivers/net/wireless/broadcom/b43/wa.c
diff --git a/drivers/net/wireless/b43/wa.h b/drivers/net/wireless/broadcom/b43/wa.h
index e163c5e56e81..e163c5e56e81 100644
--- a/drivers/net/wireless/b43/wa.h
+++ b/drivers/net/wireless/broadcom/b43/wa.h
diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/broadcom/b43/xmit.c
index 426dc13c44cd..426dc13c44cd 100644
--- a/drivers/net/wireless/b43/xmit.c
+++ b/drivers/net/wireless/broadcom/b43/xmit.c
diff --git a/drivers/net/wireless/b43/xmit.h b/drivers/net/wireless/broadcom/b43/xmit.h
index ba6115308068..ba6115308068 100644
--- a/drivers/net/wireless/b43/xmit.h
+++ b/drivers/net/wireless/broadcom/b43/xmit.h
diff --git a/drivers/net/wireless/b43legacy/Kconfig b/drivers/net/wireless/broadcom/b43legacy/Kconfig
index 1ffa28835c58..1ffa28835c58 100644
--- a/drivers/net/wireless/b43legacy/Kconfig
+++ b/drivers/net/wireless/broadcom/b43legacy/Kconfig
diff --git a/drivers/net/wireless/b43legacy/Makefile b/drivers/net/wireless/broadcom/b43legacy/Makefile
index 227a77e84362..227a77e84362 100644
--- a/drivers/net/wireless/b43legacy/Makefile
+++ b/drivers/net/wireless/broadcom/b43legacy/Makefile
diff --git a/drivers/net/wireless/b43legacy/b43legacy.h b/drivers/net/wireless/broadcom/b43legacy/b43legacy.h
index 482476fdb1f3..482476fdb1f3 100644
--- a/drivers/net/wireless/b43legacy/b43legacy.h
+++ b/drivers/net/wireless/broadcom/b43legacy/b43legacy.h
diff --git a/drivers/net/wireless/b43legacy/debugfs.c b/drivers/net/wireless/broadcom/b43legacy/debugfs.c
index 090910ea259e..090910ea259e 100644
--- a/drivers/net/wireless/b43legacy/debugfs.c
+++ b/drivers/net/wireless/broadcom/b43legacy/debugfs.c
diff --git a/drivers/net/wireless/b43legacy/debugfs.h b/drivers/net/wireless/broadcom/b43legacy/debugfs.h
index 9ee32158b947..9ee32158b947 100644
--- a/drivers/net/wireless/b43legacy/debugfs.h
+++ b/drivers/net/wireless/broadcom/b43legacy/debugfs.h
diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/broadcom/b43legacy/dma.c
index f9dd892b9f27..f9dd892b9f27 100644
--- a/drivers/net/wireless/b43legacy/dma.c
+++ b/drivers/net/wireless/broadcom/b43legacy/dma.c
diff --git a/drivers/net/wireless/b43legacy/dma.h b/drivers/net/wireless/broadcom/b43legacy/dma.h
index c3282f906bc7..c3282f906bc7 100644
--- a/drivers/net/wireless/b43legacy/dma.h
+++ b/drivers/net/wireless/broadcom/b43legacy/dma.h
diff --git a/drivers/net/wireless/b43legacy/ilt.c b/drivers/net/wireless/broadcom/b43legacy/ilt.c
index ee5682e54204..ee5682e54204 100644
--- a/drivers/net/wireless/b43legacy/ilt.c
+++ b/drivers/net/wireless/broadcom/b43legacy/ilt.c
diff --git a/drivers/net/wireless/b43legacy/ilt.h b/drivers/net/wireless/broadcom/b43legacy/ilt.h
index 48bcf37eccb8..48bcf37eccb8 100644
--- a/drivers/net/wireless/b43legacy/ilt.h
+++ b/drivers/net/wireless/broadcom/b43legacy/ilt.h
diff --git a/drivers/net/wireless/b43legacy/leds.c b/drivers/net/wireless/broadcom/b43legacy/leds.c
index fd4565389c77..fd4565389c77 100644
--- a/drivers/net/wireless/b43legacy/leds.c
+++ b/drivers/net/wireless/broadcom/b43legacy/leds.c
diff --git a/drivers/net/wireless/b43legacy/leds.h b/drivers/net/wireless/broadcom/b43legacy/leds.h
index 9ff6750dc57f..9ff6750dc57f 100644
--- a/drivers/net/wireless/b43legacy/leds.h
+++ b/drivers/net/wireless/broadcom/b43legacy/leds.h
diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/broadcom/b43legacy/main.c
index afc1fb3e38df..afc1fb3e38df 100644
--- a/drivers/net/wireless/b43legacy/main.c
+++ b/drivers/net/wireless/broadcom/b43legacy/main.c
diff --git a/drivers/net/wireless/b43legacy/main.h b/drivers/net/wireless/broadcom/b43legacy/main.h
index b74a058d7bac..b74a058d7bac 100644
--- a/drivers/net/wireless/b43legacy/main.h
+++ b/drivers/net/wireless/broadcom/b43legacy/main.h
diff --git a/drivers/net/wireless/b43legacy/phy.c b/drivers/net/wireless/broadcom/b43legacy/phy.c
index 995c7d0c212a..995c7d0c212a 100644
--- a/drivers/net/wireless/b43legacy/phy.c
+++ b/drivers/net/wireless/broadcom/b43legacy/phy.c
diff --git a/drivers/net/wireless/b43legacy/phy.h b/drivers/net/wireless/broadcom/b43legacy/phy.h
index 831a7a4760e5..831a7a4760e5 100644
--- a/drivers/net/wireless/b43legacy/phy.h
+++ b/drivers/net/wireless/broadcom/b43legacy/phy.h
diff --git a/drivers/net/wireless/b43legacy/pio.c b/drivers/net/wireless/broadcom/b43legacy/pio.c
index 282eedec675e..282eedec675e 100644
--- a/drivers/net/wireless/b43legacy/pio.c
+++ b/drivers/net/wireless/broadcom/b43legacy/pio.c
diff --git a/drivers/net/wireless/b43legacy/pio.h b/drivers/net/wireless/broadcom/b43legacy/pio.h
index 8e6773ea6e75..8e6773ea6e75 100644
--- a/drivers/net/wireless/b43legacy/pio.h
+++ b/drivers/net/wireless/broadcom/b43legacy/pio.h
diff --git a/drivers/net/wireless/b43legacy/radio.c b/drivers/net/wireless/broadcom/b43legacy/radio.c
index 9501420340a9..9501420340a9 100644
--- a/drivers/net/wireless/b43legacy/radio.c
+++ b/drivers/net/wireless/broadcom/b43legacy/radio.c
diff --git a/drivers/net/wireless/b43legacy/radio.h b/drivers/net/wireless/broadcom/b43legacy/radio.h
index dd2976d1d561..dd2976d1d561 100644
--- a/drivers/net/wireless/b43legacy/radio.h
+++ b/drivers/net/wireless/broadcom/b43legacy/radio.h
diff --git a/drivers/net/wireless/b43legacy/rfkill.c b/drivers/net/wireless/broadcom/b43legacy/rfkill.c
index 7c1bdbc02569..7c1bdbc02569 100644
--- a/drivers/net/wireless/b43legacy/rfkill.c
+++ b/drivers/net/wireless/broadcom/b43legacy/rfkill.c
diff --git a/drivers/net/wireless/b43legacy/rfkill.h b/drivers/net/wireless/broadcom/b43legacy/rfkill.h
index 75585571c544..75585571c544 100644
--- a/drivers/net/wireless/b43legacy/rfkill.h
+++ b/drivers/net/wireless/broadcom/b43legacy/rfkill.h
diff --git a/drivers/net/wireless/b43legacy/sysfs.c b/drivers/net/wireless/broadcom/b43legacy/sysfs.c
index 2a1da15c913b..2a1da15c913b 100644
--- a/drivers/net/wireless/b43legacy/sysfs.c
+++ b/drivers/net/wireless/broadcom/b43legacy/sysfs.c
diff --git a/drivers/net/wireless/b43legacy/sysfs.h b/drivers/net/wireless/broadcom/b43legacy/sysfs.h
index 417d509803c7..417d509803c7 100644
--- a/drivers/net/wireless/b43legacy/sysfs.h
+++ b/drivers/net/wireless/broadcom/b43legacy/sysfs.h
diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/broadcom/b43legacy/xmit.c
index 34bf3f0b729f..34bf3f0b729f 100644
--- a/drivers/net/wireless/b43legacy/xmit.c
+++ b/drivers/net/wireless/broadcom/b43legacy/xmit.c
diff --git a/drivers/net/wireless/b43legacy/xmit.h b/drivers/net/wireless/broadcom/b43legacy/xmit.h
index 289db00a4a7b..289db00a4a7b 100644
--- a/drivers/net/wireless/b43legacy/xmit.h
+++ b/drivers/net/wireless/broadcom/b43legacy/xmit.h
diff --git a/drivers/net/wireless/brcm80211/Kconfig b/drivers/net/wireless/broadcom/brcm80211/Kconfig
index ab42b1fea03c..ab42b1fea03c 100644
--- a/drivers/net/wireless/brcm80211/Kconfig
+++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig
diff --git a/drivers/net/wireless/brcm80211/Makefile b/drivers/net/wireless/broadcom/brcm80211/Makefile
index b987920e982e..b987920e982e 100644
--- a/drivers/net/wireless/brcm80211/Makefile
+++ b/drivers/net/wireless/broadcom/brcm80211/Makefile
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile
index dc4c75083085..9e4b505ca593 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile
@@ -16,8 +16,8 @@
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ccflags-y += \
- -Idrivers/net/wireless/brcm80211/brcmfmac \
- -Idrivers/net/wireless/brcm80211/include
+ -Idrivers/net/wireless/broadcom/brcm80211/brcmfmac \
+ -Idrivers/net/wireless/broadcom/brcm80211/include
ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c
index 288c84e7c56b..6af658e443e4 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c
@@ -187,7 +187,8 @@ retry:
goto retry;
if (id != bcdc->reqid) {
brcmf_err("%s: unexpected request id %d (expected %d)\n",
- brcmf_ifname(drvr, ifidx), id, bcdc->reqid);
+ brcmf_ifname(brcmf_get_ifp(drvr, ifidx)), id,
+ bcdc->reqid);
ret = -EINVAL;
goto done;
}
@@ -234,7 +235,8 @@ brcmf_proto_bcdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
if (id != bcdc->reqid) {
brcmf_err("%s: unexpected request id %d (expected %d)\n",
- brcmf_ifname(drvr, ifidx), id, bcdc->reqid);
+ brcmf_ifname(brcmf_get_ifp(drvr, ifidx)), id,
+ bcdc->reqid);
ret = -EINVAL;
goto done;
}
@@ -298,13 +300,13 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws,
if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) !=
BCDC_PROTO_VER) {
brcmf_err("%s: non-BCDC packet received, flags 0x%x\n",
- brcmf_ifname(drvr, tmp_if->ifidx), h->flags);
+ brcmf_ifname(tmp_if), h->flags);
return -EBADE;
}
if (h->flags & BCDC_FLAG_SUM_GOOD) {
brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n",
- brcmf_ifname(drvr, tmp_if->ifidx), h->flags);
+ brcmf_ifname(tmp_if), h->flags);
pktbuf->ip_summed = CHECKSUM_UNNECESSARY;
}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h
index 6003179c0ceb..6003179c0ceb 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
index 410a6645d316..53637399bb99 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
@@ -47,6 +47,8 @@
#include "debug.h"
#include "sdio.h"
#include "of.h"
+#include "core.h"
+#include "common.h"
#define SDIOH_API_ACCESS_RETRY_LIMIT 2
@@ -57,7 +59,6 @@
/* Maximum milliseconds to wait for F2 to come up */
#define SDIO_WAIT_F2RDY 3000
-#define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */
#define BRCMF_DEFAULT_RXGLOM_SIZE 32 /* max rx frames in glom chain */
struct brcmf_sdiod_freezer {
@@ -68,10 +69,6 @@ struct brcmf_sdiod_freezer {
struct completion resumed;
};
-static int brcmf_sdiod_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE;
-module_param_named(txglomsz, brcmf_sdiod_txglomsz, int, 0);
-MODULE_PARM_DESC(txglomsz, "maximum tx packet chain size [SDIO]");
-
static irqreturn_t brcmf_sdiod_oob_irqhandler(int irq, void *dev_id)
{
struct brcmf_bus *bus_if = dev_get_drvdata(dev_id);
@@ -890,7 +887,8 @@ static void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev)
if (!sdiodev->sg_support)
return;
- nents = max_t(uint, BRCMF_DEFAULT_RXGLOM_SIZE, brcmf_sdiod_txglomsz);
+ nents = max_t(uint, BRCMF_DEFAULT_RXGLOM_SIZE,
+ sdiodev->bus_if->drvr->settings->sdiod_txglomsz);
nents += (nents >> 4) + 1;
WARN_ON(nents > sdiodev->max_segment_count);
@@ -902,7 +900,7 @@ static void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev)
sdiodev->sg_support = false;
}
- sdiodev->txglomsz = brcmf_sdiod_txglomsz;
+ sdiodev->txglomsz = sdiodev->bus_if->drvr->settings->sdiod_txglomsz;
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c
index 4e33f96b3dd1..14a70d4b4e86 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c
@@ -29,7 +29,7 @@
#include "cfg80211.h"
/* T1 start SCO/eSCO priority suppression */
-#define BRCMF_BTCOEX_OPPR_WIN_TIME 2000
+#define BRCMF_BTCOEX_OPPR_WIN_TIME msecs_to_jiffies(2000)
/* BT registers values during DHCP */
#define BRCMF_BT_DHCP_REG50 0x8022
@@ -314,8 +314,7 @@ static void brcmf_btcoex_handler(struct work_struct *work)
} else {
btci->timeout -= BRCMF_BTCOEX_OPPR_WIN_TIME;
mod_timer(&btci->timer,
- jiffies +
- msecs_to_jiffies(BRCMF_BTCOEX_OPPR_WIN_TIME));
+ jiffies + BRCMF_BTCOEX_OPPR_WIN_TIME);
}
btci->timer_on = true;
break;
@@ -328,12 +327,11 @@ static void brcmf_btcoex_handler(struct work_struct *work)
/* DHCP is not over yet, start lowering BT priority */
brcmf_dbg(INFO, "DHCP T1:%d expired\n",
- BRCMF_BTCOEX_OPPR_WIN_TIME);
+ jiffies_to_msecs(BRCMF_BTCOEX_OPPR_WIN_TIME));
brcmf_btcoex_boost_wifi(btci, true);
btci->bt_state = BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT;
- mod_timer(&btci->timer,
- jiffies + msecs_to_jiffies(btci->timeout));
+ mod_timer(&btci->timer, jiffies + btci->timeout);
btci->timer_on = true;
break;
@@ -477,7 +475,7 @@ int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif,
return -EBUSY;
/* Start BT timer only for SCO connection */
if (brcmf_btcoex_is_sco_active(ifp)) {
- btci->timeout = duration;
+ btci->timeout = msecs_to_jiffies(duration);
btci->vif = vif;
brcmf_btcoex_dhcp_start(btci);
}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.h
index 19647c68aa9e..19647c68aa9e 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.h
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
index 230cad788ace..36093f93bfbe 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bus.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
@@ -137,7 +137,7 @@ struct brcmf_bus {
bool always_use_fws_queue;
bool wowl_supported;
- struct brcmf_bus_ops *ops;
+ const struct brcmf_bus_ops *ops;
struct brcmf_bus_msgbuf *msgbuf;
};
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index deb5f78dcacc..7b01e4ddb315 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -91,6 +91,12 @@
#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400
#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20
+#define BRCMF_SCAN_CHANNEL_TIME 40
+#define BRCMF_SCAN_UNASSOC_TIME 40
+#define BRCMF_SCAN_PASSIVE_TIME 120
+
+#define BRCMF_ND_INFO_TIMEOUT msecs_to_jiffies(2000)
+
#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
(sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
@@ -232,11 +238,6 @@ struct parsed_vndr_ies {
struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
};
-static int brcmf_roamoff;
-module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR);
-MODULE_PARM_DESC(roamoff, "do not use internal roaming engine");
-
-
static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
struct cfg80211_chan_def *ch)
{
@@ -392,15 +393,23 @@ static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg,
{
int iftype_num[NUM_NL80211_IFTYPES];
struct brcmf_cfg80211_vif *pos;
+ bool check_combos = false;
+ int ret = 0;
memset(&iftype_num[0], 0, sizeof(iftype_num));
list_for_each_entry(pos, &cfg->vif_list, list)
- if (pos == vif)
+ if (pos == vif) {
iftype_num[new_type]++;
- else
+ } else {
+ /* concurrent interfaces so need check combinations */
+ check_combos = true;
iftype_num[pos->wdev.iftype]++;
+ }
- return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
+ if (check_combos)
+ ret = cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
+
+ return ret;
}
static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg,
@@ -559,7 +568,7 @@ struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name,
/* wait for firmware event */
err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
- msecs_to_jiffies(1500));
+ BRCMF_VIF_EVENT_TIMEOUT);
brcmf_cfg80211_arm_vif_event(cfg, NULL);
if (!err) {
brcmf_err("timeout occurred\n");
@@ -776,7 +785,8 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
s32 ap = 0;
s32 err = 0;
- brcmf_dbg(TRACE, "Enter, idx=%d, type=%d\n", ifp->bssidx, type);
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, type=%d\n", ifp->bsscfgidx,
+ type);
/* WAR: There are a number of p2p interface related problems which
* need to be handled initially (before doing the validate).
@@ -945,7 +955,7 @@ static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
static s32
brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
- struct cfg80211_scan_request *request, u16 action)
+ struct cfg80211_scan_request *request)
{
s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
offsetof(struct brcmf_escan_params_le, params_le);
@@ -959,7 +969,7 @@ brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
/* Allocate space for populating ssids in struct */
- params_size += sizeof(struct brcmf_ssid) * request->n_ssids;
+ params_size += sizeof(struct brcmf_ssid_le) * request->n_ssids;
}
params = kzalloc(params_size, GFP_KERNEL);
@@ -970,7 +980,7 @@ brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
brcmf_escan_prep(cfg, &params->params_le, request);
params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
- params->action = cpu_to_le16(action);
+ params->action = cpu_to_le16(WL_ESCAN_ACTION_START);
params->sync_id = cpu_to_le16(0x1234);
err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
@@ -1012,7 +1022,7 @@ brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
results->count = 0;
results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
- err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START);
+ err = escan->run(cfg, ifp, request);
if (err)
brcmf_scan_config_mpc(ifp, 1);
return err;
@@ -1026,11 +1036,11 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
struct brcmf_if *ifp = vif->ifp;
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
struct cfg80211_ssid *ssids;
- struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int;
u32 passive_scan;
bool escan_req;
bool spec_scan;
s32 err;
+ struct brcmf_ssid_le ssid_le;
u32 SSID_len;
brcmf_dbg(SCAN, "START ESCAN\n");
@@ -1083,13 +1093,13 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
} else {
brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n",
ssids->ssid, ssids->ssid_len);
- memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
- SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
- sr->ssid_le.SSID_len = cpu_to_le32(0);
+ memset(&ssid_le, 0, sizeof(ssid_le));
+ SSID_len = min_t(u8, sizeof(ssid_le.SSID), ssids->ssid_len);
+ ssid_le.SSID_len = cpu_to_le32(0);
spec_scan = false;
if (SSID_len) {
- memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
- sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
+ memcpy(ssid_le.SSID, ssids->ssid, SSID_len);
+ ssid_le.SSID_len = cpu_to_le32(SSID_len);
spec_scan = true;
} else
brcmf_dbg(SCAN, "Broadcast scan\n");
@@ -1102,12 +1112,12 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
goto scan_out;
}
brcmf_scan_config_mpc(ifp, 0);
- err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
- &sr->ssid_le, sizeof(sr->ssid_le));
+ err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, &ssid_le,
+ sizeof(ssid_le));
if (err) {
if (err == -EBUSY)
brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n",
- sr->ssid_le.SSID);
+ ssid_le.SSID);
else
brcmf_err("WLC_SCAN error (%d)\n", err);
@@ -1260,17 +1270,17 @@ static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
brcmf_dbg(TRACE, "Enter\n");
- if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
+ if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n ");
err = brcmf_fil_cmd_data_set(vif->ifp,
BRCMF_C_DISASSOC, NULL, 0);
if (err) {
brcmf_err("WLC_DISASSOC failed (%d)\n", err);
}
- clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state);
- cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
- true, GFP_KERNEL);
-
+ if ((vif->wdev.iftype == NL80211_IFTYPE_STATION) ||
+ (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT))
+ cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
+ true, GFP_KERNEL);
}
clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
@@ -1291,6 +1301,7 @@ brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
s32 wsec = 0;
s32 bcnprd;
u16 chanspec;
+ u32 ssid_len;
brcmf_dbg(TRACE, "Enter\n");
if (!check_vif_up(ifp->vif))
@@ -1368,17 +1379,15 @@ brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
memset(&join_params, 0, sizeof(struct brcmf_join_params));
/* SSID */
- profile->ssid.SSID_len = min_t(u32, params->ssid_len, 32);
- memcpy(profile->ssid.SSID, params->ssid, profile->ssid.SSID_len);
- memcpy(join_params.ssid_le.SSID, params->ssid, profile->ssid.SSID_len);
- join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
+ ssid_len = min_t(u32, params->ssid_len, IEEE80211_MAX_SSID_LEN);
+ memcpy(join_params.ssid_le.SSID, params->ssid, ssid_len);
+ join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
join_params_size = sizeof(join_params.ssid_le);
/* BSSID */
if (params->bssid) {
memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
- join_params_size = sizeof(join_params.ssid_le) +
- BRCMF_ASSOC_PARAMS_FIXED_SIZE;
+ join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE;
memcpy(profile->bssid, params->bssid, ETH_ALEN);
} else {
eth_broadcast_addr(join_params.params_le.bssid);
@@ -1436,10 +1445,16 @@ brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
struct brcmf_if *ifp = netdev_priv(ndev);
brcmf_dbg(TRACE, "Enter\n");
- if (!check_vif_up(ifp->vif))
- return -EIO;
+ if (!check_vif_up(ifp->vif)) {
+ /* When driver is being unloaded, it can end up here. If an
+ * error is returned then later on a debug trace in the wireless
+ * core module will be printed. To avoid this 0 is returned.
+ */
+ return 0;
+ }
brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING);
+ brcmf_net_setcarrier(ifp, false);
brcmf_dbg(TRACE, "Exit\n");
@@ -1728,7 +1743,6 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
{
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
struct ieee80211_channel *chan = sme->channel;
struct brcmf_join_params join_params;
size_t join_params_size;
@@ -1739,6 +1753,7 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
struct brcmf_ext_join_params_le *ext_join_params;
u16 chanspec;
s32 err = 0;
+ u32 ssid_len;
brcmf_dbg(TRACE, "Enter\n");
if (!check_vif_up(ifp->vif))
@@ -1824,15 +1839,6 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
goto done;
}
- profile->ssid.SSID_len = min_t(u32, (u32)sizeof(profile->ssid.SSID),
- (u32)sme->ssid_len);
- memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len);
- if (profile->ssid.SSID_len < IEEE80211_MAX_SSID_LEN) {
- profile->ssid.SSID[profile->ssid.SSID_len] = 0;
- brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n", profile->ssid.SSID,
- profile->ssid.SSID_len);
- }
-
/* Join with specific BSSID and cached SSID
* If SSID is zero join based on BSSID only
*/
@@ -1845,9 +1851,12 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
err = -ENOMEM;
goto done;
}
- ext_join_params->ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
- memcpy(&ext_join_params->ssid_le.SSID, sme->ssid,
- profile->ssid.SSID_len);
+ ssid_len = min_t(u32, sme->ssid_len, IEEE80211_MAX_SSID_LEN);
+ ext_join_params->ssid_le.SSID_len = cpu_to_le32(ssid_len);
+ memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, ssid_len);
+ if (ssid_len < IEEE80211_MAX_SSID_LEN)
+ brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n",
+ ext_join_params->ssid_le.SSID, ssid_len);
/* Set up join scan parameters */
ext_join_params->scan_le.scan_type = -1;
@@ -1895,8 +1904,8 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
memset(&join_params, 0, sizeof(join_params));
join_params_size = sizeof(join_params.ssid_le);
- memcpy(&join_params.ssid_le.SSID, sme->ssid, profile->ssid.SSID_len);
- join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
+ memcpy(&join_params.ssid_le.SSID, sme->ssid, ssid_len);
+ join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
if (sme->bssid)
memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
@@ -2423,6 +2432,54 @@ static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
}
static s32
+brcmf_cfg80211_get_station_ibss(struct brcmf_if *ifp,
+ struct station_info *sinfo)
+{
+ struct brcmf_scb_val_le scbval;
+ struct brcmf_pktcnt_le pktcnt;
+ s32 err;
+ u32 rate;
+ u32 rssi;
+
+ /* Get the current tx rate */
+ err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
+ if (err < 0) {
+ brcmf_err("BRCMF_C_GET_RATE error (%d)\n", err);
+ return err;
+ }
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
+ sinfo->txrate.legacy = rate * 5;
+
+ memset(&scbval, 0, sizeof(scbval));
+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI, &scbval,
+ sizeof(scbval));
+ if (err) {
+ brcmf_err("BRCMF_C_GET_RSSI error (%d)\n", err);
+ return err;
+ }
+ rssi = le32_to_cpu(scbval.val);
+ sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
+ sinfo->signal = rssi;
+
+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_GET_PKTCNTS, &pktcnt,
+ sizeof(pktcnt));
+ if (err) {
+ brcmf_err("BRCMF_C_GET_GET_PKTCNTS error (%d)\n", err);
+ return err;
+ }
+ sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS) |
+ BIT(NL80211_STA_INFO_RX_DROP_MISC) |
+ BIT(NL80211_STA_INFO_TX_PACKETS) |
+ BIT(NL80211_STA_INFO_TX_FAILED);
+ sinfo->rx_packets = le32_to_cpu(pktcnt.rx_good_pkt);
+ sinfo->rx_dropped_misc = le32_to_cpu(pktcnt.rx_bad_pkt);
+ sinfo->tx_packets = le32_to_cpu(pktcnt.tx_good_pkt);
+ sinfo->tx_failed = le32_to_cpu(pktcnt.tx_bad_pkt);
+
+ return 0;
+}
+
+static s32
brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
const u8 *mac, struct station_info *sinfo)
{
@@ -2439,6 +2496,9 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
if (!check_vif_up(ifp->vif))
return -EIO;
+ if (brcmf_is_ibssmode(ifp->vif))
+ return brcmf_cfg80211_get_station_ibss(ifp, sinfo);
+
memset(&sta_info_le, 0, sizeof(sta_info_le));
memcpy(&sta_info_le, mac, ETH_ALEN);
err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
@@ -2691,8 +2751,8 @@ static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
return err;
}
-static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
- struct net_device *ndev, const u8 *bssid)
+static s32 brcmf_inform_ibss(struct brcmf_cfg80211_info *cfg,
+ struct net_device *ndev, const u8 *bssid)
{
struct wiphy *wiphy = cfg_to_wiphy(cfg);
struct ieee80211_channel *notify_channel;
@@ -2737,6 +2797,7 @@ static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
band = wiphy->bands[IEEE80211_BAND_5GHZ];
freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
+ cfg->channel = freq;
notify_channel = ieee80211_get_channel(wiphy, freq);
notify_capability = le16_to_cpu(bi->capability);
@@ -2775,9 +2836,7 @@ CleanUp:
static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
struct brcmf_if *ifp)
{
- struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev);
struct brcmf_bss_info_le *bi;
- struct brcmf_ssid *ssid;
const struct brcmf_tlv *tim;
u16 beacon_interval;
u8 dtim_period;
@@ -2789,8 +2848,6 @@ static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
if (brcmf_is_ibssmode(ifp->vif))
return err;
- ssid = &profile->ssid;
-
*(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
cfg->extra_buf, WL_EXTRA_BUF_MAX);
@@ -2921,7 +2978,7 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
status = e->status;
if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
- brcmf_err("scan not ready, bssidx=%d\n", ifp->bssidx);
+ brcmf_err("scan not ready, bsscfgidx=%d\n", ifp->bsscfgidx);
return -EPERM;
}
@@ -3013,296 +3070,7 @@ static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
brcmf_cfg80211_escan_timeout_worker);
}
-static __always_inline void brcmf_delay(u32 ms)
-{
- if (ms < 1000 / HZ) {
- cond_resched();
- mdelay(ms);
- } else {
- msleep(ms);
- }
-}
-
-static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
- u8 *pattern, u32 patternsize, u8 *mask,
- u32 packet_offset)
-{
- struct brcmf_fil_wowl_pattern_le *filter;
- u32 masksize;
- u32 patternoffset;
- u8 *buf;
- u32 bufsize;
- s32 ret;
-
- masksize = (patternsize + 7) / 8;
- patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
-
- bufsize = sizeof(*filter) + patternsize + masksize;
- buf = kzalloc(bufsize, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
- filter = (struct brcmf_fil_wowl_pattern_le *)buf;
-
- memcpy(filter->cmd, cmd, 4);
- filter->masksize = cpu_to_le32(masksize);
- filter->offset = cpu_to_le32(packet_offset);
- filter->patternoffset = cpu_to_le32(patternoffset);
- filter->patternsize = cpu_to_le32(patternsize);
- filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
-
- if ((mask) && (masksize))
- memcpy(buf + sizeof(*filter), mask, masksize);
- if ((pattern) && (patternsize))
- memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
-
- ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
-
- kfree(buf);
- return ret;
-}
-
-static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct net_device *ndev = cfg_to_ndev(cfg);
- struct brcmf_if *ifp = netdev_priv(ndev);
-
- brcmf_dbg(TRACE, "Enter\n");
-
- if (cfg->wowl_enabled) {
- brcmf_configure_arp_offload(ifp, true);
- brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
- cfg->pre_wowl_pmmode);
- brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
- brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
- cfg->wowl_enabled = false;
- }
- return 0;
-}
-
-static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
- struct brcmf_if *ifp,
- struct cfg80211_wowlan *wowl)
-{
- u32 wowl_config;
- u32 i;
-
- brcmf_dbg(TRACE, "Suspend, wowl config.\n");
-
- brcmf_configure_arp_offload(ifp, false);
- brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode);
- brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
-
- wowl_config = 0;
- if (wowl->disconnect)
- wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
- if (wowl->magic_pkt)
- wowl_config |= BRCMF_WOWL_MAGIC;
- if ((wowl->patterns) && (wowl->n_patterns)) {
- wowl_config |= BRCMF_WOWL_NET;
- for (i = 0; i < wowl->n_patterns; i++) {
- brcmf_config_wowl_pattern(ifp, "add",
- (u8 *)wowl->patterns[i].pattern,
- wowl->patterns[i].pattern_len,
- (u8 *)wowl->patterns[i].mask,
- wowl->patterns[i].pkt_offset);
- }
- }
- brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
- brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
- brcmf_bus_wowl_config(cfg->pub->bus_if, true);
- cfg->wowl_enabled = true;
-}
-
-static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
- struct cfg80211_wowlan *wowl)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct net_device *ndev = cfg_to_ndev(cfg);
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct brcmf_cfg80211_vif *vif;
-
- brcmf_dbg(TRACE, "Enter\n");
-
- /* if the primary net_device is not READY there is nothing
- * we can do but pray resume goes smoothly.
- */
- if (!check_vif_up(ifp->vif))
- goto exit;
-
- /* end any scanning */
- if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
- brcmf_abort_scanning(cfg);
-
- if (wowl == NULL) {
- brcmf_bus_wowl_config(cfg->pub->bus_if, false);
- list_for_each_entry(vif, &cfg->vif_list, list) {
- if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
- continue;
- /* While going to suspend if associated with AP
- * disassociate from AP to save power while system is
- * in suspended state
- */
- brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED);
- /* Make sure WPA_Supplicant receives all the event
- * generated due to DISASSOC call to the fw to keep
- * the state fw and WPA_Supplicant state consistent
- */
- brcmf_delay(500);
- }
- /* Configure MPC */
- brcmf_set_mpc(ifp, 1);
-
- } else {
- /* Configure WOWL paramaters */
- brcmf_configure_wowl(cfg, ifp, wowl);
- }
-
-exit:
- brcmf_dbg(TRACE, "Exit\n");
- /* clear any scanning activity */
- cfg->scan_status = 0;
- return 0;
-}
-
-static __used s32
-brcmf_update_pmklist(struct net_device *ndev,
- struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
-{
- int i, j;
- u32 pmkid_len;
-
- pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
-
- brcmf_dbg(CONN, "No of elements %d\n", pmkid_len);
- for (i = 0; i < pmkid_len; i++) {
- brcmf_dbg(CONN, "PMKID[%d]: %pM =\n", i,
- &pmk_list->pmkids.pmkid[i].BSSID);
- for (j = 0; j < WLAN_PMKID_LEN; j++)
- brcmf_dbg(CONN, "%02x\n",
- pmk_list->pmkids.pmkid[i].PMKID[j]);
- }
-
- if (!err)
- brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info",
- (char *)pmk_list, sizeof(*pmk_list));
-
- return err;
-}
-
-static s32
-brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
- struct cfg80211_pmksa *pmksa)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
- s32 err = 0;
- u32 pmkid_len, i;
-
- brcmf_dbg(TRACE, "Enter\n");
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
- pmkid_len = le32_to_cpu(pmkids->npmkid);
- for (i = 0; i < pmkid_len; i++)
- if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN))
- break;
- if (i < WL_NUM_PMKIDS_MAX) {
- memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN);
- memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
- if (i == pmkid_len) {
- pmkid_len++;
- pmkids->npmkid = cpu_to_le32(pmkid_len);
- }
- } else
- err = -EINVAL;
-
- brcmf_dbg(CONN, "set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
- pmkids->pmkid[pmkid_len].BSSID);
- for (i = 0; i < WLAN_PMKID_LEN; i++)
- brcmf_dbg(CONN, "%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]);
-
- err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
-
- brcmf_dbg(TRACE, "Exit\n");
- return err;
-}
-
-static s32
-brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
- struct cfg80211_pmksa *pmksa)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_if *ifp = netdev_priv(ndev);
- struct pmkid_list pmkid;
- s32 err = 0;
- u32 pmkid_len, i;
-
- brcmf_dbg(TRACE, "Enter\n");
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
- memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
- memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
-
- brcmf_dbg(CONN, "del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
- &pmkid.pmkid[0].BSSID);
- for (i = 0; i < WLAN_PMKID_LEN; i++)
- brcmf_dbg(CONN, "%02x\n", pmkid.pmkid[0].PMKID[i]);
-
- pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid);
- for (i = 0; i < pmkid_len; i++)
- if (!memcmp
- (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID,
- ETH_ALEN))
- break;
-
- if ((pmkid_len > 0)
- && (i < pmkid_len)) {
- memset(&cfg->pmk_list->pmkids.pmkid[i], 0,
- sizeof(struct pmkid));
- for (; i < (pmkid_len - 1); i++) {
- memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID,
- &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID,
- ETH_ALEN);
- memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID,
- &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID,
- WLAN_PMKID_LEN);
- }
- cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1);
- } else
- err = -EINVAL;
-
- err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
-
- brcmf_dbg(TRACE, "Exit\n");
- return err;
-
-}
-
-static s32
-brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct brcmf_if *ifp = netdev_priv(ndev);
- s32 err = 0;
-
- brcmf_dbg(TRACE, "Enter\n");
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
- memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list));
- err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
-
- brcmf_dbg(TRACE, "Exit\n");
- return err;
-
-}
-
-/*
- * PFN result doesn't have all the info which are
- * required by the supplicant
+/* PFN result doesn't have all the info which are required by the supplicant
* (For e.g IEs) Do a target Escan so that sched scan results are reported
* via wl_inform_single_bss in the required format. Escan does require the
* scan request in the form of cfg80211_scan_request. For timebeing, create
@@ -3336,8 +3104,7 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
result_count = le32_to_cpu(pfn_result->count);
status = le32_to_cpu(pfn_result->status);
- /*
- * PFN event is limited to fit 512 bytes so we may get
+ /* PFN event is limited to fit 512 bytes so we may get
* multiple NET_FOUND events. For now place a warning here.
*/
WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
@@ -3439,9 +3206,14 @@ static int brcmf_dev_pno_clean(struct net_device *ndev)
return ret;
}
-static int brcmf_dev_pno_config(struct net_device *ndev)
+static int brcmf_dev_pno_config(struct brcmf_if *ifp,
+ struct cfg80211_sched_scan_request *request)
{
struct brcmf_pno_param_le pfn_param;
+ struct brcmf_pno_macaddr_le pfn_mac;
+ s32 err;
+ u8 *mac_mask;
+ int i;
memset(&pfn_param, 0, sizeof(pfn_param));
pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
@@ -3454,8 +3226,37 @@ static int brcmf_dev_pno_config(struct net_device *ndev)
/* set up pno scan fr */
pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
- return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set",
- &pfn_param, sizeof(pfn_param));
+ err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param,
+ sizeof(pfn_param));
+ if (err) {
+ brcmf_err("pfn_set failed, err=%d\n", err);
+ return err;
+ }
+
+ /* Find out if mac randomization should be turned on */
+ if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR))
+ return 0;
+
+ pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER;
+ pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC;
+
+ memcpy(pfn_mac.mac, request->mac_addr, ETH_ALEN);
+ mac_mask = request->mac_addr_mask;
+ for (i = 0; i < ETH_ALEN; i++) {
+ pfn_mac.mac[i] &= mac_mask[i];
+ pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]);
+ }
+ /* Clear multi bit */
+ pfn_mac.mac[0] &= 0xFE;
+ /* Set locally administered */
+ pfn_mac.mac[0] |= 0x02;
+
+ err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac,
+ sizeof(pfn_mac));
+ if (err)
+ brcmf_err("pfn_macaddr failed, err=%d\n", err);
+
+ return err;
}
static int
@@ -3493,9 +3294,8 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n",
request->ssids[i].ssid);
- /*
- * match_set ssids is a supert set of n_ssid list,
- * so we need not add these set seperately.
+ /* match_set ssids is a supert set of n_ssid list,
+ * so we need not add these set separately.
*/
}
}
@@ -3509,11 +3309,8 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
}
/* configure pno */
- ret = brcmf_dev_pno_config(ndev);
- if (ret < 0) {
- brcmf_err("PNO setup failed!! ret=%d\n", ret);
+ if (brcmf_dev_pno_config(ifp, request))
return -EINVAL;
- }
/* configure each match set */
for (i = 0; i < request->n_match_sets; i++) {
@@ -3563,6 +3360,425 @@ static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
return 0;
}
+static __always_inline void brcmf_delay(u32 ms)
+{
+ if (ms < 1000 / HZ) {
+ cond_resched();
+ mdelay(ms);
+ } else {
+ msleep(ms);
+ }
+}
+
+static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
+ u8 *pattern, u32 patternsize, u8 *mask,
+ u32 packet_offset)
+{
+ struct brcmf_fil_wowl_pattern_le *filter;
+ u32 masksize;
+ u32 patternoffset;
+ u8 *buf;
+ u32 bufsize;
+ s32 ret;
+
+ masksize = (patternsize + 7) / 8;
+ patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
+
+ bufsize = sizeof(*filter) + patternsize + masksize;
+ buf = kzalloc(bufsize, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ filter = (struct brcmf_fil_wowl_pattern_le *)buf;
+
+ memcpy(filter->cmd, cmd, 4);
+ filter->masksize = cpu_to_le32(masksize);
+ filter->offset = cpu_to_le32(packet_offset);
+ filter->patternoffset = cpu_to_le32(patternoffset);
+ filter->patternsize = cpu_to_le32(patternsize);
+ filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
+
+ if ((mask) && (masksize))
+ memcpy(buf + sizeof(*filter), mask, masksize);
+ if ((pattern) && (patternsize))
+ memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
+
+ ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
+
+ kfree(buf);
+ return ret;
+}
+
+static s32
+brcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e,
+ void *data)
+{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+ struct brcmf_pno_scanresults_le *pfn_result;
+ struct brcmf_pno_net_info_le *netinfo;
+
+ brcmf_dbg(SCAN, "Enter\n");
+
+ pfn_result = (struct brcmf_pno_scanresults_le *)data;
+
+ if (e->event_code == BRCMF_E_PFN_NET_LOST) {
+ brcmf_dbg(SCAN, "PFN NET LOST event. Ignore\n");
+ return 0;
+ }
+
+ if (le32_to_cpu(pfn_result->count) < 1) {
+ brcmf_err("Invalid result count, expected 1 (%d)\n",
+ le32_to_cpu(pfn_result->count));
+ return -EINVAL;
+ }
+
+ data += sizeof(struct brcmf_pno_scanresults_le);
+ netinfo = (struct brcmf_pno_net_info_le *)data;
+ memcpy(cfg->wowl.nd->ssid.ssid, netinfo->SSID, netinfo->SSID_len);
+ cfg->wowl.nd->ssid.ssid_len = netinfo->SSID_len;
+ cfg->wowl.nd->n_channels = 1;
+ cfg->wowl.nd->channels[0] =
+ ieee80211_channel_to_frequency(netinfo->channel,
+ netinfo->channel <= CH_MAX_2G_CHANNEL ?
+ NL80211_BAND_2GHZ : NL80211_BAND_5GHZ);
+ cfg->wowl.nd_info->n_matches = 1;
+ cfg->wowl.nd_info->matches[0] = cfg->wowl.nd;
+
+ /* Inform (the resume task) that the net detect information was recvd */
+ cfg->wowl.nd_data_completed = true;
+ wake_up(&cfg->wowl.nd_data_wait);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_wowl_wakeind_le wake_ind_le;
+ struct cfg80211_wowlan_wakeup wakeup_data;
+ struct cfg80211_wowlan_wakeup *wakeup;
+ u32 wakeind;
+ s32 err;
+ int timeout;
+
+ err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le,
+ sizeof(wake_ind_le));
+ if (err) {
+ brcmf_err("Get wowl_wakeind failed, err = %d\n", err);
+ return;
+ }
+
+ wakeind = le32_to_cpu(wake_ind_le.ucode_wakeind);
+ if (wakeind & (BRCMF_WOWL_MAGIC | BRCMF_WOWL_DIS | BRCMF_WOWL_BCN |
+ BRCMF_WOWL_RETR | BRCMF_WOWL_NET |
+ BRCMF_WOWL_PFN_FOUND)) {
+ wakeup = &wakeup_data;
+ memset(&wakeup_data, 0, sizeof(wakeup_data));
+ wakeup_data.pattern_idx = -1;
+
+ if (wakeind & BRCMF_WOWL_MAGIC) {
+ brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_MAGIC\n");
+ wakeup_data.magic_pkt = true;
+ }
+ if (wakeind & BRCMF_WOWL_DIS) {
+ brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_DIS\n");
+ wakeup_data.disconnect = true;
+ }
+ if (wakeind & BRCMF_WOWL_BCN) {
+ brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_BCN\n");
+ wakeup_data.disconnect = true;
+ }
+ if (wakeind & BRCMF_WOWL_RETR) {
+ brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_RETR\n");
+ wakeup_data.disconnect = true;
+ }
+ if (wakeind & BRCMF_WOWL_NET) {
+ brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_NET\n");
+ /* For now always map to pattern 0, no API to get
+ * correct information available at the moment.
+ */
+ wakeup_data.pattern_idx = 0;
+ }
+ if (wakeind & BRCMF_WOWL_PFN_FOUND) {
+ brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_PFN_FOUND\n");
+ timeout = wait_event_timeout(cfg->wowl.nd_data_wait,
+ cfg->wowl.nd_data_completed,
+ BRCMF_ND_INFO_TIMEOUT);
+ if (!timeout)
+ brcmf_err("No result for wowl net detect\n");
+ else
+ wakeup_data.net_detect = cfg->wowl.nd_info;
+ }
+ } else {
+ wakeup = NULL;
+ }
+ cfg80211_report_wowlan_wakeup(&ifp->vif->wdev, wakeup, GFP_KERNEL);
+}
+
+#else
+
+static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
+{
+}
+
+#endif /* CONFIG_PM */
+
+static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct net_device *ndev = cfg_to_ndev(cfg);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ if (cfg->wowl.active) {
+ brcmf_report_wowl_wakeind(wiphy, ifp);
+ brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
+ brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
+ brcmf_configure_arp_offload(ifp, true);
+ brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
+ cfg->wowl.pre_pmmode);
+ cfg->wowl.active = false;
+ if (cfg->wowl.nd_enabled) {
+ brcmf_cfg80211_sched_scan_stop(cfg->wiphy, ifp->ndev);
+ brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
+ brcmf_notify_sched_scan_results);
+ cfg->wowl.nd_enabled = false;
+ }
+ }
+ return 0;
+}
+
+static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_if *ifp,
+ struct cfg80211_wowlan *wowl)
+{
+ u32 wowl_config;
+ u32 i;
+
+ brcmf_dbg(TRACE, "Suspend, wowl config.\n");
+
+ brcmf_configure_arp_offload(ifp, false);
+ brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode);
+ brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
+
+ wowl_config = 0;
+ if (wowl->disconnect)
+ wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
+ if (wowl->magic_pkt)
+ wowl_config |= BRCMF_WOWL_MAGIC;
+ if ((wowl->patterns) && (wowl->n_patterns)) {
+ wowl_config |= BRCMF_WOWL_NET;
+ for (i = 0; i < wowl->n_patterns; i++) {
+ brcmf_config_wowl_pattern(ifp, "add",
+ (u8 *)wowl->patterns[i].pattern,
+ wowl->patterns[i].pattern_len,
+ (u8 *)wowl->patterns[i].mask,
+ wowl->patterns[i].pkt_offset);
+ }
+ }
+ if (wowl->nd_config) {
+ brcmf_cfg80211_sched_scan_start(cfg->wiphy, ifp->ndev,
+ wowl->nd_config);
+ wowl_config |= BRCMF_WOWL_PFN_FOUND;
+
+ cfg->wowl.nd_data_completed = false;
+ cfg->wowl.nd_enabled = true;
+ /* Now reroute the event for PFN to the wowl function. */
+ brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);
+ brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
+ brcmf_wowl_nd_results);
+ }
+ if (!test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
+ wowl_config |= BRCMF_WOWL_UNASSOC;
+
+ brcmf_fil_iovar_data_set(ifp, "wowl_wakeind", "clear", strlen("clear"));
+ brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
+ brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
+ brcmf_bus_wowl_config(cfg->pub->bus_if, true);
+ cfg->wowl.active = true;
+}
+
+static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
+ struct cfg80211_wowlan *wowl)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct net_device *ndev = cfg_to_ndev(cfg);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_cfg80211_vif *vif;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* if the primary net_device is not READY there is nothing
+ * we can do but pray resume goes smoothly.
+ */
+ if (!check_vif_up(ifp->vif))
+ goto exit;
+
+ /* Stop scheduled scan */
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
+ brcmf_cfg80211_sched_scan_stop(wiphy, ndev);
+
+ /* end any scanning */
+ if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
+ brcmf_abort_scanning(cfg);
+
+ if (wowl == NULL) {
+ brcmf_bus_wowl_config(cfg->pub->bus_if, false);
+ list_for_each_entry(vif, &cfg->vif_list, list) {
+ if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
+ continue;
+ /* While going to suspend if associated with AP
+ * disassociate from AP to save power while system is
+ * in suspended state
+ */
+ brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED);
+ /* Make sure WPA_Supplicant receives all the event
+ * generated due to DISASSOC call to the fw to keep
+ * the state fw and WPA_Supplicant state consistent
+ */
+ brcmf_delay(500);
+ }
+ /* Configure MPC */
+ brcmf_set_mpc(ifp, 1);
+
+ } else {
+ /* Configure WOWL paramaters */
+ brcmf_configure_wowl(cfg, ifp, wowl);
+ }
+
+exit:
+ brcmf_dbg(TRACE, "Exit\n");
+ /* clear any scanning activity */
+ cfg->scan_status = 0;
+ return 0;
+}
+
+static __used s32
+brcmf_update_pmklist(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp)
+{
+ struct brcmf_pmk_list_le *pmk_list;
+ int i;
+ u32 npmk;
+ s32 err;
+
+ pmk_list = &cfg->pmk_list;
+ npmk = le32_to_cpu(pmk_list->npmk);
+
+ brcmf_dbg(CONN, "No of elements %d\n", npmk);
+ for (i = 0; i < npmk; i++)
+ brcmf_dbg(CONN, "PMK[%d]: %pM\n", i, &pmk_list->pmk[i].bssid);
+
+ err = brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_list,
+ sizeof(*pmk_list));
+
+ return err;
+}
+
+static s32
+brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_pmksa *pmksa)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
+ s32 err;
+ u32 npmk, i;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ npmk = le32_to_cpu(cfg->pmk_list.npmk);
+ for (i = 0; i < npmk; i++)
+ if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))
+ break;
+ if (i < BRCMF_MAXPMKID) {
+ memcpy(pmk[i].bssid, pmksa->bssid, ETH_ALEN);
+ memcpy(pmk[i].pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
+ if (i == npmk) {
+ npmk++;
+ cfg->pmk_list.npmk = cpu_to_le32(npmk);
+ }
+ } else {
+ brcmf_err("Too many PMKSA entries cached %d\n", npmk);
+ return -EINVAL;
+ }
+
+ brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmk[npmk].bssid);
+ for (i = 0; i < WLAN_PMKID_LEN; i += 4)
+ brcmf_dbg(CONN, "%02x %02x %02x %02x\n", pmk[npmk].pmkid[i],
+ pmk[npmk].pmkid[i + 1], pmk[npmk].pmkid[i + 2],
+ pmk[npmk].pmkid[i + 3]);
+
+ err = brcmf_update_pmklist(cfg, ifp);
+
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+}
+
+static s32
+brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_pmksa *pmksa)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
+ s32 err;
+ u32 npmk, i;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ brcmf_dbg(CONN, "del_pmksa - PMK bssid = %pM\n", &pmksa->bssid);
+
+ npmk = le32_to_cpu(cfg->pmk_list.npmk);
+ for (i = 0; i < npmk; i++)
+ if (!memcmp(&pmksa->bssid, &pmk[i].bssid, ETH_ALEN))
+ break;
+
+ if ((npmk > 0) && (i < npmk)) {
+ for (; i < (npmk - 1); i++) {
+ memcpy(&pmk[i].bssid, &pmk[i + 1].bssid, ETH_ALEN);
+ memcpy(&pmk[i].pmkid, &pmk[i + 1].pmkid,
+ WLAN_PMKID_LEN);
+ }
+ memset(&pmk[i], 0, sizeof(*pmk));
+ cfg->pmk_list.npmk = cpu_to_le32(npmk - 1);
+ } else {
+ brcmf_err("Cache entry not found\n");
+ return -EINVAL;
+ }
+
+ err = brcmf_update_pmklist(cfg, ifp);
+
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+
+}
+
+static s32
+brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(ndev);
+ s32 err;
+
+ brcmf_dbg(TRACE, "Enter\n");
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+ memset(&cfg->pmk_list, 0, sizeof(cfg->pmk_list));
+ err = brcmf_update_pmklist(cfg, ifp);
+
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+
+}
+
static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
{
s32 err;
@@ -3877,7 +4093,8 @@ s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
ifp = vif->ifp;
saved_ie = &vif->saved_ie;
- brcmf_dbg(TRACE, "bssidx %d, pktflag : 0x%02X\n", ifp->bssidx, pktflag);
+ brcmf_dbg(TRACE, "bsscfgidx %d, pktflag : 0x%02X\n", ifp->bsscfgidx,
+ pktflag);
iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
if (!iovar_ie_buf)
return -ENOMEM;
@@ -4183,7 +4400,9 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
}
}
- if (dev_role == NL80211_IFTYPE_AP) {
+ if ((dev_role == NL80211_IFTYPE_AP) &&
+ ((ifp->ifidx == 0) ||
+ !brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB))) {
err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
if (err < 0) {
brcmf_err("BRCMF_C_DOWN error %d\n", err);
@@ -4239,7 +4458,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
brcmf_err("setting ssid failed %d\n", err);
goto exit;
}
- bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
+ bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
bss_enable.enable = cpu_to_le32(1);
err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
sizeof(bss_enable));
@@ -4306,7 +4525,7 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
if (err < 0)
brcmf_err("BRCMF_C_UP error %d\n", err);
} else {
- bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
+ bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
bss_enable.enable = cpu_to_le32(0);
err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
sizeof(bss_enable));
@@ -4700,7 +4919,6 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
vif->wdev.iftype = type;
vif->pm_block = pm_block;
- vif->roam_off = -1;
brcmf_init_prof(&vif->profile);
@@ -5016,9 +5234,9 @@ brcmf_notify_connect_status(struct brcmf_if *ifp,
} else if (brcmf_is_linkup(e)) {
brcmf_dbg(CONN, "Linkup\n");
if (brcmf_is_ibssmode(ifp->vif)) {
+ brcmf_inform_ibss(cfg, ndev, e->addr);
chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
memcpy(profile->bssid, e->addr, ETH_ALEN);
- wl_inform_ibss(cfg, ndev, e->addr);
cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
clear_bit(BRCMF_VIF_STATUS_CONNECTING,
&ifp->vif->sme_state);
@@ -5031,12 +5249,13 @@ brcmf_notify_connect_status(struct brcmf_if *ifp,
brcmf_dbg(CONN, "Linkdown\n");
if (!brcmf_is_ibssmode(ifp->vif)) {
brcmf_bss_connect_done(cfg, ndev, e, false);
+ brcmf_link_down(ifp->vif,
+ brcmf_map_fw_linkdown_reason(e));
+ brcmf_init_prof(ndev_to_prof(ndev));
+ if (ndev != cfg_to_ndev(cfg))
+ complete(&cfg->vif_disabled);
+ brcmf_net_setcarrier(ifp, false);
}
- brcmf_link_down(ifp->vif, brcmf_map_fw_linkdown_reason(e));
- brcmf_init_prof(ndev_to_prof(ndev));
- if (ndev != cfg_to_ndev(cfg))
- complete(&cfg->vif_disabled);
- brcmf_net_setcarrier(ifp, false);
} else if (brcmf_is_nonetwork(cfg, e)) {
if (brcmf_is_ibssmode(ifp->vif))
clear_bit(BRCMF_VIF_STATUS_CONNECTING,
@@ -5092,9 +5311,9 @@ static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
struct brcmf_cfg80211_vif *vif;
- brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfg %u\n",
+ brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfgidx %u\n",
ifevent->action, ifevent->flags, ifevent->ifidx,
- ifevent->bssidx);
+ ifevent->bsscfgidx);
mutex_lock(&event->vif_event_lock);
event->action = ifevent->action;
@@ -5144,7 +5363,6 @@ static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
conf->rts_threshold = (u32)-1;
conf->retry_short = (u32)-1;
conf->retry_long = (u32)-1;
- conf->tx_power = -1;
}
static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
@@ -5191,8 +5409,10 @@ static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
cfg->escan_ioctl_buf = NULL;
kfree(cfg->extra_buf);
cfg->extra_buf = NULL;
- kfree(cfg->pmk_list);
- cfg->pmk_list = NULL;
+ kfree(cfg->wowl.nd);
+ cfg->wowl.nd = NULL;
+ kfree(cfg->wowl.nd_info);
+ cfg->wowl.nd_info = NULL;
}
static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
@@ -5206,8 +5426,13 @@ static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
if (!cfg->extra_buf)
goto init_priv_mem_out;
- cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL);
- if (!cfg->pmk_list)
+ cfg->wowl.nd = kzalloc(sizeof(*cfg->wowl.nd) + sizeof(u32), GFP_KERNEL);
+ if (!cfg->wowl.nd)
+ goto init_priv_mem_out;
+ cfg->wowl.nd_info = kzalloc(sizeof(*cfg->wowl.nd_info) +
+ sizeof(struct cfg80211_wowlan_nd_match *),
+ GFP_KERNEL);
+ if (!cfg->wowl.nd_info)
goto init_priv_mem_out;
return 0;
@@ -5250,35 +5475,34 @@ static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
mutex_init(&event->vif_event_lock);
}
-static s32
-brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout)
+static s32 brcmf_dongle_roam(struct brcmf_if *ifp)
{
- s32 err = 0;
+ s32 err;
+ u32 bcn_timeout;
__le32 roamtrigger[2];
__le32 roam_delta[2];
- /*
- * Setup timeout if Beacons are lost and roam is
- * off to report link down
- */
- if (brcmf_roamoff) {
- err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
- if (err) {
- brcmf_err("bcn_timeout error (%d)\n", err);
- goto dongle_rom_out;
- }
+ /* Configure beacon timeout value based upon roaming setting */
+ if (ifp->drvr->settings->roamoff)
+ bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF;
+ else
+ bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON;
+ err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
+ if (err) {
+ brcmf_err("bcn_timeout error (%d)\n", err);
+ goto roam_setup_done;
}
- /*
- * Enable/Disable built-in roaming to allow supplicant
- * to take care of roaming
+ /* Enable/Disable built-in roaming to allow supplicant to take care of
+ * roaming.
*/
brcmf_dbg(INFO, "Internal Roaming = %s\n",
- brcmf_roamoff ? "Off" : "On");
- err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff));
+ ifp->drvr->settings->roamoff ? "Off" : "On");
+ err = brcmf_fil_iovar_int_set(ifp, "roam_off",
+ ifp->drvr->settings->roamoff);
if (err) {
brcmf_err("roam_off error (%d)\n", err);
- goto dongle_rom_out;
+ goto roam_setup_done;
}
roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
@@ -5287,7 +5511,7 @@ brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout)
(void *)roamtrigger, sizeof(roamtrigger));
if (err) {
brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
- goto dongle_rom_out;
+ goto roam_setup_done;
}
roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
@@ -5296,45 +5520,35 @@ brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout)
(void *)roam_delta, sizeof(roam_delta));
if (err) {
brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
- goto dongle_rom_out;
+ goto roam_setup_done;
}
-dongle_rom_out:
+roam_setup_done:
return err;
}
static s32
-brcmf_dongle_scantime(struct brcmf_if *ifp, s32 scan_assoc_time,
- s32 scan_unassoc_time, s32 scan_passive_time)
+brcmf_dongle_scantime(struct brcmf_if *ifp)
{
s32 err = 0;
err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
- scan_assoc_time);
+ BRCMF_SCAN_CHANNEL_TIME);
if (err) {
- if (err == -EOPNOTSUPP)
- brcmf_dbg(INFO, "Scan assoc time is not supported\n");
- else
- brcmf_err("Scan assoc time error (%d)\n", err);
+ brcmf_err("Scan assoc time error (%d)\n", err);
goto dongle_scantime_out;
}
err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
- scan_unassoc_time);
+ BRCMF_SCAN_UNASSOC_TIME);
if (err) {
- if (err == -EOPNOTSUPP)
- brcmf_dbg(INFO, "Scan unassoc time is not supported\n");
- else
- brcmf_err("Scan unassoc time error (%d)\n", err);
+ brcmf_err("Scan unassoc time error (%d)\n", err);
goto dongle_scantime_out;
}
err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
- scan_passive_time);
+ BRCMF_SCAN_PASSIVE_TIME);
if (err) {
- if (err == -EOPNOTSUPP)
- brcmf_dbg(INFO, "Scan passive time is not supported\n");
- else
- brcmf_err("Scan passive time error (%d)\n", err);
+ brcmf_err("Scan passive time error (%d)\n", err);
goto dongle_scantime_out;
}
@@ -5619,7 +5833,8 @@ static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
}
static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
- u32 bw_cap[2], u32 nchain)
+ u32 bw_cap[2], u32 nchain, u32 txstreams,
+ u32 txbf_bfe_cap, u32 txbf_bfr_cap)
{
__le16 mcs_map;
@@ -5638,6 +5853,25 @@ static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
+
+ /* Beamforming support information */
+ if (txbf_bfe_cap & BRCMF_TXBF_SU_BFE_CAP)
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+ if (txbf_bfe_cap & BRCMF_TXBF_MU_BFE_CAP)
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+ if (txbf_bfr_cap & BRCMF_TXBF_SU_BFR_CAP)
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
+ if (txbf_bfr_cap & BRCMF_TXBF_MU_BFR_CAP)
+ band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+ if ((txbf_bfe_cap || txbf_bfr_cap) && (txstreams > 1)) {
+ band->vht_cap.cap |=
+ (2 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
+ band->vht_cap.cap |= ((txstreams - 1) <<
+ IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT);
+ band->vht_cap.cap |=
+ IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB;
+ }
}
static int brcmf_setup_wiphybands(struct wiphy *wiphy)
@@ -5652,6 +5886,9 @@ static int brcmf_setup_wiphybands(struct wiphy *wiphy)
int err;
s32 i;
struct ieee80211_supported_band *band;
+ u32 txstreams = 0;
+ u32 txbf_bfe_cap = 0;
+ u32 txbf_bfr_cap = 0;
(void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
@@ -5680,6 +5917,14 @@ static int brcmf_setup_wiphybands(struct wiphy *wiphy)
return err;
}
+ if (vhtmode) {
+ (void)brcmf_fil_iovar_int_get(ifp, "txstreams", &txstreams);
+ (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfe_cap",
+ &txbf_bfe_cap);
+ (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfr_cap",
+ &txbf_bfr_cap);
+ }
+
wiphy = cfg_to_wiphy(cfg);
for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
band = wiphy->bands[i];
@@ -5689,7 +5934,8 @@ static int brcmf_setup_wiphybands(struct wiphy *wiphy)
if (nmode)
brcmf_update_ht_cap(band, bw_cap, nchain);
if (vhtmode)
- brcmf_update_vht_cap(band, bw_cap, nchain);
+ brcmf_update_vht_cap(band, bw_cap, nchain, txstreams,
+ txbf_bfe_cap, txbf_bfr_cap);
}
return 0;
@@ -5864,7 +6110,7 @@ static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
}
#ifdef CONFIG_PM
-static const struct wiphy_wowlan_support brcmf_wowlan_support = {
+static struct wiphy_wowlan_support brcmf_wowlan_support = {
.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
.n_patterns = BRCMF_WOWL_MAXPATTERNS,
.pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
@@ -5873,10 +6119,23 @@ static const struct wiphy_wowlan_support brcmf_wowlan_support = {
};
#endif
-static void brcmf_wiphy_wowl_params(struct wiphy *wiphy)
+static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp)
{
#ifdef CONFIG_PM
- /* wowl settings */
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ s32 err;
+ u32 wowl_cap;
+
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
+ err = brcmf_fil_iovar_int_get(ifp, "wowl_cap", &wowl_cap);
+ if (!err) {
+ if (wowl_cap & BRCMF_WOWL_PFN_FOUND) {
+ brcmf_wowlan_support.flags |=
+ WIPHY_WOWLAN_NET_DETECT;
+ init_waitqueue_head(&cfg->wowl.nd_data_wait);
+ }
+ }
+ }
wiphy->wowlan = &brcmf_wowlan_support;
#endif
}
@@ -5893,7 +6152,7 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
- wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
+ wiphy->max_num_pmkids = BRCMF_MAXPMKID;
err = brcmf_setup_ifmodes(wiphy, ifp);
if (err)
@@ -5922,9 +6181,10 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
WIPHY_FLAG_OFFCHAN_TX |
- WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
- WIPHY_FLAG_SUPPORTS_TDLS;
- if (!brcmf_roamoff)
+ WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS))
+ wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
+ if (!ifp->drvr->settings->roamoff)
wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
wiphy->mgmt_stypes = brcmf_txrx_stypes;
wiphy->max_remain_on_channel_duration = 5000;
@@ -5936,8 +6196,7 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
- brcmf_wiphy_wowl_params(wiphy);
-
+ brcmf_wiphy_wowl_params(wiphy, ifp);
err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
sizeof(bandlist));
if (err) {
@@ -6004,8 +6263,7 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
/* make sure RF is ready for work */
brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
- brcmf_dongle_scantime(ifp, WL_SCAN_CHANNEL_TIME,
- WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
+ brcmf_dongle_scantime(ifp);
power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
@@ -6014,7 +6272,7 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
brcmf_dbg(INFO, "power save set to %s\n",
(power_mode ? "enabled" : "disabled"));
- err = brcmf_dongle_roam(ifp, WL_BEACON_TIMEOUT);
+ err = brcmf_dongle_roam(ifp);
if (err)
goto default_conf_out;
err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
@@ -6316,13 +6574,15 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
goto wiphy_unreg_out;
}
- err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
- if (err) {
- brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
- wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
- } else {
- brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
- brcmf_notify_tdls_peer_event);
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) {
+ err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
+ if (err) {
+ brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
+ wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
+ } else {
+ brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
+ brcmf_notify_tdls_peer_event);
+ }
}
/* (re-) activate FWEH event handling */
@@ -6332,6 +6592,15 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
goto wiphy_unreg_out;
}
+ /* Fill in some of the advertised nl80211 supported features */
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_RANDOM_MAC)) {
+ wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
+#ifdef CONFIG_PM
+ if (wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT)
+ wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
+#endif
+ }
+
return cfg;
wiphy_unreg_out:
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
index 6a878c8f883f..40efb539ac26 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
@@ -21,18 +21,12 @@
#include <brcmu_d11.h>
#define WL_NUM_SCAN_MAX 10
-#define WL_NUM_PMKIDS_MAX MAXPMKID
#define WL_TLV_INFO_MAX 1024
#define WL_BSS_INFO_MAX 2048
#define WL_ASSOC_INFO_MAX 512 /* assoc related fil max buf */
#define WL_EXTRA_BUF_MAX 2048
#define WL_ROAM_TRIGGER_LEVEL -75
#define WL_ROAM_DELTA 20
-#define WL_BEACON_TIMEOUT 3
-
-#define WL_SCAN_CHANNEL_TIME 40
-#define WL_SCAN_UNASSOC_TIME 40
-#define WL_SCAN_PASSIVE_TIME 120
#define WL_ESCAN_BUF_SIZE (1024 * 64)
#define WL_ESCAN_TIMER_INTERVAL_MS 10000 /* E-Scan timeout */
@@ -77,6 +71,11 @@
#define BRCMF_MAX_DEFAULT_KEYS 4
+/* beacon loss timeout defaults */
+#define BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON 2
+#define BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF 4
+
+#define BRCMF_VIF_EVENT_TIMEOUT msecs_to_jiffies(1500)
/**
* enum brcmf_scan_status - scan engine status
@@ -97,19 +96,6 @@ struct brcmf_cfg80211_conf {
u32 rts_threshold;
u32 retry_short;
u32 retry_long;
- s32 tx_power;
- struct ieee80211_channel channel;
-};
-
-/* basic structure of scan request */
-struct brcmf_cfg80211_scan_req {
- struct brcmf_ssid_le ssid_le;
-};
-
-/* basic structure of information element */
-struct brcmf_cfg80211_ie {
- u16 offset;
- u8 buf[WL_TLV_INFO_MAX];
};
/* security information with currently associated ap */
@@ -124,13 +110,11 @@ struct brcmf_cfg80211_security {
/**
* struct brcmf_cfg80211_profile - profile information.
*
- * @ssid: ssid of associated/associating ap.
* @bssid: bssid of joined/joining ibss.
* @sec: security information.
* @key: key information
*/
struct brcmf_cfg80211_profile {
- struct brcmf_ssid ssid;
u8 bssid[ETH_ALEN];
struct brcmf_cfg80211_security sec;
struct brcmf_wsec_key key[BRCMF_MAX_DEFAULT_KEYS];
@@ -180,7 +164,6 @@ struct vif_saved_ie {
* @ifp: lower layer interface pointer
* @wdev: wireless device.
* @profile: profile information.
- * @roam_off: roaming state.
* @sme_state: SME state using enum brcmf_vif_status bits.
* @pm_block: power-management blocked.
* @list: linked list.
@@ -191,7 +174,6 @@ struct brcmf_cfg80211_vif {
struct brcmf_if *ifp;
struct wireless_dev wdev;
struct brcmf_cfg80211_profile profile;
- s32 roam_off;
unsigned long sme_state;
bool pm_block;
struct vif_saved_ie saved_ie;
@@ -215,12 +197,6 @@ struct brcmf_cfg80211_assoc_ielen_le {
__le32 resp_len;
};
-/* wpa2 pmk list */
-struct brcmf_cfg80211_pmk_list {
- struct pmkid_list pmkids;
- struct pmkid foo[MAXPMKID - 1];
-};
-
/* dongle escan state */
enum wl_escan_state {
WL_ESCAN_STATE_IDLE,
@@ -233,88 +209,7 @@ struct escan_info {
struct wiphy *wiphy;
struct brcmf_if *ifp;
s32 (*run)(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
- struct cfg80211_scan_request *request, u16 action);
-};
-
-/**
- * struct brcmf_pno_param_le - PNO scan configuration parameters
- *
- * @version: PNO parameters version.
- * @scan_freq: scan frequency.
- * @lost_network_timeout: #sec. to declare discovered network as lost.
- * @flags: Bit field to control features of PFN such as sort criteria auto
- * enable switch and background scan.
- * @rssi_margin: Margin to avoid jitter for choosing a PFN based on RSSI sort
- * criteria.
- * @bestn: number of best networks in each scan.
- * @mscan: number of scans recorded.
- * @repeat: minimum number of scan intervals before scan frequency changes
- * in adaptive scan.
- * @exp: exponent of 2 for maximum scan interval.
- * @slow_freq: slow scan period.
- */
-struct brcmf_pno_param_le {
- __le32 version;
- __le32 scan_freq;
- __le32 lost_network_timeout;
- __le16 flags;
- __le16 rssi_margin;
- u8 bestn;
- u8 mscan;
- u8 repeat;
- u8 exp;
- __le32 slow_freq;
-};
-
-/**
- * struct brcmf_pno_net_param_le - scan parameters per preferred network.
- *
- * @ssid: ssid name and its length.
- * @flags: bit2: hidden.
- * @infra: BSS vs IBSS.
- * @auth: Open vs Closed.
- * @wpa_auth: WPA type.
- * @wsec: wsec value.
- */
-struct brcmf_pno_net_param_le {
- struct brcmf_ssid_le ssid;
- __le32 flags;
- __le32 infra;
- __le32 auth;
- __le32 wpa_auth;
- __le32 wsec;
-};
-
-/**
- * struct brcmf_pno_net_info_le - information per found network.
- *
- * @bssid: BSS network identifier.
- * @channel: channel number only.
- * @SSID_len: length of ssid.
- * @SSID: ssid characters.
- * @RSSI: receive signal strength (in dBm).
- * @timestamp: age in seconds.
- */
-struct brcmf_pno_net_info_le {
- u8 bssid[ETH_ALEN];
- u8 channel;
- u8 SSID_len;
- u8 SSID[32];
- __le16 RSSI;
- __le16 timestamp;
-};
-
-/**
- * struct brcmf_pno_scanresults_le - result returned in PNO NET FOUND event.
- *
- * @version: PNO version identifier.
- * @status: indicates completion status of PNO scan.
- * @count: amount of brcmf_pno_net_info_le entries appended.
- */
-struct brcmf_pno_scanresults_le {
- __le32 version;
- __le32 status;
- __le32 count;
+ struct cfg80211_scan_request *request);
};
/**
@@ -334,6 +229,27 @@ struct brcmf_cfg80211_vif_event {
};
/**
+ * struct brcmf_cfg80211_wowl - wowl related information.
+ *
+ * @active: set on suspend, cleared on resume.
+ * @pre_pmmode: firmware PM mode at entering suspend.
+ * @nd: net dectect data.
+ * @nd_info: helper struct to pass to cfg80211.
+ * @nd_data_wait: wait queue to sync net detect data.
+ * @nd_data_completed: completion for net detect data.
+ * @nd_enabled: net detect enabled.
+ */
+struct brcmf_cfg80211_wowl {
+ bool active;
+ u32 pre_pmmode;
+ struct cfg80211_wowlan_nd_match *nd;
+ struct cfg80211_wowlan_nd_info *nd_info;
+ wait_queue_head_t nd_data_wait;
+ bool nd_data_completed;
+ bool nd_enabled;
+};
+
+/**
* struct brcmf_cfg80211_info - dongle private data of cfg80211 interface
*
* @wiphy: wiphy object for cfg80211 interface.
@@ -343,9 +259,7 @@ struct brcmf_cfg80211_vif_event {
* @scan_request: cfg80211 scan request object.
* @usr_sync: mainly for dongle up/down synchronization.
* @bss_list: bss_list holding scanned ap information.
- * @scan_req_int: internal scan request object.
* @bss_info: bss information for cfg80211 layer.
- * @ie: information element object for internal purpose.
* @conn_info: association info.
* @pmk_list: wpa2 pmk list.
* @scan_status: scan activity on the dongle.
@@ -368,8 +282,7 @@ struct brcmf_cfg80211_vif_event {
* @vif_list: linked list of vif instances.
* @vif_cnt: number of vif instances.
* @vif_event: vif event signalling.
- * @wowl_enabled; set during suspend, is wowl used.
- * @pre_wowl_pmmode: intermediate storage of pm mode during wowl.
+ * @wowl: wowl related information.
*/
struct brcmf_cfg80211_info {
struct wiphy *wiphy;
@@ -378,11 +291,9 @@ struct brcmf_cfg80211_info {
struct brcmf_btcoex_info *btcoex;
struct cfg80211_scan_request *scan_request;
struct mutex usr_sync;
- struct brcmf_cfg80211_scan_req scan_req_int;
struct wl_cfg80211_bss_info *bss_info;
- struct brcmf_cfg80211_ie ie;
struct brcmf_cfg80211_connect_info conn_info;
- struct brcmf_cfg80211_pmk_list *pmk_list;
+ struct brcmf_pmk_list_le pmk_list;
unsigned long scan_status;
struct brcmf_pub *pub;
u32 channel;
@@ -403,9 +314,8 @@ struct brcmf_cfg80211_info {
struct brcmf_cfg80211_vif_event vif_event;
struct completion vif_disabled;
struct brcmu_d11inf d11inf;
- bool wowl_enabled;
- u32 pre_wowl_pmmode;
struct brcmf_assoclist_le assoclist;
+ struct brcmf_cfg80211_wowl wowl;
};
/**
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
index f04833db2fd0..82e4382eb177 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
@@ -681,6 +681,7 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci)
case BRCM_CC_43569_CHIP_ID:
case BRCM_CC_43570_CHIP_ID:
case BRCM_CC_4358_CHIP_ID:
+ case BRCM_CC_4359_CHIP_ID:
case BRCM_CC_43602_CHIP_ID:
case BRCM_CC_4371_CHIP_ID:
return 0x180000;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
index f6b5feea23d2..f6b5feea23d2 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/chip.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
index fe54844c75e0..4265b50faa98 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/common.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
@@ -29,13 +29,53 @@
const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-#define BRCMF_DEFAULT_BCN_TIMEOUT 3
#define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 40
#define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40
/* boost value for RSSI_DELTA in preferred join selection */
#define BRCMF_JOIN_PREF_RSSI_BOOST 8
+#define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */
+
+static int brcmf_sdiod_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE;
+module_param_named(txglomsz, brcmf_sdiod_txglomsz, int, 0);
+MODULE_PARM_DESC(txglomsz, "Maximum tx packet chain size [SDIO]");
+
+/* Debug level configuration. See debug.h for bits, sysfs modifiable */
+int brcmf_msg_level;
+module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(debug, "Level of debug output");
+
+static int brcmf_p2p_enable;
+module_param_named(p2pon, brcmf_p2p_enable, int, 0);
+MODULE_PARM_DESC(p2pon, "Enable legacy p2p management functionality");
+
+static int brcmf_feature_disable;
+module_param_named(feature_disable, brcmf_feature_disable, int, 0);
+MODULE_PARM_DESC(feature_disable, "Disable features");
+
+static char brcmf_firmware_path[BRCMF_FW_ALTPATH_LEN];
+module_param_string(alternative_fw_path, brcmf_firmware_path,
+ BRCMF_FW_ALTPATH_LEN, S_IRUSR);
+MODULE_PARM_DESC(alternative_fw_path, "Alternative firmware path");
+
+static int brcmf_fcmode;
+module_param_named(fcmode, brcmf_fcmode, int, 0);
+MODULE_PARM_DESC(fcmode, "Mode of firmware signalled flow control");
+
+static int brcmf_roamoff;
+module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR);
+MODULE_PARM_DESC(roamoff, "Do not use internal roaming engine");
+
+#ifdef DEBUG
+/* always succeed brcmf_bus_start() */
+static int brcmf_ignore_probe_fail;
+module_param_named(ignore_probe_fail, brcmf_ignore_probe_fail, int, 0);
+MODULE_PARM_DESC(ignore_probe_fail, "always succeed probe for debugging");
+#endif
+
+struct brcmf_mp_global_t brcmf_mp_global;
+
int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
{
s8 eventmask[BRCMF_EVENTING_MASK_LEN];
@@ -107,26 +147,6 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
goto done;
}
- /*
- * Setup timeout if Beacons are lost and roam is off to report
- * link down
- */
- err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout",
- BRCMF_DEFAULT_BCN_TIMEOUT);
- if (err) {
- brcmf_err("bcn_timeout error (%d)\n", err);
- goto done;
- }
-
- /* Enable/Disable build-in roaming to allowed ext supplicant to take
- * of romaing
- */
- err = brcmf_fil_iovar_int_set(ifp, "roam_off", 1);
- if (err) {
- brcmf_err("roam_off error (%d)\n", err);
- goto done;
- }
-
/* Setup join_pref to select target by RSSI(with boost on 5GHz) */
join_pref_params[0].type = BRCMF_JOIN_PREF_RSSI_DELTA;
join_pref_params[0].len = 2;
@@ -174,6 +194,9 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
goto done;
}
+ /* Enable tx beamforming, errors can be ignored (not supported) */
+ (void)brcmf_fil_iovar_int_set(ifp, "txbf", 1);
+
/* do bus specific preinit here */
err = brcmf_bus_preinit(ifp->drvr->bus_if);
done:
@@ -196,3 +219,34 @@ void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...)
va_end(args);
}
#endif
+
+void brcmf_mp_attach(void)
+{
+ strlcpy(brcmf_mp_global.firmware_path, brcmf_firmware_path,
+ BRCMF_FW_ALTPATH_LEN);
+}
+
+int brcmf_mp_device_attach(struct brcmf_pub *drvr)
+{
+ drvr->settings = kzalloc(sizeof(*drvr->settings), GFP_ATOMIC);
+ if (!drvr->settings) {
+ brcmf_err("Failed to alloca storage space for settings\n");
+ return -ENOMEM;
+ }
+
+ drvr->settings->sdiod_txglomsz = brcmf_sdiod_txglomsz;
+ drvr->settings->p2p_enable = !!brcmf_p2p_enable;
+ drvr->settings->feature_disable = brcmf_feature_disable;
+ drvr->settings->fcmode = brcmf_fcmode;
+ drvr->settings->roamoff = !!brcmf_roamoff;
+#ifdef DEBUG
+ drvr->settings->ignore_probe_fail = !!brcmf_ignore_probe_fail;
+#endif
+ return 0;
+}
+
+void brcmf_mp_device_detach(struct brcmf_pub *drvr)
+{
+ kfree(drvr->settings);
+}
+
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
new file mode 100644
index 000000000000..3b0a63b98e99
--- /dev/null
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h
@@ -0,0 +1,79 @@
+/* Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef BRCMFMAC_COMMON_H
+#define BRCMFMAC_COMMON_H
+
+extern const u8 ALLFFMAC[ETH_ALEN];
+
+#define BRCMF_FW_ALTPATH_LEN 256
+
+/* Definitions for the module global and device specific settings are defined
+ * here. Two structs are used for them. brcmf_mp_global_t and brcmf_mp_device.
+ * The mp_global is instantiated once in a global struct and gets initialized
+ * by the common_attach function which should be called before any other
+ * (module) initiliazation takes place. The device specific settings is part
+ * of the drvr struct and should be initialized on every brcmf_attach.
+ */
+
+/**
+ * struct brcmf_mp_global_t - Global module paramaters.
+ *
+ * @firmware_path: Alternative firmware path.
+ */
+struct brcmf_mp_global_t {
+ char firmware_path[BRCMF_FW_ALTPATH_LEN];
+};
+
+extern struct brcmf_mp_global_t brcmf_mp_global;
+
+/**
+ * struct brcmf_mp_device - Device module paramaters.
+ *
+ * @sdiod_txglomsz: SDIO txglom size.
+ * @joinboost_5g_rssi: 5g rssi booost for preferred join selection.
+ * @p2p_enable: Legacy P2P0 enable (old wpa_supplicant).
+ * @feature_disable: Feature_disable bitmask.
+ * @fcmode: FWS flow control.
+ * @roamoff: Firmware roaming off?
+ */
+struct brcmf_mp_device {
+ int sdiod_txglomsz;
+ int joinboost_5g_rssi;
+ bool p2p_enable;
+ int feature_disable;
+ int fcmode;
+ bool roamoff;
+ bool ignore_probe_fail;
+};
+
+void brcmf_mp_attach(void);
+int brcmf_mp_device_attach(struct brcmf_pub *drvr);
+void brcmf_mp_device_detach(struct brcmf_pub *drvr);
+#ifdef DEBUG
+static inline bool brcmf_ignoring_probe_fail(struct brcmf_pub *drvr)
+{
+ return drvr->settings->ignore_probe_fail;
+}
+#else
+static inline bool brcmf_ignoring_probe_fail(struct brcmf_pub *drvr)
+{
+ return false;
+}
+#endif
+
+/* Sets dongle media info (drv_version, mac address). */
+int brcmf_c_preinit_dcmds(struct brcmf_if *ifp);
+
+#endif /* BRCMFMAC_COMMON_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.c
index 7b0e52195a85..7b0e52195a85 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.c
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/commonring.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.h
index b85033611c8d..b85033611c8d 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/commonring.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/commonring.h
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
index b5ab98ee1445..ed9998b69709 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
@@ -17,6 +17,7 @@
#include <linux/kernel.h>
#include <linux/etherdevice.h>
#include <linux/module.h>
+#include <linux/inetdevice.h>
#include <net/cfg80211.h>
#include <net/rtnetlink.h>
#include <brcmu_utils.h>
@@ -39,7 +40,7 @@ MODULE_AUTHOR("Broadcom Corporation");
MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
MODULE_LICENSE("Dual BSD/GPL");
-#define MAX_WAIT_FOR_8021X_TX 50 /* msecs */
+#define MAX_WAIT_FOR_8021X_TX msecs_to_jiffies(50)
/* AMPDU rx reordering definitions */
#define BRCMF_RXREORDER_FLOWID_OFFSET 0
@@ -56,30 +57,13 @@ MODULE_LICENSE("Dual BSD/GPL");
#define BRCMF_BSSIDX_INVALID -1
-/* Error bits */
-int brcmf_msg_level;
-module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR);
-MODULE_PARM_DESC(debug, "level of debug output");
-
-/* P2P0 enable */
-static int brcmf_p2p_enable;
-module_param_named(p2pon, brcmf_p2p_enable, int, 0);
-MODULE_PARM_DESC(p2pon, "enable legacy p2p management functionality");
-
-char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx)
+char *brcmf_ifname(struct brcmf_if *ifp)
{
- if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
- brcmf_err("ifidx %d out of range\n", ifidx);
- return "<if_bad>";
- }
-
- if (drvr->iflist[ifidx] == NULL) {
- brcmf_err("null i/f %d\n", ifidx);
+ if (!ifp)
return "<if_null>";
- }
- if (drvr->iflist[ifidx]->ndev)
- return drvr->iflist[ifidx]->ndev->name;
+ if (ifp->ndev)
+ return ifp->ndev->name;
return "<if_none>";
}
@@ -87,7 +71,7 @@ char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx)
struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx)
{
struct brcmf_if *ifp;
- s32 bssidx;
+ s32 bsscfgidx;
if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
brcmf_err("ifidx %d out of range\n", ifidx);
@@ -95,9 +79,9 @@ struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx)
}
ifp = NULL;
- bssidx = drvr->if2bss[ifidx];
- if (bssidx >= 0)
- ifp = drvr->iflist[bssidx];
+ bsscfgidx = drvr->if2bss[ifidx];
+ if (bsscfgidx >= 0)
+ ifp = drvr->iflist[bsscfgidx];
return ifp;
}
@@ -115,7 +99,7 @@ static void _brcmf_set_multicast_list(struct work_struct *work)
ifp = container_of(work, struct brcmf_if, multicast_work);
- brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx);
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
ndev = ifp->ndev;
@@ -175,7 +159,7 @@ _brcmf_set_mac_address(struct work_struct *work)
ifp = container_of(work, struct brcmf_if, setmacaddr_work);
- brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx);
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
err = brcmf_fil_iovar_data_set(ifp, "cur_etheraddr", ifp->mac_addr,
ETH_ALEN);
@@ -213,7 +197,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
struct brcmf_pub *drvr = ifp->drvr;
struct ethhdr *eh = (struct ethhdr *)(skb->data);
- brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx);
+ brcmf_dbg(DATA, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
/* Can the device send data? */
if (drvr->bus_if->state != BRCMF_BUS_UP) {
@@ -224,27 +208,19 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb,
goto done;
}
- if (!drvr->iflist[ifp->bssidx]) {
- brcmf_err("bad ifidx %d\n", ifp->bssidx);
- netif_stop_queue(ndev);
- dev_kfree_skb(skb);
- ret = -ENODEV;
- goto done;
- }
-
/* Make sure there's enough room for any header */
if (skb_headroom(skb) < drvr->hdrlen) {
struct sk_buff *skb2;
brcmf_dbg(INFO, "%s: insufficient headroom\n",
- brcmf_ifname(drvr, ifp->bssidx));
+ brcmf_ifname(ifp));
drvr->bus_if->tx_realloc++;
skb2 = skb_realloc_headroom(skb, drvr->hdrlen);
dev_kfree_skb(skb);
skb = skb2;
if (skb == NULL) {
brcmf_err("%s: skb_realloc_headroom failed\n",
- brcmf_ifname(drvr, ifp->bssidx));
+ brcmf_ifname(ifp));
ret = -ENOMEM;
goto done;
}
@@ -282,8 +258,8 @@ void brcmf_txflowblock_if(struct brcmf_if *ifp,
if (!ifp || !ifp->ndev)
return;
- brcmf_dbg(TRACE, "enter: idx=%d stop=0x%X reason=%d state=%d\n",
- ifp->bssidx, ifp->netif_stop, reason, state);
+ brcmf_dbg(TRACE, "enter: bsscfgidx=%d stop=0x%X reason=%d state=%d\n",
+ ifp->bsscfgidx, ifp->netif_stop, reason, state);
spin_lock_irqsave(&ifp->netif_stop_lock, flags);
if (state) {
@@ -602,7 +578,7 @@ static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev)
{
struct brcmf_if *ifp = netdev_priv(ndev);
- brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx);
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
return &ifp->stats;
}
@@ -631,10 +607,12 @@ static int brcmf_netdev_stop(struct net_device *ndev)
{
struct brcmf_if *ifp = netdev_priv(ndev);
- brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx);
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
brcmf_cfg80211_down(ndev);
+ brcmf_fil_iovar_data_set(ifp, "arp_hostip_clear", NULL, 0);
+
brcmf_net_setcarrier(ifp, false);
return 0;
@@ -647,7 +625,7 @@ static int brcmf_netdev_open(struct net_device *ndev)
struct brcmf_bus *bus_if = drvr->bus_if;
u32 toe_ol;
- brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx);
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx);
/* If bus is not ready, can't continue */
if (bus_if->state != BRCMF_BUS_UP) {
@@ -689,7 +667,7 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked)
struct net_device *ndev;
s32 err;
- brcmf_dbg(TRACE, "Enter, idx=%d mac=%pM\n", ifp->bssidx,
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d mac=%pM\n", ifp->bsscfgidx,
ifp->mac_addr);
ndev = ifp->ndev;
@@ -721,7 +699,7 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked)
return 0;
fail:
- drvr->iflist[ifp->bssidx] = NULL;
+ drvr->iflist[ifp->bsscfgidx] = NULL;
ndev->netdev_ops = NULL;
free_netdev(ndev);
return -EBADE;
@@ -739,7 +717,8 @@ void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on)
{
struct net_device *ndev;
- brcmf_dbg(TRACE, "Enter, idx=%d carrier=%d\n", ifp->bssidx, on);
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d carrier=%d\n", ifp->bsscfgidx,
+ on);
ndev = ifp->ndev;
brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_DISCONNECTED, !on);
@@ -786,7 +765,7 @@ static int brcmf_net_p2p_attach(struct brcmf_if *ifp)
{
struct net_device *ndev;
- brcmf_dbg(TRACE, "Enter, idx=%d mac=%pM\n", ifp->bssidx,
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d mac=%pM\n", ifp->bsscfgidx,
ifp->mac_addr);
ndev = ifp->ndev;
@@ -805,39 +784,40 @@ static int brcmf_net_p2p_attach(struct brcmf_if *ifp)
return 0;
fail:
- ifp->drvr->iflist[ifp->bssidx] = NULL;
+ ifp->drvr->iflist[ifp->bsscfgidx] = NULL;
ndev->netdev_ops = NULL;
free_netdev(ndev);
return -EBADE;
}
-struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
+struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx,
bool is_p2pdev, char *name, u8 *mac_addr)
{
struct brcmf_if *ifp;
struct net_device *ndev;
- brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifidx);
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", bsscfgidx, ifidx);
- ifp = drvr->iflist[bssidx];
+ ifp = drvr->iflist[bsscfgidx];
/*
* Delete the existing interface before overwriting it
* in case we missed the BRCMF_E_IF_DEL event.
*/
if (ifp) {
- brcmf_err("ERROR: netdev:%s already exists\n",
- ifp->ndev->name);
if (ifidx) {
+ brcmf_err("ERROR: netdev:%s already exists\n",
+ ifp->ndev->name);
netif_stop_queue(ifp->ndev);
brcmf_net_detach(ifp->ndev);
- drvr->iflist[bssidx] = NULL;
+ drvr->iflist[bsscfgidx] = NULL;
} else {
- brcmf_err("ignore IF event\n");
+ brcmf_dbg(INFO, "netdev:%s ignore IF event\n",
+ ifp->ndev->name);
return ERR_PTR(-EINVAL);
}
}
- if (!brcmf_p2p_enable && is_p2pdev) {
+ if (!drvr->settings->p2p_enable && is_p2pdev) {
/* this is P2P_DEVICE interface */
brcmf_dbg(INFO, "allocate non-netdev interface\n");
ifp = kzalloc(sizeof(*ifp), GFP_KERNEL);
@@ -854,15 +834,15 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
ndev->destructor = brcmf_cfg80211_free_netdev;
ifp = netdev_priv(ndev);
ifp->ndev = ndev;
- /* store mapping ifidx to bssidx */
+ /* store mapping ifidx to bsscfgidx */
if (drvr->if2bss[ifidx] == BRCMF_BSSIDX_INVALID)
- drvr->if2bss[ifidx] = bssidx;
+ drvr->if2bss[ifidx] = bsscfgidx;
}
ifp->drvr = drvr;
- drvr->iflist[bssidx] = ifp;
+ drvr->iflist[bsscfgidx] = ifp;
ifp->ifidx = ifidx;
- ifp->bssidx = bssidx;
+ ifp->bsscfgidx = bsscfgidx;
init_waitqueue_head(&ifp->pend_8021x_wait);
spin_lock_init(&ifp->netif_stop_lock);
@@ -876,21 +856,22 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
return ifp;
}
-static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx)
+static void brcmf_del_if(struct brcmf_pub *drvr, s32 bsscfgidx)
{
struct brcmf_if *ifp;
- ifp = drvr->iflist[bssidx];
- drvr->iflist[bssidx] = NULL;
+ ifp = drvr->iflist[bsscfgidx];
+ drvr->iflist[bsscfgidx] = NULL;
if (!ifp) {
- brcmf_err("Null interface, idx=%d\n", bssidx);
+ brcmf_err("Null interface, bsscfgidx=%d\n", bsscfgidx);
return;
}
- brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifp->ifidx);
- if (drvr->if2bss[ifp->ifidx] == bssidx)
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", bsscfgidx,
+ ifp->ifidx);
+ if (drvr->if2bss[ifp->ifidx] == bsscfgidx)
drvr->if2bss[ifp->ifidx] = BRCMF_BSSIDX_INVALID;
if (ifp->ndev) {
- if (bssidx == 0) {
+ if (bsscfgidx == 0) {
if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
rtnl_lock();
brcmf_netdev_stop(ifp->ndev);
@@ -920,12 +901,12 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx)
void brcmf_remove_interface(struct brcmf_if *ifp)
{
- if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bssidx] != ifp))
+ if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bsscfgidx] != ifp))
return;
- brcmf_dbg(TRACE, "Enter, bssidx=%d, ifidx=%d\n", ifp->bssidx,
+ brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", ifp->bsscfgidx,
ifp->ifidx);
brcmf_fws_del_interface(ifp);
- brcmf_del_if(ifp->drvr, ifp->bssidx);
+ brcmf_del_if(ifp->drvr, ifp->bsscfgidx);
}
int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr)
@@ -940,10 +921,10 @@ int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr)
highest = 2;
for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) {
if (drvr->iflist[ifidx]) {
- if (drvr->iflist[ifidx]->bssidx == bsscfgidx)
+ if (drvr->iflist[ifidx]->bsscfgidx == bsscfgidx)
bsscfgidx = highest + 1;
- else if (drvr->iflist[ifidx]->bssidx > highest)
- highest = drvr->iflist[ifidx]->bssidx;
+ else if (drvr->iflist[ifidx]->bsscfgidx > highest)
+ highest = drvr->iflist[ifidx]->bsscfgidx;
} else {
available = true;
}
@@ -952,6 +933,98 @@ int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr)
return available ? bsscfgidx : -ENOMEM;
}
+#ifdef CONFIG_INET
+#define ARPOL_MAX_ENTRIES 8
+static int brcmf_inetaddr_changed(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct brcmf_pub *drvr = container_of(nb, struct brcmf_pub,
+ inetaddr_notifier);
+ struct in_ifaddr *ifa = data;
+ struct net_device *ndev = ifa->ifa_dev->dev;
+ struct brcmf_if *ifp;
+ int idx, i, ret;
+ u32 val;
+ __be32 addr_table[ARPOL_MAX_ENTRIES] = {0};
+
+ /* Find out if the notification is meant for us */
+ for (idx = 0; idx < BRCMF_MAX_IFS; idx++) {
+ ifp = drvr->iflist[idx];
+ if (ifp && ifp->ndev == ndev)
+ break;
+ if (idx == BRCMF_MAX_IFS - 1)
+ return NOTIFY_DONE;
+ }
+
+ /* check if arp offload is supported */
+ ret = brcmf_fil_iovar_int_get(ifp, "arpoe", &val);
+ if (ret)
+ return NOTIFY_OK;
+
+ /* old version only support primary index */
+ ret = brcmf_fil_iovar_int_get(ifp, "arp_version", &val);
+ if (ret)
+ val = 1;
+ if (val == 1)
+ ifp = drvr->iflist[0];
+
+ /* retrieve the table from firmware */
+ ret = brcmf_fil_iovar_data_get(ifp, "arp_hostip", addr_table,
+ sizeof(addr_table));
+ if (ret) {
+ brcmf_err("fail to get arp ip table err:%d\n", ret);
+ return NOTIFY_OK;
+ }
+
+ for (i = 0; i < ARPOL_MAX_ENTRIES; i++)
+ if (ifa->ifa_address == addr_table[i])
+ break;
+
+ switch (action) {
+ case NETDEV_UP:
+ if (i == ARPOL_MAX_ENTRIES) {
+ brcmf_dbg(TRACE, "add %pI4 to arp table\n",
+ &ifa->ifa_address);
+ /* set it directly */
+ ret = brcmf_fil_iovar_data_set(ifp, "arp_hostip",
+ &ifa->ifa_address, sizeof(ifa->ifa_address));
+ if (ret)
+ brcmf_err("add arp ip err %d\n", ret);
+ }
+ break;
+ case NETDEV_DOWN:
+ if (i < ARPOL_MAX_ENTRIES) {
+ addr_table[i] = 0;
+ brcmf_dbg(TRACE, "remove %pI4 from arp table\n",
+ &ifa->ifa_address);
+ /* clear the table in firmware */
+ ret = brcmf_fil_iovar_data_set(ifp, "arp_hostip_clear",
+ NULL, 0);
+ if (ret) {
+ brcmf_err("fail to clear arp ip table err:%d\n",
+ ret);
+ return NOTIFY_OK;
+ }
+ for (i = 0; i < ARPOL_MAX_ENTRIES; i++) {
+ if (addr_table[i] != 0) {
+ brcmf_fil_iovar_data_set(ifp,
+ "arp_hostip", &addr_table[i],
+ sizeof(addr_table[i]));
+ if (ret)
+ brcmf_err("add arp ip err %d\n",
+ ret);
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+#endif
+
int brcmf_attach(struct device *dev)
{
struct brcmf_pub *drvr = NULL;
@@ -975,6 +1048,10 @@ int brcmf_attach(struct device *dev)
drvr->bus_if = dev_get_drvdata(dev);
drvr->bus_if->drvr = drvr;
+ /* Initialize device specific settings */
+ if (brcmf_mp_device_attach(drvr))
+ goto fail;
+
/* attach debug facilities */
brcmf_debug_attach(drvr);
@@ -1067,7 +1144,7 @@ int brcmf_bus_start(struct device *dev)
brcmf_fws_add_interface(ifp);
drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev,
- brcmf_p2p_enable);
+ drvr->settings->p2p_enable);
if (drvr->config == NULL) {
ret = -ENOMEM;
goto fail;
@@ -1075,11 +1152,20 @@ int brcmf_bus_start(struct device *dev)
ret = brcmf_net_attach(ifp, false);
- if ((!ret) && (brcmf_p2p_enable)) {
+ if ((!ret) && (drvr->settings->p2p_enable)) {
p2p_ifp = drvr->iflist[1];
if (p2p_ifp)
ret = brcmf_net_p2p_attach(p2p_ifp);
}
+
+ if (ret)
+ goto fail;
+
+#ifdef CONFIG_INET
+ drvr->inetaddr_notifier.notifier_call = brcmf_inetaddr_changed;
+ ret = register_inetaddr_notifier(&drvr->inetaddr_notifier);
+#endif
+
fail:
if (ret < 0) {
brcmf_err("failed: %d\n", ret);
@@ -1095,6 +1181,10 @@ fail:
brcmf_net_detach(ifp->ndev);
if (p2p_ifp)
brcmf_net_detach(p2p_ifp->ndev);
+ drvr->iflist[0] = NULL;
+ drvr->iflist[1] = NULL;
+ if (brcmf_ignoring_probe_fail(drvr))
+ ret = 0;
return ret;
}
return 0;
@@ -1143,6 +1233,10 @@ void brcmf_detach(struct device *dev)
if (drvr == NULL)
return;
+#ifdef CONFIG_INET
+ unregister_inetaddr_notifier(&drvr->inetaddr_notifier);
+#endif
+
/* stop firmware event handling */
brcmf_fweh_detach(drvr);
if (drvr->config)
@@ -1162,6 +1256,8 @@ void brcmf_detach(struct device *dev)
brcmf_proto_detach(drvr);
+ brcmf_mp_device_detach(drvr);
+
brcmf_debug_detach(drvr);
bus_if->drvr = NULL;
kfree(drvr);
@@ -1186,7 +1282,7 @@ int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp)
err = wait_event_timeout(ifp->pend_8021x_wait,
!brcmf_get_pend_8021x_cnt(ifp),
- msecs_to_jiffies(MAX_WAIT_FOR_8021X_TX));
+ MAX_WAIT_FOR_8021X_TX);
WARN_ON(!err);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
index 2f9101b2ad34..8f39435f976f 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
@@ -69,8 +69,8 @@ struct brcmf_ampdu_rx_reorder {
/* Forward decls for struct brcmf_pub (see below) */
struct brcmf_proto; /* device communication protocol info */
-struct brcmf_cfg80211_dev; /* cfg80211 device info */
-struct brcmf_fws_info; /* firmware signalling info */
+struct brcmf_fws_info; /* firmware signalling info */
+struct brcmf_mp_device; /* module paramateres, device specific */
/*
* struct brcmf_rev_info
@@ -141,6 +141,9 @@ struct brcmf_pub {
#ifdef DEBUG
struct dentry *dbgfs_dir;
#endif
+
+ struct notifier_block inetaddr_notifier;
+ struct brcmf_mp_device *settings;
};
/* forward declarations */
@@ -174,7 +177,7 @@ enum brcmf_netif_stop_reason {
* @multicast_work: worker object for multicast provisioning.
* @fws_desc: interface specific firmware-signalling descriptor.
* @ifidx: interface index in device firmware.
- * @bssidx: index of bss associated with this interface.
+ * @bsscfgidx: index of bss associated with this interface.
* @mac_addr: assigned mac address.
* @netif_stop: bitmap indicates reason why netif queues are stopped.
* @netif_stop_lock: spinlock for update netif_stop from multiple sources.
@@ -190,7 +193,7 @@ struct brcmf_if {
struct work_struct multicast_work;
struct brcmf_fws_mac_descriptor *fws_desc;
int ifidx;
- s32 bssidx;
+ s32 bsscfgidx;
u8 mac_addr[ETH_ALEN];
u8 netif_stop;
spinlock_t netif_stop_lock;
@@ -205,10 +208,10 @@ struct brcmf_skb_reorder_data {
int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp);
/* Return pointer to interface name */
-char *brcmf_ifname(struct brcmf_pub *drvr, int idx);
+char *brcmf_ifname(struct brcmf_if *ifp);
struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx);
int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
-struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
+struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx,
bool is_p2pdev, char *name, u8 *mac_addr);
void brcmf_remove_interface(struct brcmf_if *ifp);
int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/debug.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c
index 1299dccc78b4..e64557c35553 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/debug.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c
@@ -49,7 +49,7 @@ static int brcmf_debug_psm_watchdog_notify(struct brcmf_if *ifp,
const struct brcmf_event_msg *evtmsg,
void *data)
{
- brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx);
+ brcmf_dbg(TRACE, "enter: bsscfgidx=%d\n", ifp->bsscfgidx);
return brcmf_debug_create_memdump(ifp->drvr->bus_if, data,
evtmsg->datalen);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/debug.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h
index d0d9676f7f9d..6687812770cc 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/debug.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h
@@ -17,6 +17,8 @@
#ifndef BRCMFMAC_DEBUG_H
#define BRCMFMAC_DEBUG_H
+#include <linux/net.h> /* net_ratelimit() */
+
/* message levels */
#define BRCMF_TRACE_VAL 0x00000002
#define BRCMF_INFO_VAL 0x00000004
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
index 44bb30636690..1ffa95f1b8d2 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
@@ -18,18 +18,16 @@
#include <linux/module.h>
#include <brcm_hw_ids.h>
+#include <brcmu_wifi.h>
#include "core.h"
#include "bus.h"
#include "debug.h"
#include "fwil.h"
+#include "fwil_types.h"
#include "feature.h"
+#include "common.h"
-/* Module param feature_disable (global for all devices) */
-static int brcmf_feature_disable;
-module_param_named(feature_disable, brcmf_feature_disable, int, 0);
-MODULE_PARM_DESC(feature_disable, "Disable features");
-
/*
* expand feature list to array of feature strings.
*/
@@ -40,6 +38,17 @@ static const char *brcmf_feat_names[] = {
};
#undef BRCMF_FEAT_DEF
+struct brcmf_feat_fwcap {
+ enum brcmf_feat_id feature;
+ const char * const fwcap_id;
+};
+
+static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = {
+ { BRCMF_FEAT_MBSS, "mbss" },
+ { BRCMF_FEAT_MCHAN, "mchan" },
+ { BRCMF_FEAT_P2P, "p2p" },
+};
+
#ifdef DEBUG
/*
* expand quirk list to array of quirk strings.
@@ -104,44 +113,53 @@ static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp,
}
}
-/**
- * brcmf_feat_iovar_int_set() - determine feature through iovar set.
- *
- * @ifp: interface to query.
- * @id: feature id.
- * @name: iovar name.
- */
-static void brcmf_feat_iovar_int_set(struct brcmf_if *ifp,
- enum brcmf_feat_id id, char *name, u32 val)
+static void brcmf_feat_firmware_capabilities(struct brcmf_if *ifp)
{
- int err;
-
- err = brcmf_fil_iovar_int_set(ifp, name, val);
- if (err == 0) {
- brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]);
- ifp->drvr->feat_flags |= BIT(id);
- } else {
- brcmf_dbg(TRACE, "%s feature check failed: %d\n",
- brcmf_feat_names[id], err);
+ char caps[256];
+ enum brcmf_feat_id id;
+ int i;
+
+ brcmf_fil_iovar_data_get(ifp, "cap", caps, sizeof(caps));
+ brcmf_dbg(INFO, "[ %s]\n", caps);
+
+ for (i = 0; i < ARRAY_SIZE(brcmf_fwcap_map); i++) {
+ if (strnstr(caps, brcmf_fwcap_map[i].fwcap_id, sizeof(caps))) {
+ id = brcmf_fwcap_map[i].feature;
+ brcmf_dbg(INFO, "enabling feature: %s\n",
+ brcmf_feat_names[id]);
+ ifp->drvr->feat_flags |= BIT(id);
+ }
}
}
void brcmf_feat_attach(struct brcmf_pub *drvr)
{
struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
+ struct brcmf_pno_macaddr_le pfn_mac;
+ s32 err;
+
+ brcmf_feat_firmware_capabilities(ifp);
- brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan");
brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn");
if (drvr->bus_if->wowl_supported)
brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
- if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID)
- brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0);
- brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_P2P, "p2p");
-
- if (brcmf_feature_disable) {
+ /* MBSS does not work for 43362 */
+ if (drvr->bus_if->chip == BRCM_CC_43362_CHIP_ID)
+ ifp->drvr->feat_flags &= ~BIT(BRCMF_FEAT_MBSS);
+ brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_RSDB, "rsdb_mode");
+ brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_TDLS, "tdls_enable");
+
+ pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER;
+ err = brcmf_fil_iovar_data_get(ifp, "pfn_macaddr", &pfn_mac,
+ sizeof(pfn_mac));
+ if (!err)
+ ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_SCAN_RANDOM_MAC);
+
+ if (drvr->settings->feature_disable) {
brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n",
- ifp->drvr->feat_flags, brcmf_feature_disable);
- ifp->drvr->feat_flags &= ~brcmf_feature_disable;
+ ifp->drvr->feat_flags,
+ drvr->settings->feature_disable);
+ ifp->drvr->feat_flags &= ~drvr->settings->feature_disable;
}
/* set chip related quirks */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
index 6b381f799f22..2e2479d41337 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/feature.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
@@ -24,13 +24,20 @@
* PNO: preferred network offload.
* WOWL: Wake-On-WLAN.
* P2P: peer-to-peer
+ * RSDB: Real Simultaneous Dual Band
+ * TDLS: Tunneled Direct Link Setup
+ * SCAN_RANDOM_MAC: Random MAC during (net detect) scheduled scan.
*/
#define BRCMF_FEAT_LIST \
BRCMF_FEAT_DEF(MBSS) \
BRCMF_FEAT_DEF(MCHAN) \
BRCMF_FEAT_DEF(PNO) \
BRCMF_FEAT_DEF(WOWL) \
- BRCMF_FEAT_DEF(P2P)
+ BRCMF_FEAT_DEF(P2P) \
+ BRCMF_FEAT_DEF(RSDB) \
+ BRCMF_FEAT_DEF(TDLS) \
+ BRCMF_FEAT_DEF(SCAN_RANDOM_MAC)
+
/*
* Quirks:
*
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
index 4248f3c80e78..1365c12b78fc 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
@@ -23,15 +23,13 @@
#include "debug.h"
#include "firmware.h"
+#include "core.h"
+#include "common.h"
#define BRCMF_FW_MAX_NVRAM_SIZE 64000
#define BRCMF_FW_NVRAM_DEVPATH_LEN 19 /* devpath0=pcie/1/4/ */
#define BRCMF_FW_NVRAM_PCIEDEV_LEN 10 /* pcie/1/4/ + \0 */
-char brcmf_firmware_path[BRCMF_FW_PATH_LEN];
-module_param_string(alternative_fw_path, brcmf_firmware_path,
- BRCMF_FW_PATH_LEN, 0440);
-
enum nvram_parser_state {
IDLE,
KEY,
@@ -449,8 +447,7 @@ static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
if (raw_nvram)
bcm47xx_nvram_release_contents(data);
- if (fw)
- release_firmware(fw);
+ release_firmware(fw);
if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
goto fail;
@@ -540,3 +537,45 @@ int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
0);
}
+int brcmf_fw_map_chip_to_name(u32 chip, u32 chiprev,
+ struct brcmf_firmware_mapping mapping_table[],
+ u32 table_size, char fw_name[BRCMF_FW_NAME_LEN],
+ char nvram_name[BRCMF_FW_NAME_LEN])
+{
+ u32 i;
+ char end;
+
+ for (i = 0; i < table_size; i++) {
+ if (mapping_table[i].chipid == chip &&
+ mapping_table[i].revmask & BIT(chiprev))
+ break;
+ }
+
+ if (i == table_size) {
+ brcmf_err("Unknown chipid %d [%d]\n", chip, chiprev);
+ return -ENODEV;
+ }
+
+ /* check if firmware path is provided by module parameter */
+ if (brcmf_mp_global.firmware_path[0] != '\0') {
+ strlcpy(fw_name, brcmf_mp_global.firmware_path,
+ BRCMF_FW_NAME_LEN);
+ if ((nvram_name) && (mapping_table[i].nvram))
+ strlcpy(nvram_name, brcmf_mp_global.firmware_path,
+ BRCMF_FW_NAME_LEN);
+
+ end = brcmf_mp_global.firmware_path[
+ strlen(brcmf_mp_global.firmware_path) - 1];
+ if (end != '/') {
+ strlcat(fw_name, "/", BRCMF_FW_NAME_LEN);
+ if ((nvram_name) && (mapping_table[i].nvram))
+ strlcat(nvram_name, "/", BRCMF_FW_NAME_LEN);
+ }
+ }
+ strlcat(fw_name, mapping_table[i].fw, BRCMF_FW_NAME_LEN);
+ if ((nvram_name) && (mapping_table[i].nvram))
+ strlcat(nvram_name, mapping_table[i].nvram, BRCMF_FW_NAME_LEN);
+
+ return 0;
+}
+
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h
index 604dd48ab4e0..ef06f57a7a0e 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h
@@ -21,11 +21,51 @@
#define BRCMF_FW_REQ_FLAGS 0x00F0
#define BRCMF_FW_REQ_NV_OPTIONAL 0x0010
-#define BRCMF_FW_PATH_LEN 256
-#define BRCMF_FW_NAME_LEN 32
+#define BRCMF_FW_NAME_LEN 320
-extern char brcmf_firmware_path[];
+#define BRCMF_FW_DEFAULT_PATH "brcm/"
+/**
+ * struct brcmf_firmware_mapping - Used to map chipid/revmask to firmware
+ * filename and nvram filename. Each bus type implementation should create
+ * a table of firmware mappings (using the macros defined below).
+ *
+ * @chipid: ID of chip.
+ * @revmask: bitmask of revisions, e.g. 0x10 means rev 4 only, 0xf means rev 0-3
+ * @fw: name of the firmware file.
+ * @nvram: name of nvram file.
+ */
+struct brcmf_firmware_mapping {
+ u32 chipid;
+ u32 revmask;
+ const char *fw;
+ const char *nvram;
+};
+
+#define BRCMF_FW_NVRAM_DEF(fw_nvram_name, fw, nvram) \
+static const char BRCM_ ## fw_nvram_name ## _FIRMWARE_NAME[] = \
+ BRCMF_FW_DEFAULT_PATH fw; \
+static const char BRCM_ ## fw_nvram_name ## _NVRAM_NAME[] = \
+ BRCMF_FW_DEFAULT_PATH nvram; \
+MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH fw); \
+MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH nvram)
+
+#define BRCMF_FW_DEF(fw_name, fw) \
+static const char BRCM_ ## fw_name ## _FIRMWARE_NAME[] = \
+ BRCMF_FW_DEFAULT_PATH fw; \
+MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH fw) \
+
+#define BRCMF_FW_NVRAM_ENTRY(chipid, mask, name) \
+ { chipid, mask, \
+ BRCM_ ## name ## _FIRMWARE_NAME, BRCM_ ## name ## _NVRAM_NAME }
+
+#define BRCMF_FW_ENTRY(chipid, mask, name) \
+ { chipid, mask, BRCM_ ## name ## _FIRMWARE_NAME, NULL }
+
+int brcmf_fw_map_chip_to_name(u32 chip, u32 chiprev,
+ struct brcmf_firmware_mapping mapping_table[],
+ u32 table_size, char fw_name[BRCMF_FW_NAME_LEN],
+ char nvram_name[BRCMF_FW_NAME_LEN]);
void brcmf_fw_nvram_free(void *nvram);
/*
* Request firmware(s) asynchronously. When the asynchronous request
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
index 2ca783fa50cf..2ca783fa50cf 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
index 95fd1c9675d1..95fd1c9675d1 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
index 3878b6f6cfce..7b26fb1b437c 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
@@ -182,8 +182,8 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
bool is_p2pdev;
int err = 0;
- brcmf_dbg(EVENT, "action: %u idx: %u bsscfg: %u flags: %u role: %u\n",
- ifevent->action, ifevent->ifidx, ifevent->bssidx,
+ brcmf_dbg(EVENT, "action: %u ifidx: %u bsscfgidx: %u flags: %u role: %u\n",
+ ifevent->action, ifevent->ifidx, ifevent->bsscfgidx,
ifevent->flags, ifevent->role);
/* The P2P Device interface event must not be ignored contrary to what
@@ -204,12 +204,12 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
return;
}
- ifp = drvr->iflist[ifevent->bssidx];
+ ifp = drvr->iflist[ifevent->bsscfgidx];
if (ifevent->action == BRCMF_E_IF_ADD) {
brcmf_dbg(EVENT, "adding %s (%pM)\n", emsg->ifname,
emsg->addr);
- ifp = brcmf_add_if(drvr, ifevent->bssidx, ifevent->ifidx,
+ ifp = brcmf_add_if(drvr, ifevent->bsscfgidx, ifevent->ifidx,
is_p2pdev, emsg->ifname, emsg->addr);
if (IS_ERR(ifp))
return;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
index d9a942842382..5e39e2a9e388 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
@@ -219,7 +219,7 @@ struct brcmf_if_event {
u8 ifidx;
u8 action;
u8 flags;
- u8 bssidx;
+ u8 bsscfgidx;
u8 role;
};
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c
index dcfa0bb149ce..f6a2df94dba7 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c
@@ -126,7 +126,8 @@ brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set)
brcmf_dbg(FIL, "Failed: %s (%d)\n",
brcmf_fil_get_errstr((u32)(-err)), err);
- return -EBADE;
+
+ return err;
}
s32
@@ -293,22 +294,22 @@ brcmf_fil_iovar_int_get(struct brcmf_if *ifp, char *name, u32 *data)
}
static u32
-brcmf_create_bsscfg(s32 bssidx, char *name, char *data, u32 datalen, char *buf,
- u32 buflen)
+brcmf_create_bsscfg(s32 bsscfgidx, char *name, char *data, u32 datalen,
+ char *buf, u32 buflen)
{
const s8 *prefix = "bsscfg:";
s8 *p;
u32 prefixlen;
u32 namelen;
u32 iolen;
- __le32 bssidx_le;
+ __le32 bsscfgidx_le;
- if (bssidx == 0)
+ if (bsscfgidx == 0)
return brcmf_create_iovar(name, data, datalen, buf, buflen);
prefixlen = strlen(prefix);
namelen = strlen(name) + 1; /* lengh of iovar name + null */
- iolen = prefixlen + namelen + sizeof(bssidx_le) + datalen;
+ iolen = prefixlen + namelen + sizeof(bsscfgidx_le) + datalen;
if (buflen < iolen) {
brcmf_err("buffer is too short\n");
@@ -326,9 +327,9 @@ brcmf_create_bsscfg(s32 bssidx, char *name, char *data, u32 datalen, char *buf,
p += namelen;
/* bss config index as first data */
- bssidx_le = cpu_to_le32(bssidx);
- memcpy(p, &bssidx_le, sizeof(bssidx_le));
- p += sizeof(bssidx_le);
+ bsscfgidx_le = cpu_to_le32(bsscfgidx);
+ memcpy(p, &bsscfgidx_le, sizeof(bsscfgidx_le));
+ p += sizeof(bsscfgidx_le);
/* parameter buffer follows */
if (datalen)
@@ -347,12 +348,12 @@ brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name,
mutex_lock(&drvr->proto_block);
- brcmf_dbg(FIL, "ifidx=%d, bssidx=%d, name=%s, len=%d\n", ifp->ifidx,
- ifp->bssidx, name, len);
+ brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx,
+ ifp->bsscfgidx, name, len);
brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
- buflen = brcmf_create_bsscfg(ifp->bssidx, name, data, len,
+ buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len,
drvr->proto_buf, sizeof(drvr->proto_buf));
if (buflen) {
err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf,
@@ -376,7 +377,7 @@ brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name,
mutex_lock(&drvr->proto_block);
- buflen = brcmf_create_bsscfg(ifp->bssidx, name, data, len,
+ buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len,
drvr->proto_buf, sizeof(drvr->proto_buf));
if (buflen) {
err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf,
@@ -387,8 +388,8 @@ brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name,
err = -EPERM;
brcmf_err("Creating bsscfg failed\n");
}
- brcmf_dbg(FIL, "ifidx=%d, bssidx=%d, name=%s, len=%d\n", ifp->ifidx,
- ifp->bssidx, name, len);
+ brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx,
+ ifp->bsscfgidx, name, len);
brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h
index b20fc0f82a48..6b72df17744e 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h
@@ -70,6 +70,7 @@
#define BRCMF_C_SET_WSEC 134
#define BRCMF_C_GET_PHY_NOISE 135
#define BRCMF_C_GET_BSS_INFO 136
+#define BRCMF_C_GET_GET_PKTCNTS 137
#define BRCMF_C_GET_BANDLIST 140
#define BRCMF_C_SET_SCB_TIMEOUT 158
#define BRCMF_C_GET_ASSOCLIST 159
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
index daa427b46712..1afc2ad83b6c 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -110,6 +110,8 @@
#define BRCMF_WOWL_UNASSOC (1 << 24)
/* Wakeup if received matched secured pattern: */
#define BRCMF_WOWL_SECURE (1 << 25)
+/* Wakeup on finding preferred network */
+#define BRCMF_WOWL_PFN_FOUND (1 << 26)
/* Link Down indication in WoWL mode: */
#define BRCMF_WOWL_LINKDOWN (1 << 31)
@@ -121,6 +123,17 @@
#define BRCMF_MAX_ASSOCLIST 128
+#define BRCMF_TXBF_SU_BFE_CAP BIT(0)
+#define BRCMF_TXBF_MU_BFE_CAP BIT(1)
+#define BRCMF_TXBF_SU_BFR_CAP BIT(0)
+#define BRCMF_TXBF_MU_BFR_CAP BIT(1)
+
+#define BRCMF_MAXPMKID 16 /* max # PMKID cache entries */
+
+#define BRCMF_PFN_MACADDR_CFG_VER 1
+#define BRCMF_PFN_MAC_OUI_ONLY BIT(0)
+#define BRCMF_PFN_SET_MAC_UNASSOC BIT(1)
+
/* join preference types for join_pref iovar */
enum brcmf_join_pref_types {
BRCMF_JOIN_PREF_RSSI = 1,
@@ -170,7 +183,7 @@ struct brcmf_fil_af_params_le {
};
struct brcmf_fil_bss_enable_le {
- __le32 bsscfg_idx;
+ __le32 bsscfgidx;
__le32 enable;
};
@@ -282,14 +295,9 @@ struct brcm_rateset_le {
u8 rates[BRCMF_MAXRATES_IN_SET];
};
-struct brcmf_ssid {
- u32 SSID_len;
- unsigned char SSID[32];
-};
-
struct brcmf_ssid_le {
__le32 SSID_len;
- unsigned char SSID[32];
+ unsigned char SSID[IEEE80211_MAX_SSID_LEN];
};
struct brcmf_scan_params_le {
@@ -634,4 +642,149 @@ struct brcmf_assoclist_le {
u8 mac[BRCMF_MAX_ASSOCLIST][ETH_ALEN];
};
+/**
+ * struct brcmf_wowl_wakeind_le - Wakeup indicators
+ * Note: note both fields contain same information.
+ *
+ * @pci_wakeind: Whether PCI PMECSR PMEStatus bit was set.
+ * @ucode_wakeind: What wakeup-event indication was set by ucode
+ */
+struct brcmf_wowl_wakeind_le {
+ __le32 pci_wakeind;
+ __le32 ucode_wakeind;
+};
+
+/**
+ * struct brcmf_pmksa - PMK Security Association
+ *
+ * @bssid: The AP's BSSID.
+ * @pmkid: he PMK material itself.
+ */
+struct brcmf_pmksa {
+ u8 bssid[ETH_ALEN];
+ u8 pmkid[WLAN_PMKID_LEN];
+};
+
+/**
+ * struct brcmf_pmk_list_le - List of pmksa's.
+ *
+ * @npmk: Number of pmksa's.
+ * @pmk: PMK SA information.
+ */
+struct brcmf_pmk_list_le {
+ __le32 npmk;
+ struct brcmf_pmksa pmk[BRCMF_MAXPMKID];
+};
+
+/**
+ * struct brcmf_pno_param_le - PNO scan configuration parameters
+ *
+ * @version: PNO parameters version.
+ * @scan_freq: scan frequency.
+ * @lost_network_timeout: #sec. to declare discovered network as lost.
+ * @flags: Bit field to control features of PFN such as sort criteria auto
+ * enable switch and background scan.
+ * @rssi_margin: Margin to avoid jitter for choosing a PFN based on RSSI sort
+ * criteria.
+ * @bestn: number of best networks in each scan.
+ * @mscan: number of scans recorded.
+ * @repeat: minimum number of scan intervals before scan frequency changes
+ * in adaptive scan.
+ * @exp: exponent of 2 for maximum scan interval.
+ * @slow_freq: slow scan period.
+ */
+struct brcmf_pno_param_le {
+ __le32 version;
+ __le32 scan_freq;
+ __le32 lost_network_timeout;
+ __le16 flags;
+ __le16 rssi_margin;
+ u8 bestn;
+ u8 mscan;
+ u8 repeat;
+ u8 exp;
+ __le32 slow_freq;
+};
+
+/**
+ * struct brcmf_pno_net_param_le - scan parameters per preferred network.
+ *
+ * @ssid: ssid name and its length.
+ * @flags: bit2: hidden.
+ * @infra: BSS vs IBSS.
+ * @auth: Open vs Closed.
+ * @wpa_auth: WPA type.
+ * @wsec: wsec value.
+ */
+struct brcmf_pno_net_param_le {
+ struct brcmf_ssid_le ssid;
+ __le32 flags;
+ __le32 infra;
+ __le32 auth;
+ __le32 wpa_auth;
+ __le32 wsec;
+};
+
+/**
+ * struct brcmf_pno_net_info_le - information per found network.
+ *
+ * @bssid: BSS network identifier.
+ * @channel: channel number only.
+ * @SSID_len: length of ssid.
+ * @SSID: ssid characters.
+ * @RSSI: receive signal strength (in dBm).
+ * @timestamp: age in seconds.
+ */
+struct brcmf_pno_net_info_le {
+ u8 bssid[ETH_ALEN];
+ u8 channel;
+ u8 SSID_len;
+ u8 SSID[32];
+ __le16 RSSI;
+ __le16 timestamp;
+};
+
+/**
+ * struct brcmf_pno_scanresults_le - result returned in PNO NET FOUND event.
+ *
+ * @version: PNO version identifier.
+ * @status: indicates completion status of PNO scan.
+ * @count: amount of brcmf_pno_net_info_le entries appended.
+ */
+struct brcmf_pno_scanresults_le {
+ __le32 version;
+ __le32 status;
+ __le32 count;
+};
+
+/**
+ * struct brcmf_pno_macaddr_le - to configure PNO macaddr randomization.
+ *
+ * @version: PNO version identifier.
+ * @flags: Flags defining how mac addrss should be used.
+ * @mac: MAC address.
+ */
+struct brcmf_pno_macaddr_le {
+ u8 version;
+ u8 flags;
+ u8 mac[ETH_ALEN];
+};
+
+/**
+ * struct brcmf_pktcnt_le - packet counters.
+ *
+ * @rx_good_pkt: packets (MSDUs & MMPDUs) received from this station
+ * @rx_bad_pkt: failed rx packets
+ * @tx_good_pkt: packets (MSDUs & MMPDUs) transmitted to this station
+ * @tx_bad_pkt: failed tx packets
+ * @rx_ocast_good_pkt: unicast packets destined for others
+ */
+struct brcmf_pktcnt_le {
+ __le32 rx_good_pkt;
+ __le32 rx_bad_pkt;
+ __le32 tx_good_pkt;
+ __le32 tx_bad_pkt;
+ __le32 rx_ocast_good_pkt;
+};
+
#endif /* FWIL_TYPES_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
index 086cac3f86d6..f82c9ab5480b 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
@@ -36,6 +36,7 @@
#include "p2p.h"
#include "cfg80211.h"
#include "proto.h"
+#include "common.h"
/**
* DOC: Firmware Signalling
@@ -521,10 +522,6 @@ static const int brcmf_fws_prio2fifo[] = {
BRCMF_FWS_FIFO_AC_VO
};
-static int fcmode;
-module_param(fcmode, int, S_IRUSR);
-MODULE_PARM_DESC(fcmode, "mode of firmware signalled flow control");
-
#define BRCMF_FWS_TLV_DEF(name, id, len) \
case BRCMF_FWS_TYPE_ ## name: \
return len;
@@ -719,7 +716,7 @@ static void brcmf_fws_macdesc_init(struct brcmf_fws_mac_descriptor *desc,
desc->state = BRCMF_FWS_STATE_OPEN;
desc->requested_credit = 0;
desc->requested_packet = 0;
- /* depending on use may need ifp->bssidx instead */
+ /* depending on use may need ifp->bsscfgidx instead */
desc->interface_id = ifidx;
desc->ac_bitmap = 0xff; /* update this when handling APSD */
if (addr)
@@ -1609,10 +1606,11 @@ static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp,
{
struct brcmf_fws_info *fws = ifp->drvr->fws;
- brcmf_fws_lock(fws);
- if (fws)
+ if (fws) {
+ brcmf_fws_lock(fws);
fws->bcmc_credit_check = true;
- brcmf_fws_unlock(fws);
+ brcmf_fws_unlock(fws);
+ }
return 0;
}
@@ -1938,7 +1936,7 @@ void brcmf_fws_reset_interface(struct brcmf_if *ifp)
{
struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
- brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx);
+ brcmf_dbg(TRACE, "enter: bsscfgidx=%d\n", ifp->bsscfgidx);
if (!entry)
return;
@@ -2133,10 +2131,10 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
/* set linkage back */
fws->drvr = drvr;
- fws->fcmode = fcmode;
+ fws->fcmode = drvr->settings->fcmode;
if ((drvr->bus_if->always_use_fws_queue == false) &&
- (fcmode == BRCMF_FWS_FCMODE_NONE)) {
+ (fws->fcmode == BRCMF_FWS_FCMODE_NONE)) {
fws->avoid_queueing = true;
brcmf_dbg(INFO, "FWS queueing will be avoided\n");
return 0;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h
index a36bac17eafd..a36bac17eafd 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
index 44e618f9d890..c2bdb91746cf 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
@@ -34,7 +34,7 @@
#include "tracepoint.h"
-#define MSGBUF_IOCTL_RESP_TIMEOUT 2000
+#define MSGBUF_IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000)
#define MSGBUF_TYPE_GEN_STATUS 0x1
#define MSGBUF_TYPE_RING_STATUS 0x2
@@ -466,15 +466,14 @@ static int brcmf_msgbuf_ioctl_resp_wait(struct brcmf_msgbuf *msgbuf)
{
return wait_event_timeout(msgbuf->ioctl_resp_wait,
msgbuf->ctl_completed,
- msecs_to_jiffies(MSGBUF_IOCTL_RESP_TIMEOUT));
+ MSGBUF_IOCTL_RESP_TIMEOUT);
}
static void brcmf_msgbuf_ioctl_resp_wake(struct brcmf_msgbuf *msgbuf)
{
msgbuf->ctl_completed = true;
- if (waitqueue_active(&msgbuf->ioctl_resp_wait))
- wake_up(&msgbuf->ioctl_resp_wait);
+ wake_up(&msgbuf->ioctl_resp_wait);
}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
index 3d513e407e3d..3d513e407e3d 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/of.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
index 03f35e0c52ca..03f35e0c52ca 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/of.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/of.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h
index 5f7c3550deda..5f7c3550deda 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/of.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
index d224b3dd72ed..821b6494f9d1 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
@@ -71,10 +71,10 @@
#define P2P_AF_MED_DWELL_TIME 400
#define P2P_AF_LONG_DWELL_TIME 1000
#define P2P_AF_TX_MAX_RETRY 1
-#define P2P_AF_MAX_WAIT_TIME 2000
+#define P2P_AF_MAX_WAIT_TIME msecs_to_jiffies(2000)
#define P2P_INVALID_CHANNEL -1
#define P2P_CHANNEL_SYNC_RETRY 5
-#define P2P_AF_FRM_SCAN_MAX_WAIT 1500
+#define P2P_AF_FRM_SCAN_MAX_WAIT msecs_to_jiffies(1500)
#define P2P_DEFAULT_SLEEP_TIME_VSDB 200
/* WiFi P2P Public Action Frame OUI Subtypes */
@@ -102,6 +102,7 @@
#define P2PSD_ACTION_ID_GAS_CREQ 0x0c /* GAS Comback Request AF */
#define P2PSD_ACTION_ID_GAS_CRESP 0x0d /* GAS Comback Response AF */
+#define BRCMF_P2P_DISABLE_TIMEOUT msecs_to_jiffies(500)
/**
* struct brcmf_p2p_disc_st_le - set discovery state in firmware.
*
@@ -625,11 +626,10 @@ exit:
* @num_chans: number of channels to scan.
* @chanspecs: channel parameters for @num_chans channels.
* @search_state: P2P discover state to use.
- * @action: scan action to pass to firmware.
* @bss_type: type of P2P bss.
*/
static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans,
- u16 chanspecs[], s32 search_state, u16 action,
+ u16 chanspecs[], s32 search_state,
enum p2p_bss_type bss_type)
{
s32 ret = 0;
@@ -642,7 +642,6 @@ static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans,
struct brcmf_cfg80211_vif *vif;
struct brcmf_p2p_scan_le *p2p_params;
struct brcmf_scan_params_le *sparams;
- struct brcmf_ssid ssid;
memsize += num_chans * sizeof(__le16);
memblk = kzalloc(memsize, GFP_KERNEL);
@@ -655,16 +654,16 @@ static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans,
ret = -EINVAL;
goto exit;
}
+ p2p_params = (struct brcmf_p2p_scan_le *)memblk;
+ sparams = &p2p_params->eparams.params_le;
switch (search_state) {
case WL_P2P_DISC_ST_SEARCH:
/*
* If we in SEARCH STATE, we don't need to set SSID explictly
- * because dongle use P2P WILDCARD internally by default
+ * because dongle use P2P WILDCARD internally by default, use
+ * null ssid, which it is already due to kzalloc.
*/
- /* use null ssid */
- ssid.SSID_len = 0;
- memset(ssid.SSID, 0, sizeof(ssid.SSID));
break;
case WL_P2P_DISC_ST_SCAN:
/*
@@ -673,8 +672,10 @@ static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans,
* P2P WILDCARD because we just do broadcast scan unless
* setting SSID.
*/
- ssid.SSID_len = BRCMF_P2P_WILDCARD_SSID_LEN;
- memcpy(ssid.SSID, BRCMF_P2P_WILDCARD_SSID, ssid.SSID_len);
+ sparams->ssid_le.SSID_len =
+ cpu_to_le32(BRCMF_P2P_WILDCARD_SSID_LEN);
+ memcpy(sparams->ssid_le.SSID, BRCMF_P2P_WILDCARD_SSID,
+ BRCMF_P2P_WILDCARD_SSID_LEN);
break;
default:
brcmf_err(" invalid search state %d\n", search_state);
@@ -687,11 +688,9 @@ static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans,
/*
* set p2p scan parameters.
*/
- p2p_params = (struct brcmf_p2p_scan_le *)memblk;
p2p_params->type = 'E';
/* determine the scan engine parameters */
- sparams = &p2p_params->eparams.params_le;
sparams->bss_type = DOT11_BSSTYPE_ANY;
if (p2p->cfg->active_scan)
sparams->scan_type = 0;
@@ -699,9 +698,6 @@ static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans,
sparams->scan_type = 1;
eth_broadcast_addr(sparams->bssid);
- if (ssid.SSID_len)
- memcpy(sparams->ssid_le.SSID, ssid.SSID, ssid.SSID_len);
- sparams->ssid_le.SSID_len = cpu_to_le32(ssid.SSID_len);
sparams->home_time = cpu_to_le32(P2PAPI_SCAN_HOME_TIME_MS);
/*
@@ -742,7 +738,7 @@ static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans,
/* set the escan specific parameters */
p2p_params->eparams.version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
- p2p_params->eparams.action = cpu_to_le16(action);
+ p2p_params->eparams.action = cpu_to_le16(WL_ESCAN_ACTION_START);
p2p_params->eparams.sync_id = cpu_to_le16(0x1234);
/* perform p2p scan on primary device */
ret = brcmf_fil_bsscfg_data_set(vif->ifp, "p2p_scan", memblk, memsize);
@@ -766,8 +762,7 @@ exit:
*/
static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg,
struct brcmf_if *ifp,
- struct cfg80211_scan_request *request,
- u16 action)
+ struct cfg80211_scan_request *request)
{
struct brcmf_p2p_info *p2p = &cfg->p2p;
s32 err = 0;
@@ -827,7 +822,7 @@ static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg,
num_nodfs++;
}
err = brcmf_p2p_escan(p2p, num_nodfs, chanspecs, search_state,
- action, P2PAPI_BSSCFG_DEVICE);
+ P2PAPI_BSSCFG_DEVICE);
kfree(chanspecs);
}
exit:
@@ -1096,8 +1091,7 @@ static s32 brcmf_p2p_act_frm_search(struct brcmf_p2p_info *p2p, u16 channel)
default_chan_list[2] = ch.chspec;
}
err = brcmf_p2p_escan(p2p, channel_cnt, default_chan_list,
- WL_P2P_DISC_ST_SEARCH, WL_ESCAN_ACTION_START,
- P2PAPI_BSSCFG_DEVICE);
+ WL_P2P_DISC_ST_SEARCH, P2PAPI_BSSCFG_DEVICE);
kfree(default_chan_list);
exit:
return err;
@@ -1521,7 +1515,7 @@ static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p,
p2p->af_tx_sent_jiffies = jiffies;
timeout = wait_for_completion_timeout(&p2p->send_af_done,
- msecs_to_jiffies(P2P_AF_MAX_WAIT_TIME));
+ P2P_AF_MAX_WAIT_TIME);
if (test_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status)) {
brcmf_dbg(TRACE, "TX action frame operation is success\n");
@@ -1995,7 +1989,7 @@ int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg,
return err;
}
err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_CHANGE,
- msecs_to_jiffies(1500));
+ BRCMF_VIF_EVENT_TIMEOUT);
brcmf_cfg80211_arm_vif_event(cfg, NULL);
if (!err) {
brcmf_err("No BRCMF_E_IF_CHANGE event received\n");
@@ -2067,7 +2061,7 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p,
struct brcmf_if *p2p_ifp;
struct brcmf_if *pri_ifp;
int err;
- u32 bssidx;
+ u32 bsscfgidx;
if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
return ERR_PTR(-ENOSPC);
@@ -2097,7 +2091,7 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p,
/* wait for firmware event */
err = brcmf_cfg80211_wait_vif_event_timeout(p2p->cfg, BRCMF_E_IF_ADD,
- msecs_to_jiffies(1500));
+ BRCMF_VIF_EVENT_TIMEOUT);
brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL);
brcmf_fweh_p2pdev_setup(pri_ifp, false);
if (!err) {
@@ -2113,13 +2107,13 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p,
memcpy(&p2p_vif->wdev.address, p2p->dev_addr, sizeof(p2p->dev_addr));
/* verify bsscfg index for P2P discovery */
- err = brcmf_fil_iovar_int_get(pri_ifp, "p2p_dev", &bssidx);
+ err = brcmf_fil_iovar_int_get(pri_ifp, "p2p_dev", &bsscfgidx);
if (err < 0) {
brcmf_err("retrieving discover bsscfg index failed\n");
goto fail;
}
- WARN_ON(p2p_ifp->bssidx != bssidx);
+ WARN_ON(p2p_ifp->bsscfgidx != bsscfgidx);
init_completion(&p2p->send_af_done);
INIT_WORK(&p2p->afx_hdl.afx_work, brcmf_p2p_afx_handler);
@@ -2187,7 +2181,7 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
/* wait for firmware event */
err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
- msecs_to_jiffies(1500));
+ BRCMF_VIF_EVENT_TIMEOUT);
brcmf_cfg80211_arm_vif_event(cfg, NULL);
if (!err) {
brcmf_err("timeout occurred\n");
@@ -2237,7 +2231,6 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
struct brcmf_p2p_info *p2p = &cfg->p2p;
struct brcmf_cfg80211_vif *vif;
- unsigned long jiffie_timeout = msecs_to_jiffies(1500);
bool wait_for_disable = false;
int err;
@@ -2270,7 +2263,7 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
if (wait_for_disable)
wait_for_completion_timeout(&cfg->vif_disabled,
- msecs_to_jiffies(500));
+ BRCMF_P2P_DISABLE_TIMEOUT);
err = 0;
if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE) {
@@ -2280,7 +2273,7 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
if (!err) {
/* wait for firmware event */
err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_DEL,
- jiffie_timeout);
+ BRCMF_VIF_EVENT_TIMEOUT);
if (!err)
err = -EIO;
else
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h
index 5d49059021a9..a3bd18c2360b 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h
@@ -112,7 +112,6 @@ struct afx_hdl {
* @int_addr: P2P interface address.
* @bss_idx: informate for P2P bss types.
* @listen_timer: timer for @WL_P2P_DISC_ST_LISTEN discover state.
- * @ssid: ssid for P2P GO.
* @listen_channel: channel for @WL_P2P_DISC_ST_LISTEN discover state.
* @remain_on_channel: contains copy of struct used by cfg80211.
* @remain_on_channel_cookie: cookie counter for remain on channel cmd
@@ -133,7 +132,6 @@ struct brcmf_p2p_info {
u8 int_addr[ETH_ALEN];
struct p2p_bss bss_idx[P2PAPI_BSSCFG_MAX];
struct timer_list listen_timer;
- struct brcmf_ssid ssid;
u8 listen_channel;
struct ieee80211_channel remain_on_channel;
u32 remain_on_channel_cookie;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
index 83d804221715..0480b70e3eb8 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
@@ -44,23 +44,31 @@ enum brcmf_pcie_state {
BRCMFMAC_PCIE_STATE_UP
};
-
-#define BRCMF_PCIE_43602_FW_NAME "brcm/brcmfmac43602-pcie.bin"
-#define BRCMF_PCIE_43602_NVRAM_NAME "brcm/brcmfmac43602-pcie.txt"
-#define BRCMF_PCIE_4350_FW_NAME "brcm/brcmfmac4350-pcie.bin"
-#define BRCMF_PCIE_4350_NVRAM_NAME "brcm/brcmfmac4350-pcie.txt"
-#define BRCMF_PCIE_4356_FW_NAME "brcm/brcmfmac4356-pcie.bin"
-#define BRCMF_PCIE_4356_NVRAM_NAME "brcm/brcmfmac4356-pcie.txt"
-#define BRCMF_PCIE_43570_FW_NAME "brcm/brcmfmac43570-pcie.bin"
-#define BRCMF_PCIE_43570_NVRAM_NAME "brcm/brcmfmac43570-pcie.txt"
-#define BRCMF_PCIE_4358_FW_NAME "brcm/brcmfmac4358-pcie.bin"
-#define BRCMF_PCIE_4358_NVRAM_NAME "brcm/brcmfmac4358-pcie.txt"
-#define BRCMF_PCIE_4365_FW_NAME "brcm/brcmfmac4365b-pcie.bin"
-#define BRCMF_PCIE_4365_NVRAM_NAME "brcm/brcmfmac4365b-pcie.txt"
-#define BRCMF_PCIE_4366_FW_NAME "brcm/brcmfmac4366b-pcie.bin"
-#define BRCMF_PCIE_4366_NVRAM_NAME "brcm/brcmfmac4366b-pcie.txt"
-#define BRCMF_PCIE_4371_FW_NAME "brcm/brcmfmac4371-pcie.bin"
-#define BRCMF_PCIE_4371_NVRAM_NAME "brcm/brcmfmac4371-pcie.txt"
+BRCMF_FW_NVRAM_DEF(43602, "brcmfmac43602-pcie.bin", "brcmfmac43602-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4350, "brcmfmac4350-pcie.bin", "brcmfmac4350-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4350C, "brcmfmac4350c2-pcie.bin", "brcmfmac4350c2-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4356, "brcmfmac4356-pcie.bin", "brcmfmac4356-pcie.txt");
+BRCMF_FW_NVRAM_DEF(43570, "brcmfmac43570-pcie.bin", "brcmfmac43570-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4358, "brcmfmac4358-pcie.bin", "brcmfmac4358-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4359, "brcmfmac4359-pcie.bin", "brcmfmac4359-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4365B, "brcmfmac4365b-pcie.bin", "brcmfmac4365b-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4366B, "brcmfmac4366b-pcie.bin", "brcmfmac4366b-pcie.txt");
+BRCMF_FW_NVRAM_DEF(4371, "brcmfmac4371-pcie.bin", "brcmfmac4371-pcie.txt");
+
+static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43602_CHIP_ID, 0xFFFFFFFF, 43602),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4350_CHIP_ID, 0x000000FF, 4350C),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4350_CHIP_ID, 0xFFFFFF00, 4350),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43567_CHIP_ID, 0xFFFFFFFF, 43570),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43569_CHIP_ID, 0xFFFFFFFF, 43570),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43570_CHIP_ID, 0xFFFFFFFF, 43570),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4358_CHIP_ID, 0xFFFFFFFF, 4358),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4365_CHIP_ID, 0xFFFFFFFF, 4365B),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4366_CHIP_ID, 0xFFFFFFFF, 4366B),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4371_CHIP_ID, 0xFFFFFFFF, 4371),
+};
#define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */
@@ -183,7 +191,7 @@ enum brcmf_pcie_state {
#define BRCMF_H2D_HOST_D0_INFORM_IN_USE 0x00000008
#define BRCMF_H2D_HOST_D0_INFORM 0x00000010
-#define BRCMF_PCIE_MBDATA_TIMEOUT 2000
+#define BRCMF_PCIE_MBDATA_TIMEOUT msecs_to_jiffies(2000)
#define BRCMF_PCIE_CFGREG_STATUS_CMD 0x4
#define BRCMF_PCIE_CFGREG_PM_CSR 0x4C
@@ -200,24 +208,6 @@ enum brcmf_pcie_state {
#define BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB 3
-MODULE_FIRMWARE(BRCMF_PCIE_43602_FW_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_43602_NVRAM_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4350_FW_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4350_NVRAM_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4356_FW_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4356_NVRAM_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_43570_NVRAM_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4358_FW_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4358_NVRAM_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4365_FW_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4365_NVRAM_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4366_FW_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4366_NVRAM_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4371_FW_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4371_NVRAM_NAME);
-
-
struct brcmf_pcie_console {
u32 base_addr;
u32 buf_addr;
@@ -253,10 +243,9 @@ struct brcmf_pcie_core_info {
struct brcmf_pciedev_info {
enum brcmf_pcie_state state;
bool in_irq;
- bool irq_requested;
struct pci_dev *pdev;
- char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
- char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
+ char fw_name[BRCMF_FW_NAME_LEN];
+ char nvram_name[BRCMF_FW_NAME_LEN];
void __iomem *regs;
void __iomem *tcm;
u32 tcm_size;
@@ -622,7 +611,9 @@ static int brcmf_pcie_exit_download_state(struct brcmf_pciedev_info *devinfo,
brcmf_chip_resetcore(core, 0, 0, 0);
}
- return !brcmf_chip_set_active(devinfo->ci, resetintr);
+ if (!brcmf_chip_set_active(devinfo->ci, resetintr))
+ return -EINVAL;
+ return 0;
}
@@ -885,7 +876,6 @@ static int brcmf_pcie_request_irq(struct brcmf_pciedev_info *devinfo)
brcmf_dbg(PCIE, "Enter\n");
/* is it a v1 or v2 implementation */
- devinfo->irq_requested = false;
pci_enable_msi(pdev);
if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) {
if (request_threaded_irq(pdev->irq,
@@ -908,7 +898,6 @@ static int brcmf_pcie_request_irq(struct brcmf_pciedev_info *devinfo)
return -EIO;
}
}
- devinfo->irq_requested = true;
devinfo->irq_allocated = true;
return 0;
}
@@ -926,9 +915,6 @@ static void brcmf_pcie_release_irq(struct brcmf_pciedev_info *devinfo)
pdev = devinfo->pdev;
brcmf_pcie_intr_disable(devinfo);
- if (!devinfo->irq_requested)
- return;
- devinfo->irq_requested = false;
free_irq(pdev->irq, devinfo);
pci_disable_msi(pdev);
@@ -1390,10 +1376,6 @@ static void brcmf_pcie_wowl_config(struct device *dev, bool enabled)
brcmf_dbg(PCIE, "Configuring WOWL, enabled=%d\n", enabled);
devinfo->wowl_enabled = enabled;
- if (enabled)
- device_set_wakeup_enable(&devinfo->pdev->dev, true);
- else
- device_set_wakeup_enable(&devinfo->pdev->dev, false);
}
@@ -1419,7 +1401,7 @@ static int brcmf_pcie_get_memdump(struct device *dev, void *data, size_t len)
}
-static struct brcmf_bus_ops brcmf_pcie_bus_ops = {
+static const struct brcmf_bus_ops brcmf_pcie_bus_ops = {
.txdata = brcmf_pcie_tx,
.stop = brcmf_pcie_down,
.txctl = brcmf_pcie_tx_ctlpkt,
@@ -1484,80 +1466,6 @@ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo,
}
-static int brcmf_pcie_get_fwnames(struct brcmf_pciedev_info *devinfo)
-{
- char *fw_name;
- char *nvram_name;
- uint fw_len, nv_len;
- char end;
-
- brcmf_dbg(PCIE, "Enter, chip 0x%04x chiprev %d\n", devinfo->ci->chip,
- devinfo->ci->chiprev);
-
- switch (devinfo->ci->chip) {
- case BRCM_CC_43602_CHIP_ID:
- fw_name = BRCMF_PCIE_43602_FW_NAME;
- nvram_name = BRCMF_PCIE_43602_NVRAM_NAME;
- break;
- case BRCM_CC_4350_CHIP_ID:
- fw_name = BRCMF_PCIE_4350_FW_NAME;
- nvram_name = BRCMF_PCIE_4350_NVRAM_NAME;
- break;
- case BRCM_CC_4356_CHIP_ID:
- fw_name = BRCMF_PCIE_4356_FW_NAME;
- nvram_name = BRCMF_PCIE_4356_NVRAM_NAME;
- break;
- case BRCM_CC_43567_CHIP_ID:
- case BRCM_CC_43569_CHIP_ID:
- case BRCM_CC_43570_CHIP_ID:
- fw_name = BRCMF_PCIE_43570_FW_NAME;
- nvram_name = BRCMF_PCIE_43570_NVRAM_NAME;
- break;
- case BRCM_CC_4358_CHIP_ID:
- fw_name = BRCMF_PCIE_4358_FW_NAME;
- nvram_name = BRCMF_PCIE_4358_NVRAM_NAME;
- break;
- case BRCM_CC_4365_CHIP_ID:
- fw_name = BRCMF_PCIE_4365_FW_NAME;
- nvram_name = BRCMF_PCIE_4365_NVRAM_NAME;
- break;
- case BRCM_CC_4366_CHIP_ID:
- fw_name = BRCMF_PCIE_4366_FW_NAME;
- nvram_name = BRCMF_PCIE_4366_NVRAM_NAME;
- break;
- case BRCM_CC_4371_CHIP_ID:
- fw_name = BRCMF_PCIE_4371_FW_NAME;
- nvram_name = BRCMF_PCIE_4371_NVRAM_NAME;
- break;
- default:
- brcmf_err("Unsupported chip 0x%04x\n", devinfo->ci->chip);
- return -ENODEV;
- }
-
- fw_len = sizeof(devinfo->fw_name) - 1;
- nv_len = sizeof(devinfo->nvram_name) - 1;
- /* check if firmware path is provided by module parameter */
- if (brcmf_firmware_path[0] != '\0') {
- strncpy(devinfo->fw_name, brcmf_firmware_path, fw_len);
- strncpy(devinfo->nvram_name, brcmf_firmware_path, nv_len);
- fw_len -= strlen(devinfo->fw_name);
- nv_len -= strlen(devinfo->nvram_name);
-
- end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1];
- if (end != '/') {
- strncat(devinfo->fw_name, "/", fw_len);
- strncat(devinfo->nvram_name, "/", nv_len);
- fw_len--;
- nv_len--;
- }
- }
- strncat(devinfo->fw_name, fw_name, fw_len);
- strncat(devinfo->nvram_name, nvram_name, nv_len);
-
- return 0;
-}
-
-
static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo,
const struct firmware *fw, void *nvram,
u32 nvram_len)
@@ -1893,7 +1801,10 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
bus->wowl_supported = pci_pme_capable(pdev, PCI_D3hot);
dev_set_drvdata(&pdev->dev, bus);
- ret = brcmf_pcie_get_fwnames(devinfo);
+ ret = brcmf_fw_map_chip_to_name(devinfo->ci->chip, devinfo->ci->chiprev,
+ brcmf_pcie_fwnames,
+ ARRAY_SIZE(brcmf_pcie_fwnames),
+ devinfo->fw_name, devinfo->nvram_name);
if (ret)
goto fail_bus;
@@ -1959,15 +1870,14 @@ brcmf_pcie_remove(struct pci_dev *pdev)
#ifdef CONFIG_PM
-static int brcmf_pcie_suspend(struct pci_dev *pdev, pm_message_t state)
+static int brcmf_pcie_pm_enter_D3(struct device *dev)
{
struct brcmf_pciedev_info *devinfo;
struct brcmf_bus *bus;
- int err;
- brcmf_dbg(PCIE, "Enter, state=%d, pdev=%p\n", state.event, pdev);
+ brcmf_dbg(PCIE, "Enter\n");
- bus = dev_get_drvdata(&pdev->dev);
+ bus = dev_get_drvdata(dev);
devinfo = bus->bus_priv.pcie->devinfo;
brcmf_bus_change_state(bus, BRCMF_BUS_DOWN);
@@ -1975,69 +1885,51 @@ static int brcmf_pcie_suspend(struct pci_dev *pdev, pm_message_t state)
devinfo->mbdata_completed = false;
brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D3_INFORM);
- wait_event_timeout(devinfo->mbdata_resp_wait,
- devinfo->mbdata_completed,
- msecs_to_jiffies(BRCMF_PCIE_MBDATA_TIMEOUT));
+ wait_event_timeout(devinfo->mbdata_resp_wait, devinfo->mbdata_completed,
+ BRCMF_PCIE_MBDATA_TIMEOUT);
if (!devinfo->mbdata_completed) {
brcmf_err("Timeout on response for entering D3 substate\n");
return -EIO;
}
- brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D0_INFORM_IN_USE);
- err = pci_save_state(pdev);
- if (err)
- brcmf_err("pci_save_state failed, err=%d\n", err);
- if ((err) || (!devinfo->wowl_enabled)) {
- brcmf_chip_detach(devinfo->ci);
- devinfo->ci = NULL;
- brcmf_pcie_remove(pdev);
- return 0;
- }
+ devinfo->state = BRCMFMAC_PCIE_STATE_DOWN;
- return pci_prepare_to_sleep(pdev);
+ return 0;
}
-static int brcmf_pcie_resume(struct pci_dev *pdev)
+
+static int brcmf_pcie_pm_leave_D3(struct device *dev)
{
struct brcmf_pciedev_info *devinfo;
struct brcmf_bus *bus;
+ struct pci_dev *pdev;
int err;
- bus = dev_get_drvdata(&pdev->dev);
- brcmf_dbg(PCIE, "Enter, pdev=%p, bus=%p\n", pdev, bus);
+ brcmf_dbg(PCIE, "Enter\n");
- err = pci_set_power_state(pdev, PCI_D0);
- if (err) {
- brcmf_err("pci_set_power_state failed, err=%d\n", err);
- goto cleanup;
- }
- pci_restore_state(pdev);
- pci_enable_wake(pdev, PCI_D3hot, false);
- pci_enable_wake(pdev, PCI_D3cold, false);
+ bus = dev_get_drvdata(dev);
+ devinfo = bus->bus_priv.pcie->devinfo;
+ brcmf_dbg(PCIE, "Enter, dev=%p, bus=%p\n", dev, bus);
/* Check if device is still up and running, if so we are ready */
- if (bus) {
- devinfo = bus->bus_priv.pcie->devinfo;
- if (brcmf_pcie_read_reg32(devinfo,
- BRCMF_PCIE_PCIE2REG_INTMASK) != 0) {
- if (brcmf_pcie_send_mb_data(devinfo,
- BRCMF_H2D_HOST_D0_INFORM))
- goto cleanup;
- brcmf_dbg(PCIE, "Hot resume, continue....\n");
- brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
- brcmf_bus_change_state(bus, BRCMF_BUS_UP);
- brcmf_pcie_intr_enable(devinfo);
- return 0;
- }
+ if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_INTMASK) != 0) {
+ brcmf_dbg(PCIE, "Try to wakeup device....\n");
+ if (brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D0_INFORM))
+ goto cleanup;
+ brcmf_dbg(PCIE, "Hot resume, continue....\n");
+ devinfo->state = BRCMFMAC_PCIE_STATE_UP;
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
+ brcmf_bus_change_state(bus, BRCMF_BUS_UP);
+ brcmf_pcie_intr_enable(devinfo);
+ return 0;
}
cleanup:
- if (bus) {
- devinfo = bus->bus_priv.pcie->devinfo;
- brcmf_chip_detach(devinfo->ci);
- devinfo->ci = NULL;
- brcmf_pcie_remove(pdev);
- }
+ brcmf_chip_detach(devinfo->ci);
+ devinfo->ci = NULL;
+ pdev = devinfo->pdev;
+ brcmf_pcie_remove(pdev);
+
err = brcmf_pcie_probe(pdev, NULL);
if (err)
brcmf_err("probe after resume failed, err=%d\n", err);
@@ -2046,6 +1938,14 @@ cleanup:
}
+static const struct dev_pm_ops brcmf_pciedrvr_pm = {
+ .suspend = brcmf_pcie_pm_enter_D3,
+ .resume = brcmf_pcie_pm_leave_D3,
+ .freeze = brcmf_pcie_pm_enter_D3,
+ .restore = brcmf_pcie_pm_leave_D3,
+};
+
+
#endif /* CONFIG_PM */
@@ -2058,6 +1958,7 @@ static struct pci_device_id brcmf_pcie_devid_table[] = {
BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID),
BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID),
BRCMF_PCIE_DEVICE(BRCM_PCIE_4358_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_4359_DEVICE_ID),
BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID),
BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID),
BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID),
@@ -2083,9 +1984,8 @@ static struct pci_driver brcmf_pciedrvr = {
.probe = brcmf_pcie_probe,
.remove = brcmf_pcie_remove,
#ifdef CONFIG_PM
- .suspend = brcmf_pcie_suspend,
- .resume = brcmf_pcie_resume
-#endif /* CONFIG_PM */
+ .driver.pm = &brcmf_pciedrvr_pm,
+#endif
};
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h
index 6edaaf8ef5ce..6edaaf8ef5ce 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c
index 26b68c367f57..26b68c367f57 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/proto.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h
index d55119d36755..d55119d36755 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/proto.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
index 7e74ac3ad815..dd6614332836 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -45,8 +45,8 @@
#include "chip.h"
#include "firmware.h"
-#define DCMD_RESP_TIMEOUT 2000 /* In milli second */
-#define CTL_DONE_TIMEOUT 2000 /* In milli second */
+#define DCMD_RESP_TIMEOUT msecs_to_jiffies(2000)
+#define CTL_DONE_TIMEOUT msecs_to_jiffies(2000)
#ifdef DEBUG
@@ -460,7 +460,6 @@ struct brcmf_sdio {
struct sk_buff *glomd; /* Packet containing glomming descriptor */
struct sk_buff_head glom; /* Packet list for glommed superframe */
- uint glomerr; /* Glom packet read errors */
u8 *rxbuf; /* Buffer for receiving control packets */
uint rxblen; /* Allocated length of rxbuf */
@@ -504,8 +503,7 @@ struct brcmf_sdio {
struct timer_list timer;
struct completion watchdog_wait;
struct task_struct *watchdog_tsk;
- bool wd_timer_valid;
- uint save_ms;
+ bool wd_active;
struct workqueue_struct *brcmf_wq;
struct work_struct datawork;
@@ -597,136 +595,41 @@ static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = {
{4, 0x1}
};
-#define BCM43143_FIRMWARE_NAME "brcm/brcmfmac43143-sdio.bin"
-#define BCM43143_NVRAM_NAME "brcm/brcmfmac43143-sdio.txt"
-#define BCM43241B0_FIRMWARE_NAME "brcm/brcmfmac43241b0-sdio.bin"
-#define BCM43241B0_NVRAM_NAME "brcm/brcmfmac43241b0-sdio.txt"
-#define BCM43241B4_FIRMWARE_NAME "brcm/brcmfmac43241b4-sdio.bin"
-#define BCM43241B4_NVRAM_NAME "brcm/brcmfmac43241b4-sdio.txt"
-#define BCM43241B5_FIRMWARE_NAME "brcm/brcmfmac43241b5-sdio.bin"
-#define BCM43241B5_NVRAM_NAME "brcm/brcmfmac43241b5-sdio.txt"
-#define BCM4329_FIRMWARE_NAME "brcm/brcmfmac4329-sdio.bin"
-#define BCM4329_NVRAM_NAME "brcm/brcmfmac4329-sdio.txt"
-#define BCM4330_FIRMWARE_NAME "brcm/brcmfmac4330-sdio.bin"
-#define BCM4330_NVRAM_NAME "brcm/brcmfmac4330-sdio.txt"
-#define BCM4334_FIRMWARE_NAME "brcm/brcmfmac4334-sdio.bin"
-#define BCM4334_NVRAM_NAME "brcm/brcmfmac4334-sdio.txt"
-#define BCM43340_FIRMWARE_NAME "brcm/brcmfmac43340-sdio.bin"
-#define BCM43340_NVRAM_NAME "brcm/brcmfmac43340-sdio.txt"
-#define BCM4335_FIRMWARE_NAME "brcm/brcmfmac4335-sdio.bin"
-#define BCM4335_NVRAM_NAME "brcm/brcmfmac4335-sdio.txt"
-#define BCM43362_FIRMWARE_NAME "brcm/brcmfmac43362-sdio.bin"
-#define BCM43362_NVRAM_NAME "brcm/brcmfmac43362-sdio.txt"
-#define BCM4339_FIRMWARE_NAME "brcm/brcmfmac4339-sdio.bin"
-#define BCM4339_NVRAM_NAME "brcm/brcmfmac4339-sdio.txt"
-#define BCM43430_FIRMWARE_NAME "brcm/brcmfmac43430-sdio.bin"
-#define BCM43430_NVRAM_NAME "brcm/brcmfmac43430-sdio.txt"
-#define BCM43455_FIRMWARE_NAME "brcm/brcmfmac43455-sdio.bin"
-#define BCM43455_NVRAM_NAME "brcm/brcmfmac43455-sdio.txt"
-#define BCM4354_FIRMWARE_NAME "brcm/brcmfmac4354-sdio.bin"
-#define BCM4354_NVRAM_NAME "brcm/brcmfmac4354-sdio.txt"
-
-MODULE_FIRMWARE(BCM43143_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM43143_NVRAM_NAME);
-MODULE_FIRMWARE(BCM43241B0_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM43241B0_NVRAM_NAME);
-MODULE_FIRMWARE(BCM43241B4_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM43241B4_NVRAM_NAME);
-MODULE_FIRMWARE(BCM43241B5_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM43241B5_NVRAM_NAME);
-MODULE_FIRMWARE(BCM4329_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM4329_NVRAM_NAME);
-MODULE_FIRMWARE(BCM4330_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM4330_NVRAM_NAME);
-MODULE_FIRMWARE(BCM4334_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM4334_NVRAM_NAME);
-MODULE_FIRMWARE(BCM43340_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM43340_NVRAM_NAME);
-MODULE_FIRMWARE(BCM4335_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM4335_NVRAM_NAME);
-MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM43362_NVRAM_NAME);
-MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM4339_NVRAM_NAME);
-MODULE_FIRMWARE(BCM43430_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM43430_NVRAM_NAME);
-MODULE_FIRMWARE(BCM43455_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM43455_NVRAM_NAME);
-MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME);
-MODULE_FIRMWARE(BCM4354_NVRAM_NAME);
-
-struct brcmf_firmware_names {
- u32 chipid;
- u32 revmsk;
- const char *bin;
- const char *nv;
+BRCMF_FW_NVRAM_DEF(43143, "brcmfmac43143-sdio.bin", "brcmfmac43143-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43241B0, "brcmfmac43241b0-sdio.bin",
+ "brcmfmac43241b0-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43241B4, "brcmfmac43241b4-sdio.bin",
+ "brcmfmac43241b4-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43241B5, "brcmfmac43241b5-sdio.bin",
+ "brcmfmac43241b5-sdio.txt");
+BRCMF_FW_NVRAM_DEF(4329, "brcmfmac4329-sdio.bin", "brcmfmac4329-sdio.txt");
+BRCMF_FW_NVRAM_DEF(4330, "brcmfmac4330-sdio.bin", "brcmfmac4330-sdio.txt");
+BRCMF_FW_NVRAM_DEF(4334, "brcmfmac4334-sdio.bin", "brcmfmac4334-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43340, "brcmfmac43340-sdio.bin", "brcmfmac43340-sdio.txt");
+BRCMF_FW_NVRAM_DEF(4335, "brcmfmac4335-sdio.bin", "brcmfmac4335-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43362, "brcmfmac43362-sdio.bin", "brcmfmac43362-sdio.txt");
+BRCMF_FW_NVRAM_DEF(4339, "brcmfmac4339-sdio.bin", "brcmfmac4339-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43430, "brcmfmac43430-sdio.bin", "brcmfmac43430-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43455, "brcmfmac43455-sdio.bin", "brcmfmac43455-sdio.txt");
+BRCMF_FW_NVRAM_DEF(4354, "brcmfmac4354-sdio.bin", "brcmfmac4354-sdio.txt");
+
+static struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = {
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43241_CHIP_ID, 0x0000001F, 43241B0),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43241_CHIP_ID, 0x00000020, 43241B4),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43241_CHIP_ID, 0xFFFFFFC0, 43241B5),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, 4329),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, 4330),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, 4334),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43340_CHIP_ID, 0xFFFFFFFF, 43340),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, 4335),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, 43362),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, 4339),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43430_CHIP_ID, 0xFFFFFFFF, 43430),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4345_CHIP_ID, 0xFFFFFFC0, 43455),
+ BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, 4354)
};
-enum brcmf_firmware_type {
- BRCMF_FIRMWARE_BIN,
- BRCMF_FIRMWARE_NVRAM
-};
-
-#define BRCMF_FIRMWARE_NVRAM(name) \
- name ## _FIRMWARE_NAME, name ## _NVRAM_NAME
-
-static const struct brcmf_firmware_names brcmf_fwname_data[] = {
- { BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) },
- { BRCM_CC_43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) },
- { BRCM_CC_43241_CHIP_ID, 0x00000020, BRCMF_FIRMWARE_NVRAM(BCM43241B4) },
- { BRCM_CC_43241_CHIP_ID, 0xFFFFFFC0, BRCMF_FIRMWARE_NVRAM(BCM43241B5) },
- { BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) },
- { BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) },
- { BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) },
- { BRCM_CC_43340_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43340) },
- { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) },
- { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) },
- { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) },
- { BRCM_CC_43430_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43430) },
- { BRCM_CC_4345_CHIP_ID, 0xFFFFFFC0, BRCMF_FIRMWARE_NVRAM(BCM43455) },
- { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) }
-};
-
-static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci,
- struct brcmf_sdio_dev *sdiodev)
-{
- int i;
- char end;
-
- for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) {
- if (brcmf_fwname_data[i].chipid == ci->chip &&
- brcmf_fwname_data[i].revmsk & BIT(ci->chiprev))
- break;
- }
-
- if (i == ARRAY_SIZE(brcmf_fwname_data)) {
- brcmf_err("Unknown chipid %d [%d]\n", ci->chip, ci->chiprev);
- return -ENODEV;
- }
-
- /* check if firmware path is provided by module parameter */
- if (brcmf_firmware_path[0] != '\0') {
- strlcpy(sdiodev->fw_name, brcmf_firmware_path,
- sizeof(sdiodev->fw_name));
- strlcpy(sdiodev->nvram_name, brcmf_firmware_path,
- sizeof(sdiodev->nvram_name));
-
- end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1];
- if (end != '/') {
- strlcat(sdiodev->fw_name, "/",
- sizeof(sdiodev->fw_name));
- strlcat(sdiodev->nvram_name, "/",
- sizeof(sdiodev->nvram_name));
- }
- }
- strlcat(sdiodev->fw_name, brcmf_fwname_data[i].bin,
- sizeof(sdiodev->fw_name));
- strlcat(sdiodev->nvram_name, brcmf_fwname_data[i].nv,
- sizeof(sdiodev->nvram_name));
-
- return 0;
-}
-
static void pkt_align(struct sk_buff *p, int len, int align)
{
uint datalign;
@@ -1057,7 +960,7 @@ end:
brcmf_sdio_clkctl(bus, CLK_NONE, pendok);
} else {
brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok);
- brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
+ brcmf_sdio_wd_timer(bus, true);
}
bus->sleeping = sleep;
brcmf_dbg(SDIO, "new state %s\n",
@@ -1654,20 +1557,15 @@ static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq)
sdio_release_host(bus->sdiodev->func[1]);
bus->sdcnt.f2rxdata++;
- /* On failure, kill the superframe, allow a couple retries */
+ /* On failure, kill the superframe */
if (errcode < 0) {
brcmf_err("glom read of %d bytes failed: %d\n",
dlen, errcode);
sdio_claim_host(bus->sdiodev->func[1]);
- if (bus->glomerr++ < 3) {
- brcmf_sdio_rxfail(bus, true, true);
- } else {
- bus->glomerr = 0;
- brcmf_sdio_rxfail(bus, true, false);
- bus->sdcnt.rxglomfail++;
- brcmf_sdio_free_glom(bus);
- }
+ brcmf_sdio_rxfail(bus, true, false);
+ bus->sdcnt.rxglomfail++;
+ brcmf_sdio_free_glom(bus);
sdio_release_host(bus->sdiodev->func[1]);
return 0;
}
@@ -1708,19 +1606,11 @@ static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq)
}
if (errcode) {
- /* Terminate frame on error, request
- a couple retries */
+ /* Terminate frame on error */
sdio_claim_host(bus->sdiodev->func[1]);
- if (bus->glomerr++ < 3) {
- /* Restore superframe header space */
- skb_push(pfirst, sfdoff);
- brcmf_sdio_rxfail(bus, true, true);
- } else {
- bus->glomerr = 0;
- brcmf_sdio_rxfail(bus, true, false);
- bus->sdcnt.rxglomfail++;
- brcmf_sdio_free_glom(bus);
- }
+ brcmf_sdio_rxfail(bus, true, false);
+ bus->sdcnt.rxglomfail++;
+ brcmf_sdio_free_glom(bus);
sdio_release_host(bus->sdiodev->func[1]);
bus->cur_read.len = 0;
return 0;
@@ -1767,7 +1657,7 @@ static int brcmf_sdio_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition,
bool *pending)
{
DECLARE_WAITQUEUE(wait, current);
- int timeout = msecs_to_jiffies(DCMD_RESP_TIMEOUT);
+ int timeout = DCMD_RESP_TIMEOUT;
/* Wait until control frame is available */
add_wait_queue(&bus->dcmd_resp_wait, &wait);
@@ -1787,8 +1677,7 @@ static int brcmf_sdio_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition,
static int brcmf_sdio_dcmd_resp_wake(struct brcmf_sdio *bus)
{
- if (waitqueue_active(&bus->dcmd_resp_wait))
- wake_up_interruptible(&bus->dcmd_resp_wait);
+ wake_up_interruptible(&bus->dcmd_resp_wait);
return 0;
}
@@ -2112,8 +2001,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
static void
brcmf_sdio_wait_event_wakeup(struct brcmf_sdio *bus)
{
- if (waitqueue_active(&bus->ctrl_wait))
- wake_up_interruptible(&bus->ctrl_wait);
+ wake_up_interruptible(&bus->ctrl_wait);
return;
}
@@ -2954,7 +2842,7 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
brcmf_sdio_trigger_dpc(bus);
wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat,
- msecs_to_jiffies(CTL_DONE_TIMEOUT));
+ CTL_DONE_TIMEOUT);
ret = 0;
if (bus->ctrl_frame_stat) {
sdio_claim_host(bus->sdiodev->func[1]);
@@ -3664,7 +3552,7 @@ static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus)
/* Poll for console output periodically */
if (bus->sdiodev->state == BRCMF_SDIOD_DATA && BRCMF_FWCON_ON() &&
bus->console_interval != 0) {
- bus->console.count += BRCMF_WD_POLL_MS;
+ bus->console.count += jiffies_to_msecs(BRCMF_WD_POLL);
if (bus->console.count >= bus->console_interval) {
bus->console.count -= bus->console_interval;
sdio_claim_host(bus->sdiodev->func[1]);
@@ -3687,7 +3575,7 @@ static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus)
if (bus->idlecount > bus->idletime) {
brcmf_dbg(SDIO, "idle\n");
sdio_claim_host(bus->sdiodev->func[1]);
- brcmf_sdio_wd_timer(bus, 0);
+ brcmf_sdio_wd_timer(bus, false);
bus->idlecount = 0;
brcmf_sdio_bus_sleep(bus, true, false);
sdio_release_host(bus->sdiodev->func[1]);
@@ -4019,13 +3907,13 @@ brcmf_sdio_watchdog(unsigned long data)
if (bus->watchdog_tsk) {
complete(&bus->watchdog_wait);
/* Reschedule the watchdog */
- if (bus->wd_timer_valid)
+ if (bus->wd_active)
mod_timer(&bus->timer,
- jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS));
+ jiffies + BRCMF_WD_POLL);
}
}
-static struct brcmf_bus_ops brcmf_sdio_bus_ops = {
+static const struct brcmf_bus_ops brcmf_sdio_bus_ops = {
.stop = brcmf_sdio_bus_stop,
.preinit = brcmf_sdio_bus_preinit,
.txdata = brcmf_sdio_bus_txdata,
@@ -4061,7 +3949,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev,
/* Start the watchdog timer */
bus->sdcnt.tickcnt = 0;
- brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
+ brcmf_sdio_wd_timer(bus, true);
sdio_claim_host(sdiodev->func[1]);
@@ -4266,7 +4154,10 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
brcmf_sdio_debugfs_create(bus);
brcmf_dbg(INFO, "completed!!\n");
- ret = brcmf_sdio_get_fwnames(bus->ci, sdiodev);
+ ret = brcmf_fw_map_chip_to_name(bus->ci->chip, bus->ci->chiprev,
+ brcmf_sdio_fwnames,
+ ARRAY_SIZE(brcmf_sdio_fwnames),
+ sdiodev->fw_name, sdiodev->nvram_name);
if (ret)
goto fail;
@@ -4303,7 +4194,7 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus)
if (bus->ci) {
if (bus->sdiodev->state != BRCMF_SDIOD_NOMEDIUM) {
sdio_claim_host(bus->sdiodev->func[1]);
- brcmf_sdio_wd_timer(bus, 0);
+ brcmf_sdio_wd_timer(bus, false);
brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
/* Leave the device in state where it is
* 'passive'. This is done by resetting all
@@ -4325,13 +4216,12 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus)
brcmf_dbg(TRACE, "Disconnected\n");
}
-void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick)
+void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active)
{
/* Totally stop the timer */
- if (!wdtick && bus->wd_timer_valid) {
+ if (!active && bus->wd_active) {
del_timer_sync(&bus->timer);
- bus->wd_timer_valid = false;
- bus->save_ms = wdtick;
+ bus->wd_active = false;
return;
}
@@ -4339,27 +4229,18 @@ void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick)
if (bus->sdiodev->state != BRCMF_SDIOD_DATA)
return;
- if (wdtick) {
- if (bus->save_ms != BRCMF_WD_POLL_MS) {
- if (bus->wd_timer_valid)
- /* Stop timer and restart at new value */
- del_timer_sync(&bus->timer);
-
+ if (active) {
+ if (!bus->wd_active) {
/* Create timer again when watchdog period is
dynamically changed or in the first instance
*/
- bus->timer.expires =
- jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS);
+ bus->timer.expires = jiffies + BRCMF_WD_POLL;
add_timer(&bus->timer);
-
+ bus->wd_active = true;
} else {
/* Re arm the timer, at last watchdog period */
- mod_timer(&bus->timer,
- jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS));
+ mod_timer(&bus->timer, jiffies + BRCMF_WD_POLL);
}
-
- bus->wd_timer_valid = true;
- bus->save_ms = wdtick;
}
}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
index 7328478b2d7b..5ec7a6d87672 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
@@ -152,8 +152,8 @@
/* Packet alignment for most efficient SDIO (can change based on platform) */
#define BRCMF_SDALIGN (1 << 6)
-/* watchdog polling interval in ms */
-#define BRCMF_WD_POLL_MS 10
+/* watchdog polling interval */
+#define BRCMF_WD_POLL msecs_to_jiffies(10)
/**
* enum brcmf_sdiod_state - the state of the bus.
@@ -195,8 +195,8 @@ struct brcmf_sdio_dev {
uint max_segment_size;
uint txglomsz;
struct sg_table sgtable;
- char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
- char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
+ char fw_name[BRCMF_FW_NAME_LEN];
+ char nvram_name[BRCMF_FW_NAME_LEN];
bool wowl_enabled;
enum brcmf_sdiod_state state;
struct brcmf_sdiod_freezer *freezer;
@@ -369,7 +369,7 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev);
void brcmf_sdio_remove(struct brcmf_sdio *bus);
void brcmf_sdio_isr(struct brcmf_sdio *bus);
-void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick);
+void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active);
void brcmf_sdio_wowl_config(struct device *dev, bool enabled);
int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep);
void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.c
index a10f35c5eb3d..a10f35c5eb3d 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.c
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.h
index 4d7d51f95716..4d7d51f95716 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/tracepoint.h
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
index 689e64d004bc..c72b7b352a77 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
@@ -29,7 +29,7 @@
#include "usb.h"
-#define IOCTL_RESP_TIMEOUT 2000
+#define IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000)
#define BRCMF_USB_RESET_GETVER_SPINWAIT 100 /* in unit of ms */
#define BRCMF_USB_RESET_GETVER_LOOP_CNT 10
@@ -43,10 +43,20 @@
#define BRCMF_USB_CBCTL_READ 1
#define BRCMF_USB_MAX_PKT_SIZE 1600
-#define BRCMF_USB_43143_FW_NAME "brcm/brcmfmac43143.bin"
-#define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin"
-#define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin"
-#define BRCMF_USB_43569_FW_NAME "brcm/brcmfmac43569.bin"
+BRCMF_FW_DEF(43143, "brcmfmac43143.bin");
+BRCMF_FW_DEF(43236B, "brcmfmac43236b.bin");
+BRCMF_FW_DEF(43242A, "brcmfmac43242a.bin");
+BRCMF_FW_DEF(43569, "brcmfmac43569.bin");
+
+static struct brcmf_firmware_mapping brcmf_usb_fwnames[] = {
+ BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143),
+ BRCMF_FW_ENTRY(BRCM_CC_43235_CHIP_ID, 0x00000008, 43236B),
+ BRCMF_FW_ENTRY(BRCM_CC_43236_CHIP_ID, 0x00000008, 43236B),
+ BRCMF_FW_ENTRY(BRCM_CC_43238_CHIP_ID, 0x00000008, 43236B),
+ BRCMF_FW_ENTRY(BRCM_CC_43242_CHIP_ID, 0xFFFFFFFF, 43242A),
+ BRCMF_FW_ENTRY(BRCM_CC_43566_CHIP_ID, 0xFFFFFFFF, 43569),
+ BRCMF_FW_ENTRY(BRCM_CC_43569_CHIP_ID, 0xFFFFFFFF, 43569)
+};
#define TRX_MAGIC 0x30524448 /* "HDR0" */
#define TRX_MAX_OFFSET 3 /* Max number of file offsets */
@@ -139,6 +149,7 @@ struct brcmf_usbdev_info {
struct brcmf_usbreq *tx_reqs;
struct brcmf_usbreq *rx_reqs;
+ char fw_name[BRCMF_FW_NAME_LEN];
const u8 *image; /* buffer for combine fw and nvram */
int image_len;
@@ -179,14 +190,12 @@ static struct brcmf_usbdev_info *brcmf_usb_get_businfo(struct device *dev)
static int brcmf_usb_ioctl_resp_wait(struct brcmf_usbdev_info *devinfo)
{
return wait_event_timeout(devinfo->ioctl_resp_wait,
- devinfo->ctl_completed,
- msecs_to_jiffies(IOCTL_RESP_TIMEOUT));
+ devinfo->ctl_completed, IOCTL_RESP_TIMEOUT);
}
static void brcmf_usb_ioctl_resp_wake(struct brcmf_usbdev_info *devinfo)
{
- if (waitqueue_active(&devinfo->ioctl_resp_wait))
- wake_up(&devinfo->ioctl_resp_wait);
+ wake_up(&devinfo->ioctl_resp_wait);
}
static void
@@ -983,45 +992,15 @@ static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo)
return 0;
}
-static bool brcmf_usb_chip_support(int chipid, int chiprev)
-{
- switch(chipid) {
- case BRCM_CC_43143_CHIP_ID:
- return true;
- case BRCM_CC_43235_CHIP_ID:
- case BRCM_CC_43236_CHIP_ID:
- case BRCM_CC_43238_CHIP_ID:
- return (chiprev == 3);
- case BRCM_CC_43242_CHIP_ID:
- return true;
- case BRCM_CC_43566_CHIP_ID:
- case BRCM_CC_43569_CHIP_ID:
- return true;
- default:
- break;
- }
- return false;
-}
-
static int
brcmf_usb_fw_download(struct brcmf_usbdev_info *devinfo)
{
- int devid, chiprev;
int err;
brcmf_dbg(USB, "Enter\n");
if (devinfo == NULL)
return -ENODEV;
- devid = devinfo->bus_pub.devid;
- chiprev = devinfo->bus_pub.chiprev;
-
- if (!brcmf_usb_chip_support(devid, chiprev)) {
- brcmf_err("unsupported chip %d rev %d\n",
- devid, chiprev);
- return -EINVAL;
- }
-
if (!devinfo->image) {
brcmf_err("No firmware!\n");
return -ENOENT;
@@ -1071,25 +1050,6 @@ static int check_file(const u8 *headers)
return -1;
}
-static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo)
-{
- switch (devinfo->bus_pub.devid) {
- case BRCM_CC_43143_CHIP_ID:
- return BRCMF_USB_43143_FW_NAME;
- case BRCM_CC_43235_CHIP_ID:
- case BRCM_CC_43236_CHIP_ID:
- case BRCM_CC_43238_CHIP_ID:
- return BRCMF_USB_43236_FW_NAME;
- case BRCM_CC_43242_CHIP_ID:
- return BRCMF_USB_43242_FW_NAME;
- case BRCM_CC_43566_CHIP_ID:
- case BRCM_CC_43569_CHIP_ID:
- return BRCMF_USB_43569_FW_NAME;
- default:
- return NULL;
- }
-}
-
static
struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo,
@@ -1163,7 +1123,7 @@ static void brcmf_usb_wowl_config(struct device *dev, bool enabled)
device_set_wakeup_enable(devinfo->dev, false);
}
-static struct brcmf_bus_ops brcmf_usb_bus_ops = {
+static const struct brcmf_bus_ops brcmf_usb_bus_ops = {
.txdata = brcmf_usb_tx,
.stop = brcmf_usb_down,
.txctl = brcmf_usb_tx_ctlpkt,
@@ -1274,9 +1234,16 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo)
bus->chip = bus_pub->devid;
bus->chiprev = bus_pub->chiprev;
+ ret = brcmf_fw_map_chip_to_name(bus_pub->devid, bus_pub->chiprev,
+ brcmf_usb_fwnames,
+ ARRAY_SIZE(brcmf_usb_fwnames),
+ devinfo->fw_name, NULL);
+ if (ret)
+ goto fail;
+
/* request firmware here */
- ret = brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo),
- NULL, brcmf_usb_probe_phase2);
+ ret = brcmf_fw_get_firmwares(dev, 0, devinfo->fw_name, NULL,
+ brcmf_usb_probe_phase2);
if (ret) {
brcmf_err("firmware request failed: %d\n", ret);
goto fail;
@@ -1472,8 +1439,7 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf)
brcmf_dbg(USB, "Enter\n");
- return brcmf_fw_get_firmwares(&usb->dev, 0,
- brcmf_usb_get_fwname(devinfo), NULL,
+ return brcmf_fw_get_firmwares(&usb->dev, 0, devinfo->fw_name, NULL,
brcmf_usb_probe_phase2);
}
@@ -1485,16 +1451,13 @@ static struct usb_device_id brcmf_usb_devid_table[] = {
BRCMF_USB_DEVICE(BRCM_USB_43236_DEVICE_ID),
BRCMF_USB_DEVICE(BRCM_USB_43242_DEVICE_ID),
BRCMF_USB_DEVICE(BRCM_USB_43569_DEVICE_ID),
+ { USB_DEVICE(BRCM_USB_VENDOR_ID_LG, BRCM_USB_43242_LG_DEVICE_ID) },
/* special entry for device with firmware loaded and running */
BRCMF_USB_DEVICE(BRCM_USB_BCMFW_DEVICE_ID),
{ /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(usb, brcmf_usb_devid_table);
-MODULE_FIRMWARE(BRCMF_USB_43143_FW_NAME);
-MODULE_FIRMWARE(BRCMF_USB_43236_FW_NAME);
-MODULE_FIRMWARE(BRCMF_USB_43242_FW_NAME);
-MODULE_FIRMWARE(BRCMF_USB_43569_FW_NAME);
static struct usb_driver brcmf_usbdrvr = {
.name = KBUILD_MODNAME,
@@ -1504,7 +1467,6 @@ static struct usb_driver brcmf_usbdrvr = {
.suspend = brcmf_usb_suspend,
.resume = brcmf_usb_resume,
.reset_resume = brcmf_usb_reset_resume,
- .supports_autosuspend = 1,
.disable_hub_initiated_lpm = 1,
};
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.h
index f483a8c9945b..f483a8c9945b 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/usb.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.h
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c
index 8eff2753abad..8eff2753abad 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h
index 061b7bfa2e1c..061b7bfa2e1c 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/vendor.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile
index 32464acccd90..960e6b86bbcb 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/Makefile
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/Makefile
@@ -17,9 +17,9 @@
ccflags-y := \
-D__CHECK_ENDIAN__ \
- -Idrivers/net/wireless/brcm80211/brcmsmac \
- -Idrivers/net/wireless/brcm80211/brcmsmac/phy \
- -Idrivers/net/wireless/brcm80211/include
+ -Idrivers/net/wireless/broadcom/brcm80211/brcmsmac \
+ -Idrivers/net/wireless/broadcom/brcm80211/brcmsmac/phy \
+ -Idrivers/net/wireless/broadcom/brcm80211/include
brcmsmac-y := \
mac80211_if.o \
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.c
index 53365977bfd6..53365977bfd6 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.h
index 2d08c155c23b..2d08c155c23b 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.c
index fa391e4eb098..fa391e4eb098 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.h
index 03bdcf29bd50..03bdcf29bd50 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/antsel.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.c
index 54c616919590..54c616919590 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/antsel.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/antsel.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.h
index a3d487ab1964..a3d487ab1964 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/antsel.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac.h
index a0da3248b942..a0da3248b942 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h
index 0e8a69ab909f..0e8a69ab909f 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h
index cf2cc070f1e5..cf2cc070f1e5 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_events.c
index 52fc9eeb5fa5..52fc9eeb5fa5 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_events.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_events.h
index cbf2f06436fc..cbf2f06436fc 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/brcms_trace_events.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c
index 635ae034c7e5..38bd5890bd53 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c
@@ -177,8 +177,8 @@ static bool brcms_c_country_valid(const char *ccode)
* only allow ascii alpha uppercase for the first 2
* chars.
*/
- if (!((0x80 & ccode[0]) == 0 && ccode[0] >= 0x41 && ccode[0] <= 0x5A &&
- (0x80 & ccode[1]) == 0 && ccode[1] >= 0x41 && ccode[1] <= 0x5A))
+ if (!((ccode[0] & 0x80) == 0 && ccode[0] >= 0x41 && ccode[0] <= 0x5A &&
+ (ccode[1] & 0x80) == 0 && ccode[1] >= 0x41 && ccode[1] <= 0x5A))
return false;
/*
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.h
index 39dd3a5b2979..39dd3a5b2979 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/channel.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/d11.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/d11.h
index 9035cc4d6ff3..9035cc4d6ff3 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/d11.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/d11.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/debug.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c
index 7a1fbb2e3a71..7a1fbb2e3a71 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/debug.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/debug.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.h
index 822781cf15d4..822781cf15d4 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/debug.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/debug.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c
index 796f5f9d5d5a..796f5f9d5d5a 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/dma.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.h
index ff5b80b09046..ff5b80b09046 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/dma.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/led.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c
index 74b17cecb189..74b17cecb189 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/led.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/led.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.h
index 17a0b1f5dbcf..17a0b1f5dbcf 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/led.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/led.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
index bec2dc1ca2e4..bec2dc1ca2e4 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.h
index 198053dfc310..198053dfc310 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c
index 218cbc8bf3a7..218cbc8bf3a7 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/main.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h
index c4d135cff04a..c4d135cff04a 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/main.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c
index 1c4e9dd57960..1c4e9dd57960 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_cmn.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_hal.h
index 4d3734f48d9c..4d3734f48d9c 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_hal.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_int.h
index 4960f7d26804..4960f7d26804 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_int.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c
index 93d4cde0eb31..93d4cde0eb31 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.h
index f4a8ab09da43..f4a8ab09da43 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c
index 99dac9b8a082..99dac9b8a082 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.c
index faf1ebe76068..faf1ebe76068 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.h
index 20e3783f921b..20e3783f921b 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_qmath.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_radio.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_radio.h
index c3a675455ff5..c3a675455ff5 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_radio.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_radio.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phyreg_n.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phyreg_n.h
index a97c3a799479..a97c3a799479 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phyreg_n.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phyreg_n.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.c
index d7fa312214f3..d7fa312214f3 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.h
index 489422a36085..489422a36085 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_lcn.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_n.c
index dbf50ef6cd75..dbf50ef6cd75 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_n.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_n.h
index dc8a84e85117..dc8a84e85117 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phytbl_n.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.c
index a0de5db0cd64..a0de5db0cd64 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.h
index dd8774717ade..dd8774717ade 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy_shim.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/pmu.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.c
index 71b80381f3ad..71b80381f3ad 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/pmu.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/pmu.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.h
index a014bbc4f935..a014bbc4f935 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/pmu.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/pub.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pub.h
index 4da38cb4f318..4da38cb4f318 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/pub.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pub.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/rate.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/rate.c
index 0a0c0ad4f96f..0a0c0ad4f96f 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/rate.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/rate.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/rate.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/rate.h
index 5bb88b78ed64..5bb88b78ed64 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/rate.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/rate.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/scb.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/scb.h
index 3a3d73699f83..3a3d73699f83 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/scb.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/scb.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/stf.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/stf.c
index dd9162722495..dd9162722495 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/stf.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/stf.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/stf.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/stf.h
index ba9493009a33..ba9493009a33 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/stf.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/stf.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/types.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/types.h
index ae1f3ad40d45..ae1f3ad40d45 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/types.h
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ucode_loader.c
index 80e3ccf865e3..80e3ccf865e3 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ucode_loader.c
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ucode_loader.h
index c87dd89bcb78..c87dd89bcb78 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ucode_loader.h
diff --git a/drivers/net/wireless/brcm80211/brcmutil/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmutil/Makefile
index 8a928184016a..256c91f9ac4b 100644
--- a/drivers/net/wireless/brcm80211/brcmutil/Makefile
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmutil/Makefile
@@ -16,8 +16,8 @@
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ccflags-y := \
- -Idrivers/net/wireless/brcm80211/brcmutil \
- -Idrivers/net/wireless/brcm80211/include
+ -Idrivers/net/wireless/broadcom/brcm80211/brcmutil \
+ -Idrivers/net/wireless/broadcom/brcm80211/include
obj-$(CONFIG_BRCMUTIL) += brcmutil.o
brcmutil-objs = utils.o d11.o
diff --git a/drivers/net/wireless/brcm80211/brcmutil/d11.c b/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c
index 2b2522bdd8eb..2b2522bdd8eb 100644
--- a/drivers/net/wireless/brcm80211/brcmutil/d11.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c
diff --git a/drivers/net/wireless/brcm80211/brcmutil/utils.c b/drivers/net/wireless/broadcom/brcm80211/brcmutil/utils.c
index 0543607002fd..0543607002fd 100644
--- a/drivers/net/wireless/brcm80211/brcmutil/utils.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmutil/utils.c
diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h
index aa06ea231db3..699f2c2782ee 100644
--- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
+++ b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h
@@ -21,6 +21,7 @@
#include <linux/mmc/sdio_ids.h>
#define BRCM_USB_VENDOR_ID_BROADCOM 0x0a5c
+#define BRCM_USB_VENDOR_ID_LG 0x043e
#define BRCM_PCIE_VENDOR_ID_BROADCOM PCI_VENDOR_ID_BROADCOM
/* Chipcommon Core Chip IDs */
@@ -47,6 +48,7 @@
#define BRCM_CC_43569_CHIP_ID 43569
#define BRCM_CC_43570_CHIP_ID 43570
#define BRCM_CC_4358_CHIP_ID 0x4358
+#define BRCM_CC_4359_CHIP_ID 0x4359
#define BRCM_CC_43602_CHIP_ID 43602
#define BRCM_CC_4365_CHIP_ID 0x4365
#define BRCM_CC_4366_CHIP_ID 0x4366
@@ -56,6 +58,7 @@
#define BRCM_USB_43143_DEVICE_ID 0xbd1e
#define BRCM_USB_43236_DEVICE_ID 0xbd17
#define BRCM_USB_43242_DEVICE_ID 0xbd1f
+#define BRCM_USB_43242_LG_DEVICE_ID 0x3101
#define BRCM_USB_43569_DEVICE_ID 0xbd27
#define BRCM_USB_BCMFW_DEVICE_ID 0x0bdc
@@ -66,6 +69,7 @@
#define BRCM_PCIE_43567_DEVICE_ID 0x43d3
#define BRCM_PCIE_43570_DEVICE_ID 0x43d9
#define BRCM_PCIE_4358_DEVICE_ID 0x43e9
+#define BRCM_PCIE_4359_DEVICE_ID 0x43ef
#define BRCM_PCIE_43602_DEVICE_ID 0x43ba
#define BRCM_PCIE_43602_2G_DEVICE_ID 0x43bb
#define BRCM_PCIE_43602_5G_DEVICE_ID 0x43bc
diff --git a/drivers/net/wireless/brcm80211/include/brcmu_d11.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h
index f9745ea8b3e0..f9745ea8b3e0 100644
--- a/drivers/net/wireless/brcm80211/include/brcmu_d11.h
+++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h
diff --git a/drivers/net/wireless/brcm80211/include/brcmu_utils.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h
index 41969527b459..41969527b459 100644
--- a/drivers/net/wireless/brcm80211/include/brcmu_utils.h
+++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h
diff --git a/drivers/net/wireless/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h
index 76b5d3a86294..3f68dd5ecd11 100644
--- a/drivers/net/wireless/brcm80211/include/brcmu_wifi.h
+++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h
@@ -237,9 +237,6 @@ static inline bool ac_bitmap_tst(u8 bitmap, int prec)
#define WPA2_AUTH_RESERVED4 0x0400
#define WPA2_AUTH_RESERVED5 0x0800
-/* pmkid */
-#define MAXPMKID 16
-
#define DOT11_DEFAULT_RTS_LEN 2347
#define DOT11_DEFAULT_FRAG_LEN 2346
@@ -251,24 +248,4 @@ static inline bool ac_bitmap_tst(u8 bitmap, int prec)
#define HT_CAP_RX_STBC_NO 0x0
#define HT_CAP_RX_STBC_ONE_STREAM 0x1
-struct pmkid {
- u8 BSSID[ETH_ALEN];
- u8 PMKID[WLAN_PMKID_LEN];
-};
-
-struct pmkid_list {
- __le32 npmkid;
- struct pmkid pmkid[1];
-};
-
-struct pmkid_cand {
- u8 BSSID[ETH_ALEN];
- u8 preauth;
-};
-
-struct pmkid_cand_list {
- u32 npmkid_cand;
- struct pmkid_cand pmkid_cand[1];
-};
-
#endif /* _BRCMU_WIFI_H_ */
diff --git a/drivers/net/wireless/brcm80211/include/chipcommon.h b/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h
index e1fd499930a0..e1fd499930a0 100644
--- a/drivers/net/wireless/brcm80211/include/chipcommon.h
+++ b/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h
diff --git a/drivers/net/wireless/brcm80211/include/defs.h b/drivers/net/wireless/broadcom/brcm80211/include/defs.h
index 8d1e85e0ed51..8d1e85e0ed51 100644
--- a/drivers/net/wireless/brcm80211/include/defs.h
+++ b/drivers/net/wireless/broadcom/brcm80211/include/defs.h
diff --git a/drivers/net/wireless/brcm80211/include/soc.h b/drivers/net/wireless/broadcom/brcm80211/include/soc.h
index 123cfa854a0d..123cfa854a0d 100644
--- a/drivers/net/wireless/brcm80211/include/soc.h
+++ b/drivers/net/wireless/broadcom/brcm80211/include/soc.h
diff --git a/drivers/net/wireless/cisco/Kconfig b/drivers/net/wireless/cisco/Kconfig
new file mode 100644
index 000000000000..b22567dff893
--- /dev/null
+++ b/drivers/net/wireless/cisco/Kconfig
@@ -0,0 +1,56 @@
+config WLAN_VENDOR_CISCO
+ bool "Cisco devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_CISCO
+
+config AIRO
+ tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards"
+ depends on CFG80211 && ISA_DMA_API && (PCI || BROKEN)
+ select WIRELESS_EXT
+ select CRYPTO
+ select WEXT_SPY
+ select WEXT_PRIV
+ ---help---
+ This is the standard Linux driver to support Cisco/Aironet ISA and
+ PCI 802.11 wireless cards.
+ It supports the new 802.11b cards from Cisco (Cisco 34X, Cisco 35X
+ - with or without encryption) as well as card before the Cisco
+ acquisition (Aironet 4500, Aironet 4800, Aironet 4800B).
+
+ This driver support both the standard Linux Wireless Extensions
+ and Cisco proprietary API, so both the Linux Wireless Tools and the
+ Cisco Linux utilities can be used to configure the card.
+
+ The driver can be compiled as a module and will be named "airo".
+
+config AIRO_CS
+ tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards"
+ depends on CFG80211 && PCMCIA && (BROKEN || !M32R)
+ select WIRELESS_EXT
+ select WEXT_SPY
+ select WEXT_PRIV
+ select CRYPTO
+ select CRYPTO_AES
+ ---help---
+ This is the standard Linux driver to support Cisco/Aironet PCMCIA
+ 802.11 wireless cards. This driver is the same as the Aironet
+ driver part of the Linux Pcmcia package.
+ It supports the new 802.11b cards from Cisco (Cisco 34X, Cisco 35X
+ - with or without encryption) as well as card before the Cisco
+ acquisition (Aironet 4500, Aironet 4800, Aironet 4800B). It also
+ supports OEM of Cisco such as the DELL TrueMobile 4800 and Xircom
+ 802.11b cards.
+
+ This driver support both the standard Linux Wireless Extensions
+ and Cisco proprietary API, so both the Linux Wireless Tools and the
+ Cisco Linux utilities can be used to configure the card.
+
+endif # WLAN_VENDOR_CISCO
diff --git a/drivers/net/wireless/cisco/Makefile b/drivers/net/wireless/cisco/Makefile
new file mode 100644
index 000000000000..d4110b19d6ef
--- /dev/null
+++ b/drivers/net/wireless/cisco/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_AIRO) += airo.o
+obj-$(CONFIG_AIRO_CS) += airo_cs.o airo.o
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/cisco/airo.c
index 17c40f06f13e..d2353f6e5214 100644
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/cisco/airo.c
@@ -5137,21 +5137,9 @@ static void proc_APList_on_close( struct inode *inode, struct file *file ) {
memset(APList_rid, 0, sizeof(*APList_rid));
APList_rid->len = cpu_to_le16(sizeof(*APList_rid));
- for( i = 0; i < 4 && data->writelen >= (i+1)*6*3; i++ ) {
- int j;
- for( j = 0; j < 6*3 && data->wbuffer[j+i*6*3]; j++ ) {
- switch(j%3) {
- case 0:
- APList_rid->ap[i][j/3]=
- hex_to_bin(data->wbuffer[j+i*6*3])<<4;
- break;
- case 1:
- APList_rid->ap[i][j/3]|=
- hex_to_bin(data->wbuffer[j+i*6*3]);
- break;
- }
- }
- }
+ for (i = 0; i < 4 && data->writelen >= (i + 1) * 6 * 3; i++)
+ mac_pton(data->wbuffer + i * 6 * 3, APList_rid->ap[i]);
+
disable_MAC(ai, 1);
writeAPListRid(ai, APList_rid, 1);
enable_MAC(ai, 1);
diff --git a/drivers/net/wireless/airo.h b/drivers/net/wireless/cisco/airo.h
index e480adf86be6..e480adf86be6 100644
--- a/drivers/net/wireless/airo.h
+++ b/drivers/net/wireless/cisco/airo.h
diff --git a/drivers/net/wireless/airo_cs.c b/drivers/net/wireless/cisco/airo_cs.c
index d9ed22b4cc6b..d9ed22b4cc6b 100644
--- a/drivers/net/wireless/airo_cs.c
+++ b/drivers/net/wireless/cisco/airo_cs.c
diff --git a/drivers/net/wireless/intel/Kconfig b/drivers/net/wireless/intel/Kconfig
new file mode 100644
index 000000000000..5b14f2f64a8a
--- /dev/null
+++ b/drivers/net/wireless/intel/Kconfig
@@ -0,0 +1,18 @@
+config WLAN_VENDOR_INTEL
+ bool "Intel devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_INTEL
+
+source "drivers/net/wireless/intel/ipw2x00/Kconfig"
+source "drivers/net/wireless/intel/iwlegacy/Kconfig"
+source "drivers/net/wireless/intel/iwlwifi/Kconfig"
+
+endif # WLAN_VENDOR_INTEL
diff --git a/drivers/net/wireless/intel/Makefile b/drivers/net/wireless/intel/Makefile
new file mode 100644
index 000000000000..c9cbcc85b569
--- /dev/null
+++ b/drivers/net/wireless/intel/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_IPW2100) += ipw2x00/
+obj-$(CONFIG_IPW2200) += ipw2x00/
+
+obj-$(CONFIG_IWLEGACY) += iwlegacy/
+
+obj-$(CONFIG_IWLWIFI) += iwlwifi/
diff --git a/drivers/net/wireless/ipw2x00/Kconfig b/drivers/net/wireless/intel/ipw2x00/Kconfig
index d6ec44d7a391..d6ec44d7a391 100644
--- a/drivers/net/wireless/ipw2x00/Kconfig
+++ b/drivers/net/wireless/intel/ipw2x00/Kconfig
diff --git a/drivers/net/wireless/ipw2x00/Makefile b/drivers/net/wireless/intel/ipw2x00/Makefile
index aecd2cff462b..aecd2cff462b 100644
--- a/drivers/net/wireless/ipw2x00/Makefile
+++ b/drivers/net/wireless/intel/ipw2x00/Makefile
diff --git a/drivers/net/wireless/ipw2x00/ipw.h b/drivers/net/wireless/intel/ipw2x00/ipw.h
index 4007bf5ed6f3..4007bf5ed6f3 100644
--- a/drivers/net/wireless/ipw2x00/ipw.h
+++ b/drivers/net/wireless/intel/ipw2x00/ipw.h
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c
index 36818c7f30b9..f93a7f71c047 100644
--- a/drivers/net/wireless/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c
@@ -2311,8 +2311,10 @@ static int ipw2100_alloc_skb(struct ipw2100_priv *priv,
packet->dma_addr = pci_map_single(priv->pci_dev, packet->skb->data,
sizeof(struct ipw2100_rx),
PCI_DMA_FROMDEVICE);
- /* NOTE: pci_map_single does not return an error code, and 0 is a valid
- * dma_addr */
+ if (pci_dma_mapping_error(priv->pci_dev, packet->dma_addr)) {
+ dev_kfree_skb(packet->skb);
+ return -ENOMEM;
+ }
return 0;
}
@@ -3183,6 +3185,11 @@ static void ipw2100_tx_send_data(struct ipw2100_priv *priv)
LIBIPW_3ADDR_LEN,
tbd->buf_length,
PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(priv->pci_dev,
+ tbd->host_addr)) {
+ IPW_DEBUG_TX("dma mapping error\n");
+ break;
+ }
IPW_DEBUG_TX("data frag tbd TX%d P=%08x L=%d\n",
txq->next, tbd->host_addr,
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.h b/drivers/net/wireless/intel/ipw2x00/ipw2100.h
index 193947865efd..193947865efd 100644
--- a/drivers/net/wireless/ipw2x00/ipw2100.h
+++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.h
diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c
index ed0adaf1eec4..ed0adaf1eec4 100644
--- a/drivers/net/wireless/ipw2x00/ipw2200.c
+++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c
diff --git a/drivers/net/wireless/ipw2x00/ipw2200.h b/drivers/net/wireless/intel/ipw2x00/ipw2200.h
index aa301d1eee3c..aa301d1eee3c 100644
--- a/drivers/net/wireless/ipw2x00/ipw2200.h
+++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.h
diff --git a/drivers/net/wireless/ipw2x00/libipw.h b/drivers/net/wireless/intel/ipw2x00/libipw.h
index b0571618c2ed..b0571618c2ed 100644
--- a/drivers/net/wireless/ipw2x00/libipw.h
+++ b/drivers/net/wireless/intel/ipw2x00/libipw.h
diff --git a/drivers/net/wireless/ipw2x00/libipw_geo.c b/drivers/net/wireless/intel/ipw2x00/libipw_geo.c
index 218f2a32de21..218f2a32de21 100644
--- a/drivers/net/wireless/ipw2x00/libipw_geo.c
+++ b/drivers/net/wireless/intel/ipw2x00/libipw_geo.c
diff --git a/drivers/net/wireless/ipw2x00/libipw_module.c b/drivers/net/wireless/intel/ipw2x00/libipw_module.c
index 60f28740f6af..60f28740f6af 100644
--- a/drivers/net/wireless/ipw2x00/libipw_module.c
+++ b/drivers/net/wireless/intel/ipw2x00/libipw_module.c
diff --git a/drivers/net/wireless/ipw2x00/libipw_rx.c b/drivers/net/wireless/intel/ipw2x00/libipw_rx.c
index cef7f7d79cd9..cef7f7d79cd9 100644
--- a/drivers/net/wireless/ipw2x00/libipw_rx.c
+++ b/drivers/net/wireless/intel/ipw2x00/libipw_rx.c
diff --git a/drivers/net/wireless/ipw2x00/libipw_tx.c b/drivers/net/wireless/intel/ipw2x00/libipw_tx.c
index e8c039879b05..e8c039879b05 100644
--- a/drivers/net/wireless/ipw2x00/libipw_tx.c
+++ b/drivers/net/wireless/intel/ipw2x00/libipw_tx.c
diff --git a/drivers/net/wireless/ipw2x00/libipw_wx.c b/drivers/net/wireless/intel/ipw2x00/libipw_wx.c
index dd29f46d086b..dd29f46d086b 100644
--- a/drivers/net/wireless/ipw2x00/libipw_wx.c
+++ b/drivers/net/wireless/intel/ipw2x00/libipw_wx.c
diff --git a/drivers/net/wireless/iwlegacy/3945-debug.c b/drivers/net/wireless/intel/iwlegacy/3945-debug.c
index c1b4441fb8b2..c1b4441fb8b2 100644
--- a/drivers/net/wireless/iwlegacy/3945-debug.c
+++ b/drivers/net/wireless/intel/iwlegacy/3945-debug.c
diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c
index af1b3e6839fa..af1b3e6839fa 100644
--- a/drivers/net/wireless/iwlegacy/3945-mac.c
+++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c
diff --git a/drivers/net/wireless/iwlegacy/3945-rs.c b/drivers/net/wireless/intel/iwlegacy/3945-rs.c
index 76b0729ade17..76b0729ade17 100644
--- a/drivers/net/wireless/iwlegacy/3945-rs.c
+++ b/drivers/net/wireless/intel/iwlegacy/3945-rs.c
diff --git a/drivers/net/wireless/iwlegacy/3945.c b/drivers/net/wireless/intel/iwlegacy/3945.c
index 93bdf684babe..93bdf684babe 100644
--- a/drivers/net/wireless/iwlegacy/3945.c
+++ b/drivers/net/wireless/intel/iwlegacy/3945.c
diff --git a/drivers/net/wireless/iwlegacy/3945.h b/drivers/net/wireless/intel/iwlegacy/3945.h
index 00030d43a194..00030d43a194 100644
--- a/drivers/net/wireless/iwlegacy/3945.h
+++ b/drivers/net/wireless/intel/iwlegacy/3945.h
diff --git a/drivers/net/wireless/iwlegacy/4965-calib.c b/drivers/net/wireless/intel/iwlegacy/4965-calib.c
index e78bdefb8952..e78bdefb8952 100644
--- a/drivers/net/wireless/iwlegacy/4965-calib.c
+++ b/drivers/net/wireless/intel/iwlegacy/4965-calib.c
diff --git a/drivers/net/wireless/iwlegacy/4965-debug.c b/drivers/net/wireless/intel/iwlegacy/4965-debug.c
index e0597bfdddb8..e0597bfdddb8 100644
--- a/drivers/net/wireless/iwlegacy/4965-debug.c
+++ b/drivers/net/wireless/intel/iwlegacy/4965-debug.c
diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c
index 6656215a13a9..fd38aa0763e4 100644
--- a/drivers/net/wireless/iwlegacy/4965-mac.c
+++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c
@@ -6416,7 +6416,7 @@ il4965_hw_detect(struct il_priv *il)
D_INFO("HW Revision ID = 0x%X\n", il->rev_id);
}
-static struct il_sensitivity_ranges il4965_sensitivity = {
+static const struct il_sensitivity_ranges il4965_sensitivity = {
.min_nrg_cck = 97,
.max_nrg_cck = 0, /* not used, set to 0 */
diff --git a/drivers/net/wireless/iwlegacy/4965-rs.c b/drivers/net/wireless/intel/iwlegacy/4965-rs.c
index bac60b2bc3f0..bac60b2bc3f0 100644
--- a/drivers/net/wireless/iwlegacy/4965-rs.c
+++ b/drivers/net/wireless/intel/iwlegacy/4965-rs.c
diff --git a/drivers/net/wireless/iwlegacy/4965.c b/drivers/net/wireless/intel/iwlegacy/4965.c
index fe47db9c20cd..fe47db9c20cd 100644
--- a/drivers/net/wireless/iwlegacy/4965.c
+++ b/drivers/net/wireless/intel/iwlegacy/4965.c
diff --git a/drivers/net/wireless/iwlegacy/4965.h b/drivers/net/wireless/intel/iwlegacy/4965.h
index 8ab8706f9422..8ab8706f9422 100644
--- a/drivers/net/wireless/iwlegacy/4965.h
+++ b/drivers/net/wireless/intel/iwlegacy/4965.h
diff --git a/drivers/net/wireless/iwlegacy/Kconfig b/drivers/net/wireless/intel/iwlegacy/Kconfig
index fb919727b8bb..fb919727b8bb 100644
--- a/drivers/net/wireless/iwlegacy/Kconfig
+++ b/drivers/net/wireless/intel/iwlegacy/Kconfig
diff --git a/drivers/net/wireless/iwlegacy/Makefile b/drivers/net/wireless/intel/iwlegacy/Makefile
index c985a01a0731..c985a01a0731 100644
--- a/drivers/net/wireless/iwlegacy/Makefile
+++ b/drivers/net/wireless/intel/iwlegacy/Makefile
diff --git a/drivers/net/wireless/iwlegacy/commands.h b/drivers/net/wireless/intel/iwlegacy/commands.h
index dd744135c956..dd744135c956 100644
--- a/drivers/net/wireless/iwlegacy/commands.h
+++ b/drivers/net/wireless/intel/iwlegacy/commands.h
diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/intel/iwlegacy/common.c
index 887114582583..eb5cb603bc52 100644
--- a/drivers/net/wireless/iwlegacy/common.c
+++ b/drivers/net/wireless/intel/iwlegacy/common.c
@@ -1865,14 +1865,14 @@ il_send_add_sta(struct il_priv *il, struct il_addsta_cmd *sta, u8 flags)
cmd.len = il->ops->build_addsta_hcmd(sta, data);
ret = il_send_cmd(il, &cmd);
-
- if (ret || (flags & CMD_ASYNC))
+ if (ret)
return ret;
+ if (flags & CMD_ASYNC)
+ return 0;
+
+ pkt = (struct il_rx_pkt *)cmd.reply_page;
+ ret = il_process_add_sta_resp(il, sta, pkt, true);
- if (ret == 0) {
- pkt = (struct il_rx_pkt *)cmd.reply_page;
- ret = il_process_add_sta_resp(il, sta, pkt, true);
- }
il_free_pages(il, cmd.reply_page);
return ret;
@@ -3602,7 +3602,7 @@ il_is_ht40_tx_allowed(struct il_priv *il, struct ieee80211_sta_ht_cap *ht_cap)
}
EXPORT_SYMBOL(il_is_ht40_tx_allowed);
-static u16
+static u16 noinline
il_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val)
{
u16 new_val;
diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/intel/iwlegacy/common.h
index ce52cf114fde..ce52cf114fde 100644
--- a/drivers/net/wireless/iwlegacy/common.h
+++ b/drivers/net/wireless/intel/iwlegacy/common.h
diff --git a/drivers/net/wireless/iwlegacy/csr.h b/drivers/net/wireless/intel/iwlegacy/csr.h
index 9138e15004fa..9138e15004fa 100644
--- a/drivers/net/wireless/iwlegacy/csr.h
+++ b/drivers/net/wireless/intel/iwlegacy/csr.h
diff --git a/drivers/net/wireless/iwlegacy/debug.c b/drivers/net/wireless/intel/iwlegacy/debug.c
index 908b9f4fef6f..908b9f4fef6f 100644
--- a/drivers/net/wireless/iwlegacy/debug.c
+++ b/drivers/net/wireless/intel/iwlegacy/debug.c
diff --git a/drivers/net/wireless/iwlegacy/iwl-spectrum.h b/drivers/net/wireless/intel/iwlegacy/iwl-spectrum.h
index 85fe48e520f9..85fe48e520f9 100644
--- a/drivers/net/wireless/iwlegacy/iwl-spectrum.h
+++ b/drivers/net/wireless/intel/iwlegacy/iwl-spectrum.h
diff --git a/drivers/net/wireless/iwlegacy/prph.h b/drivers/net/wireless/intel/iwlegacy/prph.h
index ffec4b4a248a..ffec4b4a248a 100644
--- a/drivers/net/wireless/iwlegacy/prph.h
+++ b/drivers/net/wireless/intel/iwlegacy/prph.h
diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/intel/iwlwifi/Kconfig
index 6e949df399d6..866067789330 100644
--- a/drivers/net/wireless/iwlwifi/Kconfig
+++ b/drivers/net/wireless/intel/iwlwifi/Kconfig
@@ -53,7 +53,7 @@ config IWLWIFI_LEDS
config IWLDVM
tristate "Intel Wireless WiFi DVM Firmware support"
- default IWLWIFI
+ depends on m
help
This is the driver that supports the DVM firmware. The list
of the devices that use this firmware is available here:
diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile
index dbfc5b18bcb7..05828c61d1ab 100644
--- a/drivers/net/wireless/iwlwifi/Makefile
+++ b/drivers/net/wireless/intel/iwlwifi/Makefile
@@ -8,7 +8,7 @@ iwlwifi-objs += iwl-eeprom-read.o iwl-eeprom-parse.o
iwlwifi-objs += iwl-phy-db.o iwl-nvm-parse.o
iwlwifi-objs += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o
iwlwifi-$(CONFIG_IWLDVM) += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o
-iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o iwl-8000.o
+iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o iwl-8000.o iwl-9000.o
iwlwifi-objs += iwl-trans.o
iwlwifi-objs += $(iwlwifi-m)
diff --git a/drivers/net/wireless/iwlwifi/dvm/Makefile b/drivers/net/wireless/intel/iwlwifi/dvm/Makefile
index 4d19685f31c3..4d19685f31c3 100644
--- a/drivers/net/wireless/iwlwifi/dvm/Makefile
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/Makefile
diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/intel/iwlwifi/dvm/agn.h
index 991def878881..9de277c6c420 100644
--- a/drivers/net/wireless/iwlwifi/dvm/agn.h
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/agn.h
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -473,13 +473,4 @@ do { \
} while (0)
#endif /* CONFIG_IWLWIFI_DEBUG */
-extern const char *const iwl_dvm_cmd_strings[REPLY_MAX + 1];
-
-static inline const char *iwl_dvm_get_cmd_string(u8 cmd)
-{
- const char *s = iwl_dvm_cmd_strings[cmd];
- if (s)
- return s;
- return "UNKNOWN";
-}
#endif /* __iwl_agn_h__ */
diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.c b/drivers/net/wireless/intel/iwlwifi/dvm/calib.c
index 20e6aa910700..07a4c644fb9b 100644
--- a/drivers/net/wireless/iwlwifi/dvm/calib.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/calib.c
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -311,7 +311,7 @@ static int iwl_sens_energy_cck(struct iwl_priv *priv,
/* If previous beacon had too many false alarms,
* give it some extra margin by reducing sensitivity again
* (but don't go below measured energy of desired Rx) */
- if (IWL_FA_TOO_MANY == data->nrg_prev_state) {
+ if (data->nrg_prev_state == IWL_FA_TOO_MANY) {
IWL_DEBUG_CALIB(priv, "... increasing margin\n");
if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN))
data->nrg_th_cck -= NRG_MARGIN;
diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.h b/drivers/net/wireless/intel/iwlwifi/dvm/calib.h
index aeae4e80ea40..099e3ce80ffc 100644
--- a/drivers/net/wireless/iwlwifi/dvm/calib.h
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/calib.h
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/intel/iwlwifi/dvm/commands.h
index 7a34e4d158d1..2ab2773655a8 100644
--- a/drivers/net/wireless/iwlwifi/dvm/commands.h
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/commands.h
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c
index b15e44f8d1bd..74c51615244e 100644
--- a/drivers/net/wireless/iwlwifi/dvm/debugfs.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c
@@ -22,7 +22,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*****************************************************************************/
@@ -32,7 +32,9 @@
#include <linux/debugfs.h>
#include <linux/ieee80211.h>
#include <net/mac80211.h>
+
#include "iwl-debug.h"
+#include "iwl-trans.h"
#include "iwl-io.h"
#include "dev.h"
#include "agn.h"
@@ -438,7 +440,7 @@ static ssize_t iwl_dbgfs_rx_handlers_read(struct file *file,
if (priv->rx_handlers_stats[cnt] > 0)
pos += scnprintf(buf + pos, bufsz - pos,
"\tRx handler[%36s]:\t\t %u\n",
- iwl_dvm_get_cmd_string(cnt),
+ iwl_get_cmd_string(priv->trans, (u32)cnt),
priv->rx_handlers_stats[cnt]);
}
diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/intel/iwlwifi/dvm/dev.h
index 0ba3e56d6015..1a7ead753eee 100644
--- a/drivers/net/wireless/iwlwifi/dvm/dev.h
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/dev.h
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/intel/iwlwifi/dvm/devices.c
index 34b41e5f7cfc..cc13c04063a5 100644
--- a/drivers/net/wireless/iwlwifi/dvm/devices.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/devices.c
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/dvm/led.c b/drivers/net/wireless/intel/iwlwifi/dvm/led.c
index ca4d6692cc4e..1aabb5ec096f 100644
--- a/drivers/net/wireless/iwlwifi/dvm/led.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/led.c
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -134,8 +134,6 @@ static int iwl_led_cmd(struct iwl_priv *priv,
on = IWL_LED_SOLID;
}
- IWL_DEBUG_LED(priv, "Led blink time compensation=%u\n",
- priv->cfg->base_params->led_compensation);
led_cmd.on = iwl_blink_compensation(priv, on,
priv->cfg->base_params->led_compensation);
led_cmd.off = iwl_blink_compensation(priv, off,
diff --git a/drivers/net/wireless/iwlwifi/dvm/led.h b/drivers/net/wireless/intel/iwlwifi/dvm/led.h
index 1c6b2252d0f2..75f74edd018f 100644
--- a/drivers/net/wireless/iwlwifi/dvm/led.h
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/led.h
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c
index e18629a16fb0..4841be2aa499 100644
--- a/drivers/net/wireless/iwlwifi/dvm/lib.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c
@@ -22,7 +22,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -1154,6 +1154,9 @@ int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan)
priv->ucode_loaded = false;
iwl_trans_stop_device(priv->trans);
+ ret = iwl_trans_start_hw(priv->trans);
+ if (ret)
+ goto out;
priv->wowlan = true;
@@ -1262,7 +1265,7 @@ int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
if (test_bit(STATUS_FW_ERROR, &priv->status)) {
IWL_ERR(priv, "Command %s failed: FW Error\n",
- iwl_dvm_get_cmd_string(cmd->id));
+ iwl_get_cmd_string(priv->trans, cmd->id));
return -EIO;
}
diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c
index b3ad34e8bf5a..29ea1c6705b4 100644
--- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c
@@ -22,7 +22,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -115,6 +115,9 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
ieee80211_hw_set(hw, WANT_MONITOR_VIF);
+ if (priv->trans->max_skb_frags)
+ hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG;
+
hw->offchannel_tx_hw_queue = IWL_AUX_QUEUE;
hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FMT;
@@ -1411,13 +1414,7 @@ static void iwlagn_mac_remove_interface(struct ieee80211_hw *hw,
mutex_lock(&priv->mutex);
- if (WARN_ON(ctx->vif != vif)) {
- struct iwl_rxon_context *tmp;
- IWL_ERR(priv, "ctx->vif = %p, vif = %p\n", ctx->vif, vif);
- for_each_context(priv, tmp)
- IWL_ERR(priv, "\tID = %d:\tctx = %p\tctx->vif = %p\n",
- tmp->ctxid, tmp, tmp->vif);
- }
+ WARN_ON(ctx->vif != vif);
ctx->vif = NULL;
iwl_teardown_interface(priv, vif, false);
diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c
index e7616f0ee6e8..f62c2d727ddb 100644
--- a/drivers/net/wireless/iwlwifi/dvm/main.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c
@@ -1,6 +1,7 @@
/******************************************************************************
*
* Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Deutschland GmbH
*
* Portions of this file are derived from the ipw3945 project, as well
* as portions of the ieee80211 subsystem header files.
@@ -22,7 +23,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -69,6 +70,93 @@ MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
MODULE_LICENSE("GPL");
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search.
+ * A warning will be triggered on violation.
+ */
+static const struct iwl_hcmd_names iwl_dvm_cmd_names[] = {
+ HCMD_NAME(REPLY_ALIVE),
+ HCMD_NAME(REPLY_ERROR),
+ HCMD_NAME(REPLY_ECHO),
+ HCMD_NAME(REPLY_RXON),
+ HCMD_NAME(REPLY_RXON_ASSOC),
+ HCMD_NAME(REPLY_QOS_PARAM),
+ HCMD_NAME(REPLY_RXON_TIMING),
+ HCMD_NAME(REPLY_ADD_STA),
+ HCMD_NAME(REPLY_REMOVE_STA),
+ HCMD_NAME(REPLY_REMOVE_ALL_STA),
+ HCMD_NAME(REPLY_TX),
+ HCMD_NAME(REPLY_TXFIFO_FLUSH),
+ HCMD_NAME(REPLY_WEPKEY),
+ HCMD_NAME(REPLY_LEDS_CMD),
+ HCMD_NAME(REPLY_TX_LINK_QUALITY_CMD),
+ HCMD_NAME(COEX_PRIORITY_TABLE_CMD),
+ HCMD_NAME(COEX_MEDIUM_NOTIFICATION),
+ HCMD_NAME(COEX_EVENT_CMD),
+ HCMD_NAME(TEMPERATURE_NOTIFICATION),
+ HCMD_NAME(CALIBRATION_CFG_CMD),
+ HCMD_NAME(CALIBRATION_RES_NOTIFICATION),
+ HCMD_NAME(CALIBRATION_COMPLETE_NOTIFICATION),
+ HCMD_NAME(REPLY_QUIET_CMD),
+ HCMD_NAME(REPLY_CHANNEL_SWITCH),
+ HCMD_NAME(CHANNEL_SWITCH_NOTIFICATION),
+ HCMD_NAME(REPLY_SPECTRUM_MEASUREMENT_CMD),
+ HCMD_NAME(SPECTRUM_MEASURE_NOTIFICATION),
+ HCMD_NAME(POWER_TABLE_CMD),
+ HCMD_NAME(PM_SLEEP_NOTIFICATION),
+ HCMD_NAME(PM_DEBUG_STATISTIC_NOTIFIC),
+ HCMD_NAME(REPLY_SCAN_CMD),
+ HCMD_NAME(REPLY_SCAN_ABORT_CMD),
+ HCMD_NAME(SCAN_START_NOTIFICATION),
+ HCMD_NAME(SCAN_RESULTS_NOTIFICATION),
+ HCMD_NAME(SCAN_COMPLETE_NOTIFICATION),
+ HCMD_NAME(BEACON_NOTIFICATION),
+ HCMD_NAME(REPLY_TX_BEACON),
+ HCMD_NAME(WHO_IS_AWAKE_NOTIFICATION),
+ HCMD_NAME(REPLY_TX_POWER_DBM_CMD),
+ HCMD_NAME(QUIET_NOTIFICATION),
+ HCMD_NAME(REPLY_TX_PWR_TABLE_CMD),
+ HCMD_NAME(REPLY_TX_POWER_DBM_CMD_V1),
+ HCMD_NAME(TX_ANT_CONFIGURATION_CMD),
+ HCMD_NAME(MEASURE_ABORT_NOTIFICATION),
+ HCMD_NAME(REPLY_BT_CONFIG),
+ HCMD_NAME(REPLY_STATISTICS_CMD),
+ HCMD_NAME(STATISTICS_NOTIFICATION),
+ HCMD_NAME(REPLY_CARD_STATE_CMD),
+ HCMD_NAME(CARD_STATE_NOTIFICATION),
+ HCMD_NAME(MISSED_BEACONS_NOTIFICATION),
+ HCMD_NAME(REPLY_CT_KILL_CONFIG_CMD),
+ HCMD_NAME(SENSITIVITY_CMD),
+ HCMD_NAME(REPLY_PHY_CALIBRATION_CMD),
+ HCMD_NAME(REPLY_WIPAN_PARAMS),
+ HCMD_NAME(REPLY_WIPAN_RXON),
+ HCMD_NAME(REPLY_WIPAN_RXON_TIMING),
+ HCMD_NAME(REPLY_WIPAN_RXON_ASSOC),
+ HCMD_NAME(REPLY_WIPAN_QOS_PARAM),
+ HCMD_NAME(REPLY_WIPAN_WEPKEY),
+ HCMD_NAME(REPLY_WIPAN_P2P_CHANNEL_SWITCH),
+ HCMD_NAME(REPLY_WIPAN_NOA_NOTIFICATION),
+ HCMD_NAME(REPLY_WIPAN_DEACTIVATION_COMPLETE),
+ HCMD_NAME(REPLY_RX_PHY_CMD),
+ HCMD_NAME(REPLY_RX_MPDU_CMD),
+ HCMD_NAME(REPLY_RX),
+ HCMD_NAME(REPLY_COMPRESSED_BA),
+ HCMD_NAME(REPLY_BT_COEX_PRIO_TABLE),
+ HCMD_NAME(REPLY_BT_COEX_PROT_ENV),
+ HCMD_NAME(REPLY_BT_COEX_PROFILE_NOTIF),
+ HCMD_NAME(REPLY_D3_CONFIG),
+ HCMD_NAME(REPLY_WOWLAN_PATTERNS),
+ HCMD_NAME(REPLY_WOWLAN_WAKEUP_FILTER),
+ HCMD_NAME(REPLY_WOWLAN_TSC_RSC_PARAMS),
+ HCMD_NAME(REPLY_WOWLAN_TKIP_PARAMS),
+ HCMD_NAME(REPLY_WOWLAN_KEK_KCK_MATERIAL),
+ HCMD_NAME(REPLY_WOWLAN_GET_STATUS),
+};
+
+static const struct iwl_hcmd_arr iwl_dvm_groups[] = {
+ [0x0] = HCMD_ARR(iwl_dvm_cmd_names),
+};
+
static const struct iwl_op_mode_ops iwl_dvm_ops;
void iwl_update_chain_flags(struct iwl_priv *priv)
@@ -341,7 +429,7 @@ static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base,
ptr = base + (4 * sizeof(u32)) + (start_idx * 3 * sizeof(u32));
/* Make sure device is powered up for SRAM reads */
- if (!iwl_trans_grab_nic_access(priv->trans, false, &reg_flags))
+ if (!iwl_trans_grab_nic_access(priv->trans, &reg_flags))
return;
/* Set starting address; reads will auto-increment */
@@ -1227,10 +1315,26 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
trans_cfg.op_mode = op_mode;
trans_cfg.no_reclaim_cmds = no_reclaim_cmds;
trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
- trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K;
+
+ switch (iwlwifi_mod_params.amsdu_size) {
+ case IWL_AMSDU_4K:
+ trans_cfg.rx_buf_size = IWL_AMSDU_4K;
+ break;
+ case IWL_AMSDU_8K:
+ trans_cfg.rx_buf_size = IWL_AMSDU_8K;
+ break;
+ case IWL_AMSDU_12K:
+ default:
+ trans_cfg.rx_buf_size = IWL_AMSDU_4K;
+ pr_err("Unsupported amsdu_size: %d\n",
+ iwlwifi_mod_params.amsdu_size);
+ }
+
trans_cfg.cmd_q_wdg_timeout = IWL_WATCHDOG_DISABLED;
- trans_cfg.command_names = iwl_dvm_cmd_strings;
+ trans_cfg.command_groups = iwl_dvm_groups;
+ trans_cfg.command_groups_size = ARRAY_SIZE(iwl_dvm_groups);
+
trans_cfg.cmd_fifo = IWLAGN_CMD_FIFO_NUM;
WARN_ON(sizeof(priv->transport_queue_stop) * BITS_PER_BYTE <
@@ -1251,6 +1355,8 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD;
trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start);
+ trans->command_groups = trans_cfg.command_groups;
+ trans->command_groups_size = trans_cfg.command_groups_size;
/* At this point both hw and priv are allocated. */
@@ -1625,7 +1731,7 @@ static int iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
ptr = base + EVENT_START_OFFSET + (start_idx * event_size);
/* Make sure device is powered up for SRAM reads */
- if (!iwl_trans_grab_nic_access(trans, false, &reg_flags))
+ if (!iwl_trans_grab_nic_access(trans, &reg_flags))
return pos;
/* Set starting address; reads will auto-increment */
diff --git a/drivers/net/wireless/iwlwifi/dvm/power.c b/drivers/net/wireless/intel/iwlwifi/dvm/power.c
index 1513dbc79c14..0ad557c89514 100644
--- a/drivers/net/wireless/iwlwifi/dvm/power.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/power.c
@@ -22,7 +22,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/dvm/power.h b/drivers/net/wireless/intel/iwlwifi/dvm/power.h
index 570d3a5e4670..2fd9b43adafd 100644
--- a/drivers/net/wireless/iwlwifi/dvm/power.h
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/power.h
@@ -22,7 +22,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*****************************************************************************/
#ifndef __iwl_power_setting_h__
diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/intel/iwlwifi/dvm/rs.c
index cef921c1a623..ee7505537c96 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rs.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/rs.c
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.h b/drivers/net/wireless/intel/iwlwifi/dvm/rs.h
index f6bd25cad203..c5fe44584613 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rs.h
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/rs.h
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c
index 4a45b0b594c7..52ab1e012e8f 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c
@@ -1,6 +1,7 @@
/******************************************************************************
*
* Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Deutschland GmbH
*
* Portions of this file are derived from the ipw3945 project, as well
* as portionhelp of the ieee80211 subsystem header files.
@@ -22,7 +23,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -32,91 +33,13 @@
#include <linux/sched.h>
#include <net/mac80211.h>
#include <asm/unaligned.h>
+
+#include "iwl-trans.h"
#include "iwl-io.h"
#include "dev.h"
#include "calib.h"
#include "agn.h"
-#define IWL_CMD_ENTRY(x) [x] = #x
-
-const char *const iwl_dvm_cmd_strings[REPLY_MAX + 1] = {
- IWL_CMD_ENTRY(REPLY_ALIVE),
- IWL_CMD_ENTRY(REPLY_ERROR),
- IWL_CMD_ENTRY(REPLY_ECHO),
- IWL_CMD_ENTRY(REPLY_RXON),
- IWL_CMD_ENTRY(REPLY_RXON_ASSOC),
- IWL_CMD_ENTRY(REPLY_QOS_PARAM),
- IWL_CMD_ENTRY(REPLY_RXON_TIMING),
- IWL_CMD_ENTRY(REPLY_ADD_STA),
- IWL_CMD_ENTRY(REPLY_REMOVE_STA),
- IWL_CMD_ENTRY(REPLY_REMOVE_ALL_STA),
- IWL_CMD_ENTRY(REPLY_TXFIFO_FLUSH),
- IWL_CMD_ENTRY(REPLY_WEPKEY),
- IWL_CMD_ENTRY(REPLY_TX),
- IWL_CMD_ENTRY(REPLY_LEDS_CMD),
- IWL_CMD_ENTRY(REPLY_TX_LINK_QUALITY_CMD),
- IWL_CMD_ENTRY(COEX_PRIORITY_TABLE_CMD),
- IWL_CMD_ENTRY(COEX_MEDIUM_NOTIFICATION),
- IWL_CMD_ENTRY(COEX_EVENT_CMD),
- IWL_CMD_ENTRY(REPLY_QUIET_CMD),
- IWL_CMD_ENTRY(REPLY_CHANNEL_SWITCH),
- IWL_CMD_ENTRY(CHANNEL_SWITCH_NOTIFICATION),
- IWL_CMD_ENTRY(REPLY_SPECTRUM_MEASUREMENT_CMD),
- IWL_CMD_ENTRY(SPECTRUM_MEASURE_NOTIFICATION),
- IWL_CMD_ENTRY(POWER_TABLE_CMD),
- IWL_CMD_ENTRY(PM_SLEEP_NOTIFICATION),
- IWL_CMD_ENTRY(PM_DEBUG_STATISTIC_NOTIFIC),
- IWL_CMD_ENTRY(REPLY_SCAN_CMD),
- IWL_CMD_ENTRY(REPLY_SCAN_ABORT_CMD),
- IWL_CMD_ENTRY(SCAN_START_NOTIFICATION),
- IWL_CMD_ENTRY(SCAN_RESULTS_NOTIFICATION),
- IWL_CMD_ENTRY(SCAN_COMPLETE_NOTIFICATION),
- IWL_CMD_ENTRY(BEACON_NOTIFICATION),
- IWL_CMD_ENTRY(REPLY_TX_BEACON),
- IWL_CMD_ENTRY(WHO_IS_AWAKE_NOTIFICATION),
- IWL_CMD_ENTRY(QUIET_NOTIFICATION),
- IWL_CMD_ENTRY(REPLY_TX_PWR_TABLE_CMD),
- IWL_CMD_ENTRY(MEASURE_ABORT_NOTIFICATION),
- IWL_CMD_ENTRY(REPLY_BT_CONFIG),
- IWL_CMD_ENTRY(REPLY_STATISTICS_CMD),
- IWL_CMD_ENTRY(STATISTICS_NOTIFICATION),
- IWL_CMD_ENTRY(REPLY_CARD_STATE_CMD),
- IWL_CMD_ENTRY(CARD_STATE_NOTIFICATION),
- IWL_CMD_ENTRY(MISSED_BEACONS_NOTIFICATION),
- IWL_CMD_ENTRY(REPLY_CT_KILL_CONFIG_CMD),
- IWL_CMD_ENTRY(SENSITIVITY_CMD),
- IWL_CMD_ENTRY(REPLY_PHY_CALIBRATION_CMD),
- IWL_CMD_ENTRY(REPLY_RX_PHY_CMD),
- IWL_CMD_ENTRY(REPLY_RX_MPDU_CMD),
- IWL_CMD_ENTRY(REPLY_COMPRESSED_BA),
- IWL_CMD_ENTRY(CALIBRATION_CFG_CMD),
- IWL_CMD_ENTRY(CALIBRATION_RES_NOTIFICATION),
- IWL_CMD_ENTRY(CALIBRATION_COMPLETE_NOTIFICATION),
- IWL_CMD_ENTRY(REPLY_TX_POWER_DBM_CMD),
- IWL_CMD_ENTRY(TEMPERATURE_NOTIFICATION),
- IWL_CMD_ENTRY(TX_ANT_CONFIGURATION_CMD),
- IWL_CMD_ENTRY(REPLY_BT_COEX_PROFILE_NOTIF),
- IWL_CMD_ENTRY(REPLY_BT_COEX_PRIO_TABLE),
- IWL_CMD_ENTRY(REPLY_BT_COEX_PROT_ENV),
- IWL_CMD_ENTRY(REPLY_WIPAN_PARAMS),
- IWL_CMD_ENTRY(REPLY_WIPAN_RXON),
- IWL_CMD_ENTRY(REPLY_WIPAN_RXON_TIMING),
- IWL_CMD_ENTRY(REPLY_WIPAN_RXON_ASSOC),
- IWL_CMD_ENTRY(REPLY_WIPAN_QOS_PARAM),
- IWL_CMD_ENTRY(REPLY_WIPAN_WEPKEY),
- IWL_CMD_ENTRY(REPLY_WIPAN_P2P_CHANNEL_SWITCH),
- IWL_CMD_ENTRY(REPLY_WIPAN_NOA_NOTIFICATION),
- IWL_CMD_ENTRY(REPLY_WIPAN_DEACTIVATION_COMPLETE),
- IWL_CMD_ENTRY(REPLY_WOWLAN_PATTERNS),
- IWL_CMD_ENTRY(REPLY_WOWLAN_WAKEUP_FILTER),
- IWL_CMD_ENTRY(REPLY_WOWLAN_TSC_RSC_PARAMS),
- IWL_CMD_ENTRY(REPLY_WOWLAN_TKIP_PARAMS),
- IWL_CMD_ENTRY(REPLY_WOWLAN_KEK_KCK_MATERIAL),
- IWL_CMD_ENTRY(REPLY_WOWLAN_GET_STATUS),
- IWL_CMD_ENTRY(REPLY_D3_CONFIG),
-};
-#undef IWL_CMD_ENTRY
-
/******************************************************************************
*
* Generic RX handler implementations
@@ -1095,7 +1018,9 @@ void iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct napi_struct *napi,
} else {
/* No handling needed */
IWL_DEBUG_RX(priv, "No handler needed for %s, 0x%02x\n",
- iwl_dvm_get_cmd_string(pkt->hdr.cmd),
+ iwl_get_cmd_string(priv->trans,
+ iwl_cmd_id(pkt->hdr.cmd,
+ 0, 0)),
pkt->hdr.cmd);
}
}
diff --git a/drivers/net/wireless/iwlwifi/dvm/rxon.c b/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c
index 85ceceb34fcc..2d47cb24c48b 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rxon.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c
@@ -20,7 +20,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/dvm/scan.c b/drivers/net/wireless/intel/iwlwifi/dvm/scan.c
index 648159495bbc..81a2ddbe9569 100644
--- a/drivers/net/wireless/iwlwifi/dvm/scan.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/scan.c
@@ -22,7 +22,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*****************************************************************************/
#include <linux/slab.h>
diff --git a/drivers/net/wireless/iwlwifi/dvm/sta.c b/drivers/net/wireless/intel/iwlwifi/dvm/sta.c
index 0fa67d3b7235..8e9768a553e4 100644
--- a/drivers/net/wireless/iwlwifi/dvm/sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/sta.c
@@ -22,7 +22,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.c b/drivers/net/wireless/intel/iwlwifi/dvm/tt.c
index c4736c8834c5..5b73492e7ff7 100644
--- a/drivers/net/wireless/iwlwifi/dvm/tt.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/tt.c
@@ -22,7 +22,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*****************************************************************************/
@@ -184,7 +184,7 @@ static void iwl_tt_check_exit_ct_kill(unsigned long data)
priv->thermal_throttle.ct_kill_toggle = true;
}
iwl_read32(priv->trans, CSR_UCODE_DRV_GP1);
- if (iwl_trans_grab_nic_access(priv->trans, false, &flags))
+ if (iwl_trans_grab_nic_access(priv->trans, &flags))
iwl_trans_release_nic_access(priv->trans, &flags);
/* Reschedule the ct_kill timer to occur in
diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.h b/drivers/net/wireless/intel/iwlwifi/dvm/tt.h
index 507726534b84..d324e9be9cbf 100644
--- a/drivers/net/wireless/iwlwifi/dvm/tt.h
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/tt.h
@@ -22,7 +22,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*****************************************************************************/
#ifndef __iwl_tt_setting_h__
diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c
index bddd19769035..59e2001c39f8 100644
--- a/drivers/net/wireless/iwlwifi/dvm/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c
@@ -22,7 +22,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -383,6 +383,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv,
iwlagn_tx_cmd_build_rate(priv, tx_cmd, info, sta, fc);
memset(&info->status, 0, sizeof(info->status));
+ memset(info->driver_data, 0, sizeof(info->driver_data));
info->driver_data[0] = ctx;
info->driver_data[1] = dev_cmd;
diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c
index 931a8e4269ef..b662cf35b033 100644
--- a/drivers/net/wireless/iwlwifi/dvm/ucode.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c
@@ -23,7 +23,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/intel/iwlwifi/iwl-1000.c
index 06f6cc08f451..a90dbab6bbbe 100644
--- a/drivers/net/wireless/iwlwifi/iwl-1000.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-1000.c
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/iwl-2000.c b/drivers/net/wireless/intel/iwlwifi/iwl-2000.c
index 890b95f497d6..a6da9594c4a5 100644
--- a/drivers/net/wireless/iwlwifi/iwl-2000.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-2000.c
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/intel/iwlwifi/iwl-5000.c
index 724194e23414..8b5afdef2d83 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-5000.c
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/intel/iwlwifi/iwl-6000.c
index 21b2630763dc..0b4ba781b631 100644
--- a/drivers/net/wireless/iwlwifi/iwl-6000.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-6000.c
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c
index 1a73c7a1da77..e60cf141ed79 100644
--- a/drivers/net/wireless/iwlwifi/iwl-7000.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c
@@ -7,6 +7,7 @@
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -26,13 +27,14 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -70,12 +72,18 @@
/* Highest firmware API version supported */
#define IWL7260_UCODE_API_MAX 17
+#define IWL7265_UCODE_API_MAX 17
+#define IWL7265D_UCODE_API_MAX 20
/* Oldest version we won't warn about */
#define IWL7260_UCODE_API_OK 13
+#define IWL7265_UCODE_API_OK 13
+#define IWL7265D_UCODE_API_OK 13
/* Lowest firmware API version supported */
#define IWL7260_UCODE_API_MIN 13
+#define IWL7265_UCODE_API_MIN 13
+#define IWL7265D_UCODE_API_MIN 13
/* NVM versions */
#define IWL7260_NVM_VERSION 0x0a1d
@@ -149,10 +157,7 @@ static const struct iwl_ht_params iwl7000_ht_params = {
.ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
};
-#define IWL_DEVICE_7000 \
- .ucode_api_max = IWL7260_UCODE_API_MAX, \
- .ucode_api_ok = IWL7260_UCODE_API_OK, \
- .ucode_api_min = IWL7260_UCODE_API_MIN, \
+#define IWL_DEVICE_7000_COMMON \
.device_family = IWL_DEVICE_FAMILY_7000, \
.max_inst_size = IWL60_RTC_INST_SIZE, \
.max_data_size = IWL60_RTC_DATA_SIZE, \
@@ -163,6 +168,24 @@ static const struct iwl_ht_params iwl7000_ht_params = {
.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \
.dccm_offset = IWL7000_DCCM_OFFSET
+#define IWL_DEVICE_7000 \
+ IWL_DEVICE_7000_COMMON, \
+ .ucode_api_max = IWL7260_UCODE_API_MAX, \
+ .ucode_api_ok = IWL7260_UCODE_API_OK, \
+ .ucode_api_min = IWL7260_UCODE_API_MIN
+
+#define IWL_DEVICE_7005 \
+ IWL_DEVICE_7000_COMMON, \
+ .ucode_api_max = IWL7265_UCODE_API_MAX, \
+ .ucode_api_ok = IWL7265_UCODE_API_OK, \
+ .ucode_api_min = IWL7265_UCODE_API_MIN
+
+#define IWL_DEVICE_7005D \
+ IWL_DEVICE_7000_COMMON, \
+ .ucode_api_max = IWL7265D_UCODE_API_MAX, \
+ .ucode_api_ok = IWL7265D_UCODE_API_OK, \
+ .ucode_api_min = IWL7265D_UCODE_API_MIN
+
const struct iwl_cfg iwl7260_2ac_cfg = {
.name = "Intel(R) Dual Band Wireless AC 7260",
.fw_name_pre = IWL7260_FW_PRE,
@@ -266,6 +289,17 @@ static const struct iwl_ht_params iwl7265_ht_params = {
const struct iwl_cfg iwl3165_2ac_cfg = {
.name = "Intel(R) Dual Band Wireless AC 3165",
.fw_name_pre = IWL7265D_FW_PRE,
+ IWL_DEVICE_7005D,
+ .ht_params = &iwl7000_ht_params,
+ .nvm_ver = IWL3165_NVM_VERSION,
+ .nvm_calib_ver = IWL3165_TX_POWER_VERSION,
+ .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+ .dccm_len = IWL7265_DCCM_LEN,
+};
+
+const struct iwl_cfg iwl3168_2ac_cfg = {
+ .name = "Intel(R) Dual Band Wireless AC 3168",
+ .fw_name_pre = IWL7265D_FW_PRE,
IWL_DEVICE_7000,
.ht_params = &iwl7000_ht_params,
.nvm_ver = IWL3165_NVM_VERSION,
@@ -277,7 +311,7 @@ const struct iwl_cfg iwl3165_2ac_cfg = {
const struct iwl_cfg iwl7265_2ac_cfg = {
.name = "Intel(R) Dual Band Wireless AC 7265",
.fw_name_pre = IWL7265_FW_PRE,
- IWL_DEVICE_7000,
+ IWL_DEVICE_7005,
.ht_params = &iwl7265_ht_params,
.nvm_ver = IWL7265_NVM_VERSION,
.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
@@ -288,7 +322,7 @@ const struct iwl_cfg iwl7265_2ac_cfg = {
const struct iwl_cfg iwl7265_2n_cfg = {
.name = "Intel(R) Dual Band Wireless N 7265",
.fw_name_pre = IWL7265_FW_PRE,
- IWL_DEVICE_7000,
+ IWL_DEVICE_7005,
.ht_params = &iwl7265_ht_params,
.nvm_ver = IWL7265_NVM_VERSION,
.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
@@ -299,7 +333,7 @@ const struct iwl_cfg iwl7265_2n_cfg = {
const struct iwl_cfg iwl7265_n_cfg = {
.name = "Intel(R) Wireless N 7265",
.fw_name_pre = IWL7265_FW_PRE,
- IWL_DEVICE_7000,
+ IWL_DEVICE_7005,
.ht_params = &iwl7265_ht_params,
.nvm_ver = IWL7265_NVM_VERSION,
.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
@@ -310,7 +344,7 @@ const struct iwl_cfg iwl7265_n_cfg = {
const struct iwl_cfg iwl7265d_2ac_cfg = {
.name = "Intel(R) Dual Band Wireless AC 7265",
.fw_name_pre = IWL7265D_FW_PRE,
- IWL_DEVICE_7000,
+ IWL_DEVICE_7005D,
.ht_params = &iwl7265_ht_params,
.nvm_ver = IWL7265D_NVM_VERSION,
.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
@@ -321,7 +355,7 @@ const struct iwl_cfg iwl7265d_2ac_cfg = {
const struct iwl_cfg iwl7265d_2n_cfg = {
.name = "Intel(R) Dual Band Wireless N 7265",
.fw_name_pre = IWL7265D_FW_PRE,
- IWL_DEVICE_7000,
+ IWL_DEVICE_7005D,
.ht_params = &iwl7265_ht_params,
.nvm_ver = IWL7265D_NVM_VERSION,
.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
@@ -332,7 +366,7 @@ const struct iwl_cfg iwl7265d_2n_cfg = {
const struct iwl_cfg iwl7265d_n_cfg = {
.name = "Intel(R) Wireless N 7265",
.fw_name_pre = IWL7265D_FW_PRE,
- IWL_DEVICE_7000,
+ IWL_DEVICE_7005D,
.ht_params = &iwl7265_ht_params,
.nvm_ver = IWL7265D_NVM_VERSION,
.nvm_calib_ver = IWL7265_TX_POWER_VERSION,
@@ -342,5 +376,5 @@ const struct iwl_cfg iwl7265d_n_cfg = {
MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
-MODULE_FIRMWARE(IWL7265_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
-MODULE_FIRMWARE(IWL7265D_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
+MODULE_FIRMWARE(IWL7265_MODULE_FIRMWARE(IWL7265_UCODE_API_OK));
+MODULE_FIRMWARE(IWL7265D_MODULE_FIRMWARE(IWL7265D_UCODE_API_OK));
diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c
index 0116e5a4c393..c84a0299d43e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-8000.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -69,7 +69,7 @@
#include "iwl-agn-hw.h"
/* Highest firmware API version supported */
-#define IWL8000_UCODE_API_MAX 17
+#define IWL8000_UCODE_API_MAX 20
/* Oldest version we won't warn about */
#define IWL8000_UCODE_API_OK 13
@@ -154,7 +154,6 @@ static const struct iwl_tt_params iwl8000_tt_params = {
.base_params = &iwl8000_base_params, \
.led_mode = IWL_LED_RF_STATE, \
.nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_8000, \
- .d0i3 = true, \
.features = NETIF_F_RXCSUM, \
.non_shared_ant = ANT_A, \
.dccm_offset = IWL8260_DCCM_OFFSET, \
@@ -187,6 +186,16 @@ const struct iwl_cfg iwl8260_2ac_cfg = {
.max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
};
+const struct iwl_cfg iwl8265_2ac_cfg = {
+ .name = "Intel(R) Dual Band Wireless AC 8265",
+ .fw_name_pre = IWL8000_FW_PRE,
+ IWL_DEVICE_8000,
+ .ht_params = &iwl8000_ht_params,
+ .nvm_ver = IWL8000_NVM_VERSION,
+ .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
+};
+
const struct iwl_cfg iwl4165_2ac_cfg = {
.name = "Intel(R) Dual Band Wireless AC 4165",
.fw_name_pre = IWL8000_FW_PRE,
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c
new file mode 100644
index 000000000000..ecbf4822cd69
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c
@@ -0,0 +1,163 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/stringify.h>
+#include "iwl-config.h"
+#include "iwl-agn-hw.h"
+
+/* Highest firmware API version supported */
+#define IWL9000_UCODE_API_MAX 20
+
+/* Oldest version we won't warn about */
+#define IWL9000_UCODE_API_OK 13
+
+/* Lowest firmware API version supported */
+#define IWL9000_UCODE_API_MIN 13
+
+/* NVM versions */
+#define IWL9000_NVM_VERSION 0x0a1d
+#define IWL9000_TX_POWER_VERSION 0xffff /* meaningless */
+
+/* Memory offsets and lengths */
+#define IWL9000_DCCM_OFFSET 0x800000
+#define IWL9000_DCCM_LEN 0x18000
+#define IWL9000_DCCM2_OFFSET 0x880000
+#define IWL9000_DCCM2_LEN 0x8000
+#define IWL9000_SMEM_OFFSET 0x400000
+#define IWL9000_SMEM_LEN 0x68000
+
+#define IWL9000_FW_PRE "iwlwifi-9000-"
+#define IWL9000_MODULE_FIRMWARE(api) \
+ IWL9000_FW_PRE "-" __stringify(api) ".ucode"
+
+#define NVM_HW_SECTION_NUM_FAMILY_9000 10
+
+static const struct iwl_base_params iwl9000_base_params = {
+ .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_9000,
+ .num_of_queues = 31,
+ .pll_cfg_val = 0,
+ .shadow_ram_support = true,
+ .led_compensation = 57,
+ .wd_timeout = IWL_LONG_WD_TIMEOUT,
+ .max_event_log_size = 512,
+ .shadow_reg_enable = true,
+ .pcie_l1_allowed = true,
+};
+
+static const struct iwl_ht_params iwl9000_ht_params = {
+ .stbc = true,
+ .ldpc = true,
+ .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
+};
+
+static const struct iwl_tt_params iwl9000_tt_params = {
+ .ct_kill_entry = 115,
+ .ct_kill_exit = 93,
+ .ct_kill_duration = 5,
+ .dynamic_smps_entry = 111,
+ .dynamic_smps_exit = 107,
+ .tx_protection_entry = 112,
+ .tx_protection_exit = 105,
+ .tx_backoff = {
+ {.temperature = 110, .backoff = 200},
+ {.temperature = 111, .backoff = 600},
+ {.temperature = 112, .backoff = 1200},
+ {.temperature = 113, .backoff = 2000},
+ {.temperature = 114, .backoff = 4000},
+ },
+ .support_ct_kill = true,
+ .support_dynamic_smps = true,
+ .support_tx_protection = true,
+ .support_tx_backoff = true,
+};
+
+#define IWL_DEVICE_9000 \
+ .ucode_api_max = IWL9000_UCODE_API_MAX, \
+ .ucode_api_ok = IWL9000_UCODE_API_OK, \
+ .ucode_api_min = IWL9000_UCODE_API_MIN, \
+ .device_family = IWL_DEVICE_FAMILY_8000, \
+ .max_inst_size = IWL60_RTC_INST_SIZE, \
+ .max_data_size = IWL60_RTC_DATA_SIZE, \
+ .base_params = &iwl9000_base_params, \
+ .led_mode = IWL_LED_RF_STATE, \
+ .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_9000, \
+ .non_shared_ant = ANT_A, \
+ .dccm_offset = IWL9000_DCCM_OFFSET, \
+ .dccm_len = IWL9000_DCCM_LEN, \
+ .dccm2_offset = IWL9000_DCCM2_OFFSET, \
+ .dccm2_len = IWL9000_DCCM2_LEN, \
+ .smem_offset = IWL9000_SMEM_OFFSET, \
+ .smem_len = IWL9000_SMEM_LEN, \
+ .thermal_params = &iwl9000_tt_params, \
+ .apmg_not_supported = true
+
+const struct iwl_cfg iwl9260_2ac_cfg = {
+ .name = "Intel(R) Dual Band Wireless AC 9260",
+ .fw_name_pre = IWL9000_FW_PRE,
+ IWL_DEVICE_9000,
+ .ht_params = &iwl9000_ht_params,
+ .nvm_ver = IWL9000_NVM_VERSION,
+ .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
+};
+
+const struct iwl_cfg iwl5165_2ac_cfg = {
+ .name = "Intel(R) Dual Band Wireless AC 5165",
+ .fw_name_pre = IWL9000_FW_PRE,
+ IWL_DEVICE_9000,
+ .ht_params = &iwl9000_ht_params,
+ .nvm_ver = IWL9000_NVM_VERSION,
+ .nvm_calib_ver = IWL9000_TX_POWER_VERSION,
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
+};
+
+MODULE_FIRMWARE(IWL9000_MODULE_FIRMWARE(IWL9000_UCODE_API_OK));
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-hw.h b/drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h
index 04a483d38659..ee9347a54cdc 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-hw.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-agn-hw.h
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
index 910970858f98..f99048135fb9 100644
--- a/drivers/net/wireless/iwlwifi/iwl-config.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -254,6 +254,7 @@ struct iwl_tt_params {
#define OTP_LOW_IMAGE_SIZE (2 * 512 * sizeof(u16)) /* 2 KB */
#define OTP_LOW_IMAGE_SIZE_FAMILY_7000 (16 * 512 * sizeof(u16)) /* 16 KB */
#define OTP_LOW_IMAGE_SIZE_FAMILY_8000 (32 * 512 * sizeof(u16)) /* 32 KB */
+#define OTP_LOW_IMAGE_SIZE_FAMILY_9000 OTP_LOW_IMAGE_SIZE_FAMILY_8000
struct iwl_eeprom_params {
const u8 regulatory_bands[7];
@@ -295,7 +296,6 @@ struct iwl_pwr_tx_backoff {
* @high_temp: Is this NIC is designated to be in high temperature.
* @host_interrupt_operation_mode: device needs host interrupt operation
* mode set
- * @d0i3: device uses d0i3 instead of d3
* @nvm_hw_section_num: the ID of the HW NVM section
* @features: hw features, any combination of feature_whitelist
* @pwr_tx_backoffs: translation table between power limits and backoffs
@@ -342,7 +342,6 @@ struct iwl_cfg {
const bool internal_wimax_coex;
const bool host_interrupt_operation_mode;
bool high_temp;
- bool d0i3;
u8 nvm_hw_section_num;
bool lp_xtal_workaround;
const struct iwl_pwr_tx_backoff *pwr_tx_backoffs;
@@ -421,6 +420,7 @@ extern const struct iwl_cfg iwl3160_2ac_cfg;
extern const struct iwl_cfg iwl3160_2n_cfg;
extern const struct iwl_cfg iwl3160_n_cfg;
extern const struct iwl_cfg iwl3165_2ac_cfg;
+extern const struct iwl_cfg iwl3168_2ac_cfg;
extern const struct iwl_cfg iwl7265_2ac_cfg;
extern const struct iwl_cfg iwl7265_2n_cfg;
extern const struct iwl_cfg iwl7265_n_cfg;
@@ -429,9 +429,12 @@ extern const struct iwl_cfg iwl7265d_2n_cfg;
extern const struct iwl_cfg iwl7265d_n_cfg;
extern const struct iwl_cfg iwl8260_2n_cfg;
extern const struct iwl_cfg iwl8260_2ac_cfg;
+extern const struct iwl_cfg iwl8265_2ac_cfg;
extern const struct iwl_cfg iwl4165_2ac_cfg;
extern const struct iwl_cfg iwl8260_2ac_sdio_cfg;
extern const struct iwl_cfg iwl4165_2ac_sdio_cfg;
+extern const struct iwl_cfg iwl9260_2ac_cfg;
+extern const struct iwl_cfg iwl5165_2ac_cfg;
#endif /* CONFIG_IWLMVM */
#endif /* __IWL_CONFIG_H__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h
index 543abeaffcf0..163b21bc20cb 100644
--- a/drivers/net/wireless/iwlwifi/iwl-csr.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.c b/drivers/net/wireless/intel/iwlwifi/iwl-debug.c
index 09feff4fa226..b1c3b0d0fcc6 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debug.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-debug.c
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/intel/iwlwifi/iwl-debug.h
index 9bb36d79c2bd..110333208450 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debug.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-debug.h
@@ -21,7 +21,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -163,7 +163,6 @@ do { \
#define IWL_DL_FW 0x00010000
#define IWL_DL_RF_KILL 0x00020000
#define IWL_DL_FW_ERRORS 0x00040000
-#define IWL_DL_LED 0x00080000
/* 0x00F00000 - 0x00100000 */
#define IWL_DL_RATE 0x00100000
#define IWL_DL_CALIB 0x00200000
@@ -189,7 +188,6 @@ do { \
#define IWL_DEBUG_RX(p, f, a...) IWL_DEBUG(p, IWL_DL_RX, f, ## a)
#define IWL_DEBUG_TX(p, f, a...) IWL_DEBUG(p, IWL_DL_TX, f, ## a)
#define IWL_DEBUG_ISR(p, f, a...) IWL_DEBUG(p, IWL_DL_ISR, f, ## a)
-#define IWL_DEBUG_LED(p, f, a...) IWL_DEBUG(p, IWL_DL_LED, f, ## a)
#define IWL_DEBUG_WEP(p, f, a...) IWL_DEBUG(p, IWL_DL_WEP, f, ## a)
#define IWL_DEBUG_HC(p, f, a...) IWL_DEBUG(p, IWL_DL_HCMD, f, ## a)
#define IWL_DEBUG_QUOTA(p, f, a...) IWL_DEBUG(p, IWL_DL_QUOTA, f, ## a)
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-data.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h
index 71a78cede9b0..d80312b46f16 100644
--- a/drivers/net/wireless/iwlwifi/iwl-devtrace-data.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-data.h
@@ -1,6 +1,7 @@
/******************************************************************************
*
* Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
@@ -19,7 +20,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -51,6 +52,22 @@ TRACE_EVENT(iwlwifi_dev_tx_data,
TP_printk("[%s] TX frame data", __get_str(dev))
);
+TRACE_EVENT(iwlwifi_dev_tx_tso_chunk,
+ TP_PROTO(const struct device *dev,
+ u8 *data_src, size_t data_len),
+ TP_ARGS(dev, data_src, data_len),
+ TP_STRUCT__entry(
+ DEV_ENTRY
+
+ __dynamic_array(u8, data, data_len)
+ ),
+ TP_fast_assign(
+ DEV_ASSIGN;
+ memcpy(__get_dynamic_array(data), data_src, data_len);
+ ),
+ TP_printk("[%s] TX frame data", __get_str(dev))
+);
+
TRACE_EVENT(iwlwifi_dev_rx_data,
TP_PROTO(const struct device *dev,
const struct iwl_trans *trans,
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-io.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h
index f62c54485852..27914eedc146 100644
--- a/drivers/net/wireless/iwlwifi/iwl-devtrace-io.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-io.h
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h
index eb4b99a1c8cd..22786d7dc00a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h
@@ -20,7 +20,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-msg.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h
index a3b3c2465f89..5dfc9295a7e0 100644
--- a/drivers/net/wireless/iwlwifi/iwl-devtrace-msg.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-msg.h
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-ucode.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-ucode.h
index 10839fae9cd9..e9b8673dd245 100644
--- a/drivers/net/wireless/iwlwifi/iwl-devtrace-ucode.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-ucode.h
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.c b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c
index 90987d6f348e..1d9dd153ef1c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-devtrace.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.c
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h
index b87acd6a229b..f4d3cd010087 100644
--- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h
@@ -19,7 +19,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
index 463cadfbfccb..7acb49075683 100644
--- a/drivers/net/wireless/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -451,7 +451,9 @@ static int iwl_set_ucode_api_flags(struct iwl_drv *drv, const u8 *data,
int i;
if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_API, 32)) {
- IWL_ERR(drv, "api_index larger than supported by driver\n");
+ IWL_ERR(drv,
+ "api flags index %d larger than supported by driver\n",
+ api_index);
/* don't return an error so we can load FW that has more bits */
return 0;
}
@@ -473,7 +475,9 @@ static int iwl_set_ucode_capabilities(struct iwl_drv *drv, const u8 *data,
int i;
if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_CAPA, 32)) {
- IWL_ERR(drv, "api_index larger than supported by driver\n");
+ IWL_ERR(drv,
+ "capa flags index %d larger than supported by driver\n",
+ api_index);
/* don't return an error so we can load FW that has more bits */
return 0;
}
@@ -590,7 +594,8 @@ static int iwl_parse_v1_v2_firmware(struct iwl_drv *drv,
static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
const struct firmware *ucode_raw,
struct iwl_firmware_pieces *pieces,
- struct iwl_ucode_capabilities *capa)
+ struct iwl_ucode_capabilities *capa,
+ bool *usniffer_images)
{
struct iwl_tlv_ucode_header *ucode = (void *)ucode_raw->data;
struct iwl_ucode_tlv *tlv;
@@ -603,7 +608,6 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
char buildstr[25];
u32 build, paging_mem_size;
int num_of_cpus;
- bool usniffer_images = false;
bool usniffer_req = false;
bool gscan_capa = false;
@@ -976,7 +980,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
break;
}
case IWL_UCODE_TLV_SEC_RT_USNIFFER:
- usniffer_images = true;
+ *usniffer_images = true;
iwl_store_ucode_sec(pieces, tlv_data,
IWL_UCODE_REGULAR_USNIFFER,
tlv_len);
@@ -1027,7 +1031,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
}
}
- if (usniffer_req && !usniffer_images) {
+ if (usniffer_req && !*usniffer_images) {
IWL_ERR(drv,
"user selected to work with usniffer but usniffer image isn't available in ucode package\n");
return -EINVAL;
@@ -1188,6 +1192,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
u32 api_ver;
int i;
bool load_module = false;
+ bool usniffer_images = false;
fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH;
fw->ucode_capa.standard_phy_calibration_size =
@@ -1225,7 +1230,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
err = iwl_parse_v1_v2_firmware(drv, ucode_raw, pieces);
else
err = iwl_parse_tlv_firmware(drv, ucode_raw, pieces,
- &fw->ucode_capa);
+ &fw->ucode_capa, &usniffer_images);
if (err)
goto try_again;
@@ -1323,6 +1328,8 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
sizeof(struct iwl_fw_dbg_trigger_time_event);
trigger_tlv_sz[FW_DBG_TRIGGER_BA] =
sizeof(struct iwl_fw_dbg_trigger_ba);
+ trigger_tlv_sz[FW_DBG_TRIGGER_TDLS] =
+ sizeof(struct iwl_fw_dbg_trigger_tdls);
for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++) {
if (pieces->dbg_trigger_tlv[i]) {
@@ -1539,6 +1546,7 @@ struct iwl_mod_params iwlwifi_mod_params = {
.bt_coex_active = true,
.power_level = IWL_POWER_INDEX_1,
.d0i3_disable = true,
+ .d0i3_entry_delay = 1000,
#ifndef CONFIG_IWLWIFI_UAPSD
.uapsd_disable = true,
#endif /* CONFIG_IWLWIFI_UAPSD */
@@ -1637,9 +1645,9 @@ MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])");
module_param_named(11n_disable, iwlwifi_mod_params.disable_11n, uint, S_IRUGO);
MODULE_PARM_DESC(11n_disable,
"disable 11n functionality, bitmap: 1: full, 2: disable agg TX, 4: disable agg RX, 8 enable agg TX");
-module_param_named(amsdu_size_8K, iwlwifi_mod_params.amsdu_size_8K,
+module_param_named(amsdu_size, iwlwifi_mod_params.amsdu_size,
int, S_IRUGO);
-MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size (default 0)");
+MODULE_PARM_DESC(amsdu_size, "amsdu size 0:4K 1:8K 2:12K (default 0)");
module_param_named(fw_restart, iwlwifi_mod_params.restart_fw, bool, S_IRUGO);
MODULE_PARM_DESC(fw_restart, "restart firmware in case of error (default true)");
@@ -1704,3 +1712,7 @@ MODULE_PARM_DESC(power_level,
module_param_named(fw_monitor, iwlwifi_mod_params.fw_monitor, bool, S_IRUGO);
MODULE_PARM_DESC(fw_monitor,
"firmware monitor - to debug FW (default: false - needs lots of memory)");
+
+module_param_named(d0i3_timeout, iwlwifi_mod_params.d0i3_entry_delay,
+ uint, S_IRUGO);
+MODULE_PARM_DESC(d0i3_timeout, "Timeout to D0i3 entry when idle (ms)");
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.h b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h
index cda746b33db1..f6eacfdbc265 100644
--- a/drivers/net/wireless/iwlwifi/iwl-drv.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -69,7 +69,7 @@
/* for all modules */
#define DRV_NAME "iwlwifi"
#define DRV_COPYRIGHT "Copyright(c) 2003- 2015 Intel Corporation"
-#define DRV_AUTHOR "<ilw@linux.intel.com>"
+#define DRV_AUTHOR "<linuxwifi@intel.com>"
/* radio config bits (actual values from NVM definition) */
#define NVM_RF_CFG_DASH_MSK(x) (x & 0x3) /* bits 0-1 */
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c
index acc3d186c5c1..c15f5be85197 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -454,11 +454,11 @@ static void iwl_eeprom_enhanced_txpower(struct device *dev,
TXP_CHECK_AND_PRINT(COMMON_TYPE),
txp->flags);
IWL_DEBUG_EEPROM(dev,
- "\t\t chain_A: 0x%02x chain_B: 0X%02x chain_C: 0X%02x\n",
+ "\t\t chain_A: %d chain_B: %d chain_C: %d\n",
txp->chain_a_max, txp->chain_b_max,
txp->chain_c_max);
IWL_DEBUG_EEPROM(dev,
- "\t\t MIMO2: 0x%02x MIMO3: 0x%02x High 20_on_40: 0x%02x Low 20_on_40: 0x%02x\n",
+ "\t\t MIMO2: %d MIMO3: %d High 20_on_40: 0x%02x Low 20_on_40: 0x%02x\n",
txp->mimo2_max, txp->mimo3_max,
((txp->delta_20_in_40 & 0xf0) >> 4),
(txp->delta_20_in_40 & 0x0f));
@@ -766,7 +766,7 @@ void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
if (cfg->ht_params->ldpc)
ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING;
- if (iwlwifi_mod_params.amsdu_size_8K)
+ if (iwlwifi_mod_params.amsdu_size >= IWL_AMSDU_8K)
ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
ht_info->ampdu_factor = cfg->max_ht_ampdu_exponent;
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h
index 750c8c9ee70d..ad2b834668ff 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c
index 219ca8acca62..f2cea1c7befc 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h
index a6d3bdf82cdd..1ed78be06c23 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.h
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/iwl-fh.h b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h
index d56064861a9c..5cc6be927eab 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fh.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h
index 9dbe19cbb4dd..a5aaf6853704 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -88,6 +88,7 @@
* &struct iwl_fw_error_dump_rb
* @IWL_FW_ERROR_PAGING: UMAC's image memory segments which were
* paged to the DRAM.
+ * @IWL_FW_ERROR_DUMP_RADIO_REG: Dump the radio registers.
*/
enum iwl_fw_error_dump_type {
/* 0 is deprecated */
@@ -103,6 +104,7 @@ enum iwl_fw_error_dump_type {
IWL_FW_ERROR_DUMP_ERROR_INFO = 10,
IWL_FW_ERROR_DUMP_RB = 11,
IWL_FW_ERROR_DUMP_PAGING = 12,
+ IWL_FW_ERROR_DUMP_RADIO_REG = 13,
IWL_FW_ERROR_DUMP_MAX,
};
@@ -288,6 +290,9 @@ iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data)
* @FW_DBG_TRIGGER_TIME_EVENT: trigger log collection upon time events related
* events.
* @FW_DBG_TRIGGER_BA: trigger log collection upon BlockAck related events.
+ * @FW_DBG_TX_LATENCY: trigger log collection when the tx latency goes above a
+ * threshold.
+ * @FW_DBG_TDLS: trigger log collection upon TDLS related events.
*/
enum iwl_fw_dbg_trigger {
FW_DBG_TRIGGER_INVALID = 0,
@@ -302,6 +307,8 @@ enum iwl_fw_dbg_trigger {
FW_DBG_TRIGGER_TXQ_TIMERS,
FW_DBG_TRIGGER_TIME_EVENT,
FW_DBG_TRIGGER_BA,
+ FW_DBG_TRIGGER_TX_LATENCY,
+ FW_DBG_TRIGGER_TDLS,
/* must be last */
FW_DBG_TRIGGER_MAX,
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h
index 08303db0000f..84f8aeb926c8 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h
@@ -7,6 +7,7 @@
*
* Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -26,13 +27,14 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -308,6 +310,10 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t;
* @IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT: supports gscan
* @IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE: extended DTS measurement
* @IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS: supports short PM timeouts
+ * @IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT: supports bt-coex Multi-priority LUT
+ * @IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION: firmware will decide on what
+ * antenna the beacon should be transmitted
+ * @IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2: support LAR API V2
*
* @NUM_IWL_UCODE_TLV_CAPA: number of bits used
*/
@@ -334,6 +340,9 @@ enum iwl_ucode_tlv_capa {
IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT = (__force iwl_ucode_tlv_capa_t)31,
IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE = (__force iwl_ucode_tlv_capa_t)64,
IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS = (__force iwl_ucode_tlv_capa_t)65,
+ IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT = (__force iwl_ucode_tlv_capa_t)67,
+ IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION = (__force iwl_ucode_tlv_capa_t)71,
+ IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2 = (__force iwl_ucode_tlv_capa_t)73,
NUM_IWL_UCODE_TLV_CAPA
#ifdef __CHECKER__
@@ -548,6 +557,8 @@ enum iwl_fw_dbg_trigger_vif_type {
* @start_conf_id: if mode is %IWL_FW_DBG_TRIGGER_START, this defines what
* configuration should be applied when the triggers kicks in.
* @occurrences: number of occurrences. 0 means the trigger will never fire.
+ * @trig_dis_ms: the time, in milliseconds, after an occurrence of this
+ * trigger in which another occurrence should be ignored.
*/
struct iwl_fw_dbg_trigger_tlv {
__le32 id;
@@ -557,7 +568,8 @@ struct iwl_fw_dbg_trigger_tlv {
u8 mode;
u8 start_conf_id;
__le16 occurrences;
- __le32 reserved[2];
+ __le16 trig_dis_ms;
+ __le16 reserved[3];
u8 data[0];
} __packed;
@@ -723,6 +735,19 @@ struct iwl_fw_dbg_trigger_ba {
} __packed;
/**
+ * struct iwl_fw_dbg_trigger_tdls - configures trigger for TDLS events.
+ * @action_bitmap: the TDLS action to trigger the collection upon
+ * @peer_mode: trigger on specific peer or all
+ * @peer: the TDLS peer to trigger the collection on
+ */
+struct iwl_fw_dbg_trigger_tdls {
+ u8 action_bitmap;
+ u8 peer_mode;
+ u8 peer[ETH_ALEN];
+ u8 reserved[4];
+} __packed;
+
+/**
* struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration.
* @id: conf id
* @usniffer: should the uSniffer image be used
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h
index 84ec0cefb62a..85d6d6d55e2f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -305,18 +305,4 @@ iwl_fw_dbg_conf_usniffer(const struct iwl_fw *fw, u8 id)
return conf_tlv->usniffer;
}
-#define iwl_fw_dbg_trigger_enabled(fw, id) ({ \
- void *__dbg_trigger = (fw)->dbg_trigger_tlv[(id)]; \
- unlikely(__dbg_trigger); \
-})
-
-static inline struct iwl_fw_dbg_trigger_tlv*
-iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, u8 id)
-{
- if (WARN_ON(id >= ARRAY_SIZE(fw->dbg_trigger_tlv)))
- return NULL;
-
- return fw->dbg_trigger_tlv[id];
-}
-
#endif /* __iwl_fw_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c
index 0bd9d4aad0c0..32c8f84ae519 100644
--- a/drivers/net/wireless/iwlwifi/iwl-io.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c
@@ -1,6 +1,7 @@
/******************************************************************************
*
* Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2015 Intel Deutschland GmbH
*
* Portions of this file are derived from the ipw3945 project.
*
@@ -21,7 +22,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -81,7 +82,7 @@ u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg)
{
u32 value = 0x5a5a5a5a;
unsigned long flags;
- if (iwl_trans_grab_nic_access(trans, false, &flags)) {
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
value = iwl_read32(trans, reg);
iwl_trans_release_nic_access(trans, &flags);
}
@@ -94,7 +95,7 @@ void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value)
{
unsigned long flags;
- if (iwl_trans_grab_nic_access(trans, false, &flags)) {
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
iwl_write32(trans, reg, value);
iwl_trans_release_nic_access(trans, &flags);
}
@@ -117,26 +118,28 @@ int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask,
}
IWL_EXPORT_SYMBOL(iwl_poll_direct_bit);
-u32 __iwl_read_prph(struct iwl_trans *trans, u32 ofs)
+u32 iwl_read_prph_no_grab(struct iwl_trans *trans, u32 ofs)
{
u32 val = iwl_trans_read_prph(trans, ofs);
trace_iwlwifi_dev_ioread_prph32(trans->dev, ofs, val);
return val;
}
+IWL_EXPORT_SYMBOL(iwl_read_prph_no_grab);
-void __iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val)
+void iwl_write_prph_no_grab(struct iwl_trans *trans, u32 ofs, u32 val)
{
trace_iwlwifi_dev_iowrite_prph32(trans->dev, ofs, val);
iwl_trans_write_prph(trans, ofs, val);
}
+IWL_EXPORT_SYMBOL(iwl_write_prph_no_grab);
u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs)
{
unsigned long flags;
u32 val = 0x5a5a5a5a;
- if (iwl_trans_grab_nic_access(trans, false, &flags)) {
- val = __iwl_read_prph(trans, ofs);
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
+ val = iwl_read_prph_no_grab(trans, ofs);
iwl_trans_release_nic_access(trans, &flags);
}
return val;
@@ -147,8 +150,8 @@ void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val)
{
unsigned long flags;
- if (iwl_trans_grab_nic_access(trans, false, &flags)) {
- __iwl_write_prph(trans, ofs, val);
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
+ iwl_write_prph_no_grab(trans, ofs, val);
iwl_trans_release_nic_access(trans, &flags);
}
}
@@ -173,9 +176,10 @@ void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
{
unsigned long flags;
- if (iwl_trans_grab_nic_access(trans, false, &flags)) {
- __iwl_write_prph(trans, ofs,
- __iwl_read_prph(trans, ofs) | mask);
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
+ iwl_write_prph_no_grab(trans, ofs,
+ iwl_read_prph_no_grab(trans, ofs) |
+ mask);
iwl_trans_release_nic_access(trans, &flags);
}
}
@@ -186,9 +190,10 @@ void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
{
unsigned long flags;
- if (iwl_trans_grab_nic_access(trans, false, &flags)) {
- __iwl_write_prph(trans, ofs,
- (__iwl_read_prph(trans, ofs) & mask) | bits);
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
+ iwl_write_prph_no_grab(trans, ofs,
+ (iwl_read_prph_no_grab(trans, ofs) &
+ mask) | bits);
iwl_trans_release_nic_access(trans, &flags);
}
}
@@ -199,9 +204,9 @@ void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
unsigned long flags;
u32 val;
- if (iwl_trans_grab_nic_access(trans, false, &flags)) {
- val = __iwl_read_prph(trans, ofs);
- __iwl_write_prph(trans, ofs, (val & ~mask));
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
+ val = iwl_read_prph_no_grab(trans, ofs);
+ iwl_write_prph_no_grab(trans, ofs, (val & ~mask));
iwl_trans_release_nic_access(trans, &flags);
}
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/intel/iwlwifi/iwl-io.h
index 501d0560c061..a9bcc788cae1 100644
--- a/drivers/net/wireless/iwlwifi/iwl-io.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.h
@@ -21,7 +21,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -55,9 +55,9 @@ u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg);
void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value);
-u32 __iwl_read_prph(struct iwl_trans *trans, u32 ofs);
+u32 iwl_read_prph_no_grab(struct iwl_trans *trans, u32 ofs);
u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs);
-void __iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val);
+void iwl_write_prph_no_grab(struct iwl_trans *trans, u32 ofs, u32 val);
void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val);
int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr,
u32 bits, u32 mask, int timeout);
diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h
index ac2b90df8413..fd42f63f5e84 100644
--- a/drivers/net/wireless/iwlwifi/iwl-modparams.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -86,6 +86,12 @@ enum iwl_disable_11n {
IWL_ENABLE_HT_TXAGG = BIT(3),
};
+enum iwl_amsdu_size {
+ IWL_AMSDU_4K = 0,
+ IWL_AMSDU_8K = 1,
+ IWL_AMSDU_12K = 2,
+};
+
/**
* struct iwl_mod_params
*
@@ -94,7 +100,7 @@ enum iwl_disable_11n {
* @sw_crypto: using hardware encryption, default = 0
* @disable_11n: disable 11n capabilities, default = 0,
* use IWL_[DIS,EN]ABLE_HT_* constants
- * @amsdu_size_8K: enable 8K amsdu size, default = 0
+ * @amsdu_size: enable 8K amsdu size, default = 4K. enum iwl_amsdu_size.
* @restart_fw: restart firmware, default = 1
* @bt_coex_active: enable bt coex, default = true
* @led_mode: system default, default = 0
@@ -103,13 +109,15 @@ enum iwl_disable_11n {
* @debug_level: levels are IWL_DL_*
* @ant_coupling: antenna coupling in dB, default = 0
* @d0i3_disable: disable d0i3, default = 1,
+ * @d0i3_entry_delay: time to wait after no refs are taken before
+ * entering D0i3 (in msecs)
* @lar_disable: disable LAR (regulatory), default = 0
* @fw_monitor: allow to use firmware monitor
*/
struct iwl_mod_params {
int sw_crypto;
unsigned int disable_11n;
- int amsdu_size_8K;
+ int amsdu_size;
bool restart_fw;
bool bt_coex_active;
int led_mode;
@@ -122,6 +130,7 @@ struct iwl_mod_params {
char *nvm_file;
bool uapsd_disable;
bool d0i3_disable;
+ unsigned int d0i3_entry_delay;
bool lar_disable;
bool fw_monitor;
};
diff --git a/drivers/net/wireless/iwlwifi/iwl-notif-wait.c b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c
index 6caf2affbbb5..8aa1f2b7fdfc 100644
--- a/drivers/net/wireless/iwlwifi/iwl-notif-wait.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/iwl-notif-wait.h b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h
index dbe8234521de..0f9995ed71cd 100644
--- a/drivers/net/wireless/iwlwifi/iwl-notif-wait.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
index d82984912e04..7b89bfc8c8ac 100644
--- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -379,8 +379,19 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
else
vht_cap->cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN;
- if (iwlwifi_mod_params.amsdu_size_8K)
+ switch (iwlwifi_mod_params.amsdu_size) {
+ case IWL_AMSDU_4K:
+ vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895;
+ break;
+ case IWL_AMSDU_8K:
vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991;
+ break;
+ case IWL_AMSDU_12K:
+ vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454;
+ break;
+ default:
+ break;
+ }
vht_cap->vht_mcs.rx_mcs_map =
cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 |
@@ -580,15 +591,13 @@ static void iwl_set_hw_address_family_8000(struct device *dev,
IWL_ERR_DEV(dev, "mac address is not found\n");
}
-#define IWL_4165_DEVICE_ID 0x5501
-
struct iwl_nvm_data *
iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
const __le16 *nvm_hw, const __le16 *nvm_sw,
const __le16 *nvm_calib, const __le16 *regulatory,
const __le16 *mac_override, const __le16 *phy_sku,
u8 tx_chains, u8 rx_chains, bool lar_fw_supported,
- u32 mac_addr0, u32 mac_addr1, u32 hw_id)
+ u32 mac_addr0, u32 mac_addr1)
{
struct iwl_nvm_data *data;
u32 sku;
@@ -627,17 +636,6 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
(sku & NVM_SKU_CAP_11AC_ENABLE);
data->sku_cap_mimo_disabled = sku & NVM_SKU_CAP_MIMO_DISABLE;
- /*
- * OTP 0x52 bug work around
- * define antenna 1x1 according to MIMO disabled
- */
- if (hw_id == IWL_4165_DEVICE_ID && data->sku_cap_mimo_disabled) {
- data->valid_tx_ant = ANT_B;
- data->valid_rx_ant = ANT_B;
- tx_chains = ANT_B;
- rx_chains = ANT_B;
- }
-
data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw);
if (cfg->device_family != IWL_DEVICE_FAMILY_8000) {
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h
index 9f44d8188c5c..92466ee72806 100644
--- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -79,7 +79,7 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
const __le16 *nvm_calib, const __le16 *regulatory,
const __le16 *mac_override, const __le16 *phy_sku,
u8 tx_chains, u8 rx_chains, bool lar_fw_supported,
- u32 mac_addr0, u32 mac_addr1, u32 hw_id);
+ u32 mac_addr0, u32 mac_addr1);
/**
* iwl_parse_mcc_info - parse MCC (mobile country code) info coming from FW
diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h
index 2a58d6833224..b49eda8150bb 100644
--- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h
@@ -27,7 +27,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -123,6 +123,8 @@ struct iwl_cfg;
* received on the RSS queue(s). The queue parameter indicates which of the
* RSS queues received this frame; it will always be non-zero.
* This method must not sleep.
+ * @async_cb: called when an ASYNC command with CMD_WANT_ASYNC_CALLBACK set
+ * completes. Must be atomic.
* @queue_full: notifies that a HW queue is full.
* Must be atomic and called with BH disabled.
* @queue_not_full: notifies that a HW queue is not full any more.
@@ -155,6 +157,8 @@ struct iwl_op_mode_ops {
struct iwl_rx_cmd_buffer *rxb);
void (*rx_rss)(struct iwl_op_mode *op_mode, struct napi_struct *napi,
struct iwl_rx_cmd_buffer *rxb, unsigned int queue);
+ void (*async_cb)(struct iwl_op_mode *op_mode,
+ const struct iwl_device_cmd *cmd);
void (*queue_full)(struct iwl_op_mode *op_mode, int queue);
void (*queue_not_full)(struct iwl_op_mode *op_mode, int queue);
bool (*hw_rf_kill)(struct iwl_op_mode *op_mode, bool state);
@@ -203,6 +207,13 @@ static inline void iwl_op_mode_rx_rss(struct iwl_op_mode *op_mode,
op_mode->ops->rx_rss(op_mode, napi, rxb, queue);
}
+static inline void iwl_op_mode_async_cb(struct iwl_op_mode *op_mode,
+ const struct iwl_device_cmd *cmd)
+{
+ if (op_mode->ops->async_cb)
+ op_mode->ops->async_cb(op_mode, cmd);
+}
+
static inline void iwl_op_mode_queue_full(struct iwl_op_mode *op_mode,
int queue)
{
diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.c b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c
index a105455b6a24..4a4dea08751c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-phy-db.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.c
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.h b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h
index 9ee18d0d2d01..24103877eab0 100644
--- a/drivers/net/wireless/iwlwifi/iwl-phy-db.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-phy-db.h
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h
index 3ab777f79e4f..5bde23a472b4 100644
--- a/drivers/net/wireless/iwlwifi/iwl-prph.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -345,6 +345,12 @@ enum secure_load_status_reg {
#define TXF_READ_MODIFY_DATA (0xa00448)
#define TXF_READ_MODIFY_ADDR (0xa0044c)
+/* Radio registers access */
+#define RSP_RADIO_CMD (0xa02804)
+#define RSP_RADIO_RDDAT (0xa02814)
+#define RADIO_RSP_ADDR_POS (6)
+#define RADIO_RSP_RD_CMD (3)
+
/* FW monitor */
#define MON_BUFF_SAMPLE_CTL (0xa03c00)
#define MON_BUFF_BASE_ADDR (0xa03c3c)
diff --git a/drivers/net/wireless/iwlwifi/iwl-scd.h b/drivers/net/wireless/intel/iwlwifi/iwl-scd.h
index f2353ebf2666..99b43da32adf 100644
--- a/drivers/net/wireless/iwlwifi/iwl-scd.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-scd.h
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
index 71610968c365..6069a9ff53fa 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -61,7 +61,10 @@
*
*****************************************************************************/
#include <linux/kernel.h>
+#include <linux/bsearch.h>
+
#include "iwl-trans.h"
+#include "iwl-drv.h"
struct iwl_trans *iwl_trans_alloc(unsigned int priv_size,
struct device *dev,
@@ -112,3 +115,91 @@ void iwl_trans_free(struct iwl_trans *trans)
kmem_cache_destroy(trans->dev_cmd_pool);
kfree(trans);
}
+
+int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
+{
+ int ret;
+
+ if (unlikely(!(cmd->flags & CMD_SEND_IN_RFKILL) &&
+ test_bit(STATUS_RFKILL, &trans->status)))
+ return -ERFKILL;
+
+ if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status)))
+ return -EIO;
+
+ if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) {
+ IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
+ return -EIO;
+ }
+
+ if (WARN_ON((cmd->flags & CMD_WANT_ASYNC_CALLBACK) &&
+ !(cmd->flags & CMD_ASYNC)))
+ return -EINVAL;
+
+ if (!(cmd->flags & CMD_ASYNC))
+ lock_map_acquire_read(&trans->sync_cmd_lockdep_map);
+
+ ret = trans->ops->send_cmd(trans, cmd);
+
+ if (!(cmd->flags & CMD_ASYNC))
+ lock_map_release(&trans->sync_cmd_lockdep_map);
+
+ return ret;
+}
+IWL_EXPORT_SYMBOL(iwl_trans_send_cmd);
+
+/* Comparator for struct iwl_hcmd_names.
+ * Used in the binary search over a list of host commands.
+ *
+ * @key: command_id that we're looking for.
+ * @elt: struct iwl_hcmd_names candidate for match.
+ *
+ * @return 0 iff equal.
+ */
+static int iwl_hcmd_names_cmp(const void *key, const void *elt)
+{
+ const struct iwl_hcmd_names *name = elt;
+ u8 cmd1 = *(u8 *)key;
+ u8 cmd2 = name->cmd_id;
+
+ return (cmd1 - cmd2);
+}
+
+const char *iwl_get_cmd_string(struct iwl_trans *trans, u32 id)
+{
+ u8 grp, cmd;
+ struct iwl_hcmd_names *ret;
+ const struct iwl_hcmd_arr *arr;
+ size_t size = sizeof(struct iwl_hcmd_names);
+
+ grp = iwl_cmd_groupid(id);
+ cmd = iwl_cmd_opcode(id);
+
+ if (!trans->command_groups || grp >= trans->command_groups_size ||
+ !trans->command_groups[grp].arr)
+ return "UNKNOWN";
+
+ arr = &trans->command_groups[grp];
+ ret = bsearch(&cmd, arr->arr, arr->size, size, iwl_hcmd_names_cmp);
+ if (!ret)
+ return "UNKNOWN";
+ return ret->cmd_name;
+}
+IWL_EXPORT_SYMBOL(iwl_get_cmd_string);
+
+int iwl_cmd_groups_verify_sorted(const struct iwl_trans_config *trans)
+{
+ int i, j;
+ const struct iwl_hcmd_arr *arr;
+
+ for (i = 0; i < trans->command_groups_size; i++) {
+ arr = &trans->command_groups[i];
+ if (!arr->arr)
+ continue;
+ for (j = 0; j < arr->size - 1; j++)
+ if (arr->arr[j].cmd_id > arr->arr[j + 1].cmd_id)
+ return -1;
+ }
+ return 0;
+}
+IWL_EXPORT_SYMBOL(iwl_cmd_groups_verify_sorted);
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
index 6f76525088f0..82fb3a97a46d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -68,6 +68,7 @@
#include <linux/ieee80211.h>
#include <linux/mm.h> /* for page_address */
#include <linux/lockdep.h>
+#include <linux/kernel.h>
#include "iwl-debug.h"
#include "iwl-config.h"
@@ -248,6 +249,8 @@ static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt)
* @CMD_MAKE_TRANS_IDLE: The command response should mark the trans as idle.
* @CMD_WAKE_UP_TRANS: The command response should wake up the trans
* (i.e. mark it as non-idle).
+ * @CMD_WANT_ASYNC_CALLBACK: the op_mode's async callback function must be
+ * called after this command completes. Valid only with CMD_ASYNC.
* @CMD_TB_BITMAP_POS: Position of the first bit for the TB bitmap. We need to
* check that we leave enough room for the TBs bitmap which needs 20 bits.
*/
@@ -259,6 +262,7 @@ enum CMD_MODE {
CMD_SEND_IN_IDLE = BIT(4),
CMD_MAKE_TRANS_IDLE = BIT(5),
CMD_WAKE_UP_TRANS = BIT(6),
+ CMD_WANT_ASYNC_CALLBACK = BIT(7),
CMD_TB_BITMAP_POS = 11,
};
@@ -377,6 +381,11 @@ static inline void iwl_free_rxb(struct iwl_rx_cmd_buffer *r)
#define MAX_NO_RECLAIM_CMDS 6
+/*
+ * The first entry in driver_data array in ieee80211_tx_info
+ * that can be used by the transport.
+ */
+#define IWL_TRANS_FIRST_DRIVER_DATA 2
#define IWL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo))))
/*
@@ -423,6 +432,38 @@ enum iwl_trans_status {
STATUS_TRANS_DEAD,
};
+static inline int
+iwl_trans_get_rb_size_order(enum iwl_amsdu_size rb_size)
+{
+ switch (rb_size) {
+ case IWL_AMSDU_4K:
+ return get_order(4 * 1024);
+ case IWL_AMSDU_8K:
+ return get_order(8 * 1024);
+ case IWL_AMSDU_12K:
+ return get_order(12 * 1024);
+ default:
+ WARN_ON(1);
+ return -1;
+ }
+}
+
+struct iwl_hcmd_names {
+ u8 cmd_id;
+ const char *const cmd_name;
+};
+
+#define HCMD_NAME(x) \
+ { .cmd_id = x, .cmd_name = #x }
+
+struct iwl_hcmd_arr {
+ const struct iwl_hcmd_names *arr;
+ int size;
+};
+
+#define HCMD_ARR(x) \
+ { .arr = x, .size = ARRAY_SIZE(x) }
+
/**
* struct iwl_trans_config - transport configuration
*
@@ -436,14 +477,16 @@ enum iwl_trans_status {
* list of such notifications to filter. Max length is
* %MAX_NO_RECLAIM_CMDS.
* @n_no_reclaim_cmds: # of commands in list
- * @rx_buf_size_8k: 8 kB RX buffer size needed for A-MSDUs,
+ * @rx_buf_size: RX buffer size needed for A-MSDUs
* if unset 4k will be the RX buffer size
* @bc_table_dword: set to true if the BC table expects the byte count to be
* in DWORD (as opposed to bytes)
* @scd_set_active: should the transport configure the SCD for HCMD queue
* @wide_cmd_header: firmware supports wide host command header
- * @command_names: array of command names, must be 256 entries
- * (one for each command); for debugging only
+ * @sw_csum_tx: transport should compute the TCP checksum
+ * @command_groups: array of command groups, each member is an array of the
+ * commands in the group; for debugging only
+ * @command_groups_size: number of command groups, to avoid illegal access
* @sdio_adma_addr: the default address to set for the ADMA in SDIO mode until
* we get the ALIVE from the uCode
*/
@@ -456,12 +499,14 @@ struct iwl_trans_config {
const u8 *no_reclaim_cmds;
unsigned int n_no_reclaim_cmds;
- bool rx_buf_size_8k;
+ enum iwl_amsdu_size rx_buf_size;
bool bc_table_dword;
bool scd_set_active;
bool wide_cmd_header;
- const char *const *command_names;
-
+ bool sw_csum_tx;
+ const struct iwl_hcmd_arr *command_groups;
+ int command_groups_size;
+
u32 sdio_adma_addr;
};
@@ -512,7 +557,11 @@ struct iwl_trans_txq_scd_cfg {
* If RFkill is asserted in the middle of a SYNC host command, it must
* return -ERFKILL straight away.
* May sleep only if CMD_ASYNC is not set
- * @tx: send an skb
+ * @tx: send an skb. The transport relies on the op_mode to zero the
+ * the ieee80211_tx_info->driver_data. If the MPDU is an A-MSDU, all
+ * the CSUM will be taken care of (TCP CSUM and IP header in case of
+ * IPv4). If the MPDU is a single MSDU, the op_mode must compute the IP
+ * header if it is IPv4.
* Must be atomic
* @reclaim: free packet until ssn. Returns a list of freed packets.
* Must be atomic
@@ -526,8 +575,11 @@ struct iwl_trans_txq_scd_cfg {
* @wait_tx_queue_empty: wait until tx queues are empty. May sleep.
* @freeze_txq_timer: prevents the timer of the queue from firing until the
* queue is set to awake. Must be atomic.
- * @dbgfs_register: add the dbgfs files under this directory. Files will be
- * automatically deleted.
+ * @block_txq_ptrs: stop updating the write pointers of the Tx queues. Note
+ * that the transport needs to refcount the calls since this function
+ * will be called several times with block = true, and then the queues
+ * need to be unblocked only after the same number of calls with
+ * block = false.
* @write8: write a u8 to a register at offset ofs from the BAR
* @write32: write a u32 to a register at offset ofs from the BAR
* @read32: read a u32 register at offset ofs from the BAR
@@ -583,10 +635,10 @@ struct iwl_trans_ops {
void (*txq_disable)(struct iwl_trans *trans, int queue,
bool configure_scd);
- int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir);
int (*wait_tx_queue_empty)(struct iwl_trans *trans, u32 txq_bm);
void (*freeze_txq_timer)(struct iwl_trans *trans, unsigned long txqs,
bool freeze);
+ void (*block_txq_ptrs)(struct iwl_trans *trans, bool block);
void (*write8)(struct iwl_trans *trans, u32 ofs, u8 val);
void (*write32)(struct iwl_trans *trans, u32 ofs, u32 val);
@@ -600,8 +652,7 @@ struct iwl_trans_ops {
void (*configure)(struct iwl_trans *trans,
const struct iwl_trans_config *trans_cfg);
void (*set_pmi)(struct iwl_trans *trans, bool state);
- bool (*grab_nic_access)(struct iwl_trans *trans, bool silent,
- unsigned long *flags);
+ bool (*grab_nic_access)(struct iwl_trans *trans, unsigned long *flags);
void (*release_nic_access)(struct iwl_trans *trans,
unsigned long *flags);
void (*set_bits_mask)(struct iwl_trans *trans, u32 reg, u32 mask,
@@ -612,7 +663,7 @@ struct iwl_trans_ops {
void (*resume)(struct iwl_trans *trans);
struct iwl_trans_dump_data *(*dump_data)(struct iwl_trans *trans,
- struct iwl_fw_dbg_trigger_tlv
+ const struct iwl_fw_dbg_trigger_tlv
*trigger);
};
@@ -628,18 +679,61 @@ enum iwl_trans_state {
};
/**
- * enum iwl_d0i3_mode - d0i3 mode
+ * DOC: Platform power management
+ *
+ * There are two types of platform power management: system-wide
+ * (WoWLAN) and runtime.
+ *
+ * In system-wide power management the entire platform goes into a low
+ * power state (e.g. idle or suspend to RAM) at the same time and the
+ * device is configured as a wakeup source for the entire platform.
+ * This is usually triggered by userspace activity (e.g. the user
+ * presses the suspend button or a power management daemon decides to
+ * put the platform in low power mode). The device's behavior in this
+ * mode is dictated by the wake-on-WLAN configuration.
+ *
+ * In runtime power management, only the devices which are themselves
+ * idle enter a low power state. This is done at runtime, which means
+ * that the entire system is still running normally. This mode is
+ * usually triggered automatically by the device driver and requires
+ * the ability to enter and exit the low power modes in a very short
+ * time, so there is not much impact in usability.
*
- * @IWL_D0I3_MODE_OFF - d0i3 is disabled
- * @IWL_D0I3_MODE_ON_IDLE - enter d0i3 when device is idle
- * (e.g. no active references)
- * @IWL_D0I3_MODE_ON_SUSPEND - enter d0i3 only on suspend
- * (in case of 'any' trigger)
+ * The terms used for the device's behavior are as follows:
+ *
+ * - D0: the device is fully powered and the host is awake;
+ * - D3: the device is in low power mode and only reacts to
+ * specific events (e.g. magic-packet received or scan
+ * results found);
+ * - D0I3: the device is in low power mode and reacts to any
+ * activity (e.g. RX);
+ *
+ * These terms reflect the power modes in the firmware and are not to
+ * be confused with the physical device power state. The NIC can be
+ * in D0I3 mode even if, for instance, the PCI device is in D3 state.
+ */
+
+/**
+ * enum iwl_plat_pm_mode - platform power management mode
+ *
+ * This enumeration describes the device's platform power management
+ * behavior when in idle mode (i.e. runtime power management) or when
+ * in system-wide suspend (i.e WoWLAN).
+ *
+ * @IWL_PLAT_PM_MODE_DISABLED: power management is disabled for this
+ * device. At runtime, this means that nothing happens and the
+ * device always remains in active. In system-wide suspend mode,
+ * it means that the all connections will be closed automatically
+ * by mac80211 before the platform is suspended.
+ * @IWL_PLAT_PM_MODE_D3: the device goes into D3 mode (i.e. WoWLAN).
+ * For runtime power management, this mode is not officially
+ * supported.
+ * @IWL_PLAT_PM_MODE_D0I3: the device goes into D0I3 mode.
*/
-enum iwl_d0i3_mode {
- IWL_D0I3_MODE_OFF = 0,
- IWL_D0I3_MODE_ON_IDLE,
- IWL_D0I3_MODE_ON_SUSPEND,
+enum iwl_plat_pm_mode {
+ IWL_PLAT_PM_MODE_DISABLED,
+ IWL_PLAT_PM_MODE_D3,
+ IWL_PLAT_PM_MODE_D0I3,
};
/**
@@ -679,6 +773,12 @@ enum iwl_d0i3_mode {
* the opmode.
* @paging_download_buf: Buffer used for copying all of the pages before
* downloading them to the FW. The buffer is allocated in the opmode
+ * @system_pm_mode: the system-wide power management mode in use.
+ * This mode is set dynamically, depending on the WoWLAN values
+ * configured from the userspace at runtime.
+ * @runtime_pm_mode: the runtime power management mode in use. This
+ * mode is set during the initialization phase and is not
+ * supposed to change during runtime.
*/
struct iwl_trans {
const struct iwl_trans_ops *ops;
@@ -698,6 +798,9 @@ struct iwl_trans {
bool pm_support;
bool ltr_enabled;
+ const struct iwl_hcmd_arr *command_groups;
+ int command_groups_size;
+
u8 num_rx_queues;
/* The following fields are internal only */
@@ -726,21 +829,24 @@ struct iwl_trans {
struct iwl_fw_paging *paging_db;
void *paging_download_buf;
- enum iwl_d0i3_mode d0i3_mode;
-
- bool wowlan_d0i3;
+ enum iwl_plat_pm_mode system_pm_mode;
+ enum iwl_plat_pm_mode runtime_pm_mode;
/* pointer to trans specific struct */
/*Ensure that this pointer will always be aligned to sizeof pointer */
char trans_specific[0] __aligned(sizeof(void *));
};
+const char *iwl_get_cmd_string(struct iwl_trans *trans, u32 id);
+int iwl_cmd_groups_verify_sorted(const struct iwl_trans_config *trans);
+
static inline void iwl_trans_configure(struct iwl_trans *trans,
const struct iwl_trans_config *trans_cfg)
{
trans->op_mode = trans_cfg->op_mode;
trans->ops->configure(trans, trans_cfg);
+ WARN_ON(iwl_cmd_groups_verify_sorted(trans_cfg));
}
static inline int _iwl_trans_start_hw(struct iwl_trans *trans, bool low_power)
@@ -860,41 +966,13 @@ static inline void iwl_trans_resume(struct iwl_trans *trans)
static inline struct iwl_trans_dump_data *
iwl_trans_dump_data(struct iwl_trans *trans,
- struct iwl_fw_dbg_trigger_tlv *trigger)
+ const struct iwl_fw_dbg_trigger_tlv *trigger)
{
if (!trans->ops->dump_data)
return NULL;
return trans->ops->dump_data(trans, trigger);
}
-static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
- struct iwl_host_cmd *cmd)
-{
- int ret;
-
- if (unlikely(!(cmd->flags & CMD_SEND_IN_RFKILL) &&
- test_bit(STATUS_RFKILL, &trans->status)))
- return -ERFKILL;
-
- if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status)))
- return -EIO;
-
- if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) {
- IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
- return -EIO;
- }
-
- if (!(cmd->flags & CMD_ASYNC))
- lock_map_acquire_read(&trans->sync_cmd_lockdep_map);
-
- ret = trans->ops->send_cmd(trans, cmd);
-
- if (!(cmd->flags & CMD_ASYNC))
- lock_map_release(&trans->sync_cmd_lockdep_map);
-
- return ret;
-}
-
static inline struct iwl_device_cmd *
iwl_trans_alloc_tx_cmd(struct iwl_trans *trans)
{
@@ -907,6 +985,8 @@ iwl_trans_alloc_tx_cmd(struct iwl_trans *trans)
(dev_cmd_ptr + trans->dev_cmd_headroom);
}
+int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd);
+
static inline void iwl_trans_free_tx_cmd(struct iwl_trans *trans,
struct iwl_device_cmd *dev_cmd)
{
@@ -921,8 +1001,10 @@ static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb,
if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status)))
return -EIO;
- if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
+ if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) {
IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
+ return -EIO;
+ }
return trans->ops->tx(trans, skb, dev_cmd, queue);
}
@@ -930,8 +1012,10 @@ static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb,
static inline void iwl_trans_reclaim(struct iwl_trans *trans, int queue,
int ssn, struct sk_buff_head *skbs)
{
- if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
+ if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) {
IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
+ return;
+ }
trans->ops->reclaim(trans, queue, ssn, skbs);
}
@@ -949,8 +1033,10 @@ iwl_trans_txq_enable_cfg(struct iwl_trans *trans, int queue, u16 ssn,
{
might_sleep();
- if (unlikely((trans->state != IWL_TRANS_FW_ALIVE)))
+ if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) {
IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
+ return;
+ }
trans->ops->txq_enable(trans, queue, ssn, cfg, queue_wdg_timeout);
}
@@ -990,26 +1076,36 @@ static inline void iwl_trans_freeze_txq_timer(struct iwl_trans *trans,
unsigned long txqs,
bool freeze)
{
- if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
+ if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) {
IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
+ return;
+ }
if (trans->ops->freeze_txq_timer)
trans->ops->freeze_txq_timer(trans, txqs, freeze);
}
-static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans,
- u32 txqs)
+static inline void iwl_trans_block_txq_ptrs(struct iwl_trans *trans,
+ bool block)
{
- if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
+ if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) {
IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
+ return;
+ }
- return trans->ops->wait_tx_queue_empty(trans, txqs);
+ if (trans->ops->block_txq_ptrs)
+ trans->ops->block_txq_ptrs(trans, block);
}
-static inline int iwl_trans_dbgfs_register(struct iwl_trans *trans,
- struct dentry *dir)
+static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans,
+ u32 txqs)
{
- return trans->ops->dbgfs_register(trans, dir);
+ if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) {
+ IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
+ return -EIO;
+ }
+
+ return trans->ops->wait_tx_queue_empty(trans, txqs);
}
static inline void iwl_trans_write8(struct iwl_trans *trans, u32 ofs, u8 val)
@@ -1085,9 +1181,9 @@ iwl_trans_set_bits_mask(struct iwl_trans *trans, u32 reg, u32 mask, u32 value)
trans->ops->set_bits_mask(trans, reg, mask, value);
}
-#define iwl_trans_grab_nic_access(trans, silent, flags) \
+#define iwl_trans_grab_nic_access(trans, flags) \
__cond_lock(nic_access, \
- likely((trans)->ops->grab_nic_access(trans, silent, flags)))
+ likely((trans)->ops->grab_nic_access(trans, flags)))
static inline void __releases(nic_access)
iwl_trans_release_nic_access(struct iwl_trans *trans, unsigned long *flags)
diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile
index 8c2c3d13b092..23e7e2937566 100644
--- a/drivers/net/wireless/iwlwifi/mvm/Makefile
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile
@@ -1,12 +1,12 @@
obj-$(CONFIG_IWLMVM) += iwlmvm.o
iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
-iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o
+iwlmvm-y += utils.o rx.o rxmq.o tx.o binding.o quota.o sta.o sf.o
iwlmvm-y += scan.o time-event.o rs.o
iwlmvm-y += power.o coex.o coex_legacy.o
iwlmvm-y += tt.o offloading.o tdls.o
iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o
-iwlmvm-y += tof.o
-iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
+iwlmvm-y += tof.o fw-dbg.o
+iwlmvm-$(CONFIG_PM) += d3.o
ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../
diff --git a/drivers/net/wireless/iwlwifi/mvm/binding.c b/drivers/net/wireless/intel/iwlwifi/mvm/binding.c
index a1376539d2dc..7cb68f6ed1b0 100644
--- a/drivers/net/wireless/iwlwifi/mvm/binding.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/binding.c
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c
index e290ac67d975..2e098f8e0f83 100644
--- a/drivers/net/wireless/iwlwifi/mvm/coex.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -443,11 +443,8 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
if (iwl_mvm_bt_is_plcr_supported(mvm))
bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_CORUN_ENABLED);
- if (IWL_MVM_BT_COEX_MPLUT) {
+ if (iwl_mvm_is_mplut_supported(mvm))
bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_MPLUT_ENABLED);
- bt_cmd.enabled_modules |=
- cpu_to_le32(BT_COEX_MPLUT_BOOST_ENABLED);
- }
bt_cmd.enabled_modules |= cpu_to_le32(BT_COEX_HIGH_BAND_RET);
@@ -904,6 +901,7 @@ u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
struct ieee80211_tx_info *info, u8 ac)
{
__le16 fc = hdr->frame_control;
+ bool mplut_enabled = iwl_mvm_is_mplut_supported(mvm);
if (info->band != IEEE80211_BAND_2GHZ)
return 0;
@@ -911,22 +909,27 @@ u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
if (unlikely(mvm->bt_tx_prio))
return mvm->bt_tx_prio - 1;
- /* High prio packet (wrt. BT coex) if it is EAPOL, MCAST or MGMT */
- if (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO ||
- is_multicast_ether_addr(hdr->addr1) ||
- ieee80211_is_ctl(fc) || ieee80211_is_mgmt(fc) ||
- ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc))
- return 3;
-
- switch (ac) {
- case IEEE80211_AC_BE:
- return 1;
- case IEEE80211_AC_VO:
+ if (likely(ieee80211_is_data(fc))) {
+ if (likely(ieee80211_is_data_qos(fc))) {
+ switch (ac) {
+ case IEEE80211_AC_BE:
+ return mplut_enabled ? 1 : 0;
+ case IEEE80211_AC_VI:
+ return mplut_enabled ? 2 : 3;
+ case IEEE80211_AC_VO:
+ return 3;
+ default:
+ return 0;
+ }
+ } else if (is_multicast_ether_addr(hdr->addr1)) {
+ return 3;
+ } else
+ return 0;
+ } else if (ieee80211_is_mgmt(fc)) {
+ return ieee80211_is_disassoc(fc) ? 0 : 3;
+ } else if (ieee80211_is_ctl(fc)) {
+ /* ignore cfend and cfendack frames as we never send those */
return 3;
- case IEEE80211_AC_VI:
- return 2;
- default:
- break;
}
return 0;
diff --git a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex_legacy.c
index 61c07b05fcaa..015045733444 100644
--- a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex_legacy.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h
index 5c21231e195d..b00c03fcd447 100644
--- a/drivers/net/wireless/iwlwifi/mvm/constants.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -106,6 +106,7 @@
#define IWL_MVM_RS_RSSI_BASED_INIT_RATE 0
#define IWL_MVM_RS_80_20_FAR_RANGE_TWEAK 1
#define IWL_MVM_TOF_IS_RESPONDER 0
+#define IWL_MVM_SW_TX_CSUM_OFFLOAD 0
#define IWL_MVM_RS_NUM_TRY_BEFORE_ANT_TOGGLE 1
#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE 2
#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE_TW 1
diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
index 85ae902df7c0..d3e21d95cece 100644
--- a/drivers/net/wireless/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -104,9 +104,13 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
struct inet6_ifaddr *ifa;
int idx = 0;
+ memset(mvmvif->tentative_addrs, 0, sizeof(mvmvif->tentative_addrs));
+
read_lock_bh(&idev->lock);
list_for_each_entry(ifa, &idev->addr_list, if_list) {
mvmvif->target_ipv6_addrs[idx] = ifa->addr;
+ if (ifa->flags & IFA_F_TENTATIVE)
+ __set_bit(idx, mvmvif->tentative_addrs);
idx++;
if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX)
break;
@@ -133,10 +137,32 @@ static void iwl_mvm_convert_p1k(u16 *p1k, __le16 *out)
out[i] = cpu_to_le16(p1k[i]);
}
+static const u8 *iwl_mvm_find_max_pn(struct ieee80211_key_conf *key,
+ struct iwl_mvm_key_pn *ptk_pn,
+ struct ieee80211_key_seq *seq,
+ int tid, int queues)
+{
+ const u8 *ret = seq->ccmp.pn;
+ int i;
+
+ /* get the PN from mac80211, used on the default queue */
+ ieee80211_get_key_rx_seq(key, tid, seq);
+
+ /* and use the internal data for the other queues */
+ for (i = 1; i < queues; i++) {
+ const u8 *tmp = ptk_pn->q[i].pn[tid];
+
+ if (memcmp(ret, tmp, IEEE80211_CCMP_PN_LEN) <= 0)
+ ret = tmp;
+ }
+
+ return ret;
+}
+
struct wowlan_key_data {
struct iwl_wowlan_rsc_tsc_params_cmd *rsc_tsc;
struct iwl_wowlan_tkip_params_cmd *tkip;
- bool error, use_rsc_tsc, use_tkip;
+ bool error, use_rsc_tsc, use_tkip, configure_keys;
int wep_key_idx;
};
@@ -158,8 +184,6 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw,
u16 p1k[IWL_P1K_SIZE];
int ret, i;
- mutex_lock(&mvm->mutex);
-
switch (key->cipher) {
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104: { /* hack it for now */
@@ -195,20 +219,25 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw,
wkc.wep_key.key_offset = data->wep_key_idx;
}
- ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, 0, sizeof(wkc), &wkc);
- data->error = ret != 0;
-
- mvm->ptk_ivlen = key->iv_len;
- mvm->ptk_icvlen = key->icv_len;
- mvm->gtk_ivlen = key->iv_len;
- mvm->gtk_icvlen = key->icv_len;
+ if (data->configure_keys) {
+ mutex_lock(&mvm->mutex);
+ ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, 0,
+ sizeof(wkc), &wkc);
+ data->error = ret != 0;
+
+ mvm->ptk_ivlen = key->iv_len;
+ mvm->ptk_icvlen = key->icv_len;
+ mvm->gtk_ivlen = key->iv_len;
+ mvm->gtk_icvlen = key->icv_len;
+ mutex_unlock(&mvm->mutex);
+ }
/* don't upload key again */
- goto out_unlock;
+ return;
}
default:
data->error = true;
- goto out_unlock;
+ return;
case WLAN_CIPHER_SUITE_AES_CMAC:
/*
* Ignore CMAC keys -- the WoWLAN firmware doesn't support them
@@ -217,7 +246,7 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw,
* IGTK for anything. This means we could spuriously wake up or
* be deauthenticated, but that was considered acceptable.
*/
- goto out_unlock;
+ return;
case WLAN_CIPHER_SUITE_TKIP:
if (sta) {
tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc;
@@ -287,47 +316,71 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw,
/*
* For non-QoS this relies on the fact that both the uCode and
- * mac80211 use TID 0 for checking the IV in the frames.
+ * mac80211/our RX code use TID 0 for checking the PN.
*/
- for (i = 0; i < IWL_NUM_RSC; i++) {
- u8 *pn = seq.ccmp.pn;
+ if (sta && iwl_mvm_has_new_rx_api(mvm)) {
+ struct iwl_mvm_sta *mvmsta;
+ struct iwl_mvm_key_pn *ptk_pn;
+ const u8 *pn;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ ptk_pn = rcu_dereference_protected(
+ mvmsta->ptk_pn[key->keyidx],
+ lockdep_is_held(&mvm->mutex));
+ if (WARN_ON(!ptk_pn))
+ break;
- ieee80211_get_key_rx_seq(key, i, &seq);
- aes_sc[i].pn = cpu_to_le64((u64)pn[5] |
- ((u64)pn[4] << 8) |
- ((u64)pn[3] << 16) |
- ((u64)pn[2] << 24) |
- ((u64)pn[1] << 32) |
- ((u64)pn[0] << 40));
+ for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
+ pn = iwl_mvm_find_max_pn(key, ptk_pn, &seq, i,
+ mvm->trans->num_rx_queues);
+ aes_sc[i].pn = cpu_to_le64((u64)pn[5] |
+ ((u64)pn[4] << 8) |
+ ((u64)pn[3] << 16) |
+ ((u64)pn[2] << 24) |
+ ((u64)pn[1] << 32) |
+ ((u64)pn[0] << 40));
+ }
+ } else {
+ for (i = 0; i < IWL_NUM_RSC; i++) {
+ u8 *pn = seq.ccmp.pn;
+
+ ieee80211_get_key_rx_seq(key, i, &seq);
+ aes_sc[i].pn = cpu_to_le64((u64)pn[5] |
+ ((u64)pn[4] << 8) |
+ ((u64)pn[3] << 16) |
+ ((u64)pn[2] << 24) |
+ ((u64)pn[1] << 32) |
+ ((u64)pn[0] << 40));
+ }
}
data->use_rsc_tsc = true;
break;
}
- /*
- * The D3 firmware hardcodes the key offset 0 as the key it uses
- * to transmit packets to the AP, i.e. the PTK.
- */
- if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
- key->hw_key_idx = 0;
- mvm->ptk_ivlen = key->iv_len;
- mvm->ptk_icvlen = key->icv_len;
- } else {
+ if (data->configure_keys) {
+ mutex_lock(&mvm->mutex);
/*
- * firmware only supports TSC/RSC for a single key,
- * so if there are multiple keep overwriting them
- * with new ones -- this relies on mac80211 doing
- * list_add_tail().
+ * The D3 firmware hardcodes the key offset 0 as the key it
+ * uses to transmit packets to the AP, i.e. the PTK.
*/
- key->hw_key_idx = 1;
- mvm->gtk_ivlen = key->iv_len;
- mvm->gtk_icvlen = key->icv_len;
+ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
+ mvm->ptk_ivlen = key->iv_len;
+ mvm->ptk_icvlen = key->icv_len;
+ ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, 0);
+ } else {
+ /*
+ * firmware only supports TSC/RSC for a single key,
+ * so if there are multiple keep overwriting them
+ * with new ones -- this relies on mac80211 doing
+ * list_add_tail().
+ */
+ mvm->gtk_ivlen = key->iv_len;
+ mvm->gtk_icvlen = key->icv_len;
+ ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, 1);
+ }
+ mutex_unlock(&mvm->mutex);
+ data->error = ret != 0;
}
-
- ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, true);
- data->error = ret != 0;
-out_unlock:
- mutex_unlock(&mvm->mutex);
}
static int iwl_mvm_send_patterns(struct iwl_mvm *mvm,
@@ -772,7 +825,7 @@ static int iwl_mvm_switch_to_d3(struct iwl_mvm *mvm)
*/
set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
- /* We reprogram keys and shouldn't allocate new key indices */
+ /* the fw is reset, so all the keys are cleared */
memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
mvm->ptk_ivlen = 0;
@@ -797,6 +850,8 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm,
wowlan_config_cmd->is_11n_connection =
ap_sta->ht_cap.ht_supported;
+ wowlan_config_cmd->flags = ENABLE_L3_FILTERING |
+ ENABLE_NBNS_FILTERING | ENABLE_DHCP_FILTERING;
/* Query the last used seqno and set it */
ret = iwl_mvm_get_last_nonqos_seq(mvm, vif);
@@ -846,20 +901,127 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm,
return 0;
}
-static int
-iwl_mvm_wowlan_config(struct iwl_mvm *mvm,
- struct cfg80211_wowlan *wowlan,
- struct iwl_wowlan_config_cmd *wowlan_config_cmd,
- struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif,
- struct ieee80211_sta *ap_sta)
+static void
+iwl_mvm_iter_d0i3_ap_keys(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ void (*iter)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *data),
+ void *data)
+{
+ struct ieee80211_sta *ap_sta;
+
+ rcu_read_lock();
+
+ ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[mvm->d0i3_ap_sta_id]);
+ if (IS_ERR_OR_NULL(ap_sta))
+ goto out;
+
+ ieee80211_iter_keys_rcu(mvm->hw, vif, iter, data);
+out:
+ rcu_read_unlock();
+}
+
+int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool d0i3,
+ u32 cmd_flags)
{
struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {};
struct iwl_wowlan_tkip_params_cmd tkip_cmd = {};
struct wowlan_key_data key_data = {
+ .configure_keys = !d0i3,
.use_rsc_tsc = false,
.tkip = &tkip_cmd,
.use_tkip = false,
};
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int ret;
+
+ key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL);
+ if (!key_data.rsc_tsc)
+ return -ENOMEM;
+
+ /*
+ * if we have to configure keys, call ieee80211_iter_keys(),
+ * as we need non-atomic context in order to take the
+ * required locks.
+ * for the d0i3 we can't use ieee80211_iter_keys(), as
+ * taking (almost) any mutex might result in deadlock.
+ */
+ if (!d0i3) {
+ /*
+ * Note that currently we don't propagate cmd_flags
+ * to the iterator. In case of key_data.configure_keys,
+ * all the configured commands are SYNC, and
+ * iwl_mvm_wowlan_program_keys() will take care of
+ * locking/unlocking mvm->mutex.
+ */
+ ieee80211_iter_keys(mvm->hw, vif,
+ iwl_mvm_wowlan_program_keys,
+ &key_data);
+ } else {
+ iwl_mvm_iter_d0i3_ap_keys(mvm, vif,
+ iwl_mvm_wowlan_program_keys,
+ &key_data);
+ }
+
+ if (key_data.error) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (key_data.use_rsc_tsc) {
+ ret = iwl_mvm_send_cmd_pdu(mvm,
+ WOWLAN_TSC_RSC_PARAM, cmd_flags,
+ sizeof(*key_data.rsc_tsc),
+ key_data.rsc_tsc);
+ if (ret)
+ goto out;
+ }
+
+ if (key_data.use_tkip) {
+ ret = iwl_mvm_send_cmd_pdu(mvm,
+ WOWLAN_TKIP_PARAM,
+ cmd_flags, sizeof(tkip_cmd),
+ &tkip_cmd);
+ if (ret)
+ goto out;
+ }
+
+ /* configure rekey data only if offloaded rekey is supported (d3) */
+ if (mvmvif->rekey_data.valid && !d0i3) {
+ memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd));
+ memcpy(kek_kck_cmd.kck, mvmvif->rekey_data.kck,
+ NL80211_KCK_LEN);
+ kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN);
+ memcpy(kek_kck_cmd.kek, mvmvif->rekey_data.kek,
+ NL80211_KEK_LEN);
+ kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN);
+ kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr;
+
+ ret = iwl_mvm_send_cmd_pdu(mvm,
+ WOWLAN_KEK_KCK_MATERIAL, cmd_flags,
+ sizeof(kek_kck_cmd),
+ &kek_kck_cmd);
+ if (ret)
+ goto out;
+ }
+ ret = 0;
+out:
+ kfree(key_data.rsc_tsc);
+ return ret;
+}
+
+static int
+iwl_mvm_wowlan_config(struct iwl_mvm *mvm,
+ struct cfg80211_wowlan *wowlan,
+ struct iwl_wowlan_config_cmd *wowlan_config_cmd,
+ struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif,
+ struct ieee80211_sta *ap_sta)
+{
int ret;
ret = iwl_mvm_switch_to_d3(mvm);
@@ -870,10 +1032,6 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm,
if (ret)
return ret;
- key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL);
- if (!key_data.rsc_tsc)
- return -ENOMEM;
-
if (!iwlwifi_mod_params.sw_crypto) {
/*
* This needs to be unlocked due to lock ordering
@@ -881,74 +1039,28 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm,
* that isn't really a problem though.
*/
mutex_unlock(&mvm->mutex);
- ieee80211_iter_keys(mvm->hw, vif,
- iwl_mvm_wowlan_program_keys,
- &key_data);
+ ret = iwl_mvm_wowlan_config_key_params(mvm, vif, false,
+ CMD_ASYNC);
mutex_lock(&mvm->mutex);
- if (key_data.error) {
- ret = -EIO;
- goto out;
- }
-
- if (key_data.use_rsc_tsc) {
- struct iwl_host_cmd rsc_tsc_cmd = {
- .id = WOWLAN_TSC_RSC_PARAM,
- .data[0] = key_data.rsc_tsc,
- .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
- .len[0] = sizeof(*key_data.rsc_tsc),
- };
-
- ret = iwl_mvm_send_cmd(mvm, &rsc_tsc_cmd);
- if (ret)
- goto out;
- }
-
- if (key_data.use_tkip) {
- ret = iwl_mvm_send_cmd_pdu(mvm,
- WOWLAN_TKIP_PARAM,
- 0, sizeof(tkip_cmd),
- &tkip_cmd);
- if (ret)
- goto out;
- }
-
- if (mvmvif->rekey_data.valid) {
- memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd));
- memcpy(kek_kck_cmd.kck, mvmvif->rekey_data.kck,
- NL80211_KCK_LEN);
- kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN);
- memcpy(kek_kck_cmd.kek, mvmvif->rekey_data.kek,
- NL80211_KEK_LEN);
- kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN);
- kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr;
-
- ret = iwl_mvm_send_cmd_pdu(mvm,
- WOWLAN_KEK_KCK_MATERIAL, 0,
- sizeof(kek_kck_cmd),
- &kek_kck_cmd);
- if (ret)
- goto out;
- }
+ if (ret)
+ return ret;
}
ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0,
sizeof(*wowlan_config_cmd),
wowlan_config_cmd);
if (ret)
- goto out;
+ return ret;
ret = iwl_mvm_send_patterns(mvm, wowlan);
if (ret)
- goto out;
+ return ret;
- ret = iwl_mvm_send_proto_offload(mvm, vif, false, 0);
+ ret = iwl_mvm_send_proto_offload(mvm, vif, false, true, 0);
if (ret)
- goto out;
+ return ret;
ret = iwl_mvm_send_remote_wake_cfg(mvm, vif, wowlan->tcp);
-
-out:
- kfree(key_data.rsc_tsc);
return ret;
}
@@ -1061,13 +1173,13 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) {
/* if we're not associated, this must be netdetect */
- if (!wowlan->nd_config && !mvm->nd_config) {
+ if (!wowlan->nd_config) {
ret = 1;
goto out_noreset;
}
ret = iwl_mvm_netdetect_config(
- mvm, wowlan, wowlan->nd_config ?: mvm->nd_config, vif);
+ mvm, wowlan, wowlan->nd_config, vif);
if (ret)
goto out;
@@ -1163,19 +1275,20 @@ remove_notif:
int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_trans *trans = mvm->trans;
int ret;
/* make sure the d0i3 exit work is not pending */
flush_work(&mvm->d0i3_exit_work);
- ret = iwl_trans_suspend(mvm->trans);
+ ret = iwl_trans_suspend(trans);
if (ret)
return ret;
- mvm->trans->wowlan_d0i3 = wowlan->any;
- if (mvm->trans->wowlan_d0i3) {
- /* 'any' trigger means d0i3 usage */
- if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) {
+ if (wowlan->any) {
+ trans->system_pm_mode = IWL_PLAT_PM_MODE_D0I3;
+
+ if (iwl_mvm_enter_d0i3_on_suspend(mvm)) {
ret = iwl_mvm_enter_d0i3_sync(mvm);
if (ret)
@@ -1186,11 +1299,13 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
__set_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
mutex_unlock(&mvm->d0i3_suspend_mutex);
- iwl_trans_d3_suspend(mvm->trans, false);
+ iwl_trans_d3_suspend(trans, false);
return 0;
}
+ trans->system_pm_mode = IWL_PLAT_PM_MODE_D3;
+
return __iwl_mvm_suspend(hw, wowlan, false);
}
@@ -1220,6 +1335,8 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm,
goto report;
}
+ pm_wakeup_event(mvm->dev, 0);
+
if (reasons & IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET)
wakeup.magic_pkt = true;
@@ -1355,18 +1472,42 @@ static void iwl_mvm_tkip_sc_to_seq(struct tkip_sc *sc,
seq->tkip.iv16 = le16_to_cpu(sc->iv16);
}
-static void iwl_mvm_set_aes_rx_seq(struct aes_sc *scs,
+static void iwl_mvm_set_aes_rx_seq(struct iwl_mvm *mvm, struct aes_sc *scs,
+ struct ieee80211_sta *sta,
struct ieee80211_key_conf *key)
{
int tid;
BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS);
- for (tid = 0; tid < IWL_NUM_RSC; tid++) {
- struct ieee80211_key_seq seq = {};
+ if (sta && iwl_mvm_has_new_rx_api(mvm)) {
+ struct iwl_mvm_sta *mvmsta;
+ struct iwl_mvm_key_pn *ptk_pn;
- iwl_mvm_aes_sc_to_seq(&scs[tid], &seq);
- ieee80211_set_key_rx_seq(key, tid, &seq);
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+ ptk_pn = rcu_dereference_protected(mvmsta->ptk_pn[key->keyidx],
+ lockdep_is_held(&mvm->mutex));
+ if (WARN_ON(!ptk_pn))
+ return;
+
+ for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+ struct ieee80211_key_seq seq = {};
+ int i;
+
+ iwl_mvm_aes_sc_to_seq(&scs[tid], &seq);
+ ieee80211_set_key_rx_seq(key, tid, &seq);
+ for (i = 1; i < mvm->trans->num_rx_queues; i++)
+ memcpy(ptk_pn->q[i].pn[tid],
+ seq.ccmp.pn, IEEE80211_CCMP_PN_LEN);
+ }
+ } else {
+ for (tid = 0; tid < IWL_NUM_RSC; tid++) {
+ struct ieee80211_key_seq seq = {};
+
+ iwl_mvm_aes_sc_to_seq(&scs[tid], &seq);
+ ieee80211_set_key_rx_seq(key, tid, &seq);
+ }
}
}
@@ -1385,14 +1526,15 @@ static void iwl_mvm_set_tkip_rx_seq(struct tkip_sc *scs,
}
}
-static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key,
+static void iwl_mvm_set_key_rx_seq(struct iwl_mvm *mvm,
+ struct ieee80211_key_conf *key,
struct iwl_wowlan_status *status)
{
union iwl_all_tsc_rsc *rsc = &status->gtk.rsc.all_tsc_rsc;
switch (key->cipher) {
case WLAN_CIPHER_SUITE_CCMP:
- iwl_mvm_set_aes_rx_seq(rsc->aes.multicast_rsc, key);
+ iwl_mvm_set_aes_rx_seq(mvm, rsc->aes.multicast_rsc, NULL, key);
break;
case WLAN_CIPHER_SUITE_TKIP:
iwl_mvm_set_tkip_rx_seq(rsc->tkip.multicast_rsc, key);
@@ -1403,6 +1545,7 @@ static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key,
}
struct iwl_mvm_d3_gtk_iter_data {
+ struct iwl_mvm *mvm;
struct iwl_wowlan_status *status;
void *last_gtk;
u32 cipher;
@@ -1410,7 +1553,7 @@ struct iwl_mvm_d3_gtk_iter_data {
int num_keys;
};
-static void iwl_mvm_d3_update_gtks(struct ieee80211_hw *hw,
+static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key,
@@ -1451,7 +1594,8 @@ static void iwl_mvm_d3_update_gtks(struct ieee80211_hw *hw,
switch (key->cipher) {
case WLAN_CIPHER_SUITE_CCMP:
- iwl_mvm_set_aes_rx_seq(sc->aes.unicast_rsc, key);
+ iwl_mvm_set_aes_rx_seq(data->mvm, sc->aes.unicast_rsc,
+ sta, key);
atomic64_set(&key->tx_pn, le64_to_cpu(sc->aes.tsc.pn));
break;
case WLAN_CIPHER_SUITE_TKIP:
@@ -1474,7 +1618,7 @@ static void iwl_mvm_d3_update_gtks(struct ieee80211_hw *hw,
if (data->status->num_of_gtk_rekeys)
ieee80211_remove_key(key);
else if (data->last_gtk == key)
- iwl_mvm_set_key_rx_seq(key, data->status);
+ iwl_mvm_set_key_rx_seq(data->mvm, key, data->status);
}
static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
@@ -1483,6 +1627,7 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_d3_gtk_iter_data gtkdata = {
+ .mvm = mvm,
.status = status,
};
u32 disconnection_reasons =
@@ -1498,7 +1643,7 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
/* find last GTK that we used initially, if any */
gtkdata.find_phase = true;
ieee80211_iter_keys(mvm->hw, vif,
- iwl_mvm_d3_update_gtks, &gtkdata);
+ iwl_mvm_d3_update_keys, &gtkdata);
/* not trying to keep connections with MFP/unhandled ciphers */
if (gtkdata.unhandled_cipher)
return false;
@@ -1513,7 +1658,7 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
*/
gtkdata.find_phase = false;
ieee80211_iter_keys(mvm->hw, vif,
- iwl_mvm_d3_update_gtks, &gtkdata);
+ iwl_mvm_d3_update_keys, &gtkdata);
if (status->num_of_gtk_rekeys) {
struct ieee80211_key_conf *key;
@@ -1544,7 +1689,7 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
key = ieee80211_gtk_rekey_add(vif, &conf.conf);
if (IS_ERR(key))
return false;
- iwl_mvm_set_key_rx_seq(key, status);
+ iwl_mvm_set_key_rx_seq(mvm, key, status);
}
if (status->num_of_gtk_rekeys) {
@@ -1693,6 +1838,30 @@ out_unlock:
return false;
}
+void iwl_mvm_d0i3_update_keys(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_wowlan_status *status)
+{
+ struct iwl_mvm_d3_gtk_iter_data gtkdata = {
+ .mvm = mvm,
+ .status = status,
+ };
+
+ /*
+ * rekey handling requires taking locks that can't be taken now.
+ * however, d0i3 doesn't offload rekey, so we're fine.
+ */
+ if (WARN_ON_ONCE(status->num_of_gtk_rekeys))
+ return;
+
+ /* find last GTK that we used initially, if any */
+ gtkdata.find_phase = true;
+ iwl_mvm_iter_d0i3_ap_keys(mvm, vif, iwl_mvm_d3_update_keys, &gtkdata);
+
+ gtkdata.find_phase = false;
+ iwl_mvm_iter_d0i3_ap_keys(mvm, vif, iwl_mvm_d3_update_keys, &gtkdata);
+}
+
struct iwl_mvm_nd_query_results {
u32 matched_profiles;
struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES];
@@ -1951,8 +2120,9 @@ static int iwl_mvm_resume_d0i3(struct iwl_mvm *mvm)
{
bool exit_now;
enum iwl_d3_status d3_status;
+ struct iwl_trans *trans = mvm->trans;
- iwl_trans_d3_resume(mvm->trans, &d3_status, false);
+ iwl_trans_d3_resume(trans, &d3_status, false);
/*
* make sure to clear D0I3_DEFER_WAKEUP before
@@ -1969,9 +2139,9 @@ static int iwl_mvm_resume_d0i3(struct iwl_mvm *mvm)
_iwl_mvm_exit_d0i3(mvm);
}
- iwl_trans_resume(mvm->trans);
+ iwl_trans_resume(trans);
- if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) {
+ if (iwl_mvm_enter_d0i3_on_suspend(mvm)) {
int ret = iwl_mvm_exit_d0i3(mvm->hw->priv);
if (ret)
@@ -1987,12 +2157,16 @@ static int iwl_mvm_resume_d0i3(struct iwl_mvm *mvm)
int iwl_mvm_resume(struct ieee80211_hw *hw)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int ret;
- /* 'any' trigger means d0i3 was used */
- if (hw->wiphy->wowlan_config->any)
- return iwl_mvm_resume_d0i3(mvm);
+ if (mvm->trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3)
+ ret = iwl_mvm_resume_d0i3(mvm);
else
- return iwl_mvm_resume_d3(mvm);
+ ret = iwl_mvm_resume_d3(mvm);
+
+ mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED;
+
+ return ret;
}
void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled)
@@ -2016,6 +2190,8 @@ static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file)
ieee80211_stop_queues(mvm->hw);
synchronize_net();
+ mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_D3;
+
/* start pseudo D3 */
rtnl_lock();
err = __iwl_mvm_suspend(mvm->hw, mvm->hw->wiphy->wowlan_config, true);
@@ -2070,9 +2246,13 @@ static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file)
int remaining_time = 10;
mvm->d3_test_active = false;
+
rtnl_lock();
__iwl_mvm_resume(mvm, true);
rtnl_unlock();
+
+ mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED;
+
iwl_abort_notification_waits(&mvm->notif_wait);
ieee80211_restart_hw(mvm->hw);
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c
index 7904b41a04c6..9e0d46368cdd 100644
--- a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
index 05928fb4021d..90500e2d107b 100644
--- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -65,6 +65,7 @@
#include <linux/vmalloc.h>
#include "mvm.h"
+#include "fw-dbg.h"
#include "sta.h"
#include "iwl-io.h"
#include "debugfs.h"
@@ -512,6 +513,10 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
pos += scnprintf(buf+pos, bufsz-pos,
"antenna isolation = %d CORUN LUT index = %d\n",
mvm->last_ant_isol, mvm->last_corun_lut);
+ pos += scnprintf(buf + pos, bufsz - pos, "bt_rrc = %d\n",
+ notif->rrc_enabled);
+ pos += scnprintf(buf + pos, bufsz - pos, "bt_ttc = %d\n",
+ notif->ttc_enabled);
} else {
struct iwl_bt_coex_profile_notif *notif =
&mvm->last_bt_notif;
@@ -530,8 +535,19 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
pos += scnprintf(buf+pos, bufsz-pos,
"antenna isolation = %d CORUN LUT index = %d\n",
mvm->last_ant_isol, mvm->last_corun_lut);
+ pos += scnprintf(buf + pos, bufsz - pos, "bt_rrc = %d\n",
+ (notif->ttc_rrc_status >> 4) & 0xF);
+ pos += scnprintf(buf + pos, bufsz - pos, "bt_ttc = %d\n",
+ notif->ttc_rrc_status & 0xF);
}
+ pos += scnprintf(buf + pos, bufsz - pos, "sync_sco = %d\n",
+ IWL_MVM_BT_COEX_SYNC2SCO);
+ pos += scnprintf(buf + pos, bufsz - pos, "mplut = %d\n",
+ IWL_MVM_BT_COEX_MPLUT);
+ pos += scnprintf(buf + pos, bufsz - pos, "corunning = %d\n",
+ IWL_MVM_BT_COEX_CORUNNING);
+
mutex_unlock(&mvm->mutex);
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
@@ -945,6 +961,44 @@ static ssize_t iwl_dbgfs_fw_dbg_conf_read(struct file *file,
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
+/*
+ * Enable / Disable continuous recording.
+ * Cause the FW to start continuous recording, by sending the relevant hcmd.
+ * Enable: input of every integer larger than 0, ENABLE_CONT_RECORDING.
+ * Disable: for 0 as input, DISABLE_CONT_RECORDING.
+ */
+static ssize_t iwl_dbgfs_cont_recording_write(struct iwl_mvm *mvm,
+ char *buf, size_t count,
+ loff_t *ppos)
+{
+ struct iwl_trans *trans = mvm->trans;
+ const struct iwl_fw_dbg_dest_tlv *dest = trans->dbg_dest_tlv;
+ struct iwl_continuous_record_cmd cont_rec = {};
+ int ret, rec_mode;
+
+ if (!dest)
+ return -EOPNOTSUPP;
+
+ if (dest->monitor_mode != SMEM_MODE ||
+ trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ return -EOPNOTSUPP;
+
+ ret = kstrtouint(buf, 0, &rec_mode);
+ if (ret)
+ return ret;
+
+ cont_rec.record_mode.enable_recording = rec_mode ?
+ cpu_to_le16(ENABLE_CONT_RECORDING) :
+ cpu_to_le16(DISABLE_CONT_RECORDING);
+
+ mutex_lock(&mvm->mutex);
+ ret = iwl_mvm_send_cmd_pdu(mvm, LDBG_CONFIG_CMD, 0,
+ sizeof(cont_rec), &cont_rec);
+ mutex_unlock(&mvm->mutex);
+
+ return ret ?: count;
+}
+
static ssize_t iwl_dbgfs_fw_dbg_conf_write(struct iwl_mvm *mvm,
char *buf, size_t count,
loff_t *ppos)
@@ -975,7 +1029,8 @@ static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm,
if (ret)
return ret;
- iwl_mvm_fw_dbg_collect(mvm, FW_DBG_TRIGGER_USER, NULL, 0, NULL);
+ iwl_mvm_fw_dbg_collect(mvm, FW_DBG_TRIGGER_USER, buf,
+ (count - 1), NULL);
iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE);
@@ -1262,6 +1317,7 @@ static ssize_t iwl_dbgfs_d0i3_refs_read(struct file *file,
PRINT_MVM_REF(IWL_MVM_REF_EXIT_WORK);
PRINT_MVM_REF(IWL_MVM_REF_PROTECT_CSA);
PRINT_MVM_REF(IWL_MVM_REF_FW_DBG_COLLECT);
+ PRINT_MVM_REF(IWL_MVM_REF_INIT_UCODE);
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
@@ -1396,7 +1452,8 @@ MVM_DEBUGFS_WRITE_FILE_OPS(bt_force_ant, 10);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg_conf, 8);
-MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 8);
+MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 64);
+MVM_DEBUGFS_WRITE_FILE_OPS(cont_recording, 8);
#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256);
@@ -1440,6 +1497,7 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(send_echo_cmd, mvm->debugfs_dir, S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE(cont_recording, mvm->debugfs_dir, S_IWUSR);
if (!debugfs_create_bool("enable_scan_iteration_notif",
S_IRUSR | S_IWUSR,
mvm->debugfs_dir,
@@ -1476,10 +1534,6 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
goto err;
#endif
- if (!debugfs_create_u8("low_latency_agg_frame_limit", S_IRUSR | S_IWUSR,
- mvm->debugfs_dir,
- &mvm->low_latency_agg_frame_limit))
- goto err;
if (!debugfs_create_u8("ps_disabled", S_IRUSR,
mvm->debugfs_dir, &mvm->ps_disabled))
goto err;
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.h b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h
index 8c4190e7e027..ede6ef8d390e 100644
--- a/drivers/net/wireless/iwlwifi/mvm/debugfs.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.h
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h
index d398a6102805..2a33b694ba10 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-coex.h
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h
index 20521bebb0b1..62b9a0a96700 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h
@@ -7,6 +7,7 @@
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -26,7 +27,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -94,10 +95,14 @@ struct iwl_d3_manager_config {
* enum iwl_d3_proto_offloads - enabled protocol offloads
* @IWL_D3_PROTO_OFFLOAD_ARP: ARP data is enabled
* @IWL_D3_PROTO_OFFLOAD_NS: NS (Neighbor Solicitation) is enabled
+ * @IWL_D3_PROTO_IPV4_VALID: IPv4 data is valid
+ * @IWL_D3_PROTO_IPV6_VALID: IPv6 data is valid
*/
enum iwl_proto_offloads {
IWL_D3_PROTO_OFFLOAD_ARP = BIT(0),
IWL_D3_PROTO_OFFLOAD_NS = BIT(1),
+ IWL_D3_PROTO_IPV4_VALID = BIT(2),
+ IWL_D3_PROTO_IPV6_VALID = BIT(3),
};
#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1 2
@@ -241,6 +246,13 @@ enum iwl_wowlan_wakeup_filters {
IWL_WOWLAN_WAKEUP_BCN_FILTERING = BIT(16),
}; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */
+enum iwl_wowlan_flags {
+ IS_11W_ASSOC = BIT(0),
+ ENABLE_L3_FILTERING = BIT(1),
+ ENABLE_NBNS_FILTERING = BIT(2),
+ ENABLE_DHCP_FILTERING = BIT(3),
+};
+
struct iwl_wowlan_config_cmd {
__le32 wakeup_filter;
__le16 non_qos_seq;
@@ -248,8 +260,9 @@ struct iwl_wowlan_config_cmd {
u8 wowlan_ba_teardown_tids;
u8 is_11n_connection;
u8 offloading_tid;
- u8 reserved[3];
-} __packed; /* WOWLAN_CONFIG_API_S_VER_3 */
+ u8 flags;
+ u8 reserved[2];
+} __packed; /* WOWLAN_CONFIG_API_S_VER_4 */
/*
* WOWLAN_TSC_RSC_PARAMS
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h
index f3f3ee0a766b..95ac59d088b1 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h
index c8f3e2536cbb..65a7c8a4cacf 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h
@@ -27,7 +27,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h
index 0f1ea80a55ef..ad9cc03e16c4 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h
index 9b7e49d4620f..fb6d341d6f3d 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-rx.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h
@@ -27,7 +27,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -68,6 +68,8 @@
#ifndef __fw_api_rx_h__
#define __fw_api_rx_h__
+/* API for pre-9000 hardware */
+
#define IWL_RX_INFO_PHY_CNT 8
#define IWL_RX_INFO_ENERGY_ANT_ABC_IDX 1
#define IWL_RX_INFO_ENERGY_ANT_A_MSK 0x000000ff
@@ -77,6 +79,11 @@
#define IWL_RX_INFO_ENERGY_ANT_B_POS 8
#define IWL_RX_INFO_ENERGY_ANT_C_POS 16
+enum iwl_mac_context_info {
+ MAC_CONTEXT_INFO_NONE,
+ MAC_CONTEXT_INFO_GSCAN,
+};
+
/**
* struct iwl_rx_phy_info - phy info
* (REPLY_RX_PHY_CMD = 0xc0)
@@ -95,6 +102,8 @@
* @frame_time: frame's time on the air, based on byte count and frame rate
* calculation
* @mac_active_msk: what MACs were active when the frame was received
+ * @mac_context_info: additional info on the context in which the frame was
+ * received as defined in &enum iwl_mac_context_info
*
* Before each Rx, the device sends this data. It contains PHY information
* about the reception of the packet.
@@ -112,7 +121,8 @@ struct iwl_rx_phy_info {
__le32 non_cfg_phy[IWL_RX_INFO_PHY_CNT];
__le32 rate_n_flags;
__le32 byte_count;
- __le16 mac_active_msk;
+ u8 mac_active_msk;
+ u8 mac_context_info;
__le16 frame_time;
} __packed;
@@ -229,10 +239,130 @@ enum iwl_mvm_rx_status {
RX_MPDU_RES_STATUS_CSUM_DONE = BIT(16),
RX_MPDU_RES_STATUS_CSUM_OK = BIT(17),
RX_MPDU_RES_STATUS_HASH_INDEX_MSK = (0x3F0000),
- RX_MPDU_RES_STATUS_STA_ID_MSK = (0x1f000000),
+ RX_MDPU_RES_STATUS_STA_ID_SHIFT = 24,
+ RX_MPDU_RES_STATUS_STA_ID_MSK = 0x1f << RX_MDPU_RES_STATUS_STA_ID_SHIFT,
RX_MPDU_RES_STATUS_RRF_KILL = BIT(29),
RX_MPDU_RES_STATUS_FILTERING_MSK = (0xc00000),
RX_MPDU_RES_STATUS2_FILTERING_MSK = (0xc0000000),
};
+/* 9000 series API */
+enum iwl_rx_mpdu_mac_flags1 {
+ IWL_RX_MDPU_MFLG1_ADDRTYPE_MASK = 0x03,
+ IWL_RX_MPDU_MFLG1_MIC_CRC_LEN_MASK = 0xf0,
+ /* shift should be 4, but the length is measured in 2-byte
+ * words, so shifting only by 3 gives a byte result
+ */
+ IWL_RX_MPDU_MFLG1_MIC_CRC_LEN_SHIFT = 3,
+};
+
+enum iwl_rx_mpdu_mac_flags2 {
+ /* in 2-byte words */
+ IWL_RX_MPDU_MFLG2_HDR_LEN_MASK = 0x1f,
+ IWL_RX_MPDU_MFLG2_PAD = 0x20,
+ IWL_RX_MPDU_MFLG2_AMSDU = 0x40,
+};
+
+enum iwl_rx_mpdu_amsdu_info {
+ IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK = 0x3f,
+ IWL_RX_MPDU_AMSDU_LAST_SUBFRAME = 0x40,
+ /* 0x80 bit reserved for now */
+};
+
+enum iwl_rx_l3l4_flags {
+ IWL_RX_L3L4_IP_HDR_CSUM_OK = BIT(0),
+ IWL_RX_L3L4_TCP_UDP_CSUM_OK = BIT(1),
+ IWL_RX_L3L4_TCP_FIN_SYN_RST_PSH = BIT(2),
+ IWL_RX_L3L4_TCP_ACK = BIT(3),
+ IWL_RX_L3L4_L3_PROTO_MASK = 0xf << 4,
+ IWL_RX_L3L4_L4_PROTO_MASK = 0xf << 8,
+ IWL_RX_L3L4_RSS_HASH_MASK = 0xf << 12,
+};
+
+enum iwl_rx_mpdu_status {
+ IWL_RX_MPDU_STATUS_CRC_OK = BIT(0),
+ IWL_RX_MPDU_STATUS_OVERRUN_OK = BIT(1),
+ IWL_RX_MPDU_STATUS_SRC_STA_FOUND = BIT(2),
+ IWL_RX_MPDU_STATUS_KEY_VALID = BIT(3),
+ IWL_RX_MPDU_STATUS_KEY_ERROR = BIT(4),
+ IWL_RX_MPDU_STATUS_ICV_OK = BIT(5),
+ IWL_RX_MPDU_STATUS_MIC_OK = BIT(6),
+ /* TODO - verify this is the correct value */
+ IWL_RX_MPDU_RES_STATUS_TTAK_OK = BIT(7),
+ IWL_RX_MPDU_STATUS_SEC_MASK = 0x7 << 8,
+ IWL_RX_MPDU_STATUS_SEC_NONE = 0x0 << 8,
+ IWL_RX_MPDU_STATUS_SEC_WEP = 0x1 << 8,
+ IWL_RX_MPDU_STATUS_SEC_CCM = 0x2 << 8,
+ IWL_RX_MPDU_STATUS_SEC_TKIP = 0x3 << 8,
+ /* TODO - define IWL_RX_MPDU_STATUS_SEC_EXT_ENC - this is a stub */
+ IWL_RX_MPDU_STATUS_SEC_EXT_ENC = 0x4 << 8,
+ /* TODO - define IWL_RX_MPDU_STATUS_SEC_GCM - this is a stub */
+ IWL_RX_MPDU_STATUS_SEC_GCM = 0x5 << 8,
+ IWL_RX_MPDU_STATUS_DECRYPTED = BIT(11),
+ IWL_RX_MPDU_STATUS_WEP_MATCH = BIT(12),
+ IWL_RX_MPDU_STATUS_EXT_IV_MATCH = BIT(13),
+ IWL_RX_MPDU_STATUS_KEY_ID_MATCH = BIT(14),
+ IWL_RX_MPDU_STATUS_KEY_COLOR = BIT(15),
+};
+
+enum iwl_rx_mpdu_hash_filter {
+ IWL_RX_MPDU_HF_A1_HASH_MASK = 0x3f,
+ IWL_RX_MPDU_HF_FILTER_STATUS_MASK = 0xc0,
+};
+
+enum iwl_rx_mpdu_sta_id_flags {
+ IWL_RX_MPDU_SIF_STA_ID_MASK = 0x1f,
+ IWL_RX_MPDU_SIF_RRF_ABORT = 0x20,
+ IWL_RX_MPDU_SIF_FILTER_STATUS_MASK = 0xc0,
+};
+
+#define IWL_RX_REORDER_DATA_INVALID_BAID 0x7f
+
+enum iwl_rx_mpdu_reorder_data {
+ IWL_RX_MPDU_REORDER_NSSN_MASK = 0x00000fff,
+ IWL_RX_MPDU_REORDER_SN_MASK = 0x00fff000,
+ IWL_RX_MPDU_REORDER_SN_SHIFT = 12,
+ IWL_RX_MPDU_REORDER_BAID_MASK = 0x7f000000,
+ IWL_RX_MPDU_REORDER_BAID_SHIFT = 24,
+ IWL_RX_MPDU_REORDER_BA_OLD_SN = 0x80000000,
+};
+
+struct iwl_rx_mpdu_desc {
+ /* DW2 */
+ __le16 mpdu_len;
+ u8 mac_flags1;
+ u8 mac_flags2;
+ /* DW3 */
+ u8 amsdu_info;
+ __le16 reserved_for_software;
+ u8 mac_phy_idx;
+ /* DW4 */
+ __le16 raw_csum; /* alledgedly unreliable */
+ __le16 l3l4_flags;
+ /* DW5 */
+ __le16 status;
+ u8 hash_filter;
+ u8 sta_id_flags;
+ /* DW6 */
+ __le32 reorder_data;
+ /* DW7 */
+ __le32 rss_hash;
+ /* DW8 */
+ __le32 filter_match;
+ /* DW9 */
+ __le32 gp2_on_air_rise;
+ /* DW10 */
+ __le32 rate_n_flags;
+ /* DW11 */
+ u8 energy_a, energy_b, energy_c, channel;
+ /* DW12 & DW13 */
+ __le64 tsf_on_air_rise;
+} __packed;
+
+struct iwl_frame_release {
+ u8 baid;
+ u8 reserved;
+ __le16 nssn;
+};
+
#endif /* __fw_api_rx_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h
index 3a657e4b60ac..f01dab0d0dac 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -285,6 +285,8 @@ struct iwl_scan_channel_opt {
* @IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED: all passive scans will be fragmented
* @IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED: insert WFA vendor-specific TPC report
* and DS parameter set IEs into probe requests.
+ * @IWL_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL: use extended dwell time on channels
+ * 1, 6 and 11.
* @IWL_MVM_LMAC_SCAN_FLAG_MATCH: Send match found notification on matches
*/
enum iwl_mvm_lmac_scan_flags {
@@ -295,6 +297,7 @@ enum iwl_mvm_lmac_scan_flags {
IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS = BIT(4),
IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED = BIT(5),
IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED = BIT(6),
+ IWL_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL = BIT(7),
IWL_MVM_LMAC_SCAN_FLAG_MATCH = BIT(9),
};
@@ -322,6 +325,7 @@ enum iwl_scan_priority_ext {
* @active-dwell: dwell time for active channels
* @passive-dwell: dwell time for passive channels
* @fragmented-dwell: dwell time for fragmented passive scan
+ * @extended_dwell: dwell time for channels 1, 6 and 11 (in certain cases)
* @reserved2: for alignment and future use
* @rx_chain_selct: PHY_RX_CHAIN_* flags
* @scan_flags: &enum iwl_mvm_lmac_scan_flags
@@ -346,7 +350,8 @@ struct iwl_scan_req_lmac {
u8 active_dwell;
u8 passive_dwell;
u8 fragmented_dwell;
- __le16 reserved2;
+ u8 extended_dwell;
+ u8 reserved2;
__le16 rx_chain_select;
__le32 scan_flags;
__le32 max_out_time;
@@ -490,7 +495,7 @@ enum iwl_channel_flags {
* @dwell_active: default dwell time for active scan
* @dwell_passive: default dwell time for passive scan
* @dwell_fragmented: default dwell time for fragmented scan
- * @reserved: for future use and alignment
+ * @dwell_extended: default dwell time for channels 1, 6 and 11
* @mac_addr: default mac address to be used in probes
* @bcast_sta_id: the index of the station in the fw
* @channel_flags: default channel flags - enum iwl_channel_flags
@@ -507,7 +512,7 @@ struct iwl_scan_config {
u8 dwell_active;
u8 dwell_passive;
u8 dwell_fragmented;
- u8 reserved;
+ u8 dwell_extended;
u8 mac_addr[ETH_ALEN];
u8 bcast_sta_id;
u8 channel_flags;
@@ -543,7 +548,8 @@ enum iwl_umac_scan_general_flags {
IWL_UMAC_SCAN_GEN_FLAGS_MULTIPLE_SSID = BIT(6),
IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED = BIT(7),
IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED = BIT(8),
- IWL_UMAC_SCAN_GEN_FLAGS_MATCH = BIT(9)
+ IWL_UMAC_SCAN_GEN_FLAGS_MATCH = BIT(9),
+ IWL_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL = BIT(10),
};
/**
@@ -597,7 +603,7 @@ struct iwl_scan_req_umac_tail {
* @uid: scan id, &enum iwl_umac_scan_uid_offsets
* @ooc_priority: out of channel priority - &enum iwl_scan_priority
* @general_flags: &enum iwl_umac_scan_general_flags
- * @reserved1: for future use and alignment
+ * @extended_dwell: dwell time for channels 1, 6 and 11
* @active_dwell: dwell time for active scan
* @passive_dwell: dwell time for passive scan
* @fragmented_dwell: dwell time for fragmented passive scan
@@ -606,7 +612,7 @@ struct iwl_scan_req_umac_tail {
* @scan_priority: scan internal prioritization &enum iwl_scan_priority
* @channel_flags: &enum iwl_scan_channel_flags
* @n_channels: num of channels in scan request
- * @reserved2: for future use and alignment
+ * @reserved: for future use and alignment
* @data: &struct iwl_scan_channel_cfg_umac and
* &struct iwl_scan_req_umac_tail
*/
@@ -616,7 +622,7 @@ struct iwl_scan_req_umac {
__le32 ooc_priority;
/* SCAN_GENERAL_PARAMS_API_S_VER_1 */
__le32 general_flags;
- u8 reserved1;
+ u8 extended_dwell;
u8 active_dwell;
u8 passive_dwell;
u8 fragmented_dwell;
@@ -626,7 +632,7 @@ struct iwl_scan_req_umac {
/* SCAN_CHANNEL_PARAMS_API_S_VER_1 */
u8 channel_flags;
u8 n_channels;
- __le16 reserved2;
+ __le16 reserved;
u8 data[];
} __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_1 */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h
index 493a8bdfbc9e..6fca4fb1d306 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h
index 0c321f63ee42..438665a54923 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-stats.h
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tof.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tof.h
index eed6271d01a3..86aa51b2210e 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tof.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tof.h
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h
index 853698ab8b05..0036d18334af 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
index 181590fbd3b3..82049bb139c2 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
@@ -7,6 +7,7 @@
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -26,13 +27,14 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -213,6 +215,7 @@ enum {
REPLY_RX_PHY_CMD = 0xc0,
REPLY_RX_MPDU_CMD = 0xc1,
+ FRAME_RELEASE = 0xc3,
BA_NOTIF = 0xc5,
/* Location Aware Regulatory */
@@ -239,6 +242,7 @@ enum {
DTS_MEASUREMENT_NOTIFICATION = 0xdd,
REPLY_DEBUG_CMD = 0xf0,
+ LDBG_CONFIG_CMD = 0xf6,
DEBUG_LOG_MSG = 0xf7,
BCAST_FILTER_CMD = 0xcf,
@@ -268,6 +272,9 @@ enum {
REPLY_MAX = 0xff,
};
+/* Please keep this enum *SORTED* by hex value.
+ * Needed for binary search, otherwise a warning will be triggered.
+ */
enum iwl_phy_ops_subcmd_ids {
CMD_DTS_MEASUREMENT_TRIGGER_WIDE = 0x0,
DTS_MEASUREMENT_NOTIF_WIDE = 0xFF,
@@ -275,6 +282,8 @@ enum iwl_phy_ops_subcmd_ids {
/* command groups */
enum {
+ LEGACY_GROUP = 0x0,
+ LONG_GROUP = 0x1,
PHY_OPS_GROUP = 0x4,
};
@@ -426,6 +435,26 @@ struct iwl_fw_get_item_cmd {
__le32 item_id;
} __packed; /* FW_GET_ITEM_CMD_API_S_VER_1 */
+#define CONT_REC_COMMAND_SIZE 80
+#define ENABLE_CONT_RECORDING 0x15
+#define DISABLE_CONT_RECORDING 0x16
+
+/*
+ * struct iwl_continuous_record_mode - recording mode
+ */
+struct iwl_continuous_record_mode {
+ __le16 enable_recording;
+} __packed;
+
+/*
+ * struct iwl_continuous_record_cmd - enable/disable continuous recording
+ */
+struct iwl_continuous_record_cmd {
+ struct iwl_continuous_record_mode record_mode;
+ u8 pad[CONT_REC_COMMAND_SIZE -
+ sizeof(struct iwl_continuous_record_mode)];
+} __packed;
+
struct iwl_fw_get_item_resp {
__le32 item_id;
__le32 item_byte_cnt;
@@ -1425,6 +1454,22 @@ struct iwl_sf_cfg_cmd {
***********************************/
/**
+ * struct iwl_mcc_update_cmd_v1 - Request the device to update geographic
+ * regulatory profile according to the given MCC (Mobile Country Code).
+ * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain.
+ * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the
+ * MCC in the cmd response will be the relevant MCC in the NVM.
+ * @mcc: given mobile country code
+ * @source_id: the source from where we got the MCC, see iwl_mcc_source
+ * @reserved: reserved for alignment
+ */
+struct iwl_mcc_update_cmd_v1 {
+ __le16 mcc;
+ u8 source_id;
+ u8 reserved;
+} __packed; /* LAR_UPDATE_MCC_CMD_API_S_VER_1 */
+
+/**
* struct iwl_mcc_update_cmd - Request the device to update geographic
* regulatory profile according to the given MCC (Mobile Country Code).
* The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain.
@@ -1433,12 +1478,39 @@ struct iwl_sf_cfg_cmd {
* @mcc: given mobile country code
* @source_id: the source from where we got the MCC, see iwl_mcc_source
* @reserved: reserved for alignment
+ * @key: integrity key for MCC API OEM testing
+ * @reserved2: reserved
*/
struct iwl_mcc_update_cmd {
__le16 mcc;
u8 source_id;
u8 reserved;
-} __packed; /* LAR_UPDATE_MCC_CMD_API_S */
+ __le32 key;
+ __le32 reserved2[5];
+} __packed; /* LAR_UPDATE_MCC_CMD_API_S_VER_2 */
+
+/**
+ * iwl_mcc_update_resp_v1 - response to MCC_UPDATE_CMD.
+ * Contains the new channel control profile map, if changed, and the new MCC
+ * (mobile country code).
+ * The new MCC may be different than what was requested in MCC_UPDATE_CMD.
+ * @status: see &enum iwl_mcc_update_status
+ * @mcc: the new applied MCC
+ * @cap: capabilities for all channels which matches the MCC
+ * @source_id: the MCC source, see iwl_mcc_source
+ * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51
+ * channels, depending on platform)
+ * @channels: channel control data map, DWORD for each channel. Only the first
+ * 16bits are used.
+ */
+struct iwl_mcc_update_resp_v1 {
+ __le32 status;
+ __le16 mcc;
+ u8 cap;
+ u8 source_id;
+ __le32 n_channels;
+ __le32 channels[0];
+} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_1 */
/**
* iwl_mcc_update_resp - response to MCC_UPDATE_CMD.
@@ -1449,6 +1521,8 @@ struct iwl_mcc_update_cmd {
* @mcc: the new applied MCC
* @cap: capabilities for all channels which matches the MCC
* @source_id: the MCC source, see iwl_mcc_source
+ * @time: time elapsed from the MCC test start (in 30 seconds TU)
+ * @reserved: reserved.
* @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51
* channels, depending on platform)
* @channels: channel control data map, DWORD for each channel. Only the first
@@ -1459,9 +1533,11 @@ struct iwl_mcc_update_resp {
__le16 mcc;
u8 cap;
u8 source_id;
+ __le16 time;
+ __le16 reserved;
__le32 n_channels;
__le32 channels[0];
-} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S */
+} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_2 */
/**
* struct iwl_mcc_chub_notif - chub notifies of mcc change
@@ -1491,6 +1567,9 @@ enum iwl_mcc_update_status {
MCC_RESP_NVM_DISABLED,
MCC_RESP_ILLEGAL,
MCC_RESP_LOW_PRIORITY,
+ MCC_RESP_TEST_MODE_ACTIVE,
+ MCC_RESP_TEST_MODE_NOT_ACTIVE,
+ MCC_RESP_TEST_MODE_DENIAL_OF_SERVICE,
};
enum iwl_mcc_source {
@@ -1503,7 +1582,9 @@ enum iwl_mcc_source {
MCC_SOURCE_RESERVED = 6,
MCC_SOURCE_DEFAULT = 7,
MCC_SOURCE_UNINITIALIZED = 8,
- MCC_SOURCE_GET_CURRENT = 0x10
+ MCC_SOURCE_MCC_API = 9,
+ MCC_SOURCE_GET_CURRENT = 0x10,
+ MCC_SOURCE_GETTING_MCC_TEST_MODE = 0x11,
};
/* DTS measurements */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c
new file mode 100644
index 000000000000..0813f8184e10
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c
@@ -0,0 +1,817 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program;
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <linux/devcoredump.h>
+
+#include "fw-dbg.h"
+#include "iwl-io.h"
+#include "mvm.h"
+#include "iwl-prph.h"
+#include "iwl-csr.h"
+
+static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count,
+ const void *data, size_t datalen)
+{
+ const struct iwl_mvm_dump_ptrs *dump_ptrs = data;
+ ssize_t bytes_read;
+ ssize_t bytes_read_trans;
+
+ if (offset < dump_ptrs->op_mode_len) {
+ bytes_read = min_t(ssize_t, count,
+ dump_ptrs->op_mode_len - offset);
+ memcpy(buffer, (u8 *)dump_ptrs->op_mode_ptr + offset,
+ bytes_read);
+ offset += bytes_read;
+ count -= bytes_read;
+
+ if (count == 0)
+ return bytes_read;
+ } else {
+ bytes_read = 0;
+ }
+
+ if (!dump_ptrs->trans_ptr)
+ return bytes_read;
+
+ offset -= dump_ptrs->op_mode_len;
+ bytes_read_trans = min_t(ssize_t, count,
+ dump_ptrs->trans_ptr->len - offset);
+ memcpy(buffer + bytes_read,
+ (u8 *)dump_ptrs->trans_ptr->data + offset,
+ bytes_read_trans);
+
+ return bytes_read + bytes_read_trans;
+}
+
+static void iwl_mvm_free_coredump(const void *data)
+{
+ const struct iwl_mvm_dump_ptrs *fw_error_dump = data;
+
+ vfree(fw_error_dump->op_mode_ptr);
+ vfree(fw_error_dump->trans_ptr);
+ kfree(fw_error_dump);
+}
+
+#define RADIO_REG_MAX_READ 0x2ad
+static void iwl_mvm_read_radio_reg(struct iwl_mvm *mvm,
+ struct iwl_fw_error_dump_data **dump_data)
+{
+ u8 *pos = (void *)(*dump_data)->data;
+ unsigned long flags;
+ int i;
+
+ if (!iwl_trans_grab_nic_access(mvm->trans, &flags))
+ return;
+
+ (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RADIO_REG);
+ (*dump_data)->len = cpu_to_le32(RADIO_REG_MAX_READ);
+
+ for (i = 0; i < RADIO_REG_MAX_READ; i++) {
+ u32 rd_cmd = RADIO_RSP_RD_CMD;
+
+ rd_cmd |= i << RADIO_RSP_ADDR_POS;
+ iwl_write_prph_no_grab(mvm->trans, RSP_RADIO_CMD, rd_cmd);
+ *pos = (u8)iwl_read_prph_no_grab(mvm->trans, RSP_RADIO_RDDAT);
+
+ pos++;
+ }
+
+ *dump_data = iwl_fw_error_next_data(*dump_data);
+
+ iwl_trans_release_nic_access(mvm->trans, &flags);
+}
+
+static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm,
+ struct iwl_fw_error_dump_data **dump_data)
+{
+ struct iwl_fw_error_dump_fifo *fifo_hdr;
+ u32 *fifo_data;
+ u32 fifo_len;
+ unsigned long flags;
+ int i, j;
+
+ if (!iwl_trans_grab_nic_access(mvm->trans, &flags))
+ return;
+
+ /* Pull RXF data from all RXFs */
+ for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) {
+ /*
+ * Keep aside the additional offset that might be needed for
+ * next RXF
+ */
+ u32 offset_diff = RXF_DIFF_FROM_PREV * i;
+
+ fifo_hdr = (void *)(*dump_data)->data;
+ fifo_data = (void *)fifo_hdr->data;
+ fifo_len = mvm->shared_mem_cfg.rxfifo_size[i];
+
+ /* No need to try to read the data if the length is 0 */
+ if (fifo_len == 0)
+ continue;
+
+ /* Add a TLV for the RXF */
+ (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
+ (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr));
+
+ fifo_hdr->fifo_num = cpu_to_le32(i);
+ fifo_hdr->available_bytes =
+ cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+ RXF_RD_D_SPACE +
+ offset_diff));
+ fifo_hdr->wr_ptr =
+ cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+ RXF_RD_WR_PTR +
+ offset_diff));
+ fifo_hdr->rd_ptr =
+ cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+ RXF_RD_RD_PTR +
+ offset_diff));
+ fifo_hdr->fence_ptr =
+ cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+ RXF_RD_FENCE_PTR +
+ offset_diff));
+ fifo_hdr->fence_mode =
+ cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+ RXF_SET_FENCE_MODE +
+ offset_diff));
+
+ /* Lock fence */
+ iwl_trans_write_prph(mvm->trans,
+ RXF_SET_FENCE_MODE + offset_diff, 0x1);
+ /* Set fence pointer to the same place like WR pointer */
+ iwl_trans_write_prph(mvm->trans,
+ RXF_LD_WR2FENCE + offset_diff, 0x1);
+ /* Set fence offset */
+ iwl_trans_write_prph(mvm->trans,
+ RXF_LD_FENCE_OFFSET_ADDR + offset_diff,
+ 0x0);
+
+ /* Read FIFO */
+ fifo_len /= sizeof(u32); /* Size in DWORDS */
+ for (j = 0; j < fifo_len; j++)
+ fifo_data[j] = iwl_trans_read_prph(mvm->trans,
+ RXF_FIFO_RD_FENCE_INC +
+ offset_diff);
+ *dump_data = iwl_fw_error_next_data(*dump_data);
+ }
+
+ /* Pull TXF data from all TXFs */
+ for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) {
+ /* Mark the number of TXF we're pulling now */
+ iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i);
+
+ fifo_hdr = (void *)(*dump_data)->data;
+ fifo_data = (void *)fifo_hdr->data;
+ fifo_len = mvm->shared_mem_cfg.txfifo_size[i];
+
+ /* No need to try to read the data if the length is 0 */
+ if (fifo_len == 0)
+ continue;
+
+ /* Add a TLV for the FIFO */
+ (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF);
+ (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr));
+
+ fifo_hdr->fifo_num = cpu_to_le32(i);
+ fifo_hdr->available_bytes =
+ cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+ TXF_FIFO_ITEM_CNT));
+ fifo_hdr->wr_ptr =
+ cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+ TXF_WR_PTR));
+ fifo_hdr->rd_ptr =
+ cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+ TXF_RD_PTR));
+ fifo_hdr->fence_ptr =
+ cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+ TXF_FENCE_PTR));
+ fifo_hdr->fence_mode =
+ cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+ TXF_LOCK_FENCE));
+
+ /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */
+ iwl_trans_write_prph(mvm->trans, TXF_READ_MODIFY_ADDR,
+ TXF_WR_PTR);
+
+ /* Dummy-read to advance the read pointer to the head */
+ iwl_trans_read_prph(mvm->trans, TXF_READ_MODIFY_DATA);
+
+ /* Read FIFO */
+ fifo_len /= sizeof(u32); /* Size in DWORDS */
+ for (j = 0; j < fifo_len; j++)
+ fifo_data[j] = iwl_trans_read_prph(mvm->trans,
+ TXF_READ_MODIFY_DATA);
+ *dump_data = iwl_fw_error_next_data(*dump_data);
+ }
+
+ iwl_trans_release_nic_access(mvm->trans, &flags);
+}
+
+void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm)
+{
+ if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert)
+ return;
+
+ kfree(mvm->fw_dump_desc);
+ mvm->fw_dump_desc = NULL;
+}
+
+#define IWL8260_ICCM_OFFSET 0x44000 /* Only for B-step */
+#define IWL8260_ICCM_LEN 0xC000 /* Only for B-step */
+
+static const struct {
+ u32 start, end;
+} iwl_prph_dump_addr[] = {
+ { .start = 0x00a00000, .end = 0x00a00000 },
+ { .start = 0x00a0000c, .end = 0x00a00024 },
+ { .start = 0x00a0002c, .end = 0x00a0003c },
+ { .start = 0x00a00410, .end = 0x00a00418 },
+ { .start = 0x00a00420, .end = 0x00a00420 },
+ { .start = 0x00a00428, .end = 0x00a00428 },
+ { .start = 0x00a00430, .end = 0x00a0043c },
+ { .start = 0x00a00444, .end = 0x00a00444 },
+ { .start = 0x00a004c0, .end = 0x00a004cc },
+ { .start = 0x00a004d8, .end = 0x00a004d8 },
+ { .start = 0x00a004e0, .end = 0x00a004f0 },
+ { .start = 0x00a00840, .end = 0x00a00840 },
+ { .start = 0x00a00850, .end = 0x00a00858 },
+ { .start = 0x00a01004, .end = 0x00a01008 },
+ { .start = 0x00a01010, .end = 0x00a01010 },
+ { .start = 0x00a01018, .end = 0x00a01018 },
+ { .start = 0x00a01024, .end = 0x00a01024 },
+ { .start = 0x00a0102c, .end = 0x00a01034 },
+ { .start = 0x00a0103c, .end = 0x00a01040 },
+ { .start = 0x00a01048, .end = 0x00a01094 },
+ { .start = 0x00a01c00, .end = 0x00a01c20 },
+ { .start = 0x00a01c58, .end = 0x00a01c58 },
+ { .start = 0x00a01c7c, .end = 0x00a01c7c },
+ { .start = 0x00a01c28, .end = 0x00a01c54 },
+ { .start = 0x00a01c5c, .end = 0x00a01c5c },
+ { .start = 0x00a01c60, .end = 0x00a01cdc },
+ { .start = 0x00a01ce0, .end = 0x00a01d0c },
+ { .start = 0x00a01d18, .end = 0x00a01d20 },
+ { .start = 0x00a01d2c, .end = 0x00a01d30 },
+ { .start = 0x00a01d40, .end = 0x00a01d5c },
+ { .start = 0x00a01d80, .end = 0x00a01d80 },
+ { .start = 0x00a01d98, .end = 0x00a01d9c },
+ { .start = 0x00a01da8, .end = 0x00a01da8 },
+ { .start = 0x00a01db8, .end = 0x00a01df4 },
+ { .start = 0x00a01dc0, .end = 0x00a01dfc },
+ { .start = 0x00a01e00, .end = 0x00a01e2c },
+ { .start = 0x00a01e40, .end = 0x00a01e60 },
+ { .start = 0x00a01e68, .end = 0x00a01e6c },
+ { .start = 0x00a01e74, .end = 0x00a01e74 },
+ { .start = 0x00a01e84, .end = 0x00a01e90 },
+ { .start = 0x00a01e9c, .end = 0x00a01ec4 },
+ { .start = 0x00a01ed0, .end = 0x00a01ee0 },
+ { .start = 0x00a01f00, .end = 0x00a01f1c },
+ { .start = 0x00a01f44, .end = 0x00a01ffc },
+ { .start = 0x00a02000, .end = 0x00a02048 },
+ { .start = 0x00a02068, .end = 0x00a020f0 },
+ { .start = 0x00a02100, .end = 0x00a02118 },
+ { .start = 0x00a02140, .end = 0x00a0214c },
+ { .start = 0x00a02168, .end = 0x00a0218c },
+ { .start = 0x00a021c0, .end = 0x00a021c0 },
+ { .start = 0x00a02400, .end = 0x00a02410 },
+ { .start = 0x00a02418, .end = 0x00a02420 },
+ { .start = 0x00a02428, .end = 0x00a0242c },
+ { .start = 0x00a02434, .end = 0x00a02434 },
+ { .start = 0x00a02440, .end = 0x00a02460 },
+ { .start = 0x00a02468, .end = 0x00a024b0 },
+ { .start = 0x00a024c8, .end = 0x00a024cc },
+ { .start = 0x00a02500, .end = 0x00a02504 },
+ { .start = 0x00a0250c, .end = 0x00a02510 },
+ { .start = 0x00a02540, .end = 0x00a02554 },
+ { .start = 0x00a02580, .end = 0x00a025f4 },
+ { .start = 0x00a02600, .end = 0x00a0260c },
+ { .start = 0x00a02648, .end = 0x00a02650 },
+ { .start = 0x00a02680, .end = 0x00a02680 },
+ { .start = 0x00a026c0, .end = 0x00a026d0 },
+ { .start = 0x00a02700, .end = 0x00a0270c },
+ { .start = 0x00a02804, .end = 0x00a02804 },
+ { .start = 0x00a02818, .end = 0x00a0281c },
+ { .start = 0x00a02c00, .end = 0x00a02db4 },
+ { .start = 0x00a02df4, .end = 0x00a02fb0 },
+ { .start = 0x00a03000, .end = 0x00a03014 },
+ { .start = 0x00a0301c, .end = 0x00a0302c },
+ { .start = 0x00a03034, .end = 0x00a03038 },
+ { .start = 0x00a03040, .end = 0x00a03048 },
+ { .start = 0x00a03060, .end = 0x00a03068 },
+ { .start = 0x00a03070, .end = 0x00a03074 },
+ { .start = 0x00a0307c, .end = 0x00a0307c },
+ { .start = 0x00a03080, .end = 0x00a03084 },
+ { .start = 0x00a0308c, .end = 0x00a03090 },
+ { .start = 0x00a03098, .end = 0x00a03098 },
+ { .start = 0x00a030a0, .end = 0x00a030a0 },
+ { .start = 0x00a030a8, .end = 0x00a030b4 },
+ { .start = 0x00a030bc, .end = 0x00a030bc },
+ { .start = 0x00a030c0, .end = 0x00a0312c },
+ { .start = 0x00a03c00, .end = 0x00a03c5c },
+ { .start = 0x00a04400, .end = 0x00a04454 },
+ { .start = 0x00a04460, .end = 0x00a04474 },
+ { .start = 0x00a044c0, .end = 0x00a044ec },
+ { .start = 0x00a04500, .end = 0x00a04504 },
+ { .start = 0x00a04510, .end = 0x00a04538 },
+ { .start = 0x00a04540, .end = 0x00a04548 },
+ { .start = 0x00a04560, .end = 0x00a0457c },
+ { .start = 0x00a04590, .end = 0x00a04598 },
+ { .start = 0x00a045c0, .end = 0x00a045f4 },
+ { .start = 0x00a44000, .end = 0x00a7bf80 },
+};
+
+static u32 iwl_dump_prph(struct iwl_trans *trans,
+ struct iwl_fw_error_dump_data **data)
+{
+ struct iwl_fw_error_dump_prph *prph;
+ unsigned long flags;
+ u32 prph_len = 0, i;
+
+ if (!iwl_trans_grab_nic_access(trans, &flags))
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) {
+ /* The range includes both boundaries */
+ int num_bytes_in_chunk = iwl_prph_dump_addr[i].end -
+ iwl_prph_dump_addr[i].start + 4;
+ int reg;
+ __le32 *val;
+
+ prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk;
+
+ (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH);
+ (*data)->len = cpu_to_le32(sizeof(*prph) +
+ num_bytes_in_chunk);
+ prph = (void *)(*data)->data;
+ prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start);
+ val = (void *)prph->data;
+
+ for (reg = iwl_prph_dump_addr[i].start;
+ reg <= iwl_prph_dump_addr[i].end;
+ reg += 4)
+ *val++ = cpu_to_le32(iwl_read_prph_no_grab(trans,
+ reg));
+
+ *data = iwl_fw_error_next_data(*data);
+ }
+
+ iwl_trans_release_nic_access(trans, &flags);
+
+ return prph_len;
+}
+
+void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
+{
+ struct iwl_fw_error_dump_file *dump_file;
+ struct iwl_fw_error_dump_data *dump_data;
+ struct iwl_fw_error_dump_info *dump_info;
+ struct iwl_fw_error_dump_mem *dump_mem;
+ struct iwl_fw_error_dump_trigger_desc *dump_trig;
+ struct iwl_mvm_dump_ptrs *fw_error_dump;
+ u32 sram_len, sram_ofs;
+ u32 file_len, fifo_data_len = 0, prph_len = 0, radio_len = 0;
+ u32 smem_len = mvm->cfg->smem_len;
+ u32 sram2_len = mvm->cfg->dccm2_len;
+ bool monitor_dump_only = false;
+ int i;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* there's no point in fw dump if the bus is dead */
+ if (test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) {
+ IWL_ERR(mvm, "Skip fw error dump since bus is dead\n");
+ goto out;
+ }
+
+ if (mvm->fw_dump_trig &&
+ mvm->fw_dump_trig->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY)
+ monitor_dump_only = true;
+
+ fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL);
+ if (!fw_error_dump)
+ goto out;
+
+ /* SRAM - include stack CCM if driver knows the values for it */
+ if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) {
+ const struct fw_img *img;
+
+ img = &mvm->fw->img[mvm->cur_ucode];
+ sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
+ sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
+ } else {
+ sram_ofs = mvm->cfg->dccm_offset;
+ sram_len = mvm->cfg->dccm_len;
+ }
+
+ /* reading RXF/TXF sizes */
+ if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) {
+ struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->shared_mem_cfg;
+
+ fifo_data_len = 0;
+
+ /* Count RXF size */
+ for (i = 0; i < ARRAY_SIZE(mem_cfg->rxfifo_size); i++) {
+ if (!mem_cfg->rxfifo_size[i])
+ continue;
+
+ /* Add header info */
+ fifo_data_len += mem_cfg->rxfifo_size[i] +
+ sizeof(*dump_data) +
+ sizeof(struct iwl_fw_error_dump_fifo);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) {
+ if (!mem_cfg->txfifo_size[i])
+ continue;
+
+ /* Add header info */
+ fifo_data_len += mem_cfg->txfifo_size[i] +
+ sizeof(*dump_data) +
+ sizeof(struct iwl_fw_error_dump_fifo);
+ }
+
+ /* Make room for PRPH registers */
+ for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) {
+ /* The range includes both boundaries */
+ int num_bytes_in_chunk = iwl_prph_dump_addr[i].end -
+ iwl_prph_dump_addr[i].start + 4;
+
+ prph_len += sizeof(*dump_data) +
+ sizeof(struct iwl_fw_error_dump_prph) +
+ num_bytes_in_chunk;
+ }
+
+ if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000)
+ radio_len = sizeof(*dump_data) + RADIO_REG_MAX_READ;
+ }
+
+ file_len = sizeof(*dump_file) +
+ sizeof(*dump_data) * 2 +
+ sram_len + sizeof(*dump_mem) +
+ fifo_data_len +
+ prph_len +
+ radio_len +
+ sizeof(*dump_info);
+
+ /* Make room for the SMEM, if it exists */
+ if (smem_len)
+ file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len;
+
+ /* Make room for the secondary SRAM, if it exists */
+ if (sram2_len)
+ file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len;
+
+ /* Make room for fw's virtual image pages, if it exists */
+ if (mvm->fw->img[mvm->cur_ucode].paging_mem_size)
+ file_len += mvm->num_of_paging_blk *
+ (sizeof(*dump_data) +
+ sizeof(struct iwl_fw_error_dump_paging) +
+ PAGING_BLOCK_SIZE);
+
+ /* If we only want a monitor dump, reset the file length */
+ if (monitor_dump_only) {
+ file_len = sizeof(*dump_file) + sizeof(*dump_data) +
+ sizeof(*dump_info);
+ }
+
+ /*
+ * In 8000 HW family B-step include the ICCM (which resides separately)
+ */
+ if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
+ CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP)
+ file_len += sizeof(*dump_data) + sizeof(*dump_mem) +
+ IWL8260_ICCM_LEN;
+
+ if (mvm->fw_dump_desc)
+ file_len += sizeof(*dump_data) + sizeof(*dump_trig) +
+ mvm->fw_dump_desc->len;
+
+ dump_file = vzalloc(file_len);
+ if (!dump_file) {
+ kfree(fw_error_dump);
+ goto out;
+ }
+
+ fw_error_dump->op_mode_ptr = dump_file;
+
+ dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
+ dump_data = (void *)dump_file->data;
+
+ dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO);
+ dump_data->len = cpu_to_le32(sizeof(*dump_info));
+ dump_info = (void *)dump_data->data;
+ dump_info->device_family =
+ mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ?
+ cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) :
+ cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8);
+ dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev));
+ memcpy(dump_info->fw_human_readable, mvm->fw->human_readable,
+ sizeof(dump_info->fw_human_readable));
+ strncpy(dump_info->dev_human_readable, mvm->cfg->name,
+ sizeof(dump_info->dev_human_readable));
+ strncpy(dump_info->bus_human_readable, mvm->dev->bus->name,
+ sizeof(dump_info->bus_human_readable));
+
+ dump_data = iwl_fw_error_next_data(dump_data);
+ /* We only dump the FIFOs if the FW is in error state */
+ if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) {
+ iwl_mvm_dump_fifos(mvm, &dump_data);
+ if (radio_len)
+ iwl_mvm_read_radio_reg(mvm, &dump_data);
+ }
+
+ if (mvm->fw_dump_desc) {
+ dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO);
+ dump_data->len = cpu_to_le32(sizeof(*dump_trig) +
+ mvm->fw_dump_desc->len);
+ dump_trig = (void *)dump_data->data;
+ memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc,
+ sizeof(*dump_trig) + mvm->fw_dump_desc->len);
+
+ dump_data = iwl_fw_error_next_data(dump_data);
+ }
+
+ /* In case we only want monitor dump, skip to dump trasport data */
+ if (monitor_dump_only)
+ goto dump_trans_data;
+
+ dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
+ dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem));
+ dump_mem = (void *)dump_data->data;
+ dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
+ dump_mem->offset = cpu_to_le32(sram_ofs);
+ iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data,
+ sram_len);
+
+ if (smem_len) {
+ dump_data = iwl_fw_error_next_data(dump_data);
+ dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
+ dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem));
+ dump_mem = (void *)dump_data->data;
+ dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM);
+ dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset);
+ iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset,
+ dump_mem->data, smem_len);
+ }
+
+ if (sram2_len) {
+ dump_data = iwl_fw_error_next_data(dump_data);
+ dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
+ dump_data->len = cpu_to_le32(sram2_len + sizeof(*dump_mem));
+ dump_mem = (void *)dump_data->data;
+ dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
+ dump_mem->offset = cpu_to_le32(mvm->cfg->dccm2_offset);
+ iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->dccm2_offset,
+ dump_mem->data, sram2_len);
+ }
+
+ if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
+ CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) {
+ dump_data = iwl_fw_error_next_data(dump_data);
+ dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
+ dump_data->len = cpu_to_le32(IWL8260_ICCM_LEN +
+ sizeof(*dump_mem));
+ dump_mem = (void *)dump_data->data;
+ dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
+ dump_mem->offset = cpu_to_le32(IWL8260_ICCM_OFFSET);
+ iwl_trans_read_mem_bytes(mvm->trans, IWL8260_ICCM_OFFSET,
+ dump_mem->data, IWL8260_ICCM_LEN);
+ }
+
+ /* Dump fw's virtual image */
+ if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) {
+ u32 i;
+
+ for (i = 1; i < mvm->num_of_paging_blk + 1; i++) {
+ struct iwl_fw_error_dump_paging *paging;
+ struct page *pages =
+ mvm->fw_paging_db[i].fw_paging_block;
+
+ dump_data = iwl_fw_error_next_data(dump_data);
+ dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING);
+ dump_data->len = cpu_to_le32(sizeof(*paging) +
+ PAGING_BLOCK_SIZE);
+ paging = (void *)dump_data->data;
+ paging->index = cpu_to_le32(i);
+ memcpy(paging->data, page_address(pages),
+ PAGING_BLOCK_SIZE);
+ }
+ }
+
+ dump_data = iwl_fw_error_next_data(dump_data);
+ if (prph_len)
+ iwl_dump_prph(mvm->trans, &dump_data);
+
+dump_trans_data:
+ fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans,
+ mvm->fw_dump_trig);
+ fw_error_dump->op_mode_len = file_len;
+ if (fw_error_dump->trans_ptr)
+ file_len += fw_error_dump->trans_ptr->len;
+ dump_file->file_len = cpu_to_le32(file_len);
+
+ dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0,
+ GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump);
+
+out:
+ iwl_mvm_free_fw_dump_desc(mvm);
+ mvm->fw_dump_trig = NULL;
+ clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status);
+}
+
+const struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = {
+ .trig_desc = {
+ .type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT),
+ },
+};
+
+int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm,
+ const struct iwl_mvm_dump_desc *desc,
+ const struct iwl_fw_dbg_trigger_tlv *trigger)
+{
+ unsigned int delay = 0;
+
+ if (trigger)
+ delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay));
+
+ if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status))
+ return -EBUSY;
+
+ if (WARN_ON(mvm->fw_dump_desc))
+ iwl_mvm_free_fw_dump_desc(mvm);
+
+ IWL_WARN(mvm, "Collecting data: trigger %d fired.\n",
+ le32_to_cpu(desc->trig_desc.type));
+
+ mvm->fw_dump_desc = desc;
+ mvm->fw_dump_trig = trigger;
+
+ queue_delayed_work(system_wq, &mvm->fw_dump_wk, delay);
+
+ return 0;
+}
+
+int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
+ const char *str, size_t len,
+ const struct iwl_fw_dbg_trigger_tlv *trigger)
+{
+ struct iwl_mvm_dump_desc *desc;
+
+ desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC);
+ if (!desc)
+ return -ENOMEM;
+
+ desc->len = len;
+ desc->trig_desc.type = cpu_to_le32(trig);
+ memcpy(desc->trig_desc.data, str, len);
+
+ return iwl_mvm_fw_dbg_collect_desc(mvm, desc, trigger);
+}
+
+int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
+ struct iwl_fw_dbg_trigger_tlv *trigger,
+ const char *fmt, ...)
+{
+ u16 occurrences = le16_to_cpu(trigger->occurrences);
+ int ret, len = 0;
+ char buf[64];
+
+ if (!occurrences)
+ return 0;
+
+ if (fmt) {
+ va_list ap;
+
+ buf[sizeof(buf) - 1] = '\0';
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ /* check for truncation */
+ if (WARN_ON_ONCE(buf[sizeof(buf) - 1]))
+ buf[sizeof(buf) - 1] = '\0';
+
+ len = strlen(buf) + 1;
+ }
+
+ ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), buf, len,
+ trigger);
+
+ if (ret)
+ return ret;
+
+ trigger->occurrences = cpu_to_le16(occurrences - 1);
+ return 0;
+}
+
+static inline void iwl_mvm_restart_early_start(struct iwl_mvm *mvm)
+{
+ if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000)
+ iwl_clear_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100);
+ else
+ iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 1);
+}
+
+int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id)
+{
+ u8 *ptr;
+ int ret;
+ int i;
+
+ if (WARN_ONCE(conf_id >= ARRAY_SIZE(mvm->fw->dbg_conf_tlv),
+ "Invalid configuration %d\n", conf_id))
+ return -EINVAL;
+
+ /* EARLY START - firmware's configuration is hard coded */
+ if ((!mvm->fw->dbg_conf_tlv[conf_id] ||
+ !mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds) &&
+ conf_id == FW_DBG_START_FROM_ALIVE) {
+ iwl_mvm_restart_early_start(mvm);
+ return 0;
+ }
+
+ if (!mvm->fw->dbg_conf_tlv[conf_id])
+ return -EINVAL;
+
+ if (mvm->fw_dbg_conf != FW_DBG_INVALID)
+ IWL_WARN(mvm, "FW already configured (%d) - re-configuring\n",
+ mvm->fw_dbg_conf);
+
+ /* Send all HCMDs for configuring the FW debug */
+ ptr = (void *)&mvm->fw->dbg_conf_tlv[conf_id]->hcmd;
+ for (i = 0; i < mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds; i++) {
+ struct iwl_fw_dbg_conf_hcmd *cmd = (void *)ptr;
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, cmd->id, 0,
+ le16_to_cpu(cmd->len), cmd->data);
+ if (ret)
+ return ret;
+
+ ptr += sizeof(*cmd);
+ ptr += le16_to_cpu(cmd->len);
+ }
+
+ mvm->fw_dbg_conf = conf_id;
+ return ret;
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h
new file mode 100644
index 000000000000..f7dff7612c9c
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h
@@ -0,0 +1,174 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program;
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __mvm_fw_dbg_h__
+#define __mvm_fw_dbg_h__
+#include "iwl-fw-file.h"
+#include "iwl-fw-error-dump.h"
+#include "mvm.h"
+
+void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm);
+void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm);
+int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm,
+ const struct iwl_mvm_dump_desc *desc,
+ const struct iwl_fw_dbg_trigger_tlv *trigger);
+int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
+ const char *str, size_t len,
+ const struct iwl_fw_dbg_trigger_tlv *trigger);
+int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
+ struct iwl_fw_dbg_trigger_tlv *trigger,
+ const char *fmt, ...) __printf(3, 4);
+int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 id);
+
+#define iwl_fw_dbg_trigger_enabled(fw, id) ({ \
+ void *__dbg_trigger = (fw)->dbg_trigger_tlv[(id)]; \
+ unlikely(__dbg_trigger); \
+})
+
+static inline struct iwl_fw_dbg_trigger_tlv*
+_iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, enum iwl_fw_dbg_trigger id)
+{
+ return fw->dbg_trigger_tlv[id];
+}
+
+#define iwl_fw_dbg_get_trigger(fw, id) ({ \
+ BUILD_BUG_ON(!__builtin_constant_p(id)); \
+ BUILD_BUG_ON((id) >= FW_DBG_TRIGGER_MAX); \
+ _iwl_fw_dbg_get_trigger((fw), (id)); \
+})
+
+static inline bool
+iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig,
+ struct ieee80211_vif *vif)
+{
+ u32 trig_vif = le32_to_cpu(trig->vif_type);
+
+ return trig_vif == IWL_FW_DBG_CONF_VIF_ANY || vif->type == trig_vif;
+}
+
+static inline bool
+iwl_fw_dbg_trigger_stop_conf_match(struct iwl_mvm *mvm,
+ struct iwl_fw_dbg_trigger_tlv *trig)
+{
+ return ((trig->mode & IWL_FW_DBG_TRIGGER_STOP) &&
+ (mvm->fw_dbg_conf == FW_DBG_INVALID ||
+ (BIT(mvm->fw_dbg_conf) & le32_to_cpu(trig->stop_conf_ids))));
+}
+
+static inline bool
+iwl_fw_dbg_no_trig_window(struct iwl_mvm *mvm,
+ struct iwl_fw_dbg_trigger_tlv *trig)
+{
+ unsigned long wind_jiff =
+ msecs_to_jiffies(le16_to_cpu(trig->trig_dis_ms));
+ u32 id = le32_to_cpu(trig->id);
+
+ /* If this is the first event checked, jump to update start ts */
+ if (mvm->fw_dbg_non_collect_ts_start[id] &&
+ (time_after(mvm->fw_dbg_non_collect_ts_start[id] + wind_jiff,
+ jiffies)))
+ return true;
+
+ mvm->fw_dbg_non_collect_ts_start[id] = jiffies;
+ return false;
+}
+
+static inline bool
+iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_fw_dbg_trigger_tlv *trig)
+{
+ if (vif && !iwl_fw_dbg_trigger_vif_match(trig, vif))
+ return false;
+
+ if (iwl_fw_dbg_no_trig_window(mvm, trig)) {
+ IWL_WARN(mvm, "Trigger %d occurred while no-collect window.\n",
+ trig->id);
+ return false;
+ }
+
+ return iwl_fw_dbg_trigger_stop_conf_match(mvm, trig);
+}
+
+static inline void
+_iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_fw_dbg_trigger_tlv *trigger)
+{
+ if (!trigger)
+ return;
+
+ if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger))
+ return;
+
+ iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL);
+}
+
+#define iwl_fw_dbg_trigger_simple_stop(mvm, vif, trig) \
+ _iwl_fw_dbg_trigger_simple_stop((mvm), (vif), \
+ iwl_fw_dbg_get_trigger((mvm)->fw,\
+ (trig)))
+
+#endif /* __mvm_fw_dbg_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
index d906fa13ba97..4ed5180c547b 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -74,6 +74,7 @@
#include "iwl-eeprom-parse.h"
#include "mvm.h"
+#include "fw-dbg.h"
#include "iwl-phy-db.h"
#define MVM_UCODE_ALIVE_TIMEOUT HZ
@@ -805,137 +806,6 @@ static void iwl_mvm_get_shared_mem_conf(struct iwl_mvm *mvm)
iwl_free_resp(&cmd);
}
-int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm,
- struct iwl_mvm_dump_desc *desc,
- struct iwl_fw_dbg_trigger_tlv *trigger)
-{
- unsigned int delay = 0;
-
- if (trigger)
- delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay));
-
- if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status))
- return -EBUSY;
-
- if (WARN_ON(mvm->fw_dump_desc))
- iwl_mvm_free_fw_dump_desc(mvm);
-
- IWL_WARN(mvm, "Collecting data: trigger %d fired.\n",
- le32_to_cpu(desc->trig_desc.type));
-
- mvm->fw_dump_desc = desc;
- mvm->fw_dump_trig = trigger;
-
- queue_delayed_work(system_wq, &mvm->fw_dump_wk, delay);
-
- return 0;
-}
-
-int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
- const char *str, size_t len,
- struct iwl_fw_dbg_trigger_tlv *trigger)
-{
- struct iwl_mvm_dump_desc *desc;
-
- desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC);
- if (!desc)
- return -ENOMEM;
-
- desc->len = len;
- desc->trig_desc.type = cpu_to_le32(trig);
- memcpy(desc->trig_desc.data, str, len);
-
- return iwl_mvm_fw_dbg_collect_desc(mvm, desc, trigger);
-}
-
-int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
- struct iwl_fw_dbg_trigger_tlv *trigger,
- const char *fmt, ...)
-{
- u16 occurrences = le16_to_cpu(trigger->occurrences);
- int ret, len = 0;
- char buf[64];
-
- if (!occurrences)
- return 0;
-
- if (fmt) {
- va_list ap;
-
- buf[sizeof(buf) - 1] = '\0';
-
- va_start(ap, fmt);
- vsnprintf(buf, sizeof(buf), fmt, ap);
- va_end(ap);
-
- /* check for truncation */
- if (WARN_ON_ONCE(buf[sizeof(buf) - 1]))
- buf[sizeof(buf) - 1] = '\0';
-
- len = strlen(buf) + 1;
- }
-
- ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), buf, len,
- trigger);
-
- if (ret)
- return ret;
-
- trigger->occurrences = cpu_to_le16(occurrences - 1);
- return 0;
-}
-
-static inline void iwl_mvm_restart_early_start(struct iwl_mvm *mvm)
-{
- if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000)
- iwl_clear_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100);
- else
- iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 1);
-}
-
-int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id)
-{
- u8 *ptr;
- int ret;
- int i;
-
- if (WARN_ONCE(conf_id >= ARRAY_SIZE(mvm->fw->dbg_conf_tlv),
- "Invalid configuration %d\n", conf_id))
- return -EINVAL;
-
- /* EARLY START - firmware's configuration is hard coded */
- if ((!mvm->fw->dbg_conf_tlv[conf_id] ||
- !mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds) &&
- conf_id == FW_DBG_START_FROM_ALIVE) {
- iwl_mvm_restart_early_start(mvm);
- return 0;
- }
-
- if (!mvm->fw->dbg_conf_tlv[conf_id])
- return -EINVAL;
-
- if (mvm->fw_dbg_conf != FW_DBG_INVALID)
- IWL_WARN(mvm, "FW already configured (%d) - re-configuring\n",
- mvm->fw_dbg_conf);
-
- /* Send all HCMDs for configuring the FW debug */
- ptr = (void *)&mvm->fw->dbg_conf_tlv[conf_id]->hcmd;
- for (i = 0; i < mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds; i++) {
- struct iwl_fw_dbg_conf_hcmd *cmd = (void *)ptr;
-
- ret = iwl_mvm_send_cmd_pdu(mvm, cmd->id, 0,
- le16_to_cpu(cmd->len), cmd->data);
- if (ret)
- return ret;
-
- ptr += sizeof(*cmd);
- ptr += le16_to_cpu(cmd->len);
- }
-
- mvm->fw_dbg_conf = conf_id;
- return ret;
-}
-
static int iwl_mvm_config_ltr(struct iwl_mvm *mvm)
{
struct iwl_ltr_config_cmd cmd = {
@@ -1073,6 +943,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
}
if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
+ mvm->scan_type = IWL_SCAN_TYPE_NOT_SET;
ret = iwl_mvm_config_scan(mvm);
if (ret)
goto error;
diff --git a/drivers/net/wireless/iwlwifi/mvm/led.c b/drivers/net/wireless/intel/iwlwifi/mvm/led.c
index e3b3cf4dbd77..1e51fbe95f7c 100644
--- a/drivers/net/wireless/iwlwifi/mvm/led.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/led.c
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
index ad7ad720d2e7..bf1e5eb5dbdb 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
@@ -27,7 +27,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -72,6 +72,7 @@
#include "fw-api.h"
#include "mvm.h"
#include "time-event.h"
+#include "fw-dbg.h"
const u8 iwl_mvm_ac_to_tx_fifo[] = {
IWL_MVM_TX_FIFO_VO,
@@ -716,6 +717,8 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
cpu_to_le32(vif->bss_conf.use_short_slot ?
MAC_FLG_SHORT_SLOT : 0);
+ cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP);
+
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
u8 txf = iwl_mvm_ac_to_tx_fifo[i];
@@ -729,11 +732,26 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
cmd->ac[txf].fifos_mask = BIT(txf);
}
- /* in AP mode, the MCAST FIFO takes the EDCA params from VO */
- if (vif->type == NL80211_IFTYPE_AP)
+ if (vif->type == NL80211_IFTYPE_AP) {
+ /* in AP mode, the MCAST FIFO takes the EDCA params from VO */
cmd->ac[IWL_MVM_TX_FIFO_VO].fifos_mask |=
BIT(IWL_MVM_TX_FIFO_MCAST);
+ /*
+ * in AP mode, pass probe requests and beacons from other APs
+ * (needed for ht protection); when there're no any associated
+ * station don't ask FW to pass beacons to prevent unnecessary
+ * wake-ups.
+ */
+ cmd->filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST);
+ if (mvmvif->ap_assoc_sta_count) {
+ cmd->filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON);
+ IWL_DEBUG_HC(mvm, "Asking FW to pass beacons\n");
+ } else {
+ IWL_DEBUG_HC(mvm, "No need to receive beacons\n");
+ }
+ }
+
if (vif->bss_conf.qos)
cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
@@ -747,8 +765,6 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN);
if (ht_enabled)
iwl_mvm_mac_ctxt_set_ht_flags(mvm, vif, cmd);
-
- cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP);
}
static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm,
@@ -854,11 +870,17 @@ static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm,
u32 action)
{
struct iwl_mac_ctx_cmd cmd = {};
+ u32 tfd_queue_msk = 0;
+ int ret, i;
WARN_ON(vif->type != NL80211_IFTYPE_MONITOR);
iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action);
+ for (i = 0; i < IEEE80211_NUM_ACS; i++)
+ if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE)
+ tfd_queue_msk |= BIT(vif->hw_queue[i]);
+
cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROMISC |
MAC_FILTER_IN_CONTROL_AND_MGMT |
MAC_FILTER_IN_BEACON |
@@ -866,6 +888,12 @@ static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm,
MAC_FILTER_IN_CRC32);
ieee80211_hw_set(mvm->hw, RX_INCLUDES_FCS);
+ /* Allocate sniffer station */
+ ret = iwl_mvm_allocate_int_sta(mvm, &mvm->snif_sta, tfd_queue_msk,
+ vif->type);
+ if (ret)
+ return ret;
+
return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
}
@@ -999,9 +1027,12 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
TX_CMD_FLG_BT_PRIO_POS;
beacon_cmd.tx.tx_flags = cpu_to_le32(tx_flags);
- mvm->mgmt_last_antenna_idx =
- iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm),
- mvm->mgmt_last_antenna_idx);
+ if (!fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION)) {
+ mvm->mgmt_last_antenna_idx =
+ iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm),
+ mvm->mgmt_last_antenna_idx);
+ }
beacon_cmd.tx.rate_n_flags =
cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) <<
@@ -1140,7 +1171,6 @@ static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
u32 action)
{
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mac_ctx_cmd cmd = {};
WARN_ON(vif->type != NL80211_IFTYPE_AP || vif->p2p);
@@ -1148,19 +1178,6 @@ static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm,
/* Fill the common data for all mac context types */
iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action);
- /*
- * pass probe requests and beacons from other APs (needed
- * for ht protection); when there're no any associated station
- * don't ask FW to pass beacons to prevent unnecessary wake-ups.
- */
- cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST);
- if (mvmvif->ap_assoc_sta_count) {
- cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON);
- IWL_DEBUG_HC(mvm, "Asking FW to pass beacons\n");
- } else {
- IWL_DEBUG_HC(mvm, "No need to receive beacons\n");
- }
-
/* Fill the data specific for ap mode */
iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.ap,
action == FW_CTXT_ACTION_ADD);
@@ -1180,13 +1197,6 @@ static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm,
/* Fill the common data for all mac context types */
iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action);
- /*
- * pass probe requests and beacons from other APs (needed
- * for ht protection)
- */
- cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST |
- MAC_FILTER_IN_BEACON);
-
/* Fill the data specific for GO mode */
iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.go.ap,
action == FW_CTXT_ACTION_ADD);
@@ -1288,8 +1298,10 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
mvmvif->uploaded = false;
- if (vif->type == NL80211_IFTYPE_MONITOR)
+ if (vif->type == NL80211_IFTYPE_MONITOR) {
__clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, mvm->hw->flags);
+ iwl_mvm_dealloc_snif_sta(mvm);
+ }
return 0;
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index 1fb684693040..d70a1716f3e0 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -70,6 +70,7 @@
#include <linux/ip.h>
#include <linux/if_arp.h>
#include <linux/devcoredump.h>
+#include <linux/time.h>
#include <net/mac80211.h>
#include <net/ieee80211_radiotap.h>
#include <net/tcp.h>
@@ -86,6 +87,7 @@
#include "iwl-prph.h"
#include "iwl-csr.h"
#include "iwl-nvm-parse.h"
+#include "fw-dbg.h"
static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
{
@@ -436,6 +438,11 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
ieee80211_hw_set(hw, CHANCTX_STA_CSA);
ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);
+ ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
+ ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR);
+
+ if (mvm->trans->max_skb_frags)
+ hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG;
hw->queues = mvm->first_agg_queue;
hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
@@ -662,6 +669,10 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
if (!iwl_mvm_is_csum_supported(mvm))
hw->netdev_features &= ~NETIF_F_RXCSUM;
+ if (IWL_MVM_SW_TX_CSUM_OFFLOAD)
+ hw->netdev_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+ NETIF_F_TSO | NETIF_F_TSO6;
+
ret = ieee80211_register_hw(mvm->hw);
if (ret)
iwl_mvm_leds_exit(mvm);
@@ -939,431 +950,6 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
memset(&mvmvif->bf_data, 0, sizeof(mvmvif->bf_data));
}
-static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count,
- const void *data, size_t datalen)
-{
- const struct iwl_mvm_dump_ptrs *dump_ptrs = data;
- ssize_t bytes_read;
- ssize_t bytes_read_trans;
-
- if (offset < dump_ptrs->op_mode_len) {
- bytes_read = min_t(ssize_t, count,
- dump_ptrs->op_mode_len - offset);
- memcpy(buffer, (u8 *)dump_ptrs->op_mode_ptr + offset,
- bytes_read);
- offset += bytes_read;
- count -= bytes_read;
-
- if (count == 0)
- return bytes_read;
- } else {
- bytes_read = 0;
- }
-
- if (!dump_ptrs->trans_ptr)
- return bytes_read;
-
- offset -= dump_ptrs->op_mode_len;
- bytes_read_trans = min_t(ssize_t, count,
- dump_ptrs->trans_ptr->len - offset);
- memcpy(buffer + bytes_read,
- (u8 *)dump_ptrs->trans_ptr->data + offset,
- bytes_read_trans);
-
- return bytes_read + bytes_read_trans;
-}
-
-static void iwl_mvm_free_coredump(const void *data)
-{
- const struct iwl_mvm_dump_ptrs *fw_error_dump = data;
-
- vfree(fw_error_dump->op_mode_ptr);
- vfree(fw_error_dump->trans_ptr);
- kfree(fw_error_dump);
-}
-
-static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm,
- struct iwl_fw_error_dump_data **dump_data)
-{
- struct iwl_fw_error_dump_fifo *fifo_hdr;
- u32 *fifo_data;
- u32 fifo_len;
- unsigned long flags;
- int i, j;
-
- if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags))
- return;
-
- /* Pull RXF data from all RXFs */
- for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) {
- /*
- * Keep aside the additional offset that might be needed for
- * next RXF
- */
- u32 offset_diff = RXF_DIFF_FROM_PREV * i;
-
- fifo_hdr = (void *)(*dump_data)->data;
- fifo_data = (void *)fifo_hdr->data;
- fifo_len = mvm->shared_mem_cfg.rxfifo_size[i];
-
- /* No need to try to read the data if the length is 0 */
- if (fifo_len == 0)
- continue;
-
- /* Add a TLV for the RXF */
- (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
- (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr));
-
- fifo_hdr->fifo_num = cpu_to_le32(i);
- fifo_hdr->available_bytes =
- cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- RXF_RD_D_SPACE +
- offset_diff));
- fifo_hdr->wr_ptr =
- cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- RXF_RD_WR_PTR +
- offset_diff));
- fifo_hdr->rd_ptr =
- cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- RXF_RD_RD_PTR +
- offset_diff));
- fifo_hdr->fence_ptr =
- cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- RXF_RD_FENCE_PTR +
- offset_diff));
- fifo_hdr->fence_mode =
- cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- RXF_SET_FENCE_MODE +
- offset_diff));
-
- /* Lock fence */
- iwl_trans_write_prph(mvm->trans,
- RXF_SET_FENCE_MODE + offset_diff, 0x1);
- /* Set fence pointer to the same place like WR pointer */
- iwl_trans_write_prph(mvm->trans,
- RXF_LD_WR2FENCE + offset_diff, 0x1);
- /* Set fence offset */
- iwl_trans_write_prph(mvm->trans,
- RXF_LD_FENCE_OFFSET_ADDR + offset_diff,
- 0x0);
-
- /* Read FIFO */
- fifo_len /= sizeof(u32); /* Size in DWORDS */
- for (j = 0; j < fifo_len; j++)
- fifo_data[j] = iwl_trans_read_prph(mvm->trans,
- RXF_FIFO_RD_FENCE_INC +
- offset_diff);
- *dump_data = iwl_fw_error_next_data(*dump_data);
- }
-
- /* Pull TXF data from all TXFs */
- for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) {
- /* Mark the number of TXF we're pulling now */
- iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i);
-
- fifo_hdr = (void *)(*dump_data)->data;
- fifo_data = (void *)fifo_hdr->data;
- fifo_len = mvm->shared_mem_cfg.txfifo_size[i];
-
- /* No need to try to read the data if the length is 0 */
- if (fifo_len == 0)
- continue;
-
- /* Add a TLV for the FIFO */
- (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF);
- (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr));
-
- fifo_hdr->fifo_num = cpu_to_le32(i);
- fifo_hdr->available_bytes =
- cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- TXF_FIFO_ITEM_CNT));
- fifo_hdr->wr_ptr =
- cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- TXF_WR_PTR));
- fifo_hdr->rd_ptr =
- cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- TXF_RD_PTR));
- fifo_hdr->fence_ptr =
- cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- TXF_FENCE_PTR));
- fifo_hdr->fence_mode =
- cpu_to_le32(iwl_trans_read_prph(mvm->trans,
- TXF_LOCK_FENCE));
-
- /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */
- iwl_trans_write_prph(mvm->trans, TXF_READ_MODIFY_ADDR,
- TXF_WR_PTR);
-
- /* Dummy-read to advance the read pointer to the head */
- iwl_trans_read_prph(mvm->trans, TXF_READ_MODIFY_DATA);
-
- /* Read FIFO */
- fifo_len /= sizeof(u32); /* Size in DWORDS */
- for (j = 0; j < fifo_len; j++)
- fifo_data[j] = iwl_trans_read_prph(mvm->trans,
- TXF_READ_MODIFY_DATA);
- *dump_data = iwl_fw_error_next_data(*dump_data);
- }
-
- iwl_trans_release_nic_access(mvm->trans, &flags);
-}
-
-void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm)
-{
- if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert ||
- !mvm->fw_dump_desc)
- return;
-
- kfree(mvm->fw_dump_desc);
- mvm->fw_dump_desc = NULL;
-}
-
-#define IWL8260_ICCM_OFFSET 0x44000 /* Only for B-step */
-#define IWL8260_ICCM_LEN 0xC000 /* Only for B-step */
-
-void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
-{
- struct iwl_fw_error_dump_file *dump_file;
- struct iwl_fw_error_dump_data *dump_data;
- struct iwl_fw_error_dump_info *dump_info;
- struct iwl_fw_error_dump_mem *dump_mem;
- struct iwl_fw_error_dump_trigger_desc *dump_trig;
- struct iwl_mvm_dump_ptrs *fw_error_dump;
- u32 sram_len, sram_ofs;
- u32 file_len, fifo_data_len = 0;
- u32 smem_len = mvm->cfg->smem_len;
- u32 sram2_len = mvm->cfg->dccm2_len;
- bool monitor_dump_only = false;
-
- lockdep_assert_held(&mvm->mutex);
-
- /* there's no point in fw dump if the bus is dead */
- if (test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) {
- IWL_ERR(mvm, "Skip fw error dump since bus is dead\n");
- return;
- }
-
- if (mvm->fw_dump_trig &&
- mvm->fw_dump_trig->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY)
- monitor_dump_only = true;
-
- fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL);
- if (!fw_error_dump)
- return;
-
- /* SRAM - include stack CCM if driver knows the values for it */
- if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) {
- const struct fw_img *img;
-
- img = &mvm->fw->img[mvm->cur_ucode];
- sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
- sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
- } else {
- sram_ofs = mvm->cfg->dccm_offset;
- sram_len = mvm->cfg->dccm_len;
- }
-
- /* reading RXF/TXF sizes */
- if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) {
- struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->shared_mem_cfg;
- int i;
-
- fifo_data_len = 0;
-
- /* Count RXF size */
- for (i = 0; i < ARRAY_SIZE(mem_cfg->rxfifo_size); i++) {
- if (!mem_cfg->rxfifo_size[i])
- continue;
-
- /* Add header info */
- fifo_data_len += mem_cfg->rxfifo_size[i] +
- sizeof(*dump_data) +
- sizeof(struct iwl_fw_error_dump_fifo);
- }
-
- for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) {
- if (!mem_cfg->txfifo_size[i])
- continue;
-
- /* Add header info */
- fifo_data_len += mem_cfg->txfifo_size[i] +
- sizeof(*dump_data) +
- sizeof(struct iwl_fw_error_dump_fifo);
- }
- }
-
- file_len = sizeof(*dump_file) +
- sizeof(*dump_data) * 2 +
- sram_len + sizeof(*dump_mem) +
- fifo_data_len +
- sizeof(*dump_info);
-
- /* Make room for the SMEM, if it exists */
- if (smem_len)
- file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len;
-
- /* Make room for the secondary SRAM, if it exists */
- if (sram2_len)
- file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len;
-
- /* Make room for fw's virtual image pages, if it exists */
- if (mvm->fw->img[mvm->cur_ucode].paging_mem_size)
- file_len += mvm->num_of_paging_blk *
- (sizeof(*dump_data) +
- sizeof(struct iwl_fw_error_dump_paging) +
- PAGING_BLOCK_SIZE);
-
- /* If we only want a monitor dump, reset the file length */
- if (monitor_dump_only) {
- file_len = sizeof(*dump_file) + sizeof(*dump_data) +
- sizeof(*dump_info);
- }
-
- /*
- * In 8000 HW family B-step include the ICCM (which resides separately)
- */
- if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
- CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP)
- file_len += sizeof(*dump_data) + sizeof(*dump_mem) +
- IWL8260_ICCM_LEN;
-
- if (mvm->fw_dump_desc)
- file_len += sizeof(*dump_data) + sizeof(*dump_trig) +
- mvm->fw_dump_desc->len;
-
- dump_file = vzalloc(file_len);
- if (!dump_file) {
- kfree(fw_error_dump);
- iwl_mvm_free_fw_dump_desc(mvm);
- return;
- }
-
- fw_error_dump->op_mode_ptr = dump_file;
-
- dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
- dump_data = (void *)dump_file->data;
-
- dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO);
- dump_data->len = cpu_to_le32(sizeof(*dump_info));
- dump_info = (void *) dump_data->data;
- dump_info->device_family =
- mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ?
- cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) :
- cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8);
- dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev));
- memcpy(dump_info->fw_human_readable, mvm->fw->human_readable,
- sizeof(dump_info->fw_human_readable));
- strncpy(dump_info->dev_human_readable, mvm->cfg->name,
- sizeof(dump_info->dev_human_readable));
- strncpy(dump_info->bus_human_readable, mvm->dev->bus->name,
- sizeof(dump_info->bus_human_readable));
-
- dump_data = iwl_fw_error_next_data(dump_data);
- /* We only dump the FIFOs if the FW is in error state */
- if (test_bit(STATUS_FW_ERROR, &mvm->trans->status))
- iwl_mvm_dump_fifos(mvm, &dump_data);
-
- if (mvm->fw_dump_desc) {
- dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO);
- dump_data->len = cpu_to_le32(sizeof(*dump_trig) +
- mvm->fw_dump_desc->len);
- dump_trig = (void *)dump_data->data;
- memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc,
- sizeof(*dump_trig) + mvm->fw_dump_desc->len);
-
- /* now we can free this copy */
- iwl_mvm_free_fw_dump_desc(mvm);
- dump_data = iwl_fw_error_next_data(dump_data);
- }
-
- /* In case we only want monitor dump, skip to dump trasport data */
- if (monitor_dump_only)
- goto dump_trans_data;
-
- dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
- dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem));
- dump_mem = (void *)dump_data->data;
- dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
- dump_mem->offset = cpu_to_le32(sram_ofs);
- iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data,
- sram_len);
-
- if (smem_len) {
- dump_data = iwl_fw_error_next_data(dump_data);
- dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
- dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem));
- dump_mem = (void *)dump_data->data;
- dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM);
- dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset);
- iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset,
- dump_mem->data, smem_len);
- }
-
- if (sram2_len) {
- dump_data = iwl_fw_error_next_data(dump_data);
- dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
- dump_data->len = cpu_to_le32(sram2_len + sizeof(*dump_mem));
- dump_mem = (void *)dump_data->data;
- dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
- dump_mem->offset = cpu_to_le32(mvm->cfg->dccm2_offset);
- iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->dccm2_offset,
- dump_mem->data, sram2_len);
- }
-
- if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
- CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) {
- dump_data = iwl_fw_error_next_data(dump_data);
- dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
- dump_data->len = cpu_to_le32(IWL8260_ICCM_LEN +
- sizeof(*dump_mem));
- dump_mem = (void *)dump_data->data;
- dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
- dump_mem->offset = cpu_to_le32(IWL8260_ICCM_OFFSET);
- iwl_trans_read_mem_bytes(mvm->trans, IWL8260_ICCM_OFFSET,
- dump_mem->data, IWL8260_ICCM_LEN);
- }
-
- /* Dump fw's virtual image */
- if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) {
- u32 i;
-
- for (i = 1; i < mvm->num_of_paging_blk + 1; i++) {
- struct iwl_fw_error_dump_paging *paging;
- struct page *pages =
- mvm->fw_paging_db[i].fw_paging_block;
-
- dump_data = iwl_fw_error_next_data(dump_data);
- dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING);
- dump_data->len = cpu_to_le32(sizeof(*paging) +
- PAGING_BLOCK_SIZE);
- paging = (void *)dump_data->data;
- paging->index = cpu_to_le32(i);
- memcpy(paging->data, page_address(pages),
- PAGING_BLOCK_SIZE);
- }
- }
-
-dump_trans_data:
- fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans,
- mvm->fw_dump_trig);
- fw_error_dump->op_mode_len = file_len;
- if (fw_error_dump->trans_ptr)
- file_len += fw_error_dump->trans_ptr->len;
- dump_file->file_len = cpu_to_le32(file_len);
-
- dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0,
- GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump);
-
- mvm->fw_dump_trig = NULL;
- clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status);
-}
-
-struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = {
- .trig_desc = {
- .type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT),
- },
-};
-
static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
{
/* clear the D3 reconfig, we only need it to avoid dumping a
@@ -1387,6 +973,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
mvm->calibrating = false;
/* just in case one was running */
+ iwl_mvm_cleanup_roc_te(mvm);
ieee80211_remain_on_channel_expired(mvm->hw);
/*
@@ -1399,6 +986,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
iwl_mvm_reset_phy_ctxts(mvm);
+ memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained));
memset(mvm->tfd_drained, 0, sizeof(mvm->tfd_drained));
memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
@@ -1427,10 +1015,18 @@ int __iwl_mvm_mac_start(struct iwl_mvm *mvm)
lockdep_assert_held(&mvm->mutex);
- /* Clean up some internal and mac80211 state on restart */
- if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+ /* Clean up some internal and mac80211 state on restart */
iwl_mvm_restart_cleanup(mvm);
-
+ } else {
+ /* Hold the reference to prevent runtime suspend while
+ * the start procedure runs. It's a bit confusing
+ * that the UCODE_DOWN reference is taken, but it just
+ * means "UCODE is not UP yet". ( TODO: rename this
+ * reference).
+ */
+ iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
+ }
ret = iwl_mvm_up(mvm);
if (ret && test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
@@ -1497,15 +1093,13 @@ static void iwl_mvm_restart_complete(struct iwl_mvm *mvm)
static void iwl_mvm_resume_complete(struct iwl_mvm *mvm)
{
- if (!iwl_mvm_is_d0i3_supported(mvm))
- return;
-
- if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND)
- if (!wait_event_timeout(mvm->d0i3_exit_waitq,
- !test_bit(IWL_MVM_STATUS_IN_D0I3,
- &mvm->status),
- HZ))
- WARN_ONCE(1, "D0i3 exit on resume timed out\n");
+ if (iwl_mvm_is_d0i3_supported(mvm) &&
+ iwl_mvm_enter_d0i3_on_suspend(mvm))
+ WARN_ONCE(!wait_event_timeout(mvm->d0i3_exit_waitq,
+ !test_bit(IWL_MVM_STATUS_IN_D0I3,
+ &mvm->status),
+ HZ),
+ "D0i3 exit on resume timed out\n");
}
static void
@@ -1533,14 +1127,6 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
*/
memset(&mvm->accu_radio_stats, 0, sizeof(mvm->accu_radio_stats));
- /*
- * Disallow low power states when the FW is down by taking
- * the UCODE_DOWN ref. in case of ongoing hw restart the
- * ref is already taken, so don't take it again.
- */
- if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
- iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
-
/* async_handlers_wk is now blocked */
/*
@@ -2152,8 +1738,8 @@ bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm,
return true;
}
-static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
+
+static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm)
{
struct iwl_bcast_filter_cmd cmd;
@@ -2167,8 +1753,7 @@ static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm,
sizeof(cmd), &cmd);
}
#else
-static inline int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
+static inline int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm)
{
return 0;
}
@@ -2283,7 +1868,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
}
iwl_mvm_recalc_multicast(mvm);
- iwl_mvm_configure_bcast_filter(mvm, vif);
+ iwl_mvm_configure_bcast_filter(mvm);
/* reset rssi values */
mvmvif->bf_data.ave_beacon_signal = 0;
@@ -2291,6 +1876,9 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
iwl_mvm_bt_coex_vif_change(mvm);
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT,
IEEE80211_SMPS_AUTOMATIC);
+ if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_UMAC_SCAN))
+ iwl_mvm_config_scan(mvm);
} else if (changes & BSS_CHANGED_BEACON_INFO) {
/*
* We received a beacon _after_ association so
@@ -2331,7 +1919,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
if (changes & BSS_CHANGED_ARP_FILTER) {
IWL_DEBUG_MAC80211(mvm, "arp filter changed\n");
- iwl_mvm_configure_bcast_filter(mvm, vif);
+ iwl_mvm_configure_bcast_filter(mvm);
}
}
@@ -2661,7 +2249,6 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
struct ieee80211_sta *sta)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
/*
@@ -2677,11 +2264,6 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
ERR_PTR(-ENOENT));
- if (mvm_sta->vif->type == NL80211_IFTYPE_AP) {
- mvmvif->ap_assoc_sta_count--;
- iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
- }
-
mutex_unlock(&mvm->mutex);
}
@@ -2699,6 +2281,34 @@ static void iwl_mvm_check_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
}
+static void
+iwl_mvm_tdls_check_trigger(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif, u8 *peer_addr,
+ enum nl80211_tdls_operation action)
+{
+ struct iwl_fw_dbg_trigger_tlv *trig;
+ struct iwl_fw_dbg_trigger_tdls *tdls_trig;
+
+ if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TDLS))
+ return;
+
+ trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TDLS);
+ tdls_trig = (void *)trig->data;
+ if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
+ return;
+
+ if (!(tdls_trig->action_bitmap & BIT(action)))
+ return;
+
+ if (tdls_trig->peer_mode &&
+ memcmp(tdls_trig->peer, peer_addr, ETH_ALEN) != 0)
+ return;
+
+ iwl_mvm_fw_dbg_collect_trig(mvm, trig,
+ "TDLS event occurred, peer %pM, action %d",
+ peer_addr, action);
+}
+
static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
@@ -2749,8 +2359,11 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
}
ret = iwl_mvm_add_sta(mvm, vif, sta);
- if (sta->tdls && ret == 0)
+ if (sta->tdls && ret == 0) {
iwl_mvm_recalc_tdls_state(mvm, vif, true);
+ iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr,
+ NL80211_TDLS_SETUP);
+ }
} else if (old_state == IEEE80211_STA_NONE &&
new_state == IEEE80211_STA_AUTH) {
/*
@@ -2762,6 +2375,10 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
ret = 0;
} else if (old_state == IEEE80211_STA_AUTH &&
new_state == IEEE80211_STA_ASSOC) {
+ if (vif->type == NL80211_IFTYPE_AP) {
+ mvmvif->ap_assoc_sta_count++;
+ iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+ }
ret = iwl_mvm_update_sta(mvm, vif, sta);
if (ret == 0)
iwl_mvm_rs_rate_init(mvm, sta,
@@ -2774,6 +2391,10 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
if (iwl_mvm_phy_ctx_count(mvm) > 1)
iwl_mvm_teardown_tdls_peers(mvm);
+ if (sta->tdls)
+ iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr,
+ NL80211_TDLS_ENABLE_LINK);
+
/* enable beacon filtering */
WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
ret = 0;
@@ -2784,6 +2405,10 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
ret = 0;
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTH) {
+ if (vif->type == NL80211_IFTYPE_AP) {
+ mvmvif->ap_assoc_sta_count--;
+ iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+ }
ret = 0;
} else if (old_state == IEEE80211_STA_AUTH &&
new_state == IEEE80211_STA_NONE) {
@@ -2791,8 +2416,11 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
} else if (old_state == IEEE80211_STA_NONE &&
new_state == IEEE80211_STA_NOTEXIST) {
ret = iwl_mvm_rm_sta(mvm, vif, sta);
- if (sta->tdls)
+ if (sta->tdls) {
iwl_mvm_recalc_tdls_state(mvm, vif, false);
+ iwl_mvm_tdls_check_trigger(mvm, vif, sta->addr,
+ NL80211_TDLS_DISABLE_LINK);
+ }
} else {
ret = -EIO;
}
@@ -2940,7 +2568,11 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
struct ieee80211_key_conf *key)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_sta *mvmsta;
+ struct iwl_mvm_key_pn *ptk_pn;
+ int keyidx = key->keyidx;
int ret;
+ u8 key_offset;
if (iwlwifi_mod_params.sw_crypto) {
IWL_DEBUG_MAC80211(mvm, "leave - hwcrypto disabled\n");
@@ -3006,10 +2638,44 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
break;
}
+ if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
+ sta && iwl_mvm_has_new_rx_api(mvm) &&
+ key->flags & IEEE80211_KEY_FLAG_PAIRWISE &&
+ (key->cipher == WLAN_CIPHER_SUITE_CCMP ||
+ key->cipher == WLAN_CIPHER_SUITE_GCMP)) {
+ struct ieee80211_key_seq seq;
+ int tid, q;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ WARN_ON(rcu_access_pointer(mvmsta->ptk_pn[keyidx]));
+ ptk_pn = kzalloc(sizeof(*ptk_pn) +
+ mvm->trans->num_rx_queues *
+ sizeof(ptk_pn->q[0]),
+ GFP_KERNEL);
+ if (!ptk_pn) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+ ieee80211_get_key_rx_seq(key, tid, &seq);
+ for (q = 0; q < mvm->trans->num_rx_queues; q++)
+ memcpy(ptk_pn->q[q].pn[tid],
+ seq.ccmp.pn,
+ IEEE80211_CCMP_PN_LEN);
+ }
+
+ rcu_assign_pointer(mvmsta->ptk_pn[keyidx], ptk_pn);
+ }
+
+ /* in HW restart reuse the index, otherwise request a new one */
+ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+ key_offset = key->hw_key_idx;
+ else
+ key_offset = STA_KEY_IDX_INVALID;
+
IWL_DEBUG_MAC80211(mvm, "set hwcrypto key\n");
- ret = iwl_mvm_set_sta_key(mvm, vif, sta, key,
- test_bit(IWL_MVM_STATUS_IN_HW_RESTART,
- &mvm->status));
+ ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, key_offset);
if (ret) {
IWL_WARN(mvm, "set key failed\n");
/*
@@ -3027,6 +2693,19 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
break;
}
+ if (sta && iwl_mvm_has_new_rx_api(mvm) &&
+ key->flags & IEEE80211_KEY_FLAG_PAIRWISE &&
+ (key->cipher == WLAN_CIPHER_SUITE_CCMP ||
+ key->cipher == WLAN_CIPHER_SUITE_GCMP)) {
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ ptk_pn = rcu_dereference_protected(
+ mvmsta->ptk_pn[keyidx],
+ lockdep_is_held(&mvm->mutex));
+ RCU_INIT_POINTER(mvmsta->ptk_pn[keyidx], NULL);
+ if (ptk_pn)
+ kfree_rcu(ptk_pn, rcu_head);
+ }
+
IWL_DEBUG_MAC80211(mvm, "disable hwcrypto key\n");
ret = iwl_mvm_remove_sta_key(mvm, vif, sta, key);
break;
@@ -3087,7 +2766,11 @@ static bool iwl_mvm_rx_aux_roc(struct iwl_notif_wait_data *notif_wait,
return true;
}
-#define AUX_ROC_MAX_DELAY_ON_CHANNEL 200
+#define AUX_ROC_MIN_DURATION MSEC_TO_TU(100)
+#define AUX_ROC_MIN_DELAY MSEC_TO_TU(200)
+#define AUX_ROC_MAX_DELAY MSEC_TO_TU(600)
+#define AUX_ROC_SAFETY_BUFFER MSEC_TO_TU(20)
+#define AUX_ROC_MIN_SAFETY_BUFFER MSEC_TO_TU(10)
static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
struct ieee80211_channel *channel,
struct ieee80211_vif *vif,
@@ -3098,6 +2781,9 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
struct iwl_mvm_time_event_data *te_data = &mvmvif->hs_time_event_data;
static const u16 time_event_response[] = { HOT_SPOT_CMD };
struct iwl_notification_wait wait_time_event;
+ u32 dtim_interval = vif->bss_conf.dtim_period *
+ vif->bss_conf.beacon_int;
+ u32 req_dur, delay;
struct iwl_hs20_roc_req aux_roc_req = {
.action = cpu_to_le32(FW_CTXT_ACTION_ADD),
.id_and_color =
@@ -3110,11 +2796,38 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
.channel_info.width = PHY_VHT_CHANNEL_MODE20,
/* Set the time and duration */
.apply_time = cpu_to_le32(iwl_read_prph(mvm->trans, time_reg)),
- .apply_time_max_delay =
- cpu_to_le32(MSEC_TO_TU(AUX_ROC_MAX_DELAY_ON_CHANNEL)),
- .duration = cpu_to_le32(MSEC_TO_TU(duration)),
};
+ delay = AUX_ROC_MIN_DELAY;
+ req_dur = MSEC_TO_TU(duration);
+
+ /*
+ * If we are associated we want the delay time to be at least one
+ * dtim interval so that the FW can wait until after the DTIM and
+ * then start the time event, this will potentially allow us to
+ * remain off-channel for the max duration.
+ * Since we want to use almost a whole dtim interval we would also
+ * like the delay to be for 2-3 dtim intervals, in case there are
+ * other time events with higher priority.
+ */
+ if (vif->bss_conf.assoc) {
+ delay = min_t(u32, dtim_interval * 3, AUX_ROC_MAX_DELAY);
+ /* We cannot remain off-channel longer than the DTIM interval */
+ if (dtim_interval <= req_dur) {
+ req_dur = dtim_interval - AUX_ROC_SAFETY_BUFFER;
+ if (req_dur <= AUX_ROC_MIN_DURATION)
+ req_dur = dtim_interval -
+ AUX_ROC_MIN_SAFETY_BUFFER;
+ }
+ }
+
+ aux_roc_req.duration = cpu_to_le32(req_dur);
+ aux_roc_req.apply_time_max_delay = cpu_to_le32(delay);
+
+ IWL_DEBUG_TE(mvm,
+ "ROC: Requesting to remain on channel %u for %ums (requested = %ums, max_delay = %ums, dtim_interval = %ums)\n",
+ channel->hw_value, req_dur, duration, delay,
+ dtim_interval);
/* Set the node address */
memcpy(aux_roc_req.node_addr, vif->addr, ETH_ALEN);
@@ -3462,6 +3175,11 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
ret = iwl_mvm_update_quotas(mvm, false, NULL);
if (ret)
goto out_remove_binding;
+
+ ret = iwl_mvm_add_snif_sta(mvm, vif);
+ if (ret)
+ goto out_remove_binding;
+
}
/* Handle binding during CSA */
@@ -3535,6 +3253,7 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
case NL80211_IFTYPE_MONITOR:
mvmvif->monitor_active = false;
mvmvif->ps_disabled = false;
+ iwl_mvm_rm_snif_sta(mvm, vif);
break;
case NL80211_IFTYPE_AP:
/* This part is triggered only during CSA */
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
index 4bde2d027dcd..5f3ac8cccf49 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -157,7 +157,7 @@ struct iwl_mvm_dump_desc {
struct iwl_fw_error_dump_trigger_desc trig_desc;
};
-extern struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert;
+extern const struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert;
struct iwl_mvm_phy_ctxt {
u16 id;
@@ -294,6 +294,7 @@ enum iwl_mvm_ref_type {
IWL_MVM_REF_EXIT_WORK,
IWL_MVM_REF_PROTECT_CSA,
IWL_MVM_REF_FW_DBG_COLLECT,
+ IWL_MVM_REF_INIT_UCODE,
/* update debugfs.c when changing this */
@@ -404,7 +405,7 @@ struct iwl_mvm_vif {
*/
struct iwl_mvm_phy_ctxt *phy_ctxt;
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
/* WoWLAN GTK rekey data */
struct {
u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN];
@@ -421,6 +422,7 @@ struct iwl_mvm_vif {
#if IS_ENABLED(CONFIG_IPV6)
/* IPv6 addresses for WoWLAN */
struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX];
+ unsigned long tentative_addrs[BITS_TO_LONGS(IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX)];
int num_target_ipv6_addrs;
#endif
@@ -475,6 +477,14 @@ enum iwl_scan_status {
IWL_MVM_SCAN_MASK = 0xff,
};
+enum iwl_mvm_scan_type {
+ IWL_SCAN_TYPE_NOT_SET,
+ IWL_SCAN_TYPE_UNASSOC,
+ IWL_SCAN_TYPE_WILD,
+ IWL_SCAN_TYPE_MILD,
+ IWL_SCAN_TYPE_FRAGMENTED,
+};
+
/**
* struct iwl_nvm_section - describes an NVM section in memory.
*
@@ -643,10 +653,14 @@ struct iwl_mvm {
unsigned int scan_status;
void *scan_cmd;
struct iwl_mcast_filter_cmd *mcast_filter_cmd;
+ enum iwl_mvm_scan_type scan_type;
/* max number of simultaneous scans the FW supports */
unsigned int max_scans;
+ /* ts of the beginning of a non-collect fw dbg data period */
+ unsigned long fw_dbg_non_collect_ts_start[FW_DBG_TRIGGER_MAX - 1];
+
/* UMAC scan tracking */
u32 scan_uid_status[IWL_MVM_MAX_UMAC_SCANS];
@@ -666,6 +680,7 @@ struct iwl_mvm {
/* Internal station */
struct iwl_mvm_int_sta aux_sta;
+ struct iwl_mvm_int_sta snif_sta;
bool last_ebs_successful;
@@ -717,8 +732,8 @@ struct iwl_mvm {
s8 restart_fw;
u8 fw_dbg_conf;
struct delayed_work fw_dump_wk;
- struct iwl_mvm_dump_desc *fw_dump_desc;
- struct iwl_fw_dbg_trigger_tlv *fw_dump_trig;
+ const struct iwl_mvm_dump_desc *fw_dump_desc;
+ const struct iwl_fw_dbg_trigger_tlv *fw_dump_trig;
#ifdef CONFIG_IWLWIFI_LEDS
struct led_classdev led;
@@ -726,12 +741,11 @@ struct iwl_mvm {
struct ieee80211_vif *p2p_device_vif;
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
struct wiphy_wowlan_support wowlan;
int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen;
/* sched scan settings for net detect */
- struct cfg80211_sched_scan_request *nd_config;
struct ieee80211_scan_ies nd_ies;
struct cfg80211_match_set *nd_match_sets;
int n_nd_match_sets;
@@ -813,8 +827,6 @@ struct iwl_mvm {
bool lar_regdom_set;
enum iwl_mcc_source mcc_src;
- u8 low_latency_agg_frame_limit;
-
/* TDLS channel switch data */
struct {
struct delayed_work dwork;
@@ -915,11 +927,9 @@ iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id)
static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm)
{
- return mvm->trans->cfg->d0i3 &&
- mvm->trans->d0i3_mode != IWL_D0I3_MODE_OFF &&
- !iwlwifi_mod_params.d0i3_disable &&
- fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_D0I3_SUPPORT);
+ return !iwlwifi_mod_params.d0i3_disable &&
+ fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_D0I3_SUPPORT);
}
static inline bool iwl_mvm_is_dqa_supported(struct iwl_mvm *mvm)
@@ -928,6 +938,19 @@ static inline bool iwl_mvm_is_dqa_supported(struct iwl_mvm *mvm)
IWL_UCODE_TLV_CAPA_DQA_SUPPORT);
}
+static inline bool iwl_mvm_enter_d0i3_on_suspend(struct iwl_mvm *mvm)
+{
+ /* For now we only use this mode to differentiate between
+ * slave transports, which handle D0i3 entry in suspend by
+ * themselves in conjunction with runtime PM D0i3. So, this
+ * function is used to check whether we need to do anything
+ * when entering suspend or if the transport layer has already
+ * done it.
+ */
+ return (mvm->trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3) &&
+ (mvm->trans->runtime_pm_mode != IWL_PLAT_PM_MODE_D0I3);
+}
+
static inline bool iwl_mvm_is_lar_supported(struct iwl_mvm *mvm)
{
bool nvm_lar = mvm->nvm_data->lar_enabled;
@@ -975,6 +998,13 @@ static inline bool iwl_mvm_is_csum_supported(struct iwl_mvm *mvm)
IWL_UCODE_TLV_CAPA_CSUM_SUPPORT);
}
+static inline bool iwl_mvm_is_mplut_supported(struct iwl_mvm *mvm)
+{
+ return fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT) &&
+ IWL_MVM_BT_COEX_MPLUT;
+}
+
static inline bool iwl_mvm_has_new_rx_api(struct iwl_mvm *mvm)
{
/* firmware flag isn't defined yet */
@@ -1108,6 +1138,11 @@ bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm,
void iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,
struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_rx_phy_cmd_mq(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb, int queue);
+void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb, int queue);
void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
@@ -1256,10 +1291,31 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, int idx);
extern const struct file_operations iwl_dbgfs_d3_test_ops;
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
+int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool host_awake,
+ u32 cmd_flags);
+void iwl_mvm_d0i3_update_keys(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_wowlan_status *status);
void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm,
struct ieee80211_vif *vif);
#else
+static inline int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool host_awake,
+ u32 cmd_flags)
+{
+ return 0;
+}
+
+static inline void iwl_mvm_d0i3_update_keys(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_wowlan_status *status)
+{
+}
+
static inline void
iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
@@ -1270,6 +1326,7 @@ void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
bool disable_offloading,
+ bool offload_ns,
u32 cmd_flags);
/* D0i3 */
@@ -1376,6 +1433,15 @@ void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
u8 tid, u8 flags);
int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 minq, u8 maxq);
+/* Return a bitmask with all the hw supported queues, except for the
+ * command queue, which can't be flushed.
+ */
+static inline u32 iwl_mvm_flushable_queues(struct iwl_mvm *mvm)
+{
+ return ((BIT(mvm->cfg->base_params->num_of_queues) - 1) &
+ ~BIT(IWL_MVM_CMD_QUEUE));
+}
+
static inline
void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue,
u8 fifo, u16 ssn, unsigned int wdg_timeout)
@@ -1468,68 +1534,10 @@ void iwl_mvm_tdls_ch_switch_work(struct work_struct *work);
struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm);
void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error);
-void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm);
-
-int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 id);
-int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
- const char *str, size_t len,
- struct iwl_fw_dbg_trigger_tlv *trigger);
-int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm,
- struct iwl_mvm_dump_desc *desc,
- struct iwl_fw_dbg_trigger_tlv *trigger);
-void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm);
-int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
- struct iwl_fw_dbg_trigger_tlv *trigger,
- const char *fmt, ...) __printf(3, 4);
unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
bool tdls, bool cmd_q);
void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
const char *errmsg);
-static inline bool
-iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig,
- struct ieee80211_vif *vif)
-{
- u32 trig_vif = le32_to_cpu(trig->vif_type);
-
- return trig_vif == IWL_FW_DBG_CONF_VIF_ANY || vif->type == trig_vif;
-}
-
-static inline bool
-iwl_fw_dbg_trigger_stop_conf_match(struct iwl_mvm *mvm,
- struct iwl_fw_dbg_trigger_tlv *trig)
-{
- return ((trig->mode & IWL_FW_DBG_TRIGGER_STOP) &&
- (mvm->fw_dbg_conf == FW_DBG_INVALID ||
- (BIT(mvm->fw_dbg_conf) & le32_to_cpu(trig->stop_conf_ids))));
-}
-
-static inline bool
-iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct iwl_fw_dbg_trigger_tlv *trig)
-{
- if (vif && !iwl_fw_dbg_trigger_vif_match(trig, vif))
- return false;
-
- return iwl_fw_dbg_trigger_stop_conf_match(mvm, trig);
-}
-
-static inline void
-iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- enum iwl_fw_dbg_trigger trig)
-{
- struct iwl_fw_dbg_trigger_tlv *trigger;
-
- if (!iwl_fw_dbg_trigger_enabled(mvm->fw, trig))
- return;
-
- trigger = iwl_fw_dbg_get_trigger(mvm->fw, trig);
- if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger))
- return;
-
- iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL);
-}
#endif /* __IWL_MVM_H__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c
index 2ee0f6fe56a1..7a3da2da6fd0 100644
--- a/drivers/net/wireless/iwlwifi/mvm/nvm.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c
@@ -7,6 +7,7 @@
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -26,13 +27,14 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -104,13 +106,35 @@ static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section,
struct iwl_host_cmd cmd = {
.id = NVM_ACCESS_CMD,
.len = { sizeof(struct iwl_nvm_access_cmd), length },
- .flags = CMD_SEND_IN_RFKILL,
+ .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
.data = { &nvm_access_cmd, data },
/* data may come from vmalloc, so use _DUP */
.dataflags = { 0, IWL_HCMD_DFL_DUP },
};
+ struct iwl_rx_packet *pkt;
+ struct iwl_nvm_access_resp *nvm_resp;
+ int ret;
- return iwl_mvm_send_cmd(mvm, &cmd);
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+ if (ret)
+ return ret;
+
+ pkt = cmd.resp_pkt;
+ if (!pkt) {
+ IWL_ERR(mvm, "Error in NVM_ACCESS response\n");
+ return -EINVAL;
+ }
+ /* Extract & check NVM write response */
+ nvm_resp = (void *)pkt->data;
+ if (le16_to_cpu(nvm_resp->status) != READ_NVM_CHUNK_SUCCEED) {
+ IWL_ERR(mvm,
+ "NVM access write command failed for section %u (status = 0x%x)\n",
+ section, le16_to_cpu(nvm_resp->status));
+ ret = -EIO;
+ }
+
+ iwl_free_resp(&cmd);
+ return ret;
}
static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
@@ -210,6 +234,19 @@ static int iwl_nvm_write_section(struct iwl_mvm *mvm, u16 section,
return 0;
}
+static void iwl_mvm_nvm_fixups(struct iwl_mvm *mvm, unsigned int section,
+ u8 *data, unsigned int len)
+{
+#define IWL_4165_DEVICE_ID 0x5501
+#define NVM_SKU_CAP_MIMO_DISABLE BIT(5)
+
+ if (section == NVM_SECTION_TYPE_PHY_SKU &&
+ mvm->trans->hw_id == IWL_4165_DEVICE_ID && data && len >= 5 &&
+ (data[4] & NVM_SKU_CAP_MIMO_DISABLE))
+ /* OTP 0x52 bug work around: it's a 1x1 device */
+ data[3] = ANT_B | (ANT_B << 4);
+}
+
/*
* Reads an NVM section completely.
* NICs prior to 7000 family doesn't have a real NVM, but just read
@@ -250,6 +287,8 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
offset += ret;
}
+ iwl_mvm_nvm_fixups(mvm, section, data, offset);
+
IWL_DEBUG_EEPROM(mvm->trans->dev,
"NVM section %d read completed\n", section);
return offset;
@@ -316,8 +355,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib,
regulatory, mac_override, phy_sku,
mvm->fw->valid_tx_ant, mvm->fw->valid_rx_ant,
- lar_enabled, mac_addr0, mac_addr1,
- mvm->trans->hw_id);
+ lar_enabled, mac_addr0, mac_addr1);
}
#define MAX_NVM_FILE_LEN 16384
@@ -353,7 +391,8 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
__le16 word2;
u8 data[];
} *file_sec;
- const u8 *eof, *temp;
+ const u8 *eof;
+ u8 *temp;
int max_section_size;
const __le32 *dword_buff;
@@ -483,6 +522,9 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
ret = -ENOMEM;
break;
}
+
+ iwl_mvm_nvm_fixups(mvm, section_id, temp, section_size);
+
kfree(mvm->nvm_sections[section_id].data);
mvm->nvm_sections[section_id].data = temp;
mvm->nvm_sections[section_id].length = section_size;
@@ -548,6 +590,9 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)
ret = -ENOMEM;
break;
}
+
+ iwl_mvm_nvm_fixups(mvm, section, temp, ret);
+
mvm->nvm_sections[section].data = temp;
mvm->nvm_sections[section].length = ret;
@@ -597,7 +642,8 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)
else
mvm->nvm_file_name = nvm_file_C;
- if (ret == -EFAULT && mvm->nvm_file_name) {
+ if ((ret == -EFAULT || ret == -ENOENT) &&
+ mvm->nvm_file_name) {
/* in case nvm file was failed try again */
ret = iwl_mvm_read_external_nvm(mvm);
if (ret)
@@ -627,6 +673,7 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
.source_id = (u8)src_id,
};
struct iwl_mcc_update_resp *mcc_resp, *resp_cp = NULL;
+ struct iwl_mcc_update_resp_v1 *mcc_resp_v1 = NULL;
struct iwl_rx_packet *pkt;
struct iwl_host_cmd cmd = {
.id = MCC_UPDATE_CMD,
@@ -638,11 +685,15 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
u32 status;
int resp_len, n_channels;
u16 mcc;
+ bool resp_v2 = fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2);
if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm)))
return ERR_PTR(-EOPNOTSUPP);
cmd.len[0] = sizeof(struct iwl_mcc_update_cmd);
+ if (!resp_v2)
+ cmd.len[0] = sizeof(struct iwl_mcc_update_cmd_v1);
IWL_DEBUG_LAR(mvm, "send MCC update to FW with '%c%c' src = %d\n",
alpha2[0], alpha2[1], src_id);
@@ -654,31 +705,50 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
pkt = cmd.resp_pkt;
/* Extract MCC response */
- mcc_resp = (void *)pkt->data;
- status = le32_to_cpu(mcc_resp->status);
+ if (resp_v2) {
+ mcc_resp = (void *)pkt->data;
+ n_channels = __le32_to_cpu(mcc_resp->n_channels);
+ } else {
+ mcc_resp_v1 = (void *)pkt->data;
+ n_channels = __le32_to_cpu(mcc_resp_v1->n_channels);
+ }
+
+ resp_len = sizeof(struct iwl_mcc_update_resp) + n_channels *
+ sizeof(__le32);
+
+ resp_cp = kzalloc(resp_len, GFP_KERNEL);
+ if (!resp_cp) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ if (resp_v2) {
+ memcpy(resp_cp, mcc_resp, resp_len);
+ } else {
+ resp_cp->status = mcc_resp_v1->status;
+ resp_cp->mcc = mcc_resp_v1->mcc;
+ resp_cp->cap = mcc_resp_v1->cap;
+ resp_cp->source_id = mcc_resp_v1->source_id;
+ resp_cp->n_channels = mcc_resp_v1->n_channels;
+ memcpy(resp_cp->channels, mcc_resp_v1->channels,
+ n_channels * sizeof(__le32));
+ }
+
+ status = le32_to_cpu(resp_cp->status);
- mcc = le16_to_cpu(mcc_resp->mcc);
+ mcc = le16_to_cpu(resp_cp->mcc);
/* W/A for a FW/NVM issue - returns 0x00 for the world domain */
if (mcc == 0) {
mcc = 0x3030; /* "00" - world */
- mcc_resp->mcc = cpu_to_le16(mcc);
+ resp_cp->mcc = cpu_to_le16(mcc);
}
- n_channels = __le32_to_cpu(mcc_resp->n_channels);
IWL_DEBUG_LAR(mvm,
"MCC response status: 0x%x. new MCC: 0x%x ('%c%c') change: %d n_chans: %d\n",
status, mcc, mcc >> 8, mcc & 0xff,
!!(status == MCC_RESP_NEW_CHAN_PROFILE), n_channels);
- resp_len = sizeof(*mcc_resp) + n_channels * sizeof(__le32);
- resp_cp = kmemdup(mcc_resp, resp_len, GFP_KERNEL);
- if (!resp_cp) {
- ret = -ENOMEM;
- goto exit;
- }
-
- ret = 0;
exit:
iwl_free_resp(&cmd);
if (ret)
diff --git a/drivers/net/wireless/iwlwifi/mvm/offloading.c b/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c
index 68b0169c8892..6338d9cf7070 100644
--- a/drivers/net/wireless/iwlwifi/mvm/offloading.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c
@@ -7,6 +7,7 @@
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -26,13 +27,14 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -64,6 +66,7 @@
*****************************************************************************/
#include <net/ipv6.h>
#include <net/addrconf.h>
+#include <linux/bitops.h>
#include "mvm.h"
void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
@@ -86,6 +89,7 @@ void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
bool disable_offloading,
+ bool offload_ns,
u32 cmd_flags)
{
union {
@@ -106,6 +110,13 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
#if IS_ENABLED(CONFIG_IPV6)
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
int i;
+ /*
+ * Skip tentative address when ns offload is enabled to avoid
+ * violating RFC4862.
+ * Keep tentative address when ns offload is disabled so the NS packets
+ * will not be filtered out and will wake up the host.
+ */
+ bool skip_tentative = offload_ns;
if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL ||
capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
@@ -113,6 +124,7 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
struct iwl_targ_addr *addrs;
int n_nsc, n_addrs;
int c;
+ int num_skipped = 0;
if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
nsc = cmd.v3s.ns_config;
@@ -126,9 +138,6 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L;
}
- if (mvmvif->num_target_ipv6_addrs)
- enabled |= IWL_D3_PROTO_OFFLOAD_NS;
-
/*
* For each address we have (and that will fit) fill a target
* address struct and combine for NS offload structs with the
@@ -140,6 +149,12 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
struct in6_addr solicited_addr;
int j;
+ if (skip_tentative &&
+ test_bit(i, mvmvif->tentative_addrs)) {
+ num_skipped++;
+ continue;
+ }
+
addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i],
&solicited_addr);
for (j = 0; j < c; j++)
@@ -154,41 +169,64 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN);
}
+ if (mvmvif->num_target_ipv6_addrs - num_skipped)
+ enabled |= IWL_D3_PROTO_IPV6_VALID;
+
if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL)
- cmd.v3s.num_valid_ipv6_addrs = cpu_to_le32(i);
+ cmd.v3s.num_valid_ipv6_addrs =
+ cpu_to_le32(i - num_skipped);
else
- cmd.v3l.num_valid_ipv6_addrs = cpu_to_le32(i);
+ cmd.v3l.num_valid_ipv6_addrs =
+ cpu_to_le32(i - num_skipped);
} else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
- if (mvmvif->num_target_ipv6_addrs) {
- enabled |= IWL_D3_PROTO_OFFLOAD_NS;
- memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN);
- }
+ bool found = false;
BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) !=
sizeof(mvmvif->target_ipv6_addrs[0]));
for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
- IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++)
+ IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++) {
+ if (skip_tentative &&
+ test_bit(i, mvmvif->tentative_addrs))
+ continue;
+
memcpy(cmd.v2.target_ipv6_addr[i],
&mvmvif->target_ipv6_addrs[i],
sizeof(cmd.v2.target_ipv6_addr[i]));
- } else {
- if (mvmvif->num_target_ipv6_addrs) {
- enabled |= IWL_D3_PROTO_OFFLOAD_NS;
- memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN);
- }
+ found = true;
+ }
+ if (found) {
+ enabled |= IWL_D3_PROTO_IPV6_VALID;
+ memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN);
+ }
+ } else {
+ bool found = false;
BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) !=
sizeof(mvmvif->target_ipv6_addrs[0]));
for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
- IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++)
+ IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++) {
+ if (skip_tentative &&
+ test_bit(i, mvmvif->tentative_addrs))
+ continue;
+
memcpy(cmd.v1.target_ipv6_addr[i],
&mvmvif->target_ipv6_addrs[i],
sizeof(cmd.v1.target_ipv6_addr[i]));
+
+ found = true;
+ }
+
+ if (found) {
+ enabled |= IWL_D3_PROTO_IPV6_VALID;
+ memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN);
+ }
}
-#endif
+ if (offload_ns && (enabled & IWL_D3_PROTO_IPV6_VALID))
+ enabled |= IWL_D3_PROTO_OFFLOAD_NS;
+#endif
if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
common = &cmd.v3s.common;
size = sizeof(cmd.v3s);
@@ -204,7 +242,7 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
}
if (vif->bss_conf.arp_addr_cnt) {
- enabled |= IWL_D3_PROTO_OFFLOAD_ARP;
+ enabled |= IWL_D3_PROTO_OFFLOAD_ARP | IWL_D3_PROTO_IPV4_VALID;
common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0];
memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN);
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
index 13c97f665ba8..89ea70deeb84 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -82,6 +82,9 @@
#include "rs.h"
#include "fw-api-scan.h"
#include "time-event.h"
+#include "fw-dbg.h"
+#include "fw-api.h"
+#include "fw-api-scan.h"
#define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux"
MODULE_DESCRIPTION(DRV_DESCRIPTION);
@@ -268,102 +271,127 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
};
#undef RX_HANDLER
#undef RX_HANDLER_GRP
-#define CMD(x) [x] = #x
-
-static const char *const iwl_mvm_cmd_strings[REPLY_MAX + 1] = {
- CMD(MVM_ALIVE),
- CMD(REPLY_ERROR),
- CMD(ECHO_CMD),
- CMD(INIT_COMPLETE_NOTIF),
- CMD(PHY_CONTEXT_CMD),
- CMD(MGMT_MCAST_KEY),
- CMD(TX_CMD),
- CMD(TXPATH_FLUSH),
- CMD(SHARED_MEM_CFG),
- CMD(MAC_CONTEXT_CMD),
- CMD(TIME_EVENT_CMD),
- CMD(TIME_EVENT_NOTIFICATION),
- CMD(BINDING_CONTEXT_CMD),
- CMD(TIME_QUOTA_CMD),
- CMD(NON_QOS_TX_COUNTER_CMD),
- CMD(DC2DC_CONFIG_CMD),
- CMD(NVM_ACCESS_CMD),
- CMD(PHY_CONFIGURATION_CMD),
- CMD(CALIB_RES_NOTIF_PHY_DB),
- CMD(SET_CALIB_DEFAULT_CMD),
- CMD(FW_PAGING_BLOCK_CMD),
- CMD(ADD_STA_KEY),
- CMD(ADD_STA),
- CMD(FW_GET_ITEM_CMD),
- CMD(REMOVE_STA),
- CMD(LQ_CMD),
- CMD(SCAN_OFFLOAD_CONFIG_CMD),
- CMD(MATCH_FOUND_NOTIFICATION),
- CMD(SCAN_OFFLOAD_REQUEST_CMD),
- CMD(SCAN_OFFLOAD_ABORT_CMD),
- CMD(HOT_SPOT_CMD),
- CMD(SCAN_OFFLOAD_COMPLETE),
- CMD(SCAN_OFFLOAD_UPDATE_PROFILES_CMD),
- CMD(SCAN_ITERATION_COMPLETE),
- CMD(POWER_TABLE_CMD),
- CMD(WEP_KEY),
- CMD(REPLY_RX_PHY_CMD),
- CMD(REPLY_RX_MPDU_CMD),
- CMD(BEACON_NOTIFICATION),
- CMD(BEACON_TEMPLATE_CMD),
- CMD(STATISTICS_CMD),
- CMD(STATISTICS_NOTIFICATION),
- CMD(EOSP_NOTIFICATION),
- CMD(REDUCE_TX_POWER_CMD),
- CMD(TX_ANT_CONFIGURATION_CMD),
- CMD(D3_CONFIG_CMD),
- CMD(D0I3_END_CMD),
- CMD(PROT_OFFLOAD_CONFIG_CMD),
- CMD(OFFLOADS_QUERY_CMD),
- CMD(REMOTE_WAKE_CONFIG_CMD),
- CMD(WOWLAN_PATTERNS),
- CMD(WOWLAN_CONFIGURATION),
- CMD(WOWLAN_TSC_RSC_PARAM),
- CMD(WOWLAN_TKIP_PARAM),
- CMD(WOWLAN_KEK_KCK_MATERIAL),
- CMD(WOWLAN_GET_STATUSES),
- CMD(WOWLAN_TX_POWER_PER_DB),
- CMD(SCAN_OFFLOAD_PROFILES_QUERY_CMD),
- CMD(SCAN_OFFLOAD_HOTSPOTS_CONFIG_CMD),
- CMD(SCAN_OFFLOAD_HOTSPOTS_QUERY_CMD),
- CMD(CARD_STATE_NOTIFICATION),
- CMD(MISSED_BEACONS_NOTIFICATION),
- CMD(BT_COEX_PRIO_TABLE),
- CMD(BT_COEX_PROT_ENV),
- CMD(BT_PROFILE_NOTIFICATION),
- CMD(BT_CONFIG),
- CMD(MCAST_FILTER_CMD),
- CMD(BCAST_FILTER_CMD),
- CMD(REPLY_SF_CFG_CMD),
- CMD(REPLY_BEACON_FILTERING_CMD),
- CMD(CMD_DTS_MEASUREMENT_TRIGGER),
- CMD(DTS_MEASUREMENT_NOTIFICATION),
- CMD(REPLY_THERMAL_MNG_BACKOFF),
- CMD(MAC_PM_POWER_TABLE),
- CMD(LTR_CONFIG),
- CMD(BT_COEX_CI),
- CMD(BT_COEX_UPDATE_SW_BOOST),
- CMD(BT_COEX_UPDATE_CORUN_LUT),
- CMD(BT_COEX_UPDATE_REDUCED_TXP),
- CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),
- CMD(ANTENNA_COUPLING_NOTIFICATION),
- CMD(SCD_QUEUE_CFG),
- CMD(SCAN_CFG_CMD),
- CMD(SCAN_REQ_UMAC),
- CMD(SCAN_ABORT_UMAC),
- CMD(SCAN_COMPLETE_UMAC),
- CMD(TDLS_CHANNEL_SWITCH_CMD),
- CMD(TDLS_CHANNEL_SWITCH_NOTIFICATION),
- CMD(TDLS_CONFIG_CMD),
- CMD(MCC_UPDATE_CMD),
- CMD(SCAN_ITERATION_COMPLETE_UMAC),
+
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search
+ */
+static const struct iwl_hcmd_names iwl_mvm_legacy_names[] = {
+ HCMD_NAME(MVM_ALIVE),
+ HCMD_NAME(REPLY_ERROR),
+ HCMD_NAME(ECHO_CMD),
+ HCMD_NAME(INIT_COMPLETE_NOTIF),
+ HCMD_NAME(PHY_CONTEXT_CMD),
+ HCMD_NAME(DBG_CFG),
+ HCMD_NAME(ANTENNA_COUPLING_NOTIFICATION),
+ HCMD_NAME(SCAN_CFG_CMD),
+ HCMD_NAME(SCAN_REQ_UMAC),
+ HCMD_NAME(SCAN_ABORT_UMAC),
+ HCMD_NAME(SCAN_COMPLETE_UMAC),
+ HCMD_NAME(TOF_CMD),
+ HCMD_NAME(TOF_NOTIFICATION),
+ HCMD_NAME(ADD_STA_KEY),
+ HCMD_NAME(ADD_STA),
+ HCMD_NAME(REMOVE_STA),
+ HCMD_NAME(FW_GET_ITEM_CMD),
+ HCMD_NAME(TX_CMD),
+ HCMD_NAME(SCD_QUEUE_CFG),
+ HCMD_NAME(TXPATH_FLUSH),
+ HCMD_NAME(MGMT_MCAST_KEY),
+ HCMD_NAME(WEP_KEY),
+ HCMD_NAME(SHARED_MEM_CFG),
+ HCMD_NAME(TDLS_CHANNEL_SWITCH_CMD),
+ HCMD_NAME(MAC_CONTEXT_CMD),
+ HCMD_NAME(TIME_EVENT_CMD),
+ HCMD_NAME(TIME_EVENT_NOTIFICATION),
+ HCMD_NAME(BINDING_CONTEXT_CMD),
+ HCMD_NAME(TIME_QUOTA_CMD),
+ HCMD_NAME(NON_QOS_TX_COUNTER_CMD),
+ HCMD_NAME(LQ_CMD),
+ HCMD_NAME(FW_PAGING_BLOCK_CMD),
+ HCMD_NAME(SCAN_OFFLOAD_REQUEST_CMD),
+ HCMD_NAME(SCAN_OFFLOAD_ABORT_CMD),
+ HCMD_NAME(HOT_SPOT_CMD),
+ HCMD_NAME(SCAN_OFFLOAD_PROFILES_QUERY_CMD),
+ HCMD_NAME(SCAN_OFFLOAD_HOTSPOTS_CONFIG_CMD),
+ HCMD_NAME(SCAN_OFFLOAD_HOTSPOTS_QUERY_CMD),
+ HCMD_NAME(BT_COEX_UPDATE_SW_BOOST),
+ HCMD_NAME(BT_COEX_UPDATE_CORUN_LUT),
+ HCMD_NAME(BT_COEX_UPDATE_REDUCED_TXP),
+ HCMD_NAME(BT_COEX_CI),
+ HCMD_NAME(PHY_CONFIGURATION_CMD),
+ HCMD_NAME(CALIB_RES_NOTIF_PHY_DB),
+ HCMD_NAME(SCAN_OFFLOAD_COMPLETE),
+ HCMD_NAME(SCAN_OFFLOAD_UPDATE_PROFILES_CMD),
+ HCMD_NAME(SCAN_OFFLOAD_CONFIG_CMD),
+ HCMD_NAME(POWER_TABLE_CMD),
+ HCMD_NAME(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),
+ HCMD_NAME(REPLY_THERMAL_MNG_BACKOFF),
+ HCMD_NAME(DC2DC_CONFIG_CMD),
+ HCMD_NAME(NVM_ACCESS_CMD),
+ HCMD_NAME(SET_CALIB_DEFAULT_CMD),
+ HCMD_NAME(BEACON_NOTIFICATION),
+ HCMD_NAME(BEACON_TEMPLATE_CMD),
+ HCMD_NAME(TX_ANT_CONFIGURATION_CMD),
+ HCMD_NAME(BT_CONFIG),
+ HCMD_NAME(STATISTICS_CMD),
+ HCMD_NAME(STATISTICS_NOTIFICATION),
+ HCMD_NAME(EOSP_NOTIFICATION),
+ HCMD_NAME(REDUCE_TX_POWER_CMD),
+ HCMD_NAME(CARD_STATE_CMD),
+ HCMD_NAME(CARD_STATE_NOTIFICATION),
+ HCMD_NAME(MISSED_BEACONS_NOTIFICATION),
+ HCMD_NAME(TDLS_CONFIG_CMD),
+ HCMD_NAME(MAC_PM_POWER_TABLE),
+ HCMD_NAME(TDLS_CHANNEL_SWITCH_NOTIFICATION),
+ HCMD_NAME(MFUART_LOAD_NOTIFICATION),
+ HCMD_NAME(SCAN_ITERATION_COMPLETE_UMAC),
+ HCMD_NAME(REPLY_RX_PHY_CMD),
+ HCMD_NAME(REPLY_RX_MPDU_CMD),
+ HCMD_NAME(BA_NOTIF),
+ HCMD_NAME(MCC_UPDATE_CMD),
+ HCMD_NAME(MCC_CHUB_UPDATE_CMD),
+ HCMD_NAME(MARKER_CMD),
+ HCMD_NAME(BT_COEX_PRIO_TABLE),
+ HCMD_NAME(BT_COEX_PROT_ENV),
+ HCMD_NAME(BT_PROFILE_NOTIFICATION),
+ HCMD_NAME(BCAST_FILTER_CMD),
+ HCMD_NAME(MCAST_FILTER_CMD),
+ HCMD_NAME(REPLY_SF_CFG_CMD),
+ HCMD_NAME(REPLY_BEACON_FILTERING_CMD),
+ HCMD_NAME(D3_CONFIG_CMD),
+ HCMD_NAME(PROT_OFFLOAD_CONFIG_CMD),
+ HCMD_NAME(OFFLOADS_QUERY_CMD),
+ HCMD_NAME(REMOTE_WAKE_CONFIG_CMD),
+ HCMD_NAME(MATCH_FOUND_NOTIFICATION),
+ HCMD_NAME(CMD_DTS_MEASUREMENT_TRIGGER),
+ HCMD_NAME(DTS_MEASUREMENT_NOTIFICATION),
+ HCMD_NAME(WOWLAN_PATTERNS),
+ HCMD_NAME(WOWLAN_CONFIGURATION),
+ HCMD_NAME(WOWLAN_TSC_RSC_PARAM),
+ HCMD_NAME(WOWLAN_TKIP_PARAM),
+ HCMD_NAME(WOWLAN_KEK_KCK_MATERIAL),
+ HCMD_NAME(WOWLAN_GET_STATUSES),
+ HCMD_NAME(WOWLAN_TX_POWER_PER_DB),
+ HCMD_NAME(SCAN_ITERATION_COMPLETE),
+ HCMD_NAME(D0I3_END_CMD),
+ HCMD_NAME(LTR_CONFIG),
+ HCMD_NAME(REPLY_DEBUG_CMD),
+};
+
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search
+ */
+static const struct iwl_hcmd_names iwl_mvm_phy_names[] = {
+ HCMD_NAME(CMD_DTS_MEASUREMENT_TRIGGER_WIDE),
+ HCMD_NAME(DTS_MEASUREMENT_NOTIF_WIDE),
};
-#undef CMD
+
+static const struct iwl_hcmd_arr iwl_mvm_groups[] = {
+ [LEGACY_GROUP] = HCMD_ARR(iwl_mvm_legacy_names),
+ [LONG_GROUP] = HCMD_ARR(iwl_mvm_legacy_names),
+ [PHY_OPS_GROUP] = HCMD_ARR(iwl_mvm_phy_names),
+};
+
/* this forward declaration can avoid to export the function */
static void iwl_mvm_async_handlers_wk(struct work_struct *wk);
@@ -452,7 +480,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
mvm->first_agg_queue = 12;
}
mvm->sf_state = SF_UNINIT;
- mvm->low_latency_agg_frame_limit = 6;
mvm->cur_ucode = IWL_UCODE_INIT;
mutex_init(&mvm->mutex);
@@ -485,20 +512,36 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
trans_cfg.op_mode = op_mode;
trans_cfg.no_reclaim_cmds = no_reclaim_cmds;
trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
- trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K;
+ switch (iwlwifi_mod_params.amsdu_size) {
+ case IWL_AMSDU_4K:
+ trans_cfg.rx_buf_size = IWL_AMSDU_4K;
+ break;
+ case IWL_AMSDU_8K:
+ trans_cfg.rx_buf_size = IWL_AMSDU_8K;
+ break;
+ case IWL_AMSDU_12K:
+ trans_cfg.rx_buf_size = IWL_AMSDU_12K;
+ break;
+ default:
+ pr_err("%s: Unsupported amsdu_size: %d\n", KBUILD_MODNAME,
+ iwlwifi_mod_params.amsdu_size);
+ trans_cfg.rx_buf_size = IWL_AMSDU_4K;
+ }
trans_cfg.wide_cmd_header = fw_has_api(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_API_WIDE_CMD_HDR);
if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DW_BC_TABLE)
trans_cfg.bc_table_dword = true;
- trans_cfg.command_names = iwl_mvm_cmd_strings;
+ trans_cfg.command_groups = iwl_mvm_groups;
+ trans_cfg.command_groups_size = ARRAY_SIZE(iwl_mvm_groups);
trans_cfg.cmd_queue = IWL_MVM_CMD_QUEUE;
trans_cfg.cmd_fifo = IWL_MVM_TX_FIFO_CMD;
trans_cfg.scd_set_active = true;
trans_cfg.sdio_adma_addr = fw->sdio_adma_addr;
+ trans_cfg.sw_csum_tx = IWL_MVM_SW_TX_CSUM_OFFLOAD;
/* Set a short watchdog for the command queue */
trans_cfg.cmd_q_wdg_timeout =
@@ -561,9 +604,11 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
goto out_free;
mutex_lock(&mvm->mutex);
+ iwl_mvm_ref(mvm, IWL_MVM_REF_INIT_UCODE);
err = iwl_run_init_mvm_ucode(mvm, true);
if (!err || !iwlmvm_mod_params.init_dbg)
iwl_trans_stop_device(trans);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_INIT_UCODE);
mutex_unlock(&mvm->mutex);
/* returns 0 if successful, 1 if success but in rfkill */
if (err < 0 && !iwlmvm_mod_params.init_dbg) {
@@ -591,8 +636,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx));
- /* rpm starts with a taken ref. only set the appropriate bit here. */
- mvm->refs[IWL_MVM_REF_UCODE_DOWN] = 1;
+ /* rpm starts with a taken reference, we can release it now */
+ iwl_trans_unref(mvm->trans);
iwl_mvm_tof_init(mvm);
@@ -628,12 +673,6 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS)
kfree(mvm->d3_resume_sram);
- if (mvm->nd_config) {
- kfree(mvm->nd_config->match_sets);
- kfree(mvm->nd_config->scan_plans);
- kfree(mvm->nd_config);
- mvm->nd_config = NULL;
- }
#endif
iwl_trans_op_mode_leave(mvm->trans);
@@ -783,6 +822,8 @@ static void iwl_mvm_rx(struct iwl_op_mode *op_mode,
if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD))
iwl_mvm_rx_rx_mpdu(mvm, napi, rxb);
+ else if (pkt->hdr.cmd == FRAME_RELEASE)
+ iwl_mvm_rx_frame_release(mvm, rxb, 0);
else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD)
iwl_mvm_rx_rx_phy_cmd(mvm, rxb);
else
@@ -797,9 +838,9 @@ static void iwl_mvm_rx_mq(struct iwl_op_mode *op_mode,
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD))
- iwl_mvm_rx_rx_mpdu(mvm, napi, rxb);
+ iwl_mvm_rx_mpdu_mq(mvm, napi, rxb, 0);
else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD)
- iwl_mvm_rx_rx_phy_cmd(mvm, rxb);
+ iwl_mvm_rx_phy_cmd_mq(mvm, rxb);
else
iwl_mvm_rx_common(mvm, rxb, pkt);
}
@@ -829,6 +870,18 @@ static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int queue)
}
}
+static void iwl_mvm_async_cb(struct iwl_op_mode *op_mode,
+ const struct iwl_device_cmd *cmd)
+{
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+
+ /*
+ * For now, we only set the CMD_WANT_ASYNC_CALLBACK for ADD_STA
+ * commands that need to block the Tx queues.
+ */
+ iwl_trans_block_txq_ptrs(mvm->trans, false);
+}
+
static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue)
{
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
@@ -1023,6 +1076,7 @@ static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode)
struct iwl_d0i3_iter_data {
struct iwl_mvm *mvm;
+ struct ieee80211_vif *connected_vif;
u8 ap_sta_id;
u8 vif_count;
u8 offloading_tid;
@@ -1103,7 +1157,8 @@ static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac,
data->disable_offloading = true;
iwl_mvm_update_d0i3_power_mode(mvm, vif, true, flags);
- iwl_mvm_send_proto_offload(mvm, vif, data->disable_offloading, flags);
+ iwl_mvm_send_proto_offload(mvm, vif, data->disable_offloading,
+ false, flags);
/*
* on init/association, mvm already configures POWER_TABLE_CMD
@@ -1113,6 +1168,12 @@ static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac,
*/
data->ap_sta_id = mvmvif->ap_sta_id;
data->vif_count++;
+
+ /*
+ * no new commands can be sent at this stage, so it's safe
+ * to save the vif pointer during d0i3 entrance.
+ */
+ data->connected_vif = vif;
}
static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm,
@@ -1134,7 +1195,8 @@ static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm,
mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta);
cmd->is_11n_connection = ap_sta->ht_cap.ht_supported;
cmd->offloading_tid = iter_data->offloading_tid;
-
+ cmd->flags = ENABLE_L3_FILTERING | ENABLE_NBNS_FILTERING |
+ ENABLE_DHCP_FILTERING;
/*
* The d0i3 uCode takes care of the nonqos counters,
* so configure only the qos seq ones.
@@ -1165,6 +1227,9 @@ int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
IWL_DEBUG_RPM(mvm, "MVM entering D0i3\n");
+ if (WARN_ON_ONCE(mvm->cur_ucode != IWL_UCODE_REGULAR))
+ return -EINVAL;
+
set_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
/*
@@ -1196,8 +1261,17 @@ int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
/* make sure we have no running tx while configuring the seqno */
synchronize_net();
+ /* Flush the hw queues, in case something got queued during entry */
+ ret = iwl_mvm_flush_tx_path(mvm, iwl_mvm_flushable_queues(mvm), flags);
+ if (ret)
+ return ret;
+
/* configure wowlan configuration only if needed */
if (mvm->d0i3_ap_sta_id != IWL_MVM_STATION_COUNT) {
+ iwl_mvm_wowlan_config_key_params(mvm,
+ d0i3_iter_data.connected_vif,
+ true, flags);
+
iwl_mvm_set_wowlan_data(mvm, &wowlan_config_cmd,
&d0i3_iter_data);
@@ -1227,25 +1301,30 @@ static void iwl_mvm_exit_d0i3_iterator(void *_data, u8 *mac,
iwl_mvm_update_d0i3_power_mode(mvm, vif, false, flags);
}
-struct iwl_mvm_wakeup_reason_iter_data {
+struct iwl_mvm_d0i3_exit_work_iter_data {
struct iwl_mvm *mvm;
+ struct iwl_wowlan_status *status;
u32 wakeup_reasons;
};
-static void iwl_mvm_d0i3_wakeup_reason_iter(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
+static void iwl_mvm_d0i3_exit_work_iter(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
{
- struct iwl_mvm_wakeup_reason_iter_data *data = _data;
+ struct iwl_mvm_d0i3_exit_work_iter_data *data = _data;
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ u32 reasons = data->wakeup_reasons;
- if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc &&
- data->mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id) {
- if (data->wakeup_reasons &
- IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH)
- iwl_mvm_connection_loss(data->mvm, vif, "D0i3");
- else
- ieee80211_beacon_loss(vif);
- }
+ /* consider only the relevant station interface */
+ if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc ||
+ data->mvm->d0i3_ap_sta_id != mvmvif->ap_sta_id)
+ return;
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH)
+ iwl_mvm_connection_loss(data->mvm, vif, "D0i3");
+ else if (reasons & IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON)
+ ieee80211_beacon_loss(vif);
+ else
+ iwl_mvm_d0i3_update_keys(data->mvm, vif, data->status);
}
void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq)
@@ -1311,9 +1390,13 @@ static void iwl_mvm_d0i3_exit_work(struct work_struct *wk)
.id = WOWLAN_GET_STATUSES,
.flags = CMD_HIGH_PRIO | CMD_WANT_SKB,
};
+ struct iwl_mvm_d0i3_exit_work_iter_data iter_data = {
+ .mvm = mvm,
+ };
+
struct iwl_wowlan_status *status;
int ret;
- u32 handled_reasons, wakeup_reasons = 0;
+ u32 wakeup_reasons = 0;
__le16 *qos_seq = NULL;
mutex_lock(&mvm->mutex);
@@ -1330,18 +1413,12 @@ static void iwl_mvm_d0i3_exit_work(struct work_struct *wk)
IWL_DEBUG_RPM(mvm, "wakeup reasons: 0x%x\n", wakeup_reasons);
- handled_reasons = IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON |
- IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH;
- if (wakeup_reasons & handled_reasons) {
- struct iwl_mvm_wakeup_reason_iter_data data = {
- .mvm = mvm,
- .wakeup_reasons = wakeup_reasons,
- };
-
- ieee80211_iterate_active_interfaces(
- mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_d0i3_wakeup_reason_iter, &data);
- }
+ iter_data.wakeup_reasons = wakeup_reasons;
+ iter_data.status = status;
+ ieee80211_iterate_active_interfaces(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_d0i3_exit_work_iter,
+ &iter_data);
out:
iwl_mvm_d0i3_enable_tx(mvm, qos_seq);
@@ -1367,6 +1444,9 @@ int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm)
IWL_DEBUG_RPM(mvm, "MVM exiting D0i3\n");
+ if (WARN_ON_ONCE(mvm->cur_ucode != IWL_UCODE_REGULAR))
+ return -EINVAL;
+
mutex_lock(&mvm->d0i3_suspend_mutex);
if (test_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags)) {
IWL_DEBUG_RPM(mvm, "Deferring d0i3 exit until resume\n");
@@ -1399,6 +1479,7 @@ int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode)
#define IWL_MVM_COMMON_OPS \
/* these could be differentiated */ \
+ .async_cb = iwl_mvm_async_cb, \
.queue_full = iwl_mvm_stop_sw_queue, \
.queue_not_full = iwl_mvm_wake_sw_queue, \
.hw_rf_kill = iwl_mvm_set_hw_rfkill_state, \
@@ -1423,8 +1504,12 @@ static void iwl_mvm_rx_mq_rss(struct iwl_op_mode *op_mode,
unsigned int queue)
{
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
- iwl_mvm_rx_rx_mpdu(mvm, napi, rxb);
+ if (unlikely(pkt->hdr.cmd == FRAME_RELEASE))
+ iwl_mvm_rx_frame_release(mvm, rxb, queue);
+ else
+ iwl_mvm_rx_mpdu_mq(mvm, napi, rxb, queue);
}
static const struct iwl_op_mode_ops iwl_mvm_ops_mq = {
diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c
index e68a475e3071..6e6a56f2153d 100644
--- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/intel/iwlwifi/mvm/power.c
index bed9696ee410..9de159f1ef2d 100644
--- a/drivers/net/wireless/iwlwifi/mvm/power.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/power.c
@@ -27,7 +27,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -613,8 +613,6 @@ static void iwl_mvm_power_get_vifs_iterator(void *_data, u8 *mac,
break;
case NL80211_IFTYPE_STATION:
- /* only a single MAC of the same type */
- WARN_ON(power_iterator->bss_vif);
power_iterator->bss_vif = vif;
if (mvmvif->phy_ctxt)
if (mvmvif->phy_ctxt->id < MAX_PHYS)
diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/intel/iwlwifi/mvm/quota.c
index 509a66d05245..0b762b4f8fad 100644
--- a/drivers/net/wireless/iwlwifi/mvm/quota.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/quota.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
index d1ad10391b47..7bb6fd0e4391 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rs.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
@@ -20,7 +20,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -552,9 +552,10 @@ static char *rs_pretty_rate(const struct rs_rate *rate)
};
const char *rate_str;
- if (is_type_legacy(rate->type))
+ if (is_type_legacy(rate->type) && (rate->index <= IWL_RATE_54M_INDEX))
rate_str = legacy_rates[rate->index];
- else if (is_type_ht(rate->type) || is_type_vht(rate->type))
+ else if ((is_type_ht(rate->type) || is_type_vht(rate->type)) &&
+ (rate->index <= IWL_RATE_MCS_9_INDEX))
rate_str = ht_vht_rates[rate->index];
else
rate_str = "BAD_RATE";
@@ -1827,7 +1828,7 @@ static int rs_switch_to_column(struct iwl_mvm *mvm,
rate->type = lq_sta->is_vht ? LQ_VHT_MIMO2 : LQ_HT_MIMO2;
rate_mask = lq_sta->active_mimo2_rate;
} else {
- WARN_ON_ONCE("Bad column mode");
+ WARN_ONCE(1, "Bad column mode");
}
if (column->mode != RS_LEGACY) {
@@ -2550,6 +2551,8 @@ static const struct rs_init_rate_info rs_optimal_rates_vht_40_80mhz[] = {
{ S8_MIN, IWL_RATE_MCS_0_INDEX },
};
+#define IWL_RS_LOW_RSSI_THRESHOLD (-76) /* dBm */
+
/* Init the optimal rate based on STA caps
* This combined with rssi is used to report the last tx rate
* to userspace when we haven't transmitted enough frames.
@@ -2635,11 +2638,13 @@ static struct rs_rate *rs_get_optimal_rate(struct iwl_mvm *mvm,
* of last Rx
*/
static void rs_get_initial_rate(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
struct iwl_lq_sta *lq_sta,
enum ieee80211_band band,
struct rs_rate *rate)
{
int i, nentries;
+ unsigned long active_rate;
s8 best_rssi = S8_MIN;
u8 best_ant = ANT_NONE;
u8 valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm);
@@ -2680,19 +2685,55 @@ static void rs_get_initial_rate(struct iwl_mvm *mvm,
nentries = ARRAY_SIZE(rs_optimal_rates_24ghz_legacy);
}
- if (IWL_MVM_RS_RSSI_BASED_INIT_RATE) {
- for (i = 0; i < nentries; i++) {
- int rate_idx = initial_rates[i].rate_idx;
- if ((best_rssi >= initial_rates[i].rssi) &&
- (BIT(rate_idx) & lq_sta->active_legacy_rate)) {
- rate->index = rate_idx;
- break;
- }
+ if (!IWL_MVM_RS_RSSI_BASED_INIT_RATE)
+ goto out;
+
+ /* Start from a higher rate if the corresponding debug capability
+ * is enabled. The rate is chosen according to AP capabilities.
+ * In case of VHT/HT when the rssi is low fallback to the case of
+ * legacy rates.
+ */
+ if (sta->vht_cap.vht_supported &&
+ best_rssi > IWL_RS_LOW_RSSI_THRESHOLD) {
+ if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) {
+ initial_rates = rs_optimal_rates_vht_40_80mhz;
+ nentries = ARRAY_SIZE(rs_optimal_rates_vht_40_80mhz);
+ if (sta->bandwidth >= IEEE80211_STA_RX_BW_80)
+ rate->bw = RATE_MCS_CHAN_WIDTH_80;
+ else
+ rate->bw = RATE_MCS_CHAN_WIDTH_40;
+ } else if (sta->bandwidth == IEEE80211_STA_RX_BW_20) {
+ initial_rates = rs_optimal_rates_vht_20mhz;
+ nentries = ARRAY_SIZE(rs_optimal_rates_vht_20mhz);
+ rate->bw = RATE_MCS_CHAN_WIDTH_20;
+ } else {
+ IWL_ERR(mvm, "Invalid BW %d\n", sta->bandwidth);
+ goto out;
+ }
+ active_rate = lq_sta->active_siso_rate;
+ rate->type = LQ_VHT_SISO;
+ } else if (sta->ht_cap.ht_supported &&
+ best_rssi > IWL_RS_LOW_RSSI_THRESHOLD) {
+ initial_rates = rs_optimal_rates_ht;
+ nentries = ARRAY_SIZE(rs_optimal_rates_ht);
+ active_rate = lq_sta->active_siso_rate;
+ rate->type = LQ_HT_SISO;
+ } else {
+ active_rate = lq_sta->active_legacy_rate;
+ }
+
+ for (i = 0; i < nentries; i++) {
+ int rate_idx = initial_rates[i].rate_idx;
+
+ if ((best_rssi >= initial_rates[i].rssi) &&
+ (BIT(rate_idx) & active_rate)) {
+ rate->index = rate_idx;
+ break;
}
}
- IWL_DEBUG_RATE(mvm, "rate_idx %d ANT %s\n", rate->index,
- rs_pretty_ant(rate->ant));
+out:
+ rs_dump_rate(mvm, rate, "INITIAL");
}
/* Save info about RSSI of last Rx */
@@ -2752,14 +2793,11 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
tbl = &(lq_sta->lq_info[active_tbl]);
rate = &tbl->rate;
- rs_get_initial_rate(mvm, lq_sta, band, rate);
+ rs_get_initial_rate(mvm, sta, lq_sta, band, rate);
rs_init_optimal_rate(mvm, sta, lq_sta);
WARN_ON_ONCE(rate->ant != ANT_A && rate->ant != ANT_B);
- if (rate->ant == ANT_A)
- tbl->column = RS_COLUMN_LEGACY_ANT_A;
- else
- tbl->column = RS_COLUMN_LEGACY_ANT_B;
+ tbl->column = rs_get_column_from_rate(rate);
rs_set_expected_tpt_table(lq_sta, tbl);
rs_fill_lq_cmd(mvm, sta, lq_sta, rate);
@@ -3454,15 +3492,9 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
* Tx Fifo so that it can start a transaction in the same TxOP. This
* basically allows the firmware to send bursts.
*/
- if (iwl_mvm_vif_low_latency(mvmvif)) {
+ if (iwl_mvm_vif_low_latency(mvmvif))
lq_cmd->agg_frame_cnt_limit--;
- if (mvm->low_latency_agg_frame_limit)
- lq_cmd->agg_frame_cnt_limit =
- min(lq_cmd->agg_frame_cnt_limit,
- mvm->low_latency_agg_frame_limit);
- }
-
if (mvmsta->vif->p2p)
lq_cmd->flags |= LQ_FLAG_USE_RTS_MSK;
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h
index 81314ad9ebe0..bdb6f2d8d854 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rs.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h
@@ -20,7 +20,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
index 5b58f5320e8d..145ec68ce6f9 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -61,10 +61,12 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
+#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include "iwl-trans.h"
#include "mvm.h"
#include "fw-api.h"
+#include "fw-dbg.h"
/*
* iwl_mvm_rx_rx_phy_cmd - REPLY_RX_PHY_CMD handler
@@ -261,7 +263,7 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_rx_phy_info *phy_info;
struct iwl_rx_mpdu_res_start *rx_res;
- struct ieee80211_sta *sta;
+ struct ieee80211_sta *sta = NULL;
struct sk_buff *skb;
u32 len;
u32 ampdu_status;
@@ -332,22 +334,33 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,
(unsigned long long)rx_status->mactime);
rcu_read_lock();
- /*
- * We have tx blocked stations (with CS bit). If we heard frames from
- * a blocked station on a new channel we can TX to it again.
- */
- if (unlikely(mvm->csa_tx_block_bcn_timeout)) {
- sta = ieee80211_find_sta(
- rcu_dereference(mvm->csa_tx_blocked_vif), hdr->addr2);
- if (sta)
- iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false);
+ if (rx_pkt_status & RX_MPDU_RES_STATUS_SRC_STA_FOUND) {
+ u32 id = rx_pkt_status & RX_MPDU_RES_STATUS_STA_ID_MSK;
+
+ id >>= RX_MDPU_RES_STATUS_STA_ID_SHIFT;
+
+ if (!WARN_ON_ONCE(id >= IWL_MVM_STATION_COUNT)) {
+ sta = rcu_dereference(mvm->fw_id_to_mac_id[id]);
+ if (IS_ERR(sta))
+ sta = NULL;
+ }
+ } else if (!is_multicast_ether_addr(hdr->addr2)) {
+ /* This is fine since we prevent two stations with the same
+ * address from being added.
+ */
+ sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL);
}
- /* This is fine since we don't support multiple AP interfaces */
- sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL);
if (sta) {
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ /* We have tx blocked stations (with CS bit). If we heard
+ * frames from a blocked station on a new channel we can
+ * TX to it again.
+ */
+ if (unlikely(mvm->csa_tx_block_bcn_timeout))
+ iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false);
+
rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status);
if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_RSSI) &&
@@ -368,11 +381,10 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,
if (trig_check && rx_status->signal < rssi)
iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL);
}
- }
-
- if (sta && ieee80211_is_data(hdr->frame_control))
- iwl_mvm_rx_csum(sta, skb, rx_pkt_status);
+ if (ieee80211_is_data(hdr->frame_control))
+ iwl_mvm_rx_csum(sta, skb, rx_pkt_status);
+ }
rcu_read_unlock();
/* set the preamble flag if appropriate */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
new file mode 100644
index 000000000000..0c073e02fd4c
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
@@ -0,0 +1,458 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include "iwl-trans.h"
+#include "mvm.h"
+#include "fw-api.h"
+#include "fw-dbg.h"
+
+void iwl_mvm_rx_phy_cmd_mq(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
+{
+ mvm->ampdu_ref++;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (mvm->last_phy_info.phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) {
+ spin_lock(&mvm->drv_stats_lock);
+ mvm->drv_rx_stats.ampdu_count++;
+ spin_unlock(&mvm->drv_stats_lock);
+ }
+#endif
+}
+
+static inline int iwl_mvm_check_pn(struct iwl_mvm *mvm, struct sk_buff *skb,
+ int queue, struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_sta *mvmsta;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_rx_status *stats = IEEE80211_SKB_RXCB(skb);
+ struct iwl_mvm_key_pn *ptk_pn;
+ u8 tid, keyidx;
+ u8 pn[IEEE80211_CCMP_PN_LEN];
+ u8 *extiv;
+
+ /* do PN checking */
+
+ /* multicast and non-data only arrives on default queue */
+ if (!ieee80211_is_data(hdr->frame_control) ||
+ is_multicast_ether_addr(hdr->addr1))
+ return 0;
+
+ /* do not check PN for open AP */
+ if (!(stats->flag & RX_FLAG_DECRYPTED))
+ return 0;
+
+ /*
+ * avoid checking for default queue - we don't want to replicate
+ * all the logic that's necessary for checking the PN on fragmented
+ * frames, leave that to mac80211
+ */
+ if (queue == 0)
+ return 0;
+
+ /* if we are here - this for sure is either CCMP or GCMP */
+ if (IS_ERR_OR_NULL(sta)) {
+ IWL_ERR(mvm,
+ "expected hw-decrypted unicast frame for station\n");
+ return -1;
+ }
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+ extiv = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control);
+ keyidx = extiv[3] >> 6;
+
+ ptk_pn = rcu_dereference(mvmsta->ptk_pn[keyidx]);
+ if (!ptk_pn)
+ return -1;
+
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
+ else
+ tid = 0;
+
+ /* we don't use HCCA/802.11 QoS TSPECs, so drop such frames */
+ if (tid >= IWL_MAX_TID_COUNT)
+ return -1;
+
+ /* load pn */
+ pn[0] = extiv[7];
+ pn[1] = extiv[6];
+ pn[2] = extiv[5];
+ pn[3] = extiv[4];
+ pn[4] = extiv[1];
+ pn[5] = extiv[0];
+
+ if (memcmp(pn, ptk_pn->q[queue].pn[tid],
+ IEEE80211_CCMP_PN_LEN) <= 0)
+ return -1;
+
+ memcpy(ptk_pn->q[queue].pn[tid], pn, IEEE80211_CCMP_PN_LEN);
+ stats->flag |= RX_FLAG_PN_VALIDATED;
+
+ return 0;
+}
+
+/* iwl_mvm_create_skb Adds the rxb to a new skb */
+static void iwl_mvm_create_skb(struct sk_buff *skb, struct ieee80211_hdr *hdr,
+ u16 len, u8 crypt_len,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ unsigned int hdrlen, fraglen;
+
+ /* If frame is small enough to fit in skb->head, pull it completely.
+ * If not, only pull ieee80211_hdr (including crypto if present, and
+ * an additional 8 bytes for SNAP/ethertype, see below) so that
+ * splice() or TCP coalesce are more efficient.
+ *
+ * Since, in addition, ieee80211_data_to_8023() always pull in at
+ * least 8 bytes (possibly more for mesh) we can do the same here
+ * to save the cost of doing it later. That still doesn't pull in
+ * the actual IP header since the typical case has a SNAP header.
+ * If the latter changes (there are efforts in the standards group
+ * to do so) we should revisit this and ieee80211_data_to_8023().
+ */
+ hdrlen = (len <= skb_tailroom(skb)) ? len :
+ sizeof(*hdr) + crypt_len + 8;
+
+ memcpy(skb_put(skb, hdrlen), hdr, hdrlen);
+ fraglen = len - hdrlen;
+
+ if (fraglen) {
+ int offset = (void *)hdr + hdrlen -
+ rxb_addr(rxb) + rxb_offset(rxb);
+
+ skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset,
+ fraglen, rxb->truesize);
+ }
+}
+
+/* iwl_mvm_pass_packet_to_mac80211 - passes the packet for mac80211 */
+static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
+ struct napi_struct *napi,
+ struct sk_buff *skb, int queue,
+ struct ieee80211_sta *sta)
+{
+ if (iwl_mvm_check_pn(mvm, skb, queue, sta))
+ kfree_skb(skb);
+ else
+ ieee80211_rx_napi(mvm->hw, skb, napi);
+}
+
+static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm,
+ struct iwl_rx_mpdu_desc *desc,
+ struct ieee80211_rx_status *rx_status)
+{
+ int energy_a, energy_b, energy_c, max_energy;
+
+ energy_a = desc->energy_a;
+ energy_a = energy_a ? -energy_a : S8_MIN;
+ energy_b = desc->energy_b;
+ energy_b = energy_b ? -energy_b : S8_MIN;
+ energy_c = desc->energy_c;
+ energy_c = energy_c ? -energy_c : S8_MIN;
+ max_energy = max(energy_a, energy_b);
+ max_energy = max(max_energy, energy_c);
+
+ IWL_DEBUG_STATS(mvm, "energy In A %d B %d C %d , and max %d\n",
+ energy_a, energy_b, energy_c, max_energy);
+
+ rx_status->signal = max_energy;
+ rx_status->chains = 0; /* TODO: phy info */
+ rx_status->chain_signal[0] = energy_a;
+ rx_status->chain_signal[1] = energy_b;
+ rx_status->chain_signal[2] = energy_c;
+}
+
+static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
+ struct ieee80211_rx_status *stats,
+ struct iwl_rx_mpdu_desc *desc, int queue,
+ u8 *crypt_len)
+{
+ u16 status = le16_to_cpu(desc->status);
+
+ if (!ieee80211_has_protected(hdr->frame_control) ||
+ (status & IWL_RX_MPDU_STATUS_SEC_MASK) ==
+ IWL_RX_MPDU_STATUS_SEC_NONE)
+ return 0;
+
+ /* TODO: handle packets encrypted with unknown alg */
+
+ switch (status & IWL_RX_MPDU_STATUS_SEC_MASK) {
+ case IWL_RX_MPDU_STATUS_SEC_CCM:
+ case IWL_RX_MPDU_STATUS_SEC_GCM:
+ BUILD_BUG_ON(IEEE80211_CCMP_PN_LEN != IEEE80211_GCMP_PN_LEN);
+ /* alg is CCM: check MIC only */
+ if (!(status & IWL_RX_MPDU_STATUS_MIC_OK))
+ return -1;
+
+ stats->flag |= RX_FLAG_DECRYPTED;
+ *crypt_len = IEEE80211_CCMP_HDR_LEN;
+ return 0;
+ case IWL_RX_MPDU_STATUS_SEC_TKIP:
+ /* Don't drop the frame and decrypt it in SW */
+ if (!(status & IWL_RX_MPDU_RES_STATUS_TTAK_OK))
+ return 0;
+
+ *crypt_len = IEEE80211_TKIP_IV_LEN;
+ /* fall through if TTAK OK */
+ case IWL_RX_MPDU_STATUS_SEC_WEP:
+ if (!(status & IWL_RX_MPDU_STATUS_ICV_OK))
+ return -1;
+
+ stats->flag |= RX_FLAG_DECRYPTED;
+ if ((status & IWL_RX_MPDU_STATUS_SEC_MASK) ==
+ IWL_RX_MPDU_STATUS_SEC_WEP)
+ *crypt_len = IEEE80211_WEP_IV_LEN;
+ return 0;
+ case IWL_RX_MPDU_STATUS_SEC_EXT_ENC:
+ if (!(status & IWL_RX_MPDU_STATUS_MIC_OK))
+ return -1;
+ stats->flag |= RX_FLAG_DECRYPTED;
+ return 0;
+ default:
+ IWL_ERR(mvm, "Unhandled alg: 0x%x\n", status);
+ }
+
+ return 0;
+}
+
+static void iwl_mvm_rx_csum(struct ieee80211_sta *sta,
+ struct sk_buff *skb,
+ struct iwl_rx_mpdu_desc *desc)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
+
+ if (mvmvif->features & NETIF_F_RXCSUM &&
+ desc->l3l4_flags & cpu_to_le16(IWL_RX_L3L4_IP_HDR_CSUM_OK) &&
+ desc->l3l4_flags & cpu_to_le16(IWL_RX_L3L4_TCP_UDP_CSUM_OK))
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+}
+
+void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
+ struct iwl_rx_cmd_buffer *rxb, int queue)
+{
+ struct ieee80211_rx_status *rx_status;
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_rx_mpdu_desc *desc = (void *)pkt->data;
+ struct ieee80211_hdr *hdr = (void *)(desc + 1);
+ u32 len = le16_to_cpu(desc->mpdu_len);
+ u32 rate_n_flags = le32_to_cpu(desc->rate_n_flags);
+ struct ieee80211_sta *sta = NULL;
+ struct sk_buff *skb;
+ u8 crypt_len = 0;
+
+ /* Dont use dev_alloc_skb(), we'll have enough headroom once
+ * ieee80211_hdr pulled.
+ */
+ skb = alloc_skb(128, GFP_ATOMIC);
+ if (!skb) {
+ IWL_ERR(mvm, "alloc_skb failed\n");
+ return;
+ }
+
+ rx_status = IEEE80211_SKB_RXCB(skb);
+
+ if (iwl_mvm_rx_crypto(mvm, hdr, rx_status, desc, queue, &crypt_len)) {
+ kfree_skb(skb);
+ return;
+ }
+
+ /*
+ * Keep packets with CRC errors (and with overrun) for monitor mode
+ * (otherwise the firmware discards them) but mark them as bad.
+ */
+ if (!(desc->status & cpu_to_le16(IWL_RX_MPDU_STATUS_CRC_OK)) ||
+ !(desc->status & cpu_to_le16(IWL_RX_MPDU_STATUS_OVERRUN_OK))) {
+ IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n",
+ le16_to_cpu(desc->status));
+ rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+ }
+
+ rx_status->mactime = le64_to_cpu(desc->tsf_on_air_rise);
+ rx_status->device_timestamp = le32_to_cpu(desc->gp2_on_air_rise);
+ rx_status->band = desc->channel > 14 ? IEEE80211_BAND_5GHZ :
+ IEEE80211_BAND_2GHZ;
+ rx_status->freq = ieee80211_channel_to_frequency(desc->channel,
+ rx_status->band);
+ iwl_mvm_get_signal_strength(mvm, desc, rx_status);
+
+ rcu_read_lock();
+
+ if (le16_to_cpu(desc->status) & IWL_RX_MPDU_STATUS_SRC_STA_FOUND) {
+ u8 id = desc->sta_id_flags & IWL_RX_MPDU_SIF_STA_ID_MASK;
+
+ if (!WARN_ON_ONCE(id >= IWL_MVM_STATION_COUNT)) {
+ sta = rcu_dereference(mvm->fw_id_to_mac_id[id]);
+ if (IS_ERR(sta))
+ sta = NULL;
+ }
+ } else if (!is_multicast_ether_addr(hdr->addr2)) {
+ /*
+ * This is fine since we prevent two stations with the same
+ * address from being added.
+ */
+ sta = ieee80211_find_sta_by_ifaddr(mvm->hw, hdr->addr2, NULL);
+ }
+
+ if (sta) {
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+ /*
+ * We have tx blocked stations (with CS bit). If we heard
+ * frames from a blocked station on a new channel we can
+ * TX to it again.
+ */
+ if (unlikely(mvm->csa_tx_block_bcn_timeout))
+ iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false);
+
+ rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status);
+
+ if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_RSSI) &&
+ ieee80211_is_beacon(hdr->frame_control)) {
+ struct iwl_fw_dbg_trigger_tlv *trig;
+ struct iwl_fw_dbg_trigger_low_rssi *rssi_trig;
+ bool trig_check;
+ s32 rssi;
+
+ trig = iwl_fw_dbg_get_trigger(mvm->fw,
+ FW_DBG_TRIGGER_RSSI);
+ rssi_trig = (void *)trig->data;
+ rssi = le32_to_cpu(rssi_trig->rssi);
+
+ trig_check =
+ iwl_fw_dbg_trigger_check_stop(mvm, mvmsta->vif,
+ trig);
+ if (trig_check && rx_status->signal < rssi)
+ iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL);
+ }
+
+ /* TODO: multi queue TCM */
+
+ if (ieee80211_is_data(hdr->frame_control))
+ iwl_mvm_rx_csum(sta, skb, desc);
+ }
+
+ /*
+ * TODO: PHY info.
+ * Verify we don't have the information in the MPDU descriptor and
+ * that it is not needed.
+ * Make sure for monitor mode that we are on default queue, update
+ * ampdu_ref and the rest of phy info then
+ */
+
+ /* Set up the HT phy flags */
+ switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
+ case RATE_MCS_CHAN_WIDTH_20:
+ break;
+ case RATE_MCS_CHAN_WIDTH_40:
+ rx_status->flag |= RX_FLAG_40MHZ;
+ break;
+ case RATE_MCS_CHAN_WIDTH_80:
+ rx_status->vht_flag |= RX_VHT_FLAG_80MHZ;
+ break;
+ case RATE_MCS_CHAN_WIDTH_160:
+ rx_status->vht_flag |= RX_VHT_FLAG_160MHZ;
+ break;
+ }
+ if (rate_n_flags & RATE_MCS_SGI_MSK)
+ rx_status->flag |= RX_FLAG_SHORT_GI;
+ if (rate_n_flags & RATE_HT_MCS_GF_MSK)
+ rx_status->flag |= RX_FLAG_HT_GF;
+ if (rate_n_flags & RATE_MCS_LDPC_MSK)
+ rx_status->flag |= RX_FLAG_LDPC;
+ if (rate_n_flags & RATE_MCS_HT_MSK) {
+ u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >>
+ RATE_MCS_STBC_POS;
+ rx_status->flag |= RX_FLAG_HT;
+ rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
+ rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT;
+ } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
+ u8 stbc = (rate_n_flags & RATE_MCS_VHT_STBC_MSK) >>
+ RATE_MCS_STBC_POS;
+ rx_status->vht_nss =
+ ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
+ RATE_VHT_MCS_NSS_POS) + 1;
+ rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
+ rx_status->flag |= RX_FLAG_VHT;
+ rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT;
+ if (rate_n_flags & RATE_MCS_BF_MSK)
+ rx_status->vht_flag |= RX_VHT_FLAG_BF;
+ } else {
+ rx_status->rate_idx =
+ iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
+ rx_status->band);
+ }
+
+ /* TODO: PHY info - update ampdu queue statistics (for debugfs) */
+ /* TODO: PHY info - gscan */
+
+ iwl_mvm_create_skb(skb, hdr, len, crypt_len, rxb);
+ iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, queue, sta);
+ rcu_read_unlock();
+}
+
+void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb, int queue)
+{
+ /* TODO */
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c
index d6e0c1b5c20c..9a15642f80dd 100644
--- a/drivers/net/wireless/iwlwifi/mvm/scan.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -72,13 +72,6 @@
#define IWL_DENSE_EBS_SCAN_RATIO 5
#define IWL_SPARSE_EBS_SCAN_RATIO 1
-enum iwl_mvm_scan_type {
- IWL_SCAN_TYPE_UNASSOC,
- IWL_SCAN_TYPE_WILD,
- IWL_SCAN_TYPE_MILD,
- IWL_SCAN_TYPE_FRAGMENTED,
-};
-
enum iwl_mvm_traffic_load {
IWL_MVM_TRAFFIC_LOW,
IWL_MVM_TRAFFIC_MEDIUM,
@@ -89,6 +82,7 @@ struct iwl_mvm_scan_timing_params {
u32 dwell_active;
u32 dwell_passive;
u32 dwell_fragmented;
+ u32 dwell_extended;
u32 suspend_time;
u32 max_out_time;
};
@@ -98,6 +92,7 @@ static struct iwl_mvm_scan_timing_params scan_timing[] = {
.dwell_active = 10,
.dwell_passive = 110,
.dwell_fragmented = 44,
+ .dwell_extended = 90,
.suspend_time = 0,
.max_out_time = 0,
},
@@ -105,6 +100,7 @@ static struct iwl_mvm_scan_timing_params scan_timing[] = {
.dwell_active = 10,
.dwell_passive = 110,
.dwell_fragmented = 44,
+ .dwell_extended = 90,
.suspend_time = 30,
.max_out_time = 120,
},
@@ -112,6 +108,7 @@ static struct iwl_mvm_scan_timing_params scan_timing[] = {
.dwell_active = 10,
.dwell_passive = 110,
.dwell_fragmented = 44,
+ .dwell_extended = 90,
.suspend_time = 120,
.max_out_time = 120,
},
@@ -206,9 +203,7 @@ static enum iwl_mvm_traffic_load iwl_mvm_get_traffic_load(struct iwl_mvm *mvm)
}
static enum
-iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif,
- struct iwl_mvm_scan_params *params)
+iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm, bool p2p_device)
{
int global_cnt = 0;
enum iwl_mvm_traffic_load load;
@@ -224,8 +219,7 @@ iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm,
load = iwl_mvm_get_traffic_load(mvm);
low_latency = iwl_mvm_low_latency(mvm);
- if ((load == IWL_MVM_TRAFFIC_HIGH || low_latency) &&
- vif->type != NL80211_IFTYPE_P2P_DEVICE &&
+ if ((load == IWL_MVM_TRAFFIC_HIGH || low_latency) && !p2p_device &&
fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_FRAGMENTED_SCAN))
return IWL_SCAN_TYPE_FRAGMENTED;
@@ -333,6 +327,13 @@ void iwl_mvm_rx_lmac_scan_complete_notif(struct iwl_mvm *mvm,
struct iwl_periodic_scan_complete *scan_notif = (void *)pkt->data;
bool aborted = (scan_notif->status == IWL_SCAN_OFFLOAD_ABORTED);
+ /* If this happens, the firmware has mistakenly sent an LMAC
+ * notification during UMAC scans -- warn and ignore it.
+ */
+ if (WARN_ON_ONCE(fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_UMAC_SCAN)))
+ return;
+
/* scan status must be locked for proper checking */
lockdep_assert_held(&mvm->mutex);
@@ -719,6 +720,7 @@ static void iwl_mvm_scan_lmac_dwell(struct iwl_mvm *mvm,
cmd->active_dwell = scan_timing[params->type].dwell_active;
cmd->passive_dwell = scan_timing[params->type].dwell_passive;
cmd->fragmented_dwell = scan_timing[params->type].dwell_fragmented;
+ cmd->extended_dwell = scan_timing[params->type].dwell_extended;
cmd->max_out_time = cpu_to_le32(scan_timing[params->type].max_out_time);
cmd->suspend_time = cpu_to_le32(scan_timing[params->type].suspend_time);
cmd->scan_prio = iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6);
@@ -752,8 +754,15 @@ static inline bool iwl_mvm_scan_use_ebs(struct iwl_mvm *mvm,
vif->type != NL80211_IFTYPE_P2P_DEVICE);
}
+static inline bool iwl_mvm_is_regular_scan(struct iwl_mvm_scan_params *params)
+{
+ return params->n_scan_plans == 1 &&
+ params->scan_plans[0].iterations == 1;
+}
+
static int iwl_mvm_scan_lmac_flags(struct iwl_mvm *mvm,
- struct iwl_mvm_scan_params *params)
+ struct iwl_mvm_scan_params *params,
+ struct ieee80211_vif *vif)
{
int flags = 0;
@@ -779,6 +788,11 @@ static int iwl_mvm_scan_lmac_flags(struct iwl_mvm *mvm,
flags |= IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE;
#endif
+ if (iwl_mvm_is_regular_scan(params) &&
+ vif->type != NL80211_IFTYPE_P2P_DEVICE &&
+ params->type != IWL_SCAN_TYPE_FRAGMENTED)
+ flags |= IWL_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL;
+
return flags;
}
@@ -807,7 +821,8 @@ static int iwl_mvm_scan_lmac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
cmd->delay = cpu_to_le32(params->delay);
- cmd->scan_flags = cpu_to_le32(iwl_mvm_scan_lmac_flags(mvm, params));
+ cmd->scan_flags = cpu_to_le32(iwl_mvm_scan_lmac_flags(mvm, params,
+ vif));
cmd->flags = iwl_mvm_scan_rxon_flags(params->channels[0]->band);
cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
@@ -910,10 +925,14 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm)
struct iwl_host_cmd cmd = {
.id = iwl_cmd_id(SCAN_CFG_CMD, IWL_ALWAYS_LONG_GROUP, 0),
};
+ enum iwl_mvm_scan_type type = iwl_mvm_get_scan_type(mvm, false);
if (WARN_ON(num_channels > mvm->fw->ucode_capa.n_scan_channels))
return -ENOBUFS;
+ if (type == mvm->scan_type)
+ return 0;
+
cmd_size = sizeof(*scan_config) + mvm->fw->ucode_capa.n_scan_channels;
scan_config = kzalloc(cmd_size, GFP_KERNEL);
@@ -928,15 +947,20 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm)
SCAN_CONFIG_FLAG_SET_LEGACY_RATES |
SCAN_CONFIG_FLAG_SET_MAC_ADDR |
SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS|
- SCAN_CONFIG_N_CHANNELS(num_channels));
+ SCAN_CONFIG_N_CHANNELS(num_channels) |
+ (type == IWL_SCAN_TYPE_FRAGMENTED ?
+ SCAN_CONFIG_FLAG_SET_FRAGMENTED :
+ SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED));
scan_config->tx_chains = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm));
scan_config->rx_chains = cpu_to_le32(iwl_mvm_scan_rx_ant(mvm));
scan_config->legacy_rates = iwl_mvm_scan_config_rates(mvm);
- scan_config->out_of_channel_time = cpu_to_le32(170);
- scan_config->suspend_time = cpu_to_le32(30);
- scan_config->dwell_active = 20;
- scan_config->dwell_passive = 110;
- scan_config->dwell_fragmented = 20;
+ scan_config->out_of_channel_time =
+ cpu_to_le32(scan_timing[type].max_out_time);
+ scan_config->suspend_time = cpu_to_le32(scan_timing[type].suspend_time);
+ scan_config->dwell_active = scan_timing[type].dwell_active;
+ scan_config->dwell_passive = scan_timing[type].dwell_passive;
+ scan_config->dwell_fragmented = scan_timing[type].dwell_fragmented;
+ scan_config->dwell_extended = scan_timing[type].dwell_extended;
memcpy(&scan_config->mac_addr, &mvm->addresses[0].addr, ETH_ALEN);
@@ -960,6 +984,8 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm)
IWL_DEBUG_SCAN(mvm, "Sending UMAC scan config\n");
ret = iwl_mvm_send_cmd(mvm, &cmd);
+ if (!ret)
+ mvm->scan_type = type;
kfree(scan_config);
return ret;
@@ -976,16 +1002,11 @@ static int iwl_mvm_scan_uid_by_status(struct iwl_mvm *mvm, int status)
return -ENOENT;
}
-static inline bool iwl_mvm_is_regular_scan(struct iwl_mvm_scan_params *params)
-{
- return params->n_scan_plans == 1 &&
- params->scan_plans[0].iterations == 1;
-}
-
static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm,
struct iwl_scan_req_umac *cmd,
struct iwl_mvm_scan_params *params)
{
+ cmd->extended_dwell = scan_timing[params->type].dwell_extended;
cmd->active_dwell = scan_timing[params->type].dwell_active;
cmd->passive_dwell = scan_timing[params->type].dwell_passive;
cmd->fragmented_dwell = scan_timing[params->type].dwell_fragmented;
@@ -1020,7 +1041,8 @@ iwl_mvm_umac_scan_cfg_channels(struct iwl_mvm *mvm,
}
static u32 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm,
- struct iwl_mvm_scan_params *params)
+ struct iwl_mvm_scan_params *params,
+ struct ieee80211_vif *vif)
{
int flags = 0;
@@ -1048,6 +1070,12 @@ static u32 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm,
if (mvm->scan_iter_notif_enabled)
flags |= IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE;
#endif
+
+ if (iwl_mvm_is_regular_scan(params) &&
+ vif->type != NL80211_IFTYPE_P2P_DEVICE &&
+ params->type != IWL_SCAN_TYPE_FRAGMENTED)
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL;
+
return flags;
}
@@ -1078,7 +1106,8 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
mvm->scan_uid_status[uid] = type;
cmd->uid = cpu_to_le32(uid);
- cmd->general_flags = cpu_to_le32(iwl_mvm_scan_umac_flags(mvm, params));
+ cmd->general_flags = cpu_to_le32(iwl_mvm_scan_umac_flags(mvm, params,
+ vif));
if (type == IWL_MVM_SCAN_SCHED)
cmd->flags = cpu_to_le32(IWL_UMAC_SCAN_FLAG_PREEMPTIVE);
@@ -1150,7 +1179,7 @@ static int iwl_mvm_check_running_scans(struct iwl_mvm *mvm, int type)
case IWL_MVM_SCAN_SCHED:
if (mvm->scan_status & IWL_MVM_SCAN_SCHED_MASK)
return -EBUSY;
- iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true);
+ return iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true);
case IWL_MVM_SCAN_NETDETECT:
/* No need to stop anything for net-detect since the
* firmware is restarted anyway. This way, any sched
@@ -1213,7 +1242,9 @@ int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
params.scan_plans = &scan_plan;
params.n_scan_plans = 1;
- params.type = iwl_mvm_get_scan_type(mvm, vif, &params);
+ params.type =
+ iwl_mvm_get_scan_type(mvm,
+ vif->type == NL80211_IFTYPE_P2P_DEVICE);
iwl_mvm_build_scan_probe(mvm, vif, ies, &params);
@@ -1295,7 +1326,9 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
params.n_scan_plans = req->n_scan_plans;
params.scan_plans = req->scan_plans;
- params.type = iwl_mvm_get_scan_type(mvm, vif, &params);
+ params.type =
+ iwl_mvm_get_scan_type(mvm,
+ vif->type == NL80211_IFTYPE_P2P_DEVICE);
/* In theory, LMAC scans can handle a 32-bit delay, but since
* waiting for over 18 hours to start the scan is a bit silly
diff --git a/drivers/net/wireless/iwlwifi/mvm/sf.c b/drivers/net/wireless/intel/iwlwifi/mvm/sf.c
index b0f59fdd287c..c2def1232a8c 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sf.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/sf.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
index 300a249486e4..b556e33658d7 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -106,6 +106,7 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
.add_modify = update ? 1 : 0,
.station_flags_msk = cpu_to_le32(STA_FLG_FAT_EN_MSK |
STA_FLG_MIMO_EN_MSK),
+ .tid_disable_tx = cpu_to_le16(mvm_sta->tid_disable_agg),
};
int ret;
u32 status;
@@ -277,11 +278,6 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
if (sta_id == IWL_MVM_STATION_COUNT)
return -ENOSPC;
- if (vif->type == NL80211_IFTYPE_AP) {
- mvmvif->ap_assoc_sta_count++;
- iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
- }
-
spin_lock_init(&mvm_sta->lock);
mvm_sta->sta_id = sta_id;
@@ -580,9 +576,9 @@ int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm,
return ret;
}
-static int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm,
- struct iwl_mvm_int_sta *sta,
- u32 qmask, enum nl80211_iftype iftype)
+int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm,
+ struct iwl_mvm_int_sta *sta,
+ u32 qmask, enum nl80211_iftype iftype)
{
if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
sta->sta_id = iwl_mvm_find_free_sta_id(mvm, iftype);
@@ -622,6 +618,7 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm,
color));
cmd.tfd_queue_msk = cpu_to_le32(sta->tfd_queue_msk);
+ cmd.tid_disable_tx = cpu_to_le16(0xffff);
if (addr)
memcpy(cmd.addr, addr, ETH_ALEN);
@@ -671,6 +668,33 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm)
return ret;
}
+int iwl_mvm_add_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ lockdep_assert_held(&mvm->mutex);
+ return iwl_mvm_add_int_sta_common(mvm, &mvm->snif_sta, vif->addr,
+ mvmvif->id, 0);
+}
+
+int iwl_mvm_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ ret = iwl_mvm_rm_sta_common(mvm, mvm->snif_sta.sta_id);
+ if (ret)
+ IWL_WARN(mvm, "Failed sending remove station\n");
+
+ return ret;
+}
+
+void iwl_mvm_dealloc_snif_sta(struct iwl_mvm *mvm)
+{
+ iwl_mvm_dealloc_int_sta(mvm, &mvm->snif_sta);
+}
+
void iwl_mvm_del_aux_sta(struct iwl_mvm *mvm)
{
lockdep_assert_held(&mvm->mutex);
@@ -1196,21 +1220,17 @@ static int iwl_mvm_set_fw_key_idx(struct iwl_mvm *mvm)
if (max_offs < 0)
return STA_KEY_IDX_INVALID;
- __set_bit(max_offs, mvm->fw_key_table);
-
return max_offs;
}
-static u8 iwl_mvm_get_key_sta_id(struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
+static struct iwl_mvm_sta *iwl_mvm_get_key_sta(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- if (sta) {
- struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
-
- return mvm_sta->sta_id;
- }
+ if (sta)
+ return iwl_mvm_sta_from_mac80211(sta);
/*
* The device expects GTKs for station interfaces to be
@@ -1218,16 +1238,30 @@ static u8 iwl_mvm_get_key_sta_id(struct ieee80211_vif *vif,
* station ID, then use AP's station ID.
*/
if (vif->type == NL80211_IFTYPE_STATION &&
- mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT)
- return mvmvif->ap_sta_id;
+ mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
+ u8 sta_id = mvmvif->ap_sta_id;
- return IWL_MVM_STATION_COUNT;
+ sta = rcu_dereference_check(mvm->fw_id_to_mac_id[sta_id],
+ lockdep_is_held(&mvm->mutex));
+ /*
+ * It is possible that the 'sta' parameter is NULL,
+ * for example when a GTK is removed - the sta_id will then
+ * be the AP ID, and no station was passed by mac80211.
+ */
+ if (IS_ERR_OR_NULL(sta))
+ return NULL;
+
+ return iwl_mvm_sta_from_mac80211(sta);
+ }
+
+ return NULL;
}
static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
struct iwl_mvm_sta *mvm_sta,
struct ieee80211_key_conf *keyconf, bool mcast,
- u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags)
+ u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags,
+ u8 key_offset)
{
struct iwl_mvm_add_sta_key_cmd cmd = {};
__le16 key_flags;
@@ -1269,7 +1303,7 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
if (mcast)
key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
- cmd.key_offset = keyconf->hw_key_idx;
+ cmd.key_offset = key_offset;
cmd.key_flags = key_flags;
cmd.sta_id = sta_id;
@@ -1360,6 +1394,7 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *keyconf,
+ u8 key_offset,
bool mcast)
{
struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
@@ -1375,17 +1410,17 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
ieee80211_get_key_rx_seq(keyconf, 0, &seq);
ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
- seq.tkip.iv32, p1k, 0);
+ seq.tkip.iv32, p1k, 0, key_offset);
break;
case WLAN_CIPHER_SUITE_CCMP:
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
- 0, NULL, 0);
+ 0, NULL, 0, key_offset);
break;
default:
ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
- 0, NULL, 0);
+ 0, NULL, 0, key_offset);
}
return ret;
@@ -1433,9 +1468,10 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *keyconf,
- bool have_key_offset)
+ u8 key_offset)
{
bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
+ struct iwl_mvm_sta *mvm_sta;
u8 sta_id;
int ret;
static const u8 __maybe_unused zero_addr[ETH_ALEN] = {0};
@@ -1443,11 +1479,12 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
lockdep_assert_held(&mvm->mutex);
/* Get the station id from the mvm local station table */
- sta_id = iwl_mvm_get_key_sta_id(vif, sta);
- if (sta_id == IWL_MVM_STATION_COUNT) {
- IWL_ERR(mvm, "Failed to find station id\n");
+ mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta);
+ if (!mvm_sta) {
+ IWL_ERR(mvm, "Failed to find station\n");
return -EINVAL;
}
+ sta_id = mvm_sta->sta_id;
if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC) {
ret = iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, false);
@@ -1470,22 +1507,27 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif))
return -EINVAL;
- if (!have_key_offset) {
- /*
- * The D3 firmware hardcodes the PTK offset to 0, so we have to
- * configure it there. As a result, this workaround exists to
- * let the caller set the key offset (hw_key_idx), see d3.c.
- */
- keyconf->hw_key_idx = iwl_mvm_set_fw_key_idx(mvm);
- if (keyconf->hw_key_idx == STA_KEY_IDX_INVALID)
+ /* If the key_offset is not pre-assigned, we need to find a
+ * new offset to use. In normal cases, the offset is not
+ * pre-assigned, but during HW_RESTART we want to reuse the
+ * same indices, so we pass them when this function is called.
+ *
+ * In D3 entry, we need to hardcoded the indices (because the
+ * firmware hardcodes the PTK offset to 0). In this case, we
+ * need to make sure we don't overwrite the hw_key_idx in the
+ * keyconf structure, because otherwise we cannot configure
+ * the original ones back when resuming.
+ */
+ if (key_offset == STA_KEY_IDX_INVALID) {
+ key_offset = iwl_mvm_set_fw_key_idx(mvm);
+ if (key_offset == STA_KEY_IDX_INVALID)
return -ENOSPC;
+ keyconf->hw_key_idx = key_offset;
}
- ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, mcast);
- if (ret) {
- __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
+ ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, key_offset, mcast);
+ if (ret)
goto end;
- }
/*
* For WEP, the same key is used for multicast and unicast. Upload it
@@ -1495,13 +1537,16 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
*/
if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||
keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) {
- ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, !mcast);
+ ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf,
+ key_offset, !mcast);
if (ret) {
- __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
__iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast);
+ goto end;
}
}
+ __set_bit(key_offset, mvm->fw_key_table);
+
end:
IWL_DEBUG_WEP(mvm, "key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n",
keyconf->cipher, keyconf->keylen, keyconf->keyidx,
@@ -1515,13 +1560,14 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
struct ieee80211_key_conf *keyconf)
{
bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
- u8 sta_id;
+ struct iwl_mvm_sta *mvm_sta;
+ u8 sta_id = IWL_MVM_STATION_COUNT;
int ret, i;
lockdep_assert_held(&mvm->mutex);
- /* Get the station id from the mvm local station table */
- sta_id = iwl_mvm_get_key_sta_id(vif, sta);
+ /* Get the station from the mvm local station table */
+ mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta);
IWL_DEBUG_WEP(mvm, "mvm remove dynamic key: idx=%d sta=%d\n",
keyconf->keyidx, sta_id);
@@ -1542,28 +1588,12 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
}
mvm->fw_key_deleted[keyconf->hw_key_idx] = 0;
- if (sta_id == IWL_MVM_STATION_COUNT) {
+ if (!mvm_sta) {
IWL_DEBUG_WEP(mvm, "station non-existent, early return.\n");
return 0;
}
- /*
- * It is possible that the 'sta' parameter is NULL, and thus
- * there is a need to retrieve the sta from the local station table,
- * for example when a GTK is removed (where the sta_id will then be
- * the AP ID, and no station was passed by mac80211.)
- */
- if (!sta) {
- sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
- lockdep_is_held(&mvm->mutex));
- if (!sta) {
- IWL_ERR(mvm, "Invalid station id\n");
- return -EINVAL;
- }
- }
-
- if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif))
- return -EINVAL;
+ sta_id = mvm_sta->sta_id;
ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast);
if (ret)
@@ -1584,25 +1614,17 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
u16 *phase1key)
{
struct iwl_mvm_sta *mvm_sta;
- u8 sta_id = iwl_mvm_get_key_sta_id(vif, sta);
bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
- if (WARN_ON_ONCE(sta_id == IWL_MVM_STATION_COUNT))
- return;
-
rcu_read_lock();
- if (!sta) {
- sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
- if (WARN_ON(IS_ERR_OR_NULL(sta))) {
- rcu_read_unlock();
- return;
- }
- }
-
- mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+ mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta);
+ if (WARN_ON_ONCE(!mvm_sta))
+ goto unlock;
iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
- iv32, phase1key, CMD_ASYNC);
+ iv32, phase1key, CMD_ASYNC, keyconf->hw_key_idx);
+
+ unlock:
rcu_read_unlock();
}
@@ -1656,6 +1678,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
*/
if (agg) {
int remaining = cnt;
+ int sleep_tx_count;
spin_lock_bh(&mvmsta->lock);
for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) {
@@ -1680,9 +1703,12 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
}
remaining -= n_queued;
}
+ sleep_tx_count = cnt - remaining;
+ if (reason == IEEE80211_FRAME_RELEASE_UAPSD)
+ mvmsta->sleep_tx_count = sleep_tx_count;
spin_unlock_bh(&mvmsta->lock);
- cmd.sleep_tx_count = cpu_to_le16(cnt - remaining);
+ cmd.sleep_tx_count = cpu_to_le16(sleep_tx_count);
if (WARN_ON(cnt - remaining == 0)) {
ieee80211_sta_eosp(sta);
return;
@@ -1700,7 +1726,12 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_UAPSD);
}
- ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd);
+ /* block the Tx queues until the FW updated the sleep Tx count */
+ iwl_trans_block_txq_ptrs(mvm->trans, true);
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA,
+ CMD_ASYNC | CMD_WANT_ASYNC_CALLBACK,
+ sizeof(cmd), &cmd);
if (ret)
IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h
index eedb215eba3f..39fdf5224e81 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h
@@ -7,6 +7,7 @@
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -26,13 +27,14 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -284,6 +286,13 @@ static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data)
tid_data->next_reclaimed);
}
+struct iwl_mvm_key_pn {
+ struct rcu_head rcu_head;
+ struct {
+ u8 pn[IWL_MAX_TID_COUNT][IEEE80211_CCMP_PN_LEN];
+ } ____cacheline_aligned_in_smp q[];
+};
+
/**
* struct iwl_mvm_sta - representation of a station in the driver
* @sta_id: the index of the station in the fw (will be replaced by id_n_color)
@@ -303,6 +312,12 @@ static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data)
* @tt_tx_protection: is thermal throttling enable Tx protection?
* @disable_tx: is tx to this STA disabled?
* @agg_tids: bitmap of tids whose status is operational aggregated (IWL_AGG_ON)
+ * @sleep_tx_count: the number of frames that we told the firmware to let out
+ * even when that station is asleep. This is useful in case the queue
+ * gets empty before all the frames were sent, which can happen when
+ * we are sending frames from an AMPDU queue and there was a hole in
+ * the BA window. To be used for UAPSD only.
+ * @ptk_pn: per-queue PTK PN data structures
*
* When mac80211 creates a station it reserves some space (hw->sta_data_size)
* in the structure for use by driver. This structure is placed in that
@@ -323,12 +338,15 @@ struct iwl_mvm_sta {
struct iwl_lq_sta lq_sta;
struct ieee80211_vif *vif;
+ struct iwl_mvm_key_pn __rcu *ptk_pn[4];
+
/* Temporary, until the new TLC will control the Tx protection */
s8 tx_protection;
bool tt_tx_protection;
bool disable_tx;
u8 agg_tids;
+ u8 sleep_tx_count;
};
static inline struct iwl_mvm_sta *
@@ -365,8 +383,8 @@ int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm,
int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
- struct ieee80211_key_conf *key,
- bool have_key_offset);
+ struct ieee80211_key_conf *keyconf,
+ u8 key_offset);
int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
@@ -401,7 +419,13 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm,
+ struct iwl_mvm_int_sta *sta,
+ u32 qmask, enum nl80211_iftype iftype);
void iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_add_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+void iwl_mvm_dealloc_snif_sta(struct iwl_mvm *mvm);
void iwl_mvm_sta_drained_wk(struct work_struct *wk);
void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,
diff --git a/drivers/net/wireless/iwlwifi/mvm/tdls.c b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c
index fe2fa5650443..18711c5de35a 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tdls.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/mvm/testmode.h b/drivers/net/wireless/intel/iwlwifi/mvm/testmode.h
index 79ab6beb6b26..cbbc16fd006a 100644
--- a/drivers/net/wireless/iwlwifi/mvm/testmode.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/testmode.h
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
index 7530eb23035d..924dd6a41626 100644
--- a/drivers/net/wireless/iwlwifi/mvm/time-event.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -73,6 +73,7 @@
#include "mvm.h"
#include "iwl-io.h"
#include "iwl-prph.h"
+#include "fw-dbg.h"
/*
* For the high priority TE use a time event type that has similar priority to
@@ -791,11 +792,9 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
}
-void iwl_mvm_stop_roc(struct iwl_mvm *mvm)
+static struct iwl_mvm_time_event_data *iwl_mvm_get_roc_te(struct iwl_mvm *mvm)
{
- struct iwl_mvm_vif *mvmvif = NULL;
struct iwl_mvm_time_event_data *te_data;
- bool is_p2p = false;
lockdep_assert_held(&mvm->mutex);
@@ -809,11 +808,8 @@ void iwl_mvm_stop_roc(struct iwl_mvm *mvm)
* request
*/
list_for_each_entry(te_data, &mvm->time_event_list, list) {
- if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
- mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
- is_p2p = true;
- goto remove_te;
- }
+ if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE)
+ goto out;
}
/* There can only be at most one AUX ROC time event, we just use the
@@ -822,18 +818,35 @@ void iwl_mvm_stop_roc(struct iwl_mvm *mvm)
te_data = list_first_entry_or_null(&mvm->aux_roc_te_list,
struct iwl_mvm_time_event_data,
list);
+out:
+ spin_unlock_bh(&mvm->time_event_lock);
+ return te_data;
+}
+
+void iwl_mvm_cleanup_roc_te(struct iwl_mvm *mvm)
+{
+ struct iwl_mvm_time_event_data *te_data;
+ u32 uid;
+
+ te_data = iwl_mvm_get_roc_te(mvm);
if (te_data)
- mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
+ __iwl_mvm_remove_time_event(mvm, te_data, &uid);
+}
-remove_te:
- spin_unlock_bh(&mvm->time_event_lock);
+void iwl_mvm_stop_roc(struct iwl_mvm *mvm)
+{
+ struct iwl_mvm_vif *mvmvif;
+ struct iwl_mvm_time_event_data *te_data;
- if (!mvmvif) {
+ te_data = iwl_mvm_get_roc_te(mvm);
+ if (!te_data) {
IWL_WARN(mvm, "No remain on channel event\n");
return;
}
- if (is_p2p)
+ mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
+
+ if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE)
iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
else
iwl_mvm_remove_aux_roc_te(mvm, mvmvif, te_data);
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.h b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h
index cbdf8e52a5f1..99d9a35ad5b1 100644
--- a/drivers/net/wireless/iwlwifi/mvm/time-event.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -215,6 +215,7 @@ void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
struct iwl_mvm_time_event_data *te_data);
+void iwl_mvm_cleanup_roc_te(struct iwl_mvm *mvm);
void iwl_mvm_roc_done_wk(struct work_struct *wk);
/**
diff --git a/drivers/net/wireless/iwlwifi/mvm/tof.c b/drivers/net/wireless/intel/iwlwifi/mvm/tof.c
index 4007f1d421dd..a1947d6f3a2c 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tof.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tof.c
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/mvm/tof.h b/drivers/net/wireless/intel/iwlwifi/mvm/tof.h
index 9beebc33cb8d..8c3421c9991d 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tof.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tof.h
@@ -25,7 +25,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
index cadfc0460597..fb76004eede4 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tt.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -120,7 +120,7 @@ static int iwl_mvm_temp_notif_parse(struct iwl_mvm *mvm,
int len = iwl_rx_packet_payload_len(pkt);
int temp;
- if (WARN_ON_ONCE(len != sizeof(*notif))) {
+ if (WARN_ON_ONCE(len < sizeof(*notif))) {
IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n");
return -EINVAL;
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
index c652a66be803..8bf48a7d0f4e 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -64,11 +64,13 @@
*****************************************************************************/
#include <linux/ieee80211.h>
#include <linux/etherdevice.h>
+#include <linux/tcp.h>
#include "iwl-trans.h"
#include "iwl-eeprom-parse.h"
#include "mvm.h"
#include "sta.h"
+#include "fw-dbg.h"
static void
iwl_mvm_bar_check_trigger(struct iwl_mvm *mvm, const u8 *addr,
@@ -344,8 +346,8 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
iwl_mvm_set_tx_cmd_rate(mvm, tx_cmd, info, sta, hdr->frame_control);
memset(&info->status, 0, sizeof(info->status));
+ memset(info->driver_data, 0, sizeof(info->driver_data));
- info->driver_data[0] = NULL;
info->driver_data[1] = dev_cmd;
return dev_cmd;
@@ -424,11 +426,39 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
return 0;
}
+static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb_gso,
+ struct ieee80211_sta *sta,
+ struct sk_buff_head *mpdus_skb)
+{
+ struct sk_buff *tmp, *next;
+ char cb[sizeof(skb_gso->cb)];
+
+ memcpy(cb, skb_gso->cb, sizeof(cb));
+ next = skb_gso_segment(skb_gso, 0);
+ if (IS_ERR(next))
+ return -EINVAL;
+ else if (next)
+ consume_skb(skb_gso);
+
+ while (next) {
+ tmp = next;
+ next = tmp->next;
+ memcpy(tmp->cb, cb, sizeof(tmp->cb));
+
+ tmp->prev = NULL;
+ tmp->next = NULL;
+
+ __skb_queue_tail(mpdus_skb, tmp);
+ }
+
+ return 0;
+}
+
/*
* Sets the fields in the Tx cmd that are crypto related
*/
-int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
- struct ieee80211_sta *sta)
+static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,
+ struct ieee80211_sta *sta)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -524,6 +554,51 @@ drop:
return -1;
}
+int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct sk_buff_head mpdus_skbs;
+ unsigned int payload_len;
+ int ret;
+
+ if (WARN_ON_ONCE(!mvmsta))
+ return -1;
+
+ if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_STATION_COUNT))
+ return -1;
+
+ if (!skb_is_gso(skb))
+ return iwl_mvm_tx_mpdu(mvm, skb, sta);
+
+ payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) -
+ tcp_hdrlen(skb) + skb->data_len;
+
+ if (payload_len <= skb_shinfo(skb)->gso_size)
+ return iwl_mvm_tx_mpdu(mvm, skb, sta);
+
+ __skb_queue_head_init(&mpdus_skbs);
+
+ ret = iwl_mvm_tx_tso(mvm, skb, sta, &mpdus_skbs);
+ if (ret)
+ return ret;
+
+ if (WARN_ON(skb_queue_empty(&mpdus_skbs)))
+ return ret;
+
+ while (!skb_queue_empty(&mpdus_skbs)) {
+ struct sk_buff *skb = __skb_dequeue(&mpdus_skbs);
+
+ ret = iwl_mvm_tx_mpdu(mvm, skb, sta);
+ if (ret) {
+ __skb_queue_purge(&mpdus_skbs);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm,
struct ieee80211_sta *sta, u8 tid)
{
@@ -787,13 +862,43 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
if (tid != IWL_TID_NON_QOS) {
struct iwl_mvm_tid_data *tid_data =
&mvmsta->tid_data[tid];
+ bool send_eosp_ndp = false;
spin_lock_bh(&mvmsta->lock);
tid_data->next_reclaimed = next_reclaimed;
IWL_DEBUG_TX_REPLY(mvm, "Next reclaimed packet:%d\n",
next_reclaimed);
iwl_mvm_check_ratid_empty(mvm, sta, tid);
+
+ if (mvmsta->sleep_tx_count) {
+ mvmsta->sleep_tx_count--;
+ if (mvmsta->sleep_tx_count &&
+ !iwl_mvm_tid_queued(tid_data)) {
+ /*
+ * The number of frames in the queue
+ * dropped to 0 even if we sent less
+ * frames than we thought we had on the
+ * Tx queue.
+ * This means we had holes in the BA
+ * window that we just filled, ask
+ * mac80211 to send EOSP since the
+ * firmware won't know how to do that.
+ * Send NDP and the firmware will send
+ * EOSP notification that will trigger
+ * a call to ieee80211_sta_eosp().
+ */
+ send_eosp_ndp = true;
+ }
+ }
+
spin_unlock_bh(&mvmsta->lock);
+ if (send_eosp_ndp) {
+ iwl_mvm_sta_modify_sleep_tx_count(mvm, sta,
+ IEEE80211_FRAME_RELEASE_UAPSD,
+ 1, tid, false, false);
+ mvmsta->sleep_tx_count = 0;
+ ieee80211_send_eosp_nullfunc(sta, tid);
+ }
}
if (mvmsta->next_status_eosp) {
diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
index ad0f16909e2e..3a989f5c20db 100644
--- a/drivers/net/wireless/iwlwifi/mvm/utils.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
@@ -27,7 +27,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -68,7 +68,7 @@
#include "iwl-debug.h"
#include "iwl-io.h"
#include "iwl-prph.h"
-
+#include "fw-dbg.h"
#include "mvm.h"
#include "fw-api-rs.h"
diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
index 644b58bc5226..6261a68cae90 100644
--- a/drivers/net/wireless/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
@@ -26,7 +26,7 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
@@ -377,6 +377,10 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
{IWL_PCI_DEVICE(0x3165, 0x8010, iwl3165_2ac_cfg)},
{IWL_PCI_DEVICE(0x3165, 0x8110, iwl3165_2ac_cfg)},
+/* 3168 Series */
+ {IWL_PCI_DEVICE(0x24FB, 0x2110, iwl3168_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24FB, 0x0000, iwl3168_2ac_cfg)},
+
/* 7265 Series */
{IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x5110, iwl7265_2ac_cfg)},
@@ -384,6 +388,7 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
{IWL_PCI_DEVICE(0x095B, 0x5310, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095B, 0x5302, iwl7265_n_cfg)},
{IWL_PCI_DEVICE(0x095B, 0x5210, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5C10, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x5012, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x5412, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x5410, iwl7265_2ac_cfg)},
@@ -401,10 +406,10 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
{IWL_PCI_DEVICE(0x095A, 0x900A, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x9110, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x9112, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9210, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x9210, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095B, 0x9200, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x9510, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9310, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x9310, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x9410, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x5020, iwl7265_2n_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x502A, iwl7265_2n_cfg)},
@@ -423,14 +428,21 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
/* 8000 Series */
{IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F3, 0x1010, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0130, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1130, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0132, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1132, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F3, 0x0110, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x01F0, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0012, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1012, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F3, 0x1110, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F3, 0x0050, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F3, 0x0250, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F3, 0x1050, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F3, 0x0150, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1150, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F4, 0x0030, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F4, 0x1130, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F4, 0x1030, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F3, 0xC010, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F3, 0xC110, iwl8260_2ac_cfg)},
@@ -438,18 +450,44 @@ static const struct pci_device_id iwl_hw_card_ids[] = {
{IWL_PCI_DEVICE(0x24F3, 0xC050, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F3, 0xD050, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F3, 0x8010, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x8110, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F3, 0x9010, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x9110, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F4, 0x8030, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F4, 0x9030, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x8130, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x9130, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x8132, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x9132, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F3, 0x8050, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x8150, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F3, 0x9050, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x9150, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F3, 0x0004, iwl8260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0044, iwl8260_2n_cfg)},
{IWL_PCI_DEVICE(0x24F5, 0x0010, iwl4165_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F6, 0x0030, iwl4165_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F3, 0x0810, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F3, 0x0910, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F3, 0x0850, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F3, 0x0950, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0930, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0000, iwl8265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x0010, iwl8265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24FD, 0x8010, iwl8265_2ac_cfg)},
+
+/* 9000 Series */
+ {IWL_PCI_DEVICE(0x9DF0, 0x2A10, iwl5165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x2010, iwl5165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x0A10, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x0010, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x0000, iwl5165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x0310, iwl5165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x0510, iwl5165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x0710, iwl5165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x0210, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x0410, iwl9260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x9DF0, 0x0610, iwl9260_2ac_cfg)},
#endif /* CONFIG_IWLMVM */
{0}
@@ -581,7 +619,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
set_dflt_pwr_limit(iwl_trans, pdev);
/* register transport layer debugfs here */
- ret = iwl_trans_dbgfs_register(iwl_trans, iwl_trans->dbgfs_dir);
+ ret = iwl_trans_pcie_dbgfs_register(iwl_trans);
if (ret)
goto out_free_drv;
diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
index feb2f7e81134..cc3888e2700d 100644
--- a/drivers/net/wireless/iwlwifi/pcie/internal.h
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
@@ -23,7 +23,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -278,6 +278,7 @@ struct iwl_txq {
bool frozen;
u8 active;
bool ampdu;
+ bool block;
unsigned long wd_timeout;
};
@@ -288,6 +289,11 @@ iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx)
sizeof(struct iwl_pcie_txq_scratch_buf) * idx;
}
+struct iwl_tso_hdr_page {
+ struct page *page;
+ u8 *pos;
+};
+
/**
* struct iwl_trans_pcie - PCIe transport specific data
* @rxq: all the RX queue data
@@ -302,10 +308,12 @@ iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx)
* @ucode_write_complete: indicates that the ucode has been copied.
* @ucode_write_waitq: wait queue for uCode load
* @cmd_queue - command queue number
- * @rx_buf_size_8k: 8 kB RX buffer size
+ * @rx_buf_size: Rx buffer size
* @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes)
* @scd_set_active: should the transport configure the SCD for HCMD queue
* @wide_cmd_header: true when ucode supports wide command header format
+ * @sw_csum_tx: if true, then the transport will compute the csum of the TXed
+ * frame.
* @rx_page_order: page order for receive buffer size
* @reg_lock: protect hw register access
* @mutex: to protect stop_device / start_fw / start_hw
@@ -323,6 +331,8 @@ struct iwl_trans_pcie {
struct net_device napi_dev;
struct napi_struct napi;
+ struct __percpu iwl_tso_hdr_page *tso_hdr_page;
+
/* INT ICT Table */
__le32 *ict_tbl;
dma_addr_t ict_tbl_dma;
@@ -356,14 +366,13 @@ struct iwl_trans_pcie {
u8 n_no_reclaim_cmds;
u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS];
- bool rx_buf_size_8k;
+ enum iwl_amsdu_size rx_buf_size;
bool bc_table_dword;
bool scd_set_active;
bool wide_cmd_header;
+ bool sw_csum_tx;
u32 rx_page_order;
- const char *const *command_names;
-
/*protect hw register */
spinlock_t reg_lock;
bool cmd_hold_nic_awake;
@@ -378,8 +387,11 @@ struct iwl_trans_pcie {
u32 fw_mon_size;
};
-#define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \
- ((struct iwl_trans_pcie *) ((_iwl_trans)->trans_specific))
+static inline struct iwl_trans_pcie *
+IWL_TRANS_GET_PCIE_TRANS(struct iwl_trans *trans)
+{
+ return (void *)trans->trans_specific;
+}
static inline struct iwl_trans *
iwl_trans_pcie_get_trans(struct iwl_trans_pcie *trans_pcie)
@@ -523,14 +535,6 @@ static inline u8 get_cmd_index(struct iwl_queue *q, u32 index)
return index & (q->n_window - 1);
}
-static inline const char *get_cmd_string(struct iwl_trans_pcie *trans_pcie,
- u8 cmd)
-{
- if (!trans_pcie->command_names || !trans_pcie->command_names[cmd])
- return "UNKNOWN";
- return trans_pcie->command_names[cmd];
-}
-
static inline bool iwl_is_rfkill_set(struct iwl_trans *trans)
{
return !(iwl_read32(trans, CSR_GP_CNTRL) &
@@ -566,4 +570,13 @@ static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans,
void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state);
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans);
+#else
+static inline int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans)
+{
+ return 0;
+}
+#endif
+
#endif /* __iwl_trans_int_pcie_h__ */
diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c
index e06591f625c4..ccafbd8cf4b3 100644
--- a/drivers/net/wireless/iwlwifi/pcie/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c
@@ -23,7 +23,7 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
@@ -602,10 +602,20 @@ static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq)
u32 rb_size;
const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */
- if (trans_pcie->rx_buf_size_8k)
+ switch (trans_pcie->rx_buf_size) {
+ case IWL_AMSDU_4K:
+ rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K;
+ break;
+ case IWL_AMSDU_8K:
rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K;
- else
+ break;
+ case IWL_AMSDU_12K:
+ rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_12K;
+ break;
+ default:
+ WARN_ON(1);
rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K;
+ }
/* Stop Rx DMA */
iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0);
@@ -629,7 +639,7 @@ static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq)
* FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY is set because of HW bug in
* the credit mechanism in 5000 HW RX FIFO
* Direct rx interrupts to hosts
- * Rx buffer size 4 or 8k
+ * Rx buffer size 4 or 8k or 12k
* RB timeout 0x10
* 256 RBDs
*/
@@ -867,7 +877,10 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,
IWL_DEBUG_RX(trans,
"cmd at offset %d: %s (0x%.2x, seq 0x%x)\n",
rxcb._offset,
- get_cmd_string(trans_pcie, pkt->hdr.cmd),
+ iwl_get_cmd_string(trans,
+ iwl_cmd_id(pkt->hdr.cmd,
+ pkt->hdr.group_id,
+ 0)),
pkt->hdr.cmd, le16_to_cpu(pkt->hdr.sequence));
len = iwl_rx_packet_len(pkt);
@@ -986,8 +999,7 @@ restart:
rxb = rxq->queue[i];
rxq->queue[i] = NULL;
- IWL_DEBUG_RX(trans, "rxbuf: HW = %d, SW = %d (%p)\n",
- r, i, rxb);
+ IWL_DEBUG_RX(trans, "rxbuf: HW = %d, SW = %d\n", r, i);
iwl_pcie_rx_handle_rb(trans, rxb, emergency);
i = (i + 1) & RX_QUEUE_MASK;
@@ -1481,10 +1493,6 @@ int iwl_pcie_alloc_ict(struct iwl_trans *trans)
return -EINVAL;
}
- IWL_DEBUG_ISR(trans, "ict dma addr %Lx ict vir addr %p\n",
- (unsigned long long)trans_pcie->ict_tbl_dma,
- trans_pcie->ict_tbl);
-
return 0;
}
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
index 90283453073c..d60a467a983c 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
@@ -7,6 +7,7 @@
*
* Copyright(c) 2007 - 2015 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -26,13 +27,14 @@
* in the file called COPYING.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* BSD LICENSE
*
* Copyright(c) 2005 - 2015 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -924,9 +926,16 @@ monitor:
if (dest->monitor_mode == EXTERNAL_MODE && trans_pcie->fw_mon_size) {
iwl_write_prph(trans, le32_to_cpu(dest->base_reg),
trans_pcie->fw_mon_phys >> dest->base_shift);
- iwl_write_prph(trans, le32_to_cpu(dest->end_reg),
- (trans_pcie->fw_mon_phys +
- trans_pcie->fw_mon_size) >> dest->end_shift);
+ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ iwl_write_prph(trans, le32_to_cpu(dest->end_reg),
+ (trans_pcie->fw_mon_phys +
+ trans_pcie->fw_mon_size - 256) >>
+ dest->end_shift);
+ else
+ iwl_write_prph(trans, le32_to_cpu(dest->end_reg),
+ (trans_pcie->fw_mon_phys +
+ trans_pcie->fw_mon_size) >>
+ dest->end_shift);
}
}
@@ -1213,7 +1222,7 @@ static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- if (trans->wowlan_d0i3) {
+ if (trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3) {
/* Enable persistence mode to avoid reset */
iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_PERSIST_MODE);
@@ -1237,7 +1246,7 @@ static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test)
iwl_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
- if (!trans->wowlan_d0i3) {
+ if (trans->system_pm_mode == IWL_PLAT_PM_MODE_D3) {
/*
* reset TX queues -- some of their registers reset during S3
* so if we don't reset everything here the D3 image would try
@@ -1286,7 +1295,7 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
iwl_pcie_set_pwr(trans, false);
- if (trans->wowlan_d0i3) {
+ if (trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3) {
iwl_clear_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
} else {
@@ -1435,16 +1444,17 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans,
memcpy(trans_pcie->no_reclaim_cmds, trans_cfg->no_reclaim_cmds,
trans_pcie->n_no_reclaim_cmds * sizeof(u8));
- trans_pcie->rx_buf_size_8k = trans_cfg->rx_buf_size_8k;
- if (trans_pcie->rx_buf_size_8k)
- trans_pcie->rx_page_order = get_order(8 * 1024);
- else
- trans_pcie->rx_page_order = get_order(4 * 1024);
+ trans_pcie->rx_buf_size = trans_cfg->rx_buf_size;
+ trans_pcie->rx_page_order =
+ iwl_trans_get_rb_size_order(trans_pcie->rx_buf_size);
trans_pcie->wide_cmd_header = trans_cfg->wide_cmd_header;
- trans_pcie->command_names = trans_cfg->command_names;
trans_pcie->bc_table_dword = trans_cfg->bc_table_dword;
trans_pcie->scd_set_active = trans_cfg->scd_set_active;
+ trans_pcie->sw_csum_tx = trans_cfg->sw_csum_tx;
+
+ trans->command_groups = trans_cfg->command_groups;
+ trans->command_groups_size = trans_cfg->command_groups_size;
/* init ref_count to 1 (should be cleared when ucode is loaded) */
trans_pcie->ref_count = 1;
@@ -1464,6 +1474,7 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans,
void iwl_trans_pcie_free(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int i;
synchronize_irq(trans_pcie->pci_dev->irq);
@@ -1483,6 +1494,15 @@ void iwl_trans_pcie_free(struct iwl_trans *trans)
iwl_pcie_free_fw_monitor(trans);
+ for_each_possible_cpu(i) {
+ struct iwl_tso_hdr_page *p =
+ per_cpu_ptr(trans_pcie->tso_hdr_page, i);
+
+ if (p->page)
+ __free_page(p->page);
+ }
+
+ free_percpu(trans_pcie->tso_hdr_page);
iwl_trans_free(trans);
}
@@ -1494,8 +1514,8 @@ static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state)
clear_bit(STATUS_TPOWER_PMI, &trans->status);
}
-static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent,
- unsigned long *flags)
+static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans,
+ unsigned long *flags)
{
int ret;
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
@@ -1536,14 +1556,11 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent,
CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000);
if (unlikely(ret < 0)) {
iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI);
- if (!silent) {
- u32 val = iwl_read32(trans, CSR_GP_CNTRL);
- WARN_ONCE(1,
- "Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n",
- val);
- spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags);
- return false;
- }
+ WARN_ONCE(1,
+ "Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n",
+ iwl_read32(trans, CSR_GP_CNTRL));
+ spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags);
+ return false;
}
out:
@@ -1591,7 +1608,7 @@ static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr,
int offs, ret = 0;
u32 *vals = buf;
- if (iwl_trans_grab_nic_access(trans, false, &flags)) {
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
iwl_write32(trans, HBUS_TARG_MEM_RADDR, addr);
for (offs = 0; offs < dwords; offs++)
vals[offs] = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
@@ -1609,7 +1626,7 @@ static int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr,
int offs, ret = 0;
const u32 *vals = buf;
- if (iwl_trans_grab_nic_access(trans, false, &flags)) {
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr);
for (offs = 0; offs < dwords; offs++)
iwl_write32(trans, HBUS_TARG_MEM_WDAT,
@@ -1675,6 +1692,33 @@ next_queue:
}
}
+static void iwl_trans_pcie_block_txq_ptrs(struct iwl_trans *trans, bool block)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int i;
+
+ for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) {
+ struct iwl_txq *txq = &trans_pcie->txq[i];
+
+ if (i == trans_pcie->cmd_queue)
+ continue;
+
+ spin_lock_bh(&txq->lock);
+
+ if (!block && !(WARN_ON_ONCE(!txq->block))) {
+ txq->block--;
+ if (!txq->block) {
+ iwl_write32(trans, HBUS_TARG_WRPTR,
+ txq->q.write_ptr | (i << 8));
+ }
+ } else if (block) {
+ txq->block++;
+ }
+
+ spin_unlock_bh(&txq->lock);
+ }
+}
+
#define IWL_FLUSH_WAIT_MS 2000
static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm)
@@ -2109,13 +2153,11 @@ DEBUGFS_READ_FILE_OPS(rx_queue);
DEBUGFS_READ_FILE_OPS(tx_queue);
DEBUGFS_WRITE_FILE_OPS(csr);
-/*
- * Create the debugfs files and directories
- *
- */
-static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
- struct dentry *dir)
+/* Create the debugfs files and directories */
+int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans)
{
+ struct dentry *dir = trans->dbgfs_dir;
+
DEBUGFS_ADD_FILE(rx_queue, dir, S_IRUSR);
DEBUGFS_ADD_FILE(tx_queue, dir, S_IRUSR);
DEBUGFS_ADD_FILE(interrupt, dir, S_IWUSR | S_IRUSR);
@@ -2127,12 +2169,6 @@ err:
IWL_ERR(trans, "failed to create the trans debugfs entry\n");
return -ENOMEM;
}
-#else
-static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
- struct dentry *dir)
-{
- return 0;
-}
#endif /*CONFIG_IWLWIFI_DEBUGFS */
static u32 iwl_trans_pcie_get_cmdlen(struct iwl_tfd *tfd)
@@ -2146,144 +2182,6 @@ static u32 iwl_trans_pcie_get_cmdlen(struct iwl_tfd *tfd)
return cmdlen;
}
-static const struct {
- u32 start, end;
-} iwl_prph_dump_addr[] = {
- { .start = 0x00a00000, .end = 0x00a00000 },
- { .start = 0x00a0000c, .end = 0x00a00024 },
- { .start = 0x00a0002c, .end = 0x00a0003c },
- { .start = 0x00a00410, .end = 0x00a00418 },
- { .start = 0x00a00420, .end = 0x00a00420 },
- { .start = 0x00a00428, .end = 0x00a00428 },
- { .start = 0x00a00430, .end = 0x00a0043c },
- { .start = 0x00a00444, .end = 0x00a00444 },
- { .start = 0x00a004c0, .end = 0x00a004cc },
- { .start = 0x00a004d8, .end = 0x00a004d8 },
- { .start = 0x00a004e0, .end = 0x00a004f0 },
- { .start = 0x00a00840, .end = 0x00a00840 },
- { .start = 0x00a00850, .end = 0x00a00858 },
- { .start = 0x00a01004, .end = 0x00a01008 },
- { .start = 0x00a01010, .end = 0x00a01010 },
- { .start = 0x00a01018, .end = 0x00a01018 },
- { .start = 0x00a01024, .end = 0x00a01024 },
- { .start = 0x00a0102c, .end = 0x00a01034 },
- { .start = 0x00a0103c, .end = 0x00a01040 },
- { .start = 0x00a01048, .end = 0x00a01094 },
- { .start = 0x00a01c00, .end = 0x00a01c20 },
- { .start = 0x00a01c58, .end = 0x00a01c58 },
- { .start = 0x00a01c7c, .end = 0x00a01c7c },
- { .start = 0x00a01c28, .end = 0x00a01c54 },
- { .start = 0x00a01c5c, .end = 0x00a01c5c },
- { .start = 0x00a01c60, .end = 0x00a01cdc },
- { .start = 0x00a01ce0, .end = 0x00a01d0c },
- { .start = 0x00a01d18, .end = 0x00a01d20 },
- { .start = 0x00a01d2c, .end = 0x00a01d30 },
- { .start = 0x00a01d40, .end = 0x00a01d5c },
- { .start = 0x00a01d80, .end = 0x00a01d80 },
- { .start = 0x00a01d98, .end = 0x00a01d9c },
- { .start = 0x00a01da8, .end = 0x00a01da8 },
- { .start = 0x00a01db8, .end = 0x00a01df4 },
- { .start = 0x00a01dc0, .end = 0x00a01dfc },
- { .start = 0x00a01e00, .end = 0x00a01e2c },
- { .start = 0x00a01e40, .end = 0x00a01e60 },
- { .start = 0x00a01e68, .end = 0x00a01e6c },
- { .start = 0x00a01e74, .end = 0x00a01e74 },
- { .start = 0x00a01e84, .end = 0x00a01e90 },
- { .start = 0x00a01e9c, .end = 0x00a01ec4 },
- { .start = 0x00a01ed0, .end = 0x00a01ee0 },
- { .start = 0x00a01f00, .end = 0x00a01f1c },
- { .start = 0x00a01f44, .end = 0x00a01ffc },
- { .start = 0x00a02000, .end = 0x00a02048 },
- { .start = 0x00a02068, .end = 0x00a020f0 },
- { .start = 0x00a02100, .end = 0x00a02118 },
- { .start = 0x00a02140, .end = 0x00a0214c },
- { .start = 0x00a02168, .end = 0x00a0218c },
- { .start = 0x00a021c0, .end = 0x00a021c0 },
- { .start = 0x00a02400, .end = 0x00a02410 },
- { .start = 0x00a02418, .end = 0x00a02420 },
- { .start = 0x00a02428, .end = 0x00a0242c },
- { .start = 0x00a02434, .end = 0x00a02434 },
- { .start = 0x00a02440, .end = 0x00a02460 },
- { .start = 0x00a02468, .end = 0x00a024b0 },
- { .start = 0x00a024c8, .end = 0x00a024cc },
- { .start = 0x00a02500, .end = 0x00a02504 },
- { .start = 0x00a0250c, .end = 0x00a02510 },
- { .start = 0x00a02540, .end = 0x00a02554 },
- { .start = 0x00a02580, .end = 0x00a025f4 },
- { .start = 0x00a02600, .end = 0x00a0260c },
- { .start = 0x00a02648, .end = 0x00a02650 },
- { .start = 0x00a02680, .end = 0x00a02680 },
- { .start = 0x00a026c0, .end = 0x00a026d0 },
- { .start = 0x00a02700, .end = 0x00a0270c },
- { .start = 0x00a02804, .end = 0x00a02804 },
- { .start = 0x00a02818, .end = 0x00a0281c },
- { .start = 0x00a02c00, .end = 0x00a02db4 },
- { .start = 0x00a02df4, .end = 0x00a02fb0 },
- { .start = 0x00a03000, .end = 0x00a03014 },
- { .start = 0x00a0301c, .end = 0x00a0302c },
- { .start = 0x00a03034, .end = 0x00a03038 },
- { .start = 0x00a03040, .end = 0x00a03048 },
- { .start = 0x00a03060, .end = 0x00a03068 },
- { .start = 0x00a03070, .end = 0x00a03074 },
- { .start = 0x00a0307c, .end = 0x00a0307c },
- { .start = 0x00a03080, .end = 0x00a03084 },
- { .start = 0x00a0308c, .end = 0x00a03090 },
- { .start = 0x00a03098, .end = 0x00a03098 },
- { .start = 0x00a030a0, .end = 0x00a030a0 },
- { .start = 0x00a030a8, .end = 0x00a030b4 },
- { .start = 0x00a030bc, .end = 0x00a030bc },
- { .start = 0x00a030c0, .end = 0x00a0312c },
- { .start = 0x00a03c00, .end = 0x00a03c5c },
- { .start = 0x00a04400, .end = 0x00a04454 },
- { .start = 0x00a04460, .end = 0x00a04474 },
- { .start = 0x00a044c0, .end = 0x00a044ec },
- { .start = 0x00a04500, .end = 0x00a04504 },
- { .start = 0x00a04510, .end = 0x00a04538 },
- { .start = 0x00a04540, .end = 0x00a04548 },
- { .start = 0x00a04560, .end = 0x00a0457c },
- { .start = 0x00a04590, .end = 0x00a04598 },
- { .start = 0x00a045c0, .end = 0x00a045f4 },
-};
-
-static u32 iwl_trans_pcie_dump_prph(struct iwl_trans *trans,
- struct iwl_fw_error_dump_data **data)
-{
- struct iwl_fw_error_dump_prph *prph;
- unsigned long flags;
- u32 prph_len = 0, i;
-
- if (!iwl_trans_grab_nic_access(trans, false, &flags))
- return 0;
-
- for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) {
- /* The range includes both boundaries */
- int num_bytes_in_chunk = iwl_prph_dump_addr[i].end -
- iwl_prph_dump_addr[i].start + 4;
- int reg;
- __le32 *val;
-
- prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk;
-
- (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH);
- (*data)->len = cpu_to_le32(sizeof(*prph) +
- num_bytes_in_chunk);
- prph = (void *)(*data)->data;
- prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start);
- val = (void *)prph->data;
-
- for (reg = iwl_prph_dump_addr[i].start;
- reg <= iwl_prph_dump_addr[i].end;
- reg += 4)
- *val++ = cpu_to_le32(iwl_trans_pcie_read_prph(trans,
- reg));
- *data = iwl_fw_error_next_data(*data);
- }
-
- iwl_trans_release_nic_access(trans, &flags);
-
- return prph_len;
-}
-
static u32 iwl_trans_pcie_dump_rbs(struct iwl_trans *trans,
struct iwl_fw_error_dump_data **data,
int allocated_rb_nums)
@@ -2354,7 +2252,7 @@ static u32 iwl_trans_pcie_fh_regs_dump(struct iwl_trans *trans,
__le32 *val;
int i;
- if (!iwl_trans_grab_nic_access(trans, false, &flags))
+ if (!iwl_trans_grab_nic_access(trans, &flags))
return 0;
(*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FH_REGS);
@@ -2381,13 +2279,14 @@ iwl_trans_pci_dump_marbh_monitor(struct iwl_trans *trans,
unsigned long flags;
u32 i;
- if (!iwl_trans_grab_nic_access(trans, false, &flags))
+ if (!iwl_trans_grab_nic_access(trans, &flags))
return 0;
- __iwl_write_prph(trans, MON_DMARB_RD_CTL_ADDR, 0x1);
+ iwl_write_prph_no_grab(trans, MON_DMARB_RD_CTL_ADDR, 0x1);
for (i = 0; i < buf_size_in_dwords; i++)
- buffer[i] = __iwl_read_prph(trans, MON_DMARB_RD_DATA_ADDR);
- __iwl_write_prph(trans, MON_DMARB_RD_CTL_ADDR, 0x0);
+ buffer[i] = iwl_read_prph_no_grab(trans,
+ MON_DMARB_RD_DATA_ADDR);
+ iwl_write_prph_no_grab(trans, MON_DMARB_RD_CTL_ADDR, 0x0);
iwl_trans_release_nic_access(trans, &flags);
@@ -2474,7 +2373,7 @@ iwl_trans_pcie_dump_monitor(struct iwl_trans *trans,
static struct iwl_trans_dump_data
*iwl_trans_pcie_dump_data(struct iwl_trans *trans,
- struct iwl_fw_dbg_trigger_tlv *trigger)
+ const struct iwl_fw_dbg_trigger_tlv *trigger)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
struct iwl_fw_error_dump_data *data;
@@ -2535,16 +2434,6 @@ static struct iwl_trans_dump_data
/* CSR registers */
len += sizeof(*data) + IWL_CSR_TO_DUMP;
- /* PRPH registers */
- for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) {
- /* The range includes both boundaries */
- int num_bytes_in_chunk = iwl_prph_dump_addr[i].end -
- iwl_prph_dump_addr[i].start + 4;
-
- len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_prph) +
- num_bytes_in_chunk;
- }
-
/* FH registers */
len += sizeof(*data) + (FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND);
@@ -2592,7 +2481,6 @@ static struct iwl_trans_dump_data
len += sizeof(*data);
data = iwl_fw_error_next_data(data);
- len += iwl_trans_pcie_dump_prph(trans, &data);
len += iwl_trans_pcie_dump_csr(trans, &data);
len += iwl_trans_pcie_fh_regs_dump(trans, &data);
if (dump_rbs)
@@ -2623,10 +2511,9 @@ static const struct iwl_trans_ops trans_ops_pcie = {
.txq_disable = iwl_trans_pcie_txq_disable,
.txq_enable = iwl_trans_pcie_txq_enable,
- .dbgfs_register = iwl_trans_pcie_dbgfs_register,
-
.wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty,
.freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer,
+ .block_txq_ptrs = iwl_trans_pcie_block_txq_ptrs,
.write8 = iwl_trans_pcie_write8,
.write32 = iwl_trans_pcie_write32,
@@ -2671,6 +2558,11 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
spin_lock_init(&trans_pcie->ref_lock);
mutex_init(&trans_pcie->mutex);
init_waitqueue_head(&trans_pcie->ucode_write_waitq);
+ trans_pcie->tso_hdr_page = alloc_percpu(struct iwl_tso_hdr_page);
+ if (!trans_pcie->tso_hdr_page) {
+ ret = -ENOMEM;
+ goto out_no_pci;
+ }
ret = pci_enable_device(pdev);
if (ret)
@@ -2772,13 +2664,13 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
goto out_pci_disable_msi;
}
- if (iwl_trans_grab_nic_access(trans, false, &flags)) {
+ if (iwl_trans_grab_nic_access(trans, &flags)) {
u32 hw_step;
- hw_step = __iwl_read_prph(trans, WFPM_CTRL_REG);
+ hw_step = iwl_read_prph_no_grab(trans, WFPM_CTRL_REG);
hw_step |= ENABLE_WFPM;
- __iwl_write_prph(trans, WFPM_CTRL_REG, hw_step);
- hw_step = __iwl_read_prph(trans, AUX_MISC_REG);
+ iwl_write_prph_no_grab(trans, WFPM_CTRL_REG, hw_step);
+ hw_step = iwl_read_prph_no_grab(trans, AUX_MISC_REG);
hw_step = (hw_step >> HW_STEP_LOCATION_BITS) & 0xF;
if (hw_step == 0x3)
trans->hw_rev = (trans->hw_rev & 0xFFFFFFF3) |
@@ -2807,7 +2699,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
}
trans_pcie->inta_mask = CSR_INI_SET_MASK;
- trans->d0i3_mode = IWL_D0I3_MODE_ON_SUSPEND;
return trans;
@@ -2820,6 +2711,7 @@ out_pci_release_regions:
out_pci_disable_device:
pci_disable_device(pdev);
out_no_pci:
+ free_percpu(trans_pcie->tso_hdr_page);
iwl_trans_free(trans);
return ERR_PTR(ret);
}
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
index a8c8a4a7420b..5262028b5505 100644
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c
@@ -23,13 +23,17 @@
* file called LICENSE.
*
* Contact Information:
- * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Linux Wireless <linuxwifi@intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
*****************************************************************************/
#include <linux/etherdevice.h>
+#include <linux/ieee80211.h>
#include <linux/slab.h>
#include <linux/sched.h>
+#include <net/ip6_checksum.h>
+#include <net/tso.h>
+#include <net/ip6_checksum.h>
#include "iwl-debug.h"
#include "iwl-csr.h"
@@ -318,7 +322,9 @@ static void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans,
* trying to tx (during RFKILL, we're not trying to tx).
*/
IWL_DEBUG_TX(trans, "Q:%d WR: 0x%x\n", txq_id, txq->q.write_ptr);
- iwl_write32(trans, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8));
+ if (!txq->block)
+ iwl_write32(trans, HBUS_TARG_WRPTR,
+ txq->q.write_ptr | (txq_id << 8));
}
void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans)
@@ -576,6 +582,19 @@ static int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq,
return 0;
}
+static void iwl_pcie_free_tso_page(struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ if (info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA]) {
+ struct page *page =
+ info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA];
+
+ __free_page(page);
+ info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA] = NULL;
+ }
+}
+
/*
* iwl_pcie_txq_unmap - Unmap any remaining DMA mappings and free skb's
*/
@@ -589,6 +608,15 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id)
while (q->write_ptr != q->read_ptr) {
IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n",
txq_id, q->read_ptr);
+
+ if (txq_id != trans_pcie->cmd_queue) {
+ struct sk_buff *skb = txq->entries[q->read_ptr].skb;
+
+ if (WARN_ON_ONCE(!skb))
+ continue;
+
+ iwl_pcie_free_tso_page(skb);
+ }
iwl_pcie_txq_free_tfd(trans, txq);
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr);
}
@@ -742,7 +770,7 @@ static void iwl_pcie_tx_stop_fh(struct iwl_trans *trans)
spin_lock(&trans_pcie->irq_lock);
- if (!iwl_trans_grab_nic_access(trans, false, &flags))
+ if (!iwl_trans_grab_nic_access(trans, &flags))
goto out;
/* Stop each Tx DMA channel */
@@ -1006,11 +1034,14 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
for (;
q->read_ptr != tfd_num;
q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) {
+ struct sk_buff *skb = txq->entries[txq->q.read_ptr].skb;
- if (WARN_ON_ONCE(txq->entries[txq->q.read_ptr].skb == NULL))
+ if (WARN_ON_ONCE(!skb))
continue;
- __skb_queue_tail(skbs, txq->entries[txq->q.read_ptr].skb);
+ iwl_pcie_free_tso_page(skb);
+
+ __skb_queue_tail(skbs, skb);
txq->entries[txq->q.read_ptr].skb = NULL;
@@ -1411,7 +1442,8 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
*/
if (WARN(copy_size > TFD_MAX_PAYLOAD_SIZE,
"Command %s (%#x) is too large (%d bytes)\n",
- get_cmd_string(trans_pcie, cmd->id), cmd->id, copy_size)) {
+ iwl_get_cmd_string(trans, cmd->id),
+ cmd->id, copy_size)) {
idx = -EINVAL;
goto free_dup_buf;
}
@@ -1501,7 +1533,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
IWL_DEBUG_HC(trans,
"Sending command %s (%.2x.%.2x), seq: 0x%04X, %d bytes at %d[%d]:%d\n",
- get_cmd_string(trans_pcie, out_cmd->hdr.cmd),
+ iwl_get_cmd_string(trans, cmd->id),
group_id, out_cmd->hdr.cmd,
le16_to_cpu(out_cmd->hdr.sequence),
cmd_size, q->write_ptr, idx, trans_pcie->cmd_queue);
@@ -1591,16 +1623,14 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
/*
* iwl_pcie_hcmd_complete - Pull unused buffers off the queue and reclaim them
* @rxb: Rx buffer to reclaim
- *
- * If an Rx buffer has an async callback associated with it the callback
- * will be executed. The attached skb (if present) will only be freed
- * if the callback returns 1
*/
void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
struct iwl_rx_cmd_buffer *rxb)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
u16 sequence = le16_to_cpu(pkt->hdr.sequence);
+ u8 group_id = iwl_cmd_groupid(pkt->hdr.group_id);
+ u32 cmd_id;
int txq_id = SEQ_TO_QUEUE(sequence);
int index = SEQ_TO_INDEX(sequence);
int cmd_index;
@@ -1626,6 +1656,7 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
cmd_index = get_cmd_index(&txq->q, index);
cmd = txq->entries[cmd_index].cmd;
meta = &txq->entries[cmd_index].meta;
+ cmd_id = iwl_cmd_id(cmd->hdr.cmd, group_id, 0);
iwl_pcie_tfd_unmap(trans, meta, &txq->tfds[index]);
@@ -1638,17 +1669,20 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
meta->source->_rx_page_order = trans_pcie->rx_page_order;
}
+ if (meta->flags & CMD_WANT_ASYNC_CALLBACK)
+ iwl_op_mode_async_cb(trans->op_mode, cmd);
+
iwl_pcie_cmdq_reclaim(trans, txq_id, index);
if (!(meta->flags & CMD_ASYNC)) {
if (!test_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status)) {
IWL_WARN(trans,
"HCMD_ACTIVE already clear for command %s\n",
- get_cmd_string(trans_pcie, cmd->hdr.cmd));
+ iwl_get_cmd_string(trans, cmd_id));
}
clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n",
- get_cmd_string(trans_pcie, cmd->hdr.cmd));
+ iwl_get_cmd_string(trans, cmd_id));
wake_up(&trans_pcie->wait_command_queue);
}
@@ -1662,7 +1696,6 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
static int iwl_pcie_send_hcmd_async(struct iwl_trans *trans,
struct iwl_host_cmd *cmd)
{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int ret;
/* An asynchronous command can not expect an SKB to be set. */
@@ -1673,7 +1706,7 @@ static int iwl_pcie_send_hcmd_async(struct iwl_trans *trans,
if (ret < 0) {
IWL_ERR(trans,
"Error sending %s: enqueue_hcmd failed: %d\n",
- get_cmd_string(trans_pcie, cmd->id), ret);
+ iwl_get_cmd_string(trans, cmd->id), ret);
return ret;
}
return 0;
@@ -1687,16 +1720,16 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
int ret;
IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n",
- get_cmd_string(trans_pcie, cmd->id));
+ iwl_get_cmd_string(trans, cmd->id));
if (WARN(test_and_set_bit(STATUS_SYNC_HCMD_ACTIVE,
&trans->status),
"Command %s: a command is already active!\n",
- get_cmd_string(trans_pcie, cmd->id)))
+ iwl_get_cmd_string(trans, cmd->id)))
return -EIO;
IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n",
- get_cmd_string(trans_pcie, cmd->id));
+ iwl_get_cmd_string(trans, cmd->id));
cmd_idx = iwl_pcie_enqueue_hcmd(trans, cmd);
if (cmd_idx < 0) {
@@ -1704,7 +1737,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
IWL_ERR(trans,
"Error sending %s: enqueue_hcmd failed: %d\n",
- get_cmd_string(trans_pcie, cmd->id), ret);
+ iwl_get_cmd_string(trans, cmd->id), ret);
return ret;
}
@@ -1717,7 +1750,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
struct iwl_queue *q = &txq->q;
IWL_ERR(trans, "Error sending %s: time out after %dms.\n",
- get_cmd_string(trans_pcie, cmd->id),
+ iwl_get_cmd_string(trans, cmd->id),
jiffies_to_msecs(HOST_COMPLETE_TIMEOUT));
IWL_ERR(trans, "Current CMD queue read_ptr %d write_ptr %d\n",
@@ -1725,7 +1758,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n",
- get_cmd_string(trans_pcie, cmd->id));
+ iwl_get_cmd_string(trans, cmd->id));
ret = -ETIMEDOUT;
iwl_force_nmi(trans);
@@ -1736,7 +1769,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
if (test_bit(STATUS_FW_ERROR, &trans->status)) {
IWL_ERR(trans, "FW error in SYNC CMD %s\n",
- get_cmd_string(trans_pcie, cmd->id));
+ iwl_get_cmd_string(trans, cmd->id));
dump_stack();
ret = -EIO;
goto cancel;
@@ -1751,7 +1784,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
if ((cmd->flags & CMD_WANT_SKB) && !cmd->resp_pkt) {
IWL_ERR(trans, "Error: Response NULL in '%s'\n",
- get_cmd_string(trans_pcie, cmd->id));
+ iwl_get_cmd_string(trans, cmd->id));
ret = -EIO;
goto cancel;
}
@@ -1794,6 +1827,305 @@ int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
return iwl_pcie_send_hcmd_sync(trans, cmd);
}
+static int iwl_fill_data_tbs(struct iwl_trans *trans, struct sk_buff *skb,
+ struct iwl_txq *txq, u8 hdr_len,
+ struct iwl_cmd_meta *out_meta,
+ struct iwl_device_cmd *dev_cmd, u16 tb1_len)
+{
+ struct iwl_queue *q = &txq->q;
+ u16 tb2_len;
+ int i;
+
+ /*
+ * Set up TFD's third entry to point directly to remainder
+ * of skb's head, if any
+ */
+ tb2_len = skb_headlen(skb) - hdr_len;
+
+ if (tb2_len > 0) {
+ dma_addr_t tb2_phys = dma_map_single(trans->dev,
+ skb->data + hdr_len,
+ tb2_len, DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(trans->dev, tb2_phys))) {
+ iwl_pcie_tfd_unmap(trans, out_meta,
+ &txq->tfds[q->write_ptr]);
+ return -EINVAL;
+ }
+ iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, false);
+ }
+
+ /* set up the remaining entries to point to the data */
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ dma_addr_t tb_phys;
+ int tb_idx;
+
+ if (!skb_frag_size(frag))
+ continue;
+
+ tb_phys = skb_frag_dma_map(trans->dev, frag, 0,
+ skb_frag_size(frag), DMA_TO_DEVICE);
+
+ if (unlikely(dma_mapping_error(trans->dev, tb_phys))) {
+ iwl_pcie_tfd_unmap(trans, out_meta,
+ &txq->tfds[q->write_ptr]);
+ return -EINVAL;
+ }
+ tb_idx = iwl_pcie_txq_build_tfd(trans, txq, tb_phys,
+ skb_frag_size(frag), false);
+
+ out_meta->flags |= BIT(tb_idx + CMD_TB_BITMAP_POS);
+ }
+
+ trace_iwlwifi_dev_tx(trans->dev, skb,
+ &txq->tfds[txq->q.write_ptr],
+ sizeof(struct iwl_tfd),
+ &dev_cmd->hdr, IWL_HCMD_SCRATCHBUF_SIZE + tb1_len,
+ skb->data + hdr_len, tb2_len);
+ trace_iwlwifi_dev_tx_data(trans->dev, skb,
+ hdr_len, skb->len - hdr_len);
+ return 0;
+}
+
+#ifdef CONFIG_INET
+static struct iwl_tso_hdr_page *
+get_page_hdr(struct iwl_trans *trans, size_t len)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct iwl_tso_hdr_page *p = this_cpu_ptr(trans_pcie->tso_hdr_page);
+
+ if (!p->page)
+ goto alloc;
+
+ /* enough room on this page */
+ if (p->pos + len < (u8 *)page_address(p->page) + PAGE_SIZE)
+ return p;
+
+ /* We don't have enough room on this page, get a new one. */
+ __free_page(p->page);
+
+alloc:
+ p->page = alloc_page(GFP_ATOMIC);
+ if (!p->page)
+ return NULL;
+ p->pos = page_address(p->page);
+ return p;
+}
+
+static void iwl_compute_pseudo_hdr_csum(void *iph, struct tcphdr *tcph,
+ bool ipv6, unsigned int len)
+{
+ if (ipv6) {
+ struct ipv6hdr *iphv6 = iph;
+
+ tcph->check = ~csum_ipv6_magic(&iphv6->saddr, &iphv6->daddr,
+ len + tcph->doff * 4,
+ IPPROTO_TCP, 0);
+ } else {
+ struct iphdr *iphv4 = iph;
+
+ ip_send_check(iphv4);
+ tcph->check = ~csum_tcpudp_magic(iphv4->saddr, iphv4->daddr,
+ len + tcph->doff * 4,
+ IPPROTO_TCP, 0);
+ }
+}
+
+static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb,
+ struct iwl_txq *txq, u8 hdr_len,
+ struct iwl_cmd_meta *out_meta,
+ struct iwl_device_cmd *dev_cmd, u16 tb1_len)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct iwl_trans_pcie *trans_pcie = txq->trans_pcie;
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ unsigned int snap_ip_tcp_hdrlen, ip_hdrlen, total_len, hdr_room;
+ unsigned int mss = skb_shinfo(skb)->gso_size;
+ struct iwl_queue *q = &txq->q;
+ u16 length, iv_len, amsdu_pad;
+ u8 *start_hdr;
+ struct iwl_tso_hdr_page *hdr_page;
+ int ret;
+ struct tso_t tso;
+
+ /* if the packet is protected, then it must be CCMP or GCMP */
+ BUILD_BUG_ON(IEEE80211_CCMP_HDR_LEN != IEEE80211_GCMP_HDR_LEN);
+ iv_len = ieee80211_has_protected(hdr->frame_control) ?
+ IEEE80211_CCMP_HDR_LEN : 0;
+
+ trace_iwlwifi_dev_tx(trans->dev, skb,
+ &txq->tfds[txq->q.write_ptr],
+ sizeof(struct iwl_tfd),
+ &dev_cmd->hdr, IWL_HCMD_SCRATCHBUF_SIZE + tb1_len,
+ NULL, 0);
+
+ ip_hdrlen = skb_transport_header(skb) - skb_network_header(skb);
+ snap_ip_tcp_hdrlen = 8 + ip_hdrlen + tcp_hdrlen(skb);
+ total_len = skb->len - snap_ip_tcp_hdrlen - hdr_len - iv_len;
+ amsdu_pad = 0;
+
+ /* total amount of header we may need for this A-MSDU */
+ hdr_room = DIV_ROUND_UP(total_len, mss) *
+ (3 + snap_ip_tcp_hdrlen + sizeof(struct ethhdr)) + iv_len;
+
+ /* Our device supports 9 segments at most, it will fit in 1 page */
+ hdr_page = get_page_hdr(trans, hdr_room);
+ if (!hdr_page)
+ return -ENOMEM;
+
+ get_page(hdr_page->page);
+ start_hdr = hdr_page->pos;
+ info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA] = hdr_page->page;
+ memcpy(hdr_page->pos, skb->data + hdr_len, iv_len);
+ hdr_page->pos += iv_len;
+
+ /*
+ * Pull the ieee80211 header + IV to be able to use TSO core,
+ * we will restore it for the tx_status flow.
+ */
+ skb_pull(skb, hdr_len + iv_len);
+
+ tso_start(skb, &tso);
+
+ while (total_len) {
+ /* this is the data left for this subframe */
+ unsigned int data_left =
+ min_t(unsigned int, mss, total_len);
+ struct sk_buff *csum_skb = NULL;
+ unsigned int hdr_tb_len;
+ dma_addr_t hdr_tb_phys;
+ struct tcphdr *tcph;
+ u8 *iph;
+
+ total_len -= data_left;
+
+ memset(hdr_page->pos, 0, amsdu_pad);
+ hdr_page->pos += amsdu_pad;
+ amsdu_pad = (4 - (sizeof(struct ethhdr) + snap_ip_tcp_hdrlen +
+ data_left)) & 0x3;
+ ether_addr_copy(hdr_page->pos, ieee80211_get_DA(hdr));
+ hdr_page->pos += ETH_ALEN;
+ ether_addr_copy(hdr_page->pos, ieee80211_get_SA(hdr));
+ hdr_page->pos += ETH_ALEN;
+
+ length = snap_ip_tcp_hdrlen + data_left;
+ *((__be16 *)hdr_page->pos) = cpu_to_be16(length);
+ hdr_page->pos += sizeof(length);
+
+ /*
+ * This will copy the SNAP as well which will be considered
+ * as MAC header.
+ */
+ tso_build_hdr(skb, hdr_page->pos, &tso, data_left, !total_len);
+ iph = hdr_page->pos + 8;
+ tcph = (void *)(iph + ip_hdrlen);
+
+ /* For testing on current hardware only */
+ if (trans_pcie->sw_csum_tx) {
+ csum_skb = alloc_skb(data_left + tcp_hdrlen(skb),
+ GFP_ATOMIC);
+ if (!csum_skb) {
+ ret = -ENOMEM;
+ goto out_unmap;
+ }
+
+ iwl_compute_pseudo_hdr_csum(iph, tcph,
+ skb->protocol ==
+ htons(ETH_P_IPV6),
+ data_left);
+
+ memcpy(skb_put(csum_skb, tcp_hdrlen(skb)),
+ tcph, tcp_hdrlen(skb));
+ skb_set_transport_header(csum_skb, 0);
+ csum_skb->csum_start =
+ (unsigned char *)tcp_hdr(csum_skb) -
+ csum_skb->head;
+ }
+
+ hdr_page->pos += snap_ip_tcp_hdrlen;
+
+ hdr_tb_len = hdr_page->pos - start_hdr;
+ hdr_tb_phys = dma_map_single(trans->dev, start_hdr,
+ hdr_tb_len, DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(trans->dev, hdr_tb_phys))) {
+ dev_kfree_skb(csum_skb);
+ ret = -EINVAL;
+ goto out_unmap;
+ }
+ iwl_pcie_txq_build_tfd(trans, txq, hdr_tb_phys,
+ hdr_tb_len, false);
+ trace_iwlwifi_dev_tx_tso_chunk(trans->dev, start_hdr,
+ hdr_tb_len);
+
+ /* prepare the start_hdr for the next subframe */
+ start_hdr = hdr_page->pos;
+
+ /* put the payload */
+ while (data_left) {
+ unsigned int size = min_t(unsigned int, tso.size,
+ data_left);
+ dma_addr_t tb_phys;
+
+ if (trans_pcie->sw_csum_tx)
+ memcpy(skb_put(csum_skb, size), tso.data, size);
+
+ tb_phys = dma_map_single(trans->dev, tso.data,
+ size, DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(trans->dev, tb_phys))) {
+ dev_kfree_skb(csum_skb);
+ ret = -EINVAL;
+ goto out_unmap;
+ }
+
+ iwl_pcie_txq_build_tfd(trans, txq, tb_phys,
+ size, false);
+ trace_iwlwifi_dev_tx_tso_chunk(trans->dev, tso.data,
+ size);
+
+ data_left -= size;
+ tso_build_data(skb, &tso, size);
+ }
+
+ /* For testing on early hardware only */
+ if (trans_pcie->sw_csum_tx) {
+ __wsum csum;
+
+ csum = skb_checksum(csum_skb,
+ skb_checksum_start_offset(csum_skb),
+ csum_skb->len -
+ skb_checksum_start_offset(csum_skb),
+ 0);
+ dev_kfree_skb(csum_skb);
+ dma_sync_single_for_cpu(trans->dev, hdr_tb_phys,
+ hdr_tb_len, DMA_TO_DEVICE);
+ tcph->check = csum_fold(csum);
+ dma_sync_single_for_device(trans->dev, hdr_tb_phys,
+ hdr_tb_len, DMA_TO_DEVICE);
+ }
+ }
+
+ /* re -add the WiFi header and IV */
+ skb_push(skb, hdr_len + iv_len);
+
+ return 0;
+
+out_unmap:
+ iwl_pcie_tfd_unmap(trans, out_meta, &txq->tfds[q->write_ptr]);
+ return ret;
+}
+#else /* CONFIG_INET */
+static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb,
+ struct iwl_txq *txq, u8 hdr_len,
+ struct iwl_cmd_meta *out_meta,
+ struct iwl_device_cmd *dev_cmd, u16 tb1_len)
+{
+ /* No A-MSDU without CONFIG_INET */
+ WARN_ON(1);
+
+ return -1;
+}
+#endif /* CONFIG_INET */
+
int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
struct iwl_device_cmd *dev_cmd, int txq_id)
{
@@ -1805,12 +2137,11 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
struct iwl_queue *q;
dma_addr_t tb0_phys, tb1_phys, scratch_phys;
void *tb1_addr;
- u16 len, tb1_len, tb2_len;
+ u16 len, tb1_len;
bool wait_write_ptr;
__le16 fc;
u8 hdr_len;
u16 wifi_seq;
- int i;
txq = &trans_pcie->txq[txq_id];
q = &txq->q;
@@ -1819,6 +2150,19 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
"TX on unused queue %d\n", txq_id))
return -EINVAL;
+ if (unlikely(trans_pcie->sw_csum_tx &&
+ skb->ip_summed == CHECKSUM_PARTIAL)) {
+ int offs = skb_checksum_start_offset(skb);
+ int csum_offs = offs + skb->csum_offset;
+ __wsum csum;
+
+ if (skb_ensure_writable(skb, csum_offs + sizeof(__sum16)))
+ return -1;
+
+ csum = skb_checksum(skb, offs, skb->len - offs, 0);
+ *(__sum16 *)(skb->data + csum_offs) = csum_fold(csum);
+ }
+
if (skb_is_nonlinear(skb) &&
skb_shinfo(skb)->nr_frags > IWL_PCIE_MAX_FRAGS &&
__skb_linearize(skb))
@@ -1893,57 +2237,20 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
goto out_err;
iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, false);
- /*
- * Set up TFD's third entry to point directly to remainder
- * of skb's head, if any
- */
- tb2_len = skb_headlen(skb) - hdr_len;
- if (tb2_len > 0) {
- dma_addr_t tb2_phys = dma_map_single(trans->dev,
- skb->data + hdr_len,
- tb2_len, DMA_TO_DEVICE);
- if (unlikely(dma_mapping_error(trans->dev, tb2_phys))) {
- iwl_pcie_tfd_unmap(trans, out_meta,
- &txq->tfds[q->write_ptr]);
- goto out_err;
- }
- iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, false);
- }
-
- /* set up the remaining entries to point to the data */
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- dma_addr_t tb_phys;
- int tb_idx;
-
- if (!skb_frag_size(frag))
- continue;
-
- tb_phys = skb_frag_dma_map(trans->dev, frag, 0,
- skb_frag_size(frag), DMA_TO_DEVICE);
-
- if (unlikely(dma_mapping_error(trans->dev, tb_phys))) {
- iwl_pcie_tfd_unmap(trans, out_meta,
- &txq->tfds[q->write_ptr]);
+ if (ieee80211_is_data_qos(fc) &&
+ (*ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_A_MSDU_PRESENT)) {
+ if (unlikely(iwl_fill_data_tbs_amsdu(trans, skb, txq, hdr_len,
+ out_meta, dev_cmd,
+ tb1_len)))
goto out_err;
- }
- tb_idx = iwl_pcie_txq_build_tfd(trans, txq, tb_phys,
- skb_frag_size(frag), false);
-
- out_meta->flags |= BIT(tb_idx + CMD_TB_BITMAP_POS);
+ } else if (unlikely(iwl_fill_data_tbs(trans, skb, txq, hdr_len,
+ out_meta, dev_cmd, tb1_len))) {
+ goto out_err;
}
/* Set up entry for this TFD in Tx byte-count array */
iwl_pcie_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len));
- trace_iwlwifi_dev_tx(trans->dev, skb,
- &txq->tfds[txq->q.write_ptr],
- sizeof(struct iwl_tfd),
- &dev_cmd->hdr, IWL_HCMD_SCRATCHBUF_SIZE + tb1_len,
- skb->data + hdr_len, tb2_len);
- trace_iwlwifi_dev_tx_data(trans->dev, skb,
- hdr_len, skb->len - hdr_len);
-
wait_write_ptr = ieee80211_has_morefrags(fc);
/* start timer if queue currently empty */
diff --git a/drivers/net/wireless/intersil/Kconfig b/drivers/net/wireless/intersil/Kconfig
new file mode 100644
index 000000000000..9da136049955
--- /dev/null
+++ b/drivers/net/wireless/intersil/Kconfig
@@ -0,0 +1,38 @@
+config WLAN_VENDOR_INTERSIL
+ bool "Intersil devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_INTERSIL
+
+source "drivers/net/wireless/intersil/hostap/Kconfig"
+source "drivers/net/wireless/intersil/orinoco/Kconfig"
+source "drivers/net/wireless/intersil/p54/Kconfig"
+
+config PRISM54
+ tristate 'Intersil Prism GT/Duette/Indigo PCI/Cardbus (DEPRECATED)'
+ depends on PCI
+ select WIRELESS_EXT
+ select WEXT_SPY
+ select WEXT_PRIV
+ select FW_LOADER
+ ---help---
+ This enables support for FullMAC PCI/Cardbus prism54 devices. This
+ driver is now deprecated in favor for the SoftMAC driver, p54pci.
+ p54pci supports FullMAC PCI/Cardbus devices as well.
+
+ For more information refer to the p54 wiki:
+
+ http://wireless.kernel.org/en/users/Drivers/p54
+
+ Note: You need a motherboard with DMA support to use any of these cards
+
+ When built as module you get the module prism54
+
+endif # WLAN_VENDOR_INTERSIL
diff --git a/drivers/net/wireless/intersil/Makefile b/drivers/net/wireless/intersil/Makefile
new file mode 100644
index 000000000000..9a8cbfee3ea5
--- /dev/null
+++ b/drivers/net/wireless/intersil/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_HOSTAP) += hostap/
+obj-$(CONFIG_HERMES) += orinoco/
+obj-$(CONFIG_P54_COMMON) += p54/
+obj-$(CONFIG_PRISM54) += prism54/
diff --git a/drivers/net/wireless/hostap/Kconfig b/drivers/net/wireless/intersil/hostap/Kconfig
index 287d82728bc3..287d82728bc3 100644
--- a/drivers/net/wireless/hostap/Kconfig
+++ b/drivers/net/wireless/intersil/hostap/Kconfig
diff --git a/drivers/net/wireless/hostap/Makefile b/drivers/net/wireless/intersil/hostap/Makefile
index b8e41a702c00..b8e41a702c00 100644
--- a/drivers/net/wireless/hostap/Makefile
+++ b/drivers/net/wireless/intersil/hostap/Makefile
diff --git a/drivers/net/wireless/hostap/hostap.h b/drivers/net/wireless/intersil/hostap/hostap.h
index ce8721fbc10e..ce8721fbc10e 100644
--- a/drivers/net/wireless/hostap/hostap.h
+++ b/drivers/net/wireless/intersil/hostap/hostap.h
diff --git a/drivers/net/wireless/hostap/hostap_80211.h b/drivers/net/wireless/intersil/hostap/hostap_80211.h
index ed98ce7c8f65..ed98ce7c8f65 100644
--- a/drivers/net/wireless/hostap/hostap_80211.h
+++ b/drivers/net/wireless/intersil/hostap/hostap_80211.h
diff --git a/drivers/net/wireless/hostap/hostap_80211_rx.c b/drivers/net/wireless/intersil/hostap/hostap_80211_rx.c
index 599f30f22841..599f30f22841 100644
--- a/drivers/net/wireless/hostap/hostap_80211_rx.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_80211_rx.c
diff --git a/drivers/net/wireless/hostap/hostap_80211_tx.c b/drivers/net/wireless/intersil/hostap/hostap_80211_tx.c
index 055e11d353ca..055e11d353ca 100644
--- a/drivers/net/wireless/hostap/hostap_80211_tx.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_80211_tx.c
diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/intersil/hostap/hostap_ap.c
index c995ace153ee..c995ace153ee 100644
--- a/drivers/net/wireless/hostap/hostap_ap.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_ap.c
diff --git a/drivers/net/wireless/hostap/hostap_ap.h b/drivers/net/wireless/intersil/hostap/hostap_ap.h
index 334e2d0b8e11..334e2d0b8e11 100644
--- a/drivers/net/wireless/hostap/hostap_ap.h
+++ b/drivers/net/wireless/intersil/hostap/hostap_ap.h
diff --git a/drivers/net/wireless/hostap/hostap_common.h b/drivers/net/wireless/intersil/hostap/hostap_common.h
index 4230102ac9e4..4230102ac9e4 100644
--- a/drivers/net/wireless/hostap/hostap_common.h
+++ b/drivers/net/wireless/intersil/hostap/hostap_common.h
diff --git a/drivers/net/wireless/hostap/hostap_config.h b/drivers/net/wireless/intersil/hostap/hostap_config.h
index 2c8f71f0ed45..2c8f71f0ed45 100644
--- a/drivers/net/wireless/hostap/hostap_config.h
+++ b/drivers/net/wireless/intersil/hostap/hostap_config.h
diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/intersil/hostap/hostap_cs.c
index 50033aa7c7d5..74f63b7bf7b4 100644
--- a/drivers/net/wireless/hostap/hostap_cs.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_cs.c
@@ -473,7 +473,7 @@ static int prism2_config(struct pcmcia_device *link)
struct net_device *dev;
struct hostap_interface *iface;
local_info_t *local;
- int ret = 1;
+ int ret;
struct hostap_cs_priv *hw_priv;
unsigned long flags;
@@ -502,8 +502,10 @@ static int prism2_config(struct pcmcia_device *link)
/* Need to allocate net_device before requesting IRQ handler */
dev = prism2_init_local_data(&prism2_pccard_funcs, 0,
&link->dev);
- if (dev == NULL)
+ if (!dev) {
+ ret = -ENOMEM;
goto failed;
+ }
link->priv = dev;
iface = netdev_priv(dev);
diff --git a/drivers/net/wireless/hostap/hostap_download.c b/drivers/net/wireless/intersil/hostap/hostap_download.c
index 705fe668b969..705fe668b969 100644
--- a/drivers/net/wireless/hostap/hostap_download.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_download.c
diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/intersil/hostap/hostap_hw.c
index 6df3ee561d52..6df3ee561d52 100644
--- a/drivers/net/wireless/hostap/hostap_hw.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_hw.c
diff --git a/drivers/net/wireless/hostap/hostap_info.c b/drivers/net/wireless/intersil/hostap/hostap_info.c
index 7635ac4f6679..7635ac4f6679 100644
--- a/drivers/net/wireless/hostap/hostap_info.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_info.c
diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/intersil/hostap/hostap_ioctl.c
index 3e5fa7872b64..3e5fa7872b64 100644
--- a/drivers/net/wireless/hostap/hostap_ioctl.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_ioctl.c
diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/intersil/hostap/hostap_main.c
index 80d4228ba754..80d4228ba754 100644
--- a/drivers/net/wireless/hostap/hostap_main.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_main.c
diff --git a/drivers/net/wireless/hostap/hostap_pci.c b/drivers/net/wireless/intersil/hostap/hostap_pci.c
index c864ef4b0015..c864ef4b0015 100644
--- a/drivers/net/wireless/hostap/hostap_pci.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_pci.c
diff --git a/drivers/net/wireless/hostap/hostap_plx.c b/drivers/net/wireless/intersil/hostap/hostap_plx.c
index 4901a99c6c59..4901a99c6c59 100644
--- a/drivers/net/wireless/hostap/hostap_plx.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_plx.c
diff --git a/drivers/net/wireless/hostap/hostap_proc.c b/drivers/net/wireless/intersil/hostap/hostap_proc.c
index dd84557cf957..dd84557cf957 100644
--- a/drivers/net/wireless/hostap/hostap_proc.c
+++ b/drivers/net/wireless/intersil/hostap/hostap_proc.c
diff --git a/drivers/net/wireless/hostap/hostap_wlan.h b/drivers/net/wireless/intersil/hostap/hostap_wlan.h
index ca25283e1c92..ca25283e1c92 100644
--- a/drivers/net/wireless/hostap/hostap_wlan.h
+++ b/drivers/net/wireless/intersil/hostap/hostap_wlan.h
diff --git a/drivers/net/wireless/orinoco/Kconfig b/drivers/net/wireless/intersil/orinoco/Kconfig
index f6fa3f4e294f..f6fa3f4e294f 100644
--- a/drivers/net/wireless/orinoco/Kconfig
+++ b/drivers/net/wireless/intersil/orinoco/Kconfig
diff --git a/drivers/net/wireless/orinoco/Makefile b/drivers/net/wireless/intersil/orinoco/Makefile
index bfdefb85abcd..bfdefb85abcd 100644
--- a/drivers/net/wireless/orinoco/Makefile
+++ b/drivers/net/wireless/intersil/orinoco/Makefile
diff --git a/drivers/net/wireless/orinoco/airport.c b/drivers/net/wireless/intersil/orinoco/airport.c
index 77e6c53040a3..77e6c53040a3 100644
--- a/drivers/net/wireless/orinoco/airport.c
+++ b/drivers/net/wireless/intersil/orinoco/airport.c
diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/intersil/orinoco/cfg.c
index 0f6ea316e38e..0f6ea316e38e 100644
--- a/drivers/net/wireless/orinoco/cfg.c
+++ b/drivers/net/wireless/intersil/orinoco/cfg.c
diff --git a/drivers/net/wireless/orinoco/cfg.h b/drivers/net/wireless/intersil/orinoco/cfg.h
index 3ddc96a06cd7..3ddc96a06cd7 100644
--- a/drivers/net/wireless/orinoco/cfg.h
+++ b/drivers/net/wireless/intersil/orinoco/cfg.h
diff --git a/drivers/net/wireless/orinoco/fw.c b/drivers/net/wireless/intersil/orinoco/fw.c
index 400a35217644..400a35217644 100644
--- a/drivers/net/wireless/orinoco/fw.c
+++ b/drivers/net/wireless/intersil/orinoco/fw.c
diff --git a/drivers/net/wireless/orinoco/fw.h b/drivers/net/wireless/intersil/orinoco/fw.h
index aca63e3c4b5b..aca63e3c4b5b 100644
--- a/drivers/net/wireless/orinoco/fw.h
+++ b/drivers/net/wireless/intersil/orinoco/fw.h
diff --git a/drivers/net/wireless/orinoco/hermes.c b/drivers/net/wireless/intersil/orinoco/hermes.c
index 43790fbea0e0..43790fbea0e0 100644
--- a/drivers/net/wireless/orinoco/hermes.c
+++ b/drivers/net/wireless/intersil/orinoco/hermes.c
diff --git a/drivers/net/wireless/orinoco/hermes.h b/drivers/net/wireless/intersil/orinoco/hermes.h
index 28a42448d329..28a42448d329 100644
--- a/drivers/net/wireless/orinoco/hermes.h
+++ b/drivers/net/wireless/intersil/orinoco/hermes.h
diff --git a/drivers/net/wireless/orinoco/hermes_dld.c b/drivers/net/wireless/intersil/orinoco/hermes_dld.c
index 4a10b7aca043..4a10b7aca043 100644
--- a/drivers/net/wireless/orinoco/hermes_dld.c
+++ b/drivers/net/wireless/intersil/orinoco/hermes_dld.c
diff --git a/drivers/net/wireless/orinoco/hermes_dld.h b/drivers/net/wireless/intersil/orinoco/hermes_dld.h
index b5377e232c63..b5377e232c63 100644
--- a/drivers/net/wireless/orinoco/hermes_dld.h
+++ b/drivers/net/wireless/intersil/orinoco/hermes_dld.h
diff --git a/drivers/net/wireless/orinoco/hermes_rid.h b/drivers/net/wireless/intersil/orinoco/hermes_rid.h
index 42eb67dea1df..42eb67dea1df 100644
--- a/drivers/net/wireless/orinoco/hermes_rid.h
+++ b/drivers/net/wireless/intersil/orinoco/hermes_rid.h
diff --git a/drivers/net/wireless/orinoco/hw.c b/drivers/net/wireless/intersil/orinoco/hw.c
index e27e32851f1e..e27e32851f1e 100644
--- a/drivers/net/wireless/orinoco/hw.c
+++ b/drivers/net/wireless/intersil/orinoco/hw.c
diff --git a/drivers/net/wireless/orinoco/hw.h b/drivers/net/wireless/intersil/orinoco/hw.h
index 466d1ede76f1..466d1ede76f1 100644
--- a/drivers/net/wireless/orinoco/hw.h
+++ b/drivers/net/wireless/intersil/orinoco/hw.h
diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/intersil/orinoco/main.c
index 7b5c554323c7..7b5c554323c7 100644
--- a/drivers/net/wireless/orinoco/main.c
+++ b/drivers/net/wireless/intersil/orinoco/main.c
diff --git a/drivers/net/wireless/orinoco/main.h b/drivers/net/wireless/intersil/orinoco/main.h
index 5a8fec26136e..5a8fec26136e 100644
--- a/drivers/net/wireless/orinoco/main.h
+++ b/drivers/net/wireless/intersil/orinoco/main.h
diff --git a/drivers/net/wireless/orinoco/mic.c b/drivers/net/wireless/intersil/orinoco/mic.c
index fce4a843e656..fce4a843e656 100644
--- a/drivers/net/wireless/orinoco/mic.c
+++ b/drivers/net/wireless/intersil/orinoco/mic.c
diff --git a/drivers/net/wireless/orinoco/mic.h b/drivers/net/wireless/intersil/orinoco/mic.h
index 04d05bc566d6..04d05bc566d6 100644
--- a/drivers/net/wireless/orinoco/mic.h
+++ b/drivers/net/wireless/intersil/orinoco/mic.h
diff --git a/drivers/net/wireless/orinoco/orinoco.h b/drivers/net/wireless/intersil/orinoco/orinoco.h
index eebd2be21ee9..eebd2be21ee9 100644
--- a/drivers/net/wireless/orinoco/orinoco.h
+++ b/drivers/net/wireless/intersil/orinoco/orinoco.h
diff --git a/drivers/net/wireless/orinoco/orinoco_cs.c b/drivers/net/wireless/intersil/orinoco/orinoco_cs.c
index a956f965a1e5..a956f965a1e5 100644
--- a/drivers/net/wireless/orinoco/orinoco_cs.c
+++ b/drivers/net/wireless/intersil/orinoco/orinoco_cs.c
diff --git a/drivers/net/wireless/orinoco/orinoco_nortel.c b/drivers/net/wireless/intersil/orinoco/orinoco_nortel.c
index 048693b6c6c2..048693b6c6c2 100644
--- a/drivers/net/wireless/orinoco/orinoco_nortel.c
+++ b/drivers/net/wireless/intersil/orinoco/orinoco_nortel.c
diff --git a/drivers/net/wireless/orinoco/orinoco_pci.c b/drivers/net/wireless/intersil/orinoco/orinoco_pci.c
index 4938a2208a37..4938a2208a37 100644
--- a/drivers/net/wireless/orinoco/orinoco_pci.c
+++ b/drivers/net/wireless/intersil/orinoco/orinoco_pci.c
diff --git a/drivers/net/wireless/orinoco/orinoco_pci.h b/drivers/net/wireless/intersil/orinoco/orinoco_pci.h
index 43f5b9f5a0b0..43f5b9f5a0b0 100644
--- a/drivers/net/wireless/orinoco/orinoco_pci.h
+++ b/drivers/net/wireless/intersil/orinoco/orinoco_pci.h
diff --git a/drivers/net/wireless/orinoco/orinoco_plx.c b/drivers/net/wireless/intersil/orinoco/orinoco_plx.c
index 221352027779..221352027779 100644
--- a/drivers/net/wireless/orinoco/orinoco_plx.c
+++ b/drivers/net/wireless/intersil/orinoco/orinoco_plx.c
diff --git a/drivers/net/wireless/orinoco/orinoco_tmd.c b/drivers/net/wireless/intersil/orinoco/orinoco_tmd.c
index 20ce569b8a43..20ce569b8a43 100644
--- a/drivers/net/wireless/orinoco/orinoco_tmd.c
+++ b/drivers/net/wireless/intersil/orinoco/orinoco_tmd.c
diff --git a/drivers/net/wireless/orinoco/orinoco_usb.c b/drivers/net/wireless/intersil/orinoco/orinoco_usb.c
index f2cd513d54b2..f2cd513d54b2 100644
--- a/drivers/net/wireless/orinoco/orinoco_usb.c
+++ b/drivers/net/wireless/intersil/orinoco/orinoco_usb.c
diff --git a/drivers/net/wireless/orinoco/scan.c b/drivers/net/wireless/intersil/orinoco/scan.c
index 2c66166add70..2c66166add70 100644
--- a/drivers/net/wireless/orinoco/scan.c
+++ b/drivers/net/wireless/intersil/orinoco/scan.c
diff --git a/drivers/net/wireless/orinoco/scan.h b/drivers/net/wireless/intersil/orinoco/scan.h
index 27281fb0a6dc..27281fb0a6dc 100644
--- a/drivers/net/wireless/orinoco/scan.h
+++ b/drivers/net/wireless/intersil/orinoco/scan.h
diff --git a/drivers/net/wireless/orinoco/spectrum_cs.c b/drivers/net/wireless/intersil/orinoco/spectrum_cs.c
index b60048c95e0a..b60048c95e0a 100644
--- a/drivers/net/wireless/orinoco/spectrum_cs.c
+++ b/drivers/net/wireless/intersil/orinoco/spectrum_cs.c
diff --git a/drivers/net/wireless/orinoco/wext.c b/drivers/net/wireless/intersil/orinoco/wext.c
index 1d4dae422106..1d4dae422106 100644
--- a/drivers/net/wireless/orinoco/wext.c
+++ b/drivers/net/wireless/intersil/orinoco/wext.c
diff --git a/drivers/net/wireless/orinoco/wext.h b/drivers/net/wireless/intersil/orinoco/wext.h
index 1479f4e26dde..1479f4e26dde 100644
--- a/drivers/net/wireless/orinoco/wext.h
+++ b/drivers/net/wireless/intersil/orinoco/wext.h
diff --git a/drivers/net/wireless/p54/Kconfig b/drivers/net/wireless/intersil/p54/Kconfig
index cdafb8c73e82..cdafb8c73e82 100644
--- a/drivers/net/wireless/p54/Kconfig
+++ b/drivers/net/wireless/intersil/p54/Kconfig
diff --git a/drivers/net/wireless/p54/Makefile b/drivers/net/wireless/intersil/p54/Makefile
index b542e68f1781..b542e68f1781 100644
--- a/drivers/net/wireless/p54/Makefile
+++ b/drivers/net/wireless/intersil/p54/Makefile
diff --git a/drivers/net/wireless/p54/eeprom.c b/drivers/net/wireless/intersil/p54/eeprom.c
index 2fe713eda7ad..2fe713eda7ad 100644
--- a/drivers/net/wireless/p54/eeprom.c
+++ b/drivers/net/wireless/intersil/p54/eeprom.c
diff --git a/drivers/net/wireless/p54/eeprom.h b/drivers/net/wireless/intersil/p54/eeprom.h
index 20ebe39a3f4e..20ebe39a3f4e 100644
--- a/drivers/net/wireless/p54/eeprom.h
+++ b/drivers/net/wireless/intersil/p54/eeprom.h
diff --git a/drivers/net/wireless/p54/fwio.c b/drivers/net/wireless/intersil/p54/fwio.c
index 257a9eadd595..257a9eadd595 100644
--- a/drivers/net/wireless/p54/fwio.c
+++ b/drivers/net/wireless/intersil/p54/fwio.c
diff --git a/drivers/net/wireless/p54/led.c b/drivers/net/wireless/intersil/p54/led.c
index 9a8fedd3c0f5..9a8fedd3c0f5 100644
--- a/drivers/net/wireless/p54/led.c
+++ b/drivers/net/wireless/intersil/p54/led.c
diff --git a/drivers/net/wireless/p54/lmac.h b/drivers/net/wireless/intersil/p54/lmac.h
index de1d46bf97df..de1d46bf97df 100644
--- a/drivers/net/wireless/p54/lmac.h
+++ b/drivers/net/wireless/intersil/p54/lmac.h
diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/intersil/p54/main.c
index 7805864e76f9..7805864e76f9 100644
--- a/drivers/net/wireless/p54/main.c
+++ b/drivers/net/wireless/intersil/p54/main.c
diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/intersil/p54/p54.h
index 40b401ed6845..40b401ed6845 100644
--- a/drivers/net/wireless/p54/p54.h
+++ b/drivers/net/wireless/intersil/p54/p54.h
diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/intersil/p54/p54pci.c
index 27a49068d32d..27a49068d32d 100644
--- a/drivers/net/wireless/p54/p54pci.c
+++ b/drivers/net/wireless/intersil/p54/p54pci.c
diff --git a/drivers/net/wireless/p54/p54pci.h b/drivers/net/wireless/intersil/p54/p54pci.h
index 68405c142f97..68405c142f97 100644
--- a/drivers/net/wireless/p54/p54pci.h
+++ b/drivers/net/wireless/intersil/p54/p54pci.h
diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/intersil/p54/p54spi.c
index 7ab2f43ab425..7ab2f43ab425 100644
--- a/drivers/net/wireless/p54/p54spi.c
+++ b/drivers/net/wireless/intersil/p54/p54spi.c
diff --git a/drivers/net/wireless/p54/p54spi.h b/drivers/net/wireless/intersil/p54/p54spi.h
index dfaa62aaeb07..dfaa62aaeb07 100644
--- a/drivers/net/wireless/p54/p54spi.h
+++ b/drivers/net/wireless/intersil/p54/p54spi.h
diff --git a/drivers/net/wireless/p54/p54spi_eeprom.h b/drivers/net/wireless/intersil/p54/p54spi_eeprom.h
index 0b7bfb0adcf2..0b7bfb0adcf2 100644
--- a/drivers/net/wireless/p54/p54spi_eeprom.h
+++ b/drivers/net/wireless/intersil/p54/p54spi_eeprom.h
diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/intersil/p54/p54usb.c
index 043bd1c23c19..043bd1c23c19 100644
--- a/drivers/net/wireless/p54/p54usb.c
+++ b/drivers/net/wireless/intersil/p54/p54usb.c
diff --git a/drivers/net/wireless/p54/p54usb.h b/drivers/net/wireless/intersil/p54/p54usb.h
index a5f5f0fea3bd..a5f5f0fea3bd 100644
--- a/drivers/net/wireless/p54/p54usb.h
+++ b/drivers/net/wireless/intersil/p54/p54usb.h
diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/intersil/p54/txrx.c
index 24e5ff9a9272..24e5ff9a9272 100644
--- a/drivers/net/wireless/p54/txrx.c
+++ b/drivers/net/wireless/intersil/p54/txrx.c
diff --git a/drivers/net/wireless/prism54/Makefile b/drivers/net/wireless/intersil/prism54/Makefile
index fad305c76737..fad305c76737 100644
--- a/drivers/net/wireless/prism54/Makefile
+++ b/drivers/net/wireless/intersil/prism54/Makefile
diff --git a/drivers/net/wireless/prism54/isl_38xx.c b/drivers/net/wireless/intersil/prism54/isl_38xx.c
index 333c1a2f882e..333c1a2f882e 100644
--- a/drivers/net/wireless/prism54/isl_38xx.c
+++ b/drivers/net/wireless/intersil/prism54/isl_38xx.c
diff --git a/drivers/net/wireless/prism54/isl_38xx.h b/drivers/net/wireless/intersil/prism54/isl_38xx.h
index 547ab885610b..547ab885610b 100644
--- a/drivers/net/wireless/prism54/isl_38xx.h
+++ b/drivers/net/wireless/intersil/prism54/isl_38xx.h
diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/intersil/prism54/isl_ioctl.c
index ecbb0546cf3e..48e8a978a832 100644
--- a/drivers/net/wireless/prism54/isl_ioctl.c
+++ b/drivers/net/wireless/intersil/prism54/isl_ioctl.c
@@ -2036,7 +2036,7 @@ format_event(islpci_private *priv, char *dest, const char *str,
mlme->address,
(error ? (mlme->code ? " : REJECTED " : " : ACCEPTED ")
: ""), mlme->code);
- BUG_ON(n > IW_CUSTOM_MAX);
+ WARN_ON(n >= IW_CUSTOM_MAX);
*length = n;
}
diff --git a/drivers/net/wireless/prism54/isl_ioctl.h b/drivers/net/wireless/intersil/prism54/isl_ioctl.h
index 842a2549facc..842a2549facc 100644
--- a/drivers/net/wireless/prism54/isl_ioctl.h
+++ b/drivers/net/wireless/intersil/prism54/isl_ioctl.h
diff --git a/drivers/net/wireless/prism54/isl_oid.h b/drivers/net/wireless/intersil/prism54/isl_oid.h
index 83fec557997e..83fec557997e 100644
--- a/drivers/net/wireless/prism54/isl_oid.h
+++ b/drivers/net/wireless/intersil/prism54/isl_oid.h
diff --git a/drivers/net/wireless/prism54/islpci_dev.c b/drivers/net/wireless/intersil/prism54/islpci_dev.c
index 931cf440ff18..84a42012aeae 100644
--- a/drivers/net/wireless/prism54/islpci_dev.c
+++ b/drivers/net/wireless/intersil/prism54/islpci_dev.c
@@ -707,7 +707,9 @@ islpci_alloc_memory(islpci_private *priv)
pci_map_single(priv->pdev, (void *) skb->data,
MAX_FRAGMENT_SIZE_RX + 2,
PCI_DMA_FROMDEVICE);
- if (!priv->pci_map_rx_address[counter]) {
+ if (pci_dma_mapping_error(priv->pdev,
+ priv->pci_map_rx_address[counter])) {
+ priv->pci_map_rx_address[counter] = 0;
/* error mapping the buffer to device
accessible memory address */
printk(KERN_ERR "failed to map skb DMA'able\n");
diff --git a/drivers/net/wireless/prism54/islpci_dev.h b/drivers/net/wireless/intersil/prism54/islpci_dev.h
index f6f088e05fe4..f6f088e05fe4 100644
--- a/drivers/net/wireless/prism54/islpci_dev.h
+++ b/drivers/net/wireless/intersil/prism54/islpci_dev.h
diff --git a/drivers/net/wireless/prism54/islpci_eth.c b/drivers/net/wireless/intersil/prism54/islpci_eth.c
index 674658f2e6ef..d83f6332019e 100644
--- a/drivers/net/wireless/prism54/islpci_eth.c
+++ b/drivers/net/wireless/intersil/prism54/islpci_eth.c
@@ -190,7 +190,7 @@ islpci_eth_transmit(struct sk_buff *skb, struct net_device *ndev)
pci_map_address = pci_map_single(priv->pdev,
(void *) skb->data, skb->len,
PCI_DMA_TODEVICE);
- if (unlikely(pci_map_address == 0)) {
+ if (pci_dma_mapping_error(priv->pdev, pci_map_address)) {
printk(KERN_WARNING "%s: cannot map buffer to PCI\n",
ndev->name);
goto drop_free;
@@ -448,7 +448,8 @@ islpci_eth_receive(islpci_private *priv)
pci_map_single(priv->pdev, (void *) skb->data,
MAX_FRAGMENT_SIZE_RX + 2,
PCI_DMA_FROMDEVICE);
- if (unlikely(!priv->pci_map_rx_address[index])) {
+ if (pci_dma_mapping_error(priv->pdev,
+ priv->pci_map_rx_address[index])) {
/* error mapping the buffer to device accessible memory address */
DEBUG(SHOW_ERROR_MESSAGES,
"Error mapping DMA address\n");
diff --git a/drivers/net/wireless/prism54/islpci_eth.h b/drivers/net/wireless/intersil/prism54/islpci_eth.h
index 80f50f1bc6f2..80f50f1bc6f2 100644
--- a/drivers/net/wireless/prism54/islpci_eth.h
+++ b/drivers/net/wireless/intersil/prism54/islpci_eth.h
diff --git a/drivers/net/wireless/prism54/islpci_hotplug.c b/drivers/net/wireless/intersil/prism54/islpci_hotplug.c
index 300c846ea087..300c846ea087 100644
--- a/drivers/net/wireless/prism54/islpci_hotplug.c
+++ b/drivers/net/wireless/intersil/prism54/islpci_hotplug.c
diff --git a/drivers/net/wireless/prism54/islpci_mgt.c b/drivers/net/wireless/intersil/prism54/islpci_mgt.c
index 0de14dfa68cc..53d7a1705e8e 100644
--- a/drivers/net/wireless/prism54/islpci_mgt.c
+++ b/drivers/net/wireless/intersil/prism54/islpci_mgt.c
@@ -130,7 +130,7 @@ islpci_mgmt_rx_fill(struct net_device *ndev)
buf->pci_addr = pci_map_single(priv->pdev, buf->mem,
MGMT_FRAME_SIZE,
PCI_DMA_FROMDEVICE);
- if (!buf->pci_addr) {
+ if (pci_dma_mapping_error(priv->pdev, buf->pci_addr)) {
printk(KERN_WARNING
"Failed to make memory DMA'able.\n");
return -ENOMEM;
@@ -217,7 +217,7 @@ islpci_mgt_transmit(struct net_device *ndev, int operation, unsigned long oid,
err = -ENOMEM;
buf.pci_addr = pci_map_single(priv->pdev, buf.mem, frag_len,
PCI_DMA_TODEVICE);
- if (!buf.pci_addr) {
+ if (pci_dma_mapping_error(priv->pdev, buf.pci_addr)) {
printk(KERN_WARNING "%s: cannot map PCI memory for mgmt\n",
ndev->name);
goto error_free;
diff --git a/drivers/net/wireless/prism54/islpci_mgt.h b/drivers/net/wireless/intersil/prism54/islpci_mgt.h
index 700c434c8803..700c434c8803 100644
--- a/drivers/net/wireless/prism54/islpci_mgt.h
+++ b/drivers/net/wireless/intersil/prism54/islpci_mgt.h
diff --git a/drivers/net/wireless/prism54/oid_mgt.c b/drivers/net/wireless/intersil/prism54/oid_mgt.c
index 3a8d2dbcfecd..6528ed5b9b1d 100644
--- a/drivers/net/wireless/prism54/oid_mgt.c
+++ b/drivers/net/wireless/intersil/prism54/oid_mgt.c
@@ -424,7 +424,7 @@ mgt_set_request(islpci_private *priv, enum oid_num_t n, int extra, void *data)
void *cache, *_data = data;
u32 oid;
- BUG_ON(OID_NUM_LAST <= n);
+ BUG_ON(n >= OID_NUM_LAST);
BUG_ON(extra > isl_oid[n].range);
if (!priv->mib)
@@ -485,7 +485,7 @@ mgt_set_varlen(islpci_private *priv, enum oid_num_t n, void *data, int extra_len
int dlen;
u32 oid;
- BUG_ON(OID_NUM_LAST <= n);
+ BUG_ON(n >= OID_NUM_LAST);
dlen = isl_oid[n].size;
oid = isl_oid[n].oid;
@@ -524,7 +524,7 @@ mgt_get_request(islpci_private *priv, enum oid_num_t n, int extra, void *data,
void *cache, *_res = NULL;
u32 oid;
- BUG_ON(OID_NUM_LAST <= n);
+ BUG_ON(n >= OID_NUM_LAST);
BUG_ON(extra > isl_oid[n].range);
res->ptr = NULL;
@@ -626,7 +626,7 @@ mgt_commit_list(islpci_private *priv, enum oid_num_t *l, int n)
void
mgt_set(islpci_private *priv, enum oid_num_t n, void *data)
{
- BUG_ON(OID_NUM_LAST <= n);
+ BUG_ON(n >= OID_NUM_LAST);
BUG_ON(priv->mib[n] == NULL);
memcpy(priv->mib[n], data, isl_oid[n].size);
@@ -636,7 +636,7 @@ mgt_set(islpci_private *priv, enum oid_num_t n, void *data)
void
mgt_get(islpci_private *priv, enum oid_num_t n, void *res)
{
- BUG_ON(OID_NUM_LAST <= n);
+ BUG_ON(n >= OID_NUM_LAST);
BUG_ON(priv->mib[n] == NULL);
BUG_ON(res == NULL);
diff --git a/drivers/net/wireless/prism54/oid_mgt.h b/drivers/net/wireless/intersil/prism54/oid_mgt.h
index cf5141df8474..cf5141df8474 100644
--- a/drivers/net/wireless/prism54/oid_mgt.h
+++ b/drivers/net/wireless/intersil/prism54/oid_mgt.h
diff --git a/drivers/net/wireless/prism54/prismcompat.h b/drivers/net/wireless/intersil/prism54/prismcompat.h
index bc1401eb4b9d..bc1401eb4b9d 100644
--- a/drivers/net/wireless/prism54/prismcompat.h
+++ b/drivers/net/wireless/intersil/prism54/prismcompat.h
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index c00a7daaa4bc..c32889a1e39c 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -495,6 +495,9 @@ struct mac80211_hwsim_data {
const struct ieee80211_regdomain *regd;
struct ieee80211_channel *tmp_chan;
+ struct ieee80211_channel *roc_chan;
+ u32 roc_duration;
+ struct delayed_work roc_start;
struct delayed_work roc_done;
struct delayed_work hw_scan;
struct cfg80211_scan_request *hw_scan_request;
@@ -514,6 +517,7 @@ struct mac80211_hwsim_data {
bool ps_poll_pending;
struct dentry *debugfs;
+ uintptr_t pending_cookie;
struct sk_buff_head pending; /* packets pending */
/*
* Only radios in the same group can communicate together (the
@@ -810,6 +814,9 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_skb);
struct ieee80211_rate *txrate = ieee80211_get_tx_rate(hw, info);
+ if (WARN_ON(!txrate))
+ return;
+
if (!netif_running(hwsim_mon))
return;
@@ -960,6 +967,7 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
unsigned int hwsim_flags = 0;
int i;
struct hwsim_tx_rate tx_attempts[IEEE80211_TX_MAX_RATES];
+ uintptr_t cookie;
if (data->ps != PS_DISABLED)
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
@@ -1018,7 +1026,10 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
goto nla_put_failure;
/* We create a cookie to identify this skb */
- if (nla_put_u64(skb, HWSIM_ATTR_COOKIE, (unsigned long) my_skb))
+ data->pending_cookie++;
+ cookie = data->pending_cookie;
+ info->rate_driver_data[0] = (void *)cookie;
+ if (nla_put_u64(skb, HWSIM_ATTR_COOKIE, cookie))
goto nla_put_failure;
genlmsg_end(skb, msg_head);
@@ -1247,6 +1258,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
{
struct mac80211_hwsim_data *data = hw->priv;
struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (void *)skb->data;
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_channel *channel;
bool ack;
@@ -1292,6 +1304,22 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
ARRAY_SIZE(txi->control.rates));
txi->rate_driver_data[0] = channel;
+
+ if (skb->len >= 24 + 8 &&
+ ieee80211_is_probe_resp(hdr->frame_control)) {
+ /* fake header transmission time */
+ struct ieee80211_mgmt *mgmt;
+ struct ieee80211_rate *txrate;
+ u64 ts;
+
+ mgmt = (struct ieee80211_mgmt *)skb->data;
+ txrate = ieee80211_get_tx_rate(hw, txi);
+ ts = mac80211_hwsim_get_tsf_raw();
+ mgmt->u.probe_resp.timestamp =
+ cpu_to_le64(ts + data->tsf_offset +
+ 24 * 8 * 10 / txrate->bitrate);
+ }
+
mac80211_hwsim_monitor_rx(hw, skb, channel);
/* wmediumd mode check */
@@ -1871,7 +1899,8 @@ static void hw_scan_work(struct work_struct *work)
req->channels[hwsim->scan_chan_idx]->center_freq);
hwsim->tmp_chan = req->channels[hwsim->scan_chan_idx];
- if (hwsim->tmp_chan->flags & IEEE80211_CHAN_NO_IR ||
+ if (hwsim->tmp_chan->flags & (IEEE80211_CHAN_NO_IR |
+ IEEE80211_CHAN_RADAR) ||
!req->n_ssids) {
dwell = 120;
} else {
@@ -1987,6 +2016,23 @@ static void mac80211_hwsim_sw_scan_complete(struct ieee80211_hw *hw,
mutex_unlock(&hwsim->mutex);
}
+static void hw_roc_start(struct work_struct *work)
+{
+ struct mac80211_hwsim_data *hwsim =
+ container_of(work, struct mac80211_hwsim_data, roc_start.work);
+
+ mutex_lock(&hwsim->mutex);
+
+ wiphy_debug(hwsim->hw->wiphy, "hwsim ROC begins\n");
+ hwsim->tmp_chan = hwsim->roc_chan;
+ ieee80211_ready_on_channel(hwsim->hw);
+
+ ieee80211_queue_delayed_work(hwsim->hw, &hwsim->roc_done,
+ msecs_to_jiffies(hwsim->roc_duration));
+
+ mutex_unlock(&hwsim->mutex);
+}
+
static void hw_roc_done(struct work_struct *work)
{
struct mac80211_hwsim_data *hwsim =
@@ -2014,16 +2060,14 @@ static int mac80211_hwsim_roc(struct ieee80211_hw *hw,
return -EBUSY;
}
- hwsim->tmp_chan = chan;
+ hwsim->roc_chan = chan;
+ hwsim->roc_duration = duration;
mutex_unlock(&hwsim->mutex);
wiphy_debug(hw->wiphy, "hwsim ROC (%d MHz, %d ms)\n",
chan->center_freq, duration);
+ ieee80211_queue_delayed_work(hw, &hwsim->roc_start, HZ/50);
- ieee80211_ready_on_channel(hw);
-
- ieee80211_queue_delayed_work(hw, &hwsim->roc_done,
- msecs_to_jiffies(duration));
return 0;
}
@@ -2031,6 +2075,7 @@ static int mac80211_hwsim_croc(struct ieee80211_hw *hw)
{
struct mac80211_hwsim_data *hwsim = hw->priv;
+ cancel_delayed_work_sync(&hwsim->roc_start);
cancel_delayed_work_sync(&hwsim->roc_done);
mutex_lock(&hwsim->mutex);
@@ -2375,6 +2420,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
hw->wiphy->n_iface_combinations = ARRAY_SIZE(hwsim_if_comb);
}
+ INIT_DELAYED_WORK(&data->roc_start, hw_roc_start);
INIT_DELAYED_WORK(&data->roc_done, hw_roc_done);
INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work);
@@ -2411,6 +2457,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
NL80211_FEATURE_STATIC_SMPS |
NL80211_FEATURE_DYNAMIC_SMPS |
NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
+ wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
/* ask mac80211 to reserve space for magic */
hw->vif_data_size = sizeof(struct hwsim_vif_priv);
@@ -2710,7 +2757,7 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
struct mac80211_hwsim_data *data2;
struct ieee80211_tx_info *txi;
struct hwsim_tx_rate *tx_attempts;
- unsigned long ret_skb_ptr;
+ u64 ret_skb_cookie;
struct sk_buff *skb, *tmp;
const u8 *src;
unsigned int hwsim_flags;
@@ -2728,7 +2775,7 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
src = (void *)nla_data(info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]);
hwsim_flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]);
- ret_skb_ptr = nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]);
+ ret_skb_cookie = nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]);
data2 = get_hwsim_data_ref_from_addr(src);
if (!data2)
@@ -2736,7 +2783,12 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
/* look for the skb matching the cookie passed back from user */
skb_queue_walk_safe(&data2->pending, skb, tmp) {
- if ((unsigned long)skb == ret_skb_ptr) {
+ u64 skb_cookie;
+
+ txi = IEEE80211_SKB_CB(skb);
+ skb_cookie = (u64)(uintptr_t)txi->rate_driver_data[0];
+
+ if (skb_cookie == ret_skb_cookie) {
skb_unlink(skb, &data2->pending);
found = true;
break;
@@ -2827,10 +2879,25 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
/* A frame is received from user space */
memset(&rx_status, 0, sizeof(rx_status));
- /* TODO: Check ATTR_FREQ if it exists, and maybe throw away off-channel
- * packets?
- */
- rx_status.freq = data2->channel->center_freq;
+ if (info->attrs[HWSIM_ATTR_FREQ]) {
+ /* throw away off-channel packets, but allow both the temporary
+ * ("hw" scan/remain-on-channel) and regular channel, since the
+ * internal datapath also allows this
+ */
+ mutex_lock(&data2->mutex);
+ rx_status.freq = nla_get_u32(info->attrs[HWSIM_ATTR_FREQ]);
+
+ if (rx_status.freq != data2->channel->center_freq &&
+ (!data2->tmp_chan ||
+ rx_status.freq != data2->tmp_chan->center_freq)) {
+ mutex_unlock(&data2->mutex);
+ goto out;
+ }
+ mutex_unlock(&data2->mutex);
+ } else {
+ rx_status.freq = data2->channel->center_freq;
+ }
+
rx_status.band = data2->channel->band;
rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
diff --git a/drivers/net/wireless/marvell/Kconfig b/drivers/net/wireless/marvell/Kconfig
new file mode 100644
index 000000000000..4938c7ec0009
--- /dev/null
+++ b/drivers/net/wireless/marvell/Kconfig
@@ -0,0 +1,27 @@
+config WLAN_VENDOR_MARVELL
+ bool "Marvell devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_MARVELL
+
+source "drivers/net/wireless/marvell/libertas/Kconfig"
+source "drivers/net/wireless/marvell/libertas_tf/Kconfig"
+source "drivers/net/wireless/marvell/mwifiex/Kconfig"
+
+config MWL8K
+ tristate "Marvell 88W8xxx PCI/PCIe Wireless support"
+ depends on MAC80211 && PCI
+ ---help---
+ This driver supports Marvell TOPDOG 802.11 wireless cards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mwl8k. If unsure, say N.
+
+endif # WLAN_VENDOR_MARVELL
diff --git a/drivers/net/wireless/marvell/Makefile b/drivers/net/wireless/marvell/Makefile
new file mode 100644
index 000000000000..1b0a7d2bc8e6
--- /dev/null
+++ b/drivers/net/wireless/marvell/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_LIBERTAS) += libertas/
+
+obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/
+obj-$(CONFIG_MWIFIEX) += mwifiex/
+
+obj-$(CONFIG_MWL8K) += mwl8k.o
diff --git a/drivers/net/wireless/libertas/Kconfig b/drivers/net/wireless/marvell/libertas/Kconfig
index e6268ceacbf1..e6268ceacbf1 100644
--- a/drivers/net/wireless/libertas/Kconfig
+++ b/drivers/net/wireless/marvell/libertas/Kconfig
diff --git a/drivers/net/wireless/libertas/LICENSE b/drivers/net/wireless/marvell/libertas/LICENSE
index 8862742213b9..8862742213b9 100644
--- a/drivers/net/wireless/libertas/LICENSE
+++ b/drivers/net/wireless/marvell/libertas/LICENSE
diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/marvell/libertas/Makefile
index eac72f7bd341..eac72f7bd341 100644
--- a/drivers/net/wireless/libertas/Makefile
+++ b/drivers/net/wireless/marvell/libertas/Makefile
diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/marvell/libertas/README
index 1a554a685e91..1a554a685e91 100644
--- a/drivers/net/wireless/libertas/README
+++ b/drivers/net/wireless/marvell/libertas/README
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/marvell/libertas/cfg.c
index 8317afd065b4..86955c416b30 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/marvell/libertas/cfg.c
@@ -1108,7 +1108,7 @@ static int lbs_associate(struct lbs_private *priv,
size_t len, resp_ie_len;
int status;
int ret;
- u8 *pos = &(cmd->iebuf[0]);
+ u8 *pos;
u8 *tmp;
lbs_deb_enter(LBS_DEB_CFG80211);
@@ -1117,6 +1117,7 @@ static int lbs_associate(struct lbs_private *priv,
ret = -ENOMEM;
goto done;
}
+ pos = &cmd->iebuf[0];
/*
* cmd 50 00
diff --git a/drivers/net/wireless/libertas/cfg.h b/drivers/net/wireless/marvell/libertas/cfg.h
index acccc2922401..acccc2922401 100644
--- a/drivers/net/wireless/libertas/cfg.h
+++ b/drivers/net/wireless/marvell/libertas/cfg.h
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/marvell/libertas/cmd.c
index 0387a5b380c8..0387a5b380c8 100644
--- a/drivers/net/wireless/libertas/cmd.c
+++ b/drivers/net/wireless/marvell/libertas/cmd.c
diff --git a/drivers/net/wireless/libertas/cmd.h b/drivers/net/wireless/marvell/libertas/cmd.h
index 0c5444b02c64..0c5444b02c64 100644
--- a/drivers/net/wireless/libertas/cmd.h
+++ b/drivers/net/wireless/marvell/libertas/cmd.h
diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/marvell/libertas/cmdresp.c
index e5442e8956f7..e5442e8956f7 100644
--- a/drivers/net/wireless/libertas/cmdresp.c
+++ b/drivers/net/wireless/marvell/libertas/cmdresp.c
diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/marvell/libertas/debugfs.c
index 26cbf1dcc662..faed1823c58e 100644
--- a/drivers/net/wireless/libertas/debugfs.c
+++ b/drivers/net/wireless/marvell/libertas/debugfs.c
@@ -56,19 +56,15 @@ static ssize_t lbs_sleepparams_write(struct file *file,
loff_t *ppos)
{
struct lbs_private *priv = file->private_data;
- ssize_t buf_size, ret;
+ ssize_t ret;
struct sleep_params sp;
int p1, p2, p3, p4, p5, p6;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
- if (!buf)
- return -ENOMEM;
+ char *buf;
+
+ buf = memdup_user_nul(user_buf, min(count, len - 1));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
- buf_size = min(count, len - 1);
- if (copy_from_user(buf, user_buf, buf_size)) {
- ret = -EFAULT;
- goto out_unlock;
- }
ret = sscanf(buf, "%d %d %d %d %d %d", &p1, &p2, &p3, &p4, &p5, &p6);
if (ret != 6) {
ret = -EINVAL;
@@ -88,7 +84,7 @@ static ssize_t lbs_sleepparams_write(struct file *file,
ret = -EINVAL;
out_unlock:
- free_page(addr);
+ kfree(buf);
return ret;
}
@@ -125,18 +121,14 @@ static ssize_t lbs_host_sleep_write(struct file *file,
loff_t *ppos)
{
struct lbs_private *priv = file->private_data;
- ssize_t buf_size, ret;
+ ssize_t ret;
int host_sleep;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
- if (!buf)
- return -ENOMEM;
+ char *buf;
+
+ buf = memdup_user_nul(user_buf, min(count, len - 1));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
- buf_size = min(count, len - 1);
- if (copy_from_user(buf, user_buf, buf_size)) {
- ret = -EFAULT;
- goto out_unlock;
- }
ret = sscanf(buf, "%d", &host_sleep);
if (ret != 1) {
ret = -EINVAL;
@@ -162,7 +154,7 @@ static ssize_t lbs_host_sleep_write(struct file *file,
ret = count;
out_unlock:
- free_page(addr);
+ kfree(buf);
return ret;
}
@@ -281,21 +273,15 @@ static ssize_t lbs_threshold_write(uint16_t tlv_type, uint16_t event_mask,
struct cmd_ds_802_11_subscribe_event *events;
struct mrvl_ie_thresholds *tlv;
struct lbs_private *priv = file->private_data;
- ssize_t buf_size;
int value, freq, new_mask;
uint16_t curr_mask;
char *buf;
int ret;
- buf = (char *)get_zeroed_page(GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
+ buf = memdup_user_nul(userbuf, min(count, len - 1));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
- buf_size = min(count, len - 1);
- if (copy_from_user(buf, userbuf, buf_size)) {
- ret = -EFAULT;
- goto out_page;
- }
ret = sscanf(buf, "%d %d %d", &value, &freq, &new_mask);
if (ret != 3) {
ret = -EINVAL;
@@ -343,7 +329,7 @@ static ssize_t lbs_threshold_write(uint16_t tlv_type, uint16_t event_mask,
out_events:
kfree(events);
out_page:
- free_page((unsigned long)buf);
+ kfree(buf);
return ret;
}
@@ -472,22 +458,15 @@ static ssize_t lbs_rdmac_write(struct file *file,
size_t count, loff_t *ppos)
{
struct lbs_private *priv = file->private_data;
- ssize_t res, buf_size;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
- if (!buf)
- return -ENOMEM;
+ char *buf;
+
+ buf = memdup_user_nul(userbuf, min(count, len - 1));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
- buf_size = min(count, len - 1);
- if (copy_from_user(buf, userbuf, buf_size)) {
- res = -EFAULT;
- goto out_unlock;
- }
priv->mac_offset = simple_strtoul(buf, NULL, 16);
- res = count;
-out_unlock:
- free_page(addr);
- return res;
+ kfree(buf);
+ return count;
}
static ssize_t lbs_wrmac_write(struct file *file,
@@ -496,18 +475,14 @@ static ssize_t lbs_wrmac_write(struct file *file,
{
struct lbs_private *priv = file->private_data;
- ssize_t res, buf_size;
+ ssize_t res;
u32 offset, value;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
- if (!buf)
- return -ENOMEM;
+ char *buf;
+
+ buf = memdup_user_nul(userbuf, min(count, len - 1));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
- buf_size = min(count, len - 1);
- if (copy_from_user(buf, userbuf, buf_size)) {
- res = -EFAULT;
- goto out_unlock;
- }
res = sscanf(buf, "%x %x", &offset, &value);
if (res != 2) {
res = -EFAULT;
@@ -520,7 +495,7 @@ static ssize_t lbs_wrmac_write(struct file *file,
if (!res)
res = count;
out_unlock:
- free_page(addr);
+ kfree(buf);
return res;
}
@@ -554,22 +529,16 @@ static ssize_t lbs_rdbbp_write(struct file *file,
size_t count, loff_t *ppos)
{
struct lbs_private *priv = file->private_data;
- ssize_t res, buf_size;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
- if (!buf)
- return -ENOMEM;
+ char *buf;
+
+ buf = memdup_user_nul(userbuf, min(count, len - 1));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
- buf_size = min(count, len - 1);
- if (copy_from_user(buf, userbuf, buf_size)) {
- res = -EFAULT;
- goto out_unlock;
- }
priv->bbp_offset = simple_strtoul(buf, NULL, 16);
- res = count;
-out_unlock:
- free_page(addr);
- return res;
+ kfree(buf);
+
+ return count;
}
static ssize_t lbs_wrbbp_write(struct file *file,
@@ -578,18 +547,14 @@ static ssize_t lbs_wrbbp_write(struct file *file,
{
struct lbs_private *priv = file->private_data;
- ssize_t res, buf_size;
+ ssize_t res;
u32 offset, value;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
- if (!buf)
- return -ENOMEM;
+ char *buf;
+
+ buf = memdup_user_nul(userbuf, min(count, len - 1));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
- buf_size = min(count, len - 1);
- if (copy_from_user(buf, userbuf, buf_size)) {
- res = -EFAULT;
- goto out_unlock;
- }
res = sscanf(buf, "%x %x", &offset, &value);
if (res != 2) {
res = -EFAULT;
@@ -602,7 +567,7 @@ static ssize_t lbs_wrbbp_write(struct file *file,
if (!res)
res = count;
out_unlock:
- free_page(addr);
+ kfree(buf);
return res;
}
@@ -636,22 +601,15 @@ static ssize_t lbs_rdrf_write(struct file *file,
size_t count, loff_t *ppos)
{
struct lbs_private *priv = file->private_data;
- ssize_t res, buf_size;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
- if (!buf)
- return -ENOMEM;
+ char *buf;
+
+ buf = memdup_user_nul(userbuf, min(count, len - 1));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
- buf_size = min(count, len - 1);
- if (copy_from_user(buf, userbuf, buf_size)) {
- res = -EFAULT;
- goto out_unlock;
- }
priv->rf_offset = simple_strtoul(buf, NULL, 16);
- res = count;
-out_unlock:
- free_page(addr);
- return res;
+ kfree(buf);
+ return count;
}
static ssize_t lbs_wrrf_write(struct file *file,
@@ -660,18 +618,14 @@ static ssize_t lbs_wrrf_write(struct file *file,
{
struct lbs_private *priv = file->private_data;
- ssize_t res, buf_size;
+ ssize_t res;
u32 offset, value;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
- if (!buf)
- return -ENOMEM;
+ char *buf;
+
+ buf = memdup_user_nul(userbuf, min(count, len - 1));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
- buf_size = min(count, len - 1);
- if (copy_from_user(buf, userbuf, buf_size)) {
- res = -EFAULT;
- goto out_unlock;
- }
res = sscanf(buf, "%x %x", &offset, &value);
if (res != 2) {
res = -EFAULT;
@@ -684,7 +638,7 @@ static ssize_t lbs_wrrf_write(struct file *file,
if (!res)
res = count;
out_unlock:
- free_page(addr);
+ kfree(buf);
return res;
}
@@ -915,16 +869,9 @@ static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf,
if (cnt == 0)
return 0;
- pdata = kmalloc(cnt + 1, GFP_KERNEL);
- if (pdata == NULL)
- return 0;
-
- if (copy_from_user(pdata, buf, cnt)) {
- lbs_deb_debugfs("Copy from user failed\n");
- kfree(pdata);
- return 0;
- }
- pdata[cnt] = '\0';
+ pdata = memdup_user_nul(buf, cnt);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
p0 = pdata;
for (i = 0; i < num_of_items; i++) {
diff --git a/drivers/net/wireless/libertas/debugfs.h b/drivers/net/wireless/marvell/libertas/debugfs.h
index f2b9c7ffe0fd..f2b9c7ffe0fd 100644
--- a/drivers/net/wireless/libertas/debugfs.h
+++ b/drivers/net/wireless/marvell/libertas/debugfs.h
diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/marvell/libertas/decl.h
index 84a3aa7ac570..84a3aa7ac570 100644
--- a/drivers/net/wireless/libertas/decl.h
+++ b/drivers/net/wireless/marvell/libertas/decl.h
diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/marvell/libertas/defs.h
index 407784aca627..407784aca627 100644
--- a/drivers/net/wireless/libertas/defs.h
+++ b/drivers/net/wireless/marvell/libertas/defs.h
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/marvell/libertas/dev.h
index 6bd1608992b0..6bd1608992b0 100644
--- a/drivers/net/wireless/libertas/dev.h
+++ b/drivers/net/wireless/marvell/libertas/dev.h
diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/marvell/libertas/ethtool.c
index f955b2d66ed6..f955b2d66ed6 100644
--- a/drivers/net/wireless/libertas/ethtool.c
+++ b/drivers/net/wireless/marvell/libertas/ethtool.c
diff --git a/drivers/net/wireless/libertas/firmware.c b/drivers/net/wireless/marvell/libertas/firmware.c
index 51b92b5df119..51b92b5df119 100644
--- a/drivers/net/wireless/libertas/firmware.c
+++ b/drivers/net/wireless/marvell/libertas/firmware.c
diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/marvell/libertas/host.h
index 96726f79a1dd..96726f79a1dd 100644
--- a/drivers/net/wireless/libertas/host.h
+++ b/drivers/net/wireless/marvell/libertas/host.h
diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/marvell/libertas/if_cs.c
index f499efc6abcf..f499efc6abcf 100644
--- a/drivers/net/wireless/libertas/if_cs.c
+++ b/drivers/net/wireless/marvell/libertas/if_cs.c
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/marvell/libertas/if_sdio.c
index 33ceda296c9c..68fd3a9779bd 100644
--- a/drivers/net/wireless/libertas/if_sdio.c
+++ b/drivers/net/wireless/marvell/libertas/if_sdio.c
@@ -228,7 +228,7 @@ static int if_sdio_handle_cmd(struct if_sdio_card *card,
memcpy(priv->resp_buf[i], buffer, size);
lbs_notify_command_response(priv, i);
- spin_unlock_irqrestore(&card->priv->driver_lock, flags);
+ spin_unlock_irqrestore(&priv->driver_lock, flags);
ret = 0;
diff --git a/drivers/net/wireless/libertas/if_sdio.h b/drivers/net/wireless/marvell/libertas/if_sdio.h
index 62fda3592f67..62fda3592f67 100644
--- a/drivers/net/wireless/libertas/if_sdio.h
+++ b/drivers/net/wireless/marvell/libertas/if_sdio.h
diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/marvell/libertas/if_spi.c
index 82c0796377aa..82c0796377aa 100644
--- a/drivers/net/wireless/libertas/if_spi.c
+++ b/drivers/net/wireless/marvell/libertas/if_spi.c
diff --git a/drivers/net/wireless/libertas/if_spi.h b/drivers/net/wireless/marvell/libertas/if_spi.h
index e450e31fd11d..e450e31fd11d 100644
--- a/drivers/net/wireless/libertas/if_spi.h
+++ b/drivers/net/wireless/marvell/libertas/if_spi.h
diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/marvell/libertas/if_usb.c
index dff08a2896a3..dff08a2896a3 100644
--- a/drivers/net/wireless/libertas/if_usb.c
+++ b/drivers/net/wireless/marvell/libertas/if_usb.c
diff --git a/drivers/net/wireless/libertas/if_usb.h b/drivers/net/wireless/marvell/libertas/if_usb.h
index 6e42eac331de..6e42eac331de 100644
--- a/drivers/net/wireless/libertas/if_usb.h
+++ b/drivers/net/wireless/marvell/libertas/if_usb.h
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/marvell/libertas/main.c
index 8079560f4965..8079560f4965 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/marvell/libertas/main.c
diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/marvell/libertas/mesh.c
index d0c881dd5846..d0c881dd5846 100644
--- a/drivers/net/wireless/libertas/mesh.c
+++ b/drivers/net/wireless/marvell/libertas/mesh.c
diff --git a/drivers/net/wireless/libertas/mesh.h b/drivers/net/wireless/marvell/libertas/mesh.h
index 6603f341c874..6603f341c874 100644
--- a/drivers/net/wireless/libertas/mesh.h
+++ b/drivers/net/wireless/marvell/libertas/mesh.h
diff --git a/drivers/net/wireless/libertas/radiotap.h b/drivers/net/wireless/marvell/libertas/radiotap.h
index b3c8ea6d610e..b3c8ea6d610e 100644
--- a/drivers/net/wireless/libertas/radiotap.h
+++ b/drivers/net/wireless/marvell/libertas/radiotap.h
diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/marvell/libertas/rx.c
index e446fed7b345..e446fed7b345 100644
--- a/drivers/net/wireless/libertas/rx.c
+++ b/drivers/net/wireless/marvell/libertas/rx.c
diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/marvell/libertas/tx.c
index c025f9c18282..c025f9c18282 100644
--- a/drivers/net/wireless/libertas/tx.c
+++ b/drivers/net/wireless/marvell/libertas/tx.c
diff --git a/drivers/net/wireless/libertas/types.h b/drivers/net/wireless/marvell/libertas/types.h
index cf1d9b047ee6..cf1d9b047ee6 100644
--- a/drivers/net/wireless/libertas/types.h
+++ b/drivers/net/wireless/marvell/libertas/types.h
diff --git a/drivers/net/wireless/marvell/libertas_tf/Kconfig b/drivers/net/wireless/marvell/libertas_tf/Kconfig
new file mode 100644
index 000000000000..b5557af90048
--- /dev/null
+++ b/drivers/net/wireless/marvell/libertas_tf/Kconfig
@@ -0,0 +1,18 @@
+config LIBERTAS_THINFIRM
+ tristate "Marvell 8xxx Libertas WLAN driver support with thin firmware"
+ depends on MAC80211
+ select FW_LOADER
+ ---help---
+ A library for Marvell Libertas 8xxx devices using thinfirm.
+
+config LIBERTAS_THINFIRM_DEBUG
+ bool "Enable full debugging output in the Libertas thin firmware module."
+ depends on LIBERTAS_THINFIRM
+ ---help---
+ Debugging support.
+
+config LIBERTAS_THINFIRM_USB
+ tristate "Marvell Libertas 8388 USB 802.11b/g cards with thin firmware"
+ depends on LIBERTAS_THINFIRM && USB
+ ---help---
+ A driver for Marvell Libertas 8388 USB devices using thinfirm.
diff --git a/drivers/net/wireless/libertas_tf/Makefile b/drivers/net/wireless/marvell/libertas_tf/Makefile
index ff5544d6ac9d..ff5544d6ac9d 100644
--- a/drivers/net/wireless/libertas_tf/Makefile
+++ b/drivers/net/wireless/marvell/libertas_tf/Makefile
diff --git a/drivers/net/wireless/libertas_tf/cmd.c b/drivers/net/wireless/marvell/libertas_tf/cmd.c
index 909ac3685010..909ac3685010 100644
--- a/drivers/net/wireless/libertas_tf/cmd.c
+++ b/drivers/net/wireless/marvell/libertas_tf/cmd.c
diff --git a/drivers/net/wireless/libertas_tf/deb_defs.h b/drivers/net/wireless/marvell/libertas_tf/deb_defs.h
index 4bd3dc5adf7c..4bd3dc5adf7c 100644
--- a/drivers/net/wireless/libertas_tf/deb_defs.h
+++ b/drivers/net/wireless/marvell/libertas_tf/deb_defs.h
diff --git a/drivers/net/wireless/libertas_tf/if_usb.c b/drivers/net/wireless/marvell/libertas_tf/if_usb.c
index 799a2efe5793..799a2efe5793 100644
--- a/drivers/net/wireless/libertas_tf/if_usb.c
+++ b/drivers/net/wireless/marvell/libertas_tf/if_usb.c
diff --git a/drivers/net/wireless/libertas_tf/if_usb.h b/drivers/net/wireless/marvell/libertas_tf/if_usb.h
index 6fa5b3f59efe..6fa5b3f59efe 100644
--- a/drivers/net/wireless/libertas_tf/if_usb.h
+++ b/drivers/net/wireless/marvell/libertas_tf/if_usb.h
diff --git a/drivers/net/wireless/libertas_tf/libertas_tf.h b/drivers/net/wireless/marvell/libertas_tf/libertas_tf.h
index ad77b92d0b41..ad77b92d0b41 100644
--- a/drivers/net/wireless/libertas_tf/libertas_tf.h
+++ b/drivers/net/wireless/marvell/libertas_tf/libertas_tf.h
diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/marvell/libertas_tf/main.c
index a47f0acc099a..a47f0acc099a 100644
--- a/drivers/net/wireless/libertas_tf/main.c
+++ b/drivers/net/wireless/marvell/libertas_tf/main.c
diff --git a/drivers/net/wireless/mwifiex/11ac.c b/drivers/net/wireless/marvell/mwifiex/11ac.c
index 59d23fb2365f..59d23fb2365f 100644
--- a/drivers/net/wireless/mwifiex/11ac.c
+++ b/drivers/net/wireless/marvell/mwifiex/11ac.c
diff --git a/drivers/net/wireless/mwifiex/11ac.h b/drivers/net/wireless/marvell/mwifiex/11ac.h
index 1ca92c7a8a4a..1ca92c7a8a4a 100644
--- a/drivers/net/wireless/mwifiex/11ac.h
+++ b/drivers/net/wireless/marvell/mwifiex/11ac.h
diff --git a/drivers/net/wireless/mwifiex/11h.c b/drivers/net/wireless/marvell/mwifiex/11h.c
index 71a1b580796f..71a1b580796f 100644
--- a/drivers/net/wireless/mwifiex/11h.c
+++ b/drivers/net/wireless/marvell/mwifiex/11h.c
diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/marvell/mwifiex/11n.c
index c174e79e6df2..c174e79e6df2 100644
--- a/drivers/net/wireless/mwifiex/11n.c
+++ b/drivers/net/wireless/marvell/mwifiex/11n.c
diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/marvell/mwifiex/11n.h
index afdd58aa90de..afdd58aa90de 100644
--- a/drivers/net/wireless/mwifiex/11n.h
+++ b/drivers/net/wireless/marvell/mwifiex/11n.h
diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c
index aa498e0d2204..1efef3b8273d 100644
--- a/drivers/net/wireless/mwifiex/11n_aggr.c
+++ b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c
@@ -203,8 +203,6 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
skb_aggr->priority = skb_src->priority;
skb_aggr->tstamp = skb_src->tstamp;
- skb_aggr->tstamp = ktime_get_real();
-
do {
/* Check if AMSDU can accommodate this MSDU */
if (skb_tailroom(skb_aggr) < (skb_src->len + LLC_SNAP_LEN))
diff --git a/drivers/net/wireless/mwifiex/11n_aggr.h b/drivers/net/wireless/marvell/mwifiex/11n_aggr.h
index 0cd2a3eb6c17..0cd2a3eb6c17 100644
--- a/drivers/net/wireless/mwifiex/11n_aggr.h
+++ b/drivers/net/wireless/marvell/mwifiex/11n_aggr.h
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c
index b3970a8c9e48..09578c6cde59 100644
--- a/drivers/net/wireless/mwifiex/11n_rxreorder.c
+++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c
@@ -48,7 +48,17 @@ static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv,
priv->wdev.iftype, 0, false);
while (!skb_queue_empty(&list)) {
+ struct rx_packet_hdr *rx_hdr;
+
rx_skb = __skb_dequeue(&list);
+ rx_hdr = (struct rx_packet_hdr *)rx_skb->data;
+ if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+ ntohs(rx_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) {
+ mwifiex_process_tdls_action_frame(priv,
+ (u8 *)rx_hdr,
+ skb->len);
+ }
+
ret = mwifiex_recv_packet(priv, rx_skb);
if (ret == -1)
mwifiex_dbg(priv->adapter, ERROR,
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.h b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.h
index 63ecea89b4ab..63ecea89b4ab 100644
--- a/drivers/net/wireless/mwifiex/11n_rxreorder.h
+++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.h
diff --git a/drivers/net/wireless/mwifiex/Kconfig b/drivers/net/wireless/marvell/mwifiex/Kconfig
index 279167ddd293..279167ddd293 100644
--- a/drivers/net/wireless/mwifiex/Kconfig
+++ b/drivers/net/wireless/marvell/mwifiex/Kconfig
diff --git a/drivers/net/wireless/mwifiex/Makefile b/drivers/net/wireless/marvell/mwifiex/Makefile
index fdfd9bf15ed4..fdfd9bf15ed4 100644
--- a/drivers/net/wireless/mwifiex/Makefile
+++ b/drivers/net/wireless/marvell/mwifiex/Makefile
diff --git a/drivers/net/wireless/mwifiex/README b/drivers/net/wireless/marvell/mwifiex/README
index 2f0f9b5609d0..2f0f9b5609d0 100644
--- a/drivers/net/wireless/mwifiex/README
+++ b/drivers/net/wireless/marvell/mwifiex/README
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
index 4073116e6e9f..e7adef72c05f 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
@@ -26,12 +26,10 @@ module_param(reg_alpha2, charp, 0);
static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = {
{
- .max = 2, .types = BIT(NL80211_IFTYPE_STATION) |
+ .max = 3, .types = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_P2P_GO) |
- BIT(NL80211_IFTYPE_P2P_CLIENT),
- },
- {
- .max = 1, .types = BIT(NL80211_IFTYPE_AP),
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_AP),
},
};
@@ -827,18 +825,26 @@ mwifiex_init_new_priv_params(struct mwifiex_private *priv,
switch (type) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
+ priv->bss_num = mwifiex_get_unused_bss_num(adapter,
+ MWIFIEX_BSS_TYPE_STA);
priv->bss_role = MWIFIEX_BSS_ROLE_STA;
priv->bss_type = MWIFIEX_BSS_TYPE_STA;
break;
case NL80211_IFTYPE_P2P_CLIENT:
+ priv->bss_num = mwifiex_get_unused_bss_num(adapter,
+ MWIFIEX_BSS_TYPE_P2P);
priv->bss_role = MWIFIEX_BSS_ROLE_STA;
priv->bss_type = MWIFIEX_BSS_TYPE_P2P;
break;
case NL80211_IFTYPE_P2P_GO:
+ priv->bss_num = mwifiex_get_unused_bss_num(adapter,
+ MWIFIEX_BSS_TYPE_P2P);
priv->bss_role = MWIFIEX_BSS_ROLE_UAP;
priv->bss_type = MWIFIEX_BSS_TYPE_P2P;
break;
case NL80211_IFTYPE_AP:
+ priv->bss_num = mwifiex_get_unused_bss_num(adapter,
+ MWIFIEX_BSS_TYPE_UAP);
priv->bss_type = MWIFIEX_BSS_TYPE_UAP;
priv->bss_role = MWIFIEX_BSS_ROLE_UAP;
break;
@@ -1533,6 +1539,7 @@ static const u32 mwifiex_cipher_suites[] = {
WLAN_CIPHER_SUITE_WEP104,
WLAN_CIPHER_SUITE_TKIP,
WLAN_CIPHER_SUITE_CCMP,
+ WLAN_CIPHER_SUITE_SMS4,
WLAN_CIPHER_SUITE_AES_CMAC,
};
@@ -1701,6 +1708,11 @@ mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
u8 deauth_mac[ETH_ALEN];
unsigned long flags;
+ if (!priv->bss_started && priv->wdev.cac_started) {
+ mwifiex_dbg(priv->adapter, INFO, "%s: abort CAC!\n", __func__);
+ mwifiex_abort_cac(priv);
+ }
+
if (list_empty(&priv->sta_list) || !priv->bss_started)
return 0;
@@ -2608,7 +2620,8 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
return ERR_PTR(-EINVAL);
}
- priv = mwifiex_get_unused_priv(adapter);
+ priv = mwifiex_get_unused_priv_by_bss_type(
+ adapter, MWIFIEX_BSS_TYPE_STA);
if (!priv) {
mwifiex_dbg(adapter, ERROR,
"could not get free private struct\n");
@@ -2627,7 +2640,6 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II;
priv->bss_priority = 0;
priv->bss_role = MWIFIEX_BSS_ROLE_STA;
- priv->bss_num = adapter->curr_iface_comb.sta_intf;
break;
case NL80211_IFTYPE_AP:
@@ -2638,7 +2650,8 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
return ERR_PTR(-EINVAL);
}
- priv = mwifiex_get_unused_priv(adapter);
+ priv = mwifiex_get_unused_priv_by_bss_type(
+ adapter, MWIFIEX_BSS_TYPE_UAP);
if (!priv) {
mwifiex_dbg(adapter, ERROR,
"could not get free private struct\n");
@@ -2653,7 +2666,6 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
priv->bss_priority = 0;
priv->bss_role = MWIFIEX_BSS_ROLE_UAP;
priv->bss_started = 0;
- priv->bss_num = adapter->curr_iface_comb.uap_intf;
priv->bss_mode = type;
break;
@@ -2665,7 +2677,8 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
return ERR_PTR(-EINVAL);
}
- priv = mwifiex_get_unused_priv(adapter);
+ priv = mwifiex_get_unused_priv_by_bss_type(
+ adapter, MWIFIEX_BSS_TYPE_P2P);
if (!priv) {
mwifiex_dbg(adapter, ERROR,
"could not get free private struct\n");
@@ -2689,7 +2702,6 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
priv->bss_priority = MWIFIEX_BSS_ROLE_STA;
priv->bss_role = MWIFIEX_BSS_ROLE_STA;
priv->bss_started = 0;
- priv->bss_num = adapter->curr_iface_comb.p2p_intf;
if (mwifiex_cfg80211_init_p2p_client(priv)) {
memset(&priv->wdev, 0, sizeof(priv->wdev));
@@ -2914,6 +2926,12 @@ mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq,
dont_care_byte = true;
}
+ /* wildcard bytes record as the offset
+ * before the valid byte
+ */
+ if (!valid_byte_cnt && !dont_care_byte)
+ pat->pkt_offset++;
+
if (valid_byte_cnt > max_byte_seq)
return false;
}
@@ -3141,8 +3159,8 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy,
memset(&hs_cfg, 0, sizeof(hs_cfg));
hs_cfg.is_invoke_hostcmd = false;
hs_cfg.conditions = HS_CFG_COND_MAC_EVENT;
- hs_cfg.gpio = HS_CFG_GPIO_DEF;
- hs_cfg.gap = HS_CFG_GAP_DEF;
+ hs_cfg.gpio = adapter->hs_cfg.gpio;
+ hs_cfg.gap = adapter->hs_cfg.gap;
ret = mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET,
MWIFIEX_SYNC_CMD, &hs_cfg);
if (ret) {
@@ -3802,6 +3820,10 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
wiphy->cipher_suites = mwifiex_cipher_suites;
wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites);
+ if (adapter->region_code)
+ wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS |
+ REGULATORY_COUNTRY_IE_IGNORE;
+
ether_addr_copy(wiphy->perm_addr, adapter->perm_addr);
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME |
@@ -3862,11 +3884,15 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
"driver hint alpha2: %2.2s\n", reg_alpha2);
regulatory_hint(wiphy, reg_alpha2);
} else {
- country_code = mwifiex_11d_code_2_region(adapter->region_code);
- if (country_code)
- mwifiex_dbg(adapter, WARN,
- "ignoring F/W country code %2.2s\n",
- country_code);
+ if (adapter->region_code == 0x00) {
+ mwifiex_dbg(adapter, WARN, "Ignore world regulatory domain\n");
+ } else {
+ country_code =
+ mwifiex_11d_code_2_region(adapter->region_code);
+ if (country_code &&
+ regulatory_hint(wiphy, country_code))
+ mwifiex_dbg(priv->adapter, ERROR, "regulatory_hint() failed\n");
+ }
}
mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
diff --git a/drivers/net/wireless/mwifiex/cfg80211.h b/drivers/net/wireless/marvell/mwifiex/cfg80211.h
index 908367857d58..908367857d58 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.h
+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.h
diff --git a/drivers/net/wireless/mwifiex/cfp.c b/drivers/net/wireless/marvell/mwifiex/cfp.c
index 3ddb8ec676ed..09fae27140f7 100644
--- a/drivers/net/wireless/mwifiex/cfp.c
+++ b/drivers/net/wireless/marvell/mwifiex/cfp.c
@@ -66,8 +66,8 @@ static u8 supported_rates_bg[BG_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x0c,
0x12, 0x16, 0x18, 0x24, 0x30, 0x48,
0x60, 0x6c, 0 };
-u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x10, 0x20, 0x30,
- 0x32, 0x40, 0x41, 0xff };
+u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x00, 0x10, 0x20, 0x30,
+ 0x31, 0x32, 0x40, 0x41, 0x50 };
static u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 };
@@ -168,7 +168,7 @@ struct region_code_mapping {
static struct region_code_mapping region_code_mapping_t[] = {
{ 0x10, "US " }, /* US FCC */
{ 0x20, "CA " }, /* IC Canada */
- { 0x30, "EU " }, /* ETSI */
+ { 0x30, "FR " }, /* France */
{ 0x31, "ES " }, /* Spain */
{ 0x32, "FR " }, /* France */
{ 0x40, "JP " }, /* Japan */
diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c
index 45ae38e32621..cb25aa7e90db 100644
--- a/drivers/net/wireless/mwifiex/cmdevt.c
+++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c
@@ -1637,9 +1637,9 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv,
if (adapter->region_code == region_code_index[i])
break;
- /* If it's unidentified region code, use the default (USA) */
+ /* If it's unidentified region code, use the default (world) */
if (i >= MWIFIEX_MAX_REGION_CODE) {
- adapter->region_code = 0x10;
+ adapter->region_code = 0x00;
mwifiex_dbg(adapter, WARN,
"cmd: unknown region code, use default (USA)\n");
}
diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/marvell/mwifiex/debugfs.c
index 9824d8dd2b44..0b9c580af988 100644
--- a/drivers/net/wireless/mwifiex/debugfs.c
+++ b/drivers/net/wireless/marvell/mwifiex/debugfs.c
@@ -447,20 +447,13 @@ static ssize_t
mwifiex_regrdwr_write(struct file *file,
const char __user *ubuf, size_t count, loff_t *ppos)
{
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *) addr;
- size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ char *buf;
int ret;
u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX;
- if (!buf)
- return -ENOMEM;
-
-
- if (copy_from_user(buf, ubuf, buf_size)) {
- ret = -EFAULT;
- goto done;
- }
+ buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
sscanf(buf, "%u %x %x", &reg_type, &reg_offset, &reg_value);
@@ -474,7 +467,7 @@ mwifiex_regrdwr_write(struct file *file,
ret = count;
}
done:
- free_page(addr);
+ kfree(buf);
return ret;
}
@@ -572,17 +565,11 @@ mwifiex_debug_mask_write(struct file *file, const char __user *ubuf,
int ret;
unsigned long debug_mask;
struct mwifiex_private *priv = (void *)file->private_data;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (void *)addr;
- size_t buf_size = min(count, (size_t)(PAGE_SIZE - 1));
+ char *buf;
- if (!buf)
- return -ENOMEM;
-
- if (copy_from_user(buf, ubuf, buf_size)) {
- ret = -EFAULT;
- goto done;
- }
+ buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
if (kstrtoul(buf, 0, &debug_mask)) {
ret = -EINVAL;
@@ -592,7 +579,7 @@ mwifiex_debug_mask_write(struct file *file, const char __user *ubuf,
priv->adapter->debug_mask = debug_mask;
ret = count;
done:
- free_page(addr);
+ kfree(buf);
return ret;
}
@@ -609,17 +596,11 @@ mwifiex_memrw_write(struct file *file, const char __user *ubuf, size_t count,
struct mwifiex_ds_mem_rw mem_rw;
u16 cmd_action;
struct mwifiex_private *priv = (void *)file->private_data;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (void *)addr;
- size_t buf_size = min(count, (size_t)(PAGE_SIZE - 1));
+ char *buf;
- if (!buf)
- return -ENOMEM;
-
- if (copy_from_user(buf, ubuf, buf_size)) {
- ret = -EFAULT;
- goto done;
- }
+ buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
ret = sscanf(buf, "%c %x %x", &cmd, &mem_rw.addr, &mem_rw.value);
if (ret != 3) {
@@ -645,7 +626,7 @@ mwifiex_memrw_write(struct file *file, const char __user *ubuf, size_t count,
ret = count;
done:
- free_page(addr);
+ kfree(buf);
return ret;
}
@@ -686,20 +667,13 @@ static ssize_t
mwifiex_rdeeprom_write(struct file *file,
const char __user *ubuf, size_t count, loff_t *ppos)
{
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *) addr;
- size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ char *buf;
int ret = 0;
int offset = -1, bytes = -1;
- if (!buf)
- return -ENOMEM;
-
-
- if (copy_from_user(buf, ubuf, buf_size)) {
- ret = -EFAULT;
- goto done;
- }
+ buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
sscanf(buf, "%d %d", &offset, &bytes);
@@ -712,7 +686,7 @@ mwifiex_rdeeprom_write(struct file *file,
ret = count;
}
done:
- free_page(addr);
+ kfree(buf);
return ret;
}
@@ -771,21 +745,15 @@ mwifiex_hscfg_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct mwifiex_private *priv = (void *)file->private_data;
- unsigned long addr = get_zeroed_page(GFP_KERNEL);
- char *buf = (char *)addr;
- size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ char *buf;
int ret, arg_num;
struct mwifiex_ds_hs_cfg hscfg;
int conditions = HS_CFG_COND_DEF;
u32 gpio = HS_CFG_GPIO_DEF, gap = HS_CFG_GAP_DEF;
- if (!buf)
- return -ENOMEM;
-
- if (copy_from_user(buf, ubuf, buf_size)) {
- ret = -EFAULT;
- goto done;
- }
+ buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
arg_num = sscanf(buf, "%d %x %x", &conditions, &gpio, &gap);
@@ -823,7 +791,7 @@ mwifiex_hscfg_write(struct file *file, const char __user *ubuf,
priv->adapter->hs_enabling = false;
ret = count;
done:
- free_page(addr);
+ kfree(buf);
return ret;
}
@@ -906,6 +874,34 @@ mwifiex_timeshare_coex_write(struct file *file, const char __user *ubuf,
return count;
}
+static ssize_t
+mwifiex_reset_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct mwifiex_private *priv = file->private_data;
+ struct mwifiex_adapter *adapter = priv->adapter;
+ char cmd;
+ bool result;
+
+ if (copy_from_user(&cmd, ubuf, sizeof(cmd)))
+ return -EFAULT;
+
+ if (strtobool(&cmd, &result))
+ return -EINVAL;
+
+ if (!result)
+ return -EINVAL;
+
+ if (adapter->if_ops.card_reset) {
+ dev_info(adapter->dev, "Resetting per request\n");
+ adapter->hw_status = MWIFIEX_HW_STATUS_RESET;
+ mwifiex_cancel_all_pending_cmd(adapter);
+ adapter->if_ops.card_reset(adapter);
+ }
+
+ return count;
+}
+
#define MWIFIEX_DFS_ADD_FILE(name) do { \
if (!debugfs_create_file(#name, 0644, priv->dfs_dev_dir, \
priv, &mwifiex_dfs_##name##_fops)) \
@@ -943,6 +939,7 @@ MWIFIEX_DFS_FILE_OPS(hscfg);
MWIFIEX_DFS_FILE_OPS(histogram);
MWIFIEX_DFS_FILE_OPS(debug_mask);
MWIFIEX_DFS_FILE_OPS(timeshare_coex);
+MWIFIEX_DFS_FILE_WRITE_OPS(reset);
/*
* This function creates the debug FS directory structure and the files.
@@ -970,6 +967,7 @@ mwifiex_dev_debugfs_init(struct mwifiex_private *priv)
MWIFIEX_DFS_ADD_FILE(histogram);
MWIFIEX_DFS_ADD_FILE(debug_mask);
MWIFIEX_DFS_ADD_FILE(timeshare_coex);
+ MWIFIEX_DFS_ADD_FILE(reset);
}
/*
diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/marvell/mwifiex/decl.h
index 098e1f14dc9a..d9c15cd36f12 100644
--- a/drivers/net/wireless/mwifiex/decl.h
+++ b/drivers/net/wireless/marvell/mwifiex/decl.h
@@ -111,9 +111,9 @@
/* Rate index for OFDM 0 */
#define MWIFIEX_RATE_INDEX_OFDM0 4
-#define MWIFIEX_MAX_STA_NUM 1
-#define MWIFIEX_MAX_UAP_NUM 1
-#define MWIFIEX_MAX_P2P_NUM 1
+#define MWIFIEX_MAX_STA_NUM 3
+#define MWIFIEX_MAX_UAP_NUM 3
+#define MWIFIEX_MAX_P2P_NUM 3
#define MWIFIEX_A_BAND_START_FREQ 5000
diff --git a/drivers/net/wireless/mwifiex/ethtool.c b/drivers/net/wireless/marvell/mwifiex/ethtool.c
index 58400c69ab26..58400c69ab26 100644
--- a/drivers/net/wireless/mwifiex/ethtool.c
+++ b/drivers/net/wireless/marvell/mwifiex/ethtool.c
diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h
index 1e1e81a0a8d4..ced7af2be29a 100644
--- a/drivers/net/wireless/mwifiex/fw.h
+++ b/drivers/net/wireless/marvell/mwifiex/fw.h
@@ -537,7 +537,7 @@ enum P2P_MODES {
#define EVENT_GET_BSS_TYPE(event_cause) \
(((event_cause) >> 24) & 0x00ff)
-#define MWIFIEX_MAX_PATTERN_LEN 20
+#define MWIFIEX_MAX_PATTERN_LEN 40
#define MWIFIEX_MAX_OFFSET_LEN 100
#define STACK_NBYTES 100
#define TYPE_DNUM 1
@@ -1092,9 +1092,15 @@ struct host_cmd_ds_802_11_ad_hoc_start {
u8 data_rate[HOSTCMD_SUPPORTED_RATES];
} __packed;
-struct host_cmd_ds_802_11_ad_hoc_result {
+struct host_cmd_ds_802_11_ad_hoc_start_result {
u8 pad[3];
u8 bssid[ETH_ALEN];
+ u8 pad2[2];
+ u8 result;
+} __packed;
+
+struct host_cmd_ds_802_11_ad_hoc_join_result {
+ u8 result;
} __packed;
struct adhoc_bss_desc {
@@ -2124,7 +2130,8 @@ struct host_cmd_ds_command {
struct host_cmd_ds_802_11_associate_rsp associate_rsp;
struct host_cmd_ds_802_11_deauthenticate deauth;
struct host_cmd_ds_802_11_ad_hoc_start adhoc_start;
- struct host_cmd_ds_802_11_ad_hoc_result adhoc_result;
+ struct host_cmd_ds_802_11_ad_hoc_start_result start_result;
+ struct host_cmd_ds_802_11_ad_hoc_join_result join_result;
struct host_cmd_ds_802_11_ad_hoc_join adhoc_join;
struct host_cmd_ds_802_11d_domain_info domain_info;
struct host_cmd_ds_802_11d_domain_info_rsp domain_info_resp;
diff --git a/drivers/net/wireless/mwifiex/ie.c b/drivers/net/wireless/marvell/mwifiex/ie.c
index abf52d25b981..c488c3068abc 100644
--- a/drivers/net/wireless/mwifiex/ie.c
+++ b/drivers/net/wireless/marvell/mwifiex/ie.c
@@ -140,7 +140,7 @@ mwifiex_update_autoindex_ies(struct mwifiex_private *priv,
if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG,
HostCmd_ACT_GEN_SET,
- UAP_CUSTOM_IE_I, ie_list, false);
+ UAP_CUSTOM_IE_I, ie_list, true);
return 0;
}
diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/marvell/mwifiex/init.c
index de74a7773fb6..6f7876ec31b7 100644
--- a/drivers/net/wireless/mwifiex/init.c
+++ b/drivers/net/wireless/marvell/mwifiex/init.c
@@ -95,7 +95,7 @@ int mwifiex_init_priv(struct mwifiex_private *priv)
priv->curr_pkt_filter = HostCmd_ACT_MAC_RX_ON | HostCmd_ACT_MAC_TX_ON |
HostCmd_ACT_MAC_ETHERNETII_ENABLE;
- priv->beacon_period = 100; /* beacon interval */ ;
+ priv->beacon_period = 100; /* beacon interval */
priv->attempted_bss_desc = NULL;
memset(&priv->curr_bss_params, 0, sizeof(priv->curr_bss_params));
priv->listen_interval = MWIFIEX_DEFAULT_LISTEN_INTERVAL;
diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/marvell/mwifiex/ioctl.h
index 4f0174c64946..4f0174c64946 100644
--- a/drivers/net/wireless/mwifiex/ioctl.h
+++ b/drivers/net/wireless/marvell/mwifiex/ioctl.h
diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/marvell/mwifiex/join.c
index 3cda1f956f0b..cc09a81dbf6a 100644
--- a/drivers/net/wireless/mwifiex/join.c
+++ b/drivers/net/wireless/marvell/mwifiex/join.c
@@ -1247,20 +1247,26 @@ int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv,
{
int ret = 0;
struct mwifiex_adapter *adapter = priv->adapter;
- struct host_cmd_ds_802_11_ad_hoc_result *adhoc_result;
+ struct host_cmd_ds_802_11_ad_hoc_start_result *start_result =
+ &resp->params.start_result;
+ struct host_cmd_ds_802_11_ad_hoc_join_result *join_result =
+ &resp->params.join_result;
struct mwifiex_bssdescriptor *bss_desc;
- u16 reason_code;
+ u16 cmd = le16_to_cpu(resp->command);
+ u8 result;
- adhoc_result = &resp->params.adhoc_result;
+ if (cmd == HostCmd_CMD_802_11_AD_HOC_START)
+ result = start_result->result;
+ else
+ result = join_result->result;
bss_desc = priv->attempted_bss_desc;
/* Join result code 0 --> SUCCESS */
- reason_code = le16_to_cpu(resp->result);
- if (reason_code) {
+ if (result) {
mwifiex_dbg(priv->adapter, ERROR, "ADHOC_RESP: failed\n");
if (priv->media_connected)
- mwifiex_reset_connect_state(priv, reason_code);
+ mwifiex_reset_connect_state(priv, result);
memset(&priv->curr_bss_params.bss_descriptor,
0x00, sizeof(struct mwifiex_bssdescriptor));
@@ -1278,7 +1284,7 @@ int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv,
/* Update the created network descriptor with the new BSSID */
memcpy(bss_desc->mac_address,
- adhoc_result->bssid, ETH_ALEN);
+ start_result->bssid, ETH_ALEN);
priv->adhoc_state = ADHOC_STARTED;
} else {
diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c
index 969ca1e1f3e9..79c16de8743e 100644
--- a/drivers/net/wireless/mwifiex/main.c
+++ b/drivers/net/wireless/marvell/mwifiex/main.c
@@ -763,7 +763,7 @@ mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
spin_lock_irqsave(&priv->ack_status_lock, flags);
id = idr_alloc(&priv->ack_status_frames, orig_skb,
- 1, 0xff, GFP_ATOMIC);
+ 1, 0x10, GFP_ATOMIC);
spin_unlock_irqrestore(&priv->ack_status_lock, flags);
if (id >= 0) {
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h
index 3959f1c97f4e..2f7f478ce04b 100644
--- a/drivers/net/wireless/mwifiex/main.h
+++ b/drivers/net/wireless/marvell/mwifiex/main.h
@@ -84,7 +84,7 @@ enum {
#define MWIFIEX_KEY_BUFFER_SIZE 16
#define MWIFIEX_DEFAULT_LISTEN_INTERVAL 10
-#define MWIFIEX_MAX_REGION_CODE 7
+#define MWIFIEX_MAX_REGION_CODE 9
#define DEFAULT_BCN_AVG_FACTOR 8
#define DEFAULT_DATA_AVG_FACTOR 8
@@ -564,14 +564,14 @@ struct mwifiex_private {
struct mwifiex_wep_key wep_key[NUM_WEP_KEYS];
u16 wep_key_curr_index;
u8 wpa_ie[256];
- u8 wpa_ie_len;
+ u16 wpa_ie_len;
u8 wpa_is_gtk_set;
struct host_cmd_ds_802_11_key_material aes_key;
struct host_cmd_ds_802_11_key_material_v2 aes_key_v2;
u8 wapi_ie[256];
- u8 wapi_ie_len;
+ u16 wapi_ie_len;
u8 *wps_ie;
- u8 wps_ie_len;
+ u16 wps_ie_len;
u8 wmm_required;
u8 wmm_enabled;
u8 wmm_qosinfo;
@@ -1273,20 +1273,46 @@ mwifiex_get_priv(struct mwifiex_adapter *adapter,
}
/*
+ * This function checks available bss_num when adding new interface or
+ * changing interface type.
+ */
+static inline u8
+mwifiex_get_unused_bss_num(struct mwifiex_adapter *adapter, u8 bss_type)
+{
+ u8 i, j;
+ int index[MWIFIEX_MAX_BSS_NUM];
+
+ memset(index, 0, sizeof(index));
+ for (i = 0; i < adapter->priv_num; i++)
+ if (adapter->priv[i]) {
+ if (adapter->priv[i]->bss_type == bss_type &&
+ !(adapter->priv[i]->bss_mode ==
+ NL80211_IFTYPE_UNSPECIFIED)) {
+ index[adapter->priv[i]->bss_num] = 1;
+ }
+ }
+ for (j = 0; j < MWIFIEX_MAX_BSS_NUM; j++)
+ if (!index[j])
+ return j;
+ return -1;
+}
+
+/*
* This function returns the first available unused private structure pointer.
*/
static inline struct mwifiex_private *
-mwifiex_get_unused_priv(struct mwifiex_adapter *adapter)
+mwifiex_get_unused_priv_by_bss_type(struct mwifiex_adapter *adapter,
+ u8 bss_type)
{
- int i;
+ u8 i;
- for (i = 0; i < adapter->priv_num; i++) {
- if (adapter->priv[i]) {
- if (adapter->priv[i]->bss_mode ==
- NL80211_IFTYPE_UNSPECIFIED)
- break;
+ for (i = 0; i < adapter->priv_num; i++)
+ if (adapter->priv[i]->bss_mode ==
+ NL80211_IFTYPE_UNSPECIFIED) {
+ adapter->priv[i]->bss_num =
+ mwifiex_get_unused_bss_num(adapter, bss_type);
+ break;
}
- }
return ((i < adapter->priv_num) ? adapter->priv[i] : NULL);
}
diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c
index 21192b6f9c64..6d0dc40e20e5 100644
--- a/drivers/net/wireless/mwifiex/pcie.c
+++ b/drivers/net/wireless/marvell/mwifiex/pcie.c
@@ -2129,14 +2129,14 @@ static irqreturn_t mwifiex_pcie_interrupt(int irq, void *context)
struct mwifiex_adapter *adapter;
if (!pdev) {
- pr_debug("info: %s: pdev is NULL\n", (u8 *)pdev);
+ pr_err("info: %s: pdev is NULL\n", __func__);
goto exit;
}
card = pci_get_drvdata(pdev);
if (!card || !card->adapter) {
- pr_debug("info: %s: card=%p adapter=%p\n", __func__, card,
- card ? card->adapter : NULL);
+ pr_err("info: %s: card=%p adapter=%p\n", __func__, card,
+ card ? card->adapter : NULL);
goto exit;
}
adapter = card->adapter;
@@ -2473,50 +2473,44 @@ static int mwifiex_pcie_init(struct mwifiex_adapter *adapter)
pci_set_master(pdev);
- mwifiex_dbg(adapter, INFO,
- "try set_consistent_dma_mask(32)\n");
+ pr_notice("try set_consistent_dma_mask(32)\n");
ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "set_dma_mask(32) failed\n");
+ pr_err("set_dma_mask(32) failed\n");
goto err_set_dma_mask;
}
ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "set_consistent_dma_mask(64) failed\n");
+ pr_err("set_consistent_dma_mask(64) failed\n");
goto err_set_dma_mask;
}
ret = pci_request_region(pdev, 0, DRV_NAME);
if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "req_reg(0) error\n");
+ pr_err("req_reg(0) error\n");
goto err_req_region0;
}
card->pci_mmap = pci_iomap(pdev, 0, 0);
if (!card->pci_mmap) {
- mwifiex_dbg(adapter, ERROR, "iomap(0) error\n");
+ pr_err("iomap(0) error\n");
ret = -EIO;
goto err_iomap0;
}
ret = pci_request_region(pdev, 2, DRV_NAME);
if (ret) {
- mwifiex_dbg(adapter, ERROR, "req_reg(2) error\n");
+ pr_err("req_reg(2) error\n");
goto err_req_region2;
}
card->pci_mmap1 = pci_iomap(pdev, 2, 0);
if (!card->pci_mmap1) {
- mwifiex_dbg(adapter, ERROR,
- "iomap(2) error\n");
+ pr_err("iomap(2) error\n");
ret = -EIO;
goto err_iomap2;
}
- mwifiex_dbg(adapter, INFO,
- "PCI memory map Virt0: %p PCI memory map Virt2: %p\n",
- card->pci_mmap, card->pci_mmap1);
+ pr_notice("PCI memory map Virt0: %p PCI memory map Virt2: %p\n",
+ card->pci_mmap, card->pci_mmap1);
card->cmdrsp_buf = NULL;
ret = mwifiex_pcie_create_txbd_ring(adapter);
@@ -2599,6 +2593,30 @@ static void mwifiex_pcie_cleanup(struct mwifiex_adapter *adapter)
kfree(card);
}
+static int mwifiex_pcie_request_irq(struct mwifiex_adapter *adapter)
+{
+ int ret;
+ struct pcie_service_card *card = adapter->card;
+ struct pci_dev *pdev = card->dev;
+
+ if (pci_enable_msi(pdev) != 0)
+ pci_disable_msi(pdev);
+ else
+ card->msi_enable = 1;
+
+ mwifiex_dbg(adapter, INFO, "msi_enable = %d\n", card->msi_enable);
+
+ ret = request_irq(pdev->irq, mwifiex_pcie_interrupt, IRQF_SHARED,
+ "MRVL_PCIE", pdev);
+ if (ret) {
+ pr_err("request_irq failed: ret=%d\n", ret);
+ adapter->card = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
/*
* This function registers the PCIE device.
*
@@ -2606,23 +2624,16 @@ static void mwifiex_pcie_cleanup(struct mwifiex_adapter *adapter)
*/
static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
{
- int ret;
struct pcie_service_card *card = adapter->card;
struct pci_dev *pdev = card->dev;
/* save adapter pointer in card */
card->adapter = adapter;
+ adapter->dev = &pdev->dev;
- ret = request_irq(pdev->irq, mwifiex_pcie_interrupt, IRQF_SHARED,
- "MRVL_PCIE", pdev);
- if (ret) {
- mwifiex_dbg(adapter, ERROR,
- "request_irq failed: ret=%d\n", ret);
- adapter->card = NULL;
+ if (mwifiex_pcie_request_irq(adapter))
return -1;
- }
- adapter->dev = &pdev->dev;
adapter->tx_buf_size = card->pcie.tx_buf_size;
adapter->mem_type_mapping_tbl = mem_type_mapping_tbl;
adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl);
diff --git a/drivers/net/wireless/mwifiex/pcie.h b/drivers/net/wireless/marvell/mwifiex/pcie.h
index 48e549c3b285..6fc28737b576 100644
--- a/drivers/net/wireless/mwifiex/pcie.h
+++ b/drivers/net/wireless/marvell/mwifiex/pcie.h
@@ -210,17 +210,17 @@ static const struct mwifiex_pcie_card_reg mwifiex_reg_8997 = {
.cmdrsp_addr_lo = PCIE_SCRATCH_4_REG,
.cmdrsp_addr_hi = PCIE_SCRATCH_5_REG,
.tx_rdptr = 0xC1A4,
- .tx_wrptr = 0xC1A8,
- .rx_rdptr = 0xC1A8,
+ .tx_wrptr = 0xC174,
+ .rx_rdptr = 0xC174,
.rx_wrptr = 0xC1A4,
.evt_rdptr = PCIE_SCRATCH_10_REG,
.evt_wrptr = PCIE_SCRATCH_11_REG,
.drv_rdy = PCIE_SCRATCH_12_REG,
.tx_start_ptr = 16,
.tx_mask = 0x0FFF0000,
- .tx_wrap_mask = 0x01FF0000,
+ .tx_wrap_mask = 0x1FFF0000,
.rx_mask = 0x00000FFF,
- .rx_wrap_mask = 0x000001FF,
+ .rx_wrap_mask = 0x00001FFF,
.tx_rollover_ind = BIT(28),
.rx_rollover_ind = BIT(12),
.evt_rollover_ind = MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND,
@@ -326,6 +326,7 @@ struct pcie_service_card {
dma_addr_t sleep_cookie_pbase;
void __iomem *pci_mmap;
void __iomem *pci_mmap1;
+ int msi_enable;
};
static inline int
@@ -342,6 +343,7 @@ mwifiex_pcie_txbd_empty(struct pcie_service_card *card, u32 rdptr)
return 1;
break;
case PCIE_DEVICE_ID_MARVELL_88W8897:
+ case PCIE_DEVICE_ID_MARVELL_88W8997:
if (((card->txbd_wrptr & reg->tx_mask) ==
(rdptr & reg->tx_mask)) &&
((card->txbd_wrptr & reg->tx_rollover_ind) ==
diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c
index c20017ced566..c20017ced566 100644
--- a/drivers/net/wireless/mwifiex/scan.c
+++ b/drivers/net/wireless/marvell/mwifiex/scan.c
diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c
index 78a8474e1a3d..4c8cae682c89 100644
--- a/drivers/net/wireless/mwifiex/sdio.c
+++ b/drivers/net/wireless/marvell/mwifiex/sdio.c
@@ -796,8 +796,8 @@ mwifiex_sdio_interrupt(struct sdio_func *func)
card = sdio_get_drvdata(func);
if (!card || !card->adapter) {
- pr_debug("int: func=%p card=%p adapter=%p\n",
- func, card, card ? card->adapter : NULL);
+ pr_err("int: func=%p card=%p adapter=%p\n",
+ func, card, card ? card->adapter : NULL);
return;
}
adapter = card->adapter;
@@ -2053,8 +2053,19 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter)
/* Allocate skb pointer buffers */
card->mpa_rx.skb_arr = kzalloc((sizeof(void *)) *
card->mp_agg_pkt_limit, GFP_KERNEL);
+ if (!card->mpa_rx.skb_arr) {
+ kfree(card->mp_regs);
+ return -ENOMEM;
+ }
+
card->mpa_rx.len_arr = kzalloc(sizeof(*card->mpa_rx.len_arr) *
card->mp_agg_pkt_limit, GFP_KERNEL);
+ if (!card->mpa_rx.len_arr) {
+ kfree(card->mp_regs);
+ kfree(card->mpa_rx.skb_arr);
+ return -ENOMEM;
+ }
+
ret = mwifiex_alloc_sdio_mpa_buffers(adapter,
card->mp_tx_agg_buf_size,
card->mp_rx_agg_buf_size);
diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/marvell/mwifiex/sdio.h
index b9fbc5cf6262..b9fbc5cf6262 100644
--- a/drivers/net/wireless/mwifiex/sdio.h
+++ b/drivers/net/wireless/marvell/mwifiex/sdio.h
diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c
index e486867a4c67..e486867a4c67 100644
--- a/drivers/net/wireless/mwifiex/sta_cmd.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c
diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c
index 9ac7aa2431b4..9ac7aa2431b4 100644
--- a/drivers/net/wireless/mwifiex/sta_cmdresp.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c
diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/marvell/mwifiex/sta_event.c
index ff3ee9dfbbd5..ff3ee9dfbbd5 100644
--- a/drivers/net/wireless/mwifiex/sta_event.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_event.c
diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
index a6c8a4f7bfe9..6a4fc5d183cf 100644
--- a/drivers/net/wireless/mwifiex/sta_ioctl.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
@@ -272,7 +272,8 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
priv->scan_block = false;
if (bss) {
- mwifiex_process_country_ie(priv, bss);
+ if (adapter->region_code == 0x00)
+ mwifiex_process_country_ie(priv, bss);
/* Allocate and fill new bss descriptor */
bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor),
@@ -758,7 +759,7 @@ static int mwifiex_set_wpa_ie_helper(struct mwifiex_private *priv,
return -1;
}
memcpy(priv->wpa_ie, ie_data_ptr, ie_len);
- priv->wpa_ie_len = (u8) ie_len;
+ priv->wpa_ie_len = ie_len;
mwifiex_dbg(priv->adapter, CMD,
"cmd: Set Wpa_ie_len=%d IE=%#x\n",
priv->wpa_ie_len, priv->wpa_ie[0]);
@@ -923,9 +924,8 @@ static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv,
if (encrypt_key->key_disable) {
memset(&priv->wep_key[index], 0,
sizeof(struct mwifiex_wep_key));
- if (wep_key->key_length)
- goto done;
- }
+ goto done;
+ }
if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2)
enc_key = encrypt_key;
@@ -1293,6 +1293,8 @@ mwifiex_set_gen_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr,
struct ieee_types_vendor_header *pvendor_ie;
const u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 };
const u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 };
+ u16 unparsed_len = ie_len;
+ int find_wpa_ie = 0;
/* If the passed length is zero, reset the buffer */
if (!ie_len) {
@@ -1304,40 +1306,69 @@ mwifiex_set_gen_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr,
return -1;
}
pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr;
- /* Test to see if it is a WPA IE, if not, then it is a gen IE */
- if (((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) &&
- (!memcmp(pvendor_ie->oui, wpa_oui, sizeof(wpa_oui)))) ||
- (pvendor_ie->element_id == WLAN_EID_RSN)) {
- /* IE is a WPA/WPA2 IE so call set_wpa function */
- ret = mwifiex_set_wpa_ie_helper(priv, ie_data_ptr, ie_len);
- priv->wps.session_enable = false;
+ while (pvendor_ie) {
+ if (pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) {
+ /* Test to see if it is a WPA IE, if not, then it is a
+ * gen IE
+ */
+ if (!memcmp(pvendor_ie->oui, wpa_oui,
+ sizeof(wpa_oui))) {
+ find_wpa_ie = 1;
+ break;
+ }
- return ret;
- } else if (pvendor_ie->element_id == WLAN_EID_BSS_AC_ACCESS_DELAY) {
+ /* Test to see if it is a WPS IE, if so, enable
+ * wps session flag
+ */
+ if (!memcmp(pvendor_ie->oui, wps_oui,
+ sizeof(wps_oui))) {
+ priv->wps.session_enable = true;
+ mwifiex_dbg(priv->adapter, MSG,
+ "info: WPS Session Enabled.\n");
+ ret = mwifiex_set_wps_ie(priv,
+ (u8 *)pvendor_ie,
+ unparsed_len);
+ }
+ }
+
+ if (pvendor_ie->element_id == WLAN_EID_RSN) {
+ find_wpa_ie = 1;
+ break;
+ }
+
+ if (pvendor_ie->element_id == WLAN_EID_BSS_AC_ACCESS_DELAY) {
/* IE is a WAPI IE so call set_wapi function */
- ret = mwifiex_set_wapi_ie(priv, ie_data_ptr, ie_len);
+ ret = mwifiex_set_wapi_ie(priv, (u8 *)pvendor_ie,
+ unparsed_len);
+ return ret;
+ }
+ unparsed_len -= (pvendor_ie->len +
+ sizeof(struct ieee_types_header));
+
+ if (unparsed_len <= sizeof(struct ieee_types_header))
+ pvendor_ie = NULL;
+ else
+ pvendor_ie = (struct ieee_types_vendor_header *)
+ (((u8 *)pvendor_ie) + pvendor_ie->len +
+ sizeof(struct ieee_types_header));
+ }
+
+ if (find_wpa_ie) {
+ /* IE is a WPA/WPA2 IE so call set_wpa function */
+ ret = mwifiex_set_wpa_ie_helper(priv, (u8 *)pvendor_ie,
+ unparsed_len);
+ priv->wps.session_enable = false;
return ret;
}
+
/*
* Verify that the passed length is not larger than the
* available space remaining in the buffer
*/
if (ie_len < (sizeof(priv->gen_ie_buf) - priv->gen_ie_buf_len)) {
- /* Test to see if it is a WPS IE, if so, enable
- * wps session flag
- */
- pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr;
- if ((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) &&
- (!memcmp(pvendor_ie->oui, wps_oui, sizeof(wps_oui)))) {
- priv->wps.session_enable = true;
- mwifiex_dbg(priv->adapter, INFO,
- "info: WPS Session Enabled.\n");
- ret = mwifiex_set_wps_ie(priv, ie_data_ptr, ie_len);
- }
-
/* Append the passed data to the end of the
genIeBuffer */
memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len, ie_data_ptr,
diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/marvell/mwifiex/sta_rx.c
index d4d4cb1ce95b..00fcbda09349 100644
--- a/drivers/net/wireless/mwifiex/sta_rx.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_rx.c
@@ -215,7 +215,7 @@ int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv,
if (rx_pkt_type == PKT_TYPE_MGMT) {
ret = mwifiex_process_mgmt_packet(priv, skb);
if (ret)
- mwifiex_dbg(adapter, ERROR, "Rx of mgmt packet failed");
+ mwifiex_dbg(adapter, DATA, "Rx of mgmt packet failed");
dev_kfree_skb_any(skb);
return ret;
}
diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/marvell/mwifiex/sta_tx.c
index f6683ea6bd5d..f6683ea6bd5d 100644
--- a/drivers/net/wireless/mwifiex/sta_tx.c
+++ b/drivers/net/wireless/marvell/mwifiex/sta_tx.c
diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c
index 9275f9c3f869..9275f9c3f869 100644
--- a/drivers/net/wireless/mwifiex/tdls.c
+++ b/drivers/net/wireless/marvell/mwifiex/tdls.c
diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/marvell/mwifiex/txrx.c
index bf6182b646a5..bf6182b646a5 100644
--- a/drivers/net/wireless/mwifiex/txrx.c
+++ b/drivers/net/wireless/marvell/mwifiex/txrx.c
diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c
index 759a6ada5b0f..e791166d90c4 100644
--- a/drivers/net/wireless/mwifiex/uap_cmd.c
+++ b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c
@@ -848,9 +848,9 @@ int mwifiex_config_start_uap(struct mwifiex_private *priv,
if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG,
HostCmd_ACT_GEN_SET,
- UAP_BSS_PARAMS_I, bss_cfg, false)) {
+ UAP_BSS_PARAMS_I, bss_cfg, true)) {
mwifiex_dbg(priv->adapter, ERROR,
- "Failed to set the SSID\n");
+ "Failed to set AP configuration\n");
return -1;
}
@@ -865,7 +865,7 @@ int mwifiex_config_start_uap(struct mwifiex_private *priv,
}
if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START,
- HostCmd_ACT_GEN_SET, 0, NULL, false)) {
+ HostCmd_ACT_GEN_SET, 0, NULL, true)) {
mwifiex_dbg(priv->adapter, ERROR,
"Failed to start the BSS\n");
return -1;
diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/marvell/mwifiex/uap_event.c
index 86ff54296f39..86ff54296f39 100644
--- a/drivers/net/wireless/mwifiex/uap_event.c
+++ b/drivers/net/wireless/marvell/mwifiex/uap_event.c
diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/marvell/mwifiex/uap_txrx.c
index 74d5d7238633..52f7981a8afc 100644
--- a/drivers/net/wireless/mwifiex/uap_txrx.c
+++ b/drivers/net/wireless/marvell/mwifiex/uap_txrx.c
@@ -310,8 +310,7 @@ int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv,
if (rx_pkt_type == PKT_TYPE_MGMT) {
ret = mwifiex_process_mgmt_packet(priv, skb);
if (ret)
- mwifiex_dbg(adapter, ERROR,
- "Rx of mgmt packet failed");
+ mwifiex_dbg(adapter, DATA, "Rx of mgmt packet failed");
dev_kfree_skb_any(skb);
return ret;
}
diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c
index e43aff932360..e43aff932360 100644
--- a/drivers/net/wireless/mwifiex/usb.c
+++ b/drivers/net/wireless/marvell/mwifiex/usb.c
diff --git a/drivers/net/wireless/mwifiex/usb.h b/drivers/net/wireless/marvell/mwifiex/usb.h
index b4e9246bbcdc..b4e9246bbcdc 100644
--- a/drivers/net/wireless/mwifiex/usb.h
+++ b/drivers/net/wireless/marvell/mwifiex/usb.h
diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/marvell/mwifiex/util.c
index 0cec8a64473e..0cec8a64473e 100644
--- a/drivers/net/wireless/mwifiex/util.c
+++ b/drivers/net/wireless/marvell/mwifiex/util.c
diff --git a/drivers/net/wireless/mwifiex/util.h b/drivers/net/wireless/marvell/mwifiex/util.h
index b541d66c01eb..b541d66c01eb 100644
--- a/drivers/net/wireless/mwifiex/util.h
+++ b/drivers/net/wireless/marvell/mwifiex/util.h
diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/marvell/mwifiex/wmm.c
index acccd6734e3b..acccd6734e3b 100644
--- a/drivers/net/wireless/mwifiex/wmm.c
+++ b/drivers/net/wireless/marvell/mwifiex/wmm.c
diff --git a/drivers/net/wireless/mwifiex/wmm.h b/drivers/net/wireless/marvell/mwifiex/wmm.h
index 38f09762bd2f..38f09762bd2f 100644
--- a/drivers/net/wireless/mwifiex/wmm.h
+++ b/drivers/net/wireless/marvell/mwifiex/wmm.h
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/marvell/mwl8k.c
index 30e3aaae32e2..30e3aaae32e2 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/marvell/mwl8k.c
diff --git a/drivers/net/wireless/mediatek/Kconfig b/drivers/net/wireless/mediatek/Kconfig
index cba300c6b5da..28843fed750a 100644
--- a/drivers/net/wireless/mediatek/Kconfig
+++ b/drivers/net/wireless/mediatek/Kconfig
@@ -1,10 +1,14 @@
-menuconfig WL_MEDIATEK
- bool "Mediatek Wireless LAN support"
+config WLAN_VENDOR_MEDIATEK
+ bool "MediaTek devices"
+ default y
---help---
- Enable community drivers for MediaTek WiFi devices.
- Those drivers make use of the Linux mac80211 stack.
+ If you have a wireless card belonging to this class, say Y.
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about cards. If you say Y, you will be asked for
+ your specific card in the following questions.
-if WL_MEDIATEK
+if WLAN_VENDOR_MEDIATEK
source "drivers/net/wireless/mediatek/mt7601u/Kconfig"
-endif # WL_MEDIATEK
+endif # WLAN_VENDOR_MEDIATEK
diff --git a/drivers/net/wireless/ralink/Kconfig b/drivers/net/wireless/ralink/Kconfig
new file mode 100644
index 000000000000..41dbf3130e2b
--- /dev/null
+++ b/drivers/net/wireless/ralink/Kconfig
@@ -0,0 +1,16 @@
+config WLAN_VENDOR_RALINK
+ bool "Ralink devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_RALINK
+
+source "drivers/net/wireless/ralink/rt2x00/Kconfig"
+
+endif # WLAN_VENDOR_RALINK
diff --git a/drivers/net/wireless/ralink/Makefile b/drivers/net/wireless/ralink/Makefile
new file mode 100644
index 000000000000..f84c0a2e4f4d
--- /dev/null
+++ b/drivers/net/wireless/ralink/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_RT2X00) += rt2x00/
diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/ralink/rt2x00/Kconfig
index de62f5dcb62f..de62f5dcb62f 100644
--- a/drivers/net/wireless/rt2x00/Kconfig
+++ b/drivers/net/wireless/ralink/rt2x00/Kconfig
diff --git a/drivers/net/wireless/rt2x00/Makefile b/drivers/net/wireless/ralink/rt2x00/Makefile
index 24a66015a495..24a66015a495 100644
--- a/drivers/net/wireless/rt2x00/Makefile
+++ b/drivers/net/wireless/ralink/rt2x00/Makefile
diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c
index 9a3966cd6fbe..9a3966cd6fbe 100644
--- a/drivers/net/wireless/rt2x00/rt2400pci.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2400pci.c
diff --git a/drivers/net/wireless/rt2x00/rt2400pci.h b/drivers/net/wireless/ralink/rt2x00/rt2400pci.h
index 0fd3a9d01a60..0fd3a9d01a60 100644
--- a/drivers/net/wireless/rt2x00/rt2400pci.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2400pci.h
diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c
index 1a6740b4d396..1a6740b4d396 100644
--- a/drivers/net/wireless/rt2x00/rt2500pci.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2500pci.c
diff --git a/drivers/net/wireless/rt2x00/rt2500pci.h b/drivers/net/wireless/ralink/rt2x00/rt2500pci.h
index 573e87bcc553..573e87bcc553 100644
--- a/drivers/net/wireless/rt2x00/rt2500pci.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2500pci.h
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c
index b50d873145d5..d26018f30b7d 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2500usb.c
@@ -229,7 +229,10 @@ static void _rt2500usb_register_read(struct rt2x00_dev *rt2x00dev,
const unsigned int offset,
u32 *value)
{
- rt2500usb_register_read(rt2x00dev, offset, (u16 *)value);
+ u16 tmp;
+
+ rt2500usb_register_read(rt2x00dev, offset, &tmp);
+ *value = tmp;
}
static void _rt2500usb_register_write(struct rt2x00_dev *rt2x00dev,
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.h b/drivers/net/wireless/ralink/rt2x00/rt2500usb.h
index 78cc035b2d17..78cc035b2d17 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2500usb.h
diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/ralink/rt2x00/rt2800.h
index 95c1d7c0a2f3..95c1d7c0a2f3 100644
--- a/drivers/net/wireless/rt2x00/rt2800.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
index 9733b31a780d..9733b31a780d 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
index 440790b92b19..440790b92b19 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
diff --git a/drivers/net/wireless/rt2x00/rt2800mmio.c b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c
index de4790b41be7..de4790b41be7 100644
--- a/drivers/net/wireless/rt2x00/rt2800mmio.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c
diff --git a/drivers/net/wireless/rt2x00/rt2800mmio.h b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.h
index b63312ce3f27..b63312ce3f27 100644
--- a/drivers/net/wireless/rt2x00/rt2800mmio.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.h
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
index 0af22573a2eb..0af22573a2eb 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.h b/drivers/net/wireless/ralink/rt2x00/rt2800pci.h
index 9dfef4607d6b..9dfef4607d6b 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.h
diff --git a/drivers/net/wireless/rt2x00/rt2800soc.c b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
index a985a5a7945e..a985a5a7945e 100644
--- a/drivers/net/wireless/rt2x00/rt2800soc.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
index bf9afbf46c1b..bf9afbf46c1b 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.h b/drivers/net/wireless/ralink/rt2x00/rt2800usb.h
index ea7cac095997..ea7cac095997 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
index 3282ddb766f4..3282ddb766f4 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/ralink/rt2x00/rt2x00config.c
index 7e8bb1198ae9..7e8bb1198ae9 100644
--- a/drivers/net/wireless/rt2x00/rt2x00config.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00config.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00crypto.c b/drivers/net/wireless/ralink/rt2x00/rt2x00crypto.c
index a2fd05ba25ca..a2fd05ba25ca 100644
--- a/drivers/net/wireless/rt2x00/rt2x00crypto.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00crypto.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c
index 90fdb02b55e7..90fdb02b55e7 100644
--- a/drivers/net/wireless/rt2x00/rt2x00debug.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.h b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.h
index e65712c235bd..e65712c235bd 100644
--- a/drivers/net/wireless/rt2x00/rt2x00debug.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
index 5639ed816813..5639ed816813 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00dump.h b/drivers/net/wireless/ralink/rt2x00/rt2x00dump.h
index 4c0e01b5d515..4c0e01b5d515 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dump.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dump.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00firmware.c b/drivers/net/wireless/ralink/rt2x00/rt2x00firmware.c
index 5813300f68a2..5813300f68a2 100644
--- a/drivers/net/wireless/rt2x00/rt2x00firmware.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00firmware.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00leds.c b/drivers/net/wireless/ralink/rt2x00/rt2x00leds.c
index c681d04b506c..c681d04b506c 100644
--- a/drivers/net/wireless/rt2x00/rt2x00leds.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00leds.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00leds.h b/drivers/net/wireless/ralink/rt2x00/rt2x00leds.h
index b2c5269570da..b2c5269570da 100644
--- a/drivers/net/wireless/rt2x00/rt2x00leds.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00leds.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h
index fb7c349ccc9c..fb7c349ccc9c 100644
--- a/drivers/net/wireless/rt2x00/rt2x00lib.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00link.c b/drivers/net/wireless/ralink/rt2x00/rt2x00link.c
index 017188e5a736..017188e5a736 100644
--- a/drivers/net/wireless/rt2x00/rt2x00link.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00link.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
index 3c26ee65a415..3c26ee65a415 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mac.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00mmio.c b/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.c
index f0178fd4fe5f..f0178fd4fe5f 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mmio.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00mmio.h b/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.h
index 701c3127efb9..701c3127efb9 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mmio.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00mmio.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.c b/drivers/net/wireless/ralink/rt2x00/rt2x00pci.c
index d93db4b0371b..eb6dbcd4fddf 100644
--- a/drivers/net/wireless/rt2x00/rt2x00pci.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00pci.c
@@ -149,6 +149,7 @@ exit_free_device:
ieee80211_free_hw(hw);
exit_release_regions:
+ pci_clear_mwi(pci_dev);
pci_release_regions(pci_dev);
exit_disable_device:
@@ -173,6 +174,7 @@ void rt2x00pci_remove(struct pci_dev *pci_dev)
/*
* Free the PCI device data.
*/
+ pci_clear_mwi(pci_dev);
pci_disable_device(pci_dev);
pci_release_regions(pci_dev);
}
diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.h b/drivers/net/wireless/ralink/rt2x00/rt2x00pci.h
index bc0ca5f58f38..bc0ca5f58f38 100644
--- a/drivers/net/wireless/rt2x00/rt2x00pci.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00pci.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c
index 68b620b2462f..68b620b2462f 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h
index 2233b911a1d7..2233b911a1d7 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00reg.h b/drivers/net/wireless/ralink/rt2x00/rt2x00reg.h
index 3cc541d13d67..3cc541d13d67 100644
--- a/drivers/net/wireless/rt2x00/rt2x00reg.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00reg.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00soc.c b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c
index 69a0cdadb07f..69a0cdadb07f 100644
--- a/drivers/net/wireless/rt2x00/rt2x00soc.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00soc.h b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.h
index 9948d355e9a4..9948d355e9a4 100644
--- a/drivers/net/wireless/rt2x00/rt2x00soc.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00soc.h
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c
index 7627af6098eb..7627af6098eb 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.h b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.h
index 569363da00a2..569363da00a2 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.h
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/ralink/rt2x00/rt61pci.c
index c0e730ea1b69..c0e730ea1b69 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt61pci.c
diff --git a/drivers/net/wireless/rt2x00/rt61pci.h b/drivers/net/wireless/ralink/rt2x00/rt61pci.h
index 1442075a8382..1442075a8382 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt61pci.h
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/ralink/rt2x00/rt73usb.c
index 7081e13b4fd6..7081e13b4fd6 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt73usb.c
diff --git a/drivers/net/wireless/rt2x00/rt73usb.h b/drivers/net/wireless/ralink/rt2x00/rt73usb.h
index 4a4f235466d1..4a4f235466d1 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt73usb.h
diff --git a/drivers/net/wireless/realtek/Kconfig b/drivers/net/wireless/realtek/Kconfig
new file mode 100644
index 000000000000..8a8ba2003964
--- /dev/null
+++ b/drivers/net/wireless/realtek/Kconfig
@@ -0,0 +1,18 @@
+config WLAN_VENDOR_REALTEK
+ bool "Realtek devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_REALTEK
+
+source "drivers/net/wireless/realtek/rtl818x/Kconfig"
+source "drivers/net/wireless/realtek/rtlwifi/Kconfig"
+source "drivers/net/wireless/realtek/rtl8xxxu/Kconfig"
+
+endif # WLAN_VENDOR_REALTEK
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c
index 53261d6f8578..451456835f87 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c
@@ -3356,9 +3356,8 @@ void ex_halbtc8192e2ant_display_coex_info(struct btc_coexist *btcoexist)
"Dot11 channel / HsMode(HsChnl)",
wifi_dot11_chnl, bt_hson, wifi_hs_chnl);
- RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %02x %02x %02x ",
- "H2C Wifi inform bt chnl Info", coex_dm->wifi_chnl_info[0],
- coex_dm->wifi_chnl_info[1], coex_dm->wifi_chnl_info[2]);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %3ph ",
+ "H2C Wifi inform bt chnl Info", coex_dm->wifi_chnl_info);
btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifirssi);
btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi);
@@ -3409,17 +3408,9 @@ void ex_halbtc8192e2ant_display_coex_info(struct btc_coexist *btcoexist)
for (i = 0; i < BT_INFO_SRC_8192E_2ANT_MAX; i++) {
if (coex_sta->bt_info_c2h_cnt[i]) {
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x %02x ",
+ "\r\n %-35s = %7ph(%d)",
GLBtInfoSrc8192e2Ant[i],
- coex_sta->bt_info_c2h[i][0],
- coex_sta->bt_info_c2h[i][1],
- coex_sta->bt_info_c2h[i][2],
- coex_sta->bt_info_c2h[i][3]);
- RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "%02x %02x %02x(%d)",
- coex_sta->bt_info_c2h[i][4],
- coex_sta->bt_info_c2h[i][5],
- coex_sta->bt_info_c2h[i][6],
+ coex_sta->bt_info_c2h[i],
coex_sta->bt_info_c2h_cnt[i]);
}
}
@@ -3453,10 +3444,8 @@ void ex_halbtc8192e2ant_display_coex_info(struct btc_coexist *btcoexist)
ps_tdma_case = coex_dm->cur_ps_tdma;
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x %02x %02x case-%d (auto:%d)",
- "PS TDMA", coex_dm->ps_tdma_para[0],
- coex_dm->ps_tdma_para[1], coex_dm->ps_tdma_para[2],
- coex_dm->ps_tdma_para[3], coex_dm->ps_tdma_para[4],
+ "\r\n %-35s = %5ph case-%d (auto:%d)",
+ "PS TDMA", coex_dm->ps_tdma_para,
ps_tdma_case, coex_dm->auto_tdma_adjust);
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %d/ %d ",
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c
index c4acd403e5f6..7e239d3cea26 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c
@@ -2457,10 +2457,9 @@ void ex_halbtc8723b1ant_display_coex_info(struct btc_coexist *btcoexist)
"Dot11 channel / HsChnl(HsMode)",
wifi_dot11_chnl, wifi_hs_chnl, bt_hs_on);
- RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %02x %02x %02x ",
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %3ph ",
"H2C Wifi inform bt chnl Info",
- coex_dm->wifi_chnl_info[0], coex_dm->wifi_chnl_info[1],
- coex_dm->wifi_chnl_info[2]);
+ coex_dm->wifi_chnl_info);
btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifi_rssi);
btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi);
@@ -2525,15 +2524,9 @@ void ex_halbtc8723b1ant_display_coex_info(struct btc_coexist *btcoexist)
for (i = 0; i < BT_INFO_SRC_8723B_1ANT_MAX; i++) {
if (coex_sta->bt_info_c2h_cnt[i]) {
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x %02x %02x %02x %02x(%d)",
+ "\r\n %-35s = %7ph(%d)",
GLBtInfoSrc8723b1Ant[i],
- coex_sta->bt_info_c2h[i][0],
- coex_sta->bt_info_c2h[i][1],
- coex_sta->bt_info_c2h[i][2],
- coex_sta->bt_info_c2h[i][3],
- coex_sta->bt_info_c2h[i][4],
- coex_sta->bt_info_c2h[i][5],
- coex_sta->bt_info_c2h[i][6],
+ coex_sta->bt_info_c2h[i],
coex_sta->bt_info_c2h_cnt[i]);
}
}
@@ -2569,10 +2562,8 @@ void ex_halbtc8723b1ant_display_coex_info(struct btc_coexist *btcoexist)
pstdmacase = coex_dm->cur_ps_tdma;
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x %02x %02x case-%d (auto:%d)",
- "PS TDMA", coex_dm->ps_tdma_para[0],
- coex_dm->ps_tdma_para[1], coex_dm->ps_tdma_para[2],
- coex_dm->ps_tdma_para[3], coex_dm->ps_tdma_para[4],
+ "\r\n %-35s = %5ph case-%d (auto:%d)",
+ "PS TDMA", coex_dm->ps_tdma_para,
pstdmacase, coex_dm->auto_tdma_adjust);
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %d ",
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c
index f2b9d11adc9e..c43ab59a690a 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c
@@ -3215,9 +3215,8 @@ void ex_btc8723b2ant_display_coex_info(struct btc_coexist *btcoexist)
"Dot11 channel / HsChnl(HsMode)",
wifi_dot11_chnl, wifi_hs_chnl, bt_hs_on);
- RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %02x %02x %02x ",
- "H2C Wifi inform bt chnl Info", coex_dm->wifi_chnl_info[0],
- coex_dm->wifi_chnl_info[1], coex_dm->wifi_chnl_info[2]);
+ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %3ph ",
+ "H2C Wifi inform bt chnl Info", coex_dm->wifi_chnl_info);
btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifi_rssi);
btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi);
@@ -3259,16 +3258,9 @@ void ex_btc8723b2ant_display_coex_info(struct btc_coexist *btcoexist)
for (i = 0; i < BT_INFO_SRC_8723B_2ANT_MAX; i++) {
if (coex_sta->bt_info_c2h_cnt[i]) {
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x "
- "%02x %02x %02x %02x(%d)",
+ "\r\n %-35s = %7ph(%d)",
glbt_info_src_8723b_2ant[i],
- coex_sta->bt_info_c2h[i][0],
- coex_sta->bt_info_c2h[i][1],
- coex_sta->bt_info_c2h[i][2],
- coex_sta->bt_info_c2h[i][3],
- coex_sta->bt_info_c2h[i][4],
- coex_sta->bt_info_c2h[i][5],
- coex_sta->bt_info_c2h[i][6],
+ coex_sta->bt_info_c2h[i],
coex_sta->bt_info_c2h_cnt[i]);
}
}
@@ -3296,10 +3288,8 @@ void ex_btc8723b2ant_display_coex_info(struct btc_coexist *btcoexist)
ps_tdma_case = coex_dm->cur_ps_tdma;
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x %02x %02x case-%d (auto:%d)",
- "PS TDMA", coex_dm->ps_tdma_para[0],
- coex_dm->ps_tdma_para[1], coex_dm->ps_tdma_para[2],
- coex_dm->ps_tdma_para[3], coex_dm->ps_tdma_para[4],
+ "\r\n %-35s = %5ph case-%d (auto:%d)",
+ "PS TDMA", coex_dm->ps_tdma_para,
ps_tdma_case, coex_dm->auto_tdma_adjust);
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %d/ %d ",
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c
index b72e5377bdbc..9cecf174a37d 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c
@@ -2302,10 +2302,9 @@ void ex_halbtc8821a1ant_display_coex_info(struct btc_coexist *btcoexist)
wifi_dot11_chnl, wifi_hs_chnl, bt_hs_on);
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x ",
+ "\r\n %-35s = %3ph ",
"H2C Wifi inform bt chnl Info",
- coex_dm->wifi_chnl_info[0], coex_dm->wifi_chnl_info[1],
- coex_dm->wifi_chnl_info[2]);
+ coex_dm->wifi_chnl_info);
btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifi_rssi);
btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi);
@@ -2366,15 +2365,9 @@ void ex_halbtc8821a1ant_display_coex_info(struct btc_coexist *btcoexist)
for (i = 0; i < BT_INFO_SRC_8821A_1ANT_MAX; i++) {
if (coex_sta->bt_info_c2h_cnt[i]) {
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x %02x %02x %02x %02x(%d)",
+ "\r\n %-35s = %7ph(%d)",
glbt_info_src_8821a_1ant[i],
- coex_sta->bt_info_c2h[i][0],
- coex_sta->bt_info_c2h[i][1],
- coex_sta->bt_info_c2h[i][2],
- coex_sta->bt_info_c2h[i][3],
- coex_sta->bt_info_c2h[i][4],
- coex_sta->bt_info_c2h[i][5],
- coex_sta->bt_info_c2h[i][6],
+ coex_sta->bt_info_c2h[i],
coex_sta->bt_info_c2h_cnt[i]);
}
}
@@ -2412,13 +2405,9 @@ void ex_halbtc8821a1ant_display_coex_info(struct btc_coexist *btcoexist)
ps_tdma_case = coex_dm->cur_ps_tdma;
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x %02x %02x case-%d (auto:%d)",
+ "\r\n %-35s = %5ph case-%d (auto:%d)",
"PS TDMA",
- coex_dm->ps_tdma_para[0],
- coex_dm->ps_tdma_para[1],
- coex_dm->ps_tdma_para[2],
- coex_dm->ps_tdma_para[3],
- coex_dm->ps_tdma_para[4],
+ coex_dm->ps_tdma_para,
ps_tdma_case,
coex_dm->auto_tdma_adjust);
diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c
index cf819f02ed23..044d914291c0 100644
--- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c
+++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c
@@ -3393,10 +3393,9 @@ ex_halbtc8821a2ant_display_coex_info(
wifi_dot_11_chnl, bt_hs_on, wifi_hs_chnl);
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x ",
+ "\r\n %-35s = %3ph ",
"H2C Wifi inform bt chnl Info",
- coex_dm->wifi_chnl_info[0], coex_dm->wifi_chnl_info[1],
- coex_dm->wifi_chnl_info[2]);
+ coex_dm->wifi_chnl_info);
btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifi_rssi);
btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi);
@@ -3454,15 +3453,9 @@ ex_halbtc8821a2ant_display_coex_info(
for (i = 0; i < BT_INFO_SRC_8821A_2ANT_MAX; i++) {
if (coex_sta->bt_info_c2h_cnt[i]) {
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x %02x %02x %02x %02x(%d)",
+ "\r\n %-35s = %7ph(%d)",
glbt_info_src_8821a_2ant[i],
- coex_sta->bt_info_c2h[i][0],
- coex_sta->bt_info_c2h[i][1],
- coex_sta->bt_info_c2h[i][2],
- coex_sta->bt_info_c2h[i][3],
- coex_sta->bt_info_c2h[i][4],
- coex_sta->bt_info_c2h[i][5],
- coex_sta->bt_info_c2h[i][6],
+ coex_sta->bt_info_c2h[i],
coex_sta->bt_info_c2h_cnt[i]);
}
}
@@ -3494,11 +3487,9 @@ ex_halbtc8821a2ant_display_coex_info(
if (!btcoexist->manual_control) {
ps_tdma_case = coex_dm->cur_ps_tdma;
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
- "\r\n %-35s = %02x %02x %02x %02x %02x case-%d",
+ "\r\n %-35s = %5ph case-%d",
"PS TDMA",
- coex_dm->ps_tdma_para[0], coex_dm->ps_tdma_para[1],
- coex_dm->ps_tdma_para[2], coex_dm->ps_tdma_para[3],
- coex_dm->ps_tdma_para[4], ps_tdma_case);
+ coex_dm->ps_tdma_para, ps_tdma_case);
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
"\r\n %-35s = %d/ %d ", "DecBtPwr/ IgnWlanAct",
diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c
index c925a4dff599..4ae421ef30d9 100644
--- a/drivers/net/wireless/realtek/rtlwifi/core.c
+++ b/drivers/net/wireless/realtek/rtlwifi/core.c
@@ -1833,8 +1833,7 @@ bool rtl_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb)
spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags);
pskb = __skb_dequeue(&ring->queue);
- if (pskb)
- kfree_skb(pskb);
+ kfree_skb(pskb);
/*this is wrong, fill_tx_cmddesc needs update*/
pdesc = &ring->desc[0];
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c
index f46c9d7f6528..7f471bff435c 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.c
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
@@ -801,7 +801,9 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
hw_queue);
if (rx_remained_cnt == 0)
return;
-
+ buffer_desc = &rtlpci->rx_ring[rxring_idx].buffer_desc[
+ rtlpci->rx_ring[rxring_idx].idx];
+ pdesc = (struct rtl_rx_desc *)skb->data;
} else { /* rx descriptor */
pdesc = &rtlpci->rx_ring[rxring_idx].desc[
rtlpci->rx_ring[rxring_idx].idx];
@@ -824,13 +826,6 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
new_skb = dev_alloc_skb(rtlpci->rxbuffersize);
if (unlikely(!new_skb))
goto no_new;
- if (rtlpriv->use_new_trx_flow) {
- buffer_desc =
- &rtlpci->rx_ring[rxring_idx].buffer_desc
- [rtlpci->rx_ring[rxring_idx].idx];
- /*means rx wifi info*/
- pdesc = (struct rtl_rx_desc *)skb->data;
- }
memset(&rx_status , 0 , sizeof(rx_status));
rtlpriv->cfg->ops->query_rx_desc(hw, &stats,
&rx_status, (u8 *)pdesc, skb);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c
index 11344121c55e..47e32cb0ec1a 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c
@@ -88,8 +88,6 @@ int rtl88e_init_sw_vars(struct ieee80211_hw *hw)
u8 tid;
rtl8188ee_bt_reg_init(hw);
- rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
-
rtlpriv->dm.dm_initialgain_enable = 1;
rtlpriv->dm.dm_flag = 0;
rtlpriv->dm.disable_framebursting = 0;
@@ -138,6 +136,11 @@ int rtl88e_init_sw_vars(struct ieee80211_hw *hw)
rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps;
rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps;
rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps;
+ rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
+ rtlpriv->cfg->mod_params->sw_crypto =
+ rtlpriv->cfg->mod_params->sw_crypto;
+ rtlpriv->cfg->mod_params->disable_watchdog =
+ rtlpriv->cfg->mod_params->disable_watchdog;
if (rtlpriv->cfg->mod_params->disable_watchdog)
pr_info("watchdog disabled\n");
if (!rtlpriv->psc.inactiveps)
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c
index de6cb6c3a48c..4780bdc63b2b 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/sw.c
@@ -139,6 +139,8 @@ int rtl92c_init_sw_vars(struct ieee80211_hw *hw)
rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps;
rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps;
rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps;
+ rtlpriv->cfg->mod_params->sw_crypto =
+ rtlpriv->cfg->mod_params->sw_crypto;
if (!rtlpriv->psc.inactiveps)
pr_info("rtl8192ce: Power Save off (module option)\n");
if (!rtlpriv->psc.fwctrl_lps)
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c
index fd4a5353d216..7c6f7f0d18c6 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/sw.c
@@ -65,6 +65,8 @@ static int rtl92cu_init_sw_vars(struct ieee80211_hw *hw)
rtlpriv->dm.disable_framebursting = false;
rtlpriv->dm.thermalvalue = 0;
rtlpriv->dbg.global_debuglevel = rtlpriv->cfg->mod_params->debug;
+ rtlpriv->cfg->mod_params->sw_crypto =
+ rtlpriv->cfg->mod_params->sw_crypto;
/* for firmware buf */
rtlpriv->rtlhal.pfirmware = vzalloc(0x4000);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c
index b19d0398215f..c6e09a19de1a 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/sw.c
@@ -376,8 +376,8 @@ module_param_named(swlps, rtl92de_mod_params.swctrl_lps, bool, 0444);
module_param_named(fwlps, rtl92de_mod_params.fwctrl_lps, bool, 0444);
MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n");
MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n");
-MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n");
-MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n");
+MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 1)\n");
+MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 0)\n");
MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)");
static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c
index e1fd27c888bf..31baca41ac2f 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/sw.c
@@ -187,6 +187,8 @@ static int rtl92s_init_sw_vars(struct ieee80211_hw *hw)
rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps;
rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps;
rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps;
+ rtlpriv->cfg->mod_params->sw_crypto =
+ rtlpriv->cfg->mod_params->sw_crypto;
if (!rtlpriv->psc.inactiveps)
pr_info("Power Save off (module option)\n");
if (!rtlpriv->psc.fwctrl_lps)
@@ -425,8 +427,8 @@ module_param_named(swlps, rtl92se_mod_params.swctrl_lps, bool, 0444);
module_param_named(fwlps, rtl92se_mod_params.fwctrl_lps, bool, 0444);
MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n");
MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n");
-MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n");
-MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n");
+MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 1)\n");
+MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 0)\n");
MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)");
static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c
index 3859b3e3d158..ff49a8c0ff61 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/sw.c
@@ -150,6 +150,11 @@ int rtl8723e_init_sw_vars(struct ieee80211_hw *hw)
rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps;
rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps;
rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps;
+ rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
+ rtlpriv->cfg->mod_params->sw_crypto =
+ rtlpriv->cfg->mod_params->sw_crypto;
+ rtlpriv->cfg->mod_params->disable_watchdog =
+ rtlpriv->cfg->mod_params->disable_watchdog;
if (rtlpriv->cfg->mod_params->disable_watchdog)
pr_info("watchdog disabled\n");
rtlpriv->psc.reg_fwctrl_lps = 3;
@@ -267,6 +272,8 @@ static struct rtl_mod_params rtl8723e_mod_params = {
.swctrl_lps = false,
.fwctrl_lps = true,
.debug = DBG_EMERG,
+ .msi_support = false,
+ .disable_watchdog = false,
};
static struct rtl_hal_cfg rtl8723e_hal_cfg = {
@@ -383,12 +390,14 @@ module_param_named(debug, rtl8723e_mod_params.debug, int, 0444);
module_param_named(ips, rtl8723e_mod_params.inactiveps, bool, 0444);
module_param_named(swlps, rtl8723e_mod_params.swctrl_lps, bool, 0444);
module_param_named(fwlps, rtl8723e_mod_params.fwctrl_lps, bool, 0444);
+module_param_named(msi, rtl8723e_mod_params.msi_support, bool, 0444);
module_param_named(disable_watchdog, rtl8723e_mod_params.disable_watchdog,
bool, 0444);
MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n");
MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n");
MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n");
MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n");
+MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 0)\n");
MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)");
MODULE_PARM_DESC(disable_watchdog, "Set to 1 to disable the watchdog (default 0)\n");
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c
index d091f1d5f91e..a78eaeda0008 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c
@@ -93,7 +93,6 @@ int rtl8723be_init_sw_vars(struct ieee80211_hw *hw)
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
rtl8723be_bt_reg_init(hw);
- rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
rtlpriv->btcoexist.btc_ops = rtl_btc_get_ops_pointer();
rtlpriv->dm.dm_initialgain_enable = 1;
@@ -151,6 +150,10 @@ int rtl8723be_init_sw_vars(struct ieee80211_hw *hw)
rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps;
rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps;
rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
+ rtlpriv->cfg->mod_params->sw_crypto =
+ rtlpriv->cfg->mod_params->sw_crypto;
+ rtlpriv->cfg->mod_params->disable_watchdog =
+ rtlpriv->cfg->mod_params->disable_watchdog;
if (rtlpriv->cfg->mod_params->disable_watchdog)
pr_info("watchdog disabled\n");
rtlpriv->psc.reg_fwctrl_lps = 3;
@@ -267,6 +270,9 @@ static struct rtl_mod_params rtl8723be_mod_params = {
.inactiveps = true,
.swctrl_lps = false,
.fwctrl_lps = true,
+ .msi_support = false,
+ .disable_watchdog = false,
+ .debug = DBG_EMERG,
};
static struct rtl_hal_cfg rtl8723be_hal_cfg = {
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723com/fw_common.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723com/fw_common.c
index a2f5e89bedfe..6e518625edbe 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8723com/fw_common.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723com/fw_common.c
@@ -318,9 +318,7 @@ bool rtl8723_cmd_send_packet(struct ieee80211_hw *hw,
ring = &rtlpci->tx_ring[BEACON_QUEUE];
pskb = __skb_dequeue(&ring->queue);
- if (pskb)
- kfree_skb(pskb);
-
+ kfree_skb(pskb);
spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags);
pdesc = &ring->desc[0];
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c
index 6e9418ed90c2..bbb789f8990b 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c
@@ -2272,7 +2272,7 @@ void rtl8821ae_enable_interrupt(struct ieee80211_hw *hw)
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
- if (!rtlpci->int_clear)
+ if (rtlpci->int_clear)
rtl8821ae_clear_interrupt(hw);/*clear it here first*/
rtl_write_dword(rtlpriv, REG_HIMR, rtlpci->irq_mask[0] & 0xFFFFFFFF);
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c
index 8ee141a55bc5..4159f9b14db6 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c
@@ -95,8 +95,6 @@ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw)
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
rtl8821ae_bt_reg_init(hw);
- rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
- rtlpci->int_clear = rtlpriv->cfg->mod_params->int_clear;
rtlpriv->btcoexist.btc_ops = rtl_btc_get_ops_pointer();
rtlpriv->dm.dm_initialgain_enable = 1;
@@ -168,12 +166,15 @@ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw)
rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps;
rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps;
rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
- rtlpci->msi_support = rtlpriv->cfg->mod_params->int_clear;
+ rtlpci->int_clear = rtlpriv->cfg->mod_params->int_clear;
+ rtlpriv->cfg->mod_params->sw_crypto =
+ rtlpriv->cfg->mod_params->sw_crypto;
+ rtlpriv->cfg->mod_params->disable_watchdog =
+ rtlpriv->cfg->mod_params->disable_watchdog;
if (rtlpriv->cfg->mod_params->disable_watchdog)
pr_info("watchdog disabled\n");
rtlpriv->psc.reg_fwctrl_lps = 3;
rtlpriv->psc.reg_max_lps_awakeintvl = 5;
- rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
/* for ASPM, you can close aspm through
* set const_support_pciaspm = 0
@@ -448,7 +449,7 @@ MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n");
MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 1)\n");
MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)");
MODULE_PARM_DESC(disable_watchdog, "Set to 1 to disable the watchdog (default 0)\n");
-MODULE_PARM_DESC(int_clear, "Set to 1 to disable interrupt clear before set (default 0)\n");
+MODULE_PARM_DESC(int_clear, "Set to 0 to disable interrupt clear before set (default 1)\n");
static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume);
diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.c b/drivers/net/wireless/realtek/rtlwifi/usb.c
index 2721cf89fb16..aac1ed3f7bb4 100644
--- a/drivers/net/wireless/realtek/rtlwifi/usb.c
+++ b/drivers/net/wireless/realtek/rtlwifi/usb.c
@@ -531,6 +531,8 @@ static void _rtl_usb_rx_process_noagg(struct ieee80211_hw *hw,
ieee80211_rx(hw, skb);
else
dev_kfree_skb_any(skb);
+ } else {
+ dev_kfree_skb_any(skb);
}
}
diff --git a/drivers/net/wireless/rsi/Kconfig b/drivers/net/wireless/rsi/Kconfig
index 35245f994c10..7c5e4ca4e3d0 100644
--- a/drivers/net/wireless/rsi/Kconfig
+++ b/drivers/net/wireless/rsi/Kconfig
@@ -1,3 +1,16 @@
+config WLAN_VENDOR_RSI
+ bool "Redpine Signals Inc devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_RSI
+
config RSI_91X
tristate "Redpine Signals Inc 91x WLAN driver support"
depends on MAC80211
@@ -28,3 +41,5 @@ config RSI_USB
---help---
This option enables the USB bus support in rsi drivers.
Select M (recommended), if you have a RSI 1x1 wireless module.
+
+endif # WLAN_VENDOR_RSI
diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
index 8d110fd9eba1..e43b59d5b53b 100644
--- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c
+++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
@@ -1023,7 +1023,7 @@ static int rsi_send_auto_rate_request(struct rsi_common *common)
return -ENOMEM;
}
- selected_rates = kmalloc(2 * RSI_TBL_SZ, GFP_KERNEL);
+ selected_rates = kzalloc(2 * RSI_TBL_SZ, GFP_KERNEL);
if (!selected_rates) {
rsi_dbg(ERR_ZONE, "%s: Failed in allocation of mem\n",
__func__);
@@ -1032,7 +1032,6 @@ static int rsi_send_auto_rate_request(struct rsi_common *common)
}
memset(skb->data, 0, sizeof(struct rsi_auto_rate));
- memset(selected_rates, 0, 2 * RSI_TBL_SZ);
auto_rate = (struct rsi_auto_rate *)skb->data;
@@ -1227,7 +1226,7 @@ int rsi_send_block_unblock_frame(struct rsi_common *common, bool block_event)
mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12);
mgmt_frame->desc_word[1] = cpu_to_le16(BLOCK_HW_QUEUE);
- if (block_event == true) {
+ if (block_event) {
rsi_dbg(INFO_ZONE, "blocking the data qs\n");
mgmt_frame->desc_word[4] = cpu_to_le16(0xf);
} else {
diff --git a/drivers/net/wireless/st/Kconfig b/drivers/net/wireless/st/Kconfig
new file mode 100644
index 000000000000..969b4f6e53b5
--- /dev/null
+++ b/drivers/net/wireless/st/Kconfig
@@ -0,0 +1,16 @@
+config WLAN_VENDOR_ST
+ bool "STMicroelectronics devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_ST
+
+source "drivers/net/wireless/st/cw1200/Kconfig"
+
+endif # WLAN_VENDOR_ST
diff --git a/drivers/net/wireless/st/Makefile b/drivers/net/wireless/st/Makefile
new file mode 100644
index 000000000000..a60d6350ba46
--- /dev/null
+++ b/drivers/net/wireless/st/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_CW1200) += cw1200/
diff --git a/drivers/net/wireless/cw1200/Kconfig b/drivers/net/wireless/st/cw1200/Kconfig
index 0880742eab17..0880742eab17 100644
--- a/drivers/net/wireless/cw1200/Kconfig
+++ b/drivers/net/wireless/st/cw1200/Kconfig
diff --git a/drivers/net/wireless/cw1200/Makefile b/drivers/net/wireless/st/cw1200/Makefile
index b086aac6547a..b086aac6547a 100644
--- a/drivers/net/wireless/cw1200/Makefile
+++ b/drivers/net/wireless/st/cw1200/Makefile
diff --git a/drivers/net/wireless/cw1200/bh.c b/drivers/net/wireless/st/cw1200/bh.c
index 92d299aa257c..92d299aa257c 100644
--- a/drivers/net/wireless/cw1200/bh.c
+++ b/drivers/net/wireless/st/cw1200/bh.c
diff --git a/drivers/net/wireless/cw1200/bh.h b/drivers/net/wireless/st/cw1200/bh.h
index af6a4853728f..af6a4853728f 100644
--- a/drivers/net/wireless/cw1200/bh.h
+++ b/drivers/net/wireless/st/cw1200/bh.h
diff --git a/drivers/net/wireless/cw1200/cw1200.h b/drivers/net/wireless/st/cw1200/cw1200.h
index 1ad7d3602520..1ad7d3602520 100644
--- a/drivers/net/wireless/cw1200/cw1200.h
+++ b/drivers/net/wireless/st/cw1200/cw1200.h
diff --git a/drivers/net/wireless/cw1200/cw1200_sdio.c b/drivers/net/wireless/st/cw1200/cw1200_sdio.c
index d3acc85932a5..d3acc85932a5 100644
--- a/drivers/net/wireless/cw1200/cw1200_sdio.c
+++ b/drivers/net/wireless/st/cw1200/cw1200_sdio.c
diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/st/cw1200/cw1200_spi.c
index a740083634d8..a740083634d8 100644
--- a/drivers/net/wireless/cw1200/cw1200_spi.c
+++ b/drivers/net/wireless/st/cw1200/cw1200_spi.c
diff --git a/drivers/net/wireless/cw1200/debug.c b/drivers/net/wireless/st/cw1200/debug.c
index 34f97c31eecf..34f97c31eecf 100644
--- a/drivers/net/wireless/cw1200/debug.c
+++ b/drivers/net/wireless/st/cw1200/debug.c
diff --git a/drivers/net/wireless/cw1200/debug.h b/drivers/net/wireless/st/cw1200/debug.h
index b525aba53bfc..b525aba53bfc 100644
--- a/drivers/net/wireless/cw1200/debug.h
+++ b/drivers/net/wireless/st/cw1200/debug.h
diff --git a/drivers/net/wireless/cw1200/fwio.c b/drivers/net/wireless/st/cw1200/fwio.c
index 30e7646d04af..30e7646d04af 100644
--- a/drivers/net/wireless/cw1200/fwio.c
+++ b/drivers/net/wireless/st/cw1200/fwio.c
diff --git a/drivers/net/wireless/cw1200/fwio.h b/drivers/net/wireless/st/cw1200/fwio.h
index ea3099362cdf..ea3099362cdf 100644
--- a/drivers/net/wireless/cw1200/fwio.h
+++ b/drivers/net/wireless/st/cw1200/fwio.h
diff --git a/drivers/net/wireless/cw1200/hwbus.h b/drivers/net/wireless/st/cw1200/hwbus.h
index 8b2fc831c3de..8b2fc831c3de 100644
--- a/drivers/net/wireless/cw1200/hwbus.h
+++ b/drivers/net/wireless/st/cw1200/hwbus.h
diff --git a/drivers/net/wireless/cw1200/hwio.c b/drivers/net/wireless/st/cw1200/hwio.c
index ff230b7aeedd..ff230b7aeedd 100644
--- a/drivers/net/wireless/cw1200/hwio.c
+++ b/drivers/net/wireless/st/cw1200/hwio.c
diff --git a/drivers/net/wireless/cw1200/hwio.h b/drivers/net/wireless/st/cw1200/hwio.h
index ddf52669dc5b..ddf52669dc5b 100644
--- a/drivers/net/wireless/cw1200/hwio.h
+++ b/drivers/net/wireless/st/cw1200/hwio.h
diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/st/cw1200/main.c
index 0e51e27d2e3f..0e51e27d2e3f 100644
--- a/drivers/net/wireless/cw1200/main.c
+++ b/drivers/net/wireless/st/cw1200/main.c
diff --git a/drivers/net/wireless/cw1200/pm.c b/drivers/net/wireless/st/cw1200/pm.c
index d2202ae92bdd..d2202ae92bdd 100644
--- a/drivers/net/wireless/cw1200/pm.c
+++ b/drivers/net/wireless/st/cw1200/pm.c
diff --git a/drivers/net/wireless/cw1200/pm.h b/drivers/net/wireless/st/cw1200/pm.h
index 3ed90ff22bb8..3ed90ff22bb8 100644
--- a/drivers/net/wireless/cw1200/pm.h
+++ b/drivers/net/wireless/st/cw1200/pm.h
diff --git a/drivers/net/wireless/cw1200/queue.c b/drivers/net/wireless/st/cw1200/queue.c
index 0ba5ef9b3e7b..0ba5ef9b3e7b 100644
--- a/drivers/net/wireless/cw1200/queue.c
+++ b/drivers/net/wireless/st/cw1200/queue.c
diff --git a/drivers/net/wireless/cw1200/queue.h b/drivers/net/wireless/st/cw1200/queue.h
index 119f9c79c14e..119f9c79c14e 100644
--- a/drivers/net/wireless/cw1200/queue.h
+++ b/drivers/net/wireless/st/cw1200/queue.h
diff --git a/drivers/net/wireless/cw1200/scan.c b/drivers/net/wireless/st/cw1200/scan.c
index bff81b8d4164..bff81b8d4164 100644
--- a/drivers/net/wireless/cw1200/scan.c
+++ b/drivers/net/wireless/st/cw1200/scan.c
diff --git a/drivers/net/wireless/cw1200/scan.h b/drivers/net/wireless/st/cw1200/scan.h
index cc75459e5784..cc75459e5784 100644
--- a/drivers/net/wireless/cw1200/scan.h
+++ b/drivers/net/wireless/st/cw1200/scan.h
diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/st/cw1200/sta.c
index 95a7fdb3cc1c..06321c799c90 100644
--- a/drivers/net/wireless/cw1200/sta.c
+++ b/drivers/net/wireless/st/cw1200/sta.c
@@ -873,12 +873,6 @@ int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
else
val32 = 0; /* disabled */
- if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
- /* device is down, can _not_ set threshold */
- ret = -ENODEV;
- goto out;
- }
-
if (priv->rts_threshold == value)
goto out;
diff --git a/drivers/net/wireless/cw1200/sta.h b/drivers/net/wireless/st/cw1200/sta.h
index bebb3379017f..bebb3379017f 100644
--- a/drivers/net/wireless/cw1200/sta.h
+++ b/drivers/net/wireless/st/cw1200/sta.h
diff --git a/drivers/net/wireless/cw1200/txrx.c b/drivers/net/wireless/st/cw1200/txrx.c
index d28bd49cb5fd..d28bd49cb5fd 100644
--- a/drivers/net/wireless/cw1200/txrx.c
+++ b/drivers/net/wireless/st/cw1200/txrx.c
diff --git a/drivers/net/wireless/cw1200/txrx.h b/drivers/net/wireless/st/cw1200/txrx.h
index 492a4e14213b..492a4e14213b 100644
--- a/drivers/net/wireless/cw1200/txrx.h
+++ b/drivers/net/wireless/st/cw1200/txrx.h
diff --git a/drivers/net/wireless/cw1200/wsm.c b/drivers/net/wireless/st/cw1200/wsm.c
index 9e0ca3048657..9e0ca3048657 100644
--- a/drivers/net/wireless/cw1200/wsm.c
+++ b/drivers/net/wireless/st/cw1200/wsm.c
diff --git a/drivers/net/wireless/cw1200/wsm.h b/drivers/net/wireless/st/cw1200/wsm.h
index 48086e849515..48086e849515 100644
--- a/drivers/net/wireless/cw1200/wsm.h
+++ b/drivers/net/wireless/st/cw1200/wsm.h
diff --git a/drivers/net/wireless/ti/Kconfig b/drivers/net/wireless/ti/Kconfig
index cbe1e7fef61b..92fbd6597e34 100644
--- a/drivers/net/wireless/ti/Kconfig
+++ b/drivers/net/wireless/ti/Kconfig
@@ -1,11 +1,15 @@
-menuconfig WL_TI
- bool "TI Wireless LAN support"
+config WLAN_VENDOR_TI
+ bool "Texas Instrument devices"
+ default y
---help---
- This section contains support for all the wireless drivers
- for Texas Instruments WLAN chips, such as wl1251 and the wl12xx
- family.
+ If you have a wireless card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about cards. If you say Y, you will be asked for
+ your specific card in the following questions.
-if WL_TI
+if WLAN_VENDOR_TI
source "drivers/net/wireless/ti/wl1251/Kconfig"
source "drivers/net/wireless/ti/wl12xx/Kconfig"
source "drivers/net/wireless/ti/wl18xx/Kconfig"
@@ -21,4 +25,4 @@ config WILINK_PLATFORM_DATA
Small platform data bit needed to pass data to the sdio modules.
-endif # WL_TI
+endif # WLAN_VENDOR_TI
diff --git a/drivers/net/wireless/ti/wl1251/Kconfig b/drivers/net/wireless/ti/wl1251/Kconfig
index 477a206c098e..7142ccf3a425 100644
--- a/drivers/net/wireless/ti/wl1251/Kconfig
+++ b/drivers/net/wireless/ti/wl1251/Kconfig
@@ -1,4 +1,4 @@
-menuconfig WL1251
+config WL1251
tristate "TI wl1251 driver support"
depends on MAC80211
select FW_LOADER
diff --git a/drivers/net/wireless/ti/wl12xx/conf.h b/drivers/net/wireless/ti/wl12xx/conf.h
index 75e29897a0f5..a606ba9ef041 100644
--- a/drivers/net/wireless/ti/wl12xx/conf.h
+++ b/drivers/net/wireless/ti/wl12xx/conf.h
@@ -47,4 +47,237 @@ struct wl12xx_priv_conf {
struct conf_memory_settings mem_wl127x;
};
+enum wl12xx_sg_params {
+ /*
+ * Configure the min and max time BT gains the antenna
+ * in WLAN / BT master basic rate
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_ACL_BT_MASTER_MIN_BR = 0,
+ WL12XX_CONF_SG_ACL_BT_MASTER_MAX_BR,
+
+ /*
+ * Configure the min and max time BT gains the antenna
+ * in WLAN / BT slave basic rate
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_ACL_BT_SLAVE_MIN_BR,
+ WL12XX_CONF_SG_ACL_BT_SLAVE_MAX_BR,
+
+ /*
+ * Configure the min and max time BT gains the antenna
+ * in WLAN / BT master EDR
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_ACL_BT_MASTER_MIN_EDR,
+ WL12XX_CONF_SG_ACL_BT_MASTER_MAX_EDR,
+
+ /*
+ * Configure the min and max time BT gains the antenna
+ * in WLAN / BT slave EDR
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_ACL_BT_SLAVE_MIN_EDR,
+ WL12XX_CONF_SG_ACL_BT_SLAVE_MAX_EDR,
+
+ /*
+ * The maximum time WLAN can gain the antenna
+ * in WLAN PSM / BT master/slave BR
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_ACL_WLAN_PS_MASTER_BR,
+ WL12XX_CONF_SG_ACL_WLAN_PS_SLAVE_BR,
+
+ /*
+ * The maximum time WLAN can gain the antenna
+ * in WLAN PSM / BT master/slave EDR
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_ACL_WLAN_PS_MASTER_EDR,
+ WL12XX_CONF_SG_ACL_WLAN_PS_SLAVE_EDR,
+
+ /* TODO: explain these values */
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR,
+ WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR,
+
+ WL12XX_CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR,
+ WL12XX_CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR,
+ WL12XX_CONF_SG_ACL_PASSIVE_SCAN_BT_BR,
+ WL12XX_CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR,
+ WL12XX_CONF_SG_ACL_PASSIVE_SCAN_BT_EDR,
+ WL12XX_CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR,
+
+ /*
+ * Compensation percentage of probe requests when scan initiated
+ * during BT voice/ACL link.
+ *
+ * Range: 0 - 255 (%)
+ */
+ WL12XX_CONF_SG_AUTO_SCAN_PROBE_REQ,
+
+ /*
+ * Compensation percentage of probe requests when active scan initiated
+ * during BT voice
+ *
+ * Range: 0 - 255 (%)
+ */
+ WL12XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3,
+
+ /*
+ * Compensation percentage of WLAN active scan window if initiated
+ * during BT A2DP
+ *
+ * Range: 0 - 1000 (%)
+ */
+ WL12XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP,
+
+ /*
+ * Compensation percentage of WLAN passive scan window if initiated
+ * during BT A2DP BR
+ *
+ * Range: 0 - 1000 (%)
+ */
+ WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_A2DP_BR,
+
+ /*
+ * Compensation percentage of WLAN passive scan window if initiated
+ * during BT A2DP EDR
+ *
+ * Range: 0 - 1000 (%)
+ */
+ WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_A2DP_EDR,
+
+ /*
+ * Compensation percentage of WLAN passive scan window if initiated
+ * during BT voice
+ *
+ * Range: 0 - 1000 (%)
+ */
+ WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_HV3,
+
+ /* TODO: explain these values */
+ WL12XX_CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN,
+ WL12XX_CONF_SG_BCN_HV3_COLL_THR_IN_PASSIVE_SCAN,
+ WL12XX_CONF_SG_TX_RX_PROTECT_BW_IN_PASSIVE_SCAN,
+
+ /*
+ * Defines whether the SG will force WLAN host to enter/exit PSM
+ *
+ * Range: 1 - SG can force, 0 - host handles PSM
+ */
+ WL12XX_CONF_SG_STA_FORCE_PS_IN_BT_SCO,
+
+ /*
+ * Defines antenna configuration (single/dual antenna)
+ *
+ * Range: 0 - single antenna, 1 - dual antenna
+ */
+ WL12XX_CONF_SG_ANTENNA_CONFIGURATION,
+
+ /*
+ * The threshold (percent) of max consecutive beacon misses before
+ * increasing priority of beacon reception.
+ *
+ * Range: 0 - 100 (%)
+ */
+ WL12XX_CONF_SG_BEACON_MISS_PERCENT,
+
+ /*
+ * Protection time of the DHCP procedure.
+ *
+ * Range: 0 - 100000 (ms)
+ */
+ WL12XX_CONF_SG_DHCP_TIME,
+
+ /*
+ * RX guard time before the beginning of a new BT voice frame during
+ * which no new WLAN trigger frame is transmitted.
+ *
+ * Range: 0 - 100000 (us)
+ */
+ WL12XX_CONF_SG_RXT,
+
+ /*
+ * TX guard time before the beginning of a new BT voice frame during
+ * which no new WLAN frame is transmitted.
+ *
+ * Range: 0 - 100000 (us)
+ */
+ WL12XX_CONF_SG_TXT,
+
+ /*
+ * Enable adaptive RXT/TXT algorithm. If disabled, the host values
+ * will be utilized.
+ *
+ * Range: 0 - disable, 1 - enable
+ */
+ WL12XX_CONF_SG_ADAPTIVE_RXT_TXT,
+
+ /* TODO: explain this value */
+ WL12XX_CONF_SG_GENERAL_USAGE_BIT_MAP,
+
+ /*
+ * Number of consecutive BT voice frames not interrupted by WLAN
+ *
+ * Range: 0 - 100
+ */
+ WL12XX_CONF_SG_HV3_MAX_SERVED,
+
+ /*
+ * The used WLAN legacy service period during active BT ACL link
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_PS_POLL_TIMEOUT,
+
+ /*
+ * The used WLAN UPSD service period during active BT ACL link
+ *
+ * Range: 0 - 255 (ms)
+ */
+ WL12XX_CONF_SG_UPSD_TIMEOUT,
+
+ WL12XX_CONF_SG_CONSECUTIVE_CTS_THRESHOLD,
+ WL12XX_CONF_SG_STA_RX_WINDOW_AFTER_DTIM,
+ WL12XX_CONF_SG_STA_CONNECTION_PROTECTION_TIME,
+
+ /* AP params */
+ WL12XX_CONF_AP_BEACON_MISS_TX,
+ WL12XX_CONF_AP_RX_WINDOW_AFTER_BEACON,
+ WL12XX_CONF_AP_BEACON_WINDOW_INTERVAL,
+ WL12XX_CONF_AP_CONNECTION_PROTECTION_TIME,
+ WL12XX_CONF_AP_BT_ACL_VAL_BT_SERVE_TIME,
+ WL12XX_CONF_AP_BT_ACL_VAL_WL_SERVE_TIME,
+
+ /* CTS Diluting params */
+ WL12XX_CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH,
+ WL12XX_CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER,
+
+ WL12XX_CONF_SG_TEMP_PARAM_1,
+ WL12XX_CONF_SG_TEMP_PARAM_2,
+ WL12XX_CONF_SG_TEMP_PARAM_3,
+ WL12XX_CONF_SG_TEMP_PARAM_4,
+ WL12XX_CONF_SG_TEMP_PARAM_5,
+ WL12XX_CONF_SG_TEMP_PARAM_6,
+ WL12XX_CONF_SG_TEMP_PARAM_7,
+ WL12XX_CONF_SG_TEMP_PARAM_8,
+ WL12XX_CONF_SG_TEMP_PARAM_9,
+ WL12XX_CONF_SG_TEMP_PARAM_10,
+
+ WL12XX_CONF_SG_PARAMS_MAX,
+ WL12XX_CONF_SG_PARAMS_ALL = 0xff
+};
+
#endif /* __WL12XX_CONF_H__ */
diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c
index af0fe2e17151..a0d6cccc56f3 100644
--- a/drivers/net/wireless/ti/wl12xx/main.c
+++ b/drivers/net/wireless/ti/wl12xx/main.c
@@ -39,6 +39,7 @@
#include "scan.h"
#include "event.h"
#include "debugfs.h"
+#include "conf.h"
static char *fref_param;
static char *tcxo_param;
@@ -46,69 +47,69 @@ static char *tcxo_param;
static struct wlcore_conf wl12xx_conf = {
.sg = {
.params = {
- [CONF_SG_ACL_BT_MASTER_MIN_BR] = 10,
- [CONF_SG_ACL_BT_MASTER_MAX_BR] = 180,
- [CONF_SG_ACL_BT_SLAVE_MIN_BR] = 10,
- [CONF_SG_ACL_BT_SLAVE_MAX_BR] = 180,
- [CONF_SG_ACL_BT_MASTER_MIN_EDR] = 10,
- [CONF_SG_ACL_BT_MASTER_MAX_EDR] = 80,
- [CONF_SG_ACL_BT_SLAVE_MIN_EDR] = 10,
- [CONF_SG_ACL_BT_SLAVE_MAX_EDR] = 80,
- [CONF_SG_ACL_WLAN_PS_MASTER_BR] = 8,
- [CONF_SG_ACL_WLAN_PS_SLAVE_BR] = 8,
- [CONF_SG_ACL_WLAN_PS_MASTER_EDR] = 20,
- [CONF_SG_ACL_WLAN_PS_SLAVE_EDR] = 20,
- [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR] = 20,
- [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR] = 35,
- [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR] = 16,
- [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR] = 35,
- [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR] = 32,
- [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR] = 50,
- [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR] = 28,
- [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR] = 50,
- [CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR] = 10,
- [CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR] = 20,
- [CONF_SG_ACL_PASSIVE_SCAN_BT_BR] = 75,
- [CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR] = 15,
- [CONF_SG_ACL_PASSIVE_SCAN_BT_EDR] = 27,
- [CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR] = 17,
+ [WL12XX_CONF_SG_ACL_BT_MASTER_MIN_BR] = 10,
+ [WL12XX_CONF_SG_ACL_BT_MASTER_MAX_BR] = 180,
+ [WL12XX_CONF_SG_ACL_BT_SLAVE_MIN_BR] = 10,
+ [WL12XX_CONF_SG_ACL_BT_SLAVE_MAX_BR] = 180,
+ [WL12XX_CONF_SG_ACL_BT_MASTER_MIN_EDR] = 10,
+ [WL12XX_CONF_SG_ACL_BT_MASTER_MAX_EDR] = 80,
+ [WL12XX_CONF_SG_ACL_BT_SLAVE_MIN_EDR] = 10,
+ [WL12XX_CONF_SG_ACL_BT_SLAVE_MAX_EDR] = 80,
+ [WL12XX_CONF_SG_ACL_WLAN_PS_MASTER_BR] = 8,
+ [WL12XX_CONF_SG_ACL_WLAN_PS_SLAVE_BR] = 8,
+ [WL12XX_CONF_SG_ACL_WLAN_PS_MASTER_EDR] = 20,
+ [WL12XX_CONF_SG_ACL_WLAN_PS_SLAVE_EDR] = 20,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR] = 20,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR] = 35,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR] = 16,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR] = 35,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR] = 32,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR] = 50,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR] = 28,
+ [WL12XX_CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR] = 50,
+ [WL12XX_CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR] = 10,
+ [WL12XX_CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR] = 20,
+ [WL12XX_CONF_SG_ACL_PASSIVE_SCAN_BT_BR] = 75,
+ [WL12XX_CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR] = 15,
+ [WL12XX_CONF_SG_ACL_PASSIVE_SCAN_BT_EDR] = 27,
+ [WL12XX_CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR] = 17,
/* active scan params */
- [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
- [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
- [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100,
+ [WL12XX_CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
+ [WL12XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
+ [WL12XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100,
/* passive scan params */
- [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_BR] = 800,
- [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_EDR] = 200,
- [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200,
+ [WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_A2DP_BR] = 800,
+ [WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_A2DP_EDR] = 200,
+ [WL12XX_CONF_SG_PASSIVE_SCAN_DUR_FACTOR_HV3] = 200,
/* passive scan in dual antenna params */
- [CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0,
- [CONF_SG_BCN_HV3_COLLISION_THRESH_IN_PASSIVE_SCAN] = 0,
- [CONF_SG_TX_RX_PROTECTION_BWIDTH_IN_PASSIVE_SCAN] = 0,
+ [WL12XX_CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0,
+ [WL12XX_CONF_SG_BCN_HV3_COLL_THR_IN_PASSIVE_SCAN] = 0,
+ [WL12XX_CONF_SG_TX_RX_PROTECT_BW_IN_PASSIVE_SCAN] = 0,
/* general params */
- [CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1,
- [CONF_SG_ANTENNA_CONFIGURATION] = 0,
- [CONF_SG_BEACON_MISS_PERCENT] = 60,
- [CONF_SG_DHCP_TIME] = 5000,
- [CONF_SG_RXT] = 1200,
- [CONF_SG_TXT] = 1000,
- [CONF_SG_ADAPTIVE_RXT_TXT] = 1,
- [CONF_SG_GENERAL_USAGE_BIT_MAP] = 3,
- [CONF_SG_HV3_MAX_SERVED] = 6,
- [CONF_SG_PS_POLL_TIMEOUT] = 10,
- [CONF_SG_UPSD_TIMEOUT] = 10,
- [CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2,
- [CONF_SG_STA_RX_WINDOW_AFTER_DTIM] = 5,
- [CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 30,
+ [WL12XX_CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1,
+ [WL12XX_CONF_SG_ANTENNA_CONFIGURATION] = 0,
+ [WL12XX_CONF_SG_BEACON_MISS_PERCENT] = 60,
+ [WL12XX_CONF_SG_DHCP_TIME] = 5000,
+ [WL12XX_CONF_SG_RXT] = 1200,
+ [WL12XX_CONF_SG_TXT] = 1000,
+ [WL12XX_CONF_SG_ADAPTIVE_RXT_TXT] = 1,
+ [WL12XX_CONF_SG_GENERAL_USAGE_BIT_MAP] = 3,
+ [WL12XX_CONF_SG_HV3_MAX_SERVED] = 6,
+ [WL12XX_CONF_SG_PS_POLL_TIMEOUT] = 10,
+ [WL12XX_CONF_SG_UPSD_TIMEOUT] = 10,
+ [WL12XX_CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2,
+ [WL12XX_CONF_SG_STA_RX_WINDOW_AFTER_DTIM] = 5,
+ [WL12XX_CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 30,
/* AP params */
- [CONF_AP_BEACON_MISS_TX] = 3,
- [CONF_AP_RX_WINDOW_AFTER_BEACON] = 10,
- [CONF_AP_BEACON_WINDOW_INTERVAL] = 2,
- [CONF_AP_CONNECTION_PROTECTION_TIME] = 0,
- [CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25,
- [CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25,
+ [WL12XX_CONF_AP_BEACON_MISS_TX] = 3,
+ [WL12XX_CONF_AP_RX_WINDOW_AFTER_BEACON] = 10,
+ [WL12XX_CONF_AP_BEACON_WINDOW_INTERVAL] = 2,
+ [WL12XX_CONF_AP_CONNECTION_PROTECTION_TIME] = 0,
+ [WL12XX_CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25,
+ [WL12XX_CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25,
/* CTS Diluting params */
- [CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0,
- [CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0,
+ [WL12XX_CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0,
+ [WL12XX_CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0,
},
.state = CONF_SG_PROTECTIVE,
},
@@ -1809,6 +1810,7 @@ static int wl12xx_setup(struct wl1271 *wl)
BUILD_BUG_ON(WL12XX_MAX_LINKS > WLCORE_MAX_LINKS);
BUILD_BUG_ON(WL12XX_MAX_AP_STATIONS > WL12XX_MAX_LINKS);
+ BUILD_BUG_ON(WL12XX_CONF_SG_PARAMS_MAX > WLCORE_CONF_SG_PARAMS_MAX);
wl->rtable = wl12xx_rtable;
wl->num_tx_desc = WL12XX_NUM_TX_DESCRIPTORS;
diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h
index 71f1ec448ba5..7aa880f14ccb 100644
--- a/drivers/net/wireless/ti/wl18xx/conf.h
+++ b/drivers/net/wireless/ti/wl18xx/conf.h
@@ -139,4 +139,94 @@ struct wl18xx_priv_conf {
struct conf_ap_sleep_settings ap_sleep;
} __packed;
+enum wl18xx_sg_params {
+ WL18XX_CONF_SG_PARAM_0 = 0,
+
+ /* Configuration Parameters */
+ WL18XX_CONF_SG_ANTENNA_CONFIGURATION,
+ WL18XX_CONF_SG_ZIGBEE_COEX,
+ WL18XX_CONF_SG_TIME_SYNC,
+
+ WL18XX_CONF_SG_PARAM_4,
+ WL18XX_CONF_SG_PARAM_5,
+ WL18XX_CONF_SG_PARAM_6,
+ WL18XX_CONF_SG_PARAM_7,
+ WL18XX_CONF_SG_PARAM_8,
+ WL18XX_CONF_SG_PARAM_9,
+ WL18XX_CONF_SG_PARAM_10,
+ WL18XX_CONF_SG_PARAM_11,
+ WL18XX_CONF_SG_PARAM_12,
+ WL18XX_CONF_SG_PARAM_13,
+ WL18XX_CONF_SG_PARAM_14,
+ WL18XX_CONF_SG_PARAM_15,
+ WL18XX_CONF_SG_PARAM_16,
+ WL18XX_CONF_SG_PARAM_17,
+ WL18XX_CONF_SG_PARAM_18,
+ WL18XX_CONF_SG_PARAM_19,
+ WL18XX_CONF_SG_PARAM_20,
+ WL18XX_CONF_SG_PARAM_21,
+ WL18XX_CONF_SG_PARAM_22,
+ WL18XX_CONF_SG_PARAM_23,
+ WL18XX_CONF_SG_PARAM_24,
+ WL18XX_CONF_SG_PARAM_25,
+
+ /* Active Scan Parameters */
+ WL18XX_CONF_SG_AUTO_SCAN_PROBE_REQ,
+ WL18XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3,
+
+ WL18XX_CONF_SG_PARAM_28,
+
+ /* Passive Scan Parameters */
+ WL18XX_CONF_SG_PARAM_29,
+ WL18XX_CONF_SG_PARAM_30,
+ WL18XX_CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3,
+
+ /* Passive Scan in Dual Antenna Parameters */
+ WL18XX_CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN,
+ WL18XX_CONF_SG_BEACON_HV3_COLL_TH_IN_PASSIVE_SCAN,
+ WL18XX_CONF_SG_TX_RX_PROTECT_BW_IN_PASSIVE_SCAN,
+
+ /* General Parameters */
+ WL18XX_CONF_SG_STA_FORCE_PS_IN_BT_SCO,
+ WL18XX_CONF_SG_PARAM_36,
+ WL18XX_CONF_SG_BEACON_MISS_PERCENT,
+ WL18XX_CONF_SG_PARAM_38,
+ WL18XX_CONF_SG_RXT,
+ WL18XX_CONF_SG_UNUSED,
+ WL18XX_CONF_SG_ADAPTIVE_RXT_TXT,
+ WL18XX_CONF_SG_GENERAL_USAGE_BIT_MAP,
+ WL18XX_CONF_SG_HV3_MAX_SERVED,
+ WL18XX_CONF_SG_PARAM_44,
+ WL18XX_CONF_SG_PARAM_45,
+ WL18XX_CONF_SG_CONSECUTIVE_CTS_THRESHOLD,
+ WL18XX_CONF_SG_GEMINI_PARAM_47,
+ WL18XX_CONF_SG_STA_CONNECTION_PROTECTION_TIME,
+
+ /* AP Parameters */
+ WL18XX_CONF_SG_AP_BEACON_MISS_TX,
+ WL18XX_CONF_SG_PARAM_50,
+ WL18XX_CONF_SG_AP_BEACON_WINDOW_INTERVAL,
+ WL18XX_CONF_SG_AP_CONNECTION_PROTECTION_TIME,
+ WL18XX_CONF_SG_PARAM_53,
+ WL18XX_CONF_SG_PARAM_54,
+
+ /* CTS Diluting Parameters */
+ WL18XX_CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH,
+ WL18XX_CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER,
+
+ WL18XX_CONF_SG_TEMP_PARAM_1,
+ WL18XX_CONF_SG_TEMP_PARAM_2,
+ WL18XX_CONF_SG_TEMP_PARAM_3,
+ WL18XX_CONF_SG_TEMP_PARAM_4,
+ WL18XX_CONF_SG_TEMP_PARAM_5,
+ WL18XX_CONF_SG_TEMP_PARAM_6,
+ WL18XX_CONF_SG_TEMP_PARAM_7,
+ WL18XX_CONF_SG_TEMP_PARAM_8,
+ WL18XX_CONF_SG_TEMP_PARAM_9,
+ WL18XX_CONF_SG_TEMP_PARAM_10,
+
+ WL18XX_CONF_SG_PARAMS_MAX,
+ WL18XX_CONF_SG_PARAMS_ALL = 0xff
+};
+
#endif /* __WL18XX_CONF_H__ */
diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c
index 09c7e098f460..719907a0a2c2 100644
--- a/drivers/net/wireless/ti/wl18xx/event.c
+++ b/drivers/net/wireless/ti/wl18xx/event.c
@@ -205,6 +205,8 @@ int wl18xx_process_mailbox_events(struct wl1271 *wl)
mbox->sc_ssid,
mbox->sc_pwd_len,
mbox->sc_pwd);
+ if (vector & FW_LOGGER_INDICATION)
+ wlcore_event_fw_logger(wl);
return 0;
}
diff --git a/drivers/net/wireless/ti/wl18xx/event.h b/drivers/net/wireless/ti/wl18xx/event.h
index f3d4f13379cb..070de1274694 100644
--- a/drivers/net/wireless/ti/wl18xx/event.h
+++ b/drivers/net/wireless/ti/wl18xx/event.h
@@ -41,6 +41,7 @@ enum {
SMART_CONFIG_SYNC_EVENT_ID = BIT(22),
SMART_CONFIG_DECODE_EVENT_ID = BIT(23),
TIME_SYNC_EVENT_ID = BIT(24),
+ FW_LOGGER_INDICATION = BIT(25),
};
enum wl18xx_radar_types {
diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
index 50cce42089a5..1bf26cc7374e 100644
--- a/drivers/net/wireless/ti/wl18xx/main.c
+++ b/drivers/net/wireless/ti/wl18xx/main.c
@@ -177,69 +177,80 @@ enum wl18xx_hw_rates {
static struct wlcore_conf wl18xx_conf = {
.sg = {
.params = {
- [CONF_SG_ACL_BT_MASTER_MIN_BR] = 10,
- [CONF_SG_ACL_BT_MASTER_MAX_BR] = 180,
- [CONF_SG_ACL_BT_SLAVE_MIN_BR] = 10,
- [CONF_SG_ACL_BT_SLAVE_MAX_BR] = 180,
- [CONF_SG_ACL_BT_MASTER_MIN_EDR] = 10,
- [CONF_SG_ACL_BT_MASTER_MAX_EDR] = 80,
- [CONF_SG_ACL_BT_SLAVE_MIN_EDR] = 10,
- [CONF_SG_ACL_BT_SLAVE_MAX_EDR] = 80,
- [CONF_SG_ACL_WLAN_PS_MASTER_BR] = 8,
- [CONF_SG_ACL_WLAN_PS_SLAVE_BR] = 8,
- [CONF_SG_ACL_WLAN_PS_MASTER_EDR] = 20,
- [CONF_SG_ACL_WLAN_PS_SLAVE_EDR] = 20,
- [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR] = 20,
- [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR] = 35,
- [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR] = 16,
- [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR] = 35,
- [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR] = 32,
- [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR] = 50,
- [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR] = 28,
- [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR] = 50,
- [CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR] = 10,
- [CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR] = 20,
- [CONF_SG_ACL_PASSIVE_SCAN_BT_BR] = 75,
- [CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR] = 15,
- [CONF_SG_ACL_PASSIVE_SCAN_BT_EDR] = 27,
- [CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR] = 17,
- /* active scan params */
- [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
- [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
- [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100,
- /* passive scan params */
- [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_BR] = 800,
- [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_EDR] = 200,
- [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200,
- /* passive scan in dual antenna params */
- [CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0,
- [CONF_SG_BCN_HV3_COLLISION_THRESH_IN_PASSIVE_SCAN] = 0,
- [CONF_SG_TX_RX_PROTECTION_BWIDTH_IN_PASSIVE_SCAN] = 0,
- /* general params */
- [CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1,
- [CONF_SG_ANTENNA_CONFIGURATION] = 0,
- [CONF_SG_BEACON_MISS_PERCENT] = 60,
- [CONF_SG_DHCP_TIME] = 5000,
- [CONF_SG_RXT] = 1200,
- [CONF_SG_TXT] = 1000,
- [CONF_SG_ADAPTIVE_RXT_TXT] = 1,
- [CONF_SG_GENERAL_USAGE_BIT_MAP] = 3,
- [CONF_SG_HV3_MAX_SERVED] = 6,
- [CONF_SG_PS_POLL_TIMEOUT] = 10,
- [CONF_SG_UPSD_TIMEOUT] = 10,
- [CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2,
- [CONF_SG_STA_RX_WINDOW_AFTER_DTIM] = 5,
- [CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 30,
- /* AP params */
- [CONF_AP_BEACON_MISS_TX] = 3,
- [CONF_AP_RX_WINDOW_AFTER_BEACON] = 10,
- [CONF_AP_BEACON_WINDOW_INTERVAL] = 2,
- [CONF_AP_CONNECTION_PROTECTION_TIME] = 0,
- [CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25,
- [CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25,
- /* CTS Diluting params */
- [CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0,
- [CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0,
+ [WL18XX_CONF_SG_PARAM_0] = 0,
+ /* Configuartion Parameters */
+ [WL18XX_CONF_SG_ANTENNA_CONFIGURATION] = 0,
+ [WL18XX_CONF_SG_ZIGBEE_COEX] = 0,
+ [WL18XX_CONF_SG_TIME_SYNC] = 0,
+ [WL18XX_CONF_SG_PARAM_4] = 0,
+ [WL18XX_CONF_SG_PARAM_5] = 0,
+ [WL18XX_CONF_SG_PARAM_6] = 0,
+ [WL18XX_CONF_SG_PARAM_7] = 0,
+ [WL18XX_CONF_SG_PARAM_8] = 0,
+ [WL18XX_CONF_SG_PARAM_9] = 0,
+ [WL18XX_CONF_SG_PARAM_10] = 0,
+ [WL18XX_CONF_SG_PARAM_11] = 0,
+ [WL18XX_CONF_SG_PARAM_12] = 0,
+ [WL18XX_CONF_SG_PARAM_13] = 0,
+ [WL18XX_CONF_SG_PARAM_14] = 0,
+ [WL18XX_CONF_SG_PARAM_15] = 0,
+ [WL18XX_CONF_SG_PARAM_16] = 0,
+ [WL18XX_CONF_SG_PARAM_17] = 0,
+ [WL18XX_CONF_SG_PARAM_18] = 0,
+ [WL18XX_CONF_SG_PARAM_19] = 0,
+ [WL18XX_CONF_SG_PARAM_20] = 0,
+ [WL18XX_CONF_SG_PARAM_21] = 0,
+ [WL18XX_CONF_SG_PARAM_22] = 0,
+ [WL18XX_CONF_SG_PARAM_23] = 0,
+ [WL18XX_CONF_SG_PARAM_24] = 0,
+ [WL18XX_CONF_SG_PARAM_25] = 0,
+ /* Active Scan Parameters */
+ [WL18XX_CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
+ [WL18XX_CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
+ [WL18XX_CONF_SG_PARAM_28] = 0,
+ /* Passive Scan Parameters */
+ [WL18XX_CONF_SG_PARAM_29] = 0,
+ [WL18XX_CONF_SG_PARAM_30] = 0,
+ [WL18XX_CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200,
+ /* Passive Scan in Dual Antenna Parameters */
+ [WL18XX_CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0,
+ [WL18XX_CONF_SG_BEACON_HV3_COLL_TH_IN_PASSIVE_SCAN] = 0,
+ [WL18XX_CONF_SG_TX_RX_PROTECT_BW_IN_PASSIVE_SCAN] = 0,
+ /* General Parameters */
+ [WL18XX_CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1,
+ [WL18XX_CONF_SG_PARAM_36] = 0,
+ [WL18XX_CONF_SG_BEACON_MISS_PERCENT] = 60,
+ [WL18XX_CONF_SG_PARAM_38] = 0,
+ [WL18XX_CONF_SG_RXT] = 1200,
+ [WL18XX_CONF_SG_UNUSED] = 0,
+ [WL18XX_CONF_SG_ADAPTIVE_RXT_TXT] = 1,
+ [WL18XX_CONF_SG_GENERAL_USAGE_BIT_MAP] = 3,
+ [WL18XX_CONF_SG_HV3_MAX_SERVED] = 6,
+ [WL18XX_CONF_SG_PARAM_44] = 0,
+ [WL18XX_CONF_SG_PARAM_45] = 0,
+ [WL18XX_CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2,
+ [WL18XX_CONF_SG_GEMINI_PARAM_47] = 0,
+ [WL18XX_CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 0,
+ /* AP Parameters */
+ [WL18XX_CONF_SG_AP_BEACON_MISS_TX] = 3,
+ [WL18XX_CONF_SG_PARAM_50] = 0,
+ [WL18XX_CONF_SG_AP_BEACON_WINDOW_INTERVAL] = 2,
+ [WL18XX_CONF_SG_AP_CONNECTION_PROTECTION_TIME] = 30,
+ [WL18XX_CONF_SG_PARAM_53] = 0,
+ [WL18XX_CONF_SG_PARAM_54] = 0,
+ /* CTS Diluting Parameters */
+ [WL18XX_CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0,
+ [WL18XX_CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0,
+ [WL18XX_CONF_SG_TEMP_PARAM_1] = 0,
+ [WL18XX_CONF_SG_TEMP_PARAM_2] = 0,
+ [WL18XX_CONF_SG_TEMP_PARAM_3] = 0,
+ [WL18XX_CONF_SG_TEMP_PARAM_4] = 0,
+ [WL18XX_CONF_SG_TEMP_PARAM_5] = 0,
+ [WL18XX_CONF_SG_TEMP_PARAM_6] = 0,
+ [WL18XX_CONF_SG_TEMP_PARAM_7] = 0,
+ [WL18XX_CONF_SG_TEMP_PARAM_8] = 0,
+ [WL18XX_CONF_SG_TEMP_PARAM_9] = 0,
+ [WL18XX_CONF_SG_TEMP_PARAM_10] = 0,
},
.state = CONF_SG_PROTECTIVE,
},
@@ -461,7 +472,7 @@ static struct wlcore_conf wl18xx_conf = {
},
.fwlog = {
.mode = WL12XX_FWLOG_CONTINUOUS,
- .mem_blocks = 2,
+ .mem_blocks = 0,
.severity = 0,
.timestamp = WL12XX_FWLOG_TIMESTAMP_DISABLED,
.output = WL12XX_FWLOG_OUTPUT_DBG_PINS,
@@ -584,7 +595,7 @@ static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = {
.mem = { .start = 0x00A00000, .size = 0x00012000 },
.reg = { .start = 0x00807000, .size = 0x00005000 },
.mem2 = { .start = 0x00800000, .size = 0x0000B000 },
- .mem3 = { .start = 0x00000000, .size = 0x00000000 },
+ .mem3 = { .start = 0x00401594, .size = 0x00001020 },
},
[PART_DOWN] = {
.mem = { .start = 0x00000000, .size = 0x00014000 },
@@ -602,7 +613,7 @@ static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = {
.mem = { .start = 0x00800000, .size = 0x000050FC },
.reg = { .start = 0x00B00404, .size = 0x00001000 },
.mem2 = { .start = 0x00C00000, .size = 0x00000400 },
- .mem3 = { .start = 0x00000000, .size = 0x00000000 },
+ .mem3 = { .start = 0x00401594, .size = 0x00001020 },
},
[PART_PHY_INIT] = {
.mem = { .start = WL18XX_PHY_INIT_MEM_ADDR,
@@ -1029,7 +1040,8 @@ static int wl18xx_boot(struct wl1271 *wl)
DFS_CHANNELS_CONFIG_COMPLETE_EVENT |
SMART_CONFIG_SYNC_EVENT_ID |
SMART_CONFIG_DECODE_EVENT_ID |
- TIME_SYNC_EVENT_ID;
+ TIME_SYNC_EVENT_ID |
+ FW_LOGGER_INDICATION;
wl->ap_event_mask = MAX_TX_FAILURE_EVENT_ID;
@@ -1895,6 +1907,7 @@ static int wl18xx_setup(struct wl1271 *wl)
BUILD_BUG_ON(WL18XX_MAX_LINKS > WLCORE_MAX_LINKS);
BUILD_BUG_ON(WL18XX_MAX_AP_STATIONS > WL18XX_MAX_LINKS);
+ BUILD_BUG_ON(WL18XX_CONF_SG_PARAMS_MAX > WLCORE_CONF_SG_PARAMS_MAX);
wl->rtable = wl18xx_rtable;
wl->num_tx_desc = WL18XX_NUM_TX_DESCRIPTORS;
diff --git a/drivers/net/wireless/ti/wlcore/Kconfig b/drivers/net/wireless/ti/wlcore/Kconfig
index 7c099542b214..969c9d79bfc8 100644
--- a/drivers/net/wireless/ti/wlcore/Kconfig
+++ b/drivers/net/wireless/ti/wlcore/Kconfig
@@ -1,6 +1,6 @@
config WLCORE
tristate "TI wlcore support"
- depends on WL_TI && MAC80211
+ depends on MAC80211
select FW_LOADER
---help---
This module contains the main code for TI WLAN chips. It abstracts
diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c
index f28fa3b5029d..26cc23f32241 100644
--- a/drivers/net/wireless/ti/wlcore/acx.c
+++ b/drivers/net/wireless/ti/wlcore/acx.c
@@ -534,9 +534,9 @@ int wl12xx_acx_sg_cfg(struct wl1271 *wl)
}
/* BT-WLAN coext parameters */
- for (i = 0; i < CONF_SG_PARAMS_MAX; i++)
+ for (i = 0; i < WLCORE_CONF_SG_PARAMS_MAX; i++)
param->params[i] = cpu_to_le32(c->params[i]);
- param->param_idx = CONF_SG_PARAMS_ALL;
+ param->param_idx = WLCORE_CONF_SG_PARAMS_ALL;
ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param));
if (ret < 0) {
diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h
index 954d57ec98f4..0d61fae88dcb 100644
--- a/drivers/net/wireless/ti/wlcore/acx.h
+++ b/drivers/net/wireless/ti/wlcore/acx.h
@@ -300,7 +300,7 @@ struct acx_bt_wlan_coex {
struct acx_bt_wlan_coex_param {
struct acx_header header;
- __le32 params[CONF_SG_PARAMS_MAX];
+ __le32 params[WLCORE_CONF_SG_PARAMS_MAX];
u8 param_idx;
u8 padding[3];
} __packed;
diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h
index 8dc46c0a489a..e28e2f2303ce 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.h
+++ b/drivers/net/wireless/ti/wlcore/cmd.h
@@ -626,7 +626,6 @@ struct wl12xx_cmd_remove_peer {
*/
enum wl12xx_fwlogger_log_mode {
WL12XX_FWLOG_CONTINUOUS,
- WL12XX_FWLOG_ON_DEMAND
};
/* Include/exclude timestamps from the log messages */
diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h
index 52a9d1b14020..44d898fe0afc 100644
--- a/drivers/net/wireless/ti/wlcore/conf.h
+++ b/drivers/net/wireless/ti/wlcore/conf.h
@@ -110,242 +110,11 @@ enum {
CONF_SG_OPPORTUNISTIC
};
-enum {
- /*
- * Configure the min and max time BT gains the antenna
- * in WLAN / BT master basic rate
- *
- * Range: 0 - 255 (ms)
- */
- CONF_SG_ACL_BT_MASTER_MIN_BR = 0,
- CONF_SG_ACL_BT_MASTER_MAX_BR,
-
- /*
- * Configure the min and max time BT gains the antenna
- * in WLAN / BT slave basic rate
- *
- * Range: 0 - 255 (ms)
- */
- CONF_SG_ACL_BT_SLAVE_MIN_BR,
- CONF_SG_ACL_BT_SLAVE_MAX_BR,
-
- /*
- * Configure the min and max time BT gains the antenna
- * in WLAN / BT master EDR
- *
- * Range: 0 - 255 (ms)
- */
- CONF_SG_ACL_BT_MASTER_MIN_EDR,
- CONF_SG_ACL_BT_MASTER_MAX_EDR,
-
- /*
- * Configure the min and max time BT gains the antenna
- * in WLAN / BT slave EDR
- *
- * Range: 0 - 255 (ms)
- */
- CONF_SG_ACL_BT_SLAVE_MIN_EDR,
- CONF_SG_ACL_BT_SLAVE_MAX_EDR,
-
- /*
- * The maximum time WLAN can gain the antenna
- * in WLAN PSM / BT master/slave BR
- *
- * Range: 0 - 255 (ms)
- */
- CONF_SG_ACL_WLAN_PS_MASTER_BR,
- CONF_SG_ACL_WLAN_PS_SLAVE_BR,
-
- /*
- * The maximum time WLAN can gain the antenna
- * in WLAN PSM / BT master/slave EDR
- *
- * Range: 0 - 255 (ms)
- */
- CONF_SG_ACL_WLAN_PS_MASTER_EDR,
- CONF_SG_ACL_WLAN_PS_SLAVE_EDR,
-
- /* TODO: explain these values */
- CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR,
- CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR,
- CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR,
- CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR,
- CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR,
- CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR,
- CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR,
- CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR,
-
- CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR,
- CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR,
- CONF_SG_ACL_PASSIVE_SCAN_BT_BR,
- CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR,
- CONF_SG_ACL_PASSIVE_SCAN_BT_EDR,
- CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR,
-
- /*
- * Compensation percentage of probe requests when scan initiated
- * during BT voice/ACL link.
- *
- * Range: 0 - 255 (%)
- */
- CONF_SG_AUTO_SCAN_PROBE_REQ,
-
- /*
- * Compensation percentage of probe requests when active scan initiated
- * during BT voice
- *
- * Range: 0 - 255 (%)
- */
- CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3,
-
- /*
- * Compensation percentage of WLAN active scan window if initiated
- * during BT A2DP
- *
- * Range: 0 - 1000 (%)
- */
- CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP,
-
- /*
- * Compensation percentage of WLAN passive scan window if initiated
- * during BT A2DP BR
- *
- * Range: 0 - 1000 (%)
- */
- CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_BR,
-
- /*
- * Compensation percentage of WLAN passive scan window if initiated
- * during BT A2DP EDR
- *
- * Range: 0 - 1000 (%)
- */
- CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_EDR,
-
- /*
- * Compensation percentage of WLAN passive scan window if initiated
- * during BT voice
- *
- * Range: 0 - 1000 (%)
- */
- CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3,
-
- /* TODO: explain these values */
- CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN,
- CONF_SG_BCN_HV3_COLLISION_THRESH_IN_PASSIVE_SCAN,
- CONF_SG_TX_RX_PROTECTION_BWIDTH_IN_PASSIVE_SCAN,
-
- /*
- * Defines whether the SG will force WLAN host to enter/exit PSM
- *
- * Range: 1 - SG can force, 0 - host handles PSM
- */
- CONF_SG_STA_FORCE_PS_IN_BT_SCO,
-
- /*
- * Defines antenna configuration (single/dual antenna)
- *
- * Range: 0 - single antenna, 1 - dual antenna
- */
- CONF_SG_ANTENNA_CONFIGURATION,
-
- /*
- * The threshold (percent) of max consecutive beacon misses before
- * increasing priority of beacon reception.
- *
- * Range: 0 - 100 (%)
- */
- CONF_SG_BEACON_MISS_PERCENT,
-
- /*
- * Protection time of the DHCP procedure.
- *
- * Range: 0 - 100000 (ms)
- */
- CONF_SG_DHCP_TIME,
-
- /*
- * RX guard time before the beginning of a new BT voice frame during
- * which no new WLAN trigger frame is transmitted.
- *
- * Range: 0 - 100000 (us)
- */
- CONF_SG_RXT,
-
- /*
- * TX guard time before the beginning of a new BT voice frame during
- * which no new WLAN frame is transmitted.
- *
- * Range: 0 - 100000 (us)
- */
-
- CONF_SG_TXT,
-
- /*
- * Enable adaptive RXT/TXT algorithm. If disabled, the host values
- * will be utilized.
- *
- * Range: 0 - disable, 1 - enable
- */
- CONF_SG_ADAPTIVE_RXT_TXT,
-
- /* TODO: explain this value */
- CONF_SG_GENERAL_USAGE_BIT_MAP,
-
- /*
- * Number of consecutive BT voice frames not interrupted by WLAN
- *
- * Range: 0 - 100
- */
- CONF_SG_HV3_MAX_SERVED,
-
- /*
- * The used WLAN legacy service period during active BT ACL link
- *
- * Range: 0 - 255 (ms)
- */
- CONF_SG_PS_POLL_TIMEOUT,
-
- /*
- * The used WLAN UPSD service period during active BT ACL link
- *
- * Range: 0 - 255 (ms)
- */
- CONF_SG_UPSD_TIMEOUT,
-
- CONF_SG_CONSECUTIVE_CTS_THRESHOLD,
- CONF_SG_STA_RX_WINDOW_AFTER_DTIM,
- CONF_SG_STA_CONNECTION_PROTECTION_TIME,
-
- /* AP params */
- CONF_AP_BEACON_MISS_TX,
- CONF_AP_RX_WINDOW_AFTER_BEACON,
- CONF_AP_BEACON_WINDOW_INTERVAL,
- CONF_AP_CONNECTION_PROTECTION_TIME,
- CONF_AP_BT_ACL_VAL_BT_SERVE_TIME,
- CONF_AP_BT_ACL_VAL_WL_SERVE_TIME,
-
- /* CTS Diluting params */
- CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH,
- CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER,
-
- CONF_SG_TEMP_PARAM_1,
- CONF_SG_TEMP_PARAM_2,
- CONF_SG_TEMP_PARAM_3,
- CONF_SG_TEMP_PARAM_4,
- CONF_SG_TEMP_PARAM_5,
- CONF_SG_TEMP_PARAM_6,
- CONF_SG_TEMP_PARAM_7,
- CONF_SG_TEMP_PARAM_8,
- CONF_SG_TEMP_PARAM_9,
- CONF_SG_TEMP_PARAM_10,
-
- CONF_SG_PARAMS_MAX,
- CONF_SG_PARAMS_ALL = 0xff
-};
+#define WLCORE_CONF_SG_PARAMS_MAX 67
+#define WLCORE_CONF_SG_PARAMS_ALL 0xff
struct conf_sg_settings {
- u32 params[CONF_SG_PARAMS_MAX];
+ u32 params[WLCORE_CONF_SG_PARAMS_MAX];
u8 state;
} __packed;
diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c
index eb43f94a1597..7f672f6879d0 100644
--- a/drivers/net/wireless/ti/wlcore/debugfs.c
+++ b/drivers/net/wireless/ti/wlcore/debugfs.c
@@ -1205,26 +1205,11 @@ err_out:
static loff_t dev_mem_seek(struct file *file, loff_t offset, int orig)
{
- loff_t ret;
-
/* only requests of dword-aligned size and offset are supported */
if (offset % 4)
return -EINVAL;
- switch (orig) {
- case SEEK_SET:
- file->f_pos = offset;
- ret = file->f_pos;
- break;
- case SEEK_CUR:
- file->f_pos += offset;
- ret = file->f_pos;
- break;
- default:
- ret = -EINVAL;
- }
-
- return ret;
+ return no_seek_end_llseek(file, offset, orig);
}
static const struct file_operations dev_mem_ops = {
@@ -1234,6 +1219,65 @@ static const struct file_operations dev_mem_ops = {
.llseek = dev_mem_seek,
};
+static ssize_t fw_logger_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wl1271 *wl = file->private_data;
+
+ return wl1271_format_buffer(user_buf, count,
+ ppos, "%d\n",
+ wl->conf.fwlog.output);
+}
+
+static ssize_t fw_logger_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wl1271 *wl = file->private_data;
+ unsigned long value;
+ int ret;
+
+ ret = kstrtoul_from_user(user_buf, count, 0, &value);
+ if (ret < 0) {
+ wl1271_warning("illegal value in fw_logger");
+ return -EINVAL;
+ }
+
+ if ((value > 2) || (value == 0)) {
+ wl1271_warning("fw_logger value must be 1-UART 2-SDIO");
+ return -ERANGE;
+ }
+
+ if (wl->conf.fwlog.output == 0) {
+ wl1271_warning("iligal opperation - fw logger disabled by default, please change mode via wlconf");
+ return -EINVAL;
+ }
+
+ mutex_lock(&wl->mutex);
+ ret = wl1271_ps_elp_wakeup(wl);
+ if (ret < 0) {
+ count = ret;
+ goto out;
+ }
+
+ wl->conf.fwlog.output = value;
+
+ ret = wl12xx_cmd_config_fwlog(wl);
+
+ wl1271_ps_elp_sleep(wl);
+
+out:
+ mutex_unlock(&wl->mutex);
+ return count;
+}
+
+static const struct file_operations fw_logger_ops = {
+ .open = simple_open,
+ .read = fw_logger_read,
+ .write = fw_logger_write,
+ .llseek = default_llseek,
+};
+
static int wl1271_debugfs_add_files(struct wl1271 *wl,
struct dentry *rootdir)
{
@@ -1260,6 +1304,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl,
DEBUGFS_ADD(irq_timeout, rootdir);
DEBUGFS_ADD(fw_stats_raw, rootdir);
DEBUGFS_ADD(sleep_auth, rootdir);
+ DEBUGFS_ADD(fw_logger, rootdir);
streaming = debugfs_create_dir("rx_streaming", rootdir);
if (!streaming || IS_ERR(streaming))
diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c
index c42e78955e7b..c96405498bf4 100644
--- a/drivers/net/wireless/ti/wlcore/event.c
+++ b/drivers/net/wireless/ti/wlcore/event.c
@@ -28,6 +28,88 @@
#include "ps.h"
#include "scan.h"
#include "wl12xx_80211.h"
+#include "hw_ops.h"
+
+#define WL18XX_LOGGER_SDIO_BUFF_MAX (0x1020)
+#define WL18XX_DATA_RAM_BASE_ADDRESS (0x20000000)
+#define WL18XX_LOGGER_SDIO_BUFF_ADDR (0x40159c)
+#define WL18XX_LOGGER_BUFF_OFFSET (sizeof(struct fw_logger_information))
+#define WL18XX_LOGGER_READ_POINT_OFFSET (12)
+
+int wlcore_event_fw_logger(struct wl1271 *wl)
+{
+ u32 ret;
+ struct fw_logger_information fw_log;
+ u8 *buffer;
+ u32 internal_fw_addrbase = WL18XX_DATA_RAM_BASE_ADDRESS;
+ u32 addr = WL18XX_LOGGER_SDIO_BUFF_ADDR;
+ u32 end_buff_addr = WL18XX_LOGGER_SDIO_BUFF_ADDR +
+ WL18XX_LOGGER_BUFF_OFFSET;
+ u32 available_len;
+ u32 actual_len;
+ u32 clear_addr;
+ size_t len;
+ u32 start_loc;
+
+ buffer = kzalloc(WL18XX_LOGGER_SDIO_BUFF_MAX, GFP_KERNEL);
+ if (!buffer) {
+ wl1271_error("Fail to allocate fw logger memory");
+ fw_log.actual_buff_size = cpu_to_le32(0);
+ goto out;
+ }
+
+ ret = wlcore_read(wl, addr, buffer, WL18XX_LOGGER_SDIO_BUFF_MAX,
+ false);
+ if (ret < 0) {
+ wl1271_error("Fail to read logger buffer, error_id = %d",
+ ret);
+ fw_log.actual_buff_size = cpu_to_le32(0);
+ goto free_out;
+ }
+
+ memcpy(&fw_log, buffer, sizeof(fw_log));
+
+ if (le32_to_cpu(fw_log.actual_buff_size) == 0)
+ goto free_out;
+
+ actual_len = le32_to_cpu(fw_log.actual_buff_size);
+ start_loc = (le32_to_cpu(fw_log.buff_read_ptr) -
+ internal_fw_addrbase) - addr;
+ end_buff_addr += le32_to_cpu(fw_log.max_buff_size);
+ available_len = end_buff_addr -
+ (le32_to_cpu(fw_log.buff_read_ptr) -
+ internal_fw_addrbase);
+ actual_len = min(actual_len, available_len);
+ len = actual_len;
+
+ wl12xx_copy_fwlog(wl, &buffer[start_loc], len);
+ clear_addr = addr + start_loc + le32_to_cpu(fw_log.actual_buff_size) +
+ internal_fw_addrbase;
+
+ len = le32_to_cpu(fw_log.actual_buff_size) - len;
+ if (len) {
+ wl12xx_copy_fwlog(wl,
+ &buffer[WL18XX_LOGGER_BUFF_OFFSET],
+ len);
+ clear_addr = addr + WL18XX_LOGGER_BUFF_OFFSET + len +
+ internal_fw_addrbase;
+ }
+
+ /* double check that clear address and write pointer are the same */
+ if (clear_addr != le32_to_cpu(fw_log.buff_write_ptr)) {
+ wl1271_error("Calculate of clear addr Clear = %x, write = %x",
+ clear_addr, le32_to_cpu(fw_log.buff_write_ptr));
+ }
+
+ /* indicate FW about Clear buffer */
+ ret = wlcore_write32(wl, addr + WL18XX_LOGGER_READ_POINT_OFFSET,
+ fw_log.buff_write_ptr);
+free_out:
+ kfree(buffer);
+out:
+ return le32_to_cpu(fw_log.actual_buff_size);
+}
+EXPORT_SYMBOL_GPL(wlcore_event_fw_logger);
void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr)
{
diff --git a/drivers/net/wireless/ti/wlcore/event.h b/drivers/net/wireless/ti/wlcore/event.h
index acc7a59d3828..75e8e98da2fe 100644
--- a/drivers/net/wireless/ti/wlcore/event.h
+++ b/drivers/net/wireless/ti/wlcore/event.h
@@ -64,6 +64,14 @@ enum {
#define NUM_OF_RSSI_SNR_TRIGGERS 8
+struct fw_logger_information {
+ __le32 max_buff_size;
+ __le32 actual_buff_size;
+ __le32 num_trace_drop;
+ __le32 buff_read_ptr;
+ __le32 buff_write_ptr;
+} __packed;
+
struct wl1271;
int wl1271_event_unmask(struct wl1271 *wl);
@@ -84,4 +92,5 @@ void wlcore_event_max_tx_failure(struct wl1271 *wl, unsigned long sta_bitmap);
void wlcore_event_inactive_sta(struct wl1271 *wl, unsigned long sta_bitmap);
void wlcore_event_roc_complete(struct wl1271 *wl);
void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr);
+int wlcore_event_fw_logger(struct wl1271 *wl);
#endif
diff --git a/drivers/net/wireless/ti/wlcore/io.c b/drivers/net/wireless/ti/wlcore/io.c
index 68e74eefd296..9ac118e727e9 100644
--- a/drivers/net/wireless/ti/wlcore/io.c
+++ b/drivers/net/wireless/ti/wlcore/io.c
@@ -175,12 +175,13 @@ int wlcore_set_partition(struct wl1271 *wl,
if (ret < 0)
goto out;
- /*
- * We don't need the size of the last partition, as it is
- * automatically calculated based on the total memory size and
- * the sizes of the previous partitions.
- */
ret = wlcore_raw_write32(wl, HW_PART3_START_ADDR, p->mem3.start);
+ if (ret < 0)
+ goto out;
+
+ ret = wlcore_raw_write32(wl, HW_PART3_SIZE_ADDR, p->mem3.size);
+ if (ret < 0)
+ goto out;
out:
return ret;
diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h
index 0305729d0986..6c257b54f415 100644
--- a/drivers/net/wireless/ti/wlcore/io.h
+++ b/drivers/net/wireless/ti/wlcore/io.h
@@ -36,8 +36,8 @@
#define HW_PART1_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 12)
#define HW_PART2_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR + 16)
#define HW_PART2_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 20)
-#define HW_PART3_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 24)
-
+#define HW_PART3_SIZE_ADDR (HW_PARTITION_REGISTERS_ADDR + 24)
+#define HW_PART3_START_ADDR (HW_PARTITION_REGISTERS_ADDR + 28)
#define HW_ACCESS_REGISTER_SIZE 4
#define HW_ACCESS_PRAM_MAX_RANGE 0x3c000
@@ -207,19 +207,23 @@ static inline int __must_check wlcore_write_reg(struct wl1271 *wl, int reg,
static inline void wl1271_power_off(struct wl1271 *wl)
{
- int ret;
+ int ret = 0;
if (!test_bit(WL1271_FLAG_GPIO_POWER, &wl->flags))
return;
- ret = wl->if_ops->power(wl->dev, false);
+ if (wl->if_ops->power)
+ ret = wl->if_ops->power(wl->dev, false);
if (!ret)
clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
}
static inline int wl1271_power_on(struct wl1271 *wl)
{
- int ret = wl->if_ops->power(wl->dev, true);
+ int ret = 0;
+
+ if (wl->if_ops->power)
+ ret = wl->if_ops->power(wl->dev, true);
if (ret == 0)
set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index ec7f6af3fab2..d1109c4f0f0d 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -1,4 +1,3 @@
-
/*
* This file is part of wlcore
*
@@ -303,25 +302,11 @@ out:
static void wlcore_adjust_conf(struct wl1271 *wl)
{
- /* Adjust settings according to optional module parameters */
-
- /* Firmware Logger params */
- if (fwlog_mem_blocks != -1) {
- if (fwlog_mem_blocks >= CONF_FWLOG_MIN_MEM_BLOCKS &&
- fwlog_mem_blocks <= CONF_FWLOG_MAX_MEM_BLOCKS) {
- wl->conf.fwlog.mem_blocks = fwlog_mem_blocks;
- } else {
- wl1271_error(
- "Illegal fwlog_mem_blocks=%d using default %d",
- fwlog_mem_blocks, wl->conf.fwlog.mem_blocks);
- }
- }
if (fwlog_param) {
if (!strcmp(fwlog_param, "continuous")) {
wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
- } else if (!strcmp(fwlog_param, "ondemand")) {
- wl->conf.fwlog.mode = WL12XX_FWLOG_ON_DEMAND;
+ wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_HOST;
} else if (!strcmp(fwlog_param, "dbgpins")) {
wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
wl->conf.fwlog.output = WL12XX_FWLOG_OUTPUT_DBG_PINS;
@@ -825,91 +810,32 @@ size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
{
- struct wlcore_partition_set part, old_part;
- u32 addr;
- u32 offset;
- u32 end_of_log;
- u8 *block;
- int ret;
+ u32 end_of_log = 0;
- if ((wl->quirks & WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED) ||
- (wl->conf.fwlog.mem_blocks == 0))
+ if (wl->quirks & WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED)
return;
wl1271_info("Reading FW panic log");
- block = kmalloc(wl->fw_mem_block_size, GFP_KERNEL);
- if (!block)
- return;
-
/*
* Make sure the chip is awake and the logger isn't active.
* Do not send a stop fwlog command if the fw is hanged or if
* dbgpins are used (due to some fw bug).
*/
if (wl1271_ps_elp_wakeup(wl))
- goto out;
+ return;
if (!wl->watchdog_recovery &&
wl->conf.fwlog.output != WL12XX_FWLOG_OUTPUT_DBG_PINS)
wl12xx_cmd_stop_fwlog(wl);
- /* Read the first memory block address */
- ret = wlcore_fw_status(wl, wl->fw_status);
- if (ret < 0)
- goto out;
-
- addr = wl->fw_status->log_start_addr;
- if (!addr)
- goto out;
-
- if (wl->conf.fwlog.mode == WL12XX_FWLOG_CONTINUOUS) {
- offset = sizeof(addr) + sizeof(struct wl1271_rx_descriptor);
- end_of_log = wl->fwlog_end;
- } else {
- offset = sizeof(addr);
- end_of_log = addr;
- }
-
- old_part = wl->curr_part;
- memset(&part, 0, sizeof(part));
-
/* Traverse the memory blocks linked list */
do {
- part.mem.start = wlcore_hw_convert_hwaddr(wl, addr);
- part.mem.size = PAGE_SIZE;
-
- ret = wlcore_set_partition(wl, &part);
- if (ret < 0) {
- wl1271_error("%s: set_partition start=0x%X size=%d",
- __func__, part.mem.start, part.mem.size);
- goto out;
+ end_of_log = wlcore_event_fw_logger(wl);
+ if (end_of_log == 0) {
+ msleep(100);
+ end_of_log = wlcore_event_fw_logger(wl);
}
-
- memset(block, 0, wl->fw_mem_block_size);
- ret = wlcore_read_hwaddr(wl, addr, block,
- wl->fw_mem_block_size, false);
-
- if (ret < 0)
- goto out;
-
- /*
- * Memory blocks are linked to one another. The first 4 bytes
- * of each memory block hold the hardware address of the next
- * one. The last memory block points to the first one in
- * on demand mode and is equal to 0x2000000 in continuous mode.
- */
- addr = le32_to_cpup((__le32 *)block);
-
- if (!wl12xx_copy_fwlog(wl, block + offset,
- wl->fw_mem_block_size - offset))
- break;
- } while (addr && (addr != end_of_log));
-
- wake_up_interruptible(&wl->fwlog_waitq);
-
-out:
- kfree(block);
- wlcore_set_partition(wl, &old_part);
+ } while (end_of_log != 0);
}
static void wlcore_save_freed_pkts(struct wl1271 *wl, struct wl12xx_vif *wlvif,
@@ -6291,7 +6217,6 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
wl->active_sta_count = 0;
wl->active_link_count = 0;
wl->fwlog_size = 0;
- init_waitqueue_head(&wl->fwlog_waitq);
/* The system link is always allocated */
__set_bit(WL12XX_SYSTEM_HLID, wl->links_map);
@@ -6377,7 +6302,6 @@ int wlcore_free_hw(struct wl1271 *wl)
/* Unblock any fwlog readers */
mutex_lock(&wl->mutex);
wl->fwlog_size = -1;
- wake_up_interruptible_all(&wl->fwlog_waitq);
mutex_unlock(&wl->mutex);
wlcore_sysfs_free(wl);
@@ -6584,7 +6508,7 @@ MODULE_PARM_DESC(debug_level, "wl12xx debugging level");
module_param_named(fwlog, fwlog_param, charp, 0);
MODULE_PARM_DESC(fwlog,
- "FW logger options: continuous, ondemand, dbgpins or disable");
+ "FW logger options: continuous, dbgpins or disable");
module_param(fwlog_mem_blocks, int, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(fwlog_mem_blocks, "fwlog mem_blocks");
diff --git a/drivers/net/wireless/ti/wlcore/rx.c b/drivers/net/wireless/ti/wlcore/rx.c
index 5b2927391d1c..34e7e938ede4 100644
--- a/drivers/net/wireless/ti/wlcore/rx.c
+++ b/drivers/net/wireless/ti/wlcore/rx.c
@@ -149,7 +149,6 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
if (desc->packet_class == WL12XX_RX_CLASS_LOGGER) {
size_t len = length - sizeof(*desc);
wl12xx_copy_fwlog(wl, data + sizeof(*desc), len);
- wake_up_interruptible(&wl->fwlog_waitq);
return 0;
}
diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c
index 236b41090827..44f059f7f34e 100644
--- a/drivers/net/wireless/ti/wlcore/spi.c
+++ b/drivers/net/wireless/ti/wlcore/spi.c
@@ -73,7 +73,10 @@
*/
#define SPI_AGGR_BUFFER_SIZE (4 * PAGE_SIZE)
-#define WSPI_MAX_NUM_OF_CHUNKS (SPI_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE)
+/* Maximum number of SPI write chunks */
+#define WSPI_MAX_NUM_OF_CHUNKS \
+ ((SPI_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE) + 1)
+
struct wl12xx_spi_glue {
struct device *dev;
@@ -268,9 +271,10 @@ static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
void *buf, size_t len, bool fixed)
{
struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
- struct spi_transfer t[2 * (WSPI_MAX_NUM_OF_CHUNKS + 1)];
+ /* SPI write buffers - 2 for each chunk */
+ struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS];
struct spi_message m;
- u32 commands[WSPI_MAX_NUM_OF_CHUNKS];
+ u32 commands[WSPI_MAX_NUM_OF_CHUNKS]; /* 1 command per chunk */
u32 *cmd;
u32 chunk_len;
int i;
diff --git a/drivers/net/wireless/ti/wlcore/sysfs.c b/drivers/net/wireless/ti/wlcore/sysfs.c
index 24dd288d6809..a9218e5b0efc 100644
--- a/drivers/net/wireless/ti/wlcore/sysfs.c
+++ b/drivers/net/wireless/ti/wlcore/sysfs.c
@@ -119,32 +119,6 @@ static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
if (ret < 0)
return -ERESTARTSYS;
- /* Let only one thread read the log at a time, blocking others */
- while (wl->fwlog_size == 0) {
- DEFINE_WAIT(wait);
-
- prepare_to_wait_exclusive(&wl->fwlog_waitq,
- &wait,
- TASK_INTERRUPTIBLE);
-
- if (wl->fwlog_size != 0) {
- finish_wait(&wl->fwlog_waitq, &wait);
- break;
- }
-
- mutex_unlock(&wl->mutex);
-
- schedule();
- finish_wait(&wl->fwlog_waitq, &wait);
-
- if (signal_pending(current))
- return -ERESTARTSYS;
-
- ret = mutex_lock_interruptible(&wl->mutex);
- if (ret < 0)
- return -ERESTARTSYS;
- }
-
/* Check if the fwlog is still valid */
if (wl->fwlog_size < 0) {
mutex_unlock(&wl->mutex);
diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h
index 906be6aa4eb6..dda01b118c26 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore.h
@@ -310,9 +310,6 @@ struct wl1271 {
/* FW memory block size */
u32 fw_mem_block_size;
- /* Sysfs FW log entry readers wait queue */
- wait_queue_head_t fwlog_waitq;
-
/* Hardware recovery work */
struct work_struct recovery_work;
bool watchdog_recovery;
diff --git a/drivers/net/wireless/zydas/Kconfig b/drivers/net/wireless/zydas/Kconfig
new file mode 100644
index 000000000000..a58c0f65e376
--- /dev/null
+++ b/drivers/net/wireless/zydas/Kconfig
@@ -0,0 +1,35 @@
+config WLAN_VENDOR_ZYDAS
+ bool "ZyDAS devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_ZYDAS
+
+config USB_ZD1201
+ tristate "USB ZD1201 based Wireless device support"
+ depends on CFG80211 && USB
+ select WIRELESS_EXT
+ select WEXT_PRIV
+ select FW_LOADER
+ ---help---
+ Say Y if you want to use wireless LAN adapters based on the ZyDAS
+ ZD1201 chip.
+
+ This driver makes the adapter appear as a normal Ethernet interface,
+ typically on wlan0.
+
+ The zd1201 device requires external firmware to be loaded.
+ This can be found at http://linux-lc100020.sourceforge.net/
+
+ To compile this driver as a module, choose M here: the
+ module will be called zd1201.
+
+source "drivers/net/wireless/zydas/zd1211rw/Kconfig"
+
+endif # WLAN_VENDOR_ZYDAS
diff --git a/drivers/net/wireless/zydas/Makefile b/drivers/net/wireless/zydas/Makefile
new file mode 100644
index 000000000000..679fbbf3a6cd
--- /dev/null
+++ b/drivers/net/wireless/zydas/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_ZD1211RW) += zd1211rw/
+
+obj-$(CONFIG_USB_ZD1201) += zd1201.o
diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zydas/zd1201.c
index 6f5c793a7855..6f5c793a7855 100644
--- a/drivers/net/wireless/zd1201.c
+++ b/drivers/net/wireless/zydas/zd1201.c
diff --git a/drivers/net/wireless/zd1201.h b/drivers/net/wireless/zydas/zd1201.h
index dd7ea1f35bef..dd7ea1f35bef 100644
--- a/drivers/net/wireless/zd1201.h
+++ b/drivers/net/wireless/zydas/zd1201.h
diff --git a/drivers/net/wireless/zd1211rw/Kconfig b/drivers/net/wireless/zydas/zd1211rw/Kconfig
index 95920581860a..95920581860a 100644
--- a/drivers/net/wireless/zd1211rw/Kconfig
+++ b/drivers/net/wireless/zydas/zd1211rw/Kconfig
diff --git a/drivers/net/wireless/zd1211rw/Makefile b/drivers/net/wireless/zydas/zd1211rw/Makefile
index 5728a918e508..5728a918e508 100644
--- a/drivers/net/wireless/zd1211rw/Makefile
+++ b/drivers/net/wireless/zydas/zd1211rw/Makefile
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zydas/zd1211rw/zd_chip.c
index 07b94eda9604..07b94eda9604 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_chip.c
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zydas/zd1211rw/zd_chip.h
index b03786c9f3aa..b03786c9f3aa 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.h
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_chip.h
diff --git a/drivers/net/wireless/zd1211rw/zd_def.h b/drivers/net/wireless/zydas/zd1211rw/zd_def.h
index 41bd755bc135..41bd755bc135 100644
--- a/drivers/net/wireless/zd1211rw/zd_def.h
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_def.h
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c
index e539d9b1b562..e539d9b1b562 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zydas/zd1211rw/zd_mac.h
index 5a484235308f..5a484235308f 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.h
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.h
diff --git a/drivers/net/wireless/zd1211rw/zd_rf.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf.c
index dc179c414518..dc179c414518 100644
--- a/drivers/net/wireless/zd1211rw/zd_rf.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf.c
diff --git a/drivers/net/wireless/zd1211rw/zd_rf.h b/drivers/net/wireless/zydas/zd1211rw/zd_rf.h
index 8f14e25e1041..8f14e25e1041 100644
--- a/drivers/net/wireless/zd1211rw/zd_rf.h
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf.h
diff --git a/drivers/net/wireless/zd1211rw/zd_rf_al2230.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf_al2230.c
index 99aed7d78952..99aed7d78952 100644
--- a/drivers/net/wireless/zd1211rw/zd_rf_al2230.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf_al2230.c
diff --git a/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf_al7230b.c
index 5fea485be574..5fea485be574 100644
--- a/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf_al7230b.c
diff --git a/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf_rf2959.c
index a93f657a41c7..a93f657a41c7 100644
--- a/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf_rf2959.c
diff --git a/drivers/net/wireless/zd1211rw/zd_rf_uw2453.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf_uw2453.c
index 61b924027356..61b924027356 100644
--- a/drivers/net/wireless/zd1211rw/zd_rf_uw2453.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf_uw2453.c
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c
index a912dc051111..a912dc051111 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.h b/drivers/net/wireless/zydas/zd1211rw/zd_usb.h
index a9075f225178..a9075f225178 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.h
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_usb.h
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
index e481f3710bd3..1049c34e7d43 100644
--- a/drivers/net/xen-netback/netback.c
+++ b/drivers/net/xen-netback/netback.c
@@ -258,18 +258,18 @@ static struct xenvif_rx_meta *get_next_rx_buffer(struct xenvif_queue *queue,
struct netrx_pending_operations *npo)
{
struct xenvif_rx_meta *meta;
- struct xen_netif_rx_request *req;
+ struct xen_netif_rx_request req;
- req = RING_GET_REQUEST(&queue->rx, queue->rx.req_cons++);
+ RING_COPY_REQUEST(&queue->rx, queue->rx.req_cons++, &req);
meta = npo->meta + npo->meta_prod++;
meta->gso_type = XEN_NETIF_GSO_TYPE_NONE;
meta->gso_size = 0;
meta->size = 0;
- meta->id = req->id;
+ meta->id = req.id;
npo->copy_off = 0;
- npo->copy_gref = req->gref;
+ npo->copy_gref = req.gref;
return meta;
}
@@ -424,7 +424,7 @@ static int xenvif_gop_skb(struct sk_buff *skb,
struct xenvif *vif = netdev_priv(skb->dev);
int nr_frags = skb_shinfo(skb)->nr_frags;
int i;
- struct xen_netif_rx_request *req;
+ struct xen_netif_rx_request req;
struct xenvif_rx_meta *meta;
unsigned char *data;
int head = 1;
@@ -443,15 +443,15 @@ static int xenvif_gop_skb(struct sk_buff *skb,
/* Set up a GSO prefix descriptor, if necessary */
if ((1 << gso_type) & vif->gso_prefix_mask) {
- req = RING_GET_REQUEST(&queue->rx, queue->rx.req_cons++);
+ RING_COPY_REQUEST(&queue->rx, queue->rx.req_cons++, &req);
meta = npo->meta + npo->meta_prod++;
meta->gso_type = gso_type;
meta->gso_size = skb_shinfo(skb)->gso_size;
meta->size = 0;
- meta->id = req->id;
+ meta->id = req.id;
}
- req = RING_GET_REQUEST(&queue->rx, queue->rx.req_cons++);
+ RING_COPY_REQUEST(&queue->rx, queue->rx.req_cons++, &req);
meta = npo->meta + npo->meta_prod++;
if ((1 << gso_type) & vif->gso_mask) {
@@ -463,9 +463,9 @@ static int xenvif_gop_skb(struct sk_buff *skb,
}
meta->size = 0;
- meta->id = req->id;
+ meta->id = req.id;
npo->copy_off = 0;
- npo->copy_gref = req->gref;
+ npo->copy_gref = req.gref;
data = skb->data;
while (data < skb_tail_pointer(skb)) {
@@ -679,9 +679,7 @@ static void tx_add_credit(struct xenvif_queue *queue)
* Allow a burst big enough to transmit a jumbo packet of up to 128kB.
* Otherwise the interface can seize up due to insufficient credit.
*/
- max_burst = RING_GET_REQUEST(&queue->tx, queue->tx.req_cons)->size;
- max_burst = min(max_burst, 131072UL);
- max_burst = max(max_burst, queue->credit_bytes);
+ max_burst = max(131072UL, queue->credit_bytes);
/* Take care that adding a new chunk of credit doesn't wrap to zero. */
max_credit = queue->remaining_credit + queue->credit_bytes;
@@ -711,7 +709,7 @@ static void xenvif_tx_err(struct xenvif_queue *queue,
spin_unlock_irqrestore(&queue->response_lock, flags);
if (cons == end)
break;
- txp = RING_GET_REQUEST(&queue->tx, cons++);
+ RING_COPY_REQUEST(&queue->tx, cons++, txp);
} while (1);
queue->tx.req_cons = cons;
}
@@ -778,8 +776,7 @@ static int xenvif_count_requests(struct xenvif_queue *queue,
if (drop_err)
txp = &dropped_tx;
- memcpy(txp, RING_GET_REQUEST(&queue->tx, cons + slots),
- sizeof(*txp));
+ RING_COPY_REQUEST(&queue->tx, cons + slots, txp);
/* If the guest submitted a frame >= 64 KiB then
* first->size overflowed and following slots will
@@ -1112,8 +1109,7 @@ static int xenvif_get_extras(struct xenvif_queue *queue,
return -EBADR;
}
- memcpy(&extra, RING_GET_REQUEST(&queue->tx, cons),
- sizeof(extra));
+ RING_COPY_REQUEST(&queue->tx, cons, &extra);
if (unlikely(!extra.type ||
extra.type >= XEN_NETIF_EXTRA_TYPE_MAX)) {
queue->tx.req_cons = ++cons;
@@ -1322,7 +1318,7 @@ static void xenvif_tx_build_gops(struct xenvif_queue *queue,
idx = queue->tx.req_cons;
rmb(); /* Ensure that we see the request before we copy it. */
- memcpy(&txreq, RING_GET_REQUEST(&queue->tx, idx), sizeof(txreq));
+ RING_COPY_REQUEST(&queue->tx, idx, &txreq);
/* Credit-based scheduling. */
if (txreq.size > queue->remaining_credit &&
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig
index 0d6003dee3af..7437c9dfd8fc 100644
--- a/drivers/nfc/Kconfig
+++ b/drivers/nfc/Kconfig
@@ -76,4 +76,5 @@ source "drivers/nfc/st21nfca/Kconfig"
source "drivers/nfc/st-nci/Kconfig"
source "drivers/nfc/nxp-nci/Kconfig"
source "drivers/nfc/s3fwrn5/Kconfig"
+source "drivers/nfc/st95hf/Kconfig"
endmenu
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile
index e3621416a48e..0a99e67daa10 100644
--- a/drivers/nfc/Makefile
+++ b/drivers/nfc/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_NFC_ST21NFCA) += st21nfca/
obj-$(CONFIG_NFC_ST_NCI) += st-nci/
obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci/
obj-$(CONFIG_NFC_S3FWRN5) += s3fwrn5/
+obj-$(CONFIG_NFC_ST95HF) += st95hf/
diff --git a/drivers/nfc/fdp/i2c.c b/drivers/nfc/fdp/i2c.c
index 532db28145c7..5e797d5c38ed 100644
--- a/drivers/nfc/fdp/i2c.c
+++ b/drivers/nfc/fdp/i2c.c
@@ -298,6 +298,12 @@ static int fdp_nci_i2c_probe(struct i2c_client *client,
return -ENODEV;
}
+ /* Checking if we have an irq */
+ if (client->irq <= 0) {
+ nfc_err(dev, "IRQ not present\n");
+ return -ENODEV;
+ }
+
phy = devm_kzalloc(dev, sizeof(struct fdp_i2c_phy),
GFP_KERNEL);
if (!phy)
@@ -307,12 +313,6 @@ static int fdp_nci_i2c_probe(struct i2c_client *client,
phy->next_read_size = FDP_NCI_I2C_MIN_PAYLOAD;
i2c_set_clientdata(client, phy);
- /* Checking if we have an irq */
- if (client->irq <= 0) {
- dev_err(dev, "IRQ not present\n");
- return -ENODEV;
- }
-
r = request_threaded_irq(client->irq, NULL, fdp_nci_i2c_irq_thread_fn,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
FDP_I2C_DRIVER_NAME, phy);
diff --git a/drivers/nfc/microread/i2c.c b/drivers/nfc/microread/i2c.c
index daf352597ef8..918e8f2eac47 100644
--- a/drivers/nfc/microread/i2c.c
+++ b/drivers/nfc/microread/i2c.c
@@ -50,8 +50,6 @@ struct microread_i2c_phy {
struct i2c_client *i2c_dev;
struct nfc_hci_dev *hdev;
- int irq;
-
int hard_fault; /*
* < 0 if hardware error occured (e.g. i2c err)
* and prevents normal operation.
diff --git a/drivers/nfc/nfcsim.c b/drivers/nfc/nfcsim.c
index 26ac9e5fa1ab..93aaca586858 100644
--- a/drivers/nfc/nfcsim.c
+++ b/drivers/nfc/nfcsim.c
@@ -32,6 +32,8 @@
#define NFCSIM_POLL_TARGET 2
#define NFCSIM_POLL_DUAL (NFCSIM_POLL_INITIATOR | NFCSIM_POLL_TARGET)
+#define RX_DEFAULT_DELAY 5
+
struct nfcsim {
struct nfc_dev *nfc_dev;
@@ -51,6 +53,8 @@ struct nfcsim {
u8 initiator;
+ u32 rx_delay;
+
data_exchange_cb_t cb;
void *cb_context;
@@ -320,10 +324,9 @@ static int nfcsim_tx(struct nfc_dev *nfc_dev, struct nfc_target *target,
* If packet transmission occurs immediately between them, we have a
* non-stop flow of several tens of thousands SYMM packets per second
* and a burning cpu.
- *
- * TODO: Add support for a sysfs entry to control this delay.
*/
- queue_delayed_work(wq, &peer->recv_work, msecs_to_jiffies(5));
+ queue_delayed_work(wq, &peer->recv_work,
+ msecs_to_jiffies(dev->rx_delay));
mutex_unlock(&peer->lock);
@@ -461,6 +464,7 @@ static struct nfcsim *nfcsim_init_dev(void)
if (rc)
goto free_nfc_dev;
+ dev->rx_delay = RX_DEFAULT_DELAY;
return dev;
free_nfc_dev:
diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c
index df4333c7ee0f..11520f472f98 100644
--- a/drivers/nfc/nxp-nci/i2c.c
+++ b/drivers/nfc/nxp-nci/i2c.c
@@ -52,7 +52,6 @@ struct nxp_nci_i2c_phy {
unsigned int gpio_en;
unsigned int gpio_fw;
- unsigned int gpio_irq;
int hard_fault; /*
* < 0 if hardware error occurred (e.g. i2c err)
@@ -85,7 +84,7 @@ static int nxp_nci_i2c_write(void *phy_id, struct sk_buff *skb)
return phy->hard_fault;
r = i2c_master_send(client, skb->data, skb->len);
- if (r == -EREMOTEIO) {
+ if (r < 0) {
/* Retry, chip was in standby */
usleep_range(110000, 120000);
r = i2c_master_send(client, skb->data, skb->len);
@@ -264,8 +263,6 @@ exit_irq_none:
return IRQ_NONE;
}
-#ifdef CONFIG_OF
-
static int nxp_nci_i2c_parse_devtree(struct i2c_client *client)
{
struct nxp_nci_i2c_phy *phy = i2c_get_clientdata(client);
@@ -294,48 +291,24 @@ static int nxp_nci_i2c_parse_devtree(struct i2c_client *client)
}
phy->gpio_fw = r;
- r = irq_of_parse_and_map(pp, 0);
- if (r < 0) {
- nfc_err(&client->dev, "Unable to get irq, error: %d\n", r);
- return r;
- }
- client->irq = r;
-
return 0;
}
-#else
-
-static int nxp_nci_i2c_parse_devtree(struct i2c_client *client)
-{
- return -ENODEV;
-}
-
-#endif
-
static int nxp_nci_i2c_acpi_config(struct nxp_nci_i2c_phy *phy)
{
struct i2c_client *client = phy->i2c_dev;
- struct gpio_desc *gpiod_en, *gpiod_fw, *gpiod_irq;
+ struct gpio_desc *gpiod_en, *gpiod_fw;
gpiod_en = devm_gpiod_get_index(&client->dev, NULL, 2, GPIOD_OUT_LOW);
gpiod_fw = devm_gpiod_get_index(&client->dev, NULL, 1, GPIOD_OUT_LOW);
- gpiod_irq = devm_gpiod_get_index(&client->dev, NULL, 0, GPIOD_IN);
- if (IS_ERR(gpiod_en) || IS_ERR(gpiod_fw) || IS_ERR(gpiod_irq)) {
+ if (IS_ERR(gpiod_en) || IS_ERR(gpiod_fw)) {
nfc_err(&client->dev, "No GPIOs\n");
return -EINVAL;
}
- client->irq = gpiod_to_irq(gpiod_irq);
- if (client->irq < 0) {
- nfc_err(&client->dev, "No IRQ\n");
- return -EINVAL;
- }
-
phy->gpio_en = desc_to_gpio(gpiod_en);
phy->gpio_fw = desc_to_gpio(gpiod_fw);
- phy->gpio_irq = desc_to_gpio(gpiod_irq);
return 0;
}
@@ -374,7 +347,6 @@ static int nxp_nci_i2c_probe(struct i2c_client *client,
} else if (pdata) {
phy->gpio_en = pdata->gpio_en;
phy->gpio_fw = pdata->gpio_fw;
- client->irq = pdata->irq;
} else if (ACPI_HANDLE(&client->dev)) {
r = nxp_nci_i2c_acpi_config(phy);
if (r < 0)
diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c
index fa75c53f3fa5..76c318444304 100644
--- a/drivers/nfc/pn544/i2c.c
+++ b/drivers/nfc/pn544/i2c.c
@@ -166,7 +166,6 @@ struct pn544_i2c_phy {
struct nfc_hci_dev *hdev;
unsigned int gpio_en;
- unsigned int gpio_irq;
unsigned int gpio_fw;
unsigned int en_polarity;
@@ -879,9 +878,8 @@ static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client)
{
struct pn544_i2c_phy *phy = i2c_get_clientdata(client);
const struct acpi_device_id *id;
- struct gpio_desc *gpiod_en, *gpiod_irq, *gpiod_fw;
+ struct gpio_desc *gpiod_en, *gpiod_fw;
struct device *dev;
- int ret;
if (!client)
return -EINVAL;
@@ -914,32 +912,9 @@ static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client)
phy->gpio_fw = desc_to_gpio(gpiod_fw);
- /* Get IRQ GPIO */
- gpiod_irq = devm_gpiod_get_index(dev, PN544_GPIO_NAME_IRQ, 0,
- GPIOD_IN);
- if (IS_ERR(gpiod_irq)) {
- nfc_err(dev, "Unable to get IRQ GPIO\n");
- return -ENODEV;
- }
-
- phy->gpio_irq = desc_to_gpio(gpiod_irq);
-
- /* Map the pin to an IRQ */
- ret = gpiod_to_irq(gpiod_irq);
- if (ret < 0) {
- nfc_err(dev, "Fail pin IRQ mapping\n");
- return ret;
- }
-
- nfc_info(dev, "GPIO resource, no:%d irq:%d\n",
- desc_to_gpio(gpiod_irq), ret);
- client->irq = ret;
-
return 0;
}
-#ifdef CONFIG_OF
-
static int pn544_hci_i2c_of_request_resources(struct i2c_client *client)
{
struct pn544_i2c_phy *phy = i2c_get_clientdata(client);
@@ -996,15 +971,6 @@ static int pn544_hci_i2c_of_request_resources(struct i2c_client *client)
goto err_gpio_fw;
}
- /* IRQ */
- ret = irq_of_parse_and_map(pp, 0);
- if (ret < 0) {
- nfc_err(&client->dev,
- "Unable to get irq, error: %d\n", ret);
- goto err_gpio_fw;
- }
- client->irq = ret;
-
return 0;
err_gpio_fw:
@@ -1015,15 +981,6 @@ err_dt:
return ret;
}
-#else
-
-static int pn544_hci_i2c_of_request_resources(struct i2c_client *client)
-{
- return -ENODEV;
-}
-
-#endif
-
static int pn544_hci_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -1076,7 +1033,6 @@ static int pn544_hci_i2c_probe(struct i2c_client *client,
phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE);
phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET);
- phy->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ);
/* Using ACPI */
} else if (ACPI_HANDLE(&client->dev)) {
r = pn544_hci_i2c_acpi_request_resources(client);
diff --git a/drivers/nfc/s3fwrn5/core.c b/drivers/nfc/s3fwrn5/core.c
index 0d866ca295e3..9d9c8d57a042 100644
--- a/drivers/nfc/s3fwrn5/core.c
+++ b/drivers/nfc/s3fwrn5/core.c
@@ -147,7 +147,7 @@ static struct nci_ops s3fwrn5_nci_ops = {
};
int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev,
- struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload)
+ const struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload)
{
struct s3fwrn5_info *info;
int ret;
diff --git a/drivers/nfc/s3fwrn5/i2c.c b/drivers/nfc/s3fwrn5/i2c.c
index c61d8a308da4..3ed0adf6479b 100644
--- a/drivers/nfc/s3fwrn5/i2c.c
+++ b/drivers/nfc/s3fwrn5/i2c.c
@@ -125,7 +125,7 @@ static int s3fwrn5_i2c_write(void *phy_id, struct sk_buff *skb)
return 0;
}
-static struct s3fwrn5_phy_ops i2c_phy_ops = {
+static const struct s3fwrn5_phy_ops i2c_phy_ops = {
.set_wake = s3fwrn5_i2c_set_wake,
.set_mode = s3fwrn5_i2c_set_mode,
.get_mode = s3fwrn5_i2c_get_mode,
diff --git a/drivers/nfc/s3fwrn5/s3fwrn5.h b/drivers/nfc/s3fwrn5/s3fwrn5.h
index 89210d4828b8..7d5e516036fb 100644
--- a/drivers/nfc/s3fwrn5/s3fwrn5.h
+++ b/drivers/nfc/s3fwrn5/s3fwrn5.h
@@ -44,7 +44,7 @@ struct s3fwrn5_info {
void *phy_id;
struct device *pdev;
- struct s3fwrn5_phy_ops *phy_ops;
+ const struct s3fwrn5_phy_ops *phy_ops;
unsigned int max_payload;
struct s3fwrn5_fw_info fw_info;
@@ -90,7 +90,7 @@ static inline int s3fwrn5_write(struct s3fwrn5_info *info, struct sk_buff *skb)
}
int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev,
- struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload);
+ const struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload);
void s3fwrn5_remove(struct nci_dev *ndev);
int s3fwrn5_recv_frame(struct nci_dev *ndev, struct sk_buff *skb,
diff --git a/drivers/nfc/st-nci/Kconfig b/drivers/nfc/st-nci/Kconfig
index e7c6db9c5860..dc9b777d78f6 100644
--- a/drivers/nfc/st-nci/Kconfig
+++ b/drivers/nfc/st-nci/Kconfig
@@ -1,19 +1,14 @@
config NFC_ST_NCI
- tristate "STMicroelectronics ST NCI NFC driver"
- depends on NFC_NCI
- default n
+ tristate
---help---
STMicroelectronics NFC NCI chips core driver. It implements the chipset
NCI logic and hooks into the NFC kernel APIs. Physical layers will
register against it.
- To compile this driver as a module, choose m here. The module will
- be called st-nci.
- Say N if unsure.
-
config NFC_ST_NCI_I2C
- tristate "NFC ST NCI i2c support"
- depends on NFC_ST_NCI && I2C
+ tristate "STMicroelectronics ST NCI NFC driver (I2C)"
+ depends on NFC_NCI && I2C
+ select NFC_ST_NCI
---help---
This module adds support for an I2C interface to the
STMicroelectronics NFC NCI chips familly.
@@ -23,8 +18,9 @@ config NFC_ST_NCI_I2C
Say N if unsure.
config NFC_ST_NCI_SPI
- tristate "NFC ST NCI spi support"
- depends on NFC_ST_NCI && SPI
+ tristate "STMicroelectronics ST NCI NFC driver (SPI)"
+ depends on NFC_NCI && SPI
+ select NFC_ST_NCI
---help---
This module adds support for an SPI interface to the
STMicroelectronics NFC NCI chips familly.
diff --git a/drivers/nfc/st-nci/i2c.c b/drivers/nfc/st-nci/i2c.c
index 15e3ce2d274c..8a56b5c6e4c4 100644
--- a/drivers/nfc/st-nci/i2c.c
+++ b/drivers/nfc/st-nci/i2c.c
@@ -20,8 +20,10 @@
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
+#include <linux/acpi.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/nfc.h>
@@ -40,11 +42,7 @@
#define ST_NCI_I2C_DRIVER_NAME "st_nci_i2c"
-static struct i2c_device_id st_nci_i2c_id_table[] = {
- {ST_NCI_DRIVER_NAME, 0},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, st_nci_i2c_id_table);
+#define ST_NCI_GPIO_NAME_RESET "clf_reset"
struct st_nci_i2c_phy {
struct i2c_client *i2c_dev;
@@ -210,7 +208,43 @@ static struct nfc_phy_ops i2c_phy_ops = {
.disable = st_nci_i2c_disable,
};
-#ifdef CONFIG_OF
+static int st_nci_i2c_acpi_request_resources(struct i2c_client *client)
+{
+ struct st_nci_i2c_phy *phy = i2c_get_clientdata(client);
+ const struct acpi_device_id *id;
+ struct gpio_desc *gpiod_reset;
+ struct device *dev;
+
+ if (!client)
+ return -EINVAL;
+
+ dev = &client->dev;
+
+ /* Match the struct device against a given list of ACPI IDs */
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!id)
+ return -ENODEV;
+
+ /* Get RESET GPIO from ACPI */
+ gpiod_reset = devm_gpiod_get_index(dev, ST_NCI_GPIO_NAME_RESET, 1,
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(gpiod_reset)) {
+ nfc_err(dev, "Unable to get RESET GPIO\n");
+ return -ENODEV;
+ }
+
+ phy->gpio_reset = desc_to_gpio(gpiod_reset);
+
+ phy->irq_polarity = irq_get_trigger_type(client->irq);
+
+ phy->se_status.is_ese_present =
+ device_property_present(dev, "ese-present");
+ phy->se_status.is_uicc_present =
+ device_property_present(dev, "uicc-present");
+
+ return 0;
+}
+
static int st_nci_i2c_of_request_resources(struct i2c_client *client)
{
struct st_nci_i2c_phy *phy = i2c_get_clientdata(client);
@@ -232,7 +266,7 @@ static int st_nci_i2c_of_request_resources(struct i2c_client *client)
/* GPIO request and configuration */
r = devm_gpio_request_one(&client->dev, gpio,
- GPIOF_OUT_INIT_HIGH, "clf_reset");
+ GPIOF_OUT_INIT_HIGH, ST_NCI_GPIO_NAME_RESET);
if (r) {
nfc_err(&client->dev, "Failed to request reset pin\n");
return r;
@@ -248,12 +282,6 @@ static int st_nci_i2c_of_request_resources(struct i2c_client *client)
return 0;
}
-#else
-static int st_nci_i2c_of_request_resources(struct i2c_client *client)
-{
- return -ENODEV;
-}
-#endif
static int st_nci_i2c_request_resources(struct i2c_client *client)
{
@@ -272,7 +300,8 @@ static int st_nci_i2c_request_resources(struct i2c_client *client)
phy->irq_polarity = pdata->irq_polarity;
r = devm_gpio_request_one(&client->dev,
- phy->gpio_reset, GPIOF_OUT_INIT_HIGH, "clf_reset");
+ phy->gpio_reset, GPIOF_OUT_INIT_HIGH,
+ ST_NCI_GPIO_NAME_RESET);
if (r) {
pr_err("%s : reset gpio_request failed\n", __FILE__);
return r;
@@ -322,6 +351,12 @@ static int st_nci_i2c_probe(struct i2c_client *client,
"Cannot get platform resources\n");
return r;
}
+ } else if (ACPI_HANDLE(&client->dev)) {
+ r = st_nci_i2c_acpi_request_resources(client);
+ if (r) {
+ nfc_err(&client->dev, "Cannot get ACPI data\n");
+ return r;
+ }
} else {
nfc_err(&client->dev,
"st_nci platform resources not available\n");
@@ -358,7 +393,19 @@ static int st_nci_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_OF
+static struct i2c_device_id st_nci_i2c_id_table[] = {
+ {ST_NCI_DRIVER_NAME, 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, st_nci_i2c_id_table);
+
+static const struct acpi_device_id st_nci_i2c_acpi_match[] = {
+ {"SMO2101"},
+ {"SMO2102"},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, st_nci_i2c_acpi_match);
+
static const struct of_device_id of_st_nci_i2c_match[] = {
{ .compatible = "st,st21nfcb-i2c", },
{ .compatible = "st,st21nfcb_i2c", },
@@ -366,19 +413,18 @@ static const struct of_device_id of_st_nci_i2c_match[] = {
{}
};
MODULE_DEVICE_TABLE(of, of_st_nci_i2c_match);
-#endif
static struct i2c_driver st_nci_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = ST_NCI_I2C_DRIVER_NAME,
.of_match_table = of_match_ptr(of_st_nci_i2c_match),
+ .acpi_match_table = ACPI_PTR(st_nci_i2c_acpi_match),
},
.probe = st_nci_i2c_probe,
.id_table = st_nci_i2c_id_table,
.remove = st_nci_i2c_remove,
};
-
module_i2c_driver(st_nci_i2c_driver);
MODULE_LICENSE("GPL");
diff --git a/drivers/nfc/st-nci/ndlc.c b/drivers/nfc/st-nci/ndlc.c
index 0884b11001ef..50880d747b02 100644
--- a/drivers/nfc/st-nci/ndlc.c
+++ b/drivers/nfc/st-nci/ndlc.c
@@ -20,7 +20,6 @@
#include <net/nfc/nci_core.h>
#include "st-nci.h"
-#include "ndlc.h"
#define NDLC_TIMER_T1 100
#define NDLC_TIMER_T1_WAIT 400
diff --git a/drivers/nfc/st-nci/se.c b/drivers/nfc/st-nci/se.c
index dbab722a0654..a53e5df803eb 100644
--- a/drivers/nfc/st-nci/se.c
+++ b/drivers/nfc/st-nci/se.c
@@ -331,7 +331,7 @@ static int st_nci_hci_connectivity_event_received(struct nci_dev *ndev,
switch (event) {
case ST_NCI_EVT_CONNECTIVITY:
-
+ r = nfc_se_connectivity(ndev->nfc_dev, host);
break;
case ST_NCI_EVT_TRANSACTION:
/* According to specification etsi 102 622
@@ -392,7 +392,6 @@ void st_nci_hci_event_received(struct nci_dev *ndev, u8 pipe,
}
EXPORT_SYMBOL_GPL(st_nci_hci_event_received);
-
void st_nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd,
struct sk_buff *skb)
{
diff --git a/drivers/nfc/st-nci/spi.c b/drivers/nfc/st-nci/spi.c
index d6519bb9dba5..821dfa950fa8 100644
--- a/drivers/nfc/st-nci/spi.c
+++ b/drivers/nfc/st-nci/spi.c
@@ -20,8 +20,10 @@
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
+#include <linux/acpi.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/nfc.h>
@@ -34,18 +36,14 @@
/* ndlc header */
#define ST_NCI_FRAME_HEADROOM 1
-#define ST_NCI_FRAME_TAILROOM 0
+#define ST_NCI_FRAME_TAILROOM 0
#define ST_NCI_SPI_MIN_SIZE 4 /* PCB(1) + NCI Packet header(3) */
#define ST_NCI_SPI_MAX_SIZE 250 /* req 4.2.1 */
#define ST_NCI_SPI_DRIVER_NAME "st_nci_spi"
-static struct spi_device_id st_nci_spi_id_table[] = {
- {ST_NCI_SPI_DRIVER_NAME, 0},
- {}
-};
-MODULE_DEVICE_TABLE(spi, st_nci_spi_id_table);
+#define ST_NCI_GPIO_NAME_RESET "clf_reset"
struct st_nci_spi_phy {
struct spi_device *spi_dev;
@@ -225,7 +223,43 @@ static struct nfc_phy_ops spi_phy_ops = {
.disable = st_nci_spi_disable,
};
-#ifdef CONFIG_OF
+static int st_nci_spi_acpi_request_resources(struct spi_device *spi_dev)
+{
+ struct st_nci_spi_phy *phy = spi_get_drvdata(spi_dev);
+ const struct acpi_device_id *id;
+ struct gpio_desc *gpiod_reset;
+ struct device *dev;
+
+ if (!spi_dev)
+ return -EINVAL;
+
+ dev = &spi_dev->dev;
+
+ /* Match the struct device against a given list of ACPI IDs */
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!id)
+ return -ENODEV;
+
+ /* Get RESET GPIO from ACPI */
+ gpiod_reset = devm_gpiod_get_index(dev, ST_NCI_GPIO_NAME_RESET, 1,
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(gpiod_reset)) {
+ nfc_err(dev, "Unable to get RESET GPIO\n");
+ return -ENODEV;
+ }
+
+ phy->gpio_reset = desc_to_gpio(gpiod_reset);
+
+ phy->irq_polarity = irq_get_trigger_type(spi_dev->irq);
+
+ phy->se_status.is_ese_present =
+ device_property_present(dev, "ese-present");
+ phy->se_status.is_uicc_present =
+ device_property_present(dev, "uicc-present");
+
+ return 0;
+}
+
static int st_nci_spi_of_request_resources(struct spi_device *dev)
{
struct st_nci_spi_phy *phy = spi_get_drvdata(dev);
@@ -247,7 +281,7 @@ static int st_nci_spi_of_request_resources(struct spi_device *dev)
/* GPIO request and configuration */
r = devm_gpio_request_one(&dev->dev, gpio,
- GPIOF_OUT_INIT_HIGH, "clf_reset");
+ GPIOF_OUT_INIT_HIGH, ST_NCI_GPIO_NAME_RESET);
if (r) {
nfc_err(&dev->dev, "Failed to request reset pin\n");
return r;
@@ -263,12 +297,6 @@ static int st_nci_spi_of_request_resources(struct spi_device *dev)
return 0;
}
-#else
-static int st_nci_spi_of_request_resources(struct spi_device *dev)
-{
- return -ENODEV;
-}
-#endif
static int st_nci_spi_request_resources(struct spi_device *dev)
{
@@ -287,7 +315,8 @@ static int st_nci_spi_request_resources(struct spi_device *dev)
phy->irq_polarity = pdata->irq_polarity;
r = devm_gpio_request_one(&dev->dev,
- phy->gpio_reset, GPIOF_OUT_INIT_HIGH, "clf_reset");
+ phy->gpio_reset, GPIOF_OUT_INIT_HIGH,
+ ST_NCI_GPIO_NAME_RESET);
if (r) {
pr_err("%s : reset gpio_request failed\n", __FILE__);
return r;
@@ -338,6 +367,12 @@ static int st_nci_spi_probe(struct spi_device *dev)
"Cannot get platform resources\n");
return r;
}
+ } else if (ACPI_HANDLE(&dev->dev)) {
+ r = st_nci_spi_acpi_request_resources(dev);
+ if (r) {
+ nfc_err(&dev->dev, "Cannot get ACPI data\n");
+ return r;
+ }
} else {
nfc_err(&dev->dev,
"st_nci platform resources not available\n");
@@ -374,24 +409,34 @@ static int st_nci_spi_remove(struct spi_device *dev)
return 0;
}
-#ifdef CONFIG_OF
+static struct spi_device_id st_nci_spi_id_table[] = {
+ {ST_NCI_SPI_DRIVER_NAME, 0},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, st_nci_spi_id_table);
+
+static const struct acpi_device_id st_nci_spi_acpi_match[] = {
+ {"SMO2101", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, st_nci_spi_acpi_match);
+
static const struct of_device_id of_st_nci_spi_match[] = {
{ .compatible = "st,st21nfcb-spi", },
{}
};
MODULE_DEVICE_TABLE(of, of_st_nci_spi_match);
-#endif
static struct spi_driver st_nci_spi_driver = {
.driver = {
.name = ST_NCI_SPI_DRIVER_NAME,
.of_match_table = of_match_ptr(of_st_nci_spi_match),
+ .acpi_match_table = ACPI_PTR(st_nci_spi_acpi_match),
},
.probe = st_nci_spi_probe,
.id_table = st_nci_spi_id_table,
.remove = st_nci_spi_remove,
};
-
module_spi_driver(st_nci_spi_driver);
MODULE_LICENSE("GPL");
diff --git a/drivers/nfc/st21nfca/Kconfig b/drivers/nfc/st21nfca/Kconfig
index ee459f066ade..cc3bd5658901 100644
--- a/drivers/nfc/st21nfca/Kconfig
+++ b/drivers/nfc/st21nfca/Kconfig
@@ -1,20 +1,15 @@
config NFC_ST21NFCA
- tristate "STMicroelectronics ST21NFCA NFC driver"
- depends on NFC_HCI
+ tristate
select CRC_CCITT
- default n
---help---
STMicroelectronics ST21NFCA core driver. It implements the chipset
HCI logic and hooks into the NFC kernel APIs. Physical layers will
register against it.
- To compile this driver as a module, choose m here. The module will
- be called st21nfca.
- Say N if unsure.
-
config NFC_ST21NFCA_I2C
- tristate "NFC ST21NFCA i2c support"
- depends on NFC_ST21NFCA && I2C && NFC_SHDLC
+ tristate "STMicroelectronics ST21NFCA NFC driver (I2C)"
+ depends on NFC_HCI && I2C && NFC_SHDLC
+ select NFC_ST21NFCA
---help---
This module adds support for the STMicroelectronics st21nfca i2c interface.
Select this if your platform is using the i2c bus.
diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c
index a98da33e680a..1f44a151d206 100644
--- a/drivers/nfc/st21nfca/i2c.c
+++ b/drivers/nfc/st21nfca/i2c.c
@@ -21,8 +21,10 @@
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
+#include <linux/acpi.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
@@ -60,12 +62,7 @@
#define ST21NFCA_HCI_I2C_DRIVER_NAME "st21nfca_hci_i2c"
-static struct i2c_device_id st21nfca_hci_i2c_id_table[] = {
- {ST21NFCA_HCI_DRIVER_NAME, 0},
- {}
-};
-
-MODULE_DEVICE_TABLE(i2c, st21nfca_hci_i2c_id_table);
+#define ST21NFCA_GPIO_NAME_EN "clf_enable"
struct st21nfca_i2c_phy {
struct i2c_client *i2c_dev;
@@ -167,7 +164,6 @@ static void st21nfca_hci_i2c_disable(void *phy_id)
{
struct st21nfca_i2c_phy *phy = phy_id;
- pr_info("\n");
gpio_set_value(phy->gpio_ena, 0);
phy->powered = 0;
@@ -210,7 +206,6 @@ static int st21nfca_hci_i2c_write(void *phy_id, struct sk_buff *skb)
I2C_DUMP_SKB("st21nfca_hci_i2c_write", skb);
-
if (phy->hard_fault != 0)
return phy->hard_fault;
@@ -509,7 +504,41 @@ static struct nfc_phy_ops i2c_phy_ops = {
.disable = st21nfca_hci_i2c_disable,
};
-#ifdef CONFIG_OF
+static int st21nfca_hci_i2c_acpi_request_resources(struct i2c_client *client)
+{
+ struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client);
+ const struct acpi_device_id *id;
+ struct gpio_desc *gpiod_ena;
+ struct device *dev;
+
+ if (!client)
+ return -EINVAL;
+
+ dev = &client->dev;
+
+ /* Match the struct device against a given list of ACPI IDs */
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!id)
+ return -ENODEV;
+
+ /* Get EN GPIO from ACPI */
+ gpiod_ena = devm_gpiod_get_index(dev, ST21NFCA_GPIO_NAME_EN, 1,
+ GPIOD_OUT_LOW);
+ if (!IS_ERR(gpiod_ena))
+ phy->gpio_ena = desc_to_gpio(gpiod_ena);
+
+ phy->gpio_ena = desc_to_gpio(gpiod_ena);
+
+ phy->irq_polarity = irq_get_trigger_type(client->irq);
+
+ phy->se_status.is_ese_present =
+ device_property_present(dev, "ese-present");
+ phy->se_status.is_uicc_present =
+ device_property_present(dev, "uicc-present");
+
+ return 0;
+}
+
static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client)
{
struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client);
@@ -530,7 +559,7 @@ static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client)
/* GPIO request and configuration */
r = devm_gpio_request_one(&client->dev, gpio, GPIOF_OUT_INIT_HIGH,
- "clf_enable");
+ ST21NFCA_GPIO_NAME_EN);
if (r) {
nfc_err(&client->dev, "Failed to request enable pin\n");
return r;
@@ -547,12 +576,6 @@ static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client)
return 0;
}
-#else
-static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client)
-{
- return -ENODEV;
-}
-#endif
static int st21nfca_hci_i2c_request_resources(struct i2c_client *client)
{
@@ -572,7 +595,8 @@ static int st21nfca_hci_i2c_request_resources(struct i2c_client *client)
if (phy->gpio_ena > 0) {
r = devm_gpio_request_one(&client->dev, phy->gpio_ena,
- GPIOF_OUT_INIT_HIGH, "clf_enable");
+ GPIOF_OUT_INIT_HIGH,
+ ST21NFCA_GPIO_NAME_EN);
if (r) {
pr_err("%s : ena gpio_request failed\n", __FILE__);
return r;
@@ -628,6 +652,12 @@ static int st21nfca_hci_i2c_probe(struct i2c_client *client,
nfc_err(&client->dev, "Cannot get platform resources\n");
return r;
}
+ } else if (ACPI_HANDLE(&client->dev)) {
+ r = st21nfca_hci_i2c_acpi_request_resources(client);
+ if (r) {
+ nfc_err(&client->dev, "Cannot get ACPI data\n");
+ return r;
+ }
} else {
nfc_err(&client->dev, "st21nfca platform resources not available\n");
return -ENODEV;
@@ -670,26 +700,36 @@ static int st21nfca_hci_i2c_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_OF
+static struct i2c_device_id st21nfca_hci_i2c_id_table[] = {
+ {ST21NFCA_HCI_DRIVER_NAME, 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, st21nfca_hci_i2c_id_table);
+
+static const struct acpi_device_id st21nfca_hci_i2c_acpi_match[] = {
+ {"SMO2100", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, st21nfca_hci_i2c_acpi_match);
+
static const struct of_device_id of_st21nfca_i2c_match[] = {
{ .compatible = "st,st21nfca-i2c", },
{ .compatible = "st,st21nfca_i2c", },
{}
};
MODULE_DEVICE_TABLE(of, of_st21nfca_i2c_match);
-#endif
static struct i2c_driver st21nfca_hci_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = ST21NFCA_HCI_I2C_DRIVER_NAME,
.of_match_table = of_match_ptr(of_st21nfca_i2c_match),
+ .acpi_match_table = ACPI_PTR(st21nfca_hci_i2c_acpi_match),
},
.probe = st21nfca_hci_i2c_probe,
.id_table = st21nfca_hci_i2c_id_table,
.remove = st21nfca_hci_i2c_remove,
};
-
module_i2c_driver(st21nfca_hci_i2c_driver);
MODULE_LICENSE("GPL");
diff --git a/drivers/nfc/st21nfca/se.c b/drivers/nfc/st21nfca/se.c
index c79d99b24c96..bd56a16e4007 100644
--- a/drivers/nfc/st21nfca/se.c
+++ b/drivers/nfc/st21nfca/se.c
@@ -312,7 +312,8 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host,
switch (event) {
case ST21NFCA_EVT_CONNECTIVITY:
- break;
+ r = nfc_se_connectivity(hdev->ndev, host);
+ break;
case ST21NFCA_EVT_TRANSACTION:
/*
* According to specification etsi 102 622
@@ -342,7 +343,7 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host,
transaction->aid_len + 4, transaction->params_len);
r = nfc_se_transaction(hdev->ndev, host, transaction);
- break;
+ break;
default:
nfc_err(&hdev->ndev->dev, "Unexpected event on connectivity gate\n");
return 1;
diff --git a/drivers/nfc/st95hf/Kconfig b/drivers/nfc/st95hf/Kconfig
new file mode 100644
index 000000000000..224f266fdcb6
--- /dev/null
+++ b/drivers/nfc/st95hf/Kconfig
@@ -0,0 +1,10 @@
+config NFC_ST95HF
+ tristate "ST95HF NFC Transceiver driver"
+ depends on SPI && NFC_DIGITAL
+ help
+ This enables the ST NFC driver for ST95HF NFC transceiver.
+ This makes use of SPI framework to communicate with transceiver
+ and registered with NFC digital core to support Linux NFC framework.
+
+ Say Y here to compile support for ST NFC transceiver ST95HF
+ linux driver into the kernel or say M to compile it as module.
diff --git a/drivers/nfc/st95hf/Makefile b/drivers/nfc/st95hf/Makefile
new file mode 100644
index 000000000000..00760b38ab7e
--- /dev/null
+++ b/drivers/nfc/st95hf/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for STMicroelectronics NFC transceiver ST95HF
+#
+
+obj-$(CONFIG_NFC_ST95HF) += st95hf.o
+st95hf-objs := spi.o core.o
diff --git a/drivers/nfc/st95hf/core.c b/drivers/nfc/st95hf/core.c
new file mode 100644
index 000000000000..c2840e412962
--- /dev/null
+++ b/drivers/nfc/st95hf/core.c
@@ -0,0 +1,1273 @@
+/*
+ * --------------------------------------------------------------------
+ * Driver for ST NFC Transceiver ST95HF
+ * --------------------------------------------------------------------
+ * Copyright (C) 2015 STMicroelectronics Pvt. Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/nfc.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/wait.h>
+#include <net/nfc/digital.h>
+#include <net/nfc/nfc.h>
+
+#include "spi.h"
+
+/* supported protocols */
+#define ST95HF_SUPPORTED_PROT (NFC_PROTO_ISO14443_MASK | \
+ NFC_PROTO_ISO14443_B_MASK | \
+ NFC_PROTO_ISO15693_MASK)
+/* driver capabilities */
+#define ST95HF_CAPABILITIES NFC_DIGITAL_DRV_CAPS_IN_CRC
+
+/* Command Send Interface */
+/* ST95HF_COMMAND_SEND CMD Ids */
+#define ECHO_CMD 0x55
+#define WRITE_REGISTER_CMD 0x9
+#define PROTOCOL_SELECT_CMD 0x2
+#define SEND_RECEIVE_CMD 0x4
+
+/* Select protocol codes */
+#define ISO15693_PROTOCOL_CODE 0x1
+#define ISO14443A_PROTOCOL_CODE 0x2
+#define ISO14443B_PROTOCOL_CODE 0x3
+
+/*
+ * head room len is 3
+ * 1 byte for control byte
+ * 1 byte for cmd
+ * 1 byte for size
+ */
+#define ST95HF_HEADROOM_LEN 3
+
+/*
+ * tailroom is 1 for ISO14443A
+ * and 0 for ISO14443B/ISO15693,
+ * hence the max value 1 should be
+ * taken.
+ */
+#define ST95HF_TAILROOM_LEN 1
+
+/* Command Response interface */
+#define MAX_RESPONSE_BUFFER_SIZE 280
+#define ECHORESPONSE 0x55
+#define ST95HF_ERR_MASK 0xF
+#define ST95HF_TIMEOUT_ERROR 0x87
+#define ST95HF_NFCA_CRC_ERR_MASK 0x20
+#define ST95HF_NFCB_CRC_ERR_MASK 0x01
+
+/* ST95HF transmission flag values */
+#define TRFLAG_NFCA_SHORT_FRAME 0x07
+#define TRFLAG_NFCA_STD_FRAME 0x08
+#define TRFLAG_NFCA_STD_FRAME_CRC 0x28
+
+/* Misc defs */
+#define HIGH 1
+#define LOW 0
+#define ISO14443A_RATS_REQ 0xE0
+#define RATS_TB1_PRESENT_MASK 0x20
+#define RATS_TA1_PRESENT_MASK 0x10
+#define TB1_FWI_MASK 0xF0
+#define WTX_REQ_FROM_TAG 0xF2
+
+#define MAX_CMD_LEN 0x7
+
+#define MAX_CMD_PARAMS 4
+struct cmd {
+ int cmd_len;
+ unsigned char cmd_id;
+ unsigned char no_cmd_params;
+ unsigned char cmd_params[MAX_CMD_PARAMS];
+ enum req_type req;
+};
+
+struct param_list {
+ int param_offset;
+ int new_param_val;
+};
+
+/*
+ * List of top-level cmds to be used internally by the driver.
+ * All these commands are build on top of ST95HF basic commands
+ * such as SEND_RECEIVE_CMD, PROTOCOL_SELECT_CMD, etc.
+ * These top level cmds are used internally while implementing various ops of
+ * digital layer/driver probe or extending the digital framework layer for
+ * features that are not yet implemented there, for example, WTX cmd handling.
+ */
+enum st95hf_cmd_list {
+ CMD_ECHO,
+ CMD_ISO14443A_CONFIG,
+ CMD_ISO14443A_DEMOGAIN,
+ CMD_ISO14443B_DEMOGAIN,
+ CMD_ISO14443A_PROTOCOL_SELECT,
+ CMD_ISO14443B_PROTOCOL_SELECT,
+ CMD_WTX_RESPONSE,
+ CMD_FIELD_OFF,
+ CMD_ISO15693_PROTOCOL_SELECT,
+};
+
+static const struct cmd cmd_array[] = {
+ [CMD_ECHO] = {
+ .cmd_len = 0x2,
+ .cmd_id = ECHO_CMD,
+ .no_cmd_params = 0,
+ .req = SYNC,
+ },
+ [CMD_ISO14443A_CONFIG] = {
+ .cmd_len = 0x7,
+ .cmd_id = WRITE_REGISTER_CMD,
+ .no_cmd_params = 0x4,
+ .cmd_params = {0x3A, 0x00, 0x5A, 0x04},
+ .req = SYNC,
+ },
+ [CMD_ISO14443A_DEMOGAIN] = {
+ .cmd_len = 0x7,
+ .cmd_id = WRITE_REGISTER_CMD,
+ .no_cmd_params = 0x4,
+ .cmd_params = {0x68, 0x01, 0x01, 0xDF},
+ .req = SYNC,
+ },
+ [CMD_ISO14443B_DEMOGAIN] = {
+ .cmd_len = 0x7,
+ .cmd_id = WRITE_REGISTER_CMD,
+ .no_cmd_params = 0x4,
+ .cmd_params = {0x68, 0x01, 0x01, 0x51},
+ .req = SYNC,
+ },
+ [CMD_ISO14443A_PROTOCOL_SELECT] = {
+ .cmd_len = 0x7,
+ .cmd_id = PROTOCOL_SELECT_CMD,
+ .no_cmd_params = 0x4,
+ .cmd_params = {ISO14443A_PROTOCOL_CODE, 0x00, 0x01, 0xA0},
+ .req = SYNC,
+ },
+ [CMD_ISO14443B_PROTOCOL_SELECT] = {
+ .cmd_len = 0x7,
+ .cmd_id = PROTOCOL_SELECT_CMD,
+ .no_cmd_params = 0x4,
+ .cmd_params = {ISO14443B_PROTOCOL_CODE, 0x01, 0x03, 0xFF},
+ .req = SYNC,
+ },
+ [CMD_WTX_RESPONSE] = {
+ .cmd_len = 0x6,
+ .cmd_id = SEND_RECEIVE_CMD,
+ .no_cmd_params = 0x3,
+ .cmd_params = {0xF2, 0x00, TRFLAG_NFCA_STD_FRAME_CRC},
+ .req = ASYNC,
+ },
+ [CMD_FIELD_OFF] = {
+ .cmd_len = 0x5,
+ .cmd_id = PROTOCOL_SELECT_CMD,
+ .no_cmd_params = 0x2,
+ .cmd_params = {0x0, 0x0},
+ .req = SYNC,
+ },
+ [CMD_ISO15693_PROTOCOL_SELECT] = {
+ .cmd_len = 0x5,
+ .cmd_id = PROTOCOL_SELECT_CMD,
+ .no_cmd_params = 0x2,
+ .cmd_params = {ISO15693_PROTOCOL_CODE, 0x0D},
+ .req = SYNC,
+ },
+};
+
+/* st95_digital_cmd_complete_arg stores client context */
+struct st95_digital_cmd_complete_arg {
+ struct sk_buff *skb_resp;
+ nfc_digital_cmd_complete_t complete_cb;
+ void *cb_usrarg;
+ bool rats;
+};
+
+/*
+ * structure containing ST95HF driver specific data.
+ * @spicontext: structure containing information required
+ * for spi communication between st95hf and host.
+ * @ddev: nfc digital device object.
+ * @nfcdev: nfc device object.
+ * @enable_gpio: gpio used to enable st95hf transceiver.
+ * @complete_cb_arg: structure to store various context information
+ * that is passed from nfc requesting thread to the threaded ISR.
+ * @st95hf_supply: regulator "consumer" for NFC device.
+ * @sendrcv_trflag: last byte of frame send by sendrecv command
+ * of st95hf. This byte contains transmission flag info.
+ * @exchange_lock: semaphore used for signaling the st95hf_remove
+ * function that the last outstanding async nfc request is finished.
+ * @rm_lock: mutex for ensuring safe access of nfc digital object
+ * from threaded ISR. Usage of this mutex avoids any race between
+ * deletion of the object from st95hf_remove() and its access from
+ * the threaded ISR.
+ * @nfcdev_free: flag to have the state of nfc device object.
+ * [alive | died]
+ * @current_protocol: current nfc protocol.
+ * @current_rf_tech: current rf technology.
+ * @fwi: frame waiting index, received in reply of RATS according to
+ * digital protocol.
+ */
+struct st95hf_context {
+ struct st95hf_spi_context spicontext;
+ struct nfc_digital_dev *ddev;
+ struct nfc_dev *nfcdev;
+ unsigned int enable_gpio;
+ struct st95_digital_cmd_complete_arg complete_cb_arg;
+ struct regulator *st95hf_supply;
+ unsigned char sendrcv_trflag;
+ struct semaphore exchange_lock;
+ struct mutex rm_lock;
+ bool nfcdev_free;
+ u8 current_protocol;
+ u8 current_rf_tech;
+ int fwi;
+};
+
+/*
+ * st95hf_send_recv_cmd() is for sending commands to ST95HF
+ * that are described in the cmd_array[]. It can optionally
+ * receive the response if the cmd request is of type
+ * SYNC. For that to happen caller must pass true to recv_res.
+ * For ASYNC request, recv_res is ignored and the
+ * function will never try to receive the response on behalf
+ * of the caller.
+ */
+static int st95hf_send_recv_cmd(struct st95hf_context *st95context,
+ enum st95hf_cmd_list cmd,
+ int no_modif,
+ struct param_list *list_array,
+ bool recv_res)
+{
+ unsigned char spi_cmd_buffer[MAX_CMD_LEN];
+ int i, ret;
+ struct device *dev = &st95context->spicontext.spidev->dev;
+
+ if (cmd_array[cmd].cmd_len > MAX_CMD_LEN)
+ return -EINVAL;
+ if (cmd_array[cmd].no_cmd_params < no_modif)
+ return -EINVAL;
+ if (no_modif && !list_array)
+ return -EINVAL;
+
+ spi_cmd_buffer[0] = ST95HF_COMMAND_SEND;
+ spi_cmd_buffer[1] = cmd_array[cmd].cmd_id;
+ spi_cmd_buffer[2] = cmd_array[cmd].no_cmd_params;
+
+ memcpy(&spi_cmd_buffer[3], cmd_array[cmd].cmd_params,
+ spi_cmd_buffer[2]);
+
+ for (i = 0; i < no_modif; i++) {
+ if (list_array[i].param_offset >= cmd_array[cmd].no_cmd_params)
+ return -EINVAL;
+ spi_cmd_buffer[3 + list_array[i].param_offset] =
+ list_array[i].new_param_val;
+ }
+
+ ret = st95hf_spi_send(&st95context->spicontext,
+ spi_cmd_buffer,
+ cmd_array[cmd].cmd_len,
+ cmd_array[cmd].req);
+ if (ret) {
+ dev_err(dev, "st95hf_spi_send failed with error %d\n", ret);
+ return ret;
+ }
+
+ if (cmd_array[cmd].req == SYNC && recv_res) {
+ unsigned char st95hf_response_arr[2];
+
+ ret = st95hf_spi_recv_response(&st95context->spicontext,
+ st95hf_response_arr);
+ if (ret < 0) {
+ dev_err(dev, "spi error from st95hf_spi_recv_response(), err = 0x%x\n",
+ ret);
+ return ret;
+ }
+
+ if (st95hf_response_arr[0]) {
+ dev_err(dev, "st95hf error from st95hf_spi_recv_response(), err = 0x%x\n",
+ st95hf_response_arr[0]);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int st95hf_echo_command(struct st95hf_context *st95context)
+{
+ int result = 0;
+ unsigned char echo_response;
+
+ result = st95hf_send_recv_cmd(st95context, CMD_ECHO, 0, NULL, false);
+ if (result)
+ return result;
+
+ /* If control reached here, response can be taken */
+ result = st95hf_spi_recv_echo_res(&st95context->spicontext,
+ &echo_response);
+ if (result) {
+ dev_err(&st95context->spicontext.spidev->dev,
+ "err: echo response receieve error = 0x%x\n", result);
+ return result;
+ }
+
+ if (echo_response == ECHORESPONSE)
+ return 0;
+
+ dev_err(&st95context->spicontext.spidev->dev, "err: echo res is 0x%x\n",
+ echo_response);
+
+ return -EIO;
+}
+
+static int secondary_configuration_type4a(struct st95hf_context *stcontext)
+{
+ int result = 0;
+ struct device *dev = &stcontext->nfcdev->dev;
+
+ /* 14443A config setting after select protocol */
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_ISO14443A_CONFIG,
+ 0,
+ NULL,
+ true);
+ if (result) {
+ dev_err(dev, "type a config cmd, err = 0x%x\n", result);
+ return result;
+ }
+
+ /* 14443A demo gain setting */
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_ISO14443A_DEMOGAIN,
+ 0,
+ NULL,
+ true);
+ if (result)
+ dev_err(dev, "type a demogain cmd, err = 0x%x\n", result);
+
+ return result;
+}
+
+static int secondary_configuration_type4b(struct st95hf_context *stcontext)
+{
+ int result = 0;
+ struct device *dev = &stcontext->nfcdev->dev;
+
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_ISO14443B_DEMOGAIN,
+ 0,
+ NULL,
+ true);
+ if (result)
+ dev_err(dev, "type b demogain cmd, err = 0x%x\n", result);
+
+ return result;
+}
+
+static int st95hf_select_protocol(struct st95hf_context *stcontext, int type)
+{
+ int result = 0;
+ struct device *dev;
+
+ dev = &stcontext->nfcdev->dev;
+
+ switch (type) {
+ case NFC_DIGITAL_RF_TECH_106A:
+ stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_106A;
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_ISO14443A_PROTOCOL_SELECT,
+ 0,
+ NULL,
+ true);
+ if (result) {
+ dev_err(dev, "protocol sel, err = 0x%x\n",
+ result);
+ return result;
+ }
+
+ /* secondary config. for 14443Type 4A after protocol select */
+ result = secondary_configuration_type4a(stcontext);
+ if (result) {
+ dev_err(dev, "type a secondary config, err = 0x%x\n",
+ result);
+ return result;
+ }
+ break;
+ case NFC_DIGITAL_RF_TECH_106B:
+ stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_106B;
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_ISO14443B_PROTOCOL_SELECT,
+ 0,
+ NULL,
+ true);
+ if (result) {
+ dev_err(dev, "protocol sel send, err = 0x%x\n",
+ result);
+ return result;
+ }
+
+ /*
+ * delay of 5-6 ms is required after select protocol
+ * command in case of ISO14443 Type B
+ */
+ usleep_range(50000, 60000);
+
+ /* secondary config. for 14443Type 4B after protocol select */
+ result = secondary_configuration_type4b(stcontext);
+ if (result) {
+ dev_err(dev, "type b secondary config, err = 0x%x\n",
+ result);
+ return result;
+ }
+ break;
+ case NFC_DIGITAL_RF_TECH_ISO15693:
+ stcontext->current_rf_tech = NFC_DIGITAL_RF_TECH_ISO15693;
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_ISO15693_PROTOCOL_SELECT,
+ 0,
+ NULL,
+ true);
+ if (result) {
+ dev_err(dev, "protocol sel send, err = 0x%x\n",
+ result);
+ return result;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void st95hf_send_st95enable_negativepulse(struct st95hf_context *st95con)
+{
+ /* First make irq_in pin high */
+ gpio_set_value(st95con->enable_gpio, HIGH);
+
+ /* wait for 1 milisecond */
+ usleep_range(1000, 2000);
+
+ /* Make irq_in pin low */
+ gpio_set_value(st95con->enable_gpio, LOW);
+
+ /* wait for minimum interrupt pulse to make st95 active */
+ usleep_range(1000, 2000);
+
+ /* At end make it high */
+ gpio_set_value(st95con->enable_gpio, HIGH);
+}
+
+/*
+ * Send a reset sequence over SPI bus (Reset command + wait 3ms +
+ * negative pulse on st95hf enable gpio
+ */
+static int st95hf_send_spi_reset_sequence(struct st95hf_context *st95context)
+{
+ int result = 0;
+ unsigned char reset_cmd = ST95HF_COMMAND_RESET;
+
+ result = st95hf_spi_send(&st95context->spicontext,
+ &reset_cmd,
+ ST95HF_RESET_CMD_LEN,
+ ASYNC);
+ if (result) {
+ dev_err(&st95context->spicontext.spidev->dev,
+ "spi reset sequence cmd error = %d", result);
+ return result;
+ }
+
+ /* wait for 3 milisecond to complete the controller reset process */
+ usleep_range(3000, 4000);
+
+ /* send negative pulse to make st95hf active */
+ st95hf_send_st95enable_negativepulse(st95context);
+
+ /* wait for 10 milisecond : HFO setup time */
+ usleep_range(10000, 20000);
+
+ return result;
+}
+
+static int st95hf_por_sequence(struct st95hf_context *st95context)
+{
+ int nth_attempt = 1;
+ int result;
+
+ st95hf_send_st95enable_negativepulse(st95context);
+
+ usleep_range(5000, 6000);
+ do {
+ /* send an ECHO command and checks ST95HF response */
+ result = st95hf_echo_command(st95context);
+
+ dev_dbg(&st95context->spicontext.spidev->dev,
+ "response from echo function = 0x%x, attempt = %d\n",
+ result, nth_attempt);
+
+ if (!result)
+ return 0;
+
+ /* send an pulse on IRQ in case of the chip is on sleep state */
+ if (nth_attempt == 2)
+ st95hf_send_st95enable_negativepulse(st95context);
+ else
+ st95hf_send_spi_reset_sequence(st95context);
+
+ /* delay of 50 milisecond */
+ usleep_range(50000, 51000);
+ } while (nth_attempt++ < 3);
+
+ return -ETIMEDOUT;
+}
+
+static int iso14443_config_fdt(struct st95hf_context *st95context, int wtxm)
+{
+ int result = 0;
+ struct device *dev = &st95context->spicontext.spidev->dev;
+ struct nfc_digital_dev *nfcddev = st95context->ddev;
+ unsigned char pp_typeb;
+ struct param_list new_params[2];
+
+ pp_typeb = cmd_array[CMD_ISO14443B_PROTOCOL_SELECT].cmd_params[2];
+
+ if (nfcddev->curr_protocol == NFC_PROTO_ISO14443 &&
+ st95context->fwi < 4)
+ st95context->fwi = 4;
+
+ new_params[0].param_offset = 2;
+ if (nfcddev->curr_protocol == NFC_PROTO_ISO14443)
+ new_params[0].new_param_val = st95context->fwi;
+ else if (nfcddev->curr_protocol == NFC_PROTO_ISO14443_B)
+ new_params[0].new_param_val = pp_typeb;
+
+ new_params[1].param_offset = 3;
+ new_params[1].new_param_val = wtxm;
+
+ switch (nfcddev->curr_protocol) {
+ case NFC_PROTO_ISO14443:
+ result = st95hf_send_recv_cmd(st95context,
+ CMD_ISO14443A_PROTOCOL_SELECT,
+ 2,
+ new_params,
+ true);
+ if (result) {
+ dev_err(dev, "WTX type a sel proto, err = 0x%x\n",
+ result);
+ return result;
+ }
+
+ /* secondary config. for 14443Type 4A after protocol select */
+ result = secondary_configuration_type4a(st95context);
+ if (result) {
+ dev_err(dev, "WTX type a second. config, err = 0x%x\n",
+ result);
+ return result;
+ }
+ break;
+ case NFC_PROTO_ISO14443_B:
+ result = st95hf_send_recv_cmd(st95context,
+ CMD_ISO14443B_PROTOCOL_SELECT,
+ 2,
+ new_params,
+ true);
+ if (result) {
+ dev_err(dev, "WTX type b sel proto, err = 0x%x\n",
+ result);
+ return result;
+ }
+
+ /* secondary config. for 14443Type 4B after protocol select */
+ result = secondary_configuration_type4b(st95context);
+ if (result) {
+ dev_err(dev, "WTX type b second. config, err = 0x%x\n",
+ result);
+ return result;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int st95hf_handle_wtx(struct st95hf_context *stcontext,
+ bool new_wtx,
+ int wtx_val)
+{
+ int result = 0;
+ unsigned char val_mm = 0;
+ struct param_list new_params[1];
+ struct nfc_digital_dev *nfcddev = stcontext->ddev;
+ struct device *dev = &stcontext->nfcdev->dev;
+
+ if (new_wtx) {
+ result = iso14443_config_fdt(stcontext, wtx_val & 0x3f);
+ if (result) {
+ dev_err(dev, "Config. setting error on WTX req, err = 0x%x\n",
+ result);
+ return result;
+ }
+
+ /* Send response of wtx with ASYNC as no response expected */
+ new_params[0].param_offset = 1;
+ new_params[0].new_param_val = wtx_val;
+
+ result = st95hf_send_recv_cmd(stcontext,
+ CMD_WTX_RESPONSE,
+ 1,
+ new_params,
+ false);
+ if (result)
+ dev_err(dev, "WTX response send, err = 0x%x\n", result);
+ return result;
+ }
+
+ /* if no new wtx, cofigure with default values */
+ if (nfcddev->curr_protocol == NFC_PROTO_ISO14443)
+ val_mm = cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[3];
+ else if (nfcddev->curr_protocol == NFC_PROTO_ISO14443_B)
+ val_mm = cmd_array[CMD_ISO14443B_PROTOCOL_SELECT].cmd_params[3];
+
+ result = iso14443_config_fdt(stcontext, val_mm);
+ if (result)
+ dev_err(dev, "Default config. setting error after WTX processing, err = 0x%x\n",
+ result);
+
+ return result;
+}
+
+static int st95hf_error_handling(struct st95hf_context *stcontext,
+ struct sk_buff *skb_resp,
+ int res_len)
+{
+ int result = 0;
+ unsigned char error_byte;
+ struct device *dev = &stcontext->nfcdev->dev;
+
+ /* First check ST95HF specific error */
+ if (skb_resp->data[0] & ST95HF_ERR_MASK) {
+ if (skb_resp->data[0] == ST95HF_TIMEOUT_ERROR)
+ result = -ETIMEDOUT;
+ else
+ result = -EIO;
+ return result;
+ }
+
+ /* Check for CRC err only if CRC is present in the tag response */
+ switch (stcontext->current_rf_tech) {
+ case NFC_DIGITAL_RF_TECH_106A:
+ if (stcontext->sendrcv_trflag == TRFLAG_NFCA_STD_FRAME_CRC) {
+ error_byte = skb_resp->data[res_len - 3];
+ if (error_byte & ST95HF_NFCA_CRC_ERR_MASK) {
+ /* CRC error occurred */
+ dev_err(dev, "CRC error, byte received = 0x%x\n",
+ error_byte);
+ result = -EIO;
+ }
+ }
+ break;
+ case NFC_DIGITAL_RF_TECH_106B:
+ case NFC_DIGITAL_RF_TECH_ISO15693:
+ error_byte = skb_resp->data[res_len - 1];
+ if (error_byte & ST95HF_NFCB_CRC_ERR_MASK) {
+ /* CRC error occurred */
+ dev_err(dev, "CRC error, byte received = 0x%x\n",
+ error_byte);
+ result = -EIO;
+ }
+ break;
+ }
+
+ return result;
+}
+
+static int st95hf_response_handler(struct st95hf_context *stcontext,
+ struct sk_buff *skb_resp,
+ int res_len)
+{
+ int result = 0;
+ int skb_len;
+ unsigned char val_mm;
+ struct nfc_digital_dev *nfcddev = stcontext->ddev;
+ struct device *dev = &stcontext->nfcdev->dev;
+ struct st95_digital_cmd_complete_arg *cb_arg;
+
+ cb_arg = &stcontext->complete_cb_arg;
+
+ /* Process the response */
+ skb_put(skb_resp, res_len);
+
+ /* Remove st95 header */
+ skb_pull(skb_resp, 2);
+
+ skb_len = skb_resp->len;
+
+ /* check if it is case of RATS request reply & FWI is present */
+ if (nfcddev->curr_protocol == NFC_PROTO_ISO14443 && cb_arg->rats &&
+ (skb_resp->data[1] & RATS_TB1_PRESENT_MASK)) {
+ if (skb_resp->data[1] & RATS_TA1_PRESENT_MASK)
+ stcontext->fwi =
+ (skb_resp->data[3] & TB1_FWI_MASK) >> 4;
+ else
+ stcontext->fwi =
+ (skb_resp->data[2] & TB1_FWI_MASK) >> 4;
+
+ val_mm = cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[3];
+
+ result = iso14443_config_fdt(stcontext, val_mm);
+ if (result) {
+ dev_err(dev, "error in config_fdt to handle fwi of ATS, error=%d\n",
+ result);
+ return result;
+ }
+ }
+ cb_arg->rats = false;
+
+ /* Remove CRC bytes only if received frames data has an eod (CRC) */
+ switch (stcontext->current_rf_tech) {
+ case NFC_DIGITAL_RF_TECH_106A:
+ if (stcontext->sendrcv_trflag == TRFLAG_NFCA_STD_FRAME_CRC)
+ skb_trim(skb_resp, (skb_len - 5));
+ else
+ skb_trim(skb_resp, (skb_len - 3));
+ break;
+ case NFC_DIGITAL_RF_TECH_106B:
+ case NFC_DIGITAL_RF_TECH_ISO15693:
+ skb_trim(skb_resp, (skb_len - 3));
+ break;
+ }
+
+ return result;
+}
+
+static irqreturn_t st95hf_irq_handler(int irq, void *st95hfcontext)
+{
+ struct st95hf_context *stcontext =
+ (struct st95hf_context *)st95hfcontext;
+
+ if (stcontext->spicontext.req_issync) {
+ complete(&stcontext->spicontext.done);
+ stcontext->spicontext.req_issync = false;
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t st95hf_irq_thread_handler(int irq, void *st95hfcontext)
+{
+ int result = 0;
+ int res_len;
+ static bool wtx;
+ struct device *dev;
+ struct device *spidevice;
+ struct nfc_digital_dev *nfcddev;
+ struct sk_buff *skb_resp;
+ struct st95hf_context *stcontext =
+ (struct st95hf_context *)st95hfcontext;
+ struct st95_digital_cmd_complete_arg *cb_arg;
+
+ spidevice = &stcontext->spicontext.spidev->dev;
+
+ /*
+ * check semaphore, if not down() already, then we don't
+ * know in which context the ISR is called and surely it
+ * will be a bug. Note that down() of the semaphore is done
+ * in the corresponding st95hf_in_send_cmd() and then
+ * only this ISR should be called. ISR will up() the
+ * semaphore before leaving. Hence when the ISR is called
+ * the correct behaviour is down_trylock() should always
+ * return 1 (indicating semaphore cant be taken and hence no
+ * change in semaphore count).
+ * If not, then we up() the semaphore and crash on
+ * a BUG() !
+ */
+ if (!down_trylock(&stcontext->exchange_lock)) {
+ up(&stcontext->exchange_lock);
+ WARN(1, "unknown context in ST95HF ISR");
+ return IRQ_NONE;
+ }
+
+ cb_arg = &stcontext->complete_cb_arg;
+ skb_resp = cb_arg->skb_resp;
+
+ mutex_lock(&stcontext->rm_lock);
+ res_len = st95hf_spi_recv_response(&stcontext->spicontext,
+ skb_resp->data);
+ if (res_len < 0) {
+ dev_err(spidevice, "TISR spi response err = 0x%x\n", res_len);
+ result = res_len;
+ goto end;
+ }
+
+ /* if stcontext->nfcdev_free is true, it means remove already ran */
+ if (stcontext->nfcdev_free) {
+ result = -ENODEV;
+ goto end;
+ }
+
+ dev = &stcontext->nfcdev->dev;
+ nfcddev = stcontext->ddev;
+ if (skb_resp->data[2] == WTX_REQ_FROM_TAG) {
+ /* Request for new FWT from tag */
+ result = st95hf_handle_wtx(stcontext, true, skb_resp->data[3]);
+ if (result)
+ goto end;
+
+ wtx = true;
+ mutex_unlock(&stcontext->rm_lock);
+ return IRQ_HANDLED;
+ }
+
+ result = st95hf_error_handling(stcontext, skb_resp, res_len);
+ if (result)
+ goto end;
+
+ result = st95hf_response_handler(stcontext, skb_resp, res_len);
+ if (result)
+ goto end;
+
+ /*
+ * If select protocol is done on wtx req. do select protocol
+ * again with default values
+ */
+ if (wtx) {
+ wtx = false;
+ result = st95hf_handle_wtx(stcontext, false, 0);
+ if (result)
+ goto end;
+ }
+
+ /* call digital layer callback */
+ cb_arg->complete_cb(stcontext->ddev, cb_arg->cb_usrarg, skb_resp);
+
+ /* up the semaphore before returning */
+ up(&stcontext->exchange_lock);
+ mutex_unlock(&stcontext->rm_lock);
+
+ return IRQ_HANDLED;
+
+end:
+ kfree_skb(skb_resp);
+ wtx = false;
+ cb_arg->rats = false;
+ skb_resp = ERR_PTR(result);
+ /* call of callback with error */
+ cb_arg->complete_cb(stcontext->ddev, cb_arg->cb_usrarg, skb_resp);
+ /* up the semaphore before returning */
+ up(&stcontext->exchange_lock);
+ mutex_unlock(&stcontext->rm_lock);
+ return IRQ_HANDLED;
+}
+
+/* NFC ops functions definition */
+static int st95hf_in_configure_hw(struct nfc_digital_dev *ddev,
+ int type,
+ int param)
+{
+ struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev);
+
+ if (type == NFC_DIGITAL_CONFIG_RF_TECH)
+ return st95hf_select_protocol(stcontext, param);
+
+ if (type == NFC_DIGITAL_CONFIG_FRAMING) {
+ switch (param) {
+ case NFC_DIGITAL_FRAMING_NFCA_SHORT:
+ stcontext->sendrcv_trflag = TRFLAG_NFCA_SHORT_FRAME;
+ break;
+ case NFC_DIGITAL_FRAMING_NFCA_STANDARD:
+ stcontext->sendrcv_trflag = TRFLAG_NFCA_STD_FRAME;
+ break;
+ case NFC_DIGITAL_FRAMING_NFCA_T4T:
+ case NFC_DIGITAL_FRAMING_NFCA_NFC_DEP:
+ case NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A:
+ stcontext->sendrcv_trflag = TRFLAG_NFCA_STD_FRAME_CRC;
+ break;
+ case NFC_DIGITAL_FRAMING_NFCB:
+ case NFC_DIGITAL_FRAMING_ISO15693_INVENTORY:
+ case NFC_DIGITAL_FRAMING_ISO15693_T5T:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int rf_off(struct st95hf_context *stcontext)
+{
+ int rc;
+ struct device *dev;
+
+ dev = &stcontext->nfcdev->dev;
+
+ rc = st95hf_send_recv_cmd(stcontext, CMD_FIELD_OFF, 0, NULL, true);
+ if (rc)
+ dev_err(dev, "protocol sel send field off, err = 0x%x\n", rc);
+
+ return rc;
+}
+
+static int st95hf_in_send_cmd(struct nfc_digital_dev *ddev,
+ struct sk_buff *skb,
+ u16 timeout,
+ nfc_digital_cmd_complete_t cb,
+ void *arg)
+{
+ struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev);
+ int rc;
+ struct sk_buff *skb_resp;
+ int len_data_to_tag = 0;
+
+ skb_resp = nfc_alloc_recv_skb(MAX_RESPONSE_BUFFER_SIZE, GFP_KERNEL);
+ if (!skb_resp) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ switch (stcontext->current_rf_tech) {
+ case NFC_DIGITAL_RF_TECH_106A:
+ len_data_to_tag = skb->len + 1;
+ *skb_put(skb, 1) = stcontext->sendrcv_trflag;
+ break;
+ case NFC_DIGITAL_RF_TECH_106B:
+ case NFC_DIGITAL_RF_TECH_ISO15693:
+ len_data_to_tag = skb->len;
+ break;
+ default:
+ rc = -EINVAL;
+ goto free_skb_resp;
+ }
+
+ skb_push(skb, 3);
+ skb->data[0] = ST95HF_COMMAND_SEND;
+ skb->data[1] = SEND_RECEIVE_CMD;
+ skb->data[2] = len_data_to_tag;
+
+ stcontext->complete_cb_arg.skb_resp = skb_resp;
+ stcontext->complete_cb_arg.cb_usrarg = arg;
+ stcontext->complete_cb_arg.complete_cb = cb;
+
+ if ((skb->data[3] == ISO14443A_RATS_REQ) &&
+ ddev->curr_protocol == NFC_PROTO_ISO14443)
+ stcontext->complete_cb_arg.rats = true;
+
+ /*
+ * down the semaphore to indicate to remove func that an
+ * ISR is pending, note that it will not block here in any case.
+ * If found blocked, it is a BUG!
+ */
+ rc = down_killable(&stcontext->exchange_lock);
+ if (rc) {
+ WARN(1, "Semaphore is not found up in st95hf_in_send_cmd\n");
+ return rc;
+ }
+
+ rc = st95hf_spi_send(&stcontext->spicontext, skb->data,
+ skb->len,
+ ASYNC);
+ if (rc) {
+ dev_err(&stcontext->nfcdev->dev,
+ "Error %d trying to perform data_exchange", rc);
+ /* up the semaphore since ISR will never come in this case */
+ up(&stcontext->exchange_lock);
+ goto free_skb_resp;
+ }
+
+ kfree_skb(skb);
+
+ return rc;
+
+free_skb_resp:
+ kfree_skb(skb_resp);
+error:
+ return rc;
+}
+
+/* p2p will be supported in a later release ! */
+static int st95hf_tg_configure_hw(struct nfc_digital_dev *ddev,
+ int type,
+ int param)
+{
+ return 0;
+}
+
+static int st95hf_tg_send_cmd(struct nfc_digital_dev *ddev,
+ struct sk_buff *skb,
+ u16 timeout,
+ nfc_digital_cmd_complete_t cb,
+ void *arg)
+{
+ return 0;
+}
+
+static int st95hf_tg_listen(struct nfc_digital_dev *ddev,
+ u16 timeout,
+ nfc_digital_cmd_complete_t cb,
+ void *arg)
+{
+ return 0;
+}
+
+static int st95hf_tg_get_rf_tech(struct nfc_digital_dev *ddev, u8 *rf_tech)
+{
+ return 0;
+}
+
+static int st95hf_switch_rf(struct nfc_digital_dev *ddev, bool on)
+{
+ u8 rf_tech;
+ struct st95hf_context *stcontext = nfc_digital_get_drvdata(ddev);
+
+ rf_tech = ddev->curr_rf_tech;
+
+ if (on)
+ /* switch on RF field */
+ return st95hf_select_protocol(stcontext, rf_tech);
+
+ /* switch OFF RF field */
+ return rf_off(stcontext);
+}
+
+/* TODO st95hf_abort_cmd */
+static void st95hf_abort_cmd(struct nfc_digital_dev *ddev)
+{
+}
+
+static struct nfc_digital_ops st95hf_nfc_digital_ops = {
+ .in_configure_hw = st95hf_in_configure_hw,
+ .in_send_cmd = st95hf_in_send_cmd,
+
+ .tg_listen = st95hf_tg_listen,
+ .tg_configure_hw = st95hf_tg_configure_hw,
+ .tg_send_cmd = st95hf_tg_send_cmd,
+ .tg_get_rf_tech = st95hf_tg_get_rf_tech,
+
+ .switch_rf = st95hf_switch_rf,
+ .abort_cmd = st95hf_abort_cmd,
+};
+
+static const struct spi_device_id st95hf_id[] = {
+ { "st95hf", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, st95hf_id);
+
+static int st95hf_probe(struct spi_device *nfc_spi_dev)
+{
+ int ret;
+
+ struct st95hf_context *st95context;
+ struct st95hf_spi_context *spicontext;
+
+ nfc_info(&nfc_spi_dev->dev, "ST95HF driver probe called.\n");
+
+ st95context = devm_kzalloc(&nfc_spi_dev->dev,
+ sizeof(struct st95hf_context),
+ GFP_KERNEL);
+ if (!st95context)
+ return -ENOMEM;
+
+ spicontext = &st95context->spicontext;
+
+ spicontext->spidev = nfc_spi_dev;
+
+ st95context->fwi =
+ cmd_array[CMD_ISO14443A_PROTOCOL_SELECT].cmd_params[2];
+
+ if (device_property_present(&nfc_spi_dev->dev, "st95hfvin")) {
+ st95context->st95hf_supply =
+ devm_regulator_get(&nfc_spi_dev->dev,
+ "st95hfvin");
+ if (IS_ERR(st95context->st95hf_supply)) {
+ dev_err(&nfc_spi_dev->dev, "failed to acquire regulator\n");
+ return PTR_ERR(st95context->st95hf_supply);
+ }
+
+ ret = regulator_enable(st95context->st95hf_supply);
+ if (ret) {
+ dev_err(&nfc_spi_dev->dev, "failed to enable regulator\n");
+ return ret;
+ }
+ }
+
+ init_completion(&spicontext->done);
+ mutex_init(&spicontext->spi_lock);
+
+ /*
+ * Store spicontext in spi device object for using it in
+ * remove function
+ */
+ dev_set_drvdata(&nfc_spi_dev->dev, spicontext);
+
+ st95context->enable_gpio =
+ of_get_named_gpio(nfc_spi_dev->dev.of_node,
+ "enable-gpio",
+ 0);
+ if (!gpio_is_valid(st95context->enable_gpio)) {
+ dev_err(&nfc_spi_dev->dev, "No valid enable gpio\n");
+ ret = st95context->enable_gpio;
+ goto err_disable_regulator;
+ }
+
+ ret = devm_gpio_request_one(&nfc_spi_dev->dev, st95context->enable_gpio,
+ GPIOF_DIR_OUT | GPIOF_INIT_HIGH,
+ "enable_gpio");
+ if (ret)
+ goto err_disable_regulator;
+
+ if (nfc_spi_dev->irq > 0) {
+ if (devm_request_threaded_irq(&nfc_spi_dev->dev,
+ nfc_spi_dev->irq,
+ st95hf_irq_handler,
+ st95hf_irq_thread_handler,
+ IRQF_TRIGGER_FALLING,
+ "st95hf",
+ (void *)st95context) < 0) {
+ dev_err(&nfc_spi_dev->dev, "err: irq request for st95hf is failed\n");
+ ret = -EINVAL;
+ goto err_disable_regulator;
+ }
+ } else {
+ dev_err(&nfc_spi_dev->dev, "not a valid IRQ associated with ST95HF\n");
+ ret = -EINVAL;
+ goto err_disable_regulator;
+ }
+
+ /*
+ * First reset SPI to handle warm reset of the system.
+ * It will put the ST95HF device in Power ON state
+ * which make the state of device identical to state
+ * at the time of cold reset of the system.
+ */
+ ret = st95hf_send_spi_reset_sequence(st95context);
+ if (ret) {
+ dev_err(&nfc_spi_dev->dev, "err: spi_reset_sequence failed\n");
+ goto err_disable_regulator;
+ }
+
+ /* call PowerOnReset sequence of ST95hf to activate it */
+ ret = st95hf_por_sequence(st95context);
+ if (ret) {
+ dev_err(&nfc_spi_dev->dev, "err: por seq failed for st95hf\n");
+ goto err_disable_regulator;
+ }
+
+ /* create NFC dev object and register with NFC Subsystem */
+ st95context->ddev = nfc_digital_allocate_device(&st95hf_nfc_digital_ops,
+ ST95HF_SUPPORTED_PROT,
+ ST95HF_CAPABILITIES,
+ ST95HF_HEADROOM_LEN,
+ ST95HF_TAILROOM_LEN);
+ if (!st95context->ddev) {
+ ret = -ENOMEM;
+ goto err_disable_regulator;
+ }
+
+ st95context->nfcdev = st95context->ddev->nfc_dev;
+ nfc_digital_set_parent_dev(st95context->ddev, &nfc_spi_dev->dev);
+
+ ret = nfc_digital_register_device(st95context->ddev);
+ if (ret) {
+ dev_err(&st95context->nfcdev->dev, "st95hf registration failed\n");
+ goto err_free_digital_device;
+ }
+
+ /* store st95context in nfc device object */
+ nfc_digital_set_drvdata(st95context->ddev, st95context);
+
+ sema_init(&st95context->exchange_lock, 1);
+ mutex_init(&st95context->rm_lock);
+
+ return ret;
+
+err_free_digital_device:
+ nfc_digital_free_device(st95context->ddev);
+err_disable_regulator:
+ if (st95context->st95hf_supply)
+ regulator_disable(st95context->st95hf_supply);
+
+ return ret;
+}
+
+static int st95hf_remove(struct spi_device *nfc_spi_dev)
+{
+ int result = 0;
+ unsigned char reset_cmd = ST95HF_COMMAND_RESET;
+ struct st95hf_spi_context *spictx = dev_get_drvdata(&nfc_spi_dev->dev);
+
+ struct st95hf_context *stcontext = container_of(spictx,
+ struct st95hf_context,
+ spicontext);
+
+ mutex_lock(&stcontext->rm_lock);
+
+ nfc_digital_unregister_device(stcontext->ddev);
+ nfc_digital_free_device(stcontext->ddev);
+ stcontext->nfcdev_free = true;
+
+ mutex_unlock(&stcontext->rm_lock);
+
+ /* if last in_send_cmd's ISR is pending, wait for it to finish */
+ result = down_killable(&stcontext->exchange_lock);
+ if (result == -EINTR)
+ dev_err(&spictx->spidev->dev, "sleep for semaphore interrupted by signal\n");
+
+ /* next reset the ST95HF controller */
+ result = st95hf_spi_send(&stcontext->spicontext,
+ &reset_cmd,
+ ST95HF_RESET_CMD_LEN,
+ ASYNC);
+ if (result) {
+ dev_err(&spictx->spidev->dev,
+ "ST95HF reset failed in remove() err = %d\n", result);
+ return result;
+ }
+
+ /* wait for 3 ms to complete the controller reset process */
+ usleep_range(3000, 4000);
+
+ /* disable regulator */
+ if (stcontext->st95hf_supply)
+ regulator_disable(stcontext->st95hf_supply);
+
+ return result;
+}
+
+/* Register as SPI protocol driver */
+static struct spi_driver st95hf_driver = {
+ .driver = {
+ .name = "st95hf",
+ .owner = THIS_MODULE,
+ },
+ .id_table = st95hf_id,
+ .probe = st95hf_probe,
+ .remove = st95hf_remove,
+};
+
+module_spi_driver(st95hf_driver);
+
+MODULE_AUTHOR("Shikha Singh <shikha.singh@st.com>");
+MODULE_DESCRIPTION("ST NFC Transceiver ST95HF driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/nfc/st95hf/spi.c b/drivers/nfc/st95hf/spi.c
new file mode 100644
index 000000000000..e2d3bbcc8c34
--- /dev/null
+++ b/drivers/nfc/st95hf/spi.c
@@ -0,0 +1,167 @@
+/*
+ * ----------------------------------------------------------------------------
+ * drivers/nfc/st95hf/spi.c function definitions for SPI communication
+ * ----------------------------------------------------------------------------
+ * Copyright (C) 2015 STMicroelectronics Pvt. Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "spi.h"
+
+/* Function to send user provided buffer to ST95HF through SPI */
+int st95hf_spi_send(struct st95hf_spi_context *spicontext,
+ unsigned char *buffertx,
+ int datalen,
+ enum req_type reqtype)
+{
+ struct spi_message m;
+ int result = 0;
+ struct spi_device *spidev = spicontext->spidev;
+ struct spi_transfer tx_transfer = {
+ .tx_buf = buffertx,
+ .len = datalen,
+ };
+
+ mutex_lock(&spicontext->spi_lock);
+
+ if (reqtype == SYNC) {
+ spicontext->req_issync = true;
+ reinit_completion(&spicontext->done);
+ } else {
+ spicontext->req_issync = false;
+ }
+
+ spi_message_init(&m);
+ spi_message_add_tail(&tx_transfer, &m);
+
+ result = spi_sync(spidev, &m);
+ if (result) {
+ dev_err(&spidev->dev, "error: sending cmd to st95hf using SPI = %d\n",
+ result);
+ mutex_unlock(&spicontext->spi_lock);
+ return result;
+ }
+
+ /* return for asynchronous or no-wait case */
+ if (reqtype == ASYNC) {
+ mutex_unlock(&spicontext->spi_lock);
+ return 0;
+ }
+
+ result = wait_for_completion_timeout(&spicontext->done,
+ msecs_to_jiffies(1000));
+ /* check for timeout or success */
+ if (!result) {
+ dev_err(&spidev->dev, "error: response not ready timeout\n");
+ result = -ETIMEDOUT;
+ } else {
+ result = 0;
+ }
+
+ mutex_unlock(&spicontext->spi_lock);
+
+ return result;
+}
+EXPORT_SYMBOL_GPL(st95hf_spi_send);
+
+/* Function to Receive command Response */
+int st95hf_spi_recv_response(struct st95hf_spi_context *spicontext,
+ unsigned char *receivebuff)
+{
+ int len = 0;
+ struct spi_transfer tx_takedata;
+ struct spi_message m;
+ struct spi_device *spidev = spicontext->spidev;
+ unsigned char readdata_cmd = ST95HF_COMMAND_RECEIVE;
+ struct spi_transfer t[2] = {
+ {.tx_buf = &readdata_cmd, .len = 1,},
+ {.rx_buf = receivebuff, .len = 2, .cs_change = 1,},
+ };
+
+ int ret = 0;
+
+ memset(&tx_takedata, 0x0, sizeof(struct spi_transfer));
+
+ mutex_lock(&spicontext->spi_lock);
+
+ /* First spi transfer to know the length of valid data */
+ spi_message_init(&m);
+ spi_message_add_tail(&t[0], &m);
+ spi_message_add_tail(&t[1], &m);
+
+ ret = spi_sync(spidev, &m);
+ if (ret) {
+ dev_err(&spidev->dev, "spi_recv_resp, data length error = %d\n",
+ ret);
+ mutex_unlock(&spicontext->spi_lock);
+ return ret;
+ }
+
+ /* As 2 bytes are already read */
+ len = 2;
+
+ /* Support of long frame */
+ if (receivebuff[0] & 0x60)
+ len += (((receivebuff[0] & 0x60) >> 5) << 8) | receivebuff[1];
+ else
+ len += receivebuff[1];
+
+ /* Now make a transfer to read only relevant bytes */
+ tx_takedata.rx_buf = &receivebuff[2];
+ tx_takedata.len = len - 2;
+
+ spi_message_init(&m);
+ spi_message_add_tail(&tx_takedata, &m);
+
+ ret = spi_sync(spidev, &m);
+
+ mutex_unlock(&spicontext->spi_lock);
+ if (ret) {
+ dev_err(&spidev->dev, "spi_recv_resp, data read error = %d\n",
+ ret);
+ return ret;
+ }
+
+ return len;
+}
+EXPORT_SYMBOL_GPL(st95hf_spi_recv_response);
+
+int st95hf_spi_recv_echo_res(struct st95hf_spi_context *spicontext,
+ unsigned char *receivebuff)
+{
+ unsigned char readdata_cmd = ST95HF_COMMAND_RECEIVE;
+ struct spi_transfer t[2] = {
+ {.tx_buf = &readdata_cmd, .len = 1,},
+ {.rx_buf = receivebuff, .len = 1,},
+ };
+ struct spi_message m;
+ struct spi_device *spidev = spicontext->spidev;
+ int ret = 0;
+
+ mutex_lock(&spicontext->spi_lock);
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t[0], &m);
+ spi_message_add_tail(&t[1], &m);
+ ret = spi_sync(spidev, &m);
+
+ mutex_unlock(&spicontext->spi_lock);
+
+ if (ret)
+ dev_err(&spidev->dev, "recv_echo_res, data read error = %d\n",
+ ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(st95hf_spi_recv_echo_res);
diff --git a/drivers/nfc/st95hf/spi.h b/drivers/nfc/st95hf/spi.h
new file mode 100644
index 000000000000..552d220747cd
--- /dev/null
+++ b/drivers/nfc/st95hf/spi.h
@@ -0,0 +1,64 @@
+/*
+ * ---------------------------------------------------------------------------
+ * drivers/nfc/st95hf/spi.h functions declarations for SPI communication
+ * ---------------------------------------------------------------------------
+ * Copyright (C) 2015 STMicroelectronics – All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LINUX_ST95HF_SPI_H
+#define __LINUX_ST95HF_SPI_H
+
+#include <linux/spi/spi.h>
+
+/* Basic ST95HF SPI CMDs */
+#define ST95HF_COMMAND_SEND 0x0
+#define ST95HF_COMMAND_RESET 0x1
+#define ST95HF_COMMAND_RECEIVE 0x2
+
+#define ST95HF_RESET_CMD_LEN 0x1
+
+/*
+ * structure to contain st95hf spi communication specific information.
+ * @req_issync: true for synchronous calls.
+ * @spidev: st95hf spi device object.
+ * @done: completion structure to wait for st95hf response
+ * for synchronous calls.
+ * @spi_lock: mutex to allow only one spi transfer at a time.
+ */
+struct st95hf_spi_context {
+ bool req_issync;
+ struct spi_device *spidev;
+ struct completion done;
+ struct mutex spi_lock;
+};
+
+/* flag to differentiate synchronous & asynchronous spi request */
+enum req_type {
+ SYNC,
+ ASYNC,
+};
+
+int st95hf_spi_send(struct st95hf_spi_context *spicontext,
+ unsigned char *buffertx,
+ int datalen,
+ enum req_type reqtype);
+
+int st95hf_spi_recv_response(struct st95hf_spi_context *spicontext,
+ unsigned char *receivebuff);
+
+int st95hf_spi_recv_echo_res(struct st95hf_spi_context *spicontext,
+ unsigned char *receivebuff);
+
+#endif
diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c
index f857feb2b573..10842b7051b3 100644
--- a/drivers/nfc/trf7970a.c
+++ b/drivers/nfc/trf7970a.c
@@ -2139,7 +2139,7 @@ static int trf7970a_remove(struct spi_device *spi)
#ifdef CONFIG_PM_SLEEP
static int trf7970a_suspend(struct device *dev)
{
- struct spi_device *spi = container_of(dev, struct spi_device, dev);
+ struct spi_device *spi = to_spi_device(dev);
struct trf7970a *trf = spi_get_drvdata(spi);
dev_dbg(dev, "Suspend\n");
@@ -2155,7 +2155,7 @@ static int trf7970a_suspend(struct device *dev)
static int trf7970a_resume(struct device *dev)
{
- struct spi_device *spi = container_of(dev, struct spi_device, dev);
+ struct spi_device *spi = to_spi_device(dev);
struct trf7970a *trf = spi_get_drvdata(spi);
int ret;
@@ -2174,7 +2174,7 @@ static int trf7970a_resume(struct device *dev)
#ifdef CONFIG_PM
static int trf7970a_pm_runtime_suspend(struct device *dev)
{
- struct spi_device *spi = container_of(dev, struct spi_device, dev);
+ struct spi_device *spi = to_spi_device(dev);
struct trf7970a *trf = spi_get_drvdata(spi);
int ret;
@@ -2191,7 +2191,7 @@ static int trf7970a_pm_runtime_suspend(struct device *dev)
static int trf7970a_pm_runtime_resume(struct device *dev)
{
- struct spi_device *spi = container_of(dev, struct spi_device, dev);
+ struct spi_device *spi = to_spi_device(dev);
struct trf7970a *trf = spi_get_drvdata(spi);
int ret;
diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c
index 82c49bb87055..2e2832b83c93 100644
--- a/drivers/nvdimm/core.c
+++ b/drivers/nvdimm/core.c
@@ -11,6 +11,7 @@
* General Public License for more details.
*/
#include <linux/libnvdimm.h>
+#include <linux/badblocks.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/blkdev.h>
@@ -325,6 +326,7 @@ struct nvdimm_bus *__nvdimm_bus_register(struct device *parent,
if (!nvdimm_bus)
return NULL;
INIT_LIST_HEAD(&nvdimm_bus->list);
+ INIT_LIST_HEAD(&nvdimm_bus->poison_list);
init_waitqueue_head(&nvdimm_bus->probe_wait);
nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL);
mutex_init(&nvdimm_bus->reconfig_mutex);
@@ -359,6 +361,172 @@ struct nvdimm_bus *__nvdimm_bus_register(struct device *parent,
}
EXPORT_SYMBOL_GPL(__nvdimm_bus_register);
+static void set_badblock(struct badblocks *bb, sector_t s, int num)
+{
+ dev_dbg(bb->dev, "Found a poison range (0x%llx, 0x%llx)\n",
+ (u64) s * 512, (u64) num * 512);
+ /* this isn't an error as the hardware will still throw an exception */
+ if (badblocks_set(bb, s, num, 1))
+ dev_info_once(bb->dev, "%s: failed for sector %llx\n",
+ __func__, (u64) s);
+}
+
+/**
+ * __add_badblock_range() - Convert a physical address range to bad sectors
+ * @bb: badblocks instance to populate
+ * @ns_offset: namespace offset where the error range begins (in bytes)
+ * @len: number of bytes of poison to be added
+ *
+ * This assumes that the range provided with (ns_offset, len) is within
+ * the bounds of physical addresses for this namespace, i.e. lies in the
+ * interval [ns_start, ns_start + ns_size)
+ */
+static void __add_badblock_range(struct badblocks *bb, u64 ns_offset, u64 len)
+{
+ const unsigned int sector_size = 512;
+ sector_t start_sector;
+ u64 num_sectors;
+ u32 rem;
+
+ start_sector = div_u64(ns_offset, sector_size);
+ num_sectors = div_u64_rem(len, sector_size, &rem);
+ if (rem)
+ num_sectors++;
+
+ if (unlikely(num_sectors > (u64)INT_MAX)) {
+ u64 remaining = num_sectors;
+ sector_t s = start_sector;
+
+ while (remaining) {
+ int done = min_t(u64, remaining, INT_MAX);
+
+ set_badblock(bb, s, done);
+ remaining -= done;
+ s += done;
+ }
+ } else
+ set_badblock(bb, start_sector, num_sectors);
+}
+
+/**
+ * nvdimm_namespace_add_poison() - Convert a list of poison ranges to badblocks
+ * @ndns: the namespace containing poison ranges
+ * @bb: badblocks instance to populate
+ * @offset: offset at the start of the namespace before 'sector 0'
+ *
+ * The poison list generated during NFIT initialization may contain multiple,
+ * possibly overlapping ranges in the SPA (System Physical Address) space.
+ * Compare each of these ranges to the namespace currently being initialized,
+ * and add badblocks to the gendisk for all matching sub-ranges
+ */
+void nvdimm_namespace_add_poison(struct nd_namespace_common *ndns,
+ struct badblocks *bb, resource_size_t offset)
+{
+ struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev);
+ struct nd_region *nd_region = to_nd_region(ndns->dev.parent);
+ struct nvdimm_bus *nvdimm_bus;
+ struct list_head *poison_list;
+ u64 ns_start, ns_end, ns_size;
+ struct nd_poison *pl;
+
+ ns_size = nvdimm_namespace_capacity(ndns) - offset;
+ ns_start = nsio->res.start + offset;
+ ns_end = nsio->res.end;
+
+ nvdimm_bus = to_nvdimm_bus(nd_region->dev.parent);
+ poison_list = &nvdimm_bus->poison_list;
+ if (list_empty(poison_list))
+ return;
+
+ list_for_each_entry(pl, poison_list, list) {
+ u64 pl_end = pl->start + pl->length - 1;
+
+ /* Discard intervals with no intersection */
+ if (pl_end < ns_start)
+ continue;
+ if (pl->start > ns_end)
+ continue;
+ /* Deal with any overlap after start of the namespace */
+ if (pl->start >= ns_start) {
+ u64 start = pl->start;
+ u64 len;
+
+ if (pl_end <= ns_end)
+ len = pl->length;
+ else
+ len = ns_start + ns_size - pl->start;
+ __add_badblock_range(bb, start - ns_start, len);
+ continue;
+ }
+ /* Deal with overlap for poison starting before the namespace */
+ if (pl->start < ns_start) {
+ u64 len;
+
+ if (pl_end < ns_end)
+ len = pl->start + pl->length - ns_start;
+ else
+ len = ns_size;
+ __add_badblock_range(bb, 0, len);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(nvdimm_namespace_add_poison);
+
+static int __add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length)
+{
+ struct nd_poison *pl;
+
+ pl = kzalloc(sizeof(*pl), GFP_KERNEL);
+ if (!pl)
+ return -ENOMEM;
+
+ pl->start = addr;
+ pl->length = length;
+ list_add_tail(&pl->list, &nvdimm_bus->poison_list);
+
+ return 0;
+}
+
+int nvdimm_bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length)
+{
+ struct nd_poison *pl;
+
+ if (list_empty(&nvdimm_bus->poison_list))
+ return __add_poison(nvdimm_bus, addr, length);
+
+ /*
+ * There is a chance this is a duplicate, check for those first.
+ * This will be the common case as ARS_STATUS returns all known
+ * errors in the SPA space, and we can't query it per region
+ */
+ list_for_each_entry(pl, &nvdimm_bus->poison_list, list)
+ if (pl->start == addr) {
+ /* If length has changed, update this list entry */
+ if (pl->length != length)
+ pl->length = length;
+ return 0;
+ }
+
+ /*
+ * If not a duplicate or a simple length update, add the entry as is,
+ * as any overlapping ranges will get resolved when the list is consumed
+ * and converted to badblocks
+ */
+ return __add_poison(nvdimm_bus, addr, length);
+}
+EXPORT_SYMBOL_GPL(nvdimm_bus_add_poison);
+
+static void free_poison_list(struct list_head *poison_list)
+{
+ struct nd_poison *pl, *next;
+
+ list_for_each_entry_safe(pl, next, poison_list, list) {
+ list_del(&pl->list);
+ kfree(pl);
+ }
+ list_del_init(poison_list);
+}
+
static int child_unregister(struct device *dev, void *data)
{
/*
@@ -385,6 +553,7 @@ void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus)
nd_synchronize();
device_for_each_child(&nvdimm_bus->dev, NULL, child_unregister);
+ free_poison_list(&nvdimm_bus->poison_list);
nvdimm_bus_destroy_ndctl(nvdimm_bus);
device_unregister(&nvdimm_bus->dev);
diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
index 0955b2cb10fe..8ebfcaae3f5a 100644
--- a/drivers/nvdimm/namespace_devs.c
+++ b/drivers/nvdimm/namespace_devs.c
@@ -77,6 +77,59 @@ static bool is_namespace_io(struct device *dev)
return dev ? dev->type == &namespace_io_device_type : false;
}
+static int is_uuid_busy(struct device *dev, void *data)
+{
+ u8 *uuid1 = data, *uuid2 = NULL;
+
+ if (is_namespace_pmem(dev)) {
+ struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
+
+ uuid2 = nspm->uuid;
+ } else if (is_namespace_blk(dev)) {
+ struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
+
+ uuid2 = nsblk->uuid;
+ } else if (is_nd_btt(dev)) {
+ struct nd_btt *nd_btt = to_nd_btt(dev);
+
+ uuid2 = nd_btt->uuid;
+ } else if (is_nd_pfn(dev)) {
+ struct nd_pfn *nd_pfn = to_nd_pfn(dev);
+
+ uuid2 = nd_pfn->uuid;
+ }
+
+ if (uuid2 && memcmp(uuid1, uuid2, NSLABEL_UUID_LEN) == 0)
+ return -EBUSY;
+
+ return 0;
+}
+
+static int is_namespace_uuid_busy(struct device *dev, void *data)
+{
+ if (is_nd_pmem(dev) || is_nd_blk(dev))
+ return device_for_each_child(dev, data, is_uuid_busy);
+ return 0;
+}
+
+/**
+ * nd_is_uuid_unique - verify that no other namespace has @uuid
+ * @dev: any device on a nvdimm_bus
+ * @uuid: uuid to check
+ */
+bool nd_is_uuid_unique(struct device *dev, u8 *uuid)
+{
+ struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
+
+ if (!nvdimm_bus)
+ return false;
+ WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm_bus->dev));
+ if (device_for_each_child(&nvdimm_bus->dev, uuid,
+ is_namespace_uuid_busy) != 0)
+ return false;
+ return true;
+}
+
bool pmem_should_map_pages(struct device *dev)
{
struct nd_region *nd_region = to_nd_region(dev->parent);
@@ -104,20 +157,10 @@ const char *nvdimm_namespace_disk_name(struct nd_namespace_common *ndns,
struct nd_region *nd_region = to_nd_region(ndns->dev.parent);
const char *suffix = NULL;
- if (ndns->claim) {
- if (is_nd_btt(ndns->claim))
- suffix = "s";
- else if (is_nd_pfn(ndns->claim))
- suffix = "m";
- else
- dev_WARN_ONCE(&ndns->dev, 1,
- "unknown claim type by %s\n",
- dev_name(ndns->claim));
- }
+ if (ndns->claim && is_nd_btt(ndns->claim))
+ suffix = "s";
if (is_namespace_pmem(&ndns->dev) || is_namespace_io(&ndns->dev)) {
- if (!suffix && pmem_should_map_pages(&ndns->dev))
- suffix = "m";
sprintf(name, "pmem%d%s", nd_region->id, suffix ? suffix : "");
} else if (is_namespace_blk(&ndns->dev)) {
struct nd_namespace_blk *nsblk;
@@ -791,6 +834,15 @@ static void nd_namespace_pmem_set_size(struct nd_region *nd_region,
res->end = nd_region->ndr_start + size - 1;
}
+static bool uuid_not_set(const u8 *uuid, struct device *dev, const char *where)
+{
+ if (!uuid) {
+ dev_dbg(dev, "%s: uuid not set\n", where);
+ return true;
+ }
+ return false;
+}
+
static ssize_t __size_store(struct device *dev, unsigned long long val)
{
resource_size_t allocated = 0, available = 0;
@@ -820,8 +872,12 @@ static ssize_t __size_store(struct device *dev, unsigned long long val)
* We need a uuid for the allocation-label and dimm(s) on which
* to store the label.
*/
- if (!uuid || nd_region->ndr_mappings == 0)
+ if (uuid_not_set(uuid, dev, __func__))
return -ENXIO;
+ if (nd_region->ndr_mappings == 0) {
+ dev_dbg(dev, "%s: not associated with dimm(s)\n", __func__);
+ return -ENXIO;
+ }
div_u64_rem(val, SZ_4K * nd_region->ndr_mappings, &remainder);
if (remainder) {
@@ -1211,6 +1267,29 @@ static ssize_t holder_show(struct device *dev,
}
static DEVICE_ATTR_RO(holder);
+static ssize_t mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nd_namespace_common *ndns = to_ndns(dev);
+ struct device *claim;
+ char *mode;
+ ssize_t rc;
+
+ device_lock(dev);
+ claim = ndns->claim;
+ if (pmem_should_map_pages(dev) || (claim && is_nd_pfn(claim)))
+ mode = "memory";
+ else if (claim && is_nd_btt(claim))
+ mode = "safe";
+ else
+ mode = "raw";
+ rc = sprintf(buf, "%s\n", mode);
+ device_unlock(dev);
+
+ return rc;
+}
+static DEVICE_ATTR_RO(mode);
+
static ssize_t force_raw_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len)
{
@@ -1234,6 +1313,7 @@ static DEVICE_ATTR_RW(force_raw);
static struct attribute *nd_namespace_attributes[] = {
&dev_attr_nstype.attr,
&dev_attr_size.attr,
+ &dev_attr_mode.attr,
&dev_attr_uuid.attr,
&dev_attr_holder.attr,
&dev_attr_resource.attr,
@@ -1267,7 +1347,8 @@ static umode_t namespace_visible(struct kobject *kobj,
if (a == &dev_attr_nstype.attr || a == &dev_attr_size.attr
|| a == &dev_attr_holder.attr
- || a == &dev_attr_force_raw.attr)
+ || a == &dev_attr_force_raw.attr
+ || a == &dev_attr_mode.attr)
return a->mode;
return 0;
@@ -1343,14 +1424,19 @@ struct nd_namespace_common *nvdimm_namespace_common_probe(struct device *dev)
struct nd_namespace_pmem *nspm;
nspm = to_nd_namespace_pmem(&ndns->dev);
- if (!nspm->uuid) {
- dev_dbg(&ndns->dev, "%s: uuid not set\n", __func__);
+ if (uuid_not_set(nspm->uuid, &ndns->dev, __func__))
return ERR_PTR(-ENODEV);
- }
} else if (is_namespace_blk(&ndns->dev)) {
struct nd_namespace_blk *nsblk;
nsblk = to_nd_namespace_blk(&ndns->dev);
+ if (uuid_not_set(nsblk->uuid, &ndns->dev, __func__))
+ return ERR_PTR(-ENODEV);
+ if (!nsblk->lbasize) {
+ dev_dbg(&ndns->dev, "%s: sector size not set\n",
+ __func__);
+ return ERR_PTR(-ENODEV);
+ }
if (!nd_namespace_blk_validate(nsblk))
return ERR_PTR(-ENODEV);
}
@@ -1689,6 +1775,18 @@ void nd_region_create_blk_seed(struct nd_region *nd_region)
nd_device_register(nd_region->ns_seed);
}
+void nd_region_create_pfn_seed(struct nd_region *nd_region)
+{
+ WARN_ON(!is_nvdimm_bus_locked(&nd_region->dev));
+ nd_region->pfn_seed = nd_pfn_create(nd_region);
+ /*
+ * Seed creation failures are not fatal, provisioning is simply
+ * disabled until memory becomes available
+ */
+ if (!nd_region->pfn_seed)
+ dev_err(&nd_region->dev, "failed to create pfn namespace\n");
+}
+
void nd_region_create_btt_seed(struct nd_region *nd_region)
{
WARN_ON(!is_nvdimm_bus_locked(&nd_region->dev));
diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h
index 159aed532042..1d1500f3d8b5 100644
--- a/drivers/nvdimm/nd-core.h
+++ b/drivers/nvdimm/nd-core.h
@@ -30,6 +30,7 @@ struct nvdimm_bus {
struct list_head list;
struct device dev;
int id, probe_active;
+ struct list_head poison_list;
struct mutex reconfig_mutex;
};
@@ -52,6 +53,7 @@ void nd_region_probe_success(struct nvdimm_bus *nvdimm_bus, struct device *dev);
struct nd_region;
void nd_region_create_blk_seed(struct nd_region *nd_region);
void nd_region_create_btt_seed(struct nd_region *nd_region);
+void nd_region_create_pfn_seed(struct nd_region *nd_region);
void nd_region_disable(struct nvdimm_bus *nvdimm_bus, struct device *dev);
int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus);
void nvdimm_bus_destroy_ndctl(struct nvdimm_bus *nvdimm_bus);
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index 417e521d299c..ba1633b9da31 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -29,13 +29,12 @@ enum {
ND_MAX_LANES = 256,
SECTOR_SHIFT = 9,
INT_LBASIZE_ALIGNMENT = 64,
-#if IS_ENABLED(CONFIG_NVDIMM_PFN)
- ND_PFN_ALIGN = PAGES_PER_SECTION * PAGE_SIZE,
- ND_PFN_MASK = ND_PFN_ALIGN - 1,
-#else
- ND_PFN_ALIGN = 0,
- ND_PFN_MASK = 0,
-#endif
+};
+
+struct nd_poison {
+ u64 start;
+ u64 length;
+ struct list_head list;
};
struct nvdimm_drvdata {
@@ -153,6 +152,7 @@ struct nd_pfn {
int id;
u8 *uuid;
struct device dev;
+ unsigned long align;
unsigned long npfns;
enum nd_pfn_mode mode;
struct nd_pfn_sb *pfn_sb;
@@ -262,6 +262,8 @@ int nvdimm_namespace_attach_btt(struct nd_namespace_common *ndns);
int nvdimm_namespace_detach_btt(struct nd_namespace_common *ndns);
const char *nvdimm_namespace_disk_name(struct nd_namespace_common *ndns,
char *name);
+void nvdimm_namespace_add_poison(struct nd_namespace_common *ndns,
+ struct badblocks *bb, resource_size_t offset);
int nd_blk_region_init(struct nd_region *nd_region);
void __nd_iostat_start(struct bio *bio, unsigned long *start);
static inline bool nd_iostat_start(struct bio *bio, unsigned long *start)
diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c
index 71805a1aa0f3..f9b674bc49db 100644
--- a/drivers/nvdimm/pfn_devs.c
+++ b/drivers/nvdimm/pfn_devs.c
@@ -103,6 +103,52 @@ static ssize_t mode_store(struct device *dev,
}
static DEVICE_ATTR_RW(mode);
+static ssize_t align_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nd_pfn *nd_pfn = to_nd_pfn(dev);
+
+ return sprintf(buf, "%lx\n", nd_pfn->align);
+}
+
+static ssize_t __align_store(struct nd_pfn *nd_pfn, const char *buf)
+{
+ unsigned long val;
+ int rc;
+
+ rc = kstrtoul(buf, 0, &val);
+ if (rc)
+ return rc;
+
+ if (!is_power_of_2(val) || val < PAGE_SIZE || val > SZ_1G)
+ return -EINVAL;
+
+ if (nd_pfn->dev.driver)
+ return -EBUSY;
+ else
+ nd_pfn->align = val;
+
+ return 0;
+}
+
+static ssize_t align_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct nd_pfn *nd_pfn = to_nd_pfn(dev);
+ ssize_t rc;
+
+ device_lock(dev);
+ nvdimm_bus_lock(dev);
+ rc = __align_store(nd_pfn, buf);
+ dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__,
+ rc, buf, buf[len - 1] == '\n' ? "" : "\n");
+ nvdimm_bus_unlock(dev);
+ device_unlock(dev);
+
+ return rc ? rc : len;
+}
+static DEVICE_ATTR_RW(align);
+
static ssize_t uuid_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -164,6 +210,7 @@ static struct attribute *nd_pfn_attributes[] = {
&dev_attr_mode.attr,
&dev_attr_namespace.attr,
&dev_attr_uuid.attr,
+ &dev_attr_align.attr,
NULL,
};
@@ -179,7 +226,6 @@ static const struct attribute_group *nd_pfn_attribute_groups[] = {
};
static struct device *__nd_pfn_create(struct nd_region *nd_region,
- u8 *uuid, enum nd_pfn_mode mode,
struct nd_namespace_common *ndns)
{
struct nd_pfn *nd_pfn;
@@ -199,10 +245,8 @@ static struct device *__nd_pfn_create(struct nd_region *nd_region,
return NULL;
}
- nd_pfn->mode = mode;
- if (uuid)
- uuid = kmemdup(uuid, 16, GFP_KERNEL);
- nd_pfn->uuid = uuid;
+ nd_pfn->mode = PFN_MODE_NONE;
+ nd_pfn->align = HPAGE_SIZE;
dev = &nd_pfn->dev;
dev_set_name(dev, "pfn%d.%d", nd_region->id, nd_pfn->id);
dev->parent = &nd_region->dev;
@@ -220,8 +264,7 @@ static struct device *__nd_pfn_create(struct nd_region *nd_region,
struct device *nd_pfn_create(struct nd_region *nd_region)
{
- struct device *dev = __nd_pfn_create(nd_region, NULL, PFN_MODE_NONE,
- NULL);
+ struct device *dev = __nd_pfn_create(nd_region, NULL);
if (dev)
__nd_device_register(dev);
@@ -230,10 +273,11 @@ struct device *nd_pfn_create(struct nd_region *nd_region)
int nd_pfn_validate(struct nd_pfn *nd_pfn)
{
- struct nd_namespace_common *ndns = nd_pfn->ndns;
- struct nd_pfn_sb *pfn_sb = nd_pfn->pfn_sb;
- struct nd_namespace_io *nsio;
u64 checksum, offset;
+ struct nd_namespace_io *nsio;
+ struct nd_pfn_sb *pfn_sb = nd_pfn->pfn_sb;
+ struct nd_namespace_common *ndns = nd_pfn->ndns;
+ const u8 *parent_uuid = nd_dev_to_uuid(&ndns->dev);
if (!pfn_sb || !ndns)
return -ENODEV;
@@ -241,10 +285,6 @@ int nd_pfn_validate(struct nd_pfn *nd_pfn)
if (!is_nd_pmem(nd_pfn->dev.parent))
return -ENODEV;
- /* section alignment for simple hotplug */
- if (nvdimm_namespace_capacity(ndns) < ND_PFN_ALIGN)
- return -ENODEV;
-
if (nvdimm_read_bytes(ndns, SZ_4K, pfn_sb, sizeof(*pfn_sb)))
return -ENXIO;
@@ -257,6 +297,9 @@ int nd_pfn_validate(struct nd_pfn *nd_pfn)
return -ENODEV;
pfn_sb->checksum = cpu_to_le64(checksum);
+ if (memcmp(pfn_sb->parent_uuid, parent_uuid, 16) != 0)
+ return -ENODEV;
+
switch (le32_to_cpu(pfn_sb->mode)) {
case PFN_MODE_RAM:
break;
@@ -278,6 +321,12 @@ int nd_pfn_validate(struct nd_pfn *nd_pfn)
return -EINVAL;
}
+ if (nd_pfn->align > nvdimm_namespace_capacity(ndns)) {
+ dev_err(&nd_pfn->dev, "alignment: %lx exceeds capacity %llx\n",
+ nd_pfn->align, nvdimm_namespace_capacity(ndns));
+ return -EINVAL;
+ }
+
/*
* These warnings are verbose because they can only trigger in
* the case where the physical address alignment of the
@@ -286,17 +335,19 @@ int nd_pfn_validate(struct nd_pfn *nd_pfn)
*/
offset = le64_to_cpu(pfn_sb->dataoff);
nsio = to_nd_namespace_io(&ndns->dev);
- if (nsio->res.start & ND_PFN_MASK) {
- dev_err(&nd_pfn->dev,
- "init failed: %s not section aligned\n",
- dev_name(&ndns->dev));
- return -EBUSY;
- } else if (offset >= resource_size(&nsio->res)) {
+ if (offset >= resource_size(&nsio->res)) {
dev_err(&nd_pfn->dev, "pfn array size exceeds capacity of %s\n",
dev_name(&ndns->dev));
return -EBUSY;
}
+ nd_pfn->align = 1UL << ilog2(offset);
+ if (!is_power_of_2(offset) || offset < PAGE_SIZE) {
+ dev_err(&nd_pfn->dev, "bad offset: %#llx dax disabled\n",
+ offset);
+ return -ENXIO;
+ }
+
return 0;
}
EXPORT_SYMBOL(nd_pfn_validate);
@@ -313,7 +364,7 @@ int nd_pfn_probe(struct nd_namespace_common *ndns, void *drvdata)
return -ENODEV;
nvdimm_bus_lock(&ndns->dev);
- dev = __nd_pfn_create(nd_region, NULL, PFN_MODE_NONE, ndns);
+ dev = __nd_pfn_create(nd_region, ndns);
nvdimm_bus_unlock(&ndns->dev);
if (!dev)
return -ENOMEM;
diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index 8ee79893d2f5..b493ff3fccb2 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -23,6 +23,7 @@
#include <linux/module.h>
#include <linux/memory_hotplug.h>
#include <linux/moduleparam.h>
+#include <linux/badblocks.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/pmem.h>
@@ -41,11 +42,25 @@ struct pmem_device {
phys_addr_t data_offset;
void __pmem *virt_addr;
size_t size;
+ struct badblocks bb;
};
static int pmem_major;
-static void pmem_do_bvec(struct pmem_device *pmem, struct page *page,
+static bool is_bad_pmem(struct badblocks *bb, sector_t sector, unsigned int len)
+{
+ if (bb->count) {
+ sector_t first_bad;
+ int num_bad;
+
+ return !!badblocks_check(bb, sector, len / 512, &first_bad,
+ &num_bad);
+ }
+
+ return false;
+}
+
+static int pmem_do_bvec(struct pmem_device *pmem, struct page *page,
unsigned int len, unsigned int off, int rw,
sector_t sector)
{
@@ -54,6 +69,8 @@ static void pmem_do_bvec(struct pmem_device *pmem, struct page *page,
void __pmem *pmem_addr = pmem->virt_addr + pmem_off;
if (rw == READ) {
+ if (unlikely(is_bad_pmem(&pmem->bb, sector, len)))
+ return -EIO;
memcpy_from_pmem(mem + off, pmem_addr, len);
flush_dcache_page(page);
} else {
@@ -62,10 +79,12 @@ static void pmem_do_bvec(struct pmem_device *pmem, struct page *page,
}
kunmap_atomic(mem);
+ return 0;
}
static blk_qc_t pmem_make_request(struct request_queue *q, struct bio *bio)
{
+ int rc = 0;
bool do_acct;
unsigned long start;
struct bio_vec bvec;
@@ -74,9 +93,15 @@ static blk_qc_t pmem_make_request(struct request_queue *q, struct bio *bio)
struct pmem_device *pmem = bdev->bd_disk->private_data;
do_acct = nd_iostat_start(bio, &start);
- bio_for_each_segment(bvec, bio, iter)
- pmem_do_bvec(pmem, bvec.bv_page, bvec.bv_len, bvec.bv_offset,
- bio_data_dir(bio), iter.bi_sector);
+ bio_for_each_segment(bvec, bio, iter) {
+ rc = pmem_do_bvec(pmem, bvec.bv_page, bvec.bv_len,
+ bvec.bv_offset, bio_data_dir(bio),
+ iter.bi_sector);
+ if (rc) {
+ bio->bi_error = rc;
+ break;
+ }
+ }
if (do_acct)
nd_iostat_end(bio, start);
@@ -91,13 +116,22 @@ static int pmem_rw_page(struct block_device *bdev, sector_t sector,
struct page *page, int rw)
{
struct pmem_device *pmem = bdev->bd_disk->private_data;
+ int rc;
- pmem_do_bvec(pmem, page, PAGE_CACHE_SIZE, 0, rw, sector);
+ rc = pmem_do_bvec(pmem, page, PAGE_CACHE_SIZE, 0, rw, sector);
if (rw & WRITE)
wmb_pmem();
- page_endio(page, rw & WRITE, 0);
- return 0;
+ /*
+ * The ->rw_page interface is subtle and tricky. The core
+ * retries on any error, so we can only invoke page_endio() in
+ * the successful completion case. Otherwise, we'll see crashes
+ * caused by double completion.
+ */
+ if (rc == 0)
+ page_endio(page, rw & WRITE, 0);
+
+ return rc;
}
static long pmem_direct_access(struct block_device *bdev, sector_t sector,
@@ -195,7 +229,12 @@ static int pmem_attach_disk(struct device *dev,
disk->driverfs_dev = dev;
set_capacity(disk, (pmem->size - pmem->data_offset) / 512);
pmem->pmem_disk = disk;
+ devm_exit_badblocks(dev, &pmem->bb);
+ if (devm_init_badblocks(dev, &pmem->bb))
+ return -ENOMEM;
+ nvdimm_namespace_add_poison(ndns, &pmem->bb, pmem->data_offset);
+ disk->bb = &pmem->bb;
add_disk(disk);
revalidate_disk(disk);
@@ -212,9 +251,13 @@ static int pmem_rw_bytes(struct nd_namespace_common *ndns,
return -EFAULT;
}
- if (rw == READ)
+ if (rw == READ) {
+ unsigned int sz_align = ALIGN(size + (offset & (512 - 1)), 512);
+
+ if (unlikely(is_bad_pmem(&pmem->bb, offset / 512, sz_align)))
+ return -EIO;
memcpy_from_pmem(buf, pmem->virt_addr + offset, size);
- else {
+ } else {
memcpy_to_pmem(pmem->virt_addr + offset, buf, size);
wmb_pmem();
}
@@ -238,14 +281,11 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn)
nd_pfn->pfn_sb = pfn_sb;
rc = nd_pfn_validate(nd_pfn);
- if (rc == 0 || rc == -EBUSY)
+ if (rc == -ENODEV)
+ /* no info block, do init */;
+ else
return rc;
- /* section alignment for simple hotplug */
- if (nvdimm_namespace_capacity(ndns) < ND_PFN_ALIGN
- || pmem->phys_addr & ND_PFN_MASK)
- return -ENODEV;
-
nd_region = to_nd_region(nd_pfn->dev.parent);
if (nd_region->ro) {
dev_info(&nd_pfn->dev,
@@ -263,9 +303,9 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn)
* ->direct_access() to those that are included in the memmap.
*/
if (nd_pfn->mode == PFN_MODE_PMEM)
- offset = ALIGN(SZ_8K + 64 * npfns, PMD_SIZE);
+ offset = ALIGN(SZ_8K + 64 * npfns, nd_pfn->align);
else if (nd_pfn->mode == PFN_MODE_RAM)
- offset = SZ_8K;
+ offset = ALIGN(SZ_8K, nd_pfn->align);
else
goto err;
@@ -275,6 +315,7 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn)
pfn_sb->npfns = cpu_to_le64(npfns);
memcpy(pfn_sb->signature, PFN_SIG, PFN_SIG_LEN);
memcpy(pfn_sb->uuid, nd_pfn->uuid, 16);
+ memcpy(pfn_sb->parent_uuid, nd_dev_to_uuid(&ndns->dev), 16);
pfn_sb->version_major = cpu_to_le16(1);
checksum = nd_sb_checksum((struct nd_gen_sb *) pfn_sb);
pfn_sb->checksum = cpu_to_le64(checksum);
@@ -326,21 +367,11 @@ static int nvdimm_namespace_attach_pfn(struct nd_namespace_common *ndns)
if (rc)
return rc;
- if (PAGE_SIZE != SZ_4K) {
- dev_err(dev, "only supported on systems with 4K PAGE_SIZE\n");
- return -ENXIO;
- }
- if (nsio->res.start & ND_PFN_MASK) {
- dev_err(dev, "%s not memory hotplug section aligned\n",
- dev_name(&ndns->dev));
- return -ENXIO;
- }
-
pfn_sb = nd_pfn->pfn_sb;
offset = le64_to_cpu(pfn_sb->dataoff);
nd_pfn->mode = le32_to_cpu(nd_pfn->pfn_sb->mode);
if (nd_pfn->mode == PFN_MODE_RAM) {
- if (offset != SZ_8K)
+ if (offset < SZ_8K)
return -EINVAL;
nd_pfn->npfns = le64_to_cpu(pfn_sb->npfns);
altmap = NULL;
@@ -389,6 +420,9 @@ static int nd_pmem_probe(struct device *dev)
pmem->ndns = ndns;
dev_set_drvdata(dev, pmem);
ndns->rw_bytes = pmem_rw_bytes;
+ if (devm_init_badblocks(dev, &pmem->bb))
+ return -ENOMEM;
+ nvdimm_namespace_add_poison(ndns, &pmem->bb, 0);
if (is_nd_btt(dev))
return nvdimm_namespace_attach_btt(ndns);
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c
index 529f3f02e7b2..139bf71ca549 100644
--- a/drivers/nvdimm/region_devs.c
+++ b/drivers/nvdimm/region_devs.c
@@ -134,62 +134,6 @@ int nd_region_to_nstype(struct nd_region *nd_region)
}
EXPORT_SYMBOL(nd_region_to_nstype);
-static int is_uuid_busy(struct device *dev, void *data)
-{
- struct nd_region *nd_region = to_nd_region(dev->parent);
- u8 *uuid = data;
-
- switch (nd_region_to_nstype(nd_region)) {
- case ND_DEVICE_NAMESPACE_PMEM: {
- struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
-
- if (!nspm->uuid)
- break;
- if (memcmp(uuid, nspm->uuid, NSLABEL_UUID_LEN) == 0)
- return -EBUSY;
- break;
- }
- case ND_DEVICE_NAMESPACE_BLK: {
- struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
-
- if (!nsblk->uuid)
- break;
- if (memcmp(uuid, nsblk->uuid, NSLABEL_UUID_LEN) == 0)
- return -EBUSY;
- break;
- }
- default:
- break;
- }
-
- return 0;
-}
-
-static int is_namespace_uuid_busy(struct device *dev, void *data)
-{
- if (is_nd_pmem(dev) || is_nd_blk(dev))
- return device_for_each_child(dev, data, is_uuid_busy);
- return 0;
-}
-
-/**
- * nd_is_uuid_unique - verify that no other namespace has @uuid
- * @dev: any device on a nvdimm_bus
- * @uuid: uuid to check
- */
-bool nd_is_uuid_unique(struct device *dev, u8 *uuid)
-{
- struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
-
- if (!nvdimm_bus)
- return false;
- WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm_bus->dev));
- if (device_for_each_child(&nvdimm_bus->dev, uuid,
- is_namespace_uuid_busy) != 0)
- return false;
- return true;
-}
-
static ssize_t size_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -406,6 +350,9 @@ static umode_t region_visible(struct kobject *kobj, struct attribute *a, int n)
struct nd_interleave_set *nd_set = nd_region->nd_set;
int type = nd_region_to_nstype(nd_region);
+ if (!is_nd_pmem(dev) && a == &dev_attr_pfn_seed.attr)
+ return 0;
+
if (a != &dev_attr_set_cookie.attr
&& a != &dev_attr_available_size.attr)
return a->mode;
@@ -487,6 +434,13 @@ static void nd_region_notify_driver_action(struct nvdimm_bus *nvdimm_bus,
nd_region_create_blk_seed(nd_region);
nvdimm_bus_unlock(dev);
}
+ if (is_nd_pfn(dev) && probe) {
+ nd_region = to_nd_region(dev->parent);
+ nvdimm_bus_lock(dev);
+ if (nd_region->pfn_seed == dev)
+ nd_region_create_pfn_seed(nd_region);
+ nvdimm_bus_unlock(dev);
+ }
}
void nd_region_probe_success(struct nvdimm_bus *nvdimm_bus, struct device *dev)
diff --git a/drivers/nvme/host/Makefile b/drivers/nvme/host/Makefile
index 219dc206fa5f..a5fe23952586 100644
--- a/drivers/nvme/host/Makefile
+++ b/drivers/nvme/host/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_BLK_DEV_NVME) += nvme.o
-nvme-y += pci.o scsi.o lightnvm.o
+lightnvm-$(CONFIG_NVM) := lightnvm.o
+nvme-y += pci.o scsi.o $(lightnvm-y)
diff --git a/drivers/nvme/host/lightnvm.c b/drivers/nvme/host/lightnvm.c
index e0b7b95813bc..15f2acb4d5cd 100644
--- a/drivers/nvme/host/lightnvm.c
+++ b/drivers/nvme/host/lightnvm.c
@@ -22,8 +22,6 @@
#include "nvme.h"
-#ifdef CONFIG_NVM
-
#include <linux/nvme.h>
#include <linux/bitops.h>
#include <linux/lightnvm.h>
@@ -93,7 +91,7 @@ struct nvme_nvm_l2ptbl {
__le16 cdw14[6];
};
-struct nvme_nvm_bbtbl {
+struct nvme_nvm_getbbtbl {
__u8 opcode;
__u8 flags;
__u16 command_id;
@@ -101,10 +99,23 @@ struct nvme_nvm_bbtbl {
__u64 rsvd[2];
__le64 prp1;
__le64 prp2;
- __le32 prp1_len;
- __le32 prp2_len;
- __le32 lbb;
- __u32 rsvd11[3];
+ __le64 spba;
+ __u32 rsvd4[4];
+};
+
+struct nvme_nvm_setbbtbl {
+ __u8 opcode;
+ __u8 flags;
+ __u16 command_id;
+ __le32 nsid;
+ __le64 rsvd[2];
+ __le64 prp1;
+ __le64 prp2;
+ __le64 spba;
+ __le16 nlb;
+ __u8 value;
+ __u8 rsvd3;
+ __u32 rsvd4[3];
};
struct nvme_nvm_erase_blk {
@@ -129,8 +140,8 @@ struct nvme_nvm_command {
struct nvme_nvm_hb_rw hb_rw;
struct nvme_nvm_ph_rw ph_rw;
struct nvme_nvm_l2ptbl l2p;
- struct nvme_nvm_bbtbl get_bb;
- struct nvme_nvm_bbtbl set_bb;
+ struct nvme_nvm_getbbtbl get_bb;
+ struct nvme_nvm_setbbtbl set_bb;
struct nvme_nvm_erase_blk erase;
};
};
@@ -142,11 +153,13 @@ struct nvme_nvm_id_group {
__u8 num_ch;
__u8 num_lun;
__u8 num_pln;
+ __u8 rsvd1;
__le16 num_blk;
__le16 num_pg;
__le16 fpg_sz;
__le16 csecs;
__le16 sos;
+ __le16 rsvd2;
__le32 trdt;
__le32 trdm;
__le32 tprt;
@@ -154,8 +167,9 @@ struct nvme_nvm_id_group {
__le32 tbet;
__le32 tbem;
__le32 mpos;
+ __le32 mccap;
__le16 cpar;
- __u8 reserved[913];
+ __u8 reserved[906];
} __packed;
struct nvme_nvm_addr_format {
@@ -178,15 +192,28 @@ struct nvme_nvm_id {
__u8 ver_id;
__u8 vmnt;
__u8 cgrps;
- __u8 res[5];
+ __u8 res;
__le32 cap;
__le32 dom;
struct nvme_nvm_addr_format ppaf;
- __u8 ppat;
- __u8 resv[223];
+ __u8 resv[228];
struct nvme_nvm_id_group groups[4];
} __packed;
+struct nvme_nvm_bb_tbl {
+ __u8 tblid[4];
+ __le16 verid;
+ __le16 revid;
+ __le32 rvsd1;
+ __le32 tblks;
+ __le32 tfact;
+ __le32 tgrown;
+ __le32 tdresv;
+ __le32 thresv;
+ __le32 rsvd2[8];
+ __u8 blk[0];
+};
+
/*
* Check we didn't inadvertently grow the command struct
*/
@@ -195,12 +222,14 @@ static inline void _nvme_nvm_check_size(void)
BUILD_BUG_ON(sizeof(struct nvme_nvm_identity) != 64);
BUILD_BUG_ON(sizeof(struct nvme_nvm_hb_rw) != 64);
BUILD_BUG_ON(sizeof(struct nvme_nvm_ph_rw) != 64);
- BUILD_BUG_ON(sizeof(struct nvme_nvm_bbtbl) != 64);
+ BUILD_BUG_ON(sizeof(struct nvme_nvm_getbbtbl) != 64);
+ BUILD_BUG_ON(sizeof(struct nvme_nvm_setbbtbl) != 64);
BUILD_BUG_ON(sizeof(struct nvme_nvm_l2ptbl) != 64);
BUILD_BUG_ON(sizeof(struct nvme_nvm_erase_blk) != 64);
BUILD_BUG_ON(sizeof(struct nvme_nvm_id_group) != 960);
BUILD_BUG_ON(sizeof(struct nvme_nvm_addr_format) != 128);
BUILD_BUG_ON(sizeof(struct nvme_nvm_id) != 4096);
+ BUILD_BUG_ON(sizeof(struct nvme_nvm_bb_tbl) != 512);
}
static int init_grps(struct nvm_id *nvm_id, struct nvme_nvm_id *nvme_nvm_id)
@@ -234,6 +263,7 @@ static int init_grps(struct nvm_id *nvm_id, struct nvme_nvm_id *nvme_nvm_id)
dst->tbet = le32_to_cpu(src->tbet);
dst->tbem = le32_to_cpu(src->tbem);
dst->mpos = le32_to_cpu(src->mpos);
+ dst->mccap = le32_to_cpu(src->mccap);
dst->cpar = le16_to_cpu(src->cpar);
}
@@ -241,9 +271,10 @@ static int init_grps(struct nvm_id *nvm_id, struct nvme_nvm_id *nvme_nvm_id)
return 0;
}
-static int nvme_nvm_identity(struct request_queue *q, struct nvm_id *nvm_id)
+static int nvme_nvm_identity(struct nvm_dev *nvmdev, struct nvm_id *nvm_id)
{
- struct nvme_ns *ns = q->queuedata;
+ struct nvme_ns *ns = nvmdev->q->queuedata;
+ struct nvme_dev *dev = ns->dev;
struct nvme_nvm_id *nvme_nvm_id;
struct nvme_nvm_command c = {};
int ret;
@@ -256,8 +287,8 @@ static int nvme_nvm_identity(struct request_queue *q, struct nvm_id *nvm_id)
if (!nvme_nvm_id)
return -ENOMEM;
- ret = nvme_submit_sync_cmd(q, (struct nvme_command *)&c, nvme_nvm_id,
- sizeof(struct nvme_nvm_id));
+ ret = nvme_submit_sync_cmd(dev->admin_q, (struct nvme_command *)&c,
+ nvme_nvm_id, sizeof(struct nvme_nvm_id));
if (ret) {
ret = -EIO;
goto out;
@@ -268,6 +299,8 @@ static int nvme_nvm_identity(struct request_queue *q, struct nvm_id *nvm_id)
nvm_id->cgrps = nvme_nvm_id->cgrps;
nvm_id->cap = le32_to_cpu(nvme_nvm_id->cap);
nvm_id->dom = le32_to_cpu(nvme_nvm_id->dom);
+ memcpy(&nvm_id->ppaf, &nvme_nvm_id->ppaf,
+ sizeof(struct nvme_nvm_addr_format));
ret = init_grps(nvm_id, nvme_nvm_id);
out:
@@ -275,13 +308,13 @@ out:
return ret;
}
-static int nvme_nvm_get_l2p_tbl(struct request_queue *q, u64 slba, u32 nlb,
+static int nvme_nvm_get_l2p_tbl(struct nvm_dev *nvmdev, u64 slba, u32 nlb,
nvm_l2p_update_fn *update_l2p, void *priv)
{
- struct nvme_ns *ns = q->queuedata;
+ struct nvme_ns *ns = nvmdev->q->queuedata;
struct nvme_dev *dev = ns->dev;
struct nvme_nvm_command c = {};
- u32 len = queue_max_hw_sectors(q) << 9;
+ u32 len = queue_max_hw_sectors(dev->admin_q) << 9;
u32 nlb_pr_rq = len / sizeof(u64);
u64 cmd_slba = slba;
void *entries;
@@ -299,8 +332,8 @@ static int nvme_nvm_get_l2p_tbl(struct request_queue *q, u64 slba, u32 nlb,
c.l2p.slba = cpu_to_le64(cmd_slba);
c.l2p.nlb = cpu_to_le32(cmd_nlb);
- ret = nvme_submit_sync_cmd(q, (struct nvme_command *)&c,
- entries, len);
+ ret = nvme_submit_sync_cmd(dev->admin_q,
+ (struct nvme_command *)&c, entries, len);
if (ret) {
dev_err(dev->dev, "L2P table transfer failed (%d)\n",
ret);
@@ -322,43 +355,84 @@ out:
return ret;
}
-static int nvme_nvm_get_bb_tbl(struct request_queue *q, int lunid,
- unsigned int nr_blocks,
- nvm_bb_update_fn *update_bbtbl, void *priv)
+static int nvme_nvm_get_bb_tbl(struct nvm_dev *nvmdev, struct ppa_addr ppa,
+ int nr_blocks, nvm_bb_update_fn *update_bbtbl,
+ void *priv)
{
+ struct request_queue *q = nvmdev->q;
struct nvme_ns *ns = q->queuedata;
struct nvme_dev *dev = ns->dev;
struct nvme_nvm_command c = {};
- void *bb_bitmap;
- u16 bb_bitmap_size;
+ struct nvme_nvm_bb_tbl *bb_tbl;
+ int tblsz = sizeof(struct nvme_nvm_bb_tbl) + nr_blocks;
int ret = 0;
c.get_bb.opcode = nvme_nvm_admin_get_bb_tbl;
c.get_bb.nsid = cpu_to_le32(ns->ns_id);
- c.get_bb.lbb = cpu_to_le32(lunid);
- bb_bitmap_size = ((nr_blocks >> 15) + 1) * PAGE_SIZE;
- bb_bitmap = kmalloc(bb_bitmap_size, GFP_KERNEL);
- if (!bb_bitmap)
- return -ENOMEM;
+ c.get_bb.spba = cpu_to_le64(ppa.ppa);
- bitmap_zero(bb_bitmap, nr_blocks);
+ bb_tbl = kzalloc(tblsz, GFP_KERNEL);
+ if (!bb_tbl)
+ return -ENOMEM;
- ret = nvme_submit_sync_cmd(q, (struct nvme_command *)&c, bb_bitmap,
- bb_bitmap_size);
+ ret = nvme_submit_sync_cmd(dev->admin_q, (struct nvme_command *)&c,
+ bb_tbl, tblsz);
if (ret) {
dev_err(dev->dev, "get bad block table failed (%d)\n", ret);
ret = -EIO;
goto out;
}
- ret = update_bbtbl(lunid, bb_bitmap, nr_blocks, priv);
+ if (bb_tbl->tblid[0] != 'B' || bb_tbl->tblid[1] != 'B' ||
+ bb_tbl->tblid[2] != 'L' || bb_tbl->tblid[3] != 'T') {
+ dev_err(dev->dev, "bbt format mismatch\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (le16_to_cpu(bb_tbl->verid) != 1) {
+ ret = -EINVAL;
+ dev_err(dev->dev, "bbt version not supported\n");
+ goto out;
+ }
+
+ if (le32_to_cpu(bb_tbl->tblks) != nr_blocks) {
+ ret = -EINVAL;
+ dev_err(dev->dev, "bbt unsuspected blocks returned (%u!=%u)",
+ le32_to_cpu(bb_tbl->tblks), nr_blocks);
+ goto out;
+ }
+
+ ppa = dev_to_generic_addr(nvmdev, ppa);
+ ret = update_bbtbl(ppa, nr_blocks, bb_tbl->blk, priv);
if (ret) {
ret = -EINTR;
goto out;
}
out:
- kfree(bb_bitmap);
+ kfree(bb_tbl);
+ return ret;
+}
+
+static int nvme_nvm_set_bb_tbl(struct nvm_dev *nvmdev, struct nvm_rq *rqd,
+ int type)
+{
+ struct nvme_ns *ns = nvmdev->q->queuedata;
+ struct nvme_dev *dev = ns->dev;
+ struct nvme_nvm_command c = {};
+ int ret = 0;
+
+ c.set_bb.opcode = nvme_nvm_admin_set_bb_tbl;
+ c.set_bb.nsid = cpu_to_le32(ns->ns_id);
+ c.set_bb.spba = cpu_to_le64(rqd->ppa_addr.ppa);
+ c.set_bb.nlb = cpu_to_le16(rqd->nr_pages - 1);
+ c.set_bb.value = type;
+
+ ret = nvme_submit_sync_cmd(dev->admin_q, (struct nvme_command *)&c,
+ NULL, 0);
+ if (ret)
+ dev_err(dev->dev, "set bad block table failed (%d)\n", ret);
return ret;
}
@@ -381,7 +455,7 @@ static void nvme_nvm_end_io(struct request *rq, int error)
struct nvm_rq *rqd = rq->end_io_data;
struct nvm_dev *dev = rqd->dev;
- if (dev->mt->end_io(rqd, error))
+ if (dev->mt && dev->mt->end_io(rqd, error))
pr_err("nvme: err status: %x result: %lx\n",
rq->errors, (unsigned long)rq->special);
@@ -389,8 +463,9 @@ static void nvme_nvm_end_io(struct request *rq, int error)
blk_mq_free_request(rq);
}
-static int nvme_nvm_submit_io(struct request_queue *q, struct nvm_rq *rqd)
+static int nvme_nvm_submit_io(struct nvm_dev *dev, struct nvm_rq *rqd)
{
+ struct request_queue *q = dev->q;
struct nvme_ns *ns = q->queuedata;
struct request *rq;
struct bio *bio = rqd->bio;
@@ -428,8 +503,9 @@ static int nvme_nvm_submit_io(struct request_queue *q, struct nvm_rq *rqd)
return 0;
}
-static int nvme_nvm_erase_block(struct request_queue *q, struct nvm_rq *rqd)
+static int nvme_nvm_erase_block(struct nvm_dev *dev, struct nvm_rq *rqd)
{
+ struct request_queue *q = dev->q;
struct nvme_ns *ns = q->queuedata;
struct nvme_nvm_command c = {};
@@ -441,9 +517,9 @@ static int nvme_nvm_erase_block(struct request_queue *q, struct nvm_rq *rqd)
return nvme_submit_sync_cmd(q, (struct nvme_command *)&c, NULL, 0);
}
-static void *nvme_nvm_create_dma_pool(struct request_queue *q, char *name)
+static void *nvme_nvm_create_dma_pool(struct nvm_dev *nvmdev, char *name)
{
- struct nvme_ns *ns = q->queuedata;
+ struct nvme_ns *ns = nvmdev->q->queuedata;
struct nvme_dev *dev = ns->dev;
return dma_pool_create(name, dev->dev, PAGE_SIZE, PAGE_SIZE, 0);
@@ -456,7 +532,7 @@ static void nvme_nvm_destroy_dma_pool(void *pool)
dma_pool_destroy(dma_pool);
}
-static void *nvme_nvm_dev_dma_alloc(struct request_queue *q, void *pool,
+static void *nvme_nvm_dev_dma_alloc(struct nvm_dev *dev, void *pool,
gfp_t mem_flags, dma_addr_t *dma_handler)
{
return dma_pool_alloc(pool, mem_flags, dma_handler);
@@ -474,6 +550,7 @@ static struct nvm_dev_ops nvme_nvm_dev_ops = {
.get_l2p_tbl = nvme_nvm_get_l2p_tbl,
.get_bb_tbl = nvme_nvm_get_bb_tbl,
+ .set_bb_tbl = nvme_nvm_set_bb_tbl,
.submit_io = nvme_nvm_submit_io,
.erase_block = nvme_nvm_erase_block,
@@ -496,31 +573,27 @@ void nvme_nvm_unregister(struct request_queue *q, char *disk_name)
nvm_unregister(disk_name);
}
+/* move to shared place when used in multiple places. */
+#define PCI_VENDOR_ID_CNEX 0x1d1d
+#define PCI_DEVICE_ID_CNEX_WL 0x2807
+#define PCI_DEVICE_ID_CNEX_QEMU 0x1f1f
+
int nvme_nvm_ns_supported(struct nvme_ns *ns, struct nvme_id_ns *id)
{
struct nvme_dev *dev = ns->dev;
struct pci_dev *pdev = to_pci_dev(dev->dev);
/* QEMU NVMe simulator - PCI ID + Vendor specific bit */
- if (pdev->vendor == PCI_VENDOR_ID_INTEL && pdev->device == 0x5845 &&
+ if (pdev->vendor == PCI_VENDOR_ID_CNEX &&
+ pdev->device == PCI_DEVICE_ID_CNEX_QEMU &&
id->vs[0] == 0x1)
return 1;
/* CNEX Labs - PCI ID + Vendor specific bit */
- if (pdev->vendor == 0x1d1d && pdev->device == 0x2807 &&
+ if (pdev->vendor == PCI_VENDOR_ID_CNEX &&
+ pdev->device == PCI_DEVICE_ID_CNEX_WL &&
id->vs[0] == 0x1)
return 1;
return 0;
}
-#else
-int nvme_nvm_register(struct request_queue *q, char *disk_name)
-{
- return 0;
-}
-void nvme_nvm_unregister(struct request_queue *q, char *disk_name) {};
-int nvme_nvm_ns_supported(struct nvme_ns *ns, struct nvme_id_ns *id)
-{
- return 0;
-}
-#endif /* CONFIG_NVM */
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index fdb4e5bad9ac..044253dca30a 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -136,8 +136,22 @@ int nvme_sg_io(struct nvme_ns *ns, struct sg_io_hdr __user *u_hdr);
int nvme_sg_io32(struct nvme_ns *ns, unsigned long arg);
int nvme_sg_get_version_num(int __user *ip);
+#ifdef CONFIG_NVM
int nvme_nvm_ns_supported(struct nvme_ns *ns, struct nvme_id_ns *id);
int nvme_nvm_register(struct request_queue *q, char *disk_name);
void nvme_nvm_unregister(struct request_queue *q, char *disk_name);
+#else
+static inline int nvme_nvm_register(struct request_queue *q, char *disk_name)
+{
+ return 0;
+}
+
+static inline void nvme_nvm_unregister(struct request_queue *q, char *disk_name) {};
+
+static inline int nvme_nvm_ns_supported(struct nvme_ns *ns, struct nvme_id_ns *id)
+{
+ return 0;
+}
+#endif /* CONFIG_NVM */
#endif /* _NVME_H */
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index 8187df204695..0c67b57be83c 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -896,19 +896,28 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx,
goto retry_cmd;
}
if (blk_integrity_rq(req)) {
- if (blk_rq_count_integrity_sg(req->q, req->bio) != 1)
+ if (blk_rq_count_integrity_sg(req->q, req->bio) != 1) {
+ dma_unmap_sg(dev->dev, iod->sg, iod->nents,
+ dma_dir);
goto error_cmd;
+ }
sg_init_table(iod->meta_sg, 1);
if (blk_rq_map_integrity_sg(
- req->q, req->bio, iod->meta_sg) != 1)
+ req->q, req->bio, iod->meta_sg) != 1) {
+ dma_unmap_sg(dev->dev, iod->sg, iod->nents,
+ dma_dir);
goto error_cmd;
+ }
if (rq_data_dir(req))
nvme_dif_remap(req, nvme_dif_prep);
- if (!dma_map_sg(nvmeq->q_dmadev, iod->meta_sg, 1, dma_dir))
+ if (!dma_map_sg(nvmeq->q_dmadev, iod->meta_sg, 1, dma_dir)) {
+ dma_unmap_sg(dev->dev, iod->sg, iod->nents,
+ dma_dir);
goto error_cmd;
+ }
}
}
@@ -968,7 +977,8 @@ static void __nvme_process_cq(struct nvme_queue *nvmeq, unsigned int *tag)
if (head == nvmeq->cq_head && phase == nvmeq->cq_phase)
return;
- writel(head, nvmeq->q_db + nvmeq->dev->db_stride);
+ if (likely(nvmeq->cq_vector >= 0))
+ writel(head, nvmeq->q_db + nvmeq->dev->db_stride);
nvmeq->cq_head = head;
nvmeq->cq_phase = phase;
@@ -1727,9 +1737,13 @@ static int nvme_configure_admin_queue(struct nvme_dev *dev)
u32 aqa;
u64 cap = lo_hi_readq(&dev->bar->cap);
struct nvme_queue *nvmeq;
- unsigned page_shift = PAGE_SHIFT;
+ /*
+ * default to a 4K page size, with the intention to update this
+ * path in the future to accomodate architectures with differing
+ * kernel and IO page sizes.
+ */
+ unsigned page_shift = 12;
unsigned dev_page_min = NVME_CAP_MPSMIN(cap) + 12;
- unsigned dev_page_max = NVME_CAP_MPSMAX(cap) + 12;
if (page_shift < dev_page_min) {
dev_err(dev->dev,
@@ -1738,13 +1752,6 @@ static int nvme_configure_admin_queue(struct nvme_dev *dev)
1 << page_shift);
return -ENODEV;
}
- if (page_shift > dev_page_max) {
- dev_info(dev->dev,
- "Device maximum page size (%u) smaller than "
- "host (%u); enabling work-around\n",
- 1 << dev_page_max, 1 << page_shift);
- page_shift = dev_page_max;
- }
dev->subsystem = readl(&dev->bar->vs) >= NVME_VS(1, 1) ?
NVME_CAP_NSSRC(cap) : 0;
@@ -2268,7 +2275,7 @@ static void nvme_alloc_ns(struct nvme_dev *dev, unsigned nsid)
if (dev->max_hw_sectors) {
blk_queue_max_hw_sectors(ns->queue, dev->max_hw_sectors);
blk_queue_max_segments(ns->queue,
- ((dev->max_hw_sectors << 9) / dev->page_size) + 1);
+ (dev->max_hw_sectors / (dev->page_size >> 9)) + 1);
}
if (dev->stripe_size)
blk_queue_chunk_sectors(ns->queue, dev->stripe_size >> 9);
@@ -2533,8 +2540,17 @@ static void nvme_ns_remove(struct nvme_ns *ns)
{
bool kill = nvme_io_incapable(ns->dev) && !blk_queue_dying(ns->queue);
- if (kill)
+ if (kill) {
blk_set_queue_dying(ns->queue);
+
+ /*
+ * The controller was shutdown first if we got here through
+ * device removal. The shutdown may requeue outstanding
+ * requests. These need to be aborted immediately so
+ * del_gendisk doesn't block indefinitely for their completion.
+ */
+ blk_mq_abort_requeue_list(ns->queue);
+ }
if (ns->disk->flags & GENHD_FL_UP)
del_gendisk(ns->disk);
if (kill || !blk_queue_dying(ns->queue)) {
@@ -2701,6 +2717,18 @@ static int nvme_dev_map(struct nvme_dev *dev)
dev->q_depth = min_t(int, NVME_CAP_MQES(cap) + 1, NVME_Q_DEPTH);
dev->db_stride = 1 << NVME_CAP_STRIDE(cap);
dev->dbs = ((void __iomem *)dev->bar) + 4096;
+
+ /*
+ * Temporary fix for the Apple controller found in the MacBook8,1 and
+ * some MacBook7,1 to avoid controller resets and data loss.
+ */
+ if (pdev->vendor == PCI_VENDOR_ID_APPLE && pdev->device == 0x2001) {
+ dev->q_depth = 2;
+ dev_warn(dev->dev, "detected Apple NVMe controller, set "
+ "queue depth=%u to work around controller resets\n",
+ dev->q_depth);
+ }
+
if (readl(&dev->bar->vs) >= NVME_VS(1, 2))
dev->cmb = nvme_map_cmb(dev);
@@ -2787,6 +2815,10 @@ static void nvme_del_queue_end(struct nvme_queue *nvmeq)
{
struct nvme_delq_ctx *dq = nvmeq->cmdinfo.ctx;
nvme_put_dq(dq);
+
+ spin_lock_irq(&nvmeq->q_lock);
+ nvme_process_cq(nvmeq);
+ spin_unlock_irq(&nvmeq->q_lock);
}
static int adapter_async_del_queue(struct nvme_queue *nvmeq, u8 opcode,
@@ -2954,6 +2986,15 @@ static void nvme_dev_remove(struct nvme_dev *dev)
{
struct nvme_ns *ns, *next;
+ if (nvme_io_incapable(dev)) {
+ /*
+ * If the device is not capable of IO (surprise hot-removal,
+ * for example), we need to quiesce prior to deleting the
+ * namespaces. This will end outstanding requests and prevent
+ * attempts to sync dirty data.
+ */
+ nvme_dev_shutdown(dev);
+ }
list_for_each_entry_safe(ns, next, &dev->namespaces, list)
nvme_ns_remove(ns);
}
diff --git a/drivers/of/address.c b/drivers/of/address.c
index cd53fe4a0c86..91a469d55b8f 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -485,9 +485,10 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus,
int rone;
u64 offset = OF_BAD_ADDR;
- /* Normally, an absence of a "ranges" property means we are
+ /*
+ * Normally, an absence of a "ranges" property means we are
* crossing a non-translatable boundary, and thus the addresses
- * below the current not cannot be converted to CPU physical ones.
+ * below the current cannot be converted to CPU physical ones.
* Unfortunately, while this is very clear in the spec, it's not
* what Apple understood, and they do have things like /uni-n or
* /ht nodes with no "ranges" property and a lot of perfectly
@@ -596,7 +597,7 @@ static u64 __of_translate_address(struct device_node *dev,
pbus = of_match_bus(parent);
pbus->count_cells(dev, &pna, &pns);
if (!OF_CHECK_COUNTS(pna, pns)) {
- printk(KERN_ERR "prom_parse: Bad cell count for %s\n",
+ pr_err("prom_parse: Bad cell count for %s\n",
of_node_full_name(dev));
break;
}
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c
index 53826b84e0ec..c647bd1b6903 100644
--- a/drivers/of/dynamic.c
+++ b/drivers/of/dynamic.c
@@ -646,6 +646,7 @@ void of_changeset_init(struct of_changeset *ocs)
memset(ocs, 0, sizeof(*ocs));
INIT_LIST_HEAD(&ocs->entries);
}
+EXPORT_SYMBOL_GPL(of_changeset_init);
/**
* of_changeset_destroy - Destroy a changeset
@@ -662,20 +663,9 @@ void of_changeset_destroy(struct of_changeset *ocs)
list_for_each_entry_safe_reverse(ce, cen, &ocs->entries, node)
__of_changeset_entry_destroy(ce);
}
+EXPORT_SYMBOL_GPL(of_changeset_destroy);
-/**
- * of_changeset_apply - Applies a changeset
- *
- * @ocs: changeset pointer
- *
- * Applies a changeset to the live tree.
- * Any side-effects of live tree state changes are applied here on
- * sucess, like creation/destruction of devices and side-effects
- * like creation of sysfs properties and directories.
- * Returns 0 on success, a negative error value in case of an error.
- * On error the partially applied effects are reverted.
- */
-int of_changeset_apply(struct of_changeset *ocs)
+int __of_changeset_apply(struct of_changeset *ocs)
{
struct of_changeset_entry *ce;
int ret;
@@ -704,17 +694,30 @@ int of_changeset_apply(struct of_changeset *ocs)
}
/**
- * of_changeset_revert - Reverts an applied changeset
+ * of_changeset_apply - Applies a changeset
*
* @ocs: changeset pointer
*
- * Reverts a changeset returning the state of the tree to what it
- * was before the application.
- * Any side-effects like creation/destruction of devices and
- * removal of sysfs properties and directories are applied.
+ * Applies a changeset to the live tree.
+ * Any side-effects of live tree state changes are applied here on
+ * success, like creation/destruction of devices and side-effects
+ * like creation of sysfs properties and directories.
* Returns 0 on success, a negative error value in case of an error.
+ * On error the partially applied effects are reverted.
*/
-int of_changeset_revert(struct of_changeset *ocs)
+int of_changeset_apply(struct of_changeset *ocs)
+{
+ int ret;
+
+ mutex_lock(&of_mutex);
+ ret = __of_changeset_apply(ocs);
+ mutex_unlock(&of_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(of_changeset_apply);
+
+int __of_changeset_revert(struct of_changeset *ocs)
{
struct of_changeset_entry *ce;
int ret;
@@ -742,6 +745,29 @@ int of_changeset_revert(struct of_changeset *ocs)
}
/**
+ * of_changeset_revert - Reverts an applied changeset
+ *
+ * @ocs: changeset pointer
+ *
+ * Reverts a changeset returning the state of the tree to what it
+ * was before the application.
+ * Any side-effects like creation/destruction of devices and
+ * removal of sysfs properties and directories are applied.
+ * Returns 0 on success, a negative error value in case of an error.
+ */
+int of_changeset_revert(struct of_changeset *ocs)
+{
+ int ret;
+
+ mutex_lock(&of_mutex);
+ ret = __of_changeset_revert(ocs);
+ mutex_unlock(&of_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(of_changeset_revert);
+
+/**
* of_changeset_action - Perform a changeset action
*
* @ocs: changeset pointer
@@ -779,3 +805,4 @@ int of_changeset_action(struct of_changeset *ocs, unsigned long action,
list_add_tail(&ce->node, &ocs->entries);
return 0;
}
+EXPORT_SYMBOL_GPL(of_changeset_action);
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index d2430298a309..655f79db7899 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/initrd.h>
#include <linux/memblock.h>
+#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_reserved_mem.h>
@@ -436,6 +437,8 @@ static void *kernel_tree_alloc(u64 size, u64 align)
return kzalloc(size, GFP_KERNEL);
}
+static DEFINE_MUTEX(of_fdt_unflatten_mutex);
+
/**
* of_fdt_unflatten_tree - create tree of device_nodes from flat blob
*
@@ -447,7 +450,9 @@ static void *kernel_tree_alloc(u64 size, u64 align)
void of_fdt_unflatten_tree(const unsigned long *blob,
struct device_node **mynodes)
{
+ mutex_lock(&of_fdt_unflatten_mutex);
__unflatten_device_tree(blob, mynodes, &kernel_tree_alloc);
+ mutex_unlock(&of_fdt_unflatten_mutex);
}
EXPORT_SYMBOL_GPL(of_fdt_unflatten_tree);
@@ -1041,7 +1046,7 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size)
int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
phys_addr_t size, bool nomap)
{
- pr_err("Reserved memory not supported, ignoring range 0x%pa - 0x%pa%s\n",
+ pr_err("Reserved memory not supported, ignoring range %pa - %pa%s\n",
&base, &size, nomap ? " (nomap)" : "");
return -ENOSYS;
}
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 902b89be7217..706e3ff67f8b 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -53,7 +53,7 @@ EXPORT_SYMBOL_GPL(irq_of_parse_and_map);
* Returns a pointer to the interrupt parent node, or NULL if the interrupt
* parent could not be determined.
*/
-static struct device_node *of_irq_find_parent(struct device_node *child)
+struct device_node *of_irq_find_parent(struct device_node *child)
{
struct device_node *p;
const __be32 *parp;
@@ -77,6 +77,7 @@ static struct device_node *of_irq_find_parent(struct device_node *child)
return p;
}
+EXPORT_SYMBOL_GPL(of_irq_find_parent);
/**
* of_irq_parse_raw - Low level interrupt tree parsing
@@ -472,6 +473,7 @@ EXPORT_SYMBOL_GPL(of_irq_to_resource_table);
struct of_intc_desc {
struct list_head list;
+ of_irq_init_cb_t irq_init_cb;
struct device_node *dev;
struct device_node *interrupt_parent;
};
@@ -485,6 +487,7 @@ struct of_intc_desc {
*/
void __init of_irq_init(const struct of_device_id *matches)
{
+ const struct of_device_id *match;
struct device_node *np, *parent = NULL;
struct of_intc_desc *desc, *temp_desc;
struct list_head intc_desc_list, intc_parent_list;
@@ -492,10 +495,15 @@ void __init of_irq_init(const struct of_device_id *matches)
INIT_LIST_HEAD(&intc_desc_list);
INIT_LIST_HEAD(&intc_parent_list);
- for_each_matching_node(np, matches) {
+ for_each_matching_node_and_match(np, matches, &match) {
if (!of_find_property(np, "interrupt-controller", NULL) ||
!of_device_is_available(np))
continue;
+
+ if (WARN(!match->data, "of_irq_init: no init function for %s\n",
+ match->compatible))
+ continue;
+
/*
* Here, we allocate and populate an of_intc_desc with the node
* pointer, interrupt-parent device_node etc.
@@ -506,6 +514,7 @@ void __init of_irq_init(const struct of_device_id *matches)
goto err;
}
+ desc->irq_init_cb = match->data;
desc->dev = of_node_get(np);
desc->interrupt_parent = of_irq_find_parent(np);
if (desc->interrupt_parent == np)
@@ -525,27 +534,18 @@ void __init of_irq_init(const struct of_device_id *matches)
* The assumption is that NULL parent means a root controller.
*/
list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
- const struct of_device_id *match;
int ret;
- of_irq_init_cb_t irq_init_cb;
if (desc->interrupt_parent != parent)
continue;
list_del(&desc->list);
- match = of_match_node(matches, desc->dev);
- if (WARN(!match->data,
- "of_irq_init: no init function for %s\n",
- match->compatible)) {
- kfree(desc);
- continue;
- }
- pr_debug("of_irq_init: init %s @ %p, parent %p\n",
- match->compatible,
+ pr_debug("of_irq_init: init %s (%p), parent %p\n",
+ desc->dev->full_name,
desc->dev, desc->interrupt_parent);
- irq_init_cb = (of_irq_init_cb_t)match->data;
- ret = irq_init_cb(desc->dev, desc->interrupt_parent);
+ ret = desc->irq_init_cb(desc->dev,
+ desc->interrupt_parent);
if (ret) {
kfree(desc);
continue;
diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c
index a87a868fed64..86829f8064a6 100644
--- a/drivers/of/of_mdio.c
+++ b/drivers/of/of_mdio.c
@@ -62,11 +62,9 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi
rc = irq_of_parse_and_map(child, 0);
if (rc > 0) {
phy->irq = rc;
- if (mdio->irq)
- mdio->irq[addr] = rc;
+ mdio->irq[addr] = rc;
} else {
- if (mdio->irq)
- phy->irq = mdio->irq[addr];
+ phy->irq = mdio->irq[addr];
}
if (of_property_read_bool(child, "broken-turn-around"))
@@ -75,7 +73,7 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi
/* Associate the OF node with the device structure so it
* can be looked up later */
of_node_get(child);
- phy->dev.of_node = child;
+ phy->mdio.dev.of_node = child;
/* All data is now stored in the phy struct;
* register it */
@@ -92,6 +90,37 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi
return 0;
}
+static int of_mdiobus_register_device(struct mii_bus *mdio,
+ struct device_node *child,
+ u32 addr)
+{
+ struct mdio_device *mdiodev;
+ int rc;
+
+ mdiodev = mdio_device_create(mdio, addr);
+ if (!mdiodev || IS_ERR(mdiodev))
+ return 1;
+
+ /* Associate the OF node with the device structure so it
+ * can be looked up later.
+ */
+ of_node_get(child);
+ mdiodev->dev.of_node = child;
+
+ /* All data is now stored in the mdiodev struct; register it. */
+ rc = mdio_device_register(mdiodev);
+ if (rc) {
+ mdio_device_free(mdiodev);
+ of_node_put(child);
+ return 1;
+ }
+
+ dev_dbg(&mdio->dev, "registered mdio device %s at address %i\n",
+ child->name, addr);
+
+ return 0;
+}
+
int of_mdio_parse_addr(struct device *dev, const struct device_node *np)
{
u32 addr;
@@ -114,6 +143,35 @@ int of_mdio_parse_addr(struct device *dev, const struct device_node *np)
}
EXPORT_SYMBOL(of_mdio_parse_addr);
+/*
+ * Return true if the child node is for a phy. It must either:
+ * o Compatible string of "ethernet-phy-idX.X"
+ * o Compatible string of "ethernet-phy-ieee802.3-c45"
+ * o Compatible string of "ethernet-phy-ieee802.3-c22"
+ * o No compatibility string
+ *
+ * A device which is not a phy is expected to have a compatible string
+ * indicating what sort of device it is.
+ */
+static bool of_mdiobus_child_is_phy(struct device_node *child)
+{
+ u32 phy_id;
+
+ if (of_get_phy_id(child, &phy_id) != -EINVAL)
+ return true;
+
+ if (of_device_is_compatible(child, "ethernet-phy-ieee802.3-c45"))
+ return true;
+
+ if (of_device_is_compatible(child, "ethernet-phy-ieee802.3-c22"))
+ return true;
+
+ if (!of_find_property(child, "compatible", NULL))
+ return true;
+
+ return false;
+}
+
/**
* of_mdiobus_register - Register mii_bus and create PHYs from the device tree
* @mdio: pointer to mii_bus structure
@@ -127,17 +185,12 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
struct device_node *child;
const __be32 *paddr;
bool scanphys = false;
- int addr, rc, i;
+ int addr, rc;
/* Mask out all PHYs from auto probing. Instead the PHYs listed in
* the device tree are populated after the bus has been registered */
mdio->phy_mask = ~0;
- /* Clear all the IRQ properties */
- if (mdio->irq)
- for (i=0; i<PHY_MAX_ADDR; i++)
- mdio->irq[i] = PHY_POLL;
-
mdio->dev.of_node = np;
/* Register the MDIO bus */
@@ -145,7 +198,7 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
if (rc)
return rc;
- /* Loop over the child nodes and register a phy_device for each one */
+ /* Loop over the child nodes and register a phy_device for each phy */
for_each_available_child_of_node(np, child) {
addr = of_mdio_parse_addr(&mdio->dev, child);
if (addr < 0) {
@@ -153,9 +206,10 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
continue;
}
- rc = of_mdiobus_register_phy(mdio, child, addr);
- if (rc)
- continue;
+ if (of_mdiobus_child_is_phy(child))
+ of_mdiobus_register_phy(mdio, child, addr);
+ else
+ of_mdiobus_register_device(mdio, child, addr);
}
if (!scanphys)
@@ -170,16 +224,15 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
/* skip already registered PHYs */
- if (mdio->phy_map[addr])
+ if (mdiobus_is_registered_device(mdio, addr))
continue;
/* be noisy to encourage people to set reg property */
dev_info(&mdio->dev, "scan phy %s at address %i\n",
child->name, addr);
- rc = of_mdiobus_register_phy(mdio, child, addr);
- if (rc)
- continue;
+ if (of_mdiobus_child_is_phy(child))
+ of_mdiobus_register_phy(mdio, child, addr);
}
}
@@ -238,7 +291,7 @@ struct phy_device *of_phy_connect(struct net_device *dev,
ret = phy_connect_direct(dev, phy, hndlr, iface);
/* refcount is held by phy_connect_direct() on success */
- put_device(&phy->dev);
+ put_device(&phy->mdio.dev);
return ret ? NULL : phy;
}
@@ -268,7 +321,7 @@ struct phy_device *of_phy_attach(struct net_device *dev,
ret = phy_attach_direct(dev, phy, flags, iface);
/* refcount is held by phy_attach_direct() on success */
- put_device(&phy->dev);
+ put_device(&phy->mdio.dev);
return ret ? NULL : phy;
}
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
index 8e882e706cd8..829469faeb23 100644
--- a/drivers/of/of_private.h
+++ b/drivers/of/of_private.h
@@ -45,6 +45,8 @@ static inline struct device_node *kobj_to_device_node(struct kobject *kobj)
extern int of_property_notify(int action, struct device_node *np,
struct property *prop, struct property *old_prop);
extern void of_node_release(struct kobject *kobj);
+extern int __of_changeset_apply(struct of_changeset *ocs);
+extern int __of_changeset_revert(struct of_changeset *ocs);
#else /* CONFIG_OF_DYNAMIC */
static inline int of_property_notify(int action, struct device_node *np,
struct property *prop, struct property *old_prop)
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index be77e75c587d..1a3556a9e9ea 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -206,7 +206,13 @@ static int __init __rmem_cmp(const void *a, const void *b)
{
const struct reserved_mem *ra = a, *rb = b;
- return ra->base - rb->base;
+ if (ra->base < rb->base)
+ return -1;
+
+ if (ra->base > rb->base)
+ return 1;
+
+ return 0;
}
static void __init __rmem_check_for_overlap(void)
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index 54e5af9d7377..82250815e9a5 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -379,9 +379,9 @@ int of_overlay_create(struct device_node *tree)
}
/* apply the changeset */
- err = of_changeset_apply(&ov->cset);
+ err = __of_changeset_apply(&ov->cset);
if (err) {
- pr_err("%s: of_changeset_apply() failed for tree@%s\n",
+ pr_err("%s: __of_changeset_apply() failed for tree@%s\n",
__func__, tree->full_name);
goto err_revert_overlay;
}
@@ -511,7 +511,7 @@ int of_overlay_destroy(int id)
list_del(&ov->node);
- of_changeset_revert(&ov->cset);
+ __of_changeset_revert(&ov->cset);
of_free_overlay_info(ov);
idr_remove(&ov_idr, id);
of_changeset_destroy(&ov->cset);
@@ -542,7 +542,7 @@ int of_overlay_destroy_all(void)
/* the tail of list is guaranteed to be safe to remove */
list_for_each_entry_safe_reverse(ov, ovn, &ov_list, node) {
list_del(&ov->node);
- of_changeset_revert(&ov->cset);
+ __of_changeset_revert(&ov->cset);
of_free_overlay_info(ov);
idr_remove(&ov_idr, ov->id);
kfree(ov);
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index af98343614d8..8d103e4968be 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -31,6 +31,7 @@ const struct of_device_id of_default_bus_match_table[] = {
#endif /* CONFIG_ARM_AMBA */
{} /* Empty terminated list */
};
+EXPORT_SYMBOL(of_default_bus_match_table);
static int of_dev_node_match(struct device *dev, void *data)
{
diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c
index e16ea5717b7f..979b6e415cea 100644
--- a/drivers/of/unittest.c
+++ b/drivers/of/unittest.c
@@ -530,18 +530,14 @@ static void __init of_unittest_changeset(void)
unittest(!of_changeset_add_property(&chgset, parent, ppadd), "fail add prop\n");
unittest(!of_changeset_update_property(&chgset, parent, ppupdate), "fail update prop\n");
unittest(!of_changeset_remove_property(&chgset, parent, ppremove), "fail remove prop\n");
- mutex_lock(&of_mutex);
unittest(!of_changeset_apply(&chgset), "apply failed\n");
- mutex_unlock(&of_mutex);
/* Make sure node names are constructed correctly */
unittest((np = of_find_node_by_path("/testcase-data/changeset/n2/n21")),
"'%s' not added\n", n21->full_name);
of_node_put(np);
- mutex_lock(&of_mutex);
unittest(!of_changeset_revert(&chgset), "revert failed\n");
- mutex_unlock(&of_mutex);
of_changeset_destroy(&chgset);
#endif
@@ -757,6 +753,11 @@ static void __init of_unittest_match_node(void)
}
}
+static struct resource test_bus_res = {
+ .start = 0xfffffff8,
+ .end = 0xfffffff9,
+ .flags = IORESOURCE_MEM,
+};
static const struct platform_device_info test_bus_info = {
.name = "unittest-bus",
};
@@ -800,6 +801,15 @@ static void __init of_unittest_platform_populate(void)
return;
test_bus->dev.of_node = np;
+ /*
+ * Add a dummy resource to the test bus node after it is
+ * registered to catch problems with un-inserted resources. The
+ * DT code doesn't insert the resources, and it has caused the
+ * kernel to oops in the past. This makes sure the same bug
+ * doesn't crop up again.
+ */
+ platform_device_add_resources(test_bus, &test_bus_res, 1);
+
of_platform_populate(np, match, NULL, &test_bus->dev);
for_each_child_of_node(np, child) {
for_each_child_of_node(child, grandchild)
diff --git a/drivers/parisc/iommu-helpers.h b/drivers/parisc/iommu-helpers.h
index 761e77bfce5d..e56f1569f6c3 100644
--- a/drivers/parisc/iommu-helpers.h
+++ b/drivers/parisc/iommu-helpers.h
@@ -104,7 +104,11 @@ iommu_coalesce_chunks(struct ioc *ioc, struct device *dev,
struct scatterlist *contig_sg; /* contig chunk head */
unsigned long dma_offset, dma_len; /* start/len of DMA stream */
unsigned int n_mappings = 0;
- unsigned int max_seg_size = dma_get_max_seg_size(dev);
+ unsigned int max_seg_size = min(dma_get_max_seg_size(dev),
+ (unsigned)DMA_CHUNK_SIZE);
+ unsigned int max_seg_boundary = dma_get_seg_boundary(dev) + 1;
+ if (max_seg_boundary) /* check if the addition above didn't overflow */
+ max_seg_size = min(max_seg_size, max_seg_boundary);
while (nents > 0) {
@@ -138,14 +142,11 @@ iommu_coalesce_chunks(struct ioc *ioc, struct device *dev,
/*
** First make sure current dma stream won't
- ** exceed DMA_CHUNK_SIZE if we coalesce the
+ ** exceed max_seg_size if we coalesce the
** next entry.
*/
- if(unlikely(ALIGN(dma_len + dma_offset + startsg->length,
- IOVP_SIZE) > DMA_CHUNK_SIZE))
- break;
-
- if (startsg->length + dma_len > max_seg_size)
+ if (unlikely(ALIGN(dma_len + dma_offset + startsg->length, IOVP_SIZE) >
+ max_seg_size))
break;
/*
diff --git a/drivers/parport/share.c b/drivers/parport/share.c
index 5ce5ef211bdb..3308427ed9f7 100644
--- a/drivers/parport/share.c
+++ b/drivers/parport/share.c
@@ -1,6 +1,6 @@
/*
* Parallel-port resource manager code.
- *
+ *
* Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
* Tim Waugh <tim@cyberelk.demon.co.uk>
* Jose Renau <renau@acm.org>
@@ -54,16 +54,16 @@ static LIST_HEAD(drivers);
static DEFINE_MUTEX(registration_lock);
/* What you can do to a port that's gone away.. */
-static void dead_write_lines (struct parport *p, unsigned char b){}
-static unsigned char dead_read_lines (struct parport *p) { return 0; }
-static unsigned char dead_frob_lines (struct parport *p, unsigned char b,
+static void dead_write_lines(struct parport *p, unsigned char b){}
+static unsigned char dead_read_lines(struct parport *p) { return 0; }
+static unsigned char dead_frob_lines(struct parport *p, unsigned char b,
unsigned char c) { return 0; }
-static void dead_onearg (struct parport *p){}
-static void dead_initstate (struct pardevice *d, struct parport_state *s) { }
-static void dead_state (struct parport *p, struct parport_state *s) { }
-static size_t dead_write (struct parport *p, const void *b, size_t l, int f)
+static void dead_onearg(struct parport *p){}
+static void dead_initstate(struct pardevice *d, struct parport_state *s) { }
+static void dead_state(struct parport *p, struct parport_state *s) { }
+static size_t dead_write(struct parport *p, const void *b, size_t l, int f)
{ return 0; }
-static size_t dead_read (struct parport *p, void *b, size_t l, int f)
+static size_t dead_read(struct parport *p, void *b, size_t l, int f)
{ return 0; }
static struct parport_operations dead_ops = {
.write_data = dead_write_lines, /* data */
@@ -93,7 +93,7 @@ static struct parport_operations dead_ops = {
.ecp_write_data = dead_write, /* ecp */
.ecp_read_data = dead_read,
.ecp_write_addr = dead_write,
-
+
.compat_write_data = dead_write, /* compat */
.nibble_read_data = dead_read, /* nibble */
.byte_read_data = dead_read, /* byte */
@@ -148,7 +148,7 @@ void parport_bus_exit(void)
/*
* iterates through all the drivers registered with the bus and sends the port
* details to the match_port callback of the driver, so that the driver can
- * know about the new port that just regsitered with the bus and decide if it
+ * know about the new port that just registered with the bus and decide if it
* wants to use this new port.
*/
static int driver_check(struct device_driver *dev_drv, void *_port)
@@ -194,7 +194,7 @@ static void detach_driver_chain(struct parport *port)
struct parport_driver *drv;
/* caller has exclusive registration_lock */
list_for_each_entry(drv, &drivers, list)
- drv->detach (port);
+ drv->detach(port);
/*
* call the detach function of the drivers registered in
@@ -205,11 +205,13 @@ static void detach_driver_chain(struct parport *port)
}
/* Ask kmod for some lowlevel drivers. */
-static void get_lowlevel_driver (void)
+static void get_lowlevel_driver(void)
{
- /* There is no actual module called this: you should set
- * up an alias for modutils. */
- request_module ("parport_lowlevel");
+ /*
+ * There is no actual module called this: you should set
+ * up an alias for modutils.
+ */
+ request_module("parport_lowlevel");
}
/*
@@ -265,7 +267,7 @@ int __parport_register_driver(struct parport_driver *drv, struct module *owner,
const char *mod_name)
{
if (list_empty(&portlist))
- get_lowlevel_driver ();
+ get_lowlevel_driver();
if (drv->devmodel) {
/* using device model */
@@ -328,7 +330,7 @@ static int port_detach(struct device *dev, void *_drv)
* finished by the time this function returns.
**/
-void parport_unregister_driver (struct parport_driver *drv)
+void parport_unregister_driver(struct parport_driver *drv)
{
struct parport *port;
@@ -343,6 +345,7 @@ void parport_unregister_driver (struct parport_driver *drv)
}
mutex_unlock(&registration_lock);
}
+EXPORT_SYMBOL(parport_unregister_driver);
static void free_port(struct device *dev)
{
@@ -372,12 +375,13 @@ static void free_port(struct device *dev)
* until the matching parport_put_port() call.
**/
-struct parport *parport_get_port (struct parport *port)
+struct parport *parport_get_port(struct parport *port)
{
struct device *dev = get_device(&port->bus_dev);
return to_parport_dev(dev);
}
+EXPORT_SYMBOL(parport_get_port);
void parport_del_port(struct parport *port)
{
@@ -394,10 +398,11 @@ EXPORT_SYMBOL(parport_del_port);
* zero (port is no longer used), free_port is called.
**/
-void parport_put_port (struct parport *port)
+void parport_put_port(struct parport *port)
{
put_device(&port->bus_dev);
}
+EXPORT_SYMBOL(parport_put_port);
/**
* parport_register_port - register a parallel port
@@ -439,10 +444,8 @@ struct parport *parport_register_port(unsigned long base, int irq, int dma,
int ret;
tmp = kzalloc(sizeof(struct parport), GFP_KERNEL);
- if (!tmp) {
- printk(KERN_WARNING "parport: memory squeeze\n");
+ if (!tmp)
return NULL;
- }
/* Init our structure */
tmp->base = base;
@@ -450,12 +453,12 @@ struct parport *parport_register_port(unsigned long base, int irq, int dma,
tmp->dma = dma;
tmp->muxport = tmp->daisy = tmp->muxsel = -1;
tmp->modes = 0;
- INIT_LIST_HEAD(&tmp->list);
+ INIT_LIST_HEAD(&tmp->list);
tmp->devices = tmp->cad = NULL;
tmp->flags = 0;
tmp->ops = ops;
tmp->physport = tmp;
- memset (tmp->probe_info, 0, 5 * sizeof (struct parport_device_info));
+ memset(tmp->probe_info, 0, 5 * sizeof(struct parport_device_info));
rwlock_init(&tmp->cad_lock);
spin_lock_init(&tmp->waitlist_lock);
spin_lock_init(&tmp->pardevice_lock);
@@ -463,12 +466,11 @@ struct parport *parport_register_port(unsigned long base, int irq, int dma,
tmp->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
sema_init(&tmp->ieee1284.irq, 0);
tmp->spintime = parport_default_spintime;
- atomic_set (&tmp->ref_count, 1);
+ atomic_set(&tmp->ref_count, 1);
INIT_LIST_HEAD(&tmp->full_list);
name = kmalloc(15, GFP_KERNEL);
if (!name) {
- printk(KERN_ERR "parport: memory squeeze\n");
kfree(tmp);
return NULL;
}
@@ -508,6 +510,7 @@ struct parport *parport_register_port(unsigned long base, int irq, int dma,
return tmp;
}
+EXPORT_SYMBOL(parport_register_port);
/**
* parport_announce_port - tell device drivers about a parallel port
@@ -521,7 +524,7 @@ struct parport *parport_register_port(unsigned long base, int irq, int dma,
* functions will be called, with @port as the parameter.
**/
-void parport_announce_port (struct parport *port)
+void parport_announce_port(struct parport *port)
{
int i;
@@ -531,9 +534,8 @@ void parport_announce_port (struct parport *port)
#endif
if (!port->dev)
- printk(KERN_WARNING "%s: fix this legacy "
- "no-device port driver!\n",
- port->name);
+ printk(KERN_WARNING "%s: fix this legacy no-device port driver!\n",
+ port->name);
parport_proc_register(port);
mutex_lock(&registration_lock);
@@ -547,7 +549,7 @@ void parport_announce_port (struct parport *port)
spin_unlock_irq(&parportlist_lock);
/* Let drivers know that new port(s) has arrived. */
- attach_driver_chain (port);
+ attach_driver_chain(port);
for (i = 1; i < 3; i++) {
struct parport *slave = port->slaves[i-1];
if (slave)
@@ -555,6 +557,7 @@ void parport_announce_port (struct parport *port)
}
mutex_unlock(&registration_lock);
}
+EXPORT_SYMBOL(parport_announce_port);
/**
* parport_remove_port - deregister a parallel port
@@ -582,7 +585,7 @@ void parport_remove_port(struct parport *port)
mutex_lock(&registration_lock);
/* Spread the word. */
- detach_driver_chain (port);
+ detach_driver_chain(port);
#ifdef CONFIG_PARPORT_1284
/* Forget the IEEE1284.3 topology of the port. */
@@ -616,6 +619,7 @@ void parport_remove_port(struct parport *port)
parport_put_port(slave);
}
}
+EXPORT_SYMBOL(parport_remove_port);
/**
* parport_register_device - register a device on a parallel port
@@ -689,14 +693,14 @@ void parport_remove_port(struct parport *port)
struct pardevice *
parport_register_device(struct parport *port, const char *name,
int (*pf)(void *), void (*kf)(void *),
- void (*irq_func)(void *),
+ void (*irq_func)(void *),
int flags, void *handle)
{
struct pardevice *tmp;
if (port->physport->flags & PARPORT_FLAG_EXCL) {
/* An exclusive device is registered. */
- printk (KERN_DEBUG "%s: no more devices allowed\n",
+ printk(KERN_DEBUG "%s: no more devices allowed\n",
port->name);
return NULL;
}
@@ -722,28 +726,24 @@ parport_register_device(struct parport *port, const char *name,
}
}
- /* We up our own module reference count, and that of the port
- on which a device is to be registered, to ensure that
- neither of us gets unloaded while we sleep in (e.g.)
- kmalloc.
- */
- if (!try_module_get(port->ops->owner)) {
+ /*
+ * We up our own module reference count, and that of the port
+ * on which a device is to be registered, to ensure that
+ * neither of us gets unloaded while we sleep in (e.g.)
+ * kmalloc.
+ */
+ if (!try_module_get(port->ops->owner))
return NULL;
- }
-
- parport_get_port (port);
+
+ parport_get_port(port);
tmp = kmalloc(sizeof(struct pardevice), GFP_KERNEL);
- if (tmp == NULL) {
- printk(KERN_WARNING "%s: memory squeeze, couldn't register %s.\n", port->name, name);
+ if (!tmp)
goto out;
- }
tmp->state = kmalloc(sizeof(struct parport_state), GFP_KERNEL);
- if (tmp->state == NULL) {
- printk(KERN_WARNING "%s: memory squeeze, couldn't register %s.\n", port->name, name);
+ if (!tmp->state)
goto out_free_pardevice;
- }
tmp->name = name;
tmp->port = port;
@@ -767,19 +767,21 @@ parport_register_device(struct parport *port, const char *name,
if (flags & PARPORT_DEV_EXCL) {
if (port->physport->devices) {
- spin_unlock (&port->physport->pardevice_lock);
- printk (KERN_DEBUG
- "%s: cannot grant exclusive access for "
- "device %s\n", port->name, name);
+ spin_unlock(&port->physport->pardevice_lock);
+ printk(KERN_DEBUG
+ "%s: cannot grant exclusive access for device %s\n",
+ port->name, name);
goto out_free_all;
}
port->flags |= PARPORT_FLAG_EXCL;
}
tmp->next = port->physport->devices;
- wmb(); /* Make sure that tmp->next is written before it's
- added to the list; see comments marked 'no locking
- required' */
+ wmb(); /*
+ * Make sure that tmp->next is written before it's
+ * added to the list; see comments marked 'no locking
+ * required'
+ */
if (port->physport->devices)
port->physport->devices->prev = tmp;
port->physport->devices = tmp;
@@ -805,11 +807,12 @@ parport_register_device(struct parport *port, const char *name,
out_free_pardevice:
kfree(tmp);
out:
- parport_put_port (port);
+ parport_put_port(port);
module_put(port->ops->owner);
return NULL;
}
+EXPORT_SYMBOL(parport_register_device);
static void free_pardevice(struct device *dev)
{
@@ -968,7 +971,7 @@ void parport_unregister_device(struct pardevice *dev)
struct parport *port;
#ifdef PARPORT_PARANOID
- if (dev == NULL) {
+ if (!dev) {
printk(KERN_ERR "parport_unregister_device: passed NULL\n");
return;
}
@@ -985,7 +988,7 @@ void parport_unregister_device(struct pardevice *dev)
if (port->cad == dev) {
printk(KERN_DEBUG "%s: %s forgot to release port\n",
port->name, dev->name);
- parport_release (dev);
+ parport_release(dev);
}
spin_lock(&port->pardevice_lock);
@@ -1001,8 +1004,10 @@ void parport_unregister_device(struct pardevice *dev)
spin_unlock(&port->pardevice_lock);
- /* Make sure we haven't left any pointers around in the wait
- * list. */
+ /*
+ * Make sure we haven't left any pointers around in the wait
+ * list.
+ */
spin_lock_irq(&port->waitlist_lock);
if (dev->waitprev || dev->waitnext || port->waithead == dev) {
if (dev->waitprev)
@@ -1023,8 +1028,9 @@ void parport_unregister_device(struct pardevice *dev)
kfree(dev);
module_put(port->ops->owner);
- parport_put_port (port);
+ parport_put_port(port);
}
+EXPORT_SYMBOL(parport_unregister_device);
/**
* parport_find_number - find a parallel port by number
@@ -1038,23 +1044,24 @@ void parport_unregister_device(struct pardevice *dev)
* gives you, use parport_put_port().
*/
-struct parport *parport_find_number (int number)
+struct parport *parport_find_number(int number)
{
struct parport *port, *result = NULL;
if (list_empty(&portlist))
- get_lowlevel_driver ();
+ get_lowlevel_driver();
- spin_lock (&parportlist_lock);
+ spin_lock(&parportlist_lock);
list_for_each_entry(port, &portlist, list) {
if (port->number == number) {
- result = parport_get_port (port);
+ result = parport_get_port(port);
break;
}
}
- spin_unlock (&parportlist_lock);
+ spin_unlock(&parportlist_lock);
return result;
}
+EXPORT_SYMBOL(parport_find_number);
/**
* parport_find_base - find a parallel port by base address
@@ -1068,23 +1075,24 @@ struct parport *parport_find_number (int number)
* gives you, use parport_put_port().
*/
-struct parport *parport_find_base (unsigned long base)
+struct parport *parport_find_base(unsigned long base)
{
struct parport *port, *result = NULL;
if (list_empty(&portlist))
- get_lowlevel_driver ();
+ get_lowlevel_driver();
- spin_lock (&parportlist_lock);
+ spin_lock(&parportlist_lock);
list_for_each_entry(port, &portlist, list) {
if (port->base == base) {
- result = parport_get_port (port);
+ result = parport_get_port(port);
break;
}
}
- spin_unlock (&parportlist_lock);
+ spin_unlock(&parportlist_lock);
return result;
}
+EXPORT_SYMBOL(parport_find_base);
/**
* parport_claim - claim access to a parallel port device
@@ -1111,8 +1119,9 @@ int parport_claim(struct pardevice *dev)
}
/* Preempt any current device */
- write_lock_irqsave (&port->cad_lock, flags);
- if ((oldcad = port->cad) != NULL) {
+ write_lock_irqsave(&port->cad_lock, flags);
+ oldcad = port->cad;
+ if (oldcad) {
if (oldcad->preempt) {
if (oldcad->preempt(oldcad->private))
goto blocked;
@@ -1121,8 +1130,10 @@ int parport_claim(struct pardevice *dev)
goto blocked;
if (port->cad != oldcad) {
- /* I think we'll actually deadlock rather than
- get here, but just in case.. */
+ /*
+ * I think we'll actually deadlock rather than
+ * get here, but just in case..
+ */
printk(KERN_WARNING
"%s: %s released port when preempted!\n",
port->name, oldcad->name);
@@ -1136,7 +1147,7 @@ int parport_claim(struct pardevice *dev)
dev->waiting = 0;
/* Take ourselves out of the wait list again. */
- spin_lock_irq (&port->waitlist_lock);
+ spin_lock_irq(&port->waitlist_lock);
if (dev->waitprev)
dev->waitprev->waitnext = dev->waitnext;
else
@@ -1145,7 +1156,7 @@ int parport_claim(struct pardevice *dev)
dev->waitnext->waitprev = dev->waitprev;
else
port->waittail = dev->waitprev;
- spin_unlock_irq (&port->waitlist_lock);
+ spin_unlock_irq(&port->waitlist_lock);
dev->waitprev = dev->waitnext = NULL;
}
@@ -1162,7 +1173,7 @@ int parport_claim(struct pardevice *dev)
/* If it's a daisy chain device, select it. */
if (dev->daisy >= 0) {
/* This could be lazier. */
- if (!parport_daisy_select (port, dev->daisy,
+ if (!parport_daisy_select(port, dev->daisy,
IEEE1284_MODE_COMPAT))
port->daisy = dev->daisy;
}
@@ -1175,13 +1186,15 @@ int parport_claim(struct pardevice *dev)
return 0;
blocked:
- /* If this is the first time we tried to claim the port, register an
- interest. This is only allowed for devices sleeping in
- parport_claim_or_block(), or those with a wakeup function. */
+ /*
+ * If this is the first time we tried to claim the port, register an
+ * interest. This is only allowed for devices sleeping in
+ * parport_claim_or_block(), or those with a wakeup function.
+ */
/* The cad_lock is still held for writing here */
if (dev->waiting & 2 || dev->wakeup) {
- spin_lock (&port->waitlist_lock);
+ spin_lock(&port->waitlist_lock);
if (test_and_set_bit(0, &dev->waiting) == 0) {
/* First add ourselves to the end of the wait list. */
dev->waitnext = NULL;
@@ -1192,11 +1205,12 @@ blocked:
} else
port->waithead = port->waittail = dev;
}
- spin_unlock (&port->waitlist_lock);
+ spin_unlock(&port->waitlist_lock);
}
- write_unlock_irqrestore (&port->cad_lock, flags);
+ write_unlock_irqrestore(&port->cad_lock, flags);
return -EAGAIN;
}
+EXPORT_SYMBOL(parport_claim);
/**
* parport_claim_or_block - claim access to a parallel port device
@@ -1212,8 +1226,10 @@ int parport_claim_or_block(struct pardevice *dev)
{
int r;
- /* Signal to parport_claim() that we can wait even without a
- wakeup function. */
+ /*
+ * Signal to parport_claim() that we can wait even without a
+ * wakeup function.
+ */
dev->waiting = 2;
/* Try to claim the port. If this fails, we need to sleep. */
@@ -1231,14 +1247,15 @@ int parport_claim_or_block(struct pardevice *dev)
* See also parport_release()
*/
- /* If dev->waiting is clear now, an interrupt
- gave us the port and we would deadlock if we slept. */
+ /*
+ * If dev->waiting is clear now, an interrupt
+ * gave us the port and we would deadlock if we slept.
+ */
if (dev->waiting) {
wait_event_interruptible(dev->wait_q,
!dev->waiting);
- if (signal_pending (current)) {
+ if (signal_pending(current))
return -EINTR;
- }
r = 1;
} else {
r = 0;
@@ -1250,15 +1267,15 @@ int parport_claim_or_block(struct pardevice *dev)
#ifdef PARPORT_DEBUG_SHARING
if (dev->port->physport->cad != dev)
- printk(KERN_DEBUG "%s: exiting parport_claim_or_block "
- "but %s owns port!\n", dev->name,
- dev->port->physport->cad ?
+ printk(KERN_DEBUG "%s: exiting parport_claim_or_block but %s owns port!\n",
+ dev->name, dev->port->physport->cad ?
dev->port->physport->cad->name:"nobody");
#endif
}
dev->waiting = 0;
return r;
}
+EXPORT_SYMBOL(parport_claim_or_block);
/**
* parport_release - give up access to a parallel port device
@@ -1278,9 +1295,9 @@ void parport_release(struct pardevice *dev)
/* Make sure that dev is the current device */
write_lock_irqsave(&port->cad_lock, flags);
if (port->cad != dev) {
- write_unlock_irqrestore (&port->cad_lock, flags);
- printk(KERN_WARNING "%s: %s tried to release parport "
- "when not owner\n", port->name, dev->name);
+ write_unlock_irqrestore(&port->cad_lock, flags);
+ printk(KERN_WARNING "%s: %s tried to release parport when not owner\n",
+ port->name, dev->name);
return;
}
@@ -1293,7 +1310,7 @@ void parport_release(struct pardevice *dev)
/* If this is a daisy device, deselect it. */
if (dev->daisy >= 0) {
- parport_daisy_deselect_all (port);
+ parport_daisy_deselect_all(port);
port->daisy = -1;
}
#endif
@@ -1304,8 +1321,10 @@ void parport_release(struct pardevice *dev)
/* Save control registers */
port->ops->save_state(port, dev->state);
- /* If anybody is waiting, find out who's been there longest and
- then wake them up. (Note: no locking required) */
+ /*
+ * If anybody is waiting, find out who's been there longest and
+ * then wake them up. (Note: no locking required)
+ */
/* !!! LOCKING IS NEEDED HERE */
for (pd = port->waithead; pd; pd = pd->waitnext) {
if (pd->waiting & 2) { /* sleeping in claim_or_block */
@@ -1322,14 +1341,17 @@ void parport_release(struct pardevice *dev)
}
}
- /* Nobody was waiting, so walk the list to see if anyone is
- interested in being woken up. (Note: no locking required) */
+ /*
+ * Nobody was waiting, so walk the list to see if anyone is
+ * interested in being woken up. (Note: no locking required)
+ */
/* !!! LOCKING IS NEEDED HERE */
- for (pd = port->devices; (port->cad == NULL) && pd; pd = pd->next) {
+ for (pd = port->devices; !port->cad && pd; pd = pd->next) {
if (pd->wakeup && pd != dev)
pd->wakeup(pd->private);
}
}
+EXPORT_SYMBOL(parport_release);
irqreturn_t parport_irq_handler(int irq, void *dev_id)
{
@@ -1339,22 +1361,6 @@ irqreturn_t parport_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
-
-/* Exported symbols for modules. */
-
-EXPORT_SYMBOL(parport_claim);
-EXPORT_SYMBOL(parport_claim_or_block);
-EXPORT_SYMBOL(parport_release);
-EXPORT_SYMBOL(parport_register_port);
-EXPORT_SYMBOL(parport_announce_port);
-EXPORT_SYMBOL(parport_remove_port);
-EXPORT_SYMBOL(parport_unregister_driver);
-EXPORT_SYMBOL(parport_register_device);
-EXPORT_SYMBOL(parport_unregister_device);
-EXPORT_SYMBOL(parport_get_port);
-EXPORT_SYMBOL(parport_put_port);
-EXPORT_SYMBOL(parport_find_number);
-EXPORT_SYMBOL(parport_find_base);
EXPORT_SYMBOL(parport_irq_handler);
MODULE_LICENSE("GPL");
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index f131ba947dc6..c0ad9aaa16a7 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -5,6 +5,7 @@ config PCI_DRA7XX
bool "TI DRA7xx PCIe controller"
select PCIE_DW
depends on OF && HAS_IOMEM && TI_PIPE3
+ depends on BROKEN
help
Enables support for the PCIe controller in the DRA7xx SoC. There
are two instances of PCIe controller in DRA7xx. This controller can
diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c
index e5dda38bdde5..99da549d5d06 100644
--- a/drivers/pci/host/pcie-altera.c
+++ b/drivers/pci/host/pcie-altera.c
@@ -55,8 +55,10 @@
#define TLP_CFG_DW2(bus, devfn, offset) \
(((bus) << 24) | ((devfn) << 16) | (offset))
#define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn))
+#define TLP_COMP_STATUS(s) (((s) >> 12) & 7)
#define TLP_HDR_SIZE 3
#define TLP_LOOP 500
+#define RP_DEVFN 0
#define INTX_NUM 4
@@ -166,34 +168,41 @@ static bool altera_pcie_valid_config(struct altera_pcie *pcie,
static int tlp_read_packet(struct altera_pcie *pcie, u32 *value)
{
- u8 loop;
+ int i;
bool sop = 0;
u32 ctrl;
u32 reg0, reg1;
+ u32 comp_status = 1;
/*
* Minimum 2 loops to read TLP headers and 1 loop to read data
* payload.
*/
- for (loop = 0; loop < TLP_LOOP; loop++) {
+ for (i = 0; i < TLP_LOOP; i++) {
ctrl = cra_readl(pcie, RP_RXCPL_STATUS);
if ((ctrl & RP_RXCPL_SOP) || (ctrl & RP_RXCPL_EOP) || sop) {
reg0 = cra_readl(pcie, RP_RXCPL_REG0);
reg1 = cra_readl(pcie, RP_RXCPL_REG1);
- if (ctrl & RP_RXCPL_SOP)
+ if (ctrl & RP_RXCPL_SOP) {
sop = true;
+ comp_status = TLP_COMP_STATUS(reg1);
+ }
if (ctrl & RP_RXCPL_EOP) {
+ if (comp_status)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
if (value)
*value = reg0;
+
return PCIBIOS_SUCCESSFUL;
}
}
udelay(5);
}
- return -ENOENT;
+ return PCIBIOS_DEVICE_NOT_FOUND;
}
static void tlp_write_packet(struct altera_pcie *pcie, u32 *headers,
@@ -233,7 +242,7 @@ static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn,
else
headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGRD1);
- headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, devfn),
+ headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN),
TLP_READ_TAG, byte_en);
headers[2] = TLP_CFG_DW2(bus, devfn, where);
@@ -253,7 +262,7 @@ static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn,
else
headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGWR1);
- headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, devfn),
+ headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN),
TLP_WRITE_TAG, byte_en);
headers[2] = TLP_CFG_DW2(bus, devfn, where);
@@ -458,7 +467,7 @@ static int altera_pcie_init_irq_domain(struct altera_pcie *pcie)
struct device_node *node = dev->of_node;
/* Setup INTx */
- pcie->irq_domain = irq_domain_add_linear(node, INTX_NUM,
+ pcie->irq_domain = irq_domain_add_linear(node, INTX_NUM + 1,
&intx_domain_ops, pcie);
if (!pcie->irq_domain) {
dev_err(dev, "Failed to get a INTx IRQ domain\n");
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
index 540f077c37ea..02a7452bdf23 100644
--- a/drivers/pci/host/pcie-designware.c
+++ b/drivers/pci/host/pcie-designware.c
@@ -440,7 +440,6 @@ int dw_pcie_host_init(struct pcie_port *pp)
ret, pp->io);
continue;
}
- pp->io_base = pp->io->start;
break;
case IORESOURCE_MEM:
pp->mem = win->res;
diff --git a/drivers/pci/host/pcie-hisi.c b/drivers/pci/host/pcie-hisi.c
index 35457ecd8e70..77f7c669a1b9 100644
--- a/drivers/pci/host/pcie-hisi.c
+++ b/drivers/pci/host/pcie-hisi.c
@@ -61,7 +61,9 @@ static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size,
*val = *(u8 __force *) walker;
else if (size == 2)
*val = *(u16 __force *) walker;
- else if (size != 4)
+ else if (size == 4)
+ *val = reg_val;
+ else
return PCIBIOS_BAD_REGISTER_NUMBER;
return PCIBIOS_SUCCESSFUL;
@@ -111,7 +113,7 @@ static struct pcie_host_ops hisi_pcie_host_ops = {
.link_up = hisi_pcie_link_up,
};
-static int __init hisi_add_pcie_port(struct pcie_port *pp,
+static int hisi_add_pcie_port(struct pcie_port *pp,
struct platform_device *pdev)
{
int ret;
@@ -139,7 +141,7 @@ static int __init hisi_add_pcie_port(struct pcie_port *pp,
return 0;
}
-static int __init hisi_pcie_probe(struct platform_device *pdev)
+static int hisi_pcie_probe(struct platform_device *pdev)
{
struct hisi_pcie *hisi_pcie;
struct pcie_port *pp;
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 53e463244bb7..7a0df3fdbfae 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -54,7 +54,7 @@ static int pci_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
struct irq_domain *domain;
domain = pci_msi_get_domain(dev);
- if (domain)
+ if (domain && irq_domain_is_hierarchy(domain))
return pci_msi_domain_alloc_irqs(domain, dev, nvec, type);
return arch_setup_msi_irqs(dev, nvec, type);
@@ -65,7 +65,7 @@ static void pci_msi_teardown_msi_irqs(struct pci_dev *dev)
struct irq_domain *domain;
domain = pci_msi_get_domain(dev);
- if (domain)
+ if (domain && irq_domain_is_hierarchy(domain))
pci_msi_domain_free_irqs(domain, dev);
else
arch_teardown_msi_irqs(dev);
@@ -257,6 +257,7 @@ void pci_msi_mask_irq(struct irq_data *data)
{
msi_set_mask_bit(data, 1);
}
+EXPORT_SYMBOL_GPL(pci_msi_mask_irq);
/**
* pci_msi_unmask_irq - Generic irq chip callback to unmask PCI/MSI interrupts
@@ -266,6 +267,7 @@ void pci_msi_unmask_irq(struct irq_data *data)
{
msi_set_mask_bit(data, 0);
}
+EXPORT_SYMBOL_GPL(pci_msi_unmask_irq);
void default_restore_msi_irqs(struct pci_dev *dev)
{
@@ -1126,6 +1128,7 @@ struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc)
{
return to_pci_dev(desc->dev);
}
+EXPORT_SYMBOL(msi_desc_to_pci_dev);
void *msi_desc_to_pci_sysdata(struct msi_desc *desc)
{
@@ -1285,6 +1288,7 @@ struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode,
domain->bus_token = DOMAIN_BUS_PCI_MSI;
return domain;
}
+EXPORT_SYMBOL_GPL(pci_msi_create_irq_domain);
/**
* pci_msi_domain_alloc_irqs - Allocate interrupts for @dev in @domain
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index a32ba753e413..9a033e8ee9a4 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -9,7 +9,9 @@
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/irqdomain.h>
#include <linux/pci.h>
+#include <linux/msi.h>
#include <linux/pci_hotplug.h>
#include <linux/module.h>
#include <linux/pci-aspm.h>
@@ -529,7 +531,7 @@ static bool acpi_pci_need_resume(struct pci_dev *dev)
return !!adev->power.flags.dsw_present;
}
-static struct pci_platform_pm_ops acpi_pci_platform_pm = {
+static const struct pci_platform_pm_ops acpi_pci_platform_pm = {
.is_manageable = acpi_pci_power_manageable,
.set_state = acpi_pci_set_power_state,
.choose_state = acpi_pci_choose_state,
@@ -689,6 +691,46 @@ static struct acpi_bus_type acpi_pci_bus = {
.cleanup = pci_acpi_cleanup,
};
+
+static struct fwnode_handle *(*pci_msi_get_fwnode_cb)(struct device *dev);
+
+/**
+ * pci_msi_register_fwnode_provider - Register callback to retrieve fwnode
+ * @fn: Callback matching a device to a fwnode that identifies a PCI
+ * MSI domain.
+ *
+ * This should be called by irqchip driver, which is the parent of
+ * the MSI domain to provide callback interface to query fwnode.
+ */
+void
+pci_msi_register_fwnode_provider(struct fwnode_handle *(*fn)(struct device *))
+{
+ pci_msi_get_fwnode_cb = fn;
+}
+
+/**
+ * pci_host_bridge_acpi_msi_domain - Retrieve MSI domain of a PCI host bridge
+ * @bus: The PCI host bridge bus.
+ *
+ * This function uses the callback function registered by
+ * pci_msi_register_fwnode_provider() to retrieve the irq_domain with
+ * type DOMAIN_BUS_PCI_MSI of the specified host bridge bus.
+ * This returns NULL on error or when the domain is not found.
+ */
+struct irq_domain *pci_host_bridge_acpi_msi_domain(struct pci_bus *bus)
+{
+ struct fwnode_handle *fwnode;
+
+ if (!pci_msi_get_fwnode_cb)
+ return NULL;
+
+ fwnode = pci_msi_get_fwnode_cb(&bus->dev);
+ if (!fwnode)
+ return NULL;
+
+ return irq_find_matching_fwnode(fwnode, DOMAIN_BUS_PCI_MSI);
+}
+
static int __init acpi_pci_init(void)
{
int ret;
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 4446fcb5effd..d7ffd66814bb 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -1146,9 +1146,21 @@ static int pci_pm_runtime_suspend(struct device *dev)
pci_dev->state_saved = false;
pci_dev->no_d3cold = false;
error = pm->runtime_suspend(dev);
- suspend_report_result(pm->runtime_suspend, error);
- if (error)
+ if (error) {
+ /*
+ * -EBUSY and -EAGAIN is used to request the runtime PM core
+ * to schedule a new suspend, so log the event only with debug
+ * log level.
+ */
+ if (error == -EBUSY || error == -EAGAIN)
+ dev_dbg(dev, "can't suspend now (%pf returned %d)\n",
+ pm->runtime_suspend, error);
+ else
+ dev_err(dev, "can't suspend (%pf returned %d)\n",
+ pm->runtime_suspend, error);
+
return error;
+ }
if (!pci_dev->d3cold_allowed)
pci_dev->no_d3cold = true;
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 92618686604c..eead54cd01b2 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -216,7 +216,10 @@ static ssize_t numa_node_store(struct device *dev,
if (ret)
return ret;
- if (node >= MAX_NUMNODES || !node_online(node))
+ if ((node < 0 && node != NUMA_NO_NODE) || node >= MAX_NUMNODES)
+ return -EINVAL;
+
+ if (node != NUMA_NO_NODE && !node_online(node))
return -EINVAL;
add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 314db8c1047a..d1a7105b9276 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -527,9 +527,9 @@ static void pci_restore_bars(struct pci_dev *dev)
pci_update_resource(dev, i);
}
-static struct pci_platform_pm_ops *pci_platform_pm;
+static const struct pci_platform_pm_ops *pci_platform_pm;
-int pci_set_platform_pm(struct pci_platform_pm_ops *ops)
+int pci_set_platform_pm(const struct pci_platform_pm_ops *ops)
{
if (!ops->is_manageable || !ops->set_state || !ops->choose_state
|| !ops->sleep_wake)
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index fd2f03fa53f3..f6f151a42147 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -68,7 +68,7 @@ struct pci_platform_pm_ops {
bool (*need_resume)(struct pci_dev *dev);
};
-int pci_set_platform_pm(struct pci_platform_pm_ops *ops);
+int pci_set_platform_pm(const struct pci_platform_pm_ops *ops);
void pci_update_current_state(struct pci_dev *dev, pci_power_t state);
void pci_power_up(struct pci_dev *dev);
void pci_disable_enabled_device(struct pci_dev *dev);
@@ -337,6 +337,4 @@ static inline int pci_dev_specific_reset(struct pci_dev *dev, int probe)
}
#endif
-struct pci_host_bridge *pci_find_host_bridge(struct pci_bus *bus);
-
#endif /* DRIVERS_PCI_H */
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index edb1984201e9..553a029e37f1 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -672,6 +672,8 @@ static struct irq_domain *pci_host_bridge_msi_domain(struct pci_bus *bus)
* should be called from here.
*/
d = pci_host_bridge_of_msi_domain(bus);
+ if (!d)
+ d = pci_host_bridge_acpi_msi_domain(bus);
return d;
}
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index be3755c973e9..166637f2917c 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -551,14 +551,6 @@ static void armpmu_init(struct arm_pmu *armpmu)
};
}
-int armpmu_register(struct arm_pmu *armpmu, int type)
-{
- armpmu_init(armpmu);
- pr_info("enabled with %s PMU driver, %d counters available\n",
- armpmu->name, armpmu->num_events);
- return perf_pmu_register(&armpmu->pmu, armpmu->name, type);
-}
-
/* Set at runtime when we know what CPU type we are. */
static struct arm_pmu *__oprofile_cpu_pmu;
@@ -887,6 +879,8 @@ int arm_pmu_device_probe(struct platform_device *pdev,
return -ENOMEM;
}
+ armpmu_init(pmu);
+
if (!__oprofile_cpu_pmu)
__oprofile_cpu_pmu = pmu;
@@ -912,10 +906,13 @@ int arm_pmu_device_probe(struct platform_device *pdev,
if (ret)
goto out_free;
- ret = armpmu_register(pmu, -1);
+ ret = perf_pmu_register(&pmu->pmu, pmu->name, -1);
if (ret)
goto out_destroy;
+ pr_info("enabled with %s PMU driver, %d counters available\n",
+ pmu->name, pmu->num_events);
+
return 0;
out_destroy:
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 7eb5859dd035..e7e117d5dbbe 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -118,6 +118,13 @@ config PHY_RCAR_GEN2
help
Support for USB PHY found on Renesas R-Car generation 2 SoCs.
+config PHY_RCAR_GEN3_USB2
+ tristate "Renesas R-Car generation 3 USB 2.0 PHY driver"
+ depends on OF && ARCH_SHMOBILE
+ select GENERIC_PHY
+ help
+ Support for USB 2.0 PHY found on Renesas R-Car generation 3 SoCs.
+
config OMAP_CONTROL_PHY
tristate "OMAP CONTROL PHY Driver"
depends on ARCH_OMAP2PLUS || COMPILE_TEST
@@ -215,6 +222,15 @@ config PHY_MT65XX_USB3
for mt65xx SoCs. it supports two usb2.0 ports and
one usb3.0 port.
+config PHY_HI6220_USB
+ tristate "hi6220 USB PHY support"
+ select GENERIC_PHY
+ select MFD_SYSCON
+ help
+ Enable this to support the HISILICON HI6220 USB PHY.
+
+ To compile this driver as a module, choose M here.
+
config PHY_SUN4I_USB
tristate "Allwinner sunxi SoC USB PHY driver"
depends on ARCH_SUNXI && HAS_IOMEM && OF
@@ -233,6 +249,7 @@ config PHY_SUN9I_USB
tristate "Allwinner sun9i SoC USB PHY driver"
depends on ARCH_SUNXI && HAS_IOMEM && OF
depends on RESET_CONTROLLER
+ depends on USB_COMMON
select GENERIC_PHY
help
Enable this to support the transceiver that is part of Allwinner
@@ -373,11 +390,11 @@ config PHY_TUSB1210
config PHY_BRCMSTB_SATA
tristate "Broadcom STB SATA PHY driver"
- depends on ARCH_BRCMSTB
+ depends on ARCH_BRCMSTB || BMIPS_GENERIC
depends on OF
select GENERIC_PHY
help
- Enable this to support the SATA3 PHY on 28nm Broadcom STB SoCs.
+ Enable this to support the SATA3 PHY on 28nm or 40nm Broadcom STB SoCs.
Likely useful only with CONFIG_SATA_BRCMSTB enabled.
config PHY_CYGNUS_PCIE
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 075db1a81aa5..c80f09df3bb8 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -17,12 +17,14 @@ obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o
obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o
obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o
+obj-$(CONFIG_PHY_RCAR_GEN3_USB2) += phy-rcar-gen3-usb2.o
obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o
obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o
obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o
+obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o
obj-$(CONFIG_PHY_MT65XX_USB3) += phy-mt65xx-usb3.o
obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
obj-$(CONFIG_PHY_SUN9I_USB) += phy-sun9i-usb.o
diff --git a/drivers/phy/phy-bcm-cygnus-pcie.c b/drivers/phy/phy-bcm-cygnus-pcie.c
index 7ad72b7d2b98..082c03f6438f 100644
--- a/drivers/phy/phy-bcm-cygnus-pcie.c
+++ b/drivers/phy/phy-bcm-cygnus-pcie.c
@@ -128,6 +128,7 @@ static int cygnus_pcie_phy_probe(struct platform_device *pdev)
struct phy_provider *provider;
struct resource *res;
unsigned cnt = 0;
+ int ret;
if (of_get_child_count(node) == 0) {
dev_err(dev, "PHY no child node\n");
@@ -154,24 +155,28 @@ static int cygnus_pcie_phy_probe(struct platform_device *pdev)
if (of_property_read_u32(child, "reg", &id)) {
dev_err(dev, "missing reg property for %s\n",
child->name);
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_child;
}
if (id >= MAX_NUM_PHYS) {
dev_err(dev, "invalid PHY id: %u\n", id);
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_child;
}
if (core->phys[id].phy) {
dev_err(dev, "duplicated PHY id: %u\n", id);
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_child;
}
p = &core->phys[id];
p->phy = devm_phy_create(dev, child, &cygnus_pcie_phy_ops);
if (IS_ERR(p->phy)) {
dev_err(dev, "failed to create PHY\n");
- return PTR_ERR(p->phy);
+ ret = PTR_ERR(p->phy);
+ goto put_child;
}
p->core = core;
@@ -191,6 +196,9 @@ static int cygnus_pcie_phy_probe(struct platform_device *pdev)
dev_dbg(dev, "registered %u PCIe PHY(s)\n", cnt);
return 0;
+put_child:
+ of_node_put(child);
+ return ret;
}
static const struct of_device_id cygnus_pcie_phy_match_table[] = {
diff --git a/drivers/phy/phy-berlin-sata.c b/drivers/phy/phy-berlin-sata.c
index 77a2e054fdea..f84a33a1bdd9 100644
--- a/drivers/phy/phy-berlin-sata.c
+++ b/drivers/phy/phy-berlin-sata.c
@@ -195,7 +195,7 @@ static int phy_berlin_sata_probe(struct platform_device *pdev)
struct phy_provider *phy_provider;
struct phy_berlin_priv *priv;
struct resource *res;
- int i = 0;
+ int ret, i = 0;
u32 phy_id;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -237,22 +237,27 @@ static int phy_berlin_sata_probe(struct platform_device *pdev)
if (of_property_read_u32(child, "reg", &phy_id)) {
dev_err(dev, "missing reg property in node %s\n",
child->name);
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_child;
}
if (phy_id >= ARRAY_SIZE(phy_berlin_power_down_bits)) {
dev_err(dev, "invalid reg in node %s\n", child->name);
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_child;
}
phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL);
- if (!phy_desc)
- return -ENOMEM;
+ if (!phy_desc) {
+ ret = -ENOMEM;
+ goto put_child;
+ }
phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create PHY %d\n", phy_id);
- return PTR_ERR(phy);
+ ret = PTR_ERR(phy);
+ goto put_child;
}
phy_desc->phy = phy;
@@ -269,6 +274,9 @@ static int phy_berlin_sata_probe(struct platform_device *pdev)
phy_provider =
devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
+put_child:
+ of_node_put(child);
+ return ret;
}
static const struct of_device_id phy_berlin_sata_of_match[] = {
diff --git a/drivers/phy/phy-berlin-usb.c b/drivers/phy/phy-berlin-usb.c
index 797ba17c404f..2017751ede26 100644
--- a/drivers/phy/phy-berlin-usb.c
+++ b/drivers/phy/phy-berlin-usb.c
@@ -9,11 +9,9 @@
* warranty of any kind, whether express or implied.
*/
-#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_device.h>
-#include <linux/of_gpio.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
@@ -195,7 +193,6 @@ static int phy_berlin_usb_probe(struct platform_device *pdev)
return PTR_ERR(phy);
}
- platform_set_drvdata(pdev, priv);
phy_set_drvdata(phy, priv);
phy_provider =
diff --git a/drivers/phy/phy-brcmstb-sata.c b/drivers/phy/phy-brcmstb-sata.c
index 8a2cb16a1937..a23172ff40e3 100644
--- a/drivers/phy/phy-brcmstb-sata.c
+++ b/drivers/phy/phy-brcmstb-sata.c
@@ -26,13 +26,21 @@
#define SATA_MDIO_BANK_OFFSET 0x23c
#define SATA_MDIO_REG_OFFSET(ofs) ((ofs) * 4)
-#define SATA_MDIO_REG_SPACE_SIZE 0x1000
-#define SATA_MDIO_REG_LENGTH 0x1f00
#define MAX_PORTS 2
/* Register offset between PHYs in PCB space */
-#define SATA_MDIO_REG_SPACE_SIZE 0x1000
+#define SATA_MDIO_REG_28NM_SPACE_SIZE 0x1000
+
+/* The older SATA PHY registers duplicated per port registers within the map,
+ * rather than having a separate map per port.
+ */
+#define SATA_MDIO_REG_40NM_SPACE_SIZE 0x10
+
+enum brcm_sata_phy_version {
+ BRCM_SATA_PHY_28NM,
+ BRCM_SATA_PHY_40NM,
+};
struct brcm_sata_port {
int portnum;
@@ -44,11 +52,12 @@ struct brcm_sata_port {
struct brcm_sata_phy {
struct device *dev;
void __iomem *phy_base;
+ enum brcm_sata_phy_version version;
struct brcm_sata_port phys[MAX_PORTS];
};
-enum sata_mdio_phy_regs_28nm {
+enum sata_mdio_phy_regs {
PLL_REG_BANK_0 = 0x50,
PLL_REG_BANK_0_PLLCONTROL_0 = 0x81,
@@ -66,8 +75,16 @@ enum sata_mdio_phy_regs_28nm {
static inline void __iomem *brcm_sata_phy_base(struct brcm_sata_port *port)
{
struct brcm_sata_phy *priv = port->phy_priv;
+ u32 offset = 0;
+
+ if (priv->version == BRCM_SATA_PHY_28NM)
+ offset = SATA_MDIO_REG_28NM_SPACE_SIZE;
+ else if (priv->version == BRCM_SATA_PHY_40NM)
+ offset = SATA_MDIO_REG_40NM_SPACE_SIZE;
+ else
+ dev_err(priv->dev, "invalid phy version\n");
- return priv->phy_base + (port->portnum * SATA_MDIO_REG_SPACE_SIZE);
+ return priv->phy_base + (port->portnum * offset);
}
static void brcm_sata_mdio_wr(void __iomem *addr, u32 bank, u32 ofs,
@@ -86,7 +103,7 @@ static void brcm_sata_mdio_wr(void __iomem *addr, u32 bank, u32 ofs,
#define FMAX_VAL_DEFAULT 0x3df
#define FMAX_VAL_SSC 0x83
-static void brcm_sata_cfg_ssc_28nm(struct brcm_sata_port *port)
+static void brcm_sata_cfg_ssc(struct brcm_sata_port *port)
{
void __iomem *base = brcm_sata_phy_base(port);
struct brcm_sata_phy *priv = port->phy_priv;
@@ -117,18 +134,21 @@ static int brcm_sata_phy_init(struct phy *phy)
{
struct brcm_sata_port *port = phy_get_drvdata(phy);
- brcm_sata_cfg_ssc_28nm(port);
+ brcm_sata_cfg_ssc(port);
return 0;
}
-static const struct phy_ops phy_ops_28nm = {
+static const struct phy_ops phy_ops = {
.init = brcm_sata_phy_init,
.owner = THIS_MODULE,
};
static const struct of_device_id brcm_sata_phy_of_match[] = {
- { .compatible = "brcm,bcm7445-sata-phy" },
+ { .compatible = "brcm,bcm7445-sata-phy",
+ .data = (void *)BRCM_SATA_PHY_28NM },
+ { .compatible = "brcm,bcm7425-sata-phy",
+ .data = (void *)BRCM_SATA_PHY_40NM },
{},
};
MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match);
@@ -137,10 +157,11 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *dn = dev->of_node, *child;
+ const struct of_device_id *of_id;
struct brcm_sata_phy *priv;
struct resource *res;
struct phy_provider *provider;
- int count = 0;
+ int ret, count = 0;
if (of_get_child_count(dn) == 0)
return -ENODEV;
@@ -156,6 +177,12 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
if (IS_ERR(priv->phy_base))
return PTR_ERR(priv->phy_base);
+ of_id = of_match_node(brcm_sata_phy_of_match, dn);
+ if (of_id)
+ priv->version = (enum brcm_sata_phy_version)of_id->data;
+ else
+ priv->version = BRCM_SATA_PHY_28NM;
+
for_each_available_child_of_node(dn, child) {
unsigned int id;
struct brcm_sata_port *port;
@@ -163,26 +190,30 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
if (of_property_read_u32(child, "reg", &id)) {
dev_err(dev, "missing reg property in node %s\n",
child->name);
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_child;
}
if (id >= MAX_PORTS) {
dev_err(dev, "invalid reg: %u\n", id);
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_child;
}
if (priv->phys[id].phy) {
dev_err(dev, "already registered port %u\n", id);
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_child;
}
port = &priv->phys[id];
port->portnum = id;
port->phy_priv = priv;
- port->phy = devm_phy_create(dev, child, &phy_ops_28nm);
+ port->phy = devm_phy_create(dev, child, &phy_ops);
port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc");
if (IS_ERR(port->phy)) {
dev_err(dev, "failed to create PHY\n");
- return PTR_ERR(port->phy);
+ ret = PTR_ERR(port->phy);
+ goto put_child;
}
phy_set_drvdata(port->phy, port);
@@ -198,6 +229,9 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
dev_info(dev, "registered %d port(s)\n", count);
return 0;
+put_child:
+ of_node_put(child);
+ return ret;
}
static struct platform_driver brcm_sata_phy_driver = {
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index fc48fac003a6..8c7f27db6ad3 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -636,8 +636,9 @@ EXPORT_SYMBOL_GPL(devm_of_phy_get);
* @np: node containing the phy
* @index: index of the phy
*
- * Gets the phy using _of_phy_get(), and associates a device with it using
- * devres. On driver detach, release function is invoked on the devres data,
+ * Gets the phy using _of_phy_get(), then gets a refcount to it,
+ * and associates a device with it using devres. On driver detach,
+ * release function is invoked on the devres data,
* then, devres data is freed.
*
*/
@@ -651,13 +652,21 @@ struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np,
return ERR_PTR(-ENOMEM);
phy = _of_phy_get(np, index);
- if (!IS_ERR(phy)) {
- *ptr = phy;
- devres_add(dev, ptr);
- } else {
+ if (IS_ERR(phy)) {
devres_free(ptr);
+ return phy;
}
+ if (!try_module_get(phy->ops->owner)) {
+ devres_free(ptr);
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ get_device(&phy->dev);
+
+ *ptr = phy;
+ devres_add(dev, ptr);
+
return phy;
}
EXPORT_SYMBOL_GPL(devm_of_phy_get_by_index);
diff --git a/drivers/phy/phy-hi6220-usb.c b/drivers/phy/phy-hi6220-usb.c
new file mode 100644
index 000000000000..b2141cbd4cf6
--- /dev/null
+++ b/drivers/phy/phy-hi6220-usb.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2015 Linaro Ltd.
+ * Copyright (c) 2015 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+
+#define SC_PERIPH_CTRL4 0x00c
+
+#define CTRL4_PICO_SIDDQ BIT(6)
+#define CTRL4_PICO_OGDISABLE BIT(8)
+#define CTRL4_PICO_VBUSVLDEXT BIT(10)
+#define CTRL4_PICO_VBUSVLDEXTSEL BIT(11)
+#define CTRL4_OTG_PHY_SEL BIT(21)
+
+#define SC_PERIPH_CTRL5 0x010
+
+#define CTRL5_USBOTG_RES_SEL BIT(3)
+#define CTRL5_PICOPHY_ACAENB BIT(4)
+#define CTRL5_PICOPHY_BC_MODE BIT(5)
+#define CTRL5_PICOPHY_CHRGSEL BIT(6)
+#define CTRL5_PICOPHY_VDATSRCEND BIT(7)
+#define CTRL5_PICOPHY_VDATDETENB BIT(8)
+#define CTRL5_PICOPHY_DCDENB BIT(9)
+#define CTRL5_PICOPHY_IDDIG BIT(10)
+
+#define SC_PERIPH_CTRL8 0x018
+#define SC_PERIPH_RSTEN0 0x300
+#define SC_PERIPH_RSTDIS0 0x304
+
+#define RST0_USBOTG_BUS BIT(4)
+#define RST0_POR_PICOPHY BIT(5)
+#define RST0_USBOTG BIT(6)
+#define RST0_USBOTG_32K BIT(7)
+
+#define EYE_PATTERN_PARA 0x7053348c
+
+struct hi6220_priv {
+ struct regmap *reg;
+ struct device *dev;
+};
+
+static void hi6220_phy_init(struct hi6220_priv *priv)
+{
+ struct regmap *reg = priv->reg;
+ u32 val, mask;
+
+ val = RST0_USBOTG_BUS | RST0_POR_PICOPHY |
+ RST0_USBOTG | RST0_USBOTG_32K;
+ mask = val;
+ regmap_update_bits(reg, SC_PERIPH_RSTEN0, mask, val);
+ regmap_update_bits(reg, SC_PERIPH_RSTDIS0, mask, val);
+}
+
+static int hi6220_phy_setup(struct hi6220_priv *priv, bool on)
+{
+ struct regmap *reg = priv->reg;
+ u32 val, mask;
+ int ret;
+
+ if (on) {
+ val = CTRL5_USBOTG_RES_SEL | CTRL5_PICOPHY_ACAENB;
+ mask = val | CTRL5_PICOPHY_BC_MODE;
+ ret = regmap_update_bits(reg, SC_PERIPH_CTRL5, mask, val);
+ if (ret)
+ goto out;
+
+ val = CTRL4_PICO_VBUSVLDEXT | CTRL4_PICO_VBUSVLDEXTSEL |
+ CTRL4_OTG_PHY_SEL;
+ mask = val | CTRL4_PICO_SIDDQ | CTRL4_PICO_OGDISABLE;
+ ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val);
+ if (ret)
+ goto out;
+
+ ret = regmap_write(reg, SC_PERIPH_CTRL8, EYE_PATTERN_PARA);
+ if (ret)
+ goto out;
+ } else {
+ val = CTRL4_PICO_SIDDQ;
+ mask = val;
+ ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val);
+ if (ret)
+ goto out;
+ }
+
+ return 0;
+out:
+ dev_err(priv->dev, "failed to setup phy ret: %d\n", ret);
+ return ret;
+}
+
+static int hi6220_phy_start(struct phy *phy)
+{
+ struct hi6220_priv *priv = phy_get_drvdata(phy);
+
+ return hi6220_phy_setup(priv, true);
+}
+
+static int hi6220_phy_exit(struct phy *phy)
+{
+ struct hi6220_priv *priv = phy_get_drvdata(phy);
+
+ return hi6220_phy_setup(priv, false);
+}
+
+static struct phy_ops hi6220_phy_ops = {
+ .init = hi6220_phy_start,
+ .exit = hi6220_phy_exit,
+ .owner = THIS_MODULE,
+};
+
+static int hi6220_phy_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct phy *phy;
+ struct hi6220_priv *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ priv->reg = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "hisilicon,peripheral-syscon");
+ if (IS_ERR(priv->reg)) {
+ dev_err(dev, "no hisilicon,peripheral-syscon\n");
+ return PTR_ERR(priv->reg);
+ }
+
+ hi6220_phy_init(priv);
+
+ phy = devm_phy_create(dev, NULL, &hi6220_phy_ops);
+ if (IS_ERR(phy))
+ return PTR_ERR(phy);
+
+ phy_set_drvdata(phy, priv);
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id hi6220_phy_of_match[] = {
+ {.compatible = "hisilicon,hi6220-usb-phy",},
+ { },
+};
+MODULE_DEVICE_TABLE(of, hi6220_phy_of_match);
+
+static struct platform_driver hi6220_phy_driver = {
+ .probe = hi6220_phy_probe,
+ .driver = {
+ .name = "hi6220-usb-phy",
+ .of_match_table = hi6220_phy_of_match,
+ }
+};
+module_platform_driver(hi6220_phy_driver);
+
+MODULE_DESCRIPTION("HISILICON HI6220 USB PHY driver");
+MODULE_ALIAS("platform:hi6220-usb-phy");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/phy-miphy28lp.c b/drivers/phy/phy-miphy28lp.c
index c47b56b4a2b8..3acd2a1808df 100644
--- a/drivers/phy/phy-miphy28lp.c
+++ b/drivers/phy/phy-miphy28lp.c
@@ -1226,15 +1226,18 @@ static int miphy28lp_probe(struct platform_device *pdev)
miphy_phy = devm_kzalloc(&pdev->dev, sizeof(*miphy_phy),
GFP_KERNEL);
- if (!miphy_phy)
- return -ENOMEM;
+ if (!miphy_phy) {
+ ret = -ENOMEM;
+ goto put_child;
+ }
miphy_dev->phys[port] = miphy_phy;
phy = devm_phy_create(&pdev->dev, child, &miphy28lp_ops);
if (IS_ERR(phy)) {
dev_err(&pdev->dev, "failed to create PHY\n");
- return PTR_ERR(phy);
+ ret = PTR_ERR(phy);
+ goto put_child;
}
miphy_dev->phys[port]->phy = phy;
@@ -1242,11 +1245,11 @@ static int miphy28lp_probe(struct platform_device *pdev)
ret = miphy28lp_of_probe(child, miphy_phy);
if (ret)
- return ret;
+ goto put_child;
ret = miphy28lp_probe_resets(child, miphy_dev->phys[port]);
if (ret)
- return ret;
+ goto put_child;
phy_set_drvdata(phy, miphy_dev->phys[port]);
port++;
@@ -1255,6 +1258,9 @@ static int miphy28lp_probe(struct platform_device *pdev)
provider = devm_of_phy_provider_register(&pdev->dev, miphy28lp_xlate);
return PTR_ERR_OR_ZERO(provider);
+put_child:
+ of_node_put(child);
+ return ret;
}
static const struct of_device_id miphy28lp_of_match[] = {
diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c
index 00a686a073ed..e661f3b36eaa 100644
--- a/drivers/phy/phy-miphy365x.c
+++ b/drivers/phy/phy-miphy365x.c
@@ -566,22 +566,25 @@ static int miphy365x_probe(struct platform_device *pdev)
miphy_phy = devm_kzalloc(&pdev->dev, sizeof(*miphy_phy),
GFP_KERNEL);
- if (!miphy_phy)
- return -ENOMEM;
+ if (!miphy_phy) {
+ ret = -ENOMEM;
+ goto put_child;
+ }
miphy_dev->phys[port] = miphy_phy;
phy = devm_phy_create(&pdev->dev, child, &miphy365x_ops);
if (IS_ERR(phy)) {
dev_err(&pdev->dev, "failed to create PHY\n");
- return PTR_ERR(phy);
+ ret = PTR_ERR(phy);
+ goto put_child;
}
miphy_dev->phys[port]->phy = phy;
ret = miphy365x_of_probe(child, miphy_phy);
if (ret)
- return ret;
+ goto put_child;
phy_set_drvdata(phy, miphy_dev->phys[port]);
@@ -591,12 +594,15 @@ static int miphy365x_probe(struct platform_device *pdev)
&miphy_phy->ctrlreg);
if (ret) {
dev_err(&pdev->dev, "No sysconfig offset found\n");
- return ret;
+ goto put_child;
}
}
provider = devm_of_phy_provider_register(&pdev->dev, miphy365x_xlate);
return PTR_ERR_OR_ZERO(provider);
+put_child:
+ of_node_put(child);
+ return ret;
}
static const struct of_device_id miphy365x_of_match[] = {
diff --git a/drivers/phy/phy-mt65xx-usb3.c b/drivers/phy/phy-mt65xx-usb3.c
index f30b28bd41fe..c0e7b4b0cf5c 100644
--- a/drivers/phy/phy-mt65xx-usb3.c
+++ b/drivers/phy/phy-mt65xx-usb3.c
@@ -17,6 +17,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
@@ -27,6 +28,7 @@
* relative to USB3_SIF2_BASE base address
*/
#define SSUSB_SIFSLV_SPLLC 0x0000
+#define SSUSB_SIFSLV_U2FREQ 0x0100
/* offsets of sub-segment in each port registers */
#define SSUSB_SIFSLV_U2PHY_COM_BASE 0x0000
@@ -41,6 +43,7 @@
#define PA2_RG_SIF_U2PLL_FORCE_EN BIT(18)
#define U3P_USBPHYACR5 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0014)
+#define PA5_RG_U2_HSTX_SRCAL_EN BIT(15)
#define PA5_RG_U2_HSTX_SRCTRL GENMASK(14, 12)
#define PA5_RG_U2_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 12)
#define PA5_RG_U2_HS_100U_U3_EN BIT(11)
@@ -49,6 +52,8 @@
#define PA6_RG_U2_ISO_EN BIT(31)
#define PA6_RG_U2_BC11_SW_EN BIT(23)
#define PA6_RG_U2_OTG_VBUSCMP_EN BIT(20)
+#define PA6_RG_U2_SQTH GENMASK(3, 0)
+#define PA6_RG_U2_SQTH_VAL(x) (0xf & (x))
#define U3P_U2PHYACR4 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0020)
#define P2C_RG_USB20_GPIO_CTL BIT(9)
@@ -111,6 +116,24 @@
#define XC3_RG_U3_XTAL_RX_PWD BIT(9)
#define XC3_RG_U3_FRC_XTAL_RX_PWD BIT(8)
+#define U3P_U2FREQ_FMCR0 (SSUSB_SIFSLV_U2FREQ + 0x00)
+#define P2F_RG_MONCLK_SEL GENMASK(27, 26)
+#define P2F_RG_MONCLK_SEL_VAL(x) ((0x3 & (x)) << 26)
+#define P2F_RG_FREQDET_EN BIT(24)
+#define P2F_RG_CYCLECNT GENMASK(23, 0)
+#define P2F_RG_CYCLECNT_VAL(x) ((P2F_RG_CYCLECNT) & (x))
+
+#define U3P_U2FREQ_VALUE (SSUSB_SIFSLV_U2FREQ + 0x0c)
+
+#define U3P_U2FREQ_FMMONR1 (SSUSB_SIFSLV_U2FREQ + 0x10)
+#define P2F_USB_FM_VALID BIT(0)
+#define P2F_RG_FRCK_EN BIT(8)
+
+#define U3P_REF_CLK 26 /* MHZ */
+#define U3P_SLEW_RATE_COEF 28
+#define U3P_SR_COEF_DIVISOR 1000
+#define U3P_FM_DET_CYCLE_CNT 1024
+
struct mt65xx_phy_instance {
struct phy *phy;
void __iomem *port_base;
@@ -126,6 +149,77 @@ struct mt65xx_u3phy {
int nphys;
};
+static void hs_slew_rate_calibrate(struct mt65xx_u3phy *u3phy,
+ struct mt65xx_phy_instance *instance)
+{
+ void __iomem *sif_base = u3phy->sif_base;
+ int calibration_val;
+ int fm_out;
+ u32 tmp;
+
+ /* enable USB ring oscillator */
+ tmp = readl(instance->port_base + U3P_USBPHYACR5);
+ tmp |= PA5_RG_U2_HSTX_SRCAL_EN;
+ writel(tmp, instance->port_base + U3P_USBPHYACR5);
+ udelay(1);
+
+ /*enable free run clock */
+ tmp = readl(sif_base + U3P_U2FREQ_FMMONR1);
+ tmp |= P2F_RG_FRCK_EN;
+ writel(tmp, sif_base + U3P_U2FREQ_FMMONR1);
+
+ /* set cycle count as 1024, and select u2 channel */
+ tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
+ tmp &= ~(P2F_RG_CYCLECNT | P2F_RG_MONCLK_SEL);
+ tmp |= P2F_RG_CYCLECNT_VAL(U3P_FM_DET_CYCLE_CNT);
+ tmp |= P2F_RG_MONCLK_SEL_VAL(instance->index);
+ writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
+
+ /* enable frequency meter */
+ tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
+ tmp |= P2F_RG_FREQDET_EN;
+ writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
+
+ /* ignore return value */
+ readl_poll_timeout(sif_base + U3P_U2FREQ_FMMONR1, tmp,
+ (tmp & P2F_USB_FM_VALID), 10, 200);
+
+ fm_out = readl(sif_base + U3P_U2FREQ_VALUE);
+
+ /* disable frequency meter */
+ tmp = readl(sif_base + U3P_U2FREQ_FMCR0);
+ tmp &= ~P2F_RG_FREQDET_EN;
+ writel(tmp, sif_base + U3P_U2FREQ_FMCR0);
+
+ /*disable free run clock */
+ tmp = readl(sif_base + U3P_U2FREQ_FMMONR1);
+ tmp &= ~P2F_RG_FRCK_EN;
+ writel(tmp, sif_base + U3P_U2FREQ_FMMONR1);
+
+ if (fm_out) {
+ /* ( 1024 / FM_OUT ) x reference clock frequency x 0.028 */
+ tmp = U3P_FM_DET_CYCLE_CNT * U3P_REF_CLK * U3P_SLEW_RATE_COEF;
+ tmp /= fm_out;
+ calibration_val = DIV_ROUND_CLOSEST(tmp, U3P_SR_COEF_DIVISOR);
+ } else {
+ /* if FM detection fail, set default value */
+ calibration_val = 4;
+ }
+ dev_dbg(u3phy->dev, "phy:%d, fm_out:%d, calib:%d\n",
+ instance->index, fm_out, calibration_val);
+
+ /* set HS slew rate */
+ tmp = readl(instance->port_base + U3P_USBPHYACR5);
+ tmp &= ~PA5_RG_U2_HSTX_SRCTRL;
+ tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(calibration_val);
+ writel(tmp, instance->port_base + U3P_USBPHYACR5);
+
+ /* disable USB ring oscillator */
+ tmp = readl(instance->port_base + U3P_USBPHYACR5);
+ tmp &= ~PA5_RG_U2_HSTX_SRCAL_EN;
+ writel(tmp, instance->port_base + U3P_USBPHYACR5);
+}
+
static void phy_instance_init(struct mt65xx_u3phy *u3phy,
struct mt65xx_phy_instance *instance)
{
@@ -165,9 +259,10 @@ static void phy_instance_init(struct mt65xx_u3phy *u3phy,
writel(tmp, port_base + U3P_U2PHYDTM0);
}
- /* DP/DM BC1.1 path Disable */
tmp = readl(port_base + U3P_USBPHYACR6);
- tmp &= ~PA6_RG_U2_BC11_SW_EN;
+ tmp &= ~PA6_RG_U2_BC11_SW_EN; /* DP/DM BC1.1 path Disable */
+ tmp &= ~PA6_RG_U2_SQTH;
+ tmp |= PA6_RG_U2_SQTH_VAL(2);
writel(tmp, port_base + U3P_USBPHYACR6);
tmp = readl(port_base + U3P_U3PHYA_DA_REG0);
@@ -223,9 +318,9 @@ static void phy_instance_power_on(struct mt65xx_u3phy *u3phy,
tmp |= XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD;
writel(tmp, u3phy->sif_base + U3P_XTALCTL3);
- /* [mt8173]disable Change 100uA current from SSUSB */
+ /* [mt8173]switch 100uA current to SSUSB */
tmp = readl(port_base + U3P_USBPHYACR5);
- tmp &= ~PA5_RG_U2_HS_100U_U3_EN;
+ tmp |= PA5_RG_U2_HS_100U_U3_EN;
writel(tmp, port_base + U3P_USBPHYACR5);
}
@@ -270,7 +365,7 @@ static void phy_instance_power_off(struct mt65xx_u3phy *u3phy,
writel(tmp, port_base + U3P_USBPHYACR6);
if (!index) {
- /* (also disable)Change 100uA current switch to USB2.0 */
+ /* switch 100uA current back to USB2.0 */
tmp = readl(port_base + U3P_USBPHYACR5);
tmp &= ~PA5_RG_U2_HS_100U_U3_EN;
writel(tmp, port_base + U3P_USBPHYACR5);
@@ -340,6 +435,7 @@ static int mt65xx_phy_power_on(struct phy *phy)
struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
phy_instance_power_on(u3phy, instance);
+ hs_slew_rate_calibrate(u3phy, instance);
return 0;
}
@@ -415,7 +511,7 @@ static int mt65xx_u3phy_probe(struct platform_device *pdev)
struct resource *sif_res;
struct mt65xx_u3phy *u3phy;
struct resource res;
- int port;
+ int port, retval;
u3phy = devm_kzalloc(dev, sizeof(*u3phy), GFP_KERNEL);
if (!u3phy)
@@ -447,31 +543,34 @@ static int mt65xx_u3phy_probe(struct platform_device *pdev)
for_each_child_of_node(np, child_np) {
struct mt65xx_phy_instance *instance;
struct phy *phy;
- int retval;
instance = devm_kzalloc(dev, sizeof(*instance), GFP_KERNEL);
- if (!instance)
- return -ENOMEM;
+ if (!instance) {
+ retval = -ENOMEM;
+ goto put_child;
+ }
u3phy->phys[port] = instance;
phy = devm_phy_create(dev, child_np, &mt65xx_u3phy_ops);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create phy\n");
- return PTR_ERR(phy);
+ retval = PTR_ERR(phy);
+ goto put_child;
}
retval = of_address_to_resource(child_np, 0, &res);
if (retval) {
dev_err(dev, "failed to get address resource(id-%d)\n",
port);
- return retval;
+ goto put_child;
}
instance->port_base = devm_ioremap_resource(&phy->dev, &res);
if (IS_ERR(instance->port_base)) {
dev_err(dev, "failed to remap phy regs\n");
- return PTR_ERR(instance->port_base);
+ retval = PTR_ERR(instance->port_base);
+ goto put_child;
}
instance->phy = phy;
@@ -483,6 +582,9 @@ static int mt65xx_u3phy_probe(struct platform_device *pdev)
provider = devm_of_phy_provider_register(dev, mt65xx_phy_xlate);
return PTR_ERR_OR_ZERO(provider);
+put_child:
+ of_node_put(child_np);
+ return retval;
}
static const struct of_device_id mt65xx_u3phy_id_table[] = {
diff --git a/drivers/phy/phy-omap-usb2.c b/drivers/phy/phy-omap-usb2.c
index 0fe80589ffbe..c134989052f5 100644
--- a/drivers/phy/phy-omap-usb2.c
+++ b/drivers/phy/phy-omap-usb2.c
@@ -29,6 +29,8 @@
#include <linux/delay.h>
#include <linux/phy/omap_control_phy.h>
#include <linux/phy/phy.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
#include <linux/of_platform.h>
#define USB2PHY_DISCON_BYP_LATCH (1 << 31)
@@ -97,22 +99,38 @@ static int omap_usb_set_peripheral(struct usb_otg *otg,
return 0;
}
+static int omap_usb_phy_power(struct omap_usb *phy, int on)
+{
+ u32 val;
+ int ret;
+
+ if (!phy->syscon_phy_power) {
+ omap_control_phy_power(phy->control_dev, on);
+ return 0;
+ }
+
+ if (on)
+ val = phy->power_on;
+ else
+ val = phy->power_off;
+
+ ret = regmap_update_bits(phy->syscon_phy_power, phy->power_reg,
+ phy->mask, val);
+ return ret;
+}
+
static int omap_usb_power_off(struct phy *x)
{
struct omap_usb *phy = phy_get_drvdata(x);
- omap_control_phy_power(phy->control_dev, 0);
-
- return 0;
+ return omap_usb_phy_power(phy, false);
}
static int omap_usb_power_on(struct phy *x)
{
struct omap_usb *phy = phy_get_drvdata(x);
- omap_control_phy_power(phy->control_dev, 1);
-
- return 0;
+ return omap_usb_phy_power(phy, true);
}
static int omap_usb_init(struct phy *x)
@@ -147,21 +165,38 @@ static const struct phy_ops ops = {
static const struct usb_phy_data omap_usb2_data = {
.label = "omap_usb2",
.flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS,
+ .mask = OMAP_DEV_PHY_PD,
+ .power_off = OMAP_DEV_PHY_PD,
};
static const struct usb_phy_data omap5_usb2_data = {
.label = "omap5_usb2",
.flags = 0,
+ .mask = OMAP_DEV_PHY_PD,
+ .power_off = OMAP_DEV_PHY_PD,
};
static const struct usb_phy_data dra7x_usb2_data = {
.label = "dra7x_usb2",
.flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
+ .mask = OMAP_DEV_PHY_PD,
+ .power_off = OMAP_DEV_PHY_PD,
+};
+
+static const struct usb_phy_data dra7x_usb2_phy2_data = {
+ .label = "dra7x_usb2_phy2",
+ .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
+ .mask = OMAP_USB2_PHY_PD,
+ .power_off = OMAP_USB2_PHY_PD,
};
static const struct usb_phy_data am437x_usb2_data = {
.label = "am437x_usb2",
.flags = 0,
+ .mask = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD |
+ AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN,
+ .power_on = AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN,
+ .power_off = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD,
};
static const struct of_device_id omap_usb2_id_table[] = {
@@ -178,6 +213,10 @@ static const struct of_device_id omap_usb2_id_table[] = {
.data = &dra7x_usb2_data,
},
{
+ .compatible = "ti,dra7x-usb2-phy2",
+ .data = &dra7x_usb2_phy2_data,
+ },
+ {
.compatible = "ti,am437x-usb2",
.data = &am437x_usb2_data,
},
@@ -219,6 +258,9 @@ static int omap_usb2_probe(struct platform_device *pdev)
phy->phy.label = phy_data->label;
phy->phy.otg = otg;
phy->phy.type = USB_PHY_TYPE_USB2;
+ phy->mask = phy_data->mask;
+ phy->power_on = phy_data->power_on;
+ phy->power_off = phy_data->power_off;
if (phy_data->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -228,21 +270,36 @@ static int omap_usb2_probe(struct platform_device *pdev)
phy->flags |= OMAP_USB2_CALIBRATE_FALSE_DISCONNECT;
}
- control_node = of_parse_phandle(node, "ctrl-module", 0);
- if (!control_node) {
- dev_err(&pdev->dev, "Failed to get control device phandle\n");
- return -EINVAL;
- }
+ phy->syscon_phy_power = syscon_regmap_lookup_by_phandle(node,
+ "syscon-phy-power");
+ if (IS_ERR(phy->syscon_phy_power)) {
+ dev_dbg(&pdev->dev,
+ "can't get syscon-phy-power, using control device\n");
+ phy->syscon_phy_power = NULL;
+
+ control_node = of_parse_phandle(node, "ctrl-module", 0);
+ if (!control_node) {
+ dev_err(&pdev->dev,
+ "Failed to get control device phandle\n");
+ return -EINVAL;
+ }
- control_pdev = of_find_device_by_node(control_node);
- if (!control_pdev) {
- dev_err(&pdev->dev, "Failed to get control device\n");
- return -EINVAL;
+ control_pdev = of_find_device_by_node(control_node);
+ if (!control_pdev) {
+ dev_err(&pdev->dev, "Failed to get control device\n");
+ return -EINVAL;
+ }
+ phy->control_dev = &control_pdev->dev;
+ } else {
+ if (of_property_read_u32_index(node,
+ "syscon-phy-power", 1,
+ &phy->power_reg)) {
+ dev_err(&pdev->dev,
+ "couldn't get power reg. offset\n");
+ return -EINVAL;
+ }
}
- phy->control_dev = &control_pdev->dev;
- omap_control_phy_power(phy->control_dev, 0);
-
otg->set_host = omap_usb_set_host;
otg->set_peripheral = omap_usb_set_peripheral;
if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS)
@@ -261,6 +318,7 @@ static int omap_usb2_probe(struct platform_device *pdev)
}
phy_set_drvdata(generic_phy, phy);
+ omap_usb_power_off(generic_phy);
phy_provider = devm_of_phy_provider_register(phy->dev,
of_phy_simple_xlate);
diff --git a/drivers/phy/phy-rcar-gen3-usb2.c b/drivers/phy/phy-rcar-gen3-usb2.c
new file mode 100644
index 000000000000..ef332ef4abc7
--- /dev/null
+++ b/drivers/phy/phy-rcar-gen3-usb2.c
@@ -0,0 +1,378 @@
+/*
+ * Renesas R-Car Gen3 for USB2.0 PHY driver
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ *
+ * This is based on the phy-rcar-gen2 driver:
+ * Copyright (C) 2014 Renesas Solutions Corp.
+ * Copyright (C) 2014 Cogent Embedded, 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.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+
+/******* USB2.0 Host registers (original offset is +0x200) *******/
+#define USB2_INT_ENABLE 0x000
+#define USB2_USBCTR 0x00c
+#define USB2_SPD_RSM_TIMSET 0x10c
+#define USB2_OC_TIMSET 0x110
+#define USB2_COMMCTRL 0x600
+#define USB2_OBINTSTA 0x604
+#define USB2_OBINTEN 0x608
+#define USB2_VBCTRL 0x60c
+#define USB2_LINECTRL1 0x610
+#define USB2_ADPCTRL 0x630
+
+/* INT_ENABLE */
+#define USB2_INT_ENABLE_UCOM_INTEN BIT(3)
+#define USB2_INT_ENABLE_USBH_INTB_EN BIT(2)
+#define USB2_INT_ENABLE_USBH_INTA_EN BIT(1)
+#define USB2_INT_ENABLE_INIT (USB2_INT_ENABLE_UCOM_INTEN | \
+ USB2_INT_ENABLE_USBH_INTB_EN | \
+ USB2_INT_ENABLE_USBH_INTA_EN)
+
+/* USBCTR */
+#define USB2_USBCTR_DIRPD BIT(2)
+#define USB2_USBCTR_PLL_RST BIT(1)
+
+/* SPD_RSM_TIMSET */
+#define USB2_SPD_RSM_TIMSET_INIT 0x014e029b
+
+/* OC_TIMSET */
+#define USB2_OC_TIMSET_INIT 0x000209ab
+
+/* COMMCTRL */
+#define USB2_COMMCTRL_OTG_PERI BIT(31) /* 1 = Peripheral mode */
+
+/* OBINTSTA and OBINTEN */
+#define USB2_OBINT_SESSVLDCHG BIT(12)
+#define USB2_OBINT_IDDIGCHG BIT(11)
+#define USB2_OBINT_BITS (USB2_OBINT_SESSVLDCHG | \
+ USB2_OBINT_IDDIGCHG)
+
+/* VBCTRL */
+#define USB2_VBCTRL_DRVVBUSSEL BIT(8)
+
+/* LINECTRL1 */
+#define USB2_LINECTRL1_DPRPD_EN BIT(19)
+#define USB2_LINECTRL1_DP_RPD BIT(18)
+#define USB2_LINECTRL1_DMRPD_EN BIT(17)
+#define USB2_LINECTRL1_DM_RPD BIT(16)
+
+/* ADPCTRL */
+#define USB2_ADPCTRL_OTGSESSVLD BIT(20)
+#define USB2_ADPCTRL_IDDIG BIT(19)
+#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */
+#define USB2_ADPCTRL_DRVVBUS BIT(4)
+
+/******* HSUSB registers (original offset is +0x100) *******/
+#define HSUSB_LPSTS 0x02
+#define HSUSB_UGCTRL2 0x84
+
+/* Low Power Status register (LPSTS) */
+#define HSUSB_LPSTS_SUSPM 0x4000
+
+/* USB General control register 2 (UGCTRL2) */
+#define HSUSB_UGCTRL2_MASK 0x00000031 /* bit[31:6] should be 0 */
+#define HSUSB_UGCTRL2_USB0SEL 0x00000030
+#define HSUSB_UGCTRL2_USB0SEL_HOST 0x00000010
+#define HSUSB_UGCTRL2_USB0SEL_HS_USB 0x00000020
+#define HSUSB_UGCTRL2_USB0SEL_OTG 0x00000030
+
+struct rcar_gen3_data {
+ void __iomem *base;
+ struct clk *clk;
+};
+
+struct rcar_gen3_chan {
+ struct rcar_gen3_data usb2;
+ struct rcar_gen3_data hsusb;
+ struct phy *phy;
+};
+
+static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host)
+{
+ void __iomem *usb2_base = ch->usb2.base;
+ u32 val = readl(usb2_base + USB2_COMMCTRL);
+
+ dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, host);
+ if (host)
+ val &= ~USB2_COMMCTRL_OTG_PERI;
+ else
+ val |= USB2_COMMCTRL_OTG_PERI;
+ writel(val, usb2_base + USB2_COMMCTRL);
+}
+
+static void rcar_gen3_set_linectrl(struct rcar_gen3_chan *ch, int dp, int dm)
+{
+ void __iomem *usb2_base = ch->usb2.base;
+ u32 val = readl(usb2_base + USB2_LINECTRL1);
+
+ dev_vdbg(&ch->phy->dev, "%s: %08x, %d, %d\n", __func__, val, dp, dm);
+ val &= ~(USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD);
+ if (dp)
+ val |= USB2_LINECTRL1_DP_RPD;
+ if (dm)
+ val |= USB2_LINECTRL1_DM_RPD;
+ writel(val, usb2_base + USB2_LINECTRL1);
+}
+
+static void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus)
+{
+ void __iomem *usb2_base = ch->usb2.base;
+ u32 val = readl(usb2_base + USB2_ADPCTRL);
+
+ dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, vbus);
+ if (vbus)
+ val |= USB2_ADPCTRL_DRVVBUS;
+ else
+ val &= ~USB2_ADPCTRL_DRVVBUS;
+ writel(val, usb2_base + USB2_ADPCTRL);
+}
+
+static void rcar_gen3_init_for_host(struct rcar_gen3_chan *ch)
+{
+ rcar_gen3_set_linectrl(ch, 1, 1);
+ rcar_gen3_set_host_mode(ch, 1);
+ rcar_gen3_enable_vbus_ctrl(ch, 1);
+}
+
+static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch)
+{
+ rcar_gen3_set_linectrl(ch, 0, 1);
+ rcar_gen3_set_host_mode(ch, 0);
+ rcar_gen3_enable_vbus_ctrl(ch, 0);
+}
+
+static bool rcar_gen3_check_vbus(struct rcar_gen3_chan *ch)
+{
+ return !!(readl(ch->usb2.base + USB2_ADPCTRL) &
+ USB2_ADPCTRL_OTGSESSVLD);
+}
+
+static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch)
+{
+ return !!(readl(ch->usb2.base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG);
+}
+
+static void rcar_gen3_device_recognition(struct rcar_gen3_chan *ch)
+{
+ bool is_host = true;
+
+ /* B-device? */
+ if (rcar_gen3_check_id(ch) && rcar_gen3_check_vbus(ch))
+ is_host = false;
+
+ if (is_host)
+ rcar_gen3_init_for_host(ch);
+ else
+ rcar_gen3_init_for_peri(ch);
+}
+
+static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch)
+{
+ void __iomem *usb2_base = ch->usb2.base;
+ u32 val;
+
+ val = readl(usb2_base + USB2_VBCTRL);
+ writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL);
+ writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA);
+ val = readl(usb2_base + USB2_OBINTEN);
+ writel(val | USB2_OBINT_BITS, usb2_base + USB2_OBINTEN);
+ val = readl(usb2_base + USB2_ADPCTRL);
+ writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL);
+ val = readl(usb2_base + USB2_LINECTRL1);
+ rcar_gen3_set_linectrl(ch, 0, 0);
+ writel(val | USB2_LINECTRL1_DPRPD_EN | USB2_LINECTRL1_DMRPD_EN,
+ usb2_base + USB2_LINECTRL1);
+
+ rcar_gen3_device_recognition(ch);
+}
+
+static int rcar_gen3_phy_usb2_init(struct phy *p)
+{
+ struct rcar_gen3_chan *channel = phy_get_drvdata(p);
+ void __iomem *usb2_base = channel->usb2.base;
+ void __iomem *hsusb_base = channel->hsusb.base;
+ u32 val;
+
+ /* Initialize USB2 part */
+ writel(USB2_INT_ENABLE_INIT, usb2_base + USB2_INT_ENABLE);
+ writel(USB2_SPD_RSM_TIMSET_INIT, usb2_base + USB2_SPD_RSM_TIMSET);
+ writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET);
+
+ /* Initialize HSUSB part */
+ if (hsusb_base) {
+ val = readl(hsusb_base + HSUSB_UGCTRL2);
+ val = (val & ~HSUSB_UGCTRL2_USB0SEL) |
+ HSUSB_UGCTRL2_USB0SEL_OTG;
+ writel(val & HSUSB_UGCTRL2_MASK, hsusb_base + HSUSB_UGCTRL2);
+
+ /* Initialize otg part */
+ rcar_gen3_init_otg(channel);
+ }
+
+ return 0;
+}
+
+static int rcar_gen3_phy_usb2_exit(struct phy *p)
+{
+ struct rcar_gen3_chan *channel = phy_get_drvdata(p);
+
+ writel(0, channel->usb2.base + USB2_INT_ENABLE);
+
+ return 0;
+}
+
+static int rcar_gen3_phy_usb2_power_on(struct phy *p)
+{
+ struct rcar_gen3_chan *channel = phy_get_drvdata(p);
+ void __iomem *usb2_base = channel->usb2.base;
+ void __iomem *hsusb_base = channel->hsusb.base;
+ u32 val;
+
+ val = readl(usb2_base + USB2_USBCTR);
+ val |= USB2_USBCTR_PLL_RST;
+ writel(val, usb2_base + USB2_USBCTR);
+ val &= ~USB2_USBCTR_PLL_RST;
+ writel(val, usb2_base + USB2_USBCTR);
+
+ /*
+ * TODO: To reduce power consuming, this driver should set the SUSPM
+ * after the PHY detects ID pin as peripheral.
+ */
+ if (hsusb_base) {
+ /* Power on HSUSB PHY */
+ val = readw(hsusb_base + HSUSB_LPSTS);
+ val |= HSUSB_LPSTS_SUSPM;
+ writew(val, hsusb_base + HSUSB_LPSTS);
+ }
+
+ return 0;
+}
+
+static int rcar_gen3_phy_usb2_power_off(struct phy *p)
+{
+ struct rcar_gen3_chan *channel = phy_get_drvdata(p);
+ void __iomem *hsusb_base = channel->hsusb.base;
+ u32 val;
+
+ if (hsusb_base) {
+ /* Power off HSUSB PHY */
+ val = readw(hsusb_base + HSUSB_LPSTS);
+ val &= ~HSUSB_LPSTS_SUSPM;
+ writew(val, hsusb_base + HSUSB_LPSTS);
+ }
+
+ return 0;
+}
+
+static struct phy_ops rcar_gen3_phy_usb2_ops = {
+ .init = rcar_gen3_phy_usb2_init,
+ .exit = rcar_gen3_phy_usb2_exit,
+ .power_on = rcar_gen3_phy_usb2_power_on,
+ .power_off = rcar_gen3_phy_usb2_power_off,
+ .owner = THIS_MODULE,
+};
+
+static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
+{
+ struct rcar_gen3_chan *ch = _ch;
+ void __iomem *usb2_base = ch->usb2.base;
+ u32 status = readl(usb2_base + USB2_OBINTSTA);
+ irqreturn_t ret = IRQ_NONE;
+
+ if (status & USB2_OBINT_BITS) {
+ dev_vdbg(&ch->phy->dev, "%s: %08x\n", __func__, status);
+ writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA);
+ rcar_gen3_device_recognition(ch);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = {
+ { .compatible = "renesas,usb2-phy-r8a7795" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table);
+
+static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rcar_gen3_chan *channel;
+ struct phy_provider *provider;
+ struct resource *res;
+
+ if (!dev->of_node) {
+ dev_err(dev, "This driver needs device tree\n");
+ return -EINVAL;
+ }
+
+ channel = devm_kzalloc(dev, sizeof(*channel), GFP_KERNEL);
+ if (!channel)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "usb2_host");
+ channel->usb2.base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(channel->usb2.base))
+ return PTR_ERR(channel->usb2.base);
+
+ /* "hsusb" memory resource is optional */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hsusb");
+
+ /* To avoid error message by devm_ioremap_resource() */
+ if (res) {
+ int irq;
+
+ channel->hsusb.base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(channel->hsusb.base))
+ channel->hsusb.base = NULL;
+ /* call request_irq for OTG */
+ irq = platform_get_irq(pdev, 0);
+ if (irq >= 0)
+ irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq,
+ IRQF_SHARED, dev_name(dev),
+ channel);
+ if (irq < 0)
+ dev_err(dev, "No irq handler (%d)\n", irq);
+ }
+
+ /* devm_phy_create() will call pm_runtime_enable(dev); */
+ channel->phy = devm_phy_create(dev, NULL, &rcar_gen3_phy_usb2_ops);
+ if (IS_ERR(channel->phy)) {
+ dev_err(dev, "Failed to create USB2 PHY\n");
+ return PTR_ERR(channel->phy);
+ }
+
+ phy_set_drvdata(channel->phy, channel);
+
+ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(provider))
+ dev_err(dev, "Failed to register PHY provider\n");
+
+ return PTR_ERR_OR_ZERO(provider);
+}
+
+static struct platform_driver rcar_gen3_phy_usb2_driver = {
+ .driver = {
+ .name = "phy_rcar_gen3_usb2",
+ .of_match_table = rcar_gen3_phy_usb2_match_table,
+ },
+ .probe = rcar_gen3_phy_usb2_probe,
+};
+module_platform_driver(rcar_gen3_phy_usb2_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Renesas R-Car Gen3 USB 2.0 PHY");
+MODULE_AUTHOR("Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>");
diff --git a/drivers/phy/phy-rockchip-usb.c b/drivers/phy/phy-rockchip-usb.c
index 91d6f342c565..33a80eba1cb4 100644
--- a/drivers/phy/phy-rockchip-usb.c
+++ b/drivers/phy/phy-rockchip-usb.c
@@ -15,12 +15,14 @@
*/
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
@@ -36,105 +38,271 @@
#define SIDDQ_ON BIT(13)
#define SIDDQ_OFF (0 << 13)
+struct rockchip_usb_phys {
+ int reg;
+ const char *pll_name;
+};
+
+struct rockchip_usb_phy_pdata {
+ struct rockchip_usb_phys *phys;
+};
+
+struct rockchip_usb_phy_base {
+ struct device *dev;
+ struct regmap *reg_base;
+ const struct rockchip_usb_phy_pdata *pdata;
+};
+
struct rockchip_usb_phy {
+ struct rockchip_usb_phy_base *base;
+ struct device_node *np;
unsigned int reg_offset;
- struct regmap *reg_base;
struct clk *clk;
+ struct clk *clk480m;
+ struct clk_hw clk480m_hw;
struct phy *phy;
};
static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
bool siddq)
{
- return regmap_write(phy->reg_base, phy->reg_offset,
+ return regmap_write(phy->base->reg_base, phy->reg_offset,
SIDDQ_WRITE_ENA | (siddq ? SIDDQ_ON : SIDDQ_OFF));
}
-static int rockchip_usb_phy_power_off(struct phy *_phy)
+static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
{
- struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
- int ret = 0;
+ return 480000000;
+}
+
+static void rockchip_usb_phy480m_disable(struct clk_hw *hw)
+{
+ struct rockchip_usb_phy *phy = container_of(hw,
+ struct rockchip_usb_phy,
+ clk480m_hw);
/* Power down usb phy analog blocks by set siddq 1 */
- ret = rockchip_usb_phy_power(phy, 1);
- if (ret)
- return ret;
+ rockchip_usb_phy_power(phy, 1);
+}
- clk_disable_unprepare(phy->clk);
+static int rockchip_usb_phy480m_enable(struct clk_hw *hw)
+{
+ struct rockchip_usb_phy *phy = container_of(hw,
+ struct rockchip_usb_phy,
+ clk480m_hw);
- return 0;
+ /* Power up usb phy analog blocks by set siddq 0 */
+ return rockchip_usb_phy_power(phy, 0);
}
-static int rockchip_usb_phy_power_on(struct phy *_phy)
+static int rockchip_usb_phy480m_is_enabled(struct clk_hw *hw)
{
- struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
- int ret = 0;
+ struct rockchip_usb_phy *phy = container_of(hw,
+ struct rockchip_usb_phy,
+ clk480m_hw);
+ int ret;
+ u32 val;
- ret = clk_prepare_enable(phy->clk);
- if (ret)
+ ret = regmap_read(phy->base->reg_base, phy->reg_offset, &val);
+ if (ret < 0)
return ret;
- /* Power up usb phy analog blocks by set siddq 0 */
- ret = rockchip_usb_phy_power(phy, 0);
- if (ret) {
- clk_disable_unprepare(phy->clk);
- return ret;
- }
+ return (val & SIDDQ_ON) ? 0 : 1;
+}
+
+static const struct clk_ops rockchip_usb_phy480m_ops = {
+ .enable = rockchip_usb_phy480m_enable,
+ .disable = rockchip_usb_phy480m_disable,
+ .is_enabled = rockchip_usb_phy480m_is_enabled,
+ .recalc_rate = rockchip_usb_phy480m_recalc_rate,
+};
+
+static int rockchip_usb_phy_power_off(struct phy *_phy)
+{
+ struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
+
+ clk_disable_unprepare(phy->clk480m);
return 0;
}
+static int rockchip_usb_phy_power_on(struct phy *_phy)
+{
+ struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
+
+ return clk_prepare_enable(phy->clk480m);
+}
+
static const struct phy_ops ops = {
.power_on = rockchip_usb_phy_power_on,
.power_off = rockchip_usb_phy_power_off,
.owner = THIS_MODULE,
};
-static int rockchip_usb_phy_probe(struct platform_device *pdev)
+static void rockchip_usb_phy_action(void *data)
+{
+ struct rockchip_usb_phy *rk_phy = data;
+
+ of_clk_del_provider(rk_phy->np);
+ clk_unregister(rk_phy->clk480m);
+
+ if (rk_phy->clk)
+ clk_put(rk_phy->clk);
+}
+
+static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
+ struct device_node *child)
{
- struct device *dev = &pdev->dev;
struct rockchip_usb_phy *rk_phy;
- struct phy_provider *phy_provider;
- struct device_node *child;
- struct regmap *grf;
unsigned int reg_offset;
- int err;
+ const char *clk_name;
+ struct clk_init_data init;
+ int err, i;
- grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
- if (IS_ERR(grf)) {
- dev_err(&pdev->dev, "Missing rockchip,grf property\n");
- return PTR_ERR(grf);
+ rk_phy = devm_kzalloc(base->dev, sizeof(*rk_phy), GFP_KERNEL);
+ if (!rk_phy)
+ return -ENOMEM;
+
+ rk_phy->base = base;
+ rk_phy->np = child;
+
+ if (of_property_read_u32(child, "reg", &reg_offset)) {
+ dev_err(base->dev, "missing reg property in node %s\n",
+ child->name);
+ return -EINVAL;
}
- for_each_available_child_of_node(dev->of_node, child) {
- rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);
- if (!rk_phy)
- return -ENOMEM;
-
- if (of_property_read_u32(child, "reg", &reg_offset)) {
- dev_err(dev, "missing reg property in node %s\n",
- child->name);
- return -EINVAL;
+ rk_phy->reg_offset = reg_offset;
+
+ rk_phy->clk = of_clk_get_by_name(child, "phyclk");
+ if (IS_ERR(rk_phy->clk))
+ rk_phy->clk = NULL;
+
+ i = 0;
+ init.name = NULL;
+ while (base->pdata->phys[i].reg) {
+ if (base->pdata->phys[i].reg == reg_offset) {
+ init.name = base->pdata->phys[i].pll_name;
+ break;
}
+ i++;
+ }
- rk_phy->reg_offset = reg_offset;
- rk_phy->reg_base = grf;
+ if (!init.name) {
+ dev_err(base->dev, "phy data not found\n");
+ return -EINVAL;
+ }
- rk_phy->clk = of_clk_get_by_name(child, "phyclk");
- if (IS_ERR(rk_phy->clk))
- rk_phy->clk = NULL;
+ if (rk_phy->clk) {
+ clk_name = __clk_get_name(rk_phy->clk);
+ init.flags = 0;
+ init.parent_names = &clk_name;
+ init.num_parents = 1;
+ } else {
+ init.flags = CLK_IS_ROOT;
+ init.parent_names = NULL;
+ init.num_parents = 0;
+ }
- rk_phy->phy = devm_phy_create(dev, child, &ops);
- if (IS_ERR(rk_phy->phy)) {
- dev_err(dev, "failed to create PHY\n");
- return PTR_ERR(rk_phy->phy);
- }
- phy_set_drvdata(rk_phy->phy, rk_phy);
+ init.ops = &rockchip_usb_phy480m_ops;
+ rk_phy->clk480m_hw.init = &init;
+
+ rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw);
+ if (IS_ERR(rk_phy->clk480m)) {
+ err = PTR_ERR(rk_phy->clk480m);
+ goto err_clk;
+ }
+
+ err = of_clk_add_provider(child, of_clk_src_simple_get,
+ rk_phy->clk480m);
+ if (err < 0)
+ goto err_clk_prov;
+
+ err = devm_add_action(base->dev, rockchip_usb_phy_action, rk_phy);
+ if (err)
+ goto err_devm_action;
+
+ rk_phy->phy = devm_phy_create(base->dev, child, &ops);
+ if (IS_ERR(rk_phy->phy)) {
+ dev_err(base->dev, "failed to create PHY\n");
+ return PTR_ERR(rk_phy->phy);
+ }
+ phy_set_drvdata(rk_phy->phy, rk_phy);
+
+ /* only power up usb phy when it use, so disable it when init*/
+ return rockchip_usb_phy_power(rk_phy, 1);
+
+err_devm_action:
+ of_clk_del_provider(child);
+err_clk_prov:
+ clk_unregister(rk_phy->clk480m);
+err_clk:
+ if (rk_phy->clk)
+ clk_put(rk_phy->clk);
+ return err;
+}
+
+static const struct rockchip_usb_phy_pdata rk3066a_pdata = {
+ .phys = (struct rockchip_usb_phys[]){
+ { .reg = 0x17c, .pll_name = "sclk_otgphy0_480m" },
+ { .reg = 0x188, .pll_name = "sclk_otgphy1_480m" },
+ { /* sentinel */ }
+ },
+};
- /* only power up usb phy when it use, so disable it when init*/
- err = rockchip_usb_phy_power(rk_phy, 1);
- if (err)
+static const struct rockchip_usb_phy_pdata rk3188_pdata = {
+ .phys = (struct rockchip_usb_phys[]){
+ { .reg = 0x10c, .pll_name = "sclk_otgphy0_480m" },
+ { .reg = 0x11c, .pll_name = "sclk_otgphy1_480m" },
+ { /* sentinel */ }
+ },
+};
+
+static const struct rockchip_usb_phy_pdata rk3288_pdata = {
+ .phys = (struct rockchip_usb_phys[]){
+ { .reg = 0x320, .pll_name = "sclk_otgphy0_480m" },
+ { .reg = 0x334, .pll_name = "sclk_otgphy1_480m" },
+ { .reg = 0x348, .pll_name = "sclk_otgphy2_480m" },
+ { /* sentinel */ }
+ },
+};
+
+static int rockchip_usb_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rockchip_usb_phy_base *phy_base;
+ struct phy_provider *phy_provider;
+ const struct of_device_id *match;
+ struct device_node *child;
+ int err;
+
+ phy_base = devm_kzalloc(dev, sizeof(*phy_base), GFP_KERNEL);
+ if (!phy_base)
+ return -ENOMEM;
+
+ match = of_match_device(dev->driver->of_match_table, dev);
+ if (!match || !match->data) {
+ dev_err(dev, "missing phy data\n");
+ return -EINVAL;
+ }
+
+ phy_base->pdata = match->data;
+
+ phy_base->dev = dev;
+ phy_base->reg_base = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,grf");
+ if (IS_ERR(phy_base->reg_base)) {
+ dev_err(&pdev->dev, "Missing rockchip,grf property\n");
+ return PTR_ERR(phy_base->reg_base);
+ }
+
+ for_each_available_child_of_node(dev->of_node, child) {
+ err = rockchip_usb_phy_init(phy_base, child);
+ if (err) {
+ of_node_put(child);
return err;
+ }
}
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
@@ -142,7 +310,9 @@ static int rockchip_usb_phy_probe(struct platform_device *pdev)
}
static const struct of_device_id rockchip_usb_phy_dt_ids[] = {
- { .compatible = "rockchip,rk3288-usb-phy" },
+ { .compatible = "rockchip,rk3066a-usb-phy", .data = &rk3066a_pdata },
+ { .compatible = "rockchip,rk3188-usb-phy", .data = &rk3188_pdata },
+ { .compatible = "rockchip,rk3288-usb-phy", .data = &rk3288_pdata },
{}
};
diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
index b12964b70625..bae54f7a1f48 100644
--- a/drivers/phy/phy-sun4i-usb.c
+++ b/drivers/phy/phy-sun4i-usb.c
@@ -32,6 +32,7 @@
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/phy/phy.h>
#include <linux/phy/phy-sun4i-usb.h>
@@ -46,6 +47,9 @@
#define REG_PHYBIST 0x08
#define REG_PHYTUNE 0x0c
#define REG_PHYCTL_A33 0x10
+#define REG_PHY_UNK_H3 0x20
+
+#define REG_PMU_UNK_H3 0x10
#define PHYCTL_DATA BIT(7)
@@ -79,7 +83,7 @@
#define PHY_DISCON_TH_SEL 0x2a
#define PHY_SQUELCH_DETECT 0x3c
-#define MAX_PHYS 3
+#define MAX_PHYS 4
/*
* Note do not raise the debounce time, we must report Vusb high within 100ms
@@ -88,12 +92,24 @@
#define DEBOUNCE_TIME msecs_to_jiffies(50)
#define POLL_TIME msecs_to_jiffies(250)
+enum sun4i_usb_phy_type {
+ sun4i_a10_phy,
+ sun8i_a33_phy,
+ sun8i_h3_phy,
+};
+
+struct sun4i_usb_phy_cfg {
+ int num_phys;
+ enum sun4i_usb_phy_type type;
+ u32 disc_thresh;
+ u8 phyctl_offset;
+ bool dedicated_clocks;
+};
+
struct sun4i_usb_phy_data {
void __iomem *base;
+ const struct sun4i_usb_phy_cfg *cfg;
struct mutex mutex;
- int num_phys;
- u32 disc_thresh;
- bool has_a33_phyctl;
struct sun4i_usb_phy {
struct phy *phy;
void __iomem *pmu;
@@ -159,17 +175,14 @@ static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
{
struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
u32 temp, usbc_bit = BIT(phy->index * 2);
- void *phyctl;
+ void *phyctl = phy_data->base + phy_data->cfg->phyctl_offset;
int i;
mutex_lock(&phy_data->mutex);
- if (phy_data->has_a33_phyctl) {
- phyctl = phy_data->base + REG_PHYCTL_A33;
+ if (phy_data->cfg->type == sun8i_a33_phy) {
/* A33 needs us to set phyctl to 0 explicitly */
writel(0, phyctl);
- } else {
- phyctl = phy_data->base + REG_PHYCTL_A10;
}
for (i = 0; i < len; i++) {
@@ -230,6 +243,7 @@ static int sun4i_usb_phy_init(struct phy *_phy)
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
int ret;
+ u32 val;
ret = clk_prepare_enable(phy->clk);
if (ret)
@@ -241,15 +255,26 @@ static int sun4i_usb_phy_init(struct phy *_phy)
return ret;
}
- /* Enable USB 45 Ohm resistor calibration */
- if (phy->index == 0)
- sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1);
+ if (data->cfg->type == sun8i_h3_phy) {
+ if (phy->index == 0) {
+ val = readl(data->base + REG_PHY_UNK_H3);
+ writel(val & ~1, data->base + REG_PHY_UNK_H3);
+ }
- /* Adjust PHY's magnitude and rate */
- sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
+ val = readl(phy->pmu + REG_PMU_UNK_H3);
+ writel(val & ~2, phy->pmu + REG_PMU_UNK_H3);
+ } else {
+ /* Enable USB 45 Ohm resistor calibration */
+ if (phy->index == 0)
+ sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1);
- /* Disconnect threshold adjustment */
- sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, data->disc_thresh, 2);
+ /* Adjust PHY's magnitude and rate */
+ sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
+
+ /* Disconnect threshold adjustment */
+ sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL,
+ data->cfg->disc_thresh, 2);
+ }
sun4i_usb_phy_passby(phy, 1);
@@ -476,7 +501,7 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev,
{
struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
- if (args->args[0] >= data->num_phys)
+ if (args->args[0] >= data->cfg->num_phys)
return ERR_PTR(-ENODEV);
return data->phys[args->args[0]].phy;
@@ -511,7 +536,6 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct phy_provider *phy_provider;
- bool dedicated_clocks;
struct resource *res;
int i, ret;
@@ -522,29 +546,9 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
mutex_init(&data->mutex);
INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
dev_set_drvdata(dev, data);
-
- if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy") ||
- of_device_is_compatible(np, "allwinner,sun8i-a23-usb-phy") ||
- of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy"))
- data->num_phys = 2;
- else
- data->num_phys = 3;
-
- if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy") ||
- of_device_is_compatible(np, "allwinner,sun7i-a20-usb-phy"))
- data->disc_thresh = 2;
- else
- data->disc_thresh = 3;
-
- if (of_device_is_compatible(np, "allwinner,sun6i-a31-usb-phy") ||
- of_device_is_compatible(np, "allwinner,sun8i-a23-usb-phy") ||
- of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy"))
- dedicated_clocks = true;
- else
- dedicated_clocks = false;
-
- if (of_device_is_compatible(np, "allwinner,sun8i-a33-usb-phy"))
- data->has_a33_phyctl = true;
+ data->cfg = of_device_get_match_data(dev);
+ if (!data->cfg)
+ return -EINVAL;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl");
data->base = devm_ioremap_resource(dev, res);
@@ -590,7 +594,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
}
}
- for (i = 0; i < data->num_phys; i++) {
+ for (i = 0; i < data->cfg->num_phys; i++) {
struct sun4i_usb_phy *phy = data->phys + i;
char name[16];
@@ -602,7 +606,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
phy->vbus = NULL;
}
- if (dedicated_clocks)
+ if (data->cfg->dedicated_clocks)
snprintf(name, sizeof(name), "usb%d_phy", i);
else
strlcpy(name, "usb_phy", sizeof(name));
@@ -689,13 +693,69 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
return 0;
}
+static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = {
+ .num_phys = 3,
+ .type = sun4i_a10_phy,
+ .disc_thresh = 3,
+ .phyctl_offset = REG_PHYCTL_A10,
+ .dedicated_clocks = false,
+};
+
+static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = {
+ .num_phys = 2,
+ .type = sun4i_a10_phy,
+ .disc_thresh = 2,
+ .phyctl_offset = REG_PHYCTL_A10,
+ .dedicated_clocks = false,
+};
+
+static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = {
+ .num_phys = 3,
+ .type = sun4i_a10_phy,
+ .disc_thresh = 3,
+ .phyctl_offset = REG_PHYCTL_A10,
+ .dedicated_clocks = true,
+};
+
+static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = {
+ .num_phys = 3,
+ .type = sun4i_a10_phy,
+ .disc_thresh = 2,
+ .phyctl_offset = REG_PHYCTL_A10,
+ .dedicated_clocks = false,
+};
+
+static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = {
+ .num_phys = 2,
+ .type = sun4i_a10_phy,
+ .disc_thresh = 3,
+ .phyctl_offset = REG_PHYCTL_A10,
+ .dedicated_clocks = true,
+};
+
+static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = {
+ .num_phys = 2,
+ .type = sun8i_a33_phy,
+ .disc_thresh = 3,
+ .phyctl_offset = REG_PHYCTL_A33,
+ .dedicated_clocks = true,
+};
+
+static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = {
+ .num_phys = 4,
+ .type = sun8i_h3_phy,
+ .disc_thresh = 3,
+ .dedicated_clocks = true,
+};
+
static const struct of_device_id sun4i_usb_phy_of_match[] = {
- { .compatible = "allwinner,sun4i-a10-usb-phy" },
- { .compatible = "allwinner,sun5i-a13-usb-phy" },
- { .compatible = "allwinner,sun6i-a31-usb-phy" },
- { .compatible = "allwinner,sun7i-a20-usb-phy" },
- { .compatible = "allwinner,sun8i-a23-usb-phy" },
- { .compatible = "allwinner,sun8i-a33-usb-phy" },
+ { .compatible = "allwinner,sun4i-a10-usb-phy", .data = &sun4i_a10_cfg },
+ { .compatible = "allwinner,sun5i-a13-usb-phy", .data = &sun5i_a13_cfg },
+ { .compatible = "allwinner,sun6i-a31-usb-phy", .data = &sun6i_a31_cfg },
+ { .compatible = "allwinner,sun7i-a20-usb-phy", .data = &sun7i_a20_cfg },
+ { .compatible = "allwinner,sun8i-a23-usb-phy", .data = &sun8i_a23_cfg },
+ { .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg },
+ { .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg },
{ },
};
MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c
index 93bc1120af12..0a477d24cf76 100644
--- a/drivers/phy/phy-ti-pipe3.c
+++ b/drivers/phy/phy-ti-pipe3.c
@@ -56,6 +56,18 @@
#define SATA_PLL_SOFT_RESET BIT(18)
+#define PIPE3_PHY_PWRCTL_CLK_CMD_MASK 0x003FC000
+#define PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT 14
+
+#define PIPE3_PHY_PWRCTL_CLK_FREQ_MASK 0xFFC00000
+#define PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT 22
+
+#define PIPE3_PHY_TX_RX_POWERON 0x3
+#define PIPE3_PHY_TX_RX_POWEROFF 0x0
+
+#define PCIE_PCS_MASK 0xFF0000
+#define PCIE_PCS_DELAY_COUNT_SHIFT 0x10
+
/*
* This is an Empirical value that works, need to confirm the actual
* value required for the PIPE3PHY_PLL_CONFIGURATION2.PLL_IDLE status
@@ -86,8 +98,12 @@ struct ti_pipe3 {
struct clk *refclk;
struct clk *div_clk;
struct pipe3_dpll_map *dpll_map;
+ struct regmap *phy_power_syscon; /* ctrl. reg. acces */
+ struct regmap *pcs_syscon; /* ctrl. reg. acces */
struct regmap *dpll_reset_syscon; /* ctrl. reg. acces */
unsigned int dpll_reset_reg; /* reg. index within syscon */
+ unsigned int power_reg; /* power reg. index within syscon */
+ unsigned int pcie_pcs_reg; /* pcs reg. index in syscon */
bool sata_refclk_enabled;
};
@@ -144,20 +160,49 @@ static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy);
static int ti_pipe3_power_off(struct phy *x)
{
+ u32 val;
+ int ret;
struct ti_pipe3 *phy = phy_get_drvdata(x);
- omap_control_phy_power(phy->control_dev, 0);
+ if (!phy->phy_power_syscon) {
+ omap_control_phy_power(phy->control_dev, 0);
+ return 0;
+ }
- return 0;
+ val = PIPE3_PHY_TX_RX_POWEROFF << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
+
+ ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg,
+ PIPE3_PHY_PWRCTL_CLK_CMD_MASK, val);
+ return ret;
}
static int ti_pipe3_power_on(struct phy *x)
{
+ u32 val;
+ u32 mask;
+ int ret;
+ unsigned long rate;
struct ti_pipe3 *phy = phy_get_drvdata(x);
- omap_control_phy_power(phy->control_dev, 1);
+ if (!phy->phy_power_syscon) {
+ omap_control_phy_power(phy->control_dev, 1);
+ return 0;
+ }
- return 0;
+ rate = clk_get_rate(phy->sys_clk);
+ if (!rate) {
+ dev_err(phy->dev, "Invalid clock rate\n");
+ return -EINVAL;
+ }
+ rate = rate / 1000000;
+ mask = OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK |
+ OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK;
+ val = PIPE3_PHY_TX_RX_POWERON << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
+ val |= rate << OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT;
+
+ ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg,
+ mask, val);
+ return ret;
}
static int ti_pipe3_dpll_wait_lock(struct ti_pipe3 *phy)
@@ -229,8 +274,15 @@ static int ti_pipe3_init(struct phy *x)
* 18-1804.
*/
if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) {
- omap_control_pcie_pcs(phy->control_dev, 0x96);
- return 0;
+ if (!phy->pcs_syscon) {
+ omap_control_pcie_pcs(phy->control_dev, 0x96);
+ return 0;
+ }
+
+ val = 0x96 << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT;
+ ret = regmap_update_bits(phy->pcs_syscon, phy->pcie_pcs_reg,
+ PCIE_PCS_MASK, val);
+ return ret;
}
/* Bring it out of IDLE if it is IDLE */
@@ -308,51 +360,15 @@ static const struct phy_ops ops = {
static const struct of_device_id ti_pipe3_id_table[];
-static int ti_pipe3_probe(struct platform_device *pdev)
+static int ti_pipe3_get_clk(struct ti_pipe3 *phy)
{
- struct ti_pipe3 *phy;
- struct phy *generic_phy;
- struct phy_provider *phy_provider;
- struct resource *res;
- struct device_node *node = pdev->dev.of_node;
- struct device_node *control_node;
- struct platform_device *control_pdev;
- const struct of_device_id *match;
struct clk *clk;
+ struct device *dev = phy->dev;
+ struct device_node *node = dev->of_node;
- phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
- if (!phy)
- return -ENOMEM;
-
- phy->dev = &pdev->dev;
-
- if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
- match = of_match_device(ti_pipe3_id_table, &pdev->dev);
- if (!match)
- return -EINVAL;
-
- phy->dpll_map = (struct pipe3_dpll_map *)match->data;
- if (!phy->dpll_map) {
- dev_err(&pdev->dev, "no DPLL data\n");
- return -EINVAL;
- }
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "pll_ctrl");
- phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(phy->pll_ctrl_base))
- return PTR_ERR(phy->pll_ctrl_base);
-
- phy->sys_clk = devm_clk_get(phy->dev, "sysclk");
- if (IS_ERR(phy->sys_clk)) {
- dev_err(&pdev->dev, "unable to get sysclk\n");
- return -EINVAL;
- }
- }
-
- phy->refclk = devm_clk_get(phy->dev, "refclk");
+ phy->refclk = devm_clk_get(dev, "refclk");
if (IS_ERR(phy->refclk)) {
- dev_err(&pdev->dev, "unable to get refclk\n");
+ dev_err(dev, "unable to get refclk\n");
/* older DTBs have missing refclk in SATA PHY
* so don't bail out in case of SATA PHY.
*/
@@ -361,80 +377,194 @@ static int ti_pipe3_probe(struct platform_device *pdev)
}
if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
- phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
+ phy->wkupclk = devm_clk_get(dev, "wkupclk");
if (IS_ERR(phy->wkupclk)) {
- dev_err(&pdev->dev, "unable to get wkupclk\n");
+ dev_err(dev, "unable to get wkupclk\n");
return PTR_ERR(phy->wkupclk);
}
} else {
phy->wkupclk = ERR_PTR(-ENODEV);
- phy->dpll_reset_syscon = syscon_regmap_lookup_by_phandle(node,
- "syscon-pllreset");
- if (IS_ERR(phy->dpll_reset_syscon)) {
- dev_info(&pdev->dev,
- "can't get syscon-pllreset, sata dpll won't idle\n");
- phy->dpll_reset_syscon = NULL;
- } else {
- if (of_property_read_u32_index(node,
- "syscon-pllreset", 1,
- &phy->dpll_reset_reg)) {
- dev_err(&pdev->dev,
- "couldn't get pllreset reg. offset\n");
- return -EINVAL;
- }
+ }
+
+ if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie") ||
+ phy->phy_power_syscon) {
+ phy->sys_clk = devm_clk_get(dev, "sysclk");
+ if (IS_ERR(phy->sys_clk)) {
+ dev_err(dev, "unable to get sysclk\n");
+ return -EINVAL;
}
}
if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
-
- clk = devm_clk_get(phy->dev, "dpll_ref");
+ clk = devm_clk_get(dev, "dpll_ref");
if (IS_ERR(clk)) {
- dev_err(&pdev->dev, "unable to get dpll ref clk\n");
+ dev_err(dev, "unable to get dpll ref clk\n");
return PTR_ERR(clk);
}
clk_set_rate(clk, 1500000000);
- clk = devm_clk_get(phy->dev, "dpll_ref_m2");
+ clk = devm_clk_get(dev, "dpll_ref_m2");
if (IS_ERR(clk)) {
- dev_err(&pdev->dev, "unable to get dpll ref m2 clk\n");
+ dev_err(dev, "unable to get dpll ref m2 clk\n");
return PTR_ERR(clk);
}
clk_set_rate(clk, 100000000);
- clk = devm_clk_get(phy->dev, "phy-div");
+ clk = devm_clk_get(dev, "phy-div");
if (IS_ERR(clk)) {
- dev_err(&pdev->dev, "unable to get phy-div clk\n");
+ dev_err(dev, "unable to get phy-div clk\n");
return PTR_ERR(clk);
}
clk_set_rate(clk, 100000000);
- phy->div_clk = devm_clk_get(phy->dev, "div-clk");
+ phy->div_clk = devm_clk_get(dev, "div-clk");
if (IS_ERR(phy->div_clk)) {
- dev_err(&pdev->dev, "unable to get div-clk\n");
+ dev_err(dev, "unable to get div-clk\n");
return PTR_ERR(phy->div_clk);
}
} else {
phy->div_clk = ERR_PTR(-ENODEV);
}
- control_node = of_parse_phandle(node, "ctrl-module", 0);
- if (!control_node) {
- dev_err(&pdev->dev, "Failed to get control device phandle\n");
- return -EINVAL;
+ return 0;
+}
+
+static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy)
+{
+ struct device *dev = phy->dev;
+ struct device_node *node = dev->of_node;
+ struct device_node *control_node;
+ struct platform_device *control_pdev;
+
+ phy->phy_power_syscon = syscon_regmap_lookup_by_phandle(node,
+ "syscon-phy-power");
+ if (IS_ERR(phy->phy_power_syscon)) {
+ dev_dbg(dev,
+ "can't get syscon-phy-power, using control device\n");
+ phy->phy_power_syscon = NULL;
+ } else {
+ if (of_property_read_u32_index(node,
+ "syscon-phy-power", 1,
+ &phy->power_reg)) {
+ dev_err(dev, "couldn't get power reg. offset\n");
+ return -EINVAL;
+ }
+ }
+
+ if (!phy->phy_power_syscon) {
+ control_node = of_parse_phandle(node, "ctrl-module", 0);
+ if (!control_node) {
+ dev_err(dev, "Failed to get control device phandle\n");
+ return -EINVAL;
+ }
+
+ control_pdev = of_find_device_by_node(control_node);
+ if (!control_pdev) {
+ dev_err(dev, "Failed to get control device\n");
+ return -EINVAL;
+ }
+
+ phy->control_dev = &control_pdev->dev;
+ }
+
+ if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
+ phy->pcs_syscon = syscon_regmap_lookup_by_phandle(node,
+ "syscon-pcs");
+ if (IS_ERR(phy->pcs_syscon)) {
+ dev_dbg(dev,
+ "can't get syscon-pcs, using omap control\n");
+ phy->pcs_syscon = NULL;
+ } else {
+ if (of_property_read_u32_index(node,
+ "syscon-pcs", 1,
+ &phy->pcie_pcs_reg)) {
+ dev_err(dev,
+ "couldn't get pcie pcs reg. offset\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
+ phy->dpll_reset_syscon = syscon_regmap_lookup_by_phandle(node,
+ "syscon-pllreset");
+ if (IS_ERR(phy->dpll_reset_syscon)) {
+ dev_info(dev,
+ "can't get syscon-pllreset, sata dpll won't idle\n");
+ phy->dpll_reset_syscon = NULL;
+ } else {
+ if (of_property_read_u32_index(node,
+ "syscon-pllreset", 1,
+ &phy->dpll_reset_reg)) {
+ dev_err(dev,
+ "couldn't get pllreset reg. offset\n");
+ return -EINVAL;
+ }
+ }
}
- control_pdev = of_find_device_by_node(control_node);
- if (!control_pdev) {
- dev_err(&pdev->dev, "Failed to get control device\n");
+ return 0;
+}
+
+static int ti_pipe3_get_pll_base(struct ti_pipe3 *phy)
+{
+ struct resource *res;
+ const struct of_device_id *match;
+ struct device *dev = phy->dev;
+ struct device_node *node = dev->of_node;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ if (of_device_is_compatible(node, "ti,phy-pipe3-pcie"))
+ return 0;
+
+ match = of_match_device(ti_pipe3_id_table, dev);
+ if (!match)
+ return -EINVAL;
+
+ phy->dpll_map = (struct pipe3_dpll_map *)match->data;
+ if (!phy->dpll_map) {
+ dev_err(dev, "no DPLL data\n");
return -EINVAL;
}
- phy->control_dev = &control_pdev->dev;
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "pll_ctrl");
+ phy->pll_ctrl_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(phy->pll_ctrl_base))
+ return PTR_ERR(phy->pll_ctrl_base);
+
+ return 0;
+}
- omap_control_phy_power(phy->control_dev, 0);
+static int ti_pipe3_probe(struct platform_device *pdev)
+{
+ struct ti_pipe3 *phy;
+ struct phy *generic_phy;
+ struct phy_provider *phy_provider;
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ phy->dev = dev;
+
+ ret = ti_pipe3_get_pll_base(phy);
+ if (ret)
+ return ret;
+
+ ret = ti_pipe3_get_sysctrl(phy);
+ if (ret)
+ return ret;
+
+ ret = ti_pipe3_get_clk(phy);
+ if (ret)
+ return ret;
platform_set_drvdata(pdev, phy);
- pm_runtime_enable(phy->dev);
+ pm_runtime_enable(dev);
/*
* Prevent auto-disable of refclk for SATA PHY due to Errata i783
@@ -446,13 +576,15 @@ static int ti_pipe3_probe(struct platform_device *pdev)
}
}
- generic_phy = devm_phy_create(phy->dev, NULL, &ops);
+ generic_phy = devm_phy_create(dev, NULL, &ops);
if (IS_ERR(generic_phy))
return PTR_ERR(generic_phy);
phy_set_drvdata(generic_phy, phy);
- phy_provider = devm_of_phy_provider_register(phy->dev,
- of_phy_simple_xlate);
+
+ ti_pipe3_power_off(generic_phy);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c
index 3a707dd14238..4a3fc6e59f8e 100644
--- a/drivers/phy/phy-twl4030-usb.c
+++ b/drivers/phy/phy-twl4030-usb.c
@@ -34,7 +34,7 @@
#include <linux/usb/otg.h>
#include <linux/phy/phy.h>
#include <linux/pm_runtime.h>
-#include <linux/usb/musb-omap.h>
+#include <linux/usb/musb.h>
#include <linux/usb/ulpi.h>
#include <linux/i2c/twl.h>
#include <linux/regulator/consumer.h>
@@ -148,10 +148,10 @@
* If VBUS is valid or ID is ground, then we know a
* cable is present and we need to be runtime-enabled
*/
-static inline bool cable_present(enum omap_musb_vbus_id_status stat)
+static inline bool cable_present(enum musb_vbus_id_status stat)
{
- return stat == OMAP_MUSB_VBUS_VALID ||
- stat == OMAP_MUSB_ID_GROUND;
+ return stat == MUSB_VBUS_VALID ||
+ stat == MUSB_ID_GROUND;
}
struct twl4030_usb {
@@ -170,7 +170,7 @@ struct twl4030_usb {
enum twl4030_usb_mode usb_mode;
int irq;
- enum omap_musb_vbus_id_status linkstat;
+ enum musb_vbus_id_status linkstat;
bool vbus_supplied;
struct delayed_work id_workaround_work;
@@ -276,11 +276,11 @@ static bool twl4030_is_driving_vbus(struct twl4030_usb *twl)
return (ret & (ULPI_OTG_DRVVBUS | ULPI_OTG_CHRGVBUS)) ? true : false;
}
-static enum omap_musb_vbus_id_status
+static enum musb_vbus_id_status
twl4030_usb_linkstat(struct twl4030_usb *twl)
{
int status;
- enum omap_musb_vbus_id_status linkstat = OMAP_MUSB_UNKNOWN;
+ enum musb_vbus_id_status linkstat = MUSB_UNKNOWN;
twl->vbus_supplied = false;
@@ -306,14 +306,14 @@ static enum omap_musb_vbus_id_status
}
if (status & BIT(2))
- linkstat = OMAP_MUSB_ID_GROUND;
+ linkstat = MUSB_ID_GROUND;
else if (status & BIT(7))
- linkstat = OMAP_MUSB_VBUS_VALID;
+ linkstat = MUSB_VBUS_VALID;
else
- linkstat = OMAP_MUSB_VBUS_OFF;
+ linkstat = MUSB_VBUS_OFF;
} else {
- if (twl->linkstat != OMAP_MUSB_UNKNOWN)
- linkstat = OMAP_MUSB_VBUS_OFF;
+ if (twl->linkstat != MUSB_UNKNOWN)
+ linkstat = MUSB_VBUS_OFF;
}
dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n",
@@ -535,7 +535,7 @@ static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL);
static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
{
struct twl4030_usb *twl = _twl;
- enum omap_musb_vbus_id_status status;
+ enum musb_vbus_id_status status;
bool status_changed = false;
status = twl4030_usb_linkstat(twl);
@@ -567,11 +567,11 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
pm_runtime_mark_last_busy(twl->dev);
pm_runtime_put_autosuspend(twl->dev);
}
- omap_musb_mailbox(status);
+ musb_mailbox(status);
}
/* don't schedule during sleep - irq works right then */
- if (status == OMAP_MUSB_ID_GROUND && pm_runtime_active(twl->dev)) {
+ if (status == MUSB_ID_GROUND && pm_runtime_active(twl->dev)) {
cancel_delayed_work(&twl->id_workaround_work);
schedule_delayed_work(&twl->id_workaround_work, HZ);
}
@@ -670,7 +670,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
twl->dev = &pdev->dev;
twl->irq = platform_get_irq(pdev, 0);
twl->vbus_supplied = false;
- twl->linkstat = OMAP_MUSB_UNKNOWN;
+ twl->linkstat = MUSB_UNKNOWN;
twl->phy.dev = twl->dev;
twl->phy.label = "twl4030";
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index b422e4ed73f4..99a4c10ed43f 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -5,8 +5,6 @@
config PINCTRL
bool
-if PINCTRL
-
menu "Pin controllers"
depends on PINCTRL
@@ -246,7 +244,7 @@ config PINCTRL_ZYNQ
select PINMUX
select GENERIC_PINCONF
help
- This selectes the pinctrl driver for Xilinx Zynq.
+ This selects the pinctrl driver for Xilinx Zynq.
source "drivers/pinctrl/bcm/Kconfig"
source "drivers/pinctrl/berlin/Kconfig"
@@ -254,6 +252,7 @@ source "drivers/pinctrl/freescale/Kconfig"
source "drivers/pinctrl/intel/Kconfig"
source "drivers/pinctrl/mvebu/Kconfig"
source "drivers/pinctrl/nomadik/Kconfig"
+source "drivers/pinctrl/pxa/Kconfig"
source "drivers/pinctrl/qcom/Kconfig"
source "drivers/pinctrl/samsung/Kconfig"
source "drivers/pinctrl/sh-pfc/Kconfig"
@@ -274,5 +273,3 @@ config PINCTRL_TB10X
select GPIOLIB
endmenu
-
-endif
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 738cb4929a49..bf1b5ca5180b 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -41,15 +41,16 @@ obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o
obj-$(CONFIG_PINCTRL_ZYNQ) += pinctrl-zynq.o
obj-$(CONFIG_ARCH_BCM) += bcm/
-obj-$(CONFIG_ARCH_BERLIN) += berlin/
+obj-$(CONFIG_PINCTRL_BERLIN) += berlin/
obj-y += freescale/
obj-$(CONFIG_X86) += intel/
-obj-$(CONFIG_PLAT_ORION) += mvebu/
+obj-$(CONFIG_PINCTRL_MVEBU) += mvebu/
obj-y += nomadik/
+obj-$(CONFIG_ARCH_PXA) += pxa/
obj-$(CONFIG_ARCH_QCOM) += qcom/
obj-$(CONFIG_PINCTRL_SAMSUNG) += samsung/
obj-$(CONFIG_PINCTRL_SH_PFC) += sh-pfc/
-obj-$(CONFIG_PLAT_SPEAR) += spear/
+obj-$(CONFIG_PINCTRL_SPEAR) += spear/
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/
obj-$(CONFIG_ARCH_VT8500) += vt8500/
diff --git a/drivers/pinctrl/bcm/Kconfig b/drivers/pinctrl/bcm/Kconfig
index cd11d4d9ad58..2cc74384cafa 100644
--- a/drivers/pinctrl/bcm/Kconfig
+++ b/drivers/pinctrl/bcm/Kconfig
@@ -9,6 +9,7 @@ config PINCTRL_BCM281XX
select PINCONF
select GENERIC_PINCONF
select REGMAP_MMIO
+ default ARCH_BCM_MOBILE
help
Say Y here to support Broadcom BCM281xx pinctrl driver, which is used
for the BCM281xx SoC family, including BCM11130, BCM11140, BCM11351,
@@ -20,27 +21,41 @@ config PINCTRL_BCM2835
select PINMUX
select PINCONF
-config PINCTRL_CYGNUS_GPIO
- bool "Broadcom Cygnus GPIO (with PINCONF) driver"
- depends on OF_GPIO && ARCH_BCM_CYGNUS
+config PINCTRL_IPROC_GPIO
+ bool "Broadcom iProc GPIO (with PINCONF) driver"
+ depends on OF_GPIO && (ARCH_BCM_IPROC || COMPILE_TEST)
select GPIOLIB_IRQCHIP
select PINCONF
select GENERIC_PINCONF
- default ARCH_BCM_CYGNUS
+ default ARCH_BCM_IPROC
help
- Say yes here to enable the Broadcom Cygnus GPIO driver.
+ Say yes here to enable the Broadcom iProc GPIO driver.
+
+ The Broadcom iProc based SoCs- Cygnus, NS2, NSP and Stingray, use
+ same GPIO Controller IP hence this driver could be used for all.
The Broadcom Cygnus SoC has 3 GPIO controllers including the ASIU
GPIO controller (ASIU), the chipCommonG GPIO controller (CCM), and
the always-ON GPIO controller (CRMU/AON). All 3 GPIO controllers are
supported by this driver.
- All 3 Cygnus GPIO controllers support basic PINCONF functions such
+ The Broadcom NSP has two GPIO controllers including the ChipcommonA
+ GPIO, the ChipcommonB GPIO. Later controller is supported by this
+ driver.
+
+ The Broadcom NS2 has two GPIO controller including the CRMU GPIO,
+ the ChipcommonG GPIO. Both controllers are supported by this driver.
+
+ The Broadcom Stingray GPIO controllers are supported by this driver.
+
+ All above SoCs GPIO controllers support basic PINCONF functions such
as bias pull up, pull down, and drive strength configurations, when
these pins are muxed to GPIO.
- Pins from the ASIU GPIO can be individually muxed to GPIO function,
- through interaction with the Cygnus IOMUX controller.
+ It provides the framework where pins from the individual GPIO can be
+ individually muxed to GPIO function, through interaction with the
+ SoCs IOMUX controller. This features could be used only on SoCs which
+ support individual pin muxing.
config PINCTRL_CYGNUS_MUX
bool "Broadcom Cygnus IOMUX driver"
@@ -54,3 +69,20 @@ config PINCTRL_CYGNUS_MUX
The Broadcom Cygnus IOMUX driver supports group based IOMUX
configuration, with the exception that certain individual pins
can be overrided to GPIO function
+
+config PINCTRL_NSP_GPIO
+ bool "Broadcom NSP GPIO (with PINCONF) driver"
+ depends on OF_GPIO && (ARCH_BCM_NSP || COMPILE_TEST)
+ select GPIOLIB_IRQCHIP
+ select PINCONF
+ select GENERIC_PINCONF
+ default ARCH_BCM_NSP
+ help
+ Say yes here to enable the Broadcom NSP GPIO driver.
+
+ The Broadcom Northstar Plus SoC ChipcommonA GPIO controller is
+ supported by this driver.
+
+ The ChipcommonA GPIO controller support basic PINCONF functions such
+ as bias pull up, pull down, and drive strength configurations, when
+ these pins are muxed to GPIO.
diff --git a/drivers/pinctrl/bcm/Makefile b/drivers/pinctrl/bcm/Makefile
index 2b2f70ee804c..6148367d5e8c 100644
--- a/drivers/pinctrl/bcm/Makefile
+++ b/drivers/pinctrl/bcm/Makefile
@@ -2,5 +2,6 @@
obj-$(CONFIG_PINCTRL_BCM281XX) += pinctrl-bcm281xx.o
obj-$(CONFIG_PINCTRL_BCM2835) += pinctrl-bcm2835.o
-obj-$(CONFIG_PINCTRL_CYGNUS_GPIO) += pinctrl-cygnus-gpio.o
+obj-$(CONFIG_PINCTRL_IPROC_GPIO) += pinctrl-iproc-gpio.o
obj-$(CONFIG_PINCTRL_CYGNUS_MUX) += pinctrl-cygnus-mux.o
+obj-$(CONFIG_PINCTRL_NSP_GPIO) += pinctrl-nsp-gpio.o
diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
index a1ea565fcd46..75b0d8c8f058 100644
--- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c
+++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
@@ -342,12 +342,6 @@ static int bcm2835_gpio_get(struct gpio_chip *chip, unsigned offset)
return bcm2835_gpio_get_bit(pc, GPLEV0, offset);
}
-static int bcm2835_gpio_direction_output(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- return pinctrl_gpio_direction_output(chip->base + offset);
-}
-
static void bcm2835_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct bcm2835_pinctrl *pc = dev_get_drvdata(chip->dev);
@@ -355,6 +349,13 @@ static void bcm2835_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
bcm2835_gpio_set_bit(pc, value ? GPSET0 : GPCLR0, offset);
}
+static int bcm2835_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ bcm2835_gpio_set(chip, offset, value);
+ return pinctrl_gpio_direction_output(chip->base + offset);
+}
+
static int bcm2835_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct bcm2835_pinctrl *pc = dev_get_drvdata(chip->dev);
@@ -794,7 +795,7 @@ static int bcm2835_pctl_dt_node_to_map(struct pinctrl_dev *pctldev,
return 0;
out:
- kfree(maps);
+ bcm2835_pctl_dt_free_map(pctldev, maps, num_pins * maps_per_pin);
return err;
}
diff --git a/drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c
index 12a48f498b75..314591a4609b 100644
--- a/drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c
+++ b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c
@@ -10,14 +10,16 @@
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * This file contains the Broadcom Cygnus GPIO driver that supports 3
- * GPIO controllers on Cygnus including the ASIU GPIO controller, the
+ * This file contains the Broadcom Iproc GPIO driver that supports 3
+ * GPIO controllers on Iproc including the ASIU GPIO controller, the
* chipCommonG GPIO controller, and the always-on GPIO controller. Basic
* PINCONF such as bias pull up/down, and drive strength are also supported
* in this driver.
*
- * Pins from the ASIU GPIO can be individually muxed to GPIO function,
- * through the interaction with the Cygnus IOMUX controller
+ * It provides the functionality where pins from the GPIO can be
+ * individually muxed to GPIO function, if individual pad
+ * configuration is supported, through the interaction with respective
+ * SoCs IOMUX controller.
*/
#include <linux/kernel.h>
@@ -34,42 +36,42 @@
#include "../pinctrl-utils.h"
-#define CYGNUS_GPIO_DATA_IN_OFFSET 0x00
-#define CYGNUS_GPIO_DATA_OUT_OFFSET 0x04
-#define CYGNUS_GPIO_OUT_EN_OFFSET 0x08
-#define CYGNUS_GPIO_INT_TYPE_OFFSET 0x0c
-#define CYGNUS_GPIO_INT_DE_OFFSET 0x10
-#define CYGNUS_GPIO_INT_EDGE_OFFSET 0x14
-#define CYGNUS_GPIO_INT_MSK_OFFSET 0x18
-#define CYGNUS_GPIO_INT_STAT_OFFSET 0x1c
-#define CYGNUS_GPIO_INT_MSTAT_OFFSET 0x20
-#define CYGNUS_GPIO_INT_CLR_OFFSET 0x24
-#define CYGNUS_GPIO_PAD_RES_OFFSET 0x34
-#define CYGNUS_GPIO_RES_EN_OFFSET 0x38
+#define IPROC_GPIO_DATA_IN_OFFSET 0x00
+#define IPROC_GPIO_DATA_OUT_OFFSET 0x04
+#define IPROC_GPIO_OUT_EN_OFFSET 0x08
+#define IPROC_GPIO_INT_TYPE_OFFSET 0x0c
+#define IPROC_GPIO_INT_DE_OFFSET 0x10
+#define IPROC_GPIO_INT_EDGE_OFFSET 0x14
+#define IPROC_GPIO_INT_MSK_OFFSET 0x18
+#define IPROC_GPIO_INT_STAT_OFFSET 0x1c
+#define IPROC_GPIO_INT_MSTAT_OFFSET 0x20
+#define IPROC_GPIO_INT_CLR_OFFSET 0x24
+#define IPROC_GPIO_PAD_RES_OFFSET 0x34
+#define IPROC_GPIO_RES_EN_OFFSET 0x38
/* drive strength control for ASIU GPIO */
-#define CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58
+#define IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58
/* drive strength control for CCM/CRMU (AON) GPIO */
-#define CYGNUS_GPIO_DRV0_CTRL_OFFSET 0x00
+#define IPROC_GPIO_DRV0_CTRL_OFFSET 0x00
#define GPIO_BANK_SIZE 0x200
#define NGPIOS_PER_BANK 32
#define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK)
-#define CYGNUS_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + (reg))
-#define CYGNUS_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK)
+#define IPROC_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + (reg))
+#define IPROC_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK)
#define GPIO_DRV_STRENGTH_BIT_SHIFT 20
#define GPIO_DRV_STRENGTH_BITS 3
#define GPIO_DRV_STRENGTH_BIT_MASK ((1 << GPIO_DRV_STRENGTH_BITS) - 1)
/*
- * Cygnus GPIO core
+ * Iproc GPIO core
*
* @dev: pointer to device
- * @base: I/O register base for Cygnus GPIO controller
- * @io_ctrl: I/O register base for certain type of Cygnus GPIO controller that
+ * @base: I/O register base for Iproc GPIO controller
+ * @io_ctrl: I/O register base for certain type of Iproc GPIO controller that
* has the PINCONF support implemented outside of the GPIO block
* @lock: lock to protect access to I/O registers
* @gc: GPIO chip
@@ -79,7 +81,7 @@
* @pctl: pointer to pinctrl_dev
* @pctldesc: pinctrl descriptor
*/
-struct cygnus_gpio {
+struct iproc_gpio {
struct device *dev;
void __iomem *base;
@@ -96,33 +98,33 @@ struct cygnus_gpio {
struct pinctrl_desc pctldesc;
};
-static inline struct cygnus_gpio *to_cygnus_gpio(struct gpio_chip *gc)
+static inline struct iproc_gpio *to_iproc_gpio(struct gpio_chip *gc)
{
- return container_of(gc, struct cygnus_gpio, gc);
+ return container_of(gc, struct iproc_gpio, gc);
}
/*
* Mapping from PINCONF pins to GPIO pins is 1-to-1
*/
-static inline unsigned cygnus_pin_to_gpio(unsigned pin)
+static inline unsigned iproc_pin_to_gpio(unsigned pin)
{
return pin;
}
/**
- * cygnus_set_bit - set or clear one bit (corresponding to the GPIO pin) in a
- * Cygnus GPIO register
+ * iproc_set_bit - set or clear one bit (corresponding to the GPIO pin) in a
+ * Iproc GPIO register
*
- * @cygnus_gpio: Cygnus GPIO device
+ * @iproc_gpio: Iproc GPIO device
* @reg: register offset
* @gpio: GPIO pin
* @set: set or clear
*/
-static inline void cygnus_set_bit(struct cygnus_gpio *chip, unsigned int reg,
+static inline void iproc_set_bit(struct iproc_gpio *chip, unsigned int reg,
unsigned gpio, bool set)
{
- unsigned int offset = CYGNUS_GPIO_REG(gpio, reg);
- unsigned int shift = CYGNUS_GPIO_SHIFT(gpio);
+ unsigned int offset = IPROC_GPIO_REG(gpio, reg);
+ unsigned int shift = IPROC_GPIO_SHIFT(gpio);
u32 val;
val = readl(chip->base + offset);
@@ -133,19 +135,19 @@ static inline void cygnus_set_bit(struct cygnus_gpio *chip, unsigned int reg,
writel(val, chip->base + offset);
}
-static inline bool cygnus_get_bit(struct cygnus_gpio *chip, unsigned int reg,
+static inline bool iproc_get_bit(struct iproc_gpio *chip, unsigned int reg,
unsigned gpio)
{
- unsigned int offset = CYGNUS_GPIO_REG(gpio, reg);
- unsigned int shift = CYGNUS_GPIO_SHIFT(gpio);
+ unsigned int offset = IPROC_GPIO_REG(gpio, reg);
+ unsigned int shift = IPROC_GPIO_SHIFT(gpio);
return !!(readl(chip->base + offset) & BIT(shift));
}
-static void cygnus_gpio_irq_handler(struct irq_desc *desc)
+static void iproc_gpio_irq_handler(struct irq_desc *desc)
{
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
- struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+ struct iproc_gpio *chip = to_iproc_gpio(gc);
struct irq_chip *irq_chip = irq_desc_get_chip(desc);
int i, bit;
@@ -154,7 +156,7 @@ static void cygnus_gpio_irq_handler(struct irq_desc *desc)
/* go through the entire GPIO banks and handle all interrupts */
for (i = 0; i < chip->num_banks; i++) {
unsigned long val = readl(chip->base + (i * GPIO_BANK_SIZE) +
- CYGNUS_GPIO_INT_MSTAT_OFFSET);
+ IPROC_GPIO_INT_MSTAT_OFFSET);
for_each_set_bit(bit, &val, NGPIOS_PER_BANK) {
unsigned pin = NGPIOS_PER_BANK * i + bit;
@@ -165,7 +167,7 @@ static void cygnus_gpio_irq_handler(struct irq_desc *desc)
* handler, so we do not leave any window
*/
writel(BIT(bit), chip->base + (i * GPIO_BANK_SIZE) +
- CYGNUS_GPIO_INT_CLR_OFFSET);
+ IPROC_GPIO_INT_CLR_OFFSET);
generic_handle_irq(child_irq);
}
@@ -175,60 +177,60 @@ static void cygnus_gpio_irq_handler(struct irq_desc *desc)
}
-static void cygnus_gpio_irq_ack(struct irq_data *d)
+static void iproc_gpio_irq_ack(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+ struct iproc_gpio *chip = to_iproc_gpio(gc);
unsigned gpio = d->hwirq;
- unsigned int offset = CYGNUS_GPIO_REG(gpio,
- CYGNUS_GPIO_INT_CLR_OFFSET);
- unsigned int shift = CYGNUS_GPIO_SHIFT(gpio);
+ unsigned int offset = IPROC_GPIO_REG(gpio,
+ IPROC_GPIO_INT_CLR_OFFSET);
+ unsigned int shift = IPROC_GPIO_SHIFT(gpio);
u32 val = BIT(shift);
writel(val, chip->base + offset);
}
/**
- * cygnus_gpio_irq_set_mask - mask/unmask a GPIO interrupt
+ * iproc_gpio_irq_set_mask - mask/unmask a GPIO interrupt
*
* @d: IRQ chip data
* @unmask: mask/unmask GPIO interrupt
*/
-static void cygnus_gpio_irq_set_mask(struct irq_data *d, bool unmask)
+static void iproc_gpio_irq_set_mask(struct irq_data *d, bool unmask)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+ struct iproc_gpio *chip = to_iproc_gpio(gc);
unsigned gpio = d->hwirq;
- cygnus_set_bit(chip, CYGNUS_GPIO_INT_MSK_OFFSET, gpio, unmask);
+ iproc_set_bit(chip, IPROC_GPIO_INT_MSK_OFFSET, gpio, unmask);
}
-static void cygnus_gpio_irq_mask(struct irq_data *d)
+static void iproc_gpio_irq_mask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+ struct iproc_gpio *chip = to_iproc_gpio(gc);
unsigned long flags;
spin_lock_irqsave(&chip->lock, flags);
- cygnus_gpio_irq_set_mask(d, false);
+ iproc_gpio_irq_set_mask(d, false);
spin_unlock_irqrestore(&chip->lock, flags);
}
-static void cygnus_gpio_irq_unmask(struct irq_data *d)
+static void iproc_gpio_irq_unmask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+ struct iproc_gpio *chip = to_iproc_gpio(gc);
unsigned long flags;
spin_lock_irqsave(&chip->lock, flags);
- cygnus_gpio_irq_set_mask(d, true);
+ iproc_gpio_irq_set_mask(d, true);
spin_unlock_irqrestore(&chip->lock, flags);
}
-static int cygnus_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+static int iproc_gpio_irq_set_type(struct irq_data *d, unsigned int type)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
- struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+ struct iproc_gpio *chip = to_iproc_gpio(gc);
unsigned gpio = d->hwirq;
bool level_triggered = false;
bool dual_edge = false;
@@ -263,10 +265,10 @@ static int cygnus_gpio_irq_set_type(struct irq_data *d, unsigned int type)
}
spin_lock_irqsave(&chip->lock, flags);
- cygnus_set_bit(chip, CYGNUS_GPIO_INT_TYPE_OFFSET, gpio,
+ iproc_set_bit(chip, IPROC_GPIO_INT_TYPE_OFFSET, gpio,
level_triggered);
- cygnus_set_bit(chip, CYGNUS_GPIO_INT_DE_OFFSET, gpio, dual_edge);
- cygnus_set_bit(chip, CYGNUS_GPIO_INT_EDGE_OFFSET, gpio,
+ iproc_set_bit(chip, IPROC_GPIO_INT_DE_OFFSET, gpio, dual_edge);
+ iproc_set_bit(chip, IPROC_GPIO_INT_EDGE_OFFSET, gpio,
rising_or_high);
spin_unlock_irqrestore(&chip->lock, flags);
@@ -277,32 +279,32 @@ static int cygnus_gpio_irq_set_type(struct irq_data *d, unsigned int type)
return 0;
}
-static struct irq_chip cygnus_gpio_irq_chip = {
- .name = "bcm-cygnus-gpio",
- .irq_ack = cygnus_gpio_irq_ack,
- .irq_mask = cygnus_gpio_irq_mask,
- .irq_unmask = cygnus_gpio_irq_unmask,
- .irq_set_type = cygnus_gpio_irq_set_type,
+static struct irq_chip iproc_gpio_irq_chip = {
+ .name = "bcm-iproc-gpio",
+ .irq_ack = iproc_gpio_irq_ack,
+ .irq_mask = iproc_gpio_irq_mask,
+ .irq_unmask = iproc_gpio_irq_unmask,
+ .irq_set_type = iproc_gpio_irq_set_type,
};
/*
- * Request the Cygnus IOMUX pinmux controller to mux individual pins to GPIO
+ * Request the Iproc IOMUX pinmux controller to mux individual pins to GPIO
*/
-static int cygnus_gpio_request(struct gpio_chip *gc, unsigned offset)
+static int iproc_gpio_request(struct gpio_chip *gc, unsigned offset)
{
- struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+ struct iproc_gpio *chip = to_iproc_gpio(gc);
unsigned gpio = gc->base + offset;
- /* not all Cygnus GPIO pins can be muxed individually */
+ /* not all Iproc GPIO pins can be muxed individually */
if (!chip->pinmux_is_supported)
return 0;
return pinctrl_request_gpio(gpio);
}
-static void cygnus_gpio_free(struct gpio_chip *gc, unsigned offset)
+static void iproc_gpio_free(struct gpio_chip *gc, unsigned offset)
{
- struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+ struct iproc_gpio *chip = to_iproc_gpio(gc);
unsigned gpio = gc->base + offset;
if (!chip->pinmux_is_supported)
@@ -311,13 +313,13 @@ static void cygnus_gpio_free(struct gpio_chip *gc, unsigned offset)
pinctrl_free_gpio(gpio);
}
-static int cygnus_gpio_direction_input(struct gpio_chip *gc, unsigned gpio)
+static int iproc_gpio_direction_input(struct gpio_chip *gc, unsigned gpio)
{
- struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+ struct iproc_gpio *chip = to_iproc_gpio(gc);
unsigned long flags;
spin_lock_irqsave(&chip->lock, flags);
- cygnus_set_bit(chip, CYGNUS_GPIO_OUT_EN_OFFSET, gpio, false);
+ iproc_set_bit(chip, IPROC_GPIO_OUT_EN_OFFSET, gpio, false);
spin_unlock_irqrestore(&chip->lock, flags);
dev_dbg(chip->dev, "gpio:%u set input\n", gpio);
@@ -325,15 +327,15 @@ static int cygnus_gpio_direction_input(struct gpio_chip *gc, unsigned gpio)
return 0;
}
-static int cygnus_gpio_direction_output(struct gpio_chip *gc, unsigned gpio,
+static int iproc_gpio_direction_output(struct gpio_chip *gc, unsigned gpio,
int val)
{
- struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+ struct iproc_gpio *chip = to_iproc_gpio(gc);
unsigned long flags;
spin_lock_irqsave(&chip->lock, flags);
- cygnus_set_bit(chip, CYGNUS_GPIO_OUT_EN_OFFSET, gpio, true);
- cygnus_set_bit(chip, CYGNUS_GPIO_DATA_OUT_OFFSET, gpio, !!(val));
+ iproc_set_bit(chip, IPROC_GPIO_OUT_EN_OFFSET, gpio, true);
+ iproc_set_bit(chip, IPROC_GPIO_DATA_OUT_OFFSET, gpio, !!(val));
spin_unlock_irqrestore(&chip->lock, flags);
dev_dbg(chip->dev, "gpio:%u set output, value:%d\n", gpio, val);
@@ -341,29 +343,29 @@ static int cygnus_gpio_direction_output(struct gpio_chip *gc, unsigned gpio,
return 0;
}
-static void cygnus_gpio_set(struct gpio_chip *gc, unsigned gpio, int val)
+static void iproc_gpio_set(struct gpio_chip *gc, unsigned gpio, int val)
{
- struct cygnus_gpio *chip = to_cygnus_gpio(gc);
+ struct iproc_gpio *chip = to_iproc_gpio(gc);
unsigned long flags;
spin_lock_irqsave(&chip->lock, flags);
- cygnus_set_bit(chip, CYGNUS_GPIO_DATA_OUT_OFFSET, gpio, !!(val));
+ iproc_set_bit(chip, IPROC_GPIO_DATA_OUT_OFFSET, gpio, !!(val));
spin_unlock_irqrestore(&chip->lock, flags);
dev_dbg(chip->dev, "gpio:%u set, value:%d\n", gpio, val);
}
-static int cygnus_gpio_get(struct gpio_chip *gc, unsigned gpio)
+static int iproc_gpio_get(struct gpio_chip *gc, unsigned gpio)
{
- struct cygnus_gpio *chip = to_cygnus_gpio(gc);
- unsigned int offset = CYGNUS_GPIO_REG(gpio,
- CYGNUS_GPIO_DATA_IN_OFFSET);
- unsigned int shift = CYGNUS_GPIO_SHIFT(gpio);
+ struct iproc_gpio *chip = to_iproc_gpio(gc);
+ unsigned int offset = IPROC_GPIO_REG(gpio,
+ IPROC_GPIO_DATA_IN_OFFSET);
+ unsigned int shift = IPROC_GPIO_SHIFT(gpio);
return !!(readl(chip->base + offset) & BIT(shift));
}
-static int cygnus_get_groups_count(struct pinctrl_dev *pctldev)
+static int iproc_get_groups_count(struct pinctrl_dev *pctldev)
{
return 1;
}
@@ -372,20 +374,20 @@ static int cygnus_get_groups_count(struct pinctrl_dev *pctldev)
* Only one group: "gpio_grp", since this local pinctrl device only performs
* GPIO specific PINCONF configurations
*/
-static const char *cygnus_get_group_name(struct pinctrl_dev *pctldev,
+static const char *iproc_get_group_name(struct pinctrl_dev *pctldev,
unsigned selector)
{
return "gpio_grp";
}
-static const struct pinctrl_ops cygnus_pctrl_ops = {
- .get_groups_count = cygnus_get_groups_count,
- .get_group_name = cygnus_get_group_name,
+static const struct pinctrl_ops iproc_pctrl_ops = {
+ .get_groups_count = iproc_get_groups_count,
+ .get_group_name = iproc_get_group_name,
.dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
.dt_free_map = pinctrl_utils_dt_free_map,
};
-static int cygnus_gpio_set_pull(struct cygnus_gpio *chip, unsigned gpio,
+static int iproc_gpio_set_pull(struct iproc_gpio *chip, unsigned gpio,
bool disable, bool pull_up)
{
unsigned long flags;
@@ -393,11 +395,11 @@ static int cygnus_gpio_set_pull(struct cygnus_gpio *chip, unsigned gpio,
spin_lock_irqsave(&chip->lock, flags);
if (disable) {
- cygnus_set_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio, false);
+ iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, false);
} else {
- cygnus_set_bit(chip, CYGNUS_GPIO_PAD_RES_OFFSET, gpio,
+ iproc_set_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio,
pull_up);
- cygnus_set_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio, true);
+ iproc_set_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio, true);
}
spin_unlock_irqrestore(&chip->lock, flags);
@@ -407,18 +409,18 @@ static int cygnus_gpio_set_pull(struct cygnus_gpio *chip, unsigned gpio,
return 0;
}
-static void cygnus_gpio_get_pull(struct cygnus_gpio *chip, unsigned gpio,
+static void iproc_gpio_get_pull(struct iproc_gpio *chip, unsigned gpio,
bool *disable, bool *pull_up)
{
unsigned long flags;
spin_lock_irqsave(&chip->lock, flags);
- *disable = !cygnus_get_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio);
- *pull_up = cygnus_get_bit(chip, CYGNUS_GPIO_PAD_RES_OFFSET, gpio);
+ *disable = !iproc_get_bit(chip, IPROC_GPIO_RES_EN_OFFSET, gpio);
+ *pull_up = iproc_get_bit(chip, IPROC_GPIO_PAD_RES_OFFSET, gpio);
spin_unlock_irqrestore(&chip->lock, flags);
}
-static int cygnus_gpio_set_strength(struct cygnus_gpio *chip, unsigned gpio,
+static int iproc_gpio_set_strength(struct iproc_gpio *chip, unsigned gpio,
unsigned strength)
{
void __iomem *base;
@@ -432,14 +434,14 @@ static int cygnus_gpio_set_strength(struct cygnus_gpio *chip, unsigned gpio,
if (chip->io_ctrl) {
base = chip->io_ctrl;
- offset = CYGNUS_GPIO_DRV0_CTRL_OFFSET;
+ offset = IPROC_GPIO_DRV0_CTRL_OFFSET;
} else {
base = chip->base;
- offset = CYGNUS_GPIO_REG(gpio,
- CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET);
+ offset = IPROC_GPIO_REG(gpio,
+ IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET);
}
- shift = CYGNUS_GPIO_SHIFT(gpio);
+ shift = IPROC_GPIO_SHIFT(gpio);
dev_dbg(chip->dev, "gpio:%u set drive strength:%d mA\n", gpio,
strength);
@@ -458,7 +460,7 @@ static int cygnus_gpio_set_strength(struct cygnus_gpio *chip, unsigned gpio,
return 0;
}
-static int cygnus_gpio_get_strength(struct cygnus_gpio *chip, unsigned gpio,
+static int iproc_gpio_get_strength(struct iproc_gpio *chip, unsigned gpio,
u16 *strength)
{
void __iomem *base;
@@ -468,14 +470,14 @@ static int cygnus_gpio_get_strength(struct cygnus_gpio *chip, unsigned gpio,
if (chip->io_ctrl) {
base = chip->io_ctrl;
- offset = CYGNUS_GPIO_DRV0_CTRL_OFFSET;
+ offset = IPROC_GPIO_DRV0_CTRL_OFFSET;
} else {
base = chip->base;
- offset = CYGNUS_GPIO_REG(gpio,
- CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET);
+ offset = IPROC_GPIO_REG(gpio,
+ IPROC_GPIO_ASIU_DRV0_CTRL_OFFSET);
}
- shift = CYGNUS_GPIO_SHIFT(gpio);
+ shift = IPROC_GPIO_SHIFT(gpio);
spin_lock_irqsave(&chip->lock, flags);
*strength = 0;
@@ -493,44 +495,43 @@ static int cygnus_gpio_get_strength(struct cygnus_gpio *chip, unsigned gpio,
return 0;
}
-static int cygnus_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin,
+static int iproc_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin,
unsigned long *config)
{
- struct cygnus_gpio *chip = pinctrl_dev_get_drvdata(pctldev);
+ struct iproc_gpio *chip = pinctrl_dev_get_drvdata(pctldev);
enum pin_config_param param = pinconf_to_config_param(*config);
- unsigned gpio = cygnus_pin_to_gpio(pin);
+ unsigned gpio = iproc_pin_to_gpio(pin);
u16 arg;
bool disable, pull_up;
int ret;
switch (param) {
case PIN_CONFIG_BIAS_DISABLE:
- cygnus_gpio_get_pull(chip, gpio, &disable, &pull_up);
+ iproc_gpio_get_pull(chip, gpio, &disable, &pull_up);
if (disable)
return 0;
else
return -EINVAL;
case PIN_CONFIG_BIAS_PULL_UP:
- cygnus_gpio_get_pull(chip, gpio, &disable, &pull_up);
+ iproc_gpio_get_pull(chip, gpio, &disable, &pull_up);
if (!disable && pull_up)
return 0;
else
return -EINVAL;
case PIN_CONFIG_BIAS_PULL_DOWN:
- cygnus_gpio_get_pull(chip, gpio, &disable, &pull_up);
+ iproc_gpio_get_pull(chip, gpio, &disable, &pull_up);
if (!disable && !pull_up)
return 0;
else
return -EINVAL;
case PIN_CONFIG_DRIVE_STRENGTH:
- ret = cygnus_gpio_get_strength(chip, gpio, &arg);
+ ret = iproc_gpio_get_strength(chip, gpio, &arg);
if (ret)
return ret;
- else
- *config = pinconf_to_config_packed(param, arg);
+ *config = pinconf_to_config_packed(param, arg);
return 0;
@@ -541,13 +542,13 @@ static int cygnus_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin,
return -ENOTSUPP;
}
-static int cygnus_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin,
+static int iproc_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin,
unsigned long *configs, unsigned num_configs)
{
- struct cygnus_gpio *chip = pinctrl_dev_get_drvdata(pctldev);
+ struct iproc_gpio *chip = pinctrl_dev_get_drvdata(pctldev);
enum pin_config_param param;
u16 arg;
- unsigned i, gpio = cygnus_pin_to_gpio(pin);
+ unsigned i, gpio = iproc_pin_to_gpio(pin);
int ret = -ENOTSUPP;
for (i = 0; i < num_configs; i++) {
@@ -556,25 +557,25 @@ static int cygnus_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin,
switch (param) {
case PIN_CONFIG_BIAS_DISABLE:
- ret = cygnus_gpio_set_pull(chip, gpio, true, false);
+ ret = iproc_gpio_set_pull(chip, gpio, true, false);
if (ret < 0)
goto out;
break;
case PIN_CONFIG_BIAS_PULL_UP:
- ret = cygnus_gpio_set_pull(chip, gpio, false, true);
+ ret = iproc_gpio_set_pull(chip, gpio, false, true);
if (ret < 0)
goto out;
break;
case PIN_CONFIG_BIAS_PULL_DOWN:
- ret = cygnus_gpio_set_pull(chip, gpio, false, false);
+ ret = iproc_gpio_set_pull(chip, gpio, false, false);
if (ret < 0)
goto out;
break;
case PIN_CONFIG_DRIVE_STRENGTH:
- ret = cygnus_gpio_set_strength(chip, gpio, arg);
+ ret = iproc_gpio_set_strength(chip, gpio, arg);
if (ret < 0)
goto out;
break;
@@ -589,20 +590,20 @@ out:
return ret;
}
-static const struct pinconf_ops cygnus_pconf_ops = {
+static const struct pinconf_ops iproc_pconf_ops = {
.is_generic = true,
- .pin_config_get = cygnus_pin_config_get,
- .pin_config_set = cygnus_pin_config_set,
+ .pin_config_get = iproc_pin_config_get,
+ .pin_config_set = iproc_pin_config_set,
};
/*
- * Cygnus GPIO controller supports some PINCONF related configurations such as
+ * Iproc GPIO controller supports some PINCONF related configurations such as
* pull up, pull down, and drive strength, when the pin is configured to GPIO
*
* Here a local pinctrl device is created with simple 1-to-1 pin mapping to the
* local GPIO pins
*/
-static int cygnus_gpio_register_pinconf(struct cygnus_gpio *chip)
+static int iproc_gpio_register_pinconf(struct iproc_gpio *chip)
{
struct pinctrl_desc *pctldesc = &chip->pctldesc;
struct pinctrl_pin_desc *pins;
@@ -622,10 +623,10 @@ static int cygnus_gpio_register_pinconf(struct cygnus_gpio *chip)
}
pctldesc->name = dev_name(chip->dev);
- pctldesc->pctlops = &cygnus_pctrl_ops;
+ pctldesc->pctlops = &iproc_pctrl_ops;
pctldesc->pins = pins;
pctldesc->npins = gc->ngpio;
- pctldesc->confops = &cygnus_pconf_ops;
+ pctldesc->confops = &iproc_pconf_ops;
chip->pctl = pinctrl_register(pctldesc, chip->dev, chip);
if (IS_ERR(chip->pctl)) {
@@ -636,59 +637,27 @@ static int cygnus_gpio_register_pinconf(struct cygnus_gpio *chip)
return 0;
}
-static void cygnus_gpio_unregister_pinconf(struct cygnus_gpio *chip)
+static void iproc_gpio_unregister_pinconf(struct iproc_gpio *chip)
{
- if (chip->pctl)
- pinctrl_unregister(chip->pctl);
+ pinctrl_unregister(chip->pctl);
}
-struct cygnus_gpio_data {
- unsigned num_gpios;
-};
-
-static const struct cygnus_gpio_data cygnus_cmm_gpio_data = {
- .num_gpios = 24,
-};
-
-static const struct cygnus_gpio_data cygnus_asiu_gpio_data = {
- .num_gpios = 146,
+static const struct of_device_id iproc_gpio_of_match[] = {
+ { .compatible = "brcm,cygnus-ccm-gpio" },
+ { .compatible = "brcm,cygnus-asiu-gpio" },
+ { .compatible = "brcm,cygnus-crmu-gpio" },
+ { .compatible = "brcm,iproc-gpio" },
+ { }
};
-static const struct cygnus_gpio_data cygnus_crmu_gpio_data = {
- .num_gpios = 6,
-};
-
-static const struct of_device_id cygnus_gpio_of_match[] = {
- {
- .compatible = "brcm,cygnus-ccm-gpio",
- .data = &cygnus_cmm_gpio_data,
- },
- {
- .compatible = "brcm,cygnus-asiu-gpio",
- .data = &cygnus_asiu_gpio_data,
- },
- {
- .compatible = "brcm,cygnus-crmu-gpio",
- .data = &cygnus_crmu_gpio_data,
- }
-};
-
-static int cygnus_gpio_probe(struct platform_device *pdev)
+static int iproc_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
- struct cygnus_gpio *chip;
+ struct iproc_gpio *chip;
struct gpio_chip *gc;
u32 ngpios;
int irq, ret;
- const struct of_device_id *match;
- const struct cygnus_gpio_data *gpio_data;
-
- match = of_match_device(cygnus_gpio_of_match, dev);
- if (!match)
- return -ENODEV;
- gpio_data = match->data;
- ngpios = gpio_data->num_gpios;
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
@@ -713,6 +682,11 @@ static int cygnus_gpio_probe(struct platform_device *pdev)
}
}
+ if (of_property_read_u32(dev->of_node, "ngpios", &ngpios)) {
+ dev_err(&pdev->dev, "missing ngpios DT property\n");
+ return -ENODEV;
+ }
+
spin_lock_init(&chip->lock);
gc = &chip->gc;
@@ -722,12 +696,12 @@ static int cygnus_gpio_probe(struct platform_device *pdev)
gc->label = dev_name(dev);
gc->dev = dev;
gc->of_node = dev->of_node;
- gc->request = cygnus_gpio_request;
- gc->free = cygnus_gpio_free;
- gc->direction_input = cygnus_gpio_direction_input;
- gc->direction_output = cygnus_gpio_direction_output;
- gc->set = cygnus_gpio_set;
- gc->get = cygnus_gpio_get;
+ gc->request = iproc_gpio_request;
+ gc->free = iproc_gpio_free;
+ gc->direction_input = iproc_gpio_direction_input;
+ gc->direction_output = iproc_gpio_direction_output;
+ gc->set = iproc_gpio_set;
+ gc->get = iproc_gpio_get;
chip->pinmux_is_supported = of_property_read_bool(dev->of_node,
"gpio-ranges");
@@ -738,7 +712,7 @@ static int cygnus_gpio_probe(struct platform_device *pdev)
return ret;
}
- ret = cygnus_gpio_register_pinconf(chip);
+ ret = iproc_gpio_register_pinconf(chip);
if (ret) {
dev_err(dev, "unable to register pinconf\n");
goto err_rm_gpiochip;
@@ -747,21 +721,21 @@ static int cygnus_gpio_probe(struct platform_device *pdev)
/* optional GPIO interrupt support */
irq = platform_get_irq(pdev, 0);
if (irq) {
- ret = gpiochip_irqchip_add(gc, &cygnus_gpio_irq_chip, 0,
+ ret = gpiochip_irqchip_add(gc, &iproc_gpio_irq_chip, 0,
handle_simple_irq, IRQ_TYPE_NONE);
if (ret) {
dev_err(dev, "no GPIO irqchip\n");
goto err_unregister_pinconf;
}
- gpiochip_set_chained_irqchip(gc, &cygnus_gpio_irq_chip, irq,
- cygnus_gpio_irq_handler);
+ gpiochip_set_chained_irqchip(gc, &iproc_gpio_irq_chip, irq,
+ iproc_gpio_irq_handler);
}
return 0;
err_unregister_pinconf:
- cygnus_gpio_unregister_pinconf(chip);
+ iproc_gpio_unregister_pinconf(chip);
err_rm_gpiochip:
gpiochip_remove(gc);
@@ -769,16 +743,16 @@ err_rm_gpiochip:
return ret;
}
-static struct platform_driver cygnus_gpio_driver = {
+static struct platform_driver iproc_gpio_driver = {
.driver = {
- .name = "cygnus-gpio",
- .of_match_table = cygnus_gpio_of_match,
+ .name = "iproc-gpio",
+ .of_match_table = iproc_gpio_of_match,
},
- .probe = cygnus_gpio_probe,
+ .probe = iproc_gpio_probe,
};
-static int __init cygnus_gpio_init(void)
+static int __init iproc_gpio_init(void)
{
- return platform_driver_probe(&cygnus_gpio_driver, cygnus_gpio_probe);
+ return platform_driver_probe(&iproc_gpio_driver, iproc_gpio_probe);
}
-arch_initcall_sync(cygnus_gpio_init);
+arch_initcall_sync(iproc_gpio_init);
diff --git a/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c b/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c
new file mode 100644
index 000000000000..725c36f917f9
--- /dev/null
+++ b/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c
@@ -0,0 +1,749 @@
+/*
+ * 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.
+ *
+ * This file contains the Broadcom Northstar Plus (NSP) GPIO driver that
+ * supports the chipCommonA GPIO controller. Basic PINCONF such as bias,
+ * pull up/down, slew and drive strength are also supported in this driver.
+ *
+ * Pins from the chipCommonA GPIO can be individually muxed to GPIO function,
+ * through the interaction with the NSP IOMUX controller.
+ */
+
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/slab.h>
+
+#include "../pinctrl-utils.h"
+
+#define NSP_CHIP_A_INT_STATUS 0x00
+#define NSP_CHIP_A_INT_MASK 0x04
+#define NSP_GPIO_DATA_IN 0x40
+#define NSP_GPIO_DATA_OUT 0x44
+#define NSP_GPIO_OUT_EN 0x48
+#define NSP_GPIO_INT_POLARITY 0x50
+#define NSP_GPIO_INT_MASK 0x54
+#define NSP_GPIO_EVENT 0x58
+#define NSP_GPIO_EVENT_INT_MASK 0x5c
+#define NSP_GPIO_EVENT_INT_POLARITY 0x64
+#define NSP_CHIP_A_GPIO_INT_BIT 0x01
+
+/* I/O parameters offset for chipcommon A GPIO */
+#define NSP_GPIO_DRV_CTRL 0x00
+#define NSP_GPIO_HYSTERESIS_EN 0x10
+#define NSP_GPIO_SLEW_RATE_EN 0x14
+#define NSP_PULL_UP_EN 0x18
+#define NSP_PULL_DOWN_EN 0x1c
+#define GPIO_DRV_STRENGTH_BITS 0x03
+
+/*
+ * nsp GPIO core
+ *
+ * @dev: pointer to device
+ * @base: I/O register base for nsp GPIO controller
+ * @io_ctrl: I/O register base for PINCONF support outside the GPIO block
+ * @gc: GPIO chip
+ * @pctl: pointer to pinctrl_dev
+ * @pctldesc: pinctrl descriptor
+ * @irq_domain: pointer to irq domain
+ * @lock: lock to protect access to I/O registers
+ */
+struct nsp_gpio {
+ struct device *dev;
+ void __iomem *base;
+ void __iomem *io_ctrl;
+ struct gpio_chip gc;
+ struct pinctrl_dev *pctl;
+ struct pinctrl_desc pctldesc;
+ struct irq_domain *irq_domain;
+ spinlock_t lock;
+};
+
+enum base_type {
+ REG,
+ IO_CTRL
+};
+
+static inline struct nsp_gpio *to_nsp_gpio(struct gpio_chip *gc)
+{
+ return container_of(gc, struct nsp_gpio, gc);
+}
+
+/*
+ * Mapping from PINCONF pins to GPIO pins is 1-to-1
+ */
+static inline unsigned nsp_pin_to_gpio(unsigned pin)
+{
+ return pin;
+}
+
+/*
+ * nsp_set_bit - set or clear one bit (corresponding to the GPIO pin) in a
+ * nsp GPIO register
+ *
+ * @nsp_gpio: nsp GPIO device
+ * @base_type: reg base to modify
+ * @reg: register offset
+ * @gpio: GPIO pin
+ * @set: set or clear
+ */
+static inline void nsp_set_bit(struct nsp_gpio *chip, enum base_type address,
+ unsigned int reg, unsigned gpio, bool set)
+{
+ u32 val;
+ void __iomem *base_address;
+
+ if (address == IO_CTRL)
+ base_address = chip->io_ctrl;
+ else
+ base_address = chip->base;
+
+ val = readl(base_address + reg);
+ if (set)
+ val |= BIT(gpio);
+ else
+ val &= ~BIT(gpio);
+
+ writel(val, base_address + reg);
+}
+
+/*
+ * nsp_get_bit - get one bit (corresponding to the GPIO pin) in a
+ * nsp GPIO register
+ */
+static inline bool nsp_get_bit(struct nsp_gpio *chip, enum base_type address,
+ unsigned int reg, unsigned gpio)
+{
+ if (address == IO_CTRL)
+ return !!(readl(chip->io_ctrl + reg) & BIT(gpio));
+ else
+ return !!(readl(chip->base + reg) & BIT(gpio));
+}
+
+static irqreturn_t nsp_gpio_irq_handler(int irq, void *data)
+{
+ struct nsp_gpio *chip = (struct nsp_gpio *)data;
+ struct gpio_chip gc = chip->gc;
+ int bit;
+ unsigned long int_bits = 0;
+ u32 int_status;
+
+ /* go through the entire GPIOs and handle all interrupts */
+ int_status = readl(chip->base + NSP_CHIP_A_INT_STATUS);
+ if (int_status & NSP_CHIP_A_GPIO_INT_BIT) {
+ unsigned int event, level;
+
+ /* Get level and edge interrupts */
+ event = readl(chip->base + NSP_GPIO_EVENT_INT_MASK) &
+ readl(chip->base + NSP_GPIO_EVENT);
+ level = readl(chip->base + NSP_GPIO_DATA_IN) ^
+ readl(chip->base + NSP_GPIO_INT_POLARITY);
+ level &= readl(chip->base + NSP_GPIO_INT_MASK);
+ int_bits = level | event;
+
+ for_each_set_bit(bit, &int_bits, gc.ngpio) {
+ /*
+ * Clear the interrupt before invoking the
+ * handler, so we do not leave any window
+ */
+ writel(BIT(bit), chip->base + NSP_GPIO_EVENT);
+ generic_handle_irq(
+ irq_linear_revmap(chip->irq_domain, bit));
+ }
+ }
+
+ return int_bits ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static void nsp_gpio_irq_ack(struct irq_data *d)
+{
+ struct nsp_gpio *chip = irq_data_get_irq_chip_data(d);
+ unsigned gpio = d->hwirq;
+ u32 val = BIT(gpio);
+ u32 trigger_type;
+
+ trigger_type = irq_get_trigger_type(d->irq);
+ if (trigger_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+ nsp_set_bit(chip, REG, NSP_GPIO_EVENT, gpio, val);
+}
+
+/*
+ * nsp_gpio_irq_set_mask - mask/unmask a GPIO interrupt
+ *
+ * @d: IRQ chip data
+ * @unmask: mask/unmask GPIO interrupt
+ */
+static void nsp_gpio_irq_set_mask(struct irq_data *d, bool unmask)
+{
+ struct nsp_gpio *chip = irq_data_get_irq_chip_data(d);
+ unsigned gpio = d->hwirq;
+ u32 trigger_type;
+
+ trigger_type = irq_get_trigger_type(d->irq);
+ if (trigger_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+ nsp_set_bit(chip, REG, NSP_GPIO_EVENT_INT_MASK, gpio, unmask);
+ else
+ nsp_set_bit(chip, REG, NSP_GPIO_INT_MASK, gpio, unmask);
+}
+
+static void nsp_gpio_irq_mask(struct irq_data *d)
+{
+ struct nsp_gpio *chip = irq_data_get_irq_chip_data(d);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ nsp_gpio_irq_set_mask(d, false);
+ spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static void nsp_gpio_irq_unmask(struct irq_data *d)
+{
+ struct nsp_gpio *chip = irq_data_get_irq_chip_data(d);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ nsp_gpio_irq_set_mask(d, true);
+ spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int nsp_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+ struct nsp_gpio *chip = irq_data_get_irq_chip_data(d);
+ unsigned gpio = d->hwirq;
+ bool level_low;
+ bool falling;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ falling = nsp_get_bit(chip, REG, NSP_GPIO_EVENT_INT_POLARITY, gpio);
+ level_low = nsp_get_bit(chip, REG, NSP_GPIO_INT_POLARITY, gpio);
+
+ switch (type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_RISING:
+ falling = false;
+ break;
+
+ case IRQ_TYPE_EDGE_FALLING:
+ falling = true;
+ break;
+
+ case IRQ_TYPE_LEVEL_HIGH:
+ level_low = false;
+ break;
+
+ case IRQ_TYPE_LEVEL_LOW:
+ level_low = true;
+ break;
+
+ default:
+ dev_err(chip->dev, "invalid GPIO IRQ type 0x%x\n",
+ type);
+ spin_unlock_irqrestore(&chip->lock, flags);
+ return -EINVAL;
+ }
+
+ nsp_set_bit(chip, REG, NSP_GPIO_EVENT_INT_POLARITY, gpio, falling);
+ nsp_set_bit(chip, REG, NSP_GPIO_INT_POLARITY, gpio, level_low);
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ dev_dbg(chip->dev, "gpio:%u level_low:%s falling:%s\n", gpio,
+ level_low ? "true" : "false", falling ? "true" : "false");
+ return 0;
+}
+
+static struct irq_chip nsp_gpio_irq_chip = {
+ .name = "gpio-a",
+ .irq_enable = nsp_gpio_irq_unmask,
+ .irq_disable = nsp_gpio_irq_mask,
+ .irq_ack = nsp_gpio_irq_ack,
+ .irq_mask = nsp_gpio_irq_mask,
+ .irq_unmask = nsp_gpio_irq_unmask,
+ .irq_set_type = nsp_gpio_irq_set_type,
+};
+
+/*
+ * Request the nsp IOMUX pinmux controller to mux individual pins to GPIO
+ */
+static int nsp_gpio_request(struct gpio_chip *gc, unsigned offset)
+{
+ unsigned gpio = gc->base + offset;
+
+ return pinctrl_request_gpio(gpio);
+}
+
+static void nsp_gpio_free(struct gpio_chip *gc, unsigned offset)
+{
+ unsigned gpio = gc->base + offset;
+
+ pinctrl_free_gpio(gpio);
+}
+
+static int nsp_gpio_direction_input(struct gpio_chip *gc, unsigned gpio)
+{
+ struct nsp_gpio *chip = to_nsp_gpio(gc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ nsp_set_bit(chip, REG, NSP_GPIO_OUT_EN, gpio, false);
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ dev_dbg(chip->dev, "gpio:%u set input\n", gpio);
+ return 0;
+}
+
+static int nsp_gpio_direction_output(struct gpio_chip *gc, unsigned gpio,
+ int val)
+{
+ struct nsp_gpio *chip = to_nsp_gpio(gc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ nsp_set_bit(chip, REG, NSP_GPIO_OUT_EN, gpio, true);
+ nsp_set_bit(chip, REG, NSP_GPIO_DATA_OUT, gpio, !!(val));
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ dev_dbg(chip->dev, "gpio:%u set output, value:%d\n", gpio, val);
+ return 0;
+}
+
+static void nsp_gpio_set(struct gpio_chip *gc, unsigned gpio, int val)
+{
+ struct nsp_gpio *chip = to_nsp_gpio(gc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ nsp_set_bit(chip, REG, NSP_GPIO_DATA_OUT, gpio, !!(val));
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ dev_dbg(chip->dev, "gpio:%u set, value:%d\n", gpio, val);
+}
+
+static int nsp_gpio_get(struct gpio_chip *gc, unsigned gpio)
+{
+ struct nsp_gpio *chip = to_nsp_gpio(gc);
+
+ return !!(readl(chip->base + NSP_GPIO_DATA_IN) & BIT(gpio));
+}
+
+static int nsp_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+ struct nsp_gpio *chip = to_nsp_gpio(gc);
+
+ return irq_linear_revmap(chip->irq_domain, offset);
+}
+
+static int nsp_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ return 1;
+}
+
+/*
+ * Only one group: "gpio_grp", since this local pinctrl device only performs
+ * GPIO specific PINCONF configurations
+ */
+static const char *nsp_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned selector)
+{
+ return "gpio_grp";
+}
+
+static const struct pinctrl_ops nsp_pctrl_ops = {
+ .get_groups_count = nsp_get_groups_count,
+ .get_group_name = nsp_get_group_name,
+ .dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
+ .dt_free_map = pinctrl_utils_dt_free_map,
+};
+
+static int nsp_gpio_set_slew(struct nsp_gpio *chip, unsigned gpio, u16 slew)
+{
+ if (slew)
+ nsp_set_bit(chip, IO_CTRL, NSP_GPIO_SLEW_RATE_EN, gpio, true);
+ else
+ nsp_set_bit(chip, IO_CTRL, NSP_GPIO_SLEW_RATE_EN, gpio, false);
+
+ return 0;
+}
+
+static int nsp_gpio_set_pull(struct nsp_gpio *chip, unsigned gpio,
+ bool pull_up, bool pull_down)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ nsp_set_bit(chip, IO_CTRL, NSP_PULL_DOWN_EN, gpio, pull_down);
+ nsp_set_bit(chip, IO_CTRL, NSP_PULL_UP_EN, gpio, pull_up);
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ dev_dbg(chip->dev, "gpio:%u set pullup:%d pulldown: %d\n",
+ gpio, pull_up, pull_down);
+ return 0;
+}
+
+static void nsp_gpio_get_pull(struct nsp_gpio *chip, unsigned gpio,
+ bool *pull_up, bool *pull_down)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ *pull_up = nsp_get_bit(chip, IO_CTRL, NSP_PULL_UP_EN, gpio);
+ *pull_down = nsp_get_bit(chip, IO_CTRL, NSP_PULL_DOWN_EN, gpio);
+ spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int nsp_gpio_set_strength(struct nsp_gpio *chip, unsigned gpio,
+ u16 strength)
+{
+ u32 offset, shift, i;
+ u32 val;
+ unsigned long flags;
+
+ /* make sure drive strength is supported */
+ if (strength < 2 || strength > 16 || (strength % 2))
+ return -ENOTSUPP;
+
+ shift = gpio;
+ offset = NSP_GPIO_DRV_CTRL;
+ dev_dbg(chip->dev, "gpio:%u set drive strength:%d mA\n", gpio,
+ strength);
+ spin_lock_irqsave(&chip->lock, flags);
+ strength = (strength / 2) - 1;
+ for (i = GPIO_DRV_STRENGTH_BITS; i > 0; i--) {
+ val = readl(chip->io_ctrl + offset);
+ val &= ~BIT(shift);
+ val |= ((strength >> (i-1)) & 0x1) << shift;
+ writel(val, chip->io_ctrl + offset);
+ offset += 4;
+ }
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+static int nsp_gpio_get_strength(struct nsp_gpio *chip, unsigned gpio,
+ u16 *strength)
+{
+ unsigned int offset, shift;
+ u32 val;
+ unsigned long flags;
+ int i;
+
+ offset = NSP_GPIO_DRV_CTRL;
+ shift = gpio;
+
+ spin_lock_irqsave(&chip->lock, flags);
+ *strength = 0;
+ for (i = (GPIO_DRV_STRENGTH_BITS - 1); i >= 0; i--) {
+ val = readl(chip->io_ctrl + offset) & BIT(shift);
+ val >>= shift;
+ *strength += (val << i);
+ offset += 4;
+ }
+
+ /* convert to mA */
+ *strength = (*strength + 1) * 2;
+ spin_unlock_irqrestore(&chip->lock, flags);
+
+ return 0;
+}
+
+int nsp_pin_config_group_get(struct pinctrl_dev *pctldev, unsigned selector,
+ unsigned long *config)
+{
+ return 0;
+}
+
+int nsp_pin_config_group_set(struct pinctrl_dev *pctldev, unsigned selector,
+ unsigned long *configs, unsigned num_configs)
+{
+ return 0;
+}
+
+static int nsp_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin,
+ unsigned long *config)
+{
+ struct nsp_gpio *chip = pinctrl_dev_get_drvdata(pctldev);
+ enum pin_config_param param = pinconf_to_config_param(*config);
+ unsigned int gpio;
+ u16 arg = 0;
+ bool pull_up, pull_down;
+ int ret;
+
+ gpio = nsp_pin_to_gpio(pin);
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ nsp_gpio_get_pull(chip, gpio, &pull_up, &pull_down);
+ if ((pull_up == false) && (pull_down == false))
+ return 0;
+ else
+ return -EINVAL;
+
+ case PIN_CONFIG_BIAS_PULL_UP:
+ nsp_gpio_get_pull(chip, gpio, &pull_up, &pull_down);
+ if (pull_up)
+ return 0;
+ else
+ return -EINVAL;
+
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ nsp_gpio_get_pull(chip, gpio, &pull_up, &pull_down);
+ if (pull_down)
+ return 0;
+ else
+ return -EINVAL;
+
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ ret = nsp_gpio_get_strength(chip, gpio, &arg);
+ if (ret)
+ return ret;
+ *config = pinconf_to_config_packed(param, arg);
+ return 0;
+
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static int nsp_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin,
+ unsigned long *configs, unsigned num_configs)
+{
+ struct nsp_gpio *chip = pinctrl_dev_get_drvdata(pctldev);
+ enum pin_config_param param;
+ u16 arg;
+ unsigned int i, gpio;
+ int ret = -ENOTSUPP;
+
+ gpio = nsp_pin_to_gpio(pin);
+ for (i = 0; i < num_configs; i++) {
+ param = pinconf_to_config_param(configs[i]);
+ arg = pinconf_to_config_argument(configs[i]);
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ ret = nsp_gpio_set_pull(chip, gpio, false, false);
+ if (ret < 0)
+ goto out;
+ break;
+
+ case PIN_CONFIG_BIAS_PULL_UP:
+ ret = nsp_gpio_set_pull(chip, gpio, true, false);
+ if (ret < 0)
+ goto out;
+ break;
+
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ ret = nsp_gpio_set_pull(chip, gpio, false, true);
+ if (ret < 0)
+ goto out;
+ break;
+
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ ret = nsp_gpio_set_strength(chip, gpio, arg);
+ if (ret < 0)
+ goto out;
+ break;
+
+ case PIN_CONFIG_SLEW_RATE:
+ ret = nsp_gpio_set_slew(chip, gpio, arg);
+ if (ret < 0)
+ goto out;
+ break;
+
+ default:
+ dev_err(chip->dev, "invalid configuration\n");
+ return -ENOTSUPP;
+ }
+ }
+
+out:
+ return ret;
+}
+
+static const struct pinconf_ops nsp_pconf_ops = {
+ .is_generic = true,
+ .pin_config_get = nsp_pin_config_get,
+ .pin_config_set = nsp_pin_config_set,
+ .pin_config_group_get = nsp_pin_config_group_get,
+ .pin_config_group_set = nsp_pin_config_group_set,
+};
+
+/*
+ * NSP GPIO controller supports some PINCONF related configurations such as
+ * pull up, pull down, slew and drive strength, when the pin is configured
+ * to GPIO.
+ *
+ * Here a local pinctrl device is created with simple 1-to-1 pin mapping to the
+ * local GPIO pins
+ */
+static int nsp_gpio_register_pinconf(struct nsp_gpio *chip)
+{
+ struct pinctrl_desc *pctldesc = &chip->pctldesc;
+ struct pinctrl_pin_desc *pins;
+ struct gpio_chip *gc = &chip->gc;
+ int i;
+
+ pins = devm_kcalloc(chip->dev, gc->ngpio, sizeof(*pins), GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+ for (i = 0; i < gc->ngpio; i++) {
+ pins[i].number = i;
+ pins[i].name = devm_kasprintf(chip->dev, GFP_KERNEL,
+ "gpio-%d", i);
+ if (!pins[i].name)
+ return -ENOMEM;
+ }
+ pctldesc->name = dev_name(chip->dev);
+ pctldesc->pctlops = &nsp_pctrl_ops;
+ pctldesc->pins = pins;
+ pctldesc->npins = gc->ngpio;
+ pctldesc->confops = &nsp_pconf_ops;
+
+ chip->pctl = pinctrl_register(pctldesc, chip->dev, chip);
+ if (IS_ERR(chip->pctl)) {
+ dev_err(chip->dev, "unable to register pinctrl device\n");
+ return PTR_ERR(chip->pctl);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id nsp_gpio_of_match[] = {
+ {.compatible = "brcm,nsp-gpio-a",},
+ {}
+};
+
+static int nsp_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct nsp_gpio *chip;
+ struct gpio_chip *gc;
+ u32 val, count;
+ int irq, ret;
+
+ if (of_property_read_u32(pdev->dev.of_node, "ngpios", &val)) {
+ dev_err(&pdev->dev, "Missing ngpios OF property\n");
+ return -ENODEV;
+ }
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->dev = dev;
+ platform_set_drvdata(pdev, chip);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ chip->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(chip->base)) {
+ dev_err(dev, "unable to map I/O memory\n");
+ return PTR_ERR(chip->base);
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ chip->io_ctrl = devm_ioremap_resource(dev, res);
+ if (IS_ERR(chip->io_ctrl)) {
+ dev_err(dev, "unable to map I/O memory\n");
+ return PTR_ERR(chip->io_ctrl);
+ }
+
+ spin_lock_init(&chip->lock);
+ gc = &chip->gc;
+ gc->base = -1;
+ gc->can_sleep = false;
+ gc->ngpio = val;
+ gc->label = dev_name(dev);
+ gc->dev = dev;
+ gc->of_node = dev->of_node;
+ gc->request = nsp_gpio_request;
+ gc->free = nsp_gpio_free;
+ gc->direction_input = nsp_gpio_direction_input;
+ gc->direction_output = nsp_gpio_direction_output;
+ gc->set = nsp_gpio_set;
+ gc->get = nsp_gpio_get;
+ gc->to_irq = nsp_gpio_to_irq;
+
+ /* optional GPIO interrupt support */
+ irq = platform_get_irq(pdev, 0);
+ if (irq > 0) {
+ /* Create irq domain so that each pin can be assigned an IRQ.*/
+ chip->irq_domain = irq_domain_add_linear(gc->of_node, gc->ngpio,
+ &irq_domain_simple_ops,
+ chip);
+ if (!chip->irq_domain) {
+ dev_err(&pdev->dev, "Couldn't allocate IRQ domain\n");
+ return -ENXIO;
+ }
+
+ /* Map each gpio to an IRQ and set the handler for gpiolib. */
+ for (count = 0; count < gc->ngpio; count++) {
+ int irq = irq_create_mapping(chip->irq_domain, count);
+
+ irq_set_chip_and_handler(irq, &nsp_gpio_irq_chip,
+ handle_simple_irq);
+ irq_set_chip_data(irq, chip);
+ }
+
+ /* Install ISR for this GPIO controller. */
+ ret = devm_request_irq(&pdev->dev, irq, nsp_gpio_irq_handler,
+ IRQF_SHARED, "gpio-a", chip);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to request IRQ%d: %d\n",
+ irq, ret);
+ goto err_rm_gpiochip;
+ }
+
+ val = readl(chip->base + NSP_CHIP_A_INT_MASK);
+ val = val | NSP_CHIP_A_GPIO_INT_BIT;
+ writel(val, (chip->base + NSP_CHIP_A_INT_MASK));
+ }
+
+ ret = gpiochip_add(gc);
+ if (ret < 0) {
+ dev_err(dev, "unable to add GPIO chip\n");
+ return ret;
+ }
+
+ ret = nsp_gpio_register_pinconf(chip);
+ if (ret) {
+ dev_err(dev, "unable to register pinconf\n");
+ goto err_rm_gpiochip;
+ }
+
+ return 0;
+
+err_rm_gpiochip:
+ gpiochip_remove(gc);
+
+ return ret;
+}
+
+static struct platform_driver nsp_gpio_driver = {
+ .driver = {
+ .name = "nsp-gpio-a",
+ .of_match_table = nsp_gpio_of_match,
+ },
+ .probe = nsp_gpio_probe,
+};
+
+static int __init nsp_gpio_init(void)
+{
+ return platform_driver_probe(&nsp_gpio_driver, nsp_gpio_probe);
+}
+arch_initcall_sync(nsp_gpio_init);
diff --git a/drivers/pinctrl/berlin/Makefile b/drivers/pinctrl/berlin/Makefile
index 06f94029ad66..6f641ce2c830 100644
--- a/drivers/pinctrl/berlin/Makefile
+++ b/drivers/pinctrl/berlin/Makefile
@@ -1,4 +1,4 @@
-obj-$(CONFIG_PINCTRL_BERLIN) += berlin.o
+obj-y += berlin.o
obj-$(CONFIG_PINCTRL_BERLIN_BG2) += berlin-bg2.o
obj-$(CONFIG_PINCTRL_BERLIN_BG2CD) += berlin-bg2cd.o
obj-$(CONFIG_PINCTRL_BERLIN_BG2Q) += berlin-bg2q.o
diff --git a/drivers/pinctrl/freescale/pinctrl-imx1-core.c b/drivers/pinctrl/freescale/pinctrl-imx1-core.c
index 88a7fac11bd4..acaf84cadca3 100644
--- a/drivers/pinctrl/freescale/pinctrl-imx1-core.c
+++ b/drivers/pinctrl/freescale/pinctrl-imx1-core.c
@@ -538,8 +538,10 @@ static int imx1_pinctrl_parse_functions(struct device_node *np,
func->groups[i] = child->name;
grp = &info->groups[grp_index++];
ret = imx1_pinctrl_parse_groups(child, grp, info, i++);
- if (ret == -ENOMEM)
+ if (ret == -ENOMEM) {
+ of_node_put(child);
return ret;
+ }
}
return 0;
@@ -582,8 +584,10 @@ static int imx1_pinctrl_parse_dt(struct platform_device *pdev,
for_each_child_of_node(np, child) {
ret = imx1_pinctrl_parse_functions(child, info, ifunc++);
- if (ret == -ENOMEM)
+ if (ret == -ENOMEM) {
+ of_node_put(child);
return -ENOMEM;
+ }
}
return 0;
diff --git a/drivers/pinctrl/freescale/pinctrl-vf610.c b/drivers/pinctrl/freescale/pinctrl-vf610.c
index 37a037543d29..587d1ff6210e 100644
--- a/drivers/pinctrl/freescale/pinctrl-vf610.c
+++ b/drivers/pinctrl/freescale/pinctrl-vf610.c
@@ -299,7 +299,7 @@ static const struct pinctrl_pin_desc vf610_pinctrl_pads[] = {
static struct imx_pinctrl_soc_info vf610_pinctrl_info = {
.pins = vf610_pinctrl_pads,
.npins = ARRAY_SIZE(vf610_pinctrl_pads),
- .flags = SHARE_MUX_CONF_REG,
+ .flags = SHARE_MUX_CONF_REG | ZERO_OFFSET_VALID,
};
static const struct of_device_id vf610_pinctrl_of_match[] = {
diff --git a/drivers/pinctrl/intel/pinctrl-broxton.c b/drivers/pinctrl/intel/pinctrl-broxton.c
index e42d5d4183f5..5979d38c46b2 100644
--- a/drivers/pinctrl/intel/pinctrl-broxton.c
+++ b/drivers/pinctrl/intel/pinctrl-broxton.c
@@ -28,6 +28,7 @@
.padcfglock_offset = BXT_PADCFGLOCK, \
.hostown_offset = BXT_HOSTSW_OWN, \
.ie_offset = BXT_GPI_IE, \
+ .gpp_size = 32, \
.pin_base = (s), \
.npins = ((e) - (s) + 1), \
}
diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c
index 392e28d3f48d..26f6b6ffea5b 100644
--- a/drivers/pinctrl/intel/pinctrl-intel.c
+++ b/drivers/pinctrl/intel/pinctrl-intel.c
@@ -25,9 +25,6 @@
#include "pinctrl-intel.h"
-/* Maximum number of pads in each group */
-#define NPADS_IN_GPP 24
-
/* Offset from regs */
#define PADBAR 0x00c
#define GPI_IS 0x100
@@ -37,6 +34,7 @@
#define PADOWN_BITS 4
#define PADOWN_SHIFT(p) ((p) % 8 * PADOWN_BITS)
#define PADOWN_MASK(p) (0xf << PADOWN_SHIFT(p))
+#define PADOWN_GPP(p) ((p) / 8)
/* Offset from pad_regs */
#define PADCFG0 0x000
@@ -142,7 +140,7 @@ static void __iomem *intel_get_padcfg(struct intel_pinctrl *pctrl, unsigned pin,
static bool intel_pad_owned_by_host(struct intel_pinctrl *pctrl, unsigned pin)
{
const struct intel_community *community;
- unsigned padno, gpp, gpp_offset, offset;
+ unsigned padno, gpp, offset, group;
void __iomem *padown;
community = intel_get_community(pctrl, pin);
@@ -152,9 +150,9 @@ static bool intel_pad_owned_by_host(struct intel_pinctrl *pctrl, unsigned pin)
return true;
padno = pin_to_padno(community, pin);
- gpp = padno / NPADS_IN_GPP;
- gpp_offset = padno % NPADS_IN_GPP;
- offset = community->padown_offset + gpp * 16 + (gpp_offset / 8) * 4;
+ group = padno / community->gpp_size;
+ gpp = PADOWN_GPP(padno % community->gpp_size);
+ offset = community->padown_offset + 0x10 * group + gpp * 4;
padown = community->regs + offset;
return !(readl(padown) & PADOWN_MASK(padno));
@@ -173,11 +171,11 @@ static bool intel_pad_acpi_mode(struct intel_pinctrl *pctrl, unsigned pin)
return false;
padno = pin_to_padno(community, pin);
- gpp = padno / NPADS_IN_GPP;
+ gpp = padno / community->gpp_size;
offset = community->hostown_offset + gpp * 4;
hostown = community->regs + offset;
- return !(readl(hostown) & BIT(padno % NPADS_IN_GPP));
+ return !(readl(hostown) & BIT(padno % community->gpp_size));
}
static bool intel_pad_locked(struct intel_pinctrl *pctrl, unsigned pin)
@@ -193,7 +191,7 @@ static bool intel_pad_locked(struct intel_pinctrl *pctrl, unsigned pin)
return false;
padno = pin_to_padno(community, pin);
- gpp = padno / NPADS_IN_GPP;
+ gpp = padno / community->gpp_size;
/*
* If PADCFGLOCK and PADCFGLOCKTX bits are both clear for this pad,
@@ -202,12 +200,12 @@ static bool intel_pad_locked(struct intel_pinctrl *pctrl, unsigned pin)
*/
offset = community->padcfglock_offset + gpp * 8;
value = readl(community->regs + offset);
- if (value & BIT(pin % NPADS_IN_GPP))
+ if (value & BIT(pin % community->gpp_size))
return true;
offset = community->padcfglock_offset + 4 + gpp * 8;
value = readl(community->regs + offset);
- if (value & BIT(pin % NPADS_IN_GPP))
+ if (value & BIT(pin % community->gpp_size))
return true;
return false;
@@ -663,8 +661,8 @@ static void intel_gpio_irq_ack(struct irq_data *d)
community = intel_get_community(pctrl, pin);
if (community) {
unsigned padno = pin_to_padno(community, pin);
- unsigned gpp_offset = padno % NPADS_IN_GPP;
- unsigned gpp = padno / NPADS_IN_GPP;
+ unsigned gpp_offset = padno % community->gpp_size;
+ unsigned gpp = padno / community->gpp_size;
writel(BIT(gpp_offset), community->regs + GPI_IS + gpp * 4);
}
@@ -685,8 +683,8 @@ static void intel_gpio_irq_mask_unmask(struct irq_data *d, bool mask)
community = intel_get_community(pctrl, pin);
if (community) {
unsigned padno = pin_to_padno(community, pin);
- unsigned gpp_offset = padno % NPADS_IN_GPP;
- unsigned gpp = padno / NPADS_IN_GPP;
+ unsigned gpp_offset = padno % community->gpp_size;
+ unsigned gpp = padno / community->gpp_size;
void __iomem *reg;
u32 value;
@@ -780,8 +778,8 @@ static int intel_gpio_irq_wake(struct irq_data *d, unsigned int on)
return -EINVAL;
padno = pin_to_padno(community, pin);
- gpp = padno / NPADS_IN_GPP;
- gpp_offset = padno % NPADS_IN_GPP;
+ gpp = padno / community->gpp_size;
+ gpp_offset = padno % community->gpp_size;
/* Clear the existing wake status */
writel(BIT(gpp_offset), community->regs + GPI_GPE_STS + gpp * 4);
@@ -819,14 +817,14 @@ static irqreturn_t intel_gpio_community_irq_handler(struct intel_pinctrl *pctrl,
/* Only interrupts that are enabled */
pending &= enabled;
- for_each_set_bit(gpp_offset, &pending, NPADS_IN_GPP) {
+ for_each_set_bit(gpp_offset, &pending, community->gpp_size) {
unsigned padno, irq;
/*
* The last group in community can have less pins
* than NPADS_IN_GPP.
*/
- padno = gpp_offset + gpp * NPADS_IN_GPP;
+ padno = gpp_offset + gpp * community->gpp_size;
if (padno >= community->npins)
break;
@@ -1002,7 +1000,8 @@ int intel_pinctrl_probe(struct platform_device *pdev,
community->regs = regs;
community->pad_regs = regs + padbar;
- community->ngpps = DIV_ROUND_UP(community->npins, NPADS_IN_GPP);
+ community->ngpps = DIV_ROUND_UP(community->npins,
+ community->gpp_size);
}
irq = platform_get_irq(pdev, 0);
diff --git a/drivers/pinctrl/intel/pinctrl-intel.h b/drivers/pinctrl/intel/pinctrl-intel.h
index 4ec8b572a288..b60215793017 100644
--- a/drivers/pinctrl/intel/pinctrl-intel.h
+++ b/drivers/pinctrl/intel/pinctrl-intel.h
@@ -55,6 +55,8 @@ struct intel_function {
* ACPI).
* @ie_offset: Register offset of GPI_IE from @regs.
* @pin_base: Starting pin of pins in this community
+ * @gpp_size: Maximum number of pads in each group, such as PADCFGLOCK,
+ * HOSTSW_OWN, GPI_IS, GPI_IE, etc.
* @npins: Number of pins in this community
* @regs: Community specific common registers (reserved for core driver)
* @pad_regs: Community specific pad registers (reserved for core driver)
@@ -68,6 +70,7 @@ struct intel_community {
unsigned hostown_offset;
unsigned ie_offset;
unsigned pin_base;
+ unsigned gpp_size;
size_t npins;
void __iomem *regs;
void __iomem *pad_regs;
diff --git a/drivers/pinctrl/intel/pinctrl-sunrisepoint.c b/drivers/pinctrl/intel/pinctrl-sunrisepoint.c
index 1de9ae5010db..c725a5313b4e 100644
--- a/drivers/pinctrl/intel/pinctrl-sunrisepoint.c
+++ b/drivers/pinctrl/intel/pinctrl-sunrisepoint.c
@@ -30,6 +30,7 @@
.padcfglock_offset = SPT_PADCFGLOCK, \
.hostown_offset = SPT_HOSTSW_OWN, \
.ie_offset = SPT_GPI_IE, \
+ .gpp_size = 24, \
.pin_base = (s), \
.npins = ((e) - (s) + 1), \
}
diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8127.c b/drivers/pinctrl/mediatek/pinctrl-mt8127.c
index b317b0b664ea..98e0bebfdf92 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mt8127.c
+++ b/drivers/pinctrl/mediatek/pinctrl-mt8127.c
@@ -351,7 +351,7 @@ static int __init mtk_pinctrl_init(void)
return platform_driver_register(&mtk_pinctrl_driver);
}
-module_init(mtk_pinctrl_init);
+arch_initcall(mtk_pinctrl_init);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MediaTek MT8127 Pinctrl Driver");
diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8135.c b/drivers/pinctrl/mediatek/pinctrl-mt8135.c
index 404f1178511d..1c153b860f36 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mt8135.c
+++ b/drivers/pinctrl/mediatek/pinctrl-mt8135.c
@@ -366,7 +366,7 @@ static int __init mtk_pinctrl_init(void)
return platform_driver_register(&mtk_pinctrl_driver);
}
-module_init(mtk_pinctrl_init);
+arch_initcall(mtk_pinctrl_init);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MediaTek Pinctrl Driver");
diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8173.c b/drivers/pinctrl/mediatek/pinctrl-mt8173.c
index ad271840d865..a62514eb2129 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mt8173.c
+++ b/drivers/pinctrl/mediatek/pinctrl-mt8173.c
@@ -394,7 +394,7 @@ static int __init mtk_pinctrl_init(void)
return platform_driver_register(&mtk_pinctrl_driver);
}
-module_init(mtk_pinctrl_init);
+arch_initcall(mtk_pinctrl_init);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MediaTek Pinctrl Driver");
diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
index f307f1d27d64..e22cbaf9f9cf 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
@@ -509,6 +509,9 @@ static int mtk_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
err = pinconf_generic_parse_dt_config(node, pctldev, &configs,
&num_configs);
+ if (err)
+ return err;
+
if (num_configs)
has_config = 1;
@@ -520,21 +523,23 @@ static int mtk_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
if (has_config && num_pins >= 1)
maps_per_pin++;
- if (!num_pins || !maps_per_pin)
- return -EINVAL;
+ if (!num_pins || !maps_per_pin) {
+ err = -EINVAL;
+ goto exit;
+ }
reserve = num_pins * maps_per_pin;
err = pinctrl_utils_reserve_map(pctldev, map,
reserved_maps, num_maps, reserve);
if (err < 0)
- goto fail;
+ goto exit;
for (i = 0; i < num_pins; i++) {
err = of_property_read_u32_index(node, "pinmux",
i, &pinfunc);
if (err)
- goto fail;
+ goto exit;
pin = MTK_GET_PIN_NO(pinfunc);
func = MTK_GET_PIN_FUNC(pinfunc);
@@ -543,20 +548,21 @@ static int mtk_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
func >= ARRAY_SIZE(mtk_gpio_functions)) {
dev_err(pctl->dev, "invalid pins value.\n");
err = -EINVAL;
- goto fail;
+ goto exit;
}
grp = mtk_pctrl_find_group_by_pin(pctl, pin);
if (!grp) {
dev_err(pctl->dev, "unable to match pin %d to group\n",
pin);
- return -EINVAL;
+ err = -EINVAL;
+ goto exit;
}
err = mtk_pctrl_dt_node_to_map_func(pctl, pin, func, grp, map,
reserved_maps, num_maps);
if (err < 0)
- goto fail;
+ goto exit;
if (has_config) {
err = pinctrl_utils_add_map_configs(pctldev, map,
@@ -564,13 +570,14 @@ static int mtk_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
configs, num_configs,
PIN_MAP_TYPE_CONFIGS_GROUP);
if (err < 0)
- goto fail;
+ goto exit;
}
}
- return 0;
+ err = 0;
-fail:
+exit:
+ kfree(configs);
return err;
}
@@ -591,6 +598,7 @@ static int mtk_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
&reserved_maps, num_maps);
if (ret < 0) {
pinctrl_utils_dt_free_map(pctldev, *map, *num_maps);
+ of_node_put(np);
return ret;
}
}
@@ -747,7 +755,7 @@ static int mtk_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
reg_addr = mtk_get_port(pctl, offset) + pctl->devdata->dir_offset;
bit = BIT(offset & 0xf);
regmap_read(pctl->regmap1, reg_addr, &read_val);
- return !!(read_val & bit);
+ return !(read_val & bit);
}
static int mtk_gpio_get(struct gpio_chip *chip, unsigned offset)
@@ -757,12 +765,8 @@ static int mtk_gpio_get(struct gpio_chip *chip, unsigned offset)
unsigned int read_val = 0;
struct mtk_pinctrl *pctl = dev_get_drvdata(chip->dev);
- if (mtk_gpio_get_direction(chip, offset))
- reg_addr = mtk_get_port(pctl, offset) +
- pctl->devdata->dout_offset;
- else
- reg_addr = mtk_get_port(pctl, offset) +
- pctl->devdata->din_offset;
+ reg_addr = mtk_get_port(pctl, offset) +
+ pctl->devdata->din_offset;
bit = BIT(offset & 0xf);
regmap_read(pctl->regmap1, reg_addr, &read_val);
@@ -997,6 +1001,7 @@ static struct gpio_chip mtk_gpio_chip = {
.owner = THIS_MODULE,
.request = gpiochip_generic_request,
.free = gpiochip_generic_free,
+ .get_direction = mtk_gpio_get_direction,
.direction_input = mtk_gpio_direction_input,
.direction_output = mtk_gpio_direction_output,
.get = mtk_gpio_get,
diff --git a/drivers/pinctrl/mvebu/Makefile b/drivers/pinctrl/mvebu/Makefile
index 554d8af14eeb..18270cd5ea43 100644
--- a/drivers/pinctrl/mvebu/Makefile
+++ b/drivers/pinctrl/mvebu/Makefile
@@ -1,4 +1,4 @@
-obj-$(CONFIG_PINCTRL_MVEBU) += pinctrl-mvebu.o
+obj-y += pinctrl-mvebu.o
obj-$(CONFIG_PINCTRL_DOVE) += pinctrl-dove.o
obj-$(CONFIG_PINCTRL_KIRKWOOD) += pinctrl-kirkwood.o
obj-$(CONFIG_PINCTRL_ARMADA_370) += pinctrl-armada-370.o
diff --git a/drivers/pinctrl/mvebu/pinctrl-mvebu.c b/drivers/pinctrl/mvebu/pinctrl-mvebu.c
index 77d2221d379d..e4d473811bb3 100644
--- a/drivers/pinctrl/mvebu/pinctrl-mvebu.c
+++ b/drivers/pinctrl/mvebu/pinctrl-mvebu.c
@@ -663,28 +663,20 @@ int mvebu_pinctrl_probe(struct platform_device *pdev)
/* assign mpp modes to groups */
for (n = 0; n < soc->nmodes; n++) {
struct mvebu_mpp_mode *mode = &soc->modes[n];
- struct mvebu_pinctrl_group *grp =
- mvebu_pinctrl_find_group_by_pid(pctl, mode->pid);
+ struct mvebu_mpp_ctrl_setting *set = &mode->settings[0];
+ struct mvebu_pinctrl_group *grp;
unsigned num_settings;
- if (!grp) {
- dev_warn(&pdev->dev, "unknown pinctrl group %d\n",
- mode->pid);
- continue;
- }
-
- for (num_settings = 0; ;) {
- struct mvebu_mpp_ctrl_setting *set =
- &mode->settings[num_settings];
-
+ for (num_settings = 0; ; set++) {
if (!set->name)
break;
- num_settings++;
/* skip unsupported settings for this variant */
if (pctl->variant && !(pctl->variant & set->variant))
continue;
+ num_settings++;
+
/* find gpio/gpo/gpi settings */
if (strcmp(set->name, "gpio") == 0)
set->flags = MVEBU_SETTING_GPI |
@@ -695,6 +687,17 @@ int mvebu_pinctrl_probe(struct platform_device *pdev)
set->flags = MVEBU_SETTING_GPI;
}
+ /* skip modes with no settings for this variant */
+ if (!num_settings)
+ continue;
+
+ grp = mvebu_pinctrl_find_group_by_pid(pctl, mode->pid);
+ if (!grp) {
+ dev_warn(&pdev->dev, "unknown pinctrl group %d\n",
+ mode->pid);
+ continue;
+ }
+
grp->settings = mode->settings;
grp->num_settings = num_settings;
}
diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c
index 099a3442ff42..79e6159712c2 100644
--- a/drivers/pinctrl/pinconf-generic.c
+++ b/drivers/pinctrl/pinconf-generic.c
@@ -220,6 +220,7 @@ static void parse_dt_cfg(struct device_node *np,
* parse the config properties into generic pinconfig values.
* @np: node containing the pinconfig properties
* @configs: array with nconfigs entries containing the generic pinconf values
+ * must be freed when no longer necessary.
* @nconfigs: umber of configurations
*/
int pinconf_generic_parse_dt_config(struct device_node *np,
diff --git a/drivers/pinctrl/pinctrl-adi2.c b/drivers/pinctrl/pinctrl-adi2.c
index fd342dffe4dc..8e9e8eab59ba 100644
--- a/drivers/pinctrl/pinctrl-adi2.c
+++ b/drivers/pinctrl/pinctrl-adi2.c
@@ -1102,32 +1102,24 @@ static struct platform_driver adi_gpio_driver = {
},
};
+static struct platform_driver * const drivers[] = {
+ &adi_pinctrl_driver,
+ &adi_gpio_pint_driver,
+ &adi_gpio_driver,
+};
+
static int __init adi_pinctrl_setup(void)
{
int ret;
- ret = platform_driver_register(&adi_pinctrl_driver);
+ ret = platform_register_drivers(drivers, ARRAY_SIZE(drivers));
if (ret)
return ret;
- ret = platform_driver_register(&adi_gpio_pint_driver);
- if (ret)
- goto pint_error;
-
- ret = platform_driver_register(&adi_gpio_driver);
- if (ret)
- goto gpio_error;
-
#ifdef CONFIG_PM
register_syscore_ops(&gpio_pm_syscore_ops);
#endif
- return ret;
-gpio_error:
- platform_driver_unregister(&adi_gpio_pint_driver);
-pint_error:
- platform_driver_unregister(&adi_pinctrl_driver);
-
- return ret;
+ return 0;
}
arch_initcall(adi_pinctrl_setup);
diff --git a/drivers/pinctrl/pinctrl-at91-pio4.c b/drivers/pinctrl/pinctrl-at91-pio4.c
index 33edd07d9149..d5bdcebc6aa6 100644
--- a/drivers/pinctrl/pinctrl-at91-pio4.c
+++ b/drivers/pinctrl/pinctrl-at91-pio4.c
@@ -500,7 +500,8 @@ static int atmel_pctl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
if (!num_pins) {
dev_err(pctldev->dev, "no pins found in node %s\n",
of_node_full_name(np));
- return -EINVAL;
+ ret = -EINVAL;
+ goto exit;
}
/*
@@ -514,19 +515,19 @@ static int atmel_pctl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
ret = pinctrl_utils_reserve_map(pctldev, map, reserved_maps, num_maps,
reserve);
if (ret < 0)
- return ret;
+ goto exit;
for (i = 0; i < num_pins; i++) {
const char *group, *func;
ret = of_property_read_u32_index(np, "pinmux", i, &pinfunc);
if (ret)
- return ret;
+ goto exit;
ret = atmel_pctl_xlate_pinfunc(pctldev, np, pinfunc, &group,
&func);
if (ret)
- return ret;
+ goto exit;
pinctrl_utils_add_map_mux(pctldev, map, reserved_maps, num_maps,
group, func);
@@ -537,11 +538,13 @@ static int atmel_pctl_dt_subnode_to_map(struct pinctrl_dev *pctldev,
configs, num_configs,
PIN_MAP_TYPE_CONFIGS_GROUP);
if (ret < 0)
- return ret;
+ goto exit;
}
}
- return 0;
+exit:
+ kfree(configs);
+ return ret;
}
static int atmel_pctl_dt_node_to_map(struct pinctrl_dev *pctldev,
@@ -1000,7 +1003,7 @@ static int atmel_pinctrl_probe(struct platform_device *pdev)
atmel_pioctrl->irqs[i] = res->start;
irq_set_chained_handler(res->start, atmel_gpio_irq_handler);
irq_set_handler_data(res->start, atmel_pioctrl);
- dev_dbg(dev, "bank %i: hwirq=%u\n", i, res->start);
+ dev_dbg(dev, "bank %i: irq=%pr\n", i, res);
}
atmel_pioctrl->irq_domain = irq_domain_add_linear(dev->of_node,
diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c
index 0d2fc0cff35e..47b625b1b789 100644
--- a/drivers/pinctrl/pinctrl-at91.c
+++ b/drivers/pinctrl/pinctrl-at91.c
@@ -1828,20 +1828,20 @@ static struct platform_driver at91_pinctrl_driver = {
.remove = at91_pinctrl_remove,
};
+static struct platform_driver * const drivers[] = {
+ &at91_gpio_driver,
+ &at91_pinctrl_driver,
+};
+
static int __init at91_pinctrl_init(void)
{
- int ret;
-
- ret = platform_driver_register(&at91_gpio_driver);
- if (ret)
- return ret;
- return platform_driver_register(&at91_pinctrl_driver);
+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
}
arch_initcall(at91_pinctrl_init);
static void __exit at91_pinctrl_exit(void)
{
- platform_driver_unregister(&at91_pinctrl_driver);
+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
}
module_exit(at91_pinctrl_exit);
diff --git a/drivers/pinctrl/pinctrl-lantiq.h b/drivers/pinctrl/pinctrl-lantiq.h
index eb89ba045228..e137d139e494 100644
--- a/drivers/pinctrl/pinctrl-lantiq.h
+++ b/drivers/pinctrl/pinctrl-lantiq.h
@@ -162,6 +162,14 @@ enum ltq_pin {
GPIO53,
GPIO54,
GPIO55,
+ GPIO56,
+ GPIO57,
+ GPIO58,
+ GPIO59,
+ GPIO60, /* 60 */
+ GPIO61,
+ GPIO62,
+ GPIO63,
GPIO64,
GPIO65,
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
index a0651128e23a..91288265e856 100644
--- a/drivers/pinctrl/pinctrl-rockchip.c
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -614,6 +614,40 @@ static void rk3288_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
}
}
+#define RK3228_PULL_OFFSET 0x100
+
+static void rk3228_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+
+ *regmap = info->regmap_base;
+ *reg = RK3228_PULL_OFFSET;
+ *reg += bank->bank_num * RK3188_PULL_BANK_STRIDE;
+ *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+
+ *bit = (pin_num % RK3188_PULL_PINS_PER_REG);
+ *bit *= RK3188_PULL_BITS_PER_PIN;
+}
+
+#define RK3228_DRV_GRF_OFFSET 0x200
+
+static void rk3228_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, struct regmap **regmap,
+ int *reg, u8 *bit)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+
+ *regmap = info->regmap_base;
+ *reg = RK3228_DRV_GRF_OFFSET;
+ *reg += bank->bank_num * RK3288_DRV_BANK_STRIDE;
+ *reg += ((pin_num / RK3288_DRV_PINS_PER_REG) * 4);
+
+ *bit = (pin_num % RK3288_DRV_PINS_PER_REG);
+ *bit *= RK3288_DRV_BITS_PER_PIN;
+}
+
#define RK3368_PULL_GRF_OFFSET 0x100
#define RK3368_PULL_PMU_OFFSET 0x10
@@ -1258,8 +1292,10 @@ static int rockchip_pinctrl_parse_functions(struct device_node *np,
func->groups[i] = child->name;
grp = &info->groups[grp_index++];
ret = rockchip_pinctrl_parse_groups(child, grp, info, i++);
- if (ret)
+ if (ret) {
+ of_node_put(child);
return ret;
+ }
}
return 0;
@@ -1304,6 +1340,7 @@ static int rockchip_pinctrl_parse_dt(struct platform_device *pdev,
ret = rockchip_pinctrl_parse_functions(child, info, i++);
if (ret) {
dev_err(&pdev->dev, "failed to parse function\n");
+ of_node_put(child);
return ret;
}
}
@@ -2143,6 +2180,23 @@ static struct rockchip_pin_ctrl rk3188_pin_ctrl = {
.pull_calc_reg = rk3188_calc_pull_reg_and_bit,
};
+static struct rockchip_pin_bank rk3228_pin_banks[] = {
+ PIN_BANK(0, 32, "gpio0"),
+ PIN_BANK(1, 32, "gpio1"),
+ PIN_BANK(2, 32, "gpio2"),
+ PIN_BANK(3, 32, "gpio3"),
+};
+
+static struct rockchip_pin_ctrl rk3228_pin_ctrl = {
+ .pin_banks = rk3228_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3228_pin_banks),
+ .label = "RK3228-GPIO",
+ .type = RK3288,
+ .grf_mux_offset = 0x0,
+ .pull_calc_reg = rk3228_calc_pull_reg_and_bit,
+ .drv_calc_reg = rk3228_calc_drv_reg_and_bit,
+};
+
static struct rockchip_pin_bank rk3288_pin_banks[] = {
PIN_BANK_IOMUX_FLAGS(0, 24, "gpio0", IOMUX_SOURCE_PMU,
IOMUX_SOURCE_PMU,
@@ -2220,6 +2274,8 @@ static const struct of_device_id rockchip_pinctrl_dt_match[] = {
.data = (void *)&rk3066b_pin_ctrl },
{ .compatible = "rockchip,rk3188-pinctrl",
.data = (void *)&rk3188_pin_ctrl },
+ { .compatible = "rockchip,rk3228-pinctrl",
+ .data = (void *)&rk3228_pin_ctrl },
{ .compatible = "rockchip,rk3288-pinctrl",
.data = (void *)&rk3288_pin_ctrl },
{ .compatible = "rockchip,rk3368-pinctrl",
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
index ef04b962c3d5..d24e5f1d1525 100644
--- a/drivers/pinctrl/pinctrl-single.c
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -1484,10 +1484,7 @@ static void pcs_irq_free(struct pcs_device *pcs)
static void pcs_free_resources(struct pcs_device *pcs)
{
pcs_irq_free(pcs);
-
- if (pcs->pctl)
- pinctrl_unregister(pcs->pctl);
-
+ pinctrl_unregister(pcs->pctl);
pcs_free_funcs(pcs);
pcs_free_pingroups(pcs);
}
diff --git a/drivers/pinctrl/pinctrl-tegra-xusb.c b/drivers/pinctrl/pinctrl-tegra-xusb.c
index 84a43e612952..bd3aa5a4fd6d 100644
--- a/drivers/pinctrl/pinctrl-tegra-xusb.c
+++ b/drivers/pinctrl/pinctrl-tegra-xusb.c
@@ -253,8 +253,10 @@ static int tegra_xusb_padctl_dt_node_to_map(struct pinctrl_dev *pinctrl,
err = tegra_xusb_padctl_parse_subnode(padctl, np, maps,
&reserved_maps,
num_maps);
- if (err < 0)
+ if (err < 0) {
+ of_node_put(np);
return err;
+ }
}
return 0;
diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c
index 0fd7fd2b0f72..9da4da219a07 100644
--- a/drivers/pinctrl/pinctrl-tegra.c
+++ b/drivers/pinctrl/pinctrl-tegra.c
@@ -217,6 +217,7 @@ static int tegra_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
if (ret < 0) {
pinctrl_utils_dt_free_map(pctldev, *map,
*num_maps);
+ of_node_put(np);
return ret;
}
}
diff --git a/drivers/pinctrl/pinctrl-xway.c b/drivers/pinctrl/pinctrl-xway.c
index ae724bdab3d3..7db74699fda4 100644
--- a/drivers/pinctrl/pinctrl-xway.c
+++ b/drivers/pinctrl/pinctrl-xway.c
@@ -7,6 +7,7 @@
* publishhed by the Free Software Foundation.
*
* Copyright (C) 2012 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2015 Martin Schiller <mschiller@tdt.de>
*/
#include <linux/err.h>
@@ -24,7 +25,7 @@
#include <lantiq_soc.h>
-/* we have 3 1/2 banks of 16 bit each */
+/* we have up to 4 banks of 16 bit each */
#define PINS 16
#define PORT3 3
#define PORT(x) (x / PINS)
@@ -35,7 +36,7 @@
#define MUX_ALT1 0x2
/*
- * each bank has this offset apart from the 1/2 bank that is mixed into the
+ * each bank has this offset apart from the 4th bank that is mixed into the
* other 3 ranges
*/
#define REG_OFF 0x30
@@ -51,7 +52,7 @@
#define GPIO_PUDSEL(p) (GPIO_BASE(p) + 0x1c)
#define GPIO_PUDEN(p) (GPIO_BASE(p) + 0x20)
-/* the 1/2 port needs special offsets for some registers */
+/* the 4th port needs special offsets for some registers */
#define GPIO3_OD (GPIO_BASE(0) + 0x24)
#define GPIO3_PUDSEL (GPIO_BASE(0) + 0x28)
#define GPIO3_PUDEN (GPIO_BASE(0) + 0x2C)
@@ -80,17 +81,18 @@
#define FUNC_MUX(f, m) \
{ .func = f, .mux = XWAY_MUX_##m, }
-#define XWAY_MAX_PIN 32
-#define XR9_MAX_PIN 56
-
enum xway_mux {
XWAY_MUX_GPIO = 0,
XWAY_MUX_SPI,
XWAY_MUX_ASC,
+ XWAY_MUX_USIF,
XWAY_MUX_PCI,
+ XWAY_MUX_CBUS,
XWAY_MUX_CGU,
XWAY_MUX_EBU,
+ XWAY_MUX_EBU2,
XWAY_MUX_JTAG,
+ XWAY_MUX_MCD,
XWAY_MUX_EXIN,
XWAY_MUX_TDM,
XWAY_MUX_STP,
@@ -103,9 +105,15 @@ enum xway_mux {
XWAY_MUX_DFE,
XWAY_MUX_SDIO,
XWAY_MUX_GPHY,
+ XWAY_MUX_SSI,
+ XWAY_MUX_WIFI,
XWAY_MUX_NONE = 0xffff,
};
+/* --------- DEPRECATED: xr9 related code --------- */
+/* ---------- use xrx100/xrx200 instead ---------- */
+#define XR9_MAX_PIN 56
+
static const struct ltq_mfp_pin xway_mfp[] = {
/* pin f0 f1 f2 f3 */
MFP_XWAY(GPIO0, GPIO, EXIN, NONE, TDM),
@@ -113,7 +121,7 @@ static const struct ltq_mfp_pin xway_mfp[] = {
MFP_XWAY(GPIO2, GPIO, CGU, EXIN, GPHY),
MFP_XWAY(GPIO3, GPIO, CGU, NONE, PCI),
MFP_XWAY(GPIO4, GPIO, STP, NONE, ASC),
- MFP_XWAY(GPIO5, GPIO, STP, NONE, GPHY),
+ MFP_XWAY(GPIO5, GPIO, STP, GPHY, NONE),
MFP_XWAY(GPIO6, GPIO, STP, GPT, ASC),
MFP_XWAY(GPIO7, GPIO, CGU, PCI, GPHY),
MFP_XWAY(GPIO8, GPIO, CGU, NMI, NONE),
@@ -152,10 +160,10 @@ static const struct ltq_mfp_pin xway_mfp[] = {
MFP_XWAY(GPIO41, GPIO, NONE, NONE, NONE),
MFP_XWAY(GPIO42, GPIO, MDIO, NONE, NONE),
MFP_XWAY(GPIO43, GPIO, MDIO, NONE, NONE),
- MFP_XWAY(GPIO44, GPIO, NONE, GPHY, SIN),
+ MFP_XWAY(GPIO44, GPIO, MII, SIN, GPHY),
MFP_XWAY(GPIO45, GPIO, NONE, GPHY, SIN),
MFP_XWAY(GPIO46, GPIO, NONE, NONE, EXIN),
- MFP_XWAY(GPIO47, GPIO, NONE, GPHY, SIN),
+ MFP_XWAY(GPIO47, GPIO, MII, GPHY, SIN),
MFP_XWAY(GPIO48, GPIO, EBU, NONE, NONE),
MFP_XWAY(GPIO49, GPIO, EBU, NONE, NONE),
MFP_XWAY(GPIO50, GPIO, NONE, NONE, NONE),
@@ -166,42 +174,6 @@ static const struct ltq_mfp_pin xway_mfp[] = {
MFP_XWAY(GPIO55, GPIO, NONE, NONE, NONE),
};
-static const struct ltq_mfp_pin ase_mfp[] = {
- /* pin f0 f1 f2 f3 */
- MFP_XWAY(GPIO0, GPIO, EXIN, MII, TDM),
- MFP_XWAY(GPIO1, GPIO, STP, DFE, EBU),
- MFP_XWAY(GPIO2, GPIO, STP, DFE, EPHY),
- MFP_XWAY(GPIO3, GPIO, STP, EPHY, EBU),
- MFP_XWAY(GPIO4, GPIO, GPT, EPHY, MII),
- MFP_XWAY(GPIO5, GPIO, MII, ASC, GPT),
- MFP_XWAY(GPIO6, GPIO, MII, ASC, EXIN),
- MFP_XWAY(GPIO7, GPIO, SPI, MII, JTAG),
- MFP_XWAY(GPIO8, GPIO, SPI, MII, JTAG),
- MFP_XWAY(GPIO9, GPIO, SPI, MII, JTAG),
- MFP_XWAY(GPIO10, GPIO, SPI, MII, JTAG),
- MFP_XWAY(GPIO11, GPIO, EBU, CGU, JTAG),
- MFP_XWAY(GPIO12, GPIO, EBU, MII, SDIO),
- MFP_XWAY(GPIO13, GPIO, EBU, MII, CGU),
- MFP_XWAY(GPIO14, GPIO, EBU, SPI, CGU),
- MFP_XWAY(GPIO15, GPIO, EBU, SPI, SDIO),
- MFP_XWAY(GPIO16, GPIO, NONE, NONE, NONE),
- MFP_XWAY(GPIO17, GPIO, NONE, NONE, NONE),
- MFP_XWAY(GPIO18, GPIO, NONE, NONE, NONE),
- MFP_XWAY(GPIO19, GPIO, EBU, MII, SDIO),
- MFP_XWAY(GPIO20, GPIO, EBU, MII, SDIO),
- MFP_XWAY(GPIO21, GPIO, EBU, MII, SDIO),
- MFP_XWAY(GPIO22, GPIO, EBU, MII, CGU),
- MFP_XWAY(GPIO23, GPIO, EBU, MII, CGU),
- MFP_XWAY(GPIO24, GPIO, EBU, NONE, MII),
- MFP_XWAY(GPIO25, GPIO, EBU, MII, GPT),
- MFP_XWAY(GPIO26, GPIO, EBU, MII, SDIO),
- MFP_XWAY(GPIO27, GPIO, EBU, NONE, MII),
- MFP_XWAY(GPIO28, GPIO, MII, EBU, SDIO),
- MFP_XWAY(GPIO29, GPIO, EBU, MII, EXIN),
- MFP_XWAY(GPIO30, GPIO, NONE, NONE, NONE),
- MFP_XWAY(GPIO31, GPIO, NONE, NONE, NONE),
-};
-
static const unsigned pins_jtag[] = {GPIO15, GPIO16, GPIO17, GPIO19, GPIO35};
static const unsigned pins_asc0[] = {GPIO11, GPIO12};
static const unsigned pins_asc0_cts_rts[] = {GPIO9, GPIO10};
@@ -231,6 +203,8 @@ static const unsigned pins_nand_cle[] = {GPIO24};
static const unsigned pins_nand_rdy[] = {GPIO48};
static const unsigned pins_nand_rd[] = {GPIO49};
+static const unsigned xway_exin_pin_map[] = {GPIO0, GPIO1, GPIO2, GPIO39, GPIO46, GPIO9};
+
static const unsigned pins_exin0[] = {GPIO0};
static const unsigned pins_exin1[] = {GPIO1};
static const unsigned pins_exin2[] = {GPIO2};
@@ -240,7 +214,7 @@ static const unsigned pins_exin5[] = {GPIO9};
static const unsigned pins_spi[] = {GPIO16, GPIO17, GPIO18};
static const unsigned pins_spi_cs1[] = {GPIO15};
-static const unsigned pins_spi_cs2[] = {GPIO21};
+static const unsigned pins_spi_cs2[] = {GPIO22};
static const unsigned pins_spi_cs3[] = {GPIO13};
static const unsigned pins_spi_cs4[] = {GPIO10};
static const unsigned pins_spi_cs5[] = {GPIO9};
@@ -264,25 +238,6 @@ static const unsigned pins_pci_req2[] = {GPIO31};
static const unsigned pins_pci_req3[] = {GPIO3};
static const unsigned pins_pci_req4[] = {GPIO37};
-static const unsigned ase_pins_jtag[] = {GPIO7, GPIO8, GPIO9, GPIO10, GPIO11};
-static const unsigned ase_pins_asc[] = {GPIO5, GPIO6};
-static const unsigned ase_pins_stp[] = {GPIO1, GPIO2, GPIO3};
-static const unsigned ase_pins_ephy[] = {GPIO2, GPIO3, GPIO4};
-static const unsigned ase_pins_dfe[] = {GPIO1, GPIO2};
-
-static const unsigned ase_pins_spi[] = {GPIO8, GPIO9, GPIO10};
-static const unsigned ase_pins_spi_cs1[] = {GPIO7};
-static const unsigned ase_pins_spi_cs2[] = {GPIO15};
-static const unsigned ase_pins_spi_cs3[] = {GPIO14};
-
-static const unsigned ase_pins_exin0[] = {GPIO6};
-static const unsigned ase_pins_exin1[] = {GPIO29};
-static const unsigned ase_pins_exin2[] = {GPIO0};
-
-static const unsigned ase_pins_gpt1[] = {GPIO5};
-static const unsigned ase_pins_gpt2[] = {GPIO4};
-static const unsigned ase_pins_gpt3[] = {GPIO25};
-
static const struct ltq_pin_group xway_grps[] = {
GRP_MUX("exin0", EXIN, pins_exin0),
GRP_MUX("exin1", EXIN, pins_exin1),
@@ -338,24 +293,6 @@ static const struct ltq_pin_group xway_grps[] = {
GRP_MUX("gphy1 led2", GPHY, pins_gphy1_led2),
};
-static const struct ltq_pin_group ase_grps[] = {
- GRP_MUX("exin0", EXIN, ase_pins_exin0),
- GRP_MUX("exin1", EXIN, ase_pins_exin1),
- GRP_MUX("exin2", EXIN, ase_pins_exin2),
- GRP_MUX("jtag", JTAG, ase_pins_jtag),
- GRP_MUX("stp", STP, ase_pins_stp),
- GRP_MUX("asc", ASC, ase_pins_asc),
- GRP_MUX("gpt1", GPT, ase_pins_gpt1),
- GRP_MUX("gpt2", GPT, ase_pins_gpt2),
- GRP_MUX("gpt3", GPT, ase_pins_gpt3),
- GRP_MUX("ephy", EPHY, ase_pins_ephy),
- GRP_MUX("dfe", DFE, ase_pins_dfe),
- GRP_MUX("spi", SPI, ase_pins_spi),
- GRP_MUX("spi_cs1", SPI, ase_pins_spi_cs1),
- GRP_MUX("spi_cs2", SPI, ase_pins_spi_cs2),
- GRP_MUX("spi_cs3", SPI, ase_pins_spi_cs3),
-};
-
static const char * const xway_pci_grps[] = {"gnt1", "gnt2",
"gnt3", "req1",
"req2", "req3"};
@@ -395,30 +332,6 @@ static const char * const xrx_pci_grps[] = {"gnt1", "gnt2",
"req1", "req2",
"req3", "req4"};
-/* ase */
-static const char * const ase_exin_grps[] = {"exin0", "exin1", "exin2"};
-static const char * const ase_gpt_grps[] = {"gpt1", "gpt2", "gpt3"};
-static const char * const ase_dfe_grps[] = {"dfe"};
-static const char * const ase_ephy_grps[] = {"ephy"};
-static const char * const ase_asc_grps[] = {"asc"};
-static const char * const ase_jtag_grps[] = {"jtag"};
-static const char * const ase_stp_grps[] = {"stp"};
-static const char * const ase_spi_grps[] = {"spi", "spi_cs1",
- "spi_cs2", "spi_cs3"};
-
-static const struct ltq_pmx_func danube_funcs[] = {
- {"spi", ARRAY_AND_SIZE(xway_spi_grps)},
- {"asc", ARRAY_AND_SIZE(xway_asc_grps)},
- {"cgu", ARRAY_AND_SIZE(xway_cgu_grps)},
- {"jtag", ARRAY_AND_SIZE(xway_jtag_grps)},
- {"exin", ARRAY_AND_SIZE(xway_exin_grps)},
- {"stp", ARRAY_AND_SIZE(xway_stp_grps)},
- {"gpt", ARRAY_AND_SIZE(xway_gpt_grps)},
- {"nmi", ARRAY_AND_SIZE(xway_nmi_grps)},
- {"pci", ARRAY_AND_SIZE(xway_pci_grps)},
- {"ebu", ARRAY_AND_SIZE(xway_ebu_grps)},
-};
-
static const struct ltq_pmx_func xrx_funcs[] = {
{"spi", ARRAY_AND_SIZE(xway_spi_grps)},
{"asc", ARRAY_AND_SIZE(xway_asc_grps)},
@@ -434,17 +347,991 @@ static const struct ltq_pmx_func xrx_funcs[] = {
{"gphy", ARRAY_AND_SIZE(xrx_gphy_grps)},
};
+/* --------- ase related code --------- */
+#define ASE_MAX_PIN 32
+
+static const struct ltq_mfp_pin ase_mfp[] = {
+ /* pin f0 f1 f2 f3 */
+ MFP_XWAY(GPIO0, GPIO, EXIN, MII, TDM),
+ MFP_XWAY(GPIO1, GPIO, STP, DFE, EBU),
+ MFP_XWAY(GPIO2, GPIO, STP, DFE, EPHY),
+ MFP_XWAY(GPIO3, GPIO, STP, EPHY, EBU),
+ MFP_XWAY(GPIO4, GPIO, GPT, EPHY, MII),
+ MFP_XWAY(GPIO5, GPIO, MII, ASC, GPT),
+ MFP_XWAY(GPIO6, GPIO, MII, ASC, EXIN),
+ MFP_XWAY(GPIO7, GPIO, SPI, MII, JTAG),
+ MFP_XWAY(GPIO8, GPIO, SPI, MII, JTAG),
+ MFP_XWAY(GPIO9, GPIO, SPI, MII, JTAG),
+ MFP_XWAY(GPIO10, GPIO, SPI, MII, JTAG),
+ MFP_XWAY(GPIO11, GPIO, EBU, CGU, JTAG),
+ MFP_XWAY(GPIO12, GPIO, EBU, MII, SDIO),
+ MFP_XWAY(GPIO13, GPIO, EBU, MII, CGU),
+ MFP_XWAY(GPIO14, GPIO, EBU, SPI, CGU),
+ MFP_XWAY(GPIO15, GPIO, EBU, SPI, SDIO),
+ MFP_XWAY(GPIO16, GPIO, NONE, NONE, NONE),
+ MFP_XWAY(GPIO17, GPIO, NONE, NONE, NONE),
+ MFP_XWAY(GPIO18, GPIO, NONE, NONE, NONE),
+ MFP_XWAY(GPIO19, GPIO, EBU, MII, SDIO),
+ MFP_XWAY(GPIO20, GPIO, EBU, MII, SDIO),
+ MFP_XWAY(GPIO21, GPIO, EBU, MII, EBU2),
+ MFP_XWAY(GPIO22, GPIO, EBU, MII, CGU),
+ MFP_XWAY(GPIO23, GPIO, EBU, MII, CGU),
+ MFP_XWAY(GPIO24, GPIO, EBU, EBU2, MDIO),
+ MFP_XWAY(GPIO25, GPIO, EBU, MII, GPT),
+ MFP_XWAY(GPIO26, GPIO, EBU, MII, SDIO),
+ MFP_XWAY(GPIO27, GPIO, EBU, NONE, MDIO),
+ MFP_XWAY(GPIO28, GPIO, MII, EBU, SDIO),
+ MFP_XWAY(GPIO29, GPIO, EBU, MII, EXIN),
+ MFP_XWAY(GPIO30, GPIO, NONE, NONE, NONE),
+ MFP_XWAY(GPIO31, GPIO, NONE, NONE, NONE),
+};
+
+static const unsigned ase_exin_pin_map[] = {GPIO6, GPIO29, GPIO0};
+
+static const unsigned ase_pins_exin0[] = {GPIO6};
+static const unsigned ase_pins_exin1[] = {GPIO29};
+static const unsigned ase_pins_exin2[] = {GPIO0};
+
+static const unsigned ase_pins_jtag[] = {GPIO7, GPIO8, GPIO9, GPIO10, GPIO11};
+static const unsigned ase_pins_asc[] = {GPIO5, GPIO6};
+static const unsigned ase_pins_stp[] = {GPIO1, GPIO2, GPIO3};
+static const unsigned ase_pins_mdio[] = {GPIO24, GPIO27};
+static const unsigned ase_pins_ephy_led0[] = {GPIO2};
+static const unsigned ase_pins_ephy_led1[] = {GPIO3};
+static const unsigned ase_pins_ephy_led2[] = {GPIO4};
+static const unsigned ase_pins_dfe_led0[] = {GPIO1};
+static const unsigned ase_pins_dfe_led1[] = {GPIO2};
+
+static const unsigned ase_pins_spi[] = {GPIO8, GPIO9, GPIO10}; /* DEPRECATED */
+static const unsigned ase_pins_spi_di[] = {GPIO8};
+static const unsigned ase_pins_spi_do[] = {GPIO9};
+static const unsigned ase_pins_spi_clk[] = {GPIO10};
+static const unsigned ase_pins_spi_cs1[] = {GPIO7};
+static const unsigned ase_pins_spi_cs2[] = {GPIO15};
+static const unsigned ase_pins_spi_cs3[] = {GPIO14};
+
+static const unsigned ase_pins_gpt1[] = {GPIO5};
+static const unsigned ase_pins_gpt2[] = {GPIO4};
+static const unsigned ase_pins_gpt3[] = {GPIO25};
+
+static const unsigned ase_pins_clkout0[] = {GPIO23};
+static const unsigned ase_pins_clkout1[] = {GPIO22};
+static const unsigned ase_pins_clkout2[] = {GPIO14};
+
+static const struct ltq_pin_group ase_grps[] = {
+ GRP_MUX("exin0", EXIN, ase_pins_exin0),
+ GRP_MUX("exin1", EXIN, ase_pins_exin1),
+ GRP_MUX("exin2", EXIN, ase_pins_exin2),
+ GRP_MUX("jtag", JTAG, ase_pins_jtag),
+ GRP_MUX("spi", SPI, ase_pins_spi), /* DEPRECATED */
+ GRP_MUX("spi_di", SPI, ase_pins_spi_di),
+ GRP_MUX("spi_do", SPI, ase_pins_spi_do),
+ GRP_MUX("spi_clk", SPI, ase_pins_spi_clk),
+ GRP_MUX("spi_cs1", SPI, ase_pins_spi_cs1),
+ GRP_MUX("spi_cs2", SPI, ase_pins_spi_cs2),
+ GRP_MUX("spi_cs3", SPI, ase_pins_spi_cs3),
+ GRP_MUX("asc", ASC, ase_pins_asc),
+ GRP_MUX("stp", STP, ase_pins_stp),
+ GRP_MUX("gpt1", GPT, ase_pins_gpt1),
+ GRP_MUX("gpt2", GPT, ase_pins_gpt2),
+ GRP_MUX("gpt3", GPT, ase_pins_gpt3),
+ GRP_MUX("clkout0", CGU, ase_pins_clkout0),
+ GRP_MUX("clkout1", CGU, ase_pins_clkout1),
+ GRP_MUX("clkout2", CGU, ase_pins_clkout2),
+ GRP_MUX("mdio", MDIO, ase_pins_mdio),
+ GRP_MUX("dfe led0", DFE, ase_pins_dfe_led0),
+ GRP_MUX("dfe led1", DFE, ase_pins_dfe_led1),
+ GRP_MUX("ephy led0", EPHY, ase_pins_ephy_led0),
+ GRP_MUX("ephy led1", EPHY, ase_pins_ephy_led1),
+ GRP_MUX("ephy led2", EPHY, ase_pins_ephy_led2),
+};
+
+static const char * const ase_exin_grps[] = {"exin0", "exin1", "exin2"};
+static const char * const ase_gpt_grps[] = {"gpt1", "gpt2", "gpt3"};
+static const char * const ase_cgu_grps[] = {"clkout0", "clkout1",
+ "clkout2"};
+static const char * const ase_mdio_grps[] = {"mdio"};
+static const char * const ase_dfe_grps[] = {"dfe led0", "dfe led1"};
+static const char * const ase_ephy_grps[] = {"ephy led0", "ephy led1",
+ "ephy led2"};
+static const char * const ase_asc_grps[] = {"asc"};
+static const char * const ase_jtag_grps[] = {"jtag"};
+static const char * const ase_stp_grps[] = {"stp"};
+static const char * const ase_spi_grps[] = {"spi", /* DEPRECATED */
+ "spi_di", "spi_do",
+ "spi_clk", "spi_cs1",
+ "spi_cs2", "spi_cs3"};
+
static const struct ltq_pmx_func ase_funcs[] = {
{"spi", ARRAY_AND_SIZE(ase_spi_grps)},
{"asc", ARRAY_AND_SIZE(ase_asc_grps)},
+ {"cgu", ARRAY_AND_SIZE(ase_cgu_grps)},
{"jtag", ARRAY_AND_SIZE(ase_jtag_grps)},
{"exin", ARRAY_AND_SIZE(ase_exin_grps)},
{"stp", ARRAY_AND_SIZE(ase_stp_grps)},
{"gpt", ARRAY_AND_SIZE(ase_gpt_grps)},
+ {"mdio", ARRAY_AND_SIZE(ase_mdio_grps)},
{"ephy", ARRAY_AND_SIZE(ase_ephy_grps)},
{"dfe", ARRAY_AND_SIZE(ase_dfe_grps)},
};
+/* --------- danube related code --------- */
+#define DANUBE_MAX_PIN 32
+
+static const struct ltq_mfp_pin danube_mfp[] = {
+ /* pin f0 f1 f2 f3 */
+ MFP_XWAY(GPIO0, GPIO, EXIN, SDIO, TDM),
+ MFP_XWAY(GPIO1, GPIO, EXIN, CBUS, MII),
+ MFP_XWAY(GPIO2, GPIO, CGU, EXIN, MII),
+ MFP_XWAY(GPIO3, GPIO, CGU, SDIO, PCI),
+ MFP_XWAY(GPIO4, GPIO, STP, DFE, ASC),
+ MFP_XWAY(GPIO5, GPIO, STP, MII, DFE),
+ MFP_XWAY(GPIO6, GPIO, STP, GPT, ASC),
+ MFP_XWAY(GPIO7, GPIO, CGU, CBUS, MII),
+ MFP_XWAY(GPIO8, GPIO, CGU, NMI, MII),
+ MFP_XWAY(GPIO9, GPIO, ASC, SPI, MII),
+ MFP_XWAY(GPIO10, GPIO, ASC, SPI, MII),
+ MFP_XWAY(GPIO11, GPIO, ASC, CBUS, SPI),
+ MFP_XWAY(GPIO12, GPIO, ASC, CBUS, MCD),
+ MFP_XWAY(GPIO13, GPIO, EBU, SPI, MII),
+ MFP_XWAY(GPIO14, GPIO, CGU, CBUS, MII),
+ MFP_XWAY(GPIO15, GPIO, SPI, SDIO, JTAG),
+ MFP_XWAY(GPIO16, GPIO, SPI, SDIO, JTAG),
+ MFP_XWAY(GPIO17, GPIO, SPI, SDIO, JTAG),
+ MFP_XWAY(GPIO18, GPIO, SPI, SDIO, JTAG),
+ MFP_XWAY(GPIO19, GPIO, PCI, SDIO, MII),
+ MFP_XWAY(GPIO20, GPIO, JTAG, SDIO, MII),
+ MFP_XWAY(GPIO21, GPIO, PCI, EBU, GPT),
+ MFP_XWAY(GPIO22, GPIO, SPI, MCD, MII),
+ MFP_XWAY(GPIO23, GPIO, EBU, PCI, STP),
+ MFP_XWAY(GPIO24, GPIO, EBU, TDM, PCI),
+ MFP_XWAY(GPIO25, GPIO, TDM, SDIO, ASC),
+ MFP_XWAY(GPIO26, GPIO, EBU, TDM, SDIO),
+ MFP_XWAY(GPIO27, GPIO, TDM, SDIO, ASC),
+ MFP_XWAY(GPIO28, GPIO, GPT, MII, SDIO),
+ MFP_XWAY(GPIO29, GPIO, PCI, CBUS, MII),
+ MFP_XWAY(GPIO30, GPIO, PCI, CBUS, MII),
+ MFP_XWAY(GPIO31, GPIO, EBU, PCI, MII),
+};
+
+static const unsigned danube_exin_pin_map[] = {GPIO0, GPIO1, GPIO2};
+
+static const unsigned danube_pins_exin0[] = {GPIO0};
+static const unsigned danube_pins_exin1[] = {GPIO1};
+static const unsigned danube_pins_exin2[] = {GPIO2};
+
+static const unsigned danube_pins_jtag[] = {GPIO15, GPIO16, GPIO17, GPIO18, GPIO20};
+static const unsigned danube_pins_asc0[] = {GPIO11, GPIO12};
+static const unsigned danube_pins_asc0_cts_rts[] = {GPIO9, GPIO10};
+static const unsigned danube_pins_stp[] = {GPIO4, GPIO5, GPIO6};
+static const unsigned danube_pins_nmi[] = {GPIO8};
+
+static const unsigned danube_pins_dfe_led0[] = {GPIO4};
+static const unsigned danube_pins_dfe_led1[] = {GPIO5};
+
+static const unsigned danube_pins_ebu_a24[] = {GPIO13};
+static const unsigned danube_pins_ebu_clk[] = {GPIO21};
+static const unsigned danube_pins_ebu_cs1[] = {GPIO23};
+static const unsigned danube_pins_ebu_a23[] = {GPIO24};
+static const unsigned danube_pins_ebu_wait[] = {GPIO26};
+static const unsigned danube_pins_ebu_a25[] = {GPIO31};
+
+static const unsigned danube_pins_nand_ale[] = {GPIO13};
+static const unsigned danube_pins_nand_cs1[] = {GPIO23};
+static const unsigned danube_pins_nand_cle[] = {GPIO24};
+
+static const unsigned danube_pins_spi[] = {GPIO16, GPIO17, GPIO18}; /* DEPRECATED */
+static const unsigned danube_pins_spi_di[] = {GPIO16};
+static const unsigned danube_pins_spi_do[] = {GPIO17};
+static const unsigned danube_pins_spi_clk[] = {GPIO18};
+static const unsigned danube_pins_spi_cs1[] = {GPIO15};
+static const unsigned danube_pins_spi_cs2[] = {GPIO21};
+static const unsigned danube_pins_spi_cs3[] = {GPIO13};
+static const unsigned danube_pins_spi_cs4[] = {GPIO10};
+static const unsigned danube_pins_spi_cs5[] = {GPIO9};
+static const unsigned danube_pins_spi_cs6[] = {GPIO11};
+
+static const unsigned danube_pins_gpt1[] = {GPIO28};
+static const unsigned danube_pins_gpt2[] = {GPIO21};
+static const unsigned danube_pins_gpt3[] = {GPIO6};
+
+static const unsigned danube_pins_clkout0[] = {GPIO8};
+static const unsigned danube_pins_clkout1[] = {GPIO7};
+static const unsigned danube_pins_clkout2[] = {GPIO3};
+static const unsigned danube_pins_clkout3[] = {GPIO2};
+
+static const unsigned danube_pins_pci_gnt1[] = {GPIO30};
+static const unsigned danube_pins_pci_gnt2[] = {GPIO23};
+static const unsigned danube_pins_pci_gnt3[] = {GPIO19};
+static const unsigned danube_pins_pci_req1[] = {GPIO29};
+static const unsigned danube_pins_pci_req2[] = {GPIO31};
+static const unsigned danube_pins_pci_req3[] = {GPIO3};
+
+static const struct ltq_pin_group danube_grps[] = {
+ GRP_MUX("exin0", EXIN, danube_pins_exin0),
+ GRP_MUX("exin1", EXIN, danube_pins_exin1),
+ GRP_MUX("exin2", EXIN, danube_pins_exin2),
+ GRP_MUX("jtag", JTAG, danube_pins_jtag),
+ GRP_MUX("ebu a23", EBU, danube_pins_ebu_a23),
+ GRP_MUX("ebu a24", EBU, danube_pins_ebu_a24),
+ GRP_MUX("ebu a25", EBU, danube_pins_ebu_a25),
+ GRP_MUX("ebu clk", EBU, danube_pins_ebu_clk),
+ GRP_MUX("ebu cs1", EBU, danube_pins_ebu_cs1),
+ GRP_MUX("ebu wait", EBU, danube_pins_ebu_wait),
+ GRP_MUX("nand ale", EBU, danube_pins_nand_ale),
+ GRP_MUX("nand cs1", EBU, danube_pins_nand_cs1),
+ GRP_MUX("nand cle", EBU, danube_pins_nand_cle),
+ GRP_MUX("spi", SPI, danube_pins_spi), /* DEPRECATED */
+ GRP_MUX("spi_di", SPI, danube_pins_spi_di),
+ GRP_MUX("spi_do", SPI, danube_pins_spi_do),
+ GRP_MUX("spi_clk", SPI, danube_pins_spi_clk),
+ GRP_MUX("spi_cs1", SPI, danube_pins_spi_cs1),
+ GRP_MUX("spi_cs2", SPI, danube_pins_spi_cs2),
+ GRP_MUX("spi_cs3", SPI, danube_pins_spi_cs3),
+ GRP_MUX("spi_cs4", SPI, danube_pins_spi_cs4),
+ GRP_MUX("spi_cs5", SPI, danube_pins_spi_cs5),
+ GRP_MUX("spi_cs6", SPI, danube_pins_spi_cs6),
+ GRP_MUX("asc0", ASC, danube_pins_asc0),
+ GRP_MUX("asc0 cts rts", ASC, danube_pins_asc0_cts_rts),
+ GRP_MUX("stp", STP, danube_pins_stp),
+ GRP_MUX("nmi", NMI, danube_pins_nmi),
+ GRP_MUX("gpt1", GPT, danube_pins_gpt1),
+ GRP_MUX("gpt2", GPT, danube_pins_gpt2),
+ GRP_MUX("gpt3", GPT, danube_pins_gpt3),
+ GRP_MUX("clkout0", CGU, danube_pins_clkout0),
+ GRP_MUX("clkout1", CGU, danube_pins_clkout1),
+ GRP_MUX("clkout2", CGU, danube_pins_clkout2),
+ GRP_MUX("clkout3", CGU, danube_pins_clkout3),
+ GRP_MUX("gnt1", PCI, danube_pins_pci_gnt1),
+ GRP_MUX("gnt2", PCI, danube_pins_pci_gnt2),
+ GRP_MUX("gnt3", PCI, danube_pins_pci_gnt3),
+ GRP_MUX("req1", PCI, danube_pins_pci_req1),
+ GRP_MUX("req2", PCI, danube_pins_pci_req2),
+ GRP_MUX("req3", PCI, danube_pins_pci_req3),
+ GRP_MUX("dfe led0", DFE, danube_pins_dfe_led0),
+ GRP_MUX("dfe led1", DFE, danube_pins_dfe_led1),
+};
+
+static const char * const danube_pci_grps[] = {"gnt1", "gnt2",
+ "gnt3", "req1",
+ "req2", "req3"};
+static const char * const danube_spi_grps[] = {"spi", /* DEPRECATED */
+ "spi_di", "spi_do",
+ "spi_clk", "spi_cs1",
+ "spi_cs2", "spi_cs3",
+ "spi_cs4", "spi_cs5",
+ "spi_cs6"};
+static const char * const danube_cgu_grps[] = {"clkout0", "clkout1",
+ "clkout2", "clkout3"};
+static const char * const danube_ebu_grps[] = {"ebu a23", "ebu a24",
+ "ebu a25", "ebu cs1",
+ "ebu wait", "ebu clk",
+ "nand ale", "nand cs1",
+ "nand cle"};
+static const char * const danube_dfe_grps[] = {"dfe led0", "dfe led1"};
+static const char * const danube_exin_grps[] = {"exin0", "exin1", "exin2"};
+static const char * const danube_gpt_grps[] = {"gpt1", "gpt2", "gpt3"};
+static const char * const danube_asc_grps[] = {"asc0", "asc0 cts rts"};
+static const char * const danube_jtag_grps[] = {"jtag"};
+static const char * const danube_stp_grps[] = {"stp"};
+static const char * const danube_nmi_grps[] = {"nmi"};
+
+static const struct ltq_pmx_func danube_funcs[] = {
+ {"spi", ARRAY_AND_SIZE(danube_spi_grps)},
+ {"asc", ARRAY_AND_SIZE(danube_asc_grps)},
+ {"cgu", ARRAY_AND_SIZE(danube_cgu_grps)},
+ {"jtag", ARRAY_AND_SIZE(danube_jtag_grps)},
+ {"exin", ARRAY_AND_SIZE(danube_exin_grps)},
+ {"stp", ARRAY_AND_SIZE(danube_stp_grps)},
+ {"gpt", ARRAY_AND_SIZE(danube_gpt_grps)},
+ {"nmi", ARRAY_AND_SIZE(danube_nmi_grps)},
+ {"pci", ARRAY_AND_SIZE(danube_pci_grps)},
+ {"ebu", ARRAY_AND_SIZE(danube_ebu_grps)},
+ {"dfe", ARRAY_AND_SIZE(danube_dfe_grps)},
+};
+
+/* --------- xrx100 related code --------- */
+#define XRX100_MAX_PIN 56
+
+static const struct ltq_mfp_pin xrx100_mfp[] = {
+ /* pin f0 f1 f2 f3 */
+ MFP_XWAY(GPIO0, GPIO, EXIN, SDIO, TDM),
+ MFP_XWAY(GPIO1, GPIO, EXIN, CBUS, SIN),
+ MFP_XWAY(GPIO2, GPIO, CGU, EXIN, NONE),
+ MFP_XWAY(GPIO3, GPIO, CGU, SDIO, PCI),
+ MFP_XWAY(GPIO4, GPIO, STP, DFE, ASC),
+ MFP_XWAY(GPIO5, GPIO, STP, NONE, DFE),
+ MFP_XWAY(GPIO6, GPIO, STP, GPT, ASC),
+ MFP_XWAY(GPIO7, GPIO, CGU, CBUS, NONE),
+ MFP_XWAY(GPIO8, GPIO, CGU, NMI, NONE),
+ MFP_XWAY(GPIO9, GPIO, ASC, SPI, EXIN),
+ MFP_XWAY(GPIO10, GPIO, ASC, SPI, EXIN),
+ MFP_XWAY(GPIO11, GPIO, ASC, CBUS, SPI),
+ MFP_XWAY(GPIO12, GPIO, ASC, CBUS, MCD),
+ MFP_XWAY(GPIO13, GPIO, EBU, SPI, NONE),
+ MFP_XWAY(GPIO14, GPIO, CGU, NONE, NONE),
+ MFP_XWAY(GPIO15, GPIO, SPI, SDIO, MCD),
+ MFP_XWAY(GPIO16, GPIO, SPI, SDIO, NONE),
+ MFP_XWAY(GPIO17, GPIO, SPI, SDIO, NONE),
+ MFP_XWAY(GPIO18, GPIO, SPI, SDIO, NONE),
+ MFP_XWAY(GPIO19, GPIO, PCI, SDIO, CGU),
+ MFP_XWAY(GPIO20, GPIO, NONE, SDIO, EBU),
+ MFP_XWAY(GPIO21, GPIO, PCI, EBU, GPT),
+ MFP_XWAY(GPIO22, GPIO, SPI, NONE, EBU),
+ MFP_XWAY(GPIO23, GPIO, EBU, PCI, STP),
+ MFP_XWAY(GPIO24, GPIO, EBU, TDM, PCI),
+ MFP_XWAY(GPIO25, GPIO, TDM, SDIO, ASC),
+ MFP_XWAY(GPIO26, GPIO, EBU, TDM, SDIO),
+ MFP_XWAY(GPIO27, GPIO, TDM, SDIO, ASC),
+ MFP_XWAY(GPIO28, GPIO, GPT, NONE, SDIO),
+ MFP_XWAY(GPIO29, GPIO, PCI, CBUS, NONE),
+ MFP_XWAY(GPIO30, GPIO, PCI, CBUS, NONE),
+ MFP_XWAY(GPIO31, GPIO, EBU, PCI, NONE),
+ MFP_XWAY(GPIO32, GPIO, MII, NONE, EBU),
+ MFP_XWAY(GPIO33, GPIO, MII, NONE, EBU),
+ MFP_XWAY(GPIO34, GPIO, SIN, SSI, NONE),
+ MFP_XWAY(GPIO35, GPIO, SIN, SSI, NONE),
+ MFP_XWAY(GPIO36, GPIO, SIN, SSI, NONE),
+ MFP_XWAY(GPIO37, GPIO, PCI, NONE, NONE),
+ MFP_XWAY(GPIO38, GPIO, PCI, NONE, NONE),
+ MFP_XWAY(GPIO39, GPIO, NONE, EXIN, NONE),
+ MFP_XWAY(GPIO40, GPIO, MII, TDM, NONE),
+ MFP_XWAY(GPIO41, GPIO, MII, TDM, NONE),
+ MFP_XWAY(GPIO42, GPIO, MDIO, NONE, NONE),
+ MFP_XWAY(GPIO43, GPIO, MDIO, NONE, NONE),
+ MFP_XWAY(GPIO44, GPIO, MII, SIN, NONE),
+ MFP_XWAY(GPIO45, GPIO, MII, NONE, SIN),
+ MFP_XWAY(GPIO46, GPIO, MII, NONE, EXIN),
+ MFP_XWAY(GPIO47, GPIO, MII, NONE, SIN),
+ MFP_XWAY(GPIO48, GPIO, EBU, NONE, NONE),
+ MFP_XWAY(GPIO49, GPIO, EBU, NONE, NONE),
+ MFP_XWAY(GPIO50, GPIO, NONE, NONE, NONE),
+ MFP_XWAY(GPIO51, GPIO, NONE, NONE, NONE),
+ MFP_XWAY(GPIO52, GPIO, NONE, NONE, NONE),
+ MFP_XWAY(GPIO53, GPIO, NONE, NONE, NONE),
+ MFP_XWAY(GPIO54, GPIO, NONE, NONE, NONE),
+ MFP_XWAY(GPIO55, GPIO, NONE, NONE, NONE),
+};
+
+static const unsigned xrx100_exin_pin_map[] = {GPIO0, GPIO1, GPIO2, GPIO39, GPIO10, GPIO9};
+
+static const unsigned xrx100_pins_exin0[] = {GPIO0};
+static const unsigned xrx100_pins_exin1[] = {GPIO1};
+static const unsigned xrx100_pins_exin2[] = {GPIO2};
+static const unsigned xrx100_pins_exin3[] = {GPIO39};
+static const unsigned xrx100_pins_exin4[] = {GPIO10};
+static const unsigned xrx100_pins_exin5[] = {GPIO9};
+
+static const unsigned xrx100_pins_asc0[] = {GPIO11, GPIO12};
+static const unsigned xrx100_pins_asc0_cts_rts[] = {GPIO9, GPIO10};
+static const unsigned xrx100_pins_stp[] = {GPIO4, GPIO5, GPIO6};
+static const unsigned xrx100_pins_nmi[] = {GPIO8};
+static const unsigned xrx100_pins_mdio[] = {GPIO42, GPIO43};
+
+static const unsigned xrx100_pins_dfe_led0[] = {GPIO4};
+static const unsigned xrx100_pins_dfe_led1[] = {GPIO5};
+
+static const unsigned xrx100_pins_ebu_a24[] = {GPIO13};
+static const unsigned xrx100_pins_ebu_clk[] = {GPIO21};
+static const unsigned xrx100_pins_ebu_cs1[] = {GPIO23};
+static const unsigned xrx100_pins_ebu_a23[] = {GPIO24};
+static const unsigned xrx100_pins_ebu_wait[] = {GPIO26};
+static const unsigned xrx100_pins_ebu_a25[] = {GPIO31};
+
+static const unsigned xrx100_pins_nand_ale[] = {GPIO13};
+static const unsigned xrx100_pins_nand_cs1[] = {GPIO23};
+static const unsigned xrx100_pins_nand_cle[] = {GPIO24};
+static const unsigned xrx100_pins_nand_rdy[] = {GPIO48};
+static const unsigned xrx100_pins_nand_rd[] = {GPIO49};
+
+static const unsigned xrx100_pins_spi_di[] = {GPIO16};
+static const unsigned xrx100_pins_spi_do[] = {GPIO17};
+static const unsigned xrx100_pins_spi_clk[] = {GPIO18};
+static const unsigned xrx100_pins_spi_cs1[] = {GPIO15};
+static const unsigned xrx100_pins_spi_cs2[] = {GPIO22};
+static const unsigned xrx100_pins_spi_cs3[] = {GPIO13};
+static const unsigned xrx100_pins_spi_cs4[] = {GPIO10};
+static const unsigned xrx100_pins_spi_cs5[] = {GPIO9};
+static const unsigned xrx100_pins_spi_cs6[] = {GPIO11};
+
+static const unsigned xrx100_pins_gpt1[] = {GPIO28};
+static const unsigned xrx100_pins_gpt2[] = {GPIO21};
+static const unsigned xrx100_pins_gpt3[] = {GPIO6};
+
+static const unsigned xrx100_pins_clkout0[] = {GPIO8};
+static const unsigned xrx100_pins_clkout1[] = {GPIO7};
+static const unsigned xrx100_pins_clkout2[] = {GPIO3};
+static const unsigned xrx100_pins_clkout3[] = {GPIO2};
+
+static const unsigned xrx100_pins_pci_gnt1[] = {GPIO30};
+static const unsigned xrx100_pins_pci_gnt2[] = {GPIO23};
+static const unsigned xrx100_pins_pci_gnt3[] = {GPIO19};
+static const unsigned xrx100_pins_pci_gnt4[] = {GPIO38};
+static const unsigned xrx100_pins_pci_req1[] = {GPIO29};
+static const unsigned xrx100_pins_pci_req2[] = {GPIO31};
+static const unsigned xrx100_pins_pci_req3[] = {GPIO3};
+static const unsigned xrx100_pins_pci_req4[] = {GPIO37};
+
+static const struct ltq_pin_group xrx100_grps[] = {
+ GRP_MUX("exin0", EXIN, xrx100_pins_exin0),
+ GRP_MUX("exin1", EXIN, xrx100_pins_exin1),
+ GRP_MUX("exin2", EXIN, xrx100_pins_exin2),
+ GRP_MUX("exin3", EXIN, xrx100_pins_exin3),
+ GRP_MUX("exin4", EXIN, xrx100_pins_exin4),
+ GRP_MUX("exin5", EXIN, xrx100_pins_exin5),
+ GRP_MUX("ebu a23", EBU, xrx100_pins_ebu_a23),
+ GRP_MUX("ebu a24", EBU, xrx100_pins_ebu_a24),
+ GRP_MUX("ebu a25", EBU, xrx100_pins_ebu_a25),
+ GRP_MUX("ebu clk", EBU, xrx100_pins_ebu_clk),
+ GRP_MUX("ebu cs1", EBU, xrx100_pins_ebu_cs1),
+ GRP_MUX("ebu wait", EBU, xrx100_pins_ebu_wait),
+ GRP_MUX("nand ale", EBU, xrx100_pins_nand_ale),
+ GRP_MUX("nand cs1", EBU, xrx100_pins_nand_cs1),
+ GRP_MUX("nand cle", EBU, xrx100_pins_nand_cle),
+ GRP_MUX("nand rdy", EBU, xrx100_pins_nand_rdy),
+ GRP_MUX("nand rd", EBU, xrx100_pins_nand_rd),
+ GRP_MUX("spi_di", SPI, xrx100_pins_spi_di),
+ GRP_MUX("spi_do", SPI, xrx100_pins_spi_do),
+ GRP_MUX("spi_clk", SPI, xrx100_pins_spi_clk),
+ GRP_MUX("spi_cs1", SPI, xrx100_pins_spi_cs1),
+ GRP_MUX("spi_cs2", SPI, xrx100_pins_spi_cs2),
+ GRP_MUX("spi_cs3", SPI, xrx100_pins_spi_cs3),
+ GRP_MUX("spi_cs4", SPI, xrx100_pins_spi_cs4),
+ GRP_MUX("spi_cs5", SPI, xrx100_pins_spi_cs5),
+ GRP_MUX("spi_cs6", SPI, xrx100_pins_spi_cs6),
+ GRP_MUX("asc0", ASC, xrx100_pins_asc0),
+ GRP_MUX("asc0 cts rts", ASC, xrx100_pins_asc0_cts_rts),
+ GRP_MUX("stp", STP, xrx100_pins_stp),
+ GRP_MUX("nmi", NMI, xrx100_pins_nmi),
+ GRP_MUX("gpt1", GPT, xrx100_pins_gpt1),
+ GRP_MUX("gpt2", GPT, xrx100_pins_gpt2),
+ GRP_MUX("gpt3", GPT, xrx100_pins_gpt3),
+ GRP_MUX("clkout0", CGU, xrx100_pins_clkout0),
+ GRP_MUX("clkout1", CGU, xrx100_pins_clkout1),
+ GRP_MUX("clkout2", CGU, xrx100_pins_clkout2),
+ GRP_MUX("clkout3", CGU, xrx100_pins_clkout3),
+ GRP_MUX("gnt1", PCI, xrx100_pins_pci_gnt1),
+ GRP_MUX("gnt2", PCI, xrx100_pins_pci_gnt2),
+ GRP_MUX("gnt3", PCI, xrx100_pins_pci_gnt3),
+ GRP_MUX("gnt4", PCI, xrx100_pins_pci_gnt4),
+ GRP_MUX("req1", PCI, xrx100_pins_pci_req1),
+ GRP_MUX("req2", PCI, xrx100_pins_pci_req2),
+ GRP_MUX("req3", PCI, xrx100_pins_pci_req3),
+ GRP_MUX("req4", PCI, xrx100_pins_pci_req4),
+ GRP_MUX("mdio", MDIO, xrx100_pins_mdio),
+ GRP_MUX("dfe led0", DFE, xrx100_pins_dfe_led0),
+ GRP_MUX("dfe led1", DFE, xrx100_pins_dfe_led1),
+};
+
+static const char * const xrx100_pci_grps[] = {"gnt1", "gnt2",
+ "gnt3", "gnt4",
+ "req1", "req2",
+ "req3", "req4"};
+static const char * const xrx100_spi_grps[] = {"spi_di", "spi_do",
+ "spi_clk", "spi_cs1",
+ "spi_cs2", "spi_cs3",
+ "spi_cs4", "spi_cs5",
+ "spi_cs6"};
+static const char * const xrx100_cgu_grps[] = {"clkout0", "clkout1",
+ "clkout2", "clkout3"};
+static const char * const xrx100_ebu_grps[] = {"ebu a23", "ebu a24",
+ "ebu a25", "ebu cs1",
+ "ebu wait", "ebu clk",
+ "nand ale", "nand cs1",
+ "nand cle", "nand rdy",
+ "nand rd"};
+static const char * const xrx100_exin_grps[] = {"exin0", "exin1", "exin2",
+ "exin3", "exin4", "exin5"};
+static const char * const xrx100_gpt_grps[] = {"gpt1", "gpt2", "gpt3"};
+static const char * const xrx100_asc_grps[] = {"asc0", "asc0 cts rts"};
+static const char * const xrx100_stp_grps[] = {"stp"};
+static const char * const xrx100_nmi_grps[] = {"nmi"};
+static const char * const xrx100_mdio_grps[] = {"mdio"};
+static const char * const xrx100_dfe_grps[] = {"dfe led0", "dfe led1"};
+
+static const struct ltq_pmx_func xrx100_funcs[] = {
+ {"spi", ARRAY_AND_SIZE(xrx100_spi_grps)},
+ {"asc", ARRAY_AND_SIZE(xrx100_asc_grps)},
+ {"cgu", ARRAY_AND_SIZE(xrx100_cgu_grps)},
+ {"exin", ARRAY_AND_SIZE(xrx100_exin_grps)},
+ {"stp", ARRAY_AND_SIZE(xrx100_stp_grps)},
+ {"gpt", ARRAY_AND_SIZE(xrx100_gpt_grps)},
+ {"nmi", ARRAY_AND_SIZE(xrx100_nmi_grps)},
+ {"pci", ARRAY_AND_SIZE(xrx100_pci_grps)},
+ {"ebu", ARRAY_AND_SIZE(xrx100_ebu_grps)},
+ {"mdio", ARRAY_AND_SIZE(xrx100_mdio_grps)},
+ {"dfe", ARRAY_AND_SIZE(xrx100_dfe_grps)},
+};
+
+/* --------- xrx200 related code --------- */
+#define XRX200_MAX_PIN 50
+
+static const struct ltq_mfp_pin xrx200_mfp[] = {
+ /* pin f0 f1 f2 f3 */
+ MFP_XWAY(GPIO0, GPIO, EXIN, SDIO, TDM),
+ MFP_XWAY(GPIO1, GPIO, EXIN, CBUS, SIN),
+ MFP_XWAY(GPIO2, GPIO, CGU, EXIN, GPHY),
+ MFP_XWAY(GPIO3, GPIO, CGU, SDIO, PCI),
+ MFP_XWAY(GPIO4, GPIO, STP, DFE, USIF),
+ MFP_XWAY(GPIO5, GPIO, STP, GPHY, DFE),
+ MFP_XWAY(GPIO6, GPIO, STP, GPT, USIF),
+ MFP_XWAY(GPIO7, GPIO, CGU, CBUS, GPHY),
+ MFP_XWAY(GPIO8, GPIO, CGU, NMI, NONE),
+ MFP_XWAY(GPIO9, GPIO, USIF, SPI, EXIN),
+ MFP_XWAY(GPIO10, GPIO, USIF, SPI, EXIN),
+ MFP_XWAY(GPIO11, GPIO, USIF, CBUS, SPI),
+ MFP_XWAY(GPIO12, GPIO, USIF, CBUS, MCD),
+ MFP_XWAY(GPIO13, GPIO, EBU, SPI, NONE),
+ MFP_XWAY(GPIO14, GPIO, CGU, CBUS, USIF),
+ MFP_XWAY(GPIO15, GPIO, SPI, SDIO, MCD),
+ MFP_XWAY(GPIO16, GPIO, SPI, SDIO, NONE),
+ MFP_XWAY(GPIO17, GPIO, SPI, SDIO, NONE),
+ MFP_XWAY(GPIO18, GPIO, SPI, SDIO, NONE),
+ MFP_XWAY(GPIO19, GPIO, PCI, SDIO, CGU),
+ MFP_XWAY(GPIO20, GPIO, NONE, SDIO, EBU),
+ MFP_XWAY(GPIO21, GPIO, PCI, EBU, GPT),
+ MFP_XWAY(GPIO22, GPIO, SPI, CGU, EBU),
+ MFP_XWAY(GPIO23, GPIO, EBU, PCI, STP),
+ MFP_XWAY(GPIO24, GPIO, EBU, TDM, PCI),
+ MFP_XWAY(GPIO25, GPIO, TDM, SDIO, USIF),
+ MFP_XWAY(GPIO26, GPIO, EBU, TDM, SDIO),
+ MFP_XWAY(GPIO27, GPIO, TDM, SDIO, USIF),
+ MFP_XWAY(GPIO28, GPIO, GPT, PCI, SDIO),
+ MFP_XWAY(GPIO29, GPIO, PCI, CBUS, EXIN),
+ MFP_XWAY(GPIO30, GPIO, PCI, CBUS, NONE),
+ MFP_XWAY(GPIO31, GPIO, EBU, PCI, NONE),
+ MFP_XWAY(GPIO32, GPIO, MII, NONE, EBU),
+ MFP_XWAY(GPIO33, GPIO, MII, NONE, EBU),
+ MFP_XWAY(GPIO34, GPIO, SIN, SSI, NONE),
+ MFP_XWAY(GPIO35, GPIO, SIN, SSI, NONE),
+ MFP_XWAY(GPIO36, GPIO, SIN, SSI, EXIN),
+ MFP_XWAY(GPIO37, GPIO, USIF, NONE, PCI),
+ MFP_XWAY(GPIO38, GPIO, PCI, USIF, NONE),
+ MFP_XWAY(GPIO39, GPIO, USIF, EXIN, NONE),
+ MFP_XWAY(GPIO40, GPIO, MII, TDM, NONE),
+ MFP_XWAY(GPIO41, GPIO, MII, TDM, NONE),
+ MFP_XWAY(GPIO42, GPIO, MDIO, NONE, NONE),
+ MFP_XWAY(GPIO43, GPIO, MDIO, NONE, NONE),
+ MFP_XWAY(GPIO44, GPIO, MII, SIN, GPHY),
+ MFP_XWAY(GPIO45, GPIO, MII, GPHY, SIN),
+ MFP_XWAY(GPIO46, GPIO, MII, NONE, EXIN),
+ MFP_XWAY(GPIO47, GPIO, MII, GPHY, SIN),
+ MFP_XWAY(GPIO48, GPIO, EBU, NONE, NONE),
+ MFP_XWAY(GPIO49, GPIO, EBU, NONE, NONE),
+};
+
+static const unsigned xrx200_exin_pin_map[] = {GPIO0, GPIO1, GPIO2, GPIO39, GPIO10, GPIO9};
+
+static const unsigned xrx200_pins_exin0[] = {GPIO0};
+static const unsigned xrx200_pins_exin1[] = {GPIO1};
+static const unsigned xrx200_pins_exin2[] = {GPIO2};
+static const unsigned xrx200_pins_exin3[] = {GPIO39};
+static const unsigned xrx200_pins_exin4[] = {GPIO10};
+static const unsigned xrx200_pins_exin5[] = {GPIO9};
+
+static const unsigned xrx200_pins_usif_uart_rx[] = {GPIO11};
+static const unsigned xrx200_pins_usif_uart_tx[] = {GPIO12};
+static const unsigned xrx200_pins_usif_uart_rts[] = {GPIO9};
+static const unsigned xrx200_pins_usif_uart_cts[] = {GPIO10};
+static const unsigned xrx200_pins_usif_uart_dtr[] = {GPIO4};
+static const unsigned xrx200_pins_usif_uart_dsr[] = {GPIO6};
+static const unsigned xrx200_pins_usif_uart_dcd[] = {GPIO25};
+static const unsigned xrx200_pins_usif_uart_ri[] = {GPIO27};
+
+static const unsigned xrx200_pins_usif_spi_di[] = {GPIO11};
+static const unsigned xrx200_pins_usif_spi_do[] = {GPIO12};
+static const unsigned xrx200_pins_usif_spi_clk[] = {GPIO38};
+static const unsigned xrx200_pins_usif_spi_cs0[] = {GPIO37};
+static const unsigned xrx200_pins_usif_spi_cs1[] = {GPIO39};
+static const unsigned xrx200_pins_usif_spi_cs2[] = {GPIO14};
+
+static const unsigned xrx200_pins_stp[] = {GPIO4, GPIO5, GPIO6};
+static const unsigned xrx200_pins_nmi[] = {GPIO8};
+static const unsigned xrx200_pins_mdio[] = {GPIO42, GPIO43};
+
+static const unsigned xrx200_pins_dfe_led0[] = {GPIO4};
+static const unsigned xrx200_pins_dfe_led1[] = {GPIO5};
+
+static const unsigned xrx200_pins_gphy0_led0[] = {GPIO5};
+static const unsigned xrx200_pins_gphy0_led1[] = {GPIO7};
+static const unsigned xrx200_pins_gphy0_led2[] = {GPIO2};
+static const unsigned xrx200_pins_gphy1_led0[] = {GPIO44};
+static const unsigned xrx200_pins_gphy1_led1[] = {GPIO45};
+static const unsigned xrx200_pins_gphy1_led2[] = {GPIO47};
+
+static const unsigned xrx200_pins_ebu_a24[] = {GPIO13};
+static const unsigned xrx200_pins_ebu_clk[] = {GPIO21};
+static const unsigned xrx200_pins_ebu_cs1[] = {GPIO23};
+static const unsigned xrx200_pins_ebu_a23[] = {GPIO24};
+static const unsigned xrx200_pins_ebu_wait[] = {GPIO26};
+static const unsigned xrx200_pins_ebu_a25[] = {GPIO31};
+
+static const unsigned xrx200_pins_nand_ale[] = {GPIO13};
+static const unsigned xrx200_pins_nand_cs1[] = {GPIO23};
+static const unsigned xrx200_pins_nand_cle[] = {GPIO24};
+static const unsigned xrx200_pins_nand_rdy[] = {GPIO48};
+static const unsigned xrx200_pins_nand_rd[] = {GPIO49};
+
+static const unsigned xrx200_pins_spi_di[] = {GPIO16};
+static const unsigned xrx200_pins_spi_do[] = {GPIO17};
+static const unsigned xrx200_pins_spi_clk[] = {GPIO18};
+static const unsigned xrx200_pins_spi_cs1[] = {GPIO15};
+static const unsigned xrx200_pins_spi_cs2[] = {GPIO22};
+static const unsigned xrx200_pins_spi_cs3[] = {GPIO13};
+static const unsigned xrx200_pins_spi_cs4[] = {GPIO10};
+static const unsigned xrx200_pins_spi_cs5[] = {GPIO9};
+static const unsigned xrx200_pins_spi_cs6[] = {GPIO11};
+
+static const unsigned xrx200_pins_gpt1[] = {GPIO28};
+static const unsigned xrx200_pins_gpt2[] = {GPIO21};
+static const unsigned xrx200_pins_gpt3[] = {GPIO6};
+
+static const unsigned xrx200_pins_clkout0[] = {GPIO8};
+static const unsigned xrx200_pins_clkout1[] = {GPIO7};
+static const unsigned xrx200_pins_clkout2[] = {GPIO3};
+static const unsigned xrx200_pins_clkout3[] = {GPIO2};
+
+static const unsigned xrx200_pins_pci_gnt1[] = {GPIO28};
+static const unsigned xrx200_pins_pci_gnt2[] = {GPIO23};
+static const unsigned xrx200_pins_pci_gnt3[] = {GPIO19};
+static const unsigned xrx200_pins_pci_gnt4[] = {GPIO38};
+static const unsigned xrx200_pins_pci_req1[] = {GPIO29};
+static const unsigned xrx200_pins_pci_req2[] = {GPIO31};
+static const unsigned xrx200_pins_pci_req3[] = {GPIO3};
+static const unsigned xrx200_pins_pci_req4[] = {GPIO37};
+
+static const struct ltq_pin_group xrx200_grps[] = {
+ GRP_MUX("exin0", EXIN, xrx200_pins_exin0),
+ GRP_MUX("exin1", EXIN, xrx200_pins_exin1),
+ GRP_MUX("exin2", EXIN, xrx200_pins_exin2),
+ GRP_MUX("exin3", EXIN, xrx200_pins_exin3),
+ GRP_MUX("exin4", EXIN, xrx200_pins_exin4),
+ GRP_MUX("exin5", EXIN, xrx200_pins_exin5),
+ GRP_MUX("ebu a23", EBU, xrx200_pins_ebu_a23),
+ GRP_MUX("ebu a24", EBU, xrx200_pins_ebu_a24),
+ GRP_MUX("ebu a25", EBU, xrx200_pins_ebu_a25),
+ GRP_MUX("ebu clk", EBU, xrx200_pins_ebu_clk),
+ GRP_MUX("ebu cs1", EBU, xrx200_pins_ebu_cs1),
+ GRP_MUX("ebu wait", EBU, xrx200_pins_ebu_wait),
+ GRP_MUX("nand ale", EBU, xrx200_pins_nand_ale),
+ GRP_MUX("nand cs1", EBU, xrx200_pins_nand_cs1),
+ GRP_MUX("nand cle", EBU, xrx200_pins_nand_cle),
+ GRP_MUX("nand rdy", EBU, xrx200_pins_nand_rdy),
+ GRP_MUX("nand rd", EBU, xrx200_pins_nand_rd),
+ GRP_MUX("spi_di", SPI, xrx200_pins_spi_di),
+ GRP_MUX("spi_do", SPI, xrx200_pins_spi_do),
+ GRP_MUX("spi_clk", SPI, xrx200_pins_spi_clk),
+ GRP_MUX("spi_cs1", SPI, xrx200_pins_spi_cs1),
+ GRP_MUX("spi_cs2", SPI, xrx200_pins_spi_cs2),
+ GRP_MUX("spi_cs3", SPI, xrx200_pins_spi_cs3),
+ GRP_MUX("spi_cs4", SPI, xrx200_pins_spi_cs4),
+ GRP_MUX("spi_cs5", SPI, xrx200_pins_spi_cs5),
+ GRP_MUX("spi_cs6", SPI, xrx200_pins_spi_cs6),
+ GRP_MUX("usif uart_rx", USIF, xrx200_pins_usif_uart_rx),
+ GRP_MUX("usif uart_rx", USIF, xrx200_pins_usif_uart_tx),
+ GRP_MUX("usif uart_rts", USIF, xrx200_pins_usif_uart_rts),
+ GRP_MUX("usif uart_cts", USIF, xrx200_pins_usif_uart_cts),
+ GRP_MUX("usif uart_dtr", USIF, xrx200_pins_usif_uart_dtr),
+ GRP_MUX("usif uart_dsr", USIF, xrx200_pins_usif_uart_dsr),
+ GRP_MUX("usif uart_dcd", USIF, xrx200_pins_usif_uart_dcd),
+ GRP_MUX("usif uart_ri", USIF, xrx200_pins_usif_uart_ri),
+ GRP_MUX("usif spi_di", USIF, xrx200_pins_usif_spi_di),
+ GRP_MUX("usif spi_do", USIF, xrx200_pins_usif_spi_do),
+ GRP_MUX("usif spi_clk", USIF, xrx200_pins_usif_spi_clk),
+ GRP_MUX("usif spi_cs0", USIF, xrx200_pins_usif_spi_cs0),
+ GRP_MUX("usif spi_cs1", USIF, xrx200_pins_usif_spi_cs1),
+ GRP_MUX("usif spi_cs2", USIF, xrx200_pins_usif_spi_cs2),
+ GRP_MUX("stp", STP, xrx200_pins_stp),
+ GRP_MUX("nmi", NMI, xrx200_pins_nmi),
+ GRP_MUX("gpt1", GPT, xrx200_pins_gpt1),
+ GRP_MUX("gpt2", GPT, xrx200_pins_gpt2),
+ GRP_MUX("gpt3", GPT, xrx200_pins_gpt3),
+ GRP_MUX("clkout0", CGU, xrx200_pins_clkout0),
+ GRP_MUX("clkout1", CGU, xrx200_pins_clkout1),
+ GRP_MUX("clkout2", CGU, xrx200_pins_clkout2),
+ GRP_MUX("clkout3", CGU, xrx200_pins_clkout3),
+ GRP_MUX("gnt1", PCI, xrx200_pins_pci_gnt1),
+ GRP_MUX("gnt2", PCI, xrx200_pins_pci_gnt2),
+ GRP_MUX("gnt3", PCI, xrx200_pins_pci_gnt3),
+ GRP_MUX("gnt4", PCI, xrx200_pins_pci_gnt4),
+ GRP_MUX("req1", PCI, xrx200_pins_pci_req1),
+ GRP_MUX("req2", PCI, xrx200_pins_pci_req2),
+ GRP_MUX("req3", PCI, xrx200_pins_pci_req3),
+ GRP_MUX("req4", PCI, xrx200_pins_pci_req4),
+ GRP_MUX("mdio", MDIO, xrx200_pins_mdio),
+ GRP_MUX("dfe led0", DFE, xrx200_pins_dfe_led0),
+ GRP_MUX("dfe led1", DFE, xrx200_pins_dfe_led1),
+ GRP_MUX("gphy0 led0", GPHY, xrx200_pins_gphy0_led0),
+ GRP_MUX("gphy0 led1", GPHY, xrx200_pins_gphy0_led1),
+ GRP_MUX("gphy0 led2", GPHY, xrx200_pins_gphy0_led2),
+ GRP_MUX("gphy1 led0", GPHY, xrx200_pins_gphy1_led0),
+ GRP_MUX("gphy1 led1", GPHY, xrx200_pins_gphy1_led1),
+ GRP_MUX("gphy1 led2", GPHY, xrx200_pins_gphy1_led2),
+};
+
+static const char * const xrx200_pci_grps[] = {"gnt1", "gnt2",
+ "gnt3", "gnt4",
+ "req1", "req2",
+ "req3", "req4"};
+static const char * const xrx200_spi_grps[] = {"spi_di", "spi_do",
+ "spi_clk", "spi_cs1",
+ "spi_cs2", "spi_cs3",
+ "spi_cs4", "spi_cs5",
+ "spi_cs6"};
+static const char * const xrx200_cgu_grps[] = {"clkout0", "clkout1",
+ "clkout2", "clkout3"};
+static const char * const xrx200_ebu_grps[] = {"ebu a23", "ebu a24",
+ "ebu a25", "ebu cs1",
+ "ebu wait", "ebu clk",
+ "nand ale", "nand cs1",
+ "nand cle", "nand rdy",
+ "nand rd"};
+static const char * const xrx200_exin_grps[] = {"exin0", "exin1", "exin2",
+ "exin3", "exin4", "exin5"};
+static const char * const xrx200_gpt_grps[] = {"gpt1", "gpt2", "gpt3"};
+static const char * const xrx200_usif_grps[] = {"usif uart_rx", "usif uart_tx",
+ "usif uart_rts", "usif uart_cts",
+ "usif uart_dtr", "usif uart_dsr",
+ "usif uart_dcd", "usif uart_ri",
+ "usif spi_di", "usif spi_do",
+ "usif spi_clk", "usif spi_cs0",
+ "usif spi_cs1", "usif spi_cs2"};
+static const char * const xrx200_stp_grps[] = {"stp"};
+static const char * const xrx200_nmi_grps[] = {"nmi"};
+static const char * const xrx200_mdio_grps[] = {"mdio"};
+static const char * const xrx200_dfe_grps[] = {"dfe led0", "dfe led1"};
+static const char * const xrx200_gphy_grps[] = {"gphy0 led0", "gphy0 led1",
+ "gphy0 led2", "gphy1 led0",
+ "gphy1 led1", "gphy1 led2"};
+
+static const struct ltq_pmx_func xrx200_funcs[] = {
+ {"spi", ARRAY_AND_SIZE(xrx200_spi_grps)},
+ {"usif", ARRAY_AND_SIZE(xrx200_usif_grps)},
+ {"cgu", ARRAY_AND_SIZE(xrx200_cgu_grps)},
+ {"exin", ARRAY_AND_SIZE(xrx200_exin_grps)},
+ {"stp", ARRAY_AND_SIZE(xrx200_stp_grps)},
+ {"gpt", ARRAY_AND_SIZE(xrx200_gpt_grps)},
+ {"nmi", ARRAY_AND_SIZE(xrx200_nmi_grps)},
+ {"pci", ARRAY_AND_SIZE(xrx200_pci_grps)},
+ {"ebu", ARRAY_AND_SIZE(xrx200_ebu_grps)},
+ {"mdio", ARRAY_AND_SIZE(xrx200_mdio_grps)},
+ {"dfe", ARRAY_AND_SIZE(xrx200_dfe_grps)},
+ {"gphy", ARRAY_AND_SIZE(xrx200_gphy_grps)},
+};
+
+/* --------- xrx300 related code --------- */
+#define XRX300_MAX_PIN 64
+
+static const struct ltq_mfp_pin xrx300_mfp[] = {
+ /* pin f0 f1 f2 f3 */
+ MFP_XWAY(GPIO0, GPIO, EXIN, EPHY, NONE),
+ MFP_XWAY(GPIO1, GPIO, NONE, EXIN, NONE),
+ MFP_XWAY(GPIO2, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO3, GPIO, CGU, NONE, NONE),
+ MFP_XWAY(GPIO4, GPIO, STP, DFE, NONE),
+ MFP_XWAY(GPIO5, GPIO, STP, EPHY, DFE),
+ MFP_XWAY(GPIO6, GPIO, STP, NONE, NONE),
+ MFP_XWAY(GPIO7, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO8, GPIO, CGU, GPHY, EPHY),
+ MFP_XWAY(GPIO9, GPIO, WIFI, NONE, EXIN),
+ MFP_XWAY(GPIO10, GPIO, USIF, SPI, EXIN),
+ MFP_XWAY(GPIO11, GPIO, USIF, WIFI, SPI),
+ MFP_XWAY(GPIO12, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO13, GPIO, EBU, NONE, NONE),
+ MFP_XWAY(GPIO14, GPIO, CGU, USIF, EPHY),
+ MFP_XWAY(GPIO15, GPIO, SPI, NONE, MCD),
+ MFP_XWAY(GPIO16, GPIO, SPI, EXIN, NONE),
+ MFP_XWAY(GPIO17, GPIO, SPI, NONE, NONE),
+ MFP_XWAY(GPIO18, GPIO, SPI, NONE, NONE),
+ MFP_XWAY(GPIO19, GPIO, USIF, NONE, EPHY),
+ MFP_XWAY(GPIO20, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO21, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO22, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO23, GPIO, EBU, NONE, NONE),
+ MFP_XWAY(GPIO24, GPIO, EBU, NONE, NONE),
+ MFP_XWAY(GPIO25, GPIO, TDM, NONE, NONE),
+ MFP_XWAY(GPIO26, GPIO, TDM, NONE, NONE),
+ MFP_XWAY(GPIO27, GPIO, TDM, NONE, NONE),
+ MFP_XWAY(GPIO28, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO29, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO30, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO31, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO32, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO33, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO34, GPIO, NONE, SSI, NONE),
+ MFP_XWAY(GPIO35, GPIO, NONE, SSI, NONE),
+ MFP_XWAY(GPIO36, GPIO, NONE, SSI, NONE),
+ MFP_XWAY(GPIO37, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO38, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO39, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO40, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO41, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO42, GPIO, MDIO, NONE, NONE),
+ MFP_XWAY(GPIO43, GPIO, MDIO, NONE, NONE),
+ MFP_XWAY(GPIO44, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO45, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO46, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO47, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO48, GPIO, EBU, NONE, NONE),
+ MFP_XWAY(GPIO49, GPIO, EBU, NONE, NONE),
+ MFP_XWAY(GPIO50, GPIO, EBU, NONE, NONE),
+ MFP_XWAY(GPIO51, GPIO, EBU, NONE, NONE),
+ MFP_XWAY(GPIO52, GPIO, EBU, NONE, NONE),
+ MFP_XWAY(GPIO53, GPIO, EBU, NONE, NONE),
+ MFP_XWAY(GPIO54, GPIO, EBU, NONE, NONE),
+ MFP_XWAY(GPIO55, GPIO, EBU, NONE, NONE),
+ MFP_XWAY(GPIO56, GPIO, EBU, NONE, NONE),
+ MFP_XWAY(GPIO57, GPIO, EBU, NONE, NONE),
+ MFP_XWAY(GPIO58, GPIO, EBU, TDM, NONE),
+ MFP_XWAY(GPIO59, GPIO, EBU, NONE, NONE),
+ MFP_XWAY(GPIO60, GPIO, EBU, NONE, NONE),
+ MFP_XWAY(GPIO61, GPIO, EBU, NONE, NONE),
+ MFP_XWAY(GPIO62, NONE, NONE, NONE, NONE),
+ MFP_XWAY(GPIO63, NONE, NONE, NONE, NONE),
+};
+
+static const unsigned xrx300_exin_pin_map[] = {GPIO0, GPIO1, GPIO16, GPIO10, GPIO9};
+
+static const unsigned xrx300_pins_exin0[] = {GPIO0};
+static const unsigned xrx300_pins_exin1[] = {GPIO1};
+static const unsigned xrx300_pins_exin2[] = {GPIO16};
+/* EXIN3 is not available on xrX300 */
+static const unsigned xrx300_pins_exin4[] = {GPIO10};
+static const unsigned xrx300_pins_exin5[] = {GPIO9};
+
+static const unsigned xrx300_pins_usif_uart_rx[] = {GPIO11};
+static const unsigned xrx300_pins_usif_uart_tx[] = {GPIO10};
+
+static const unsigned xrx300_pins_usif_spi_di[] = {GPIO11};
+static const unsigned xrx300_pins_usif_spi_do[] = {GPIO10};
+static const unsigned xrx300_pins_usif_spi_clk[] = {GPIO19};
+static const unsigned xrx300_pins_usif_spi_cs0[] = {GPIO14};
+
+static const unsigned xrx300_pins_stp[] = {GPIO4, GPIO5, GPIO6};
+static const unsigned xrx300_pins_mdio[] = {GPIO42, GPIO43};
+
+static const unsigned xrx300_pins_dfe_led0[] = {GPIO4};
+static const unsigned xrx300_pins_dfe_led1[] = {GPIO5};
+
+static const unsigned xrx300_pins_ephy0_led0[] = {GPIO5};
+static const unsigned xrx300_pins_ephy0_led1[] = {GPIO8};
+static const unsigned xrx300_pins_ephy1_led0[] = {GPIO14};
+static const unsigned xrx300_pins_ephy1_led1[] = {GPIO19};
+
+static const unsigned xrx300_pins_nand_ale[] = {GPIO13};
+static const unsigned xrx300_pins_nand_cs1[] = {GPIO23};
+static const unsigned xrx300_pins_nand_cle[] = {GPIO24};
+static const unsigned xrx300_pins_nand_rdy[] = {GPIO48};
+static const unsigned xrx300_pins_nand_rd[] = {GPIO49};
+static const unsigned xrx300_pins_nand_d1[] = {GPIO50};
+static const unsigned xrx300_pins_nand_d0[] = {GPIO51};
+static const unsigned xrx300_pins_nand_d2[] = {GPIO52};
+static const unsigned xrx300_pins_nand_d7[] = {GPIO53};
+static const unsigned xrx300_pins_nand_d6[] = {GPIO54};
+static const unsigned xrx300_pins_nand_d5[] = {GPIO55};
+static const unsigned xrx300_pins_nand_d4[] = {GPIO56};
+static const unsigned xrx300_pins_nand_d3[] = {GPIO57};
+static const unsigned xrx300_pins_nand_cs0[] = {GPIO58};
+static const unsigned xrx300_pins_nand_wr[] = {GPIO59};
+static const unsigned xrx300_pins_nand_wp[] = {GPIO60};
+static const unsigned xrx300_pins_nand_se[] = {GPIO61};
+
+static const unsigned xrx300_pins_spi_di[] = {GPIO16};
+static const unsigned xrx300_pins_spi_do[] = {GPIO17};
+static const unsigned xrx300_pins_spi_clk[] = {GPIO18};
+static const unsigned xrx300_pins_spi_cs1[] = {GPIO15};
+/* SPI_CS2 is not available on xrX300 */
+/* SPI_CS3 is not available on xrX300 */
+static const unsigned xrx300_pins_spi_cs4[] = {GPIO10};
+/* SPI_CS5 is not available on xrX300 */
+static const unsigned xrx300_pins_spi_cs6[] = {GPIO11};
+
+/* CLKOUT0 is not available on xrX300 */
+/* CLKOUT1 is not available on xrX300 */
+static const unsigned xrx300_pins_clkout2[] = {GPIO3};
+
+static const struct ltq_pin_group xrx300_grps[] = {
+ GRP_MUX("exin0", EXIN, xrx300_pins_exin0),
+ GRP_MUX("exin1", EXIN, xrx300_pins_exin1),
+ GRP_MUX("exin2", EXIN, xrx300_pins_exin2),
+ GRP_MUX("exin4", EXIN, xrx300_pins_exin4),
+ GRP_MUX("exin5", EXIN, xrx300_pins_exin5),
+ GRP_MUX("nand ale", EBU, xrx300_pins_nand_ale),
+ GRP_MUX("nand cs1", EBU, xrx300_pins_nand_cs1),
+ GRP_MUX("nand cle", EBU, xrx300_pins_nand_cle),
+ GRP_MUX("nand rdy", EBU, xrx300_pins_nand_rdy),
+ GRP_MUX("nand rd", EBU, xrx300_pins_nand_rd),
+ GRP_MUX("nand d1", EBU, xrx300_pins_nand_d1),
+ GRP_MUX("nand d0", EBU, xrx300_pins_nand_d0),
+ GRP_MUX("nand d2", EBU, xrx300_pins_nand_d2),
+ GRP_MUX("nand d7", EBU, xrx300_pins_nand_d7),
+ GRP_MUX("nand d6", EBU, xrx300_pins_nand_d6),
+ GRP_MUX("nand d5", EBU, xrx300_pins_nand_d5),
+ GRP_MUX("nand d4", EBU, xrx300_pins_nand_d4),
+ GRP_MUX("nand d3", EBU, xrx300_pins_nand_d3),
+ GRP_MUX("nand cs0", EBU, xrx300_pins_nand_cs0),
+ GRP_MUX("nand wr", EBU, xrx300_pins_nand_wr),
+ GRP_MUX("nand wp", EBU, xrx300_pins_nand_wp),
+ GRP_MUX("nand se", EBU, xrx300_pins_nand_se),
+ GRP_MUX("spi_di", SPI, xrx300_pins_spi_di),
+ GRP_MUX("spi_do", SPI, xrx300_pins_spi_do),
+ GRP_MUX("spi_clk", SPI, xrx300_pins_spi_clk),
+ GRP_MUX("spi_cs1", SPI, xrx300_pins_spi_cs1),
+ GRP_MUX("spi_cs4", SPI, xrx300_pins_spi_cs4),
+ GRP_MUX("spi_cs6", SPI, xrx300_pins_spi_cs6),
+ GRP_MUX("usif uart_rx", USIF, xrx300_pins_usif_uart_rx),
+ GRP_MUX("usif uart_tx", USIF, xrx300_pins_usif_uart_tx),
+ GRP_MUX("usif spi_di", USIF, xrx300_pins_usif_spi_di),
+ GRP_MUX("usif spi_do", USIF, xrx300_pins_usif_spi_do),
+ GRP_MUX("usif spi_clk", USIF, xrx300_pins_usif_spi_clk),
+ GRP_MUX("usif spi_cs0", USIF, xrx300_pins_usif_spi_cs0),
+ GRP_MUX("stp", STP, xrx300_pins_stp),
+ GRP_MUX("clkout2", CGU, xrx300_pins_clkout2),
+ GRP_MUX("mdio", MDIO, xrx300_pins_mdio),
+ GRP_MUX("dfe led0", DFE, xrx300_pins_dfe_led0),
+ GRP_MUX("dfe led1", DFE, xrx300_pins_dfe_led1),
+ GRP_MUX("ephy0 led0", GPHY, xrx300_pins_ephy0_led0),
+ GRP_MUX("ephy0 led1", GPHY, xrx300_pins_ephy0_led1),
+ GRP_MUX("ephy1 led0", GPHY, xrx300_pins_ephy1_led0),
+ GRP_MUX("ephy1 led1", GPHY, xrx300_pins_ephy1_led1),
+};
+
+static const char * const xrx300_spi_grps[] = {"spi_di", "spi_do",
+ "spi_clk", "spi_cs1",
+ "spi_cs4", "spi_cs6"};
+static const char * const xrx300_cgu_grps[] = {"clkout2"};
+static const char * const xrx300_ebu_grps[] = {"nand ale", "nand cs1",
+ "nand cle", "nand rdy",
+ "nand rd", "nand d1",
+ "nand d0", "nand d2",
+ "nand d7", "nand d6",
+ "nand d5", "nand d4",
+ "nand d3", "nand cs0",
+ "nand wr", "nand wp",
+ "nand se"};
+static const char * const xrx300_exin_grps[] = {"exin0", "exin1", "exin2",
+ "exin4", "exin5"};
+static const char * const xrx300_usif_grps[] = {"usif uart_rx", "usif uart_tx",
+ "usif spi_di", "usif spi_do",
+ "usif spi_clk", "usif spi_cs0"};
+static const char * const xrx300_stp_grps[] = {"stp"};
+static const char * const xrx300_mdio_grps[] = {"mdio"};
+static const char * const xrx300_dfe_grps[] = {"dfe led0", "dfe led1"};
+static const char * const xrx300_gphy_grps[] = {"ephy0 led0", "ephy0 led1",
+ "ephy1 led0", "ephy1 led1"};
+
+static const struct ltq_pmx_func xrx300_funcs[] = {
+ {"spi", ARRAY_AND_SIZE(xrx300_spi_grps)},
+ {"usif", ARRAY_AND_SIZE(xrx300_usif_grps)},
+ {"cgu", ARRAY_AND_SIZE(xrx300_cgu_grps)},
+ {"exin", ARRAY_AND_SIZE(xrx300_exin_grps)},
+ {"stp", ARRAY_AND_SIZE(xrx300_stp_grps)},
+ {"ebu", ARRAY_AND_SIZE(xrx300_ebu_grps)},
+ {"mdio", ARRAY_AND_SIZE(xrx300_mdio_grps)},
+ {"dfe", ARRAY_AND_SIZE(xrx300_dfe_grps)},
+ {"ephy", ARRAY_AND_SIZE(xrx300_gphy_grps)},
+};
+
/* --------- pinconf related code --------- */
static int xway_pinconf_get(struct pinctrl_dev *pctldev,
unsigned pin,
@@ -676,6 +1563,10 @@ static int xway_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, int val)
{
struct ltq_pinmux_info *info = dev_get_drvdata(chip->dev);
+ if (PORT(pin) == PORT3)
+ gpio_setbit(info->membase[0], GPIO3_OD, PORT_PIN(pin));
+ else
+ gpio_setbit(info->membase[0], GPIO_OD(pin), PORT_PIN(pin));
gpio_setbit(info->membase[0], GPIO_DIR(pin), PORT_PIN(pin));
xway_gpio_set(chip, pin, val);
@@ -695,10 +1586,7 @@ static struct gpio_chip xway_chip = {
/* --------- register the pinctrl layer --------- */
-static const unsigned xway_exin_pin_map[] = {GPIO0, GPIO1, GPIO2, GPIO39, GPIO46, GPIO9};
-static const unsigned ase_exin_pins_map[] = {GPIO6, GPIO29, GPIO0};
-
-static struct pinctrl_xway_soc {
+struct pinctrl_xway_soc {
int pin_count;
const struct ltq_mfp_pin *mfp;
const struct ltq_pin_group *grps;
@@ -707,22 +1595,54 @@ static struct pinctrl_xway_soc {
unsigned int num_funcs;
const unsigned *exin;
unsigned int num_exin;
-} soc_cfg[] = {
- /* legacy xway */
- {XWAY_MAX_PIN, xway_mfp,
- xway_grps, ARRAY_SIZE(xway_grps),
- danube_funcs, ARRAY_SIZE(danube_funcs),
- xway_exin_pin_map, 3},
- /* xway xr9 series */
- {XR9_MAX_PIN, xway_mfp,
- xway_grps, ARRAY_SIZE(xway_grps),
- xrx_funcs, ARRAY_SIZE(xrx_funcs),
- xway_exin_pin_map, 6},
- /* xway ase series */
- {XWAY_MAX_PIN, ase_mfp,
- ase_grps, ARRAY_SIZE(ase_grps),
- ase_funcs, ARRAY_SIZE(ase_funcs),
- ase_exin_pins_map, 3},
+};
+
+/* xway xr9 series (DEPRECATED: Use XWAY xRX100/xRX200 Family) */
+static struct pinctrl_xway_soc xr9_pinctrl = {
+ XR9_MAX_PIN, xway_mfp,
+ xway_grps, ARRAY_SIZE(xway_grps),
+ xrx_funcs, ARRAY_SIZE(xrx_funcs),
+ xway_exin_pin_map, 6
+};
+
+/* XWAY AMAZON Family */
+static struct pinctrl_xway_soc ase_pinctrl = {
+ ASE_MAX_PIN, ase_mfp,
+ ase_grps, ARRAY_SIZE(ase_grps),
+ ase_funcs, ARRAY_SIZE(ase_funcs),
+ ase_exin_pin_map, 3
+};
+
+/* XWAY DANUBE Family */
+static struct pinctrl_xway_soc danube_pinctrl = {
+ DANUBE_MAX_PIN, danube_mfp,
+ danube_grps, ARRAY_SIZE(danube_grps),
+ danube_funcs, ARRAY_SIZE(danube_funcs),
+ danube_exin_pin_map, 3
+};
+
+/* XWAY xRX100 Family */
+static struct pinctrl_xway_soc xrx100_pinctrl = {
+ XRX100_MAX_PIN, xrx100_mfp,
+ xrx100_grps, ARRAY_SIZE(xrx100_grps),
+ xrx100_funcs, ARRAY_SIZE(xrx100_funcs),
+ xrx100_exin_pin_map, 6
+};
+
+/* XWAY xRX200 Family */
+static struct pinctrl_xway_soc xrx200_pinctrl = {
+ XRX200_MAX_PIN, xrx200_mfp,
+ xrx200_grps, ARRAY_SIZE(xrx200_grps),
+ xrx200_funcs, ARRAY_SIZE(xrx200_funcs),
+ xrx200_exin_pin_map, 6
+};
+
+/* XWAY xRX300 Family */
+static struct pinctrl_xway_soc xrx300_pinctrl = {
+ XRX300_MAX_PIN, xrx300_mfp,
+ xrx300_grps, ARRAY_SIZE(xrx300_grps),
+ xrx300_funcs, ARRAY_SIZE(xrx300_funcs),
+ xrx300_exin_pin_map, 5
};
static struct pinctrl_gpio_range xway_gpio_range = {
@@ -731,9 +1651,14 @@ static struct pinctrl_gpio_range xway_gpio_range = {
};
static const struct of_device_id xway_match[] = {
- { .compatible = "lantiq,pinctrl-xway", .data = &soc_cfg[0]},
- { .compatible = "lantiq,pinctrl-xr9", .data = &soc_cfg[1]},
- { .compatible = "lantiq,pinctrl-ase", .data = &soc_cfg[2]},
+ { .compatible = "lantiq,pinctrl-xway", .data = &danube_pinctrl}, /*DEPRECATED*/
+ { .compatible = "lantiq,pinctrl-xr9", .data = &xr9_pinctrl}, /*DEPRECATED*/
+ { .compatible = "lantiq,pinctrl-ase", .data = &ase_pinctrl}, /*DEPRECATED*/
+ { .compatible = "lantiq,ase-pinctrl", .data = &ase_pinctrl},
+ { .compatible = "lantiq,danube-pinctrl", .data = &danube_pinctrl},
+ { .compatible = "lantiq,xrx100-pinctrl", .data = &xrx100_pinctrl},
+ { .compatible = "lantiq,xrx200-pinctrl", .data = &xrx200_pinctrl},
+ { .compatible = "lantiq,xrx300-pinctrl", .data = &xrx300_pinctrl},
{},
};
MODULE_DEVICE_TABLE(of, xway_match);
@@ -755,7 +1680,7 @@ static int pinmux_xway_probe(struct platform_device *pdev)
if (match)
xway_soc = (const struct pinctrl_xway_soc *) match->data;
else
- xway_soc = &soc_cfg[0];
+ xway_soc = &danube_pinctrl;
/* find out how many pads we have */
xway_chip.ngpio = xway_soc->pin_count;
diff --git a/drivers/pinctrl/pxa/Kconfig b/drivers/pinctrl/pxa/Kconfig
new file mode 100644
index 000000000000..990667ff772c
--- /dev/null
+++ b/drivers/pinctrl/pxa/Kconfig
@@ -0,0 +1,17 @@
+if (ARCH_PXA || COMPILE_TEST)
+
+config PINCTRL_PXA
+ bool
+ select PINMUX
+ select PINCONF
+ select GENERIC_PINCONF
+
+config PINCTRL_PXA27X
+ tristate "Marvell PXA27x pin controller driver"
+ select PINCTRL_PXA
+ default y if PXA27x
+ help
+ This is the pinctrl, pinmux, pinconf driver for the Marvell
+ PXA2xx block found in the pxa25x and pxa27x platforms.
+
+endif
diff --git a/drivers/pinctrl/pxa/Makefile b/drivers/pinctrl/pxa/Makefile
new file mode 100644
index 000000000000..f1d56af2bfc0
--- /dev/null
+++ b/drivers/pinctrl/pxa/Makefile
@@ -0,0 +1,2 @@
+# Marvell PXA pin control drivers
+obj-$(CONFIG_PINCTRL_PXA27X) += pinctrl-pxa2xx.o pinctrl-pxa27x.o
diff --git a/drivers/pinctrl/pxa/pinctrl-pxa27x.c b/drivers/pinctrl/pxa/pinctrl-pxa27x.c
new file mode 100644
index 000000000000..2e2c3709ef05
--- /dev/null
+++ b/drivers/pinctrl/pxa/pinctrl-pxa27x.c
@@ -0,0 +1,566 @@
+/*
+ * Marvell PXA27x family pin control
+ *
+ * Copyright (C) 2015 Robert Jarzmik
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/pinctrl.h>
+
+#include "pinctrl-pxa2xx.h"
+
+static const struct pxa_desc_pin pxa27x_pins[] = {
+ PXA_GPIO_ONLY_PIN(PXA_PINCTRL_PIN(0)),
+ PXA_GPIO_ONLY_PIN(PXA_PINCTRL_PIN(1)),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(9),
+ PXA_FUNCTION(0, 3, "FFCTS"),
+ PXA_FUNCTION(1, 1, "HZ_CLK"),
+ PXA_FUNCTION(1, 3, "CHOUT<0>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(10),
+ PXA_FUNCTION(0, 1, "FFDCD"),
+ PXA_FUNCTION(0, 3, "USB_P3_5"),
+ PXA_FUNCTION(1, 1, "HZ_CLK"),
+ PXA_FUNCTION(1, 3, "CHOUT<1>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(11),
+ PXA_FUNCTION(0, 1, "EXT_SYNC<0>"),
+ PXA_FUNCTION(0, 2, "SSPRXD2"),
+ PXA_FUNCTION(0, 3, "USB_P3_1"),
+ PXA_FUNCTION(1, 1, "CHOUT<0>"),
+ PXA_FUNCTION(1, 1, "PWM_OUT<2>"),
+ PXA_FUNCTION(1, 3, "48_MHz")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(12),
+ PXA_FUNCTION(0, 1, "EXT_SYNC<1>"),
+ PXA_FUNCTION(0, 2, "CIF_DD<7>"),
+ PXA_FUNCTION(1, 1, "CHOUT<1>"),
+ PXA_FUNCTION(1, 1, "PWM_OUT<3>"),
+ PXA_FUNCTION(1, 3, "48_MHz")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(13),
+ PXA_FUNCTION(0, 1, "CLK_EXT"),
+ PXA_FUNCTION(0, 2, "KP_DKIN<7>"),
+ PXA_FUNCTION(0, 3, "KP_MKIN<7>"),
+ PXA_FUNCTION(1, 1, "SSPTXD2")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(14),
+ PXA_FUNCTION(0, 1, "L_VSYNC"),
+ PXA_FUNCTION(0, 2, "SSPSFRM2"),
+ PXA_FUNCTION(1, 1, "SSPSFRM2"),
+ PXA_FUNCTION(1, 3, "UCLK")),
+ PXA_GPIO_ONLY_PIN(PXA_PINCTRL_PIN(15)),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(16),
+ PXA_FUNCTION(0, 1, "KP_MKIN<5>"),
+ PXA_FUNCTION(1, 2, "PWM_OUT<0>"),
+ PXA_FUNCTION(1, 3, "FFTXD")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(17),
+ PXA_FUNCTION(0, 1, "KP_MKIN<6>"),
+ PXA_FUNCTION(0, 2, "CIF_DD<6>"),
+ PXA_FUNCTION(1, 2, "PWM_OUT<1>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(18),
+ PXA_FUNCTION(0, 1, "RDY")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(19),
+ PXA_FUNCTION(0, 1, "SSPSCLK2"),
+ PXA_FUNCTION(0, 3, "FFRXD"),
+ PXA_FUNCTION(1, 1, "SSPSCLK2"),
+ PXA_FUNCTION(1, 2, "L_CS"),
+ PXA_FUNCTION(1, 3, "nURST")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(20),
+ PXA_FUNCTION(0, 1, "DREQ<0>"),
+ PXA_FUNCTION(0, 2, "MBREQ"),
+ PXA_FUNCTION(1, 1, "nSDCS<2>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(21),
+ PXA_FUNCTION(1, 1, "nSDCS<3>"),
+ PXA_FUNCTION(1, 2, "DVAL<0>"),
+ PXA_FUNCTION(1, 3, "MBGNT")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(22),
+ PXA_FUNCTION(0, 1, "SSPEXTCLK2"),
+ PXA_FUNCTION(0, 2, "SSPSCLKEN2"),
+ PXA_FUNCTION(0, 3, "SSPSCLK2"),
+ PXA_FUNCTION(1, 1, "KP_MKOUT<7>"),
+ PXA_FUNCTION(1, 2, "SSPSYSCLK2"),
+ PXA_FUNCTION(1, 3, "SSPSCLK2")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(23),
+ PXA_FUNCTION(0, 2, "SSPSCLK"),
+ PXA_FUNCTION(1, 1, "CIF_MCLK"),
+ PXA_FUNCTION(1, 1, "SSPSCLK")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(24),
+ PXA_FUNCTION(0, 1, "CIF_FV"),
+ PXA_FUNCTION(0, 2, "SSPSFRM"),
+ PXA_FUNCTION(1, 1, "CIF_FV"),
+ PXA_FUNCTION(1, 2, "SSPSFRM")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(25),
+ PXA_FUNCTION(0, 1, "CIF_LV"),
+ PXA_FUNCTION(1, 1, "CIF_LV"),
+ PXA_FUNCTION(1, 2, "SSPTXD")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(26),
+ PXA_FUNCTION(0, 1, "SSPRXD"),
+ PXA_FUNCTION(0, 2, "CIF_PCLK"),
+ PXA_FUNCTION(0, 3, "FFCTS")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(27),
+ PXA_FUNCTION(0, 1, "SSPEXTCLK"),
+ PXA_FUNCTION(0, 2, "SSPSCLKEN"),
+ PXA_FUNCTION(0, 3, "CIF_DD<0>"),
+ PXA_FUNCTION(1, 1, "SSPSYSCLK"),
+ PXA_FUNCTION(1, 3, "FFRTS")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(28),
+ PXA_FUNCTION(0, 1, "AC97_BITCLK"),
+ PXA_FUNCTION(0, 2, "I2S_BITCLK"),
+ PXA_FUNCTION(0, 3, "SSPSFRM"),
+ PXA_FUNCTION(1, 1, "I2S_BITCLK"),
+ PXA_FUNCTION(1, 3, "SSPSFRM")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(29),
+ PXA_FUNCTION(0, 1, "AC97_SDATA_IN_0"),
+ PXA_FUNCTION(0, 2, "I2S_SDATA_IN"),
+ PXA_FUNCTION(0, 3, "SSPSCLK"),
+ PXA_FUNCTION(1, 1, "SSPRXD2"),
+ PXA_FUNCTION(1, 3, "SSPSCLK")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(30),
+ PXA_FUNCTION(1, 1, "I2S_SDATA_OUT"),
+ PXA_FUNCTION(1, 2, "AC97_SDATA_OUT"),
+ PXA_FUNCTION(1, 3, "USB_P3_2")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(31),
+ PXA_FUNCTION(1, 1, "I2S_SYNC"),
+ PXA_FUNCTION(1, 2, "AC97_SYNC"),
+ PXA_FUNCTION(1, 3, "USB_P3_6")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(32),
+ PXA_FUNCTION(1, 1, "MSSCLK"),
+ PXA_FUNCTION(1, 2, "MMCLK")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(33),
+ PXA_FUNCTION(0, 1, "FFRXD"),
+ PXA_FUNCTION(0, 2, "FFDSR"),
+ PXA_FUNCTION(1, 1, "DVAL<1>"),
+ PXA_FUNCTION(1, 2, "nCS<5>"),
+ PXA_FUNCTION(1, 3, "MBGNT")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(34),
+ PXA_FUNCTION(0, 1, "FFRXD"),
+ PXA_FUNCTION(0, 2, "KP_MKIN<3>"),
+ PXA_FUNCTION(0, 3, "SSPSCLK3"),
+ PXA_FUNCTION(1, 1, "USB_P2_2"),
+ PXA_FUNCTION(1, 3, "SSPSCLK3")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(35),
+ PXA_FUNCTION(0, 1, "FFCTS"),
+ PXA_FUNCTION(0, 2, "USB_P2_1"),
+ PXA_FUNCTION(0, 3, "SSPSFRM3"),
+ PXA_FUNCTION(1, 2, "KP_MKOUT<6>"),
+ PXA_FUNCTION(1, 3, "SSPTXD3")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(36),
+ PXA_FUNCTION(0, 1, "FFDCD"),
+ PXA_FUNCTION(0, 2, "SSPSCLK2"),
+ PXA_FUNCTION(0, 3, "KP_MKIN<7>"),
+ PXA_FUNCTION(1, 1, "USB_P2_4"),
+ PXA_FUNCTION(1, 2, "SSPSCLK2")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(37),
+ PXA_FUNCTION(0, 1, "FFDSR"),
+ PXA_FUNCTION(0, 2, "SSPSFRM2"),
+ PXA_FUNCTION(0, 3, "KP_MKIN<3>"),
+ PXA_FUNCTION(1, 1, "USB_P2_8"),
+ PXA_FUNCTION(1, 2, "SSPSFRM2"),
+ PXA_FUNCTION(1, 3, "FFTXD")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(38),
+ PXA_FUNCTION(0, 1, "FFRI"),
+ PXA_FUNCTION(0, 2, "KP_MKIN<4>"),
+ PXA_FUNCTION(0, 3, "USB_P2_3"),
+ PXA_FUNCTION(1, 1, "SSPTXD3"),
+ PXA_FUNCTION(1, 2, "SSPTXD2"),
+ PXA_FUNCTION(1, 3, "PWM_OUT<0>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(39),
+ PXA_FUNCTION(0, 1, "KP_MKIN<4>"),
+ PXA_FUNCTION(0, 3, "SSPSFRM3"),
+ PXA_FUNCTION(1, 1, "USB_P2_6"),
+ PXA_FUNCTION(1, 2, "FFTXD"),
+ PXA_FUNCTION(1, 3, "SSPSFRM3")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(40),
+ PXA_FUNCTION(0, 1, "SSPRXD2"),
+ PXA_FUNCTION(0, 3, "USB_P2_5"),
+ PXA_FUNCTION(1, 1, "KP_MKOUT<6>"),
+ PXA_FUNCTION(1, 2, "FFDTR"),
+ PXA_FUNCTION(1, 3, "SSPSCLK3")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(41),
+ PXA_FUNCTION(0, 1, "FFRXD"),
+ PXA_FUNCTION(0, 2, "USB_P2_7"),
+ PXA_FUNCTION(0, 3, "SSPRXD3"),
+ PXA_FUNCTION(1, 1, "KP_MKOUT<7>"),
+ PXA_FUNCTION(1, 2, "FFRTS")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(42),
+ PXA_FUNCTION(0, 1, "BTRXD"),
+ PXA_FUNCTION(0, 2, "ICP_RXD"),
+ PXA_FUNCTION(1, 3, "CIF_MCLK")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(43),
+ PXA_FUNCTION(0, 3, "CIF_FV"),
+ PXA_FUNCTION(1, 1, "ICP_TXD"),
+ PXA_FUNCTION(1, 2, "BTTXD"),
+ PXA_FUNCTION(1, 3, "CIF_FV")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(44),
+ PXA_FUNCTION(0, 1, "BTCTS"),
+ PXA_FUNCTION(0, 3, "CIF_LV"),
+ PXA_FUNCTION(1, 3, "CIF_LV")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(45),
+ PXA_FUNCTION(0, 3, "CIF_PCLK"),
+ PXA_FUNCTION(1, 1, "AC97_SYSCLK"),
+ PXA_FUNCTION(1, 2, "BTRTS"),
+ PXA_FUNCTION(1, 3, "SSPSYSCLK3")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(46),
+ PXA_FUNCTION(0, 1, "ICP_RXD"),
+ PXA_FUNCTION(0, 2, "STD_RXD"),
+ PXA_FUNCTION(1, 2, "PWM_OUT<2>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(47),
+ PXA_FUNCTION(0, 1, "CIF_DD<0>"),
+ PXA_FUNCTION(1, 1, "STD_TXD"),
+ PXA_FUNCTION(1, 2, "ICP_TXD"),
+ PXA_FUNCTION(1, 3, "PWM_OUT<3>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(48),
+ PXA_FUNCTION(0, 1, "CIF_DD<5>"),
+ PXA_FUNCTION(1, 1, "BB_OB_DAT<1>"),
+ PXA_FUNCTION(1, 2, "nPOE")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(49),
+ PXA_FUNCTION(1, 2, "nPWE")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(50),
+ PXA_FUNCTION(0, 1, "CIF_DD<3>"),
+ PXA_FUNCTION(0, 3, "SSPSCLK2"),
+ PXA_FUNCTION(1, 1, "BB_OB_DAT<2>"),
+ PXA_FUNCTION(1, 2, "nPIOR"),
+ PXA_FUNCTION(1, 3, "SSPSCLK2")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(51),
+ PXA_FUNCTION(0, 1, "CIF_DD<2>"),
+ PXA_FUNCTION(1, 1, "BB_OB_DAT<3>"),
+ PXA_FUNCTION(1, 2, "nPIOW")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(52),
+ PXA_FUNCTION(0, 1, "CIF_DD<4>"),
+ PXA_FUNCTION(0, 2, "SSPSCLK3"),
+ PXA_FUNCTION(1, 1, "BB_OB_CLK"),
+ PXA_FUNCTION(1, 2, "SSPSCLK3")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(53),
+ PXA_FUNCTION(0, 1, "FFRXD"),
+ PXA_FUNCTION(0, 2, "USB_P2_3"),
+ PXA_FUNCTION(1, 1, "BB_OB_STB"),
+ PXA_FUNCTION(1, 2, "CIF_MCLK"),
+ PXA_FUNCTION(1, 3, "SSPSYSCLK")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(54),
+ PXA_FUNCTION(0, 2, "BB_OB_WAIT"),
+ PXA_FUNCTION(0, 3, "CIF_PCLK"),
+ PXA_FUNCTION(1, 2, "nPCE<2>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(55),
+ PXA_FUNCTION(0, 1, "CIF_DD<1>"),
+ PXA_FUNCTION(0, 2, "BB_IB_DAT<1>"),
+ PXA_FUNCTION(1, 2, "nPREG")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(56),
+ PXA_FUNCTION(0, 1, "nPWAIT"),
+ PXA_FUNCTION(0, 2, "BB_IB_DAT<2>"),
+ PXA_FUNCTION(1, 1, "USB_P3_4")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(57),
+ PXA_FUNCTION(0, 1, "nIOS16"),
+ PXA_FUNCTION(0, 2, "BB_IB_DAT<3>"),
+ PXA_FUNCTION(1, 3, "SSPTXD")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(58),
+ PXA_FUNCTION(0, 2, "LDD<0>"),
+ PXA_FUNCTION(1, 2, "LDD<0>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(59),
+ PXA_FUNCTION(0, 2, "LDD<1>"),
+ PXA_FUNCTION(1, 2, "LDD<1>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(60),
+ PXA_FUNCTION(0, 2, "LDD<2>"),
+ PXA_FUNCTION(1, 2, "LDD<2>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(61),
+ PXA_FUNCTION(0, 2, "LDD<3>"),
+ PXA_FUNCTION(1, 2, "LDD<3>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(62),
+ PXA_FUNCTION(0, 2, "LDD<4>"),
+ PXA_FUNCTION(1, 2, "LDD<4>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(63),
+ PXA_FUNCTION(0, 2, "LDD<5>"),
+ PXA_FUNCTION(1, 2, "LDD<5>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(64),
+ PXA_FUNCTION(0, 2, "LDD<6>"),
+ PXA_FUNCTION(1, 2, "LDD<6>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(65),
+ PXA_FUNCTION(0, 2, "LDD<7>"),
+ PXA_FUNCTION(1, 2, "LDD<7>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(66),
+ PXA_FUNCTION(0, 2, "LDD<8>"),
+ PXA_FUNCTION(1, 2, "LDD<8>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(67),
+ PXA_FUNCTION(0, 2, "LDD<9>"),
+ PXA_FUNCTION(1, 2, "LDD<9>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(68),
+ PXA_FUNCTION(0, 2, "LDD<10>"),
+ PXA_FUNCTION(1, 2, "LDD<10>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(69),
+ PXA_FUNCTION(0, 2, "LDD<11>"),
+ PXA_FUNCTION(1, 2, "LDD<11>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(70),
+ PXA_FUNCTION(0, 2, "LDD<12>"),
+ PXA_FUNCTION(1, 2, "LDD<12>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(71),
+ PXA_FUNCTION(0, 2, "LDD<13>"),
+ PXA_FUNCTION(1, 2, "LDD<13>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(72),
+ PXA_FUNCTION(0, 2, "LDD<14>"),
+ PXA_FUNCTION(1, 2, "LDD<14>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(73),
+ PXA_FUNCTION(0, 2, "LDD<15>"),
+ PXA_FUNCTION(1, 2, "LDD<15>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(74),
+ PXA_FUNCTION(1, 2, "L_FCLK_RD")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(75),
+ PXA_FUNCTION(1, 2, "L_LCLK_A0")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(76),
+ PXA_FUNCTION(1, 2, "L_PCLK_WR")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(77),
+ PXA_FUNCTION(1, 2, "L_BIAS")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(78),
+ PXA_FUNCTION(1, 1, "nPCE<2>"),
+ PXA_FUNCTION(1, 2, "nCS<2>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(79),
+ PXA_FUNCTION(1, 1, "PSKTSEL"),
+ PXA_FUNCTION(1, 2, "nCS<3>"),
+ PXA_FUNCTION(1, 3, "PWM_OUT<2>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(80),
+ PXA_FUNCTION(0, 1, "DREQ<1>"),
+ PXA_FUNCTION(0, 2, "MBREQ"),
+ PXA_FUNCTION(1, 2, "nCS<4>"),
+ PXA_FUNCTION(1, 3, "PWM_OUT<3>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(81),
+ PXA_FUNCTION(0, 2, "CIF_DD<0>"),
+ PXA_FUNCTION(1, 1, "SSPTXD3"),
+ PXA_FUNCTION(1, 2, "BB_OB_DAT<0>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(82),
+ PXA_FUNCTION(0, 1, "SSPRXD3"),
+ PXA_FUNCTION(0, 2, "BB_IB_DAT<0>"),
+ PXA_FUNCTION(0, 3, "CIF_DD<5>"),
+ PXA_FUNCTION(1, 3, "FFDTR")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(83),
+ PXA_FUNCTION(0, 1, "SSPSFRM3"),
+ PXA_FUNCTION(0, 2, "BB_IB_CLK"),
+ PXA_FUNCTION(0, 3, "CIF_DD<5>"),
+ PXA_FUNCTION(1, 1, "SSPSFRM3"),
+ PXA_FUNCTION(1, 2, "FFTXD"),
+ PXA_FUNCTION(1, 3, "FFRTS")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(84),
+ PXA_FUNCTION(0, 1, "SSPCLK3"),
+ PXA_FUNCTION(0, 2, "BB_IB_STB"),
+ PXA_FUNCTION(0, 3, "CIF_FV"),
+ PXA_FUNCTION(1, 1, "SSPCLK3"),
+ PXA_FUNCTION(1, 3, "CIF_FV")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(85),
+ PXA_FUNCTION(0, 1, "FFRXD"),
+ PXA_FUNCTION(0, 2, "DREQ<2>"),
+ PXA_FUNCTION(0, 3, "CIF_LV"),
+ PXA_FUNCTION(1, 1, "nPCE<1>"),
+ PXA_FUNCTION(1, 2, "BB_IB_WAIT"),
+ PXA_FUNCTION(1, 3, "CIF_LV")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(86),
+ PXA_FUNCTION(0, 1, "SSPRXD2"),
+ PXA_FUNCTION(0, 2, "LDD<16>"),
+ PXA_FUNCTION(0, 3, "USB_P3_5"),
+ PXA_FUNCTION(1, 1, "nPCE<1>"),
+ PXA_FUNCTION(1, 2, "LDD<16>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(87),
+ PXA_FUNCTION(0, 1, "nPCE<2>"),
+ PXA_FUNCTION(0, 2, "LDD<17>"),
+ PXA_FUNCTION(0, 3, "USB_P3_1"),
+ PXA_FUNCTION(1, 1, "SSPTXD2"),
+ PXA_FUNCTION(1, 2, "LDD<17>"),
+ PXA_FUNCTION(1, 3, "SSPSFRM2")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(88),
+ PXA_FUNCTION(0, 1, "USBHPWR<1>"),
+ PXA_FUNCTION(0, 2, "SSPRXD2"),
+ PXA_FUNCTION(0, 3, "SSPSFRM2"),
+ PXA_FUNCTION(1, 2, "SSPTXD2"),
+ PXA_FUNCTION(1, 3, "SSPSFRM2")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(89),
+ PXA_FUNCTION(0, 1, "SSPRXD3"),
+ PXA_FUNCTION(0, 3, "FFRI"),
+ PXA_FUNCTION(1, 1, "AC97_SYSCLK"),
+ PXA_FUNCTION(1, 2, "USBHPEN<1>"),
+ PXA_FUNCTION(1, 3, "SSPTXD2")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(90),
+ PXA_FUNCTION(0, 1, "KP_MKIN<5>"),
+ PXA_FUNCTION(0, 3, "USB_P3_5"),
+ PXA_FUNCTION(1, 1, "CIF_DD<4>"),
+ PXA_FUNCTION(1, 2, "nURST")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(91),
+ PXA_FUNCTION(0, 1, "KP_MKIN<6>"),
+ PXA_FUNCTION(0, 3, "USB_P3_1"),
+ PXA_FUNCTION(1, 1, "CIF_DD<5>"),
+ PXA_FUNCTION(1, 2, "UCLK")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(92),
+ PXA_FUNCTION(0, 1, "MMDAT<0>"),
+ PXA_FUNCTION(1, 1, "MMDAT<0>"),
+ PXA_FUNCTION(1, 2, "MSBS")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(93),
+ PXA_FUNCTION(0, 1, "KP_DKIN<0>"),
+ PXA_FUNCTION(0, 2, "CIF_DD<6>"),
+ PXA_FUNCTION(1, 1, "AC97_SDATA_OUT")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(94),
+ PXA_FUNCTION(0, 1, "KP_DKIN<1>"),
+ PXA_FUNCTION(0, 2, "CIF_DD<5>"),
+ PXA_FUNCTION(1, 1, "AC97_SYNC")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(95),
+ PXA_FUNCTION(0, 1, "KP_DKIN<2>"),
+ PXA_FUNCTION(0, 2, "CIF_DD<4>"),
+ PXA_FUNCTION(0, 3, "KP_MKIN<6>"),
+ PXA_FUNCTION(1, 1, "AC97_RESET_n")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(96),
+ PXA_FUNCTION(0, 1, "KP_DKIN<3>"),
+ PXA_FUNCTION(0, 2, "MBREQ"),
+ PXA_FUNCTION(0, 3, "FFRXD"),
+ PXA_FUNCTION(1, 2, "DVAL<1>"),
+ PXA_FUNCTION(1, 3, "KP_MKOUT<6>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(97),
+ PXA_FUNCTION(0, 1, "KP_DKIN<4>"),
+ PXA_FUNCTION(0, 2, "DREQ<1>"),
+ PXA_FUNCTION(0, 3, "KP_MKIN<3>"),
+ PXA_FUNCTION(1, 2, "MBGNT")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(98),
+ PXA_FUNCTION(0, 1, "KP_DKIN<5>"),
+ PXA_FUNCTION(0, 2, "CIF_DD<0>"),
+ PXA_FUNCTION(0, 3, "KP_MKIN<4>"),
+ PXA_FUNCTION(1, 1, "AC97_SYSCLK"),
+ PXA_FUNCTION(1, 3, "FFRTS")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(99),
+ PXA_FUNCTION(0, 1, "KP_DKIN<6>"),
+ PXA_FUNCTION(0, 2, "AC97_SDATA_IN_1"),
+ PXA_FUNCTION(0, 3, "KP_MKIN<5>"),
+ PXA_FUNCTION(1, 3, "FFTXD")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(100),
+ PXA_FUNCTION(0, 1, "KP_MKIN<0>"),
+ PXA_FUNCTION(0, 2, "DREQ<2>"),
+ PXA_FUNCTION(0, 3, "FFCTS")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(101),
+ PXA_FUNCTION(0, 1, "KP_MKIN<1>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(102),
+ PXA_FUNCTION(0, 1, "KP_MKIN<2>"),
+ PXA_FUNCTION(0, 3, "FFRXD"),
+ PXA_FUNCTION(1, 1, "nPCE<1>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(103),
+ PXA_FUNCTION(0, 1, "CIF_DD<3>"),
+ PXA_FUNCTION(1, 2, "KP_MKOUT<0>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(104),
+ PXA_FUNCTION(0, 1, "CIF_DD<2>"),
+ PXA_FUNCTION(1, 1, "PSKTSEL"),
+ PXA_FUNCTION(1, 2, "KP_MKOUT<1>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(105),
+ PXA_FUNCTION(0, 1, "CIF_DD<1>"),
+ PXA_FUNCTION(1, 1, "nPCE<2>"),
+ PXA_FUNCTION(1, 2, "KP_MKOUT<2>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(106),
+ PXA_FUNCTION(0, 1, "CIF_DD<9>"),
+ PXA_FUNCTION(1, 2, "KP_MKOUT<3>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(107),
+ PXA_FUNCTION(0, 1, "CIF_DD<8>"),
+ PXA_FUNCTION(1, 2, "KP_MKOUT<4>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(108),
+ PXA_FUNCTION(0, 1, "CIF_DD<7>"),
+ PXA_FUNCTION(1, 1, "CHOUT<0>"),
+ PXA_FUNCTION(1, 2, "KP_MKOUT<5>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(109),
+ PXA_FUNCTION(0, 1, "MMDAT<1>"),
+ PXA_FUNCTION(0, 2, "MSSDIO"),
+ PXA_FUNCTION(1, 1, "MMDAT<1>"),
+ PXA_FUNCTION(1, 2, "MSSDIO")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(110),
+ PXA_FUNCTION(0, 1, "MMDAT<2>"),
+ PXA_FUNCTION(1, 1, "MMDAT<2>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(111),
+ PXA_FUNCTION(0, 1, "MMDAT<3>"),
+ PXA_FUNCTION(1, 1, "MMDAT<3>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(112),
+ PXA_FUNCTION(0, 1, "MMCMD"),
+ PXA_FUNCTION(0, 2, "nMSINS"),
+ PXA_FUNCTION(1, 1, "MMCMD")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(113),
+ PXA_FUNCTION(0, 3, "USB_P3_3"),
+ PXA_FUNCTION(1, 1, "I2S_SYSCLK"),
+ PXA_FUNCTION(1, 2, "AC97_RESET_n")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(114),
+ PXA_FUNCTION(0, 1, "CIF_DD<1>"),
+ PXA_FUNCTION(1, 1, "UEN"),
+ PXA_FUNCTION(1, 2, "UVS0")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(115),
+ PXA_FUNCTION(0, 1, "DREQ<0>"),
+ PXA_FUNCTION(0, 2, "CIF_DD<3>"),
+ PXA_FUNCTION(0, 3, "MBREQ"),
+ PXA_FUNCTION(1, 1, "UEN"),
+ PXA_FUNCTION(1, 2, "nUVS1"),
+ PXA_FUNCTION(1, 3, "PWM_OUT<1>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(116),
+ PXA_FUNCTION(0, 1, "CIF_DD<2>"),
+ PXA_FUNCTION(0, 2, "AC97_SDATA_IN_0"),
+ PXA_FUNCTION(0, 3, "UDET"),
+ PXA_FUNCTION(1, 1, "DVAL<0>"),
+ PXA_FUNCTION(1, 2, "nUVS2"),
+ PXA_FUNCTION(1, 3, "MBGNT")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(117),
+ PXA_FUNCTION(0, 1, "SCL"),
+ PXA_FUNCTION(1, 1, "SCL")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(118),
+ PXA_FUNCTION(0, 1, "SDA"),
+ PXA_FUNCTION(1, 1, "SDA")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(119),
+ PXA_FUNCTION(0, 1, "USBHPWR<2>")),
+ PXA_GPIO_PIN(PXA_PINCTRL_PIN(120),
+ PXA_FUNCTION(1, 2, "USBHPEN<2>")),
+};
+
+static int pxa27x_pinctrl_probe(struct platform_device *pdev)
+{
+ int ret, i;
+ void __iomem *base_af[8];
+ void __iomem *base_dir[4];
+ void __iomem *base_sleep[4];
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base_af[0] = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base_af[0]))
+ return PTR_ERR(base_af[0]);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ base_dir[0] = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base_dir[0]))
+ return PTR_ERR(base_dir[0]);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ base_dir[3] = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base_dir[3]))
+ return PTR_ERR(base_dir[3]);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
+ base_sleep[0] = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base_sleep[0]))
+ return PTR_ERR(base_sleep[0]);
+
+ for (i = 0; i < ARRAY_SIZE(base_af); i++)
+ base_af[i] = base_af[0] + sizeof(base_af[0]) * i;
+ for (i = 0; i < 3; i++)
+ base_dir[i] = base_dir[0] + sizeof(base_dir[0]) * i;
+ for (i = 0; i < ARRAY_SIZE(base_sleep); i++)
+ base_sleep[i] = base_sleep[0] + sizeof(base_af[0]) * i;
+
+ ret = pxa2xx_pinctrl_init(pdev, pxa27x_pins, ARRAY_SIZE(pxa27x_pins),
+ base_af, base_dir, base_sleep);
+ return ret;
+}
+
+static const struct of_device_id pxa27x_pinctrl_match[] = {
+ { .compatible = "marvell,pxa27x-pinctrl", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, pxa27x_pinctrl_match);
+
+static struct platform_driver pxa27x_pinctrl_driver = {
+ .probe = pxa27x_pinctrl_probe,
+ .driver = {
+ .name = "pxa27x-pinctrl",
+ .of_match_table = pxa27x_pinctrl_match,
+ },
+};
+module_platform_driver(pxa27x_pinctrl_driver);
+
+MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");
+MODULE_DESCRIPTION("Marvell PXA27x pinctrl driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pinctrl/pxa/pinctrl-pxa2xx.c b/drivers/pinctrl/pxa/pinctrl-pxa2xx.c
new file mode 100644
index 000000000000..d90e205cf809
--- /dev/null
+++ b/drivers/pinctrl/pxa/pinctrl-pxa2xx.c
@@ -0,0 +1,436 @@
+/*
+ * Marvell PXA2xx family pin control
+ *
+ * Copyright (C) 2015 Robert Jarzmik
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/module.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "../pinctrl-utils.h"
+#include "pinctrl-pxa2xx.h"
+
+static int pxa2xx_pctrl_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ struct pxa_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+
+ return pctl->ngroups;
+}
+
+static const char *pxa2xx_pctrl_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned tgroup)
+{
+ struct pxa_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+ struct pxa_pinctrl_group *group = pctl->groups + tgroup;
+
+ return group->name;
+}
+
+static int pxa2xx_pctrl_get_group_pins(struct pinctrl_dev *pctldev,
+ unsigned tgroup,
+ const unsigned **pins,
+ unsigned *num_pins)
+{
+ struct pxa_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+ struct pxa_pinctrl_group *group = pctl->groups + tgroup;
+
+ *pins = (unsigned *)&group->pin;
+ *num_pins = 1;
+
+ return 0;
+}
+
+static const struct pinctrl_ops pxa2xx_pctl_ops = {
+#ifdef CONFIG_OF
+ .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
+ .dt_free_map = pinctrl_utils_dt_free_map,
+#endif
+ .get_groups_count = pxa2xx_pctrl_get_groups_count,
+ .get_group_name = pxa2xx_pctrl_get_group_name,
+ .get_group_pins = pxa2xx_pctrl_get_group_pins,
+};
+
+static struct pxa_desc_function *
+pxa_desc_by_func_group(struct pxa_pinctrl *pctl, const char *pin_name,
+ const char *func_name)
+{
+ int i;
+ struct pxa_desc_function *df;
+
+ for (i = 0; i < pctl->npins; i++) {
+ const struct pxa_desc_pin *pin = pctl->ppins + i;
+
+ if (!strcmp(pin->pin.name, pin_name))
+ for (df = pin->functions; df->name; df++)
+ if (!strcmp(df->name, func_name))
+ return df;
+ }
+
+ return NULL;
+}
+
+static int pxa2xx_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned pin,
+ bool input)
+{
+ struct pxa_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+ unsigned long flags;
+ uint32_t val;
+ void __iomem *gpdr;
+
+ gpdr = pctl->base_gpdr[pin / 32];
+ dev_dbg(pctl->dev, "set_direction(pin=%d): dir=%d\n",
+ pin, !input);
+
+ spin_lock_irqsave(&pctl->lock, flags);
+
+ val = readl_relaxed(gpdr);
+ val = (val & ~BIT(pin % 32)) | (input ? 0 : BIT(pin % 32));
+ writel_relaxed(val, gpdr);
+
+ spin_unlock_irqrestore(&pctl->lock, flags);
+
+ return 0;
+}
+
+static const char *pxa2xx_pmx_get_func_name(struct pinctrl_dev *pctldev,
+ unsigned function)
+{
+ struct pxa_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+ struct pxa_pinctrl_function *pf = pctl->functions + function;
+
+ return pf->name;
+}
+
+static int pxa2xx_get_functions_count(struct pinctrl_dev *pctldev)
+{
+ struct pxa_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+
+ return pctl->nfuncs;
+}
+
+static int pxa2xx_pmx_get_func_groups(struct pinctrl_dev *pctldev,
+ unsigned function,
+ const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct pxa_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+ struct pxa_pinctrl_function *pf = pctl->functions + function;
+
+ *groups = pf->groups;
+ *num_groups = pf->ngroups;
+
+ return 0;
+}
+
+static int pxa2xx_pmx_set_mux(struct pinctrl_dev *pctldev, unsigned function,
+ unsigned tgroup)
+{
+ struct pxa_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+ struct pxa_pinctrl_group *group = pctl->groups + tgroup;
+ struct pxa_desc_function *df;
+ int pin, shift;
+ unsigned long flags;
+ void __iomem *gafr, *gpdr;
+ u32 val;
+
+
+ df = pxa_desc_by_func_group(pctl, group->name,
+ (pctl->functions + function)->name);
+ if (!df)
+ return -EINVAL;
+
+ pin = group->pin;
+ gafr = pctl->base_gafr[pin / 16];
+ gpdr = pctl->base_gpdr[pin / 32];
+ shift = (pin % 16) << 1;
+ dev_dbg(pctl->dev, "set_mux(pin=%d): af=%d dir=%d\n",
+ pin, df->muxval >> 1, df->muxval & 0x1);
+
+ spin_lock_irqsave(&pctl->lock, flags);
+
+ val = readl_relaxed(gafr);
+ val = (val & ~(0x3 << shift)) | ((df->muxval >> 1) << shift);
+ writel_relaxed(val, gafr);
+
+ val = readl_relaxed(gpdr);
+ val = (val & ~BIT(pin % 32)) | ((df->muxval & 1) ? BIT(pin % 32) : 0);
+ writel_relaxed(val, gpdr);
+
+ spin_unlock_irqrestore(&pctl->lock, flags);
+
+ return 0;
+}
+static const struct pinmux_ops pxa2xx_pinmux_ops = {
+ .get_functions_count = pxa2xx_get_functions_count,
+ .get_function_name = pxa2xx_pmx_get_func_name,
+ .get_function_groups = pxa2xx_pmx_get_func_groups,
+ .set_mux = pxa2xx_pmx_set_mux,
+ .gpio_set_direction = pxa2xx_pmx_gpio_set_direction,
+};
+
+static int pxa2xx_pconf_group_get(struct pinctrl_dev *pctldev,
+ unsigned group,
+ unsigned long *config)
+{
+ struct pxa_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+ struct pxa_pinctrl_group *g = pctl->groups + group;
+ unsigned long flags;
+ unsigned pin = g->pin;
+ void __iomem *pgsr = pctl->base_pgsr[pin / 32];
+ u32 val;
+
+ spin_lock_irqsave(&pctl->lock, flags);
+ val = readl_relaxed(pgsr) & BIT(pin % 32);
+ *config = val ? PIN_CONFIG_LOW_POWER_MODE : 0;
+ spin_unlock_irqrestore(&pctl->lock, flags);
+
+ dev_dbg(pctl->dev, "get sleep gpio state(pin=%d) %d\n",
+ pin, !!val);
+ return 0;
+}
+
+static int pxa2xx_pconf_group_set(struct pinctrl_dev *pctldev,
+ unsigned group,
+ unsigned long *configs,
+ unsigned num_configs)
+{
+ struct pxa_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+ struct pxa_pinctrl_group *g = pctl->groups + group;
+ unsigned long flags;
+ unsigned pin = g->pin;
+ void __iomem *pgsr = pctl->base_pgsr[pin / 32];
+ int i, is_set = 0;
+ u32 val;
+
+ for (i = 0; i < num_configs; i++) {
+ switch (pinconf_to_config_param(configs[i])) {
+ case PIN_CONFIG_LOW_POWER_MODE:
+ is_set = pinconf_to_config_argument(configs[i]);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ dev_dbg(pctl->dev, "set sleep gpio state(pin=%d) %d\n",
+ pin, is_set);
+
+ spin_lock_irqsave(&pctl->lock, flags);
+ val = readl_relaxed(pgsr);
+ val = (val & ~BIT(pin % 32)) | (is_set ? BIT(pin % 32) : 0);
+ writel_relaxed(val, pgsr);
+ spin_unlock_irqrestore(&pctl->lock, flags);
+
+ return 0;
+}
+
+static const struct pinconf_ops pxa2xx_pconf_ops = {
+ .pin_config_group_get = pxa2xx_pconf_group_get,
+ .pin_config_group_set = pxa2xx_pconf_group_set,
+ .is_generic = true,
+};
+
+static struct pinctrl_desc pxa2xx_pinctrl_desc = {
+ .confops = &pxa2xx_pconf_ops,
+ .pctlops = &pxa2xx_pctl_ops,
+ .pmxops = &pxa2xx_pinmux_ops,
+};
+
+static const struct pxa_pinctrl_function *
+pxa2xx_find_function(struct pxa_pinctrl *pctl, const char *fname,
+ const struct pxa_pinctrl_function *functions)
+{
+ const struct pxa_pinctrl_function *func;
+
+ for (func = functions; func->name; func++)
+ if (!strcmp(fname, func->name))
+ return func;
+
+ return NULL;
+}
+
+static int pxa2xx_build_functions(struct pxa_pinctrl *pctl)
+{
+ int i;
+ struct pxa_pinctrl_function *functions;
+ struct pxa_desc_function *df;
+
+ /*
+ * Each pin can have at most 6 alternate functions, and 2 gpio functions
+ * which are common to each pin. As there are more than 2 pins without
+ * alternate function, 6 * npins is an absolute high limit of the number
+ * of functions.
+ */
+ functions = devm_kcalloc(pctl->dev, pctl->npins * 6,
+ sizeof(*functions), GFP_KERNEL);
+ if (!functions)
+ return -ENOMEM;
+
+ for (i = 0; i < pctl->npins; i++)
+ for (df = pctl->ppins[i].functions; df->name; df++)
+ if (!pxa2xx_find_function(pctl, df->name, functions))
+ (functions + pctl->nfuncs++)->name = df->name;
+ pctl->functions = devm_kmemdup(pctl->dev, functions,
+ pctl->nfuncs * sizeof(*functions),
+ GFP_KERNEL);
+ if (!pctl->functions)
+ return -ENOMEM;
+
+ devm_kfree(pctl->dev, functions);
+ return 0;
+}
+
+static int pxa2xx_build_groups(struct pxa_pinctrl *pctl)
+{
+ int i, j, ngroups;
+ struct pxa_pinctrl_function *func;
+ struct pxa_desc_function *df;
+ char **gtmp;
+
+ gtmp = devm_kmalloc_array(pctl->dev, pctl->npins, sizeof(*gtmp),
+ GFP_KERNEL);
+ if (!gtmp)
+ return -ENOMEM;
+
+ for (i = 0; i < pctl->nfuncs; i++) {
+ ngroups = 0;
+ for (j = 0; j < pctl->npins; j++)
+ for (df = pctl->ppins[j].functions; df->name;
+ df++)
+ if (!strcmp(pctl->functions[i].name,
+ df->name))
+ gtmp[ngroups++] = (char *)
+ pctl->ppins[j].pin.name;
+ func = pctl->functions + i;
+ func->ngroups = ngroups;
+ func->groups =
+ devm_kmalloc_array(pctl->dev, ngroups,
+ sizeof(char *), GFP_KERNEL);
+ if (!func->groups)
+ return -ENOMEM;
+
+ memcpy(func->groups, gtmp, ngroups * sizeof(*gtmp));
+ }
+
+ devm_kfree(pctl->dev, gtmp);
+ return 0;
+}
+
+static int pxa2xx_build_state(struct pxa_pinctrl *pctl,
+ const struct pxa_desc_pin *ppins, int npins)
+{
+ struct pxa_pinctrl_group *group;
+ struct pinctrl_pin_desc *pins;
+ int ret, i;
+
+ pctl->npins = npins;
+ pctl->ppins = ppins;
+ pctl->ngroups = npins;
+
+ pctl->desc.npins = npins;
+ pins = devm_kcalloc(pctl->dev, npins, sizeof(*pins), GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ pctl->desc.pins = pins;
+ for (i = 0; i < npins; i++)
+ pins[i] = ppins[i].pin;
+
+ pctl->groups = devm_kmalloc_array(pctl->dev, pctl->ngroups,
+ sizeof(*pctl->groups), GFP_KERNEL);
+ if (!pctl->groups)
+ return -ENOMEM;
+
+ for (i = 0; i < npins; i++) {
+ group = pctl->groups + i;
+ group->name = ppins[i].pin.name;
+ group->pin = ppins[i].pin.number;
+ }
+
+ ret = pxa2xx_build_functions(pctl);
+ if (ret)
+ return ret;
+
+ ret = pxa2xx_build_groups(pctl);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int pxa2xx_pinctrl_init(struct platform_device *pdev,
+ const struct pxa_desc_pin *ppins, int npins,
+ void __iomem *base_gafr[], void __iomem *base_gpdr[],
+ void __iomem *base_pgsr[])
+{
+ struct pxa_pinctrl *pctl;
+ int ret, i, maxpin = 0;
+
+ for (i = 0; i < npins; i++)
+ maxpin = max_t(int, ppins[i].pin.number, maxpin);
+
+ pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);
+ if (!pctl)
+ return -ENOMEM;
+ pctl->base_gafr = devm_kcalloc(&pdev->dev, roundup(maxpin, 16),
+ sizeof(*pctl->base_gafr), GFP_KERNEL);
+ pctl->base_gpdr = devm_kcalloc(&pdev->dev, roundup(maxpin, 32),
+ sizeof(*pctl->base_gpdr), GFP_KERNEL);
+ pctl->base_pgsr = devm_kcalloc(&pdev->dev, roundup(maxpin, 32),
+ sizeof(*pctl->base_pgsr), GFP_KERNEL);
+ if (!pctl->base_gafr || !pctl->base_gpdr || !pctl->base_pgsr)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, pctl);
+ spin_lock_init(&pctl->lock);
+
+ pctl->dev = &pdev->dev;
+ pctl->desc = pxa2xx_pinctrl_desc;
+ pctl->desc.name = dev_name(&pdev->dev);
+ pctl->desc.owner = THIS_MODULE;
+
+ for (i = 0; i < roundup(maxpin, 16); i += 16)
+ pctl->base_gafr[i / 16] = base_gafr[i / 16];
+ for (i = 0; i < roundup(maxpin, 32); i += 32) {
+ pctl->base_gpdr[i / 32] = base_gpdr[i / 32];
+ pctl->base_pgsr[i / 32] = base_pgsr[i / 32];
+ }
+
+ ret = pxa2xx_build_state(pctl, ppins, npins);
+ if (ret)
+ return ret;
+
+ pctl->pctl_dev = pinctrl_register(&pctl->desc, &pdev->dev, pctl);
+ if (IS_ERR(pctl->pctl_dev)) {
+ dev_err(&pdev->dev, "couldn't register pinctrl driver\n");
+ return PTR_ERR(pctl->pctl_dev);
+ }
+
+ dev_info(&pdev->dev, "initialized pxa2xx pinctrl driver\n");
+
+ return 0;
+}
+
+int pxa2xx_pinctrl_exit(struct platform_device *pdev)
+{
+ struct pxa_pinctrl *pctl = platform_get_drvdata(pdev);
+
+ pinctrl_unregister(pctl->pctl_dev);
+ return 0;
+}
diff --git a/drivers/pinctrl/pxa/pinctrl-pxa2xx.h b/drivers/pinctrl/pxa/pinctrl-pxa2xx.h
new file mode 100644
index 000000000000..8be1e0b79751
--- /dev/null
+++ b/drivers/pinctrl/pxa/pinctrl-pxa2xx.h
@@ -0,0 +1,92 @@
+/*
+ * Marvell PXA2xx family pin control
+ *
+ * Copyright (C) 2015 Robert Jarzmik
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ */
+
+#ifndef __PINCTRL_PXA_H
+#define __PINCTRL_PXA_H
+
+#define PXA_FUNCTION(_dir, _af, _name) \
+ { \
+ .name = _name, \
+ .muxval = (_dir | (_af << 1)), \
+ }
+
+#define PXA_PIN(_pin, funcs...) \
+ { \
+ .pin = _pin, \
+ .functions = (struct pxa_desc_function[]){ \
+ funcs, { } }, \
+ }
+
+#define PXA_GPIO_PIN(_pin, funcs...) \
+ { \
+ .pin = _pin, \
+ .functions = (struct pxa_desc_function[]){ \
+ PXA_FUNCTION(0, 0, "gpio_in"), \
+ PXA_FUNCTION(1, 0, "gpio_out"), \
+ funcs, { } }, \
+ }
+
+#define PXA_GPIO_ONLY_PIN(_pin) \
+ { \
+ .pin = _pin, \
+ .functions = (struct pxa_desc_function[]){ \
+ PXA_FUNCTION(0, 0, "gpio_in"), \
+ PXA_FUNCTION(1, 0, "gpio_out"), \
+ { } }, \
+ }
+
+#define PXA_PINCTRL_PIN(pin) \
+ PINCTRL_PIN(pin, "P" #pin)
+
+struct pxa_desc_function {
+ const char *name;
+ u8 muxval;
+};
+
+struct pxa_desc_pin {
+ struct pinctrl_pin_desc pin;
+ struct pxa_desc_function *functions;
+};
+
+struct pxa_pinctrl_group {
+ const char *name;
+ unsigned pin;
+};
+
+struct pxa_pinctrl_function {
+ const char *name;
+ const char **groups;
+ unsigned ngroups;
+};
+
+struct pxa_pinctrl {
+ spinlock_t lock;
+ void __iomem **base_gafr;
+ void __iomem **base_gpdr;
+ void __iomem **base_pgsr;
+ struct device *dev;
+ struct pinctrl_desc desc;
+ struct pinctrl_dev *pctl_dev;
+ unsigned npins;
+ const struct pxa_desc_pin *ppins;
+ unsigned ngroups;
+ struct pxa_pinctrl_group *groups;
+ unsigned nfuncs;
+ struct pxa_pinctrl_function *functions;
+ char *name;
+};
+
+int pxa2xx_pinctrl_init(struct platform_device *pdev,
+ const struct pxa_desc_pin *ppins, int npins,
+ void __iomem *base_gafr[], void __iomem *base_gpdr[],
+ void __iomem *base_gpsr[]);
+
+#endif /* __PINCTRL_PXA_H */
diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
index 383263a92e59..eeac8cba8a21 100644
--- a/drivers/pinctrl/qcom/Kconfig
+++ b/drivers/pinctrl/qcom/Kconfig
@@ -63,6 +63,14 @@ config PINCTRL_MSM8916
This is the pinctrl, pinmux, pinconf and gpiolib driver for the
Qualcomm TLMM block found on the Qualcomm 8916 platform.
+config PINCTRL_MSM8996
+ tristate "Qualcomm MSM8996 pin controller driver"
+ depends on GPIOLIB && OF
+ select PINCTRL_MSM
+ help
+ This is the pinctrl, pinmux, pinconf and gpiolib driver for the
+ Qualcomm TLMM block found in the Qualcomm MSM8996 platform.
+
config PINCTRL_QDF2XXX
tristate "Qualcomm Technologies QDF2xxx pin controller driver"
depends on GPIOLIB && ACPI
diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
index 13b190e72c21..dfb50a9fe04a 100644
--- a/drivers/pinctrl/qcom/Makefile
+++ b/drivers/pinctrl/qcom/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_PINCTRL_MSM8660) += pinctrl-msm8660.o
obj-$(CONFIG_PINCTRL_MSM8960) += pinctrl-msm8960.o
obj-$(CONFIG_PINCTRL_MSM8X74) += pinctrl-msm8x74.o
obj-$(CONFIG_PINCTRL_MSM8916) += pinctrl-msm8916.o
+obj-$(CONFIG_PINCTRL_MSM8996) += pinctrl-msm8996.o
obj-$(CONFIG_PINCTRL_QDF2XXX) += pinctrl-qdf2xxx.o
obj-$(CONFIG_PINCTRL_QCOM_SPMI_PMIC) += pinctrl-spmi-gpio.o
obj-$(CONFIG_PINCTRL_QCOM_SPMI_PMIC) += pinctrl-spmi-mpp.o
diff --git a/drivers/pinctrl/qcom/pinctrl-msm8996.c b/drivers/pinctrl/qcom/pinctrl-msm8996.c
new file mode 100644
index 000000000000..c257927bea05
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-msm8996.c
@@ -0,0 +1,1942 @@
+/*
+ * Copyright (c) 2014-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/of.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinctrl.h>
+
+#include "pinctrl-msm.h"
+
+#define FUNCTION(fname) \
+ [msm_mux_##fname] = { \
+ .name = #fname, \
+ .groups = fname##_groups, \
+ .ngroups = ARRAY_SIZE(fname##_groups), \
+ }
+
+#define REG_BASE 0x0
+#define REG_SIZE 0x1000
+#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9) \
+ { \
+ .name = "gpio" #id, \
+ .pins = gpio##id##_pins, \
+ .npins = (unsigned)ARRAY_SIZE(gpio##id##_pins), \
+ .funcs = (int[]){ \
+ msm_mux_gpio, /* gpio mode */ \
+ msm_mux_##f1, \
+ msm_mux_##f2, \
+ msm_mux_##f3, \
+ msm_mux_##f4, \
+ msm_mux_##f5, \
+ msm_mux_##f6, \
+ msm_mux_##f7, \
+ msm_mux_##f8, \
+ msm_mux_##f9 \
+ }, \
+ .nfuncs = 10, \
+ .ctl_reg = REG_BASE + REG_SIZE * id, \
+ .io_reg = REG_BASE + 0x4 + REG_SIZE * id, \
+ .intr_cfg_reg = REG_BASE + 0x8 + REG_SIZE * id, \
+ .intr_status_reg = REG_BASE + 0xc + REG_SIZE * id, \
+ .intr_target_reg = REG_BASE + 0x8 + REG_SIZE * id, \
+ .mux_bit = 2, \
+ .pull_bit = 0, \
+ .drv_bit = 6, \
+ .oe_bit = 9, \
+ .in_bit = 0, \
+ .out_bit = 1, \
+ .intr_enable_bit = 0, \
+ .intr_status_bit = 0, \
+ .intr_target_bit = 5, \
+ .intr_target_kpss_val = 3, \
+ .intr_raw_status_bit = 4, \
+ .intr_polarity_bit = 1, \
+ .intr_detection_bit = 2, \
+ .intr_detection_width = 2, \
+ }
+
+#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv) \
+ { \
+ .name = #pg_name, \
+ .pins = pg_name##_pins, \
+ .npins = (unsigned)ARRAY_SIZE(pg_name##_pins), \
+ .ctl_reg = ctl, \
+ .io_reg = 0, \
+ .intr_cfg_reg = 0, \
+ .intr_status_reg = 0, \
+ .intr_target_reg = 0, \
+ .mux_bit = -1, \
+ .pull_bit = pull, \
+ .drv_bit = drv, \
+ .oe_bit = -1, \
+ .in_bit = -1, \
+ .out_bit = -1, \
+ .intr_enable_bit = -1, \
+ .intr_status_bit = -1, \
+ .intr_target_bit = -1, \
+ .intr_raw_status_bit = -1, \
+ .intr_polarity_bit = -1, \
+ .intr_detection_bit = -1, \
+ .intr_detection_width = -1, \
+ }
+static const struct pinctrl_pin_desc msm8996_pins[] = {
+ PINCTRL_PIN(0, "GPIO_0"),
+ PINCTRL_PIN(1, "GPIO_1"),
+ PINCTRL_PIN(2, "GPIO_2"),
+ PINCTRL_PIN(3, "GPIO_3"),
+ PINCTRL_PIN(4, "GPIO_4"),
+ PINCTRL_PIN(5, "GPIO_5"),
+ PINCTRL_PIN(6, "GPIO_6"),
+ PINCTRL_PIN(7, "GPIO_7"),
+ PINCTRL_PIN(8, "GPIO_8"),
+ PINCTRL_PIN(9, "GPIO_9"),
+ PINCTRL_PIN(10, "GPIO_10"),
+ PINCTRL_PIN(11, "GPIO_11"),
+ PINCTRL_PIN(12, "GPIO_12"),
+ PINCTRL_PIN(13, "GPIO_13"),
+ PINCTRL_PIN(14, "GPIO_14"),
+ PINCTRL_PIN(15, "GPIO_15"),
+ PINCTRL_PIN(16, "GPIO_16"),
+ PINCTRL_PIN(17, "GPIO_17"),
+ PINCTRL_PIN(18, "GPIO_18"),
+ PINCTRL_PIN(19, "GPIO_19"),
+ PINCTRL_PIN(20, "GPIO_20"),
+ PINCTRL_PIN(21, "GPIO_21"),
+ PINCTRL_PIN(22, "GPIO_22"),
+ PINCTRL_PIN(23, "GPIO_23"),
+ PINCTRL_PIN(24, "GPIO_24"),
+ PINCTRL_PIN(25, "GPIO_25"),
+ PINCTRL_PIN(26, "GPIO_26"),
+ PINCTRL_PIN(27, "GPIO_27"),
+ PINCTRL_PIN(28, "GPIO_28"),
+ PINCTRL_PIN(29, "GPIO_29"),
+ PINCTRL_PIN(30, "GPIO_30"),
+ PINCTRL_PIN(31, "GPIO_31"),
+ PINCTRL_PIN(32, "GPIO_32"),
+ PINCTRL_PIN(33, "GPIO_33"),
+ PINCTRL_PIN(34, "GPIO_34"),
+ PINCTRL_PIN(35, "GPIO_35"),
+ PINCTRL_PIN(36, "GPIO_36"),
+ PINCTRL_PIN(37, "GPIO_37"),
+ PINCTRL_PIN(38, "GPIO_38"),
+ PINCTRL_PIN(39, "GPIO_39"),
+ PINCTRL_PIN(40, "GPIO_40"),
+ PINCTRL_PIN(41, "GPIO_41"),
+ PINCTRL_PIN(42, "GPIO_42"),
+ PINCTRL_PIN(43, "GPIO_43"),
+ PINCTRL_PIN(44, "GPIO_44"),
+ PINCTRL_PIN(45, "GPIO_45"),
+ PINCTRL_PIN(46, "GPIO_46"),
+ PINCTRL_PIN(47, "GPIO_47"),
+ PINCTRL_PIN(48, "GPIO_48"),
+ PINCTRL_PIN(49, "GPIO_49"),
+ PINCTRL_PIN(50, "GPIO_50"),
+ PINCTRL_PIN(51, "GPIO_51"),
+ PINCTRL_PIN(52, "GPIO_52"),
+ PINCTRL_PIN(53, "GPIO_53"),
+ PINCTRL_PIN(54, "GPIO_54"),
+ PINCTRL_PIN(55, "GPIO_55"),
+ PINCTRL_PIN(56, "GPIO_56"),
+ PINCTRL_PIN(57, "GPIO_57"),
+ PINCTRL_PIN(58, "GPIO_58"),
+ PINCTRL_PIN(59, "GPIO_59"),
+ PINCTRL_PIN(60, "GPIO_60"),
+ PINCTRL_PIN(61, "GPIO_61"),
+ PINCTRL_PIN(62, "GPIO_62"),
+ PINCTRL_PIN(63, "GPIO_63"),
+ PINCTRL_PIN(64, "GPIO_64"),
+ PINCTRL_PIN(65, "GPIO_65"),
+ PINCTRL_PIN(66, "GPIO_66"),
+ PINCTRL_PIN(67, "GPIO_67"),
+ PINCTRL_PIN(68, "GPIO_68"),
+ PINCTRL_PIN(69, "GPIO_69"),
+ PINCTRL_PIN(70, "GPIO_70"),
+ PINCTRL_PIN(71, "GPIO_71"),
+ PINCTRL_PIN(72, "GPIO_72"),
+ PINCTRL_PIN(73, "GPIO_73"),
+ PINCTRL_PIN(74, "GPIO_74"),
+ PINCTRL_PIN(75, "GPIO_75"),
+ PINCTRL_PIN(76, "GPIO_76"),
+ PINCTRL_PIN(77, "GPIO_77"),
+ PINCTRL_PIN(78, "GPIO_78"),
+ PINCTRL_PIN(79, "GPIO_79"),
+ PINCTRL_PIN(80, "GPIO_80"),
+ PINCTRL_PIN(81, "GPIO_81"),
+ PINCTRL_PIN(82, "GPIO_82"),
+ PINCTRL_PIN(83, "GPIO_83"),
+ PINCTRL_PIN(84, "GPIO_84"),
+ PINCTRL_PIN(85, "GPIO_85"),
+ PINCTRL_PIN(86, "GPIO_86"),
+ PINCTRL_PIN(87, "GPIO_87"),
+ PINCTRL_PIN(88, "GPIO_88"),
+ PINCTRL_PIN(89, "GPIO_89"),
+ PINCTRL_PIN(90, "GPIO_90"),
+ PINCTRL_PIN(91, "GPIO_91"),
+ PINCTRL_PIN(92, "GPIO_92"),
+ PINCTRL_PIN(93, "GPIO_93"),
+ PINCTRL_PIN(94, "GPIO_94"),
+ PINCTRL_PIN(95, "GPIO_95"),
+ PINCTRL_PIN(96, "GPIO_96"),
+ PINCTRL_PIN(97, "GPIO_97"),
+ PINCTRL_PIN(98, "GPIO_98"),
+ PINCTRL_PIN(99, "GPIO_99"),
+ PINCTRL_PIN(100, "GPIO_100"),
+ PINCTRL_PIN(101, "GPIO_101"),
+ PINCTRL_PIN(102, "GPIO_102"),
+ PINCTRL_PIN(103, "GPIO_103"),
+ PINCTRL_PIN(104, "GPIO_104"),
+ PINCTRL_PIN(105, "GPIO_105"),
+ PINCTRL_PIN(106, "GPIO_106"),
+ PINCTRL_PIN(107, "GPIO_107"),
+ PINCTRL_PIN(108, "GPIO_108"),
+ PINCTRL_PIN(109, "GPIO_109"),
+ PINCTRL_PIN(110, "GPIO_110"),
+ PINCTRL_PIN(111, "GPIO_111"),
+ PINCTRL_PIN(112, "GPIO_112"),
+ PINCTRL_PIN(113, "GPIO_113"),
+ PINCTRL_PIN(114, "GPIO_114"),
+ PINCTRL_PIN(115, "GPIO_115"),
+ PINCTRL_PIN(116, "GPIO_116"),
+ PINCTRL_PIN(117, "GPIO_117"),
+ PINCTRL_PIN(118, "GPIO_118"),
+ PINCTRL_PIN(119, "GPIO_119"),
+ PINCTRL_PIN(120, "GPIO_120"),
+ PINCTRL_PIN(121, "GPIO_121"),
+ PINCTRL_PIN(122, "GPIO_122"),
+ PINCTRL_PIN(123, "GPIO_123"),
+ PINCTRL_PIN(124, "GPIO_124"),
+ PINCTRL_PIN(125, "GPIO_125"),
+ PINCTRL_PIN(126, "GPIO_126"),
+ PINCTRL_PIN(127, "GPIO_127"),
+ PINCTRL_PIN(128, "GPIO_128"),
+ PINCTRL_PIN(129, "GPIO_129"),
+ PINCTRL_PIN(130, "GPIO_130"),
+ PINCTRL_PIN(131, "GPIO_131"),
+ PINCTRL_PIN(132, "GPIO_132"),
+ PINCTRL_PIN(133, "GPIO_133"),
+ PINCTRL_PIN(134, "GPIO_134"),
+ PINCTRL_PIN(135, "GPIO_135"),
+ PINCTRL_PIN(136, "GPIO_136"),
+ PINCTRL_PIN(137, "GPIO_137"),
+ PINCTRL_PIN(138, "GPIO_138"),
+ PINCTRL_PIN(139, "GPIO_139"),
+ PINCTRL_PIN(140, "GPIO_140"),
+ PINCTRL_PIN(141, "GPIO_141"),
+ PINCTRL_PIN(142, "GPIO_142"),
+ PINCTRL_PIN(143, "GPIO_143"),
+ PINCTRL_PIN(144, "GPIO_144"),
+ PINCTRL_PIN(145, "GPIO_145"),
+ PINCTRL_PIN(146, "GPIO_146"),
+ PINCTRL_PIN(147, "GPIO_147"),
+ PINCTRL_PIN(148, "GPIO_148"),
+ PINCTRL_PIN(149, "GPIO_149"),
+ PINCTRL_PIN(150, "SDC1_CLK"),
+ PINCTRL_PIN(151, "SDC1_CMD"),
+ PINCTRL_PIN(152, "SDC1_DATA"),
+ PINCTRL_PIN(153, "SDC2_CLK"),
+ PINCTRL_PIN(154, "SDC2_CMD"),
+ PINCTRL_PIN(155, "SDC2_DATA"),
+ PINCTRL_PIN(156, "SDC1_RCLK"),
+};
+
+#define DECLARE_MSM_GPIO_PINS(pin) \
+ static const unsigned int gpio##pin##_pins[] = { pin }
+DECLARE_MSM_GPIO_PINS(0);
+DECLARE_MSM_GPIO_PINS(1);
+DECLARE_MSM_GPIO_PINS(2);
+DECLARE_MSM_GPIO_PINS(3);
+DECLARE_MSM_GPIO_PINS(4);
+DECLARE_MSM_GPIO_PINS(5);
+DECLARE_MSM_GPIO_PINS(6);
+DECLARE_MSM_GPIO_PINS(7);
+DECLARE_MSM_GPIO_PINS(8);
+DECLARE_MSM_GPIO_PINS(9);
+DECLARE_MSM_GPIO_PINS(10);
+DECLARE_MSM_GPIO_PINS(11);
+DECLARE_MSM_GPIO_PINS(12);
+DECLARE_MSM_GPIO_PINS(13);
+DECLARE_MSM_GPIO_PINS(14);
+DECLARE_MSM_GPIO_PINS(15);
+DECLARE_MSM_GPIO_PINS(16);
+DECLARE_MSM_GPIO_PINS(17);
+DECLARE_MSM_GPIO_PINS(18);
+DECLARE_MSM_GPIO_PINS(19);
+DECLARE_MSM_GPIO_PINS(20);
+DECLARE_MSM_GPIO_PINS(21);
+DECLARE_MSM_GPIO_PINS(22);
+DECLARE_MSM_GPIO_PINS(23);
+DECLARE_MSM_GPIO_PINS(24);
+DECLARE_MSM_GPIO_PINS(25);
+DECLARE_MSM_GPIO_PINS(26);
+DECLARE_MSM_GPIO_PINS(27);
+DECLARE_MSM_GPIO_PINS(28);
+DECLARE_MSM_GPIO_PINS(29);
+DECLARE_MSM_GPIO_PINS(30);
+DECLARE_MSM_GPIO_PINS(31);
+DECLARE_MSM_GPIO_PINS(32);
+DECLARE_MSM_GPIO_PINS(33);
+DECLARE_MSM_GPIO_PINS(34);
+DECLARE_MSM_GPIO_PINS(35);
+DECLARE_MSM_GPIO_PINS(36);
+DECLARE_MSM_GPIO_PINS(37);
+DECLARE_MSM_GPIO_PINS(38);
+DECLARE_MSM_GPIO_PINS(39);
+DECLARE_MSM_GPIO_PINS(40);
+DECLARE_MSM_GPIO_PINS(41);
+DECLARE_MSM_GPIO_PINS(42);
+DECLARE_MSM_GPIO_PINS(43);
+DECLARE_MSM_GPIO_PINS(44);
+DECLARE_MSM_GPIO_PINS(45);
+DECLARE_MSM_GPIO_PINS(46);
+DECLARE_MSM_GPIO_PINS(47);
+DECLARE_MSM_GPIO_PINS(48);
+DECLARE_MSM_GPIO_PINS(49);
+DECLARE_MSM_GPIO_PINS(50);
+DECLARE_MSM_GPIO_PINS(51);
+DECLARE_MSM_GPIO_PINS(52);
+DECLARE_MSM_GPIO_PINS(53);
+DECLARE_MSM_GPIO_PINS(54);
+DECLARE_MSM_GPIO_PINS(55);
+DECLARE_MSM_GPIO_PINS(56);
+DECLARE_MSM_GPIO_PINS(57);
+DECLARE_MSM_GPIO_PINS(58);
+DECLARE_MSM_GPIO_PINS(59);
+DECLARE_MSM_GPIO_PINS(60);
+DECLARE_MSM_GPIO_PINS(61);
+DECLARE_MSM_GPIO_PINS(62);
+DECLARE_MSM_GPIO_PINS(63);
+DECLARE_MSM_GPIO_PINS(64);
+DECLARE_MSM_GPIO_PINS(65);
+DECLARE_MSM_GPIO_PINS(66);
+DECLARE_MSM_GPIO_PINS(67);
+DECLARE_MSM_GPIO_PINS(68);
+DECLARE_MSM_GPIO_PINS(69);
+DECLARE_MSM_GPIO_PINS(70);
+DECLARE_MSM_GPIO_PINS(71);
+DECLARE_MSM_GPIO_PINS(72);
+DECLARE_MSM_GPIO_PINS(73);
+DECLARE_MSM_GPIO_PINS(74);
+DECLARE_MSM_GPIO_PINS(75);
+DECLARE_MSM_GPIO_PINS(76);
+DECLARE_MSM_GPIO_PINS(77);
+DECLARE_MSM_GPIO_PINS(78);
+DECLARE_MSM_GPIO_PINS(79);
+DECLARE_MSM_GPIO_PINS(80);
+DECLARE_MSM_GPIO_PINS(81);
+DECLARE_MSM_GPIO_PINS(82);
+DECLARE_MSM_GPIO_PINS(83);
+DECLARE_MSM_GPIO_PINS(84);
+DECLARE_MSM_GPIO_PINS(85);
+DECLARE_MSM_GPIO_PINS(86);
+DECLARE_MSM_GPIO_PINS(87);
+DECLARE_MSM_GPIO_PINS(88);
+DECLARE_MSM_GPIO_PINS(89);
+DECLARE_MSM_GPIO_PINS(90);
+DECLARE_MSM_GPIO_PINS(91);
+DECLARE_MSM_GPIO_PINS(92);
+DECLARE_MSM_GPIO_PINS(93);
+DECLARE_MSM_GPIO_PINS(94);
+DECLARE_MSM_GPIO_PINS(95);
+DECLARE_MSM_GPIO_PINS(96);
+DECLARE_MSM_GPIO_PINS(97);
+DECLARE_MSM_GPIO_PINS(98);
+DECLARE_MSM_GPIO_PINS(99);
+DECLARE_MSM_GPIO_PINS(100);
+DECLARE_MSM_GPIO_PINS(101);
+DECLARE_MSM_GPIO_PINS(102);
+DECLARE_MSM_GPIO_PINS(103);
+DECLARE_MSM_GPIO_PINS(104);
+DECLARE_MSM_GPIO_PINS(105);
+DECLARE_MSM_GPIO_PINS(106);
+DECLARE_MSM_GPIO_PINS(107);
+DECLARE_MSM_GPIO_PINS(108);
+DECLARE_MSM_GPIO_PINS(109);
+DECLARE_MSM_GPIO_PINS(110);
+DECLARE_MSM_GPIO_PINS(111);
+DECLARE_MSM_GPIO_PINS(112);
+DECLARE_MSM_GPIO_PINS(113);
+DECLARE_MSM_GPIO_PINS(114);
+DECLARE_MSM_GPIO_PINS(115);
+DECLARE_MSM_GPIO_PINS(116);
+DECLARE_MSM_GPIO_PINS(117);
+DECLARE_MSM_GPIO_PINS(118);
+DECLARE_MSM_GPIO_PINS(119);
+DECLARE_MSM_GPIO_PINS(120);
+DECLARE_MSM_GPIO_PINS(121);
+DECLARE_MSM_GPIO_PINS(122);
+DECLARE_MSM_GPIO_PINS(123);
+DECLARE_MSM_GPIO_PINS(124);
+DECLARE_MSM_GPIO_PINS(125);
+DECLARE_MSM_GPIO_PINS(126);
+DECLARE_MSM_GPIO_PINS(127);
+DECLARE_MSM_GPIO_PINS(128);
+DECLARE_MSM_GPIO_PINS(129);
+DECLARE_MSM_GPIO_PINS(130);
+DECLARE_MSM_GPIO_PINS(131);
+DECLARE_MSM_GPIO_PINS(132);
+DECLARE_MSM_GPIO_PINS(133);
+DECLARE_MSM_GPIO_PINS(134);
+DECLARE_MSM_GPIO_PINS(135);
+DECLARE_MSM_GPIO_PINS(136);
+DECLARE_MSM_GPIO_PINS(137);
+DECLARE_MSM_GPIO_PINS(138);
+DECLARE_MSM_GPIO_PINS(139);
+DECLARE_MSM_GPIO_PINS(140);
+DECLARE_MSM_GPIO_PINS(141);
+DECLARE_MSM_GPIO_PINS(142);
+DECLARE_MSM_GPIO_PINS(143);
+DECLARE_MSM_GPIO_PINS(144);
+DECLARE_MSM_GPIO_PINS(145);
+DECLARE_MSM_GPIO_PINS(146);
+DECLARE_MSM_GPIO_PINS(147);
+DECLARE_MSM_GPIO_PINS(148);
+DECLARE_MSM_GPIO_PINS(149);
+
+static const unsigned int sdc1_clk_pins[] = { 150 };
+static const unsigned int sdc1_cmd_pins[] = { 151 };
+static const unsigned int sdc1_data_pins[] = { 152 };
+static const unsigned int sdc2_clk_pins[] = { 153 };
+static const unsigned int sdc2_cmd_pins[] = { 154 };
+static const unsigned int sdc2_data_pins[] = { 155 };
+static const unsigned int sdc1_rclk_pins[] = { 156 };
+
+enum msm8996_functions {
+ msm_mux_adsp_ext,
+ msm_mux_atest_bbrx0,
+ msm_mux_atest_bbrx1,
+ msm_mux_atest_char,
+ msm_mux_atest_char0,
+ msm_mux_atest_char1,
+ msm_mux_atest_char2,
+ msm_mux_atest_char3,
+ msm_mux_atest_gpsadc0,
+ msm_mux_atest_gpsadc1,
+ msm_mux_atest_tsens,
+ msm_mux_atest_tsens2,
+ msm_mux_atest_usb1,
+ msm_mux_atest_usb10,
+ msm_mux_atest_usb11,
+ msm_mux_atest_usb12,
+ msm_mux_atest_usb13,
+ msm_mux_atest_usb2,
+ msm_mux_atest_usb20,
+ msm_mux_atest_usb21,
+ msm_mux_atest_usb22,
+ msm_mux_atest_usb23,
+ msm_mux_audio_ref,
+ msm_mux_bimc_dte0,
+ msm_mux_bimc_dte1,
+ msm_mux_blsp10_spi,
+ msm_mux_blsp11_i2c_scl_b,
+ msm_mux_blsp11_i2c_sda_b,
+ msm_mux_blsp11_uart_rx_b,
+ msm_mux_blsp11_uart_tx_b,
+ msm_mux_blsp1_spi,
+ msm_mux_blsp2_spi,
+ msm_mux_blsp_i2c1,
+ msm_mux_blsp_i2c10,
+ msm_mux_blsp_i2c11,
+ msm_mux_blsp_i2c12,
+ msm_mux_blsp_i2c2,
+ msm_mux_blsp_i2c3,
+ msm_mux_blsp_i2c4,
+ msm_mux_blsp_i2c5,
+ msm_mux_blsp_i2c6,
+ msm_mux_blsp_i2c7,
+ msm_mux_blsp_i2c8,
+ msm_mux_blsp_i2c9,
+ msm_mux_blsp_spi1,
+ msm_mux_blsp_spi10,
+ msm_mux_blsp_spi11,
+ msm_mux_blsp_spi12,
+ msm_mux_blsp_spi2,
+ msm_mux_blsp_spi3,
+ msm_mux_blsp_spi4,
+ msm_mux_blsp_spi5,
+ msm_mux_blsp_spi6,
+ msm_mux_blsp_spi7,
+ msm_mux_blsp_spi8,
+ msm_mux_blsp_spi9,
+ msm_mux_blsp_uart1,
+ msm_mux_blsp_uart10,
+ msm_mux_blsp_uart11,
+ msm_mux_blsp_uart12,
+ msm_mux_blsp_uart2,
+ msm_mux_blsp_uart3,
+ msm_mux_blsp_uart4,
+ msm_mux_blsp_uart5,
+ msm_mux_blsp_uart6,
+ msm_mux_blsp_uart7,
+ msm_mux_blsp_uart8,
+ msm_mux_blsp_uart9,
+ msm_mux_blsp_uim1,
+ msm_mux_blsp_uim10,
+ msm_mux_blsp_uim11,
+ msm_mux_blsp_uim12,
+ msm_mux_blsp_uim2,
+ msm_mux_blsp_uim3,
+ msm_mux_blsp_uim4,
+ msm_mux_blsp_uim5,
+ msm_mux_blsp_uim6,
+ msm_mux_blsp_uim7,
+ msm_mux_blsp_uim8,
+ msm_mux_blsp_uim9,
+ msm_mux_btfm_slimbus,
+ msm_mux_cam_mclk,
+ msm_mux_cci_async,
+ msm_mux_cci_i2c,
+ msm_mux_cci_timer0,
+ msm_mux_cci_timer1,
+ msm_mux_cci_timer2,
+ msm_mux_cci_timer3,
+ msm_mux_cci_timer4,
+ msm_mux_cri_trng,
+ msm_mux_cri_trng0,
+ msm_mux_cri_trng1,
+ msm_mux_dac_calib0,
+ msm_mux_dac_calib1,
+ msm_mux_dac_calib10,
+ msm_mux_dac_calib11,
+ msm_mux_dac_calib12,
+ msm_mux_dac_calib13,
+ msm_mux_dac_calib14,
+ msm_mux_dac_calib15,
+ msm_mux_dac_calib16,
+ msm_mux_dac_calib17,
+ msm_mux_dac_calib18,
+ msm_mux_dac_calib19,
+ msm_mux_dac_calib2,
+ msm_mux_dac_calib20,
+ msm_mux_dac_calib21,
+ msm_mux_dac_calib22,
+ msm_mux_dac_calib23,
+ msm_mux_dac_calib24,
+ msm_mux_dac_calib25,
+ msm_mux_dac_calib26,
+ msm_mux_dac_calib3,
+ msm_mux_dac_calib4,
+ msm_mux_dac_calib5,
+ msm_mux_dac_calib6,
+ msm_mux_dac_calib7,
+ msm_mux_dac_calib8,
+ msm_mux_dac_calib9,
+ msm_mux_dac_gpio,
+ msm_mux_dbg_out,
+ msm_mux_ddr_bist,
+ msm_mux_edp_hot,
+ msm_mux_edp_lcd,
+ msm_mux_gcc_gp1_clk_a,
+ msm_mux_gcc_gp1_clk_b,
+ msm_mux_gcc_gp2_clk_a,
+ msm_mux_gcc_gp2_clk_b,
+ msm_mux_gcc_gp3_clk_a,
+ msm_mux_gcc_gp3_clk_b,
+ msm_mux_gsm_tx,
+ msm_mux_hdmi_cec,
+ msm_mux_hdmi_ddc,
+ msm_mux_hdmi_hot,
+ msm_mux_hdmi_rcv,
+ msm_mux_isense_dbg,
+ msm_mux_ldo_en,
+ msm_mux_ldo_update,
+ msm_mux_lpass_slimbus,
+ msm_mux_m_voc,
+ msm_mux_mdp_vsync,
+ msm_mux_mdp_vsync_p_b,
+ msm_mux_mdp_vsync_s_b,
+ msm_mux_modem_tsync,
+ msm_mux_mss_lte,
+ msm_mux_nav_dr,
+ msm_mux_nav_pps,
+ msm_mux_pa_indicator,
+ msm_mux_pci_e0,
+ msm_mux_pci_e1,
+ msm_mux_pci_e2,
+ msm_mux_pll_bypassnl,
+ msm_mux_pll_reset,
+ msm_mux_pri_mi2s,
+ msm_mux_prng_rosc,
+ msm_mux_pwr_crypto,
+ msm_mux_pwr_modem,
+ msm_mux_pwr_nav,
+ msm_mux_qdss_cti,
+ msm_mux_qdss_cti_trig_in_a,
+ msm_mux_qdss_cti_trig_in_b,
+ msm_mux_qdss_cti_trig_out_a,
+ msm_mux_qdss_cti_trig_out_b,
+ msm_mux_qdss_stm0,
+ msm_mux_qdss_stm1,
+ msm_mux_qdss_stm10,
+ msm_mux_qdss_stm11,
+ msm_mux_qdss_stm12,
+ msm_mux_qdss_stm13,
+ msm_mux_qdss_stm14,
+ msm_mux_qdss_stm15,
+ msm_mux_qdss_stm16,
+ msm_mux_qdss_stm17,
+ msm_mux_qdss_stm18,
+ msm_mux_qdss_stm19,
+ msm_mux_qdss_stm2,
+ msm_mux_qdss_stm20,
+ msm_mux_qdss_stm21,
+ msm_mux_qdss_stm22,
+ msm_mux_qdss_stm23,
+ msm_mux_qdss_stm24,
+ msm_mux_qdss_stm25,
+ msm_mux_qdss_stm26,
+ msm_mux_qdss_stm27,
+ msm_mux_qdss_stm28,
+ msm_mux_qdss_stm29,
+ msm_mux_qdss_stm3,
+ msm_mux_qdss_stm30,
+ msm_mux_qdss_stm31,
+ msm_mux_qdss_stm4,
+ msm_mux_qdss_stm5,
+ msm_mux_qdss_stm6,
+ msm_mux_qdss_stm7,
+ msm_mux_qdss_stm8,
+ msm_mux_qdss_stm9,
+ msm_mux_qdss_traceclk_a,
+ msm_mux_qdss_traceclk_b,
+ msm_mux_qdss_tracectl_a,
+ msm_mux_qdss_tracectl_b,
+ msm_mux_qdss_tracedata_11,
+ msm_mux_qdss_tracedata_12,
+ msm_mux_qdss_tracedata_a,
+ msm_mux_qdss_tracedata_b,
+ msm_mux_qspi0,
+ msm_mux_qspi1,
+ msm_mux_qspi2,
+ msm_mux_qspi3,
+ msm_mux_qspi_clk,
+ msm_mux_qspi_cs,
+ msm_mux_qua_mi2s,
+ msm_mux_sd_card,
+ msm_mux_sd_write,
+ msm_mux_sdc40,
+ msm_mux_sdc41,
+ msm_mux_sdc42,
+ msm_mux_sdc43,
+ msm_mux_sdc4_clk,
+ msm_mux_sdc4_cmd,
+ msm_mux_sec_mi2s,
+ msm_mux_spkr_i2s,
+ msm_mux_ssbi1,
+ msm_mux_ssbi2,
+ msm_mux_ssc_irq,
+ msm_mux_ter_mi2s,
+ msm_mux_tsense_pwm1,
+ msm_mux_tsense_pwm2,
+ msm_mux_tsif1_clk,
+ msm_mux_tsif1_data,
+ msm_mux_tsif1_en,
+ msm_mux_tsif1_error,
+ msm_mux_tsif1_sync,
+ msm_mux_tsif2_clk,
+ msm_mux_tsif2_data,
+ msm_mux_tsif2_en,
+ msm_mux_tsif2_error,
+ msm_mux_tsif2_sync,
+ msm_mux_uim1,
+ msm_mux_uim2,
+ msm_mux_uim3,
+ msm_mux_uim4,
+ msm_mux_uim_batt,
+ msm_mux_vfr_1,
+ msm_mux_gpio,
+ msm_mux_NA,
+};
+
+static const char * const gpio_groups[] = {
+ "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7",
+ "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14",
+ "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21",
+ "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28",
+ "gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35",
+ "gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42",
+ "gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", "gpio49",
+ "gpio50", "gpio51", "gpio52", "gpio53", "gpio54", "gpio55", "gpio56",
+ "gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63",
+ "gpio64", "gpio65", "gpio66", "gpio67", "gpio68", "gpio69", "gpio70",
+ "gpio71", "gpio72", "gpio73", "gpio74", "gpio75", "gpio76", "gpio77",
+ "gpio78", "gpio79", "gpio80", "gpio81", "gpio82", "gpio83", "gpio84",
+ "gpio85", "gpio86", "gpio87", "gpio88", "gpio89", "gpio90", "gpio91",
+ "gpio92", "gpio93", "gpio94", "gpio95", "gpio96", "gpio97", "gpio98",
+ "gpio99", "gpio100", "gpio101", "gpio102", "gpio103", "gpio104",
+ "gpio105", "gpio106", "gpio107", "gpio108", "gpio109", "gpio110",
+ "gpio111", "gpio112", "gpio113", "gpio114", "gpio115", "gpio116",
+ "gpio117", "gpio118", "gpio119", "gpio120", "gpio121", "gpio122",
+ "gpio123", "gpio124", "gpio125", "gpio126", "gpio127", "gpio128",
+ "gpio129", "gpio130", "gpio131", "gpio132", "gpio133", "gpio134",
+ "gpio135", "gpio136", "gpio137", "gpio138", "gpio139", "gpio140",
+ "gpio141", "gpio142", "gpio143", "gpio144", "gpio145", "gpio146",
+ "gpio147", "gpio148", "gpio149"
+};
+
+
+static const char * const blsp_uart1_groups[] = {
+ "gpio0", "gpio1", "gpio2", "gpio3",
+};
+static const char * const blsp_spi1_groups[] = {
+ "gpio0", "gpio1", "gpio2", "gpio3",
+};
+static const char * const blsp_i2c1_groups[] = {
+ "gpio2", "gpio3",
+};
+static const char * const blsp_uim1_groups[] = {
+ "gpio0", "gpio1",
+};
+static const char * const atest_tsens_groups[] = {
+ "gpio3",
+};
+static const char * const bimc_dte1_groups[] = {
+ "gpio3", "gpio5",
+};
+static const char * const blsp_spi8_groups[] = {
+ "gpio4", "gpio5", "gpio6", "gpio7",
+};
+static const char * const blsp_uart8_groups[] = {
+ "gpio4", "gpio5", "gpio6", "gpio7",
+};
+static const char * const blsp_uim8_groups[] = {
+ "gpio4", "gpio5",
+};
+static const char * const qdss_cti_trig_out_b_groups[] = {
+ "gpio4",
+};
+static const char * const dac_calib0_groups[] = {
+ "gpio4", "gpio41",
+};
+static const char * const bimc_dte0_groups[] = {
+ "gpio4", "gpio6",
+};
+static const char * const qdss_cti_trig_in_b_groups[] = {
+ "gpio5",
+};
+static const char * const dac_calib1_groups[] = {
+ "gpio5", "gpio42",
+};
+static const char * const dac_calib2_groups[] = {
+ "gpio6", "gpio43",
+};
+static const char * const atest_tsens2_groups[] = {
+ "gpio7",
+};
+static const char * const blsp_spi10_groups[] = {
+ "gpio8", "gpio9", "gpio10", "gpio11",
+};
+static const char * const blsp_uart10_groups[] = {
+ "gpio8", "gpio9", "gpio10", "gpio11",
+};
+static const char * const blsp_uim10_groups[] = {
+ "gpio8", "gpio9",
+};
+static const char * const atest_bbrx1_groups[] = {
+ "gpio8",
+};
+static const char * const atest_usb12_groups[] = {
+ "gpio9",
+};
+static const char * const mdp_vsync_groups[] = {
+ "gpio10", "gpio11", "gpio12",
+};
+static const char * const edp_lcd_groups[] = {
+ "gpio10",
+};
+static const char * const blsp_i2c10_groups[] = {
+ "gpio10", "gpio11",
+};
+static const char * const atest_usb11_groups[] = {
+ "gpio10",
+};
+static const char * const atest_gpsadc0_groups[] = {
+ "gpio11",
+};
+static const char * const edp_hot_groups[] = {
+ "gpio11",
+};
+static const char * const atest_usb10_groups[] = {
+ "gpio11",
+};
+static const char * const m_voc_groups[] = {
+ "gpio12",
+};
+static const char * const dac_gpio_groups[] = {
+ "gpio12",
+};
+static const char * const atest_char_groups[] = {
+ "gpio12",
+};
+static const char * const cam_mclk_groups[] = {
+ "gpio13", "gpio14", "gpio15", "gpio16",
+};
+static const char * const pll_bypassnl_groups[] = {
+ "gpio13",
+};
+static const char * const qdss_stm7_groups[] = {
+ "gpio13",
+};
+static const char * const blsp_i2c8_groups[] = {
+ "gpio6", "gpio7",
+};
+static const char * const atest_usb1_groups[] = {
+ "gpio7",
+};
+static const char * const atest_usb13_groups[] = {
+ "gpio8",
+};
+static const char * const atest_bbrx0_groups[] = {
+ "gpio9",
+};
+static const char * const atest_gpsadc1_groups[] = {
+ "gpio10",
+};
+static const char * const qdss_tracedata_b_groups[] = {
+ "gpio13", "gpio14", "gpio15", "gpio16", "gpio17", "gpio18", "gpio19",
+ "gpio21", "gpio22", "gpio23", "gpio26", "gpio29", "gpio57", "gpio58",
+ "gpio92", "gpio93",
+};
+static const char * const pll_reset_groups[] = {
+ "gpio14",
+};
+static const char * const qdss_stm6_groups[] = {
+ "gpio14",
+};
+static const char * const qdss_stm5_groups[] = {
+ "gpio15",
+};
+static const char * const qdss_stm4_groups[] = {
+ "gpio16",
+};
+static const char * const atest_usb2_groups[] = {
+ "gpio16",
+};
+static const char * const dac_calib3_groups[] = {
+ "gpio17", "gpio44",
+};
+static const char * const cci_i2c_groups[] = {
+ "gpio17", "gpio18", "gpio19", "gpio20",
+};
+static const char * const qdss_stm3_groups[] = {
+ "gpio17",
+};
+static const char * const atest_usb23_groups[] = {
+ "gpio17",
+};
+static const char * const atest_char3_groups[] = {
+ "gpio17",
+};
+static const char * const dac_calib4_groups[] = {
+ "gpio18", "gpio45",
+};
+static const char * const qdss_stm2_groups[] = {
+ "gpio18",
+};
+static const char * const atest_usb22_groups[] = {
+ "gpio18",
+};
+static const char * const atest_char2_groups[] = {
+ "gpio18",
+};
+static const char * const dac_calib5_groups[] = {
+ "gpio19", "gpio46",
+};
+static const char * const qdss_stm1_groups[] = {
+ "gpio19",
+};
+static const char * const atest_usb21_groups[] = {
+ "gpio19",
+};
+static const char * const atest_char1_groups[] = {
+ "gpio19",
+};
+static const char * const dac_calib6_groups[] = {
+ "gpio20", "gpio47",
+};
+static const char * const dbg_out_groups[] = {
+ "gpio20",
+};
+static const char * const qdss_stm0_groups[] = {
+ "gpio20",
+};
+static const char * const atest_usb20_groups[] = {
+ "gpio20",
+};
+static const char * const atest_char0_groups[] = {
+ "gpio20",
+};
+static const char * const dac_calib7_groups[] = {
+ "gpio21", "gpio48",
+};
+static const char * const cci_timer0_groups[] = {
+ "gpio21",
+};
+static const char * const qdss_stm13_groups[] = {
+ "gpio21",
+};
+static const char * const dac_calib8_groups[] = {
+ "gpio22", "gpio49",
+};
+static const char * const cci_timer1_groups[] = {
+ "gpio22",
+};
+static const char * const qdss_stm12_groups[] = {
+ "gpio22",
+};
+static const char * const dac_calib9_groups[] = {
+ "gpio23", "gpio50",
+};
+static const char * const cci_timer2_groups[] = {
+ "gpio23",
+};
+static const char * const qdss_stm11_groups[] = {
+ "gpio23",
+};
+static const char * const dac_calib10_groups[] = {
+ "gpio24", "gpio51",
+};
+static const char * const cci_timer3_groups[] = {
+ "gpio24",
+};
+static const char * const cci_async_groups[] = {
+ "gpio24", "gpio25", "gpio26",
+};
+static const char * const blsp1_spi_groups[] = {
+ "gpio24", "gpio27", "gpio28", "gpio90",
+};
+static const char * const qdss_stm10_groups[] = {
+ "gpio24",
+};
+static const char * const qdss_cti_trig_in_a_groups[] = {
+ "gpio24",
+};
+static const char * const dac_calib11_groups[] = {
+ "gpio25", "gpio52",
+};
+static const char * const cci_timer4_groups[] = {
+ "gpio25",
+};
+static const char * const blsp_spi6_groups[] = {
+ "gpio25", "gpio26", "gpio27", "gpio28",
+};
+static const char * const blsp_uart6_groups[] = {
+ "gpio25", "gpio26", "gpio27", "gpio28",
+};
+static const char * const blsp_uim6_groups[] = {
+ "gpio25", "gpio26",
+};
+static const char * const blsp2_spi_groups[] = {
+ "gpio25", "gpio29", "gpio30",
+};
+static const char * const qdss_stm9_groups[] = {
+ "gpio25",
+};
+static const char * const qdss_cti_trig_out_a_groups[] = {
+ "gpio25",
+};
+static const char * const dac_calib12_groups[] = {
+ "gpio26", "gpio53",
+};
+static const char * const qdss_stm8_groups[] = {
+ "gpio26",
+};
+static const char * const dac_calib13_groups[] = {
+ "gpio27", "gpio54",
+};
+static const char * const blsp_i2c6_groups[] = {
+ "gpio27", "gpio28",
+};
+static const char * const qdss_tracectl_a_groups[] = {
+ "gpio27",
+};
+static const char * const dac_calib14_groups[] = {
+ "gpio28", "gpio55",
+};
+static const char * const qdss_traceclk_a_groups[] = {
+ "gpio28",
+};
+static const char * const dac_calib15_groups[] = {
+ "gpio29", "gpio56",
+};
+static const char * const dac_calib16_groups[] = {
+ "gpio30", "gpio57",
+};
+static const char * const hdmi_rcv_groups[] = {
+ "gpio30",
+};
+static const char * const dac_calib17_groups[] = {
+ "gpio31", "gpio58",
+};
+static const char * const pwr_modem_groups[] = {
+ "gpio31",
+};
+static const char * const hdmi_cec_groups[] = {
+ "gpio31",
+};
+static const char * const pwr_nav_groups[] = {
+ "gpio32",
+};
+static const char * const dac_calib18_groups[] = {
+ "gpio32", "gpio59",
+};
+static const char * const hdmi_ddc_groups[] = {
+ "gpio32", "gpio33",
+};
+static const char * const pwr_crypto_groups[] = {
+ "gpio33",
+};
+static const char * const dac_calib19_groups[] = {
+ "gpio33", "gpio60",
+};
+static const char * const dac_calib20_groups[] = {
+ "gpio34", "gpio61",
+};
+static const char * const hdmi_hot_groups[] = {
+ "gpio34",
+};
+static const char * const dac_calib21_groups[] = {
+ "gpio35", "gpio62",
+};
+static const char * const pci_e0_groups[] = {
+ "gpio35", "gpio36",
+};
+static const char * const dac_calib22_groups[] = {
+ "gpio36", "gpio63",
+};
+static const char * const dac_calib23_groups[] = {
+ "gpio37", "gpio64",
+};
+static const char * const blsp_i2c2_groups[] = {
+ "gpio43", "gpio44",
+};
+static const char * const blsp_spi3_groups[] = {
+ "gpio45", "gpio46", "gpio47", "gpio48",
+};
+static const char * const blsp_uart3_groups[] = {
+ "gpio45", "gpio46", "gpio47", "gpio48",
+};
+static const char * const blsp_uim3_groups[] = {
+ "gpio45", "gpio46",
+};
+static const char * const blsp_i2c3_groups[] = {
+ "gpio47", "gpio48",
+};
+static const char * const dac_calib24_groups[] = {
+ "gpio38", "gpio65",
+};
+static const char * const dac_calib25_groups[] = {
+ "gpio39", "gpio66",
+};
+static const char * const tsif1_sync_groups[] = {
+ "gpio39",
+};
+static const char * const sd_write_groups[] = {
+ "gpio40",
+};
+static const char * const tsif1_error_groups[] = {
+ "gpio40",
+};
+static const char * const blsp_spi2_groups[] = {
+ "gpio41", "gpio42", "gpio43", "gpio44",
+};
+static const char * const blsp_uart2_groups[] = {
+ "gpio41", "gpio42", "gpio43", "gpio44",
+};
+static const char * const blsp_uim2_groups[] = {
+ "gpio41", "gpio42",
+};
+static const char * const qdss_cti_groups[] = {
+ "gpio41", "gpio42", "gpio100", "gpio101",
+};
+static const char * const uim3_groups[] = {
+ "gpio49", "gpio50", "gpio51", "gpio52",
+};
+static const char * const blsp_spi9_groups[] = {
+ "gpio49", "gpio50", "gpio51", "gpio52",
+};
+static const char * const blsp_uart9_groups[] = {
+ "gpio49", "gpio50", "gpio51", "gpio52",
+};
+static const char * const blsp_uim9_groups[] = {
+ "gpio49", "gpio50",
+};
+static const char * const blsp10_spi_groups[] = {
+ "gpio49", "gpio50", "gpio51", "gpio52", "gpio88",
+};
+static const char * const blsp_i2c9_groups[] = {
+ "gpio51", "gpio52",
+};
+static const char * const blsp_spi7_groups[] = {
+ "gpio53", "gpio54", "gpio55", "gpio56",
+};
+static const char * const blsp_uart7_groups[] = {
+ "gpio53", "gpio54", "gpio55", "gpio56",
+};
+static const char * const blsp_uim7_groups[] = {
+ "gpio53", "gpio54",
+};
+static const char * const qdss_tracedata_a_groups[] = {
+ "gpio53", "gpio54", "gpio63", "gpio64", "gpio65", "gpio66", "gpio67",
+ "gpio74", "gpio75", "gpio76", "gpio77", "gpio85", "gpio86", "gpio87",
+ "gpio89", "gpio90",
+};
+static const char * const blsp_i2c7_groups[] = {
+ "gpio55", "gpio56",
+};
+static const char * const qua_mi2s_groups[] = {
+ "gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63",
+};
+static const char * const gcc_gp1_clk_a_groups[] = {
+ "gpio57",
+};
+static const char * const uim4_groups[] = {
+ "gpio58", "gpio59", "gpio60", "gpio61",
+};
+static const char * const blsp_spi11_groups[] = {
+ "gpio58", "gpio59", "gpio60", "gpio61",
+};
+static const char * const blsp_uart11_groups[] = {
+ "gpio58", "gpio59", "gpio60", "gpio61",
+};
+static const char * const blsp_uim11_groups[] = {
+ "gpio58", "gpio59",
+};
+static const char * const gcc_gp2_clk_a_groups[] = {
+ "gpio58",
+};
+static const char * const gcc_gp3_clk_a_groups[] = {
+ "gpio59",
+};
+static const char * const blsp_i2c11_groups[] = {
+ "gpio60", "gpio61",
+};
+static const char * const cri_trng0_groups[] = {
+ "gpio60",
+};
+static const char * const cri_trng1_groups[] = {
+ "gpio61",
+};
+static const char * const cri_trng_groups[] = {
+ "gpio62",
+};
+static const char * const qdss_stm18_groups[] = {
+ "gpio63",
+};
+static const char * const pri_mi2s_groups[] = {
+ "gpio64", "gpio65", "gpio66", "gpio67", "gpio68",
+};
+static const char * const qdss_stm17_groups[] = {
+ "gpio64",
+};
+static const char * const blsp_spi4_groups[] = {
+ "gpio65", "gpio66", "gpio67", "gpio68",
+};
+static const char * const blsp_uart4_groups[] = {
+ "gpio65", "gpio66", "gpio67", "gpio68",
+};
+static const char * const blsp_uim4_groups[] = {
+ "gpio65", "gpio66",
+};
+static const char * const qdss_stm16_groups[] = {
+ "gpio65",
+};
+static const char * const qdss_stm15_groups[] = {
+ "gpio66",
+};
+static const char * const dac_calib26_groups[] = {
+ "gpio67",
+};
+static const char * const blsp_i2c4_groups[] = {
+ "gpio67", "gpio68",
+};
+static const char * const qdss_stm14_groups[] = {
+ "gpio67",
+};
+static const char * const spkr_i2s_groups[] = {
+ "gpio69", "gpio70", "gpio71", "gpio72",
+};
+static const char * const audio_ref_groups[] = {
+ "gpio69",
+};
+static const char * const lpass_slimbus_groups[] = {
+ "gpio70", "gpio71", "gpio72",
+};
+static const char * const isense_dbg_groups[] = {
+ "gpio70",
+};
+static const char * const tsense_pwm1_groups[] = {
+ "gpio71",
+};
+static const char * const tsense_pwm2_groups[] = {
+ "gpio71",
+};
+static const char * const btfm_slimbus_groups[] = {
+ "gpio73", "gpio74",
+};
+static const char * const ter_mi2s_groups[] = {
+ "gpio74", "gpio75", "gpio76", "gpio77", "gpio78",
+};
+static const char * const qdss_stm22_groups[] = {
+ "gpio74",
+};
+static const char * const qdss_stm21_groups[] = {
+ "gpio75",
+};
+static const char * const qdss_stm20_groups[] = {
+ "gpio76",
+};
+static const char * const qdss_stm19_groups[] = {
+ "gpio77",
+};
+static const char * const ssc_irq_groups[] = {
+ "gpio78", "gpio79", "gpio80", "gpio117", "gpio118", "gpio119",
+ "gpio120", "gpio121", "gpio122", "gpio123", "gpio124", "gpio125",
+};
+static const char * const gcc_gp1_clk_b_groups[] = {
+ "gpio78",
+};
+static const char * const sec_mi2s_groups[] = {
+ "gpio79", "gpio80", "gpio81", "gpio82", "gpio83",
+};
+static const char * const blsp_spi5_groups[] = {
+ "gpio81", "gpio82", "gpio83", "gpio84",
+};
+static const char * const blsp_uart5_groups[] = {
+ "gpio81", "gpio82", "gpio83", "gpio84",
+};
+static const char * const blsp_uim5_groups[] = {
+ "gpio81", "gpio82",
+};
+static const char * const gcc_gp2_clk_b_groups[] = {
+ "gpio81",
+};
+static const char * const gcc_gp3_clk_b_groups[] = {
+ "gpio82",
+};
+static const char * const blsp_i2c5_groups[] = {
+ "gpio83", "gpio84",
+};
+static const char * const blsp_spi12_groups[] = {
+ "gpio85", "gpio86", "gpio87", "gpio88",
+};
+static const char * const blsp_uart12_groups[] = {
+ "gpio85", "gpio86", "gpio87", "gpio88",
+};
+static const char * const blsp_uim12_groups[] = {
+ "gpio85", "gpio86",
+};
+static const char * const qdss_stm25_groups[] = {
+ "gpio85",
+};
+static const char * const qdss_stm31_groups[] = {
+ "gpio86",
+};
+static const char * const blsp_i2c12_groups[] = {
+ "gpio87", "gpio88",
+};
+static const char * const qdss_stm30_groups[] = {
+ "gpio87",
+};
+static const char * const qdss_stm29_groups[] = {
+ "gpio88",
+};
+static const char * const tsif1_clk_groups[] = {
+ "gpio89",
+};
+static const char * const qdss_stm28_groups[] = {
+ "gpio89",
+};
+static const char * const tsif1_en_groups[] = {
+ "gpio90",
+};
+static const char * const tsif1_data_groups[] = {
+ "gpio91",
+};
+static const char * const sdc4_cmd_groups[] = {
+ "gpio91",
+};
+static const char * const qdss_stm27_groups[] = {
+ "gpio91",
+};
+static const char * const qdss_traceclk_b_groups[] = {
+ "gpio91",
+};
+static const char * const tsif2_error_groups[] = {
+ "gpio92",
+};
+static const char * const sdc43_groups[] = {
+ "gpio92",
+};
+static const char * const vfr_1_groups[] = {
+ "gpio92",
+};
+static const char * const qdss_stm26_groups[] = {
+ "gpio92",
+};
+static const char * const tsif2_clk_groups[] = {
+ "gpio93",
+};
+static const char * const sdc4_clk_groups[] = {
+ "gpio93",
+};
+static const char * const qdss_stm24_groups[] = {
+ "gpio93",
+};
+static const char * const tsif2_en_groups[] = {
+ "gpio94",
+};
+static const char * const sdc42_groups[] = {
+ "gpio94",
+};
+static const char * const qdss_stm23_groups[] = {
+ "gpio94",
+};
+static const char * const qdss_tracectl_b_groups[] = {
+ "gpio94",
+};
+static const char * const sd_card_groups[] = {
+ "gpio95",
+};
+static const char * const tsif2_data_groups[] = {
+ "gpio95",
+};
+static const char * const sdc41_groups[] = {
+ "gpio95",
+};
+static const char * const tsif2_sync_groups[] = {
+ "gpio96",
+};
+static const char * const sdc40_groups[] = {
+ "gpio96",
+};
+static const char * const mdp_vsync_p_b_groups[] = {
+ "gpio97",
+};
+static const char * const ldo_en_groups[] = {
+ "gpio97",
+};
+static const char * const mdp_vsync_s_b_groups[] = {
+ "gpio98",
+};
+static const char * const ldo_update_groups[] = {
+ "gpio98",
+};
+static const char * const blsp11_uart_tx_b_groups[] = {
+ "gpio100",
+};
+static const char * const blsp11_uart_rx_b_groups[] = {
+ "gpio101",
+};
+static const char * const blsp11_i2c_sda_b_groups[] = {
+ "gpio102",
+};
+static const char * const prng_rosc_groups[] = {
+ "gpio102",
+};
+static const char * const blsp11_i2c_scl_b_groups[] = {
+ "gpio103",
+};
+static const char * const uim2_groups[] = {
+ "gpio105", "gpio106", "gpio107", "gpio108",
+};
+static const char * const uim1_groups[] = {
+ "gpio109", "gpio110", "gpio111", "gpio112",
+};
+static const char * const uim_batt_groups[] = {
+ "gpio113",
+};
+static const char * const pci_e2_groups[] = {
+ "gpio114", "gpio115", "gpio116",
+};
+static const char * const pa_indicator_groups[] = {
+ "gpio116",
+};
+static const char * const adsp_ext_groups[] = {
+ "gpio118",
+};
+static const char * const ddr_bist_groups[] = {
+ "gpio121", "gpio122", "gpio123", "gpio124",
+};
+static const char * const qdss_tracedata_11_groups[] = {
+ "gpio123",
+};
+static const char * const qdss_tracedata_12_groups[] = {
+ "gpio124",
+};
+static const char * const modem_tsync_groups[] = {
+ "gpio128",
+};
+static const char * const nav_dr_groups[] = {
+ "gpio128",
+};
+static const char * const nav_pps_groups[] = {
+ "gpio128",
+};
+static const char * const pci_e1_groups[] = {
+ "gpio130", "gpio131", "gpio132",
+};
+static const char * const gsm_tx_groups[] = {
+ "gpio134", "gpio135",
+};
+static const char * const qspi_cs_groups[] = {
+ "gpio138", "gpio141",
+};
+static const char * const ssbi2_groups[] = {
+ "gpio139",
+};
+static const char * const ssbi1_groups[] = {
+ "gpio140",
+};
+static const char * const mss_lte_groups[] = {
+ "gpio144", "gpio145",
+};
+static const char * const qspi_clk_groups[] = {
+ "gpio145",
+};
+static const char * const qspi0_groups[] = {
+ "gpio146",
+};
+static const char * const qspi1_groups[] = {
+ "gpio147",
+};
+static const char * const qspi2_groups[] = {
+ "gpio148",
+};
+static const char * const qspi3_groups[] = {
+ "gpio149",
+};
+
+static const struct msm_function msm8996_functions[] = {
+ FUNCTION(adsp_ext),
+ FUNCTION(atest_bbrx0),
+ FUNCTION(atest_bbrx1),
+ FUNCTION(atest_char),
+ FUNCTION(atest_char0),
+ FUNCTION(atest_char1),
+ FUNCTION(atest_char2),
+ FUNCTION(atest_char3),
+ FUNCTION(atest_gpsadc0),
+ FUNCTION(atest_gpsadc1),
+ FUNCTION(atest_tsens),
+ FUNCTION(atest_tsens2),
+ FUNCTION(atest_usb1),
+ FUNCTION(atest_usb10),
+ FUNCTION(atest_usb11),
+ FUNCTION(atest_usb12),
+ FUNCTION(atest_usb13),
+ FUNCTION(atest_usb2),
+ FUNCTION(atest_usb20),
+ FUNCTION(atest_usb21),
+ FUNCTION(atest_usb22),
+ FUNCTION(atest_usb23),
+ FUNCTION(audio_ref),
+ FUNCTION(bimc_dte0),
+ FUNCTION(bimc_dte1),
+ FUNCTION(blsp10_spi),
+ FUNCTION(blsp11_i2c_scl_b),
+ FUNCTION(blsp11_i2c_sda_b),
+ FUNCTION(blsp11_uart_rx_b),
+ FUNCTION(blsp11_uart_tx_b),
+ FUNCTION(blsp1_spi),
+ FUNCTION(blsp2_spi),
+ FUNCTION(blsp_i2c1),
+ FUNCTION(blsp_i2c10),
+ FUNCTION(blsp_i2c11),
+ FUNCTION(blsp_i2c12),
+ FUNCTION(blsp_i2c2),
+ FUNCTION(blsp_i2c3),
+ FUNCTION(blsp_i2c4),
+ FUNCTION(blsp_i2c5),
+ FUNCTION(blsp_i2c6),
+ FUNCTION(blsp_i2c7),
+ FUNCTION(blsp_i2c8),
+ FUNCTION(blsp_i2c9),
+ FUNCTION(blsp_spi1),
+ FUNCTION(blsp_spi10),
+ FUNCTION(blsp_spi11),
+ FUNCTION(blsp_spi12),
+ FUNCTION(blsp_spi2),
+ FUNCTION(blsp_spi3),
+ FUNCTION(blsp_spi4),
+ FUNCTION(blsp_spi5),
+ FUNCTION(blsp_spi6),
+ FUNCTION(blsp_spi7),
+ FUNCTION(blsp_spi8),
+ FUNCTION(blsp_spi9),
+ FUNCTION(blsp_uart1),
+ FUNCTION(blsp_uart10),
+ FUNCTION(blsp_uart11),
+ FUNCTION(blsp_uart12),
+ FUNCTION(blsp_uart2),
+ FUNCTION(blsp_uart3),
+ FUNCTION(blsp_uart4),
+ FUNCTION(blsp_uart5),
+ FUNCTION(blsp_uart6),
+ FUNCTION(blsp_uart7),
+ FUNCTION(blsp_uart8),
+ FUNCTION(blsp_uart9),
+ FUNCTION(blsp_uim1),
+ FUNCTION(blsp_uim10),
+ FUNCTION(blsp_uim11),
+ FUNCTION(blsp_uim12),
+ FUNCTION(blsp_uim2),
+ FUNCTION(blsp_uim3),
+ FUNCTION(blsp_uim4),
+ FUNCTION(blsp_uim5),
+ FUNCTION(blsp_uim6),
+ FUNCTION(blsp_uim7),
+ FUNCTION(blsp_uim8),
+ FUNCTION(blsp_uim9),
+ FUNCTION(btfm_slimbus),
+ FUNCTION(cam_mclk),
+ FUNCTION(cci_async),
+ FUNCTION(cci_i2c),
+ FUNCTION(cci_timer0),
+ FUNCTION(cci_timer1),
+ FUNCTION(cci_timer2),
+ FUNCTION(cci_timer3),
+ FUNCTION(cci_timer4),
+ FUNCTION(cri_trng),
+ FUNCTION(cri_trng0),
+ FUNCTION(cri_trng1),
+ FUNCTION(dac_calib0),
+ FUNCTION(dac_calib1),
+ FUNCTION(dac_calib10),
+ FUNCTION(dac_calib11),
+ FUNCTION(dac_calib12),
+ FUNCTION(dac_calib13),
+ FUNCTION(dac_calib14),
+ FUNCTION(dac_calib15),
+ FUNCTION(dac_calib16),
+ FUNCTION(dac_calib17),
+ FUNCTION(dac_calib18),
+ FUNCTION(dac_calib19),
+ FUNCTION(dac_calib2),
+ FUNCTION(dac_calib20),
+ FUNCTION(dac_calib21),
+ FUNCTION(dac_calib22),
+ FUNCTION(dac_calib23),
+ FUNCTION(dac_calib24),
+ FUNCTION(dac_calib25),
+ FUNCTION(dac_calib26),
+ FUNCTION(dac_calib3),
+ FUNCTION(dac_calib4),
+ FUNCTION(dac_calib5),
+ FUNCTION(dac_calib6),
+ FUNCTION(dac_calib7),
+ FUNCTION(dac_calib8),
+ FUNCTION(dac_calib9),
+ FUNCTION(dac_gpio),
+ FUNCTION(dbg_out),
+ FUNCTION(ddr_bist),
+ FUNCTION(edp_hot),
+ FUNCTION(edp_lcd),
+ FUNCTION(gcc_gp1_clk_a),
+ FUNCTION(gcc_gp1_clk_b),
+ FUNCTION(gcc_gp2_clk_a),
+ FUNCTION(gcc_gp2_clk_b),
+ FUNCTION(gcc_gp3_clk_a),
+ FUNCTION(gcc_gp3_clk_b),
+ FUNCTION(gpio),
+ FUNCTION(gsm_tx),
+ FUNCTION(hdmi_cec),
+ FUNCTION(hdmi_ddc),
+ FUNCTION(hdmi_hot),
+ FUNCTION(hdmi_rcv),
+ FUNCTION(isense_dbg),
+ FUNCTION(ldo_en),
+ FUNCTION(ldo_update),
+ FUNCTION(lpass_slimbus),
+ FUNCTION(m_voc),
+ FUNCTION(mdp_vsync),
+ FUNCTION(mdp_vsync_p_b),
+ FUNCTION(mdp_vsync_s_b),
+ FUNCTION(modem_tsync),
+ FUNCTION(mss_lte),
+ FUNCTION(nav_dr),
+ FUNCTION(nav_pps),
+ FUNCTION(pa_indicator),
+ FUNCTION(pci_e0),
+ FUNCTION(pci_e1),
+ FUNCTION(pci_e2),
+ FUNCTION(pll_bypassnl),
+ FUNCTION(pll_reset),
+ FUNCTION(pri_mi2s),
+ FUNCTION(prng_rosc),
+ FUNCTION(pwr_crypto),
+ FUNCTION(pwr_modem),
+ FUNCTION(pwr_nav),
+ FUNCTION(qdss_cti),
+ FUNCTION(qdss_cti_trig_in_a),
+ FUNCTION(qdss_cti_trig_in_b),
+ FUNCTION(qdss_cti_trig_out_a),
+ FUNCTION(qdss_cti_trig_out_b),
+ FUNCTION(qdss_stm0),
+ FUNCTION(qdss_stm1),
+ FUNCTION(qdss_stm10),
+ FUNCTION(qdss_stm11),
+ FUNCTION(qdss_stm12),
+ FUNCTION(qdss_stm13),
+ FUNCTION(qdss_stm14),
+ FUNCTION(qdss_stm15),
+ FUNCTION(qdss_stm16),
+ FUNCTION(qdss_stm17),
+ FUNCTION(qdss_stm18),
+ FUNCTION(qdss_stm19),
+ FUNCTION(qdss_stm2),
+ FUNCTION(qdss_stm20),
+ FUNCTION(qdss_stm21),
+ FUNCTION(qdss_stm22),
+ FUNCTION(qdss_stm23),
+ FUNCTION(qdss_stm24),
+ FUNCTION(qdss_stm25),
+ FUNCTION(qdss_stm26),
+ FUNCTION(qdss_stm27),
+ FUNCTION(qdss_stm28),
+ FUNCTION(qdss_stm29),
+ FUNCTION(qdss_stm3),
+ FUNCTION(qdss_stm30),
+ FUNCTION(qdss_stm31),
+ FUNCTION(qdss_stm4),
+ FUNCTION(qdss_stm5),
+ FUNCTION(qdss_stm6),
+ FUNCTION(qdss_stm7),
+ FUNCTION(qdss_stm8),
+ FUNCTION(qdss_stm9),
+ FUNCTION(qdss_traceclk_a),
+ FUNCTION(qdss_traceclk_b),
+ FUNCTION(qdss_tracectl_a),
+ FUNCTION(qdss_tracectl_b),
+ FUNCTION(qdss_tracedata_11),
+ FUNCTION(qdss_tracedata_12),
+ FUNCTION(qdss_tracedata_a),
+ FUNCTION(qdss_tracedata_b),
+ FUNCTION(qspi0),
+ FUNCTION(qspi1),
+ FUNCTION(qspi2),
+ FUNCTION(qspi3),
+ FUNCTION(qspi_clk),
+ FUNCTION(qspi_cs),
+ FUNCTION(qua_mi2s),
+ FUNCTION(sd_card),
+ FUNCTION(sd_write),
+ FUNCTION(sdc40),
+ FUNCTION(sdc41),
+ FUNCTION(sdc42),
+ FUNCTION(sdc43),
+ FUNCTION(sdc4_clk),
+ FUNCTION(sdc4_cmd),
+ FUNCTION(sec_mi2s),
+ FUNCTION(spkr_i2s),
+ FUNCTION(ssbi1),
+ FUNCTION(ssbi2),
+ FUNCTION(ssc_irq),
+ FUNCTION(ter_mi2s),
+ FUNCTION(tsense_pwm1),
+ FUNCTION(tsense_pwm2),
+ FUNCTION(tsif1_clk),
+ FUNCTION(tsif1_data),
+ FUNCTION(tsif1_en),
+ FUNCTION(tsif1_error),
+ FUNCTION(tsif1_sync),
+ FUNCTION(tsif2_clk),
+ FUNCTION(tsif2_data),
+ FUNCTION(tsif2_en),
+ FUNCTION(tsif2_error),
+ FUNCTION(tsif2_sync),
+ FUNCTION(uim1),
+ FUNCTION(uim2),
+ FUNCTION(uim3),
+ FUNCTION(uim4),
+ FUNCTION(uim_batt),
+ FUNCTION(vfr_1),
+};
+
+static const struct msm_pingroup msm8996_groups[] = {
+ PINGROUP(0, blsp_spi1, blsp_uart1, blsp_uim1, NA, NA, NA, NA, NA, NA),
+ PINGROUP(1, blsp_spi1, blsp_uart1, blsp_uim1, NA, NA, NA, NA, NA, NA),
+ PINGROUP(2, blsp_spi1, blsp_uart1, blsp_i2c1, NA, NA, NA, NA, NA, NA),
+ PINGROUP(3, blsp_spi1, blsp_uart1, blsp_i2c1, NA, atest_tsens,
+ bimc_dte1, NA, NA, NA),
+ PINGROUP(4, blsp_spi8, blsp_uart8, blsp_uim8, NA, qdss_cti_trig_out_b,
+ dac_calib0, bimc_dte0, NA, NA),
+ PINGROUP(5, blsp_spi8, blsp_uart8, blsp_uim8, NA, qdss_cti_trig_in_b,
+ dac_calib1, bimc_dte1, NA, NA),
+ PINGROUP(6, blsp_spi8, blsp_uart8, blsp_i2c8, NA, dac_calib2,
+ bimc_dte0, NA, NA, NA),
+ PINGROUP(7, blsp_spi8, blsp_uart8, blsp_i2c8, NA, atest_tsens2,
+ atest_usb1, NA, NA, NA),
+ PINGROUP(8, blsp_spi10, blsp_uart10, blsp_uim10, NA, atest_bbrx1,
+ atest_usb13, NA, NA, NA),
+ PINGROUP(9, blsp_spi10, blsp_uart10, blsp_uim10, atest_bbrx0,
+ atest_usb12, NA, NA, NA, NA),
+ PINGROUP(10, mdp_vsync, blsp_spi10, blsp_uart10, blsp_i2c10,
+ atest_gpsadc1, atest_usb11, NA, NA, NA),
+ PINGROUP(11, mdp_vsync, blsp_spi10, blsp_uart10, blsp_i2c10,
+ atest_gpsadc0, atest_usb10, NA, NA, NA),
+ PINGROUP(12, mdp_vsync, m_voc, dac_gpio, atest_char, NA, NA, NA, NA,
+ NA),
+ PINGROUP(13, cam_mclk, pll_bypassnl, qdss_stm7, qdss_tracedata_b, NA,
+ NA, NA, NA, NA),
+ PINGROUP(14, cam_mclk, pll_reset, qdss_stm6, qdss_tracedata_b, NA, NA,
+ NA, NA, NA),
+ PINGROUP(15, cam_mclk, qdss_stm5, qdss_tracedata_b, NA, NA, NA, NA, NA,
+ NA),
+ PINGROUP(16, cam_mclk, qdss_stm4, qdss_tracedata_b, NA, atest_usb2, NA,
+ NA, NA, NA),
+ PINGROUP(17, cci_i2c, qdss_stm3, qdss_tracedata_b, dac_calib3,
+ atest_usb23, atest_char3, NA, NA, NA),
+ PINGROUP(18, cci_i2c, qdss_stm2, qdss_tracedata_b, dac_calib4,
+ atest_usb22, atest_char2, NA, NA, NA),
+ PINGROUP(19, cci_i2c, qdss_stm1, qdss_tracedata_b, dac_calib5,
+ atest_usb21, atest_char1, NA, NA, NA),
+ PINGROUP(20, cci_i2c, dbg_out, qdss_stm0, dac_calib6, atest_usb20,
+ atest_char0, NA, NA, NA),
+ PINGROUP(21, cci_timer0, qdss_stm13, qdss_tracedata_b, dac_calib7, NA,
+ NA, NA, NA, NA),
+ PINGROUP(22, cci_timer1, qdss_stm12, qdss_tracedata_b, dac_calib8, NA,
+ NA, NA, NA, NA),
+ PINGROUP(23, cci_timer2, blsp1_spi, qdss_stm11, qdss_tracedata_b,
+ dac_calib9, NA, NA, NA, NA),
+ PINGROUP(24, cci_timer3, cci_async, blsp1_spi, qdss_stm10,
+ qdss_cti_trig_in_a, dac_calib10, NA, NA, NA),
+ PINGROUP(25, cci_timer4, cci_async, blsp_spi6, blsp_uart6, blsp_uim6,
+ blsp2_spi, qdss_stm9, qdss_cti_trig_out_a, dac_calib11),
+ PINGROUP(26, cci_async, blsp_spi6, blsp_uart6, blsp_uim6, qdss_stm8,
+ qdss_tracedata_b, dac_calib12, NA, NA),
+ PINGROUP(27, blsp_spi6, blsp_uart6, blsp_i2c6, blsp1_spi,
+ qdss_tracectl_a, dac_calib13, NA, NA, NA),
+ PINGROUP(28, blsp_spi6, blsp_uart6, blsp_i2c6, blsp1_spi,
+ qdss_traceclk_a, dac_calib14, NA, NA, NA),
+ PINGROUP(29, blsp2_spi, NA, qdss_tracedata_b, dac_calib15, NA, NA, NA,
+ NA, NA),
+ PINGROUP(30, hdmi_rcv, blsp2_spi, dac_calib16, NA, NA, NA, NA, NA, NA),
+ PINGROUP(31, hdmi_cec, pwr_modem, dac_calib17, NA, NA, NA, NA, NA, NA),
+ PINGROUP(32, hdmi_ddc, pwr_nav, NA, dac_calib18, NA, NA, NA, NA, NA),
+ PINGROUP(33, hdmi_ddc, pwr_crypto, NA, dac_calib19, NA, NA, NA, NA, NA),
+ PINGROUP(34, hdmi_hot, NA, dac_calib20, NA, NA, NA, NA, NA, NA),
+ PINGROUP(35, pci_e0, NA, dac_calib21, NA, NA, NA, NA, NA, NA),
+ PINGROUP(36, pci_e0, NA, dac_calib22, NA, NA, NA, NA, NA, NA),
+ PINGROUP(37, NA, dac_calib23, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(38, NA, dac_calib24, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(39, tsif1_sync, NA, dac_calib25, NA, NA, NA, NA, NA, NA),
+ PINGROUP(40, sd_write, tsif1_error, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(41, blsp_spi2, blsp_uart2, blsp_uim2, NA, qdss_cti,
+ dac_calib0, NA, NA, NA),
+ PINGROUP(42, blsp_spi2, blsp_uart2, blsp_uim2, NA, qdss_cti,
+ dac_calib1, NA, NA, NA),
+ PINGROUP(43, blsp_spi2, blsp_uart2, blsp_i2c2, NA, dac_calib2, NA, NA,
+ NA, NA),
+ PINGROUP(44, blsp_spi2, blsp_uart2, blsp_i2c2, NA, dac_calib3, NA, NA,
+ NA, NA),
+ PINGROUP(45, blsp_spi3, blsp_uart3, blsp_uim3, NA, dac_calib4, NA, NA,
+ NA, NA),
+ PINGROUP(46, blsp_spi3, blsp_uart3, blsp_uim3, NA, dac_calib5, NA, NA,
+ NA, NA),
+ PINGROUP(47, blsp_spi3, blsp_uart3, blsp_i2c3, dac_calib6, NA, NA, NA,
+ NA, NA),
+ PINGROUP(48, blsp_spi3, blsp_uart3, blsp_i2c3, dac_calib7, NA, NA, NA,
+ NA, NA),
+ PINGROUP(49, uim3, blsp_spi9, blsp_uart9, blsp_uim9, blsp10_spi,
+ dac_calib8, NA, NA, NA),
+ PINGROUP(50, uim3, blsp_spi9, blsp_uart9, blsp_uim9, blsp10_spi,
+ dac_calib9, NA, NA, NA),
+ PINGROUP(51, uim3, blsp_spi9, blsp_uart9, blsp_i2c9, blsp10_spi,
+ dac_calib10, NA, NA, NA),
+ PINGROUP(52, uim3, blsp_spi9, blsp_uart9, blsp_i2c9,
+ blsp10_spi, dac_calib11, NA, NA, NA),
+ PINGROUP(53, blsp_spi7, blsp_uart7, blsp_uim7, NA, qdss_tracedata_a,
+ dac_calib12, NA, NA, NA),
+ PINGROUP(54, blsp_spi7, blsp_uart7, blsp_uim7, NA, NA,
+ qdss_tracedata_a, dac_calib13, NA, NA),
+ PINGROUP(55, blsp_spi7, blsp_uart7, blsp_i2c7, NA, dac_calib14, NA, NA,
+ NA, NA),
+ PINGROUP(56, blsp_spi7, blsp_uart7, blsp_i2c7, NA, dac_calib15, NA, NA,
+ NA, NA),
+ PINGROUP(57, qua_mi2s, gcc_gp1_clk_a, NA, qdss_tracedata_b,
+ dac_calib16, NA, NA, NA, NA),
+ PINGROUP(58, qua_mi2s, uim4, blsp_spi11, blsp_uart11, blsp_uim11,
+ gcc_gp2_clk_a, NA, qdss_tracedata_b, dac_calib17),
+ PINGROUP(59, qua_mi2s, uim4, blsp_spi11, blsp_uart11, blsp_uim11,
+ gcc_gp3_clk_a, NA, dac_calib18, NA),
+ PINGROUP(60, qua_mi2s, uim4, blsp_spi11, blsp_uart11, blsp_i2c11,
+ cri_trng0, NA, dac_calib19, NA),
+ PINGROUP(61, qua_mi2s, uim4, blsp_spi11, blsp_uart11,
+ blsp_i2c11, cri_trng1, NA, dac_calib20, NA),
+ PINGROUP(62, qua_mi2s, cri_trng, NA, dac_calib21, NA, NA, NA, NA, NA),
+ PINGROUP(63, qua_mi2s, NA, NA, qdss_stm18, qdss_tracedata_a,
+ dac_calib22, NA, NA, NA),
+ PINGROUP(64, pri_mi2s, NA, qdss_stm17, qdss_tracedata_a, dac_calib23,
+ NA, NA, NA, NA),
+ PINGROUP(65, pri_mi2s, blsp_spi4, blsp_uart4, blsp_uim4, NA,
+ qdss_stm16, qdss_tracedata_a, dac_calib24, NA),
+ PINGROUP(66, pri_mi2s, blsp_spi4, blsp_uart4, blsp_uim4, NA,
+ qdss_stm15, qdss_tracedata_a, dac_calib25, NA),
+ PINGROUP(67, pri_mi2s, blsp_spi4, blsp_uart4, blsp_i2c4, qdss_stm14,
+ qdss_tracedata_a, dac_calib26, NA, NA),
+ PINGROUP(68, pri_mi2s, blsp_spi4, blsp_uart4, blsp_i2c4, NA, NA, NA,
+ NA, NA),
+ PINGROUP(69, spkr_i2s, audio_ref, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(70, lpass_slimbus, spkr_i2s, isense_dbg, NA, NA, NA, NA, NA,
+ NA),
+ PINGROUP(71, lpass_slimbus, spkr_i2s, tsense_pwm1, tsense_pwm2, NA, NA,
+ NA, NA, NA),
+ PINGROUP(72, lpass_slimbus, spkr_i2s, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(73, btfm_slimbus, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(74, btfm_slimbus, ter_mi2s, qdss_stm22, qdss_tracedata_a, NA,
+ NA, NA, NA, NA),
+ PINGROUP(75, ter_mi2s, qdss_stm21, qdss_tracedata_a, NA, NA, NA, NA,
+ NA, NA),
+ PINGROUP(76, ter_mi2s, qdss_stm20, qdss_tracedata_a, NA, NA, NA, NA,
+ NA, NA),
+ PINGROUP(77, ter_mi2s, qdss_stm19, qdss_tracedata_a, NA, NA, NA, NA,
+ NA, NA),
+ PINGROUP(78, ter_mi2s, gcc_gp1_clk_b, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(79, sec_mi2s, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(80, sec_mi2s, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(81, sec_mi2s, blsp_spi5, blsp_uart5, blsp_uim5, gcc_gp2_clk_b,
+ NA, NA, NA, NA),
+ PINGROUP(82, sec_mi2s, blsp_spi5, blsp_uart5, blsp_uim5, gcc_gp3_clk_b,
+ NA, NA, NA, NA),
+ PINGROUP(83, sec_mi2s, blsp_spi5, blsp_uart5, blsp_i2c5, NA, NA, NA,
+ NA, NA),
+ PINGROUP(84, blsp_spi5, blsp_uart5, blsp_i2c5, NA, NA, NA, NA, NA, NA),
+ PINGROUP(85, blsp_spi12, blsp_uart12, blsp_uim12, NA, qdss_stm25,
+ qdss_tracedata_a, NA, NA, NA),
+ PINGROUP(86, blsp_spi12, blsp_uart12, blsp_uim12, NA, NA, qdss_stm31,
+ qdss_tracedata_a, NA, NA),
+ PINGROUP(87, blsp_spi12, blsp_uart12, blsp_i2c12, NA, qdss_stm30,
+ qdss_tracedata_a, NA, NA, NA),
+ PINGROUP(88, blsp_spi12, blsp_uart12, blsp_i2c12, blsp10_spi, NA,
+ qdss_stm29, NA, NA, NA),
+ PINGROUP(89, tsif1_clk, qdss_stm28, qdss_tracedata_a, NA, NA, NA, NA,
+ NA, NA),
+ PINGROUP(90, tsif1_en, blsp1_spi, qdss_tracedata_a, NA, NA, NA, NA, NA,
+ NA),
+ PINGROUP(91, tsif1_data, sdc4_cmd, qdss_stm27, qdss_traceclk_b, NA, NA,
+ NA, NA, NA),
+ PINGROUP(92, tsif2_error, sdc43, vfr_1, qdss_stm26, qdss_tracedata_b,
+ NA, NA, NA, NA),
+ PINGROUP(93, tsif2_clk, sdc4_clk, NA, qdss_stm24, qdss_tracedata_b, NA,
+ NA, NA, NA),
+ PINGROUP(94, tsif2_en, sdc42, NA, qdss_stm23, qdss_tracectl_b, NA, NA,
+ NA, NA),
+ PINGROUP(95, tsif2_data, sdc41, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(96, tsif2_sync, sdc40, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(97, NA, NA, mdp_vsync_p_b, ldo_en, NA, NA, NA, NA, NA),
+ PINGROUP(98, NA, NA, mdp_vsync_s_b, ldo_update, NA, NA, NA, NA, NA),
+ PINGROUP(99, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(100, NA, NA, blsp11_uart_tx_b, qdss_cti, NA, NA, NA, NA, NA),
+ PINGROUP(101, NA, blsp11_uart_rx_b, qdss_cti, NA, NA, NA, NA, NA, NA),
+ PINGROUP(102, NA, blsp11_i2c_sda_b, prng_rosc, NA, NA, NA, NA, NA, NA),
+ PINGROUP(103, NA, blsp11_i2c_scl_b, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(104, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(105, uim2, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(106, uim2, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(107, uim2, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(108, uim2, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(109, uim1, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(110, uim1, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(111, uim1, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(112, uim1, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(113, uim_batt, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(114, NA, pci_e2, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(115, NA, pci_e2, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(116, NA, pa_indicator, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(117, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(118, adsp_ext, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(119, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(120, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(121, ddr_bist, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(122, ddr_bist, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(123, ddr_bist, qdss_tracedata_11, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(124, ddr_bist, qdss_tracedata_12, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(125, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(126, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(127, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(128, NA, modem_tsync, nav_dr, nav_pps, NA, NA, NA, NA, NA),
+ PINGROUP(129, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(130, pci_e1, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(131, pci_e1, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(132, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(133, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(134, gsm_tx, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(135, gsm_tx, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(136, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(137, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(138, NA, qspi_cs, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(139, NA, ssbi2, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(140, NA, ssbi1, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(141, NA, qspi_cs, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(142, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(143, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(144, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(145, mss_lte, qspi_clk, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(146, NA, qspi0, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(147, NA, qspi1, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(148, NA, qspi2, NA, NA, NA, NA, NA, NA, NA),
+ PINGROUP(149, NA, qspi3, NA, NA, NA, NA, NA, NA, NA),
+ SDC_QDSD_PINGROUP(sdc1_clk, 0x12c000, 13, 6),
+ SDC_QDSD_PINGROUP(sdc1_cmd, 0x12c000, 11, 3),
+ SDC_QDSD_PINGROUP(sdc1_data, 0x12c000, 9, 0),
+ SDC_QDSD_PINGROUP(sdc2_clk, 0x12d000, 14, 6),
+ SDC_QDSD_PINGROUP(sdc2_cmd, 0x12d000, 11, 3),
+ SDC_QDSD_PINGROUP(sdc2_data, 0x12d000, 9, 0),
+ SDC_QDSD_PINGROUP(sdc1_rclk, 0x12c000, 15, 0),
+};
+
+static const struct msm_pinctrl_soc_data msm8996_pinctrl = {
+ .pins = msm8996_pins,
+ .npins = ARRAY_SIZE(msm8996_pins),
+ .functions = msm8996_functions,
+ .nfunctions = ARRAY_SIZE(msm8996_functions),
+ .groups = msm8996_groups,
+ .ngroups = ARRAY_SIZE(msm8996_groups),
+ .ngpios = 150,
+};
+
+static int msm8996_pinctrl_probe(struct platform_device *pdev)
+{
+ return msm_pinctrl_probe(pdev, &msm8996_pinctrl);
+}
+
+static const struct of_device_id msm8996_pinctrl_of_match[] = {
+ { .compatible = "qcom,msm8996-pinctrl", },
+ { }
+};
+
+static struct platform_driver msm8996_pinctrl_driver = {
+ .driver = {
+ .name = "msm8996-pinctrl",
+ .of_match_table = msm8996_pinctrl_of_match,
+ },
+ .probe = msm8996_pinctrl_probe,
+ .remove = msm_pinctrl_remove,
+};
+
+static int __init msm8996_pinctrl_init(void)
+{
+ return platform_driver_register(&msm8996_pinctrl_driver);
+}
+arch_initcall(msm8996_pinctrl_init);
+
+static void __exit msm8996_pinctrl_exit(void)
+{
+ platform_driver_unregister(&msm8996_pinctrl_driver);
+}
+module_exit(msm8996_pinctrl_exit);
+
+MODULE_DESCRIPTION("Qualcomm msm8996 pinctrl driver");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, msm8996_pinctrl_of_match);
diff --git a/drivers/pinctrl/qcom/pinctrl-qdf2xxx.c b/drivers/pinctrl/qcom/pinctrl-qdf2xxx.c
index e9ff3bc150bb..f448534edf46 100644
--- a/drivers/pinctrl/qcom/pinctrl-qdf2xxx.c
+++ b/drivers/pinctrl/qcom/pinctrl-qdf2xxx.c
@@ -32,6 +32,9 @@
static struct msm_pinctrl_soc_data qdf2xxx_pinctrl;
+/* A reasonable limit to the number of GPIOS */
+#define MAX_GPIOS 256
+
static int qdf2xxx_pinctrl_probe(struct platform_device *pdev)
{
struct pinctrl_pin_desc *pins;
@@ -42,11 +45,13 @@ static int qdf2xxx_pinctrl_probe(struct platform_device *pdev)
/* Query the number of GPIOs from ACPI */
ret = device_property_read_u32(&pdev->dev, "num-gpios", &num_gpios);
- if (ret < 0)
+ if (ret < 0) {
+ dev_warn(&pdev->dev, "missing num-gpios property\n");
return ret;
+ }
- if (!num_gpios) {
- dev_warn(&pdev->dev, "missing num-gpios property\n");
+ if (!num_gpios || num_gpios > MAX_GPIOS) {
+ dev_warn(&pdev->dev, "invalid num-gpios property\n");
return -ENODEV;
}
@@ -55,6 +60,9 @@ static int qdf2xxx_pinctrl_probe(struct platform_device *pdev)
groups = devm_kcalloc(&pdev->dev, num_gpios,
sizeof(struct msm_pingroup), GFP_KERNEL);
+ if (!pins || !groups)
+ return -ENOMEM;
+
for (i = 0; i < num_gpios; i++) {
pins[i].number = i;
diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
index 6c42ca14d2fd..77f6a5cb1008 100644
--- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
+++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
@@ -14,6 +14,7 @@
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinmux.h>
@@ -693,18 +694,19 @@ static int pmic_gpio_probe(struct platform_device *pdev)
struct pmic_gpio_pad *pad, *pads;
struct pmic_gpio_state *state;
int ret, npins, i;
- u32 res[2];
+ u32 reg;
- ret = of_property_read_u32_array(dev->of_node, "reg", res, 2);
+ ret = of_property_read_u32(dev->of_node, "reg", &reg);
if (ret < 0) {
- dev_err(dev, "missing base address and/or range");
+ dev_err(dev, "missing base address");
return ret;
}
- npins = res[1] / PMIC_GPIO_ADDRESS_RANGE;
-
+ npins = platform_irq_count(pdev);
if (!npins)
return -EINVAL;
+ if (npins < 0)
+ return npins;
BUG_ON(npins > ARRAY_SIZE(pmic_gpio_groups));
@@ -752,7 +754,7 @@ static int pmic_gpio_probe(struct platform_device *pdev)
if (pad->irq < 0)
return pad->irq;
- pad->base = res[0] + i * PMIC_GPIO_ADDRESS_RANGE;
+ pad->base = reg + i * PMIC_GPIO_ADDRESS_RANGE;
ret = pmic_gpio_populate(state, pad);
if (ret < 0)
@@ -804,6 +806,7 @@ static int pmic_gpio_remove(struct platform_device *pdev)
static const struct of_device_id pmic_gpio_of_match[] = {
{ .compatible = "qcom,pm8916-gpio" }, /* 4 GPIO's */
{ .compatible = "qcom,pm8941-gpio" }, /* 36 GPIO's */
+ { .compatible = "qcom,pm8994-gpio" }, /* 22 GPIO's */
{ .compatible = "qcom,pma8084-gpio" }, /* 22 GPIO's */
{ },
};
diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c b/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c
index 9ce0e30e33e8..2df4f29175ae 100644
--- a/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c
+++ b/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c
@@ -14,6 +14,7 @@
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinmux.h>
@@ -795,17 +796,19 @@ static int pmic_mpp_probe(struct platform_device *pdev)
struct pmic_mpp_pad *pad, *pads;
struct pmic_mpp_state *state;
int ret, npins, i;
- u32 res[2];
+ u32 reg;
- ret = of_property_read_u32_array(dev->of_node, "reg", res, 2);
+ ret = of_property_read_u32(dev->of_node, "reg", &reg);
if (ret < 0) {
- dev_err(dev, "missing base address and/or range");
+ dev_err(dev, "missing base address");
return ret;
}
- npins = res[1] / PMIC_MPP_ADDRESS_RANGE;
+ npins = platform_irq_count(pdev);
if (!npins)
return -EINVAL;
+ if (npins < 0)
+ return npins;
BUG_ON(npins > ARRAY_SIZE(pmic_mpp_groups));
@@ -854,7 +857,7 @@ static int pmic_mpp_probe(struct platform_device *pdev)
if (pad->irq < 0)
return pad->irq;
- pad->base = res[0] + i * PMIC_MPP_ADDRESS_RANGE;
+ pad->base = reg + i * PMIC_MPP_ADDRESS_RANGE;
ret = pmic_mpp_populate(state, pad);
if (ret < 0)
@@ -907,6 +910,7 @@ static const struct of_device_id pmic_mpp_of_match[] = {
{ .compatible = "qcom,pm8841-mpp" }, /* 4 MPP's */
{ .compatible = "qcom,pm8916-mpp" }, /* 4 MPP's */
{ .compatible = "qcom,pm8941-mpp" }, /* 8 MPP's */
+ { .compatible = "qcom,pm8994-mpp" }, /* 8 MPP's */
{ .compatible = "qcom,pma8084-mpp" }, /* 8 MPP's */
{ },
};
diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c
index d809c9eaa323..e51176ec83d2 100644
--- a/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c
+++ b/drivers/pinctrl/qcom/pinctrl-ssbi-gpio.c
@@ -23,6 +23,7 @@
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/of_device.h>
+#include <linux/of_irq.h>
#include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
@@ -650,11 +651,12 @@ static int pm8xxx_pin_populate(struct pm8xxx_gpio *pctrl,
}
static const struct of_device_id pm8xxx_gpio_of_match[] = {
- { .compatible = "qcom,pm8018-gpio", .data = (void *)6 },
- { .compatible = "qcom,pm8038-gpio", .data = (void *)12 },
- { .compatible = "qcom,pm8058-gpio", .data = (void *)40 },
- { .compatible = "qcom,pm8917-gpio", .data = (void *)38 },
- { .compatible = "qcom,pm8921-gpio", .data = (void *)44 },
+ { .compatible = "qcom,pm8018-gpio" },
+ { .compatible = "qcom,pm8038-gpio" },
+ { .compatible = "qcom,pm8058-gpio" },
+ { .compatible = "qcom,pm8917-gpio" },
+ { .compatible = "qcom,pm8921-gpio" },
+ { .compatible = "qcom,ssbi-gpio" },
{ },
};
MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match);
@@ -665,14 +667,19 @@ static int pm8xxx_gpio_probe(struct platform_device *pdev)
struct pinctrl_pin_desc *pins;
struct pm8xxx_gpio *pctrl;
int ret;
- int i;
+ int i, npins;
pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL);
if (!pctrl)
return -ENOMEM;
pctrl->dev = &pdev->dev;
- pctrl->npins = (unsigned)of_device_get_match_data(&pdev->dev);
+ npins = platform_irq_count(pdev);
+ if (!npins)
+ return -EINVAL;
+ if (npins < 0)
+ return npins;
+ pctrl->npins = npins;
pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!pctrl->regmap) {
diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c b/drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c
index 8982027de8e8..e9f01de51e18 100644
--- a/drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c
+++ b/drivers/pinctrl/qcom/pinctrl-ssbi-mpp.c
@@ -23,6 +23,7 @@
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/of_device.h>
+#include <linux/of_irq.h>
#include <dt-bindings/pinctrl/qcom,pmic-mpp.h>
@@ -741,11 +742,12 @@ static int pm8xxx_pin_populate(struct pm8xxx_mpp *pctrl,
}
static const struct of_device_id pm8xxx_mpp_of_match[] = {
- { .compatible = "qcom,pm8018-mpp", .data = (void *)6 },
- { .compatible = "qcom,pm8038-mpp", .data = (void *)6 },
- { .compatible = "qcom,pm8917-mpp", .data = (void *)10 },
- { .compatible = "qcom,pm8821-mpp", .data = (void *)4 },
- { .compatible = "qcom,pm8921-mpp", .data = (void *)12 },
+ { .compatible = "qcom,pm8018-mpp" },
+ { .compatible = "qcom,pm8038-mpp" },
+ { .compatible = "qcom,pm8917-mpp" },
+ { .compatible = "qcom,pm8821-mpp" },
+ { .compatible = "qcom,pm8921-mpp" },
+ { .compatible = "qcom,ssbi-mpp" },
{ },
};
MODULE_DEVICE_TABLE(of, pm8xxx_mpp_of_match);
@@ -756,14 +758,19 @@ static int pm8xxx_mpp_probe(struct platform_device *pdev)
struct pinctrl_pin_desc *pins;
struct pm8xxx_mpp *pctrl;
int ret;
- int i;
+ int i, npins;
pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL);
if (!pctrl)
return -ENOMEM;
pctrl->dev = &pdev->dev;
- pctrl->npins = (unsigned)of_device_get_match_data(&pdev->dev);
+ npins = platform_irq_count(pdev);
+ if (!npins)
+ return -EINVAL;
+ if (npins < 0)
+ return npins;
+ pctrl->npins = npins;
pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!pctrl->regmap) {
diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.c b/drivers/pinctrl/samsung/pinctrl-exynos.c
index 71ccf6a90b22..16e2293cc2bc 100644
--- a/drivers/pinctrl/samsung/pinctrl-exynos.c
+++ b/drivers/pinctrl/samsung/pinctrl-exynos.c
@@ -1150,6 +1150,109 @@ const struct samsung_pin_ctrl exynos5260_pin_ctrl[] __initconst = {
},
};
+/* pin banks of exynos5410 pin-controller 0 */
+static const struct samsung_pin_bank_data exynos5410_pin_banks0[] __initconst = {
+ EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpa0", 0x00),
+ EXYNOS_PIN_BANK_EINTG(6, 0x020, "gpa1", 0x04),
+ EXYNOS_PIN_BANK_EINTG(8, 0x040, "gpa2", 0x08),
+ EXYNOS_PIN_BANK_EINTG(5, 0x060, "gpb0", 0x0c),
+ EXYNOS_PIN_BANK_EINTG(5, 0x080, "gpb1", 0x10),
+ EXYNOS_PIN_BANK_EINTG(4, 0x0A0, "gpb2", 0x14),
+ EXYNOS_PIN_BANK_EINTG(4, 0x0C0, "gpb3", 0x18),
+ EXYNOS_PIN_BANK_EINTG(7, 0x0E0, "gpc0", 0x1c),
+ EXYNOS_PIN_BANK_EINTG(4, 0x100, "gpc3", 0x20),
+ EXYNOS_PIN_BANK_EINTG(7, 0x120, "gpc1", 0x24),
+ EXYNOS_PIN_BANK_EINTG(7, 0x140, "gpc2", 0x28),
+ EXYNOS_PIN_BANK_EINTN(2, 0x160, "gpm5"),
+ EXYNOS_PIN_BANK_EINTG(8, 0x180, "gpd1", 0x2c),
+ EXYNOS_PIN_BANK_EINTG(8, 0x1A0, "gpe0", 0x30),
+ EXYNOS_PIN_BANK_EINTG(2, 0x1C0, "gpe1", 0x34),
+ EXYNOS_PIN_BANK_EINTG(6, 0x1E0, "gpf0", 0x38),
+ EXYNOS_PIN_BANK_EINTG(8, 0x200, "gpf1", 0x3c),
+ EXYNOS_PIN_BANK_EINTG(8, 0x220, "gpg0", 0x40),
+ EXYNOS_PIN_BANK_EINTG(8, 0x240, "gpg1", 0x44),
+ EXYNOS_PIN_BANK_EINTG(2, 0x260, "gpg2", 0x48),
+ EXYNOS_PIN_BANK_EINTG(4, 0x280, "gph0", 0x4c),
+ EXYNOS_PIN_BANK_EINTG(8, 0x2A0, "gph1", 0x50),
+ EXYNOS_PIN_BANK_EINTN(8, 0x2C0, "gpm7"),
+ EXYNOS_PIN_BANK_EINTN(6, 0x2E0, "gpy0"),
+ EXYNOS_PIN_BANK_EINTN(4, 0x300, "gpy1"),
+ EXYNOS_PIN_BANK_EINTN(6, 0x320, "gpy2"),
+ EXYNOS_PIN_BANK_EINTN(8, 0x340, "gpy3"),
+ EXYNOS_PIN_BANK_EINTN(8, 0x360, "gpy4"),
+ EXYNOS_PIN_BANK_EINTN(8, 0x380, "gpy5"),
+ EXYNOS_PIN_BANK_EINTN(8, 0x3A0, "gpy6"),
+ EXYNOS_PIN_BANK_EINTN(8, 0x3C0, "gpy7"),
+ EXYNOS_PIN_BANK_EINTW(8, 0xC00, "gpx0", 0x00),
+ EXYNOS_PIN_BANK_EINTW(8, 0xC20, "gpx1", 0x04),
+ EXYNOS_PIN_BANK_EINTW(8, 0xC40, "gpx2", 0x08),
+ EXYNOS_PIN_BANK_EINTW(8, 0xC60, "gpx3", 0x0c),
+};
+
+/* pin banks of exynos5410 pin-controller 1 */
+static const struct samsung_pin_bank_data exynos5410_pin_banks1[] __initconst = {
+ EXYNOS_PIN_BANK_EINTG(5, 0x000, "gpj0", 0x00),
+ EXYNOS_PIN_BANK_EINTG(8, 0x020, "gpj1", 0x04),
+ EXYNOS_PIN_BANK_EINTG(8, 0x040, "gpj2", 0x08),
+ EXYNOS_PIN_BANK_EINTG(8, 0x060, "gpj3", 0x0c),
+ EXYNOS_PIN_BANK_EINTG(2, 0x080, "gpj4", 0x10),
+ EXYNOS_PIN_BANK_EINTG(8, 0x0A0, "gpk0", 0x14),
+ EXYNOS_PIN_BANK_EINTG(8, 0x0C0, "gpk1", 0x18),
+ EXYNOS_PIN_BANK_EINTG(8, 0x0E0, "gpk2", 0x1c),
+ EXYNOS_PIN_BANK_EINTG(7, 0x100, "gpk3", 0x20),
+};
+
+/* pin banks of exynos5410 pin-controller 2 */
+static const struct samsung_pin_bank_data exynos5410_pin_banks2[] __initconst = {
+ EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpv0", 0x00),
+ EXYNOS_PIN_BANK_EINTG(8, 0x020, "gpv1", 0x04),
+ EXYNOS_PIN_BANK_EINTG(8, 0x060, "gpv2", 0x08),
+ EXYNOS_PIN_BANK_EINTG(8, 0x080, "gpv3", 0x0c),
+ EXYNOS_PIN_BANK_EINTG(2, 0x0C0, "gpv4", 0x10),
+};
+
+/* pin banks of exynos5410 pin-controller 3 */
+static const struct samsung_pin_bank_data exynos5410_pin_banks3[] __initconst = {
+ EXYNOS_PIN_BANK_EINTG(7, 0x000, "gpz", 0x00),
+};
+
+/*
+ * Samsung pinctrl driver data for Exynos5410 SoC. Exynos5410 SoC includes
+ * four gpio/pin-mux/pinconfig controllers.
+ */
+const struct samsung_pin_ctrl exynos5410_pin_ctrl[] __initconst = {
+ {
+ /* pin-controller instance 0 data */
+ .pin_banks = exynos5410_pin_banks0,
+ .nr_banks = ARRAY_SIZE(exynos5410_pin_banks0),
+ .eint_gpio_init = exynos_eint_gpio_init,
+ .eint_wkup_init = exynos_eint_wkup_init,
+ .suspend = exynos_pinctrl_suspend,
+ .resume = exynos_pinctrl_resume,
+ }, {
+ /* pin-controller instance 1 data */
+ .pin_banks = exynos5410_pin_banks1,
+ .nr_banks = ARRAY_SIZE(exynos5410_pin_banks1),
+ .eint_gpio_init = exynos_eint_gpio_init,
+ .suspend = exynos_pinctrl_suspend,
+ .resume = exynos_pinctrl_resume,
+ }, {
+ /* pin-controller instance 2 data */
+ .pin_banks = exynos5410_pin_banks2,
+ .nr_banks = ARRAY_SIZE(exynos5410_pin_banks2),
+ .eint_gpio_init = exynos_eint_gpio_init,
+ .suspend = exynos_pinctrl_suspend,
+ .resume = exynos_pinctrl_resume,
+ }, {
+ /* pin-controller instance 3 data */
+ .pin_banks = exynos5410_pin_banks3,
+ .nr_banks = ARRAY_SIZE(exynos5410_pin_banks3),
+ .eint_gpio_init = exynos_eint_gpio_init,
+ .suspend = exynos_pinctrl_suspend,
+ .resume = exynos_pinctrl_resume,
+ },
+};
+
/* pin banks of exynos5420 pin-controller 0 */
static const struct samsung_pin_bank_data exynos5420_pin_banks0[] __initconst = {
EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpy7", 0x00),
diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.c b/drivers/pinctrl/samsung/pinctrl-samsung.c
index 3f622ccd8eab..48294e7449a4 100644
--- a/drivers/pinctrl/samsung/pinctrl-samsung.c
+++ b/drivers/pinctrl/samsung/pinctrl-samsung.c
@@ -1222,6 +1222,8 @@ static const struct of_device_id samsung_pinctrl_dt_match[] = {
.data = (void *)exynos5250_pin_ctrl },
{ .compatible = "samsung,exynos5260-pinctrl",
.data = (void *)exynos5260_pin_ctrl },
+ { .compatible = "samsung,exynos5410-pinctrl",
+ .data = (void *)exynos5410_pin_ctrl },
{ .compatible = "samsung,exynos5420-pinctrl",
.data = (void *)exynos5420_pin_ctrl },
{ .compatible = "samsung,exynos5433-pinctrl",
diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.h b/drivers/pinctrl/samsung/pinctrl-samsung.h
index c1239ff6157d..cd31bfaf62cb 100644
--- a/drivers/pinctrl/samsung/pinctrl-samsung.h
+++ b/drivers/pinctrl/samsung/pinctrl-samsung.h
@@ -270,6 +270,7 @@ extern const struct samsung_pin_ctrl exynos4x12_pin_ctrl[];
extern const struct samsung_pin_ctrl exynos4415_pin_ctrl[];
extern const struct samsung_pin_ctrl exynos5250_pin_ctrl[];
extern const struct samsung_pin_ctrl exynos5260_pin_ctrl[];
+extern const struct samsung_pin_ctrl exynos5410_pin_ctrl[];
extern const struct samsung_pin_ctrl exynos5420_pin_ctrl[];
extern const struct samsung_pin_ctrl exynos5433_pin_ctrl[];
extern const struct samsung_pin_ctrl exynos7_pin_ctrl[];
diff --git a/drivers/pinctrl/sh-pfc/pfc-emev2.c b/drivers/pinctrl/sh-pfc/pfc-emev2.c
index 02118ab336fc..1cbbe04d7df6 100644
--- a/drivers/pinctrl/sh-pfc/pfc-emev2.c
+++ b/drivers/pinctrl/sh-pfc/pfc-emev2.c
@@ -258,18 +258,18 @@ static const u16 pinmux_data[] = {
/* GPSR0 */
/* V9 */
- PINMUX_DATA(JT_SEL_MARK, FN_JT_SEL),
+ PINMUX_SINGLE(JT_SEL),
/* U9 */
- PINMUX_DATA(ERR_RST_REQB_MARK, FN_ERR_RST_REQB),
+ PINMUX_SINGLE(ERR_RST_REQB),
/* V8 */
- PINMUX_DATA(REF_CLKO_MARK, FN_REF_CLKO),
+ PINMUX_SINGLE(REF_CLKO),
/* U8 */
- PINMUX_DATA(EXT_CLKI_MARK, FN_EXT_CLKI),
+ PINMUX_SINGLE(EXT_CLKI),
/* B22*/
PINMUX_IPSR_NOFN(LCD3_1_0_PORT18, LCD3_PXCLK, SEL_LCD3_1_0_00),
PINMUX_IPSR_NOFN(LCD3_1_0_PORT18, YUV3_CLK_O, SEL_LCD3_1_0_01),
/* C21 */
- PINMUX_DATA(LCD3_PXCLKB_MARK, FN_LCD3_PXCLKB),
+ PINMUX_SINGLE(LCD3_PXCLKB),
/* A21 */
PINMUX_IPSR_NOFN(LCD3_1_0_PORT20, LCD3_CLK_I, SEL_LCD3_1_0_00),
PINMUX_IPSR_NOFN(LCD3_1_0_PORT20, YUV3_CLK_I, SEL_LCD3_1_0_01),
@@ -285,17 +285,17 @@ static const u16 pinmux_data[] = {
/* GPSR1 */
/* A20 */
- PINMUX_DATA(LCD3_R0_MARK, FN_LCD3_R0),
+ PINMUX_SINGLE(LCD3_R0),
/* B20 */
- PINMUX_DATA(LCD3_R1_MARK, FN_LCD3_R1),
+ PINMUX_SINGLE(LCD3_R1),
/* A19 */
- PINMUX_DATA(LCD3_R2_MARK, FN_LCD3_R2),
+ PINMUX_SINGLE(LCD3_R2),
/* B19 */
- PINMUX_DATA(LCD3_R3_MARK, FN_LCD3_R3),
+ PINMUX_SINGLE(LCD3_R3),
/* C19 */
- PINMUX_DATA(LCD3_R4_MARK, FN_LCD3_R4),
+ PINMUX_SINGLE(LCD3_R4),
/* B18 */
- PINMUX_DATA(LCD3_R5_MARK, FN_LCD3_R5),
+ PINMUX_SINGLE(LCD3_R5),
/* C18 */
PINMUX_IPSR_NOFN(LCD3_9_8_PORT38, LCD3_R6, SEL_LCD3_9_8_00),
PINMUX_IPSR_NOFN(LCD3_9_8_PORT38, TP33_CLK, SEL_LCD3_9_8_10),
@@ -367,9 +367,9 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_NOFN(LCD3_11_10_PORT43, YUV3_D15, SEL_LCD3_11_10_01),
PINMUX_IPSR_NOFN(LCD3_11_10_PORT43, TP33_DATA15, SEL_LCD3_11_10_10),
/* AA9 */
- PINMUX_DATA(IIC0_SCL_MARK, FN_IIC0_SCL),
+ PINMUX_SINGLE(IIC0_SCL),
/* AA8 */
- PINMUX_DATA(IIC0_SDA_MARK, FN_IIC0_SDA),
+ PINMUX_SINGLE(IIC0_SDA),
/* Y9 */
PINMUX_IPSR_NOFN(IIC_1_0_PORT46, IIC1_SCL, SEL_IIC_1_0_00),
PINMUX_IPSR_NOFN(IIC_1_0_PORT46, UART3_RX, SEL_IIC_1_0_01),
@@ -377,51 +377,51 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_NOFN(IIC_1_0_PORT47, IIC1_SDA, SEL_IIC_1_0_00),
PINMUX_IPSR_NOFN(IIC_1_0_PORT47, UART3_TX, SEL_IIC_1_0_01),
/* AC19 */
- PINMUX_DATA(SD_CKI_MARK, FN_SD_CKI),
+ PINMUX_SINGLE(SD_CKI),
/* AB18 */
- PINMUX_DATA(SDI0_CKO_MARK, FN_SDI0_CKO),
+ PINMUX_SINGLE(SDI0_CKO),
/* AC18 */
- PINMUX_DATA(SDI0_CKI_MARK, FN_SDI0_CKI),
+ PINMUX_SINGLE(SDI0_CKI),
/* Y12 */
- PINMUX_DATA(SDI0_CMD_MARK, FN_SDI0_CMD),
+ PINMUX_SINGLE(SDI0_CMD),
/* AA13 */
- PINMUX_DATA(SDI0_DATA0_MARK, FN_SDI0_DATA0),
+ PINMUX_SINGLE(SDI0_DATA0),
/* Y13 */
- PINMUX_DATA(SDI0_DATA1_MARK, FN_SDI0_DATA1),
+ PINMUX_SINGLE(SDI0_DATA1),
/* AA14 */
- PINMUX_DATA(SDI0_DATA2_MARK, FN_SDI0_DATA2),
+ PINMUX_SINGLE(SDI0_DATA2),
/* Y14 */
- PINMUX_DATA(SDI0_DATA3_MARK, FN_SDI0_DATA3),
+ PINMUX_SINGLE(SDI0_DATA3),
/* AA15 */
- PINMUX_DATA(SDI0_DATA4_MARK, FN_SDI0_DATA4),
+ PINMUX_SINGLE(SDI0_DATA4),
/* Y15 */
- PINMUX_DATA(SDI0_DATA5_MARK, FN_SDI0_DATA5),
+ PINMUX_SINGLE(SDI0_DATA5),
/* AA16 */
- PINMUX_DATA(SDI0_DATA6_MARK, FN_SDI0_DATA6),
+ PINMUX_SINGLE(SDI0_DATA6),
/* Y16 */
- PINMUX_DATA(SDI0_DATA7_MARK, FN_SDI0_DATA7),
+ PINMUX_SINGLE(SDI0_DATA7),
/* AB22 */
- PINMUX_DATA(SDI1_CKO_MARK, FN_SDI1_CKO),
+ PINMUX_SINGLE(SDI1_CKO),
/* AA23 */
- PINMUX_DATA(SDI1_CKI_MARK, FN_SDI1_CKI),
+ PINMUX_SINGLE(SDI1_CKI),
/* AC21 */
- PINMUX_DATA(SDI1_CMD_MARK, FN_SDI1_CMD),
+ PINMUX_SINGLE(SDI1_CMD),
/* GPSR2 */
/* AB21 */
- PINMUX_DATA(SDI1_DATA0_MARK, FN_SDI1_DATA0),
+ PINMUX_SINGLE(SDI1_DATA0),
/* AB20 */
- PINMUX_DATA(SDI1_DATA1_MARK, FN_SDI1_DATA1),
+ PINMUX_SINGLE(SDI1_DATA1),
/* AB19 */
- PINMUX_DATA(SDI1_DATA2_MARK, FN_SDI1_DATA2),
+ PINMUX_SINGLE(SDI1_DATA2),
/* AA19 */
- PINMUX_DATA(SDI1_DATA3_MARK, FN_SDI1_DATA3),
+ PINMUX_SINGLE(SDI1_DATA3),
/* J23 */
- PINMUX_DATA(AB_CLK_MARK, FN_AB_CLK),
+ PINMUX_SINGLE(AB_CLK),
/* D21 */
- PINMUX_DATA(AB_CSB0_MARK, FN_AB_CSB0),
+ PINMUX_SINGLE(AB_CSB0),
/* E21 */
- PINMUX_DATA(AB_CSB1_MARK, FN_AB_CSB1),
+ PINMUX_SINGLE(AB_CSB1),
/* F20 */
PINMUX_IPSR_NOFN(AB_1_0_PORT71, AB_CSB2, SEL_AB_1_0_00),
PINMUX_IPSR_NOFN(AB_1_0_PORT71, CF_CSB0, SEL_AB_1_0_10),
@@ -514,7 +514,7 @@ static const u16 pinmux_data[] = {
/* GPSR3 */
/* M21 */
- PINMUX_DATA(AB_A20_MARK, FN_AB_A20),
+ PINMUX_SINGLE(AB_A20),
/* N21 */
PINMUX_IPSR_NOFN(AB_9_8_PORT97, AB_A21, SEL_AB_9_8_00),
PINMUX_IPSR_NOFN(AB_9_8_PORT97, SDI2_CKO, SEL_AB_9_8_01),
@@ -541,13 +541,13 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_NOFN(AB_13_12_PORT104, AB_A28, SEL_AB_13_12_00),
PINMUX_IPSR_NOFN(AB_13_12_PORT104, AB_BEN1, SEL_AB_13_12_10),
/* B8 */
- PINMUX_DATA(USI0_CS1_MARK, FN_USI0_CS1),
+ PINMUX_SINGLE(USI0_CS1),
/* B9 */
- PINMUX_DATA(USI0_CS2_MARK, FN_USI0_CS2),
+ PINMUX_SINGLE(USI0_CS2),
/* C10 */
- PINMUX_DATA(USI1_DI_MARK, FN_USI1_DI),
+ PINMUX_SINGLE(USI1_DI),
/* D10 */
- PINMUX_DATA(USI1_DO_MARK, FN_USI1_DO),
+ PINMUX_SINGLE(USI1_DO),
/* AB5 */
PINMUX_IPSR_NOFN(USI_1_0_PORT109, USI2_CLK, SEL_USI_1_0_00),
PINMUX_IPSR_NOFN(USI_1_0_PORT109, DTV_BCLK_B, SEL_USI_1_0_01),
@@ -587,49 +587,49 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_NOFN(USI_9_8_PORT121, PWM1, SEL_USI_9_8_00),
PINMUX_IPSR_NOFN(USI_9_8_PORT121, USI4_DO, SEL_USI_9_8_01),
/* V20 */
- PINMUX_DATA(NTSC_CLK_MARK, FN_NTSC_CLK),
+ PINMUX_SINGLE(NTSC_CLK),
/* P20 */
- PINMUX_DATA(NTSC_DATA0_MARK, FN_NTSC_DATA0),
+ PINMUX_SINGLE(NTSC_DATA0),
/* P18 */
- PINMUX_DATA(NTSC_DATA1_MARK, FN_NTSC_DATA1),
+ PINMUX_SINGLE(NTSC_DATA1),
/* R20 */
- PINMUX_DATA(NTSC_DATA2_MARK, FN_NTSC_DATA2),
+ PINMUX_SINGLE(NTSC_DATA2),
/* R18 */
- PINMUX_DATA(NTSC_DATA3_MARK, FN_NTSC_DATA3),
+ PINMUX_SINGLE(NTSC_DATA3),
/* T20 */
- PINMUX_DATA(NTSC_DATA4_MARK, FN_NTSC_DATA4),
+ PINMUX_SINGLE(NTSC_DATA4),
/* GPRS3 */
/* T18 */
- PINMUX_DATA(NTSC_DATA5_MARK, FN_NTSC_DATA5),
+ PINMUX_SINGLE(NTSC_DATA5),
/* U20 */
- PINMUX_DATA(NTSC_DATA6_MARK, FN_NTSC_DATA6),
+ PINMUX_SINGLE(NTSC_DATA6),
/* U18 */
- PINMUX_DATA(NTSC_DATA7_MARK, FN_NTSC_DATA7),
+ PINMUX_SINGLE(NTSC_DATA7),
/* W23 */
- PINMUX_DATA(CAM_CLKO_MARK, FN_CAM_CLKO),
+ PINMUX_SINGLE(CAM_CLKO),
/* Y23 */
- PINMUX_DATA(CAM_CLKI_MARK, FN_CAM_CLKI),
+ PINMUX_SINGLE(CAM_CLKI),
/* W22 */
- PINMUX_DATA(CAM_VS_MARK, FN_CAM_VS),
+ PINMUX_SINGLE(CAM_VS),
/* V21 */
- PINMUX_DATA(CAM_HS_MARK, FN_CAM_HS),
+ PINMUX_SINGLE(CAM_HS),
/* T21 */
- PINMUX_DATA(CAM_YUV0_MARK, FN_CAM_YUV0),
+ PINMUX_SINGLE(CAM_YUV0),
/* T22 */
- PINMUX_DATA(CAM_YUV1_MARK, FN_CAM_YUV1),
+ PINMUX_SINGLE(CAM_YUV1),
/* T23 */
- PINMUX_DATA(CAM_YUV2_MARK, FN_CAM_YUV2),
+ PINMUX_SINGLE(CAM_YUV2),
/* U21 */
- PINMUX_DATA(CAM_YUV3_MARK, FN_CAM_YUV3),
+ PINMUX_SINGLE(CAM_YUV3),
/* U22 */
- PINMUX_DATA(CAM_YUV4_MARK, FN_CAM_YUV4),
+ PINMUX_SINGLE(CAM_YUV4),
/* U23 */
- PINMUX_DATA(CAM_YUV5_MARK, FN_CAM_YUV5),
+ PINMUX_SINGLE(CAM_YUV5),
/* V22 */
- PINMUX_DATA(CAM_YUV6_MARK, FN_CAM_YUV6),
+ PINMUX_SINGLE(CAM_YUV6),
/* V23 */
- PINMUX_DATA(CAM_YUV7_MARK, FN_CAM_YUV7),
+ PINMUX_SINGLE(CAM_YUV7),
/* K22 */
PINMUX_IPSR_NOFN(HSI_1_0_PORT143, USI5_CLK_B, SEL_HSI_1_0_01),
/* K23 */
@@ -647,17 +647,17 @@ static const u16 pinmux_data[] = {
/* M22 */
PINMUX_IPSR_NOFN(HSI_1_0_PORT150, USI5_DI_B, SEL_HSI_1_0_01),
/* D13 */
- PINMUX_DATA(JT_TDO_MARK, FN_JT_TDO),
+ PINMUX_SINGLE(JT_TDO),
/* F13 */
- PINMUX_DATA(JT_TDOEN_MARK, FN_JT_TDOEN),
+ PINMUX_SINGLE(JT_TDOEN),
/* AA12 */
- PINMUX_DATA(USB_VBUS_MARK, FN_USB_VBUS),
+ PINMUX_SINGLE(USB_VBUS),
/* A12 */
- PINMUX_DATA(LOWPWR_MARK, FN_LOWPWR),
+ PINMUX_SINGLE(LOWPWR),
/* Y11 */
- PINMUX_DATA(UART1_RX_MARK, FN_UART1_RX),
+ PINMUX_SINGLE(UART1_RX),
/* Y10 */
- PINMUX_DATA(UART1_TX_MARK, FN_UART1_TX),
+ PINMUX_SINGLE(UART1_TX),
/* AA10 */
PINMUX_IPSR_NOFN(UART_1_0_PORT157, UART1_CTSB, SEL_UART_1_0_00),
PINMUX_IPSR_NOFN(UART_1_0_PORT157, UART2_RX, SEL_UART_1_0_01),
@@ -749,7 +749,7 @@ static const unsigned int cf_ctrl_mux[] = {
};
static const unsigned int cf_data8_pins[] = {
- /* CF_D[0:8] */
+ /* CF_D[0:7] */
77, 78, 79, 80,
81, 82, 83, 84,
};
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7740.c b/drivers/pinctrl/sh-pfc/pfc-r8a7740.c
index 279e9dd442e4..7f7c8a6e76e8 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7740.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7740.c
@@ -2214,7 +2214,7 @@ static const unsigned int lcd1_data9_mux[] = {
LCD1_D8_MARK,
};
static const unsigned int lcd1_data12_pins[] = {
- /* D[0:12] */
+ /* D[0:11] */
4, 3, 2, 1, 0, 91, 92, 23,
93, 94, 21, 201,
};
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7778.c b/drivers/pinctrl/sh-pfc/pfc-r8a7778.c
index bbd35dc1a0c4..ad09a670c2ff 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7778.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7778.c
@@ -548,17 +548,17 @@ enum {
static const u16 pinmux_data[] = {
PINMUX_DATA_GP_ALL(), /* PINMUX_DATA(GP_M_N_DATA, GP_M_N_FN...), */
- PINMUX_DATA(PENC0_MARK, FN_PENC0),
- PINMUX_DATA(PENC1_MARK, FN_PENC1),
- PINMUX_DATA(A1_MARK, FN_A1),
- PINMUX_DATA(A2_MARK, FN_A2),
- PINMUX_DATA(A3_MARK, FN_A3),
- PINMUX_DATA(WE0_MARK, FN_WE0),
- PINMUX_DATA(AUDIO_CLKA_MARK, FN_AUDIO_CLKA),
- PINMUX_DATA(AUDIO_CLKB_MARK, FN_AUDIO_CLKB),
- PINMUX_DATA(SSI_SCK34_MARK, FN_SSI_SCK34),
- PINMUX_DATA(AVS1_MARK, FN_AVS1),
- PINMUX_DATA(AVS2_MARK, FN_AVS2),
+ PINMUX_SINGLE(PENC0),
+ PINMUX_SINGLE(PENC1),
+ PINMUX_SINGLE(A1),
+ PINMUX_SINGLE(A2),
+ PINMUX_SINGLE(A3),
+ PINMUX_SINGLE(WE0),
+ PINMUX_SINGLE(AUDIO_CLKA),
+ PINMUX_SINGLE(AUDIO_CLKB),
+ PINMUX_SINGLE(SSI_SCK34),
+ PINMUX_SINGLE(AVS1),
+ PINMUX_SINGLE(AVS2),
/* IPSR0 */
PINMUX_IPSR_DATA(IP0_1_0, PRESETOUT),
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7779.c b/drivers/pinctrl/sh-pfc/pfc-r8a7779.c
index ed4e0788035c..bd17eccb6a89 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7779.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7779.c
@@ -23,13 +23,6 @@
#include "sh_pfc.h"
-#define PORT_GP_9(bank, fn, sfx) \
- PORT_GP_1(bank, 0, fn, sfx), PORT_GP_1(bank, 1, fn, sfx), \
- PORT_GP_1(bank, 2, fn, sfx), PORT_GP_1(bank, 3, fn, sfx), \
- PORT_GP_1(bank, 4, fn, sfx), PORT_GP_1(bank, 5, fn, sfx), \
- PORT_GP_1(bank, 6, fn, sfx), PORT_GP_1(bank, 7, fn, sfx), \
- PORT_GP_1(bank, 8, fn, sfx)
-
#define CPU_ALL_PORT(fn, sfx) \
PORT_GP_32(0, fn, sfx), \
PORT_GP_32(1, fn, sfx), \
@@ -609,14 +602,14 @@ enum {
static const u16 pinmux_data[] = {
PINMUX_DATA_GP_ALL(), /* PINMUX_DATA(GP_M_N_DATA, GP_M_N_FN...), */
- PINMUX_DATA(AVS1_MARK, FN_AVS1),
- PINMUX_DATA(AVS1_MARK, FN_AVS1),
- PINMUX_DATA(A17_MARK, FN_A17),
- PINMUX_DATA(A18_MARK, FN_A18),
- PINMUX_DATA(A19_MARK, FN_A19),
+ PINMUX_SINGLE(AVS1),
+ PINMUX_SINGLE(AVS1),
+ PINMUX_SINGLE(A17),
+ PINMUX_SINGLE(A18),
+ PINMUX_SINGLE(A19),
- PINMUX_DATA(USB_PENC0_MARK, FN_USB_PENC0),
- PINMUX_DATA(USB_PENC1_MARK, FN_USB_PENC1),
+ PINMUX_SINGLE(USB_PENC0),
+ PINMUX_SINGLE(USB_PENC1),
PINMUX_IPSR_DATA(IP0_2_0, USB_PENC2),
PINMUX_IPSR_MSEL(IP0_2_0, SCK0, SEL_SCIF0_0),
@@ -2289,6 +2282,35 @@ static const unsigned int scif5_clk_d_pins[] = {
static const unsigned int scif5_clk_d_mux[] = {
SCK5_D_MARK,
};
+/* - SCIF Clock ------------------------------------------------------------- */
+static const unsigned int scif_clk_pins[] = {
+ /* SCIF_CLK */
+ RCAR_GP_PIN(4, 28),
+};
+static const unsigned int scif_clk_mux[] = {
+ SCIF_CLK_MARK,
+};
+static const unsigned int scif_clk_b_pins[] = {
+ /* SCIF_CLK */
+ RCAR_GP_PIN(4, 5),
+};
+static const unsigned int scif_clk_b_mux[] = {
+ SCIF_CLK_B_MARK,
+};
+static const unsigned int scif_clk_c_pins[] = {
+ /* SCIF_CLK */
+ RCAR_GP_PIN(4, 18),
+};
+static const unsigned int scif_clk_c_mux[] = {
+ SCIF_CLK_C_MARK,
+};
+static const unsigned int scif_clk_d_pins[] = {
+ /* SCIF_CLK */
+ RCAR_GP_PIN(2, 29),
+};
+static const unsigned int scif_clk_d_mux[] = {
+ SCIF_CLK_D_MARK,
+};
/* - SDHI0 ------------------------------------------------------------------ */
static const unsigned int sdhi0_data1_pins[] = {
/* D0 */
@@ -2700,6 +2722,10 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(scif5_clk_c),
SH_PFC_PIN_GROUP(scif5_data_d),
SH_PFC_PIN_GROUP(scif5_clk_d),
+ SH_PFC_PIN_GROUP(scif_clk),
+ SH_PFC_PIN_GROUP(scif_clk_b),
+ SH_PFC_PIN_GROUP(scif_clk_c),
+ SH_PFC_PIN_GROUP(scif_clk_d),
SH_PFC_PIN_GROUP(sdhi0_data1),
SH_PFC_PIN_GROUP(sdhi0_data4),
SH_PFC_PIN_GROUP(sdhi0_ctrl),
@@ -2909,6 +2935,13 @@ static const char * const scif5_groups[] = {
"scif5_clk_d",
};
+static const char * const scif_clk_groups[] = {
+ "scif_clk",
+ "scif_clk_b",
+ "scif_clk_c",
+ "scif_clk_d",
+};
+
static const char * const sdhi0_groups[] = {
"sdhi0_data1",
"sdhi0_data4",
@@ -3004,6 +3037,7 @@ static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(scif3),
SH_PFC_FUNCTION(scif4),
SH_PFC_FUNCTION(scif5),
+ SH_PFC_FUNCTION(scif_clk),
SH_PFC_FUNCTION(usb0),
SH_PFC_FUNCTION(usb1),
SH_PFC_FUNCTION(usb2),
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7790.c b/drivers/pinctrl/sh-pfc/pfc-r8a7790.c
index d9924b0d53b7..a8b629bc7a55 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7790.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7790.c
@@ -26,23 +26,6 @@
#include "core.h"
#include "sh_pfc.h"
-#define PORT_GP_30(bank, fn, sfx) \
- PORT_GP_1(bank, 0, fn, sfx), PORT_GP_1(bank, 1, fn, sfx), \
- PORT_GP_1(bank, 2, fn, sfx), PORT_GP_1(bank, 3, fn, sfx), \
- PORT_GP_1(bank, 4, fn, sfx), PORT_GP_1(bank, 5, fn, sfx), \
- PORT_GP_1(bank, 6, fn, sfx), PORT_GP_1(bank, 7, fn, sfx), \
- PORT_GP_1(bank, 8, fn, sfx), PORT_GP_1(bank, 9, fn, sfx), \
- PORT_GP_1(bank, 10, fn, sfx), PORT_GP_1(bank, 11, fn, sfx), \
- PORT_GP_1(bank, 12, fn, sfx), PORT_GP_1(bank, 13, fn, sfx), \
- PORT_GP_1(bank, 14, fn, sfx), PORT_GP_1(bank, 15, fn, sfx), \
- PORT_GP_1(bank, 16, fn, sfx), PORT_GP_1(bank, 17, fn, sfx), \
- PORT_GP_1(bank, 18, fn, sfx), PORT_GP_1(bank, 19, fn, sfx), \
- PORT_GP_1(bank, 20, fn, sfx), PORT_GP_1(bank, 21, fn, sfx), \
- PORT_GP_1(bank, 22, fn, sfx), PORT_GP_1(bank, 23, fn, sfx), \
- PORT_GP_1(bank, 24, fn, sfx), PORT_GP_1(bank, 25, fn, sfx), \
- PORT_GP_1(bank, 26, fn, sfx), PORT_GP_1(bank, 27, fn, sfx), \
- PORT_GP_1(bank, 28, fn, sfx), PORT_GP_1(bank, 29, fn, sfx)
-
#define CPU_ALL_PORT(fn, sfx) \
PORT_GP_32(0, fn, sfx), \
PORT_GP_30(1, fn, sfx), \
@@ -806,15 +789,15 @@ enum {
static const u16 pinmux_data[] = {
PINMUX_DATA_GP_ALL(), /* PINMUX_DATA(GP_M_N_DATA, GP_M_N_FN...), */
- PINMUX_DATA(VI1_DATA7_VI1_B7_MARK, FN_VI1_DATA7_VI1_B7),
- PINMUX_DATA(USB0_PWEN_MARK, FN_USB0_PWEN),
- PINMUX_DATA(USB0_OVC_VBUS_MARK, FN_USB0_OVC_VBUS),
- PINMUX_DATA(USB2_PWEN_MARK, FN_USB2_PWEN),
- PINMUX_DATA(USB2_OVC_MARK, FN_USB2_OVC),
- PINMUX_DATA(AVS1_MARK, FN_AVS1),
- PINMUX_DATA(AVS2_MARK, FN_AVS2),
- PINMUX_DATA(DU_DOTCLKIN0_MARK, FN_DU_DOTCLKIN0),
- PINMUX_DATA(DU_DOTCLKIN2_MARK, FN_DU_DOTCLKIN2),
+ PINMUX_SINGLE(VI1_DATA7_VI1_B7),
+ PINMUX_SINGLE(USB0_PWEN),
+ PINMUX_SINGLE(USB0_OVC_VBUS),
+ PINMUX_SINGLE(USB2_PWEN),
+ PINMUX_SINGLE(USB2_OVC),
+ PINMUX_SINGLE(AVS1),
+ PINMUX_SINGLE(AVS2),
+ PINMUX_SINGLE(DU_DOTCLKIN0),
+ PINMUX_SINGLE(DU_DOTCLKIN2),
PINMUX_IPSR_DATA(IP0_2_0, D0),
PINMUX_IPSR_MSEL(IP0_2_0, MSIOF3_SCK_B, SEL_SOF3_1),
@@ -3236,6 +3219,21 @@ static const unsigned int scifb2_data_c_pins[] = {
static const unsigned int scifb2_data_c_mux[] = {
SCIFB2_RXD_C_MARK, SCIFB2_TXD_C_MARK,
};
+/* - SCIF Clock ------------------------------------------------------------- */
+static const unsigned int scif_clk_pins[] = {
+ /* SCIF_CLK */
+ RCAR_GP_PIN(4, 26),
+};
+static const unsigned int scif_clk_mux[] = {
+ SCIF_CLK_MARK,
+};
+static const unsigned int scif_clk_b_pins[] = {
+ /* SCIF_CLK */
+ RCAR_GP_PIN(5, 4),
+};
+static const unsigned int scif_clk_b_mux[] = {
+ SCIF_CLK_B_MARK,
+};
/* - SDHI0 ------------------------------------------------------------------ */
static const unsigned int sdhi0_data1_pins[] = {
/* D0 */
@@ -4139,6 +4137,8 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(scifb2_clk_b),
SH_PFC_PIN_GROUP(scifb2_ctrl_b),
SH_PFC_PIN_GROUP(scifb2_data_c),
+ SH_PFC_PIN_GROUP(scif_clk),
+ SH_PFC_PIN_GROUP(scif_clk_b),
SH_PFC_PIN_GROUP(sdhi0_data1),
SH_PFC_PIN_GROUP(sdhi0_data4),
SH_PFC_PIN_GROUP(sdhi0_ctrl),
@@ -4555,6 +4555,11 @@ static const char * const scifb2_groups[] = {
"scifb2_data_c",
};
+static const char * const scif_clk_groups[] = {
+ "scif_clk",
+ "scif_clk_b",
+};
+
static const char * const sdhi0_groups[] = {
"sdhi0_data1",
"sdhi0_data4",
@@ -4729,6 +4734,7 @@ static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(scifb0),
SH_PFC_FUNCTION(scifb1),
SH_PFC_FUNCTION(scifb2),
+ SH_PFC_FUNCTION(scif_clk),
SH_PFC_FUNCTION(sdhi0),
SH_PFC_FUNCTION(sdhi1),
SH_PFC_FUNCTION(sdhi2),
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7791.c b/drivers/pinctrl/sh-pfc/pfc-r8a7791.c
index 87a4f44147c1..4cfbb94ad5d0 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7791.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7791.c
@@ -2,6 +2,7 @@
* r8a7791 processor support - PFC hardware block.
*
* Copyright (C) 2013 Renesas Electronics Corporation
+ * Copyright (C) 2014-2015 Cogent Embedded, 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
@@ -13,21 +14,6 @@
#include "core.h"
#include "sh_pfc.h"
-#define PORT_GP_26(bank, fn, sfx) \
- PORT_GP_1(bank, 0, fn, sfx), PORT_GP_1(bank, 1, fn, sfx), \
- PORT_GP_1(bank, 2, fn, sfx), PORT_GP_1(bank, 3, fn, sfx), \
- PORT_GP_1(bank, 4, fn, sfx), PORT_GP_1(bank, 5, fn, sfx), \
- PORT_GP_1(bank, 6, fn, sfx), PORT_GP_1(bank, 7, fn, sfx), \
- PORT_GP_1(bank, 8, fn, sfx), PORT_GP_1(bank, 9, fn, sfx), \
- PORT_GP_1(bank, 10, fn, sfx), PORT_GP_1(bank, 11, fn, sfx), \
- PORT_GP_1(bank, 12, fn, sfx), PORT_GP_1(bank, 13, fn, sfx), \
- PORT_GP_1(bank, 14, fn, sfx), PORT_GP_1(bank, 15, fn, sfx), \
- PORT_GP_1(bank, 16, fn, sfx), PORT_GP_1(bank, 17, fn, sfx), \
- PORT_GP_1(bank, 18, fn, sfx), PORT_GP_1(bank, 19, fn, sfx), \
- PORT_GP_1(bank, 20, fn, sfx), PORT_GP_1(bank, 21, fn, sfx), \
- PORT_GP_1(bank, 22, fn, sfx), PORT_GP_1(bank, 23, fn, sfx), \
- PORT_GP_1(bank, 24, fn, sfx), PORT_GP_1(bank, 25, fn, sfx)
-
#define CPU_ALL_PORT(fn, sfx) \
PORT_GP_32(0, fn, sfx), \
PORT_GP_26(1, fn, sfx), \
@@ -787,23 +773,23 @@ enum {
static const u16 pinmux_data[] = {
PINMUX_DATA_GP_ALL(), /* PINMUX_DATA(GP_M_N_DATA, GP_M_N_FN...), */
- PINMUX_DATA(EX_CS0_N_MARK, FN_EX_CS0_N),
- PINMUX_DATA(RD_N_MARK, FN_RD_N),
- PINMUX_DATA(AUDIO_CLKA_MARK, FN_AUDIO_CLKA),
- PINMUX_DATA(VI0_CLK_MARK, FN_VI0_CLK),
- PINMUX_DATA(VI0_DATA0_VI0_B0_MARK, FN_VI0_DATA0_VI0_B0),
- PINMUX_DATA(VI0_DATA1_VI0_B1_MARK, FN_VI0_DATA1_VI0_B1),
- PINMUX_DATA(VI0_DATA2_VI0_B2_MARK, FN_VI0_DATA2_VI0_B2),
- PINMUX_DATA(VI0_DATA4_VI0_B4_MARK, FN_VI0_DATA4_VI0_B4),
- PINMUX_DATA(VI0_DATA5_VI0_B5_MARK, FN_VI0_DATA5_VI0_B5),
- PINMUX_DATA(VI0_DATA6_VI0_B6_MARK, FN_VI0_DATA6_VI0_B6),
- PINMUX_DATA(VI0_DATA7_VI0_B7_MARK, FN_VI0_DATA7_VI0_B7),
- PINMUX_DATA(USB0_PWEN_MARK, FN_USB0_PWEN),
- PINMUX_DATA(USB0_OVC_MARK, FN_USB0_OVC),
- PINMUX_DATA(USB1_PWEN_MARK, FN_USB1_PWEN),
- PINMUX_DATA(USB1_OVC_MARK, FN_USB1_OVC),
- PINMUX_DATA(DU0_DOTCLKIN_MARK, FN_DU0_DOTCLKIN),
- PINMUX_DATA(SD1_CLK_MARK, FN_SD1_CLK),
+ PINMUX_SINGLE(EX_CS0_N),
+ PINMUX_SINGLE(RD_N),
+ PINMUX_SINGLE(AUDIO_CLKA),
+ PINMUX_SINGLE(VI0_CLK),
+ PINMUX_SINGLE(VI0_DATA0_VI0_B0),
+ PINMUX_SINGLE(VI0_DATA1_VI0_B1),
+ PINMUX_SINGLE(VI0_DATA2_VI0_B2),
+ PINMUX_SINGLE(VI0_DATA4_VI0_B4),
+ PINMUX_SINGLE(VI0_DATA5_VI0_B5),
+ PINMUX_SINGLE(VI0_DATA6_VI0_B6),
+ PINMUX_SINGLE(VI0_DATA7_VI0_B7),
+ PINMUX_SINGLE(USB0_PWEN),
+ PINMUX_SINGLE(USB0_OVC),
+ PINMUX_SINGLE(USB1_PWEN),
+ PINMUX_SINGLE(USB1_OVC),
+ PINMUX_SINGLE(DU0_DOTCLKIN),
+ PINMUX_SINGLE(SD1_CLK),
/* IPSR0 */
PINMUX_IPSR_DATA(IP0_0, D0),
@@ -1740,6 +1726,82 @@ static const unsigned int audio_clkout_mux[] = {
AUDIO_CLKOUT_MARK,
};
+/* - AVB -------------------------------------------------------------------- */
+static const unsigned int avb_link_pins[] = {
+ RCAR_GP_PIN(5, 14),
+};
+static const unsigned int avb_link_mux[] = {
+ AVB_LINK_MARK,
+};
+static const unsigned int avb_magic_pins[] = {
+ RCAR_GP_PIN(5, 11),
+};
+static const unsigned int avb_magic_mux[] = {
+ AVB_MAGIC_MARK,
+};
+static const unsigned int avb_phy_int_pins[] = {
+ RCAR_GP_PIN(5, 16),
+};
+static const unsigned int avb_phy_int_mux[] = {
+ AVB_PHY_INT_MARK,
+};
+static const unsigned int avb_mdio_pins[] = {
+ RCAR_GP_PIN(5, 12), RCAR_GP_PIN(5, 9),
+};
+static const unsigned int avb_mdio_mux[] = {
+ AVB_MDC_MARK, AVB_MDIO_MARK,
+};
+static const unsigned int avb_mii_pins[] = {
+ RCAR_GP_PIN(5, 18), RCAR_GP_PIN(5, 19), RCAR_GP_PIN(5, 20),
+ RCAR_GP_PIN(5, 21),
+
+ RCAR_GP_PIN(5, 0), RCAR_GP_PIN(5, 1), RCAR_GP_PIN(5, 2),
+ RCAR_GP_PIN(5, 3),
+
+ RCAR_GP_PIN(5, 8), RCAR_GP_PIN(5, 13), RCAR_GP_PIN(5, 10),
+ RCAR_GP_PIN(5, 15), RCAR_GP_PIN(5, 26), RCAR_GP_PIN(5, 27),
+ RCAR_GP_PIN(5, 28), RCAR_GP_PIN(5, 29),
+};
+static const unsigned int avb_mii_mux[] = {
+ AVB_TXD0_MARK, AVB_TXD1_MARK, AVB_TXD2_MARK,
+ AVB_TXD3_MARK,
+
+ AVB_RXD0_MARK, AVB_RXD1_MARK, AVB_RXD2_MARK,
+ AVB_RXD3_MARK,
+
+ AVB_RX_ER_MARK, AVB_RX_CLK_MARK, AVB_RX_DV_MARK,
+ AVB_CRS_MARK, AVB_TX_EN_MARK, AVB_TX_ER_MARK,
+ AVB_TX_CLK_MARK, AVB_COL_MARK,
+};
+static const unsigned int avb_gmii_pins[] = {
+ RCAR_GP_PIN(5, 18), RCAR_GP_PIN(5, 19), RCAR_GP_PIN(5, 20),
+ RCAR_GP_PIN(5, 21), RCAR_GP_PIN(5, 22), RCAR_GP_PIN(5, 23),
+ RCAR_GP_PIN(5, 24), RCAR_GP_PIN(5, 25),
+
+ RCAR_GP_PIN(5, 0), RCAR_GP_PIN(5, 1), RCAR_GP_PIN(5, 2),
+ RCAR_GP_PIN(5, 3), RCAR_GP_PIN(5, 4), RCAR_GP_PIN(5, 5),
+ RCAR_GP_PIN(5, 6), RCAR_GP_PIN(5, 7),
+
+ RCAR_GP_PIN(5, 8), RCAR_GP_PIN(5, 13), RCAR_GP_PIN(5, 10),
+ RCAR_GP_PIN(5, 15), RCAR_GP_PIN(5, 30), RCAR_GP_PIN(5, 17),
+ RCAR_GP_PIN(5, 26), RCAR_GP_PIN(5, 27), RCAR_GP_PIN(5, 28),
+ RCAR_GP_PIN(5, 29),
+};
+static const unsigned int avb_gmii_mux[] = {
+ AVB_TXD0_MARK, AVB_TXD1_MARK, AVB_TXD2_MARK,
+ AVB_TXD3_MARK, AVB_TXD4_MARK, AVB_TXD5_MARK,
+ AVB_TXD6_MARK, AVB_TXD7_MARK,
+
+ AVB_RXD0_MARK, AVB_RXD1_MARK, AVB_RXD2_MARK,
+ AVB_RXD3_MARK, AVB_RXD4_MARK, AVB_RXD5_MARK,
+ AVB_RXD6_MARK, AVB_RXD7_MARK,
+
+ AVB_RX_ER_MARK, AVB_RX_CLK_MARK, AVB_RX_DV_MARK,
+ AVB_CRS_MARK, AVB_GTX_CLK_MARK, AVB_GTXREFCLK_MARK,
+ AVB_TX_EN_MARK, AVB_TX_ER_MARK, AVB_TX_CLK_MARK,
+ AVB_COL_MARK,
+};
+
/* - CAN -------------------------------------------------------------------- */
static const unsigned int can0_data_pins[] = {
@@ -3602,6 +3664,23 @@ static const unsigned int scifb2_data_d_pins[] = {
static const unsigned int scifb2_data_d_mux[] = {
SCIFB2_RXD_D_MARK, SCIFB2_TXD_D_MARK,
};
+
+/* - SCIF Clock ------------------------------------------------------------- */
+static const unsigned int scif_clk_pins[] = {
+ /* SCIF_CLK */
+ RCAR_GP_PIN(2, 29),
+};
+static const unsigned int scif_clk_mux[] = {
+ SCIF_CLK_MARK,
+};
+static const unsigned int scif_clk_b_pins[] = {
+ /* SCIF_CLK */
+ RCAR_GP_PIN(7, 19),
+};
+static const unsigned int scif_clk_b_mux[] = {
+ SCIF_CLK_B_MARK,
+};
+
/* - SDHI0 ------------------------------------------------------------------ */
static const unsigned int sdhi0_data1_pins[] = {
/* D0 */
@@ -4258,6 +4337,12 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(audio_clk_b_b),
SH_PFC_PIN_GROUP(audio_clk_c),
SH_PFC_PIN_GROUP(audio_clkout),
+ SH_PFC_PIN_GROUP(avb_link),
+ SH_PFC_PIN_GROUP(avb_magic),
+ SH_PFC_PIN_GROUP(avb_phy_int),
+ SH_PFC_PIN_GROUP(avb_mdio),
+ SH_PFC_PIN_GROUP(avb_mii),
+ SH_PFC_PIN_GROUP(avb_gmii),
SH_PFC_PIN_GROUP(can0_data),
SH_PFC_PIN_GROUP(can0_data_b),
SH_PFC_PIN_GROUP(can0_data_c),
@@ -4510,6 +4595,8 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(scifb2_data_c),
SH_PFC_PIN_GROUP(scifb2_clk_c),
SH_PFC_PIN_GROUP(scifb2_data_d),
+ SH_PFC_PIN_GROUP(scif_clk),
+ SH_PFC_PIN_GROUP(scif_clk_b),
SH_PFC_PIN_GROUP(sdhi0_data1),
SH_PFC_PIN_GROUP(sdhi0_data4),
SH_PFC_PIN_GROUP(sdhi0_ctrl),
@@ -4597,6 +4684,15 @@ static const char * const audio_clk_groups[] = {
"audio_clkout",
};
+static const char * const avb_groups[] = {
+ "avb_link",
+ "avb_magic",
+ "avb_phy_int",
+ "avb_mdio",
+ "avb_mii",
+ "avb_gmii",
+};
+
static const char * const can0_groups[] = {
"can0_data",
"can0_data_b",
@@ -4976,6 +5072,11 @@ static const char * const scifb2_groups[] = {
"scifb2_data_d",
};
+static const char * const scif_clk_groups[] = {
+ "scif_clk",
+ "scif_clk_b",
+};
+
static const char * const sdhi0_groups[] = {
"sdhi0_data1",
"sdhi0_data4",
@@ -5081,6 +5182,7 @@ static const char * const vin2_groups[] = {
static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(audio_clk),
+ SH_PFC_FUNCTION(avb),
SH_PFC_FUNCTION(can0),
SH_PFC_FUNCTION(can1),
SH_PFC_FUNCTION(du),
@@ -5126,6 +5228,7 @@ static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(scifb0),
SH_PFC_FUNCTION(scifb1),
SH_PFC_FUNCTION(scifb2),
+ SH_PFC_FUNCTION(scif_clk),
SH_PFC_FUNCTION(sdhi0),
SH_PFC_FUNCTION(sdhi1),
SH_PFC_FUNCTION(sdhi2),
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7794.c b/drivers/pinctrl/sh-pfc/pfc-r8a7794.c
index 086f6798b129..3718c7846bfd 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7794.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7794.c
@@ -15,25 +15,6 @@
#include "core.h"
#include "sh_pfc.h"
-#define PORT_GP_26(bank, fn, sfx) \
- PORT_GP_1(bank, 0, fn, sfx), PORT_GP_1(bank, 1, fn, sfx), \
- PORT_GP_1(bank, 2, fn, sfx), PORT_GP_1(bank, 3, fn, sfx), \
- PORT_GP_1(bank, 4, fn, sfx), PORT_GP_1(bank, 5, fn, sfx), \
- PORT_GP_1(bank, 6, fn, sfx), PORT_GP_1(bank, 7, fn, sfx), \
- PORT_GP_1(bank, 8, fn, sfx), PORT_GP_1(bank, 9, fn, sfx), \
- PORT_GP_1(bank, 10, fn, sfx), PORT_GP_1(bank, 11, fn, sfx), \
- PORT_GP_1(bank, 12, fn, sfx), PORT_GP_1(bank, 13, fn, sfx), \
- PORT_GP_1(bank, 14, fn, sfx), PORT_GP_1(bank, 15, fn, sfx), \
- PORT_GP_1(bank, 16, fn, sfx), PORT_GP_1(bank, 17, fn, sfx), \
- PORT_GP_1(bank, 18, fn, sfx), PORT_GP_1(bank, 19, fn, sfx), \
- PORT_GP_1(bank, 20, fn, sfx), PORT_GP_1(bank, 21, fn, sfx), \
- PORT_GP_1(bank, 22, fn, sfx), PORT_GP_1(bank, 23, fn, sfx), \
- PORT_GP_1(bank, 24, fn, sfx), PORT_GP_1(bank, 25, fn, sfx)
-
-#define PORT_GP_28(bank, fn, sfx) \
- PORT_GP_26(bank, fn, sfx), \
- PORT_GP_1(bank, 26, fn, sfx), PORT_GP_1(bank, 27, fn, sfx)
-
#define CPU_ALL_PORT(fn, sfx) \
PORT_GP_32(0, fn, sfx), \
PORT_GP_26(1, fn, sfx), \
@@ -618,28 +599,28 @@ enum {
static const u16 pinmux_data[] = {
PINMUX_DATA_GP_ALL(), /* PINMUX_DATA(GP_M_N_DATA, GP_M_N_FN...), */
- PINMUX_DATA(A2_MARK, FN_A2),
- PINMUX_DATA(WE0_N_MARK, FN_WE0_N),
- PINMUX_DATA(WE1_N_MARK, FN_WE1_N),
- PINMUX_DATA(DACK0_MARK, FN_DACK0),
- PINMUX_DATA(USB0_PWEN_MARK, FN_USB0_PWEN),
- PINMUX_DATA(USB0_OVC_MARK, FN_USB0_OVC),
- PINMUX_DATA(USB1_PWEN_MARK, FN_USB1_PWEN),
- PINMUX_DATA(USB1_OVC_MARK, FN_USB1_OVC),
- PINMUX_DATA(SD0_CLK_MARK, FN_SD0_CLK),
- PINMUX_DATA(SD0_CMD_MARK, FN_SD0_CMD),
- PINMUX_DATA(SD0_DATA0_MARK, FN_SD0_DATA0),
- PINMUX_DATA(SD0_DATA1_MARK, FN_SD0_DATA1),
- PINMUX_DATA(SD0_DATA2_MARK, FN_SD0_DATA2),
- PINMUX_DATA(SD0_DATA3_MARK, FN_SD0_DATA3),
- PINMUX_DATA(SD0_CD_MARK, FN_SD0_CD),
- PINMUX_DATA(SD0_WP_MARK, FN_SD0_WP),
- PINMUX_DATA(SD1_CLK_MARK, FN_SD1_CLK),
- PINMUX_DATA(SD1_CMD_MARK, FN_SD1_CMD),
- PINMUX_DATA(SD1_DATA0_MARK, FN_SD1_DATA0),
- PINMUX_DATA(SD1_DATA1_MARK, FN_SD1_DATA1),
- PINMUX_DATA(SD1_DATA2_MARK, FN_SD1_DATA2),
- PINMUX_DATA(SD1_DATA3_MARK, FN_SD1_DATA3),
+ PINMUX_SINGLE(A2),
+ PINMUX_SINGLE(WE0_N),
+ PINMUX_SINGLE(WE1_N),
+ PINMUX_SINGLE(DACK0),
+ PINMUX_SINGLE(USB0_PWEN),
+ PINMUX_SINGLE(USB0_OVC),
+ PINMUX_SINGLE(USB1_PWEN),
+ PINMUX_SINGLE(USB1_OVC),
+ PINMUX_SINGLE(SD0_CLK),
+ PINMUX_SINGLE(SD0_CMD),
+ PINMUX_SINGLE(SD0_DATA0),
+ PINMUX_SINGLE(SD0_DATA1),
+ PINMUX_SINGLE(SD0_DATA2),
+ PINMUX_SINGLE(SD0_DATA3),
+ PINMUX_SINGLE(SD0_CD),
+ PINMUX_SINGLE(SD0_WP),
+ PINMUX_SINGLE(SD1_CLK),
+ PINMUX_SINGLE(SD1_CMD),
+ PINMUX_SINGLE(SD1_DATA0),
+ PINMUX_SINGLE(SD1_DATA1),
+ PINMUX_SINGLE(SD1_DATA2),
+ PINMUX_SINGLE(SD1_DATA3),
/* IPSR0 */
PINMUX_IPSR_DATA(IP0_0, SD1_CD),
@@ -2644,6 +2625,21 @@ static const unsigned int scifb2_ctrl_pins[] = {
static const unsigned int scifb2_ctrl_mux[] = {
SCIFB2_RTS_N_MARK, SCIFB2_CTS_N_MARK,
};
+/* - SCIF Clock ------------------------------------------------------------- */
+static const unsigned int scif_clk_pins[] = {
+ /* SCIF_CLK */
+ RCAR_GP_PIN(1, 23),
+};
+static const unsigned int scif_clk_mux[] = {
+ SCIF_CLK_MARK,
+};
+static const unsigned int scif_clk_b_pins[] = {
+ /* SCIF_CLK */
+ RCAR_GP_PIN(3, 29),
+};
+static const unsigned int scif_clk_b_mux[] = {
+ SCIF_CLK_B_MARK,
+};
/* - SDHI0 ------------------------------------------------------------------ */
static const unsigned int sdhi0_data1_pins[] = {
/* D0 */
@@ -3071,6 +3067,8 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(scifb2_data),
SH_PFC_PIN_GROUP(scifb2_clk),
SH_PFC_PIN_GROUP(scifb2_ctrl),
+ SH_PFC_PIN_GROUP(scif_clk),
+ SH_PFC_PIN_GROUP(scif_clk_b),
SH_PFC_PIN_GROUP(sdhi0_data1),
SH_PFC_PIN_GROUP(sdhi0_data4),
SH_PFC_PIN_GROUP(sdhi0_ctrl),
@@ -3354,6 +3352,11 @@ static const char * const scifb2_groups[] = {
"scifb2_ctrl",
};
+static const char * const scif_clk_groups[] = {
+ "scif_clk",
+ "scif_clk_b",
+};
+
static const char * const sdhi0_groups[] = {
"sdhi0_data1",
"sdhi0_data4",
@@ -3441,6 +3444,7 @@ static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(scifb0),
SH_PFC_FUNCTION(scifb1),
SH_PFC_FUNCTION(scifb2),
+ SH_PFC_FUNCTION(scif_clk),
SH_PFC_FUNCTION(sdhi0),
SH_PFC_FUNCTION(sdhi1),
SH_PFC_FUNCTION(sdhi2),
diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7795.c b/drivers/pinctrl/sh-pfc/pfc-r8a7795.c
index 7ddb2adfc5a5..ce4f5cdb0579 100644
--- a/drivers/pinctrl/sh-pfc/pfc-r8a7795.c
+++ b/drivers/pinctrl/sh-pfc/pfc-r8a7795.c
@@ -13,46 +13,15 @@
#include "core.h"
#include "sh_pfc.h"
-#define PORT_GP_3(bank, fn, sfx) \
- PORT_GP_1(bank, 0, fn, sfx), PORT_GP_1(bank, 1, fn, sfx), \
- PORT_GP_1(bank, 2, fn, sfx), PORT_GP_1(bank, 3, fn, sfx)
-
-#define PORT_GP_14(bank, fn, sfx) \
- PORT_GP_3(bank, fn, sfx), \
- PORT_GP_1(bank, 4, fn, sfx), PORT_GP_1(bank, 5, fn, sfx), \
- PORT_GP_1(bank, 6, fn, sfx), PORT_GP_1(bank, 7, fn, sfx), \
- PORT_GP_1(bank, 8, fn, sfx), PORT_GP_1(bank, 9, fn, sfx), \
- PORT_GP_1(bank, 10, fn, sfx), PORT_GP_1(bank, 11, fn, sfx), \
- PORT_GP_1(bank, 12, fn, sfx), PORT_GP_1(bank, 13, fn, sfx), \
- PORT_GP_1(bank, 14, fn, sfx)
-
-#define PORT_GP_15(bank, fn, sfx) \
- PORT_GP_14(bank, fn, sfx), PORT_GP_1(bank, 15, fn, sfx)
-
-#define PORT_GP_17(bank, fn, sfx) \
- PORT_GP_15(bank, fn, sfx), \
- PORT_GP_1(bank, 16, fn, sfx), PORT_GP_1(bank, 17, fn, sfx)
-
-#define PORT_GP_25(bank, fn, sfx) \
- PORT_GP_17(bank, fn, sfx), \
- PORT_GP_1(bank, 18, fn, sfx), PORT_GP_1(bank, 19, fn, sfx), \
- PORT_GP_1(bank, 20, fn, sfx), PORT_GP_1(bank, 21, fn, sfx), \
- PORT_GP_1(bank, 22, fn, sfx), PORT_GP_1(bank, 23, fn, sfx), \
- PORT_GP_1(bank, 24, fn, sfx), PORT_GP_1(bank, 25, fn, sfx)
-
-#define PORT_GP_27(bank, fn, sfx) \
- PORT_GP_25(bank, fn, sfx), \
- PORT_GP_1(bank, 26, fn, sfx), PORT_GP_1(bank, 27, fn, sfx)
-
#define CPU_ALL_PORT(fn, sfx) \
- PORT_GP_15(0, fn, sfx), \
- PORT_GP_27(1, fn, sfx), \
- PORT_GP_14(2, fn, sfx), \
- PORT_GP_15(3, fn, sfx), \
- PORT_GP_17(4, fn, sfx), \
- PORT_GP_25(5, fn, sfx), \
+ PORT_GP_16(0, fn, sfx), \
+ PORT_GP_28(1, fn, sfx), \
+ PORT_GP_15(2, fn, sfx), \
+ PORT_GP_16(3, fn, sfx), \
+ PORT_GP_18(4, fn, sfx), \
+ PORT_GP_26(5, fn, sfx), \
PORT_GP_32(6, fn, sfx), \
- PORT_GP_3(7, fn, sfx)
+ PORT_GP_4(7, fn, sfx)
/*
* F_() : just information
* FM() : macro for FN_xxx / xxx_MARK
@@ -495,7 +464,7 @@ FM(IP16_31_28) IP16_31_28
#define MOD_SEL1_13 FM(SEL_SCIF3_0) FM(SEL_SCIF3_1)
#define MOD_SEL1_12 FM(SEL_SCIF2_0) FM(SEL_SCIF2_1)
#define MOD_SEL1_11 FM(SEL_SCIF1_0) FM(SEL_SCIF1_1)
-#define MOD_SEL1_10 FM(SEL_SCIF_0) FM(SEL_SCIF_1)
+#define MOD_SEL1_10 FM(SEL_SATA_0) FM(SEL_SATA_1)
#define MOD_SEL1_9 FM(SEL_REMOCON_0) FM(SEL_REMOCON_1)
#define MOD_SEL1_6 FM(SEL_RCAN0_0) FM(SEL_RCAN0_1)
#define MOD_SEL1_5 FM(SEL_PWM6_0) FM(SEL_PWM6_1)
@@ -580,6 +549,25 @@ enum {
static const u16 pinmux_data[] = {
PINMUX_DATA_GP_ALL(),
+ PINMUX_SINGLE(AVS1),
+ PINMUX_SINGLE(AVS2),
+ PINMUX_SINGLE(HDMI0_CEC),
+ PINMUX_SINGLE(HDMI1_CEC),
+ PINMUX_SINGLE(MSIOF0_RXD),
+ PINMUX_SINGLE(MSIOF0_SCK),
+ PINMUX_SINGLE(MSIOF0_TXD),
+ PINMUX_SINGLE(SD2_CMD),
+ PINMUX_SINGLE(SD3_CLK),
+ PINMUX_SINGLE(SD3_CMD),
+ PINMUX_SINGLE(SD3_DAT0),
+ PINMUX_SINGLE(SD3_DAT1),
+ PINMUX_SINGLE(SD3_DAT2),
+ PINMUX_SINGLE(SD3_DAT3),
+ PINMUX_SINGLE(SD3_DS),
+ PINMUX_SINGLE(SSI_SCK5),
+ PINMUX_SINGLE(SSI_SDATA5),
+ PINMUX_SINGLE(SSI_WS5),
+
/* IPSR0 */
PINMUX_IPSR_DATA(IP0_3_0, AVB_MDC),
PINMUX_IPSR_MSEL(IP0_3_0, MSIOF2_SS2_C, SEL_MSIOF2_2),
@@ -1033,7 +1021,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_DATA(IP9_19_16, SD2_DAT3),
PINMUX_IPSR_DATA(IP9_23_20, SD2_DS),
- PINMUX_IPSR_MSEL(IP9_23_20, SATA_DEVSLP_B, SEL_SCIF_1),
+ PINMUX_IPSR_MSEL(IP9_23_20, SATA_DEVSLP_B, SEL_SATA_1),
PINMUX_IPSR_DATA(IP9_27_24, SD3_DAT4),
PINMUX_IPSR_MSEL(IP9_27_24, SD2_CD_A, SEL_SDHI2_0),
@@ -1293,7 +1281,7 @@ static const u16 pinmux_data[] = {
PINMUX_IPSR_DATA(IP15_11_8, SSI_SDATA6),
PINMUX_IPSR_MSEL(IP15_11_8, SIM0_CLK_D, SEL_SIMCARD_3),
- PINMUX_IPSR_MSEL(IP15_11_8, SATA_DEVSLP_A, SEL_SCIF_0),
+ PINMUX_IPSR_MSEL(IP15_11_8, SATA_DEVSLP_A, SEL_SATA_0),
PINMUX_IPSR_DATA(IP15_15_12, SSI_SCK78),
PINMUX_IPSR_MSEL(IP15_15_12, HRX2_B, SEL_HSCIF2_1),
@@ -1612,6 +1600,191 @@ static const unsigned int avb_avtp_capture_b_mux[] = {
AVB_AVTP_CAPTURE_B_MARK,
};
+/* - HSCIF0 ----------------------------------------------------------------- */
+static const unsigned int hscif0_data_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(5, 13), RCAR_GP_PIN(5, 14),
+};
+static const unsigned int hscif0_data_mux[] = {
+ HRX0_MARK, HTX0_MARK,
+};
+static const unsigned int hscif0_clk_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(5, 12),
+};
+static const unsigned int hscif0_clk_mux[] = {
+ HSCK0_MARK,
+};
+static const unsigned int hscif0_ctrl_pins[] = {
+ /* RTS, CTS */
+ RCAR_GP_PIN(5, 16), RCAR_GP_PIN(5, 15),
+};
+static const unsigned int hscif0_ctrl_mux[] = {
+ HRTS0_N_MARK, HCTS0_N_MARK,
+};
+/* - HSCIF1 ----------------------------------------------------------------- */
+static const unsigned int hscif1_data_a_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(5, 5), RCAR_GP_PIN(5, 6),
+};
+static const unsigned int hscif1_data_a_mux[] = {
+ HRX1_A_MARK, HTX1_A_MARK,
+};
+static const unsigned int hscif1_clk_a_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(6, 21),
+};
+static const unsigned int hscif1_clk_a_mux[] = {
+ HSCK1_A_MARK,
+};
+static const unsigned int hscif1_ctrl_a_pins[] = {
+ /* RTS, CTS */
+ RCAR_GP_PIN(5, 8), RCAR_GP_PIN(5, 7),
+};
+static const unsigned int hscif1_ctrl_a_mux[] = {
+ HRTS1_N_A_MARK, HCTS1_N_A_MARK,
+};
+
+static const unsigned int hscif1_data_b_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(5, 1), RCAR_GP_PIN(5, 2),
+};
+static const unsigned int hscif1_data_b_mux[] = {
+ HRX1_B_MARK, HTX1_B_MARK,
+};
+static const unsigned int hscif1_clk_b_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(5, 0),
+};
+static const unsigned int hscif1_clk_b_mux[] = {
+ HSCK1_B_MARK,
+};
+static const unsigned int hscif1_ctrl_b_pins[] = {
+ /* RTS, CTS */
+ RCAR_GP_PIN(5, 4), RCAR_GP_PIN(5, 3),
+};
+static const unsigned int hscif1_ctrl_b_mux[] = {
+ HRTS1_N_B_MARK, HCTS1_N_B_MARK,
+};
+/* - HSCIF2 ----------------------------------------------------------------- */
+static const unsigned int hscif2_data_a_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(6, 8), RCAR_GP_PIN(6, 9),
+};
+static const unsigned int hscif2_data_a_mux[] = {
+ HRX2_A_MARK, HTX2_A_MARK,
+};
+static const unsigned int hscif2_clk_a_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(6, 10),
+};
+static const unsigned int hscif2_clk_a_mux[] = {
+ HSCK2_A_MARK,
+};
+static const unsigned int hscif2_ctrl_a_pins[] = {
+ /* RTS, CTS */
+ RCAR_GP_PIN(6, 7), RCAR_GP_PIN(6, 6),
+};
+static const unsigned int hscif2_ctrl_a_mux[] = {
+ HRTS2_N_A_MARK, HCTS2_N_A_MARK,
+};
+
+static const unsigned int hscif2_data_b_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(6, 17), RCAR_GP_PIN(6, 18),
+};
+static const unsigned int hscif2_data_b_mux[] = {
+ HRX2_B_MARK, HTX2_B_MARK,
+};
+static const unsigned int hscif2_clk_b_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(6, 21),
+};
+static const unsigned int hscif2_clk_b_mux[] = {
+ HSCK1_B_MARK,
+};
+static const unsigned int hscif2_ctrl_b_pins[] = {
+ /* RTS, CTS */
+ RCAR_GP_PIN(6, 20), RCAR_GP_PIN(6, 19),
+};
+static const unsigned int hscif2_ctrl_b_mux[] = {
+ HRTS2_N_B_MARK, HCTS2_N_B_MARK,
+};
+/* - HSCIF3 ----------------------------------------------------------------- */
+static const unsigned int hscif3_data_a_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(1, 23), RCAR_GP_PIN(1, 24),
+};
+static const unsigned int hscif3_data_a_mux[] = {
+ HRX3_A_MARK, HTX3_A_MARK,
+};
+static const unsigned int hscif3_clk_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(1, 22),
+};
+static const unsigned int hscif3_clk_mux[] = {
+ HSCK3_MARK,
+};
+static const unsigned int hscif3_ctrl_pins[] = {
+ /* RTS, CTS */
+ RCAR_GP_PIN(1, 26), RCAR_GP_PIN(1, 25),
+};
+static const unsigned int hscif3_ctrl_mux[] = {
+ HRTS3_N_MARK, HCTS3_N_MARK,
+};
+
+static const unsigned int hscif3_data_b_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(0, 10), RCAR_GP_PIN(0, 11),
+};
+static const unsigned int hscif3_data_b_mux[] = {
+ HRX3_B_MARK, HTX3_B_MARK,
+};
+static const unsigned int hscif3_data_c_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(0, 14), RCAR_GP_PIN(0, 15),
+};
+static const unsigned int hscif3_data_c_mux[] = {
+ HRX3_C_MARK, HTX3_C_MARK,
+};
+static const unsigned int hscif3_data_d_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(2, 7), RCAR_GP_PIN(2, 8),
+};
+static const unsigned int hscif3_data_d_mux[] = {
+ HRX3_D_MARK, HTX3_D_MARK,
+};
+/* - HSCIF4 ----------------------------------------------------------------- */
+static const unsigned int hscif4_data_a_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(1, 12), RCAR_GP_PIN(1, 13),
+};
+static const unsigned int hscif4_data_a_mux[] = {
+ HRX4_A_MARK, HTX4_A_MARK,
+};
+static const unsigned int hscif4_clk_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(1, 11),
+};
+static const unsigned int hscif4_clk_mux[] = {
+ HSCK4_MARK,
+};
+static const unsigned int hscif4_ctrl_pins[] = {
+ /* RTS, CTS */
+ RCAR_GP_PIN(1, 15), RCAR_GP_PIN(1, 14),
+};
+static const unsigned int hscif4_ctrl_mux[] = {
+ HRTS4_N_MARK, HCTS3_N_MARK,
+};
+
+static const unsigned int hscif4_data_b_pins[] = {
+ /* RX, TX */
+ RCAR_GP_PIN(1, 8), RCAR_GP_PIN(1, 11),
+};
+static const unsigned int hscif4_data_b_mux[] = {
+ HRX4_B_MARK, HTX4_B_MARK,
+};
+
/* - I2C -------------------------------------------------------------------- */
static const unsigned int i2c1_a_pins[] = {
/* SDA, SCL */
@@ -1663,6 +1836,678 @@ static const unsigned int i2c6_c_mux[] = {
SDA6_C_MARK, SCL6_C_MARK,
};
+/* - MSIOF0 ----------------------------------------------------------------- */
+static const unsigned int msiof0_clk_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(5, 17),
+};
+static const unsigned int msiof0_clk_mux[] = {
+ MSIOF0_SCK_MARK,
+};
+static const unsigned int msiof0_sync_pins[] = {
+ /* SYNC */
+ RCAR_GP_PIN(5, 18),
+};
+static const unsigned int msiof0_sync_mux[] = {
+ MSIOF0_SYNC_MARK,
+};
+static const unsigned int msiof0_ss1_pins[] = {
+ /* SS1 */
+ RCAR_GP_PIN(5, 19),
+};
+static const unsigned int msiof0_ss1_mux[] = {
+ MSIOF0_SS1_MARK,
+};
+static const unsigned int msiof0_ss2_pins[] = {
+ /* SS2 */
+ RCAR_GP_PIN(5, 21),
+};
+static const unsigned int msiof0_ss2_mux[] = {
+ MSIOF0_SS2_MARK,
+};
+static const unsigned int msiof0_txd_pins[] = {
+ /* TXD */
+ RCAR_GP_PIN(5, 20),
+};
+static const unsigned int msiof0_txd_mux[] = {
+ MSIOF0_TXD_MARK,
+};
+static const unsigned int msiof0_rxd_pins[] = {
+ /* RXD */
+ RCAR_GP_PIN(5, 22),
+};
+static const unsigned int msiof0_rxd_mux[] = {
+ MSIOF0_RXD_MARK,
+};
+/* - MSIOF1 ----------------------------------------------------------------- */
+static const unsigned int msiof1_clk_a_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(6, 8),
+};
+static const unsigned int msiof1_clk_a_mux[] = {
+ MSIOF1_SCK_A_MARK,
+};
+static const unsigned int msiof1_sync_a_pins[] = {
+ /* SYNC */
+ RCAR_GP_PIN(6, 9),
+};
+static const unsigned int msiof1_sync_a_mux[] = {
+ MSIOF1_SYNC_A_MARK,
+};
+static const unsigned int msiof1_ss1_a_pins[] = {
+ /* SS1 */
+ RCAR_GP_PIN(6, 5),
+};
+static const unsigned int msiof1_ss1_a_mux[] = {
+ MSIOF1_SS1_A_MARK,
+};
+static const unsigned int msiof1_ss2_a_pins[] = {
+ /* SS2 */
+ RCAR_GP_PIN(6, 6),
+};
+static const unsigned int msiof1_ss2_a_mux[] = {
+ MSIOF1_SS2_A_MARK,
+};
+static const unsigned int msiof1_txd_a_pins[] = {
+ /* TXD */
+ RCAR_GP_PIN(6, 7),
+};
+static const unsigned int msiof1_txd_a_mux[] = {
+ MSIOF1_TXD_A_MARK,
+};
+static const unsigned int msiof1_rxd_a_pins[] = {
+ /* RXD */
+ RCAR_GP_PIN(6, 10),
+};
+static const unsigned int msiof1_rxd_a_mux[] = {
+ MSIOF1_RXD_A_MARK,
+};
+static const unsigned int msiof1_clk_b_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(5, 9),
+};
+static const unsigned int msiof1_clk_b_mux[] = {
+ MSIOF1_SCK_B_MARK,
+};
+static const unsigned int msiof1_sync_b_pins[] = {
+ /* SYNC */
+ RCAR_GP_PIN(5, 3),
+};
+static const unsigned int msiof1_sync_b_mux[] = {
+ MSIOF1_SYNC_B_MARK,
+};
+static const unsigned int msiof1_ss1_b_pins[] = {
+ /* SS1 */
+ RCAR_GP_PIN(5, 4),
+};
+static const unsigned int msiof1_ss1_b_mux[] = {
+ MSIOF1_SS1_B_MARK,
+};
+static const unsigned int msiof1_ss2_b_pins[] = {
+ /* SS2 */
+ RCAR_GP_PIN(5, 0),
+};
+static const unsigned int msiof1_ss2_b_mux[] = {
+ MSIOF1_SS2_B_MARK,
+};
+static const unsigned int msiof1_txd_b_pins[] = {
+ /* TXD */
+ RCAR_GP_PIN(5, 8),
+};
+static const unsigned int msiof1_txd_b_mux[] = {
+ MSIOF1_TXD_B_MARK,
+};
+static const unsigned int msiof1_rxd_b_pins[] = {
+ /* RXD */
+ RCAR_GP_PIN(5, 7),
+};
+static const unsigned int msiof1_rxd_b_mux[] = {
+ MSIOF1_RXD_B_MARK,
+};
+static const unsigned int msiof1_clk_c_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(6, 17),
+};
+static const unsigned int msiof1_clk_c_mux[] = {
+ MSIOF1_SCK_C_MARK,
+};
+static const unsigned int msiof1_sync_c_pins[] = {
+ /* SYNC */
+ RCAR_GP_PIN(6, 18),
+};
+static const unsigned int msiof1_sync_c_mux[] = {
+ MSIOF1_SYNC_C_MARK,
+};
+static const unsigned int msiof1_ss1_c_pins[] = {
+ /* SS1 */
+ RCAR_GP_PIN(6, 21),
+};
+static const unsigned int msiof1_ss1_c_mux[] = {
+ MSIOF1_SS1_C_MARK,
+};
+static const unsigned int msiof1_ss2_c_pins[] = {
+ /* SS2 */
+ RCAR_GP_PIN(6, 27),
+};
+static const unsigned int msiof1_ss2_c_mux[] = {
+ MSIOF1_SS2_C_MARK,
+};
+static const unsigned int msiof1_txd_c_pins[] = {
+ /* TXD */
+ RCAR_GP_PIN(6, 20),
+};
+static const unsigned int msiof1_txd_c_mux[] = {
+ MSIOF1_TXD_C_MARK,
+};
+static const unsigned int msiof1_rxd_c_pins[] = {
+ /* RXD */
+ RCAR_GP_PIN(6, 19),
+};
+static const unsigned int msiof1_rxd_c_mux[] = {
+ MSIOF1_RXD_C_MARK,
+};
+static const unsigned int msiof1_clk_d_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(5, 12),
+};
+static const unsigned int msiof1_clk_d_mux[] = {
+ MSIOF1_SCK_D_MARK,
+};
+static const unsigned int msiof1_sync_d_pins[] = {
+ /* SYNC */
+ RCAR_GP_PIN(5, 15),
+};
+static const unsigned int msiof1_sync_d_mux[] = {
+ MSIOF1_SYNC_D_MARK,
+};
+static const unsigned int msiof1_ss1_d_pins[] = {
+ /* SS1 */
+ RCAR_GP_PIN(5, 16),
+};
+static const unsigned int msiof1_ss1_d_mux[] = {
+ MSIOF1_SS1_D_MARK,
+};
+static const unsigned int msiof1_ss2_d_pins[] = {
+ /* SS2 */
+ RCAR_GP_PIN(5, 21),
+};
+static const unsigned int msiof1_ss2_d_mux[] = {
+ MSIOF1_SS2_D_MARK,
+};
+static const unsigned int msiof1_txd_d_pins[] = {
+ /* TXD */
+ RCAR_GP_PIN(5, 14),
+};
+static const unsigned int msiof1_txd_d_mux[] = {
+ MSIOF1_TXD_D_MARK,
+};
+static const unsigned int msiof1_rxd_d_pins[] = {
+ /* RXD */
+ RCAR_GP_PIN(5, 13),
+};
+static const unsigned int msiof1_rxd_d_mux[] = {
+ MSIOF1_RXD_D_MARK,
+};
+static const unsigned int msiof1_clk_e_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(3, 0),
+};
+static const unsigned int msiof1_clk_e_mux[] = {
+ MSIOF1_SCK_E_MARK,
+};
+static const unsigned int msiof1_sync_e_pins[] = {
+ /* SYNC */
+ RCAR_GP_PIN(3, 1),
+};
+static const unsigned int msiof1_sync_e_mux[] = {
+ MSIOF1_SYNC_E_MARK,
+};
+static const unsigned int msiof1_ss1_e_pins[] = {
+ /* SS1 */
+ RCAR_GP_PIN(3, 4),
+};
+static const unsigned int msiof1_ss1_e_mux[] = {
+ MSIOF1_SS1_E_MARK,
+};
+static const unsigned int msiof1_ss2_e_pins[] = {
+ /* SS2 */
+ RCAR_GP_PIN(3, 5),
+};
+static const unsigned int msiof1_ss2_e_mux[] = {
+ MSIOF1_SS2_E_MARK,
+};
+static const unsigned int msiof1_txd_e_pins[] = {
+ /* TXD */
+ RCAR_GP_PIN(3, 3),
+};
+static const unsigned int msiof1_txd_e_mux[] = {
+ MSIOF1_TXD_E_MARK,
+};
+static const unsigned int msiof1_rxd_e_pins[] = {
+ /* RXD */
+ RCAR_GP_PIN(3, 2),
+};
+static const unsigned int msiof1_rxd_e_mux[] = {
+ MSIOF1_RXD_E_MARK,
+};
+static const unsigned int msiof1_clk_f_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(5, 23),
+};
+static const unsigned int msiof1_clk_f_mux[] = {
+ MSIOF1_SCK_F_MARK,
+};
+static const unsigned int msiof1_sync_f_pins[] = {
+ /* SYNC */
+ RCAR_GP_PIN(5, 24),
+};
+static const unsigned int msiof1_sync_f_mux[] = {
+ MSIOF1_SYNC_F_MARK,
+};
+static const unsigned int msiof1_ss1_f_pins[] = {
+ /* SS1 */
+ RCAR_GP_PIN(6, 1),
+};
+static const unsigned int msiof1_ss1_f_mux[] = {
+ MSIOF1_SS1_F_MARK,
+};
+static const unsigned int msiof1_ss2_f_pins[] = {
+ /* SS2 */
+ RCAR_GP_PIN(6, 2),
+};
+static const unsigned int msiof1_ss2_f_mux[] = {
+ MSIOF1_SS2_F_MARK,
+};
+static const unsigned int msiof1_txd_f_pins[] = {
+ /* TXD */
+ RCAR_GP_PIN(6, 0),
+};
+static const unsigned int msiof1_txd_f_mux[] = {
+ MSIOF1_TXD_F_MARK,
+};
+static const unsigned int msiof1_rxd_f_pins[] = {
+ /* RXD */
+ RCAR_GP_PIN(5, 25),
+};
+static const unsigned int msiof1_rxd_f_mux[] = {
+ MSIOF1_RXD_F_MARK,
+};
+static const unsigned int msiof1_clk_g_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(3, 6),
+};
+static const unsigned int msiof1_clk_g_mux[] = {
+ MSIOF1_SCK_G_MARK,
+};
+static const unsigned int msiof1_sync_g_pins[] = {
+ /* SYNC */
+ RCAR_GP_PIN(3, 7),
+};
+static const unsigned int msiof1_sync_g_mux[] = {
+ MSIOF1_SYNC_G_MARK,
+};
+static const unsigned int msiof1_ss1_g_pins[] = {
+ /* SS1 */
+ RCAR_GP_PIN(3, 10),
+};
+static const unsigned int msiof1_ss1_g_mux[] = {
+ MSIOF1_SS1_G_MARK,
+};
+static const unsigned int msiof1_ss2_g_pins[] = {
+ /* SS2 */
+ RCAR_GP_PIN(3, 11),
+};
+static const unsigned int msiof1_ss2_g_mux[] = {
+ MSIOF1_SS2_G_MARK,
+};
+static const unsigned int msiof1_txd_g_pins[] = {
+ /* TXD */
+ RCAR_GP_PIN(3, 9),
+};
+static const unsigned int msiof1_txd_g_mux[] = {
+ MSIOF1_TXD_G_MARK,
+};
+static const unsigned int msiof1_rxd_g_pins[] = {
+ /* RXD */
+ RCAR_GP_PIN(3, 8),
+};
+static const unsigned int msiof1_rxd_g_mux[] = {
+ MSIOF1_RXD_G_MARK,
+};
+/* - MSIOF2 ----------------------------------------------------------------- */
+static const unsigned int msiof2_clk_a_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(1, 9),
+};
+static const unsigned int msiof2_clk_a_mux[] = {
+ MSIOF2_SCK_A_MARK,
+};
+static const unsigned int msiof2_sync_a_pins[] = {
+ /* SYNC */
+ RCAR_GP_PIN(1, 8),
+};
+static const unsigned int msiof2_sync_a_mux[] = {
+ MSIOF2_SYNC_A_MARK,
+};
+static const unsigned int msiof2_ss1_a_pins[] = {
+ /* SS1 */
+ RCAR_GP_PIN(1, 6),
+};
+static const unsigned int msiof2_ss1_a_mux[] = {
+ MSIOF2_SS1_A_MARK,
+};
+static const unsigned int msiof2_ss2_a_pins[] = {
+ /* SS2 */
+ RCAR_GP_PIN(1, 7),
+};
+static const unsigned int msiof2_ss2_a_mux[] = {
+ MSIOF2_SS2_A_MARK,
+};
+static const unsigned int msiof2_txd_a_pins[] = {
+ /* TXD */
+ RCAR_GP_PIN(1, 11),
+};
+static const unsigned int msiof2_txd_a_mux[] = {
+ MSIOF2_TXD_A_MARK,
+};
+static const unsigned int msiof2_rxd_a_pins[] = {
+ /* RXD */
+ RCAR_GP_PIN(1, 10),
+};
+static const unsigned int msiof2_rxd_a_mux[] = {
+ MSIOF2_RXD_A_MARK,
+};
+static const unsigned int msiof2_clk_b_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(0, 4),
+};
+static const unsigned int msiof2_clk_b_mux[] = {
+ MSIOF2_SCK_B_MARK,
+};
+static const unsigned int msiof2_sync_b_pins[] = {
+ /* SYNC */
+ RCAR_GP_PIN(0, 5),
+};
+static const unsigned int msiof2_sync_b_mux[] = {
+ MSIOF2_SYNC_B_MARK,
+};
+static const unsigned int msiof2_ss1_b_pins[] = {
+ /* SS1 */
+ RCAR_GP_PIN(0, 0),
+};
+static const unsigned int msiof2_ss1_b_mux[] = {
+ MSIOF2_SS1_B_MARK,
+};
+static const unsigned int msiof2_ss2_b_pins[] = {
+ /* SS2 */
+ RCAR_GP_PIN(0, 1),
+};
+static const unsigned int msiof2_ss2_b_mux[] = {
+ MSIOF2_SS2_B_MARK,
+};
+static const unsigned int msiof2_txd_b_pins[] = {
+ /* TXD */
+ RCAR_GP_PIN(0, 7),
+};
+static const unsigned int msiof2_txd_b_mux[] = {
+ MSIOF2_TXD_B_MARK,
+};
+static const unsigned int msiof2_rxd_b_pins[] = {
+ /* RXD */
+ RCAR_GP_PIN(0, 6),
+};
+static const unsigned int msiof2_rxd_b_mux[] = {
+ MSIOF2_RXD_B_MARK,
+};
+static const unsigned int msiof2_clk_c_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(2, 12),
+};
+static const unsigned int msiof2_clk_c_mux[] = {
+ MSIOF2_SCK_C_MARK,
+};
+static const unsigned int msiof2_sync_c_pins[] = {
+ /* SYNC */
+ RCAR_GP_PIN(2, 11),
+};
+static const unsigned int msiof2_sync_c_mux[] = {
+ MSIOF2_SYNC_C_MARK,
+};
+static const unsigned int msiof2_ss1_c_pins[] = {
+ /* SS1 */
+ RCAR_GP_PIN(2, 10),
+};
+static const unsigned int msiof2_ss1_c_mux[] = {
+ MSIOF2_SS1_C_MARK,
+};
+static const unsigned int msiof2_ss2_c_pins[] = {
+ /* SS2 */
+ RCAR_GP_PIN(2, 9),
+};
+static const unsigned int msiof2_ss2_c_mux[] = {
+ MSIOF2_SS2_C_MARK,
+};
+static const unsigned int msiof2_txd_c_pins[] = {
+ /* TXD */
+ RCAR_GP_PIN(2, 14),
+};
+static const unsigned int msiof2_txd_c_mux[] = {
+ MSIOF2_TXD_C_MARK,
+};
+static const unsigned int msiof2_rxd_c_pins[] = {
+ /* RXD */
+ RCAR_GP_PIN(2, 13),
+};
+static const unsigned int msiof2_rxd_c_mux[] = {
+ MSIOF2_RXD_C_MARK,
+};
+static const unsigned int msiof2_clk_d_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(0, 8),
+};
+static const unsigned int msiof2_clk_d_mux[] = {
+ MSIOF2_SCK_D_MARK,
+};
+static const unsigned int msiof2_sync_d_pins[] = {
+ /* SYNC */
+ RCAR_GP_PIN(0, 9),
+};
+static const unsigned int msiof2_sync_d_mux[] = {
+ MSIOF2_SYNC_D_MARK,
+};
+static const unsigned int msiof2_ss1_d_pins[] = {
+ /* SS1 */
+ RCAR_GP_PIN(0, 12),
+};
+static const unsigned int msiof2_ss1_d_mux[] = {
+ MSIOF2_SS1_D_MARK,
+};
+static const unsigned int msiof2_ss2_d_pins[] = {
+ /* SS2 */
+ RCAR_GP_PIN(0, 13),
+};
+static const unsigned int msiof2_ss2_d_mux[] = {
+ MSIOF2_SS2_D_MARK,
+};
+static const unsigned int msiof2_txd_d_pins[] = {
+ /* TXD */
+ RCAR_GP_PIN(0, 11),
+};
+static const unsigned int msiof2_txd_d_mux[] = {
+ MSIOF2_TXD_D_MARK,
+};
+static const unsigned int msiof2_rxd_d_pins[] = {
+ /* RXD */
+ RCAR_GP_PIN(0, 10),
+};
+static const unsigned int msiof2_rxd_d_mux[] = {
+ MSIOF2_RXD_D_MARK,
+};
+/* - MSIOF3 ----------------------------------------------------------------- */
+static const unsigned int msiof3_clk_a_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(0, 0),
+};
+static const unsigned int msiof3_clk_a_mux[] = {
+ MSIOF3_SCK_A_MARK,
+};
+static const unsigned int msiof3_sync_a_pins[] = {
+ /* SYNC */
+ RCAR_GP_PIN(0, 1),
+};
+static const unsigned int msiof3_sync_a_mux[] = {
+ MSIOF3_SYNC_A_MARK,
+};
+static const unsigned int msiof3_ss1_a_pins[] = {
+ /* SS1 */
+ RCAR_GP_PIN(0, 14),
+};
+static const unsigned int msiof3_ss1_a_mux[] = {
+ MSIOF3_SS1_A_MARK,
+};
+static const unsigned int msiof3_ss2_a_pins[] = {
+ /* SS2 */
+ RCAR_GP_PIN(0, 15),
+};
+static const unsigned int msiof3_ss2_a_mux[] = {
+ MSIOF3_SS2_A_MARK,
+};
+static const unsigned int msiof3_txd_a_pins[] = {
+ /* TXD */
+ RCAR_GP_PIN(0, 3),
+};
+static const unsigned int msiof3_txd_a_mux[] = {
+ MSIOF3_TXD_A_MARK,
+};
+static const unsigned int msiof3_rxd_a_pins[] = {
+ /* RXD */
+ RCAR_GP_PIN(0, 2),
+};
+static const unsigned int msiof3_rxd_a_mux[] = {
+ MSIOF3_RXD_A_MARK,
+};
+static const unsigned int msiof3_clk_b_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(1, 2),
+};
+static const unsigned int msiof3_clk_b_mux[] = {
+ MSIOF3_SCK_B_MARK,
+};
+static const unsigned int msiof3_sync_b_pins[] = {
+ /* SYNC */
+ RCAR_GP_PIN(1, 0),
+};
+static const unsigned int msiof3_sync_b_mux[] = {
+ MSIOF3_SYNC_B_MARK,
+};
+static const unsigned int msiof3_ss1_b_pins[] = {
+ /* SS1 */
+ RCAR_GP_PIN(1, 4),
+};
+static const unsigned int msiof3_ss1_b_mux[] = {
+ MSIOF3_SS1_B_MARK,
+};
+static const unsigned int msiof3_ss2_b_pins[] = {
+ /* SS2 */
+ RCAR_GP_PIN(1, 5),
+};
+static const unsigned int msiof3_ss2_b_mux[] = {
+ MSIOF3_SS2_B_MARK,
+};
+static const unsigned int msiof3_txd_b_pins[] = {
+ /* TXD */
+ RCAR_GP_PIN(1, 1),
+};
+static const unsigned int msiof3_txd_b_mux[] = {
+ MSIOF3_TXD_B_MARK,
+};
+static const unsigned int msiof3_rxd_b_pins[] = {
+ /* RXD */
+ RCAR_GP_PIN(1, 3),
+};
+static const unsigned int msiof3_rxd_b_mux[] = {
+ MSIOF3_RXD_B_MARK,
+};
+static const unsigned int msiof3_clk_c_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(1, 12),
+};
+static const unsigned int msiof3_clk_c_mux[] = {
+ MSIOF3_SCK_C_MARK,
+};
+static const unsigned int msiof3_sync_c_pins[] = {
+ /* SYNC */
+ RCAR_GP_PIN(1, 13),
+};
+static const unsigned int msiof3_sync_c_mux[] = {
+ MSIOF3_SYNC_C_MARK,
+};
+static const unsigned int msiof3_txd_c_pins[] = {
+ /* TXD */
+ RCAR_GP_PIN(1, 15),
+};
+static const unsigned int msiof3_txd_c_mux[] = {
+ MSIOF3_TXD_C_MARK,
+};
+static const unsigned int msiof3_rxd_c_pins[] = {
+ /* RXD */
+ RCAR_GP_PIN(1, 14),
+};
+static const unsigned int msiof3_rxd_c_mux[] = {
+ MSIOF3_RXD_C_MARK,
+};
+static const unsigned int msiof3_clk_d_pins[] = {
+ /* SCK */
+ RCAR_GP_PIN(1, 22),
+};
+static const unsigned int msiof3_clk_d_mux[] = {
+ MSIOF3_SCK_D_MARK,
+};
+static const unsigned int msiof3_sync_d_pins[] = {
+ /* SYNC */
+ RCAR_GP_PIN(1, 23),
+};
+static const unsigned int msiof3_sync_d_mux[] = {
+ MSIOF3_SYNC_D_MARK,
+};
+static const unsigned int msiof3_ss1_d_pins[] = {
+ /* SS1 */
+ RCAR_GP_PIN(1, 26),
+};
+static const unsigned int msiof3_ss1_d_mux[] = {
+ MSIOF3_SS1_D_MARK,
+};
+static const unsigned int msiof3_txd_d_pins[] = {
+ /* TXD */
+ RCAR_GP_PIN(1, 25),
+};
+static const unsigned int msiof3_txd_d_mux[] = {
+ MSIOF3_TXD_D_MARK,
+};
+static const unsigned int msiof3_rxd_d_pins[] = {
+ /* RXD */
+ RCAR_GP_PIN(1, 24),
+};
+static const unsigned int msiof3_rxd_d_mux[] = {
+ MSIOF3_RXD_D_MARK,
+};
+
+/* - SATA --------------------------------------------------------------------*/
+static const unsigned int sata0_devslp_a_pins[] = {
+ /* DEVSLP */
+ RCAR_GP_PIN(6, 16),
+};
+static const unsigned int sata0_devslp_a_mux[] = {
+ SATA_DEVSLP_A_MARK,
+};
+static const unsigned int sata0_devslp_b_pins[] = {
+ /* DEVSLP */
+ RCAR_GP_PIN(4, 6),
+};
+static const unsigned int sata0_devslp_b_mux[] = {
+ SATA_DEVSLP_B_MARK,
+};
+
/* - SCIF0 ------------------------------------------------------------------ */
static const unsigned int scif0_data_pins[] = {
/* RX, TX */
@@ -1845,6 +2690,228 @@ static const unsigned int scif5_clk_pins[] = {
static const unsigned int scif5_clk_mux[] = {
SCK5_MARK,
};
+/* - SDHI0 ------------------------------------------------------------------ */
+static const unsigned int sdhi0_data1_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(3, 2),
+};
+static const unsigned int sdhi0_data1_mux[] = {
+ SD0_DAT0_MARK,
+};
+static const unsigned int sdhi0_data4_pins[] = {
+ /* D[0:3] */
+ RCAR_GP_PIN(3, 2), RCAR_GP_PIN(3, 3),
+ RCAR_GP_PIN(3, 4), RCAR_GP_PIN(3, 5),
+};
+static const unsigned int sdhi0_data4_mux[] = {
+ SD0_DAT0_MARK, SD0_DAT1_MARK,
+ SD0_DAT2_MARK, SD0_DAT3_MARK,
+};
+static const unsigned int sdhi0_ctrl_pins[] = {
+ /* CLK, CMD */
+ RCAR_GP_PIN(3, 0), RCAR_GP_PIN(3, 1),
+};
+static const unsigned int sdhi0_ctrl_mux[] = {
+ SD0_CLK_MARK, SD0_CMD_MARK,
+};
+static const unsigned int sdhi0_cd_pins[] = {
+ /* CD */
+ RCAR_GP_PIN(3, 12),
+};
+static const unsigned int sdhi0_cd_mux[] = {
+ SD0_CD_MARK,
+};
+static const unsigned int sdhi0_wp_pins[] = {
+ /* WP */
+ RCAR_GP_PIN(3, 13),
+};
+static const unsigned int sdhi0_wp_mux[] = {
+ SD0_WP_MARK,
+};
+/* - SDHI1 ------------------------------------------------------------------ */
+static const unsigned int sdhi1_data1_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(3, 8),
+};
+static const unsigned int sdhi1_data1_mux[] = {
+ SD1_DAT0_MARK,
+};
+static const unsigned int sdhi1_data4_pins[] = {
+ /* D[0:3] */
+ RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9),
+ RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11),
+};
+static const unsigned int sdhi1_data4_mux[] = {
+ SD1_DAT0_MARK, SD1_DAT1_MARK,
+ SD1_DAT2_MARK, SD1_DAT3_MARK,
+};
+static const unsigned int sdhi1_ctrl_pins[] = {
+ /* CLK, CMD */
+ RCAR_GP_PIN(3, 6), RCAR_GP_PIN(3, 7),
+};
+static const unsigned int sdhi1_ctrl_mux[] = {
+ SD1_CLK_MARK, SD1_CMD_MARK,
+};
+static const unsigned int sdhi1_cd_pins[] = {
+ /* CD */
+ RCAR_GP_PIN(3, 14),
+};
+static const unsigned int sdhi1_cd_mux[] = {
+ SD1_CD_MARK,
+};
+static const unsigned int sdhi1_wp_pins[] = {
+ /* WP */
+ RCAR_GP_PIN(3, 15),
+};
+static const unsigned int sdhi1_wp_mux[] = {
+ SD1_WP_MARK,
+};
+/* - SDHI2 ------------------------------------------------------------------ */
+static const unsigned int sdhi2_data1_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(4, 2),
+};
+static const unsigned int sdhi2_data1_mux[] = {
+ SD2_DAT0_MARK,
+};
+static const unsigned int sdhi2_data4_pins[] = {
+ /* D[0:3] */
+ RCAR_GP_PIN(4, 2), RCAR_GP_PIN(4, 3),
+ RCAR_GP_PIN(4, 4), RCAR_GP_PIN(4, 5),
+};
+static const unsigned int sdhi2_data4_mux[] = {
+ SD2_DAT0_MARK, SD2_DAT1_MARK,
+ SD2_DAT2_MARK, SD2_DAT3_MARK,
+};
+static const unsigned int sdhi2_data8_pins[] = {
+ /* D[0:7] */
+ RCAR_GP_PIN(4, 2), RCAR_GP_PIN(4, 3),
+ RCAR_GP_PIN(4, 4), RCAR_GP_PIN(4, 5),
+ RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9),
+ RCAR_GP_PIN(3, 10), RCAR_GP_PIN(3, 11),
+};
+static const unsigned int sdhi2_data8_mux[] = {
+ SD2_DAT0_MARK, SD2_DAT1_MARK,
+ SD2_DAT2_MARK, SD2_DAT3_MARK,
+ SD2_DAT4_MARK, SD2_DAT5_MARK,
+ SD2_DAT6_MARK, SD2_DAT7_MARK,
+};
+static const unsigned int sdhi2_ctrl_pins[] = {
+ /* CLK, CMD */
+ RCAR_GP_PIN(4, 0), RCAR_GP_PIN(4, 1),
+};
+static const unsigned int sdhi2_ctrl_mux[] = {
+ SD2_CLK_MARK, SD2_CMD_MARK,
+};
+static const unsigned int sdhi2_cd_a_pins[] = {
+ /* CD */
+ RCAR_GP_PIN(4, 13),
+};
+static const unsigned int sdhi2_cd_a_mux[] = {
+ SD2_CD_A_MARK,
+};
+static const unsigned int sdhi2_cd_b_pins[] = {
+ /* CD */
+ RCAR_GP_PIN(5, 10),
+};
+static const unsigned int sdhi2_cd_b_mux[] = {
+ SD2_CD_B_MARK,
+};
+static const unsigned int sdhi2_wp_a_pins[] = {
+ /* WP */
+ RCAR_GP_PIN(4, 14),
+};
+static const unsigned int sdhi2_wp_a_mux[] = {
+ SD2_WP_A_MARK,
+};
+static const unsigned int sdhi2_wp_b_pins[] = {
+ /* WP */
+ RCAR_GP_PIN(5, 11),
+};
+static const unsigned int sdhi2_wp_b_mux[] = {
+ SD2_WP_B_MARK,
+};
+static const unsigned int sdhi2_ds_pins[] = {
+ /* DS */
+ RCAR_GP_PIN(4, 6),
+};
+static const unsigned int sdhi2_ds_mux[] = {
+ SD2_DS_MARK,
+};
+/* - SDHI3 ------------------------------------------------------------------ */
+static const unsigned int sdhi3_data1_pins[] = {
+ /* D0 */
+ RCAR_GP_PIN(4, 9),
+};
+static const unsigned int sdhi3_data1_mux[] = {
+ SD3_DAT0_MARK,
+};
+static const unsigned int sdhi3_data4_pins[] = {
+ /* D[0:3] */
+ RCAR_GP_PIN(4, 9), RCAR_GP_PIN(4, 10),
+ RCAR_GP_PIN(4, 11), RCAR_GP_PIN(4, 12),
+};
+static const unsigned int sdhi3_data4_mux[] = {
+ SD3_DAT0_MARK, SD3_DAT1_MARK,
+ SD3_DAT2_MARK, SD3_DAT3_MARK,
+};
+static const unsigned int sdhi3_data8_pins[] = {
+ /* D[0:7] */
+ RCAR_GP_PIN(4, 9), RCAR_GP_PIN(4, 10),
+ RCAR_GP_PIN(4, 11), RCAR_GP_PIN(4, 12),
+ RCAR_GP_PIN(4, 13), RCAR_GP_PIN(4, 14),
+ RCAR_GP_PIN(4, 15), RCAR_GP_PIN(4, 16),
+};
+static const unsigned int sdhi3_data8_mux[] = {
+ SD3_DAT0_MARK, SD3_DAT1_MARK,
+ SD3_DAT2_MARK, SD3_DAT3_MARK,
+ SD3_DAT4_MARK, SD3_DAT5_MARK,
+ SD3_DAT6_MARK, SD3_DAT7_MARK,
+};
+static const unsigned int sdhi3_ctrl_pins[] = {
+ /* CLK, CMD */
+ RCAR_GP_PIN(4, 7), RCAR_GP_PIN(4, 8),
+};
+static const unsigned int sdhi3_ctrl_mux[] = {
+ SD3_CLK_MARK, SD3_CMD_MARK,
+};
+static const unsigned int sdhi3_cd_pins[] = {
+ /* CD */
+ RCAR_GP_PIN(4, 15),
+};
+static const unsigned int sdhi3_cd_mux[] = {
+ SD3_CD_MARK,
+};
+static const unsigned int sdhi3_wp_pins[] = {
+ /* WP */
+ RCAR_GP_PIN(4, 16),
+};
+static const unsigned int sdhi3_wp_mux[] = {
+ SD3_WP_MARK,
+};
+static const unsigned int sdhi3_ds_pins[] = {
+ /* DS */
+ RCAR_GP_PIN(4, 17),
+};
+static const unsigned int sdhi3_ds_mux[] = {
+ SD3_DS_MARK,
+};
+
+/* - SCIF Clock ------------------------------------------------------------- */
+static const unsigned int scif_clk_a_pins[] = {
+ /* SCIF_CLK */
+ RCAR_GP_PIN(6, 23),
+};
+static const unsigned int scif_clk_a_mux[] = {
+ SCIF_CLK_A_MARK,
+};
+static const unsigned int scif_clk_b_pins[] = {
+ /* SCIF_CLK */
+ RCAR_GP_PIN(5, 9),
+};
+static const unsigned int scif_clk_b_mux[] = {
+ SCIF_CLK_B_MARK,
+};
/* - SSI -------------------------------------------------------------------- */
static const unsigned int ssi0_data_pins[] = {
@@ -2050,6 +3117,31 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(avb_avtp_capture_a),
SH_PFC_PIN_GROUP(avb_avtp_match_b),
SH_PFC_PIN_GROUP(avb_avtp_capture_b),
+ SH_PFC_PIN_GROUP(hscif0_data),
+ SH_PFC_PIN_GROUP(hscif0_clk),
+ SH_PFC_PIN_GROUP(hscif0_ctrl),
+ SH_PFC_PIN_GROUP(hscif1_data_a),
+ SH_PFC_PIN_GROUP(hscif1_clk_a),
+ SH_PFC_PIN_GROUP(hscif1_ctrl_a),
+ SH_PFC_PIN_GROUP(hscif1_data_b),
+ SH_PFC_PIN_GROUP(hscif1_clk_b),
+ SH_PFC_PIN_GROUP(hscif1_ctrl_b),
+ SH_PFC_PIN_GROUP(hscif2_data_a),
+ SH_PFC_PIN_GROUP(hscif2_clk_a),
+ SH_PFC_PIN_GROUP(hscif2_ctrl_a),
+ SH_PFC_PIN_GROUP(hscif2_data_b),
+ SH_PFC_PIN_GROUP(hscif2_clk_b),
+ SH_PFC_PIN_GROUP(hscif2_ctrl_b),
+ SH_PFC_PIN_GROUP(hscif3_data_a),
+ SH_PFC_PIN_GROUP(hscif3_clk),
+ SH_PFC_PIN_GROUP(hscif3_ctrl),
+ SH_PFC_PIN_GROUP(hscif3_data_b),
+ SH_PFC_PIN_GROUP(hscif3_data_c),
+ SH_PFC_PIN_GROUP(hscif3_data_d),
+ SH_PFC_PIN_GROUP(hscif4_data_a),
+ SH_PFC_PIN_GROUP(hscif4_clk),
+ SH_PFC_PIN_GROUP(hscif4_ctrl),
+ SH_PFC_PIN_GROUP(hscif4_data_b),
SH_PFC_PIN_GROUP(i2c1_a),
SH_PFC_PIN_GROUP(i2c1_b),
SH_PFC_PIN_GROUP(i2c2_a),
@@ -2057,6 +3149,101 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(i2c6_a),
SH_PFC_PIN_GROUP(i2c6_b),
SH_PFC_PIN_GROUP(i2c6_c),
+ SH_PFC_PIN_GROUP(msiof0_clk),
+ SH_PFC_PIN_GROUP(msiof0_sync),
+ SH_PFC_PIN_GROUP(msiof0_ss1),
+ SH_PFC_PIN_GROUP(msiof0_ss2),
+ SH_PFC_PIN_GROUP(msiof0_txd),
+ SH_PFC_PIN_GROUP(msiof0_rxd),
+ SH_PFC_PIN_GROUP(msiof1_clk_a),
+ SH_PFC_PIN_GROUP(msiof1_sync_a),
+ SH_PFC_PIN_GROUP(msiof1_ss1_a),
+ SH_PFC_PIN_GROUP(msiof1_ss2_a),
+ SH_PFC_PIN_GROUP(msiof1_txd_a),
+ SH_PFC_PIN_GROUP(msiof1_rxd_a),
+ SH_PFC_PIN_GROUP(msiof1_clk_b),
+ SH_PFC_PIN_GROUP(msiof1_sync_b),
+ SH_PFC_PIN_GROUP(msiof1_ss1_b),
+ SH_PFC_PIN_GROUP(msiof1_ss2_b),
+ SH_PFC_PIN_GROUP(msiof1_txd_b),
+ SH_PFC_PIN_GROUP(msiof1_rxd_b),
+ SH_PFC_PIN_GROUP(msiof1_clk_c),
+ SH_PFC_PIN_GROUP(msiof1_sync_c),
+ SH_PFC_PIN_GROUP(msiof1_ss1_c),
+ SH_PFC_PIN_GROUP(msiof1_ss2_c),
+ SH_PFC_PIN_GROUP(msiof1_txd_c),
+ SH_PFC_PIN_GROUP(msiof1_rxd_c),
+ SH_PFC_PIN_GROUP(msiof1_clk_d),
+ SH_PFC_PIN_GROUP(msiof1_sync_d),
+ SH_PFC_PIN_GROUP(msiof1_ss1_d),
+ SH_PFC_PIN_GROUP(msiof1_ss2_d),
+ SH_PFC_PIN_GROUP(msiof1_txd_d),
+ SH_PFC_PIN_GROUP(msiof1_rxd_d),
+ SH_PFC_PIN_GROUP(msiof1_clk_e),
+ SH_PFC_PIN_GROUP(msiof1_sync_e),
+ SH_PFC_PIN_GROUP(msiof1_ss1_e),
+ SH_PFC_PIN_GROUP(msiof1_ss2_e),
+ SH_PFC_PIN_GROUP(msiof1_txd_e),
+ SH_PFC_PIN_GROUP(msiof1_rxd_e),
+ SH_PFC_PIN_GROUP(msiof1_clk_f),
+ SH_PFC_PIN_GROUP(msiof1_sync_f),
+ SH_PFC_PIN_GROUP(msiof1_ss1_f),
+ SH_PFC_PIN_GROUP(msiof1_ss2_f),
+ SH_PFC_PIN_GROUP(msiof1_txd_f),
+ SH_PFC_PIN_GROUP(msiof1_rxd_f),
+ SH_PFC_PIN_GROUP(msiof1_clk_g),
+ SH_PFC_PIN_GROUP(msiof1_sync_g),
+ SH_PFC_PIN_GROUP(msiof1_ss1_g),
+ SH_PFC_PIN_GROUP(msiof1_ss2_g),
+ SH_PFC_PIN_GROUP(msiof1_txd_g),
+ SH_PFC_PIN_GROUP(msiof1_rxd_g),
+ SH_PFC_PIN_GROUP(msiof2_clk_a),
+ SH_PFC_PIN_GROUP(msiof2_sync_a),
+ SH_PFC_PIN_GROUP(msiof2_ss1_a),
+ SH_PFC_PIN_GROUP(msiof2_ss2_a),
+ SH_PFC_PIN_GROUP(msiof2_txd_a),
+ SH_PFC_PIN_GROUP(msiof2_rxd_a),
+ SH_PFC_PIN_GROUP(msiof2_clk_b),
+ SH_PFC_PIN_GROUP(msiof2_sync_b),
+ SH_PFC_PIN_GROUP(msiof2_ss1_b),
+ SH_PFC_PIN_GROUP(msiof2_ss2_b),
+ SH_PFC_PIN_GROUP(msiof2_txd_b),
+ SH_PFC_PIN_GROUP(msiof2_rxd_b),
+ SH_PFC_PIN_GROUP(msiof2_clk_c),
+ SH_PFC_PIN_GROUP(msiof2_sync_c),
+ SH_PFC_PIN_GROUP(msiof2_ss1_c),
+ SH_PFC_PIN_GROUP(msiof2_ss2_c),
+ SH_PFC_PIN_GROUP(msiof2_txd_c),
+ SH_PFC_PIN_GROUP(msiof2_rxd_c),
+ SH_PFC_PIN_GROUP(msiof2_clk_d),
+ SH_PFC_PIN_GROUP(msiof2_sync_d),
+ SH_PFC_PIN_GROUP(msiof2_ss1_d),
+ SH_PFC_PIN_GROUP(msiof2_ss2_d),
+ SH_PFC_PIN_GROUP(msiof2_txd_d),
+ SH_PFC_PIN_GROUP(msiof2_rxd_d),
+ SH_PFC_PIN_GROUP(msiof3_clk_a),
+ SH_PFC_PIN_GROUP(msiof3_sync_a),
+ SH_PFC_PIN_GROUP(msiof3_ss1_a),
+ SH_PFC_PIN_GROUP(msiof3_ss2_a),
+ SH_PFC_PIN_GROUP(msiof3_txd_a),
+ SH_PFC_PIN_GROUP(msiof3_rxd_a),
+ SH_PFC_PIN_GROUP(msiof3_clk_b),
+ SH_PFC_PIN_GROUP(msiof3_sync_b),
+ SH_PFC_PIN_GROUP(msiof3_ss1_b),
+ SH_PFC_PIN_GROUP(msiof3_ss2_b),
+ SH_PFC_PIN_GROUP(msiof3_txd_b),
+ SH_PFC_PIN_GROUP(msiof3_rxd_b),
+ SH_PFC_PIN_GROUP(msiof3_clk_c),
+ SH_PFC_PIN_GROUP(msiof3_sync_c),
+ SH_PFC_PIN_GROUP(msiof3_txd_c),
+ SH_PFC_PIN_GROUP(msiof3_rxd_c),
+ SH_PFC_PIN_GROUP(msiof3_clk_d),
+ SH_PFC_PIN_GROUP(msiof3_sync_d),
+ SH_PFC_PIN_GROUP(msiof3_ss1_d),
+ SH_PFC_PIN_GROUP(msiof3_txd_d),
+ SH_PFC_PIN_GROUP(msiof3_rxd_d),
+ SH_PFC_PIN_GROUP(sata0_devslp_a),
+ SH_PFC_PIN_GROUP(sata0_devslp_b),
SH_PFC_PIN_GROUP(scif0_data),
SH_PFC_PIN_GROUP(scif0_clk),
SH_PFC_PIN_GROUP(scif0_ctrl),
@@ -2082,6 +3269,34 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(scif4_ctrl_c),
SH_PFC_PIN_GROUP(scif5_data),
SH_PFC_PIN_GROUP(scif5_clk),
+ SH_PFC_PIN_GROUP(scif_clk_a),
+ SH_PFC_PIN_GROUP(scif_clk_b),
+ SH_PFC_PIN_GROUP(sdhi0_data1),
+ SH_PFC_PIN_GROUP(sdhi0_data4),
+ SH_PFC_PIN_GROUP(sdhi0_ctrl),
+ SH_PFC_PIN_GROUP(sdhi0_cd),
+ SH_PFC_PIN_GROUP(sdhi0_wp),
+ SH_PFC_PIN_GROUP(sdhi1_data1),
+ SH_PFC_PIN_GROUP(sdhi1_data4),
+ SH_PFC_PIN_GROUP(sdhi1_ctrl),
+ SH_PFC_PIN_GROUP(sdhi1_cd),
+ SH_PFC_PIN_GROUP(sdhi1_wp),
+ SH_PFC_PIN_GROUP(sdhi2_data1),
+ SH_PFC_PIN_GROUP(sdhi2_data4),
+ SH_PFC_PIN_GROUP(sdhi2_data8),
+ SH_PFC_PIN_GROUP(sdhi2_ctrl),
+ SH_PFC_PIN_GROUP(sdhi2_cd_a),
+ SH_PFC_PIN_GROUP(sdhi2_wp_a),
+ SH_PFC_PIN_GROUP(sdhi2_cd_b),
+ SH_PFC_PIN_GROUP(sdhi2_wp_b),
+ SH_PFC_PIN_GROUP(sdhi2_ds),
+ SH_PFC_PIN_GROUP(sdhi3_data1),
+ SH_PFC_PIN_GROUP(sdhi3_data4),
+ SH_PFC_PIN_GROUP(sdhi3_data8),
+ SH_PFC_PIN_GROUP(sdhi3_ctrl),
+ SH_PFC_PIN_GROUP(sdhi3_cd),
+ SH_PFC_PIN_GROUP(sdhi3_wp),
+ SH_PFC_PIN_GROUP(sdhi3_ds),
SH_PFC_PIN_GROUP(ssi0_data),
SH_PFC_PIN_GROUP(ssi01239_ctrl),
SH_PFC_PIN_GROUP(ssi1_data_a),
@@ -2141,6 +3356,46 @@ static const char * const avb_groups[] = {
"avb_avtp_capture_b",
};
+static const char * const hscif0_groups[] = {
+ "hscif0_data",
+ "hscif0_clk",
+ "hscif0_ctrl",
+};
+
+static const char * const hscif1_groups[] = {
+ "hscif1_data_a",
+ "hscif1_clk_a",
+ "hscif1_ctrl_a",
+ "hscif1_data_b",
+ "hscif1_clk_b",
+ "hscif1_ctrl_b",
+};
+
+static const char * const hscif2_groups[] = {
+ "hscif2_data_a",
+ "hscif2_clk_a",
+ "hscif2_ctrl_a",
+ "hscif2_data_b",
+ "hscif2_clk_b",
+ "hscif2_ctrl_b",
+};
+
+static const char * const hscif3_groups[] = {
+ "hscif3_data_a",
+ "hscif3_clk",
+ "hscif3_ctrl",
+ "hscif3_data_b",
+ "hscif3_data_c",
+ "hscif3_data_d",
+};
+
+static const char * const hscif4_groups[] = {
+ "hscif4_data_a",
+ "hscif4_clk",
+ "hscif4_ctrl",
+ "hscif4_data_b",
+};
+
static const char * const i2c1_groups[] = {
"i2c1_a",
"i2c1_b",
@@ -2157,6 +3412,116 @@ static const char * const i2c6_groups[] = {
"i2c6_c",
};
+static const char * const msiof0_groups[] = {
+ "msiof0_clk",
+ "msiof0_sync",
+ "msiof0_ss1",
+ "msiof0_ss2",
+ "msiof0_txd",
+ "msiof0_rxd",
+};
+
+static const char * const msiof1_groups[] = {
+ "msiof1_clk_a",
+ "msiof1_sync_a",
+ "msiof1_ss1_a",
+ "msiof1_ss2_a",
+ "msiof1_txd_a",
+ "msiof1_rxd_a",
+ "msiof1_clk_b",
+ "msiof1_sync_b",
+ "msiof1_ss1_b",
+ "msiof1_ss2_b",
+ "msiof1_txd_b",
+ "msiof1_rxd_b",
+ "msiof1_clk_c",
+ "msiof1_sync_c",
+ "msiof1_ss1_c",
+ "msiof1_ss2_c",
+ "msiof1_txd_c",
+ "msiof1_rxd_c",
+ "msiof1_clk_d",
+ "msiof1_sync_d",
+ "msiof1_ss1_d",
+ "msiof1_ss2_d",
+ "msiof1_txd_d",
+ "msiof1_rxd_d",
+ "msiof1_clk_e",
+ "msiof1_sync_e",
+ "msiof1_ss1_e",
+ "msiof1_ss2_e",
+ "msiof1_txd_e",
+ "msiof1_rxd_e",
+ "msiof1_clk_f",
+ "msiof1_sync_f",
+ "msiof1_ss1_f",
+ "msiof1_ss2_f",
+ "msiof1_txd_f",
+ "msiof1_rxd_f",
+ "msiof1_clk_g",
+ "msiof1_sync_g",
+ "msiof1_ss1_g",
+ "msiof1_ss2_g",
+ "msiof1_txd_g",
+ "msiof1_rxd_g",
+};
+
+static const char * const msiof2_groups[] = {
+ "msiof2_clk_a",
+ "msiof2_sync_a",
+ "msiof2_ss1_a",
+ "msiof2_ss2_a",
+ "msiof2_txd_a",
+ "msiof2_rxd_a",
+ "msiof2_clk_b",
+ "msiof2_sync_b",
+ "msiof2_ss1_b",
+ "msiof2_ss2_b",
+ "msiof2_txd_b",
+ "msiof2_rxd_b",
+ "msiof2_clk_c",
+ "msiof2_sync_c",
+ "msiof2_ss1_c",
+ "msiof2_ss2_c",
+ "msiof2_txd_c",
+ "msiof2_rxd_c",
+ "msiof2_clk_d",
+ "msiof2_sync_d",
+ "msiof2_ss1_d",
+ "msiof2_ss2_d",
+ "msiof2_txd_d",
+ "msiof2_rxd_d",
+};
+
+static const char * const msiof3_groups[] = {
+ "msiof3_clk_a",
+ "msiof3_sync_a",
+ "msiof3_ss1_a",
+ "msiof3_ss2_a",
+ "msiof3_txd_a",
+ "msiof3_rxd_a",
+ "msiof3_clk_b",
+ "msiof3_sync_b",
+ "msiof3_ss1_b",
+ "msiof3_ss2_b",
+ "msiof3_txd_b",
+ "msiof3_rxd_b",
+ "msiof3_clk_c",
+ "msiof3_sync_c",
+ "msiof3_txd_c",
+ "msiof3_rxd_c",
+ "msiof3_clk_d",
+ "msiof3_sync_d",
+ "msiof3_ss1_d",
+ "msiof3_txd_d",
+ "msiof3_rxd_d",
+};
+
+static const char * const sata0_groups[] = {
+ "sata0_devslp_a",
+ "sata0_devslp_b",
+};
+
static const char * const scif0_groups[] = {
"scif0_data",
"scif0_clk",
@@ -2200,6 +3565,49 @@ static const char * const scif5_groups[] = {
"scif5_clk",
};
+static const char * const scif_clk_groups[] = {
+ "scif_clk_a",
+ "scif_clk_b",
+};
+
+static const char * const sdhi0_groups[] = {
+ "sdhi0_data1",
+ "sdhi0_data4",
+ "sdhi0_ctrl",
+ "sdhi0_cd",
+ "sdhi0_wp",
+};
+
+static const char * const sdhi1_groups[] = {
+ "sdhi1_data1",
+ "sdhi1_data4",
+ "sdhi1_ctrl",
+ "sdhi1_cd",
+ "sdhi1_wp",
+};
+
+static const char * const sdhi2_groups[] = {
+ "sdhi2_data1",
+ "sdhi2_data4",
+ "sdhi2_data8",
+ "sdhi2_ctrl",
+ "sdhi2_cd_a",
+ "sdhi2_wp_a",
+ "sdhi2_cd_b",
+ "sdhi2_wp_b",
+ "sdhi2_ds",
+};
+
+static const char * const sdhi3_groups[] = {
+ "sdhi3_data1",
+ "sdhi3_data4",
+ "sdhi3_data8",
+ "sdhi3_ctrl",
+ "sdhi3_cd",
+ "sdhi3_wp",
+ "sdhi3_ds",
+};
+
static const char * const ssi_groups[] = {
"ssi0_data",
"ssi01239_ctrl",
@@ -2231,15 +3639,30 @@ static const char * const ssi_groups[] = {
static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(audio_clk),
SH_PFC_FUNCTION(avb),
+ SH_PFC_FUNCTION(hscif0),
+ SH_PFC_FUNCTION(hscif1),
+ SH_PFC_FUNCTION(hscif2),
+ SH_PFC_FUNCTION(hscif3),
+ SH_PFC_FUNCTION(hscif4),
SH_PFC_FUNCTION(i2c1),
SH_PFC_FUNCTION(i2c2),
SH_PFC_FUNCTION(i2c6),
+ SH_PFC_FUNCTION(msiof0),
+ SH_PFC_FUNCTION(msiof1),
+ SH_PFC_FUNCTION(msiof2),
+ SH_PFC_FUNCTION(msiof3),
+ SH_PFC_FUNCTION(sata0),
SH_PFC_FUNCTION(scif0),
SH_PFC_FUNCTION(scif1),
SH_PFC_FUNCTION(scif2),
SH_PFC_FUNCTION(scif3),
SH_PFC_FUNCTION(scif4),
SH_PFC_FUNCTION(scif5),
+ SH_PFC_FUNCTION(scif_clk),
+ SH_PFC_FUNCTION(sdhi0),
+ SH_PFC_FUNCTION(sdhi1),
+ SH_PFC_FUNCTION(sdhi2),
+ SH_PFC_FUNCTION(sdhi3),
SH_PFC_FUNCTION(ssi),
};
diff --git a/drivers/pinctrl/sh-pfc/pfc-sh73a0.c b/drivers/pinctrl/sh-pfc/pfc-sh73a0.c
index 6a69c8c5d943..d25e6f674d0a 100644
--- a/drivers/pinctrl/sh-pfc/pfc-sh73a0.c
+++ b/drivers/pinctrl/sh-pfc/pfc-sh73a0.c
@@ -2059,7 +2059,7 @@ static const unsigned int lcd2_data9_mux[] = {
LCD2D8_MARK,
};
static const unsigned int lcd2_data12_pins[] = {
- /* D[0:12] */
+ /* D[0:11] */
128, 129, 142, 143, 144, 145, 138, 139,
140, 141, 130, 131,
};
@@ -2198,6 +2198,420 @@ static const unsigned int mmc0_ctrl_1_pins[] = {
static const unsigned int mmc0_ctrl_1_mux[] = {
MMCCMD1_MARK, MMCCLK1_MARK,
};
+/* - MSIOF0 ----------------------------------------------------------------- */
+static const unsigned int msiof0_rsck_pins[] = {
+ /* RSCK */
+ 66,
+};
+static const unsigned int msiof0_rsck_mux[] = {
+ MSIOF0_RSCK_MARK,
+};
+static const unsigned int msiof0_tsck_pins[] = {
+ /* TSCK */
+ 64,
+};
+static const unsigned int msiof0_tsck_mux[] = {
+ MSIOF0_TSCK_MARK,
+};
+static const unsigned int msiof0_rsync_pins[] = {
+ /* RSYNC */
+ 67,
+};
+static const unsigned int msiof0_rsync_mux[] = {
+ MSIOF0_RSYNC_MARK,
+};
+static const unsigned int msiof0_tsync_pins[] = {
+ /* TSYNC */
+ 63,
+};
+static const unsigned int msiof0_tsync_mux[] = {
+ MSIOF0_TSYNC_MARK,
+};
+static const unsigned int msiof0_ss1_pins[] = {
+ /* SS1 */
+ 62,
+};
+static const unsigned int msiof0_ss1_mux[] = {
+ MSIOF0_SS1_MARK,
+};
+static const unsigned int msiof0_ss2_pins[] = {
+ /* SS2 */
+ 71,
+};
+static const unsigned int msiof0_ss2_mux[] = {
+ MSIOF0_SS2_MARK,
+};
+static const unsigned int msiof0_rxd_pins[] = {
+ /* RXD */
+ 70,
+};
+static const unsigned int msiof0_rxd_mux[] = {
+ MSIOF0_RXD_MARK,
+};
+static const unsigned int msiof0_txd_pins[] = {
+ /* TXD */
+ 65,
+};
+static const unsigned int msiof0_txd_mux[] = {
+ MSIOF0_TXD_MARK,
+};
+static const unsigned int msiof0_mck0_pins[] = {
+ /* MSCK0 */
+ 68,
+};
+static const unsigned int msiof0_mck0_mux[] = {
+ MSIOF0_MCK0_MARK,
+};
+
+static const unsigned int msiof0_mck1_pins[] = {
+ /* MSCK1 */
+ 69,
+};
+static const unsigned int msiof0_mck1_mux[] = {
+ MSIOF0_MCK1_MARK,
+};
+
+static const unsigned int msiof0l_rsck_pins[] = {
+ /* RSCK */
+ 214,
+};
+static const unsigned int msiof0l_rsck_mux[] = {
+ MSIOF0L_RSCK_MARK,
+};
+static const unsigned int msiof0l_tsck_pins[] = {
+ /* TSCK */
+ 219,
+};
+static const unsigned int msiof0l_tsck_mux[] = {
+ MSIOF0L_TSCK_MARK,
+};
+static const unsigned int msiof0l_rsync_pins[] = {
+ /* RSYNC */
+ 215,
+};
+static const unsigned int msiof0l_rsync_mux[] = {
+ MSIOF0L_RSYNC_MARK,
+};
+static const unsigned int msiof0l_tsync_pins[] = {
+ /* TSYNC */
+ 217,
+};
+static const unsigned int msiof0l_tsync_mux[] = {
+ MSIOF0L_TSYNC_MARK,
+};
+static const unsigned int msiof0l_ss1_a_pins[] = {
+ /* SS1 */
+ 207,
+};
+static const unsigned int msiof0l_ss1_a_mux[] = {
+ PORT207_MSIOF0L_SS1_MARK,
+};
+static const unsigned int msiof0l_ss1_b_pins[] = {
+ /* SS1 */
+ 210,
+};
+static const unsigned int msiof0l_ss1_b_mux[] = {
+ PORT210_MSIOF0L_SS1_MARK,
+};
+static const unsigned int msiof0l_ss2_a_pins[] = {
+ /* SS2 */
+ 208,
+};
+static const unsigned int msiof0l_ss2_a_mux[] = {
+ PORT208_MSIOF0L_SS2_MARK,
+};
+static const unsigned int msiof0l_ss2_b_pins[] = {
+ /* SS2 */
+ 211,
+};
+static const unsigned int msiof0l_ss2_b_mux[] = {
+ PORT211_MSIOF0L_SS2_MARK,
+};
+static const unsigned int msiof0l_rxd_pins[] = {
+ /* RXD */
+ 221,
+};
+static const unsigned int msiof0l_rxd_mux[] = {
+ MSIOF0L_RXD_MARK,
+};
+static const unsigned int msiof0l_txd_pins[] = {
+ /* TXD */
+ 222,
+};
+static const unsigned int msiof0l_txd_mux[] = {
+ MSIOF0L_TXD_MARK,
+};
+static const unsigned int msiof0l_mck0_pins[] = {
+ /* MSCK0 */
+ 212,
+};
+static const unsigned int msiof0l_mck0_mux[] = {
+ MSIOF0L_MCK0_MARK,
+};
+static const unsigned int msiof0l_mck1_pins[] = {
+ /* MSCK1 */
+ 213,
+};
+static const unsigned int msiof0l_mck1_mux[] = {
+ MSIOF0L_MCK1_MARK,
+};
+/* - MSIOF1 ----------------------------------------------------------------- */
+static const unsigned int msiof1_rsck_pins[] = {
+ /* RSCK */
+ 234,
+};
+static const unsigned int msiof1_rsck_mux[] = {
+ MSIOF1_RSCK_MARK,
+};
+static const unsigned int msiof1_tsck_pins[] = {
+ /* TSCK */
+ 232,
+};
+static const unsigned int msiof1_tsck_mux[] = {
+ MSIOF1_TSCK_MARK,
+};
+static const unsigned int msiof1_rsync_pins[] = {
+ /* RSYNC */
+ 235,
+};
+static const unsigned int msiof1_rsync_mux[] = {
+ MSIOF1_RSYNC_MARK,
+};
+static const unsigned int msiof1_tsync_pins[] = {
+ /* TSYNC */
+ 231,
+};
+static const unsigned int msiof1_tsync_mux[] = {
+ MSIOF1_TSYNC_MARK,
+};
+static const unsigned int msiof1_ss1_pins[] = {
+ /* SS1 */
+ 238,
+};
+static const unsigned int msiof1_ss1_mux[] = {
+ MSIOF1_SS1_MARK,
+};
+static const unsigned int msiof1_ss2_pins[] = {
+ /* SS2 */
+ 239,
+};
+static const unsigned int msiof1_ss2_mux[] = {
+ MSIOF1_SS2_MARK,
+};
+static const unsigned int msiof1_rxd_pins[] = {
+ /* RXD */
+ 233,
+};
+static const unsigned int msiof1_rxd_mux[] = {
+ MSIOF1_RXD_MARK,
+};
+static const unsigned int msiof1_txd_pins[] = {
+ /* TXD */
+ 230,
+};
+static const unsigned int msiof1_txd_mux[] = {
+ MSIOF1_TXD_MARK,
+};
+static const unsigned int msiof1_mck0_pins[] = {
+ /* MSCK0 */
+ 236,
+};
+static const unsigned int msiof1_mck0_mux[] = {
+ MSIOF1_MCK0_MARK,
+};
+static const unsigned int msiof1_mck1_pins[] = {
+ /* MSCK1 */
+ 237,
+};
+static const unsigned int msiof1_mck1_mux[] = {
+ MSIOF1_MCK1_MARK,
+};
+/* - MSIOF2 ----------------------------------------------------------------- */
+static const unsigned int msiof2_rsck_pins[] = {
+ /* RSCK */
+ 151,
+};
+static const unsigned int msiof2_rsck_mux[] = {
+ MSIOF2_RSCK_MARK,
+};
+static const unsigned int msiof2_tsck_pins[] = {
+ /* TSCK */
+ 135,
+};
+static const unsigned int msiof2_tsck_mux[] = {
+ MSIOF2_TSCK_MARK,
+};
+static const unsigned int msiof2_rsync_pins[] = {
+ /* RSYNC */
+ 152,
+};
+static const unsigned int msiof2_rsync_mux[] = {
+ MSIOF2_RSYNC_MARK,
+};
+static const unsigned int msiof2_tsync_pins[] = {
+ /* TSYNC */
+ 133,
+};
+static const unsigned int msiof2_tsync_mux[] = {
+ MSIOF2_TSYNC_MARK,
+};
+static const unsigned int msiof2_ss1_a_pins[] = {
+ /* SS1 */
+ 131,
+};
+static const unsigned int msiof2_ss1_a_mux[] = {
+ PORT131_MSIOF2_SS1_MARK,
+};
+static const unsigned int msiof2_ss1_b_pins[] = {
+ /* SS1 */
+ 153,
+};
+static const unsigned int msiof2_ss1_b_mux[] = {
+ PORT153_MSIOF2_SS1_MARK,
+};
+static const unsigned int msiof2_ss2_a_pins[] = {
+ /* SS2 */
+ 132,
+};
+static const unsigned int msiof2_ss2_a_mux[] = {
+ PORT132_MSIOF2_SS2_MARK,
+};
+static const unsigned int msiof2_ss2_b_pins[] = {
+ /* SS2 */
+ 156,
+};
+static const unsigned int msiof2_ss2_b_mux[] = {
+ PORT156_MSIOF2_SS2_MARK,
+};
+static const unsigned int msiof2_rxd_a_pins[] = {
+ /* RXD */
+ 130,
+};
+static const unsigned int msiof2_rxd_a_mux[] = {
+ PORT130_MSIOF2_RXD_MARK,
+};
+static const unsigned int msiof2_rxd_b_pins[] = {
+ /* RXD */
+ 157,
+};
+static const unsigned int msiof2_rxd_b_mux[] = {
+ PORT157_MSIOF2_RXD_MARK,
+};
+static const unsigned int msiof2_txd_pins[] = {
+ /* TXD */
+ 134,
+};
+static const unsigned int msiof2_txd_mux[] = {
+ MSIOF2_TXD_MARK,
+};
+static const unsigned int msiof2_mck0_pins[] = {
+ /* MSCK0 */
+ 154,
+};
+static const unsigned int msiof2_mck0_mux[] = {
+ MSIOF2_MCK0_MARK,
+};
+static const unsigned int msiof2_mck1_pins[] = {
+ /* MSCK1 */
+ 155,
+};
+static const unsigned int msiof2_mck1_mux[] = {
+ MSIOF2_MCK1_MARK,
+};
+
+static const unsigned int msiof2r_tsck_pins[] = {
+ /* TSCK */
+ 248,
+};
+static const unsigned int msiof2r_tsck_mux[] = {
+ MSIOF2R_TSCK_MARK,
+};
+static const unsigned int msiof2r_tsync_pins[] = {
+ /* TSYNC */
+ 249,
+};
+static const unsigned int msiof2r_tsync_mux[] = {
+ MSIOF2R_TSYNC_MARK,
+};
+static const unsigned int msiof2r_rxd_pins[] = {
+ /* RXD */
+ 244,
+};
+static const unsigned int msiof2r_rxd_mux[] = {
+ MSIOF2R_RXD_MARK,
+};
+static const unsigned int msiof2r_txd_pins[] = {
+ /* TXD */
+ 245,
+};
+static const unsigned int msiof2r_txd_mux[] = {
+ MSIOF2R_TXD_MARK,
+};
+/* - MSIOF3 (Pin function name of MSIOF3 is named BBIF1) -------------------- */
+static const unsigned int msiof3_rsck_pins[] = {
+ /* RSCK */
+ 115,
+};
+static const unsigned int msiof3_rsck_mux[] = {
+ BBIF1_RSCK_MARK,
+};
+static const unsigned int msiof3_tsck_pins[] = {
+ /* TSCK */
+ 112,
+};
+static const unsigned int msiof3_tsck_mux[] = {
+ BBIF1_TSCK_MARK,
+};
+static const unsigned int msiof3_rsync_pins[] = {
+ /* RSYNC */
+ 116,
+};
+static const unsigned int msiof3_rsync_mux[] = {
+ BBIF1_RSYNC_MARK,
+};
+static const unsigned int msiof3_tsync_pins[] = {
+ /* TSYNC */
+ 113,
+};
+static const unsigned int msiof3_tsync_mux[] = {
+ BBIF1_TSYNC_MARK,
+};
+static const unsigned int msiof3_ss1_pins[] = {
+ /* SS1 */
+ 117,
+};
+static const unsigned int msiof3_ss1_mux[] = {
+ BBIF1_SS1_MARK,
+};
+static const unsigned int msiof3_ss2_pins[] = {
+ /* SS2 */
+ 109,
+};
+static const unsigned int msiof3_ss2_mux[] = {
+ BBIF1_SS2_MARK,
+};
+static const unsigned int msiof3_rxd_pins[] = {
+ /* RXD */
+ 111,
+};
+static const unsigned int msiof3_rxd_mux[] = {
+ BBIF1_RXD_MARK,
+};
+static const unsigned int msiof3_txd_pins[] = {
+ /* TXD */
+ 114,
+};
+static const unsigned int msiof3_txd_mux[] = {
+ BBIF1_TXD_MARK,
+};
+static const unsigned int msiof3_flow_pins[] = {
+ /* FLOW */
+ 117,
+};
+static const unsigned int msiof3_flow_mux[] = {
+ BBIF1_FLOW_MARK,
+};
+
/* - SCIFA0 ----------------------------------------------------------------- */
static const unsigned int scifa0_data_pins[] = {
/* RXD, TXD */
@@ -2782,6 +3196,64 @@ static const struct sh_pfc_pin_group pinmux_groups[] = {
SH_PFC_PIN_GROUP(mmc0_data4_1),
SH_PFC_PIN_GROUP(mmc0_data8_1),
SH_PFC_PIN_GROUP(mmc0_ctrl_1),
+ SH_PFC_PIN_GROUP(msiof0_rsck),
+ SH_PFC_PIN_GROUP(msiof0_tsck),
+ SH_PFC_PIN_GROUP(msiof0_rsync),
+ SH_PFC_PIN_GROUP(msiof0_tsync),
+ SH_PFC_PIN_GROUP(msiof0_ss1),
+ SH_PFC_PIN_GROUP(msiof0_ss2),
+ SH_PFC_PIN_GROUP(msiof0_rxd),
+ SH_PFC_PIN_GROUP(msiof0_txd),
+ SH_PFC_PIN_GROUP(msiof0_mck0),
+ SH_PFC_PIN_GROUP(msiof0_mck1),
+ SH_PFC_PIN_GROUP(msiof0l_rsck),
+ SH_PFC_PIN_GROUP(msiof0l_tsck),
+ SH_PFC_PIN_GROUP(msiof0l_rsync),
+ SH_PFC_PIN_GROUP(msiof0l_tsync),
+ SH_PFC_PIN_GROUP(msiof0l_ss1_a),
+ SH_PFC_PIN_GROUP(msiof0l_ss1_b),
+ SH_PFC_PIN_GROUP(msiof0l_ss2_a),
+ SH_PFC_PIN_GROUP(msiof0l_ss2_b),
+ SH_PFC_PIN_GROUP(msiof0l_rxd),
+ SH_PFC_PIN_GROUP(msiof0l_txd),
+ SH_PFC_PIN_GROUP(msiof0l_mck0),
+ SH_PFC_PIN_GROUP(msiof0l_mck1),
+ SH_PFC_PIN_GROUP(msiof1_rsck),
+ SH_PFC_PIN_GROUP(msiof1_tsck),
+ SH_PFC_PIN_GROUP(msiof1_rsync),
+ SH_PFC_PIN_GROUP(msiof1_tsync),
+ SH_PFC_PIN_GROUP(msiof1_ss1),
+ SH_PFC_PIN_GROUP(msiof1_ss2),
+ SH_PFC_PIN_GROUP(msiof1_rxd),
+ SH_PFC_PIN_GROUP(msiof1_txd),
+ SH_PFC_PIN_GROUP(msiof1_mck0),
+ SH_PFC_PIN_GROUP(msiof1_mck1),
+ SH_PFC_PIN_GROUP(msiof2_rsck),
+ SH_PFC_PIN_GROUP(msiof2_tsck),
+ SH_PFC_PIN_GROUP(msiof2_rsync),
+ SH_PFC_PIN_GROUP(msiof2_tsync),
+ SH_PFC_PIN_GROUP(msiof2_ss1_a),
+ SH_PFC_PIN_GROUP(msiof2_ss1_b),
+ SH_PFC_PIN_GROUP(msiof2_ss2_a),
+ SH_PFC_PIN_GROUP(msiof2_ss2_b),
+ SH_PFC_PIN_GROUP(msiof2_rxd_a),
+ SH_PFC_PIN_GROUP(msiof2_rxd_b),
+ SH_PFC_PIN_GROUP(msiof2_txd),
+ SH_PFC_PIN_GROUP(msiof2_mck0),
+ SH_PFC_PIN_GROUP(msiof2_mck1),
+ SH_PFC_PIN_GROUP(msiof2r_tsck),
+ SH_PFC_PIN_GROUP(msiof2r_tsync),
+ SH_PFC_PIN_GROUP(msiof2r_rxd),
+ SH_PFC_PIN_GROUP(msiof2r_txd),
+ SH_PFC_PIN_GROUP(msiof3_rsck),
+ SH_PFC_PIN_GROUP(msiof3_tsck),
+ SH_PFC_PIN_GROUP(msiof3_rsync),
+ SH_PFC_PIN_GROUP(msiof3_tsync),
+ SH_PFC_PIN_GROUP(msiof3_ss1),
+ SH_PFC_PIN_GROUP(msiof3_ss2),
+ SH_PFC_PIN_GROUP(msiof3_rxd),
+ SH_PFC_PIN_GROUP(msiof3_txd),
+ SH_PFC_PIN_GROUP(msiof3_flow),
SH_PFC_PIN_GROUP(scifa0_data),
SH_PFC_PIN_GROUP(scifa0_clk),
SH_PFC_PIN_GROUP(scifa0_ctrl),
@@ -2982,6 +3454,76 @@ static const char * const mmc0_groups[] = {
"mmc0_ctrl_1",
};
+static const char * const msiof0_groups[] = {
+ "msiof0_rsck",
+ "msiof0_tsck",
+ "msiof0_rsync",
+ "msiof0_tsync",
+ "msiof0_ss1",
+ "msiof0_ss2",
+ "msiof0_rxd",
+ "msiof0_txd",
+ "msiof0_mck0",
+ "msiof0_mck1",
+ "msiof0l_rsck",
+ "msiof0l_tsck",
+ "msiof0l_rsync",
+ "msiof0l_tsync",
+ "msiof0l_ss1_a",
+ "msiof0l_ss1_b",
+ "msiof0l_ss2_a",
+ "msiof0l_ss2_b",
+ "msiof0l_rxd",
+ "msiof0l_txd",
+ "msiof0l_mck0",
+ "msiof0l_mck1",
+};
+
+static const char * const msiof1_groups[] = {
+ "msiof1_rsck",
+ "msiof1_tsck",
+ "msiof1_rsync",
+ "msiof1_tsync",
+ "msiof1_ss1",
+ "msiof1_ss2",
+ "msiof1_rxd",
+ "msiof1_txd",
+ "msiof1_mck0",
+ "msiof1_mck1",
+};
+
+static const char * const msiof2_groups[] = {
+ "msiof2_rsck",
+ "msiof2_tsck",
+ "msiof2_rsync",
+ "msiof2_tsync",
+ "msiof2_ss1_a",
+ "msiof2_ss1_b",
+ "msiof2_ss2_a",
+ "msiof2_ss2_b",
+ "msiof2_rxd_a",
+ "msiof2_rxd_b",
+ "msiof2_txd",
+ "msiof2_mck0",
+ "msiof2_mck1",
+ "msiof2r_tsck",
+ "msiof2r_tsync",
+ "msiof2r_rxd",
+ "msiof2r_txd",
+};
+
+static const char * const msiof3_groups[] = {
+ "msiof3_rsck",
+ "msiof3_tsck",
+ "msiof3_rsync",
+ "msiof3_tsync",
+ "msiof3_ss1",
+ "msiof3_ss2",
+ "msiof3_rxd",
+ "msiof3_txd",
+ "msiof3_flow",
+};
+
static const char * const scifa0_groups[] = {
"scifa0_data",
"scifa0_clk",
@@ -3116,6 +3658,10 @@ static const struct sh_pfc_function pinmux_functions[] = {
SH_PFC_FUNCTION(lcd),
SH_PFC_FUNCTION(lcd2),
SH_PFC_FUNCTION(mmc0),
+ SH_PFC_FUNCTION(msiof0),
+ SH_PFC_FUNCTION(msiof1),
+ SH_PFC_FUNCTION(msiof2),
+ SH_PFC_FUNCTION(msiof3),
SH_PFC_FUNCTION(scifa0),
SH_PFC_FUNCTION(scifa1),
SH_PFC_FUNCTION(scifa2),
diff --git a/drivers/pinctrl/sh-pfc/pfc-sh7734.c b/drivers/pinctrl/sh-pfc/pfc-sh7734.c
index e7deb51de7dc..b0b328b3130b 100644
--- a/drivers/pinctrl/sh-pfc/pfc-sh7734.c
+++ b/drivers/pinctrl/sh-pfc/pfc-sh7734.c
@@ -14,14 +14,6 @@
#include "sh_pfc.h"
-#define PORT_GP_12(bank, fn, sfx) \
- PORT_GP_1(bank, 0, fn, sfx), PORT_GP_1(bank, 1, fn, sfx), \
- PORT_GP_1(bank, 2, fn, sfx), PORT_GP_1(bank, 3, fn, sfx), \
- PORT_GP_1(bank, 4, fn, sfx), PORT_GP_1(bank, 5, fn, sfx), \
- PORT_GP_1(bank, 6, fn, sfx), PORT_GP_1(bank, 7, fn, sfx), \
- PORT_GP_1(bank, 8, fn, sfx), PORT_GP_1(bank, 9, fn, sfx), \
- PORT_GP_1(bank, 10, fn, sfx), PORT_GP_1(bank, 11, fn, sfx)
-
#define CPU_ALL_PORT(fn, sfx) \
PORT_GP_32(0, fn, sfx), \
PORT_GP_32(1, fn, sfx), \
@@ -31,11 +23,11 @@
PORT_GP_12(5, fn, sfx)
#undef _GP_DATA
-#define _GP_DATA(bank, pin, name, sfx) \
+#define _GP_DATA(bank, pin, name, sfx, cfg) \
PINMUX_DATA(name##_DATA, name##_FN, name##_IN, name##_OUT)
-#define _GP_INOUTSEL(bank, pin, name, sfx) name##_IN, name##_OUT
-#define _GP_INDT(bank, pin, name, sfx) name##_DATA
+#define _GP_INOUTSEL(bank, pin, name, sfx, cfg) name##_IN, name##_OUT
+#define _GP_INDT(bank, pin, name, sfx, cfg) name##_DATA
#define GP_INOUTSEL(bank) PORT_GP_32_REV(bank, _GP_INOUTSEL, unused)
#define GP_INDT(bank) PORT_GP_32_REV(bank, _GP_INDT, unused)
@@ -585,15 +577,18 @@ enum {
static const u16 pinmux_data[] = {
PINMUX_DATA_GP_ALL(), /* PINMUX_DATA(GP_M_N_DATA, GP_M_N_FN...), */
- PINMUX_DATA(CLKOUT_MARK, FN_CLKOUT),
- PINMUX_DATA(BS_MARK, FN_BS), PINMUX_DATA(CS0_MARK, FN_CS0),
- PINMUX_DATA(EX_CS0_MARK, FN_EX_CS0),
- PINMUX_DATA(RD_MARK, FN_RD), PINMUX_DATA(WE0_MARK, FN_WE0),
- PINMUX_DATA(WE1_MARK, FN_WE1),
- PINMUX_DATA(SCL0_MARK, FN_SCL0), PINMUX_DATA(PENC0_MARK, FN_PENC0),
- PINMUX_DATA(USB_OVC0_MARK, FN_USB_OVC0),
- PINMUX_DATA(IRQ2_B_MARK, FN_IRQ2_B),
- PINMUX_DATA(IRQ3_B_MARK, FN_IRQ3_B),
+ PINMUX_SINGLE(CLKOUT),
+ PINMUX_SINGLE(BS),
+ PINMUX_SINGLE(CS0),
+ PINMUX_SINGLE(EX_CS0),
+ PINMUX_SINGLE(RD),
+ PINMUX_SINGLE(WE0),
+ PINMUX_SINGLE(WE1),
+ PINMUX_SINGLE(SCL0),
+ PINMUX_SINGLE(PENC0),
+ PINMUX_SINGLE(USB_OVC0),
+ PINMUX_SINGLE(IRQ2_B),
+ PINMUX_SINGLE(IRQ3_B),
/* IPSR0 */
PINMUX_IPSR_DATA(IP0_1_0, A0),
diff --git a/drivers/pinctrl/sh-pfc/pinctrl.c b/drivers/pinctrl/sh-pfc/pinctrl.c
index 863c3e30ce05..87b0a599afaf 100644
--- a/drivers/pinctrl/sh-pfc/pinctrl.c
+++ b/drivers/pinctrl/sh-pfc/pinctrl.c
@@ -273,8 +273,10 @@ static int sh_pfc_dt_node_to_map(struct pinctrl_dev *pctldev,
for_each_child_of_node(np, child) {
ret = sh_pfc_dt_subnode_to_map(pctldev, child, map, num_maps,
&index);
- if (ret < 0)
+ if (ret < 0) {
+ of_node_put(child);
goto done;
+ }
}
/* If no mapping has been found in child nodes try the config node. */
diff --git a/drivers/pinctrl/sh-pfc/sh_pfc.h b/drivers/pinctrl/sh-pfc/sh_pfc.h
index 7b373d43d981..2123ab49d6a5 100644
--- a/drivers/pinctrl/sh-pfc/sh_pfc.h
+++ b/drivers/pinctrl/sh-pfc/sh_pfc.h
@@ -199,28 +199,82 @@ struct sh_pfc_soc_info {
PINMUX_DATA(fn##_MARK, FN_##ms, FN_##ipsr, FN_##fn)
/*
+ * Describe a pinmux configuration for a single-function pin with GPIO
+ * capability.
+ * - fn: Function name
+ */
+#define PINMUX_SINGLE(fn) \
+ PINMUX_DATA(fn##_MARK, FN_##fn)
+
+/*
* GP port style (32 ports banks)
*/
#define PORT_GP_CFG_1(bank, pin, fn, sfx, cfg) fn(bank, pin, GP_##bank##_##pin, sfx, cfg)
#define PORT_GP_1(bank, pin, fn, sfx) PORT_GP_CFG_1(bank, pin, fn, sfx, 0)
-#define PORT_GP_CFG_32(bank, fn, sfx, cfg) \
+#define PORT_GP_CFG_4(bank, fn, sfx, cfg) \
PORT_GP_CFG_1(bank, 0, fn, sfx, cfg), PORT_GP_CFG_1(bank, 1, fn, sfx, cfg), \
- PORT_GP_CFG_1(bank, 2, fn, sfx, cfg), PORT_GP_CFG_1(bank, 3, fn, sfx, cfg), \
+ PORT_GP_CFG_1(bank, 2, fn, sfx, cfg), PORT_GP_CFG_1(bank, 3, fn, sfx, cfg)
+#define PORT_GP_4(bank, fn, sfx) PORT_GP_CFG_4(bank, fn, sfx, 0)
+
+#define PORT_GP_CFG_8(bank, fn, sfx, cfg) \
+ PORT_GP_CFG_4(bank, fn, sfx, cfg), \
PORT_GP_CFG_1(bank, 4, fn, sfx, cfg), PORT_GP_CFG_1(bank, 5, fn, sfx, cfg), \
- PORT_GP_CFG_1(bank, 6, fn, sfx, cfg), PORT_GP_CFG_1(bank, 7, fn, sfx, cfg), \
+ PORT_GP_CFG_1(bank, 6, fn, sfx, cfg), PORT_GP_CFG_1(bank, 7, fn, sfx, cfg)
+#define PORT_GP_8(bank, fn, sfx) PORT_GP_CFG_8(bank, fn, sfx, 0)
+
+#define PORT_GP_CFG_9(bank, fn, sfx, cfg) \
+ PORT_GP_CFG_8(bank, fn, sfx, cfg), \
+ PORT_GP_CFG_1(bank, 8, fn, sfx, cfg)
+#define PORT_GP_9(bank, fn, sfx) PORT_GP_CFG_9(bank, fn, sfx, 0)
+
+#define PORT_GP_CFG_12(bank, fn, sfx, cfg) \
+ PORT_GP_CFG_8(bank, fn, sfx, cfg), \
PORT_GP_CFG_1(bank, 8, fn, sfx, cfg), PORT_GP_CFG_1(bank, 9, fn, sfx, cfg), \
- PORT_GP_CFG_1(bank, 10, fn, sfx, cfg), PORT_GP_CFG_1(bank, 11, fn, sfx, cfg), \
- PORT_GP_CFG_1(bank, 12, fn, sfx, cfg), PORT_GP_CFG_1(bank, 13, fn, sfx, cfg), \
- PORT_GP_CFG_1(bank, 14, fn, sfx, cfg), PORT_GP_CFG_1(bank, 15, fn, sfx, cfg), \
- PORT_GP_CFG_1(bank, 16, fn, sfx, cfg), PORT_GP_CFG_1(bank, 17, fn, sfx, cfg), \
+ PORT_GP_CFG_1(bank, 10, fn, sfx, cfg), PORT_GP_CFG_1(bank, 11, fn, sfx, cfg)
+#define PORT_GP_12(bank, fn, sfx) PORT_GP_CFG_12(bank, fn, sfx, 0)
+
+#define PORT_GP_CFG_14(bank, fn, sfx, cfg) \
+ PORT_GP_CFG_12(bank, fn, sfx, cfg), \
+ PORT_GP_CFG_1(bank, 12, fn, sfx, cfg), PORT_GP_CFG_1(bank, 13, fn, sfx, cfg)
+#define PORT_GP_14(bank, fn, sfx) PORT_GP_CFG_14(bank, fn, sfx, 0)
+
+#define PORT_GP_CFG_15(bank, fn, sfx, cfg) \
+ PORT_GP_CFG_14(bank, fn, sfx, cfg), \
+ PORT_GP_CFG_1(bank, 14, fn, sfx, cfg)
+#define PORT_GP_15(bank, fn, sfx) PORT_GP_CFG_15(bank, fn, sfx, 0)
+
+#define PORT_GP_CFG_16(bank, fn, sfx, cfg) \
+ PORT_GP_CFG_14(bank, fn, sfx, cfg), \
+ PORT_GP_CFG_1(bank, 14, fn, sfx, cfg), PORT_GP_CFG_1(bank, 15, fn, sfx, cfg)
+#define PORT_GP_16(bank, fn, sfx) PORT_GP_CFG_16(bank, fn, sfx, 0)
+
+#define PORT_GP_CFG_18(bank, fn, sfx, cfg) \
+ PORT_GP_CFG_16(bank, fn, sfx, cfg), \
+ PORT_GP_CFG_1(bank, 16, fn, sfx, cfg), PORT_GP_CFG_1(bank, 17, fn, sfx, cfg)
+#define PORT_GP_18(bank, fn, sfx) PORT_GP_CFG_18(bank, fn, sfx, 0)
+
+#define PORT_GP_CFG_26(bank, fn, sfx, cfg) \
+ PORT_GP_CFG_18(bank, fn, sfx, cfg), \
PORT_GP_CFG_1(bank, 18, fn, sfx, cfg), PORT_GP_CFG_1(bank, 19, fn, sfx, cfg), \
PORT_GP_CFG_1(bank, 20, fn, sfx, cfg), PORT_GP_CFG_1(bank, 21, fn, sfx, cfg), \
PORT_GP_CFG_1(bank, 22, fn, sfx, cfg), PORT_GP_CFG_1(bank, 23, fn, sfx, cfg), \
- PORT_GP_CFG_1(bank, 24, fn, sfx, cfg), PORT_GP_CFG_1(bank, 25, fn, sfx, cfg), \
- PORT_GP_CFG_1(bank, 26, fn, sfx, cfg), PORT_GP_CFG_1(bank, 27, fn, sfx, cfg), \
- PORT_GP_CFG_1(bank, 28, fn, sfx, cfg), PORT_GP_CFG_1(bank, 29, fn, sfx, cfg), \
+ PORT_GP_CFG_1(bank, 24, fn, sfx, cfg), PORT_GP_CFG_1(bank, 25, fn, sfx, cfg)
+#define PORT_GP_26(bank, fn, sfx) PORT_GP_CFG_26(bank, fn, sfx, 0)
+
+#define PORT_GP_CFG_28(bank, fn, sfx, cfg) \
+ PORT_GP_CFG_26(bank, fn, sfx, cfg), \
+ PORT_GP_CFG_1(bank, 26, fn, sfx, cfg), PORT_GP_CFG_1(bank, 27, fn, sfx, cfg)
+#define PORT_GP_28(bank, fn, sfx) PORT_GP_CFG_28(bank, fn, sfx, 0)
+
+#define PORT_GP_CFG_30(bank, fn, sfx, cfg) \
+ PORT_GP_CFG_28(bank, fn, sfx, cfg), \
+ PORT_GP_CFG_1(bank, 28, fn, sfx, cfg), PORT_GP_CFG_1(bank, 29, fn, sfx, cfg)
+#define PORT_GP_30(bank, fn, sfx) PORT_GP_CFG_30(bank, fn, sfx, 0)
+
+#define PORT_GP_CFG_32(bank, fn, sfx, cfg) \
+ PORT_GP_CFG_30(bank, fn, sfx, cfg), \
PORT_GP_CFG_1(bank, 30, fn, sfx, cfg), PORT_GP_CFG_1(bank, 31, fn, sfx, cfg)
#define PORT_GP_32(bank, fn, sfx) PORT_GP_CFG_32(bank, fn, sfx, 0)
diff --git a/drivers/pinctrl/sirf/pinctrl-atlas7.c b/drivers/pinctrl/sirf/pinctrl-atlas7.c
index 829018c812bd..053d98e33944 100644
--- a/drivers/pinctrl/sirf/pinctrl-atlas7.c
+++ b/drivers/pinctrl/sirf/pinctrl-atlas7.c
@@ -161,6 +161,9 @@ enum altas7_pad_type {
#define IN_DISABLE_VAL_1_REG_SET 0x0A88
#define IN_DISABLE_VAL_1_REG_CLR 0x0A8C
+/* Offset of the SDIO9SEL*/
+#define SYS2PCI_SDIO9SEL 0x14
+
struct dt_params {
const char *property;
int value;
@@ -370,6 +373,7 @@ struct atlas7_pmx {
struct pinctrl_desc pctl_desc;
struct atlas7_pinctrl_data *pctl_data;
void __iomem *regs[ATLAS7_PINCTRL_REG_BANKS];
+ void __iomem *sys2pci_base;
u32 status_ds[NUM_OF_IN_DISABLE_REG];
u32 status_dsv[NUM_OF_IN_DISABLE_REG];
struct atlas7_pad_status sleep_data[ATLAS7_PINCTRL_TOTAL_PINS];
@@ -885,11 +889,12 @@ static const unsigned int lr_lcdrom_pins[] = { 73, 54, 57, 58, 59, 60, 61,
62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 56, 53, 55, };
static const unsigned int lvds_analog_pins[] = { 149, 150, 151, 152, 153, 154,
155, 156, 157, 158, };
-static const unsigned int nd_df_pins[] = { 44, 43, 42, 41, 40, 39, 38, 37,
- 47, 46, 52, 51, 45, 49, 50, 48, 124, };
-static const unsigned int nd_df_nowp_pins[] = { 44, 43, 42, 41, 40, 39, 38,
- 37, 47, 46, 52, 51, 45, 49, 50, 48, };
+static const unsigned int nd_df_basic_pins[] = { 44, 43, 42, 41, 40, 39, 38,
+ 37, 47, 46, 52, 45, 49, 50, 48, };
+static const unsigned int nd_df_wp_pins[] = { 124, };
+static const unsigned int nd_df_cs_pins[] = { 51, };
static const unsigned int ps_pins[] = { 120, 119, 121, };
+static const unsigned int ps_no_dir_pins[] = { 119, };
static const unsigned int pwc_core_on_pins[] = { 8, };
static const unsigned int pwc_ext_on_pins[] = { 6, };
static const unsigned int pwc_gpio3_clk_pins[] = { 3, };
@@ -944,7 +949,7 @@ static const unsigned int sd2_cdb_pins0[] = { 124, };
static const unsigned int sd2_cdb_pins1[] = { 161, };
static const unsigned int sd2_wpb_pins0[] = { 123, };
static const unsigned int sd2_wpb_pins1[] = { 163, };
-static const unsigned int sd3_pins[] = { 85, 86, 87, 88, 89, 90, };
+static const unsigned int sd3_9_pins[] = { 85, 86, 87, 88, 89, 90, };
static const unsigned int sd5_pins[] = { 91, 92, 93, 94, 95, 96, };
static const unsigned int sd6_pins0[] = { 79, 78, 74, 75, 76, 77, };
static const unsigned int sd6_pins1[] = { 101, 99, 100, 110, 109, 111, };
@@ -998,9 +1003,9 @@ static const unsigned int vi_vip1_ext_pins[] = { 74, 75, 76, 77, 78, 79, 80,
81, 82, 83, 84, 108, 103, 104, 105, 106, 107, 102, 97, 98,
99, 100, };
static const unsigned int vi_vip1_low8bit_pins[] = { 74, 75, 76, 77, 78, 79,
- 80, 81, };
-static const unsigned int vi_vip1_high8bit_pins[] = { 82, 83, 84, 108, 103,
- 104, 105, 106, };
+ 80, 81, 82, 83, 84, };
+static const unsigned int vi_vip1_high8bit_pins[] = { 82, 83, 84, 103, 104,
+ 105, 106, 107, 102, 97, 98, };
/* definition of pin group table */
struct atlas7_pin_group altas7_pin_groups[] = {
@@ -1142,9 +1147,11 @@ struct atlas7_pin_group altas7_pin_groups[] = {
GROUP("ld_ldd_lck_grp", ld_ldd_lck_pins),
GROUP("lr_lcdrom_grp", lr_lcdrom_pins),
GROUP("lvds_analog_grp", lvds_analog_pins),
- GROUP("nd_df_grp", nd_df_pins),
- GROUP("nd_df_nowp_grp", nd_df_nowp_pins),
+ GROUP("nd_df_basic_grp", nd_df_basic_pins),
+ GROUP("nd_df_wp_grp", nd_df_wp_pins),
+ GROUP("nd_df_cs_grp", nd_df_cs_pins),
GROUP("ps_grp", ps_pins),
+ GROUP("ps_no_dir_grp", ps_no_dir_pins),
GROUP("pwc_core_on_grp", pwc_core_on_pins),
GROUP("pwc_ext_on_grp", pwc_ext_on_pins),
GROUP("pwc_gpio3_clk_grp", pwc_gpio3_clk_pins),
@@ -1196,7 +1203,7 @@ struct atlas7_pin_group altas7_pin_groups[] = {
GROUP("sd2_cdb_grp1", sd2_cdb_pins1),
GROUP("sd2_wpb_grp0", sd2_wpb_pins0),
GROUP("sd2_wpb_grp1", sd2_wpb_pins1),
- GROUP("sd3_grp", sd3_pins),
+ GROUP("sd3_9_grp", sd3_9_pins),
GROUP("sd5_grp", sd5_pins),
GROUP("sd6_grp0", sd6_pins0),
GROUP("sd6_grp1", sd6_pins1),
@@ -1421,9 +1428,11 @@ static const char * const ld_ldd_fck_grp[] = { "ld_ldd_fck_grp", };
static const char * const ld_ldd_lck_grp[] = { "ld_ldd_lck_grp", };
static const char * const lr_lcdrom_grp[] = { "lr_lcdrom_grp", };
static const char * const lvds_analog_grp[] = { "lvds_analog_grp", };
-static const char * const nd_df_grp[] = { "nd_df_grp", };
-static const char * const nd_df_nowp_grp[] = { "nd_df_nowp_grp", };
+static const char * const nd_df_basic_grp[] = { "nd_df_basic_grp", };
+static const char * const nd_df_wp_grp[] = { "nd_df_wp_grp", };
+static const char * const nd_df_cs_grp[] = { "nd_df_cs_grp", };
static const char * const ps_grp[] = { "ps_grp", };
+static const char * const ps_no_dir_grp[] = { "ps_no_dir_grp", };
static const char * const pwc_core_on_grp[] = { "pwc_core_on_grp", };
static const char * const pwc_ext_on_grp[] = { "pwc_ext_on_grp", };
static const char * const pwc_gpio3_clk_grp[] = { "pwc_gpio3_clk_grp", };
@@ -1478,7 +1487,7 @@ static const char * const sd2_cdb_grp0[] = { "sd2_cdb_grp0", };
static const char * const sd2_cdb_grp1[] = { "sd2_cdb_grp1", };
static const char * const sd2_wpb_grp0[] = { "sd2_wpb_grp0", };
static const char * const sd2_wpb_grp1[] = { "sd2_wpb_grp1", };
-static const char * const sd3_grp[] = { "sd3_grp", };
+static const char * const sd3_9_grp[] = { "sd3_9_grp", };
static const char * const sd5_grp[] = { "sd5_grp", };
static const char * const sd6_grp0[] = { "sd6_grp0", };
static const char * const sd6_grp1[] = { "sd6_grp1", };
@@ -3174,7 +3183,7 @@ static struct atlas7_grp_mux lvds_analog_grp_mux = {
.pad_mux_list = lvds_analog_grp_pad_mux,
};
-static struct atlas7_pad_mux nd_df_grp_pad_mux[] = {
+static struct atlas7_pad_mux nd_df_basic_grp_pad_mux[] = {
MUX(1, 44, 1, N, N, N, N),
MUX(1, 43, 1, N, N, N, N),
MUX(1, 42, 1, N, N, N, N),
@@ -3186,41 +3195,33 @@ static struct atlas7_pad_mux nd_df_grp_pad_mux[] = {
MUX(1, 47, 1, N, N, N, N),
MUX(1, 46, 1, N, N, N, N),
MUX(1, 52, 1, N, N, N, N),
- MUX(1, 51, 1, N, N, N, N),
MUX(1, 45, 1, N, N, N, N),
MUX(1, 49, 1, N, N, N, N),
MUX(1, 50, 1, N, N, N, N),
MUX(1, 48, 1, N, N, N, N),
+};
+
+static struct atlas7_grp_mux nd_df_basic_grp_mux = {
+ .pad_mux_count = ARRAY_SIZE(nd_df_basic_grp_pad_mux),
+ .pad_mux_list = nd_df_basic_grp_pad_mux,
+};
+
+static struct atlas7_pad_mux nd_df_wp_grp_pad_mux[] = {
MUX(1, 124, 4, N, N, N, N),
};
-static struct atlas7_grp_mux nd_df_grp_mux = {
- .pad_mux_count = ARRAY_SIZE(nd_df_grp_pad_mux),
- .pad_mux_list = nd_df_grp_pad_mux,
+static struct atlas7_grp_mux nd_df_wp_grp_mux = {
+ .pad_mux_count = ARRAY_SIZE(nd_df_wp_grp_pad_mux),
+ .pad_mux_list = nd_df_wp_grp_pad_mux,
};
-static struct atlas7_pad_mux nd_df_nowp_grp_pad_mux[] = {
- MUX(1, 44, 1, N, N, N, N),
- MUX(1, 43, 1, N, N, N, N),
- MUX(1, 42, 1, N, N, N, N),
- MUX(1, 41, 1, N, N, N, N),
- MUX(1, 40, 1, N, N, N, N),
- MUX(1, 39, 1, N, N, N, N),
- MUX(1, 38, 1, N, N, N, N),
- MUX(1, 37, 1, N, N, N, N),
- MUX(1, 47, 1, N, N, N, N),
- MUX(1, 46, 1, N, N, N, N),
- MUX(1, 52, 1, N, N, N, N),
+static struct atlas7_pad_mux nd_df_cs_grp_pad_mux[] = {
MUX(1, 51, 1, N, N, N, N),
- MUX(1, 45, 1, N, N, N, N),
- MUX(1, 49, 1, N, N, N, N),
- MUX(1, 50, 1, N, N, N, N),
- MUX(1, 48, 1, N, N, N, N),
};
-static struct atlas7_grp_mux nd_df_nowp_grp_mux = {
- .pad_mux_count = ARRAY_SIZE(nd_df_nowp_grp_pad_mux),
- .pad_mux_list = nd_df_nowp_grp_pad_mux,
+static struct atlas7_grp_mux nd_df_cs_grp_mux = {
+ .pad_mux_count = ARRAY_SIZE(nd_df_cs_grp_pad_mux),
+ .pad_mux_list = nd_df_cs_grp_pad_mux,
};
static struct atlas7_pad_mux ps_grp_pad_mux[] = {
@@ -3234,6 +3235,15 @@ static struct atlas7_grp_mux ps_grp_mux = {
.pad_mux_list = ps_grp_pad_mux,
};
+static struct atlas7_pad_mux ps_no_dir_grp_pad_mux[] = {
+ MUX(1, 119, 2, N, N, N, N),
+};
+
+static struct atlas7_grp_mux ps_no_dir_grp_mux = {
+ .pad_mux_count = ARRAY_SIZE(ps_no_dir_grp_pad_mux),
+ .pad_mux_list = ps_no_dir_grp_pad_mux,
+};
+
static struct atlas7_pad_mux pwc_core_on_grp_pad_mux[] = {
MUX(0, 8, 1, N, N, N, N),
};
@@ -3743,7 +3753,7 @@ static struct atlas7_grp_mux sd2_wpb_grp1_mux = {
.pad_mux_list = sd2_wpb_grp1_pad_mux,
};
-static struct atlas7_pad_mux sd3_grp_pad_mux[] = {
+static struct atlas7_pad_mux sd3_9_grp_pad_mux[] = {
MUX(1, 85, 1, N, N, N, N),
MUX(1, 86, 1, N, N, N, N),
MUX(1, 87, 1, N, N, N, N),
@@ -3752,9 +3762,9 @@ static struct atlas7_pad_mux sd3_grp_pad_mux[] = {
MUX(1, 90, 1, N, N, N, N),
};
-static struct atlas7_grp_mux sd3_grp_mux = {
- .pad_mux_count = ARRAY_SIZE(sd3_grp_pad_mux),
- .pad_mux_list = sd3_grp_pad_mux,
+static struct atlas7_grp_mux sd3_9_grp_mux = {
+ .pad_mux_count = ARRAY_SIZE(sd3_9_grp_pad_mux),
+ .pad_mux_list = sd3_9_grp_pad_mux,
};
static struct atlas7_pad_mux sd5_grp_pad_mux[] = {
@@ -4296,6 +4306,9 @@ static struct atlas7_pad_mux vi_vip1_low8bit_grp_pad_mux[] = {
MUX(1, 79, 1, N, N, N, N),
MUX(1, 80, 1, N, N, N, N),
MUX(1, 81, 1, N, N, N, N),
+ MUX(1, 82, 1, N, N, N, N),
+ MUX(1, 83, 1, N, N, N, N),
+ MUX(1, 84, 1, N, N, N, N),
};
static struct atlas7_grp_mux vi_vip1_low8bit_grp_mux = {
@@ -4307,11 +4320,14 @@ static struct atlas7_pad_mux vi_vip1_high8bit_grp_pad_mux[] = {
MUX(1, 82, 1, N, N, N, N),
MUX(1, 83, 1, N, N, N, N),
MUX(1, 84, 1, N, N, N, N),
- MUX(1, 108, 2, N, N, N, N),
MUX(1, 103, 2, N, N, N, N),
MUX(1, 104, 2, N, N, N, N),
MUX(1, 105, 2, N, N, N, N),
MUX(1, 106, 2, N, N, N, N),
+ MUX(1, 107, 2, N, N, N, N),
+ MUX(1, 102, 2, N, N, N, N),
+ MUX(1, 97, 2, N, N, N, N),
+ MUX(1, 98, 2, N, N, N, N),
};
static struct atlas7_grp_mux vi_vip1_high8bit_grp_mux = {
@@ -4598,9 +4614,11 @@ static struct atlas7_pmx_func atlas7_pmx_functions[] = {
FUNCTION("ld_ldd_lck", ld_ldd_lck_grp, &ld_ldd_lck_grp_mux),
FUNCTION("lr_lcdrom", lr_lcdrom_grp, &lr_lcdrom_grp_mux),
FUNCTION("lvds_analog", lvds_analog_grp, &lvds_analog_grp_mux),
- FUNCTION("nd_df", nd_df_grp, &nd_df_grp_mux),
- FUNCTION("nd_df_nowp", nd_df_nowp_grp, &nd_df_nowp_grp_mux),
+ FUNCTION("nd_df_basic", nd_df_basic_grp, &nd_df_basic_grp_mux),
+ FUNCTION("nd_df_wp", nd_df_wp_grp, &nd_df_wp_grp_mux),
+ FUNCTION("nd_df_cs", nd_df_cs_grp, &nd_df_cs_grp_mux),
FUNCTION("ps", ps_grp, &ps_grp_mux),
+ FUNCTION("ps_no_dir", ps_no_dir_grp, &ps_no_dir_grp_mux),
FUNCTION("pwc_core_on", pwc_core_on_grp, &pwc_core_on_grp_mux),
FUNCTION("pwc_ext_on", pwc_ext_on_grp, &pwc_ext_on_grp_mux),
FUNCTION("pwc_gpio3_clk", pwc_gpio3_clk_grp, &pwc_gpio3_clk_grp_mux),
@@ -4686,10 +4704,11 @@ static struct atlas7_pmx_func atlas7_pmx_functions[] = {
FUNCTION("sd2_cdb_m1", sd2_cdb_grp1, &sd2_cdb_grp1_mux),
FUNCTION("sd2_wpb_m0", sd2_wpb_grp0, &sd2_wpb_grp0_mux),
FUNCTION("sd2_wpb_m1", sd2_wpb_grp1, &sd2_wpb_grp1_mux),
- FUNCTION("sd3", sd3_grp, &sd3_grp_mux),
+ FUNCTION("sd3", sd3_9_grp, &sd3_9_grp_mux),
FUNCTION("sd5", sd5_grp, &sd5_grp_mux),
FUNCTION("sd6_m0", sd6_grp0, &sd6_grp0_mux),
FUNCTION("sd6_m1", sd6_grp1, &sd6_grp1_mux),
+ FUNCTION("sd9", sd3_9_grp, &sd3_9_grp_mux),
FUNCTION("sp0_ext_ldo_on",
sp0_ext_ldo_on_grp,
&sp0_ext_ldo_on_grp_mux),
@@ -5097,6 +5116,14 @@ static int atlas7_pmx_set_mux(struct pinctrl_dev *pctldev,
pr_debug("PMX DUMP ### Function:[%s] Group:[%s] #### START >>>\n",
pmx_func->name, pin_grp->name);
+ /* the sd3 and sd9 pin select by SYS2PCI_SDIO9SEL register */
+ if (pin_grp->pins == (unsigned int *)&sd3_9_pins) {
+ if (!strcmp(pmx_func->name, "sd9"))
+ writel(1, pmx->sys2pci_base + SYS2PCI_SDIO9SEL);
+ else
+ writel(0, pmx->sys2pci_base + SYS2PCI_SDIO9SEL);
+ }
+
grp_mux = pmx_func->grpmux;
for (idx = 0; idx < grp_mux->pad_mux_count; idx++) {
@@ -5385,12 +5412,27 @@ static int atlas7_pinmux_probe(struct platform_device *pdev)
struct atlas7_pmx *pmx;
struct device_node *np = pdev->dev.of_node;
u32 banks = ATLAS7_PINCTRL_REG_BANKS;
+ struct device_node *sys2pci_np;
+ struct resource res;
/* Create state holders etc for this driver */
pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL);
if (!pmx)
return -ENOMEM;
+ /* The sd3 and sd9 shared all pins, and the function select by
+ * SYS2PCI_SDIO9SEL register
+ */
+ sys2pci_np = of_find_node_by_name(NULL, "sys2pci");
+ if (!sys2pci_np)
+ return -EINVAL;
+ ret = of_address_to_resource(sys2pci_np, 0, &res);
+ if (ret)
+ return ret;
+ pmx->sys2pci_base = devm_ioremap_resource(&pdev->dev, &res);
+ if (IS_ERR(pmx->sys2pci_base))
+ return -ENOMEM;
+
pmx->dev = &pdev->dev;
pmx->pctl_data = &atlas7_ioc_data;
diff --git a/drivers/pinctrl/sirf/pinctrl-sirf.c b/drivers/pinctrl/sirf/pinctrl-sirf.c
index 2a8d69725de8..edf40df05ec0 100644
--- a/drivers/pinctrl/sirf/pinctrl-sirf.c
+++ b/drivers/pinctrl/sirf/pinctrl-sirf.c
@@ -85,12 +85,16 @@ static int sirfsoc_dt_node_to_map(struct pinctrl_dev *pctldev,
/* calculate number of maps required */
for_each_child_of_node(np_config, np) {
ret = of_property_read_string(np, "sirf,function", &function);
- if (ret < 0)
+ if (ret < 0) {
+ of_node_put(np);
return ret;
+ }
ret = of_property_count_strings(np, "sirf,pins");
- if (ret < 0)
+ if (ret < 0) {
+ of_node_put(np);
return ret;
+ }
count += ret;
}
diff --git a/drivers/pinctrl/spear/Makefile b/drivers/pinctrl/spear/Makefile
index 0e400ebeb8ff..37b8412ac8a3 100644
--- a/drivers/pinctrl/spear/Makefile
+++ b/drivers/pinctrl/spear/Makefile
@@ -1,7 +1,7 @@
# SPEAr pinmux support
obj-$(CONFIG_PINCTRL_SPEAR_PLGPIO) += pinctrl-plgpio.o
-obj-$(CONFIG_PINCTRL_SPEAR) += pinctrl-spear.o
+obj-y += pinctrl-spear.o
obj-$(CONFIG_PINCTRL_SPEAR3XX) += pinctrl-spear3xx.o
obj-$(CONFIG_PINCTRL_SPEAR300) += pinctrl-spear300.o
obj-$(CONFIG_PINCTRL_SPEAR310) += pinctrl-spear310.o
diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig
index e68fd951129a..f8dbc8bec0e1 100644
--- a/drivers/pinctrl/sunxi/Kconfig
+++ b/drivers/pinctrl/sunxi/Kconfig
@@ -51,8 +51,17 @@ config PINCTRL_SUN8I_A23_R
depends on RESET_CONTROLLER
select PINCTRL_SUNXI_COMMON
+config PINCTRL_SUN8I_H3
+ def_bool MACH_SUN8I
+ select PINCTRL_SUNXI_COMMON
+
config PINCTRL_SUN9I_A80
def_bool MACH_SUN9I
select PINCTRL_SUNXI_COMMON
+config PINCTRL_SUN9I_A80_R
+ def_bool MACH_SUN9I
+ depends on RESET_CONTROLLER
+ select PINCTRL_SUNXI_COMMON
+
endif
diff --git a/drivers/pinctrl/sunxi/Makefile b/drivers/pinctrl/sunxi/Makefile
index e08029034510..ef82f22bb9ef 100644
--- a/drivers/pinctrl/sunxi/Makefile
+++ b/drivers/pinctrl/sunxi/Makefile
@@ -13,4 +13,6 @@ obj-$(CONFIG_PINCTRL_SUN8I_A23) += pinctrl-sun8i-a23.o
obj-$(CONFIG_PINCTRL_SUN8I_A23_R) += pinctrl-sun8i-a23-r.o
obj-$(CONFIG_PINCTRL_SUN8I_A33) += pinctrl-sun8i-a33.o
obj-$(CONFIG_PINCTRL_SUN8I_A83T) += pinctrl-sun8i-a83t.o
+obj-$(CONFIG_PINCTRL_SUN8I_H3) += pinctrl-sun8i-h3.o
obj-$(CONFIG_PINCTRL_SUN9I_A80) += pinctrl-sun9i-a80.o
+obj-$(CONFIG_PINCTRL_SUN9I_A80_R) += pinctrl-sun9i-a80-r.o
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun8i-h3.c b/drivers/pinctrl/sunxi/pinctrl-sun8i-h3.c
new file mode 100644
index 000000000000..77d4cf047cee
--- /dev/null
+++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-h3.c
@@ -0,0 +1,515 @@
+/*
+ * Allwinner H3 SoCs pinctrl driver.
+ *
+ * Copyright (C) 2015 Jens Kuske <jenskuske@gmail.com>
+ *
+ * Based on pinctrl-sun8i-a23.c, which is:
+ * Copyright (C) 2014 Chen-Yu Tsai <wens@csie.org>
+ * Copyright (C) 2014 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/pinctrl.h>
+
+#include "pinctrl-sunxi.h"
+
+static const struct sunxi_desc_pin sun8i_h3_pins[] = {
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 0),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "uart2"), /* TX */
+ SUNXI_FUNCTION(0x3, "jtag"), /* MS */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 0)), /* PA_EINT0 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 1),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "uart2"), /* RX */
+ SUNXI_FUNCTION(0x3, "jtag"), /* CK */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 1)), /* PA_EINT1 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 2),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "uart2"), /* RTS */
+ SUNXI_FUNCTION(0x3, "jtag"), /* DO */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 2)), /* PA_EINT2 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 3),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "uart2"), /* CTS */
+ SUNXI_FUNCTION(0x3, "jtag"), /* DI */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 3)), /* PA_EINT3 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 4),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "uart0"), /* TX */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 4)), /* PA_EINT4 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 5),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "uart0"), /* RX */
+ SUNXI_FUNCTION(0x3, "pwm0"),
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 5)), /* PA_EINT5 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 6),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "sim"), /* PWREN */
+ SUNXI_FUNCTION(0x3, "pwm1"),
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 6)), /* PA_EINT6 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 7),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "sim"), /* CLK */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 7)), /* PA_EINT7 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 8),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "sim"), /* DATA */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 8)), /* PA_EINT8 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 9),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "sim"), /* RST */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 9)), /* PA_EINT9 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 10),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "sim"), /* DET */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 10)), /* PA_EINT10 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 11),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2c0"), /* SCK */
+ SUNXI_FUNCTION(0x3, "di"), /* TX */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 11)), /* PA_EINT11 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 12),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2c0"), /* SDA */
+ SUNXI_FUNCTION(0x3, "di"), /* RX */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 12)), /* PA_EINT12 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 13),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "spi1"), /* CS */
+ SUNXI_FUNCTION(0x3, "uart3"), /* TX */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 13)), /* PA_EINT13 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 14),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "spi1"), /* CLK */
+ SUNXI_FUNCTION(0x3, "uart3"), /* RX */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 14)), /* PA_EINT14 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 15),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "spi1"), /* MOSI */
+ SUNXI_FUNCTION(0x3, "uart3"), /* RTS */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 15)), /* PA_EINT15 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 16),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "spi1"), /* MISO */
+ SUNXI_FUNCTION(0x3, "uart3"), /* CTS */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 16)), /* PA_EINT16 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 17),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "spdif"), /* OUT */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 17)), /* PA_EINT17 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 18),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2s0"), /* SYNC */
+ SUNXI_FUNCTION(0x3, "i2c1"), /* SCK */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 18)), /* PA_EINT18 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 19),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2s0"), /* CLK */
+ SUNXI_FUNCTION(0x3, "i2c1"), /* SDA */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 19)), /* PA_EINT19 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 20),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2s0"), /* DOUT */
+ SUNXI_FUNCTION(0x3, "sim"), /* VPPEN */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 20)), /* PA_EINT20 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 21),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2s0"), /* DIN */
+ SUNXI_FUNCTION(0x3, "sim"), /* VPPPP */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 21)), /* PA_EINT21 */
+ /* Hole */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 0),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* WE */
+ SUNXI_FUNCTION(0x3, "spi0")), /* MOSI */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 1),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* ALE */
+ SUNXI_FUNCTION(0x3, "spi0")), /* MISO */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 2),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* CLE */
+ SUNXI_FUNCTION(0x3, "spi0")), /* CLK */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 3),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* CE1 */
+ SUNXI_FUNCTION(0x3, "spi0")), /* CS */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 4),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0")), /* CE0 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 5),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* RE */
+ SUNXI_FUNCTION(0x3, "mmc2")), /* CLK */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 6),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* RB0 */
+ SUNXI_FUNCTION(0x3, "mmc2")), /* CMD */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 7),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0")), /* RB1 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 8),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* DQ0 */
+ SUNXI_FUNCTION(0x3, "mmc2")), /* D0 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 9),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* DQ1 */
+ SUNXI_FUNCTION(0x3, "mmc2")), /* D1 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 10),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* DQ2 */
+ SUNXI_FUNCTION(0x3, "mmc2")), /* D2 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 11),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* DQ3 */
+ SUNXI_FUNCTION(0x3, "mmc2")), /* D3 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 12),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* DQ4 */
+ SUNXI_FUNCTION(0x3, "mmc2")), /* D4 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 13),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand0"), /* DQ5 */
+ SUNXI_FUNCTION(0x3, "mmc2")), /* D5 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 14),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand"), /* DQ6 */
+ SUNXI_FUNCTION(0x3, "mmc2")), /* D6 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 15),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand"), /* DQ7 */
+ SUNXI_FUNCTION(0x3, "mmc2")), /* D7 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 16),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "nand"), /* DQS */
+ SUNXI_FUNCTION(0x3, "mmc2")), /* RST */
+ /* Hole */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 0),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "emac")), /* RXD3 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 1),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "emac")), /* RXD2 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 2),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "emac")), /* RXD1 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 3),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "emac")), /* RXD0 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 4),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "emac")), /* RXCK */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 5),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "emac")), /* RXCTL/RXDV */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 6),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "emac")), /* RXERR */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 7),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "emac")), /* TXD3 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 8),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "emac")), /* TXD2 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 9),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "emac")), /* TXD1 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 10),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "emac")), /* TXD0 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 11),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "emac")), /* CRS */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 12),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "emac")), /* TXCK */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 13),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "emac")), /* TXCTL/TXEN */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 14),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "emac")), /* TXERR */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 15),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "emac")), /* CLKIN/COL */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 16),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "emac")), /* MDC */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 17),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "emac")), /* MDIO */
+ /* Hole */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 0),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "csi"), /* PCLK */
+ SUNXI_FUNCTION(0x3, "ts")), /* CLK */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 1),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "csi"), /* MCLK */
+ SUNXI_FUNCTION(0x3, "ts")), /* ERR */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 2),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "csi"), /* HSYNC */
+ SUNXI_FUNCTION(0x3, "ts")), /* SYNC */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 3),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "csi"), /* VSYNC */
+ SUNXI_FUNCTION(0x3, "ts")), /* DVLD */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 4),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "csi"), /* D0 */
+ SUNXI_FUNCTION(0x3, "ts")), /* D0 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 5),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "csi"), /* D1 */
+ SUNXI_FUNCTION(0x3, "ts")), /* D1 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 6),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "csi"), /* D2 */
+ SUNXI_FUNCTION(0x3, "ts")), /* D2 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 7),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "csi"), /* D3 */
+ SUNXI_FUNCTION(0x3, "ts")), /* D3 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 8),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "csi"), /* D4 */
+ SUNXI_FUNCTION(0x3, "ts")), /* D4 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 9),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "csi"), /* D5 */
+ SUNXI_FUNCTION(0x3, "ts")), /* D5 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 10),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "csi"), /* D6 */
+ SUNXI_FUNCTION(0x3, "ts")), /* D6 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 11),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "csi"), /* D7 */
+ SUNXI_FUNCTION(0x3, "ts")), /* D7 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 12),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "csi"), /* SCK */
+ SUNXI_FUNCTION(0x3, "i2c2")), /* SCK */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 13),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "csi"), /* SDA */
+ SUNXI_FUNCTION(0x3, "i2c2")), /* SDA */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 14),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out")),
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 15),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out")),
+ /* Hole */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 0),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc0"), /* D1 */
+ SUNXI_FUNCTION(0x3, "jtag")), /* MS */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 1),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc0"), /* D0 */
+ SUNXI_FUNCTION(0x3, "jtag")), /* DI */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 2),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc0"), /* CLK */
+ SUNXI_FUNCTION(0x3, "uart0")), /* TX */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 3),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc0"), /* CMD */
+ SUNXI_FUNCTION(0x3, "jtag")), /* DO */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 4),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc0"), /* D3 */
+ SUNXI_FUNCTION(0x3, "uart0")), /* RX */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 5),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc0"), /* D2 */
+ SUNXI_FUNCTION(0x3, "jtag")), /* CK */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 6),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out")),
+ /* Hole */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 0),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc1"), /* CLK */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 0)), /* PG_EINT0 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 1),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc1"), /* CMD */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 1)), /* PG_EINT1 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 2),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc1"), /* D0 */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 2)), /* PG_EINT2 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 3),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc1"), /* D1 */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 3)), /* PG_EINT3 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 4),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc1"), /* D2 */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 4)), /* PG_EINT4 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 5),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "mmc1"), /* D3 */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 5)), /* PG_EINT5 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 6),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "uart1"), /* TX */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 6)), /* PG_EINT6 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 7),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "uart1"), /* RX */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 7)), /* PG_EINT7 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 8),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "uart1"), /* RTS */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 8)), /* PG_EINT8 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 9),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "uart1"), /* CTS */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 9)), /* PG_EINT9 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 10),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2s1"), /* SYNC */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 10)), /* PG_EINT10 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 11),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2s1"), /* CLK */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 11)), /* PG_EINT11 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 12),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2s1"), /* DOUT */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 12)), /* PG_EINT12 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 13),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "i2s1"), /* DIN */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 13)), /* PG_EINT13 */
+};
+
+static const struct sunxi_pinctrl_desc sun8i_h3_pinctrl_data = {
+ .pins = sun8i_h3_pins,
+ .npins = ARRAY_SIZE(sun8i_h3_pins),
+ .irq_banks = 2,
+};
+
+static int sun8i_h3_pinctrl_probe(struct platform_device *pdev)
+{
+ return sunxi_pinctrl_init(pdev,
+ &sun8i_h3_pinctrl_data);
+}
+
+static const struct of_device_id sun8i_h3_pinctrl_match[] = {
+ { .compatible = "allwinner,sun8i-h3-pinctrl", },
+ {}
+};
+
+static struct platform_driver sun8i_h3_pinctrl_driver = {
+ .probe = sun8i_h3_pinctrl_probe,
+ .driver = {
+ .name = "sun8i-h3-pinctrl",
+ .of_match_table = sun8i_h3_pinctrl_match,
+ },
+};
+builtin_platform_driver(sun8i_h3_pinctrl_driver);
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c
new file mode 100644
index 000000000000..42547ffa20a8
--- /dev/null
+++ b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80-r.c
@@ -0,0 +1,181 @@
+/*
+ * Allwinner A80 SoCs special pins pinctrl driver.
+ *
+ * Copyright (C) 2014 Maxime Ripard
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/reset.h>
+
+#include "pinctrl-sunxi.h"
+
+static const struct sunxi_desc_pin sun9i_a80_r_pins[] = {
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 0),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x3, "s_uart"), /* TX */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 0)), /* PL_EINT0 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 1),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x3, "s_uart"), /* RX */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 1)), /* PL_EINT1 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 2),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x3, "s_jtag"), /* TMS */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 2)), /* PL_EINT2 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 3),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x3, "s_jtag"), /* TCK */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 3)), /* PL_EINT3 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 4),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x3, "s_jtag"), /* TDO */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 4)), /* PL_EINT4 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 5),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x3, "s_jtag"), /* TDI */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 5)), /* PL_EINT5 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 6),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x3, "s_cir_rx"),
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 6)), /* PL_EINT6 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 7),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x3, "1wire"),
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 7)), /* PL_EINT7 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 8),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "s_ps2"), /* SCK1 */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 8)), /* PL_EINT8 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 9),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "s_ps2"), /* SDA1 */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 9)), /* PL_EINT9 */
+
+ /* Hole */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 0),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 0)), /* PM_EINT0 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 1),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 1)), /* PM_EINT1 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 2),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 2)), /* PM_EINT2 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 3),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 3)), /* PM_EINT3 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 4),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x3, "s_i2s1"), /* LRCKR */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 4)), /* PM_EINT4 */
+
+ /* Hole */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 8),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x3, "s_i2c1"), /* SCK */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 8)), /* PM_EINT8 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 9),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x3, "s_i2c1"), /* SDA */
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 9)), /* PM_EINT9 */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 10),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "s_i2s0"), /* MCLK */
+ SUNXI_FUNCTION(0x3, "s_i2s1")), /* MCLK */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 11),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "s_i2s0"), /* BCLK */
+ SUNXI_FUNCTION(0x3, "s_i2s1")), /* BCLK */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 12),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "s_i2s0"), /* LRCK */
+ SUNXI_FUNCTION(0x3, "s_i2s1")), /* LRCK */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 13),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "s_i2s0"), /* DIN */
+ SUNXI_FUNCTION(0x3, "s_i2s1")), /* DIN */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 14),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "s_i2s0"), /* DOUT */
+ SUNXI_FUNCTION(0x3, "s_i2s1")), /* DOUT */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(M, 15),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 15)), /* PM_EINT15 */
+
+ /* Hole */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(N, 0),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "s_i2c0"), /* SCK */
+ SUNXI_FUNCTION(0x3, "s_rsb")), /* SCK */
+ SUNXI_PIN(SUNXI_PINCTRL_PIN(N, 1),
+ SUNXI_FUNCTION(0x0, "gpio_in"),
+ SUNXI_FUNCTION(0x1, "gpio_out"),
+ SUNXI_FUNCTION(0x2, "s_i2c0"), /* SDA */
+ SUNXI_FUNCTION(0x3, "s_rsb")), /* SDA */
+};
+
+static const struct sunxi_pinctrl_desc sun9i_a80_r_pinctrl_data = {
+ .pins = sun9i_a80_r_pins,
+ .npins = ARRAY_SIZE(sun9i_a80_r_pins),
+ .pin_base = PL_BASE,
+ .irq_banks = 2,
+};
+
+static int sun9i_a80_r_pinctrl_probe(struct platform_device *pdev)
+{
+ return sunxi_pinctrl_init(pdev,
+ &sun9i_a80_r_pinctrl_data);
+}
+
+static const struct of_device_id sun9i_a80_r_pinctrl_match[] = {
+ { .compatible = "allwinner,sun9i-a80-r-pinctrl", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sun9i_a80_r_pinctrl_match);
+
+static struct platform_driver sun9i_a80_r_pinctrl_driver = {
+ .probe = sun9i_a80_r_pinctrl_probe,
+ .driver = {
+ .name = "sun9i-a80-r-pinctrl",
+ .owner = THIS_MODULE,
+ .of_match_table = sun9i_a80_r_pinctrl_match,
+ },
+};
+module_platform_driver(sun9i_a80_r_pinctrl_driver);
+
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com");
+MODULE_DESCRIPTION("Allwinner A80 R_PIO pinctrl driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pinctrl/uniphier/Kconfig b/drivers/pinctrl/uniphier/Kconfig
index ad907072e09f..7abd614dc383 100644
--- a/drivers/pinctrl/uniphier/Kconfig
+++ b/drivers/pinctrl/uniphier/Kconfig
@@ -1,32 +1,35 @@
-if ARCH_UNIPHIER
-
-config PINCTRL_UNIPHIER
- bool
+menuconfig PINCTRL_UNIPHIER
+ bool "UniPhier SoC pinctrl drivers"
+ depends on ARCH_UNIPHIER
+ depends on OF && MFD_SYSCON
+ default y
select PINMUX
select GENERIC_PINCONF
+if PINCTRL_UNIPHIER
+
config PINCTRL_UNIPHIER_PH1_LD4
tristate "UniPhier PH1-LD4 SoC pinctrl driver"
- select PINCTRL_UNIPHIER
+ default y
config PINCTRL_UNIPHIER_PH1_PRO4
tristate "UniPhier PH1-Pro4 SoC pinctrl driver"
- select PINCTRL_UNIPHIER
+ default y
config PINCTRL_UNIPHIER_PH1_SLD8
tristate "UniPhier PH1-sLD8 SoC pinctrl driver"
- select PINCTRL_UNIPHIER
+ default y
config PINCTRL_UNIPHIER_PH1_PRO5
tristate "UniPhier PH1-Pro5 SoC pinctrl driver"
- select PINCTRL_UNIPHIER
+ default y
config PINCTRL_UNIPHIER_PROXSTREAM2
tristate "UniPhier ProXstream2 SoC pinctrl driver"
- select PINCTRL_UNIPHIER
+ default y
config PINCTRL_UNIPHIER_PH1_LD6B
tristate "UniPhier PH1-LD6b SoC pinctrl driver"
- select PINCTRL_UNIPHIER
+ default y
endif
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
index f2d77fe696ac..cb8a9c2a3a1f 100644
--- a/drivers/platform/x86/dell-wmi.c
+++ b/drivers/platform/x86/dell-wmi.c
@@ -43,8 +43,6 @@ MODULE_LICENSE("GPL");
#define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492"
-static int acpi_video;
-
MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
/*
@@ -159,7 +157,8 @@ static void dell_wmi_process_key(int reported_key)
/* Don't report brightness notifications that will also come via ACPI */
if ((key->keycode == KEY_BRIGHTNESSUP ||
- key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video)
+ key->keycode == KEY_BRIGHTNESSDOWN) &&
+ acpi_video_handles_brightness_key_presses())
return;
sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true);
@@ -398,7 +397,6 @@ static int __init dell_wmi_init(void)
}
dmi_walk(find_hk_type, NULL);
- acpi_video = acpi_video_get_backlight_type() != acpi_backlight_vendor;
err = dell_wmi_input_setup();
if (err)
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 0bed4733c4f0..f453d5dc085e 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -3488,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_get_backlight_type() != acpi_backlight_vendor) {
+ if (acpi_video_handles_brightness_key_presses()) {
pr_info("This ThinkPad has standard ACPI backlight "
"brightness control, supported by the ACPI "
"video driver\n");
diff --git a/drivers/pnp/driver.c b/drivers/pnp/driver.c
index 153a493b5413..63452f20e3e9 100644
--- a/drivers/pnp/driver.c
+++ b/drivers/pnp/driver.c
@@ -74,7 +74,6 @@ void pnp_device_detach(struct pnp_dev *pnp_dev)
if (pnp_dev->status == PNP_ATTACHED)
pnp_dev->status = PNP_READY;
mutex_unlock(&pnp_lock);
- pnp_disable_dev(pnp_dev);
}
static int pnp_device_probe(struct device *dev)
@@ -131,6 +130,11 @@ static int pnp_device_remove(struct device *dev)
drv->remove(pnp_dev);
pnp_dev->driver = NULL;
}
+
+ if (pnp_dev->active &&
+ (!drv || !(drv->flags & PNP_DRIVER_RES_DO_NOT_CHANGE)))
+ pnp_disable_dev(pnp_dev);
+
pnp_device_detach(pnp_dev);
return 0;
}
diff --git a/drivers/pnp/quirks.c b/drivers/pnp/quirks.c
index 943c1cb9566c..f700723ca5d6 100644
--- a/drivers/pnp/quirks.c
+++ b/drivers/pnp/quirks.c
@@ -343,6 +343,7 @@ static void quirk_amd_mmconfig_area(struct pnp_dev *dev)
static const unsigned int mch_quirk_devices[] = {
0x0154, /* Ivy Bridge */
0x0c00, /* Haswell */
+ 0x1604, /* Broadwell */
};
static struct pci_dev *get_intel_host(void)
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 237d7aa73e8c..1ddd13cc0c07 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -160,22 +160,16 @@ config BATTERY_SBS
config BATTERY_BQ27XXX
tristate "BQ27xxx battery driver"
help
- Say Y here to enable support for batteries with BQ27xxx (I2C/HDQ) chips.
+ Say Y here to enable support for batteries with BQ27xxx chips.
config BATTERY_BQ27XXX_I2C
- bool "BQ27xxx I2C support"
+ tristate "BQ27xxx I2C support"
depends on BATTERY_BQ27XXX
depends on I2C
default y
help
- Say Y here to enable support for batteries with BQ27xxx (I2C) chips.
-
-config BATTERY_BQ27XXX_PLATFORM
- bool "BQ27xxx HDQ support"
- depends on BATTERY_BQ27XXX
- default y
- help
- Say Y here to enable support for batteries with BQ27xxx (HDQ) chips.
+ Say Y here to enable support for batteries with BQ27xxx chips
+ connected over an I2C bus.
config BATTERY_DA9030
tristate "DA9030 battery driver"
@@ -508,8 +502,7 @@ config AXP20X_POWER
This driver provides support for the power supply features of
AXP20x PMIC.
-source "drivers/power/reset/Kconfig"
-
endif # POWER_SUPPLY
+source "drivers/power/reset/Kconfig"
source "drivers/power/avs/Kconfig"
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index b656638f8b39..0e4eab55f8d7 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_BATTERY_IPAQ_MICRO) += ipaq_micro_battery.o
obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o
obj-$(CONFIG_BATTERY_SBS) += sbs-battery.o
obj-$(CONFIG_BATTERY_BQ27XXX) += bq27xxx_battery.o
+obj-$(CONFIG_BATTERY_BQ27XXX_I2C) += bq27xxx_battery_i2c.o
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o
obj-$(CONFIG_CHARGER_DA9150) += da9150-charger.o
diff --git a/drivers/power/bq2415x_charger.c b/drivers/power/bq2415x_charger.c
index 4afd76848bce..27e89536689a 100644
--- a/drivers/power/bq2415x_charger.c
+++ b/drivers/power/bq2415x_charger.c
@@ -1704,7 +1704,7 @@ error_4:
error_3:
bq2415x_power_supply_exit(bq);
error_2:
- if (bq && bq->notify_node)
+ if (bq)
of_node_put(bq->notify_node);
kfree(name);
error_1:
@@ -1724,9 +1724,7 @@ static int bq2415x_remove(struct i2c_client *client)
if (bq->nb.notifier_call)
power_supply_unreg_notifier(&bq->nb);
- if (bq->notify_node)
- of_node_put(bq->notify_node);
-
+ of_node_put(bq->notify_node);
bq2415x_sysfs_exit(bq);
bq2415x_power_supply_exit(bq);
diff --git a/drivers/power/bq27xxx_battery.c b/drivers/power/bq27xxx_battery.c
index 880233ce9343..6b027a418943 100644
--- a/drivers/power/bq27xxx_battery.c
+++ b/drivers/power/bq27xxx_battery.c
@@ -45,11 +45,7 @@
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
-#include <linux/idr.h>
-#include <linux/i2c.h>
#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <asm/unaligned.h>
#include <linux/power/bq27xxx_battery.h>
@@ -78,11 +74,6 @@
#define BQ27XXX_POWER_CONSTANT (29200) /* 29.2 µV^2 * 1000 */
#define BQ27XXX_CURRENT_CONSTANT (3570) /* 3.57 µV * 1000 */
-struct bq27xxx_device_info;
-struct bq27xxx_access_methods {
- int (*read)(struct bq27xxx_device_info *di, u8 reg, bool single);
-};
-
#define INVALID_REG_ADDR 0xff
/*
@@ -110,40 +101,6 @@ enum bq27xxx_reg_index {
BQ27XXX_REG_AP, /* Average Power */
};
-struct bq27xxx_reg_cache {
- int temperature;
- int time_to_empty;
- int time_to_empty_avg;
- int time_to_full;
- int charge_full;
- int cycle_count;
- int capacity;
- int energy;
- int flags;
- int power_avg;
- int health;
-};
-
-struct bq27xxx_device_info {
- struct device *dev;
- int id;
- enum bq27xxx_chip chip;
-
- struct bq27xxx_reg_cache cache;
- int charge_design_full;
-
- unsigned long last_update;
- struct delayed_work work;
-
- struct power_supply *bat;
-
- struct bq27xxx_access_methods bus;
-
- struct mutex lock;
-
- u8 *regs;
-};
-
/* Register mappings */
static u8 bq27000_regs[] = {
0x00, /* CONTROL */
@@ -198,10 +155,10 @@ static u8 bq27500_regs[] = {
INVALID_REG_ADDR, /* TTECP - NA */
0x0c, /* NAC */
0x12, /* LMD(FCC) */
- 0x1e, /* CYCT */
+ 0x2a, /* CYCT */
INVALID_REG_ADDR, /* AE - NA */
- 0x20, /* SOC(RSOC) */
- 0x2e, /* DCAP(ILMD) */
+ 0x2c, /* SOC(RSOC) */
+ 0x3c, /* DCAP(ILMD) */
INVALID_REG_ADDR, /* AP - NA */
};
@@ -242,7 +199,7 @@ static u8 bq27541_regs[] = {
INVALID_REG_ADDR, /* AE - NA */
0x2c, /* SOC(RSOC) */
0x3c, /* DCAP */
- 0x76, /* AP */
+ 0x24, /* AP */
};
static u8 bq27545_regs[] = {
@@ -471,7 +428,10 @@ static int bq27xxx_battery_read_soc(struct bq27xxx_device_info *di)
{
int soc;
- soc = bq27xxx_read(di, BQ27XXX_REG_SOC, false);
+ if (di->chip == BQ27000 || di->chip == BQ27010)
+ soc = bq27xxx_read(di, BQ27XXX_REG_SOC, true);
+ else
+ soc = bq27xxx_read(di, BQ27XXX_REG_SOC, false);
if (soc < 0)
dev_dbg(di->dev, "error reading State-of-Charge\n");
@@ -536,7 +496,10 @@ static int bq27xxx_battery_read_dcap(struct bq27xxx_device_info *di)
{
int dcap;
- dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, false);
+ if (di->chip == BQ27000 || di->chip == BQ27010)
+ dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, true);
+ else
+ dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, false);
if (dcap < 0) {
dev_dbg(di->dev, "error reading initial last measured discharge\n");
@@ -544,7 +507,7 @@ static int bq27xxx_battery_read_dcap(struct bq27xxx_device_info *di)
}
if (di->chip == BQ27000 || di->chip == BQ27010)
- dcap *= BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS;
+ dcap = (dcap << 8) * BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS;
else
dcap *= 1000;
@@ -710,7 +673,7 @@ static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di)
return POWER_SUPPLY_HEALTH_GOOD;
}
-static void bq27xxx_battery_update(struct bq27xxx_device_info *di)
+void bq27xxx_battery_update(struct bq27xxx_device_info *di)
{
struct bq27xxx_reg_cache cache = {0, };
bool has_ci_flag = di->chip == BQ27000 || di->chip == BQ27010;
@@ -722,7 +685,7 @@ static void bq27xxx_battery_update(struct bq27xxx_device_info *di)
if (cache.flags >= 0) {
cache.temperature = bq27xxx_battery_read_temperature(di);
if (has_ci_flag && (cache.flags & BQ27000_FLAG_CI)) {
- dev_info(di->dev, "battery is not calibrated! ignoring capacity values\n");
+ dev_info_once(di->dev, "battery is not calibrated! ignoring capacity values\n");
cache.capacity = -ENODATA;
cache.energy = -ENODATA;
cache.time_to_empty = -ENODATA;
@@ -761,6 +724,7 @@ static void bq27xxx_battery_update(struct bq27xxx_device_info *di)
di->last_update = jiffies;
}
+EXPORT_SYMBOL_GPL(bq27xxx_battery_update);
static void bq27xxx_battery_poll(struct work_struct *work)
{
@@ -991,32 +955,30 @@ static void bq27xxx_external_power_changed(struct power_supply *psy)
schedule_delayed_work(&di->work, 0);
}
-static int bq27xxx_powersupply_init(struct bq27xxx_device_info *di,
- const char *name)
+int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
{
- int ret;
struct power_supply_desc *psy_desc;
struct power_supply_config psy_cfg = { .drv_data = di, };
+ INIT_DELAYED_WORK(&di->work, bq27xxx_battery_poll);
+ mutex_init(&di->lock);
+ di->regs = bq27xxx_regs[di->chip];
+
psy_desc = devm_kzalloc(di->dev, sizeof(*psy_desc), GFP_KERNEL);
if (!psy_desc)
return -ENOMEM;
- psy_desc->name = name;
+ psy_desc->name = di->name;
psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
psy_desc->properties = bq27xxx_battery_props[di->chip].props;
psy_desc->num_properties = bq27xxx_battery_props[di->chip].size;
psy_desc->get_property = bq27xxx_battery_get_property;
psy_desc->external_power_changed = bq27xxx_external_power_changed;
- INIT_DELAYED_WORK(&di->work, bq27xxx_battery_poll);
- mutex_init(&di->lock);
-
di->bat = power_supply_register_no_ws(di->dev, psy_desc, &psy_cfg);
if (IS_ERR(di->bat)) {
- ret = PTR_ERR(di->bat);
- dev_err(di->dev, "failed to register battery: %d\n", ret);
- return ret;
+ dev_err(di->dev, "failed to register battery\n");
+ return PTR_ERR(di->bat);
}
dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION);
@@ -1025,8 +987,9 @@ static int bq27xxx_powersupply_init(struct bq27xxx_device_info *di,
return 0;
}
+EXPORT_SYMBOL_GPL(bq27xxx_battery_setup);
-static void bq27xxx_powersupply_unregister(struct bq27xxx_device_info *di)
+void bq27xxx_battery_teardown(struct bq27xxx_device_info *di)
{
/*
* power_supply_unregister call bq27xxx_battery_get_property which
@@ -1042,192 +1005,7 @@ static void bq27xxx_powersupply_unregister(struct bq27xxx_device_info *di)
mutex_destroy(&di->lock);
}
-
-/* i2c specific code */
-#ifdef CONFIG_BATTERY_BQ27XXX_I2C
-
-/* If the system has several batteries we need a different name for each
- * of them...
- */
-static DEFINE_IDR(battery_id);
-static DEFINE_MUTEX(battery_mutex);
-
-static irqreturn_t bq27xxx_battery_irq_handler_thread(int irq, void *data)
-{
- struct bq27xxx_device_info *di = data;
-
- bq27xxx_battery_update(di);
-
- return IRQ_HANDLED;
-}
-
-static int bq27xxx_battery_i2c_read(struct bq27xxx_device_info *di, u8 reg,
- bool single)
-{
- struct i2c_client *client = to_i2c_client(di->dev);
- struct i2c_msg msg[2];
- unsigned char data[2];
- int ret;
-
- if (!client->adapter)
- return -ENODEV;
-
- msg[0].addr = client->addr;
- msg[0].flags = 0;
- msg[0].buf = &reg;
- msg[0].len = sizeof(reg);
- msg[1].addr = client->addr;
- msg[1].flags = I2C_M_RD;
- msg[1].buf = data;
- if (single)
- msg[1].len = 1;
- else
- msg[1].len = 2;
-
- ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
- if (ret < 0)
- return ret;
-
- if (!single)
- ret = get_unaligned_le16(data);
- else
- ret = data[0];
-
- return ret;
-}
-
-static int bq27xxx_battery_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- char *name;
- struct bq27xxx_device_info *di;
- int num;
- int retval = 0;
-
- /* Get new ID for the new battery device */
- mutex_lock(&battery_mutex);
- num = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL);
- mutex_unlock(&battery_mutex);
- if (num < 0)
- return num;
-
- name = devm_kasprintf(&client->dev, GFP_KERNEL, "%s-%d", id->name, num);
- if (!name) {
- retval = -ENOMEM;
- goto batt_failed;
- }
-
- di = devm_kzalloc(&client->dev, sizeof(*di), GFP_KERNEL);
- if (!di) {
- retval = -ENOMEM;
- goto batt_failed;
- }
-
- di->id = num;
- di->dev = &client->dev;
- di->chip = id->driver_data;
- di->bus.read = &bq27xxx_battery_i2c_read;
- di->regs = bq27xxx_regs[di->chip];
-
- retval = bq27xxx_powersupply_init(di, name);
- if (retval)
- goto batt_failed;
-
- /* Schedule a polling after about 1 min */
- schedule_delayed_work(&di->work, 60 * HZ);
-
- i2c_set_clientdata(client, di);
-
- if (client->irq) {
- retval = devm_request_threaded_irq(&client->dev, client->irq,
- NULL, bq27xxx_battery_irq_handler_thread,
- IRQF_ONESHOT,
- name, di);
- if (retval) {
- dev_err(&client->dev,
- "Unable to register IRQ %d error %d\n",
- client->irq, retval);
- return retval;
- }
- }
-
- return 0;
-
-batt_failed:
- mutex_lock(&battery_mutex);
- idr_remove(&battery_id, num);
- mutex_unlock(&battery_mutex);
-
- return retval;
-}
-
-static int bq27xxx_battery_i2c_remove(struct i2c_client *client)
-{
- struct bq27xxx_device_info *di = i2c_get_clientdata(client);
-
- bq27xxx_powersupply_unregister(di);
-
- mutex_lock(&battery_mutex);
- idr_remove(&battery_id, di->id);
- mutex_unlock(&battery_mutex);
-
- return 0;
-}
-
-static const struct i2c_device_id bq27xxx_id[] = {
- { "bq27200", BQ27000 },
- { "bq27210", BQ27010 },
- { "bq27500", BQ27500 },
- { "bq27510", BQ27500 },
- { "bq27520", BQ27500 },
- { "bq27530", BQ27530 },
- { "bq27531", BQ27530 },
- { "bq27541", BQ27541 },
- { "bq27542", BQ27541 },
- { "bq27546", BQ27541 },
- { "bq27742", BQ27541 },
- { "bq27545", BQ27545 },
- { "bq27421", BQ27421 },
- { "bq27425", BQ27421 },
- { "bq27441", BQ27421 },
- { "bq27621", BQ27421 },
- {},
-};
-MODULE_DEVICE_TABLE(i2c, bq27xxx_id);
-
-static struct i2c_driver bq27xxx_battery_i2c_driver = {
- .driver = {
- .name = "bq27xxx-battery",
- },
- .probe = bq27xxx_battery_i2c_probe,
- .remove = bq27xxx_battery_i2c_remove,
- .id_table = bq27xxx_id,
-};
-
-static inline int bq27xxx_battery_i2c_init(void)
-{
- int ret = i2c_add_driver(&bq27xxx_battery_i2c_driver);
-
- if (ret)
- pr_err("Unable to register BQ27xxx i2c driver\n");
-
- return ret;
-}
-
-static inline void bq27xxx_battery_i2c_exit(void)
-{
- i2c_del_driver(&bq27xxx_battery_i2c_driver);
-}
-
-#else
-
-static inline int bq27xxx_battery_i2c_init(void) { return 0; }
-static inline void bq27xxx_battery_i2c_exit(void) {};
-
-#endif
-
-/* platform specific code */
-#ifdef CONFIG_BATTERY_BQ27XXX_PLATFORM
+EXPORT_SYMBOL_GPL(bq27xxx_battery_teardown);
static int bq27xxx_battery_platform_read(struct bq27xxx_device_info *di, u8 reg,
bool single)
@@ -1267,7 +1045,6 @@ static int bq27xxx_battery_platform_probe(struct platform_device *pdev)
{
struct bq27xxx_device_info *di;
struct bq27xxx_platform_data *pdata = pdev->dev.platform_data;
- const char *name;
if (!pdata) {
dev_err(&pdev->dev, "no platform_data supplied\n");
@@ -1292,83 +1069,36 @@ static int bq27xxx_battery_platform_probe(struct platform_device *pdev)
di->dev = &pdev->dev;
di->chip = pdata->chip;
- di->regs = bq27xxx_regs[di->chip];
-
- name = pdata->name ?: dev_name(&pdev->dev);
- di->bus.read = &bq27xxx_battery_platform_read;
+ di->name = pdata->name ?: dev_name(&pdev->dev);
+ di->bus.read = bq27xxx_battery_platform_read;
- return bq27xxx_powersupply_init(di, name);
+ return bq27xxx_battery_setup(di);
}
static int bq27xxx_battery_platform_remove(struct platform_device *pdev)
{
struct bq27xxx_device_info *di = platform_get_drvdata(pdev);
- bq27xxx_powersupply_unregister(di);
+ bq27xxx_battery_teardown(di);
return 0;
}
+static const struct platform_device_id bq27xxx_battery_platform_id_table[] = {
+ { "bq27000-battery", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, bq27xxx_battery_platform_id_table);
+
static struct platform_driver bq27xxx_battery_platform_driver = {
.probe = bq27xxx_battery_platform_probe,
.remove = bq27xxx_battery_platform_remove,
.driver = {
.name = "bq27000-battery",
},
+ .id_table = bq27xxx_battery_platform_id_table,
};
-
-static inline int bq27xxx_battery_platform_init(void)
-{
- int ret = platform_driver_register(&bq27xxx_battery_platform_driver);
-
- if (ret)
- pr_err("Unable to register BQ27xxx platform driver\n");
-
- return ret;
-}
-
-static inline void bq27xxx_battery_platform_exit(void)
-{
- platform_driver_unregister(&bq27xxx_battery_platform_driver);
-}
-
-#else
-
-static inline int bq27xxx_battery_platform_init(void) { return 0; }
-static inline void bq27xxx_battery_platform_exit(void) {};
-
-#endif
-
-/*
- * Module stuff
- */
-
-static int __init bq27xxx_battery_init(void)
-{
- int ret;
-
- ret = bq27xxx_battery_i2c_init();
- if (ret)
- return ret;
-
- ret = bq27xxx_battery_platform_init();
- if (ret)
- bq27xxx_battery_i2c_exit();
-
- return ret;
-}
-module_init(bq27xxx_battery_init);
-
-static void __exit bq27xxx_battery_exit(void)
-{
- bq27xxx_battery_platform_exit();
- bq27xxx_battery_i2c_exit();
-}
-module_exit(bq27xxx_battery_exit);
-
-#ifdef CONFIG_BATTERY_BQ27XXX_PLATFORM
-MODULE_ALIAS("platform:bq27000-battery");
-#endif
+module_platform_driver(bq27xxx_battery_platform_driver);
MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
MODULE_DESCRIPTION("BQ27xxx battery monitor driver");
diff --git a/drivers/power/bq27xxx_battery_i2c.c b/drivers/power/bq27xxx_battery_i2c.c
new file mode 100644
index 000000000000..9429e66be096
--- /dev/null
+++ b/drivers/power/bq27xxx_battery_i2c.c
@@ -0,0 +1,150 @@
+/*
+ * SCI Reset driver for Keystone based devices
+ *
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+ * Andrew F. Davis <afd@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <asm/unaligned.h>
+
+#include <linux/power/bq27xxx_battery.h>
+
+static irqreturn_t bq27xxx_battery_irq_handler_thread(int irq, void *data)
+{
+ struct bq27xxx_device_info *di = data;
+
+ bq27xxx_battery_update(di);
+
+ return IRQ_HANDLED;
+}
+
+static int bq27xxx_battery_i2c_read(struct bq27xxx_device_info *di, u8 reg,
+ bool single)
+{
+ struct i2c_client *client = to_i2c_client(di->dev);
+ struct i2c_msg msg[2];
+ unsigned char data[2];
+ int ret;
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].buf = &reg;
+ msg[0].len = sizeof(reg);
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].buf = data;
+ if (single)
+ msg[1].len = 1;
+ else
+ msg[1].len = 2;
+
+ ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+ if (ret < 0)
+ return ret;
+
+ if (!single)
+ ret = get_unaligned_le16(data);
+ else
+ ret = data[0];
+
+ return ret;
+}
+
+static int bq27xxx_battery_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct bq27xxx_device_info *di;
+ int ret;
+
+ di = devm_kzalloc(&client->dev, sizeof(*di), GFP_KERNEL);
+ if (!di)
+ return -ENOMEM;
+
+ di->dev = &client->dev;
+ di->chip = id->driver_data;
+ di->name = id->name;
+ di->bus.read = bq27xxx_battery_i2c_read;
+
+ ret = bq27xxx_battery_setup(di);
+ if (ret)
+ return ret;
+
+ /* Schedule a polling after about 1 min */
+ schedule_delayed_work(&di->work, 60 * HZ);
+
+ i2c_set_clientdata(client, di);
+
+ if (client->irq) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, bq27xxx_battery_irq_handler_thread,
+ IRQF_ONESHOT,
+ di->name, di);
+ if (ret) {
+ dev_err(&client->dev,
+ "Unable to register IRQ %d error %d\n",
+ client->irq, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int bq27xxx_battery_i2c_remove(struct i2c_client *client)
+{
+ struct bq27xxx_device_info *di = i2c_get_clientdata(client);
+
+ bq27xxx_battery_teardown(di);
+
+ return 0;
+}
+
+static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
+ { "bq27200", BQ27000 },
+ { "bq27210", BQ27010 },
+ { "bq27500", BQ27500 },
+ { "bq27510", BQ27500 },
+ { "bq27520", BQ27500 },
+ { "bq27530", BQ27530 },
+ { "bq27531", BQ27530 },
+ { "bq27541", BQ27541 },
+ { "bq27542", BQ27541 },
+ { "bq27546", BQ27541 },
+ { "bq27742", BQ27541 },
+ { "bq27545", BQ27545 },
+ { "bq27421", BQ27421 },
+ { "bq27425", BQ27421 },
+ { "bq27441", BQ27421 },
+ { "bq27621", BQ27421 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table);
+
+static struct i2c_driver bq27xxx_battery_i2c_driver = {
+ .driver = {
+ .name = "bq27xxx-battery",
+ },
+ .probe = bq27xxx_battery_i2c_probe,
+ .remove = bq27xxx_battery_i2c_remove,
+ .id_table = bq27xxx_i2c_id_table,
+};
+module_i2c_driver(bq27xxx_battery_i2c_driver);
+
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("BQ27xxx battery monitor i2c driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/ds2782_battery.c b/drivers/power/ds2782_battery.c
index ed4d756d21e4..a1b7e0592245 100644
--- a/drivers/power/ds2782_battery.c
+++ b/drivers/power/ds2782_battery.c
@@ -59,7 +59,7 @@ struct ds278x_info {
struct i2c_client *client;
struct power_supply *battery;
struct power_supply_desc battery_desc;
- struct ds278x_battery_ops *ops;
+ const struct ds278x_battery_ops *ops;
struct delayed_work bat_work;
int id;
int rsns;
@@ -361,7 +361,7 @@ enum ds278x_num_id {
DS2786,
};
-static struct ds278x_battery_ops ds278x_ops[] = {
+static const struct ds278x_battery_ops ds278x_ops[] = {
[DS2782] = {
.get_battery_current = ds2782_get_current,
.get_battery_voltage = ds2782_get_voltage,
diff --git a/drivers/power/generic-adc-battery.c b/drivers/power/generic-adc-battery.c
index fedc5818fab7..edb36bf781b0 100644
--- a/drivers/power/generic-adc-battery.c
+++ b/drivers/power/generic-adc-battery.c
@@ -206,7 +206,7 @@ static void gab_work(struct work_struct *work)
bool is_plugged;
int status;
- delayed_work = container_of(work, struct delayed_work, work);
+ delayed_work = to_delayed_work(work);
adc_bat = container_of(delayed_work, struct gab, bat_work);
pdata = adc_bat->pdata;
status = adc_bat->status;
diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c
index f2a7d970388f..46a292aa182d 100644
--- a/drivers/power/isp1704_charger.c
+++ b/drivers/power/isp1704_charger.c
@@ -76,7 +76,7 @@ static inline int isp1704_read(struct isp1704_charger *isp, u32 reg)
return usb_phy_io_read(isp->phy, reg);
}
-static inline int isp1704_write(struct isp1704_charger *isp, u32 val, u32 reg)
+static inline int isp1704_write(struct isp1704_charger *isp, u32 reg, u32 val)
{
return usb_phy_io_write(isp->phy, val, reg);
}
diff --git a/drivers/power/max8903_charger.c b/drivers/power/max8903_charger.c
index 6d39d52040d4..17876caf31e5 100644
--- a/drivers/power/max8903_charger.c
+++ b/drivers/power/max8903_charger.c
@@ -291,10 +291,10 @@ static int max8903_probe(struct platform_device *pdev)
if (pdata->dc_valid) {
ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->dok),
- NULL, max8903_dcin,
- IRQF_TRIGGER_FALLING |
- IRQF_TRIGGER_RISING,
- "MAX8903 DC IN", data);
+ NULL, max8903_dcin,
+ IRQF_TRIGGER_FALLING |
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "MAX8903 DC IN", data);
if (ret) {
dev_err(dev, "Cannot request irq %d for DC (%d)\n",
gpio_to_irq(pdata->dok), ret);
@@ -304,10 +304,10 @@ static int max8903_probe(struct platform_device *pdev)
if (pdata->usb_valid) {
ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->uok),
- NULL, max8903_usbin,
- IRQF_TRIGGER_FALLING |
- IRQF_TRIGGER_RISING,
- "MAX8903 USB IN", data);
+ NULL, max8903_usbin,
+ IRQF_TRIGGER_FALLING |
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "MAX8903 USB IN", data);
if (ret) {
dev_err(dev, "Cannot request irq %d for USB (%d)\n",
gpio_to_irq(pdata->uok), ret);
@@ -317,10 +317,10 @@ static int max8903_probe(struct platform_device *pdev)
if (pdata->flt) {
ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->flt),
- NULL, max8903_fault,
- IRQF_TRIGGER_FALLING |
- IRQF_TRIGGER_RISING,
- "MAX8903 Fault", data);
+ NULL, max8903_fault,
+ IRQF_TRIGGER_FALLING |
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "MAX8903 Fault", data);
if (ret) {
dev_err(dev, "Cannot request irq %d for Fault (%d)\n",
gpio_to_irq(pdata->flt), ret);
diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c
index 3f6b5dd7c3d4..1b5d450586d1 100644
--- a/drivers/power/reset/at91-reset.c
+++ b/drivers/power/reset/at91-reset.c
@@ -198,6 +198,7 @@ static int __init at91_reset_probe(struct platform_device *pdev)
at91_ramc_base[idx] = of_iomap(np, 0);
if (!at91_ramc_base[idx]) {
dev_err(&pdev->dev, "Could not map ram controller address\n");
+ of_node_put(np);
return -ENODEV;
}
idx++;
diff --git a/drivers/power/test_power.c b/drivers/power/test_power.c
index 83c42ea88f2b..57246cdbd042 100644
--- a/drivers/power/test_power.c
+++ b/drivers/power/test_power.c
@@ -301,6 +301,8 @@ static int map_get_value(struct battery_property_map *map, const char *key,
buf[MAX_KEYLENGTH-1] = '\0';
cr = strnlen(buf, MAX_KEYLENGTH) - 1;
+ if (cr < 0)
+ return def_val;
if (buf[cr] == '\n')
buf[cr] = '\0';
diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c
index cc97f0869791..6c592dc71aee 100644
--- a/drivers/powercap/intel_rapl.c
+++ b/drivers/powercap/intel_rapl.c
@@ -388,7 +388,7 @@ static int get_domain_enable(struct powercap_zone *power_zone, bool *mode)
}
/* per RAPL domain ops, in the order of rapl_domain_type */
-static struct powercap_zone_ops zone_ops[] = {
+static const struct powercap_zone_ops zone_ops[] = {
/* RAPL_DOMAIN_PACKAGE */
{
.get_energy_uj = get_energy_counter,
@@ -584,7 +584,7 @@ static int get_max_power(struct powercap_zone *power_zone, int id,
return ret;
}
-static struct powercap_zone_constraint_ops constraint_ops = {
+static const struct powercap_zone_constraint_ops constraint_ops = {
.set_power_limit_uw = set_power_limit,
.get_power_limit_uw = get_current_power_limit,
.set_time_window_us = set_time_window,
@@ -988,16 +988,16 @@ static void set_floor_freq_atom(struct rapl_domain *rd, bool enable)
}
if (!power_ctrl_orig_val)
- iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_PMC_READ,
- rapl_defaults->floor_freq_reg_addr,
- &power_ctrl_orig_val);
+ iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_CR_READ,
+ 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,
- rapl_defaults->floor_freq_reg_addr, mdata);
+ iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_CR_WRITE,
+ rapl_defaults->floor_freq_reg_addr, mdata);
}
static u64 rapl_compute_time_window_core(struct rapl_package *rp, u64 value,
@@ -1341,10 +1341,13 @@ static int rapl_detect_domains(struct rapl_package *rp, int cpu)
for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++) {
/* check if the domain is locked by BIOS */
- if (rapl_read_data_raw(rd, FW_LOCK, false, &locked)) {
+ ret = rapl_read_data_raw(rd, FW_LOCK, false, &locked);
+ if (ret)
+ return ret;
+ if (locked) {
pr_info("RAPL package %d domain %s locked by BIOS\n",
rp->id, rd->name);
- rd->state |= DOMAIN_STATE_BIOS_LOCKED;
+ rd->state |= DOMAIN_STATE_BIOS_LOCKED;
}
}
diff --git a/drivers/powercap/powercap_sys.c b/drivers/powercap/powercap_sys.c
index 84419af16f77..14bde0db8c24 100644
--- a/drivers/powercap/powercap_sys.c
+++ b/drivers/powercap/powercap_sys.c
@@ -293,8 +293,8 @@ err_alloc:
}
static int create_constraints(struct powercap_zone *power_zone,
- int nr_constraints,
- struct powercap_zone_constraint_ops *const_ops)
+ int nr_constraints,
+ const struct powercap_zone_constraint_ops *const_ops)
{
int i;
int ret = 0;
@@ -492,13 +492,13 @@ static struct class powercap_class = {
};
struct powercap_zone *powercap_register_zone(
- struct powercap_zone *power_zone,
- struct powercap_control_type *control_type,
- const char *name,
- struct powercap_zone *parent,
- const struct powercap_zone_ops *ops,
- int nr_constraints,
- struct powercap_zone_constraint_ops *const_ops)
+ struct powercap_zone *power_zone,
+ struct powercap_control_type *control_type,
+ const char *name,
+ struct powercap_zone *parent,
+ const struct powercap_zone_ops *ops,
+ int nr_constraints,
+ const struct powercap_zone_constraint_ops *const_ops)
{
int result;
int nr_attrs;
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 8df0b0e62976..8155e80dd3f8 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -274,6 +274,15 @@ config REGULATOR_ISL6271A
help
This driver supports ISL6271A voltage regulator chip.
+config REGULATOR_LM363X
+ tristate "TI LM363X voltage regulators"
+ depends on MFD_TI_LMU
+ help
+ This driver supports LM3631 and LM3632 voltage regulators for
+ the LCD bias.
+ One boost output voltage is configurable and always on.
+ Other LDOs are used for the display module.
+
config REGULATOR_LP3971
tristate "National Semiconductors LP3971 PMIC regulator driver"
depends on I2C
@@ -446,6 +455,7 @@ config REGULATOR_MC13892
config REGULATOR_MT6311
tristate "MediaTek MT6311 PMIC"
depends on I2C
+ select REGMAP_I2C
help
Say y here to select this option to enable the power regulator of
MediaTek MT6311 PMIC.
@@ -504,6 +514,22 @@ config REGULATOR_PFUZE100
Say y here to support the regulators found on the Freescale
PFUZE100/PFUZE200 PMIC.
+config REGULATOR_PV88060
+ tristate "Powerventure Semiconductor PV88060 regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say y here to support the voltage regulators and convertors
+ PV88060
+
+config REGULATOR_PV88090
+ tristate "Powerventure Semiconductor PV88090 regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say y here to support the voltage regulators and convertors
+ on PV88090
+
config REGULATOR_PWM
tristate "PWM voltage regulator"
depends on PWM
@@ -588,10 +614,10 @@ config REGULATOR_S2MPA01
via I2C bus. S2MPA01 has 10 Bucks and 26 LDO outputs.
config REGULATOR_S2MPS11
- tristate "Samsung S2MPS11/S2MPS13/S2MPS14/S2MPU02 voltage regulator"
+ tristate "Samsung S2MPS11/13/14/15/S2MPU02 voltage regulator"
depends on MFD_SEC_CORE
help
- This driver supports a Samsung S2MPS11/S2MPS13/S2MPS14/S2MPU02 voltage
+ This driver supports a Samsung S2MPS11/13/14/15/S2MPU02 voltage
output regulator via I2C bus. The chip is comprised of high efficient
Buck converters including Dual-Phase Buck converter, Buck-Boost
converter, various LDOs.
@@ -680,6 +706,13 @@ config REGULATOR_TPS6507X
three step-down converters and two general-purpose LDO voltage regulators.
It supports TI's software based Class-2 SmartReflex implementation.
+config REGULATOR_TPS65086
+ tristate "TI TPS65086 Power regulators"
+ depends on MFD_TPS65086
+ help
+ This driver provides support for the voltage regulators on
+ TI TPS65086 PMICs.
+
config REGULATOR_TPS65090
tristate "TI TPS65090 Power regulator"
depends on MFD_TPS65090
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 0f8174913c17..980b1943fa81 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o
obj-$(CONFIG_REGULATOR_HI6421) += hi6421-regulator.o
obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
obj-$(CONFIG_REGULATOR_ISL9305) += isl9305.o
+obj-$(CONFIG_REGULATOR_LM363X) += lm363x-regulator.o
obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
obj-$(CONFIG_REGULATOR_LP3972) += lp3972.o
obj-$(CONFIG_REGULATOR_LP872X) += lp872x.o
@@ -66,6 +67,8 @@ obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-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_PV88060) += pv88060-regulator.o
+obj-$(CONFIG_REGULATOR_PV88090) += pv88090-regulator.o
obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o
obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o
obj-$(CONFIG_REGULATOR_PBIAS) += pbias-regulator.o
@@ -85,6 +88,7 @@ obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o
obj-$(CONFIG_REGULATOR_TPS62360) += tps62360-regulator.o
obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o
obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o
+obj-$(CONFIG_REGULATOR_TPS65086) += tps65086-regulator.o
obj-$(CONFIG_REGULATOR_TPS65090) += tps65090-regulator.o
obj-$(CONFIG_REGULATOR_TPS65217) += tps65217-regulator.o
obj-$(CONFIG_REGULATOR_TPS65218) += tps65218-regulator.o
diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c
index 35de22fdb7a0..f2e1a39ce0f3 100644
--- a/drivers/regulator/axp20x-regulator.c
+++ b/drivers/regulator/axp20x-regulator.c
@@ -27,8 +27,8 @@
#define AXP20X_IO_ENABLED 0x03
#define AXP20X_IO_DISABLED 0x07
-#define AXP22X_IO_ENABLED 0x04
-#define AXP22X_IO_DISABLED 0x03
+#define AXP22X_IO_ENABLED 0x03
+#define AXP22X_IO_DISABLED 0x04
#define AXP20X_WORKMODE_DCDC2_MASK BIT(2)
#define AXP20X_WORKMODE_DCDC3_MASK BIT(1)
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 73b7683355cd..744c9889f88d 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -132,24 +132,24 @@ static bool have_full_constraints(void)
return has_full_constraints || of_have_populated_dt();
}
+static inline struct regulator_dev *rdev_get_supply(struct regulator_dev *rdev)
+{
+ if (rdev && rdev->supply)
+ return rdev->supply->rdev;
+
+ return NULL;
+}
+
/**
* regulator_lock_supply - lock a regulator and its supplies
* @rdev: regulator source
*/
static void regulator_lock_supply(struct regulator_dev *rdev)
{
- struct regulator *supply;
- int i = 0;
-
- while (1) {
- mutex_lock_nested(&rdev->mutex, i++);
- supply = rdev->supply;
-
- if (!rdev->supply)
- return;
+ int i;
- rdev = supply->rdev;
- }
+ for (i = 0; rdev; rdev = rdev_get_supply(rdev), i++)
+ mutex_lock_nested(&rdev->mutex, i);
}
/**
@@ -2368,7 +2368,6 @@ static void regulator_disable_work(struct work_struct *work)
int regulator_disable_deferred(struct regulator *regulator, int ms)
{
struct regulator_dev *rdev = regulator->rdev;
- int ret;
if (regulator->always_on)
return 0;
@@ -2380,13 +2379,9 @@ int regulator_disable_deferred(struct regulator *regulator, int ms)
rdev->deferred_disables++;
mutex_unlock(&rdev->mutex);
- ret = queue_delayed_work(system_power_efficient_wq,
- &rdev->disable_work,
- msecs_to_jiffies(ms));
- if (ret < 0)
- return ret;
- else
- return 0;
+ queue_delayed_work(system_power_efficient_wq, &rdev->disable_work,
+ msecs_to_jiffies(ms));
+ return 0;
}
EXPORT_SYMBOL_GPL(regulator_disable_deferred);
@@ -3451,8 +3446,10 @@ int regulator_bulk_get(struct device *dev, int num_consumers,
consumers[i].consumer = NULL;
for (i = 0; i < num_consumers; i++) {
- consumers[i].consumer = regulator_get(dev,
- consumers[i].supply);
+ consumers[i].consumer = _regulator_get(dev,
+ consumers[i].supply,
+ false,
+ !consumers[i].optional);
if (IS_ERR(consumers[i].consumer)) {
ret = PTR_ERR(consumers[i].consumer);
dev_err(dev, "Failed to get supply '%s': %d\n",
@@ -3708,7 +3705,7 @@ static umode_t regulator_attr_is_visible(struct kobject *kobj,
struct attribute *attr, int idx)
{
struct device *dev = kobj_to_dev(kobj);
- struct regulator_dev *rdev = container_of(dev, struct regulator_dev, dev);
+ struct regulator_dev *rdev = dev_to_rdev(dev);
const struct regulator_ops *ops = rdev->desc->ops;
umode_t mode = attr->mode;
diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c
index affa1b191314..33e8f3b8d2bd 100644
--- a/drivers/regulator/da903x.c
+++ b/drivers/regulator/da903x.c
@@ -257,7 +257,7 @@ static const struct regulator_linear_range da9034_ldo12_ranges[] = {
REGULATOR_LINEAR_RANGE(2700000, 8, 15, 50000),
};
-static struct regulator_ops da903x_regulator_ldo_ops = {
+static const struct regulator_ops da903x_regulator_ldo_ops = {
.set_voltage_sel = da903x_set_voltage_sel,
.get_voltage_sel = da903x_get_voltage_sel,
.list_voltage = regulator_list_voltage_linear,
@@ -268,7 +268,7 @@ static struct regulator_ops da903x_regulator_ldo_ops = {
};
/* NOTE: this is dedicated for the insane DA9030 LDO14 */
-static struct regulator_ops da9030_regulator_ldo14_ops = {
+static const struct regulator_ops da9030_regulator_ldo14_ops = {
.set_voltage_sel = da903x_set_voltage_sel,
.get_voltage_sel = da903x_get_voltage_sel,
.list_voltage = da9030_list_ldo14_voltage,
@@ -279,7 +279,7 @@ static struct regulator_ops da9030_regulator_ldo14_ops = {
};
/* NOTE: this is dedicated for the DA9030 LDO1 and LDO15 that have locks */
-static struct regulator_ops da9030_regulator_ldo1_15_ops = {
+static const struct regulator_ops da9030_regulator_ldo1_15_ops = {
.set_voltage_sel = da9030_set_ldo1_15_voltage_sel,
.get_voltage_sel = da903x_get_voltage_sel,
.list_voltage = regulator_list_voltage_linear,
@@ -289,7 +289,7 @@ static struct regulator_ops da9030_regulator_ldo1_15_ops = {
.is_enabled = da903x_is_enabled,
};
-static struct regulator_ops da9034_regulator_dvc_ops = {
+static const struct regulator_ops da9034_regulator_dvc_ops = {
.set_voltage_sel = da9034_set_dvc_voltage_sel,
.get_voltage_sel = da903x_get_voltage_sel,
.list_voltage = regulator_list_voltage_linear,
@@ -300,7 +300,7 @@ static struct regulator_ops da9034_regulator_dvc_ops = {
};
/* NOTE: this is dedicated for the insane LDO12 */
-static struct regulator_ops da9034_regulator_ldo12_ops = {
+static const struct regulator_ops da9034_regulator_ldo12_ops = {
.set_voltage_sel = da903x_set_voltage_sel,
.get_voltage_sel = da903x_get_voltage_sel,
.list_voltage = regulator_list_voltage_linear_range,
diff --git a/drivers/regulator/da9052-regulator.c b/drivers/regulator/da9052-regulator.c
index 12a25b40e473..1050cb77561a 100644
--- a/drivers/regulator/da9052-regulator.c
+++ b/drivers/regulator/da9052-regulator.c
@@ -265,7 +265,7 @@ static int da9052_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
return ret;
}
-static struct regulator_ops da9052_dcdc_ops = {
+static const struct regulator_ops da9052_dcdc_ops = {
.get_current_limit = da9052_dcdc_get_current_limit,
.set_current_limit = da9052_dcdc_set_current_limit,
@@ -279,7 +279,7 @@ static struct regulator_ops da9052_dcdc_ops = {
.disable = regulator_disable_regmap,
};
-static struct regulator_ops da9052_ldo_ops = {
+static const struct regulator_ops da9052_ldo_ops = {
.list_voltage = da9052_list_voltage,
.map_voltage = da9052_map_voltage,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
diff --git a/drivers/regulator/da9055-regulator.c b/drivers/regulator/da9055-regulator.c
index cafdafbffcaf..d029c941a1e1 100644
--- a/drivers/regulator/da9055-regulator.c
+++ b/drivers/regulator/da9055-regulator.c
@@ -324,7 +324,7 @@ static int da9055_suspend_disable(struct regulator_dev *rdev)
return 0;
}
-static struct regulator_ops da9055_buck_ops = {
+static const struct regulator_ops da9055_buck_ops = {
.get_mode = da9055_buck_get_mode,
.set_mode = da9055_buck_set_mode,
@@ -345,7 +345,7 @@ static struct regulator_ops da9055_buck_ops = {
.set_suspend_mode = da9055_buck_set_mode,
};
-static struct regulator_ops da9055_ldo_ops = {
+static const struct regulator_ops da9055_ldo_ops = {
.get_mode = da9055_ldo_get_mode,
.set_mode = da9055_ldo_set_mode,
diff --git a/drivers/regulator/da9062-regulator.c b/drivers/regulator/da9062-regulator.c
index 5638fe8d759d..0638c8b40521 100644
--- a/drivers/regulator/da9062-regulator.c
+++ b/drivers/regulator/da9062-regulator.c
@@ -371,7 +371,7 @@ static int da9062_ldo_set_suspend_mode(struct regulator_dev *rdev,
return regmap_field_write(regl->suspend_sleep, val);
}
-static struct regulator_ops da9062_buck_ops = {
+static const struct regulator_ops da9062_buck_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
@@ -389,7 +389,7 @@ static struct regulator_ops da9062_buck_ops = {
.set_suspend_mode = da9062_buck_set_suspend_mode,
};
-static struct regulator_ops da9062_ldo_ops = {
+static const struct regulator_ops da9062_ldo_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
diff --git a/drivers/regulator/da9063-regulator.c b/drivers/regulator/da9063-regulator.c
index 536e931eb921..ed9e7e96f877 100644
--- a/drivers/regulator/da9063-regulator.c
+++ b/drivers/regulator/da9063-regulator.c
@@ -427,7 +427,7 @@ static int da9063_ldo_set_suspend_mode(struct regulator_dev *rdev, unsigned mode
return regmap_field_write(regl->suspend_sleep, val);
}
-static struct regulator_ops da9063_buck_ops = {
+static const struct regulator_ops da9063_buck_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
@@ -445,7 +445,7 @@ static struct regulator_ops da9063_buck_ops = {
.set_suspend_mode = da9063_buck_set_suspend_mode,
};
-static struct regulator_ops da9063_ldo_ops = {
+static const struct regulator_ops da9063_ldo_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
diff --git a/drivers/regulator/da9210-regulator.c b/drivers/regulator/da9210-regulator.c
index b3517830edb6..8b3cc9f0cd64 100644
--- a/drivers/regulator/da9210-regulator.c
+++ b/drivers/regulator/da9210-regulator.c
@@ -46,7 +46,7 @@ static int da9210_set_current_limit(struct regulator_dev *rdev, int min_uA,
int max_uA);
static int da9210_get_current_limit(struct regulator_dev *rdev);
-static struct regulator_ops da9210_buck_ops = {
+static const struct regulator_ops da9210_buck_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
diff --git a/drivers/regulator/da9211-regulator.c b/drivers/regulator/da9211-regulator.c
index 04ef65b7eb3d..236abf473db5 100644
--- a/drivers/regulator/da9211-regulator.c
+++ b/drivers/regulator/da9211-regulator.c
@@ -219,7 +219,7 @@ static int da9211_get_current_limit(struct regulator_dev *rdev)
return current_limits[data];
}
-static struct regulator_ops da9211_buck_ops = {
+static const struct regulator_ops da9211_buck_ops = {
.get_mode = da9211_buck_get_mode,
.set_mode = da9211_buck_set_mode,
.enable = regulator_enable_regmap,
diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c
index 6ec1d400adae..6ad8ab4c578d 100644
--- a/drivers/regulator/devres.c
+++ b/drivers/regulator/devres.c
@@ -164,8 +164,11 @@ int devm_regulator_bulk_get(struct device *dev, int num_consumers,
consumers[i].consumer = NULL;
for (i = 0; i < num_consumers; i++) {
- consumers[i].consumer = devm_regulator_get(dev,
- consumers[i].supply);
+ consumers[i].consumer = _devm_regulator_get(dev,
+ consumers[i].supply,
+ consumers[i].optional ?
+ OPTIONAL_GET :
+ NORMAL_GET);
if (IS_ERR(consumers[i].consumer)) {
ret = PTR_ERR(consumers[i].consumer);
dev_err(dev, "Failed to get supply '%s': %d\n",
diff --git a/drivers/regulator/lm363x-regulator.c b/drivers/regulator/lm363x-regulator.c
new file mode 100644
index 000000000000..f53e63301a20
--- /dev/null
+++ b/drivers/regulator/lm363x-regulator.c
@@ -0,0 +1,291 @@
+/*
+ * TI LM363X Regulator Driver
+ *
+ * Copyright 2015 Texas Instruments
+ *
+ * Author: Milo Kim <milo.kim@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/mfd/ti-lmu.h>
+#include <linux/mfd/ti-lmu-register.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
+
+/* LM3631 */
+#define LM3631_BOOST_VSEL_MAX 0x25
+#define LM3631_LDO_VSEL_MAX 0x28
+#define LM3631_CONT_VSEL_MAX 0x03
+#define LM3631_VBOOST_MIN 4500000
+#define LM3631_VCONT_MIN 1800000
+#define LM3631_VLDO_MIN 4000000
+#define ENABLE_TIME_USEC 1000
+
+/* LM3632 */
+#define LM3632_BOOST_VSEL_MAX 0x26
+#define LM3632_LDO_VSEL_MAX 0x29
+#define LM3632_VBOOST_MIN 4500000
+#define LM3632_VLDO_MIN 4000000
+
+/* Common */
+#define LM363X_STEP_50mV 50000
+#define LM363X_STEP_500mV 500000
+
+static const int ldo_cont_enable_time[] = {
+ 0, 2000, 5000, 10000, 20000, 50000, 100000, 200000,
+};
+
+static int lm363x_regulator_enable_time(struct regulator_dev *rdev)
+{
+ enum lm363x_regulator_id id = rdev_get_id(rdev);
+ u8 val, addr, mask;
+
+ switch (id) {
+ case LM3631_LDO_CONT:
+ addr = LM3631_REG_ENTIME_VCONT;
+ mask = LM3631_ENTIME_CONT_MASK;
+ break;
+ case LM3631_LDO_OREF:
+ addr = LM3631_REG_ENTIME_VOREF;
+ mask = LM3631_ENTIME_MASK;
+ break;
+ case LM3631_LDO_POS:
+ addr = LM3631_REG_ENTIME_VPOS;
+ mask = LM3631_ENTIME_MASK;
+ break;
+ case LM3631_LDO_NEG:
+ addr = LM3631_REG_ENTIME_VNEG;
+ mask = LM3631_ENTIME_MASK;
+ break;
+ default:
+ return 0;
+ }
+
+ if (regmap_read(rdev->regmap, addr, (unsigned int *)&val))
+ return -EINVAL;
+
+ val = (val & mask) >> LM3631_ENTIME_SHIFT;
+
+ if (id == LM3631_LDO_CONT)
+ return ldo_cont_enable_time[val];
+ else
+ return ENABLE_TIME_USEC * val;
+}
+
+static struct regulator_ops lm363x_boost_voltage_table_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+static struct regulator_ops lm363x_regulator_voltage_table_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable_time = lm363x_regulator_enable_time,
+};
+
+static const struct regulator_desc lm363x_regulator_desc[] = {
+ /* LM3631 */
+ {
+ .name = "vboost",
+ .of_match = "vboost",
+ .id = LM3631_BOOST,
+ .ops = &lm363x_boost_voltage_table_ops,
+ .n_voltages = LM3631_BOOST_VSEL_MAX + 1,
+ .min_uV = LM3631_VBOOST_MIN,
+ .uV_step = LM363X_STEP_50mV,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3631_REG_VOUT_BOOST,
+ .vsel_mask = LM3631_VOUT_MASK,
+ },
+ {
+ .name = "ldo_cont",
+ .of_match = "vcont",
+ .id = LM3631_LDO_CONT,
+ .ops = &lm363x_regulator_voltage_table_ops,
+ .n_voltages = LM3631_CONT_VSEL_MAX + 1,
+ .min_uV = LM3631_VCONT_MIN,
+ .uV_step = LM363X_STEP_500mV,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3631_REG_VOUT_CONT,
+ .vsel_mask = LM3631_VOUT_CONT_MASK,
+ .enable_reg = LM3631_REG_LDO_CTRL2,
+ .enable_mask = LM3631_EN_CONT_MASK,
+ },
+ {
+ .name = "ldo_oref",
+ .of_match = "voref",
+ .id = LM3631_LDO_OREF,
+ .ops = &lm363x_regulator_voltage_table_ops,
+ .n_voltages = LM3631_LDO_VSEL_MAX + 1,
+ .min_uV = LM3631_VLDO_MIN,
+ .uV_step = LM363X_STEP_50mV,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3631_REG_VOUT_OREF,
+ .vsel_mask = LM3631_VOUT_MASK,
+ .enable_reg = LM3631_REG_LDO_CTRL1,
+ .enable_mask = LM3631_EN_OREF_MASK,
+ },
+ {
+ .name = "ldo_vpos",
+ .of_match = "vpos",
+ .id = LM3631_LDO_POS,
+ .ops = &lm363x_regulator_voltage_table_ops,
+ .n_voltages = LM3631_LDO_VSEL_MAX + 1,
+ .min_uV = LM3631_VLDO_MIN,
+ .uV_step = LM363X_STEP_50mV,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3631_REG_VOUT_POS,
+ .vsel_mask = LM3631_VOUT_MASK,
+ .enable_reg = LM3631_REG_LDO_CTRL1,
+ .enable_mask = LM3631_EN_VPOS_MASK,
+ },
+ {
+ .name = "ldo_vneg",
+ .of_match = "vneg",
+ .id = LM3631_LDO_NEG,
+ .ops = &lm363x_regulator_voltage_table_ops,
+ .n_voltages = LM3631_LDO_VSEL_MAX + 1,
+ .min_uV = LM3631_VLDO_MIN,
+ .uV_step = LM363X_STEP_50mV,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3631_REG_VOUT_NEG,
+ .vsel_mask = LM3631_VOUT_MASK,
+ .enable_reg = LM3631_REG_LDO_CTRL1,
+ .enable_mask = LM3631_EN_VNEG_MASK,
+ },
+ /* LM3632 */
+ {
+ .name = "vboost",
+ .of_match = "vboost",
+ .id = LM3632_BOOST,
+ .ops = &lm363x_boost_voltage_table_ops,
+ .n_voltages = LM3632_BOOST_VSEL_MAX + 1,
+ .min_uV = LM3632_VBOOST_MIN,
+ .uV_step = LM363X_STEP_50mV,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3632_REG_VOUT_BOOST,
+ .vsel_mask = LM3632_VOUT_MASK,
+ },
+ {
+ .name = "ldo_vpos",
+ .of_match = "vpos",
+ .id = LM3632_LDO_POS,
+ .ops = &lm363x_regulator_voltage_table_ops,
+ .n_voltages = LM3632_LDO_VSEL_MAX + 1,
+ .min_uV = LM3632_VLDO_MIN,
+ .uV_step = LM363X_STEP_50mV,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3632_REG_VOUT_POS,
+ .vsel_mask = LM3632_VOUT_MASK,
+ .enable_reg = LM3632_REG_BIAS_CONFIG,
+ .enable_mask = LM3632_EN_VPOS_MASK,
+ },
+ {
+ .name = "ldo_vneg",
+ .of_match = "vneg",
+ .id = LM3632_LDO_NEG,
+ .ops = &lm363x_regulator_voltage_table_ops,
+ .n_voltages = LM3632_LDO_VSEL_MAX + 1,
+ .min_uV = LM3632_VLDO_MIN,
+ .uV_step = LM363X_STEP_50mV,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .vsel_reg = LM3632_REG_VOUT_NEG,
+ .vsel_mask = LM3632_VOUT_MASK,
+ .enable_reg = LM3632_REG_BIAS_CONFIG,
+ .enable_mask = LM3632_EN_VNEG_MASK,
+ },
+};
+
+static int lm363x_regulator_of_get_enable_gpio(struct device_node *np, int id)
+{
+ /*
+ * Check LCM_EN1/2_GPIO is configured.
+ * Those pins are used for enabling VPOS/VNEG LDOs.
+ */
+ switch (id) {
+ case LM3632_LDO_POS:
+ return of_get_named_gpio(np, "ti,lcm-en1-gpio", 0);
+ case LM3632_LDO_NEG:
+ return of_get_named_gpio(np, "ti,lcm-en2-gpio", 0);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int lm363x_regulator_probe(struct platform_device *pdev)
+{
+ struct ti_lmu *lmu = dev_get_drvdata(pdev->dev.parent);
+ struct regmap *regmap = lmu->regmap;
+ struct regulator_config cfg = { };
+ struct regulator_dev *rdev;
+ struct device *dev = &pdev->dev;
+ int id = pdev->id;
+ int ret, ena_gpio;
+
+ cfg.dev = dev;
+ cfg.regmap = regmap;
+
+ /*
+ * LM3632 LDOs can be controlled by external pin.
+ * Register update is required if the pin is used.
+ */
+ ena_gpio = lm363x_regulator_of_get_enable_gpio(dev->of_node, id);
+ if (gpio_is_valid(ena_gpio)) {
+ cfg.ena_gpio = ena_gpio;
+ cfg.ena_gpio_flags = GPIOF_OUT_INIT_LOW;
+
+ ret = regmap_update_bits(regmap, LM3632_REG_BIAS_CONFIG,
+ LM3632_EXT_EN_MASK,
+ LM3632_EXT_EN_MASK);
+ if (ret) {
+ dev_err(dev, "External pin err: %d\n", ret);
+ return ret;
+ }
+ }
+
+ rdev = devm_regulator_register(dev, &lm363x_regulator_desc[id], &cfg);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(dev, "[%d] regulator register err: %d\n", id, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct platform_driver lm363x_regulator_driver = {
+ .probe = lm363x_regulator_probe,
+ .driver = {
+ .name = "lm363x-regulator",
+ },
+};
+
+module_platform_driver(lm363x_regulator_driver);
+
+MODULE_DESCRIPTION("TI LM363X Regulator Driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:lm363x-regulator");
diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c
index e5af07208f9d..19d758486553 100644
--- a/drivers/regulator/lp872x.c
+++ b/drivers/regulator/lp872x.c
@@ -108,7 +108,6 @@ struct lp872x {
struct lp872x_platform_data *pdata;
int num_regulators;
enum lp872x_dvs_state dvs_pin;
- int dvs_gpio;
};
/* LP8720/LP8725 shared voltage table for LDOs */
@@ -520,6 +519,7 @@ static struct regulator_ops lp8725_buck_ops = {
static struct regulator_desc lp8720_regulator_desc[] = {
{
.name = "ldo1",
+ .of_match = of_match_ptr("ldo1"),
.id = LP8720_ID_LDO1,
.ops = &lp872x_ldo_ops,
.n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl),
@@ -533,6 +533,7 @@ static struct regulator_desc lp8720_regulator_desc[] = {
},
{
.name = "ldo2",
+ .of_match = of_match_ptr("ldo2"),
.id = LP8720_ID_LDO2,
.ops = &lp872x_ldo_ops,
.n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl),
@@ -546,6 +547,7 @@ static struct regulator_desc lp8720_regulator_desc[] = {
},
{
.name = "ldo3",
+ .of_match = of_match_ptr("ldo3"),
.id = LP8720_ID_LDO3,
.ops = &lp872x_ldo_ops,
.n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl),
@@ -559,6 +561,7 @@ static struct regulator_desc lp8720_regulator_desc[] = {
},
{
.name = "ldo4",
+ .of_match = of_match_ptr("ldo4"),
.id = LP8720_ID_LDO4,
.ops = &lp872x_ldo_ops,
.n_voltages = ARRAY_SIZE(lp8720_ldo4_vtbl),
@@ -572,6 +575,7 @@ static struct regulator_desc lp8720_regulator_desc[] = {
},
{
.name = "ldo5",
+ .of_match = of_match_ptr("ldo5"),
.id = LP8720_ID_LDO5,
.ops = &lp872x_ldo_ops,
.n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl),
@@ -585,6 +589,7 @@ static struct regulator_desc lp8720_regulator_desc[] = {
},
{
.name = "buck",
+ .of_match = of_match_ptr("buck"),
.id = LP8720_ID_BUCK,
.ops = &lp8720_buck_ops,
.n_voltages = ARRAY_SIZE(lp8720_buck_vtbl),
@@ -599,6 +604,7 @@ static struct regulator_desc lp8720_regulator_desc[] = {
static struct regulator_desc lp8725_regulator_desc[] = {
{
.name = "ldo1",
+ .of_match = of_match_ptr("ldo1"),
.id = LP8725_ID_LDO1,
.ops = &lp872x_ldo_ops,
.n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl),
@@ -612,6 +618,7 @@ static struct regulator_desc lp8725_regulator_desc[] = {
},
{
.name = "ldo2",
+ .of_match = of_match_ptr("ldo2"),
.id = LP8725_ID_LDO2,
.ops = &lp872x_ldo_ops,
.n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl),
@@ -625,6 +632,7 @@ static struct regulator_desc lp8725_regulator_desc[] = {
},
{
.name = "ldo3",
+ .of_match = of_match_ptr("ldo3"),
.id = LP8725_ID_LDO3,
.ops = &lp872x_ldo_ops,
.n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl),
@@ -638,6 +646,7 @@ static struct regulator_desc lp8725_regulator_desc[] = {
},
{
.name = "ldo4",
+ .of_match = of_match_ptr("ldo4"),
.id = LP8725_ID_LDO4,
.ops = &lp872x_ldo_ops,
.n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl),
@@ -651,6 +660,7 @@ static struct regulator_desc lp8725_regulator_desc[] = {
},
{
.name = "ldo5",
+ .of_match = of_match_ptr("ldo5"),
.id = LP8725_ID_LDO5,
.ops = &lp872x_ldo_ops,
.n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl),
@@ -664,6 +674,7 @@ static struct regulator_desc lp8725_regulator_desc[] = {
},
{
.name = "lilo1",
+ .of_match = of_match_ptr("lilo1"),
.id = LP8725_ID_LILO1,
.ops = &lp872x_ldo_ops,
.n_voltages = ARRAY_SIZE(lp8725_lilo_vtbl),
@@ -677,6 +688,7 @@ static struct regulator_desc lp8725_regulator_desc[] = {
},
{
.name = "lilo2",
+ .of_match = of_match_ptr("lilo2"),
.id = LP8725_ID_LILO2,
.ops = &lp872x_ldo_ops,
.n_voltages = ARRAY_SIZE(lp8725_lilo_vtbl),
@@ -690,6 +702,7 @@ static struct regulator_desc lp8725_regulator_desc[] = {
},
{
.name = "buck1",
+ .of_match = of_match_ptr("buck1"),
.id = LP8725_ID_BUCK1,
.ops = &lp8725_buck_ops,
.n_voltages = ARRAY_SIZE(lp8725_buck_vtbl),
@@ -701,6 +714,7 @@ static struct regulator_desc lp8725_regulator_desc[] = {
},
{
.name = "buck2",
+ .of_match = of_match_ptr("buck2"),
.id = LP8725_ID_BUCK2,
.ops = &lp8725_buck_ops,
.n_voltages = ARRAY_SIZE(lp8725_buck_vtbl),
@@ -737,7 +751,6 @@ static int lp872x_init_dvs(struct lp872x *lp)
}
lp->dvs_pin = pinstate;
- lp->dvs_gpio = gpio;
return 0;
diff --git a/drivers/regulator/lp8788-buck.c b/drivers/regulator/lp8788-buck.c
index a97bed90d39b..ec46290b647e 100644
--- a/drivers/regulator/lp8788-buck.c
+++ b/drivers/regulator/lp8788-buck.c
@@ -344,7 +344,7 @@ static unsigned int lp8788_buck_get_mode(struct regulator_dev *rdev)
REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL;
}
-static struct regulator_ops lp8788_buck12_ops = {
+static const struct regulator_ops lp8788_buck12_ops = {
.list_voltage = regulator_list_voltage_table,
.map_voltage = regulator_map_voltage_ascend,
.set_voltage_sel = lp8788_buck12_set_voltage_sel,
@@ -357,7 +357,7 @@ static struct regulator_ops lp8788_buck12_ops = {
.get_mode = lp8788_buck_get_mode,
};
-static struct regulator_ops lp8788_buck34_ops = {
+static const struct regulator_ops lp8788_buck34_ops = {
.list_voltage = regulator_list_voltage_table,
.map_voltage = regulator_map_voltage_ascend,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
diff --git a/drivers/regulator/lp8788-ldo.c b/drivers/regulator/lp8788-ldo.c
index 9f22d079c8cc..cbfd35873575 100644
--- a/drivers/regulator/lp8788-ldo.c
+++ b/drivers/regulator/lp8788-ldo.c
@@ -170,7 +170,7 @@ static int lp8788_ldo_enable_time(struct regulator_dev *rdev)
return ENABLE_TIME_USEC * val;
}
-static struct regulator_ops lp8788_ldo_voltage_table_ops = {
+static const struct regulator_ops lp8788_ldo_voltage_table_ops = {
.list_voltage = regulator_list_voltage_table,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
@@ -180,7 +180,7 @@ static struct regulator_ops lp8788_ldo_voltage_table_ops = {
.enable_time = lp8788_ldo_enable_time,
};
-static struct regulator_ops lp8788_ldo_voltage_fixed_ops = {
+static const struct regulator_ops lp8788_ldo_voltage_fixed_ops = {
.list_voltage = regulator_list_voltage_linear,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
@@ -613,22 +613,20 @@ static struct platform_driver lp8788_aldo_driver = {
},
};
+static struct platform_driver * const drivers[] = {
+ &lp8788_dldo_driver,
+ &lp8788_aldo_driver,
+};
+
static int __init lp8788_ldo_init(void)
{
- int ret;
-
- ret = platform_driver_register(&lp8788_dldo_driver);
- if (ret)
- return ret;
-
- return platform_driver_register(&lp8788_aldo_driver);
+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
}
subsys_initcall(lp8788_ldo_init);
static void __exit lp8788_ldo_exit(void)
{
- platform_driver_unregister(&lp8788_aldo_driver);
- platform_driver_unregister(&lp8788_dldo_driver);
+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
}
module_exit(lp8788_ldo_exit);
diff --git a/drivers/regulator/mt6311-regulator.c b/drivers/regulator/mt6311-regulator.c
index 02c4e5feca8e..0495716fd35f 100644
--- a/drivers/regulator/mt6311-regulator.c
+++ b/drivers/regulator/mt6311-regulator.c
@@ -30,6 +30,7 @@ static const struct regmap_config mt6311_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MT6311_FQMTR_CON4,
+ .cache_type = REGCACHE_RBTREE,
};
/* Default limits measured in millivolts and milliamps */
diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c
index 8217613807d3..6efc7ee8aea3 100644
--- a/drivers/regulator/palmas-regulator.c
+++ b/drivers/regulator/palmas-regulator.c
@@ -612,6 +612,18 @@ static struct regulator_ops palmas_ops_ldo = {
.map_voltage = regulator_map_voltage_linear,
};
+static struct regulator_ops palmas_ops_ldo9 = {
+ .is_enabled = palmas_is_enabled_ldo,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .set_bypass = regulator_set_bypass_regmap,
+ .get_bypass = regulator_get_bypass_regmap,
+};
+
static struct regulator_ops palmas_ops_ext_control_ldo = {
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
@@ -639,6 +651,19 @@ static struct regulator_ops tps65917_ops_ldo = {
.set_voltage_time_sel = regulator_set_voltage_time_sel,
};
+static struct regulator_ops tps65917_ops_ldo_1_2 = {
+ .is_enabled = palmas_is_enabled_ldo,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_bypass = regulator_set_bypass_regmap,
+ .get_bypass = regulator_get_bypass_regmap,
+};
+
static int palmas_regulator_config_external(struct palmas *palmas, int id,
struct palmas_reg_init *reg_init)
{
@@ -915,6 +940,13 @@ static int palmas_ldo_registration(struct palmas_pmic *pmic,
if (pdata && pdata->ldo6_vibrator &&
(id == PALMAS_REG_LDO6))
desc->enable_time = 2000;
+
+ if (id == PALMAS_REG_LDO9) {
+ desc->ops = &palmas_ops_ldo9;
+ desc->bypass_reg = desc->enable_reg;
+ desc->bypass_mask =
+ PALMAS_LDO9_CTRL_LDO_BYPASS_EN;
+ }
} else {
if (!ddata->has_regen3 && id == PALMAS_REG_REGEN3)
continue;
@@ -1019,6 +1051,13 @@ static int tps65917_ldo_registration(struct palmas_pmic *pmic,
* It is of the order of ~60mV/uS.
*/
desc->ramp_delay = 2500;
+ if (id == TPS65917_REG_LDO1 ||
+ id == TPS65917_REG_LDO2) {
+ desc->ops = &tps65917_ops_ldo_1_2;
+ desc->bypass_reg = desc->enable_reg;
+ desc->bypass_mask =
+ TPS65917_LDO1_CTRL_BYPASS_EN;
+ }
} else {
desc->n_voltages = 1;
if (reg_init && reg_init->roof_floor)
diff --git a/drivers/regulator/pv88060-regulator.c b/drivers/regulator/pv88060-regulator.c
new file mode 100644
index 000000000000..094376c8de4b
--- /dev/null
+++ b/drivers/regulator/pv88060-regulator.c
@@ -0,0 +1,437 @@
+/*
+ * pv88060-regulator.c - Regulator device driver for PV88060
+ * Copyright (C) 2015 Powerventure 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/err.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regmap.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/proc_fs.h>
+#include <linux/uaccess.h>
+#include "pv88060-regulator.h"
+
+#define PV88060_MAX_REGULATORS 14
+
+/* PV88060 REGULATOR IDs */
+enum {
+ /* BUCKs */
+ PV88060_ID_BUCK1,
+
+ /* LDOs */
+ PV88060_ID_LDO1,
+ PV88060_ID_LDO2,
+ PV88060_ID_LDO3,
+ PV88060_ID_LDO4,
+ PV88060_ID_LDO5,
+ PV88060_ID_LDO6,
+ PV88060_ID_LDO7,
+
+ /* SWTs */
+ PV88060_ID_SW1,
+ PV88060_ID_SW2,
+ PV88060_ID_SW3,
+ PV88060_ID_SW4,
+ PV88060_ID_SW5,
+ PV88060_ID_SW6,
+};
+
+struct pv88060_regulator {
+ struct regulator_desc desc;
+ /* Current limiting */
+ unsigned n_current_limits;
+ const int *current_limits;
+ unsigned int limit_mask;
+ unsigned int conf; /* buck configuration register */
+};
+
+struct pv88060 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator_dev *rdev[PV88060_MAX_REGULATORS];
+};
+
+static const struct regmap_config pv88060_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+/* Current limits array (in uA) for BUCK1
+ * Entry indexes corresponds to register values.
+ */
+
+static const int pv88060_buck1_limits[] = {
+ 1496000, 2393000, 3291000, 4189000
+};
+
+static unsigned int pv88060_buck_get_mode(struct regulator_dev *rdev)
+{
+ struct pv88060_regulator *info = rdev_get_drvdata(rdev);
+ unsigned int data;
+ int ret, mode = 0;
+
+ ret = regmap_read(rdev->regmap, info->conf, &data);
+ if (ret < 0)
+ return ret;
+
+ switch (data & PV88060_BUCK_MODE_MASK) {
+ case PV88060_BUCK_MODE_SYNC:
+ mode = REGULATOR_MODE_FAST;
+ break;
+ case PV88060_BUCK_MODE_AUTO:
+ mode = REGULATOR_MODE_NORMAL;
+ break;
+ case PV88060_BUCK_MODE_SLEEP:
+ mode = REGULATOR_MODE_STANDBY;
+ break;
+ }
+
+ return mode;
+}
+
+static int pv88060_buck_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct pv88060_regulator *info = rdev_get_drvdata(rdev);
+ int val = 0;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = PV88060_BUCK_MODE_SYNC;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = PV88060_BUCK_MODE_AUTO;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = PV88060_BUCK_MODE_SLEEP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(rdev->regmap, info->conf,
+ PV88060_BUCK_MODE_MASK, val);
+}
+
+static int pv88060_set_current_limit(struct regulator_dev *rdev, int min,
+ int max)
+{
+ struct pv88060_regulator *info = rdev_get_drvdata(rdev);
+ int i;
+
+ /* search for closest to maximum */
+ for (i = info->n_current_limits; i >= 0; i--) {
+ if (min <= info->current_limits[i]
+ && max >= info->current_limits[i]) {
+ return regmap_update_bits(rdev->regmap,
+ info->conf,
+ info->limit_mask,
+ i << PV88060_BUCK_ILIM_SHIFT);
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int pv88060_get_current_limit(struct regulator_dev *rdev)
+{
+ struct pv88060_regulator *info = rdev_get_drvdata(rdev);
+ unsigned int data;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, info->conf, &data);
+ if (ret < 0)
+ return ret;
+
+ data = (data & info->limit_mask) >> PV88060_BUCK_ILIM_SHIFT;
+ return info->current_limits[data];
+}
+
+static struct regulator_ops pv88060_buck_ops = {
+ .get_mode = pv88060_buck_get_mode,
+ .set_mode = pv88060_buck_set_mode,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_current_limit = pv88060_set_current_limit,
+ .get_current_limit = pv88060_get_current_limit,
+};
+
+static struct regulator_ops pv88060_ldo_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+#define PV88060_BUCK(chip, regl_name, min, step, max, limits_array) \
+{\
+ .desc = {\
+ .id = chip##_ID_##regl_name,\
+ .name = __stringify(chip##_##regl_name),\
+ .of_match = of_match_ptr(#regl_name),\
+ .regulators_node = of_match_ptr("regulators"),\
+ .type = REGULATOR_VOLTAGE,\
+ .owner = THIS_MODULE,\
+ .ops = &pv88060_buck_ops,\
+ .min_uV = min,\
+ .uV_step = step,\
+ .n_voltages = ((max) - (min))/(step) + 1,\
+ .enable_reg = PV88060_REG_##regl_name##_CONF0,\
+ .enable_mask = PV88060_BUCK_EN, \
+ .vsel_reg = PV88060_REG_##regl_name##_CONF0,\
+ .vsel_mask = PV88060_VBUCK_MASK,\
+ },\
+ .current_limits = limits_array,\
+ .n_current_limits = ARRAY_SIZE(limits_array),\
+ .limit_mask = PV88060_BUCK_ILIM_MASK, \
+ .conf = PV88060_REG_##regl_name##_CONF1,\
+}
+
+#define PV88060_LDO(chip, regl_name, min, step, max) \
+{\
+ .desc = {\
+ .id = chip##_ID_##regl_name,\
+ .name = __stringify(chip##_##regl_name),\
+ .of_match = of_match_ptr(#regl_name),\
+ .regulators_node = of_match_ptr("regulators"),\
+ .type = REGULATOR_VOLTAGE,\
+ .owner = THIS_MODULE,\
+ .ops = &pv88060_ldo_ops,\
+ .min_uV = min, \
+ .uV_step = step, \
+ .n_voltages = (step) ? ((max - min) / step + 1) : 1, \
+ .enable_reg = PV88060_REG_##regl_name##_CONF, \
+ .enable_mask = PV88060_LDO_EN, \
+ .vsel_reg = PV88060_REG_##regl_name##_CONF, \
+ .vsel_mask = PV88060_VLDO_MASK, \
+ },\
+}
+
+#define PV88060_SW(chip, regl_name, max) \
+{\
+ .desc = {\
+ .id = chip##_ID_##regl_name,\
+ .name = __stringify(chip##_##regl_name),\
+ .of_match = of_match_ptr(#regl_name),\
+ .regulators_node = of_match_ptr("regulators"),\
+ .type = REGULATOR_VOLTAGE,\
+ .owner = THIS_MODULE,\
+ .ops = &pv88060_ldo_ops,\
+ .min_uV = max,\
+ .uV_step = 0,\
+ .n_voltages = 1,\
+ .enable_reg = PV88060_REG_##regl_name##_CONF,\
+ .enable_mask = PV88060_SW_EN,\
+ },\
+}
+
+static const struct pv88060_regulator pv88060_regulator_info[] = {
+ PV88060_BUCK(PV88060, BUCK1, 2800000, 12500, 4387500,
+ pv88060_buck1_limits),
+ PV88060_LDO(PV88060, LDO1, 1200000, 50000, 3350000),
+ PV88060_LDO(PV88060, LDO2, 1200000, 50000, 3350000),
+ PV88060_LDO(PV88060, LDO3, 1200000, 50000, 3350000),
+ PV88060_LDO(PV88060, LDO4, 1200000, 50000, 3350000),
+ PV88060_LDO(PV88060, LDO5, 1200000, 50000, 3350000),
+ PV88060_LDO(PV88060, LDO6, 1200000, 50000, 3350000),
+ PV88060_LDO(PV88060, LDO7, 1200000, 50000, 3350000),
+ PV88060_SW(PV88060, SW1, 5000000),
+ PV88060_SW(PV88060, SW2, 5000000),
+ PV88060_SW(PV88060, SW3, 5000000),
+ PV88060_SW(PV88060, SW4, 5000000),
+ PV88060_SW(PV88060, SW5, 5000000),
+ PV88060_SW(PV88060, SW6, 5000000),
+};
+
+static irqreturn_t pv88060_irq_handler(int irq, void *data)
+{
+ struct pv88060 *chip = data;
+ int i, reg_val, err, ret = IRQ_NONE;
+
+ err = regmap_read(chip->regmap, PV88060_REG_EVENT_A, &reg_val);
+ if (err < 0)
+ goto error_i2c;
+
+ if (reg_val & PV88060_E_VDD_FLT) {
+ for (i = 0; i < PV88060_MAX_REGULATORS; i++) {
+ if (chip->rdev[i] != NULL) {
+ regulator_notifier_call_chain(chip->rdev[i],
+ REGULATOR_EVENT_UNDER_VOLTAGE,
+ NULL);
+ }
+ }
+
+ err = regmap_update_bits(chip->regmap, PV88060_REG_EVENT_A,
+ PV88060_E_VDD_FLT, PV88060_E_VDD_FLT);
+ if (err < 0)
+ goto error_i2c;
+
+ ret = IRQ_HANDLED;
+ }
+
+ if (reg_val & PV88060_E_OVER_TEMP) {
+ for (i = 0; i < PV88060_MAX_REGULATORS; i++) {
+ if (chip->rdev[i] != NULL) {
+ regulator_notifier_call_chain(chip->rdev[i],
+ REGULATOR_EVENT_OVER_TEMP,
+ NULL);
+ }
+ }
+
+ err = regmap_update_bits(chip->regmap, PV88060_REG_EVENT_A,
+ PV88060_E_OVER_TEMP, PV88060_E_OVER_TEMP);
+ if (err < 0)
+ goto error_i2c;
+
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+
+error_i2c:
+ dev_err(chip->dev, "I2C error : %d\n", err);
+ return IRQ_NONE;
+}
+
+/*
+ * I2C driver interface functions
+ */
+static int pv88060_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regulator_init_data *init_data = dev_get_platdata(&i2c->dev);
+ struct pv88060 *chip;
+ struct regulator_config config = { };
+ int error, i, ret = 0;
+
+ chip = devm_kzalloc(&i2c->dev, sizeof(struct pv88060), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->dev = &i2c->dev;
+ chip->regmap = devm_regmap_init_i2c(i2c, &pv88060_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ error = PTR_ERR(chip->regmap);
+ dev_err(chip->dev, "Failed to allocate register map: %d\n",
+ error);
+ return error;
+ }
+
+ i2c_set_clientdata(i2c, chip);
+
+ if (i2c->irq != 0) {
+ ret = regmap_write(chip->regmap, PV88060_REG_MASK_A, 0xFF);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to mask A reg: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(chip->regmap, PV88060_REG_MASK_B, 0xFF);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to mask B reg: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(chip->regmap, PV88060_REG_MASK_C, 0xFF);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to mask C reg: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
+ pv88060_irq_handler,
+ IRQF_TRIGGER_LOW|IRQF_ONESHOT,
+ "pv88060", chip);
+ if (ret != 0) {
+ dev_err(chip->dev, "Failed to request IRQ: %d\n",
+ i2c->irq);
+ return ret;
+ }
+
+ ret = regmap_update_bits(chip->regmap, PV88060_REG_MASK_A,
+ PV88060_M_VDD_FLT | PV88060_M_OVER_TEMP, 0);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to update mask reg: %d\n", ret);
+ return ret;
+ }
+
+ } else {
+ dev_warn(chip->dev, "No IRQ configured\n");
+ }
+
+ config.dev = chip->dev;
+ config.regmap = chip->regmap;
+
+ for (i = 0; i < PV88060_MAX_REGULATORS; i++) {
+ if (init_data)
+ config.init_data = &init_data[i];
+
+ config.driver_data = (void *)&pv88060_regulator_info[i];
+ chip->rdev[i] = devm_regulator_register(chip->dev,
+ &pv88060_regulator_info[i].desc, &config);
+ if (IS_ERR(chip->rdev[i])) {
+ dev_err(chip->dev,
+ "Failed to register PV88060 regulator\n");
+ return PTR_ERR(chip->rdev[i]);
+ }
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id pv88060_i2c_id[] = {
+ {"pv88060", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, pv88060_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pv88060_dt_ids[] = {
+ { .compatible = "pvs,pv88060", .data = &pv88060_i2c_id[0] },
+ {},
+};
+MODULE_DEVICE_TABLE(of, pv88060_dt_ids);
+#endif
+
+static struct i2c_driver pv88060_regulator_driver = {
+ .driver = {
+ .name = "pv88060",
+ .of_match_table = of_match_ptr(pv88060_dt_ids),
+ },
+ .probe = pv88060_i2c_probe,
+ .id_table = pv88060_i2c_id,
+};
+
+module_i2c_driver(pv88060_regulator_driver);
+
+MODULE_AUTHOR("James Ban <James.Ban.opensource@diasemi.com>");
+MODULE_DESCRIPTION("Regulator device driver for Powerventure PV88060");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/pv88060-regulator.h b/drivers/regulator/pv88060-regulator.h
new file mode 100644
index 000000000000..02ca9203a172
--- /dev/null
+++ b/drivers/regulator/pv88060-regulator.h
@@ -0,0 +1,69 @@
+/*
+ * pv88060-regulator.h - Regulator definitions for PV88060
+ * Copyright (C) 2015 Powerventure 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.
+ */
+
+#ifndef __PV88060_REGISTERS_H__
+#define __PV88060_REGISTERS_H__
+
+/* System Control and Event Registers */
+#define PV88060_REG_EVENT_A 0x04
+#define PV88060_REG_MASK_A 0x08
+#define PV88060_REG_MASK_B 0x09
+#define PV88060_REG_MASK_C 0x0A
+
+/* Regulator Registers */
+#define PV88060_REG_BUCK1_CONF0 0x1B
+#define PV88060_REG_BUCK1_CONF1 0x1C
+#define PV88060_REG_LDO1_CONF 0x1D
+#define PV88060_REG_LDO2_CONF 0x1E
+#define PV88060_REG_LDO3_CONF 0x1F
+#define PV88060_REG_LDO4_CONF 0x20
+#define PV88060_REG_LDO5_CONF 0x21
+#define PV88060_REG_LDO6_CONF 0x22
+#define PV88060_REG_LDO7_CONF 0x23
+
+#define PV88060_REG_SW1_CONF 0x3B
+#define PV88060_REG_SW2_CONF 0x3C
+#define PV88060_REG_SW3_CONF 0x3D
+#define PV88060_REG_SW4_CONF 0x3E
+#define PV88060_REG_SW5_CONF 0x3F
+#define PV88060_REG_SW6_CONF 0x40
+
+/* PV88060_REG_EVENT_A (addr=0x04) */
+#define PV88060_E_VDD_FLT 0x01
+#define PV88060_E_OVER_TEMP 0x02
+
+/* PV88060_REG_MASK_A (addr=0x08) */
+#define PV88060_M_VDD_FLT 0x01
+#define PV88060_M_OVER_TEMP 0x02
+
+/* PV88060_REG_BUCK1_CONF0 (addr=0x1B) */
+#define PV88060_BUCK_EN 0x80
+#define PV88060_VBUCK_MASK 0x7F
+/* PV88060_REG_LDO1/2/3/4/5/6/7_CONT */
+#define PV88060_LDO_EN 0x40
+#define PV88060_VLDO_MASK 0x3F
+/* PV88060_REG_SW1/2/3/4/5_CONF */
+#define PV88060_SW_EN 0x80
+
+/* PV88060_REG_BUCK1_CONF1 (addr=0x1C) */
+#define PV88060_BUCK_ILIM_SHIFT 2
+#define PV88060_BUCK_ILIM_MASK 0x0C
+#define PV88060_BUCK_MODE_SHIFT 0
+#define PV88060_BUCK_MODE_MASK 0x03
+#define PV88060_BUCK_MODE_SLEEP 0x00
+#define PV88060_BUCK_MODE_AUTO 0x01
+#define PV88060_BUCK_MODE_SYNC 0x02
+
+#endif /* __PV88060_REGISTERS_H__ */
diff --git a/drivers/regulator/pv88090-regulator.c b/drivers/regulator/pv88090-regulator.c
new file mode 100644
index 000000000000..ac15f31b5fe0
--- /dev/null
+++ b/drivers/regulator/pv88090-regulator.c
@@ -0,0 +1,458 @@
+/*
+ * pv88090-regulator.c - Regulator device driver for PV88090
+ * Copyright (C) 2015 Powerventure 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/err.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regmap.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/proc_fs.h>
+#include <linux/uaccess.h>
+#include "pv88090-regulator.h"
+
+#define PV88090_MAX_REGULATORS 5
+
+/* PV88090 REGULATOR IDs */
+enum {
+ /* BUCKs */
+ PV88090_ID_BUCK1,
+ PV88090_ID_BUCK2,
+ PV88090_ID_BUCK3,
+
+ /* LDOs */
+ PV88090_ID_LDO1,
+ PV88090_ID_LDO2,
+};
+
+struct pv88090_regulator {
+ struct regulator_desc desc;
+ /* Current limiting */
+ unsigned n_current_limits;
+ const int *current_limits;
+ unsigned int limit_mask;
+ unsigned int conf;
+ unsigned int conf2;
+};
+
+struct pv88090 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator_dev *rdev[PV88090_MAX_REGULATORS];
+};
+
+struct pv88090_buck_voltage {
+ int min_uV;
+ int max_uV;
+ int uV_step;
+};
+
+static const struct regmap_config pv88090_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+/* Current limits array (in uA) for BUCK1, BUCK2, BUCK3.
+ * Entry indexes corresponds to register values.
+ */
+
+static const int pv88090_buck1_limits[] = {
+ 220000, 440000, 660000, 880000, 1100000, 1320000, 1540000, 1760000,
+ 1980000, 2200000, 2420000, 2640000, 2860000, 3080000, 3300000, 3520000,
+ 3740000, 3960000, 4180000, 4400000, 4620000, 4840000, 5060000, 5280000,
+ 5500000, 5720000, 5940000, 6160000, 6380000, 6600000, 6820000, 7040000
+};
+
+static const int pv88090_buck23_limits[] = {
+ 1496000, 2393000, 3291000, 4189000
+};
+
+static const struct pv88090_buck_voltage pv88090_buck_vol[3] = {
+ {
+ .min_uV = 600000,
+ .max_uV = 1393750,
+ .uV_step = 6250,
+ },
+
+ {
+ .min_uV = 1400000,
+ .max_uV = 2193750,
+ .uV_step = 6250,
+ },
+ {
+ .min_uV = 1250000,
+ .max_uV = 2837500,
+ .uV_step = 12500,
+ },
+};
+
+static unsigned int pv88090_buck_get_mode(struct regulator_dev *rdev)
+{
+ struct pv88090_regulator *info = rdev_get_drvdata(rdev);
+ unsigned int data;
+ int ret, mode = 0;
+
+ ret = regmap_read(rdev->regmap, info->conf, &data);
+ if (ret < 0)
+ return ret;
+
+ switch (data & PV88090_BUCK1_MODE_MASK) {
+ case PV88090_BUCK_MODE_SYNC:
+ mode = REGULATOR_MODE_FAST;
+ break;
+ case PV88090_BUCK_MODE_AUTO:
+ mode = REGULATOR_MODE_NORMAL;
+ break;
+ case PV88090_BUCK_MODE_SLEEP:
+ mode = REGULATOR_MODE_STANDBY;
+ break;
+ }
+
+ return mode;
+}
+
+static int pv88090_buck_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct pv88090_regulator *info = rdev_get_drvdata(rdev);
+ int val = 0;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = PV88090_BUCK_MODE_SYNC;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = PV88090_BUCK_MODE_AUTO;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = PV88090_BUCK_MODE_SLEEP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(rdev->regmap, info->conf,
+ PV88090_BUCK1_MODE_MASK, val);
+}
+
+static int pv88090_set_current_limit(struct regulator_dev *rdev, int min,
+ int max)
+{
+ struct pv88090_regulator *info = rdev_get_drvdata(rdev);
+ int i;
+
+ /* search for closest to maximum */
+ for (i = info->n_current_limits; i >= 0; i--) {
+ if (min <= info->current_limits[i]
+ && max >= info->current_limits[i]) {
+ return regmap_update_bits(rdev->regmap,
+ info->conf,
+ info->limit_mask,
+ i << PV88090_BUCK1_ILIM_SHIFT);
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int pv88090_get_current_limit(struct regulator_dev *rdev)
+{
+ struct pv88090_regulator *info = rdev_get_drvdata(rdev);
+ unsigned int data;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, info->conf, &data);
+ if (ret < 0)
+ return ret;
+
+ data = (data & info->limit_mask) >> PV88090_BUCK1_ILIM_SHIFT;
+ return info->current_limits[data];
+}
+
+static struct regulator_ops pv88090_buck_ops = {
+ .get_mode = pv88090_buck_get_mode,
+ .set_mode = pv88090_buck_set_mode,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_current_limit = pv88090_set_current_limit,
+ .get_current_limit = pv88090_get_current_limit,
+};
+
+static struct regulator_ops pv88090_ldo_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+#define PV88090_BUCK(chip, regl_name, min, step, max, limits_array) \
+{\
+ .desc = {\
+ .id = chip##_ID_##regl_name,\
+ .name = __stringify(chip##_##regl_name),\
+ .of_match = of_match_ptr(#regl_name),\
+ .regulators_node = of_match_ptr("regulators"),\
+ .type = REGULATOR_VOLTAGE,\
+ .owner = THIS_MODULE,\
+ .ops = &pv88090_buck_ops,\
+ .min_uV = min, \
+ .uV_step = step, \
+ .n_voltages = ((max) - (min))/(step) + 1, \
+ .enable_reg = PV88090_REG_##regl_name##_CONF0, \
+ .enable_mask = PV88090_##regl_name##_EN, \
+ .vsel_reg = PV88090_REG_##regl_name##_CONF0, \
+ .vsel_mask = PV88090_V##regl_name##_MASK, \
+ },\
+ .current_limits = limits_array, \
+ .n_current_limits = ARRAY_SIZE(limits_array), \
+ .limit_mask = PV88090_##regl_name##_ILIM_MASK, \
+ .conf = PV88090_REG_##regl_name##_CONF1, \
+ .conf2 = PV88090_REG_##regl_name##_CONF2, \
+}
+
+#define PV88090_LDO(chip, regl_name, min, step, max) \
+{\
+ .desc = {\
+ .id = chip##_ID_##regl_name,\
+ .name = __stringify(chip##_##regl_name),\
+ .of_match = of_match_ptr(#regl_name),\
+ .regulators_node = of_match_ptr("regulators"),\
+ .type = REGULATOR_VOLTAGE,\
+ .owner = THIS_MODULE,\
+ .ops = &pv88090_ldo_ops,\
+ .min_uV = min, \
+ .uV_step = step, \
+ .n_voltages = ((max) - (min))/(step) + 1, \
+ .enable_reg = PV88090_REG_##regl_name##_CONT, \
+ .enable_mask = PV88090_##regl_name##_EN, \
+ .vsel_reg = PV88090_REG_##regl_name##_CONT, \
+ .vsel_mask = PV88090_V##regl_name##_MASK, \
+ },\
+}
+
+static struct pv88090_regulator pv88090_regulator_info[] = {
+ PV88090_BUCK(PV88090, BUCK1, 600000, 6250, 1393750,
+ pv88090_buck1_limits),
+ PV88090_BUCK(PV88090, BUCK2, 600000, 6250, 1393750,
+ pv88090_buck23_limits),
+ PV88090_BUCK(PV88090, BUCK3, 600000, 6250, 1393750,
+ pv88090_buck23_limits),
+ PV88090_LDO(PV88090, LDO1, 1200000, 50000, 4350000),
+ PV88090_LDO(PV88090, LDO2, 650000, 25000, 2225000),
+};
+
+static irqreturn_t pv88090_irq_handler(int irq, void *data)
+{
+ struct pv88090 *chip = data;
+ int i, reg_val, err, ret = IRQ_NONE;
+
+ err = regmap_read(chip->regmap, PV88090_REG_EVENT_A, &reg_val);
+ if (err < 0)
+ goto error_i2c;
+
+ if (reg_val & PV88090_E_VDD_FLT) {
+ for (i = 0; i < PV88090_MAX_REGULATORS; i++) {
+ if (chip->rdev[i] != NULL) {
+ regulator_notifier_call_chain(chip->rdev[i],
+ REGULATOR_EVENT_UNDER_VOLTAGE,
+ NULL);
+ }
+ }
+
+ err = regmap_update_bits(chip->regmap, PV88090_REG_EVENT_A,
+ PV88090_E_VDD_FLT, PV88090_E_VDD_FLT);
+ if (err < 0)
+ goto error_i2c;
+
+ ret = IRQ_HANDLED;
+ }
+
+ if (reg_val & PV88090_E_OVER_TEMP) {
+ for (i = 0; i < PV88090_MAX_REGULATORS; i++) {
+ if (chip->rdev[i] != NULL) {
+ regulator_notifier_call_chain(chip->rdev[i],
+ REGULATOR_EVENT_OVER_TEMP,
+ NULL);
+ }
+ }
+
+ err = regmap_update_bits(chip->regmap, PV88090_REG_EVENT_A,
+ PV88090_E_OVER_TEMP, PV88090_E_OVER_TEMP);
+ if (err < 0)
+ goto error_i2c;
+
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+
+error_i2c:
+ dev_err(chip->dev, "I2C error : %d\n", err);
+ return IRQ_NONE;
+}
+
+/*
+ * I2C driver interface functions
+ */
+static int pv88090_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regulator_init_data *init_data = dev_get_platdata(&i2c->dev);
+ struct pv88090 *chip;
+ struct regulator_config config = { };
+ int error, i, ret = 0;
+ unsigned int conf2, range, index;
+
+ chip = devm_kzalloc(&i2c->dev, sizeof(struct pv88090), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->dev = &i2c->dev;
+ chip->regmap = devm_regmap_init_i2c(i2c, &pv88090_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ error = PTR_ERR(chip->regmap);
+ dev_err(chip->dev, "Failed to allocate register map: %d\n",
+ error);
+ return error;
+ }
+
+ i2c_set_clientdata(i2c, chip);
+
+ if (i2c->irq != 0) {
+ ret = regmap_write(chip->regmap, PV88090_REG_MASK_A, 0xFF);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to mask A reg: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(chip->regmap, PV88090_REG_MASK_B, 0xFF);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to mask B reg: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
+ pv88090_irq_handler,
+ IRQF_TRIGGER_LOW|IRQF_ONESHOT,
+ "pv88090", chip);
+ if (ret != 0) {
+ dev_err(chip->dev, "Failed to request IRQ: %d\n",
+ i2c->irq);
+ return ret;
+ }
+
+ ret = regmap_update_bits(chip->regmap, PV88090_REG_MASK_A,
+ PV88090_M_VDD_FLT | PV88090_M_OVER_TEMP, 0);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to update mask reg: %d\n", ret);
+ return ret;
+ }
+
+ } else {
+ dev_warn(chip->dev, "No IRQ configured\n");
+ }
+
+ config.dev = chip->dev;
+ config.regmap = chip->regmap;
+
+ for (i = 0; i < PV88090_MAX_REGULATORS; i++) {
+ if (init_data)
+ config.init_data = &init_data[i];
+
+ if (i == PV88090_ID_BUCK2 || i == PV88090_ID_BUCK3) {
+ ret = regmap_read(chip->regmap,
+ pv88090_regulator_info[i].conf2, &conf2);
+ if (ret < 0)
+ return ret;
+
+ conf2 = (conf2 >> PV88090_BUCK_VDAC_RANGE_SHIFT) &
+ PV88090_BUCK_VDAC_RANGE_MASK;
+
+ ret = regmap_read(chip->regmap,
+ PV88090_REG_BUCK_FOLD_RANGE, &range);
+ if (ret < 0)
+ return ret;
+
+ range = (range >>
+ (PV88080_BUCK_VRANGE_GAIN_SHIFT + i - 1)) &
+ PV88080_BUCK_VRANGE_GAIN_MASK;
+ index = ((range << 1) | conf2);
+
+ pv88090_regulator_info[i].desc.min_uV
+ = pv88090_buck_vol[index].min_uV;
+ pv88090_regulator_info[i].desc.uV_step
+ = pv88090_buck_vol[index].uV_step;
+ pv88090_regulator_info[i].desc.n_voltages
+ = ((pv88090_buck_vol[index].max_uV)
+ - (pv88090_buck_vol[index].min_uV))
+ /(pv88090_buck_vol[index].uV_step) + 1;
+ }
+
+ config.driver_data = (void *)&pv88090_regulator_info[i];
+ chip->rdev[i] = devm_regulator_register(chip->dev,
+ &pv88090_regulator_info[i].desc, &config);
+ if (IS_ERR(chip->rdev[i])) {
+ dev_err(chip->dev,
+ "Failed to register PV88090 regulator\n");
+ return PTR_ERR(chip->rdev[i]);
+ }
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id pv88090_i2c_id[] = {
+ {"pv88090", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, pv88090_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pv88090_dt_ids[] = {
+ { .compatible = "pvs,pv88090", .data = &pv88090_i2c_id[0] },
+ {},
+};
+MODULE_DEVICE_TABLE(of, pv88090_dt_ids);
+#endif
+
+static struct i2c_driver pv88090_regulator_driver = {
+ .driver = {
+ .name = "pv88090",
+ .of_match_table = of_match_ptr(pv88090_dt_ids),
+ },
+ .probe = pv88090_i2c_probe,
+ .id_table = pv88090_i2c_id,
+};
+
+module_i2c_driver(pv88090_regulator_driver);
+
+MODULE_AUTHOR("James Ban <James.Ban.opensource@diasemi.com>");
+MODULE_DESCRIPTION("Regulator device driver for Powerventure PV88090");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/pv88090-regulator.h b/drivers/regulator/pv88090-regulator.h
new file mode 100644
index 000000000000..d7aca8d8266d
--- /dev/null
+++ b/drivers/regulator/pv88090-regulator.h
@@ -0,0 +1,98 @@
+/*
+ * pv88090-regulator.h - Regulator definitions for PV88090
+ * Copyright (C) 2015 Powerventure 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.
+ */
+
+#ifndef __PV88090_REGISTERS_H__
+#define __PV88090_REGISTERS_H__
+
+/* System Control and Event Registers */
+#define PV88090_REG_EVENT_A 0x03
+#define PV88090_REG_MASK_A 0x06
+#define PV88090_REG_MASK_B 0x07
+
+/* Regulator Registers */
+#define PV88090_REG_BUCK1_CONF0 0x18
+#define PV88090_REG_BUCK1_CONF1 0x19
+#define PV88090_REG_BUCK1_CONF2 0x1a
+#define PV88090_REG_BUCK2_CONF0 0x1b
+#define PV88090_REG_BUCK2_CONF1 0x1c
+#define PV88090_REG_BUCK2_CONF2 0x58
+#define PV88090_REG_BUCK3_CONF0 0x1d
+#define PV88090_REG_BUCK3_CONF1 0x1e
+#define PV88090_REG_BUCK3_CONF2 0x5c
+
+#define PV88090_REG_LDO1_CONT 0x1f
+#define PV88090_REG_LDO2_CONT 0x20
+#define PV88090_REG_LDO3_CONT 0x21
+#define PV88090_REG_BUCK_FOLD_RANGE 0x61
+
+/* PV88090_REG_EVENT_A (addr=0x03) */
+#define PV88090_E_VDD_FLT 0x01
+#define PV88090_E_OVER_TEMP 0x02
+
+/* PV88090_REG_MASK_A (addr=0x06) */
+#define PV88090_M_VDD_FLT 0x01
+#define PV88090_M_OVER_TEMP 0x02
+
+/* PV88090_REG_BUCK1_CONF0 (addr=0x18) */
+#define PV88090_BUCK1_EN 0x80
+#define PV88090_VBUCK1_MASK 0x7F
+/* PV88090_REG_BUCK2_CONF0 (addr=0x1b) */
+#define PV88090_BUCK2_EN 0x80
+#define PV88090_VBUCK2_MASK 0x7F
+/* PV88090_REG_BUCK3_CONF0 (addr=0x1d) */
+#define PV88090_BUCK3_EN 0x80
+#define PV88090_VBUCK3_MASK 0x7F
+/* PV88090_REG_LDO1_CONT (addr=0x1f) */
+#define PV88090_LDO1_EN 0x40
+#define PV88090_VLDO1_MASK 0x3F
+/* PV88090_REG_LDO2_CONT (addr=0x20) */
+#define PV88090_LDO2_EN 0x40
+#define PV88090_VLDO2_MASK 0x3F
+
+/* PV88090_REG_BUCK1_CONF1 (addr=0x19) */
+#define PV88090_BUCK1_ILIM_SHIFT 2
+#define PV88090_BUCK1_ILIM_MASK 0x7C
+#define PV88090_BUCK1_MODE_MASK 0x03
+
+/* PV88090_REG_BUCK2_CONF1 (addr=0x1c) */
+#define PV88090_BUCK2_ILIM_SHIFT 2
+#define PV88090_BUCK2_ILIM_MASK 0x0C
+#define PV88090_BUCK2_MODE_MASK 0x03
+
+/* PV88090_REG_BUCK3_CONF1 (addr=0x1e) */
+#define PV88090_BUCK3_ILIM_SHIFT 2
+#define PV88090_BUCK3_ILIM_MASK 0x0C
+#define PV88090_BUCK3_MODE_MASK 0x03
+
+#define PV88090_BUCK_MODE_SLEEP 0x00
+#define PV88090_BUCK_MODE_AUTO 0x01
+#define PV88090_BUCK_MODE_SYNC 0x02
+
+/* PV88090_REG_BUCK2_CONF2 (addr=0x58) */
+/* PV88090_REG_BUCK3_CONF2 (addr=0x5c) */
+#define PV88090_BUCK_VDAC_RANGE_SHIFT 7
+#define PV88090_BUCK_VDAC_RANGE_MASK 0x01
+
+#define PV88090_BUCK_VDAC_RANGE_1 0x00
+#define PV88090_BUCK_VDAC_RANGE_2 0x01
+
+/* PV88090_REG_BUCK_FOLD_RANGE (addr=0x61) */
+#define PV88080_BUCK_VRANGE_GAIN_SHIFT 3
+#define PV88080_BUCK_VRANGE_GAIN_MASK 0x01
+
+#define PV88080_BUCK_VRANGE_GAIN_1 0x00
+#define PV88080_BUCK_VRANGE_GAIN_2 0x01
+
+#endif /* __PV88090_REGISTERS_H__ */
diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c
index 6fa0c7d13290..56a17ec5b5ef 100644
--- a/drivers/regulator/qcom_smd-regulator.c
+++ b/drivers/regulator/qcom_smd-regulator.c
@@ -153,6 +153,49 @@ static const struct regulator_ops rpm_switch_ops = {
.is_enabled = rpm_reg_is_enabled,
};
+static const struct regulator_desc pma8084_hfsmps = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(375000, 0, 95, 12500),
+ REGULATOR_LINEAR_RANGE(1550000, 96, 158, 25000),
+ },
+ .n_linear_ranges = 2,
+ .n_voltages = 159,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pma8084_ftsmps = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(350000, 0, 184, 5000),
+ REGULATOR_LINEAR_RANGE(700000, 185, 339, 10000),
+ },
+ .n_linear_ranges = 2,
+ .n_voltages = 340,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pma8084_pldo = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(750000, 0, 30, 25000),
+ REGULATOR_LINEAR_RANGE(1500000, 31, 99, 50000),
+ },
+ .n_linear_ranges = 2,
+ .n_voltages = 100,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pma8084_nldo = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(750000, 0, 63, 12500),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 64,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pma8084_switch = {
+ .ops = &rpm_switch_ops,
+};
+
static const struct regulator_desc pm8x41_hfsmps = {
.linear_ranges = (struct regulator_linear_range[]) {
REGULATOR_LINEAR_RANGE( 375000, 0, 95, 12500),
@@ -211,6 +254,43 @@ static const struct regulator_desc pm8941_switch = {
.ops = &rpm_switch_ops,
};
+static const struct regulator_desc pm8916_pldo = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(750000, 0, 208, 12500),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 209,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8916_nldo = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(375000, 0, 93, 12500),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 94,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8916_buck_lvo_smps = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(375000, 0, 95, 12500),
+ REGULATOR_LINEAR_RANGE(750000, 96, 127, 25000),
+ },
+ .n_linear_ranges = 2,
+ .n_voltages = 128,
+ .ops = &rpm_smps_ldo_ops,
+};
+
+static const struct regulator_desc pm8916_buck_hvo_smps = {
+ .linear_ranges = (struct regulator_linear_range[]) {
+ REGULATOR_LINEAR_RANGE(1550000, 0, 31, 25000),
+ },
+ .n_linear_ranges = 1,
+ .n_voltages = 32,
+ .ops = &rpm_smps_ldo_ops,
+};
+
struct rpm_regulator_data {
const char *name;
u32 type;
@@ -231,6 +311,32 @@ static const struct rpm_regulator_data rpm_pm8841_regulators[] = {
{}
};
+static const struct rpm_regulator_data rpm_pm8916_regulators[] = {
+ { "s1", QCOM_SMD_RPM_SMPA, 1, &pm8916_buck_lvo_smps, "vdd_s1" },
+ { "s2", QCOM_SMD_RPM_SMPA, 2, &pm8916_buck_lvo_smps, "vdd_s2" },
+ { "s3", QCOM_SMD_RPM_SMPA, 3, &pm8916_buck_lvo_smps, "vdd_s3" },
+ { "s4", QCOM_SMD_RPM_SMPA, 4, &pm8916_buck_hvo_smps, "vdd_s4" },
+ { "l1", QCOM_SMD_RPM_LDOA, 1, &pm8916_nldo, "vdd_l1_l2_l3" },
+ { "l2", QCOM_SMD_RPM_LDOA, 2, &pm8916_nldo, "vdd_l1_l2_l3" },
+ { "l3", QCOM_SMD_RPM_LDOA, 3, &pm8916_nldo, "vdd_l1_l2_l3" },
+ { "l4", QCOM_SMD_RPM_LDOA, 4, &pm8916_pldo, "vdd_l4_l5_l6" },
+ { "l5", QCOM_SMD_RPM_LDOA, 5, &pm8916_pldo, "vdd_l4_l5_l6" },
+ { "l6", QCOM_SMD_RPM_LDOA, 6, &pm8916_pldo, "vdd_l4_l5_l6" },
+ { "l7", QCOM_SMD_RPM_LDOA, 7, &pm8916_pldo, "vdd_l7" },
+ { "l8", QCOM_SMD_RPM_LDOA, 8, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18" },
+ { "l9", QCOM_SMD_RPM_LDOA, 9, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18" },
+ { "l10", QCOM_SMD_RPM_LDOA, 10, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"},
+ { "l11", QCOM_SMD_RPM_LDOA, 11, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"},
+ { "l12", QCOM_SMD_RPM_LDOA, 12, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"},
+ { "l13", QCOM_SMD_RPM_LDOA, 13, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"},
+ { "l14", QCOM_SMD_RPM_LDOA, 14, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"},
+ { "l15", QCOM_SMD_RPM_LDOA, 15, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"},
+ { "l16", QCOM_SMD_RPM_LDOA, 16, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"},
+ { "l17", QCOM_SMD_RPM_LDOA, 17, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"},
+ { "l18", QCOM_SMD_RPM_LDOA, 18, &pm8916_pldo, "vdd_l8_l9_l10_l11_l12_l13_l14_l15_l16_l17_l18"},
+ {}
+};
+
static const struct rpm_regulator_data rpm_pm8941_regulators[] = {
{ "s1", QCOM_SMD_RPM_SMPA, 1, &pm8x41_hfsmps, "vdd_s1" },
{ "s2", QCOM_SMD_RPM_SMPA, 2, &pm8x41_hfsmps, "vdd_s2" },
@@ -272,9 +378,62 @@ static const struct rpm_regulator_data rpm_pm8941_regulators[] = {
{}
};
+static const struct rpm_regulator_data rpm_pma8084_regulators[] = {
+ { "s1", QCOM_SMD_RPM_SMPA, 1, &pma8084_ftsmps, "vdd_s1" },
+ { "s2", QCOM_SMD_RPM_SMPA, 2, &pma8084_ftsmps, "vdd_s2" },
+ { "s3", QCOM_SMD_RPM_SMPA, 3, &pma8084_hfsmps, "vdd_s3" },
+ { "s4", QCOM_SMD_RPM_SMPA, 4, &pma8084_hfsmps, "vdd_s4" },
+ { "s5", QCOM_SMD_RPM_SMPA, 5, &pma8084_hfsmps, "vdd_s5" },
+ { "s6", QCOM_SMD_RPM_SMPA, 6, &pma8084_ftsmps, "vdd_s6" },
+ { "s7", QCOM_SMD_RPM_SMPA, 7, &pma8084_ftsmps, "vdd_s7" },
+ { "s8", QCOM_SMD_RPM_SMPA, 8, &pma8084_ftsmps, "vdd_s8" },
+ { "s9", QCOM_SMD_RPM_SMPA, 9, &pma8084_ftsmps, "vdd_s9" },
+ { "s10", QCOM_SMD_RPM_SMPA, 10, &pma8084_ftsmps, "vdd_s10" },
+ { "s11", QCOM_SMD_RPM_SMPA, 11, &pma8084_ftsmps, "vdd_s11" },
+ { "s12", QCOM_SMD_RPM_SMPA, 12, &pma8084_ftsmps, "vdd_s12" },
+
+ { "l1", QCOM_SMD_RPM_LDOA, 1, &pma8084_nldo, "vdd_l1_l11" },
+ { "l2", QCOM_SMD_RPM_LDOA, 2, &pma8084_nldo, "vdd_l2_l3_l4_l27" },
+ { "l3", QCOM_SMD_RPM_LDOA, 3, &pma8084_nldo, "vdd_l2_l3_l4_l27" },
+ { "l4", QCOM_SMD_RPM_LDOA, 4, &pma8084_nldo, "vdd_l2_l3_l4_l27" },
+ { "l5", QCOM_SMD_RPM_LDOA, 5, &pma8084_pldo, "vdd_l5_l7" },
+ { "l6", QCOM_SMD_RPM_LDOA, 6, &pma8084_pldo, "vdd_l6_l12_l14_l15_l26" },
+ { "l7", QCOM_SMD_RPM_LDOA, 7, &pma8084_pldo, "vdd_l5_l7" },
+ { "l8", QCOM_SMD_RPM_LDOA, 8, &pma8084_pldo, "vdd_l8" },
+ { "l9", QCOM_SMD_RPM_LDOA, 9, &pma8084_pldo, "vdd_l9_l10_l13_l20_l23_l24" },
+ { "l10", QCOM_SMD_RPM_LDOA, 10, &pma8084_pldo, "vdd_l9_l10_l13_l20_l23_l24" },
+ { "l11", QCOM_SMD_RPM_LDOA, 11, &pma8084_nldo, "vdd_l1_l11" },
+ { "l12", QCOM_SMD_RPM_LDOA, 12, &pma8084_pldo, "vdd_l6_l12_l14_l15_l26" },
+ { "l13", QCOM_SMD_RPM_LDOA, 13, &pma8084_pldo, "vdd_l9_l10_l13_l20_l23_l24" },
+ { "l14", QCOM_SMD_RPM_LDOA, 14, &pma8084_pldo, "vdd_l6_l12_l14_l15_l26" },
+ { "l15", QCOM_SMD_RPM_LDOA, 15, &pma8084_pldo, "vdd_l6_l12_l14_l15_l26" },
+ { "l16", QCOM_SMD_RPM_LDOA, 16, &pma8084_pldo, "vdd_l16_l25" },
+ { "l17", QCOM_SMD_RPM_LDOA, 17, &pma8084_pldo, "vdd_l17" },
+ { "l18", QCOM_SMD_RPM_LDOA, 18, &pma8084_pldo, "vdd_l18" },
+ { "l19", QCOM_SMD_RPM_LDOA, 19, &pma8084_pldo, "vdd_l19" },
+ { "l20", QCOM_SMD_RPM_LDOA, 20, &pma8084_pldo, "vdd_l9_l10_l13_l20_l23_l24" },
+ { "l21", QCOM_SMD_RPM_LDOA, 21, &pma8084_pldo, "vdd_l21" },
+ { "l22", QCOM_SMD_RPM_LDOA, 22, &pma8084_pldo, "vdd_l22" },
+ { "l23", QCOM_SMD_RPM_LDOA, 23, &pma8084_pldo, "vdd_l9_l10_l13_l20_l23_l24" },
+ { "l24", QCOM_SMD_RPM_LDOA, 24, &pma8084_pldo, "vdd_l9_l10_l13_l20_l23_l24" },
+ { "l25", QCOM_SMD_RPM_LDOA, 25, &pma8084_pldo, "vdd_l16_l25" },
+ { "l26", QCOM_SMD_RPM_LDOA, 26, &pma8084_pldo, "vdd_l6_l12_l14_l15_l26" },
+ { "l27", QCOM_SMD_RPM_LDOA, 27, &pma8084_nldo, "vdd_l2_l3_l4_l27" },
+
+ { "lvs1", QCOM_SMD_RPM_VSA, 1, &pma8084_switch },
+ { "lvs2", QCOM_SMD_RPM_VSA, 2, &pma8084_switch },
+ { "lvs3", QCOM_SMD_RPM_VSA, 3, &pma8084_switch },
+ { "lvs4", QCOM_SMD_RPM_VSA, 4, &pma8084_switch },
+ { "5vs1", QCOM_SMD_RPM_VSA, 5, &pma8084_switch },
+
+ {}
+};
+
static const struct of_device_id rpm_of_match[] = {
{ .compatible = "qcom,rpm-pm8841-regulators", .data = &rpm_pm8841_regulators },
+ { .compatible = "qcom,rpm-pm8916-regulators", .data = &rpm_pm8916_regulators },
{ .compatible = "qcom,rpm-pm8941-regulators", .data = &rpm_pm8941_regulators },
+ { .compatible = "qcom,rpm-pma8084-regulators", .data = &rpm_pma8084_regulators },
{}
};
MODULE_DEVICE_TABLE(of, rpm_of_match);
diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c
index 72fc3c32db49..3242ffc0cb25 100644
--- a/drivers/regulator/s2mps11.c
+++ b/drivers/regulator/s2mps11.c
@@ -32,6 +32,7 @@
#include <linux/mfd/samsung/s2mps11.h>
#include <linux/mfd/samsung/s2mps13.h>
#include <linux/mfd/samsung/s2mps14.h>
+#include <linux/mfd/samsung/s2mps15.h>
#include <linux/mfd/samsung/s2mpu02.h>
/* The highest number of possible regulators for supported devices. */
@@ -661,6 +662,133 @@ static const struct regulator_desc s2mps14_regulators[] = {
S2MPS14_BUCK1235_START_SEL),
};
+static struct regulator_ops s2mps15_reg_ldo_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+};
+
+static struct regulator_ops s2mps15_reg_buck_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+};
+
+#define regulator_desc_s2mps15_ldo(num, range) { \
+ .name = "LDO"#num, \
+ .id = S2MPS15_LDO##num, \
+ .ops = &s2mps15_reg_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .linear_ranges = range, \
+ .n_linear_ranges = ARRAY_SIZE(range), \
+ .n_voltages = S2MPS15_LDO_N_VOLTAGES, \
+ .vsel_reg = S2MPS15_REG_L1CTRL + num - 1, \
+ .vsel_mask = S2MPS15_LDO_VSEL_MASK, \
+ .enable_reg = S2MPS15_REG_L1CTRL + num - 1, \
+ .enable_mask = S2MPS15_ENABLE_MASK \
+}
+
+#define regulator_desc_s2mps15_buck(num, range) { \
+ .name = "BUCK"#num, \
+ .id = S2MPS15_BUCK##num, \
+ .ops = &s2mps15_reg_buck_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .linear_ranges = range, \
+ .n_linear_ranges = ARRAY_SIZE(range), \
+ .ramp_delay = 12500, \
+ .n_voltages = S2MPS15_BUCK_N_VOLTAGES, \
+ .vsel_reg = S2MPS15_REG_B1CTRL2 + ((num - 1) * 2), \
+ .vsel_mask = S2MPS15_BUCK_VSEL_MASK, \
+ .enable_reg = S2MPS15_REG_B1CTRL1 + ((num - 1) * 2), \
+ .enable_mask = S2MPS15_ENABLE_MASK \
+}
+
+/* voltage range for s2mps15 LDO 3, 5, 15, 16, 18, 20, 23 and 27 */
+static const struct regulator_linear_range s2mps15_ldo_voltage_ranges1[] = {
+ REGULATOR_LINEAR_RANGE(1000000, 0xc, 0x38, 25000),
+};
+
+/* voltage range for s2mps15 LDO 2, 6, 14, 17, 19, 21, 24 and 25 */
+static const struct regulator_linear_range s2mps15_ldo_voltage_ranges2[] = {
+ REGULATOR_LINEAR_RANGE(1800000, 0x0, 0x3f, 25000),
+};
+
+/* voltage range for s2mps15 LDO 4, 11, 12, 13, 22 and 26 */
+static const struct regulator_linear_range s2mps15_ldo_voltage_ranges3[] = {
+ REGULATOR_LINEAR_RANGE(700000, 0x0, 0x34, 12500),
+};
+
+/* voltage range for s2mps15 LDO 7, 8, 9 and 10 */
+static const struct regulator_linear_range s2mps15_ldo_voltage_ranges4[] = {
+ REGULATOR_LINEAR_RANGE(700000, 0xc, 0x18, 25000),
+};
+
+/* voltage range for s2mps15 LDO 1 */
+static const struct regulator_linear_range s2mps15_ldo_voltage_ranges5[] = {
+ REGULATOR_LINEAR_RANGE(500000, 0x0, 0x20, 12500),
+};
+
+/* voltage range for s2mps15 BUCK 1, 2, 3, 4, 5, 6 and 7 */
+static const struct regulator_linear_range s2mps15_buck_voltage_ranges1[] = {
+ REGULATOR_LINEAR_RANGE(500000, 0x20, 0xb0, 6250),
+};
+
+/* voltage range for s2mps15 BUCK 8, 9 and 10 */
+static const struct regulator_linear_range s2mps15_buck_voltage_ranges2[] = {
+ REGULATOR_LINEAR_RANGE(1000000, 0x20, 0xc0, 12500),
+};
+
+static const struct regulator_desc s2mps15_regulators[] = {
+ regulator_desc_s2mps15_ldo(1, s2mps15_ldo_voltage_ranges5),
+ regulator_desc_s2mps15_ldo(2, s2mps15_ldo_voltage_ranges2),
+ regulator_desc_s2mps15_ldo(3, s2mps15_ldo_voltage_ranges1),
+ regulator_desc_s2mps15_ldo(4, s2mps15_ldo_voltage_ranges3),
+ regulator_desc_s2mps15_ldo(5, s2mps15_ldo_voltage_ranges1),
+ regulator_desc_s2mps15_ldo(6, s2mps15_ldo_voltage_ranges2),
+ regulator_desc_s2mps15_ldo(7, s2mps15_ldo_voltage_ranges4),
+ regulator_desc_s2mps15_ldo(8, s2mps15_ldo_voltage_ranges4),
+ regulator_desc_s2mps15_ldo(9, s2mps15_ldo_voltage_ranges4),
+ regulator_desc_s2mps15_ldo(10, s2mps15_ldo_voltage_ranges4),
+ regulator_desc_s2mps15_ldo(11, s2mps15_ldo_voltage_ranges3),
+ regulator_desc_s2mps15_ldo(12, s2mps15_ldo_voltage_ranges3),
+ regulator_desc_s2mps15_ldo(13, s2mps15_ldo_voltage_ranges3),
+ regulator_desc_s2mps15_ldo(14, s2mps15_ldo_voltage_ranges2),
+ regulator_desc_s2mps15_ldo(15, s2mps15_ldo_voltage_ranges1),
+ regulator_desc_s2mps15_ldo(16, s2mps15_ldo_voltage_ranges1),
+ regulator_desc_s2mps15_ldo(17, s2mps15_ldo_voltage_ranges2),
+ regulator_desc_s2mps15_ldo(18, s2mps15_ldo_voltage_ranges1),
+ regulator_desc_s2mps15_ldo(19, s2mps15_ldo_voltage_ranges2),
+ regulator_desc_s2mps15_ldo(20, s2mps15_ldo_voltage_ranges1),
+ regulator_desc_s2mps15_ldo(21, s2mps15_ldo_voltage_ranges2),
+ regulator_desc_s2mps15_ldo(22, s2mps15_ldo_voltage_ranges3),
+ regulator_desc_s2mps15_ldo(23, s2mps15_ldo_voltage_ranges1),
+ regulator_desc_s2mps15_ldo(24, s2mps15_ldo_voltage_ranges2),
+ regulator_desc_s2mps15_ldo(25, s2mps15_ldo_voltage_ranges2),
+ regulator_desc_s2mps15_ldo(26, s2mps15_ldo_voltage_ranges3),
+ regulator_desc_s2mps15_ldo(27, s2mps15_ldo_voltage_ranges1),
+ regulator_desc_s2mps15_buck(1, s2mps15_buck_voltage_ranges1),
+ regulator_desc_s2mps15_buck(2, s2mps15_buck_voltage_ranges1),
+ regulator_desc_s2mps15_buck(3, s2mps15_buck_voltage_ranges1),
+ regulator_desc_s2mps15_buck(4, s2mps15_buck_voltage_ranges1),
+ regulator_desc_s2mps15_buck(5, s2mps15_buck_voltage_ranges1),
+ regulator_desc_s2mps15_buck(6, s2mps15_buck_voltage_ranges1),
+ regulator_desc_s2mps15_buck(7, s2mps15_buck_voltage_ranges1),
+ regulator_desc_s2mps15_buck(8, s2mps15_buck_voltage_ranges2),
+ regulator_desc_s2mps15_buck(9, s2mps15_buck_voltage_ranges2),
+ regulator_desc_s2mps15_buck(10, s2mps15_buck_voltage_ranges2),
+};
+
static int s2mps14_pmic_enable_ext_control(struct s2mps11_info *s2mps11,
struct regulator_dev *rdev)
{
@@ -974,6 +1102,10 @@ static int s2mps11_pmic_probe(struct platform_device *pdev)
regulators = s2mps14_regulators;
BUILD_BUG_ON(S2MPS_REGULATOR_MAX < s2mps11->rdev_num);
break;
+ case S2MPS15X:
+ s2mps11->rdev_num = ARRAY_SIZE(s2mps15_regulators);
+ regulators = s2mps15_regulators;
+ break;
case S2MPU02:
s2mps11->rdev_num = ARRAY_SIZE(s2mpu02_regulators);
regulators = s2mpu02_regulators;
@@ -1067,10 +1199,11 @@ out:
}
static const struct platform_device_id s2mps11_pmic_id[] = {
- { "s2mps11-pmic", S2MPS11X},
- { "s2mps13-pmic", S2MPS13X},
- { "s2mps14-pmic", S2MPS14X},
- { "s2mpu02-pmic", S2MPU02},
+ { "s2mps11-regulator", S2MPS11X},
+ { "s2mps13-regulator", S2MPS13X},
+ { "s2mps14-regulator", S2MPS14X},
+ { "s2mps15-regulator", S2MPS15X},
+ { "s2mpu02-regulator", S2MPU02},
{ },
};
MODULE_DEVICE_TABLE(platform, s2mps11_pmic_id);
@@ -1097,5 +1230,5 @@ module_exit(s2mps11_pmic_exit);
/* Module information */
MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
-MODULE_DESCRIPTION("SAMSUNG S2MPS11/S2MPS14/S2MPU02 Regulator Driver");
+MODULE_DESCRIPTION("SAMSUNG S2MPS11/S2MPS14/S2MPS15/S2MPU02 Regulator Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/tps6105x-regulator.c b/drivers/regulator/tps6105x-regulator.c
index ddc4f10e268a..584ef3dedca6 100644
--- a/drivers/regulator/tps6105x-regulator.c
+++ b/drivers/regulator/tps6105x-regulator.c
@@ -27,90 +27,12 @@ static const unsigned int tps6105x_voltages[] = {
5000000, /* There is an additional 5V */
};
-static int tps6105x_regulator_enable(struct regulator_dev *rdev)
-{
- struct tps6105x *tps6105x = rdev_get_drvdata(rdev);
- int ret;
-
- /* Activate voltage mode */
- ret = regmap_update_bits(tps6105x->regmap, TPS6105X_REG_0,
- TPS6105X_REG0_MODE_MASK,
- TPS6105X_REG0_MODE_VOLTAGE << TPS6105X_REG0_MODE_SHIFT);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static int tps6105x_regulator_disable(struct regulator_dev *rdev)
-{
- struct tps6105x *tps6105x = rdev_get_drvdata(rdev);
- int ret;
-
- /* Set into shutdown mode */
- ret = regmap_update_bits(tps6105x->regmap, TPS6105X_REG_0,
- TPS6105X_REG0_MODE_MASK,
- TPS6105X_REG0_MODE_SHUTDOWN << TPS6105X_REG0_MODE_SHIFT);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static int tps6105x_regulator_is_enabled(struct regulator_dev *rdev)
-{
- struct tps6105x *tps6105x = rdev_get_drvdata(rdev);
- unsigned int regval;
- int ret;
-
- ret = regmap_read(tps6105x->regmap, TPS6105X_REG_0, &regval);
- if (ret)
- return ret;
- regval &= TPS6105X_REG0_MODE_MASK;
- regval >>= TPS6105X_REG0_MODE_SHIFT;
-
- if (regval == TPS6105X_REG0_MODE_VOLTAGE)
- return 1;
-
- return 0;
-}
-
-static int tps6105x_regulator_get_voltage_sel(struct regulator_dev *rdev)
-{
- struct tps6105x *tps6105x = rdev_get_drvdata(rdev);
- unsigned int regval;
- int ret;
-
- ret = regmap_read(tps6105x->regmap, TPS6105X_REG_0, &regval);
- if (ret)
- return ret;
-
- regval &= TPS6105X_REG0_VOLTAGE_MASK;
- regval >>= TPS6105X_REG0_VOLTAGE_SHIFT;
- return (int) regval;
-}
-
-static int tps6105x_regulator_set_voltage_sel(struct regulator_dev *rdev,
- unsigned selector)
-{
- struct tps6105x *tps6105x = rdev_get_drvdata(rdev);
- int ret;
-
- ret = regmap_update_bits(tps6105x->regmap, TPS6105X_REG_0,
- TPS6105X_REG0_VOLTAGE_MASK,
- selector << TPS6105X_REG0_VOLTAGE_SHIFT);
- if (ret)
- return ret;
-
- return 0;
-}
-
static struct regulator_ops tps6105x_regulator_ops = {
- .enable = tps6105x_regulator_enable,
- .disable = tps6105x_regulator_disable,
- .is_enabled = tps6105x_regulator_is_enabled,
- .get_voltage_sel = tps6105x_regulator_get_voltage_sel,
- .set_voltage_sel = tps6105x_regulator_set_voltage_sel,
+ .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_table,
};
@@ -122,6 +44,12 @@ static const struct regulator_desc tps6105x_regulator_desc = {
.owner = THIS_MODULE,
.n_voltages = ARRAY_SIZE(tps6105x_voltages),
.volt_table = tps6105x_voltages,
+ .vsel_reg = TPS6105X_REG_0,
+ .vsel_mask = TPS6105X_REG0_VOLTAGE_MASK,
+ .enable_reg = TPS6105X_REG_0,
+ .enable_mask = TPS6105X_REG0_MODE_MASK,
+ .enable_val = TPS6105X_REG0_MODE_VOLTAGE <<
+ TPS6105X_REG0_MODE_SHIFT,
};
/*
@@ -144,6 +72,7 @@ static int tps6105x_regulator_probe(struct platform_device *pdev)
config.dev = &tps6105x->client->dev;
config.init_data = pdata->regulator_data;
config.driver_data = tps6105x;
+ config.regmap = tps6105x->regmap;
/* Register regulator with framework */
tps6105x->regulator = devm_regulator_register(&pdev->dev,
diff --git a/drivers/regulator/tps65086-regulator.c b/drivers/regulator/tps65086-regulator.c
new file mode 100644
index 000000000000..33f389d583ef
--- /dev/null
+++ b/drivers/regulator/tps65086-regulator.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * Author: Andrew F. Davis <afd@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether expressed or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ *
+ * Based on the TPS65912 driver
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+
+#include <linux/mfd/tps65086.h>
+
+enum tps65086_regulators { BUCK1, BUCK2, BUCK3, BUCK4, BUCK5, BUCK6, LDOA1,
+ LDOA2, LDOA3, SWA1, SWB1, SWB2, VTT };
+
+#define TPS65086_REGULATOR(_name, _of, _id, _nv, _vr, _vm, _er, _em, _lr, _dr, _dm) \
+ [_id] = { \
+ .desc = { \
+ .name = _name, \
+ .of_match = of_match_ptr(_of), \
+ .regulators_node = "regulators", \
+ .of_parse_cb = tps65086_of_parse_cb, \
+ .id = _id, \
+ .ops = &reg_ops, \
+ .n_voltages = _nv, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .vsel_reg = _vr, \
+ .vsel_mask = _vm, \
+ .enable_reg = _er, \
+ .enable_mask = _em, \
+ .volt_table = NULL, \
+ .linear_ranges = _lr, \
+ .n_linear_ranges = ARRAY_SIZE(_lr), \
+ }, \
+ .decay_reg = _dr, \
+ .decay_mask = _dm, \
+ }
+
+#define TPS65086_SWITCH(_name, _of, _id, _er, _em) \
+ [_id] = { \
+ .desc = { \
+ .name = _name, \
+ .of_match = of_match_ptr(_of), \
+ .regulators_node = "regulators", \
+ .of_parse_cb = tps65086_of_parse_cb, \
+ .id = _id, \
+ .ops = &switch_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .enable_reg = _er, \
+ .enable_mask = _em, \
+ }, \
+ }
+
+struct tps65086_regulator {
+ struct regulator_desc desc;
+ unsigned int decay_reg;
+ unsigned int decay_mask;
+};
+
+static const struct regulator_linear_range tps65086_buck126_10mv_ranges[] = {
+ REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0),
+ REGULATOR_LINEAR_RANGE(410000, 0x1, 0x7F, 10000),
+};
+
+static const struct regulator_linear_range tps65086_buck126_25mv_ranges[] = {
+ REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0),
+ REGULATOR_LINEAR_RANGE(1000000, 0x1, 0x18, 0),
+ REGULATOR_LINEAR_RANGE(1025000, 0x19, 0x7F, 25000),
+};
+
+static const struct regulator_linear_range tps65086_buck345_ranges[] = {
+ REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0),
+ REGULATOR_LINEAR_RANGE(425000, 0x1, 0x7F, 25000),
+};
+
+static const struct regulator_linear_range tps65086_ldoa1_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1350000, 0x0, 0x0, 0),
+ REGULATOR_LINEAR_RANGE(1500000, 0x1, 0x7, 100000),
+ REGULATOR_LINEAR_RANGE(2300000, 0x8, 0xA, 100000),
+ REGULATOR_LINEAR_RANGE(2700000, 0xB, 0xD, 150000),
+ REGULATOR_LINEAR_RANGE(3300000, 0xE, 0xE, 0),
+};
+
+static const struct regulator_linear_range tps65086_ldoa23_ranges[] = {
+ REGULATOR_LINEAR_RANGE(700000, 0x0, 0xD, 50000),
+ REGULATOR_LINEAR_RANGE(1400000, 0xE, 0xF, 100000),
+};
+
+/* Operations permitted on regulators */
+static struct regulator_ops reg_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+};
+
+/* Operations permitted on load switches */
+static struct regulator_ops switch_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static int tps65086_of_parse_cb(struct device_node *dev,
+ const struct regulator_desc *desc,
+ struct regulator_config *config);
+
+static struct tps65086_regulator regulators[] = {
+ TPS65086_REGULATOR("BUCK1", "buck1", BUCK1, 0x80, TPS65086_BUCK1CTRL,
+ BUCK_VID_MASK, TPS65086_BUCK123CTRL, BIT(0),
+ tps65086_buck126_10mv_ranges, TPS65086_BUCK1CTRL,
+ BIT(0)),
+ TPS65086_REGULATOR("BUCK2", "buck2", BUCK2, 0x80, TPS65086_BUCK2CTRL,
+ BUCK_VID_MASK, TPS65086_BUCK123CTRL, BIT(1),
+ tps65086_buck126_10mv_ranges, TPS65086_BUCK2CTRL,
+ BIT(0)),
+ TPS65086_REGULATOR("BUCK3", "buck3", BUCK3, 0x80, TPS65086_BUCK3VID,
+ BUCK_VID_MASK, TPS65086_BUCK123CTRL, BIT(2),
+ tps65086_buck345_ranges, TPS65086_BUCK3DECAY,
+ BIT(0)),
+ TPS65086_REGULATOR("BUCK4", "buck4", BUCK4, 0x80, TPS65086_BUCK4VID,
+ BUCK_VID_MASK, TPS65086_BUCK4CTRL, BIT(0),
+ tps65086_buck345_ranges, TPS65086_BUCK4VID,
+ BIT(0)),
+ TPS65086_REGULATOR("BUCK5", "buck5", BUCK5, 0x80, TPS65086_BUCK5VID,
+ BUCK_VID_MASK, TPS65086_BUCK5CTRL, BIT(0),
+ tps65086_buck345_ranges, TPS65086_BUCK5CTRL,
+ BIT(0)),
+ TPS65086_REGULATOR("BUCK6", "buck6", BUCK6, 0x80, TPS65086_BUCK6VID,
+ BUCK_VID_MASK, TPS65086_BUCK6CTRL, BIT(0),
+ tps65086_buck126_10mv_ranges, TPS65086_BUCK6CTRL,
+ BIT(0)),
+ TPS65086_REGULATOR("LDOA1", "ldoa1", LDOA1, 0xF, TPS65086_LDOA1CTRL,
+ VDOA1_VID_MASK, TPS65086_LDOA1CTRL, BIT(0),
+ tps65086_ldoa1_ranges, 0, 0),
+ TPS65086_REGULATOR("LDOA2", "ldoa2", LDOA2, 0x10, TPS65086_LDOA2VID,
+ VDOA23_VID_MASK, TPS65086_LDOA2CTRL, BIT(0),
+ tps65086_ldoa23_ranges, 0, 0),
+ TPS65086_REGULATOR("LDOA3", "ldoa3", LDOA3, 0x10, TPS65086_LDOA3VID,
+ VDOA23_VID_MASK, TPS65086_LDOA3CTRL, BIT(0),
+ tps65086_ldoa23_ranges, 0, 0),
+ TPS65086_SWITCH("SWA1", "swa1", SWA1, TPS65086_SWVTT_EN, BIT(5)),
+ TPS65086_SWITCH("SWB1", "swa2", SWB1, TPS65086_SWVTT_EN, BIT(6)),
+ TPS65086_SWITCH("SWB2", "swa3", SWB2, TPS65086_SWVTT_EN, BIT(7)),
+ TPS65086_SWITCH("VTT", "vtt", VTT, TPS65086_SWVTT_EN, BIT(4)),
+};
+
+static inline bool has_25mv_mode(int id)
+{
+ switch (id) {
+ case BUCK1:
+ case BUCK2:
+ case BUCK6:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int tps65086_of_parse_cb(struct device_node *dev,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ int ret;
+
+ /* Check for 25mV step mode */
+ if (has_25mv_mode(desc->id) &&
+ of_property_read_bool(config->of_node, "ti,regulator-step-size-25mv")) {
+ regulators[desc->id].desc.linear_ranges =
+ tps65086_buck126_25mv_ranges;
+ regulators[desc->id].desc.n_linear_ranges =
+ ARRAY_SIZE(tps65086_buck126_25mv_ranges);
+ }
+
+ /* Check for decay mode */
+ if (desc->id <= BUCK6 && of_property_read_bool(config->of_node, "ti,regulator-decay")) {
+ ret = regmap_write_bits(config->regmap,
+ regulators[desc->id].decay_reg,
+ regulators[desc->id].decay_mask,
+ regulators[desc->id].decay_mask);
+ if (ret) {
+ dev_err(config->dev, "Error setting decay\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int tps65086_regulator_probe(struct platform_device *pdev)
+{
+ struct tps65086 *tps = dev_get_drvdata(pdev->dev.parent);
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ int i;
+
+ platform_set_drvdata(pdev, tps);
+
+ config.dev = &pdev->dev;
+ config.dev->of_node = tps->dev->of_node;
+ config.driver_data = tps;
+ config.regmap = tps->regmap;
+
+ for (i = 0; i < ARRAY_SIZE(regulators); i++) {
+ rdev = devm_regulator_register(&pdev->dev, &regulators[i].desc,
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(tps->dev, "failed to register %s regulator\n",
+ pdev->name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id tps65086_regulator_id_table[] = {
+ { "tps65086-regulator", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, tps65086_regulator_id_table);
+
+static struct platform_driver tps65086_regulator_driver = {
+ .driver = {
+ .name = "tps65086-regulator",
+ },
+ .probe = tps65086_regulator_probe,
+ .id_table = tps65086_regulator_id_table,
+};
+module_platform_driver(tps65086_regulator_driver);
+
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+MODULE_DESCRIPTION("TPS65086 Regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/tps65218-regulator.c b/drivers/regulator/tps65218-regulator.c
index a02c1b961039..a5e5634eeb9e 100644
--- a/drivers/regulator/tps65218-regulator.c
+++ b/drivers/regulator/tps65218-regulator.c
@@ -27,19 +27,22 @@
#include <linux/regulator/machine.h>
#include <linux/mfd/tps65218.h>
-enum tps65218_regulators { DCDC1, DCDC2, DCDC3, DCDC4, DCDC5, DCDC6, LDO1 };
+enum tps65218_regulators { DCDC1, DCDC2, DCDC3, DCDC4,
+ DCDC5, DCDC6, LDO1, LS3 };
-#define TPS65218_REGULATOR(_name, _id, _ops, _n, _vr, _vm, _er, _em, \
- _lr, _nlr, _delay, _fuv) \
+#define TPS65218_REGULATOR(_name, _id, _type, _ops, _n, _vr, _vm, _er, _em, \
+ _cr, _cm, _lr, _nlr, _delay, _fuv) \
{ \
.name = _name, \
.id = _id, \
.ops = &_ops, \
.n_voltages = _n, \
- .type = REGULATOR_VOLTAGE, \
+ .type = _type, \
.owner = THIS_MODULE, \
.vsel_reg = _vr, \
.vsel_mask = _vm, \
+ .csel_reg = _cr, \
+ .csel_mask = _cm, \
.enable_reg = _er, \
.enable_mask = _em, \
.volt_table = NULL, \
@@ -80,6 +83,7 @@ static struct tps_info tps65218_pmic_regs[] = {
TPS65218_INFO(DCDC5, "DCDC5", 1000000, 1000000),
TPS65218_INFO(DCDC6, "DCDC6", 1800000, 1800000),
TPS65218_INFO(LDO1, "LDO1", 900000, 3400000),
+ TPS65218_INFO(LS3, "LS3", -1, -1),
};
#define TPS65218_OF_MATCH(comp, label) \
@@ -96,6 +100,7 @@ static const struct of_device_id tps65218_of_match[] = {
TPS65218_OF_MATCH("ti,tps65218-dcdc5", tps65218_pmic_regs[DCDC5]),
TPS65218_OF_MATCH("ti,tps65218-dcdc6", tps65218_pmic_regs[DCDC6]),
TPS65218_OF_MATCH("ti,tps65218-ldo1", tps65218_pmic_regs[LDO1]),
+ TPS65218_OF_MATCH("ti,tps65218-ls3", tps65218_pmic_regs[LS3]),
{ }
};
MODULE_DEVICE_TABLE(of, tps65218_of_match);
@@ -175,6 +180,68 @@ static struct regulator_ops tps65218_ldo1_dcdc34_ops = {
.map_voltage = regulator_map_voltage_linear_range,
};
+static const int ls3_currents[] = { 100, 200, 500, 1000 };
+
+static int tps65218_pmic_set_input_current_lim(struct regulator_dev *dev,
+ int lim_uA)
+{
+ unsigned int index = 0;
+ unsigned int num_currents = ARRAY_SIZE(ls3_currents);
+ struct tps65218 *tps = rdev_get_drvdata(dev);
+
+ while (index < num_currents && ls3_currents[index] != lim_uA)
+ index++;
+
+ if (index == num_currents)
+ return -EINVAL;
+
+ return tps65218_set_bits(tps, dev->desc->csel_reg, dev->desc->csel_mask,
+ index << 2, TPS65218_PROTECT_L1);
+}
+
+static int tps65218_pmic_set_current_limit(struct regulator_dev *dev,
+ int min_uA, int max_uA)
+{
+ int index = 0;
+ unsigned int num_currents = ARRAY_SIZE(ls3_currents);
+ struct tps65218 *tps = rdev_get_drvdata(dev);
+
+ while (index < num_currents && ls3_currents[index] < max_uA)
+ index++;
+
+ index--;
+
+ if (index < 0 || ls3_currents[index] < min_uA)
+ return -EINVAL;
+
+ return tps65218_set_bits(tps, dev->desc->csel_reg, dev->desc->csel_mask,
+ index << 2, TPS65218_PROTECT_L1);
+}
+
+static int tps65218_pmic_get_current_limit(struct regulator_dev *dev)
+{
+ int retval;
+ unsigned int index;
+ struct tps65218 *tps = rdev_get_drvdata(dev);
+
+ retval = tps65218_reg_read(tps, dev->desc->csel_reg, &index);
+ if (retval < 0)
+ return retval;
+
+ index = (index & dev->desc->csel_mask) >> 2;
+
+ return ls3_currents[index];
+}
+
+static struct regulator_ops tps65218_ls3_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = tps65218_pmic_enable,
+ .disable = tps65218_pmic_disable,
+ .set_input_current_limit = tps65218_pmic_set_input_current_lim,
+ .set_current_limit = tps65218_pmic_set_current_limit,
+ .get_current_limit = tps65218_pmic_get_current_limit,
+};
+
/* Operations permitted on DCDC5, DCDC6 */
static struct regulator_ops tps65218_dcdc56_pmic_ops = {
.is_enabled = regulator_is_enabled_regmap,
@@ -183,36 +250,46 @@ static struct regulator_ops tps65218_dcdc56_pmic_ops = {
};
static const struct regulator_desc regulators[] = {
- TPS65218_REGULATOR("DCDC1", TPS65218_DCDC_1, tps65218_dcdc12_ops, 64,
- TPS65218_REG_CONTROL_DCDC1,
- TPS65218_CONTROL_DCDC1_MASK,
- TPS65218_REG_ENABLE1, TPS65218_ENABLE1_DC1_EN,
- dcdc1_dcdc2_ranges, 2, 4000, 0),
- TPS65218_REGULATOR("DCDC2", TPS65218_DCDC_2, tps65218_dcdc12_ops, 64,
- TPS65218_REG_CONTROL_DCDC2,
- TPS65218_CONTROL_DCDC2_MASK,
- TPS65218_REG_ENABLE1, TPS65218_ENABLE1_DC2_EN,
- dcdc1_dcdc2_ranges, 2, 4000, 0),
- TPS65218_REGULATOR("DCDC3", TPS65218_DCDC_3, tps65218_ldo1_dcdc34_ops,
- 64, TPS65218_REG_CONTROL_DCDC3,
+ TPS65218_REGULATOR("DCDC1", TPS65218_DCDC_1, REGULATOR_VOLTAGE,
+ tps65218_dcdc12_ops, 64, TPS65218_REG_CONTROL_DCDC1,
+ TPS65218_CONTROL_DCDC1_MASK, TPS65218_REG_ENABLE1,
+ TPS65218_ENABLE1_DC1_EN, 0, 0, dcdc1_dcdc2_ranges,
+ 2, 4000, 0),
+ TPS65218_REGULATOR("DCDC2", TPS65218_DCDC_2, REGULATOR_VOLTAGE,
+ tps65218_dcdc12_ops, 64, TPS65218_REG_CONTROL_DCDC2,
+ TPS65218_CONTROL_DCDC2_MASK, TPS65218_REG_ENABLE1,
+ TPS65218_ENABLE1_DC2_EN, 0, 0, dcdc1_dcdc2_ranges,
+ 2, 4000, 0),
+ TPS65218_REGULATOR("DCDC3", TPS65218_DCDC_3, REGULATOR_VOLTAGE,
+ tps65218_ldo1_dcdc34_ops, 64,
+ TPS65218_REG_CONTROL_DCDC3,
TPS65218_CONTROL_DCDC3_MASK, TPS65218_REG_ENABLE1,
- TPS65218_ENABLE1_DC3_EN, ldo1_dcdc3_ranges, 2, 0, 0),
- TPS65218_REGULATOR("DCDC4", TPS65218_DCDC_4, tps65218_ldo1_dcdc34_ops,
- 53, TPS65218_REG_CONTROL_DCDC4,
- TPS65218_CONTROL_DCDC4_MASK,
- TPS65218_REG_ENABLE1, TPS65218_ENABLE1_DC4_EN,
- dcdc4_ranges, 2, 0, 0),
- TPS65218_REGULATOR("DCDC5", TPS65218_DCDC_5, tps65218_dcdc56_pmic_ops,
- 1, -1, -1, TPS65218_REG_ENABLE1,
- TPS65218_ENABLE1_DC5_EN, NULL, 0, 0, 1000000),
- TPS65218_REGULATOR("DCDC6", TPS65218_DCDC_6, tps65218_dcdc56_pmic_ops,
- 1, -1, -1, TPS65218_REG_ENABLE1,
- TPS65218_ENABLE1_DC6_EN, NULL, 0, 0, 1800000),
- TPS65218_REGULATOR("LDO1", TPS65218_LDO_1, tps65218_ldo1_dcdc34_ops, 64,
+ TPS65218_ENABLE1_DC3_EN, 0, 0, ldo1_dcdc3_ranges, 2,
+ 0, 0),
+ TPS65218_REGULATOR("DCDC4", TPS65218_DCDC_4, REGULATOR_VOLTAGE,
+ tps65218_ldo1_dcdc34_ops, 53,
+ TPS65218_REG_CONTROL_DCDC4,
+ TPS65218_CONTROL_DCDC4_MASK, TPS65218_REG_ENABLE1,
+ TPS65218_ENABLE1_DC4_EN, 0, 0, dcdc4_ranges, 2,
+ 0, 0),
+ TPS65218_REGULATOR("DCDC5", TPS65218_DCDC_5, REGULATOR_VOLTAGE,
+ tps65218_dcdc56_pmic_ops, 1, -1, -1,
+ TPS65218_REG_ENABLE1, TPS65218_ENABLE1_DC5_EN, 0, 0,
+ NULL, 0, 0, 1000000),
+ TPS65218_REGULATOR("DCDC6", TPS65218_DCDC_6, REGULATOR_VOLTAGE,
+ tps65218_dcdc56_pmic_ops, 1, -1, -1,
+ TPS65218_REG_ENABLE1, TPS65218_ENABLE1_DC6_EN, 0, 0,
+ NULL, 0, 0, 1800000),
+ TPS65218_REGULATOR("LDO1", TPS65218_LDO_1, REGULATOR_VOLTAGE,
+ tps65218_ldo1_dcdc34_ops, 64,
TPS65218_REG_CONTROL_LDO1,
TPS65218_CONTROL_LDO1_MASK, TPS65218_REG_ENABLE2,
- TPS65218_ENABLE2_LDO1_EN, ldo1_dcdc3_ranges,
+ TPS65218_ENABLE2_LDO1_EN, 0, 0, ldo1_dcdc3_ranges,
2, 0, 0),
+ TPS65218_REGULATOR("LS3", TPS65218_LS_3, REGULATOR_CURRENT,
+ tps65218_ls3_ops, 0, 0, 0, TPS65218_REG_ENABLE2,
+ TPS65218_ENABLE2_LS3_EN, TPS65218_REG_CONFIG2,
+ TPS65218_CONFIG2_LS3ILIM_MASK, NULL, 0, 0, 0),
};
static int tps65218_regulator_probe(struct platform_device *pdev)
diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c
index 8cbb82ceec40..5a5bc4bb08d2 100644
--- a/drivers/regulator/wm831x-dcdc.c
+++ b/drivers/regulator/wm831x-dcdc.c
@@ -365,7 +365,7 @@ static int wm831x_buckv_get_current_limit(struct regulator_dev *rdev)
return wm831x_dcdc_ilim[val];
}
-static struct regulator_ops wm831x_buckv_ops = {
+static const struct regulator_ops wm831x_buckv_ops = {
.set_voltage_sel = wm831x_buckv_set_voltage_sel,
.get_voltage_sel = wm831x_buckv_get_voltage_sel,
.list_voltage = wm831x_buckv_list_voltage,
@@ -585,7 +585,7 @@ static int wm831x_buckp_set_suspend_voltage(struct regulator_dev *rdev, int uV)
return wm831x_set_bits(wm831x, reg, WM831X_DC3_ON_VSEL_MASK, sel);
}
-static struct regulator_ops wm831x_buckp_ops = {
+static const struct regulator_ops wm831x_buckp_ops = {
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.list_voltage = regulator_list_voltage_linear,
@@ -725,7 +725,7 @@ static int wm831x_boostp_get_status(struct regulator_dev *rdev)
return REGULATOR_STATUS_OFF;
}
-static struct regulator_ops wm831x_boostp_ops = {
+static const struct regulator_ops wm831x_boostp_ops = {
.get_status = wm831x_boostp_get_status,
.is_enabled = regulator_is_enabled_regmap,
@@ -818,7 +818,7 @@ static struct platform_driver wm831x_boostp_driver = {
#define WM831X_EPE_BASE 6
-static struct regulator_ops wm831x_epe_ops = {
+static const struct regulator_ops wm831x_epe_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
@@ -884,35 +884,22 @@ static struct platform_driver wm831x_epe_driver = {
},
};
+static struct platform_driver * const drivers[] = {
+ &wm831x_buckv_driver,
+ &wm831x_buckp_driver,
+ &wm831x_boostp_driver,
+ &wm831x_epe_driver,
+};
+
static int __init wm831x_dcdc_init(void)
{
- int ret;
- ret = platform_driver_register(&wm831x_buckv_driver);
- if (ret != 0)
- pr_err("Failed to register WM831x BUCKV driver: %d\n", ret);
-
- ret = platform_driver_register(&wm831x_buckp_driver);
- if (ret != 0)
- pr_err("Failed to register WM831x BUCKP driver: %d\n", ret);
-
- ret = platform_driver_register(&wm831x_boostp_driver);
- if (ret != 0)
- pr_err("Failed to register WM831x BOOST driver: %d\n", ret);
-
- ret = platform_driver_register(&wm831x_epe_driver);
- if (ret != 0)
- pr_err("Failed to register WM831x EPE driver: %d\n", ret);
-
- return 0;
+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
}
subsys_initcall(wm831x_dcdc_init);
static void __exit wm831x_dcdc_exit(void)
{
- platform_driver_unregister(&wm831x_epe_driver);
- platform_driver_unregister(&wm831x_boostp_driver);
- platform_driver_unregister(&wm831x_buckp_driver);
- platform_driver_unregister(&wm831x_buckv_driver);
+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
}
module_exit(wm831x_dcdc_exit);
diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c
index 1442828fcd9a..6dd891d7eee3 100644
--- a/drivers/regulator/wm831x-isink.c
+++ b/drivers/regulator/wm831x-isink.c
@@ -128,7 +128,7 @@ static int wm831x_isink_get_current(struct regulator_dev *rdev)
return wm831x_isinkv_values[ret];
}
-static struct regulator_ops wm831x_isink_ops = {
+static const struct regulator_ops wm831x_isink_ops = {
.is_enabled = wm831x_isink_is_enabled,
.enable = wm831x_isink_enable,
.disable = wm831x_isink_disable,
diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c
index 5a7b65e8a529..e4a6f888484e 100644
--- a/drivers/regulator/wm831x-ldo.c
+++ b/drivers/regulator/wm831x-ldo.c
@@ -198,7 +198,7 @@ static unsigned int wm831x_gp_ldo_get_optimum_mode(struct regulator_dev *rdev,
}
-static struct regulator_ops wm831x_gp_ldo_ops = {
+static const struct regulator_ops wm831x_gp_ldo_ops = {
.list_voltage = regulator_list_voltage_linear_range,
.map_voltage = regulator_map_voltage_linear_range,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
@@ -409,7 +409,7 @@ static int wm831x_aldo_get_status(struct regulator_dev *rdev)
return regulator_mode_to_status(ret);
}
-static struct regulator_ops wm831x_aldo_ops = {
+static const struct regulator_ops wm831x_aldo_ops = {
.list_voltage = regulator_list_voltage_linear_range,
.map_voltage = regulator_map_voltage_linear_range,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
@@ -557,7 +557,7 @@ static int wm831x_alive_ldo_get_status(struct regulator_dev *rdev)
return REGULATOR_STATUS_OFF;
}
-static struct regulator_ops wm831x_alive_ldo_ops = {
+static const struct regulator_ops wm831x_alive_ldo_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
@@ -653,32 +653,21 @@ static struct platform_driver wm831x_alive_ldo_driver = {
},
};
+static struct platform_driver * const drivers[] = {
+ &wm831x_gp_ldo_driver,
+ &wm831x_aldo_driver,
+ &wm831x_alive_ldo_driver,
+};
+
static int __init wm831x_ldo_init(void)
{
- int ret;
-
- ret = platform_driver_register(&wm831x_gp_ldo_driver);
- if (ret != 0)
- pr_err("Failed to register WM831x GP LDO driver: %d\n", ret);
-
- ret = platform_driver_register(&wm831x_aldo_driver);
- if (ret != 0)
- pr_err("Failed to register WM831x ALDO driver: %d\n", ret);
-
- ret = platform_driver_register(&wm831x_alive_ldo_driver);
- if (ret != 0)
- pr_err("Failed to register WM831x alive LDO driver: %d\n",
- ret);
-
- return 0;
+ return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
}
subsys_initcall(wm831x_ldo_init);
static void __exit wm831x_ldo_exit(void)
{
- platform_driver_unregister(&wm831x_alive_ldo_driver);
- platform_driver_unregister(&wm831x_aldo_driver);
- platform_driver_unregister(&wm831x_gp_ldo_driver);
+ platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
}
module_exit(wm831x_ldo_exit);
diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c
index 95f6b040186e..da9106bd2109 100644
--- a/drivers/regulator/wm8350-regulator.c
+++ b/drivers/regulator/wm8350-regulator.c
@@ -941,7 +941,7 @@ static unsigned int wm8350_dcdc_get_optimum_mode(struct regulator_dev *rdev,
return mode;
}
-static struct regulator_ops wm8350_dcdc_ops = {
+static const struct regulator_ops wm8350_dcdc_ops = {
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.list_voltage = regulator_list_voltage_linear,
@@ -958,7 +958,7 @@ static struct regulator_ops wm8350_dcdc_ops = {
.set_suspend_mode = wm8350_dcdc_set_suspend_mode,
};
-static struct regulator_ops wm8350_dcdc2_5_ops = {
+static const struct regulator_ops wm8350_dcdc2_5_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
@@ -966,7 +966,7 @@ static struct regulator_ops wm8350_dcdc2_5_ops = {
.set_suspend_disable = wm8350_dcdc25_set_suspend_disable,
};
-static struct regulator_ops wm8350_ldo_ops = {
+static const struct regulator_ops wm8350_ldo_ops = {
.map_voltage = regulator_map_voltage_linear_range,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
@@ -980,7 +980,7 @@ static struct regulator_ops wm8350_ldo_ops = {
.set_suspend_disable = wm8350_ldo_set_suspend_disable,
};
-static struct regulator_ops wm8350_isink_ops = {
+static const struct regulator_ops wm8350_isink_ops = {
.set_current_limit = wm8350_isink_set_current,
.get_current_limit = wm8350_isink_get_current,
.enable = wm8350_isink_enable,
diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c
index 82d829000851..fb1837657b64 100644
--- a/drivers/regulator/wm8400-regulator.c
+++ b/drivers/regulator/wm8400-regulator.c
@@ -24,7 +24,7 @@ static const struct regulator_linear_range wm8400_ldo_ranges[] = {
REGULATOR_LINEAR_RANGE(1700000, 15, 31, 100000),
};
-static struct regulator_ops wm8400_ldo_ops = {
+static const struct regulator_ops wm8400_ldo_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
@@ -106,7 +106,7 @@ static unsigned int wm8400_dcdc_get_optimum_mode(struct regulator_dev *dev,
return REGULATOR_MODE_NORMAL;
}
-static struct regulator_ops wm8400_dcdc_ops = {
+static const struct regulator_ops wm8400_dcdc_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c
index 750e0bd61f9f..7a4ce6df4f22 100644
--- a/drivers/regulator/wm8994-regulator.c
+++ b/drivers/regulator/wm8994-regulator.c
@@ -36,7 +36,7 @@ struct wm8994_ldo {
#define WM8994_LDO1_MAX_SELECTOR 0x7
#define WM8994_LDO2_MAX_SELECTOR 0x3
-static struct regulator_ops wm8994_ldo1_ops = {
+static const struct regulator_ops wm8994_ldo1_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
@@ -69,7 +69,7 @@ static int wm8994_ldo2_list_voltage(struct regulator_dev *rdev,
}
}
-static struct regulator_ops wm8994_ldo2_ops = {
+static const struct regulator_ops wm8994_ldo2_ops = {
.list_voltage = wm8994_ldo2_list_voltage,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index 8b3130f22b42..9e03d158f411 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -1478,6 +1478,8 @@ module_init(remoteproc_init);
static void __exit remoteproc_exit(void)
{
+ ida_destroy(&rproc_dev_index);
+
rproc_exit_debugfs();
}
module_exit(remoteproc_exit);
diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c
index 9d30809bb407..916af5096f57 100644
--- a/drivers/remoteproc/remoteproc_debugfs.c
+++ b/drivers/remoteproc/remoteproc_debugfs.c
@@ -156,7 +156,7 @@ rproc_recovery_write(struct file *filp, const char __user *user_buf,
char buf[10];
int ret;
- if (count > sizeof(buf))
+ if (count < 1 || count > sizeof(buf))
return count;
ret = copy_from_user(buf, user_buf, count);
diff --git a/drivers/rtc/rtc-da9063.c b/drivers/rtc/rtc-da9063.c
index 284b587da65c..d6c853bbfa9f 100644
--- a/drivers/rtc/rtc-da9063.c
+++ b/drivers/rtc/rtc-da9063.c
@@ -483,24 +483,23 @@ static int da9063_rtc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, rtc);
+ rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, DA9063_DRVNAME_RTC,
+ &da9063_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc_dev))
+ return PTR_ERR(rtc->rtc_dev);
+
+ da9063_data_to_tm(data, &rtc->alarm_time, rtc);
+ rtc->rtc_sync = false;
+
irq_alarm = platform_get_irq_byname(pdev, "ALARM");
ret = devm_request_threaded_irq(&pdev->dev, irq_alarm, NULL,
da9063_alarm_event,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"ALARM", rtc);
- if (ret) {
+ if (ret)
dev_err(&pdev->dev, "Failed to request ALARM IRQ %d: %d\n",
irq_alarm, ret);
- return ret;
- }
-
- rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, DA9063_DRVNAME_RTC,
- &da9063_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc->rtc_dev))
- return PTR_ERR(rtc->rtc_dev);
- da9063_data_to_tm(data, &rtc->alarm_time, rtc);
- rtc->rtc_sync = false;
return ret;
}
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 188006c55ce0..aa705bb4748c 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -15,9 +15,6 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
-#include <linux/pm_wakeirq.h>
#include <linux/rtc/ds1307.h>
#include <linux/rtc.h>
#include <linux/slab.h>
@@ -117,7 +114,6 @@ struct ds1307 {
#define HAS_ALARM 1 /* bit 1 == irq claimed */
struct i2c_client *client;
struct rtc_device *rtc;
- int wakeirq;
s32 (*read_block_data)(const struct i2c_client *client, u8 command,
u8 length, u8 *values);
s32 (*write_block_data)(const struct i2c_client *client, u8 command,
@@ -1138,7 +1134,10 @@ read_rtc:
bin2bcd(tmp));
}
- device_set_wakeup_capable(&client->dev, want_irq);
+ if (want_irq) {
+ device_set_wakeup_capable(&client->dev, true);
+ set_bit(HAS_ALARM, &ds1307->flags);
+ }
ds1307->rtc = devm_rtc_device_register(&client->dev, client->name,
rtc_ops, THIS_MODULE);
if (IS_ERR(ds1307->rtc)) {
@@ -1146,43 +1145,19 @@ read_rtc:
}
if (want_irq) {
- struct device_node *node = client->dev.of_node;
-
err = devm_request_threaded_irq(&client->dev,
client->irq, NULL, irq_handler,
IRQF_SHARED | IRQF_ONESHOT,
ds1307->rtc->name, client);
if (err) {
client->irq = 0;
+ device_set_wakeup_capable(&client->dev, false);
+ clear_bit(HAS_ALARM, &ds1307->flags);
dev_err(&client->dev, "unable to request IRQ!\n");
- goto no_irq;
- }
-
- set_bit(HAS_ALARM, &ds1307->flags);
- dev_dbg(&client->dev, "got IRQ %d\n", client->irq);
-
- /* Currently supported by OF code only! */
- if (!node)
- goto no_irq;
-
- err = of_irq_get(node, 1);
- if (err <= 0) {
- if (err == -EPROBE_DEFER)
- goto exit;
- goto no_irq;
- }
- ds1307->wakeirq = err;
-
- err = dev_pm_set_dedicated_wake_irq(&client->dev,
- ds1307->wakeirq);
- if (err) {
- dev_err(&client->dev, "unable to setup wakeIRQ %d!\n",
- err);
- goto exit;
- }
+ } else
+ dev_dbg(&client->dev, "got IRQ %d\n", client->irq);
}
-no_irq:
if (chip->nvram_size) {
ds1307->nvram = devm_kzalloc(&client->dev,
@@ -1226,9 +1201,6 @@ static int ds1307_remove(struct i2c_client *client)
{
struct ds1307 *ds1307 = i2c_get_clientdata(client);
- if (ds1307->wakeirq)
- dev_pm_clear_wake_irq(&client->dev);
-
if (test_and_clear_bit(HAS_NVRAM, &ds1307->flags))
sysfs_remove_bin_file(&client->dev.kobj, ds1307->nvram);
diff --git a/drivers/rtc/rtc-rk808.c b/drivers/rtc/rtc-rk808.c
index 91ca0bc1b484..35c9aada07c8 100644
--- a/drivers/rtc/rtc-rk808.c
+++ b/drivers/rtc/rtc-rk808.c
@@ -56,6 +56,42 @@ struct rk808_rtc {
int irq;
};
+/*
+ * The Rockchip calendar used by the RK808 counts November with 31 days. We use
+ * these translation functions to convert its dates to/from the Gregorian
+ * calendar used by the rest of the world. We arbitrarily define Jan 1st, 2016
+ * as the day when both calendars were in sync, and treat all other dates
+ * relative to that.
+ * NOTE: Other system software (e.g. firmware) that reads the same hardware must
+ * implement this exact same conversion algorithm, with the same anchor date.
+ */
+static time64_t nov2dec_transitions(struct rtc_time *tm)
+{
+ return (tm->tm_year + 1900) - 2016 + (tm->tm_mon + 1 > 11 ? 1 : 0);
+}
+
+static void rockchip_to_gregorian(struct rtc_time *tm)
+{
+ /* If it's Nov 31st, rtc_tm_to_time64() will count that like Dec 1st */
+ time64_t time = rtc_tm_to_time64(tm);
+ rtc_time64_to_tm(time + nov2dec_transitions(tm) * 86400, tm);
+}
+
+static void gregorian_to_rockchip(struct rtc_time *tm)
+{
+ time64_t extra_days = nov2dec_transitions(tm);
+ time64_t time = rtc_tm_to_time64(tm);
+ rtc_time64_to_tm(time - extra_days * 86400, tm);
+
+ /* Compensate if we went back over Nov 31st (will work up to 2381) */
+ if (nov2dec_transitions(tm) < extra_days) {
+ if (tm->tm_mon + 1 == 11)
+ tm->tm_mday++; /* This may result in 31! */
+ else
+ rtc_time64_to_tm(time - (extra_days - 1) * 86400, tm);
+ }
+}
+
/* Read current time and date in RTC */
static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm)
{
@@ -101,9 +137,10 @@ static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm)
tm->tm_mon = (bcd2bin(rtc_data[4] & MONTHS_REG_MSK)) - 1;
tm->tm_year = (bcd2bin(rtc_data[5] & YEARS_REG_MSK)) + 100;
tm->tm_wday = bcd2bin(rtc_data[6] & WEEKS_REG_MSK);
+ rockchip_to_gregorian(tm);
dev_dbg(dev, "RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
- tm->tm_wday, tm->tm_hour , tm->tm_min, tm->tm_sec);
+ tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec);
return ret;
}
@@ -116,6 +153,10 @@ static int rk808_rtc_set_time(struct device *dev, struct rtc_time *tm)
u8 rtc_data[NUM_TIME_REGS];
int ret;
+ dev_dbg(dev, "set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
+ 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+ gregorian_to_rockchip(tm);
rtc_data[0] = bin2bcd(tm->tm_sec);
rtc_data[1] = bin2bcd(tm->tm_min);
rtc_data[2] = bin2bcd(tm->tm_hour);
@@ -123,9 +164,6 @@ static int rk808_rtc_set_time(struct device *dev, struct rtc_time *tm)
rtc_data[4] = bin2bcd(tm->tm_mon + 1);
rtc_data[5] = bin2bcd(tm->tm_year - 100);
rtc_data[6] = bin2bcd(tm->tm_wday);
- dev_dbg(dev, "set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
- 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
- tm->tm_wday, tm->tm_hour , tm->tm_min, tm->tm_sec);
/* Stop RTC while updating the RTC registers */
ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
@@ -170,6 +208,7 @@ static int rk808_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
alrm->time.tm_mday = bcd2bin(alrm_data[3] & DAYS_REG_MSK);
alrm->time.tm_mon = (bcd2bin(alrm_data[4] & MONTHS_REG_MSK)) - 1;
alrm->time.tm_year = (bcd2bin(alrm_data[5] & YEARS_REG_MSK)) + 100;
+ rockchip_to_gregorian(&alrm->time);
ret = regmap_read(rk808->regmap, RK808_RTC_INT_REG, &int_reg);
if (ret) {
@@ -227,6 +266,7 @@ static int rk808_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour,
alrm->time.tm_min, alrm->time.tm_sec);
+ gregorian_to_rockchip(&alrm->time);
alrm_data[0] = bin2bcd(alrm->time.tm_sec);
alrm_data[1] = bin2bcd(alrm->time.tm_min);
alrm_data[2] = bin2bcd(alrm->time.tm_hour);
diff --git a/drivers/rtc/rtc-s5m.c b/drivers/rtc/rtc-s5m.c
index f2504b4eef34..0d68a85dd429 100644
--- a/drivers/rtc/rtc-s5m.c
+++ b/drivers/rtc/rtc-s5m.c
@@ -188,6 +188,7 @@ static inline int s5m_check_peding_alarm_interrupt(struct s5m_rtc_info *info,
ret = regmap_read(info->regmap, S5M_RTC_STATUS, &val);
val &= S5M_ALARM0_STATUS;
break;
+ case S2MPS15X:
case S2MPS14X:
case S2MPS13X:
ret = regmap_read(info->s5m87xx->regmap_pmic, S2MPS14_REG_ST2,
@@ -219,9 +220,22 @@ static inline int s5m8767_rtc_set_time_reg(struct s5m_rtc_info *info)
return ret;
}
- data |= info->regs->rtc_udr_mask;
- if (info->device_type == S5M8763X || info->device_type == S5M8767X)
- data |= S5M_RTC_TIME_EN_MASK;
+ switch (info->device_type) {
+ case S5M8763X:
+ case S5M8767X:
+ data |= info->regs->rtc_udr_mask | S5M_RTC_TIME_EN_MASK;
+ case S2MPS15X:
+ /* As per UM, for write time register, set WUDR bit to high */
+ data |= S2MPS15_RTC_WUDR_MASK;
+ break;
+ case S2MPS14X:
+ case S2MPS13X:
+ data |= info->regs->rtc_udr_mask;
+ break;
+ default:
+ return -EINVAL;
+ }
+
ret = regmap_write(info->regmap, info->regs->rtc_udr_update, data);
if (ret < 0) {
@@ -252,6 +266,11 @@ static inline int s5m8767_rtc_set_alarm_reg(struct s5m_rtc_info *info)
case S5M8767X:
data &= ~S5M_RTC_TIME_EN_MASK;
break;
+ case S2MPS15X:
+ /* As per UM, for write alarm, set A_UDR(bit[4]) to high
+ * rtc_udr_mask above sets bit[4]
+ */
+ break;
case S2MPS14X:
data |= S2MPS_RTC_RUDR_MASK;
break;
@@ -317,7 +336,8 @@ static int s5m_rtc_read_time(struct device *dev, struct rtc_time *tm)
u8 data[info->regs->regs_count];
int ret;
- if (info->device_type == S2MPS14X || info->device_type == S2MPS13X) {
+ if (info->device_type == S2MPS15X || info->device_type == S2MPS14X ||
+ info->device_type == S2MPS13X) {
ret = regmap_update_bits(info->regmap,
info->regs->rtc_udr_update,
S2MPS_RTC_RUDR_MASK, S2MPS_RTC_RUDR_MASK);
@@ -339,6 +359,7 @@ static int s5m_rtc_read_time(struct device *dev, struct rtc_time *tm)
break;
case S5M8767X:
+ case S2MPS15X:
case S2MPS14X:
case S2MPS13X:
s5m8767_data_to_tm(data, tm, info->rtc_24hr_mode);
@@ -366,6 +387,7 @@ static int s5m_rtc_set_time(struct device *dev, struct rtc_time *tm)
s5m8763_tm_to_data(tm, data);
break;
case S5M8767X:
+ case S2MPS15X:
case S2MPS14X:
case S2MPS13X:
ret = s5m8767_tm_to_data(tm, data);
@@ -414,6 +436,7 @@ static int s5m_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
break;
case S5M8767X:
+ case S2MPS15X:
case S2MPS14X:
case S2MPS13X:
s5m8767_data_to_tm(data, &alrm->time, info->rtc_24hr_mode);
@@ -463,6 +486,7 @@ static int s5m_rtc_stop_alarm(struct s5m_rtc_info *info)
break;
case S5M8767X:
+ case S2MPS15X:
case S2MPS14X:
case S2MPS13X:
for (i = 0; i < info->regs->regs_count; i++)
@@ -508,6 +532,7 @@ static int s5m_rtc_start_alarm(struct s5m_rtc_info *info)
break;
case S5M8767X:
+ case S2MPS15X:
case S2MPS14X:
case S2MPS13X:
data[RTC_SEC] |= ALARM_ENABLE_MASK;
@@ -548,6 +573,7 @@ static int s5m_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
break;
case S5M8767X:
+ case S2MPS15X:
case S2MPS14X:
case S2MPS13X:
s5m8767_tm_to_data(&alrm->time, data);
@@ -631,6 +657,7 @@ static int s5m8767_rtc_init_reg(struct s5m_rtc_info *info)
ret = regmap_raw_write(info->regmap, S5M_ALARM0_CONF, data, 2);
break;
+ case S2MPS15X:
case S2MPS14X:
case S2MPS13X:
data[0] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
@@ -679,6 +706,7 @@ static int s5m_rtc_probe(struct platform_device *pdev)
return -ENOMEM;
switch (platform_get_device_id(pdev)->driver_data) {
+ case S2MPS15X:
case S2MPS14X:
case S2MPS13X:
regmap_cfg = &s2mps14_rtc_regmap_config;
@@ -805,6 +833,7 @@ static const struct platform_device_id s5m_rtc_id[] = {
{ "s5m-rtc", S5M8767X },
{ "s2mps13-rtc", S2MPS13X },
{ "s2mps14-rtc", S2MPS14X },
+ { "s2mps15-rtc", S2MPS15X },
{ },
};
MODULE_DEVICE_TABLE(platform, s5m_rtc_id);
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index a263c10359e1..41605dac8309 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -2556,8 +2556,12 @@ static void __dasd_process_request_queue(struct dasd_block *block)
return;
}
- /* if device ist stopped do not fetch new requests */
- if (basedev->stopped)
+ /*
+ * if device is stopped do not fetch new requests
+ * except failfast is active which will let requests fail
+ * immediately in __dasd_block_start_head()
+ */
+ if (basedev->stopped && !(basedev->features & DASD_FEATURE_FAILFAST))
return;
/* Now we try to fetch requests from the request queue */
diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig
index eaca3e006301..b3f1c458905f 100644
--- a/drivers/s390/char/Kconfig
+++ b/drivers/s390/char/Kconfig
@@ -78,19 +78,6 @@ config SCLP_VT220_CONSOLE
Include support for using an IBM SCLP VT220-compatible terminal as a
Linux system console.
-config SCLP_CPI
- def_tristate m
- prompt "Control-Program Identification"
- depends on S390
- help
- This option enables the hardware console interface for system
- identification. This is commonly used for workload management and
- gives you a nice name for the system on the service element.
- Please select this option as a module since built-in operation is
- completely untested.
- You should only select this option if you know what you are doing,
- need this feature and intend to run your kernel in LPAR.
-
config SCLP_ASYNC
def_tristate m
prompt "Support for Call Home via Asynchronous SCLP Records"
@@ -125,6 +112,14 @@ config HMC_DRV
transfer cache size from it's default value 0.5MB to N bytes. If N
is zero, then no caching is performed.
+config SCLP_OFB
+ def_bool n
+ prompt "Support for Open-for-Business SCLP Event"
+ depends on S390
+ help
+ This option enables the Open-for-Business interface to the s390
+ Service Element.
+
config S390_TAPE
def_tristate m
prompt "S/390 tape device support"
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index 6fa9364d1c07..dd2f7c832e5e 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -16,7 +16,6 @@ obj-$(CONFIG_TN3215) += con3215.o
obj-$(CONFIG_SCLP_TTY) += sclp_tty.o
obj-$(CONFIG_SCLP_CONSOLE) += sclp_con.o
obj-$(CONFIG_SCLP_VT220_TTY) += sclp_vt220.o
-obj-$(CONFIG_SCLP_CPI) += sclp_cpi.o
obj-$(CONFIG_SCLP_ASYNC) += sclp_async.o
obj-$(CONFIG_VMLOGRDR) += vmlogrdr.o
@@ -30,9 +29,7 @@ obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o
obj-$(CONFIG_MONREADER) += monreader.o
obj-$(CONFIG_MONWRITER) += monwriter.o
obj-$(CONFIG_S390_VMUR) += vmur.o
-
-zcore_mod-objs := sclp_sdias.o zcore.o
-obj-$(CONFIG_CRASH_DUMP) += zcore_mod.o
+obj-$(CONFIG_CRASH_DUMP) += sclp_sdias.o zcore.o
hmcdrv-objs := hmcdrv_mod.o hmcdrv_dev.o hmcdrv_ftp.o hmcdrv_cache.o diag_ftp.o sclp_ftp.o
obj-$(CONFIG_HMC_DRV) += hmcdrv.o
diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c
index 0fc3fe5fd5b8..7d82bbcb12df 100644
--- a/drivers/s390/char/con3215.c
+++ b/drivers/s390/char/con3215.c
@@ -922,6 +922,8 @@ static int __init con3215_init(void)
spin_lock_init(&raw3215_freelist_lock);
for (i = 0; i < NR_3215_REQ; i++) {
req = kzalloc(sizeof(struct raw3215_req), GFP_KERNEL | GFP_DMA);
+ if (!req)
+ return -ENOMEM;
req->next = raw3215_freelist;
raw3215_freelist = req;
}
diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c
index 7c511add5aa7..4d7a9badfede 100644
--- a/drivers/s390/char/con3270.c
+++ b/drivers/s390/char/con3270.c
@@ -606,6 +606,8 @@ con3270_init(void)
return PTR_ERR(rp);
condev = kzalloc(sizeof(struct con3270), GFP_KERNEL | GFP_DMA);
+ if (!condev)
+ return -ENOMEM;
condev->view.dev = rp;
condev->read = raw3270_request_alloc(0);
diff --git a/drivers/s390/char/hmcdrv_ftp.c b/drivers/s390/char/hmcdrv_ftp.c
index d4b61d9088fb..8cb7d8fbadd6 100644
--- a/drivers/s390/char/hmcdrv_ftp.c
+++ b/drivers/s390/char/hmcdrv_ftp.c
@@ -37,7 +37,7 @@ struct hmcdrv_ftp_ops {
static enum hmcdrv_ftp_cmdid hmcdrv_ftp_cmd_getid(const char *cmd, int len);
static int hmcdrv_ftp_parse(char *cmd, struct hmcdrv_ftp_cmdspec *ftp);
-static struct hmcdrv_ftp_ops *hmcdrv_ftp_funcs; /* current operations */
+static const struct hmcdrv_ftp_ops *hmcdrv_ftp_funcs; /* current operations */
static DEFINE_MUTEX(hmcdrv_ftp_mutex); /* mutex for hmcdrv_ftp_funcs */
static unsigned hmcdrv_ftp_refcnt; /* start/shutdown reference counter */
@@ -290,13 +290,13 @@ ssize_t hmcdrv_ftp_cmd(char __kernel *cmd, loff_t offset,
*/
int hmcdrv_ftp_startup(void)
{
- static struct hmcdrv_ftp_ops hmcdrv_ftp_zvm = {
+ static const struct hmcdrv_ftp_ops hmcdrv_ftp_zvm = {
.startup = diag_ftp_startup,
.shutdown = diag_ftp_shutdown,
.transfer = diag_ftp_cmd
};
- static struct hmcdrv_ftp_ops hmcdrv_ftp_lpar = {
+ static const struct hmcdrv_ftp_ops hmcdrv_ftp_lpar = {
.startup = sclp_ftp_startup,
.shutdown = sclp_ftp_shutdown,
.transfer = sclp_ftp_cmd
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index f58bf4c6c3ee..272898225dbb 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -579,9 +579,8 @@ sclp_sync_wait(void)
old_tick = local_tick_disable();
trace_hardirqs_on();
__ctl_store(cr0, 0, 0);
- cr0_sync = cr0;
- cr0_sync &= 0xffff00a0;
- cr0_sync |= 0x00000200;
+ cr0_sync = cr0 & ~CR0_IRQ_SUBCLASS_MASK;
+ cr0_sync |= 1UL << (63 - 54);
__ctl_load(cr0_sync, 0, 0);
__arch_local_irq_stosm(0x01);
/* Loop until driver state indicates finished request */
diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c
index 944156207477..2ced50ccca63 100644
--- a/drivers/s390/char/sclp_config.c
+++ b/drivers/s390/char/sclp_config.c
@@ -11,6 +11,8 @@
#include <linux/cpu.h>
#include <linux/device.h>
#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
#include <asm/smp.h>
#include "sclp.h"
@@ -20,8 +22,22 @@ struct conf_mgm_data {
u8 ev_qualifier;
} __attribute__((packed));
+#define OFB_DATA_MAX 64
+
+struct sclp_ofb_evbuf {
+ struct evbuf_header header;
+ struct conf_mgm_data cm_data;
+ char ev_data[OFB_DATA_MAX];
+} __packed;
+
+struct sclp_ofb_sccb {
+ struct sccb_header header;
+ struct sclp_ofb_evbuf ofb_evbuf;
+} __packed;
+
#define EV_QUAL_CPU_CHANGE 1
#define EV_QUAL_CAP_CHANGE 3
+#define EV_QUAL_OPEN4BUSINESS 5
static struct work_struct sclp_cpu_capability_work;
static struct work_struct sclp_cpu_change_work;
@@ -63,15 +79,99 @@ static void sclp_conf_receiver_fn(struct evbuf_header *evbuf)
static struct sclp_register sclp_conf_register =
{
+#ifdef CONFIG_SCLP_OFB
+ .send_mask = EVTYP_CONFMGMDATA_MASK,
+#endif
.receive_mask = EVTYP_CONFMGMDATA_MASK,
.receiver_fn = sclp_conf_receiver_fn,
};
+#ifdef CONFIG_SCLP_OFB
+static int sclp_ofb_send_req(char *ev_data, size_t len)
+{
+ static DEFINE_MUTEX(send_mutex);
+ struct sclp_ofb_sccb *sccb;
+ int rc, response;
+
+ if (len > OFB_DATA_MAX)
+ return -EINVAL;
+ sccb = (struct sclp_ofb_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!sccb)
+ return -ENOMEM;
+ /* Setup SCCB for Control-Program Identification */
+ sccb->header.length = sizeof(struct sclp_ofb_sccb);
+ sccb->ofb_evbuf.header.length = sizeof(struct sclp_ofb_evbuf);
+ sccb->ofb_evbuf.header.type = EVTYP_CONFMGMDATA;
+ sccb->ofb_evbuf.cm_data.ev_qualifier = EV_QUAL_OPEN4BUSINESS;
+ memcpy(sccb->ofb_evbuf.ev_data, ev_data, len);
+
+ if (!(sclp_conf_register.sclp_receive_mask & EVTYP_CONFMGMDATA_MASK))
+ pr_warn("SCLP receiver did not register to receive "
+ "Configuration Management Data Events.\n");
+
+ mutex_lock(&send_mutex);
+ rc = sclp_sync_request(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
+ mutex_unlock(&send_mutex);
+ if (rc)
+ goto out;
+ response = sccb->header.response_code;
+ if (response != 0x0020) {
+ pr_err("Open for Business request failed with response code "
+ "0x%04x\n", response);
+ rc = -EIO;
+ }
+out:
+ free_page((unsigned long)sccb);
+ return rc;
+}
+
+static ssize_t sysfs_ofb_data_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ int rc;
+
+ rc = sclp_ofb_send_req(buf, count);
+ return rc ?: count;
+}
+
+static struct bin_attribute ofb_bin_attr = {
+ .attr = {
+ .name = "event_data",
+ .mode = S_IWUSR,
+ },
+ .write = sysfs_ofb_data_write,
+};
+#endif
+
+static int __init sclp_ofb_setup(void)
+{
+#ifdef CONFIG_SCLP_OFB
+ struct kset *ofb_kset;
+ int rc;
+
+ ofb_kset = kset_create_and_add("ofb", NULL, firmware_kobj);
+ if (!ofb_kset)
+ return -ENOMEM;
+ rc = sysfs_create_bin_file(&ofb_kset->kobj, &ofb_bin_attr);
+ if (rc) {
+ kset_unregister(ofb_kset);
+ return rc;
+ }
+#endif
+ return 0;
+}
+
static int __init sclp_conf_init(void)
{
+ int rc;
+
INIT_WORK(&sclp_cpu_capability_work, sclp_cpu_capability_notify);
INIT_WORK(&sclp_cpu_change_work, sclp_cpu_change_notify);
- return sclp_register(&sclp_conf_register);
+ rc = sclp_register(&sclp_conf_register);
+ if (rc)
+ return rc;
+ return sclp_ofb_setup();
}
__initcall(sclp_conf_init);
diff --git a/drivers/s390/char/sclp_cpi.c b/drivers/s390/char/sclp_cpi.c
deleted file mode 100644
index d70d8c20229c..000000000000
--- a/drivers/s390/char/sclp_cpi.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * SCLP control programm identification
- *
- * Copyright IBM Corp. 2001, 2007
- * Author(s): Martin Peschke <mpeschke@de.ibm.com>
- * Michael Ernst <mernst@de.ibm.com>
- */
-
-#include <linux/kmod.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/version.h>
-#include "sclp_cpi_sys.h"
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Identify this operating system instance "
- "to the System z hardware");
-MODULE_AUTHOR("Martin Peschke <mpeschke@de.ibm.com>, "
- "Michael Ernst <mernst@de.ibm.com>");
-
-static char *system_name = "";
-static char *sysplex_name = "";
-
-module_param(system_name, charp, 0);
-MODULE_PARM_DESC(system_name, "e.g. hostname - max. 8 characters");
-module_param(sysplex_name, charp, 0);
-MODULE_PARM_DESC(sysplex_name, "if applicable - max. 8 characters");
-
-static int __init cpi_module_init(void)
-{
- return sclp_cpi_set_data(system_name, sysplex_name, "LINUX",
- LINUX_VERSION_CODE);
-}
-
-static void __exit cpi_module_exit(void)
-{
-}
-
-module_init(cpi_module_init);
-module_exit(cpi_module_exit);
diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c
index 7bc6df3100ef..6804354c42bd 100644
--- a/drivers/s390/char/sclp_early.c
+++ b/drivers/s390/char/sclp_early.c
@@ -40,10 +40,14 @@ struct read_info_sccb {
u8 fac85; /* 85 */
u8 _pad_86[91 - 86]; /* 86-90 */
u8 flags; /* 91 */
- u8 _pad_92[100 - 92]; /* 92-99 */
+ u8 _pad_92[99 - 92]; /* 92-98 */
+ u8 hamaxpow; /* 99 */
u32 rnsize2; /* 100-103 */
u64 rnmax2; /* 104-111 */
- u8 _pad_112[120 - 112]; /* 112-119 */
+ u8 _pad_112[116 - 112]; /* 112-115 */
+ u8 fac116; /* 116 */
+ u8 _pad_117[119 - 117]; /* 117-118 */
+ u8 fac119; /* 119 */
u16 hcpua; /* 120-121 */
u8 _pad_122[4096 - 122]; /* 122-4095 */
} __packed __aligned(PAGE_SIZE);
@@ -108,6 +112,8 @@ static void __init sclp_facilities_detect(struct read_info_sccb *sccb)
sclp.facilities = sccb->facilities;
sclp.has_sprp = !!(sccb->fac84 & 0x02);
sclp.has_core_type = !!(sccb->fac84 & 0x01);
+ sclp.has_esca = !!(sccb->fac116 & 0x08);
+ sclp.has_hvs = !!(sccb->fac119 & 0x80);
if (sccb->fac85 & 0x02)
S390_lowcore.machine_flags |= MACHINE_FLAG_ESOP;
sclp.rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2;
@@ -115,6 +121,11 @@ static void __init sclp_facilities_detect(struct read_info_sccb *sccb)
sclp.rzm <<= 20;
sclp.ibc = sccb->ibc;
+ if (sccb->hamaxpow && sccb->hamaxpow < 64)
+ sclp.hamax = (1UL << sccb->hamaxpow) - 1;
+ else
+ sclp.hamax = U64_MAX;
+
if (!sccb->hcpua) {
if (MACHINE_IS_VM)
sclp.max_cores = 64;
@@ -131,6 +142,7 @@ static void __init sclp_facilities_detect(struct read_info_sccb *sccb)
continue;
sclp.has_siif = cpue->siif;
sclp.has_sigpif = cpue->sigpif;
+ sclp.has_sief2 = cpue->sief2;
break;
}
diff --git a/drivers/s390/char/vmcp.c b/drivers/s390/char/vmcp.c
index 0fdedadff7bc..2a67b496a9e2 100644
--- a/drivers/s390/char/vmcp.c
+++ b/drivers/s390/char/vmcp.c
@@ -88,14 +88,9 @@ vmcp_write(struct file *file, const char __user *buff, size_t count,
if (count > 240)
return -EINVAL;
- cmd = kmalloc(count + 1, GFP_KERNEL);
- if (!cmd)
- return -ENOMEM;
- if (copy_from_user(cmd, buff, count)) {
- kfree(cmd);
- return -EFAULT;
- }
- cmd[count] = '\0';
+ cmd = memdup_user_nul(buff, count);
+ if (IS_ERR(cmd))
+ return PTR_ERR(cmd);
session = file->private_data;
if (mutex_lock_interruptible(&session->mutex)) {
kfree(cmd);
diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c
index 0efb27f6f199..6c30e93ab8fa 100644
--- a/drivers/s390/char/vmur.c
+++ b/drivers/s390/char/vmur.c
@@ -782,24 +782,11 @@ static int ur_release(struct inode *inode, struct file *file)
static loff_t ur_llseek(struct file *file, loff_t offset, int whence)
{
- loff_t newpos;
-
if ((file->f_flags & O_ACCMODE) != O_RDONLY)
return -ESPIPE; /* seek allowed only for reader */
if (offset % PAGE_SIZE)
return -ESPIPE; /* only multiples of 4K allowed */
- switch (whence) {
- case 0: /* SEEK_SET */
- newpos = offset;
- break;
- case 1: /* SEEK_CUR */
- newpos = file->f_pos + offset;
- break;
- default:
- return -EINVAL;
- }
- file->f_pos = newpos;
- return newpos;
+ return no_seek_end_llseek(file, offset, whence);
}
static const struct file_operations ur_fops = {
diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c
index 823f41fc4bbd..5043ecfa1fbc 100644
--- a/drivers/s390/char/zcore.c
+++ b/drivers/s390/char/zcore.c
@@ -28,13 +28,12 @@
#include <asm/processor.h>
#include <asm/irqflags.h>
#include <asm/checksum.h>
+#include <asm/os_info.h>
#include <asm/switch_to.h>
#include "sclp.h"
#define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x)
-#define TO_USER 1
-#define TO_KERNEL 0
#define CHUNK_INFO_SIZE 34 /* 2 16-byte char, each followed by blank */
enum arch_id {
@@ -42,241 +41,93 @@ enum arch_id {
ARCH_S390X = 1,
};
-/* dump system info */
-
-struct sys_info {
- enum arch_id arch;
- unsigned long sa_base;
- u32 sa_size;
- int cpu_map[NR_CPUS];
- unsigned long mem_size;
- struct save_area lc_mask;
-};
-
struct ipib_info {
unsigned long ipib;
u32 checksum;
} __attribute__((packed));
-static struct sys_info sys_info;
static struct debug_info *zcore_dbf;
static int hsa_available;
static struct dentry *zcore_dir;
-static struct dentry *zcore_file;
static struct dentry *zcore_memmap_file;
static struct dentry *zcore_reipl_file;
static struct dentry *zcore_hsa_file;
static struct ipl_parameter_block *ipl_block;
+static char hsa_buf[PAGE_SIZE] __aligned(PAGE_SIZE);
+
/*
- * Copy memory from HSA to kernel or user memory (not reentrant):
+ * Copy memory from HSA to user memory (not reentrant):
*
- * @dest: Kernel or user buffer where memory should be copied to
+ * @dest: User buffer where memory should be copied to
* @src: Start address within HSA where data should be copied
* @count: Size of buffer, which should be copied
- * @mode: Either TO_KERNEL or TO_USER
*/
-int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode)
+int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count)
{
- int offs, blk_num;
- static char buf[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
+ unsigned long offset, bytes;
if (!hsa_available)
return -ENODATA;
- if (count == 0)
- return 0;
- /* copy first block */
- offs = 0;
- if ((src % PAGE_SIZE) != 0) {
- blk_num = src / PAGE_SIZE + 2;
- if (sclp_sdias_copy(buf, blk_num, 1)) {
+ while (count) {
+ if (sclp_sdias_copy(hsa_buf, src / PAGE_SIZE + 2, 1)) {
TRACE("sclp_sdias_copy() failed\n");
return -EIO;
}
- offs = min((PAGE_SIZE - (src % PAGE_SIZE)), count);
- if (mode == TO_USER) {
- if (copy_to_user((__force __user void*) dest,
- buf + (src % PAGE_SIZE), offs))
- return -EFAULT;
- } else
- memcpy(dest, buf + (src % PAGE_SIZE), offs);
- }
- if (offs == count)
- goto out;
-
- /* copy middle */
- for (; (offs + PAGE_SIZE) <= count; offs += PAGE_SIZE) {
- blk_num = (src + offs) / PAGE_SIZE + 2;
- if (sclp_sdias_copy(buf, blk_num, 1)) {
- TRACE("sclp_sdias_copy() failed\n");
- return -EIO;
- }
- if (mode == TO_USER) {
- if (copy_to_user((__force __user void*) dest + offs,
- buf, PAGE_SIZE))
- return -EFAULT;
- } else
- memcpy(dest + offs, buf, PAGE_SIZE);
- }
- if (offs == count)
- goto out;
-
- /* copy last block */
- blk_num = (src + offs) / PAGE_SIZE + 2;
- if (sclp_sdias_copy(buf, blk_num, 1)) {
- TRACE("sclp_sdias_copy() failed\n");
- return -EIO;
- }
- if (mode == TO_USER) {
- if (copy_to_user((__force __user void*) dest + offs, buf,
- count - offs))
+ offset = src % PAGE_SIZE;
+ bytes = min(PAGE_SIZE - offset, count);
+ if (copy_to_user(dest, hsa_buf + offset, bytes))
return -EFAULT;
- } else
- memcpy(dest + offs, buf, count - offs);
-out:
- return 0;
-}
-
-static int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count)
-{
- return memcpy_hsa((void __force *) dest, src, count, TO_USER);
-}
-
-static int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count)
-{
- return memcpy_hsa(dest, src, count, TO_KERNEL);
-}
-
-static int __init init_cpu_info(enum arch_id arch)
-{
- struct save_area_ext *sa_ext;
-
- /* get info for boot cpu from lowcore, stored in the HSA */
-
- sa_ext = dump_save_areas.areas[0];
- if (!sa_ext)
- return -ENOMEM;
- if (memcpy_hsa_kernel(&sa_ext->sa, sys_info.sa_base,
- sys_info.sa_size) < 0) {
- TRACE("could not copy from HSA\n");
- kfree(sa_ext);
- return -EIO;
+ src += bytes;
+ dest += bytes;
+ count -= bytes;
}
- if (MACHINE_HAS_VX)
- save_vx_regs_safe(sa_ext->vx_regs);
return 0;
}
-static DEFINE_MUTEX(zcore_mutex);
-
-#define DUMP_VERSION 0x5
-#define DUMP_MAGIC 0xa8190173618f23fdULL
-#define DUMP_ARCH_S390X 2
-#define DUMP_ARCH_S390 1
-#define HEADER_SIZE 4096
-
-/* dump header dumped according to s390 crash dump format */
-
-struct zcore_header {
- u64 magic;
- u32 version;
- u32 header_size;
- u32 dump_level;
- u32 page_size;
- u64 mem_size;
- u64 mem_start;
- u64 mem_end;
- u32 num_pages;
- u32 pad1;
- u64 tod;
- struct cpuid cpu_id;
- u32 arch_id;
- u32 volnr;
- u32 build_arch;
- u64 rmem_size;
- u8 mvdump;
- u16 cpu_cnt;
- u16 real_cpu_cnt;
- u8 end_pad1[0x200-0x061];
- u64 mvdump_sign;
- u64 mvdump_zipl_time;
- u8 end_pad2[0x800-0x210];
- u32 lc_vec[512];
-} __attribute__((packed,__aligned__(16)));
-
-static struct zcore_header zcore_header = {
- .magic = DUMP_MAGIC,
- .version = DUMP_VERSION,
- .header_size = 4096,
- .dump_level = 0,
- .page_size = PAGE_SIZE,
- .mem_start = 0,
- .build_arch = DUMP_ARCH_S390X,
-};
-
/*
- * Copy lowcore info to buffer. Use map in order to copy only register parts.
+ * Copy memory from HSA to kernel memory (not reentrant):
*
- * @buf: User buffer
- * @sa: Pointer to save area
- * @sa_off: Offset in save area to copy
- * @len: Number of bytes to copy
+ * @dest: Kernel or user buffer where memory should be copied to
+ * @src: Start address within HSA where data should be copied
+ * @count: Size of buffer, which should be copied
*/
-static int copy_lc(void __user *buf, void *sa, int sa_off, int len)
+int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count)
{
- int i;
- char *lc_mask = (char*)&sys_info.lc_mask;
+ unsigned long offset, bytes;
- for (i = 0; i < len; i++) {
- if (!lc_mask[i + sa_off])
- continue;
- if (copy_to_user(buf + i, sa + sa_off + i, 1))
- return -EFAULT;
+ if (!hsa_available)
+ return -ENODATA;
+
+ while (count) {
+ if (sclp_sdias_copy(hsa_buf, src / PAGE_SIZE + 2, 1)) {
+ TRACE("sclp_sdias_copy() failed\n");
+ return -EIO;
+ }
+ offset = src % PAGE_SIZE;
+ bytes = min(PAGE_SIZE - offset, count);
+ memcpy(dest, hsa_buf + offset, bytes);
+ src += bytes;
+ dest += bytes;
+ count -= bytes;
}
return 0;
}
-/*
- * Copy lowcores info to memory, if necessary
- *
- * @buf: User buffer
- * @addr: Start address of buffer in dump memory
- * @count: Size of buffer
- */
-static int zcore_add_lc(char __user *buf, unsigned long start, size_t count)
+static int __init init_cpu_info(void)
{
- unsigned long end;
- int i;
-
- if (count == 0)
- return 0;
+ struct save_area *sa;
- end = start + count;
- for (i = 0; i < dump_save_areas.count; i++) {
- unsigned long cp_start, cp_end; /* copy range */
- unsigned long sa_start, sa_end; /* save area range */
- unsigned long prefix;
- unsigned long sa_off, len, buf_off;
- struct save_area *save_area = &dump_save_areas.areas[i]->sa;
-
- prefix = save_area->pref_reg;
- sa_start = prefix + sys_info.sa_base;
- sa_end = prefix + sys_info.sa_base + sys_info.sa_size;
-
- if ((end < sa_start) || (start > sa_end))
- continue;
- cp_start = max(start, sa_start);
- cp_end = min(end, sa_end);
-
- buf_off = cp_start - start;
- sa_off = cp_start - sa_start;
- len = cp_end - cp_start;
-
- TRACE("copy_lc for: %lx\n", start);
- if (copy_lc(buf + buf_off, save_area, sa_off, len))
- return -EFAULT;
+ /* get info for boot cpu from lowcore, stored in the HSA */
+ sa = save_area_boot_cpu();
+ if (!sa)
+ return -ENOMEM;
+ if (memcpy_hsa_kernel(hsa_buf, __LC_FPREGS_SAVE_AREA, 512) < 0) {
+ TRACE("could not copy from HSA\n");
+ return -EIO;
}
+ save_area_add_regs(sa, hsa_buf); /* vx registers are saved in smp.c */
return 0;
}
@@ -289,126 +140,6 @@ static void release_hsa(void)
hsa_available = 0;
}
-/*
- * Read routine for zcore character device
- * First 4K are dump header
- * Next 32MB are HSA Memory
- * Rest is read from absolute Memory
- */
-static ssize_t zcore_read(struct file *file, char __user *buf, size_t count,
- loff_t *ppos)
-{
- unsigned long mem_start; /* Start address in memory */
- size_t mem_offs; /* Offset in dump memory */
- size_t hdr_count; /* Size of header part of output buffer */
- size_t size;
- int rc;
-
- mutex_lock(&zcore_mutex);
-
- if (*ppos > (sys_info.mem_size + HEADER_SIZE)) {
- rc = -EINVAL;
- goto fail;
- }
-
- count = min(count, (size_t) (sys_info.mem_size + HEADER_SIZE - *ppos));
-
- /* Copy dump header */
- if (*ppos < HEADER_SIZE) {
- size = min(count, (size_t) (HEADER_SIZE - *ppos));
- if (copy_to_user(buf, &zcore_header + *ppos, size)) {
- rc = -EFAULT;
- goto fail;
- }
- hdr_count = size;
- mem_start = 0;
- } else {
- hdr_count = 0;
- mem_start = *ppos - HEADER_SIZE;
- }
-
- mem_offs = 0;
-
- /* Copy from HSA data */
- if (*ppos < sclp.hsa_size + HEADER_SIZE) {
- size = min((count - hdr_count),
- (size_t) (sclp.hsa_size - mem_start));
- rc = memcpy_hsa_user(buf + hdr_count, mem_start, size);
- if (rc)
- goto fail;
-
- mem_offs += size;
- }
-
- /* Copy from real mem */
- size = count - mem_offs - hdr_count;
- rc = copy_to_user_real(buf + hdr_count + mem_offs,
- (void *) mem_start + mem_offs, size);
- if (rc)
- goto fail;
-
- /*
- * Since s390 dump analysis tools like lcrash or crash
- * expect register sets in the prefix pages of the cpus,
- * we copy them into the read buffer, if necessary.
- * buf + hdr_count: Start of memory part of output buffer
- * mem_start: Start memory address to copy from
- * count - hdr_count: Size of memory area to copy
- */
- if (zcore_add_lc(buf + hdr_count, mem_start, count - hdr_count)) {
- rc = -EFAULT;
- goto fail;
- }
- *ppos += count;
-fail:
- mutex_unlock(&zcore_mutex);
- return (rc < 0) ? rc : count;
-}
-
-static int zcore_open(struct inode *inode, struct file *filp)
-{
- if (!hsa_available)
- return -ENODATA;
- else
- return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
-}
-
-static int zcore_release(struct inode *inode, struct file *filep)
-{
- if (hsa_available)
- release_hsa();
- return 0;
-}
-
-static loff_t zcore_lseek(struct file *file, loff_t offset, int orig)
-{
- loff_t rc;
-
- mutex_lock(&zcore_mutex);
- switch (orig) {
- case 0:
- file->f_pos = offset;
- rc = file->f_pos;
- break;
- case 1:
- file->f_pos += offset;
- rc = file->f_pos;
- break;
- default:
- rc = -EINVAL;
- }
- mutex_unlock(&zcore_mutex);
- return rc;
-}
-
-static const struct file_operations zcore_fops = {
- .owner = THIS_MODULE,
- .llseek = zcore_lseek,
- .read = zcore_read,
- .open = zcore_open,
- .release = zcore_release,
-};
-
static ssize_t zcore_memmap_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
@@ -512,50 +243,6 @@ static const struct file_operations zcore_hsa_fops = {
.llseek = no_llseek,
};
-static void __init set_lc_mask(struct save_area *map)
-{
- memset(&map->fp_regs, 0xff, sizeof(map->fp_regs));
- memset(&map->gp_regs, 0xff, sizeof(map->gp_regs));
- memset(&map->psw, 0xff, sizeof(map->psw));
- memset(&map->pref_reg, 0xff, sizeof(map->pref_reg));
- memset(&map->fp_ctrl_reg, 0xff, sizeof(map->fp_ctrl_reg));
- memset(&map->tod_reg, 0xff, sizeof(map->tod_reg));
- memset(&map->timer, 0xff, sizeof(map->timer));
- memset(&map->clk_cmp, 0xff, sizeof(map->clk_cmp));
- memset(&map->acc_regs, 0xff, sizeof(map->acc_regs));
- memset(&map->ctrl_regs, 0xff, sizeof(map->ctrl_regs));
-}
-
-/*
- * Initialize dump globals for a given architecture
- */
-static int __init sys_info_init(enum arch_id arch, unsigned long mem_end)
-{
- int rc;
-
- switch (arch) {
- case ARCH_S390X:
- pr_alert("DETECTED 'S390X (64 bit) OS'\n");
- break;
- case ARCH_S390:
- pr_alert("DETECTED 'S390 (32 bit) OS'\n");
- break;
- default:
- pr_alert("0x%x is an unknown architecture.\n",arch);
- return -EINVAL;
- }
- sys_info.sa_base = SAVE_AREA_BASE;
- sys_info.sa_size = sizeof(struct save_area);
- sys_info.arch = arch;
- set_lc_mask(&sys_info.lc_mask);
- rc = init_cpu_info(arch);
- if (rc)
- return rc;
- sys_info.mem_size = mem_end;
-
- return 0;
-}
-
static int __init check_sdias(void)
{
if (!sclp.hsa_size) {
@@ -565,43 +252,6 @@ static int __init check_sdias(void)
return 0;
}
-static int __init get_mem_info(unsigned long *mem, unsigned long *end)
-{
- struct memblock_region *reg;
-
- for_each_memblock(memory, reg) {
- *mem += reg->size;
- *end = max_t(unsigned long, *end, reg->base + reg->size);
- }
- return 0;
-}
-
-static void __init zcore_header_init(int arch, struct zcore_header *hdr,
- unsigned long mem_size)
-{
- u32 prefix;
- int i;
-
- if (arch == ARCH_S390X)
- hdr->arch_id = DUMP_ARCH_S390X;
- else
- hdr->arch_id = DUMP_ARCH_S390;
- hdr->mem_size = mem_size;
- hdr->rmem_size = mem_size;
- hdr->mem_end = sys_info.mem_size;
- hdr->num_pages = mem_size / PAGE_SIZE;
- hdr->tod = get_tod_clock();
- get_cpu_id(&hdr->cpu_id);
- for (i = 0; i < dump_save_areas.count; i++) {
- prefix = dump_save_areas.areas[i]->sa.pref_reg;
- hdr->real_cpu_cnt++;
- if (!prefix)
- continue;
- hdr->lc_vec[hdr->cpu_cnt] = prefix;
- hdr->cpu_cnt++;
- }
-}
-
/*
* Provide IPL parameter information block from either HSA or memory
* for future reipl
@@ -634,11 +284,9 @@ static int __init zcore_reipl_init(void)
static int __init zcore_init(void)
{
- unsigned long mem_size, mem_end;
unsigned char arch;
int rc;
- mem_size = mem_end = 0;
if (ipl_info.type != IPL_TYPE_FCP_DUMP)
return -ENODATA;
if (OLDMEM_BASE)
@@ -672,14 +320,10 @@ static int __init zcore_init(void)
goto fail;
}
- rc = get_mem_info(&mem_size, &mem_end);
- if (rc)
- goto fail;
-
- rc = sys_info_init(arch, mem_end);
+ pr_alert("DETECTED 'S390X (64 bit) OS'\n");
+ rc = init_cpu_info();
if (rc)
goto fail;
- zcore_header_init(arch, &zcore_header, mem_size);
rc = zcore_reipl_init();
if (rc)
@@ -690,17 +334,11 @@ static int __init zcore_init(void)
rc = -ENOMEM;
goto fail;
}
- zcore_file = debugfs_create_file("mem", S_IRUSR, zcore_dir, NULL,
- &zcore_fops);
- if (!zcore_file) {
- rc = -ENOMEM;
- goto fail_dir;
- }
zcore_memmap_file = debugfs_create_file("memmap", S_IRUSR, zcore_dir,
NULL, &zcore_memmap_fops);
if (!zcore_memmap_file) {
rc = -ENOMEM;
- goto fail_file;
+ goto fail_dir;
}
zcore_reipl_file = debugfs_create_file("reipl", S_IRUSR, zcore_dir,
NULL, &zcore_reipl_fops);
@@ -720,8 +358,6 @@ fail_reipl_file:
debugfs_remove(zcore_reipl_file);
fail_memmap_file:
debugfs_remove(zcore_memmap_file);
-fail_file:
- debugfs_remove(zcore_file);
fail_dir:
debugfs_remove(zcore_dir);
fail:
@@ -737,7 +373,6 @@ static void __exit zcore_exit(void)
debugfs_remove(zcore_hsa_file);
debugfs_remove(zcore_reipl_file);
debugfs_remove(zcore_memmap_file);
- debugfs_remove(zcore_file);
debugfs_remove(zcore_dir);
diag308(DIAG308_REL_HSA, NULL);
}
diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile
index 8c4a386e97f6..3ab9aedeb84a 100644
--- a/drivers/s390/cio/Makefile
+++ b/drivers/s390/cio/Makefile
@@ -2,8 +2,11 @@
# Makefile for the S/390 common i/o drivers
#
+# The following is required for define_trace.h to find ./trace.h
+CFLAGS_trace.o := -I$(src)
+
obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o isc.o \
- fcx.o itcw.o crw.o ccwreq.o
+ fcx.o itcw.o crw.o ccwreq.o trace.o ioasm.o
ccw_device-objs += device.o device_fsm.o device_ops.o
ccw_device-objs += device_id.o device_pgid.o device_status.o
obj-y += ccw_device.o cmf.o
diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c
index 56eb4ee4deba..99b5db469097 100644
--- a/drivers/s390/cio/airq.c
+++ b/drivers/s390/cio/airq.c
@@ -89,6 +89,7 @@ static irqreturn_t do_airq_interrupt(int irq, void *dummy)
set_cpu_flag(CIF_NOHZ_DELAY);
tpi_info = (struct tpi_info *) &get_irq_regs()->int_code;
+ trace_s390_cio_adapter_int(tpi_info);
head = &airq_lists[tpi_info->isc];
rcu_read_lock();
hlist_for_each_entry_rcu(airq, head, list)
diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
index 213159dec89e..b6f12c2bb114 100644
--- a/drivers/s390/cio/chsc_sch.c
+++ b/drivers/s390/cio/chsc_sch.c
@@ -133,7 +133,7 @@ static int chsc_subchannel_prepare(struct subchannel *sch)
* since we don't have a way to clear the subchannel and
* cannot disable it with a request running.
*/
- cc = stsch_err(sch->schid, &schib);
+ cc = stsch(sch->schid, &schib);
if (!cc && scsw_stctl(&schib.scsw))
return -EAGAIN;
return 0;
@@ -185,8 +185,7 @@ static int __init chsc_init_dbfs(void)
debug_set_level(chsc_debug_log_id, 2);
return 0;
out:
- if (chsc_debug_msg_id)
- debug_unregister(chsc_debug_msg_id);
+ debug_unregister(chsc_debug_msg_id);
return -ENOMEM;
}
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 690b8547e828..39a8ae54e9c1 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -41,6 +41,7 @@
#include "blacklist.h"
#include "cio_debug.h"
#include "chp.h"
+#include "trace.h"
debug_info_t *cio_debug_msg_id;
debug_info_t *cio_debug_trace_id;
@@ -76,12 +77,9 @@ static int __init cio_debug_init(void)
return 0;
out_unregister:
- if (cio_debug_msg_id)
- debug_unregister(cio_debug_msg_id);
- if (cio_debug_trace_id)
- debug_unregister(cio_debug_trace_id);
- if (cio_debug_crw_id)
- debug_unregister(cio_debug_crw_id);
+ debug_unregister(cio_debug_msg_id);
+ debug_unregister(cio_debug_trace_id);
+ debug_unregister(cio_debug_crw_id);
return -1;
}
@@ -348,18 +346,18 @@ int cio_commit_config(struct subchannel *sch)
struct schib schib;
struct irb irb;
- if (stsch_err(sch->schid, &schib) || !css_sch_is_valid(&schib))
+ if (stsch(sch->schid, &schib) || !css_sch_is_valid(&schib))
return -ENODEV;
for (retry = 0; retry < 5; retry++) {
/* copy desired changes to local schib */
cio_apply_config(sch, &schib);
- ccode = msch_err(sch->schid, &schib);
+ ccode = msch(sch->schid, &schib);
if (ccode < 0) /* -EIO if msch gets a program check. */
return ccode;
switch (ccode) {
case 0: /* successful */
- if (stsch_err(sch->schid, &schib) ||
+ if (stsch(sch->schid, &schib) ||
!css_sch_is_valid(&schib))
return -ENODEV;
if (cio_check_config(sch, &schib)) {
@@ -394,7 +392,7 @@ int cio_update_schib(struct subchannel *sch)
{
struct schib schib;
- if (stsch_err(sch->schid, &schib) || !css_sch_is_valid(&schib))
+ if (stsch(sch->schid, &schib) || !css_sch_is_valid(&schib))
return -ENODEV;
memcpy(&sch->schib, &schib, sizeof(schib));
@@ -503,7 +501,7 @@ int cio_validate_subchannel(struct subchannel *sch, struct subchannel_id schid)
* If stsch gets an exception, it means the current subchannel set
* is not valid.
*/
- ccode = stsch_err(schid, &sch->schib);
+ ccode = stsch(schid, &sch->schib);
if (ccode) {
err = (ccode == 3) ? -ENXIO : ccode;
goto out;
@@ -542,6 +540,7 @@ static irqreturn_t do_cio_interrupt(int irq, void *dummy)
set_cpu_flag(CIF_NOHZ_DELAY);
tpi_info = (struct tpi_info *) &get_irq_regs()->int_code;
+ trace_s390_cio_interrupt(tpi_info);
irb = this_cpu_ptr(&cio_irb);
sch = (struct subchannel *)(unsigned long) tpi_info->intparm;
if (!sch) {
@@ -619,7 +618,7 @@ static int cio_test_for_console(struct subchannel_id schid, void *data)
{
struct schib schib;
- if (stsch_err(schid, &schib) != 0)
+ if (stsch(schid, &schib) != 0)
return -ENXIO;
if ((schib.pmcw.st == SUBCHANNEL_TYPE_IO) && schib.pmcw.dnv &&
(schib.pmcw.dev == console_devno)) {
@@ -638,7 +637,7 @@ static int cio_get_console_sch_no(void)
if (console_irq != -1) {
/* VM provided us with the irq number of the console. */
schid.sch_no = console_irq;
- if (stsch_err(schid, &schib) != 0 ||
+ if (stsch(schid, &schib) != 0 ||
(schib.pmcw.st != SUBCHANNEL_TYPE_IO) || !schib.pmcw.dnv)
return -1;
console_devno = schib.pmcw.dev;
@@ -708,10 +707,10 @@ __disable_subchannel_easy(struct subchannel_id schid, struct schib *schib)
cc = 0;
for (retry=0;retry<3;retry++) {
schib->pmcw.ena = 0;
- cc = msch_err(schid, schib);
+ cc = msch(schid, schib);
if (cc)
return (cc==3?-ENODEV:-EBUSY);
- if (stsch_err(schid, schib) || !css_sch_is_valid(schib))
+ if (stsch(schid, schib) || !css_sch_is_valid(schib))
return -ENODEV;
if (!schib->pmcw.ena)
return 0;
@@ -758,7 +757,7 @@ static int stsch_reset(struct subchannel_id schid, struct schib *addr)
pgm_check_occured = 0;
s390_base_pgm_handler_fn = cio_reset_pgm_check_handler;
- rc = stsch_err(schid, addr);
+ rc = stsch(schid, addr);
s390_base_pgm_handler_fn = NULL;
/* The program check handler could have changed pgm_check_occured. */
@@ -795,7 +794,7 @@ static int __shutdown_subchannel_easy(struct subchannel_id schid, void *data)
/* No default clear strategy */
break;
}
- stsch_err(schid, &schib);
+ stsch(schid, &schib);
__disable_subchannel_easy(schid, &schib);
}
out:
@@ -917,7 +916,7 @@ void reipl_ccw_dev(struct ccw_dev_id *devid)
{
struct subchannel_id uninitialized_var(schid);
- s390_reset_system(NULL, NULL, NULL);
+ s390_reset_system();
if (reipl_find_schid(devid, &schid) != 0)
panic("IPL Device not found\n");
do_reipl_asm(*((__u32*)&schid));
@@ -943,7 +942,7 @@ int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo)
if (__chsc_enable_facility(&sda_area, CHSC_SDA_OC_MSS))
return -ENODEV;
}
- if (stsch_err(schid, &schib))
+ if (stsch(schid, &schib))
return -ENODEV;
if (schib.pmcw.st != SUBCHANNEL_TYPE_IO)
return -ENODEV;
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index a01376ae1749..93de0b46b489 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -45,6 +45,18 @@ struct pmcw {
/* ... in an operand exception. */
} __attribute__ ((packed));
+/* I/O-Interruption Code as stored by TEST PENDING INTERRUPTION (TPI). */
+struct tpi_info {
+ struct subchannel_id schid;
+ u32 intparm;
+ u32 adapter_IO:1;
+ u32 :1;
+ u32 isc:3;
+ u32 :27;
+ u32 type:3;
+ u32 :12;
+} __packed __aligned(4);
+
/* Target SCHIB configuration. */
struct schib_config {
u64 mba;
diff --git a/drivers/s390/cio/crw.c b/drivers/s390/cio/crw.c
index 0f8a25f98b10..3d3cd402b376 100644
--- a/drivers/s390/cio/crw.c
+++ b/drivers/s390/cio/crw.c
@@ -14,6 +14,7 @@
#include <linux/wait.h>
#include <asm/crw.h>
#include <asm/ctl_reg.h>
+#include "ioasm.h"
static DEFINE_MUTEX(crw_handler_mutex);
static crw_handler_t crw_handlers[NR_RSCS];
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 489e703dc82d..3d2b20ee613f 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -390,7 +390,7 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow)
/* Will be done on the slow path. */
return -EAGAIN;
}
- if (stsch_err(schid, &schib)) {
+ if (stsch(schid, &schib)) {
/* Subchannel is not provided. */
return -ENXIO;
}
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 92e03b42e661..8327d47e08b6 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -44,7 +44,7 @@ static void ccw_timeout_log(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent);
private = to_io_private(sch);
orb = &private->orb;
- cc = stsch_err(sch->schid, &schib);
+ cc = stsch(sch->schid, &schib);
printk(KERN_WARNING "cio: ccw device timeout occurred at %llx, "
"device information:\n", get_tod_clock());
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h
index b108f4a5c7dd..8975060af96c 100644
--- a/drivers/s390/cio/io_sch.h
+++ b/drivers/s390/cio/io_sch.h
@@ -169,49 +169,4 @@ struct ccw_device_private {
enum interruption_class int_class;
};
-static inline int rsch(struct subchannel_id schid)
-{
- register struct subchannel_id reg1 asm("1") = schid;
- int ccode;
-
- asm volatile(
- " rsch\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (reg1)
- : "cc", "memory");
- return ccode;
-}
-
-static inline int hsch(struct subchannel_id schid)
-{
- register struct subchannel_id reg1 asm("1") = schid;
- int ccode;
-
- asm volatile(
- " hsch\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (reg1)
- : "cc");
- return ccode;
-}
-
-static inline int xsch(struct subchannel_id schid)
-{
- register struct subchannel_id reg1 asm("1") = schid;
- int ccode;
-
- asm volatile(
- " .insn rre,0xb2760000,%1,0\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (reg1)
- : "cc");
- return ccode;
-}
-
#endif
diff --git a/drivers/s390/cio/ioasm.c b/drivers/s390/cio/ioasm.c
new file mode 100644
index 000000000000..98984818618f
--- /dev/null
+++ b/drivers/s390/cio/ioasm.c
@@ -0,0 +1,224 @@
+/*
+ * Channel subsystem I/O instructions.
+ */
+
+#include <linux/export.h>
+
+#include <asm/chpid.h>
+#include <asm/schid.h>
+#include <asm/crw.h>
+
+#include "ioasm.h"
+#include "orb.h"
+#include "cio.h"
+
+int stsch(struct subchannel_id schid, struct schib *addr)
+{
+ register struct subchannel_id reg1 asm ("1") = schid;
+ int ccode = -EIO;
+
+ asm volatile(
+ " stsch 0(%3)\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
+ "1:\n"
+ EX_TABLE(0b, 1b)
+ : "+d" (ccode), "=m" (*addr)
+ : "d" (reg1), "a" (addr)
+ : "cc");
+ trace_s390_cio_stsch(schid, addr, ccode);
+
+ return ccode;
+}
+EXPORT_SYMBOL(stsch);
+
+int msch(struct subchannel_id schid, struct schib *addr)
+{
+ register struct subchannel_id reg1 asm ("1") = schid;
+ int ccode = -EIO;
+
+ asm volatile(
+ " msch 0(%2)\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
+ "1:\n"
+ EX_TABLE(0b, 1b)
+ : "+d" (ccode)
+ : "d" (reg1), "a" (addr), "m" (*addr)
+ : "cc");
+ trace_s390_cio_msch(schid, addr, ccode);
+
+ return ccode;
+}
+
+int tsch(struct subchannel_id schid, struct irb *addr)
+{
+ register struct subchannel_id reg1 asm ("1") = schid;
+ int ccode;
+
+ asm volatile(
+ " tsch 0(%3)\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode), "=m" (*addr)
+ : "d" (reg1), "a" (addr)
+ : "cc");
+ trace_s390_cio_tsch(schid, addr, ccode);
+
+ return ccode;
+}
+
+int ssch(struct subchannel_id schid, union orb *addr)
+{
+ register struct subchannel_id reg1 asm("1") = schid;
+ int ccode = -EIO;
+
+ asm volatile(
+ " ssch 0(%2)\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
+ "1:\n"
+ EX_TABLE(0b, 1b)
+ : "+d" (ccode)
+ : "d" (reg1), "a" (addr), "m" (*addr)
+ : "cc", "memory");
+ trace_s390_cio_ssch(schid, addr, ccode);
+
+ return ccode;
+}
+EXPORT_SYMBOL(ssch);
+
+int csch(struct subchannel_id schid)
+{
+ register struct subchannel_id reg1 asm("1") = schid;
+ int ccode;
+
+ asm volatile(
+ " csch\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode)
+ : "d" (reg1)
+ : "cc");
+ trace_s390_cio_csch(schid, ccode);
+
+ return ccode;
+}
+EXPORT_SYMBOL(csch);
+
+int tpi(struct tpi_info *addr)
+{
+ int ccode;
+
+ asm volatile(
+ " tpi 0(%2)\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode), "=m" (*addr)
+ : "a" (addr)
+ : "cc");
+ trace_s390_cio_tpi(addr, ccode);
+
+ return ccode;
+}
+
+int chsc(void *chsc_area)
+{
+ typedef struct { char _[4096]; } addr_type;
+ int cc;
+
+ asm volatile(
+ " .insn rre,0xb25f0000,%2,0\n"
+ " ipm %0\n"
+ " srl %0,28\n"
+ : "=d" (cc), "=m" (*(addr_type *) chsc_area)
+ : "d" (chsc_area), "m" (*(addr_type *) chsc_area)
+ : "cc");
+ trace_s390_cio_chsc(chsc_area, cc);
+
+ return cc;
+}
+EXPORT_SYMBOL(chsc);
+
+int rchp(struct chp_id chpid)
+{
+ register struct chp_id reg1 asm ("1") = chpid;
+ int ccode;
+
+ asm volatile(
+ " lr 1,%1\n"
+ " rchp\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1) : "cc");
+ trace_s390_cio_rchp(chpid, ccode);
+
+ return ccode;
+}
+
+int rsch(struct subchannel_id schid)
+{
+ register struct subchannel_id reg1 asm("1") = schid;
+ int ccode;
+
+ asm volatile(
+ " rsch\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode)
+ : "d" (reg1)
+ : "cc", "memory");
+ trace_s390_cio_rsch(schid, ccode);
+
+ return ccode;
+}
+
+int hsch(struct subchannel_id schid)
+{
+ register struct subchannel_id reg1 asm("1") = schid;
+ int ccode;
+
+ asm volatile(
+ " hsch\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode)
+ : "d" (reg1)
+ : "cc");
+ trace_s390_cio_hsch(schid, ccode);
+
+ return ccode;
+}
+
+int xsch(struct subchannel_id schid)
+{
+ register struct subchannel_id reg1 asm("1") = schid;
+ int ccode;
+
+ asm volatile(
+ " xsch\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode)
+ : "d" (reg1)
+ : "cc");
+ trace_s390_cio_xsch(schid, ccode);
+
+ return ccode;
+}
+
+int stcrw(struct crw *crw)
+{
+ int ccode;
+
+ asm volatile(
+ " stcrw 0(%2)\n"
+ " ipm %0\n"
+ " srl %0,28\n"
+ : "=d" (ccode), "=m" (*crw)
+ : "a" (crw)
+ : "cc");
+ trace_s390_cio_stcrw(crw, ccode);
+
+ return ccode;
+}
diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h
index 4d80fc67a06b..b31ee6bff1e4 100644
--- a/drivers/s390/cio/ioasm.h
+++ b/drivers/s390/cio/ioasm.h
@@ -3,165 +3,26 @@
#include <asm/chpid.h>
#include <asm/schid.h>
+#include <asm/crw.h>
#include "orb.h"
#include "cio.h"
+#include "trace.h"
/*
- * TPI info structure
+ * Some S390 specific IO instructions
*/
-struct tpi_info {
- struct subchannel_id schid;
- __u32 intparm; /* interruption parameter */
- __u32 adapter_IO : 1;
- __u32 reserved2 : 1;
- __u32 isc : 3;
- __u32 reserved3 : 12;
- __u32 int_type : 3;
- __u32 reserved4 : 12;
-} __attribute__ ((packed));
-
-/*
- * Some S390 specific IO instructions as inline
- */
-
-static inline int stsch_err(struct subchannel_id schid, struct schib *addr)
-{
- register struct subchannel_id reg1 asm ("1") = schid;
- int ccode = -EIO;
-
- asm volatile(
- " stsch 0(%3)\n"
- "0: ipm %0\n"
- " srl %0,28\n"
- "1:\n"
- EX_TABLE(0b,1b)
- : "+d" (ccode), "=m" (*addr)
- : "d" (reg1), "a" (addr)
- : "cc");
- return ccode;
-}
-
-static inline int msch(struct subchannel_id schid, struct schib *addr)
-{
- register struct subchannel_id reg1 asm ("1") = schid;
- int ccode;
-
- asm volatile(
- " msch 0(%2)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (reg1), "a" (addr), "m" (*addr)
- : "cc");
- return ccode;
-}
-
-static inline int msch_err(struct subchannel_id schid, struct schib *addr)
-{
- register struct subchannel_id reg1 asm ("1") = schid;
- int ccode = -EIO;
-
- asm volatile(
- " msch 0(%2)\n"
- "0: ipm %0\n"
- " srl %0,28\n"
- "1:\n"
- EX_TABLE(0b,1b)
- : "+d" (ccode)
- : "d" (reg1), "a" (addr), "m" (*addr)
- : "cc");
- return ccode;
-}
-
-static inline int tsch(struct subchannel_id schid, struct irb *addr)
-{
- register struct subchannel_id reg1 asm ("1") = schid;
- int ccode;
-
- asm volatile(
- " tsch 0(%3)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode), "=m" (*addr)
- : "d" (reg1), "a" (addr)
- : "cc");
- return ccode;
-}
-
-static inline int ssch(struct subchannel_id schid, union orb *addr)
-{
- register struct subchannel_id reg1 asm("1") = schid;
- int ccode = -EIO;
-
- asm volatile(
- " ssch 0(%2)\n"
- "0: ipm %0\n"
- " srl %0,28\n"
- "1:\n"
- EX_TABLE(0b, 1b)
- : "+d" (ccode)
- : "d" (reg1), "a" (addr), "m" (*addr)
- : "cc", "memory");
- return ccode;
-}
-
-static inline int csch(struct subchannel_id schid)
-{
- register struct subchannel_id reg1 asm("1") = schid;
- int ccode;
-
- asm volatile(
- " csch\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (reg1)
- : "cc");
- return ccode;
-}
-
-static inline int tpi(struct tpi_info *addr)
-{
- int ccode;
-
- asm volatile(
- " tpi 0(%2)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode), "=m" (*addr)
- : "a" (addr)
- : "cc");
- return ccode;
-}
-
-static inline int chsc(void *chsc_area)
-{
- typedef struct { char _[4096]; } addr_type;
- int cc;
-
- asm volatile(
- " .insn rre,0xb25f0000,%2,0\n"
- " ipm %0\n"
- " srl %0,28\n"
- : "=d" (cc), "=m" (*(addr_type *) chsc_area)
- : "d" (chsc_area), "m" (*(addr_type *) chsc_area)
- : "cc");
- return cc;
-}
-
-static inline int rchp(struct chp_id chpid)
-{
- register struct chp_id reg1 asm ("1") = chpid;
- int ccode;
-
- asm volatile(
- " lr 1,%1\n"
- " rchp\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode) : "d" (reg1) : "cc");
- return ccode;
-}
+int stsch(struct subchannel_id schid, struct schib *addr);
+int msch(struct subchannel_id schid, struct schib *addr);
+int tsch(struct subchannel_id schid, struct irb *addr);
+int ssch(struct subchannel_id schid, union orb *addr);
+int csch(struct subchannel_id schid);
+int tpi(struct tpi_info *addr);
+int chsc(void *chsc_area);
+int rchp(struct chp_id chpid);
+int rsch(struct subchannel_id schid);
+int hsch(struct subchannel_id schid);
+int xsch(struct subchannel_id schid);
+int stcrw(struct crw *crw);
#endif
diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c
index f1f3baa8e6e4..b6fc147f83d8 100644
--- a/drivers/s390/cio/qdio_debug.c
+++ b/drivers/s390/cio/qdio_debug.c
@@ -366,8 +366,6 @@ void qdio_debug_exit(void)
{
qdio_clear_dbf_list();
debugfs_remove(debugfs_root);
- if (qdio_dbf_setup)
- debug_unregister(qdio_dbf_setup);
- if (qdio_dbf_error)
- debug_unregister(qdio_dbf_error);
+ debug_unregister(qdio_dbf_setup);
+ debug_unregister(qdio_dbf_error);
}
diff --git a/drivers/s390/cio/trace.c b/drivers/s390/cio/trace.c
new file mode 100644
index 000000000000..8e706669ac8b
--- /dev/null
+++ b/drivers/s390/cio/trace.c
@@ -0,0 +1,24 @@
+/*
+ * Tracepoint definitions for s390_cio
+ *
+ * Copyright IBM Corp. 2015
+ * Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
+ */
+
+#include <asm/crw.h>
+#include "cio.h"
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
+
+EXPORT_TRACEPOINT_SYMBOL(s390_cio_stsch);
+EXPORT_TRACEPOINT_SYMBOL(s390_cio_msch);
+EXPORT_TRACEPOINT_SYMBOL(s390_cio_tsch);
+EXPORT_TRACEPOINT_SYMBOL(s390_cio_tpi);
+EXPORT_TRACEPOINT_SYMBOL(s390_cio_ssch);
+EXPORT_TRACEPOINT_SYMBOL(s390_cio_csch);
+EXPORT_TRACEPOINT_SYMBOL(s390_cio_hsch);
+EXPORT_TRACEPOINT_SYMBOL(s390_cio_xsch);
+EXPORT_TRACEPOINT_SYMBOL(s390_cio_rsch);
+EXPORT_TRACEPOINT_SYMBOL(s390_cio_rchp);
+EXPORT_TRACEPOINT_SYMBOL(s390_cio_chsc);
diff --git a/drivers/s390/cio/trace.h b/drivers/s390/cio/trace.h
new file mode 100644
index 000000000000..5b807a09f21b
--- /dev/null
+++ b/drivers/s390/cio/trace.h
@@ -0,0 +1,363 @@
+/*
+ * Tracepoint header for the s390 Common I/O layer (CIO)
+ *
+ * Copyright IBM Corp. 2015
+ * Author(s): Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
+ */
+
+#include <linux/kernel.h>
+#include <asm/crw.h>
+#include <uapi/asm/chpid.h>
+#include <uapi/asm/schid.h>
+#include "cio.h"
+#include "orb.h"
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM s390
+
+#if !defined(_TRACE_S390_CIO_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_S390_CIO_H
+
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(s390_class_schib,
+ TP_PROTO(struct subchannel_id schid, struct schib *schib, int cc),
+ TP_ARGS(schid, schib, cc),
+ TP_STRUCT__entry(
+ __field(u8, cssid)
+ __field(u8, ssid)
+ __field(u16, schno)
+ __field(u16, devno)
+ __field_struct(struct schib, schib)
+ __field(int, cc)
+ ),
+ TP_fast_assign(
+ __entry->cssid = schid.cssid;
+ __entry->ssid = schid.ssid;
+ __entry->schno = schid.sch_no;
+ __entry->devno = schib->pmcw.dev;
+ __entry->schib = *schib;
+ __entry->cc = cc;
+ ),
+ TP_printk("schid=%x.%x.%04x cc=%d ena=%d st=%d dnv=%d dev=%04x "
+ "lpm=0x%02x pnom=0x%02x lpum=0x%02x pim=0x%02x pam=0x%02x "
+ "pom=0x%02x chpids=%016llx",
+ __entry->cssid, __entry->ssid, __entry->schno, __entry->cc,
+ __entry->schib.pmcw.ena, __entry->schib.pmcw.st,
+ __entry->schib.pmcw.dnv, __entry->schib.pmcw.dev,
+ __entry->schib.pmcw.lpm, __entry->schib.pmcw.pnom,
+ __entry->schib.pmcw.lpum, __entry->schib.pmcw.pim,
+ __entry->schib.pmcw.pam, __entry->schib.pmcw.pom,
+ *((u64 *) __entry->schib.pmcw.chpid)
+ )
+);
+
+/**
+ * s390_cio_stsch - Store Subchannel instruction (STSCH) was performed
+ * @schid: Subchannel ID
+ * @schib: Subchannel-Information block
+ * @cc: Condition code
+ */
+DEFINE_EVENT(s390_class_schib, s390_cio_stsch,
+ TP_PROTO(struct subchannel_id schid, struct schib *schib, int cc),
+ TP_ARGS(schid, schib, cc)
+);
+
+/**
+ * s390_cio_msch - Modify Subchannel instruction (MSCH) was performed
+ * @schid: Subchannel ID
+ * @schib: Subchannel-Information block
+ * @cc: Condition code
+ */
+DEFINE_EVENT(s390_class_schib, s390_cio_msch,
+ TP_PROTO(struct subchannel_id schid, struct schib *schib, int cc),
+ TP_ARGS(schid, schib, cc)
+);
+
+/**
+ * s390_cio_tsch - Test Subchannel instruction (TSCH) was performed
+ * @schid: Subchannel ID
+ * @irb: Interruption-Response Block
+ * @cc: Condition code
+ */
+TRACE_EVENT(s390_cio_tsch,
+ TP_PROTO(struct subchannel_id schid, struct irb *irb, int cc),
+ TP_ARGS(schid, irb, cc),
+ TP_STRUCT__entry(
+ __field(u8, cssid)
+ __field(u8, ssid)
+ __field(u16, schno)
+ __field_struct(struct irb, irb)
+ __field(int, cc)
+ ),
+ TP_fast_assign(
+ __entry->cssid = schid.cssid;
+ __entry->ssid = schid.ssid;
+ __entry->schno = schid.sch_no;
+ __entry->irb = *irb;
+ __entry->cc = cc;
+ ),
+ TP_printk("schid=%x.%x.%04x cc=%d dcc=%d pno=%d fctl=0x%x actl=0x%x "
+ "stctl=0x%x dstat=0x%x cstat=0x%x",
+ __entry->cssid, __entry->ssid, __entry->schno, __entry->cc,
+ scsw_cc(&__entry->irb.scsw), scsw_pno(&__entry->irb.scsw),
+ scsw_fctl(&__entry->irb.scsw), scsw_actl(&__entry->irb.scsw),
+ scsw_stctl(&__entry->irb.scsw),
+ scsw_dstat(&__entry->irb.scsw), scsw_cstat(&__entry->irb.scsw)
+ )
+);
+
+/**
+ * s390_cio_tpi - Test Pending Interruption instruction (TPI) was performed
+ * @addr: Address of the I/O interruption code or %NULL
+ * @cc: Condition code
+ */
+TRACE_EVENT(s390_cio_tpi,
+ TP_PROTO(struct tpi_info *addr, int cc),
+ TP_ARGS(addr, cc),
+ TP_STRUCT__entry(
+ __field(int, cc)
+ __field_struct(struct tpi_info, tpi_info)
+ __field(u8, cssid)
+ __field(u8, ssid)
+ __field(u16, schno)
+ ),
+ TP_fast_assign(
+ __entry->cc = cc;
+ if (cc != 0)
+ memset(&__entry->tpi_info, 0, sizeof(struct tpi_info));
+ else if (addr)
+ __entry->tpi_info = *addr;
+ else {
+ memcpy(&__entry->tpi_info, &S390_lowcore.subchannel_id,
+ sizeof(struct tpi_info));
+ }
+ __entry->cssid = __entry->tpi_info.schid.cssid;
+ __entry->ssid = __entry->tpi_info.schid.ssid;
+ __entry->schno = __entry->tpi_info.schid.sch_no;
+ ),
+ TP_printk("schid=%x.%x.%04x cc=%d a=%d isc=%d type=%d",
+ __entry->cssid, __entry->ssid, __entry->schno, __entry->cc,
+ __entry->tpi_info.adapter_IO, __entry->tpi_info.isc,
+ __entry->tpi_info.type
+ )
+);
+
+/**
+ * s390_cio_ssch - Start Subchannel instruction (SSCH) was performed
+ * @schid: Subchannel ID
+ * @orb: Operation-Request Block
+ * @cc: Condition code
+ */
+TRACE_EVENT(s390_cio_ssch,
+ TP_PROTO(struct subchannel_id schid, union orb *orb, int cc),
+ TP_ARGS(schid, orb, cc),
+ TP_STRUCT__entry(
+ __field(u8, cssid)
+ __field(u8, ssid)
+ __field(u16, schno)
+ __field_struct(union orb, orb)
+ __field(int, cc)
+ ),
+ TP_fast_assign(
+ __entry->cssid = schid.cssid;
+ __entry->ssid = schid.ssid;
+ __entry->schno = schid.sch_no;
+ __entry->orb = *orb;
+ __entry->cc = cc;
+ ),
+ TP_printk("schid=%x.%x.%04x cc=%d", __entry->cssid, __entry->ssid,
+ __entry->schno, __entry->cc
+ )
+);
+
+DECLARE_EVENT_CLASS(s390_class_schid,
+ TP_PROTO(struct subchannel_id schid, int cc),
+ TP_ARGS(schid, cc),
+ TP_STRUCT__entry(
+ __field(u8, cssid)
+ __field(u8, ssid)
+ __field(u16, schno)
+ __field(int, cc)
+ ),
+ TP_fast_assign(
+ __entry->cssid = schid.cssid;
+ __entry->ssid = schid.ssid;
+ __entry->schno = schid.sch_no;
+ __entry->cc = cc;
+ ),
+ TP_printk("schid=%x.%x.%04x cc=%d", __entry->cssid, __entry->ssid,
+ __entry->schno, __entry->cc
+ )
+);
+
+/**
+ * s390_cio_csch - Clear Subchannel instruction (CSCH) was performed
+ * @schid: Subchannel ID
+ * @cc: Condition code
+ */
+DEFINE_EVENT(s390_class_schid, s390_cio_csch,
+ TP_PROTO(struct subchannel_id schid, int cc),
+ TP_ARGS(schid, cc)
+);
+
+/**
+ * s390_cio_hsch - Halt Subchannel instruction (HSCH) was performed
+ * @schid: Subchannel ID
+ * @cc: Condition code
+ */
+DEFINE_EVENT(s390_class_schid, s390_cio_hsch,
+ TP_PROTO(struct subchannel_id schid, int cc),
+ TP_ARGS(schid, cc)
+);
+
+/**
+ * s390_cio_xsch - Cancel Subchannel instruction (XSCH) was performed
+ * @schid: Subchannel ID
+ * @cc: Condition code
+ */
+DEFINE_EVENT(s390_class_schid, s390_cio_xsch,
+ TP_PROTO(struct subchannel_id schid, int cc),
+ TP_ARGS(schid, cc)
+);
+
+/**
+ * s390_cio_rsch - Resume Subchannel instruction (RSCH) was performed
+ * @schid: Subchannel ID
+ * @cc: Condition code
+ */
+DEFINE_EVENT(s390_class_schid, s390_cio_rsch,
+ TP_PROTO(struct subchannel_id schid, int cc),
+ TP_ARGS(schid, cc)
+);
+
+/**
+ * s390_cio_rchp - Reset Channel Path (RCHP) instruction was performed
+ * @chpid: Channel-Path Identifier
+ * @cc: Condition code
+ */
+TRACE_EVENT(s390_cio_rchp,
+ TP_PROTO(struct chp_id chpid, int cc),
+ TP_ARGS(chpid, cc),
+ TP_STRUCT__entry(
+ __field(u8, cssid)
+ __field(u8, id)
+ __field(int, cc)
+ ),
+ TP_fast_assign(
+ __entry->cssid = chpid.cssid;
+ __entry->id = chpid.id;
+ __entry->cc = cc;
+ ),
+ TP_printk("chpid=%x.%02x cc=%d", __entry->cssid, __entry->id,
+ __entry->cc
+ )
+);
+
+#define CHSC_MAX_REQUEST_LEN 64
+#define CHSC_MAX_RESPONSE_LEN 64
+
+/**
+ * s390_cio_chsc - Channel Subsystem Call (CHSC) instruction was performed
+ * @chsc: CHSC block
+ * @cc: Condition code
+ */
+TRACE_EVENT(s390_cio_chsc,
+ TP_PROTO(struct chsc_header *chsc, int cc),
+ TP_ARGS(chsc, cc),
+ TP_STRUCT__entry(
+ __field(int, cc)
+ __field(u16, code)
+ __field(u16, rcode)
+ __array(u8, request, CHSC_MAX_REQUEST_LEN)
+ __array(u8, response, CHSC_MAX_RESPONSE_LEN)
+ ),
+ TP_fast_assign(
+ __entry->cc = cc;
+ __entry->code = chsc->code;
+ memcpy(&entry->request, chsc,
+ min_t(u16, chsc->length, CHSC_MAX_REQUEST_LEN));
+ chsc = (struct chsc_header *) ((char *) chsc + chsc->length);
+ __entry->rcode = chsc->code;
+ memcpy(&entry->response, chsc,
+ min_t(u16, chsc->length, CHSC_MAX_RESPONSE_LEN));
+ ),
+ TP_printk("code=0x%04x cc=%d rcode=0x%04x", __entry->code,
+ __entry->cc, __entry->rcode)
+);
+
+/**
+ * s390_cio_interrupt - An I/O interrupt occurred
+ * @tpi_info: Address of the I/O interruption code
+ */
+TRACE_EVENT(s390_cio_interrupt,
+ TP_PROTO(struct tpi_info *tpi_info),
+ TP_ARGS(tpi_info),
+ TP_STRUCT__entry(
+ __field_struct(struct tpi_info, tpi_info)
+ __field(u8, cssid)
+ __field(u8, ssid)
+ __field(u16, schno)
+ ),
+ TP_fast_assign(
+ __entry->tpi_info = *tpi_info;
+ __entry->cssid = __entry->tpi_info.schid.cssid;
+ __entry->ssid = __entry->tpi_info.schid.ssid;
+ __entry->schno = __entry->tpi_info.schid.sch_no;
+ ),
+ TP_printk("schid=%x.%x.%04x isc=%d type=%d",
+ __entry->cssid, __entry->ssid, __entry->schno,
+ __entry->tpi_info.isc, __entry->tpi_info.type
+ )
+);
+
+/**
+ * s390_cio_adapter_int - An adapter interrupt occurred
+ * @tpi_info: Address of the I/O interruption code
+ */
+TRACE_EVENT(s390_cio_adapter_int,
+ TP_PROTO(struct tpi_info *tpi_info),
+ TP_ARGS(tpi_info),
+ TP_STRUCT__entry(
+ __field_struct(struct tpi_info, tpi_info)
+ ),
+ TP_fast_assign(
+ __entry->tpi_info = *tpi_info;
+ ),
+ TP_printk("isc=%d", __entry->tpi_info.isc)
+);
+
+/**
+ * s390_cio_stcrw - Store Channel Report Word (STCRW) was performed
+ * @crw: Channel Report Word
+ * @cc: Condition code
+ */
+TRACE_EVENT(s390_cio_stcrw,
+ TP_PROTO(struct crw *crw, int cc),
+ TP_ARGS(crw, cc),
+ TP_STRUCT__entry(
+ __field_struct(struct crw, crw)
+ __field(int, cc)
+ ),
+ TP_fast_assign(
+ __entry->crw = *crw;
+ __entry->cc = cc;
+ ),
+ TP_printk("cc=%d slct=%d oflw=%d chn=%d rsc=%d anc=%d erc=0x%x "
+ "rsid=0x%x",
+ __entry->cc, __entry->crw.slct, __entry->crw.oflw,
+ __entry->crw.chn, __entry->crw.rsc, __entry->crw.anc,
+ __entry->crw.erc, __entry->crw.rsid
+ )
+);
+
+#endif /* _TRACE_S390_CIO_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+#include <trace/define_trace.h>
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index 61f768518a34..24ec282e15d8 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -599,8 +599,10 @@ static enum ap_wait ap_sm_read(struct ap_device *ap_dev)
status = ap_sm_recv(ap_dev);
switch (status.response_code) {
case AP_RESPONSE_NORMAL:
- if (ap_dev->queue_count > 0)
+ if (ap_dev->queue_count > 0) {
+ ap_dev->state = AP_STATE_WORKING;
return AP_WAIT_AGAIN;
+ }
ap_dev->state = AP_STATE_IDLE;
return AP_WAIT_NONE;
case AP_RESPONSE_NO_PENDING_REPLY:
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index 9f8fa42c062c..5d3d04c040c2 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -1428,10 +1428,8 @@ int __init zcrypt_debug_init(void)
void zcrypt_debug_exit(void)
{
debugfs_remove(debugfs_root);
- if (zcrypt_dbf_common)
- debug_unregister(zcrypt_dbf_common);
- if (zcrypt_dbf_devices)
- debug_unregister(zcrypt_dbf_devices);
+ debug_unregister(zcrypt_dbf_common);
+ debug_unregister(zcrypt_dbf_devices);
}
/**
diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c
index 05c37d6d4afe..c3e22523faf3 100644
--- a/drivers/s390/net/ctcm_main.c
+++ b/drivers/s390/net/ctcm_main.c
@@ -1677,11 +1677,8 @@ static int ctcm_shutdown_device(struct ccwgroup_device *cgdev)
ccw_device_set_offline(cgdev->cdev[1]);
ccw_device_set_offline(cgdev->cdev[0]);
-
- if (priv->channel[CTCM_READ])
- channel_remove(priv->channel[CTCM_READ]);
- if (priv->channel[CTCM_WRITE])
- channel_remove(priv->channel[CTCM_WRITE]);
+ channel_remove(priv->channel[CTCM_READ]);
+ channel_remove(priv->channel[CTCM_WRITE]);
priv->channel[CTCM_READ] = priv->channel[CTCM_WRITE] = NULL;
return 0;
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index 1766a20ebcb1..ec2e014e885c 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -981,6 +981,10 @@ int qeth_send_setassparms(struct qeth_card *, struct qeth_cmd_buffer *, __u16,
int (*reply_cb)(struct qeth_card *,
struct qeth_reply *, unsigned long),
void *);
+struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *,
+ enum qeth_ipa_funcs,
+ __u16, __u16,
+ enum qeth_prot_versions);
int qeth_start_ipa_tx_checksum(struct qeth_card *);
int qeth_set_rx_csum(struct qeth_card *, int);
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 31ac53fa5cee..787153764120 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -2684,8 +2684,6 @@ void qeth_print_status_message(struct qeth_card *card)
sprintf(card->info.mcl_level, "%02x%02x",
card->info.mcl_level[2],
card->info.mcl_level[3]);
-
- card->info.mcl_level[QETH_MCL_LENGTH] = 0;
break;
}
/* fallthrough */
@@ -5297,10 +5295,10 @@ static int qeth_setassparms_cb(struct qeth_card *card,
return 0;
}
-static struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card,
- enum qeth_ipa_funcs ipa_func,
- __u16 cmd_code, __u16 len,
- enum qeth_prot_versions prot)
+struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card,
+ enum qeth_ipa_funcs ipa_func,
+ __u16 cmd_code, __u16 len,
+ enum qeth_prot_versions prot)
{
struct qeth_cmd_buffer *iob;
struct qeth_ipa_cmd *cmd;
@@ -5319,6 +5317,7 @@ static struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card,
return iob;
}
+EXPORT_SYMBOL_GPL(qeth_get_setassparms_cmd);
int qeth_send_setassparms(struct qeth_card *card,
struct qeth_cmd_buffer *iob, __u16 len, long data,
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 8f1b091e1732..80b1979e8d95 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -1126,6 +1126,7 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
qeth_l2_request_initial_mac(card);
SET_NETDEV_DEV(card->dev, &card->gdev->dev);
netif_napi_add(card->dev, &card->napi, qeth_l2_poll, QETH_NAPI_WEIGHT);
+ netif_carrier_off(card->dev);
return register_netdev(card->dev);
}
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 543960e96b42..7c8c68c26540 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -1043,28 +1043,6 @@ static int qeth_l3_default_setassparms_cb(struct qeth_card *card,
return 0;
}
-static struct qeth_cmd_buffer *qeth_l3_get_setassparms_cmd(
- struct qeth_card *card, enum qeth_ipa_funcs ipa_func, __u16 cmd_code,
- __u16 len, enum qeth_prot_versions prot)
-{
- struct qeth_cmd_buffer *iob;
- struct qeth_ipa_cmd *cmd;
-
- QETH_CARD_TEXT(card, 4, "getasscm");
- iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETASSPARMS, prot);
-
- if (iob) {
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
- cmd->data.setassparms.hdr.assist_no = ipa_func;
- cmd->data.setassparms.hdr.length = 8 + len;
- cmd->data.setassparms.hdr.command_code = cmd_code;
- cmd->data.setassparms.hdr.return_code = 0;
- cmd->data.setassparms.hdr.seq_no = 0;
- }
-
- return iob;
-}
-
#ifdef CONFIG_QETH_IPV6
static int qeth_l3_send_simple_setassparms_ipv6(struct qeth_card *card,
enum qeth_ipa_funcs ipa_func, __u16 cmd_code)
@@ -1073,7 +1051,7 @@ static int qeth_l3_send_simple_setassparms_ipv6(struct qeth_card *card,
struct qeth_cmd_buffer *iob;
QETH_CARD_TEXT(card, 4, "simassp6");
- iob = qeth_l3_get_setassparms_cmd(card, ipa_func, cmd_code,
+ iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code,
0, QETH_PROT_IPV6);
if (!iob)
return -ENOMEM;
@@ -2344,10 +2322,11 @@ static int qeth_l3_query_arp_cache_info(struct qeth_card *card,
QETH_CARD_TEXT_(card, 3, "qarpipv%i", prot);
- iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
- IPA_CMD_ASS_ARP_QUERY_INFO,
- sizeof(struct qeth_arp_query_data) - sizeof(char),
- prot);
+ iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_ARP_QUERY_INFO,
+ sizeof(struct qeth_arp_query_data)
+ - sizeof(char),
+ prot);
if (!iob)
return -ENOMEM;
cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
@@ -2439,7 +2418,7 @@ static int qeth_l3_arp_add_entry(struct qeth_card *card,
return -EOPNOTSUPP;
}
- iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
+ iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
IPA_CMD_ASS_ARP_ADD_ENTRY,
sizeof(struct qeth_arp_cache_entry),
QETH_PROT_IPV4);
@@ -2480,7 +2459,7 @@ static int qeth_l3_arp_remove_entry(struct qeth_card *card,
return -EOPNOTSUPP;
}
memcpy(buf, entry, 12);
- iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
+ iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
IPA_CMD_ASS_ARP_REMOVE_ENTRY,
12,
QETH_PROT_IPV4);
@@ -2818,7 +2797,7 @@ static inline int qeth_l3_tso_elements(struct sk_buff *skb)
{
unsigned long tcpd = (unsigned long)tcp_hdr(skb) +
tcp_hdr(skb)->doff * 4;
- int tcpd_len = skb->len - (tcpd - (unsigned long)skb->data);
+ int tcpd_len = skb_headlen(skb) - (tcpd - (unsigned long)skb->data);
int elements = PFN_UP(tcpd + tcpd_len - 1) - PFN_DOWN(tcpd);
elements += qeth_get_elements_for_frags(skb);
@@ -3220,6 +3199,7 @@ static int qeth_l3_setup_netdev(struct qeth_card *card)
SET_NETDEV_DEV(card->dev, &card->gdev->dev);
netif_napi_add(card->dev, &card->napi, qeth_l3_poll, QETH_NAPI_WEIGHT);
+ netif_carrier_off(card->dev);
return register_netdev(card->dev);
}
diff --git a/drivers/s390/virtio/virtio_ccw.c b/drivers/s390/virtio/virtio_ccw.c
index b2a1a81e6fc8..1b831598df7c 100644
--- a/drivers/s390/virtio/virtio_ccw.c
+++ b/drivers/s390/virtio/virtio_ccw.c
@@ -984,6 +984,36 @@ static struct virtqueue *virtio_ccw_vq_by_ind(struct virtio_ccw_device *vcdev,
return vq;
}
+static void virtio_ccw_check_activity(struct virtio_ccw_device *vcdev,
+ __u32 activity)
+{
+ if (vcdev->curr_io & activity) {
+ switch (activity) {
+ case VIRTIO_CCW_DOING_READ_FEAT:
+ case VIRTIO_CCW_DOING_WRITE_FEAT:
+ case VIRTIO_CCW_DOING_READ_CONFIG:
+ case VIRTIO_CCW_DOING_WRITE_CONFIG:
+ case VIRTIO_CCW_DOING_WRITE_STATUS:
+ case VIRTIO_CCW_DOING_SET_VQ:
+ case VIRTIO_CCW_DOING_SET_IND:
+ case VIRTIO_CCW_DOING_SET_CONF_IND:
+ case VIRTIO_CCW_DOING_RESET:
+ case VIRTIO_CCW_DOING_READ_VQ_CONF:
+ case VIRTIO_CCW_DOING_SET_IND_ADAPTER:
+ case VIRTIO_CCW_DOING_SET_VIRTIO_REV:
+ vcdev->curr_io &= ~activity;
+ wake_up(&vcdev->wait_q);
+ break;
+ default:
+ /* don't know what to do... */
+ dev_warn(&vcdev->cdev->dev,
+ "Suspicious activity '%08x'\n", activity);
+ WARN_ON(1);
+ break;
+ }
+ }
+}
+
static void virtio_ccw_int_handler(struct ccw_device *cdev,
unsigned long intparm,
struct irb *irb)
@@ -995,6 +1025,12 @@ static void virtio_ccw_int_handler(struct ccw_device *cdev,
if (!vcdev)
return;
+ if (IS_ERR(irb)) {
+ vcdev->err = PTR_ERR(irb);
+ virtio_ccw_check_activity(vcdev, activity);
+ /* Don't poke around indicators, something's wrong. */
+ return;
+ }
/* Check if it's a notification from the host. */
if ((intparm == 0) &&
(scsw_stctl(&irb->scsw) ==
@@ -1010,31 +1046,7 @@ static void virtio_ccw_int_handler(struct ccw_device *cdev,
/* Map everything else to -EIO. */
vcdev->err = -EIO;
}
- if (vcdev->curr_io & activity) {
- switch (activity) {
- case VIRTIO_CCW_DOING_READ_FEAT:
- case VIRTIO_CCW_DOING_WRITE_FEAT:
- case VIRTIO_CCW_DOING_READ_CONFIG:
- case VIRTIO_CCW_DOING_WRITE_CONFIG:
- case VIRTIO_CCW_DOING_WRITE_STATUS:
- case VIRTIO_CCW_DOING_SET_VQ:
- case VIRTIO_CCW_DOING_SET_IND:
- case VIRTIO_CCW_DOING_SET_CONF_IND:
- case VIRTIO_CCW_DOING_RESET:
- case VIRTIO_CCW_DOING_READ_VQ_CONF:
- case VIRTIO_CCW_DOING_SET_IND_ADAPTER:
- case VIRTIO_CCW_DOING_SET_VIRTIO_REV:
- vcdev->curr_io &= ~activity;
- wake_up(&vcdev->wait_q);
- break;
- default:
- /* don't know what to do... */
- dev_warn(&cdev->dev, "Suspicious activity '%08x'\n",
- activity);
- WARN_ON(1);
- break;
- }
- }
+ virtio_ccw_check_activity(vcdev, activity);
for_each_set_bit(i, &vcdev->indicators,
sizeof(vcdev->indicators) * BITS_PER_BYTE) {
/* The bit clear must happen before the vring kick. */
diff --git a/drivers/sbus/char/openprom.c b/drivers/sbus/char/openprom.c
index 5843288f64bc..e077ebd89319 100644
--- a/drivers/sbus/char/openprom.c
+++ b/drivers/sbus/char/openprom.c
@@ -390,16 +390,9 @@ static int copyin_string(char __user *user, size_t len, char **ptr)
if ((ssize_t)len < 0 || (ssize_t)(len + 1) < 0)
return -EINVAL;
- tmp = kmalloc(len + 1, GFP_KERNEL);
- if (!tmp)
- return -ENOMEM;
-
- if (copy_from_user(tmp, user, len)) {
- kfree(tmp);
- return -EFAULT;
- }
-
- tmp[len] = '\0';
+ tmp = memdup_user_nul(user, len);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
*ptr = tmp;
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 5f692ae40749..c1fe0d2f90ca 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -194,6 +194,7 @@ config CHR_DEV_SCH
config SCSI_ENCLOSURE
tristate "SCSI Enclosure Support"
depends on SCSI && ENCLOSURE_SERVICES
+ depends on m || SCSI_SAS_ATTRS != m
help
Enclosures are devices sitting on or in SCSI backplanes that
manage devices. If you have a disk cage, the chances are that
@@ -364,6 +365,7 @@ config SCSI_HPSA
tristate "HP Smart Array SCSI driver"
depends on PCI && SCSI
select CHECK_SIGNATURE
+ select SCSI_SAS_ATTRS
help
This driver supports HP Smart Array Controllers (circa 2009).
It is a SCSI alternative to the cciss driver, which is a block
@@ -473,6 +475,7 @@ config SCSI_AACRAID
source "drivers/scsi/aic7xxx/Kconfig.aic7xxx"
source "drivers/scsi/aic7xxx/Kconfig.aic79xx"
source "drivers/scsi/aic94xx/Kconfig"
+source "drivers/scsi/hisi_sas/Kconfig"
source "drivers/scsi/mvsas/Kconfig"
config SCSI_MVUMI
@@ -499,6 +502,7 @@ config SCSI_ADVANSYS
tristate "AdvanSys SCSI support"
depends on SCSI
depends on ISA || EISA || PCI
+ depends on ISA_DMA_API || !ISA
help
This is a driver for all SCSI host adapters manufactured by
AdvanSys. It is documented in the kernel source in
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index c14bca4a9675..862ab4efad61 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -157,6 +157,7 @@ obj-$(CONFIG_CHR_DEV_SCH) += ch.o
obj-$(CONFIG_SCSI_ENCLOSURE) += ses.o
obj-$(CONFIG_SCSI_OSD_INITIATOR) += osd/
+obj-$(CONFIG_SCSI_HISI_SAS) += hisi_sas/
# This goes last, so that "real" scsi devices probe earlier
obj-$(CONFIG_SCSI_DEBUG) += scsi_debug.o
diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c
index 3b6e5c67e853..76eaa38ffd6e 100644
--- a/drivers/scsi/aacraid/linit.c
+++ b/drivers/scsi/aacraid/linit.c
@@ -1318,7 +1318,7 @@ static int aac_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
}
#if (defined(CONFIG_PM))
-void aac_release_resources(struct aac_dev *aac)
+static void aac_release_resources(struct aac_dev *aac)
{
int i;
diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c
index 519f9a4b3dad..febbd83e2ecd 100644
--- a/drivers/scsi/advansys.c
+++ b/drivers/scsi/advansys.c
@@ -7803,7 +7803,7 @@ adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
return ASC_BUSY;
}
scsiqp->sense_addr = cpu_to_le32(sense_addr);
- scsiqp->sense_len = cpu_to_le32(SCSI_SENSE_BUFFERSIZE);
+ scsiqp->sense_len = SCSI_SENSE_BUFFERSIZE;
/* Build ADV_SCSI_REQ_Q */
diff --git a/drivers/scsi/aic94xx/aic94xx_sas.h b/drivers/scsi/aic94xx/aic94xx_sas.h
index 912e6b755f74..101072cab70f 100644
--- a/drivers/scsi/aic94xx/aic94xx_sas.h
+++ b/drivers/scsi/aic94xx/aic94xx_sas.h
@@ -327,46 +327,9 @@ struct scb_header {
#define LUN_SIZE 8
-/* See SAS spec, task IU
- */
-struct ssp_task_iu {
- u8 lun[LUN_SIZE]; /* BE */
- u16 _r_a;
- u8 tmf;
- u8 _r_b;
- __be16 tag; /* BE */
- u8 _r_c[14];
-} __attribute__ ((packed));
-
-/* See SAS spec, command IU
- */
-struct ssp_command_iu {
- u8 lun[LUN_SIZE];
- u8 _r_a;
- u8 efb_prio_attr; /* enable first burst, task prio & attr */
-#define EFB_MASK 0x80
-#define TASK_PRIO_MASK 0x78
-#define TASK_ATTR_MASK 0x07
-
- u8 _r_b;
- u8 add_cdb_len; /* in dwords, since bit 0,1 are reserved */
- union {
- u8 cdb[16];
- struct {
- __le64 long_cdb_addr; /* bus address, LE */
- __le32 long_cdb_size; /* LE */
- u8 _r_c[3];
- u8 eol_ds; /* eol:6,6, ds:5,4 */
- } long_cdb; /* sequencer extension */
- };
-} __attribute__ ((packed));
-
-struct xfer_rdy_iu {
- __be32 requested_offset; /* BE */
- __be32 write_data_len; /* BE */
- __be32 _r_a;
-} __attribute__ ((packed));
-
+#define EFB_MASK 0x80
+#define TASK_PRIO_MASK 0x78
+#define TASK_ATTR_MASK 0x07
/* ---------- SCB tasks ---------- */
/* This is both ssp_task and long_ssp_task
@@ -511,7 +474,7 @@ struct abort_task {
u8 proto_conn_rate;
__le32 _r_a;
struct ssp_frame_hdr ssp_frame;
- struct ssp_task_iu ssp_task;
+ struct ssp_tmf_iu ssp_task;
__le16 sister_scb;
__le16 conn_handle;
u8 flags; /* ovrd_itnl_timer:3,3, suspend_data_trans:2,2 */
@@ -549,7 +512,7 @@ struct clear_nexus {
u8 _r_b[3];
u8 conn_mask;
u8 _r_c[19];
- struct ssp_task_iu ssp_task; /* LUN and TAG */
+ struct ssp_tmf_iu ssp_task; /* LUN and TAG */
__le16 _r_d;
__le16 conn_handle;
__le64 _r_e;
@@ -562,7 +525,7 @@ struct initiate_ssp_tmf {
u8 proto_conn_rate;
__le32 _r_a;
struct ssp_frame_hdr ssp_frame;
- struct ssp_task_iu ssp_task;
+ struct ssp_tmf_iu ssp_task;
__le16 sister_scb;
__le16 conn_handle;
u8 flags; /* itnl override and suspend data tx */
diff --git a/drivers/scsi/arcmsr/arcmsr.h b/drivers/scsi/arcmsr/arcmsr.h
index 3bcaaac0ae4b..cf99f8cf4cdd 100644
--- a/drivers/scsi/arcmsr/arcmsr.h
+++ b/drivers/scsi/arcmsr/arcmsr.h
@@ -52,7 +52,7 @@ struct device_attribute;
#define ARCMSR_MAX_FREECCB_NUM 320
#define ARCMSR_MAX_OUTSTANDING_CMD 255
#endif
-#define ARCMSR_DRIVER_VERSION "v1.30.00.04-20140919"
+#define ARCMSR_DRIVER_VERSION "v1.30.00.22-20151126"
#define ARCMSR_SCSI_INITIATOR_ID 255
#define ARCMSR_MAX_XFER_SECTORS 512
#define ARCMSR_MAX_XFER_SECTORS_B 4096
@@ -74,6 +74,9 @@ struct device_attribute;
#ifndef PCI_DEVICE_ID_ARECA_1214
#define PCI_DEVICE_ID_ARECA_1214 0x1214
#endif
+#ifndef PCI_DEVICE_ID_ARECA_1203
+ #define PCI_DEVICE_ID_ARECA_1203 0x1203
+#endif
/*
**********************************************************************************
**
@@ -245,6 +248,12 @@ struct FIRMWARE_INFO
/* window of "instruction flags" from iop to driver */
#define ARCMSR_IOP2DRV_DOORBELL 0x00020408
#define ARCMSR_IOP2DRV_DOORBELL_MASK 0x0002040C
+/* window of "instruction flags" from iop to driver */
+#define ARCMSR_IOP2DRV_DOORBELL_1203 0x00021870
+#define ARCMSR_IOP2DRV_DOORBELL_MASK_1203 0x00021874
+/* window of "instruction flags" from driver to iop */
+#define ARCMSR_DRV2IOP_DOORBELL_1203 0x00021878
+#define ARCMSR_DRV2IOP_DOORBELL_MASK_1203 0x0002187C
/* ARECA FLAG LANGUAGE */
/* ioctl transfer */
#define ARCMSR_IOP2DRV_DATA_WRITE_OK 0x00000001
@@ -288,6 +297,9 @@ struct FIRMWARE_INFO
#define ARCMSR_MESSAGE_RBUFFER 0x0000ff00
/* iop message_rwbuffer for message command */
#define ARCMSR_MESSAGE_RWBUFFER 0x0000fa00
+
+#define MEM_BASE0(x) (u32 __iomem *)((unsigned long)acb->mem_base0 + x)
+#define MEM_BASE1(x) (u32 __iomem *)((unsigned long)acb->mem_base1 + x)
/*
************************************************************************
** SPEC. for Areca HBC adapter
diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c
index 333db5953607..7640498964a5 100644
--- a/drivers/scsi/arcmsr/arcmsr_hba.c
+++ b/drivers/scsi/arcmsr/arcmsr_hba.c
@@ -114,6 +114,7 @@ static void arcmsr_hardware_reset(struct AdapterControlBlock *acb);
static const char *arcmsr_info(struct Scsi_Host *);
static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb);
static void arcmsr_free_irq(struct pci_dev *, struct AdapterControlBlock *);
+static void arcmsr_wait_firmware_ready(struct AdapterControlBlock *acb);
static int arcmsr_adjust_disk_queue_depth(struct scsi_device *sdev, int queue_depth)
{
if (queue_depth > ARCMSR_MAX_CMD_PERLUN)
@@ -157,6 +158,8 @@ static struct pci_device_id arcmsr_device_id_table[] = {
.driver_data = ACB_ADAPTER_TYPE_B},
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1202),
.driver_data = ACB_ADAPTER_TYPE_B},
+ {PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1203),
+ .driver_data = ACB_ADAPTER_TYPE_B},
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1210),
.driver_data = ACB_ADAPTER_TYPE_A},
{PCI_DEVICE(PCI_VENDOR_ID_ARECA, PCI_DEVICE_ID_ARECA_1214),
@@ -495,6 +498,91 @@ static void arcmsr_flush_adapter_cache(struct AdapterControlBlock *acb)
}
}
+static bool arcmsr_alloc_io_queue(struct AdapterControlBlock *acb)
+{
+ bool rtn = true;
+ void *dma_coherent;
+ dma_addr_t dma_coherent_handle;
+ struct pci_dev *pdev = acb->pdev;
+
+ switch (acb->adapter_type) {
+ case ACB_ADAPTER_TYPE_B: {
+ struct MessageUnit_B *reg;
+ acb->roundup_ccbsize = roundup(sizeof(struct MessageUnit_B), 32);
+ dma_coherent = dma_zalloc_coherent(&pdev->dev, acb->roundup_ccbsize,
+ &dma_coherent_handle, GFP_KERNEL);
+ if (!dma_coherent) {
+ pr_notice("arcmsr%d: DMA allocation failed\n", acb->host->host_no);
+ return false;
+ }
+ acb->dma_coherent_handle2 = dma_coherent_handle;
+ acb->dma_coherent2 = dma_coherent;
+ reg = (struct MessageUnit_B *)dma_coherent;
+ acb->pmuB = reg;
+ if (acb->pdev->device == PCI_DEVICE_ID_ARECA_1203) {
+ reg->drv2iop_doorbell = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL_1203);
+ reg->drv2iop_doorbell_mask = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL_MASK_1203);
+ reg->iop2drv_doorbell = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL_1203);
+ reg->iop2drv_doorbell_mask = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL_MASK_1203);
+ } else {
+ reg->drv2iop_doorbell = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL);
+ reg->drv2iop_doorbell_mask = MEM_BASE0(ARCMSR_DRV2IOP_DOORBELL_MASK);
+ reg->iop2drv_doorbell = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL);
+ reg->iop2drv_doorbell_mask = MEM_BASE0(ARCMSR_IOP2DRV_DOORBELL_MASK);
+ }
+ reg->message_wbuffer = MEM_BASE1(ARCMSR_MESSAGE_WBUFFER);
+ reg->message_rbuffer = MEM_BASE1(ARCMSR_MESSAGE_RBUFFER);
+ reg->message_rwbuffer = MEM_BASE1(ARCMSR_MESSAGE_RWBUFFER);
+ }
+ break;
+ case ACB_ADAPTER_TYPE_D: {
+ struct MessageUnit_D *reg;
+
+ acb->roundup_ccbsize = roundup(sizeof(struct MessageUnit_D), 32);
+ dma_coherent = dma_zalloc_coherent(&pdev->dev, acb->roundup_ccbsize,
+ &dma_coherent_handle, GFP_KERNEL);
+ if (!dma_coherent) {
+ pr_notice("arcmsr%d: DMA allocation failed\n", acb->host->host_no);
+ return false;
+ }
+ acb->dma_coherent_handle2 = dma_coherent_handle;
+ acb->dma_coherent2 = dma_coherent;
+ reg = (struct MessageUnit_D *)dma_coherent;
+ acb->pmuD = reg;
+ reg->chip_id = MEM_BASE0(ARCMSR_ARC1214_CHIP_ID);
+ reg->cpu_mem_config = MEM_BASE0(ARCMSR_ARC1214_CPU_MEMORY_CONFIGURATION);
+ reg->i2o_host_interrupt_mask = MEM_BASE0(ARCMSR_ARC1214_I2_HOST_INTERRUPT_MASK);
+ reg->sample_at_reset = MEM_BASE0(ARCMSR_ARC1214_SAMPLE_RESET);
+ reg->reset_request = MEM_BASE0(ARCMSR_ARC1214_RESET_REQUEST);
+ reg->host_int_status = MEM_BASE0(ARCMSR_ARC1214_MAIN_INTERRUPT_STATUS);
+ reg->pcief0_int_enable = MEM_BASE0(ARCMSR_ARC1214_PCIE_F0_INTERRUPT_ENABLE);
+ reg->inbound_msgaddr0 = MEM_BASE0(ARCMSR_ARC1214_INBOUND_MESSAGE0);
+ reg->inbound_msgaddr1 = MEM_BASE0(ARCMSR_ARC1214_INBOUND_MESSAGE1);
+ reg->outbound_msgaddr0 = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_MESSAGE0);
+ reg->outbound_msgaddr1 = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_MESSAGE1);
+ reg->inbound_doorbell = MEM_BASE0(ARCMSR_ARC1214_INBOUND_DOORBELL);
+ reg->outbound_doorbell = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_DOORBELL);
+ reg->outbound_doorbell_enable = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_DOORBELL_ENABLE);
+ reg->inboundlist_base_low = MEM_BASE0(ARCMSR_ARC1214_INBOUND_LIST_BASE_LOW);
+ reg->inboundlist_base_high = MEM_BASE0(ARCMSR_ARC1214_INBOUND_LIST_BASE_HIGH);
+ reg->inboundlist_write_pointer = MEM_BASE0(ARCMSR_ARC1214_INBOUND_LIST_WRITE_POINTER);
+ reg->outboundlist_base_low = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_BASE_LOW);
+ reg->outboundlist_base_high = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_BASE_HIGH);
+ reg->outboundlist_copy_pointer = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_COPY_POINTER);
+ reg->outboundlist_read_pointer = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_LIST_READ_POINTER);
+ reg->outboundlist_interrupt_cause = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_INTERRUPT_CAUSE);
+ reg->outboundlist_interrupt_enable = MEM_BASE0(ARCMSR_ARC1214_OUTBOUND_INTERRUPT_ENABLE);
+ reg->message_wbuffer = MEM_BASE0(ARCMSR_ARC1214_MESSAGE_WBUFFER);
+ reg->message_rbuffer = MEM_BASE0(ARCMSR_ARC1214_MESSAGE_RBUFFER);
+ reg->msgcode_rwbuffer = MEM_BASE0(ARCMSR_ARC1214_MESSAGE_RWBUFFER);
+ }
+ break;
+ default:
+ break;
+ }
+ return rtn;
+}
+
static int arcmsr_alloc_ccb_pool(struct AdapterControlBlock *acb)
{
struct pci_dev *pdev = acb->pdev;
@@ -739,9 +827,12 @@ static int arcmsr_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if(!error){
goto pci_release_regs;
}
+ error = arcmsr_alloc_io_queue(acb);
+ if (!error)
+ goto unmap_pci_region;
error = arcmsr_get_firmware_spec(acb);
if(!error){
- goto unmap_pci_region;
+ goto free_hbb_mu;
}
error = arcmsr_alloc_ccb_pool(acb);
if(error){
@@ -2622,9 +2713,6 @@ static bool arcmsr_hbaA_get_config(struct AdapterControlBlock *acb)
static bool arcmsr_hbaB_get_config(struct AdapterControlBlock *acb)
{
struct MessageUnit_B *reg = acb->pmuB;
- struct pci_dev *pdev = acb->pdev;
- void *dma_coherent;
- dma_addr_t dma_coherent_handle;
char *acb_firm_model = acb->firm_model;
char *acb_firm_version = acb->firm_version;
char *acb_device_map = acb->device_map;
@@ -2636,30 +2724,16 @@ static bool arcmsr_hbaB_get_config(struct AdapterControlBlock *acb)
/*firm_version,21,84-99*/
int count;
- acb->roundup_ccbsize = roundup(sizeof(struct MessageUnit_B), 32);
- dma_coherent = dma_alloc_coherent(&pdev->dev, acb->roundup_ccbsize,
- &dma_coherent_handle, GFP_KERNEL);
- if (!dma_coherent){
- printk(KERN_NOTICE
- "arcmsr%d: dma_alloc_coherent got error for hbb mu\n",
- acb->host->host_no);
- return false;
- }
- acb->dma_coherent_handle2 = dma_coherent_handle;
- acb->dma_coherent2 = dma_coherent;
- reg = (struct MessageUnit_B *)dma_coherent;
- acb->pmuB = reg;
- reg->drv2iop_doorbell= (uint32_t __iomem *)((unsigned long)acb->mem_base0 + ARCMSR_DRV2IOP_DOORBELL);
- reg->drv2iop_doorbell_mask = (uint32_t __iomem *)((unsigned long)acb->mem_base0 + ARCMSR_DRV2IOP_DOORBELL_MASK);
- reg->iop2drv_doorbell = (uint32_t __iomem *)((unsigned long)acb->mem_base0 + ARCMSR_IOP2DRV_DOORBELL);
- reg->iop2drv_doorbell_mask = (uint32_t __iomem *)((unsigned long)acb->mem_base0 + ARCMSR_IOP2DRV_DOORBELL_MASK);
- reg->message_wbuffer = (uint32_t __iomem *)((unsigned long)acb->mem_base1 + ARCMSR_MESSAGE_WBUFFER);
- reg->message_rbuffer = (uint32_t __iomem *)((unsigned long)acb->mem_base1 + ARCMSR_MESSAGE_RBUFFER);
- reg->message_rwbuffer = (uint32_t __iomem *)((unsigned long)acb->mem_base1 + ARCMSR_MESSAGE_RWBUFFER);
iop_firm_model = (char __iomem *)(&reg->message_rwbuffer[15]); /*firm_model,15,60-67*/
iop_firm_version = (char __iomem *)(&reg->message_rwbuffer[17]); /*firm_version,17,68-83*/
iop_device_map = (char __iomem *)(&reg->message_rwbuffer[21]); /*firm_version,21,84-99*/
+ arcmsr_wait_firmware_ready(acb);
+ writel(ARCMSR_MESSAGE_START_DRIVER_MODE, reg->drv2iop_doorbell);
+ if (!arcmsr_hbaB_wait_msgint_ready(acb)) {
+ printk(KERN_ERR "arcmsr%d: can't set driver mode.\n", acb->host->host_no);
+ return false;
+ }
writel(ARCMSR_MESSAGE_GET_CONFIG, reg->drv2iop_doorbell);
if (!arcmsr_hbaB_wait_msgint_ready(acb)) {
printk(KERN_NOTICE "arcmsr%d: wait 'get adapter firmware \
@@ -2694,15 +2768,15 @@ static bool arcmsr_hbaB_get_config(struct AdapterControlBlock *acb)
acb->firm_model,
acb->firm_version);
- acb->signature = readl(&reg->message_rwbuffer[1]);
+ acb->signature = readl(&reg->message_rwbuffer[0]);
/*firm_signature,1,00-03*/
- acb->firm_request_len = readl(&reg->message_rwbuffer[2]);
+ acb->firm_request_len = readl(&reg->message_rwbuffer[1]);
/*firm_request_len,1,04-07*/
- acb->firm_numbers_queue = readl(&reg->message_rwbuffer[3]);
+ acb->firm_numbers_queue = readl(&reg->message_rwbuffer[2]);
/*firm_numbers_queue,2,08-11*/
- acb->firm_sdram_size = readl(&reg->message_rwbuffer[4]);
+ acb->firm_sdram_size = readl(&reg->message_rwbuffer[3]);
/*firm_sdram_size,3,12-15*/
- acb->firm_hd_channels = readl(&reg->message_rwbuffer[5]);
+ acb->firm_hd_channels = readl(&reg->message_rwbuffer[4]);
/*firm_ide_channels,4,16-19*/
acb->firm_cfg_version = readl(&reg->message_rwbuffer[25]); /*firm_cfg_version,25,100-103*/
/*firm_ide_channels,4,16-19*/
@@ -2777,70 +2851,8 @@ static bool arcmsr_hbaD_get_config(struct AdapterControlBlock *acb)
char __iomem *iop_firm_version;
char __iomem *iop_device_map;
u32 count;
- struct MessageUnit_D *reg;
- void *dma_coherent2;
- dma_addr_t dma_coherent_handle2;
- struct pci_dev *pdev = acb->pdev;
+ struct MessageUnit_D *reg = acb->pmuD;
- acb->roundup_ccbsize = roundup(sizeof(struct MessageUnit_D), 32);
- dma_coherent2 = dma_alloc_coherent(&pdev->dev, acb->roundup_ccbsize,
- &dma_coherent_handle2, GFP_KERNEL);
- if (!dma_coherent2) {
- pr_notice("DMA allocation failed...\n");
- return false;
- }
- memset(dma_coherent2, 0, acb->roundup_ccbsize);
- acb->dma_coherent_handle2 = dma_coherent_handle2;
- acb->dma_coherent2 = dma_coherent2;
- reg = (struct MessageUnit_D *)dma_coherent2;
- acb->pmuD = reg;
- reg->chip_id = acb->mem_base0 + ARCMSR_ARC1214_CHIP_ID;
- reg->cpu_mem_config = acb->mem_base0 +
- ARCMSR_ARC1214_CPU_MEMORY_CONFIGURATION;
- reg->i2o_host_interrupt_mask = acb->mem_base0 +
- ARCMSR_ARC1214_I2_HOST_INTERRUPT_MASK;
- reg->sample_at_reset = acb->mem_base0 + ARCMSR_ARC1214_SAMPLE_RESET;
- reg->reset_request = acb->mem_base0 + ARCMSR_ARC1214_RESET_REQUEST;
- reg->host_int_status = acb->mem_base0 +
- ARCMSR_ARC1214_MAIN_INTERRUPT_STATUS;
- reg->pcief0_int_enable = acb->mem_base0 +
- ARCMSR_ARC1214_PCIE_F0_INTERRUPT_ENABLE;
- reg->inbound_msgaddr0 = acb->mem_base0 +
- ARCMSR_ARC1214_INBOUND_MESSAGE0;
- reg->inbound_msgaddr1 = acb->mem_base0 +
- ARCMSR_ARC1214_INBOUND_MESSAGE1;
- reg->outbound_msgaddr0 = acb->mem_base0 +
- ARCMSR_ARC1214_OUTBOUND_MESSAGE0;
- reg->outbound_msgaddr1 = acb->mem_base0 +
- ARCMSR_ARC1214_OUTBOUND_MESSAGE1;
- reg->inbound_doorbell = acb->mem_base0 +
- ARCMSR_ARC1214_INBOUND_DOORBELL;
- reg->outbound_doorbell = acb->mem_base0 +
- ARCMSR_ARC1214_OUTBOUND_DOORBELL;
- reg->outbound_doorbell_enable = acb->mem_base0 +
- ARCMSR_ARC1214_OUTBOUND_DOORBELL_ENABLE;
- reg->inboundlist_base_low = acb->mem_base0 +
- ARCMSR_ARC1214_INBOUND_LIST_BASE_LOW;
- reg->inboundlist_base_high = acb->mem_base0 +
- ARCMSR_ARC1214_INBOUND_LIST_BASE_HIGH;
- reg->inboundlist_write_pointer = acb->mem_base0 +
- ARCMSR_ARC1214_INBOUND_LIST_WRITE_POINTER;
- reg->outboundlist_base_low = acb->mem_base0 +
- ARCMSR_ARC1214_OUTBOUND_LIST_BASE_LOW;
- reg->outboundlist_base_high = acb->mem_base0 +
- ARCMSR_ARC1214_OUTBOUND_LIST_BASE_HIGH;
- reg->outboundlist_copy_pointer = acb->mem_base0 +
- ARCMSR_ARC1214_OUTBOUND_LIST_COPY_POINTER;
- reg->outboundlist_read_pointer = acb->mem_base0 +
- ARCMSR_ARC1214_OUTBOUND_LIST_READ_POINTER;
- reg->outboundlist_interrupt_cause = acb->mem_base0 +
- ARCMSR_ARC1214_OUTBOUND_INTERRUPT_CAUSE;
- reg->outboundlist_interrupt_enable = acb->mem_base0 +
- ARCMSR_ARC1214_OUTBOUND_INTERRUPT_ENABLE;
- reg->message_wbuffer = acb->mem_base0 + ARCMSR_ARC1214_MESSAGE_WBUFFER;
- reg->message_rbuffer = acb->mem_base0 + ARCMSR_ARC1214_MESSAGE_RBUFFER;
- reg->msgcode_rwbuffer = acb->mem_base0 +
- ARCMSR_ARC1214_MESSAGE_RWBUFFER;
iop_firm_model = (char __iomem *)(&reg->msgcode_rwbuffer[15]);
iop_firm_version = (char __iomem *)(&reg->msgcode_rwbuffer[17]);
iop_device_map = (char __iomem *)(&reg->msgcode_rwbuffer[21]);
@@ -2855,8 +2867,6 @@ static bool arcmsr_hbaD_get_config(struct AdapterControlBlock *acb)
if (!arcmsr_hbaD_wait_msgint_ready(acb)) {
pr_notice("arcmsr%d: wait get adapter firmware "
"miscellaneous data timeout\n", acb->host->host_no);
- dma_free_coherent(&acb->pdev->dev, acb->roundup_ccbsize,
- acb->dma_coherent2, acb->dma_coherent_handle2);
return false;
}
count = 8;
@@ -2880,15 +2890,15 @@ static bool arcmsr_hbaD_get_config(struct AdapterControlBlock *acb)
iop_device_map++;
count--;
}
- acb->signature = readl(&reg->msgcode_rwbuffer[1]);
+ acb->signature = readl(&reg->msgcode_rwbuffer[0]);
/*firm_signature,1,00-03*/
- acb->firm_request_len = readl(&reg->msgcode_rwbuffer[2]);
+ acb->firm_request_len = readl(&reg->msgcode_rwbuffer[1]);
/*firm_request_len,1,04-07*/
- acb->firm_numbers_queue = readl(&reg->msgcode_rwbuffer[3]);
+ acb->firm_numbers_queue = readl(&reg->msgcode_rwbuffer[2]);
/*firm_numbers_queue,2,08-11*/
- acb->firm_sdram_size = readl(&reg->msgcode_rwbuffer[4]);
+ acb->firm_sdram_size = readl(&reg->msgcode_rwbuffer[3]);
/*firm_sdram_size,3,12-15*/
- acb->firm_hd_channels = readl(&reg->msgcode_rwbuffer[5]);
+ acb->firm_hd_channels = readl(&reg->msgcode_rwbuffer[4]);
/*firm_hd_channels,4,16-19*/
acb->firm_cfg_version = readl(&reg->msgcode_rwbuffer[25]);
pr_notice("Areca RAID Controller%d: Model %s, F/W %s\n",
@@ -3998,6 +4008,7 @@ static const char *arcmsr_info(struct Scsi_Host *host)
case PCI_DEVICE_ID_ARECA_1160:
case PCI_DEVICE_ID_ARECA_1170:
case PCI_DEVICE_ID_ARECA_1201:
+ case PCI_DEVICE_ID_ARECA_1203:
case PCI_DEVICE_ID_ARECA_1220:
case PCI_DEVICE_ID_ARECA_1230:
case PCI_DEVICE_ID_ARECA_1260:
diff --git a/drivers/scsi/atp870u.c b/drivers/scsi/atp870u.c
index 05301bc752ee..8b52a9dbb9cf 100644
--- a/drivers/scsi/atp870u.c
+++ b/drivers/scsi/atp870u.c
@@ -41,77 +41,128 @@
static struct scsi_host_template atp870u_template;
static void send_s870(struct atp_unit *dev,unsigned char c);
-static void is885(struct atp_unit *dev, unsigned int wkport,unsigned char c);
-static void tscam_885(void);
+static void atp_is(struct atp_unit *dev, unsigned char c, bool wide_chip, unsigned char lvdmode);
+
+static inline void atp_writeb_base(struct atp_unit *atp, u8 reg, u8 val)
+{
+ outb(val, atp->baseport + reg);
+}
+
+static inline void atp_writew_base(struct atp_unit *atp, u8 reg, u16 val)
+{
+ outw(val, atp->baseport + reg);
+}
+
+static inline void atp_writeb_io(struct atp_unit *atp, u8 channel, u8 reg, u8 val)
+{
+ outb(val, atp->ioport[channel] + reg);
+}
+
+static inline void atp_writew_io(struct atp_unit *atp, u8 channel, u8 reg, u16 val)
+{
+ outw(val, atp->ioport[channel] + reg);
+}
+
+static inline void atp_writeb_pci(struct atp_unit *atp, u8 channel, u8 reg, u8 val)
+{
+ outb(val, atp->pciport[channel] + reg);
+}
+
+static inline void atp_writel_pci(struct atp_unit *atp, u8 channel, u8 reg, u32 val)
+{
+ outl(val, atp->pciport[channel] + reg);
+}
+
+static inline u8 atp_readb_base(struct atp_unit *atp, u8 reg)
+{
+ return inb(atp->baseport + reg);
+}
+
+static inline u16 atp_readw_base(struct atp_unit *atp, u8 reg)
+{
+ return inw(atp->baseport + reg);
+}
+
+static inline u32 atp_readl_base(struct atp_unit *atp, u8 reg)
+{
+ return inl(atp->baseport + reg);
+}
+
+static inline u8 atp_readb_io(struct atp_unit *atp, u8 channel, u8 reg)
+{
+ return inb(atp->ioport[channel] + reg);
+}
+
+static inline u16 atp_readw_io(struct atp_unit *atp, u8 channel, u8 reg)
+{
+ return inw(atp->ioport[channel] + reg);
+}
+
+static inline u8 atp_readb_pci(struct atp_unit *atp, u8 channel, u8 reg)
+{
+ return inb(atp->pciport[channel] + reg);
+}
+
+static inline bool is880(struct atp_unit *atp)
+{
+ return atp->pdev->device == ATP880_DEVID1 ||
+ atp->pdev->device == ATP880_DEVID2;
+}
+
+static inline bool is885(struct atp_unit *atp)
+{
+ return atp->pdev->device == ATP885_DEVID;
+}
static irqreturn_t atp870u_intr_handle(int irq, void *dev_id)
{
unsigned long flags;
- unsigned short int tmpcip, id;
+ unsigned short int id;
unsigned char i, j, c, target_id, lun,cmdp;
unsigned char *prd;
struct scsi_cmnd *workreq;
- unsigned int workport, tmport, tmport1;
unsigned long adrcnt, k;
#ifdef ED_DBGP
unsigned long l;
#endif
- int errstus;
struct Scsi_Host *host = dev_id;
struct atp_unit *dev = (struct atp_unit *)&host->hostdata;
for (c = 0; c < 2; c++) {
- tmport = dev->ioport[c] + 0x1f;
- j = inb(tmport);
+ j = atp_readb_io(dev, c, 0x1f);
if ((j & 0x80) != 0)
- {
- goto ch_sel;
- }
+ break;
dev->in_int[c] = 0;
}
- return IRQ_NONE;
-ch_sel:
+ if ((j & 0x80) == 0)
+ return IRQ_NONE;
#ifdef ED_DBGP
printk("atp870u_intr_handle enter\n");
#endif
dev->in_int[c] = 1;
- cmdp = inb(dev->ioport[c] + 0x10);
- workport = dev->ioport[c];
+ cmdp = atp_readb_io(dev, c, 0x10);
if (dev->working[c] != 0) {
- if (dev->dev_id == ATP885_DEVID) {
- tmport1 = workport + 0x16;
- if ((inb(tmport1) & 0x80) == 0)
- outb((inb(tmport1) | 0x80), tmport1);
+ if (is885(dev)) {
+ if ((atp_readb_io(dev, c, 0x16) & 0x80) == 0)
+ atp_writeb_io(dev, c, 0x16, (atp_readb_io(dev, c, 0x16) | 0x80));
}
- tmpcip = dev->pciport[c];
- if ((inb(tmpcip) & 0x08) != 0)
+ if ((atp_readb_pci(dev, c, 0x00) & 0x08) != 0)
{
- tmpcip += 0x2;
for (k=0; k < 1000; k++) {
- if ((inb(tmpcip) & 0x08) == 0) {
- goto stop_dma;
- }
- if ((inb(tmpcip) & 0x01) == 0) {
- goto stop_dma;
- }
+ if ((atp_readb_pci(dev, c, 2) & 0x08) == 0)
+ break;
+ if ((atp_readb_pci(dev, c, 2) & 0x01) == 0)
+ break;
}
}
-stop_dma:
- tmpcip = dev->pciport[c];
- outb(0x00, tmpcip);
- tmport -= 0x08;
+ atp_writeb_pci(dev, c, 0, 0x00);
- i = inb(tmport);
+ i = atp_readb_io(dev, c, 0x17);
- if (dev->dev_id == ATP885_DEVID) {
- tmpcip += 2;
- outb(0x06, tmpcip);
- tmpcip -= 2;
- }
+ if (is885(dev))
+ atp_writeb_pci(dev, c, 2, 0x06);
- tmport -= 0x02;
- target_id = inb(tmport);
- tmport += 0x02;
+ target_id = atp_readb_io(dev, c, 0x15);
/*
* Remap wide devices onto id numbers
@@ -129,7 +180,7 @@ stop_dma:
}
dev->last_cmd[c] |= 0x40;
}
- if (dev->dev_id == ATP885_DEVID)
+ if (is885(dev))
dev->r1f[c][target_id] |= j;
#ifdef ED_DBGP
printk("atp870u_intr_handle status = %x\n",i);
@@ -138,12 +189,11 @@ stop_dma:
if ((dev->last_cmd[c] & 0xf0) != 0x40) {
dev->last_cmd[c] = 0xff;
}
- if (dev->dev_id == ATP885_DEVID) {
- tmport -= 0x05;
+ if (is885(dev)) {
adrcnt = 0;
- ((unsigned char *) &adrcnt)[2] = inb(tmport++);
- ((unsigned char *) &adrcnt)[1] = inb(tmport++);
- ((unsigned char *) &adrcnt)[0] = inb(tmport);
+ ((unsigned char *) &adrcnt)[2] = atp_readb_io(dev, c, 0x12);
+ ((unsigned char *) &adrcnt)[1] = atp_readb_io(dev, c, 0x13);
+ ((unsigned char *) &adrcnt)[0] = atp_readb_io(dev, c, 0x14);
if (dev->id[c][target_id].last_len != adrcnt)
{
k = dev->id[c][target_id].last_len;
@@ -152,7 +202,7 @@ stop_dma:
dev->id[c][target_id].last_len = adrcnt;
}
#ifdef ED_DBGP
- printk("tmport = %x dev->id[c][target_id].last_len = %d dev->id[c][target_id].tran_len = %d\n",tmport,dev->id[c][target_id].last_len,dev->id[c][target_id].tran_len);
+ printk("dev->id[c][target_id].last_len = %d dev->id[c][target_id].tran_len = %d\n",dev->id[c][target_id].last_len,dev->id[c][target_id].tran_len);
#endif
}
@@ -160,11 +210,9 @@ stop_dma:
* Flip wide
*/
if (dev->wide_id[c] != 0) {
- tmport = workport + 0x1b;
- outb(0x01, tmport);
- while ((inb(tmport) & 0x01) != 0x01) {
- outb(0x01, tmport);
- }
+ atp_writeb_io(dev, c, 0x1b, 0x01);
+ while ((atp_readb_io(dev, c, 0x1b) & 0x01) != 0x01)
+ atp_writeb_io(dev, c, 0x1b, 0x01);
}
/*
* Issue more commands
@@ -185,37 +233,34 @@ stop_dma:
#ifdef ED_DBGP
printk("Status 0x85 return\n");
#endif
- goto handled;
+ return IRQ_HANDLED;
}
if (i == 0x40) {
dev->last_cmd[c] |= 0x40;
dev->in_int[c] = 0;
- goto handled;
+ return IRQ_HANDLED;
}
if (i == 0x21) {
if ((dev->last_cmd[c] & 0xf0) != 0x40) {
dev->last_cmd[c] = 0xff;
}
- tmport -= 0x05;
adrcnt = 0;
- ((unsigned char *) &adrcnt)[2] = inb(tmport++);
- ((unsigned char *) &adrcnt)[1] = inb(tmport++);
- ((unsigned char *) &adrcnt)[0] = inb(tmport);
+ ((unsigned char *) &adrcnt)[2] = atp_readb_io(dev, c, 0x12);
+ ((unsigned char *) &adrcnt)[1] = atp_readb_io(dev, c, 0x13);
+ ((unsigned char *) &adrcnt)[0] = atp_readb_io(dev, c, 0x14);
k = dev->id[c][target_id].last_len;
k -= adrcnt;
dev->id[c][target_id].tran_len = k;
dev->id[c][target_id].last_len = adrcnt;
- tmport -= 0x04;
- outb(0x41, tmport);
- tmport += 0x08;
- outb(0x08, tmport);
+ atp_writeb_io(dev, c, 0x10, 0x41);
+ atp_writeb_io(dev, c, 0x18, 0x08);
dev->in_int[c] = 0;
- goto handled;
+ return IRQ_HANDLED;
}
- if (dev->dev_id == ATP885_DEVID) {
+ if (is885(dev)) {
if ((i == 0x4c) || (i == 0x4d) || (i == 0x8c) || (i == 0x8d)) {
if ((i == 0x4c) || (i == 0x8c))
i=0x48;
@@ -229,11 +274,9 @@ stop_dma:
printk(KERN_DEBUG "Device reselect\n");
#endif
lun = 0;
- tmport -= 0x07;
- if (cmdp == 0x44 || i==0x80) {
- tmport += 0x0d;
- lun = inb(tmport) & 0x07;
- } else {
+ if (cmdp == 0x44 || i == 0x80)
+ lun = atp_readb_io(dev, c, 0x1d) & 0x07;
+ else {
if ((dev->last_cmd[c] & 0xf0) != 0x40) {
dev->last_cmd[c] = 0xff;
}
@@ -241,49 +284,41 @@ stop_dma:
#ifdef ED_DBGP
printk("cmdp = 0x41\n");
#endif
- tmport += 0x02;
adrcnt = 0;
- ((unsigned char *) &adrcnt)[2] = inb(tmport++);
- ((unsigned char *) &adrcnt)[1] = inb(tmport++);
- ((unsigned char *) &adrcnt)[0] = inb(tmport);
+ ((unsigned char *) &adrcnt)[2] = atp_readb_io(dev, c, 0x12);
+ ((unsigned char *) &adrcnt)[1] = atp_readb_io(dev, c, 0x13);
+ ((unsigned char *) &adrcnt)[0] = atp_readb_io(dev, c, 0x14);
k = dev->id[c][target_id].last_len;
k -= adrcnt;
dev->id[c][target_id].tran_len = k;
dev->id[c][target_id].last_len = adrcnt;
- tmport += 0x04;
- outb(0x08, tmport);
+ atp_writeb_io(dev, c, 0x18, 0x08);
dev->in_int[c] = 0;
- goto handled;
+ return IRQ_HANDLED;
} else {
#ifdef ED_DBGP
printk("cmdp != 0x41\n");
#endif
- outb(0x46, tmport);
+ atp_writeb_io(dev, c, 0x10, 0x46);
dev->id[c][target_id].dirct = 0x00;
- tmport += 0x02;
- outb(0x00, tmport++);
- outb(0x00, tmport++);
- outb(0x00, tmport++);
- tmport += 0x03;
- outb(0x08, tmport);
+ atp_writeb_io(dev, c, 0x12, 0x00);
+ atp_writeb_io(dev, c, 0x13, 0x00);
+ atp_writeb_io(dev, c, 0x14, 0x00);
+ atp_writeb_io(dev, c, 0x18, 0x08);
dev->in_int[c] = 0;
- goto handled;
+ return IRQ_HANDLED;
}
}
if (dev->last_cmd[c] != 0xff) {
dev->last_cmd[c] |= 0x40;
}
- if (dev->dev_id == ATP885_DEVID) {
- j = inb(dev->baseport + 0x29) & 0xfe;
- outb(j, dev->baseport + 0x29);
- tmport = workport + 0x16;
- } else {
- tmport = workport + 0x10;
- outb(0x45, tmport);
- tmport += 0x06;
- }
-
- target_id = inb(tmport);
+ if (is885(dev)) {
+ j = atp_readb_base(dev, 0x29) & 0xfe;
+ atp_writeb_base(dev, 0x29, j);
+ } else
+ atp_writeb_io(dev, c, 0x10, 0x45);
+
+ target_id = atp_readb_io(dev, c, 0x16);
/*
* Remap wide identifiers
*/
@@ -292,10 +327,8 @@ stop_dma:
} else {
target_id &= 0x07;
}
- if (dev->dev_id == ATP885_DEVID) {
- tmport = workport + 0x10;
- outb(0x45, tmport);
- }
+ if (is885(dev))
+ atp_writeb_io(dev, c, 0x10, 0x45);
workreq = dev->id[c][target_id].curr_req;
#ifdef ED_DBGP
scmd_printk(KERN_DEBUG, workreq, "CDB");
@@ -304,18 +337,16 @@ stop_dma:
printk("\n");
#endif
- tmport = workport + 0x0f;
- outb(lun, tmport);
- tmport += 0x02;
- outb(dev->id[c][target_id].devsp, tmport++);
+ atp_writeb_io(dev, c, 0x0f, lun);
+ atp_writeb_io(dev, c, 0x11, dev->id[c][target_id].devsp);
adrcnt = dev->id[c][target_id].tran_len;
k = dev->id[c][target_id].last_len;
- outb(((unsigned char *) &k)[2], tmport++);
- outb(((unsigned char *) &k)[1], tmport++);
- outb(((unsigned char *) &k)[0], tmport++);
+ atp_writeb_io(dev, c, 0x12, ((unsigned char *) &k)[2]);
+ atp_writeb_io(dev, c, 0x13, ((unsigned char *) &k)[1]);
+ atp_writeb_io(dev, c, 0x14, ((unsigned char *) &k)[0]);
#ifdef ED_DBGP
- printk("k %x, k[0] 0x%x k[1] 0x%x k[2] 0x%x\n", k, inb(tmport-1), inb(tmport-2), inb(tmport-3));
+ printk("k %x, k[0] 0x%x k[1] 0x%x k[2] 0x%x\n", k, atp_readb_io(dev, c, 0x14), atp_readb_io(dev, c, 0x13), atp_readb_io(dev, c, 0x12));
#endif
/* Remap wide */
j = target_id;
@@ -324,35 +355,28 @@ stop_dma:
}
/* Add direction */
j |= dev->id[c][target_id].dirct;
- outb(j, tmport++);
- outb(0x80,tmport);
+ atp_writeb_io(dev, c, 0x15, j);
+ atp_writeb_io(dev, c, 0x16, 0x80);
/* enable 32 bit fifo transfer */
- if (dev->dev_id == ATP885_DEVID) {
- tmpcip = dev->pciport[c] + 1;
- i=inb(tmpcip) & 0xf3;
+ if (is885(dev)) {
+ i = atp_readb_pci(dev, c, 1) & 0xf3;
//j=workreq->cmnd[0];
if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {
i |= 0x0c;
}
- outb(i,tmpcip);
- } else if ((dev->dev_id == ATP880_DEVID1) ||
- (dev->dev_id == ATP880_DEVID2) ) {
- tmport = workport - 0x05;
- if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {
- outb((unsigned char) ((inb(tmport) & 0x3f) | 0xc0), tmport);
- } else {
- outb((unsigned char) (inb(tmport) & 0x3f), tmport);
- }
+ atp_writeb_pci(dev, c, 1, i);
+ } else if (is880(dev)) {
+ if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a))
+ atp_writeb_base(dev, 0x3b, (atp_readb_base(dev, 0x3b) & 0x3f) | 0xc0);
+ else
+ atp_writeb_base(dev, 0x3b, atp_readb_base(dev, 0x3b) & 0x3f);
} else {
- tmport = workport + 0x3a;
- if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {
- outb((unsigned char) ((inb(tmport) & 0xf3) | 0x08), tmport);
- } else {
- outb((unsigned char) (inb(tmport) & 0xf3), tmport);
- }
+ if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a))
+ atp_writeb_base(dev, 0x3a, (atp_readb_base(dev, 0x3a) & 0xf3) | 0x08);
+ else
+ atp_writeb_base(dev, 0x3a, atp_readb_base(dev, 0x3a) & 0xf3);
}
- tmport = workport + 0x1b;
j = 0;
id = 1;
id = id << target_id;
@@ -362,18 +386,16 @@ stop_dma:
if ((id & dev->wide_id[c]) != 0) {
j |= 0x01;
}
- outb(j, tmport);
- while ((inb(tmport) & 0x01) != j) {
- outb(j,tmport);
- }
+ atp_writeb_io(dev, c, 0x1b, j);
+ while ((atp_readb_io(dev, c, 0x1b) & 0x01) != j)
+ atp_writeb_io(dev, c, 0x1b, j);
if (dev->id[c][target_id].last_len == 0) {
- tmport = workport + 0x18;
- outb(0x08, tmport);
+ atp_writeb_io(dev, c, 0x18, 0x08);
dev->in_int[c] = 0;
#ifdef ED_DBGP
printk("dev->id[c][target_id].last_len = 0\n");
#endif
- goto handled;
+ return IRQ_HANDLED;
}
#ifdef ED_DBGP
printk("target_id = %d adrcnt = %d\n",target_id,adrcnt);
@@ -401,39 +423,33 @@ stop_dma:
}
}
}
- tmpcip = dev->pciport[c] + 0x04;
- outl(dev->id[c][target_id].prdaddr, tmpcip);
+ atp_writel_pci(dev, c, 0x04, dev->id[c][target_id].prdaddr);
#ifdef ED_DBGP
printk("dev->id[%d][%d].prdaddr 0x%8x\n", c, target_id, dev->id[c][target_id].prdaddr);
#endif
- if (dev->dev_id == ATP885_DEVID) {
- tmpcip -= 0x04;
- } else {
- tmpcip -= 0x02;
- outb(0x06, tmpcip);
- outb(0x00, tmpcip);
- tmpcip -= 0x02;
+ if (!is885(dev)) {
+ atp_writeb_pci(dev, c, 2, 0x06);
+ atp_writeb_pci(dev, c, 2, 0x00);
}
- tmport = workport + 0x18;
/*
* Check transfer direction
*/
if (dev->id[c][target_id].dirct != 0) {
- outb(0x08, tmport);
- outb(0x01, tmpcip);
+ atp_writeb_io(dev, c, 0x18, 0x08);
+ atp_writeb_pci(dev, c, 0, 0x01);
dev->in_int[c] = 0;
#ifdef ED_DBGP
printk("status 0x80 return dirct != 0\n");
#endif
- goto handled;
+ return IRQ_HANDLED;
}
- outb(0x08, tmport);
- outb(0x09, tmpcip);
+ atp_writeb_io(dev, c, 0x18, 0x08);
+ atp_writeb_pci(dev, c, 0, 0x09);
dev->in_int[c] = 0;
#ifdef ED_DBGP
printk("status 0x80 return dirct = 0\n");
#endif
- goto handled;
+ return IRQ_HANDLED;
}
/*
@@ -442,31 +458,22 @@ stop_dma:
workreq = dev->id[c][target_id].curr_req;
- if (i == 0x42) {
- if ((dev->last_cmd[c] & 0xf0) != 0x40)
- {
- dev->last_cmd[c] = 0xff;
- }
- errstus = 0x02;
- workreq->result = errstus;
- goto go_42;
- }
- if (i == 0x16) {
+ if (i == 0x42 || i == 0x16) {
if ((dev->last_cmd[c] & 0xf0) != 0x40) {
dev->last_cmd[c] = 0xff;
}
- errstus = 0;
- tmport -= 0x08;
- errstus = inb(tmport);
- if (((dev->r1f[c][target_id] & 0x10) != 0)&&(dev->dev_id==ATP885_DEVID)) {
- printk(KERN_WARNING "AEC67162 CRC ERROR !\n");
- errstus = 0x02;
- }
- workreq->result = errstus;
-go_42:
- if (dev->dev_id == ATP885_DEVID) {
- j = inb(dev->baseport + 0x29) | 0x01;
- outb(j, dev->baseport + 0x29);
+ if (i == 0x16) {
+ workreq->result = atp_readb_io(dev, c, 0x0f);
+ if (((dev->r1f[c][target_id] & 0x10) != 0) && is885(dev)) {
+ printk(KERN_WARNING "AEC67162 CRC ERROR !\n");
+ workreq->result = 0x02;
+ }
+ } else
+ workreq->result = 0x02;
+
+ if (is885(dev)) {
+ j = atp_readb_base(dev, 0x29) | 0x01;
+ atp_writeb_base(dev, 0x29, j);
}
/*
* Complete the command
@@ -488,11 +495,9 @@ go_42:
* Take it back wide
*/
if (dev->wide_id[c] != 0) {
- tmport = workport + 0x1b;
- outb(0x01, tmport);
- while ((inb(tmport) & 0x01) != 0x01) {
- outb(0x01, tmport);
- }
+ atp_writeb_io(dev, c, 0x1b, 0x01);
+ while ((atp_readb_io(dev, c, 0x1b) & 0x01) != 0x01)
+ atp_writeb_io(dev, c, 0x1b, 0x01);
}
/*
* If there is stuff to send and nothing going then send it
@@ -507,7 +512,7 @@ go_42:
}
spin_unlock_irqrestore(dev->host->host_lock, flags);
dev->in_int[c] = 0;
- goto handled;
+ return IRQ_HANDLED;
}
if ((dev->last_cmd[c] & 0xf0) != 0x40) {
dev->last_cmd[c] = 0xff;
@@ -517,84 +522,54 @@ go_42:
}
i &= 0x0f;
if (i == 0x09) {
- tmpcip += 4;
- outl(dev->id[c][target_id].prdaddr, tmpcip);
- tmpcip = tmpcip - 2;
- outb(0x06, tmpcip);
- outb(0x00, tmpcip);
- tmpcip = tmpcip - 2;
- tmport = workport + 0x10;
- outb(0x41, tmport);
- if (dev->dev_id == ATP885_DEVID) {
- tmport += 2;
+ atp_writel_pci(dev, c, 4, dev->id[c][target_id].prdaddr);
+ atp_writeb_pci(dev, c, 2, 0x06);
+ atp_writeb_pci(dev, c, 2, 0x00);
+ atp_writeb_io(dev, c, 0x10, 0x41);
+ if (is885(dev)) {
k = dev->id[c][target_id].last_len;
- outb((unsigned char) (((unsigned char *) (&k))[2]), tmport++);
- outb((unsigned char) (((unsigned char *) (&k))[1]), tmport++);
- outb((unsigned char) (((unsigned char *) (&k))[0]), tmport);
+ atp_writeb_io(dev, c, 0x12, ((unsigned char *) (&k))[2]);
+ atp_writeb_io(dev, c, 0x13, ((unsigned char *) (&k))[1]);
+ atp_writeb_io(dev, c, 0x14, ((unsigned char *) (&k))[0]);
dev->id[c][target_id].dirct = 0x00;
- tmport += 0x04;
} else {
dev->id[c][target_id].dirct = 0x00;
- tmport += 0x08;
}
- outb(0x08, tmport);
- outb(0x09, tmpcip);
+ atp_writeb_io(dev, c, 0x18, 0x08);
+ atp_writeb_pci(dev, c, 0, 0x09);
dev->in_int[c] = 0;
- goto handled;
+ return IRQ_HANDLED;
}
if (i == 0x08) {
- tmpcip += 4;
- outl(dev->id[c][target_id].prdaddr, tmpcip);
- tmpcip = tmpcip - 2;
- outb(0x06, tmpcip);
- outb(0x00, tmpcip);
- tmpcip = tmpcip - 2;
- tmport = workport + 0x10;
- outb(0x41, tmport);
- if (dev->dev_id == ATP885_DEVID) {
- tmport += 2;
+ atp_writel_pci(dev, c, 4, dev->id[c][target_id].prdaddr);
+ atp_writeb_pci(dev, c, 2, 0x06);
+ atp_writeb_pci(dev, c, 2, 0x00);
+ atp_writeb_io(dev, c, 0x10, 0x41);
+ if (is885(dev)) {
k = dev->id[c][target_id].last_len;
- outb((unsigned char) (((unsigned char *) (&k))[2]), tmport++);
- outb((unsigned char) (((unsigned char *) (&k))[1]), tmport++);
- outb((unsigned char) (((unsigned char *) (&k))[0]), tmport++);
- } else {
- tmport += 5;
+ atp_writeb_io(dev, c, 0x12, ((unsigned char *) (&k))[2]);
+ atp_writeb_io(dev, c, 0x13, ((unsigned char *) (&k))[1]);
+ atp_writeb_io(dev, c, 0x14, ((unsigned char *) (&k))[0]);
}
- outb((unsigned char) (inb(tmport) | 0x20), tmport);
+ atp_writeb_io(dev, c, 0x15, atp_readb_io(dev, c, 0x15) | 0x20);
dev->id[c][target_id].dirct = 0x20;
- tmport += 0x03;
- outb(0x08, tmport);
- outb(0x01, tmpcip);
+ atp_writeb_io(dev, c, 0x18, 0x08);
+ atp_writeb_pci(dev, c, 0, 0x01);
dev->in_int[c] = 0;
- goto handled;
- }
- tmport -= 0x07;
- if (i == 0x0a) {
- outb(0x30, tmport);
- } else {
- outb(0x46, tmport);
+ return IRQ_HANDLED;
}
+ if (i == 0x0a)
+ atp_writeb_io(dev, c, 0x10, 0x30);
+ else
+ atp_writeb_io(dev, c, 0x10, 0x46);
dev->id[c][target_id].dirct = 0x00;
- tmport += 0x02;
- outb(0x00, tmport++);
- outb(0x00, tmport++);
- outb(0x00, tmport++);
- tmport += 0x03;
- outb(0x08, tmport);
- dev->in_int[c] = 0;
- goto handled;
- } else {
-// tmport = workport + 0x17;
-// inb(tmport);
-// dev->working[c] = 0;
- dev->in_int[c] = 0;
- goto handled;
+ atp_writeb_io(dev, c, 0x12, 0x00);
+ atp_writeb_io(dev, c, 0x13, 0x00);
+ atp_writeb_io(dev, c, 0x14, 0x00);
+ atp_writeb_io(dev, c, 0x18, 0x08);
}
-
-handled:
-#ifdef ED_DBGP
- printk("atp870u_intr_handle exit\n");
-#endif
+ dev->in_int[c] = 0;
+
return IRQ_HANDLED;
}
/**
@@ -608,7 +583,7 @@ static int atp870u_queuecommand_lck(struct scsi_cmnd *req_p,
void (*done) (struct scsi_cmnd *))
{
unsigned char c;
- unsigned int tmport,m;
+ unsigned int m;
struct atp_unit *dev;
struct Scsi_Host *host;
@@ -677,11 +652,10 @@ static int atp870u_queuecommand_lck(struct scsi_cmnd *req_p,
return 0;
}
dev->quereq[c][dev->quend[c]] = req_p;
- tmport = dev->ioport[c] + 0x1c;
#ifdef ED_DBGP
- printk("dev->ioport[c] = %x inb(tmport) = %x dev->in_int[%d] = %d dev->in_snd[%d] = %d\n",dev->ioport[c],inb(tmport),c,dev->in_int[c],c,dev->in_snd[c]);
+ printk("dev->ioport[c] = %x atp_readb_io(dev, c, 0x1c) = %x dev->in_int[%d] = %d dev->in_snd[%d] = %d\n",dev->ioport[c],atp_readb_io(dev, c, 0x1c),c,dev->in_int[c],c,dev->in_snd[c]);
#endif
- if ((inb(tmport) == 0) && (dev->in_int[c] == 0) && (dev->in_snd[c] == 0)) {
+ if ((atp_readb_io(dev, c, 0x1c) == 0) && (dev->in_int[c] == 0) && (dev->in_snd[c] == 0)) {
#ifdef ED_DBGP
printk("Call sent_s870(atp870u_queuecommand)\n");
#endif
@@ -706,14 +680,12 @@ static DEF_SCSI_QCMD(atp870u_queuecommand)
*/
static void send_s870(struct atp_unit *dev,unsigned char c)
{
- unsigned int tmport;
- struct scsi_cmnd *workreq;
+ struct scsi_cmnd *workreq = NULL;
unsigned int i;//,k;
unsigned char j, target_id;
unsigned char *prd;
- unsigned short int tmpcip, w;
+ unsigned short int w;
unsigned long l, bttl = 0;
- unsigned int workport;
unsigned long sg_count;
if (dev->in_snd[c] != 0) {
@@ -729,53 +701,42 @@ static void send_s870(struct atp_unit *dev,unsigned char c)
if ((dev->last_cmd[c] != 0xff) && ((dev->last_cmd[c] & 0x40) != 0)) {
dev->last_cmd[c] &= 0x0f;
workreq = dev->id[c][dev->last_cmd[c]].curr_req;
- if (workreq != NULL) { /* check NULL pointer */
- goto cmd_subp;
- }
- dev->last_cmd[c] = 0xff;
- if (dev->quhd[c] == dev->quend[c]) {
- dev->in_snd[c] = 0;
- return ;
+ if (!workreq) {
+ dev->last_cmd[c] = 0xff;
+ if (dev->quhd[c] == dev->quend[c]) {
+ dev->in_snd[c] = 0;
+ return;
+ }
}
}
- if ((dev->last_cmd[c] != 0xff) && (dev->working[c] != 0)) {
- dev->in_snd[c] = 0;
- return ;
- }
- dev->working[c]++;
- j = dev->quhd[c];
- dev->quhd[c]++;
- if (dev->quhd[c] >= qcnt) {
- dev->quhd[c] = 0;
- }
- workreq = dev->quereq[c][dev->quhd[c]];
- if (dev->id[c][scmd_id(workreq)].curr_req == NULL) {
+ if (!workreq) {
+ if ((dev->last_cmd[c] != 0xff) && (dev->working[c] != 0)) {
+ dev->in_snd[c] = 0;
+ return;
+ }
+ dev->working[c]++;
+ j = dev->quhd[c];
+ dev->quhd[c]++;
+ if (dev->quhd[c] >= qcnt)
+ dev->quhd[c] = 0;
+ workreq = dev->quereq[c][dev->quhd[c]];
+ if (dev->id[c][scmd_id(workreq)].curr_req != NULL) {
+ dev->quhd[c] = j;
+ dev->working[c]--;
+ dev->in_snd[c] = 0;
+ return;
+ }
dev->id[c][scmd_id(workreq)].curr_req = workreq;
dev->last_cmd[c] = scmd_id(workreq);
- goto cmd_subp;
- }
- dev->quhd[c] = j;
- dev->working[c]--;
- dev->in_snd[c] = 0;
- return;
-cmd_subp:
- workport = dev->ioport[c];
- tmport = workport + 0x1f;
- if ((inb(tmport) & 0xb0) != 0) {
- goto abortsnd;
- }
- tmport = workport + 0x1c;
- if (inb(tmport) == 0) {
- goto oktosend;
}
-abortsnd:
+ if ((atp_readb_io(dev, c, 0x1f) & 0xb0) != 0 || atp_readb_io(dev, c, 0x1c) != 0) {
#ifdef ED_DBGP
- printk("Abort to Send\n");
+ printk("Abort to Send\n");
#endif
- dev->last_cmd[c] |= 0x40;
- dev->in_snd[c] = 0;
- return;
-oktosend:
+ dev->last_cmd[c] |= 0x40;
+ dev->in_snd[c] = 0;
+ return;
+ }
#ifdef ED_DBGP
printk("OK to Send\n");
scmd_printk(KERN_DEBUG, workreq, "CDB");
@@ -786,9 +747,9 @@ oktosend:
#endif
l = scsi_bufflen(workreq);
- if (dev->dev_id == ATP885_DEVID) {
- j = inb(dev->baseport + 0x29) & 0xfe;
- outb(j, dev->baseport + 0x29);
+ if (is885(dev)) {
+ j = atp_readb_base(dev, 0x29) & 0xfe;
+ atp_writeb_base(dev, 0x29, j);
dev->r1f[c][scmd_id(workreq)] = 0;
}
@@ -800,7 +761,6 @@ oktosend:
l = 0;
}
- tmport = workport + 0x1b;
j = 0;
target_id = scmd_id(workreq);
@@ -812,9 +772,9 @@ oktosend:
if ((w & dev->wide_id[c]) != 0) {
j |= 0x01;
}
- outb(j, tmport);
- while ((inb(tmport) & 0x01) != j) {
- outb(j,tmport);
+ atp_writeb_io(dev, c, 0x1b, j);
+ while ((atp_readb_io(dev, c, 0x1b) & 0x01) != j) {
+ atp_writeb_pci(dev, c, 0x1b, j);
#ifdef ED_DBGP
printk("send_s870 while loop 1\n");
#endif
@@ -823,24 +783,19 @@ oktosend:
* Write the command
*/
- tmport = workport;
- outb(workreq->cmd_len, tmport++);
- outb(0x2c, tmport++);
- if (dev->dev_id == ATP885_DEVID) {
- outb(0x7f, tmport++);
- } else {
- outb(0xcf, tmport++);
- }
- for (i = 0; i < workreq->cmd_len; i++) {
- outb(workreq->cmnd[i], tmport++);
- }
- tmport = workport + 0x0f;
- outb(workreq->device->lun, tmport);
- tmport += 0x02;
+ atp_writeb_io(dev, c, 0x00, workreq->cmd_len);
+ atp_writeb_io(dev, c, 0x01, 0x2c);
+ if (is885(dev))
+ atp_writeb_io(dev, c, 0x02, 0x7f);
+ else
+ atp_writeb_io(dev, c, 0x02, 0xcf);
+ for (i = 0; i < workreq->cmd_len; i++)
+ atp_writeb_io(dev, c, 0x03 + i, workreq->cmnd[i]);
+ atp_writeb_io(dev, c, 0x0f, workreq->device->lun);
/*
* Write the target
*/
- outb(dev->id[c][target_id].devsp, tmport++);
+ atp_writeb_io(dev, c, 0x11, dev->id[c][target_id].devsp);
#ifdef ED_DBGP
printk("dev->id[%d][%d].devsp = %2x\n",c,target_id,dev->id[c][target_id].devsp);
#endif
@@ -849,9 +804,9 @@ oktosend:
/*
* Write transfer size
*/
- outb((unsigned char) (((unsigned char *) (&l))[2]), tmport++);
- outb((unsigned char) (((unsigned char *) (&l))[1]), tmport++);
- outb((unsigned char) (((unsigned char *) (&l))[0]), tmport++);
+ atp_writeb_io(dev, c, 0x12, ((unsigned char *) (&l))[2]);
+ atp_writeb_io(dev, c, 0x13, ((unsigned char *) (&l))[1]);
+ atp_writeb_io(dev, c, 0x14, ((unsigned char *) (&l))[0]);
j = target_id;
dev->id[c][j].last_len = l;
dev->id[c][j].tran_len = 0;
@@ -867,29 +822,24 @@ oktosend:
/*
* Check transfer direction
*/
- if (workreq->sc_data_direction == DMA_TO_DEVICE) {
- outb((unsigned char) (j | 0x20), tmport++);
- } else {
- outb(j, tmport++);
- }
- outb((unsigned char) (inb(tmport) | 0x80), tmport);
- outb(0x80, tmport);
- tmport = workport + 0x1c;
+ if (workreq->sc_data_direction == DMA_TO_DEVICE)
+ atp_writeb_io(dev, c, 0x15, j | 0x20);
+ else
+ atp_writeb_io(dev, c, 0x15, j);
+ atp_writeb_io(dev, c, 0x16, atp_readb_io(dev, c, 0x16) | 0x80);
+ atp_writeb_io(dev, c, 0x16, 0x80);
dev->id[c][target_id].dirct = 0;
if (l == 0) {
- if (inb(tmport) == 0) {
- tmport = workport + 0x18;
+ if (atp_readb_io(dev, c, 0x1c) == 0) {
#ifdef ED_DBGP
printk("change SCSI_CMD_REG 0x08\n");
#endif
- outb(0x08, tmport);
- } else {
+ atp_writeb_io(dev, c, 0x18, 0x08);
+ } else
dev->last_cmd[c] |= 0x40;
- }
dev->in_snd[c] = 0;
return;
}
- tmpcip = dev->pciport[c];
prd = dev->id[c][target_id].prd_table;
dev->id[c][target_id].prd_pos = prd;
@@ -926,50 +876,37 @@ oktosend:
printk("2. bttl %x, l %x\n",bttl, l);
#endif
}
- tmpcip += 4;
#ifdef ED_DBGP
- printk("send_s870: prdaddr_2 0x%8x tmpcip %x target_id %d\n", dev->id[c][target_id].prdaddr,tmpcip,target_id);
+ printk("send_s870: prdaddr_2 0x%8x target_id %d\n", dev->id[c][target_id].prdaddr,target_id);
#endif
dev->id[c][target_id].prdaddr = dev->id[c][target_id].prd_bus;
- outl(dev->id[c][target_id].prdaddr, tmpcip);
- tmpcip = tmpcip - 2;
- outb(0x06, tmpcip);
- outb(0x00, tmpcip);
- if (dev->dev_id == ATP885_DEVID) {
- tmpcip--;
- j=inb(tmpcip) & 0xf3;
+ atp_writel_pci(dev, c, 4, dev->id[c][target_id].prdaddr);
+ atp_writeb_pci(dev, c, 2, 0x06);
+ atp_writeb_pci(dev, c, 2, 0x00);
+ if (is885(dev)) {
+ j = atp_readb_pci(dev, c, 1) & 0xf3;
if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) ||
(workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {
j |= 0x0c;
}
- outb(j,tmpcip);
- tmpcip--;
- } else if ((dev->dev_id == ATP880_DEVID1) ||
- (dev->dev_id == ATP880_DEVID2)) {
- tmpcip =tmpcip -2;
- tmport = workport - 0x05;
- if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {
- outb((unsigned char) ((inb(tmport) & 0x3f) | 0xc0), tmport);
- } else {
- outb((unsigned char) (inb(tmport) & 0x3f), tmport);
- }
+ atp_writeb_pci(dev, c, 1, j);
+ } else if (is880(dev)) {
+ if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a))
+ atp_writeb_base(dev, 0x3b, (atp_readb_base(dev, 0x3b) & 0x3f) | 0xc0);
+ else
+ atp_writeb_base(dev, 0x3b, atp_readb_base(dev, 0x3b) & 0x3f);
} else {
- tmpcip =tmpcip -2;
- tmport = workport + 0x3a;
- if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {
- outb((inb(tmport) & 0xf3) | 0x08, tmport);
- } else {
- outb(inb(tmport) & 0xf3, tmport);
- }
+ if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a))
+ atp_writeb_base(dev, 0x3a, (atp_readb_base(dev, 0x3a) & 0xf3) | 0x08);
+ else
+ atp_writeb_base(dev, 0x3a, atp_readb_base(dev, 0x3a) & 0xf3);
}
- tmport = workport + 0x1c;
if(workreq->sc_data_direction == DMA_TO_DEVICE) {
dev->id[c][target_id].dirct = 0x20;
- if (inb(tmport) == 0) {
- tmport = workport + 0x18;
- outb(0x08, tmport);
- outb(0x01, tmpcip);
+ if (atp_readb_io(dev, c, 0x1c) == 0) {
+ atp_writeb_io(dev, c, 0x18, 0x08);
+ atp_writeb_pci(dev, c, 0, 0x01);
#ifdef ED_DBGP
printk( "start DMA(to target)\n");
#endif
@@ -979,10 +916,9 @@ oktosend:
dev->in_snd[c] = 0;
return;
}
- if (inb(tmport) == 0) {
- tmport = workport + 0x18;
- outb(0x08, tmport);
- outb(0x09, tmpcip);
+ if (atp_readb_io(dev, c, 0x1c) == 0) {
+ atp_writeb_io(dev, c, 0x18, 0x08);
+ atp_writeb_pci(dev, c, 0, 0x09);
#ifdef ED_DBGP
printk( "start DMA(to host)\n");
#endif
@@ -996,49 +932,40 @@ oktosend:
static unsigned char fun_scam(struct atp_unit *dev, unsigned short int *val)
{
- unsigned int tmport;
unsigned short int i, k;
unsigned char j;
- tmport = dev->ioport[0] + 0x1c;
- outw(*val, tmport);
-FUN_D7:
+ atp_writew_io(dev, 0, 0x1c, *val);
for (i = 0; i < 10; i++) { /* stable >= bus settle delay(400 ns) */
- k = inw(tmport);
+ k = atp_readw_io(dev, 0, 0x1c);
j = (unsigned char) (k >> 8);
- if ((k & 0x8000) != 0) { /* DB7 all release? */
- goto FUN_D7;
- }
+ if ((k & 0x8000) != 0) /* DB7 all release? */
+ i = 0;
}
*val |= 0x4000; /* assert DB6 */
- outw(*val, tmport);
+ atp_writew_io(dev, 0, 0x1c, *val);
*val &= 0xdfff; /* assert DB5 */
- outw(*val, tmport);
-FUN_D5:
+ atp_writew_io(dev, 0, 0x1c, *val);
for (i = 0; i < 10; i++) { /* stable >= bus settle delay(400 ns) */
- if ((inw(tmport) & 0x2000) != 0) { /* DB5 all release? */
- goto FUN_D5;
- }
+ if ((atp_readw_io(dev, 0, 0x1c) & 0x2000) != 0) /* DB5 all release? */
+ i = 0;
}
*val |= 0x8000; /* no DB4-0, assert DB7 */
*val &= 0xe0ff;
- outw(*val, tmport);
+ atp_writew_io(dev, 0, 0x1c, *val);
*val &= 0xbfff; /* release DB6 */
- outw(*val, tmport);
-FUN_D6:
+ atp_writew_io(dev, 0, 0x1c, *val);
for (i = 0; i < 10; i++) { /* stable >= bus settle delay(400 ns) */
- if ((inw(tmport) & 0x4000) != 0) { /* DB6 all release? */
- goto FUN_D6;
- }
+ if ((atp_readw_io(dev, 0, 0x1c) & 0x4000) != 0) /* DB6 all release? */
+ i = 0;
}
return j;
}
-static void tscam(struct Scsi_Host *host)
+static void tscam(struct Scsi_Host *host, bool wide_chip, u8 scam_on)
{
- unsigned int tmport;
unsigned char i, j, k;
unsigned long n;
unsigned short int m, assignid_map, val;
@@ -1055,31 +982,28 @@ static void tscam(struct Scsi_Host *host)
}
*/
- tmport = dev->ioport[0] + 1;
- outb(0x08, tmport++);
- outb(0x7f, tmport);
- tmport = dev->ioport[0] + 0x11;
- outb(0x20, tmport);
+ atp_writeb_io(dev, 0, 1, 0x08);
+ atp_writeb_io(dev, 0, 2, 0x7f);
+ atp_writeb_io(dev, 0, 0x11, 0x20);
- if ((dev->scam_on & 0x40) == 0) {
+ if ((scam_on & 0x40) == 0) {
return;
}
m = 1;
m <<= dev->host_id[0];
j = 16;
- if (dev->chip_ver < 4) {
+ if (!wide_chip) {
m |= 0xff00;
j = 8;
}
assignid_map = m;
- tmport = dev->ioport[0] + 0x02;
- outb(0x02, tmport++); /* 2*2=4ms,3EH 2/32*3E=3.9ms */
- outb(0, tmport++);
- outb(0, tmport++);
- outb(0, tmport++);
- outb(0, tmport++);
- outb(0, tmport++);
- outb(0, tmport++);
+ atp_writeb_io(dev, 0, 0x02, 0x02); /* 2*2=4ms,3EH 2/32*3E=3.9ms */
+ atp_writeb_io(dev, 0, 0x03, 0);
+ atp_writeb_io(dev, 0, 0x04, 0);
+ atp_writeb_io(dev, 0, 0x05, 0);
+ atp_writeb_io(dev, 0, 0x06, 0);
+ atp_writeb_io(dev, 0, 0x07, 0);
+ atp_writeb_io(dev, 0, 0x08, 0);
for (i = 0; i < j; i++) {
m = 1;
@@ -1087,92 +1011,73 @@ static void tscam(struct Scsi_Host *host)
if ((m & assignid_map) != 0) {
continue;
}
- tmport = dev->ioport[0] + 0x0f;
- outb(0, tmport++);
- tmport += 0x02;
- outb(0, tmport++);
- outb(0, tmport++);
- outb(0, tmport++);
+ atp_writeb_io(dev, 0, 0x0f, 0);
+ atp_writeb_io(dev, 0, 0x12, 0);
+ atp_writeb_io(dev, 0, 0x13, 0);
+ atp_writeb_io(dev, 0, 0x14, 0);
if (i > 7) {
k = (i & 0x07) | 0x40;
} else {
k = i;
}
- outb(k, tmport++);
- tmport = dev->ioport[0] + 0x1b;
- if (dev->chip_ver == 4) {
- outb(0x01, tmport);
- } else {
- outb(0x00, tmport);
- }
-wait_rdyok:
- tmport = dev->ioport[0] + 0x18;
- outb(0x09, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
- tmport -= 0x08;
- k = inb(tmport);
- if (k != 0x16) {
- if ((k == 0x85) || (k == 0x42)) {
- continue;
- }
- tmport = dev->ioport[0] + 0x10;
- outb(0x41, tmport);
- goto wait_rdyok;
- }
+ atp_writeb_io(dev, 0, 0x15, k);
+ if (wide_chip)
+ atp_writeb_io(dev, 0, 0x1b, 0x01);
+ else
+ atp_writeb_io(dev, 0, 0x1b, 0x00);
+ do {
+ atp_writeb_io(dev, 0, 0x18, 0x09);
+
+ while ((atp_readb_io(dev, 0, 0x1f) & 0x80) == 0x00)
+ cpu_relax();
+ k = atp_readb_io(dev, 0, 0x17);
+ if ((k == 0x85) || (k == 0x42))
+ break;
+ if (k != 0x16)
+ atp_writeb_io(dev, 0, 0x10, 0x41);
+ } while (k != 0x16);
+ if ((k == 0x85) || (k == 0x42))
+ continue;
assignid_map |= m;
}
- tmport = dev->ioport[0] + 0x02;
- outb(0x7f, tmport);
- tmport = dev->ioport[0] + 0x1b;
- outb(0x02, tmport);
+ atp_writeb_io(dev, 0, 0x02, 0x7f);
+ atp_writeb_io(dev, 0, 0x1b, 0x02);
- outb(0, 0x80);
+ udelay(2);
val = 0x0080; /* bsy */
- tmport = dev->ioport[0] + 0x1c;
- outw(val, tmport);
+ atp_writew_io(dev, 0, 0x1c, val);
val |= 0x0040; /* sel */
- outw(val, tmport);
+ atp_writew_io(dev, 0, 0x1c, val);
val |= 0x0004; /* msg */
- outw(val, tmport);
- inb(0x80); /* 2 deskew delay(45ns*2=90ns) */
+ atp_writew_io(dev, 0, 0x1c, val);
+ udelay(2); /* 2 deskew delay(45ns*2=90ns) */
val &= 0x007f; /* no bsy */
- outw(val, tmport);
+ atp_writew_io(dev, 0, 0x1c, val);
mdelay(128);
val &= 0x00fb; /* after 1ms no msg */
- outw(val, tmport);
-wait_nomsg:
- if ((inb(tmport) & 0x04) != 0) {
- goto wait_nomsg;
- }
- outb(1, 0x80);
+ atp_writew_io(dev, 0, 0x1c, val);
+ while ((atp_readb_io(dev, 0, 0x1c) & 0x04) != 0)
+ ;
+ udelay(2);
udelay(100);
- for (n = 0; n < 0x30000; n++) {
- if ((inb(tmport) & 0x80) != 0) { /* bsy ? */
- goto wait_io;
- }
- }
- goto TCM_SYNC;
-wait_io:
- for (n = 0; n < 0x30000; n++) {
- if ((inb(tmport) & 0x81) == 0x0081) {
- goto wait_io1;
- }
- }
- goto TCM_SYNC;
-wait_io1:
- inb(0x80);
- val |= 0x8003; /* io,cd,db7 */
- outw(val, tmport);
- inb(0x80);
- val &= 0x00bf; /* no sel */
- outw(val, tmport);
- outb(2, 0x80);
-TCM_SYNC:
+ for (n = 0; n < 0x30000; n++)
+ if ((atp_readb_io(dev, 0, 0x1c) & 0x80) != 0) /* bsy ? */
+ break;
+ if (n < 0x30000)
+ for (n = 0; n < 0x30000; n++)
+ if ((atp_readb_io(dev, 0, 0x1c) & 0x81) == 0x0081) {
+ udelay(2);
+ val |= 0x8003; /* io,cd,db7 */
+ atp_writew_io(dev, 0, 0x1c, val);
+ udelay(2);
+ val &= 0x00bf; /* no sel */
+ atp_writew_io(dev, 0, 0x1c, val);
+ udelay(2);
+ break;
+ }
+ while (1) {
/*
* The funny division into multiple delays is to accomodate
* arches like ARM where udelay() multiplies its argument by
@@ -1183,55 +1088,48 @@ TCM_SYNC:
*/
mdelay(2);
udelay(48);
- if ((inb(tmport) & 0x80) == 0x00) { /* bsy ? */
- outw(0, tmport--);
- outb(0, tmport);
- tmport = dev->ioport[0] + 0x15;
- outb(0, tmport);
- tmport += 0x03;
- outb(0x09, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0)
+ if ((atp_readb_io(dev, 0, 0x1c) & 0x80) == 0x00) { /* bsy ? */
+ atp_writew_io(dev, 0, 0x1c, 0);
+ atp_writeb_io(dev, 0, 0x1b, 0);
+ atp_writeb_io(dev, 0, 0x15, 0);
+ atp_writeb_io(dev, 0, 0x18, 0x09);
+ while ((atp_readb_io(dev, 0, 0x1f) & 0x80) == 0)
cpu_relax();
- tmport -= 0x08;
- inb(tmport);
+ atp_readb_io(dev, 0, 0x17);
return;
}
val &= 0x00ff; /* synchronization */
val |= 0x3f00;
fun_scam(dev, &val);
- outb(3, 0x80);
+ udelay(2);
val &= 0x00ff; /* isolation */
val |= 0x2000;
fun_scam(dev, &val);
- outb(4, 0x80);
+ udelay(2);
i = 8;
j = 0;
-TCM_ID:
- if ((inw(tmport) & 0x2000) == 0) {
- goto TCM_ID;
- }
- outb(5, 0x80);
- val &= 0x00ff; /* get ID_STRING */
- val |= 0x2000;
- k = fun_scam(dev, &val);
- if ((k & 0x03) == 0) {
- goto TCM_5;
- }
- mbuf[j] <<= 0x01;
- mbuf[j] &= 0xfe;
- if ((k & 0x02) != 0) {
- mbuf[j] |= 0x01;
- }
- i--;
- if (i > 0) {
- goto TCM_ID;
+
+ while (1) {
+ if ((atp_readw_io(dev, 0, 0x1c) & 0x2000) == 0)
+ continue;
+ udelay(2);
+ val &= 0x00ff; /* get ID_STRING */
+ val |= 0x2000;
+ k = fun_scam(dev, &val);
+ if ((k & 0x03) == 0)
+ break;
+ mbuf[j] <<= 0x01;
+ mbuf[j] &= 0xfe;
+ if ((k & 0x02) != 0)
+ mbuf[j] |= 0x01;
+ i--;
+ if (i > 0)
+ continue;
+ j++;
+ i = 8;
}
- j++;
- i = 8;
- goto TCM_ID;
-TCM_5: /* isolation complete.. */
+ /* isolation complete.. */
/* mbuf[32]=0;
printk(" \n%x %x %x %s\n ",assignid_map,mbuf[0],mbuf[1],&mbuf[2]); */
i = 15;
@@ -1239,33 +1137,33 @@ TCM_5: /* isolation complete.. */
if ((j & 0x20) != 0) { /* bit5=1:ID up to 7 */
i = 7;
}
- if ((j & 0x06) == 0) { /* IDvalid? */
- goto G2Q5;
- }
- k = mbuf[1];
-small_id:
- m = 1;
- m <<= k;
- if ((m & assignid_map) == 0) {
- goto G2Q_QUIN;
- }
- if (k > 0) {
- k--;
- goto small_id;
- }
-G2Q5: /* srch from max acceptable ID# */
- k = i; /* max acceptable ID# */
-G2Q_LP:
- m = 1;
- m <<= k;
- if ((m & assignid_map) == 0) {
- goto G2Q_QUIN;
+ if ((j & 0x06) != 0) { /* IDvalid? */
+ k = mbuf[1];
+ while (1) {
+ m = 1;
+ m <<= k;
+ if ((m & assignid_map) == 0)
+ break;
+ if (k > 0)
+ k--;
+ else
+ break;
+ }
}
- if (k > 0) {
- k--;
- goto G2Q_LP;
+ if ((m & assignid_map) != 0) { /* srch from max acceptable ID# */
+ k = i; /* max acceptable ID# */
+ while (1) {
+ m = 1;
+ m <<= k;
+ if ((m & assignid_map) == 0)
+ break;
+ if (k > 0)
+ k--;
+ else
+ break;
+ }
}
-G2Q_QUIN: /* k=binID#, */
+ /* k=binID#, */
assignid_map |= m;
if (k < 8) {
quintet[0] = 0x38; /* 1st dft ID<8 */
@@ -1284,1227 +1182,6 @@ G2Q_QUIN: /* k=binID#, */
val |= m;
fun_scam(dev, &val);
- goto TCM_SYNC;
-
-}
-
-static void is870(struct atp_unit *dev, unsigned int wkport)
-{
- unsigned int tmport;
- unsigned char i, j, k, rmb, n;
- unsigned short int m;
- static unsigned char mbuf[512];
- static unsigned char satn[9] = { 0, 0, 0, 0, 0, 0, 0, 6, 6 };
- static unsigned char inqd[9] = { 0x12, 0, 0, 0, 0x24, 0, 0, 0x24, 6 };
- static unsigned char synn[6] = { 0x80, 1, 3, 1, 0x19, 0x0e };
- static unsigned char synu[6] = { 0x80, 1, 3, 1, 0x0c, 0x0e };
- static unsigned char synw[6] = { 0x80, 1, 3, 1, 0x0c, 0x07 };
- static unsigned char wide[6] = { 0x80, 1, 2, 3, 1, 0 };
-
- tmport = wkport + 0x3a;
- outb((unsigned char) (inb(tmport) | 0x10), tmport);
-
- for (i = 0; i < 16; i++) {
- if ((dev->chip_ver != 4) && (i > 7)) {
- break;
- }
- m = 1;
- m = m << i;
- if ((m & dev->active_id[0]) != 0) {
- continue;
- }
- if (i == dev->host_id[0]) {
- printk(KERN_INFO " ID: %2d Host Adapter\n", dev->host_id[0]);
- continue;
- }
- tmport = wkport + 0x1b;
- if (dev->chip_ver == 4) {
- outb(0x01, tmport);
- } else {
- outb(0x00, tmport);
- }
- tmport = wkport + 1;
- outb(0x08, tmport++);
- outb(0x7f, tmport++);
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[0][i].devsp, tmport++);
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
- j = i;
- if ((j & 0x08) != 0) {
- j = (j & 0x07) | 0x40;
- }
- outb(j, tmport);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
- continue;
-
- while (inb(tmport) != 0x8e)
- cpu_relax();
-
- dev->active_id[0] |= m;
-
- tmport = wkport + 0x10;
- outb(0x30, tmport);
- tmport = wkport + 0x04;
- outb(0x00, tmport);
-
-phase_cmd:
- tmport = wkport + 0x18;
- outb(0x08, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
- tmport -= 0x08;
- j = inb(tmport);
- if (j != 0x16) {
- tmport = wkport + 0x10;
- outb(0x41, tmport);
- goto phase_cmd;
- }
-sel_ok:
- tmport = wkport + 3;
- outb(inqd[0], tmport++);
- outb(inqd[1], tmport++);
- outb(inqd[2], tmport++);
- outb(inqd[3], tmport++);
- outb(inqd[4], tmport++);
- outb(inqd[5], tmport);
- tmport += 0x07;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[0][i].devsp, tmport++);
- outb(0, tmport++);
- outb(inqd[6], tmport++);
- outb(inqd[7], tmport++);
- tmport += 0x03;
- outb(inqd[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
- continue;
-
- while (inb(tmport) != 0x8e)
- cpu_relax();
-
- tmport = wkport + 0x1b;
- if (dev->chip_ver == 4)
- outb(0x00, tmport);
-
- tmport = wkport + 0x18;
- outb(0x08, tmport);
- tmport += 0x07;
- j = 0;
-rd_inq_data:
- k = inb(tmport);
- if ((k & 0x01) != 0) {
- tmport -= 0x06;
- mbuf[j++] = inb(tmport);
- tmport += 0x06;
- goto rd_inq_data;
- }
- if ((k & 0x80) == 0) {
- goto rd_inq_data;
- }
- tmport -= 0x08;
- j = inb(tmport);
- if (j == 0x16) {
- goto inq_ok;
- }
- tmport = wkport + 0x10;
- outb(0x46, tmport);
- tmport += 0x02;
- outb(0, tmport++);
- outb(0, tmport++);
- outb(0, tmport++);
- tmport += 0x03;
- outb(0x08, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- if (inb(tmport) != 0x16) {
- goto sel_ok;
- }
-inq_ok:
- mbuf[36] = 0;
- printk(KERN_INFO " ID: %2d %s\n", i, &mbuf[8]);
- dev->id[0][i].devtype = mbuf[0];
- rmb = mbuf[1];
- n = mbuf[7];
- if (dev->chip_ver != 4) {
- goto not_wide;
- }
- if ((mbuf[7] & 0x60) == 0) {
- goto not_wide;
- }
- if ((dev->global_map[0] & 0x20) == 0) {
- goto not_wide;
- }
- tmport = wkport + 0x1b;
- outb(0x01, tmport);
- tmport = wkport + 3;
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[0][i].devsp, tmport++);
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
- continue;
-
- while (inb(tmport) != 0x8e)
- cpu_relax();
-
-try_wide:
- j = 0;
- tmport = wkport + 0x14;
- outb(0x05, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- outb(wide[j++], tmport);
- tmport += 0x06;
- }
- }
- tmport -= 0x08;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto widep_in;
- }
- if (j == 0x0a) {
- goto widep_cmd;
- }
- if (j == 0x0e) {
- goto try_wide;
- }
- continue;
-widep_out:
- tmport = wkport + 0x18;
- outb(0x20, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- outb(0, tmport);
- tmport += 0x06;
- }
- }
- tmport -= 0x08;
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto widep_in;
- }
- if (j == 0x0a) {
- goto widep_cmd;
- }
- if (j == 0x0e) {
- goto widep_out;
- }
- continue;
-widep_in:
- tmport = wkport + 0x14;
- outb(0xff, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
- k = 0;
-widep_in1:
- j = inb(tmport);
- if ((j & 0x01) != 0) {
- tmport -= 0x06;
- mbuf[k++] = inb(tmport);
- tmport += 0x06;
- goto widep_in1;
- }
- if ((j & 0x80) == 0x00) {
- goto widep_in1;
- }
- tmport -= 0x08;
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto widep_in;
- }
- if (j == 0x0a) {
- goto widep_cmd;
- }
- if (j == 0x0e) {
- goto widep_out;
- }
- continue;
-widep_cmd:
- tmport = wkport + 0x10;
- outb(0x30, tmport);
- tmport = wkport + 0x14;
- outb(0x00, tmport);
- tmport += 0x04;
- outb(0x08, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- j = inb(tmport);
- if (j != 0x16) {
- if (j == 0x4e) {
- goto widep_out;
- }
- continue;
- }
- if (mbuf[0] != 0x01) {
- goto not_wide;
- }
- if (mbuf[1] != 0x02) {
- goto not_wide;
- }
- if (mbuf[2] != 0x03) {
- goto not_wide;
- }
- if (mbuf[3] != 0x01) {
- goto not_wide;
- }
- m = 1;
- m = m << i;
- dev->wide_id[0] |= m;
-not_wide:
- if ((dev->id[0][i].devtype == 0x00) || (dev->id[0][i].devtype == 0x07) || ((dev->id[0][i].devtype == 0x05) && ((n & 0x10) != 0))) {
- goto set_sync;
- }
- continue;
-set_sync:
- tmport = wkport + 0x1b;
- j = 0;
- if ((m & dev->wide_id[0]) != 0) {
- j |= 0x01;
- }
- outb(j, tmport);
- tmport = wkport + 3;
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[0][i].devsp, tmport++);
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
- continue;
-
- while (inb(tmport) != 0x8e)
- cpu_relax();
-
-try_sync:
- j = 0;
- tmport = wkport + 0x14;
- outb(0x06, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- if ((m & dev->wide_id[0]) != 0) {
- outb(synw[j++], tmport);
- } else {
- if ((m & dev->ultra_map[0]) != 0) {
- outb(synu[j++], tmport);
- } else {
- outb(synn[j++], tmport);
- }
- }
- tmport += 0x06;
- }
- }
- tmport -= 0x08;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto phase_ins;
- }
- if (j == 0x0a) {
- goto phase_cmds;
- }
- if (j == 0x0e) {
- goto try_sync;
- }
- continue;
-phase_outs:
- tmport = wkport + 0x18;
- outb(0x20, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00) {
- if ((inb(tmport) & 0x01) != 0x00) {
- tmport -= 0x06;
- outb(0x00, tmport);
- tmport += 0x06;
- }
- }
- tmport -= 0x08;
- j = inb(tmport);
- if (j == 0x85) {
- goto tar_dcons;
- }
- j &= 0x0f;
- if (j == 0x0f) {
- goto phase_ins;
- }
- if (j == 0x0a) {
- goto phase_cmds;
- }
- if (j == 0x0e) {
- goto phase_outs;
- }
- continue;
-phase_ins:
- tmport = wkport + 0x14;
- outb(0xff, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
- k = 0;
-phase_ins1:
- j = inb(tmport);
- if ((j & 0x01) != 0x00) {
- tmport -= 0x06;
- mbuf[k++] = inb(tmport);
- tmport += 0x06;
- goto phase_ins1;
- }
- if ((j & 0x80) == 0x00) {
- goto phase_ins1;
- }
- tmport -= 0x08;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- j = inb(tmport);
- if (j == 0x85) {
- goto tar_dcons;
- }
- j &= 0x0f;
- if (j == 0x0f) {
- goto phase_ins;
- }
- if (j == 0x0a) {
- goto phase_cmds;
- }
- if (j == 0x0e) {
- goto phase_outs;
- }
- continue;
-phase_cmds:
- tmport = wkport + 0x10;
- outb(0x30, tmport);
-tar_dcons:
- tmport = wkport + 0x14;
- outb(0x00, tmport);
- tmport += 0x04;
- outb(0x08, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- j = inb(tmport);
- if (j != 0x16) {
- continue;
- }
- if (mbuf[0] != 0x01) {
- continue;
- }
- if (mbuf[1] != 0x03) {
- continue;
- }
- if (mbuf[4] == 0x00) {
- continue;
- }
- if (mbuf[3] > 0x64) {
- continue;
- }
- if (mbuf[4] > 0x0c) {
- mbuf[4] = 0x0c;
- }
- dev->id[0][i].devsp = mbuf[4];
- if ((mbuf[3] < 0x0d) && (rmb == 0)) {
- j = 0xa0;
- goto set_syn_ok;
- }
- if (mbuf[3] < 0x1a) {
- j = 0x20;
- goto set_syn_ok;
- }
- if (mbuf[3] < 0x33) {
- j = 0x40;
- goto set_syn_ok;
- }
- if (mbuf[3] < 0x4c) {
- j = 0x50;
- goto set_syn_ok;
- }
- j = 0x60;
-set_syn_ok:
- dev->id[0][i].devsp = (dev->id[0][i].devsp & 0x0f) | j;
- }
- tmport = wkport + 0x3a;
- outb((unsigned char) (inb(tmport) & 0xef), tmport);
-}
-
-static void is880(struct atp_unit *dev, unsigned int wkport)
-{
- unsigned int tmport;
- unsigned char i, j, k, rmb, n, lvdmode;
- unsigned short int m;
- static unsigned char mbuf[512];
- static unsigned char satn[9] = { 0, 0, 0, 0, 0, 0, 0, 6, 6 };
- static unsigned char inqd[9] = { 0x12, 0, 0, 0, 0x24, 0, 0, 0x24, 6 };
- static unsigned char synn[6] = { 0x80, 1, 3, 1, 0x19, 0x0e };
- unsigned char synu[6] = { 0x80, 1, 3, 1, 0x0a, 0x0e };
- static unsigned char synw[6] = { 0x80, 1, 3, 1, 0x19, 0x0e };
- unsigned char synuw[6] = { 0x80, 1, 3, 1, 0x0a, 0x0e };
- static unsigned char wide[6] = { 0x80, 1, 2, 3, 1, 0 };
- static unsigned char u3[9] = { 0x80, 1, 6, 4, 0x09, 00, 0x0e, 0x01, 0x02 };
-
- lvdmode = inb(wkport + 0x3f) & 0x40;
-
- for (i = 0; i < 16; i++) {
- m = 1;
- m = m << i;
- if ((m & dev->active_id[0]) != 0) {
- continue;
- }
- if (i == dev->host_id[0]) {
- printk(KERN_INFO " ID: %2d Host Adapter\n", dev->host_id[0]);
- continue;
- }
- tmport = wkport + 0x5b;
- outb(0x01, tmport);
- tmport = wkport + 0x41;
- outb(0x08, tmport++);
- outb(0x7f, tmport++);
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[0][i].devsp, tmport++);
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
- j = i;
- if ((j & 0x08) != 0) {
- j = (j & 0x07) | 0x40;
- }
- outb(j, tmport);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
- continue;
-
- while (inb(tmport) != 0x8e)
- cpu_relax();
-
- dev->active_id[0] |= m;
-
- tmport = wkport + 0x50;
- outb(0x30, tmport);
- tmport = wkport + 0x54;
- outb(0x00, tmport);
-
-phase_cmd:
- tmport = wkport + 0x58;
- outb(0x08, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- j = inb(tmport);
- if (j != 0x16) {
- tmport = wkport + 0x50;
- outb(0x41, tmport);
- goto phase_cmd;
- }
-sel_ok:
- tmport = wkport + 0x43;
- outb(inqd[0], tmport++);
- outb(inqd[1], tmport++);
- outb(inqd[2], tmport++);
- outb(inqd[3], tmport++);
- outb(inqd[4], tmport++);
- outb(inqd[5], tmport);
- tmport += 0x07;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[0][i].devsp, tmport++);
- outb(0, tmport++);
- outb(inqd[6], tmport++);
- outb(inqd[7], tmport++);
- tmport += 0x03;
- outb(inqd[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
- continue;
-
- while (inb(tmport) != 0x8e)
- cpu_relax();
-
- tmport = wkport + 0x5b;
- outb(0x00, tmport);
- tmport = wkport + 0x58;
- outb(0x08, tmport);
- tmport += 0x07;
- j = 0;
-rd_inq_data:
- k = inb(tmport);
- if ((k & 0x01) != 0) {
- tmport -= 0x06;
- mbuf[j++] = inb(tmport);
- tmport += 0x06;
- goto rd_inq_data;
- }
- if ((k & 0x80) == 0) {
- goto rd_inq_data;
- }
- tmport -= 0x08;
- j = inb(tmport);
- if (j == 0x16) {
- goto inq_ok;
- }
- tmport = wkport + 0x50;
- outb(0x46, tmport);
- tmport += 0x02;
- outb(0, tmport++);
- outb(0, tmport++);
- outb(0, tmport++);
- tmport += 0x03;
- outb(0x08, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- if (inb(tmport) != 0x16)
- goto sel_ok;
-
-inq_ok:
- mbuf[36] = 0;
- printk(KERN_INFO " ID: %2d %s\n", i, &mbuf[8]);
- dev->id[0][i].devtype = mbuf[0];
- rmb = mbuf[1];
- n = mbuf[7];
- if ((mbuf[7] & 0x60) == 0) {
- goto not_wide;
- }
- if ((i < 8) && ((dev->global_map[0] & 0x20) == 0)) {
- goto not_wide;
- }
- if (lvdmode == 0) {
- goto chg_wide;
- }
- if (dev->sp[0][i] != 0x04) // force u2
- {
- goto chg_wide;
- }
-
- tmport = wkport + 0x5b;
- outb(0x01, tmport);
- tmport = wkport + 0x43;
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[0][i].devsp, tmport++);
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
-
- if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
- continue;
-
- while (inb(tmport) != 0x8e)
- cpu_relax();
-
-try_u3:
- j = 0;
- tmport = wkport + 0x54;
- outb(0x09, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- outb(u3[j++], tmport);
- tmport += 0x06;
- }
- }
- tmport -= 0x08;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto u3p_in;
- }
- if (j == 0x0a) {
- goto u3p_cmd;
- }
- if (j == 0x0e) {
- goto try_u3;
- }
- continue;
-u3p_out:
- tmport = wkport + 0x58;
- outb(0x20, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- outb(0, tmport);
- tmport += 0x06;
- }
- }
- tmport -= 0x08;
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto u3p_in;
- }
- if (j == 0x0a) {
- goto u3p_cmd;
- }
- if (j == 0x0e) {
- goto u3p_out;
- }
- continue;
-u3p_in:
- tmport = wkport + 0x54;
- outb(0x09, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
- k = 0;
-u3p_in1:
- j = inb(tmport);
- if ((j & 0x01) != 0) {
- tmport -= 0x06;
- mbuf[k++] = inb(tmport);
- tmport += 0x06;
- goto u3p_in1;
- }
- if ((j & 0x80) == 0x00) {
- goto u3p_in1;
- }
- tmport -= 0x08;
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto u3p_in;
- }
- if (j == 0x0a) {
- goto u3p_cmd;
- }
- if (j == 0x0e) {
- goto u3p_out;
- }
- continue;
-u3p_cmd:
- tmport = wkport + 0x50;
- outb(0x30, tmport);
- tmport = wkport + 0x54;
- outb(0x00, tmport);
- tmport += 0x04;
- outb(0x08, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- j = inb(tmport);
- if (j != 0x16) {
- if (j == 0x4e) {
- goto u3p_out;
- }
- continue;
- }
- if (mbuf[0] != 0x01) {
- goto chg_wide;
- }
- if (mbuf[1] != 0x06) {
- goto chg_wide;
- }
- if (mbuf[2] != 0x04) {
- goto chg_wide;
- }
- if (mbuf[3] == 0x09) {
- m = 1;
- m = m << i;
- dev->wide_id[0] |= m;
- dev->id[0][i].devsp = 0xce;
- continue;
- }
-chg_wide:
- tmport = wkport + 0x5b;
- outb(0x01, tmport);
- tmport = wkport + 0x43;
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[0][i].devsp, tmport++);
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- if (inb(tmport) != 0x11 && inb(tmport) != 0x8e)
- continue;
-
- while (inb(tmport) != 0x8e)
- cpu_relax();
-
-try_wide:
- j = 0;
- tmport = wkport + 0x54;
- outb(0x05, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- outb(wide[j++], tmport);
- tmport += 0x06;
- }
- }
- tmport -= 0x08;
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto widep_in;
- }
- if (j == 0x0a) {
- goto widep_cmd;
- }
- if (j == 0x0e) {
- goto try_wide;
- }
- continue;
-widep_out:
- tmport = wkport + 0x58;
- outb(0x20, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- outb(0, tmport);
- tmport += 0x06;
- }
- }
- tmport -= 0x08;
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto widep_in;
- }
- if (j == 0x0a) {
- goto widep_cmd;
- }
- if (j == 0x0e) {
- goto widep_out;
- }
- continue;
-widep_in:
- tmport = wkport + 0x54;
- outb(0xff, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
- k = 0;
-widep_in1:
- j = inb(tmport);
- if ((j & 0x01) != 0) {
- tmport -= 0x06;
- mbuf[k++] = inb(tmport);
- tmport += 0x06;
- goto widep_in1;
- }
- if ((j & 0x80) == 0x00) {
- goto widep_in1;
- }
- tmport -= 0x08;
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto widep_in;
- }
- if (j == 0x0a) {
- goto widep_cmd;
- }
- if (j == 0x0e) {
- goto widep_out;
- }
- continue;
-widep_cmd:
- tmport = wkport + 0x50;
- outb(0x30, tmport);
- tmport = wkport + 0x54;
- outb(0x00, tmport);
- tmport += 0x04;
- outb(0x08, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- j = inb(tmport);
- if (j != 0x16) {
- if (j == 0x4e) {
- goto widep_out;
- }
- continue;
- }
- if (mbuf[0] != 0x01) {
- goto not_wide;
- }
- if (mbuf[1] != 0x02) {
- goto not_wide;
- }
- if (mbuf[2] != 0x03) {
- goto not_wide;
- }
- if (mbuf[3] != 0x01) {
- goto not_wide;
- }
- m = 1;
- m = m << i;
- dev->wide_id[0] |= m;
-not_wide:
- if ((dev->id[0][i].devtype == 0x00) || (dev->id[0][i].devtype == 0x07) || ((dev->id[0][i].devtype == 0x05) && ((n & 0x10) != 0))) {
- m = 1;
- m = m << i;
- if ((dev->async[0] & m) != 0) {
- goto set_sync;
- }
- }
- continue;
-set_sync:
- if (dev->sp[0][i] == 0x02) {
- synu[4] = 0x0c;
- synuw[4] = 0x0c;
- } else {
- if (dev->sp[0][i] >= 0x03) {
- synu[4] = 0x0a;
- synuw[4] = 0x0a;
- }
- }
- tmport = wkport + 0x5b;
- j = 0;
- if ((m & dev->wide_id[0]) != 0) {
- j |= 0x01;
- }
- outb(j, tmport);
- tmport = wkport + 0x43;
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[0][i].devsp, tmport++);
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) {
- continue;
- }
- while (inb(tmport) != 0x8e)
- cpu_relax();
-
-try_sync:
- j = 0;
- tmport = wkport + 0x54;
- outb(0x06, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- if ((m & dev->wide_id[0]) != 0) {
- if ((m & dev->ultra_map[0]) != 0) {
- outb(synuw[j++], tmport);
- } else {
- outb(synw[j++], tmport);
- }
- } else {
- if ((m & dev->ultra_map[0]) != 0) {
- outb(synu[j++], tmport);
- } else {
- outb(synn[j++], tmport);
- }
- }
- tmport += 0x06;
- }
- }
- tmport -= 0x08;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- j = inb(tmport) & 0x0f;
- if (j == 0x0f) {
- goto phase_ins;
- }
- if (j == 0x0a) {
- goto phase_cmds;
- }
- if (j == 0x0e) {
- goto try_sync;
- }
- continue;
-phase_outs:
- tmport = wkport + 0x58;
- outb(0x20, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00) {
- if ((inb(tmport) & 0x01) != 0x00) {
- tmport -= 0x06;
- outb(0x00, tmport);
- tmport += 0x06;
- }
- }
- tmport -= 0x08;
- j = inb(tmport);
- if (j == 0x85) {
- goto tar_dcons;
- }
- j &= 0x0f;
- if (j == 0x0f) {
- goto phase_ins;
- }
- if (j == 0x0a) {
- goto phase_cmds;
- }
- if (j == 0x0e) {
- goto phase_outs;
- }
- continue;
-phase_ins:
- tmport = wkport + 0x54;
- outb(0x06, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
- k = 0;
-phase_ins1:
- j = inb(tmport);
- if ((j & 0x01) != 0x00) {
- tmport -= 0x06;
- mbuf[k++] = inb(tmport);
- tmport += 0x06;
- goto phase_ins1;
- }
- if ((j & 0x80) == 0x00) {
- goto phase_ins1;
- }
- tmport -= 0x08;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- j = inb(tmport);
- if (j == 0x85) {
- goto tar_dcons;
- }
- j &= 0x0f;
- if (j == 0x0f) {
- goto phase_ins;
- }
- if (j == 0x0a) {
- goto phase_cmds;
- }
- if (j == 0x0e) {
- goto phase_outs;
- }
- continue;
-phase_cmds:
- tmport = wkport + 0x50;
- outb(0x30, tmport);
-tar_dcons:
- tmport = wkport + 0x54;
- outb(0x00, tmport);
- tmport += 0x04;
- outb(0x08, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
- cpu_relax();
-
- tmport -= 0x08;
- j = inb(tmport);
- if (j != 0x16) {
- continue;
- }
- if (mbuf[0] != 0x01) {
- continue;
- }
- if (mbuf[1] != 0x03) {
- continue;
- }
- if (mbuf[4] == 0x00) {
- continue;
- }
- if (mbuf[3] > 0x64) {
- continue;
- }
- if (mbuf[4] > 0x0e) {
- mbuf[4] = 0x0e;
- }
- dev->id[0][i].devsp = mbuf[4];
- if (mbuf[3] < 0x0c) {
- j = 0xb0;
- goto set_syn_ok;
- }
- if ((mbuf[3] < 0x0d) && (rmb == 0)) {
- j = 0xa0;
- goto set_syn_ok;
- }
- if (mbuf[3] < 0x1a) {
- j = 0x20;
- goto set_syn_ok;
- }
- if (mbuf[3] < 0x33) {
- j = 0x40;
- goto set_syn_ok;
- }
- if (mbuf[3] < 0x4c) {
- j = 0x50;
- goto set_syn_ok;
- }
- j = 0x60;
-set_syn_ok:
- dev->id[0][i].devsp = (dev->id[0][i].devsp & 0x0f) | j;
}
}
@@ -2560,491 +1237,345 @@ static int atp870u_init_tables(struct Scsi_Host *host)
return 0;
}
-/* return non-zero on detection */
-static int atp870u_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+static void atp_set_host_id(struct atp_unit *atp, u8 c, u8 host_id)
{
- unsigned char k, m, c;
- unsigned long flags;
- unsigned int base_io, tmport, error,n;
- unsigned char host_id;
- struct Scsi_Host *shpnt = NULL;
- struct atp_unit *atpdev, *p;
- unsigned char setupdata[2][16];
- int count = 0;
+ atp_writeb_io(atp, c, 0, host_id | 0x08);
+ atp_writeb_io(atp, c, 0x18, 0);
+ while ((atp_readb_io(atp, c, 0x1f) & 0x80) == 0)
+ mdelay(1);
+ atp_readb_io(atp, c, 0x17);
+ atp_writeb_io(atp, c, 1, 8);
+ atp_writeb_io(atp, c, 2, 0x7f);
+ atp_writeb_io(atp, c, 0x11, 0x20);
+}
- atpdev = kzalloc(sizeof(*atpdev), GFP_KERNEL);
- if (!atpdev)
- return -ENOMEM;
+static void atp870_init(struct Scsi_Host *shpnt)
+{
+ struct atp_unit *atpdev = shost_priv(shpnt);
+ struct pci_dev *pdev = atpdev->pdev;
+ unsigned char k, host_id;
+ u8 scam_on;
+ bool wide_chip =
+ (pdev->device == PCI_DEVICE_ID_ARTOP_AEC7610 &&
+ pdev->revision == 4) ||
+ (pdev->device == PCI_DEVICE_ID_ARTOP_AEC7612UW) ||
+ (pdev->device == PCI_DEVICE_ID_ARTOP_AEC7612SUW);
+
+ pci_read_config_byte(pdev, 0x49, &host_id);
+
+ dev_info(&pdev->dev, "ACARD AEC-671X PCI Ultra/W SCSI-2/3 Host Adapter: IO:%lx, IRQ:%d.\n",
+ shpnt->io_port, shpnt->irq);
+
+ atpdev->ioport[0] = shpnt->io_port;
+ atpdev->pciport[0] = shpnt->io_port + 0x20;
+ host_id &= 0x07;
+ atpdev->host_id[0] = host_id;
+ scam_on = atp_readb_pci(atpdev, 0, 2);
+ atpdev->global_map[0] = atp_readb_base(atpdev, 0x2d);
+ atpdev->ultra_map[0] = atp_readw_base(atpdev, 0x2e);
+
+ if (atpdev->ultra_map[0] == 0) {
+ scam_on = 0x00;
+ atpdev->global_map[0] = 0x20;
+ atpdev->ultra_map[0] = 0xffff;
+ }
- if (pci_enable_device(pdev))
- goto err_eio;
+ if (pdev->revision > 0x07) /* check if atp876 chip */
+ atp_writeb_base(atpdev, 0x3e, 0x00); /* enable terminator */
+
+ k = (atp_readb_base(atpdev, 0x3a) & 0xf3) | 0x10;
+ atp_writeb_base(atpdev, 0x3a, k);
+ atp_writeb_base(atpdev, 0x3a, k & 0xdf);
+ mdelay(32);
+ atp_writeb_base(atpdev, 0x3a, k);
+ mdelay(32);
+ atp_set_host_id(atpdev, 0, host_id);
+
+ tscam(shpnt, wide_chip, scam_on);
+ atp_writeb_base(atpdev, 0x3a, atp_readb_base(atpdev, 0x3a) | 0x10);
+ atp_is(atpdev, 0, wide_chip, 0);
+ atp_writeb_base(atpdev, 0x3a, atp_readb_base(atpdev, 0x3a) & 0xef);
+ atp_writeb_base(atpdev, 0x3b, atp_readb_base(atpdev, 0x3b) | 0x20);
+ shpnt->max_id = wide_chip ? 16 : 8;
+ shpnt->this_id = host_id;
+}
- if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
- printk(KERN_INFO "atp870u: use 32bit DMA mask.\n");
- } else {
- printk(KERN_ERR "atp870u: DMA mask required but not available.\n");
- goto err_eio;
- }
+static void atp880_init(struct Scsi_Host *shpnt)
+{
+ struct atp_unit *atpdev = shost_priv(shpnt);
+ struct pci_dev *pdev = atpdev->pdev;
+ unsigned char k, m, host_id;
+ unsigned int n;
- /*
- * It's probably easier to weed out some revisions like
- * this than via the PCI device table
- */
- if (ent->device == PCI_DEVICE_ID_ARTOP_AEC7610) {
- atpdev->chip_ver = pdev->revision;
- if (atpdev->chip_ver < 2)
- goto err_eio;
- }
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x80);
- switch (ent->device) {
- case PCI_DEVICE_ID_ARTOP_AEC7612UW:
- case PCI_DEVICE_ID_ARTOP_AEC7612SUW:
- case ATP880_DEVID1:
- case ATP880_DEVID2:
- case ATP885_DEVID:
- atpdev->chip_ver = 0x04;
- default:
- break;
- }
- base_io = pci_resource_start(pdev, 0);
- base_io &= 0xfffffff8;
-
- if ((ent->device == ATP880_DEVID1)||(ent->device == ATP880_DEVID2)) {
- atpdev->chip_ver = pdev->revision;
- pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x80);//JCC082803
-
- host_id = inb(base_io + 0x39);
- host_id >>= 0x04;
-
- printk(KERN_INFO " ACARD AEC-67160 PCI Ultra3 LVD Host Adapter: %d"
- " IO:%x, IRQ:%d.\n", count, base_io, pdev->irq);
- atpdev->ioport[0] = base_io + 0x40;
- atpdev->pciport[0] = base_io + 0x28;
- atpdev->dev_id = ent->device;
- atpdev->host_id[0] = host_id;
-
- tmport = base_io + 0x22;
- atpdev->scam_on = inb(tmport);
- tmport += 0x13;
- atpdev->global_map[0] = inb(tmport);
- tmport += 0x07;
- atpdev->ultra_map[0] = inw(tmport);
-
- n = 0x3f09;
-next_fblk_880:
- if (n >= 0x4000)
- goto flash_ok_880;
+ atpdev->ioport[0] = shpnt->io_port + 0x40;
+ atpdev->pciport[0] = shpnt->io_port + 0x28;
+
+ host_id = atp_readb_base(atpdev, 0x39) >> 4;
+ dev_info(&pdev->dev, "ACARD AEC-67160 PCI Ultra3 LVD Host Adapter: IO:%lx, IRQ:%d.\n",
+ shpnt->io_port, shpnt->irq);
+ atpdev->host_id[0] = host_id;
+
+ atpdev->global_map[0] = atp_readb_base(atpdev, 0x35);
+ atpdev->ultra_map[0] = atp_readw_base(atpdev, 0x3c);
+
+ n = 0x3f09;
+ while (n < 0x4000) {
m = 0;
- outw(n, base_io + 0x34);
+ atp_writew_base(atpdev, 0x34, n);
n += 0x0002;
- if (inb(base_io + 0x30) == 0xff)
- goto flash_ok_880;
-
- atpdev->sp[0][m++] = inb(base_io + 0x30);
- atpdev->sp[0][m++] = inb(base_io + 0x31);
- atpdev->sp[0][m++] = inb(base_io + 0x32);
- atpdev->sp[0][m++] = inb(base_io + 0x33);
- outw(n, base_io + 0x34);
+ if (atp_readb_base(atpdev, 0x30) == 0xff)
+ break;
+
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x30);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x31);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x32);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x33);
+ atp_writew_base(atpdev, 0x34, n);
n += 0x0002;
- atpdev->sp[0][m++] = inb(base_io + 0x30);
- atpdev->sp[0][m++] = inb(base_io + 0x31);
- atpdev->sp[0][m++] = inb(base_io + 0x32);
- atpdev->sp[0][m++] = inb(base_io + 0x33);
- outw(n, base_io + 0x34);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x30);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x31);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x32);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x33);
+ atp_writew_base(atpdev, 0x34, n);
n += 0x0002;
- atpdev->sp[0][m++] = inb(base_io + 0x30);
- atpdev->sp[0][m++] = inb(base_io + 0x31);
- atpdev->sp[0][m++] = inb(base_io + 0x32);
- atpdev->sp[0][m++] = inb(base_io + 0x33);
- outw(n, base_io + 0x34);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x30);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x31);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x32);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x33);
+ atp_writew_base(atpdev, 0x34, n);
n += 0x0002;
- atpdev->sp[0][m++] = inb(base_io + 0x30);
- atpdev->sp[0][m++] = inb(base_io + 0x31);
- atpdev->sp[0][m++] = inb(base_io + 0x32);
- atpdev->sp[0][m++] = inb(base_io + 0x33);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x30);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x31);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x32);
+ atpdev->sp[0][m++] = atp_readb_base(atpdev, 0x33);
n += 0x0018;
- goto next_fblk_880;
-flash_ok_880:
- outw(0, base_io + 0x34);
- atpdev->ultra_map[0] = 0;
- atpdev->async[0] = 0;
+ }
+ atp_writew_base(atpdev, 0x34, 0);
+ atpdev->ultra_map[0] = 0;
+ atpdev->async[0] = 0;
+ for (k = 0; k < 16; k++) {
+ n = 1 << k;
+ if (atpdev->sp[0][k] > 1)
+ atpdev->ultra_map[0] |= n;
+ else
+ if (atpdev->sp[0][k] == 0)
+ atpdev->async[0] |= n;
+ }
+ atpdev->async[0] = ~(atpdev->async[0]);
+ atp_writeb_base(atpdev, 0x35, atpdev->global_map[0]);
+
+ k = atp_readb_base(atpdev, 0x38) & 0x80;
+ atp_writeb_base(atpdev, 0x38, k);
+ atp_writeb_base(atpdev, 0x3b, 0x20);
+ mdelay(32);
+ atp_writeb_base(atpdev, 0x3b, 0);
+ mdelay(32);
+ atp_readb_io(atpdev, 0, 0x1b);
+ atp_readb_io(atpdev, 0, 0x17);
+
+ atp_set_host_id(atpdev, 0, host_id);
+
+ tscam(shpnt, true, atp_readb_base(atpdev, 0x22));
+ atp_is(atpdev, 0, true, atp_readb_base(atpdev, 0x3f) & 0x40);
+ atp_writeb_base(atpdev, 0x38, 0xb0);
+ shpnt->max_id = 16;
+ shpnt->this_id = host_id;
+}
+
+static void atp885_init(struct Scsi_Host *shpnt)
+{
+ struct atp_unit *atpdev = shost_priv(shpnt);
+ struct pci_dev *pdev = atpdev->pdev;
+ unsigned char k, m, c;
+ unsigned int n;
+ unsigned char setupdata[2][16];
+
+ dev_info(&pdev->dev, "ACARD AEC-67162 PCI Ultra3 LVD Host Adapter: IO:%lx, IRQ:%d.\n",
+ shpnt->io_port, shpnt->irq);
+
+ atpdev->ioport[0] = shpnt->io_port + 0x80;
+ atpdev->ioport[1] = shpnt->io_port + 0xc0;
+ atpdev->pciport[0] = shpnt->io_port + 0x40;
+ atpdev->pciport[1] = shpnt->io_port + 0x50;
+
+ c = atp_readb_base(atpdev, 0x29);
+ atp_writeb_base(atpdev, 0x29, c | 0x04);
+
+ n = 0x1f80;
+ while (n < 0x2000) {
+ atp_writew_base(atpdev, 0x3c, n);
+ if (atp_readl_base(atpdev, 0x38) == 0xffffffff)
+ break;
+ for (m = 0; m < 2; m++) {
+ atpdev->global_map[m] = 0;
+ for (k = 0; k < 4; k++) {
+ atp_writew_base(atpdev, 0x3c, n++);
+ ((unsigned long *)&setupdata[m][0])[k] = atp_readl_base(atpdev, 0x38);
+ }
+ for (k = 0; k < 4; k++) {
+ atp_writew_base(atpdev, 0x3c, n++);
+ ((unsigned long *)&atpdev->sp[m][0])[k] = atp_readl_base(atpdev, 0x38);
+ }
+ n += 8;
+ }
+ }
+ c = atp_readb_base(atpdev, 0x29);
+ atp_writeb_base(atpdev, 0x29, c & 0xfb);
+ for (c = 0; c < 2; c++) {
+ atpdev->ultra_map[c] = 0;
+ atpdev->async[c] = 0;
for (k = 0; k < 16; k++) {
- n = 1;
- n = n << k;
- if (atpdev->sp[0][k] > 1) {
- atpdev->ultra_map[0] |= n;
- } else {
- if (atpdev->sp[0][k] == 0)
- atpdev->async[0] |= n;
- }
- }
- atpdev->async[0] = ~(atpdev->async[0]);
- outb(atpdev->global_map[0], base_io + 0x35);
-
- shpnt = scsi_host_alloc(&atp870u_template, sizeof(struct atp_unit));
- if (!shpnt)
- goto err_nomem;
-
- p = (struct atp_unit *)&shpnt->hostdata;
-
- atpdev->host = shpnt;
- atpdev->pdev = pdev;
- pci_set_drvdata(pdev, p);
- memcpy(p, atpdev, sizeof(*atpdev));
- if (atp870u_init_tables(shpnt) < 0) {
- printk(KERN_ERR "Unable to allocate tables for Acard controller\n");
- goto unregister;
- }
-
- if (request_irq(pdev->irq, atp870u_intr_handle, IRQF_SHARED, "atp880i", shpnt)) {
- printk(KERN_ERR "Unable to allocate IRQ%d for Acard controller.\n", pdev->irq);
- goto free_tables;
- }
-
- spin_lock_irqsave(shpnt->host_lock, flags);
- tmport = base_io + 0x38;
- k = inb(tmport) & 0x80;
- outb(k, tmport);
- tmport += 0x03;
- outb(0x20, tmport);
- mdelay(32);
- outb(0, tmport);
- mdelay(32);
- tmport = base_io + 0x5b;
- inb(tmport);
- tmport -= 0x04;
- inb(tmport);
- tmport = base_io + 0x40;
- outb((host_id | 0x08), tmport);
- tmport += 0x18;
- outb(0, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0)
- mdelay(1);
- tmport -= 0x08;
- inb(tmport);
- tmport = base_io + 0x41;
- outb(8, tmport++);
- outb(0x7f, tmport);
- tmport = base_io + 0x51;
- outb(0x20, tmport);
-
- tscam(shpnt);
- is880(p, base_io);
- tmport = base_io + 0x38;
- outb(0xb0, tmport);
- shpnt->max_id = 16;
- shpnt->this_id = host_id;
- shpnt->unique_id = base_io;
- shpnt->io_port = base_io;
- shpnt->n_io_port = 0x60; /* Number of bytes of I/O space used */
- shpnt->irq = pdev->irq;
- } else if (ent->device == ATP885_DEVID) {
- printk(KERN_INFO " ACARD AEC-67162 PCI Ultra3 LVD Host Adapter: IO:%x, IRQ:%d.\n"
- , base_io, pdev->irq);
-
- atpdev->pdev = pdev;
- atpdev->dev_id = ent->device;
- atpdev->baseport = base_io;
- atpdev->ioport[0] = base_io + 0x80;
- atpdev->ioport[1] = base_io + 0xc0;
- atpdev->pciport[0] = base_io + 0x40;
- atpdev->pciport[1] = base_io + 0x50;
-
- shpnt = scsi_host_alloc(&atp870u_template, sizeof(struct atp_unit));
- if (!shpnt)
- goto err_nomem;
-
- p = (struct atp_unit *)&shpnt->hostdata;
-
- atpdev->host = shpnt;
- atpdev->pdev = pdev;
- pci_set_drvdata(pdev, p);
- memcpy(p, atpdev, sizeof(struct atp_unit));
- if (atp870u_init_tables(shpnt) < 0)
- goto unregister;
-
-#ifdef ED_DBGP
- printk("request_irq() shpnt %p hostdata %p\n", shpnt, p);
-#endif
- if (request_irq(pdev->irq, atp870u_intr_handle, IRQF_SHARED, "atp870u", shpnt)) {
- printk(KERN_ERR "Unable to allocate IRQ for Acard controller.\n");
- goto free_tables;
+ n = 1 << k;
+ if (atpdev->sp[c][k] > 1)
+ atpdev->ultra_map[c] |= n;
+ else
+ if (atpdev->sp[c][k] == 0)
+ atpdev->async[c] |= n;
+ }
+ atpdev->async[c] = ~(atpdev->async[c]);
+
+ if (atpdev->global_map[c] == 0) {
+ k = setupdata[c][1];
+ if ((k & 0x40) != 0)
+ atpdev->global_map[c] |= 0x20;
+ k &= 0x07;
+ atpdev->global_map[c] |= k;
+ if ((setupdata[c][2] & 0x04) != 0)
+ atpdev->global_map[c] |= 0x08;
+ atpdev->host_id[c] = setupdata[c][0] & 0x07;
}
-
- spin_lock_irqsave(shpnt->host_lock, flags);
-
- c=inb(base_io + 0x29);
- outb((c | 0x04),base_io + 0x29);
-
- n=0x1f80;
-next_fblk_885:
- if (n >= 0x2000) {
- goto flash_ok_885;
- }
- outw(n,base_io + 0x3c);
- if (inl(base_io + 0x38) == 0xffffffff) {
- goto flash_ok_885;
- }
- for (m=0; m < 2; m++) {
- p->global_map[m]= 0;
- for (k=0; k < 4; k++) {
- outw(n++,base_io + 0x3c);
- ((unsigned long *)&setupdata[m][0])[k]=inl(base_io + 0x38);
- }
- for (k=0; k < 4; k++) {
- outw(n++,base_io + 0x3c);
- ((unsigned long *)&p->sp[m][0])[k]=inl(base_io + 0x38);
- }
- n += 8;
- }
- goto next_fblk_885;
-flash_ok_885:
-#ifdef ED_DBGP
- printk( "Flash Read OK\n");
-#endif
- c=inb(base_io + 0x29);
- outb((c & 0xfb),base_io + 0x29);
- for (c=0;c < 2;c++) {
- p->ultra_map[c]=0;
- p->async[c] = 0;
- for (k=0; k < 16; k++) {
- n=1;
- n = n << k;
- if (p->sp[c][k] > 1) {
- p->ultra_map[c] |= n;
- } else {
- if (p->sp[c][k] == 0) {
- p->async[c] |= n;
- }
- }
- }
- p->async[c] = ~(p->async[c]);
-
- if (p->global_map[c] == 0) {
- k=setupdata[c][1];
- if ((k & 0x40) != 0)
- p->global_map[c] |= 0x20;
- k &= 0x07;
- p->global_map[c] |= k;
- if ((setupdata[c][2] & 0x04) != 0)
- p->global_map[c] |= 0x08;
- p->host_id[c] = setupdata[c][0] & 0x07;
- }
- }
-
- k = inb(base_io + 0x28) & 0x8f;
- k |= 0x10;
- outb(k, base_io + 0x28);
- outb(0x80, base_io + 0x41);
- outb(0x80, base_io + 0x51);
- mdelay(100);
- outb(0, base_io + 0x41);
- outb(0, base_io + 0x51);
- mdelay(1000);
- inb(base_io + 0x9b);
- inb(base_io + 0x97);
- inb(base_io + 0xdb);
- inb(base_io + 0xd7);
- tmport = base_io + 0x80;
- k=p->host_id[0];
- if (k > 7)
- k = (k & 0x07) | 0x40;
- k |= 0x08;
- outb(k, tmport);
- tmport += 0x18;
- outb(0, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0)
- cpu_relax();
-
- tmport -= 0x08;
- inb(tmport);
- tmport = base_io + 0x81;
- outb(8, tmport++);
- outb(0x7f, tmport);
- tmport = base_io + 0x91;
- outb(0x20, tmport);
-
- tmport = base_io + 0xc0;
- k=p->host_id[1];
- if (k > 7)
- k = (k & 0x07) | 0x40;
- k |= 0x08;
- outb(k, tmport);
- tmport += 0x18;
- outb(0, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0)
- cpu_relax();
+ }
- tmport -= 0x08;
- inb(tmport);
- tmport = base_io + 0xc1;
- outb(8, tmport++);
- outb(0x7f, tmport);
- tmport = base_io + 0xd1;
- outb(0x20, tmport);
-
- tscam_885();
- printk(KERN_INFO " Scanning Channel A SCSI Device ...\n");
- is885(p, base_io + 0x80, 0);
- printk(KERN_INFO " Scanning Channel B SCSI Device ...\n");
- is885(p, base_io + 0xc0, 1);
-
- k = inb(base_io + 0x28) & 0xcf;
- k |= 0xc0;
- outb(k, base_io + 0x28);
- k = inb(base_io + 0x1f) | 0x80;
- outb(k, base_io + 0x1f);
- k = inb(base_io + 0x29) | 0x01;
- outb(k, base_io + 0x29);
-#ifdef ED_DBGP
- //printk("atp885: atp_host[0] 0x%p\n", atp_host[0]);
-#endif
- shpnt->max_id = 16;
- shpnt->max_lun = (p->global_map[0] & 0x07) + 1;
- shpnt->max_channel = 1;
- shpnt->this_id = p->host_id[0];
- shpnt->unique_id = base_io;
- shpnt->io_port = base_io;
- shpnt->n_io_port = 0xff; /* Number of bytes of I/O space used */
- shpnt->irq = pdev->irq;
-
- } else {
- error = pci_read_config_byte(pdev, 0x49, &host_id);
+ k = atp_readb_base(atpdev, 0x28) & 0x8f;
+ k |= 0x10;
+ atp_writeb_base(atpdev, 0x28, k);
+ atp_writeb_pci(atpdev, 0, 1, 0x80);
+ atp_writeb_pci(atpdev, 1, 1, 0x80);
+ mdelay(100);
+ atp_writeb_pci(atpdev, 0, 1, 0);
+ atp_writeb_pci(atpdev, 1, 1, 0);
+ mdelay(1000);
+ atp_readb_io(atpdev, 0, 0x1b);
+ atp_readb_io(atpdev, 0, 0x17);
+ atp_readb_io(atpdev, 1, 0x1b);
+ atp_readb_io(atpdev, 1, 0x17);
+
+ k = atpdev->host_id[0];
+ if (k > 7)
+ k = (k & 0x07) | 0x40;
+ atp_set_host_id(atpdev, 0, k);
+
+ k = atpdev->host_id[1];
+ if (k > 7)
+ k = (k & 0x07) | 0x40;
+ atp_set_host_id(atpdev, 1, k);
+
+ mdelay(600); /* this delay used to be called tscam_885() */
+ dev_info(&pdev->dev, "Scanning Channel A SCSI Device ...\n");
+ atp_is(atpdev, 0, true, atp_readb_io(atpdev, 0, 0x1b) >> 7);
+ atp_writeb_io(atpdev, 0, 0x16, 0x80);
+ dev_info(&pdev->dev, "Scanning Channel B SCSI Device ...\n");
+ atp_is(atpdev, 1, true, atp_readb_io(atpdev, 1, 0x1b) >> 7);
+ atp_writeb_io(atpdev, 1, 0x16, 0x80);
+ k = atp_readb_base(atpdev, 0x28) & 0xcf;
+ k |= 0xc0;
+ atp_writeb_base(atpdev, 0x28, k);
+ k = atp_readb_base(atpdev, 0x1f) | 0x80;
+ atp_writeb_base(atpdev, 0x1f, k);
+ k = atp_readb_base(atpdev, 0x29) | 0x01;
+ atp_writeb_base(atpdev, 0x29, k);
+ shpnt->max_id = 16;
+ shpnt->max_lun = (atpdev->global_map[0] & 0x07) + 1;
+ shpnt->max_channel = 1;
+ shpnt->this_id = atpdev->host_id[0];
+}
- printk(KERN_INFO " ACARD AEC-671X PCI Ultra/W SCSI-2/3 Host Adapter: %d "
- "IO:%x, IRQ:%d.\n", count, base_io, pdev->irq);
+/* return non-zero on detection */
+static int atp870u_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct Scsi_Host *shpnt = NULL;
+ struct atp_unit *atpdev;
+ int err;
- atpdev->ioport[0] = base_io;
- atpdev->pciport[0] = base_io + 0x20;
- atpdev->dev_id = ent->device;
- host_id &= 0x07;
- atpdev->host_id[0] = host_id;
- tmport = base_io + 0x22;
- atpdev->scam_on = inb(tmport);
- tmport += 0x0b;
- atpdev->global_map[0] = inb(tmport++);
- atpdev->ultra_map[0] = inw(tmport);
+ if (ent->device == PCI_DEVICE_ID_ARTOP_AEC7610 && pdev->revision < 2) {
+ dev_err(&pdev->dev, "ATP850S chips (AEC6710L/F cards) are not supported.\n");
+ return -ENODEV;
+ }
- if (atpdev->ultra_map[0] == 0) {
- atpdev->scam_on = 0x00;
- atpdev->global_map[0] = 0x20;
- atpdev->ultra_map[0] = 0xffff;
- }
+ err = pci_enable_device(pdev);
+ if (err)
+ goto fail;
- shpnt = scsi_host_alloc(&atp870u_template, sizeof(struct atp_unit));
- if (!shpnt)
- goto err_nomem;
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ printk(KERN_ERR "atp870u: DMA mask required but not available.\n");
+ err = -EIO;
+ goto disable_device;
+ }
- p = (struct atp_unit *)&shpnt->hostdata;
-
- atpdev->host = shpnt;
- atpdev->pdev = pdev;
- pci_set_drvdata(pdev, p);
- memcpy(p, atpdev, sizeof(*atpdev));
- if (atp870u_init_tables(shpnt) < 0)
- goto unregister;
-
- if (request_irq(pdev->irq, atp870u_intr_handle, IRQF_SHARED, "atp870i", shpnt)) {
- printk(KERN_ERR "Unable to allocate IRQ%d for Acard controller.\n", pdev->irq);
- goto free_tables;
- }
-
- spin_lock_irqsave(shpnt->host_lock, flags);
- if (atpdev->chip_ver > 0x07) { /* check if atp876 chip then enable terminator */
- tmport = base_io + 0x3e;
- outb(0x00, tmport);
- }
-
- tmport = base_io + 0x3a;
- k = (inb(tmport) & 0xf3) | 0x10;
- outb(k, tmport);
- outb((k & 0xdf), tmport);
- mdelay(32);
- outb(k, tmport);
- mdelay(32);
- tmport = base_io;
- outb((host_id | 0x08), tmport);
- tmport += 0x18;
- outb(0, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0)
- mdelay(1);
-
- tmport -= 0x08;
- inb(tmport);
- tmport = base_io + 1;
- outb(8, tmport++);
- outb(0x7f, tmport);
- tmport = base_io + 0x11;
- outb(0x20, tmport);
-
- tscam(shpnt);
- is870(p, base_io);
- tmport = base_io + 0x3a;
- outb((inb(tmport) & 0xef), tmport);
- tmport++;
- outb((inb(tmport) | 0x20), tmport);
- if (atpdev->chip_ver == 4)
- shpnt->max_id = 16;
- else
- shpnt->max_id = 8;
- shpnt->this_id = host_id;
- shpnt->unique_id = base_io;
- shpnt->io_port = base_io;
- shpnt->n_io_port = 0x40; /* Number of bytes of I/O space used */
- shpnt->irq = pdev->irq;
- }
- spin_unlock_irqrestore(shpnt->host_lock, flags);
- if(ent->device==ATP885_DEVID) {
- if(!request_region(base_io, 0xff, "atp870u")) /* Register the IO ports that we use */
- goto request_io_fail;
- } else if((ent->device==ATP880_DEVID1)||(ent->device==ATP880_DEVID2)) {
- if(!request_region(base_io, 0x60, "atp870u")) /* Register the IO ports that we use */
- goto request_io_fail;
- } else {
- if(!request_region(base_io, 0x40, "atp870u")) /* Register the IO ports that we use */
- goto request_io_fail;
- }
- count++;
- if (scsi_add_host(shpnt, &pdev->dev))
- goto scsi_add_fail;
- scsi_scan_host(shpnt);
-#ifdef ED_DBGP
- printk("atp870u_prob : exit\n");
-#endif
- return 0;
+ err = pci_request_regions(pdev, "atp870u");
+ if (err)
+ goto disable_device;
+ pci_set_master(pdev);
+
+ err = -ENOMEM;
+ shpnt = scsi_host_alloc(&atp870u_template, sizeof(struct atp_unit));
+ if (!shpnt)
+ goto release_region;
+
+ atpdev = shost_priv(shpnt);
+
+ atpdev->host = shpnt;
+ atpdev->pdev = pdev;
+ pci_set_drvdata(pdev, atpdev);
+
+ shpnt->io_port = pci_resource_start(pdev, 0);
+ shpnt->io_port &= 0xfffffff8;
+ shpnt->n_io_port = pci_resource_len(pdev, 0);
+ atpdev->baseport = shpnt->io_port;
+ shpnt->unique_id = shpnt->io_port;
+ shpnt->irq = pdev->irq;
+
+ err = atp870u_init_tables(shpnt);
+ if (err) {
+ dev_err(&pdev->dev, "Unable to allocate tables for Acard controller\n");
+ goto unregister;
+ }
-scsi_add_fail:
- printk("atp870u_prob:scsi_add_fail\n");
- if(ent->device==ATP885_DEVID) {
- release_region(base_io, 0xff);
- } else if((ent->device==ATP880_DEVID1)||(ent->device==ATP880_DEVID2)) {
- release_region(base_io, 0x60);
- } else {
- release_region(base_io, 0x40);
+ if (is880(atpdev))
+ atp880_init(shpnt);
+ else if (is885(atpdev))
+ atp885_init(shpnt);
+ else
+ atp870_init(shpnt);
+
+ err = request_irq(shpnt->irq, atp870u_intr_handle, IRQF_SHARED, "atp870u", shpnt);
+ if (err) {
+ dev_err(&pdev->dev, "Unable to allocate IRQ %d.\n", shpnt->irq);
+ goto free_tables;
}
-request_io_fail:
- printk("atp870u_prob:request_io_fail\n");
- free_irq(pdev->irq, shpnt);
+
+ err = scsi_add_host(shpnt, &pdev->dev);
+ if (err)
+ goto scsi_add_fail;
+ scsi_scan_host(shpnt);
+
+ return 0;
+
+scsi_add_fail:
+ free_irq(shpnt->irq, shpnt);
free_tables:
- printk("atp870u_prob:free_table\n");
atp870u_free_tables(shpnt);
unregister:
- printk("atp870u_prob:unregister\n");
scsi_host_put(shpnt);
- return -1;
-err_eio:
- kfree(atpdev);
- return -EIO;
-err_nomem:
- kfree(atpdev);
- return -ENOMEM;
+release_region:
+ pci_release_regions(pdev);
+disable_device:
+ pci_disable_device(pdev);
+fail:
+ return err;
}
/* The abort command does not leave the device in a clean state where
@@ -3055,7 +1586,6 @@ static int atp870u_abort(struct scsi_cmnd * SCpnt)
{
unsigned char j, k, c;
struct scsi_cmnd *workrequ;
- unsigned int tmport;
struct atp_unit *dev;
struct Scsi_Host *host;
host = SCpnt->device->host;
@@ -3065,18 +1595,13 @@ static int atp870u_abort(struct scsi_cmnd * SCpnt)
printk(" atp870u: abort Channel = %x \n", c);
printk("working=%x last_cmd=%x ", dev->working[c], dev->last_cmd[c]);
printk(" quhdu=%x quendu=%x ", dev->quhd[c], dev->quend[c]);
- tmport = dev->ioport[c];
for (j = 0; j < 0x18; j++) {
- printk(" r%2x=%2x", j, inb(tmport++));
+ printk(" r%2x=%2x", j, atp_readb_io(dev, c, j));
}
- tmport += 0x04;
- printk(" r1c=%2x", inb(tmport));
- tmport += 0x03;
- printk(" r1f=%2x in_snd=%2x ", inb(tmport), dev->in_snd[c]);
- tmport= dev->pciport[c];
- printk(" d00=%2x", inb(tmport));
- tmport += 0x02;
- printk(" d02=%2x", inb(tmport));
+ printk(" r1c=%2x", atp_readb_io(dev, c, 0x1c));
+ printk(" r1f=%2x in_snd=%2x ", atp_readb_io(dev, c, 0x1f), dev->in_snd[c]);
+ printk(" d00=%2x", atp_readb_pci(dev, c, 0x00));
+ printk(" d02=%2x", atp_readb_pci(dev, c, 0x02));
for(j=0;j<16;j++) {
if (dev->id[c][j].curr_req != NULL) {
workrequ = dev->id[c][j].curr_req;
@@ -3136,12 +1661,10 @@ static void atp870u_remove (struct pci_dev *pdev)
scsi_remove_host(pshost);
- printk(KERN_INFO "free_irq : %d\n",pshost->irq);
free_irq(pshost->irq, pshost);
- release_region(pshost->io_port, pshost->n_io_port);
- printk(KERN_INFO "atp870u_free_tables : %p\n",pshost);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
atp870u_free_tables(pshost);
- printk(KERN_INFO "scsi_host_put : %p\n",pshost);
scsi_host_put(pshost);
}
MODULE_LICENSE("GPL");
@@ -3185,52 +1708,26 @@ static struct pci_driver atp870u_driver = {
.remove = atp870u_remove,
};
-static int __init atp870u_init(void)
-{
-#ifdef ED_DBGP
- printk("atp870u_init: Entry\n");
-#endif
- return pci_register_driver(&atp870u_driver);
-}
-
-static void __exit atp870u_exit(void)
-{
-#ifdef ED_DBGP
- printk("atp870u_exit: Entry\n");
-#endif
- pci_unregister_driver(&atp870u_driver);
-}
-
-static void tscam_885(void)
-{
- unsigned char i;
-
- for (i = 0; i < 0x2; i++) {
- mdelay(300);
- }
- return;
-}
-
-
+module_pci_driver(atp870u_driver);
-static void is885(struct atp_unit *dev, unsigned int wkport,unsigned char c)
+static void atp_is(struct atp_unit *dev, unsigned char c, bool wide_chip, unsigned char lvdmode)
{
- unsigned int tmport;
- unsigned char i, j, k, rmb, n, lvdmode;
+ unsigned char i, j, k, rmb, n;
unsigned short int m;
static unsigned char mbuf[512];
- static unsigned char satn[9] = {0, 0, 0, 0, 0, 0, 0, 6, 6};
- static unsigned char inqd[9] = {0x12, 0, 0, 0, 0x24, 0, 0, 0x24, 6};
- static unsigned char synn[6] = {0x80, 1, 3, 1, 0x19, 0x0e};
- unsigned char synu[6] = {0x80, 1, 3, 1, 0x0a, 0x0e};
- static unsigned char synw[6] = {0x80, 1, 3, 1, 0x19, 0x0e};
- unsigned char synuw[6] = {0x80, 1, 3, 1, 0x0a, 0x0e};
- static unsigned char wide[6] = {0x80, 1, 2, 3, 1, 0};
- static unsigned char u3[9] = { 0x80,1,6,4,0x09,00,0x0e,0x01,0x02 };
-
- lvdmode=inb(wkport + 0x1b) >> 7;
+ static unsigned char satn[9] = { 0, 0, 0, 0, 0, 0, 0, 6, 6 };
+ static unsigned char inqd[9] = { 0x12, 0, 0, 0, 0x24, 0, 0, 0x24, 6 };
+ static unsigned char synn[6] = { 0x80, 1, 3, 1, 0x19, 0x0e };
+ unsigned char synu[6] = { 0x80, 1, 3, 1, 0x0a, 0x0e };
+ static unsigned char synw[6] = { 0x80, 1, 3, 1, 0x19, 0x0e };
+ static unsigned char synw_870[6] = { 0x80, 1, 3, 1, 0x0c, 0x07 };
+ unsigned char synuw[6] = { 0x80, 1, 3, 1, 0x0a, 0x0e };
+ static unsigned char wide[6] = { 0x80, 1, 2, 3, 1, 0 };
+ static unsigned char u3[9] = { 0x80, 1, 6, 4, 0x09, 00, 0x0e, 0x01, 0x02 };
for (i = 0; i < 16; i++) {
+ if (!wide_chip && (i > 7))
+ break;
m = 1;
m = m << i;
if ((m & dev->active_id[c]) != 0) {
@@ -3240,192 +1737,172 @@ static void is885(struct atp_unit *dev, unsigned int wkport,unsigned char c)
printk(KERN_INFO " ID: %2d Host Adapter\n", dev->host_id[c]);
continue;
}
- tmport = wkport + 0x1b;
- outb(0x01, tmport);
- tmport = wkport + 0x01;
- outb(0x08, tmport++);
- outb(0x7f, tmport++);
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[c][i].devsp, tmport++);
-
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
+ atp_writeb_io(dev, c, 0x1b, wide_chip ? 0x01 : 0x00);
+ atp_writeb_io(dev, c, 1, 0x08);
+ atp_writeb_io(dev, c, 2, 0x7f);
+ atp_writeb_io(dev, c, 3, satn[0]);
+ atp_writeb_io(dev, c, 4, satn[1]);
+ atp_writeb_io(dev, c, 5, satn[2]);
+ atp_writeb_io(dev, c, 6, satn[3]);
+ atp_writeb_io(dev, c, 7, satn[4]);
+ atp_writeb_io(dev, c, 8, satn[5]);
+ atp_writeb_io(dev, c, 0x0f, 0);
+ atp_writeb_io(dev, c, 0x11, dev->id[c][i].devsp);
+ atp_writeb_io(dev, c, 0x12, 0);
+ atp_writeb_io(dev, c, 0x13, satn[6]);
+ atp_writeb_io(dev, c, 0x14, satn[7]);
j = i;
if ((j & 0x08) != 0) {
j = (j & 0x07) | 0x40;
}
- outb(j, tmport);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
+ atp_writeb_io(dev, c, 0x15, j);
+ atp_writeb_io(dev, c, 0x18, satn[8]);
- while ((inb(tmport) & 0x80) == 0x00)
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00)
cpu_relax();
- tmport -= 0x08;
- if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) {
+
+ if (atp_readb_io(dev, c, 0x17) != 0x11 && atp_readb_io(dev, c, 0x17) != 0x8e)
continue;
- }
- while (inb(tmport) != 0x8e)
+
+ while (atp_readb_io(dev, c, 0x17) != 0x8e)
cpu_relax();
+
dev->active_id[c] |= m;
- tmport = wkport + 0x10;
- outb(0x30, tmport);
- tmport = wkport + 0x14;
- outb(0x00, tmport);
+ atp_writeb_io(dev, c, 0x10, 0x30);
+ if (is885(dev) || is880(dev))
+ atp_writeb_io(dev, c, 0x14, 0x00);
+ else /* result of is870() merge - is this a bug? */
+ atp_writeb_io(dev, c, 0x04, 0x00);
phase_cmd:
- tmport = wkport + 0x18;
- outb(0x08, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00)
+ atp_writeb_io(dev, c, 0x18, 0x08);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00)
cpu_relax();
- tmport -= 0x08;
- j = inb(tmport);
+
+ j = atp_readb_io(dev, c, 0x17);
if (j != 0x16) {
- tmport = wkport + 0x10;
- outb(0x41, tmport);
+ atp_writeb_io(dev, c, 0x10, 0x41);
goto phase_cmd;
}
sel_ok:
- tmport = wkport + 0x03;
- outb(inqd[0], tmport++);
- outb(inqd[1], tmport++);
- outb(inqd[2], tmport++);
- outb(inqd[3], tmport++);
- outb(inqd[4], tmport++);
- outb(inqd[5], tmport);
- tmport += 0x07;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[c][i].devsp, tmport++);
- outb(0, tmport++);
- outb(inqd[6], tmport++);
- outb(inqd[7], tmport++);
- tmport += 0x03;
- outb(inqd[8], tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00)
+ atp_writeb_io(dev, c, 3, inqd[0]);
+ atp_writeb_io(dev, c, 4, inqd[1]);
+ atp_writeb_io(dev, c, 5, inqd[2]);
+ atp_writeb_io(dev, c, 6, inqd[3]);
+ atp_writeb_io(dev, c, 7, inqd[4]);
+ atp_writeb_io(dev, c, 8, inqd[5]);
+ atp_writeb_io(dev, c, 0x0f, 0);
+ atp_writeb_io(dev, c, 0x11, dev->id[c][i].devsp);
+ atp_writeb_io(dev, c, 0x12, 0);
+ atp_writeb_io(dev, c, 0x13, inqd[6]);
+ atp_writeb_io(dev, c, 0x14, inqd[7]);
+ atp_writeb_io(dev, c, 0x18, inqd[8]);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00)
cpu_relax();
- tmport -= 0x08;
- if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) {
+
+ if (atp_readb_io(dev, c, 0x17) != 0x11 && atp_readb_io(dev, c, 0x17) != 0x8e)
continue;
- }
- while (inb(tmport) != 0x8e)
+
+ while (atp_readb_io(dev, c, 0x17) != 0x8e)
cpu_relax();
- tmport = wkport + 0x1b;
- outb(0x00, tmport);
- tmport = wkport + 0x18;
- outb(0x08, tmport);
- tmport += 0x07;
+
+ if (wide_chip)
+ atp_writeb_io(dev, c, 0x1b, 0x00);
+
+ atp_writeb_io(dev, c, 0x18, 0x08);
j = 0;
rd_inq_data:
- k = inb(tmport);
+ k = atp_readb_io(dev, c, 0x1f);
if ((k & 0x01) != 0) {
- tmport -= 0x06;
- mbuf[j++] = inb(tmport);
- tmport += 0x06;
+ mbuf[j++] = atp_readb_io(dev, c, 0x19);
goto rd_inq_data;
}
if ((k & 0x80) == 0) {
goto rd_inq_data;
}
- tmport -= 0x08;
- j = inb(tmport);
+ j = atp_readb_io(dev, c, 0x17);
if (j == 0x16) {
goto inq_ok;
}
- tmport = wkport + 0x10;
- outb(0x46, tmport);
- tmport += 0x02;
- outb(0, tmport++);
- outb(0, tmport++);
- outb(0, tmport++);
- tmport += 0x03;
- outb(0x08, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00)
+ atp_writeb_io(dev, c, 0x10, 0x46);
+ atp_writeb_io(dev, c, 0x12, 0);
+ atp_writeb_io(dev, c, 0x13, 0);
+ atp_writeb_io(dev, c, 0x14, 0);
+ atp_writeb_io(dev, c, 0x18, 0x08);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00)
cpu_relax();
- tmport -= 0x08;
- if (inb(tmport) != 0x16) {
+
+ if (atp_readb_io(dev, c, 0x17) != 0x16)
goto sel_ok;
- }
+
inq_ok:
mbuf[36] = 0;
- printk( KERN_INFO" ID: %2d %s\n", i, &mbuf[8]);
+ printk(KERN_INFO " ID: %2d %s\n", i, &mbuf[8]);
dev->id[c][i].devtype = mbuf[0];
rmb = mbuf[1];
n = mbuf[7];
+ if (!wide_chip)
+ goto not_wide;
if ((mbuf[7] & 0x60) == 0) {
goto not_wide;
}
- if ((i < 8) && ((dev->global_map[c] & 0x20) == 0)) {
- goto not_wide;
+ if (is885(dev) || is880(dev)) {
+ if ((i < 8) && ((dev->global_map[c] & 0x20) == 0))
+ goto not_wide;
+ } else { /* result of is870() merge - is this a bug? */
+ if ((dev->global_map[c] & 0x20) == 0)
+ goto not_wide;
}
if (lvdmode == 0) {
- goto chg_wide;
- }
- if (dev->sp[c][i] != 0x04) { // force u2
- goto chg_wide;
- }
-
- tmport = wkport + 0x1b;
- outb(0x01, tmport);
- tmport = wkport + 0x03;
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[c][i].devsp, tmport++);
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
+ goto chg_wide;
+ }
+ if (dev->sp[c][i] != 0x04) // force u2
+ {
+ goto chg_wide;
+ }
+
+ atp_writeb_io(dev, c, 0x1b, 0x01);
+ atp_writeb_io(dev, c, 3, satn[0]);
+ atp_writeb_io(dev, c, 4, satn[1]);
+ atp_writeb_io(dev, c, 5, satn[2]);
+ atp_writeb_io(dev, c, 6, satn[3]);
+ atp_writeb_io(dev, c, 7, satn[4]);
+ atp_writeb_io(dev, c, 8, satn[5]);
+ atp_writeb_io(dev, c, 0x0f, 0);
+ atp_writeb_io(dev, c, 0x11, dev->id[c][i].devsp);
+ atp_writeb_io(dev, c, 0x12, 0);
+ atp_writeb_io(dev, c, 0x13, satn[6]);
+ atp_writeb_io(dev, c, 0x14, satn[7]);
+ atp_writeb_io(dev, c, 0x18, satn[8]);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00)
cpu_relax();
- tmport -= 0x08;
- if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) {
+
+ if (atp_readb_io(dev, c, 0x17) != 0x11 && atp_readb_io(dev, c, 0x17) != 0x8e)
continue;
- }
- while (inb(tmport) != 0x8e)
+
+ while (atp_readb_io(dev, c, 0x17) != 0x8e)
cpu_relax();
+
try_u3:
j = 0;
- tmport = wkport + 0x14;
- outb(0x09, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- outb(u3[j++], tmport);
- tmport += 0x06;
- }
+ atp_writeb_io(dev, c, 0x14, 0x09);
+ atp_writeb_io(dev, c, 0x18, 0x20);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0) {
+ if ((atp_readb_io(dev, c, 0x1f) & 0x01) != 0)
+ atp_writeb_io(dev, c, 0x19, u3[j++]);
cpu_relax();
}
- tmport -= 0x08;
- while ((inb(tmport) & 0x80) == 0x00)
+
+ while ((atp_readb_io(dev, c, 0x17) & 0x80) == 0x00)
cpu_relax();
- j = inb(tmport) & 0x0f;
+
+ j = atp_readb_io(dev, c, 0x17) & 0x0f;
if (j == 0x0f) {
goto u3p_in;
}
@@ -3437,19 +1914,13 @@ try_u3:
}
continue;
u3p_out:
- tmport = wkport + 0x18;
- outb(0x20, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- outb(0, tmport);
- tmport += 0x06;
- }
+ atp_writeb_io(dev, c, 0x18, 0x20);
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0) {
+ if ((atp_readb_io(dev, c, 0x1f) & 0x01) != 0)
+ atp_writeb_io(dev, c, 0x19, 0);
cpu_relax();
}
- tmport -= 0x08;
- j = inb(tmport) & 0x0f;
+ j = atp_readb_io(dev, c, 0x17) & 0x0f;
if (j == 0x0f) {
goto u3p_in;
}
@@ -3461,25 +1932,19 @@ u3p_out:
}
continue;
u3p_in:
- tmport = wkport + 0x14;
- outb(0x09, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
+ atp_writeb_io(dev, c, 0x14, 0x09);
+ atp_writeb_io(dev, c, 0x18, 0x20);
k = 0;
u3p_in1:
- j = inb(tmport);
+ j = atp_readb_io(dev, c, 0x1f);
if ((j & 0x01) != 0) {
- tmport -= 0x06;
- mbuf[k++] = inb(tmport);
- tmport += 0x06;
+ mbuf[k++] = atp_readb_io(dev, c, 0x19);
goto u3p_in1;
}
if ((j & 0x80) == 0x00) {
goto u3p_in1;
}
- tmport -= 0x08;
- j = inb(tmport) & 0x0f;
+ j = atp_readb_io(dev, c, 0x17) & 0x0f;
if (j == 0x0f) {
goto u3p_in;
}
@@ -3491,16 +1956,13 @@ u3p_in1:
}
continue;
u3p_cmd:
- tmport = wkport + 0x10;
- outb(0x30, tmport);
- tmport = wkport + 0x14;
- outb(0x00, tmport);
- tmport += 0x04;
- outb(0x08, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00);
- tmport -= 0x08;
- j = inb(tmport);
+ atp_writeb_io(dev, c, 0x10, 0x30);
+ atp_writeb_io(dev, c, 0x14, 0x00);
+ atp_writeb_io(dev, c, 0x18, 0x08);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00);
+
+ j = atp_readb_io(dev, c, 0x17);
if (j != 0x16) {
if (j == 0x4e) {
goto u3p_out;
@@ -3527,54 +1989,44 @@ u3p_cmd:
continue;
}
chg_wide:
- tmport = wkport + 0x1b;
- outb(0x01, tmport);
- tmport = wkport + 0x03;
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[c][i].devsp, tmport++);
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
+ atp_writeb_io(dev, c, 0x1b, 0x01);
+ atp_writeb_io(dev, c, 3, satn[0]);
+ atp_writeb_io(dev, c, 4, satn[1]);
+ atp_writeb_io(dev, c, 5, satn[2]);
+ atp_writeb_io(dev, c, 6, satn[3]);
+ atp_writeb_io(dev, c, 7, satn[4]);
+ atp_writeb_io(dev, c, 8, satn[5]);
+ atp_writeb_io(dev, c, 0x0f, 0);
+ atp_writeb_io(dev, c, 0x11, dev->id[c][i].devsp);
+ atp_writeb_io(dev, c, 0x12, 0);
+ atp_writeb_io(dev, c, 0x13, satn[6]);
+ atp_writeb_io(dev, c, 0x14, satn[7]);
+ atp_writeb_io(dev, c, 0x18, satn[8]);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00)
cpu_relax();
- tmport -= 0x08;
- if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) {
+
+ if (atp_readb_io(dev, c, 0x17) != 0x11 && atp_readb_io(dev, c, 0x17) != 0x8e)
continue;
- }
- while (inb(tmport) != 0x8e)
+
+ while (atp_readb_io(dev, c, 0x17) != 0x8e)
cpu_relax();
+
try_wide:
j = 0;
- tmport = wkport + 0x14;
- outb(0x05, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- outb(wide[j++], tmport);
- tmport += 0x06;
- }
+ atp_writeb_io(dev, c, 0x14, 0x05);
+ atp_writeb_io(dev, c, 0x18, 0x20);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0) {
+ if ((atp_readb_io(dev, c, 0x1f) & 0x01) != 0)
+ atp_writeb_io(dev, c, 0x19, wide[j++]);
cpu_relax();
}
- tmport -= 0x08;
- while ((inb(tmport) & 0x80) == 0x00)
+
+ while ((atp_readb_io(dev, c, 0x17) & 0x80) == 0x00)
cpu_relax();
- j = inb(tmport) & 0x0f;
+
+ j = atp_readb_io(dev, c, 0x17) & 0x0f;
if (j == 0x0f) {
goto widep_in;
}
@@ -3586,19 +2038,13 @@ try_wide:
}
continue;
widep_out:
- tmport = wkport + 0x18;
- outb(0x20, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
- outb(0, tmport);
- tmport += 0x06;
- }
+ atp_writeb_io(dev, c, 0x18, 0x20);
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0) {
+ if ((atp_readb_io(dev, c, 0x1f) & 0x01) != 0)
+ atp_writeb_io(dev, c, 0x19, 0);
cpu_relax();
}
- tmport -= 0x08;
- j = inb(tmport) & 0x0f;
+ j = atp_readb_io(dev, c, 0x17) & 0x0f;
if (j == 0x0f) {
goto widep_in;
}
@@ -3610,25 +2056,19 @@ widep_out:
}
continue;
widep_in:
- tmport = wkport + 0x14;
- outb(0xff, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
+ atp_writeb_io(dev, c, 0x14, 0xff);
+ atp_writeb_io(dev, c, 0x18, 0x20);
k = 0;
widep_in1:
- j = inb(tmport);
+ j = atp_readb_io(dev, c, 0x1f);
if ((j & 0x01) != 0) {
- tmport -= 0x06;
- mbuf[k++] = inb(tmport);
- tmport += 0x06;
+ mbuf[k++] = atp_readb_io(dev, c, 0x19);
goto widep_in1;
}
if ((j & 0x80) == 0x00) {
goto widep_in1;
}
- tmport -= 0x08;
- j = inb(tmport) & 0x0f;
+ j = atp_readb_io(dev, c, 0x17) & 0x0f;
if (j == 0x0f) {
goto widep_in;
}
@@ -3640,17 +2080,14 @@ widep_in1:
}
continue;
widep_cmd:
- tmport = wkport + 0x10;
- outb(0x30, tmport);
- tmport = wkport + 0x14;
- outb(0x00, tmport);
- tmport += 0x04;
- outb(0x08, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00)
+ atp_writeb_io(dev, c, 0x10, 0x30);
+ atp_writeb_io(dev, c, 0x14, 0x00);
+ atp_writeb_io(dev, c, 0x18, 0x08);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00)
cpu_relax();
- tmport -= 0x08;
- j = inb(tmport);
+
+ j = atp_readb_io(dev, c, 0x17);
if (j != 0x16) {
if (j == 0x4e) {
goto widep_out;
@@ -3673,88 +2110,81 @@ widep_cmd:
m = m << i;
dev->wide_id[c] |= m;
not_wide:
- if ((dev->id[c][i].devtype == 0x00) || (dev->id[c][i].devtype == 0x07) ||
- ((dev->id[c][i].devtype == 0x05) && ((n & 0x10) != 0))) {
+ if ((dev->id[c][i].devtype == 0x00) || (dev->id[c][i].devtype == 0x07) || ((dev->id[c][i].devtype == 0x05) && ((n & 0x10) != 0))) {
m = 1;
m = m << i;
if ((dev->async[c] & m) != 0) {
- goto set_sync;
+ goto set_sync;
}
}
continue;
set_sync:
- if (dev->sp[c][i] == 0x02) {
- synu[4]=0x0c;
- synuw[4]=0x0c;
+ if ((!is885(dev) && !is880(dev)) || (dev->sp[c][i] == 0x02)) {
+ synu[4] = 0x0c;
+ synuw[4] = 0x0c;
} else {
- if (dev->sp[c][i] >= 0x03) {
- synu[4]=0x0a;
- synuw[4]=0x0a;
- }
+ if (dev->sp[c][i] >= 0x03) {
+ synu[4] = 0x0a;
+ synuw[4] = 0x0a;
+ }
}
- tmport = wkport + 0x1b;
j = 0;
if ((m & dev->wide_id[c]) != 0) {
j |= 0x01;
}
- outb(j, tmport);
- tmport = wkport + 0x03;
- outb(satn[0], tmport++);
- outb(satn[1], tmport++);
- outb(satn[2], tmport++);
- outb(satn[3], tmport++);
- outb(satn[4], tmport++);
- outb(satn[5], tmport++);
- tmport += 0x06;
- outb(0, tmport);
- tmport += 0x02;
- outb(dev->id[c][i].devsp, tmport++);
- outb(0, tmport++);
- outb(satn[6], tmport++);
- outb(satn[7], tmport++);
- tmport += 0x03;
- outb(satn[8], tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0x00)
+ atp_writeb_io(dev, c, 0x1b, j);
+ atp_writeb_io(dev, c, 3, satn[0]);
+ atp_writeb_io(dev, c, 4, satn[1]);
+ atp_writeb_io(dev, c, 5, satn[2]);
+ atp_writeb_io(dev, c, 6, satn[3]);
+ atp_writeb_io(dev, c, 7, satn[4]);
+ atp_writeb_io(dev, c, 8, satn[5]);
+ atp_writeb_io(dev, c, 0x0f, 0);
+ atp_writeb_io(dev, c, 0x11, dev->id[c][i].devsp);
+ atp_writeb_io(dev, c, 0x12, 0);
+ atp_writeb_io(dev, c, 0x13, satn[6]);
+ atp_writeb_io(dev, c, 0x14, satn[7]);
+ atp_writeb_io(dev, c, 0x18, satn[8]);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00)
cpu_relax();
- tmport -= 0x08;
- if ((inb(tmport) != 0x11) && (inb(tmport) != 0x8e)) {
+
+ if (atp_readb_io(dev, c, 0x17) != 0x11 && atp_readb_io(dev, c, 0x17) != 0x8e)
continue;
- }
- while (inb(tmport) != 0x8e)
+
+ while (atp_readb_io(dev, c, 0x17) != 0x8e)
cpu_relax();
+
try_sync:
j = 0;
- tmport = wkport + 0x14;
- outb(0x06, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
-
- while ((inb(tmport) & 0x80) == 0) {
- if ((inb(tmport) & 0x01) != 0) {
- tmport -= 0x06;
+ atp_writeb_io(dev, c, 0x14, 0x06);
+ atp_writeb_io(dev, c, 0x18, 0x20);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0) {
+ if ((atp_readb_io(dev, c, 0x1f) & 0x01) != 0) {
if ((m & dev->wide_id[c]) != 0) {
- if ((m & dev->ultra_map[c]) != 0) {
- outb(synuw[j++], tmport);
- } else {
- outb(synw[j++], tmport);
- }
+ if (is885(dev) || is880(dev)) {
+ if ((m & dev->ultra_map[c]) != 0) {
+ atp_writeb_io(dev, c, 0x19, synuw[j++]);
+ } else {
+ atp_writeb_io(dev, c, 0x19, synw[j++]);
+ }
+ } else
+ atp_writeb_io(dev, c, 0x19, synw_870[j++]);
} else {
if ((m & dev->ultra_map[c]) != 0) {
- outb(synu[j++], tmport);
+ atp_writeb_io(dev, c, 0x19, synu[j++]);
} else {
- outb(synn[j++], tmport);
+ atp_writeb_io(dev, c, 0x19, synn[j++]);
}
}
- tmport += 0x06;
}
}
- tmport -= 0x08;
- while ((inb(tmport) & 0x80) == 0x00)
+
+ while ((atp_readb_io(dev, c, 0x17) & 0x80) == 0x00)
cpu_relax();
- j = inb(tmport) & 0x0f;
+
+ j = atp_readb_io(dev, c, 0x17) & 0x0f;
if (j == 0x0f) {
goto phase_ins;
}
@@ -3766,19 +2196,13 @@ try_sync:
}
continue;
phase_outs:
- tmport = wkport + 0x18;
- outb(0x20, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00) {
- if ((inb(tmport) & 0x01) != 0x00) {
- tmport -= 0x06;
- outb(0x00, tmport);
- tmport += 0x06;
- }
+ atp_writeb_io(dev, c, 0x18, 0x20);
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00) {
+ if ((atp_readb_io(dev, c, 0x1f) & 0x01) != 0x00)
+ atp_writeb_io(dev, c, 0x19, 0x00);
cpu_relax();
}
- tmport -= 0x08;
- j = inb(tmport);
+ j = atp_readb_io(dev, c, 0x17);
if (j == 0x85) {
goto tar_dcons;
}
@@ -3794,26 +2218,25 @@ phase_outs:
}
continue;
phase_ins:
- tmport = wkport + 0x14;
- outb(0x06, tmport);
- tmport += 0x04;
- outb(0x20, tmport);
- tmport += 0x07;
+ if (is885(dev) || is880(dev))
+ atp_writeb_io(dev, c, 0x14, 0x06);
+ else
+ atp_writeb_io(dev, c, 0x14, 0xff);
+ atp_writeb_io(dev, c, 0x18, 0x20);
k = 0;
phase_ins1:
- j = inb(tmport);
+ j = atp_readb_io(dev, c, 0x1f);
if ((j & 0x01) != 0x00) {
- tmport -= 0x06;
- mbuf[k++] = inb(tmport);
- tmport += 0x06;
+ mbuf[k++] = atp_readb_io(dev, c, 0x19);
goto phase_ins1;
}
if ((j & 0x80) == 0x00) {
goto phase_ins1;
}
- tmport -= 0x08;
- while ((inb(tmport) & 0x80) == 0x00);
- j = inb(tmport);
+
+ while ((atp_readb_io(dev, c, 0x17) & 0x80) == 0x00);
+
+ j = atp_readb_io(dev, c, 0x17);
if (j == 0x85) {
goto tar_dcons;
}
@@ -3829,18 +2252,15 @@ phase_ins1:
}
continue;
phase_cmds:
- tmport = wkport + 0x10;
- outb(0x30, tmport);
+ atp_writeb_io(dev, c, 0x10, 0x30);
tar_dcons:
- tmport = wkport + 0x14;
- outb(0x00, tmport);
- tmport += 0x04;
- outb(0x08, tmport);
- tmport += 0x07;
- while ((inb(tmport) & 0x80) == 0x00)
+ atp_writeb_io(dev, c, 0x14, 0x00);
+ atp_writeb_io(dev, c, 0x18, 0x08);
+
+ while ((atp_readb_io(dev, c, 0x1f) & 0x80) == 0x00)
cpu_relax();
- tmport -= 0x08;
- j = inb(tmport);
+
+ j = atp_readb_io(dev, c, 0x17);
if (j != 0x16) {
continue;
}
@@ -3856,14 +2276,21 @@ tar_dcons:
if (mbuf[3] > 0x64) {
continue;
}
- if (mbuf[4] > 0x0e) {
- mbuf[4] = 0x0e;
+ if (is885(dev) || is880(dev)) {
+ if (mbuf[4] > 0x0e) {
+ mbuf[4] = 0x0e;
+ }
+ } else {
+ if (mbuf[4] > 0x0c) {
+ mbuf[4] = 0x0c;
+ }
}
dev->id[c][i].devsp = mbuf[4];
- if (mbuf[3] < 0x0c){
- j = 0xb0;
- goto set_syn_ok;
- }
+ if (is885(dev) || is880(dev))
+ if (mbuf[3] < 0x0c) {
+ j = 0xb0;
+ goto set_syn_ok;
+ }
if ((mbuf[3] < 0x0d) && (rmb == 0)) {
j = 0xa0;
goto set_syn_ok;
@@ -3881,16 +2308,10 @@ tar_dcons:
goto set_syn_ok;
}
j = 0x60;
- set_syn_ok:
+set_syn_ok:
dev->id[c][i].devsp = (dev->id[c][i].devsp & 0x0f) | j;
-#ifdef ED_DBGP
+#ifdef ED_DBGP
printk("dev->id[%2d][%2d].devsp = %2x\n",c,i,dev->id[c][i].devsp);
#endif
}
- tmport = wkport + 0x16;
- outb(0x80, tmport);
}
-
-module_init(atp870u_init);
-module_exit(atp870u_exit);
-
diff --git a/drivers/scsi/atp870u.h b/drivers/scsi/atp870u.h
index 5cf62566ad42..9b839b1e895a 100644
--- a/drivers/scsi/atp870u.h
+++ b/drivers/scsi/atp870u.h
@@ -26,22 +26,18 @@ struct atp_unit
unsigned long baseport;
unsigned long ioport[2];
unsigned long pciport[2];
- unsigned long irq;
unsigned char last_cmd[2];
unsigned char in_snd[2];
unsigned char in_int[2];
unsigned char quhd[2];
unsigned char quend[2];
unsigned char global_map[2];
- unsigned char chip_ver;
- unsigned char scam_on;
unsigned char host_id[2];
unsigned int working[2];
unsigned short wide_id[2];
unsigned short active_id[2];
unsigned short ultra_map[2];
unsigned short async[2];
- unsigned short dev_id;
unsigned char sp[2][16];
unsigned char r1f[2][16];
struct scsi_cmnd *quereq[2][qcnt];
diff --git a/drivers/scsi/bfa/bfa.h b/drivers/scsi/bfa/bfa.h
index 4ad7e368bbc2..0e119d838e1b 100644
--- a/drivers/scsi/bfa/bfa.h
+++ b/drivers/scsi/bfa/bfa.h
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_core.c b/drivers/scsi/bfa/bfa_core.c
index e3f67b097a5c..2ea0db4b62a7 100644
--- a/drivers/scsi/bfa/bfa_core.c
+++ b/drivers/scsi/bfa/bfa_core.c
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_cs.h b/drivers/scsi/bfa/bfa_cs.h
index 91a8aa394db5..da9cf655be26 100644
--- a/drivers/scsi/bfa/bfa_cs.h
+++ b/drivers/scsi/bfa/bfa_cs.h
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_defs.h b/drivers/scsi/bfa/bfa_defs.h
index 877b86dd2837..5dc3782d615b 100644
--- a/drivers/scsi/bfa/bfa_defs.h
+++ b/drivers/scsi/bfa/bfa_defs.h
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_defs_fcs.h b/drivers/scsi/bfa/bfa_defs_fcs.h
index 06f0a163ca35..5815a904574d 100644
--- a/drivers/scsi/bfa/bfa_defs_fcs.h
+++ b/drivers/scsi/bfa/bfa_defs_fcs.h
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_defs_svc.h b/drivers/scsi/bfa/bfa_defs_svc.h
index 638f441ffc38..e81707f938cb 100644
--- a/drivers/scsi/bfa/bfa_defs_svc.h
+++ b/drivers/scsi/bfa/bfa_defs_svc.h
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_fc.h b/drivers/scsi/bfa/bfa_fc.h
index 64069a0a3d0d..18b7304d6b0b 100644
--- a/drivers/scsi/bfa/bfa_fc.h
+++ b/drivers/scsi/bfa/bfa_fc.h
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_fcbuild.c b/drivers/scsi/bfa/bfa_fcbuild.c
index dce787f6cca2..b8dadc9cc993 100644
--- a/drivers/scsi/bfa/bfa_fcbuild.c
+++ b/drivers/scsi/bfa/bfa_fcbuild.c
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_fcbuild.h b/drivers/scsi/bfa/bfa_fcbuild.h
index 03c753d1e548..b109a8813401 100644
--- a/drivers/scsi/bfa/bfa_fcbuild.h
+++ b/drivers/scsi/bfa/bfa_fcbuild.h
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_fcpim.c b/drivers/scsi/bfa/bfa_fcpim.c
index d7385d1d9c5a..20982e7cdd81 100644
--- a/drivers/scsi/bfa/bfa_fcpim.c
+++ b/drivers/scsi/bfa/bfa_fcpim.c
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_fcpim.h b/drivers/scsi/bfa/bfa_fcpim.h
index e693af6e5930..e93921dec347 100644
--- a/drivers/scsi/bfa/bfa_fcpim.h
+++ b/drivers/scsi/bfa/bfa_fcpim.h
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_fcs.c b/drivers/scsi/bfa/bfa_fcs.c
index 0f19455951ec..1e7e139d71ea 100644
--- a/drivers/scsi/bfa/bfa_fcs.c
+++ b/drivers/scsi/bfa/bfa_fcs.c
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_fcs.h b/drivers/scsi/bfa/bfa_fcs.h
index 42bcb970445a..06dc215ea050 100644
--- a/drivers/scsi/bfa/bfa_fcs.h
+++ b/drivers/scsi/bfa/bfa_fcs.h
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
@@ -633,7 +634,7 @@ void bfa_fcs_fcpim_uf_recv(struct bfa_fcs_itnim_s *itnim,
/*
* HBA Attribute Block : BFA internal representation. Note : Some variable
- * sizes have been trimmed to suit BFA For Ex : Model will be "Brocade". Based
+ * sizes have been trimmed to suit BFA For Ex : Model will be "QLogic ". Based
* on this the size has been reduced to 16 bytes from the standard's 64 bytes.
*/
struct bfa_fcs_fdmi_hba_attr_s {
diff --git a/drivers/scsi/bfa/bfa_fcs_fcpim.c b/drivers/scsi/bfa/bfa_fcs_fcpim.c
index 6dc7926a3edd..4f089d76afb1 100644
--- a/drivers/scsi/bfa/bfa_fcs_fcpim.c
+++ b/drivers/scsi/bfa/bfa_fcs_fcpim.c
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_fcs_lport.c b/drivers/scsi/bfa/bfa_fcs_lport.c
index ff75ef891755..7733ad5305d4 100644
--- a/drivers/scsi/bfa/bfa_fcs_lport.c
+++ b/drivers/scsi/bfa/bfa_fcs_lport.c
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
@@ -2653,7 +2654,7 @@ bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_lport_fdmi_s *fdmi,
strncpy(hba_attr->node_sym_name.symname,
port->port_cfg.node_sym_name.symname, BFA_SYMNAME_MAXLEN);
- strcpy(hba_attr->vendor_info, "BROCADE");
+ strcpy(hba_attr->vendor_info, "QLogic");
hba_attr->num_ports =
cpu_to_be32(bfa_ioc_get_nports(&port->fcs->bfa->ioc));
hba_attr->fabric_name = port->fabric->lps->pr_nwwn;
diff --git a/drivers/scsi/bfa/bfa_fcs_rport.c b/drivers/scsi/bfa/bfa_fcs_rport.c
index 2035b0d64351..de50349a39ce 100644
--- a/drivers/scsi/bfa/bfa_fcs_rport.c
+++ b/drivers/scsi/bfa/bfa_fcs_rport.c
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_hw_cb.c b/drivers/scsi/bfa/bfa_hw_cb.c
index ea24d4c6e67a..c4a0c0eb88a5 100644
--- a/drivers/scsi/bfa/bfa_hw_cb.c
+++ b/drivers/scsi/bfa/bfa_hw_cb.c
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_hw_ct.c b/drivers/scsi/bfa/bfa_hw_ct.c
index 637527f48b40..b0ff378dece2 100644
--- a/drivers/scsi/bfa/bfa_hw_ct.c
+++ b/drivers/scsi/bfa/bfa_hw_ct.c
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c
index 98f7e8cca52d..251e2ff8ff5f 100644
--- a/drivers/scsi/bfa/bfa_ioc.c
+++ b/drivers/scsi/bfa/bfa_ioc.c
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
@@ -2697,7 +2698,7 @@ bfa_ioc_reset_fwstate(struct bfa_ioc_s *ioc)
bfa_ioc_set_alt_ioc_fwstate(ioc, BFI_IOC_UNINIT);
}
-#define BFA_MFG_NAME "Brocade"
+#define BFA_MFG_NAME "QLogic"
void
bfa_ioc_get_adapter_attr(struct bfa_ioc_s *ioc,
struct bfa_adapter_attr_s *ad_attr)
diff --git a/drivers/scsi/bfa/bfa_ioc.h b/drivers/scsi/bfa/bfa_ioc.h
index a38aafa030b3..713745da44c6 100644
--- a/drivers/scsi/bfa/bfa_ioc.h
+++ b/drivers/scsi/bfa/bfa_ioc.h
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_ioc_cb.c b/drivers/scsi/bfa/bfa_ioc_cb.c
index 453c2f5b5561..f1b80da298c8 100644
--- a/drivers/scsi/bfa/bfa_ioc_cb.c
+++ b/drivers/scsi/bfa/bfa_ioc_cb.c
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_ioc_ct.c b/drivers/scsi/bfa/bfa_ioc_ct.c
index bd53150e4ee0..651a8fb93037 100644
--- a/drivers/scsi/bfa/bfa_ioc_ct.c
+++ b/drivers/scsi/bfa/bfa_ioc_ct.c
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_modules.h b/drivers/scsi/bfa/bfa_modules.h
index a14c784ff3fc..53135f21fa0e 100644
--- a/drivers/scsi/bfa/bfa_modules.h
+++ b/drivers/scsi/bfa/bfa_modules.h
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_plog.h b/drivers/scsi/bfa/bfa_plog.h
index 1c9baa68339b..da570c0b8275 100644
--- a/drivers/scsi/bfa/bfa_plog.h
+++ b/drivers/scsi/bfa/bfa_plog.h
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_port.c b/drivers/scsi/bfa/bfa_port.c
index 8ea7697deb9b..da1721e0d167 100644
--- a/drivers/scsi/bfa/bfa_port.c
+++ b/drivers/scsi/bfa/bfa_port.c
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_port.h b/drivers/scsi/bfa/bfa_port.h
index 2fcab6bc6280..26dc1bf14c85 100644
--- a/drivers/scsi/bfa/bfa_port.h
+++ b/drivers/scsi/bfa/bfa_port.h
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_svc.c b/drivers/scsi/bfa/bfa_svc.c
index 625225f31081..12de292175ef 100644
--- a/drivers/scsi/bfa/bfa_svc.c
+++ b/drivers/scsi/bfa/bfa_svc.c
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfa_svc.h b/drivers/scsi/bfa/bfa_svc.h
index ef07365991e7..ea2278bc78a8 100644
--- a/drivers/scsi/bfa/bfa_svc.h
+++ b/drivers/scsi/bfa/bfa_svc.h
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfad.c b/drivers/scsi/bfa/bfad.c
index cc3b9d3d6d40..9d253cb83ee7 100644
--- a/drivers/scsi/bfa/bfad.c
+++ b/drivers/scsi/bfa/bfad.c
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
@@ -130,13 +131,9 @@ MODULE_PARM_DESC(bfa_linkup_delay, "Link up delay, default=30 secs for "
"boot port. Otherwise 10 secs in RHEL4 & 0 for "
"[RHEL5, SLES10, ESX40] Range[>0]");
module_param(msix_disable_cb, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(msix_disable_cb, "Disable Message Signaled Interrupts "
- "for Brocade-415/425/815/825 cards, default=0, "
- " Range[false:0|true:1]");
+MODULE_PARM_DESC(msix_disable_cb, "Disable Message Signaled Interrupts for QLogic-415/425/815/825 cards, default=0 Range[false:0|true:1]");
module_param(msix_disable_ct, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(msix_disable_ct, "Disable Message Signaled Interrupts "
- "if possible for Brocade-1010/1020/804/1007/902/1741 "
- "cards, default=0, Range[false:0|true:1]");
+MODULE_PARM_DESC(msix_disable_ct, "Disable Message Signaled Interrupts if possible for QLogic-1010/1020/804/1007/902/1741 cards, default=0, Range[false:0|true:1]");
module_param(fdmi_enable, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(fdmi_enable, "Enables fdmi registration, default=1, "
"Range[false:0|true:1]");
@@ -838,8 +835,7 @@ bfad_drv_init(struct bfad_s *bfad)
printk(KERN_WARNING "bfad%d bfad_hal_mem_alloc failure\n",
bfad->inst_no);
printk(KERN_WARNING
- "Not enough memory to attach all Brocade HBA ports, %s",
- "System may need more memory.\n");
+ "Not enough memory to attach all QLogic BR-series HBA ports. System may need more memory.\n");
return BFA_STATUS_FAILED;
}
@@ -1710,7 +1706,7 @@ bfad_init(void)
{
int error = 0;
- printk(KERN_INFO "Brocade BFA FC/FCOE SCSI driver - version: %s\n",
+ pr_info("QLogic BR-series BFA FC/FCOE SCSI driver - version: %s\n",
BFAD_DRIVER_VERSION);
if (num_sgpgs > 0)
@@ -1817,6 +1813,6 @@ bfad_free_fwimg(void)
module_init(bfad_init);
module_exit(bfad_exit);
MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Brocade Fibre Channel HBA Driver" BFAD_PROTO_NAME);
-MODULE_AUTHOR("Brocade Communications Systems, Inc.");
+MODULE_DESCRIPTION("QLogic BR-series Fibre Channel HBA Driver" BFAD_PROTO_NAME);
+MODULE_AUTHOR("QLogic Corporation");
MODULE_VERSION(BFAD_DRIVER_VERSION);
diff --git a/drivers/scsi/bfa/bfad_attr.c b/drivers/scsi/bfa/bfad_attr.c
index 40be670a1cbc..13db3b7bc873 100644
--- a/drivers/scsi/bfa/bfad_attr.c
+++ b/drivers/scsi/bfa/bfad_attr.c
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
@@ -750,65 +751,65 @@ bfad_im_model_desc_show(struct device *dev, struct device_attribute *attr,
bfa_get_adapter_model(&bfad->bfa, model);
nports = bfa_get_nports(&bfad->bfa);
- if (!strcmp(model, "Brocade-425"))
+ if (!strcmp(model, "QLogic-425"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 4Gbps PCIe dual port FC HBA");
- else if (!strcmp(model, "Brocade-825"))
+ "QLogic BR-series 4Gbps PCIe dual port FC HBA");
+ else if (!strcmp(model, "QLogic-825"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 8Gbps PCIe dual port FC HBA");
- else if (!strcmp(model, "Brocade-42B"))
+ "QLogic BR-series 8Gbps PCIe dual port FC HBA");
+ else if (!strcmp(model, "QLogic-42B"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 4Gbps PCIe dual port FC HBA for HP");
- else if (!strcmp(model, "Brocade-82B"))
+ "QLogic BR-series 4Gbps PCIe dual port FC HBA for HP");
+ else if (!strcmp(model, "QLogic-82B"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 8Gbps PCIe dual port FC HBA for HP");
- else if (!strcmp(model, "Brocade-1010"))
+ "QLogic BR-series 8Gbps PCIe dual port FC HBA for HP");
+ else if (!strcmp(model, "QLogic-1010"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 10Gbps single port CNA");
- else if (!strcmp(model, "Brocade-1020"))
+ "QLogic BR-series 10Gbps single port CNA");
+ else if (!strcmp(model, "QLogic-1020"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 10Gbps dual port CNA");
- else if (!strcmp(model, "Brocade-1007"))
+ "QLogic BR-series 10Gbps dual port CNA");
+ else if (!strcmp(model, "QLogic-1007"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 10Gbps CNA for IBM Blade Center");
- else if (!strcmp(model, "Brocade-415"))
+ "QLogic BR-series 10Gbps CNA for IBM Blade Center");
+ else if (!strcmp(model, "QLogic-415"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 4Gbps PCIe single port FC HBA");
- else if (!strcmp(model, "Brocade-815"))
+ "QLogic BR-series 4Gbps PCIe single port FC HBA");
+ else if (!strcmp(model, "QLogic-815"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 8Gbps PCIe single port FC HBA");
- else if (!strcmp(model, "Brocade-41B"))
+ "QLogic BR-series 8Gbps PCIe single port FC HBA");
+ else if (!strcmp(model, "QLogic-41B"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 4Gbps PCIe single port FC HBA for HP");
- else if (!strcmp(model, "Brocade-81B"))
+ "QLogic BR-series 4Gbps PCIe single port FC HBA for HP");
+ else if (!strcmp(model, "QLogic-81B"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 8Gbps PCIe single port FC HBA for HP");
- else if (!strcmp(model, "Brocade-804"))
+ "QLogic BR-series 8Gbps PCIe single port FC HBA for HP");
+ else if (!strcmp(model, "QLogic-804"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 8Gbps FC HBA for HP Bladesystem C-class");
- else if (!strcmp(model, "Brocade-1741"))
+ "QLogic BR-series 8Gbps FC HBA for HP Bladesystem C-class");
+ else if (!strcmp(model, "QLogic-1741"))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 10Gbps CNA for Dell M-Series Blade Servers");
- else if (strstr(model, "Brocade-1860")) {
+ "QLogic BR-series 10Gbps CNA for Dell M-Series Blade Servers");
+ else if (strstr(model, "QLogic-1860")) {
if (nports == 1 && bfa_ioc_is_cna(&bfad->bfa.ioc))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 10Gbps single port CNA");
+ "QLogic BR-series 10Gbps single port CNA");
else if (nports == 1 && !bfa_ioc_is_cna(&bfad->bfa.ioc))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 16Gbps PCIe single port FC HBA");
+ "QLogic BR-series 16Gbps PCIe single port FC HBA");
else if (nports == 2 && bfa_ioc_is_cna(&bfad->bfa.ioc))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 10Gbps dual port CNA");
+ "QLogic BR-series 10Gbps dual port CNA");
else if (nports == 2 && !bfa_ioc_is_cna(&bfad->bfa.ioc))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 16Gbps PCIe dual port FC HBA");
- } else if (!strcmp(model, "Brocade-1867")) {
+ "QLogic BR-series 16Gbps PCIe dual port FC HBA");
+ } else if (!strcmp(model, "QLogic-1867")) {
if (nports == 1 && !bfa_ioc_is_cna(&bfad->bfa.ioc))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 16Gbps PCIe single port FC HBA for IBM");
+ "QLogic BR-series 16Gbps PCIe single port FC HBA for IBM");
else if (nports == 2 && !bfa_ioc_is_cna(&bfad->bfa.ioc))
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
- "Brocade 16Gbps PCIe dual port FC HBA for IBM");
+ "QLogic BR-series 16Gbps PCIe dual port FC HBA for IBM");
} else
snprintf(model_descr, BFA_ADAPTER_MODEL_DESCR_LEN,
"Invalid Model");
diff --git a/drivers/scsi/bfa/bfad_bsg.c b/drivers/scsi/bfa/bfad_bsg.c
index 023b9d42ad9a..d1ad0208dfe7 100644
--- a/drivers/scsi/bfa/bfad_bsg.c
+++ b/drivers/scsi/bfa/bfad_bsg.c
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfad_bsg.h b/drivers/scsi/bfa/bfad_bsg.h
index 90abef691585..917e140dfbcc 100644
--- a/drivers/scsi/bfa/bfad_bsg.h
+++ b/drivers/scsi/bfa/bfad_bsg.h
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfad_debugfs.c b/drivers/scsi/bfa/bfad_debugfs.c
index 74a307c0a240..8dcd8c70c7ee 100644
--- a/drivers/scsi/bfa/bfad_debugfs.c
+++ b/drivers/scsi/bfa/bfad_debugfs.c
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfad_drv.h b/drivers/scsi/bfa/bfad_drv.h
index 8b97877d42cf..f9e862093a25 100644
--- a/drivers/scsi/bfa/bfad_drv.h
+++ b/drivers/scsi/bfa/bfad_drv.h
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
@@ -57,7 +58,7 @@
#ifdef BFA_DRIVER_VERSION
#define BFAD_DRIVER_VERSION BFA_DRIVER_VERSION
#else
-#define BFAD_DRIVER_VERSION "3.2.23.0"
+#define BFAD_DRIVER_VERSION "3.2.25.0"
#endif
#define BFAD_PROTO_NAME FCPI_NAME
diff --git a/drivers/scsi/bfa/bfad_im.c b/drivers/scsi/bfa/bfad_im.c
index 299c6f80d460..6c805e13f8dd 100644
--- a/drivers/scsi/bfa/bfad_im.c
+++ b/drivers/scsi/bfa/bfad_im.c
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
@@ -185,7 +186,7 @@ bfad_im_info(struct Scsi_Host *shost)
memset(bfa_buf, 0, sizeof(bfa_buf));
snprintf(bfa_buf, sizeof(bfa_buf),
- "Brocade FC/FCOE Adapter, " "hwpath: %s driver: %s",
+ "QLogic BR-series FC/FCOE Adapter, hwpath: %s driver: %s",
bfad->pci_name, BFAD_DRIVER_VERSION);
return bfa_buf;
@@ -271,6 +272,19 @@ bfad_im_target_reset_send(struct bfad_s *bfad, struct scsi_cmnd *cmnd,
cmnd->host_scribble = NULL;
cmnd->SCp.Status = 0;
bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim->fcs_itnim);
+ /*
+ * bfa_itnim can be NULL if the port gets disconnected and the bfa
+ * and fcs layers have cleaned up their nexus with the targets and
+ * the same has not been cleaned up by the shim
+ */
+ if (bfa_itnim == NULL) {
+ bfa_tskim_free(tskim);
+ BFA_LOG(KERN_ERR, bfad, bfa_log_level,
+ "target reset, bfa_itnim is NULL\n");
+ rc = BFA_STATUS_FAILED;
+ goto out;
+ }
+
memset(&scsilun, 0, sizeof(scsilun));
bfa_tskim_start(tskim, bfa_itnim, scsilun,
FCP_TM_TARGET_RESET, BFAD_TARGET_RESET_TMO);
@@ -326,6 +340,19 @@ bfad_im_reset_lun_handler(struct scsi_cmnd *cmnd)
cmnd->SCp.ptr = (char *)&wq;
cmnd->SCp.Status = 0;
bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim->fcs_itnim);
+ /*
+ * bfa_itnim can be NULL if the port gets disconnected and the bfa
+ * and fcs layers have cleaned up their nexus with the targets and
+ * the same has not been cleaned up by the shim
+ */
+ if (bfa_itnim == NULL) {
+ bfa_tskim_free(tskim);
+ BFA_LOG(KERN_ERR, bfad, bfa_log_level,
+ "lun reset, bfa_itnim is NULL\n");
+ spin_unlock_irqrestore(&bfad->bfad_lock, flags);
+ rc = FAILED;
+ goto out;
+ }
int_to_scsilun(cmnd->device->lun, &scsilun);
bfa_tskim_start(tskim, bfa_itnim, scsilun,
FCP_TM_LUN_RESET, BFAD_LUN_RESET_TMO);
diff --git a/drivers/scsi/bfa/bfad_im.h b/drivers/scsi/bfa/bfad_im.h
index f6c1023e502a..836fdc221edd 100644
--- a/drivers/scsi/bfa/bfad_im.h
+++ b/drivers/scsi/bfa/bfad_im.h
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfi.h b/drivers/scsi/bfa/bfi.h
index 9ef91f907dec..97600dcec649 100644
--- a/drivers/scsi/bfa/bfi.h
+++ b/drivers/scsi/bfa/bfi.h
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfi_ms.h b/drivers/scsi/bfa/bfi_ms.h
index 1a3fe5ad58fa..ae5bfe039fcc 100644
--- a/drivers/scsi/bfa/bfi_ms.h
+++ b/drivers/scsi/bfa/bfi_ms.h
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
diff --git a/drivers/scsi/bfa/bfi_reg.h b/drivers/scsi/bfa/bfi_reg.h
index 99133bcf53f9..fd5b87616e8b 100644
--- a/drivers/scsi/bfa/bfi_reg.h
+++ b/drivers/scsi/bfa/bfi_reg.h
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2005-2010 Brocade Communications Systems, Inc.
+ * Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
+ * Copyright (c) 2014- QLogic Corporation.
* All rights reserved
- * www.brocade.com
+ * www.qlogic.com
*
- * Linux driver for Brocade Fibre Channel Host Bus Adapter.
+ * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License (GPL) Version 2 as
@@ -16,7 +17,7 @@
*/
/*
- * bfi_reg.h ASIC register defines for all Brocade adapter ASICs
+ * bfi_reg.h ASIC register defines for all QLogic BR-series adapter ASICs
*/
#ifndef __BFI_REG_H__
diff --git a/drivers/scsi/cxlflash/common.h b/drivers/scsi/cxlflash/common.h
index c11cd193f896..5ada9268a450 100644
--- a/drivers/scsi/cxlflash/common.h
+++ b/drivers/scsi/cxlflash/common.h
@@ -165,6 +165,8 @@ struct afu {
struct sisl_host_map __iomem *host_map; /* MC host map */
struct sisl_ctrl_map __iomem *ctrl_map; /* MC control map */
+ struct kref mapcount;
+
ctx_hndl_t ctx_hndl; /* master's context handle */
u64 *hrrq_start;
u64 *hrrq_end;
diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c
index 1e5bf0ca81da..f6d90ce8f3b7 100644
--- a/drivers/scsi/cxlflash/main.c
+++ b/drivers/scsi/cxlflash/main.c
@@ -368,6 +368,7 @@ out:
no_room:
afu->read_room = true;
+ kref_get(&cfg->afu->mapcount);
schedule_work(&cfg->work_q);
rc = SCSI_MLQUEUE_HOST_BUSY;
goto out;
@@ -473,6 +474,16 @@ out:
return rc;
}
+static void afu_unmap(struct kref *ref)
+{
+ struct afu *afu = container_of(ref, struct afu, mapcount);
+
+ if (likely(afu->afu_map)) {
+ cxl_psa_unmap((void __iomem *)afu->afu_map);
+ afu->afu_map = NULL;
+ }
+}
+
/**
* cxlflash_driver_info() - information handler for this host driver
* @host: SCSI host associated with device.
@@ -503,6 +514,7 @@ static int cxlflash_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scp)
ulong lock_flags;
short lflag = 0;
int rc = 0;
+ int kref_got = 0;
dev_dbg_ratelimited(dev, "%s: (scp=%p) %d/%d/%d/%llu "
"cdb=(%08X-%08X-%08X-%08X)\n",
@@ -547,6 +559,9 @@ static int cxlflash_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scp)
goto out;
}
+ kref_get(&cfg->afu->mapcount);
+ kref_got = 1;
+
cmd->rcb.ctx_id = afu->ctx_hndl;
cmd->rcb.port_sel = port_sel;
cmd->rcb.lun_id = lun_to_lunid(scp->device->lun);
@@ -587,6 +602,8 @@ static int cxlflash_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scp)
}
out:
+ if (kref_got)
+ kref_put(&afu->mapcount, afu_unmap);
pr_devel("%s: returning rc=%d\n", __func__, rc);
return rc;
}
@@ -632,20 +649,36 @@ static void free_mem(struct cxlflash_cfg *cfg)
* @cfg: Internal structure associated with the host.
*
* Safe to call with AFU in a partially allocated/initialized state.
+ *
+ * Cleans up all state associated with the command queue, and unmaps
+ * the MMIO space.
+ *
+ * - complete() will take care of commands we initiated (they'll be checked
+ * in as part of the cleanup that occurs after the completion)
+ *
+ * - cmd_checkin() will take care of entries that we did not initiate and that
+ * have not (and will not) complete because they are sitting on a [now stale]
+ * hardware queue
*/
static void stop_afu(struct cxlflash_cfg *cfg)
{
int i;
struct afu *afu = cfg->afu;
+ struct afu_cmd *cmd;
if (likely(afu)) {
- for (i = 0; i < CXLFLASH_NUM_CMDS; i++)
- complete(&afu->cmd[i].cevent);
+ for (i = 0; i < CXLFLASH_NUM_CMDS; i++) {
+ cmd = &afu->cmd[i];
+ complete(&cmd->cevent);
+ if (!atomic_read(&cmd->free))
+ cmd_checkin(cmd);
+ }
if (likely(afu->afu_map)) {
cxl_psa_unmap((void __iomem *)afu->afu_map);
afu->afu_map = NULL;
}
+ kref_put(&afu->mapcount, afu_unmap);
}
}
@@ -731,8 +764,8 @@ static void cxlflash_remove(struct pci_dev *pdev)
scsi_remove_host(cfg->host);
/* fall through */
case INIT_STATE_AFU:
- term_afu(cfg);
cancel_work_sync(&cfg->work_q);
+ term_afu(cfg);
case INIT_STATE_PCI:
pci_release_regions(cfg->dev);
pci_disable_device(pdev);
@@ -1108,7 +1141,7 @@ static const struct asyc_intr_info ainfo[] = {
{SISL_ASTATUS_FC1_OTHER, "other error", 1, CLR_FC_ERROR | LINK_RESET},
{SISL_ASTATUS_FC1_LOGO, "target initiated LOGO", 1, 0},
{SISL_ASTATUS_FC1_CRC_T, "CRC threshold exceeded", 1, LINK_RESET},
- {SISL_ASTATUS_FC1_LOGI_R, "login timed out, retrying", 1, 0},
+ {SISL_ASTATUS_FC1_LOGI_R, "login timed out, retrying", 1, LINK_RESET},
{SISL_ASTATUS_FC1_LOGI_F, "login failed", 1, CLR_FC_ERROR},
{SISL_ASTATUS_FC1_LOGI_S, "login succeeded", 1, SCAN_HOST},
{SISL_ASTATUS_FC1_LINK_DN, "link down", 1, 0},
@@ -1316,6 +1349,7 @@ static irqreturn_t cxlflash_async_err_irq(int irq, void *data)
__func__, port);
cfg->lr_state = LINK_RESET_REQUIRED;
cfg->lr_port = port;
+ kref_get(&cfg->afu->mapcount);
schedule_work(&cfg->work_q);
}
@@ -1336,6 +1370,7 @@ static irqreturn_t cxlflash_async_err_irq(int irq, void *data)
if (info->action & SCAN_HOST) {
atomic_inc(&cfg->scan_host_needed);
+ kref_get(&cfg->afu->mapcount);
schedule_work(&cfg->work_q);
}
}
@@ -1731,6 +1766,7 @@ static int init_afu(struct cxlflash_cfg *cfg)
rc = -ENOMEM;
goto err1;
}
+ kref_init(&afu->mapcount);
/* No byte reverse on reading afu_version or string will be backwards */
reg = readq(&afu->afu_map->global.regs.afu_version);
@@ -1765,8 +1801,7 @@ out:
return rc;
err2:
- cxl_psa_unmap((void __iomem *)afu->afu_map);
- afu->afu_map = NULL;
+ kref_put(&afu->mapcount, afu_unmap);
err1:
term_mc(cfg, UNDO_START);
goto out;
@@ -2274,6 +2309,7 @@ static struct scsi_host_template driver_template = {
* Device dependent values
*/
static struct dev_dependent_vals dev_corsa_vals = { CXLFLASH_MAX_SECTORS };
+static struct dev_dependent_vals dev_flash_gt_vals = { CXLFLASH_MAX_SECTORS };
/*
* PCI device binding table
@@ -2281,6 +2317,8 @@ static struct dev_dependent_vals dev_corsa_vals = { CXLFLASH_MAX_SECTORS };
static struct pci_device_id cxlflash_pci_table[] = {
{PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CORSA,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, (kernel_ulong_t)&dev_corsa_vals},
+ {PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_FLASH_GT,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (kernel_ulong_t)&dev_flash_gt_vals},
{}
};
@@ -2339,6 +2377,7 @@ static void cxlflash_worker_thread(struct work_struct *work)
if (atomic_dec_if_positive(&cfg->scan_host_needed) >= 0)
scsi_scan_host(cfg->host);
+ kref_put(&afu->mapcount, afu_unmap);
}
/**
@@ -2585,8 +2624,7 @@ static struct pci_driver cxlflash_driver = {
*/
static int __init init_cxlflash(void)
{
- pr_info("%s: IBM Power CXL Flash Adapter: %s\n",
- __func__, CXLFLASH_DRIVER_DATE);
+ pr_info("%s: %s\n", __func__, CXLFLASH_ADAPTER_NAME);
cxlflash_list_init();
diff --git a/drivers/scsi/cxlflash/main.h b/drivers/scsi/cxlflash/main.h
index 60324566c14f..0faed422c7f4 100644
--- a/drivers/scsi/cxlflash/main.h
+++ b/drivers/scsi/cxlflash/main.h
@@ -22,10 +22,9 @@
#define CXLFLASH_NAME "cxlflash"
#define CXLFLASH_ADAPTER_NAME "IBM POWER CXL Flash Adapter"
-#define CXLFLASH_DRIVER_DATE "(August 13, 2015)"
-#define PCI_DEVICE_ID_IBM_CORSA 0x04F0
-#define CXLFLASH_SUBS_DEV_ID 0x04F0
+#define PCI_DEVICE_ID_IBM_CORSA 0x04F0
+#define PCI_DEVICE_ID_IBM_FLASH_GT 0x0600
/* Since there is only one target, make it 0 */
#define CXLFLASH_TARGET 0
diff --git a/drivers/scsi/cxlflash/superpipe.c b/drivers/scsi/cxlflash/superpipe.c
index cac2e6a50efd..f4020dbb55c3 100644
--- a/drivers/scsi/cxlflash/superpipe.c
+++ b/drivers/scsi/cxlflash/superpipe.c
@@ -1372,7 +1372,7 @@ static int cxlflash_disk_attach(struct scsi_device *sdev,
}
ctx = cxl_dev_context_init(cfg->dev);
- if (unlikely(IS_ERR_OR_NULL(ctx))) {
+ if (IS_ERR_OR_NULL(ctx)) {
dev_err(dev, "%s: Could not initialize context %p\n",
__func__, ctx);
rc = -ENODEV;
@@ -1380,7 +1380,7 @@ static int cxlflash_disk_attach(struct scsi_device *sdev,
}
ctxid = cxl_process_element(ctx);
- if (unlikely((ctxid > MAX_CONTEXT) || (ctxid < 0))) {
+ if (unlikely((ctxid >= MAX_CONTEXT) || (ctxid < 0))) {
dev_err(dev, "%s: ctxid (%d) invalid!\n", __func__, ctxid);
rc = -EPERM;
goto err2;
@@ -1500,7 +1500,7 @@ static int recover_context(struct cxlflash_cfg *cfg, struct ctx_info *ctxi)
struct afu *afu = cfg->afu;
ctx = cxl_dev_context_init(cfg->dev);
- if (unlikely(IS_ERR_OR_NULL(ctx))) {
+ if (IS_ERR_OR_NULL(ctx)) {
dev_err(dev, "%s: Could not initialize context %p\n",
__func__, ctx);
rc = -ENODEV;
@@ -1508,7 +1508,7 @@ static int recover_context(struct cxlflash_cfg *cfg, struct ctx_info *ctxi)
}
ctxid = cxl_process_element(ctx);
- if (unlikely((ctxid > MAX_CONTEXT) || (ctxid < 0))) {
+ if (unlikely((ctxid >= MAX_CONTEXT) || (ctxid < 0))) {
dev_err(dev, "%s: ctxid (%d) invalid!\n", __func__, ctxid);
rc = -EPERM;
goto err1;
diff --git a/drivers/scsi/cxlflash/vlun.c b/drivers/scsi/cxlflash/vlun.c
index a53f583e2d7b..50f8e9300770 100644
--- a/drivers/scsi/cxlflash/vlun.c
+++ b/drivers/scsi/cxlflash/vlun.c
@@ -1008,6 +1008,8 @@ int cxlflash_disk_virtual_open(struct scsi_device *sdev, void *arg)
virt->last_lba = last_lba;
virt->rsrc_handle = rsrc_handle;
+ if (lli->port_sel == BOTH_PORTS)
+ virt->hdr.return_flags |= DK_CXLFLASH_ALL_PORTS_ACTIVE;
out:
if (likely(ctxi))
put_context(ctxi);
diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c
index cc2773b5de68..5a328bf81836 100644
--- a/drivers/scsi/device_handler/scsi_dh_alua.c
+++ b/drivers/scsi/device_handler/scsi_dh_alua.c
@@ -22,7 +22,9 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/module.h>
+#include <asm/unaligned.h>
#include <scsi/scsi.h>
+#include <scsi/scsi_dbg.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_dh.h>
@@ -58,8 +60,9 @@
#define ALUA_FAILOVER_TIMEOUT 60
#define ALUA_FAILOVER_RETRIES 5
-/* flags passed from user level */
+/* device handler flags */
#define ALUA_OPTIMIZE_STPG 1
+#define ALUA_RTPG_EXT_HDR_UNSUPP 2
struct alua_dh_data {
int group_id;
@@ -73,7 +76,6 @@ struct alua_dh_data {
int bufflen;
unsigned char transition_tmo;
unsigned char sense[SCSI_SENSE_BUFFERSIZE];
- int senselen;
struct scsi_device *sdev;
activate_complete callback_fn;
void *callback_data;
@@ -83,7 +85,6 @@ struct alua_dh_data {
#define ALUA_POLICY_SWITCH_ALL 1
static char print_alua_state(int);
-static int alua_check_sense(struct scsi_device *, struct scsi_sense_hdr *);
static int realloc_buffer(struct alua_dh_data *h, unsigned len)
{
@@ -131,93 +132,47 @@ static struct request *get_alua_req(struct scsi_device *sdev,
}
/*
- * submit_vpd_inquiry - Issue an INQUIRY VPD page 0x83 command
- * @sdev: sdev the command should be sent to
- */
-static int submit_vpd_inquiry(struct scsi_device *sdev, struct alua_dh_data *h)
-{
- struct request *rq;
- int err = SCSI_DH_RES_TEMP_UNAVAIL;
-
- rq = get_alua_req(sdev, h->buff, h->bufflen, READ);
- if (!rq)
- goto done;
-
- /* Prepare the command. */
- rq->cmd[0] = INQUIRY;
- rq->cmd[1] = 1;
- rq->cmd[2] = 0x83;
- rq->cmd[4] = h->bufflen;
- rq->cmd_len = COMMAND_SIZE(INQUIRY);
-
- rq->sense = h->sense;
- memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
- rq->sense_len = h->senselen = 0;
-
- err = blk_execute_rq(rq->q, NULL, rq, 1);
- if (err == -EIO) {
- sdev_printk(KERN_INFO, sdev,
- "%s: evpd inquiry failed with %x\n",
- ALUA_DH_NAME, rq->errors);
- h->senselen = rq->sense_len;
- err = SCSI_DH_IO;
- }
- blk_put_request(rq);
-done:
- return err;
-}
-
-/*
* submit_rtpg - Issue a REPORT TARGET GROUP STATES command
* @sdev: sdev the command should be sent to
*/
-static unsigned submit_rtpg(struct scsi_device *sdev, struct alua_dh_data *h,
- bool rtpg_ext_hdr_req)
+static unsigned submit_rtpg(struct scsi_device *sdev, struct alua_dh_data *h)
{
struct request *rq;
- int err = SCSI_DH_RES_TEMP_UNAVAIL;
+ int err = 0;
rq = get_alua_req(sdev, h->buff, h->bufflen, READ);
- if (!rq)
+ if (!rq) {
+ err = DRIVER_BUSY << 24;
goto done;
+ }
/* Prepare the command. */
rq->cmd[0] = MAINTENANCE_IN;
- if (rtpg_ext_hdr_req)
+ if (!(h->flags & ALUA_RTPG_EXT_HDR_UNSUPP))
rq->cmd[1] = MI_REPORT_TARGET_PGS | MI_EXT_HDR_PARAM_FMT;
else
rq->cmd[1] = MI_REPORT_TARGET_PGS;
- rq->cmd[6] = (h->bufflen >> 24) & 0xff;
- rq->cmd[7] = (h->bufflen >> 16) & 0xff;
- rq->cmd[8] = (h->bufflen >> 8) & 0xff;
- rq->cmd[9] = h->bufflen & 0xff;
+ put_unaligned_be32(h->bufflen, &rq->cmd[6]);
rq->cmd_len = COMMAND_SIZE(MAINTENANCE_IN);
rq->sense = h->sense;
memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
- rq->sense_len = h->senselen = 0;
+ rq->sense_len = 0;
- err = blk_execute_rq(rq->q, NULL, rq, 1);
- if (err == -EIO) {
- sdev_printk(KERN_INFO, sdev,
- "%s: rtpg failed with %x\n",
- ALUA_DH_NAME, rq->errors);
- h->senselen = rq->sense_len;
- err = SCSI_DH_IO;
- }
+ blk_execute_rq(rq->q, NULL, rq, 1);
+ if (rq->errors)
+ err = rq->errors;
blk_put_request(rq);
done:
return err;
}
/*
- * alua_stpg - Evaluate SET TARGET GROUP STATES
+ * stpg_endio - Evaluate SET TARGET GROUP STATES
* @sdev: the device to be evaluated
* @state: the new target group state
*
- * Send a SET TARGET GROUP STATES command to the device.
- * We only have to test here if we should resubmit the command;
- * any other error is assumed as a failure.
+ * Evaluate a SET TARGET GROUP STATES command response.
*/
static void stpg_endio(struct request *req, int error)
{
@@ -231,22 +186,21 @@ static void stpg_endio(struct request *req, int error)
goto done;
}
- if (req->sense_len > 0) {
- err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE,
- &sense_hdr);
- if (!err) {
- err = SCSI_DH_IO;
+ if (scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE,
+ &sense_hdr)) {
+ if (sense_hdr.sense_key == NOT_READY &&
+ sense_hdr.asc == 0x04 && sense_hdr.ascq == 0x0a) {
+ /* ALUA state transition already in progress */
+ err = SCSI_DH_OK;
goto done;
}
- err = alua_check_sense(h->sdev, &sense_hdr);
- if (err == ADD_TO_MLQUEUE) {
+ if (sense_hdr.sense_key == UNIT_ATTENTION) {
err = SCSI_DH_RETRY;
goto done;
}
- sdev_printk(KERN_INFO, h->sdev,
- "%s: stpg sense code: %02x/%02x/%02x\n",
- ALUA_DH_NAME, sense_hdr.sense_key,
- sense_hdr.asc, sense_hdr.ascq);
+ sdev_printk(KERN_INFO, h->sdev, "%s: stpg failed\n",
+ ALUA_DH_NAME);
+ scsi_print_sense_hdr(h->sdev, ALUA_DH_NAME, &sense_hdr);
err = SCSI_DH_IO;
} else if (error)
err = SCSI_DH_IO;
@@ -284,8 +238,7 @@ static unsigned submit_stpg(struct alua_dh_data *h)
/* Prepare the data buffer */
memset(h->buff, 0, stpg_len);
h->buff[4] = TPGS_STATE_OPTIMIZED & 0x0f;
- h->buff[6] = (h->group_id >> 8) & 0xff;
- h->buff[7] = h->group_id & 0xff;
+ put_unaligned_be16(h->group_id, &h->buff[6]);
rq = get_alua_req(sdev, h->buff, stpg_len, WRITE);
if (!rq)
@@ -294,15 +247,12 @@ static unsigned submit_stpg(struct alua_dh_data *h)
/* Prepare the command. */
rq->cmd[0] = MAINTENANCE_OUT;
rq->cmd[1] = MO_SET_TARGET_PGS;
- rq->cmd[6] = (stpg_len >> 24) & 0xff;
- rq->cmd[7] = (stpg_len >> 16) & 0xff;
- rq->cmd[8] = (stpg_len >> 8) & 0xff;
- rq->cmd[9] = stpg_len & 0xff;
+ put_unaligned_be32(stpg_len, &rq->cmd[6]);
rq->cmd_len = COMMAND_SIZE(MAINTENANCE_OUT);
rq->sense = h->sense;
memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
- rq->sense_len = h->senselen = 0;
+ rq->sense_len = 0;
rq->end_io_data = h;
blk_execute_rq_nowait(rq->q, NULL, rq, 1, stpg_endio);
@@ -316,12 +266,23 @@ static unsigned submit_stpg(struct alua_dh_data *h)
* Examine the TPGS setting of the sdev to find out if ALUA
* is supported.
*/
-static int alua_check_tpgs(struct scsi_device *sdev, struct alua_dh_data *h)
+static int alua_check_tpgs(struct scsi_device *sdev)
{
- int err = SCSI_DH_OK;
+ int tpgs = TPGS_MODE_NONE;
+
+ /*
+ * ALUA support for non-disk devices is fraught with
+ * difficulties, so disable it for now.
+ */
+ if (sdev->type != TYPE_DISK) {
+ sdev_printk(KERN_INFO, sdev,
+ "%s: disable for non-disk devices\n",
+ ALUA_DH_NAME);
+ return tpgs;
+ }
- h->tpgs = scsi_device_tpgs(sdev);
- switch (h->tpgs) {
+ tpgs = scsi_device_tpgs(sdev);
+ switch (tpgs) {
case TPGS_MODE_EXPLICIT|TPGS_MODE_IMPLICIT:
sdev_printk(KERN_INFO, sdev,
"%s: supports implicit and explicit TPGS\n",
@@ -335,71 +296,34 @@ static int alua_check_tpgs(struct scsi_device *sdev, struct alua_dh_data *h)
sdev_printk(KERN_INFO, sdev, "%s: supports implicit TPGS\n",
ALUA_DH_NAME);
break;
- default:
- h->tpgs = TPGS_MODE_NONE;
+ case TPGS_MODE_NONE:
sdev_printk(KERN_INFO, sdev, "%s: not supported\n",
ALUA_DH_NAME);
- err = SCSI_DH_DEV_UNSUPP;
+ break;
+ default:
+ sdev_printk(KERN_INFO, sdev,
+ "%s: unsupported TPGS setting %d\n",
+ ALUA_DH_NAME, tpgs);
+ tpgs = TPGS_MODE_NONE;
break;
}
- return err;
+ return tpgs;
}
/*
- * alua_vpd_inquiry - Evaluate INQUIRY vpd page 0x83
+ * alua_check_vpd - Evaluate INQUIRY vpd page 0x83
* @sdev: device to be checked
*
* Extract the relative target port and the target port group
* descriptor from the list of identificators.
*/
-static int alua_vpd_inquiry(struct scsi_device *sdev, struct alua_dh_data *h)
+static int alua_check_vpd(struct scsi_device *sdev, struct alua_dh_data *h)
{
- int len;
- unsigned err;
- unsigned char *d;
-
- retry:
- err = submit_vpd_inquiry(sdev, h);
-
- if (err != SCSI_DH_OK)
- return err;
-
- /* Check if vpd page exceeds initial buffer */
- len = (h->buff[2] << 8) + h->buff[3] + 4;
- if (len > h->bufflen) {
- /* Resubmit with the correct length */
- if (realloc_buffer(h, len)) {
- sdev_printk(KERN_WARNING, sdev,
- "%s: kmalloc buffer failed\n",
- ALUA_DH_NAME);
- /* Temporary failure, bypass */
- return SCSI_DH_DEV_TEMP_BUSY;
- }
- goto retry;
- }
+ int rel_port = -1, group_id;
- /*
- * Now look for the correct descriptor.
- */
- d = h->buff + 4;
- while (d < h->buff + len) {
- switch (d[1] & 0xf) {
- case 0x4:
- /* Relative target port */
- h->rel_port = (d[6] << 8) + d[7];
- break;
- case 0x5:
- /* Target port group */
- h->group_id = (d[6] << 8) + d[7];
- break;
- default:
- break;
- }
- d += d[3] + 4;
- }
-
- if (h->group_id == -1) {
+ group_id = scsi_vpd_tpg_id(sdev, &rel_port);
+ if (group_id < 0) {
/*
* Internal error; TPGS supported but required
* VPD identification descriptors not present.
@@ -408,16 +332,16 @@ static int alua_vpd_inquiry(struct scsi_device *sdev, struct alua_dh_data *h)
sdev_printk(KERN_INFO, sdev,
"%s: No target port descriptors found\n",
ALUA_DH_NAME);
- h->state = TPGS_STATE_OPTIMIZED;
- h->tpgs = TPGS_MODE_NONE;
- err = SCSI_DH_DEV_UNSUPP;
- } else {
- sdev_printk(KERN_INFO, sdev,
- "%s: port group %02x rel port %02x\n",
- ALUA_DH_NAME, h->group_id, h->rel_port);
+ return SCSI_DH_DEV_UNSUPP;
}
+ h->state = TPGS_STATE_OPTIMIZED;
+ h->group_id = group_id;
- return err;
+ sdev_printk(KERN_INFO, sdev,
+ "%s: port group %02x rel port %02x\n",
+ ALUA_DH_NAME, h->group_id, h->rel_port);
+
+ return 0;
}
static char print_alua_state(int state)
@@ -452,28 +376,6 @@ static int alua_check_sense(struct scsi_device *sdev,
* LUN Not Accessible - ALUA state transition
*/
return ADD_TO_MLQUEUE;
- if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0b)
- /*
- * LUN Not Accessible -- Target port in standby state
- */
- return SUCCESS;
- if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0c)
- /*
- * LUN Not Accessible -- Target port in unavailable state
- */
- return SUCCESS;
- if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x12)
- /*
- * LUN Not Ready -- Offline
- */
- return SUCCESS;
- if (sdev->allow_restart &&
- sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x02)
- /*
- * if the device is not started, we need to wake
- * the error handler to start the motor
- */
- return FAILED;
break;
case UNIT_ATTENTION:
if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)
@@ -533,8 +435,7 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h, int wait_
struct scsi_sense_hdr sense_hdr;
int len, k, off, valid_states = 0;
unsigned char *ucp;
- unsigned err;
- bool rtpg_ext_hdr_req = 1;
+ unsigned err, retval;
unsigned long expiry, interval = 0;
unsigned int tpg_desc_tbl_off;
unsigned char orig_transition_tmo;
@@ -545,13 +446,17 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h, int wait_
expiry = round_jiffies_up(jiffies + h->transition_tmo * HZ);
retry:
- err = submit_rtpg(sdev, h, rtpg_ext_hdr_req);
-
- if (err == SCSI_DH_IO && h->senselen > 0) {
- err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE,
- &sense_hdr);
- if (!err)
+ retval = submit_rtpg(sdev, h);
+ if (retval) {
+ if (!scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE,
+ &sense_hdr)) {
+ sdev_printk(KERN_INFO, sdev,
+ "%s: rtpg failed, result %d\n",
+ ALUA_DH_NAME, retval);
+ if (driver_byte(retval) == DRIVER_BUSY)
+ return SCSI_DH_DEV_TEMP_BUSY;
return SCSI_DH_IO;
+ }
/*
* submit_rtpg() has failed on existing arrays
@@ -561,27 +466,34 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h, int wait_
* The retry without rtpg_ext_hdr_req set
* handles this.
*/
- if (rtpg_ext_hdr_req == 1 &&
+ if (!(h->flags & ALUA_RTPG_EXT_HDR_UNSUPP) &&
sense_hdr.sense_key == ILLEGAL_REQUEST &&
sense_hdr.asc == 0x24 && sense_hdr.ascq == 0) {
- rtpg_ext_hdr_req = 0;
+ h->flags |= ALUA_RTPG_EXT_HDR_UNSUPP;
goto retry;
}
-
- err = alua_check_sense(sdev, &sense_hdr);
- if (err == ADD_TO_MLQUEUE && time_before(jiffies, expiry))
+ /*
+ * Retry on ALUA state transition or if any
+ * UNIT ATTENTION occurred.
+ */
+ if (sense_hdr.sense_key == NOT_READY &&
+ sense_hdr.asc == 0x04 && sense_hdr.ascq == 0x0a)
+ err = SCSI_DH_RETRY;
+ else if (sense_hdr.sense_key == UNIT_ATTENTION)
+ err = SCSI_DH_RETRY;
+ if (err == SCSI_DH_RETRY && time_before(jiffies, expiry)) {
+ sdev_printk(KERN_ERR, sdev, "%s: rtpg retry\n",
+ ALUA_DH_NAME);
+ scsi_print_sense_hdr(sdev, ALUA_DH_NAME, &sense_hdr);
goto retry;
- sdev_printk(KERN_INFO, sdev,
- "%s: rtpg sense code %02x/%02x/%02x\n",
- ALUA_DH_NAME, sense_hdr.sense_key,
- sense_hdr.asc, sense_hdr.ascq);
- err = SCSI_DH_IO;
+ }
+ sdev_printk(KERN_ERR, sdev, "%s: rtpg failed\n",
+ ALUA_DH_NAME);
+ scsi_print_sense_hdr(sdev, ALUA_DH_NAME, &sense_hdr);
+ return SCSI_DH_IO;
}
- if (err != SCSI_DH_OK)
- return err;
- len = (h->buff[0] << 24) + (h->buff[1] << 16) +
- (h->buff[2] << 8) + h->buff[3] + 4;
+ len = get_unaligned_be32(&h->buff[0]) + 4;
if (len > h->bufflen) {
/* Resubmit with the correct length */
@@ -616,7 +528,7 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h, int wait_
k < len;
k += off, ucp += off) {
- if (h->group_id == (ucp[2] << 8) + ucp[3]) {
+ if (h->group_id == get_unaligned_be16(&ucp[2])) {
h->state = ucp[0] & 0x0f;
h->pref = ucp[0] >> 7;
valid_states = ucp[1];
@@ -674,13 +586,13 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h, int wait_
*/
static int alua_initialize(struct scsi_device *sdev, struct alua_dh_data *h)
{
- int err;
+ int err = SCSI_DH_DEV_UNSUPP;
- err = alua_check_tpgs(sdev, h);
- if (err != SCSI_DH_OK)
+ h->tpgs = alua_check_tpgs(sdev);
+ if (h->tpgs == TPGS_MODE_NONE)
goto out;
- err = alua_vpd_inquiry(sdev, h);
+ err = alua_check_vpd(sdev, h);
if (err != SCSI_DH_OK)
goto out;
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index f4424063b860..0efe7112fc1f 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -1625,7 +1625,7 @@ static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
/* crc offload */
if (likely(lport->crc_offload)) {
- skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum_start = skb_headroom(skb);
skb->csum_offset = skb->len;
crc = 0;
diff --git a/drivers/scsi/hisi_sas/Kconfig b/drivers/scsi/hisi_sas/Kconfig
new file mode 100644
index 000000000000..37a0c7156087
--- /dev/null
+++ b/drivers/scsi/hisi_sas/Kconfig
@@ -0,0 +1,6 @@
+config SCSI_HISI_SAS
+ tristate "HiSilicon SAS"
+ select SCSI_SAS_LIBSAS
+ select BLK_DEV_INTEGRITY
+ help
+ This driver supports HiSilicon's SAS HBA
diff --git a/drivers/scsi/hisi_sas/Makefile b/drivers/scsi/hisi_sas/Makefile
new file mode 100644
index 000000000000..3e70eae81343
--- /dev/null
+++ b/drivers/scsi/hisi_sas/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_SCSI_HISI_SAS) += hisi_sas_main.o
+obj-$(CONFIG_SCSI_HISI_SAS) += hisi_sas_v1_hw.o
diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h
new file mode 100644
index 000000000000..5af2e4187f01
--- /dev/null
+++ b/drivers/scsi/hisi_sas/hisi_sas.h
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2015 Linaro Ltd.
+ * Copyright (c) 2015 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifndef _HISI_SAS_H_
+#define _HISI_SAS_H_
+
+#include <linux/dmapool.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <scsi/libsas.h>
+
+#define DRV_VERSION "v1.0"
+
+#define HISI_SAS_MAX_PHYS 9
+#define HISI_SAS_MAX_QUEUES 32
+#define HISI_SAS_QUEUE_SLOTS 512
+#define HISI_SAS_MAX_ITCT_ENTRIES 4096
+#define HISI_SAS_MAX_DEVICES HISI_SAS_MAX_ITCT_ENTRIES
+#define HISI_SAS_COMMAND_ENTRIES 8192
+
+#define HISI_SAS_STATUS_BUF_SZ \
+ (sizeof(struct hisi_sas_err_record) + 1024)
+#define HISI_SAS_COMMAND_TABLE_SZ \
+ (((sizeof(union hisi_sas_command_table)+3)/4)*4)
+
+#define HISI_SAS_MAX_SSP_RESP_SZ (sizeof(struct ssp_frame_hdr) + 1024)
+#define HISI_SAS_MAX_SMP_RESP_SZ 1028
+
+struct hisi_hba;
+
+enum {
+ PORT_TYPE_SAS = (1U << 1),
+ PORT_TYPE_SATA = (1U << 0),
+};
+
+enum dev_status {
+ HISI_SAS_DEV_NORMAL,
+ HISI_SAS_DEV_EH,
+};
+
+enum hisi_sas_dev_type {
+ HISI_SAS_DEV_TYPE_STP = 0,
+ HISI_SAS_DEV_TYPE_SSP,
+ HISI_SAS_DEV_TYPE_SATA,
+};
+
+struct hisi_sas_phy {
+ struct hisi_hba *hisi_hba;
+ struct hisi_sas_port *port;
+ struct asd_sas_phy sas_phy;
+ struct sas_identify identify;
+ struct timer_list timer;
+ struct work_struct phyup_ws;
+ u64 port_id; /* from hw */
+ u64 dev_sas_addr;
+ u64 phy_type;
+ u64 frame_rcvd_size;
+ u8 frame_rcvd[32];
+ u8 phy_attached;
+ u8 reserved[3];
+ enum sas_linkrate minimum_linkrate;
+ enum sas_linkrate maximum_linkrate;
+};
+
+struct hisi_sas_port {
+ struct asd_sas_port sas_port;
+ u8 port_attached;
+ u8 id; /* from hw */
+ struct list_head list;
+};
+
+struct hisi_sas_cq {
+ struct hisi_hba *hisi_hba;
+ int id;
+};
+
+struct hisi_sas_device {
+ enum sas_device_type dev_type;
+ struct hisi_hba *hisi_hba;
+ struct domain_device *sas_device;
+ u64 attached_phy;
+ u64 device_id;
+ u64 running_req;
+ u8 dev_status;
+};
+
+struct hisi_sas_slot {
+ struct list_head entry;
+ struct sas_task *task;
+ struct hisi_sas_port *port;
+ u64 n_elem;
+ int dlvry_queue;
+ int dlvry_queue_slot;
+ int cmplt_queue;
+ int cmplt_queue_slot;
+ int idx;
+ void *cmd_hdr;
+ dma_addr_t cmd_hdr_dma;
+ void *status_buffer;
+ dma_addr_t status_buffer_dma;
+ void *command_table;
+ dma_addr_t command_table_dma;
+ struct hisi_sas_sge_page *sge_page;
+ dma_addr_t sge_page_dma;
+};
+
+struct hisi_sas_tmf_task {
+ u8 tmf;
+ u16 tag_of_task_to_be_managed;
+};
+
+struct hisi_sas_hw {
+ int (*hw_init)(struct hisi_hba *hisi_hba);
+ void (*setup_itct)(struct hisi_hba *hisi_hba,
+ struct hisi_sas_device *device);
+ void (*sl_notify)(struct hisi_hba *hisi_hba, int phy_no);
+ int (*get_free_slot)(struct hisi_hba *hisi_hba, int *q, int *s);
+ void (*start_delivery)(struct hisi_hba *hisi_hba);
+ int (*prep_ssp)(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot, int is_tmf,
+ struct hisi_sas_tmf_task *tmf);
+ int (*prep_smp)(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot);
+ int (*slot_complete)(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot, int abort);
+ void (*phy_enable)(struct hisi_hba *hisi_hba, int phy_no);
+ void (*phy_disable)(struct hisi_hba *hisi_hba, int phy_no);
+ void (*phy_hard_reset)(struct hisi_hba *hisi_hba, int phy_no);
+ void (*free_device)(struct hisi_hba *hisi_hba,
+ struct hisi_sas_device *dev);
+ int (*get_wideport_bitmap)(struct hisi_hba *hisi_hba, int port_id);
+ int complete_hdr_size;
+};
+
+struct hisi_hba {
+ /* This must be the first element, used by SHOST_TO_SAS_HA */
+ struct sas_ha_struct *p;
+
+ struct platform_device *pdev;
+ void __iomem *regs;
+ struct regmap *ctrl;
+ u32 ctrl_reset_reg;
+ u32 ctrl_reset_sts_reg;
+ u32 ctrl_clock_ena_reg;
+ u8 sas_addr[SAS_ADDR_SIZE];
+
+ int n_phy;
+ int scan_finished;
+ spinlock_t lock;
+
+ struct timer_list timer;
+ struct workqueue_struct *wq;
+
+ int slot_index_count;
+ unsigned long *slot_index_tags;
+
+ /* SCSI/SAS glue */
+ struct sas_ha_struct sha;
+ struct Scsi_Host *shost;
+
+ struct hisi_sas_cq cq[HISI_SAS_MAX_QUEUES];
+ struct hisi_sas_phy phy[HISI_SAS_MAX_PHYS];
+ struct hisi_sas_port port[HISI_SAS_MAX_PHYS];
+
+ int queue_count;
+ int queue;
+ struct hisi_sas_slot *slot_prep;
+
+ struct dma_pool *sge_page_pool;
+ struct hisi_sas_device devices[HISI_SAS_MAX_DEVICES];
+ struct dma_pool *command_table_pool;
+ struct dma_pool *status_buffer_pool;
+ struct hisi_sas_cmd_hdr *cmd_hdr[HISI_SAS_MAX_QUEUES];
+ dma_addr_t cmd_hdr_dma[HISI_SAS_MAX_QUEUES];
+ void *complete_hdr[HISI_SAS_MAX_QUEUES];
+ dma_addr_t complete_hdr_dma[HISI_SAS_MAX_QUEUES];
+ struct hisi_sas_initial_fis *initial_fis;
+ dma_addr_t initial_fis_dma;
+ struct hisi_sas_itct *itct;
+ dma_addr_t itct_dma;
+ struct hisi_sas_iost *iost;
+ dma_addr_t iost_dma;
+ struct hisi_sas_breakpoint *breakpoint;
+ dma_addr_t breakpoint_dma;
+ struct hisi_sas_breakpoint *sata_breakpoint;
+ dma_addr_t sata_breakpoint_dma;
+ struct hisi_sas_slot *slot_info;
+ const struct hisi_sas_hw *hw; /* Low level hw interface */
+};
+
+/* Generic HW DMA host memory structures */
+/* Delivery queue header */
+struct hisi_sas_cmd_hdr {
+ /* dw0 */
+ __le32 dw0;
+
+ /* dw1 */
+ __le32 dw1;
+
+ /* dw2 */
+ __le32 dw2;
+
+ /* dw3 */
+ __le32 transfer_tags;
+
+ /* dw4 */
+ __le32 data_transfer_len;
+
+ /* dw5 */
+ __le32 first_burst_num;
+
+ /* dw6 */
+ __le32 sg_len;
+
+ /* dw7 */
+ __le32 dw7;
+
+ /* dw8-9 */
+ __le64 cmd_table_addr;
+
+ /* dw10-11 */
+ __le64 sts_buffer_addr;
+
+ /* dw12-13 */
+ __le64 prd_table_addr;
+
+ /* dw14-15 */
+ __le64 dif_prd_table_addr;
+};
+
+struct hisi_sas_itct {
+ __le64 qw0;
+ __le64 sas_addr;
+ __le64 qw2;
+ __le64 qw3;
+ __le64 qw4;
+ __le64 qw_sata_ncq0_3;
+ __le64 qw_sata_ncq7_4;
+ __le64 qw_sata_ncq11_8;
+ __le64 qw_sata_ncq15_12;
+ __le64 qw_sata_ncq19_16;
+ __le64 qw_sata_ncq23_20;
+ __le64 qw_sata_ncq27_24;
+ __le64 qw_sata_ncq31_28;
+ __le64 qw_non_ncq_iptt;
+ __le64 qw_rsvd0;
+ __le64 qw_rsvd1;
+};
+
+struct hisi_sas_iost {
+ __le64 qw0;
+ __le64 qw1;
+ __le64 qw2;
+ __le64 qw3;
+};
+
+struct hisi_sas_err_record {
+ /* dw0 */
+ __le32 dma_err_type;
+
+ /* dw1 */
+ __le32 trans_tx_fail_type;
+
+ /* dw2 */
+ __le32 trans_rx_fail_type;
+
+ /* dw3 */
+ u32 rsvd;
+};
+
+struct hisi_sas_initial_fis {
+ struct hisi_sas_err_record err_record;
+ struct dev_to_host_fis fis;
+ u32 rsvd[3];
+};
+
+struct hisi_sas_breakpoint {
+ u8 data[128]; /*io128 byte*/
+};
+
+struct hisi_sas_sge {
+ __le64 addr;
+ __le32 page_ctrl_0;
+ __le32 page_ctrl_1;
+ __le32 data_len;
+ __le32 data_off;
+};
+
+struct hisi_sas_command_table_smp {
+ u8 bytes[44];
+};
+
+struct hisi_sas_command_table_stp {
+ struct host_to_dev_fis command_fis;
+ u8 dummy[12];
+ u8 atapi_cdb[ATAPI_CDB_LEN];
+};
+
+#define HISI_SAS_SGE_PAGE_CNT SCSI_MAX_SG_SEGMENTS
+struct hisi_sas_sge_page {
+ struct hisi_sas_sge sge[HISI_SAS_SGE_PAGE_CNT];
+};
+
+struct hisi_sas_command_table_ssp {
+ struct ssp_frame_hdr hdr;
+ union {
+ struct {
+ struct ssp_command_iu task;
+ u32 prot[6];
+ };
+ struct ssp_tmf_iu ssp_task;
+ struct xfer_rdy_iu xfer_rdy;
+ struct ssp_response_iu ssp_res;
+ } u;
+};
+
+union hisi_sas_command_table {
+ struct hisi_sas_command_table_ssp ssp;
+ struct hisi_sas_command_table_smp smp;
+ struct hisi_sas_command_table_stp stp;
+};
+extern int hisi_sas_probe(struct platform_device *pdev,
+ const struct hisi_sas_hw *ops);
+extern int hisi_sas_remove(struct platform_device *pdev);
+
+extern void hisi_sas_phy_down(struct hisi_hba *hisi_hba, int phy_no, int rdy);
+extern void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba,
+ struct sas_task *task,
+ struct hisi_sas_slot *slot);
+#endif
diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c
new file mode 100644
index 000000000000..99b1950d751c
--- /dev/null
+++ b/drivers/scsi/hisi_sas/hisi_sas_main.c
@@ -0,0 +1,1358 @@
+/*
+ * Copyright (c) 2015 Linaro Ltd.
+ * Copyright (c) 2015 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include "hisi_sas.h"
+#define DRV_NAME "hisi_sas"
+
+#define DEV_IS_EXPANDER(type) \
+ ((type == SAS_EDGE_EXPANDER_DEVICE) || \
+ (type == SAS_FANOUT_EXPANDER_DEVICE))
+
+#define DEV_IS_GONE(dev) \
+ ((!dev) || (dev->dev_type == SAS_PHY_UNUSED))
+
+static struct hisi_hba *dev_to_hisi_hba(struct domain_device *device)
+{
+ return device->port->ha->lldd_ha;
+}
+
+static void hisi_sas_slot_index_clear(struct hisi_hba *hisi_hba, int slot_idx)
+{
+ void *bitmap = hisi_hba->slot_index_tags;
+
+ clear_bit(slot_idx, bitmap);
+}
+
+static void hisi_sas_slot_index_free(struct hisi_hba *hisi_hba, int slot_idx)
+{
+ hisi_sas_slot_index_clear(hisi_hba, slot_idx);
+}
+
+static void hisi_sas_slot_index_set(struct hisi_hba *hisi_hba, int slot_idx)
+{
+ void *bitmap = hisi_hba->slot_index_tags;
+
+ set_bit(slot_idx, bitmap);
+}
+
+static int hisi_sas_slot_index_alloc(struct hisi_hba *hisi_hba, int *slot_idx)
+{
+ unsigned int index;
+ void *bitmap = hisi_hba->slot_index_tags;
+
+ index = find_first_zero_bit(bitmap, hisi_hba->slot_index_count);
+ if (index >= hisi_hba->slot_index_count)
+ return -SAS_QUEUE_FULL;
+ hisi_sas_slot_index_set(hisi_hba, index);
+ *slot_idx = index;
+ return 0;
+}
+
+static void hisi_sas_slot_index_init(struct hisi_hba *hisi_hba)
+{
+ int i;
+
+ for (i = 0; i < hisi_hba->slot_index_count; ++i)
+ hisi_sas_slot_index_clear(hisi_hba, i);
+}
+
+void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task,
+ struct hisi_sas_slot *slot)
+{
+ struct device *dev = &hisi_hba->pdev->dev;
+
+ if (!slot->task)
+ return;
+
+ if (!sas_protocol_ata(task->task_proto))
+ if (slot->n_elem)
+ dma_unmap_sg(dev, task->scatter, slot->n_elem,
+ task->data_dir);
+
+ if (slot->command_table)
+ dma_pool_free(hisi_hba->command_table_pool,
+ slot->command_table, slot->command_table_dma);
+
+ if (slot->status_buffer)
+ dma_pool_free(hisi_hba->status_buffer_pool,
+ slot->status_buffer, slot->status_buffer_dma);
+
+ if (slot->sge_page)
+ dma_pool_free(hisi_hba->sge_page_pool, slot->sge_page,
+ slot->sge_page_dma);
+
+ list_del_init(&slot->entry);
+ task->lldd_task = NULL;
+ slot->task = NULL;
+ slot->port = NULL;
+ hisi_sas_slot_index_free(hisi_hba, slot->idx);
+ memset(slot, 0, sizeof(*slot));
+}
+EXPORT_SYMBOL_GPL(hisi_sas_slot_task_free);
+
+static int hisi_sas_task_prep_smp(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+ return hisi_hba->hw->prep_smp(hisi_hba, slot);
+}
+
+static int hisi_sas_task_prep_ssp(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot, int is_tmf,
+ struct hisi_sas_tmf_task *tmf)
+{
+ return hisi_hba->hw->prep_ssp(hisi_hba, slot, is_tmf, tmf);
+}
+
+static int hisi_sas_task_prep(struct sas_task *task, struct hisi_hba *hisi_hba,
+ int is_tmf, struct hisi_sas_tmf_task *tmf,
+ int *pass)
+{
+ struct domain_device *device = task->dev;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct hisi_sas_port *port;
+ struct hisi_sas_slot *slot;
+ struct hisi_sas_cmd_hdr *cmd_hdr_base;
+ struct device *dev = &hisi_hba->pdev->dev;
+ int dlvry_queue_slot, dlvry_queue, n_elem = 0, rc, slot_idx;
+
+ if (!device->port) {
+ struct task_status_struct *ts = &task->task_status;
+
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_PHY_DOWN;
+ /*
+ * libsas will use dev->port, should
+ * not call task_done for sata
+ */
+ if (device->dev_type != SAS_SATA_DEV)
+ task->task_done(task);
+ return 0;
+ }
+
+ if (DEV_IS_GONE(sas_dev)) {
+ if (sas_dev)
+ dev_info(dev, "task prep: device %llu not ready\n",
+ sas_dev->device_id);
+ else
+ dev_info(dev, "task prep: device %016llx not ready\n",
+ SAS_ADDR(device->sas_addr));
+
+ rc = SAS_PHY_DOWN;
+ return rc;
+ }
+ port = device->port->lldd_port;
+ if (port && !port->port_attached && !tmf) {
+ if (sas_protocol_ata(task->task_proto)) {
+ struct task_status_struct *ts = &task->task_status;
+
+ dev_info(dev,
+ "task prep: SATA/STP port%d not attach device\n",
+ device->port->id);
+ ts->resp = SAS_TASK_COMPLETE;
+ ts->stat = SAS_PHY_DOWN;
+ task->task_done(task);
+ } else {
+ struct task_status_struct *ts = &task->task_status;
+
+ dev_info(dev,
+ "task prep: SAS port%d does not attach device\n",
+ device->port->id);
+ ts->resp = SAS_TASK_UNDELIVERED;
+ ts->stat = SAS_PHY_DOWN;
+ task->task_done(task);
+ }
+ return 0;
+ }
+
+ if (!sas_protocol_ata(task->task_proto)) {
+ if (task->num_scatter) {
+ n_elem = dma_map_sg(dev, task->scatter,
+ task->num_scatter, task->data_dir);
+ if (!n_elem) {
+ rc = -ENOMEM;
+ goto prep_out;
+ }
+ }
+ } else
+ n_elem = task->num_scatter;
+
+ rc = hisi_sas_slot_index_alloc(hisi_hba, &slot_idx);
+ if (rc)
+ goto err_out;
+ rc = hisi_hba->hw->get_free_slot(hisi_hba, &dlvry_queue,
+ &dlvry_queue_slot);
+ if (rc)
+ goto err_out_tag;
+
+ slot = &hisi_hba->slot_info[slot_idx];
+ memset(slot, 0, sizeof(struct hisi_sas_slot));
+
+ slot->idx = slot_idx;
+ slot->n_elem = n_elem;
+ slot->dlvry_queue = dlvry_queue;
+ slot->dlvry_queue_slot = dlvry_queue_slot;
+ cmd_hdr_base = hisi_hba->cmd_hdr[dlvry_queue];
+ slot->cmd_hdr = &cmd_hdr_base[dlvry_queue_slot];
+ slot->task = task;
+ slot->port = port;
+ task->lldd_task = slot;
+
+ slot->status_buffer = dma_pool_alloc(hisi_hba->status_buffer_pool,
+ GFP_ATOMIC,
+ &slot->status_buffer_dma);
+ if (!slot->status_buffer) {
+ rc = -ENOMEM;
+ goto err_out_slot_buf;
+ }
+ memset(slot->status_buffer, 0, HISI_SAS_STATUS_BUF_SZ);
+
+ slot->command_table = dma_pool_alloc(hisi_hba->command_table_pool,
+ GFP_ATOMIC,
+ &slot->command_table_dma);
+ if (!slot->command_table) {
+ rc = -ENOMEM;
+ goto err_out_status_buf;
+ }
+ memset(slot->command_table, 0, HISI_SAS_COMMAND_TABLE_SZ);
+ memset(slot->cmd_hdr, 0, sizeof(struct hisi_sas_cmd_hdr));
+
+ switch (task->task_proto) {
+ case SAS_PROTOCOL_SMP:
+ rc = hisi_sas_task_prep_smp(hisi_hba, slot);
+ break;
+ case SAS_PROTOCOL_SSP:
+ rc = hisi_sas_task_prep_ssp(hisi_hba, slot, is_tmf, tmf);
+ break;
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+ default:
+ dev_err(dev, "task prep: unknown/unsupported proto (0x%x)\n",
+ task->task_proto);
+ rc = -EINVAL;
+ break;
+ }
+
+ if (rc) {
+ dev_err(dev, "task prep: rc = 0x%x\n", rc);
+ if (slot->sge_page)
+ goto err_out_sge;
+ goto err_out_command_table;
+ }
+
+ list_add_tail(&slot->entry, &port->list);
+ spin_lock(&task->task_state_lock);
+ task->task_state_flags |= SAS_TASK_AT_INITIATOR;
+ spin_unlock(&task->task_state_lock);
+
+ hisi_hba->slot_prep = slot;
+
+ sas_dev->running_req++;
+ ++(*pass);
+
+ return 0;
+
+err_out_sge:
+ dma_pool_free(hisi_hba->sge_page_pool, slot->sge_page,
+ slot->sge_page_dma);
+err_out_command_table:
+ dma_pool_free(hisi_hba->command_table_pool, slot->command_table,
+ slot->command_table_dma);
+err_out_status_buf:
+ dma_pool_free(hisi_hba->status_buffer_pool, slot->status_buffer,
+ slot->status_buffer_dma);
+err_out_slot_buf:
+ /* Nothing to be done */
+err_out_tag:
+ hisi_sas_slot_index_free(hisi_hba, slot_idx);
+err_out:
+ dev_err(dev, "task prep: failed[%d]!\n", rc);
+ if (!sas_protocol_ata(task->task_proto))
+ if (n_elem)
+ dma_unmap_sg(dev, task->scatter, n_elem,
+ task->data_dir);
+prep_out:
+ return rc;
+}
+
+static int hisi_sas_task_exec(struct sas_task *task, gfp_t gfp_flags,
+ int is_tmf, struct hisi_sas_tmf_task *tmf)
+{
+ u32 rc;
+ u32 pass = 0;
+ unsigned long flags;
+ struct hisi_hba *hisi_hba = dev_to_hisi_hba(task->dev);
+ struct device *dev = &hisi_hba->pdev->dev;
+
+ /* protect task_prep and start_delivery sequence */
+ spin_lock_irqsave(&hisi_hba->lock, flags);
+ rc = hisi_sas_task_prep(task, hisi_hba, is_tmf, tmf, &pass);
+ if (rc)
+ dev_err(dev, "task exec: failed[%d]!\n", rc);
+
+ if (likely(pass))
+ hisi_hba->hw->start_delivery(hisi_hba);
+ spin_unlock_irqrestore(&hisi_hba->lock, flags);
+
+ return rc;
+}
+
+static void hisi_sas_bytes_dmaed(struct hisi_hba *hisi_hba, int phy_no)
+{
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct sas_ha_struct *sas_ha;
+
+ if (!phy->phy_attached)
+ return;
+
+ sas_ha = &hisi_hba->sha;
+ sas_ha->notify_phy_event(sas_phy, PHYE_OOB_DONE);
+
+ if (sas_phy->phy) {
+ struct sas_phy *sphy = sas_phy->phy;
+
+ sphy->negotiated_linkrate = sas_phy->linkrate;
+ sphy->minimum_linkrate = phy->minimum_linkrate;
+ sphy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
+ sphy->maximum_linkrate = phy->maximum_linkrate;
+ }
+
+ if (phy->phy_type & PORT_TYPE_SAS) {
+ struct sas_identify_frame *id;
+
+ id = (struct sas_identify_frame *)phy->frame_rcvd;
+ id->dev_type = phy->identify.device_type;
+ id->initiator_bits = SAS_PROTOCOL_ALL;
+ id->target_bits = phy->identify.target_port_protocols;
+ } else if (phy->phy_type & PORT_TYPE_SATA) {
+ /*Nothing*/
+ }
+
+ sas_phy->frame_rcvd_size = phy->frame_rcvd_size;
+ sas_ha->notify_port_event(sas_phy, PORTE_BYTES_DMAED);
+}
+
+static struct hisi_sas_device *hisi_sas_alloc_dev(struct domain_device *device)
+{
+ struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
+ struct hisi_sas_device *sas_dev = NULL;
+ int i;
+
+ spin_lock(&hisi_hba->lock);
+ for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) {
+ if (hisi_hba->devices[i].dev_type == SAS_PHY_UNUSED) {
+ hisi_hba->devices[i].device_id = i;
+ sas_dev = &hisi_hba->devices[i];
+ sas_dev->dev_status = HISI_SAS_DEV_NORMAL;
+ sas_dev->dev_type = device->dev_type;
+ sas_dev->hisi_hba = hisi_hba;
+ sas_dev->sas_device = device;
+ break;
+ }
+ }
+ spin_unlock(&hisi_hba->lock);
+
+ return sas_dev;
+}
+
+static int hisi_sas_dev_found(struct domain_device *device)
+{
+ struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
+ struct domain_device *parent_dev = device->parent;
+ struct hisi_sas_device *sas_dev;
+ struct device *dev = &hisi_hba->pdev->dev;
+
+ sas_dev = hisi_sas_alloc_dev(device);
+ if (!sas_dev) {
+ dev_err(dev, "fail alloc dev: max support %d devices\n",
+ HISI_SAS_MAX_DEVICES);
+ return -EINVAL;
+ }
+
+ device->lldd_dev = sas_dev;
+ hisi_hba->hw->setup_itct(hisi_hba, sas_dev);
+
+ if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type)) {
+ int phy_no;
+ u8 phy_num = parent_dev->ex_dev.num_phys;
+ struct ex_phy *phy;
+
+ for (phy_no = 0; phy_no < phy_num; phy_no++) {
+ phy = &parent_dev->ex_dev.ex_phy[phy_no];
+ if (SAS_ADDR(phy->attached_sas_addr) ==
+ SAS_ADDR(device->sas_addr)) {
+ sas_dev->attached_phy = phy_no;
+ break;
+ }
+ }
+
+ if (phy_no == phy_num) {
+ dev_info(dev, "dev found: no attached "
+ "dev:%016llx at ex:%016llx\n",
+ SAS_ADDR(device->sas_addr),
+ SAS_ADDR(parent_dev->sas_addr));
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static void hisi_sas_scan_start(struct Scsi_Host *shost)
+{
+ struct hisi_hba *hisi_hba = shost_priv(shost);
+ int i;
+
+ for (i = 0; i < hisi_hba->n_phy; ++i)
+ hisi_sas_bytes_dmaed(hisi_hba, i);
+
+ hisi_hba->scan_finished = 1;
+}
+
+static int hisi_sas_scan_finished(struct Scsi_Host *shost, unsigned long time)
+{
+ struct hisi_hba *hisi_hba = shost_priv(shost);
+ struct sas_ha_struct *sha = &hisi_hba->sha;
+
+ if (hisi_hba->scan_finished == 0)
+ return 0;
+
+ sas_drain_work(sha);
+ return 1;
+}
+
+static void hisi_sas_phyup_work(struct work_struct *work)
+{
+ struct hisi_sas_phy *phy =
+ container_of(work, struct hisi_sas_phy, phyup_ws);
+ struct hisi_hba *hisi_hba = phy->hisi_hba;
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ int phy_no = sas_phy->id;
+
+ hisi_hba->hw->sl_notify(hisi_hba, phy_no); /* This requires a sleep */
+ hisi_sas_bytes_dmaed(hisi_hba, phy_no);
+}
+
+static void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int phy_no)
+{
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+
+ phy->hisi_hba = hisi_hba;
+ phy->port = NULL;
+ init_timer(&phy->timer);
+ sas_phy->enabled = (phy_no < hisi_hba->n_phy) ? 1 : 0;
+ sas_phy->class = SAS;
+ sas_phy->iproto = SAS_PROTOCOL_ALL;
+ sas_phy->tproto = 0;
+ sas_phy->type = PHY_TYPE_PHYSICAL;
+ sas_phy->role = PHY_ROLE_INITIATOR;
+ sas_phy->oob_mode = OOB_NOT_CONNECTED;
+ sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN;
+ sas_phy->id = phy_no;
+ sas_phy->sas_addr = &hisi_hba->sas_addr[0];
+ sas_phy->frame_rcvd = &phy->frame_rcvd[0];
+ sas_phy->ha = (struct sas_ha_struct *)hisi_hba->shost->hostdata;
+ sas_phy->lldd_phy = phy;
+
+ INIT_WORK(&phy->phyup_ws, hisi_sas_phyup_work);
+}
+
+static void hisi_sas_port_notify_formed(struct asd_sas_phy *sas_phy)
+{
+ struct sas_ha_struct *sas_ha = sas_phy->ha;
+ struct hisi_hba *hisi_hba = sas_ha->lldd_ha;
+ struct hisi_sas_phy *phy = sas_phy->lldd_phy;
+ struct asd_sas_port *sas_port = sas_phy->port;
+ struct hisi_sas_port *port = &hisi_hba->port[sas_phy->id];
+ unsigned long flags;
+
+ if (!sas_port)
+ return;
+
+ spin_lock_irqsave(&hisi_hba->lock, flags);
+ port->port_attached = 1;
+ port->id = phy->port_id;
+ phy->port = port;
+ sas_port->lldd_port = port;
+ spin_unlock_irqrestore(&hisi_hba->lock, flags);
+}
+
+static void hisi_sas_do_release_task(struct hisi_hba *hisi_hba, int phy_no,
+ struct domain_device *device)
+{
+ struct hisi_sas_phy *phy;
+ struct hisi_sas_port *port;
+ struct hisi_sas_slot *slot, *slot2;
+ struct device *dev = &hisi_hba->pdev->dev;
+
+ phy = &hisi_hba->phy[phy_no];
+ port = phy->port;
+ if (!port)
+ return;
+
+ list_for_each_entry_safe(slot, slot2, &port->list, entry) {
+ struct sas_task *task;
+
+ task = slot->task;
+ if (device && task->dev != device)
+ continue;
+
+ dev_info(dev, "Release slot [%d:%d], task [%p]:\n",
+ slot->dlvry_queue, slot->dlvry_queue_slot, task);
+ hisi_hba->hw->slot_complete(hisi_hba, slot, 1);
+ }
+}
+
+static void hisi_sas_port_notify_deformed(struct asd_sas_phy *sas_phy)
+{
+ struct domain_device *device;
+ struct hisi_sas_phy *phy = sas_phy->lldd_phy;
+ struct asd_sas_port *sas_port = sas_phy->port;
+
+ list_for_each_entry(device, &sas_port->dev_list, dev_list_node)
+ hisi_sas_do_release_task(phy->hisi_hba, sas_phy->id, device);
+}
+
+static void hisi_sas_release_task(struct hisi_hba *hisi_hba,
+ struct domain_device *device)
+{
+ struct asd_sas_port *port = device->port;
+ struct asd_sas_phy *sas_phy;
+
+ list_for_each_entry(sas_phy, &port->phy_list, port_phy_el)
+ hisi_sas_do_release_task(hisi_hba, sas_phy->id, device);
+}
+
+static void hisi_sas_dev_gone(struct domain_device *device)
+{
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
+ struct device *dev = &hisi_hba->pdev->dev;
+ u64 dev_id = sas_dev->device_id;
+
+ dev_info(dev, "found dev[%lld:%x] is gone\n",
+ sas_dev->device_id, sas_dev->dev_type);
+
+ hisi_hba->hw->free_device(hisi_hba, sas_dev);
+ device->lldd_dev = NULL;
+ memset(sas_dev, 0, sizeof(*sas_dev));
+ sas_dev->device_id = dev_id;
+ sas_dev->dev_type = SAS_PHY_UNUSED;
+ sas_dev->dev_status = HISI_SAS_DEV_NORMAL;
+}
+
+static int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags)
+{
+ return hisi_sas_task_exec(task, gfp_flags, 0, NULL);
+}
+
+static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func,
+ void *funcdata)
+{
+ struct sas_ha_struct *sas_ha = sas_phy->ha;
+ struct hisi_hba *hisi_hba = sas_ha->lldd_ha;
+ int phy_no = sas_phy->id;
+
+ switch (func) {
+ case PHY_FUNC_HARD_RESET:
+ hisi_hba->hw->phy_hard_reset(hisi_hba, phy_no);
+ break;
+
+ case PHY_FUNC_LINK_RESET:
+ hisi_hba->hw->phy_enable(hisi_hba, phy_no);
+ hisi_hba->hw->phy_hard_reset(hisi_hba, phy_no);
+ break;
+
+ case PHY_FUNC_DISABLE:
+ hisi_hba->hw->phy_disable(hisi_hba, phy_no);
+ break;
+
+ case PHY_FUNC_SET_LINK_RATE:
+ case PHY_FUNC_RELEASE_SPINUP_HOLD:
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static void hisi_sas_task_done(struct sas_task *task)
+{
+ if (!del_timer(&task->slow_task->timer))
+ return;
+ complete(&task->slow_task->completion);
+}
+
+static void hisi_sas_tmf_timedout(unsigned long data)
+{
+ struct sas_task *task = (struct sas_task *)data;
+
+ task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+ complete(&task->slow_task->completion);
+}
+
+#define TASK_TIMEOUT 20
+#define TASK_RETRY 3
+static int hisi_sas_exec_internal_tmf_task(struct domain_device *device,
+ void *parameter, u32 para_len,
+ struct hisi_sas_tmf_task *tmf)
+{
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct hisi_hba *hisi_hba = sas_dev->hisi_hba;
+ struct device *dev = &hisi_hba->pdev->dev;
+ struct sas_task *task;
+ int res, retry;
+
+ for (retry = 0; retry < TASK_RETRY; retry++) {
+ task = sas_alloc_slow_task(GFP_KERNEL);
+ if (!task)
+ return -ENOMEM;
+
+ task->dev = device;
+ task->task_proto = device->tproto;
+
+ memcpy(&task->ssp_task, parameter, para_len);
+ task->task_done = hisi_sas_task_done;
+
+ task->slow_task->timer.data = (unsigned long) task;
+ task->slow_task->timer.function = hisi_sas_tmf_timedout;
+ task->slow_task->timer.expires = jiffies + TASK_TIMEOUT*HZ;
+ add_timer(&task->slow_task->timer);
+
+ res = hisi_sas_task_exec(task, GFP_KERNEL, 1, tmf);
+
+ if (res) {
+ del_timer(&task->slow_task->timer);
+ dev_err(dev, "abort tmf: executing internal task failed: %d\n",
+ res);
+ goto ex_err;
+ }
+
+ wait_for_completion(&task->slow_task->completion);
+ res = TMF_RESP_FUNC_FAILED;
+ /* Even TMF timed out, return direct. */
+ if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
+ if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
+ dev_err(dev, "abort tmf: TMF task[%d] timeout\n",
+ tmf->tag_of_task_to_be_managed);
+ if (task->lldd_task) {
+ struct hisi_sas_slot *slot =
+ task->lldd_task;
+
+ hisi_sas_slot_task_free(hisi_hba,
+ task, slot);
+ }
+
+ goto ex_err;
+ }
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAM_STAT_GOOD) {
+ res = TMF_RESP_FUNC_COMPLETE;
+ break;
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAS_DATA_UNDERRUN) {
+ /* no error, but return the number of bytes of
+ * underrun
+ */
+ dev_warn(dev, "abort tmf: task to dev %016llx "
+ "resp: 0x%x sts 0x%x underrun\n",
+ SAS_ADDR(device->sas_addr),
+ task->task_status.resp,
+ task->task_status.stat);
+ res = task->task_status.residual;
+ break;
+ }
+
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAS_DATA_OVERRUN) {
+ dev_warn(dev, "abort tmf: blocked task error\n");
+ res = -EMSGSIZE;
+ break;
+ }
+
+ dev_warn(dev, "abort tmf: task to dev "
+ "%016llx resp: 0x%x status 0x%x\n",
+ SAS_ADDR(device->sas_addr), task->task_status.resp,
+ task->task_status.stat);
+ sas_free_task(task);
+ task = NULL;
+ }
+ex_err:
+ WARN_ON(retry == TASK_RETRY);
+ sas_free_task(task);
+ return res;
+}
+
+static int hisi_sas_debug_issue_ssp_tmf(struct domain_device *device,
+ u8 *lun, struct hisi_sas_tmf_task *tmf)
+{
+ struct sas_ssp_task ssp_task;
+
+ if (!(device->tproto & SAS_PROTOCOL_SSP))
+ return TMF_RESP_FUNC_ESUPP;
+
+ memcpy(ssp_task.LUN, lun, 8);
+
+ return hisi_sas_exec_internal_tmf_task(device, &ssp_task,
+ sizeof(ssp_task), tmf);
+}
+
+static int hisi_sas_abort_task(struct sas_task *task)
+{
+ struct scsi_lun lun;
+ struct hisi_sas_tmf_task tmf_task;
+ struct domain_device *device = task->dev;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct hisi_hba *hisi_hba = dev_to_hisi_hba(task->dev);
+ struct device *dev = &hisi_hba->pdev->dev;
+ int rc = TMF_RESP_FUNC_FAILED;
+ unsigned long flags;
+
+ if (!sas_dev) {
+ dev_warn(dev, "Device has been removed\n");
+ return TMF_RESP_FUNC_FAILED;
+ }
+
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ if (task->task_state_flags & SAS_TASK_STATE_DONE) {
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ rc = TMF_RESP_FUNC_COMPLETE;
+ goto out;
+ }
+
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ sas_dev->dev_status = HISI_SAS_DEV_EH;
+ if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) {
+ struct scsi_cmnd *cmnd = task->uldd_task;
+ struct hisi_sas_slot *slot = task->lldd_task;
+ u32 tag = slot->idx;
+
+ int_to_scsilun(cmnd->device->lun, &lun);
+ tmf_task.tmf = TMF_ABORT_TASK;
+ tmf_task.tag_of_task_to_be_managed = cpu_to_le16(tag);
+
+ rc = hisi_sas_debug_issue_ssp_tmf(task->dev, lun.scsi_lun,
+ &tmf_task);
+
+ /* if successful, clear the task and callback forwards.*/
+ if (rc == TMF_RESP_FUNC_COMPLETE) {
+ if (task->lldd_task) {
+ struct hisi_sas_slot *slot;
+
+ slot = &hisi_hba->slot_info
+ [tmf_task.tag_of_task_to_be_managed];
+ spin_lock_irqsave(&hisi_hba->lock, flags);
+ hisi_hba->hw->slot_complete(hisi_hba, slot, 1);
+ spin_unlock_irqrestore(&hisi_hba->lock, flags);
+ }
+ }
+
+ } else if (task->task_proto & SAS_PROTOCOL_SATA ||
+ task->task_proto & SAS_PROTOCOL_STP) {
+ if (task->dev->dev_type == SAS_SATA_DEV) {
+ struct hisi_slot_info *slot = task->lldd_task;
+
+ dev_notice(dev, "abort task: hba=%p task=%p slot=%p\n",
+ hisi_hba, task, slot);
+ task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+ rc = TMF_RESP_FUNC_COMPLETE;
+ goto out;
+ }
+
+ }
+
+out:
+ if (rc != TMF_RESP_FUNC_COMPLETE)
+ dev_notice(dev, "abort task: rc=%d\n", rc);
+ return rc;
+}
+
+static int hisi_sas_abort_task_set(struct domain_device *device, u8 *lun)
+{
+ struct hisi_sas_tmf_task tmf_task;
+ int rc = TMF_RESP_FUNC_FAILED;
+
+ tmf_task.tmf = TMF_ABORT_TASK_SET;
+ rc = hisi_sas_debug_issue_ssp_tmf(device, lun, &tmf_task);
+
+ return rc;
+}
+
+static int hisi_sas_clear_aca(struct domain_device *device, u8 *lun)
+{
+ int rc = TMF_RESP_FUNC_FAILED;
+ struct hisi_sas_tmf_task tmf_task;
+
+ tmf_task.tmf = TMF_CLEAR_ACA;
+ rc = hisi_sas_debug_issue_ssp_tmf(device, lun, &tmf_task);
+
+ return rc;
+}
+
+static int hisi_sas_debug_I_T_nexus_reset(struct domain_device *device)
+{
+ struct sas_phy *phy = sas_get_local_phy(device);
+ int rc, reset_type = (device->dev_type == SAS_SATA_DEV ||
+ (device->tproto & SAS_PROTOCOL_STP)) ? 0 : 1;
+ rc = sas_phy_reset(phy, reset_type);
+ sas_put_local_phy(phy);
+ msleep(2000);
+ return rc;
+}
+
+static int hisi_sas_I_T_nexus_reset(struct domain_device *device)
+{
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
+ unsigned long flags;
+ int rc = TMF_RESP_FUNC_FAILED;
+
+ if (sas_dev->dev_status != HISI_SAS_DEV_EH)
+ return TMF_RESP_FUNC_FAILED;
+ sas_dev->dev_status = HISI_SAS_DEV_NORMAL;
+
+ rc = hisi_sas_debug_I_T_nexus_reset(device);
+
+ spin_lock_irqsave(&hisi_hba->lock, flags);
+ hisi_sas_release_task(hisi_hba, device);
+ spin_unlock_irqrestore(&hisi_hba->lock, flags);
+
+ return 0;
+}
+
+static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun)
+{
+ struct hisi_sas_tmf_task tmf_task;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
+ struct device *dev = &hisi_hba->pdev->dev;
+ unsigned long flags;
+ int rc = TMF_RESP_FUNC_FAILED;
+
+ tmf_task.tmf = TMF_LU_RESET;
+ sas_dev->dev_status = HISI_SAS_DEV_EH;
+ rc = hisi_sas_debug_issue_ssp_tmf(device, lun, &tmf_task);
+ if (rc == TMF_RESP_FUNC_COMPLETE) {
+ spin_lock_irqsave(&hisi_hba->lock, flags);
+ hisi_sas_release_task(hisi_hba, device);
+ spin_unlock_irqrestore(&hisi_hba->lock, flags);
+ }
+
+ /* If failed, fall-through I_T_Nexus reset */
+ dev_err(dev, "lu_reset: for device[%llx]:rc= %d\n",
+ sas_dev->device_id, rc);
+ return rc;
+}
+
+static int hisi_sas_query_task(struct sas_task *task)
+{
+ struct scsi_lun lun;
+ struct hisi_sas_tmf_task tmf_task;
+ int rc = TMF_RESP_FUNC_FAILED;
+
+ if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) {
+ struct scsi_cmnd *cmnd = task->uldd_task;
+ struct domain_device *device = task->dev;
+ struct hisi_sas_slot *slot = task->lldd_task;
+ u32 tag = slot->idx;
+
+ int_to_scsilun(cmnd->device->lun, &lun);
+ tmf_task.tmf = TMF_QUERY_TASK;
+ tmf_task.tag_of_task_to_be_managed = cpu_to_le16(tag);
+
+ rc = hisi_sas_debug_issue_ssp_tmf(device,
+ lun.scsi_lun,
+ &tmf_task);
+ switch (rc) {
+ /* The task is still in Lun, release it then */
+ case TMF_RESP_FUNC_SUCC:
+ /* The task is not in Lun or failed, reset the phy */
+ case TMF_RESP_FUNC_FAILED:
+ case TMF_RESP_FUNC_COMPLETE:
+ break;
+ }
+ }
+ return rc;
+}
+
+static void hisi_sas_port_formed(struct asd_sas_phy *sas_phy)
+{
+ hisi_sas_port_notify_formed(sas_phy);
+}
+
+static void hisi_sas_port_deformed(struct asd_sas_phy *sas_phy)
+{
+ hisi_sas_port_notify_deformed(sas_phy);
+}
+
+static void hisi_sas_phy_disconnected(struct hisi_sas_phy *phy)
+{
+ phy->phy_attached = 0;
+ phy->phy_type = 0;
+ phy->port = NULL;
+}
+
+void hisi_sas_phy_down(struct hisi_hba *hisi_hba, int phy_no, int rdy)
+{
+ struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct sas_ha_struct *sas_ha = &hisi_hba->sha;
+
+ if (rdy) {
+ /* Phy down but ready */
+ hisi_sas_bytes_dmaed(hisi_hba, phy_no);
+ hisi_sas_port_notify_formed(sas_phy);
+ } else {
+ struct hisi_sas_port *port = phy->port;
+
+ /* Phy down and not ready */
+ sas_ha->notify_phy_event(sas_phy, PHYE_LOSS_OF_SIGNAL);
+ sas_phy_disconnected(sas_phy);
+
+ if (port) {
+ if (phy->phy_type & PORT_TYPE_SAS) {
+ int port_id = port->id;
+
+ if (!hisi_hba->hw->get_wideport_bitmap(hisi_hba,
+ port_id))
+ port->port_attached = 0;
+ } else if (phy->phy_type & PORT_TYPE_SATA)
+ port->port_attached = 0;
+ }
+ hisi_sas_phy_disconnected(phy);
+ }
+}
+EXPORT_SYMBOL_GPL(hisi_sas_phy_down);
+
+static struct scsi_transport_template *hisi_sas_stt;
+
+static struct scsi_host_template hisi_sas_sht = {
+ .module = THIS_MODULE,
+ .name = DRV_NAME,
+ .queuecommand = sas_queuecommand,
+ .target_alloc = sas_target_alloc,
+ .slave_configure = sas_slave_configure,
+ .scan_finished = hisi_sas_scan_finished,
+ .scan_start = hisi_sas_scan_start,
+ .change_queue_depth = sas_change_queue_depth,
+ .bios_param = sas_bios_param,
+ .can_queue = 1,
+ .this_id = -1,
+ .sg_tablesize = SG_ALL,
+ .max_sectors = SCSI_DEFAULT_MAX_SECTORS,
+ .use_clustering = ENABLE_CLUSTERING,
+ .eh_device_reset_handler = sas_eh_device_reset_handler,
+ .eh_bus_reset_handler = sas_eh_bus_reset_handler,
+ .target_destroy = sas_target_destroy,
+ .ioctl = sas_ioctl,
+};
+
+static struct sas_domain_function_template hisi_sas_transport_ops = {
+ .lldd_dev_found = hisi_sas_dev_found,
+ .lldd_dev_gone = hisi_sas_dev_gone,
+ .lldd_execute_task = hisi_sas_queue_command,
+ .lldd_control_phy = hisi_sas_control_phy,
+ .lldd_abort_task = hisi_sas_abort_task,
+ .lldd_abort_task_set = hisi_sas_abort_task_set,
+ .lldd_clear_aca = hisi_sas_clear_aca,
+ .lldd_I_T_nexus_reset = hisi_sas_I_T_nexus_reset,
+ .lldd_lu_reset = hisi_sas_lu_reset,
+ .lldd_query_task = hisi_sas_query_task,
+ .lldd_port_formed = hisi_sas_port_formed,
+ .lldd_port_deformed = hisi_sas_port_deformed,
+};
+
+static int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost)
+{
+ int i, s;
+ struct platform_device *pdev = hisi_hba->pdev;
+ struct device *dev = &pdev->dev;
+
+ spin_lock_init(&hisi_hba->lock);
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ hisi_sas_phy_init(hisi_hba, i);
+ hisi_hba->port[i].port_attached = 0;
+ hisi_hba->port[i].id = -1;
+ INIT_LIST_HEAD(&hisi_hba->port[i].list);
+ }
+
+ for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) {
+ hisi_hba->devices[i].dev_type = SAS_PHY_UNUSED;
+ hisi_hba->devices[i].device_id = i;
+ hisi_hba->devices[i].dev_status = HISI_SAS_DEV_NORMAL;
+ }
+
+ for (i = 0; i < hisi_hba->queue_count; i++) {
+ struct hisi_sas_cq *cq = &hisi_hba->cq[i];
+
+ /* Completion queue structure */
+ cq->id = i;
+ cq->hisi_hba = hisi_hba;
+
+ /* Delivery queue */
+ s = sizeof(struct hisi_sas_cmd_hdr) * HISI_SAS_QUEUE_SLOTS;
+ hisi_hba->cmd_hdr[i] = dma_alloc_coherent(dev, s,
+ &hisi_hba->cmd_hdr_dma[i], GFP_KERNEL);
+ if (!hisi_hba->cmd_hdr[i])
+ goto err_out;
+ memset(hisi_hba->cmd_hdr[i], 0, s);
+
+ /* Completion queue */
+ s = hisi_hba->hw->complete_hdr_size * HISI_SAS_QUEUE_SLOTS;
+ hisi_hba->complete_hdr[i] = dma_alloc_coherent(dev, s,
+ &hisi_hba->complete_hdr_dma[i], GFP_KERNEL);
+ if (!hisi_hba->complete_hdr[i])
+ goto err_out;
+ memset(hisi_hba->complete_hdr[i], 0, s);
+ }
+
+ s = HISI_SAS_STATUS_BUF_SZ;
+ hisi_hba->status_buffer_pool = dma_pool_create("status_buffer",
+ dev, s, 16, 0);
+ if (!hisi_hba->status_buffer_pool)
+ goto err_out;
+
+ s = HISI_SAS_COMMAND_TABLE_SZ;
+ hisi_hba->command_table_pool = dma_pool_create("command_table",
+ dev, s, 16, 0);
+ if (!hisi_hba->command_table_pool)
+ goto err_out;
+
+ s = HISI_SAS_MAX_ITCT_ENTRIES * sizeof(struct hisi_sas_itct);
+ hisi_hba->itct = dma_alloc_coherent(dev, s, &hisi_hba->itct_dma,
+ GFP_KERNEL);
+ if (!hisi_hba->itct)
+ goto err_out;
+
+ memset(hisi_hba->itct, 0, s);
+
+ hisi_hba->slot_info = devm_kcalloc(dev, HISI_SAS_COMMAND_ENTRIES,
+ sizeof(struct hisi_sas_slot),
+ GFP_KERNEL);
+ if (!hisi_hba->slot_info)
+ goto err_out;
+
+ s = HISI_SAS_COMMAND_ENTRIES * sizeof(struct hisi_sas_iost);
+ hisi_hba->iost = dma_alloc_coherent(dev, s, &hisi_hba->iost_dma,
+ GFP_KERNEL);
+ if (!hisi_hba->iost)
+ goto err_out;
+
+ memset(hisi_hba->iost, 0, s);
+
+ s = HISI_SAS_COMMAND_ENTRIES * sizeof(struct hisi_sas_breakpoint);
+ hisi_hba->breakpoint = dma_alloc_coherent(dev, s,
+ &hisi_hba->breakpoint_dma, GFP_KERNEL);
+ if (!hisi_hba->breakpoint)
+ goto err_out;
+
+ memset(hisi_hba->breakpoint, 0, s);
+
+ hisi_hba->slot_index_count = HISI_SAS_COMMAND_ENTRIES;
+ s = hisi_hba->slot_index_count / sizeof(unsigned long);
+ hisi_hba->slot_index_tags = devm_kzalloc(dev, s, GFP_KERNEL);
+ if (!hisi_hba->slot_index_tags)
+ goto err_out;
+
+ hisi_hba->sge_page_pool = dma_pool_create("status_sge", dev,
+ sizeof(struct hisi_sas_sge_page), 16, 0);
+ if (!hisi_hba->sge_page_pool)
+ goto err_out;
+
+ s = sizeof(struct hisi_sas_initial_fis) * HISI_SAS_MAX_PHYS;
+ hisi_hba->initial_fis = dma_alloc_coherent(dev, s,
+ &hisi_hba->initial_fis_dma, GFP_KERNEL);
+ if (!hisi_hba->initial_fis)
+ goto err_out;
+ memset(hisi_hba->initial_fis, 0, s);
+
+ s = HISI_SAS_COMMAND_ENTRIES * sizeof(struct hisi_sas_breakpoint) * 2;
+ hisi_hba->sata_breakpoint = dma_alloc_coherent(dev, s,
+ &hisi_hba->sata_breakpoint_dma, GFP_KERNEL);
+ if (!hisi_hba->sata_breakpoint)
+ goto err_out;
+ memset(hisi_hba->sata_breakpoint, 0, s);
+
+ hisi_sas_slot_index_init(hisi_hba);
+
+ hisi_hba->wq = create_singlethread_workqueue(dev_name(dev));
+ if (!hisi_hba->wq) {
+ dev_err(dev, "sas_alloc: failed to create workqueue\n");
+ goto err_out;
+ }
+
+ return 0;
+err_out:
+ return -ENOMEM;
+}
+
+static void hisi_sas_free(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = &hisi_hba->pdev->dev;
+ int i, s;
+
+ for (i = 0; i < hisi_hba->queue_count; i++) {
+ s = sizeof(struct hisi_sas_cmd_hdr) * HISI_SAS_QUEUE_SLOTS;
+ if (hisi_hba->cmd_hdr[i])
+ dma_free_coherent(dev, s,
+ hisi_hba->cmd_hdr[i],
+ hisi_hba->cmd_hdr_dma[i]);
+
+ s = hisi_hba->hw->complete_hdr_size * HISI_SAS_QUEUE_SLOTS;
+ if (hisi_hba->complete_hdr[i])
+ dma_free_coherent(dev, s,
+ hisi_hba->complete_hdr[i],
+ hisi_hba->complete_hdr_dma[i]);
+ }
+
+ dma_pool_destroy(hisi_hba->status_buffer_pool);
+ dma_pool_destroy(hisi_hba->command_table_pool);
+ dma_pool_destroy(hisi_hba->sge_page_pool);
+
+ s = HISI_SAS_MAX_ITCT_ENTRIES * sizeof(struct hisi_sas_itct);
+ if (hisi_hba->itct)
+ dma_free_coherent(dev, s,
+ hisi_hba->itct, hisi_hba->itct_dma);
+
+ s = HISI_SAS_COMMAND_ENTRIES * sizeof(struct hisi_sas_iost);
+ if (hisi_hba->iost)
+ dma_free_coherent(dev, s,
+ hisi_hba->iost, hisi_hba->iost_dma);
+
+ s = HISI_SAS_COMMAND_ENTRIES * sizeof(struct hisi_sas_breakpoint);
+ if (hisi_hba->breakpoint)
+ dma_free_coherent(dev, s,
+ hisi_hba->breakpoint,
+ hisi_hba->breakpoint_dma);
+
+
+ s = sizeof(struct hisi_sas_initial_fis) * HISI_SAS_MAX_PHYS;
+ if (hisi_hba->initial_fis)
+ dma_free_coherent(dev, s,
+ hisi_hba->initial_fis,
+ hisi_hba->initial_fis_dma);
+
+ s = HISI_SAS_COMMAND_ENTRIES * sizeof(struct hisi_sas_breakpoint) * 2;
+ if (hisi_hba->sata_breakpoint)
+ dma_free_coherent(dev, s,
+ hisi_hba->sata_breakpoint,
+ hisi_hba->sata_breakpoint_dma);
+
+ if (hisi_hba->wq)
+ destroy_workqueue(hisi_hba->wq);
+}
+
+static struct Scsi_Host *hisi_sas_shost_alloc(struct platform_device *pdev,
+ const struct hisi_sas_hw *hw)
+{
+ struct resource *res;
+ struct Scsi_Host *shost;
+ struct hisi_hba *hisi_hba;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+ struct property *sas_addr_prop;
+
+ shost = scsi_host_alloc(&hisi_sas_sht, sizeof(*hisi_hba));
+ if (!shost)
+ goto err_out;
+ hisi_hba = shost_priv(shost);
+
+ hisi_hba->hw = hw;
+ hisi_hba->pdev = pdev;
+ hisi_hba->shost = shost;
+ SHOST_TO_SAS_HA(shost) = &hisi_hba->sha;
+
+ init_timer(&hisi_hba->timer);
+
+ sas_addr_prop = of_find_property(np, "sas-addr", NULL);
+ if (!sas_addr_prop || (sas_addr_prop->length != SAS_ADDR_SIZE))
+ goto err_out;
+ memcpy(hisi_hba->sas_addr, sas_addr_prop->value, SAS_ADDR_SIZE);
+
+ if (of_property_read_u32(np, "ctrl-reset-reg",
+ &hisi_hba->ctrl_reset_reg))
+ goto err_out;
+
+ if (of_property_read_u32(np, "ctrl-reset-sts-reg",
+ &hisi_hba->ctrl_reset_sts_reg))
+ goto err_out;
+
+ if (of_property_read_u32(np, "ctrl-clock-ena-reg",
+ &hisi_hba->ctrl_clock_ena_reg))
+ goto err_out;
+
+ if (of_property_read_u32(np, "phy-count", &hisi_hba->n_phy))
+ goto err_out;
+
+ if (of_property_read_u32(np, "queue-count", &hisi_hba->queue_count))
+ goto err_out;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hisi_hba->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(hisi_hba->regs))
+ goto err_out;
+
+ hisi_hba->ctrl = syscon_regmap_lookup_by_phandle(
+ np, "hisilicon,sas-syscon");
+ if (IS_ERR(hisi_hba->ctrl))
+ goto err_out;
+
+ if (hisi_sas_alloc(hisi_hba, shost)) {
+ hisi_sas_free(hisi_hba);
+ goto err_out;
+ }
+
+ return shost;
+err_out:
+ dev_err(dev, "shost alloc failed\n");
+ return NULL;
+}
+
+static void hisi_sas_init_add(struct hisi_hba *hisi_hba)
+{
+ int i;
+
+ for (i = 0; i < hisi_hba->n_phy; i++)
+ memcpy(&hisi_hba->phy[i].dev_sas_addr,
+ hisi_hba->sas_addr,
+ SAS_ADDR_SIZE);
+}
+
+int hisi_sas_probe(struct platform_device *pdev,
+ const struct hisi_sas_hw *hw)
+{
+ struct Scsi_Host *shost;
+ struct hisi_hba *hisi_hba;
+ struct device *dev = &pdev->dev;
+ struct asd_sas_phy **arr_phy;
+ struct asd_sas_port **arr_port;
+ struct sas_ha_struct *sha;
+ int rc, phy_nr, port_nr, i;
+
+ shost = hisi_sas_shost_alloc(pdev, hw);
+ if (!shost) {
+ rc = -ENOMEM;
+ goto err_out_ha;
+ }
+
+ sha = SHOST_TO_SAS_HA(shost);
+ hisi_hba = shost_priv(shost);
+ platform_set_drvdata(pdev, sha);
+
+ if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)) &&
+ dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32))) {
+ dev_err(dev, "No usable DMA addressing method\n");
+ rc = -EIO;
+ goto err_out_ha;
+ }
+
+ phy_nr = port_nr = hisi_hba->n_phy;
+
+ arr_phy = devm_kcalloc(dev, phy_nr, sizeof(void *), GFP_KERNEL);
+ arr_port = devm_kcalloc(dev, port_nr, sizeof(void *), GFP_KERNEL);
+ if (!arr_phy || !arr_port)
+ return -ENOMEM;
+
+ sha->sas_phy = arr_phy;
+ sha->sas_port = arr_port;
+ sha->core.shost = shost;
+ sha->lldd_ha = hisi_hba;
+
+ shost->transportt = hisi_sas_stt;
+ shost->max_id = HISI_SAS_MAX_DEVICES;
+ shost->max_lun = ~0;
+ shost->max_channel = 1;
+ shost->max_cmd_len = 16;
+ shost->sg_tablesize = min_t(u16, SG_ALL, HISI_SAS_SGE_PAGE_CNT);
+ shost->can_queue = HISI_SAS_COMMAND_ENTRIES;
+ shost->cmd_per_lun = HISI_SAS_COMMAND_ENTRIES;
+
+ sha->sas_ha_name = DRV_NAME;
+ sha->dev = &hisi_hba->pdev->dev;
+ sha->lldd_module = THIS_MODULE;
+ sha->sas_addr = &hisi_hba->sas_addr[0];
+ sha->num_phys = hisi_hba->n_phy;
+ sha->core.shost = hisi_hba->shost;
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ sha->sas_phy[i] = &hisi_hba->phy[i].sas_phy;
+ sha->sas_port[i] = &hisi_hba->port[i].sas_port;
+ }
+
+ hisi_sas_init_add(hisi_hba);
+
+ rc = hisi_hba->hw->hw_init(hisi_hba);
+ if (rc)
+ goto err_out_ha;
+
+ rc = scsi_add_host(shost, &pdev->dev);
+ if (rc)
+ goto err_out_ha;
+
+ rc = sas_register_ha(sha);
+ if (rc)
+ goto err_out_register_ha;
+
+ scsi_scan_host(shost);
+
+ return 0;
+
+err_out_register_ha:
+ scsi_remove_host(shost);
+err_out_ha:
+ kfree(shost);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(hisi_sas_probe);
+
+int hisi_sas_remove(struct platform_device *pdev)
+{
+ struct sas_ha_struct *sha = platform_get_drvdata(pdev);
+ struct hisi_hba *hisi_hba = sha->lldd_ha;
+
+ scsi_remove_host(sha->core.shost);
+ sas_unregister_ha(sha);
+ sas_remove_host(sha->core.shost);
+
+ hisi_sas_free(hisi_hba);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hisi_sas_remove);
+
+static __init int hisi_sas_init(void)
+{
+ pr_info("hisi_sas: driver version %s\n", DRV_VERSION);
+
+ hisi_sas_stt = sas_domain_attach_transport(&hisi_sas_transport_ops);
+ if (!hisi_sas_stt)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static __exit void hisi_sas_exit(void)
+{
+ sas_release_transport(hisi_sas_stt);
+}
+
+module_init(hisi_sas_init);
+module_exit(hisi_sas_exit);
+
+MODULE_VERSION(DRV_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Garry <john.garry@huawei.com>");
+MODULE_DESCRIPTION("HISILICON SAS controller driver");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
new file mode 100644
index 000000000000..d54381149c0d
--- /dev/null
+++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
@@ -0,0 +1,1839 @@
+/*
+ * Copyright (c) 2015 Linaro Ltd.
+ * Copyright (c) 2015 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include "hisi_sas.h"
+#define DRV_NAME "hisi_sas_v1_hw"
+
+/* global registers need init*/
+#define DLVRY_QUEUE_ENABLE 0x0
+#define IOST_BASE_ADDR_LO 0x8
+#define IOST_BASE_ADDR_HI 0xc
+#define ITCT_BASE_ADDR_LO 0x10
+#define ITCT_BASE_ADDR_HI 0x14
+#define BROKEN_MSG_ADDR_LO 0x18
+#define BROKEN_MSG_ADDR_HI 0x1c
+#define PHY_CONTEXT 0x20
+#define PHY_STATE 0x24
+#define PHY_PORT_NUM_MA 0x28
+#define PORT_STATE 0x2c
+#define PHY_CONN_RATE 0x30
+#define HGC_TRANS_TASK_CNT_LIMIT 0x38
+#define AXI_AHB_CLK_CFG 0x3c
+#define HGC_SAS_TXFAIL_RETRY_CTRL 0x84
+#define HGC_GET_ITV_TIME 0x90
+#define DEVICE_MSG_WORK_MODE 0x94
+#define I_T_NEXUS_LOSS_TIME 0xa0
+#define BUS_INACTIVE_LIMIT_TIME 0xa8
+#define REJECT_TO_OPEN_LIMIT_TIME 0xac
+#define CFG_AGING_TIME 0xbc
+#define CFG_AGING_TIME_ITCT_REL_OFF 0
+#define CFG_AGING_TIME_ITCT_REL_MSK (0x1 << CFG_AGING_TIME_ITCT_REL_OFF)
+#define HGC_DFX_CFG2 0xc0
+#define FIS_LIST_BADDR_L 0xc4
+#define CFG_1US_TIMER_TRSH 0xcc
+#define CFG_SAS_CONFIG 0xd4
+#define HGC_IOST_ECC_ADDR 0x140
+#define HGC_IOST_ECC_ADDR_BAD_OFF 16
+#define HGC_IOST_ECC_ADDR_BAD_MSK (0x3ff << HGC_IOST_ECC_ADDR_BAD_OFF)
+#define HGC_DQ_ECC_ADDR 0x144
+#define HGC_DQ_ECC_ADDR_BAD_OFF 16
+#define HGC_DQ_ECC_ADDR_BAD_MSK (0xfff << HGC_DQ_ECC_ADDR_BAD_OFF)
+#define HGC_INVLD_DQE_INFO 0x148
+#define HGC_INVLD_DQE_INFO_DQ_OFF 0
+#define HGC_INVLD_DQE_INFO_DQ_MSK (0xffff << HGC_INVLD_DQE_INFO_DQ_OFF)
+#define HGC_INVLD_DQE_INFO_TYPE_OFF 16
+#define HGC_INVLD_DQE_INFO_TYPE_MSK (0x1 << HGC_INVLD_DQE_INFO_TYPE_OFF)
+#define HGC_INVLD_DQE_INFO_FORCE_OFF 17
+#define HGC_INVLD_DQE_INFO_FORCE_MSK (0x1 << HGC_INVLD_DQE_INFO_FORCE_OFF)
+#define HGC_INVLD_DQE_INFO_PHY_OFF 18
+#define HGC_INVLD_DQE_INFO_PHY_MSK (0x1 << HGC_INVLD_DQE_INFO_PHY_OFF)
+#define HGC_INVLD_DQE_INFO_ABORT_OFF 19
+#define HGC_INVLD_DQE_INFO_ABORT_MSK (0x1 << HGC_INVLD_DQE_INFO_ABORT_OFF)
+#define HGC_INVLD_DQE_INFO_IPTT_OF_OFF 20
+#define HGC_INVLD_DQE_INFO_IPTT_OF_MSK (0x1 << HGC_INVLD_DQE_INFO_IPTT_OF_OFF)
+#define HGC_INVLD_DQE_INFO_SSP_ERR_OFF 21
+#define HGC_INVLD_DQE_INFO_SSP_ERR_MSK (0x1 << HGC_INVLD_DQE_INFO_SSP_ERR_OFF)
+#define HGC_INVLD_DQE_INFO_OFL_OFF 22
+#define HGC_INVLD_DQE_INFO_OFL_MSK (0x1 << HGC_INVLD_DQE_INFO_OFL_OFF)
+#define HGC_ITCT_ECC_ADDR 0x150
+#define HGC_ITCT_ECC_ADDR_BAD_OFF 16
+#define HGC_ITCT_ECC_ADDR_BAD_MSK (0x3ff << HGC_ITCT_ECC_ADDR_BAD_OFF)
+#define HGC_AXI_FIFO_ERR_INFO 0x154
+#define INT_COAL_EN 0x1bc
+#define OQ_INT_COAL_TIME 0x1c0
+#define OQ_INT_COAL_CNT 0x1c4
+#define ENT_INT_COAL_TIME 0x1c8
+#define ENT_INT_COAL_CNT 0x1cc
+#define OQ_INT_SRC 0x1d0
+#define OQ_INT_SRC_MSK 0x1d4
+#define ENT_INT_SRC1 0x1d8
+#define ENT_INT_SRC2 0x1dc
+#define ENT_INT_SRC2_DQ_CFG_ERR_OFF 25
+#define ENT_INT_SRC2_DQ_CFG_ERR_MSK (0x1 << ENT_INT_SRC2_DQ_CFG_ERR_OFF)
+#define ENT_INT_SRC2_CQ_CFG_ERR_OFF 27
+#define ENT_INT_SRC2_CQ_CFG_ERR_MSK (0x1 << ENT_INT_SRC2_CQ_CFG_ERR_OFF)
+#define ENT_INT_SRC2_AXI_WRONG_INT_OFF 28
+#define ENT_INT_SRC2_AXI_WRONG_INT_MSK (0x1 << ENT_INT_SRC2_AXI_WRONG_INT_OFF)
+#define ENT_INT_SRC2_AXI_OVERLF_INT_OFF 29
+#define ENT_INT_SRC2_AXI_OVERLF_INT_MSK (0x1 << ENT_INT_SRC2_AXI_OVERLF_INT_OFF)
+#define ENT_INT_SRC_MSK1 0x1e0
+#define ENT_INT_SRC_MSK2 0x1e4
+#define SAS_ECC_INTR 0x1e8
+#define SAS_ECC_INTR_DQ_ECC1B_OFF 0
+#define SAS_ECC_INTR_DQ_ECC1B_MSK (0x1 << SAS_ECC_INTR_DQ_ECC1B_OFF)
+#define SAS_ECC_INTR_DQ_ECCBAD_OFF 1
+#define SAS_ECC_INTR_DQ_ECCBAD_MSK (0x1 << SAS_ECC_INTR_DQ_ECCBAD_OFF)
+#define SAS_ECC_INTR_IOST_ECC1B_OFF 2
+#define SAS_ECC_INTR_IOST_ECC1B_MSK (0x1 << SAS_ECC_INTR_IOST_ECC1B_OFF)
+#define SAS_ECC_INTR_IOST_ECCBAD_OFF 3
+#define SAS_ECC_INTR_IOST_ECCBAD_MSK (0x1 << SAS_ECC_INTR_IOST_ECCBAD_OFF)
+#define SAS_ECC_INTR_ITCT_ECC1B_OFF 4
+#define SAS_ECC_INTR_ITCT_ECC1B_MSK (0x1 << SAS_ECC_INTR_ITCT_ECC1B_OFF)
+#define SAS_ECC_INTR_ITCT_ECCBAD_OFF 5
+#define SAS_ECC_INTR_ITCT_ECCBAD_MSK (0x1 << SAS_ECC_INTR_ITCT_ECCBAD_OFF)
+#define SAS_ECC_INTR_MSK 0x1ec
+#define HGC_ERR_STAT_EN 0x238
+#define DLVRY_Q_0_BASE_ADDR_LO 0x260
+#define DLVRY_Q_0_BASE_ADDR_HI 0x264
+#define DLVRY_Q_0_DEPTH 0x268
+#define DLVRY_Q_0_WR_PTR 0x26c
+#define DLVRY_Q_0_RD_PTR 0x270
+#define COMPL_Q_0_BASE_ADDR_LO 0x4e0
+#define COMPL_Q_0_BASE_ADDR_HI 0x4e4
+#define COMPL_Q_0_DEPTH 0x4e8
+#define COMPL_Q_0_WR_PTR 0x4ec
+#define COMPL_Q_0_RD_PTR 0x4f0
+#define HGC_ECC_ERR 0x7d0
+
+/* phy registers need init */
+#define PORT_BASE (0x800)
+
+#define PHY_CFG (PORT_BASE + 0x0)
+#define PHY_CFG_ENA_OFF 0
+#define PHY_CFG_ENA_MSK (0x1 << PHY_CFG_ENA_OFF)
+#define PHY_CFG_DC_OPT_OFF 2
+#define PHY_CFG_DC_OPT_MSK (0x1 << PHY_CFG_DC_OPT_OFF)
+#define PROG_PHY_LINK_RATE (PORT_BASE + 0xc)
+#define PROG_PHY_LINK_RATE_MAX_OFF 0
+#define PROG_PHY_LINK_RATE_MAX_MSK (0xf << PROG_PHY_LINK_RATE_MAX_OFF)
+#define PROG_PHY_LINK_RATE_MIN_OFF 4
+#define PROG_PHY_LINK_RATE_MIN_MSK (0xf << PROG_PHY_LINK_RATE_MIN_OFF)
+#define PROG_PHY_LINK_RATE_OOB_OFF 8
+#define PROG_PHY_LINK_RATE_OOB_MSK (0xf << PROG_PHY_LINK_RATE_OOB_OFF)
+#define PHY_CTRL (PORT_BASE + 0x14)
+#define PHY_CTRL_RESET_OFF 0
+#define PHY_CTRL_RESET_MSK (0x1 << PHY_CTRL_RESET_OFF)
+#define PHY_RATE_NEGO (PORT_BASE + 0x30)
+#define PHY_PCN (PORT_BASE + 0x44)
+#define SL_TOUT_CFG (PORT_BASE + 0x8c)
+#define SL_CONTROL (PORT_BASE + 0x94)
+#define SL_CONTROL_NOTIFY_EN_OFF 0
+#define SL_CONTROL_NOTIFY_EN_MSK (0x1 << SL_CONTROL_NOTIFY_EN_OFF)
+#define TX_ID_DWORD0 (PORT_BASE + 0x9c)
+#define TX_ID_DWORD1 (PORT_BASE + 0xa0)
+#define TX_ID_DWORD2 (PORT_BASE + 0xa4)
+#define TX_ID_DWORD3 (PORT_BASE + 0xa8)
+#define TX_ID_DWORD4 (PORT_BASE + 0xaC)
+#define TX_ID_DWORD5 (PORT_BASE + 0xb0)
+#define TX_ID_DWORD6 (PORT_BASE + 0xb4)
+#define RX_IDAF_DWORD0 (PORT_BASE + 0xc4)
+#define RX_IDAF_DWORD1 (PORT_BASE + 0xc8)
+#define RX_IDAF_DWORD2 (PORT_BASE + 0xcc)
+#define RX_IDAF_DWORD3 (PORT_BASE + 0xd0)
+#define RX_IDAF_DWORD4 (PORT_BASE + 0xd4)
+#define RX_IDAF_DWORD5 (PORT_BASE + 0xd8)
+#define RX_IDAF_DWORD6 (PORT_BASE + 0xdc)
+#define RXOP_CHECK_CFG_H (PORT_BASE + 0xfc)
+#define DONE_RECEIVED_TIME (PORT_BASE + 0x12c)
+#define CON_CFG_DRIVER (PORT_BASE + 0x130)
+#define PHY_CONFIG2 (PORT_BASE + 0x1a8)
+#define PHY_CONFIG2_FORCE_TXDEEMPH_OFF 3
+#define PHY_CONFIG2_FORCE_TXDEEMPH_MSK (0x1 << PHY_CONFIG2_FORCE_TXDEEMPH_OFF)
+#define PHY_CONFIG2_TX_TRAIN_COMP_OFF 24
+#define PHY_CONFIG2_TX_TRAIN_COMP_MSK (0x1 << PHY_CONFIG2_TX_TRAIN_COMP_OFF)
+#define CHL_INT0 (PORT_BASE + 0x1b0)
+#define CHL_INT0_PHYCTRL_NOTRDY_OFF 0
+#define CHL_INT0_PHYCTRL_NOTRDY_MSK (0x1 << CHL_INT0_PHYCTRL_NOTRDY_OFF)
+#define CHL_INT0_SN_FAIL_NGR_OFF 2
+#define CHL_INT0_SN_FAIL_NGR_MSK (0x1 << CHL_INT0_SN_FAIL_NGR_OFF)
+#define CHL_INT0_DWS_LOST_OFF 4
+#define CHL_INT0_DWS_LOST_MSK (0x1 << CHL_INT0_DWS_LOST_OFF)
+#define CHL_INT0_SL_IDAF_FAIL_OFF 10
+#define CHL_INT0_SL_IDAF_FAIL_MSK (0x1 << CHL_INT0_SL_IDAF_FAIL_OFF)
+#define CHL_INT0_ID_TIMEOUT_OFF 11
+#define CHL_INT0_ID_TIMEOUT_MSK (0x1 << CHL_INT0_ID_TIMEOUT_OFF)
+#define CHL_INT0_SL_OPAF_FAIL_OFF 12
+#define CHL_INT0_SL_OPAF_FAIL_MSK (0x1 << CHL_INT0_SL_OPAF_FAIL_OFF)
+#define CHL_INT0_SL_PS_FAIL_OFF 21
+#define CHL_INT0_SL_PS_FAIL_MSK (0x1 << CHL_INT0_SL_PS_FAIL_OFF)
+#define CHL_INT1 (PORT_BASE + 0x1b4)
+#define CHL_INT2 (PORT_BASE + 0x1b8)
+#define CHL_INT2_SL_RX_BC_ACK_OFF 2
+#define CHL_INT2_SL_RX_BC_ACK_MSK (0x1 << CHL_INT2_SL_RX_BC_ACK_OFF)
+#define CHL_INT2_SL_PHY_ENA_OFF 6
+#define CHL_INT2_SL_PHY_ENA_MSK (0x1 << CHL_INT2_SL_PHY_ENA_OFF)
+#define CHL_INT0_MSK (PORT_BASE + 0x1bc)
+#define CHL_INT0_MSK_PHYCTRL_NOTRDY_OFF 0
+#define CHL_INT0_MSK_PHYCTRL_NOTRDY_MSK (0x1 << CHL_INT0_MSK_PHYCTRL_NOTRDY_OFF)
+#define CHL_INT1_MSK (PORT_BASE + 0x1c0)
+#define CHL_INT2_MSK (PORT_BASE + 0x1c4)
+#define CHL_INT_COAL_EN (PORT_BASE + 0x1d0)
+#define DMA_TX_STATUS (PORT_BASE + 0x2d0)
+#define DMA_TX_STATUS_BUSY_OFF 0
+#define DMA_TX_STATUS_BUSY_MSK (0x1 << DMA_TX_STATUS_BUSY_OFF)
+#define DMA_RX_STATUS (PORT_BASE + 0x2e8)
+#define DMA_RX_STATUS_BUSY_OFF 0
+#define DMA_RX_STATUS_BUSY_MSK (0x1 << DMA_RX_STATUS_BUSY_OFF)
+
+#define AXI_CFG 0x5100
+#define RESET_VALUE 0x7ffff
+
+/* HW dma structures */
+/* Delivery queue header */
+/* dw0 */
+#define CMD_HDR_RESP_REPORT_OFF 5
+#define CMD_HDR_RESP_REPORT_MSK 0x20
+#define CMD_HDR_TLR_CTRL_OFF 6
+#define CMD_HDR_TLR_CTRL_MSK 0xc0
+#define CMD_HDR_PORT_OFF 17
+#define CMD_HDR_PORT_MSK 0xe0000
+#define CMD_HDR_PRIORITY_OFF 27
+#define CMD_HDR_PRIORITY_MSK 0x8000000
+#define CMD_HDR_MODE_OFF 28
+#define CMD_HDR_MODE_MSK 0x10000000
+#define CMD_HDR_CMD_OFF 29
+#define CMD_HDR_CMD_MSK 0xe0000000
+/* dw1 */
+#define CMD_HDR_VERIFY_DTL_OFF 10
+#define CMD_HDR_VERIFY_DTL_MSK 0x400
+#define CMD_HDR_SSP_FRAME_TYPE_OFF 13
+#define CMD_HDR_SSP_FRAME_TYPE_MSK 0xe000
+#define CMD_HDR_DEVICE_ID_OFF 16
+#define CMD_HDR_DEVICE_ID_MSK 0xffff0000
+/* dw2 */
+#define CMD_HDR_CFL_OFF 0
+#define CMD_HDR_CFL_MSK 0x1ff
+#define CMD_HDR_MRFL_OFF 15
+#define CMD_HDR_MRFL_MSK 0xff8000
+#define CMD_HDR_FIRST_BURST_OFF 25
+#define CMD_HDR_FIRST_BURST_MSK 0x2000000
+/* dw3 */
+#define CMD_HDR_IPTT_OFF 0
+#define CMD_HDR_IPTT_MSK 0xffff
+/* dw6 */
+#define CMD_HDR_DATA_SGL_LEN_OFF 16
+#define CMD_HDR_DATA_SGL_LEN_MSK 0xffff0000
+
+/* Completion header */
+#define CMPLT_HDR_IPTT_OFF 0
+#define CMPLT_HDR_IPTT_MSK (0xffff << CMPLT_HDR_IPTT_OFF)
+#define CMPLT_HDR_CMD_CMPLT_OFF 17
+#define CMPLT_HDR_CMD_CMPLT_MSK (0x1 << CMPLT_HDR_CMD_CMPLT_OFF)
+#define CMPLT_HDR_ERR_RCRD_XFRD_OFF 18
+#define CMPLT_HDR_ERR_RCRD_XFRD_MSK (0x1 << CMPLT_HDR_ERR_RCRD_XFRD_OFF)
+#define CMPLT_HDR_RSPNS_XFRD_OFF 19
+#define CMPLT_HDR_RSPNS_XFRD_MSK (0x1 << CMPLT_HDR_RSPNS_XFRD_OFF)
+#define CMPLT_HDR_IO_CFG_ERR_OFF 27
+#define CMPLT_HDR_IO_CFG_ERR_MSK (0x1 << CMPLT_HDR_IO_CFG_ERR_OFF)
+
+/* ITCT header */
+/* qw0 */
+#define ITCT_HDR_DEV_TYPE_OFF 0
+#define ITCT_HDR_DEV_TYPE_MSK (0x3 << ITCT_HDR_DEV_TYPE_OFF)
+#define ITCT_HDR_VALID_OFF 2
+#define ITCT_HDR_VALID_MSK (0x1 << ITCT_HDR_VALID_OFF)
+#define ITCT_HDR_BREAK_REPLY_ENA_OFF 3
+#define ITCT_HDR_BREAK_REPLY_ENA_MSK (0x1 << ITCT_HDR_BREAK_REPLY_ENA_OFF)
+#define ITCT_HDR_AWT_CONTROL_OFF 4
+#define ITCT_HDR_AWT_CONTROL_MSK (0x1 << ITCT_HDR_AWT_CONTROL_OFF)
+#define ITCT_HDR_MAX_CONN_RATE_OFF 5
+#define ITCT_HDR_MAX_CONN_RATE_MSK (0xf << ITCT_HDR_MAX_CONN_RATE_OFF)
+#define ITCT_HDR_VALID_LINK_NUM_OFF 9
+#define ITCT_HDR_VALID_LINK_NUM_MSK (0xf << ITCT_HDR_VALID_LINK_NUM_OFF)
+#define ITCT_HDR_PORT_ID_OFF 13
+#define ITCT_HDR_PORT_ID_MSK (0x7 << ITCT_HDR_PORT_ID_OFF)
+#define ITCT_HDR_SMP_TIMEOUT_OFF 16
+#define ITCT_HDR_SMP_TIMEOUT_MSK (0xffff << ITCT_HDR_SMP_TIMEOUT_OFF)
+#define ITCT_HDR_MAX_BURST_BYTES_OFF 16
+#define ITCT_HDR_MAX_BURST_BYTES_MSK (0xffffffff << \
+ ITCT_MAX_BURST_BYTES_OFF)
+/* qw1 */
+#define ITCT_HDR_MAX_SAS_ADDR_OFF 0
+#define ITCT_HDR_MAX_SAS_ADDR_MSK (0xffffffffffffffff << \
+ ITCT_HDR_MAX_SAS_ADDR_OFF)
+/* qw2 */
+#define ITCT_HDR_IT_NEXUS_LOSS_TL_OFF 0
+#define ITCT_HDR_IT_NEXUS_LOSS_TL_MSK (0xffff << \
+ ITCT_HDR_IT_NEXUS_LOSS_TL_OFF)
+#define ITCT_HDR_BUS_INACTIVE_TL_OFF 16
+#define ITCT_HDR_BUS_INACTIVE_TL_MSK (0xffff << \
+ ITCT_HDR_BUS_INACTIVE_TL_OFF)
+#define ITCT_HDR_MAX_CONN_TL_OFF 32
+#define ITCT_HDR_MAX_CONN_TL_MSK (0xffff << \
+ ITCT_HDR_MAX_CONN_TL_OFF)
+#define ITCT_HDR_REJ_OPEN_TL_OFF 48
+#define ITCT_HDR_REJ_OPEN_TL_MSK (0xffff << \
+ ITCT_REJ_OPEN_TL_OFF)
+
+/* Err record header */
+#define ERR_HDR_DMA_TX_ERR_TYPE_OFF 0
+#define ERR_HDR_DMA_TX_ERR_TYPE_MSK (0xffff << ERR_HDR_DMA_TX_ERR_TYPE_OFF)
+#define ERR_HDR_DMA_RX_ERR_TYPE_OFF 16
+#define ERR_HDR_DMA_RX_ERR_TYPE_MSK (0xffff << ERR_HDR_DMA_RX_ERR_TYPE_OFF)
+
+struct hisi_sas_complete_v1_hdr {
+ __le32 data;
+};
+
+enum {
+ HISI_SAS_PHY_BCAST_ACK = 0,
+ HISI_SAS_PHY_SL_PHY_ENABLED,
+ HISI_SAS_PHY_INT_ABNORMAL,
+ HISI_SAS_PHY_INT_NR
+};
+
+enum {
+ DMA_TX_ERR_BASE = 0x0,
+ DMA_RX_ERR_BASE = 0x100,
+ TRANS_TX_FAIL_BASE = 0x200,
+ TRANS_RX_FAIL_BASE = 0x300,
+
+ /* dma tx */
+ DMA_TX_DIF_CRC_ERR = DMA_TX_ERR_BASE, /* 0x0 */
+ DMA_TX_DIF_APP_ERR, /* 0x1 */
+ DMA_TX_DIF_RPP_ERR, /* 0x2 */
+ DMA_TX_AXI_BUS_ERR, /* 0x3 */
+ DMA_TX_DATA_SGL_OVERFLOW_ERR, /* 0x4 */
+ DMA_TX_DIF_SGL_OVERFLOW_ERR, /* 0x5 */
+ DMA_TX_UNEXP_XFER_RDY_ERR, /* 0x6 */
+ DMA_TX_XFER_RDY_OFFSET_ERR, /* 0x7 */
+ DMA_TX_DATA_UNDERFLOW_ERR, /* 0x8 */
+ DMA_TX_XFER_RDY_LENGTH_OVERFLOW_ERR, /* 0x9 */
+
+ /* dma rx */
+ DMA_RX_BUFFER_ECC_ERR = DMA_RX_ERR_BASE, /* 0x100 */
+ DMA_RX_DIF_CRC_ERR, /* 0x101 */
+ DMA_RX_DIF_APP_ERR, /* 0x102 */
+ DMA_RX_DIF_RPP_ERR, /* 0x103 */
+ DMA_RX_RESP_BUFFER_OVERFLOW_ERR, /* 0x104 */
+ DMA_RX_AXI_BUS_ERR, /* 0x105 */
+ DMA_RX_DATA_SGL_OVERFLOW_ERR, /* 0x106 */
+ DMA_RX_DIF_SGL_OVERFLOW_ERR, /* 0x107 */
+ DMA_RX_DATA_OFFSET_ERR, /* 0x108 */
+ DMA_RX_UNEXP_RX_DATA_ERR, /* 0x109 */
+ DMA_RX_DATA_OVERFLOW_ERR, /* 0x10a */
+ DMA_RX_DATA_UNDERFLOW_ERR, /* 0x10b */
+ DMA_RX_UNEXP_RETRANS_RESP_ERR, /* 0x10c */
+
+ /* trans tx */
+ TRANS_TX_RSVD0_ERR = TRANS_TX_FAIL_BASE, /* 0x200 */
+ TRANS_TX_PHY_NOT_ENABLE_ERR, /* 0x201 */
+ TRANS_TX_OPEN_REJCT_WRONG_DEST_ERR, /* 0x202 */
+ TRANS_TX_OPEN_REJCT_ZONE_VIOLATION_ERR, /* 0x203 */
+ TRANS_TX_OPEN_REJCT_BY_OTHER_ERR, /* 0x204 */
+ TRANS_TX_RSVD1_ERR, /* 0x205 */
+ TRANS_TX_OPEN_REJCT_AIP_TIMEOUT_ERR, /* 0x206 */
+ TRANS_TX_OPEN_REJCT_STP_BUSY_ERR, /* 0x207 */
+ TRANS_TX_OPEN_REJCT_PROTOCOL_NOT_SUPPORT_ERR, /* 0x208 */
+ TRANS_TX_OPEN_REJCT_RATE_NOT_SUPPORT_ERR, /* 0x209 */
+ TRANS_TX_OPEN_REJCT_BAD_DEST_ERR, /* 0x20a */
+ TRANS_TX_OPEN_BREAK_RECEIVE_ERR, /* 0x20b */
+ TRANS_TX_LOW_PHY_POWER_ERR, /* 0x20c */
+ TRANS_TX_OPEN_REJCT_PATHWAY_BLOCKED_ERR, /* 0x20d */
+ TRANS_TX_OPEN_TIMEOUT_ERR, /* 0x20e */
+ TRANS_TX_OPEN_REJCT_NO_DEST_ERR, /* 0x20f */
+ TRANS_TX_OPEN_RETRY_ERR, /* 0x210 */
+ TRANS_TX_RSVD2_ERR, /* 0x211 */
+ TRANS_TX_BREAK_TIMEOUT_ERR, /* 0x212 */
+ TRANS_TX_BREAK_REQUEST_ERR, /* 0x213 */
+ TRANS_TX_BREAK_RECEIVE_ERR, /* 0x214 */
+ TRANS_TX_CLOSE_TIMEOUT_ERR, /* 0x215 */
+ TRANS_TX_CLOSE_NORMAL_ERR, /* 0x216 */
+ TRANS_TX_CLOSE_PHYRESET_ERR, /* 0x217 */
+ TRANS_TX_WITH_CLOSE_DWS_TIMEOUT_ERR, /* 0x218 */
+ TRANS_TX_WITH_CLOSE_COMINIT_ERR, /* 0x219 */
+ TRANS_TX_NAK_RECEIVE_ERR, /* 0x21a */
+ TRANS_TX_ACK_NAK_TIMEOUT_ERR, /* 0x21b */
+ TRANS_TX_CREDIT_TIMEOUT_ERR, /* 0x21c */
+ TRANS_TX_IPTT_CONFLICT_ERR, /* 0x21d */
+ TRANS_TX_TXFRM_TYPE_ERR, /* 0x21e */
+ TRANS_TX_TXSMP_LENGTH_ERR, /* 0x21f */
+
+ /* trans rx */
+ TRANS_RX_FRAME_CRC_ERR = TRANS_RX_FAIL_BASE, /* 0x300 */
+ TRANS_RX_FRAME_DONE_ERR, /* 0x301 */
+ TRANS_RX_FRAME_ERRPRM_ERR, /* 0x302 */
+ TRANS_RX_FRAME_NO_CREDIT_ERR, /* 0x303 */
+ TRANS_RX_RSVD0_ERR, /* 0x304 */
+ TRANS_RX_FRAME_OVERRUN_ERR, /* 0x305 */
+ TRANS_RX_FRAME_NO_EOF_ERR, /* 0x306 */
+ TRANS_RX_LINK_BUF_OVERRUN_ERR, /* 0x307 */
+ TRANS_RX_BREAK_TIMEOUT_ERR, /* 0x308 */
+ TRANS_RX_BREAK_REQUEST_ERR, /* 0x309 */
+ TRANS_RX_BREAK_RECEIVE_ERR, /* 0x30a */
+ TRANS_RX_CLOSE_TIMEOUT_ERR, /* 0x30b */
+ TRANS_RX_CLOSE_NORMAL_ERR, /* 0x30c */
+ TRANS_RX_CLOSE_PHYRESET_ERR, /* 0x30d */
+ TRANS_RX_WITH_CLOSE_DWS_TIMEOUT_ERR, /* 0x30e */
+ TRANS_RX_WITH_CLOSE_COMINIT_ERR, /* 0x30f */
+ TRANS_RX_DATA_LENGTH0_ERR, /* 0x310 */
+ TRANS_RX_BAD_HASH_ERR, /* 0x311 */
+ TRANS_RX_XRDY_ZERO_ERR, /* 0x312 */
+ TRANS_RX_SSP_FRAME_LEN_ERR, /* 0x313 */
+ TRANS_RX_TRANS_RX_RSVD1_ERR, /* 0x314 */
+ TRANS_RX_NO_BALANCE_ERR, /* 0x315 */
+ TRANS_RX_TRANS_RX_RSVD2_ERR, /* 0x316 */
+ TRANS_RX_TRANS_RX_RSVD3_ERR, /* 0x317 */
+ TRANS_RX_BAD_FRAME_TYPE_ERR, /* 0x318 */
+ TRANS_RX_SMP_FRAME_LEN_ERR, /* 0x319 */
+ TRANS_RX_SMP_RESP_TIMEOUT_ERR, /* 0x31a */
+};
+
+#define HISI_SAS_PHY_MAX_INT_NR (HISI_SAS_PHY_INT_NR * HISI_SAS_MAX_PHYS)
+#define HISI_SAS_CQ_MAX_INT_NR (HISI_SAS_MAX_QUEUES)
+#define HISI_SAS_FATAL_INT_NR (2)
+
+#define HISI_SAS_MAX_INT_NR \
+ (HISI_SAS_PHY_MAX_INT_NR + HISI_SAS_CQ_MAX_INT_NR +\
+ HISI_SAS_FATAL_INT_NR)
+
+static u32 hisi_sas_read32(struct hisi_hba *hisi_hba, u32 off)
+{
+ void __iomem *regs = hisi_hba->regs + off;
+
+ return readl(regs);
+}
+
+static u32 hisi_sas_read32_relaxed(struct hisi_hba *hisi_hba, u32 off)
+{
+ void __iomem *regs = hisi_hba->regs + off;
+
+ return readl_relaxed(regs);
+}
+
+static void hisi_sas_write32(struct hisi_hba *hisi_hba,
+ u32 off, u32 val)
+{
+ void __iomem *regs = hisi_hba->regs + off;
+
+ writel(val, regs);
+}
+
+static void hisi_sas_phy_write32(struct hisi_hba *hisi_hba,
+ int phy_no, u32 off, u32 val)
+{
+ void __iomem *regs = hisi_hba->regs + (0x400 * phy_no) + off;
+
+ writel(val, regs);
+}
+
+static u32 hisi_sas_phy_read32(struct hisi_hba *hisi_hba,
+ int phy_no, u32 off)
+{
+ void __iomem *regs = hisi_hba->regs + (0x400 * phy_no) + off;
+
+ return readl(regs);
+}
+
+static void config_phy_opt_mode_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CFG);
+
+ cfg &= ~PHY_CFG_DC_OPT_MSK;
+ cfg |= 1 << PHY_CFG_DC_OPT_OFF;
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CFG, cfg);
+}
+
+static void config_tx_tfe_autoneg_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CONFIG2);
+
+ cfg &= ~PHY_CONFIG2_FORCE_TXDEEMPH_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CONFIG2, cfg);
+}
+
+static void config_id_frame_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ struct sas_identify_frame identify_frame;
+ u32 *identify_buffer;
+
+ memset(&identify_frame, 0, sizeof(identify_frame));
+ identify_frame.dev_type = SAS_END_DEVICE;
+ identify_frame.frame_type = 0;
+ identify_frame._un1 = 1;
+ identify_frame.initiator_bits = SAS_PROTOCOL_ALL;
+ identify_frame.target_bits = SAS_PROTOCOL_NONE;
+ memcpy(&identify_frame._un4_11[0], hisi_hba->sas_addr, SAS_ADDR_SIZE);
+ memcpy(&identify_frame.sas_addr[0], hisi_hba->sas_addr, SAS_ADDR_SIZE);
+ identify_frame.phy_id = phy_no;
+ identify_buffer = (u32 *)(&identify_frame);
+
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD0,
+ __swab32(identify_buffer[0]));
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD1,
+ identify_buffer[2]);
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD2,
+ identify_buffer[1]);
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD3,
+ identify_buffer[4]);
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD4,
+ identify_buffer[3]);
+ hisi_sas_phy_write32(hisi_hba, phy_no, TX_ID_DWORD5,
+ __swab32(identify_buffer[5]));
+}
+
+static void init_id_frame_v1_hw(struct hisi_hba *hisi_hba)
+{
+ int i;
+
+ for (i = 0; i < hisi_hba->n_phy; i++)
+ config_id_frame_v1_hw(hisi_hba, i);
+}
+
+static void setup_itct_v1_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_device *sas_dev)
+{
+ struct domain_device *device = sas_dev->sas_device;
+ struct device *dev = &hisi_hba->pdev->dev;
+ u64 qw0, device_id = sas_dev->device_id;
+ struct hisi_sas_itct *itct = &hisi_hba->itct[device_id];
+
+ memset(itct, 0, sizeof(*itct));
+
+ /* qw0 */
+ qw0 = 0;
+ switch (sas_dev->dev_type) {
+ case SAS_END_DEVICE:
+ case SAS_EDGE_EXPANDER_DEVICE:
+ case SAS_FANOUT_EXPANDER_DEVICE:
+ qw0 = HISI_SAS_DEV_TYPE_SSP << ITCT_HDR_DEV_TYPE_OFF;
+ break;
+ default:
+ dev_warn(dev, "setup itct: unsupported dev type (%d)\n",
+ sas_dev->dev_type);
+ }
+
+ qw0 |= ((1 << ITCT_HDR_VALID_OFF) |
+ (1 << ITCT_HDR_AWT_CONTROL_OFF) |
+ (device->max_linkrate << ITCT_HDR_MAX_CONN_RATE_OFF) |
+ (1 << ITCT_HDR_VALID_LINK_NUM_OFF) |
+ (device->port->id << ITCT_HDR_PORT_ID_OFF));
+ itct->qw0 = cpu_to_le64(qw0);
+
+ /* qw1 */
+ memcpy(&itct->sas_addr, device->sas_addr, SAS_ADDR_SIZE);
+ itct->sas_addr = __swab64(itct->sas_addr);
+
+ /* qw2 */
+ itct->qw2 = cpu_to_le64((500 < ITCT_HDR_IT_NEXUS_LOSS_TL_OFF) |
+ (0xff00 < ITCT_HDR_BUS_INACTIVE_TL_OFF) |
+ (0xff00 < ITCT_HDR_MAX_CONN_TL_OFF) |
+ (0xff00 < ITCT_HDR_REJ_OPEN_TL_OFF));
+}
+
+static void free_device_v1_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_device *sas_dev)
+{
+ u64 dev_id = sas_dev->device_id;
+ struct hisi_sas_itct *itct = &hisi_hba->itct[dev_id];
+ u32 qw0, reg_val = hisi_sas_read32(hisi_hba, CFG_AGING_TIME);
+
+ reg_val |= CFG_AGING_TIME_ITCT_REL_MSK;
+ hisi_sas_write32(hisi_hba, CFG_AGING_TIME, reg_val);
+
+ /* free itct */
+ udelay(1);
+ reg_val = hisi_sas_read32(hisi_hba, CFG_AGING_TIME);
+ reg_val &= ~CFG_AGING_TIME_ITCT_REL_MSK;
+ hisi_sas_write32(hisi_hba, CFG_AGING_TIME, reg_val);
+
+ qw0 = cpu_to_le64(itct->qw0);
+ qw0 &= ~ITCT_HDR_VALID_MSK;
+ itct->qw0 = cpu_to_le64(qw0);
+}
+
+static int reset_hw_v1_hw(struct hisi_hba *hisi_hba)
+{
+ int i;
+ unsigned long end_time;
+ u32 val;
+ struct device *dev = &hisi_hba->pdev->dev;
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ u32 phy_ctrl = hisi_sas_phy_read32(hisi_hba, i, PHY_CTRL);
+
+ phy_ctrl |= PHY_CTRL_RESET_MSK;
+ hisi_sas_phy_write32(hisi_hba, i, PHY_CTRL, phy_ctrl);
+ }
+ msleep(1); /* It is safe to wait for 50us */
+
+ /* Ensure DMA tx & rx idle */
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ u32 dma_tx_status, dma_rx_status;
+
+ end_time = jiffies + msecs_to_jiffies(1000);
+
+ while (1) {
+ dma_tx_status = hisi_sas_phy_read32(hisi_hba, i,
+ DMA_TX_STATUS);
+ dma_rx_status = hisi_sas_phy_read32(hisi_hba, i,
+ DMA_RX_STATUS);
+
+ if (!(dma_tx_status & DMA_TX_STATUS_BUSY_MSK) &&
+ !(dma_rx_status & DMA_RX_STATUS_BUSY_MSK))
+ break;
+
+ msleep(20);
+ if (time_after(jiffies, end_time))
+ return -EIO;
+ }
+ }
+
+ /* Ensure axi bus idle */
+ end_time = jiffies + msecs_to_jiffies(1000);
+ while (1) {
+ u32 axi_status =
+ hisi_sas_read32(hisi_hba, AXI_CFG);
+
+ if (axi_status == 0)
+ break;
+
+ msleep(20);
+ if (time_after(jiffies, end_time))
+ return -EIO;
+ }
+
+ /* Apply reset and disable clock */
+ /* clk disable reg is offset by +4 bytes from clk enable reg */
+ regmap_write(hisi_hba->ctrl, hisi_hba->ctrl_reset_reg,
+ RESET_VALUE);
+ regmap_write(hisi_hba->ctrl, hisi_hba->ctrl_clock_ena_reg + 4,
+ RESET_VALUE);
+ msleep(1);
+ regmap_read(hisi_hba->ctrl, hisi_hba->ctrl_reset_sts_reg, &val);
+ if (RESET_VALUE != (val & RESET_VALUE)) {
+ dev_err(dev, "Reset failed\n");
+ return -EIO;
+ }
+
+ /* De-reset and enable clock */
+ /* deassert rst reg is offset by +4 bytes from assert reg */
+ regmap_write(hisi_hba->ctrl, hisi_hba->ctrl_reset_reg + 4,
+ RESET_VALUE);
+ regmap_write(hisi_hba->ctrl, hisi_hba->ctrl_clock_ena_reg,
+ RESET_VALUE);
+ msleep(1);
+ regmap_read(hisi_hba->ctrl, hisi_hba->ctrl_reset_sts_reg, &val);
+ if (val & RESET_VALUE) {
+ dev_err(dev, "De-reset failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void init_reg_v1_hw(struct hisi_hba *hisi_hba)
+{
+ int i;
+
+ /* Global registers init*/
+ hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE,
+ (u32)((1ULL << hisi_hba->queue_count) - 1));
+ hisi_sas_write32(hisi_hba, HGC_TRANS_TASK_CNT_LIMIT, 0x11);
+ hisi_sas_write32(hisi_hba, DEVICE_MSG_WORK_MODE, 0x1);
+ hisi_sas_write32(hisi_hba, HGC_SAS_TXFAIL_RETRY_CTRL, 0x1ff);
+ hisi_sas_write32(hisi_hba, HGC_ERR_STAT_EN, 0x401);
+ hisi_sas_write32(hisi_hba, CFG_1US_TIMER_TRSH, 0x64);
+ hisi_sas_write32(hisi_hba, HGC_GET_ITV_TIME, 0x1);
+ hisi_sas_write32(hisi_hba, I_T_NEXUS_LOSS_TIME, 0x64);
+ hisi_sas_write32(hisi_hba, BUS_INACTIVE_LIMIT_TIME, 0x2710);
+ hisi_sas_write32(hisi_hba, REJECT_TO_OPEN_LIMIT_TIME, 0x1);
+ hisi_sas_write32(hisi_hba, CFG_AGING_TIME, 0x7a12);
+ hisi_sas_write32(hisi_hba, HGC_DFX_CFG2, 0x9c40);
+ hisi_sas_write32(hisi_hba, FIS_LIST_BADDR_L, 0x2);
+ hisi_sas_write32(hisi_hba, INT_COAL_EN, 0xc);
+ hisi_sas_write32(hisi_hba, OQ_INT_COAL_TIME, 0x186a0);
+ hisi_sas_write32(hisi_hba, OQ_INT_COAL_CNT, 1);
+ hisi_sas_write32(hisi_hba, ENT_INT_COAL_TIME, 0x1);
+ hisi_sas_write32(hisi_hba, ENT_INT_COAL_CNT, 0x1);
+ hisi_sas_write32(hisi_hba, OQ_INT_SRC, 0xffffffff);
+ hisi_sas_write32(hisi_hba, OQ_INT_SRC_MSK, 0);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC1, 0xffffffff);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK1, 0);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC2, 0xffffffff);
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK2, 0);
+ hisi_sas_write32(hisi_hba, SAS_ECC_INTR_MSK, 0);
+ hisi_sas_write32(hisi_hba, AXI_AHB_CLK_CFG, 0x2);
+ hisi_sas_write32(hisi_hba, CFG_SAS_CONFIG, 0x22000000);
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ hisi_sas_phy_write32(hisi_hba, i, PROG_PHY_LINK_RATE, 0x88a);
+ hisi_sas_phy_write32(hisi_hba, i, PHY_CONFIG2, 0x7c080);
+ hisi_sas_phy_write32(hisi_hba, i, PHY_RATE_NEGO, 0x415ee00);
+ hisi_sas_phy_write32(hisi_hba, i, PHY_PCN, 0x80a80000);
+ hisi_sas_phy_write32(hisi_hba, i, SL_TOUT_CFG, 0x7d7d7d7d);
+ hisi_sas_phy_write32(hisi_hba, i, DONE_RECEIVED_TIME, 0x0);
+ hisi_sas_phy_write32(hisi_hba, i, RXOP_CHECK_CFG_H, 0x1000);
+ hisi_sas_phy_write32(hisi_hba, i, DONE_RECEIVED_TIME, 0);
+ hisi_sas_phy_write32(hisi_hba, i, CON_CFG_DRIVER, 0x13f0a);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT_COAL_EN, 3);
+ hisi_sas_phy_write32(hisi_hba, i, DONE_RECEIVED_TIME, 8);
+ }
+
+ for (i = 0; i < hisi_hba->queue_count; i++) {
+ /* Delivery queue */
+ hisi_sas_write32(hisi_hba,
+ DLVRY_Q_0_BASE_ADDR_HI + (i * 0x14),
+ upper_32_bits(hisi_hba->cmd_hdr_dma[i]));
+
+ hisi_sas_write32(hisi_hba,
+ DLVRY_Q_0_BASE_ADDR_LO + (i * 0x14),
+ lower_32_bits(hisi_hba->cmd_hdr_dma[i]));
+
+ hisi_sas_write32(hisi_hba,
+ DLVRY_Q_0_DEPTH + (i * 0x14),
+ HISI_SAS_QUEUE_SLOTS);
+
+ /* Completion queue */
+ hisi_sas_write32(hisi_hba,
+ COMPL_Q_0_BASE_ADDR_HI + (i * 0x14),
+ upper_32_bits(hisi_hba->complete_hdr_dma[i]));
+
+ hisi_sas_write32(hisi_hba,
+ COMPL_Q_0_BASE_ADDR_LO + (i * 0x14),
+ lower_32_bits(hisi_hba->complete_hdr_dma[i]));
+
+ hisi_sas_write32(hisi_hba, COMPL_Q_0_DEPTH + (i * 0x14),
+ HISI_SAS_QUEUE_SLOTS);
+ }
+
+ /* itct */
+ hisi_sas_write32(hisi_hba, ITCT_BASE_ADDR_LO,
+ lower_32_bits(hisi_hba->itct_dma));
+
+ hisi_sas_write32(hisi_hba, ITCT_BASE_ADDR_HI,
+ upper_32_bits(hisi_hba->itct_dma));
+
+ /* iost */
+ hisi_sas_write32(hisi_hba, IOST_BASE_ADDR_LO,
+ lower_32_bits(hisi_hba->iost_dma));
+
+ hisi_sas_write32(hisi_hba, IOST_BASE_ADDR_HI,
+ upper_32_bits(hisi_hba->iost_dma));
+
+ /* breakpoint */
+ hisi_sas_write32(hisi_hba, BROKEN_MSG_ADDR_LO,
+ lower_32_bits(hisi_hba->breakpoint_dma));
+
+ hisi_sas_write32(hisi_hba, BROKEN_MSG_ADDR_HI,
+ upper_32_bits(hisi_hba->breakpoint_dma));
+}
+
+static int hw_init_v1_hw(struct hisi_hba *hisi_hba)
+{
+ struct device *dev = &hisi_hba->pdev->dev;
+ int rc;
+
+ rc = reset_hw_v1_hw(hisi_hba);
+ if (rc) {
+ dev_err(dev, "hisi_sas_reset_hw failed, rc=%d", rc);
+ return rc;
+ }
+
+ msleep(100);
+ init_reg_v1_hw(hisi_hba);
+
+ init_id_frame_v1_hw(hisi_hba);
+
+ return 0;
+}
+
+static void enable_phy_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CFG);
+
+ cfg |= PHY_CFG_ENA_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CFG, cfg);
+}
+
+static void disable_phy_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 cfg = hisi_sas_phy_read32(hisi_hba, phy_no, PHY_CFG);
+
+ cfg &= ~PHY_CFG_ENA_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, PHY_CFG, cfg);
+}
+
+static void start_phy_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ config_id_frame_v1_hw(hisi_hba, phy_no);
+ config_phy_opt_mode_v1_hw(hisi_hba, phy_no);
+ config_tx_tfe_autoneg_v1_hw(hisi_hba, phy_no);
+ enable_phy_v1_hw(hisi_hba, phy_no);
+}
+
+static void stop_phy_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ disable_phy_v1_hw(hisi_hba, phy_no);
+}
+
+static void phy_hard_reset_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ stop_phy_v1_hw(hisi_hba, phy_no);
+ msleep(100);
+ start_phy_v1_hw(hisi_hba, phy_no);
+}
+
+static void start_phys_v1_hw(unsigned long data)
+{
+ struct hisi_hba *hisi_hba = (struct hisi_hba *)data;
+ int i;
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0x12a);
+ start_phy_v1_hw(hisi_hba, i);
+ }
+}
+
+static void phys_init_v1_hw(struct hisi_hba *hisi_hba)
+{
+ int i;
+ struct timer_list *timer = &hisi_hba->timer;
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0x6a);
+ hisi_sas_phy_read32(hisi_hba, i, CHL_INT2_MSK);
+ }
+
+ setup_timer(timer, start_phys_v1_hw, (unsigned long)hisi_hba);
+ mod_timer(timer, jiffies + HZ);
+}
+
+static void sl_notify_v1_hw(struct hisi_hba *hisi_hba, int phy_no)
+{
+ u32 sl_control;
+
+ sl_control = hisi_sas_phy_read32(hisi_hba, phy_no, SL_CONTROL);
+ sl_control |= SL_CONTROL_NOTIFY_EN_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, SL_CONTROL, sl_control);
+ msleep(1);
+ sl_control = hisi_sas_phy_read32(hisi_hba, phy_no, SL_CONTROL);
+ sl_control &= ~SL_CONTROL_NOTIFY_EN_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, SL_CONTROL, sl_control);
+}
+
+static int get_wideport_bitmap_v1_hw(struct hisi_hba *hisi_hba, int port_id)
+{
+ int i, bitmap = 0;
+ u32 phy_port_num_ma = hisi_sas_read32(hisi_hba, PHY_PORT_NUM_MA);
+
+ for (i = 0; i < hisi_hba->n_phy; i++)
+ if (((phy_port_num_ma >> (i * 4)) & 0xf) == port_id)
+ bitmap |= 1 << i;
+
+ return bitmap;
+}
+
+/**
+ * This function allocates across all queues to load balance.
+ * Slots are allocated from queues in a round-robin fashion.
+ *
+ * The callpath to this function and upto writing the write
+ * queue pointer should be safe from interruption.
+ */
+static int get_free_slot_v1_hw(struct hisi_hba *hisi_hba, int *q, int *s)
+{
+ struct device *dev = &hisi_hba->pdev->dev;
+ u32 r, w;
+ int queue = hisi_hba->queue;
+
+ while (1) {
+ w = hisi_sas_read32_relaxed(hisi_hba,
+ DLVRY_Q_0_WR_PTR + (queue * 0x14));
+ r = hisi_sas_read32_relaxed(hisi_hba,
+ DLVRY_Q_0_RD_PTR + (queue * 0x14));
+ if (r == (w+1) % HISI_SAS_QUEUE_SLOTS) {
+ queue = (queue + 1) % hisi_hba->queue_count;
+ if (queue == hisi_hba->queue) {
+ dev_warn(dev, "could not find free slot\n");
+ return -EAGAIN;
+ }
+ continue;
+ }
+ break;
+ }
+ hisi_hba->queue = (queue + 1) % hisi_hba->queue_count;
+ *q = queue;
+ *s = w;
+ return 0;
+}
+
+static void start_delivery_v1_hw(struct hisi_hba *hisi_hba)
+{
+ int dlvry_queue = hisi_hba->slot_prep->dlvry_queue;
+ int dlvry_queue_slot = hisi_hba->slot_prep->dlvry_queue_slot;
+
+ hisi_sas_write32(hisi_hba,
+ DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14),
+ ++dlvry_queue_slot % HISI_SAS_QUEUE_SLOTS);
+}
+
+static int prep_prd_sge_v1_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot,
+ struct hisi_sas_cmd_hdr *hdr,
+ struct scatterlist *scatter,
+ int n_elem)
+{
+ struct device *dev = &hisi_hba->pdev->dev;
+ struct scatterlist *sg;
+ int i;
+
+ if (n_elem > HISI_SAS_SGE_PAGE_CNT) {
+ dev_err(dev, "prd err: n_elem(%d) > HISI_SAS_SGE_PAGE_CNT",
+ n_elem);
+ return -EINVAL;
+ }
+
+ slot->sge_page = dma_pool_alloc(hisi_hba->sge_page_pool, GFP_ATOMIC,
+ &slot->sge_page_dma);
+ if (!slot->sge_page)
+ return -ENOMEM;
+
+ for_each_sg(scatter, sg, n_elem, i) {
+ struct hisi_sas_sge *entry = &slot->sge_page->sge[i];
+
+ entry->addr = cpu_to_le64(sg_dma_address(sg));
+ entry->page_ctrl_0 = entry->page_ctrl_1 = 0;
+ entry->data_len = cpu_to_le32(sg_dma_len(sg));
+ entry->data_off = 0;
+ }
+
+ hdr->prd_table_addr = cpu_to_le64(slot->sge_page_dma);
+
+ hdr->sg_len = cpu_to_le32(n_elem << CMD_HDR_DATA_SGL_LEN_OFF);
+
+ return 0;
+}
+
+static int prep_smp_v1_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot)
+{
+ struct sas_task *task = slot->task;
+ struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
+ struct domain_device *device = task->dev;
+ struct device *dev = &hisi_hba->pdev->dev;
+ struct hisi_sas_port *port = slot->port;
+ struct scatterlist *sg_req, *sg_resp;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ dma_addr_t req_dma_addr;
+ unsigned int req_len, resp_len;
+ int elem, rc;
+
+ /*
+ * DMA-map SMP request, response buffers
+ */
+ /* req */
+ sg_req = &task->smp_task.smp_req;
+ elem = dma_map_sg(dev, sg_req, 1, DMA_TO_DEVICE);
+ if (!elem)
+ return -ENOMEM;
+ req_len = sg_dma_len(sg_req);
+ req_dma_addr = sg_dma_address(sg_req);
+
+ /* resp */
+ sg_resp = &task->smp_task.smp_resp;
+ elem = dma_map_sg(dev, sg_resp, 1, DMA_FROM_DEVICE);
+ if (!elem) {
+ rc = -ENOMEM;
+ goto err_out_req;
+ }
+ resp_len = sg_dma_len(sg_resp);
+ if ((req_len & 0x3) || (resp_len & 0x3)) {
+ rc = -EINVAL;
+ goto err_out_resp;
+ }
+
+ /* create header */
+ /* dw0 */
+ hdr->dw0 = cpu_to_le32((port->id << CMD_HDR_PORT_OFF) |
+ (1 << CMD_HDR_PRIORITY_OFF) | /* high pri */
+ (1 << CMD_HDR_MODE_OFF) | /* ini mode */
+ (2 << CMD_HDR_CMD_OFF)); /* smp */
+
+ /* map itct entry */
+ hdr->dw1 = cpu_to_le32(sas_dev->device_id << CMD_HDR_DEVICE_ID_OFF);
+
+ /* dw2 */
+ hdr->dw2 = cpu_to_le32((((req_len-4)/4) << CMD_HDR_CFL_OFF) |
+ (HISI_SAS_MAX_SMP_RESP_SZ/4 <<
+ CMD_HDR_MRFL_OFF));
+
+ hdr->transfer_tags = cpu_to_le32(slot->idx << CMD_HDR_IPTT_OFF);
+
+ hdr->cmd_table_addr = cpu_to_le64(req_dma_addr);
+ hdr->sts_buffer_addr = cpu_to_le64(slot->status_buffer_dma);
+
+ return 0;
+
+err_out_resp:
+ dma_unmap_sg(dev, &slot->task->smp_task.smp_resp, 1,
+ DMA_FROM_DEVICE);
+err_out_req:
+ dma_unmap_sg(dev, &slot->task->smp_task.smp_req, 1,
+ DMA_TO_DEVICE);
+ return rc;
+}
+
+static int prep_ssp_v1_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot, int is_tmf,
+ struct hisi_sas_tmf_task *tmf)
+{
+ struct sas_task *task = slot->task;
+ struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
+ struct domain_device *device = task->dev;
+ struct hisi_sas_device *sas_dev = device->lldd_dev;
+ struct hisi_sas_port *port = slot->port;
+ struct sas_ssp_task *ssp_task = &task->ssp_task;
+ struct scsi_cmnd *scsi_cmnd = ssp_task->cmd;
+ int has_data = 0, rc, priority = is_tmf;
+ u8 *buf_cmd, fburst = 0;
+ u32 dw1, dw2;
+
+ /* create header */
+ hdr->dw0 = cpu_to_le32((1 << CMD_HDR_RESP_REPORT_OFF) |
+ (0x2 << CMD_HDR_TLR_CTRL_OFF) |
+ (port->id << CMD_HDR_PORT_OFF) |
+ (priority << CMD_HDR_PRIORITY_OFF) |
+ (1 << CMD_HDR_MODE_OFF) | /* ini mode */
+ (1 << CMD_HDR_CMD_OFF)); /* ssp */
+
+ dw1 = 1 << CMD_HDR_VERIFY_DTL_OFF;
+
+ if (is_tmf) {
+ dw1 |= 3 << CMD_HDR_SSP_FRAME_TYPE_OFF;
+ } else {
+ switch (scsi_cmnd->sc_data_direction) {
+ case DMA_TO_DEVICE:
+ dw1 |= 2 << CMD_HDR_SSP_FRAME_TYPE_OFF;
+ has_data = 1;
+ break;
+ case DMA_FROM_DEVICE:
+ dw1 |= 1 << CMD_HDR_SSP_FRAME_TYPE_OFF;
+ has_data = 1;
+ break;
+ default:
+ dw1 |= 0 << CMD_HDR_SSP_FRAME_TYPE_OFF;
+ }
+ }
+
+ /* map itct entry */
+ dw1 |= sas_dev->device_id << CMD_HDR_DEVICE_ID_OFF;
+ hdr->dw1 = cpu_to_le32(dw1);
+
+ if (is_tmf) {
+ dw2 = ((sizeof(struct ssp_tmf_iu) +
+ sizeof(struct ssp_frame_hdr)+3)/4) <<
+ CMD_HDR_CFL_OFF;
+ } else {
+ dw2 = ((sizeof(struct ssp_command_iu) +
+ sizeof(struct ssp_frame_hdr)+3)/4) <<
+ CMD_HDR_CFL_OFF;
+ }
+
+ dw2 |= (HISI_SAS_MAX_SSP_RESP_SZ/4) << CMD_HDR_MRFL_OFF;
+
+ hdr->transfer_tags = cpu_to_le32(slot->idx << CMD_HDR_IPTT_OFF);
+
+ if (has_data) {
+ rc = prep_prd_sge_v1_hw(hisi_hba, slot, hdr, task->scatter,
+ slot->n_elem);
+ if (rc)
+ return rc;
+ }
+
+ hdr->data_transfer_len = cpu_to_le32(task->total_xfer_len);
+ hdr->cmd_table_addr = cpu_to_le64(slot->command_table_dma);
+ hdr->sts_buffer_addr = cpu_to_le64(slot->status_buffer_dma);
+
+ buf_cmd = slot->command_table + sizeof(struct ssp_frame_hdr);
+ if (task->ssp_task.enable_first_burst) {
+ fburst = (1 << 7);
+ dw2 |= 1 << CMD_HDR_FIRST_BURST_OFF;
+ }
+ hdr->dw2 = cpu_to_le32(dw2);
+
+ memcpy(buf_cmd, &task->ssp_task.LUN, 8);
+ if (!is_tmf) {
+ buf_cmd[9] = fburst | task->ssp_task.task_attr |
+ (task->ssp_task.task_prio << 3);
+ memcpy(buf_cmd + 12, task->ssp_task.cmd->cmnd,
+ task->ssp_task.cmd->cmd_len);
+ } else {
+ buf_cmd[10] = tmf->tmf;
+ switch (tmf->tmf) {
+ case TMF_ABORT_TASK:
+ case TMF_QUERY_TASK:
+ buf_cmd[12] =
+ (tmf->tag_of_task_to_be_managed >> 8) & 0xff;
+ buf_cmd[13] =
+ tmf->tag_of_task_to_be_managed & 0xff;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/* by default, task resp is complete */
+static void slot_err_v1_hw(struct hisi_hba *hisi_hba,
+ struct sas_task *task,
+ struct hisi_sas_slot *slot)
+{
+ struct task_status_struct *ts = &task->task_status;
+ struct hisi_sas_err_record *err_record = slot->status_buffer;
+ struct device *dev = &hisi_hba->pdev->dev;
+
+ switch (task->task_proto) {
+ case SAS_PROTOCOL_SSP:
+ {
+ int error = -1;
+ u32 dma_err_type = cpu_to_le32(err_record->dma_err_type);
+ u32 dma_tx_err_type = ((dma_err_type &
+ ERR_HDR_DMA_TX_ERR_TYPE_MSK)) >>
+ ERR_HDR_DMA_TX_ERR_TYPE_OFF;
+ u32 dma_rx_err_type = ((dma_err_type &
+ ERR_HDR_DMA_RX_ERR_TYPE_MSK)) >>
+ ERR_HDR_DMA_RX_ERR_TYPE_OFF;
+ u32 trans_tx_fail_type =
+ cpu_to_le32(err_record->trans_tx_fail_type);
+ u32 trans_rx_fail_type =
+ cpu_to_le32(err_record->trans_rx_fail_type);
+
+ if (dma_tx_err_type) {
+ /* dma tx err */
+ error = ffs(dma_tx_err_type)
+ - 1 + DMA_TX_ERR_BASE;
+ } else if (dma_rx_err_type) {
+ /* dma rx err */
+ error = ffs(dma_rx_err_type)
+ - 1 + DMA_RX_ERR_BASE;
+ } else if (trans_tx_fail_type) {
+ /* trans tx err */
+ error = ffs(trans_tx_fail_type)
+ - 1 + TRANS_TX_FAIL_BASE;
+ } else if (trans_rx_fail_type) {
+ /* trans rx err */
+ error = ffs(trans_rx_fail_type)
+ - 1 + TRANS_RX_FAIL_BASE;
+ }
+
+ switch (error) {
+ case DMA_TX_DATA_UNDERFLOW_ERR:
+ case DMA_RX_DATA_UNDERFLOW_ERR:
+ {
+ ts->residual = 0;
+ ts->stat = SAS_DATA_UNDERRUN;
+ break;
+ }
+ case DMA_TX_DATA_SGL_OVERFLOW_ERR:
+ case DMA_TX_DIF_SGL_OVERFLOW_ERR:
+ case DMA_TX_XFER_RDY_LENGTH_OVERFLOW_ERR:
+ case DMA_RX_DATA_OVERFLOW_ERR:
+ case TRANS_RX_FRAME_OVERRUN_ERR:
+ case TRANS_RX_LINK_BUF_OVERRUN_ERR:
+ {
+ ts->stat = SAS_DATA_OVERRUN;
+ ts->residual = 0;
+ break;
+ }
+ case TRANS_TX_PHY_NOT_ENABLE_ERR:
+ {
+ ts->stat = SAS_PHY_DOWN;
+ break;
+ }
+ case TRANS_TX_OPEN_REJCT_WRONG_DEST_ERR:
+ case TRANS_TX_OPEN_REJCT_ZONE_VIOLATION_ERR:
+ case TRANS_TX_OPEN_REJCT_BY_OTHER_ERR:
+ case TRANS_TX_OPEN_REJCT_AIP_TIMEOUT_ERR:
+ case TRANS_TX_OPEN_REJCT_STP_BUSY_ERR:
+ case TRANS_TX_OPEN_REJCT_PROTOCOL_NOT_SUPPORT_ERR:
+ case TRANS_TX_OPEN_REJCT_RATE_NOT_SUPPORT_ERR:
+ case TRANS_TX_OPEN_REJCT_BAD_DEST_ERR:
+ case TRANS_TX_OPEN_BREAK_RECEIVE_ERR:
+ case TRANS_TX_OPEN_REJCT_PATHWAY_BLOCKED_ERR:
+ case TRANS_TX_OPEN_REJCT_NO_DEST_ERR:
+ case TRANS_TX_OPEN_RETRY_ERR:
+ {
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ break;
+ }
+ case TRANS_TX_OPEN_TIMEOUT_ERR:
+ {
+ ts->stat = SAS_OPEN_TO;
+ break;
+ }
+ case TRANS_TX_NAK_RECEIVE_ERR:
+ case TRANS_TX_ACK_NAK_TIMEOUT_ERR:
+ {
+ ts->stat = SAS_NAK_R_ERR;
+ break;
+ }
+ default:
+ {
+ ts->stat = SAM_STAT_CHECK_CONDITION;
+ break;
+ }
+ }
+ }
+ break;
+ case SAS_PROTOCOL_SMP:
+ ts->stat = SAM_STAT_CHECK_CONDITION;
+ break;
+
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+ {
+ dev_err(dev, "slot err: SATA/STP not supported");
+ }
+ break;
+ default:
+ break;
+ }
+
+}
+
+static int slot_complete_v1_hw(struct hisi_hba *hisi_hba,
+ struct hisi_sas_slot *slot, int abort)
+{
+ struct sas_task *task = slot->task;
+ struct hisi_sas_device *sas_dev;
+ struct device *dev = &hisi_hba->pdev->dev;
+ struct task_status_struct *ts;
+ struct domain_device *device;
+ enum exec_status sts;
+ struct hisi_sas_complete_v1_hdr *complete_queue =
+ (struct hisi_sas_complete_v1_hdr *)
+ hisi_hba->complete_hdr[slot->cmplt_queue];
+ struct hisi_sas_complete_v1_hdr *complete_hdr;
+ u32 cmplt_hdr_data;
+
+ complete_hdr = &complete_queue[slot->cmplt_queue_slot];
+ cmplt_hdr_data = le32_to_cpu(complete_hdr->data);
+
+ if (unlikely(!task || !task->lldd_task || !task->dev))
+ return -EINVAL;
+
+ ts = &task->task_status;
+ device = task->dev;
+ sas_dev = device->lldd_dev;
+
+ task->task_state_flags &=
+ ~(SAS_TASK_STATE_PENDING | SAS_TASK_AT_INITIATOR);
+ task->task_state_flags |= SAS_TASK_STATE_DONE;
+
+ memset(ts, 0, sizeof(*ts));
+ ts->resp = SAS_TASK_COMPLETE;
+
+ if (unlikely(!sas_dev || abort)) {
+ if (!sas_dev)
+ dev_dbg(dev, "slot complete: port has not device\n");
+ ts->stat = SAS_PHY_DOWN;
+ goto out;
+ }
+
+ if (cmplt_hdr_data & CMPLT_HDR_IO_CFG_ERR_MSK) {
+ u32 info_reg = hisi_sas_read32(hisi_hba, HGC_INVLD_DQE_INFO);
+
+ if (info_reg & HGC_INVLD_DQE_INFO_DQ_MSK)
+ dev_err(dev, "slot complete: [%d:%d] has dq IPTT err",
+ slot->cmplt_queue, slot->cmplt_queue_slot);
+
+ if (info_reg & HGC_INVLD_DQE_INFO_TYPE_MSK)
+ dev_err(dev, "slot complete: [%d:%d] has dq type err",
+ slot->cmplt_queue, slot->cmplt_queue_slot);
+
+ if (info_reg & HGC_INVLD_DQE_INFO_FORCE_MSK)
+ dev_err(dev, "slot complete: [%d:%d] has dq force phy err",
+ slot->cmplt_queue, slot->cmplt_queue_slot);
+
+ if (info_reg & HGC_INVLD_DQE_INFO_PHY_MSK)
+ dev_err(dev, "slot complete: [%d:%d] has dq phy id err",
+ slot->cmplt_queue, slot->cmplt_queue_slot);
+
+ if (info_reg & HGC_INVLD_DQE_INFO_ABORT_MSK)
+ dev_err(dev, "slot complete: [%d:%d] has dq abort flag err",
+ slot->cmplt_queue, slot->cmplt_queue_slot);
+
+ if (info_reg & HGC_INVLD_DQE_INFO_IPTT_OF_MSK)
+ dev_err(dev, "slot complete: [%d:%d] has dq IPTT or ICT err",
+ slot->cmplt_queue, slot->cmplt_queue_slot);
+
+ if (info_reg & HGC_INVLD_DQE_INFO_SSP_ERR_MSK)
+ dev_err(dev, "slot complete: [%d:%d] has dq SSP frame type err",
+ slot->cmplt_queue, slot->cmplt_queue_slot);
+
+ if (info_reg & HGC_INVLD_DQE_INFO_OFL_MSK)
+ dev_err(dev, "slot complete: [%d:%d] has dq order frame len err",
+ slot->cmplt_queue, slot->cmplt_queue_slot);
+
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+ goto out;
+ }
+
+ if (cmplt_hdr_data & CMPLT_HDR_ERR_RCRD_XFRD_MSK) {
+ if (!(cmplt_hdr_data & CMPLT_HDR_CMD_CMPLT_MSK) ||
+ !(cmplt_hdr_data & CMPLT_HDR_RSPNS_XFRD_MSK))
+ ts->stat = SAS_DATA_OVERRUN;
+ else
+ slot_err_v1_hw(hisi_hba, task, slot);
+
+ goto out;
+ }
+
+ switch (task->task_proto) {
+ case SAS_PROTOCOL_SSP:
+ {
+ struct ssp_response_iu *iu = slot->status_buffer +
+ sizeof(struct hisi_sas_err_record);
+ sas_ssp_task_response(dev, task, iu);
+ break;
+ }
+ case SAS_PROTOCOL_SMP:
+ {
+ void *to;
+ struct scatterlist *sg_resp = &task->smp_task.smp_resp;
+
+ ts->stat = SAM_STAT_GOOD;
+ to = kmap_atomic(sg_page(sg_resp));
+
+ dma_unmap_sg(dev, &task->smp_task.smp_resp, 1,
+ DMA_FROM_DEVICE);
+ dma_unmap_sg(dev, &task->smp_task.smp_req, 1,
+ DMA_TO_DEVICE);
+ memcpy(to + sg_resp->offset,
+ slot->status_buffer +
+ sizeof(struct hisi_sas_err_record),
+ sg_dma_len(sg_resp));
+ kunmap_atomic(to);
+ break;
+ }
+ case SAS_PROTOCOL_SATA:
+ case SAS_PROTOCOL_STP:
+ case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+ dev_err(dev, "slot complete: SATA/STP not supported");
+ break;
+
+ default:
+ ts->stat = SAM_STAT_CHECK_CONDITION;
+ break;
+ }
+
+ if (!slot->port->port_attached) {
+ dev_err(dev, "slot complete: port %d has removed\n",
+ slot->port->sas_port.id);
+ ts->stat = SAS_PHY_DOWN;
+ }
+
+out:
+ if (sas_dev && sas_dev->running_req)
+ sas_dev->running_req--;
+
+ hisi_sas_slot_task_free(hisi_hba, task, slot);
+ sts = ts->stat;
+
+ if (task->task_done)
+ task->task_done(task);
+
+ return sts;
+}
+
+/* Interrupts */
+static irqreturn_t int_phyup_v1_hw(int irq_no, void *p)
+{
+ struct hisi_sas_phy *phy = p;
+ struct hisi_hba *hisi_hba = phy->hisi_hba;
+ struct device *dev = &hisi_hba->pdev->dev;
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ int i, phy_no = sas_phy->id;
+ u32 irq_value, context, port_id, link_rate;
+ u32 *frame_rcvd = (u32 *)sas_phy->frame_rcvd;
+ struct sas_identify_frame *id = (struct sas_identify_frame *)frame_rcvd;
+ irqreturn_t res = IRQ_HANDLED;
+
+ irq_value = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT2);
+ if (!(irq_value & CHL_INT2_SL_PHY_ENA_MSK)) {
+ dev_dbg(dev, "phyup: irq_value = %x not set enable bit\n",
+ irq_value);
+ res = IRQ_NONE;
+ goto end;
+ }
+
+ context = hisi_sas_read32(hisi_hba, PHY_CONTEXT);
+ if (context & 1 << phy_no) {
+ dev_err(dev, "phyup: phy%d SATA attached equipment\n",
+ phy_no);
+ goto end;
+ }
+
+ port_id = (hisi_sas_read32(hisi_hba, PHY_PORT_NUM_MA) >> (4 * phy_no))
+ & 0xf;
+ if (port_id == 0xf) {
+ dev_err(dev, "phyup: phy%d invalid portid\n", phy_no);
+ res = IRQ_NONE;
+ goto end;
+ }
+
+ for (i = 0; i < 6; i++) {
+ u32 idaf = hisi_sas_phy_read32(hisi_hba, phy_no,
+ RX_IDAF_DWORD0 + (i * 4));
+ frame_rcvd[i] = __swab32(idaf);
+ }
+
+ /* Get the linkrate */
+ link_rate = hisi_sas_read32(hisi_hba, PHY_CONN_RATE);
+ link_rate = (link_rate >> (phy_no * 4)) & 0xf;
+ sas_phy->linkrate = link_rate;
+ sas_phy->oob_mode = SAS_OOB_MODE;
+ memcpy(sas_phy->attached_sas_addr,
+ &id->sas_addr, SAS_ADDR_SIZE);
+ dev_info(dev, "phyup: phy%d link_rate=%d\n",
+ phy_no, link_rate);
+ phy->port_id = port_id;
+ phy->phy_type &= ~(PORT_TYPE_SAS | PORT_TYPE_SATA);
+ phy->phy_type |= PORT_TYPE_SAS;
+ phy->phy_attached = 1;
+ phy->identify.device_type = id->dev_type;
+ phy->frame_rcvd_size = sizeof(struct sas_identify_frame);
+ if (phy->identify.device_type == SAS_END_DEVICE)
+ phy->identify.target_port_protocols =
+ SAS_PROTOCOL_SSP;
+ else if (phy->identify.device_type != SAS_PHY_UNUSED)
+ phy->identify.target_port_protocols =
+ SAS_PROTOCOL_SMP;
+ queue_work(hisi_hba->wq, &phy->phyup_ws);
+
+end:
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT2,
+ CHL_INT2_SL_PHY_ENA_MSK);
+
+ if (irq_value & CHL_INT2_SL_PHY_ENA_MSK) {
+ u32 chl_int0 = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT0);
+
+ chl_int0 &= ~CHL_INT0_PHYCTRL_NOTRDY_MSK;
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0, chl_int0);
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0_MSK, 0x3ce3ee);
+ }
+
+ return res;
+}
+
+static irqreturn_t int_bcast_v1_hw(int irq, void *p)
+{
+ struct hisi_sas_phy *phy = p;
+ struct hisi_hba *hisi_hba = phy->hisi_hba;
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ struct sas_ha_struct *sha = &hisi_hba->sha;
+ struct device *dev = &hisi_hba->pdev->dev;
+ int phy_no = sas_phy->id;
+ u32 irq_value;
+ irqreturn_t res = IRQ_HANDLED;
+
+ irq_value = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT2);
+
+ if (!(irq_value & CHL_INT2_SL_RX_BC_ACK_MSK)) {
+ dev_err(dev, "bcast: irq_value = %x not set enable bit",
+ irq_value);
+ res = IRQ_NONE;
+ goto end;
+ }
+
+ sha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
+
+end:
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT2,
+ CHL_INT2_SL_RX_BC_ACK_MSK);
+
+ return res;
+}
+
+static irqreturn_t int_abnormal_v1_hw(int irq, void *p)
+{
+ struct hisi_sas_phy *phy = p;
+ struct hisi_hba *hisi_hba = phy->hisi_hba;
+ struct device *dev = &hisi_hba->pdev->dev;
+ struct asd_sas_phy *sas_phy = &phy->sas_phy;
+ u32 irq_value, irq_mask_old;
+ int phy_no = sas_phy->id;
+
+ /* mask_int0 */
+ irq_mask_old = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT0_MSK);
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0_MSK, 0x3fffff);
+
+ /* read int0 */
+ irq_value = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT0);
+
+ if (irq_value & CHL_INT0_PHYCTRL_NOTRDY_MSK) {
+ u32 phy_state = hisi_sas_read32(hisi_hba, PHY_STATE);
+
+ hisi_sas_phy_down(hisi_hba, phy_no,
+ (phy_state & 1 << phy_no) ? 1 : 0);
+ }
+
+ if (irq_value & CHL_INT0_ID_TIMEOUT_MSK)
+ dev_dbg(dev, "abnormal: ID_TIMEOUT phy%d identify timeout\n",
+ phy_no);
+
+ if (irq_value & CHL_INT0_DWS_LOST_MSK)
+ dev_dbg(dev, "abnormal: DWS_LOST phy%d dws lost\n", phy_no);
+
+ if (irq_value & CHL_INT0_SN_FAIL_NGR_MSK)
+ dev_dbg(dev, "abnormal: SN_FAIL_NGR phy%d sn fail ngr\n",
+ phy_no);
+
+ if (irq_value & CHL_INT0_SL_IDAF_FAIL_MSK ||
+ irq_value & CHL_INT0_SL_OPAF_FAIL_MSK)
+ dev_dbg(dev, "abnormal: SL_ID/OPAF_FAIL phy%d check adr frm err\n",
+ phy_no);
+
+ if (irq_value & CHL_INT0_SL_PS_FAIL_OFF)
+ dev_dbg(dev, "abnormal: SL_PS_FAIL phy%d fail\n", phy_no);
+
+ /* write to zero */
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0, irq_value);
+
+ if (irq_value & CHL_INT0_PHYCTRL_NOTRDY_MSK)
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0_MSK,
+ 0x3fffff & ~CHL_INT0_MSK_PHYCTRL_NOTRDY_MSK);
+ else
+ hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0_MSK,
+ irq_mask_old);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cq_interrupt_v1_hw(int irq, void *p)
+{
+ struct hisi_sas_cq *cq = p;
+ struct hisi_hba *hisi_hba = cq->hisi_hba;
+ struct hisi_sas_slot *slot;
+ int queue = cq->id;
+ struct hisi_sas_complete_v1_hdr *complete_queue =
+ (struct hisi_sas_complete_v1_hdr *)
+ hisi_hba->complete_hdr[queue];
+ u32 irq_value, rd_point, wr_point;
+
+ irq_value = hisi_sas_read32(hisi_hba, OQ_INT_SRC);
+
+ hisi_sas_write32(hisi_hba, OQ_INT_SRC, 1 << queue);
+
+ rd_point = hisi_sas_read32(hisi_hba,
+ COMPL_Q_0_RD_PTR + (0x14 * queue));
+ wr_point = hisi_sas_read32(hisi_hba,
+ COMPL_Q_0_WR_PTR + (0x14 * queue));
+
+ while (rd_point != wr_point) {
+ struct hisi_sas_complete_v1_hdr *complete_hdr;
+ int idx;
+ u32 cmplt_hdr_data;
+
+ complete_hdr = &complete_queue[rd_point];
+ cmplt_hdr_data = cpu_to_le32(complete_hdr->data);
+ idx = (cmplt_hdr_data & CMPLT_HDR_IPTT_MSK) >>
+ CMPLT_HDR_IPTT_OFF;
+ slot = &hisi_hba->slot_info[idx];
+
+ /* The completion queue and queue slot index are not
+ * necessarily the same as the delivery queue and
+ * queue slot index.
+ */
+ slot->cmplt_queue_slot = rd_point;
+ slot->cmplt_queue = queue;
+ slot_complete_v1_hw(hisi_hba, slot, 0);
+
+ if (++rd_point >= HISI_SAS_QUEUE_SLOTS)
+ rd_point = 0;
+ }
+
+ /* update rd_point */
+ hisi_sas_write32(hisi_hba, COMPL_Q_0_RD_PTR + (0x14 * queue), rd_point);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fatal_ecc_int_v1_hw(int irq, void *p)
+{
+ struct hisi_hba *hisi_hba = p;
+ struct device *dev = &hisi_hba->pdev->dev;
+ u32 ecc_int = hisi_sas_read32(hisi_hba, SAS_ECC_INTR);
+
+ if (ecc_int & SAS_ECC_INTR_DQ_ECC1B_MSK) {
+ u32 ecc_err = hisi_sas_read32(hisi_hba, HGC_ECC_ERR);
+
+ panic("%s: Fatal DQ 1b ECC interrupt (0x%x)\n",
+ dev_name(dev), ecc_err);
+ }
+
+ if (ecc_int & SAS_ECC_INTR_DQ_ECCBAD_MSK) {
+ u32 addr = (hisi_sas_read32(hisi_hba, HGC_DQ_ECC_ADDR) &
+ HGC_DQ_ECC_ADDR_BAD_MSK) >>
+ HGC_DQ_ECC_ADDR_BAD_OFF;
+
+ panic("%s: Fatal DQ RAM ECC interrupt @ 0x%08x\n",
+ dev_name(dev), addr);
+ }
+
+ if (ecc_int & SAS_ECC_INTR_IOST_ECC1B_MSK) {
+ u32 ecc_err = hisi_sas_read32(hisi_hba, HGC_ECC_ERR);
+
+ panic("%s: Fatal IOST 1b ECC interrupt (0x%x)\n",
+ dev_name(dev), ecc_err);
+ }
+
+ if (ecc_int & SAS_ECC_INTR_IOST_ECCBAD_MSK) {
+ u32 addr = (hisi_sas_read32(hisi_hba, HGC_IOST_ECC_ADDR) &
+ HGC_IOST_ECC_ADDR_BAD_MSK) >>
+ HGC_IOST_ECC_ADDR_BAD_OFF;
+
+ panic("%s: Fatal IOST RAM ECC interrupt @ 0x%08x\n",
+ dev_name(dev), addr);
+ }
+
+ if (ecc_int & SAS_ECC_INTR_ITCT_ECCBAD_MSK) {
+ u32 addr = (hisi_sas_read32(hisi_hba, HGC_ITCT_ECC_ADDR) &
+ HGC_ITCT_ECC_ADDR_BAD_MSK) >>
+ HGC_ITCT_ECC_ADDR_BAD_OFF;
+
+ panic("%s: Fatal TCT RAM ECC interrupt @ 0x%08x\n",
+ dev_name(dev), addr);
+ }
+
+ if (ecc_int & SAS_ECC_INTR_ITCT_ECC1B_MSK) {
+ u32 ecc_err = hisi_sas_read32(hisi_hba, HGC_ECC_ERR);
+
+ panic("%s: Fatal ITCT 1b ECC interrupt (0x%x)\n",
+ dev_name(dev), ecc_err);
+ }
+
+ hisi_sas_write32(hisi_hba, SAS_ECC_INTR, ecc_int | 0x3f);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t fatal_axi_int_v1_hw(int irq, void *p)
+{
+ struct hisi_hba *hisi_hba = p;
+ struct device *dev = &hisi_hba->pdev->dev;
+ u32 axi_int = hisi_sas_read32(hisi_hba, ENT_INT_SRC2);
+ u32 axi_info = hisi_sas_read32(hisi_hba, HGC_AXI_FIFO_ERR_INFO);
+
+ if (axi_int & ENT_INT_SRC2_DQ_CFG_ERR_MSK)
+ panic("%s: Fatal DQ_CFG_ERR interrupt (0x%x)\n",
+ dev_name(dev), axi_info);
+
+ if (axi_int & ENT_INT_SRC2_CQ_CFG_ERR_MSK)
+ panic("%s: Fatal CQ_CFG_ERR interrupt (0x%x)\n",
+ dev_name(dev), axi_info);
+
+ if (axi_int & ENT_INT_SRC2_AXI_WRONG_INT_MSK)
+ panic("%s: Fatal AXI_WRONG_INT interrupt (0x%x)\n",
+ dev_name(dev), axi_info);
+
+ if (axi_int & ENT_INT_SRC2_AXI_OVERLF_INT_MSK)
+ panic("%s: Fatal AXI_OVERLF_INT incorrect interrupt (0x%x)\n",
+ dev_name(dev), axi_info);
+
+ hisi_sas_write32(hisi_hba, ENT_INT_SRC2, axi_int | 0x30000000);
+
+ return IRQ_HANDLED;
+}
+
+static irq_handler_t phy_interrupts[HISI_SAS_PHY_INT_NR] = {
+ int_bcast_v1_hw,
+ int_phyup_v1_hw,
+ int_abnormal_v1_hw
+};
+
+static irq_handler_t fatal_interrupts[HISI_SAS_MAX_QUEUES] = {
+ fatal_ecc_int_v1_hw,
+ fatal_axi_int_v1_hw
+};
+
+static int interrupt_init_v1_hw(struct hisi_hba *hisi_hba)
+{
+ struct platform_device *pdev = hisi_hba->pdev;
+ struct device *dev = &pdev->dev;
+ int i, j, irq, rc, idx;
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ struct hisi_sas_phy *phy = &hisi_hba->phy[i];
+
+ idx = i * HISI_SAS_PHY_INT_NR;
+ for (j = 0; j < HISI_SAS_PHY_INT_NR; j++, idx++) {
+ irq = platform_get_irq(pdev, idx);
+ if (!irq) {
+ dev_err(dev,
+ "irq init: fail map phy interrupt %d\n",
+ idx);
+ return -ENOENT;
+ }
+
+ rc = devm_request_irq(dev, irq, phy_interrupts[j], 0,
+ DRV_NAME " phy", phy);
+ if (rc) {
+ dev_err(dev, "irq init: could not request "
+ "phy interrupt %d, rc=%d\n",
+ irq, rc);
+ return -ENOENT;
+ }
+ }
+ }
+
+ idx = hisi_hba->n_phy * HISI_SAS_PHY_INT_NR;
+ for (i = 0; i < hisi_hba->queue_count; i++, idx++) {
+ irq = platform_get_irq(pdev, idx);
+ if (!irq) {
+ dev_err(dev, "irq init: could not map cq interrupt %d\n",
+ idx);
+ return -ENOENT;
+ }
+
+ rc = devm_request_irq(dev, irq, cq_interrupt_v1_hw, 0,
+ DRV_NAME " cq", &hisi_hba->cq[i]);
+ if (rc) {
+ dev_err(dev, "irq init: could not request cq interrupt %d, rc=%d\n",
+ irq, rc);
+ return -ENOENT;
+ }
+ }
+
+ idx = (hisi_hba->n_phy * HISI_SAS_PHY_INT_NR) + hisi_hba->queue_count;
+ for (i = 0; i < HISI_SAS_FATAL_INT_NR; i++, idx++) {
+ irq = platform_get_irq(pdev, idx);
+ if (!irq) {
+ dev_err(dev, "irq init: could not map fatal interrupt %d\n",
+ idx);
+ return -ENOENT;
+ }
+
+ rc = devm_request_irq(dev, irq, fatal_interrupts[i], 0,
+ DRV_NAME " fatal", hisi_hba);
+ if (rc) {
+ dev_err(dev,
+ "irq init: could not request fatal interrupt %d, rc=%d\n",
+ irq, rc);
+ return -ENOENT;
+ }
+ }
+
+ return 0;
+}
+
+static int interrupt_openall_v1_hw(struct hisi_hba *hisi_hba)
+{
+ int i;
+ u32 val;
+
+ for (i = 0; i < hisi_hba->n_phy; i++) {
+ /* Clear interrupt status */
+ val = hisi_sas_phy_read32(hisi_hba, i, CHL_INT0);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT0, val);
+ val = hisi_sas_phy_read32(hisi_hba, i, CHL_INT1);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT1, val);
+ val = hisi_sas_phy_read32(hisi_hba, i, CHL_INT2);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT2, val);
+
+ /* Unmask interrupt */
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT0_MSK, 0x3ce3ee);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT1_MSK, 0x17fff);
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0x8000012a);
+
+ /* bypass chip bug mask abnormal intr */
+ hisi_sas_phy_write32(hisi_hba, i, CHL_INT0_MSK,
+ 0x3fffff & ~CHL_INT0_MSK_PHYCTRL_NOTRDY_MSK);
+ }
+
+ return 0;
+}
+
+static int hisi_sas_v1_init(struct hisi_hba *hisi_hba)
+{
+ int rc;
+
+ rc = hw_init_v1_hw(hisi_hba);
+ if (rc)
+ return rc;
+
+ rc = interrupt_init_v1_hw(hisi_hba);
+ if (rc)
+ return rc;
+
+ rc = interrupt_openall_v1_hw(hisi_hba);
+ if (rc)
+ return rc;
+
+ phys_init_v1_hw(hisi_hba);
+
+ return 0;
+}
+
+static const struct hisi_sas_hw hisi_sas_v1_hw = {
+ .hw_init = hisi_sas_v1_init,
+ .setup_itct = setup_itct_v1_hw,
+ .sl_notify = sl_notify_v1_hw,
+ .free_device = free_device_v1_hw,
+ .prep_smp = prep_smp_v1_hw,
+ .prep_ssp = prep_ssp_v1_hw,
+ .get_free_slot = get_free_slot_v1_hw,
+ .start_delivery = start_delivery_v1_hw,
+ .slot_complete = slot_complete_v1_hw,
+ .phy_enable = enable_phy_v1_hw,
+ .phy_disable = disable_phy_v1_hw,
+ .phy_hard_reset = phy_hard_reset_v1_hw,
+ .get_wideport_bitmap = get_wideport_bitmap_v1_hw,
+ .complete_hdr_size = sizeof(struct hisi_sas_complete_v1_hdr),
+};
+
+static int hisi_sas_v1_probe(struct platform_device *pdev)
+{
+ return hisi_sas_probe(pdev, &hisi_sas_v1_hw);
+}
+
+static int hisi_sas_v1_remove(struct platform_device *pdev)
+{
+ return hisi_sas_remove(pdev);
+}
+
+static const struct of_device_id sas_v1_of_match[] = {
+ { .compatible = "hisilicon,hip05-sas-v1",},
+ {},
+};
+MODULE_DEVICE_TABLE(of, sas_v1_of_match);
+
+static struct platform_driver hisi_sas_v1_driver = {
+ .probe = hisi_sas_v1_probe,
+ .remove = hisi_sas_v1_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = sas_v1_of_match,
+ },
+};
+
+module_platform_driver(hisi_sas_v1_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Garry <john.garry@huawei.com>");
+MODULE_DESCRIPTION("HISILICON SAS controller v1 hw driver");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index 323982fd00c3..82ac1cd818ac 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -333,6 +333,17 @@ static void scsi_host_dev_release(struct device *dev)
kfree(queuedata);
}
+ if (shost->shost_state == SHOST_CREATED) {
+ /*
+ * Free the shost_dev device name here if scsi_host_alloc()
+ * and scsi_host_put() have been called but neither
+ * scsi_host_add() nor scsi_host_remove() has been called.
+ * This avoids that the memory allocated for the shost_dev
+ * name is leaked.
+ */
+ kfree(dev_name(&shost->shost_dev));
+ }
+
scsi_destroy_command_freelist(shost);
if (shost_use_blk_mq(shost)) {
if (shost->tag_set.tags)
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index 6a8f95808ee0..38ce0e308fbe 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -750,7 +750,6 @@ static ssize_t host_show_hp_ssd_smart_path_enabled(struct device *dev,
}
#define MAX_PATHS 8
-
static ssize_t path_info_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -792,10 +791,8 @@ static ssize_t path_info_show(struct device *dev,
hdev->bus, hdev->target, hdev->lun,
scsi_device_type(hdev->devtype));
- if (hdev->external ||
- hdev->devtype == TYPE_RAID ||
- is_logical_device(hdev)) {
- output_len += snprintf(buf + output_len,
+ if (hdev->devtype == TYPE_RAID || is_logical_device(hdev)) {
+ output_len += scnprintf(buf + output_len,
PAGE_SIZE - output_len,
"%s\n", active);
continue;
@@ -808,29 +805,28 @@ static ssize_t path_info_show(struct device *dev,
phys_connector[0] = '0';
if (phys_connector[1] < '0')
phys_connector[1] = '0';
- if (hdev->phys_connector[i] > 0)
- output_len += snprintf(buf + output_len,
+ output_len += scnprintf(buf + output_len,
PAGE_SIZE - output_len,
"PORT: %.2s ",
phys_connector);
if (hdev->devtype == TYPE_DISK && hdev->expose_device) {
if (box == 0 || box == 0xFF) {
- output_len += snprintf(buf + output_len,
+ output_len += scnprintf(buf + output_len,
PAGE_SIZE - output_len,
"BAY: %hhu %s\n",
bay, active);
} else {
- output_len += snprintf(buf + output_len,
+ output_len += scnprintf(buf + output_len,
PAGE_SIZE - output_len,
"BOX: %hhu BAY: %hhu %s\n",
box, bay, active);
}
} else if (box != 0 && box != 0xFF) {
- output_len += snprintf(buf + output_len,
+ output_len += scnprintf(buf + output_len,
PAGE_SIZE - output_len, "BOX: %hhu %s\n",
box, active);
} else
- output_len += snprintf(buf + output_len,
+ output_len += scnprintf(buf + output_len,
PAGE_SIZE - output_len, "%s\n", active);
}
@@ -3191,6 +3187,87 @@ out:
return rc;
}
+/*
+ * get enclosure information
+ * struct ReportExtendedLUNdata *rlep - Used for BMIC drive number
+ * struct hpsa_scsi_dev_t *encl_dev - device entry for enclosure
+ * Uses id_physical_device to determine the box_index.
+ */
+static void hpsa_get_enclosure_info(struct ctlr_info *h,
+ unsigned char *scsi3addr,
+ struct ReportExtendedLUNdata *rlep, int rle_index,
+ struct hpsa_scsi_dev_t *encl_dev)
+{
+ int rc = -1;
+ struct CommandList *c = NULL;
+ struct ErrorInfo *ei = NULL;
+ struct bmic_sense_storage_box_params *bssbp = NULL;
+ struct bmic_identify_physical_device *id_phys = NULL;
+ struct ext_report_lun_entry *rle = &rlep->LUN[rle_index];
+ u16 bmic_device_index = 0;
+
+ bmic_device_index = GET_BMIC_DRIVE_NUMBER(&rle->lunid[0]);
+
+ if (bmic_device_index == 0xFF00)
+ goto out;
+
+ bssbp = kzalloc(sizeof(*bssbp), GFP_KERNEL);
+ if (!bssbp)
+ goto out;
+
+ id_phys = kzalloc(sizeof(*id_phys), GFP_KERNEL);
+ if (!id_phys)
+ goto out;
+
+ rc = hpsa_bmic_id_physical_device(h, scsi3addr, bmic_device_index,
+ id_phys, sizeof(*id_phys));
+ if (rc) {
+ dev_warn(&h->pdev->dev, "%s: id_phys failed %d bdi[0x%x]\n",
+ __func__, encl_dev->external, bmic_device_index);
+ goto out;
+ }
+
+ c = cmd_alloc(h);
+
+ rc = fill_cmd(c, BMIC_SENSE_STORAGE_BOX_PARAMS, h, bssbp,
+ sizeof(*bssbp), 0, RAID_CTLR_LUNID, TYPE_CMD);
+
+ if (rc)
+ goto out;
+
+ if (id_phys->phys_connector[1] == 'E')
+ c->Request.CDB[5] = id_phys->box_index;
+ else
+ c->Request.CDB[5] = 0;
+
+ 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) {
+ rc = -1;
+ goto out;
+ }
+
+ encl_dev->box[id_phys->active_path_number] = bssbp->phys_box_on_port;
+ memcpy(&encl_dev->phys_connector[id_phys->active_path_number],
+ bssbp->phys_connector, sizeof(bssbp->phys_connector));
+
+ rc = IO_OK;
+out:
+ kfree(bssbp);
+ kfree(id_phys);
+
+ if (c)
+ cmd_free(h, c);
+
+ if (rc != IO_OK)
+ hpsa_show_dev_msg(KERN_INFO, h, encl_dev,
+ "Error, could not get enclosure information\n");
+}
+
static u64 hpsa_get_sas_address_from_report_physical(struct ctlr_info *h,
unsigned char *scsi3addr)
{
@@ -4032,7 +4109,8 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h)
/* skip masked non-disk devices */
if (MASKED_DEVICE(lunaddrbytes) && physical_device &&
- (physdev_list->LUN[phys_dev_index].device_flags & 0x01))
+ (physdev_list->LUN[phys_dev_index].device_type != 0x06) &&
+ (physdev_list->LUN[phys_dev_index].device_flags & 0x01))
continue;
/* Get device type, vendor, model, device id */
@@ -4116,7 +4194,12 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h)
break;
case TYPE_TAPE:
case TYPE_MEDIUM_CHANGER:
+ ncurrent++;
+ break;
case TYPE_ENCLOSURE:
+ hpsa_get_enclosure_info(h, lunaddrbytes,
+ physdev_list, phys_dev_index,
+ this_device);
ncurrent++;
break;
case TYPE_RAID:
@@ -6629,6 +6712,16 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
c->Request.CDB[7] = (size >> 16) & 0xFF;
c->Request.CDB[8] = (size >> 8) & 0XFF;
break;
+ case BMIC_SENSE_STORAGE_BOX_PARAMS:
+ c->Request.CDBLen = 10;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ);
+ c->Request.Timeout = 0;
+ c->Request.CDB[0] = BMIC_READ;
+ c->Request.CDB[6] = BMIC_SENSE_STORAGE_BOX_PARAMS;
+ c->Request.CDB[7] = (size >> 16) & 0xFF;
+ c->Request.CDB[8] = (size >> 8) & 0XFF;
+ break;
case BMIC_IDENTIFY_CONTROLLER:
c->Request.CDBLen = 10;
c->Request.type_attr_dir =
@@ -8671,7 +8764,7 @@ static void hpsa_disable_rld_caching(struct ctlr_info *h)
if ((rc != 0) || (c->err_info->CommandStatus != 0))
goto errout;
- if (*options && HPSA_DIAG_OPTS_DISABLE_RLD_CACHING)
+ if (*options & HPSA_DIAG_OPTS_DISABLE_RLD_CACHING)
goto out;
errout:
diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h
index ae5beda1bdb5..fdd39fc0b199 100644
--- a/drivers/scsi/hpsa.h
+++ b/drivers/scsi/hpsa.h
@@ -400,7 +400,7 @@ struct offline_device_entry {
#define HPSA_PHYSICAL_DEVICE_BUS 0
#define HPSA_RAID_VOLUME_BUS 1
#define HPSA_EXTERNAL_RAID_VOLUME_BUS 2
-#define HPSA_HBA_BUS 3
+#define HPSA_HBA_BUS 0
/*
Send the command to the hardware
diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h
index d92ef0d352b5..6a919ada96b3 100644
--- a/drivers/scsi/hpsa_cmd.h
+++ b/drivers/scsi/hpsa_cmd.h
@@ -291,6 +291,7 @@ struct SenseSubsystem_info {
#define BMIC_SENSE_DIAG_OPTIONS 0xF5
#define HPSA_DIAG_OPTS_DISABLE_RLD_CACHING 0x40000000
#define BMIC_SENSE_SUBSYSTEM_INFORMATION 0x66
+#define BMIC_SENSE_STORAGE_BOX_PARAMS 0x65
/* Command List Structure */
union SCSI3Addr {
@@ -842,5 +843,17 @@ struct bmic_sense_subsystem_info {
u8 pad[332];
};
+struct bmic_sense_storage_box_params {
+ u8 reserved[36];
+ u8 inquiry_valid;
+ u8 reserved_1[68];
+ u8 phys_box_on_port;
+ u8 reserved_2[22];
+ u16 connection_info;
+ u8 reserver_3[84];
+ u8 phys_connector[2];
+ u8 reserved_4[296];
+};
+
#pragma pack()
#endif /* HPSA_CMD_H */
diff --git a/drivers/scsi/initio.c b/drivers/scsi/initio.c
index 6a926bae76b2..7a91cf3ff173 100644
--- a/drivers/scsi/initio.c
+++ b/drivers/scsi/initio.c
@@ -110,11 +110,6 @@
#define i91u_MAXQUEUE 2
#define i91u_REVID "Initio INI-9X00U/UW SCSI device driver; Revision: 1.04a"
-#define I950_DEVICE_ID 0x9500 /* Initio's inic-950 product ID */
-#define I940_DEVICE_ID 0x9400 /* Initio's inic-940 product ID */
-#define I935_DEVICE_ID 0x9401 /* Initio's inic-935 product ID */
-#define I920_DEVICE_ID 0x0002 /* Initio's other product ID */
-
#ifdef DEBUG_i91u
static unsigned int i91u_debug = DEBUG_DEFAULT;
#endif
@@ -127,17 +122,6 @@ static int setup_debug = 0;
static void i91uSCBPost(u8 * pHcb, u8 * pScb);
-/* PCI Devices supported by this driver */
-static struct pci_device_id i91u_pci_devices[] = {
- { PCI_VENDOR_ID_INIT, I950_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- { PCI_VENDOR_ID_INIT, I940_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- { PCI_VENDOR_ID_INIT, I935_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- { PCI_VENDOR_ID_INIT, I920_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- { PCI_VENDOR_ID_DOMEX, I920_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- { }
-};
-MODULE_DEVICE_TABLE(pci, i91u_pci_devices);
-
#define DEBUG_INTERRUPT 0
#define DEBUG_QUEUE 0
#define DEBUG_STATE 0
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index ceee9a3fd9e5..90a3ca5a4dbd 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -386,7 +386,6 @@ struct lpfc_vport {
uint32_t work_port_events; /* Timeout to be handled */
#define WORKER_DISC_TMO 0x1 /* vport: Discovery timeout */
#define WORKER_ELS_TMO 0x2 /* vport: ELS timeout */
-#define WORKER_FDMI_TMO 0x4 /* vport: FDMI timeout */
#define WORKER_DELAYED_DISC_TMO 0x8 /* vport: delayed discovery */
#define WORKER_MBOX_TMO 0x100 /* hba: MBOX timeout */
@@ -396,7 +395,6 @@ struct lpfc_vport {
#define WORKER_RAMP_UP_QUEUE 0x1000 /* hba: Increase Q depth */
#define WORKER_SERVICE_TXQ 0x2000 /* hba: IOCBs on the txq */
- struct timer_list fc_fdmitmo;
struct timer_list els_tmofunc;
struct timer_list delayed_disc_tmo;
@@ -405,6 +403,7 @@ struct lpfc_vport {
uint8_t load_flag;
#define FC_LOADING 0x1 /* HBA in process of loading drvr */
#define FC_UNLOADING 0x2 /* HBA in process of unloading drvr */
+#define FC_ALLOW_FDMI 0x4 /* port is ready for FDMI requests */
/* Vport Config Parameters */
uint32_t cfg_scan_down;
uint32_t cfg_lun_queue_depth;
@@ -414,10 +413,6 @@ struct lpfc_vport {
uint32_t cfg_peer_port_login;
uint32_t cfg_fcp_class;
uint32_t cfg_use_adisc;
- uint32_t cfg_fdmi_on;
-#define LPFC_FDMI_SUPPORT 1 /* bit 0 - FDMI supported? */
-#define LPFC_FDMI_REG_DELAY 2 /* bit 1 - 60 sec registration delay */
-#define LPFC_FDMI_ALL_ATTRIB 4 /* bit 2 - register ALL attributes? */
uint32_t cfg_discovery_threads;
uint32_t cfg_log_verbose;
uint32_t cfg_max_luns;
@@ -443,6 +438,10 @@ struct lpfc_vport {
unsigned long rcv_buffer_time_stamp;
uint32_t vport_flag;
#define STATIC_VPORT 1
+
+ uint16_t fdmi_num_disc;
+ uint32_t fdmi_hba_mask;
+ uint32_t fdmi_port_mask;
};
struct hbq_s {
@@ -755,6 +754,11 @@ struct lpfc_hba {
#define LPFC_DELAY_INIT_LINK 1 /* layered driver hold off */
#define LPFC_DELAY_INIT_LINK_INDEFINITELY 2 /* wait, manual intervention */
uint32_t cfg_enable_dss;
+ uint32_t cfg_fdmi_on;
+#define LPFC_FDMI_NO_SUPPORT 0 /* FDMI not supported */
+#define LPFC_FDMI_SUPPORT 1 /* FDMI supported? */
+#define LPFC_FDMI_SMART_SAN 2 /* SmartSAN supported */
+ uint32_t cfg_enable_SmartSAN;
lpfc_vpd_t vpd; /* vital product data */
struct pci_dev *pcidev;
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index f6446d759d7f..343ae9482891 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -4572,19 +4572,27 @@ LPFC_ATTR_R(multi_ring_type, FC_TYPE_IP, 1,
255, "Identifies TYPE for additional ring configuration");
/*
-# lpfc_fdmi_on: controls FDMI support.
-# Set NOT Set
-# bit 0 = FDMI support no FDMI support
-# LPFC_FDMI_SUPPORT just turns basic support on/off
-# bit 1 = Register delay no register delay (60 seconds)
-# LPFC_FDMI_REG_DELAY 60 sec registration delay after FDMI login
-# bit 2 = All attributes Use a attribute subset
-# LPFC_FDMI_ALL_ATTRIB applies to both port and HBA attributes
-# Port attrutes subset: 1 thru 6 OR all: 1 thru 0xd 0x101 0x102 0x103
-# HBA attributes subset: 1 thru 0xb OR all: 1 thru 0xc
-# Value range [0,7]. Default value is 0.
+# lpfc_enable_SmartSAN: Sets up FDMI support for SmartSAN
+# 0 = SmartSAN functionality disabled (default)
+# 1 = SmartSAN functionality enabled
+# This parameter will override the value of lpfc_fdmi_on module parameter.
+# Value range is [0,1]. Default value is 0.
*/
-LPFC_VPORT_ATTR_RW(fdmi_on, 0, 0, 7, "Enable FDMI support");
+LPFC_ATTR_R(enable_SmartSAN, 0, 0, 1, "Enable SmartSAN functionality");
+
+/*
+# lpfc_fdmi_on: Controls FDMI support.
+# 0 No FDMI support (default)
+# 1 Traditional FDMI support
+# 2 Smart SAN support
+# If lpfc_enable_SmartSAN is set 1, the driver sets lpfc_fdmi_on to value 2
+# overwriting the current value. If lpfc_enable_SmartSAN is set 0, the
+# driver uses the current value of lpfc_fdmi_on provided it has value 0 or 1.
+# A value of 2 with lpfc_enable_SmartSAN set to 0 causes the driver to
+# set lpfc_fdmi_on back to 1.
+# Value range [0,2]. Default value is 0.
+*/
+LPFC_ATTR_R(fdmi_on, 0, 0, 2, "Enable FDMI support");
/*
# Specifies the maximum number of ELS cmds we can have outstanding (for
@@ -4815,6 +4823,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
&dev_attr_lpfc_multi_ring_rctl,
&dev_attr_lpfc_multi_ring_type,
&dev_attr_lpfc_fdmi_on,
+ &dev_attr_lpfc_enable_SmartSAN,
&dev_attr_lpfc_max_luns,
&dev_attr_lpfc_enable_npiv,
&dev_attr_lpfc_fcf_failover_policy,
@@ -4887,7 +4896,6 @@ struct device_attribute *lpfc_vport_attrs[] = {
&dev_attr_lpfc_fcp_class,
&dev_attr_lpfc_use_adisc,
&dev_attr_lpfc_first_burst_size,
- &dev_attr_lpfc_fdmi_on,
&dev_attr_lpfc_max_luns,
&dev_attr_nport_evt_cnt,
&dev_attr_npiv_info,
@@ -5247,7 +5255,7 @@ lpfc_get_host_speed(struct Scsi_Host *shost)
spin_lock_irq(shost->host_lock);
- if (lpfc_is_link_up(phba)) {
+ if ((lpfc_is_link_up(phba)) && (!(phba->hba_flag & HBA_FCOE_MODE))) {
switch(phba->fc_linkspeed) {
case LPFC_LINK_SPEED_1GHZ:
fc_host_speed(shost) = FC_PORTSPEED_1GBIT;
@@ -5826,6 +5834,8 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
lpfc_enable_npiv_init(phba, lpfc_enable_npiv);
lpfc_fcf_failover_policy_init(phba, lpfc_fcf_failover_policy);
lpfc_enable_rrq_init(phba, lpfc_enable_rrq);
+ lpfc_fdmi_on_init(phba, lpfc_fdmi_on);
+ lpfc_enable_SmartSAN_init(phba, lpfc_enable_SmartSAN);
lpfc_use_msi_init(phba, lpfc_use_msi);
lpfc_fcp_imax_init(phba, lpfc_fcp_imax);
lpfc_fcp_cpu_map_init(phba, lpfc_fcp_cpu_map);
@@ -5846,6 +5856,15 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
phba->cfg_poll = 0;
else
phba->cfg_poll = lpfc_poll;
+
+ /* Ensure fdmi_on and enable_SmartSAN don't conflict */
+ if (phba->cfg_enable_SmartSAN) {
+ phba->cfg_fdmi_on = LPFC_FDMI_SMART_SAN;
+ } else {
+ if (phba->cfg_fdmi_on == LPFC_FDMI_SMART_SAN)
+ phba->cfg_fdmi_on = LPFC_FDMI_SUPPORT;
+ }
+
phba->cfg_soft_wwnn = 0L;
phba->cfg_soft_wwpn = 0L;
lpfc_sg_seg_cnt_init(phba, lpfc_sg_seg_cnt);
@@ -5879,7 +5898,6 @@ lpfc_get_vport_cfgparam(struct lpfc_vport *vport)
lpfc_use_adisc_init(vport, lpfc_use_adisc);
lpfc_first_burst_size_init(vport, lpfc_first_burst_size);
lpfc_max_scsicmpl_time_init(vport, lpfc_max_scsicmpl_time);
- lpfc_fdmi_on_init(vport, lpfc_fdmi_on);
lpfc_discovery_threads_init(vport, lpfc_discovery_threads);
lpfc_max_luns_init(vport, lpfc_max_luns);
lpfc_scan_down_init(vport, lpfc_scan_down);
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index b0e6fe46448d..4e55b35180a4 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -72,6 +72,7 @@ void lpfc_cancel_all_vport_retry_delay_timer(struct lpfc_hba *);
void lpfc_retry_pport_discovery(struct lpfc_hba *);
void lpfc_release_rpi(struct lpfc_hba *, struct lpfc_vport *, uint16_t);
+void lpfc_mbx_cmpl_local_config_link(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_mbx_cmpl_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *);
@@ -167,9 +168,8 @@ void lpfc_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
struct lpfc_iocbq *);
int lpfc_ct_handle_unsol_abort(struct lpfc_hba *, struct hbq_dmabuf *);
int lpfc_ns_cmd(struct lpfc_vport *, int, uint8_t, uint32_t);
-int lpfc_fdmi_cmd(struct lpfc_vport *, struct lpfc_nodelist *, int);
-void lpfc_fdmi_tmo(unsigned long);
-void lpfc_fdmi_timeout_handler(struct lpfc_vport *);
+int lpfc_fdmi_cmd(struct lpfc_vport *, struct lpfc_nodelist *, int, uint32_t);
+void lpfc_fdmi_num_disc_check(struct lpfc_vport *);
void lpfc_delayed_disc_tmo(unsigned long);
void lpfc_delayed_disc_timeout_handler(struct lpfc_vport *);
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
index 8fded1f7605f..79e261d2a0c8 100644
--- a/drivers/scsi/lpfc/lpfc_ct.c
+++ b/drivers/scsi/lpfc/lpfc_ct.c
@@ -48,15 +48,26 @@
#include "lpfc_vport.h"
#include "lpfc_debugfs.h"
-/* FDMI Port Speed definitions */
-#define HBA_PORTSPEED_1GBIT 0x0001 /* 1 GBit/sec */
-#define HBA_PORTSPEED_2GBIT 0x0002 /* 2 GBit/sec */
-#define HBA_PORTSPEED_4GBIT 0x0008 /* 4 GBit/sec */
-#define HBA_PORTSPEED_10GBIT 0x0004 /* 10 GBit/sec */
-#define HBA_PORTSPEED_8GBIT 0x0010 /* 8 GBit/sec */
-#define HBA_PORTSPEED_16GBIT 0x0020 /* 16 GBit/sec */
-#define HBA_PORTSPEED_32GBIT 0x0040 /* 32 GBit/sec */
-#define HBA_PORTSPEED_UNKNOWN 0x0800 /* Unknown */
+/* FDMI Port Speed definitions - FC-GS-7 */
+#define HBA_PORTSPEED_1GFC 0x00000001 /* 1G FC */
+#define HBA_PORTSPEED_2GFC 0x00000002 /* 2G FC */
+#define HBA_PORTSPEED_4GFC 0x00000008 /* 4G FC */
+#define HBA_PORTSPEED_10GFC 0x00000004 /* 10G FC */
+#define HBA_PORTSPEED_8GFC 0x00000010 /* 8G FC */
+#define HBA_PORTSPEED_16GFC 0x00000020 /* 16G FC */
+#define HBA_PORTSPEED_32GFC 0x00000040 /* 32G FC */
+#define HBA_PORTSPEED_20GFC 0x00000080 /* 20G FC */
+#define HBA_PORTSPEED_40GFC 0x00000100 /* 40G FC */
+#define HBA_PORTSPEED_128GFC 0x00000200 /* 128G FC */
+#define HBA_PORTSPEED_64GFC 0x00000400 /* 64G FC */
+#define HBA_PORTSPEED_256GFC 0x00000800 /* 256G FC */
+#define HBA_PORTSPEED_UNKNOWN 0x00008000 /* Unknown */
+#define HBA_PORTSPEED_10GE 0x00010000 /* 10G E */
+#define HBA_PORTSPEED_40GE 0x00020000 /* 40G E */
+#define HBA_PORTSPEED_100GE 0x00040000 /* 100G E */
+#define HBA_PORTSPEED_25GE 0x00080000 /* 25G E */
+#define HBA_PORTSPEED_50GE 0x00100000 /* 50G E */
+#define HBA_PORTSPEED_400GE 0x00200000 /* 400G E */
#define FOURBYTES 4
@@ -287,6 +298,17 @@ lpfc_ct_free_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *ctiocb)
return 0;
}
+/**
+ * lpfc_gen_req - Build and issue a GEN_REQUEST command to the SLI Layer
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @bmp: Pointer to BPL for SLI command
+ * @inp: Pointer to data buffer for response data.
+ * @outp: Pointer to data buffer that hold the CT command.
+ * @cmpl: completion routine to call when command completes
+ * @ndlp: Destination NPort nodelist entry
+ *
+ * This function as the final part for issuing a CT command.
+ */
static int
lpfc_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp,
struct lpfc_dmabuf *inp, struct lpfc_dmabuf *outp,
@@ -311,7 +333,7 @@ lpfc_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp,
icmd->un.genreq64.bdl.addrHigh = putPaddrHigh(bmp->phys);
icmd->un.genreq64.bdl.addrLow = putPaddrLow(bmp->phys);
icmd->un.genreq64.bdl.bdeFlags = BUFF_TYPE_BLP_64;
- icmd->un.genreq64.bdl.bdeSize = (num_entry * sizeof (struct ulp_bde64));
+ icmd->un.genreq64.bdl.bdeSize = (num_entry * sizeof(struct ulp_bde64));
if (usr_flg)
geniocb->context3 = NULL;
@@ -370,6 +392,16 @@ lpfc_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp,
return 0;
}
+/**
+ * lpfc_ct_cmd - Build and issue a CT command
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @inmp: Pointer to data buffer for response data.
+ * @bmp: Pointer to BPL for SLI command
+ * @ndlp: Destination NPort nodelist entry
+ * @cmpl: completion routine to call when command completes
+ *
+ * This function is called for issuing a CT command.
+ */
static int
lpfc_ct_cmd(struct lpfc_vport *vport, struct lpfc_dmabuf *inmp,
struct lpfc_dmabuf *bmp, struct lpfc_nodelist *ndlp,
@@ -453,7 +485,7 @@ lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint32_t Size)
Cnt -= 16; /* subtract length of CT header */
/* Loop through entire NameServer list of DIDs */
- while (Cnt >= sizeof (uint32_t)) {
+ while (Cnt >= sizeof(uint32_t)) {
/* Get next DID from NameServer List */
CTentry = *ctptr++;
Did = ((be32_to_cpu(CTentry)) & Mask_DID);
@@ -558,7 +590,7 @@ lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint32_t Size)
}
if (CTentry & (cpu_to_be32(SLI_CT_LAST_ENTRY)))
goto nsout1;
- Cnt -= sizeof (uint32_t);
+ Cnt -= sizeof(uint32_t);
}
ctptr = NULL;
@@ -1146,7 +1178,7 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
/* fill in BDEs for command */
/* Allocate buffer for command payload */
- mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL);
+ mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
if (!mp) {
rc=2;
goto ns_cmd_exit;
@@ -1160,7 +1192,7 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
}
/* Allocate buffer for Buffer ptr list */
- bmp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL);
+ bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
if (!bmp) {
rc=4;
goto ns_cmd_free_mpvirt;
@@ -1204,7 +1236,7 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
bpl->tus.w = le32_to_cpu(bpl->tus.w);
CtReq = (struct lpfc_sli_ct_request *) mp->virt;
- memset(CtReq, 0, sizeof (struct lpfc_sli_ct_request));
+ memset(CtReq, 0, sizeof(struct lpfc_sli_ct_request));
CtReq->RevisionId.bits.Revision = SLI_CT_REVISION;
CtReq->RevisionId.bits.InId = 0;
CtReq->FsType = SLI_CT_DIRECTORY_SERVICE;
@@ -1244,7 +1276,7 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
cpu_to_be16(SLI_CTNS_RNN_ID);
CtReq->un.rnn.PortId = cpu_to_be32(vport->fc_myDID);
memcpy(CtReq->un.rnn.wwnn, &vport->fc_nodename,
- sizeof (struct lpfc_name));
+ sizeof(struct lpfc_name));
cmpl = lpfc_cmpl_ct_cmd_rnn_id;
break;
@@ -1264,7 +1296,7 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
CtReq->CommandResponse.bits.CmdRsp =
cpu_to_be16(SLI_CTNS_RSNN_NN);
memcpy(CtReq->un.rsnn.wwnn, &vport->fc_nodename,
- sizeof (struct lpfc_name));
+ sizeof(struct lpfc_name));
size = sizeof(CtReq->un.rsnn.symbname);
CtReq->un.rsnn.len =
lpfc_vport_symbolic_node_name(vport,
@@ -1319,20 +1351,29 @@ ns_cmd_exit:
return 1;
}
+/**
+ * lpfc_cmpl_ct_disc_fdmi - Handle a discovery FDMI completion
+ * @phba: Pointer to HBA context object.
+ * @cmdiocb: Pointer to the command IOCBQ.
+ * @rspiocb: Pointer to the response IOCBQ.
+ *
+ * This function to handle the completion of a driver initiated FDMI
+ * CT command issued during discovery.
+ */
static void
-lpfc_cmpl_ct_cmd_fdmi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
- struct lpfc_iocbq * rspiocb)
+lpfc_cmpl_ct_disc_fdmi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
+ struct lpfc_iocbq *rspiocb)
{
+ struct lpfc_vport *vport = cmdiocb->vport;
struct lpfc_dmabuf *inp = cmdiocb->context1;
struct lpfc_dmabuf *outp = cmdiocb->context2;
- struct lpfc_sli_ct_request *CTrsp = outp->virt;
struct lpfc_sli_ct_request *CTcmd = inp->virt;
- struct lpfc_nodelist *ndlp;
+ struct lpfc_sli_ct_request *CTrsp = outp->virt;
uint16_t fdmi_cmd = CTcmd->CommandResponse.bits.CmdRsp;
uint16_t fdmi_rsp = CTrsp->CommandResponse.bits.CmdRsp;
- struct lpfc_vport *vport = cmdiocb->vport;
IOCB_t *irsp = &rspiocb->iocb;
- uint32_t latt;
+ struct lpfc_nodelist *ndlp;
+ uint32_t latt, cmd, err;
latt = lpfc_els_chk_latt(vport);
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT,
@@ -1340,91 +1381,1115 @@ lpfc_cmpl_ct_cmd_fdmi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
irsp->ulpStatus, irsp->un.ulpWord[4], latt);
if (latt || irsp->ulpStatus) {
+
+ /* Look for a retryable error */
+ if (irsp->ulpStatus == IOSTAT_LOCAL_REJECT) {
+ switch ((irsp->un.ulpWord[4] & IOERR_PARAM_MASK)) {
+ case IOERR_SLI_ABORTED:
+ case IOERR_ABORT_IN_PROGRESS:
+ case IOERR_SEQUENCE_TIMEOUT:
+ case IOERR_ILLEGAL_FRAME:
+ case IOERR_NO_RESOURCES:
+ case IOERR_ILLEGAL_COMMAND:
+ cmdiocb->retry++;
+ if (cmdiocb->retry >= LPFC_FDMI_MAX_RETRY)
+ break;
+
+ /* Retry the same FDMI command */
+ err = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING,
+ cmdiocb, 0);
+ if (err == IOCB_ERROR)
+ break;
+ return;
+ default:
+ break;
+ }
+ }
+
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
"0229 FDMI cmd %04x failed, latt = %d "
"ulpStatus: x%x, rid x%x\n",
be16_to_cpu(fdmi_cmd), latt, irsp->ulpStatus,
irsp->un.ulpWord[4]);
- goto fail_out;
}
+ lpfc_ct_free_iocb(phba, cmdiocb);
ndlp = lpfc_findnode_did(vport, FDMI_DID);
if (!ndlp || !NLP_CHK_NODE_ACT(ndlp))
- goto fail_out;
+ return;
+ /* Check for a CT LS_RJT response */
+ cmd = be16_to_cpu(fdmi_cmd);
if (fdmi_rsp == cpu_to_be16(SLI_CT_RESPONSE_FS_RJT)) {
/* FDMI rsp failed */
lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY,
- "0220 FDMI rsp failed Data: x%x\n",
- be16_to_cpu(fdmi_cmd));
+ "0220 FDMI cmd failed FS_RJT Data: x%x", cmd);
+
+ /* Should we fallback to FDMI-2 / FDMI-1 ? */
+ switch (cmd) {
+ case SLI_MGMT_RHBA:
+ if (vport->fdmi_hba_mask == LPFC_FDMI2_HBA_ATTR) {
+ /* Fallback to FDMI-1 */
+ vport->fdmi_hba_mask = LPFC_FDMI1_HBA_ATTR;
+ vport->fdmi_port_mask = LPFC_FDMI1_PORT_ATTR;
+ /* Start over */
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DHBA, 0);
+ }
+ return;
+
+ case SLI_MGMT_RPRT:
+ if (vport->fdmi_port_mask == LPFC_FDMI2_PORT_ATTR) {
+ /* Fallback to FDMI-1 */
+ vport->fdmi_port_mask = LPFC_FDMI1_PORT_ATTR;
+ /* Start over */
+ lpfc_fdmi_cmd(vport, ndlp, cmd, 0);
+ }
+ if (vport->fdmi_port_mask == LPFC_FDMI2_SMART_ATTR) {
+ vport->fdmi_port_mask = LPFC_FDMI2_PORT_ATTR;
+ /* Retry the same command */
+ lpfc_fdmi_cmd(vport, ndlp, cmd, 0);
+ }
+ return;
+
+ case SLI_MGMT_RPA:
+ if (vport->fdmi_port_mask == LPFC_FDMI2_PORT_ATTR) {
+ /* Fallback to FDMI-1 */
+ vport->fdmi_hba_mask = LPFC_FDMI1_HBA_ATTR;
+ vport->fdmi_port_mask = LPFC_FDMI1_PORT_ATTR;
+ /* Start over */
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DHBA, 0);
+ }
+ if (vport->fdmi_port_mask == LPFC_FDMI2_SMART_ATTR) {
+ vport->fdmi_port_mask = LPFC_FDMI2_PORT_ATTR;
+ /* Retry the same command */
+ lpfc_fdmi_cmd(vport, ndlp, cmd, 0);
+ }
+ return;
+ }
}
-fail_out:
- lpfc_ct_free_iocb(phba, cmdiocb);
+ /*
+ * On success, need to cycle thru FDMI registration for discovery
+ * DHBA -> DPRT -> RHBA -> RPA (physical port)
+ * DPRT -> RPRT (vports)
+ */
+ switch (cmd) {
+ case SLI_MGMT_RHBA:
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RPA, 0);
+ break;
+
+ case SLI_MGMT_DHBA:
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DPRT, 0);
+ break;
+
+ case SLI_MGMT_DPRT:
+ if (vport->port_type == LPFC_PHYSICAL_PORT)
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RHBA, 0);
+ else
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RPRT, 0);
+ break;
+ }
+ return;
}
-static void
-lpfc_cmpl_ct_disc_fdmi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
- struct lpfc_iocbq *rspiocb)
+
+/**
+ * lpfc_fdmi_num_disc_check - Check how many mapped NPorts we are connected to
+ * @vport: pointer to a host virtual N_Port data structure.
+ *
+ * Called from hbeat timeout routine to check if the number of discovered
+ * ports has changed. If so, re-register thar port Attribute.
+ */
+void
+lpfc_fdmi_num_disc_check(struct lpfc_vport *vport)
{
- struct lpfc_vport *vport = cmdiocb->vport;
- struct lpfc_dmabuf *inp = cmdiocb->context1;
- struct lpfc_sli_ct_request *CTcmd = inp->virt;
- uint16_t fdmi_cmd = CTcmd->CommandResponse.bits.CmdRsp;
+ struct lpfc_hba *phba = vport->phba;
struct lpfc_nodelist *ndlp;
+ uint16_t cnt;
+
+ if (!lpfc_is_link_up(phba))
+ return;
+
+ if (!(vport->fdmi_port_mask & LPFC_FDMI_PORT_ATTR_num_disc))
+ return;
- lpfc_cmpl_ct_cmd_fdmi(phba, cmdiocb, rspiocb);
+ cnt = lpfc_find_map_node(vport);
+ if (cnt == vport->fdmi_num_disc)
+ return;
ndlp = lpfc_findnode_did(vport, FDMI_DID);
if (!ndlp || !NLP_CHK_NODE_ACT(ndlp))
return;
- /*
- * Need to cycle thru FDMI registration for discovery
- * DHBA -> DPRT -> RHBA -> RPA
- */
- switch (be16_to_cpu(fdmi_cmd)) {
- case SLI_MGMT_RHBA:
- lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RPA);
- break;
+ if (vport->port_type == LPFC_PHYSICAL_PORT) {
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RPA,
+ LPFC_FDMI_PORT_ATTR_num_disc);
+ } else {
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RPRT,
+ LPFC_FDMI_PORT_ATTR_num_disc);
+ }
+}
- case SLI_MGMT_DHBA:
- lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DPRT);
- break;
+/* Routines for all individual HBA attributes */
+int
+lpfc_fdmi_hba_attr_wwnn(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
- case SLI_MGMT_DPRT:
- lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_RHBA);
- break;
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, sizeof(struct lpfc_name));
+
+ memcpy(&ae->un.AttrWWN, &vport->fc_sparam.nodeName,
+ sizeof(struct lpfc_name));
+ size = FOURBYTES + sizeof(struct lpfc_name);
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RHBA_NODENAME);
+ return size;
+}
+int
+lpfc_fdmi_hba_attr_manufacturer(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t len, size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 256);
+
+ strncpy(ae->un.AttrString,
+ "Emulex Corporation",
+ sizeof(ae->un.AttrString));
+ len = strnlen(ae->un.AttrString,
+ sizeof(ae->un.AttrString));
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ size = FOURBYTES + len;
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RHBA_MANUFACTURER);
+ return size;
+}
+
+int
+lpfc_fdmi_hba_attr_sn(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t len, size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 256);
+
+ strncpy(ae->un.AttrString, phba->SerialNumber,
+ sizeof(ae->un.AttrString));
+ len = strnlen(ae->un.AttrString,
+ sizeof(ae->un.AttrString));
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ size = FOURBYTES + len;
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RHBA_SERIAL_NUMBER);
+ return size;
+}
+
+int
+lpfc_fdmi_hba_attr_model(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t len, size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 256);
+
+ strncpy(ae->un.AttrString, phba->ModelName,
+ sizeof(ae->un.AttrString));
+ len = strnlen(ae->un.AttrString, sizeof(ae->un.AttrString));
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ size = FOURBYTES + len;
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RHBA_MODEL);
+ return size;
+}
+
+int
+lpfc_fdmi_hba_attr_description(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t len, size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 256);
+
+ strncpy(ae->un.AttrString, phba->ModelDesc,
+ sizeof(ae->un.AttrString));
+ len = strnlen(ae->un.AttrString,
+ sizeof(ae->un.AttrString));
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ size = FOURBYTES + len;
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RHBA_MODEL_DESCRIPTION);
+ return size;
+}
+
+int
+lpfc_fdmi_hba_attr_hdw_ver(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_hba *phba = vport->phba;
+ lpfc_vpd_t *vp = &phba->vpd;
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t i, j, incr, size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 256);
+
+ /* Convert JEDEC ID to ascii for hardware version */
+ incr = vp->rev.biuRev;
+ for (i = 0; i < 8; i++) {
+ j = (incr & 0xf);
+ if (j <= 9)
+ ae->un.AttrString[7 - i] =
+ (char)((uint8_t) 0x30 +
+ (uint8_t) j);
+ else
+ ae->un.AttrString[7 - i] =
+ (char)((uint8_t) 0x61 +
+ (uint8_t) (j - 10));
+ incr = (incr >> 4);
}
+ size = FOURBYTES + 8;
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RHBA_HARDWARE_VERSION);
+ return size;
+}
+
+int
+lpfc_fdmi_hba_attr_drvr_ver(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t len, size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 256);
+
+ strncpy(ae->un.AttrString, lpfc_release_version,
+ sizeof(ae->un.AttrString));
+ len = strnlen(ae->un.AttrString,
+ sizeof(ae->un.AttrString));
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ size = FOURBYTES + len;
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RHBA_DRIVER_VERSION);
+ return size;
+}
+
+int
+lpfc_fdmi_hba_attr_rom_ver(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t len, size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 256);
+
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ lpfc_decode_firmware_rev(phba, ae->un.AttrString, 1);
+ else
+ strncpy(ae->un.AttrString, phba->OptionROMVersion,
+ sizeof(ae->un.AttrString));
+ len = strnlen(ae->un.AttrString,
+ sizeof(ae->un.AttrString));
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ size = FOURBYTES + len;
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RHBA_OPTION_ROM_VERSION);
+ return size;
+}
+
+int
+lpfc_fdmi_hba_attr_fmw_ver(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t len, size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 256);
+
+ lpfc_decode_firmware_rev(phba, ae->un.AttrString, 1);
+ len = strnlen(ae->un.AttrString,
+ sizeof(ae->un.AttrString));
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ size = FOURBYTES + len;
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RHBA_FIRMWARE_VERSION);
+ return size;
+}
+
+int
+lpfc_fdmi_hba_attr_os_ver(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t len, size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 256);
+
+ snprintf(ae->un.AttrString, sizeof(ae->un.AttrString), "%s %s %s",
+ init_utsname()->sysname,
+ init_utsname()->release,
+ init_utsname()->version);
+
+ len = strnlen(ae->un.AttrString, sizeof(ae->un.AttrString));
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ size = FOURBYTES + len;
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RHBA_OS_NAME_VERSION);
+ return size;
+}
+
+int
+lpfc_fdmi_hba_attr_ct_len(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+
+ ae->un.AttrInt = cpu_to_be32(LPFC_MAX_CT_SIZE);
+ size = FOURBYTES + sizeof(uint32_t);
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RHBA_MAX_CT_PAYLOAD_LEN);
+ return size;
+}
+
+int
+lpfc_fdmi_hba_attr_symbolic_name(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t len, size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 256);
+
+ len = lpfc_vport_symbolic_node_name(vport,
+ ae->un.AttrString, 256);
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ size = FOURBYTES + len;
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RHBA_SYM_NODENAME);
+ return size;
+}
+
+int
+lpfc_fdmi_hba_attr_vendor_info(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+
+ /* Nothing is defined for this currently */
+ ae->un.AttrInt = cpu_to_be32(0);
+ size = FOURBYTES + sizeof(uint32_t);
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RHBA_VENDOR_INFO);
+ return size;
}
+int
+lpfc_fdmi_hba_attr_num_ports(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+
+ /* Each driver instance corresponds to a single port */
+ ae->un.AttrInt = cpu_to_be32(1);
+ size = FOURBYTES + sizeof(uint32_t);
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RHBA_NUM_PORTS);
+ return size;
+}
+
+int
+lpfc_fdmi_hba_attr_fabric_wwnn(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, sizeof(struct lpfc_name));
+
+ memcpy(&ae->un.AttrWWN, &vport->fabric_nodename,
+ sizeof(struct lpfc_name));
+ size = FOURBYTES + sizeof(struct lpfc_name);
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RHBA_FABRIC_WWNN);
+ return size;
+}
int
-lpfc_fdmi_cmd(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, int cmdcode)
+lpfc_fdmi_hba_attr_bios_ver(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t len, size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 256);
+
+ lpfc_decode_firmware_rev(phba, ae->un.AttrString, 1);
+ len = strnlen(ae->un.AttrString,
+ sizeof(ae->un.AttrString));
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ size = FOURBYTES + len;
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RHBA_BIOS_VERSION);
+ return size;
+}
+
+int
+lpfc_fdmi_hba_attr_bios_state(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+
+ /* Driver doesn't have access to this information */
+ ae->un.AttrInt = cpu_to_be32(0);
+ size = FOURBYTES + sizeof(uint32_t);
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RHBA_BIOS_STATE);
+ return size;
+}
+
+int
+lpfc_fdmi_hba_attr_vendor_id(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t len, size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 256);
+
+ strncpy(ae->un.AttrString, "EMULEX",
+ sizeof(ae->un.AttrString));
+ len = strnlen(ae->un.AttrString,
+ sizeof(ae->un.AttrString));
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ size = FOURBYTES + len;
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RHBA_VENDOR_ID);
+ return size;
+}
+
+/* Routines for all individual PORT attributes */
+int
+lpfc_fdmi_port_attr_fc4type(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 32);
+
+ ae->un.AttrTypes[3] = 0x02; /* Type 1 - ELS */
+ ae->un.AttrTypes[2] = 0x01; /* Type 8 - FCP */
+ ae->un.AttrTypes[7] = 0x01; /* Type 32 - CT */
+ size = FOURBYTES + 32;
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_SUPPORTED_FC4_TYPES);
+ return size;
+}
+
+int
+lpfc_fdmi_port_attr_support_speed(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+
+ ae->un.AttrInt = 0;
+ if (!(phba->hba_flag & HBA_FCOE_MODE)) {
+ if (phba->lmt & LMT_32Gb)
+ ae->un.AttrInt |= HBA_PORTSPEED_32GFC;
+ if (phba->lmt & LMT_16Gb)
+ ae->un.AttrInt |= HBA_PORTSPEED_16GFC;
+ if (phba->lmt & LMT_10Gb)
+ ae->un.AttrInt |= HBA_PORTSPEED_10GFC;
+ if (phba->lmt & LMT_8Gb)
+ ae->un.AttrInt |= HBA_PORTSPEED_8GFC;
+ if (phba->lmt & LMT_4Gb)
+ ae->un.AttrInt |= HBA_PORTSPEED_4GFC;
+ if (phba->lmt & LMT_2Gb)
+ ae->un.AttrInt |= HBA_PORTSPEED_2GFC;
+ if (phba->lmt & LMT_1Gb)
+ ae->un.AttrInt |= HBA_PORTSPEED_1GFC;
+ } else {
+ /* FCoE links support only one speed */
+ switch (phba->fc_linkspeed) {
+ case LPFC_ASYNC_LINK_SPEED_10GBPS:
+ ae->un.AttrInt = HBA_PORTSPEED_10GE;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_25GBPS:
+ ae->un.AttrInt = HBA_PORTSPEED_25GE;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_40GBPS:
+ ae->un.AttrInt = HBA_PORTSPEED_40GE;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_100GBPS:
+ ae->un.AttrInt = HBA_PORTSPEED_100GE;
+ break;
+ }
+ }
+ ae->un.AttrInt = cpu_to_be32(ae->un.AttrInt);
+ size = FOURBYTES + sizeof(uint32_t);
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_SUPPORTED_SPEED);
+ return size;
+}
+
+int
+lpfc_fdmi_port_attr_speed(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+
+ if (!(phba->hba_flag & HBA_FCOE_MODE)) {
+ switch (phba->fc_linkspeed) {
+ case LPFC_LINK_SPEED_1GHZ:
+ ae->un.AttrInt = HBA_PORTSPEED_1GFC;
+ break;
+ case LPFC_LINK_SPEED_2GHZ:
+ ae->un.AttrInt = HBA_PORTSPEED_2GFC;
+ break;
+ case LPFC_LINK_SPEED_4GHZ:
+ ae->un.AttrInt = HBA_PORTSPEED_4GFC;
+ break;
+ case LPFC_LINK_SPEED_8GHZ:
+ ae->un.AttrInt = HBA_PORTSPEED_8GFC;
+ break;
+ case LPFC_LINK_SPEED_10GHZ:
+ ae->un.AttrInt = HBA_PORTSPEED_10GFC;
+ break;
+ case LPFC_LINK_SPEED_16GHZ:
+ ae->un.AttrInt = HBA_PORTSPEED_16GFC;
+ break;
+ case LPFC_LINK_SPEED_32GHZ:
+ ae->un.AttrInt = HBA_PORTSPEED_32GFC;
+ break;
+ default:
+ ae->un.AttrInt = HBA_PORTSPEED_UNKNOWN;
+ break;
+ }
+ } else {
+ switch (phba->fc_linkspeed) {
+ case LPFC_ASYNC_LINK_SPEED_10GBPS:
+ ae->un.AttrInt = HBA_PORTSPEED_10GE;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_25GBPS:
+ ae->un.AttrInt = HBA_PORTSPEED_25GE;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_40GBPS:
+ ae->un.AttrInt = HBA_PORTSPEED_40GE;
+ break;
+ case LPFC_ASYNC_LINK_SPEED_100GBPS:
+ ae->un.AttrInt = HBA_PORTSPEED_100GE;
+ break;
+ default:
+ ae->un.AttrInt = HBA_PORTSPEED_UNKNOWN;
+ break;
+ }
+ }
+
+ ae->un.AttrInt = cpu_to_be32(ae->un.AttrInt);
+ size = FOURBYTES + sizeof(uint32_t);
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_PORT_SPEED);
+ return size;
+}
+
+int
+lpfc_fdmi_port_attr_max_frame(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct serv_parm *hsp;
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+
+ hsp = (struct serv_parm *)&vport->fc_sparam;
+ ae->un.AttrInt = (((uint32_t) hsp->cmn.bbRcvSizeMsb) << 8) |
+ (uint32_t) hsp->cmn.bbRcvSizeLsb;
+ ae->un.AttrInt = cpu_to_be32(ae->un.AttrInt);
+ size = FOURBYTES + sizeof(uint32_t);
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_MAX_FRAME_SIZE);
+ return size;
+}
+
+int
+lpfc_fdmi_port_attr_os_devname(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t len, size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 256);
+
+ snprintf(ae->un.AttrString, sizeof(ae->un.AttrString),
+ "/sys/class/scsi_host/host%d", shost->host_no);
+ len = strnlen((char *)ae->un.AttrString,
+ sizeof(ae->un.AttrString));
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ size = FOURBYTES + len;
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_OS_DEVICE_NAME);
+ return size;
+}
+
+int
+lpfc_fdmi_port_attr_host_name(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t len, size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 256);
+
+ snprintf(ae->un.AttrString, sizeof(ae->un.AttrString), "%s",
+ init_utsname()->nodename);
+
+ len = strnlen(ae->un.AttrString, sizeof(ae->un.AttrString));
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ size = FOURBYTES + len;
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_HOST_NAME);
+ return size;
+}
+
+int
+lpfc_fdmi_port_attr_wwnn(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, sizeof(struct lpfc_name));
+
+ memcpy(&ae->un.AttrWWN, &vport->fc_sparam.nodeName,
+ sizeof(struct lpfc_name));
+ size = FOURBYTES + sizeof(struct lpfc_name);
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_NODENAME);
+ return size;
+}
+
+int
+lpfc_fdmi_port_attr_wwpn(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, sizeof(struct lpfc_name));
+
+ memcpy(&ae->un.AttrWWN, &vport->fc_sparam.portName,
+ sizeof(struct lpfc_name));
+ size = FOURBYTES + sizeof(struct lpfc_name);
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_PORTNAME);
+ return size;
+}
+
+int
+lpfc_fdmi_port_attr_symbolic_name(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t len, size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 256);
+
+ len = lpfc_vport_symbolic_port_name(vport, ae->un.AttrString, 256);
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ size = FOURBYTES + len;
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_SYM_PORTNAME);
+ return size;
+}
+
+int
+lpfc_fdmi_port_attr_port_type(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ if (phba->fc_topology == LPFC_TOPOLOGY_LOOP)
+ ae->un.AttrInt = cpu_to_be32(LPFC_FDMI_PORTTYPE_NLPORT);
+ else
+ ae->un.AttrInt = cpu_to_be32(LPFC_FDMI_PORTTYPE_NPORT);
+ size = FOURBYTES + sizeof(uint32_t);
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_PORT_TYPE);
+ return size;
+}
+
+int
+lpfc_fdmi_port_attr_class(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ ae->un.AttrInt = cpu_to_be32(FC_COS_CLASS2 | FC_COS_CLASS3);
+ size = FOURBYTES + sizeof(uint32_t);
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_SUPPORTED_CLASS);
+ return size;
+}
+
+int
+lpfc_fdmi_port_attr_fabric_wwpn(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, sizeof(struct lpfc_name));
+
+ memcpy(&ae->un.AttrWWN, &vport->fabric_portname,
+ sizeof(struct lpfc_name));
+ size = FOURBYTES + sizeof(struct lpfc_name);
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_FABRICNAME);
+ return size;
+}
+
+int
+lpfc_fdmi_port_attr_active_fc4type(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 32);
+
+ ae->un.AttrTypes[3] = 0x02; /* Type 1 - ELS */
+ ae->un.AttrTypes[2] = 0x01; /* Type 8 - FCP */
+ ae->un.AttrTypes[7] = 0x01; /* Type 32 - CT */
+ size = FOURBYTES + 32;
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_ACTIVE_FC4_TYPES);
+ return size;
+}
+
+int
+lpfc_fdmi_port_attr_port_state(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ /* Link Up - operational */
+ ae->un.AttrInt = cpu_to_be32(LPFC_FDMI_PORTSTATE_ONLINE);
+ size = FOURBYTES + sizeof(uint32_t);
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_PORT_STATE);
+ return size;
+}
+
+int
+lpfc_fdmi_port_attr_num_disc(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ vport->fdmi_num_disc = lpfc_find_map_node(vport);
+ ae->un.AttrInt = cpu_to_be32(vport->fdmi_num_disc);
+ size = FOURBYTES + sizeof(uint32_t);
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_DISC_PORT);
+ return size;
+}
+
+int
+lpfc_fdmi_port_attr_nportid(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ ae->un.AttrInt = cpu_to_be32(vport->fc_myDID);
+ size = FOURBYTES + sizeof(uint32_t);
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_PORT_ID);
+ return size;
+}
+
+int
+lpfc_fdmi_smart_attr_service(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t len, size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 256);
+
+ strncpy(ae->un.AttrString, "Smart SAN Initiator",
+ sizeof(ae->un.AttrString));
+ len = strnlen(ae->un.AttrString,
+ sizeof(ae->un.AttrString));
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ size = FOURBYTES + len;
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_SMART_SERVICE);
+ return size;
+}
+
+int
+lpfc_fdmi_smart_attr_guid(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 256);
+
+ memcpy(&ae->un.AttrString, &vport->fc_sparam.nodeName,
+ sizeof(struct lpfc_name));
+ memcpy((((uint8_t *)&ae->un.AttrString) +
+ sizeof(struct lpfc_name)),
+ &vport->fc_sparam.portName, sizeof(struct lpfc_name));
+ size = FOURBYTES + (2 * sizeof(struct lpfc_name));
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_SMART_GUID);
+ return size;
+}
+
+int
+lpfc_fdmi_smart_attr_version(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t len, size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 256);
+
+ strncpy(ae->un.AttrString, "Smart SAN Version 1.0",
+ sizeof(ae->un.AttrString));
+ len = strnlen(ae->un.AttrString,
+ sizeof(ae->un.AttrString));
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ size = FOURBYTES + len;
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_SMART_VERSION);
+ return size;
+}
+
+int
+lpfc_fdmi_smart_attr_model(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t len, size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ memset(ae, 0, 256);
+
+ strncpy(ae->un.AttrString, phba->ModelName,
+ sizeof(ae->un.AttrString));
+ len = strnlen(ae->un.AttrString, sizeof(ae->un.AttrString));
+ len += (len & 3) ? (4 - (len & 3)) : 4;
+ size = FOURBYTES + len;
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_SMART_MODEL);
+ return size;
+}
+
+int
+lpfc_fdmi_smart_attr_port_info(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+
+ /* SRIOV (type 3) is not supported */
+ if (vport->vpi)
+ ae->un.AttrInt = cpu_to_be32(2); /* NPIV */
+ else
+ ae->un.AttrInt = cpu_to_be32(1); /* Physical */
+ size = FOURBYTES + sizeof(uint32_t);
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_SMART_PORT_INFO);
+ return size;
+}
+
+int
+lpfc_fdmi_smart_attr_qos(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ ae->un.AttrInt = cpu_to_be32(0);
+ size = FOURBYTES + sizeof(uint32_t);
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_SMART_QOS);
+ return size;
+}
+
+int
+lpfc_fdmi_smart_attr_security(struct lpfc_vport *vport,
+ struct lpfc_fdmi_attr_def *ad)
+{
+ struct lpfc_fdmi_attr_entry *ae;
+ uint32_t size;
+
+ ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
+ ae->un.AttrInt = cpu_to_be32(0);
+ size = FOURBYTES + sizeof(uint32_t);
+ ad->AttrLen = cpu_to_be16(size);
+ ad->AttrType = cpu_to_be16(RPRT_SMART_SECURITY);
+ return size;
+}
+
+/* RHBA attribute jump table */
+int (*lpfc_fdmi_hba_action[])
+ (struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) = {
+ /* Action routine Mask bit Attribute type */
+ lpfc_fdmi_hba_attr_wwnn, /* bit0 RHBA_NODENAME */
+ lpfc_fdmi_hba_attr_manufacturer, /* bit1 RHBA_MANUFACTURER */
+ lpfc_fdmi_hba_attr_sn, /* bit2 RHBA_SERIAL_NUMBER */
+ lpfc_fdmi_hba_attr_model, /* bit3 RHBA_MODEL */
+ lpfc_fdmi_hba_attr_description, /* bit4 RHBA_MODEL_DESCRIPTION */
+ lpfc_fdmi_hba_attr_hdw_ver, /* bit5 RHBA_HARDWARE_VERSION */
+ lpfc_fdmi_hba_attr_drvr_ver, /* bit6 RHBA_DRIVER_VERSION */
+ lpfc_fdmi_hba_attr_rom_ver, /* bit7 RHBA_OPTION_ROM_VERSION */
+ lpfc_fdmi_hba_attr_fmw_ver, /* bit8 RHBA_FIRMWARE_VERSION */
+ lpfc_fdmi_hba_attr_os_ver, /* bit9 RHBA_OS_NAME_VERSION */
+ lpfc_fdmi_hba_attr_ct_len, /* bit10 RHBA_MAX_CT_PAYLOAD_LEN */
+ lpfc_fdmi_hba_attr_symbolic_name, /* bit11 RHBA_SYM_NODENAME */
+ lpfc_fdmi_hba_attr_vendor_info, /* bit12 RHBA_VENDOR_INFO */
+ lpfc_fdmi_hba_attr_num_ports, /* bit13 RHBA_NUM_PORTS */
+ lpfc_fdmi_hba_attr_fabric_wwnn, /* bit14 RHBA_FABRIC_WWNN */
+ lpfc_fdmi_hba_attr_bios_ver, /* bit15 RHBA_BIOS_VERSION */
+ lpfc_fdmi_hba_attr_bios_state, /* bit16 RHBA_BIOS_STATE */
+ lpfc_fdmi_hba_attr_vendor_id, /* bit17 RHBA_VENDOR_ID */
+};
+
+/* RPA / RPRT attribute jump table */
+int (*lpfc_fdmi_port_action[])
+ (struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad) = {
+ /* Action routine Mask bit Attribute type */
+ lpfc_fdmi_port_attr_fc4type, /* bit0 RPRT_SUPPORT_FC4_TYPES */
+ lpfc_fdmi_port_attr_support_speed, /* bit1 RPRT_SUPPORTED_SPEED */
+ lpfc_fdmi_port_attr_speed, /* bit2 RPRT_PORT_SPEED */
+ lpfc_fdmi_port_attr_max_frame, /* bit3 RPRT_MAX_FRAME_SIZE */
+ lpfc_fdmi_port_attr_os_devname, /* bit4 RPRT_OS_DEVICE_NAME */
+ lpfc_fdmi_port_attr_host_name, /* bit5 RPRT_HOST_NAME */
+ lpfc_fdmi_port_attr_wwnn, /* bit6 RPRT_NODENAME */
+ lpfc_fdmi_port_attr_wwpn, /* bit7 RPRT_PORTNAME */
+ lpfc_fdmi_port_attr_symbolic_name, /* bit8 RPRT_SYM_PORTNAME */
+ lpfc_fdmi_port_attr_port_type, /* bit9 RPRT_PORT_TYPE */
+ lpfc_fdmi_port_attr_class, /* bit10 RPRT_SUPPORTED_CLASS */
+ lpfc_fdmi_port_attr_fabric_wwpn, /* bit11 RPRT_FABRICNAME */
+ lpfc_fdmi_port_attr_active_fc4type, /* bit12 RPRT_ACTIVE_FC4_TYPES */
+ lpfc_fdmi_port_attr_port_state, /* bit13 RPRT_PORT_STATE */
+ lpfc_fdmi_port_attr_num_disc, /* bit14 RPRT_DISC_PORT */
+ lpfc_fdmi_port_attr_nportid, /* bit15 RPRT_PORT_ID */
+ lpfc_fdmi_smart_attr_service, /* bit16 RPRT_SMART_SERVICE */
+ lpfc_fdmi_smart_attr_guid, /* bit17 RPRT_SMART_GUID */
+ lpfc_fdmi_smart_attr_version, /* bit18 RPRT_SMART_VERSION */
+ lpfc_fdmi_smart_attr_model, /* bit19 RPRT_SMART_MODEL */
+ lpfc_fdmi_smart_attr_port_info, /* bit20 RPRT_SMART_PORT_INFO */
+ lpfc_fdmi_smart_attr_qos, /* bit21 RPRT_SMART_QOS */
+ lpfc_fdmi_smart_attr_security, /* bit22 RPRT_SMART_SECURITY */
+};
+
+/**
+ * lpfc_fdmi_cmd - Build and send a FDMI cmd to the specified NPort
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @ndlp: ndlp to send FDMI cmd to (if NULL use FDMI_DID)
+ * cmdcode: FDMI command to send
+ * mask: Mask of HBA or PORT Attributes to send
+ *
+ * Builds and sends a FDMI command using the CT subsystem.
+ */
+int
+lpfc_fdmi_cmd(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
+ int cmdcode, uint32_t new_mask)
{
struct lpfc_hba *phba = vport->phba;
struct lpfc_dmabuf *mp, *bmp;
struct lpfc_sli_ct_request *CtReq;
struct ulp_bde64 *bpl;
+ uint32_t bit_pos;
uint32_t size;
uint32_t rsp_size;
+ uint32_t mask;
struct lpfc_fdmi_reg_hba *rh;
struct lpfc_fdmi_port_entry *pe;
struct lpfc_fdmi_reg_portattr *pab = NULL;
struct lpfc_fdmi_attr_block *ab = NULL;
- struct lpfc_fdmi_attr_entry *ae;
- struct lpfc_fdmi_attr_def *ad;
- void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,
- struct lpfc_iocbq *);
+ int (*func)(struct lpfc_vport *vport, struct lpfc_fdmi_attr_def *ad);
+ void (*cmpl)(struct lpfc_hba *, struct lpfc_iocbq *,
+ struct lpfc_iocbq *);
- if (ndlp == NULL) {
- ndlp = lpfc_findnode_did(vport, FDMI_DID);
- if (!ndlp || !NLP_CHK_NODE_ACT(ndlp))
- return 0;
- cmpl = lpfc_cmpl_ct_cmd_fdmi; /* cmd interface */
- } else {
- cmpl = lpfc_cmpl_ct_disc_fdmi; /* called from discovery */
- }
+ if (!ndlp || !NLP_CHK_NODE_ACT(ndlp))
+ return 0;
+
+ cmpl = lpfc_cmpl_ct_disc_fdmi; /* called from discovery */
/* fill in BDEs for command */
/* Allocate buffer for command payload */
@@ -1470,573 +2535,99 @@ lpfc_fdmi_cmd(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, int cmdcode)
switch (cmdcode) {
case SLI_MGMT_RHAT:
case SLI_MGMT_RHBA:
- {
- lpfc_vpd_t *vp = &phba->vpd;
- uint32_t i, j, incr;
- int len = 0;
+ rh = (struct lpfc_fdmi_reg_hba *)&CtReq->un.PortID;
+ /* HBA Identifier */
+ memcpy(&rh->hi.PortName, &phba->pport->fc_sparam.portName,
+ sizeof(struct lpfc_name));
- rh = (struct lpfc_fdmi_reg_hba *)&CtReq->un.PortID;
- /* HBA Identifier */
- memcpy(&rh->hi.PortName, &vport->fc_sparam.portName,
+ if (cmdcode == SLI_MGMT_RHBA) {
+ /* Registered Port List */
+ /* One entry (port) per adapter */
+ rh->rpl.EntryCnt = cpu_to_be32(1);
+ memcpy(&rh->rpl.pe, &phba->pport->fc_sparam.portName,
sizeof(struct lpfc_name));
- if (cmdcode == SLI_MGMT_RHBA) {
- /* Registered Port List */
- /* One entry (port) per adapter */
- rh->rpl.EntryCnt = cpu_to_be32(1);
- memcpy(&rh->rpl.pe, &vport->fc_sparam.portName,
- sizeof(struct lpfc_name));
-
- /* point to the HBA attribute block */
- size = 2 * sizeof(struct lpfc_name) +
- FOURBYTES;
- } else {
- size = sizeof(struct lpfc_name);
- }
- ab = (struct lpfc_fdmi_attr_block *)
- ((uint8_t *)rh + size);
- ab->EntryCnt = 0;
- size += FOURBYTES;
-
- /*
- * Point to beginning of first HBA attribute entry
- */
- /* #1 HBA attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)rh + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- memset(ae, 0, sizeof(struct lpfc_name));
- ad->AttrType = cpu_to_be16(RHBA_NODENAME);
- ad->AttrLen = cpu_to_be16(FOURBYTES
- + sizeof(struct lpfc_name));
- memcpy(&ae->un.NodeName, &vport->fc_sparam.nodeName,
- sizeof(struct lpfc_name));
- ab->EntryCnt++;
- size += FOURBYTES + sizeof(struct lpfc_name);
- if ((size + LPFC_FDMI_MAX_AE_SIZE) >
- (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto hba_out;
-
- /* #2 HBA attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)rh + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- memset(ae, 0, sizeof(ae->un.Manufacturer));
- ad->AttrType = cpu_to_be16(RHBA_MANUFACTURER);
- strncpy(ae->un.Manufacturer, "Emulex Corporation",
- sizeof(ae->un.Manufacturer));
- len = strnlen(ae->un.Manufacturer,
- sizeof(ae->un.Manufacturer));
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ad->AttrLen = cpu_to_be16(FOURBYTES + len);
- ab->EntryCnt++;
- size += FOURBYTES + len;
- if ((size + LPFC_FDMI_MAX_AE_SIZE) >
- (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto hba_out;
-
- /* #3 HBA attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)rh + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- memset(ae, 0, sizeof(ae->un.SerialNumber));
- ad->AttrType = cpu_to_be16(RHBA_SERIAL_NUMBER);
- strncpy(ae->un.SerialNumber, phba->SerialNumber,
- sizeof(ae->un.SerialNumber));
- len = strnlen(ae->un.SerialNumber,
- sizeof(ae->un.SerialNumber));
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ad->AttrLen = cpu_to_be16(FOURBYTES + len);
- ab->EntryCnt++;
- size += FOURBYTES + len;
- if ((size + LPFC_FDMI_MAX_AE_SIZE) >
- (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto hba_out;
-
- /* #4 HBA attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)rh + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- memset(ae, 0, sizeof(ae->un.Model));
- ad->AttrType = cpu_to_be16(RHBA_MODEL);
- strncpy(ae->un.Model, phba->ModelName,
- sizeof(ae->un.Model));
- len = strnlen(ae->un.Model, sizeof(ae->un.Model));
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ad->AttrLen = cpu_to_be16(FOURBYTES + len);
- ab->EntryCnt++;
- size += FOURBYTES + len;
- if ((size + LPFC_FDMI_MAX_AE_SIZE) >
- (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto hba_out;
-
- /* #5 HBA attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)rh + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- memset(ae, 0, sizeof(ae->un.ModelDescription));
- ad->AttrType = cpu_to_be16(RHBA_MODEL_DESCRIPTION);
- strncpy(ae->un.ModelDescription, phba->ModelDesc,
- sizeof(ae->un.ModelDescription));
- len = strnlen(ae->un.ModelDescription,
- sizeof(ae->un.ModelDescription));
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ad->AttrLen = cpu_to_be16(FOURBYTES + len);
- ab->EntryCnt++;
- size += FOURBYTES + len;
- if ((size + 8) > (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto hba_out;
-
- /* #6 HBA attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)rh + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- memset(ae, 0, 8);
- ad->AttrType = cpu_to_be16(RHBA_HARDWARE_VERSION);
- ad->AttrLen = cpu_to_be16(FOURBYTES + 8);
- /* Convert JEDEC ID to ascii for hardware version */
- incr = vp->rev.biuRev;
- for (i = 0; i < 8; i++) {
- j = (incr & 0xf);
- if (j <= 9)
- ae->un.HardwareVersion[7 - i] =
- (char)((uint8_t)0x30 +
- (uint8_t)j);
- else
- ae->un.HardwareVersion[7 - i] =
- (char)((uint8_t)0x61 +
- (uint8_t)(j - 10));
- incr = (incr >> 4);
+ /* point to the HBA attribute block */
+ size = 2 * sizeof(struct lpfc_name) +
+ FOURBYTES;
+ } else {
+ size = sizeof(struct lpfc_name);
+ }
+ ab = (struct lpfc_fdmi_attr_block *)((uint8_t *)rh + size);
+ ab->EntryCnt = 0;
+ size += FOURBYTES;
+ bit_pos = 0;
+ if (new_mask)
+ mask = new_mask;
+ else
+ mask = vport->fdmi_hba_mask;
+
+ /* Mask will dictate what attributes to build in the request */
+ while (mask) {
+ if (mask & 0x1) {
+ func = lpfc_fdmi_hba_action[bit_pos];
+ size += func(vport,
+ (struct lpfc_fdmi_attr_def *)
+ ((uint8_t *)rh + size));
+ ab->EntryCnt++;
+ if ((size + 256) >
+ (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
+ goto hba_out;
}
- ab->EntryCnt++;
- size += FOURBYTES + 8;
- if ((size + LPFC_FDMI_MAX_AE_SIZE) >
- (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto hba_out;
-
- /* #7 HBA attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)rh + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- memset(ae, 0, sizeof(ae->un.DriverVersion));
- ad->AttrType = cpu_to_be16(RHBA_DRIVER_VERSION);
- strncpy(ae->un.DriverVersion, lpfc_release_version,
- sizeof(ae->un.DriverVersion));
- len = strnlen(ae->un.DriverVersion,
- sizeof(ae->un.DriverVersion));
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ad->AttrLen = cpu_to_be16(FOURBYTES + len);
- ab->EntryCnt++;
- size += FOURBYTES + len;
- if ((size + LPFC_FDMI_MAX_AE_SIZE) >
- (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto hba_out;
-
- /* #8 HBA attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)rh + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- memset(ae, 0, sizeof(ae->un.OptionROMVersion));
- ad->AttrType = cpu_to_be16(RHBA_OPTION_ROM_VERSION);
- strncpy(ae->un.OptionROMVersion, phba->OptionROMVersion,
- sizeof(ae->un.OptionROMVersion));
- len = strnlen(ae->un.OptionROMVersion,
- sizeof(ae->un.OptionROMVersion));
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ad->AttrLen = cpu_to_be16(FOURBYTES + len);
- ab->EntryCnt++;
- size += FOURBYTES + len;
- if ((size + LPFC_FDMI_MAX_AE_SIZE) >
- (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto hba_out;
-
- /* #9 HBA attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)rh + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- memset(ae, 0, sizeof(ae->un.FirmwareVersion));
- ad->AttrType = cpu_to_be16(RHBA_FIRMWARE_VERSION);
- lpfc_decode_firmware_rev(phba, ae->un.FirmwareVersion,
- 1);
- len = strnlen(ae->un.FirmwareVersion,
- sizeof(ae->un.FirmwareVersion));
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ad->AttrLen = cpu_to_be16(FOURBYTES + len);
- ab->EntryCnt++;
- size += FOURBYTES + len;
- if ((size + LPFC_FDMI_MAX_AE_SIZE) >
- (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto hba_out;
-
- /* #10 HBA attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)rh + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- memset(ae, 0, sizeof(ae->un.OsNameVersion));
- ad->AttrType = cpu_to_be16(RHBA_OS_NAME_VERSION);
- snprintf(ae->un.OsNameVersion,
- sizeof(ae->un.OsNameVersion),
- "%s %s %s",
- init_utsname()->sysname,
- init_utsname()->release,
- init_utsname()->version);
- len = strnlen(ae->un.OsNameVersion,
- sizeof(ae->un.OsNameVersion));
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ad->AttrLen = cpu_to_be16(FOURBYTES + len);
- ab->EntryCnt++;
- size += FOURBYTES + len;
- if ((size + 4) > (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto hba_out;
-
- /* #11 HBA attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)rh + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- ad->AttrType =
- cpu_to_be16(RHBA_MAX_CT_PAYLOAD_LEN);
- ad->AttrLen = cpu_to_be16(FOURBYTES + 4);
- ae->un.MaxCTPayloadLen = cpu_to_be32(LPFC_MAX_CT_SIZE);
- ab->EntryCnt++;
- size += FOURBYTES + 4;
- if ((size + LPFC_FDMI_MAX_AE_SIZE) >
- (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto hba_out;
-
- /*
- * Currently switches don't seem to support the
- * following extended HBA attributes.
- */
- if (!(vport->cfg_fdmi_on & LPFC_FDMI_ALL_ATTRIB))
- goto hba_out;
-
- /* #12 HBA attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)rh + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- memset(ae, 0, sizeof(ae->un.NodeSymName));
- ad->AttrType = cpu_to_be16(RHBA_SYM_NODENAME);
- len = lpfc_vport_symbolic_node_name(vport,
- ae->un.NodeSymName, sizeof(ae->un.NodeSymName));
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ad->AttrLen = cpu_to_be16(FOURBYTES + len);
- ab->EntryCnt++;
- size += FOURBYTES + len;
-hba_out:
- ab->EntryCnt = cpu_to_be32(ab->EntryCnt);
- /* Total size */
- size = GID_REQUEST_SZ - 4 + size;
+ mask = mask >> 1;
+ bit_pos++;
}
+hba_out:
+ ab->EntryCnt = cpu_to_be32(ab->EntryCnt);
+ /* Total size */
+ size = GID_REQUEST_SZ - 4 + size;
break;
case SLI_MGMT_RPRT:
case SLI_MGMT_RPA:
- {
- struct serv_parm *hsp;
- int len = 0;
-
- if (cmdcode == SLI_MGMT_RPRT) {
- rh = (struct lpfc_fdmi_reg_hba *)
- &CtReq->un.PortID;
- /* HBA Identifier */
- memcpy(&rh->hi.PortName,
- &vport->fc_sparam.portName,
- sizeof(struct lpfc_name));
- pab = (struct lpfc_fdmi_reg_portattr *)
- &rh->rpl.EntryCnt;
- } else
- pab = (struct lpfc_fdmi_reg_portattr *)
- &CtReq->un.PortID;
- size = sizeof(struct lpfc_name) + FOURBYTES;
- memcpy((uint8_t *)&pab->PortName,
- (uint8_t *)&vport->fc_sparam.portName,
+ pab = (struct lpfc_fdmi_reg_portattr *)&CtReq->un.PortID;
+ if (cmdcode == SLI_MGMT_RPRT) {
+ rh = (struct lpfc_fdmi_reg_hba *)pab;
+ /* HBA Identifier */
+ memcpy(&rh->hi.PortName,
+ &phba->pport->fc_sparam.portName,
sizeof(struct lpfc_name));
- pab->ab.EntryCnt = 0;
-
- /* #1 Port attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)pab + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- memset(ae, 0, sizeof(ae->un.FC4Types));
- ad->AttrType =
- cpu_to_be16(RPRT_SUPPORTED_FC4_TYPES);
- ad->AttrLen = cpu_to_be16(FOURBYTES + 32);
- ae->un.FC4Types[0] = 0x40; /* Type 1 - ELS */
- ae->un.FC4Types[1] = 0x80; /* Type 8 - FCP */
- ae->un.FC4Types[4] = 0x80; /* Type 32 - CT */
- pab->ab.EntryCnt++;
- size += FOURBYTES + 32;
-
- /* #2 Port attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)pab + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- ad->AttrType = cpu_to_be16(RPRT_SUPPORTED_SPEED);
- ad->AttrLen = cpu_to_be16(FOURBYTES + 4);
- ae->un.SupportSpeed = 0;
- if (phba->lmt & LMT_32Gb)
- ae->un.SupportSpeed |= HBA_PORTSPEED_32GBIT;
- if (phba->lmt & LMT_16Gb)
- ae->un.SupportSpeed |= HBA_PORTSPEED_16GBIT;
- if (phba->lmt & LMT_10Gb)
- ae->un.SupportSpeed |= HBA_PORTSPEED_10GBIT;
- if (phba->lmt & LMT_8Gb)
- ae->un.SupportSpeed |= HBA_PORTSPEED_8GBIT;
- if (phba->lmt & LMT_4Gb)
- ae->un.SupportSpeed |= HBA_PORTSPEED_4GBIT;
- if (phba->lmt & LMT_2Gb)
- ae->un.SupportSpeed |= HBA_PORTSPEED_2GBIT;
- if (phba->lmt & LMT_1Gb)
- ae->un.SupportSpeed |= HBA_PORTSPEED_1GBIT;
- ae->un.SupportSpeed =
- cpu_to_be32(ae->un.SupportSpeed);
-
- pab->ab.EntryCnt++;
- size += FOURBYTES + 4;
-
- /* #3 Port attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)pab + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- ad->AttrType = cpu_to_be16(RPRT_PORT_SPEED);
- ad->AttrLen = cpu_to_be16(FOURBYTES + 4);
- switch (phba->fc_linkspeed) {
- case LPFC_LINK_SPEED_1GHZ:
- ae->un.PortSpeed = HBA_PORTSPEED_1GBIT;
- break;
- case LPFC_LINK_SPEED_2GHZ:
- ae->un.PortSpeed = HBA_PORTSPEED_2GBIT;
- break;
- case LPFC_LINK_SPEED_4GHZ:
- ae->un.PortSpeed = HBA_PORTSPEED_4GBIT;
- break;
- case LPFC_LINK_SPEED_8GHZ:
- ae->un.PortSpeed = HBA_PORTSPEED_8GBIT;
- break;
- case LPFC_LINK_SPEED_10GHZ:
- ae->un.PortSpeed = HBA_PORTSPEED_10GBIT;
- break;
- case LPFC_LINK_SPEED_16GHZ:
- ae->un.PortSpeed = HBA_PORTSPEED_16GBIT;
- break;
- case LPFC_LINK_SPEED_32GHZ:
- ae->un.PortSpeed = HBA_PORTSPEED_32GBIT;
- break;
- default:
- ae->un.PortSpeed = HBA_PORTSPEED_UNKNOWN;
- break;
- }
- ae->un.PortSpeed = cpu_to_be32(ae->un.PortSpeed);
- pab->ab.EntryCnt++;
- size += FOURBYTES + 4;
-
- /* #4 Port attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)pab + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- ad->AttrType = cpu_to_be16(RPRT_MAX_FRAME_SIZE);
- ad->AttrLen = cpu_to_be16(FOURBYTES + 4);
- hsp = (struct serv_parm *)&vport->fc_sparam;
- ae->un.MaxFrameSize =
- (((uint32_t)hsp->cmn.
- bbRcvSizeMsb) << 8) | (uint32_t)hsp->cmn.
- bbRcvSizeLsb;
- ae->un.MaxFrameSize =
- cpu_to_be32(ae->un.MaxFrameSize);
- pab->ab.EntryCnt++;
- size += FOURBYTES + 4;
- if ((size + LPFC_FDMI_MAX_AE_SIZE) >
- (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto port_out;
-
- /* #5 Port attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)pab + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- memset(ae, 0, sizeof(ae->un.OsDeviceName));
- ad->AttrType = cpu_to_be16(RPRT_OS_DEVICE_NAME);
- strncpy((char *)ae->un.OsDeviceName, LPFC_DRIVER_NAME,
- sizeof(ae->un.OsDeviceName));
- len = strnlen((char *)ae->un.OsDeviceName,
- sizeof(ae->un.OsDeviceName));
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ad->AttrLen = cpu_to_be16(FOURBYTES + len);
- pab->ab.EntryCnt++;
- size += FOURBYTES + len;
- if ((size + LPFC_FDMI_MAX_AE_SIZE) >
- (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto port_out;
-
- /* #6 Port attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)pab + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- memset(ae, 0, sizeof(ae->un.HostName));
- snprintf(ae->un.HostName, sizeof(ae->un.HostName), "%s",
- init_utsname()->nodename);
- ad->AttrType = cpu_to_be16(RPRT_HOST_NAME);
- len = strnlen(ae->un.HostName,
- sizeof(ae->un.HostName));
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ad->AttrLen =
- cpu_to_be16(FOURBYTES + len);
- pab->ab.EntryCnt++;
- size += FOURBYTES + len;
- if ((size + sizeof(struct lpfc_name)) >
- (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto port_out;
+ pab = (struct lpfc_fdmi_reg_portattr *)
+ ((uint8_t *)pab + sizeof(struct lpfc_name));
+ }
- /*
- * Currently switches don't seem to support the
- * following extended Port attributes.
- */
- if (!(vport->cfg_fdmi_on & LPFC_FDMI_ALL_ATTRIB))
- goto port_out;
-
- /* #7 Port attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)pab + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- memset(ae, 0, sizeof(struct lpfc_name));
- ad->AttrType = cpu_to_be16(RPRT_NODENAME);
- ad->AttrLen = cpu_to_be16(FOURBYTES
- + sizeof(struct lpfc_name));
- memcpy(&ae->un.NodeName, &vport->fc_sparam.nodeName,
- sizeof(struct lpfc_name));
- pab->ab.EntryCnt++;
- size += FOURBYTES + sizeof(struct lpfc_name);
- if ((size + sizeof(struct lpfc_name)) >
- (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto port_out;
-
- /* #8 Port attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)pab + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- memset(ae, 0, sizeof(struct lpfc_name));
- ad->AttrType = cpu_to_be16(RPRT_PORTNAME);
- ad->AttrLen = cpu_to_be16(FOURBYTES
- + sizeof(struct lpfc_name));
- memcpy(&ae->un.PortName, &vport->fc_sparam.portName,
- sizeof(struct lpfc_name));
- pab->ab.EntryCnt++;
- size += FOURBYTES + sizeof(struct lpfc_name);
- if ((size + LPFC_FDMI_MAX_AE_SIZE) >
- (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto port_out;
-
- /* #9 Port attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)pab + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- memset(ae, 0, sizeof(ae->un.NodeSymName));
- ad->AttrType = cpu_to_be16(RPRT_SYM_PORTNAME);
- len = lpfc_vport_symbolic_port_name(vport,
- ae->un.NodeSymName, sizeof(ae->un.NodeSymName));
- len += (len & 3) ? (4 - (len & 3)) : 4;
- ad->AttrLen = cpu_to_be16(FOURBYTES + len);
- pab->ab.EntryCnt++;
- size += FOURBYTES + len;
- if ((size + 4) > (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto port_out;
-
- /* #10 Port attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)pab + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- ad->AttrType = cpu_to_be16(RPRT_PORT_TYPE);
- ae->un.PortState = 0;
- ad->AttrLen = cpu_to_be16(FOURBYTES + 4);
- pab->ab.EntryCnt++;
- size += FOURBYTES + 4;
- if ((size + 4) > (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto port_out;
-
- /* #11 Port attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)pab + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- ad->AttrType = cpu_to_be16(RPRT_SUPPORTED_CLASS);
- ae->un.SupportClass =
- cpu_to_be32(FC_COS_CLASS2 | FC_COS_CLASS3);
- ad->AttrLen = cpu_to_be16(FOURBYTES + 4);
- pab->ab.EntryCnt++;
- size += FOURBYTES + 4;
- if ((size + sizeof(struct lpfc_name)) >
- (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto port_out;
-
- /* #12 Port attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)pab + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- memset(ae, 0, sizeof(struct lpfc_name));
- ad->AttrType = cpu_to_be16(RPRT_FABRICNAME);
- ad->AttrLen = cpu_to_be16(FOURBYTES
- + sizeof(struct lpfc_name));
- memcpy(&ae->un.FabricName, &vport->fabric_nodename,
- sizeof(struct lpfc_name));
- pab->ab.EntryCnt++;
- size += FOURBYTES + sizeof(struct lpfc_name);
- if ((size + LPFC_FDMI_MAX_AE_SIZE) >
- (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto port_out;
-
- /* #13 Port attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)pab + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- memset(ae, 0, sizeof(ae->un.FC4Types));
- ad->AttrType =
- cpu_to_be16(RPRT_ACTIVE_FC4_TYPES);
- ad->AttrLen = cpu_to_be16(FOURBYTES + 32);
- ae->un.FC4Types[0] = 0x40; /* Type 1 - ELS */
- ae->un.FC4Types[1] = 0x80; /* Type 8 - FCP */
- ae->un.FC4Types[4] = 0x80; /* Type 32 - CT */
- pab->ab.EntryCnt++;
- size += FOURBYTES + 32;
- if ((size + 4) > (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto port_out;
-
- /* #257 Port attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)pab + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- ad->AttrType = cpu_to_be16(RPRT_PORT_STATE);
- ae->un.PortState = 0;
- ad->AttrLen = cpu_to_be16(FOURBYTES + 4);
- pab->ab.EntryCnt++;
- size += FOURBYTES + 4;
- if ((size + 4) > (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto port_out;
-
- /* #258 Port attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)pab + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- ad->AttrType = cpu_to_be16(RPRT_DISC_PORT);
- ae->un.PortState = lpfc_find_map_node(vport);
- ae->un.PortState = cpu_to_be32(ae->un.PortState);
- ad->AttrLen = cpu_to_be16(FOURBYTES + 4);
- pab->ab.EntryCnt++;
- size += FOURBYTES + 4;
- if ((size + 4) > (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
- goto port_out;
-
- /* #259 Port attribute entry */
- ad = (struct lpfc_fdmi_attr_def *)
- ((uint8_t *)pab + size);
- ae = (struct lpfc_fdmi_attr_entry *)&ad->AttrValue;
- ad->AttrType = cpu_to_be16(RPRT_PORT_ID);
- ae->un.PortId = cpu_to_be32(vport->fc_myDID);
- ad->AttrLen = cpu_to_be16(FOURBYTES + 4);
- pab->ab.EntryCnt++;
- size += FOURBYTES + 4;
-port_out:
- pab->ab.EntryCnt = cpu_to_be32(pab->ab.EntryCnt);
- /* Total size */
- size = GID_REQUEST_SZ - 4 + size;
+ memcpy((uint8_t *)&pab->PortName,
+ (uint8_t *)&vport->fc_sparam.portName,
+ sizeof(struct lpfc_name));
+ size += sizeof(struct lpfc_name) + FOURBYTES;
+ pab->ab.EntryCnt = 0;
+ bit_pos = 0;
+ if (new_mask)
+ mask = new_mask;
+ else
+ mask = vport->fdmi_port_mask;
+
+ /* Mask will dictate what attributes to build in the request */
+ while (mask) {
+ if (mask & 0x1) {
+ func = lpfc_fdmi_port_action[bit_pos];
+ size += func(vport,
+ (struct lpfc_fdmi_attr_def *)
+ ((uint8_t *)pab + size));
+ pab->ab.EntryCnt++;
+ if ((size + 256) >
+ (LPFC_BPL_SIZE - LPFC_CT_PREAMBLE))
+ goto port_out;
+ }
+ mask = mask >> 1;
+ bit_pos++;
}
+port_out:
+ pab->ab.EntryCnt = cpu_to_be32(pab->ab.EntryCnt);
+ /* Total size */
+ if (cmdcode == SLI_MGMT_RPRT)
+ size += sizeof(struct lpfc_name);
+ size = GID_REQUEST_SZ - 4 + size;
break;
case SLI_MGMT_GHAT:
@@ -2158,41 +2749,6 @@ lpfc_delayed_disc_timeout_handler(struct lpfc_vport *vport)
}
void
-lpfc_fdmi_tmo(unsigned long ptr)
-{
- struct lpfc_vport *vport = (struct lpfc_vport *)ptr;
- struct lpfc_hba *phba = vport->phba;
- uint32_t tmo_posted;
- unsigned long iflag;
-
- spin_lock_irqsave(&vport->work_port_lock, iflag);
- tmo_posted = vport->work_port_events & WORKER_FDMI_TMO;
- if (!tmo_posted)
- vport->work_port_events |= WORKER_FDMI_TMO;
- spin_unlock_irqrestore(&vport->work_port_lock, iflag);
-
- if (!tmo_posted)
- lpfc_worker_wake_up(phba);
- return;
-}
-
-void
-lpfc_fdmi_timeout_handler(struct lpfc_vport *vport)
-{
- struct lpfc_nodelist *ndlp;
-
- ndlp = lpfc_findnode_did(vport, FDMI_DID);
- if (ndlp && NLP_CHK_NODE_ACT(ndlp)) {
- if (init_utsname()->nodename[0] != '\0')
- lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DHBA);
- else
- mod_timer(&vport->fc_fdmitmo, jiffies +
- msecs_to_jiffies(1000 * 60));
- }
- return;
-}
-
-void
lpfc_decode_firmware_rev(struct lpfc_hba *phba, char *fwrevision, int flag)
{
struct lpfc_sli *psli = &phba->sli;
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index b6fa257ea3e0..7f5abb8f52bc 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -455,9 +455,9 @@ int
lpfc_issue_reg_vfi(struct lpfc_vport *vport)
{
struct lpfc_hba *phba = vport->phba;
- LPFC_MBOXQ_t *mboxq;
+ LPFC_MBOXQ_t *mboxq = NULL;
struct lpfc_nodelist *ndlp;
- struct lpfc_dmabuf *dmabuf;
+ struct lpfc_dmabuf *dmabuf = NULL;
int rc = 0;
/* move forward in case of SLI4 FC port loopback test and pt2pt mode */
@@ -471,25 +471,33 @@ lpfc_issue_reg_vfi(struct lpfc_vport *vport)
}
}
- dmabuf = kzalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
- if (!dmabuf) {
+ mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq) {
rc = -ENOMEM;
goto fail;
}
- dmabuf->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &dmabuf->phys);
- if (!dmabuf->virt) {
- rc = -ENOMEM;
- goto fail_free_dmabuf;
- }
- mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
- if (!mboxq) {
- rc = -ENOMEM;
- goto fail_free_coherent;
+ /* Supply CSP's only if we are fabric connect or pt-to-pt connect */
+ if ((vport->fc_flag & FC_FABRIC) || (vport->fc_flag & FC_PT2PT)) {
+ dmabuf = kzalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (!dmabuf) {
+ rc = -ENOMEM;
+ goto fail;
+ }
+ dmabuf->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &dmabuf->phys);
+ if (!dmabuf->virt) {
+ rc = -ENOMEM;
+ goto fail;
+ }
+ memcpy(dmabuf->virt, &phba->fc_fabparam,
+ sizeof(struct serv_parm));
}
+
vport->port_state = LPFC_FABRIC_CFG_LINK;
- memcpy(dmabuf->virt, &phba->fc_fabparam, sizeof(vport->fc_sparam));
- lpfc_reg_vfi(mboxq, vport, dmabuf->phys);
+ if (dmabuf)
+ lpfc_reg_vfi(mboxq, vport, dmabuf->phys);
+ else
+ lpfc_reg_vfi(mboxq, vport, 0);
mboxq->mbox_cmpl = lpfc_mbx_cmpl_reg_vfi;
mboxq->vport = vport;
@@ -497,17 +505,19 @@ lpfc_issue_reg_vfi(struct lpfc_vport *vport)
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED) {
rc = -ENXIO;
- goto fail_free_mbox;
+ goto fail;
}
return 0;
-fail_free_mbox:
- mempool_free(mboxq, phba->mbox_mem_pool);
-fail_free_coherent:
- lpfc_mbuf_free(phba, dmabuf->virt, dmabuf->phys);
-fail_free_dmabuf:
- kfree(dmabuf);
fail:
+ if (mboxq)
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ if (dmabuf) {
+ if (dmabuf->virt)
+ lpfc_mbuf_free(phba, dmabuf->virt, dmabuf->phys);
+ kfree(dmabuf);
+ }
+
lpfc_vport_set_state(vport, FC_VPORT_FAILED);
lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
"0289 Issue Register VFI failed: Err %d\n", rc);
@@ -678,6 +688,21 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
sp->cmn.bbRcvSizeLsb;
fabric_param_changed = lpfc_check_clean_addr_bit(vport, sp);
+ if (fabric_param_changed) {
+ /* Reset FDMI attribute masks based on config parameter */
+ if (phba->cfg_fdmi_on == LPFC_FDMI_NO_SUPPORT) {
+ vport->fdmi_hba_mask = 0;
+ vport->fdmi_port_mask = 0;
+ } else {
+ /* Setup appropriate attribute masks */
+ vport->fdmi_hba_mask = LPFC_FDMI2_HBA_ATTR;
+ if (phba->cfg_fdmi_on == LPFC_FDMI_SMART_SAN)
+ vport->fdmi_port_mask = LPFC_FDMI2_SMART_ATTR;
+ else
+ vport->fdmi_port_mask = LPFC_FDMI2_PORT_ATTR;
+ }
+
+ }
memcpy(&vport->fabric_portname, &sp->portName,
sizeof(struct lpfc_name));
memcpy(&vport->fabric_nodename, &sp->nodeName,
@@ -711,9 +736,10 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
* For FC we need to do some special processing because of the SLI
* Port's default settings of the Common Service Parameters.
*/
- if (phba->sli4_hba.lnk_info.lnk_tp == LPFC_LNK_TYPE_FC) {
+ if ((phba->sli_rev == LPFC_SLI_REV4) &&
+ (phba->sli4_hba.lnk_info.lnk_tp == LPFC_LNK_TYPE_FC)) {
/* If physical FC port changed, unreg VFI and ALL VPIs / RPIs */
- if ((phba->sli_rev == LPFC_SLI_REV4) && fabric_param_changed)
+ if (fabric_param_changed)
lpfc_unregister_fcf_prep(phba);
/* This should just update the VFI CSPs*/
@@ -824,13 +850,21 @@ lpfc_cmpl_els_flogi_nport(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
spin_lock_irq(shost->host_lock);
vport->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP);
+ vport->fc_flag |= FC_PT2PT;
spin_unlock_irq(shost->host_lock);
- phba->fc_edtov = FF_DEF_EDTOV;
- phba->fc_ratov = FF_DEF_RATOV;
+ /* If physical FC port changed, unreg VFI and ALL VPIs / RPIs */
+ if ((phba->sli_rev == LPFC_SLI_REV4) && phba->fc_topology_changed) {
+ lpfc_unregister_fcf_prep(phba);
+
+ spin_lock_irq(shost->host_lock);
+ vport->fc_flag &= ~FC_VFI_REGISTERED;
+ spin_unlock_irq(shost->host_lock);
+ phba->fc_topology_changed = 0;
+ }
+
rc = memcmp(&vport->fc_portname, &sp->portName,
sizeof(vport->fc_portname));
- memcpy(&phba->fc_fabparam, sp, sizeof(struct serv_parm));
if (rc >= 0) {
/* This side will initiate the PLOGI */
@@ -839,38 +873,14 @@ lpfc_cmpl_els_flogi_nport(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
spin_unlock_irq(shost->host_lock);
/*
- * N_Port ID cannot be 0, set our to LocalID the other
- * side will be RemoteID.
+ * N_Port ID cannot be 0, set our Id to LocalID
+ * the other side will be RemoteID.
*/
/* not equal */
if (rc)
vport->fc_myDID = PT2PT_LocalID;
- mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
- if (!mbox)
- goto fail;
-
- lpfc_config_link(phba, mbox);
-
- mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
- mbox->vport = vport;
- rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
- if (rc == MBX_NOT_FINISHED) {
- mempool_free(mbox, phba->mbox_mem_pool);
- goto fail;
- }
-
- /*
- * For SLI4, the VFI/VPI are registered AFTER the
- * Nport with the higher WWPN sends the PLOGI with
- * an assigned NPortId.
- */
-
- /* not equal */
- if ((phba->sli_rev == LPFC_SLI_REV4) && rc)
- lpfc_issue_reg_vfi(vport);
-
/* Decrement ndlp reference count indicating that ndlp can be
* safely released when other references to it are done.
*/
@@ -912,29 +922,20 @@ lpfc_cmpl_els_flogi_nport(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
/* If we are pt2pt with another NPort, force NPIV off! */
phba->sli3_options &= ~LPFC_SLI3_NPIV_ENABLED;
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_PT2PT;
- spin_unlock_irq(shost->host_lock);
- /* If physical FC port changed, unreg VFI and ALL VPIs / RPIs */
- if ((phba->sli_rev == LPFC_SLI_REV4) && phba->fc_topology_changed) {
- lpfc_unregister_fcf_prep(phba);
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox)
+ goto fail;
- /* The FC_VFI_REGISTERED flag will get clear in the cmpl
- * handler for unreg_vfi, but if we don't force the
- * FC_VFI_REGISTERED flag then the reg_vfi mailbox could be
- * built with the update bit set instead of just the vp bit to
- * change the Nport ID. We need to have the vp set and the
- * Upd cleared on topology changes.
- */
- spin_lock_irq(shost->host_lock);
- vport->fc_flag &= ~FC_VFI_REGISTERED;
- spin_unlock_irq(shost->host_lock);
- phba->fc_topology_changed = 0;
- lpfc_issue_reg_vfi(vport);
+ lpfc_config_link(phba, mbox);
+
+ mbox->mbox_cmpl = lpfc_mbx_cmpl_local_config_link;
+ mbox->vport = vport;
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ mempool_free(mbox, phba->mbox_mem_pool);
+ goto fail;
}
- /* Start discovery - this should just do CLEAR_LA */
- lpfc_disc_start(vport);
return 0;
fail:
return -ENXIO;
@@ -1157,6 +1158,7 @@ flogifail:
spin_lock_irq(&phba->hbalock);
phba->fcf.fcf_flag &= ~FCF_DISCOVERY;
spin_unlock_irq(&phba->hbalock);
+
lpfc_nlp_put(ndlp);
if (!lpfc_error_lost_link(irsp)) {
@@ -3792,14 +3794,17 @@ lpfc_cmpl_els_rsp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
lpfc_nlp_set_state(vport, ndlp,
NLP_STE_REG_LOGIN_ISSUE);
}
+
+ ndlp->nlp_flag |= NLP_REG_LOGIN_SEND;
if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT)
!= MBX_NOT_FINISHED)
goto out;
- else
- /* Decrement the ndlp reference count we
- * set for this failed mailbox command.
- */
- lpfc_nlp_put(ndlp);
+
+ /* Decrement the ndlp reference count we
+ * set for this failed mailbox command.
+ */
+ lpfc_nlp_put(ndlp);
+ ndlp->nlp_flag &= ~NLP_REG_LOGIN_SEND;
/* ELS rsp: Cannot issue reg_login for <NPortid> */
lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
@@ -3856,6 +3861,7 @@ out:
* the routine lpfc_els_free_iocb.
*/
cmdiocb->context1 = NULL;
+
}
lpfc_els_free_iocb(phba, cmdiocb);
@@ -3898,6 +3904,7 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag,
IOCB_t *oldcmd;
struct lpfc_iocbq *elsiocb;
uint8_t *pcmd;
+ struct serv_parm *sp;
uint16_t cmdsize;
int rc;
ELS_PKT *els_pkt_ptr;
@@ -3927,6 +3934,7 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag,
"Issue ACC: did:x%x flg:x%x",
ndlp->nlp_DID, ndlp->nlp_flag, 0);
break;
+ case ELS_CMD_FLOGI:
case ELS_CMD_PLOGI:
cmdsize = (sizeof(struct serv_parm) + sizeof(uint32_t));
elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, oldiocb->retry,
@@ -3944,10 +3952,34 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag,
*((uint32_t *) (pcmd)) = ELS_CMD_ACC;
pcmd += sizeof(uint32_t);
- memcpy(pcmd, &vport->fc_sparam, sizeof(struct serv_parm));
+ sp = (struct serv_parm *)pcmd;
+
+ if (flag == ELS_CMD_FLOGI) {
+ /* Copy the received service parameters back */
+ memcpy(sp, &phba->fc_fabparam,
+ sizeof(struct serv_parm));
+
+ /* Clear the F_Port bit */
+ sp->cmn.fPort = 0;
+
+ /* Mark all class service parameters as invalid */
+ sp->cls1.classValid = 0;
+ sp->cls2.classValid = 0;
+ sp->cls3.classValid = 0;
+ sp->cls4.classValid = 0;
+
+ /* Copy our worldwide names */
+ memcpy(&sp->portName, &vport->fc_sparam.portName,
+ sizeof(struct lpfc_name));
+ memcpy(&sp->nodeName, &vport->fc_sparam.nodeName,
+ sizeof(struct lpfc_name));
+ } else {
+ memcpy(pcmd, &vport->fc_sparam,
+ sizeof(struct serv_parm));
+ }
lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP,
- "Issue ACC PLOGI: did:x%x flg:x%x",
+ "Issue ACC FLOGI/PLOGI: did:x%x flg:x%x",
ndlp->nlp_DID, ndlp->nlp_flag, 0);
break;
case ELS_CMD_PRLO:
@@ -4673,6 +4705,23 @@ lpfc_rdp_res_link_error(struct fc_rdp_link_error_status_desc *desc,
desc->length = cpu_to_be32(sizeof(desc->info));
}
+int
+lpfc_rdp_res_fec_desc(struct fc_fec_rdp_desc *desc, READ_LNK_VAR *stat)
+{
+ if (bf_get(lpfc_read_link_stat_gec2, stat) == 0)
+ return 0;
+ desc->tag = cpu_to_be32(RDP_FEC_DESC_TAG);
+
+ desc->info.CorrectedBlocks =
+ cpu_to_be32(stat->fecCorrBlkCount);
+ desc->info.UncorrectableBlocks =
+ cpu_to_be32(stat->fecUncorrBlkCount);
+
+ desc->length = cpu_to_be32(sizeof(desc->info));
+
+ return sizeof(struct fc_fec_rdp_desc);
+}
+
void
lpfc_rdp_res_speed(struct fc_rdp_port_speed_desc *desc, struct lpfc_hba *phba)
{
@@ -4681,26 +4730,26 @@ lpfc_rdp_res_speed(struct fc_rdp_port_speed_desc *desc, struct lpfc_hba *phba)
desc->tag = cpu_to_be32(RDP_PORT_SPEED_DESC_TAG);
- switch (phba->sli4_hba.link_state.speed) {
- case LPFC_FC_LA_SPEED_1G:
+ switch (phba->fc_linkspeed) {
+ case LPFC_LINK_SPEED_1GHZ:
rdp_speed = RDP_PS_1GB;
break;
- case LPFC_FC_LA_SPEED_2G:
+ case LPFC_LINK_SPEED_2GHZ:
rdp_speed = RDP_PS_2GB;
break;
- case LPFC_FC_LA_SPEED_4G:
+ case LPFC_LINK_SPEED_4GHZ:
rdp_speed = RDP_PS_4GB;
break;
- case LPFC_FC_LA_SPEED_8G:
+ case LPFC_LINK_SPEED_8GHZ:
rdp_speed = RDP_PS_8GB;
break;
- case LPFC_FC_LA_SPEED_10G:
+ case LPFC_LINK_SPEED_10GHZ:
rdp_speed = RDP_PS_10GB;
break;
- case LPFC_FC_LA_SPEED_16G:
+ case LPFC_LINK_SPEED_16GHZ:
rdp_speed = RDP_PS_16GB;
break;
- case LPFC_FC_LA_SPEED_32G:
+ case LPFC_LINK_SPEED_32GHZ:
rdp_speed = RDP_PS_32GB;
break;
default:
@@ -4778,15 +4827,18 @@ lpfc_els_rdp_cmpl(struct lpfc_hba *phba, struct lpfc_rdp_context *rdp_context,
struct lpfc_nodelist *ndlp = rdp_context->ndlp;
struct lpfc_vport *vport = ndlp->vport;
struct lpfc_iocbq *elsiocb;
+ struct ulp_bde64 *bpl;
IOCB_t *icmd;
uint8_t *pcmd;
struct ls_rjt *stat;
struct fc_rdp_res_frame *rdp_res;
uint32_t cmdsize;
- int rc;
+ int rc, fec_size;
if (status != SUCCESS)
goto error;
+
+ /* This will change once we know the true size of the RDP payload */
cmdsize = sizeof(struct fc_rdp_res_frame);
elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize,
@@ -4823,10 +4875,18 @@ lpfc_els_rdp_cmpl(struct lpfc_hba *phba, struct lpfc_rdp_context *rdp_context,
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);
-
+ fec_size = lpfc_rdp_res_fec_desc(&rdp_res->fec_desc,
+ &rdp_context->link_stat);
+ rdp_res->length = cpu_to_be32(fec_size + RDP_DESC_PAYLOAD_SIZE);
elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
+ /* Now that we know the true size of the payload, update the BPL */
+ bpl = (struct ulp_bde64 *)
+ (((struct lpfc_dmabuf *)(elsiocb->context3))->virt);
+ bpl->tus.f.bdeSize = (fec_size + RDP_DESC_PAYLOAD_SIZE + 8);
+ bpl->tus.f.bdeFlags = 0;
+ bpl->tus.w = le32_to_cpu(bpl->tus.w);
+
phba->fc_stat.elsXmitACC++;
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
if (rc == IOCB_ERROR)
@@ -4956,13 +5016,12 @@ lpfc_els_rcv_rdp(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
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);
+ rdp_context = kzalloc(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;
@@ -5739,7 +5798,6 @@ lpfc_els_rcv_flogi(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
IOCB_t *icmd = &cmdiocb->iocb;
struct serv_parm *sp;
LPFC_MBOXQ_t *mbox;
- struct ls_rjt stat;
uint32_t cmd, did;
int rc;
uint32_t fc_flag = 0;
@@ -5765,135 +5823,92 @@ lpfc_els_rcv_flogi(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
return 1;
}
- if ((lpfc_check_sparm(vport, ndlp, sp, CLASS3, 1))) {
- /* For a FLOGI we accept, then if our portname is greater
- * then the remote portname we initiate Nport login.
- */
+ (void) lpfc_check_sparm(vport, ndlp, sp, CLASS3, 1);
- rc = memcmp(&vport->fc_portname, &sp->portName,
- sizeof(struct lpfc_name));
- if (!rc) {
- if (phba->sli_rev < LPFC_SLI_REV4) {
- mbox = mempool_alloc(phba->mbox_mem_pool,
- GFP_KERNEL);
- if (!mbox)
- return 1;
- lpfc_linkdown(phba);
- lpfc_init_link(phba, mbox,
- phba->cfg_topology,
- phba->cfg_link_speed);
- mbox->u.mb.un.varInitLnk.lipsr_AL_PA = 0;
- mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
- mbox->vport = vport;
- rc = lpfc_sli_issue_mbox(phba, mbox,
- MBX_NOWAIT);
- lpfc_set_loopback_flag(phba);
- if (rc == MBX_NOT_FINISHED)
- mempool_free(mbox, phba->mbox_mem_pool);
- return 1;
- } else {
- /* abort the flogi coming back to ourselves
- * due to external loopback on the port.
- */
- lpfc_els_abort_flogi(phba);
- return 0;
- }
- } else if (rc > 0) { /* greater than */
- spin_lock_irq(shost->host_lock);
- vport->fc_flag |= FC_PT2PT_PLOGI;
- spin_unlock_irq(shost->host_lock);
+ /*
+ * If our portname is greater than the remote portname,
+ * then we initiate Nport login.
+ */
- /* If we have the high WWPN we can assign our own
- * myDID; otherwise, we have to WAIT for a PLOGI
- * from the remote NPort to find out what it
- * will be.
- */
- vport->fc_myDID = PT2PT_LocalID;
- } else
- vport->fc_myDID = PT2PT_RemoteID;
+ rc = memcmp(&vport->fc_portname, &sp->portName,
+ sizeof(struct lpfc_name));
- /*
- * The vport state should go to LPFC_FLOGI only
- * AFTER we issue a FLOGI, not receive one.
+ if (!rc) {
+ if (phba->sli_rev < LPFC_SLI_REV4) {
+ mbox = mempool_alloc(phba->mbox_mem_pool,
+ GFP_KERNEL);
+ if (!mbox)
+ return 1;
+ lpfc_linkdown(phba);
+ lpfc_init_link(phba, mbox,
+ phba->cfg_topology,
+ phba->cfg_link_speed);
+ mbox->u.mb.un.varInitLnk.lipsr_AL_PA = 0;
+ mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ mbox->vport = vport;
+ rc = lpfc_sli_issue_mbox(phba, mbox,
+ MBX_NOWAIT);
+ lpfc_set_loopback_flag(phba);
+ if (rc == MBX_NOT_FINISHED)
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return 1;
+ }
+
+ /* abort the flogi coming back to ourselves
+ * due to external loopback on the port.
*/
+ lpfc_els_abort_flogi(phba);
+ return 0;
+
+ } else if (rc > 0) { /* greater than */
spin_lock_irq(shost->host_lock);
- fc_flag = vport->fc_flag;
- port_state = vport->port_state;
- vport->fc_flag |= FC_PT2PT;
- vport->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP);
+ vport->fc_flag |= FC_PT2PT_PLOGI;
spin_unlock_irq(shost->host_lock);
- lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
- "3311 Rcv Flogi PS x%x new PS x%x "
- "fc_flag x%x new fc_flag x%x\n",
- port_state, vport->port_state,
- fc_flag, vport->fc_flag);
- /*
- * We temporarily set fc_myDID to make it look like we are
- * a Fabric. This is done just so we end up with the right
- * did / sid on the FLOGI ACC rsp.
+ /* If we have the high WWPN we can assign our own
+ * myDID; otherwise, we have to WAIT for a PLOGI
+ * from the remote NPort to find out what it
+ * will be.
*/
- did = vport->fc_myDID;
- vport->fc_myDID = Fabric_DID;
-
+ vport->fc_myDID = PT2PT_LocalID;
} else {
- /* Reject this request because invalid parameters */
- stat.un.b.lsRjtRsvd0 = 0;
- stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
- stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS;
- stat.un.b.vendorUnique = 0;
-
- /*
- * We temporarily set fc_myDID to make it look like we are
- * a Fabric. This is done just so we end up with the right
- * did / sid on the FLOGI LS_RJT rsp.
- */
- did = vport->fc_myDID;
- vport->fc_myDID = Fabric_DID;
-
- lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp,
- NULL);
+ vport->fc_myDID = PT2PT_RemoteID;
+ }
- /* Now lets put fc_myDID back to what its supposed to be */
- vport->fc_myDID = did;
+ /*
+ * The vport state should go to LPFC_FLOGI only
+ * AFTER we issue a FLOGI, not receive one.
+ */
+ spin_lock_irq(shost->host_lock);
+ fc_flag = vport->fc_flag;
+ port_state = vport->port_state;
+ vport->fc_flag |= FC_PT2PT;
+ vport->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP);
+ spin_unlock_irq(shost->host_lock);
+ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+ "3311 Rcv Flogi PS x%x new PS x%x "
+ "fc_flag x%x new fc_flag x%x\n",
+ port_state, vport->port_state,
+ fc_flag, vport->fc_flag);
- return 1;
- }
+ /*
+ * We temporarily set fc_myDID to make it look like we are
+ * a Fabric. This is done just so we end up with the right
+ * did / sid on the FLOGI ACC rsp.
+ */
+ did = vport->fc_myDID;
+ vport->fc_myDID = Fabric_DID;
- /* send our FLOGI first */
- if (vport->port_state < LPFC_FLOGI) {
- vport->fc_myDID = 0;
- lpfc_initial_flogi(vport);
- vport->fc_myDID = Fabric_DID;
- }
+ memcpy(&phba->fc_fabparam, sp, sizeof(struct serv_parm));
/* Send back ACC */
- lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL);
+ lpfc_els_rsp_acc(vport, ELS_CMD_FLOGI, cmdiocb, ndlp, NULL);
/* Now lets put fc_myDID back to what its supposed to be */
vport->fc_myDID = did;
- if (!(vport->fc_flag & FC_PT2PT_PLOGI)) {
-
- mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
- if (!mbox)
- goto fail;
-
- lpfc_config_link(phba, mbox);
-
- mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
- mbox->vport = vport;
- rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
- if (rc == MBX_NOT_FINISHED) {
- mempool_free(mbox, phba->mbox_mem_pool);
- goto fail;
- }
- }
-
return 0;
-fail:
- return 1;
}
/**
@@ -7345,7 +7360,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
/* reject till our FLOGI completes */
if ((vport->port_state < LPFC_FABRIC_CFG_LINK) &&
- (cmd != ELS_CMD_FLOGI)) {
+ (cmd != ELS_CMD_FLOGI)) {
rjt_err = LSRJT_UNABLE_TPC;
rjt_exp = LSEXP_NOTHING_MORE;
goto lsrjt;
@@ -7381,6 +7396,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
rjt_exp = LSEXP_NOTHING_MORE;
break;
}
+
if (vport->port_state < LPFC_DISC_AUTH) {
if (!(phba->pport->fc_flag & FC_PT2PT) ||
(phba->pport->fc_flag & FC_PT2PT_PLOGI)) {
@@ -7730,6 +7746,35 @@ lpfc_els_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
}
}
+void
+lpfc_start_fdmi(struct lpfc_vport *vport)
+{
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_nodelist *ndlp;
+
+ /* If this is the first time, allocate an ndlp and initialize
+ * it. Otherwise, make sure the node is enabled and then do the
+ * login.
+ */
+ ndlp = lpfc_findnode_did(vport, FDMI_DID);
+ if (!ndlp) {
+ ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL);
+ if (ndlp) {
+ lpfc_nlp_init(vport, ndlp, FDMI_DID);
+ ndlp->nlp_type |= NLP_FABRIC;
+ } else {
+ return;
+ }
+ }
+ if (!NLP_CHK_NODE_ACT(ndlp))
+ ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_NPR_NODE);
+
+ if (ndlp) {
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE);
+ lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0);
+ }
+}
+
/**
* lpfc_do_scr_ns_plogi - Issue a plogi to the name server for scr
* @phba: pointer to lpfc hba data structure.
@@ -7746,7 +7791,7 @@ lpfc_els_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
void
lpfc_do_scr_ns_plogi(struct lpfc_hba *phba, struct lpfc_vport *vport)
{
- struct lpfc_nodelist *ndlp, *ndlp_fdmi;
+ struct lpfc_nodelist *ndlp;
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
/*
@@ -7804,32 +7849,9 @@ lpfc_do_scr_ns_plogi(struct lpfc_hba *phba, struct lpfc_vport *vport)
return;
}
- if (vport->cfg_fdmi_on & LPFC_FDMI_SUPPORT) {
- /* If this is the first time, allocate an ndlp and initialize
- * it. Otherwise, make sure the node is enabled and then do the
- * login.
- */
- ndlp_fdmi = lpfc_findnode_did(vport, FDMI_DID);
- if (!ndlp_fdmi) {
- ndlp_fdmi = mempool_alloc(phba->nlp_mem_pool,
- GFP_KERNEL);
- if (ndlp_fdmi) {
- lpfc_nlp_init(vport, ndlp_fdmi, FDMI_DID);
- ndlp_fdmi->nlp_type |= NLP_FABRIC;
- } else
- return;
- }
- if (!NLP_CHK_NODE_ACT(ndlp_fdmi))
- ndlp_fdmi = lpfc_enable_node(vport,
- ndlp_fdmi,
- NLP_STE_NPR_NODE);
-
- if (ndlp_fdmi) {
- lpfc_nlp_set_state(vport, ndlp_fdmi,
- NLP_STE_PLOGI_ISSUE);
- lpfc_issue_els_plogi(vport, ndlp_fdmi->nlp_DID, 0);
- }
- }
+ if ((phba->cfg_fdmi_on > LPFC_FDMI_NO_SUPPORT) &&
+ (vport->load_flag & FC_ALLOW_FDMI))
+ lpfc_start_fdmi(vport);
}
/**
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index bfc2442dd74a..c37d72effbff 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -674,8 +674,6 @@ lpfc_work_done(struct lpfc_hba *phba)
lpfc_mbox_timeout_handler(phba);
if (work_port_events & WORKER_FABRIC_BLOCK_TMO)
lpfc_unblock_fabric_iocbs(phba);
- if (work_port_events & WORKER_FDMI_TMO)
- lpfc_fdmi_timeout_handler(vport);
if (work_port_events & WORKER_RAMP_DOWN_QUEUE)
lpfc_ramp_down_queue_handler(phba);
if (work_port_events & WORKER_DELAYED_DISC_TMO)
@@ -1083,7 +1081,7 @@ out:
}
-static void
+void
lpfc_mbx_cmpl_local_config_link(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
{
struct lpfc_vport *vport = pmb->vport;
@@ -1113,8 +1111,10 @@ lpfc_mbx_cmpl_local_config_link(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
/* Start discovery by sending a FLOGI. port_state is identically
* LPFC_FLOGI while waiting for FLOGI cmpl
*/
- if (vport->port_state != LPFC_FLOGI || vport->fc_flag & FC_PT2PT_PLOGI)
+ if (vport->port_state != LPFC_FLOGI)
lpfc_initial_flogi(vport);
+ else if (vport->fc_flag & FC_PT2PT)
+ lpfc_disc_start(vport);
return;
out:
@@ -2963,8 +2963,10 @@ lpfc_mbx_cmpl_reg_vfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
out_free_mem:
mempool_free(mboxq, phba->mbox_mem_pool);
- lpfc_mbuf_free(phba, dmabuf->virt, dmabuf->phys);
- kfree(dmabuf);
+ if (dmabuf) {
+ lpfc_mbuf_free(phba, dmabuf->virt, dmabuf->phys);
+ kfree(dmabuf);
+ }
return;
}
@@ -3035,19 +3037,22 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, struct lpfc_mbx_read_top *la)
uint32_t fc_flags = 0;
spin_lock_irq(&phba->hbalock);
- switch (bf_get(lpfc_mbx_read_top_link_spd, la)) {
- case LPFC_LINK_SPEED_1GHZ:
- case LPFC_LINK_SPEED_2GHZ:
- case LPFC_LINK_SPEED_4GHZ:
- case LPFC_LINK_SPEED_8GHZ:
- case LPFC_LINK_SPEED_10GHZ:
- case LPFC_LINK_SPEED_16GHZ:
- case LPFC_LINK_SPEED_32GHZ:
- phba->fc_linkspeed = bf_get(lpfc_mbx_read_top_link_spd, la);
- break;
- default:
- phba->fc_linkspeed = LPFC_LINK_SPEED_UNKNOWN;
- break;
+ phba->fc_linkspeed = bf_get(lpfc_mbx_read_top_link_spd, la);
+
+ if (!(phba->hba_flag & HBA_FCOE_MODE)) {
+ switch (bf_get(lpfc_mbx_read_top_link_spd, la)) {
+ case LPFC_LINK_SPEED_1GHZ:
+ case LPFC_LINK_SPEED_2GHZ:
+ case LPFC_LINK_SPEED_4GHZ:
+ case LPFC_LINK_SPEED_8GHZ:
+ case LPFC_LINK_SPEED_10GHZ:
+ case LPFC_LINK_SPEED_16GHZ:
+ case LPFC_LINK_SPEED_32GHZ:
+ break;
+ default:
+ phba->fc_linkspeed = LPFC_LINK_SPEED_UNKNOWN;
+ break;
+ }
}
if (phba->fc_topology &&
@@ -3448,10 +3453,10 @@ lpfc_mbx_cmpl_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~NLP_IGNR_REG_CMPL;
spin_unlock_irq(shost->host_lock);
- } else
- /* Good status, call state machine */
- lpfc_disc_state_machine(vport, ndlp, pmb,
- NLP_EVT_CMPL_REG_LOGIN);
+ }
+
+ /* Call state machine */
+ lpfc_disc_state_machine(vport, ndlp, pmb, NLP_EVT_CMPL_REG_LOGIN);
lpfc_mbuf_free(phba, mp->virt, mp->phys);
kfree(mp);
@@ -5550,15 +5555,15 @@ lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
ndlp->nlp_usg_map, ndlp);
/*
* Start issuing Fabric-Device Management Interface (FDMI) command to
- * 0xfffffa (FDMI well known port) or Delay issuing FDMI command if
- * fdmi-on=2 (supporting RPA/hostnmae)
+ * 0xfffffa (FDMI well known port).
+ * DHBA -> DPRT -> RHBA -> RPA (physical port)
+ * DPRT -> RPRT (vports)
*/
-
- if (vport->cfg_fdmi_on & LPFC_FDMI_REG_DELAY)
- mod_timer(&vport->fc_fdmitmo,
- jiffies + msecs_to_jiffies(1000 * 60));
+ if (vport->port_type == LPFC_PHYSICAL_PORT)
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DHBA, 0);
else
- lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DHBA);
+ lpfc_fdmi_cmd(vport, ndlp, SLI_MGMT_DPRT, 0);
+
/* decrement the node reference count held for this callback
* function.
diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
index 2cce88e967ce..dd20412c7e4c 100644
--- a/drivers/scsi/lpfc/lpfc_hw.h
+++ b/drivers/scsi/lpfc/lpfc_hw.h
@@ -1097,6 +1097,18 @@ struct fc_rdp_port_name_desc {
};
+struct fc_rdp_fec_info {
+ uint32_t CorrectedBlocks;
+ uint32_t UncorrectableBlocks;
+};
+
+#define RDP_FEC_DESC_TAG 0x00010005
+struct fc_fec_rdp_desc {
+ uint32_t tag;
+ uint32_t length;
+ struct fc_rdp_fec_info info;
+};
+
struct fc_rdp_link_error_status_payload_info {
struct fc_link_status link_status; /* 24 bytes */
uint32_t port_type; /* bits 31-30 only */
@@ -1196,14 +1208,15 @@ struct fc_rdp_res_frame {
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 */
+ struct fc_fec_rdp_desc fec_desc; /* FC Word 34 - 37 */
};
#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))
+ + 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 ********/
@@ -1233,31 +1246,10 @@ struct lpfc_fdmi_attr_def { /* Defined in TLV format */
/* Attribute Entry */
struct lpfc_fdmi_attr_entry {
union {
- uint32_t VendorSpecific;
- uint32_t SupportClass;
- uint32_t SupportSpeed;
- uint32_t PortSpeed;
- uint32_t MaxFrameSize;
- uint32_t MaxCTPayloadLen;
- uint32_t PortState;
- uint32_t PortId;
- struct lpfc_name NodeName;
- struct lpfc_name PortName;
- struct lpfc_name FabricName;
- uint8_t FC4Types[32];
- uint8_t Manufacturer[64];
- uint8_t SerialNumber[64];
- uint8_t Model[256];
- uint8_t ModelDescription[256];
- uint8_t HardwareVersion[256];
- uint8_t DriverVersion[256];
- uint8_t OptionROMVersion[256];
- uint8_t FirmwareVersion[256];
- uint8_t OsHostName[256];
- uint8_t NodeSymName[256];
- uint8_t OsDeviceName[256];
- uint8_t OsNameVersion[256];
- uint8_t HostName[256];
+ uint32_t AttrInt;
+ uint8_t AttrTypes[32];
+ uint8_t AttrString[256];
+ struct lpfc_name AttrWWN;
} un;
};
@@ -1327,6 +1319,8 @@ struct lpfc_fdmi_reg_portattr {
#define SLI_MGMT_DPRT 0x310 /* De-register Port */
#define SLI_MGMT_DPA 0x311 /* De-register Port attributes */
+#define LPFC_FDMI_MAX_RETRY 3 /* Max retries for a FDMI command */
+
/*
* HBA Attribute Types
*/
@@ -1342,6 +1336,39 @@ struct lpfc_fdmi_reg_portattr {
#define RHBA_OS_NAME_VERSION 0xa /* 4 to 256 byte ASCII string */
#define RHBA_MAX_CT_PAYLOAD_LEN 0xb /* 32-bit unsigned int */
#define RHBA_SYM_NODENAME 0xc /* 4 to 256 byte ASCII string */
+#define RHBA_VENDOR_INFO 0xd /* 32-bit unsigned int */
+#define RHBA_NUM_PORTS 0xe /* 32-bit unsigned int */
+#define RHBA_FABRIC_WWNN 0xf /* 8 byte WWNN */
+#define RHBA_BIOS_VERSION 0x10 /* 4 to 256 byte ASCII string */
+#define RHBA_BIOS_STATE 0x11 /* 32-bit unsigned int */
+#define RHBA_VENDOR_ID 0xe0 /* 8 byte ASCII string */
+
+/* Bit mask for all individual HBA attributes */
+#define LPFC_FDMI_HBA_ATTR_wwnn 0x00000001
+#define LPFC_FDMI_HBA_ATTR_manufacturer 0x00000002
+#define LPFC_FDMI_HBA_ATTR_sn 0x00000004
+#define LPFC_FDMI_HBA_ATTR_model 0x00000008
+#define LPFC_FDMI_HBA_ATTR_description 0x00000010
+#define LPFC_FDMI_HBA_ATTR_hdw_ver 0x00000020
+#define LPFC_FDMI_HBA_ATTR_drvr_ver 0x00000040
+#define LPFC_FDMI_HBA_ATTR_rom_ver 0x00000080
+#define LPFC_FDMI_HBA_ATTR_fmw_ver 0x00000100
+#define LPFC_FDMI_HBA_ATTR_os_ver 0x00000200
+#define LPFC_FDMI_HBA_ATTR_ct_len 0x00000400
+#define LPFC_FDMI_HBA_ATTR_symbolic_name 0x00000800
+#define LPFC_FDMI_HBA_ATTR_vendor_info 0x00001000 /* Not used */
+#define LPFC_FDMI_HBA_ATTR_num_ports 0x00002000
+#define LPFC_FDMI_HBA_ATTR_fabric_wwnn 0x00004000
+#define LPFC_FDMI_HBA_ATTR_bios_ver 0x00008000
+#define LPFC_FDMI_HBA_ATTR_bios_state 0x00010000 /* Not used */
+#define LPFC_FDMI_HBA_ATTR_vendor_id 0x00020000
+
+/* Bit mask for FDMI-1 defined HBA attributes */
+#define LPFC_FDMI1_HBA_ATTR 0x000007ff
+
+/* Bit mask for FDMI-2 defined HBA attributes */
+/* Skip vendor_info and bios_state */
+#define LPFC_FDMI2_HBA_ATTR 0x0002efff
/*
* Port Attrubute Types
@@ -1353,15 +1380,65 @@ struct lpfc_fdmi_reg_portattr {
#define RPRT_OS_DEVICE_NAME 0x5 /* 4 to 256 byte ASCII string */
#define RPRT_HOST_NAME 0x6 /* 4 to 256 byte ASCII string */
#define RPRT_NODENAME 0x7 /* 8 byte WWNN */
-#define RPRT_PORTNAME 0x8 /* 8 byte WWNN */
+#define RPRT_PORTNAME 0x8 /* 8 byte WWPN */
#define RPRT_SYM_PORTNAME 0x9 /* 4 to 256 byte ASCII string */
#define RPRT_PORT_TYPE 0xa /* 32-bit unsigned int */
#define RPRT_SUPPORTED_CLASS 0xb /* 32-bit unsigned int */
-#define RPRT_FABRICNAME 0xc /* 8 byte Fabric WWNN */
+#define RPRT_FABRICNAME 0xc /* 8 byte Fabric WWPN */
#define RPRT_ACTIVE_FC4_TYPES 0xd /* 32 byte binary array */
#define RPRT_PORT_STATE 0x101 /* 32-bit unsigned int */
#define RPRT_DISC_PORT 0x102 /* 32-bit unsigned int */
#define RPRT_PORT_ID 0x103 /* 32-bit unsigned int */
+#define RPRT_SMART_SERVICE 0xf100 /* 4 to 256 byte ASCII string */
+#define RPRT_SMART_GUID 0xf101 /* 8 byte WWNN + 8 byte WWPN */
+#define RPRT_SMART_VERSION 0xf102 /* 4 to 256 byte ASCII string */
+#define RPRT_SMART_MODEL 0xf103 /* 4 to 256 byte ASCII string */
+#define RPRT_SMART_PORT_INFO 0xf104 /* 32-bit unsigned int */
+#define RPRT_SMART_QOS 0xf105 /* 32-bit unsigned int */
+#define RPRT_SMART_SECURITY 0xf106 /* 32-bit unsigned int */
+
+/* Bit mask for all individual PORT attributes */
+#define LPFC_FDMI_PORT_ATTR_fc4type 0x00000001
+#define LPFC_FDMI_PORT_ATTR_support_speed 0x00000002
+#define LPFC_FDMI_PORT_ATTR_speed 0x00000004
+#define LPFC_FDMI_PORT_ATTR_max_frame 0x00000008
+#define LPFC_FDMI_PORT_ATTR_os_devname 0x00000010
+#define LPFC_FDMI_PORT_ATTR_host_name 0x00000020
+#define LPFC_FDMI_PORT_ATTR_wwnn 0x00000040
+#define LPFC_FDMI_PORT_ATTR_wwpn 0x00000080
+#define LPFC_FDMI_PORT_ATTR_symbolic_name 0x00000100
+#define LPFC_FDMI_PORT_ATTR_port_type 0x00000200
+#define LPFC_FDMI_PORT_ATTR_class 0x00000400
+#define LPFC_FDMI_PORT_ATTR_fabric_wwpn 0x00000800
+#define LPFC_FDMI_PORT_ATTR_port_state 0x00001000
+#define LPFC_FDMI_PORT_ATTR_active_fc4type 0x00002000
+#define LPFC_FDMI_PORT_ATTR_num_disc 0x00004000
+#define LPFC_FDMI_PORT_ATTR_nportid 0x00008000
+#define LPFC_FDMI_SMART_ATTR_service 0x00010000 /* Vendor specific */
+#define LPFC_FDMI_SMART_ATTR_guid 0x00020000 /* Vendor specific */
+#define LPFC_FDMI_SMART_ATTR_version 0x00040000 /* Vendor specific */
+#define LPFC_FDMI_SMART_ATTR_model 0x00080000 /* Vendor specific */
+#define LPFC_FDMI_SMART_ATTR_port_info 0x00100000 /* Vendor specific */
+#define LPFC_FDMI_SMART_ATTR_qos 0x00200000 /* Vendor specific */
+#define LPFC_FDMI_SMART_ATTR_security 0x00400000 /* Vendor specific */
+
+/* Bit mask for FDMI-1 defined PORT attributes */
+#define LPFC_FDMI1_PORT_ATTR 0x0000003f
+
+/* Bit mask for FDMI-2 defined PORT attributes */
+#define LPFC_FDMI2_PORT_ATTR 0x0000ffff
+
+/* Bit mask for Smart SAN defined PORT attributes */
+#define LPFC_FDMI2_SMART_ATTR 0x007fffff
+
+/* Defines for PORT port state attribute */
+#define LPFC_FDMI_PORTSTATE_UNKNOWN 1
+#define LPFC_FDMI_PORTSTATE_ONLINE 2
+
+/* Defines for PORT port type attribute */
+#define LPFC_FDMI_PORTTYPE_UNKNOWN 0
+#define LPFC_FDMI_PORTTYPE_NPORT 1
+#define LPFC_FDMI_PORTTYPE_NLPORT 2
/*
* Begin HBA configuration parameters.
@@ -2498,10 +2575,38 @@ typedef struct {
/* Structure for MB Command READ_LINK_STAT (18) */
typedef struct {
- uint32_t rsvd1;
+ uint32_t word0;
+
+#define lpfc_read_link_stat_rec_SHIFT 0
+#define lpfc_read_link_stat_rec_MASK 0x1
+#define lpfc_read_link_stat_rec_WORD word0
+
+#define lpfc_read_link_stat_gec_SHIFT 1
+#define lpfc_read_link_stat_gec_MASK 0x1
+#define lpfc_read_link_stat_gec_WORD word0
+
+#define lpfc_read_link_stat_w02oftow23of_SHIFT 2
+#define lpfc_read_link_stat_w02oftow23of_MASK 0x3FFFFF
+#define lpfc_read_link_stat_w02oftow23of_WORD word0
+
+#define lpfc_read_link_stat_rsvd_SHIFT 24
+#define lpfc_read_link_stat_rsvd_MASK 0x1F
+#define lpfc_read_link_stat_rsvd_WORD word0
+
+#define lpfc_read_link_stat_gec2_SHIFT 29
+#define lpfc_read_link_stat_gec2_MASK 0x1
+#define lpfc_read_link_stat_gec2_WORD word0
+
+#define lpfc_read_link_stat_clrc_SHIFT 30
+#define lpfc_read_link_stat_clrc_MASK 0x1
+#define lpfc_read_link_stat_clrc_WORD word0
+
+#define lpfc_read_link_stat_clof_SHIFT 31
+#define lpfc_read_link_stat_clof_MASK 0x1
+#define lpfc_read_link_stat_clof_WORD word0
+
uint32_t linkFailureCnt;
uint32_t lossSyncCnt;
-
uint32_t lossSignalCnt;
uint32_t primSeqErrCnt;
uint32_t invalidXmitWord;
@@ -2509,6 +2614,19 @@ typedef struct {
uint32_t primSeqTimeout;
uint32_t elasticOverrun;
uint32_t arbTimeout;
+ uint32_t advRecBufCredit;
+ uint32_t curRecBufCredit;
+ uint32_t advTransBufCredit;
+ uint32_t curTransBufCredit;
+ uint32_t recEofCount;
+ uint32_t recEofdtiCount;
+ uint32_t recEofniCount;
+ uint32_t recSofcount;
+ uint32_t rsvd1;
+ uint32_t rsvd2;
+ uint32_t recDrpXriCount;
+ uint32_t fecCorrBlkCount;
+ uint32_t fecUncorrBlkCount;
} READ_LNK_VAR;
/* Structure for MB Command REG_LOGIN (19) */
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
index 33ec4fa39ccb..608f9415fb08 100644
--- a/drivers/scsi/lpfc/lpfc_hw4.h
+++ b/drivers/scsi/lpfc/lpfc_hw4.h
@@ -3317,6 +3317,7 @@ struct lpfc_acqe_link {
#define LPFC_ASYNC_LINK_SPEED_20GBPS 0x5
#define LPFC_ASYNC_LINK_SPEED_25GBPS 0x6
#define LPFC_ASYNC_LINK_SPEED_40GBPS 0x7
+#define LPFC_ASYNC_LINK_SPEED_100GBPS 0x8
#define lpfc_acqe_link_duplex_SHIFT 16
#define lpfc_acqe_link_duplex_MASK 0x000000FF
#define lpfc_acqe_link_duplex_WORD word0
@@ -3447,23 +3448,50 @@ struct lpfc_acqe_fc_la {
struct lpfc_acqe_misconfigured_event {
struct {
uint32_t word0;
-#define lpfc_sli_misconfigured_port0_SHIFT 0
-#define lpfc_sli_misconfigured_port0_MASK 0x000000FF
-#define lpfc_sli_misconfigured_port0_WORD word0
-#define lpfc_sli_misconfigured_port1_SHIFT 8
-#define lpfc_sli_misconfigured_port1_MASK 0x000000FF
-#define lpfc_sli_misconfigured_port1_WORD word0
-#define lpfc_sli_misconfigured_port2_SHIFT 16
-#define lpfc_sli_misconfigured_port2_MASK 0x000000FF
-#define lpfc_sli_misconfigured_port2_WORD word0
-#define lpfc_sli_misconfigured_port3_SHIFT 24
-#define lpfc_sli_misconfigured_port3_MASK 0x000000FF
-#define lpfc_sli_misconfigured_port3_WORD word0
+#define lpfc_sli_misconfigured_port0_state_SHIFT 0
+#define lpfc_sli_misconfigured_port0_state_MASK 0x000000FF
+#define lpfc_sli_misconfigured_port0_state_WORD word0
+#define lpfc_sli_misconfigured_port1_state_SHIFT 8
+#define lpfc_sli_misconfigured_port1_state_MASK 0x000000FF
+#define lpfc_sli_misconfigured_port1_state_WORD word0
+#define lpfc_sli_misconfigured_port2_state_SHIFT 16
+#define lpfc_sli_misconfigured_port2_state_MASK 0x000000FF
+#define lpfc_sli_misconfigured_port2_state_WORD word0
+#define lpfc_sli_misconfigured_port3_state_SHIFT 24
+#define lpfc_sli_misconfigured_port3_state_MASK 0x000000FF
+#define lpfc_sli_misconfigured_port3_state_WORD word0
+ uint32_t word1;
+#define lpfc_sli_misconfigured_port0_op_SHIFT 0
+#define lpfc_sli_misconfigured_port0_op_MASK 0x00000001
+#define lpfc_sli_misconfigured_port0_op_WORD word1
+#define lpfc_sli_misconfigured_port0_severity_SHIFT 1
+#define lpfc_sli_misconfigured_port0_severity_MASK 0x00000003
+#define lpfc_sli_misconfigured_port0_severity_WORD word1
+#define lpfc_sli_misconfigured_port1_op_SHIFT 8
+#define lpfc_sli_misconfigured_port1_op_MASK 0x00000001
+#define lpfc_sli_misconfigured_port1_op_WORD word1
+#define lpfc_sli_misconfigured_port1_severity_SHIFT 9
+#define lpfc_sli_misconfigured_port1_severity_MASK 0x00000003
+#define lpfc_sli_misconfigured_port1_severity_WORD word1
+#define lpfc_sli_misconfigured_port2_op_SHIFT 16
+#define lpfc_sli_misconfigured_port2_op_MASK 0x00000001
+#define lpfc_sli_misconfigured_port2_op_WORD word1
+#define lpfc_sli_misconfigured_port2_severity_SHIFT 17
+#define lpfc_sli_misconfigured_port2_severity_MASK 0x00000003
+#define lpfc_sli_misconfigured_port2_severity_WORD word1
+#define lpfc_sli_misconfigured_port3_op_SHIFT 24
+#define lpfc_sli_misconfigured_port3_op_MASK 0x00000001
+#define lpfc_sli_misconfigured_port3_op_WORD word1
+#define lpfc_sli_misconfigured_port3_severity_SHIFT 25
+#define lpfc_sli_misconfigured_port3_severity_MASK 0x00000003
+#define lpfc_sli_misconfigured_port3_severity_WORD word1
} theEvent;
#define LPFC_SLI_EVENT_STATUS_VALID 0x00
#define LPFC_SLI_EVENT_STATUS_NOT_PRESENT 0x01
#define LPFC_SLI_EVENT_STATUS_WRONG_TYPE 0x02
#define LPFC_SLI_EVENT_STATUS_UNSUPPORTED 0x03
+#define LPFC_SLI_EVENT_STATUS_UNQUALIFIED 0x04
+#define LPFC_SLI_EVENT_STATUS_UNCERTIFIED 0x05
};
struct lpfc_acqe_sli {
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index db9446c612da..a544366a367e 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -1184,8 +1184,10 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL)
- for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++)
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
lpfc_rcv_seq_check_edtov(vports[i]);
+ lpfc_fdmi_num_disc_check(vports[i]);
+ }
lpfc_destroy_vport_work_array(phba, vports);
if ((phba->link_state == LPFC_HBA_ERROR) ||
@@ -1290,6 +1292,10 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
jiffies +
msecs_to_jiffies(1000 * LPFC_HB_MBOX_TIMEOUT));
}
+ } else {
+ mod_timer(&phba->hb_tmofunc,
+ jiffies +
+ msecs_to_jiffies(1000 * LPFC_HB_MBOX_INTERVAL));
}
}
@@ -2621,7 +2627,6 @@ void
lpfc_stop_vport_timers(struct lpfc_vport *vport)
{
del_timer_sync(&vport->els_tmofunc);
- del_timer_sync(&vport->fc_fdmitmo);
del_timer_sync(&vport->delayed_disc_tmo);
lpfc_can_disctmo(vport);
return;
@@ -3340,10 +3345,6 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
vport->fc_disctmo.function = lpfc_disc_timeout;
vport->fc_disctmo.data = (unsigned long)vport;
- init_timer(&vport->fc_fdmitmo);
- vport->fc_fdmitmo.function = lpfc_fdmi_tmo;
- vport->fc_fdmitmo.data = (unsigned long)vport;
-
init_timer(&vport->els_tmofunc);
vport->els_tmofunc.function = lpfc_els_timeout;
vport->els_tmofunc.data = (unsigned long)vport;
@@ -3709,49 +3710,6 @@ lpfc_sli4_parse_latt_type(struct lpfc_hba *phba,
}
/**
- * lpfc_sli4_parse_latt_link_speed - Parse sli4 link-attention link speed
- * @phba: pointer to lpfc hba data structure.
- * @acqe_link: pointer to the async link completion queue entry.
- *
- * This routine is to parse the SLI4 link-attention link speed and translate
- * it into the base driver's link-attention link speed coding.
- *
- * Return: Link-attention link speed in terms of base driver's coding.
- **/
-static uint8_t
-lpfc_sli4_parse_latt_link_speed(struct lpfc_hba *phba,
- struct lpfc_acqe_link *acqe_link)
-{
- uint8_t link_speed;
-
- switch (bf_get(lpfc_acqe_link_speed, acqe_link)) {
- case LPFC_ASYNC_LINK_SPEED_ZERO:
- case LPFC_ASYNC_LINK_SPEED_10MBPS:
- case LPFC_ASYNC_LINK_SPEED_100MBPS:
- link_speed = LPFC_LINK_SPEED_UNKNOWN;
- break;
- case LPFC_ASYNC_LINK_SPEED_1GBPS:
- link_speed = LPFC_LINK_SPEED_1GHZ;
- break;
- case LPFC_ASYNC_LINK_SPEED_10GBPS:
- link_speed = LPFC_LINK_SPEED_10GHZ;
- break;
- case LPFC_ASYNC_LINK_SPEED_20GBPS:
- case LPFC_ASYNC_LINK_SPEED_25GBPS:
- case LPFC_ASYNC_LINK_SPEED_40GBPS:
- link_speed = LPFC_LINK_SPEED_UNKNOWN;
- break;
- default:
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0483 Invalid link-attention link speed: x%x\n",
- bf_get(lpfc_acqe_link_speed, acqe_link));
- link_speed = LPFC_LINK_SPEED_UNKNOWN;
- break;
- }
- return link_speed;
-}
-
-/**
* lpfc_sli_port_speed_get - Get sli3 link speed code to link speed
* @phba: pointer to lpfc hba data structure.
*
@@ -3767,27 +3725,35 @@ lpfc_sli_port_speed_get(struct lpfc_hba *phba)
if (!lpfc_is_link_up(phba))
return 0;
- switch (phba->fc_linkspeed) {
- case LPFC_LINK_SPEED_1GHZ:
- link_speed = 1000;
- break;
- case LPFC_LINK_SPEED_2GHZ:
- link_speed = 2000;
- break;
- case LPFC_LINK_SPEED_4GHZ:
- link_speed = 4000;
- break;
- case LPFC_LINK_SPEED_8GHZ:
- link_speed = 8000;
- break;
- case LPFC_LINK_SPEED_10GHZ:
- link_speed = 10000;
- break;
- case LPFC_LINK_SPEED_16GHZ:
- link_speed = 16000;
- break;
- default:
- link_speed = 0;
+ if (phba->sli_rev <= LPFC_SLI_REV3) {
+ switch (phba->fc_linkspeed) {
+ case LPFC_LINK_SPEED_1GHZ:
+ link_speed = 1000;
+ break;
+ case LPFC_LINK_SPEED_2GHZ:
+ link_speed = 2000;
+ break;
+ case LPFC_LINK_SPEED_4GHZ:
+ link_speed = 4000;
+ break;
+ case LPFC_LINK_SPEED_8GHZ:
+ link_speed = 8000;
+ break;
+ case LPFC_LINK_SPEED_10GHZ:
+ link_speed = 10000;
+ break;
+ case LPFC_LINK_SPEED_16GHZ:
+ link_speed = 16000;
+ break;
+ default:
+ link_speed = 0;
+ }
+ } else {
+ if (phba->sli4_hba.link_state.logical_speed)
+ link_speed =
+ phba->sli4_hba.link_state.logical_speed;
+ else
+ link_speed = phba->sli4_hba.link_state.speed;
}
return link_speed;
}
@@ -3983,7 +3949,7 @@ lpfc_sli4_async_link_evt(struct lpfc_hba *phba,
la->eventTag = acqe_link->event_tag;
bf_set(lpfc_mbx_read_top_att_type, la, att_type);
bf_set(lpfc_mbx_read_top_link_spd, la,
- lpfc_sli4_parse_latt_link_speed(phba, acqe_link));
+ (bf_get(lpfc_acqe_link_speed, acqe_link)));
/* Fake the the following irrelvant fields */
bf_set(lpfc_mbx_read_top_topology, la, LPFC_TOPOLOGY_PT_PT);
@@ -4113,22 +4079,18 @@ lpfc_sli4_async_sli_evt(struct lpfc_hba *phba, struct lpfc_acqe_sli *acqe_sli)
char message[128];
uint8_t status;
uint8_t evt_type;
+ uint8_t operational = 0;
struct temp_event temp_event_data;
struct lpfc_acqe_misconfigured_event *misconfigured;
struct Scsi_Host *shost;
evt_type = bf_get(lpfc_trailer_type, acqe_sli);
- /* Special case Lancer */
- if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
- LPFC_SLI_INTF_IF_TYPE_2) {
- lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
- "2901 Async SLI event - Event Data1:x%08x Event Data2:"
- "x%08x SLI Event Type:%d\n",
- acqe_sli->event_data1, acqe_sli->event_data2,
- evt_type);
- return;
- }
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "2901 Async SLI event - Event Data1:x%08x Event Data2:"
+ "x%08x SLI Event Type:%d\n",
+ acqe_sli->event_data1, acqe_sli->event_data2,
+ evt_type);
port_name = phba->Port[0];
if (port_name == 0x00)
@@ -4174,29 +4136,46 @@ lpfc_sli4_async_sli_evt(struct lpfc_hba *phba, struct lpfc_acqe_sli *acqe_sli)
/* fetch the status for this port */
switch (phba->sli4_hba.lnk_info.lnk_no) {
case LPFC_LINK_NUMBER_0:
- status = bf_get(lpfc_sli_misconfigured_port0,
+ status = bf_get(lpfc_sli_misconfigured_port0_state,
+ &misconfigured->theEvent);
+ operational = bf_get(lpfc_sli_misconfigured_port0_op,
&misconfigured->theEvent);
break;
case LPFC_LINK_NUMBER_1:
- status = bf_get(lpfc_sli_misconfigured_port1,
+ status = bf_get(lpfc_sli_misconfigured_port1_state,
+ &misconfigured->theEvent);
+ operational = bf_get(lpfc_sli_misconfigured_port1_op,
&misconfigured->theEvent);
break;
case LPFC_LINK_NUMBER_2:
- status = bf_get(lpfc_sli_misconfigured_port2,
+ status = bf_get(lpfc_sli_misconfigured_port2_state,
+ &misconfigured->theEvent);
+ operational = bf_get(lpfc_sli_misconfigured_port2_op,
&misconfigured->theEvent);
break;
case LPFC_LINK_NUMBER_3:
- status = bf_get(lpfc_sli_misconfigured_port3,
+ status = bf_get(lpfc_sli_misconfigured_port3_state,
+ &misconfigured->theEvent);
+ operational = bf_get(lpfc_sli_misconfigured_port3_op,
&misconfigured->theEvent);
break;
default:
- status = ~LPFC_SLI_EVENT_STATUS_VALID;
- break;
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "3296 "
+ "LPFC_SLI_EVENT_TYPE_MISCONFIGURED "
+ "event: Invalid link %d",
+ phba->sli4_hba.lnk_info.lnk_no);
+ return;
}
+ /* Skip if optic state unchanged */
+ if (phba->sli4_hba.lnk_info.optic_state == status)
+ return;
+
switch (status) {
case LPFC_SLI_EVENT_STATUS_VALID:
- return; /* no message if the sfp is okay */
+ sprintf(message, "Physical Link is functional");
+ break;
case LPFC_SLI_EVENT_STATUS_NOT_PRESENT:
sprintf(message, "Optics faulted/incorrectly "
"installed/not installed - Reseat optics, "
@@ -4211,15 +4190,26 @@ lpfc_sli4_async_sli_evt(struct lpfc_hba *phba, struct lpfc_acqe_sli *acqe_sli)
sprintf(message, "Incompatible optics - Replace with "
"compatible optics for card to function.");
break;
+ case LPFC_SLI_EVENT_STATUS_UNQUALIFIED:
+ sprintf(message, "Unqualified optics - Replace with "
+ "Avago optics for Warranty and Technical "
+ "Support - Link is%s operational",
+ (operational) ? "" : " not");
+ break;
+ case LPFC_SLI_EVENT_STATUS_UNCERTIFIED:
+ sprintf(message, "Uncertified optics - Replace with "
+ "Avago-certified optics to enable link "
+ "operation - Link is%s operational",
+ (operational) ? "" : " not");
+ break;
default:
/* firmware is reporting a status we don't know about */
sprintf(message, "Unknown event status x%02x", status);
break;
}
-
+ phba->sli4_hba.lnk_info.optic_state = status;
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "3176 Misconfigured Physical Port - "
- "Port Name %c %s\n", port_name, message);
+ "3176 Port Name %c %s\n", port_name, message);
break;
case LPFC_SLI_EVENT_TYPE_REMOTE_DPORT:
lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
@@ -5293,6 +5283,9 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
INIT_LIST_HEAD(&phba->sli4_hba.lpfc_vfi_blk_list);
INIT_LIST_HEAD(&phba->lpfc_vpi_blk_list);
+ /* initialize optic_state to 0xFF */
+ phba->sli4_hba.lnk_info.optic_state = 0xff;
+
/* Initialize the driver internal SLI layer lists. */
lpfc_sli_setup(phba);
lpfc_sli_queue_setup(phba);
@@ -6159,6 +6152,20 @@ lpfc_create_shost(struct lpfc_hba *phba)
/* Put reference to SCSI host to driver's device private data */
pci_set_drvdata(phba->pcidev, shost);
+ /*
+ * At this point we are fully registered with PSA. In addition,
+ * any initial discovery should be completed.
+ */
+ vport->load_flag |= FC_ALLOW_FDMI;
+ if (phba->cfg_fdmi_on > LPFC_FDMI_NO_SUPPORT) {
+
+ /* Setup appropriate attribute masks */
+ vport->fdmi_hba_mask = LPFC_FDMI2_HBA_ATTR;
+ if (phba->cfg_fdmi_on == LPFC_FDMI_SMART_SAN)
+ vport->fdmi_port_mask = LPFC_FDMI2_SMART_ATTR;
+ else
+ vport->fdmi_port_mask = LPFC_FDMI2_PORT_ATTR;
+ }
return 0;
}
@@ -8833,9 +8840,12 @@ found:
* already mapped to this phys_id.
*/
if (cpup->irq != LPFC_VECTOR_MAP_EMPTY) {
- chann[saved_chann] =
- cpup->channel_id;
- saved_chann++;
+ if (saved_chann <=
+ LPFC_FCP_IO_CHAN_MAX) {
+ chann[saved_chann] =
+ cpup->channel_id;
+ saved_chann++;
+ }
goto out;
}
diff --git a/drivers/scsi/lpfc/lpfc_mem.c b/drivers/scsi/lpfc/lpfc_mem.c
index 3fa65338d3f5..4fb3581d4614 100644
--- a/drivers/scsi/lpfc/lpfc_mem.c
+++ b/drivers/scsi/lpfc/lpfc_mem.c
@@ -231,15 +231,13 @@ lpfc_mem_free(struct lpfc_hba *phba)
if (phba->lpfc_hbq_pool)
pci_pool_destroy(phba->lpfc_hbq_pool);
phba->lpfc_hbq_pool = NULL;
-
- if (phba->rrq_pool)
- mempool_destroy(phba->rrq_pool);
+ mempool_destroy(phba->rrq_pool);
phba->rrq_pool = NULL;
/* Free NLP memory pool */
mempool_destroy(phba->nlp_mem_pool);
phba->nlp_mem_pool = NULL;
- if (phba->sli_rev == LPFC_SLI_REV4 && phba->active_rrq_pool) {
+ if (phba->sli_rev == LPFC_SLI_REV4) {
mempool_destroy(phba->active_rrq_pool);
phba->active_rrq_pool = NULL;
}
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
index ed9a2c80c4aa..193733e8c823 100644
--- a/drivers/scsi/lpfc/lpfc_nportdisc.c
+++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
@@ -280,38 +280,12 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
uint32_t *lp;
IOCB_t *icmd;
struct serv_parm *sp;
+ uint32_t ed_tov;
LPFC_MBOXQ_t *mbox;
struct ls_rjt stat;
int rc;
memset(&stat, 0, sizeof (struct ls_rjt));
- if (vport->port_state <= LPFC_FDISC) {
- /* Before responding to PLOGI, check for pt2pt mode.
- * If we are pt2pt, with an outstanding FLOGI, abort
- * the FLOGI and resend it first.
- */
- if (vport->fc_flag & FC_PT2PT) {
- lpfc_els_abort_flogi(phba);
- if (!(vport->fc_flag & FC_PT2PT_PLOGI)) {
- /* If the other side is supposed to initiate
- * the PLOGI anyway, just ACC it now and
- * move on with discovery.
- */
- phba->fc_edtov = FF_DEF_EDTOV;
- phba->fc_ratov = FF_DEF_RATOV;
- /* Start discovery - this should just do
- CLEAR_LA */
- lpfc_disc_start(vport);
- } else
- lpfc_initial_flogi(vport);
- } else {
- stat.un.b.lsRjtRsnCode = LSRJT_LOGICAL_BSY;
- stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE;
- lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb,
- ndlp, NULL);
- return 0;
- }
- }
pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
lp = (uint32_t *) pcmd->virt;
sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t));
@@ -404,30 +378,46 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
/* Check for Nport to NPort pt2pt protocol */
if ((vport->fc_flag & FC_PT2PT) &&
!(vport->fc_flag & FC_PT2PT_PLOGI)) {
-
/* rcv'ed PLOGI decides what our NPortId will be */
vport->fc_myDID = icmd->un.rcvels.parmRo;
- mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
- if (mbox == NULL)
- goto out;
- lpfc_config_link(phba, mbox);
- mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
- mbox->vport = vport;
- rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
- if (rc == MBX_NOT_FINISHED) {
- mempool_free(mbox, phba->mbox_mem_pool);
- goto out;
+
+ ed_tov = be32_to_cpu(sp->cmn.e_d_tov);
+ if (sp->cmn.edtovResolution) {
+ /* E_D_TOV ticks are in nanoseconds */
+ ed_tov = (phba->fc_edtov + 999999) / 1000000;
}
+
/*
- * For SLI4, the VFI/VPI are registered AFTER the
- * Nport with the higher WWPN sends us a PLOGI with
- * our assigned NPortId.
+ * For pt-to-pt, use the larger EDTOV
+ * RATOV = 2 * EDTOV
*/
+ if (ed_tov > phba->fc_edtov)
+ phba->fc_edtov = ed_tov;
+ phba->fc_ratov = (2 * phba->fc_edtov) / 1000;
+
+ memcpy(&phba->fc_fabparam, sp, sizeof(struct serv_parm));
+
+ /* Issue config_link / reg_vfi to account for updated TOV's */
+
if (phba->sli_rev == LPFC_SLI_REV4)
lpfc_issue_reg_vfi(vport);
+ else {
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (mbox == NULL)
+ goto out;
+ lpfc_config_link(phba, mbox);
+ mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ mbox->vport = vport;
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ mempool_free(mbox, phba->mbox_mem_pool);
+ goto out;
+ }
+ }
lpfc_can_disctmo(vport);
}
+
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox)
goto out;
@@ -1038,7 +1028,9 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport,
uint32_t *lp;
IOCB_t *irsp;
struct serv_parm *sp;
+ uint32_t ed_tov;
LPFC_MBOXQ_t *mbox;
+ int rc;
cmdiocb = (struct lpfc_iocbq *) arg;
rspiocb = cmdiocb->context_un.rsp_iocb;
@@ -1094,18 +1086,63 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_vport *vport,
ndlp->nlp_maxframe =
((sp->cmn.bbRcvSizeMsb & 0x0F) << 8) | sp->cmn.bbRcvSizeLsb;
+ if ((vport->fc_flag & FC_PT2PT) &&
+ (vport->fc_flag & FC_PT2PT_PLOGI)) {
+ ed_tov = be32_to_cpu(sp->cmn.e_d_tov);
+ if (sp->cmn.edtovResolution) {
+ /* E_D_TOV ticks are in nanoseconds */
+ ed_tov = (phba->fc_edtov + 999999) / 1000000;
+ }
+
+ /*
+ * Use the larger EDTOV
+ * RATOV = 2 * EDTOV for pt-to-pt
+ */
+ if (ed_tov > phba->fc_edtov)
+ phba->fc_edtov = ed_tov;
+ phba->fc_ratov = (2 * phba->fc_edtov) / 1000;
+
+ memcpy(&phba->fc_fabparam, sp, sizeof(struct serv_parm));
+
+ /* Issue config_link / reg_vfi to account for updated TOV's */
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ lpfc_issue_reg_vfi(vport);
+ } else {
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+ "0133 PLOGI: no memory "
+ "for config_link "
+ "Data: x%x x%x x%x x%x\n",
+ ndlp->nlp_DID, ndlp->nlp_state,
+ ndlp->nlp_flag, ndlp->nlp_rpi);
+ goto out;
+ }
+
+ lpfc_config_link(phba, mbox);
+
+ mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ mbox->vport = vport;
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ mempool_free(mbox, phba->mbox_mem_pool);
+ goto out;
+ }
+ }
+ }
+
+ lpfc_unreg_rpi(vport, ndlp);
+
mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mbox) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "0133 PLOGI: no memory for reg_login "
- "Data: x%x x%x x%x x%x\n",
- ndlp->nlp_DID, ndlp->nlp_state,
- ndlp->nlp_flag, ndlp->nlp_rpi);
+ "0018 PLOGI: no memory for reg_login "
+ "Data: x%x x%x x%x x%x\n",
+ ndlp->nlp_DID, ndlp->nlp_state,
+ ndlp->nlp_flag, ndlp->nlp_rpi);
goto out;
}
- lpfc_unreg_rpi(vport, ndlp);
-
if (lpfc_reg_rpi(phba, vport->vpi, irsp->un.elsreq64.remoteID,
(uint8_t *) sp, mbox, ndlp->nlp_rpi) == 0) {
switch (ndlp->nlp_DID) {
@@ -2299,6 +2336,9 @@ lpfc_cmpl_reglogin_npr_node(struct lpfc_vport *vport,
if (vport->phba->sli_rev < LPFC_SLI_REV4)
ndlp->nlp_rpi = mb->un.varWords[0];
ndlp->nlp_flag |= NLP_RPI_REGISTERED;
+ if (ndlp->nlp_flag & NLP_LOGO_ACC) {
+ lpfc_unreg_rpi(vport, ndlp);
+ }
} else {
if (ndlp->nlp_flag & NLP_NODEV_REMOVE) {
lpfc_drop_node(vport, ndlp);
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index 4679ed4444a7..152b3c8a5428 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -3676,6 +3676,7 @@ static void
lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
struct lpfc_iocbq *rsp_iocb)
{
+ struct lpfc_hba *phba = vport->phba;
struct scsi_cmnd *cmnd = lpfc_cmd->pCmd;
struct fcp_cmnd *fcpcmd = lpfc_cmd->fcp_cmnd;
struct fcp_rsp *fcprsp = lpfc_cmd->fcp_rsp;
@@ -3685,6 +3686,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
uint32_t *lp;
uint32_t host_status = DID_OK;
uint32_t rsplen = 0;
+ uint32_t fcpDl;
uint32_t logit = LOG_FCP | LOG_FCP_ERROR;
@@ -3755,13 +3757,14 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
fcprsp->rspInfo3);
scsi_set_resid(cmnd, 0);
+ fcpDl = be32_to_cpu(fcpcmd->fcpDl);
if (resp_info & RESID_UNDER) {
scsi_set_resid(cmnd, be32_to_cpu(fcprsp->rspResId));
lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP_UNDER,
"9025 FCP Read Underrun, expected %d, "
"residual %d Data: x%x x%x x%x\n",
- be32_to_cpu(fcpcmd->fcpDl),
+ fcpDl,
scsi_get_resid(cmnd), fcpi_parm, cmnd->cmnd[0],
cmnd->underflow);
@@ -3777,7 +3780,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
LOG_FCP | LOG_FCP_ERROR,
"9026 FCP Read Check Error "
"and Underrun Data: x%x x%x x%x x%x\n",
- be32_to_cpu(fcpcmd->fcpDl),
+ fcpDl,
scsi_get_resid(cmnd), fcpi_parm,
cmnd->cmnd[0]);
scsi_set_resid(cmnd, scsi_bufflen(cmnd));
@@ -3812,13 +3815,25 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
* Check SLI validation that all the transfer was actually done
* (fcpi_parm should be zero). Apply check only to reads.
*/
- } else if (fcpi_parm && (cmnd->sc_data_direction == DMA_FROM_DEVICE)) {
+ } else if (fcpi_parm) {
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP | LOG_FCP_ERROR,
- "9029 FCP Read Check Error Data: "
+ "9029 FCP %s Check Error xri x%x Data: "
"x%x x%x x%x x%x x%x\n",
- be32_to_cpu(fcpcmd->fcpDl),
- be32_to_cpu(fcprsp->rspResId),
+ ((cmnd->sc_data_direction == DMA_FROM_DEVICE) ?
+ "Read" : "Write"),
+ ((phba->sli_rev == LPFC_SLI_REV4) ?
+ lpfc_cmd->cur_iocbq.sli4_xritag :
+ rsp_iocb->iocb.ulpContext),
+ fcpDl, be32_to_cpu(fcprsp->rspResId),
fcpi_parm, cmnd->cmnd[0], scsi_status);
+
+ /* There is some issue with the LPe12000 that causes it
+ * to miscalculate the fcpi_parm and falsely trip this
+ * recovery logic. Detect this case and don't error when true.
+ */
+ if (fcpi_parm > fcpDl)
+ goto out;
+
switch (scsi_status) {
case SAM_STAT_GOOD:
case SAM_STAT_CHECK_CONDITION:
@@ -3908,9 +3923,9 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
uint32_t logit = LOG_FCP;
/* Sanity check on return of outstanding command */
- if (!(lpfc_cmd->pCmd))
- return;
cmd = lpfc_cmd->pCmd;
+ if (!cmd)
+ return;
shost = cmd->device->host;
lpfc_cmd->result = (pIocbOut->iocb.un.ulpWord[4] & IOERR_PARAM_MASK);
@@ -4446,15 +4461,7 @@ lpfc_info(struct Scsi_Host *host)
phba->Port);
}
len = strlen(lpfcinfobuf);
- if (phba->sli_rev <= LPFC_SLI_REV3) {
- link_speed = lpfc_sli_port_speed_get(phba);
- } else {
- if (phba->sli4_hba.link_state.logical_speed)
- link_speed =
- phba->sli4_hba.link_state.logical_speed;
- else
- link_speed = phba->sli4_hba.link_state.speed;
- }
+ link_speed = lpfc_sli_port_speed_get(phba);
if (link_speed != 0)
snprintf(lpfcinfobuf + len, 384-len,
" Logical Link Speed: %d Mbps", link_speed);
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index f9585cdd8933..92dfd6a5178c 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -14842,10 +14842,12 @@ lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf)
struct lpfc_dmabuf *h_buf;
struct hbq_dmabuf *seq_dmabuf = NULL;
struct hbq_dmabuf *temp_dmabuf = NULL;
+ uint8_t found = 0;
INIT_LIST_HEAD(&dmabuf->dbuf.list);
dmabuf->time_stamp = jiffies;
new_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
+
/* Use the hdr_buf to find the sequence that this frame belongs to */
list_for_each_entry(h_buf, &vport->rcv_buffer_list, list) {
temp_hdr = (struct fc_frame_header *)h_buf->virt;
@@ -14885,7 +14887,8 @@ lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf)
return seq_dmabuf;
}
/* find the correct place in the sequence to insert this frame */
- list_for_each_entry_reverse(d_buf, &seq_dmabuf->dbuf.list, list) {
+ d_buf = list_entry(seq_dmabuf->dbuf.list.prev, typeof(*d_buf), list);
+ while (!found) {
temp_dmabuf = container_of(d_buf, struct hbq_dmabuf, dbuf);
temp_hdr = (struct fc_frame_header *)temp_dmabuf->hbuf.virt;
/*
@@ -14895,9 +14898,17 @@ lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf)
if (be16_to_cpu(new_hdr->fh_seq_cnt) >
be16_to_cpu(temp_hdr->fh_seq_cnt)) {
list_add(&dmabuf->dbuf.list, &temp_dmabuf->dbuf.list);
- return seq_dmabuf;
+ found = 1;
+ break;
}
+
+ if (&d_buf->list == &seq_dmabuf->dbuf.list)
+ break;
+ d_buf = list_entry(d_buf->list.prev, typeof(*d_buf), list);
}
+
+ if (found)
+ return seq_dmabuf;
return NULL;
}
@@ -16173,7 +16184,7 @@ fail_fcf_read:
}
/**
- * lpfc_check_next_fcf_pri
+ * lpfc_check_next_fcf_pri_level
* phba pointer to the lpfc_hba struct for this port.
* This routine is called from the lpfc_sli4_fcf_rr_next_index_get
* routine when the rr_bmask is empty. The FCF indecies are put into the
@@ -16329,8 +16340,12 @@ next_priority:
if (next_fcf_index < LPFC_SLI4_FCF_TBL_INDX_MAX &&
phba->fcf.fcf_pri[next_fcf_index].fcf_rec.flag &
- LPFC_FCF_FLOGI_FAILED)
+ LPFC_FCF_FLOGI_FAILED) {
+ if (list_is_singular(&phba->fcf.fcf_pri_list))
+ return LPFC_FCOE_FCF_NEXT_NONE;
+
goto next_priority;
+ }
lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
"2845 Get next roundrobin failover FCF (x%x)\n",
diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h
index 1e916e16ce98..cd780c29495a 100644
--- a/drivers/scsi/lpfc/lpfc_sli4.h
+++ b/drivers/scsi/lpfc/lpfc_sli4.h
@@ -442,6 +442,7 @@ struct lpfc_sli4_lnk_info {
#define LPFC_LNK_GE 0x0 /* FCoE */
#define LPFC_LNK_FC 0x1 /* FC */
uint8_t lnk_no;
+ uint8_t optic_state;
};
#define LPFC_SLI4_HANDLER_CNT (LPFC_FCP_IO_CHAN_MAX+ \
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index ea53aa664759..4dc22562aaf1 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -18,7 +18,7 @@
* included with this package. *
*******************************************************************/
-#define LPFC_DRIVER_VERSION "11.0.0.0."
+#define LPFC_DRIVER_VERSION "11.0.0.10."
#define LPFC_DRIVER_NAME "lpfc"
/* Used for SLI 2/3 */
diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c
index 769012663a8f..b3f85def18cc 100644
--- a/drivers/scsi/lpfc/lpfc_vport.c
+++ b/drivers/scsi/lpfc/lpfc_vport.c
@@ -393,6 +393,14 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
*(struct lpfc_vport **)fc_vport->dd_data = vport;
vport->fc_vport = fc_vport;
+ /* At this point we are fully registered with SCSI Layer. */
+ vport->load_flag |= FC_ALLOW_FDMI;
+ if (phba->cfg_fdmi_on > LPFC_FDMI_NO_SUPPORT) {
+ /* Setup appropriate attribute masks */
+ vport->fdmi_hba_mask = phba->pport->fdmi_hba_mask;
+ vport->fdmi_port_mask = phba->pport->fdmi_port_mask;
+ }
+
/*
* In SLI4, the vpi must be activated before it can be used
* by the port.
diff --git a/drivers/scsi/mpt3sas/Kconfig b/drivers/scsi/mpt3sas/Kconfig
index 29061467cc17..b736dbc80485 100644
--- a/drivers/scsi/mpt3sas/Kconfig
+++ b/drivers/scsi/mpt3sas/Kconfig
@@ -71,3 +71,12 @@ config SCSI_MPT3SAS_MAX_SGE
MAX_PHYS_SEGMENTS in most kernels. However in SuSE kernels this
can be 256. However, it may decreased down to 16. Decreasing this
parameter will reduce memory requirements on a per controller instance.
+
+config SCSI_MPT2SAS
+ tristate "Legacy MPT2SAS config option"
+ default n
+ select SCSI_MPT3SAS
+ depends on PCI && SCSI
+ ---help---
+ Dummy config option for backwards compatiblity: configure the MPT3SAS
+ driver instead.
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c
index 11393ebf1a68..83658acddd58 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_base.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.c
@@ -2020,8 +2020,10 @@ mpt3sas_base_unmap_resources(struct MPT3SAS_ADAPTER *ioc)
_base_free_irq(ioc);
_base_disable_msix(ioc);
- if (ioc->msix96_vector)
+ if (ioc->msix96_vector) {
kfree(ioc->replyPostRegisterIndex);
+ ioc->replyPostRegisterIndex = NULL;
+ }
if (ioc->chip_phys) {
iounmap(ioc->chip);
diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
index d95206b7e116..9ab77b06434d 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c
@@ -3905,8 +3905,7 @@ scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd)
* We do not expose raid functionality to upper layer for warpdrive.
*/
if (!ioc->is_warpdrive && !scsih_is_raid(&scmd->device->sdev_gendev)
- && (sas_device_priv_data->flags & MPT_DEVICE_TLR_ON) &&
- scmd->cmd_len != 32)
+ && sas_is_tlr_enabled(scmd->device) && scmd->cmd_len != 32)
mpi_control |= MPI2_SCSIIO_CONTROL_TLR_ON;
smid = mpt3sas_base_get_smid_scsiio(ioc, ioc->scsi_io_cb_idx, scmd);
diff --git a/drivers/scsi/mvsas/mv_94xx.c b/drivers/scsi/mvsas/mv_94xx.c
index 9270d15ff1a4..f6fc4a705924 100644
--- a/drivers/scsi/mvsas/mv_94xx.c
+++ b/drivers/scsi/mvsas/mv_94xx.c
@@ -330,6 +330,51 @@ static void mvs_94xx_phy_enable(struct mvs_info *mvi, u32 phy_id)
mvs_write_port_vsr_data(mvi, phy_id, tmp & 0xfd7fffff);
}
+static void mvs_94xx_sgpio_init(struct mvs_info *mvi)
+{
+ void __iomem *regs = mvi->regs_ex - 0x10200;
+ u32 tmp;
+
+ tmp = mr32(MVS_HST_CHIP_CONFIG);
+ tmp |= 0x100;
+ mw32(MVS_HST_CHIP_CONFIG, tmp);
+
+ mw32(MVS_SGPIO_CTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
+ MVS_SGPIO_CTRL_SDOUT_AUTO << MVS_SGPIO_CTRL_SDOUT_SHIFT);
+
+ mw32(MVS_SGPIO_CFG1 + MVS_SGPIO_HOST_OFFSET * mvi->id,
+ 8 << MVS_SGPIO_CFG1_LOWA_SHIFT |
+ 8 << MVS_SGPIO_CFG1_HIA_SHIFT |
+ 4 << MVS_SGPIO_CFG1_LOWB_SHIFT |
+ 4 << MVS_SGPIO_CFG1_HIB_SHIFT |
+ 2 << MVS_SGPIO_CFG1_MAXACTON_SHIFT |
+ 1 << MVS_SGPIO_CFG1_FORCEACTOFF_SHIFT
+ );
+
+ mw32(MVS_SGPIO_CFG2 + MVS_SGPIO_HOST_OFFSET * mvi->id,
+ (300000 / 100) << MVS_SGPIO_CFG2_CLK_SHIFT | /* 100kHz clock */
+ 66 << MVS_SGPIO_CFG2_BLINK_SHIFT /* (66 * 0,121 Hz?)*/
+ );
+
+ mw32(MVS_SGPIO_CFG0 + MVS_SGPIO_HOST_OFFSET * mvi->id,
+ MVS_SGPIO_CFG0_ENABLE |
+ MVS_SGPIO_CFG0_BLINKA |
+ MVS_SGPIO_CFG0_BLINKB |
+ /* 3*4 data bits / PDU */
+ (12 - 1) << MVS_SGPIO_CFG0_AUT_BITLEN_SHIFT
+ );
+
+ mw32(MVS_SGPIO_DCTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
+ DEFAULT_SGPIO_BITS);
+
+ mw32(MVS_SGPIO_DSRC + MVS_SGPIO_HOST_OFFSET * mvi->id,
+ ((mvi->id * 4) + 3) << (8 * 3) |
+ ((mvi->id * 4) + 2) << (8 * 2) |
+ ((mvi->id * 4) + 1) << (8 * 1) |
+ ((mvi->id * 4) + 0) << (8 * 0));
+
+}
+
static int mvs_94xx_init(struct mvs_info *mvi)
{
void __iomem *regs = mvi->regs;
@@ -533,6 +578,8 @@ static int mvs_94xx_init(struct mvs_info *mvi)
/* Enable SRS interrupt */
mw32(MVS_INT_MASK_SRS_0, 0xFFFF);
+ mvs_94xx_sgpio_init(mvi);
+
return 0;
}
@@ -1005,6 +1052,92 @@ static void mvs_94xx_tune_interrupt(struct mvs_info *mvi, u32 time)
}
+static int mvs_94xx_gpio_write(struct mvs_prv_info *mvs_prv,
+ u8 reg_type, u8 reg_index,
+ u8 reg_count, u8 *write_data)
+{
+ int i;
+
+ switch (reg_type) {
+
+ case SAS_GPIO_REG_TX_GP:
+ if (reg_index == 0)
+ return -EINVAL;
+
+ if (reg_count > 1)
+ return -EINVAL;
+
+ if (reg_count == 0)
+ return 0;
+
+ /* maximum supported bits = hosts * 4 drives * 3 bits */
+ for (i = 0; i < mvs_prv->n_host * 4 * 3; i++) {
+
+ /* select host */
+ struct mvs_info *mvi = mvs_prv->mvi[i/(4*3)];
+
+ void __iomem *regs = mvi->regs_ex - 0x10200;
+
+ int drive = (i/3) & (4-1); /* drive number on host */
+ u32 block = mr32(MVS_SGPIO_DCTRL +
+ MVS_SGPIO_HOST_OFFSET * mvi->id);
+
+
+ /*
+ * if bit is set then create a mask with the first
+ * bit of the drive set in the mask ...
+ */
+ u32 bit = (write_data[i/8] & (1 << (i&(8-1)))) ?
+ 1<<(24-drive*8) : 0;
+
+ /*
+ * ... and then shift it to the right position based
+ * on the led type (activity/id/fail)
+ */
+ switch (i%3) {
+ case 0: /* activity */
+ block &= ~((0x7 << MVS_SGPIO_DCTRL_ACT_SHIFT)
+ << (24-drive*8));
+ /* hardwire activity bit to SOF */
+ block |= LED_BLINKA_SOF << (
+ MVS_SGPIO_DCTRL_ACT_SHIFT +
+ (24-drive*8));
+ break;
+ case 1: /* id */
+ block &= ~((0x3 << MVS_SGPIO_DCTRL_LOC_SHIFT)
+ << (24-drive*8));
+ block |= bit << MVS_SGPIO_DCTRL_LOC_SHIFT;
+ break;
+ case 2: /* fail */
+ block &= ~((0x7 << MVS_SGPIO_DCTRL_ERR_SHIFT)
+ << (24-drive*8));
+ block |= bit << MVS_SGPIO_DCTRL_ERR_SHIFT;
+ break;
+ }
+
+ mw32(MVS_SGPIO_DCTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
+ block);
+
+ }
+
+ return reg_count;
+
+ case SAS_GPIO_REG_TX:
+ if (reg_index + reg_count > mvs_prv->n_host)
+ return -EINVAL;
+
+ for (i = 0; i < reg_count; i++) {
+ struct mvs_info *mvi = mvs_prv->mvi[i+reg_index];
+ void __iomem *regs = mvi->regs_ex - 0x10200;
+
+ mw32(MVS_SGPIO_DCTRL + MVS_SGPIO_HOST_OFFSET * mvi->id,
+ be32_to_cpu(((u32 *) write_data)[i]));
+ }
+ return reg_count;
+ }
+ return -ENOSYS;
+}
+
const struct mvs_dispatch mvs_94xx_dispatch = {
"mv94xx",
mvs_94xx_init,
@@ -1057,5 +1190,6 @@ const struct mvs_dispatch mvs_94xx_dispatch = {
mvs_94xx_fix_dma,
mvs_94xx_tune_interrupt,
mvs_94xx_non_spec_ncq_error,
+ mvs_94xx_gpio_write,
};
diff --git a/drivers/scsi/mvsas/mv_94xx.h b/drivers/scsi/mvsas/mv_94xx.h
index 14e197497b46..578960803a00 100644
--- a/drivers/scsi/mvsas/mv_94xx.h
+++ b/drivers/scsi/mvsas/mv_94xx.h
@@ -38,6 +38,10 @@ enum VANIR_REVISION_ID {
VANIR_C2_REV = 0xC2,
};
+enum host_registers {
+ MVS_HST_CHIP_CONFIG = 0x10104, /* chip configuration */
+};
+
enum hw_registers {
MVS_GBL_CTL = 0x04, /* global control */
MVS_GBL_INT_STAT = 0x00, /* global irq status */
@@ -239,6 +243,73 @@ struct mvs_prd {
__le32 im_len;
} __attribute__ ((packed));
+enum sgpio_registers {
+ MVS_SGPIO_HOST_OFFSET = 0x100, /* offset between hosts */
+
+ MVS_SGPIO_CFG0 = 0xc200,
+ MVS_SGPIO_CFG0_ENABLE = (1 << 0), /* enable pins */
+ MVS_SGPIO_CFG0_BLINKB = (1 << 1), /* blink generators */
+ MVS_SGPIO_CFG0_BLINKA = (1 << 2),
+ MVS_SGPIO_CFG0_INVSCLK = (1 << 3), /* invert signal? */
+ MVS_SGPIO_CFG0_INVSLOAD = (1 << 4),
+ MVS_SGPIO_CFG0_INVSDOUT = (1 << 5),
+ MVS_SGPIO_CFG0_SLOAD_FALLEDGE = (1 << 6), /* rise/fall edge? */
+ MVS_SGPIO_CFG0_SDOUT_FALLEDGE = (1 << 7),
+ MVS_SGPIO_CFG0_SDIN_RISEEDGE = (1 << 8),
+ MVS_SGPIO_CFG0_MAN_BITLEN_SHIFT = 18, /* bits/frame manual mode */
+ MVS_SGPIO_CFG0_AUT_BITLEN_SHIFT = 24, /* bits/frame auto mode */
+
+ MVS_SGPIO_CFG1 = 0xc204, /* blink timing register */
+ MVS_SGPIO_CFG1_LOWA_SHIFT = 0, /* A off time */
+ MVS_SGPIO_CFG1_HIA_SHIFT = 4, /* A on time */
+ MVS_SGPIO_CFG1_LOWB_SHIFT = 8, /* B off time */
+ MVS_SGPIO_CFG1_HIB_SHIFT = 12, /* B on time */
+ MVS_SGPIO_CFG1_MAXACTON_SHIFT = 16, /* max activity on time */
+
+ /* force activity off time */
+ MVS_SGPIO_CFG1_FORCEACTOFF_SHIFT = 20,
+ /* stretch activity on time */
+ MVS_SGPIO_CFG1_STRCHACTON_SHIFT = 24,
+ /* stretch activiity off time */
+ MVS_SGPIO_CFG1_STRCHACTOFF_SHIFT = 28,
+
+
+ MVS_SGPIO_CFG2 = 0xc208, /* clock speed register */
+ MVS_SGPIO_CFG2_CLK_SHIFT = 0,
+ MVS_SGPIO_CFG2_BLINK_SHIFT = 20,
+
+ MVS_SGPIO_CTRL = 0xc20c, /* SDOUT/SDIN mode control */
+ MVS_SGPIO_CTRL_SDOUT_AUTO = 2,
+ MVS_SGPIO_CTRL_SDOUT_SHIFT = 2,
+
+ MVS_SGPIO_DSRC = 0xc220, /* map ODn bits to drives */
+
+ MVS_SGPIO_DCTRL = 0xc238,
+ MVS_SGPIO_DCTRL_ERR_SHIFT = 0,
+ MVS_SGPIO_DCTRL_LOC_SHIFT = 3,
+ MVS_SGPIO_DCTRL_ACT_SHIFT = 5,
+};
+
+enum sgpio_led_status {
+ LED_OFF = 0,
+ LED_ON = 1,
+ LED_BLINKA = 2,
+ LED_BLINKA_INV = 3,
+ LED_BLINKA_SOF = 4,
+ LED_BLINKA_EOF = 5,
+ LED_BLINKB = 6,
+ LED_BLINKB_INV = 7,
+};
+
+#define DEFAULT_SGPIO_BITS ((LED_BLINKA_SOF << \
+ MVS_SGPIO_DCTRL_ACT_SHIFT) << (8 * 3) | \
+ (LED_BLINKA_SOF << \
+ MVS_SGPIO_DCTRL_ACT_SHIFT) << (8 * 2) | \
+ (LED_BLINKA_SOF << \
+ MVS_SGPIO_DCTRL_ACT_SHIFT) << (8 * 1) | \
+ (LED_BLINKA_SOF << \
+ MVS_SGPIO_DCTRL_ACT_SHIFT) << (8 * 0))
+
/*
* these registers are accessed through port vendor
* specific address/data registers
diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c
index 90fdf0e859e3..c7c250519c4b 100644
--- a/drivers/scsi/mvsas/mv_init.c
+++ b/drivers/scsi/mvsas/mv_init.c
@@ -84,6 +84,8 @@ static struct sas_domain_function_template mvs_transport_ops = {
.lldd_port_formed = mvs_port_formed,
.lldd_port_deformed = mvs_port_deformed,
+ .lldd_write_gpio = mvs_gpio_write,
+
};
static void mvs_phy_init(struct mvs_info *mvi, int phy_id)
@@ -758,7 +760,7 @@ mvs_store_interrupt_coalescing(struct device *cdev,
struct device_attribute *attr,
const char *buffer, size_t size)
{
- int val = 0;
+ unsigned int val = 0;
struct mvs_info *mvi = NULL;
struct Scsi_Host *shost = class_to_shost(cdev);
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
@@ -766,7 +768,7 @@ mvs_store_interrupt_coalescing(struct device *cdev,
if (buffer == NULL)
return size;
- if (sscanf(buffer, "%d", &val) != 1)
+ if (sscanf(buffer, "%u", &val) != 1)
return -EINVAL;
if (val >= 0x10000) {
diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c
index 9c780740fb82..83cd3ea2df41 100644
--- a/drivers/scsi/mvsas/mv_sas.c
+++ b/drivers/scsi/mvsas/mv_sas.c
@@ -737,8 +737,8 @@ static int mvs_task_prep(struct sas_task *task, struct mvs_info *mvi, int is_tmf
mv_dprintk("device %016llx not ready.\n",
SAS_ADDR(dev->sas_addr));
- rc = SAS_PHY_DOWN;
- return rc;
+ rc = SAS_PHY_DOWN;
+ return rc;
}
tei.port = dev->port->lldd_port;
if (tei.port && !tei.port->port_attached && !tmf) {
@@ -2105,3 +2105,16 @@ int mvs_int_rx(struct mvs_info *mvi, bool self_clear)
return 0;
}
+int mvs_gpio_write(struct sas_ha_struct *sha, u8 reg_type, u8 reg_index,
+ u8 reg_count, u8 *write_data)
+{
+ struct mvs_prv_info *mvs_prv = sha->lldd_ha;
+ struct mvs_info *mvi = mvs_prv->mvi[0];
+
+ if (MVS_CHIP_DISP->gpio_write) {
+ return MVS_CHIP_DISP->gpio_write(mvs_prv, reg_type,
+ reg_index, reg_count, write_data);
+ }
+
+ return -ENOSYS;
+}
diff --git a/drivers/scsi/mvsas/mv_sas.h b/drivers/scsi/mvsas/mv_sas.h
index dc409c04747a..f9afd4cdd4c4 100644
--- a/drivers/scsi/mvsas/mv_sas.h
+++ b/drivers/scsi/mvsas/mv_sas.h
@@ -103,6 +103,7 @@ enum dev_reset {
};
struct mvs_info;
+struct mvs_prv_info;
struct mvs_dispatch {
char *name;
@@ -172,6 +173,8 @@ struct mvs_dispatch {
int buf_len, int from, void *prd);
void (*tune_interrupt)(struct mvs_info *mvi, u32 time);
void (*non_spec_ncq_error)(struct mvs_info *mvi);
+ int (*gpio_write)(struct mvs_prv_info *mvs_prv, u8 reg_type,
+ u8 reg_index, u8 reg_count, u8 *write_data);
};
@@ -476,5 +479,7 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events);
void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st);
int mvs_int_rx(struct mvs_info *mvi, bool self_clear);
struct mvs_device *mvs_find_dev_by_reg_set(struct mvs_info *mvi, u8 reg_set);
+int mvs_gpio_write(struct sas_ha_struct *, u8 reg_type, u8 reg_index,
+ u8 reg_count, u8 *write_data);
#endif
diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c
index 0cccd6033feb..d8a2b5185f56 100644
--- a/drivers/scsi/osd/osd_initiator.c
+++ b/drivers/scsi/osd/osd_initiator.c
@@ -170,10 +170,7 @@ static int _osd_get_print_system_info(struct osd_dev *od,
/* FIXME: Where are the time utilities */
pFirst = get_attrs[a++].val_ptr;
- OSD_INFO("CLOCK [0x%02x%02x%02x%02x%02x%02x]\n",
- ((char *)pFirst)[0], ((char *)pFirst)[1],
- ((char *)pFirst)[2], ((char *)pFirst)[3],
- ((char *)pFirst)[4], ((char *)pFirst)[5]);
+ OSD_INFO("CLOCK [0x%6phN]\n", pFirst);
if (a < nelem) { /* IBM-OSD-SIM bug, Might not have it */
unsigned len = get_attrs[a].len;
diff --git a/drivers/scsi/qla2xxx/Kconfig b/drivers/scsi/qla2xxx/Kconfig
index a0f732b138e4..10aa18ba05fd 100644
--- a/drivers/scsi/qla2xxx/Kconfig
+++ b/drivers/scsi/qla2xxx/Kconfig
@@ -18,9 +18,6 @@ config SCSI_QLA_FC
2322, 6322 ql2322_fw.bin
24xx, 54xx ql2400_fw.bin
25xx ql2500_fw.bin
- 2031 ql2600_fw.bin
- 8031 ql8300_fw.bin
- 27xx ql2700_fw.bin
Upon request, the driver caches the firmware image until
the driver is unloaded.
diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c
index eb0cc5475c45..b6b4cfdd7620 100644
--- a/drivers/scsi/qla2xxx/qla_nx.c
+++ b/drivers/scsi/qla2xxx/qla_nx.c
@@ -433,7 +433,7 @@ qla82xx_pci_get_crb_addr_2M(struct qla_hw_data *ha, ulong off_in,
if (off_in < QLA82XX_PCI_CRBSPACE)
return -1;
- *off_out = (void __iomem *)(off_in - QLA82XX_PCI_CRBSPACE);
+ off_in -= QLA82XX_PCI_CRBSPACE;
/* Try direct map */
m = &crb_128M_2M_map[CRB_BLK(off_in)].sub_block[CRB_SUBBLK(off_in)];
@@ -443,6 +443,7 @@ qla82xx_pci_get_crb_addr_2M(struct qla_hw_data *ha, ulong off_in,
return 0;
}
/* Not in direct map, use crb window */
+ *off_out = (void __iomem *)off_in;
return 1;
}
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index bfa9a64c316b..6be32fdab365 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -5843,6 +5843,3 @@ MODULE_FIRMWARE(FW_FILE_ISP2300);
MODULE_FIRMWARE(FW_FILE_ISP2322);
MODULE_FIRMWARE(FW_FILE_ISP24XX);
MODULE_FIRMWARE(FW_FILE_ISP25XX);
-MODULE_FIRMWARE(FW_FILE_ISP2031);
-MODULE_FIRMWARE(FW_FILE_ISP8031);
-MODULE_FIRMWARE(FW_FILE_ISP27XX);
diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
index 3ba2e9564b9a..81af294f15a7 100644
--- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c
+++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c
@@ -902,7 +902,7 @@ static ssize_t tcm_qla2xxx_tpg_fabric_prot_type_show(struct config_item *item,
return sprintf(page, "%d\n", tpg->tpg_attrib.fabric_prot_type);
}
-CONFIGFS_ATTR_WO(tcm_qla2xxx_tpg_, enable);
+CONFIGFS_ATTR(tcm_qla2xxx_tpg_, enable);
CONFIGFS_ATTR_RO(tcm_qla2xxx_tpg_, dynamic_sessions);
CONFIGFS_ATTR(tcm_qla2xxx_tpg_, fabric_prot_type);
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index d07fb653f5dc..b1bf42b93fcc 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -782,7 +782,7 @@ void scsi_attach_vpd(struct scsi_device *sdev)
int vpd_len = SCSI_VPD_PG_LEN;
int pg80_supported = 0;
int pg83_supported = 0;
- unsigned char *vpd_buf;
+ unsigned char __rcu *vpd_buf, *orig_vpd_buf = NULL;
if (sdev->skip_vpd_pages)
return;
@@ -828,8 +828,16 @@ retry_pg80:
kfree(vpd_buf);
goto retry_pg80;
}
+ mutex_lock(&sdev->inquiry_mutex);
+ orig_vpd_buf = sdev->vpd_pg80;
sdev->vpd_pg80_len = result;
- sdev->vpd_pg80 = vpd_buf;
+ rcu_assign_pointer(sdev->vpd_pg80, vpd_buf);
+ mutex_unlock(&sdev->inquiry_mutex);
+ synchronize_rcu();
+ if (orig_vpd_buf) {
+ kfree(orig_vpd_buf);
+ orig_vpd_buf = NULL;
+ }
vpd_len = SCSI_VPD_PG_LEN;
}
@@ -849,8 +857,14 @@ retry_pg83:
kfree(vpd_buf);
goto retry_pg83;
}
+ mutex_lock(&sdev->inquiry_mutex);
+ orig_vpd_buf = sdev->vpd_pg83;
sdev->vpd_pg83_len = result;
- sdev->vpd_pg83 = vpd_buf;
+ rcu_assign_pointer(sdev->vpd_pg83, vpd_buf);
+ mutex_unlock(&sdev->inquiry_mutex);
+ synchronize_rcu();
+ if (orig_vpd_buf)
+ kfree(orig_vpd_buf);
}
}
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index dfcc45bb03b1..f3d69a98c725 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -129,7 +129,7 @@ static const char *scsi_debug_version_date = "20141022";
#define DEF_NO_LUN_0 0
#define DEF_NUM_PARTS 0
#define DEF_OPTS 0
-#define DEF_OPT_BLKS 64
+#define DEF_OPT_BLKS 1024
#define DEF_PHYSBLK_EXP 0
#define DEF_PTYPE 0
#define DEF_REMOVABLE false
@@ -465,8 +465,9 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = {
0} },
{0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* MAINT OUT */
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
- {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* VERIFY */
- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+ {0, 0x2f, 0, F_D_OUT_MAYBE | FF_DIRECT_IO, NULL, NULL, /* VERIFY(10) */
+ {10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7,
+ 0, 0, 0, 0, 0, 0} },
{1, 0x7f, 0x9, F_SA_HIGH | F_D_IN | FF_DIRECT_IO, resp_read_dt0,
vl_iarr, {32, 0xc7, 0, 0, 0, 0, 0x1f, 0x18, 0x0, 0x9, 0xfe, 0,
0xff, 0xff, 0xff, 0xff} },/* VARIABLE LENGTH, READ(32) */
@@ -477,8 +478,8 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = {
{10, 0x13, 0xff, 0xff, 0, 0, 0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0,
0} },
/* 20 */
- {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* ALLOW REMOVAL */
- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+ {0, 0x1e, 0, 0, NULL, NULL, /* ALLOW REMOVAL */
+ {6, 0, 0, 0, 0x3, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
{0, 0x1, 0, 0, resp_start_stop, NULL, /* REWIND ?? */
{6, 0x1, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
{0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* ATA_PT */
@@ -678,7 +679,7 @@ static void *fake_store(unsigned long long lba)
static struct sd_dif_tuple *dif_store(sector_t sector)
{
- sector = do_div(sector, sdebug_store_sectors);
+ sector = sector_div(sector, sdebug_store_sectors);
return dif_storep + sector;
}
@@ -2780,7 +2781,7 @@ static unsigned long lba_to_map_index(sector_t lba)
lba += scsi_debug_unmap_granularity -
scsi_debug_unmap_alignment;
}
- do_div(lba, scsi_debug_unmap_granularity);
+ sector_div(lba, scsi_debug_unmap_granularity);
return lba;
}
@@ -4139,7 +4140,7 @@ MODULE_PARM_DESC(no_lun_0, "no LU number 0 (def=0 -> have lun 0)");
MODULE_PARM_DESC(no_uld, "stop ULD (e.g. sd driver) attaching (def=0))");
MODULE_PARM_DESC(num_parts, "number of partitions(def=0)");
MODULE_PARM_DESC(num_tgts, "number of targets per host to simulate(def=1)");
-MODULE_PARM_DESC(opt_blks, "optimal transfer length in block (def=64)");
+MODULE_PARM_DESC(opt_blks, "optimal transfer length in blocks (def=1024)");
MODULE_PARM_DESC(opts, "1->noise, 2->medium_err, 4->timeout, 8->recovered_err... (def=0)");
MODULE_PARM_DESC(physblk_exp, "physical block exponent (def=0)");
MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])");
@@ -4846,10 +4847,10 @@ static int __init scsi_debug_init(void)
/* play around with geometry, don't waste too much on track 0 */
sdebug_heads = 8;
sdebug_sectors_per = 32;
- if (scsi_debug_dev_size_mb >= 16)
- sdebug_heads = 32;
- else if (scsi_debug_dev_size_mb >= 256)
+ if (scsi_debug_dev_size_mb >= 256)
sdebug_heads = 64;
+ else if (scsi_debug_dev_size_mb >= 16)
+ sdebug_heads = 32;
sdebug_cylinders_per = (unsigned long)sdebug_capacity /
(sdebug_sectors_per * sdebug_heads);
if (sdebug_cylinders_per >= 1024) {
diff --git a/drivers/scsi/scsi_dh.c b/drivers/scsi/scsi_dh.c
index e7649ed3f667..54d446c9f56e 100644
--- a/drivers/scsi/scsi_dh.c
+++ b/drivers/scsi/scsi_dh.c
@@ -153,76 +153,11 @@ static void scsi_dh_handler_detach(struct scsi_device *sdev)
module_put(sdev->handler->module);
}
-/*
- * Functions for sysfs attribute 'dh_state'
- */
-static ssize_t
-store_dh_state(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct scsi_device *sdev = to_scsi_device(dev);
- struct scsi_device_handler *scsi_dh;
- int err = -EINVAL;
-
- if (sdev->sdev_state == SDEV_CANCEL ||
- sdev->sdev_state == SDEV_DEL)
- return -ENODEV;
-
- if (!sdev->handler) {
- /*
- * Attach to a device handler
- */
- scsi_dh = scsi_dh_lookup(buf);
- if (!scsi_dh)
- return err;
- err = scsi_dh_handler_attach(sdev, scsi_dh);
- } else {
- if (!strncmp(buf, "detach", 6)) {
- /*
- * Detach from a device handler
- */
- sdev_printk(KERN_WARNING, sdev,
- "can't detach handler %s.\n",
- sdev->handler->name);
- err = -EINVAL;
- } else if (!strncmp(buf, "activate", 8)) {
- /*
- * Activate a device handler
- */
- if (sdev->handler->activate)
- err = sdev->handler->activate(sdev, NULL, NULL);
- else
- err = 0;
- }
- }
-
- return err<0?err:count;
-}
-
-static ssize_t
-show_dh_state(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct scsi_device *sdev = to_scsi_device(dev);
-
- if (!sdev->handler)
- return snprintf(buf, 20, "detached\n");
-
- return snprintf(buf, 20, "%s\n", sdev->handler->name);
-}
-
-static struct device_attribute scsi_dh_state_attr =
- __ATTR(dh_state, S_IRUGO | S_IWUSR, show_dh_state,
- store_dh_state);
-
int scsi_dh_add_device(struct scsi_device *sdev)
{
struct scsi_device_handler *devinfo = NULL;
const char *drv;
- int err;
-
- err = device_create_file(&sdev->sdev_gendev, &scsi_dh_state_attr);
- if (err)
- return err;
+ int err = 0;
drv = scsi_dh_find_driver(sdev);
if (drv)
@@ -238,11 +173,6 @@ void scsi_dh_release_device(struct scsi_device *sdev)
scsi_dh_handler_detach(sdev);
}
-void scsi_dh_remove_device(struct scsi_device *sdev)
-{
- device_remove_file(&sdev->sdev_gendev, &scsi_dh_state_attr);
-}
-
/*
* scsi_register_device_handler - register a device handler personality
* module.
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index dd8ad2a44510..fa6b2c4eb7a2 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -23,6 +23,7 @@
#include <linux/scatterlist.h>
#include <linux/blk-mq.h>
#include <linux/ratelimit.h>
+#include <asm/unaligned.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -3154,3 +3155,190 @@ void sdev_enable_disk_events(struct scsi_device *sdev)
atomic_dec(&sdev->disk_events_disable_depth);
}
EXPORT_SYMBOL(sdev_enable_disk_events);
+
+/**
+ * scsi_vpd_lun_id - return a unique device identification
+ * @sdev: SCSI device
+ * @id: buffer for the identification
+ * @id_len: length of the buffer
+ *
+ * Copies a unique device identification into @id based
+ * on the information in the VPD page 0x83 of the device.
+ * The string will be formatted as a SCSI name string.
+ *
+ * Returns the length of the identification or error on failure.
+ * If the identifier is longer than the supplied buffer the actual
+ * identifier length is returned and the buffer is not zero-padded.
+ */
+int scsi_vpd_lun_id(struct scsi_device *sdev, char *id, size_t id_len)
+{
+ u8 cur_id_type = 0xff;
+ u8 cur_id_size = 0;
+ unsigned char *d, *cur_id_str;
+ unsigned char __rcu *vpd_pg83;
+ int id_size = -EINVAL;
+
+ rcu_read_lock();
+ vpd_pg83 = rcu_dereference(sdev->vpd_pg83);
+ if (!vpd_pg83) {
+ rcu_read_unlock();
+ return -ENXIO;
+ }
+
+ /*
+ * Look for the correct descriptor.
+ * Order of preference for lun descriptor:
+ * - SCSI name string
+ * - NAA IEEE Registered Extended
+ * - EUI-64 based 16-byte
+ * - EUI-64 based 12-byte
+ * - NAA IEEE Registered
+ * - NAA IEEE Extended
+ * as longer descriptors reduce the likelyhood
+ * of identification clashes.
+ */
+
+ /* The id string must be at least 20 bytes + terminating NULL byte */
+ if (id_len < 21) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+
+ memset(id, 0, id_len);
+ d = vpd_pg83 + 4;
+ while (d < vpd_pg83 + sdev->vpd_pg83_len) {
+ /* Skip designators not referring to the LUN */
+ if ((d[1] & 0x30) != 0x00)
+ goto next_desig;
+
+ switch (d[1] & 0xf) {
+ case 0x2:
+ /* EUI-64 */
+ if (cur_id_size > d[3])
+ break;
+ /* Prefer NAA IEEE Registered Extended */
+ if (cur_id_type == 0x3 &&
+ cur_id_size == d[3])
+ break;
+ cur_id_size = d[3];
+ cur_id_str = d + 4;
+ cur_id_type = d[1] & 0xf;
+ switch (cur_id_size) {
+ case 8:
+ id_size = snprintf(id, id_len,
+ "eui.%8phN",
+ cur_id_str);
+ break;
+ case 12:
+ id_size = snprintf(id, id_len,
+ "eui.%12phN",
+ cur_id_str);
+ break;
+ case 16:
+ id_size = snprintf(id, id_len,
+ "eui.%16phN",
+ cur_id_str);
+ break;
+ default:
+ cur_id_size = 0;
+ break;
+ }
+ break;
+ case 0x3:
+ /* NAA */
+ if (cur_id_size > d[3])
+ break;
+ cur_id_size = d[3];
+ cur_id_str = d + 4;
+ cur_id_type = d[1] & 0xf;
+ switch (cur_id_size) {
+ case 8:
+ id_size = snprintf(id, id_len,
+ "naa.%8phN",
+ cur_id_str);
+ break;
+ case 16:
+ id_size = snprintf(id, id_len,
+ "naa.%16phN",
+ cur_id_str);
+ break;
+ default:
+ cur_id_size = 0;
+ break;
+ }
+ break;
+ case 0x8:
+ /* SCSI name string */
+ if (cur_id_size + 4 > d[3])
+ break;
+ /* Prefer others for truncated descriptor */
+ if (cur_id_size && d[3] > id_len)
+ break;
+ cur_id_size = id_size = d[3];
+ cur_id_str = d + 4;
+ cur_id_type = d[1] & 0xf;
+ if (cur_id_size >= id_len)
+ cur_id_size = id_len - 1;
+ memcpy(id, cur_id_str, cur_id_size);
+ /* Decrease priority for truncated descriptor */
+ if (cur_id_size != id_size)
+ cur_id_size = 6;
+ break;
+ default:
+ break;
+ }
+next_desig:
+ d += d[3] + 4;
+ }
+ rcu_read_unlock();
+
+ return id_size;
+}
+EXPORT_SYMBOL(scsi_vpd_lun_id);
+
+/*
+ * scsi_vpd_tpg_id - return a target port group identifier
+ * @sdev: SCSI device
+ *
+ * Returns the Target Port Group identifier from the information
+ * froom VPD page 0x83 of the device.
+ *
+ * Returns the identifier or error on failure.
+ */
+int scsi_vpd_tpg_id(struct scsi_device *sdev, int *rel_id)
+{
+ unsigned char *d;
+ unsigned char __rcu *vpd_pg83;
+ int group_id = -EAGAIN, rel_port = -1;
+
+ rcu_read_lock();
+ vpd_pg83 = rcu_dereference(sdev->vpd_pg83);
+ if (!vpd_pg83) {
+ rcu_read_unlock();
+ return -ENXIO;
+ }
+
+ d = sdev->vpd_pg83 + 4;
+ while (d < sdev->vpd_pg83 + sdev->vpd_pg83_len) {
+ switch (d[1] & 0xf) {
+ case 0x4:
+ /* Relative target port */
+ rel_port = get_unaligned_be16(&d[6]);
+ break;
+ case 0x5:
+ /* Target port group */
+ group_id = get_unaligned_be16(&d[6]);
+ break;
+ default:
+ break;
+ }
+ d += d[3] + 4;
+ }
+ rcu_read_unlock();
+
+ if (group_id >= 0 && rel_id && rel_port != -1)
+ *rel_id = rel_port;
+
+ return group_id;
+}
+EXPORT_SYMBOL(scsi_vpd_tpg_id);
diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c
index e4b799837948..459abe1dcc87 100644
--- a/drivers/scsi/scsi_pm.c
+++ b/drivers/scsi/scsi_pm.c
@@ -219,13 +219,13 @@ static int sdev_runtime_suspend(struct device *dev)
struct scsi_device *sdev = to_scsi_device(dev);
int err = 0;
- if (pm && pm->runtime_suspend) {
- err = blk_pre_runtime_suspend(sdev->request_queue);
- if (err)
- return err;
+ err = blk_pre_runtime_suspend(sdev->request_queue);
+ if (err)
+ return err;
+ if (pm && pm->runtime_suspend)
err = pm->runtime_suspend(dev);
- blk_post_runtime_suspend(sdev->request_queue, err);
- }
+ blk_post_runtime_suspend(sdev->request_queue, err);
+
return err;
}
@@ -248,11 +248,11 @@ static int sdev_runtime_resume(struct device *dev)
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
int err = 0;
- if (pm && pm->runtime_resume) {
- blk_pre_runtime_resume(sdev->request_queue);
+ blk_pre_runtime_resume(sdev->request_queue);
+ if (pm && pm->runtime_resume)
err = pm->runtime_resume(dev);
- blk_post_runtime_resume(sdev->request_queue, err);
- }
+ blk_post_runtime_resume(sdev->request_queue, err);
+
return err;
}
diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h
index 4d01cdb1b348..27b4d0a6a01d 100644
--- a/drivers/scsi/scsi_priv.h
+++ b/drivers/scsi/scsi_priv.h
@@ -174,12 +174,11 @@ extern struct async_domain scsi_sd_probe_domain;
#ifdef CONFIG_SCSI_DH
int scsi_dh_add_device(struct scsi_device *sdev);
void scsi_dh_release_device(struct scsi_device *sdev);
-void scsi_dh_remove_device(struct scsi_device *sdev);
#else
static inline int scsi_dh_add_device(struct scsi_device *sdev) { return 0; }
static inline void scsi_dh_release_device(struct scsi_device *sdev) { }
-static inline void scsi_dh_remove_device(struct scsi_device *sdev) { }
#endif
+static inline void scsi_dh_remove_device(struct scsi_device *sdev) { }
/*
* internal scsi timeout functions: for use by mid-layer and transport
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 83245391e956..6a820668d442 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -236,6 +236,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
INIT_LIST_HEAD(&sdev->starved_entry);
INIT_LIST_HEAD(&sdev->event_list);
spin_lock_init(&sdev->list_lock);
+ mutex_init(&sdev->inquiry_mutex);
INIT_WORK(&sdev->event_work, scsi_evt_thread);
INIT_WORK(&sdev->requeue_work, scsi_requeue_run_queue);
@@ -701,9 +702,12 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result,
* strings.
*/
if (sdev->inquiry_len < 36) {
- sdev_printk(KERN_INFO, sdev,
- "scsi scan: INQUIRY result too short (%d),"
- " using 36\n", sdev->inquiry_len);
+ if (!sdev->host->short_inquiry) {
+ shost_printk(KERN_INFO, sdev->host,
+ "scsi scan: INQUIRY result too short (%d),"
+ " using 36\n", sdev->inquiry_len);
+ sdev->host->short_inquiry = 1;
+ }
sdev->inquiry_len = 36;
}
@@ -1516,6 +1520,9 @@ EXPORT_SYMBOL(scsi_add_device);
void scsi_rescan_device(struct device *dev)
{
device_lock(dev);
+
+ scsi_attach_vpd(to_scsi_device(dev));
+
if (dev->driver && try_module_get(dev->driver->owner)) {
struct scsi_driver *drv = to_scsi_driver(dev->driver);
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 8d2312239ae0..4f18a851e2c7 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -17,6 +17,7 @@
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_dh.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_driver.h>
@@ -760,11 +761,15 @@ show_vpd_##_page(struct file *filp, struct kobject *kobj, \
{ \
struct device *dev = container_of(kobj, struct device, kobj); \
struct scsi_device *sdev = to_scsi_device(dev); \
+ int ret; \
if (!sdev->vpd_##_page) \
return -EINVAL; \
- return memory_read_from_buffer(buf, count, &off, \
- sdev->vpd_##_page, \
+ rcu_read_lock(); \
+ ret = memory_read_from_buffer(buf, count, &off, \
+ rcu_dereference(sdev->vpd_##_page), \
sdev->vpd_##_page##_len); \
+ rcu_read_unlock(); \
+ return ret; \
} \
static struct bin_attribute dev_attr_vpd_##_page = { \
.attr = {.name = __stringify(vpd_##_page), .mode = S_IRUGO }, \
@@ -901,6 +906,76 @@ static DEVICE_ATTR(queue_depth, S_IRUGO | S_IWUSR, sdev_show_queue_depth,
sdev_store_queue_depth);
static ssize_t
+sdev_show_wwid(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ ssize_t count;
+
+ count = scsi_vpd_lun_id(sdev, buf, PAGE_SIZE);
+ if (count > 0) {
+ buf[count] = '\n';
+ count++;
+ }
+ return count;
+}
+static DEVICE_ATTR(wwid, S_IRUGO, sdev_show_wwid, NULL);
+
+#ifdef CONFIG_SCSI_DH
+static ssize_t
+sdev_show_dh_state(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+
+ if (!sdev->handler)
+ return snprintf(buf, 20, "detached\n");
+
+ return snprintf(buf, 20, "%s\n", sdev->handler->name);
+}
+
+static ssize_t
+sdev_store_dh_state(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ int err = -EINVAL;
+
+ if (sdev->sdev_state == SDEV_CANCEL ||
+ sdev->sdev_state == SDEV_DEL)
+ return -ENODEV;
+
+ if (!sdev->handler) {
+ /*
+ * Attach to a device handler
+ */
+ err = scsi_dh_attach(sdev->request_queue, buf);
+ } else if (!strncmp(buf, "activate", 8)) {
+ /*
+ * Activate a device handler
+ */
+ if (sdev->handler->activate)
+ err = sdev->handler->activate(sdev, NULL, NULL);
+ else
+ err = 0;
+ } else if (!strncmp(buf, "detach", 6)) {
+ /*
+ * Detach from a device handler
+ */
+ sdev_printk(KERN_WARNING, sdev,
+ "can't detach handler %s.\n",
+ sdev->handler->name);
+ err = -EINVAL;
+ }
+
+ return err < 0 ? err : count;
+}
+
+static DEVICE_ATTR(dh_state, S_IRUGO | S_IWUSR, sdev_show_dh_state,
+ sdev_store_dh_state);
+#endif
+
+static ssize_t
sdev_show_queue_ramp_up_period(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -969,6 +1044,10 @@ static struct attribute *scsi_sdev_attrs[] = {
&dev_attr_modalias.attr,
&dev_attr_queue_depth.attr,
&dev_attr_queue_type.attr,
+ &dev_attr_wwid.attr,
+#ifdef CONFIG_SCSI_DH
+ &dev_attr_dh_state.attr,
+#endif
&dev_attr_queue_ramp_up_period.attr,
REF_EVT(media_change),
REF_EVT(inquiry_change_reported),
@@ -1058,11 +1137,12 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
}
error = scsi_dh_add_device(sdev);
- if (error) {
+ if (error)
+ /*
+ * device_handler is optional, so any error can be ignored
+ */
sdev_printk(KERN_INFO, sdev,
"failed to add device handler: %d\n", error);
- return error;
- }
device_enable_async_suspend(&sdev->sdev_dev);
error = device_add(&sdev->sdev_dev);
@@ -1102,6 +1182,14 @@ void __scsi_remove_device(struct scsi_device *sdev)
{
struct device *dev = &sdev->sdev_gendev;
+ /*
+ * This cleanup path is not reentrant and while it is impossible
+ * to get a new reference with scsi_device_get() someone can still
+ * hold a previously acquired one.
+ */
+ if (sdev->sdev_state == SDEV_DEL)
+ return;
+
if (sdev->is_visible) {
if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0)
return;
@@ -1110,7 +1198,9 @@ void __scsi_remove_device(struct scsi_device *sdev)
device_unregister(&sdev->sdev_dev);
transport_remove_device(dev);
scsi_dh_remove_device(sdev);
- }
+ device_del(dev);
+ } else
+ put_device(&sdev->sdev_dev);
/*
* Stop accepting new requests and wait until all queuecommand() and
@@ -1121,16 +1211,6 @@ void __scsi_remove_device(struct scsi_device *sdev)
blk_cleanup_queue(sdev->request_queue);
cancel_work_sync(&sdev->requeue_work);
- /*
- * Remove the device after blk_cleanup_queue() has been called such
- * a possible bdi_register() call with the same name occurs after
- * blk_cleanup_queue() has called bdi_destroy().
- */
- if (sdev->is_visible)
- device_del(dev);
- else
- put_device(&sdev->sdev_dev);
-
if (sdev->host->hostt->slave_destroy)
sdev->host->hostt->slave_destroy(sdev);
transport_destroy_device(dev);
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index 24eaaf66af71..8a8822641b26 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -2586,7 +2586,7 @@ fc_rport_final_delete(struct work_struct *work)
transport_remove_device(dev);
device_del(dev);
transport_destroy_device(dev);
- put_device(&shost->shost_gendev); /* for fc_host->rport list */
+ scsi_host_put(shost); /* for fc_host->rport list */
put_device(dev); /* for self-reference */
}
@@ -2650,7 +2650,7 @@ fc_rport_create(struct Scsi_Host *shost, int channel,
else
rport->scsi_target_id = -1;
list_add_tail(&rport->peers, &fc_host->rports);
- get_device(&shost->shost_gendev); /* for fc_host->rport list */
+ scsi_host_get(shost); /* for fc_host->rport list */
spin_unlock_irqrestore(shost->host_lock, flags);
@@ -2685,7 +2685,7 @@ delete_rport:
transport_destroy_device(dev);
spin_lock_irqsave(shost->host_lock, flags);
list_del(&rport->peers);
- put_device(&shost->shost_gendev); /* for fc_host->rport list */
+ scsi_host_put(shost); /* for fc_host->rport list */
spin_unlock_irqrestore(shost->host_lock, flags);
put_device(dev->parent);
kfree(rport);
@@ -3383,7 +3383,7 @@ fc_vport_setup(struct Scsi_Host *shost, int channel, struct device *pdev,
fc_host->npiv_vports_inuse++;
vport->number = fc_host->next_vport_number++;
list_add_tail(&vport->peers, &fc_host->vports);
- get_device(&shost->shost_gendev); /* for fc_host->vport list */
+ scsi_host_get(shost); /* for fc_host->vport list */
spin_unlock_irqrestore(shost->host_lock, flags);
@@ -3441,7 +3441,7 @@ delete_vport:
transport_destroy_device(dev);
spin_lock_irqsave(shost->host_lock, flags);
list_del(&vport->peers);
- put_device(&shost->shost_gendev); /* for fc_host->vport list */
+ scsi_host_put(shost); /* for fc_host->vport list */
fc_host->npiv_vports_inuse--;
spin_unlock_irqrestore(shost->host_lock, flags);
put_device(dev->parent);
@@ -3504,7 +3504,7 @@ fc_vport_terminate(struct fc_vport *vport)
vport->flags |= FC_VPORT_DELETED;
list_del(&vport->peers);
fc_host->npiv_vports_inuse--;
- put_device(&shost->shost_gendev); /* for fc_host->vport list */
+ scsi_host_put(shost); /* for fc_host->vport list */
}
spin_unlock_irqrestore(shost->host_lock, flags);
diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c
index 30d26e345dcc..80520e2f0fa2 100644
--- a/drivers/scsi/scsi_transport_sas.c
+++ b/drivers/scsi/scsi_transport_sas.c
@@ -341,6 +341,22 @@ static int do_sas_phy_delete(struct device *dev, void *data)
}
/**
+ * is_sas_attached - check if device is SAS attached
+ * @sdev: scsi device to check
+ *
+ * returns true if the device is SAS attached
+ */
+int is_sas_attached(struct scsi_device *sdev)
+{
+ struct Scsi_Host *shost = sdev->host;
+
+ return shost->transportt->host_attrs.ac.class ==
+ &sas_host_class.class;
+}
+EXPORT_SYMBOL(is_sas_attached);
+
+
+/**
* sas_remove_children - tear down a devices SAS data structures
* @dev: device belonging to the sas object
*
@@ -367,6 +383,20 @@ void sas_remove_host(struct Scsi_Host *shost)
EXPORT_SYMBOL(sas_remove_host);
/**
+ * sas_get_address - return the SAS address of the device
+ * @sdev: scsi device
+ *
+ * Returns the SAS address of the scsi device
+ */
+u64 sas_get_address(struct scsi_device *sdev)
+{
+ struct sas_end_device *rdev = sas_sdev_to_rdev(sdev);
+
+ return rdev->rphy.identify.sas_address;
+}
+EXPORT_SYMBOL(sas_get_address);
+
+/**
* sas_tlr_supported - checking TLR bit in vpd 0x90
* @sdev: scsi device struct
*
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 54519804c46a..4e08d1cd704d 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -638,11 +638,24 @@ static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode)
unsigned int max_blocks = 0;
q->limits.discard_zeroes_data = 0;
- q->limits.discard_alignment = sdkp->unmap_alignment *
- logical_block_size;
- q->limits.discard_granularity =
- max(sdkp->physical_block_size,
- sdkp->unmap_granularity * logical_block_size);
+
+ /*
+ * When LBPRZ is reported, discard alignment and granularity
+ * must be fixed to the logical block size. Otherwise the block
+ * layer will drop misaligned portions of the request which can
+ * lead to data corruption. If LBPRZ is not set, we honor the
+ * device preference.
+ */
+ if (sdkp->lbprz) {
+ q->limits.discard_alignment = 0;
+ q->limits.discard_granularity = 1;
+ } else {
+ q->limits.discard_alignment = sdkp->unmap_alignment *
+ logical_block_size;
+ q->limits.discard_granularity =
+ max(sdkp->physical_block_size,
+ sdkp->unmap_granularity * logical_block_size);
+ }
sdkp->provisioning_mode = mode;
@@ -2321,11 +2334,8 @@ got_data:
}
}
- if (sdkp->capacity > 0xffffffff) {
+ if (sdkp->capacity > 0xffffffff)
sdp->use_16_for_rw = 1;
- sdkp->max_xfer_blocks = SD_MAX_XFER_BLOCKS;
- } else
- sdkp->max_xfer_blocks = SD_DEF_XFER_BLOCKS;
/* Rescale capacity to 512-byte units */
if (sector_size == 4096)
@@ -2642,7 +2652,6 @@ static void sd_read_block_limits(struct scsi_disk *sdkp)
{
unsigned int sector_sz = sdkp->device->sector_size;
const int vpd_len = 64;
- u32 max_xfer_length;
unsigned char *buffer = kmalloc(vpd_len, GFP_KERNEL);
if (!buffer ||
@@ -2650,14 +2659,11 @@ static void sd_read_block_limits(struct scsi_disk *sdkp)
scsi_get_vpd_page(sdkp->device, 0xb0, buffer, vpd_len))
goto out;
- max_xfer_length = get_unaligned_be32(&buffer[8]);
- if (max_xfer_length)
- sdkp->max_xfer_blocks = max_xfer_length;
-
blk_queue_io_min(sdkp->disk->queue,
get_unaligned_be16(&buffer[6]) * sector_sz);
- blk_queue_io_opt(sdkp->disk->queue,
- get_unaligned_be32(&buffer[12]) * sector_sz);
+
+ sdkp->max_xfer_blocks = get_unaligned_be32(&buffer[8]);
+ sdkp->opt_xfer_blocks = get_unaligned_be32(&buffer[12]);
if (buffer[3] == 0x3c) {
unsigned int lba_count, desc_count;
@@ -2806,6 +2812,11 @@ static int sd_try_extended_inquiry(struct scsi_device *sdp)
return 0;
}
+static inline u32 logical_to_sectors(struct scsi_device *sdev, u32 blocks)
+{
+ return blocks << (ilog2(sdev->sector_size) - 9);
+}
+
/**
* sd_revalidate_disk - called the first time a new disk is seen,
* performs disk spin up, read_capacity, etc.
@@ -2815,8 +2826,9 @@ static int sd_revalidate_disk(struct gendisk *disk)
{
struct scsi_disk *sdkp = scsi_disk(disk);
struct scsi_device *sdp = sdkp->device;
+ struct request_queue *q = sdkp->disk->queue;
unsigned char *buffer;
- unsigned int max_xfer;
+ unsigned int dev_max, rw_max;
SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp,
"sd_revalidate_disk\n"));
@@ -2864,11 +2876,29 @@ static int sd_revalidate_disk(struct gendisk *disk)
*/
sd_set_flush_flag(sdkp);
- max_xfer = sdkp->max_xfer_blocks;
- max_xfer <<= ilog2(sdp->sector_size) - 9;
+ /* Initial block count limit based on CDB TRANSFER LENGTH field size. */
+ dev_max = sdp->use_16_for_rw ? SD_MAX_XFER_BLOCKS : SD_DEF_XFER_BLOCKS;
+
+ /* Some devices report a maximum block count for READ/WRITE requests. */
+ dev_max = min_not_zero(dev_max, sdkp->max_xfer_blocks);
+ q->limits.max_dev_sectors = logical_to_sectors(sdp, dev_max);
+
+ /*
+ * Use the device's preferred I/O size for reads and writes
+ * unless the reported value is unreasonably small, large, or
+ * garbage.
+ */
+ if (sdkp->opt_xfer_blocks &&
+ sdkp->opt_xfer_blocks <= dev_max &&
+ sdkp->opt_xfer_blocks <= SD_DEF_XFER_BLOCKS &&
+ sdkp->opt_xfer_blocks * sdp->sector_size >= PAGE_CACHE_SIZE)
+ rw_max = q->limits.io_opt =
+ logical_to_sectors(sdp, sdkp->opt_xfer_blocks);
+ else
+ rw_max = BLK_DEF_MAX_SECTORS;
- sdkp->disk->queue->limits.max_sectors =
- min_not_zero(queue_max_hw_sectors(sdkp->disk->queue), max_xfer);
+ /* Combine with controller limits */
+ q->limits.max_sectors = min(rw_max, queue_max_hw_sectors(q));
set_capacity(disk, sdkp->capacity);
sd_config_write_same(sdkp);
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h
index 63ba5ca7f9a1..5f2a84aff29f 100644
--- a/drivers/scsi/sd.h
+++ b/drivers/scsi/sd.h
@@ -67,6 +67,7 @@ struct scsi_disk {
atomic_t openers;
sector_t capacity; /* size in 512-byte sectors */
u32 max_xfer_blocks;
+ u32 opt_xfer_blocks;
u32 max_ws_blocks;
u32 max_unmap_blocks;
u32 unmap_granularity;
diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c
index dcb0d76d7312..53ef1cb6418e 100644
--- a/drivers/scsi/ses.c
+++ b/drivers/scsi/ses.c
@@ -34,6 +34,8 @@
#include <scsi/scsi_driver.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_transport_sas.h>
+
struct ses_device {
unsigned char *page1;
unsigned char *page1_types;
@@ -84,6 +86,7 @@ static void init_device_slot_control(unsigned char *dest_desc,
static int ses_recv_diag(struct scsi_device *sdev, int page_code,
void *buf, int bufflen)
{
+ int ret;
unsigned char cmd[] = {
RECEIVE_DIAGNOSTIC,
1, /* Set PCV bit */
@@ -92,9 +95,26 @@ static int ses_recv_diag(struct scsi_device *sdev, int page_code,
bufflen & 0xff,
0
};
+ unsigned char recv_page_code;
- return scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf, bufflen,
+ ret = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf, bufflen,
NULL, SES_TIMEOUT, SES_RETRIES, NULL);
+ if (unlikely(!ret))
+ return ret;
+
+ recv_page_code = ((unsigned char *)buf)[0];
+
+ if (likely(recv_page_code == page_code))
+ return ret;
+
+ /* successful diagnostic but wrong page code. This happens to some
+ * USB devices, just print a message and pretend there was an error */
+
+ sdev_printk(KERN_ERR, sdev,
+ "Wrong diagnostic page; asked for %d got %u\n",
+ page_code, recv_page_code);
+
+ return -EINVAL;
}
static int ses_send_diag(struct scsi_device *sdev, int page_code,
@@ -541,7 +561,15 @@ static void ses_enclosure_data_process(struct enclosure_device *edev,
if (desc_ptr)
desc_ptr += len;
- if (addl_desc_ptr)
+ if (addl_desc_ptr &&
+ /* only find additional descriptions for specific devices */
+ (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE ||
+ type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE ||
+ type_ptr[0] == ENCLOSURE_COMPONENT_SAS_EXPANDER ||
+ /* these elements are optional */
+ type_ptr[0] == ENCLOSURE_COMPONENT_SCSI_TARGET_PORT ||
+ type_ptr[0] == ENCLOSURE_COMPONENT_SCSI_INITIATOR_PORT ||
+ type_ptr[0] == ENCLOSURE_COMPONENT_CONTROLLER_ELECTRONICS))
addl_desc_ptr += addl_desc_ptr[1] + 2;
}
@@ -553,31 +581,15 @@ static void ses_enclosure_data_process(struct enclosure_device *edev,
static void ses_match_to_enclosure(struct enclosure_device *edev,
struct scsi_device *sdev)
{
- unsigned char *desc;
struct efd efd = {
.addr = 0,
};
ses_enclosure_data_process(edev, to_scsi_device(edev->edev.parent), 0);
- if (!sdev->vpd_pg83_len)
- return;
-
- desc = sdev->vpd_pg83 + 4;
- while (desc < sdev->vpd_pg83 + sdev->vpd_pg83_len) {
- enum scsi_protocol proto = desc[0] >> 4;
- u8 code_set = desc[0] & 0x0f;
- u8 piv = desc[1] & 0x80;
- u8 assoc = (desc[1] & 0x30) >> 4;
- u8 type = desc[1] & 0x0f;
- u8 len = desc[3];
+ if (is_sas_attached(sdev))
+ efd.addr = sas_get_address(sdev);
- if (piv && code_set == 1 && assoc == 1
- && proto == SCSI_PROTOCOL_SAS && type == 3 && len == 8)
- efd.addr = get_unaligned_be64(&desc[4]);
-
- desc += len + 4;
- }
if (efd.addr) {
efd.dev = &sdev->sdev_gendev;
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index e0a1e52a04e7..2e522951b619 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -4083,6 +4083,7 @@ static int create_one_cdev(struct scsi_tape *tape, int mode, int rew)
}
cdev->owner = THIS_MODULE;
cdev->ops = &st_fops;
+ STm->cdevs[rew] = cdev;
error = cdev_add(cdev, cdev_devno, 1);
if (error) {
@@ -4091,7 +4092,6 @@ static int create_one_cdev(struct scsi_tape *tape, int mode, int rew)
pr_err("st%d: Device not attached.\n", dev_num);
goto out_free;
}
- STm->cdevs[rew] = cdev;
i = mode << (4 - ST_NBR_MODE_BITS);
snprintf(name, 10, "%s%s%s", rew ? "n" : "",
@@ -4110,8 +4110,9 @@ static int create_one_cdev(struct scsi_tape *tape, int mode, int rew)
return 0;
out_free:
cdev_del(STm->cdevs[rew]);
- STm->cdevs[rew] = NULL;
out:
+ STm->cdevs[rew] = NULL;
+ STm->devs[rew] = NULL;
return error;
}
diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h
index b6486b5d8681..8c732c8de015 100644
--- a/drivers/scsi/st.h
+++ b/drivers/scsi/st.h
@@ -148,8 +148,6 @@ struct scsi_tape {
int tape_type;
int long_timeout; /* timeout for commands known to take long time */
- unsigned long max_pfn; /* the maximum page number reachable by the HBA */
-
/* Mode characteristics */
struct st_modedef modes[ST_NBR_MODES];
int current_mode;
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
index 3fba42ad9fb8..41c115c230d9 100644
--- a/drivers/scsi/storvsc_drv.c
+++ b/drivers/scsi/storvsc_drv.c
@@ -41,6 +41,7 @@
#include <scsi/scsi_eh.h>
#include <scsi/scsi_devinfo.h>
#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_transport_fc.h>
/*
* All wire protocol details (storage protocol between the guest and the host)
@@ -92,9 +93,8 @@ enum vstor_packet_operation {
*/
struct hv_fc_wwn_packet {
- bool primary_active;
- u8 reserved1;
- u8 reserved2;
+ u8 primary_active;
+ u8 reserved1[3];
u8 primary_port_wwn[8];
u8 primary_node_wwn[8];
u8 secondary_port_wwn[8];
@@ -164,6 +164,26 @@ static int sense_buffer_size = PRE_WIN8_STORVSC_SENSE_BUFFER_SIZE;
*/
static int vmstor_proto_version;
+#define STORVSC_LOGGING_NONE 0
+#define STORVSC_LOGGING_ERROR 1
+#define STORVSC_LOGGING_WARN 2
+
+static int logging_level = STORVSC_LOGGING_ERROR;
+module_param(logging_level, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(logging_level,
+ "Logging level, 0 - None, 1 - Error (default), 2 - Warning.");
+
+static inline bool do_logging(int level)
+{
+ return logging_level >= level;
+}
+
+#define storvsc_log(dev, level, fmt, ...) \
+do { \
+ if (do_logging(level)) \
+ dev_warn(&(dev)->device, fmt, ##__VA_ARGS__); \
+} while (0)
+
struct vmscsi_win8_extension {
/*
* The following were added in Windows 8
@@ -378,6 +398,9 @@ static int storvsc_timeout = 180;
static int msft_blist_flags = BLIST_TRY_VPD_PAGES;
+#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS)
+static struct scsi_transport_template *fc_transport_template;
+#endif
static void storvsc_on_channel_callback(void *context);
@@ -437,6 +460,11 @@ struct storvsc_device {
/* Used for vsc/vsp channel reset process */
struct storvsc_cmd_request init_request;
struct storvsc_cmd_request reset_request;
+ /*
+ * Currently active port and node names for FC devices.
+ */
+ u64 node_name;
+ u64 port_name;
};
struct hv_host_device {
@@ -676,29 +704,36 @@ static void handle_multichannel_storage(struct hv_device *device, int max_chns)
vmbus_are_subchannels_present(device->channel);
}
-static int storvsc_channel_init(struct hv_device *device)
+static void cache_wwn(struct storvsc_device *stor_device,
+ struct vstor_packet *vstor_packet)
{
- struct storvsc_device *stor_device;
- struct storvsc_cmd_request *request;
- struct vstor_packet *vstor_packet;
- int ret, t, i;
- int max_chns;
- bool process_sub_channels = false;
+ /*
+ * Cache the currently active port and node ww names.
+ */
+ if (vstor_packet->wwn_packet.primary_active) {
+ stor_device->node_name =
+ wwn_to_u64(vstor_packet->wwn_packet.primary_node_wwn);
+ stor_device->port_name =
+ wwn_to_u64(vstor_packet->wwn_packet.primary_port_wwn);
+ } else {
+ stor_device->node_name =
+ wwn_to_u64(vstor_packet->wwn_packet.secondary_node_wwn);
+ stor_device->port_name =
+ wwn_to_u64(vstor_packet->wwn_packet.secondary_port_wwn);
+ }
+}
- stor_device = get_out_stor_device(device);
- if (!stor_device)
- return -ENODEV;
- request = &stor_device->init_request;
+static int storvsc_execute_vstor_op(struct hv_device *device,
+ struct storvsc_cmd_request *request,
+ bool status_check)
+{
+ struct vstor_packet *vstor_packet;
+ int ret, t;
+
vstor_packet = &request->vstor_packet;
- /*
- * Now, initiate the vsc/vsp initialization protocol on the open
- * channel
- */
- memset(request, 0, sizeof(struct storvsc_cmd_request));
init_completion(&request->wait_event);
- vstor_packet->operation = VSTOR_OPERATION_BEGIN_INITIALIZATION;
vstor_packet->flags = REQUEST_COMPLETION_FLAG;
ret = vmbus_sendpacket(device->channel, vstor_packet,
@@ -708,27 +743,56 @@ static int storvsc_channel_init(struct hv_device *device)
VM_PKT_DATA_INBAND,
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
if (ret != 0)
- goto cleanup;
+ return ret;
t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
- if (t == 0) {
- ret = -ETIMEDOUT;
- goto cleanup;
- }
+ if (t == 0)
+ return -ETIMEDOUT;
+
+ if (!status_check)
+ return ret;
if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO ||
- vstor_packet->status != 0) {
- ret = -EINVAL;
- goto cleanup;
- }
+ vstor_packet->status != 0)
+ return -EINVAL;
+
+ return ret;
+}
+
+static int storvsc_channel_init(struct hv_device *device, bool is_fc)
+{
+ struct storvsc_device *stor_device;
+ struct storvsc_cmd_request *request;
+ struct vstor_packet *vstor_packet;
+ int ret, i;
+ int max_chns;
+ bool process_sub_channels = false;
+ stor_device = get_out_stor_device(device);
+ if (!stor_device)
+ return -ENODEV;
+
+ request = &stor_device->init_request;
+ vstor_packet = &request->vstor_packet;
+
+ /*
+ * Now, initiate the vsc/vsp initialization protocol on the open
+ * channel
+ */
+ memset(request, 0, sizeof(struct storvsc_cmd_request));
+ vstor_packet->operation = VSTOR_OPERATION_BEGIN_INITIALIZATION;
+ ret = storvsc_execute_vstor_op(device, request, true);
+ if (ret)
+ return ret;
+ /*
+ * Query host supported protocol version.
+ */
for (i = 0; i < ARRAY_SIZE(vmstor_protocols); i++) {
/* reuse the packet for version range supported */
memset(vstor_packet, 0, sizeof(struct vstor_packet));
vstor_packet->operation =
VSTOR_OPERATION_QUERY_PROTOCOL_VERSION;
- vstor_packet->flags = REQUEST_COMPLETION_FLAG;
vstor_packet->version.major_minor =
vmstor_protocols[i].protocol_version;
@@ -737,26 +801,12 @@ static int storvsc_channel_init(struct hv_device *device)
* The revision number is only used in Windows; set it to 0.
*/
vstor_packet->version.revision = 0;
-
- ret = vmbus_sendpacket(device->channel, vstor_packet,
- (sizeof(struct vstor_packet) -
- vmscsi_size_delta),
- (unsigned long)request,
- VM_PKT_DATA_INBAND,
- VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+ ret = storvsc_execute_vstor_op(device, request, false);
if (ret != 0)
- goto cleanup;
+ return ret;
- t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
- if (t == 0) {
- ret = -ETIMEDOUT;
- goto cleanup;
- }
-
- if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO) {
- ret = -EINVAL;
- goto cleanup;
- }
+ if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO)
+ return -EINVAL;
if (vstor_packet->status == 0) {
vmstor_proto_version =
@@ -772,37 +822,15 @@ static int storvsc_channel_init(struct hv_device *device)
}
}
- if (vstor_packet->status != 0) {
- ret = -EINVAL;
- goto cleanup;
- }
+ if (vstor_packet->status != 0)
+ return -EINVAL;
memset(vstor_packet, 0, sizeof(struct vstor_packet));
vstor_packet->operation = VSTOR_OPERATION_QUERY_PROPERTIES;
- vstor_packet->flags = REQUEST_COMPLETION_FLAG;
-
- ret = vmbus_sendpacket(device->channel, vstor_packet,
- (sizeof(struct vstor_packet) -
- vmscsi_size_delta),
- (unsigned long)request,
- VM_PKT_DATA_INBAND,
- VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
-
+ ret = storvsc_execute_vstor_op(device, request, true);
if (ret != 0)
- goto cleanup;
-
- t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
- if (t == 0) {
- ret = -ETIMEDOUT;
- goto cleanup;
- }
-
- if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO ||
- vstor_packet->status != 0) {
- ret = -EINVAL;
- goto cleanup;
- }
+ return ret;
/*
* Check to see if multi-channel support is there.
@@ -818,37 +846,34 @@ static int storvsc_channel_init(struct hv_device *device)
stor_device->max_transfer_bytes =
vstor_packet->storage_channel_properties.max_transfer_bytes;
- memset(vstor_packet, 0, sizeof(struct vstor_packet));
- vstor_packet->operation = VSTOR_OPERATION_END_INITIALIZATION;
- vstor_packet->flags = REQUEST_COMPLETION_FLAG;
-
- ret = vmbus_sendpacket(device->channel, vstor_packet,
- (sizeof(struct vstor_packet) -
- vmscsi_size_delta),
- (unsigned long)request,
- VM_PKT_DATA_INBAND,
- VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+ if (!is_fc)
+ goto done;
+ /*
+ * For FC devices retrieve FC HBA data.
+ */
+ memset(vstor_packet, 0, sizeof(struct vstor_packet));
+ vstor_packet->operation = VSTOR_OPERATION_FCHBA_DATA;
+ ret = storvsc_execute_vstor_op(device, request, true);
if (ret != 0)
- goto cleanup;
+ return ret;
- t = wait_for_completion_timeout(&request->wait_event, 5*HZ);
- if (t == 0) {
- ret = -ETIMEDOUT;
- goto cleanup;
- }
+ /*
+ * Cache the currently active port and node ww names.
+ */
+ cache_wwn(stor_device, vstor_packet);
- if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO ||
- vstor_packet->status != 0) {
- ret = -EINVAL;
- goto cleanup;
- }
+done:
+
+ memset(vstor_packet, 0, sizeof(struct vstor_packet));
+ vstor_packet->operation = VSTOR_OPERATION_END_INITIALIZATION;
+ ret = storvsc_execute_vstor_op(device, request, true);
+ if (ret != 0)
+ return ret;
if (process_sub_channels)
handle_multichannel_storage(device, max_chns);
-
-cleanup:
return ret;
}
@@ -920,19 +945,16 @@ static void storvsc_handle_error(struct vmscsi_request *vm_srb,
}
-static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request)
+static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request,
+ struct storvsc_device *stor_dev)
{
struct scsi_cmnd *scmnd = cmd_request->cmd;
- struct hv_host_device *host_dev = shost_priv(scmnd->device->host);
struct scsi_sense_hdr sense_hdr;
struct vmscsi_request *vm_srb;
struct Scsi_Host *host;
- struct storvsc_device *stor_dev;
- struct hv_device *dev = host_dev->dev;
u32 payload_sz = cmd_request->payload_sz;
void *payload = cmd_request->payload;
- stor_dev = get_in_stor_device(dev);
host = stor_dev->host;
vm_srb = &cmd_request->vstor_packet.vm_srb;
@@ -941,7 +963,8 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request)
if (scmnd->result) {
if (scsi_normalize_sense(scmnd->sense_buffer,
- SCSI_SENSE_BUFFERSIZE, &sense_hdr))
+ SCSI_SENSE_BUFFERSIZE, &sense_hdr) &&
+ do_logging(STORVSC_LOGGING_ERROR))
scsi_print_sense_hdr(scmnd->device, "storvsc",
&sense_hdr);
}
@@ -961,14 +984,13 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request)
kfree(payload);
}
-static void storvsc_on_io_completion(struct hv_device *device,
+static void storvsc_on_io_completion(struct storvsc_device *stor_device,
struct vstor_packet *vstor_packet,
struct storvsc_cmd_request *request)
{
- struct storvsc_device *stor_device;
struct vstor_packet *stor_pkt;
+ struct hv_device *device = stor_device->device;
- stor_device = hv_get_drvdata(device);
stor_pkt = &request->vstor_packet;
/*
@@ -995,6 +1017,13 @@ static void storvsc_on_io_completion(struct hv_device *device,
stor_pkt->vm_srb.sense_info_length =
vstor_packet->vm_srb.sense_info_length;
+ if (vstor_packet->vm_srb.scsi_status != 0 ||
+ vstor_packet->vm_srb.srb_status != SRB_STATUS_SUCCESS)
+ storvsc_log(device, STORVSC_LOGGING_WARN,
+ "cmd 0x%x scsi status 0x%x srb status 0x%x\n",
+ stor_pkt->vm_srb.cdb[0],
+ vstor_packet->vm_srb.scsi_status,
+ vstor_packet->vm_srb.srb_status);
if ((vstor_packet->vm_srb.scsi_status & 0xFF) == 0x02) {
/* CHECK_CONDITION */
@@ -1002,6 +1031,10 @@ static void storvsc_on_io_completion(struct hv_device *device,
SRB_STATUS_AUTOSENSE_VALID) {
/* autosense data available */
+ storvsc_log(device, STORVSC_LOGGING_WARN,
+ "stor pkt %p autosense data valid - len %d\n",
+ request, vstor_packet->vm_srb.sense_info_length);
+
memcpy(request->cmd->sense_buffer,
vstor_packet->vm_srb.sense_data,
vstor_packet->vm_srb.sense_info_length);
@@ -1012,7 +1045,7 @@ static void storvsc_on_io_completion(struct hv_device *device,
stor_pkt->vm_srb.data_transfer_length =
vstor_packet->vm_srb.data_transfer_length;
- storvsc_command_completion(request);
+ storvsc_command_completion(request, stor_device);
if (atomic_dec_and_test(&stor_device->num_outstanding_req) &&
stor_device->drain_notify)
@@ -1021,21 +1054,19 @@ static void storvsc_on_io_completion(struct hv_device *device,
}
-static void storvsc_on_receive(struct hv_device *device,
+static void storvsc_on_receive(struct storvsc_device *stor_device,
struct vstor_packet *vstor_packet,
struct storvsc_cmd_request *request)
{
struct storvsc_scan_work *work;
- struct storvsc_device *stor_device;
switch (vstor_packet->operation) {
case VSTOR_OPERATION_COMPLETE_IO:
- storvsc_on_io_completion(device, vstor_packet, request);
+ storvsc_on_io_completion(stor_device, vstor_packet, request);
break;
case VSTOR_OPERATION_REMOVE_DEVICE:
case VSTOR_OPERATION_ENUMERATE_BUS:
- stor_device = get_in_stor_device(device);
work = kmalloc(sizeof(struct storvsc_scan_work), GFP_ATOMIC);
if (!work)
return;
@@ -1045,6 +1076,13 @@ static void storvsc_on_receive(struct hv_device *device,
schedule_work(&work->work);
break;
+ case VSTOR_OPERATION_FCHBA_DATA:
+ cache_wwn(stor_device, vstor_packet);
+#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS)
+ fc_host_node_name(stor_device->host) = stor_device->node_name;
+ fc_host_port_name(stor_device->host) = stor_device->port_name;
+#endif
+ break;
default:
break;
}
@@ -1088,7 +1126,7 @@ static void storvsc_on_channel_callback(void *context)
vmscsi_size_delta));
complete(&request->wait_event);
} else {
- storvsc_on_receive(device,
+ storvsc_on_receive(stor_device,
(struct vstor_packet *)packet,
request);
}
@@ -1100,7 +1138,8 @@ static void storvsc_on_channel_callback(void *context)
return;
}
-static int storvsc_connect_to_vsp(struct hv_device *device, u32 ring_size)
+static int storvsc_connect_to_vsp(struct hv_device *device, u32 ring_size,
+ bool is_fc)
{
struct vmstorage_channel_properties props;
int ret;
@@ -1117,7 +1156,7 @@ static int storvsc_connect_to_vsp(struct hv_device *device, u32 ring_size)
if (ret != 0)
return ret;
- ret = storvsc_channel_init(device);
+ ret = storvsc_channel_init(device, is_fc);
return ret;
}
@@ -1542,6 +1581,7 @@ static int storvsc_probe(struct hv_device *device,
struct Scsi_Host *host;
struct hv_host_device *host_dev;
bool dev_is_ide = ((dev_id->driver_data == IDE_GUID) ? true : false);
+ bool is_fc = ((dev_id->driver_data == SFC_GUID) ? true : false);
int target = 0;
struct storvsc_device *stor_device;
int max_luns_per_target;
@@ -1599,7 +1639,7 @@ static int storvsc_probe(struct hv_device *device,
hv_set_drvdata(device, stor_device);
stor_device->port_number = host->host_no;
- ret = storvsc_connect_to_vsp(device, storvsc_ringbuffer_size);
+ ret = storvsc_connect_to_vsp(device, storvsc_ringbuffer_size, is_fc);
if (ret)
goto err_out1;
@@ -1611,6 +1651,9 @@ static int storvsc_probe(struct hv_device *device,
host->max_lun = STORVSC_FC_MAX_LUNS_PER_TARGET;
host->max_id = STORVSC_FC_MAX_TARGETS;
host->max_channel = STORVSC_FC_MAX_CHANNELS - 1;
+#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS)
+ host->transportt = fc_transport_template;
+#endif
break;
case SCSI_GUID:
@@ -1650,6 +1693,12 @@ static int storvsc_probe(struct hv_device *device,
goto err_out2;
}
}
+#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS)
+ if (host->transportt == fc_transport_template) {
+ fc_host_node_name(host) = stor_device->node_name;
+ fc_host_port_name(host) = stor_device->port_name;
+ }
+#endif
return 0;
err_out2:
@@ -1675,6 +1724,10 @@ static int storvsc_remove(struct hv_device *dev)
struct storvsc_device *stor_device = hv_get_drvdata(dev);
struct Scsi_Host *host = stor_device->host;
+#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS)
+ if (host->transportt == fc_transport_template)
+ fc_remove_host(host);
+#endif
scsi_remove_host(host);
storvsc_dev_remove(dev);
scsi_host_put(host);
@@ -1689,8 +1742,16 @@ static struct hv_driver storvsc_drv = {
.remove = storvsc_remove,
};
+#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS)
+static struct fc_function_template fc_transport_functions = {
+ .show_host_node_name = 1,
+ .show_host_port_name = 1,
+};
+#endif
+
static int __init storvsc_drv_init(void)
{
+ int ret;
/*
* Divide the ring buffer data size (which is 1 page less
@@ -1705,12 +1766,28 @@ static int __init storvsc_drv_init(void)
vmscsi_size_delta,
sizeof(u64)));
- return vmbus_driver_register(&storvsc_drv);
+#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS)
+ fc_transport_template = fc_attach_transport(&fc_transport_functions);
+ if (!fc_transport_template)
+ return -ENODEV;
+#endif
+
+ ret = vmbus_driver_register(&storvsc_drv);
+
+#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS)
+ if (ret)
+ fc_release_transport(fc_transport_template);
+#endif
+
+ return ret;
}
static void __exit storvsc_drv_exit(void)
{
vmbus_driver_unregister(&storvsc_drv);
+#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS)
+ fc_release_transport(fc_transport_template);
+#endif
}
MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index 9714f2a8b329..d2a7b127b05c 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -333,7 +333,7 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
err = ufshcd_init(hba, mmio_base, irq);
if (err) {
- dev_err(dev, "Intialization failed\n");
+ dev_err(dev, "Initialization failed\n");
goto out_disable_rpm;
}
diff --git a/drivers/scsi/vmw_pvscsi.c b/drivers/scsi/vmw_pvscsi.c
index 0f133c1817de..6164634aff18 100644
--- a/drivers/scsi/vmw_pvscsi.c
+++ b/drivers/scsi/vmw_pvscsi.c
@@ -349,9 +349,9 @@ static void pvscsi_create_sg(struct pvscsi_ctx *ctx,
* Map all data buffers for a command into PCI space and
* setup the scatter/gather list if needed.
*/
-static void pvscsi_map_buffers(struct pvscsi_adapter *adapter,
- struct pvscsi_ctx *ctx, struct scsi_cmnd *cmd,
- struct PVSCSIRingReqDesc *e)
+static int pvscsi_map_buffers(struct pvscsi_adapter *adapter,
+ struct pvscsi_ctx *ctx, struct scsi_cmnd *cmd,
+ struct PVSCSIRingReqDesc *e)
{
unsigned count;
unsigned bufflen = scsi_bufflen(cmd);
@@ -360,18 +360,30 @@ static void pvscsi_map_buffers(struct pvscsi_adapter *adapter,
e->dataLen = bufflen;
e->dataAddr = 0;
if (bufflen == 0)
- return;
+ return 0;
sg = scsi_sglist(cmd);
count = scsi_sg_count(cmd);
if (count != 0) {
int segs = scsi_dma_map(cmd);
- if (segs > 1) {
+
+ if (segs == -ENOMEM) {
+ scmd_printk(KERN_ERR, cmd,
+ "vmw_pvscsi: Failed to map cmd sglist for DMA.\n");
+ return -ENOMEM;
+ } else if (segs > 1) {
pvscsi_create_sg(ctx, sg, segs);
e->flags |= PVSCSI_FLAG_CMD_WITH_SG_LIST;
ctx->sglPA = pci_map_single(adapter->dev, ctx->sgl,
SGL_SIZE, PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(adapter->dev, ctx->sglPA)) {
+ scmd_printk(KERN_ERR, cmd,
+ "vmw_pvscsi: Failed to map ctx sglist for DMA.\n");
+ scsi_dma_unmap(cmd);
+ ctx->sglPA = 0;
+ return -ENOMEM;
+ }
e->dataAddr = ctx->sglPA;
} else
e->dataAddr = sg_dma_address(sg);
@@ -382,8 +394,15 @@ static void pvscsi_map_buffers(struct pvscsi_adapter *adapter,
*/
ctx->dataPA = pci_map_single(adapter->dev, sg, bufflen,
cmd->sc_data_direction);
+ if (pci_dma_mapping_error(adapter->dev, ctx->dataPA)) {
+ scmd_printk(KERN_ERR, cmd,
+ "vmw_pvscsi: Failed to map direct data buffer for DMA.\n");
+ return -ENOMEM;
+ }
e->dataAddr = ctx->dataPA;
}
+
+ return 0;
}
static void pvscsi_unmap_buffers(const struct pvscsi_adapter *adapter,
@@ -690,6 +709,12 @@ static int pvscsi_queue_ring(struct pvscsi_adapter *adapter,
ctx->sensePA = pci_map_single(adapter->dev, cmd->sense_buffer,
SCSI_SENSE_BUFFERSIZE,
PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(adapter->dev, ctx->sensePA)) {
+ scmd_printk(KERN_ERR, cmd,
+ "vmw_pvscsi: Failed to map sense buffer for DMA.\n");
+ ctx->sensePA = 0;
+ return -ENOMEM;
+ }
e->senseAddr = ctx->sensePA;
e->senseLen = SCSI_SENSE_BUFFERSIZE;
} else {
@@ -711,7 +736,15 @@ static int pvscsi_queue_ring(struct pvscsi_adapter *adapter,
else
e->flags = 0;
- pvscsi_map_buffers(adapter, ctx, cmd, e);
+ if (pvscsi_map_buffers(adapter, ctx, cmd, e) != 0) {
+ if (cmd->sense_buffer) {
+ pci_unmap_single(adapter->dev, ctx->sensePA,
+ SCSI_SENSE_BUFFERSIZE,
+ PCI_DMA_FROMDEVICE);
+ ctx->sensePA = 0;
+ }
+ return -ENOMEM;
+ }
e->context = pvscsi_map_context(adapter, ctx);
diff --git a/drivers/scsi/vmw_pvscsi.h b/drivers/scsi/vmw_pvscsi.h
index ee16f0c5c47d..12712c92f37a 100644
--- a/drivers/scsi/vmw_pvscsi.h
+++ b/drivers/scsi/vmw_pvscsi.h
@@ -26,7 +26,7 @@
#include <linux/types.h>
-#define PVSCSI_DRIVER_VERSION_STRING "1.0.5.0-k"
+#define PVSCSI_DRIVER_VERSION_STRING "1.0.6.0-k"
#define PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT 128
diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
index 9d5068248aa0..0a4ea809a61b 100644
--- a/drivers/soc/mediatek/Kconfig
+++ b/drivers/soc/mediatek/Kconfig
@@ -23,6 +23,7 @@ config MTK_PMIC_WRAP
config MTK_SCPSYS
bool "MediaTek SCPSYS Support"
depends on ARCH_MEDIATEK || COMPILE_TEST
+ default ARM64 && ARCH_MEDIATEK
select REGMAP
select MTK_INFRACFG
select PM_GENERIC_DOMAINS if PM
diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c
index b04b05a0904e..0ad66fa9bb1a 100644
--- a/drivers/soc/qcom/spm.c
+++ b/drivers/soc/qcom/spm.c
@@ -116,7 +116,7 @@ static const struct spm_reg_data spm_reg_8064_cpu = {
static DEFINE_PER_CPU(struct spm_driver_data *, cpu_spm_drv);
-typedef int (*idle_fn)(int);
+typedef int (*idle_fn)(void);
static DEFINE_PER_CPU(idle_fn*, qcom_idle_ops);
static inline void spm_register_write(struct spm_driver_data *drv,
@@ -179,10 +179,10 @@ static int qcom_pm_collapse(unsigned long int unused)
return -1;
}
-static int qcom_cpu_spc(int cpu)
+static int qcom_cpu_spc(void)
{
int ret;
- struct spm_driver_data *drv = per_cpu(cpu_spm_drv, cpu);
+ struct spm_driver_data *drv = __this_cpu_read(cpu_spm_drv);
spm_set_low_power_mode(drv, PM_SLEEP_MODE_SPC);
ret = cpu_suspend(0, qcom_pm_collapse);
@@ -197,9 +197,9 @@ static int qcom_cpu_spc(int cpu)
return ret;
}
-static int qcom_idle_enter(int cpu, unsigned long index)
+static int qcom_idle_enter(unsigned long index)
{
- return per_cpu(qcom_idle_ops, cpu)[index](cpu);
+ return __this_cpu_read(qcom_idle_ops)[index]();
}
static const struct of_device_id qcom_idle_state_match[] __initconst = {
diff --git a/drivers/soc/ti/knav_dma.c b/drivers/soc/ti/knav_dma.c
index bc1b80ec6afe..1a7b5caa127b 100644
--- a/drivers/soc/ti/knav_dma.c
+++ b/drivers/soc/ti/knav_dma.c
@@ -389,7 +389,7 @@ static int of_channel_match_helper(struct device_node *np, const char *name,
*dma_instance = dma_node->name;
index = of_property_match_string(np, "ti,navigator-dma-names", name);
if (index < 0) {
- dev_err(kdev->dev, "No 'ti,navigator-dma-names' propery\n");
+ dev_err(kdev->dev, "No 'ti,navigator-dma-names' property\n");
return -ENODEV;
}
diff --git a/drivers/soc/ti/knav_qmss_queue.c b/drivers/soc/ti/knav_qmss_queue.c
index f3a0b6a4b54e..8c03a80b482d 100644
--- a/drivers/soc/ti/knav_qmss_queue.c
+++ b/drivers/soc/ti/knav_qmss_queue.c
@@ -1179,7 +1179,7 @@ static int knav_queue_setup_link_ram(struct knav_device *kdev)
block++;
if (!block->size)
- return 0;
+ continue;
dev_dbg(kdev->dev, "linkram1: phys:%x, virt:%p, size:%x\n",
block->phys, block->virt, block->size);
@@ -1519,9 +1519,9 @@ static int knav_queue_load_pdsp(struct knav_device *kdev,
for (i = 0; i < ARRAY_SIZE(knav_acc_firmwares); i++) {
if (knav_acc_firmwares[i]) {
- ret = request_firmware(&fw,
- knav_acc_firmwares[i],
- kdev->dev);
+ ret = request_firmware_direct(&fw,
+ knav_acc_firmwares[i],
+ kdev->dev);
if (!ret) {
found = true;
break;
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 8b9c2a38d1cc..77064160dd76 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -585,7 +585,7 @@ config SPI_TEGRA20_SLINK
config SPI_TOPCLIFF_PCH
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) SPI"
- depends on PCI && (X86_32 || COMPILE_TEST)
+ depends on PCI && (X86_32 || MIPS || COMPILE_TEST)
help
SPI driver for the Topcliff PCH (Platform Controller Hub) SPI bus
used in some x86 embedded processors.
@@ -689,6 +689,15 @@ config SPI_SPIDEV
Note that this application programming interface is EXPERIMENTAL
and hence SUBJECT TO CHANGE WITHOUT NOTICE while it stabilizes.
+config SPI_LOOPBACK_TEST
+ tristate "spi loopback test framework support"
+ depends on m
+ help
+ This enables the SPI loopback testing framework driver
+
+ primarily used for development of spi_master drivers
+ and to detect regressions
+
config SPI_TLE62X0
tristate "Infineon TLE62X0 (for power switching)"
depends on SYSFS
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 31fb7fb2a0b6..8991ffce6e12 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -8,6 +8,7 @@ ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG
# config declarations into driver model code
obj-$(CONFIG_SPI_MASTER) += spi.o
obj-$(CONFIG_SPI_SPIDEV) += spidev.o
+obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
# SPI master controller drivers (bus)
obj-$(CONFIG_SPI_ALTERA) += spi-altera.o
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
index 06858e04ec59..fee747030ee6 100644
--- a/drivers/spi/spi-bcm63xx.c
+++ b/drivers/spi/spi-bcm63xx.c
@@ -207,6 +207,9 @@ static void bcm63xx_spi_setup_transfer(struct spi_device *spi,
u8 clk_cfg, reg;
int i;
+ /* Default to lowest clock configuration */
+ clk_cfg = SPI_CLK_0_391MHZ;
+
/* Find the closest clock configuration */
for (i = 0; i < SPI_CLK_MASK; i++) {
if (t->speed_hz >= bcm63xx_spi_freq_table[i][0]) {
@@ -215,10 +218,6 @@ static void bcm63xx_spi_setup_transfer(struct spi_device *spi,
}
}
- /* No matching configuration found, default to lowest */
- if (i == SPI_CLK_MASK)
- clk_cfg = SPI_CLK_0_391MHZ;
-
/* clear existing clock configuration bits of the register */
reg = bcm_spi_readb(bs, SPI_CLK_CFG);
reg &= ~SPI_CLK_MASK;
@@ -562,8 +561,8 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
goto out_clk_disable;
}
- dev_info(dev, "at 0x%08x (irq %d, FIFOs size %d)\n",
- r->start, irq, bs->fifo_size);
+ dev_info(dev, "at %pr (irq %d, FIFOs size %d)\n",
+ r, irq, bs->fifo_size);
return 0;
diff --git a/drivers/spi/spi-butterfly.c b/drivers/spi/spi-butterfly.c
index 9a95862986c8..22a31e4a1a11 100644
--- a/drivers/spi/spi-butterfly.c
+++ b/drivers/spi/spi-butterfly.c
@@ -27,7 +27,6 @@
#include <linux/mtd/partitions.h>
-
/*
* This uses SPI to talk with an "AVR Butterfly", which is a $US20 card
* with a battery powered AVR microcontroller and lots of goodies. You
@@ -37,7 +36,6 @@
* and use this custom parallel port cable.
*/
-
/* DATA output bits (pins 2..9 == D0..D7) */
#define butterfly_nreset (1 << 1) /* pin 3 */
@@ -52,14 +50,11 @@
/* CONTROL output bits */
#define spi_cs_bit PARPORT_CONTROL_SELECT /* pin 17 */
-
-
static inline struct butterfly *spidev_to_pp(struct spi_device *spi)
{
return spi->controller_data;
}
-
struct butterfly {
/* REVISIT ... for now, this must be first */
struct spi_bitbang bitbang;
@@ -140,7 +135,6 @@ static void butterfly_chipselect(struct spi_device *spi, int value)
parport_frob_control(pp->port, spi_cs_bit, value ? spi_cs_bit : 0);
}
-
/* we only needed to implement one mode here, and choose SPI_MODE_0 */
#define spidelay(X) do { } while (0)
@@ -149,9 +143,8 @@ static void butterfly_chipselect(struct spi_device *spi, int value)
#include "spi-bitbang-txrx.h"
static u32
-butterfly_txrx_word_mode0(struct spi_device *spi,
- unsigned nsecs,
- u32 word, u8 bits)
+butterfly_txrx_word_mode0(struct spi_device *spi, unsigned nsecs, u32 word,
+ u8 bits)
{
return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits);
}
@@ -186,7 +179,6 @@ static struct flash_platform_data flash = {
.nr_parts = ARRAY_SIZE(partitions),
};
-
/* REVISIT remove this ugly global and its "only one" limitation */
static struct butterfly *butterfly;
@@ -197,6 +189,7 @@ static void butterfly_attach(struct parport *p)
struct butterfly *pp;
struct spi_master *master;
struct device *dev = p->physport->dev;
+ struct pardev_cb butterfly_cb;
if (butterfly || !dev)
return;
@@ -229,9 +222,9 @@ static void butterfly_attach(struct parport *p)
* parport hookup
*/
pp->port = p;
- pd = parport_register_device(p, "spi_butterfly",
- NULL, NULL, NULL,
- 0 /* FLAGS */, pp);
+ memset(&butterfly_cb, 0, sizeof(butterfly_cb));
+ butterfly_cb.private = pp;
+ pd = parport_register_dev_model(p, "spi_butterfly", &butterfly_cb, 0);
if (!pd) {
status = -ENOMEM;
goto clean0;
@@ -262,7 +255,6 @@ static void butterfly_attach(struct parport *p)
parport_write_data(pp->port, pp->lastbyte);
msleep(100);
-
/*
* Start SPI ... for now, hide that we're two physical busses.
*/
@@ -283,7 +275,7 @@ static void butterfly_attach(struct parport *p)
pp->dataflash = spi_new_device(pp->bitbang.master, &pp->info[0]);
if (pp->dataflash)
pr_debug("%s: dataflash at %s\n", p->name,
- dev_name(&pp->dataflash->dev));
+ dev_name(&pp->dataflash->dev));
pr_info("%s: AVR Butterfly\n", p->name);
butterfly = pp;
@@ -297,7 +289,7 @@ clean2:
clean1:
parport_unregister_device(pd);
clean0:
- (void) spi_master_put(pp->bitbang.master);
+ spi_master_put(pp->bitbang.master);
done:
pr_debug("%s: butterfly probe, fail %d\n", p->name, status);
}
@@ -325,16 +317,16 @@ static void butterfly_detach(struct parport *p)
parport_release(pp->pd);
parport_unregister_device(pp->pd);
- (void) spi_master_put(pp->bitbang.master);
+ spi_master_put(pp->bitbang.master);
}
static struct parport_driver butterfly_driver = {
.name = "spi_butterfly",
- .attach = butterfly_attach,
+ .match_port = butterfly_attach,
.detach = butterfly_detach,
+ .devmodel = true,
};
-
static int __init butterfly_init(void)
{
return parport_register_driver(&butterfly_driver);
diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c
index 5a6749881ff9..121a4135b540 100644
--- a/drivers/spi/spi-cadence.c
+++ b/drivers/spi/spi-cadence.c
@@ -617,8 +617,7 @@ static int cdns_spi_remove(struct platform_device *pdev)
*/
static int __maybe_unused cdns_spi_suspend(struct device *dev)
{
- struct platform_device *pdev = container_of(dev,
- struct platform_device, dev);
+ struct platform_device *pdev = to_platform_device(dev);
struct spi_master *master = platform_get_drvdata(pdev);
struct cdns_spi *xspi = spi_master_get_devdata(master);
@@ -641,8 +640,7 @@ static int __maybe_unused cdns_spi_suspend(struct device *dev)
*/
static int __maybe_unused cdns_spi_resume(struct device *dev)
{
- struct platform_device *pdev = container_of(dev,
- struct platform_device, dev);
+ struct platform_device *pdev = to_platform_device(dev);
struct spi_master *master = platform_get_drvdata(pdev);
struct cdns_spi *xspi = spi_master_get_devdata(master);
int ret = 0;
diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c
index 7d3af3eacf57..fddb7a3be322 100644
--- a/drivers/spi/spi-davinci.c
+++ b/drivers/spi/spi-davinci.c
@@ -477,33 +477,33 @@ static int davinci_spi_check_error(struct davinci_spi *dspi, int int_status)
struct device *sdev = dspi->bitbang.master->dev.parent;
if (int_status & SPIFLG_TIMEOUT_MASK) {
- dev_dbg(sdev, "SPI Time-out Error\n");
+ dev_err(sdev, "SPI Time-out Error\n");
return -ETIMEDOUT;
}
if (int_status & SPIFLG_DESYNC_MASK) {
- dev_dbg(sdev, "SPI Desynchronization Error\n");
+ dev_err(sdev, "SPI Desynchronization Error\n");
return -EIO;
}
if (int_status & SPIFLG_BITERR_MASK) {
- dev_dbg(sdev, "SPI Bit error\n");
+ dev_err(sdev, "SPI Bit error\n");
return -EIO;
}
if (dspi->version == SPI_VERSION_2) {
if (int_status & SPIFLG_DLEN_ERR_MASK) {
- dev_dbg(sdev, "SPI Data Length Error\n");
+ dev_err(sdev, "SPI Data Length Error\n");
return -EIO;
}
if (int_status & SPIFLG_PARERR_MASK) {
- dev_dbg(sdev, "SPI Parity Error\n");
+ dev_err(sdev, "SPI Parity Error\n");
return -EIO;
}
if (int_status & SPIFLG_OVRRUN_MASK) {
- dev_dbg(sdev, "SPI Data Overrun error\n");
+ dev_err(sdev, "SPI Data Overrun error\n");
return -EIO;
}
if (int_status & SPIFLG_BUF_INIT_ACTIVE_MASK) {
- dev_dbg(sdev, "SPI Buffer Init Active\n");
+ dev_err(sdev, "SPI Buffer Init Active\n");
return -EBUSY;
}
}
@@ -703,7 +703,8 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
/* Wait for the transfer to complete */
if (spicfg->io_type != SPI_IO_TYPE_POLL) {
- wait_for_completion_interruptible(&(dspi->done));
+ if (wait_for_completion_timeout(&dspi->done, HZ) == 0)
+ errors = SPIFLG_TIMEOUT_MASK;
} else {
while (dspi->rcount > 0 || dspi->wcount > 0) {
errors = davinci_spi_process_events(dspi);
diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c
index bb1052e748f2..9185f6c08459 100644
--- a/drivers/spi/spi-dw-mid.c
+++ b/drivers/spi/spi-dw-mid.c
@@ -283,7 +283,7 @@ static void mid_spi_dma_stop(struct dw_spi *dws)
}
}
-static struct dw_spi_dma_ops mid_dma_ops = {
+static const struct dw_spi_dma_ops mid_dma_ops = {
.dma_init = mid_spi_dma_init,
.dma_exit = mid_spi_dma_exit,
.dma_setup = mid_spi_dma_setup,
diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c
index 882cd6618cd5..c09bb745693a 100644
--- a/drivers/spi/spi-dw.c
+++ b/drivers/spi/spi-dw.c
@@ -425,7 +425,7 @@ static int dw_spi_setup(struct spi_device *spi)
chip->type = chip_info->type;
}
- chip->tmode = 0; /* Tx & Rx */
+ chip->tmode = SPI_TMOD_TR;
if (gpio_is_valid(spi->cs_gpio)) {
ret = gpio_direction_output(spi->cs_gpio,
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index 35589a270468..61bc3cbab38d 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -130,7 +130,7 @@ struct dw_spi {
struct dma_chan *rxchan;
unsigned long dma_chan_busy;
dma_addr_t dma_addr; /* phy address of the Data register */
- struct dw_spi_dma_ops *dma_ops;
+ const struct dw_spi_dma_ops *dma_ops;
void *dma_tx;
void *dma_rx;
diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
index 59a11437db70..39412c9097c6 100644
--- a/drivers/spi/spi-fsl-dspi.c
+++ b/drivers/spi/spi-fsl-dspi.c
@@ -167,7 +167,7 @@ static inline int is_double_byte_mode(struct fsl_dspi *dspi)
{
unsigned int val;
- regmap_read(dspi->regmap, SPI_CTAR(dspi->cs), &val);
+ regmap_read(dspi->regmap, SPI_CTAR(0), &val);
return ((val & SPI_FRAME_BITS_MASK) == SPI_FRAME_BITS(8)) ? 0 : 1;
}
@@ -257,7 +257,7 @@ static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word)
return SPI_PUSHR_TXDATA(d16) |
SPI_PUSHR_PCS(dspi->cs) |
- SPI_PUSHR_CTAS(dspi->cs) |
+ SPI_PUSHR_CTAS(0) |
SPI_PUSHR_CONT;
}
@@ -290,7 +290,7 @@ static int dspi_eoq_write(struct fsl_dspi *dspi)
*/
if (tx_word && (dspi->len == 1)) {
dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM;
- regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
+ regmap_update_bits(dspi->regmap, SPI_CTAR(0),
SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8));
tx_word = 0;
}
@@ -339,7 +339,7 @@ static int dspi_tcfq_write(struct fsl_dspi *dspi)
if (tx_word && (dspi->len == 1)) {
dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM;
- regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
+ regmap_update_bits(dspi->regmap, SPI_CTAR(0),
SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8));
tx_word = 0;
}
@@ -407,7 +407,7 @@ static int dspi_transfer_one_message(struct spi_master *master,
regmap_update_bits(dspi->regmap, SPI_MCR,
SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF,
SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF);
- regmap_write(dspi->regmap, SPI_CTAR(dspi->cs),
+ regmap_write(dspi->regmap, SPI_CTAR(0),
dspi->cur_chip->ctar_val);
trans_mode = dspi->devtype_data->trans_mode;
@@ -566,7 +566,7 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id)
if (!dspi->len) {
if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) {
regmap_update_bits(dspi->regmap,
- SPI_CTAR(dspi->cs),
+ SPI_CTAR(0),
SPI_FRAME_BITS_MASK,
SPI_FRAME_BITS(16));
dspi->dataflags &= ~TRAN_STATE_WORD_ODD_NUM;
diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c
index c27124a5ec8e..7fd6a4c009d2 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -643,6 +643,11 @@ static int fsl_espi_runtime_resume(struct device *dev)
}
#endif
+static size_t fsl_espi_max_transfer_size(struct spi_device *spi)
+{
+ return SPCOM_TRANLEN_MAX;
+}
+
static struct spi_master * fsl_espi_probe(struct device *dev,
struct resource *mem, unsigned int irq)
{
@@ -670,6 +675,7 @@ static struct spi_master * fsl_espi_probe(struct device *dev,
master->cleanup = fsl_espi_cleanup;
master->transfer_one_message = fsl_espi_do_one_msg;
master->auto_runtime_pm = true;
+ master->max_transfer_size = fsl_espi_max_transfer_size;
mpc8xxx_spi = spi_master_get_devdata(master);
diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
index 0e5723ab47f0..d98c33cb64f9 100644
--- a/drivers/spi/spi-imx.c
+++ b/drivers/spi/spi-imx.c
@@ -104,9 +104,7 @@ struct spi_imx_data {
unsigned int dma_is_inited;
unsigned int dma_finished;
bool usedma;
- u32 rx_wml;
- u32 tx_wml;
- u32 rxt_wml;
+ u32 wml;
struct completion dma_rx_completion;
struct completion dma_tx_completion;
@@ -124,9 +122,14 @@ static inline int is_imx35_cspi(struct spi_imx_data *d)
return d->devtype_data->devtype == IMX35_CSPI;
}
+static inline int is_imx51_ecspi(struct spi_imx_data *d)
+{
+ return d->devtype_data->devtype == IMX51_ECSPI;
+}
+
static inline unsigned spi_imx_get_fifosize(struct spi_imx_data *d)
{
- return (d->devtype_data->devtype == IMX51_ECSPI) ? 64 : 8;
+ return is_imx51_ecspi(d) ? 64 : 8;
}
#define MXC_SPI_BUF_RX(type) \
@@ -201,9 +204,8 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
{
struct spi_imx_data *spi_imx = spi_master_get_devdata(master);
- if (spi_imx->dma_is_inited
- && transfer->len > spi_imx->rx_wml * sizeof(u32)
- && transfer->len > spi_imx->tx_wml * sizeof(u32))
+ if (spi_imx->dma_is_inited &&
+ transfer->len > spi_imx->wml * sizeof(u32))
return true;
return false;
}
@@ -244,6 +246,9 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,
#define MX51_ECSPI_STAT 0x18
#define MX51_ECSPI_STAT_RR (1 << 3)
+#define MX51_ECSPI_TESTREG 0x20
+#define MX51_ECSPI_TESTREG_LBC BIT(31)
+
/* MX51 eCSPI */
static unsigned int mx51_ecspi_clkdiv(unsigned int fin, unsigned int fspi,
unsigned int *fres)
@@ -313,7 +318,7 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
{
u32 ctrl = MX51_ECSPI_CTRL_ENABLE, cfg = 0, dma = 0;
u32 tx_wml_cfg, rx_wml_cfg, rxt_wml_cfg;
- u32 clk = config->speed_hz, delay;
+ u32 clk = config->speed_hz, delay, reg;
/*
* The hardware seems to have a race condition when changing modes. The
@@ -351,7 +356,16 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
else
cfg &= ~MX51_ECSPI_CONFIG_SSBPOL(config->cs);
+ /* CTRL register always go first to bring out controller from reset */
writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL);
+
+ reg = readl(spi_imx->base + MX51_ECSPI_TESTREG);
+ if (config->mode & SPI_LOOP)
+ reg |= MX51_ECSPI_TESTREG_LBC;
+ else
+ reg &= ~MX51_ECSPI_TESTREG_LBC;
+ writel(reg, spi_imx->base + MX51_ECSPI_TESTREG);
+
writel(cfg, spi_imx->base + MX51_ECSPI_CONFIG);
/*
@@ -378,10 +392,9 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
if (spi_imx->dma_is_inited) {
dma = readl(spi_imx->base + MX51_ECSPI_DMA);
- spi_imx->rxt_wml = spi_imx_get_fifosize(spi_imx) / 2;
- rx_wml_cfg = spi_imx->rx_wml << MX51_ECSPI_DMA_RX_WML_OFFSET;
- tx_wml_cfg = spi_imx->tx_wml << MX51_ECSPI_DMA_TX_WML_OFFSET;
- rxt_wml_cfg = spi_imx->rxt_wml << MX51_ECSPI_DMA_RXT_WML_OFFSET;
+ rx_wml_cfg = spi_imx->wml << MX51_ECSPI_DMA_RX_WML_OFFSET;
+ tx_wml_cfg = spi_imx->wml << MX51_ECSPI_DMA_TX_WML_OFFSET;
+ rxt_wml_cfg = spi_imx->wml << MX51_ECSPI_DMA_RXT_WML_OFFSET;
dma = (dma & ~MX51_ECSPI_DMA_TX_WML_MASK
& ~MX51_ECSPI_DMA_RX_WML_MASK
& ~MX51_ECSPI_DMA_RXT_WML_MASK)
@@ -832,18 +845,21 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx,
if (of_machine_is_compatible("fsl,imx6dl"))
return 0;
+ spi_imx->wml = spi_imx_get_fifosize(spi_imx) / 2;
+
/* Prepare for TX DMA: */
- master->dma_tx = dma_request_slave_channel(dev, "tx");
- if (!master->dma_tx) {
- dev_err(dev, "cannot get the TX DMA channel!\n");
- ret = -EINVAL;
+ master->dma_tx = dma_request_slave_channel_reason(dev, "tx");
+ if (IS_ERR(master->dma_tx)) {
+ ret = PTR_ERR(master->dma_tx);
+ dev_dbg(dev, "can't get the TX DMA channel, error %d!\n", ret);
+ master->dma_tx = NULL;
goto err;
}
slave_config.direction = DMA_MEM_TO_DEV;
slave_config.dst_addr = res->start + MXC_CSPITXDATA;
slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
- slave_config.dst_maxburst = spi_imx_get_fifosize(spi_imx) / 2;
+ slave_config.dst_maxburst = spi_imx->wml;
ret = dmaengine_slave_config(master->dma_tx, &slave_config);
if (ret) {
dev_err(dev, "error in TX dma configuration.\n");
@@ -851,17 +867,18 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx,
}
/* Prepare for RX : */
- master->dma_rx = dma_request_slave_channel(dev, "rx");
- if (!master->dma_rx) {
- dev_dbg(dev, "cannot get the DMA channel.\n");
- ret = -EINVAL;
+ master->dma_rx = dma_request_slave_channel_reason(dev, "rx");
+ if (IS_ERR(master->dma_rx)) {
+ ret = PTR_ERR(master->dma_rx);
+ dev_dbg(dev, "can't get the RX DMA channel, error %d\n", ret);
+ master->dma_rx = NULL;
goto err;
}
slave_config.direction = DMA_DEV_TO_MEM;
slave_config.src_addr = res->start + MXC_CSPIRXDATA;
slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
- slave_config.src_maxburst = spi_imx_get_fifosize(spi_imx) / 2;
+ slave_config.src_maxburst = spi_imx->wml;
ret = dmaengine_slave_config(master->dma_rx, &slave_config);
if (ret) {
dev_err(dev, "error in RX dma configuration.\n");
@@ -874,8 +891,6 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx,
master->max_dma_len = MAX_SDMA_BD_BYTES;
spi_imx->bitbang.master->flags = SPI_MASTER_MUST_RX |
SPI_MASTER_MUST_TX;
- spi_imx->tx_wml = spi_imx_get_fifosize(spi_imx) / 2;
- spi_imx->rx_wml = spi_imx_get_fifosize(spi_imx) / 2;
spi_imx->dma_is_inited = 1;
return 0;
@@ -942,14 +957,22 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
dma = readl(spi_imx->base + MX51_ECSPI_DMA);
dma = dma & (~MX51_ECSPI_DMA_RXT_WML_MASK);
/* Change RX_DMA_LENGTH trigger dma fetch tail data */
- left = transfer->len % spi_imx->rxt_wml;
+ left = transfer->len % spi_imx->wml;
if (left)
writel(dma | (left << MX51_ECSPI_DMA_RXT_WML_OFFSET),
spi_imx->base + MX51_ECSPI_DMA);
+ /*
+ * Set these order to avoid potential RX overflow. The overflow may
+ * happen if we enable SPI HW before starting RX DMA due to rescheduling
+ * for another task and/or interrupt.
+ * So RX DMA enabled first to make sure data would be read out from FIFO
+ * ASAP. TX DMA enabled next to start filling TX FIFO with new data.
+ * And finaly SPI HW enabled to start actual data transfer.
+ */
+ dma_async_issue_pending(master->dma_rx);
+ dma_async_issue_pending(master->dma_tx);
spi_imx->devtype_data->trigger(spi_imx);
- dma_async_issue_pending(master->dma_tx);
- dma_async_issue_pending(master->dma_rx);
/* Wait SDMA to finish the data transfer.*/
timeout = wait_for_completion_timeout(&spi_imx->dma_tx_completion,
IMX_DMA_TIMEOUT);
@@ -958,6 +981,7 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
dev_driver_string(&master->dev),
dev_name(&master->dev));
dmaengine_terminate_all(master->dma_tx);
+ dmaengine_terminate_all(master->dma_rx);
} else {
timeout = wait_for_completion_timeout(
&spi_imx->dma_rx_completion, IMX_DMA_TIMEOUT);
@@ -968,8 +992,9 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,
spi_imx->devtype_data->reset(spi_imx);
dmaengine_terminate_all(master->dma_rx);
}
+ dma &= ~MX51_ECSPI_DMA_RXT_WML_MASK;
writel(dma |
- spi_imx->rxt_wml << MX51_ECSPI_DMA_RXT_WML_OFFSET,
+ spi_imx->wml << MX51_ECSPI_DMA_RXT_WML_OFFSET,
spi_imx->base + MX51_ECSPI_DMA);
}
@@ -1117,6 +1142,9 @@ static int spi_imx_probe(struct platform_device *pdev)
spi_imx = spi_master_get_devdata(master);
spi_imx->bitbang.master = master;
+ spi_imx->devtype_data = of_id ? of_id->data :
+ (struct spi_imx_devtype_data *)pdev->id_entry->driver_data;
+
for (i = 0; i < master->num_chipselect; i++) {
int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
if (!gpio_is_valid(cs_gpio) && mxc_platform_info)
@@ -1142,12 +1170,11 @@ static int spi_imx_probe(struct platform_device *pdev)
spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message;
spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message;
spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
+ if (is_imx51_ecspi(spi_imx))
+ spi_imx->bitbang.master->mode_bits |= SPI_LOOP;
init_completion(&spi_imx->xfer_done);
- spi_imx->devtype_data = of_id ? of_id->data :
- (struct spi_imx_devtype_data *) pdev->id_entry->driver_data;
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
spi_imx->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(spi_imx->base)) {
@@ -1193,9 +1220,15 @@ static int spi_imx_probe(struct platform_device *pdev)
* Only validated on i.mx6 now, can remove the constrain if validated on
* other chips.
*/
- if (spi_imx->devtype_data == &imx51_ecspi_devtype_data
- && spi_imx_sdma_init(&pdev->dev, spi_imx, master, res))
- dev_err(&pdev->dev, "dma setup error,use pio instead\n");
+ if (is_imx51_ecspi(spi_imx)) {
+ ret = spi_imx_sdma_init(&pdev->dev, spi_imx, master, res);
+ if (ret == -EPROBE_DEFER)
+ goto out_clk_put;
+
+ if (ret < 0)
+ dev_err(&pdev->dev, "dma setup error %d, use pio\n",
+ ret);
+ }
spi_imx->devtype_data->reset(spi_imx);
diff --git a/drivers/spi/spi-lm70llp.c b/drivers/spi/spi-lm70llp.c
index ba72347cb99d..61ee0f4269ae 100644
--- a/drivers/spi/spi-lm70llp.c
+++ b/drivers/spi/spi-lm70llp.c
@@ -14,6 +14,8 @@
* GNU General Public License for more details.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
@@ -23,11 +25,9 @@
#include <linux/sysfs.h>
#include <linux/workqueue.h>
-
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
-
/*
* The LM70 communicates with a host processor using a 3-wire variant of
* the SPI/Microwire bus interface. This driver specifically supports an
@@ -88,7 +88,6 @@ struct spi_lm70llp {
/* REVISIT : ugly global ; provides "exclusive open" facility */
static struct spi_lm70llp *lm70llp;
-
/*-------------------------------------------------------------------*/
static inline struct spi_lm70llp *spidev_to_pp(struct spi_device *spi)
@@ -122,12 +121,14 @@ static inline void assertCS(struct spi_lm70llp *pp)
static inline void clkHigh(struct spi_lm70llp *pp)
{
u8 data = parport_read_data(pp->port);
+
parport_write_data(pp->port, data | SCLK);
}
static inline void clkLow(struct spi_lm70llp *pp)
{
u8 data = parport_read_data(pp->port);
+
parport_write_data(pp->port, data & ~SCLK);
}
@@ -166,8 +167,10 @@ static inline void setmosi(struct spi_device *s, int is_on)
static inline int getmiso(struct spi_device *s)
{
struct spi_lm70llp *pp = spidev_to_pp(s);
- return ((SIO == (parport_read_status(pp->port) & SIO)) ? 0 : 1 );
+
+ return ((SIO == (parport_read_status(pp->port) & SIO)) ? 0 : 1);
}
+
/*--------------------------------------------------------------------*/
#include "spi-bitbang-txrx.h"
@@ -196,11 +199,10 @@ static void spi_lm70llp_attach(struct parport *p)
struct spi_lm70llp *pp;
struct spi_master *master;
int status;
+ struct pardev_cb lm70llp_cb;
if (lm70llp) {
- printk(KERN_WARNING
- "%s: spi_lm70llp instance already loaded. Aborting.\n",
- DRVNAME);
+ pr_warn("spi_lm70llp instance already loaded. Aborting.\n");
return;
}
@@ -227,9 +229,11 @@ static void spi_lm70llp_attach(struct parport *p)
* Parport hookup
*/
pp->port = p;
- pd = parport_register_device(p, DRVNAME,
- NULL, NULL, NULL,
- PARPORT_FLAG_EXCL, pp);
+ memset(&lm70llp_cb, 0, sizeof(lm70llp_cb));
+ lm70llp_cb.private = pp;
+ lm70llp_cb.flags = PARPORT_FLAG_EXCL;
+ pd = parport_register_dev_model(p, DRVNAME, &lm70llp_cb, 0);
+
if (!pd) {
status = -ENOMEM;
goto out_free_master;
@@ -245,9 +249,8 @@ static void spi_lm70llp_attach(struct parport *p)
*/
status = spi_bitbang_start(&pp->bitbang);
if (status < 0) {
- printk(KERN_WARNING
- "%s: spi_bitbang_start failed with status %d\n",
- DRVNAME, status);
+ dev_warn(&pd->dev, "spi_bitbang_start failed with status %d\n",
+ status);
goto out_off_and_release;
}
@@ -272,9 +275,9 @@ static void spi_lm70llp_attach(struct parport *p)
pp->spidev_lm70 = spi_new_device(pp->bitbang.master, &pp->info);
if (pp->spidev_lm70)
dev_dbg(&pp->spidev_lm70->dev, "spidev_lm70 at %s\n",
- dev_name(&pp->spidev_lm70->dev));
+ dev_name(&pp->spidev_lm70->dev));
else {
- printk(KERN_WARNING "%s: spi_new_device failed\n", DRVNAME);
+ dev_warn(&pd->dev, "spi_new_device failed\n");
status = -ENODEV;
goto out_bitbang_stop;
}
@@ -293,9 +296,9 @@ out_off_and_release:
out_parport_unreg:
parport_unregister_device(pd);
out_free_master:
- (void) spi_master_put(master);
+ spi_master_put(master);
out_fail:
- pr_info("%s: spi_lm70llp probe fail, status %d\n", DRVNAME, status);
+ pr_info("spi_lm70llp probe fail, status %d\n", status);
}
static void spi_lm70llp_detach(struct parport *p)
@@ -314,16 +317,16 @@ static void spi_lm70llp_detach(struct parport *p)
parport_release(pp->pd);
parport_unregister_device(pp->pd);
- (void) spi_master_put(pp->bitbang.master);
+ spi_master_put(pp->bitbang.master);
lm70llp = NULL;
}
-
static struct parport_driver spi_lm70llp_drv = {
.name = DRVNAME,
- .attach = spi_lm70llp_attach,
+ .match_port = spi_lm70llp_attach,
.detach = spi_lm70llp_detach,
+ .devmodel = true,
};
static int __init init_spi_lm70llp(void)
diff --git a/drivers/spi/spi-loopback-test.c b/drivers/spi/spi-loopback-test.c
new file mode 100644
index 000000000000..894616f687b0
--- /dev/null
+++ b/drivers/spi/spi-loopback-test.c
@@ -0,0 +1,1005 @@
+/*
+ * linux/drivers/spi/spi-loopback-test.c
+ *
+ * (c) Martin Sperl <kernel@martin.sperl.org>
+ *
+ * Loopback test driver to test several typical spi_message conditions
+ * that a spi_master driver may encounter
+ * this can also get used for regression testing
+ *
+ * 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/kernel.h>
+#include <linux/list.h>
+#include <linux/list_sort.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/printk.h>
+#include <linux/spi/spi.h>
+
+#include "spi-test.h"
+
+/* flag to only simulate transfers */
+int simulate_only;
+module_param(simulate_only, int, 0);
+MODULE_PARM_DESC(simulate_only, "if not 0 do not execute the spi message");
+
+/* dump spi messages */
+int dump_messages;
+module_param(dump_messages, int, 0);
+MODULE_PARM_DESC(dump_messages,
+ "=1 dump the basic spi_message_structure, " \
+ "=2 dump the spi_message_structure including data, " \
+ "=3 dump the spi_message structure before and after execution");
+/* the device is jumpered for loopback - enabling some rx_buf tests */
+int loopback;
+module_param(loopback, int, 0);
+MODULE_PARM_DESC(loopback,
+ "if set enable loopback mode, where the rx_buf " \
+ "is checked to match tx_buf after the spi_message " \
+ "is executed");
+
+/* run only a specific test */
+int run_only_test = -1;
+module_param(run_only_test, int, 0);
+MODULE_PARM_DESC(run_only_test,
+ "only run the test with this number (0-based !)");
+
+/* the actual tests to execute */
+static struct spi_test spi_tests[] = {
+ {
+ .description = "tx/rx-transfer - start of page",
+ .fill_option = FILL_COUNT_8,
+ .iterate_len = { ITERATE_MAX_LEN },
+ .iterate_tx_align = ITERATE_ALIGN,
+ .iterate_rx_align = ITERATE_ALIGN,
+ .transfers = {
+ {
+ .len = 1,
+ .tx_buf = TX(0),
+ .rx_buf = RX(0),
+ },
+ },
+ },
+ {
+ .description = "tx/rx-transfer - crossing PAGE_SIZE",
+ .fill_option = FILL_COUNT_8,
+ .iterate_len = { ITERATE_MAX_LEN },
+ .iterate_tx_align = ITERATE_ALIGN,
+ .iterate_rx_align = ITERATE_ALIGN,
+ .transfers = {
+ {
+ .len = 1,
+ .tx_buf = TX(PAGE_SIZE - 4),
+ .rx_buf = RX(PAGE_SIZE - 4),
+ },
+ },
+ },
+ {
+ .description = "tx-transfer - only",
+ .fill_option = FILL_COUNT_8,
+ .iterate_len = { ITERATE_MAX_LEN },
+ .iterate_tx_align = ITERATE_ALIGN,
+ .transfers = {
+ {
+ .len = 1,
+ .tx_buf = TX(0),
+ },
+ },
+ },
+ {
+ .description = "rx-transfer - only",
+ .fill_option = FILL_COUNT_8,
+ .iterate_len = { ITERATE_MAX_LEN },
+ .iterate_rx_align = ITERATE_ALIGN,
+ .transfers = {
+ {
+ .len = 1,
+ .rx_buf = RX(0),
+ },
+ },
+ },
+ {
+ .description = "two tx-transfers - alter both",
+ .fill_option = FILL_COUNT_8,
+ .iterate_len = { ITERATE_LEN },
+ .iterate_tx_align = ITERATE_ALIGN,
+ .iterate_transfer_mask = BIT(0) | BIT(1),
+ .transfers = {
+ {
+ .len = 1,
+ .tx_buf = TX(0),
+ },
+ {
+ .len = 1,
+ /* this is why we cant use ITERATE_MAX_LEN */
+ .tx_buf = TX(SPI_TEST_MAX_SIZE_HALF),
+ },
+ },
+ },
+ {
+ .description = "two tx-transfers - alter first",
+ .fill_option = FILL_COUNT_8,
+ .iterate_len = { ITERATE_MAX_LEN },
+ .iterate_tx_align = ITERATE_ALIGN,
+ .iterate_transfer_mask = BIT(1),
+ .transfers = {
+ {
+ .len = 1,
+ .tx_buf = TX(64),
+ },
+ {
+ .len = 1,
+ .tx_buf = TX(0),
+ },
+ },
+ },
+ {
+ .description = "two tx-transfers - alter second",
+ .fill_option = FILL_COUNT_8,
+ .iterate_len = { ITERATE_MAX_LEN },
+ .iterate_tx_align = ITERATE_ALIGN,
+ .iterate_transfer_mask = BIT(0),
+ .transfers = {
+ {
+ .len = 16,
+ .tx_buf = TX(0),
+ },
+ {
+ .len = 1,
+ .tx_buf = TX(64),
+ },
+ },
+ },
+ {
+ .description = "two transfers tx then rx - alter both",
+ .fill_option = FILL_COUNT_8,
+ .iterate_len = { ITERATE_MAX_LEN },
+ .iterate_tx_align = ITERATE_ALIGN,
+ .iterate_transfer_mask = BIT(0) | BIT(1),
+ .transfers = {
+ {
+ .len = 1,
+ .tx_buf = TX(0),
+ },
+ {
+ .len = 1,
+ .rx_buf = RX(0),
+ },
+ },
+ },
+ {
+ .description = "two transfers tx then rx - alter tx",
+ .fill_option = FILL_COUNT_8,
+ .iterate_len = { ITERATE_MAX_LEN },
+ .iterate_tx_align = ITERATE_ALIGN,
+ .iterate_transfer_mask = BIT(0),
+ .transfers = {
+ {
+ .len = 1,
+ .tx_buf = TX(0),
+ },
+ {
+ .len = 1,
+ .rx_buf = RX(0),
+ },
+ },
+ },
+ {
+ .description = "two transfers tx then rx - alter rx",
+ .fill_option = FILL_COUNT_8,
+ .iterate_len = { ITERATE_MAX_LEN },
+ .iterate_tx_align = ITERATE_ALIGN,
+ .iterate_transfer_mask = BIT(1),
+ .transfers = {
+ {
+ .len = 1,
+ .tx_buf = TX(0),
+ },
+ {
+ .len = 1,
+ .rx_buf = RX(0),
+ },
+ },
+ },
+ {
+ .description = "two tx+rx transfers - alter both",
+ .fill_option = FILL_COUNT_8,
+ .iterate_len = { ITERATE_LEN },
+ .iterate_tx_align = ITERATE_ALIGN,
+ .iterate_transfer_mask = BIT(0) | BIT(1),
+ .transfers = {
+ {
+ .len = 1,
+ .tx_buf = TX(0),
+ .rx_buf = RX(0),
+ },
+ {
+ .len = 1,
+ /* making sure we align without overwrite
+ * the reason we can not use ITERATE_MAX_LEN
+ */
+ .tx_buf = TX(SPI_TEST_MAX_SIZE_HALF),
+ .rx_buf = RX(SPI_TEST_MAX_SIZE_HALF),
+ },
+ },
+ },
+ {
+ .description = "two tx+rx transfers - alter first",
+ .fill_option = FILL_COUNT_8,
+ .iterate_len = { ITERATE_MAX_LEN },
+ .iterate_tx_align = ITERATE_ALIGN,
+ .iterate_transfer_mask = BIT(0),
+ .transfers = {
+ {
+ .len = 1,
+ /* making sure we align without overwrite */
+ .tx_buf = TX(1024),
+ .rx_buf = RX(1024),
+ },
+ {
+ .len = 1,
+ /* making sure we align without overwrite */
+ .tx_buf = TX(0),
+ .rx_buf = RX(0),
+ },
+ },
+ },
+ {
+ .description = "two tx+rx transfers - alter second",
+ .fill_option = FILL_COUNT_8,
+ .iterate_len = { ITERATE_MAX_LEN },
+ .iterate_tx_align = ITERATE_ALIGN,
+ .iterate_transfer_mask = BIT(1),
+ .transfers = {
+ {
+ .len = 1,
+ .tx_buf = TX(0),
+ .rx_buf = RX(0),
+ },
+ {
+ .len = 1,
+ /* making sure we align without overwrite */
+ .tx_buf = TX(1024),
+ .rx_buf = RX(1024),
+ },
+ },
+ },
+
+ { /* end of tests sequence */ }
+};
+
+static int spi_loopback_test_probe(struct spi_device *spi)
+{
+ int ret;
+
+ dev_info(&spi->dev, "Executing spi-loopback-tests\n");
+
+ ret = spi_test_run_tests(spi, spi_tests);
+
+ dev_info(&spi->dev, "Finished spi-loopback-tests with return: %i\n",
+ ret);
+
+ return ret;
+}
+
+/* non const match table to permit to change via a module parameter */
+static struct of_device_id spi_loopback_test_of_match[] = {
+ { .compatible = "linux,spi-loopback-test", },
+ { }
+};
+
+/* allow to override the compatible string via a module_parameter */
+module_param_string(compatible, spi_loopback_test_of_match[0].compatible,
+ sizeof(spi_loopback_test_of_match[0].compatible),
+ 0000);
+
+MODULE_DEVICE_TABLE(of, spi_loopback_test_of_match);
+
+static struct spi_driver spi_loopback_test_driver = {
+ .driver = {
+ .name = "spi-loopback-test",
+ .owner = THIS_MODULE,
+ .of_match_table = spi_loopback_test_of_match,
+ },
+ .probe = spi_loopback_test_probe,
+};
+
+module_spi_driver(spi_loopback_test_driver);
+
+MODULE_AUTHOR("Martin Sperl <kernel@martin.sperl.org>");
+MODULE_DESCRIPTION("test spi_driver to check core functionality");
+MODULE_LICENSE("GPL");
+
+/*-------------------------------------------------------------------------*/
+
+/* spi_test implementation */
+
+#define RANGE_CHECK(ptr, plen, start, slen) \
+ ((ptr >= start) && (ptr + plen <= start + slen))
+
+/* we allocate one page more, to allow for offsets */
+#define SPI_TEST_MAX_SIZE_PLUS (SPI_TEST_MAX_SIZE + PAGE_SIZE)
+
+static void spi_test_print_hex_dump(char *pre, const void *ptr, size_t len)
+{
+ /* limit the hex_dump */
+ if (len < 1024) {
+ print_hex_dump(KERN_INFO, pre,
+ DUMP_PREFIX_OFFSET, 16, 1,
+ ptr, len, 0);
+ return;
+ }
+ /* print head */
+ print_hex_dump(KERN_INFO, pre,
+ DUMP_PREFIX_OFFSET, 16, 1,
+ ptr, 512, 0);
+ /* print tail */
+ pr_info("%s truncated - continuing at offset %04zx\n",
+ pre, len - 512);
+ print_hex_dump(KERN_INFO, pre,
+ DUMP_PREFIX_OFFSET, 16, 1,
+ ptr + (len - 512), 512, 0);
+}
+
+static void spi_test_dump_message(struct spi_device *spi,
+ struct spi_message *msg,
+ bool dump_data)
+{
+ struct spi_transfer *xfer;
+ int i;
+ u8 b;
+
+ dev_info(&spi->dev, " spi_msg@%pK\n", msg);
+ if (msg->status)
+ dev_info(&spi->dev, " status: %i\n",
+ msg->status);
+ dev_info(&spi->dev, " frame_length: %i\n",
+ msg->frame_length);
+ dev_info(&spi->dev, " actual_length: %i\n",
+ msg->actual_length);
+
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ dev_info(&spi->dev, " spi_transfer@%pK\n", xfer);
+ dev_info(&spi->dev, " len: %i\n", xfer->len);
+ dev_info(&spi->dev, " tx_buf: %pK\n", xfer->tx_buf);
+ if (dump_data && xfer->tx_buf)
+ spi_test_print_hex_dump(" TX: ",
+ xfer->tx_buf,
+ xfer->len);
+
+ dev_info(&spi->dev, " rx_buf: %pK\n", xfer->rx_buf);
+ if (dump_data && xfer->rx_buf)
+ spi_test_print_hex_dump(" RX: ",
+ xfer->rx_buf,
+ xfer->len);
+ /* check for unwritten test pattern on rx_buf */
+ if (xfer->rx_buf) {
+ for (i = 0 ; i < xfer->len ; i++) {
+ b = ((u8 *)xfer->rx_buf)[xfer->len - 1 - i];
+ if (b != SPI_TEST_PATTERN_UNWRITTEN)
+ break;
+ }
+ if (i)
+ dev_info(&spi->dev,
+ " rx_buf filled with %02x starts at offset: %i\n",
+ SPI_TEST_PATTERN_UNWRITTEN,
+ xfer->len - i);
+ }
+ }
+}
+
+struct rx_ranges {
+ struct list_head list;
+ u8 *start;
+ u8 *end;
+};
+
+int rx_ranges_cmp(void *priv, struct list_head *a, struct list_head *b)
+{
+ struct rx_ranges *rx_a = list_entry(a, struct rx_ranges, list);
+ struct rx_ranges *rx_b = list_entry(b, struct rx_ranges, list);
+
+ if (rx_a->start > rx_b->start)
+ return 1;
+ if (rx_a->start < rx_b->start)
+ return -1;
+ return 0;
+}
+
+static int spi_check_rx_ranges(struct spi_device *spi,
+ struct spi_message *msg,
+ void *rx)
+{
+ struct spi_transfer *xfer;
+ struct rx_ranges ranges[SPI_TEST_MAX_TRANSFERS], *r;
+ int i = 0;
+ LIST_HEAD(ranges_list);
+ u8 *addr;
+ int ret = 0;
+
+ /* loop over all transfers to fill in the rx_ranges */
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ /* if there is no rx, then no check is needed */
+ if (!xfer->rx_buf)
+ continue;
+ /* fill in the rx_range */
+ if (RANGE_CHECK(xfer->rx_buf, xfer->len,
+ rx, SPI_TEST_MAX_SIZE_PLUS)) {
+ ranges[i].start = xfer->rx_buf;
+ ranges[i].end = xfer->rx_buf + xfer->len;
+ list_add(&ranges[i].list, &ranges_list);
+ i++;
+ }
+ }
+
+ /* if no ranges, then we can return and avoid the checks...*/
+ if (!i)
+ return 0;
+
+ /* sort the list */
+ list_sort(NULL, &ranges_list, rx_ranges_cmp);
+
+ /* and iterate over all the rx addresses */
+ for (addr = rx; addr < (u8 *)rx + SPI_TEST_MAX_SIZE_PLUS; addr++) {
+ /* if we are the DO not write pattern,
+ * then continue with the loop...
+ */
+ if (*addr == SPI_TEST_PATTERN_DO_NOT_WRITE)
+ continue;
+
+ /* check if we are inside a range */
+ list_for_each_entry(r, &ranges_list, list) {
+ /* if so then set to end... */
+ if ((addr >= r->start) && (addr < r->end))
+ addr = r->end;
+ }
+ /* second test after a (hopefull) translation */
+ if (*addr == SPI_TEST_PATTERN_DO_NOT_WRITE)
+ continue;
+
+ /* if still not found then something has modified too much */
+ /* we could list the "closest" transfer here... */
+ dev_err(&spi->dev,
+ "loopback strangeness - rx changed outside of allowed range at: %pK\n",
+ addr);
+ /* do not return, only set ret,
+ * so that we list all addresses
+ */
+ ret = -ERANGE;
+ }
+
+ return ret;
+}
+
+static int spi_test_check_loopback_result(struct spi_device *spi,
+ struct spi_message *msg,
+ void *tx, void *rx)
+{
+ struct spi_transfer *xfer;
+ u8 rxb, txb;
+ size_t i;
+ int ret;
+
+ /* checks rx_buffer pattern are valid with loopback or without */
+ ret = spi_check_rx_ranges(spi, msg, rx);
+ if (ret)
+ return ret;
+
+ /* if we run without loopback, then return now */
+ if (!loopback)
+ return 0;
+
+ /* if applicable to transfer check that rx_buf is equal to tx_buf */
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ /* if there is no rx, then no check is needed */
+ if (!xfer->rx_buf)
+ continue;
+ /* so depending on tx_buf we need to handle things */
+ if (xfer->tx_buf) {
+ for (i = 1; i < xfer->len; i++) {
+ txb = ((u8 *)xfer->tx_buf)[i];
+ rxb = ((u8 *)xfer->rx_buf)[i];
+ if (txb != rxb)
+ goto mismatch_error;
+ }
+ } else {
+ /* first byte received */
+ txb = ((u8 *)xfer->rx_buf)[0];
+ /* first byte may be 0 or xff */
+ if (!((txb == 0) || (txb == 0xff))) {
+ dev_err(&spi->dev,
+ "loopback strangeness - we expect 0x00 or 0xff, but not 0x%02x\n",
+ txb);
+ return -EINVAL;
+ }
+ /* check that all bytes are identical */
+ for (i = 1; i < xfer->len; i++) {
+ rxb = ((u8 *)xfer->rx_buf)[i];
+ if (rxb != txb)
+ goto mismatch_error;
+ }
+ }
+ }
+
+ return 0;
+
+mismatch_error:
+ dev_err(&spi->dev,
+ "loopback strangeness - transfer missmatch on byte %04zx - expected 0x%02x, but got 0x%02x\n",
+ i, txb, rxb);
+
+ return -EINVAL;
+}
+
+static int spi_test_translate(struct spi_device *spi,
+ void **ptr, size_t len,
+ void *tx, void *rx)
+{
+ size_t off;
+
+ /* return on null */
+ if (!*ptr)
+ return 0;
+
+ /* in the MAX_SIZE_HALF case modify the pointer */
+ if (((size_t)*ptr) & SPI_TEST_MAX_SIZE_HALF)
+ /* move the pointer to the correct range */
+ *ptr += (SPI_TEST_MAX_SIZE_PLUS / 2) -
+ SPI_TEST_MAX_SIZE_HALF;
+
+ /* RX range
+ * - we check against MAX_SIZE_PLUS to allow for automated alignment
+ */
+ if (RANGE_CHECK(*ptr, len, RX(0), SPI_TEST_MAX_SIZE_PLUS)) {
+ off = *ptr - RX(0);
+ *ptr = rx + off;
+
+ return 0;
+ }
+
+ /* TX range */
+ if (RANGE_CHECK(*ptr, len, TX(0), SPI_TEST_MAX_SIZE_PLUS)) {
+ off = *ptr - TX(0);
+ *ptr = tx + off;
+
+ return 0;
+ }
+
+ dev_err(&spi->dev,
+ "PointerRange [%pK:%pK[ not in range [%pK:%pK[ or [%pK:%pK[\n",
+ *ptr, *ptr + len,
+ RX(0), RX(SPI_TEST_MAX_SIZE),
+ TX(0), TX(SPI_TEST_MAX_SIZE));
+
+ return -EINVAL;
+}
+
+static int spi_test_fill_pattern(struct spi_device *spi,
+ struct spi_test *test)
+{
+ struct spi_transfer *xfers = test->transfers;
+ u8 *tx_buf;
+ size_t count = 0;
+ int i, j;
+
+#ifdef __BIG_ENDIAN
+#define GET_VALUE_BYTE(value, index, bytes) \
+ (value >> (8 * (bytes - 1 - count % bytes)))
+#else
+#define GET_VALUE_BYTE(value, index, bytes) \
+ (value >> (8 * (count % bytes)))
+#endif
+
+ /* fill all transfers with the pattern requested */
+ for (i = 0; i < test->transfer_count; i++) {
+ /* fill rx_buf with SPI_TEST_PATTERN_UNWRITTEN */
+ if (xfers[i].rx_buf)
+ memset(xfers[i].rx_buf, SPI_TEST_PATTERN_UNWRITTEN,
+ xfers[i].len);
+ /* if tx_buf is NULL then skip */
+ tx_buf = (u8 *)xfers[i].tx_buf;
+ if (!tx_buf)
+ continue;
+ /* modify all the transfers */
+ for (j = 0; j < xfers[i].len; j++, tx_buf++, count++) {
+ /* fill tx */
+ switch (test->fill_option) {
+ case FILL_MEMSET_8:
+ *tx_buf = test->fill_pattern;
+ break;
+ case FILL_MEMSET_16:
+ *tx_buf = GET_VALUE_BYTE(test->fill_pattern,
+ count, 2);
+ break;
+ case FILL_MEMSET_24:
+ *tx_buf = GET_VALUE_BYTE(test->fill_pattern,
+ count, 3);
+ break;
+ case FILL_MEMSET_32:
+ *tx_buf = GET_VALUE_BYTE(test->fill_pattern,
+ count, 4);
+ break;
+ case FILL_COUNT_8:
+ *tx_buf = count;
+ break;
+ case FILL_COUNT_16:
+ *tx_buf = GET_VALUE_BYTE(count, count, 2);
+ break;
+ case FILL_COUNT_24:
+ *tx_buf = GET_VALUE_BYTE(count, count, 3);
+ break;
+ case FILL_COUNT_32:
+ *tx_buf = GET_VALUE_BYTE(count, count, 4);
+ break;
+ case FILL_TRANSFER_BYTE_8:
+ *tx_buf = j;
+ break;
+ case FILL_TRANSFER_BYTE_16:
+ *tx_buf = GET_VALUE_BYTE(j, j, 2);
+ break;
+ case FILL_TRANSFER_BYTE_24:
+ *tx_buf = GET_VALUE_BYTE(j, j, 3);
+ break;
+ case FILL_TRANSFER_BYTE_32:
+ *tx_buf = GET_VALUE_BYTE(j, j, 4);
+ break;
+ case FILL_TRANSFER_NUM:
+ *tx_buf = i;
+ break;
+ default:
+ dev_err(&spi->dev,
+ "unsupported fill_option: %i\n",
+ test->fill_option);
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int _spi_test_run_iter(struct spi_device *spi,
+ struct spi_test *test,
+ void *tx, void *rx)
+{
+ struct spi_message *msg = &test->msg;
+ struct spi_transfer *x;
+ int i, ret;
+
+ /* initialize message - zero-filled via static initialization */
+ spi_message_init_no_memset(msg);
+
+ /* fill rx with the DO_NOT_WRITE pattern */
+ memset(rx, SPI_TEST_PATTERN_DO_NOT_WRITE, SPI_TEST_MAX_SIZE_PLUS);
+
+ /* add the individual transfers */
+ for (i = 0; i < test->transfer_count; i++) {
+ x = &test->transfers[i];
+
+ /* patch the values of tx_buf */
+ ret = spi_test_translate(spi, (void **)&x->tx_buf, x->len,
+ (void *)tx, rx);
+ if (ret)
+ return ret;
+
+ /* patch the values of rx_buf */
+ ret = spi_test_translate(spi, &x->rx_buf, x->len,
+ (void *)tx, rx);
+ if (ret)
+ return ret;
+
+ /* and add it to the list */
+ spi_message_add_tail(x, msg);
+ }
+
+ /* fill in the transfer buffers with pattern */
+ ret = spi_test_fill_pattern(spi, test);
+ if (ret)
+ return ret;
+
+ /* and execute */
+ if (test->execute_msg)
+ ret = test->execute_msg(spi, test, tx, rx);
+ else
+ ret = spi_test_execute_msg(spi, test, tx, rx);
+
+ /* handle result */
+ if (ret == test->expected_return)
+ return 0;
+
+ dev_err(&spi->dev,
+ "test failed - test returned %i, but we expect %i\n",
+ ret, test->expected_return);
+
+ if (ret)
+ return ret;
+
+ /* if it is 0, as we expected something else,
+ * then return something special
+ */
+ return -EFAULT;
+}
+
+static int spi_test_run_iter(struct spi_device *spi,
+ const struct spi_test *testtemplate,
+ void *tx, void *rx,
+ size_t len,
+ size_t tx_off,
+ size_t rx_off
+ )
+{
+ struct spi_test test;
+ int i, tx_count, rx_count;
+
+ /* copy the test template to test */
+ memcpy(&test, testtemplate, sizeof(test));
+
+ /* set up test->transfers to the correct count */
+ if (!test.transfer_count) {
+ for (i = 0;
+ (i < SPI_TEST_MAX_TRANSFERS) && test.transfers[i].len;
+ i++) {
+ test.transfer_count++;
+ }
+ }
+
+ /* if iterate_transfer_mask is not set,
+ * then set it to first transfer only
+ */
+ if (!(test.iterate_transfer_mask & (BIT(test.transfer_count) - 1)))
+ test.iterate_transfer_mask = 1;
+
+ /* count number of transfers with tx/rx_buf != NULL */
+ for (i = 0; i < test.transfer_count; i++) {
+ if (test.transfers[i].tx_buf)
+ tx_count++;
+ if (test.transfers[i].rx_buf)
+ rx_count++;
+ }
+
+ /* in some iteration cases warn and exit early,
+ * as there is nothing to do, that has not been tested already...
+ */
+ if (tx_off && (!tx_count)) {
+ dev_warn_once(&spi->dev,
+ "%s: iterate_tx_off configured with tx_buf==NULL - ignoring\n",
+ test.description);
+ return 0;
+ }
+ if (rx_off && (!rx_count)) {
+ dev_warn_once(&spi->dev,
+ "%s: iterate_rx_off configured with rx_buf==NULL - ignoring\n",
+ test.description);
+ return 0;
+ }
+
+ /* write out info */
+ if (!(len || tx_off || rx_off)) {
+ dev_info(&spi->dev, "Running test %s\n", test.description);
+ } else {
+ dev_info(&spi->dev,
+ " with iteration values: len = %zu, tx_off = %zu, rx_off = %zu\n",
+ len, tx_off, rx_off);
+ }
+
+ /* update in the values from iteration values */
+ for (i = 0; i < test.transfer_count; i++) {
+ /* only when bit in transfer mask is set */
+ if (!(test.iterate_transfer_mask & BIT(i)))
+ continue;
+ if (len)
+ test.transfers[i].len = len;
+ if (test.transfers[i].tx_buf)
+ test.transfers[i].tx_buf += tx_off;
+ if (test.transfers[i].tx_buf)
+ test.transfers[i].rx_buf += rx_off;
+ }
+
+ /* and execute */
+ return _spi_test_run_iter(spi, &test, tx, rx);
+}
+
+/**
+ * spi_test_execute_msg - default implementation to run a test
+ *
+ * spi: @spi_device on which to run the @spi_message
+ * test: the test to execute, which already contains @msg
+ * tx: the tx buffer allocated for the test sequence
+ * rx: the rx buffer allocated for the test sequence
+ *
+ * Returns: error code of spi_sync as well as basic error checking
+ */
+int spi_test_execute_msg(struct spi_device *spi, struct spi_test *test,
+ void *tx, void *rx)
+{
+ struct spi_message *msg = &test->msg;
+ int ret = 0;
+ int i;
+
+ /* only if we do not simulate */
+ if (!simulate_only) {
+ /* dump the complete message before and after the transfer */
+ if (dump_messages == 3)
+ spi_test_dump_message(spi, msg, true);
+
+ /* run spi message */
+ ret = spi_sync(spi, msg);
+ if (ret == -ETIMEDOUT) {
+ dev_info(&spi->dev,
+ "spi-message timed out - reruning...\n");
+ /* rerun after a few explicit schedules */
+ for (i = 0; i < 16; i++)
+ schedule();
+ ret = spi_sync(spi, msg);
+ }
+ if (ret) {
+ dev_err(&spi->dev,
+ "Failed to execute spi_message: %i\n",
+ ret);
+ goto exit;
+ }
+
+ /* do some extra error checks */
+ if (msg->frame_length != msg->actual_length) {
+ dev_err(&spi->dev,
+ "actual length differs from expected\n");
+ ret = -EIO;
+ goto exit;
+ }
+
+ /* run rx-buffer tests */
+ ret = spi_test_check_loopback_result(spi, msg, tx, rx);
+ }
+
+ /* if requested or on error dump message (including data) */
+exit:
+ if (dump_messages || ret)
+ spi_test_dump_message(spi, msg,
+ (dump_messages >= 2) || (ret));
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(spi_test_execute_msg);
+
+/**
+ * spi_test_run_test - run an individual spi_test
+ * including all the relevant iterations on:
+ * length and buffer alignment
+ *
+ * spi: the spi_device to send the messages to
+ * test: the test which we need to execute
+ * tx: the tx buffer allocated for the test sequence
+ * rx: the rx buffer allocated for the test sequence
+ *
+ * Returns: status code of spi_sync or other failures
+ */
+
+int spi_test_run_test(struct spi_device *spi, const struct spi_test *test,
+ void *tx, void *rx)
+{
+ int idx_len;
+ size_t len;
+ size_t tx_align, rx_align;
+ int ret;
+
+ /* test for transfer limits */
+ if (test->transfer_count >= SPI_TEST_MAX_TRANSFERS) {
+ dev_err(&spi->dev,
+ "%s: Exceeded max number of transfers with %i\n",
+ test->description, test->transfer_count);
+ return -E2BIG;
+ }
+
+ /* setting up some values in spi_message
+ * based on some settings in spi_master
+ * some of this can also get done in the run() method
+ */
+
+ /* iterate over all the iterable values using macros
+ * (to make it a bit more readable...
+ */
+#define FOR_EACH_ITERATE(var, defaultvalue) \
+ for (idx_##var = -1, var = defaultvalue; \
+ ((idx_##var < 0) || \
+ ( \
+ (idx_##var < SPI_TEST_MAX_ITERATE) && \
+ (var = test->iterate_##var[idx_##var]) \
+ ) \
+ ); \
+ idx_##var++)
+#define FOR_EACH_ALIGNMENT(var) \
+ for (var = 0; \
+ var < (test->iterate_##var ? \
+ (spi->master->dma_alignment ? \
+ spi->master->dma_alignment : \
+ test->iterate_##var) : \
+ 1); \
+ var++)
+
+ FOR_EACH_ITERATE(len, 0) {
+ FOR_EACH_ALIGNMENT(tx_align) {
+ FOR_EACH_ALIGNMENT(rx_align) {
+ /* and run the iteration */
+ ret = spi_test_run_iter(spi, test,
+ tx, rx,
+ len,
+ tx_align,
+ rx_align);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(spi_test_run_test);
+
+/**
+ * spi_test_run_tests - run an array of spi_messages tests
+ * @spi: the spi device on which to run the tests
+ * @tests: NULL-terminated array of @spi_test
+ *
+ * Returns: status errors as per @spi_test_run_test()
+ */
+
+int spi_test_run_tests(struct spi_device *spi,
+ struct spi_test *tests)
+{
+ char *rx = NULL, *tx = NULL;
+ int ret = 0, count = 0;
+ struct spi_test *test;
+
+ /* allocate rx/tx buffers of 128kB size without devm
+ * in the hope that is on a page boundary
+ */
+ rx = kzalloc(SPI_TEST_MAX_SIZE_PLUS, GFP_KERNEL);
+ if (!rx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ tx = kzalloc(SPI_TEST_MAX_SIZE_PLUS, GFP_KERNEL);
+ if (!tx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* now run the individual tests in the table */
+ for (test = tests, count = 0; test->description[0];
+ test++, count++) {
+ /* only run test if requested */
+ if ((run_only_test > -1) && (count != run_only_test))
+ continue;
+ /* run custom implementation */
+ if (test->run_test)
+ ret = test->run_test(spi, test, tx, rx);
+ else
+ ret = spi_test_run_test(spi, test, tx, rx);
+ if (ret)
+ goto out;
+ /* add some delays so that we can easily
+ * detect the individual tests when using a logic analyzer
+ * we also add scheduling to avoid potential spi_timeouts...
+ */
+ mdelay(100);
+ schedule();
+ }
+
+out:
+ kfree(rx);
+ kfree(tx);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(spi_test_run_tests);
diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c
index 563954a61424..0be89e052428 100644
--- a/drivers/spi/spi-mt65xx.c
+++ b/drivers/spi/spi-mt65xx.c
@@ -95,8 +95,7 @@ struct mtk_spi {
const struct mtk_spi_compatible *dev_comp;
};
-static const struct mtk_spi_compatible mt6589_compat;
-static const struct mtk_spi_compatible mt8135_compat;
+static const struct mtk_spi_compatible mtk_common_compat;
static const struct mtk_spi_compatible mt8173_compat = {
.need_pad_sel = true,
.must_tx = true,
@@ -112,9 +111,18 @@ static const struct mtk_chip_config mtk_default_chip_info = {
};
static const struct of_device_id mtk_spi_of_match[] = {
- { .compatible = "mediatek,mt6589-spi", .data = (void *)&mt6589_compat },
- { .compatible = "mediatek,mt8135-spi", .data = (void *)&mt8135_compat },
- { .compatible = "mediatek,mt8173-spi", .data = (void *)&mt8173_compat },
+ { .compatible = "mediatek,mt2701-spi",
+ .data = (void *)&mtk_common_compat,
+ },
+ { .compatible = "mediatek,mt6589-spi",
+ .data = (void *)&mtk_common_compat,
+ },
+ { .compatible = "mediatek,mt8135-spi",
+ .data = (void *)&mtk_common_compat,
+ },
+ { .compatible = "mediatek,mt8173-spi",
+ .data = (void *)&mt8173_compat,
+ },
{}
};
MODULE_DEVICE_TABLE(of, mtk_spi_of_match);
@@ -154,9 +162,6 @@ static int mtk_spi_prepare_message(struct spi_master *master,
reg_val |= SPI_CMD_CPOL;
else
reg_val &= ~SPI_CMD_CPOL;
- writel(reg_val, mdata->base + SPI_CMD_REG);
-
- reg_val = readl(mdata->base + SPI_CMD_REG);
/* set the mlsbx and mlsbtx */
if (chip_config->tx_mlsb)
@@ -323,7 +328,8 @@ static int mtk_spi_fifo_transfer(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *xfer)
{
- int cnt;
+ int cnt, remainder;
+ u32 reg_val;
struct mtk_spi *mdata = spi_master_get_devdata(master);
mdata->cur_transfer = xfer;
@@ -331,12 +337,16 @@ static int mtk_spi_fifo_transfer(struct spi_master *master,
mtk_spi_prepare_transfer(master, xfer);
mtk_spi_setup_packet(master);
- if (xfer->len % 4)
- cnt = xfer->len / 4 + 1;
- else
- cnt = xfer->len / 4;
+ cnt = xfer->len / 4;
iowrite32_rep(mdata->base + SPI_TX_DATA_REG, xfer->tx_buf, cnt);
+ remainder = xfer->len % 4;
+ if (remainder > 0) {
+ reg_val = 0;
+ memcpy(&reg_val, xfer->tx_buf + (cnt * 4), remainder);
+ writel(reg_val, mdata->base + SPI_TX_DATA_REG);
+ }
+
mtk_spi_enable_transfer(master);
return 1;
@@ -410,7 +420,7 @@ static int mtk_spi_setup(struct spi_device *spi)
if (!spi->controller_data)
spi->controller_data = (void *)&mtk_default_chip_info;
- if (mdata->dev_comp->need_pad_sel)
+ if (mdata->dev_comp->need_pad_sel && gpio_is_valid(spi->cs_gpio))
gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
return 0;
@@ -418,7 +428,7 @@ static int mtk_spi_setup(struct spi_device *spi)
static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
{
- u32 cmd, reg_val, cnt;
+ u32 cmd, reg_val, cnt, remainder;
struct spi_master *master = dev_id;
struct mtk_spi *mdata = spi_master_get_devdata(master);
struct spi_transfer *trans = mdata->cur_transfer;
@@ -431,12 +441,15 @@ static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
if (!master->can_dma(master, master->cur_msg->spi, trans)) {
if (trans->rx_buf) {
- if (mdata->xfer_len % 4)
- cnt = mdata->xfer_len / 4 + 1;
- else
- cnt = mdata->xfer_len / 4;
+ cnt = mdata->xfer_len / 4;
ioread32_rep(mdata->base + SPI_RX_DATA_REG,
trans->rx_buf, cnt);
+ remainder = mdata->xfer_len % 4;
+ if (remainder > 0) {
+ reg_val = readl(mdata->base + SPI_RX_DATA_REG);
+ memcpy(trans->rx_buf + (cnt * 4),
+ &reg_val, remainder);
+ }
}
spi_finalize_current_transfer(master);
return IRQ_HANDLED;
@@ -610,7 +623,8 @@ static int mtk_spi_probe(struct platform_device *pdev)
ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk);
if (ret < 0) {
dev_err(&pdev->dev, "failed to clk_set_parent (%d)\n", ret);
- goto err_disable_clk;
+ clk_disable_unprepare(mdata->spi_clk);
+ goto err_put_master;
}
clk_disable_unprepare(mdata->spi_clk);
@@ -620,7 +634,7 @@ static int mtk_spi_probe(struct platform_device *pdev)
ret = devm_spi_register_master(&pdev->dev, master);
if (ret) {
dev_err(&pdev->dev, "failed to register master (%d)\n", ret);
- goto err_put_master;
+ goto err_disable_runtime_pm;
}
if (mdata->dev_comp->need_pad_sel) {
@@ -629,24 +643,34 @@ static int mtk_spi_probe(struct platform_device *pdev)
"pad_num does not match num_chipselect(%d != %d)\n",
mdata->pad_num, master->num_chipselect);
ret = -EINVAL;
- goto err_put_master;
+ goto err_disable_runtime_pm;
}
- for (i = 0; i < master->num_chipselect; i++) {
- ret = devm_gpio_request(&pdev->dev, master->cs_gpios[i],
- dev_name(&pdev->dev));
- if (ret) {
- dev_err(&pdev->dev,
- "can't get CS GPIO %i\n", i);
- goto err_put_master;
+ if (!master->cs_gpios && master->num_chipselect > 1) {
+ dev_err(&pdev->dev,
+ "cs_gpios not specified and num_chipselect > 1\n");
+ ret = -EINVAL;
+ goto err_disable_runtime_pm;
+ }
+
+ if (master->cs_gpios) {
+ for (i = 0; i < master->num_chipselect; i++) {
+ ret = devm_gpio_request(&pdev->dev,
+ master->cs_gpios[i],
+ dev_name(&pdev->dev));
+ if (ret) {
+ dev_err(&pdev->dev,
+ "can't get CS GPIO %i\n", i);
+ goto err_disable_runtime_pm;
+ }
}
}
}
return 0;
-err_disable_clk:
- clk_disable_unprepare(mdata->spi_clk);
+err_disable_runtime_pm:
+ pm_runtime_disable(&pdev->dev);
err_put_master:
spi_master_put(master);
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index 1f8903d356e5..7273820275e9 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -24,6 +24,7 @@
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/omap-dma.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/clk.h>
@@ -1024,6 +1025,16 @@ static int omap2_mcspi_setup(struct spi_device *spi)
spi->controller_state = cs;
/* Link this to context save list */
list_add_tail(&cs->node, &ctx->cs);
+
+ 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));
+ }
}
if (!mcspi_dma->dma_rx || !mcspi_dma->dma_tx) {
@@ -1032,15 +1043,6 @@ 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;
@@ -1536,14 +1538,23 @@ static int omap2_mcspi_resume(struct device *dev)
}
pm_runtime_mark_last_busy(mcspi->dev);
pm_runtime_put_autosuspend(mcspi->dev);
- return 0;
+
+ return pinctrl_pm_select_default_state(dev);
+}
+
+static int omap2_mcspi_suspend(struct device *dev)
+{
+ return pinctrl_pm_select_sleep_state(dev);
}
+
#else
+#define omap2_mcspi_suspend NULL
#define omap2_mcspi_resume NULL
#endif
static const struct dev_pm_ops omap2_mcspi_pm_ops = {
.resume = omap2_mcspi_resume,
+ .suspend = omap2_mcspi_suspend,
.runtime_resume = omap_mcspi_runtime_resume,
};
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index 94af80676684..5e5fd77e2711 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -1171,19 +1171,31 @@ err_no_rxchan:
static int pl022_dma_autoprobe(struct pl022 *pl022)
{
struct device *dev = &pl022->adev->dev;
+ struct dma_chan *chan;
+ int err;
/* automatically configure DMA channels from platform, normally using DT */
- pl022->dma_rx_channel = dma_request_slave_channel(dev, "rx");
- if (!pl022->dma_rx_channel)
+ chan = dma_request_slave_channel_reason(dev, "rx");
+ if (IS_ERR(chan)) {
+ err = PTR_ERR(chan);
goto err_no_rxchan;
+ }
+
+ pl022->dma_rx_channel = chan;
- pl022->dma_tx_channel = dma_request_slave_channel(dev, "tx");
- if (!pl022->dma_tx_channel)
+ chan = dma_request_slave_channel_reason(dev, "tx");
+ if (IS_ERR(chan)) {
+ err = PTR_ERR(chan);
goto err_no_txchan;
+ }
+
+ pl022->dma_tx_channel = chan;
pl022->dummypage = kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (!pl022->dummypage)
+ if (!pl022->dummypage) {
+ err = -ENOMEM;
goto err_no_dummypage;
+ }
return 0;
@@ -1194,7 +1206,7 @@ err_no_txchan:
dma_release_channel(pl022->dma_rx_channel);
pl022->dma_rx_channel = NULL;
err_no_rxchan:
- return -ENODEV;
+ return err;
}
static void terminate_dma(struct pl022 *pl022)
@@ -2236,6 +2248,10 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
/* Get DMA channels, try autoconfiguration first */
status = pl022_dma_autoprobe(pl022);
+ if (status == -EPROBE_DEFER) {
+ dev_dbg(dev, "deferring probe to get DMA channel\n");
+ goto err_no_irq;
+ }
/* If that failed, use channels from platform_info */
if (status == 0)
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c
index b25dc71b0ea9..ab9914ad8365 100644
--- a/drivers/spi/spi-pxa2xx.c
+++ b/drivers/spi/spi-pxa2xx.c
@@ -1567,9 +1567,6 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
if (!is_quark_x1000_ssp(drv_data))
pxa2xx_spi_write(drv_data, SSPSP, 0);
- if (is_lpss_ssp(drv_data))
- lpss_ssp_setup(drv_data);
-
if (is_lpss_ssp(drv_data)) {
lpss_ssp_setup(drv_data);
config = lpss_get_config(drv_data);
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index 8e86e7f6663a..5a76a50063b5 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -133,7 +133,6 @@
struct s3c64xx_spi_dma_data {
struct dma_chan *ch;
enum dma_transfer_direction direction;
- unsigned int dmach;
};
/**
@@ -325,7 +324,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
/* Acquire DMA channels */
sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter,
- (void *)(long)sdd->rx_dma.dmach, dev, "rx");
+ sdd->cntrlr_info->dma_rx, dev, "rx");
if (!sdd->rx_dma.ch) {
dev_err(dev, "Failed to get RX DMA channel\n");
ret = -EBUSY;
@@ -334,7 +333,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
spi->dma_rx = sdd->rx_dma.ch;
sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter,
- (void *)(long)sdd->tx_dma.dmach, dev, "tx");
+ sdd->cntrlr_info->dma_tx, dev, "tx");
if (!sdd->tx_dma.ch) {
dev_err(dev, "Failed to get TX DMA channel\n");
ret = -EBUSY;
@@ -1028,7 +1027,6 @@ static inline struct s3c64xx_spi_port_config *s3c64xx_spi_get_port_config(
static int s3c64xx_spi_probe(struct platform_device *pdev)
{
struct resource *mem_res;
- struct resource *res;
struct s3c64xx_spi_driver_data *sdd;
struct s3c64xx_spi_info *sci = dev_get_platdata(&pdev->dev);
struct spi_master *master;
@@ -1087,20 +1085,9 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
sdd->cur_bpw = 8;
- if (!sdd->pdev->dev.of_node) {
- res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!res) {
- dev_warn(&pdev->dev, "Unable to get SPI tx dma resource. Switching to poll mode\n");
- sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL;
- } else
- sdd->tx_dma.dmach = res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (!res) {
- dev_warn(&pdev->dev, "Unable to get SPI rx dma resource. Switching to poll mode\n");
- sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL;
- } else
- sdd->rx_dma.dmach = res->start;
+ if (!sdd->pdev->dev.of_node && (!sci->dma_tx || !sci->dma_rx)) {
+ dev_warn(&pdev->dev, "Unable to get SPI tx/rx DMA data. Switching to poll mode\n");
+ sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL;
}
sdd->tx_dma.direction = DMA_MEM_TO_DEV;
@@ -1197,9 +1184,9 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n",
sdd->port_id, master->num_chipselect);
- dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes\tDMA=[Rx-%d, Tx-%d]\n",
+ dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes\tDMA=[Rx-%p, Tx-%p]\n",
mem_res, (FIFO_LVL_MASK(sdd) >> 1) + 1,
- sdd->rx_dma.dmach, sdd->tx_dma.dmach);
+ sci->dma_rx, sci->dma_tx);
pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
@@ -1370,12 +1357,6 @@ static const struct platform_device_id s3c64xx_spi_driver_ids[] = {
}, {
.name = "s3c6410-spi",
.driver_data = (kernel_ulong_t)&s3c6410_spi_port_config,
- }, {
- .name = "s5pv210-spi",
- .driver_data = (kernel_ulong_t)&s5pv210_spi_port_config,
- }, {
- .name = "exynos4210-spi",
- .driver_data = (kernel_ulong_t)&exynos4_spi_port_config,
},
{ },
};
diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c
index fbb0a4d74e91..1ddd9e2309b6 100644
--- a/drivers/spi/spi-sun4i.c
+++ b/drivers/spi/spi-sun4i.c
@@ -140,6 +140,9 @@ static void sun4i_spi_set_cs(struct spi_device *spi, bool enable)
reg &= ~SUN4I_CTL_CS_MASK;
reg |= SUN4I_CTL_CS(spi->chip_select);
+ /* We want to control the chip select manually */
+ reg |= SUN4I_CTL_CS_MANUAL;
+
if (enable)
reg |= SUN4I_CTL_CS_LEVEL;
else
@@ -222,15 +225,12 @@ static int sun4i_spi_transfer_one(struct spi_master *master,
else
reg |= SUN4I_CTL_DHB;
- /* We want to control the chip select manually */
- reg |= SUN4I_CTL_CS_MANUAL;
-
sun4i_spi_write(sspi, SUN4I_CTL_REG, reg);
/* Ensure that we have a parent clock fast enough */
mclk_rate = clk_get_rate(sspi->mclk);
- if (mclk_rate < (2 * spi->max_speed_hz)) {
- clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz);
+ if (mclk_rate < (2 * tfr->speed_hz)) {
+ clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
mclk_rate = clk_get_rate(sspi->mclk);
}
@@ -248,14 +248,14 @@ static int sun4i_spi_transfer_one(struct spi_master *master,
* First try CDR2, and if we can't reach the expected
* frequency, fall back to CDR1.
*/
- div = mclk_rate / (2 * spi->max_speed_hz);
+ div = mclk_rate / (2 * tfr->speed_hz);
if (div <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) {
if (div > 0)
div--;
reg = SUN4I_CLK_CTL_CDR2(div) | SUN4I_CLK_CTL_DRS;
} else {
- div = ilog2(mclk_rate) - ilog2(spi->max_speed_hz);
+ div = ilog2(mclk_rate) - ilog2(tfr->speed_hz);
reg = SUN4I_CLK_CTL_CDR1(div);
}
diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c
index ac48f59705a8..42e2c4bd690a 100644
--- a/drivers/spi/spi-sun6i.c
+++ b/drivers/spi/spi-sun6i.c
@@ -217,8 +217,8 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
/* Ensure that we have a parent clock fast enough */
mclk_rate = clk_get_rate(sspi->mclk);
- if (mclk_rate < (2 * spi->max_speed_hz)) {
- clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz);
+ if (mclk_rate < (2 * tfr->speed_hz)) {
+ clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
mclk_rate = clk_get_rate(sspi->mclk);
}
@@ -236,14 +236,14 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
* First try CDR2, and if we can't reach the expected
* frequency, fall back to CDR1.
*/
- div = mclk_rate / (2 * spi->max_speed_hz);
+ div = mclk_rate / (2 * tfr->speed_hz);
if (div <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
if (div > 0)
div--;
reg = SUN6I_CLK_CTL_CDR2(div) | SUN6I_CLK_CTL_DRS;
} else {
- div = ilog2(mclk_rate) - ilog2(spi->max_speed_hz);
+ div = ilog2(mclk_rate) - ilog2(tfr->speed_hz);
reg = SUN6I_CLK_CTL_CDR1(div);
}
diff --git a/drivers/spi/spi-test.h b/drivers/spi/spi-test.h
new file mode 100644
index 000000000000..922c52833239
--- /dev/null
+++ b/drivers/spi/spi-test.h
@@ -0,0 +1,136 @@
+/*
+ * linux/drivers/spi/spi-test.h
+ *
+ * (c) Martin Sperl <kernel@martin.sperl.org>
+ *
+ * spi_test definitions
+ *
+ * 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/spi/spi.h>
+
+#define SPI_TEST_MAX_TRANSFERS 4
+#define SPI_TEST_MAX_SIZE (32 * PAGE_SIZE)
+#define SPI_TEST_MAX_ITERATE 32
+
+/* the "dummy" start addresses used in spi_test
+ * these addresses get translated at a later stage
+ */
+#define RX_START BIT(30)
+#define TX_START BIT(31)
+#define RX(off) ((void *)(RX_START + off))
+#define TX(off) ((void *)(TX_START + off))
+
+/* some special defines for offsets */
+#define SPI_TEST_MAX_SIZE_HALF BIT(29)
+
+/* detection pattern for unfinished reads...
+ * - 0x00 or 0xff could be valid levels for tx_buf = NULL,
+ * so we do not use either of them
+ */
+#define SPI_TEST_PATTERN_UNWRITTEN 0xAA
+#define SPI_TEST_PATTERN_DO_NOT_WRITE 0x55
+#define SPI_TEST_CHECK_DO_NOT_WRITE 64
+
+/**
+ * struct spi_test - describes a specific (set of) tests to execute
+ *
+ * @description: description of the test
+ *
+ * @msg: a template @spi_message usedfor the default settings
+ * @transfers: array of @spi_transfers that are part of the
+ * resulting spi_message. The first transfer with len == 0
+ * signifies the end of the list
+ * @transfer_count: normally computed number of transfers with len > 0
+ *
+ * @run_test: run a specific spi_test - this allows to override
+ * the default implementation of @spi_test_run_transfer
+ * either to add some custom filters for a specific test
+ * or to effectively run some very custom tests...
+ * @execute_msg: run the spi_message for real - this allows to override
+ * @spi_test_execute_msg to apply final modifications
+ * on the spi_message
+ * @expected_return: the expected return code - in some cases we want to
+ * test also for error conditions
+ *
+ * @iterate_len: list of length to iterate on (in addition to the
+ * explicitly set @spi_transfer.len)
+ * @iterate_tx_align: change the alignment of @spi_transfer.tx_buf
+ * for all values in the below range if set.
+ * the ranges are:
+ * [0 : @spi_master.dma_alignment[ if set
+ * [0 : iterate_tx_align[ if unset
+ * @iterate_rx_align: change the alignment of @spi_transfer.rx_buf
+ * see @iterate_tx_align for details
+ * @iterate_transfer_mask: the bitmask of transfers to which the iterations
+ * apply - if 0, then it applies to all transfer
+ *
+ * @fill_option: define the way how tx_buf is filled
+ * @fill_pattern: fill pattern to apply to the tx_buf
+ * (used in some of the @fill_options)
+ */
+
+struct spi_test {
+ char description[64];
+ struct spi_message msg;
+ struct spi_transfer transfers[SPI_TEST_MAX_TRANSFERS];
+ unsigned int transfer_count;
+ int (*run_test)(struct spi_device *spi, struct spi_test *test,
+ void *tx, void *rx);
+ int (*execute_msg)(struct spi_device *spi, struct spi_test *test,
+ void *tx, void *rx);
+ int expected_return;
+ /* iterate over all the non-zero values */
+ int iterate_len[SPI_TEST_MAX_ITERATE];
+ int iterate_tx_align;
+ int iterate_rx_align;
+ u32 iterate_transfer_mask;
+ /* the tx-fill operation */
+ u32 fill_option;
+#define FILL_MEMSET_8 0 /* just memset with 8 bit */
+#define FILL_MEMSET_16 1 /* just memset with 16 bit */
+#define FILL_MEMSET_24 2 /* just memset with 24 bit */
+#define FILL_MEMSET_32 3 /* just memset with 32 bit */
+#define FILL_COUNT_8 4 /* fill with a 8 byte counter */
+#define FILL_COUNT_16 5 /* fill with a 16 bit counter */
+#define FILL_COUNT_24 6 /* fill with a 24 bit counter */
+#define FILL_COUNT_32 7 /* fill with a 32 bit counter */
+#define FILL_TRANSFER_BYTE_8 8 /* fill with the transfer byte - 8 bit */
+#define FILL_TRANSFER_BYTE_16 9 /* fill with the transfer byte - 16 bit */
+#define FILL_TRANSFER_BYTE_24 10 /* fill with the transfer byte - 24 bit */
+#define FILL_TRANSFER_BYTE_32 11 /* fill with the transfer byte - 32 bit */
+#define FILL_TRANSFER_NUM 16 /* fill with the transfer number */
+ u32 fill_pattern;
+};
+
+/* default implementation for @spi_test.run_test */
+int spi_test_run_test(struct spi_device *spi,
+ const struct spi_test *test,
+ void *tx, void *rx);
+
+/* default implementation for @spi_test.execute_msg */
+int spi_test_execute_msg(struct spi_device *spi,
+ struct spi_test *test,
+ void *tx, void *rx);
+
+/* function to execute a set of tests */
+int spi_test_run_tests(struct spi_device *spi,
+ struct spi_test *tests);
+
+/* some of the default @spi_transfer.len to test */
+#define ITERATE_LEN 2, 3, 7, 11, 16, 31, 32, 64, 97, 128, 251, 256, \
+ 1021, 1024, 1031, 4093, PAGE_SIZE, 4099, 65536, 65537
+
+#define ITERATE_MAX_LEN ITERATE_LEN, SPI_TEST_MAX_SIZE - 1, SPI_TEST_MAX_SIZE
+
+/* the default alignment to test */
+#define ITERATE_ALIGN sizeof(int)
diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c
index f23f36ebaf3d..aab9b492c627 100644
--- a/drivers/spi/spi-zynqmp-gqspi.c
+++ b/drivers/spi/spi-zynqmp-gqspi.c
@@ -917,9 +917,7 @@ static int zynqmp_qspi_start_transfer(struct spi_master *master,
*/
static int __maybe_unused zynqmp_qspi_suspend(struct device *dev)
{
- struct platform_device *pdev = container_of(dev,
- struct platform_device,
- dev);
+ struct platform_device *pdev = to_platform_device(dev);
struct spi_master *master = platform_get_drvdata(pdev);
spi_master_suspend(master);
@@ -940,9 +938,7 @@ static int __maybe_unused zynqmp_qspi_suspend(struct device *dev)
*/
static int __maybe_unused zynqmp_qspi_resume(struct device *dev)
{
- struct platform_device *pdev = container_of(dev,
- struct platform_device,
- dev);
+ struct platform_device *pdev = to_platform_device(dev);
struct spi_master *master = platform_get_drvdata(pdev);
struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
int ret = 0;
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index e2415be209d5..47eff8012a77 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -84,8 +84,7 @@ static ssize_t spi_device_##field##_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
- struct spi_device *spi = container_of(dev, \
- struct spi_device, dev); \
+ struct spi_device *spi = to_spi_device(dev); \
return spi_statistics_##field##_show(&spi->statistics, buf); \
} \
static struct device_attribute dev_attr_spi_device_##field = { \
@@ -376,6 +375,7 @@ static void spi_drv_shutdown(struct device *dev)
/**
* __spi_register_driver - register a SPI driver
+ * @owner: owner module of the driver to register
* @sdrv: the driver to register
* Context: can sleep
*
@@ -604,6 +604,24 @@ struct spi_device *spi_new_device(struct spi_master *master,
}
EXPORT_SYMBOL_GPL(spi_new_device);
+/**
+ * spi_unregister_device - unregister a single SPI device
+ * @spi: spi_device to unregister
+ *
+ * Start making the passed SPI device vanish. Normally this would be handled
+ * by spi_unregister_master().
+ */
+void spi_unregister_device(struct spi_device *spi)
+{
+ if (!spi)
+ return;
+
+ if (spi->dev.of_node)
+ of_node_clear_flag(spi->dev.of_node, OF_POPULATED);
+ device_unregister(&spi->dev);
+}
+EXPORT_SYMBOL_GPL(spi_unregister_device);
+
static void spi_match_master_to_boardinfo(struct spi_master *master,
struct spi_board_info *bi)
{
@@ -1547,6 +1565,8 @@ static void of_register_spi_devices(struct spi_master *master)
return;
for_each_available_child_of_node(master->dev.of_node, nc) {
+ if (of_node_test_and_set_flag(nc, OF_POPULATED))
+ continue;
spi = of_register_spi_device(master, nc);
if (IS_ERR(spi))
dev_warn(&master->dev, "Failed to create SPI device for %s\n",
@@ -1622,6 +1642,9 @@ static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
return AE_OK;
}
+ if (spi->irq < 0)
+ spi->irq = acpi_dev_gpio_irq_get(adev, 0);
+
adev->power.flags.ignore_parent = true;
strlcpy(spi->modalias, acpi_device_hid(adev), sizeof(spi->modalias));
if (spi_add_device(spi)) {
@@ -1704,7 +1727,7 @@ struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
master->bus_num = -1;
master->num_chipselect = 1;
master->dev.class = &spi_master_class;
- master->dev.parent = get_device(dev);
+ master->dev.parent = dev;
spi_master_set_devdata(master, &master[1]);
return master;
@@ -2130,6 +2153,7 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message)
* Set transfer tx_nbits and rx_nbits as single transfer default
* (SPI_NBITS_SINGLE) if it is not set for this transfer.
*/
+ message->frame_length = 0;
list_for_each_entry(xfer, &message->transfers, transfer_list) {
message->frame_length += xfer->len;
if (!xfer->bits_per_word)
@@ -2631,6 +2655,11 @@ static int of_spi_notify(struct notifier_block *nb, unsigned long action,
if (master == NULL)
return NOTIFY_OK; /* not for us */
+ if (of_node_test_and_set_flag(rd->dn, OF_POPULATED)) {
+ put_device(&master->dev);
+ return NOTIFY_OK;
+ }
+
spi = of_register_spi_device(master, rd->dn);
put_device(&master->dev);
@@ -2642,6 +2671,10 @@ static int of_spi_notify(struct notifier_block *nb, unsigned long action,
break;
case OF_RECONFIG_CHANGE_REMOVE:
+ /* already depopulated? */
+ if (!of_node_check_flag(rd->dn, OF_POPULATED))
+ return NOTIFY_OK;
+
/* find our device by node */
spi = of_find_spi_device_by_node(rd->dn);
if (spi == NULL)
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 91a0fcd72423..e3c19f30f591 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -284,7 +284,7 @@ static int spidev_message(struct spidev_data *spidev,
k_tmp->speed_hz = spidev->speed_hz;
#ifdef VERBOSE
dev_dbg(&spidev->spi->dev,
- " xfer len %zd %s%s%s%dbits %u usec %uHz\n",
+ " xfer len %u %s%s%s%dbits %u usec %uHz\n",
u_tmp->len,
u_tmp->rx_buf ? "rx " : "",
u_tmp->tx_buf ? "tx " : "",
@@ -651,11 +651,11 @@ static int spidev_release(struct inode *inode, struct file *filp)
kfree(spidev->rx_buffer);
spidev->rx_buffer = NULL;
+ spin_lock_irq(&spidev->spi_lock);
if (spidev->spi)
spidev->speed_hz = spidev->spi->max_speed_hz;
/* ... after we unbound from the underlying device? */
- spin_lock_irq(&spidev->spi_lock);
dofree = (spidev->spi == NULL);
spin_unlock_irq(&spidev->spi_lock);
diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig
index 149214beeda9..0c675861623f 100644
--- a/drivers/ssb/Kconfig
+++ b/drivers/ssb/Kconfig
@@ -82,7 +82,7 @@ config SSB_SDIOHOST
config SSB_HOST_SOC
bool "Support for SSB bus on SoC"
- depends on SSB
+ depends on SSB && BCM47XX_NVRAM
help
Host interface for a SSB directly mapped into memory. This is
for some Broadcom SoCs from the BCM47xx and BCM53xx lines.
diff --git a/drivers/ssb/host_soc.c b/drivers/ssb/host_soc.c
index c809f255af34..d62992dc08b2 100644
--- a/drivers/ssb/host_soc.c
+++ b/drivers/ssb/host_soc.c
@@ -8,6 +8,7 @@
* Licensed under the GNU/GPL. See COPYING for details.
*/
+#include <linux/bcm47xx_nvram.h>
#include <linux/ssb/ssb.h>
#include "ssb_private.h"
@@ -171,3 +172,39 @@ const struct ssb_bus_ops ssb_host_soc_ops = {
.block_write = ssb_host_soc_block_write,
#endif
};
+
+int ssb_host_soc_get_invariants(struct ssb_bus *bus,
+ struct ssb_init_invariants *iv)
+{
+ char buf[20];
+ int len, err;
+
+ /* Fill boardinfo structure */
+ memset(&iv->boardinfo, 0, sizeof(struct ssb_boardinfo));
+
+ len = bcm47xx_nvram_getenv("boardvendor", buf, sizeof(buf));
+ if (len > 0) {
+ err = kstrtou16(strim(buf), 0, &iv->boardinfo.vendor);
+ if (err)
+ pr_warn("Couldn't parse nvram board vendor entry with value \"%s\"\n",
+ buf);
+ }
+ if (!iv->boardinfo.vendor)
+ iv->boardinfo.vendor = SSB_BOARDVENDOR_BCM;
+
+ len = bcm47xx_nvram_getenv("boardtype", buf, sizeof(buf));
+ if (len > 0) {
+ err = kstrtou16(strim(buf), 0, &iv->boardinfo.type);
+ if (err)
+ pr_warn("Couldn't parse nvram board type entry with value \"%s\"\n",
+ buf);
+ }
+
+ memset(&iv->sprom, 0, sizeof(struct ssb_sprom));
+ ssb_fill_sprom_with_fallback(bus, &iv->sprom);
+
+ if (bcm47xx_nvram_getenv("cardbus", buf, sizeof(buf)) >= 0)
+ iv->has_cardbus_slot = !!simple_strtoul(buf, NULL, 10);
+
+ return 0;
+}
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c
index 5d1e9a0fc389..cde5ff7529eb 100644
--- a/drivers/ssb/main.c
+++ b/drivers/ssb/main.c
@@ -762,15 +762,14 @@ EXPORT_SYMBOL(ssb_bus_sdiobus_register);
#endif /* CONFIG_SSB_PCMCIAHOST */
#ifdef CONFIG_SSB_HOST_SOC
-int ssb_bus_ssbbus_register(struct ssb_bus *bus, unsigned long baseaddr,
- ssb_invariants_func_t get_invariants)
+int ssb_bus_host_soc_register(struct ssb_bus *bus, unsigned long baseaddr)
{
int err;
bus->bustype = SSB_BUSTYPE_SSB;
bus->ops = &ssb_host_soc_ops;
- err = ssb_bus_register(bus, get_invariants, baseaddr);
+ err = ssb_bus_register(bus, ssb_host_soc_get_invariants, baseaddr);
if (!err) {
ssb_info("Sonics Silicon Backplane found at address 0x%08lX\n",
baseaddr);
diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h
index 15bfd5c7d2d7..c2f5d3969c8b 100644
--- a/drivers/ssb/ssb_private.h
+++ b/drivers/ssb/ssb_private.h
@@ -163,6 +163,9 @@ static inline int ssb_sdio_init(struct ssb_bus *bus)
#ifdef CONFIG_SSB_HOST_SOC
extern const struct ssb_bus_ops ssb_host_soc_ops;
+
+extern int ssb_host_soc_get_invariants(struct ssb_bus *bus,
+ struct ssb_init_invariants *iv);
#endif
/* scan.c */
diff --git a/drivers/staging/android/TODO b/drivers/staging/android/TODO
index 8f3ac37bfe12..64d8c8720960 100644
--- a/drivers/staging/android/TODO
+++ b/drivers/staging/android/TODO
@@ -25,5 +25,13 @@ ion/
exposes existing cma regions and doesn't reserve unecessarily memory when
booting a system which doesn't use ion.
+sync framework:
+ - remove CONFIG_SW_SYNC_USER, it is used only for testing/debugging and
+ should not be upstreamed.
+ - port CONFIG_SW_SYNC_USER tests interfaces to use debugfs somehow
+ - port libsync tests to kselftest
+ - clean up and ABI check for security issues
+ - move it to drivers/base/dma-buf
+
Please send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc:
Arve Hjønnevåg <arve@android.com> and Riley Andrews <riandrews@android.com>
diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c
index 3f2a3d611e4b..5bb1283d19cd 100644
--- a/drivers/staging/android/ashmem.c
+++ b/drivers/staging/android/ashmem.c
@@ -831,14 +831,14 @@ static struct miscdevice ashmem_misc = {
static int __init ashmem_init(void)
{
- int ret;
+ int ret = -ENOMEM;
ashmem_area_cachep = kmem_cache_create("ashmem_area_cache",
sizeof(struct ashmem_area),
0, 0, NULL);
if (unlikely(!ashmem_area_cachep)) {
pr_err("failed to create slab cache\n");
- return -ENOMEM;
+ goto out;
}
ashmem_range_cachep = kmem_cache_create("ashmem_range_cache",
@@ -846,13 +846,13 @@ static int __init ashmem_init(void)
0, 0, NULL);
if (unlikely(!ashmem_range_cachep)) {
pr_err("failed to create slab cache\n");
- return -ENOMEM;
+ goto out_free1;
}
ret = misc_register(&ashmem_misc);
if (unlikely(ret)) {
pr_err("failed to register misc device!\n");
- return ret;
+ goto out_free2;
}
register_shrinker(&ashmem_shrinker);
@@ -860,5 +860,12 @@ static int __init ashmem_init(void)
pr_info("initialized\n");
return 0;
+
+out_free2:
+ kmem_cache_destroy(ashmem_range_cachep);
+out_free1:
+ kmem_cache_destroy(ashmem_area_cachep);
+out:
+ return ret;
}
device_initcall(ashmem_init);
diff --git a/drivers/staging/android/ion/Kconfig b/drivers/staging/android/ion/Kconfig
index 345234624492..19c1572f1525 100644
--- a/drivers/staging/android/ion/Kconfig
+++ b/drivers/staging/android/ion/Kconfig
@@ -33,3 +33,10 @@ config ION_TEGRA
help
Choose this option if you wish to use ion on an nVidia Tegra.
+config ION_HISI
+ tristate "Ion for Hisilicon"
+ depends on ARCH_HISI && ION
+ help
+ Choose this option if you wish to use ion on Hisilicon Platform.
+
+source "drivers/staging/android/ion/hisilicon/Kconfig"
diff --git a/drivers/staging/android/ion/Makefile b/drivers/staging/android/ion/Makefile
index b56fd2bf2b4f..18cc2aa593c2 100644
--- a/drivers/staging/android/ion/Makefile
+++ b/drivers/staging/android/ion/Makefile
@@ -7,4 +7,5 @@ endif
obj-$(CONFIG_ION_DUMMY) += ion_dummy_driver.o
obj-$(CONFIG_ION_TEGRA) += tegra/
+obj-$(CONFIG_ION_HISI) += hisilicon/
diff --git a/drivers/staging/android/ion/compat_ion.c b/drivers/staging/android/ion/compat_ion.c
index a402fdaf54ca..9a978d21785e 100644
--- a/drivers/staging/android/ion/compat_ion.c
+++ b/drivers/staging/android/ion/compat_ion.c
@@ -137,7 +137,7 @@ long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
data32 = compat_ptr(arg);
data = compat_alloc_user_space(sizeof(*data));
- if (data == NULL)
+ if (!data)
return -EFAULT;
err = compat_get_ion_allocation_data(data32, data);
@@ -156,7 +156,7 @@ long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
data32 = compat_ptr(arg);
data = compat_alloc_user_space(sizeof(*data));
- if (data == NULL)
+ if (!data)
return -EFAULT;
err = compat_get_ion_handle_data(data32, data);
@@ -173,7 +173,7 @@ long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
data32 = compat_ptr(arg);
data = compat_alloc_user_space(sizeof(*data));
- if (data == NULL)
+ if (!data)
return -EFAULT;
err = compat_get_ion_custom_data(data32, data);
diff --git a/drivers/staging/android/ion/hisilicon/Kconfig b/drivers/staging/android/ion/hisilicon/Kconfig
new file mode 100644
index 000000000000..2b4bd0798290
--- /dev/null
+++ b/drivers/staging/android/ion/hisilicon/Kconfig
@@ -0,0 +1,5 @@
+config HI6220_ION
+ bool "Hi6220 ION Driver"
+ depends on ARCH_HISI && ION
+ help
+ Build the Hisilicon Hi6220 ion driver.
diff --git a/drivers/staging/android/ion/hisilicon/Makefile b/drivers/staging/android/ion/hisilicon/Makefile
new file mode 100644
index 000000000000..2a89414280ac
--- /dev/null
+++ b/drivers/staging/android/ion/hisilicon/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_HI6220_ION) += hi6220_ion.o
diff --git a/drivers/staging/android/ion/hisilicon/hi6220_ion.c b/drivers/staging/android/ion/hisilicon/hi6220_ion.c
new file mode 100644
index 000000000000..e3c07b2ba00e
--- /dev/null
+++ b/drivers/staging/android/ion/hisilicon/hi6220_ion.c
@@ -0,0 +1,223 @@
+/*
+ * Hisilicon Hi6220 ION Driver
+ *
+ * Copyright (c) 2015 Hisilicon Limited.
+ *
+ * Author: Chen Feng <puck.chen@hisilicon.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.
+ */
+
+#define pr_fmt(fmt) "Ion: " fmt
+
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/mm.h>
+#include "../ion_priv.h"
+#include "../ion.h"
+
+struct hi6220_ion_type_table {
+ const char *name;
+ enum ion_heap_type type;
+};
+
+static struct hi6220_ion_type_table ion_type_table[] = {
+ {"ion_system", ION_HEAP_TYPE_SYSTEM},
+ {"ion_system_contig", ION_HEAP_TYPE_SYSTEM_CONTIG},
+ {"ion_carveout", ION_HEAP_TYPE_CARVEOUT},
+ {"ion_chunk", ION_HEAP_TYPE_CHUNK},
+ {"ion_dma", ION_HEAP_TYPE_DMA},
+ {"ion_custom", ION_HEAP_TYPE_CUSTOM},
+};
+
+static struct ion_device *idev;
+static int num_heaps;
+static struct ion_heap **heaps;
+static struct ion_platform_heap **heaps_data;
+
+static int get_type_by_name(const char *name, enum ion_heap_type *type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ion_type_table); i++) {
+ if (strncmp(name, ion_type_table[i].name, strlen(name)))
+ continue;
+
+ *type = ion_type_table[i].type;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int hi6220_set_platform_data(struct platform_device *pdev)
+{
+ unsigned int base;
+ unsigned int size;
+ unsigned int id;
+ const char *heap_name;
+ const char *type_name;
+ enum ion_heap_type type;
+ int ret;
+ struct device_node *np;
+ struct ion_platform_heap *p_data;
+ const struct device_node *dt_node = pdev->dev.of_node;
+ int index = 0;
+
+ for_each_child_of_node(dt_node, np)
+ num_heaps++;
+
+ heaps_data = devm_kzalloc(&pdev->dev,
+ sizeof(struct ion_platform_heap *) *
+ num_heaps,
+ GFP_KERNEL);
+ if (!heaps_data)
+ return -ENOMEM;
+
+ for_each_child_of_node(dt_node, np) {
+ ret = of_property_read_string(np, "heap-name", &heap_name);
+ if (ret < 0) {
+ pr_err("check the name of node %s\n", np->name);
+ continue;
+ }
+
+ ret = of_property_read_u32(np, "heap-id", &id);
+ if (ret < 0) {
+ pr_err("check the id %s\n", np->name);
+ continue;
+ }
+
+ ret = of_property_read_u32(np, "heap-base", &base);
+ if (ret < 0) {
+ pr_err("check the base of node %s\n", np->name);
+ continue;
+ }
+
+ ret = of_property_read_u32(np, "heap-size", &size);
+ if (ret < 0) {
+ pr_err("check the size of node %s\n", np->name);
+ continue;
+ }
+
+ ret = of_property_read_string(np, "heap-type", &type_name);
+ if (ret < 0) {
+ pr_err("check the type of node %s\n", np->name);
+ continue;
+ }
+
+ ret = get_type_by_name(type_name, &type);
+ if (ret < 0) {
+ pr_err("type name error %s!\n", type_name);
+ continue;
+ }
+ pr_info("heap index %d : name %s base 0x%x size 0x%x id %d type %d\n",
+ index, heap_name, base, size, id, type);
+
+ p_data = devm_kzalloc(&pdev->dev,
+ sizeof(struct ion_platform_heap),
+ GFP_KERNEL);
+ if (!p_data)
+ return -ENOMEM;
+
+ p_data->name = heap_name;
+ p_data->base = base;
+ p_data->size = size;
+ p_data->id = id;
+ p_data->type = type;
+
+ heaps_data[index] = p_data;
+ index++;
+ }
+ return 0;
+}
+
+static int hi6220_ion_probe(struct platform_device *pdev)
+{
+ int i;
+ int err;
+ static struct ion_platform_heap *p_heap;
+
+ idev = ion_device_create(NULL);
+ err = hi6220_set_platform_data(pdev);
+ if (err) {
+ pr_err("ion set platform data error!\n");
+ goto err_free_idev;
+ }
+ heaps = devm_kzalloc(&pdev->dev,
+ sizeof(struct ion_heap *) * num_heaps,
+ GFP_KERNEL);
+ if (!heaps) {
+ err = -ENOMEM;
+ goto err_free_idev;
+ }
+
+ /*
+ * create the heaps as specified in the dts file
+ */
+ for (i = 0; i < num_heaps; i++) {
+ p_heap = heaps_data[i];
+ heaps[i] = ion_heap_create(p_heap);
+ if (IS_ERR_OR_NULL(heaps[i])) {
+ err = PTR_ERR(heaps[i]);
+ goto err_free_heaps;
+ }
+
+ ion_device_add_heap(idev, heaps[i]);
+
+ pr_info("%s: adding heap %s of type %d with %lx@%lx\n",
+ __func__, p_heap->name, p_heap->type,
+ p_heap->base, (unsigned long)p_heap->size);
+ }
+ return err;
+
+err_free_heaps:
+ for (i = 0; i < num_heaps; ++i) {
+ ion_heap_destroy(heaps[i]);
+ heaps[i] = NULL;
+ }
+err_free_idev:
+ ion_device_destroy(idev);
+
+ return err;
+}
+
+static int hi6220_ion_remove(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < num_heaps; i++) {
+ ion_heap_destroy(heaps[i]);
+ heaps[i] = NULL;
+ }
+ ion_device_destroy(idev);
+
+ return 0;
+}
+
+static const struct of_device_id hi6220_ion_match_table[] = {
+ {.compatible = "hisilicon,hi6220-ion"},
+ {},
+};
+
+static struct platform_driver hi6220_ion_driver = {
+ .probe = hi6220_ion_probe,
+ .remove = hi6220_ion_remove,
+ .driver = {
+ .name = "ion-hi6220",
+ .of_match_table = hi6220_ion_match_table,
+ },
+};
+
+static int __init hi6220_ion_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&hi6220_ion_driver);
+ return ret;
+}
+
+subsys_initcall(hi6220_ion_init);
diff --git a/drivers/staging/android/ion/ion_chunk_heap.c b/drivers/staging/android/ion/ion_chunk_heap.c
index 195c41d7bd53..0813163f962f 100644
--- a/drivers/staging/android/ion/ion_chunk_heap.c
+++ b/drivers/staging/android/ion/ion_chunk_heap.c
@@ -81,7 +81,7 @@ static int ion_chunk_heap_allocate(struct ion_heap *heap,
err:
sg = table->sgl;
for (i -= 1; i >= 0; i--) {
- gen_pool_free(chunk_heap->pool, sg_phys(sg) & PAGE_MASK,
+ gen_pool_free(chunk_heap->pool, page_to_phys(sg_page(sg)),
sg->length);
sg = sg_next(sg);
}
@@ -109,7 +109,7 @@ static void ion_chunk_heap_free(struct ion_buffer *buffer)
DMA_BIDIRECTIONAL);
for_each_sg(table->sgl, sg, table->nents, i) {
- gen_pool_free(chunk_heap->pool, sg_phys(sg) & PAGE_MASK,
+ gen_pool_free(chunk_heap->pool, page_to_phys(sg_page(sg)),
sg->length);
}
chunk_heap->allocated -= allocated_size;
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index e679d8432810..8b5a4a82d8b8 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -43,7 +43,7 @@
#include <linux/profile.h>
#include <linux/notifier.h>
-static uint32_t lowmem_debug_level = 1;
+static u32 lowmem_debug_level = 1;
static short lowmem_adj[6] = {
0,
1,
@@ -105,8 +105,8 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
}
lowmem_print(3, "lowmem_scan %lu, %x, ofree %d %d, ma %hd\n",
- sc->nr_to_scan, sc->gfp_mask, other_free,
- other_file, min_score_adj);
+ sc->nr_to_scan, sc->gfp_mask, other_free,
+ other_file, min_score_adj);
if (min_score_adj == OOM_SCORE_ADJ_MAX + 1) {
lowmem_print(5, "lowmem_scan %lu, %x, return 0\n",
diff --git a/drivers/staging/android/sync.c b/drivers/staging/android/sync.c
index f83e00c78051..ed43796b5b58 100644
--- a/drivers/staging/android/sync.c
+++ b/drivers/staging/android/sync.c
@@ -43,7 +43,7 @@ struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops,
return NULL;
obj = kzalloc(size, GFP_KERNEL);
- if (obj == NULL)
+ if (!obj)
return NULL;
kref_init(&obj->kref);
@@ -130,7 +130,7 @@ struct sync_pt *sync_pt_create(struct sync_timeline *obj, int size)
return NULL;
pt = kzalloc(size, GFP_KERNEL);
- if (pt == NULL)
+ if (!pt)
return NULL;
spin_lock_irqsave(&obj->child_list_lock, flags);
@@ -155,7 +155,7 @@ static struct sync_fence *sync_fence_alloc(int size, const char *name)
struct sync_fence *fence;
fence = kzalloc(size, GFP_KERNEL);
- if (fence == NULL)
+ if (!fence)
return NULL;
fence->file = anon_inode_getfile("sync_fence", &sync_fence_fops,
@@ -188,34 +188,39 @@ static void fence_check_cb_func(struct fence *f, struct fence_cb *cb)
}
/* TODO: implement a create which takes more that one sync_pt */
-struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt)
+struct sync_fence *sync_fence_create_dma(const char *name, struct fence *pt)
{
struct sync_fence *fence;
fence = sync_fence_alloc(offsetof(struct sync_fence, cbs[1]), name);
- if (fence == NULL)
+ if (!fence)
return NULL;
fence->num_fences = 1;
atomic_set(&fence->status, 1);
- fence->cbs[0].sync_pt = &pt->base;
+ fence->cbs[0].sync_pt = pt;
fence->cbs[0].fence = fence;
- if (fence_add_callback(&pt->base, &fence->cbs[0].cb,
- fence_check_cb_func))
+ if (fence_add_callback(pt, &fence->cbs[0].cb, fence_check_cb_func))
atomic_dec(&fence->status);
sync_fence_debug_add(fence);
return fence;
}
+EXPORT_SYMBOL(sync_fence_create_dma);
+
+struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt)
+{
+ return sync_fence_create_dma(name, &pt->base);
+}
EXPORT_SYMBOL(sync_fence_create);
struct sync_fence *sync_fence_fdget(int fd)
{
struct file *file = fget(fd);
- if (file == NULL)
+ if (!file)
return NULL;
if (file->f_op != &sync_fence_fops)
@@ -262,7 +267,7 @@ struct sync_fence *sync_fence_merge(const char *name,
unsigned long size = offsetof(struct sync_fence, cbs[num_fences]);
fence = sync_fence_alloc(size, name);
- if (fence == NULL)
+ if (!fence)
return NULL;
atomic_set(&fence->status, num_fences);
@@ -313,7 +318,7 @@ struct sync_fence *sync_fence_merge(const char *name,
EXPORT_SYMBOL(sync_fence_merge);
int sync_fence_wake_up_wq(wait_queue_t *curr, unsigned mode,
- int wake_flags, void *key)
+ int wake_flags, void *key)
{
struct sync_fence_waiter *wait;
@@ -353,7 +358,7 @@ int sync_fence_wait_async(struct sync_fence *fence,
EXPORT_SYMBOL(sync_fence_wait_async);
int sync_fence_cancel_async(struct sync_fence *fence,
- struct sync_fence_waiter *waiter)
+ struct sync_fence_waiter *waiter)
{
unsigned long flags;
int ret = 0;
@@ -519,12 +524,10 @@ static const struct fence_ops android_fence_ops = {
static void sync_fence_free(struct kref *kref)
{
struct sync_fence *fence = container_of(kref, struct sync_fence, kref);
- int i, status = atomic_read(&fence->status);
+ int i;
for (i = 0; i < fence->num_fences; ++i) {
- if (status)
- fence_remove_callback(fence->cbs[i].sync_pt,
- &fence->cbs[i].cb);
+ fence_remove_callback(fence->cbs[i].sync_pt, &fence->cbs[i].cb);
fence_put(fence->cbs[i].sync_pt);
}
@@ -583,14 +586,14 @@ static long sync_fence_ioctl_merge(struct sync_fence *fence, unsigned long arg)
}
fence2 = sync_fence_fdget(data.fd2);
- if (fence2 == NULL) {
+ if (!fence2) {
err = -ENOENT;
goto err_put_fd;
}
data.name[sizeof(data.name) - 1] = '\0';
fence3 = sync_fence_merge(data.name, fence, fence2);
- if (fence3 == NULL) {
+ if (!fence3) {
err = -ENOMEM;
goto err_put_fence2;
}
@@ -666,7 +669,7 @@ static long sync_fence_ioctl_fence_info(struct sync_fence *fence,
size = 4096;
data = kzalloc(size, GFP_KERNEL);
- if (data == NULL)
+ if (!data)
return -ENOMEM;
strlcpy(data->name, fence->name, sizeof(data->name));
diff --git a/drivers/staging/android/sync.h b/drivers/staging/android/sync.h
index 61f8a3aede96..afa0752275a7 100644
--- a/drivers/staging/android/sync.h
+++ b/drivers/staging/android/sync.h
@@ -254,6 +254,16 @@ void sync_pt_free(struct sync_pt *pt);
*/
struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt);
+/**
+ * sync_fence_create_dma() - creates a sync fence from dma-fence
+ * @name: name of fence to create
+ * @pt: dma-fence to add to the fence
+ *
+ * Creates a fence containg @pt. Once this is called, the fence takes
+ * ownership of @pt.
+ */
+struct sync_fence *sync_fence_create_dma(const char *name, struct fence *pt);
+
/*
* API for sync_fence consumers
*/
diff --git a/drivers/staging/android/sync_debug.c b/drivers/staging/android/sync_debug.c
index 91ed2c4cff45..f45d13cdd42b 100644
--- a/drivers/staging/android/sync_debug.c
+++ b/drivers/staging/android/sync_debug.c
@@ -82,36 +82,42 @@ static const char *sync_status_str(int status)
return "error";
}
-static void sync_print_pt(struct seq_file *s, struct sync_pt *pt, bool fence)
+static void sync_print_pt(struct seq_file *s, struct fence *pt, bool fence)
{
int status = 1;
- struct sync_timeline *parent = sync_pt_parent(pt);
- if (fence_is_signaled_locked(&pt->base))
- status = pt->base.status;
+ if (fence_is_signaled_locked(pt))
+ status = pt->status;
seq_printf(s, " %s%spt %s",
- fence ? parent->name : "",
+ fence && pt->ops->get_timeline_name ?
+ pt->ops->get_timeline_name(pt) : "",
fence ? "_" : "",
sync_status_str(status));
if (status <= 0) {
struct timespec64 ts64 =
- ktime_to_timespec64(pt->base.timestamp);
+ ktime_to_timespec64(pt->timestamp);
seq_printf(s, "@%lld.%09ld", (s64)ts64.tv_sec, ts64.tv_nsec);
}
- if (parent->ops->timeline_value_str &&
- parent->ops->pt_value_str) {
+ if ((!fence || pt->ops->timeline_value_str) &&
+ pt->ops->fence_value_str) {
char value[64];
+ bool success;
- parent->ops->pt_value_str(pt, value, sizeof(value));
- seq_printf(s, ": %s", value);
- if (fence) {
- parent->ops->timeline_value_str(parent, value,
- sizeof(value));
- seq_printf(s, " / %s", value);
+ pt->ops->fence_value_str(pt, value, sizeof(value));
+ success = strlen(value);
+
+ if (success)
+ seq_printf(s, ": %s", value);
+
+ if (success && fence) {
+ pt->ops->timeline_value_str(pt, value, sizeof(value));
+
+ if (strlen(value))
+ seq_printf(s, " / %s", value);
}
}
@@ -138,7 +144,7 @@ static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj)
list_for_each(pos, &obj->child_list_head) {
struct sync_pt *pt =
container_of(pos, struct sync_pt, child_list);
- sync_print_pt(s, pt, false);
+ sync_print_pt(s, &pt->base, false);
}
spin_unlock_irqrestore(&obj->child_list_lock, flags);
}
@@ -153,11 +159,7 @@ static void sync_print_fence(struct seq_file *s, struct sync_fence *fence)
sync_status_str(atomic_read(&fence->status)));
for (i = 0; i < fence->num_fences; ++i) {
- struct sync_pt *pt =
- container_of(fence->cbs[i].sync_pt,
- struct sync_pt, base);
-
- sync_print_pt(s, pt, true);
+ sync_print_pt(s, fence->cbs[i].sync_pt, true);
}
spin_lock_irqsave(&fence->wq.lock, flags);
diff --git a/drivers/staging/android/timed_gpio.c b/drivers/staging/android/timed_gpio.c
index ce11726f1a6c..bcd9924d4631 100644
--- a/drivers/staging/android/timed_gpio.c
+++ b/drivers/staging/android/timed_gpio.c
@@ -25,7 +25,6 @@
#include "timed_output.h"
#include "timed_gpio.h"
-
struct timed_gpio_data {
struct timed_output_dev dev;
struct hrtimer timer;
@@ -76,8 +75,8 @@ static void gpio_enable(struct timed_output_dev *dev, int value)
value = data->max_timeout;
hrtimer_start(&data->timer,
- ktime_set(value / 1000, (value % 1000) * 1000000),
- HRTIMER_MODE_REL);
+ ktime_set(value / 1000, (value % 1000) * 1000000),
+ HRTIMER_MODE_REL);
}
spin_unlock_irqrestore(&data->lock, flags);
@@ -94,8 +93,8 @@ static int timed_gpio_probe(struct platform_device *pdev)
return -EBUSY;
gpio_data = devm_kzalloc(&pdev->dev,
- sizeof(struct timed_gpio_data) * pdata->num_gpios,
- GFP_KERNEL);
+ sizeof(*gpio_data) * pdata->num_gpios,
+ GFP_KERNEL);
if (!gpio_data)
return -ENOMEM;
@@ -104,7 +103,7 @@ static int timed_gpio_probe(struct platform_device *pdev)
gpio_dat = &gpio_data[i];
hrtimer_init(&gpio_dat->timer, CLOCK_MONOTONIC,
- HRTIMER_MODE_REL);
+ HRTIMER_MODE_REL);
gpio_dat->timer.function = gpio_timer_func;
spin_lock_init(&gpio_dat->lock);
diff --git a/drivers/staging/comedi/Kconfig b/drivers/staging/comedi/Kconfig
index ac0f01007abd..e7255f811611 100644
--- a/drivers/staging/comedi/Kconfig
+++ b/drivers/staging/comedi/Kconfig
@@ -737,15 +737,23 @@ config COMEDI_ADL_PCI9118
called adl_pci9118.
config COMEDI_ADV_PCI1710
- tristate "Advantech PCI-171x, PCI-1720 and PCI-1731 support"
+ tristate "Advantech PCI-171x and PCI-1731 support"
select COMEDI_8254
---help---
Enable support for Advantech PCI-1710, PCI-1710HG, PCI-1711,
- PCI-1713, PCI-1720 and PCI-1731
+ PCI-1713 and PCI-1731
To compile this driver as a module, choose M here: the module will be
called adv_pci1710.
+config COMEDI_ADV_PCI1720
+ tristate "Advantech PCI-1720 support"
+ ---help---
+ Enable support for Advantech PCI-1720 Analog Output board.
+
+ To compile this driver as a module, choose M here: the module will be
+ called adv_pci1720.
+
config COMEDI_ADV_PCI1723
tristate "Advantech PCI-1723 support"
---help---
@@ -764,6 +772,14 @@ config COMEDI_ADV_PCI1724
To compile this driver as a module, choose M here: the module will be
called adv_pci1724.
+config COMEDI_ADV_PCI1760
+ tristate "Advantech PCI-1760 support"
+ ---help---
+ Enable support for Advantech PCI-1760 board.
+
+ To compile this driver as a module, choose M here: the module will be
+ called adv_pci1760.
+
config COMEDI_ADV_PCI_DIO
tristate "Advantech PCI DIO card support"
select COMEDI_8254
@@ -771,8 +787,8 @@ config COMEDI_ADV_PCI_DIO
---help---
Enable support for Advantech PCI DIO cards
PCI-1730, PCI-1733, PCI-1734, PCI-1735U, PCI-1736UP, PCI-1739U,
- PCI-1750, PCI-1751, PCI-1752, PCI-1753/E, PCI-1754, PCI-1756,
- PCI-1760 and PCI-1762
+ PCI-1750, PCI-1751, PCI-1752, PCI-1753/E, PCI-1754, PCI-1756 and
+ PCI-1762
To compile this driver as a module, choose M here: the module will be
called adv_pci_dio.
diff --git a/drivers/staging/comedi/comedi.h b/drivers/staging/comedi/comedi.h
index 66edda190b75..83bd309d011b 100644
--- a/drivers/staging/comedi/comedi.h
+++ b/drivers/staging/comedi/comedi.h
@@ -1,20 +1,20 @@
/*
- include/comedi.h (installed as /usr/include/comedi.h)
- header file for comedi
-
- COMEDI - Linux Control and Measurement Device Interface
- Copyright (C) 1998-2001 David A. Schleef <ds@schleef.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser 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/comedi.h (installed as /usr/include/comedi.h)
+ * header file for comedi
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1998-2001 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
#ifndef _COMEDI_H
#define _COMEDI_H
@@ -28,9 +28,9 @@
#define COMEDI_MAJOR 98
/*
- maximum number of minor devices. This can be increased, although
- kernel structures are currently statically allocated, thus you
- don't want this to be much more than you actually use.
+ * maximum number of minor devices. This can be increased, although
+ * kernel structures are currently statically allocated, thus you
+ * don't want this to be much more than you actually use.
*/
#define COMEDI_NDEVICES 16
@@ -63,21 +63,21 @@
/* packs and unpacks a channel/range number */
#define CR_PACK(chan, rng, aref) \
- ((((aref)&0x3)<<24) | (((rng)&0xff)<<16) | (chan))
+ ((((aref) & 0x3) << 24) | (((rng) & 0xff) << 16) | (chan))
#define CR_PACK_FLAGS(chan, range, aref, flags) \
(CR_PACK(chan, range, aref) | ((flags) & CR_FLAGS_MASK))
-#define CR_CHAN(a) ((a)&0xffff)
-#define CR_RANGE(a) (((a)>>16)&0xff)
-#define CR_AREF(a) (((a)>>24)&0x03)
+#define CR_CHAN(a) ((a) & 0xffff)
+#define CR_RANGE(a) (((a) >> 16) & 0xff)
+#define CR_AREF(a) (((a) >> 24) & 0x03)
#define CR_FLAGS_MASK 0xfc000000
-#define CR_ALT_FILTER (1<<26)
+#define CR_ALT_FILTER (1 << 26)
#define CR_DITHER CR_ALT_FILTER
#define CR_DEGLITCH CR_ALT_FILTER
-#define CR_ALT_SOURCE (1<<27)
-#define CR_EDGE (1<<30)
-#define CR_INVERT (1<<31)
+#define CR_ALT_SOURCE (1 << 27)
+#define CR_EDGE (1 << 30)
+#define CR_INVERT (1 << 31)
#define AREF_GROUND 0x00 /* analog ref = analog ground */
#define AREF_COMMON 0x01 /* analog ref = analog common */
@@ -114,11 +114,11 @@
#define INSN_READ (0 | INSN_MASK_READ)
#define INSN_WRITE (1 | INSN_MASK_WRITE)
-#define INSN_BITS (2 | INSN_MASK_READ|INSN_MASK_WRITE)
-#define INSN_CONFIG (3 | INSN_MASK_READ|INSN_MASK_WRITE)
-#define INSN_GTOD (4 | INSN_MASK_READ|INSN_MASK_SPECIAL)
-#define INSN_WAIT (5 | INSN_MASK_WRITE|INSN_MASK_SPECIAL)
-#define INSN_INTTRIG (6 | INSN_MASK_WRITE|INSN_MASK_SPECIAL)
+#define INSN_BITS (2 | INSN_MASK_READ | INSN_MASK_WRITE)
+#define INSN_CONFIG (3 | INSN_MASK_READ | INSN_MASK_WRITE)
+#define INSN_GTOD (4 | INSN_MASK_READ | INSN_MASK_SPECIAL)
+#define INSN_WAIT (5 | INSN_MASK_WRITE | INSN_MASK_SPECIAL)
+#define INSN_INTTRIG (6 | INSN_MASK_WRITE | INSN_MASK_SPECIAL)
/* trigger flags */
/* These flags are used in comedi_trig structures */
@@ -279,7 +279,8 @@ enum configuration_ids {
INSN_CONFIG_SET_OTHER_SRC = 2005, /* Set other source */
/* INSN_CONFIG_GET_OTHER_SRC = 2006,*//* Get other source */
/* Get size in bytes of subdevice's on-board fifos used during
- * streaming input/output */
+ * streaming input/output
+ */
INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE = 2006,
INSN_CONFIG_SET_COUNTER_MODE = 4097,
/* INSN_CONFIG_8254_SET_MODE is deprecated */
@@ -292,7 +293,8 @@ enum configuration_ids {
INSN_CONFIG_PWM_GET_PERIOD = 5001, /* gets frequency */
INSN_CONFIG_GET_PWM_STATUS = 5002, /* is it running? */
/* sets H bridge: duty cycle and sign bit for a relay at the
- * same time */
+ * same time
+ */
INSN_CONFIG_PWM_SET_H_BRIDGE = 5003,
/* gets H bridge data: duty cycle and the sign bit */
INSN_CONFIG_PWM_GET_H_BRIDGE = 5004
@@ -502,13 +504,13 @@ struct comedi_bufinfo {
/* range stuff */
-#define __RANGE(a, b) ((((a)&0xffff)<<16)|((b)&0xffff))
+#define __RANGE(a, b) ((((a) & 0xffff) << 16) | ((b) & 0xffff))
-#define RANGE_OFFSET(a) (((a)>>16)&0xffff)
-#define RANGE_LENGTH(b) ((b)&0xffff)
+#define RANGE_OFFSET(a) (((a) >> 16) & 0xffff)
+#define RANGE_LENGTH(b) ((b) & 0xffff)
-#define RF_UNIT(flags) ((flags)&0xff)
-#define RF_EXTERNAL (1<<8)
+#define RF_UNIT(flags) ((flags) & 0xff)
+#define RF_EXTERNAL (1 << 8)
#define UNIT_volt 0
#define UNIT_mA 1
@@ -521,23 +523,22 @@ struct comedi_bufinfo {
/**********************************************************/
/*
- 8254 specific configuration.
-
- It supports two config commands:
-
- 0 ID: INSN_CONFIG_SET_COUNTER_MODE
- 1 8254 Mode
- I8254_MODE0, I8254_MODE1, ..., I8254_MODE5
- OR'ed with:
- I8254_BCD, I8254_BINARY
-
- 0 ID: INSN_CONFIG_8254_READ_STATUS
- 1 <-- Status byte returned here.
- B7 = Output
- B6 = NULL Count
- B5 - B0 Current mode.
-
-*/
+ * 8254 specific configuration.
+ *
+ * It supports two config commands:
+ *
+ * 0 ID: INSN_CONFIG_SET_COUNTER_MODE
+ * 1 8254 Mode
+ * I8254_MODE0, I8254_MODE1, ..., I8254_MODE5
+ * OR'ed with:
+ * I8254_BCD, I8254_BINARY
+ *
+ * 0 ID: INSN_CONFIG_8254_READ_STATUS
+ * 1 <-- Status byte returned here.
+ * B7 = Output
+ * B6 = NULL Count
+ * B5 - B0 Current mode.
+ */
enum i8254_mode {
I8254_MODE0 = (0 << 1), /* Interrupt on terminal count */
@@ -545,18 +546,20 @@ enum i8254_mode {
I8254_MODE2 = (2 << 1), /* Rate generator */
I8254_MODE3 = (3 << 1), /* Square wave mode */
I8254_MODE4 = (4 << 1), /* Software triggered strobe */
- I8254_MODE5 = (5 << 1), /* Hardware triggered strobe
- * (retriggerable) */
- I8254_BCD = 1, /* use binary-coded decimal instead of binary
- * (pretty useless) */
+ /* Hardware triggered strobe (retriggerable) */
+ I8254_MODE5 = (5 << 1),
+ /* Use binary-coded decimal instead of binary (pretty useless) */
+ I8254_BCD = 1,
I8254_BINARY = 0
};
#define NI_USUAL_PFI_SELECT(x) (((x) < 10) ? (0x1 + (x)) : (0xb + (x)))
#define NI_USUAL_RTSI_SELECT(x) (((x) < 7) ? (0xb + (x)) : 0x1b)
-/* mode bits for NI general-purpose counters, set with
- * INSN_CONFIG_SET_COUNTER_MODE */
+/*
+ * mode bits for NI general-purpose counters, set with
+ * INSN_CONFIG_SET_COUNTER_MODE
+ */
#define NI_GPCT_COUNTING_MODE_SHIFT 16
#define NI_GPCT_INDEX_PHASE_BITSHIFT 20
#define NI_GPCT_COUNTING_DIRECTION_SHIFT 24
@@ -624,8 +627,10 @@ enum ni_gpct_mode_bits {
NI_GPCT_INVERT_OUTPUT_BIT = 0x20000000
};
-/* Bits for setting a clock source with
- * INSN_CONFIG_SET_CLOCK_SRC when using NI general-purpose counters. */
+/*
+ * Bits for setting a clock source with
+ * INSN_CONFIG_SET_CLOCK_SRC when using NI general-purpose counters.
+ */
enum ni_gpct_clock_source_bits {
NI_GPCT_CLOCK_SRC_SELECT_MASK = 0x3f,
NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS = 0x0,
@@ -656,9 +661,11 @@ enum ni_gpct_clock_source_bits {
/* no pfi on NI 660x */
#define NI_GPCT_PFI_CLOCK_SRC_BITS(x) (0x20 + (x))
-/* Possibilities for setting a gate source with
-INSN_CONFIG_SET_GATE_SRC when using NI general-purpose counters.
-May be bitwise-or'd with CR_EDGE or CR_INVERT. */
+/*
+ * Possibilities for setting a gate source with
+ * INSN_CONFIG_SET_GATE_SRC when using NI general-purpose counters.
+ * May be bitwise-or'd with CR_EDGE or CR_INVERT.
+ */
enum ni_gpct_gate_select {
/* m-series gates */
NI_GPCT_TIMESTAMP_MUX_GATE_SELECT = 0x0,
@@ -675,9 +682,11 @@ enum ni_gpct_gate_select {
/* more gates for 660x "second gate" */
NI_GPCT_UP_DOWN_PIN_i_GATE_SELECT = 0x201,
NI_GPCT_SELECTED_GATE_GATE_SELECT = 0x21e,
- /* m-series "second gate" sources are unknown,
+ /*
+ * m-series "second gate" sources are unknown,
* we should add them here with an offset of 0x300 when
- * known. */
+ * known.
+ */
NI_GPCT_DISABLED_GATE_SELECT = 0x8000,
};
@@ -686,8 +695,10 @@ enum ni_gpct_gate_select {
#define NI_GPCT_PFI_GATE_SELECT(x) NI_USUAL_PFI_SELECT(x)
#define NI_GPCT_UP_DOWN_PIN_GATE_SELECT(x) (0x202 + (x))
-/* Possibilities for setting a source with
-INSN_CONFIG_SET_OTHER_SRC when using NI general-purpose counters. */
+/*
+ * Possibilities for setting a source with
+ * INSN_CONFIG_SET_OTHER_SRC when using NI general-purpose counters.
+ */
enum ni_gpct_other_index {
NI_GPCT_SOURCE_ENCODER_A,
NI_GPCT_SOURCE_ENCODER_B,
@@ -702,18 +713,24 @@ enum ni_gpct_other_select {
#define NI_GPCT_PFI_OTHER_SELECT(x) NI_USUAL_PFI_SELECT(x)
-/* start sources for ni general-purpose counters for use with
-INSN_CONFIG_ARM */
+/*
+ * start sources for ni general-purpose counters for use with
+ * INSN_CONFIG_ARM
+ */
enum ni_gpct_arm_source {
NI_GPCT_ARM_IMMEDIATE = 0x0,
- NI_GPCT_ARM_PAIRED_IMMEDIATE = 0x1, /* Start both the counter
- * and the adjacent paired
- * counter simultaneously */
- /* NI doesn't document bits for selecting hardware arm triggers.
+ /*
+ * Start both the counter and the adjacent pared
+ * counter simultaneously
+ */
+ NI_GPCT_ARM_PAIRED_IMMEDIATE = 0x1,
+ /*
+ * NI doesn't document bits for selecting hardware arm triggers.
* If the NI_GPCT_ARM_UNKNOWN bit is set, we will pass the least
* significant bits (3 bits for 660x or 5 bits for m-series)
* through to the hardware. This will at least allow someone to
- * figure out what the bits do later. */
+ * figure out what the bits do later.
+ */
NI_GPCT_ARM_UNKNOWN = 0x1000,
};
@@ -728,8 +745,10 @@ enum ni_gpct_filter_select {
NI_GPCT_FILTER_2x_TIMEBASE_3 = 0x6
};
-/* PFI digital filtering options for ni m-series for use with
- * INSN_CONFIG_FILTER. */
+/*
+ * PFI digital filtering options for ni m-series for use with
+ * INSN_CONFIG_FILTER.
+ */
enum ni_pfi_filter_select {
NI_PFI_FILTER_OFF = 0x0,
NI_PFI_FILTER_125ns = 0x1,
@@ -740,9 +759,11 @@ enum ni_pfi_filter_select {
/* master clock sources for ni mio boards and INSN_CONFIG_SET_CLOCK_SRC */
enum ni_mio_clock_source {
NI_MIO_INTERNAL_CLOCK = 0,
- NI_MIO_RTSI_CLOCK = 1, /* doesn't work for m-series, use
- NI_MIO_PLL_RTSI_CLOCK() */
- /* the NI_MIO_PLL_* sources are m-series only */
+ /*
+ * Doesn't work for m-series, use NI_MIO_PLL_RTSI_CLOCK()
+ * the NI_MIO_PLL_* sources are m-series only
+ */
+ NI_MIO_RTSI_CLOCK = 1,
NI_MIO_PLL_PXI_STAR_TRIGGER_CLOCK = 2,
NI_MIO_PLL_PXI10_CLOCK = 3,
NI_MIO_PLL_RTSI0_CLOCK = 4
@@ -750,9 +771,11 @@ enum ni_mio_clock_source {
#define NI_MIO_PLL_RTSI_CLOCK(x) (NI_MIO_PLL_RTSI0_CLOCK + (x))
-/* Signals which can be routed to an NI RTSI pin with INSN_CONFIG_SET_ROUTING.
- The numbers assigned are not arbitrary, they correspond to the bits required
- to program the board. */
+/*
+ * Signals which can be routed to an NI RTSI pin with INSN_CONFIG_SET_ROUTING.
+ * The numbers assigned are not arbitrary, they correspond to the bits required
+ * to program the board.
+ */
enum ni_rtsi_routing {
NI_RTSI_OUTPUT_ADR_START1 = 0,
NI_RTSI_OUTPUT_ADR_START2 = 1,
@@ -763,17 +786,19 @@ enum ni_rtsi_routing {
NI_RTSI_OUTPUT_G_GATE0 = 6,
NI_RTSI_OUTPUT_RGOUT0 = 7,
NI_RTSI_OUTPUT_RTSI_BRD_0 = 8,
- NI_RTSI_OUTPUT_RTSI_OSC = 12 /* pre-m-series always have RTSI
- * clock on line 7 */
+ /* Pre-m-series always have RTSI clock on line 7 */
+ NI_RTSI_OUTPUT_RTSI_OSC = 12
};
#define NI_RTSI_OUTPUT_RTSI_BRD(x) (NI_RTSI_OUTPUT_RTSI_BRD_0 + (x))
-/* Signals which can be routed to an NI PFI pin on an m-series board with
+/*
+ * Signals which can be routed to an NI PFI pin on an m-series board with
* INSN_CONFIG_SET_ROUTING. These numbers are also returned by
* INSN_CONFIG_GET_ROUTING on pre-m-series boards, even though their routing
* cannot be changed. The numbers assigned are not arbitrary, they correspond
- * to the bits required to program the board. */
+ * to the bits required to program the board.
+ */
enum ni_pfi_routing {
NI_PFI_OUTPUT_PFI_DEFAULT = 0,
NI_PFI_OUTPUT_AI_START1 = 1,
@@ -803,20 +828,24 @@ enum ni_pfi_routing {
#define NI_PFI_OUTPUT_RTSI(x) (NI_PFI_OUTPUT_RTSI0 + (x))
-/* Signals which can be routed to output on a NI PFI pin on a 660x board
- with INSN_CONFIG_SET_ROUTING. The numbers assigned are
- not arbitrary, they correspond to the bits required
- to program the board. Lines 0 to 7 can only be set to
- NI_660X_PFI_OUTPUT_DIO. Lines 32 to 39 can only be set to
- NI_660X_PFI_OUTPUT_COUNTER. */
+/*
+ * Signals which can be routed to output on a NI PFI pin on a 660x board
+ * with INSN_CONFIG_SET_ROUTING. The numbers assigned are
+ * not arbitrary, they correspond to the bits required
+ * to program the board. Lines 0 to 7 can only be set to
+ * NI_660X_PFI_OUTPUT_DIO. Lines 32 to 39 can only be set to
+ * NI_660X_PFI_OUTPUT_COUNTER.
+ */
enum ni_660x_pfi_routing {
NI_660X_PFI_OUTPUT_COUNTER = 1, /* counter */
NI_660X_PFI_OUTPUT_DIO = 2, /* static digital output */
};
-/* NI External Trigger lines. These values are not arbitrary, but are related
+/*
+ * NI External Trigger lines. These values are not arbitrary, but are related
* to the bits required to program the board (offset by 1 for historical
- * reasons). */
+ * reasons).
+ */
#define NI_EXT_PFI(x) (NI_USUAL_PFI_SELECT(x) - 1)
#define NI_EXT_RTSI(x) (NI_USUAL_RTSI_SELECT(x) - 1)
@@ -827,9 +856,11 @@ enum comedi_counter_status_flags {
COMEDI_COUNTER_TERMINAL_COUNT = 0x4,
};
-/* Clock sources for CDIO subdevice on NI m-series boards. Used as the
+/*
+ * Clock sources for CDIO subdevice on NI m-series boards. Used as the
* scan_begin_arg for a comedi_command. These sources may also be bitwise-or'd
- * with CR_INVERT to change polarity. */
+ * with CR_INVERT to change polarity.
+ */
enum ni_m_series_cdio_scan_begin_src {
NI_CDIO_SCAN_BEGIN_SRC_GROUND = 0,
NI_CDIO_SCAN_BEGIN_SRC_AI_START = 18,
@@ -846,38 +877,50 @@ enum ni_m_series_cdio_scan_begin_src {
#define NI_CDIO_SCAN_BEGIN_SRC_PFI(x) NI_USUAL_PFI_SELECT(x)
#define NI_CDIO_SCAN_BEGIN_SRC_RTSI(x) NI_USUAL_RTSI_SELECT(x)
-/* scan_begin_src for scan_begin_arg==TRIG_EXT with analog output command on NI
+/*
+ * scan_begin_src for scan_begin_arg==TRIG_EXT with analog output command on NI
* boards. These scan begin sources can also be bitwise-or'd with CR_INVERT to
- * change polarity. */
+ * change polarity.
+ */
#define NI_AO_SCAN_BEGIN_SRC_PFI(x) NI_USUAL_PFI_SELECT(x)
#define NI_AO_SCAN_BEGIN_SRC_RTSI(x) NI_USUAL_RTSI_SELECT(x)
-/* Bits for setting a clock source with
- * INSN_CONFIG_SET_CLOCK_SRC when using NI frequency output subdevice. */
+/*
+ * Bits for setting a clock source with
+ * INSN_CONFIG_SET_CLOCK_SRC when using NI frequency output subdevice.
+ */
enum ni_freq_out_clock_source_bits {
NI_FREQ_OUT_TIMEBASE_1_DIV_2_CLOCK_SRC, /* 10 MHz */
NI_FREQ_OUT_TIMEBASE_2_CLOCK_SRC /* 100 KHz */
};
-/* Values for setting a clock source with INSN_CONFIG_SET_CLOCK_SRC for
- * 8254 counter subdevices on Amplicon DIO boards (amplc_dio200 driver). */
+/*
+ * Values for setting a clock source with INSN_CONFIG_SET_CLOCK_SRC for
+ * 8254 counter subdevices on Amplicon DIO boards (amplc_dio200 driver).
+ */
enum amplc_dio_clock_source {
- AMPLC_DIO_CLK_CLKN, /* per channel external clock
- input/output pin (pin is only an
- input when clock source set to this
- value, otherwise it is an output) */
+ /*
+ * Per channel external clock
+ * input/output pin (pin is only an
+ * input when clock source set to this value,
+ * otherwise it is an output)
+ */
+ AMPLC_DIO_CLK_CLKN,
AMPLC_DIO_CLK_10MHZ, /* 10 MHz internal clock */
AMPLC_DIO_CLK_1MHZ, /* 1 MHz internal clock */
AMPLC_DIO_CLK_100KHZ, /* 100 kHz internal clock */
AMPLC_DIO_CLK_10KHZ, /* 10 kHz internal clock */
AMPLC_DIO_CLK_1KHZ, /* 1 kHz internal clock */
- AMPLC_DIO_CLK_OUTNM1, /* output of preceding counter channel
- (for channel 0, preceding counter
- channel is channel 2 on preceding
- counter subdevice, for first counter
- subdevice, preceding counter
- subdevice is the last counter
- subdevice) */
+ /*
+ * Output of preceding counter channel
+ * (for channel 0, preceding counter
+ * channel is channel 2 on preceding
+ * counter subdevice, for first counter
+ * subdevice, preceding counter
+ * subdevice is the last counter
+ * subdevice)
+ */
+ AMPLC_DIO_CLK_OUTNM1,
AMPLC_DIO_CLK_EXT, /* per chip external input pin */
/* the following are "enhanced" clock sources for PCIe models */
AMPLC_DIO_CLK_VCC, /* clock input HIGH */
@@ -886,35 +929,39 @@ enum amplc_dio_clock_source {
AMPLC_DIO_CLK_20MHZ /* 20 MHz internal clock */
};
-/* Values for setting a clock source with INSN_CONFIG_SET_CLOCK_SRC for
- * timer subdevice on some Amplicon DIO PCIe boards (amplc_dio200 driver). */
+/*
+ * Values for setting a clock source with INSN_CONFIG_SET_CLOCK_SRC for
+ * timer subdevice on some Amplicon DIO PCIe boards (amplc_dio200 driver).
+ */
enum amplc_dio_ts_clock_src {
AMPLC_DIO_TS_CLK_1GHZ, /* 1 ns period with 20 ns granularity */
AMPLC_DIO_TS_CLK_1MHZ, /* 1 us period */
AMPLC_DIO_TS_CLK_1KHZ /* 1 ms period */
};
-/* Values for setting a gate source with INSN_CONFIG_SET_GATE_SRC for
- * 8254 counter subdevices on Amplicon DIO boards (amplc_dio200 driver). */
+/*
+ * Values for setting a gate source with INSN_CONFIG_SET_GATE_SRC for
+ * 8254 counter subdevices on Amplicon DIO boards (amplc_dio200 driver).
+ */
enum amplc_dio_gate_source {
AMPLC_DIO_GAT_VCC, /* internal high logic level */
AMPLC_DIO_GAT_GND, /* internal low logic level */
AMPLC_DIO_GAT_GATN, /* per channel external gate input */
- AMPLC_DIO_GAT_NOUTNM2, /* negated output of counter channel
- minus 2 (for channels 0 or 1,
- channel minus 2 is channel 1 or 2 on
- the preceding counter subdevice, for
- the first counter subdevice the
- preceding counter subdevice is the
- last counter subdevice) */
+ /*
+ * negated output of counter channel minus 2
+ * (for channels 0 or 1, channel minus 2 is channel 1 or 2 on
+ * the preceding counter subdevice, for the first counter subdevice
+ * the preceding counter subdevice is the last counter subdevice)
+ */
+ AMPLC_DIO_GAT_NOUTNM2,
AMPLC_DIO_GAT_RESERVED4,
AMPLC_DIO_GAT_RESERVED5,
AMPLC_DIO_GAT_RESERVED6,
AMPLC_DIO_GAT_RESERVED7,
/* the following are "enhanced" gate sources for PCIe models */
AMPLC_DIO_GAT_NGATN = 6, /* negated per channel gate input */
- AMPLC_DIO_GAT_OUTNM2, /* non-negated output of counter
- channel minus 2 */
+ /* non-negated output of counter channel minus 2 */
+ AMPLC_DIO_GAT_OUTNM2,
AMPLC_DIO_GAT_PAT_PRESENT, /* "pattern present" signal */
AMPLC_DIO_GAT_PAT_OCCURRED, /* "pattern occurred" latched */
AMPLC_DIO_GAT_PAT_GONE, /* "pattern gone away" latched */
diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c
index 7b4af519e17e..d57fadef47fc 100644
--- a/drivers/staging/comedi/comedi_fops.c
+++ b/drivers/staging/comedi/comedi_fops.c
@@ -2303,11 +2303,13 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
{
struct comedi_subdevice *s;
struct comedi_async *async;
- int n, m, count = 0, retval = 0;
+ unsigned int n, m;
+ ssize_t count = 0;
+ int retval = 0;
DECLARE_WAITQUEUE(wait, current);
struct comedi_file *cfp = file->private_data;
struct comedi_device *dev = cfp->dev;
- bool on_wait_queue = false;
+ bool become_nonbusy = false;
bool attach_locked;
unsigned int old_detach_count;
@@ -2329,74 +2331,33 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
}
async = s->async;
-
- if (!s->busy || !nbytes)
- goto out;
- if (s->busy != file) {
- retval = -EACCES;
- goto out;
- }
- if (!(async->cmd.flags & CMDF_WRITE)) {
+ if (s->busy != file || !(async->cmd.flags & CMDF_WRITE)) {
retval = -EINVAL;
goto out;
}
add_wait_queue(&async->wait_head, &wait);
- on_wait_queue = true;
- while (nbytes > 0 && !retval) {
+ while (count == 0 && !retval) {
unsigned runflags;
+ unsigned int wp, n1, n2;
set_current_state(TASK_INTERRUPTIBLE);
runflags = comedi_get_subdevice_runflags(s);
if (!comedi_is_runflags_running(runflags)) {
- if (count == 0) {
- struct comedi_subdevice *new_s;
-
- if (comedi_is_runflags_in_error(runflags))
- retval = -EPIPE;
- else
- retval = 0;
- /*
- * To avoid deadlock, cannot acquire dev->mutex
- * while dev->attach_lock is held. Need to
- * remove task from the async wait queue before
- * releasing dev->attach_lock, as it might not
- * be valid afterwards.
- */
- remove_wait_queue(&async->wait_head, &wait);
- on_wait_queue = false;
- up_read(&dev->attach_lock);
- attach_locked = false;
- mutex_lock(&dev->mutex);
- /*
- * Become non-busy unless things have changed
- * behind our back. Checking dev->detach_count
- * is unchanged ought to be sufficient (unless
- * there have been 2**32 detaches in the
- * meantime!), but check the subdevice pointer
- * as well just in case.
- */
- new_s = comedi_file_write_subdevice(file);
- if (dev->attached &&
- old_detach_count == dev->detach_count &&
- s == new_s && new_s->async == async)
- do_become_nonbusy(dev, s);
- mutex_unlock(&dev->mutex);
- }
+ if (comedi_is_runflags_in_error(runflags))
+ retval = -EPIPE;
+ if (retval || nbytes)
+ become_nonbusy = true;
break;
}
+ if (nbytes == 0)
+ break;
- n = nbytes;
-
- m = n;
- if (async->buf_write_ptr + m > async->prealloc_bufsz)
- m = async->prealloc_bufsz - async->buf_write_ptr;
+ /* Allocate all free buffer space. */
comedi_buf_write_alloc(s, async->prealloc_bufsz);
- if (m > comedi_buf_write_n_allocated(s))
- m = comedi_buf_write_n_allocated(s);
- if (m < n)
- n = m;
+ m = comedi_buf_write_n_allocated(s);
+ n = min_t(size_t, m, nbytes);
if (n == 0) {
if (file->f_flags & O_NONBLOCK) {
@@ -2408,21 +2369,22 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
retval = -ERESTARTSYS;
break;
}
- if (!s->busy)
- break;
- if (s->busy != file) {
- retval = -EACCES;
- break;
- }
- if (!(async->cmd.flags & CMDF_WRITE)) {
+ if (s->busy != file ||
+ !(async->cmd.flags & CMDF_WRITE)) {
retval = -EINVAL;
break;
}
continue;
}
- m = copy_from_user(async->prealloc_buf + async->buf_write_ptr,
- buf, n);
+ wp = async->buf_write_ptr;
+ n1 = min(n, async->prealloc_bufsz - wp);
+ n2 = n - n1;
+ m = copy_from_user(async->prealloc_buf + wp, buf, n1);
+ if (m)
+ m += n2;
+ else if (n2)
+ m = copy_from_user(async->prealloc_buf, buf + n1, n2);
if (m) {
n -= m;
retval = -EFAULT;
@@ -2433,12 +2395,38 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
nbytes -= n;
buf += n;
- break; /* makes device work like a pipe */
}
-out:
- if (on_wait_queue)
- remove_wait_queue(&async->wait_head, &wait);
+ remove_wait_queue(&async->wait_head, &wait);
set_current_state(TASK_RUNNING);
+ if (become_nonbusy && count == 0) {
+ struct comedi_subdevice *new_s;
+
+ /*
+ * To avoid deadlock, cannot acquire dev->mutex
+ * while dev->attach_lock is held.
+ */
+ up_read(&dev->attach_lock);
+ attach_locked = false;
+ mutex_lock(&dev->mutex);
+ /*
+ * Check device hasn't become detached behind our back.
+ * Checking dev->detach_count is unchanged ought to be
+ * sufficient (unless there have been 2**32 detaches in the
+ * meantime!), but check the subdevice pointer as well just in
+ * case.
+ *
+ * Also check the subdevice is still in a suitable state to
+ * become non-busy in case it changed behind our back.
+ */
+ new_s = comedi_file_write_subdevice(file);
+ if (dev->attached && old_detach_count == dev->detach_count &&
+ s == new_s && new_s->async == async && s->busy == file &&
+ (async->cmd.flags & CMDF_WRITE) &&
+ !comedi_is_subdevice_running(s))
+ do_become_nonbusy(dev, s);
+ mutex_unlock(&dev->mutex);
+ }
+out:
if (attach_locked)
up_read(&dev->attach_lock);
diff --git a/drivers/staging/comedi/comedilib.h b/drivers/staging/comedi/comedilib.h
index 56baf852ecf5..f9b56396e161 100644
--- a/drivers/staging/comedi/comedilib.h
+++ b/drivers/staging/comedi/comedilib.h
@@ -1,20 +1,20 @@
/*
- linux/include/comedilib.h
- header file for kcomedilib
-
- COMEDI - Linux Control and Measurement Device Interface
- Copyright (C) 1998-2001 David A. Schleef <ds@schleef.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 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.
-*/
+ * comedilib.h
+ * Header file for kcomedilib
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1998-2001 David A. Schleef <ds@schleef.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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
#ifndef _LINUX_COMEDILIB_H
#define _LINUX_COMEDILIB_H
diff --git a/drivers/staging/comedi/drivers/Makefile b/drivers/staging/comedi/drivers/Makefile
index c3b8f2d7611b..0c8cfa738727 100644
--- a/drivers/staging/comedi/drivers/Makefile
+++ b/drivers/staging/comedi/drivers/Makefile
@@ -78,8 +78,10 @@ obj-$(CONFIG_COMEDI_ADL_PCI8164) += adl_pci8164.o
obj-$(CONFIG_COMEDI_ADL_PCI9111) += adl_pci9111.o
obj-$(CONFIG_COMEDI_ADL_PCI9118) += adl_pci9118.o
obj-$(CONFIG_COMEDI_ADV_PCI1710) += adv_pci1710.o
+obj-$(CONFIG_COMEDI_ADV_PCI1720) += adv_pci1720.o
obj-$(CONFIG_COMEDI_ADV_PCI1723) += adv_pci1723.o
obj-$(CONFIG_COMEDI_ADV_PCI1724) += adv_pci1724.o
+obj-$(CONFIG_COMEDI_ADV_PCI1760) += adv_pci1760.o
obj-$(CONFIG_COMEDI_ADV_PCI_DIO) += adv_pci_dio.o
obj-$(CONFIG_COMEDI_AMPLC_DIO200_PCI) += amplc_dio200_pci.o
obj-$(CONFIG_COMEDI_AMPLC_PC236_PCI) += amplc_pci236.o
diff --git a/drivers/staging/comedi/drivers/adl_pci9118.c b/drivers/staging/comedi/drivers/adl_pci9118.c
index 0dff1dbb53fb..4437ea3abe8d 100644
--- a/drivers/staging/comedi/drivers/adl_pci9118.c
+++ b/drivers/staging/comedi/drivers/adl_pci9118.c
@@ -603,10 +603,11 @@ static void pci9118_ai_munge(struct comedi_device *dev,
unsigned short *array = data;
unsigned int num_samples = comedi_bytes_to_samples(s, num_bytes);
unsigned int i;
+ __be16 *barray = data;
for (i = 0; i < num_samples; i++) {
if (devpriv->usedma)
- array[i] = be16_to_cpu(array[i]);
+ array[i] = be16_to_cpu(barray[i]);
if (s->maxdata == 0xffff)
array[i] ^= 0x8000;
else
diff --git a/drivers/staging/comedi/drivers/adv_pci1710.c b/drivers/staging/comedi/drivers/adv_pci1710.c
index 399c511cfe0a..2c1b6de30da8 100644
--- a/drivers/staging/comedi/drivers/adv_pci1710.c
+++ b/drivers/staging/comedi/drivers/adv_pci1710.c
@@ -11,8 +11,9 @@
* Driver: adv_pci1710
* Description: Comedi driver for Advantech PCI-1710 series boards
* Devices: [Advantech] PCI-1710 (adv_pci1710), PCI-1710HG, PCI-1711,
- * PCI-1713, PCI-1720, PCI-1731
+ * PCI-1713, PCI-1731
* Author: Michal Dobes <dobes@tesnet.cz>
+ * Updated: Fri, 29 Oct 2015 17:19:35 -0700
* Status: works
*
* Configuration options: not applicable, uses PCI auto config
@@ -40,7 +41,13 @@
#define PCI171X_AD_DATA_REG 0x00 /* R: A/D data */
#define PCI171X_SOFTTRG_REG 0x00 /* W: soft trigger for A/D */
#define PCI171X_RANGE_REG 0x02 /* W: A/D gain/range register */
+#define PCI171X_RANGE_DIFF BIT(5)
+#define PCI171X_RANGE_UNI BIT(4)
+#define PCI171X_RANGE_GAIN(x) (((x) & 0x7) << 0)
#define PCI171X_MUX_REG 0x04 /* W: A/D multiplexor control */
+#define PCI171X_MUX_CHANH(x) (((x) & 0xf) << 8)
+#define PCI171X_MUX_CHANL(x) (((x) & 0xf) << 0)
+#define PCI171X_MUX_CHAN(x) (PCI171X_MUX_CHANH(x) | PCI171X_MUX_CHANL(x))
#define PCI171X_STATUS_REG 0x06 /* R: status register */
#define PCI171X_STATUS_IRQ BIT(11) /* 1=IRQ occurred */
#define PCI171X_STATUS_FF BIT(10) /* 1=FIFO is full, fatal error */
@@ -58,83 +65,58 @@
#define PCI171X_CLRFIFO_REG 0x09 /* W: clear FIFO */
#define PCI171X_DA_REG(x) (0x0a + ((x) * 2)) /* W: D/A register */
#define PCI171X_DAREF_REG 0x0e /* W: D/A reference control */
+#define PCI171X_DAREF(c, r) (((r) & 0x3) << ((c) * 2))
+#define PCI171X_DAREF_MASK(c) PCI171X_DAREF((c), 0x3)
#define PCI171X_DI_REG 0x10 /* R: digital inputs */
#define PCI171X_DO_REG 0x10 /* W: digital outputs */
#define PCI171X_TIMER_BASE 0x18 /* R/W: 8254 timer */
-/*
- * PCI-1720 only has analog outputs and has a different
- * register map (dev->iobase)
- */
-#define PCI1720_DA_REG(x) (0x00 + ((x) * 2)) /* W: D/A registers */
-#define PCI1720_RANGE_REG 0x08 /* R/W: D/A range register */
-#define PCI1720_SYNC_REG 0x09 /* W: D/A synchronized output */
-#define PCI1720_SYNC_CTRL_REG 0x0f /* R/W: D/A synchronized control */
-#define PCI1720_SYNC_CTRL_SC0 BIT(0) /* set synchronous output mode */
-
-static const struct comedi_lrange range_pci1710_3 = {
+static const struct comedi_lrange pci1710_ai_range = {
9, {
- BIP_RANGE(5),
- BIP_RANGE(2.5),
- BIP_RANGE(1.25),
- BIP_RANGE(0.625),
- BIP_RANGE(10),
- UNI_RANGE(10),
- UNI_RANGE(5),
- UNI_RANGE(2.5),
- UNI_RANGE(1.25)
+ BIP_RANGE(5), /* gain 1 (0x00) */
+ BIP_RANGE(2.5), /* gain 2 (0x01) */
+ BIP_RANGE(1.25), /* gain 4 (0x02) */
+ BIP_RANGE(0.625), /* gain 8 (0x03) */
+ BIP_RANGE(10), /* gain 0.5 (0x04) */
+ UNI_RANGE(10), /* gain 1 (0x00 | UNI) */
+ UNI_RANGE(5), /* gain 2 (0x01 | UNI) */
+ UNI_RANGE(2.5), /* gain 4 (0x02 | UNI) */
+ UNI_RANGE(1.25) /* gain 8 (0x03 | UNI) */
}
};
-static const char range_codes_pci1710_3[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
- 0x10, 0x11, 0x12, 0x13 };
-
-static const struct comedi_lrange range_pci1710hg = {
+static const struct comedi_lrange pci1710hg_ai_range = {
12, {
- BIP_RANGE(5),
- BIP_RANGE(0.5),
- BIP_RANGE(0.05),
- BIP_RANGE(0.005),
- BIP_RANGE(10),
- BIP_RANGE(1),
- BIP_RANGE(0.1),
- BIP_RANGE(0.01),
- UNI_RANGE(10),
- UNI_RANGE(1),
- UNI_RANGE(0.1),
- UNI_RANGE(0.01)
+ BIP_RANGE(5), /* gain 1 (0x00) */
+ BIP_RANGE(0.5), /* gain 10 (0x01) */
+ BIP_RANGE(0.05), /* gain 100 (0x02) */
+ BIP_RANGE(0.005), /* gain 1000 (0x03) */
+ BIP_RANGE(10), /* gain 0.5 (0x04) */
+ BIP_RANGE(1), /* gain 5 (0x05) */
+ BIP_RANGE(0.1), /* gain 50 (0x06) */
+ BIP_RANGE(0.01), /* gain 500 (0x07) */
+ UNI_RANGE(10), /* gain 1 (0x00 | UNI) */
+ UNI_RANGE(1), /* gain 10 (0x01 | UNI) */
+ UNI_RANGE(0.1), /* gain 100 (0x02 | UNI) */
+ UNI_RANGE(0.01) /* gain 1000 (0x03 | UNI) */
}
};
-static const char range_codes_pci1710hg[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
- 0x05, 0x06, 0x07, 0x10, 0x11,
- 0x12, 0x13 };
-
-static const struct comedi_lrange range_pci17x1 = {
+static const struct comedi_lrange pci1711_ai_range = {
5, {
- BIP_RANGE(10),
- BIP_RANGE(5),
- BIP_RANGE(2.5),
- BIP_RANGE(1.25),
- BIP_RANGE(0.625)
- }
-};
-
-static const char range_codes_pci17x1[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
-
-static const struct comedi_lrange pci1720_ao_range = {
- 4, {
- UNI_RANGE(5),
- UNI_RANGE(10),
- BIP_RANGE(5),
- BIP_RANGE(10)
+ BIP_RANGE(10), /* gain 1 (0x00) */
+ BIP_RANGE(5), /* gain 2 (0x01) */
+ BIP_RANGE(2.5), /* gain 4 (0x02) */
+ BIP_RANGE(1.25), /* gain 8 (0x03) */
+ BIP_RANGE(0.625) /* gain 16 (0x04) */
}
};
static const struct comedi_lrange pci171x_ao_range = {
- 2, {
- UNI_RANGE(5),
- UNI_RANGE(10)
+ 3, {
+ UNI_RANGE(5), /* internal -5V ref */
+ UNI_RANGE(10), /* internal -10V ref */
+ RANGE_ext(0, 1) /* external -Vref (+/-10V max) */
}
};
@@ -143,82 +125,43 @@ enum pci1710_boardid {
BOARD_PCI1710HG,
BOARD_PCI1711,
BOARD_PCI1713,
- BOARD_PCI1720,
BOARD_PCI1731,
};
struct boardtype {
- const char *name; /* board name */
- int n_aichan; /* num of A/D chans */
- const struct comedi_lrange *rangelist_ai; /* rangelist for A/D */
- const char *rangecode_ai; /* range codes for programming */
+ const char *name;
+ const struct comedi_lrange *ai_range;
+ unsigned int is_pci1711:1;
unsigned int is_pci1713:1;
- unsigned int is_pci1720:1;
- unsigned int has_irq:1;
- unsigned int has_large_fifo:1; /* 4K or 1K FIFO */
- unsigned int has_diff_ai:1;
unsigned int has_ao:1;
- unsigned int has_di_do:1;
- unsigned int has_counter:1;
};
static const struct boardtype boardtypes[] = {
[BOARD_PCI1710] = {
.name = "pci1710",
- .n_aichan = 16,
- .rangelist_ai = &range_pci1710_3,
- .rangecode_ai = range_codes_pci1710_3,
- .has_irq = 1,
- .has_large_fifo = 1,
- .has_diff_ai = 1,
+ .ai_range = &pci1710_ai_range,
.has_ao = 1,
- .has_di_do = 1,
- .has_counter = 1,
},
[BOARD_PCI1710HG] = {
.name = "pci1710hg",
- .n_aichan = 16,
- .rangelist_ai = &range_pci1710hg,
- .rangecode_ai = range_codes_pci1710hg,
- .has_irq = 1,
- .has_large_fifo = 1,
- .has_diff_ai = 1,
+ .ai_range = &pci1710hg_ai_range,
.has_ao = 1,
- .has_di_do = 1,
- .has_counter = 1,
},
[BOARD_PCI1711] = {
.name = "pci1711",
- .n_aichan = 16,
- .rangelist_ai = &range_pci17x1,
- .rangecode_ai = range_codes_pci17x1,
- .has_irq = 1,
+ .ai_range = &pci1711_ai_range,
+ .is_pci1711 = 1,
.has_ao = 1,
- .has_di_do = 1,
- .has_counter = 1,
},
[BOARD_PCI1713] = {
.name = "pci1713",
- .n_aichan = 32,
- .rangelist_ai = &range_pci1710_3,
- .rangecode_ai = range_codes_pci1710_3,
+ .ai_range = &pci1710_ai_range,
.is_pci1713 = 1,
- .has_irq = 1,
- .has_large_fifo = 1,
- .has_diff_ai = 1,
- },
- [BOARD_PCI1720] = {
- .name = "pci1720",
- .is_pci1720 = 1,
- .has_ao = 1,
},
[BOARD_PCI1731] = {
.name = "pci1731",
- .n_aichan = 16,
- .rangelist_ai = &range_pci17x1,
- .rangecode_ai = range_codes_pci17x1,
- .has_irq = 1,
- .has_di_do = 1,
+ .ai_range = &pci1711_ai_range,
+ .is_pci1711 = 1,
},
};
@@ -226,14 +169,15 @@ struct pci1710_private {
unsigned int max_samples;
unsigned int ctrl; /* control register value */
unsigned int ctrl_ext; /* used to switch from TRIG_EXT to TRIG_xxx */
- unsigned int mux_ext; /* used to set the channel interval to scan */
+ unsigned int mux_scan; /* used to set the channel interval to scan */
unsigned char ai_et;
unsigned int act_chanlist[32]; /* list of scanned channel */
unsigned char saved_seglen; /* len of the non-repeating chanlist */
unsigned char da_ranges; /* copy of D/A outpit range register */
+ unsigned char unipolar_gain; /* adjust for unipolar gain codes */
};
-static int pci171x_ai_check_chanlist(struct comedi_device *dev,
+static int pci1710_ai_check_chanlist(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd)
{
@@ -299,13 +243,12 @@ static int pci171x_ai_check_chanlist(struct comedi_device *dev,
return 0;
}
-static void pci171x_ai_setup_chanlist(struct comedi_device *dev,
+static void pci1710_ai_setup_chanlist(struct comedi_device *dev,
struct comedi_subdevice *s,
unsigned int *chanlist,
unsigned int n_chan,
unsigned int seglen)
{
- const struct boardtype *board = dev->board_ptr;
struct pci1710_private *devpriv = dev->private;
unsigned int first_chan = CR_CHAN(chanlist[0]);
unsigned int last_chan = CR_CHAN(chanlist[seglen - 1]);
@@ -315,14 +258,18 @@ static void pci171x_ai_setup_chanlist(struct comedi_device *dev,
unsigned int chan = CR_CHAN(chanlist[i]);
unsigned int range = CR_RANGE(chanlist[i]);
unsigned int aref = CR_AREF(chanlist[i]);
- unsigned int rangeval;
+ unsigned int rangeval = 0;
- rangeval = board->rangecode_ai[range];
if (aref == AREF_DIFF)
- rangeval |= 0x0020;
+ rangeval |= PCI171X_RANGE_DIFF;
+ if (comedi_range_is_unipolar(s, range)) {
+ rangeval |= PCI171X_RANGE_UNI;
+ range -= devpriv->unipolar_gain;
+ }
+ rangeval |= PCI171X_RANGE_GAIN(range);
/* select channel and set range */
- outw(chan | (chan << 8), dev->iobase + PCI171X_MUX_REG);
+ outw(PCI171X_MUX_CHAN(chan), dev->iobase + PCI171X_MUX_REG);
outw(rangeval, dev->iobase + PCI171X_RANGE_REG);
devpriv->act_chanlist[i] = chan;
@@ -331,11 +278,12 @@ static void pci171x_ai_setup_chanlist(struct comedi_device *dev,
devpriv->act_chanlist[i] = CR_CHAN(chanlist[i]);
/* select channel interval to scan */
- devpriv->mux_ext = first_chan | (last_chan << 8);
- outw(devpriv->mux_ext, dev->iobase + PCI171X_MUX_REG);
+ devpriv->mux_scan = PCI171X_MUX_CHANL(first_chan) |
+ PCI171X_MUX_CHANH(last_chan);
+ outw(devpriv->mux_scan, dev->iobase + PCI171X_MUX_REG);
}
-static int pci171x_ai_eoc(struct comedi_device *dev,
+static int pci1710_ai_eoc(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned long context)
@@ -348,7 +296,7 @@ static int pci171x_ai_eoc(struct comedi_device *dev,
return -EBUSY;
}
-static int pci171x_ai_read_sample(struct comedi_device *dev,
+static int pci1710_ai_read_sample(struct comedi_device *dev,
struct comedi_subdevice *s,
unsigned int cur_chan,
unsigned int *val)
@@ -377,7 +325,7 @@ static int pci171x_ai_read_sample(struct comedi_device *dev,
return 0;
}
-static int pci171x_ai_insn_read(struct comedi_device *dev,
+static int pci1710_ai_insn_read(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
@@ -386,13 +334,14 @@ static int pci171x_ai_insn_read(struct comedi_device *dev,
int ret = 0;
int i;
- devpriv->ctrl &= PCI171X_CTRL_CNT0;
- devpriv->ctrl |= PCI171X_CTRL_SW; /* set software trigger */
+ /* enable software trigger */
+ devpriv->ctrl |= PCI171X_CTRL_SW;
outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
+
outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
outb(0, dev->iobase + PCI171X_CLRINT_REG);
- pci171x_ai_setup_chanlist(dev, s, &insn->chanspec, 1, 1);
+ pci1710_ai_setup_chanlist(dev, s, &insn->chanspec, 1, 1);
for (i = 0; i < insn->n; i++) {
unsigned int val;
@@ -400,111 +349,40 @@ static int pci171x_ai_insn_read(struct comedi_device *dev,
/* start conversion */
outw(0, dev->iobase + PCI171X_SOFTTRG_REG);
- ret = comedi_timeout(dev, s, insn, pci171x_ai_eoc, 0);
+ ret = comedi_timeout(dev, s, insn, pci1710_ai_eoc, 0);
if (ret)
break;
- ret = pci171x_ai_read_sample(dev, s, 0, &val);
+ ret = pci1710_ai_read_sample(dev, s, 0, &val);
if (ret)
break;
data[i] = val;
}
+ /* disable software trigger */
+ devpriv->ctrl &= ~PCI171X_CTRL_SW;
+ outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
+
outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
outb(0, dev->iobase + PCI171X_CLRINT_REG);
return ret ? ret : insn->n;
}
-static int pci171x_ao_insn_write(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- struct pci1710_private *devpriv = dev->private;
- unsigned int chan = CR_CHAN(insn->chanspec);
- unsigned int range = CR_RANGE(insn->chanspec);
- unsigned int val = s->readback[chan];
- int i;
-
- devpriv->da_ranges &= ~(1 << (chan << 1));
- devpriv->da_ranges |= (range << (chan << 1));
- outw(devpriv->da_ranges, dev->iobase + PCI171X_DAREF_REG);
-
- for (i = 0; i < insn->n; i++) {
- val = data[i];
- outw(val, dev->iobase + PCI171X_DA_REG(chan));
- }
-
- s->readback[chan] = val;
-
- return insn->n;
-}
-
-static int pci171x_di_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- data[1] = inw(dev->iobase + PCI171X_DI_REG);
-
- return insn->n;
-}
-
-static int pci171x_do_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- if (comedi_dio_update_state(s, data))
- outw(s->state, dev->iobase + PCI171X_DO_REG);
-
- data[1] = s->state;
-
- return insn->n;
-}
-
-static int pci1720_ao_insn_write(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- struct pci1710_private *devpriv = dev->private;
- unsigned int chan = CR_CHAN(insn->chanspec);
- unsigned int range = CR_RANGE(insn->chanspec);
- unsigned int val;
- int i;
-
- val = devpriv->da_ranges & (~(0x03 << (chan << 1)));
- val |= (range << (chan << 1));
- if (val != devpriv->da_ranges) {
- outb(val, dev->iobase + PCI1720_RANGE_REG);
- devpriv->da_ranges = val;
- }
-
- val = s->readback[chan];
- for (i = 0; i < insn->n; i++) {
- val = data[i];
- outw(val, dev->iobase + PCI1720_DA_REG(chan));
- outb(0, dev->iobase + PCI1720_SYNC_REG); /* update outputs */
- }
-
- s->readback[chan] = val;
-
- return insn->n;
-}
-
-static int pci171x_ai_cancel(struct comedi_device *dev,
+static int pci1710_ai_cancel(struct comedi_device *dev,
struct comedi_subdevice *s)
{
struct pci1710_private *devpriv = dev->private;
- devpriv->ctrl &= PCI171X_CTRL_CNT0;
- devpriv->ctrl |= PCI171X_CTRL_SW;
- /* reset any operations */
+ /* disable A/D triggers and interrupt sources */
+ devpriv->ctrl &= PCI171X_CTRL_CNT0; /* preserve counter 0 clk src */
outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
+
+ /* disable pacer */
comedi_8254_pacer_enable(dev->pacer, 1, 2, false);
+
+ /* clear A/D FIFO and any pending interrutps */
outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
outb(0, dev->iobase + PCI171X_CLRINT_REG);
@@ -535,7 +413,7 @@ static void pci1710_handle_every_sample(struct comedi_device *dev,
outb(0, dev->iobase + PCI171X_CLRINT_REG);
for (; !(inw(dev->iobase + PCI171X_STATUS_REG) & PCI171X_STATUS_FE);) {
- ret = pci171x_ai_read_sample(dev, s, s->async->cur_chan, &val);
+ ret = pci1710_ai_read_sample(dev, s, s->async->cur_chan, &val);
if (ret) {
s->async->events |= COMEDI_CB_ERROR;
break;
@@ -579,7 +457,7 @@ static void pci1710_handle_fifo(struct comedi_device *dev,
unsigned int val;
int ret;
- ret = pci171x_ai_read_sample(dev, s, s->async->cur_chan, &val);
+ ret = pci1710_ai_read_sample(dev, s, s->async->cur_chan, &val);
if (ret) {
s->async->events |= COMEDI_CB_ERROR;
break;
@@ -598,7 +476,7 @@ static void pci1710_handle_fifo(struct comedi_device *dev,
outb(0, dev->iobase + PCI171X_CLRINT_REG);
}
-static irqreturn_t interrupt_service_pci1710(int irq, void *d)
+static irqreturn_t pci1710_irq_handler(int irq, void *d)
{
struct comedi_device *dev = d;
struct pci1710_private *devpriv = dev->private;
@@ -624,7 +502,7 @@ static irqreturn_t interrupt_service_pci1710(int irq, void *d)
outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
outb(0, dev->iobase + PCI171X_CLRINT_REG);
/* no sample on this interrupt; reset the channel interval */
- outw(devpriv->mux_ext, dev->iobase + PCI171X_MUX_REG);
+ outw(devpriv->mux_scan, dev->iobase + PCI171X_MUX_REG);
outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
return IRQ_HANDLED;
@@ -640,12 +518,12 @@ static irqreturn_t interrupt_service_pci1710(int irq, void *d)
return IRQ_HANDLED;
}
-static int pci171x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+static int pci1710_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
struct pci1710_private *devpriv = dev->private;
struct comedi_cmd *cmd = &s->async->cmd;
- pci171x_ai_setup_chanlist(dev, s, cmd->chanlist, cmd->chanlist_len,
+ pci1710_ai_setup_chanlist(dev, s, cmd->chanlist, cmd->chanlist_len,
devpriv->saved_seglen);
outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
@@ -681,7 +559,7 @@ static int pci171x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
return 0;
}
-static int pci171x_ai_cmdtest(struct comedi_device *dev,
+static int pci1710_ai_cmdtest(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd)
{
@@ -745,7 +623,7 @@ static int pci171x_ai_cmdtest(struct comedi_device *dev,
/* Step 5: check channel list */
- err |= pci171x_ai_check_chanlist(dev, s, cmd);
+ err |= pci1710_ai_check_chanlist(dev, s, cmd);
if (err)
return 5;
@@ -753,7 +631,55 @@ static int pci171x_ai_cmdtest(struct comedi_device *dev,
return 0;
}
-static int pci171x_insn_counter_config(struct comedi_device *dev,
+static int pci1710_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct pci1710_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ int i;
+
+ devpriv->da_ranges &= ~PCI171X_DAREF_MASK(chan);
+ devpriv->da_ranges |= PCI171X_DAREF(chan, range);
+ outw(devpriv->da_ranges, dev->iobase + PCI171X_DAREF_REG);
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+ outw(val, dev->iobase + PCI171X_DA_REG(chan));
+ }
+
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static int pci1710_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = inw(dev->iobase + PCI171X_DI_REG);
+
+ return insn->n;
+}
+
+static int pci1710_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outw(s->state, dev->iobase + PCI171X_DO_REG);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int pci1710_counter_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
@@ -780,7 +706,7 @@ static int pci171x_insn_counter_config(struct comedi_device *dev,
data[2] = 0;
} else {
data[1] = 0;
- data[2] = I8254_OSC_BASE_10MHZ;
+ data[2] = I8254_OSC_BASE_1MHZ;
}
break;
default:
@@ -790,56 +716,29 @@ static int pci171x_insn_counter_config(struct comedi_device *dev,
return insn->n;
}
-static int pci171x_reset(struct comedi_device *dev)
+static void pci1710_reset(struct comedi_device *dev)
{
const struct boardtype *board = dev->board_ptr;
- struct pci1710_private *devpriv = dev->private;
- /* Software trigger, CNT0=external */
- devpriv->ctrl = PCI171X_CTRL_SW | PCI171X_CTRL_CNT0;
- /* reset any operations */
- outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
+ /*
+ * Disable A/D triggers and interrupt sources, set counter 0
+ * to use internal 1 MHz clock.
+ */
+ outw(0, dev->iobase + PCI171X_CTRL_REG);
+
+ /* clear A/D FIFO and any pending interrutps */
outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
outb(0, dev->iobase + PCI171X_CLRINT_REG);
- devpriv->da_ranges = 0;
+
if (board->has_ao) {
/* set DACs to 0..5V and outputs to 0V */
- outb(devpriv->da_ranges, dev->iobase + PCI171X_DAREF_REG);
+ outb(0, dev->iobase + PCI171X_DAREF_REG);
outw(0, dev->iobase + PCI171X_DA_REG(0));
outw(0, dev->iobase + PCI171X_DA_REG(1));
}
- outw(0, dev->iobase + PCI171X_DO_REG); /* digital outputs to 0 */
- outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
- outb(0, dev->iobase + PCI171X_CLRINT_REG);
- return 0;
-}
-
-static int pci1720_reset(struct comedi_device *dev)
-{
- struct pci1710_private *devpriv = dev->private;
- /* set synchronous output mode */
- outb(PCI1720_SYNC_CTRL_SC0, dev->iobase + PCI1720_SYNC_CTRL_REG);
- devpriv->da_ranges = 0xAA;
- /* set all ranges to +/-5V and outputs to 0V */
- outb(devpriv->da_ranges, dev->iobase + PCI1720_RANGE_REG);
- outw(0x0800, dev->iobase + PCI1720_DA_REG(0));
- outw(0x0800, dev->iobase + PCI1720_DA_REG(1));
- outw(0x0800, dev->iobase + PCI1720_DA_REG(2));
- outw(0x0800, dev->iobase + PCI1720_DA_REG(3));
- outb(0, dev->iobase + PCI1720_SYNC_REG); /* update outputs */
-
- return 0;
-}
-
-static int pci1710_reset(struct comedi_device *dev)
-{
- const struct boardtype *board = dev->board_ptr;
-
- if (board->is_pci1720)
- return pci1720_reset(dev);
-
- return pci171x_reset(dev);
+ /* set digital outputs to 0 */
+ outw(0, dev->iobase + PCI171X_DO_REG);
}
static int pci1710_auto_attach(struct comedi_device *dev,
@@ -850,6 +749,7 @@ static int pci1710_auto_attach(struct comedi_device *dev,
struct pci1710_private *devpriv;
struct comedi_subdevice *s;
int ret, subdev, n_subdevices;
+ int i;
if (context < ARRAY_SIZE(boardtypes))
board = &boardtypes[context];
@@ -872,15 +772,16 @@ static int pci1710_auto_attach(struct comedi_device *dev,
if (!dev->pacer)
return -ENOMEM;
- n_subdevices = 0;
- if (board->n_aichan)
- n_subdevices++;
+ n_subdevices = 1; /* all boards have analog inputs */
if (board->has_ao)
n_subdevices++;
- if (board->has_di_do)
- n_subdevices += 2;
- if (board->has_counter)
- n_subdevices++;
+ if (!board->is_pci1713) {
+ /*
+ * All other boards have digital inputs and outputs as
+ * well as a user counter.
+ */
+ n_subdevices += 3;
+ }
ret = comedi_alloc_subdevices(dev, n_subdevices);
if (ret)
@@ -888,8 +789,8 @@ static int pci1710_auto_attach(struct comedi_device *dev,
pci1710_reset(dev);
- if (board->has_irq && pcidev->irq) {
- ret = request_irq(pcidev->irq, interrupt_service_pci1710,
+ if (pcidev->irq) {
+ ret = request_irq(pcidev->irq, pci1710_irq_handler,
IRQF_SHARED, dev->board_name, dev);
if (ret == 0)
dev->irq = pcidev->irq;
@@ -897,109 +798,89 @@ static int pci1710_auto_attach(struct comedi_device *dev,
subdev = 0;
- if (board->n_aichan) {
- s = &dev->subdevices[subdev];
- s->type = COMEDI_SUBD_AI;
- s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
- if (board->has_diff_ai)
- s->subdev_flags |= SDF_DIFF;
- s->n_chan = board->n_aichan;
- s->maxdata = 0x0fff;
- s->range_table = board->rangelist_ai;
- s->insn_read = pci171x_ai_insn_read;
- if (dev->irq) {
- dev->read_subdev = s;
- s->subdev_flags |= SDF_CMD_READ;
- s->len_chanlist = s->n_chan;
- s->do_cmdtest = pci171x_ai_cmdtest;
- s->do_cmd = pci171x_ai_cmd;
- s->cancel = pci171x_ai_cancel;
+ /* Analog Input subdevice */
+ s = &dev->subdevices[subdev++];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ if (!board->is_pci1711)
+ s->subdev_flags |= SDF_DIFF;
+ s->n_chan = board->is_pci1713 ? 32 : 16;
+ s->maxdata = 0x0fff;
+ s->range_table = board->ai_range;
+ s->insn_read = pci1710_ai_insn_read;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = s->n_chan;
+ s->do_cmdtest = pci1710_ai_cmdtest;
+ s->do_cmd = pci1710_ai_cmd;
+ s->cancel = pci1710_ai_cancel;
+ }
+
+ /* find the value needed to adjust for unipolar gain codes */
+ for (i = 0; i < s->range_table->length; i++) {
+ if (comedi_range_is_unipolar(s, i)) {
+ devpriv->unipolar_gain = i;
+ break;
}
- subdev++;
}
if (board->has_ao) {
- s = &dev->subdevices[subdev];
+ /* Analog Output subdevice */
+ s = &dev->subdevices[subdev++];
s->type = COMEDI_SUBD_AO;
- s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
+ s->n_chan = 2;
s->maxdata = 0x0fff;
- if (board->is_pci1720) {
- s->n_chan = 4;
- s->range_table = &pci1720_ao_range;
- s->insn_write = pci1720_ao_insn_write;
- } else {
- s->n_chan = 2;
- s->range_table = &pci171x_ao_range;
- s->insn_write = pci171x_ao_insn_write;
- }
+ s->range_table = &pci171x_ao_range;
+ s->insn_write = pci1710_ao_insn_write;
ret = comedi_alloc_subdev_readback(s);
if (ret)
return ret;
-
- /* initialize the readback values to match the board reset */
- if (board->is_pci1720) {
- int i;
-
- for (i = 0; i < s->n_chan; i++)
- s->readback[i] = 0x0800;
- }
-
- subdev++;
}
- if (board->has_di_do) {
- s = &dev->subdevices[subdev];
+ if (!board->is_pci1713) {
+ /* Digital Input subdevice */
+ s = &dev->subdevices[subdev++];
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
s->n_chan = 16;
s->maxdata = 1;
s->range_table = &range_digital;
- s->insn_bits = pci171x_di_insn_bits;
- subdev++;
+ s->insn_bits = pci1710_di_insn_bits;
- s = &dev->subdevices[subdev];
+ /* Digital Output subdevice */
+ s = &dev->subdevices[subdev++];
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE;
s->n_chan = 16;
s->maxdata = 1;
s->range_table = &range_digital;
- s->insn_bits = pci171x_do_insn_bits;
- subdev++;
- }
+ s->insn_bits = pci1710_do_insn_bits;
- /* Counter subdevice (8254) */
- if (board->has_counter) {
- s = &dev->subdevices[subdev];
+ /* Counter subdevice (8254) */
+ s = &dev->subdevices[subdev++];
comedi_8254_subdevice_init(s, dev->pacer);
- dev->pacer->insn_config = pci171x_insn_counter_config;
+ dev->pacer->insn_config = pci1710_counter_insn_config;
/* counters 1 and 2 are used internally for the pacer */
comedi_8254_set_busy(dev->pacer, 1, true);
comedi_8254_set_busy(dev->pacer, 2, true);
-
- subdev++;
}
/* max_samples is half the FIFO size (2 bytes/sample) */
- devpriv->max_samples = (board->has_large_fifo) ? 2048 : 512;
+ devpriv->max_samples = (board->is_pci1711) ? 512 : 2048;
return 0;
}
-static void pci1710_detach(struct comedi_device *dev)
-{
- if (dev->iobase)
- pci1710_reset(dev);
- comedi_pci_detach(dev);
-}
-
static struct comedi_driver adv_pci1710_driver = {
.driver_name = "adv_pci1710",
.module = THIS_MODULE,
.auto_attach = pci1710_auto_attach,
- .detach = pci1710_detach,
+ .detach = comedi_pci_detach,
};
static int adv_pci1710_pci_probe(struct pci_dev *dev,
@@ -1063,7 +944,6 @@ static const struct pci_device_id adv_pci1710_pci_table[] = {
},
{ PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 },
{ PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 },
- { PCI_VDEVICE(ADVANTECH, 0x1720), BOARD_PCI1720 },
{ PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 },
{ 0 }
};
diff --git a/drivers/staging/comedi/drivers/adv_pci1720.c b/drivers/staging/comedi/drivers/adv_pci1720.c
new file mode 100644
index 000000000000..4830a1c93d15
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adv_pci1720.c
@@ -0,0 +1,195 @@
+/*
+ * COMEDI driver for Advantech PCI-1720U
+ * Copyright (c) 2015 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * Separated from the adv_pci1710 driver written by:
+ * Michal Dobes <dobes@tesnet.cz>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.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 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.
+ */
+
+/*
+ * Driver: adv_pci1720
+ * Description: 4-channel Isolated D/A Output board
+ * Devices: [Advantech] PCI-7120U (adv_pci1720)
+ * Author: H Hartley Sweeten <hsweeten@visionengravers.com>
+ * Updated: Fri, 29 Oct 2015 17:19:35 -0700
+ * Status: untested
+ *
+ * Configuration options: not applicable, uses PCI auto config
+ *
+ * The PCI-1720 has 4 isolated 12-bit analog output channels with multiple
+ * output ranges. It also has a BoardID switch to allow differentiating
+ * multiple boards in the system.
+ *
+ * The analog outputs can operate in two modes, immediate and synchronized.
+ * This driver currently does not support the synchronized output mode.
+ *
+ * Jumpers JP1 to JP4 are used to set the current sink ranges for each
+ * analog output channel. In order to use the current sink ranges, the
+ * unipolar 5V range must be used. The voltage output and sink output for
+ * each channel is available on the connector as separate pins.
+ *
+ * Jumper JP5 controls the "hot" reset state of the analog outputs.
+ * Depending on its setting, the analog outputs will either keep the
+ * last settings and output values or reset to the default state after
+ * a "hot" reset. The default state for all channels is uniploar 5V range
+ * and all the output values are 0V. To allow this feature to work, the
+ * analog outputs are not "reset" when the driver attaches.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include "../comedi_pci.h"
+
+/*
+ * PCI BAR2 Register map (dev->iobase)
+ */
+#define PCI1720_AO_LSB_REG(x) (0x00 + ((x) * 2))
+#define PCI1720_AO_MSB_REG(x) (0x01 + ((x) * 2))
+#define PCI1720_AO_RANGE_REG 0x08
+#define PCI1720_AO_RANGE(c, r) (((r) & 0x3) << ((c) * 2))
+#define PCI1720_AO_RANGE_MASK(c) PCI1720_AO_RANGE((c), 0x3)
+#define PCI1720_SYNC_REG 0x09
+#define PCI1720_SYNC_CTRL_REG 0x0f
+#define PCI1720_SYNC_CTRL_SC0 BIT(0)
+#define PCI1720_BOARDID_REG 0x14
+
+static const struct comedi_lrange pci1720_ao_range = {
+ 4, {
+ UNI_RANGE(5),
+ UNI_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(10)
+ }
+};
+
+static int pci1720_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int val;
+ int i;
+
+ /* set the channel range and polarity */
+ val = inb(dev->iobase + PCI1720_AO_RANGE_REG);
+ val &= ~PCI1720_AO_RANGE_MASK(chan);
+ val |= PCI1720_AO_RANGE(chan, range);
+ outb(val, dev->iobase + PCI1720_AO_RANGE_REG);
+
+ val = s->readback[chan];
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+
+ outb(val & 0xff, dev->iobase + PCI1720_AO_LSB_REG(chan));
+ outb((val >> 8) & 0xff, dev->iobase + PCI1720_AO_MSB_REG(chan));
+
+ /* conversion time is 2us (500 kHz throughput) */
+ usleep_range(2, 100);
+ }
+
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static int pci1720_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = inb(dev->iobase + PCI1720_BOARDID_REG);
+
+ return insn->n;
+}
+
+static int pci1720_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+ dev->iobase = pci_resource_start(pcidev, 2);
+
+ ret = comedi_alloc_subdevices(dev, 2);
+ if (ret)
+ return ret;
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 0x0fff;
+ s->range_table = &pci1720_ao_range;
+ s->insn_write = pci1720_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ /* Digital Input subdevice (BoardID SW1) */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pci1720_di_insn_bits;
+
+ /* disable synchronized output, channels update when written */
+ outb(0, dev->iobase + PCI1720_SYNC_CTRL_REG);
+
+ return 0;
+}
+
+static struct comedi_driver adv_pci1720_driver = {
+ .driver_name = "adv_pci1720",
+ .module = THIS_MODULE,
+ .auto_attach = pci1720_auto_attach,
+ .detach = comedi_pci_detach,
+};
+
+static int adv_pci1720_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &adv_pci1720_driver,
+ id->driver_data);
+}
+
+static const struct pci_device_id adv_pci1720_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1720) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, adv_pci1720_pci_table);
+
+static struct pci_driver adv_pci1720_pci_driver = {
+ .name = "adv_pci1720",
+ .id_table = adv_pci1720_pci_table,
+ .probe = adv_pci1720_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(adv_pci1720_driver, adv_pci1720_pci_driver);
+
+MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
+MODULE_DESCRIPTION("Comedi driver for Advantech PCI-1720 Analog Output board");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/adv_pci1760.c b/drivers/staging/comedi/drivers/adv_pci1760.c
new file mode 100644
index 000000000000..d7dd1e55e347
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adv_pci1760.c
@@ -0,0 +1,432 @@
+/*
+ * COMEDI driver for the Advantech PCI-1760
+ * Copyright (C) 2015 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * Based on the pci1760 support in the adv_pci_dio driver written by:
+ * Michal Dobes <dobes@tesnet.cz>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.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 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.
+ */
+
+/*
+ * Driver: adv_pci1760
+ * Description: Advantech PCI-1760 Relay & Isolated Digital Input Card
+ * Devices: [Advantech] PCI-1760 (adv_pci1760)
+ * Author: H Hartley Sweeten <hsweeten@visionengravers.com>
+ * Updated: Fri, 13 Nov 2015 12:34:00 -0700
+ * Status: untested
+ *
+ * Configuration Options: not applicable, uses PCI auto config
+ */
+
+#include <linux/module.h>
+
+#include "../comedi_pci.h"
+
+/*
+ * PCI-1760 Register Map
+ *
+ * Outgoing Mailbox Bytes
+ * OMB3: Not used (must be 0)
+ * OMB2: The command code to the PCI-1760
+ * OMB1: The hi byte of the parameter for the command in OMB2
+ * OMB0: The lo byte of the parameter for the command in OMB2
+ *
+ * Incoming Mailbox Bytes
+ * IMB3: The Isolated Digital Input status (updated every 100us)
+ * IMB2: The current command (matches OMB2 when command is successful)
+ * IMB1: The hi byte of the feedback data for the command in OMB2
+ * IMB0: The lo byte of the feedback data for the command in OMB2
+ *
+ * Interrupt Control/Status
+ * INTCSR3: Not used (must be 0)
+ * INTCSR2: The interrupt status (read only)
+ * INTCSR1: Interrupt enable/disable
+ * INTCSR0: Not used (must be 0)
+ */
+#define PCI1760_OMB_REG(x) (0x0c + (x))
+#define PCI1760_IMB_REG(x) (0x1c + (x))
+#define PCI1760_INTCSR_REG(x) (0x38 + (x))
+#define PCI1760_INTCSR1_IRQ_ENA BIT(5)
+#define PCI1760_INTCSR2_OMB_IRQ BIT(0)
+#define PCI1760_INTCSR2_IMB_IRQ BIT(1)
+#define PCI1760_INTCSR2_IRQ_STATUS BIT(6)
+#define PCI1760_INTCSR2_IRQ_ASSERTED BIT(7)
+
+/* PCI-1760 command codes */
+#define PCI1760_CMD_CLR_IMB2 0x00 /* Clears IMB2 */
+#define PCI1760_CMD_SET_DO 0x01 /* Set output state */
+#define PCI1760_CMD_GET_DO 0x02 /* Read output status */
+#define PCI1760_CMD_GET_STATUS 0x03 /* Read current status */
+#define PCI1760_CMD_GET_FW_VER 0x0e /* Read firware version */
+#define PCI1760_CMD_GET_HW_VER 0x0f /* Read hardware version */
+#define PCI1760_CMD_SET_PWM_HI(x) (0x10 + (x) * 2) /* Set "hi" period */
+#define PCI1760_CMD_SET_PWM_LO(x) (0x11 + (x) * 2) /* Set "lo" period */
+#define PCI1760_CMD_SET_PWM_CNT(x) (0x14 + (x)) /* Set burst count */
+#define PCI1760_CMD_ENA_PWM 0x1f /* Enable PWM outputs */
+#define PCI1760_CMD_ENA_FILT 0x20 /* Enable input filter */
+#define PCI1760_CMD_ENA_PAT_MATCH 0x21 /* Enable input pattern match */
+#define PCI1760_CMD_SET_PAT_MATCH 0x22 /* Set input pattern match */
+#define PCI1760_CMD_ENA_RISE_EDGE 0x23 /* Enable input rising edge */
+#define PCI1760_CMD_ENA_FALL_EDGE 0x24 /* Enable input falling edge */
+#define PCI1760_CMD_ENA_CNT 0x28 /* Enable counter */
+#define PCI1760_CMD_RST_CNT 0x29 /* Reset counter */
+#define PCI1760_CMD_ENA_CNT_OFLOW 0x2a /* Enable counter overflow */
+#define PCI1760_CMD_ENA_CNT_MATCH 0x2b /* Enable counter match */
+#define PCI1760_CMD_SET_CNT_EDGE 0x2c /* Set counter edge */
+#define PCI1760_CMD_GET_CNT 0x2f /* Reads counter value */
+#define PCI1760_CMD_SET_HI_SAMP(x) (0x30 + (x)) /* Set "hi" sample time */
+#define PCI1760_CMD_SET_LO_SAMP(x) (0x38 + (x)) /* Set "lo" sample time */
+#define PCI1760_CMD_SET_CNT(x) (0x40 + (x)) /* Set counter reset val */
+#define PCI1760_CMD_SET_CNT_MATCH(x) (0x48 + (x)) /* Set counter match val */
+#define PCI1760_CMD_GET_INT_FLAGS 0x60 /* Read interrupt flags */
+#define PCI1760_CMD_GET_INT_FLAGS_MATCH BIT(0)
+#define PCI1760_CMD_GET_INT_FLAGS_COS BIT(1)
+#define PCI1760_CMD_GET_INT_FLAGS_OFLOW BIT(2)
+#define PCI1760_CMD_GET_OS 0x61 /* Read edge change flags */
+#define PCI1760_CMD_GET_CNT_STATUS 0x62 /* Read counter oflow/match */
+
+#define PCI1760_CMD_TIMEOUT 250 /* 250 usec timeout */
+#define PCI1760_CMD_RETRIES 3 /* limit number of retries */
+
+#define PCI1760_PWM_TIMEBASE 100000 /* 1 unit = 100 usec */
+
+static int pci1760_send_cmd(struct comedi_device *dev,
+ unsigned char cmd, unsigned short val)
+{
+ unsigned long timeout;
+
+ /* send the command and parameter */
+ outb(val & 0xff, dev->iobase + PCI1760_OMB_REG(0));
+ outb((val >> 8) & 0xff, dev->iobase + PCI1760_OMB_REG(1));
+ outb(cmd, dev->iobase + PCI1760_OMB_REG(2));
+ outb(0, dev->iobase + PCI1760_OMB_REG(3));
+
+ /* datasheet says to allow up to 250 usec for the command to complete */
+ timeout = jiffies + usecs_to_jiffies(PCI1760_CMD_TIMEOUT);
+ do {
+ if (inb(dev->iobase + PCI1760_IMB_REG(2)) == cmd) {
+ /* command success; return the feedback data */
+ return inb(dev->iobase + PCI1760_IMB_REG(0)) |
+ (inb(dev->iobase + PCI1760_IMB_REG(1)) << 8);
+ }
+ cpu_relax();
+ } while (time_before(jiffies, timeout));
+
+ return -EBUSY;
+}
+
+static int pci1760_cmd(struct comedi_device *dev,
+ unsigned char cmd, unsigned short val)
+{
+ int repeats;
+ int ret;
+
+ /* send PCI1760_CMD_CLR_IMB2 between identical commands */
+ if (inb(dev->iobase + PCI1760_IMB_REG(2)) == cmd) {
+ ret = pci1760_send_cmd(dev, PCI1760_CMD_CLR_IMB2, 0);
+ if (ret < 0) {
+ /* timeout? try it once more */
+ ret = pci1760_send_cmd(dev, PCI1760_CMD_CLR_IMB2, 0);
+ if (ret < 0)
+ return -ETIMEDOUT;
+ }
+ }
+
+ /* datasheet says to keep retrying the command */
+ for (repeats = 0; repeats < PCI1760_CMD_RETRIES; repeats++) {
+ ret = pci1760_send_cmd(dev, cmd, val);
+ if (ret >= 0)
+ return ret;
+ }
+
+ /* command failed! */
+ return -ETIMEDOUT;
+}
+
+static int pci1760_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = inb(dev->iobase + PCI1760_IMB_REG(3));
+
+ return insn->n;
+}
+
+static int pci1760_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int ret;
+
+ if (comedi_dio_update_state(s, data)) {
+ ret = pci1760_cmd(dev, PCI1760_CMD_SET_DO, s->state);
+ if (ret < 0)
+ return ret;
+ }
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int pci1760_pwm_ns_to_div(unsigned int flags, unsigned int ns)
+{
+ unsigned int divisor;
+
+ switch (flags) {
+ case CMDF_ROUND_NEAREST:
+ divisor = DIV_ROUND_CLOSEST(ns, PCI1760_PWM_TIMEBASE);
+ break;
+ case CMDF_ROUND_UP:
+ divisor = DIV_ROUND_UP(ns, PCI1760_PWM_TIMEBASE);
+ break;
+ case CMDF_ROUND_DOWN:
+ divisor = ns / PCI1760_PWM_TIMEBASE;
+ default:
+ return -EINVAL;
+ }
+
+ if (divisor < 1)
+ divisor = 1;
+ if (divisor > 0xffff)
+ divisor = 0xffff;
+
+ return divisor;
+}
+
+static int pci1760_pwm_enable(struct comedi_device *dev,
+ unsigned int chan, bool enable)
+{
+ int ret;
+
+ ret = pci1760_cmd(dev, PCI1760_CMD_GET_STATUS, PCI1760_CMD_ENA_PWM);
+ if (ret < 0)
+ return ret;
+
+ if (enable)
+ ret |= BIT(chan);
+ else
+ ret &= ~BIT(chan);
+
+ return pci1760_cmd(dev, PCI1760_CMD_ENA_PWM, ret);
+}
+
+static int pci1760_pwm_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int hi_div;
+ int lo_div;
+ int ret;
+
+ switch (data[0]) {
+ case INSN_CONFIG_ARM:
+ ret = pci1760_pwm_enable(dev, chan, false);
+ if (ret < 0)
+ return ret;
+
+ if (data[1] > 0xffff)
+ return -EINVAL;
+ ret = pci1760_cmd(dev, PCI1760_CMD_SET_PWM_CNT(chan), data[1]);
+ if (ret < 0)
+ return ret;
+
+ ret = pci1760_pwm_enable(dev, chan, true);
+ if (ret < 0)
+ return ret;
+ break;
+ case INSN_CONFIG_DISARM:
+ ret = pci1760_pwm_enable(dev, chan, false);
+ if (ret < 0)
+ return ret;
+ break;
+ case INSN_CONFIG_PWM_OUTPUT:
+ ret = pci1760_pwm_enable(dev, chan, false);
+ if (ret < 0)
+ return ret;
+
+ hi_div = pci1760_pwm_ns_to_div(data[1], data[2]);
+ lo_div = pci1760_pwm_ns_to_div(data[3], data[4]);
+ if (hi_div < 0 || lo_div < 0)
+ return -EINVAL;
+ if ((hi_div * PCI1760_PWM_TIMEBASE) != data[2] ||
+ (lo_div * PCI1760_PWM_TIMEBASE) != data[4]) {
+ data[2] = hi_div * PCI1760_PWM_TIMEBASE;
+ data[4] = lo_div * PCI1760_PWM_TIMEBASE;
+ return -EAGAIN;
+ }
+ ret = pci1760_cmd(dev, PCI1760_CMD_SET_PWM_HI(chan), hi_div);
+ if (ret < 0)
+ return ret;
+ ret = pci1760_cmd(dev, PCI1760_CMD_SET_PWM_LO(chan), lo_div);
+ if (ret < 0)
+ return ret;
+ break;
+ case INSN_CONFIG_GET_PWM_OUTPUT:
+ hi_div = pci1760_cmd(dev, PCI1760_CMD_GET_STATUS,
+ PCI1760_CMD_SET_PWM_HI(chan));
+ lo_div = pci1760_cmd(dev, PCI1760_CMD_GET_STATUS,
+ PCI1760_CMD_SET_PWM_LO(chan));
+ if (hi_div < 0 || lo_div < 0)
+ return -ETIMEDOUT;
+
+ data[1] = hi_div * PCI1760_PWM_TIMEBASE;
+ data[2] = lo_div * PCI1760_PWM_TIMEBASE;
+ break;
+ case INSN_CONFIG_GET_PWM_STATUS:
+ ret = pci1760_cmd(dev, PCI1760_CMD_GET_STATUS,
+ PCI1760_CMD_ENA_PWM);
+ if (ret < 0)
+ return ret;
+
+ data[1] = (ret & BIT(chan)) ? 1 : 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return insn->n;
+}
+
+static void pci1760_reset(struct comedi_device *dev)
+{
+ int i;
+
+ /* disable interrupts (intcsr2 is read-only) */
+ outb(0, dev->iobase + PCI1760_INTCSR_REG(0));
+ outb(0, dev->iobase + PCI1760_INTCSR_REG(1));
+ outb(0, dev->iobase + PCI1760_INTCSR_REG(3));
+
+ /* disable counters */
+ pci1760_cmd(dev, PCI1760_CMD_ENA_CNT, 0);
+
+ /* disable overflow interrupts */
+ pci1760_cmd(dev, PCI1760_CMD_ENA_CNT_OFLOW, 0);
+
+ /* disable match */
+ pci1760_cmd(dev, PCI1760_CMD_ENA_CNT_MATCH, 0);
+
+ /* set match and counter reset values */
+ for (i = 0; i < 8; i++) {
+ pci1760_cmd(dev, PCI1760_CMD_SET_CNT_MATCH(i), 0x8000);
+ pci1760_cmd(dev, PCI1760_CMD_SET_CNT(i), 0x0000);
+ }
+
+ /* reset counters to reset values */
+ pci1760_cmd(dev, PCI1760_CMD_RST_CNT, 0xff);
+
+ /* set counter count edges */
+ pci1760_cmd(dev, PCI1760_CMD_SET_CNT_EDGE, 0);
+
+ /* disable input filters */
+ pci1760_cmd(dev, PCI1760_CMD_ENA_FILT, 0);
+
+ /* disable pattern matching */
+ pci1760_cmd(dev, PCI1760_CMD_ENA_PAT_MATCH, 0);
+
+ /* set pattern match value */
+ pci1760_cmd(dev, PCI1760_CMD_SET_PAT_MATCH, 0);
+}
+
+static int pci1760_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+ dev->iobase = pci_resource_start(pcidev, 0);
+
+ pci1760_reset(dev);
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ /* Digital Input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pci1760_di_insn_bits;
+
+ /* Digital Output subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pci1760_do_insn_bits;
+
+ /* get the current state of the outputs */
+ ret = pci1760_cmd(dev, PCI1760_CMD_GET_DO, 0);
+ if (ret < 0)
+ return ret;
+ s->state = ret;
+
+ /* PWM subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_PWM;
+ s->subdev_flags = SDF_PWM_COUNTER;
+ s->n_chan = 2;
+ s->insn_config = pci1760_pwm_insn_config;
+
+ /* Counter subdevice */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_UNUSED;
+
+ return 0;
+}
+
+static struct comedi_driver pci1760_driver = {
+ .driver_name = "adv_pci1760",
+ .module = THIS_MODULE,
+ .auto_attach = pci1760_auto_attach,
+ .detach = comedi_pci_detach,
+};
+
+static int pci1760_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &pci1760_driver, id->driver_data);
+}
+
+static const struct pci_device_id pci1760_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1760) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, pci1760_pci_table);
+
+static struct pci_driver pci1760_pci_driver = {
+ .name = "adv_pci1760",
+ .id_table = pci1760_pci_table,
+ .probe = pci1760_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(pci1760_driver, pci1760_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for Advantech PCI-1760");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/adv_pci_dio.c b/drivers/staging/comedi/drivers/adv_pci_dio.c
index f1b3c5aa8d79..620cec13d74c 100644
--- a/drivers/staging/comedi/drivers/adv_pci_dio.c
+++ b/drivers/staging/comedi/drivers/adv_pci_dio.c
@@ -4,30 +4,21 @@
* Author: Michal Dobes <dobes@tesnet.cz>
*
* Hardware driver for Advantech PCI DIO cards.
-*/
-/*
-Driver: adv_pci_dio
-Description: Advantech PCI-1730, PCI-1733, PCI-1734, PCI-1735U,
- PCI-1736UP, PCI-1739U, PCI-1750, PCI-1751, PCI-1752,
- PCI-1753/E, PCI-1754, PCI-1756, PCI-1760, PCI-1762
-Author: Michal Dobes <dobes@tesnet.cz>
-Devices: [Advantech] PCI-1730 (adv_pci_dio), PCI-1733,
- PCI-1734, PCI-1735U, PCI-1736UP, PCI-1739U, PCI-1750,
- PCI-1751, PCI-1752, PCI-1753,
- PCI-1753+PCI-1753E, PCI-1754, PCI-1756,
- PCI-1760, PCI-1762
-Status: untested
-Updated: Mon, 09 Jan 2012 12:40:46 +0000
-
-This driver supports now only insn interface for DI/DO/DIO.
+ */
-Configuration options:
- [0] - PCI bus of device (optional)
- [1] - PCI slot of device (optional)
- If bus/slot is not specified, the first available PCI
- device will be used.
-
-*/
+/*
+ * Driver: adv_pci_dio
+ * Description: Advantech Digital I/O Cards
+ * Devices: [Advantech] PCI-1730 (adv_pci_dio), PCI-1733,
+ * PCI-1734, PCI-1735U, PCI-1736UP, PCI-1739U, PCI-1750,
+ * PCI-1751, PCI-1752, PCI-1753, PCI-1753+PCI-1753E,
+ * PCI-1754, PCI-1756, PCI-1762
+ * Author: Michal Dobes <dobes@tesnet.cz>
+ * Updated: Mon, 09 Jan 2012 12:40:46 +0000
+ * Status: untested
+ *
+ * Configuration Options: not applicable, uses PCI auto config
+ */
#include <linux/module.h>
#include <linux/delay.h>
@@ -37,403 +28,199 @@ Configuration options:
#include "8255.h"
#include "comedi_8254.h"
-/* hardware types of the cards */
-enum hw_cards_id {
- TYPE_PCI1730, TYPE_PCI1733, TYPE_PCI1734, TYPE_PCI1735, TYPE_PCI1736,
- TYPE_PCI1739,
- TYPE_PCI1750,
- TYPE_PCI1751,
- TYPE_PCI1752,
- TYPE_PCI1753, TYPE_PCI1753E,
- TYPE_PCI1754, TYPE_PCI1756,
- TYPE_PCI1760,
- TYPE_PCI1762
-};
-
-/* which I/O instructions to use */
-enum hw_io_access {
- IO_8b, IO_16b
-};
-
-#define MAX_DI_SUBDEVS 2 /* max number of DI subdevices per card */
-#define MAX_DO_SUBDEVS 2 /* max number of DO subdevices per card */
-#define MAX_DIO_SUBDEVG 2 /* max number of DIO subdevices group per
- * card */
-
-#define PCIDIO_MAINREG 2 /* main I/O region for all Advantech cards? */
-
-/* Register offset definitions */
-/* Advantech PCI-1730/3/4 */
-#define PCI1730_IDI 0 /* R: Isolated digital input 0-15 */
-#define PCI1730_IDO 0 /* W: Isolated digital output 0-15 */
-#define PCI1730_DI 2 /* R: Digital input 0-15 */
-#define PCI1730_DO 2 /* W: Digital output 0-15 */
-#define PCI1733_IDI 0 /* R: Isolated digital input 0-31 */
-#define PCI1730_3_INT_EN 0x08 /* R/W: enable/disable interrupts */
-#define PCI1730_3_INT_RF 0x0c /* R/W: set falling/raising edge for
- * interrupts */
-#define PCI1730_3_INT_CLR 0x10 /* R/W: clear interrupts */
-#define PCI1734_IDO 0 /* W: Isolated digital output 0-31 */
-#define PCI173x_BOARDID 4 /* R: Board I/D switch for 1730/3/4 */
-
-/* Advantech PCI-1735U */
-#define PCI1735_DI 0 /* R: Digital input 0-31 */
-#define PCI1735_DO 0 /* W: Digital output 0-31 */
-#define PCI1735_C8254 4 /* R/W: 8254 counter */
-#define PCI1735_BOARDID 8 /* R: Board I/D switch for 1735U */
-
-/* Advantech PCI-1736UP */
-#define PCI1736_IDI 0 /* R: Isolated digital input 0-15 */
-#define PCI1736_IDO 0 /* W: Isolated digital output 0-15 */
-#define PCI1736_3_INT_EN 0x08 /* R/W: enable/disable interrupts */
-#define PCI1736_3_INT_RF 0x0c /* R/W: set falling/raising edge for
- * interrupts */
-#define PCI1736_3_INT_CLR 0x10 /* R/W: clear interrupts */
-#define PCI1736_BOARDID 4 /* R: Board I/D switch for 1736UP */
-#define PCI1736_MAINREG 0 /* Normal register (2) doesn't work */
+/*
+ * Register offset definitions
+ */
-/* Advantech PCI-1739U */
-#define PCI1739_DIO 0 /* R/W: begin of 8255 registers block */
-#define PCI1739_ICR 32 /* W: Interrupt control register */
-#define PCI1739_ISR 32 /* R: Interrupt status register */
-#define PCI1739_BOARDID 8 /* R: Board I/D switch for 1739U */
+/* PCI-1730, PCI-1733, PCI-1736 interrupt control registers */
+#define PCI173X_INT_EN_REG 0x08 /* R/W: enable/disable */
+#define PCI173X_INT_RF_REG 0x0c /* R/W: falling/rising edge */
+#define PCI173X_INT_CLR_REG 0x10 /* R/W: clear */
-/* Advantech PCI-1750 */
-#define PCI1750_IDI 0 /* R: Isolated digital input 0-15 */
-#define PCI1750_IDO 0 /* W: Isolated digital output 0-15 */
-#define PCI1750_ICR 32 /* W: Interrupt control register */
-#define PCI1750_ISR 32 /* R: Interrupt status register */
+/* PCI-1739U, PCI-1750, PCI1751 interrupt control registers */
+#define PCI1750_INT_REG 0x20 /* R/W: status/control */
-/* Advantech PCI-1751/3/3E */
-#define PCI1751_DIO 0 /* R/W: begin of 8255 registers block */
-#define PCI1751_CNT 24 /* R/W: begin of 8254 registers block */
-#define PCI1751_ICR 32 /* W: Interrupt control register */
-#define PCI1751_ISR 32 /* R: Interrupt status register */
-#define PCI1753_DIO 0 /* R/W: begin of 8255 registers block */
-#define PCI1753_ICR0 16 /* R/W: Interrupt control register group 0 */
-#define PCI1753_ICR1 17 /* R/W: Interrupt control register group 1 */
-#define PCI1753_ICR2 18 /* R/W: Interrupt control register group 2 */
-#define PCI1753_ICR3 19 /* R/W: Interrupt control register group 3 */
-#define PCI1753E_DIO 32 /* R/W: begin of 8255 registers block */
-#define PCI1753E_ICR0 48 /* R/W: Interrupt control register group 0 */
-#define PCI1753E_ICR1 49 /* R/W: Interrupt control register group 1 */
-#define PCI1753E_ICR2 50 /* R/W: Interrupt control register group 2 */
-#define PCI1753E_ICR3 51 /* R/W: Interrupt control register group 3 */
+/* PCI-1753, PCI-1753E interrupt control registers */
+#define PCI1753_INT_REG(x) (0x10 + (x)) /* R/W: control group 0 to 3 */
+#define PCI1753E_INT_REG(x) (0x30 + (x)) /* R/W: control group 0 to 3 */
-/* Advantech PCI-1752/4/6 */
-#define PCI1752_IDO 0 /* R/W: Digital output 0-31 */
-#define PCI1752_IDO2 4 /* R/W: Digital output 32-63 */
-#define PCI1754_IDI 0 /* R: Digital input 0-31 */
-#define PCI1754_IDI2 4 /* R: Digital input 32-64 */
-#define PCI1756_IDI 0 /* R: Digital input 0-31 */
-#define PCI1756_IDO 4 /* R/W: Digital output 0-31 */
-#define PCI1754_6_ICR0 0x08 /* R/W: Interrupt control register group 0 */
-#define PCI1754_6_ICR1 0x0a /* R/W: Interrupt control register group 1 */
-#define PCI1754_ICR2 0x0c /* R/W: Interrupt control register group 2 */
-#define PCI1754_ICR3 0x0e /* R/W: Interrupt control register group 3 */
-#define PCI1752_6_CFC 0x12 /* R/W: set/read channel freeze function */
-#define PCI175x_BOARDID 0x10 /* R: Board I/D switch for 1752/4/6 */
+/* PCI-1754, PCI-1756 interrupt control registers */
+#define PCI1754_INT_REG(x) (0x08 + (x) * 2) /* R/W: control group 0 to 3 */
-/* Advantech PCI-1762 registers */
-#define PCI1762_RO 0 /* R/W: Relays status/output */
-#define PCI1762_IDI 2 /* R: Isolated input status */
-#define PCI1762_BOARDID 4 /* R: Board I/D switch */
-#define PCI1762_ICR 6 /* W: Interrupt control register */
-#define PCI1762_ISR 6 /* R: Interrupt status register */
+/* PCI-1752, PCI-1756 special registers */
+#define PCI1752_CFC_REG 0x12 /* R/W: channel freeze function */
-/* Advantech PCI-1760 registers */
-#define OMB0 0x0c /* W: Mailbox outgoing registers */
-#define OMB1 0x0d
-#define OMB2 0x0e
-#define OMB3 0x0f
-#define IMB0 0x1c /* R: Mailbox incoming registers */
-#define IMB1 0x1d
-#define IMB2 0x1e
-#define IMB3 0x1f
-#define INTCSR0 0x38 /* R/W: Interrupt control registers */
-#define INTCSR1 0x39
-#define INTCSR2 0x3a
-#define INTCSR3 0x3b
+/* PCI-1762 interrupt control registers */
+#define PCI1762_INT_REG 0x06 /* R/W: status/control */
-/* PCI-1760 mailbox commands */
-#define CMD_ClearIMB2 0x00 /* Clear IMB2 status and return actual
- * DI status in IMB3 */
-#define CMD_SetRelaysOutput 0x01 /* Set relay output from OMB0 */
-#define CMD_GetRelaysStatus 0x02 /* Get relay status to IMB0 */
-#define CMD_ReadCurrentStatus 0x07 /* Read the current status of the
- * register in OMB0, result in IMB0 */
-#define CMD_ReadFirmwareVersion 0x0e /* Read the firmware ver., result in
- * IMB1.IMB0 */
-#define CMD_ReadHardwareVersion 0x0f /* Read the hardware ver., result in
- * IMB1.IMB0 */
-#define CMD_EnableIDIFilters 0x20 /* Enable IDI filters based on bits in
- * OMB0 */
-#define CMD_EnableIDIPatternMatch 0x21 /* Enable IDI pattern match based on
- * bits in OMB0 */
-#define CMD_SetIDIPatternMatch 0x22 /* Enable IDI pattern match based on
- * bits in OMB0 */
-#define CMD_EnableIDICounters 0x28 /* Enable IDI counters based on bits in
- * OMB0 */
-#define CMD_ResetIDICounters 0x29 /* Reset IDI counters based on bits in
- * OMB0 to its reset values */
-#define CMD_OverflowIDICounters 0x2a /* Enable IDI counters overflow
- * interrupts based on bits in OMB0 */
-#define CMD_MatchIntIDICounters 0x2b /* Enable IDI counters match value
- * interrupts based on bits in OMB0 */
-#define CMD_EdgeIDICounters 0x2c /* Set IDI up counters count edge (bit=0
- * - rising, =1 - falling) */
-#define CMD_GetIDICntCurValue 0x2f /* Read IDI{OMB0} up counter current
- * value */
-#define CMD_SetIDI0CntResetValue 0x40 /* Set IDI0 Counter Reset Value
- * 256*OMB1+OMB0 */
-#define CMD_SetIDI1CntResetValue 0x41 /* Set IDI1 Counter Reset Value
- * 256*OMB1+OMB0 */
-#define CMD_SetIDI2CntResetValue 0x42 /* Set IDI2 Counter Reset Value
- * 256*OMB1+OMB0 */
-#define CMD_SetIDI3CntResetValue 0x43 /* Set IDI3 Counter Reset Value
- * 256*OMB1+OMB0 */
-#define CMD_SetIDI4CntResetValue 0x44 /* Set IDI4 Counter Reset Value
- * 256*OMB1+OMB0 */
-#define CMD_SetIDI5CntResetValue 0x45 /* Set IDI5 Counter Reset Value
- * 256*OMB1+OMB0 */
-#define CMD_SetIDI6CntResetValue 0x46 /* Set IDI6 Counter Reset Value
- * 256*OMB1+OMB0 */
-#define CMD_SetIDI7CntResetValue 0x47 /* Set IDI7 Counter Reset Value
- * 256*OMB1+OMB0 */
-#define CMD_SetIDI0CntMatchValue 0x48 /* Set IDI0 Counter Match Value
- * 256*OMB1+OMB0 */
-#define CMD_SetIDI1CntMatchValue 0x49 /* Set IDI1 Counter Match Value
- * 256*OMB1+OMB0 */
-#define CMD_SetIDI2CntMatchValue 0x4a /* Set IDI2 Counter Match Value
- * 256*OMB1+OMB0 */
-#define CMD_SetIDI3CntMatchValue 0x4b /* Set IDI3 Counter Match Value
- * 256*OMB1+OMB0 */
-#define CMD_SetIDI4CntMatchValue 0x4c /* Set IDI4 Counter Match Value
- * 256*OMB1+OMB0 */
-#define CMD_SetIDI5CntMatchValue 0x4d /* Set IDI5 Counter Match Value
- * 256*OMB1+OMB0 */
-#define CMD_SetIDI6CntMatchValue 0x4e /* Set IDI6 Counter Match Value
- * 256*OMB1+OMB0 */
-#define CMD_SetIDI7CntMatchValue 0x4f /* Set IDI7 Counter Match Value
- * 256*OMB1+OMB0 */
+/* maximum number of subdevice descriptions in the boardinfo */
+#define PCI_DIO_MAX_DI_SUBDEVS 2 /* 2 x 8/16/32 input channels max */
+#define PCI_DIO_MAX_DO_SUBDEVS 2 /* 2 x 8/16/32 output channels max */
+#define PCI_DIO_MAX_DIO_SUBDEVG 2 /* 2 x any number of 8255 devices max */
-#define OMBCMD_RETRY 0x03 /* 3 times try request before error */
+enum pci_dio_boardid {
+ TYPE_PCI1730,
+ TYPE_PCI1733,
+ TYPE_PCI1734,
+ TYPE_PCI1735,
+ TYPE_PCI1736,
+ TYPE_PCI1739,
+ TYPE_PCI1750,
+ TYPE_PCI1751,
+ TYPE_PCI1752,
+ TYPE_PCI1753,
+ TYPE_PCI1753E,
+ TYPE_PCI1754,
+ TYPE_PCI1756,
+ TYPE_PCI1762
+};
struct diosubd_data {
- int chans; /* num of chans */
- int addr; /* PCI address ofset */
- int regs; /* number of registers to read or 8255
- subdevices */
- unsigned int specflags; /* addon subdevice flags */
+ int chans; /* num of chans or 8255 devices */
+ unsigned long addr; /* PCI address ofset */
};
struct dio_boardtype {
const char *name; /* board name */
- int main_pci_region; /* main I/O PCI region */
- enum hw_cards_id cardtype;
int nsubdevs;
- struct diosubd_data sdi[MAX_DI_SUBDEVS]; /* DI chans */
- struct diosubd_data sdo[MAX_DO_SUBDEVS]; /* DO chans */
- struct diosubd_data sdio[MAX_DIO_SUBDEVG]; /* DIO 8255 chans */
- struct diosubd_data boardid; /* card supports board ID switch */
+ struct diosubd_data sdi[PCI_DIO_MAX_DI_SUBDEVS];
+ struct diosubd_data sdo[PCI_DIO_MAX_DO_SUBDEVS];
+ struct diosubd_data sdio[PCI_DIO_MAX_DIO_SUBDEVG];
+ unsigned long id_reg;
unsigned long timer_regbase;
- enum hw_io_access io_access;
+ unsigned int is_16bit:1;
};
static const struct dio_boardtype boardtypes[] = {
[TYPE_PCI1730] = {
.name = "pci1730",
- .main_pci_region = PCIDIO_MAINREG,
- .cardtype = TYPE_PCI1730,
.nsubdevs = 5,
- .sdi[0] = { 16, PCI1730_DI, 2, 0, },
- .sdi[1] = { 16, PCI1730_IDI, 2, 0, },
- .sdo[0] = { 16, PCI1730_DO, 2, 0, },
- .sdo[1] = { 16, PCI1730_IDO, 2, 0, },
- .boardid = { 4, PCI173x_BOARDID, 1, SDF_INTERNAL, },
- .io_access = IO_8b,
+ .sdi[0] = { 16, 0x02, }, /* DI 0-15 */
+ .sdi[1] = { 16, 0x00, }, /* ISO DI 0-15 */
+ .sdo[0] = { 16, 0x02, }, /* DO 0-15 */
+ .sdo[1] = { 16, 0x00, }, /* ISO DO 0-15 */
+ .id_reg = 0x04,
},
[TYPE_PCI1733] = {
.name = "pci1733",
- .main_pci_region = PCIDIO_MAINREG,
- .cardtype = TYPE_PCI1733,
.nsubdevs = 2,
- .sdi[1] = { 32, PCI1733_IDI, 4, 0, },
- .boardid = { 4, PCI173x_BOARDID, 1, SDF_INTERNAL, },
- .io_access = IO_8b,
+ .sdi[1] = { 32, 0x00, }, /* ISO DI 0-31 */
+ .id_reg = 0x04,
},
[TYPE_PCI1734] = {
.name = "pci1734",
- .main_pci_region = PCIDIO_MAINREG,
- .cardtype = TYPE_PCI1734,
.nsubdevs = 2,
- .sdo[1] = { 32, PCI1734_IDO, 4, 0, },
- .boardid = { 4, PCI173x_BOARDID, 1, SDF_INTERNAL, },
- .io_access = IO_8b,
+ .sdo[1] = { 32, 0x00, }, /* ISO DO 0-31 */
+ .id_reg = 0x04,
},
[TYPE_PCI1735] = {
.name = "pci1735",
- .main_pci_region = PCIDIO_MAINREG,
- .cardtype = TYPE_PCI1735,
.nsubdevs = 4,
- .sdi[0] = { 32, PCI1735_DI, 4, 0, },
- .sdo[0] = { 32, PCI1735_DO, 4, 0, },
- .boardid = { 4, PCI1735_BOARDID, 1, SDF_INTERNAL, },
- .timer_regbase = PCI1735_C8254,
- .io_access = IO_8b,
+ .sdi[0] = { 32, 0x00, }, /* DI 0-31 */
+ .sdo[0] = { 32, 0x00, }, /* DO 0-31 */
+ .id_reg = 0x08,
+ .timer_regbase = 0x04,
},
[TYPE_PCI1736] = {
.name = "pci1736",
- .main_pci_region = PCI1736_MAINREG,
- .cardtype = TYPE_PCI1736,
.nsubdevs = 3,
- .sdi[1] = { 16, PCI1736_IDI, 2, 0, },
- .sdo[1] = { 16, PCI1736_IDO, 2, 0, },
- .boardid = { 4, PCI1736_BOARDID, 1, SDF_INTERNAL, },
- .io_access = IO_8b,
+ .sdi[1] = { 16, 0x00, }, /* ISO DI 0-15 */
+ .sdo[1] = { 16, 0x00, }, /* ISO DO 0-15 */
+ .id_reg = 0x04,
},
[TYPE_PCI1739] = {
.name = "pci1739",
- .main_pci_region = PCIDIO_MAINREG,
- .cardtype = TYPE_PCI1739,
- .nsubdevs = 2,
- .sdio[0] = { 48, PCI1739_DIO, 2, 0, },
- .io_access = IO_8b,
+ .nsubdevs = 3,
+ .sdio[0] = { 2, 0x00, }, /* 8255 DIO */
+ .id_reg = 0x08,
},
[TYPE_PCI1750] = {
.name = "pci1750",
- .main_pci_region = PCIDIO_MAINREG,
- .cardtype = TYPE_PCI1750,
.nsubdevs = 2,
- .sdi[1] = { 16, PCI1750_IDI, 2, 0, },
- .sdo[1] = { 16, PCI1750_IDO, 2, 0, },
- .io_access = IO_8b,
+ .sdi[1] = { 16, 0x00, }, /* ISO DI 0-15 */
+ .sdo[1] = { 16, 0x00, }, /* ISO DO 0-15 */
},
[TYPE_PCI1751] = {
.name = "pci1751",
- .main_pci_region = PCIDIO_MAINREG,
- .cardtype = TYPE_PCI1751,
.nsubdevs = 3,
- .sdio[0] = { 48, PCI1751_DIO, 2, 0, },
- .timer_regbase = PCI1751_CNT,
- .io_access = IO_8b,
+ .sdio[0] = { 2, 0x00, }, /* 8255 DIO */
+ .timer_regbase = 0x18,
},
[TYPE_PCI1752] = {
.name = "pci1752",
- .main_pci_region = PCIDIO_MAINREG,
- .cardtype = TYPE_PCI1752,
.nsubdevs = 3,
- .sdo[0] = { 32, PCI1752_IDO, 2, 0, },
- .sdo[1] = { 32, PCI1752_IDO2, 2, 0, },
- .boardid = { 4, PCI175x_BOARDID, 1, SDF_INTERNAL, },
- .io_access = IO_16b,
+ .sdo[0] = { 32, 0x00, }, /* DO 0-31 */
+ .sdo[1] = { 32, 0x04, }, /* DO 32-63 */
+ .id_reg = 0x10,
+ .is_16bit = 1,
},
[TYPE_PCI1753] = {
.name = "pci1753",
- .main_pci_region = PCIDIO_MAINREG,
- .cardtype = TYPE_PCI1753,
.nsubdevs = 4,
- .sdio[0] = { 96, PCI1753_DIO, 4, 0, },
- .io_access = IO_8b,
+ .sdio[0] = { 4, 0x00, }, /* 8255 DIO */
},
[TYPE_PCI1753E] = {
.name = "pci1753e",
- .main_pci_region = PCIDIO_MAINREG,
- .cardtype = TYPE_PCI1753E,
.nsubdevs = 8,
- .sdio[0] = { 96, PCI1753_DIO, 4, 0, },
- .sdio[1] = { 96, PCI1753E_DIO, 4, 0, },
- .io_access = IO_8b,
+ .sdio[0] = { 4, 0x00, }, /* 8255 DIO */
+ .sdio[1] = { 4, 0x20, }, /* 8255 DIO */
},
[TYPE_PCI1754] = {
.name = "pci1754",
- .main_pci_region = PCIDIO_MAINREG,
- .cardtype = TYPE_PCI1754,
.nsubdevs = 3,
- .sdi[0] = { 32, PCI1754_IDI, 2, 0, },
- .sdi[1] = { 32, PCI1754_IDI2, 2, 0, },
- .boardid = { 4, PCI175x_BOARDID, 1, SDF_INTERNAL, },
- .io_access = IO_16b,
+ .sdi[0] = { 32, 0x00, }, /* DI 0-31 */
+ .sdi[1] = { 32, 0x04, }, /* DI 32-63 */
+ .id_reg = 0x10,
+ .is_16bit = 1,
},
[TYPE_PCI1756] = {
.name = "pci1756",
- .main_pci_region = PCIDIO_MAINREG,
- .cardtype = TYPE_PCI1756,
.nsubdevs = 3,
- .sdi[1] = { 32, PCI1756_IDI, 2, 0, },
- .sdo[1] = { 32, PCI1756_IDO, 2, 0, },
- .boardid = { 4, PCI175x_BOARDID, 1, SDF_INTERNAL, },
- .io_access = IO_16b,
- },
- [TYPE_PCI1760] = {
- /* This card has its own 'attach' */
- .name = "pci1760",
- .main_pci_region = 0,
- .cardtype = TYPE_PCI1760,
- .nsubdevs = 4,
- .io_access = IO_8b,
+ .sdi[1] = { 32, 0x00, }, /* DI 0-31 */
+ .sdo[1] = { 32, 0x04, }, /* DO 0-31 */
+ .id_reg = 0x10,
+ .is_16bit = 1,
},
[TYPE_PCI1762] = {
.name = "pci1762",
- .main_pci_region = PCIDIO_MAINREG,
- .cardtype = TYPE_PCI1762,
.nsubdevs = 3,
- .sdi[1] = { 16, PCI1762_IDI, 1, 0, },
- .sdo[1] = { 16, PCI1762_RO, 1, 0, },
- .boardid = { 4, PCI1762_BOARDID, 1, SDF_INTERNAL, },
- .io_access = IO_16b,
+ .sdi[1] = { 16, 0x02, }, /* ISO DI 0-15 */
+ .sdo[1] = { 16, 0x00, }, /* ISO DO 0-15 */
+ .id_reg = 0x04,
+ .is_16bit = 1,
},
};
-struct pci_dio_private {
- char GlobalIrqEnabled; /* 1= any IRQ source is enabled */
- /* PCI-1760 specific data */
- unsigned char IDICntEnable; /* counter's counting enable status */
- unsigned char IDICntOverEnable; /* counter's overflow interrupts enable
- * status */
- unsigned char IDICntMatchEnable; /* counter's match interrupts
- * enable status */
- unsigned char IDICntEdge; /* counter's count edge value
- * (bit=0 - rising, =1 - falling) */
- unsigned short CntResValue[8]; /* counters' reset value */
- unsigned short CntMatchValue[8]; /* counters' match interrupt value */
- unsigned char IDIFiltersEn; /* IDI's digital filters enable status */
- unsigned char IDIPatMatchEn; /* IDI's pattern match enable status */
- unsigned char IDIPatMatchValue; /* IDI's pattern match value */
- unsigned short IDIFiltrLow[8]; /* IDI's filter value low signal */
- unsigned short IDIFiltrHigh[8]; /* IDI's filter value high signal */
-};
-
-/*
-==============================================================================
-*/
static int pci_dio_insn_bits_di_b(struct comedi_device *dev,
struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
+ struct comedi_insn *insn,
+ unsigned int *data)
{
- const struct diosubd_data *d = (const struct diosubd_data *)s->private;
- int i;
+ unsigned long reg = (unsigned long)s->private;
+ unsigned long iobase = dev->iobase + reg;
- data[1] = 0;
- for (i = 0; i < d->regs; i++)
- data[1] |= inb(dev->iobase + d->addr + i) << (8 * i);
+ data[1] = inb(iobase);
+ if (s->n_chan > 8)
+ data[1] |= (inb(iobase + 1) << 8);
+ if (s->n_chan > 16)
+ data[1] |= (inb(iobase + 2) << 16);
+ if (s->n_chan > 24)
+ data[1] |= (inb(iobase + 3) << 24);
return insn->n;
}
-/*
-==============================================================================
-*/
static int pci_dio_insn_bits_di_w(struct comedi_device *dev,
struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
+ struct comedi_insn *insn,
+ unsigned int *data)
{
- const struct diosubd_data *d = (const struct diosubd_data *)s->private;
- int i;
+ unsigned long reg = (unsigned long)s->private;
+ unsigned long iobase = dev->iobase + reg;
- data[1] = 0;
- for (i = 0; i < d->regs; i++)
- data[1] |= inw(dev->iobase + d->addr + 2 * i) << (16 * i);
+ data[1] = inw(iobase);
+ if (s->n_chan > 16)
+ data[1] |= (inw(iobase + 2) << 16);
return insn->n;
}
@@ -443,13 +230,17 @@ static int pci_dio_insn_bits_do_b(struct comedi_device *dev,
struct comedi_insn *insn,
unsigned int *data)
{
- const struct diosubd_data *d = (const struct diosubd_data *)s->private;
- int i;
+ unsigned long reg = (unsigned long)s->private;
+ unsigned long iobase = dev->iobase + reg;
if (comedi_dio_update_state(s, data)) {
- for (i = 0; i < d->regs; i++)
- outb((s->state >> (8 * i)) & 0xff,
- dev->iobase + d->addr + i);
+ outb(s->state & 0xff, iobase);
+ if (s->n_chan > 8)
+ outb((s->state >> 8) & 0xff, iobase + 1);
+ if (s->n_chan > 16)
+ outb((s->state >> 16) & 0xff, iobase + 2);
+ if (s->n_chan > 24)
+ outb((s->state >> 24) & 0xff, iobase + 3);
}
data[1] = s->state;
@@ -462,13 +253,13 @@ static int pci_dio_insn_bits_do_w(struct comedi_device *dev,
struct comedi_insn *insn,
unsigned int *data)
{
- const struct diosubd_data *d = (const struct diosubd_data *)s->private;
- int i;
+ unsigned long reg = (unsigned long)s->private;
+ unsigned long iobase = dev->iobase + reg;
if (comedi_dio_update_state(s, data)) {
- for (i = 0; i < d->regs; i++)
- outw((s->state >> (16 * i)) & 0xffff,
- dev->iobase + d->addr + 2 * i);
+ outw(s->state & 0xffff, iobase);
+ if (s->n_chan > 16)
+ outw((s->state >> 16) & 0xffff, iobase + 2);
}
data[1] = s->state;
@@ -476,510 +267,64 @@ static int pci_dio_insn_bits_do_w(struct comedi_device *dev,
return insn->n;
}
-/*
-==============================================================================
-*/
-static int pci1760_unchecked_mbxrequest(struct comedi_device *dev,
- unsigned char *omb, unsigned char *imb,
- int repeats)
-{
- int cnt, tout, ok = 0;
-
- for (cnt = 0; cnt < repeats; cnt++) {
- outb(omb[0], dev->iobase + OMB0);
- outb(omb[1], dev->iobase + OMB1);
- outb(omb[2], dev->iobase + OMB2);
- outb(omb[3], dev->iobase + OMB3);
- for (tout = 0; tout < 251; tout++) {
- imb[2] = inb(dev->iobase + IMB2);
- if (imb[2] == omb[2]) {
- imb[0] = inb(dev->iobase + IMB0);
- imb[1] = inb(dev->iobase + IMB1);
- imb[3] = inb(dev->iobase + IMB3);
- ok = 1;
- break;
- }
- udelay(1);
- }
- if (ok)
- return 0;
- }
-
- dev_err(dev->class_dev, "PCI-1760 mailbox request timeout!\n");
- return -ETIME;
-}
-
-static int pci1760_clear_imb2(struct comedi_device *dev)
-{
- unsigned char omb[4] = { 0x0, 0x0, CMD_ClearIMB2, 0x0 };
- unsigned char imb[4];
- /* check if imb2 is already clear */
- if (inb(dev->iobase + IMB2) == CMD_ClearIMB2)
- return 0;
- return pci1760_unchecked_mbxrequest(dev, omb, imb, OMBCMD_RETRY);
-}
-
-static int pci1760_mbxrequest(struct comedi_device *dev,
- unsigned char *omb, unsigned char *imb)
-{
- if (omb[2] == CMD_ClearIMB2) {
- dev_err(dev->class_dev,
- "bug! this function should not be used for CMD_ClearIMB2 command\n");
- return -EINVAL;
- }
- if (inb(dev->iobase + IMB2) == omb[2]) {
- int retval;
-
- retval = pci1760_clear_imb2(dev);
- if (retval < 0)
- return retval;
- }
- return pci1760_unchecked_mbxrequest(dev, omb, imb, OMBCMD_RETRY);
-}
-
-/*
-==============================================================================
-*/
-static int pci1760_insn_bits_di(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
+static int pci_dio_reset(struct comedi_device *dev, unsigned long cardtype)
{
- data[1] = inb(dev->iobase + IMB3);
+ /* disable channel freeze function on the PCI-1752/1756 boards */
+ if (cardtype == TYPE_PCI1752 || cardtype == TYPE_PCI1756)
+ outw(0, dev->iobase + PCI1752_CFC_REG);
- return insn->n;
-}
-
-static int pci1760_insn_bits_do(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- int ret;
- unsigned char omb[4] = {
- 0x00,
- 0x00,
- CMD_SetRelaysOutput,
- 0x00
- };
- unsigned char imb[4];
-
- if (comedi_dio_update_state(s, data)) {
- omb[0] = s->state;
- ret = pci1760_mbxrequest(dev, omb, imb);
- if (!ret)
- return ret;
- }
-
- data[1] = s->state;
-
- return insn->n;
-}
-
-/*
-==============================================================================
-*/
-static int pci1760_insn_cnt_read(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
-{
- int ret, n;
- unsigned char omb[4] = {
- CR_CHAN(insn->chanspec) & 0x07,
- 0x00,
- CMD_GetIDICntCurValue,
- 0x00
- };
- unsigned char imb[4];
-
- for (n = 0; n < insn->n; n++) {
- ret = pci1760_mbxrequest(dev, omb, imb);
- if (!ret)
- return ret;
- data[n] = (imb[1] << 8) + imb[0];
- }
-
- return n;
-}
-
-/*
-==============================================================================
-*/
-static int pci1760_insn_cnt_write(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
-{
- struct pci_dio_private *devpriv = dev->private;
- int ret;
- unsigned char chan = CR_CHAN(insn->chanspec) & 0x07;
- unsigned char bitmask = 1 << chan;
- unsigned char omb[4] = {
- data[0] & 0xff,
- (data[0] >> 8) & 0xff,
- CMD_SetIDI0CntResetValue + chan,
- 0x00
- };
- unsigned char imb[4];
-
- /* Set reset value if different */
- if (devpriv->CntResValue[chan] != (data[0] & 0xffff)) {
- ret = pci1760_mbxrequest(dev, omb, imb);
- if (!ret)
- return ret;
- devpriv->CntResValue[chan] = data[0] & 0xffff;
- }
-
- omb[0] = bitmask; /* reset counter to it reset value */
- omb[2] = CMD_ResetIDICounters;
- ret = pci1760_mbxrequest(dev, omb, imb);
- if (!ret)
- return ret;
-
- /* start counter if it don't run */
- if (!(bitmask & devpriv->IDICntEnable)) {
- omb[0] = bitmask;
- omb[2] = CMD_EnableIDICounters;
- ret = pci1760_mbxrequest(dev, omb, imb);
- if (!ret)
- return ret;
- devpriv->IDICntEnable |= bitmask;
- }
- return 1;
-}
-
-/*
-==============================================================================
-*/
-static int pci1760_reset(struct comedi_device *dev)
-{
- struct pci_dio_private *devpriv = dev->private;
- int i;
- unsigned char omb[4] = { 0x00, 0x00, 0x00, 0x00 };
- unsigned char imb[4];
-
- outb(0, dev->iobase + INTCSR0); /* disable IRQ */
- outb(0, dev->iobase + INTCSR1);
- outb(0, dev->iobase + INTCSR2);
- outb(0, dev->iobase + INTCSR3);
- devpriv->GlobalIrqEnabled = 0;
-
- omb[0] = 0x00;
- omb[2] = CMD_SetRelaysOutput; /* reset relay outputs */
- pci1760_mbxrequest(dev, omb, imb);
-
- omb[0] = 0x00;
- omb[2] = CMD_EnableIDICounters; /* disable IDI up counters */
- pci1760_mbxrequest(dev, omb, imb);
- devpriv->IDICntEnable = 0;
-
- omb[0] = 0x00;
- omb[2] = CMD_OverflowIDICounters; /* disable counters overflow
- * interrupts */
- pci1760_mbxrequest(dev, omb, imb);
- devpriv->IDICntOverEnable = 0;
-
- omb[0] = 0x00;
- omb[2] = CMD_MatchIntIDICounters; /* disable counters match value
- * interrupts */
- pci1760_mbxrequest(dev, omb, imb);
- devpriv->IDICntMatchEnable = 0;
-
- omb[0] = 0x00;
- omb[1] = 0x80;
- for (i = 0; i < 8; i++) { /* set IDI up counters match value */
- omb[2] = CMD_SetIDI0CntMatchValue + i;
- pci1760_mbxrequest(dev, omb, imb);
- devpriv->CntMatchValue[i] = 0x8000;
- }
-
- omb[0] = 0x00;
- omb[1] = 0x00;
- for (i = 0; i < 8; i++) { /* set IDI up counters reset value */
- omb[2] = CMD_SetIDI0CntResetValue + i;
- pci1760_mbxrequest(dev, omb, imb);
- devpriv->CntResValue[i] = 0x0000;
- }
-
- omb[0] = 0xff;
- omb[2] = CMD_ResetIDICounters; /* reset IDI up counters to reset
- * values */
- pci1760_mbxrequest(dev, omb, imb);
-
- omb[0] = 0x00;
- omb[2] = CMD_EdgeIDICounters; /* set IDI up counters count edge */
- pci1760_mbxrequest(dev, omb, imb);
- devpriv->IDICntEdge = 0x00;
-
- omb[0] = 0x00;
- omb[2] = CMD_EnableIDIFilters; /* disable all digital in filters */
- pci1760_mbxrequest(dev, omb, imb);
- devpriv->IDIFiltersEn = 0x00;
-
- omb[0] = 0x00;
- omb[2] = CMD_EnableIDIPatternMatch; /* disable pattern matching */
- pci1760_mbxrequest(dev, omb, imb);
- devpriv->IDIPatMatchEn = 0x00;
-
- omb[0] = 0x00;
- omb[2] = CMD_SetIDIPatternMatch; /* set pattern match value */
- pci1760_mbxrequest(dev, omb, imb);
- devpriv->IDIPatMatchValue = 0x00;
-
- return 0;
-}
-
-/*
-==============================================================================
-*/
-static int pci_dio_reset(struct comedi_device *dev)
-{
- const struct dio_boardtype *board = dev->board_ptr;
-
- switch (board->cardtype) {
+ /* disable and clear interrupts */
+ switch (cardtype) {
case TYPE_PCI1730:
- outb(0, dev->iobase + PCI1730_DO); /* clear outputs */
- outb(0, dev->iobase + PCI1730_DO + 1);
- outb(0, dev->iobase + PCI1730_IDO);
- outb(0, dev->iobase + PCI1730_IDO + 1);
- /* fallthrough */
case TYPE_PCI1733:
- /* disable interrupts */
- outb(0, dev->iobase + PCI1730_3_INT_EN);
- /* clear interrupts */
- outb(0x0f, dev->iobase + PCI1730_3_INT_CLR);
- /* set rising edge trigger */
- outb(0, dev->iobase + PCI1730_3_INT_RF);
- break;
- case TYPE_PCI1734:
- outb(0, dev->iobase + PCI1734_IDO); /* clear outputs */
- outb(0, dev->iobase + PCI1734_IDO + 1);
- outb(0, dev->iobase + PCI1734_IDO + 2);
- outb(0, dev->iobase + PCI1734_IDO + 3);
- break;
- case TYPE_PCI1735:
- outb(0, dev->iobase + PCI1735_DO); /* clear outputs */
- outb(0, dev->iobase + PCI1735_DO + 1);
- outb(0, dev->iobase + PCI1735_DO + 2);
- outb(0, dev->iobase + PCI1735_DO + 3);
- break;
-
case TYPE_PCI1736:
- outb(0, dev->iobase + PCI1736_IDO);
- outb(0, dev->iobase + PCI1736_IDO + 1);
- /* disable interrupts */
- outb(0, dev->iobase + PCI1736_3_INT_EN);
- /* clear interrupts */
- outb(0x0f, dev->iobase + PCI1736_3_INT_CLR);
- /* set rising edge trigger */
- outb(0, dev->iobase + PCI1736_3_INT_RF);
+ outb(0, dev->iobase + PCI173X_INT_EN_REG);
+ outb(0x0f, dev->iobase + PCI173X_INT_CLR_REG);
+ outb(0, dev->iobase + PCI173X_INT_RF_REG);
break;
-
case TYPE_PCI1739:
- /* disable & clear interrupts */
- outb(0x88, dev->iobase + PCI1739_ICR);
- break;
-
case TYPE_PCI1750:
case TYPE_PCI1751:
- /* disable & clear interrupts */
- outb(0x88, dev->iobase + PCI1750_ICR);
- break;
- case TYPE_PCI1752:
- outw(0, dev->iobase + PCI1752_6_CFC); /* disable channel freeze
- * function */
- outw(0, dev->iobase + PCI1752_IDO); /* clear outputs */
- outw(0, dev->iobase + PCI1752_IDO + 2);
- outw(0, dev->iobase + PCI1752_IDO2);
- outw(0, dev->iobase + PCI1752_IDO2 + 2);
+ outb(0x88, dev->iobase + PCI1750_INT_REG);
break;
- case TYPE_PCI1753E:
- outb(0x88, dev->iobase + PCI1753E_ICR0); /* disable & clear
- * interrupts */
- outb(0x80, dev->iobase + PCI1753E_ICR1);
- outb(0x80, dev->iobase + PCI1753E_ICR2);
- outb(0x80, dev->iobase + PCI1753E_ICR3);
- /* fallthrough */
case TYPE_PCI1753:
- outb(0x88, dev->iobase + PCI1753_ICR0); /* disable & clear
- * interrupts */
- outb(0x80, dev->iobase + PCI1753_ICR1);
- outb(0x80, dev->iobase + PCI1753_ICR2);
- outb(0x80, dev->iobase + PCI1753_ICR3);
+ case TYPE_PCI1753E:
+ outb(0x88, dev->iobase + PCI1753_INT_REG(0));
+ outb(0x80, dev->iobase + PCI1753_INT_REG(1));
+ outb(0x80, dev->iobase + PCI1753_INT_REG(2));
+ outb(0x80, dev->iobase + PCI1753_INT_REG(3));
+ if (cardtype == TYPE_PCI1753E) {
+ outb(0x88, dev->iobase + PCI1753E_INT_REG(0));
+ outb(0x80, dev->iobase + PCI1753E_INT_REG(1));
+ outb(0x80, dev->iobase + PCI1753E_INT_REG(2));
+ outb(0x80, dev->iobase + PCI1753E_INT_REG(3));
+ }
break;
case TYPE_PCI1754:
- outw(0x08, dev->iobase + PCI1754_6_ICR0); /* disable and clear
- * interrupts */
- outw(0x08, dev->iobase + PCI1754_6_ICR1);
- outw(0x08, dev->iobase + PCI1754_ICR2);
- outw(0x08, dev->iobase + PCI1754_ICR3);
- break;
case TYPE_PCI1756:
- outw(0, dev->iobase + PCI1752_6_CFC); /* disable channel freeze
- * function */
- outw(0x08, dev->iobase + PCI1754_6_ICR0); /* disable and clear
- * interrupts */
- outw(0x08, dev->iobase + PCI1754_6_ICR1);
- outw(0, dev->iobase + PCI1756_IDO); /* clear outputs */
- outw(0, dev->iobase + PCI1756_IDO + 2);
- break;
- case TYPE_PCI1760:
- pci1760_reset(dev);
+ outw(0x08, dev->iobase + PCI1754_INT_REG(0));
+ outw(0x08, dev->iobase + PCI1754_INT_REG(1));
+ if (cardtype == TYPE_PCI1754) {
+ outw(0x08, dev->iobase + PCI1754_INT_REG(2));
+ outw(0x08, dev->iobase + PCI1754_INT_REG(3));
+ }
break;
case TYPE_PCI1762:
- outw(0x0101, dev->iobase + PCI1762_ICR); /* disable & clear
- * interrupts */
- break;
- }
-
- return 0;
-}
-
-/*
-==============================================================================
-*/
-static int pci1760_attach(struct comedi_device *dev)
-{
- struct comedi_subdevice *s;
-
- s = &dev->subdevices[0];
- s->type = COMEDI_SUBD_DI;
- s->subdev_flags = SDF_READABLE;
- s->n_chan = 8;
- s->maxdata = 1;
- s->len_chanlist = 8;
- s->range_table = &range_digital;
- s->insn_bits = pci1760_insn_bits_di;
-
- s = &dev->subdevices[1];
- s->type = COMEDI_SUBD_DO;
- s->subdev_flags = SDF_WRITABLE;
- s->n_chan = 8;
- s->maxdata = 1;
- s->len_chanlist = 8;
- s->range_table = &range_digital;
- s->state = 0;
- s->insn_bits = pci1760_insn_bits_do;
-
- s = &dev->subdevices[2];
- s->type = COMEDI_SUBD_TIMER;
- s->subdev_flags = SDF_WRITABLE | SDF_LSAMPL;
- s->n_chan = 2;
- s->maxdata = 0xffffffff;
- s->len_chanlist = 2;
-/* s->insn_config=pci1760_insn_pwm_cfg; */
-
- s = &dev->subdevices[3];
- s->type = COMEDI_SUBD_COUNTER;
- s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
- s->n_chan = 8;
- s->maxdata = 0xffff;
- s->len_chanlist = 8;
- s->insn_read = pci1760_insn_cnt_read;
- s->insn_write = pci1760_insn_cnt_write;
-/* s->insn_config=pci1760_insn_cnt_cfg; */
-
- return 0;
-}
-
-/*
-==============================================================================
-*/
-static int pci_dio_add_di(struct comedi_device *dev,
- struct comedi_subdevice *s,
- const struct diosubd_data *d)
-{
- const struct dio_boardtype *board = dev->board_ptr;
-
- s->type = COMEDI_SUBD_DI;
- s->subdev_flags = SDF_READABLE | d->specflags;
- if (d->chans > 16)
- s->subdev_flags |= SDF_LSAMPL;
- s->n_chan = d->chans;
- s->maxdata = 1;
- s->len_chanlist = d->chans;
- s->range_table = &range_digital;
- switch (board->io_access) {
- case IO_8b:
- s->insn_bits = pci_dio_insn_bits_di_b;
- break;
- case IO_16b:
- s->insn_bits = pci_dio_insn_bits_di_w;
- break;
- }
- s->private = (void *)d;
-
- return 0;
-}
-
-/*
-==============================================================================
-*/
-static int pci_dio_add_do(struct comedi_device *dev,
- struct comedi_subdevice *s,
- const struct diosubd_data *d)
-{
- const struct dio_boardtype *board = dev->board_ptr;
-
- s->type = COMEDI_SUBD_DO;
- s->subdev_flags = SDF_WRITABLE;
- if (d->chans > 16)
- s->subdev_flags |= SDF_LSAMPL;
- s->n_chan = d->chans;
- s->maxdata = 1;
- s->len_chanlist = d->chans;
- s->range_table = &range_digital;
- s->state = 0;
- switch (board->io_access) {
- case IO_8b:
- s->insn_bits = pci_dio_insn_bits_do_b;
+ outw(0x0101, dev->iobase + PCI1762_INT_REG);
break;
- case IO_16b:
- s->insn_bits = pci_dio_insn_bits_do_w;
+ default:
break;
}
- s->private = (void *)d;
return 0;
}
-static unsigned long pci_dio_override_cardtype(struct pci_dev *pcidev,
- unsigned long cardtype)
-{
- /*
- * Change cardtype from TYPE_PCI1753 to TYPE_PCI1753E if expansion
- * board available. Need to enable PCI device and request the main
- * registers PCI BAR temporarily to perform the test.
- */
- if (cardtype != TYPE_PCI1753)
- return cardtype;
- if (pci_enable_device(pcidev) < 0)
- return cardtype;
- if (pci_request_region(pcidev, PCIDIO_MAINREG, "adv_pci_dio") == 0) {
- /*
- * This test is based on Advantech's "advdaq" driver source
- * (which declares its module licence as "GPL" although the
- * driver source does not include a "COPYING" file).
- */
- unsigned long reg =
- pci_resource_start(pcidev, PCIDIO_MAINREG) + 53;
-
- outb(0x05, reg);
- if ((inb(reg) & 0x07) == 0x02) {
- outb(0x02, reg);
- if ((inb(reg) & 0x07) == 0x05)
- cardtype = TYPE_PCI1753E;
- }
- pci_release_region(pcidev, PCIDIO_MAINREG);
- }
- pci_disable_device(pcidev);
- return cardtype;
-}
-
static int pci_dio_auto_attach(struct comedi_device *dev,
unsigned long context)
{
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
const struct dio_boardtype *board = NULL;
- struct pci_dio_private *devpriv;
+ const struct diosubd_data *d;
struct comedi_subdevice *s;
int ret, subdev, i, j;
@@ -990,54 +335,93 @@ static int pci_dio_auto_attach(struct comedi_device *dev,
dev->board_ptr = board;
dev->board_name = board->name;
- devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
- if (!devpriv)
- return -ENOMEM;
-
ret = comedi_pci_enable(dev);
if (ret)
return ret;
- dev->iobase = pci_resource_start(pcidev, board->main_pci_region);
+ if (context == TYPE_PCI1736)
+ dev->iobase = pci_resource_start(pcidev, 0);
+ else
+ dev->iobase = pci_resource_start(pcidev, 2);
+
+ pci_dio_reset(dev, context);
ret = comedi_alloc_subdevices(dev, board->nsubdevs);
if (ret)
return ret;
subdev = 0;
- for (i = 0; i < MAX_DI_SUBDEVS; i++)
- if (board->sdi[i].chans) {
- s = &dev->subdevices[subdev];
- pci_dio_add_di(dev, s, &board->sdi[i]);
- subdev++;
+ for (i = 0; i < PCI_DIO_MAX_DI_SUBDEVS; i++) {
+ d = &board->sdi[i];
+ if (d->chans) {
+ s = &dev->subdevices[subdev++];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = d->chans;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = board->is_16bit
+ ? pci_dio_insn_bits_di_w
+ : pci_dio_insn_bits_di_b;
+ s->private = (void *)d->addr;
}
+ }
- for (i = 0; i < MAX_DO_SUBDEVS; i++)
- if (board->sdo[i].chans) {
- s = &dev->subdevices[subdev];
- pci_dio_add_do(dev, s, &board->sdo[i]);
- subdev++;
+ for (i = 0; i < PCI_DIO_MAX_DO_SUBDEVS; i++) {
+ d = &board->sdo[i];
+ if (d->chans) {
+ s = &dev->subdevices[subdev++];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = d->chans;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = board->is_16bit
+ ? pci_dio_insn_bits_do_w
+ : pci_dio_insn_bits_do_b;
+ s->private = (void *)d->addr;
+
+ /* reset all outputs to 0 */
+ if (board->is_16bit) {
+ outw(0, dev->iobase + d->addr);
+ if (s->n_chan > 16)
+ outw(0, dev->iobase + d->addr + 2);
+ } else {
+ outb(0, dev->iobase + d->addr);
+ if (s->n_chan > 8)
+ outb(0, dev->iobase + d->addr + 1);
+ if (s->n_chan > 16)
+ outb(0, dev->iobase + d->addr + 2);
+ if (s->n_chan > 24)
+ outb(0, dev->iobase + d->addr + 3);
+ }
}
+ }
- for (i = 0; i < MAX_DIO_SUBDEVG; i++)
- for (j = 0; j < board->sdio[i].regs; j++) {
- s = &dev->subdevices[subdev];
+ for (i = 0; i < PCI_DIO_MAX_DIO_SUBDEVG; i++) {
+ d = &board->sdio[i];
+ for (j = 0; j < d->chans; j++) {
+ s = &dev->subdevices[subdev++];
ret = subdev_8255_init(dev, s, NULL,
- board->sdio[i].addr +
- j * I8255_SIZE);
+ d->addr + j * I8255_SIZE);
if (ret)
return ret;
- subdev++;
}
+ }
- if (board->boardid.chans) {
- s = &dev->subdevices[subdev];
- s->type = COMEDI_SUBD_DI;
- pci_dio_add_di(dev, s, &board->boardid);
- subdev++;
+ if (board->id_reg) {
+ s = &dev->subdevices[subdev++];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE | SDF_INTERNAL;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = board->is_16bit ? pci_dio_insn_bits_di_w
+ : pci_dio_insn_bits_di_b;
+ s->private = (void *)board->id_reg;
}
if (board->timer_regbase) {
- s = &dev->subdevices[subdev];
+ s = &dev->subdevices[subdev++];
dev->pacer = comedi_8254_init(dev->iobase +
board->timer_regbase,
@@ -1046,32 +430,50 @@ static int pci_dio_auto_attach(struct comedi_device *dev,
return -ENOMEM;
comedi_8254_subdevice_init(s, dev->pacer);
-
- subdev++;
}
- if (board->cardtype == TYPE_PCI1760)
- pci1760_attach(dev);
-
- pci_dio_reset(dev);
-
return 0;
}
-static void pci_dio_detach(struct comedi_device *dev)
-{
- if (dev->iobase)
- pci_dio_reset(dev);
- comedi_pci_detach(dev);
-}
-
static struct comedi_driver adv_pci_dio_driver = {
.driver_name = "adv_pci_dio",
.module = THIS_MODULE,
.auto_attach = pci_dio_auto_attach,
- .detach = pci_dio_detach,
+ .detach = comedi_pci_detach,
};
+static unsigned long pci_dio_override_cardtype(struct pci_dev *pcidev,
+ unsigned long cardtype)
+{
+ /*
+ * Change cardtype from TYPE_PCI1753 to TYPE_PCI1753E if expansion
+ * board available. Need to enable PCI device and request the main
+ * registers PCI BAR temporarily to perform the test.
+ */
+ if (cardtype != TYPE_PCI1753)
+ return cardtype;
+ if (pci_enable_device(pcidev) < 0)
+ return cardtype;
+ if (pci_request_region(pcidev, 2, "adv_pci_dio") == 0) {
+ /*
+ * This test is based on Advantech's "advdaq" driver source
+ * (which declares its module licence as "GPL" although the
+ * driver source does not include a "COPYING" file).
+ */
+ unsigned long reg = pci_resource_start(pcidev, 2) + 53;
+
+ outb(0x05, reg);
+ if ((inb(reg) & 0x07) == 0x02) {
+ outb(0x02, reg);
+ if ((inb(reg) & 0x07) == 0x05)
+ cardtype = TYPE_PCI1753E;
+ }
+ pci_release_region(pcidev, 2);
+ }
+ pci_disable_device(pcidev);
+ return cardtype;
+}
+
static int adv_pci_dio_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
@@ -1094,7 +496,6 @@ static const struct pci_device_id adv_pci_dio_pci_table[] = {
{ PCI_VDEVICE(ADVANTECH, 0x1753), TYPE_PCI1753 },
{ PCI_VDEVICE(ADVANTECH, 0x1754), TYPE_PCI1754 },
{ PCI_VDEVICE(ADVANTECH, 0x1756), TYPE_PCI1756 },
- { PCI_VDEVICE(ADVANTECH, 0x1760), TYPE_PCI1760 },
{ PCI_VDEVICE(ADVANTECH, 0x1762), TYPE_PCI1762 },
{ 0 }
};
@@ -1109,5 +510,5 @@ static struct pci_driver adv_pci_dio_pci_driver = {
module_comedi_pci_driver(adv_pci_dio_driver, adv_pci_dio_pci_driver);
MODULE_AUTHOR("Comedi http://www.comedi.org");
-MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_DESCRIPTION("Comedi driver for Advantech Digital I/O Cards");
MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/amplc_pci224.c b/drivers/staging/comedi/drivers/amplc_pci224.c
index b2f7679a0116..cac011fdd375 100644
--- a/drivers/staging/comedi/drivers/amplc_pci224.c
+++ b/drivers/staging/comedi/drivers/amplc_pci224.c
@@ -1022,14 +1022,17 @@ pci224_auto_attach(struct comedi_device *dev, unsigned long context_model)
irq = pci_dev->irq;
/* Allocate buffer to hold values for AO channel scan. */
- devpriv->ao_scan_vals = kmalloc(sizeof(devpriv->ao_scan_vals[0]) *
- board->ao_chans, GFP_KERNEL);
+ devpriv->ao_scan_vals = kmalloc_array(board->ao_chans,
+ sizeof(devpriv->ao_scan_vals[0]),
+ GFP_KERNEL);
if (!devpriv->ao_scan_vals)
return -ENOMEM;
/* Allocate buffer to hold AO channel scan order. */
- devpriv->ao_scan_order = kmalloc(sizeof(devpriv->ao_scan_order[0]) *
- board->ao_chans, GFP_KERNEL);
+ devpriv->ao_scan_order =
+ kmalloc_array(board->ao_chans,
+ sizeof(devpriv->ao_scan_order[0]),
+ GFP_KERNEL);
if (!devpriv->ao_scan_order)
return -ENOMEM;
diff --git a/drivers/staging/comedi/drivers/cb_pcidda.c b/drivers/staging/comedi/drivers/cb_pcidda.c
index b00a36a5cb36..ccb37d1f0f8e 100644
--- a/drivers/staging/comedi/drivers/cb_pcidda.c
+++ b/drivers/staging/comedi/drivers/cb_pcidda.c
@@ -51,13 +51,13 @@
/* DAC registers */
#define CB_DDA_DA_CTRL_REG 0x00 /* D/A Control Register */
-#define CB_DDA_DA_CTRL_SU (1 << 0) /* Simultaneous update */
-#define CB_DDA_DA_CTRL_EN (1 << 1) /* Enable specified DAC */
+#define CB_DDA_DA_CTRL_SU BIT(0) /* Simultaneous update */
+#define CB_DDA_DA_CTRL_EN BIT(1) /* Enable specified DAC */
#define CB_DDA_DA_CTRL_DAC(x) ((x) << 2) /* Specify DAC channel */
#define CB_DDA_DA_CTRL_RANGE2V5 (0 << 6) /* 2.5V range */
#define CB_DDA_DA_CTRL_RANGE5V (2 << 6) /* 5V range */
#define CB_DDA_DA_CTRL_RANGE10V (3 << 6) /* 10V range */
-#define CB_DDA_DA_CTRL_UNIP (1 << 8) /* Unipolar range */
+#define CB_DDA_DA_CTRL_UNIP BIT(8) /* Unipolar range */
#define DACALIBRATION1 4 /* D/A CALIBRATION REGISTER 1 */
/* write bits */
diff --git a/drivers/staging/comedi/drivers/comedi_parport.c b/drivers/staging/comedi/drivers/comedi_parport.c
index 15a4093efda1..1bf8ddc6f07c 100644
--- a/drivers/staging/comedi/drivers/comedi_parport.c
+++ b/drivers/staging/comedi/drivers/comedi_parport.c
@@ -75,8 +75,8 @@
#define PARPORT_DATA_REG 0x00
#define PARPORT_STATUS_REG 0x01
#define PARPORT_CTRL_REG 0x02
-#define PARPORT_CTRL_IRQ_ENA (1 << 4)
-#define PARPORT_CTRL_BIDIR_ENA (1 << 5)
+#define PARPORT_CTRL_IRQ_ENA BIT(4)
+#define PARPORT_CTRL_BIDIR_ENA BIT(5)
static int parport_data_reg_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
diff --git a/drivers/staging/comedi/drivers/das16.c b/drivers/staging/comedi/drivers/das16.c
index 056bca9c67d5..fd8e0b76f764 100644
--- a/drivers/staging/comedi/drivers/das16.c
+++ b/drivers/staging/comedi/drivers/das16.c
@@ -801,9 +801,10 @@ static void das16_ai_munge(struct comedi_device *dev,
unsigned short *data = array;
unsigned int num_samples = comedi_bytes_to_samples(s, num_bytes);
unsigned int i;
+ __le16 *buf = array;
for (i = 0; i < num_samples; i++) {
- data[i] = le16_to_cpu(data[i]);
+ data[i] = le16_to_cpu(buf[i]);
if (s->maxdata == 0x0fff)
data[i] >>= 4;
data[i] &= s->maxdata;
diff --git a/drivers/staging/comedi/drivers/ni_6527.c b/drivers/staging/comedi/drivers/ni_6527.c
index 62a817e4cd64..84c62e256094 100644
--- a/drivers/staging/comedi/drivers/ni_6527.c
+++ b/drivers/staging/comedi/drivers/ni_6527.c
@@ -42,24 +42,24 @@
#define NI6527_DO_REG(x) (0x03 + (x))
#define NI6527_ID_REG 0x06
#define NI6527_CLR_REG 0x07
-#define NI6527_CLR_EDGE (1 << 3)
-#define NI6527_CLR_OVERFLOW (1 << 2)
-#define NI6527_CLR_FILT (1 << 1)
-#define NI6527_CLR_INTERVAL (1 << 0)
+#define NI6527_CLR_EDGE BIT(3)
+#define NI6527_CLR_OVERFLOW BIT(2)
+#define NI6527_CLR_FILT BIT(1)
+#define NI6527_CLR_INTERVAL BIT(0)
#define NI6527_CLR_IRQS (NI6527_CLR_EDGE | NI6527_CLR_OVERFLOW)
#define NI6527_CLR_RESET_FILT (NI6527_CLR_FILT | NI6527_CLR_INTERVAL)
#define NI6527_FILT_INTERVAL_REG(x) (0x08 + (x))
#define NI6527_FILT_ENA_REG(x) (0x0c + (x))
#define NI6527_STATUS_REG 0x14
-#define NI6527_STATUS_IRQ (1 << 2)
-#define NI6527_STATUS_OVERFLOW (1 << 1)
-#define NI6527_STATUS_EDGE (1 << 0)
+#define NI6527_STATUS_IRQ BIT(2)
+#define NI6527_STATUS_OVERFLOW BIT(1)
+#define NI6527_STATUS_EDGE BIT(0)
#define NI6527_CTRL_REG 0x15
-#define NI6527_CTRL_FALLING (1 << 4)
-#define NI6527_CTRL_RISING (1 << 3)
-#define NI6527_CTRL_IRQ (1 << 2)
-#define NI6527_CTRL_OVERFLOW (1 << 1)
-#define NI6527_CTRL_EDGE (1 << 0)
+#define NI6527_CTRL_FALLING BIT(4)
+#define NI6527_CTRL_RISING BIT(3)
+#define NI6527_CTRL_IRQ BIT(2)
+#define NI6527_CTRL_OVERFLOW BIT(1)
+#define NI6527_CTRL_EDGE BIT(0)
#define NI6527_CTRL_DISABLE_IRQS 0
#define NI6527_CTRL_ENABLE_IRQS (NI6527_CTRL_FALLING | \
NI6527_CTRL_RISING | \
diff --git a/drivers/staging/comedi/drivers/ni_65xx.c b/drivers/staging/comedi/drivers/ni_65xx.c
index 800d57426070..251117be1205 100644
--- a/drivers/staging/comedi/drivers/ni_65xx.c
+++ b/drivers/staging/comedi/drivers/ni_65xx.c
@@ -68,25 +68,25 @@
/* Non-recurring Registers (8-bit except where noted) */
#define NI_65XX_ID_REG 0x00
#define NI_65XX_CLR_REG 0x01
-#define NI_65XX_CLR_WDOG_INT (1 << 6)
-#define NI_65XX_CLR_WDOG_PING (1 << 5)
-#define NI_65XX_CLR_WDOG_EXP (1 << 4)
-#define NI_65XX_CLR_EDGE_INT (1 << 3)
-#define NI_65XX_CLR_OVERFLOW_INT (1 << 2)
+#define NI_65XX_CLR_WDOG_INT BIT(6)
+#define NI_65XX_CLR_WDOG_PING BIT(5)
+#define NI_65XX_CLR_WDOG_EXP BIT(4)
+#define NI_65XX_CLR_EDGE_INT BIT(3)
+#define NI_65XX_CLR_OVERFLOW_INT BIT(2)
#define NI_65XX_STATUS_REG 0x02
-#define NI_65XX_STATUS_WDOG_INT (1 << 5)
-#define NI_65XX_STATUS_FALL_EDGE (1 << 4)
-#define NI_65XX_STATUS_RISE_EDGE (1 << 3)
-#define NI_65XX_STATUS_INT (1 << 2)
-#define NI_65XX_STATUS_OVERFLOW_INT (1 << 1)
-#define NI_65XX_STATUS_EDGE_INT (1 << 0)
+#define NI_65XX_STATUS_WDOG_INT BIT(5)
+#define NI_65XX_STATUS_FALL_EDGE BIT(4)
+#define NI_65XX_STATUS_RISE_EDGE BIT(3)
+#define NI_65XX_STATUS_INT BIT(2)
+#define NI_65XX_STATUS_OVERFLOW_INT BIT(1)
+#define NI_65XX_STATUS_EDGE_INT BIT(0)
#define NI_65XX_CTRL_REG 0x03
-#define NI_65XX_CTRL_WDOG_ENA (1 << 5)
-#define NI_65XX_CTRL_FALL_EDGE_ENA (1 << 4)
-#define NI_65XX_CTRL_RISE_EDGE_ENA (1 << 3)
-#define NI_65XX_CTRL_INT_ENA (1 << 2)
-#define NI_65XX_CTRL_OVERFLOW_ENA (1 << 1)
-#define NI_65XX_CTRL_EDGE_ENA (1 << 0)
+#define NI_65XX_CTRL_WDOG_ENA BIT(5)
+#define NI_65XX_CTRL_FALL_EDGE_ENA BIT(4)
+#define NI_65XX_CTRL_RISE_EDGE_ENA BIT(3)
+#define NI_65XX_CTRL_INT_ENA BIT(2)
+#define NI_65XX_CTRL_OVERFLOW_ENA BIT(1)
+#define NI_65XX_CTRL_EDGE_ENA BIT(0)
#define NI_65XX_REV_REG 0x04 /* 32-bit */
#define NI_65XX_FILTER_REG 0x08 /* 32-bit */
#define NI_65XX_RTSI_ROUTE_REG 0x0c /* 16-bit */
@@ -94,24 +94,24 @@
#define NI_65XX_RTSI_WDOG_REG 0x10 /* 16-bit */
#define NI_65XX_RTSI_TRIG_REG 0x12 /* 16-bit */
#define NI_65XX_AUTO_CLK_SEL_REG 0x14 /* PXI-6528 only */
-#define NI_65XX_AUTO_CLK_SEL_STATUS (1 << 1)
-#define NI_65XX_AUTO_CLK_SEL_DISABLE (1 << 0)
+#define NI_65XX_AUTO_CLK_SEL_STATUS BIT(1)
+#define NI_65XX_AUTO_CLK_SEL_DISABLE BIT(0)
#define NI_65XX_WDOG_CTRL_REG 0x15
-#define NI_65XX_WDOG_CTRL_ENA (1 << 0)
+#define NI_65XX_WDOG_CTRL_ENA BIT(0)
#define NI_65XX_RTSI_CFG_REG 0x16
-#define NI_65XX_RTSI_CFG_RISE_SENSE (1 << 2)
-#define NI_65XX_RTSI_CFG_FALL_SENSE (1 << 1)
-#define NI_65XX_RTSI_CFG_SYNC_DETECT (1 << 0)
+#define NI_65XX_RTSI_CFG_RISE_SENSE BIT(2)
+#define NI_65XX_RTSI_CFG_FALL_SENSE BIT(1)
+#define NI_65XX_RTSI_CFG_SYNC_DETECT BIT(0)
#define NI_65XX_WDOG_STATUS_REG 0x17
-#define NI_65XX_WDOG_STATUS_EXP (1 << 0)
+#define NI_65XX_WDOG_STATUS_EXP BIT(0)
#define NI_65XX_WDOG_INTERVAL_REG 0x18 /* 32-bit */
/* Recurring port registers (8-bit) */
#define NI_65XX_PORT(x) ((x) * 0x10)
#define NI_65XX_IO_DATA_REG(x) (0x40 + NI_65XX_PORT(x))
#define NI_65XX_IO_SEL_REG(x) (0x41 + NI_65XX_PORT(x))
-#define NI_65XX_IO_SEL_OUTPUT (0 << 0)
-#define NI_65XX_IO_SEL_INPUT (1 << 0)
+#define NI_65XX_IO_SEL_OUTPUT 0
+#define NI_65XX_IO_SEL_INPUT BIT(0)
#define NI_65XX_RISE_EDGE_ENA_REG(x) (0x42 + NI_65XX_PORT(x))
#define NI_65XX_FALL_EDGE_ENA_REG(x) (0x43 + NI_65XX_PORT(x))
#define NI_65XX_FILTER_ENA(x) (0x44 + NI_65XX_PORT(x))
@@ -613,7 +613,7 @@ static int ni_65xx_intr_insn_config(struct comedi_device *dev,
/* ripped from mite.h and mite_setup2() to avoid mite dependency */
#define MITE_IODWBSR 0xc0 /* IO Device Window Base Size Register */
-#define WENAB (1 << 7) /* window enable */
+#define WENAB BIT(7) /* window enable */
static int ni_65xx_mite_init(struct pci_dev *pcidev)
{
diff --git a/drivers/staging/comedi/drivers/ni_670x.c b/drivers/staging/comedi/drivers/ni_670x.c
index f4c580f65a89..3e72718801a9 100644
--- a/drivers/staging/comedi/drivers/ni_670x.c
+++ b/drivers/staging/comedi/drivers/ni_670x.c
@@ -214,8 +214,9 @@ static int ni_670x_auto_attach(struct comedi_device *dev,
if (s->n_chan == 32) {
const struct comedi_lrange **range_table_list;
- range_table_list = kmalloc(sizeof(struct comedi_lrange *) * 32,
- GFP_KERNEL);
+ range_table_list = kmalloc_array(32,
+ sizeof(struct comedi_lrange *),
+ GFP_KERNEL);
if (!range_table_list)
return -ENOMEM;
s->range_table_list = range_table_list;
diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c
index 6cc304a4c59b..5e8130a7d670 100644
--- a/drivers/staging/comedi/drivers/ni_mio_common.c
+++ b/drivers/staging/comedi/drivers/ni_mio_common.c
@@ -579,48 +579,54 @@ static inline unsigned ni_stc_dma_channel_select_bitfield(unsigned channel)
return 0;
}
-/* negative channel means no channel */
-static inline void ni_set_ai_dma_channel(struct comedi_device *dev, int channel)
+static inline void ni_set_ai_dma_channel(struct comedi_device *dev,
+ unsigned channel)
{
- unsigned bits = 0;
-
- if (channel >= 0)
- bits = ni_stc_dma_channel_select_bitfield(channel);
+ unsigned bits = ni_stc_dma_channel_select_bitfield(channel);
ni_set_bitfield(dev, NI_E_DMA_AI_AO_SEL_REG,
NI_E_DMA_AI_SEL_MASK, NI_E_DMA_AI_SEL(bits));
}
-/* negative channel means no channel */
-static inline void ni_set_ao_dma_channel(struct comedi_device *dev, int channel)
+static inline void ni_set_ai_dma_no_channel(struct comedi_device *dev)
{
- unsigned bits = 0;
+ ni_set_bitfield(dev, NI_E_DMA_AI_AO_SEL_REG, NI_E_DMA_AI_SEL_MASK, 0);
+}
- if (channel >= 0)
- bits = ni_stc_dma_channel_select_bitfield(channel);
+static inline void ni_set_ao_dma_channel(struct comedi_device *dev,
+ unsigned channel)
+{
+ unsigned bits = ni_stc_dma_channel_select_bitfield(channel);
ni_set_bitfield(dev, NI_E_DMA_AI_AO_SEL_REG,
NI_E_DMA_AO_SEL_MASK, NI_E_DMA_AO_SEL(bits));
}
-/* negative channel means no channel */
+static inline void ni_set_ao_dma_no_channel(struct comedi_device *dev)
+{
+ ni_set_bitfield(dev, NI_E_DMA_AI_AO_SEL_REG, NI_E_DMA_AO_SEL_MASK, 0);
+}
+
static inline void ni_set_gpct_dma_channel(struct comedi_device *dev,
unsigned gpct_index,
- int channel)
+ unsigned channel)
{
- unsigned bits = 0;
-
- if (channel >= 0)
- bits = ni_stc_dma_channel_select_bitfield(channel);
+ unsigned bits = ni_stc_dma_channel_select_bitfield(channel);
ni_set_bitfield(dev, NI_E_DMA_G0_G1_SEL_REG,
NI_E_DMA_G0_G1_SEL_MASK(gpct_index),
NI_E_DMA_G0_G1_SEL(gpct_index, bits));
}
-/* negative mite_channel means no channel */
+static inline void ni_set_gpct_dma_no_channel(struct comedi_device *dev,
+ unsigned gpct_index)
+{
+ ni_set_bitfield(dev, NI_E_DMA_G0_G1_SEL_REG,
+ NI_E_DMA_G0_G1_SEL_MASK(gpct_index), 0);
+}
+
static inline void ni_set_cdo_dma_channel(struct comedi_device *dev,
- int mite_channel)
+ unsigned mite_channel)
{
struct ni_private *devpriv = dev->private;
unsigned long flags;
@@ -628,16 +634,26 @@ static inline void ni_set_cdo_dma_channel(struct comedi_device *dev,
spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags);
devpriv->cdio_dma_select_reg &= ~NI_M_CDIO_DMA_SEL_CDO_MASK;
- if (mite_channel >= 0) {
- /*
- * XXX just guessing ni_stc_dma_channel_select_bitfield()
- * returns the right bits, under the assumption the cdio dma
- * selection works just like ai/ao/gpct.
- * Definitely works for dma channels 0 and 1.
- */
- bits = ni_stc_dma_channel_select_bitfield(mite_channel);
- devpriv->cdio_dma_select_reg |= NI_M_CDIO_DMA_SEL_CDO(bits);
- }
+ /*
+ * XXX just guessing ni_stc_dma_channel_select_bitfield()
+ * returns the right bits, under the assumption the cdio dma
+ * selection works just like ai/ao/gpct.
+ * Definitely works for dma channels 0 and 1.
+ */
+ bits = ni_stc_dma_channel_select_bitfield(mite_channel);
+ devpriv->cdio_dma_select_reg |= NI_M_CDIO_DMA_SEL_CDO(bits);
+ ni_writeb(dev, devpriv->cdio_dma_select_reg, NI_M_CDIO_DMA_SEL_REG);
+ mmiowb();
+ spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags);
+}
+
+static inline void ni_set_cdo_dma_no_channel(struct comedi_device *dev)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags);
+ devpriv->cdio_dma_select_reg &= ~NI_M_CDIO_DMA_SEL_CDO_MASK;
ni_writeb(dev, devpriv->cdio_dma_select_reg, NI_M_CDIO_DMA_SEL_REG);
mmiowb();
spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags);
@@ -745,7 +761,7 @@ static void ni_release_ai_mite_channel(struct comedi_device *dev)
spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
if (devpriv->ai_mite_chan) {
- ni_set_ai_dma_channel(dev, -1);
+ ni_set_ai_dma_no_channel(dev);
mite_release_channel(devpriv->ai_mite_chan);
devpriv->ai_mite_chan = NULL;
}
@@ -761,7 +777,7 @@ static void ni_release_ao_mite_channel(struct comedi_device *dev)
spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
if (devpriv->ao_mite_chan) {
- ni_set_ao_dma_channel(dev, -1);
+ ni_set_ao_dma_no_channel(dev);
mite_release_channel(devpriv->ao_mite_chan);
devpriv->ao_mite_chan = NULL;
}
@@ -781,7 +797,7 @@ static void ni_release_gpct_mite_channel(struct comedi_device *dev,
struct mite_channel *mite_chan =
devpriv->counter_dev->counters[gpct_index].mite_chan;
- ni_set_gpct_dma_channel(dev, gpct_index, -1);
+ ni_set_gpct_dma_no_channel(dev, gpct_index);
ni_tio_set_mite_channel(&devpriv->
counter_dev->counters[gpct_index],
NULL);
@@ -799,7 +815,7 @@ static void ni_release_cdo_mite_channel(struct comedi_device *dev)
spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
if (devpriv->cdo_mite_chan) {
- ni_set_cdo_dma_channel(dev, -1);
+ ni_set_cdo_dma_no_channel(dev);
mite_release_channel(devpriv->cdo_mite_chan);
devpriv->cdo_mite_chan = NULL;
}
@@ -1516,13 +1532,17 @@ static void ni_ai_munge(struct comedi_device *dev, struct comedi_subdevice *s,
unsigned short *array = data;
unsigned int *larray = data;
unsigned int i;
+#ifdef PCIDMA
+ __le16 *barray = data;
+ __le32 *blarray = data;
+#endif
for (i = 0; i < nsamples; i++) {
#ifdef PCIDMA
if (s->subdev_flags & SDF_LSAMPL)
- larray[i] = le32_to_cpu(larray[i]);
+ larray[i] = le32_to_cpu(blarray[i]);
else
- array[i] = le16_to_cpu(array[i]);
+ array[i] = le16_to_cpu(barray[i]);
#endif
if (s->subdev_flags & SDF_LSAMPL)
larray[i] += devpriv->ai_offset[chan_index];
@@ -2574,6 +2594,9 @@ static void ni_ao_munge(struct comedi_device *dev, struct comedi_subdevice *s,
unsigned int nsamples = comedi_bytes_to_samples(s, num_bytes);
unsigned short *array = data;
unsigned int i;
+#ifdef PCIDMA
+ __le16 buf, *barray = data;
+#endif
for (i = 0; i < nsamples; i++) {
unsigned int range = CR_RANGE(cmd->chanlist[chan_index]);
@@ -2586,10 +2609,11 @@ static void ni_ao_munge(struct comedi_device *dev, struct comedi_subdevice *s,
if (comedi_range_is_bipolar(s, range))
val = comedi_offset_munge(s, val);
#ifdef PCIDMA
- val = cpu_to_le16(val);
-#endif
+ buf = cpu_to_le16(val);
+ barray[i] = buf;
+#else
array[i] = val;
-
+#endif
chan_index++;
chan_index %= cmd->chanlist_len;
}
diff --git a/drivers/staging/comedi/drivers/plx9080.h b/drivers/staging/comedi/drivers/plx9080.h
index 25706531b885..f5cd6d5004bd 100644
--- a/drivers/staging/comedi/drivers/plx9080.h
+++ b/drivers/staging/comedi/drivers/plx9080.h
@@ -1,4 +1,5 @@
-/* plx9080.h
+/*
+ * plx9080.h
*
* Copyright (C) 2002,2003 Frank Mori Hess <fmhess@users.sourceforge.net>
*
@@ -33,8 +34,10 @@ struct plx_dma_desc {
__le32 local_start_addr;
/* transfer_size is in bytes, only first 23 bits of register are used */
__le32 transfer_size;
- /* address of next descriptor (quad word aligned), plus some
- * additional bits (see PLX_DMA0_DESCRIPTOR_REG) */
+ /*
+ * address of next descriptor (quad word aligned), plus some
+ * additional bits (see PLX_DMA0_DESCRIPTOR_REG)
+ */
__le32 next;
};
@@ -46,23 +49,31 @@ struct plx_dma_desc {
**
**********************************************************************/
-#define PLX_LAS0RNG_REG 0x0000 /* L, Local Addr Space 0 Range Register */
-#define PLX_LAS1RNG_REG 0x00f0 /* L, Local Addr Space 1 Range Register */
+/* L, Local Addr Space 0 Range Register */
+#define PLX_LAS0RNG_REG 0x0000
+/* L, Local Addr Space 1 Range Register */
+#define PLX_LAS1RNG_REG 0x00f0
#define LRNG_IO 0x00000001 /* Map to: 1=I/O, 0=Mem */
#define LRNG_ANY32 0x00000000 /* Locate anywhere in 32 bit */
#define LRNG_LT1MB 0x00000002 /* Locate in 1st meg */
#define LRNG_ANY64 0x00000004 /* Locate anywhere in 64 bit */
-#define LRNG_MEM_MASK 0xfffffff0 /* bits that specify range for memory io */
-#define LRNG_IO_MASK 0xfffffffa /* bits that specify range for normal io */
-
-#define PLX_LAS0MAP_REG 0x0004 /* L, Local Addr Space 0 Remap Register */
-#define PLX_LAS1MAP_REG 0x00f4 /* L, Local Addr Space 1 Remap Register */
+/* bits that specify range for memory io */
+#define LRNG_MEM_MASK 0xfffffff0
+/* bits that specify range for normal io */
+#define LRNG_IO_MASK 0xfffffffa
+/* L, Local Addr Space 0 Remap Register */
+#define PLX_LAS0MAP_REG 0x0004
+/* L, Local Addr Space 1 Remap Register */
+#define PLX_LAS1MAP_REG 0x00f4
#define LMAP_EN 0x00000001 /* Enable slave decode */
-#define LMAP_MEM_MASK 0xfffffff0 /* bits that specify decode for memory io */
-#define LMAP_IO_MASK 0xfffffffa /* bits that specify decode bits for normal io */
+/* bits that specify decode for memory io */
+#define LMAP_MEM_MASK 0xfffffff0
+/* bits that specify decode bits for normal io */
+#define LMAP_IO_MASK 0xfffffffa
-/* Mode/Arbitration Register.
-*/
+/*
+ * Mode/Arbitration Register.
+ */
#define PLX_MARB_REG 0x8 /* L, Local Arbitration Register */
#define PLX_DMAARB_REG 0xac
enum marb_bits {
@@ -72,35 +83,45 @@ enum marb_bits {
MARB_LPEN = 0x00020000, /* Pause Timer Enable */
MARB_BREQ = 0x00040000, /* Local Bus BREQ Enable */
MARB_DMA_PRIORITY_MASK = 0x00180000,
- MARB_LBDS_GIVE_UP_BUS_MODE = 0x00200000, /* local bus direct slave give up bus mode */
- MARB_DS_LLOCK_ENABLE = 0x00400000, /* direct slave LLOCKo# enable */
+ /* local bus direct slave give up bus mode */
+ MARB_LBDS_GIVE_UP_BUS_MODE = 0x00200000,
+ /* direct slave LLOCKo# enable */
+ MARB_DS_LLOCK_ENABLE = 0x00400000,
MARB_PCI_REQUEST_MODE = 0x00800000,
MARB_PCIv21_MODE = 0x01000000, /* pci specification v2.1 mode */
MARB_PCI_READ_NO_WRITE_MODE = 0x02000000,
MARB_PCI_READ_WITH_WRITE_FLUSH_MODE = 0x04000000,
- MARB_GATE_TIMER_WITH_BREQ = 0x08000000, /* gate local bus latency timer with BREQ */
+ /* gate local bus latency timer with BREQ */
+ MARB_GATE_TIMER_WITH_BREQ = 0x08000000,
MARB_PCI_READ_NO_FLUSH_MODE = 0x10000000,
MARB_USE_SUBSYSTEM_IDS = 0x20000000,
};
#define PLX_BIGEND_REG 0xc
enum bigend_bits {
- BIGEND_CONFIG = 0x1, /* use big endian ordering for configuration register accesses */
+ /* use big endian ordering for configuration register accesses */
+ BIGEND_CONFIG = 0x1,
BIGEND_DIRECT_MASTER = 0x2,
BIGEND_DIRECT_SLAVE_LOCAL0 = 0x4,
BIGEND_ROM = 0x8,
- BIGEND_BYTE_LANE = 0x10, /* use byte lane consisting of most significant bits instead of least significant */
+ /*
+ * use byte lane consisting of most significant bits instead of
+ * least significant
+ */
+ BIGEND_BYTE_LANE = 0x10,
BIGEND_DIRECT_SLAVE_LOCAL1 = 0x20,
BIGEND_DMA1 = 0x40,
BIGEND_DMA0 = 0x80,
};
-/* Note: The Expansion ROM stuff is only relevant to the PC environment.
+/*
+** Note: The Expansion ROM stuff is only relevant to the PC environment.
** This expansion ROM code is executed by the host CPU at boot time.
** For this reason no bit definitions are provided here.
-*/
+ */
#define PLX_ROMRNG_REG 0x0010 /* L, Expn ROM Space Range Register */
-#define PLX_ROMMAP_REG 0x0014 /* L, Local Addr Space Range Register */
+/* L, Local Addr Space Range Register */
+#define PLX_ROMMAP_REG 0x0014
#define PLX_REGION0_REG 0x0018 /* L, Local Bus Region 0 Descriptor */
#define RGN_WIDTH 0x00000002 /* Local bus width bits */
@@ -190,7 +211,8 @@ enum bigend_bits {
#define ICS_TA_DMA0 0x02000000 /* Target Abort - DMA #0 */
#define ICS_TA_DMA1 0x04000000 /* Target Abort - DMA #1 */
#define ICS_TA_RA 0x08000000 /* Target Abort - Retry Timeout */
-#define ICS_MBIA(x) (0x10000000 << ((x) & 0x3)) /* mailbox x is active */
+/* mailbox x is active */
+#define ICS_MBIA(x) (0x10000000 << ((x) & 0x3))
#define PLX_CONTROL_REG 0x006C /* L, EEPROM Cntl & PCI Cmd Codes */
#define CTL_RDMA 0x0000000E /* DMA Read Command */
@@ -221,28 +243,38 @@ enum bigend_bits {
#define PLX_EN_BTERM_BIT 0x80 /* enable BTERM# input */
#define PLX_DMA_LOCAL_BURST_EN_BIT 0x100 /* enable local burst mode */
#define PLX_EN_CHAIN_BIT 0x200 /* enables chaining */
-#define PLX_EN_DMA_DONE_INTR_BIT 0x400 /* enables interrupt on dma done */
-#define PLX_LOCAL_ADDR_CONST_BIT 0x800 /* hold local address constant (don't increment) */
-#define PLX_DEMAND_MODE_BIT 0x1000 /* enables demand-mode for dma transfer */
+/* enables interrupt on dma done */
+#define PLX_EN_DMA_DONE_INTR_BIT 0x400
+/* hold local address constant (don't increment) */
+#define PLX_LOCAL_ADDR_CONST_BIT 0x800
+/* enables demand-mode for dma transfer */
+#define PLX_DEMAND_MODE_BIT 0x1000
#define PLX_EOT_ENABLE_BIT 0x4000
#define PLX_STOP_MODE_BIT 0x8000
-#define PLX_DMA_INTR_PCI_BIT 0x20000 /* routes dma interrupt to pci bus (instead of local bus) */
+/* routes dma interrupt to pci bus (instead of local bus) */
+#define PLX_DMA_INTR_PCI_BIT 0x20000
-#define PLX_DMA0_PCI_ADDRESS_REG 0x84 /* pci address that dma transfers start at */
+/* pci address that dma transfers start at */
+#define PLX_DMA0_PCI_ADDRESS_REG 0x84
#define PLX_DMA1_PCI_ADDRESS_REG 0x98
-#define PLX_DMA0_LOCAL_ADDRESS_REG 0x88 /* local address that dma transfers start at */
+/* local address that dma transfers start at */
+#define PLX_DMA0_LOCAL_ADDRESS_REG 0x88
#define PLX_DMA1_LOCAL_ADDRESS_REG 0x9c
-#define PLX_DMA0_TRANSFER_SIZE_REG 0x8c /* number of bytes to transfer (first 23 bits) */
+/* number of bytes to transfer (first 23 bits) */
+#define PLX_DMA0_TRANSFER_SIZE_REG 0x8c
#define PLX_DMA1_TRANSFER_SIZE_REG 0xa0
#define PLX_DMA0_DESCRIPTOR_REG 0x90 /* descriptor pointer register */
#define PLX_DMA1_DESCRIPTOR_REG 0xa4
-#define PLX_DESC_IN_PCI_BIT 0x1 /* descriptor is located in pci space (not local space) */
+/* descriptor is located in pci space (not local space) */
+#define PLX_DESC_IN_PCI_BIT 0x1
#define PLX_END_OF_CHAIN_BIT 0x2 /* end of chain bit */
-#define PLX_INTR_TERM_COUNT 0x4 /* interrupt when this descriptor's transfer is finished */
-#define PLX_XFER_LOCAL_TO_PCI 0x8 /* transfer from local to pci bus (not pci to local) */
+/* interrupt when this descriptor's transfer is finished */
+#define PLX_INTR_TERM_COUNT 0x4
+/* transfer from local to pci bus (not pci to local) */
+#define PLX_XFER_LOCAL_TO_PCI 0x8
#define PLX_DMA0_CS_REG 0xa8 /* command status register */
#define PLX_DMA1_CS_REG 0xa9
@@ -288,10 +320,11 @@ enum bigend_bits {
#define MBX_STS_PCIRESET 0x00000100 /* Host issued PCI reset request */
#define MBX_STS_BUSY 0x00000080 /* PUTS is in progress */
#define MBX_STS_ERROR 0x00000040 /* PUTS has failed */
-#define MBX_STS_RESERVED 0x000000c0 /* Undefined -> status in transition.
- We are in process of changing
- bits; we SET Error bit before
- RESET of Busy bit */
+/*
+ * Undefined -> status in transition. We are in process of changing bits;
+ * we SET Error bit before RESET of Busy bit
+ */
+#define MBX_STS_RESERVED 0x000000c0
#define MBX_RESERVED_5 0x00000020 /* FYI: reserved/unused bit */
#define MBX_RESERVED_4 0x00000010 /* FYI: reserved/unused bit */
@@ -320,12 +353,12 @@ enum bigend_bits {
#define MBX_CMD_BSWAP_0 0x8c000000 /* use scheme 0 */
#define MBX_CMD_BSWAP_1 0x8c000001 /* use scheme 1 */
-#define MBX_CMD_SETHMS 0x8d000000 /* setup host memory access window
- size */
-#define MBX_CMD_SETHBA 0x8e000000 /* setup host memory access base
- address */
-#define MBX_CMD_MGO 0x8f000000 /* perform memory setup and continue
- (IE. Done) */
+/* setup host memory access window size */
+#define MBX_CMD_SETHMS 0x8d000000
+/* setup host memory access base address */
+#define MBX_CMD_SETHBA 0x8e000000
+/* perform memory setup and continue (IE. Done) */
+#define MBX_CMD_MGO 0x8f000000
#define MBX_CMD_NOOP 0xFF000000 /* dummy, illegal command */
/*****************************************/
@@ -348,7 +381,8 @@ enum bigend_bits {
/***************************************/
#define MBX_BTYPE_MASK 0x0000ffff /* PUTS Board Type Register */
-#define MBX_BTYPE_FAMILY_MASK 0x0000ff00 /* PUTS Board Family Register */
+/* PUTS Board Family Register */
+#define MBX_BTYPE_FAMILY_MASK 0x0000ff00
#define MBX_BTYPE_SUBTYPE_MASK 0x000000ff /* PUTS Board Subtype */
#define MBX_BTYPE_PLX9060 0x00000100 /* PLX family type */
@@ -378,12 +412,12 @@ enum bigend_bits {
/* system allocates this many bytes for address mapping mailbox space */
#define MBX_ADDR_SPACE_360 0x80 /* wanXL100s/200/400 */
-#define MBX_ADDR_MASK_360 (MBX_ADDR_SPACE_360-1)
+#define MBX_ADDR_MASK_360 (MBX_ADDR_SPACE_360 - 1)
static inline int plx9080_abort_dma(void __iomem *iobase, unsigned int channel)
{
void __iomem *dma_cs_addr;
- uint8_t dma_status;
+ u8 dma_status;
const int timeout = 10000;
unsigned int i;
diff --git a/drivers/staging/comedi/drivers/s526.c b/drivers/staging/comedi/drivers/s526.c
index d70c97947627..c80527db9c19 100644
--- a/drivers/staging/comedi/drivers/s526.c
+++ b/drivers/staging/comedi/drivers/s526.c
@@ -37,7 +37,6 @@
#include <linux/module.h>
#include "../comedidev.h"
-#include <asm/byteorder.h>
/*
* Register I/O map
@@ -84,7 +83,92 @@
#define S526_GPCT_LSB_REG(x) (0x12 + ((x) * 8))
#define S526_GPCT_MSB_REG(x) (0x14 + ((x) * 8))
#define S526_GPCT_MODE_REG(x) (0x16 + ((x) * 8))
+#define S526_GPCT_MODE_COUT_SRC(x) ((x) << 0)
+#define S526_GPCT_MODE_COUT_SRC_MASK S526_GPCT_MODE_COUT_SRC(0x1)
+#define S526_GPCT_MODE_COUT_SRC_RCAP S526_GPCT_MODE_COUT_SRC(0)
+#define S526_GPCT_MODE_COUT_SRC_RTGL S526_GPCT_MODE_COUT_SRC(1)
+#define S526_GPCT_MODE_COUT_POL(x) ((x) << 1)
+#define S526_GPCT_MODE_COUT_POL_MASK S526_GPCT_MODE_COUT_POL(0x1)
+#define S526_GPCT_MODE_COUT_POL_NORM S526_GPCT_MODE_COUT_POL(0)
+#define S526_GPCT_MODE_COUT_POL_INV S526_GPCT_MODE_COUT_POL(1)
+#define S526_GPCT_MODE_AUTOLOAD(x) ((x) << 2)
+#define S526_GPCT_MODE_AUTOLOAD_MASK S526_GPCT_MODE_AUTOLOAD(0x7)
+#define S526_GPCT_MODE_AUTOLOAD_NONE S526_GPCT_MODE_AUTOLOAD(0)
+/* these 3 bits can be OR'ed */
+#define S526_GPCT_MODE_AUTOLOAD_RO S526_GPCT_MODE_AUTOLOAD(0x1)
+#define S526_GPCT_MODE_AUTOLOAD_IXFALL S526_GPCT_MODE_AUTOLOAD(0x2)
+#define S526_GPCT_MODE_AUTOLOAD_IXRISE S526_GPCT_MODE_AUTOLOAD(0x4)
+#define S526_GPCT_MODE_HWCTEN_SRC(x) ((x) << 5)
+#define S526_GPCT_MODE_HWCTEN_SRC_MASK S526_GPCT_MODE_HWCTEN_SRC(0x3)
+#define S526_GPCT_MODE_HWCTEN_SRC_CEN S526_GPCT_MODE_HWCTEN_SRC(0)
+#define S526_GPCT_MODE_HWCTEN_SRC_IX S526_GPCT_MODE_HWCTEN_SRC(1)
+#define S526_GPCT_MODE_HWCTEN_SRC_IXRF S526_GPCT_MODE_HWCTEN_SRC(2)
+#define S526_GPCT_MODE_HWCTEN_SRC_NRCAP S526_GPCT_MODE_HWCTEN_SRC(3)
+#define S526_GPCT_MODE_CTEN_CTRL(x) ((x) << 7)
+#define S526_GPCT_MODE_CTEN_CTRL_MASK S526_GPCT_MODE_CTEN_CTRL(0x3)
+#define S526_GPCT_MODE_CTEN_CTRL_DIS S526_GPCT_MODE_CTEN_CTRL(0)
+#define S526_GPCT_MODE_CTEN_CTRL_ENA S526_GPCT_MODE_CTEN_CTRL(1)
+#define S526_GPCT_MODE_CTEN_CTRL_HW S526_GPCT_MODE_CTEN_CTRL(2)
+#define S526_GPCT_MODE_CTEN_CTRL_INVHW S526_GPCT_MODE_CTEN_CTRL(3)
+#define S526_GPCT_MODE_CLK_SRC(x) ((x) << 9)
+#define S526_GPCT_MODE_CLK_SRC_MASK S526_GPCT_MODE_CLK_SRC(0x3)
+/* if count direction control set to quadrature */
+#define S526_GPCT_MODE_CLK_SRC_QUADX1 S526_GPCT_MODE_CLK_SRC(0)
+#define S526_GPCT_MODE_CLK_SRC_QUADX2 S526_GPCT_MODE_CLK_SRC(1)
+#define S526_GPCT_MODE_CLK_SRC_QUADX4 S526_GPCT_MODE_CLK_SRC(2)
+#define S526_GPCT_MODE_CLK_SRC_QUADX4_ S526_GPCT_MODE_CLK_SRC(3)
+/* if count direction control set to software control */
+#define S526_GPCT_MODE_CLK_SRC_ARISE S526_GPCT_MODE_CLK_SRC(0)
+#define S526_GPCT_MODE_CLK_SRC_AFALL S526_GPCT_MODE_CLK_SRC(1)
+#define S526_GPCT_MODE_CLK_SRC_INT S526_GPCT_MODE_CLK_SRC(2)
+#define S526_GPCT_MODE_CLK_SRC_INTHALF S526_GPCT_MODE_CLK_SRC(3)
+#define S526_GPCT_MODE_CT_DIR(x) ((x) << 11)
+#define S526_GPCT_MODE_CT_DIR_MASK S526_GPCT_MODE_CT_DIR(0x1)
+/* if count direction control set to software control */
+#define S526_GPCT_MODE_CT_DIR_UP S526_GPCT_MODE_CT_DIR(0)
+#define S526_GPCT_MODE_CT_DIR_DOWN S526_GPCT_MODE_CT_DIR(1)
+#define S526_GPCT_MODE_CTDIR_CTRL(x) ((x) << 12)
+#define S526_GPCT_MODE_CTDIR_CTRL_MASK S526_GPCT_MODE_CTDIR_CTRL(0x1)
+#define S526_GPCT_MODE_CTDIR_CTRL_QUAD S526_GPCT_MODE_CTDIR_CTRL(0)
+#define S526_GPCT_MODE_CTDIR_CTRL_SOFT S526_GPCT_MODE_CTDIR_CTRL(1)
+#define S526_GPCT_MODE_LATCH_CTRL(x) ((x) << 13)
+#define S526_GPCT_MODE_LATCH_CTRL_MASK S526_GPCT_MODE_LATCH_CTRL(0x1)
+#define S526_GPCT_MODE_LATCH_CTRL_READ S526_GPCT_MODE_LATCH_CTRL(0)
+#define S526_GPCT_MODE_LATCH_CTRL_EVENT S526_GPCT_MODE_LATCH_CTRL(1)
+#define S526_GPCT_MODE_PR_SELECT(x) ((x) << 14)
+#define S526_GPCT_MODE_PR_SELECT_MASK S526_GPCT_MODE_PR_SELECT(0x1)
+#define S526_GPCT_MODE_PR_SELECT_PR0 S526_GPCT_MODE_PR_SELECT(0)
+#define S526_GPCT_MODE_PR_SELECT_PR1 S526_GPCT_MODE_PR_SELECT(1)
+/* Control/Status - R = readable, W = writeable, C = write 1 to clear */
#define S526_GPCT_CTRL_REG(x) (0x18 + ((x) * 8))
+#define S526_GPCT_CTRL_EV_STATUS(x) ((x) << 0) /* RC */
+#define S526_GPCT_CTRL_EV_STATUS_MASK S526_GPCT_EV_STATUS(0xf)
+#define S526_GPCT_CTRL_EV_STATUS_NONE S526_GPCT_EV_STATUS(0)
+/* these 4 bits can be OR'ed */
+#define S526_GPCT_CTRL_EV_STATUS_ECAP S526_GPCT_EV_STATUS(0x1)
+#define S526_GPCT_CTRL_EV_STATUS_ICAPN S526_GPCT_EV_STATUS(0x2)
+#define S526_GPCT_CTRL_EV_STATUS_ICAPP S526_GPCT_EV_STATUS(0x4)
+#define S526_GPCT_CTRL_EV_STATUS_RCAP S526_GPCT_EV_STATUS(0x8)
+#define S526_GPCT_CTRL_COUT_STATUS BIT(4) /* R */
+#define S526_GPCT_CTRL_INDEX_STATUS BIT(5) /* R */
+#define S525_GPCT_CTRL_INTEN(x) ((x) << 6) /* W */
+#define S525_GPCT_CTRL_INTEN_MASK S526_GPCT_CTRL_INTEN(0xf)
+#define S525_GPCT_CTRL_INTEN_NONE S526_GPCT_CTRL_INTEN(0)
+/* these 4 bits can be OR'ed */
+#define S525_GPCT_CTRL_INTEN_ERROR S526_GPCT_CTRL_INTEN(0x1)
+#define S525_GPCT_CTRL_INTEN_IXFALL S526_GPCT_CTRL_INTEN(0x2)
+#define S525_GPCT_CTRL_INTEN_IXRISE S526_GPCT_CTRL_INTEN(0x4)
+#define S525_GPCT_CTRL_INTEN_RO S526_GPCT_CTRL_INTEN(0x8)
+#define S525_GPCT_CTRL_LATCH_SEL(x) ((x) << 10) /* W */
+#define S525_GPCT_CTRL_LATCH_SEL_MASK S526_GPCT_CTRL_LATCH_SEL(0x7)
+#define S525_GPCT_CTRL_LATCH_SEL_NONE S526_GPCT_CTRL_LATCH_SEL(0)
+/* these 3 bits can be OR'ed */
+#define S525_GPCT_CTRL_LATCH_SEL_IXFALL S526_GPCT_CTRL_LATCH_SEL(0x1)
+#define S525_GPCT_CTRL_LATCH_SEL_IXRISE S526_GPCT_CTRL_LATCH_SEL(0x2)
+#define S525_GPCT_CTRL_LATCH_SEL_ITIMER S526_GPCT_CTRL_LATCH_SEL(0x4)
+#define S525_GPCT_CTRL_CT_ARM BIT(13) /* W */
+#define S525_GPCT_CTRL_CT_LOAD BIT(14) /* W */
+#define S526_GPCT_CTRL_CT_RESET BIT(15) /* W */
#define S526_EEPROM_DATA_REG 0x32
#define S526_EEPROM_CTRL_REG 0x34
#define S526_EEPROM_CTRL_ADDR(x) (((x) & 0x3f) << 3)
@@ -92,41 +176,6 @@
#define S526_EEPROM_CTRL_READ S526_EEPROM_CTRL(2)
#define S526_EEPROM_CTRL_START BIT(0)
-struct counter_mode_register_t {
-#if defined(__LITTLE_ENDIAN_BITFIELD)
- unsigned short coutSource:1;
- unsigned short coutPolarity:1;
- unsigned short autoLoadResetRcap:3;
- unsigned short hwCtEnableSource:2;
- unsigned short ctEnableCtrl:2;
- unsigned short clockSource:2;
- unsigned short countDir:1;
- unsigned short countDirCtrl:1;
- unsigned short outputRegLatchCtrl:1;
- unsigned short preloadRegSel:1;
- unsigned short reserved:1;
- #elif defined(__BIG_ENDIAN_BITFIELD)
- unsigned short reserved:1;
- unsigned short preloadRegSel:1;
- unsigned short outputRegLatchCtrl:1;
- unsigned short countDirCtrl:1;
- unsigned short countDir:1;
- unsigned short clockSource:2;
- unsigned short ctEnableCtrl:2;
- unsigned short hwCtEnableSource:2;
- unsigned short autoLoadResetRcap:3;
- unsigned short coutPolarity:1;
- unsigned short coutSource:1;
-#else
-#error Unknown bit field order
-#endif
-};
-
-union cmReg {
- struct counter_mode_register_t reg;
- unsigned short value;
-};
-
struct s526_private {
unsigned int gpct_config[4];
unsigned short ai_ctrl;
@@ -174,7 +223,6 @@ static int s526_gpct_insn_config(struct comedi_device *dev,
struct s526_private *devpriv = dev->private;
unsigned int chan = CR_CHAN(insn->chanspec);
unsigned int val;
- union cmReg cmReg;
/*
* Check what type of Counter the user requested
@@ -192,28 +240,31 @@ static int s526_gpct_insn_config(struct comedi_device *dev,
#if 1
/* Set Counter Mode Register */
- cmReg.value = data[1] & 0xffff;
- outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
+ val = data[1] & 0xffff;
+ outw(val, dev->iobase + S526_GPCT_MODE_REG(chan));
/* Reset the counter if it is software preload */
- if (cmReg.reg.autoLoadResetRcap == 0) {
+ if ((val & S526_GPCT_MODE_AUTOLOAD_MASK) ==
+ S526_GPCT_MODE_AUTOLOAD_NONE) {
/* Reset the counter */
- outw(0x8000, dev->iobase + S526_GPCT_CTRL_REG(chan));
- /* Load the counter from PR0
- * outw(0x4000, dev->iobase + S526_GPCT_CTRL_REG(chan));
+ outw(S526_GPCT_CTRL_CT_RESET,
+ dev->iobase + S526_GPCT_CTRL_REG(chan));
+ /*
+ * Load the counter from PR0
+ * outw(S526_GPCT_CTRL_CT_LOAD,
+ * dev->iobase + S526_GPCT_CTRL_REG(chan));
*/
}
#else
- /* 0 quadrature, 1 software control */
- cmReg.reg.countDirCtrl = 0;
+ val = S526_GPCT_MODE_CTDIR_CTRL_QUAD;
/* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
if (data[1] == GPCT_X2)
- cmReg.reg.clockSource = 1;
+ val |= S526_GPCT_MODE_CLK_SRC_QUADX2;
else if (data[1] == GPCT_X4)
- cmReg.reg.clockSource = 2;
+ val |= S526_GPCT_MODE_CLK_SRC_QUADX4;
else
- cmReg.reg.clockSource = 0;
+ val |= S526_GPCT_MODE_CLK_SRC_QUADX1;
/* When to take into account the indexpulse: */
/*
@@ -224,13 +275,14 @@ static int s526_gpct_insn_config(struct comedi_device *dev,
* }
*/
/* Take into account the index pulse? */
- if (data[3] == GPCT_RESET_COUNTER_ON_INDEX)
+ if (data[3] == GPCT_RESET_COUNTER_ON_INDEX) {
/* Auto load with INDEX^ */
- cmReg.reg.autoLoadResetRcap = 4;
+ val |= S526_GPCT_MODE_AUTOLOAD_IXRISE;
+ }
/* Set Counter Mode Register */
- cmReg.value = data[1] & 0xffff;
- outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
+ val = data[1] & 0xffff;
+ outw(val, dev->iobase + S526_GPCT_MODE_REG(chan));
/* Load the pre-load register */
s526_gpct_write(dev, chan, data[2]);
@@ -241,11 +293,14 @@ static int s526_gpct_insn_config(struct comedi_device *dev,
dev->iobase + S526_GPCT_CTRL_REG(chan));
/* Reset the counter if it is software preload */
- if (cmReg.reg.autoLoadResetRcap == 0) {
+ if ((val & S526_GPCT_MODE_AUTOLOAD_MASK) ==
+ S526_GPCT_MODE_AUTOLOAD_NONE) {
/* Reset the counter */
- outw(0x8000, dev->iobase + S526_GPCT_CTRL_REG(chan));
+ outw(S526_GPCT_CTRL_CT_RESET,
+ dev->iobase + S526_GPCT_CTRL_REG(chan));
/* Load the counter from PR0 */
- outw(0x4000, dev->iobase + S526_GPCT_CTRL_REG(chan));
+ outw(S526_GPCT_CTRL_CT_LOAD,
+ dev->iobase + S526_GPCT_CTRL_REG(chan));
}
#endif
break;
@@ -261,17 +316,21 @@ static int s526_gpct_insn_config(struct comedi_device *dev,
devpriv->gpct_config[chan] = data[0];
/* Set Counter Mode Register */
- cmReg.value = data[1] & 0xffff;
- cmReg.reg.preloadRegSel = 0; /* PR0 */
- outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
+ val = data[1] & 0xffff;
+ /* Select PR0 */
+ val &= ~S526_GPCT_MODE_PR_SELECT_MASK;
+ val |= S526_GPCT_MODE_PR_SELECT_PR0;
+ outw(val, dev->iobase + S526_GPCT_MODE_REG(chan));
/* Load the pre-load register 0 */
s526_gpct_write(dev, chan, data[2]);
/* Set Counter Mode Register */
- cmReg.value = data[1] & 0xffff;
- cmReg.reg.preloadRegSel = 1; /* PR1 */
- outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
+ val = data[1] & 0xffff;
+ /* Select PR1 */
+ val &= ~S526_GPCT_MODE_PR_SELECT_MASK;
+ val |= S526_GPCT_MODE_PR_SELECT_PR1;
+ outw(val, dev->iobase + S526_GPCT_MODE_REG(chan));
/* Load the pre-load register 1 */
s526_gpct_write(dev, chan, data[3]);
@@ -294,17 +353,21 @@ static int s526_gpct_insn_config(struct comedi_device *dev,
devpriv->gpct_config[chan] = data[0];
/* Set Counter Mode Register */
- cmReg.value = data[1] & 0xffff;
- cmReg.reg.preloadRegSel = 0; /* PR0 */
- outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
+ val = data[1] & 0xffff;
+ /* Select PR0 */
+ val &= ~S526_GPCT_MODE_PR_SELECT_MASK;
+ val |= S526_GPCT_MODE_PR_SELECT_PR0;
+ outw(val, dev->iobase + S526_GPCT_MODE_REG(chan));
/* Load the pre-load register 0 */
s526_gpct_write(dev, chan, data[2]);
/* Set Counter Mode Register */
- cmReg.value = data[1] & 0xffff;
- cmReg.reg.preloadRegSel = 1; /* PR1 */
- outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
+ val = data[1] & 0xffff;
+ /* Select PR1 */
+ val &= ~S526_GPCT_MODE_PR_SELECT_MASK;
+ val |= S526_GPCT_MODE_PR_SELECT_PR1;
+ outw(val, dev->iobase + S526_GPCT_MODE_REG(chan));
/* Load the pre-load register 1 */
s526_gpct_write(dev, chan, data[3]);
diff --git a/drivers/staging/dgnc/dgnc_cls.c b/drivers/staging/dgnc/dgnc_cls.c
index 75040daa40ce..72f0aaa6911f 100644
--- a/drivers/staging/dgnc/dgnc_cls.c
+++ b/drivers/staging/dgnc/dgnc_cls.c
@@ -934,7 +934,7 @@ static void cls_flush_uart_write(struct channel_t *ch)
writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_XMIT),
&ch->ch_cls_uart->isr_fcr);
- udelay(10);
+ usleep_range(10, 20);
ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
}
diff --git a/drivers/staging/dgnc/dgnc_neo.c b/drivers/staging/dgnc/dgnc_neo.c
index 8106f5234bf5..39c76e78e56a 100644
--- a/drivers/staging/dgnc/dgnc_neo.c
+++ b/drivers/staging/dgnc/dgnc_neo.c
@@ -1108,9 +1108,9 @@ static void neo_copy_data_from_uart_to_queue(struct channel_t *ch)
* On the other hand, if the UART IS in FIFO mode, then ask
* the UART to give us an approximation of data it has RX'ed.
*/
- if (!(ch->ch_flags & CH_FIFO_ENABLED))
+ if (!(ch->ch_flags & CH_FIFO_ENABLED)) {
total = 0;
- else {
+ } else {
total = readb(&ch->ch_neo_uart->rfifo);
/*
@@ -1628,7 +1628,7 @@ static void neo_uart_init(struct channel_t *ch)
/* Clear out UART and FIFO */
readb(&ch->ch_neo_uart->txrx);
- writeb((UART_FCR_ENABLE_FIFO|UART_FCR_CLEAR_RCVR|UART_FCR_CLEAR_XMIT), &ch->ch_neo_uart->isr_fcr);
+ writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), &ch->ch_neo_uart->isr_fcr);
readb(&ch->ch_neo_uart->lsr);
readb(&ch->ch_neo_uart->msr);
@@ -1779,8 +1779,8 @@ static void neo_vpd(struct dgnc_board *brd)
/* Store the VPD into our buffer */
for (i = 0; i < NEO_VPD_IMAGESIZE; i++) {
a = neo_read_eeprom(brd->re_map_membase, i);
- brd->vpd[i*2] = a & 0xff;
- brd->vpd[(i*2)+1] = (a >> 8) & 0xff;
+ brd->vpd[i * 2] = a & 0xff;
+ brd->vpd[(i * 2) + 1] = (a >> 8) & 0xff;
}
if (((brd->vpd[0x08] != 0x82) /* long resource name tag */
diff --git a/drivers/staging/dgnc/dgnc_tty.c b/drivers/staging/dgnc/dgnc_tty.c
index 48e4b90578c1..b79eab084c02 100644
--- a/drivers/staging/dgnc/dgnc_tty.c
+++ b/drivers/staging/dgnc/dgnc_tty.c
@@ -448,7 +448,7 @@ void dgnc_tty_uninit(struct dgnc_board *brd)
* dgnc_wmove - Write data to transmit queue.
*
* ch - Pointer to channel structure.
- * buf - Poiter to characters to be moved.
+ * buf - Pointer to characters to be moved.
* n - Number of characters to move.
*
*=======================================================================*/
diff --git a/drivers/staging/dgnc/dgnc_utils.c b/drivers/staging/dgnc/dgnc_utils.c
index f76de82908d3..95272f4765fc 100644
--- a/drivers/staging/dgnc/dgnc_utils.c
+++ b/drivers/staging/dgnc/dgnc_utils.c
@@ -1,7 +1,6 @@
#include <linux/tty.h>
#include <linux/sched.h>
#include "dgnc_utils.h"
-#include "digi.h"
/*
* dgnc_ms_sleep()
diff --git a/drivers/staging/emxx_udc/emxx_udc.c b/drivers/staging/emxx_udc/emxx_udc.c
index 4e6c16af40fc..beb9411658ba 100644
--- a/drivers/staging/emxx_udc/emxx_udc.c
+++ b/drivers/staging/emxx_udc/emxx_udc.c
@@ -823,7 +823,7 @@ static int _nbu2ss_out_dma(
u32 length
)
{
- u8 *pBuffer;
+ dma_addr_t pBuffer;
u32 mpkt;
u32 lmpkt;
u32 dmacnt;
@@ -836,7 +836,7 @@ static int _nbu2ss_out_dma(
return 1; /* DMA is forwarded */
req->dma_flag = TRUE;
- pBuffer = (u8 *)req->req.dma;
+ pBuffer = req->req.dma;
pBuffer += req->req.actual;
/* DMA Address */
@@ -1034,7 +1034,7 @@ static int _nbu2ss_in_dma(
u32 length
)
{
- u8 *pBuffer;
+ dma_addr_t pBuffer;
u32 mpkt; /* MaxPacketSize */
u32 lmpkt; /* Last Packet Data Size */
u32 dmacnt; /* IN Data Size */
@@ -1080,7 +1080,7 @@ static int _nbu2ss_in_dma(
_nbu2ss_writel(&preg->EP_DCR[num].EP_DCR2, data);
/* Address setting */
- pBuffer = (u8 *)req->req.dma;
+ pBuffer = req->req.dma;
pBuffer += req->req.actual;
_nbu2ss_writel(&preg->EP_DCR[num].EP_TADR, (u32)pBuffer);
@@ -1285,11 +1285,7 @@ static void _nbu2ss_restert_transfer(struct nbu2ss_ep *ep)
bool bflag = FALSE;
struct nbu2ss_req *req;
- if (list_empty(&ep->queue))
- req = NULL;
- else
- req = list_entry(ep->queue.next, struct nbu2ss_req, queue);
-
+ req = list_first_entry_or_null(&ep->queue, struct nbu2ss_req, queue);
if (!req)
return;
@@ -1784,11 +1780,7 @@ static inline int _nbu2ss_ep0_in_data_stage(struct nbu2ss_udc *udc)
struct nbu2ss_req *req;
struct nbu2ss_ep *ep = &udc->ep[0];
- if (list_empty(&ep->queue))
- req = NULL;
- else
- req = list_entry(ep->queue.next, struct nbu2ss_req, queue);
-
+ req = list_first_entry_or_null(&ep->queue, struct nbu2ss_req, queue);
if (!req)
req = &udc->ep0_req;
@@ -1811,11 +1803,7 @@ static inline int _nbu2ss_ep0_out_data_stage(struct nbu2ss_udc *udc)
struct nbu2ss_req *req;
struct nbu2ss_ep *ep = &udc->ep[0];
- if (list_empty(&ep->queue))
- req = NULL;
- else
- req = list_entry(ep->queue.next, struct nbu2ss_req, queue);
-
+ req = list_first_entry_or_null(&ep->queue, struct nbu2ss_req, queue);
if (!req)
req = &udc->ep0_req;
@@ -1838,11 +1826,7 @@ static inline int _nbu2ss_ep0_status_stage(struct nbu2ss_udc *udc)
struct nbu2ss_req *req;
struct nbu2ss_ep *ep = &udc->ep[0];
- if (list_empty(&ep->queue))
- req = NULL;
- else
- req = list_entry(ep->queue.next, struct nbu2ss_req, queue);
-
+ req = list_first_entry_or_null(&ep->queue, struct nbu2ss_req, queue);
if (!req) {
req = &udc->ep0_req;
if (req->req.complete)
@@ -2145,11 +2129,7 @@ static inline void _nbu2ss_epn_int(struct nbu2ss_udc *udc, u32 epnum)
/* Interrupt Clear */
_nbu2ss_writel(&udc->p_regs->EP_REGS[num].EP_STATUS, ~(u32)status);
- if (list_empty(&ep->queue))
- req = NULL;
- else
- req = list_entry(ep->queue.next, struct nbu2ss_req, queue);
-
+ req = list_first_entry_or_null(&ep->queue, struct nbu2ss_req, queue);
if (!req) {
/* pr_warn("=== %s(%d) req == NULL\n", __func__, epnum); */
return;
@@ -2728,7 +2708,7 @@ static int nbu2ss_ep_queue(
spin_lock_irqsave(&udc->lock, flags);
#ifdef USE_DMA
- if ((u32)req->req.buf & 0x3)
+ if ((uintptr_t)req->req.buf & 0x3)
req->unaligned = TRUE;
else
req->unaligned = FALSE;
diff --git a/drivers/staging/fwserial/fwserial.c b/drivers/staging/fwserial/fwserial.c
index b3ea4bb54e2c..b676c486cb18 100644
--- a/drivers/staging/fwserial/fwserial.c
+++ b/drivers/staging/fwserial/fwserial.c
@@ -893,11 +893,10 @@ static void fwserial_destroy(struct kref *kref)
kfree(serial);
}
-void fwtty_port_put(struct fwtty_port *port)
+static void fwtty_port_put(struct fwtty_port *port)
{
kref_put(&port->serial->kref, fwserial_destroy);
}
-EXPORT_SYMBOL(fwtty_port_put);
static void fwtty_port_dtr_rts(struct tty_port *tty_port, int on)
{
diff --git a/drivers/staging/fwserial/fwserial.h b/drivers/staging/fwserial/fwserial.h
index 787aa4f3a41b..e13fe33a6897 100644
--- a/drivers/staging/fwserial/fwserial.h
+++ b/drivers/staging/fwserial/fwserial.h
@@ -342,16 +342,6 @@ static const char loop_dev_name[] = "fwloop";
extern struct tty_driver *fwtty_driver;
struct fwtty_port *fwtty_port_get(unsigned index);
-void fwtty_port_put(struct fwtty_port *port);
-
-static inline void fwtty_bind_console(struct fwtty_port *port,
- struct fwconsole_ops *fwcon_ops,
- void *data)
-{
- port->con_data = data;
- port->fwcon_ops = fwcon_ops;
-}
-
/*
* Returns the max send async payload size in bytes based on the unit device
* link speed. Self-limiting asynchronous bandwidth (via reducing the payload)
diff --git a/drivers/staging/gdm724x/gdm_lte.c b/drivers/staging/gdm724x/gdm_lte.c
index 79de678807cc..17d148f6e02c 100644
--- a/drivers/staging/gdm724x/gdm_lte.c
+++ b/drivers/staging/gdm724x/gdm_lte.c
@@ -555,7 +555,7 @@ int gdm_lte_event_init(void)
void gdm_lte_event_exit(void)
{
if (lte_event.sock && --lte_event.ref_cnt == 0) {
- netlink_exit(lte_event.sock);
+ sock_release(lte_event.sock->sk_socket);
lte_event.sock = NULL;
}
}
diff --git a/drivers/staging/gdm724x/gdm_tty.c b/drivers/staging/gdm724x/gdm_tty.c
index e2c0f228f369..eb7e2523c354 100644
--- a/drivers/staging/gdm724x/gdm_tty.c
+++ b/drivers/staging/gdm724x/gdm_tty.c
@@ -64,7 +64,7 @@ static void gdm_port_destruct(struct tty_port *port)
kfree(gdm);
}
-static struct tty_port_operations gdm_port_ops = {
+static const struct tty_port_operations gdm_port_ops = {
.destruct = gdm_port_destruct,
};
diff --git a/drivers/staging/gdm724x/netlink_k.c b/drivers/staging/gdm724x/netlink_k.c
index 92254fdaae1e..9d8347769e88 100644
--- a/drivers/staging/gdm724x/netlink_k.c
+++ b/drivers/staging/gdm724x/netlink_k.c
@@ -107,11 +107,6 @@ struct sock *netlink_init(int unit,
return sock;
}
-void netlink_exit(struct sock *sock)
-{
- sock_release(sock->sk_socket);
-}
-
int netlink_send(struct sock *sock, int group, u16 type, void *msg, int len)
{
static u32 seq;
diff --git a/drivers/staging/gdm724x/netlink_k.h b/drivers/staging/gdm724x/netlink_k.h
index 589486d76714..7cf979b3f826 100644
--- a/drivers/staging/gdm724x/netlink_k.h
+++ b/drivers/staging/gdm724x/netlink_k.h
@@ -19,7 +19,6 @@
struct sock *netlink_init(int unit,
void (*cb)(struct net_device *dev, u16 type, void *msg, int len));
-void netlink_exit(struct sock *sock);
int netlink_send(struct sock *sock, int group, u16 type, void *msg, int len);
#endif /* _NETLINK_K_H_ */
diff --git a/drivers/staging/gdm72xx/gdm_qos.c b/drivers/staging/gdm72xx/gdm_qos.c
index 81feffa5784a..cad347a05d18 100644
--- a/drivers/staging/gdm72xx/gdm_qos.c
+++ b/drivers/staging/gdm72xx/gdm_qos.c
@@ -101,7 +101,7 @@ void gdm_qos_init(void *nic_ptr)
}
qcb->qos_list_cnt = 0;
- qcb->qos_null_idx = QOS_MAX-1;
+ qcb->qos_null_idx = QOS_MAX - 1;
qcb->qos_limit_size = 255;
spin_lock_init(&qcb->qos_lock);
@@ -128,7 +128,7 @@ void gdm_qos_release_list(void *nic_ptr)
}
qcb->qos_list_cnt = 0;
- qcb->qos_null_idx = QOS_MAX-1;
+ qcb->qos_null_idx = QOS_MAX - 1;
for (i = 0; i < QOS_MAX; i++) {
list_for_each_entry_safe(entry, n, &qcb->qos_list[i], list) {
@@ -143,18 +143,18 @@ static int chk_ipv4_rule(struct gdm_wimax_csr_s *csr, u8 *stream, u8 *port)
{
int i;
- if (csr->classifier_rule_en&IPTYPEOFSERVICE) {
+ if (csr->classifier_rule_en & IPTYPEOFSERVICE) {
if (((stream[1] & csr->ip2s_mask) < csr->ip2s_lo) ||
((stream[1] & csr->ip2s_mask) > csr->ip2s_hi))
return 1;
}
- if (csr->classifier_rule_en&PROTOCOL) {
+ if (csr->classifier_rule_en & PROTOCOL) {
if (stream[9] != csr->protocol)
return 1;
}
- if (csr->classifier_rule_en&IPMASKEDSRCADDRESS) {
+ if (csr->classifier_rule_en & IPMASKEDSRCADDRESS) {
for (i = 0; i < 4; i++) {
if ((stream[12 + i] & csr->ipsrc_addrmask[i]) !=
(csr->ipsrc_addr[i] & csr->ipsrc_addrmask[i]))
@@ -162,7 +162,7 @@ static int chk_ipv4_rule(struct gdm_wimax_csr_s *csr, u8 *stream, u8 *port)
}
}
- if (csr->classifier_rule_en&IPMASKEDDSTADDRESS) {
+ if (csr->classifier_rule_en & IPMASKEDDSTADDRESS) {
for (i = 0; i < 4; i++) {
if ((stream[16 + i] & csr->ipdst_addrmask[i]) !=
(csr->ipdst_addr[i] & csr->ipdst_addrmask[i]))
@@ -170,14 +170,14 @@ static int chk_ipv4_rule(struct gdm_wimax_csr_s *csr, u8 *stream, u8 *port)
}
}
- if (csr->classifier_rule_en&PROTOCOLSRCPORTRANGE) {
- i = ((port[0]<<8)&0xff00)+port[1];
+ if (csr->classifier_rule_en & PROTOCOLSRCPORTRANGE) {
+ i = ((port[0] << 8) & 0xff00) + port[1];
if ((i < csr->srcport_lo) || (i > csr->srcport_hi))
return 1;
}
- if (csr->classifier_rule_en&PROTOCOLDSTPORTRANGE) {
- i = ((port[2]<<8)&0xff00)+port[3];
+ if (csr->classifier_rule_en & PROTOCOLDSTPORTRANGE) {
+ i = ((port[2] << 8) & 0xff00) + port[3];
if ((i < csr->dstport_lo) || (i > csr->dstport_hi))
return 1;
}
@@ -193,7 +193,7 @@ static int get_qos_index(struct nic *nic, u8 *iph, u8 *tcpudph)
if (!iph || !tcpudph)
return -1;
- ip_ver = (iph[0]>>4)&0xf;
+ ip_ver = (iph[0] >> 4) & 0xf;
if (ip_ver != 4)
return -1;
@@ -342,17 +342,17 @@ void gdm_recv_qos_hci_packet(void *nic_ptr, u8 *buf, int size)
if (sub_cmd_evt == QOS_REPORT) {
spin_lock_irqsave(&qcb->qos_lock, flags);
for (i = 0; i < qcb->qos_list_cnt; i++) {
- sfid = ((buf[(i*5)+6]<<24)&0xff000000);
- sfid += ((buf[(i*5)+7]<<16)&0xff0000);
- sfid += ((buf[(i*5)+8]<<8)&0xff00);
- sfid += (buf[(i*5)+9]);
+ sfid = ((buf[(i*5) + 6] << 24) & 0xff000000);
+ sfid += ((buf[(i*5) + 7] << 16) & 0xff0000);
+ sfid += ((buf[(i*5) + 8] << 8) & 0xff00);
+ sfid += (buf[(i*5) + 9]);
index = get_csr(qcb, sfid, 0);
if (index == -1) {
spin_unlock_irqrestore(&qcb->qos_lock, flags);
netdev_err(nic->netdev, "QoS ERROR: No SF\n");
return;
}
- qcb->csr[index].qos_buf_count = buf[(i*5)+10];
+ qcb->csr[index].qos_buf_count = buf[(i*5) + 10];
}
extract_qos_list(nic, &send_list);
@@ -363,9 +363,9 @@ void gdm_recv_qos_hci_packet(void *nic_ptr, u8 *buf, int size)
/* sub_cmd_evt == QOS_ADD || sub_cmd_evt == QOS_CHANG_DEL */
pos = 6;
- sfid = ((buf[pos++]<<24)&0xff000000);
- sfid += ((buf[pos++]<<16)&0xff0000);
- sfid += ((buf[pos++]<<8)&0xff00);
+ sfid = ((buf[pos++] << 24) & 0xff000000);
+ sfid += ((buf[pos++] << 16) & 0xff0000);
+ sfid += ((buf[pos++] << 8) & 0xff00);
sfid += (buf[pos++]);
index = get_csr(qcb, sfid, 1);
@@ -382,7 +382,7 @@ void gdm_recv_qos_hci_packet(void *nic_ptr, u8 *buf, int size)
spin_lock_irqsave(&qcb->qos_lock, flags);
qcb->csr[index].sfid = sfid;
- qcb->csr[index].classifier_rule_en = ((buf[pos++]<<8)&0xff00);
+ qcb->csr[index].classifier_rule_en = ((buf[pos++] << 8) & 0xff00);
qcb->csr[index].classifier_rule_en += buf[pos++];
if (qcb->csr[index].classifier_rule_en == 0)
qcb->qos_null_idx = index;
@@ -406,16 +406,16 @@ void gdm_recv_qos_hci_packet(void *nic_ptr, u8 *buf, int size)
qcb->csr[index].ipdst_addr[1] = buf[pos++];
qcb->csr[index].ipdst_addr[2] = buf[pos++];
qcb->csr[index].ipdst_addr[3] = buf[pos++];
- qcb->csr[index].srcport_lo = ((buf[pos++]<<8)&0xff00);
+ qcb->csr[index].srcport_lo = ((buf[pos++] << 8) & 0xff00);
qcb->csr[index].srcport_lo += buf[pos++];
- qcb->csr[index].srcport_hi = ((buf[pos++]<<8)&0xff00);
+ qcb->csr[index].srcport_hi = ((buf[pos++] << 8) & 0xff00);
qcb->csr[index].srcport_hi += buf[pos++];
- qcb->csr[index].dstport_lo = ((buf[pos++]<<8)&0xff00);
+ qcb->csr[index].dstport_lo = ((buf[pos++] << 8) & 0xff00);
qcb->csr[index].dstport_lo += buf[pos++];
- qcb->csr[index].dstport_hi = ((buf[pos++]<<8)&0xff00);
+ qcb->csr[index].dstport_hi = ((buf[pos++] << 8) & 0xff00);
qcb->csr[index].dstport_hi += buf[pos++];
- qcb->qos_limit_size = 254/qcb->qos_list_cnt;
+ qcb->qos_limit_size = 254 / qcb->qos_list_cnt;
spin_unlock_irqrestore(&qcb->qos_lock, flags);
} else if (sub_cmd_evt == QOS_CHANGE_DEL) {
netdev_dbg(nic->netdev, "QOS_CHANGE_DEL SFID = 0x%x, index=%d\n",
@@ -426,7 +426,7 @@ void gdm_recv_qos_hci_packet(void *nic_ptr, u8 *buf, int size)
spin_lock_irqsave(&qcb->qos_lock, flags);
qcb->csr[index].enabled = false;
qcb->qos_list_cnt--;
- qcb->qos_limit_size = 254/qcb->qos_list_cnt;
+ qcb->qos_limit_size = 254 / qcb->qos_list_cnt;
list_for_each_entry_safe(entry, n, &qcb->qos_list[index],
list) {
diff --git a/drivers/staging/gdm72xx/gdm_sdio.c b/drivers/staging/gdm72xx/gdm_sdio.c
index b0521da3c793..1f5a087723ba 100644
--- a/drivers/staging/gdm72xx/gdm_sdio.c
+++ b/drivers/staging/gdm72xx/gdm_sdio.c
@@ -36,7 +36,7 @@
#define RX_BUF_SIZE (25*1024)
#define TX_HZ 2000
-#define TX_INTERVAL (1000000/TX_HZ)
+#define TX_INTERVAL (NSEC_PER_SEC/TX_HZ)
static struct sdio_tx *alloc_tx_struct(struct tx_cxt *tx)
{
@@ -303,7 +303,7 @@ static void send_sdu(struct sdio_func *func, struct tx_cxt *tx)
put_tx_struct(t->tx_cxt, t);
}
- do_gettimeofday(&tx->sdu_stamp);
+ tx->sdu_stamp = ktime_get();
spin_unlock_irqrestore(&tx->lock, flags);
}
@@ -330,7 +330,7 @@ static void do_tx(struct work_struct *work)
struct sdio_func *func = sdev->func;
struct tx_cxt *tx = &sdev->tx;
struct sdio_tx *t = NULL;
- struct timeval now, *before;
+ ktime_t now, before;
int is_sdu = 0;
long diff;
unsigned long flags;
@@ -346,11 +346,10 @@ static void do_tx(struct work_struct *work)
list_del(&t->list);
is_sdu = 0;
} else if (!tx->stop_sdu_tx && !list_empty(&tx->sdu_list)) {
- do_gettimeofday(&now);
- before = &tx->sdu_stamp;
+ now = ktime_get();
+ before = tx->sdu_stamp;
- diff = (now.tv_sec - before->tv_sec) * 1000000 +
- (now.tv_usec - before->tv_usec);
+ diff = ktime_to_ns(ktime_sub(now, before));
if (diff >= 0 && diff < TX_INTERVAL) {
schedule_work(&sdev->ws);
spin_unlock_irqrestore(&tx->lock, flags);
diff --git a/drivers/staging/gdm72xx/gdm_sdio.h b/drivers/staging/gdm72xx/gdm_sdio.h
index 77ad9d686f8e..aa7dad22a219 100644
--- a/drivers/staging/gdm72xx/gdm_sdio.h
+++ b/drivers/staging/gdm72xx/gdm_sdio.h
@@ -15,7 +15,7 @@
#define __GDM72XX_GDM_SDIO_H__
#include <linux/types.h>
-#include <linux/time.h>
+#include <linux/ktime.h>
#define MAX_NR_SDU_BUF 64
@@ -32,7 +32,7 @@ struct tx_cxt {
struct list_head free_list;
struct list_head sdu_list;
struct list_head hci_list;
- struct timeval sdu_stamp;
+ ktime_t sdu_stamp;
u8 *sdu_buf;
spinlock_t lock;
int can_send;
diff --git a/drivers/staging/gdm72xx/gdm_wimax.c b/drivers/staging/gdm72xx/gdm_wimax.c
index d9ddced96e19..ba03f9386567 100644
--- a/drivers/staging/gdm72xx/gdm_wimax.c
+++ b/drivers/staging/gdm72xx/gdm_wimax.c
@@ -84,11 +84,6 @@ static inline struct evt_entry *alloc_event_entry(void)
return kmalloc(sizeof(struct evt_entry), GFP_ATOMIC);
}
-static inline void free_event_entry(struct evt_entry *e)
-{
- kfree(e);
-}
-
static struct evt_entry *get_event_entry(void)
{
struct evt_entry *e;
@@ -180,11 +175,11 @@ static void gdm_wimax_event_exit(void)
list_for_each_entry_safe(e, temp, &wm_event.evtq, list) {
list_del(&e->list);
- free_event_entry(e);
+ kfree(e);
}
list_for_each_entry_safe(e, temp, &wm_event.freeq, list) {
list_del(&e->list);
- free_event_entry(e);
+ kfree(e);
}
spin_unlock_irqrestore(&wm_event.evt_lock, flags);
@@ -368,7 +363,7 @@ static void kdelete(void **buf)
}
}
-static int gdm_wimax_ioctl_get_data(struct data_s *dst, struct data_s *src)
+static int gdm_wimax_ioctl_get_data(struct udata_s *dst, struct data_s *src)
{
int size;
@@ -384,7 +379,7 @@ static int gdm_wimax_ioctl_get_data(struct data_s *dst, struct data_s *src)
return 0;
}
-static int gdm_wimax_ioctl_set_data(struct data_s *dst, struct data_s *src)
+static int gdm_wimax_ioctl_set_data(struct data_s *dst, struct udata_s *src)
{
if (!src->size) {
dst->size = 0;
@@ -460,6 +455,7 @@ static int gdm_wimax_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
struct wm_req_s *req = (struct wm_req_s *)ifr;
struct nic *nic = netdev_priv(dev);
int ret;
+ struct fsm_s fsm_buf;
if (cmd != SIOCWMIOCTL)
return -EOPNOTSUPP;
@@ -482,8 +478,11 @@ static int gdm_wimax_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
/* NOTE: gdm_update_fsm should be called
* before gdm_wimax_ioctl_set_data is called.
*/
- gdm_update_fsm(dev,
- req->data.buf);
+ if (copy_from_user(&fsm_buf, req->data.buf,
+ sizeof(struct fsm_s)))
+ return -EFAULT;
+
+ gdm_update_fsm(dev, &fsm_buf);
}
ret = gdm_wimax_ioctl_set_data(
&nic->sdk_data[req->data_id], &req->data);
diff --git a/drivers/staging/gdm72xx/wm_ioctl.h b/drivers/staging/gdm72xx/wm_ioctl.h
index ed8f649c0042..631cb1d23c7e 100644
--- a/drivers/staging/gdm72xx/wm_ioctl.h
+++ b/drivers/staging/gdm72xx/wm_ioctl.h
@@ -78,13 +78,18 @@ struct data_s {
void *buf;
};
+struct udata_s {
+ int size;
+ void __user *buf;
+};
+
struct wm_req_s {
union {
char ifrn_name[IFNAMSIZ];
} ifr_ifrn;
unsigned short cmd;
unsigned short data_id;
- struct data_s data;
+ struct udata_s data;
/* NOTE: sizeof(struct wm_req_s) must be less than sizeof(struct ifreq). */
};
diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
index 9d7f0004d2d7..0e044cb0def8 100644
--- a/drivers/staging/iio/Kconfig
+++ b/drivers/staging/iio/Kconfig
@@ -17,33 +17,4 @@ source "drivers/staging/iio/meter/Kconfig"
source "drivers/staging/iio/resolver/Kconfig"
source "drivers/staging/iio/trigger/Kconfig"
-config IIO_DUMMY_EVGEN
- tristate
- select IRQ_WORK
-
-config IIO_SIMPLE_DUMMY
- tristate "An example driver with no hardware requirements"
- help
- Driver intended mainly as documentation for how to write
- a driver. May also be useful for testing userspace code
- without hardware.
-
-if IIO_SIMPLE_DUMMY
-
-config IIO_SIMPLE_DUMMY_EVENTS
- bool "Event generation support"
- select IIO_DUMMY_EVGEN
- help
- Add some dummy events to the simple dummy driver.
-
-config IIO_SIMPLE_DUMMY_BUFFER
- bool "Buffered capture support"
- select IIO_BUFFER
- select IIO_TRIGGER
- select IIO_KFIFO_BUF
- help
- Add buffered data capture to the simple dummy driver.
-
-endif # IIO_SIMPLE_DUMMY
-
endmenu
diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile
index d87106135b27..3e616b4437f5 100644
--- a/drivers/staging/iio/Makefile
+++ b/drivers/staging/iio/Makefile
@@ -2,13 +2,6 @@
# Makefile for the industrial I/O core.
#
-obj-$(CONFIG_IIO_SIMPLE_DUMMY) += iio_dummy.o
-iio_dummy-y := iio_simple_dummy.o
-iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_EVENTS) += iio_simple_dummy_events.o
-iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_BUFFER) += iio_simple_dummy_buffer.o
-
-obj-$(CONFIG_IIO_DUMMY_EVGEN) += iio_dummy_evgen.o
-
obj-y += accel/
obj-y += adc/
obj-y += addac/
diff --git a/drivers/staging/iio/accel/sca3000_ring.c b/drivers/staging/iio/accel/sca3000_ring.c
index 20b878d35ea2..1920dc60cf3d 100644
--- a/drivers/staging/iio/accel/sca3000_ring.c
+++ b/drivers/staging/iio/accel/sca3000_ring.c
@@ -48,7 +48,7 @@ static int sca3000_read_data(struct sca3000_state *st,
}
};
*rx_p = kmalloc(len, GFP_KERNEL);
- if (*rx_p == NULL) {
+ if (!*rx_p) {
ret = -ENOMEM;
goto error_ret;
}
diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c
index bb40f3728742..92211039ffa9 100644
--- a/drivers/staging/iio/adc/ad7192.c
+++ b/drivers/staging/iio/adc/ad7192.c
@@ -609,7 +609,7 @@ static const struct iio_chan_spec ad7192_channels[] = {
static int ad7192_probe(struct spi_device *spi)
{
- const struct ad7192_platform_data *pdata = spi->dev.platform_data;
+ const struct ad7192_platform_data *pdata = dev_get_platdata(&spi->dev);
struct ad7192_state *st;
struct iio_dev *indio_dev;
int ret, voltage_uv = 0;
diff --git a/drivers/staging/iio/adc/ad7280a.c b/drivers/staging/iio/adc/ad7280a.c
index 35acb1a4669b..f45ebedb7a05 100644
--- a/drivers/staging/iio/adc/ad7280a.c
+++ b/drivers/staging/iio/adc/ad7280a.c
@@ -833,7 +833,7 @@ static const struct ad7280_platform_data ad7793_default_pdata = {
static int ad7280_probe(struct spi_device *spi)
{
- const struct ad7280_platform_data *pdata = spi->dev.platform_data;
+ const struct ad7280_platform_data *pdata = dev_get_platdata(&spi->dev);
struct ad7280_state *st;
int ret;
const unsigned short tACQ_ns[4] = {465, 1010, 1460, 1890};
diff --git a/drivers/staging/iio/adc/ad7780.c b/drivers/staging/iio/adc/ad7780.c
index 3abc7789237f..1439cfdbb09c 100644
--- a/drivers/staging/iio/adc/ad7780.c
+++ b/drivers/staging/iio/adc/ad7780.c
@@ -15,15 +15,13 @@
#include <linux/regulator/consumer.h>
#include <linux/err.h>
#include <linux/sched.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/adc/ad_sigma_delta.h>
-#include "ad7780.h"
-
#define AD7780_RDY BIT(7)
#define AD7780_FILTER BIT(6)
#define AD7780_ERR BIT(5)
@@ -42,7 +40,7 @@ struct ad7780_chip_info {
struct ad7780_state {
const struct ad7780_chip_info *chip_info;
struct regulator *reg;
- int powerdown_gpio;
+ struct gpio_desc *powerdown_gpio;
unsigned int gain;
u16 int_vref_mv;
@@ -77,8 +75,7 @@ static int ad7780_set_mode(struct ad_sigma_delta *sigma_delta,
break;
}
- if (gpio_is_valid(st->powerdown_gpio))
- gpio_set_value(st->powerdown_gpio, val);
+ gpiod_set_value(st->powerdown_gpio, val);
return 0;
}
@@ -163,7 +160,6 @@ static const struct iio_info ad7780_info = {
static int ad7780_probe(struct spi_device *spi)
{
- struct ad7780_platform_data *pdata = spi->dev.platform_data;
struct ad7780_state *st;
struct iio_dev *indio_dev;
int ret, voltage_uv = 0;
@@ -189,12 +185,10 @@ static int ad7780_probe(struct spi_device *spi)
st->chip_info =
&ad7780_chip_info_tbl[spi_get_device_id(spi)->driver_data];
- if (pdata && pdata->vref_mv)
- st->int_vref_mv = pdata->vref_mv;
- else if (voltage_uv)
+ if (voltage_uv)
st->int_vref_mv = voltage_uv / 1000;
else
- dev_warn(&spi->dev, "reference voltage unspecified\n");
+ dev_warn(&spi->dev, "Reference voltage unspecified\n");
spi_set_drvdata(spi, indio_dev);
@@ -205,18 +199,14 @@ static int ad7780_probe(struct spi_device *spi)
indio_dev->num_channels = 1;
indio_dev->info = &ad7780_info;
- if (pdata && gpio_is_valid(pdata->gpio_pdrst)) {
- ret = devm_gpio_request_one(&spi->dev,
- pdata->gpio_pdrst,
- GPIOF_OUT_INIT_LOW,
- "AD7780 /PDRST");
- if (ret) {
- dev_err(&spi->dev, "failed to request GPIO PDRST\n");
- goto error_disable_reg;
- }
- st->powerdown_gpio = pdata->gpio_pdrst;
- } else {
- st->powerdown_gpio = -1;
+ st->powerdown_gpio = devm_gpiod_get_optional(&spi->dev,
+ "powerdown",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(st->powerdown_gpio)) {
+ ret = PTR_ERR(st->powerdown_gpio);
+ dev_err(&spi->dev, "Failed to request powerdown GPIO: %d\n",
+ ret);
+ goto error_disable_reg;
}
ret = ad_sd_setup_buffer_and_trigger(indio_dev);
diff --git a/drivers/staging/iio/adc/ad7780.h b/drivers/staging/iio/adc/ad7780.h
deleted file mode 100644
index 67e511c3d6f0..000000000000
--- a/drivers/staging/iio/adc/ad7780.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * AD7780/AD7781 SPI ADC driver
- *
- * Copyright 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
- */
-#ifndef IIO_ADC_AD7780_H_
-#define IIO_ADC_AD7780_H_
-
-/*
- * TODO: struct ad7780_platform_data needs to go into include/linux/iio
- */
-
-/* NOTE:
- * The AD7780 doesn't feature a dedicated SPI chip select, in addition it
- * features a dual use data out ready DOUT/RDY output.
- * In order to avoid contentions on the SPI bus, it's therefore necessary
- * to use spi bus locking combined with a dedicated GPIO to control the
- * power down reset signal of the AD7780.
- *
- * The DOUT/RDY output must also be wired to an interrupt capable GPIO.
- */
-
-struct ad7780_platform_data {
- u16 vref_mv;
- int gpio_pdrst;
-};
-
-#endif /* IIO_ADC_AD7780_H_ */
diff --git a/drivers/staging/iio/adc/ad7816.c b/drivers/staging/iio/adc/ad7816.c
index c8e156646528..22260512cf01 100644
--- a/drivers/staging/iio/adc/ad7816.c
+++ b/drivers/staging/iio/adc/ad7816.c
@@ -345,7 +345,7 @@ static int ad7816_probe(struct spi_device *spi_dev)
{
struct ad7816_chip_info *chip;
struct iio_dev *indio_dev;
- unsigned short *pins = spi_dev->dev.platform_data;
+ unsigned short *pins = dev_get_platdata(&spi_dev->dev);
int ret = 0;
int i;
diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c
index d997d9c74ca8..bb1f15224ac8 100644
--- a/drivers/staging/iio/adc/mxs-lradc.c
+++ b/drivers/staging/iio/adc/mxs-lradc.c
@@ -319,12 +319,12 @@ struct mxs_lradc {
#define LRADC_CH_VALUE_OFFSET 0
#define LRADC_DELAY(n) (0xd0 + (0x10 * (n)))
-#define LRADC_DELAY_TRIGGER_LRADCS_MASK (0xff << 24)
+#define LRADC_DELAY_TRIGGER_LRADCS_MASK (0xffUL << 24)
#define LRADC_DELAY_TRIGGER_LRADCS_OFFSET 24
#define LRADC_DELAY_TRIGGER(x) \
(((x) << LRADC_DELAY_TRIGGER_LRADCS_OFFSET) & \
LRADC_DELAY_TRIGGER_LRADCS_MASK)
-#define LRADC_DELAY_KICK (1 << 20)
+#define LRADC_DELAY_KICK BIT(20)
#define LRADC_DELAY_TRIGGER_DELAYS_MASK (0xf << 16)
#define LRADC_DELAY_TRIGGER_DELAYS_OFFSET 16
#define LRADC_DELAY_TRIGGER_DELAYS(x) \
diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c
index 2b65faa6296a..18b27a1984b2 100644
--- a/drivers/staging/iio/frequency/ad9832.c
+++ b/drivers/staging/iio/frequency/ad9832.c
@@ -201,7 +201,7 @@ static const struct iio_info ad9832_info = {
static int ad9832_probe(struct spi_device *spi)
{
- struct ad9832_platform_data *pdata = spi->dev.platform_data;
+ struct ad9832_platform_data *pdata = dev_get_platdata(&spi->dev);
struct iio_dev *indio_dev;
struct ad9832_state *st;
struct regulator *reg;
diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c
index 6464f2cbe94b..6366216e4f37 100644
--- a/drivers/staging/iio/frequency/ad9834.c
+++ b/drivers/staging/iio/frequency/ad9834.c
@@ -318,7 +318,7 @@ static const struct iio_info ad9833_info = {
static int ad9834_probe(struct spi_device *spi)
{
- struct ad9834_platform_data *pdata = spi->dev.platform_data;
+ struct ad9834_platform_data *pdata = dev_get_platdata(&spi->dev);
struct ad9834_state *st;
struct iio_dev *indio_dev;
struct regulator *reg;
diff --git a/drivers/staging/iio/light/tsl2x7x_core.c b/drivers/staging/iio/light/tsl2x7x_core.c
index 9dfd04855a1b..5b1c1650a0e4 100644
--- a/drivers/staging/iio/light/tsl2x7x_core.c
+++ b/drivers/staging/iio/light/tsl2x7x_core.c
@@ -1898,7 +1898,7 @@ static int tsl2x7x_probe(struct i2c_client *clientp,
mutex_init(&chip->prox_mutex);
chip->tsl2x7x_chip_status = TSL2X7X_CHIP_UNKNOWN;
- chip->pdata = clientp->dev.platform_data;
+ chip->pdata = dev_get_platdata(&clientp->dev);
chip->id = id->driver_data;
chip->chip_info =
&tsl2x7x_chip_info_tbl[device_channel_config[id->driver_data]];
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs.h b/drivers/staging/lustre/include/linux/libcfs/libcfs.h
index 4d74e8af5088..0d8a91ee5ffc 100644
--- a/drivers/staging/lustre/include/linux/libcfs/libcfs.h
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs.h
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -42,13 +42,6 @@
#include "curproc.h"
-static inline int __is_po2(unsigned long long val)
-{
- return !(val & (val - 1));
-}
-
-#define IS_PO2(val) __is_po2((unsigned long long)(val))
-
#define LOWEST_BIT_SET(x) ((x) & ~((x) - 1))
/*
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs_cpu.h b/drivers/staging/lustre/include/linux/libcfs/libcfs_cpu.h
index 787867847483..1530b0458a61 100644
--- a/drivers/staging/lustre/include/linux/libcfs/libcfs_cpu.h
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs_cpu.h
@@ -22,7 +22,8 @@
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, Intel Corporation.
+ *
+ * Copyright (c) 2012, 2015 Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs_hash.h b/drivers/staging/lustre/include/linux/libcfs/libcfs_hash.h
index 70b8b29e831c..c3f2332fa043 100644
--- a/drivers/staging/lustre/include/linux/libcfs/libcfs_hash.h
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs_hash.h
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015 Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -41,6 +41,9 @@
#ifndef __LIBCFS_HASH_H__
#define __LIBCFS_HASH_H__
+
+#include <linux/hash.h>
+
/*
* Knuth recommends primes in approximately golden ratio to the maximum
* integer representable by a machine word for multiplicative hashing.
@@ -56,22 +59,13 @@
/* 2^63 + 2^61 - 2^57 + 2^54 - 2^51 - 2^18 + 1 */
#define CFS_GOLDEN_RATIO_PRIME_64 0x9e37fffffffc0001ULL
-/*
- * Ideally we would use HAVE_HASH_LONG for this, but on linux we configure
- * the linux kernel and user space at the same time, so we need to differentiate
- * between them explicitly. If this is not needed on other architectures, then
- * we'll need to move the functions to architecture specific headers.
- */
-
-#include <linux/hash.h>
-
/** disable debug */
-#define CFS_HASH_DEBUG_NONE 0
+#define CFS_HASH_DEBUG_NONE 0
/** record hash depth and output to console when it's too deep,
* computing overhead is low but consume more memory */
-#define CFS_HASH_DEBUG_1 1
+#define CFS_HASH_DEBUG_1 1
/** expensive, check key validation */
-#define CFS_HASH_DEBUG_2 2
+#define CFS_HASH_DEBUG_2 2
#define CFS_HASH_DEBUG_LEVEL CFS_HASH_DEBUG_NONE
@@ -108,16 +102,18 @@ struct cfs_hash_bucket {
* cfs_hash bucket descriptor, it's normally in stack of caller
*/
struct cfs_hash_bd {
- struct cfs_hash_bucket *bd_bucket; /**< address of bucket */
- unsigned int bd_offset; /**< offset in bucket */
+ /* address of bucket */
+ struct cfs_hash_bucket *bd_bucket;
+ /* offset in bucket */
+ unsigned int bd_offset;
};
-#define CFS_HASH_NAME_LEN 16 /**< default name length */
-#define CFS_HASH_BIGNAME_LEN 64 /**< bigname for param tree */
+#define CFS_HASH_NAME_LEN 16 /**< default name length */
+#define CFS_HASH_BIGNAME_LEN 64 /**< bigname for param tree */
-#define CFS_HASH_BKT_BITS 3 /**< default bits of bucket */
-#define CFS_HASH_BITS_MAX 30 /**< max bits of bucket */
-#define CFS_HASH_BITS_MIN CFS_HASH_BKT_BITS
+#define CFS_HASH_BKT_BITS 3 /**< default bits of bucket */
+#define CFS_HASH_BITS_MAX 30 /**< max bits of bucket */
+#define CFS_HASH_BITS_MIN CFS_HASH_BKT_BITS
/**
* common hash attributes.
@@ -133,41 +129,41 @@ enum cfs_hash_tag {
*/
CFS_HASH_NO_LOCK = 1 << 0,
/** no bucket lock, use one spinlock to protect the whole hash */
- CFS_HASH_NO_BKTLOCK = 1 << 1,
+ CFS_HASH_NO_BKTLOCK = 1 << 1,
/** rwlock to protect bucket */
- CFS_HASH_RW_BKTLOCK = 1 << 2,
+ CFS_HASH_RW_BKTLOCK = 1 << 2,
/** spinlock to protect bucket */
- CFS_HASH_SPIN_BKTLOCK = 1 << 3,
+ CFS_HASH_SPIN_BKTLOCK = 1 << 3,
/** always add new item to tail */
- CFS_HASH_ADD_TAIL = 1 << 4,
+ CFS_HASH_ADD_TAIL = 1 << 4,
/** hash-table doesn't have refcount on item */
- CFS_HASH_NO_ITEMREF = 1 << 5,
+ CFS_HASH_NO_ITEMREF = 1 << 5,
/** big name for param-tree */
CFS_HASH_BIGNAME = 1 << 6,
/** track global count */
CFS_HASH_COUNTER = 1 << 7,
/** rehash item by new key */
- CFS_HASH_REHASH_KEY = 1 << 8,
+ CFS_HASH_REHASH_KEY = 1 << 8,
/** Enable dynamic hash resizing */
- CFS_HASH_REHASH = 1 << 9,
+ CFS_HASH_REHASH = 1 << 9,
/** can shrink hash-size */
- CFS_HASH_SHRINK = 1 << 10,
+ CFS_HASH_SHRINK = 1 << 10,
/** assert hash is empty on exit */
- CFS_HASH_ASSERT_EMPTY = 1 << 11,
+ CFS_HASH_ASSERT_EMPTY = 1 << 11,
/** record hlist depth */
- CFS_HASH_DEPTH = 1 << 12,
+ CFS_HASH_DEPTH = 1 << 12,
/**
* rehash is always scheduled in a different thread, so current
* change on hash table is non-blocking
*/
- CFS_HASH_NBLK_CHANGE = 1 << 13,
+ CFS_HASH_NBLK_CHANGE = 1 << 13,
/** NB, we typed hs_flags as __u16, please change it
* if you need to extend >=16 flags */
};
/** most used attributes */
-#define CFS_HASH_DEFAULT (CFS_HASH_RW_BKTLOCK | \
- CFS_HASH_COUNTER | CFS_HASH_REHASH)
+#define CFS_HASH_DEFAULT (CFS_HASH_RW_BKTLOCK | \
+ CFS_HASH_COUNTER | CFS_HASH_REHASH)
/**
* cfs_hash is a hash-table implementation for general purpose, it can support:
@@ -211,7 +207,7 @@ enum cfs_hash_tag {
struct cfs_hash {
/** serialize with rehash, or serialize all operations if
* the hash-table has CFS_HASH_NO_BKTLOCK */
- union cfs_hash_lock hs_lock;
+ union cfs_hash_lock hs_lock;
/** hash operations */
struct cfs_hash_ops *hs_ops;
/** hash lock operations */
@@ -219,57 +215,57 @@ struct cfs_hash {
/** hash list operations */
struct cfs_hash_hlist_ops *hs_hops;
/** hash buckets-table */
- struct cfs_hash_bucket **hs_buckets;
+ struct cfs_hash_bucket **hs_buckets;
/** total number of items on this hash-table */
- atomic_t hs_count;
+ atomic_t hs_count;
/** hash flags, see cfs_hash_tag for detail */
- __u16 hs_flags;
+ __u16 hs_flags;
/** # of extra-bytes for bucket, for user saving extended attributes */
- __u16 hs_extra_bytes;
+ __u16 hs_extra_bytes;
/** wants to iterate */
- __u8 hs_iterating;
+ __u8 hs_iterating;
/** hash-table is dying */
- __u8 hs_exiting;
+ __u8 hs_exiting;
/** current hash bits */
- __u8 hs_cur_bits;
+ __u8 hs_cur_bits;
/** min hash bits */
- __u8 hs_min_bits;
+ __u8 hs_min_bits;
/** max hash bits */
- __u8 hs_max_bits;
+ __u8 hs_max_bits;
/** bits for rehash */
- __u8 hs_rehash_bits;
+ __u8 hs_rehash_bits;
/** bits for each bucket */
- __u8 hs_bkt_bits;
+ __u8 hs_bkt_bits;
/** resize min threshold */
- __u16 hs_min_theta;
+ __u16 hs_min_theta;
/** resize max threshold */
- __u16 hs_max_theta;
+ __u16 hs_max_theta;
/** resize count */
- __u32 hs_rehash_count;
+ __u32 hs_rehash_count;
/** # of iterators (caller of cfs_hash_for_each_*) */
- __u32 hs_iterators;
+ __u32 hs_iterators;
/** rehash workitem */
- cfs_workitem_t hs_rehash_wi;
+ cfs_workitem_t hs_rehash_wi;
/** refcount on this hash table */
- atomic_t hs_refcount;
+ atomic_t hs_refcount;
/** rehash buckets-table */
- struct cfs_hash_bucket **hs_rehash_buckets;
+ struct cfs_hash_bucket **hs_rehash_buckets;
#if CFS_HASH_DEBUG_LEVEL >= CFS_HASH_DEBUG_1
/** serialize debug members */
spinlock_t hs_dep_lock;
/** max depth */
- unsigned int hs_dep_max;
+ unsigned int hs_dep_max;
/** id of the deepest bucket */
- unsigned int hs_dep_bkt;
+ unsigned int hs_dep_bkt;
/** offset in the deepest bucket */
- unsigned int hs_dep_off;
+ unsigned int hs_dep_off;
/** bits when we found the max depth */
- unsigned int hs_dep_bits;
+ unsigned int hs_dep_bits;
/** workitem to output max depth */
- cfs_workitem_t hs_dep_wi;
+ cfs_workitem_t hs_dep_wi;
#endif
/** name of htable */
- char hs_name[0];
+ char hs_name[0];
};
struct cfs_hash_lock_ops {
@@ -324,11 +320,11 @@ struct cfs_hash_ops {
};
/** total number of buckets in @hs */
-#define CFS_HASH_NBKT(hs) \
+#define CFS_HASH_NBKT(hs) \
(1U << ((hs)->hs_cur_bits - (hs)->hs_bkt_bits))
/** total number of buckets in @hs while rehashing */
-#define CFS_HASH_RH_NBKT(hs) \
+#define CFS_HASH_RH_NBKT(hs) \
(1U << ((hs)->hs_rehash_bits - (hs)->hs_bkt_bits))
/** number of hlist for in bucket */
@@ -433,19 +429,22 @@ cfs_hash_with_nblk_change(struct cfs_hash *hs)
static inline int
cfs_hash_is_exiting(struct cfs_hash *hs)
-{ /* cfs_hash_destroy is called */
+{
+ /* cfs_hash_destroy is called */
return hs->hs_exiting;
}
static inline int
cfs_hash_is_rehashing(struct cfs_hash *hs)
-{ /* rehash is launched */
+{
+ /* rehash is launched */
return hs->hs_rehash_bits != 0;
}
static inline int
cfs_hash_is_iterating(struct cfs_hash *hs)
-{ /* someone is calling cfs_hash_for_each_* */
+{
+ /* someone is calling cfs_hash_for_each_* */
return hs->hs_iterating || hs->hs_iterators != 0;
}
@@ -641,13 +640,6 @@ cfs_hash_bd_lookup_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd,
struct hlist_node *
cfs_hash_bd_peek_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd,
const void *key);
-struct hlist_node *
-cfs_hash_bd_findadd_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd,
- const void *key, struct hlist_node *hnode,
- int insist_add);
-struct hlist_node *
-cfs_hash_bd_finddel_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd,
- const void *key, struct hlist_node *hnode);
/**
* operations on cfs_hash bucket (bd: bucket descriptor),
@@ -758,7 +750,7 @@ static inline void
cfs_hash_bucket_validate(struct cfs_hash *hs, struct cfs_hash_bd *bd,
struct hlist_node *hnode)
{
- struct cfs_hash_bd bds[2];
+ struct cfs_hash_bd bds[2];
cfs_hash_dual_bd_get(hs, cfs_hash_key(hs, hnode), bds);
LASSERT(bds[0].bd_bucket == bd->bd_bucket ||
@@ -777,9 +769,9 @@ cfs_hash_bucket_validate(struct cfs_hash *hs, struct cfs_hash_bd *bd,
#endif /* CFS_HASH_DEBUG_LEVEL */
-#define CFS_HASH_THETA_BITS 10
-#define CFS_HASH_MIN_THETA (1U << (CFS_HASH_THETA_BITS - 1))
-#define CFS_HASH_MAX_THETA (1U << (CFS_HASH_THETA_BITS + 1))
+#define CFS_HASH_THETA_BITS 10
+#define CFS_HASH_MIN_THETA (1U << (CFS_HASH_THETA_BITS - 1))
+#define CFS_HASH_MAX_THETA (1U << (CFS_HASH_THETA_BITS + 1))
/* Return integer component of theta */
static inline int __cfs_hash_theta_int(int theta)
@@ -848,20 +840,20 @@ cfs_hash_u64_hash(const __u64 key, unsigned mask)
}
/** iterate over all buckets in @bds (array of struct cfs_hash_bd) */
-#define cfs_hash_for_each_bd(bds, n, i) \
+#define cfs_hash_for_each_bd(bds, n, i) \
for (i = 0; i < n && (bds)[i].bd_bucket != NULL; i++)
/** iterate over all buckets of @hs */
-#define cfs_hash_for_each_bucket(hs, bd, pos) \
- for (pos = 0; \
- pos < CFS_HASH_NBKT(hs) && \
+#define cfs_hash_for_each_bucket(hs, bd, pos) \
+ for (pos = 0; \
+ pos < CFS_HASH_NBKT(hs) && \
((bd)->bd_bucket = (hs)->hs_buckets[pos]) != NULL; pos++)
/** iterate over all hlist of bucket @bd */
-#define cfs_hash_bd_for_each_hlist(hs, bd, hlist) \
- for ((bd)->bd_offset = 0; \
- (bd)->bd_offset < CFS_HASH_BKT_NHLIST(hs) && \
- (hlist = cfs_hash_bd_hhead(hs, bd)) != NULL; \
+#define cfs_hash_bd_for_each_hlist(hs, bd, hlist) \
+ for ((bd)->bd_offset = 0; \
+ (bd)->bd_offset < CFS_HASH_BKT_NHLIST(hs) && \
+ (hlist = cfs_hash_bd_hhead(hs, bd)) != NULL; \
(bd)->bd_offset++)
/* !__LIBCFS__HASH_H__ */
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs_ioctl.h b/drivers/staging/lustre/include/linux/libcfs/libcfs_ioctl.h
index f5d741f25ffd..485ab2670918 100644
--- a/drivers/staging/lustre/include/linux/libcfs/libcfs_ioctl.h
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs_ioctl.h
@@ -110,7 +110,6 @@ struct libcfs_ioctl_handler {
#define IOC_LIBCFS_CLEAR_DEBUG _IOWR('e', 31, long)
#define IOC_LIBCFS_MARK_DEBUG _IOWR('e', 32, long)
#define IOC_LIBCFS_MEMHOG _IOWR('e', 36, long)
-#define IOC_LIBCFS_PING_TEST _IOWR('e', 37, long)
/* lnet ioctls */
#define IOC_LIBCFS_GET_NI _IOWR('e', 50, long)
#define IOC_LIBCFS_FAIL_NID _IOWR('e', 51, long)
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs_kernelcomm.h b/drivers/staging/lustre/include/linux/libcfs/libcfs_kernelcomm.h
index a989d2666230..41f3d810aea4 100644
--- a/drivers/staging/lustre/include/linux/libcfs/libcfs_kernelcomm.h
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs_kernelcomm.h
@@ -91,7 +91,7 @@ typedef int (*libcfs_kkuc_cb_t)(__u32 data, void *cb_arg);
/* Kernel methods */
int libcfs_kkuc_msg_put(struct file *fp, void *payload);
int libcfs_kkuc_group_put(int group, void *payload);
-int libcfs_kkuc_group_add(struct file *fp, int uid, int group,
+int libcfs_kkuc_group_add(struct file *fp, int uid, unsigned int group,
__u32 data);
int libcfs_kkuc_group_rem(int uid, int group);
int libcfs_kkuc_group_foreach(int group, libcfs_kkuc_cb_t cb_func,
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs_private.h b/drivers/staging/lustre/include/linux/libcfs/libcfs_private.h
index f0b0423a716b..d6273e143324 100644
--- a/drivers/staging/lustre/include/linux/libcfs/libcfs_private.h
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs_private.h
@@ -185,8 +185,6 @@ int libcfs_debug_cleanup(void);
int libcfs_debug_clear_buffer(void);
int libcfs_debug_mark_buffer(const char *text);
-void libcfs_debug_set_level(unsigned int debug_level);
-
/*
* allocate per-cpu-partition data, returned value is an array of pointers,
* variable can be indexed by CPU ID.
diff --git a/drivers/staging/lustre/include/linux/lnet/api.h b/drivers/staging/lustre/include/linux/lnet/api.h
index 9493d5e236c5..75285fde15e8 100644
--- a/drivers/staging/lustre/include/linux/lnet/api.h
+++ b/drivers/staging/lustre/include/linux/lnet/api.h
@@ -161,12 +161,6 @@ int LNetEQAlloc(unsigned int count_in,
int LNetEQFree(lnet_handle_eq_t eventq_in);
-int LNetEQGet(lnet_handle_eq_t eventq_in,
- lnet_event_t *event_out);
-
-int LNetEQWait(lnet_handle_eq_t eventq_in,
- lnet_event_t *event_out);
-
int LNetEQPoll(lnet_handle_eq_t *eventqs_in,
int neq_in,
int timeout_ms,
diff --git a/drivers/staging/lustre/include/linux/lnet/lib-lnet.h b/drivers/staging/lustre/include/linux/lnet/lib-lnet.h
index b61d5045a566..b67a6607bb3b 100644
--- a/drivers/staging/lustre/include/linux/lnet/lib-lnet.h
+++ b/drivers/staging/lustre/include/linux/lnet/lib-lnet.h
@@ -23,7 +23,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012 - 2015, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/include/linux/lnet/lib-types.h b/drivers/staging/lustre/include/linux/lnet/lib-types.h
index d792c4adb0ca..3bb9468e0b9d 100644
--- a/drivers/staging/lustre/include/linux/lnet/lib-types.h
+++ b/drivers/staging/lustre/include/linux/lnet/lib-types.h
@@ -23,7 +23,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012 - 2015, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/include/linux/lnet/nidstr.h b/drivers/staging/lustre/include/linux/lnet/nidstr.h
index 46ad9147ad2a..4fc9ddce829d 100644
--- a/drivers/staging/lustre/include/linux/lnet/nidstr.h
+++ b/drivers/staging/lustre/include/linux/lnet/nidstr.h
@@ -23,7 +23,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011 - 2015, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
#ifndef _LNET_NIDSTRINGS_H
#define _LNET_NIDSTRINGS_H
diff --git a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c
index 7c730e3f7453..72af486b65df 100644
--- a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c
+++ b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -2865,7 +2865,7 @@ static int __init kiblnd_module_init(void)
return 0;
}
-MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_AUTHOR("OpenSFS, Inc. <http://www.lustre.org/>");
MODULE_DESCRIPTION("Kernel OpenIB gen2 LND v2.00");
MODULE_LICENSE("GPL");
diff --git a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.h b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.h
index 263db37de7c8..025faa9f86b3 100644
--- a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.h
+++ b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.h
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c
index 260750354a41..c7b9ccb13f1c 100644
--- a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c
+++ b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.c b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.c
index ecfe73302350..05aa90ea597a 100644
--- a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.c
+++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.c
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -2621,8 +2621,8 @@ ksocknal_enumerate_interfaces(ksock_net_t *net)
net->ksnn_interfaces[j].ksni_ipaddr = ip;
net->ksnn_interfaces[j].ksni_netmask = mask;
- strncpy(&net->ksnn_interfaces[j].ksni_name[0],
- names[i], IFNAMSIZ);
+ strlcpy(net->ksnn_interfaces[j].ksni_name,
+ names[i], sizeof(net->ksnn_interfaces[j].ksni_name));
j++;
}
@@ -2805,8 +2805,9 @@ ksocknal_startup(lnet_ni_t *ni)
goto fail_1;
}
- strncpy(&net->ksnn_interfaces[i].ksni_name[0],
- ni->ni_interfaces[i], IFNAMSIZ);
+ strlcpy(net->ksnn_interfaces[i].ksni_name,
+ ni->ni_interfaces[i],
+ sizeof(net->ksnn_interfaces[i].ksni_name));
}
net->ksnn_ninterfaces = i;
}
@@ -2868,7 +2869,7 @@ ksocknal_module_init(void)
return 0;
}
-MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_AUTHOR("OpenSFS, Inc. <http://www.lustre.org/>");
MODULE_DESCRIPTION("Kernel TCP Socket LND v3.0.0");
MODULE_LICENSE("GPL");
MODULE_VERSION("3.0.0");
diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.h b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.h
index b349847f9cf9..f4fa72550657 100644
--- a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.h
+++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.h
@@ -679,6 +679,9 @@ int ksocknal_lib_recv_kiov(ksock_conn_t *conn);
int ksocknal_lib_get_conn_tunables(ksock_conn_t *conn, int *txmem,
int *rxmem, int *nagle);
+void ksocknal_read_callback(ksock_conn_t *conn);
+void ksocknal_write_callback(ksock_conn_t *conn);
+
int ksocknal_tunables_init(void);
void ksocknal_lib_csum_tx(ksock_tx_t *tx);
diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c
index 679785b0209c..cf8e43bd3c03 100644
--- a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c
+++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib.c
@@ -69,7 +69,7 @@ ksocknal_lib_zc_capable(ksock_conn_t *conn)
/* ZC if the socket supports scatter/gather and doesn't need software
* checksums */
- return ((caps & NETIF_F_SG) != 0 && (caps & NETIF_F_ALL_CSUM) != 0);
+ return ((caps & NETIF_F_SG) != 0 && (caps & NETIF_F_CSUM_MASK) != 0);
}
int
@@ -580,8 +580,6 @@ ksocknal_lib_push_conn(ksock_conn_t *conn)
ksocknal_connsock_decref(conn);
}
-extern void ksocknal_read_callback(ksock_conn_t *conn);
-extern void ksocknal_write_callback(ksock_conn_t *conn);
/*
* socket call back in Linux
*/
diff --git a/drivers/staging/lustre/lnet/lnet/acceptor.c b/drivers/staging/lustre/lnet/lnet/acceptor.c
index 92ca1dd64076..fed57d90028d 100644
--- a/drivers/staging/lustre/lnet/lnet/acceptor.c
+++ b/drivers/staging/lustre/lnet/lnet/acceptor.c
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lnet/lnet/api-ni.c b/drivers/staging/lustre/lnet/lnet/api-ni.c
index 395412639935..362282fa00bf 100644
--- a/drivers/staging/lustre/lnet/lnet/api-ni.c
+++ b/drivers/staging/lustre/lnet/lnet/api-ni.c
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -354,16 +354,6 @@ lnet_counters_reset(void)
lnet_net_unlock(LNET_LOCK_EX);
}
-EXPORT_SYMBOL(lnet_counters_reset);
-
-static __u64
-lnet_create_interface_cookie(void)
-{
- /* NB the interface cookie in wire handles guards against delayed
- * replies and ACKs appearing valid after reboot.
- */
- return ktime_get_ns();
-}
static char *
lnet_res_type2str(int type)
@@ -553,8 +543,11 @@ lnet_prepare(lnet_pid_t requested_pid)
rc = lnet_create_remote_nets_table();
if (rc != 0)
goto failed;
-
- the_lnet.ln_interface_cookie = lnet_create_interface_cookie();
+ /*
+ * NB the interface cookie in wire handles guards against delayed
+ * replies and ACKs appearing valid after reboot.
+ */
+ the_lnet.ln_interface_cookie = ktime_get_ns();
the_lnet.ln_counters = cfs_percpt_alloc(lnet_cpt_table(),
sizeof(lnet_counters_t));
@@ -1159,7 +1152,6 @@ lnet_init(void)
lnet_register_lnd(&the_lolnd);
return 0;
}
-EXPORT_SYMBOL(lnet_init);
/**
* Finalize LNet library.
@@ -1183,7 +1175,6 @@ lnet_fini(void)
the_lnet.ln_init = 0;
}
-EXPORT_SYMBOL(lnet_fini);
/**
* Set LNet PID and start LNet interfaces, routing, and forwarding.
diff --git a/drivers/staging/lustre/lnet/lnet/config.c b/drivers/staging/lustre/lnet/lnet/config.c
index 1b3bc8386524..284a3c271bc6 100644
--- a/drivers/staging/lustre/lnet/lnet/config.c
+++ b/drivers/staging/lustre/lnet/lnet/config.c
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -650,8 +650,8 @@ lnet_parse_route(char *str, int *im_a_router)
INIT_LIST_HEAD(&nets);
/* save a copy of the string for error messages */
- strncpy(cmd, str, sizeof(cmd) - 1);
- cmd[sizeof(cmd) - 1] = 0;
+ strncpy(cmd, str, sizeof(cmd));
+ cmd[sizeof(cmd) - 1] = '\0';
sep = str;
for (;;) {
@@ -972,11 +972,13 @@ lnet_splitnets(char *source, struct list_head *nets)
return 0;
offset += (int)(sep - tb->ltb_text);
- tb2 = lnet_new_text_buf(strlen(sep));
+ len = strlen(sep);
+ tb2 = lnet_new_text_buf(len);
if (tb2 == NULL)
return -ENOMEM;
- strcpy(tb2->ltb_text, sep);
+ strncpy(tb2->ltb_text, sep, len);
+ tb2->ltb_text[len] = '\0';
list_add_tail(&tb2->ltb_list, nets);
tb = tb2;
@@ -1021,8 +1023,8 @@ lnet_match_networks(char **networksp, char *ip2nets, __u32 *ipaddrs, int nip)
tb = list_entry(raw_entries.next, struct lnet_text_buf_t,
ltb_list);
- strncpy(source, tb->ltb_text, sizeof(source)-1);
- source[sizeof(source)-1] = 0;
+ strncpy(source, tb->ltb_text, sizeof(source));
+ source[sizeof(source)-1] = '\0';
/* replace ltb_text with the network(s) add on match */
rc = lnet_match_network_tokens(tb->ltb_text, ipaddrs, nip);
@@ -1103,12 +1105,6 @@ lnet_match_networks(char **networksp, char *ip2nets, __u32 *ipaddrs, int nip)
return count;
}
-static void
-lnet_ipaddr_free_enumeration(__u32 *ipaddrs, int nip)
-{
- LIBCFS_FREE(ipaddrs, nip * sizeof(*ipaddrs));
-}
-
static int
lnet_ipaddr_enumerate(__u32 **ipaddrsp)
{
@@ -1169,7 +1165,7 @@ lnet_ipaddr_enumerate(__u32 **ipaddrsp)
rc = nip;
}
}
- lnet_ipaddr_free_enumeration(ipaddrs, nif);
+ LIBCFS_FREE(ipaddrs, nip * sizeof(*ipaddrs));
}
return nip;
}
@@ -1195,7 +1191,7 @@ lnet_parse_ip2nets(char **networksp, char *ip2nets)
}
rc = lnet_match_networks(networksp, ip2nets, ipaddrs, nip);
- lnet_ipaddr_free_enumeration(ipaddrs, nip);
+ LIBCFS_FREE(ipaddrs, nip * sizeof(*ipaddrs));
if (rc < 0) {
LCONSOLE_ERROR_MSG(0x119, "Error %d parsing ip2nets\n", rc);
diff --git a/drivers/staging/lustre/lnet/lnet/lib-eq.c b/drivers/staging/lustre/lnet/lnet/lib-eq.c
index 60889ebd2f2b..64f94a690081 100644
--- a/drivers/staging/lustre/lnet/lnet/lib-eq.c
+++ b/drivers/staging/lustre/lnet/lnet/lib-eq.c
@@ -282,15 +282,6 @@ lnet_eq_dequeue_event(lnet_eq_t *eq, lnet_event_t *ev)
* at least one event between this event and the last event obtained from the
* EQ has been dropped due to limited space in the EQ.
*/
-int
-LNetEQGet(lnet_handle_eq_t eventq, lnet_event_t *event)
-{
- int which;
-
- return LNetEQPoll(&eventq, 1, 0,
- event, &which);
-}
-EXPORT_SYMBOL(LNetEQGet);
/**
* Block the calling process until there is an event in the EQ.
@@ -308,15 +299,6 @@ EXPORT_SYMBOL(LNetEQGet);
* at least one event between this event and the last event obtained from the
* EQ has been dropped due to limited space in the EQ.
*/
-int
-LNetEQWait(lnet_handle_eq_t eventq, lnet_event_t *event)
-{
- int which;
-
- return LNetEQPoll(&eventq, 1, LNET_TIME_FOREVER,
- event, &which);
-}
-EXPORT_SYMBOL(LNetEQWait);
static int
lnet_eq_wait_locked(int *timeout_ms)
diff --git a/drivers/staging/lustre/lnet/lnet/lib-move.c b/drivers/staging/lustre/lnet/lnet/lib-move.c
index 5631f60a39bc..fb8f7be043ec 100644
--- a/drivers/staging/lustre/lnet/lnet/lib-move.c
+++ b/drivers/staging/lustre/lnet/lnet/lib-move.c
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -1645,7 +1645,6 @@ lnet_msgtyp2str(int type)
return "<UNKNOWN>";
}
}
-EXPORT_SYMBOL(lnet_msgtyp2str);
void
lnet_print_hdr(lnet_hdr_t *hdr)
diff --git a/drivers/staging/lustre/lnet/lnet/lib-ptl.c b/drivers/staging/lustre/lnet/lnet/lib-ptl.c
index b4f573ab62cc..bd7b071b2873 100644
--- a/drivers/staging/lustre/lnet/lnet/lib-ptl.c
+++ b/drivers/staging/lustre/lnet/lnet/lib-ptl.c
@@ -21,7 +21,7 @@
* GPL HEADER END
*/
/*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lnet/lnet/lib-socket.c b/drivers/staging/lustre/lnet/lnet/lib-socket.c
index 6f7ef4c737cd..589ecc84d1b8 100644
--- a/drivers/staging/lustre/lnet/lnet/lib-socket.c
+++ b/drivers/staging/lustre/lnet/lnet/lib-socket.c
@@ -23,7 +23,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, 2015 Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lnet/lnet/module.c b/drivers/staging/lustre/lnet/lnet/module.c
index 576201a8390c..c93c00752a4c 100644
--- a/drivers/staging/lustre/lnet/lnet/module.c
+++ b/drivers/staging/lustre/lnet/lnet/module.c
@@ -27,7 +27,7 @@
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -146,7 +146,7 @@ fini_lnet(void)
lnet_fini();
}
-MODULE_AUTHOR("Peter J. Braam <braam@clusterfs.com>");
+MODULE_AUTHOR("OpenSFS, Inc. <http://www.lustre.org/>");
MODULE_DESCRIPTION("LNet v3.1");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.0.0");
diff --git a/drivers/staging/lustre/lnet/lnet/router.c b/drivers/staging/lustre/lnet/lnet/router.c
index 4ea651c6db3a..f5faa414d250 100644
--- a/drivers/staging/lustre/lnet/lnet/router.c
+++ b/drivers/staging/lustre/lnet/lnet/router.c
@@ -1,7 +1,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*
* This file is part of Portals
* http://sourceforge.net/projects/sandiaportals/
diff --git a/drivers/staging/lustre/lnet/selftest/brw_test.c b/drivers/staging/lustre/lnet/selftest/brw_test.c
index 0605c651f797..1f04cc1fc31c 100644
--- a/drivers/staging/lustre/lnet/selftest/brw_test.c
+++ b/drivers/staging/lustre/lnet/selftest/brw_test.c
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -358,7 +358,7 @@ out:
}
static void
-brw_server_rpc_done(srpc_server_rpc_t *rpc)
+brw_server_rpc_done(struct srpc_server_rpc *rpc)
{
srpc_bulk_t *blk = rpc->srpc_bulk;
@@ -378,7 +378,7 @@ brw_server_rpc_done(srpc_server_rpc_t *rpc)
}
static int
-brw_bulk_ready(srpc_server_rpc_t *rpc, int status)
+brw_bulk_ready(struct srpc_server_rpc *rpc, int status)
{
__u64 magic = BRW_MAGIC;
srpc_brw_reply_t *reply = &rpc->srpc_replymsg.msg_body.brw_reply;
diff --git a/drivers/staging/lustre/lnet/selftest/conctl.c b/drivers/staging/lustre/lnet/selftest/conctl.c
index 556c837cf62c..a534665403e5 100644
--- a/drivers/staging/lustre/lnet/selftest/conctl.c
+++ b/drivers/staging/lustre/lnet/selftest/conctl.c
@@ -925,5 +925,3 @@ out:
return rc;
}
-
-EXPORT_SYMBOL(lstcon_ioctl_entry);
diff --git a/drivers/staging/lustre/lnet/selftest/conrpc.c b/drivers/staging/lustre/lnet/selftest/conrpc.c
index 64a0335934f3..1066c70434b1 100644
--- a/drivers/staging/lustre/lnet/selftest/conrpc.c
+++ b/drivers/staging/lustre/lnet/selftest/conrpc.c
@@ -612,8 +612,8 @@ lstcon_sesrpc_prep(lstcon_node_t *nd, int transop,
msrq = &(*crpc)->crp_rpc->crpc_reqstmsg.msg_body.mksn_reqst;
msrq->mksn_sid = console_session.ses_id;
msrq->mksn_force = console_session.ses_force;
- strncpy(msrq->mksn_name, console_session.ses_name,
- strlen(console_session.ses_name));
+ strlcpy(msrq->mksn_name, console_session.ses_name,
+ sizeof(msrq->mksn_name));
break;
case LST_TRANS_SESEND:
diff --git a/drivers/staging/lustre/lnet/selftest/console.c b/drivers/staging/lustre/lnet/selftest/console.c
index d315dd44ae3b..5619fc430e8d 100644
--- a/drivers/staging/lustre/lnet/selftest/console.c
+++ b/drivers/staging/lustre/lnet/selftest/console.c
@@ -277,12 +277,6 @@ lstcon_group_find(const char *name, lstcon_group_t **grpp)
return -ENOENT;
}
-static void
-lstcon_group_put(lstcon_group_t *grp)
-{
- lstcon_group_decref(grp);
-}
-
static int
lstcon_group_ndlink_find(lstcon_group_t *grp, lnet_process_id_t id,
lstcon_ndlink_t **ndlpp, int create)
@@ -324,8 +318,6 @@ lstcon_group_ndlink_move(lstcon_group_t *old,
list_add_tail(&ndl->ndl_hlink, &new->grp_ndl_hash[idx]);
list_add_tail(&ndl->ndl_link, &new->grp_ndl_list);
new->grp_nnode++;
-
- return;
}
static void
@@ -436,7 +428,7 @@ lstcon_group_nodes_add(lstcon_group_t *grp,
}
if (rc != 0) {
- lstcon_group_put(tmp);
+ lstcon_group_decref(tmp);
return rc;
}
@@ -445,7 +437,7 @@ lstcon_group_nodes_add(lstcon_group_t *grp,
tmp, lstcon_sesrpc_condition, &trans);
if (rc != 0) {
CERROR("Can't create transaction: %d\n", rc);
- lstcon_group_put(tmp);
+ lstcon_group_decref(tmp);
return rc;
}
@@ -460,7 +452,7 @@ lstcon_group_nodes_add(lstcon_group_t *grp,
lstcon_rpc_trans_destroy(trans);
lstcon_group_move(tmp, grp);
- lstcon_group_put(tmp);
+ lstcon_group_decref(tmp);
return rc;
}
@@ -510,12 +502,12 @@ lstcon_group_nodes_remove(lstcon_group_t *grp,
lstcon_rpc_trans_destroy(trans);
/* release nodes anyway, because we can't rollback status */
- lstcon_group_put(tmp);
+ lstcon_group_decref(tmp);
return rc;
error:
lstcon_group_move(tmp, grp);
- lstcon_group_put(tmp);
+ lstcon_group_decref(tmp);
return rc;
}
@@ -529,7 +521,7 @@ lstcon_group_add(char *name)
rc = (lstcon_group_find(name, &grp) == 0) ? -EEXIST : 0;
if (rc != 0) {
/* find a group with same name */
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
return rc;
}
@@ -563,14 +555,14 @@ lstcon_nodes_add(char *name, int count, lnet_process_id_t *ids_up,
if (grp->grp_ref > 2) {
/* referred by other threads or test */
CDEBUG(D_NET, "Group %s is busy\n", name);
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
return -EBUSY;
}
rc = lstcon_group_nodes_add(grp, count, ids_up, featp, result_up);
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
return rc;
}
@@ -591,7 +583,7 @@ lstcon_group_del(char *name)
if (grp->grp_ref > 2) {
/* referred by others threads or test */
CDEBUG(D_NET, "Group %s is busy\n", name);
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
return -EBUSY;
}
@@ -600,7 +592,7 @@ lstcon_group_del(char *name)
grp, lstcon_sesrpc_condition, &trans);
if (rc != 0) {
CERROR("Can't create transaction: %d\n", rc);
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
return rc;
}
@@ -608,10 +600,10 @@ lstcon_group_del(char *name)
lstcon_rpc_trans_destroy(trans);
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
/* -ref for session, it's destroyed,
* status can't be rolled back, destroy group anyway */
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
return rc;
}
@@ -631,7 +623,7 @@ lstcon_group_clean(char *name, int args)
if (grp->grp_ref > 2) {
/* referred by test */
CDEBUG(D_NET, "Group %s is busy\n", name);
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
return -EBUSY;
}
@@ -640,10 +632,10 @@ lstcon_group_clean(char *name, int args)
lstcon_group_drain(grp, args);
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
/* release empty group */
if (list_empty(&grp->grp_ndl_list))
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
return 0;
}
@@ -664,16 +656,16 @@ lstcon_nodes_remove(char *name, int count,
if (grp->grp_ref > 2) {
/* referred by test */
CDEBUG(D_NET, "Group %s is busy\n", name);
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
return -EBUSY;
}
rc = lstcon_group_nodes_remove(grp, count, ids_up, result_up);
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
/* release empty group */
if (list_empty(&grp->grp_ndl_list))
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
return rc;
}
@@ -694,7 +686,7 @@ lstcon_group_refresh(char *name, struct list_head *result_up)
if (grp->grp_ref > 2) {
/* referred by test */
CDEBUG(D_NET, "Group %s is busy\n", name);
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
return -EBUSY;
}
@@ -705,7 +697,7 @@ lstcon_group_refresh(char *name, struct list_head *result_up)
if (rc != 0) {
/* local error, return */
CDEBUG(D_NET, "Can't create transaction: %d\n", rc);
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
return rc;
}
@@ -715,7 +707,7 @@ lstcon_group_refresh(char *name, struct list_head *result_up)
lstcon_rpc_trans_destroy(trans);
/* -ref for me */
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
return rc;
}
@@ -797,7 +789,7 @@ lstcon_group_info(char *name, lstcon_ndlist_ent_t *gents_p,
/* verbose query */
rc = lstcon_nodes_getent(&grp->grp_ndl_list,
index_p, count_p, dents_up);
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
return rc;
}
@@ -806,7 +798,7 @@ lstcon_group_info(char *name, lstcon_ndlist_ent_t *gents_p,
LIBCFS_ALLOC(gentp, sizeof(lstcon_ndlist_ent_t));
if (gentp == NULL) {
CERROR("Can't allocate ndlist_ent\n");
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
return -ENOMEM;
}
@@ -819,7 +811,7 @@ lstcon_group_info(char *name, lstcon_ndlist_ent_t *gents_p,
LIBCFS_FREE(gentp, sizeof(lstcon_ndlist_ent_t));
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
return 0;
}
@@ -1096,8 +1088,8 @@ lstcon_batch_destroy(lstcon_batch_t *bat)
list_del(&test->tes_link);
- lstcon_group_put(test->tes_src_grp);
- lstcon_group_put(test->tes_dst_grp);
+ lstcon_group_decref(test->tes_src_grp);
+ lstcon_group_decref(test->tes_dst_grp);
LIBCFS_FREE(test, offsetof(lstcon_test_t,
tes_param[test->tes_paramlen]));
@@ -1352,10 +1344,10 @@ out:
LIBCFS_FREE(test, offsetof(lstcon_test_t, tes_param[paramlen]));
if (dst_grp != NULL)
- lstcon_group_put(dst_grp);
+ lstcon_group_decref(dst_grp);
if (src_grp != NULL)
- lstcon_group_put(src_grp);
+ lstcon_group_decref(src_grp);
return rc;
}
@@ -1518,7 +1510,7 @@ lstcon_group_stat(char *grp_name, int timeout, struct list_head *result_up)
rc = lstcon_ndlist_stat(&grp->grp_ndl_list, timeout, result_up);
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
return rc;
}
@@ -1556,13 +1548,13 @@ lstcon_nodes_stat(int count, lnet_process_id_t *ids_up,
}
if (rc != 0) {
- lstcon_group_put(tmp);
+ lstcon_group_decref(tmp);
return rc;
}
rc = lstcon_ndlist_stat(&tmp->grp_ndl_list, timeout, result_up);
- lstcon_group_put(tmp);
+ lstcon_group_decref(tmp);
return rc;
}
@@ -1629,7 +1621,7 @@ lstcon_group_debug(int timeout, char *name,
rc = lstcon_debug_ndlist(&grp->grp_ndl_list, NULL,
timeout, result_up);
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
return rc;
}
@@ -1666,14 +1658,14 @@ lstcon_nodes_debug(int timeout,
}
if (rc != 0) {
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
return rc;
}
rc = lstcon_debug_ndlist(&grp->grp_ndl_list, NULL,
timeout, result_up);
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
return rc;
}
@@ -1739,7 +1731,8 @@ lstcon_session_new(char *name, int key, unsigned feats,
console_session.ses_feats_updated = 0;
console_session.ses_timeout = (timeout <= 0) ?
LST_CONSOLE_TIMEOUT : timeout;
- strcpy(console_session.ses_name, name);
+ strlcpy(console_session.ses_name, name,
+ sizeof(console_session.ses_name));
rc = lstcon_batch_add(LST_DEFAULT_BATCH);
if (rc != 0)
@@ -1847,7 +1840,7 @@ lstcon_session_end(void)
lstcon_group_t, grp_link);
LASSERT(grp->grp_ref == 1);
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
}
/* all nodes should be released */
@@ -1891,7 +1884,7 @@ lstcon_session_feats_check(unsigned feats)
}
static int
-lstcon_acceptor_handle(srpc_server_rpc_t *rpc)
+lstcon_acceptor_handle(struct srpc_server_rpc *rpc)
{
srpc_msg_t *rep = &rpc->srpc_replymsg;
srpc_msg_t *req = &rpc->srpc_reqstbuf->buf_msg;
@@ -1959,14 +1952,15 @@ lstcon_acceptor_handle(srpc_server_rpc_t *rpc)
if (grp->grp_userland == 0)
grp->grp_userland = 1;
- strcpy(jrep->join_session, console_session.ses_name);
+ strlcpy(jrep->join_session, console_session.ses_name,
+ sizeof(jrep->join_session));
jrep->join_timeout = console_session.ses_timeout;
jrep->join_status = 0;
out:
rep->msg_ses_feats = console_session.ses_features;
if (grp != NULL)
- lstcon_group_put(grp);
+ lstcon_group_decref(grp);
mutex_unlock(&console_session.ses_mutex);
diff --git a/drivers/staging/lustre/lnet/selftest/framework.c b/drivers/staging/lustre/lnet/selftest/framework.c
index f18e50036809..1a2da7430190 100644
--- a/drivers/staging/lustre/lnet/selftest/framework.c
+++ b/drivers/staging/lustre/lnet/selftest/framework.c
@@ -111,7 +111,7 @@ static struct smoketest_framework {
spinlock_t fw_lock; /* serialise */
sfw_session_t *fw_session; /* _the_ session */
int fw_shuttingdown; /* shutdown in progress */
- srpc_server_rpc_t *fw_active_srpc; /* running RPC */
+ struct srpc_server_rpc *fw_active_srpc;/* running RPC */
} sfw_data;
/* forward ref's */
@@ -722,7 +722,7 @@ sfw_unpack_addtest_req(srpc_msg_t *msg)
}
static int
-sfw_add_test_instance(sfw_batch_t *tsb, srpc_server_rpc_t *rpc)
+sfw_add_test_instance(sfw_batch_t *tsb, struct srpc_server_rpc *rpc)
{
srpc_msg_t *msg = &rpc->srpc_reqstbuf->buf_msg;
srpc_test_reqst_t *req = &msg->msg_body.tes_reqst;
@@ -1091,7 +1091,7 @@ sfw_query_batch(sfw_batch_t *tsb, int testidx, srpc_batch_reply_t *reply)
}
void
-sfw_free_pages(srpc_server_rpc_t *rpc)
+sfw_free_pages(struct srpc_server_rpc *rpc)
{
srpc_free_bulk(rpc->srpc_bulk);
rpc->srpc_bulk = NULL;
@@ -1112,7 +1112,7 @@ sfw_alloc_pages(struct srpc_server_rpc *rpc, int cpt, int npages, int len,
}
static int
-sfw_add_test(srpc_server_rpc_t *rpc)
+sfw_add_test(struct srpc_server_rpc *rpc)
{
sfw_session_t *sn = sfw_data.fw_session;
srpc_test_reply_t *reply = &rpc->srpc_replymsg.msg_body.tes_reply;
diff --git a/drivers/staging/lustre/lnet/selftest/rpc.c b/drivers/staging/lustre/lnet/selftest/rpc.c
index 7005002c15da..2acf6ec717be 100644
--- a/drivers/staging/lustre/lnet/selftest/rpc.c
+++ b/drivers/staging/lustre/lnet/selftest/rpc.c
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -445,15 +445,6 @@ srpc_post_active_rdma(int portal, __u64 matchbits, void *buf, int len,
}
static int
-srpc_post_active_rqtbuf(lnet_process_id_t peer, int service, void *buf,
- int len, lnet_handle_md_t *mdh, srpc_event_t *ev)
-{
- return srpc_post_active_rdma(srpc_serv_portal(service), service,
- buf, len, LNET_MD_OP_PUT, peer,
- LNET_NID_ANY, mdh, ev);
-}
-
-static int
srpc_post_passive_rqtbuf(int service, int local, void *buf, int len,
lnet_handle_md_t *mdh, srpc_event_t *ev)
{
@@ -798,9 +789,11 @@ srpc_send_request(srpc_client_rpc_t *rpc)
ev->ev_data = rpc;
ev->ev_type = SRPC_REQUEST_SENT;
- rc = srpc_post_active_rqtbuf(rpc->crpc_dest, rpc->crpc_service,
- &rpc->crpc_reqstmsg, sizeof(srpc_msg_t),
- &rpc->crpc_reqstmdh, ev);
+ rc = srpc_post_active_rdma(srpc_serv_portal(rpc->crpc_service),
+ rpc->crpc_service, &rpc->crpc_reqstmsg,
+ sizeof(srpc_msg_t), LNET_MD_OP_PUT,
+ rpc->crpc_dest, LNET_NID_ANY,
+ &rpc->crpc_reqstmdh, ev);
if (rc != 0) {
LASSERT(rc == -ENOMEM);
ev->ev_fired = 1; /* no more event expected */
@@ -866,7 +859,7 @@ srpc_prepare_bulk(srpc_client_rpc_t *rpc)
}
static int
-srpc_do_bulk(srpc_server_rpc_t *rpc)
+srpc_do_bulk(struct srpc_server_rpc *rpc)
{
srpc_event_t *ev = &rpc->srpc_ev;
srpc_bulk_t *bk = rpc->srpc_bulk;
@@ -894,7 +887,7 @@ srpc_do_bulk(srpc_server_rpc_t *rpc)
/* only called from srpc_handle_rpc */
static void
-srpc_server_rpc_done(srpc_server_rpc_t *rpc, int status)
+srpc_server_rpc_done(struct srpc_server_rpc *rpc, int status)
{
struct srpc_service_cd *scd = rpc->srpc_scd;
struct srpc_service *sv = scd->scd_svc;
@@ -1404,7 +1397,7 @@ srpc_lnet_ev_handler(lnet_event_t *ev)
struct srpc_service_cd *scd;
srpc_event_t *rpcev = ev->md.user_ptr;
srpc_client_rpc_t *crpc;
- srpc_server_rpc_t *srpc;
+ struct srpc_server_rpc *srpc;
srpc_buffer_t *buffer;
srpc_service_t *sv;
srpc_msg_t *msg;
diff --git a/drivers/staging/lustre/lnet/selftest/selftest.h b/drivers/staging/lustre/lnet/selftest/selftest.h
index 8a77d3fdfa54..870498339538 100644
--- a/drivers/staging/lustre/lnet/selftest/selftest.h
+++ b/drivers/staging/lustre/lnet/selftest/selftest.h
@@ -182,7 +182,7 @@ typedef struct swi_workitem {
} swi_workitem_t;
/* server-side state of a RPC */
-typedef struct srpc_server_rpc {
+struct srpc_server_rpc {
/* chain on srpc_service::*_rpcq */
struct list_head srpc_list;
struct srpc_service_cd *srpc_scd;
@@ -198,7 +198,7 @@ typedef struct srpc_server_rpc {
unsigned int srpc_aborted; /* being given up */
int srpc_status;
void (*srpc_done)(struct srpc_server_rpc *);
-} srpc_server_rpc_t;
+};
/* client-side state of a RPC */
typedef struct srpc_client_rpc {
@@ -318,8 +318,8 @@ typedef struct srpc_service {
* - sv_handler: process incoming RPC request
* - sv_bulk_ready: notify bulk data
*/
- int (*sv_handler) (srpc_server_rpc_t *);
- int (*sv_bulk_ready) (srpc_server_rpc_t *, int);
+ int (*sv_handler)(struct srpc_server_rpc *);
+ int (*sv_bulk_ready)(struct srpc_server_rpc *, int);
} srpc_service_t;
typedef struct {
@@ -423,9 +423,9 @@ void sfw_abort_rpc(srpc_client_rpc_t *rpc);
void sfw_post_rpc(srpc_client_rpc_t *rpc);
void sfw_client_rpc_done(srpc_client_rpc_t *rpc);
void sfw_unpack_message(srpc_msg_t *msg);
-void sfw_free_pages(srpc_server_rpc_t *rpc);
+void sfw_free_pages(struct srpc_server_rpc *rpc);
void sfw_add_bulk_page(srpc_bulk_t *bk, struct page *pg, int i);
-int sfw_alloc_pages(srpc_server_rpc_t *rpc, int cpt, int npages, int len,
+int sfw_alloc_pages(struct srpc_server_rpc *rpc, int cpt, int npages, int len,
int sink);
int sfw_make_session (srpc_mksn_reqst_t *request, srpc_mksn_reply_t *reply);
@@ -440,7 +440,7 @@ void srpc_free_bulk(srpc_bulk_t *bk);
srpc_bulk_t *srpc_alloc_bulk(int cpt, unsigned bulk_npg, unsigned bulk_len,
int sink);
int srpc_send_rpc(swi_workitem_t *wi);
-int srpc_send_reply(srpc_server_rpc_t *rpc);
+int srpc_send_reply(struct srpc_server_rpc *rpc);
int srpc_add_service(srpc_service_t *sv);
int srpc_remove_service(srpc_service_t *sv);
void srpc_shutdown_service(srpc_service_t *sv);
@@ -585,7 +585,7 @@ swi_state2str (int state)
do { \
int __I = 2; \
while (!(cond)) { \
- CDEBUG(IS_PO2(++__I) ? D_WARNING : D_NET, \
+ CDEBUG(is_power_of_2(++__I) ? D_WARNING : D_NET, \
fmt, ## __VA_ARGS__); \
spin_unlock(&(lock)); \
\
diff --git a/drivers/staging/lustre/lustre/fid/fid_internal.h b/drivers/staging/lustre/lustre/fid/fid_internal.h
index 84daee1154dc..b79a813977cf 100644
--- a/drivers/staging/lustre/lustre/fid/fid_internal.h
+++ b/drivers/staging/lustre/lustre/fid/fid_internal.h
@@ -44,8 +44,6 @@
#include "../../include/linux/libcfs/libcfs.h"
/* Functions used internally in module. */
-int seq_client_alloc_super(struct lu_client_seq *seq,
- const struct lu_env *env);
extern struct lprocfs_vars seq_client_debugfs_list[];
diff --git a/drivers/staging/lustre/lustre/fid/fid_request.c b/drivers/staging/lustre/lustre/fid/fid_request.c
index 7c45e7479087..ff8f38dc10ce 100644
--- a/drivers/staging/lustre/lustre/fid/fid_request.c
+++ b/drivers/staging/lustre/lustre/fid/fid_request.c
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2013, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -142,27 +142,6 @@ out_req:
return rc;
}
-/* Request sequence-controller node to allocate new super-sequence. */
-int seq_client_alloc_super(struct lu_client_seq *seq,
- const struct lu_env *env)
-{
- int rc;
-
- mutex_lock(&seq->lcs_mutex);
-
- /* Check whether the connection to seq controller has been
- * setup (lcs_exp != NULL) */
- if (!seq->lcs_exp) {
- mutex_unlock(&seq->lcs_mutex);
- return -EINPROGRESS;
- }
-
- rc = seq_client_rpc(seq, &seq->lcs_space,
- SEQ_ALLOC_SUPER, "super");
- mutex_unlock(&seq->lcs_mutex);
- return rc;
-}
-
/* Request sequence-controller node to allocate new meta-sequence. */
static int seq_client_alloc_meta(const struct lu_env *env,
struct lu_client_seq *seq)
@@ -483,7 +462,7 @@ static void __exit fid_mod_exit(void)
ldebugfs_remove(&seq_debugfs_dir);
}
-MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_AUTHOR("OpenSFS, Inc. <http://www.lustre.org/>");
MODULE_DESCRIPTION("Lustre FID Module");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.1.0");
diff --git a/drivers/staging/lustre/lustre/fid/lproc_fid.c b/drivers/staging/lustre/lustre/fid/lproc_fid.c
index ce90c1c54a63..39f2aa32e984 100644
--- a/drivers/staging/lustre/lustre/fid/lproc_fid.c
+++ b/drivers/staging/lustre/lustre/fid/lproc_fid.c
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/fld/fld_cache.c b/drivers/staging/lustre/lustre/fld/fld_cache.c
index 446917484637..d9459e58e2ce 100644
--- a/drivers/staging/lustre/lustre/fld/fld_cache.c
+++ b/drivers/staging/lustre/lustre/fld/fld_cache.c
@@ -121,8 +121,8 @@ void fld_cache_fini(struct fld_cache *cache)
/**
* delete given node from list.
*/
-void fld_cache_entry_delete(struct fld_cache *cache,
- struct fld_cache_entry *node)
+static void fld_cache_entry_delete(struct fld_cache *cache,
+ struct fld_cache_entry *node)
{
list_del(&node->fce_list);
list_del(&node->fce_lru);
@@ -227,7 +227,6 @@ static int fld_cache_shrink(struct fld_cache *cache)
while (cache->fci_cache_count + cache->fci_threshold >
cache->fci_cache_size && curr != &cache->fci_lru) {
-
flde = list_entry(curr, struct fld_cache_entry, fce_lru);
curr = curr->prev;
fld_cache_entry_delete(cache, flde);
@@ -377,8 +376,8 @@ struct fld_cache_entry
* This function handles all cases of merging and breaking up of
* ranges.
*/
-int fld_cache_insert_nolock(struct fld_cache *cache,
- struct fld_cache_entry *f_new)
+static int fld_cache_insert_nolock(struct fld_cache *cache,
+ struct fld_cache_entry *f_new)
{
struct fld_cache_entry *f_curr;
struct fld_cache_entry *n;
@@ -444,36 +443,10 @@ int fld_cache_insert(struct fld_cache *cache,
return rc;
}
-void fld_cache_delete_nolock(struct fld_cache *cache,
- const struct lu_seq_range *range)
-{
- struct fld_cache_entry *flde;
- struct fld_cache_entry *tmp;
- struct list_head *head;
-
- head = &cache->fci_entries_head;
- list_for_each_entry_safe(flde, tmp, head, fce_list) {
- /* add list if next is end of list */
- if (range->lsr_start == flde->fce_range.lsr_start ||
- (range->lsr_end == flde->fce_range.lsr_end &&
- range->lsr_flags == flde->fce_range.lsr_flags)) {
- fld_cache_entry_delete(cache, flde);
- break;
- }
- }
-}
-
/**
* Delete FLD entry in FLD cache.
*
*/
-void fld_cache_delete(struct fld_cache *cache,
- const struct lu_seq_range *range)
-{
- write_lock(&cache->fci_lock);
- fld_cache_delete_nolock(cache, range);
- write_unlock(&cache->fci_lock);
-}
struct fld_cache_entry
*fld_cache_entry_lookup_nolock(struct fld_cache *cache,
diff --git a/drivers/staging/lustre/lustre/fld/fld_internal.h b/drivers/staging/lustre/lustre/fld/fld_internal.h
index fbb232de6c74..12eb1647b4bf 100644
--- a/drivers/staging/lustre/lustre/fld/fld_internal.h
+++ b/drivers/staging/lustre/lustre/fld/fld_internal.h
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, 2013, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -156,20 +156,11 @@ int fld_cache_insert(struct fld_cache *cache,
struct fld_cache_entry
*fld_cache_entry_create(const struct lu_seq_range *range);
-int fld_cache_insert_nolock(struct fld_cache *cache,
- struct fld_cache_entry *f_new);
-void fld_cache_delete(struct fld_cache *cache,
- const struct lu_seq_range *range);
-void fld_cache_delete_nolock(struct fld_cache *cache,
- const struct lu_seq_range *range);
int fld_cache_lookup(struct fld_cache *cache,
const u64 seq, struct lu_seq_range *range);
struct fld_cache_entry*
fld_cache_entry_lookup(struct fld_cache *cache, struct lu_seq_range *range);
-void fld_cache_entry_delete(struct fld_cache *cache,
- struct fld_cache_entry *node);
-void fld_dump_cache_entries(struct fld_cache *cache);
struct fld_cache_entry
*fld_cache_entry_lookup_nolock(struct fld_cache *cache,
diff --git a/drivers/staging/lustre/lustre/fld/fld_request.c b/drivers/staging/lustre/lustre/fld/fld_request.c
index 3fd91bc77da5..d92c01b74865 100644
--- a/drivers/staging/lustre/lustre/fld/fld_request.c
+++ b/drivers/staging/lustre/lustre/fld/fld_request.c
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2013, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -501,7 +501,7 @@ static void __exit fld_mod_exit(void)
ldebugfs_remove(&fld_debugfs_dir);
}
-MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_AUTHOR("OpenSFS, Inc. <http://www.lustre.org/>");
MODULE_DESCRIPTION("Lustre FLD");
MODULE_LICENSE("GPL");
diff --git a/drivers/staging/lustre/lustre/fld/lproc_fld.c b/drivers/staging/lustre/lustre/fld/lproc_fld.c
index 603f56e6095b..41ceaa8198a7 100644
--- a/drivers/staging/lustre/lustre/fld/lproc_fld.c
+++ b/drivers/staging/lustre/lustre/fld/lproc_fld.c
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, 2013, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/include/cl_object.h b/drivers/staging/lustre/lustre/include/cl_object.h
index 73564f8e3884..bd7acc2a1219 100644
--- a/drivers/staging/lustre/lustre/include/cl_object.h
+++ b/drivers/staging/lustre/lustre/include/cl_object.h
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -3127,7 +3127,6 @@ void cl_page_list_disown (const struct lu_env *env,
struct cl_io *io, struct cl_page_list *plist);
void cl_2queue_init (struct cl_2queue *queue);
-void cl_2queue_add (struct cl_2queue *queue, struct cl_page *page);
void cl_2queue_disown (const struct lu_env *env,
struct cl_io *io, struct cl_2queue *queue);
void cl_2queue_discard (const struct lu_env *env,
diff --git a/drivers/staging/lustre/lustre/include/lprocfs_status.h b/drivers/staging/lustre/lustre/include/lprocfs_status.h
index 9e654b218ca3..0ac8e0edcc48 100644
--- a/drivers/staging/lustre/lustre/include/lprocfs_status.h
+++ b/drivers/staging/lustre/lustre/include/lprocfs_status.h
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -624,9 +624,6 @@ void lprocfs_stats_collect(struct lprocfs_stats *stats, int idx,
int lprocfs_single_release(struct inode *, struct file *);
int lprocfs_seq_release(struct inode *, struct file *);
-#define LPROCFS_CLIMP_EXIT(obd) \
- up_read(&(obd)->u.cli.cl_sem)
-
/* write the name##_seq_show function, call LPROC_SEQ_FOPS_RO for read-only
proc entries; otherwise, you will define name##_seq_write function also for
a read-write proc entry, and then call LPROC_SEQ_SEQ instead. Finally,
diff --git a/drivers/staging/lustre/lustre/include/lu_object.h b/drivers/staging/lustre/lustre/include/lu_object.h
index fa78689748a9..1d79341a495d 100644
--- a/drivers/staging/lustre/lustre/include/lu_object.h
+++ b/drivers/staging/lustre/lustre/include/lu_object.h
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/include/lustre/ll_fiemap.h b/drivers/staging/lustre/lustre/include/lustre/ll_fiemap.h
index 06ce8c9ae9ad..09088f40ba88 100644
--- a/drivers/staging/lustre/lustre/include/lustre/ll_fiemap.h
+++ b/drivers/staging/lustre/lustre/include/lustre/ll_fiemap.h
@@ -26,6 +26,8 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright (c) 2014, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/include/lustre/lustre_idl.h b/drivers/staging/lustre/lustre/include/lustre/lustre_idl.h
index 0b721c65c2a3..b064b5821e3f 100644
--- a/drivers/staging/lustre/lustre/include/lustre/lustre_idl.h
+++ b/drivers/staging/lustre/lustre/include/lustre/lustre_idl.h
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/include/lustre/lustre_user.h b/drivers/staging/lustre/lustre/include/lustre/lustre_user.h
index 80f8ec529424..2b4dd656d5f5 100644
--- a/drivers/staging/lustre/lustre/include/lustre/lustre_user.h
+++ b/drivers/staging/lustre/lustre/include/lustre/lustre_user.h
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2010, 2012, Intel Corporation.
+ * Copyright (c) 2010, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/include/lustre_disk.h b/drivers/staging/lustre/lustre/include/lustre_disk.h
index 5e1ac129a681..7c6933ffc9c1 100644
--- a/drivers/staging/lustre/lustre/include/lustre_disk.h
+++ b/drivers/staging/lustre/lustre/include/lustre_disk.h
@@ -68,6 +68,7 @@
everything as string options */
#define LMD_MAGIC 0xbdacbd03
+#define LMD_PARAMS_MAXLEN 4096
/* gleaned from the mount command - no persistent info here */
struct lustre_mount_data {
diff --git a/drivers/staging/lustre/lustre/include/lustre_dlm.h b/drivers/staging/lustre/lustre/include/lustre_dlm.h
index 0e75a15fe0d4..9b319f1df025 100644
--- a/drivers/staging/lustre/lustre/include/lustre_dlm.h
+++ b/drivers/staging/lustre/lustre/include/lustre_dlm.h
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2010, 2012, Intel Corporation.
+ * Copyright (c) 2010, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -872,8 +872,6 @@ struct ldlm_resource {
*/
struct mutex lr_lvb_mutex;
int lr_lvb_len;
- /** protected by lr_lock */
- void *lr_lvb_data;
/** When the resource was considered as contended. */
unsigned long lr_contention_time;
diff --git a/drivers/staging/lustre/lustre/include/lustre_eacl.h b/drivers/staging/lustre/lustre/include/lustre_eacl.h
index fee4d2c75506..0b66593a9526 100644
--- a/drivers/staging/lustre/lustre/include/lustre_eacl.h
+++ b/drivers/staging/lustre/lustre/include/lustre_eacl.h
@@ -76,8 +76,6 @@ extern int
lustre_posix_acl_xattr_filter(posix_acl_xattr_header *header, size_t size,
posix_acl_xattr_header **out);
extern void
-lustre_posix_acl_xattr_free(posix_acl_xattr_header *header, int size);
-extern void
lustre_ext_acl_xattr_free(ext_acl_xattr_header *header);
extern ext_acl_xattr_header *
lustre_acl_xattr_merge2ext(posix_acl_xattr_header *posix_header, int size,
diff --git a/drivers/staging/lustre/lustre/include/lustre_export.h b/drivers/staging/lustre/lustre/include/lustre_export.h
index 1daf4c572415..311e5aa9b0db 100644
--- a/drivers/staging/lustre/lustre/include/lustre_export.h
+++ b/drivers/staging/lustre/lustre/include/lustre_export.h
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/include/lustre_fid.h b/drivers/staging/lustre/lustre/include/lustre_fid.h
index 47c3f3750240..9b1a9c695113 100644
--- a/drivers/staging/lustre/lustre/include/lustre_fid.h
+++ b/drivers/staging/lustre/lustre/include/lustre_fid.h
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/include/lustre_fld.h b/drivers/staging/lustre/lustre/include/lustre_fld.h
index d8b3db9cdeba..551162624974 100644
--- a/drivers/staging/lustre/lustre/include/lustre_fld.h
+++ b/drivers/staging/lustre/lustre/include/lustre_fld.h
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2013, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/include/lustre_ha.h b/drivers/staging/lustre/lustre/include/lustre_ha.h
index 49dfbb14f381..5488a698dabd 100644
--- a/drivers/staging/lustre/lustre/include/lustre_ha.h
+++ b/drivers/staging/lustre/lustre/include/lustre_ha.h
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/include/lustre_log.h b/drivers/staging/lustre/lustre/include/lustre_log.h
index 1de0c4d6f7f7..e4fc8b5e1336 100644
--- a/drivers/staging/lustre/lustre/include/lustre_log.h
+++ b/drivers/staging/lustre/lustre/include/lustre_log.h
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -94,9 +94,6 @@ int llog_open(const struct lu_env *env, struct llog_ctxt *ctxt,
struct llog_handle **lgh, struct llog_logid *logid,
char *name, enum llog_open_param open_param);
int llog_close(const struct lu_env *env, struct llog_handle *cathandle);
-int llog_backup(const struct lu_env *env, struct obd_device *obd,
- struct llog_ctxt *ctxt, struct llog_ctxt *bak_ctxt,
- char *name, char *backup);
/* llog_process flags */
#define LLOG_FLAG_NODEAMON 0x0001
diff --git a/drivers/staging/lustre/lustre/include/lustre_mds.h b/drivers/staging/lustre/lustre/include/lustre_mds.h
index a16eb8b61178..95d27ddecfb3 100644
--- a/drivers/staging/lustre/lustre/include/lustre_mds.h
+++ b/drivers/staging/lustre/lustre/include/lustre_mds.h
@@ -62,12 +62,6 @@ struct mds_group_info {
#define MDD_OBD_NAME "mdd_obd"
#define MDD_OBD_UUID "mdd_obd_uuid"
-static inline int md_should_create(__u64 flags)
-{
- return !(flags & MDS_OPEN_DELAY_CREATE ||
- !(flags & FMODE_WRITE));
-}
-
/* these are local flags, used only on the client, private */
#define M_CHECK_STALE 0200000000
diff --git a/drivers/staging/lustre/lustre/include/lustre_net.h b/drivers/staging/lustre/lustre/include/lustre_net.h
index 0127f45ca0c3..d834ddd8183b 100644
--- a/drivers/staging/lustre/lustre/include/lustre_net.h
+++ b/drivers/staging/lustre/lustre/include/lustre_net.h
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2010, 2012, Intel Corporation.
+ * Copyright (c) 2010, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/include/lustre_param.h b/drivers/staging/lustre/lustre/include/lustre_param.h
index 8f6c0b26cfab..383fe6febe4b 100644
--- a/drivers/staging/lustre/lustre/include/lustre_param.h
+++ b/drivers/staging/lustre/lustre/include/lustre_param.h
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/include/lustre_req_layout.h b/drivers/staging/lustre/lustre/include/lustre_req_layout.h
index df292f6d4a85..46a662f89322 100644
--- a/drivers/staging/lustre/lustre/include/lustre_req_layout.h
+++ b/drivers/staging/lustre/lustre/include/lustre_req_layout.h
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/include/obd.h b/drivers/staging/lustre/lustre/include/obd.h
index 5e93afca3435..bcbe61301713 100644
--- a/drivers/staging/lustre/lustre/include/obd.h
+++ b/drivers/staging/lustre/lustre/include/obd.h
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -963,123 +963,123 @@ struct md_enqueue_info {
};
struct obd_ops {
- struct module *o_owner;
- int (*o_iocontrol)(unsigned int cmd, struct obd_export *exp, int len,
- void *karg, void *uarg);
- int (*o_get_info)(const struct lu_env *env, struct obd_export *,
- __u32 keylen, void *key, __u32 *vallen, void *val,
- struct lov_stripe_md *lsm);
- int (*o_set_info_async)(const struct lu_env *, struct obd_export *,
- __u32 keylen, void *key,
- __u32 vallen, void *val,
- struct ptlrpc_request_set *set);
- int (*o_attach)(struct obd_device *dev, u32 len, void *data);
- int (*o_detach)(struct obd_device *dev);
- int (*o_setup)(struct obd_device *dev, struct lustre_cfg *cfg);
- int (*o_precleanup)(struct obd_device *dev,
- enum obd_cleanup_stage cleanup_stage);
- int (*o_cleanup)(struct obd_device *dev);
- int (*o_process_config)(struct obd_device *dev, u32 len, void *data);
- int (*o_postrecov)(struct obd_device *dev);
- int (*o_add_conn)(struct obd_import *imp, struct obd_uuid *uuid,
- int priority);
- int (*o_del_conn)(struct obd_import *imp, struct obd_uuid *uuid);
+ struct module *owner;
+ int (*iocontrol)(unsigned int cmd, struct obd_export *exp, int len,
+ void *karg, void *uarg);
+ int (*get_info)(const struct lu_env *env, struct obd_export *,
+ __u32 keylen, void *key, __u32 *vallen, void *val,
+ struct lov_stripe_md *lsm);
+ int (*set_info_async)(const struct lu_env *, struct obd_export *,
+ __u32 keylen, void *key,
+ __u32 vallen, void *val,
+ struct ptlrpc_request_set *set);
+ int (*attach)(struct obd_device *dev, u32 len, void *data);
+ int (*detach)(struct obd_device *dev);
+ int (*setup)(struct obd_device *dev, struct lustre_cfg *cfg);
+ int (*precleanup)(struct obd_device *dev,
+ enum obd_cleanup_stage cleanup_stage);
+ int (*cleanup)(struct obd_device *dev);
+ int (*process_config)(struct obd_device *dev, u32 len, void *data);
+ int (*postrecov)(struct obd_device *dev);
+ int (*add_conn)(struct obd_import *imp, struct obd_uuid *uuid,
+ int priority);
+ int (*del_conn)(struct obd_import *imp, struct obd_uuid *uuid);
/* connect to the target device with given connection
* data. @ocd->ocd_connect_flags is modified to reflect flags actually
* granted by the target, which are guaranteed to be a subset of flags
* asked for. If @ocd == NULL, use default parameters. */
- int (*o_connect)(const struct lu_env *env,
- struct obd_export **exp, struct obd_device *src,
- struct obd_uuid *cluuid, struct obd_connect_data *ocd,
+ int (*connect)(const struct lu_env *env,
+ struct obd_export **exp, struct obd_device *src,
+ struct obd_uuid *cluuid, struct obd_connect_data *ocd,
+ void *localdata);
+ int (*reconnect)(const struct lu_env *env,
+ struct obd_export *exp, struct obd_device *src,
+ struct obd_uuid *cluuid,
+ struct obd_connect_data *ocd,
void *localdata);
- int (*o_reconnect)(const struct lu_env *env,
- struct obd_export *exp, struct obd_device *src,
- struct obd_uuid *cluuid,
- struct obd_connect_data *ocd,
- void *localdata);
- int (*o_disconnect)(struct obd_export *exp);
+ int (*disconnect)(struct obd_export *exp);
/* Initialize/finalize fids infrastructure. */
- int (*o_fid_init)(struct obd_device *obd,
- struct obd_export *exp, enum lu_cli_type type);
- int (*o_fid_fini)(struct obd_device *obd);
+ int (*fid_init)(struct obd_device *obd,
+ struct obd_export *exp, enum lu_cli_type type);
+ int (*fid_fini)(struct obd_device *obd);
/* Allocate new fid according to passed @hint. */
- int (*o_fid_alloc)(struct obd_export *exp, struct lu_fid *fid,
- struct md_op_data *op_data);
+ int (*fid_alloc)(struct obd_export *exp, struct lu_fid *fid,
+ struct md_op_data *op_data);
/*
* Object with @fid is getting deleted, we may want to do something
* about this.
*/
- int (*o_statfs)(const struct lu_env *, struct obd_export *exp,
- struct obd_statfs *osfs, __u64 max_age, __u32 flags);
- int (*o_statfs_async)(struct obd_export *exp, struct obd_info *oinfo,
- __u64 max_age, struct ptlrpc_request_set *set);
- int (*o_packmd)(struct obd_export *exp, struct lov_mds_md **disk_tgt,
- struct lov_stripe_md *mem_src);
- int (*o_unpackmd)(struct obd_export *exp,
- struct lov_stripe_md **mem_tgt,
- struct lov_mds_md *disk_src, int disk_len);
- int (*o_preallocate)(struct lustre_handle *, u32 *req, u64 *ids);
- int (*o_create)(const struct lu_env *env, struct obd_export *exp,
- struct obdo *oa, struct lov_stripe_md **ea,
- struct obd_trans_info *oti);
- int (*o_destroy)(const struct lu_env *env, struct obd_export *exp,
- struct obdo *oa, struct lov_stripe_md *ea,
- struct obd_trans_info *oti, struct obd_export *md_exp);
- int (*o_setattr)(const struct lu_env *, struct obd_export *exp,
- struct obd_info *oinfo, struct obd_trans_info *oti);
- int (*o_setattr_async)(struct obd_export *exp, struct obd_info *oinfo,
- struct obd_trans_info *oti,
- struct ptlrpc_request_set *rqset);
- int (*o_getattr)(const struct lu_env *env, struct obd_export *exp,
- struct obd_info *oinfo);
- int (*o_getattr_async)(struct obd_export *exp, struct obd_info *oinfo,
- struct ptlrpc_request_set *set);
- int (*o_adjust_kms)(struct obd_export *exp, struct lov_stripe_md *lsm,
- u64 size, int shrink);
- int (*o_preprw)(const struct lu_env *env, int cmd,
- struct obd_export *exp, struct obdo *oa, int objcount,
- struct obd_ioobj *obj, struct niobuf_remote *remote,
- int *nr_pages, struct niobuf_local *local,
- struct obd_trans_info *oti);
- int (*o_commitrw)(const struct lu_env *env, int cmd,
- struct obd_export *exp, struct obdo *oa,
- int objcount, struct obd_ioobj *obj,
- struct niobuf_remote *remote, int pages,
- struct niobuf_local *local,
- struct obd_trans_info *oti, int rc);
- int (*o_find_cbdata)(struct obd_export *, struct lov_stripe_md *,
- ldlm_iterator_t it, void *data);
- int (*o_init_export)(struct obd_export *exp);
- int (*o_destroy_export)(struct obd_export *exp);
+ int (*statfs)(const struct lu_env *, struct obd_export *exp,
+ struct obd_statfs *osfs, __u64 max_age, __u32 flags);
+ int (*statfs_async)(struct obd_export *exp, struct obd_info *oinfo,
+ __u64 max_age, struct ptlrpc_request_set *set);
+ int (*packmd)(struct obd_export *exp, struct lov_mds_md **disk_tgt,
+ struct lov_stripe_md *mem_src);
+ int (*unpackmd)(struct obd_export *exp,
+ struct lov_stripe_md **mem_tgt,
+ struct lov_mds_md *disk_src, int disk_len);
+ int (*preallocate)(struct lustre_handle *, u32 *req, u64 *ids);
+ int (*create)(const struct lu_env *env, struct obd_export *exp,
+ struct obdo *oa, struct lov_stripe_md **ea,
+ struct obd_trans_info *oti);
+ int (*destroy)(const struct lu_env *env, struct obd_export *exp,
+ struct obdo *oa, struct lov_stripe_md *ea,
+ struct obd_trans_info *oti, struct obd_export *md_exp);
+ int (*setattr)(const struct lu_env *, struct obd_export *exp,
+ struct obd_info *oinfo, struct obd_trans_info *oti);
+ int (*setattr_async)(struct obd_export *exp, struct obd_info *oinfo,
+ struct obd_trans_info *oti,
+ struct ptlrpc_request_set *rqset);
+ int (*getattr)(const struct lu_env *env, struct obd_export *exp,
+ struct obd_info *oinfo);
+ int (*getattr_async)(struct obd_export *exp, struct obd_info *oinfo,
+ struct ptlrpc_request_set *set);
+ int (*adjust_kms)(struct obd_export *exp, struct lov_stripe_md *lsm,
+ u64 size, int shrink);
+ int (*preprw)(const struct lu_env *env, int cmd,
+ struct obd_export *exp, struct obdo *oa, int objcount,
+ struct obd_ioobj *obj, struct niobuf_remote *remote,
+ int *nr_pages, struct niobuf_local *local,
+ struct obd_trans_info *oti);
+ int (*commitrw)(const struct lu_env *env, int cmd,
+ struct obd_export *exp, struct obdo *oa,
+ int objcount, struct obd_ioobj *obj,
+ struct niobuf_remote *remote, int pages,
+ struct niobuf_local *local,
+ struct obd_trans_info *oti, int rc);
+ int (*find_cbdata)(struct obd_export *, struct lov_stripe_md *,
+ ldlm_iterator_t it, void *data);
+ int (*init_export)(struct obd_export *exp);
+ int (*destroy_export)(struct obd_export *exp);
/* metadata-only methods */
- int (*o_import_event)(struct obd_device *, struct obd_import *,
- enum obd_import_event);
+ int (*import_event)(struct obd_device *, struct obd_import *,
+ enum obd_import_event);
- int (*o_notify)(struct obd_device *obd, struct obd_device *watched,
- enum obd_notify_event ev, void *data);
+ int (*notify)(struct obd_device *obd, struct obd_device *watched,
+ enum obd_notify_event ev, void *data);
- int (*o_health_check)(const struct lu_env *env, struct obd_device *);
- struct obd_uuid *(*o_get_uuid)(struct obd_export *exp);
+ int (*health_check)(const struct lu_env *env, struct obd_device *);
+ struct obd_uuid *(*get_uuid)(struct obd_export *exp);
/* quota methods */
- int (*o_quotacheck)(struct obd_device *, struct obd_export *,
- struct obd_quotactl *);
- int (*o_quotactl)(struct obd_device *, struct obd_export *,
+ int (*quotacheck)(struct obd_device *, struct obd_export *,
struct obd_quotactl *);
+ int (*quotactl)(struct obd_device *, struct obd_export *,
+ struct obd_quotactl *);
/* pools methods */
- int (*o_pool_new)(struct obd_device *obd, char *poolname);
- int (*o_pool_del)(struct obd_device *obd, char *poolname);
- int (*o_pool_add)(struct obd_device *obd, char *poolname,
- char *ostname);
- int (*o_pool_rem)(struct obd_device *obd, char *poolname,
- char *ostname);
- void (*o_getref)(struct obd_device *obd);
- void (*o_putref)(struct obd_device *obd);
+ int (*pool_new)(struct obd_device *obd, char *poolname);
+ int (*pool_del)(struct obd_device *obd, char *poolname);
+ int (*pool_add)(struct obd_device *obd, char *poolname,
+ char *ostname);
+ int (*pool_rem)(struct obd_device *obd, char *poolname,
+ char *ostname);
+ void (*getref)(struct obd_device *obd);
+ void (*putref)(struct obd_device *obd);
/*
* NOTE: If adding ops, add another LPROCFS_OBD_OP_INIT() line
* to lprocfs_alloc_obd_stats() in obdclass/lprocfs_status.c.
@@ -1124,89 +1124,89 @@ struct md_open_data {
struct lookup_intent;
struct md_ops {
- int (*m_getstatus)(struct obd_export *, struct lu_fid *);
- int (*m_null_inode)(struct obd_export *, const struct lu_fid *);
- int (*m_find_cbdata)(struct obd_export *, const struct lu_fid *,
- ldlm_iterator_t, void *);
- int (*m_close)(struct obd_export *, struct md_op_data *,
- struct md_open_data *, struct ptlrpc_request **);
- int (*m_create)(struct obd_export *, struct md_op_data *,
- const void *, int, int, __u32, __u32, cfs_cap_t,
- __u64, struct ptlrpc_request **);
- int (*m_done_writing)(struct obd_export *, struct md_op_data *,
- struct md_open_data *);
- int (*m_enqueue)(struct obd_export *, struct ldlm_enqueue_info *,
- struct lookup_intent *, struct md_op_data *,
- struct lustre_handle *, void *, int,
- struct ptlrpc_request **, __u64);
- int (*m_getattr)(struct obd_export *, struct md_op_data *,
- struct ptlrpc_request **);
- int (*m_getattr_name)(struct obd_export *, struct md_op_data *,
- struct ptlrpc_request **);
- int (*m_intent_lock)(struct obd_export *, struct md_op_data *,
- void *, int, struct lookup_intent *, int,
- struct ptlrpc_request **,
- ldlm_blocking_callback, __u64);
- int (*m_link)(struct obd_export *, struct md_op_data *,
+ int (*getstatus)(struct obd_export *, struct lu_fid *);
+ int (*null_inode)(struct obd_export *, const struct lu_fid *);
+ int (*find_cbdata)(struct obd_export *, const struct lu_fid *,
+ ldlm_iterator_t, void *);
+ int (*close)(struct obd_export *, struct md_op_data *,
+ struct md_open_data *, struct ptlrpc_request **);
+ int (*create)(struct obd_export *, struct md_op_data *,
+ const void *, int, int, __u32, __u32, cfs_cap_t,
+ __u64, struct ptlrpc_request **);
+ int (*done_writing)(struct obd_export *, struct md_op_data *,
+ struct md_open_data *);
+ int (*enqueue)(struct obd_export *, struct ldlm_enqueue_info *,
+ struct lookup_intent *, struct md_op_data *,
+ struct lustre_handle *, void *, int,
+ struct ptlrpc_request **, __u64);
+ int (*getattr)(struct obd_export *, struct md_op_data *,
+ struct ptlrpc_request **);
+ int (*getattr_name)(struct obd_export *, struct md_op_data *,
+ struct ptlrpc_request **);
+ int (*intent_lock)(struct obd_export *, struct md_op_data *,
+ void *, int, struct lookup_intent *, int,
+ struct ptlrpc_request **,
+ ldlm_blocking_callback, __u64);
+ int (*link)(struct obd_export *, struct md_op_data *,
+ struct ptlrpc_request **);
+ int (*rename)(struct obd_export *, struct md_op_data *,
+ const char *, int, const char *, int,
struct ptlrpc_request **);
- int (*m_rename)(struct obd_export *, struct md_op_data *,
- const char *, int, const char *, int,
- struct ptlrpc_request **);
- int (*m_is_subdir)(struct obd_export *, const struct lu_fid *,
- const struct lu_fid *,
+ int (*is_subdir)(struct obd_export *, const struct lu_fid *,
+ const struct lu_fid *,
struct ptlrpc_request **);
- int (*m_setattr)(struct obd_export *, struct md_op_data *, void *,
- int, void *, int, struct ptlrpc_request **,
+ int (*setattr)(struct obd_export *, struct md_op_data *, void *,
+ int, void *, int, struct ptlrpc_request **,
struct md_open_data **mod);
- int (*m_sync)(struct obd_export *, const struct lu_fid *,
+ int (*sync)(struct obd_export *, const struct lu_fid *,
+ struct ptlrpc_request **);
+ int (*readpage)(struct obd_export *, struct md_op_data *,
+ struct page **, struct ptlrpc_request **);
+
+ int (*unlink)(struct obd_export *, struct md_op_data *,
struct ptlrpc_request **);
- int (*m_readpage)(struct obd_export *, struct md_op_data *,
- struct page **, struct ptlrpc_request **);
- int (*m_unlink)(struct obd_export *, struct md_op_data *,
+ int (*setxattr)(struct obd_export *, const struct lu_fid *,
+ u64, const char *, const char *, int, int, int, __u32,
struct ptlrpc_request **);
- int (*m_setxattr)(struct obd_export *, const struct lu_fid *,
- u64, const char *, const char *, int, int, int, __u32,
- struct ptlrpc_request **);
-
- int (*m_getxattr)(struct obd_export *, const struct lu_fid *,
- u64, const char *, const char *, int, int, int,
- struct ptlrpc_request **);
+ int (*getxattr)(struct obd_export *, const struct lu_fid *,
+ u64, const char *, const char *, int, int, int,
+ struct ptlrpc_request **);
- int (*m_init_ea_size)(struct obd_export *, int, int, int, int);
+ int (*init_ea_size)(struct obd_export *, int, int, int, int);
- int (*m_get_lustre_md)(struct obd_export *, struct ptlrpc_request *,
- struct obd_export *, struct obd_export *,
- struct lustre_md *);
+ int (*get_lustre_md)(struct obd_export *, struct ptlrpc_request *,
+ struct obd_export *, struct obd_export *,
+ struct lustre_md *);
- int (*m_free_lustre_md)(struct obd_export *, struct lustre_md *);
+ int (*free_lustre_md)(struct obd_export *, struct lustre_md *);
- int (*m_set_open_replay_data)(struct obd_export *,
- struct obd_client_handle *,
- struct lookup_intent *);
- int (*m_clear_open_replay_data)(struct obd_export *,
- struct obd_client_handle *);
- int (*m_set_lock_data)(struct obd_export *, __u64 *, void *, __u64 *);
+ int (*set_open_replay_data)(struct obd_export *,
+ struct obd_client_handle *,
+ struct lookup_intent *);
+ int (*clear_open_replay_data)(struct obd_export *,
+ struct obd_client_handle *);
+ int (*set_lock_data)(struct obd_export *, __u64 *, void *, __u64 *);
- ldlm_mode_t (*m_lock_match)(struct obd_export *, __u64,
- const struct lu_fid *, ldlm_type_t,
- ldlm_policy_data_t *, ldlm_mode_t,
- struct lustre_handle *);
+ ldlm_mode_t (*lock_match)(struct obd_export *, __u64,
+ const struct lu_fid *, ldlm_type_t,
+ ldlm_policy_data_t *, ldlm_mode_t,
+ struct lustre_handle *);
- int (*m_cancel_unused)(struct obd_export *, const struct lu_fid *,
- ldlm_policy_data_t *, ldlm_mode_t,
- ldlm_cancel_flags_t flags, void *opaque);
+ int (*cancel_unused)(struct obd_export *, const struct lu_fid *,
+ ldlm_policy_data_t *, ldlm_mode_t,
+ ldlm_cancel_flags_t flags, void *opaque);
- int (*m_get_remote_perm)(struct obd_export *, const struct lu_fid *,
- __u32, struct ptlrpc_request **);
+ int (*get_remote_perm)(struct obd_export *, const struct lu_fid *,
+ __u32, struct ptlrpc_request **);
- int (*m_intent_getattr_async)(struct obd_export *,
- struct md_enqueue_info *,
- struct ldlm_enqueue_info *);
+ int (*intent_getattr_async)(struct obd_export *,
+ struct md_enqueue_info *,
+ struct ldlm_enqueue_info *);
- int (*m_revalidate_lock)(struct obd_export *, struct lookup_intent *,
- struct lu_fid *, __u64 *bits);
+ int (*revalidate_lock)(struct obd_export *, struct lookup_intent *,
+ struct lu_fid *, __u64 *bits);
/*
* NOTE: If adding ops, add another LPROCFS_MD_OP_INIT() line to
diff --git a/drivers/staging/lustre/lustre/include/obd_cksum.h b/drivers/staging/lustre/lustre/include/obd_cksum.h
index a0099d71773a..01db60405393 100644
--- a/drivers/staging/lustre/lustre/include/obd_cksum.h
+++ b/drivers/staging/lustre/lustre/include/obd_cksum.h
@@ -133,29 +133,6 @@ static inline cksum_type_t cksum_types_supported_client(void)
return ret;
}
-/* Server uses algos that perform at 50% or better of the Adler */
-static inline cksum_type_t cksum_types_supported_server(void)
-{
- int base_speed;
- cksum_type_t ret = OBD_CKSUM_ADLER;
-
- CDEBUG(D_INFO, "Crypto hash speed: crc %d, crc32c %d, adler %d\n",
- cfs_crypto_hash_speed(cksum_obd2cfs(OBD_CKSUM_CRC32)),
- cfs_crypto_hash_speed(cksum_obd2cfs(OBD_CKSUM_CRC32C)),
- cfs_crypto_hash_speed(cksum_obd2cfs(OBD_CKSUM_ADLER)));
-
- base_speed = cfs_crypto_hash_speed(cksum_obd2cfs(OBD_CKSUM_ADLER)) / 2;
-
- if (cfs_crypto_hash_speed(cksum_obd2cfs(OBD_CKSUM_CRC32C)) >=
- base_speed)
- ret |= OBD_CKSUM_CRC32C;
- if (cfs_crypto_hash_speed(cksum_obd2cfs(OBD_CKSUM_CRC32)) >=
- base_speed)
- ret |= OBD_CKSUM_CRC32;
-
- return ret;
-}
-
/* Select the best checksum algorithm among those supplied in the cksum_types
* input.
*
diff --git a/drivers/staging/lustre/lustre/include/obd_class.h b/drivers/staging/lustre/lustre/include/obd_class.h
index fd5f3731db92..97d80397503c 100644
--- a/drivers/staging/lustre/lustre/include/obd_class.h
+++ b/drivers/staging/lustre/lustre/include/obd_class.h
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -270,8 +270,8 @@ void obdo_to_ioobj(struct obdo *oa, struct obd_ioobj *ioobj);
void md_from_obdo(struct md_op_data *op_data, struct obdo *oa, u32 valid);
#define OBT(dev) (dev)->obd_type
-#define OBP(dev, op) (dev)->obd_type->typ_dt_ops->o_ ## op
-#define MDP(dev, op) (dev)->obd_type->typ_md_ops->m_ ## op
+#define OBP(dev, op) (dev)->obd_type->typ_dt_ops->op
+#define MDP(dev, op) (dev)->obd_type->typ_md_ops->op
#define CTXTP(ctxt, op) (ctxt)->loc_logops->lop_##op
/* Ensure obd_setup: used for cleanup which must be called
@@ -301,9 +301,9 @@ static inline int obd_check_dev_active(struct obd_device *obd)
}
#define OBD_COUNTER_OFFSET(op) \
- ((offsetof(struct obd_ops, o_ ## op) - \
- offsetof(struct obd_ops, o_iocontrol)) \
- / sizeof(((struct obd_ops *)(0))->o_iocontrol))
+ ((offsetof(struct obd_ops, op) - \
+ offsetof(struct obd_ops, iocontrol)) \
+ / sizeof(((struct obd_ops *)(0))->iocontrol))
#define OBD_COUNTER_INCREMENT(obdx, op) \
if ((obdx)->obd_stats != NULL) { \
@@ -324,9 +324,9 @@ static inline int obd_check_dev_active(struct obd_device *obd)
}
#define MD_COUNTER_OFFSET(op) \
- ((offsetof(struct md_ops, m_ ## op) - \
- offsetof(struct md_ops, m_getstatus)) \
- / sizeof(((struct md_ops *)(0))->m_getstatus))
+ ((offsetof(struct md_ops, op) - \
+ offsetof(struct md_ops, getstatus)) \
+ / sizeof(((struct md_ops *)(0))->getstatus))
#define MD_COUNTER_INCREMENT(obdx, op) \
if ((obd)->md_stats != NULL) { \
diff --git a/drivers/staging/lustre/lustre/include/obd_support.h b/drivers/staging/lustre/lustre/include/obd_support.h
index a22a5308fb48..d031437c0528 100644
--- a/drivers/staging/lustre/lustre/include/obd_support.h
+++ b/drivers/staging/lustre/lustre/include/obd_support.h
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/lclient/lcommon_cl.c b/drivers/staging/lustre/lustre/lclient/lcommon_cl.c
index 0b8e4d2175ae..34dde7dede74 100644
--- a/drivers/staging/lustre/lustre/lclient/lcommon_cl.c
+++ b/drivers/staging/lustre/lustre/lclient/lcommon_cl.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -427,7 +427,7 @@ static void ccc_object_size_lock(struct cl_object *obj)
{
struct inode *inode = ccc_object_inode(obj);
- cl_isize_lock(inode);
+ ll_inode_size_lock(inode);
cl_object_attr_lock(obj);
}
@@ -436,7 +436,7 @@ static void ccc_object_size_unlock(struct cl_object *obj)
struct inode *inode = ccc_object_inode(obj);
cl_object_attr_unlock(obj);
- cl_isize_unlock(inode);
+ ll_inode_size_unlock(inode);
}
/*****************************************************************************
diff --git a/drivers/staging/lustre/lustre/ldlm/interval_tree.c b/drivers/staging/lustre/lustre/ldlm/interval_tree.c
index 39b571721881..a2ea8e5b93d8 100644
--- a/drivers/staging/lustre/lustre/ldlm/interval_tree.c
+++ b/drivers/staging/lustre/lustre/ldlm/interval_tree.c
@@ -96,18 +96,6 @@ static inline int extent_equal(struct interval_node_extent *e1,
return (e1->start == e2->start) && (e1->end == e2->end);
}
-static inline int node_compare(struct interval_node *n1,
- struct interval_node *n2)
-{
- return extent_compare(&n1->in_extent, &n2->in_extent);
-}
-
-static inline int node_equal(struct interval_node *n1,
- struct interval_node *n2)
-{
- return extent_equal(&n1->in_extent, &n2->in_extent);
-}
-
static inline __u64 max_u64(__u64 x, __u64 y)
{
return x > y ? x : y;
@@ -278,14 +266,14 @@ struct interval_node *interval_insert(struct interval_node *node,
p = root;
while (*p) {
parent = *p;
- if (node_equal(parent, node))
+ if (extent_equal(&parent->in_extent, &node->in_extent))
return parent;
/* max_high field must be updated after each iteration */
if (parent->in_max_high < interval_high(node))
parent->in_max_high = interval_high(node);
- if (node_compare(node, parent) < 0)
+ if (extent_compare(&node->in_extent, &parent->in_extent) < 0)
p = &parent->in_left;
else
p = &parent->in_right;
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_extent.c b/drivers/staging/lustre/lustre/ldlm/ldlm_extent.c
index c787888eb8af..9c70f31ea56e 100644
--- a/drivers/staging/lustre/lustre/ldlm/ldlm_extent.c
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_extent.c
@@ -149,7 +149,7 @@ static inline int lock_mode_to_index(ldlm_mode_t mode)
int index;
LASSERT(mode != 0);
- LASSERT(IS_PO2(mode));
+ LASSERT(is_power_of_2(mode));
for (index = -1; mode; index++)
mode >>= 1;
LASSERT(index < LCK_MODE_NUM);
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_internal.h b/drivers/staging/lustre/lustre/ldlm/ldlm_internal.h
index db3c9b7af7d5..849cc98df7dd 100644
--- a/drivers/staging/lustre/lustre/ldlm/ldlm_internal.h
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_internal.h
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_lib.c b/drivers/staging/lustre/lustre/ldlm/ldlm_lib.c
index ccce1e503120..3c8d4413d976 100644
--- a/drivers/staging/lustre/lustre/ldlm/ldlm_lib.c
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_lib.c
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2010, 2012, Intel Corporation.
+ * Copyright (c) 2010, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_lock.c b/drivers/staging/lustre/lustre/ldlm/ldlm_lock.c
index 7f8c70056ffd..cf9ec0cfe247 100644
--- a/drivers/staging/lustre/lustre/ldlm/ldlm_lock.c
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_lock.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2010, 2012, Intel Corporation.
+ * Copyright (c) 2010, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_lockd.c b/drivers/staging/lustre/lustre/ldlm/ldlm_lockd.c
index ca115119501a..79aeb2bf6c8e 100644
--- a/drivers/staging/lustre/lustre/ldlm/ldlm_lockd.c
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_lockd.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2010, 2012, Intel Corporation.
+ * Copyright (c) 2010, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_pool.c b/drivers/staging/lustre/lustre/ldlm/ldlm_pool.c
index 1a4eef64658f..3d7c137d223a 100644
--- a/drivers/staging/lustre/lustre/ldlm/ldlm_pool.c
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_pool.c
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2010, 2012, Intel Corporation.
+ * Copyright (c) 2010, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -176,11 +176,6 @@ enum {
LDLM_POOL_LAST_STAT
};
-static inline struct ldlm_namespace *ldlm_pl2ns(struct ldlm_pool *pl)
-{
- return container_of(pl, struct ldlm_namespace, ns_pool);
-}
-
/**
* Calculates suggested grant_step in % of available locks for passed
* \a period. This is later used in grant_plan calculations.
@@ -213,22 +208,6 @@ static inline int ldlm_pool_t2gsp(unsigned int t)
}
/**
- * Returns current \a pl limit.
- */
-static __u32 ldlm_pool_get_limit(struct ldlm_pool *pl)
-{
- return atomic_read(&pl->pl_limit);
-}
-
-/**
- * Sets passed \a limit to \a pl.
- */
-static void ldlm_pool_set_limit(struct ldlm_pool *pl, __u32 limit)
-{
- atomic_set(&pl->pl_limit, limit);
-}
-
-/**
* Recalculates next stats on passed \a pl.
*
* \pre ->pl_lock is locked.
@@ -254,7 +233,8 @@ static void ldlm_pool_recalc_stats(struct ldlm_pool *pl)
}
/**
- * Sets SLV and Limit from ldlm_pl2ns(pl)->ns_obd tp passed \a pl.
+ * Sets SLV and Limit from container_of(pl, struct ldlm_namespace,
+ * ns_pool)->ns_obd tp passed \a pl.
*/
static void ldlm_cli_pool_pop_slv(struct ldlm_pool *pl)
{
@@ -264,11 +244,12 @@ static void ldlm_cli_pool_pop_slv(struct ldlm_pool *pl)
* Get new SLV and Limit from obd which is updated with coming
* RPCs.
*/
- obd = ldlm_pl2ns(pl)->ns_obd;
+ obd = container_of(pl, struct ldlm_namespace,
+ ns_pool)->ns_obd;
LASSERT(obd != NULL);
read_lock(&obd->obd_pool_lock);
pl->pl_server_lock_volume = obd->obd_pool_slv;
- ldlm_pool_set_limit(pl, obd->obd_pool_limit);
+ atomic_set(&pl->pl_limit, obd->obd_pool_limit);
read_unlock(&obd->obd_pool_lock);
}
@@ -304,7 +285,8 @@ static int ldlm_cli_pool_recalc(struct ldlm_pool *pl)
/*
* Do not cancel locks in case lru resize is disabled for this ns.
*/
- if (!ns_connect_lru_resize(ldlm_pl2ns(pl))) {
+ if (!ns_connect_lru_resize(container_of(pl, struct ldlm_namespace,
+ ns_pool))) {
ret = 0;
goto out;
}
@@ -315,7 +297,8 @@ static int ldlm_cli_pool_recalc(struct ldlm_pool *pl)
* It may be called when SLV has changed much, this is why we do not
* take into account pl->pl_recalc_time here.
*/
- ret = ldlm_cancel_lru(ldlm_pl2ns(pl), 0, LCF_ASYNC, LDLM_CANCEL_LRUR);
+ ret = ldlm_cancel_lru(container_of(pl, struct ldlm_namespace, ns_pool),
+ 0, LCF_ASYNC, LDLM_CANCEL_LRUR);
out:
spin_lock(&pl->pl_lock);
@@ -341,7 +324,7 @@ static int ldlm_cli_pool_shrink(struct ldlm_pool *pl,
struct ldlm_namespace *ns;
int unused;
- ns = ldlm_pl2ns(pl);
+ ns = container_of(pl, struct ldlm_namespace, ns_pool);
/*
* Do not cancel locks in case lru resize is disabled for this ns.
@@ -453,7 +436,7 @@ static int lprocfs_pool_state_seq_show(struct seq_file *m, void *unused)
spin_lock(&pl->pl_lock);
slv = pl->pl_server_lock_volume;
clv = pl->pl_client_lock_volume;
- limit = ldlm_pool_get_limit(pl);
+ limit = atomic_read(&pl->pl_limit);
granted = atomic_read(&pl->pl_granted);
grant_rate = atomic_read(&pl->pl_grant_rate);
cancel_rate = atomic_read(&pl->pl_cancel_rate);
@@ -558,7 +541,8 @@ static struct kobj_type ldlm_pl_ktype = {
static int ldlm_pool_sysfs_init(struct ldlm_pool *pl)
{
- struct ldlm_namespace *ns = ldlm_pl2ns(pl);
+ struct ldlm_namespace *ns = container_of(pl, struct ldlm_namespace,
+ ns_pool);
int err;
init_completion(&pl->pl_kobj_unregister);
@@ -570,7 +554,8 @@ static int ldlm_pool_sysfs_init(struct ldlm_pool *pl)
static int ldlm_pool_debugfs_init(struct ldlm_pool *pl)
{
- struct ldlm_namespace *ns = ldlm_pl2ns(pl);
+ struct ldlm_namespace *ns = container_of(pl, struct ldlm_namespace,
+ ns_pool);
struct dentry *debugfs_ns_parent;
struct lprocfs_vars pool_vars[2];
char *var_name = NULL;
@@ -685,7 +670,7 @@ int ldlm_pool_init(struct ldlm_pool *pl, struct ldlm_namespace *ns,
snprintf(pl->pl_name, sizeof(pl->pl_name), "ldlm-pool-%s-%d",
ldlm_ns_name(ns), idx);
- ldlm_pool_set_limit(pl, 1);
+ atomic_set(&pl->pl_limit, 1);
pl->pl_server_lock_volume = 0;
pl->pl_ops = &ldlm_cli_pool_ops;
pl->pl_recalc_period = LDLM_POOL_CLI_DEF_RECALC_PERIOD;
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_request.c b/drivers/staging/lustre/lustre/ldlm/ldlm_request.c
index fdf81b87aad7..b9eb37762434 100644
--- a/drivers/staging/lustre/lustre/ldlm/ldlm_request.c
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_request.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2010, 2012, Intel Corporation.
+ * Copyright (c) 2010, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_resource.c b/drivers/staging/lustre/lustre/ldlm/ldlm_resource.c
index c0a54bf406ca..0ae610015b7c 100644
--- a/drivers/staging/lustre/lustre/ldlm/ldlm_resource.c
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_resource.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2010, 2012, Intel Corporation.
+ * Copyright (c) 2010, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -1154,8 +1154,6 @@ ldlm_resource_get(struct ldlm_namespace *ns, struct ldlm_resource *parent,
CERROR("%s: lvbo_init failed for resource %#llx:%#llx: rc = %d\n",
ns->ns_obd->obd_name, name->name[0],
name->name[1], rc);
- kfree(res->lr_lvb_data);
- res->lr_lvb_data = NULL;
res->lr_lvb_len = rc;
mutex_unlock(&res->lr_lvb_mutex);
ldlm_resource_putref(res);
diff --git a/drivers/staging/lustre/lustre/libcfs/debug.c b/drivers/staging/lustre/lustre/libcfs/debug.c
index 1d1c67164418..0b38dad13546 100644
--- a/drivers/staging/lustre/lustre/libcfs/debug.c
+++ b/drivers/staging/lustre/lustre/libcfs/debug.c
@@ -94,17 +94,14 @@ static struct kernel_param_ops param_ops_debugmb = {
static unsigned int libcfs_debug_mb;
module_param(libcfs_debug_mb, debugmb, 0644);
MODULE_PARM_DESC(libcfs_debug_mb, "Total debug buffer size.");
-EXPORT_SYMBOL(libcfs_debug_mb);
unsigned int libcfs_printk = D_CANTMASK;
module_param(libcfs_printk, uint, 0644);
MODULE_PARM_DESC(libcfs_printk, "Lustre kernel debug console mask");
-EXPORT_SYMBOL(libcfs_printk);
unsigned int libcfs_console_ratelimit = 1;
module_param(libcfs_console_ratelimit, uint, 0644);
MODULE_PARM_DESC(libcfs_console_ratelimit, "Lustre kernel debug console ratelimit (0 to disable)");
-EXPORT_SYMBOL(libcfs_console_ratelimit);
static int param_set_delay_minmax(const char *val,
const struct kernel_param *kp,
@@ -135,9 +132,7 @@ static int param_get_delay(char *buffer, const struct kernel_param *kp)
}
unsigned int libcfs_console_max_delay;
-EXPORT_SYMBOL(libcfs_console_max_delay);
unsigned int libcfs_console_min_delay;
-EXPORT_SYMBOL(libcfs_console_min_delay);
static int param_set_console_max_delay(const char *val,
const struct kernel_param *kp)
@@ -207,10 +202,8 @@ static struct kernel_param_ops param_ops_uintpos = {
unsigned int libcfs_console_backoff = CDEBUG_DEFAULT_BACKOFF;
module_param(libcfs_console_backoff, uintpos, 0644);
MODULE_PARM_DESC(libcfs_console_backoff, "Lustre kernel debug console backoff factor");
-EXPORT_SYMBOL(libcfs_console_backoff);
unsigned int libcfs_debug_binary = 1;
-EXPORT_SYMBOL(libcfs_debug_binary);
unsigned int libcfs_stack = 3 * THREAD_SIZE / 4;
EXPORT_SYMBOL(libcfs_stack);
@@ -221,7 +214,6 @@ EXPORT_SYMBOL(libcfs_catastrophe);
unsigned int libcfs_panic_on_lbug = 1;
module_param(libcfs_panic_on_lbug, uint, 0644);
MODULE_PARM_DESC(libcfs_panic_on_lbug, "Lustre kernel panic on LBUG");
-EXPORT_SYMBOL(libcfs_panic_on_lbug);
static wait_queue_head_t debug_ctlwq;
@@ -512,9 +504,9 @@ int libcfs_debug_init(unsigned long bufsize)
}
if (libcfs_debug_file_path != NULL) {
- strncpy(libcfs_debug_file_path_arr,
- libcfs_debug_file_path, PATH_MAX-1);
- libcfs_debug_file_path_arr[PATH_MAX - 1] = '\0';
+ strlcpy(libcfs_debug_file_path_arr,
+ libcfs_debug_file_path,
+ sizeof(libcfs_debug_file_path_arr));
}
/* If libcfs_debug_mb is set to an invalid value or uninitialized
@@ -565,12 +557,3 @@ int libcfs_debug_mark_buffer(const char *text)
#undef DEBUG_SUBSYSTEM
#define DEBUG_SUBSYSTEM S_LNET
-
-void libcfs_debug_set_level(unsigned int debug_level)
-{
- pr_warn("Lustre: Setting portals debug level to %08x\n",
- debug_level);
- libcfs_debug = debug_level;
-}
-
-EXPORT_SYMBOL(libcfs_debug_set_level);
diff --git a/drivers/staging/lustre/lustre/libcfs/fail.c b/drivers/staging/lustre/lustre/libcfs/fail.c
index d39fecebd12d..27831432d69a 100644
--- a/drivers/staging/lustre/lustre/libcfs/fail.c
+++ b/drivers/staging/lustre/lustre/libcfs/fail.c
@@ -26,7 +26,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -126,7 +126,7 @@ int __cfs_fail_timeout_set(__u32 id, __u32 value, int ms, int set)
int ret;
ret = __cfs_fail_check_set(id, value, set);
- if (ret) {
+ if (ret && likely(ms > 0)) {
CERROR("cfs_fail_timeout id %x sleeping for %dms\n",
id, ms);
set_current_state(TASK_UNINTERRUPTIBLE);
diff --git a/drivers/staging/lustre/lustre/libcfs/hash.c b/drivers/staging/lustre/lustre/libcfs/hash.c
index 030874428952..4d50510434be 100644
--- a/drivers/staging/lustre/lustre/libcfs/hash.c
+++ b/drivers/staging/lustre/lustre/libcfs/hash.c
@@ -106,9 +106,10 @@
* Now we support both locked iteration & lockless iteration of hash
* table. Also, user can break the iteration by return 1 in callback.
*/
+#include <linux/seq_file.h>
+#include <linux/log2.h>
#include "../../include/linux/libcfs/libcfs.h"
-#include <linux/seq_file.h>
#if CFS_HASH_DEBUG_LEVEL >= CFS_HASH_DEBUG_1
static unsigned int warn_on_depth = 8;
@@ -161,49 +162,49 @@ cfs_hash_rw_unlock(union cfs_hash_lock *lock, int exclusive)
/** No lock hash */
static struct cfs_hash_lock_ops cfs_hash_nl_lops = {
.hs_lock = cfs_hash_nl_lock,
- .hs_unlock = cfs_hash_nl_unlock,
- .hs_bkt_lock = cfs_hash_nl_lock,
- .hs_bkt_unlock = cfs_hash_nl_unlock,
+ .hs_unlock = cfs_hash_nl_unlock,
+ .hs_bkt_lock = cfs_hash_nl_lock,
+ .hs_bkt_unlock = cfs_hash_nl_unlock,
};
/** no bucket lock, one spinlock to protect everything */
static struct cfs_hash_lock_ops cfs_hash_nbl_lops = {
.hs_lock = cfs_hash_spin_lock,
- .hs_unlock = cfs_hash_spin_unlock,
- .hs_bkt_lock = cfs_hash_nl_lock,
- .hs_bkt_unlock = cfs_hash_nl_unlock,
+ .hs_unlock = cfs_hash_spin_unlock,
+ .hs_bkt_lock = cfs_hash_nl_lock,
+ .hs_bkt_unlock = cfs_hash_nl_unlock,
};
/** spin bucket lock, rehash is enabled */
static struct cfs_hash_lock_ops cfs_hash_bkt_spin_lops = {
.hs_lock = cfs_hash_rw_lock,
- .hs_unlock = cfs_hash_rw_unlock,
- .hs_bkt_lock = cfs_hash_spin_lock,
- .hs_bkt_unlock = cfs_hash_spin_unlock,
+ .hs_unlock = cfs_hash_rw_unlock,
+ .hs_bkt_lock = cfs_hash_spin_lock,
+ .hs_bkt_unlock = cfs_hash_spin_unlock,
};
/** rw bucket lock, rehash is enabled */
static struct cfs_hash_lock_ops cfs_hash_bkt_rw_lops = {
.hs_lock = cfs_hash_rw_lock,
- .hs_unlock = cfs_hash_rw_unlock,
- .hs_bkt_lock = cfs_hash_rw_lock,
- .hs_bkt_unlock = cfs_hash_rw_unlock,
+ .hs_unlock = cfs_hash_rw_unlock,
+ .hs_bkt_lock = cfs_hash_rw_lock,
+ .hs_bkt_unlock = cfs_hash_rw_unlock,
};
/** spin bucket lock, rehash is disabled */
static struct cfs_hash_lock_ops cfs_hash_nr_bkt_spin_lops = {
.hs_lock = cfs_hash_nl_lock,
- .hs_unlock = cfs_hash_nl_unlock,
- .hs_bkt_lock = cfs_hash_spin_lock,
- .hs_bkt_unlock = cfs_hash_spin_unlock,
+ .hs_unlock = cfs_hash_nl_unlock,
+ .hs_bkt_lock = cfs_hash_spin_lock,
+ .hs_bkt_unlock = cfs_hash_spin_unlock,
};
/** rw bucket lock, rehash is disabled */
static struct cfs_hash_lock_ops cfs_hash_nr_bkt_rw_lops = {
.hs_lock = cfs_hash_nl_lock,
- .hs_unlock = cfs_hash_nl_unlock,
- .hs_bkt_lock = cfs_hash_rw_lock,
- .hs_bkt_unlock = cfs_hash_rw_unlock,
+ .hs_unlock = cfs_hash_nl_unlock,
+ .hs_bkt_lock = cfs_hash_rw_lock,
+ .hs_bkt_unlock = cfs_hash_rw_unlock,
};
static void
@@ -280,7 +281,7 @@ cfs_hash_hh_hnode_del(struct cfs_hash *hs, struct cfs_hash_bd *bd,
*/
struct cfs_hash_head_dep {
struct hlist_head hd_head; /**< entries list */
- unsigned int hd_depth; /**< list length */
+ unsigned int hd_depth; /**< list length */
};
static int
@@ -328,7 +329,7 @@ cfs_hash_hd_hnode_del(struct cfs_hash *hs, struct cfs_hash_bd *bd,
*/
struct cfs_hash_dhead {
struct hlist_head dh_head; /**< entries list */
- struct hlist_node *dh_tail; /**< the last entry */
+ struct hlist_node *dh_tail; /**< the last entry */
};
static int
@@ -384,8 +385,8 @@ cfs_hash_dh_hnode_del(struct cfs_hash *hs, struct cfs_hash_bd *bd,
*/
struct cfs_hash_dhead_dep {
struct hlist_head dd_head; /**< entries list */
- struct hlist_node *dd_tail; /**< the last entry */
- unsigned int dd_depth; /**< list length */
+ struct hlist_node *dd_tail; /**< the last entry */
+ unsigned int dd_depth; /**< list length */
};
static int
@@ -436,31 +437,31 @@ cfs_hash_dd_hnode_del(struct cfs_hash *hs, struct cfs_hash_bd *bd,
}
static struct cfs_hash_hlist_ops cfs_hash_hh_hops = {
- .hop_hhead = cfs_hash_hh_hhead,
- .hop_hhead_size = cfs_hash_hh_hhead_size,
- .hop_hnode_add = cfs_hash_hh_hnode_add,
- .hop_hnode_del = cfs_hash_hh_hnode_del,
+ .hop_hhead = cfs_hash_hh_hhead,
+ .hop_hhead_size = cfs_hash_hh_hhead_size,
+ .hop_hnode_add = cfs_hash_hh_hnode_add,
+ .hop_hnode_del = cfs_hash_hh_hnode_del,
};
static struct cfs_hash_hlist_ops cfs_hash_hd_hops = {
- .hop_hhead = cfs_hash_hd_hhead,
- .hop_hhead_size = cfs_hash_hd_hhead_size,
- .hop_hnode_add = cfs_hash_hd_hnode_add,
- .hop_hnode_del = cfs_hash_hd_hnode_del,
+ .hop_hhead = cfs_hash_hd_hhead,
+ .hop_hhead_size = cfs_hash_hd_hhead_size,
+ .hop_hnode_add = cfs_hash_hd_hnode_add,
+ .hop_hnode_del = cfs_hash_hd_hnode_del,
};
static struct cfs_hash_hlist_ops cfs_hash_dh_hops = {
- .hop_hhead = cfs_hash_dh_hhead,
- .hop_hhead_size = cfs_hash_dh_hhead_size,
- .hop_hnode_add = cfs_hash_dh_hnode_add,
- .hop_hnode_del = cfs_hash_dh_hnode_del,
+ .hop_hhead = cfs_hash_dh_hhead,
+ .hop_hhead_size = cfs_hash_dh_hhead_size,
+ .hop_hnode_add = cfs_hash_dh_hnode_add,
+ .hop_hnode_del = cfs_hash_dh_hnode_del,
};
static struct cfs_hash_hlist_ops cfs_hash_dd_hops = {
- .hop_hhead = cfs_hash_dd_hhead,
- .hop_hhead_size = cfs_hash_dd_hhead_size,
- .hop_hnode_add = cfs_hash_dd_hnode_add,
- .hop_hnode_del = cfs_hash_dd_hnode_del,
+ .hop_hhead = cfs_hash_dd_hhead,
+ .hop_hhead_size = cfs_hash_dd_hhead_size,
+ .hop_hnode_add = cfs_hash_dd_hnode_add,
+ .hop_hnode_del = cfs_hash_dd_hnode_del,
};
static void
@@ -529,7 +530,7 @@ void
cfs_hash_bd_add_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd,
struct hlist_node *hnode)
{
- int rc;
+ int rc;
rc = hs->hs_hops->hop_hnode_add(hs, bd, hnode);
cfs_hash_bd_dep_record(hs, bd, rc);
@@ -572,7 +573,7 @@ cfs_hash_bd_move_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd_old,
{
struct cfs_hash_bucket *obkt = bd_old->bd_bucket;
struct cfs_hash_bucket *nbkt = bd_new->bd_bucket;
- int rc;
+ int rc;
if (cfs_hash_bd_compare(bd_old, bd_new) == 0)
return;
@@ -593,34 +594,33 @@ cfs_hash_bd_move_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd_old,
if (unlikely(nbkt->hsb_version == 0))
nbkt->hsb_version++;
}
-EXPORT_SYMBOL(cfs_hash_bd_move_locked);
enum {
/** always set, for sanity (avoid ZERO intent) */
- CFS_HS_LOOKUP_MASK_FIND = BIT(0),
+ CFS_HS_LOOKUP_MASK_FIND = BIT(0),
/** return entry with a ref */
- CFS_HS_LOOKUP_MASK_REF = BIT(1),
+ CFS_HS_LOOKUP_MASK_REF = BIT(1),
/** add entry if not existing */
- CFS_HS_LOOKUP_MASK_ADD = BIT(2),
+ CFS_HS_LOOKUP_MASK_ADD = BIT(2),
/** delete entry, ignore other masks */
- CFS_HS_LOOKUP_MASK_DEL = BIT(3),
+ CFS_HS_LOOKUP_MASK_DEL = BIT(3),
};
enum cfs_hash_lookup_intent {
/** return item w/o refcount */
- CFS_HS_LOOKUP_IT_PEEK = CFS_HS_LOOKUP_MASK_FIND,
+ CFS_HS_LOOKUP_IT_PEEK = CFS_HS_LOOKUP_MASK_FIND,
/** return item with refcount */
- CFS_HS_LOOKUP_IT_FIND = (CFS_HS_LOOKUP_MASK_FIND |
- CFS_HS_LOOKUP_MASK_REF),
+ CFS_HS_LOOKUP_IT_FIND = (CFS_HS_LOOKUP_MASK_FIND |
+ CFS_HS_LOOKUP_MASK_REF),
/** return item w/o refcount if existed, otherwise add */
- CFS_HS_LOOKUP_IT_ADD = (CFS_HS_LOOKUP_MASK_FIND |
- CFS_HS_LOOKUP_MASK_ADD),
+ CFS_HS_LOOKUP_IT_ADD = (CFS_HS_LOOKUP_MASK_FIND |
+ CFS_HS_LOOKUP_MASK_ADD),
/** return item with refcount if existed, otherwise add */
- CFS_HS_LOOKUP_IT_FINDADD = (CFS_HS_LOOKUP_IT_FIND |
- CFS_HS_LOOKUP_MASK_ADD),
+ CFS_HS_LOOKUP_IT_FINDADD = (CFS_HS_LOOKUP_IT_FIND |
+ CFS_HS_LOOKUP_MASK_ADD),
/** delete if existed */
- CFS_HS_LOOKUP_IT_FINDDEL = (CFS_HS_LOOKUP_MASK_FIND |
- CFS_HS_LOOKUP_MASK_DEL)
+ CFS_HS_LOOKUP_IT_FINDDEL = (CFS_HS_LOOKUP_MASK_FIND |
+ CFS_HS_LOOKUP_MASK_DEL)
};
static struct hlist_node *
@@ -629,10 +629,10 @@ cfs_hash_bd_lookup_intent(struct cfs_hash *hs, struct cfs_hash_bd *bd,
enum cfs_hash_lookup_intent intent)
{
- struct hlist_head *hhead = cfs_hash_bd_hhead(hs, bd);
- struct hlist_node *ehnode;
- struct hlist_node *match;
- int intent_add = (intent & CFS_HS_LOOKUP_MASK_ADD) != 0;
+ struct hlist_head *hhead = cfs_hash_bd_hhead(hs, bd);
+ struct hlist_node *ehnode;
+ struct hlist_node *match;
+ int intent_add = (intent & CFS_HS_LOOKUP_MASK_ADD) != 0;
/* with this function, we can avoid a lot of useless refcount ops,
* which are expensive atomic operations most time. */
@@ -665,7 +665,8 @@ cfs_hash_bd_lookup_intent(struct cfs_hash *hs, struct cfs_hash_bd *bd,
}
struct hlist_node *
-cfs_hash_bd_lookup_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd, const void *key)
+cfs_hash_bd_lookup_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ const void *key)
{
return cfs_hash_bd_lookup_intent(hs, bd, key, NULL,
CFS_HS_LOOKUP_IT_FIND);
@@ -673,40 +674,20 @@ cfs_hash_bd_lookup_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd, const voi
EXPORT_SYMBOL(cfs_hash_bd_lookup_locked);
struct hlist_node *
-cfs_hash_bd_peek_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd, const void *key)
+cfs_hash_bd_peek_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ const void *key)
{
return cfs_hash_bd_lookup_intent(hs, bd, key, NULL,
CFS_HS_LOOKUP_IT_PEEK);
}
EXPORT_SYMBOL(cfs_hash_bd_peek_locked);
-struct hlist_node *
-cfs_hash_bd_findadd_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd,
- const void *key, struct hlist_node *hnode,
- int noref)
-{
- return cfs_hash_bd_lookup_intent(hs, bd, key, hnode,
- (!noref * CFS_HS_LOOKUP_MASK_REF) |
- CFS_HS_LOOKUP_IT_ADD);
-}
-EXPORT_SYMBOL(cfs_hash_bd_findadd_locked);
-
-struct hlist_node *
-cfs_hash_bd_finddel_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd,
- const void *key, struct hlist_node *hnode)
-{
- /* hnode can be NULL, we find the first item with @key */
- return cfs_hash_bd_lookup_intent(hs, bd, key, hnode,
- CFS_HS_LOOKUP_IT_FINDDEL);
-}
-EXPORT_SYMBOL(cfs_hash_bd_finddel_locked);
-
static void
cfs_hash_multi_bd_lock(struct cfs_hash *hs, struct cfs_hash_bd *bds,
unsigned n, int excl)
{
struct cfs_hash_bucket *prev = NULL;
- int i;
+ int i;
/**
* bds must be ascendantly ordered by bd->bd_bucket->hsb_index.
@@ -729,7 +710,7 @@ cfs_hash_multi_bd_unlock(struct cfs_hash *hs, struct cfs_hash_bd *bds,
unsigned n, int excl)
{
struct cfs_hash_bucket *prev = NULL;
- int i;
+ int i;
cfs_hash_for_each_bd(bds, n, i) {
if (prev != bds[i].bd_bucket) {
@@ -743,8 +724,8 @@ static struct hlist_node *
cfs_hash_multi_bd_lookup_locked(struct cfs_hash *hs, struct cfs_hash_bd *bds,
unsigned n, const void *key)
{
- struct hlist_node *ehnode;
- unsigned i;
+ struct hlist_node *ehnode;
+ unsigned i;
cfs_hash_for_each_bd(bds, n, i) {
ehnode = cfs_hash_bd_lookup_intent(hs, &bds[i], key, NULL,
@@ -756,13 +737,13 @@ cfs_hash_multi_bd_lookup_locked(struct cfs_hash *hs, struct cfs_hash_bd *bds,
}
static struct hlist_node *
-cfs_hash_multi_bd_findadd_locked(struct cfs_hash *hs,
- struct cfs_hash_bd *bds, unsigned n, const void *key,
+cfs_hash_multi_bd_findadd_locked(struct cfs_hash *hs, struct cfs_hash_bd *bds,
+ unsigned n, const void *key,
struct hlist_node *hnode, int noref)
{
- struct hlist_node *ehnode;
- int intent;
- unsigned i;
+ struct hlist_node *ehnode;
+ int intent;
+ unsigned i;
LASSERT(hnode != NULL);
intent = (!noref * CFS_HS_LOOKUP_MASK_REF) | CFS_HS_LOOKUP_IT_PEEK;
@@ -777,7 +758,7 @@ cfs_hash_multi_bd_findadd_locked(struct cfs_hash *hs,
if (i == 1) { /* only one bucket */
cfs_hash_bd_add_locked(hs, &bds[0], hnode);
} else {
- struct cfs_hash_bd mybd;
+ struct cfs_hash_bd mybd;
cfs_hash_bd_get(hs, key, &mybd);
cfs_hash_bd_add_locked(hs, &mybd, hnode);
@@ -791,8 +772,8 @@ cfs_hash_multi_bd_finddel_locked(struct cfs_hash *hs, struct cfs_hash_bd *bds,
unsigned n, const void *key,
struct hlist_node *hnode)
{
- struct hlist_node *ehnode;
- unsigned i;
+ struct hlist_node *ehnode;
+ unsigned int i;
cfs_hash_for_each_bd(bds, n, i) {
ehnode = cfs_hash_bd_lookup_intent(hs, &bds[i], key, hnode,
@@ -806,7 +787,7 @@ cfs_hash_multi_bd_finddel_locked(struct cfs_hash *hs, struct cfs_hash_bd *bds,
static void
cfs_hash_bd_order(struct cfs_hash_bd *bd1, struct cfs_hash_bd *bd2)
{
- int rc;
+ int rc;
if (bd2->bd_bucket == NULL)
return;
@@ -831,7 +812,8 @@ cfs_hash_bd_order(struct cfs_hash_bd *bd1, struct cfs_hash_bd *bd2)
}
void
-cfs_hash_dual_bd_get(struct cfs_hash *hs, const void *key, struct cfs_hash_bd *bds)
+cfs_hash_dual_bd_get(struct cfs_hash *hs, const void *key,
+ struct cfs_hash_bd *bds)
{
/* NB: caller should hold hs_lock.rw if REHASH is set */
cfs_hash_bd_from_key(hs, hs->hs_buckets,
@@ -848,21 +830,18 @@ cfs_hash_dual_bd_get(struct cfs_hash *hs, const void *key, struct cfs_hash_bd *b
cfs_hash_bd_order(&bds[0], &bds[1]);
}
-EXPORT_SYMBOL(cfs_hash_dual_bd_get);
void
cfs_hash_dual_bd_lock(struct cfs_hash *hs, struct cfs_hash_bd *bds, int excl)
{
cfs_hash_multi_bd_lock(hs, bds, 2, excl);
}
-EXPORT_SYMBOL(cfs_hash_dual_bd_lock);
void
cfs_hash_dual_bd_unlock(struct cfs_hash *hs, struct cfs_hash_bd *bds, int excl)
{
cfs_hash_multi_bd_unlock(hs, bds, 2, excl);
}
-EXPORT_SYMBOL(cfs_hash_dual_bd_unlock);
struct hlist_node *
cfs_hash_dual_bd_lookup_locked(struct cfs_hash *hs, struct cfs_hash_bd *bds,
@@ -870,7 +849,6 @@ cfs_hash_dual_bd_lookup_locked(struct cfs_hash *hs, struct cfs_hash_bd *bds,
{
return cfs_hash_multi_bd_lookup_locked(hs, bds, 2, key);
}
-EXPORT_SYMBOL(cfs_hash_dual_bd_lookup_locked);
struct hlist_node *
cfs_hash_dual_bd_findadd_locked(struct cfs_hash *hs, struct cfs_hash_bd *bds,
@@ -880,7 +858,6 @@ cfs_hash_dual_bd_findadd_locked(struct cfs_hash *hs, struct cfs_hash_bd *bds,
return cfs_hash_multi_bd_findadd_locked(hs, bds, 2, key,
hnode, noref);
}
-EXPORT_SYMBOL(cfs_hash_dual_bd_findadd_locked);
struct hlist_node *
cfs_hash_dual_bd_finddel_locked(struct cfs_hash *hs, struct cfs_hash_bd *bds,
@@ -888,13 +865,12 @@ cfs_hash_dual_bd_finddel_locked(struct cfs_hash *hs, struct cfs_hash_bd *bds,
{
return cfs_hash_multi_bd_finddel_locked(hs, bds, 2, key, hnode);
}
-EXPORT_SYMBOL(cfs_hash_dual_bd_finddel_locked);
static void
cfs_hash_buckets_free(struct cfs_hash_bucket **buckets,
int bkt_size, int prev_size, int size)
{
- int i;
+ int i;
for (i = prev_size; i < size; i++) {
if (buckets[i] != NULL)
@@ -914,7 +890,7 @@ cfs_hash_buckets_realloc(struct cfs_hash *hs, struct cfs_hash_bucket **old_bkts,
unsigned int old_size, unsigned int new_size)
{
struct cfs_hash_bucket **new_bkts;
- int i;
+ int i;
LASSERT(old_size == 0 || old_bkts != NULL);
@@ -932,7 +908,7 @@ cfs_hash_buckets_realloc(struct cfs_hash *hs, struct cfs_hash_bucket **old_bkts,
for (i = old_size; i < new_size; i++) {
struct hlist_head *hhead;
- struct cfs_hash_bd bd;
+ struct cfs_hash_bd bd;
LIBCFS_ALLOC(new_bkts[i], cfs_hash_bkt_size(hs));
if (new_bkts[i] == NULL) {
@@ -969,7 +945,7 @@ cfs_hash_buckets_realloc(struct cfs_hash *hs, struct cfs_hash_bucket **old_bkts,
* @max_bits - Maximum allowed hash table resize, in bits
* @ops - Registered hash table operations
* @flags - CFS_HASH_REHASH enable synamic hash resizing
- * - CFS_HASH_SORT enable chained hash sort
+ * - CFS_HASH_SORT enable chained hash sort
*/
static int cfs_hash_rehash_worker(cfs_workitem_t *wi);
@@ -977,10 +953,10 @@ static int cfs_hash_rehash_worker(cfs_workitem_t *wi);
static int cfs_hash_dep_print(cfs_workitem_t *wi)
{
struct cfs_hash *hs = container_of(wi, struct cfs_hash, hs_dep_wi);
- int dep;
- int bkt;
- int off;
- int bits;
+ int dep;
+ int bkt;
+ int off;
+ int bits;
spin_lock(&hs->hs_dep_lock);
dep = hs->hs_dep_max;
@@ -1031,7 +1007,7 @@ cfs_hash_create(char *name, unsigned cur_bits, unsigned max_bits,
struct cfs_hash_ops *ops, unsigned flags)
{
struct cfs_hash *hs;
- int len;
+ int len;
CLASSERT(CFS_HASH_THETA_BITS < 15);
@@ -1062,8 +1038,7 @@ cfs_hash_create(char *name, unsigned cur_bits, unsigned max_bits,
if (hs == NULL)
return NULL;
- strncpy(hs->hs_name, name, len);
- hs->hs_name[len - 1] = '\0';
+ strlcpy(hs->hs_name, name, len);
hs->hs_flags = flags;
atomic_set(&hs->hs_refcount, 1);
@@ -1077,7 +1052,7 @@ cfs_hash_create(char *name, unsigned cur_bits, unsigned max_bits,
hs->hs_max_bits = (__u8)max_bits;
hs->hs_bkt_bits = (__u8)bkt_bits;
- hs->hs_ops = ops;
+ hs->hs_ops = ops;
hs->hs_extra_bytes = extra_bytes;
hs->hs_rehash_bits = 0;
cfs_wi_init(&hs->hs_rehash_wi, hs, cfs_hash_rehash_worker);
@@ -1102,10 +1077,10 @@ EXPORT_SYMBOL(cfs_hash_create);
static void
cfs_hash_destroy(struct cfs_hash *hs)
{
- struct hlist_node *hnode;
- struct hlist_node *pos;
- struct cfs_hash_bd bd;
- int i;
+ struct hlist_node *hnode;
+ struct hlist_node *pos;
+ struct cfs_hash_bd bd;
+ int i;
LASSERT(hs != NULL);
LASSERT(!cfs_hash_is_exiting(hs) &&
@@ -1223,8 +1198,8 @@ cfs_hash_rehash_inline(struct cfs_hash *hs)
void
cfs_hash_add(struct cfs_hash *hs, const void *key, struct hlist_node *hnode)
{
- struct cfs_hash_bd bd;
- int bits;
+ struct cfs_hash_bd bd;
+ int bits;
LASSERT(hlist_unhashed(hnode));
@@ -1248,8 +1223,8 @@ cfs_hash_find_or_add(struct cfs_hash *hs, const void *key,
struct hlist_node *hnode, int noref)
{
struct hlist_node *ehnode;
- struct cfs_hash_bd bds[2];
- int bits = 0;
+ struct cfs_hash_bd bds[2];
+ int bits = 0;
LASSERT(hlist_unhashed(hnode));
@@ -1261,7 +1236,7 @@ cfs_hash_find_or_add(struct cfs_hash *hs, const void *key,
hnode, noref);
cfs_hash_dual_bd_unlock(hs, bds, 1);
- if (ehnode == hnode) /* new item added */
+ if (ehnode == hnode) /* new item added */
bits = cfs_hash_rehash_bits(hs);
cfs_hash_unlock(hs, 0);
if (bits > 0)
@@ -1276,7 +1251,8 @@ cfs_hash_find_or_add(struct cfs_hash *hs, const void *key,
* Returns 0 on success or -EALREADY on key collisions.
*/
int
-cfs_hash_add_unique(struct cfs_hash *hs, const void *key, struct hlist_node *hnode)
+cfs_hash_add_unique(struct cfs_hash *hs, const void *key,
+ struct hlist_node *hnode)
{
return cfs_hash_find_or_add(hs, key, hnode, 1) != hnode ?
-EALREADY : 0;
@@ -1309,9 +1285,9 @@ EXPORT_SYMBOL(cfs_hash_findadd_unique);
void *
cfs_hash_del(struct cfs_hash *hs, const void *key, struct hlist_node *hnode)
{
- void *obj = NULL;
- int bits = 0;
- struct cfs_hash_bd bds[2];
+ void *obj = NULL;
+ int bits = 0;
+ struct cfs_hash_bd bds[2];
cfs_hash_lock(hs, 0);
cfs_hash_dual_bd_get_and_lock(hs, key, bds, 1);
@@ -1364,9 +1340,9 @@ EXPORT_SYMBOL(cfs_hash_del_key);
void *
cfs_hash_lookup(struct cfs_hash *hs, const void *key)
{
- void *obj = NULL;
- struct hlist_node *hnode;
- struct cfs_hash_bd bds[2];
+ void *obj = NULL;
+ struct hlist_node *hnode;
+ struct cfs_hash_bd bds[2];
cfs_hash_lock(hs, 0);
cfs_hash_dual_bd_get_and_lock(hs, key, bds, 0);
@@ -1383,7 +1359,8 @@ cfs_hash_lookup(struct cfs_hash *hs, const void *key)
EXPORT_SYMBOL(cfs_hash_lookup);
static void
-cfs_hash_for_each_enter(struct cfs_hash *hs) {
+cfs_hash_for_each_enter(struct cfs_hash *hs)
+{
LASSERT(!cfs_hash_is_exiting(hs));
if (!cfs_hash_with_rehash(hs))
@@ -1408,7 +1385,8 @@ cfs_hash_for_each_enter(struct cfs_hash *hs) {
}
static void
-cfs_hash_for_each_exit(struct cfs_hash *hs) {
+cfs_hash_for_each_exit(struct cfs_hash *hs)
+{
int remained;
int bits;
@@ -1439,14 +1417,15 @@ cfs_hash_for_each_exit(struct cfs_hash *hs) {
*/
static __u64
cfs_hash_for_each_tight(struct cfs_hash *hs, cfs_hash_for_each_cb_t func,
- void *data, int remove_safe) {
- struct hlist_node *hnode;
- struct hlist_node *pos;
- struct cfs_hash_bd bd;
- __u64 count = 0;
- int excl = !!remove_safe;
- int loop = 0;
- int i;
+ void *data, int remove_safe)
+{
+ struct hlist_node *hnode;
+ struct hlist_node *pos;
+ struct cfs_hash_bd bd;
+ __u64 count = 0;
+ int excl = !!remove_safe;
+ int loop = 0;
+ int i;
cfs_hash_for_each_enter(hs);
@@ -1514,8 +1493,8 @@ void
cfs_hash_cond_del(struct cfs_hash *hs, cfs_hash_cond_opt_cb_t func, void *data)
{
struct cfs_hash_cond_arg arg = {
- .func = func,
- .arg = data,
+ .func = func,
+ .arg = data,
};
cfs_hash_for_each_tight(hs, cfs_hash_cond_del_locked, &arg, 1);
@@ -1523,16 +1502,17 @@ cfs_hash_cond_del(struct cfs_hash *hs, cfs_hash_cond_opt_cb_t func, void *data)
EXPORT_SYMBOL(cfs_hash_cond_del);
void
-cfs_hash_for_each(struct cfs_hash *hs,
- cfs_hash_for_each_cb_t func, void *data)
+cfs_hash_for_each(struct cfs_hash *hs, cfs_hash_for_each_cb_t func,
+ void *data)
{
cfs_hash_for_each_tight(hs, func, data, 0);
}
EXPORT_SYMBOL(cfs_hash_for_each);
void
-cfs_hash_for_each_safe(struct cfs_hash *hs,
- cfs_hash_for_each_cb_t func, void *data) {
+cfs_hash_for_each_safe(struct cfs_hash *hs, cfs_hash_for_each_cb_t func,
+ void *data)
+{
cfs_hash_for_each_tight(hs, func, data, 1);
}
EXPORT_SYMBOL(cfs_hash_for_each_safe);
@@ -1581,15 +1561,16 @@ EXPORT_SYMBOL(cfs_hash_size_get);
*/
static int
cfs_hash_for_each_relax(struct cfs_hash *hs, cfs_hash_for_each_cb_t func,
- void *data) {
+ void *data)
+{
struct hlist_node *hnode;
struct hlist_node *tmp;
- struct cfs_hash_bd bd;
- __u32 version;
- int count = 0;
- int stop_on_change;
- int rc;
- int i;
+ struct cfs_hash_bd bd;
+ __u32 version;
+ int count = 0;
+ int stop_on_change;
+ int rc;
+ int i;
stop_on_change = cfs_hash_with_rehash_key(hs) ||
!cfs_hash_with_no_itemref(hs) ||
@@ -1645,8 +1626,9 @@ cfs_hash_for_each_relax(struct cfs_hash *hs, cfs_hash_for_each_cb_t func,
}
int
-cfs_hash_for_each_nolock(struct cfs_hash *hs,
- cfs_hash_for_each_cb_t func, void *data) {
+cfs_hash_for_each_nolock(struct cfs_hash *hs, cfs_hash_for_each_cb_t func,
+ void *data)
+{
if (cfs_hash_with_no_lock(hs) ||
cfs_hash_with_rehash_key(hs) ||
!cfs_hash_with_no_itemref(hs))
@@ -1677,9 +1659,10 @@ EXPORT_SYMBOL(cfs_hash_for_each_nolock);
* the required locking is in place to prevent concurrent insertions.
*/
int
-cfs_hash_for_each_empty(struct cfs_hash *hs,
- cfs_hash_for_each_cb_t func, void *data) {
- unsigned i = 0;
+cfs_hash_for_each_empty(struct cfs_hash *hs, cfs_hash_for_each_cb_t func,
+ void *data)
+{
+ unsigned i = 0;
if (cfs_hash_with_no_lock(hs))
return -EOPNOTSUPP;
@@ -1703,9 +1686,9 @@ void
cfs_hash_hlist_for_each(struct cfs_hash *hs, unsigned hindex,
cfs_hash_for_each_cb_t func, void *data)
{
- struct hlist_head *hhead;
- struct hlist_node *hnode;
- struct cfs_hash_bd bd;
+ struct hlist_head *hhead;
+ struct hlist_node *hnode;
+ struct cfs_hash_bd bd;
cfs_hash_for_each_enter(hs);
cfs_hash_lock(hs, 0);
@@ -1721,7 +1704,7 @@ cfs_hash_hlist_for_each(struct cfs_hash *hs, unsigned hindex,
break;
}
cfs_hash_bd_unlock(hs, &bd, 0);
- out:
+out:
cfs_hash_unlock(hs, 0);
cfs_hash_for_each_exit(hs);
}
@@ -1736,10 +1719,11 @@ EXPORT_SYMBOL(cfs_hash_hlist_for_each);
*/
void
cfs_hash_for_each_key(struct cfs_hash *hs, const void *key,
- cfs_hash_for_each_cb_t func, void *data) {
- struct hlist_node *hnode;
- struct cfs_hash_bd bds[2];
- unsigned i;
+ cfs_hash_for_each_cb_t func, void *data)
+{
+ struct hlist_node *hnode;
+ struct cfs_hash_bd bds[2];
+ unsigned int i;
cfs_hash_lock(hs, 0);
@@ -1777,7 +1761,7 @@ EXPORT_SYMBOL(cfs_hash_for_each_key);
void
cfs_hash_rehash_cancel_locked(struct cfs_hash *hs)
{
- int i;
+ int i;
/* need hold cfs_hash_lock(hs, 1) */
LASSERT(cfs_hash_with_rehash(hs) &&
@@ -1794,14 +1778,13 @@ cfs_hash_rehash_cancel_locked(struct cfs_hash *hs)
for (i = 2; cfs_hash_is_rehashing(hs); i++) {
cfs_hash_unlock(hs, 1);
/* raise console warning while waiting too long */
- CDEBUG(IS_PO2(i >> 3) ? D_WARNING : D_INFO,
+ CDEBUG(is_power_of_2(i >> 3) ? D_WARNING : D_INFO,
"hash %s is still rehashing, rescheded %d\n",
hs->hs_name, i - 1);
cond_resched();
cfs_hash_lock(hs, 1);
}
}
-EXPORT_SYMBOL(cfs_hash_rehash_cancel_locked);
void
cfs_hash_rehash_cancel(struct cfs_hash *hs)
@@ -1810,12 +1793,11 @@ cfs_hash_rehash_cancel(struct cfs_hash *hs)
cfs_hash_rehash_cancel_locked(hs);
cfs_hash_unlock(hs, 1);
}
-EXPORT_SYMBOL(cfs_hash_rehash_cancel);
int
cfs_hash_rehash(struct cfs_hash *hs, int do_rehash)
{
- int rc;
+ int rc;
LASSERT(cfs_hash_with_rehash(hs) && !cfs_hash_with_no_lock(hs));
@@ -1840,17 +1822,16 @@ cfs_hash_rehash(struct cfs_hash *hs, int do_rehash)
return cfs_hash_rehash_worker(&hs->hs_rehash_wi);
}
-EXPORT_SYMBOL(cfs_hash_rehash);
static int
cfs_hash_rehash_bd(struct cfs_hash *hs, struct cfs_hash_bd *old)
{
- struct cfs_hash_bd new;
- struct hlist_head *hhead;
- struct hlist_node *hnode;
- struct hlist_node *pos;
- void *key;
- int c = 0;
+ struct cfs_hash_bd new;
+ struct hlist_head *hhead;
+ struct hlist_node *hnode;
+ struct hlist_node *pos;
+ void *key;
+ int c = 0;
/* hold cfs_hash_lock(hs, 1), so don't need any bucket lock */
cfs_hash_bd_for_each_hlist(hs, old, hhead) {
@@ -1876,17 +1857,17 @@ cfs_hash_rehash_bd(struct cfs_hash *hs, struct cfs_hash_bd *old)
static int
cfs_hash_rehash_worker(cfs_workitem_t *wi)
{
- struct cfs_hash *hs = container_of(wi, struct cfs_hash, hs_rehash_wi);
+ struct cfs_hash *hs = container_of(wi, struct cfs_hash, hs_rehash_wi);
struct cfs_hash_bucket **bkts;
- struct cfs_hash_bd bd;
- unsigned int old_size;
- unsigned int new_size;
- int bsize;
- int count = 0;
- int rc = 0;
- int i;
+ struct cfs_hash_bd bd;
+ unsigned int old_size;
+ unsigned int new_size;
+ int bsize;
+ int count = 0;
+ int rc = 0;
+ int i;
- LASSERT (hs != NULL && cfs_hash_with_rehash(hs));
+ LASSERT(hs != NULL && cfs_hash_with_rehash(hs));
cfs_hash_lock(hs, 0);
LASSERT(cfs_hash_is_rehashing(hs));
@@ -1958,7 +1939,7 @@ cfs_hash_rehash_worker(cfs_workitem_t *wi)
hs->hs_rehash_buckets = NULL;
hs->hs_cur_bits = hs->hs_rehash_bits;
- out:
+out:
hs->hs_rehash_bits = 0;
if (rc == -ESRCH) /* never be scheduled again */
cfs_wi_exit(cfs_sched_rehash, wi);
@@ -1986,9 +1967,9 @@ cfs_hash_rehash_worker(cfs_workitem_t *wi)
void cfs_hash_rehash_key(struct cfs_hash *hs, const void *old_key,
void *new_key, struct hlist_node *hnode)
{
- struct cfs_hash_bd bds[3];
- struct cfs_hash_bd old_bds[2];
- struct cfs_hash_bd new_bd;
+ struct cfs_hash_bd bds[3];
+ struct cfs_hash_bd old_bds[2];
+ struct cfs_hash_bd new_bd;
LASSERT(!hlist_unhashed(hnode));
@@ -2014,7 +1995,7 @@ void cfs_hash_rehash_key(struct cfs_hash *hs, const void *old_key,
}
/* overwrite key inside locks, otherwise may screw up with
* other operations, i.e: rehash */
- cfs_hash_keycpy(hs, new_key, hnode);
+ cfs_hash_keycpy(hs, hnode, new_key);
cfs_hash_multi_bd_unlock(hs, bds, 3, 1);
cfs_hash_unlock(hs, 0);
@@ -2054,12 +2035,12 @@ cfs_hash_full_nbkt(struct cfs_hash *hs)
void cfs_hash_debug_str(struct cfs_hash *hs, struct seq_file *m)
{
- int dist[8] = { 0, };
- int maxdep = -1;
- int maxdepb = -1;
- int total = 0;
- int theta;
- int i;
+ int dist[8] = { 0, };
+ int maxdep = -1;
+ int maxdepb = -1;
+ int total = 0;
+ int theta;
+ int i;
cfs_hash_lock(hs, 0);
theta = __cfs_hash_theta(hs);
@@ -2085,11 +2066,11 @@ void cfs_hash_debug_str(struct cfs_hash *hs, struct seq_file *m)
* If you hash function results in a non-uniform hash the will
* be observable by outlier bucks in the distribution histogram.
*
- * Uniform hash distribution: 128/128/0/0/0/0/0/0
- * Non-Uniform hash distribution: 128/125/0/0/0/0/2/1
+ * Uniform hash distribution: 128/128/0/0/0/0/0/0
+ * Non-Uniform hash distribution: 128/125/0/0/0/0/2/1
*/
for (i = 0; i < cfs_hash_full_nbkt(hs); i++) {
- struct cfs_hash_bd bd;
+ struct cfs_hash_bd bd;
bd.bd_bucket = cfs_hash_full_bkts(hs)[i];
cfs_hash_bd_lock(hs, &bd, 0);
diff --git a/drivers/staging/lustre/lustre/libcfs/kernel_user_comm.c b/drivers/staging/lustre/lustre/libcfs/kernel_user_comm.c
index ad661a33a211..d8230aec9a2b 100644
--- a/drivers/staging/lustre/lustre/libcfs/kernel_user_comm.c
+++ b/drivers/staging/lustre/lustre/libcfs/kernel_user_comm.c
@@ -110,7 +110,8 @@ static DECLARE_RWSEM(kg_sem);
* @param uid identifier for this receiver
* @param group group number
*/
-int libcfs_kkuc_group_add(struct file *filp, int uid, int group, __u32 data)
+int libcfs_kkuc_group_add(struct file *filp, int uid, unsigned int group,
+ __u32 data)
{
struct kkuc_reg *reg;
diff --git a/drivers/staging/lustre/lustre/libcfs/libcfs_lock.c b/drivers/staging/lustre/lustre/libcfs/libcfs_lock.c
index 94bc00785000..15782d9e6aa9 100644
--- a/drivers/staging/lustre/lustre/libcfs/libcfs_lock.c
+++ b/drivers/staging/lustre/lustre/libcfs/libcfs_lock.c
@@ -21,7 +21,7 @@
* GPL HEADER END
*/
/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015 Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/libcfs/libcfs_mem.c b/drivers/staging/lustre/lustre/libcfs/libcfs_mem.c
index f4e08daba4d9..27cf86106363 100644
--- a/drivers/staging/lustre/lustre/libcfs/libcfs_mem.c
+++ b/drivers/staging/lustre/lustre/libcfs/libcfs_mem.c
@@ -134,7 +134,6 @@ cfs_percpt_current(void *vars)
return arr->va_ptrs[cpt];
}
-EXPORT_SYMBOL(cfs_percpt_current);
void *
cfs_percpt_index(void *vars, int idx)
@@ -146,7 +145,6 @@ cfs_percpt_index(void *vars, int idx)
LASSERT(idx >= 0 && idx < arr->va_count);
return arr->va_ptrs[idx];
}
-EXPORT_SYMBOL(cfs_percpt_index);
/*
* free variable array, see more detail in cfs_array_alloc
diff --git a/drivers/staging/lustre/lustre/libcfs/libcfs_string.c b/drivers/staging/lustre/lustre/libcfs/libcfs_string.c
index d40be5396769..205a3ed435a8 100644
--- a/drivers/staging/lustre/lustre/libcfs/libcfs_string.c
+++ b/drivers/staging/lustre/lustre/libcfs/libcfs_string.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015 Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/libcfs/linux/linux-cpu.c b/drivers/staging/lustre/lustre/libcfs/linux/linux-cpu.c
index 209736454d06..e52afe35e7ea 100644
--- a/drivers/staging/lustre/lustre/libcfs/linux/linux-cpu.c
+++ b/drivers/staging/lustre/lustre/libcfs/linux/linux-cpu.c
@@ -22,7 +22,8 @@
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, Intel Corporation.
+ *
+ * Copyright (c) 2012, 2015 Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -78,23 +79,6 @@ struct cfs_cpt_data {
static struct cfs_cpt_data cpt_data;
-static void cfs_cpu_core_siblings(int cpu, cpumask_t *mask)
-{
- /* return cpumask of cores in the same socket */
- cpumask_copy(mask, topology_core_cpumask(cpu));
-}
-
-/* return cpumask of HTs in the same core */
-static void cfs_cpu_ht_siblings(int cpu, cpumask_t *mask)
-{
- cpumask_copy(mask, topology_sibling_cpumask(cpu));
-}
-
-static void cfs_node_to_cpumask(int node, cpumask_t *mask)
-{
- cpumask_copy(mask, cpumask_of_node(node));
-}
-
void
cfs_cpt_table_free(struct cfs_cpt_table *cptab)
{
@@ -426,7 +410,7 @@ cfs_cpt_set_node(struct cfs_cpt_table *cptab, int cpt, int node)
mutex_lock(&cpt_data.cpt_mutex);
mask = cpt_data.cpt_cpumask;
- cfs_node_to_cpumask(node, mask);
+ cpumask_copy(mask, cpumask_of_node(node));
rc = cfs_cpt_set_cpumask(cptab, cpt, mask);
@@ -450,7 +434,7 @@ cfs_cpt_unset_node(struct cfs_cpt_table *cptab, int cpt, int node)
mutex_lock(&cpt_data.cpt_mutex);
mask = cpt_data.cpt_cpumask;
- cfs_node_to_cpumask(node, mask);
+ cpumask_copy(mask, cpumask_of_node(node));
cfs_cpt_unset_cpumask(cptab, cpt, mask);
@@ -643,7 +627,7 @@ cfs_cpt_choose_ncpus(struct cfs_cpt_table *cptab, int cpt,
cpu = cpumask_first(node);
/* get cpumask for cores in the same socket */
- cfs_cpu_core_siblings(cpu, socket);
+ cpumask_copy(socket, topology_core_cpumask(cpu));
cpumask_and(socket, socket, node);
LASSERT(!cpumask_empty(socket));
@@ -652,7 +636,7 @@ cfs_cpt_choose_ncpus(struct cfs_cpt_table *cptab, int cpt,
int i;
/* get cpumask for hts in the same core */
- cfs_cpu_ht_siblings(cpu, core);
+ cpumask_copy(core, topology_sibling_cpumask(cpu));
cpumask_and(core, core, node);
LASSERT(!cpumask_empty(core));
@@ -769,7 +753,7 @@ cfs_cpt_table_create(int ncpt)
}
for_each_online_node(i) {
- cfs_node_to_cpumask(i, mask);
+ cpumask_copy(mask, cpumask_of_node(i));
while (!cpumask_empty(mask)) {
struct cfs_cpu_partition *part;
@@ -968,7 +952,8 @@ cfs_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu)
mutex_lock(&cpt_data.cpt_mutex);
/* if all HTs in a core are offline, it may break affinity */
- cfs_cpu_ht_siblings(cpu, cpt_data.cpt_cpumask);
+ cpumask_copy(cpt_data.cpt_cpumask,
+ topology_sibling_cpumask(cpu));
warn = cpumask_any_and(cpt_data.cpt_cpumask,
cpu_online_mask) >= nr_cpu_ids;
mutex_unlock(&cpt_data.cpt_mutex);
diff --git a/drivers/staging/lustre/lustre/libcfs/linux/linux-crypto-adler.c b/drivers/staging/lustre/lustre/libcfs/linux/linux-crypto-adler.c
index 5d8d8b79fa1f..db0572733712 100644
--- a/drivers/staging/lustre/lustre/libcfs/linux/linux-crypto-adler.c
+++ b/drivers/staging/lustre/lustre/libcfs/linux/linux-crypto-adler.c
@@ -37,11 +37,6 @@
#define CHKSUM_BLOCK_SIZE 1
#define CHKSUM_DIGEST_SIZE 4
-static u32 __adler32(u32 cksum, unsigned char const *p, size_t len)
-{
- return zlib_adler32(cksum, p, len);
-}
-
static int adler32_cra_init(struct crypto_tfm *tfm)
{
u32 *key = crypto_tfm_ctx(tfm);
@@ -79,14 +74,14 @@ static int adler32_update(struct shash_desc *desc, const u8 *data,
{
u32 *cksump = shash_desc_ctx(desc);
- *cksump = __adler32(*cksump, data, len);
+ *cksump = zlib_adler32(*cksump, data, len);
return 0;
}
static int __adler32_finup(u32 *cksump, const u8 *data, unsigned int len,
u8 *out)
{
- *(u32 *)out = __adler32(*cksump, data, len);
+ *(u32 *)out = zlib_adler32(*cksump, data, len);
return 0;
}
diff --git a/drivers/staging/lustre/lustre/libcfs/linux/linux-curproc.c b/drivers/staging/lustre/lustre/libcfs/linux/linux-curproc.c
index c74c80915dca..68515d9130c1 100644
--- a/drivers/staging/lustre/lustre/libcfs/linux/linux-curproc.c
+++ b/drivers/staging/lustre/lustre/libcfs/linux/linux-curproc.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/libcfs/linux/linux-debug.c b/drivers/staging/lustre/lustre/libcfs/linux/linux-debug.c
index 8689ea757c99..59c7bf3cbc1f 100644
--- a/drivers/staging/lustre/lustre/libcfs/linux/linux-debug.c
+++ b/drivers/staging/lustre/lustre/libcfs/linux/linux-debug.c
@@ -195,6 +195,5 @@ void libcfs_unregister_panic_notifier(void)
atomic_notifier_chain_unregister(&panic_notifier_list, &libcfs_panic_notifier);
}
-EXPORT_SYMBOL(libcfs_run_upcall);
EXPORT_SYMBOL(libcfs_run_lbug_upcall);
EXPORT_SYMBOL(lbug_with_loc);
diff --git a/drivers/staging/lustre/lustre/libcfs/linux/linux-tracefile.h b/drivers/staging/lustre/lustre/libcfs/linux/linux-tracefile.h
deleted file mode 100644
index ba84e4ffddd1..000000000000
--- a/drivers/staging/lustre/lustre/libcfs/linux/linux-tracefile.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * GPL HEADER START
- *
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 only,
- * 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 version 2 for more details (a copy is included
- * in the LICENSE file that accompanied this code).
- *
- * You should have received a copy of the GNU General Public License
- * version 2 along with this program; If not, see
- * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
- *
- * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
- *
- * GPL HEADER END
- */
-/*
- * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- * Use is subject to license terms.
- */
-/*
- * This file is part of Lustre, http://www.lustre.org/
- * Lustre is a trademark of Sun Microsystems, Inc.
- */
-
-#ifndef __LIBCFS_LINUX_TRACEFILE_H__
-#define __LIBCFS_LINUX_TRACEFILE_H__
-
-/**
- * three types of trace_data in linux
- */
-typedef enum {
- CFS_TCD_TYPE_PROC = 0,
- CFS_TCD_TYPE_SOFTIRQ,
- CFS_TCD_TYPE_IRQ,
- CFS_TCD_TYPE_MAX
-} cfs_trace_buf_type_t;
-
-#endif
diff --git a/drivers/staging/lustre/lustre/libcfs/module.c b/drivers/staging/lustre/lustre/libcfs/module.c
index 07a68594c279..329d78ce272d 100644
--- a/drivers/staging/lustre/lustre/libcfs/module.c
+++ b/drivers/staging/lustre/lustre/libcfs/module.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015 Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -62,7 +62,7 @@
#include "../../include/linux/lnet/lnet.h"
#include "tracefile.h"
-MODULE_AUTHOR("Peter J. Braam <braam@clusterfs.com>");
+MODULE_AUTHOR("OpenSFS, Inc. <http://www.lustre.org/>");
MODULE_DESCRIPTION("Portals v3.1");
MODULE_LICENSE("GPL");
@@ -274,23 +274,6 @@ static int libcfs_ioctl_int(struct cfs_psdev_file *pfile, unsigned long cmd,
}
break;
- case IOC_LIBCFS_PING_TEST: {
- extern void (kping_client)(struct libcfs_ioctl_data *);
- void (*ping)(struct libcfs_ioctl_data *);
-
- CDEBUG(D_IOCTL, "doing %d pings to nid %s (%s)\n",
- data->ioc_count, libcfs_nid2str(data->ioc_nid),
- libcfs_nid2str(data->ioc_nid));
- ping = symbol_get(kping_client);
- if (!ping)
- CERROR("symbol_get failed\n");
- else {
- ping(data);
- symbol_put(kping_client);
- }
- return 0;
- }
-
default: {
struct libcfs_ioctl_handler *hand;
@@ -392,7 +375,7 @@ static int __proc_dobitmasks(void *data, int write,
} else {
rc = cfs_trace_copyin_string(tmpstr, tmpstrlen, buffer, nob);
if (rc < 0) {
- cfs_trace_free_string_buffer(tmpstr, tmpstrlen);
+ kfree(tmpstr);
return rc;
}
@@ -402,7 +385,7 @@ static int __proc_dobitmasks(void *data, int write,
*mask |= D_EMERG;
}
- cfs_trace_free_string_buffer(tmpstr, tmpstrlen);
+ kfree(tmpstr);
return rc;
}
diff --git a/drivers/staging/lustre/lustre/libcfs/tracefile.c b/drivers/staging/lustre/lustre/libcfs/tracefile.c
index f2d018d7823c..65c4f1ab0de8 100644
--- a/drivers/staging/lustre/lustre/libcfs/tracefile.c
+++ b/drivers/staging/lustre/lustre/libcfs/tracefile.c
@@ -199,7 +199,6 @@ static void cfs_tcd_shrink(struct cfs_trace_cpu_data *tcd)
pgcount + 1, tcd->tcd_cur_pages);
INIT_LIST_HEAD(&pc.pc_pages);
- spin_lock_init(&pc.pc_lock);
list_for_each_entry_safe(tage, tmp, &tcd->tcd_pages, linkage) {
if (pgcount-- == 0)
@@ -451,7 +450,7 @@ console:
cfs_print_to_console(&header, mask,
string_buf, needed, file, msgdata->msg_fn);
- cfs_trace_put_console_buffer(string_buf);
+ put_cpu();
}
if (cdls != NULL && cdls->cdls_count != 0) {
@@ -465,7 +464,7 @@ console:
cfs_print_to_console(&header, mask,
string_buf, needed, file, msgdata->msg_fn);
- cfs_trace_put_console_buffer(string_buf);
+ put_cpu();
cdls->cdls_count = 0;
}
@@ -522,7 +521,6 @@ static void collect_pages_on_all_cpus(struct page_collection *pc)
struct cfs_trace_cpu_data *tcd;
int i, cpu;
- spin_lock(&pc->pc_lock);
for_each_possible_cpu(cpu) {
cfs_tcd_for_each_type_lock(tcd, i, cpu) {
list_splice_init(&tcd->tcd_pages, &pc->pc_pages);
@@ -534,7 +532,6 @@ static void collect_pages_on_all_cpus(struct page_collection *pc)
}
}
}
- spin_unlock(&pc->pc_lock);
}
static void collect_pages(struct page_collection *pc)
@@ -555,7 +552,6 @@ static void put_pages_back_on_all_cpus(struct page_collection *pc)
struct cfs_trace_page *tmp;
int i, cpu;
- spin_lock(&pc->pc_lock);
for_each_possible_cpu(cpu) {
cfs_tcd_for_each_type_lock(tcd, i, cpu) {
cur_head = tcd->tcd_pages.next;
@@ -573,7 +569,6 @@ static void put_pages_back_on_all_cpus(struct page_collection *pc)
}
}
}
- spin_unlock(&pc->pc_lock);
}
static void put_pages_back(struct page_collection *pc)
@@ -592,7 +587,6 @@ static void put_pages_on_tcd_daemon_list(struct page_collection *pc,
struct cfs_trace_page *tage;
struct cfs_trace_page *tmp;
- spin_lock(&pc->pc_lock);
list_for_each_entry_safe(tage, tmp, &pc->pc_pages, linkage) {
__LASSERT_TAGE_INVARIANT(tage);
@@ -616,7 +610,6 @@ static void put_pages_on_tcd_daemon_list(struct page_collection *pc,
tcd->tcd_cur_daemon_pages--;
}
}
- spin_unlock(&pc->pc_lock);
}
static void put_pages_on_daemon_list(struct page_collection *pc)
@@ -636,8 +629,6 @@ void cfs_trace_debug_print(void)
struct cfs_trace_page *tage;
struct cfs_trace_page *tmp;
- spin_lock_init(&pc.pc_lock);
-
pc.pc_want_daemon_pages = 1;
collect_pages(&pc);
list_for_each_entry_safe(tage, tmp, &pc.pc_pages, linkage) {
@@ -692,7 +683,6 @@ int cfs_tracefile_dump_all_pages(char *filename)
goto out;
}
- spin_lock_init(&pc.pc_lock);
pc.pc_want_daemon_pages = 1;
collect_pages(&pc);
if (list_empty(&pc.pc_pages)) {
@@ -739,8 +729,6 @@ void cfs_trace_flush_pages(void)
struct cfs_trace_page *tage;
struct cfs_trace_page *tmp;
- spin_lock_init(&pc.pc_lock);
-
pc.pc_want_daemon_pages = 1;
collect_pages(&pc);
list_for_each_entry_safe(tage, tmp, &pc.pc_pages, linkage) {
@@ -817,11 +805,6 @@ int cfs_trace_allocate_string_buffer(char **str, int nob)
return 0;
}
-void cfs_trace_free_string_buffer(char *str, int nob)
-{
- kfree(str);
-}
-
int cfs_trace_dump_debug_buffer_usrstr(void __user *usr_str, int usr_str_nob)
{
char *str;
@@ -842,7 +825,7 @@ int cfs_trace_dump_debug_buffer_usrstr(void __user *usr_str, int usr_str_nob)
}
rc = cfs_tracefile_dump_all_pages(str);
out:
- cfs_trace_free_string_buffer(str, usr_str_nob + 1);
+ kfree(str);
return rc;
}
@@ -898,7 +881,7 @@ int cfs_trace_daemon_command_usrstr(void __user *usr_str, int usr_str_nob)
if (rc == 0)
rc = cfs_trace_daemon_command(str);
- cfs_trace_free_string_buffer(str, usr_str_nob + 1);
+ kfree(str);
return rc;
}
@@ -970,7 +953,6 @@ static int tracefiled(void *arg)
/* we're started late enough that we pick up init's fs context */
/* this is so broken in uml? what on earth is going on? */
- spin_lock_init(&pc.pc_lock);
complete(&tctl->tctl_start);
while (1) {
@@ -1170,7 +1152,6 @@ static void cfs_trace_cleanup(void)
struct page_collection pc;
INIT_LIST_HEAD(&pc.pc_pages);
- spin_lock_init(&pc.pc_lock);
trace_cleanup_on_all_cpus();
diff --git a/drivers/staging/lustre/lustre/libcfs/tracefile.h b/drivers/staging/lustre/lustre/libcfs/tracefile.h
index cb7a3963589f..7bf1471a54fb 100644
--- a/drivers/staging/lustre/lustre/libcfs/tracefile.h
+++ b/drivers/staging/lustre/lustre/libcfs/tracefile.h
@@ -39,7 +39,12 @@
#include "../../include/linux/libcfs/libcfs.h"
-#include "linux/linux-tracefile.h"
+typedef enum {
+ CFS_TCD_TYPE_PROC = 0,
+ CFS_TCD_TYPE_SOFTIRQ,
+ CFS_TCD_TYPE_IRQ,
+ CFS_TCD_TYPE_MAX
+} cfs_trace_buf_type_t;
/* trace file lock routines */
@@ -70,7 +75,6 @@ int cfs_trace_copyin_string(char *knl_buffer, int knl_buffer_nob,
int cfs_trace_copyout_string(char __user *usr_buffer, int usr_buffer_nob,
const char *knl_str, char *append);
int cfs_trace_allocate_string_buffer(char **str, int nob);
-void cfs_trace_free_string_buffer(char *str, int nob);
int cfs_trace_dump_debug_buffer_usrstr(void __user *usr_str, int usr_str_nob);
int cfs_trace_daemon_command(char *str);
int cfs_trace_daemon_command_usrstr(void __user *usr_str, int usr_str_nob);
@@ -196,14 +200,6 @@ extern union cfs_trace_data_union (*cfs_trace_data[TCD_MAX_TYPES])[NR_CPUS];
struct page_collection {
struct list_head pc_pages;
/*
- * spin-lock protecting ->pc_pages. It is taken by smp_call_function()
- * call-back functions. XXX nikita: Which is horrible: all processors
- * receive NMI at the same time only to be serialized by this
- * lock. Probably ->pc_pages should be replaced with an array of
- * NR_CPUS elements accessed locklessly.
- */
- spinlock_t pc_lock;
- /*
* if this flag is set, collect_pages() will spill both
* ->tcd_daemon_pages and ->tcd_pages to the ->pc_pages. Otherwise,
* only ->tcd_pages are spilled.
@@ -260,13 +256,6 @@ void cfs_print_to_console(struct ptldebug_header *hdr, int mask,
int cfs_trace_lock_tcd(struct cfs_trace_cpu_data *tcd, int walking);
void cfs_trace_unlock_tcd(struct cfs_trace_cpu_data *tcd, int walking);
-/**
- * trace_buf_type_t, trace_buf_idx_get() and trace_console_buffers[][]
- * are not public libcfs API; they should be defined in
- * platform-specific tracefile include files
- * (see, for example, linux-tracefile.h).
- */
-
extern char *cfs_trace_console_buffers[NR_CPUS][CFS_TCD_TYPE_MAX];
cfs_trace_buf_type_t cfs_trace_buf_idx_get(void);
@@ -279,12 +268,6 @@ cfs_trace_get_console_buffer(void)
return cfs_trace_console_buffers[i][j];
}
-static inline void
-cfs_trace_put_console_buffer(char *buffer)
-{
- put_cpu();
-}
-
static inline struct cfs_trace_cpu_data *
cfs_trace_get_tcd(void)
{
diff --git a/drivers/staging/lustre/lustre/libcfs/workitem.c b/drivers/staging/lustre/lustre/libcfs/workitem.c
index e1143a566ac4..60bb88a00b41 100644
--- a/drivers/staging/lustre/lustre/libcfs/workitem.c
+++ b/drivers/staging/lustre/lustre/libcfs/workitem.c
@@ -86,32 +86,20 @@ static struct cfs_workitem_data {
int wi_stopping;
} cfs_wi_data;
-static inline void
-cfs_wi_sched_lock(struct cfs_wi_sched *sched)
-{
- spin_lock(&sched->ws_lock);
-}
-
-static inline void
-cfs_wi_sched_unlock(struct cfs_wi_sched *sched)
-{
- spin_unlock(&sched->ws_lock);
-}
-
static inline int
cfs_wi_sched_cansleep(struct cfs_wi_sched *sched)
{
- cfs_wi_sched_lock(sched);
+ spin_lock(&sched->ws_lock);
if (sched->ws_stopping) {
- cfs_wi_sched_unlock(sched);
+ spin_unlock(&sched->ws_lock);
return 0;
}
if (!list_empty(&sched->ws_runq)) {
- cfs_wi_sched_unlock(sched);
+ spin_unlock(&sched->ws_lock);
return 0;
}
- cfs_wi_sched_unlock(sched);
+ spin_unlock(&sched->ws_lock);
return 1;
}
@@ -125,7 +113,7 @@ cfs_wi_exit(struct cfs_wi_sched *sched, cfs_workitem_t *wi)
LASSERT(!in_interrupt()); /* because we use plain spinlock */
LASSERT(!sched->ws_stopping);
- cfs_wi_sched_lock(sched);
+ spin_lock(&sched->ws_lock);
LASSERT(wi->wi_running);
if (wi->wi_scheduled) { /* cancel pending schedules */
@@ -139,7 +127,7 @@ cfs_wi_exit(struct cfs_wi_sched *sched, cfs_workitem_t *wi)
LASSERT(list_empty(&wi->wi_list));
wi->wi_scheduled = 1; /* LBUG future schedule attempts */
- cfs_wi_sched_unlock(sched);
+ spin_unlock(&sched->ws_lock);
return;
}
@@ -161,7 +149,7 @@ cfs_wi_deschedule(struct cfs_wi_sched *sched, cfs_workitem_t *wi)
* means the workitem will not be scheduled and will not have
* any race with wi_action.
*/
- cfs_wi_sched_lock(sched);
+ spin_lock(&sched->ws_lock);
rc = !(wi->wi_running);
@@ -177,7 +165,7 @@ cfs_wi_deschedule(struct cfs_wi_sched *sched, cfs_workitem_t *wi)
LASSERT (list_empty(&wi->wi_list));
- cfs_wi_sched_unlock(sched);
+ spin_unlock(&sched->ws_lock);
return rc;
}
EXPORT_SYMBOL(cfs_wi_deschedule);
@@ -195,7 +183,7 @@ cfs_wi_schedule(struct cfs_wi_sched *sched, cfs_workitem_t *wi)
LASSERT(!in_interrupt()); /* because we use plain spinlock */
LASSERT(!sched->ws_stopping);
- cfs_wi_sched_lock(sched);
+ spin_lock(&sched->ws_lock);
if (!wi->wi_scheduled) {
LASSERT (list_empty(&wi->wi_list));
@@ -211,7 +199,7 @@ cfs_wi_schedule(struct cfs_wi_sched *sched, cfs_workitem_t *wi)
}
LASSERT (!list_empty(&wi->wi_list));
- cfs_wi_sched_unlock(sched);
+ spin_unlock(&sched->ws_lock);
return;
}
EXPORT_SYMBOL(cfs_wi_schedule);
@@ -225,7 +213,9 @@ cfs_wi_scheduler (void *arg)
/* CPT affinity scheduler? */
if (sched->ws_cptab != NULL)
- cfs_cpt_bind(sched->ws_cptab, sched->ws_cpt);
+ if (cfs_cpt_bind(sched->ws_cptab, sched->ws_cpt) != 0)
+ CWARN("Failed to bind %s on CPT %d\n",
+ sched->ws_name, sched->ws_cpt);
spin_lock(&cfs_wi_data.wi_glock);
@@ -235,7 +225,7 @@ cfs_wi_scheduler (void *arg)
spin_unlock(&cfs_wi_data.wi_glock);
- cfs_wi_sched_lock(sched);
+ spin_lock(&sched->ws_lock);
while (!sched->ws_stopping) {
int nloops = 0;
@@ -256,12 +246,12 @@ cfs_wi_scheduler (void *arg)
wi->wi_running = 1;
wi->wi_scheduled = 0;
- cfs_wi_sched_unlock(sched);
+ spin_unlock(&sched->ws_lock);
nloops++;
rc = (*wi->wi_action) (wi);
- cfs_wi_sched_lock(sched);
+ spin_lock(&sched->ws_lock);
if (rc != 0) /* WI should be dead, even be freed! */
continue;
@@ -276,21 +266,21 @@ cfs_wi_scheduler (void *arg)
}
if (!list_empty(&sched->ws_runq)) {
- cfs_wi_sched_unlock(sched);
+ spin_unlock(&sched->ws_lock);
/* don't sleep because some workitems still
* expect me to come back soon */
cond_resched();
- cfs_wi_sched_lock(sched);
+ spin_lock(&sched->ws_lock);
continue;
}
- cfs_wi_sched_unlock(sched);
+ spin_unlock(&sched->ws_lock);
rc = wait_event_interruptible_exclusive(sched->ws_waitq,
!cfs_wi_sched_cansleep(sched));
- cfs_wi_sched_lock(sched);
+ spin_lock(&sched->ws_lock);
}
- cfs_wi_sched_unlock(sched);
+ spin_unlock(&sched->ws_lock);
spin_lock(&cfs_wi_data.wi_glock);
sched->ws_nthreads--;
@@ -325,7 +315,7 @@ cfs_wi_sched_destroy(struct cfs_wi_sched *sched)
spin_lock(&cfs_wi_data.wi_glock);
while (sched->ws_nthreads > 0) {
- CDEBUG(IS_PO2(++i) ? D_WARNING : D_NET,
+ CDEBUG(is_power_of_2(++i) ? D_WARNING : D_NET,
"waiting for %d threads of WI sched[%s] to terminate\n",
sched->ws_nthreads, sched->ws_name);
@@ -360,8 +350,8 @@ cfs_wi_sched_create(char *name, struct cfs_cpt_table *cptab,
if (sched == NULL)
return -ENOMEM;
- strncpy(sched->ws_name, name, CFS_WS_NAME_LEN);
- sched->ws_name[CFS_WS_NAME_LEN - 1] = '\0';
+ strlcpy(sched->ws_name, name, CFS_WS_NAME_LEN);
+
sched->ws_cptab = cptab;
sched->ws_cpt = cpt;
diff --git a/drivers/staging/lustre/lustre/llite/dcache.c b/drivers/staging/lustre/lustre/llite/dcache.c
index 80cba0448499..3d6745e63fe3 100644
--- a/drivers/staging/lustre/lustre/llite/dcache.c
+++ b/drivers/staging/lustre/lustre/llite/dcache.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/llite/dir.c b/drivers/staging/lustre/lustre/llite/dir.c
index 5c9502b5b358..7b355319079c 100644
--- a/drivers/staging/lustre/lustre/llite/dir.c
+++ b/drivers/staging/lustre/lustre/llite/dir.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -239,12 +239,6 @@ static int ll_dir_filler(void *_hash, struct page *page0)
return rc;
}
-static void ll_check_page(struct inode *dir, struct page *page)
-{
- /* XXX: check page format later */
- SetPageChecked(page);
-}
-
void ll_release_page(struct page *page, int remove)
{
kunmap(page);
@@ -432,7 +426,8 @@ struct page *ll_get_dir_page(struct inode *dir, __u64 hash,
goto fail;
}
if (!PageChecked(page))
- ll_check_page(dir, page);
+ /* XXX: check page format later */
+ SetPageChecked(page);
if (PageError(page)) {
CERROR("page error: "DFID" at %llu: rc %d\n",
PFID(ll_inode2fid(dir)), hash, -5);
@@ -641,7 +636,7 @@ static int ll_send_mgc_param(struct obd_export *mgc, char *string)
if (!msp)
return -ENOMEM;
- strncpy(msp->mgs_param, string, MGS_PARAM_MAXLEN);
+ strlcpy(msp->mgs_param, string, sizeof(msp->mgs_param));
rc = obd_set_info_async(NULL, mgc, sizeof(KEY_SET_INFO), KEY_SET_INFO,
sizeof(struct mgs_send_param), msp, NULL);
if (rc)
diff --git a/drivers/staging/lustre/lustre/llite/file.c b/drivers/staging/lustre/lustre/llite/file.c
index 02f27593013e..c92d58b770ec 100644
--- a/drivers/staging/lustre/lustre/llite/file.c
+++ b/drivers/staging/lustre/lustre/llite/file.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -3139,7 +3139,7 @@ struct file_operations ll_file_operations_noflock = {
.lock = ll_file_noflock
};
-struct inode_operations ll_file_inode_operations = {
+const struct inode_operations ll_file_inode_operations = {
.setattr = ll_setattr,
.getattr = ll_getattr,
.permission = ll_inode_permission,
diff --git a/drivers/staging/lustre/lustre/llite/llite_internal.h b/drivers/staging/lustre/lustre/llite/llite_internal.h
index 9096d311e45d..ee8a1d67d191 100644
--- a/drivers/staging/lustre/lustre/llite/llite_internal.h
+++ b/drivers/staging/lustre/lustre/llite/llite_internal.h
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -705,7 +705,7 @@ extern const struct address_space_operations ll_aops;
extern struct file_operations ll_file_operations;
extern struct file_operations ll_file_operations_flock;
extern struct file_operations ll_file_operations_noflock;
-extern struct inode_operations ll_file_inode_operations;
+extern const struct inode_operations ll_file_inode_operations;
int ll_have_md_lock(struct inode *inode, __u64 *bits,
ldlm_mode_t l_req_mode);
ldlm_mode_t ll_take_md_lock(struct inode *inode, __u64 bits,
@@ -805,7 +805,7 @@ struct inode *search_inode_for_lustre(struct super_block *sb,
const struct lu_fid *fid);
/* llite/symlink.c */
-extern struct inode_operations ll_fast_symlink_inode_operations;
+extern const struct inode_operations ll_fast_symlink_inode_operations;
/* llite/llite_close.c */
struct ll_close_queue {
@@ -1285,16 +1285,6 @@ static inline struct ll_file_data *cl_iattr2fd(struct inode *inode,
return LUSTRE_FPRIVATE(attr->ia_file);
}
-static inline void cl_isize_lock(struct inode *inode)
-{
- ll_inode_size_lock(inode);
-}
-
-static inline void cl_isize_unlock(struct inode *inode)
-{
- ll_inode_size_unlock(inode);
-}
-
static inline void cl_isize_write_nolock(struct inode *inode, loff_t kms)
{
LASSERT(mutex_is_locked(&ll_i2info(inode)->lli_size_mutex));
diff --git a/drivers/staging/lustre/lustre/llite/llite_lib.c b/drivers/staging/lustre/lustre/llite/llite_lib.c
index 4a8c759fef42..1db93af62bad 100644
--- a/drivers/staging/lustre/lustre/llite/llite_lib.c
+++ b/drivers/staging/lustre/lustre/llite/llite_lib.c
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/llite/llite_mmap.c b/drivers/staging/lustre/lustre/llite/llite_mmap.c
index 7df978371c9a..bbae95c9feed 100644
--- a/drivers/staging/lustre/lustre/llite/llite_mmap.c
+++ b/drivers/staging/lustre/lustre/llite/llite_mmap.c
@@ -27,7 +27,7 @@
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/llite/lloop.c b/drivers/staging/lustre/lustre/llite/lloop.c
index fed50d538a41..420d39123877 100644
--- a/drivers/staging/lustre/lustre/llite/lloop.c
+++ b/drivers/staging/lustre/lustre/llite/lloop.c
@@ -877,6 +877,6 @@ module_exit(lloop_exit);
module_param(max_loop, int, 0444);
MODULE_PARM_DESC(max_loop, "maximum of lloop_device");
-MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_AUTHOR("OpenSFS, Inc. <http://www.lustre.org/>");
MODULE_DESCRIPTION("Lustre virtual block device");
MODULE_LICENSE("GPL");
diff --git a/drivers/staging/lustre/lustre/llite/lproc_llite.c b/drivers/staging/lustre/lustre/llite/lproc_llite.c
index 190fc44114e1..f134ad9d23f0 100644
--- a/drivers/staging/lustre/lustre/llite/lproc_llite.c
+++ b/drivers/staging/lustre/lustre/llite/lproc_llite.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/llite/namei.c b/drivers/staging/lustre/lustre/llite/namei.c
index 2ca22001a534..da5f443a0768 100644
--- a/drivers/staging/lustre/lustre/llite/namei.c
+++ b/drivers/staging/lustre/lustre/llite/namei.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -126,9 +126,7 @@ struct inode *ll_iget(struct super_block *sb, ino_t hash,
rc = cl_file_inode_init(inode, md);
}
if (rc != 0) {
- make_bad_inode(inode);
- unlock_new_inode(inode);
- iput(inode);
+ iget_failed(inode);
inode = ERR_PTR(rc);
} else
unlock_new_inode(inode);
@@ -556,7 +554,6 @@ static struct dentry *ll_lookup_it(struct inode *parent, struct dentry *dentry,
retval = NULL;
else
retval = dentry;
- goto out;
out:
if (req)
ptlrpc_req_finished(req);
diff --git a/drivers/staging/lustre/lustre/llite/rw.c b/drivers/staging/lustre/lustre/llite/rw.c
index f79193fa2fb7..95cdb0c58b04 100644
--- a/drivers/staging/lustre/lustre/llite/rw.c
+++ b/drivers/staging/lustre/lustre/llite/rw.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -880,14 +880,6 @@ static void ras_update_stride_detector(struct ll_readahead_state *ras,
return;
}
-static unsigned long
-stride_page_count(struct ll_readahead_state *ras, unsigned long len)
-{
- return stride_pg_count(ras->ras_stride_offset, ras->ras_stride_length,
- ras->ras_stride_pages, ras->ras_stride_offset,
- len);
-}
-
/* Stride Read-ahead window will be increased inc_len according to
* stride I/O pattern */
static void ras_stride_increase_window(struct ll_readahead_state *ras,
@@ -921,7 +913,9 @@ static void ras_stride_increase_window(struct ll_readahead_state *ras,
window_len += step * ras->ras_stride_length + left;
- if (stride_page_count(ras, window_len) <= ra->ra_max_pages_per_file)
+ if (stride_pg_count(ras->ras_stride_offset, ras->ras_stride_length,
+ ras->ras_stride_pages, ras->ras_stride_offset,
+ window_len) <= ra->ra_max_pages_per_file)
ras->ras_window_len = window_len;
RAS_CDEBUG(ras);
diff --git a/drivers/staging/lustre/lustre/llite/rw26.c b/drivers/staging/lustre/lustre/llite/rw26.c
index 3da4c01e2159..39fa13b74cbd 100644
--- a/drivers/staging/lustre/lustre/llite/rw26.c
+++ b/drivers/staging/lustre/lustre/llite/rw26.c
@@ -298,7 +298,10 @@ ssize_t ll_direct_rw_pages(const struct lu_env *env, struct cl_io *io,
}
if (likely(do_io)) {
- cl_2queue_add(queue, clp);
+ /*
+ * Add a page to the incoming page list of 2-queue.
+ */
+ cl_page_list_add(&queue->c2_qin, clp);
/*
* Set page clip to tell transfer formation engine
diff --git a/drivers/staging/lustre/lustre/llite/statahead.c b/drivers/staging/lustre/lustre/llite/statahead.c
index 18f5f2b7e902..88ffd8e3abdb 100644
--- a/drivers/staging/lustre/lustre/llite/statahead.c
+++ b/drivers/staging/lustre/lustre/llite/statahead.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -87,11 +87,6 @@ struct ll_sa_entry {
static unsigned int sai_generation;
static DEFINE_SPINLOCK(sai_generation_lock);
-static inline int ll_sa_entry_unhashed(struct ll_sa_entry *entry)
-{
- return list_empty(&entry->se_hash);
-}
-
/*
* The entry only can be released by the caller, it is necessary to hold lock.
*/
@@ -138,20 +133,6 @@ static inline int agl_should_run(struct ll_statahead_info *sai,
return (inode != NULL && S_ISREG(inode->i_mode) && sai->sai_agl_valid);
}
-static inline struct ll_sa_entry *
-sa_first_received_entry(struct ll_statahead_info *sai)
-{
- return list_entry(sai->sai_entries_received.next,
- struct ll_sa_entry, se_list);
-}
-
-static inline struct ll_inode_info *
-agl_first_entry(struct ll_statahead_info *sai)
-{
- return list_entry(sai->sai_entries_agl.next,
- struct ll_inode_info, lli_agl_list);
-}
-
static inline int sa_sent_full(struct ll_statahead_info *sai)
{
return atomic_read(&sai->sai_cache_count) >= sai->sai_max;
@@ -331,7 +312,7 @@ static void ll_sa_entry_put(struct ll_statahead_info *sai,
LASSERT(list_empty(&entry->se_link));
LASSERT(list_empty(&entry->se_list));
- LASSERT(ll_sa_entry_unhashed(entry));
+ LASSERT(list_empty(&entry->se_hash));
ll_sa_entry_cleanup(sai, entry);
iput(entry->se_inode);
@@ -346,7 +327,7 @@ do_sa_entry_fini(struct ll_statahead_info *sai, struct ll_sa_entry *entry)
{
struct ll_inode_info *lli = ll_i2info(sai->sai_inode);
- LASSERT(!ll_sa_entry_unhashed(entry));
+ LASSERT(!list_empty(&entry->se_hash));
LASSERT(!list_empty(&entry->se_link));
ll_sa_entry_unhash(sai, entry);
@@ -447,7 +428,7 @@ static void ll_agl_add(struct ll_statahead_info *sai,
igrab(inode);
spin_lock(&parent->lli_agl_lock);
- if (agl_list_empty(sai))
+ if (list_empty(&sai->sai_entries_agl))
added = 1;
list_add_tail(&child->lli_agl_list, &sai->sai_entries_agl);
spin_unlock(&parent->lli_agl_lock);
@@ -537,11 +518,11 @@ static void ll_sai_put(struct ll_statahead_info *sai)
do_sa_entry_fini(sai, entry);
LASSERT(list_empty(&sai->sai_entries));
- LASSERT(sa_received_empty(sai));
+ LASSERT(list_empty(&sai->sai_entries_received));
LASSERT(list_empty(&sai->sai_entries_stated));
LASSERT(atomic_read(&sai->sai_cache_count) == 0);
- LASSERT(agl_list_empty(sai));
+ LASSERT(list_empty(&sai->sai_entries_agl));
iput(inode);
kfree(sai);
@@ -621,11 +602,12 @@ static void ll_post_statahead(struct ll_statahead_info *sai)
int rc = 0;
spin_lock(&lli->lli_sa_lock);
- if (unlikely(sa_received_empty(sai))) {
+ if (unlikely(list_empty(&sai->sai_entries_received))) {
spin_unlock(&lli->lli_sa_lock);
return;
}
- entry = sa_first_received_entry(sai);
+ entry = list_entry(sai->sai_entries_received.next,
+ struct ll_sa_entry, se_list);
atomic_inc(&entry->se_refcount);
list_del_init(&entry->se_list);
spin_unlock(&lli->lli_sa_lock);
@@ -756,7 +738,7 @@ static int ll_statahead_interpret(struct ptlrpc_request *req,
* for readpage and other tries to enqueue lock on child
* with parent's lock held, for example: unlink. */
entry->se_handle = handle;
- wakeup = sa_received_empty(sai);
+ wakeup = list_empty(&sai->sai_entries_received);
list_add_tail(&entry->se_list,
&sai->sai_entries_received);
}
@@ -973,7 +955,7 @@ static int ll_agl_thread(void *arg)
while (1) {
l_wait_event(thread->t_ctl_waitq,
- !agl_list_empty(sai) ||
+ !list_empty(&sai->sai_entries_agl) ||
!thread_is_running(thread),
&lwi);
@@ -983,8 +965,9 @@ static int ll_agl_thread(void *arg)
spin_lock(&plli->lli_agl_lock);
/* The statahead thread maybe help to process AGL entries,
* so check whether list empty again. */
- if (!agl_list_empty(sai)) {
- clli = agl_first_entry(sai);
+ if (!list_empty(&sai->sai_entries_agl)) {
+ clli = list_entry(sai->sai_entries_agl.next,
+ struct ll_inode_info, lli_agl_list);
list_del_init(&clli->lli_agl_list);
spin_unlock(&plli->lli_agl_lock);
ll_agl_trigger(&clli->lli_vfs_inode, sai);
@@ -995,8 +978,9 @@ static int ll_agl_thread(void *arg)
spin_lock(&plli->lli_agl_lock);
sai->sai_agl_valid = 0;
- while (!agl_list_empty(sai)) {
- clli = agl_first_entry(sai);
+ while (!list_empty(&sai->sai_entries_agl)) {
+ clli = list_entry(sai->sai_entries_agl.next,
+ struct ll_inode_info, lli_agl_list);
list_del_init(&clli->lli_agl_list);
spin_unlock(&plli->lli_agl_lock);
clli->lli_agl_index = 0;
@@ -1136,13 +1120,13 @@ static int ll_statahead_thread(void *arg)
keep_it:
l_wait_event(thread->t_ctl_waitq,
!sa_sent_full(sai) ||
- !sa_received_empty(sai) ||
- !agl_list_empty(sai) ||
+ !list_empty(&sai->sai_entries_received) ||
+ !list_empty(&sai->sai_entries_agl) ||
!thread_is_running(thread),
&lwi);
interpret_it:
- while (!sa_received_empty(sai))
+ while (!list_empty(&sai->sai_entries_received))
ll_post_statahead(sai);
if (unlikely(!thread_is_running(thread))) {
@@ -1156,14 +1140,15 @@ interpret_it:
* to process the AGL entries. */
if (sa_sent_full(sai)) {
spin_lock(&plli->lli_agl_lock);
- while (!agl_list_empty(sai)) {
- clli = agl_first_entry(sai);
+ while (!list_empty(&sai->sai_entries_agl)) {
+ clli = list_entry(sai->sai_entries_agl.next,
+ struct ll_inode_info, lli_agl_list);
list_del_init(&clli->lli_agl_list);
spin_unlock(&plli->lli_agl_lock);
ll_agl_trigger(&clli->lli_vfs_inode,
sai);
- if (!sa_received_empty(sai))
+ if (!list_empty(&sai->sai_entries_received))
goto interpret_it;
if (unlikely(
@@ -1194,12 +1179,12 @@ do_it:
ll_release_page(page, 0);
while (1) {
l_wait_event(thread->t_ctl_waitq,
- !sa_received_empty(sai) ||
+ !list_empty(&sai->sai_entries_received) ||
sai->sai_sent == sai->sai_replied ||
!thread_is_running(thread),
&lwi);
- while (!sa_received_empty(sai))
+ while (!list_empty(&sai->sai_entries_received))
ll_post_statahead(sai);
if (unlikely(!thread_is_running(thread))) {
@@ -1208,14 +1193,15 @@ do_it:
}
if (sai->sai_sent == sai->sai_replied &&
- sa_received_empty(sai))
+ list_empty(&sai->sai_entries_received))
break;
}
spin_lock(&plli->lli_agl_lock);
- while (!agl_list_empty(sai) &&
+ while (!list_empty(&sai->sai_entries_agl) &&
thread_is_running(thread)) {
- clli = agl_first_entry(sai);
+ clli = list_entry(sai->sai_entries_agl.next,
+ struct ll_inode_info, lli_agl_list);
list_del_init(&clli->lli_agl_list);
spin_unlock(&plli->lli_agl_lock);
ll_agl_trigger(&clli->lli_vfs_inode, sai);
@@ -1260,12 +1246,12 @@ out:
}
ll_dir_chain_fini(&chain);
spin_lock(&plli->lli_sa_lock);
- if (!sa_received_empty(sai)) {
+ if (!list_empty(&sai->sai_entries_received)) {
thread_set_flags(thread, SVC_STOPPING);
spin_unlock(&plli->lli_sa_lock);
/* To release the resources held by received entries. */
- while (!sa_received_empty(sai))
+ while (!list_empty(&sai->sai_entries_received))
ll_post_statahead(sai);
spin_lock(&plli->lli_sa_lock);
diff --git a/drivers/staging/lustre/lustre/llite/super25.c b/drivers/staging/lustre/lustre/llite/super25.c
index 013136860664..86c371ef71ea 100644
--- a/drivers/staging/lustre/lustre/llite/super25.c
+++ b/drivers/staging/lustre/lustre/llite/super25.c
@@ -106,7 +106,8 @@ static int __init init_lustre_lite(void)
rc = -ENOMEM;
ll_inode_cachep = kmem_cache_create("lustre_inode_cache",
sizeof(struct ll_inode_info),
- 0, SLAB_HWCACHE_ALIGN, NULL);
+ 0, SLAB_HWCACHE_ALIGN|SLAB_ACCOUNT,
+ NULL);
if (ll_inode_cachep == NULL)
goto out_cache;
@@ -205,7 +206,7 @@ static void __exit exit_lustre_lite(void)
kmem_cache_destroy(ll_file_data_slab);
}
-MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_AUTHOR("OpenSFS, Inc. <http://www.lustre.org/>");
MODULE_DESCRIPTION("Lustre Lite Client File System");
MODULE_LICENSE("GPL");
diff --git a/drivers/staging/lustre/lustre/llite/symlink.c b/drivers/staging/lustre/lustre/llite/symlink.c
index 69b203651905..2610348f6c72 100644
--- a/drivers/staging/lustre/lustre/llite/symlink.c
+++ b/drivers/staging/lustre/lustre/llite/symlink.c
@@ -118,12 +118,20 @@ failed:
return rc;
}
-static const char *ll_follow_link(struct dentry *dentry, void **cookie)
+static void ll_put_link(void *p)
+{
+ ptlrpc_req_finished(p);
+}
+
+static const char *ll_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
- struct inode *inode = d_inode(dentry);
struct ptlrpc_request *request = NULL;
int rc;
char *symname = NULL;
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
CDEBUG(D_VFSTRACE, "VFS Op\n");
ll_inode_size_lock(inode);
@@ -135,22 +143,16 @@ static const char *ll_follow_link(struct dentry *dentry, void **cookie)
}
/* symname may contain a pointer to the request message buffer,
- * we delay request releasing until ll_put_link then.
+ * we delay request releasing then.
*/
- *cookie = request;
+ set_delayed_call(done, ll_put_link, request);
return symname;
}
-static void ll_put_link(struct inode *unused, void *cookie)
-{
- ptlrpc_req_finished(cookie);
-}
-
-struct inode_operations ll_fast_symlink_inode_operations = {
+const struct inode_operations ll_fast_symlink_inode_operations = {
.readlink = generic_readlink,
.setattr = ll_setattr,
- .follow_link = ll_follow_link,
- .put_link = ll_put_link,
+ .get_link = ll_get_link,
.getattr = ll_getattr,
.permission = ll_inode_permission,
.setxattr = ll_setxattr,
diff --git a/drivers/staging/lustre/lustre/llite/vvp_dev.c b/drivers/staging/lustre/lustre/llite/vvp_dev.c
index d16d6cfce81a..fdca4ec0555d 100644
--- a/drivers/staging/lustre/lustre/llite/vvp_dev.c
+++ b/drivers/staging/lustre/lustre/llite/vvp_dev.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/llite/vvp_internal.h b/drivers/staging/lustre/lustre/llite/vvp_internal.h
index b5a6661d47d5..2e39533a45f8 100644
--- a/drivers/staging/lustre/lustre/llite/vvp_internal.h
+++ b/drivers/staging/lustre/lustre/llite/vvp_internal.h
@@ -26,6 +26,8 @@
/*
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright (c) 2013, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/llite/vvp_io.c b/drivers/staging/lustre/lustre/llite/vvp_io.c
index 37773c181729..f68e972886ca 100644
--- a/drivers/staging/lustre/lustre/llite/vvp_io.c
+++ b/drivers/staging/lustre/lustre/llite/vvp_io.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -849,7 +849,7 @@ static int vvp_io_read_page(const struct lu_env *env,
* Add page into the queue even when it is marked uptodate above.
* this will unlock it automatically as part of cl_page_list_disown().
*/
- cl_2queue_add(queue, page);
+ cl_page_list_add(&queue->c2_qin, page);
if (sbi->ll_ra_info.ra_max_pages_per_file &&
sbi->ll_ra_info.ra_max_pages)
ll_readahead(env, io, ras,
diff --git a/drivers/staging/lustre/lustre/llite/vvp_lock.c b/drivers/staging/lustre/lustre/llite/vvp_lock.c
index f7b1144aadee..ff0948043c7a 100644
--- a/drivers/staging/lustre/lustre/llite/vvp_lock.c
+++ b/drivers/staging/lustre/lustre/llite/vvp_lock.c
@@ -26,6 +26,8 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright (c) 2014, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/llite/vvp_object.c b/drivers/staging/lustre/lustre/llite/vvp_object.c
index e13afb7e8dca..c82714ea898e 100644
--- a/drivers/staging/lustre/lustre/llite/vvp_object.c
+++ b/drivers/staging/lustre/lustre/llite/vvp_object.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/llite/vvp_page.c b/drivers/staging/lustre/lustre/llite/vvp_page.c
index 92f60c350f35..99c0d7aee921 100644
--- a/drivers/staging/lustre/lustre/llite/vvp_page.c
+++ b/drivers/staging/lustre/lustre/llite/vvp_page.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/llite/xattr.c b/drivers/staging/lustre/lustre/llite/xattr.c
index 4b7eb33f7d01..8eb43f192d1f 100644
--- a/drivers/staging/lustre/lustre/llite/xattr.c
+++ b/drivers/staging/lustre/lustre/llite/xattr.c
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -60,10 +60,10 @@
static
int get_xattr_type(const char *name)
{
- if (!strcmp(name, POSIX_ACL_XATTR_ACCESS))
+ if (!strcmp(name, XATTR_NAME_POSIX_ACL_ACCESS))
return XATTR_ACL_ACCESS_T;
- if (!strcmp(name, POSIX_ACL_XATTR_DEFAULT))
+ if (!strcmp(name, XATTR_NAME_POSIX_ACL_DEFAULT))
return XATTR_ACL_DEFAULT_T;
if (!strncmp(name, XATTR_USER_PREFIX,
@@ -193,7 +193,10 @@ int ll_setxattr_common(struct inode *inode, const char *name,
ll_i2suppgid(inode), &req);
#ifdef CONFIG_FS_POSIX_ACL
if (new_value != NULL)
- lustre_posix_acl_xattr_free(new_value, size);
+ /*
+ * Release the posix ACL space.
+ */
+ kfree(new_value);
if (acl != NULL)
lustre_ext_acl_xattr_free(acl);
#endif
diff --git a/drivers/staging/lustre/lustre/llite/xattr_cache.c b/drivers/staging/lustre/lustre/llite/xattr_cache.c
index e1e599ceb173..d1402762a0b2 100644
--- a/drivers/staging/lustre/lustre/llite/xattr_cache.c
+++ b/drivers/staging/lustre/lustre/llite/xattr_cache.c
@@ -1,6 +1,8 @@
/*
* Copyright 2012 Xyratex Technology Limited
*
+ * Copyright (c) 2013, 2015, Intel Corporation.
+ *
* Author: Andrew Perepechko <Andrew_Perepechko@xyratex.com>
*
*/
diff --git a/drivers/staging/lustre/lustre/lmv/lmv_intent.c b/drivers/staging/lustre/lustre/lmv/lmv_intent.c
index eebe45bdceb6..66de27f1d289 100644
--- a/drivers/staging/lustre/lustre/lmv/lmv_intent.c
+++ b/drivers/staging/lustre/lustre/lmv/lmv_intent.c
@@ -27,7 +27,7 @@
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -156,11 +156,11 @@ out:
* IT_OPEN is intended to open (and create, possible) an object. Parent (pid)
* may be split dir.
*/
-int lmv_intent_open(struct obd_export *exp, struct md_op_data *op_data,
- void *lmm, int lmmsize, struct lookup_intent *it,
- int flags, struct ptlrpc_request **reqp,
- ldlm_blocking_callback cb_blocking,
- __u64 extra_lock_flags)
+static int lmv_intent_open(struct obd_export *exp, struct md_op_data *op_data,
+ void *lmm, int lmmsize, struct lookup_intent *it,
+ int flags, struct ptlrpc_request **reqp,
+ ldlm_blocking_callback cb_blocking,
+ __u64 extra_lock_flags)
{
struct obd_device *obd = exp->exp_obd;
struct lmv_obd *lmv = &obd->u.lmv;
@@ -239,11 +239,12 @@ int lmv_intent_open(struct obd_export *exp, struct md_op_data *op_data,
/*
* Handler for: getattr, lookup and revalidate cases.
*/
-int lmv_intent_lookup(struct obd_export *exp, struct md_op_data *op_data,
- void *lmm, int lmmsize, struct lookup_intent *it,
- int flags, struct ptlrpc_request **reqp,
- ldlm_blocking_callback cb_blocking,
- __u64 extra_lock_flags)
+static int lmv_intent_lookup(struct obd_export *exp,
+ struct md_op_data *op_data,
+ void *lmm, int lmmsize, struct lookup_intent *it,
+ int flags, struct ptlrpc_request **reqp,
+ ldlm_blocking_callback cb_blocking,
+ __u64 extra_lock_flags)
{
struct obd_device *obd = exp->exp_obd;
struct lmv_obd *lmv = &obd->u.lmv;
diff --git a/drivers/staging/lustre/lustre/lmv/lmv_internal.h b/drivers/staging/lustre/lustre/lmv/lmv_internal.h
index b808728daee7..eb8e673cbc3f 100644
--- a/drivers/staging/lustre/lustre/lmv/lmv_internal.h
+++ b/drivers/staging/lustre/lustre/lmv/lmv_internal.h
@@ -27,7 +27,7 @@
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -56,20 +56,6 @@ int lmv_intent_lock(struct obd_export *exp, struct md_op_data *op_data,
ldlm_blocking_callback cb_blocking,
__u64 extra_lock_flags);
-int lmv_intent_lookup(struct obd_export *exp, struct md_op_data *op_data,
- void *lmm, int lmmsize, struct lookup_intent *it,
- int flags, struct ptlrpc_request **reqp,
- ldlm_blocking_callback cb_blocking,
- __u64 extra_lock_flags);
-
-int lmv_intent_open(struct obd_export *exp, struct md_op_data *op_data,
- void *lmm, int lmmsize, struct lookup_intent *it,
- int flags, struct ptlrpc_request **reqp,
- ldlm_blocking_callback cb_blocking,
- __u64 extra_lock_flags);
-
-int lmv_blocking_ast(struct ldlm_lock *, struct ldlm_lock_desc *,
- void *, int);
int lmv_fld_lookup(struct lmv_obd *lmv, const struct lu_fid *fid, u32 *mds);
int __lmv_fid_alloc(struct lmv_obd *lmv, struct lu_fid *fid, u32 mds);
int lmv_fid_alloc(struct obd_export *exp, struct lu_fid *fid,
diff --git a/drivers/staging/lustre/lustre/lmv/lmv_obd.c b/drivers/staging/lustre/lustre/lmv/lmv_obd.c
index 635a93cc94de..bbafe0a710d8 100644
--- a/drivers/staging/lustre/lustre/lmv/lmv_obd.c
+++ b/drivers/staging/lustre/lustre/lmv/lmv_obd.c
@@ -27,7 +27,7 @@
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -2744,55 +2744,55 @@ static int lmv_quotacheck(struct obd_device *unused, struct obd_export *exp,
}
static struct obd_ops lmv_obd_ops = {
- .o_owner = THIS_MODULE,
- .o_setup = lmv_setup,
- .o_cleanup = lmv_cleanup,
- .o_precleanup = lmv_precleanup,
- .o_process_config = lmv_process_config,
- .o_connect = lmv_connect,
- .o_disconnect = lmv_disconnect,
- .o_statfs = lmv_statfs,
- .o_get_info = lmv_get_info,
- .o_set_info_async = lmv_set_info_async,
- .o_packmd = lmv_packmd,
- .o_unpackmd = lmv_unpackmd,
- .o_notify = lmv_notify,
- .o_get_uuid = lmv_get_uuid,
- .o_iocontrol = lmv_iocontrol,
- .o_quotacheck = lmv_quotacheck,
- .o_quotactl = lmv_quotactl
+ .owner = THIS_MODULE,
+ .setup = lmv_setup,
+ .cleanup = lmv_cleanup,
+ .precleanup = lmv_precleanup,
+ .process_config = lmv_process_config,
+ .connect = lmv_connect,
+ .disconnect = lmv_disconnect,
+ .statfs = lmv_statfs,
+ .get_info = lmv_get_info,
+ .set_info_async = lmv_set_info_async,
+ .packmd = lmv_packmd,
+ .unpackmd = lmv_unpackmd,
+ .notify = lmv_notify,
+ .get_uuid = lmv_get_uuid,
+ .iocontrol = lmv_iocontrol,
+ .quotacheck = lmv_quotacheck,
+ .quotactl = lmv_quotactl
};
static struct md_ops lmv_md_ops = {
- .m_getstatus = lmv_getstatus,
- .m_null_inode = lmv_null_inode,
- .m_find_cbdata = lmv_find_cbdata,
- .m_close = lmv_close,
- .m_create = lmv_create,
- .m_done_writing = lmv_done_writing,
- .m_enqueue = lmv_enqueue,
- .m_getattr = lmv_getattr,
- .m_getxattr = lmv_getxattr,
- .m_getattr_name = lmv_getattr_name,
- .m_intent_lock = lmv_intent_lock,
- .m_link = lmv_link,
- .m_rename = lmv_rename,
- .m_setattr = lmv_setattr,
- .m_setxattr = lmv_setxattr,
- .m_sync = lmv_sync,
- .m_readpage = lmv_readpage,
- .m_unlink = lmv_unlink,
- .m_init_ea_size = lmv_init_ea_size,
- .m_cancel_unused = lmv_cancel_unused,
- .m_set_lock_data = lmv_set_lock_data,
- .m_lock_match = lmv_lock_match,
- .m_get_lustre_md = lmv_get_lustre_md,
- .m_free_lustre_md = lmv_free_lustre_md,
- .m_set_open_replay_data = lmv_set_open_replay_data,
- .m_clear_open_replay_data = lmv_clear_open_replay_data,
- .m_get_remote_perm = lmv_get_remote_perm,
- .m_intent_getattr_async = lmv_intent_getattr_async,
- .m_revalidate_lock = lmv_revalidate_lock
+ .getstatus = lmv_getstatus,
+ .null_inode = lmv_null_inode,
+ .find_cbdata = lmv_find_cbdata,
+ .close = lmv_close,
+ .create = lmv_create,
+ .done_writing = lmv_done_writing,
+ .enqueue = lmv_enqueue,
+ .getattr = lmv_getattr,
+ .getxattr = lmv_getxattr,
+ .getattr_name = lmv_getattr_name,
+ .intent_lock = lmv_intent_lock,
+ .link = lmv_link,
+ .rename = lmv_rename,
+ .setattr = lmv_setattr,
+ .setxattr = lmv_setxattr,
+ .sync = lmv_sync,
+ .readpage = lmv_readpage,
+ .unlink = lmv_unlink,
+ .init_ea_size = lmv_init_ea_size,
+ .cancel_unused = lmv_cancel_unused,
+ .set_lock_data = lmv_set_lock_data,
+ .lock_match = lmv_lock_match,
+ .get_lustre_md = lmv_get_lustre_md,
+ .free_lustre_md = lmv_free_lustre_md,
+ .set_open_replay_data = lmv_set_open_replay_data,
+ .clear_open_replay_data = lmv_clear_open_replay_data,
+ .get_remote_perm = lmv_get_remote_perm,
+ .intent_getattr_async = lmv_intent_getattr_async,
+ .revalidate_lock = lmv_revalidate_lock
};
static int __init lmv_init(void)
@@ -2812,7 +2812,7 @@ static void lmv_exit(void)
class_unregister_type(LUSTRE_LMV_NAME);
}
-MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_AUTHOR("OpenSFS, Inc. <http://www.lustre.org/>");
MODULE_DESCRIPTION("Lustre Logical Metadata Volume OBD driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/staging/lustre/lustre/lov/lov_cl_internal.h b/drivers/staging/lustre/lustre/lov/lov_cl_internal.h
index 1c0fe65243e1..66a2492c1cc3 100644
--- a/drivers/staging/lustre/lustre/lov/lov_cl_internal.h
+++ b/drivers/staging/lustre/lustre/lov/lov_cl_internal.h
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015 Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/lov/lov_dev.c b/drivers/staging/lustre/lustre/lov/lov_dev.c
index 2e8b566458f6..3733fdc88c8c 100644
--- a/drivers/staging/lustre/lustre/lov/lov_dev.c
+++ b/drivers/staging/lustre/lustre/lov/lov_dev.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/lov/lov_ea.c b/drivers/staging/lustre/lustre/lov/lov_ea.c
index 34c1346f0dc7..b3c9c85aab9d 100644
--- a/drivers/staging/lustre/lustre/lov/lov_ea.c
+++ b/drivers/staging/lustre/lustre/lov/lov_ea.c
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/lov/lov_internal.h b/drivers/staging/lustre/lustre/lov/lov_internal.h
index 515a5c147827..2d00bad58e35 100644
--- a/drivers/staging/lustre/lustre/lov/lov_internal.h
+++ b/drivers/staging/lustre/lustre/lov/lov_internal.h
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -151,23 +151,10 @@ int lov_stripe_number(struct lov_stripe_md *lsm, u64 lov_off);
/* lov_qos.c */
#define LOV_USES_ASSIGNED_STRIPE 0
#define LOV_USES_DEFAULT_STRIPE 1
-int qos_add_tgt(struct obd_device *obd, __u32 index);
-int qos_del_tgt(struct obd_device *obd, struct lov_tgt_desc *tgt);
-void qos_shrink_lsm(struct lov_request_set *set);
-int qos_prep_create(struct obd_export *exp, struct lov_request_set *set);
-void qos_update(struct lov_obd *lov);
-void qos_statfs_done(struct lov_obd *lov);
-void qos_statfs_update(struct obd_device *obd, __u64 max_age, int wait);
-int qos_remedy_create(struct lov_request_set *set, struct lov_request *req);
/* lov_request.c */
-void lov_set_add_req(struct lov_request *req, struct lov_request_set *set);
-int lov_set_finished(struct lov_request_set *set, int idempotent);
-void lov_update_set(struct lov_request_set *set,
- struct lov_request *req, int rc);
int lov_update_common_set(struct lov_request_set *set,
struct lov_request *req, int rc);
-int lov_check_and_wait_active(struct lov_obd *lov, int ost_idx);
int lov_prep_getattr_set(struct obd_export *exp, struct obd_info *oinfo,
struct lov_request_set **reqset);
int lov_fini_getattr_set(struct lov_request_set *set);
@@ -184,8 +171,6 @@ int lov_update_setattr_set(struct lov_request_set *set,
int lov_fini_setattr_set(struct lov_request_set *set);
int lov_prep_statfs_set(struct obd_device *obd, struct obd_info *oinfo,
struct lov_request_set **reqset);
-void lov_update_statfs(struct obd_statfs *osfs, struct obd_statfs *lov_sfs,
- int success);
int lov_fini_statfs(struct obd_device *obd, struct obd_statfs *osfs,
int success);
int lov_fini_statfs_set(struct lov_request_set *set);
diff --git a/drivers/staging/lustre/lustre/lov/lov_io.c b/drivers/staging/lustre/lustre/lov/lov_io.c
index 5e6228b9ca01..93fe69eb2560 100644
--- a/drivers/staging/lustre/lustre/lov/lov_io.c
+++ b/drivers/staging/lustre/lustre/lov/lov_io.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/lov/lov_merge.c b/drivers/staging/lustre/lustre/lov/lov_merge.c
index dd1cf3d2d039..97115bec7cca 100644
--- a/drivers/staging/lustre/lustre/lov/lov_merge.c
+++ b/drivers/staging/lustre/lustre/lov/lov_merge.c
@@ -27,7 +27,7 @@
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/lov/lov_obd.c b/drivers/staging/lustre/lustre/lov/lov_obd.c
index 7abe484c07c0..6c2bdfe9cdcf 100644
--- a/drivers/staging/lustre/lustre/lov/lov_obd.c
+++ b/drivers/staging/lustre/lustre/lov/lov_obd.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -2277,35 +2277,35 @@ out:
}
static struct obd_ops lov_obd_ops = {
- .o_owner = THIS_MODULE,
- .o_setup = lov_setup,
- .o_precleanup = lov_precleanup,
- .o_cleanup = lov_cleanup,
- /*.o_process_config = lov_process_config,*/
- .o_connect = lov_connect,
- .o_disconnect = lov_disconnect,
- .o_statfs = lov_statfs,
- .o_statfs_async = lov_statfs_async,
- .o_packmd = lov_packmd,
- .o_unpackmd = lov_unpackmd,
- .o_create = lov_create,
- .o_destroy = lov_destroy,
- .o_getattr_async = lov_getattr_async,
- .o_setattr_async = lov_setattr_async,
- .o_adjust_kms = lov_adjust_kms,
- .o_find_cbdata = lov_find_cbdata,
- .o_iocontrol = lov_iocontrol,
- .o_get_info = lov_get_info,
- .o_set_info_async = lov_set_info_async,
- .o_notify = lov_notify,
- .o_pool_new = lov_pool_new,
- .o_pool_rem = lov_pool_remove,
- .o_pool_add = lov_pool_add,
- .o_pool_del = lov_pool_del,
- .o_getref = lov_getref,
- .o_putref = lov_putref,
- .o_quotactl = lov_quotactl,
- .o_quotacheck = lov_quotacheck,
+ .owner = THIS_MODULE,
+ .setup = lov_setup,
+ .precleanup = lov_precleanup,
+ .cleanup = lov_cleanup,
+ /*.process_config = lov_process_config,*/
+ .connect = lov_connect,
+ .disconnect = lov_disconnect,
+ .statfs = lov_statfs,
+ .statfs_async = lov_statfs_async,
+ .packmd = lov_packmd,
+ .unpackmd = lov_unpackmd,
+ .create = lov_create,
+ .destroy = lov_destroy,
+ .getattr_async = lov_getattr_async,
+ .setattr_async = lov_setattr_async,
+ .adjust_kms = lov_adjust_kms,
+ .find_cbdata = lov_find_cbdata,
+ .iocontrol = lov_iocontrol,
+ .get_info = lov_get_info,
+ .set_info_async = lov_set_info_async,
+ .notify = lov_notify,
+ .pool_new = lov_pool_new,
+ .pool_rem = lov_pool_remove,
+ .pool_add = lov_pool_add,
+ .pool_del = lov_pool_del,
+ .getref = lov_getref,
+ .putref = lov_putref,
+ .quotactl = lov_quotactl,
+ .quotacheck = lov_quotacheck,
};
struct kmem_cache *lov_oinfo_slab;
@@ -2352,7 +2352,7 @@ static void /*__exit*/ lov_exit(void)
lu_kmem_fini(lov_caches);
}
-MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_AUTHOR("OpenSFS, Inc. <http://www.lustre.org/>");
MODULE_DESCRIPTION("Lustre Logical Object Volume OBD driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(LUSTRE_VERSION_STRING);
diff --git a/drivers/staging/lustre/lustre/lov/lov_object.c b/drivers/staging/lustre/lustre/lov/lov_object.c
index c7ff817bb6fb..3b79ebc8eccf 100644
--- a/drivers/staging/lustre/lustre/lov/lov_object.c
+++ b/drivers/staging/lustre/lustre/lov/lov_object.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/lov/lov_offset.c b/drivers/staging/lustre/lustre/lov/lov_offset.c
index 9c8c77c05a8a..aa520aa76e09 100644
--- a/drivers/staging/lustre/lustre/lov/lov_offset.c
+++ b/drivers/staging/lustre/lustre/lov/lov_offset.c
@@ -27,7 +27,7 @@
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/lov/lov_pack.c b/drivers/staging/lustre/lustre/lov/lov_pack.c
index 2fb1e974cc70..6b2d1007192b 100644
--- a/drivers/staging/lustre/lustre/lov/lov_pack.c
+++ b/drivers/staging/lustre/lustre/lov/lov_pack.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -258,22 +258,9 @@ static int lov_verify_lmm(void *lmm, int lmm_bytes, __u16 *stripe_count)
int rc;
if (lsm_op_find(le32_to_cpu(*(__u32 *)lmm)) == NULL) {
- char *buffer;
- int sz;
-
CERROR("bad disk LOV MAGIC: 0x%08X; dumping LMM (size=%d):\n",
le32_to_cpu(*(__u32 *)lmm), lmm_bytes);
- sz = lmm_bytes * 2 + 1;
- buffer = libcfs_kvzalloc(sz, GFP_NOFS);
- if (buffer != NULL) {
- int i;
-
- for (i = 0; i < lmm_bytes; i++)
- sprintf(buffer+2*i, "%.2X", ((char *)lmm)[i]);
- buffer[sz - 1] = '\0';
- CERROR("%s\n", buffer);
- kvfree(buffer);
- }
+ CERROR("%*phN\n", lmm_bytes, lmm);
return -EINVAL;
}
rc = lsm_op_find(le32_to_cpu(*(__u32 *)lmm))->lsm_lmm_verify(lmm,
diff --git a/drivers/staging/lustre/lustre/lov/lov_page.c b/drivers/staging/lustre/lustre/lov/lov_page.c
index 463cadbd9d40..037ae91b74e7 100644
--- a/drivers/staging/lustre/lustre/lov/lov_page.c
+++ b/drivers/staging/lustre/lustre/lov/lov_page.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/lov/lov_pool.c b/drivers/staging/lustre/lustre/lov/lov_pool.c
index b03827ef6514..b43ce6cd64c2 100644
--- a/drivers/staging/lustre/lustre/lov/lov_pool.c
+++ b/drivers/staging/lustre/lustre/lov/lov_pool.c
@@ -412,8 +412,7 @@ int lov_pool_new(struct obd_device *obd, char *poolname)
if (!new_pool)
return -ENOMEM;
- strncpy(new_pool->pool_name, poolname, LOV_MAXPOOLNAME);
- new_pool->pool_name[LOV_MAXPOOLNAME] = '\0';
+ strlcpy(new_pool->pool_name, poolname, sizeof(new_pool->pool_name));
new_pool->pool_lobd = obd;
/* ref count init to 1 because when created a pool is always used
* up to deletion
diff --git a/drivers/staging/lustre/lustre/lov/lov_request.c b/drivers/staging/lustre/lustre/lov/lov_request.c
index 1a150c26798d..42deda71f577 100644
--- a/drivers/staging/lustre/lustre/lov/lov_request.c
+++ b/drivers/staging/lustre/lustre/lov/lov_request.c
@@ -27,7 +27,7 @@
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -74,7 +74,7 @@ void lov_finish_set(struct lov_request_set *set)
kfree(set);
}
-int lov_set_finished(struct lov_request_set *set, int idempotent)
+static int lov_set_finished(struct lov_request_set *set, int idempotent)
{
int completes = atomic_read(&set->set_completes);
@@ -89,8 +89,8 @@ int lov_set_finished(struct lov_request_set *set, int idempotent)
return 0;
}
-void lov_update_set(struct lov_request_set *set,
- struct lov_request *req, int rc)
+static void lov_update_set(struct lov_request_set *set,
+ struct lov_request *req, int rc)
{
req->rq_complete = 1;
req->rq_rc = rc;
@@ -118,7 +118,8 @@ int lov_update_common_set(struct lov_request_set *set,
return rc;
}
-void lov_set_add_req(struct lov_request *req, struct lov_request_set *set)
+static void lov_set_add_req(struct lov_request *req,
+ struct lov_request_set *set)
{
list_add_tail(&req->rq_link, &set->set_list);
set->set_count++;
@@ -144,7 +145,7 @@ static int lov_check_set(struct lov_obd *lov, int idx)
* If the OSC has not yet had a chance to connect to the OST the first time,
* wait once for it to connect instead of returning an error.
*/
-int lov_check_and_wait_active(struct lov_obd *lov, int ost_idx)
+static int lov_check_and_wait_active(struct lov_obd *lov, int ost_idx)
{
wait_queue_head_t waitq;
struct l_wait_info lwi;
@@ -591,8 +592,9 @@ int lov_fini_statfs_set(struct lov_request_set *set)
return rc;
}
-void lov_update_statfs(struct obd_statfs *osfs, struct obd_statfs *lov_sfs,
- int success)
+static void lov_update_statfs(struct obd_statfs *osfs,
+ struct obd_statfs *lov_sfs,
+ int success)
{
int shift = 0, quit = 0;
__u64 tmp;
diff --git a/drivers/staging/lustre/lustre/lov/lovsub_dev.c b/drivers/staging/lustre/lustre/lov/lovsub_dev.c
index 8bc04c8d3d60..f1795c3e2db5 100644
--- a/drivers/staging/lustre/lustre/lov/lovsub_dev.c
+++ b/drivers/staging/lustre/lustre/lov/lovsub_dev.c
@@ -26,6 +26,8 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright (c) 2013, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/lov/lovsub_object.c b/drivers/staging/lustre/lustre/lov/lovsub_object.c
index d775e28d4097..5ba5ee1b8681 100644
--- a/drivers/staging/lustre/lustre/lov/lovsub_object.c
+++ b/drivers/staging/lustre/lustre/lov/lovsub_object.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015 Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/lov/lproc_lov.c b/drivers/staging/lustre/lustre/lov/lproc_lov.c
index a0be15c6b55a..337241d84980 100644
--- a/drivers/staging/lustre/lustre/lov/lproc_lov.c
+++ b/drivers/staging/lustre/lustre/lov/lproc_lov.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015 Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/mdc/lproc_mdc.c b/drivers/staging/lustre/lustre/mdc/lproc_mdc.c
index 1c95f87a0e2a..38f267a60f59 100644
--- a/drivers/staging/lustre/lustre/mdc/lproc_mdc.c
+++ b/drivers/staging/lustre/lustre/mdc/lproc_mdc.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -82,82 +82,6 @@ static ssize_t max_rpcs_in_flight_store(struct kobject *kobj,
}
LUSTRE_RW_ATTR(max_rpcs_in_flight);
-static int mdc_kuc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, NULL, inode->i_private);
-}
-
-/* temporary for testing */
-static ssize_t mdc_kuc_write(struct file *file,
- const char __user *buffer,
- size_t count, loff_t *off)
-{
- struct obd_device *obd =
- ((struct seq_file *)file->private_data)->private;
- struct kuc_hdr *lh;
- struct hsm_action_list *hal;
- struct hsm_action_item *hai;
- int len;
- int fd, rc;
-
- rc = lprocfs_write_helper(buffer, count, &fd);
- if (rc)
- return rc;
-
- if (fd < 0)
- return -ERANGE;
- CWARN("message to fd %d\n", fd);
-
- len = sizeof(*lh) + sizeof(*hal) + MTI_NAME_MAXLEN +
- /* for mockup below */ 2 * cfs_size_round(sizeof(*hai));
-
- lh = kzalloc(len, GFP_NOFS);
- if (!lh)
- return -ENOMEM;
-
- lh->kuc_magic = KUC_MAGIC;
- lh->kuc_transport = KUC_TRANSPORT_HSM;
- lh->kuc_msgtype = HMT_ACTION_LIST;
- lh->kuc_msglen = len;
-
- hal = (struct hsm_action_list *)(lh + 1);
- hal->hal_version = HAL_VERSION;
- hal->hal_archive_id = 1;
- hal->hal_flags = 0;
- obd_uuid2fsname(hal->hal_fsname, obd->obd_name, MTI_NAME_MAXLEN);
-
- /* mock up an action list */
- hal->hal_count = 2;
- hai = hai_zero(hal);
- hai->hai_action = HSMA_ARCHIVE;
- hai->hai_fid.f_oid = 5;
- hai->hai_len = sizeof(*hai);
- hai = hai_next(hai);
- hai->hai_action = HSMA_RESTORE;
- hai->hai_fid.f_oid = 10;
- hai->hai_len = sizeof(*hai);
-
- /* This works for either broadcast or unicast to a single fd */
- if (fd == 0) {
- rc = libcfs_kkuc_group_put(KUC_GRP_HSM, lh);
- } else {
- struct file *fp = fget(fd);
-
- rc = libcfs_kkuc_msg_put(fp, lh);
- fput(fp);
- }
- kfree(lh);
- if (rc < 0)
- return rc;
- return count;
-}
-
-static struct file_operations mdc_kuc_fops = {
- .open = mdc_kuc_open,
- .write = mdc_kuc_write,
- .release = single_release,
-};
-
LPROC_SEQ_FOPS_WR_ONLY(mdc, ping);
LPROC_SEQ_FOPS_RO_TYPE(mdc, connect_flags);
@@ -196,7 +120,6 @@ static struct lprocfs_vars lprocfs_mdc_obd_vars[] = {
{ "timeouts", &mdc_timeouts_fops, NULL, 0 },
{ "import", &mdc_import_fops, NULL, 0 },
{ "state", &mdc_state_fops, NULL, 0 },
- { "hsm_nl", &mdc_kuc_fops, NULL, 0200 },
{ "pinger_recov", &mdc_pinger_recov_fops, NULL, 0 },
{ NULL }
};
diff --git a/drivers/staging/lustre/lustre/mdc/mdc_internal.h b/drivers/staging/lustre/lustre/mdc/mdc_internal.h
index 29b46f754726..3d2997a161b6 100644
--- a/drivers/staging/lustre/lustre/mdc/mdc_internal.h
+++ b/drivers/staging/lustre/lustre/mdc/mdc_internal.h
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, Intel Corporation.
+ * Copyright (c) 2011, 2015 Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -44,7 +44,6 @@ void lprocfs_mdc_init_vars(struct lprocfs_static_vars *lvars);
void mdc_pack_body(struct ptlrpc_request *req, const struct lu_fid *fid,
__u64 valid, int ea_size, __u32 suppgid, int flags);
-int mdc_pack_req(struct ptlrpc_request *req, int version, int opc);
void mdc_is_subdir_pack(struct ptlrpc_request *req, const struct lu_fid *pfid,
const struct lu_fid *cfid, int flags);
void mdc_swap_layouts_pack(struct ptlrpc_request *req,
@@ -62,7 +61,6 @@ void mdc_open_pack(struct ptlrpc_request *req, struct md_op_data *op_data,
__u32 mode, __u64 rdev, __u64 flags, const void *data,
int datalen);
void mdc_unlink_pack(struct ptlrpc_request *req, struct md_op_data *op_data);
-void mdc_getxattr_pack(struct ptlrpc_request *req, struct md_op_data *op_data);
void mdc_link_pack(struct ptlrpc_request *req, struct md_op_data *op_data);
void mdc_rename_pack(struct ptlrpc_request *req, struct md_op_data *op_data,
const char *old, int oldlen, const char *new, int newlen);
@@ -97,25 +95,12 @@ int mdc_resource_get_unused(struct obd_export *exp, const struct lu_fid *fid,
/* mdc/mdc_request.c */
int mdc_fid_alloc(struct obd_export *exp, struct lu_fid *fid,
struct md_op_data *op_data);
-
-int mdc_open(struct obd_export *exp, u64 ino, int type, int flags,
- struct lov_mds_md *lmm, int lmm_size, struct lustre_handle *fh,
- struct ptlrpc_request **);
-
struct obd_client_handle;
-int mdc_get_lustre_md(struct obd_export *md_exp, struct ptlrpc_request *req,
- struct obd_export *dt_exp, struct obd_export *lmv_exp,
- struct lustre_md *md);
-
-int mdc_free_lustre_md(struct obd_export *exp, struct lustre_md *md);
-
int mdc_set_open_replay_data(struct obd_export *exp,
struct obd_client_handle *och,
struct lookup_intent *it);
-int mdc_clear_open_replay_data(struct obd_export *exp,
- struct obd_client_handle *och);
void mdc_commit_open(struct ptlrpc_request *req);
void mdc_replay_open(struct ptlrpc_request *req);
diff --git a/drivers/staging/lustre/lustre/mdc/mdc_lib.c b/drivers/staging/lustre/lustre/mdc/mdc_lib.c
index 227fc9ee0dcf..7218532ffea3 100644
--- a/drivers/staging/lustre/lustre/mdc/mdc_lib.c
+++ b/drivers/staging/lustre/lustre/mdc/mdc_lib.c
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/mdc/mdc_locks.c b/drivers/staging/lustre/lustre/mdc/mdc_locks.c
index d4bf34b61f3a..ef9a1e124ea4 100644
--- a/drivers/staging/lustre/lustre/mdc/mdc_locks.c
+++ b/drivers/staging/lustre/lustre/mdc/mdc_locks.c
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/mdc/mdc_reint.c b/drivers/staging/lustre/lustre/mdc/mdc_reint.c
index c87c7d8efa07..ac7695a10753 100644
--- a/drivers/staging/lustre/lustre/mdc/mdc_reint.c
+++ b/drivers/staging/lustre/lustre/mdc/mdc_reint.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/mdc/mdc_request.c b/drivers/staging/lustre/lustre/mdc/mdc_request.c
index 16a5a10d371e..57e0fc1e8549 100644
--- a/drivers/staging/lustre/lustre/mdc/mdc_request.c
+++ b/drivers/staging/lustre/lustre/mdc/mdc_request.c
@@ -27,7 +27,7 @@
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -447,9 +447,11 @@ static int mdc_unpack_acl(struct ptlrpc_request *req, struct lustre_md *md)
#define mdc_unpack_acl(req, md) 0
#endif
-int mdc_get_lustre_md(struct obd_export *exp, struct ptlrpc_request *req,
- struct obd_export *dt_exp, struct obd_export *md_exp,
- struct lustre_md *md)
+static int mdc_get_lustre_md(struct obd_export *exp,
+ struct ptlrpc_request *req,
+ struct obd_export *dt_exp,
+ struct obd_export *md_exp,
+ struct lustre_md *md)
{
struct req_capsule *pill = &req->rq_pill;
int rc;
@@ -573,7 +575,7 @@ out:
return rc;
}
-int mdc_free_lustre_md(struct obd_export *exp, struct lustre_md *md)
+static int mdc_free_lustre_md(struct obd_export *exp, struct lustre_md *md)
{
return 0;
}
@@ -737,8 +739,8 @@ static void mdc_free_open(struct md_open_data *mod)
ptlrpc_request_committed(mod->mod_close_req, committed);
}
-int mdc_clear_open_replay_data(struct obd_export *exp,
- struct obd_client_handle *och)
+static int mdc_clear_open_replay_data(struct obd_export *exp,
+ struct obd_client_handle *och)
{
struct md_open_data *mod = och->och_mod;
@@ -1179,7 +1181,6 @@ static int mdc_ioc_hsm_progress(struct obd_export *exp,
ptlrpc_request_set_replen(req);
rc = mdc_queue_wait(req);
- goto out;
out:
ptlrpc_req_finished(req);
return rc;
@@ -1214,7 +1215,6 @@ static int mdc_ioc_hsm_ct_register(struct obd_import *imp, __u32 archives)
ptlrpc_request_set_replen(req);
rc = mdc_queue_wait(req);
- goto out;
out:
ptlrpc_req_finished(req);
return rc;
@@ -1280,7 +1280,6 @@ static int mdc_ioc_hsm_ct_unregister(struct obd_import *imp)
ptlrpc_request_set_replen(req);
rc = mdc_queue_wait(req);
- goto out;
out:
ptlrpc_req_finished(req);
return rc;
@@ -1360,8 +1359,6 @@ static int mdc_ioc_hsm_state_set(struct obd_export *exp,
ptlrpc_request_set_replen(req);
rc = mdc_queue_wait(req);
- goto out;
-
out:
ptlrpc_req_finished(req);
return rc;
@@ -1425,8 +1422,6 @@ static int mdc_ioc_hsm_request(struct obd_export *exp,
ptlrpc_request_set_replen(req);
rc = mdc_queue_wait(req);
- goto out;
-
out:
ptlrpc_req_finished(req);
return rc;
@@ -2035,17 +2030,6 @@ static int mdc_hsm_ct_reregister(__u32 data, void *cb_arg)
return ((rc != 0) && (rc != -EEXIST)) ? rc : 0;
}
-/**
- * Re-establish all kuc contexts with MDT
- * after MDT shutdown/recovery.
- */
-static int mdc_kuc_reregister(struct obd_import *imp)
-{
- /* re-register HSM agents */
- return libcfs_kkuc_group_foreach(KUC_GRP_HSM, mdc_hsm_ct_reregister,
- (void *)imp);
-}
-
static int mdc_set_info_async(const struct lu_env *env,
struct obd_export *exp,
u32 keylen, void *key,
@@ -2208,7 +2192,10 @@ static int mdc_import_event(struct obd_device *obd, struct obd_import *imp,
rc = obd_notify_observer(obd, obd, OBD_NOTIFY_ACTIVE, NULL);
/* redo the kuc registration after reconnecting */
if (rc == 0)
- rc = mdc_kuc_reregister(imp);
+ /* re-register HSM agents */
+ rc = libcfs_kkuc_group_foreach(KUC_GRP_HSM,
+ mdc_hsm_ct_reregister,
+ (void *)imp);
break;
case IMP_EVENT_OCD:
rc = obd_notify_observer(obd, obd, OBD_NOTIFY_OCD, NULL);
@@ -2460,59 +2447,59 @@ static int mdc_get_remote_perm(struct obd_export *exp, const struct lu_fid *fid,
}
static struct obd_ops mdc_obd_ops = {
- .o_owner = THIS_MODULE,
- .o_setup = mdc_setup,
- .o_precleanup = mdc_precleanup,
- .o_cleanup = mdc_cleanup,
- .o_add_conn = client_import_add_conn,
- .o_del_conn = client_import_del_conn,
- .o_connect = client_connect_import,
- .o_disconnect = client_disconnect_export,
- .o_iocontrol = mdc_iocontrol,
- .o_set_info_async = mdc_set_info_async,
- .o_statfs = mdc_statfs,
- .o_fid_init = client_fid_init,
- .o_fid_fini = client_fid_fini,
- .o_fid_alloc = mdc_fid_alloc,
- .o_import_event = mdc_import_event,
- .o_get_info = mdc_get_info,
- .o_process_config = mdc_process_config,
- .o_get_uuid = mdc_get_uuid,
- .o_quotactl = mdc_quotactl,
- .o_quotacheck = mdc_quotacheck
+ .owner = THIS_MODULE,
+ .setup = mdc_setup,
+ .precleanup = mdc_precleanup,
+ .cleanup = mdc_cleanup,
+ .add_conn = client_import_add_conn,
+ .del_conn = client_import_del_conn,
+ .connect = client_connect_import,
+ .disconnect = client_disconnect_export,
+ .iocontrol = mdc_iocontrol,
+ .set_info_async = mdc_set_info_async,
+ .statfs = mdc_statfs,
+ .fid_init = client_fid_init,
+ .fid_fini = client_fid_fini,
+ .fid_alloc = mdc_fid_alloc,
+ .import_event = mdc_import_event,
+ .get_info = mdc_get_info,
+ .process_config = mdc_process_config,
+ .get_uuid = mdc_get_uuid,
+ .quotactl = mdc_quotactl,
+ .quotacheck = mdc_quotacheck
};
static struct md_ops mdc_md_ops = {
- .m_getstatus = mdc_getstatus,
- .m_null_inode = mdc_null_inode,
- .m_find_cbdata = mdc_find_cbdata,
- .m_close = mdc_close,
- .m_create = mdc_create,
- .m_done_writing = mdc_done_writing,
- .m_enqueue = mdc_enqueue,
- .m_getattr = mdc_getattr,
- .m_getattr_name = mdc_getattr_name,
- .m_intent_lock = mdc_intent_lock,
- .m_link = mdc_link,
- .m_is_subdir = mdc_is_subdir,
- .m_rename = mdc_rename,
- .m_setattr = mdc_setattr,
- .m_setxattr = mdc_setxattr,
- .m_getxattr = mdc_getxattr,
- .m_sync = mdc_sync,
- .m_readpage = mdc_readpage,
- .m_unlink = mdc_unlink,
- .m_cancel_unused = mdc_cancel_unused,
- .m_init_ea_size = mdc_init_ea_size,
- .m_set_lock_data = mdc_set_lock_data,
- .m_lock_match = mdc_lock_match,
- .m_get_lustre_md = mdc_get_lustre_md,
- .m_free_lustre_md = mdc_free_lustre_md,
- .m_set_open_replay_data = mdc_set_open_replay_data,
- .m_clear_open_replay_data = mdc_clear_open_replay_data,
- .m_get_remote_perm = mdc_get_remote_perm,
- .m_intent_getattr_async = mdc_intent_getattr_async,
- .m_revalidate_lock = mdc_revalidate_lock
+ .getstatus = mdc_getstatus,
+ .null_inode = mdc_null_inode,
+ .find_cbdata = mdc_find_cbdata,
+ .close = mdc_close,
+ .create = mdc_create,
+ .done_writing = mdc_done_writing,
+ .enqueue = mdc_enqueue,
+ .getattr = mdc_getattr,
+ .getattr_name = mdc_getattr_name,
+ .intent_lock = mdc_intent_lock,
+ .link = mdc_link,
+ .is_subdir = mdc_is_subdir,
+ .rename = mdc_rename,
+ .setattr = mdc_setattr,
+ .setxattr = mdc_setxattr,
+ .getxattr = mdc_getxattr,
+ .sync = mdc_sync,
+ .readpage = mdc_readpage,
+ .unlink = mdc_unlink,
+ .cancel_unused = mdc_cancel_unused,
+ .init_ea_size = mdc_init_ea_size,
+ .set_lock_data = mdc_set_lock_data,
+ .lock_match = mdc_lock_match,
+ .get_lustre_md = mdc_get_lustre_md,
+ .free_lustre_md = mdc_free_lustre_md,
+ .set_open_replay_data = mdc_set_open_replay_data,
+ .clear_open_replay_data = mdc_clear_open_replay_data,
+ .get_remote_perm = mdc_get_remote_perm,
+ .intent_getattr_async = mdc_intent_getattr_async,
+ .revalidate_lock = mdc_revalidate_lock
};
static int __init mdc_init(void)
@@ -2530,7 +2517,7 @@ static void /*__exit*/ mdc_exit(void)
class_unregister_type(LUSTRE_MDC_NAME);
}
-MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_AUTHOR("OpenSFS, Inc. <http://www.lustre.org/>");
MODULE_DESCRIPTION("Lustre Metadata Client");
MODULE_LICENSE("GPL");
diff --git a/drivers/staging/lustre/lustre/mgc/mgc_request.c b/drivers/staging/lustre/lustre/mgc/mgc_request.c
index 5f53f3b7ceff..ab4800c20a95 100644
--- a/drivers/staging/lustre/lustre/mgc/mgc_request.c
+++ b/drivers/staging/lustre/lustre/mgc/mgc_request.c
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -463,7 +463,7 @@ int lprocfs_mgc_rd_ir_state(struct seq_file *m, void *data)
}
spin_unlock(&config_list_lock);
- LPROCFS_CLIMP_EXIT(obd);
+ up_read(&obd->u.cli.cl_sem);
return 0;
}
@@ -1293,7 +1293,7 @@ static int mgc_process_recover_log(struct obd_device *obd,
struct page **pages;
int nrpages;
bool eof = true;
- bool mne_swab = false;
+ bool mne_swab;
int i;
int ealen;
int rc;
@@ -1698,20 +1698,20 @@ out:
}
static struct obd_ops mgc_obd_ops = {
- .o_owner = THIS_MODULE,
- .o_setup = mgc_setup,
- .o_precleanup = mgc_precleanup,
- .o_cleanup = mgc_cleanup,
- .o_add_conn = client_import_add_conn,
- .o_del_conn = client_import_del_conn,
- .o_connect = client_connect_import,
- .o_disconnect = client_disconnect_export,
- /* .o_enqueue = mgc_enqueue, */
- /* .o_iocontrol = mgc_iocontrol, */
- .o_set_info_async = mgc_set_info_async,
- .o_get_info = mgc_get_info,
- .o_import_event = mgc_import_event,
- .o_process_config = mgc_process_config,
+ .owner = THIS_MODULE,
+ .setup = mgc_setup,
+ .precleanup = mgc_precleanup,
+ .cleanup = mgc_cleanup,
+ .add_conn = client_import_add_conn,
+ .del_conn = client_import_del_conn,
+ .connect = client_connect_import,
+ .disconnect = client_disconnect_export,
+ /* .enqueue = mgc_enqueue, */
+ /* .iocontrol = mgc_iocontrol, */
+ .set_info_async = mgc_set_info_async,
+ .get_info = mgc_get_info,
+ .import_event = mgc_import_event,
+ .process_config = mgc_process_config,
};
static int __init mgc_init(void)
@@ -1725,7 +1725,7 @@ static void /*__exit*/ mgc_exit(void)
class_unregister_type(LUSTRE_MGC_NAME);
}
-MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_AUTHOR("OpenSFS, Inc. <http://www.lustre.org/>");
MODULE_DESCRIPTION("Lustre Management Client");
MODULE_LICENSE("GPL");
diff --git a/drivers/staging/lustre/lustre/obdclass/acl.c b/drivers/staging/lustre/lustre/obdclass/acl.c
index 2e20cf635b27..49ba8851c8ac 100644
--- a/drivers/staging/lustre/lustre/obdclass/acl.c
+++ b/drivers/staging/lustre/lustre/obdclass/acl.c
@@ -236,15 +236,6 @@ _out:
EXPORT_SYMBOL(lustre_posix_acl_xattr_filter);
/*
- * Release the posix ACL space.
- */
-void lustre_posix_acl_xattr_free(posix_acl_xattr_header *header, int size)
-{
- kfree(header);
-}
-EXPORT_SYMBOL(lustre_posix_acl_xattr_free);
-
-/*
* Release the extended ACL space.
*/
void lustre_ext_acl_xattr_free(ext_acl_xattr_header *header)
diff --git a/drivers/staging/lustre/lustre/obdclass/cl_io.c b/drivers/staging/lustre/lustre/obdclass/cl_io.c
index e67cea758405..63246ba36798 100644
--- a/drivers/staging/lustre/lustre/obdclass/cl_io.c
+++ b/drivers/staging/lustre/lustre/obdclass/cl_io.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -236,16 +236,11 @@ int cl_io_rw_init(const struct lu_env *env, struct cl_io *io,
}
EXPORT_SYMBOL(cl_io_rw_init);
-static inline const struct lu_fid *
-cl_lock_descr_fid(const struct cl_lock_descr *descr)
-{
- return lu_object_fid(&descr->cld_obj->co_lu);
-}
-
static int cl_lock_descr_sort(const struct cl_lock_descr *d0,
const struct cl_lock_descr *d1)
{
- return lu_fid_cmp(cl_lock_descr_fid(d0), cl_lock_descr_fid(d1)) ?:
+ return lu_fid_cmp(lu_object_fid(&d0->cld_obj->co_lu),
+ lu_object_fid(&d1->cld_obj->co_lu)) ?:
__diff_normalize(d0->cld_start, d1->cld_start);
}
@@ -254,7 +249,8 @@ static int cl_lock_descr_cmp(const struct cl_lock_descr *d0,
{
int ret;
- ret = lu_fid_cmp(cl_lock_descr_fid(d0), cl_lock_descr_fid(d1));
+ ret = lu_fid_cmp(lu_object_fid(&d0->cld_obj->co_lu),
+ lu_object_fid(&d1->cld_obj->co_lu));
if (ret)
return ret;
if (d0->cld_end < d1->cld_start)
@@ -1216,15 +1212,6 @@ void cl_2queue_init(struct cl_2queue *queue)
EXPORT_SYMBOL(cl_2queue_init);
/**
- * Add a page to the incoming page list of 2-queue.
- */
-void cl_2queue_add(struct cl_2queue *queue, struct cl_page *page)
-{
- cl_page_list_add(&queue->c2_qin, page);
-}
-EXPORT_SYMBOL(cl_2queue_add);
-
-/**
* Disown pages in both lists of a 2-queue.
*/
void cl_2queue_disown(const struct lu_env *env,
@@ -1262,7 +1249,10 @@ EXPORT_SYMBOL(cl_2queue_fini);
void cl_2queue_init_page(struct cl_2queue *queue, struct cl_page *page)
{
cl_2queue_init(queue);
- cl_2queue_add(queue, page);
+ /*
+ * Add a page to the incoming page list of 2-queue.
+ */
+ cl_page_list_add(&queue->c2_qin, page);
}
EXPORT_SYMBOL(cl_2queue_init_page);
diff --git a/drivers/staging/lustre/lustre/obdclass/cl_lock.c b/drivers/staging/lustre/lustre/obdclass/cl_lock.c
index 5621bebf33a9..1836dc01499a 100644
--- a/drivers/staging/lustre/lustre/obdclass/cl_lock.c
+++ b/drivers/staging/lustre/lustre/obdclass/cl_lock.c
@@ -687,7 +687,7 @@ EXPORT_SYMBOL(cl_lock_mutex_get);
*
* \see cl_lock_mutex_get()
*/
-int cl_lock_mutex_try(const struct lu_env *env, struct cl_lock *lock)
+static int cl_lock_mutex_try(const struct lu_env *env, struct cl_lock *lock)
{
int result;
@@ -705,7 +705,6 @@ int cl_lock_mutex_try(const struct lu_env *env, struct cl_lock *lock)
result = -EBUSY;
return result;
}
-EXPORT_SYMBOL(cl_lock_mutex_try);
/**
{* Unlocks cl_lock object.
diff --git a/drivers/staging/lustre/lustre/obdclass/cl_object.c b/drivers/staging/lustre/lustre/obdclass/cl_object.c
index a1a6024220ff..57c8d5412bbd 100644
--- a/drivers/staging/lustre/lustre/obdclass/cl_object.c
+++ b/drivers/staging/lustre/lustre/obdclass/cl_object.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -704,7 +704,7 @@ static inline struct cl_env *cl_env_container(struct lu_env *env)
return container_of(env, struct cl_env, ce_lu);
}
-struct lu_env *cl_env_peek(int *refcheck)
+static struct lu_env *cl_env_peek(int *refcheck)
{
struct lu_env *env;
struct cl_env *cle;
@@ -724,7 +724,6 @@ struct lu_env *cl_env_peek(int *refcheck)
CDEBUG(D_OTHER, "%d@%p\n", cle ? cle->ce_ref : 0, cle);
return env;
}
-EXPORT_SYMBOL(cl_env_peek);
/**
* Returns lu_env: if there already is an environment associated with the
diff --git a/drivers/staging/lustre/lustre/obdclass/cl_page.c b/drivers/staging/lustre/lustre/obdclass/cl_page.c
index 2f569edd0811..61f28ebfc058 100644
--- a/drivers/staging/lustre/lustre/obdclass/cl_page.c
+++ b/drivers/staging/lustre/lustre/obdclass/cl_page.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/obdclass/class_obd.c b/drivers/staging/lustre/lustre/obdclass/class_obd.c
index 3e9c24684690..0975e443057c 100644
--- a/drivers/staging/lustre/lustre/obdclass/class_obd.c
+++ b/drivers/staging/lustre/lustre/obdclass/class_obd.c
@@ -27,7 +27,7 @@
* Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -576,7 +576,7 @@ static void cleanup_obdclass(void)
obd_zombie_impexp_stop();
}
-MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_AUTHOR("OpenSFS, Inc. <http://www.lustre.org/>");
MODULE_DESCRIPTION("Lustre Class Driver Build Version: " BUILD_VERSION);
MODULE_LICENSE("GPL");
MODULE_VERSION(LUSTRE_VERSION_STRING);
diff --git a/drivers/staging/lustre/lustre/obdclass/genops.c b/drivers/staging/lustre/lustre/obdclass/genops.c
index 6477aeb88028..228c44c37c4a 100644
--- a/drivers/staging/lustre/lustre/obdclass/genops.c
+++ b/drivers/staging/lustre/lustre/obdclass/genops.c
@@ -27,7 +27,7 @@
* Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -132,7 +132,7 @@ static struct obd_type *class_get_type(const char *name)
if (type) {
spin_lock(&type->obd_type_lock);
type->typ_refcnt++;
- try_module_get(type->typ_dt_ops->o_owner);
+ try_module_get(type->typ_dt_ops->owner);
spin_unlock(&type->obd_type_lock);
}
return type;
@@ -143,7 +143,7 @@ void class_put_type(struct obd_type *type)
LASSERT(type);
spin_lock(&type->obd_type_lock);
type->typ_refcnt--;
- module_put(type->typ_dt_ops->o_owner);
+ module_put(type->typ_dt_ops->owner);
spin_unlock(&type->obd_type_lock);
}
EXPORT_SYMBOL(class_put_type);
@@ -155,7 +155,7 @@ int class_register_type(struct obd_ops *dt_ops, struct md_ops *md_ops,
struct lu_device_type *ldt)
{
struct obd_type *type;
- int rc = 0;
+ int rc;
/* sanity check */
LASSERT(strnlen(name, CLASS_MAX_NAME) < CLASS_MAX_NAME);
diff --git a/drivers/staging/lustre/lustre/obdclass/linux/linux-sysctl.c b/drivers/staging/lustre/lustre/obdclass/linux/linux-sysctl.c
index 518288df4d53..42fc26f4ae25 100644
--- a/drivers/staging/lustre/lustre/obdclass/linux/linux-sysctl.c
+++ b/drivers/staging/lustre/lustre/obdclass/linux/linux-sysctl.c
@@ -27,7 +27,7 @@
* Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/obdclass/llog.c b/drivers/staging/lustre/lustre/obdclass/llog.c
index 7cb55ef79737..f956d7ed6785 100644
--- a/drivers/staging/lustre/lustre/obdclass/llog.c
+++ b/drivers/staging/lustre/lustre/obdclass/llog.c
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/obdclass/llog_cat.c b/drivers/staging/lustre/lustre/obdclass/llog_cat.c
index c442cae5fd37..0f05e9c4a5b2 100644
--- a/drivers/staging/lustre/lustre/obdclass/llog_cat.c
+++ b/drivers/staging/lustre/lustre/obdclass/llog_cat.c
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/obdclass/llog_internal.h b/drivers/staging/lustre/lustre/obdclass/llog_internal.h
index b9fe4b01c690..7fb48dda355e 100644
--- a/drivers/staging/lustre/lustre/obdclass/llog_internal.h
+++ b/drivers/staging/lustre/lustre/obdclass/llog_internal.h
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/obdclass/llog_obd.c b/drivers/staging/lustre/lustre/obdclass/llog_obd.c
index 3900b9d4007e..9bc51998c05c 100644
--- a/drivers/staging/lustre/lustre/obdclass/llog_obd.c
+++ b/drivers/staging/lustre/lustre/obdclass/llog_obd.c
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/obdclass/llog_swab.c b/drivers/staging/lustre/lustre/obdclass/llog_swab.c
index 9354f75b5cab..3aa7393b20c3 100644
--- a/drivers/staging/lustre/lustre/obdclass/llog_swab.c
+++ b/drivers/staging/lustre/lustre/obdclass/llog_swab.c
@@ -27,7 +27,7 @@
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/obdclass/lprocfs_status.c b/drivers/staging/lustre/lustre/obdclass/lprocfs_status.c
index 333ac7d269b7..51fe15f5d687 100644
--- a/drivers/staging/lustre/lustre/obdclass/lprocfs_status.c
+++ b/drivers/staging/lustre/lustre/obdclass/lprocfs_status.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -502,7 +502,7 @@ int lprocfs_rd_server_uuid(struct seq_file *m, void *data)
obd2cli_tgt(obd), imp_state_name,
imp->imp_deactive ? "\tDEACTIVATED" : "");
- LPROCFS_CLIMP_EXIT(obd);
+ up_read(&obd->u.cli.cl_sem);
return 0;
}
@@ -526,7 +526,7 @@ int lprocfs_rd_conn_uuid(struct seq_file *m, void *data)
else
seq_puts(m, "<none>\n");
- LPROCFS_CLIMP_EXIT(obd);
+ up_read(&obd->u.cli.cl_sem);
return 0;
}
@@ -665,15 +665,18 @@ int lprocfs_rd_import(struct seq_file *m, void *data)
seq_printf(m, "%s%s", j ? ", " : "", nidstr);
j++;
}
- libcfs_nid2str_r(imp->imp_connection->c_peer.nid,
- nidstr, sizeof(nidstr));
+ if (imp->imp_connection != NULL)
+ libcfs_nid2str_r(imp->imp_connection->c_peer.nid,
+ nidstr, sizeof(nidstr));
+ else
+ strncpy(nidstr, "<none>", sizeof(nidstr));
seq_printf(m,
"]\n"
" current_connection: %s\n"
" connection_attempts: %u\n"
" generation: %u\n"
" in-progress_invalidations: %u\n",
- imp->imp_connection == NULL ? "<none>" : nidstr,
+ nidstr,
imp->imp_conn_cnt,
imp->imp_generation,
atomic_read(&imp->imp_inval_count));
@@ -765,7 +768,7 @@ int lprocfs_rd_import(struct seq_file *m, void *data)
}
out_climp:
- LPROCFS_CLIMP_EXIT(obd);
+ up_read(&obd->u.cli.cl_sem);
return 0;
}
EXPORT_SYMBOL(lprocfs_rd_import);
@@ -796,7 +799,7 @@ int lprocfs_rd_state(struct seq_file *m, void *data)
ptlrpc_import_state_name(ish->ish_state));
}
- LPROCFS_CLIMP_EXIT(obd);
+ up_read(&obd->u.cli.cl_sem);
return 0;
}
EXPORT_SYMBOL(lprocfs_rd_state);
@@ -857,7 +860,7 @@ int lprocfs_rd_timeouts(struct seq_file *m, void *data)
lprocfs_at_hist_helper(m, &imp->imp_at.iat_service_estimate[i]);
}
- LPROCFS_CLIMP_EXIT(obd);
+ up_read(&obd->u.cli.cl_sem);
return 0;
}
EXPORT_SYMBOL(lprocfs_rd_timeouts);
@@ -876,7 +879,7 @@ int lprocfs_rd_connect_flags(struct seq_file *m, void *data)
seq_printf(m, "flags=%#llx\n", flags);
obd_connect_seq_flags2str(m, flags, "\n");
seq_printf(m, "\n");
- LPROCFS_CLIMP_EXIT(obd);
+ up_read(&obd->u.cli.cl_sem);
return 0;
}
EXPORT_SYMBOL(lprocfs_rd_connect_flags);
diff --git a/drivers/staging/lustre/lustre/obdclass/lu_object.c b/drivers/staging/lustre/lustre/obdclass/lu_object.c
index 0193608a930a..ce248f4072c2 100644
--- a/drivers/staging/lustre/lustre/obdclass/lu_object.c
+++ b/drivers/staging/lustre/lustre/obdclass/lu_object.c
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -916,7 +916,7 @@ static void lu_obj_hop_put_locked(struct cfs_hash *hs, struct hlist_node *hnode)
LBUG(); /* we should never called it */
}
-struct cfs_hash_ops lu_site_hash_ops = {
+static struct cfs_hash_ops lu_site_hash_ops = {
.hs_hash = lu_obj_hop_hash,
.hs_key = lu_obj_hop_key,
.hs_keycmp = lu_obj_hop_keycmp,
diff --git a/drivers/staging/lustre/lustre/obdclass/obd_config.c b/drivers/staging/lustre/lustre/obdclass/obd_config.c
index c231e0da0e2a..49cdc647910c 100644
--- a/drivers/staging/lustre/lustre/obdclass/obd_config.c
+++ b/drivers/staging/lustre/lustre/obdclass/obd_config.c
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/obdclass/obd_mount.c b/drivers/staging/lustre/lustre/obdclass/obd_mount.c
index 48003d5325e3..b5aa8168dbff 100644
--- a/drivers/staging/lustre/lustre/obdclass/obd_mount.c
+++ b/drivers/staging/lustre/lustre/obdclass/obd_mount.c
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -892,7 +892,7 @@ static int lmd_parse(char *options, struct lustre_mount_data *lmd)
}
lmd->lmd_magic = LMD_MAGIC;
- lmd->lmd_params = kzalloc(4096, GFP_NOFS);
+ lmd->lmd_params = kzalloc(LMD_PARAMS_MAXLEN, GFP_NOFS);
if (!lmd->lmd_params)
return -ENOMEM;
lmd->lmd_params[0] = '\0';
@@ -978,7 +978,7 @@ static int lmd_parse(char *options, struct lustre_mount_data *lmd)
goto invalid;
clear++;
} else if (strncmp(s1, "param=", 6) == 0) {
- int length;
+ size_t length, params_length;
char *tail = strchr(s1 + 6, ',');
if (tail == NULL)
@@ -986,8 +986,12 @@ static int lmd_parse(char *options, struct lustre_mount_data *lmd)
else
length = tail - s1;
length -= 6;
+ params_length = strlen(lmd->lmd_params);
+ if (params_length + length + 1 >= LMD_PARAMS_MAXLEN)
+ return -E2BIG;
strncat(lmd->lmd_params, s1 + 6, length);
- strcat(lmd->lmd_params, " ");
+ lmd->lmd_params[params_length + length] = '\0';
+ strlcat(lmd->lmd_params, " ", LMD_PARAMS_MAXLEN);
clear++;
} else if (strncmp(s1, "osd=", 4) == 0) {
rc = lmd_parse_string(&lmd->lmd_osd_type, s1 + 4);
diff --git a/drivers/staging/lustre/lustre/obdecho/echo_client.c b/drivers/staging/lustre/lustre/obdecho/echo_client.c
index f61ef669644c..7b53f7dd1797 100644
--- a/drivers/staging/lustre/lustre/obdecho/echo_client.c
+++ b/drivers/staging/lustre/lustre/obdecho/echo_client.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -1228,8 +1228,10 @@ static int cl_echo_object_brw(struct echo_object *eco, int rw, u64 offset,
cl_page_put(env, clp);
break;
}
-
- cl_2queue_add(queue, clp);
+ /*
+ * Add a page to the incoming page list of 2-queue.
+ */
+ cl_page_list_add(&queue->c2_qin, clp);
/* drop the reference count for cl_page_find, so that the page
* will be freed in cl_2queue_fini. */
@@ -1270,6 +1272,7 @@ static int
echo_copyout_lsm(struct lov_stripe_md *lsm, void *_ulsm, int ulsm_nob)
{
struct lov_stripe_md *ulsm = _ulsm;
+ struct lov_oinfo **p;
int nob, i;
nob = offsetof(struct lov_stripe_md, lsm_oinfo[lsm->lsm_stripe_count]);
@@ -1279,9 +1282,10 @@ echo_copyout_lsm(struct lov_stripe_md *lsm, void *_ulsm, int ulsm_nob)
if (copy_to_user(ulsm, lsm, sizeof(*ulsm)))
return -EFAULT;
- for (i = 0; i < lsm->lsm_stripe_count; i++) {
- if (copy_to_user(ulsm->lsm_oinfo[i], lsm->lsm_oinfo[i],
- sizeof(lsm->lsm_oinfo[0])))
+ for (i = 0, p = lsm->lsm_oinfo; i < lsm->lsm_stripe_count; i++, p++) {
+ struct lov_oinfo __user *up;
+ if (get_user(up, ulsm->lsm_oinfo + i) ||
+ copy_to_user(up, *p, sizeof(struct lov_oinfo)))
return -EFAULT;
}
return 0;
@@ -1289,9 +1293,10 @@ echo_copyout_lsm(struct lov_stripe_md *lsm, void *_ulsm, int ulsm_nob)
static int
echo_copyin_lsm(struct echo_device *ed, struct lov_stripe_md *lsm,
- void *ulsm, int ulsm_nob)
+ struct lov_stripe_md __user *ulsm, int ulsm_nob)
{
struct echo_client_obd *ec = ed->ed_ec;
+ struct lov_oinfo **p;
int i;
if (ulsm_nob < sizeof(*lsm))
@@ -1306,11 +1311,10 @@ echo_copyin_lsm(struct echo_device *ed, struct lov_stripe_md *lsm,
((__u64)lsm->lsm_stripe_size * lsm->lsm_stripe_count > ~0UL))
return -EINVAL;
- for (i = 0; i < lsm->lsm_stripe_count; i++) {
- if (copy_from_user(lsm->lsm_oinfo[i],
- ((struct lov_stripe_md *)ulsm)-> \
- lsm_oinfo[i],
- sizeof(lsm->lsm_oinfo[0])))
+ for (i = 0, p = lsm->lsm_oinfo; i < lsm->lsm_stripe_count; i++, p++) {
+ struct lov_oinfo __user *up;
+ if (get_user(up, ulsm->lsm_oinfo + i) ||
+ copy_from_user(*p, up, sizeof(struct lov_oinfo)))
return -EFAULT;
}
return 0;
@@ -2128,10 +2132,10 @@ static int echo_client_disconnect(struct obd_export *exp)
}
static struct obd_ops echo_client_obd_ops = {
- .o_owner = THIS_MODULE,
- .o_iocontrol = echo_client_iocontrol,
- .o_connect = echo_client_connect,
- .o_disconnect = echo_client_disconnect
+ .owner = THIS_MODULE,
+ .iocontrol = echo_client_iocontrol,
+ .connect = echo_client_connect,
+ .disconnect = echo_client_disconnect
};
static int echo_client_init(void)
@@ -2170,7 +2174,7 @@ static void /*__exit*/ obdecho_exit(void)
}
-MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_AUTHOR("OpenSFS, Inc. <http://www.lustre.org/>");
MODULE_DESCRIPTION("Lustre Testing Echo OBD driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(LUSTRE_VERSION_STRING);
diff --git a/drivers/staging/lustre/lustre/osc/lproc_osc.c b/drivers/staging/lustre/lustre/osc/lproc_osc.c
index c4d44e70f1d7..1091536fc90d 100644
--- a/drivers/staging/lustre/lustre/osc/lproc_osc.c
+++ b/drivers/staging/lustre/lustre/osc/lproc_osc.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/osc/osc_cache.c b/drivers/staging/lustre/lustre/osc/osc_cache.c
index b1d1a87f05e3..2229419b7184 100644
--- a/drivers/staging/lustre/lustre/osc/osc_cache.c
+++ b/drivers/staging/lustre/lustre/osc/osc_cache.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*
*/
/*
@@ -619,9 +619,9 @@ static inline int overlapped(struct osc_extent *ex1, struct osc_extent *ex2)
* Find or create an extent which includes @index, core function to manage
* extent tree.
*/
-struct osc_extent *osc_extent_find(const struct lu_env *env,
- struct osc_object *obj, pgoff_t index,
- int *grants)
+static struct osc_extent *osc_extent_find(const struct lu_env *env,
+ struct osc_object *obj, pgoff_t index,
+ int *grants)
{
struct client_obd *cli = osc_cli(obj);
@@ -1420,8 +1420,8 @@ static void __osc_unreserve_grant(struct client_obd *cli,
}
}
-void osc_unreserve_grant(struct client_obd *cli,
- unsigned int reserved, unsigned int unused)
+static void osc_unreserve_grant(struct client_obd *cli,
+ unsigned int reserved, unsigned int unused)
{
client_obd_list_lock(&cli->cl_loi_list_lock);
__osc_unreserve_grant(cli, reserved, unused);
diff --git a/drivers/staging/lustre/lustre/osc/osc_cl_internal.h b/drivers/staging/lustre/lustre/osc/osc_cl_internal.h
index d2d68452d382..415c27e4ab66 100644
--- a/drivers/staging/lustre/lustre/osc/osc_cl_internal.h
+++ b/drivers/staging/lustre/lustre/osc/osc_cl_internal.h
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/osc/osc_dev.c b/drivers/staging/lustre/lustre/osc/osc_dev.c
index 69b523c0f570..7078cc57d8b9 100644
--- a/drivers/staging/lustre/lustre/osc/osc_dev.c
+++ b/drivers/staging/lustre/lustre/osc/osc_dev.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/osc/osc_internal.h b/drivers/staging/lustre/lustre/osc/osc_internal.h
index 5ed30ecc84e3..a4c61463b1c7 100644
--- a/drivers/staging/lustre/lustre/osc/osc_internal.h
+++ b/drivers/staging/lustre/lustre/osc/osc_internal.h
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -89,11 +89,6 @@ struct osc_cache_waiter {
int ocw_rc;
};
-int osc_create(const struct lu_env *env, struct obd_export *exp,
- struct obdo *oa, struct lov_stripe_md **ea,
- struct obd_trans_info *oti);
-int osc_real_create(struct obd_export *exp, struct obdo *oa,
- struct lov_stripe_md **ea, struct obd_trans_info *oti);
void osc_wake_cache_waiters(struct client_obd *cli);
int osc_shrink_grant_to_target(struct client_obd *cli, __u64 target_bytes);
void osc_update_next_shrink(struct client_obd *cli);
@@ -137,7 +132,6 @@ int osc_lru_shrink(struct client_obd *cli, int target);
extern spinlock_t osc_ast_guard;
-int osc_cleanup(struct obd_device *obd);
int osc_setup(struct obd_device *obd, struct lustre_cfg *lcfg);
int lproc_osc_attach_seqstat(struct obd_device *dev);
diff --git a/drivers/staging/lustre/lustre/osc/osc_io.c b/drivers/staging/lustre/lustre/osc/osc_io.c
index d413496c0f63..abd0beb483fe 100644
--- a/drivers/staging/lustre/lustre/osc/osc_io.c
+++ b/drivers/staging/lustre/lustre/osc/osc_io.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/osc/osc_lock.c b/drivers/staging/lustre/lustre/osc/osc_lock.c
index 194490dcaca9..71f2810d18b9 100644
--- a/drivers/staging/lustre/lustre/osc/osc_lock.c
+++ b/drivers/staging/lustre/lustre/osc/osc_lock.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/osc/osc_object.c b/drivers/staging/lustre/lustre/osc/osc_object.c
index ba57f8df5c7f..fdd6219aacf6 100644
--- a/drivers/staging/lustre/lustre/osc/osc_object.c
+++ b/drivers/staging/lustre/lustre/osc/osc_object.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -158,8 +158,8 @@ static int osc_attr_get(const struct lu_env *env, struct cl_object *obj,
return 0;
}
-int osc_attr_set(const struct lu_env *env, struct cl_object *obj,
- const struct cl_attr *attr, unsigned valid)
+static int osc_attr_set(const struct lu_env *env, struct cl_object *obj,
+ const struct cl_attr *attr, unsigned valid)
{
struct lov_oinfo *oinfo = cl2osc(obj)->oo_oinfo;
struct ost_lvb *lvb = &oinfo->loi_lvb;
diff --git a/drivers/staging/lustre/lustre/osc/osc_page.c b/drivers/staging/lustre/lustre/osc/osc_page.c
index 61eaf7172fdf..2439d804fe75 100644
--- a/drivers/staging/lustre/lustre/osc/osc_page.c
+++ b/drivers/staging/lustre/lustre/osc/osc_page.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/osc/osc_quota.c b/drivers/staging/lustre/lustre/osc/osc_quota.c
index 199783103f71..e70e7961d763 100644
--- a/drivers/staging/lustre/lustre/osc/osc_quota.c
+++ b/drivers/staging/lustre/lustre/osc/osc_quota.c
@@ -23,7 +23,7 @@
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*
* Code originally extracted from quota directory
*/
diff --git a/drivers/staging/lustre/lustre/osc/osc_request.c b/drivers/staging/lustre/lustre/osc/osc_request.c
index 367f83af12c0..7034f0a942c5 100644
--- a/drivers/staging/lustre/lustre/osc/osc_request.c
+++ b/drivers/staging/lustre/lustre/osc/osc_request.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -104,7 +104,7 @@ struct osc_enqueue_args {
static void osc_release_ppga(struct brw_page **ppga, u32 count);
static int brw_interpret(const struct lu_env *env,
struct ptlrpc_request *req, void *data, int rc);
-int osc_cleanup(struct obd_device *obd);
+static int osc_cleanup(struct obd_device *obd);
/* Pack OSC object metadata for disk storage (LE byte order). */
static int osc_packmd(struct obd_export *exp, struct lov_mds_md **lmmp,
@@ -431,8 +431,9 @@ static int osc_setattr_async(struct obd_export *exp, struct obd_info *oinfo,
oinfo->oi_cb_up, oinfo, rqset);
}
-int osc_real_create(struct obd_export *exp, struct obdo *oa,
- struct lov_stripe_md **ea, struct obd_trans_info *oti)
+static int osc_real_create(struct obd_export *exp, struct obdo *oa,
+ struct lov_stripe_md **ea,
+ struct obd_trans_info *oti)
{
struct ptlrpc_request *req;
struct ost_body *body;
@@ -689,9 +690,9 @@ static int osc_can_send_destroy(struct client_obd *cli)
return 0;
}
-int osc_create(const struct lu_env *env, struct obd_export *exp,
- struct obdo *oa, struct lov_stripe_md **ea,
- struct obd_trans_info *oti)
+static int osc_create(const struct lu_env *env, struct obd_export *exp,
+ struct obdo *oa, struct lov_stripe_md **ea,
+ struct obd_trans_info *oti)
{
int rc = 0;
@@ -2726,7 +2727,7 @@ static int osc_get_info(const struct lu_env *env, struct obd_export *exp,
}
*((u64 *)val) = *reply;
- out:
+out:
ptlrpc_req_finished(req);
return rc;
} else if (KEY_IS(KEY_FIEMAP)) {
@@ -3255,33 +3256,33 @@ static int osc_process_config(struct obd_device *obd, u32 len, void *buf)
}
struct obd_ops osc_obd_ops = {
- .o_owner = THIS_MODULE,
- .o_setup = osc_setup,
- .o_precleanup = osc_precleanup,
- .o_cleanup = osc_cleanup,
- .o_add_conn = client_import_add_conn,
- .o_del_conn = client_import_del_conn,
- .o_connect = client_connect_import,
- .o_reconnect = osc_reconnect,
- .o_disconnect = osc_disconnect,
- .o_statfs = osc_statfs,
- .o_statfs_async = osc_statfs_async,
- .o_packmd = osc_packmd,
- .o_unpackmd = osc_unpackmd,
- .o_create = osc_create,
- .o_destroy = osc_destroy,
- .o_getattr = osc_getattr,
- .o_getattr_async = osc_getattr_async,
- .o_setattr = osc_setattr,
- .o_setattr_async = osc_setattr_async,
- .o_find_cbdata = osc_find_cbdata,
- .o_iocontrol = osc_iocontrol,
- .o_get_info = osc_get_info,
- .o_set_info_async = osc_set_info_async,
- .o_import_event = osc_import_event,
- .o_process_config = osc_process_config,
- .o_quotactl = osc_quotactl,
- .o_quotacheck = osc_quotacheck,
+ .owner = THIS_MODULE,
+ .setup = osc_setup,
+ .precleanup = osc_precleanup,
+ .cleanup = osc_cleanup,
+ .add_conn = client_import_add_conn,
+ .del_conn = client_import_del_conn,
+ .connect = client_connect_import,
+ .reconnect = osc_reconnect,
+ .disconnect = osc_disconnect,
+ .statfs = osc_statfs,
+ .statfs_async = osc_statfs_async,
+ .packmd = osc_packmd,
+ .unpackmd = osc_unpackmd,
+ .create = osc_create,
+ .destroy = osc_destroy,
+ .getattr = osc_getattr,
+ .getattr_async = osc_getattr_async,
+ .setattr = osc_setattr,
+ .setattr_async = osc_setattr_async,
+ .find_cbdata = osc_find_cbdata,
+ .iocontrol = osc_iocontrol,
+ .get_info = osc_get_info,
+ .set_info_async = osc_set_info_async,
+ .import_event = osc_import_event,
+ .process_config = osc_process_config,
+ .quotactl = osc_quotactl,
+ .quotacheck = osc_quotacheck,
};
extern struct lu_kmem_descr osc_caches[];
@@ -3357,7 +3358,7 @@ static void /*__exit*/ osc_exit(void)
ptlrpc_free_rq_pool(osc_rq_pool);
}
-MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_AUTHOR("OpenSFS, Inc. <http://www.lustre.org/>");
MODULE_DESCRIPTION("Lustre Object Storage Client (OSC)");
MODULE_LICENSE("GPL");
MODULE_VERSION(LUSTRE_VERSION_STRING);
diff --git a/drivers/staging/lustre/lustre/ptlrpc/client.c b/drivers/staging/lustre/lustre/ptlrpc/client.c
index a9f1bf536da9..efdda09507bf 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/client.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/client.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/ptlrpc/events.c b/drivers/staging/lustre/lustre/ptlrpc/events.c
index 9c2fd34e2eb9..990156986986 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/events.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/events.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015 Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/ptlrpc/import.c b/drivers/staging/lustre/lustre/ptlrpc/import.c
index bfa410f7e773..f752c789bda0 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/import.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/import.c
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/ptlrpc/layout.c b/drivers/staging/lustre/lustre/ptlrpc/layout.c
index d7c4f47808bd..c0e613c23854 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/layout.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/layout.c
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/ptlrpc/llog_client.c b/drivers/staging/lustre/lustre/ptlrpc/llog_client.c
index 5122205cbb99..e87702073f1f 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/llog_client.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/llog_client.c
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/ptlrpc/lproc_ptlrpc.c b/drivers/staging/lustre/lustre/ptlrpc/lproc_ptlrpc.c
index afab0dee7a5c..cc55b7973721 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/lproc_ptlrpc.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/lproc_ptlrpc.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -1197,7 +1197,7 @@ int lprocfs_wr_ping(struct file *file, const char __user *buffer,
return rc;
req = ptlrpc_prep_ping(obd->u.cli.cl_import);
- LPROCFS_CLIMP_EXIT(obd);
+ up_read(&obd->u.cli.cl_sem);
if (req == NULL)
return -ENOMEM;
@@ -1291,7 +1291,7 @@ int lprocfs_rd_pinger_recov(struct seq_file *m, void *n)
return rc;
seq_printf(m, "%d\n", !imp->imp_no_pinger_recover);
- LPROCFS_CLIMP_EXIT(obd);
+ up_read(&obd->u.cli.cl_sem);
return 0;
}
@@ -1319,7 +1319,7 @@ int lprocfs_wr_pinger_recov(struct file *file, const char __user *buffer,
spin_lock(&imp->imp_lock);
imp->imp_no_pinger_recover = !val;
spin_unlock(&imp->imp_lock);
- LPROCFS_CLIMP_EXIT(obd);
+ up_read(&obd->u.cli.cl_sem);
return count;
diff --git a/drivers/staging/lustre/lustre/ptlrpc/niobuf.c b/drivers/staging/lustre/lustre/ptlrpc/niobuf.c
index 09ddeef6ba48..c5d7ff5cbd73 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/niobuf.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/niobuf.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/ptlrpc/pers.c b/drivers/staging/lustre/lustre/ptlrpc/pers.c
index 2a2a9fb6549c..ec3af109a1d7 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/pers.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/pers.c
@@ -26,6 +26,8 @@
/*
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright (c) 2014, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/ptlrpc/pinger.c b/drivers/staging/lustre/lustre/ptlrpc/pinger.c
index 5c719f175657..fb2d5236a971 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/pinger.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/pinger.c
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_internal.h b/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_internal.h
index ab6c4580f91c..8f67e0562b73 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_internal.h
+++ b/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_internal.h
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -73,7 +73,6 @@ void ptlrpc_request_handle_notconn(struct ptlrpc_request *);
void lustre_assert_wire_constants(void);
int ptlrpc_import_in_recovery(struct obd_import *imp);
int ptlrpc_set_import_discon(struct obd_import *imp, __u32 conn_cnt);
-void ptlrpc_handle_failed_import(struct obd_import *imp);
int ptlrpc_replay_next(struct obd_import *imp, int *inflight);
void ptlrpc_initiate_recovery(struct obd_import *imp);
@@ -88,8 +87,6 @@ void ptlrpc_ldebugfs_register_service(struct dentry *debugfs_entry,
struct ptlrpc_service *svc);
void ptlrpc_lprocfs_unregister_service(struct ptlrpc_service *svc);
void ptlrpc_lprocfs_rpc_sent(struct ptlrpc_request *req, long amount);
-void ptlrpc_lprocfs_do_request_stat(struct ptlrpc_request *req,
- long q_usec, long work_usec);
/* NRS */
diff --git a/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_module.c b/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_module.c
index 9deeb244166f..c4f1d0f5deb2 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_module.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_module.c
@@ -160,7 +160,7 @@ static void __exit ptlrpc_exit(void)
ptlrpc_connection_fini();
}
-MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_AUTHOR("OpenSFS, Inc. <http://www.lustre.org/>");
MODULE_DESCRIPTION("Lustre Request Processor and Lock Management");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.0.0");
diff --git a/drivers/staging/lustre/lustre/ptlrpc/ptlrpcd.c b/drivers/staging/lustre/lustre/ptlrpc/ptlrpcd.c
index ce036a1ac466..60fb0ced7137 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/ptlrpcd.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/ptlrpcd.c
@@ -27,7 +27,7 @@
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -422,6 +422,7 @@ static int ptlrpcd(void *arg)
complete(&pc->pc_starting);
/*
+
* This mainloop strongly resembles ptlrpc_set_wait() except that our
* set never completes. ptlrpcd_check() calls ptlrpc_check_set() when
* there are requests in the set. New requests come in on the set's
diff --git a/drivers/staging/lustre/lustre/ptlrpc/recover.c b/drivers/staging/lustre/lustre/ptlrpc/recover.c
index 7b1d72947330..db6626cab6f2 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/recover.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/recover.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/ptlrpc/sec_bulk.c b/drivers/staging/lustre/lustre/ptlrpc/sec_bulk.c
index cd8a9987f7ac..6152c1b766c3 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/sec_bulk.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/sec_bulk.c
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/ptlrpc/sec_config.c b/drivers/staging/lustre/lustre/ptlrpc/sec_config.c
index 7ff948fe1424..4b0b81c115ee 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/sec_config.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/sec_config.c
@@ -27,7 +27,7 @@
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
@@ -83,8 +83,7 @@ int sptlrpc_parse_flavor(const char *str, struct sptlrpc_flavor *flvr)
return 0;
}
- strncpy(buf, str, sizeof(buf));
- buf[sizeof(buf) - 1] = '\0';
+ strlcpy(buf, str, sizeof(buf));
bulk = strchr(buf, '-');
if (bulk)
diff --git a/drivers/staging/lustre/lustre/ptlrpc/sec_plain.c b/drivers/staging/lustre/lustre/ptlrpc/sec_plain.c
index f448b4567af0..905a41451ca3 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/sec_plain.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/sec_plain.c
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, 2012, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/ptlrpc/service.c b/drivers/staging/lustre/lustre/ptlrpc/service.c
index f45898f17793..8598300a61d1 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/service.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/service.c
@@ -27,7 +27,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2010, 2012, Intel Corporation.
+ * Copyright (c) 2010, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/lustre/lustre/ptlrpc/wiretest.c b/drivers/staging/lustre/lustre/ptlrpc/wiretest.c
index 40f720ca3b14..61d9ca93c53a 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/wiretest.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/wiretest.c
@@ -27,7 +27,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2011, Intel Corporation.
+ * Copyright (c) 2011, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
diff --git a/drivers/staging/media/bcm2048/radio-bcm2048.c b/drivers/staging/media/bcm2048/radio-bcm2048.c
index b10d6016b993..8fdf0ac4f287 100644
--- a/drivers/staging/media/bcm2048/radio-bcm2048.c
+++ b/drivers/staging/media/bcm2048/radio-bcm2048.c
@@ -179,14 +179,13 @@
#define BCM2048_DEFAULT_TIMEOUT 1500
#define BCM2048_AUTO_SEARCH_TIMEOUT 3000
-
#define BCM2048_FREQDEV_UNIT 10000
#define BCM2048_FREQV4L2_MULTI 625
#define dev_to_v4l2(f) ((f * BCM2048_FREQDEV_UNIT) / BCM2048_FREQV4L2_MULTI)
#define v4l2_to_dev(f) ((f * BCM2048_FREQV4L2_MULTI) / BCM2048_FREQDEV_UNIT)
-#define msb(x) ((u8)((u16) x >> 8))
-#define lsb(x) ((u8)((u16) x & 0x00FF))
+#define msb(x) ((u8)((u16)x >> 8))
+#define lsb(x) ((u8)((u16)x & 0x00FF))
#define compose_u16(msb, lsb) (((u16)msb << 8) | lsb)
#define BCM2048_DEFAULT_POWERING_DELAY 20
@@ -348,7 +347,7 @@ static struct region_info region_configs[] = {
* I2C Interface read / write
*/
static int bcm2048_send_command(struct bcm2048_device *bdev, unsigned int reg,
- unsigned int value)
+ unsigned int value)
{
struct i2c_client *client = bdev->client;
u8 data[2];
@@ -370,7 +369,7 @@ static int bcm2048_send_command(struct bcm2048_device *bdev, unsigned int reg,
}
static int bcm2048_recv_command(struct bcm2048_device *bdev, unsigned int reg,
- u8 *value)
+ u8 *value)
{
struct i2c_client *client = bdev->client;
@@ -385,7 +384,7 @@ static int bcm2048_recv_command(struct bcm2048_device *bdev, unsigned int reg,
}
static int bcm2048_recv_duples(struct bcm2048_device *bdev, unsigned int reg,
- u8 *value, u8 duples)
+ u8 *value, u8 duples)
{
struct i2c_client *client = bdev->client;
struct i2c_adapter *adap = client->adapter;
@@ -436,7 +435,7 @@ static int bcm2048_set_power_state(struct bcm2048_device *bdev, u8 power)
*/
if (power)
err = bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_SYSTEM,
- bdev->cache_fm_rds_system);
+ bdev->cache_fm_rds_system);
msleep(BCM2048_DEFAULT_POWERING_DELAY);
if (!power)
@@ -475,17 +474,17 @@ static int bcm2048_set_rds_no_lock(struct bcm2048_device *bdev, u8 rds_on)
bdev->rds_state = BCM2048_RDS_ON;
flags = BCM2048_RDS_FLAG_FIFO_WLINE;
err = bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_MASK1,
- flags);
+ flags);
} else {
flags = 0;
bdev->rds_state = 0;
err = bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_MASK1,
- flags);
+ flags);
memset(&bdev->rds_info, 0, sizeof(bdev->rds_info));
}
err = bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_SYSTEM,
- bdev->cache_fm_rds_system);
+ bdev->cache_fm_rds_system);
return err;
}
@@ -545,14 +544,14 @@ static int bcm2048_set_fm_automatic_stereo_mono(struct bcm2048_device *bdev,
bdev->cache_fm_ctrl |= BCM2048_STEREO_MONO_AUTO_SELECT;
err = bcm2048_send_command(bdev, BCM2048_I2C_FM_CTRL,
- bdev->cache_fm_ctrl);
+ bdev->cache_fm_ctrl);
mutex_unlock(&bdev->mutex);
return err;
}
static int bcm2048_set_fm_hi_lo_injection(struct bcm2048_device *bdev,
- u8 hi_lo)
+ u8 hi_lo)
{
int err;
@@ -564,7 +563,7 @@ static int bcm2048_set_fm_hi_lo_injection(struct bcm2048_device *bdev,
bdev->cache_fm_ctrl |= BCM2048_HI_LO_INJECTION;
err = bcm2048_send_command(bdev, BCM2048_I2C_FM_CTRL,
- bdev->cache_fm_ctrl);
+ bdev->cache_fm_ctrl);
mutex_unlock(&bdev->mutex);
return err;
@@ -592,7 +591,7 @@ static int bcm2048_set_fm_frequency(struct bcm2048_device *bdev, u32 frequency)
int err;
if (frequency < bdev->region_info.bottom_frequency ||
- frequency > bdev->region_info.top_frequency)
+ frequency > bdev->region_info.top_frequency)
return -EDOM;
frequency -= BCM2048_FREQUENCY_BASE;
@@ -601,7 +600,7 @@ static int bcm2048_set_fm_frequency(struct bcm2048_device *bdev, u32 frequency)
err = bcm2048_send_command(bdev, BCM2048_I2C_FM_FREQ0, lsb(frequency));
err |= bcm2048_send_command(bdev, BCM2048_I2C_FM_FREQ1,
- msb(frequency));
+ msb(frequency));
if (!err)
bdev->frequency = frequency;
@@ -632,12 +631,12 @@ static int bcm2048_get_fm_frequency(struct bcm2048_device *bdev)
}
static int bcm2048_set_fm_af_frequency(struct bcm2048_device *bdev,
- u32 frequency)
+ u32 frequency)
{
int err;
if (frequency < bdev->region_info.bottom_frequency ||
- frequency > bdev->region_info.top_frequency)
+ frequency > bdev->region_info.top_frequency)
return -EDOM;
frequency -= BCM2048_FREQUENCY_BASE;
@@ -645,9 +644,9 @@ static int bcm2048_set_fm_af_frequency(struct bcm2048_device *bdev,
mutex_lock(&bdev->mutex);
err = bcm2048_send_command(bdev, BCM2048_I2C_FM_AF_FREQ0,
- lsb(frequency));
+ lsb(frequency));
err |= bcm2048_send_command(bdev, BCM2048_I2C_FM_AF_FREQ1,
- msb(frequency));
+ msb(frequency));
if (!err)
bdev->frequency = frequency;
@@ -692,7 +691,7 @@ static int bcm2048_set_fm_deemphasis(struct bcm2048_device *bdev, int d)
bdev->cache_fm_audio_ctrl0 |= deemphasis;
err = bcm2048_send_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0,
- bdev->cache_fm_audio_ctrl0);
+ bdev->cache_fm_audio_ctrl0);
if (!err)
bdev->region_info.deemphasis = d;
@@ -740,7 +739,7 @@ static int bcm2048_set_region(struct bcm2048_device *bdev, u8 region)
bdev->cache_fm_ctrl &= ~BCM2048_BAND_SELECT;
err = bcm2048_send_command(bdev, BCM2048_I2C_FM_CTRL,
- bdev->cache_fm_ctrl);
+ bdev->cache_fm_ctrl);
if (err) {
mutex_unlock(&bdev->mutex);
goto done;
@@ -748,7 +747,7 @@ static int bcm2048_set_region(struct bcm2048_device *bdev, u8 region)
mutex_unlock(&bdev->mutex);
if (bdev->frequency < region_configs[region].bottom_frequency ||
- bdev->frequency > region_configs[region].top_frequency)
+ bdev->frequency > region_configs[region].top_frequency)
new_frequency = region_configs[region].bottom_frequency;
if (new_frequency > 0) {
@@ -759,7 +758,7 @@ static int bcm2048_set_region(struct bcm2048_device *bdev, u8 region)
}
err = bcm2048_set_fm_deemphasis(bdev,
- region_configs[region].deemphasis);
+ region_configs[region].deemphasis);
done:
return err;
@@ -786,10 +785,10 @@ static int bcm2048_set_mute(struct bcm2048_device *bdev, u16 mute)
if (mute)
bdev->cache_fm_audio_ctrl0 |= (BCM2048_RF_MUTE |
- BCM2048_MANUAL_MUTE);
+ BCM2048_MANUAL_MUTE);
err = bcm2048_send_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0,
- bdev->cache_fm_audio_ctrl0);
+ bdev->cache_fm_audio_ctrl0);
if (!err)
bdev->mute_state = mute;
@@ -807,7 +806,7 @@ static int bcm2048_get_mute(struct bcm2048_device *bdev)
if (bdev->power_state) {
err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0,
- &value);
+ &value);
if (!err)
err = value & (BCM2048_RF_MUTE | BCM2048_MANUAL_MUTE);
} else {
@@ -826,11 +825,11 @@ static int bcm2048_set_audio_route(struct bcm2048_device *bdev, u8 route)
route &= (BCM2048_AUDIO_ROUTE_DAC | BCM2048_AUDIO_ROUTE_I2S);
bdev->cache_fm_audio_ctrl0 &= ~(BCM2048_AUDIO_ROUTE_DAC |
- BCM2048_AUDIO_ROUTE_I2S);
+ BCM2048_AUDIO_ROUTE_I2S);
bdev->cache_fm_audio_ctrl0 |= route;
err = bcm2048_send_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0,
- bdev->cache_fm_audio_ctrl0);
+ bdev->cache_fm_audio_ctrl0);
mutex_unlock(&bdev->mutex);
return err;
@@ -849,7 +848,7 @@ static int bcm2048_get_audio_route(struct bcm2048_device *bdev)
if (!err)
return value & (BCM2048_AUDIO_ROUTE_DAC |
- BCM2048_AUDIO_ROUTE_I2S);
+ BCM2048_AUDIO_ROUTE_I2S);
return err;
}
@@ -865,7 +864,7 @@ static int bcm2048_set_dac_output(struct bcm2048_device *bdev, u8 channels)
bdev->cache_fm_audio_ctrl0 |= channels;
err = bcm2048_send_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0,
- bdev->cache_fm_audio_ctrl0);
+ bdev->cache_fm_audio_ctrl0);
mutex_unlock(&bdev->mutex);
return err;
@@ -884,13 +883,13 @@ static int bcm2048_get_dac_output(struct bcm2048_device *bdev)
if (!err)
return value & (BCM2048_DAC_OUTPUT_LEFT |
- BCM2048_DAC_OUTPUT_RIGHT);
+ BCM2048_DAC_OUTPUT_RIGHT);
return err;
}
static int bcm2048_set_fm_search_rssi_threshold(struct bcm2048_device *bdev,
- u8 threshold)
+ u8 threshold)
{
int err;
@@ -901,7 +900,7 @@ static int bcm2048_set_fm_search_rssi_threshold(struct bcm2048_device *bdev,
bdev->cache_fm_search_ctrl0 |= threshold;
err = bcm2048_send_command(bdev, BCM2048_I2C_FM_SEARCH_CTRL0,
- bdev->cache_fm_search_ctrl0);
+ bdev->cache_fm_search_ctrl0);
mutex_unlock(&bdev->mutex);
return err;
@@ -937,7 +936,7 @@ static int bcm2048_set_fm_search_mode_direction(struct bcm2048_device *bdev,
bdev->cache_fm_search_ctrl0 |= BCM2048_SEARCH_DIRECTION;
err = bcm2048_send_command(bdev, BCM2048_I2C_FM_SEARCH_CTRL0,
- bdev->cache_fm_search_ctrl0);
+ bdev->cache_fm_search_ctrl0);
mutex_unlock(&bdev->mutex);
return err;
@@ -961,7 +960,7 @@ static int bcm2048_get_fm_search_mode_direction(struct bcm2048_device *bdev)
}
static int bcm2048_set_fm_search_tune_mode(struct bcm2048_device *bdev,
- u8 mode)
+ u8 mode)
{
int err, timeout, restart_rds = 0;
u8 value, flags;
@@ -1000,8 +999,8 @@ static int bcm2048_set_fm_search_tune_mode(struct bcm2048_device *bdev,
timeout = BCM2048_AUTO_SEARCH_TIMEOUT;
if (!wait_for_completion_timeout(&bdev->compl,
- msecs_to_jiffies(timeout)))
- dev_err(&bdev->client->dev, "IRQ timeout.\n");
+ msecs_to_jiffies(timeout)))
+ dev_err(&bdev->client->dev, "IRQ timeout.\n");
if (value)
if (!bdev->scan_state)
@@ -1024,7 +1023,7 @@ static int bcm2048_get_fm_search_tune_mode(struct bcm2048_device *bdev)
mutex_lock(&bdev->mutex);
err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_SEARCH_TUNE_MODE,
- &value);
+ &value);
mutex_unlock(&bdev->mutex);
@@ -1040,10 +1039,10 @@ static int bcm2048_set_rds_b_block_mask(struct bcm2048_device *bdev, u16 mask)
mutex_lock(&bdev->mutex);
- err = bcm2048_send_command(bdev,
- BCM2048_I2C_RDS_BLKB_MASK0, lsb(mask));
- err |= bcm2048_send_command(bdev,
- BCM2048_I2C_RDS_BLKB_MASK1, msb(mask));
+ err = bcm2048_send_command(bdev, BCM2048_I2C_RDS_BLKB_MASK0,
+ lsb(mask));
+ err |= bcm2048_send_command(bdev, BCM2048_I2C_RDS_BLKB_MASK1,
+ msb(mask));
mutex_unlock(&bdev->mutex);
return err;
@@ -1056,10 +1055,8 @@ static int bcm2048_get_rds_b_block_mask(struct bcm2048_device *bdev)
mutex_lock(&bdev->mutex);
- err = bcm2048_recv_command(bdev,
- BCM2048_I2C_RDS_BLKB_MASK0, &lsb);
- err |= bcm2048_recv_command(bdev,
- BCM2048_I2C_RDS_BLKB_MASK1, &msb);
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_RDS_BLKB_MASK0, &lsb);
+ err |= bcm2048_recv_command(bdev, BCM2048_I2C_RDS_BLKB_MASK1, &msb);
mutex_unlock(&bdev->mutex);
@@ -1070,16 +1067,16 @@ static int bcm2048_get_rds_b_block_mask(struct bcm2048_device *bdev)
}
static int bcm2048_set_rds_b_block_match(struct bcm2048_device *bdev,
- u16 match)
+ u16 match)
{
int err;
mutex_lock(&bdev->mutex);
- err = bcm2048_send_command(bdev,
- BCM2048_I2C_RDS_BLKB_MATCH0, lsb(match));
- err |= bcm2048_send_command(bdev,
- BCM2048_I2C_RDS_BLKB_MATCH1, msb(match));
+ err = bcm2048_send_command(bdev, BCM2048_I2C_RDS_BLKB_MATCH0,
+ lsb(match));
+ err |= bcm2048_send_command(bdev, BCM2048_I2C_RDS_BLKB_MATCH1,
+ msb(match));
mutex_unlock(&bdev->mutex);
return err;
@@ -1092,10 +1089,8 @@ static int bcm2048_get_rds_b_block_match(struct bcm2048_device *bdev)
mutex_lock(&bdev->mutex);
- err = bcm2048_recv_command(bdev,
- BCM2048_I2C_RDS_BLKB_MATCH0, &lsb);
- err |= bcm2048_recv_command(bdev,
- BCM2048_I2C_RDS_BLKB_MATCH1, &msb);
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_RDS_BLKB_MATCH0, &lsb);
+ err |= bcm2048_recv_command(bdev, BCM2048_I2C_RDS_BLKB_MATCH1, &msb);
mutex_unlock(&bdev->mutex);
@@ -1111,10 +1106,8 @@ static int bcm2048_set_rds_pi_mask(struct bcm2048_device *bdev, u16 mask)
mutex_lock(&bdev->mutex);
- err = bcm2048_send_command(bdev,
- BCM2048_I2C_RDS_PI_MASK0, lsb(mask));
- err |= bcm2048_send_command(bdev,
- BCM2048_I2C_RDS_PI_MASK1, msb(mask));
+ err = bcm2048_send_command(bdev, BCM2048_I2C_RDS_PI_MASK0, lsb(mask));
+ err |= bcm2048_send_command(bdev, BCM2048_I2C_RDS_PI_MASK1, msb(mask));
mutex_unlock(&bdev->mutex);
return err;
@@ -1127,10 +1120,8 @@ static int bcm2048_get_rds_pi_mask(struct bcm2048_device *bdev)
mutex_lock(&bdev->mutex);
- err = bcm2048_recv_command(bdev,
- BCM2048_I2C_RDS_PI_MASK0, &lsb);
- err |= bcm2048_recv_command(bdev,
- BCM2048_I2C_RDS_PI_MASK1, &msb);
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_RDS_PI_MASK0, &lsb);
+ err |= bcm2048_recv_command(bdev, BCM2048_I2C_RDS_PI_MASK1, &msb);
mutex_unlock(&bdev->mutex);
@@ -1146,10 +1137,10 @@ static int bcm2048_set_rds_pi_match(struct bcm2048_device *bdev, u16 match)
mutex_lock(&bdev->mutex);
- err = bcm2048_send_command(bdev,
- BCM2048_I2C_RDS_PI_MATCH0, lsb(match));
- err |= bcm2048_send_command(bdev,
- BCM2048_I2C_RDS_PI_MATCH1, msb(match));
+ err = bcm2048_send_command(bdev, BCM2048_I2C_RDS_PI_MATCH0,
+ lsb(match));
+ err |= bcm2048_send_command(bdev, BCM2048_I2C_RDS_PI_MATCH1,
+ msb(match));
mutex_unlock(&bdev->mutex);
return err;
@@ -1162,10 +1153,8 @@ static int bcm2048_get_rds_pi_match(struct bcm2048_device *bdev)
mutex_lock(&bdev->mutex);
- err = bcm2048_recv_command(bdev,
- BCM2048_I2C_RDS_PI_MATCH0, &lsb);
- err |= bcm2048_recv_command(bdev,
- BCM2048_I2C_RDS_PI_MATCH1, &msb);
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_RDS_PI_MATCH0, &lsb);
+ err |= bcm2048_recv_command(bdev, BCM2048_I2C_RDS_PI_MATCH1, &msb);
mutex_unlock(&bdev->mutex);
@@ -1181,10 +1170,8 @@ static int bcm2048_set_fm_rds_mask(struct bcm2048_device *bdev, u16 mask)
mutex_lock(&bdev->mutex);
- err = bcm2048_send_command(bdev,
- BCM2048_I2C_FM_RDS_MASK0, lsb(mask));
- err |= bcm2048_send_command(bdev,
- BCM2048_I2C_FM_RDS_MASK1, msb(mask));
+ err = bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_MASK0, lsb(mask));
+ err |= bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_MASK1, msb(mask));
mutex_unlock(&bdev->mutex);
return err;
@@ -1245,13 +1232,13 @@ static int bcm2048_set_fm_best_tune_mode(struct bcm2048_device *bdev, u8 mode)
/* Perform read as the manual indicates */
err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_BEST_TUNE_MODE,
- &value);
+ &value);
value &= ~BCM2048_BEST_TUNE_MODE;
if (mode)
value |= BCM2048_BEST_TUNE_MODE;
err |= bcm2048_send_command(bdev, BCM2048_I2C_FM_BEST_TUNE_MODE,
- value);
+ value);
mutex_unlock(&bdev->mutex);
return err;
@@ -1265,7 +1252,7 @@ static int bcm2048_get_fm_best_tune_mode(struct bcm2048_device *bdev)
mutex_lock(&bdev->mutex);
err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_BEST_TUNE_MODE,
- &value);
+ &value);
mutex_unlock(&bdev->mutex);
@@ -1352,7 +1339,7 @@ static int bcm2048_checkrev(struct bcm2048_device *bdev)
if (!err) {
dev_info(&bdev->client->dev, "BCM2048 Version 0x%x\n",
- version);
+ version);
return version;
}
@@ -1362,7 +1349,7 @@ static int bcm2048_checkrev(struct bcm2048_device *bdev)
static int bcm2048_get_rds_rt(struct bcm2048_device *bdev, char *data)
{
int err = 0, i, j = 0, ce = 0, cr = 0;
- char data_buffer[BCM2048_MAX_RDS_RT+1];
+ char data_buffer[BCM2048_MAX_RDS_RT + 1];
mutex_lock(&bdev->mutex);
@@ -1412,7 +1399,7 @@ unlock:
static int bcm2048_get_rds_ps(struct bcm2048_device *bdev, char *data)
{
int err = 0, i, j = 0;
- char data_buffer[BCM2048_MAX_RDS_PS+1];
+ char data_buffer[BCM2048_MAX_RDS_PS + 1];
mutex_lock(&bdev->mutex);
@@ -1448,12 +1435,10 @@ static void bcm2048_parse_rds_pi(struct bcm2048_device *bdev)
u16 pi;
for (i = 0; i < bdev->fifo_size; i += BCM2048_RDS_FIFO_DUPLE_SIZE) {
-
/* Block A match, only data without crc errors taken */
if (bdev->rds_info.radio_text[i] == BCM2048_RDS_BLOCK_A) {
-
- pi = (bdev->rds_info.radio_text[i+1] << 8) +
- bdev->rds_info.radio_text[i+2];
+ pi = (bdev->rds_info.radio_text[i + 1] << 8) +
+ bdev->rds_info.radio_text[i + 2];
if (!bdev->rds_info.rds_pi) {
bdev->rds_info.rds_pi = pi;
@@ -1478,20 +1463,21 @@ static int bcm2048_rds_block_crc(struct bcm2048_device *bdev, int i)
}
static void bcm2048_parse_rds_rt_block(struct bcm2048_device *bdev, int i,
- int index, int crc)
+ int index, int crc)
{
/* Good data will overwrite poor data */
if (crc) {
if (!bdev->rds_info.rds_rt[index])
bdev->rds_info.rds_rt[index] =
- bdev->rds_info.radio_text[i+1];
- if (!bdev->rds_info.rds_rt[index+1])
- bdev->rds_info.rds_rt[index+1] =
- bdev->rds_info.radio_text[i+2];
+ bdev->rds_info.radio_text[i + 1];
+ if (!bdev->rds_info.rds_rt[index + 1])
+ bdev->rds_info.rds_rt[index + 1] =
+ bdev->rds_info.radio_text[i + 2];
} else {
- bdev->rds_info.rds_rt[index] = bdev->rds_info.radio_text[i+1];
- bdev->rds_info.rds_rt[index+1] =
- bdev->rds_info.radio_text[i+2];
+ bdev->rds_info.rds_rt[index] =
+ bdev->rds_info.radio_text[i + 1];
+ bdev->rds_info.rds_rt[index + 1] =
+ bdev->rds_info.radio_text[i + 2];
}
}
@@ -1505,18 +1491,17 @@ static int bcm2048_parse_rt_match_b(struct bcm2048_device *bdev, int i)
return -EIO;
if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) ==
- BCM2048_RDS_BLOCK_B) {
-
- rt_id = bdev->rds_info.radio_text[i+1] &
+ BCM2048_RDS_BLOCK_B) {
+ rt_id = bdev->rds_info.radio_text[i + 1] &
BCM2048_RDS_BLOCK_MASK;
- rt_group_b = bdev->rds_info.radio_text[i+1] &
+ rt_group_b = bdev->rds_info.radio_text[i + 1] &
BCM2048_RDS_GROUP_AB_MASK;
- rt_ab = bdev->rds_info.radio_text[i+2] &
+ rt_ab = bdev->rds_info.radio_text[i + 2] &
BCM2048_RDS_RT_AB_MASK;
if (rt_group_b != bdev->rds_info.rds_rt_group_b) {
memset(bdev->rds_info.rds_rt, 0,
- sizeof(bdev->rds_info.rds_rt));
+ sizeof(bdev->rds_info.rds_rt));
bdev->rds_info.rds_rt_group_b = rt_group_b;
}
@@ -1524,11 +1509,11 @@ static int bcm2048_parse_rt_match_b(struct bcm2048_device *bdev, int i)
/* A to B or (vice versa), means: clear screen */
if (rt_ab != bdev->rds_info.rds_rt_ab) {
memset(bdev->rds_info.rds_rt, 0,
- sizeof(bdev->rds_info.rds_rt));
+ sizeof(bdev->rds_info.rds_rt));
bdev->rds_info.rds_rt_ab = rt_ab;
}
- index = bdev->rds_info.radio_text[i+2] &
+ index = bdev->rds_info.radio_text[i + 2] &
BCM2048_RDS_RT_INDEX;
if (bdev->rds_info.rds_rt_group_b)
@@ -1544,7 +1529,7 @@ static int bcm2048_parse_rt_match_b(struct bcm2048_device *bdev, int i)
}
static int bcm2048_parse_rt_match_c(struct bcm2048_device *bdev, int i,
- int index)
+ int index)
{
int crc;
@@ -1567,7 +1552,7 @@ static int bcm2048_parse_rt_match_c(struct bcm2048_device *bdev, int i,
}
static void bcm2048_parse_rt_match_d(struct bcm2048_device *bdev, int i,
- int index)
+ int index)
{
int crc;
@@ -1579,8 +1564,8 @@ static void bcm2048_parse_rt_match_d(struct bcm2048_device *bdev, int i,
BUG_ON((index+4) >= BCM2048_MAX_RDS_RT);
if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) ==
- BCM2048_RDS_BLOCK_D)
- bcm2048_parse_rds_rt_block(bdev, i, index+2, crc);
+ BCM2048_RDS_BLOCK_D)
+ bcm2048_parse_rds_rt_block(bdev, i, index + 2, crc);
}
static void bcm2048_parse_rds_rt(struct bcm2048_device *bdev)
@@ -1588,7 +1573,6 @@ static void bcm2048_parse_rds_rt(struct bcm2048_device *bdev)
int i, index = 0, crc, match_b = 0, match_c = 0, match_d = 0;
for (i = 0; i < bdev->fifo_size; i += BCM2048_RDS_FIFO_DUPLE_SIZE) {
-
if (match_b) {
match_b = 0;
index = bcm2048_parse_rt_match_b(bdev, i);
@@ -1607,40 +1591,41 @@ static void bcm2048_parse_rds_rt(struct bcm2048_device *bdev)
}
/* Skip erroneous blocks due to messed up A block altogether */
- if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK)
- == BCM2048_RDS_BLOCK_A) {
+ if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) ==
+ BCM2048_RDS_BLOCK_A) {
crc = bcm2048_rds_block_crc(bdev, i);
if (crc == BCM2048_RDS_CRC_UNRECOVARABLE)
continue;
- /* Syncronize to a good RDS PI */
- if (((bdev->rds_info.radio_text[i+1] << 8) +
- bdev->rds_info.radio_text[i+2]) ==
- bdev->rds_info.rds_pi)
- match_b = 1;
+ /* Synchronize to a good RDS PI */
+ if (((bdev->rds_info.radio_text[i + 1] << 8) +
+ bdev->rds_info.radio_text[i + 2]) ==
+ bdev->rds_info.rds_pi)
+ match_b = 1;
}
}
}
static void bcm2048_parse_rds_ps_block(struct bcm2048_device *bdev, int i,
- int index, int crc)
+ int index, int crc)
{
/* Good data will overwrite poor data */
if (crc) {
if (!bdev->rds_info.rds_ps[index])
bdev->rds_info.rds_ps[index] =
- bdev->rds_info.radio_text[i+1];
- if (!bdev->rds_info.rds_ps[index+1])
- bdev->rds_info.rds_ps[index+1] =
- bdev->rds_info.radio_text[i+2];
+ bdev->rds_info.radio_text[i + 1];
+ if (!bdev->rds_info.rds_ps[index + 1])
+ bdev->rds_info.rds_ps[index + 1] =
+ bdev->rds_info.radio_text[i + 2];
} else {
- bdev->rds_info.rds_ps[index] = bdev->rds_info.radio_text[i+1];
- bdev->rds_info.rds_ps[index+1] =
- bdev->rds_info.radio_text[i+2];
+ bdev->rds_info.rds_ps[index] =
+ bdev->rds_info.radio_text[i + 1];
+ bdev->rds_info.rds_ps[index + 1] =
+ bdev->rds_info.radio_text[i + 2];
}
}
static int bcm2048_parse_ps_match_c(struct bcm2048_device *bdev, int i,
- int index)
+ int index)
{
int crc;
@@ -1650,14 +1635,14 @@ static int bcm2048_parse_ps_match_c(struct bcm2048_device *bdev, int i,
return 0;
if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) ==
- BCM2048_RDS_BLOCK_C)
+ BCM2048_RDS_BLOCK_C)
return 1;
return 0;
}
static void bcm2048_parse_ps_match_d(struct bcm2048_device *bdev, int i,
- int index)
+ int index)
{
int crc;
@@ -1667,7 +1652,7 @@ static void bcm2048_parse_ps_match_d(struct bcm2048_device *bdev, int i,
return;
if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) ==
- BCM2048_RDS_BLOCK_D)
+ BCM2048_RDS_BLOCK_D)
bcm2048_parse_rds_ps_block(bdev, i, index, crc);
}
@@ -1682,10 +1667,10 @@ static int bcm2048_parse_ps_match_b(struct bcm2048_device *bdev, int i)
/* Block B Radio PS match */
if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) ==
- BCM2048_RDS_BLOCK_B) {
- ps_id = bdev->rds_info.radio_text[i+1] &
+ BCM2048_RDS_BLOCK_B) {
+ ps_id = bdev->rds_info.radio_text[i + 1] &
BCM2048_RDS_BLOCK_MASK;
- ps_group = bdev->rds_info.radio_text[i+1] &
+ ps_group = bdev->rds_info.radio_text[i + 1] &
BCM2048_RDS_GROUP_AB_MASK;
/*
@@ -1709,7 +1694,7 @@ static int bcm2048_parse_ps_match_b(struct bcm2048_device *bdev, int i)
}
if (ps_id == BCM2048_RDS_PS) {
- index = bdev->rds_info.radio_text[i+2] &
+ index = bdev->rds_info.radio_text[i + 2] &
BCM2048_RDS_PS_INDEX;
index <<= 1;
return index;
@@ -1724,7 +1709,6 @@ static void bcm2048_parse_rds_ps(struct bcm2048_device *bdev)
int i, index = 0, crc, match_b = 0, match_c = 0, match_d = 0;
for (i = 0; i < bdev->fifo_size; i += BCM2048_RDS_FIFO_DUPLE_SIZE) {
-
if (match_b) {
match_b = 0;
index = bcm2048_parse_ps_match_b(bdev, i);
@@ -1743,16 +1727,16 @@ static void bcm2048_parse_rds_ps(struct bcm2048_device *bdev)
}
/* Skip erroneous blocks due to messed up A block altogether */
- if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK)
- == BCM2048_RDS_BLOCK_A) {
+ if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) ==
+ BCM2048_RDS_BLOCK_A) {
crc = bcm2048_rds_block_crc(bdev, i);
if (crc == BCM2048_RDS_CRC_UNRECOVARABLE)
continue;
- /* Syncronize to a good RDS PI */
- if (((bdev->rds_info.radio_text[i+1] << 8) +
- bdev->rds_info.radio_text[i+2]) ==
- bdev->rds_info.rds_pi)
- match_b = 1;
+ /* Synchronize to a good RDS PI */
+ if (((bdev->rds_info.radio_text[i + 1] << 8) +
+ bdev->rds_info.radio_text[i + 2]) ==
+ bdev->rds_info.rds_pi)
+ match_b = 1;
}
}
}
@@ -1764,7 +1748,7 @@ static void bcm2048_rds_fifo_receive(struct bcm2048_device *bdev)
mutex_lock(&bdev->mutex);
err = bcm2048_recv_duples(bdev, BCM2048_I2C_RDS_DATA,
- bdev->rds_info.radio_text, bdev->fifo_size);
+ bdev->rds_info.radio_text, bdev->fifo_size);
if (err != 2) {
dev_err(&bdev->client->dev, "RDS Read problem\n");
mutex_unlock(&bdev->mutex);
@@ -1801,8 +1785,8 @@ static int bcm2048_get_rds_data(struct bcm2048_device *bdev, char *data)
}
for (i = 0; i < bdev->rds_info.text_len; i++) {
- p += sprintf(data_buffer+p, "%x ",
- bdev->rds_info.radio_text[i]);
+ p += sprintf(data_buffer + p, "%x ",
+ bdev->rds_info.radio_text[i]);
}
memcpy(data, data_buffer, p);
@@ -1829,7 +1813,7 @@ static int bcm2048_init(struct bcm2048_device *bdev)
goto exit;
err = bcm2048_set_dac_output(bdev, BCM2048_DAC_OUTPUT_LEFT |
- BCM2048_DAC_OUTPUT_RIGHT);
+ BCM2048_DAC_OUTPUT_RIGHT);
exit:
return err;
@@ -1921,7 +1905,6 @@ static void bcm2048_work(struct work_struct *work)
if (flag_lsb & (BCM2048_FM_FLAG_SEARCH_TUNE_FINISHED |
BCM2048_FM_FLAG_SEARCH_TUNE_FAIL)) {
-
if (flag_lsb & BCM2048_FM_FLAG_SEARCH_TUNE_FAIL)
bdev->scan_state = BCM2048_SCAN_FAIL;
else
@@ -1935,7 +1918,7 @@ static void bcm2048_work(struct work_struct *work)
if (bdev->rds_state) {
flags = BCM2048_RDS_FLAG_FIFO_WLINE;
bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_MASK1,
- flags);
+ flags);
}
bdev->rds_data_available = 1;
bdev->rd_index = 0; /* new data, new start */
@@ -2074,7 +2057,7 @@ property_str_read(rds_rt, (BCM2048_MAX_RDS_RT + 1))
property_str_read(rds_ps, (BCM2048_MAX_RDS_PS + 1))
property_read(fm_rds_flags, unsigned int, "%u")
-property_str_read(rds_data, BCM2048_MAX_RDS_RADIO_TEXT*5)
+property_str_read(rds_data, BCM2048_MAX_RDS_RADIO_TEXT * 5)
property_read(region_bottom_frequency, unsigned int, "%u")
property_read(region_top_frequency, unsigned int, "%u")
@@ -2084,70 +2067,70 @@ DEFINE_SYSFS_PROPERTY(region, unsigned, int, "%u", 0)
static struct device_attribute attrs[] = {
__ATTR(power_state, S_IRUGO | S_IWUSR, bcm2048_power_state_read,
- bcm2048_power_state_write),
+ bcm2048_power_state_write),
__ATTR(mute, S_IRUGO | S_IWUSR, bcm2048_mute_read,
- bcm2048_mute_write),
+ bcm2048_mute_write),
__ATTR(audio_route, S_IRUGO | S_IWUSR, bcm2048_audio_route_read,
- bcm2048_audio_route_write),
+ bcm2048_audio_route_write),
__ATTR(dac_output, S_IRUGO | S_IWUSR, bcm2048_dac_output_read,
- bcm2048_dac_output_write),
+ bcm2048_dac_output_write),
__ATTR(fm_hi_lo_injection, S_IRUGO | S_IWUSR,
- bcm2048_fm_hi_lo_injection_read,
- bcm2048_fm_hi_lo_injection_write),
+ bcm2048_fm_hi_lo_injection_read,
+ bcm2048_fm_hi_lo_injection_write),
__ATTR(fm_frequency, S_IRUGO | S_IWUSR, bcm2048_fm_frequency_read,
- bcm2048_fm_frequency_write),
+ bcm2048_fm_frequency_write),
__ATTR(fm_af_frequency, S_IRUGO | S_IWUSR,
- bcm2048_fm_af_frequency_read,
- bcm2048_fm_af_frequency_write),
+ bcm2048_fm_af_frequency_read,
+ bcm2048_fm_af_frequency_write),
__ATTR(fm_deemphasis, S_IRUGO | S_IWUSR, bcm2048_fm_deemphasis_read,
- bcm2048_fm_deemphasis_write),
+ bcm2048_fm_deemphasis_write),
__ATTR(fm_rds_mask, S_IRUGO | S_IWUSR, bcm2048_fm_rds_mask_read,
- bcm2048_fm_rds_mask_write),
+ bcm2048_fm_rds_mask_write),
__ATTR(fm_best_tune_mode, S_IRUGO | S_IWUSR,
- bcm2048_fm_best_tune_mode_read,
- bcm2048_fm_best_tune_mode_write),
+ bcm2048_fm_best_tune_mode_read,
+ bcm2048_fm_best_tune_mode_write),
__ATTR(fm_search_rssi_threshold, S_IRUGO | S_IWUSR,
- bcm2048_fm_search_rssi_threshold_read,
- bcm2048_fm_search_rssi_threshold_write),
+ bcm2048_fm_search_rssi_threshold_read,
+ bcm2048_fm_search_rssi_threshold_write),
__ATTR(fm_search_mode_direction, S_IRUGO | S_IWUSR,
- bcm2048_fm_search_mode_direction_read,
- bcm2048_fm_search_mode_direction_write),
+ bcm2048_fm_search_mode_direction_read,
+ bcm2048_fm_search_mode_direction_write),
__ATTR(fm_search_tune_mode, S_IRUGO | S_IWUSR,
- bcm2048_fm_search_tune_mode_read,
- bcm2048_fm_search_tune_mode_write),
+ bcm2048_fm_search_tune_mode_read,
+ bcm2048_fm_search_tune_mode_write),
__ATTR(rds, S_IRUGO | S_IWUSR, bcm2048_rds_read,
- bcm2048_rds_write),
+ bcm2048_rds_write),
__ATTR(rds_b_block_mask, S_IRUGO | S_IWUSR,
- bcm2048_rds_b_block_mask_read,
- bcm2048_rds_b_block_mask_write),
+ bcm2048_rds_b_block_mask_read,
+ bcm2048_rds_b_block_mask_write),
__ATTR(rds_b_block_match, S_IRUGO | S_IWUSR,
- bcm2048_rds_b_block_match_read,
- bcm2048_rds_b_block_match_write),
+ bcm2048_rds_b_block_match_read,
+ bcm2048_rds_b_block_match_write),
__ATTR(rds_pi_mask, S_IRUGO | S_IWUSR, bcm2048_rds_pi_mask_read,
- bcm2048_rds_pi_mask_write),
+ bcm2048_rds_pi_mask_write),
__ATTR(rds_pi_match, S_IRUGO | S_IWUSR, bcm2048_rds_pi_match_read,
- bcm2048_rds_pi_match_write),
+ bcm2048_rds_pi_match_write),
__ATTR(rds_wline, S_IRUGO | S_IWUSR, bcm2048_rds_wline_read,
- bcm2048_rds_wline_write),
+ bcm2048_rds_wline_write),
__ATTR(rds_pi, S_IRUGO, bcm2048_rds_pi_read, NULL),
__ATTR(rds_rt, S_IRUGO, bcm2048_rds_rt_read, NULL),
__ATTR(rds_ps, S_IRUGO, bcm2048_rds_ps_read, NULL),
__ATTR(fm_rds_flags, S_IRUGO, bcm2048_fm_rds_flags_read, NULL),
__ATTR(region_bottom_frequency, S_IRUGO,
- bcm2048_region_bottom_frequency_read, NULL),
+ bcm2048_region_bottom_frequency_read, NULL),
__ATTR(region_top_frequency, S_IRUGO,
- bcm2048_region_top_frequency_read, NULL),
+ bcm2048_region_top_frequency_read, NULL),
__ATTR(fm_carrier_error, S_IRUGO,
- bcm2048_fm_carrier_error_read, NULL),
+ bcm2048_fm_carrier_error_read, NULL),
__ATTR(fm_rssi, S_IRUGO,
- bcm2048_fm_rssi_read, NULL),
+ bcm2048_fm_rssi_read, NULL),
__ATTR(region, S_IRUGO | S_IWUSR, bcm2048_region_read,
- bcm2048_region_write),
+ bcm2048_region_write),
__ATTR(rds_data, S_IRUGO, bcm2048_rds_data_read, NULL),
};
static int bcm2048_sysfs_unregister_properties(struct bcm2048_device *bdev,
- int size)
+ int size)
{
int i;
@@ -2165,7 +2148,7 @@ static int bcm2048_sysfs_register_properties(struct bcm2048_device *bdev)
for (i = 0; i < ARRAY_SIZE(attrs); i++) {
if (device_create_file(&bdev->client->dev, &attrs[i]) != 0) {
dev_err(&bdev->client->dev,
- "could not register sysfs entry\n");
+ "could not register sysfs entry\n");
err = -EBUSY;
bcm2048_sysfs_unregister_properties(bdev, i);
break;
@@ -2175,7 +2158,6 @@ static int bcm2048_sysfs_register_properties(struct bcm2048_device *bdev)
return err;
}
-
static int bcm2048_fops_open(struct file *file)
{
struct bcm2048_device *bdev = video_drvdata(file);
@@ -2197,7 +2179,7 @@ static int bcm2048_fops_release(struct file *file)
}
static unsigned int bcm2048_fops_poll(struct file *file,
- struct poll_table_struct *pts)
+ struct poll_table_struct *pts)
{
struct bcm2048_device *bdev = video_drvdata(file);
int retval = 0;
@@ -2211,7 +2193,7 @@ static unsigned int bcm2048_fops_poll(struct file *file,
}
static ssize_t bcm2048_fops_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
+ size_t count, loff_t *ppos)
{
struct bcm2048_device *bdev = video_drvdata(file);
int i;
@@ -2229,7 +2211,7 @@ static ssize_t bcm2048_fops_read(struct file *file, char __user *buf,
}
/* interruptible_sleep_on(&bdev->read_queue); */
if (wait_event_interruptible(bdev->read_queue,
- bdev->rds_data_available) < 0) {
+ bdev->rds_data_available) < 0) {
retval = -EINTR;
goto done;
}
@@ -2245,13 +2227,16 @@ static ssize_t bcm2048_fops_read(struct file *file, char __user *buf,
while (i < count) {
unsigned char tmpbuf[3];
- tmpbuf[i] = bdev->rds_info.radio_text[bdev->rd_index+i+2];
- tmpbuf[i+1] = bdev->rds_info.radio_text[bdev->rd_index+i+1];
- tmpbuf[i+2] = (bdev->rds_info.radio_text[bdev->rd_index + i] & 0xf0) >> 4;
- if ((bdev->rds_info.radio_text[bdev->rd_index+i] &
- BCM2048_RDS_CRC_MASK) == BCM2048_RDS_CRC_UNRECOVARABLE)
- tmpbuf[i+2] |= 0x80;
- if (copy_to_user(buf+i, tmpbuf, 3)) {
+ tmpbuf[i] = bdev->rds_info.radio_text[bdev->rd_index + i + 2];
+ tmpbuf[i + 1] =
+ bdev->rds_info.radio_text[bdev->rd_index + i + 1];
+ tmpbuf[i + 2] =
+ (bdev->rds_info.radio_text[bdev->rd_index + i] &
+ 0xf0) >> 4;
+ if ((bdev->rds_info.radio_text[bdev->rd_index + i] &
+ BCM2048_RDS_CRC_MASK) == BCM2048_RDS_CRC_UNRECOVARABLE)
+ tmpbuf[i + 2] |= 0x80;
+ if (copy_to_user(buf + i, tmpbuf, 3)) {
retval = -EFAULT;
break;
}
@@ -2319,7 +2304,7 @@ static struct v4l2_queryctrl bcm2048_v4l2_queryctrl[] = {
};
static int bcm2048_vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *capability)
+ struct v4l2_capability *capability)
{
struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file));
@@ -2337,7 +2322,7 @@ static int bcm2048_vidioc_querycap(struct file *file, void *priv,
}
static int bcm2048_vidioc_g_input(struct file *filp, void *priv,
- unsigned int *i)
+ unsigned int *i)
{
*i = 0;
@@ -2345,7 +2330,7 @@ static int bcm2048_vidioc_g_input(struct file *filp, void *priv,
}
static int bcm2048_vidioc_s_input(struct file *filp, void *priv,
- unsigned int i)
+ unsigned int i)
{
if (i)
return -EINVAL;
@@ -2354,7 +2339,7 @@ static int bcm2048_vidioc_s_input(struct file *filp, void *priv,
}
static int bcm2048_vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
+ struct v4l2_queryctrl *qc)
{
int i;
@@ -2369,7 +2354,7 @@ static int bcm2048_vidioc_queryctrl(struct file *file, void *priv,
}
static int bcm2048_vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
+ struct v4l2_control *ctrl)
{
struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file));
int err = 0;
@@ -2389,7 +2374,7 @@ static int bcm2048_vidioc_g_ctrl(struct file *file, void *priv,
}
static int bcm2048_vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
+ struct v4l2_control *ctrl)
{
struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file));
int err = 0;
@@ -2417,7 +2402,7 @@ static int bcm2048_vidioc_s_ctrl(struct file *file, void *priv,
}
static int bcm2048_vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *audio)
+ struct v4l2_audio *audio)
{
if (audio->index > 1)
return -EINVAL;
@@ -2429,7 +2414,7 @@ static int bcm2048_vidioc_g_audio(struct file *file, void *priv,
}
static int bcm2048_vidioc_s_audio(struct file *file, void *priv,
- const struct v4l2_audio *audio)
+ const struct v4l2_audio *audio)
{
if (audio->index != 0)
return -EINVAL;
@@ -2438,7 +2423,7 @@ static int bcm2048_vidioc_s_audio(struct file *file, void *priv,
}
static int bcm2048_vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *tuner)
+ struct v4l2_tuner *tuner)
{
struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file));
s8 f_error;
@@ -2493,7 +2478,7 @@ static int bcm2048_vidioc_g_tuner(struct file *file, void *priv,
}
static int bcm2048_vidioc_s_tuner(struct file *file, void *priv,
- const struct v4l2_tuner *tuner)
+ const struct v4l2_tuner *tuner)
{
struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file));
@@ -2507,7 +2492,7 @@ static int bcm2048_vidioc_s_tuner(struct file *file, void *priv,
}
static int bcm2048_vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *freq)
+ struct v4l2_frequency *freq)
{
struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file));
int err = 0;
@@ -2528,7 +2513,7 @@ static int bcm2048_vidioc_g_frequency(struct file *file, void *priv,
}
static int bcm2048_vidioc_s_frequency(struct file *file, void *priv,
- const struct v4l2_frequency *freq)
+ const struct v4l2_frequency *freq)
{
struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file));
int err;
@@ -2546,7 +2531,7 @@ static int bcm2048_vidioc_s_frequency(struct file *file, void *priv,
}
static int bcm2048_vidioc_s_hw_freq_seek(struct file *file, void *priv,
- const struct v4l2_hw_freq_seek *seek)
+ const struct v4l2_hw_freq_seek *seek)
{
struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file));
int err;
@@ -2559,7 +2544,7 @@ static int bcm2048_vidioc_s_hw_freq_seek(struct file *file, void *priv,
err = bcm2048_set_fm_search_mode_direction(bdev, seek->seek_upward);
err |= bcm2048_set_fm_search_tune_mode(bdev,
- BCM2048_FM_AUTO_SEARCH_MODE);
+ BCM2048_FM_AUTO_SEARCH_MODE);
return err;
}
@@ -2594,7 +2579,7 @@ static struct video_device bcm2048_viddev_template = {
* I2C driver interface
*/
static int bcm2048_i2c_driver_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id)
{
struct bcm2048_device *bdev;
int err;
@@ -2613,8 +2598,8 @@ static int bcm2048_i2c_driver_probe(struct i2c_client *client,
if (client->irq) {
err = request_irq(client->irq,
- bcm2048_handler, IRQF_TRIGGER_FALLING,
- client->name, bdev);
+ bcm2048_handler, IRQF_TRIGGER_FALLING,
+ client->name, bdev);
if (err < 0) {
dev_err(&client->dev, "Could not request IRQ\n");
goto free_bdev;
diff --git a/drivers/staging/media/davinci_vpfe/Kconfig b/drivers/staging/media/davinci_vpfe/Kconfig
index 4de2f082491d..f40a06954a92 100644
--- a/drivers/staging/media/davinci_vpfe/Kconfig
+++ b/drivers/staging/media/davinci_vpfe/Kconfig
@@ -2,6 +2,8 @@ config VIDEO_DM365_VPFE
tristate "DM365 VPFE Media Controller Capture Driver"
depends on VIDEO_V4L2 && ARCH_DAVINCI_DM365 && !VIDEO_DM365_ISIF
depends on HAS_DMA
+ depends on VIDEO_V4L2_SUBDEV_API
+ depends on VIDEO_DAVINCI_VPBE_DISPLAY
select VIDEOBUF2_DMA_CONTIG
help
Support for DM365 VPFE based Media Controller Capture driver.
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c
index b1dfa2ccc4ef..ac78ed2f8bcc 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c
@@ -1536,8 +1536,9 @@ ipipe_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
* @fse: pointer to v4l2_subdev_frame_size_enum structure.
*/
static int
-ipipe_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_frame_size_enum *fse)
+ipipe_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
{
struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt format;
@@ -1711,8 +1712,11 @@ ipipe_link_setup(struct media_entity *entity, const struct media_pad *local,
struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe);
u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input;
- switch (local->index | media_entity_type(remote->entity)) {
- case IPIPE_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ if (!is_media_entity_v4l2_subdev(remote->entity))
+ return -EINVAL;
+
+ switch (local->index) {
+ case IPIPE_PAD_SINK:
if (!(flags & MEDIA_LNK_FL_ENABLED)) {
ipipe->input = IPIPE_INPUT_NONE;
break;
@@ -1725,7 +1729,7 @@ ipipe_link_setup(struct media_entity *entity, const struct media_pad *local,
ipipe->input = IPIPE_INPUT_CCDC;
break;
- case IPIPE_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ case IPIPE_PAD_SOURCE:
/* out to RESIZER */
if (flags & MEDIA_LNK_FL_ENABLED)
ipipe->output = IPIPE_OUTPUT_RESIZER;
@@ -1839,7 +1843,7 @@ vpfe_ipipe_init(struct vpfe_ipipe_device *ipipe, struct platform_device *pdev)
v4l2_ctrl_handler_setup(&ipipe->ctrls);
sd->ctrl_handler = &ipipe->ctrls;
- return media_entity_init(me, IPIPE_PADS_NUM, pads, 0);
+ return media_entity_pads_init(me, IPIPE_PADS_NUM, pads);
}
/*
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c
index 2a3a56b88de1..b1d5e23ae6e0 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c
@@ -254,7 +254,7 @@ int config_ipipe_hw(struct vpfe_ipipe_device *ipipe)
void __iomem *ipipe_base = ipipe->base_addr;
struct v4l2_mbus_framefmt *outformat;
u32 color_pat;
- u32 ipipe_mode;
+ int ipipe_mode;
u32 data_path;
/* enable clock to IPIPE */
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c
index 8b230541b1d1..633d6456fdce 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c
@@ -885,9 +885,14 @@ ipipeif_link_setup(struct media_entity *entity, const struct media_pad *local,
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
struct vpfe_device *vpfe = to_vpfe_device(ipipeif);
+ unsigned int index = local->index;
- switch (local->index | media_entity_type(remote->entity)) {
- case IPIPEIF_PAD_SINK | MEDIA_ENT_T_DEVNODE:
+ /* FIXME: this is actually a hack! */
+ if (is_media_entity_v4l2_subdev(remote->entity))
+ index |= 2 << 16;
+
+ switch (index) {
+ case IPIPEIF_PAD_SINK:
/* Single shot mode */
if (!(flags & MEDIA_LNK_FL_ENABLED)) {
ipipeif->input = IPIPEIF_INPUT_NONE;
@@ -896,7 +901,7 @@ ipipeif_link_setup(struct media_entity *entity, const struct media_pad *local,
ipipeif->input = IPIPEIF_INPUT_MEMORY;
break;
- case IPIPEIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ case IPIPEIF_PAD_SINK | 2 << 16:
/* read from isif */
if (!(flags & MEDIA_LNK_FL_ENABLED)) {
ipipeif->input = IPIPEIF_INPUT_NONE;
@@ -908,7 +913,7 @@ ipipeif_link_setup(struct media_entity *entity, const struct media_pad *local,
ipipeif->input = IPIPEIF_INPUT_ISIF;
break;
- case IPIPEIF_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ case IPIPEIF_PAD_SOURCE | 2 << 16:
if (!(flags & MEDIA_LNK_FL_ENABLED)) {
ipipeif->output = IPIPEIF_OUTPUT_NONE;
break;
@@ -971,7 +976,7 @@ vpfe_ipipeif_register_entities(struct vpfe_ipipeif_device *ipipeif,
ipipeif->video_in.vpfe_dev = vpfe_dev;
flags = 0;
- ret = media_entity_create_link(&ipipeif->video_in.video_dev.entity, 0,
+ ret = media_create_pad_link(&ipipeif->video_in.video_dev.entity, 0,
&ipipeif->subdev.entity, 0, flags);
if (ret < 0)
goto fail;
@@ -1026,7 +1031,7 @@ int vpfe_ipipeif_init(struct vpfe_ipipeif_device *ipipeif,
ipipeif->output = IPIPEIF_OUTPUT_NONE;
me->ops = &ipipeif_media_ops;
- ret = media_entity_init(me, IPIPEIF_NUM_PADS, pads, 0);
+ ret = media_entity_pads_init(me, IPIPEIF_NUM_PADS, pads);
if (ret)
goto fail;
diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c
index 80907b464412..99057892d88d 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_isif.c
+++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c
@@ -1707,9 +1707,14 @@ isif_link_setup(struct media_entity *entity, const struct media_pad *local,
{
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd);
+ unsigned int index = local->index;
- switch (local->index | media_entity_type(remote->entity)) {
- case ISIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* FIXME: this is actually a hack! */
+ if (is_media_entity_v4l2_subdev(remote->entity))
+ index |= 2 << 16;
+
+ switch (index) {
+ case ISIF_PAD_SINK | 2 << 16:
/* read from decoder/sensor */
if (!(flags & MEDIA_LNK_FL_ENABLED)) {
isif->input = ISIF_INPUT_NONE;
@@ -1720,7 +1725,7 @@ isif_link_setup(struct media_entity *entity, const struct media_pad *local,
isif->input = ISIF_INPUT_PARALLEL;
break;
- case ISIF_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+ case ISIF_PAD_SOURCE:
/* write to memory */
if (flags & MEDIA_LNK_FL_ENABLED)
isif->output = ISIF_OUTPUT_MEMORY;
@@ -1728,7 +1733,7 @@ isif_link_setup(struct media_entity *entity, const struct media_pad *local,
isif->output = ISIF_OUTPUT_NONE;
break;
- case ISIF_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ case ISIF_PAD_SOURCE | 2 << 16:
if (flags & MEDIA_LNK_FL_ENABLED)
isif->output = ISIF_OUTPUT_IPIPEIF;
else
@@ -1817,7 +1822,7 @@ int vpfe_isif_register_entities(struct vpfe_isif_device *isif,
isif->video_out.vpfe_dev = vpfe_dev;
flags = 0;
/* connect isif to video node */
- ret = media_entity_create_link(&isif->subdev.entity, 1,
+ ret = media_create_pad_link(&isif->subdev.entity, 1,
&isif->video_out.video_dev.entity,
0, flags);
if (ret < 0)
@@ -2052,7 +2057,7 @@ int vpfe_isif_init(struct vpfe_isif_device *isif, struct platform_device *pdev)
isif->input = ISIF_INPUT_NONE;
isif->output = ISIF_OUTPUT_NONE;
me->ops = &isif_media_ops;
- status = media_entity_init(me, ISIF_PADS_NUM, pads, 0);
+ status = media_entity_pads_init(me, ISIF_PADS_NUM, pads);
if (status)
goto isif_fail;
isif->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.c b/drivers/staging/media/davinci_vpfe/dm365_resizer.c
index acb293ed9c91..a91395ce91e1 100644
--- a/drivers/staging/media/davinci_vpfe/dm365_resizer.c
+++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.c
@@ -63,16 +63,11 @@ resizer_calculate_line_length(u32 pix, int width, int height,
if (pix == MEDIA_BUS_FMT_UYVY8_2X8 ||
pix == MEDIA_BUS_FMT_SGRBG12_1X12) {
*line_len = width << 1;
- } else if (pix == MEDIA_BUS_FMT_Y8_1X8 ||
- pix == MEDIA_BUS_FMT_UV8_1X8) {
- *line_len = width;
- *line_len_c = width;
} else {
- /* YUV 420 */
- /* round width to upper 32 byte boundary */
*line_len = width;
*line_len_c = width;
}
+
/* adjust the line len to be a multiple of 32 */
*line_len += 31;
*line_len &= ~0x1f;
@@ -1653,10 +1648,15 @@ static int resizer_link_setup(struct media_entity *entity,
struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
u16 ipipeif_source = vpfe_dev->vpfe_ipipeif.output;
u16 ipipe_source = vpfe_dev->vpfe_ipipe.output;
+ unsigned int index = local->index;
+
+ /* FIXME: this is actually a hack! */
+ if (is_media_entity_v4l2_subdev(remote->entity))
+ index |= 2 << 16;
if (&resizer->crop_resizer.subdev == sd) {
- switch (local->index | media_entity_type(remote->entity)) {
- case RESIZER_CROP_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ switch (index) {
+ case RESIZER_CROP_PAD_SINK | 2 << 16:
if (!(flags & MEDIA_LNK_FL_ENABLED)) {
resizer->crop_resizer.input =
RESIZER_CROP_INPUT_NONE;
@@ -1676,7 +1676,7 @@ static int resizer_link_setup(struct media_entity *entity,
return -EINVAL;
break;
- case RESIZER_CROP_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ case RESIZER_CROP_PAD_SOURCE | 2 << 16:
if (!(flags & MEDIA_LNK_FL_ENABLED)) {
resizer->crop_resizer.output =
RESIZER_CROP_OUTPUT_NONE;
@@ -1688,7 +1688,7 @@ static int resizer_link_setup(struct media_entity *entity,
resizer->crop_resizer.output = RESIZER_A;
break;
- case RESIZER_CROP_PAD_SOURCE2 | MEDIA_ENT_T_V4L2_SUBDEV:
+ case RESIZER_CROP_PAD_SOURCE2 | 2 << 16:
if (!(flags & MEDIA_LNK_FL_ENABLED)) {
resizer->crop_resizer.output2 =
RESIZER_CROP_OUTPUT_NONE;
@@ -1704,8 +1704,8 @@ static int resizer_link_setup(struct media_entity *entity,
return -EINVAL;
}
} else if (&resizer->resizer_a.subdev == sd) {
- switch (local->index | media_entity_type(remote->entity)) {
- case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ switch (index) {
+ case RESIZER_PAD_SINK | 2 << 16:
if (!(flags & MEDIA_LNK_FL_ENABLED)) {
resizer->resizer_a.input = RESIZER_INPUT_NONE;
break;
@@ -1715,7 +1715,7 @@ static int resizer_link_setup(struct media_entity *entity,
resizer->resizer_a.input = RESIZER_INPUT_CROP_RESIZER;
break;
- case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+ case RESIZER_PAD_SOURCE:
if (!(flags & MEDIA_LNK_FL_ENABLED)) {
resizer->resizer_a.output = RESIZER_OUTPUT_NONE;
break;
@@ -1729,8 +1729,8 @@ static int resizer_link_setup(struct media_entity *entity,
return -EINVAL;
}
} else if (&resizer->resizer_b.subdev == sd) {
- switch (local->index | media_entity_type(remote->entity)) {
- case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ switch (index) {
+ case RESIZER_PAD_SINK | 2 << 16:
if (!(flags & MEDIA_LNK_FL_ENABLED)) {
resizer->resizer_b.input = RESIZER_INPUT_NONE;
break;
@@ -1740,7 +1740,7 @@ static int resizer_link_setup(struct media_entity *entity,
resizer->resizer_b.input = RESIZER_INPUT_CROP_RESIZER;
break;
- case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+ case RESIZER_PAD_SOURCE:
if (!(flags & MEDIA_LNK_FL_ENABLED)) {
resizer->resizer_b.output = RESIZER_OUTPUT_NONE;
break;
@@ -1831,27 +1831,27 @@ int vpfe_resizer_register_entities(struct vpfe_resizer_device *resizer,
resizer->resizer_b.video_out.vpfe_dev = vpfe_dev;
/* create link between Resizer Crop----> Resizer A*/
- ret = media_entity_create_link(&resizer->crop_resizer.subdev.entity, 1,
+ ret = media_create_pad_link(&resizer->crop_resizer.subdev.entity, 1,
&resizer->resizer_a.subdev.entity,
0, flags);
if (ret < 0)
goto out_create_link;
/* create link between Resizer Crop----> Resizer B*/
- ret = media_entity_create_link(&resizer->crop_resizer.subdev.entity, 2,
+ ret = media_create_pad_link(&resizer->crop_resizer.subdev.entity, 2,
&resizer->resizer_b.subdev.entity,
0, flags);
if (ret < 0)
goto out_create_link;
/* create link between Resizer A ----> video out */
- ret = media_entity_create_link(&resizer->resizer_a.subdev.entity, 1,
+ ret = media_create_pad_link(&resizer->resizer_a.subdev.entity, 1,
&resizer->resizer_a.video_out.video_dev.entity, 0, flags);
if (ret < 0)
goto out_create_link;
/* create link between Resizer B ----> video out */
- ret = media_entity_create_link(&resizer->resizer_b.subdev.entity, 1,
+ ret = media_create_pad_link(&resizer->resizer_b.subdev.entity, 1,
&resizer->resizer_b.video_out.video_dev.entity, 0, flags);
if (ret < 0)
goto out_create_link;
@@ -1915,7 +1915,7 @@ int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz,
vpfe_rsz->crop_resizer.output2 = RESIZER_CROP_OUTPUT_NONE;
vpfe_rsz->crop_resizer.rsz_device = vpfe_rsz;
me->ops = &resizer_media_ops;
- ret = media_entity_init(me, RESIZER_CROP_PADS_NUM, pads, 0);
+ ret = media_entity_pads_init(me, RESIZER_CROP_PADS_NUM, pads);
if (ret)
return ret;
@@ -1937,7 +1937,7 @@ int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz,
vpfe_rsz->resizer_a.output = RESIZER_OUTPUT_NONE;
vpfe_rsz->resizer_a.rsz_device = vpfe_rsz;
me->ops = &resizer_media_ops;
- ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0);
+ ret = media_entity_pads_init(me, RESIZER_PADS_NUM, pads);
if (ret)
return ret;
@@ -1959,7 +1959,7 @@ int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz,
vpfe_rsz->resizer_b.output = RESIZER_OUTPUT_NONE;
vpfe_rsz->resizer_b.rsz_device = vpfe_rsz;
me->ops = &resizer_media_ops;
- ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0);
+ ret = media_entity_pads_init(me, RESIZER_PADS_NUM, pads);
if (ret)
return ret;
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
index 01df0683e950..ec46f366dd17 100644
--- a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
+++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
@@ -227,7 +227,7 @@ static int vpfe_enable_clock(struct vpfe_device *vpfe_dev)
return 0;
vpfe_dev->clks = kcalloc(vpfe_cfg->num_clocks,
- sizeof(struct clock *), GFP_KERNEL);
+ sizeof(*vpfe_dev->clks), GFP_KERNEL);
if (vpfe_dev->clks == NULL)
return -ENOMEM;
@@ -445,32 +445,32 @@ static int vpfe_register_entities(struct vpfe_device *vpfe_dev)
/* if entity has no pads (ex: amplifier),
cant establish link */
if (vpfe_dev->sd[i]->entity.num_pads) {
- ret = media_entity_create_link(&vpfe_dev->sd[i]->entity,
+ ret = media_create_pad_link(&vpfe_dev->sd[i]->entity,
0, &vpfe_dev->vpfe_isif.subdev.entity,
0, flags);
if (ret < 0)
goto out_resizer_register;
}
- ret = media_entity_create_link(&vpfe_dev->vpfe_isif.subdev.entity, 1,
+ ret = media_create_pad_link(&vpfe_dev->vpfe_isif.subdev.entity, 1,
&vpfe_dev->vpfe_ipipeif.subdev.entity,
0, flags);
if (ret < 0)
goto out_resizer_register;
- ret = media_entity_create_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1,
+ ret = media_create_pad_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1,
&vpfe_dev->vpfe_ipipe.subdev.entity,
0, flags);
if (ret < 0)
goto out_resizer_register;
- ret = media_entity_create_link(&vpfe_dev->vpfe_ipipe.subdev.entity,
+ ret = media_create_pad_link(&vpfe_dev->vpfe_ipipe.subdev.entity,
1, &vpfe_dev->vpfe_resizer.crop_resizer.subdev.entity,
0, flags);
if (ret < 0)
goto out_resizer_register;
- ret = media_entity_create_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1,
+ ret = media_create_pad_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1,
&vpfe_dev->vpfe_resizer.crop_resizer.subdev.entity,
0, flags);
if (ret < 0)
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c
index 0fdff91624fd..3ec7e65a3ffa 100644
--- a/drivers/staging/media/davinci_vpfe/vpfe_video.c
+++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c
@@ -88,7 +88,7 @@ vpfe_video_remote_subdev(struct vpfe_video_device *video, u32 *pad)
{
struct media_pad *remote = media_entity_remote_pad(&video->pad);
- if (remote == NULL || remote->entity->type != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (!remote || !is_media_entity_v4l2_subdev(remote->entity))
return NULL;
if (pad)
*pad = remote->index;
@@ -127,13 +127,14 @@ __vpfe_video_get_format(struct vpfe_video_device *video,
}
/* make a note of pipeline details */
-static void vpfe_prepare_pipeline(struct vpfe_video_device *video)
+static int vpfe_prepare_pipeline(struct vpfe_video_device *video)
{
+ struct media_entity_graph graph;
struct media_entity *entity = &video->video_dev.entity;
- struct media_device *mdev = entity->parent;
+ struct media_device *mdev = entity->graph_obj.mdev;
struct vpfe_pipeline *pipe = &video->pipe;
struct vpfe_video_device *far_end = NULL;
- struct media_entity_graph graph;
+ int ret;
pipe->input_num = 0;
pipe->output_num = 0;
@@ -144,11 +145,16 @@ static void vpfe_prepare_pipeline(struct vpfe_video_device *video)
pipe->outputs[pipe->output_num++] = video;
mutex_lock(&mdev->graph_mutex);
+ ret = media_entity_graph_walk_init(&graph, entity->graph_obj.mdev);
+ if (ret) {
+ mutex_unlock(&video->lock);
+ return -ENOMEM;
+ }
media_entity_graph_walk_start(&graph, entity);
while ((entity = media_entity_graph_walk_next(&graph))) {
if (entity == &video->video_dev.entity)
continue;
- if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+ if (!is_media_entity_v4l2_io(entity))
continue;
far_end = to_vpfe_video(media_entity_to_video_device(entity));
if (far_end->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
@@ -156,7 +162,10 @@ static void vpfe_prepare_pipeline(struct vpfe_video_device *video)
else
pipe->outputs[pipe->output_num++] = far_end;
}
+ media_entity_graph_walk_cleanup(&graph);
mutex_unlock(&mdev->graph_mutex);
+
+ return 0;
}
/* update pipe state selected by user */
@@ -165,7 +174,9 @@ static int vpfe_update_pipe_state(struct vpfe_video_device *video)
struct vpfe_pipeline *pipe = &video->pipe;
int ret;
- vpfe_prepare_pipeline(video);
+ ret = vpfe_prepare_pipeline(video);
+ if (ret)
+ return ret;
/* Find out if there is any input video
if yes, it is single shot.
@@ -243,8 +254,7 @@ static int vpfe_video_validate_pipeline(struct vpfe_pipeline *pipe)
/* Retrieve the source format */
pad = media_entity_remote_pad(pad);
- if (pad == NULL ||
- pad->entity->type != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
break;
subdev = media_entity_to_v4l2_subdev(pad->entity);
@@ -277,29 +287,35 @@ static int vpfe_video_validate_pipeline(struct vpfe_pipeline *pipe)
*/
static int vpfe_pipeline_enable(struct vpfe_pipeline *pipe)
{
- struct media_entity_graph graph;
struct media_entity *entity;
struct v4l2_subdev *subdev;
struct media_device *mdev;
- int ret = 0;
+ int ret;
if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS)
entity = vpfe_get_input_entity(pipe->outputs[0]);
else
entity = &pipe->inputs[0]->video_dev.entity;
- mdev = entity->parent;
+ mdev = entity->graph_obj.mdev;
mutex_lock(&mdev->graph_mutex);
- media_entity_graph_walk_start(&graph, entity);
- while ((entity = media_entity_graph_walk_next(&graph))) {
+ ret = media_entity_graph_walk_init(&pipe->graph,
+ entity->graph_obj.mdev);
+ if (ret)
+ goto out;
+ media_entity_graph_walk_start(&pipe->graph, entity);
+ while ((entity = media_entity_graph_walk_next(&pipe->graph))) {
- if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
+ if (!is_media_entity_v4l2_subdev(entity))
continue;
subdev = media_entity_to_v4l2_subdev(entity);
ret = v4l2_subdev_call(subdev, video, s_stream, 1);
if (ret < 0 && ret != -ENOIOCTLCMD)
break;
}
+out:
+ if (ret)
+ media_entity_graph_walk_cleanup(&pipe->graph);
mutex_unlock(&mdev->graph_mutex);
return ret;
}
@@ -317,7 +333,6 @@ static int vpfe_pipeline_enable(struct vpfe_pipeline *pipe)
*/
static int vpfe_pipeline_disable(struct vpfe_pipeline *pipe)
{
- struct media_entity_graph graph;
struct media_entity *entity;
struct v4l2_subdev *subdev;
struct media_device *mdev;
@@ -328,13 +343,13 @@ static int vpfe_pipeline_disable(struct vpfe_pipeline *pipe)
else
entity = &pipe->inputs[0]->video_dev.entity;
- mdev = entity->parent;
+ mdev = entity->graph_obj.mdev;
mutex_lock(&mdev->graph_mutex);
- media_entity_graph_walk_start(&graph, entity);
+ media_entity_graph_walk_start(&pipe->graph, entity);
- while ((entity = media_entity_graph_walk_next(&graph))) {
+ while ((entity = media_entity_graph_walk_next(&pipe->graph))) {
- if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
+ if (!is_media_entity_v4l2_subdev(entity))
continue;
subdev = media_entity_to_v4l2_subdev(entity);
ret = v4l2_subdev_call(subdev, video, s_stream, 0);
@@ -343,6 +358,7 @@ static int vpfe_pipeline_disable(struct vpfe_pipeline *pipe)
}
mutex_unlock(&mdev->graph_mutex);
+ media_entity_graph_walk_cleanup(&pipe->graph);
return ret ? -ETIMEDOUT : 0;
}
@@ -470,7 +486,7 @@ void vpfe_video_process_buffer_complete(struct vpfe_video_device *video)
{
struct vpfe_pipeline *pipe = &video->pipe;
- v4l2_get_timestamp(&video->cur_frm->vb.timestamp);
+ video->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns();
vb2_buffer_done(&video->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS)
video->cur_frm = video->next_frm;
@@ -1078,7 +1094,7 @@ vpfe_g_dv_timings(struct file *file, void *fh,
* the buffer nbuffers and buffer size
*/
static int
-vpfe_buffer_queue_setup(struct vb2_queue *vq, const void *parg,
+vpfe_buffer_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
@@ -1600,8 +1616,8 @@ int vpfe_video_init(struct vpfe_video_device *video, const char *name)
spin_lock_init(&video->irqlock);
spin_lock_init(&video->dma_queue_lock);
mutex_init(&video->lock);
- ret = media_entity_init(&video->video_dev.entity,
- 1, &video->pad, 0);
+ ret = media_entity_pads_init(&video->video_dev.entity,
+ 1, &video->pad);
if (ret < 0)
return ret;
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.h b/drivers/staging/media/davinci_vpfe/vpfe_video.h
index 673cefe3ef61..653334d537d3 100644
--- a/drivers/staging/media/davinci_vpfe/vpfe_video.h
+++ b/drivers/staging/media/davinci_vpfe/vpfe_video.h
@@ -52,6 +52,7 @@ enum vpfe_video_state {
struct vpfe_pipeline {
/* media pipeline */
struct media_pipeline *pipe;
+ struct media_entity_graph graph;
/* state of the pipeline, continuous,
* single-shot or stopped
*/
diff --git a/drivers/staging/media/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c
index 534b8103ae80..ff1926ca1f96 100644
--- a/drivers/staging/media/lirc/lirc_imon.c
+++ b/drivers/staging/media/lirc/lirc_imon.c
@@ -885,12 +885,14 @@ static int imon_probe(struct usb_interface *interface,
vendor, product, ifnum, usbdev->bus->busnum, usbdev->devnum);
/* Everything went fine. Just unlock and return retval (with is 0) */
+ mutex_unlock(&context->ctx_lock);
goto driver_unlock;
unregister_lirc:
lirc_unregister_driver(driver->minor);
free_tx_urb:
+ mutex_unlock(&context->ctx_lock);
usb_free_urb(tx_urb);
free_rx_urb:
diff --git a/drivers/staging/media/lirc/lirc_parallel.c b/drivers/staging/media/lirc/lirc_parallel.c
index c1408342b1d0..d009bcb439f0 100644
--- a/drivers/staging/media/lirc/lirc_parallel.c
+++ b/drivers/staging/media/lirc/lirc_parallel.c
@@ -33,7 +33,7 @@
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
-#include <linux/time.h>
+#include <linux/ktime.h>
#include <linux/mm.h>
#include <linux/delay.h>
@@ -144,25 +144,22 @@ static void lirc_off(void)
static unsigned int init_lirc_timer(void)
{
- struct timeval tv, now;
+ ktime_t kt, now, timeout;
unsigned int level, newlevel, timeelapsed, newtimer;
int count = 0;
- do_gettimeofday(&tv);
- tv.tv_sec++; /* wait max. 1 sec. */
+ kt = ktime_get();
+ /* wait max. 1 sec. */
+ timeout = ktime_add_ns(kt, NSEC_PER_SEC);
level = lirc_get_timer();
do {
newlevel = lirc_get_timer();
if (level == 0 && newlevel != 0)
count++;
level = newlevel;
- do_gettimeofday(&now);
- } while (count < 1000 && (now.tv_sec < tv.tv_sec
- || (now.tv_sec == tv.tv_sec
- && now.tv_usec < tv.tv_usec)));
-
- timeelapsed = (now.tv_sec + 1 - tv.tv_sec)*1000000
- + (now.tv_usec - tv.tv_usec);
+ now = ktime_get();
+ } while (count < 1000 && (ktime_before(now, timeout)));
+ timeelapsed = ktime_us_delta(now, kt);
if (count >= 1000 && timeelapsed > 0) {
if (default_timer == 0) {
/* autodetect timer */
@@ -220,8 +217,8 @@ static void rbuf_write(int signal)
static void lirc_lirc_irq_handler(void *blah)
{
- struct timeval tv;
- static struct timeval lasttv;
+ ktime_t kt, delkt;
+ static ktime_t lastkt;
static int init;
long signal;
int data;
@@ -244,16 +241,14 @@ static void lirc_lirc_irq_handler(void *blah)
#ifdef LIRC_TIMER
if (init) {
- do_gettimeofday(&tv);
+ kt = ktime_get();
- signal = tv.tv_sec - lasttv.tv_sec;
- if (signal > 15)
+ delkt = ktime_sub(kt, lastkt);
+ if (ktime_compare(delkt, ktime_set(15, 0)) > 0)
/* really long time */
data = PULSE_MASK;
else
- data = (int) (signal*1000000 +
- tv.tv_usec - lasttv.tv_usec +
- LIRC_SFH506_DELAY);
+ data = (int)(ktime_to_us(delkt) + LIRC_SFH506_DELAY);
rbuf_write(data); /* space */
} else {
@@ -301,7 +296,7 @@ static void lirc_lirc_irq_handler(void *blah)
data = 1;
rbuf_write(PULSE_BIT|data); /* pulse */
}
- do_gettimeofday(&lasttv);
+ lastkt = ktime_get();
#else
/* add your code here */
#endif
diff --git a/drivers/staging/media/lirc/lirc_sasem.c b/drivers/staging/media/lirc/lirc_sasem.c
index f2dca69c2bc0..2218d0042030 100644
--- a/drivers/staging/media/lirc/lirc_sasem.c
+++ b/drivers/staging/media/lirc/lirc_sasem.c
@@ -42,6 +42,7 @@
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
+#include <linux/ktime.h>
#include <media/lirc.h>
#include <media/lirc_dev.h>
@@ -111,7 +112,7 @@ struct sasem_context {
} tx;
/* for dealing with repeat codes (wish there was a toggle bit!) */
- struct timeval presstime;
+ ktime_t presstime;
char lastcode[8];
int codesaved;
};
@@ -566,8 +567,8 @@ static void incoming_packet(struct sasem_context *context,
{
int len = urb->actual_length;
unsigned char *buf = urb->transfer_buffer;
- long ms;
- struct timeval tv;
+ u64 ns;
+ ktime_t kt;
if (len != 8) {
dev_warn(&context->dev->dev,
@@ -584,9 +585,8 @@ static void incoming_packet(struct sasem_context *context,
*/
/* get the time since the last button press */
- do_gettimeofday(&tv);
- ms = (tv.tv_sec - context->presstime.tv_sec) * 1000 +
- (tv.tv_usec - context->presstime.tv_usec) / 1000;
+ kt = ktime_get();
+ ns = ktime_to_ns(ktime_sub(kt, context->presstime));
if (memcmp(buf, "\x08\0\0\0\0\0\0\0", 8) == 0) {
/*
@@ -600,10 +600,9 @@ static void incoming_packet(struct sasem_context *context,
* in that time and then get a false repeat of the previous
* press but it is long enough for a genuine repeat
*/
- if ((ms < 250) && (context->codesaved != 0)) {
+ if ((ns < 250 * NSEC_PER_MSEC) && (context->codesaved != 0)) {
memcpy(buf, &context->lastcode, 8);
- context->presstime.tv_sec = tv.tv_sec;
- context->presstime.tv_usec = tv.tv_usec;
+ context->presstime = kt;
}
} else {
/* save the current valid code for repeats */
@@ -613,8 +612,7 @@ static void incoming_packet(struct sasem_context *context,
* just for safety reasons
*/
context->codesaved = 1;
- context->presstime.tv_sec = tv.tv_sec;
- context->presstime.tv_usec = tv.tv_usec;
+ context->presstime = kt;
}
lirc_buffer_write(context->driver->rbuf, buf);
diff --git a/drivers/staging/media/lirc/lirc_serial.c b/drivers/staging/media/lirc/lirc_serial.c
index 64a7b2fc5289..b798b311d32c 100644
--- a/drivers/staging/media/lirc/lirc_serial.c
+++ b/drivers/staging/media/lirc/lirc_serial.c
@@ -59,7 +59,7 @@
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/serial_reg.h>
-#include <linux/time.h>
+#include <linux/ktime.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/wait.h>
@@ -204,7 +204,7 @@ static struct lirc_serial hardware[] = {
#define RBUF_LEN 256
-static struct timeval lasttv = {0, 0};
+static ktime_t lastkt;
static struct lirc_buffer rbuf;
@@ -542,10 +542,10 @@ static void frbwrite(int l)
static irqreturn_t lirc_irq_handler(int i, void *blah)
{
- struct timeval tv;
+ ktime_t kt;
int counter, dcd;
u8 status;
- long deltv;
+ ktime_t delkt;
int data;
static int last_dcd = -1;
@@ -565,7 +565,7 @@ static irqreturn_t lirc_irq_handler(int i, void *blah)
if ((status & hardware[type].signal_pin_change)
&& sense != -1) {
/* get current time */
- do_gettimeofday(&tv);
+ kt = ktime_get();
/* New mode, written by Trent Piepho
<xyzzy@u.washington.edu>. */
@@ -594,34 +594,20 @@ static irqreturn_t lirc_irq_handler(int i, void *blah)
dcd = (status & hardware[type].signal_pin) ? 1 : 0;
if (dcd == last_dcd) {
- pr_warn("ignoring spike: %d %d %lx %lx %lx %lx\n",
- dcd, sense,
- tv.tv_sec, lasttv.tv_sec,
- (unsigned long)tv.tv_usec,
- (unsigned long)lasttv.tv_usec);
+ pr_warn("ignoring spike: %d %d %llx %llx\n",
+ dcd, sense, ktime_to_us(kt),
+ ktime_to_us(lastkt));
continue;
}
- deltv = tv.tv_sec-lasttv.tv_sec;
- if (tv.tv_sec < lasttv.tv_sec ||
- (tv.tv_sec == lasttv.tv_sec &&
- tv.tv_usec < lasttv.tv_usec)) {
- pr_warn("AIEEEE: your clock just jumped backwards\n");
- pr_warn("%d %d %lx %lx %lx %lx\n",
- dcd, sense,
- tv.tv_sec, lasttv.tv_sec,
- (unsigned long)tv.tv_usec,
- (unsigned long)lasttv.tv_usec);
- data = PULSE_MASK;
- } else if (deltv > 15) {
+ delkt = ktime_sub(kt, lastkt);
+ if (ktime_compare(delkt, ktime_set(15, 0)) > 0) {
data = PULSE_MASK; /* really long time */
if (!(dcd^sense)) {
/* sanity check */
- pr_warn("AIEEEE: %d %d %lx %lx %lx %lx\n",
- dcd, sense,
- tv.tv_sec, lasttv.tv_sec,
- (unsigned long)tv.tv_usec,
- (unsigned long)lasttv.tv_usec);
+ pr_warn("AIEEEE: %d %d %llx %llx\n",
+ dcd, sense, ktime_to_us(kt),
+ ktime_to_us(lastkt));
/*
* detecting pulse while this
* MUST be a space!
@@ -629,11 +615,9 @@ static irqreturn_t lirc_irq_handler(int i, void *blah)
sense = sense ? 0 : 1;
}
} else
- data = (int) (deltv*1000000 +
- tv.tv_usec -
- lasttv.tv_usec);
+ data = (int) ktime_to_us(delkt);
frbwrite(dcd^sense ? data : (data|PULSE_BIT));
- lasttv = tv;
+ lastkt = kt;
last_dcd = dcd;
wake_up_interruptible(&rbuf.wait_poll);
}
@@ -790,7 +774,7 @@ static int set_use_inc(void *data)
unsigned long flags;
/* initialize timestamp */
- do_gettimeofday(&lasttv);
+ lastkt = ktime_get();
spin_lock_irqsave(&hardware[type].lock, flags);
@@ -979,7 +963,7 @@ static int lirc_serial_resume(struct platform_device *dev)
spin_lock_irqsave(&hardware[type].lock, flags);
/* Enable Interrupt */
- do_gettimeofday(&lasttv);
+ lastkt = ktime_get();
soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
off();
diff --git a/drivers/staging/media/omap4iss/Kconfig b/drivers/staging/media/omap4iss/Kconfig
index 8d4e3bd1bfe1..46183464ee79 100644
--- a/drivers/staging/media/omap4iss/Kconfig
+++ b/drivers/staging/media/omap4iss/Kconfig
@@ -1,6 +1,6 @@
config VIDEO_OMAP4
tristate "OMAP 4 Camera support"
- depends on VIDEO_V4L2=y && VIDEO_V4L2_SUBDEV_API && I2C=y && ARCH_OMAP4
+ depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && I2C && ARCH_OMAP4
depends on HAS_DMA
select MFD_SYSCON
select VIDEOBUF2_DMA_CONTIG
diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c
index aa76ccda5b42..30b473cfb020 100644
--- a/drivers/staging/media/omap4iss/iss.c
+++ b/drivers/staging/media/omap4iss/iss.c
@@ -389,15 +389,15 @@ static irqreturn_t iss_isr(int irq, void *_iss)
*
* Return the total number of users of all video device nodes in the pipeline.
*/
-static int iss_pipeline_pm_use_count(struct media_entity *entity)
+static int iss_pipeline_pm_use_count(struct media_entity *entity,
+ struct media_entity_graph *graph)
{
- struct media_entity_graph graph;
int use = 0;
- media_entity_graph_walk_start(&graph, entity);
+ media_entity_graph_walk_start(graph, entity);
- while ((entity = media_entity_graph_walk_next(&graph))) {
- if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
+ while ((entity = media_entity_graph_walk_next(graph))) {
+ if (is_media_entity_v4l2_io(entity))
use += entity->use_count;
}
@@ -419,7 +419,7 @@ static int iss_pipeline_pm_power_one(struct media_entity *entity, int change)
{
struct v4l2_subdev *subdev;
- subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV
+ subdev = is_media_entity_v4l2_subdev(entity)
? media_entity_to_v4l2_subdev(entity) : NULL;
if (entity->use_count == 0 && change > 0 && subdev) {
@@ -449,29 +449,29 @@ static int iss_pipeline_pm_power_one(struct media_entity *entity, int change)
*
* Return 0 on success or a negative error code on failure.
*/
-static int iss_pipeline_pm_power(struct media_entity *entity, int change)
+static int iss_pipeline_pm_power(struct media_entity *entity, int change,
+ struct media_entity_graph *graph)
{
- struct media_entity_graph graph;
struct media_entity *first = entity;
int ret = 0;
if (!change)
return 0;
- media_entity_graph_walk_start(&graph, entity);
+ media_entity_graph_walk_start(graph, entity);
- while (!ret && (entity = media_entity_graph_walk_next(&graph)))
- if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+ while (!ret && (entity = media_entity_graph_walk_next(graph)))
+ if (is_media_entity_v4l2_subdev(entity))
ret = iss_pipeline_pm_power_one(entity, change);
if (!ret)
return 0;
- media_entity_graph_walk_start(&graph, first);
+ media_entity_graph_walk_start(graph, first);
- while ((first = media_entity_graph_walk_next(&graph)) &&
+ while ((first = media_entity_graph_walk_next(graph)) &&
first != entity)
- if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE)
+ if (is_media_entity_v4l2_subdev(first))
iss_pipeline_pm_power_one(first, -change);
return ret;
@@ -489,23 +489,24 @@ static int iss_pipeline_pm_power(struct media_entity *entity, int change)
* off is assumed to never fail. No failure can occur when the use parameter is
* set to 0.
*/
-int omap4iss_pipeline_pm_use(struct media_entity *entity, int use)
+int omap4iss_pipeline_pm_use(struct media_entity *entity, int use,
+ struct media_entity_graph *graph)
{
int change = use ? 1 : -1;
int ret;
- mutex_lock(&entity->parent->graph_mutex);
+ mutex_lock(&entity->graph_obj.mdev->graph_mutex);
/* Apply use count to node. */
entity->use_count += change;
WARN_ON(entity->use_count < 0);
/* Apply power change to connected non-nodes. */
- ret = iss_pipeline_pm_power(entity, change);
+ ret = iss_pipeline_pm_power(entity, change, graph);
if (ret < 0)
entity->use_count -= change;
- mutex_unlock(&entity->parent->graph_mutex);
+ mutex_unlock(&entity->graph_obj.mdev->graph_mutex);
return ret;
}
@@ -526,34 +527,48 @@ int omap4iss_pipeline_pm_use(struct media_entity *entity, int use)
static int iss_pipeline_link_notify(struct media_link *link, u32 flags,
unsigned int notification)
{
+ struct media_entity_graph *graph =
+ &container_of(link->graph_obj.mdev, struct iss_device,
+ media_dev)->pm_count_graph;
struct media_entity *source = link->source->entity;
struct media_entity *sink = link->sink->entity;
- int source_use = iss_pipeline_pm_use_count(source);
- int sink_use = iss_pipeline_pm_use_count(sink);
+ int source_use;
+ int sink_use;
int ret;
+ if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH) {
+ ret = media_entity_graph_walk_init(graph,
+ link->graph_obj.mdev);
+ if (ret)
+ return ret;
+ }
+
+ source_use = iss_pipeline_pm_use_count(source, graph);
+ sink_use = iss_pipeline_pm_use_count(sink, graph);
+
if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
- !(link->flags & MEDIA_LNK_FL_ENABLED)) {
+ !(flags & MEDIA_LNK_FL_ENABLED)) {
/* Powering off entities is assumed to never fail. */
- iss_pipeline_pm_power(source, -sink_use);
- iss_pipeline_pm_power(sink, -source_use);
+ iss_pipeline_pm_power(source, -sink_use, graph);
+ iss_pipeline_pm_power(sink, -source_use, graph);
return 0;
}
- if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
+ if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH &&
(flags & MEDIA_LNK_FL_ENABLED)) {
- ret = iss_pipeline_pm_power(source, sink_use);
+ ret = iss_pipeline_pm_power(source, sink_use, graph);
if (ret < 0)
return ret;
- ret = iss_pipeline_pm_power(sink, source_use);
+ ret = iss_pipeline_pm_power(sink, source_use, graph);
if (ret < 0)
- iss_pipeline_pm_power(source, -sink_use);
-
- return ret;
+ iss_pipeline_pm_power(source, -sink_use, graph);
}
- return 0;
+ if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH)
+ media_entity_graph_walk_cleanup(graph);
+
+ return ret;
}
/* -----------------------------------------------------------------------------
@@ -590,8 +605,7 @@ static int iss_pipeline_disable(struct iss_pipeline *pipe,
break;
pad = media_entity_remote_pad(pad);
- if (!pad ||
- media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
break;
entity = pad->entity;
@@ -601,13 +615,13 @@ static int iss_pipeline_disable(struct iss_pipeline *pipe,
subdev = media_entity_to_v4l2_subdev(entity);
ret = v4l2_subdev_call(subdev, video, s_stream, 0);
if (ret < 0) {
- dev_dbg(iss->dev, "%s: module stop timeout.\n",
- subdev->name);
+ dev_warn(iss->dev, "%s: module stop timeout.\n",
+ subdev->name);
/* If the entity failed to stopped, assume it has
* crashed. Mark it as such, the ISS will be reset when
* applications will release it.
*/
- iss->crashed |= 1U << subdev->entity.id;
+ media_entity_enum_set(&iss->crashed, &subdev->entity);
failure = -ETIMEDOUT;
}
}
@@ -642,7 +656,7 @@ static int iss_pipeline_enable(struct iss_pipeline *pipe,
* pipeline won't start anyway (those entities would then likely fail to
* stop, making the problem worse).
*/
- if (pipe->entities & iss->crashed)
+ if (media_entity_enum_intersects(&pipe->ent_enum, &iss->crashed))
return -EIO;
spin_lock_irqsave(&pipe->lock, flags);
@@ -658,8 +672,7 @@ static int iss_pipeline_enable(struct iss_pipeline *pipe,
break;
pad = media_entity_remote_pad(pad);
- if (!pad ||
- media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
break;
entity = pad->entity;
@@ -763,7 +776,8 @@ static int iss_reset(struct iss_device *iss)
return -ETIMEDOUT;
}
- iss->crashed = 0;
+ media_entity_enum_zero(&iss->crashed);
+
return 0;
}
@@ -1092,7 +1106,7 @@ void omap4iss_put(struct iss_device *iss)
* be worth investigating whether resetting the ISP only can't
* fix the problem in some cases.
*/
- if (iss->crashed)
+ if (!media_entity_enum_empty(&iss->crashed))
iss_reset(iss);
iss_disable_clocks(iss);
}
@@ -1259,7 +1273,7 @@ static int iss_register_entities(struct iss_device *iss)
goto done;
}
- ret = media_entity_create_link(&sensor->entity, 0, input, pad,
+ ret = media_create_pad_link(&sensor->entity, 0, input, pad,
flags);
if (ret < 0)
goto done;
@@ -1274,6 +1288,68 @@ done:
return ret;
}
+/*
+ * iss_create_links() - Pads links creation for the subdevices
+ * @iss : Pointer to ISS device
+ *
+ * return negative error code or zero on success
+ */
+static int iss_create_links(struct iss_device *iss)
+{
+ int ret;
+
+ ret = omap4iss_csi2_create_links(iss);
+ if (ret < 0) {
+ dev_err(iss->dev, "CSI2 pads links creation failed\n");
+ return ret;
+ }
+
+ ret = omap4iss_ipipeif_create_links(iss);
+ if (ret < 0) {
+ dev_err(iss->dev, "ISP IPIPEIF pads links creation failed\n");
+ return ret;
+ }
+
+ ret = omap4iss_resizer_create_links(iss);
+ if (ret < 0) {
+ dev_err(iss->dev, "ISP RESIZER pads links creation failed\n");
+ return ret;
+ }
+
+ /* Connect the submodules. */
+ ret = media_create_pad_link(
+ &iss->csi2a.subdev.entity, CSI2_PAD_SOURCE,
+ &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SINK, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = media_create_pad_link(
+ &iss->csi2b.subdev.entity, CSI2_PAD_SOURCE,
+ &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SINK, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = media_create_pad_link(
+ &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SOURCE_VP,
+ &iss->resizer.subdev.entity, RESIZER_PAD_SINK, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = media_create_pad_link(
+ &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SOURCE_VP,
+ &iss->ipipe.subdev.entity, IPIPE_PAD_SINK, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = media_create_pad_link(
+ &iss->ipipe.subdev.entity, IPIPE_PAD_SOURCE_VP,
+ &iss->resizer.subdev.entity, RESIZER_PAD_SINK, 0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+};
+
static void iss_cleanup_modules(struct iss_device *iss)
{
omap4iss_csi2_cleanup(iss);
@@ -1316,41 +1392,8 @@ static int iss_initialize_modules(struct iss_device *iss)
goto error_resizer;
}
- /* Connect the submodules. */
- ret = media_entity_create_link(
- &iss->csi2a.subdev.entity, CSI2_PAD_SOURCE,
- &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SINK, 0);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(
- &iss->csi2b.subdev.entity, CSI2_PAD_SOURCE,
- &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SINK, 0);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(
- &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SOURCE_VP,
- &iss->resizer.subdev.entity, RESIZER_PAD_SINK, 0);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(
- &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SOURCE_VP,
- &iss->ipipe.subdev.entity, IPIPE_PAD_SINK, 0);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(
- &iss->ipipe.subdev.entity, IPIPE_PAD_SOURCE_VP,
- &iss->resizer.subdev.entity, RESIZER_PAD_SINK, 0);
- if (ret < 0)
- goto error_link;
-
return 0;
-error_link:
- omap4iss_resizer_cleanup(iss);
error_resizer:
omap4iss_ipipe_cleanup(iss);
error_ipipe:
@@ -1464,10 +1507,21 @@ static int iss_probe(struct platform_device *pdev)
if (ret < 0)
goto error_modules;
+ ret = media_entity_enum_init(&iss->crashed, &iss->media_dev);
+ if (ret)
+ goto error_entities;
+
+ ret = iss_create_links(iss);
+ if (ret < 0)
+ goto error_entities;
+
omap4iss_put(iss);
return 0;
+error_entities:
+ iss_unregister_entities(iss);
+ media_entity_enum_cleanup(&iss->crashed);
error_modules:
iss_cleanup_modules(iss);
error_iss:
@@ -1485,6 +1539,7 @@ static int iss_remove(struct platform_device *pdev)
struct iss_device *iss = platform_get_drvdata(pdev);
iss_unregister_entities(iss);
+ media_entity_enum_cleanup(&iss->crashed);
iss_cleanup_modules(iss);
return 0;
diff --git a/drivers/staging/media/omap4iss/iss.h b/drivers/staging/media/omap4iss/iss.h
index 35df8b4709e6..05f08a3caa19 100644
--- a/drivers/staging/media/omap4iss/iss.h
+++ b/drivers/staging/media/omap4iss/iss.h
@@ -20,7 +20,7 @@
#include <linux/platform_device.h>
#include <linux/wait.h>
-#include <media/omap4iss.h>
+#include <linux/platform_data/media/omap4iss.h>
#include "iss_regs.h"
#include "iss_csiphy.h"
@@ -82,11 +82,12 @@ struct iss_reg {
/*
* struct iss_device - ISS device structure.
* @syscon: Regmap for the syscon register space
- * @crashed: Bitmask of crashed entities (indexed by entity ID)
+ * @crashed: Crashed entities
*/
struct iss_device {
struct v4l2_device v4l2_dev;
struct media_device media_dev;
+ struct media_entity_graph pm_count_graph;
struct device *dev;
u32 revision;
@@ -101,7 +102,7 @@ struct iss_device {
u64 raw_dmamask;
struct mutex iss_mutex; /* For handling ref_count field */
- unsigned int crashed;
+ struct media_entity_enum crashed;
int has_context;
int ref_count;
@@ -151,7 +152,8 @@ void omap4iss_isp_subclk_enable(struct iss_device *iss,
void omap4iss_isp_subclk_disable(struct iss_device *iss,
enum iss_isp_subclk_resource res);
-int omap4iss_pipeline_pm_use(struct media_entity *entity, int use);
+int omap4iss_pipeline_pm_use(struct media_entity *entity, int use,
+ struct media_entity_graph *graph);
int omap4iss_register_entities(struct platform_device *pdev,
struct v4l2_device *v4l2_dev);
diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c
index c6e6d47ac57f..aaca39d751a5 100644
--- a/drivers/staging/media/omap4iss/iss_csi2.c
+++ b/drivers/staging/media/omap4iss/iss_csi2.c
@@ -674,6 +674,9 @@ static void csi2_isr_ctx(struct iss_csi2_device *csi2,
status = iss_reg_read(csi2->iss, csi2->regs1, CSI2_CTX_IRQSTATUS(n));
iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_IRQSTATUS(n), status);
+ if (omap4iss_module_sync_is_stopping(&csi2->wait, &csi2->stopping))
+ return;
+
/* Propagate frame number */
if (status & CSI2_CTX_IRQ_FS) {
struct iss_pipeline *pipe =
@@ -776,9 +779,6 @@ void omap4iss_csi2_isr(struct iss_csi2_device *csi2)
pipe->error = true;
}
- if (omap4iss_module_sync_is_stopping(&csi2->wait, &csi2->stopping))
- return;
-
/* Successful cases */
if (csi2_irqstatus & CSI2_IRQ_CONTEXT0)
csi2_isr_ctx(csi2, &csi2->contexts[0]);
@@ -1170,14 +1170,19 @@ static int csi2_link_setup(struct media_entity *entity,
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
struct iss_csi2_ctrl_cfg *ctrl = &csi2->ctrl;
+ unsigned int index = local->index;
+
+ /* FIXME: this is actually a hack! */
+ if (is_media_entity_v4l2_subdev(remote->entity))
+ index |= 2 << 16;
/*
* The ISS core doesn't support pipelines with multiple video outputs.
* Revisit this when it will be implemented, and return -EBUSY for now.
*/
- switch (local->index | media_entity_type(remote->entity)) {
- case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+ switch (index) {
+ case CSI2_PAD_SOURCE:
if (flags & MEDIA_LNK_FL_ENABLED) {
if (csi2->output & ~CSI2_OUTPUT_MEMORY)
return -EBUSY;
@@ -1187,7 +1192,7 @@ static int csi2_link_setup(struct media_entity *entity,
}
break;
- case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ case CSI2_PAD_SOURCE | 2 << 16:
if (flags & MEDIA_LNK_FL_ENABLED) {
if (csi2->output & ~CSI2_OUTPUT_IPIPEIF)
return -EBUSY;
@@ -1271,7 +1276,7 @@ static int csi2_init_entities(struct iss_csi2_device *csi2, const char *subname)
pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
me->ops = &csi2_media_ops;
- ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0);
+ ret = media_entity_pads_init(me, CSI2_PADS_NUM, pads);
if (ret < 0)
return ret;
@@ -1290,16 +1295,8 @@ static int csi2_init_entities(struct iss_csi2_device *csi2, const char *subname)
if (ret < 0)
goto error_video;
- /* Connect the CSI2 subdev to the video node. */
- ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE,
- &csi2->video_out.video.entity, 0, 0);
- if (ret < 0)
- goto error_link;
-
return 0;
-error_link:
- omap4iss_video_cleanup(&csi2->video_out);
error_video:
media_entity_cleanup(&csi2->subdev.entity);
return ret;
@@ -1342,6 +1339,33 @@ int omap4iss_csi2_init(struct iss_device *iss)
}
/*
+ * omap4iss_csi2_create_links() - CSI2 pads links creation
+ * @iss: Pointer to ISS device
+ *
+ * return negative error code or zero on success
+ */
+int omap4iss_csi2_create_links(struct iss_device *iss)
+{
+ struct iss_csi2_device *csi2a = &iss->csi2a;
+ struct iss_csi2_device *csi2b = &iss->csi2b;
+ int ret;
+
+ /* Connect the CSI2a subdev to the video node. */
+ ret = media_create_pad_link(&csi2a->subdev.entity, CSI2_PAD_SOURCE,
+ &csi2a->video_out.video.entity, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Connect the CSI2b subdev to the video node. */
+ ret = media_create_pad_link(&csi2b->subdev.entity, CSI2_PAD_SOURCE,
+ &csi2b->video_out.video.entity, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/*
* omap4iss_csi2_cleanup - Routine for module driver cleanup
*/
void omap4iss_csi2_cleanup(struct iss_device *iss)
diff --git a/drivers/staging/media/omap4iss/iss_csi2.h b/drivers/staging/media/omap4iss/iss_csi2.h
index f2f5343b4a80..24ab378d469f 100644
--- a/drivers/staging/media/omap4iss/iss_csi2.h
+++ b/drivers/staging/media/omap4iss/iss_csi2.h
@@ -151,6 +151,7 @@ struct iss_csi2_device {
void omap4iss_csi2_isr(struct iss_csi2_device *csi2);
int omap4iss_csi2_reset(struct iss_csi2_device *csi2);
int omap4iss_csi2_init(struct iss_device *iss);
+int omap4iss_csi2_create_links(struct iss_device *iss);
void omap4iss_csi2_cleanup(struct iss_device *iss);
void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2);
int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2,
diff --git a/drivers/staging/media/omap4iss/iss_csiphy.h b/drivers/staging/media/omap4iss/iss_csiphy.h
index e9ca43955654..a0f2d974daeb 100644
--- a/drivers/staging/media/omap4iss/iss_csiphy.h
+++ b/drivers/staging/media/omap4iss/iss_csiphy.h
@@ -14,7 +14,7 @@
#ifndef OMAP4_ISS_CSI_PHY_H
#define OMAP4_ISS_CSI_PHY_H
-#include <media/omap4iss.h>
+#include <linux/platform_data/media/omap4iss.h>
struct iss_csi2_device;
diff --git a/drivers/staging/media/omap4iss/iss_ipipe.c b/drivers/staging/media/omap4iss/iss_ipipe.c
index dd0abeffd893..d38782e8e84c 100644
--- a/drivers/staging/media/omap4iss/iss_ipipe.c
+++ b/drivers/staging/media/omap4iss/iss_ipipe.c
@@ -447,8 +447,11 @@ static int ipipe_link_setup(struct media_entity *entity,
struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
struct iss_device *iss = to_iss_device(ipipe);
- switch (local->index | media_entity_type(remote->entity)) {
- case IPIPE_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ if (!is_media_entity_v4l2_subdev(remote->entity))
+ return -EINVAL;
+
+ switch (local->index) {
+ case IPIPE_PAD_SINK:
/* Read from IPIPEIF. */
if (!(flags & MEDIA_LNK_FL_ENABLED)) {
ipipe->input = IPIPE_INPUT_NONE;
@@ -463,7 +466,7 @@ static int ipipe_link_setup(struct media_entity *entity,
break;
- case IPIPE_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV:
+ case IPIPE_PAD_SOURCE_VP:
/* Send to RESIZER */
if (flags & MEDIA_LNK_FL_ENABLED) {
if (ipipe->output & ~IPIPE_OUTPUT_VP)
@@ -513,7 +516,7 @@ static int ipipe_init_entities(struct iss_ipipe_device *ipipe)
pads[IPIPE_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE;
me->ops = &ipipe_media_ops;
- ret = media_entity_init(me, IPIPE_PADS_NUM, pads, 0);
+ ret = media_entity_pads_init(me, IPIPE_PADS_NUM, pads);
if (ret < 0)
return ret;
diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c
index 5f9e449e7007..23de8330731d 100644
--- a/drivers/staging/media/omap4iss/iss_ipipeif.c
+++ b/drivers/staging/media/omap4iss/iss_ipipeif.c
@@ -662,9 +662,14 @@ static int ipipeif_link_setup(struct media_entity *entity,
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
struct iss_device *iss = to_iss_device(ipipeif);
+ unsigned int index = local->index;
- switch (local->index | media_entity_type(remote->entity)) {
- case IPIPEIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* FIXME: this is actually a hack! */
+ if (is_media_entity_v4l2_subdev(remote->entity))
+ index |= 2 << 16;
+
+ switch (index) {
+ case IPIPEIF_PAD_SINK | 2 << 16:
/* Read from the sensor CSI2a or CSI2b. */
if (!(flags & MEDIA_LNK_FL_ENABLED)) {
ipipeif->input = IPIPEIF_INPUT_NONE;
@@ -681,7 +686,7 @@ static int ipipeif_link_setup(struct media_entity *entity,
break;
- case IPIPEIF_PAD_SOURCE_ISIF_SF | MEDIA_ENT_T_DEVNODE:
+ case IPIPEIF_PAD_SOURCE_ISIF_SF:
/* Write to memory */
if (flags & MEDIA_LNK_FL_ENABLED) {
if (ipipeif->output & ~IPIPEIF_OUTPUT_MEMORY)
@@ -692,7 +697,7 @@ static int ipipeif_link_setup(struct media_entity *entity,
}
break;
- case IPIPEIF_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV:
+ case IPIPEIF_PAD_SOURCE_VP | 2 << 16:
/* Send to IPIPE/RESIZER */
if (flags & MEDIA_LNK_FL_ENABLED) {
if (ipipeif->output & ~IPIPEIF_OUTPUT_VP)
@@ -743,7 +748,7 @@ static int ipipeif_init_entities(struct iss_ipipeif_device *ipipeif)
pads[IPIPEIF_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE;
me->ops = &ipipeif_media_ops;
- ret = media_entity_init(me, IPIPEIF_PADS_NUM, pads, 0);
+ ret = media_entity_pads_init(me, IPIPEIF_PADS_NUM, pads);
if (ret < 0)
return ret;
@@ -757,18 +762,7 @@ static int ipipeif_init_entities(struct iss_ipipeif_device *ipipeif)
ipipeif->video_out.bpl_zero_padding = 1;
ipipeif->video_out.bpl_max = 0x1ffe0;
- ret = omap4iss_video_init(&ipipeif->video_out, "ISP IPIPEIF");
- if (ret < 0)
- return ret;
-
- /* Connect the IPIPEIF subdev to the video node. */
- ret = media_entity_create_link(&ipipeif->subdev.entity,
- IPIPEIF_PAD_SOURCE_ISIF_SF,
- &ipipeif->video_out.video.entity, 0, 0);
- if (ret < 0)
- return ret;
-
- return 0;
+ return omap4iss_video_init(&ipipeif->video_out, "ISP IPIPEIF");
}
void omap4iss_ipipeif_unregister_entities(struct iss_ipipeif_device *ipipeif)
@@ -821,6 +815,22 @@ int omap4iss_ipipeif_init(struct iss_device *iss)
}
/*
+ * omap4iss_ipipeif_create_links() - IPIPEIF pads links creation
+ * @iss: Pointer to ISS device
+ *
+ * return negative error code or zero on success
+ */
+int omap4iss_ipipeif_create_links(struct iss_device *iss)
+{
+ struct iss_ipipeif_device *ipipeif = &iss->ipipeif;
+
+ /* Connect the IPIPEIF subdev to the video node. */
+ return media_create_pad_link(&ipipeif->subdev.entity,
+ IPIPEIF_PAD_SOURCE_ISIF_SF,
+ &ipipeif->video_out.video.entity, 0, 0);
+}
+
+/*
* omap4iss_ipipeif_cleanup - IPIPEIF module cleanup.
* @iss: Device pointer specific to the OMAP4 ISS.
*/
diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.h b/drivers/staging/media/omap4iss/iss_ipipeif.h
index c6bd96d9656c..bad32b1d6ad8 100644
--- a/drivers/staging/media/omap4iss/iss_ipipeif.h
+++ b/drivers/staging/media/omap4iss/iss_ipipeif.h
@@ -78,6 +78,7 @@ struct iss_ipipeif_device {
struct iss_device;
int omap4iss_ipipeif_init(struct iss_device *iss);
+int omap4iss_ipipeif_create_links(struct iss_device *iss);
void omap4iss_ipipeif_cleanup(struct iss_device *iss);
int omap4iss_ipipeif_register_entities(struct iss_ipipeif_device *ipipeif,
struct v4l2_device *vdev);
diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c
index 9c8180bba77e..f1d352c711d5 100644
--- a/drivers/staging/media/omap4iss/iss_resizer.c
+++ b/drivers/staging/media/omap4iss/iss_resizer.c
@@ -158,8 +158,8 @@ static void resizer_set_outaddr(struct iss_resizer_device *resizer, u32 addr)
/* Program UV buffer address... Hardcoded to be contiguous! */
if ((informat->code == MEDIA_BUS_FMT_UYVY8_1X16) &&
(outformat->code == MEDIA_BUS_FMT_YUYV8_1_5X8)) {
- u32 c_addr = addr + (resizer->video_out.bpl_value *
- (outformat->height - 1));
+ u32 c_addr = addr + resizer->video_out.bpl_value
+ * outformat->height;
/* Ensure Y_BAD_L[6:0] = C_BAD_L[6:0]*/
if ((c_addr ^ addr) & 0x7f) {
@@ -716,9 +716,14 @@ static int resizer_link_setup(struct media_entity *entity,
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
struct iss_device *iss = to_iss_device(resizer);
+ unsigned int index = local->index;
- switch (local->index | media_entity_type(remote->entity)) {
- case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* FIXME: this is actually a hack! */
+ if (is_media_entity_v4l2_subdev(remote->entity))
+ index |= 2 << 16;
+
+ switch (index) {
+ case RESIZER_PAD_SINK | 2 << 16:
/* Read from IPIPE or IPIPEIF. */
if (!(flags & MEDIA_LNK_FL_ENABLED)) {
resizer->input = RESIZER_INPUT_NONE;
@@ -735,7 +740,7 @@ static int resizer_link_setup(struct media_entity *entity,
break;
- case RESIZER_PAD_SOURCE_MEM | MEDIA_ENT_T_DEVNODE:
+ case RESIZER_PAD_SOURCE_MEM:
/* Write to memory */
if (flags & MEDIA_LNK_FL_ENABLED) {
if (resizer->output & ~RESIZER_OUTPUT_MEMORY)
@@ -785,7 +790,7 @@ static int resizer_init_entities(struct iss_resizer_device *resizer)
pads[RESIZER_PAD_SOURCE_MEM].flags = MEDIA_PAD_FL_SOURCE;
me->ops = &resizer_media_ops;
- ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0);
+ ret = media_entity_pads_init(me, RESIZER_PADS_NUM, pads);
if (ret < 0)
return ret;
@@ -799,18 +804,7 @@ static int resizer_init_entities(struct iss_resizer_device *resizer)
resizer->video_out.bpl_zero_padding = 1;
resizer->video_out.bpl_max = 0x1ffe0;
- ret = omap4iss_video_init(&resizer->video_out, "ISP resizer a");
- if (ret < 0)
- return ret;
-
- /* Connect the RESIZER subdev to the video node. */
- ret = media_entity_create_link(&resizer->subdev.entity,
- RESIZER_PAD_SOURCE_MEM,
- &resizer->video_out.video.entity, 0, 0);
- if (ret < 0)
- return ret;
-
- return 0;
+ return omap4iss_video_init(&resizer->video_out, "ISP resizer a");
}
void omap4iss_resizer_unregister_entities(struct iss_resizer_device *resizer)
@@ -863,6 +857,22 @@ int omap4iss_resizer_init(struct iss_device *iss)
}
/*
+ * omap4iss_resizer_create_links() - RESIZER pads links creation
+ * @iss: Pointer to ISS device
+ *
+ * return negative error code or zero on success
+ */
+int omap4iss_resizer_create_links(struct iss_device *iss)
+{
+ struct iss_resizer_device *resizer = &iss->resizer;
+
+ /* Connect the RESIZER subdev to the video node. */
+ return media_create_pad_link(&resizer->subdev.entity,
+ RESIZER_PAD_SOURCE_MEM,
+ &resizer->video_out.video.entity, 0, 0);
+}
+
+/*
* omap4iss_resizer_cleanup - RESIZER module cleanup.
* @iss: Device pointer specific to the OMAP4 ISS.
*/
diff --git a/drivers/staging/media/omap4iss/iss_resizer.h b/drivers/staging/media/omap4iss/iss_resizer.h
index 1e145abafc65..8b7c5fe9ffed 100644
--- a/drivers/staging/media/omap4iss/iss_resizer.h
+++ b/drivers/staging/media/omap4iss/iss_resizer.h
@@ -61,6 +61,7 @@ struct iss_resizer_device {
struct iss_device;
int omap4iss_resizer_init(struct iss_device *iss);
+int omap4iss_resizer_create_links(struct iss_device *iss);
void omap4iss_resizer_cleanup(struct iss_device *iss);
int omap4iss_resizer_register_entities(struct iss_resizer_device *resizer,
struct v4l2_device *vdev);
diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c
index 2a0158bb4974..058233a9de67 100644
--- a/drivers/staging/media/omap4iss/iss_video.c
+++ b/drivers/staging/media/omap4iss/iss_video.c
@@ -190,8 +190,7 @@ iss_video_remote_subdev(struct iss_video *video, u32 *pad)
remote = media_entity_remote_pad(&video->pad);
- if (!remote ||
- media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ if (!remote || !is_media_entity_v4l2_subdev(remote->entity))
return NULL;
if (pad)
@@ -206,17 +205,23 @@ iss_video_far_end(struct iss_video *video)
{
struct media_entity_graph graph;
struct media_entity *entity = &video->video.entity;
- struct media_device *mdev = entity->parent;
+ struct media_device *mdev = entity->graph_obj.mdev;
struct iss_video *far_end = NULL;
mutex_lock(&mdev->graph_mutex);
+
+ if (media_entity_graph_walk_init(&graph, mdev)) {
+ mutex_unlock(&mdev->graph_mutex);
+ return NULL;
+ }
+
media_entity_graph_walk_start(&graph, entity);
while ((entity = media_entity_graph_walk_next(&graph))) {
if (entity == &video->video.entity)
continue;
- if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+ if (!is_media_entity_v4l2_io(entity))
continue;
far_end = to_iss_video(media_entity_to_video_device(entity));
@@ -227,6 +232,9 @@ iss_video_far_end(struct iss_video *video)
}
mutex_unlock(&mdev->graph_mutex);
+
+ media_entity_graph_walk_cleanup(&graph);
+
return far_end;
}
@@ -287,7 +295,6 @@ iss_video_check_format(struct iss_video *video, struct iss_video_fh *vfh)
*/
static int iss_video_queue_setup(struct vb2_queue *vq,
- const void *parg,
unsigned int *count, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[])
{
@@ -434,7 +441,7 @@ struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video)
list_del(&buf->list);
spin_unlock_irqrestore(&video->qlock, flags);
- v4l2_get_timestamp(&buf->vb.timestamp);
+ buf->vb.vb2_buf.timestamp = ktime_get_ns();
/* Do frame number propagation only if this is the output video node.
* Frame number either comes from the CSI receivers or it gets
@@ -751,7 +758,7 @@ iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
struct iss_video_fh *vfh = to_iss_video_fh(fh);
struct iss_video *video = video_drvdata(file);
struct media_entity_graph graph;
- struct media_entity *entity;
+ struct media_entity *entity = &video->video.entity;
enum iss_pipeline_state state;
struct iss_pipeline *pipe;
struct iss_video *far_end;
@@ -766,24 +773,30 @@ iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
/* Start streaming on the pipeline. No link touching an entity in the
* pipeline can be activated or deactivated once streaming is started.
*/
- pipe = video->video.entity.pipe
- ? to_iss_pipeline(&video->video.entity) : &video->pipe;
+ pipe = entity->pipe
+ ? to_iss_pipeline(entity) : &video->pipe;
pipe->external = NULL;
pipe->external_rate = 0;
pipe->external_bpp = 0;
- pipe->entities = 0;
+
+ ret = media_entity_enum_init(&pipe->ent_enum, entity->graph_obj.mdev);
+ if (ret)
+ goto err_graph_walk_init;
+
+ ret = media_entity_graph_walk_init(&graph, entity->graph_obj.mdev);
+ if (ret)
+ goto err_graph_walk_init;
if (video->iss->pdata->set_constraints)
video->iss->pdata->set_constraints(video->iss, true);
- ret = media_entity_pipeline_start(&video->video.entity, &pipe->pipe);
+ ret = media_entity_pipeline_start(entity, &pipe->pipe);
if (ret < 0)
goto err_media_entity_pipeline_start;
- entity = &video->video.entity;
media_entity_graph_walk_start(&graph, entity);
while ((entity = media_entity_graph_walk_next(&graph)))
- pipe->entities |= 1 << entity->id;
+ media_entity_enum_set(&pipe->ent_enum, entity);
/* Verify that the currently configured format matches the output of
* the connected subdev.
@@ -853,7 +866,10 @@ iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
spin_unlock_irqrestore(&video->qlock, flags);
}
+ media_entity_graph_walk_cleanup(&graph);
+
mutex_unlock(&video->stream_lock);
+
return 0;
err_omap4iss_set_stream:
@@ -865,7 +881,13 @@ err_media_entity_pipeline_start:
video->iss->pdata->set_constraints(video->iss, false);
video->queue = NULL;
+ media_entity_graph_walk_cleanup(&graph);
+
+err_graph_walk_init:
+ media_entity_enum_cleanup(&pipe->ent_enum);
+
mutex_unlock(&video->stream_lock);
+
return ret;
}
@@ -903,6 +925,8 @@ iss_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
vb2_streamoff(&vfh->queue, type);
video->queue = NULL;
+ media_entity_enum_cleanup(&pipe->ent_enum);
+
if (video->iss->pdata->set_constraints)
video->iss->pdata->set_constraints(video->iss, false);
media_entity_pipeline_stop(&video->video.entity);
@@ -985,7 +1009,13 @@ static int iss_video_open(struct file *file)
goto done;
}
- ret = omap4iss_pipeline_pm_use(&video->video.entity, 1);
+ ret = media_entity_graph_walk_init(&handle->graph,
+ &video->iss->media_dev);
+ if (ret)
+ goto done;
+
+ ret = omap4iss_pipeline_pm_use(&video->video.entity, 1,
+ &handle->graph);
if (ret < 0) {
omap4iss_put(video->iss);
goto done;
@@ -1024,6 +1054,7 @@ static int iss_video_open(struct file *file)
done:
if (ret < 0) {
v4l2_fh_del(&handle->vfh);
+ media_entity_graph_walk_cleanup(&handle->graph);
kfree(handle);
}
@@ -1039,12 +1070,13 @@ static int iss_video_release(struct file *file)
/* Disable streaming and free the buffers queue resources. */
iss_video_streamoff(file, vfh, video->type);
- omap4iss_pipeline_pm_use(&video->video.entity, 0);
+ omap4iss_pipeline_pm_use(&video->video.entity, 0, &handle->graph);
/* Release the videobuf2 queue */
vb2_queue_release(&handle->queue);
/* Release the file handle. */
+ media_entity_graph_walk_cleanup(&handle->graph);
v4l2_fh_del(vfh);
kfree(handle);
file->private_data = NULL;
@@ -1103,7 +1135,7 @@ int omap4iss_video_init(struct iss_video *video, const char *name)
return -EINVAL;
}
- ret = media_entity_init(&video->video.entity, 1, &video->pad, 0);
+ ret = media_entity_pads_init(&video->video.entity, 1, &video->pad);
if (ret < 0)
return ret;
diff --git a/drivers/staging/media/omap4iss/iss_video.h b/drivers/staging/media/omap4iss/iss_video.h
index 41532eda1277..34588b7176ca 100644
--- a/drivers/staging/media/omap4iss/iss_video.h
+++ b/drivers/staging/media/omap4iss/iss_video.h
@@ -77,7 +77,7 @@ enum iss_pipeline_state {
/*
* struct iss_pipeline - An OMAP4 ISS hardware pipeline
- * @entities: Bitmask of entities in the pipeline (indexed by entity ID)
+ * @ent_enum: Entities in the pipeline
* @error: A hardware error occurred during capture
*/
struct iss_pipeline {
@@ -87,7 +87,7 @@ struct iss_pipeline {
enum iss_pipeline_stream_state stream_state;
struct iss_video *input;
struct iss_video *output;
- unsigned int entities;
+ struct media_entity_enum ent_enum;
atomic_t frame_number;
bool do_propagation; /* of frame number */
bool error;
@@ -183,6 +183,7 @@ struct iss_video_fh {
struct vb2_queue queue;
struct v4l2_format format;
struct v4l2_fract timeperframe;
+ struct media_entity_graph graph;
};
#define to_iss_video_fh(fh) container_of(fh, struct iss_video_fh, vfh)
diff --git a/drivers/staging/most/aim-network/networking.h b/drivers/staging/most/aim-network/networking.h
index 1b8b434fabb0..6f346d410525 100644
--- a/drivers/staging/most/aim-network/networking.h
+++ b/drivers/staging/most/aim-network/networking.h
@@ -15,9 +15,7 @@
#include "mostcore.h"
-
void most_deliver_netinfo(struct most_interface *iface,
unsigned char link_stat, unsigned char *mac_addr);
-
#endif
diff --git a/drivers/staging/most/hdm-dim2/dim2_errors.h b/drivers/staging/most/hdm-dim2/dim2_errors.h
index 314f7de2be73..5a713df1d1d4 100644
--- a/drivers/staging/most/hdm-dim2/dim2_errors.h
+++ b/drivers/staging/most/hdm-dim2/dim2_errors.h
@@ -19,7 +19,6 @@
extern "C" {
#endif
-
/**
* MOST DIM errors.
*/
@@ -59,7 +58,6 @@ enum dim_errors_t {
DIM_ERR_OVERFLOW,
};
-
#ifdef __cplusplus
}
#endif
diff --git a/drivers/staging/most/hdm-dim2/dim2_hal.c b/drivers/staging/most/hdm-dim2/dim2_hal.c
index c915c44f025e..172257596f1f 100644
--- a/drivers/staging/most/hdm-dim2/dim2_hal.c
+++ b/drivers/staging/most/hdm-dim2/dim2_hal.c
@@ -74,7 +74,7 @@ static inline u32 bit_mask(u8 position)
static inline bool dim_on_error(u8 error_id, const char *error_message)
{
- DIMCB_OnError(error_id, error_message);
+ dimcb_on_error(error_id, error_message);
return false;
}
@@ -151,44 +151,44 @@ static void free_dbr(int offs, int size)
static u32 dim2_read_ctr(u32 ctr_addr, u16 mdat_idx)
{
- DIMCB_IoWrite(&g.dim2->MADR, ctr_addr);
+ dimcb_io_write(&g.dim2->MADR, ctr_addr);
/* wait till transfer is completed */
- while ((DIMCB_IoRead(&g.dim2->MCTL) & 1) != 1)
+ while ((dimcb_io_read(&g.dim2->MCTL) & 1) != 1)
continue;
- DIMCB_IoWrite(&g.dim2->MCTL, 0); /* clear transfer complete */
+ dimcb_io_write(&g.dim2->MCTL, 0); /* clear transfer complete */
- return DIMCB_IoRead((&g.dim2->MDAT0) + mdat_idx);
+ return dimcb_io_read((&g.dim2->MDAT0) + mdat_idx);
}
static void dim2_write_ctr_mask(u32 ctr_addr, const u32 *mask, const u32 *value)
{
enum { MADR_WNR_BIT = 31 };
- DIMCB_IoWrite(&g.dim2->MCTL, 0); /* clear transfer complete */
+ dimcb_io_write(&g.dim2->MCTL, 0); /* clear transfer complete */
if (mask[0] != 0)
- DIMCB_IoWrite(&g.dim2->MDAT0, value[0]);
+ dimcb_io_write(&g.dim2->MDAT0, value[0]);
if (mask[1] != 0)
- DIMCB_IoWrite(&g.dim2->MDAT1, value[1]);
+ dimcb_io_write(&g.dim2->MDAT1, value[1]);
if (mask[2] != 0)
- DIMCB_IoWrite(&g.dim2->MDAT2, value[2]);
+ dimcb_io_write(&g.dim2->MDAT2, value[2]);
if (mask[3] != 0)
- DIMCB_IoWrite(&g.dim2->MDAT3, value[3]);
+ dimcb_io_write(&g.dim2->MDAT3, value[3]);
- DIMCB_IoWrite(&g.dim2->MDWE0, mask[0]);
- DIMCB_IoWrite(&g.dim2->MDWE1, mask[1]);
- DIMCB_IoWrite(&g.dim2->MDWE2, mask[2]);
- DIMCB_IoWrite(&g.dim2->MDWE3, mask[3]);
+ dimcb_io_write(&g.dim2->MDWE0, mask[0]);
+ dimcb_io_write(&g.dim2->MDWE1, mask[1]);
+ dimcb_io_write(&g.dim2->MDWE2, mask[2]);
+ dimcb_io_write(&g.dim2->MDWE3, mask[3]);
- DIMCB_IoWrite(&g.dim2->MADR, bit_mask(MADR_WNR_BIT) | ctr_addr);
+ dimcb_io_write(&g.dim2->MADR, bit_mask(MADR_WNR_BIT) | ctr_addr);
/* wait till transfer is completed */
- while ((DIMCB_IoRead(&g.dim2->MCTL) & 1) != 1)
+ while ((dimcb_io_read(&g.dim2->MCTL) & 1) != 1)
continue;
- DIMCB_IoWrite(&g.dim2->MCTL, 0); /* clear transfer complete */
+ dimcb_io_write(&g.dim2->MCTL, 0); /* clear transfer complete */
}
static inline void dim2_write_ctr(u32 ctr_addr, const u32 *value)
@@ -341,15 +341,15 @@ static void dim2_configure_channel(
dim2_configure_cat(AHB_CAT, ch_addr, type, is_tx ? 0 : 1, sync_mfe);
/* unmask interrupt for used channel, enable mlb_sys_int[0] interrupt */
- DIMCB_IoWrite(&g.dim2->ACMR0,
- DIMCB_IoRead(&g.dim2->ACMR0) | bit_mask(ch_addr));
+ dimcb_io_write(&g.dim2->ACMR0,
+ dimcb_io_read(&g.dim2->ACMR0) | bit_mask(ch_addr));
}
static void dim2_clear_channel(u8 ch_addr)
{
/* mask interrupt for used channel, disable mlb_sys_int[0] interrupt */
- DIMCB_IoWrite(&g.dim2->ACMR0,
- DIMCB_IoRead(&g.dim2->ACMR0) & ~bit_mask(ch_addr));
+ dimcb_io_write(&g.dim2->ACMR0,
+ dimcb_io_read(&g.dim2->ACMR0) & ~bit_mask(ch_addr));
dim2_clear_cat(AHB_CAT, ch_addr);
dim2_clear_adt(ch_addr);
@@ -455,20 +455,20 @@ static inline u16 norm_sync_buffer_size(u16 buf_size, u16 bytes_per_frame)
static void dim2_cleanup(void)
{
/* disable MediaLB */
- DIMCB_IoWrite(&g.dim2->MLBC0, false << MLBC0_MLBEN_BIT);
+ dimcb_io_write(&g.dim2->MLBC0, false << MLBC0_MLBEN_BIT);
dim2_clear_ctram();
/* disable mlb_int interrupt */
- DIMCB_IoWrite(&g.dim2->MIEN, 0);
+ dimcb_io_write(&g.dim2->MIEN, 0);
/* clear status for all dma channels */
- DIMCB_IoWrite(&g.dim2->ACSR0, 0xFFFFFFFF);
- DIMCB_IoWrite(&g.dim2->ACSR1, 0xFFFFFFFF);
+ dimcb_io_write(&g.dim2->ACSR0, 0xFFFFFFFF);
+ dimcb_io_write(&g.dim2->ACSR1, 0xFFFFFFFF);
/* mask interrupts for all channels */
- DIMCB_IoWrite(&g.dim2->ACMR0, 0);
- DIMCB_IoWrite(&g.dim2->ACMR1, 0);
+ dimcb_io_write(&g.dim2->ACMR0, 0);
+ dimcb_io_write(&g.dim2->ACMR1, 0);
}
static void dim2_initialize(bool enable_6pin, u8 mlb_clock)
@@ -476,23 +476,23 @@ static void dim2_initialize(bool enable_6pin, u8 mlb_clock)
dim2_cleanup();
/* configure and enable MediaLB */
- DIMCB_IoWrite(&g.dim2->MLBC0,
- enable_6pin << MLBC0_MLBPEN_BIT |
- mlb_clock << MLBC0_MLBCLK_SHIFT |
- MLBC0_FCNT_VAL(FRAMES_PER_SUBBUFF) << MLBC0_FCNT_SHIFT |
- true << MLBC0_MLBEN_BIT);
+ dimcb_io_write(&g.dim2->MLBC0,
+ enable_6pin << MLBC0_MLBPEN_BIT |
+ mlb_clock << MLBC0_MLBCLK_SHIFT |
+ MLBC0_FCNT_VAL(FRAMES_PER_SUBBUFF) << MLBC0_FCNT_SHIFT |
+ true << MLBC0_MLBEN_BIT);
/* activate all HBI channels */
- DIMCB_IoWrite(&g.dim2->HCMR0, 0xFFFFFFFF);
- DIMCB_IoWrite(&g.dim2->HCMR1, 0xFFFFFFFF);
+ dimcb_io_write(&g.dim2->HCMR0, 0xFFFFFFFF);
+ dimcb_io_write(&g.dim2->HCMR1, 0xFFFFFFFF);
/* enable HBI */
- DIMCB_IoWrite(&g.dim2->HCTL, bit_mask(HCTL_EN_BIT));
+ dimcb_io_write(&g.dim2->HCTL, bit_mask(HCTL_EN_BIT));
/* configure DMA */
- DIMCB_IoWrite(&g.dim2->ACTL,
- ACTL_DMA_MODE_VAL_DMA_MODE_1 << ACTL_DMA_MODE_BIT |
- true << ACTL_SCE_BIT);
+ dimcb_io_write(&g.dim2->ACTL,
+ ACTL_DMA_MODE_VAL_DMA_MODE_1 << ACTL_DMA_MODE_BIT |
+ true << ACTL_SCE_BIT);
}
static bool dim2_is_mlb_locked(void)
@@ -500,12 +500,12 @@ static bool dim2_is_mlb_locked(void)
u32 const mask0 = bit_mask(MLBC0_MLBLK_BIT);
u32 const mask1 = bit_mask(MLBC1_CLKMERR_BIT) |
bit_mask(MLBC1_LOCKERR_BIT);
- u32 const c1 = DIMCB_IoRead(&g.dim2->MLBC1);
+ u32 const c1 = dimcb_io_read(&g.dim2->MLBC1);
u32 const nda_mask = (u32)MLBC1_NDA_MASK << MLBC1_NDA_SHIFT;
- DIMCB_IoWrite(&g.dim2->MLBC1, c1 & nda_mask);
- return (DIMCB_IoRead(&g.dim2->MLBC1) & mask1) == 0 &&
- (DIMCB_IoRead(&g.dim2->MLBC0) & mask0) != 0;
+ dimcb_io_write(&g.dim2->MLBC1, c1 & nda_mask);
+ return (dimcb_io_read(&g.dim2->MLBC1) & mask1) == 0 &&
+ (dimcb_io_read(&g.dim2->MLBC0) & mask0) != 0;
}
/* -------------------------------------------------------------------------- */
@@ -531,7 +531,7 @@ static inline bool service_channel(u8 ch_addr, u8 idx)
}
/* clear channel status bit */
- DIMCB_IoWrite(&g.dim2->ACSR0, bit_mask(ch_addr));
+ dimcb_io_write(&g.dim2->ACSR0, bit_mask(ch_addr));
return true;
}
@@ -650,7 +650,7 @@ static bool channel_detach_buffers(struct dim_channel *ch, u16 buffers_number)
/* -------------------------------------------------------------------------- */
/* API */
-u8 DIM_Startup(void *dim_base_address, u32 mlb_clock)
+u8 dim_startup(void *dim_base_address, u32 mlb_clock)
{
g.dim_is_initialized = false;
@@ -673,13 +673,13 @@ u8 DIM_Startup(void *dim_base_address, u32 mlb_clock)
return DIM_NO_ERROR;
}
-void DIM_Shutdown(void)
+void dim_shutdown(void)
{
g.dim_is_initialized = false;
dim2_cleanup();
}
-bool DIM_GetLockState(void)
+bool dim_get_lock_state(void)
{
return dim2_is_mlb_locked();
}
@@ -706,7 +706,7 @@ static u8 init_ctrl_async(struct dim_channel *ch, u8 type, u8 is_tx,
return DIM_NO_ERROR;
}
-u16 DIM_NormCtrlAsyncBufferSize(u16 buf_size)
+u16 dim_norm_ctrl_async_buffer_size(u16 buf_size)
{
return norm_ctrl_async_buffer_size(buf_size);
}
@@ -717,7 +717,7 @@ u16 DIM_NormCtrlAsyncBufferSize(u16 buf_size)
*
* Returns non-zero correct buffer size or zero by error.
*/
-u16 DIM_NormIsocBufferSize(u16 buf_size, u16 packet_length)
+u16 dim_norm_isoc_buffer_size(u16 buf_size, u16 packet_length)
{
if (!check_packet_length(packet_length))
return 0;
@@ -731,7 +731,7 @@ u16 DIM_NormIsocBufferSize(u16 buf_size, u16 packet_length)
*
* Returns non-zero correct buffer size or zero by error.
*/
-u16 DIM_NormSyncBufferSize(u16 buf_size, u16 bytes_per_frame)
+u16 dim_norm_sync_buffer_size(u16 buf_size, u16 bytes_per_frame)
{
if (!check_bytes_per_frame(bytes_per_frame))
return 0;
@@ -739,22 +739,22 @@ u16 DIM_NormSyncBufferSize(u16 buf_size, u16 bytes_per_frame)
return norm_sync_buffer_size(buf_size, bytes_per_frame);
}
-u8 DIM_InitControl(struct dim_channel *ch, u8 is_tx, u16 ch_address,
- u16 max_buffer_size)
+u8 dim_init_control(struct dim_channel *ch, u8 is_tx, u16 ch_address,
+ u16 max_buffer_size)
{
return init_ctrl_async(ch, CAT_CT_VAL_CONTROL, is_tx, ch_address,
max_buffer_size);
}
-u8 DIM_InitAsync(struct dim_channel *ch, u8 is_tx, u16 ch_address,
- u16 max_buffer_size)
+u8 dim_init_async(struct dim_channel *ch, u8 is_tx, u16 ch_address,
+ u16 max_buffer_size)
{
return init_ctrl_async(ch, CAT_CT_VAL_ASYNC, is_tx, ch_address,
max_buffer_size);
}
-u8 DIM_InitIsoc(struct dim_channel *ch, u8 is_tx, u16 ch_address,
- u16 packet_length)
+u8 dim_init_isoc(struct dim_channel *ch, u8 is_tx, u16 ch_address,
+ u16 packet_length)
{
if (!g.dim_is_initialized || !ch)
return DIM_ERR_DRIVER_NOT_INITIALIZED;
@@ -778,8 +778,8 @@ u8 DIM_InitIsoc(struct dim_channel *ch, u8 is_tx, u16 ch_address,
return DIM_NO_ERROR;
}
-u8 DIM_InitSync(struct dim_channel *ch, u8 is_tx, u16 ch_address,
- u16 bytes_per_frame)
+u8 dim_init_sync(struct dim_channel *ch, u8 is_tx, u16 ch_address,
+ u16 bytes_per_frame)
{
if (!g.dim_is_initialized || !ch)
return DIM_ERR_DRIVER_NOT_INITIALIZED;
@@ -803,7 +803,7 @@ u8 DIM_InitSync(struct dim_channel *ch, u8 is_tx, u16 ch_address,
return DIM_NO_ERROR;
}
-u8 DIM_DestroyChannel(struct dim_channel *ch)
+u8 dim_destroy_channel(struct dim_channel *ch)
{
if (!g.dim_is_initialized || !ch)
return DIM_ERR_DRIVER_NOT_INITIALIZED;
@@ -816,7 +816,7 @@ u8 DIM_DestroyChannel(struct dim_channel *ch)
return DIM_NO_ERROR;
}
-void DIM_ServiceIrq(struct dim_channel *const *channels)
+void dim_service_irq(struct dim_channel *const *channels)
{
bool state_changed;
@@ -850,11 +850,11 @@ void DIM_ServiceIrq(struct dim_channel *const *channels)
} while (state_changed);
/* clear pending Interrupts */
- DIMCB_IoWrite(&g.dim2->MS0, 0);
- DIMCB_IoWrite(&g.dim2->MS1, 0);
+ dimcb_io_write(&g.dim2->MS0, 0);
+ dimcb_io_write(&g.dim2->MS1, 0);
}
-u8 DIM_ServiceChannel(struct dim_channel *ch)
+u8 dim_service_channel(struct dim_channel *ch)
{
if (!g.dim_is_initialized || !ch)
return DIM_ERR_DRIVER_NOT_INITIALIZED;
@@ -862,8 +862,8 @@ u8 DIM_ServiceChannel(struct dim_channel *ch)
return channel_service(ch);
}
-struct dim_ch_state_t *DIM_GetChannelState(struct dim_channel *ch,
- struct dim_ch_state_t *state_ptr)
+struct dim_ch_state_t *dim_get_channel_state(struct dim_channel *ch,
+ struct dim_ch_state_t *state_ptr)
{
if (!ch || !state_ptr)
return NULL;
@@ -874,7 +874,8 @@ struct dim_ch_state_t *DIM_GetChannelState(struct dim_channel *ch,
return state_ptr;
}
-bool DIM_EnqueueBuffer(struct dim_channel *ch, u32 buffer_addr, u16 buffer_size)
+bool dim_enqueue_buffer(struct dim_channel *ch, u32 buffer_addr,
+ u16 buffer_size)
{
if (!ch)
return dim_on_error(DIM_ERR_DRIVER_NOT_INITIALIZED,
@@ -883,7 +884,7 @@ bool DIM_EnqueueBuffer(struct dim_channel *ch, u32 buffer_addr, u16 buffer_size)
return channel_start(ch, buffer_addr, buffer_size);
}
-bool DIM_DetachBuffers(struct dim_channel *ch, u16 buffers_number)
+bool dim_detach_buffers(struct dim_channel *ch, u16 buffers_number)
{
if (!ch)
return dim_on_error(DIM_ERR_DRIVER_NOT_INITIALIZED,
diff --git a/drivers/staging/most/hdm-dim2/dim2_hal.h b/drivers/staging/most/hdm-dim2/dim2_hal.h
index ebb7d87a45fc..48cdd9c8cde1 100644
--- a/drivers/staging/most/hdm-dim2/dim2_hal.h
+++ b/drivers/staging/most/hdm-dim2/dim2_hal.h
@@ -17,7 +17,6 @@
#include <linux/types.h>
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -66,51 +65,49 @@ struct dim_channel {
u16 done_sw_buffers_number; /*< Done software buffers number. */
};
+u8 dim_startup(void *dim_base_address, u32 mlb_clock);
-u8 DIM_Startup(void *dim_base_address, u32 mlb_clock);
-
-void DIM_Shutdown(void);
-
-bool DIM_GetLockState(void);
+void dim_shutdown(void);
-u16 DIM_NormCtrlAsyncBufferSize(u16 buf_size);
+bool dim_get_lock_state(void);
-u16 DIM_NormIsocBufferSize(u16 buf_size, u16 packet_length);
+u16 dim_norm_ctrl_async_buffer_size(u16 buf_size);
-u16 DIM_NormSyncBufferSize(u16 buf_size, u16 bytes_per_frame);
+u16 dim_norm_isoc_buffer_size(u16 buf_size, u16 packet_length);
-u8 DIM_InitControl(struct dim_channel *ch, u8 is_tx, u16 ch_address,
- u16 max_buffer_size);
+u16 dim_norm_sync_buffer_size(u16 buf_size, u16 bytes_per_frame);
-u8 DIM_InitAsync(struct dim_channel *ch, u8 is_tx, u16 ch_address,
- u16 max_buffer_size);
+u8 dim_init_control(struct dim_channel *ch, u8 is_tx, u16 ch_address,
+ u16 max_buffer_size);
-u8 DIM_InitIsoc(struct dim_channel *ch, u8 is_tx, u16 ch_address,
- u16 packet_length);
+u8 dim_init_async(struct dim_channel *ch, u8 is_tx, u16 ch_address,
+ u16 max_buffer_size);
-u8 DIM_InitSync(struct dim_channel *ch, u8 is_tx, u16 ch_address,
- u16 bytes_per_frame);
+u8 dim_init_isoc(struct dim_channel *ch, u8 is_tx, u16 ch_address,
+ u16 packet_length);
-u8 DIM_DestroyChannel(struct dim_channel *ch);
+u8 dim_init_sync(struct dim_channel *ch, u8 is_tx, u16 ch_address,
+ u16 bytes_per_frame);
-void DIM_ServiceIrq(struct dim_channel *const *channels);
+u8 dim_destroy_channel(struct dim_channel *ch);
-u8 DIM_ServiceChannel(struct dim_channel *ch);
+void dim_service_irq(struct dim_channel *const *channels);
-struct dim_ch_state_t *DIM_GetChannelState(struct dim_channel *ch,
- struct dim_ch_state_t *dim_ch_state_ptr);
+u8 dim_service_channel(struct dim_channel *ch);
-bool DIM_EnqueueBuffer(struct dim_channel *ch, u32 buffer_addr,
- u16 buffer_size);
+struct dim_ch_state_t *dim_get_channel_state(struct dim_channel *ch,
+ struct dim_ch_state_t *state_ptr);
-bool DIM_DetachBuffers(struct dim_channel *ch, u16 buffers_number);
+bool dim_enqueue_buffer(struct dim_channel *ch, u32 buffer_addr,
+ u16 buffer_size);
-u32 DIMCB_IoRead(u32 *ptr32);
+bool dim_detach_buffers(struct dim_channel *ch, u16 buffers_number);
-void DIMCB_IoWrite(u32 *ptr32, u32 value);
+u32 dimcb_io_read(u32 *ptr32);
-void DIMCB_OnError(u8 error_id, const char *error_message);
+void dimcb_io_write(u32 *ptr32, u32 value);
+void dimcb_on_error(u8 error_id, const char *error_message);
#ifdef __cplusplus
}
diff --git a/drivers/staging/most/hdm-dim2/dim2_hdm.c b/drivers/staging/most/hdm-dim2/dim2_hdm.c
index b6fe346f73b0..327d738c7194 100644
--- a/drivers/staging/most/hdm-dim2/dim2_hdm.c
+++ b/drivers/staging/most/hdm-dim2/dim2_hdm.c
@@ -73,8 +73,8 @@ struct hdm_channel {
char name[sizeof "caNNN"];
bool is_initialized;
struct dim_channel ch;
- struct list_head pending_list; /* before DIM_EnqueueBuffer() */
- struct list_head started_list; /* after DIM_EnqueueBuffer() */
+ struct list_head pending_list; /* before dim_enqueue_buffer() */
+ struct list_head started_list; /* after dim_enqueue_buffer() */
enum most_channel_direction direction;
enum most_channel_data_type data_type;
};
@@ -128,40 +128,40 @@ bool dim2_sysfs_get_state_cb(void)
unsigned long flags;
spin_lock_irqsave(&dim_lock, flags);
- state = DIM_GetLockState();
+ state = dim_get_lock_state();
spin_unlock_irqrestore(&dim_lock, flags);
return state;
}
/**
- * DIMCB_IoRead - callback from HAL to read an I/O register
+ * dimcb_io_read - callback from HAL to read an I/O register
* @ptr32: register address
*/
-u32 DIMCB_IoRead(u32 *ptr32)
+u32 dimcb_io_read(u32 *ptr32)
{
return __raw_readl(ptr32);
}
/**
- * DIMCB_IoWrite - callback from HAL to write value to an I/O register
+ * dimcb_io_write - callback from HAL to write value to an I/O register
* @ptr32: register address
* @value: value to write
*/
-void DIMCB_IoWrite(u32 *ptr32, u32 value)
+void dimcb_io_write(u32 *ptr32, u32 value)
{
__raw_writel(value, ptr32);
}
/**
- * DIMCB_OnError - callback from HAL to report miscommunication between
+ * dimcb_on_error - callback from HAL to report miscommunication between
* HDM and HAL
* @error_id: Error ID
* @error_message: Error message. Some text in a free format
*/
-void DIMCB_OnError(u8 error_id, const char *error_message)
+void dimcb_on_error(u8 error_id, const char *error_message)
{
- pr_err("DIMCB_OnError: error_id - %d, error_message - %s\n", error_id,
+ pr_err("dimcb_on_error: error_id - %d, error_message - %s\n", error_id,
error_message);
}
@@ -212,9 +212,9 @@ static int startup_dim(struct platform_device *pdev)
return ret;
}
- hal_ret = DIM_Startup(dev->io_base, dev->clk_speed);
+ hal_ret = dim_startup(dev->io_base, dev->clk_speed);
if (hal_ret != DIM_NO_ERROR) {
- pr_err("DIM_Startup failed: %d\n", hal_ret);
+ pr_err("dim_startup failed: %d\n", hal_ret);
if (pdata && pdata->destroy)
pdata->destroy(pdata);
return -ENODEV;
@@ -246,7 +246,7 @@ static int try_start_dim_transfer(struct hdm_channel *hdm_ch)
return -EAGAIN;
}
- if (!DIM_GetChannelState(&hdm_ch->ch, &st)->ready) {
+ if (!dim_get_channel_state(&hdm_ch->ch, &st)->ready) {
spin_unlock_irqrestore(&dim_lock, flags);
return -EAGAIN;
}
@@ -255,7 +255,7 @@ static int try_start_dim_transfer(struct hdm_channel *hdm_ch)
buf_size = mbo->buffer_length;
BUG_ON(mbo->bus_address == 0);
- if (!DIM_EnqueueBuffer(&hdm_ch->ch, mbo->bus_address, buf_size)) {
+ if (!dim_enqueue_buffer(&hdm_ch->ch, mbo->bus_address, buf_size)) {
list_del(head->next);
spin_unlock_irqrestore(&dim_lock, flags);
mbo->processed_length = 0;
@@ -340,13 +340,13 @@ static void service_done_flag(struct dim2_hdm *dev, int ch_idx)
spin_lock_irqsave(&dim_lock, flags);
- done_buffers = DIM_GetChannelState(&hdm_ch->ch, &st)->done_buffers;
+ done_buffers = dim_get_channel_state(&hdm_ch->ch, &st)->done_buffers;
if (!done_buffers) {
spin_unlock_irqrestore(&dim_lock, flags);
return;
}
- if (!DIM_DetachBuffers(&hdm_ch->ch, done_buffers)) {
+ if (!dim_detach_buffers(&hdm_ch->ch, done_buffers)) {
spin_unlock_irqrestore(&dim_lock, flags);
return;
}
@@ -371,7 +371,6 @@ static void service_done_flag(struct dim2_hdm *dev, int ch_idx)
if (hdm_ch->data_type == MOST_CH_ASYNC &&
hdm_ch->direction == MOST_CH_RX &&
PACKET_IS_NET_INFO(data)) {
-
retrieve_netinfo(dev, mbo);
spin_lock_irqsave(&dim_lock, flags);
@@ -380,7 +379,6 @@ static void service_done_flag(struct dim2_hdm *dev, int ch_idx)
} else {
if (hdm_ch->data_type == MOST_CH_CONTROL ||
hdm_ch->data_type == MOST_CH_ASYNC) {
-
u32 const data_size =
(u32)data[0] * 256 + data[1] + 2;
@@ -430,7 +428,7 @@ static void dim2_tasklet_fn(unsigned long data)
continue;
spin_lock_irqsave(&dim_lock, flags);
- DIM_ServiceChannel(&dev->hch[ch_idx].ch);
+ dim_service_channel(&dev->hch[ch_idx].ch);
spin_unlock_irqrestore(&dim_lock, flags);
service_done_flag(dev, ch_idx);
@@ -454,7 +452,7 @@ static irqreturn_t dim2_ahb_isr(int irq, void *_dev)
unsigned long flags;
spin_lock_irqsave(&dim_lock, flags);
- DIM_ServiceIrq(get_active_channels(dev, buffer));
+ dim_service_irq(get_active_channels(dev, buffer));
spin_unlock_irqrestore(&dim_lock, flags);
#if !defined(ENABLE_HDM_TEST)
@@ -536,7 +534,7 @@ static int configure_channel(struct most_interface *most_iface, int ch_idx,
switch (ccfg->data_type) {
case MOST_CH_CONTROL:
- new_size = DIM_NormCtrlAsyncBufferSize(buf_size);
+ new_size = dim_norm_ctrl_async_buffer_size(buf_size);
if (new_size == 0) {
pr_err("%s: too small buffer size\n", hdm_ch->name);
return -EINVAL;
@@ -546,11 +544,11 @@ static int configure_channel(struct most_interface *most_iface, int ch_idx,
pr_warn("%s: fixed buffer size (%d -> %d)\n",
hdm_ch->name, buf_size, new_size);
spin_lock_irqsave(&dim_lock, flags);
- hal_ret = DIM_InitControl(&hdm_ch->ch, is_tx, ch_addr,
- buf_size);
+ hal_ret = dim_init_control(&hdm_ch->ch, is_tx, ch_addr,
+ buf_size);
break;
case MOST_CH_ASYNC:
- new_size = DIM_NormCtrlAsyncBufferSize(buf_size);
+ new_size = dim_norm_ctrl_async_buffer_size(buf_size);
if (new_size == 0) {
pr_err("%s: too small buffer size\n", hdm_ch->name);
return -EINVAL;
@@ -560,10 +558,10 @@ static int configure_channel(struct most_interface *most_iface, int ch_idx,
pr_warn("%s: fixed buffer size (%d -> %d)\n",
hdm_ch->name, buf_size, new_size);
spin_lock_irqsave(&dim_lock, flags);
- hal_ret = DIM_InitAsync(&hdm_ch->ch, is_tx, ch_addr, buf_size);
+ hal_ret = dim_init_async(&hdm_ch->ch, is_tx, ch_addr, buf_size);
break;
case MOST_CH_ISOC_AVP:
- new_size = DIM_NormIsocBufferSize(buf_size, sub_size);
+ new_size = dim_norm_isoc_buffer_size(buf_size, sub_size);
if (new_size == 0) {
pr_err("%s: invalid sub-buffer size or too small buffer size\n",
hdm_ch->name);
@@ -574,10 +572,10 @@ static int configure_channel(struct most_interface *most_iface, int ch_idx,
pr_warn("%s: fixed buffer size (%d -> %d)\n",
hdm_ch->name, buf_size, new_size);
spin_lock_irqsave(&dim_lock, flags);
- hal_ret = DIM_InitIsoc(&hdm_ch->ch, is_tx, ch_addr, sub_size);
+ hal_ret = dim_init_isoc(&hdm_ch->ch, is_tx, ch_addr, sub_size);
break;
case MOST_CH_SYNC:
- new_size = DIM_NormSyncBufferSize(buf_size, sub_size);
+ new_size = dim_norm_sync_buffer_size(buf_size, sub_size);
if (new_size == 0) {
pr_err("%s: invalid sub-buffer size or too small buffer size\n",
hdm_ch->name);
@@ -588,7 +586,7 @@ static int configure_channel(struct most_interface *most_iface, int ch_idx,
pr_warn("%s: fixed buffer size (%d -> %d)\n",
hdm_ch->name, buf_size, new_size);
spin_lock_irqsave(&dim_lock, flags);
- hal_ret = DIM_InitSync(&hdm_ch->ch, is_tx, ch_addr, sub_size);
+ hal_ret = dim_init_sync(&hdm_ch->ch, is_tx, ch_addr, sub_size);
break;
default:
pr_err("%s: configure failed, bad channel type: %d\n",
@@ -708,7 +706,7 @@ static int poison_channel(struct most_interface *most_iface, int ch_idx)
return -EPERM;
spin_lock_irqsave(&dim_lock, flags);
- hal_ret = DIM_DestroyChannel(&hdm_ch->ch);
+ hal_ret = dim_destroy_channel(&hdm_ch->ch);
hdm_ch->is_initialized = false;
if (ch_idx == dev->atx_idx)
dev->atx_idx = -1;
@@ -885,7 +883,7 @@ static int dim2_remove(struct platform_device *pdev)
unsigned long flags;
spin_lock_irqsave(&dim_lock, flags);
- DIM_Shutdown();
+ dim_shutdown();
spin_unlock_irqrestore(&dim_lock, flags);
if (pdata && pdata->destroy)
diff --git a/drivers/staging/most/hdm-dim2/dim2_reg.h b/drivers/staging/most/hdm-dim2/dim2_reg.h
index 476f66f4c566..bcf6a79f6744 100644
--- a/drivers/staging/most/hdm-dim2/dim2_reg.h
+++ b/drivers/staging/most/hdm-dim2/dim2_reg.h
@@ -21,7 +21,6 @@
extern "C" {
#endif
-
struct dim2_regs {
/* 0x00 */ u32 MLBC0;
/* 0x01 */ u32 rsvd0[1];
@@ -67,8 +66,7 @@ struct dim2_regs {
/* 0xF7 */ u32 ACMR1;
};
-
-#define DIM2_MASK(n) (~((~(u32)0)<<(n)))
+#define DIM2_MASK(n) (~((~(u32)0) << (n)))
enum {
MLBC0_MLBLK_BIT = 7,
@@ -168,7 +166,6 @@ enum {
CAT_CL_MASK = DIM2_MASK(6)
};
-
#ifdef __cplusplus
}
#endif
diff --git a/drivers/staging/most/hdm-dim2/dim2_sysfs.h b/drivers/staging/most/hdm-dim2/dim2_sysfs.h
index e719691035b0..b71dd027ebc7 100644
--- a/drivers/staging/most/hdm-dim2/dim2_sysfs.h
+++ b/drivers/staging/most/hdm-dim2/dim2_sysfs.h
@@ -16,10 +16,8 @@
#ifndef DIM2_SYSFS_H
#define DIM2_SYSFS_H
-
#include <linux/kobject.h>
-
struct medialb_bus {
struct kobject kobj_group;
};
@@ -35,5 +33,4 @@ void dim2_sysfs_destroy(struct medialb_bus *bus);
*/
bool dim2_sysfs_get_state_cb(void);
-
#endif /* DIM2_SYSFS_H */
diff --git a/drivers/staging/most/mostcore/core.c b/drivers/staging/most/mostcore/core.c
index 19852ca7e59c..ed1ed25b6d1d 100644
--- a/drivers/staging/most/mostcore/core.c
+++ b/drivers/staging/most/mostcore/core.c
@@ -1587,8 +1587,7 @@ out:
return 0;
error:
- if (iface->mod)
- module_put(iface->mod);
+ module_put(iface->mod);
modref--;
mutex_unlock(&c->start_mutex);
return ret;
diff --git a/drivers/staging/most/mostcore/mostcore.h b/drivers/staging/most/mostcore/mostcore.h
index e148b324331a..bda3850d5435 100644
--- a/drivers/staging/most/mostcore/mostcore.h
+++ b/drivers/staging/most/mostcore/mostcore.h
@@ -60,7 +60,6 @@ enum most_channel_data_type {
MOST_CH_SYNC = 1 << 5,
};
-
enum mbo_status_flags {
/* MBO was processed successfully (data was send or received )*/
MBO_SUCCESS = 0,
@@ -317,5 +316,4 @@ int most_start_channel(struct most_interface *iface, int channel_idx,
int most_stop_channel(struct most_interface *iface, int channel_idx,
struct most_aim *);
-
#endif /* MOST_CORE_H_ */
diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.c b/drivers/staging/mt29f_spinand/mt29f_spinand.c
index 47bb56f1f8c0..197d1124733d 100644
--- a/drivers/staging/mt29f_spinand/mt29f_spinand.c
+++ b/drivers/staging/mt29f_spinand/mt29f_spinand.c
@@ -31,8 +31,8 @@
static inline struct spinand_state *mtd_to_state(struct mtd_info *mtd)
{
- struct nand_chip *chip = (struct nand_chip *)mtd->priv;
- struct spinand_info *info = (struct spinand_info *)chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct spinand_info *info = nand_get_controller_data(chip);
struct spinand_state *state = (struct spinand_state *)info->priv;
return state;
@@ -633,7 +633,7 @@ static int spinand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
u8 *p = buf;
int eccsize = chip->ecc.size;
int eccsteps = chip->ecc.steps;
- struct spinand_info *info = (struct spinand_info *)chip->priv;
+ struct spinand_info *info = nand_get_controller_data(chip);
enable_read_hw_ecc = 1;
@@ -679,7 +679,7 @@ static u8 spinand_read_byte(struct mtd_info *mtd)
static int spinand_wait(struct mtd_info *mtd, struct nand_chip *chip)
{
- struct spinand_info *info = (struct spinand_info *)chip->priv;
+ struct spinand_info *info = nand_get_controller_data(chip);
unsigned long timeo = jiffies;
int retval, state = chip->state;
@@ -744,8 +744,8 @@ static void spinand_reset(struct spi_device *spi_nand)
static void spinand_cmdfunc(struct mtd_info *mtd, unsigned int command,
int column, int page)
{
- struct nand_chip *chip = (struct nand_chip *)mtd->priv;
- struct spinand_info *info = (struct spinand_info *)chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct spinand_info *info = nand_get_controller_data(chip);
struct spinand_state *state = (struct spinand_state *)info->priv;
switch (command) {
@@ -850,7 +850,6 @@ static int spinand_probe(struct spi_device *spi_nand)
struct nand_chip *chip;
struct spinand_info *info;
struct spinand_state *state;
- struct mtd_part_parser_data ppdata;
info = devm_kzalloc(&spi_nand->dev, sizeof(struct spinand_info),
GFP_KERNEL);
@@ -894,7 +893,8 @@ static int spinand_probe(struct spi_device *spi_nand)
pr_info("%s: disable ecc failed!\n", __func__);
#endif
- chip->priv = info;
+ nand_set_flash_node(chip, spi_nand->dev.of_node);
+ nand_set_controller_data(chip, info);
chip->read_buf = spinand_read_buf;
chip->write_buf = spinand_write_buf;
chip->read_byte = spinand_read_byte;
@@ -903,21 +903,17 @@ static int spinand_probe(struct spi_device *spi_nand)
chip->options |= NAND_CACHEPRG;
chip->select_chip = spinand_select_chip;
- mtd = devm_kzalloc(&spi_nand->dev, sizeof(struct mtd_info), GFP_KERNEL);
- if (!mtd)
- return -ENOMEM;
+ mtd = nand_to_mtd(chip);
dev_set_drvdata(&spi_nand->dev, mtd);
- mtd->priv = chip;
mtd->dev.parent = &spi_nand->dev;
mtd->oobsize = 64;
if (nand_scan(mtd, 1))
return -ENXIO;
- ppdata.of_node = spi_nand->dev.of_node;
- return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+ return mtd_device_register(mtd, NULL, 0);
}
/*
diff --git a/drivers/staging/netlogic/xlr_net.c b/drivers/staging/netlogic/xlr_net.c
index 8ae01753b011..0b4e819f5164 100644
--- a/drivers/staging/netlogic/xlr_net.c
+++ b/drivers/staging/netlogic/xlr_net.c
@@ -165,13 +165,18 @@ static void xlr_net_fmn_handler(int bkt, int src_stnid, int size,
}
}
+static struct phy_device *xlr_get_phydev(struct xlr_net_priv *priv)
+{
+ return mdiobus_get_phy(priv->mii_bus, priv->phy_addr);
+}
+
/*
* Ethtool operation
*/
static int xlr_get_settings(struct net_device *ndev, struct ethtool_cmd *ecmd)
{
struct xlr_net_priv *priv = netdev_priv(ndev);
- struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+ struct phy_device *phydev = xlr_get_phydev(priv);
if (!phydev)
return -ENODEV;
@@ -181,7 +186,7 @@ static int xlr_get_settings(struct net_device *ndev, struct ethtool_cmd *ecmd)
static int xlr_set_settings(struct net_device *ndev, struct ethtool_cmd *ecmd)
{
struct xlr_net_priv *priv = netdev_priv(ndev);
- struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+ struct phy_device *phydev = xlr_get_phydev(priv);
if (!phydev)
return -ENODEV;
@@ -218,7 +223,7 @@ static int xlr_net_open(struct net_device *ndev)
{
u32 err;
struct xlr_net_priv *priv = netdev_priv(ndev);
- struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+ struct phy_device *phydev = xlr_get_phydev(priv);
/* schedule a link state check */
phy_start(phydev);
@@ -239,7 +244,7 @@ static int xlr_net_open(struct net_device *ndev)
static int xlr_net_stop(struct net_device *ndev)
{
struct xlr_net_priv *priv = netdev_priv(ndev);
- struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+ struct phy_device *phydev = xlr_get_phydev(priv);
phy_stop(phydev);
netif_tx_stop_all_queues(ndev);
@@ -268,7 +273,7 @@ static void __maybe_unused xlr_wakeup_queue(unsigned long dev)
{
struct net_device *ndev = (struct net_device *) dev;
struct xlr_net_priv *priv = netdev_priv(ndev);
- struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+ struct phy_device *phydev = xlr_get_phydev(priv);
if (phydev->link)
netif_tx_wake_queue(netdev_get_tx_queue(ndev, priv->wakeup_q));
@@ -771,7 +776,7 @@ static void xlr_sgmii_init(struct xlr_net_priv *priv)
void xlr_set_gmac_speed(struct xlr_net_priv *priv)
{
- struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+ struct phy_device *phydev = xlr_get_phydev(priv);
int speed;
if (phydev->interface == PHY_INTERFACE_MODE_SGMII)
@@ -813,7 +818,7 @@ void xlr_set_gmac_speed(struct xlr_net_priv *priv)
static void xlr_gmac_link_adjust(struct net_device *ndev)
{
struct xlr_net_priv *priv = netdev_priv(ndev);
- struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+ struct phy_device *phydev = xlr_get_phydev(priv);
u32 intreg;
intreg = xlr_nae_rdreg(priv->base_addr, R_INTREG);
@@ -830,7 +835,7 @@ static void xlr_gmac_link_adjust(struct net_device *ndev)
static int xlr_mii_probe(struct xlr_net_priv *priv)
{
- struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+ struct phy_device *phydev = xlr_get_phydev(priv);
if (!phydev) {
pr_err("no PHY found on phy_addr %d\n", priv->phy_addr);
@@ -838,8 +843,8 @@ static int xlr_mii_probe(struct xlr_net_priv *priv)
}
/* Attach MAC to PHY */
- phydev = phy_connect(priv->ndev, dev_name(&phydev->dev),
- &xlr_gmac_link_adjust, priv->nd->phy_interface);
+ phydev = phy_connect(priv->ndev, phydev_name(phydev),
+ &xlr_gmac_link_adjust, priv->nd->phy_interface);
if (IS_ERR(phydev)) {
pr_err("could not attach PHY\n");
@@ -854,8 +859,7 @@ static int xlr_mii_probe(struct xlr_net_priv *priv)
| ADVERTISED_MII);
phydev->advertising = phydev->supported;
- pr_info("attached PHY driver [%s] (mii_bus:phy_addr=%s\n",
- phydev->drv->name, dev_name(&phydev->dev));
+ phy_attached_info(phydev);
return 0;
}
@@ -877,14 +881,6 @@ static int xlr_setup_mdio(struct xlr_net_priv *priv,
priv->mii_bus->read = xlr_mii_read;
priv->mii_bus->write = xlr_mii_write;
priv->mii_bus->parent = &pdev->dev;
- priv->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL);
- if (priv->mii_bus->irq == NULL) {
- pr_err("irq alloc failed\n");
- mdiobus_free(priv->mii_bus);
- return -ENOMEM;
- }
-
- priv->mii_bus->irq[priv->phy_addr] = priv->ndev->irq;
/* Scan only the enabled address */
priv->mii_bus->phy_mask = ~(1 << priv->phy_addr);
diff --git a/drivers/staging/nvec/README b/drivers/staging/nvec/README
index 9a320b7fdbe6..0e2d5c4c875f 100644
--- a/drivers/staging/nvec/README
+++ b/drivers/staging/nvec/README
@@ -1,4 +1,4 @@
-NVEC: An NVidia compliant Embedded Controller Protocol Implemenation
+NVEC: An NVidia compliant Embedded Controller Protocol Implementation
This is an implementation of the NVEC protocol used to communicate with an
embedded controller (EC) via I2C bus. The EC is an I2C master while the host
diff --git a/drivers/staging/nvec/nvec.c b/drivers/staging/nvec/nvec.c
index 802c9597d421..4ae44a5168f9 100644
--- a/drivers/staging/nvec/nvec.c
+++ b/drivers/staging/nvec/nvec.c
@@ -40,18 +40,18 @@
#include "nvec.h"
#define I2C_CNFG 0x00
-#define I2C_CNFG_PACKET_MODE_EN (1<<10)
-#define I2C_CNFG_NEW_MASTER_SFM (1<<11)
+#define I2C_CNFG_PACKET_MODE_EN (1 << 10)
+#define I2C_CNFG_NEW_MASTER_SFM (1 << 11)
#define I2C_CNFG_DEBOUNCE_CNT_SHIFT 12
#define I2C_SL_CNFG 0x20
-#define I2C_SL_NEWSL (1<<2)
-#define I2C_SL_NACK (1<<1)
-#define I2C_SL_RESP (1<<0)
-#define I2C_SL_IRQ (1<<3)
-#define END_TRANS (1<<4)
-#define RCVD (1<<2)
-#define RNW (1<<1)
+#define I2C_SL_NEWSL (1 << 2)
+#define I2C_SL_NACK (1 << 1)
+#define I2C_SL_RESP (1 << 0)
+#define I2C_SL_IRQ (1 << 3)
+#define END_TRANS (1 << 4)
+#define RCVD (1 << 2)
+#define RNW (1 << 1)
#define I2C_SL_RCVD 0x24
#define I2C_SL_STATUS 0x28
@@ -740,7 +740,7 @@ static void tegra_init_i2c_slave(struct nvec_chip *nvec)
writel(I2C_SL_NEWSL, nvec->base + I2C_SL_CNFG);
writel(0x1E, nvec->base + I2C_SL_DELAY_COUNT);
- writel(nvec->i2c_addr>>1, nvec->base + I2C_SL_ADDR1);
+ writel(nvec->i2c_addr >> 1, nvec->base + I2C_SL_ADDR1);
writel(0, nvec->base + I2C_SL_ADDR2);
enable_irq(nvec->irq);
diff --git a/drivers/staging/octeon/ethernet-defines.h b/drivers/staging/octeon/ethernet-defines.h
index 13e4cee1f86d..07bd2b87f6a0 100644
--- a/drivers/staging/octeon/ethernet-defines.h
+++ b/drivers/staging/octeon/ethernet-defines.h
@@ -40,6 +40,6 @@
#define FAU_TOTAL_TX_TO_CLEAN (CVMX_FAU_REG_END - sizeof(u32))
#define FAU_NUM_PACKET_BUFFERS_TO_FREE (FAU_TOTAL_TX_TO_CLEAN - sizeof(u32))
-#define TOTAL_NUMBER_OF_PORTS (CVMX_PIP_NUM_INPUT_PORTS+1)
+#define TOTAL_NUMBER_OF_PORTS (CVMX_PIP_NUM_INPUT_PORTS + 1)
#endif /* __ETHERNET_DEFINES_H__ */
diff --git a/drivers/staging/octeon/ethernet-rgmii.c b/drivers/staging/octeon/ethernet-rgmii.c
index 613344b886e1..1055ee14b66a 100644
--- a/drivers/staging/octeon/ethernet-rgmii.c
+++ b/drivers/staging/octeon/ethernet-rgmii.c
@@ -78,7 +78,7 @@ static void cvm_oct_rgmii_poll(struct net_device *dev)
*/
spin_lock_irqsave(&global_register_lock, flags);
} else {
- mutex_lock(&priv->phydev->bus->mdio_lock);
+ mutex_lock(&priv->phydev->mdio.bus->mdio_lock);
}
link_info = cvmx_helper_link_get(priv->port);
@@ -113,7 +113,7 @@ static void cvm_oct_rgmii_poll(struct net_device *dev)
if (use_global_register_lock)
spin_unlock_irqrestore(&global_register_lock, flags);
else
- mutex_unlock(&priv->phydev->bus->mdio_lock);
+ mutex_unlock(&priv->phydev->mdio.bus->mdio_lock);
return;
}
@@ -132,7 +132,7 @@ static void cvm_oct_rgmii_poll(struct net_device *dev)
if (use_global_register_lock)
spin_unlock_irqrestore(&global_register_lock, flags);
else
- mutex_unlock(&priv->phydev->bus->mdio_lock);
+ mutex_unlock(&priv->phydev->mdio.bus->mdio_lock);
if (priv->phydev == NULL) {
/* Tell core. */
diff --git a/drivers/staging/rdma/amso1100/c2.c b/drivers/staging/rdma/amso1100/c2.c
index 35ac536b2d45..b46ebd1ae15a 100644
--- a/drivers/staging/rdma/amso1100/c2.c
+++ b/drivers/staging/rdma/amso1100/c2.c
@@ -87,11 +87,6 @@ static struct pci_device_id c2_pci_table[] = {
MODULE_DEVICE_TABLE(pci, c2_pci_table);
-static void c2_print_macaddr(struct net_device *netdev)
-{
- pr_debug("%s: MAC %pM, IRQ %u\n", netdev->name, netdev->dev_addr, netdev->irq);
-}
-
static void c2_set_rxbufsize(struct c2_port *c2_port)
{
struct net_device *netdev = c2_port->netdev;
@@ -116,7 +111,8 @@ static int c2_tx_ring_alloc(struct c2_ring *tx_ring, void *vaddr,
struct c2_element *elem;
int i;
- tx_ring->start = kmalloc(sizeof(*elem) * tx_ring->count, GFP_KERNEL);
+ tx_ring->start = kmalloc_array(tx_ring->count, sizeof(*elem),
+ GFP_KERNEL);
if (!tx_ring->start)
return -ENOMEM;
@@ -165,7 +161,8 @@ static int c2_rx_ring_alloc(struct c2_ring *rx_ring, void *vaddr,
struct c2_element *elem;
int i;
- rx_ring->start = kmalloc(sizeof(*elem) * rx_ring->count, GFP_KERNEL);
+ rx_ring->start = kmalloc_array(rx_ring->count, sizeof(*elem),
+ GFP_KERNEL);
if (!rx_ring->start)
return -ENOMEM;
@@ -908,7 +905,8 @@ static struct net_device *c2_devinit(struct c2_dev *c2dev,
/* Validate the MAC address */
if (!is_valid_ether_addr(netdev->dev_addr)) {
pr_debug("Invalid MAC Address\n");
- c2_print_macaddr(netdev);
+ pr_debug("%s: MAC %pM, IRQ %u\n", netdev->name,
+ netdev->dev_addr, netdev->irq);
free_netdev(netdev);
return NULL;
}
@@ -1142,7 +1140,8 @@ static int c2_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
}
/* Print out the MAC address */
- c2_print_macaddr(netdev);
+ pr_debug("%s: MAC %pM, IRQ %u\n", netdev->name, netdev->dev_addr,
+ netdev->irq);
ret = c2_rnic_init(c2dev);
if (ret) {
diff --git a/drivers/staging/rdma/amso1100/c2.h b/drivers/staging/rdma/amso1100/c2.h
index d619d735838b..21b565a91fd6 100644
--- a/drivers/staging/rdma/amso1100/c2.h
+++ b/drivers/staging/rdma/amso1100/c2.h
@@ -476,72 +476,72 @@ static inline int c2_errno(void *reply)
}
/* Device */
-extern int c2_register_device(struct c2_dev *c2dev);
-extern void c2_unregister_device(struct c2_dev *c2dev);
-extern int c2_rnic_init(struct c2_dev *c2dev);
-extern void c2_rnic_term(struct c2_dev *c2dev);
-extern void c2_rnic_interrupt(struct c2_dev *c2dev);
-extern int c2_del_addr(struct c2_dev *c2dev, __be32 inaddr, __be32 inmask);
-extern int c2_add_addr(struct c2_dev *c2dev, __be32 inaddr, __be32 inmask);
+int c2_register_device(struct c2_dev *c2dev);
+void c2_unregister_device(struct c2_dev *c2dev);
+int c2_rnic_init(struct c2_dev *c2dev);
+void c2_rnic_term(struct c2_dev *c2dev);
+void c2_rnic_interrupt(struct c2_dev *c2dev);
+int c2_del_addr(struct c2_dev *c2dev, __be32 inaddr, __be32 inmask);
+int c2_add_addr(struct c2_dev *c2dev, __be32 inaddr, __be32 inmask);
/* QPs */
-extern int c2_alloc_qp(struct c2_dev *c2dev, struct c2_pd *pd,
+int c2_alloc_qp(struct c2_dev *c2dev, struct c2_pd *pd,
struct ib_qp_init_attr *qp_attrs, struct c2_qp *qp);
-extern void c2_free_qp(struct c2_dev *c2dev, struct c2_qp *qp);
-extern struct ib_qp *c2_get_qp(struct ib_device *device, int qpn);
-extern int c2_qp_modify(struct c2_dev *c2dev, struct c2_qp *qp,
+void c2_free_qp(struct c2_dev *c2dev, struct c2_qp *qp);
+struct ib_qp *c2_get_qp(struct ib_device *device, int qpn);
+int c2_qp_modify(struct c2_dev *c2dev, struct c2_qp *qp,
struct ib_qp_attr *attr, int attr_mask);
-extern int c2_qp_set_read_limits(struct c2_dev *c2dev, struct c2_qp *qp,
+int c2_qp_set_read_limits(struct c2_dev *c2dev, struct c2_qp *qp,
int ord, int ird);
-extern int c2_post_send(struct ib_qp *ibqp, struct ib_send_wr *ib_wr,
+int c2_post_send(struct ib_qp *ibqp, struct ib_send_wr *ib_wr,
struct ib_send_wr **bad_wr);
-extern int c2_post_receive(struct ib_qp *ibqp, struct ib_recv_wr *ib_wr,
+int c2_post_receive(struct ib_qp *ibqp, struct ib_recv_wr *ib_wr,
struct ib_recv_wr **bad_wr);
-extern void c2_init_qp_table(struct c2_dev *c2dev);
-extern void c2_cleanup_qp_table(struct c2_dev *c2dev);
-extern void c2_set_qp_state(struct c2_qp *, int);
-extern struct c2_qp *c2_find_qpn(struct c2_dev *c2dev, int qpn);
+void c2_init_qp_table(struct c2_dev *c2dev);
+void c2_cleanup_qp_table(struct c2_dev *c2dev);
+void c2_set_qp_state(struct c2_qp *, int);
+struct c2_qp *c2_find_qpn(struct c2_dev *c2dev, int qpn);
/* PDs */
-extern int c2_pd_alloc(struct c2_dev *c2dev, int privileged, struct c2_pd *pd);
-extern void c2_pd_free(struct c2_dev *c2dev, struct c2_pd *pd);
-extern int c2_init_pd_table(struct c2_dev *c2dev);
-extern void c2_cleanup_pd_table(struct c2_dev *c2dev);
+int c2_pd_alloc(struct c2_dev *c2dev, int privileged, struct c2_pd *pd);
+void c2_pd_free(struct c2_dev *c2dev, struct c2_pd *pd);
+int c2_init_pd_table(struct c2_dev *c2dev);
+void c2_cleanup_pd_table(struct c2_dev *c2dev);
/* CQs */
-extern int c2_init_cq(struct c2_dev *c2dev, int entries,
+int c2_init_cq(struct c2_dev *c2dev, int entries,
struct c2_ucontext *ctx, struct c2_cq *cq);
-extern void c2_free_cq(struct c2_dev *c2dev, struct c2_cq *cq);
-extern void c2_cq_event(struct c2_dev *c2dev, u32 mq_index);
-extern void c2_cq_clean(struct c2_dev *c2dev, struct c2_qp *qp, u32 mq_index);
-extern int c2_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry);
-extern int c2_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags);
+void c2_free_cq(struct c2_dev *c2dev, struct c2_cq *cq);
+void c2_cq_event(struct c2_dev *c2dev, u32 mq_index);
+void c2_cq_clean(struct c2_dev *c2dev, struct c2_qp *qp, u32 mq_index);
+int c2_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry);
+int c2_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags);
/* CM */
-extern int c2_llp_connect(struct iw_cm_id *cm_id,
+int c2_llp_connect(struct iw_cm_id *cm_id,
struct iw_cm_conn_param *iw_param);
-extern int c2_llp_accept(struct iw_cm_id *cm_id,
+int c2_llp_accept(struct iw_cm_id *cm_id,
struct iw_cm_conn_param *iw_param);
-extern int c2_llp_reject(struct iw_cm_id *cm_id, const void *pdata,
+int c2_llp_reject(struct iw_cm_id *cm_id, const void *pdata,
u8 pdata_len);
-extern int c2_llp_service_create(struct iw_cm_id *cm_id, int backlog);
-extern int c2_llp_service_destroy(struct iw_cm_id *cm_id);
+int c2_llp_service_create(struct iw_cm_id *cm_id, int backlog);
+int c2_llp_service_destroy(struct iw_cm_id *cm_id);
/* MM */
-extern int c2_nsmr_register_phys_kern(struct c2_dev *c2dev, u64 *addr_list,
+int c2_nsmr_register_phys_kern(struct c2_dev *c2dev, u64 *addr_list,
int page_size, int pbl_depth, u32 length,
u32 off, u64 *va, enum c2_acf acf,
struct c2_mr *mr);
-extern int c2_stag_dealloc(struct c2_dev *c2dev, u32 stag_index);
+int c2_stag_dealloc(struct c2_dev *c2dev, u32 stag_index);
/* AE */
-extern void c2_ae_event(struct c2_dev *c2dev, u32 mq_index);
+void c2_ae_event(struct c2_dev *c2dev, u32 mq_index);
/* MQSP Allocator */
-extern int c2_init_mqsp_pool(struct c2_dev *c2dev, gfp_t gfp_mask,
+int c2_init_mqsp_pool(struct c2_dev *c2dev, gfp_t gfp_mask,
struct sp_chunk **root);
-extern void c2_free_mqsp_pool(struct c2_dev *c2dev, struct sp_chunk *root);
-extern __be16 *c2_alloc_mqsp(struct c2_dev *c2dev, struct sp_chunk *head,
+void c2_free_mqsp_pool(struct c2_dev *c2dev, struct sp_chunk *root);
+__be16 *c2_alloc_mqsp(struct c2_dev *c2dev, struct sp_chunk *head,
dma_addr_t *dma_addr, gfp_t gfp_mask);
-extern void c2_free_mqsp(__be16* mqsp);
+void c2_free_mqsp(__be16* mqsp);
#endif
diff --git a/drivers/staging/rdma/amso1100/c2_mq.h b/drivers/staging/rdma/amso1100/c2_mq.h
index fc1b9a7cec4b..8e1b4d13409e 100644
--- a/drivers/staging/rdma/amso1100/c2_mq.h
+++ b/drivers/staging/rdma/amso1100/c2_mq.h
@@ -93,14 +93,14 @@ static __inline__ int c2_mq_full(struct c2_mq *q)
return q->priv == (be16_to_cpu(*q->shared) + q->q_size - 1) % q->q_size;
}
-extern void c2_mq_lconsume(struct c2_mq *q, u32 wqe_count);
-extern void *c2_mq_alloc(struct c2_mq *q);
-extern void c2_mq_produce(struct c2_mq *q);
-extern void *c2_mq_consume(struct c2_mq *q);
-extern void c2_mq_free(struct c2_mq *q);
-extern void c2_mq_req_init(struct c2_mq *q, u32 index, u32 q_size, u32 msg_size,
+void c2_mq_lconsume(struct c2_mq *q, u32 wqe_count);
+void *c2_mq_alloc(struct c2_mq *q);
+void c2_mq_produce(struct c2_mq *q);
+void *c2_mq_consume(struct c2_mq *q);
+void c2_mq_free(struct c2_mq *q);
+void c2_mq_req_init(struct c2_mq *q, u32 index, u32 q_size, u32 msg_size,
u8 __iomem *pool_start, u16 __iomem *peer, u32 type);
-extern void c2_mq_rep_init(struct c2_mq *q, u32 index, u32 q_size, u32 msg_size,
+void c2_mq_rep_init(struct c2_mq *q, u32 index, u32 q_size, u32 msg_size,
u8 *pool_start, u16 __iomem *peer, u32 type);
#endif /* _C2_MQ_H_ */
diff --git a/drivers/staging/rdma/amso1100/c2_provider.c b/drivers/staging/rdma/amso1100/c2_provider.c
index c707e45887c2..a092ac743c72 100644
--- a/drivers/staging/rdma/amso1100/c2_provider.c
+++ b/drivers/staging/rdma/amso1100/c2_provider.c
@@ -620,12 +620,9 @@ static int c2_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
static int c2_reject(struct iw_cm_id *cm_id, const void *pdata, u8 pdata_len)
{
- int err;
-
pr_debug("%s:%u\n", __func__, __LINE__);
- err = c2_llp_reject(cm_id, pdata, pdata_len);
- return err;
+ return c2_llp_reject(cm_id, pdata, pdata_len);
}
static int c2_service_create(struct iw_cm_id *cm_id, int backlog)
@@ -642,12 +639,9 @@ static int c2_service_create(struct iw_cm_id *cm_id, int backlog)
static int c2_service_destroy(struct iw_cm_id *cm_id)
{
- int err;
pr_debug("%s:%u\n", __func__, __LINE__);
- err = c2_llp_service_destroy(cm_id);
-
- return err;
+ return c2_llp_service_destroy(cm_id);
}
static int c2_pseudo_up(struct net_device *netdev)
diff --git a/drivers/staging/rdma/amso1100/c2_rnic.c b/drivers/staging/rdma/amso1100/c2_rnic.c
index d3c0f77767d9..5e65c6d07ca4 100644
--- a/drivers/staging/rdma/amso1100/c2_rnic.c
+++ b/drivers/staging/rdma/amso1100/c2_rnic.c
@@ -81,7 +81,6 @@
static int c2_adapter_init(struct c2_dev *c2dev)
{
struct c2wr_init_req wr;
- int err;
memset(&wr, 0, sizeof(wr));
c2_wr_set_id(&wr, CCWR_INIT);
@@ -94,9 +93,7 @@ static int c2_adapter_init(struct c2_dev *c2dev)
wr.q2_host_msg_pool = cpu_to_be64(c2dev->aeq.host_dma);
/* Post the init message */
- err = vq_send_wr(c2dev, (union c2wr *) & wr);
-
- return err;
+ return vq_send_wr(c2dev, (union c2wr *) & wr);
}
/*
diff --git a/drivers/staging/rdma/amso1100/c2_vq.h b/drivers/staging/rdma/amso1100/c2_vq.h
index 33805627a607..c1f6cef60213 100644
--- a/drivers/staging/rdma/amso1100/c2_vq.h
+++ b/drivers/staging/rdma/amso1100/c2_vq.h
@@ -47,17 +47,17 @@ struct c2_vq_req {
struct c2_qp *qp;
};
-extern int vq_init(struct c2_dev *c2dev);
-extern void vq_term(struct c2_dev *c2dev);
+int vq_init(struct c2_dev *c2dev);
+void vq_term(struct c2_dev *c2dev);
-extern struct c2_vq_req *vq_req_alloc(struct c2_dev *c2dev);
-extern void vq_req_free(struct c2_dev *c2dev, struct c2_vq_req *req);
-extern void vq_req_get(struct c2_dev *c2dev, struct c2_vq_req *req);
-extern void vq_req_put(struct c2_dev *c2dev, struct c2_vq_req *req);
-extern int vq_send_wr(struct c2_dev *c2dev, union c2wr * wr);
+struct c2_vq_req *vq_req_alloc(struct c2_dev *c2dev);
+void vq_req_free(struct c2_dev *c2dev, struct c2_vq_req *req);
+void vq_req_get(struct c2_dev *c2dev, struct c2_vq_req *req);
+void vq_req_put(struct c2_dev *c2dev, struct c2_vq_req *req);
+int vq_send_wr(struct c2_dev *c2dev, union c2wr * wr);
-extern void *vq_repbuf_alloc(struct c2_dev *c2dev);
-extern void vq_repbuf_free(struct c2_dev *c2dev, void *reply);
+void *vq_repbuf_alloc(struct c2_dev *c2dev);
+void vq_repbuf_free(struct c2_dev *c2dev, void *reply);
-extern int vq_wait_for_reply(struct c2_dev *c2dev, struct c2_vq_req *req);
+int vq_wait_for_reply(struct c2_dev *c2dev, struct c2_vq_req *req);
#endif /* _C2_VQ_H_ */
diff --git a/drivers/staging/rdma/ehca/ehca_av.c b/drivers/staging/rdma/ehca/ehca_av.c
index 465926319f3d..94e088c2d989 100644
--- a/drivers/staging/rdma/ehca/ehca_av.c
+++ b/drivers/staging/rdma/ehca/ehca_av.c
@@ -105,6 +105,7 @@ struct ib_ah *ehca_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr)
if (ehca_static_rate < 0) {
u32 ipd;
+
if (ehca_calc_ipd(shca, ah_attr->port_num,
ah_attr->static_rate, &ipd)) {
ret = -EINVAL;
@@ -128,6 +129,7 @@ struct ib_ah *ehca_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr)
int rc;
struct ib_port_attr port_attr;
union ib_gid gid;
+
memset(&port_attr, 0, sizeof(port_attr));
rc = ehca_query_port(pd->device, ah_attr->port_num,
&port_attr);
@@ -192,6 +194,7 @@ int ehca_modify_ah(struct ib_ah *ah, struct ib_ah_attr *ah_attr)
int rc;
struct ib_port_attr port_attr;
union ib_gid gid;
+
memset(&port_attr, 0, sizeof(port_attr));
rc = ehca_query_port(ah->device, ah_attr->port_num,
&port_attr);
@@ -272,6 +275,5 @@ int ehca_init_av_cache(void)
void ehca_cleanup_av_cache(void)
{
- if (av_cache)
- kmem_cache_destroy(av_cache);
+ kmem_cache_destroy(av_cache);
}
diff --git a/drivers/staging/rdma/ehca/ehca_cq.c b/drivers/staging/rdma/ehca/ehca_cq.c
index ea1b5c1203b4..1aa7931fe860 100644
--- a/drivers/staging/rdma/ehca/ehca_cq.c
+++ b/drivers/staging/rdma/ehca/ehca_cq.c
@@ -393,6 +393,5 @@ int ehca_init_cq_cache(void)
void ehca_cleanup_cq_cache(void)
{
- if (cq_cache)
- kmem_cache_destroy(cq_cache);
+ kmem_cache_destroy(cq_cache);
}
diff --git a/drivers/staging/rdma/ehca/ehca_main.c b/drivers/staging/rdma/ehca/ehca_main.c
index 8246418cd4e0..860b974e9faa 100644
--- a/drivers/staging/rdma/ehca/ehca_main.c
+++ b/drivers/staging/rdma/ehca/ehca_main.c
@@ -245,8 +245,7 @@ static void ehca_destroy_slab_caches(void)
ehca_cleanup_cq_cache();
ehca_cleanup_pd_cache();
#ifdef CONFIG_PPC_64K_PAGES
- if (ctblk_cache)
- kmem_cache_destroy(ctblk_cache);
+ kmem_cache_destroy(ctblk_cache);
#endif
}
diff --git a/drivers/staging/rdma/ehca/ehca_mrmw.c b/drivers/staging/rdma/ehca/ehca_mrmw.c
index f914b30999f8..553e883a5718 100644
--- a/drivers/staging/rdma/ehca/ehca_mrmw.c
+++ b/drivers/staging/rdma/ehca/ehca_mrmw.c
@@ -2251,10 +2251,8 @@ int ehca_init_mrmw_cache(void)
void ehca_cleanup_mrmw_cache(void)
{
- if (mr_cache)
- kmem_cache_destroy(mr_cache);
- if (mw_cache)
- kmem_cache_destroy(mw_cache);
+ kmem_cache_destroy(mr_cache);
+ kmem_cache_destroy(mw_cache);
}
static inline int ehca_init_top_bmap(struct ehca_top_bmap *ehca_top_bmap,
diff --git a/drivers/staging/rdma/ehca/ehca_pd.c b/drivers/staging/rdma/ehca/ehca_pd.c
index 351577a6670a..2a8aae411941 100644
--- a/drivers/staging/rdma/ehca/ehca_pd.c
+++ b/drivers/staging/rdma/ehca/ehca_pd.c
@@ -119,6 +119,5 @@ int ehca_init_pd_cache(void)
void ehca_cleanup_pd_cache(void)
{
- if (pd_cache)
- kmem_cache_destroy(pd_cache);
+ kmem_cache_destroy(pd_cache);
}
diff --git a/drivers/staging/rdma/ehca/ehca_qp.c b/drivers/staging/rdma/ehca/ehca_qp.c
index 2e89356c46fa..896c01f810f6 100644
--- a/drivers/staging/rdma/ehca/ehca_qp.c
+++ b/drivers/staging/rdma/ehca/ehca_qp.c
@@ -2252,6 +2252,5 @@ int ehca_init_qp_cache(void)
void ehca_cleanup_qp_cache(void)
{
- if (qp_cache)
- kmem_cache_destroy(qp_cache);
+ kmem_cache_destroy(qp_cache);
}
diff --git a/drivers/staging/rdma/hfi1/Makefile b/drivers/staging/rdma/hfi1/Makefile
index 2e5daa6cdcc2..68c5a315e557 100644
--- a/drivers/staging/rdma/hfi1/Makefile
+++ b/drivers/staging/rdma/hfi1/Makefile
@@ -7,7 +7,7 @@
#
obj-$(CONFIG_INFINIBAND_HFI1) += hfi1.o
-hfi1-y := chip.o cq.o device.o diag.o dma.o driver.o eprom.o file_ops.o firmware.o \
+hfi1-y := chip.o cq.o device.o diag.o dma.o driver.o efivar.o eprom.o file_ops.o firmware.o \
init.o intr.o keys.o mad.o mmap.o mr.o pcie.o pio.o pio_copy.o \
qp.o qsfp.o rc.o ruc.o sdma.o srq.o sysfs.o trace.o twsi.o \
uc.o ud.o user_pages.o user_sdma.o verbs_mcast.o verbs.o
diff --git a/drivers/staging/rdma/hfi1/chip.c b/drivers/staging/rdma/hfi1/chip.c
index e48981994b10..bbe5ad85cec0 100644
--- a/drivers/staging/rdma/hfi1/chip.c
+++ b/drivers/staging/rdma/hfi1/chip.c
@@ -63,6 +63,7 @@
#include "pio.h"
#include "sdma.h"
#include "eprom.h"
+#include "efivar.h"
#define NUM_IB_PORTS 1
@@ -121,8 +122,8 @@ struct flag_table {
#define SEC_SC_HALTED 0x4 /* per-context only */
#define SEC_SPC_FREEZE 0x8 /* per-HFI only */
-#define VL15CTXT 1
#define MIN_KERNEL_KCTXTS 2
+#define FIRST_KERNEL_KCTXT 1
#define NUM_MAP_REGS 32
/* Bit offset into the GUID which carries HFI id information */
@@ -663,7 +664,7 @@ static struct flag_table egress_err_info_flags[] = {
*/
#define SES(name) SEND_ERR_STATUS_SEND_##name##_ERR_SMASK
static struct flag_table send_err_status_flags[] = {
-/* 0*/ FLAG_ENTRY0("SDmaRpyTagErr", SES(CSR_PARITY)),
+/* 0*/ FLAG_ENTRY0("SendCsrParityErr", SES(CSR_PARITY)),
/* 1*/ FLAG_ENTRY0("SendCsrReadBadAddrErr", SES(CSR_READ_BAD_ADDR)),
/* 2*/ FLAG_ENTRY0("SendCsrWriteBadAddrErr", SES(CSR_WRITE_BAD_ADDR))
};
@@ -1417,6 +1418,17 @@ static u64 access_sw_link_up_cnt(const struct cntr_entry *entry, void *context,
return read_write_sw(ppd->dd, &ppd->link_up, mode, data);
}
+static u64 access_sw_unknown_frame_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_pportdata *ppd = (struct hfi1_pportdata *)context;
+
+ if (vl != CNTR_INVALID_VL)
+ return 0;
+ return read_write_sw(ppd->dd, &ppd->unknown_frame_count, mode, data);
+}
+
static u64 access_sw_xmit_discards(const struct cntr_entry *entry,
void *context, int vl, int mode, u64 data)
{
@@ -1538,6 +1550,2336 @@ static u64 access_sw_send_schedule(const struct cntr_entry *entry,
return dd->verbs_dev.n_send_schedule;
}
+/* Software counters for the error status bits within MISC_ERR_STATUS */
+static u64 access_misc_pll_lock_fail_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->misc_err_status_cnt[12];
+}
+
+static u64 access_misc_mbist_fail_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->misc_err_status_cnt[11];
+}
+
+static u64 access_misc_invalid_eep_cmd_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->misc_err_status_cnt[10];
+}
+
+static u64 access_misc_efuse_done_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->misc_err_status_cnt[9];
+}
+
+static u64 access_misc_efuse_write_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->misc_err_status_cnt[8];
+}
+
+static u64 access_misc_efuse_read_bad_addr_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->misc_err_status_cnt[7];
+}
+
+static u64 access_misc_efuse_csr_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->misc_err_status_cnt[6];
+}
+
+static u64 access_misc_fw_auth_failed_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->misc_err_status_cnt[5];
+}
+
+static u64 access_misc_key_mismatch_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->misc_err_status_cnt[4];
+}
+
+static u64 access_misc_sbus_write_failed_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->misc_err_status_cnt[3];
+}
+
+static u64 access_misc_csr_write_bad_addr_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->misc_err_status_cnt[2];
+}
+
+static u64 access_misc_csr_read_bad_addr_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->misc_err_status_cnt[1];
+}
+
+static u64 access_misc_csr_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->misc_err_status_cnt[0];
+}
+
+/*
+ * Software counter for the aggregate of
+ * individual CceErrStatus counters
+ */
+static u64 access_sw_cce_err_status_aggregated_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_cce_err_status_aggregate;
+}
+
+/*
+ * Software counters corresponding to each of the
+ * error status bits within CceErrStatus
+ */
+static u64 access_cce_msix_csr_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[40];
+}
+
+static u64 access_cce_int_map_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[39];
+}
+
+static u64 access_cce_int_map_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[38];
+}
+
+static u64 access_cce_msix_table_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[37];
+}
+
+static u64 access_cce_msix_table_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[36];
+}
+
+static u64 access_cce_rxdma_conv_fifo_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[35];
+}
+
+static u64 access_cce_rcpl_async_fifo_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[34];
+}
+
+static u64 access_cce_seg_write_bad_addr_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[33];
+}
+
+static u64 access_cce_seg_read_bad_addr_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[32];
+}
+
+static u64 access_la_triggered_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[31];
+}
+
+static u64 access_cce_trgt_cpl_timeout_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[30];
+}
+
+static u64 access_pcic_receive_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[29];
+}
+
+static u64 access_pcic_transmit_back_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[28];
+}
+
+static u64 access_pcic_transmit_front_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[27];
+}
+
+static u64 access_pcic_cpl_dat_q_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[26];
+}
+
+static u64 access_pcic_cpl_hd_q_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[25];
+}
+
+static u64 access_pcic_post_dat_q_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[24];
+}
+
+static u64 access_pcic_post_hd_q_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[23];
+}
+
+static u64 access_pcic_retry_sot_mem_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[22];
+}
+
+static u64 access_pcic_retry_mem_unc_err(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[21];
+}
+
+static u64 access_pcic_n_post_dat_q_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[20];
+}
+
+static u64 access_pcic_n_post_h_q_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[19];
+}
+
+static u64 access_pcic_cpl_dat_q_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[18];
+}
+
+static u64 access_pcic_cpl_hd_q_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[17];
+}
+
+static u64 access_pcic_post_dat_q_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[16];
+}
+
+static u64 access_pcic_post_hd_q_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[15];
+}
+
+static u64 access_pcic_retry_sot_mem_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[14];
+}
+
+static u64 access_pcic_retry_mem_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[13];
+}
+
+static u64 access_cce_cli1_async_fifo_dbg_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[12];
+}
+
+static u64 access_cce_cli1_async_fifo_rxdma_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[11];
+}
+
+static u64 access_cce_cli1_async_fifo_sdma_hd_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[10];
+}
+
+static u64 access_cce_cl1_async_fifo_pio_crdt_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[9];
+}
+
+static u64 access_cce_cli2_async_fifo_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[8];
+}
+
+static u64 access_cce_csr_cfg_bus_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[7];
+}
+
+static u64 access_cce_cli0_async_fifo_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[6];
+}
+
+static u64 access_cce_rspd_data_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[5];
+}
+
+static u64 access_cce_trgt_access_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[4];
+}
+
+static u64 access_cce_trgt_async_fifo_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[3];
+}
+
+static u64 access_cce_csr_write_bad_addr_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[2];
+}
+
+static u64 access_cce_csr_read_bad_addr_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[1];
+}
+
+static u64 access_ccs_csr_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->cce_err_status_cnt[0];
+}
+
+/*
+ * Software counters corresponding to each of the
+ * error status bits within RcvErrStatus
+ */
+static u64 access_rx_csr_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[63];
+}
+
+static u64 access_rx_csr_write_bad_addr_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[62];
+}
+
+static u64 access_rx_csr_read_bad_addr_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[61];
+}
+
+static u64 access_rx_dma_csr_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[60];
+}
+
+static u64 access_rx_dma_dq_fsm_encoding_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[59];
+}
+
+static u64 access_rx_dma_eq_fsm_encoding_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[58];
+}
+
+static u64 access_rx_dma_csr_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[57];
+}
+
+static u64 access_rx_rbuf_data_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[56];
+}
+
+static u64 access_rx_rbuf_data_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[55];
+}
+
+static u64 access_rx_dma_data_fifo_rd_cor_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[54];
+}
+
+static u64 access_rx_dma_data_fifo_rd_unc_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[53];
+}
+
+static u64 access_rx_dma_hdr_fifo_rd_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[52];
+}
+
+static u64 access_rx_dma_hdr_fifo_rd_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[51];
+}
+
+static u64 access_rx_rbuf_desc_part2_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[50];
+}
+
+static u64 access_rx_rbuf_desc_part2_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[49];
+}
+
+static u64 access_rx_rbuf_desc_part1_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[48];
+}
+
+static u64 access_rx_rbuf_desc_part1_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[47];
+}
+
+static u64 access_rx_hq_intr_fsm_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[46];
+}
+
+static u64 access_rx_hq_intr_csr_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[45];
+}
+
+static u64 access_rx_lookup_csr_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[44];
+}
+
+static u64 access_rx_lookup_rcv_array_cor_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[43];
+}
+
+static u64 access_rx_lookup_rcv_array_unc_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[42];
+}
+
+static u64 access_rx_lookup_des_part2_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[41];
+}
+
+static u64 access_rx_lookup_des_part1_unc_cor_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[40];
+}
+
+static u64 access_rx_lookup_des_part1_unc_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[39];
+}
+
+static u64 access_rx_rbuf_next_free_buf_cor_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[38];
+}
+
+static u64 access_rx_rbuf_next_free_buf_unc_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[37];
+}
+
+static u64 access_rbuf_fl_init_wr_addr_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[36];
+}
+
+static u64 access_rx_rbuf_fl_initdone_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[35];
+}
+
+static u64 access_rx_rbuf_fl_write_addr_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[34];
+}
+
+static u64 access_rx_rbuf_fl_rd_addr_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[33];
+}
+
+static u64 access_rx_rbuf_empty_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[32];
+}
+
+static u64 access_rx_rbuf_full_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[31];
+}
+
+static u64 access_rbuf_bad_lookup_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[30];
+}
+
+static u64 access_rbuf_ctx_id_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[29];
+}
+
+static u64 access_rbuf_csr_qeopdw_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[28];
+}
+
+static u64 access_rx_rbuf_csr_q_num_of_pkt_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[27];
+}
+
+static u64 access_rx_rbuf_csr_q_t1_ptr_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[26];
+}
+
+static u64 access_rx_rbuf_csr_q_hd_ptr_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[25];
+}
+
+static u64 access_rx_rbuf_csr_q_vld_bit_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[24];
+}
+
+static u64 access_rx_rbuf_csr_q_next_buf_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[23];
+}
+
+static u64 access_rx_rbuf_csr_q_ent_cnt_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[22];
+}
+
+static u64 access_rx_rbuf_csr_q_head_buf_num_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[21];
+}
+
+static u64 access_rx_rbuf_block_list_read_cor_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[20];
+}
+
+static u64 access_rx_rbuf_block_list_read_unc_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[19];
+}
+
+static u64 access_rx_rbuf_lookup_des_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[18];
+}
+
+static u64 access_rx_rbuf_lookup_des_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[17];
+}
+
+static u64 access_rx_rbuf_lookup_des_reg_unc_cor_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[16];
+}
+
+static u64 access_rx_rbuf_lookup_des_reg_unc_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[15];
+}
+
+static u64 access_rx_rbuf_free_list_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[14];
+}
+
+static u64 access_rx_rbuf_free_list_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[13];
+}
+
+static u64 access_rx_rcv_fsm_encoding_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[12];
+}
+
+static u64 access_rx_dma_flag_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[11];
+}
+
+static u64 access_rx_dma_flag_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[10];
+}
+
+static u64 access_rx_dc_sop_eop_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[9];
+}
+
+static u64 access_rx_rcv_csr_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[8];
+}
+
+static u64 access_rx_rcv_qp_map_table_cor_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[7];
+}
+
+static u64 access_rx_rcv_qp_map_table_unc_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[6];
+}
+
+static u64 access_rx_rcv_data_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[5];
+}
+
+static u64 access_rx_rcv_data_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[4];
+}
+
+static u64 access_rx_rcv_hdr_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[3];
+}
+
+static u64 access_rx_rcv_hdr_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[2];
+}
+
+static u64 access_rx_dc_intf_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[1];
+}
+
+static u64 access_rx_dma_csr_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->rcv_err_status_cnt[0];
+}
+
+/*
+ * Software counters corresponding to each of the
+ * error status bits within SendPioErrStatus
+ */
+static u64 access_pio_pec_sop_head_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[35];
+}
+
+static u64 access_pio_pcc_sop_head_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[34];
+}
+
+static u64 access_pio_last_returned_cnt_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[33];
+}
+
+static u64 access_pio_current_free_cnt_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[32];
+}
+
+static u64 access_pio_reserved_31_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[31];
+}
+
+static u64 access_pio_reserved_30_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[30];
+}
+
+static u64 access_pio_ppmc_sop_len_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[29];
+}
+
+static u64 access_pio_ppmc_bqc_mem_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[28];
+}
+
+static u64 access_pio_vl_fifo_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[27];
+}
+
+static u64 access_pio_vlf_sop_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[26];
+}
+
+static u64 access_pio_vlf_v1_len_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[25];
+}
+
+static u64 access_pio_block_qw_count_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[24];
+}
+
+static u64 access_pio_write_qw_valid_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[23];
+}
+
+static u64 access_pio_state_machine_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[22];
+}
+
+static u64 access_pio_write_data_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[21];
+}
+
+static u64 access_pio_host_addr_mem_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[20];
+}
+
+static u64 access_pio_host_addr_mem_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[19];
+}
+
+static u64 access_pio_pkt_evict_sm_or_arb_sm_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[18];
+}
+
+static u64 access_pio_init_sm_in_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[17];
+}
+
+static u64 access_pio_ppmc_pbl_fifo_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[16];
+}
+
+static u64 access_pio_credit_ret_fifo_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[15];
+}
+
+static u64 access_pio_v1_len_mem_bank1_cor_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[14];
+}
+
+static u64 access_pio_v1_len_mem_bank0_cor_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[13];
+}
+
+static u64 access_pio_v1_len_mem_bank1_unc_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[12];
+}
+
+static u64 access_pio_v1_len_mem_bank0_unc_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[11];
+}
+
+static u64 access_pio_sm_pkt_reset_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[10];
+}
+
+static u64 access_pio_pkt_evict_fifo_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[9];
+}
+
+static u64 access_pio_sbrdctrl_crrel_fifo_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[8];
+}
+
+static u64 access_pio_sbrdctl_crrel_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[7];
+}
+
+static u64 access_pio_pec_fifo_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[6];
+}
+
+static u64 access_pio_pcc_fifo_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[5];
+}
+
+static u64 access_pio_sb_mem_fifo1_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[4];
+}
+
+static u64 access_pio_sb_mem_fifo0_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[3];
+}
+
+static u64 access_pio_csr_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[2];
+}
+
+static u64 access_pio_write_addr_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[1];
+}
+
+static u64 access_pio_write_bad_ctxt_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_pio_err_status_cnt[0];
+}
+
+/*
+ * Software counters corresponding to each of the
+ * error status bits within SendDmaErrStatus
+ */
+static u64 access_sdma_pcie_req_tracking_cor_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_dma_err_status_cnt[3];
+}
+
+static u64 access_sdma_pcie_req_tracking_unc_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_dma_err_status_cnt[2];
+}
+
+static u64 access_sdma_csr_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_dma_err_status_cnt[1];
+}
+
+static u64 access_sdma_rpy_tag_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_dma_err_status_cnt[0];
+}
+
+/*
+ * Software counters corresponding to each of the
+ * error status bits within SendEgressErrStatus
+ */
+static u64 access_tx_read_pio_memory_csr_unc_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[63];
+}
+
+static u64 access_tx_read_sdma_memory_csr_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[62];
+}
+
+static u64 access_tx_egress_fifo_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[61];
+}
+
+static u64 access_tx_read_pio_memory_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[60];
+}
+
+static u64 access_tx_read_sdma_memory_cor_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[59];
+}
+
+static u64 access_tx_sb_hdr_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[58];
+}
+
+static u64 access_tx_credit_overrun_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[57];
+}
+
+static u64 access_tx_launch_fifo8_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[56];
+}
+
+static u64 access_tx_launch_fifo7_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[55];
+}
+
+static u64 access_tx_launch_fifo6_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[54];
+}
+
+static u64 access_tx_launch_fifo5_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[53];
+}
+
+static u64 access_tx_launch_fifo4_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[52];
+}
+
+static u64 access_tx_launch_fifo3_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[51];
+}
+
+static u64 access_tx_launch_fifo2_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[50];
+}
+
+static u64 access_tx_launch_fifo1_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[49];
+}
+
+static u64 access_tx_launch_fifo0_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[48];
+}
+
+static u64 access_tx_credit_return_vl_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[47];
+}
+
+static u64 access_tx_hcrc_insertion_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[46];
+}
+
+static u64 access_tx_egress_fifo_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[45];
+}
+
+static u64 access_tx_read_pio_memory_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[44];
+}
+
+static u64 access_tx_read_sdma_memory_unc_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[43];
+}
+
+static u64 access_tx_sb_hdr_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[42];
+}
+
+static u64 access_tx_credit_return_partiy_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[41];
+}
+
+static u64 access_tx_launch_fifo8_unc_or_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[40];
+}
+
+static u64 access_tx_launch_fifo7_unc_or_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[39];
+}
+
+static u64 access_tx_launch_fifo6_unc_or_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[38];
+}
+
+static u64 access_tx_launch_fifo5_unc_or_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[37];
+}
+
+static u64 access_tx_launch_fifo4_unc_or_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[36];
+}
+
+static u64 access_tx_launch_fifo3_unc_or_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[35];
+}
+
+static u64 access_tx_launch_fifo2_unc_or_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[34];
+}
+
+static u64 access_tx_launch_fifo1_unc_or_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[33];
+}
+
+static u64 access_tx_launch_fifo0_unc_or_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[32];
+}
+
+static u64 access_tx_sdma15_disallowed_packet_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[31];
+}
+
+static u64 access_tx_sdma14_disallowed_packet_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[30];
+}
+
+static u64 access_tx_sdma13_disallowed_packet_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[29];
+}
+
+static u64 access_tx_sdma12_disallowed_packet_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[28];
+}
+
+static u64 access_tx_sdma11_disallowed_packet_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[27];
+}
+
+static u64 access_tx_sdma10_disallowed_packet_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[26];
+}
+
+static u64 access_tx_sdma9_disallowed_packet_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[25];
+}
+
+static u64 access_tx_sdma8_disallowed_packet_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[24];
+}
+
+static u64 access_tx_sdma7_disallowed_packet_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[23];
+}
+
+static u64 access_tx_sdma6_disallowed_packet_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[22];
+}
+
+static u64 access_tx_sdma5_disallowed_packet_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[21];
+}
+
+static u64 access_tx_sdma4_disallowed_packet_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[20];
+}
+
+static u64 access_tx_sdma3_disallowed_packet_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[19];
+}
+
+static u64 access_tx_sdma2_disallowed_packet_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[18];
+}
+
+static u64 access_tx_sdma1_disallowed_packet_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[17];
+}
+
+static u64 access_tx_sdma0_disallowed_packet_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[16];
+}
+
+static u64 access_tx_config_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[15];
+}
+
+static u64 access_tx_sbrd_ctl_csr_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[14];
+}
+
+static u64 access_tx_launch_csr_parity_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[13];
+}
+
+static u64 access_tx_illegal_vl_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[12];
+}
+
+static u64 access_tx_sbrd_ctl_state_machine_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[11];
+}
+
+static u64 access_egress_reserved_10_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[10];
+}
+
+static u64 access_egress_reserved_9_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[9];
+}
+
+static u64 access_tx_sdma_launch_intf_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[8];
+}
+
+static u64 access_tx_pio_launch_intf_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[7];
+}
+
+static u64 access_egress_reserved_6_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[6];
+}
+
+static u64 access_tx_incorrect_link_state_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[5];
+}
+
+static u64 access_tx_linkdown_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[4];
+}
+
+static u64 access_tx_egress_fifi_underrun_or_parity_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[3];
+}
+
+static u64 access_egress_reserved_2_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[2];
+}
+
+static u64 access_tx_pkt_integrity_mem_unc_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[1];
+}
+
+static u64 access_tx_pkt_integrity_mem_cor_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_egress_err_status_cnt[0];
+}
+
+/*
+ * Software counters corresponding to each of the
+ * error status bits within SendErrStatus
+ */
+static u64 access_send_csr_write_bad_addr_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_err_status_cnt[2];
+}
+
+static u64 access_send_csr_read_bad_addr_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_err_status_cnt[1];
+}
+
+static u64 access_send_csr_parity_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->send_err_status_cnt[0];
+}
+
+/*
+ * Software counters corresponding to each of the
+ * error status bits within SendCtxtErrStatus
+ */
+static u64 access_pio_write_out_of_bounds_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_ctxt_err_status_cnt[4];
+}
+
+static u64 access_pio_write_overflow_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_ctxt_err_status_cnt[3];
+}
+
+static u64 access_pio_write_crosses_boundary_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_ctxt_err_status_cnt[2];
+}
+
+static u64 access_pio_disallowed_packet_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_ctxt_err_status_cnt[1];
+}
+
+static u64 access_pio_inconsistent_sop_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_ctxt_err_status_cnt[0];
+}
+
+/*
+ * Software counters corresponding to each of the
+ * error status bits within SendDmaEngErrStatus
+ */
+static u64 access_sdma_header_request_fifo_cor_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[23];
+}
+
+static u64 access_sdma_header_storage_cor_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[22];
+}
+
+static u64 access_sdma_packet_tracking_cor_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[21];
+}
+
+static u64 access_sdma_assembly_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[20];
+}
+
+static u64 access_sdma_desc_table_cor_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[19];
+}
+
+static u64 access_sdma_header_request_fifo_unc_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[18];
+}
+
+static u64 access_sdma_header_storage_unc_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[17];
+}
+
+static u64 access_sdma_packet_tracking_unc_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[16];
+}
+
+static u64 access_sdma_assembly_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[15];
+}
+
+static u64 access_sdma_desc_table_unc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[14];
+}
+
+static u64 access_sdma_timeout_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[13];
+}
+
+static u64 access_sdma_header_length_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[12];
+}
+
+static u64 access_sdma_header_address_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[11];
+}
+
+static u64 access_sdma_header_select_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[10];
+}
+
+static u64 access_sdma_reserved_9_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[9];
+}
+
+static u64 access_sdma_packet_desc_overflow_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[8];
+}
+
+static u64 access_sdma_length_mismatch_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl,
+ int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[7];
+}
+
+static u64 access_sdma_halt_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[6];
+}
+
+static u64 access_sdma_mem_read_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[5];
+}
+
+static u64 access_sdma_first_desc_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[4];
+}
+
+static u64 access_sdma_tail_out_of_bounds_err_cnt(
+ const struct cntr_entry *entry,
+ void *context, int vl, int mode, u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[3];
+}
+
+static u64 access_sdma_too_long_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[2];
+}
+
+static u64 access_sdma_gen_mismatch_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[1];
+}
+
+static u64 access_sdma_wrong_dw_err_cnt(const struct cntr_entry *entry,
+ void *context, int vl, int mode,
+ u64 data)
+{
+ struct hfi1_devdata *dd = (struct hfi1_devdata *)context;
+
+ return dd->sw_send_dma_eng_err_status_cnt[0];
+}
+
#define def_access_sw_cpu(cntr) \
static u64 access_sw_cpu_##cntr(const struct cntr_entry *entry, \
void *context, int vl, int mode, u64 data) \
@@ -1587,8 +3929,6 @@ static struct cntr_entry dev_cntrs[DEV_CNTR_LAST] = {
[C_RX_TID_FLGMS] = RXE32_DEV_CNTR_ELEM(RxTidFLGMs,
RCV_TID_FLOW_GEN_MISMATCH_CNT,
CNTR_NORMAL),
-[C_RX_CTX_RHQS] = RXE32_DEV_CNTR_ELEM(RxCtxRHQS, RCV_CONTEXT_RHQ_STALL,
- CNTR_NORMAL),
[C_RX_CTX_EGRS] = RXE32_DEV_CNTR_ELEM(RxCtxEgrS, RCV_CONTEXT_EGR_STALL,
CNTR_NORMAL),
[C_RCV_TID_FLSMS] = RXE32_DEV_CNTR_ELEM(RxTidFLSMs,
@@ -1730,6 +4070,794 @@ static struct cntr_entry dev_cntrs[DEV_CNTR_LAST] = {
access_sw_kmem_wait),
[C_SW_SEND_SCHED] = CNTR_ELEM("SendSched", 0, 0, CNTR_NORMAL,
access_sw_send_schedule),
+/* MISC_ERR_STATUS */
+[C_MISC_PLL_LOCK_FAIL_ERR] = CNTR_ELEM("MISC_PLL_LOCK_FAIL_ERR", 0, 0,
+ CNTR_NORMAL,
+ access_misc_pll_lock_fail_err_cnt),
+[C_MISC_MBIST_FAIL_ERR] = CNTR_ELEM("MISC_MBIST_FAIL_ERR", 0, 0,
+ CNTR_NORMAL,
+ access_misc_mbist_fail_err_cnt),
+[C_MISC_INVALID_EEP_CMD_ERR] = CNTR_ELEM("MISC_INVALID_EEP_CMD_ERR", 0, 0,
+ CNTR_NORMAL,
+ access_misc_invalid_eep_cmd_err_cnt),
+[C_MISC_EFUSE_DONE_PARITY_ERR] = CNTR_ELEM("MISC_EFUSE_DONE_PARITY_ERR", 0, 0,
+ CNTR_NORMAL,
+ access_misc_efuse_done_parity_err_cnt),
+[C_MISC_EFUSE_WRITE_ERR] = CNTR_ELEM("MISC_EFUSE_WRITE_ERR", 0, 0,
+ CNTR_NORMAL,
+ access_misc_efuse_write_err_cnt),
+[C_MISC_EFUSE_READ_BAD_ADDR_ERR] = CNTR_ELEM("MISC_EFUSE_READ_BAD_ADDR_ERR", 0,
+ 0, CNTR_NORMAL,
+ access_misc_efuse_read_bad_addr_err_cnt),
+[C_MISC_EFUSE_CSR_PARITY_ERR] = CNTR_ELEM("MISC_EFUSE_CSR_PARITY_ERR", 0, 0,
+ CNTR_NORMAL,
+ access_misc_efuse_csr_parity_err_cnt),
+[C_MISC_FW_AUTH_FAILED_ERR] = CNTR_ELEM("MISC_FW_AUTH_FAILED_ERR", 0, 0,
+ CNTR_NORMAL,
+ access_misc_fw_auth_failed_err_cnt),
+[C_MISC_KEY_MISMATCH_ERR] = CNTR_ELEM("MISC_KEY_MISMATCH_ERR", 0, 0,
+ CNTR_NORMAL,
+ access_misc_key_mismatch_err_cnt),
+[C_MISC_SBUS_WRITE_FAILED_ERR] = CNTR_ELEM("MISC_SBUS_WRITE_FAILED_ERR", 0, 0,
+ CNTR_NORMAL,
+ access_misc_sbus_write_failed_err_cnt),
+[C_MISC_CSR_WRITE_BAD_ADDR_ERR] = CNTR_ELEM("MISC_CSR_WRITE_BAD_ADDR_ERR", 0, 0,
+ CNTR_NORMAL,
+ access_misc_csr_write_bad_addr_err_cnt),
+[C_MISC_CSR_READ_BAD_ADDR_ERR] = CNTR_ELEM("MISC_CSR_READ_BAD_ADDR_ERR", 0, 0,
+ CNTR_NORMAL,
+ access_misc_csr_read_bad_addr_err_cnt),
+[C_MISC_CSR_PARITY_ERR] = CNTR_ELEM("MISC_CSR_PARITY_ERR", 0, 0,
+ CNTR_NORMAL,
+ access_misc_csr_parity_err_cnt),
+/* CceErrStatus */
+[C_CCE_ERR_STATUS_AGGREGATED_CNT] = CNTR_ELEM("CceErrStatusAggregatedCnt", 0, 0,
+ CNTR_NORMAL,
+ access_sw_cce_err_status_aggregated_cnt),
+[C_CCE_MSIX_CSR_PARITY_ERR] = CNTR_ELEM("CceMsixCsrParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_cce_msix_csr_parity_err_cnt),
+[C_CCE_INT_MAP_UNC_ERR] = CNTR_ELEM("CceIntMapUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_cce_int_map_unc_err_cnt),
+[C_CCE_INT_MAP_COR_ERR] = CNTR_ELEM("CceIntMapCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_cce_int_map_cor_err_cnt),
+[C_CCE_MSIX_TABLE_UNC_ERR] = CNTR_ELEM("CceMsixTableUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_cce_msix_table_unc_err_cnt),
+[C_CCE_MSIX_TABLE_COR_ERR] = CNTR_ELEM("CceMsixTableCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_cce_msix_table_cor_err_cnt),
+[C_CCE_RXDMA_CONV_FIFO_PARITY_ERR] = CNTR_ELEM("CceRxdmaConvFifoParityErr", 0,
+ 0, CNTR_NORMAL,
+ access_cce_rxdma_conv_fifo_parity_err_cnt),
+[C_CCE_RCPL_ASYNC_FIFO_PARITY_ERR] = CNTR_ELEM("CceRcplAsyncFifoParityErr", 0,
+ 0, CNTR_NORMAL,
+ access_cce_rcpl_async_fifo_parity_err_cnt),
+[C_CCE_SEG_WRITE_BAD_ADDR_ERR] = CNTR_ELEM("CceSegWriteBadAddrErr", 0, 0,
+ CNTR_NORMAL,
+ access_cce_seg_write_bad_addr_err_cnt),
+[C_CCE_SEG_READ_BAD_ADDR_ERR] = CNTR_ELEM("CceSegReadBadAddrErr", 0, 0,
+ CNTR_NORMAL,
+ access_cce_seg_read_bad_addr_err_cnt),
+[C_LA_TRIGGERED] = CNTR_ELEM("Cce LATriggered", 0, 0,
+ CNTR_NORMAL,
+ access_la_triggered_cnt),
+[C_CCE_TRGT_CPL_TIMEOUT_ERR] = CNTR_ELEM("CceTrgtCplTimeoutErr", 0, 0,
+ CNTR_NORMAL,
+ access_cce_trgt_cpl_timeout_err_cnt),
+[C_PCIC_RECEIVE_PARITY_ERR] = CNTR_ELEM("PcicReceiveParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pcic_receive_parity_err_cnt),
+[C_PCIC_TRANSMIT_BACK_PARITY_ERR] = CNTR_ELEM("PcicTransmitBackParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pcic_transmit_back_parity_err_cnt),
+[C_PCIC_TRANSMIT_FRONT_PARITY_ERR] = CNTR_ELEM("PcicTransmitFrontParityErr", 0,
+ 0, CNTR_NORMAL,
+ access_pcic_transmit_front_parity_err_cnt),
+[C_PCIC_CPL_DAT_Q_UNC_ERR] = CNTR_ELEM("PcicCplDatQUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_pcic_cpl_dat_q_unc_err_cnt),
+[C_PCIC_CPL_HD_Q_UNC_ERR] = CNTR_ELEM("PcicCplHdQUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_pcic_cpl_hd_q_unc_err_cnt),
+[C_PCIC_POST_DAT_Q_UNC_ERR] = CNTR_ELEM("PcicPostDatQUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_pcic_post_dat_q_unc_err_cnt),
+[C_PCIC_POST_HD_Q_UNC_ERR] = CNTR_ELEM("PcicPostHdQUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_pcic_post_hd_q_unc_err_cnt),
+[C_PCIC_RETRY_SOT_MEM_UNC_ERR] = CNTR_ELEM("PcicRetrySotMemUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_pcic_retry_sot_mem_unc_err_cnt),
+[C_PCIC_RETRY_MEM_UNC_ERR] = CNTR_ELEM("PcicRetryMemUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_pcic_retry_mem_unc_err),
+[C_PCIC_N_POST_DAT_Q_PARITY_ERR] = CNTR_ELEM("PcicNPostDatQParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pcic_n_post_dat_q_parity_err_cnt),
+[C_PCIC_N_POST_H_Q_PARITY_ERR] = CNTR_ELEM("PcicNPostHQParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pcic_n_post_h_q_parity_err_cnt),
+[C_PCIC_CPL_DAT_Q_COR_ERR] = CNTR_ELEM("PcicCplDatQCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_pcic_cpl_dat_q_cor_err_cnt),
+[C_PCIC_CPL_HD_Q_COR_ERR] = CNTR_ELEM("PcicCplHdQCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_pcic_cpl_hd_q_cor_err_cnt),
+[C_PCIC_POST_DAT_Q_COR_ERR] = CNTR_ELEM("PcicPostDatQCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_pcic_post_dat_q_cor_err_cnt),
+[C_PCIC_POST_HD_Q_COR_ERR] = CNTR_ELEM("PcicPostHdQCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_pcic_post_hd_q_cor_err_cnt),
+[C_PCIC_RETRY_SOT_MEM_COR_ERR] = CNTR_ELEM("PcicRetrySotMemCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_pcic_retry_sot_mem_cor_err_cnt),
+[C_PCIC_RETRY_MEM_COR_ERR] = CNTR_ELEM("PcicRetryMemCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_pcic_retry_mem_cor_err_cnt),
+[C_CCE_CLI1_ASYNC_FIFO_DBG_PARITY_ERR] = CNTR_ELEM(
+ "CceCli1AsyncFifoDbgParityError", 0, 0,
+ CNTR_NORMAL,
+ access_cce_cli1_async_fifo_dbg_parity_err_cnt),
+[C_CCE_CLI1_ASYNC_FIFO_RXDMA_PARITY_ERR] = CNTR_ELEM(
+ "CceCli1AsyncFifoRxdmaParityError", 0, 0,
+ CNTR_NORMAL,
+ access_cce_cli1_async_fifo_rxdma_parity_err_cnt
+ ),
+[C_CCE_CLI1_ASYNC_FIFO_SDMA_HD_PARITY_ERR] = CNTR_ELEM(
+ "CceCli1AsyncFifoSdmaHdParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_cce_cli1_async_fifo_sdma_hd_parity_err_cnt),
+[C_CCE_CLI1_ASYNC_FIFO_PIO_CRDT_PARITY_ERR] = CNTR_ELEM(
+ "CceCli1AsyncFifoPioCrdtParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_cce_cl1_async_fifo_pio_crdt_parity_err_cnt),
+[C_CCE_CLI2_ASYNC_FIFO_PARITY_ERR] = CNTR_ELEM("CceCli2AsyncFifoParityErr", 0,
+ 0, CNTR_NORMAL,
+ access_cce_cli2_async_fifo_parity_err_cnt),
+[C_CCE_CSR_CFG_BUS_PARITY_ERR] = CNTR_ELEM("CceCsrCfgBusParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_cce_csr_cfg_bus_parity_err_cnt),
+[C_CCE_CLI0_ASYNC_FIFO_PARTIY_ERR] = CNTR_ELEM("CceCli0AsyncFifoParityErr", 0,
+ 0, CNTR_NORMAL,
+ access_cce_cli0_async_fifo_parity_err_cnt),
+[C_CCE_RSPD_DATA_PARITY_ERR] = CNTR_ELEM("CceRspdDataParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_cce_rspd_data_parity_err_cnt),
+[C_CCE_TRGT_ACCESS_ERR] = CNTR_ELEM("CceTrgtAccessErr", 0, 0,
+ CNTR_NORMAL,
+ access_cce_trgt_access_err_cnt),
+[C_CCE_TRGT_ASYNC_FIFO_PARITY_ERR] = CNTR_ELEM("CceTrgtAsyncFifoParityErr", 0,
+ 0, CNTR_NORMAL,
+ access_cce_trgt_async_fifo_parity_err_cnt),
+[C_CCE_CSR_WRITE_BAD_ADDR_ERR] = CNTR_ELEM("CceCsrWriteBadAddrErr", 0, 0,
+ CNTR_NORMAL,
+ access_cce_csr_write_bad_addr_err_cnt),
+[C_CCE_CSR_READ_BAD_ADDR_ERR] = CNTR_ELEM("CceCsrReadBadAddrErr", 0, 0,
+ CNTR_NORMAL,
+ access_cce_csr_read_bad_addr_err_cnt),
+[C_CCE_CSR_PARITY_ERR] = CNTR_ELEM("CceCsrParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_ccs_csr_parity_err_cnt),
+
+/* RcvErrStatus */
+[C_RX_CSR_PARITY_ERR] = CNTR_ELEM("RxCsrParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_csr_parity_err_cnt),
+[C_RX_CSR_WRITE_BAD_ADDR_ERR] = CNTR_ELEM("RxCsrWriteBadAddrErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_csr_write_bad_addr_err_cnt),
+[C_RX_CSR_READ_BAD_ADDR_ERR] = CNTR_ELEM("RxCsrReadBadAddrErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_csr_read_bad_addr_err_cnt),
+[C_RX_DMA_CSR_UNC_ERR] = CNTR_ELEM("RxDmaCsrUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_dma_csr_unc_err_cnt),
+[C_RX_DMA_DQ_FSM_ENCODING_ERR] = CNTR_ELEM("RxDmaDqFsmEncodingErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_dma_dq_fsm_encoding_err_cnt),
+[C_RX_DMA_EQ_FSM_ENCODING_ERR] = CNTR_ELEM("RxDmaEqFsmEncodingErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_dma_eq_fsm_encoding_err_cnt),
+[C_RX_DMA_CSR_PARITY_ERR] = CNTR_ELEM("RxDmaCsrParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_dma_csr_parity_err_cnt),
+[C_RX_RBUF_DATA_COR_ERR] = CNTR_ELEM("RxRbufDataCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rbuf_data_cor_err_cnt),
+[C_RX_RBUF_DATA_UNC_ERR] = CNTR_ELEM("RxRbufDataUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rbuf_data_unc_err_cnt),
+[C_RX_DMA_DATA_FIFO_RD_COR_ERR] = CNTR_ELEM("RxDmaDataFifoRdCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_dma_data_fifo_rd_cor_err_cnt),
+[C_RX_DMA_DATA_FIFO_RD_UNC_ERR] = CNTR_ELEM("RxDmaDataFifoRdUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_dma_data_fifo_rd_unc_err_cnt),
+[C_RX_DMA_HDR_FIFO_RD_COR_ERR] = CNTR_ELEM("RxDmaHdrFifoRdCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_dma_hdr_fifo_rd_cor_err_cnt),
+[C_RX_DMA_HDR_FIFO_RD_UNC_ERR] = CNTR_ELEM("RxDmaHdrFifoRdUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_dma_hdr_fifo_rd_unc_err_cnt),
+[C_RX_RBUF_DESC_PART2_COR_ERR] = CNTR_ELEM("RxRbufDescPart2CorErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rbuf_desc_part2_cor_err_cnt),
+[C_RX_RBUF_DESC_PART2_UNC_ERR] = CNTR_ELEM("RxRbufDescPart2UncErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rbuf_desc_part2_unc_err_cnt),
+[C_RX_RBUF_DESC_PART1_COR_ERR] = CNTR_ELEM("RxRbufDescPart1CorErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rbuf_desc_part1_cor_err_cnt),
+[C_RX_RBUF_DESC_PART1_UNC_ERR] = CNTR_ELEM("RxRbufDescPart1UncErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rbuf_desc_part1_unc_err_cnt),
+[C_RX_HQ_INTR_FSM_ERR] = CNTR_ELEM("RxHqIntrFsmErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_hq_intr_fsm_err_cnt),
+[C_RX_HQ_INTR_CSR_PARITY_ERR] = CNTR_ELEM("RxHqIntrCsrParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_hq_intr_csr_parity_err_cnt),
+[C_RX_LOOKUP_CSR_PARITY_ERR] = CNTR_ELEM("RxLookupCsrParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_lookup_csr_parity_err_cnt),
+[C_RX_LOOKUP_RCV_ARRAY_COR_ERR] = CNTR_ELEM("RxLookupRcvArrayCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_lookup_rcv_array_cor_err_cnt),
+[C_RX_LOOKUP_RCV_ARRAY_UNC_ERR] = CNTR_ELEM("RxLookupRcvArrayUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_lookup_rcv_array_unc_err_cnt),
+[C_RX_LOOKUP_DES_PART2_PARITY_ERR] = CNTR_ELEM("RxLookupDesPart2ParityErr", 0,
+ 0, CNTR_NORMAL,
+ access_rx_lookup_des_part2_parity_err_cnt),
+[C_RX_LOOKUP_DES_PART1_UNC_COR_ERR] = CNTR_ELEM("RxLookupDesPart1UncCorErr", 0,
+ 0, CNTR_NORMAL,
+ access_rx_lookup_des_part1_unc_cor_err_cnt),
+[C_RX_LOOKUP_DES_PART1_UNC_ERR] = CNTR_ELEM("RxLookupDesPart1UncErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_lookup_des_part1_unc_err_cnt),
+[C_RX_RBUF_NEXT_FREE_BUF_COR_ERR] = CNTR_ELEM("RxRbufNextFreeBufCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rbuf_next_free_buf_cor_err_cnt),
+[C_RX_RBUF_NEXT_FREE_BUF_UNC_ERR] = CNTR_ELEM("RxRbufNextFreeBufUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rbuf_next_free_buf_unc_err_cnt),
+[C_RX_RBUF_FL_INIT_WR_ADDR_PARITY_ERR] = CNTR_ELEM(
+ "RxRbufFlInitWrAddrParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_rbuf_fl_init_wr_addr_parity_err_cnt),
+[C_RX_RBUF_FL_INITDONE_PARITY_ERR] = CNTR_ELEM("RxRbufFlInitdoneParityErr", 0,
+ 0, CNTR_NORMAL,
+ access_rx_rbuf_fl_initdone_parity_err_cnt),
+[C_RX_RBUF_FL_WRITE_ADDR_PARITY_ERR] = CNTR_ELEM("RxRbufFlWrAddrParityErr", 0,
+ 0, CNTR_NORMAL,
+ access_rx_rbuf_fl_write_addr_parity_err_cnt),
+[C_RX_RBUF_FL_RD_ADDR_PARITY_ERR] = CNTR_ELEM("RxRbufFlRdAddrParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rbuf_fl_rd_addr_parity_err_cnt),
+[C_RX_RBUF_EMPTY_ERR] = CNTR_ELEM("RxRbufEmptyErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rbuf_empty_err_cnt),
+[C_RX_RBUF_FULL_ERR] = CNTR_ELEM("RxRbufFullErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rbuf_full_err_cnt),
+[C_RX_RBUF_BAD_LOOKUP_ERR] = CNTR_ELEM("RxRBufBadLookupErr", 0, 0,
+ CNTR_NORMAL,
+ access_rbuf_bad_lookup_err_cnt),
+[C_RX_RBUF_CTX_ID_PARITY_ERR] = CNTR_ELEM("RxRbufCtxIdParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_rbuf_ctx_id_parity_err_cnt),
+[C_RX_RBUF_CSR_QEOPDW_PARITY_ERR] = CNTR_ELEM("RxRbufCsrQEOPDWParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_rbuf_csr_qeopdw_parity_err_cnt),
+[C_RX_RBUF_CSR_Q_NUM_OF_PKT_PARITY_ERR] = CNTR_ELEM(
+ "RxRbufCsrQNumOfPktParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rbuf_csr_q_num_of_pkt_parity_err_cnt),
+[C_RX_RBUF_CSR_Q_T1_PTR_PARITY_ERR] = CNTR_ELEM(
+ "RxRbufCsrQTlPtrParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rbuf_csr_q_t1_ptr_parity_err_cnt),
+[C_RX_RBUF_CSR_Q_HD_PTR_PARITY_ERR] = CNTR_ELEM("RxRbufCsrQHdPtrParityErr", 0,
+ 0, CNTR_NORMAL,
+ access_rx_rbuf_csr_q_hd_ptr_parity_err_cnt),
+[C_RX_RBUF_CSR_Q_VLD_BIT_PARITY_ERR] = CNTR_ELEM("RxRbufCsrQVldBitParityErr", 0,
+ 0, CNTR_NORMAL,
+ access_rx_rbuf_csr_q_vld_bit_parity_err_cnt),
+[C_RX_RBUF_CSR_Q_NEXT_BUF_PARITY_ERR] = CNTR_ELEM("RxRbufCsrQNextBufParityErr",
+ 0, 0, CNTR_NORMAL,
+ access_rx_rbuf_csr_q_next_buf_parity_err_cnt),
+[C_RX_RBUF_CSR_Q_ENT_CNT_PARITY_ERR] = CNTR_ELEM("RxRbufCsrQEntCntParityErr", 0,
+ 0, CNTR_NORMAL,
+ access_rx_rbuf_csr_q_ent_cnt_parity_err_cnt),
+[C_RX_RBUF_CSR_Q_HEAD_BUF_NUM_PARITY_ERR] = CNTR_ELEM(
+ "RxRbufCsrQHeadBufNumParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rbuf_csr_q_head_buf_num_parity_err_cnt),
+[C_RX_RBUF_BLOCK_LIST_READ_COR_ERR] = CNTR_ELEM("RxRbufBlockListReadCorErr", 0,
+ 0, CNTR_NORMAL,
+ access_rx_rbuf_block_list_read_cor_err_cnt),
+[C_RX_RBUF_BLOCK_LIST_READ_UNC_ERR] = CNTR_ELEM("RxRbufBlockListReadUncErr", 0,
+ 0, CNTR_NORMAL,
+ access_rx_rbuf_block_list_read_unc_err_cnt),
+[C_RX_RBUF_LOOKUP_DES_COR_ERR] = CNTR_ELEM("RxRbufLookupDesCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rbuf_lookup_des_cor_err_cnt),
+[C_RX_RBUF_LOOKUP_DES_UNC_ERR] = CNTR_ELEM("RxRbufLookupDesUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rbuf_lookup_des_unc_err_cnt),
+[C_RX_RBUF_LOOKUP_DES_REG_UNC_COR_ERR] = CNTR_ELEM(
+ "RxRbufLookupDesRegUncCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rbuf_lookup_des_reg_unc_cor_err_cnt),
+[C_RX_RBUF_LOOKUP_DES_REG_UNC_ERR] = CNTR_ELEM("RxRbufLookupDesRegUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rbuf_lookup_des_reg_unc_err_cnt),
+[C_RX_RBUF_FREE_LIST_COR_ERR] = CNTR_ELEM("RxRbufFreeListCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rbuf_free_list_cor_err_cnt),
+[C_RX_RBUF_FREE_LIST_UNC_ERR] = CNTR_ELEM("RxRbufFreeListUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rbuf_free_list_unc_err_cnt),
+[C_RX_RCV_FSM_ENCODING_ERR] = CNTR_ELEM("RxRcvFsmEncodingErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rcv_fsm_encoding_err_cnt),
+[C_RX_DMA_FLAG_COR_ERR] = CNTR_ELEM("RxDmaFlagCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_dma_flag_cor_err_cnt),
+[C_RX_DMA_FLAG_UNC_ERR] = CNTR_ELEM("RxDmaFlagUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_dma_flag_unc_err_cnt),
+[C_RX_DC_SOP_EOP_PARITY_ERR] = CNTR_ELEM("RxDcSopEopParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_dc_sop_eop_parity_err_cnt),
+[C_RX_RCV_CSR_PARITY_ERR] = CNTR_ELEM("RxRcvCsrParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rcv_csr_parity_err_cnt),
+[C_RX_RCV_QP_MAP_TABLE_COR_ERR] = CNTR_ELEM("RxRcvQpMapTableCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rcv_qp_map_table_cor_err_cnt),
+[C_RX_RCV_QP_MAP_TABLE_UNC_ERR] = CNTR_ELEM("RxRcvQpMapTableUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rcv_qp_map_table_unc_err_cnt),
+[C_RX_RCV_DATA_COR_ERR] = CNTR_ELEM("RxRcvDataCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rcv_data_cor_err_cnt),
+[C_RX_RCV_DATA_UNC_ERR] = CNTR_ELEM("RxRcvDataUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rcv_data_unc_err_cnt),
+[C_RX_RCV_HDR_COR_ERR] = CNTR_ELEM("RxRcvHdrCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rcv_hdr_cor_err_cnt),
+[C_RX_RCV_HDR_UNC_ERR] = CNTR_ELEM("RxRcvHdrUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_rcv_hdr_unc_err_cnt),
+[C_RX_DC_INTF_PARITY_ERR] = CNTR_ELEM("RxDcIntfParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_dc_intf_parity_err_cnt),
+[C_RX_DMA_CSR_COR_ERR] = CNTR_ELEM("RxDmaCsrCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_rx_dma_csr_cor_err_cnt),
+/* SendPioErrStatus */
+[C_PIO_PEC_SOP_HEAD_PARITY_ERR] = CNTR_ELEM("PioPecSopHeadParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_pec_sop_head_parity_err_cnt),
+[C_PIO_PCC_SOP_HEAD_PARITY_ERR] = CNTR_ELEM("PioPccSopHeadParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_pcc_sop_head_parity_err_cnt),
+[C_PIO_LAST_RETURNED_CNT_PARITY_ERR] = CNTR_ELEM("PioLastReturnedCntParityErr",
+ 0, 0, CNTR_NORMAL,
+ access_pio_last_returned_cnt_parity_err_cnt),
+[C_PIO_CURRENT_FREE_CNT_PARITY_ERR] = CNTR_ELEM("PioCurrentFreeCntParityErr", 0,
+ 0, CNTR_NORMAL,
+ access_pio_current_free_cnt_parity_err_cnt),
+[C_PIO_RSVD_31_ERR] = CNTR_ELEM("Pio Reserved 31", 0, 0,
+ CNTR_NORMAL,
+ access_pio_reserved_31_err_cnt),
+[C_PIO_RSVD_30_ERR] = CNTR_ELEM("Pio Reserved 30", 0, 0,
+ CNTR_NORMAL,
+ access_pio_reserved_30_err_cnt),
+[C_PIO_PPMC_SOP_LEN_ERR] = CNTR_ELEM("PioPpmcSopLenErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_ppmc_sop_len_err_cnt),
+[C_PIO_PPMC_BQC_MEM_PARITY_ERR] = CNTR_ELEM("PioPpmcBqcMemParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_ppmc_bqc_mem_parity_err_cnt),
+[C_PIO_VL_FIFO_PARITY_ERR] = CNTR_ELEM("PioVlFifoParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_vl_fifo_parity_err_cnt),
+[C_PIO_VLF_SOP_PARITY_ERR] = CNTR_ELEM("PioVlfSopParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_vlf_sop_parity_err_cnt),
+[C_PIO_VLF_V1_LEN_PARITY_ERR] = CNTR_ELEM("PioVlfVlLenParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_vlf_v1_len_parity_err_cnt),
+[C_PIO_BLOCK_QW_COUNT_PARITY_ERR] = CNTR_ELEM("PioBlockQwCountParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_block_qw_count_parity_err_cnt),
+[C_PIO_WRITE_QW_VALID_PARITY_ERR] = CNTR_ELEM("PioWriteQwValidParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_write_qw_valid_parity_err_cnt),
+[C_PIO_STATE_MACHINE_ERR] = CNTR_ELEM("PioStateMachineErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_state_machine_err_cnt),
+[C_PIO_WRITE_DATA_PARITY_ERR] = CNTR_ELEM("PioWriteDataParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_write_data_parity_err_cnt),
+[C_PIO_HOST_ADDR_MEM_COR_ERR] = CNTR_ELEM("PioHostAddrMemCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_host_addr_mem_cor_err_cnt),
+[C_PIO_HOST_ADDR_MEM_UNC_ERR] = CNTR_ELEM("PioHostAddrMemUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_host_addr_mem_unc_err_cnt),
+[C_PIO_PKT_EVICT_SM_OR_ARM_SM_ERR] = CNTR_ELEM("PioPktEvictSmOrArbSmErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_pkt_evict_sm_or_arb_sm_err_cnt),
+[C_PIO_INIT_SM_IN_ERR] = CNTR_ELEM("PioInitSmInErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_init_sm_in_err_cnt),
+[C_PIO_PPMC_PBL_FIFO_ERR] = CNTR_ELEM("PioPpmcPblFifoErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_ppmc_pbl_fifo_err_cnt),
+[C_PIO_CREDIT_RET_FIFO_PARITY_ERR] = CNTR_ELEM("PioCreditRetFifoParityErr", 0,
+ 0, CNTR_NORMAL,
+ access_pio_credit_ret_fifo_parity_err_cnt),
+[C_PIO_V1_LEN_MEM_BANK1_COR_ERR] = CNTR_ELEM("PioVlLenMemBank1CorErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_v1_len_mem_bank1_cor_err_cnt),
+[C_PIO_V1_LEN_MEM_BANK0_COR_ERR] = CNTR_ELEM("PioVlLenMemBank0CorErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_v1_len_mem_bank0_cor_err_cnt),
+[C_PIO_V1_LEN_MEM_BANK1_UNC_ERR] = CNTR_ELEM("PioVlLenMemBank1UncErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_v1_len_mem_bank1_unc_err_cnt),
+[C_PIO_V1_LEN_MEM_BANK0_UNC_ERR] = CNTR_ELEM("PioVlLenMemBank0UncErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_v1_len_mem_bank0_unc_err_cnt),
+[C_PIO_SM_PKT_RESET_PARITY_ERR] = CNTR_ELEM("PioSmPktResetParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_sm_pkt_reset_parity_err_cnt),
+[C_PIO_PKT_EVICT_FIFO_PARITY_ERR] = CNTR_ELEM("PioPktEvictFifoParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_pkt_evict_fifo_parity_err_cnt),
+[C_PIO_SBRDCTRL_CRREL_FIFO_PARITY_ERR] = CNTR_ELEM(
+ "PioSbrdctrlCrrelFifoParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_sbrdctrl_crrel_fifo_parity_err_cnt),
+[C_PIO_SBRDCTL_CRREL_PARITY_ERR] = CNTR_ELEM("PioSbrdctlCrrelParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_sbrdctl_crrel_parity_err_cnt),
+[C_PIO_PEC_FIFO_PARITY_ERR] = CNTR_ELEM("PioPecFifoParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_pec_fifo_parity_err_cnt),
+[C_PIO_PCC_FIFO_PARITY_ERR] = CNTR_ELEM("PioPccFifoParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_pcc_fifo_parity_err_cnt),
+[C_PIO_SB_MEM_FIFO1_ERR] = CNTR_ELEM("PioSbMemFifo1Err", 0, 0,
+ CNTR_NORMAL,
+ access_pio_sb_mem_fifo1_err_cnt),
+[C_PIO_SB_MEM_FIFO0_ERR] = CNTR_ELEM("PioSbMemFifo0Err", 0, 0,
+ CNTR_NORMAL,
+ access_pio_sb_mem_fifo0_err_cnt),
+[C_PIO_CSR_PARITY_ERR] = CNTR_ELEM("PioCsrParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_csr_parity_err_cnt),
+[C_PIO_WRITE_ADDR_PARITY_ERR] = CNTR_ELEM("PioWriteAddrParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_write_addr_parity_err_cnt),
+[C_PIO_WRITE_BAD_CTXT_ERR] = CNTR_ELEM("PioWriteBadCtxtErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_write_bad_ctxt_err_cnt),
+/* SendDmaErrStatus */
+[C_SDMA_PCIE_REQ_TRACKING_COR_ERR] = CNTR_ELEM("SDmaPcieReqTrackingCorErr", 0,
+ 0, CNTR_NORMAL,
+ access_sdma_pcie_req_tracking_cor_err_cnt),
+[C_SDMA_PCIE_REQ_TRACKING_UNC_ERR] = CNTR_ELEM("SDmaPcieReqTrackingUncErr", 0,
+ 0, CNTR_NORMAL,
+ access_sdma_pcie_req_tracking_unc_err_cnt),
+[C_SDMA_CSR_PARITY_ERR] = CNTR_ELEM("SDmaCsrParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_csr_parity_err_cnt),
+[C_SDMA_RPY_TAG_ERR] = CNTR_ELEM("SDmaRpyTagErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_rpy_tag_err_cnt),
+/* SendEgressErrStatus */
+[C_TX_READ_PIO_MEMORY_CSR_UNC_ERR] = CNTR_ELEM("TxReadPioMemoryCsrUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_read_pio_memory_csr_unc_err_cnt),
+[C_TX_READ_SDMA_MEMORY_CSR_UNC_ERR] = CNTR_ELEM("TxReadSdmaMemoryCsrUncErr", 0,
+ 0, CNTR_NORMAL,
+ access_tx_read_sdma_memory_csr_err_cnt),
+[C_TX_EGRESS_FIFO_COR_ERR] = CNTR_ELEM("TxEgressFifoCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_egress_fifo_cor_err_cnt),
+[C_TX_READ_PIO_MEMORY_COR_ERR] = CNTR_ELEM("TxReadPioMemoryCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_read_pio_memory_cor_err_cnt),
+[C_TX_READ_SDMA_MEMORY_COR_ERR] = CNTR_ELEM("TxReadSdmaMemoryCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_read_sdma_memory_cor_err_cnt),
+[C_TX_SB_HDR_COR_ERR] = CNTR_ELEM("TxSbHdrCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_sb_hdr_cor_err_cnt),
+[C_TX_CREDIT_OVERRUN_ERR] = CNTR_ELEM("TxCreditOverrunErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_credit_overrun_err_cnt),
+[C_TX_LAUNCH_FIFO8_COR_ERR] = CNTR_ELEM("TxLaunchFifo8CorErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_launch_fifo8_cor_err_cnt),
+[C_TX_LAUNCH_FIFO7_COR_ERR] = CNTR_ELEM("TxLaunchFifo7CorErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_launch_fifo7_cor_err_cnt),
+[C_TX_LAUNCH_FIFO6_COR_ERR] = CNTR_ELEM("TxLaunchFifo6CorErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_launch_fifo6_cor_err_cnt),
+[C_TX_LAUNCH_FIFO5_COR_ERR] = CNTR_ELEM("TxLaunchFifo5CorErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_launch_fifo5_cor_err_cnt),
+[C_TX_LAUNCH_FIFO4_COR_ERR] = CNTR_ELEM("TxLaunchFifo4CorErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_launch_fifo4_cor_err_cnt),
+[C_TX_LAUNCH_FIFO3_COR_ERR] = CNTR_ELEM("TxLaunchFifo3CorErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_launch_fifo3_cor_err_cnt),
+[C_TX_LAUNCH_FIFO2_COR_ERR] = CNTR_ELEM("TxLaunchFifo2CorErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_launch_fifo2_cor_err_cnt),
+[C_TX_LAUNCH_FIFO1_COR_ERR] = CNTR_ELEM("TxLaunchFifo1CorErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_launch_fifo1_cor_err_cnt),
+[C_TX_LAUNCH_FIFO0_COR_ERR] = CNTR_ELEM("TxLaunchFifo0CorErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_launch_fifo0_cor_err_cnt),
+[C_TX_CREDIT_RETURN_VL_ERR] = CNTR_ELEM("TxCreditReturnVLErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_credit_return_vl_err_cnt),
+[C_TX_HCRC_INSERTION_ERR] = CNTR_ELEM("TxHcrcInsertionErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_hcrc_insertion_err_cnt),
+[C_TX_EGRESS_FIFI_UNC_ERR] = CNTR_ELEM("TxEgressFifoUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_egress_fifo_unc_err_cnt),
+[C_TX_READ_PIO_MEMORY_UNC_ERR] = CNTR_ELEM("TxReadPioMemoryUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_read_pio_memory_unc_err_cnt),
+[C_TX_READ_SDMA_MEMORY_UNC_ERR] = CNTR_ELEM("TxReadSdmaMemoryUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_read_sdma_memory_unc_err_cnt),
+[C_TX_SB_HDR_UNC_ERR] = CNTR_ELEM("TxSbHdrUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_sb_hdr_unc_err_cnt),
+[C_TX_CREDIT_RETURN_PARITY_ERR] = CNTR_ELEM("TxCreditReturnParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_credit_return_partiy_err_cnt),
+[C_TX_LAUNCH_FIFO8_UNC_OR_PARITY_ERR] = CNTR_ELEM("TxLaunchFifo8UncOrParityErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_launch_fifo8_unc_or_parity_err_cnt),
+[C_TX_LAUNCH_FIFO7_UNC_OR_PARITY_ERR] = CNTR_ELEM("TxLaunchFifo7UncOrParityErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_launch_fifo7_unc_or_parity_err_cnt),
+[C_TX_LAUNCH_FIFO6_UNC_OR_PARITY_ERR] = CNTR_ELEM("TxLaunchFifo6UncOrParityErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_launch_fifo6_unc_or_parity_err_cnt),
+[C_TX_LAUNCH_FIFO5_UNC_OR_PARITY_ERR] = CNTR_ELEM("TxLaunchFifo5UncOrParityErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_launch_fifo5_unc_or_parity_err_cnt),
+[C_TX_LAUNCH_FIFO4_UNC_OR_PARITY_ERR] = CNTR_ELEM("TxLaunchFifo4UncOrParityErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_launch_fifo4_unc_or_parity_err_cnt),
+[C_TX_LAUNCH_FIFO3_UNC_OR_PARITY_ERR] = CNTR_ELEM("TxLaunchFifo3UncOrParityErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_launch_fifo3_unc_or_parity_err_cnt),
+[C_TX_LAUNCH_FIFO2_UNC_OR_PARITY_ERR] = CNTR_ELEM("TxLaunchFifo2UncOrParityErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_launch_fifo2_unc_or_parity_err_cnt),
+[C_TX_LAUNCH_FIFO1_UNC_OR_PARITY_ERR] = CNTR_ELEM("TxLaunchFifo1UncOrParityErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_launch_fifo1_unc_or_parity_err_cnt),
+[C_TX_LAUNCH_FIFO0_UNC_OR_PARITY_ERR] = CNTR_ELEM("TxLaunchFifo0UncOrParityErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_launch_fifo0_unc_or_parity_err_cnt),
+[C_TX_SDMA15_DISALLOWED_PACKET_ERR] = CNTR_ELEM("TxSdma15DisallowedPacketErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_sdma15_disallowed_packet_err_cnt),
+[C_TX_SDMA14_DISALLOWED_PACKET_ERR] = CNTR_ELEM("TxSdma14DisallowedPacketErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_sdma14_disallowed_packet_err_cnt),
+[C_TX_SDMA13_DISALLOWED_PACKET_ERR] = CNTR_ELEM("TxSdma13DisallowedPacketErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_sdma13_disallowed_packet_err_cnt),
+[C_TX_SDMA12_DISALLOWED_PACKET_ERR] = CNTR_ELEM("TxSdma12DisallowedPacketErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_sdma12_disallowed_packet_err_cnt),
+[C_TX_SDMA11_DISALLOWED_PACKET_ERR] = CNTR_ELEM("TxSdma11DisallowedPacketErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_sdma11_disallowed_packet_err_cnt),
+[C_TX_SDMA10_DISALLOWED_PACKET_ERR] = CNTR_ELEM("TxSdma10DisallowedPacketErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_sdma10_disallowed_packet_err_cnt),
+[C_TX_SDMA9_DISALLOWED_PACKET_ERR] = CNTR_ELEM("TxSdma9DisallowedPacketErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_sdma9_disallowed_packet_err_cnt),
+[C_TX_SDMA8_DISALLOWED_PACKET_ERR] = CNTR_ELEM("TxSdma8DisallowedPacketErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_sdma8_disallowed_packet_err_cnt),
+[C_TX_SDMA7_DISALLOWED_PACKET_ERR] = CNTR_ELEM("TxSdma7DisallowedPacketErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_sdma7_disallowed_packet_err_cnt),
+[C_TX_SDMA6_DISALLOWED_PACKET_ERR] = CNTR_ELEM("TxSdma6DisallowedPacketErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_sdma6_disallowed_packet_err_cnt),
+[C_TX_SDMA5_DISALLOWED_PACKET_ERR] = CNTR_ELEM("TxSdma5DisallowedPacketErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_sdma5_disallowed_packet_err_cnt),
+[C_TX_SDMA4_DISALLOWED_PACKET_ERR] = CNTR_ELEM("TxSdma4DisallowedPacketErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_sdma4_disallowed_packet_err_cnt),
+[C_TX_SDMA3_DISALLOWED_PACKET_ERR] = CNTR_ELEM("TxSdma3DisallowedPacketErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_sdma3_disallowed_packet_err_cnt),
+[C_TX_SDMA2_DISALLOWED_PACKET_ERR] = CNTR_ELEM("TxSdma2DisallowedPacketErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_sdma2_disallowed_packet_err_cnt),
+[C_TX_SDMA1_DISALLOWED_PACKET_ERR] = CNTR_ELEM("TxSdma1DisallowedPacketErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_sdma1_disallowed_packet_err_cnt),
+[C_TX_SDMA0_DISALLOWED_PACKET_ERR] = CNTR_ELEM("TxSdma0DisallowedPacketErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_sdma0_disallowed_packet_err_cnt),
+[C_TX_CONFIG_PARITY_ERR] = CNTR_ELEM("TxConfigParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_config_parity_err_cnt),
+[C_TX_SBRD_CTL_CSR_PARITY_ERR] = CNTR_ELEM("TxSbrdCtlCsrParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_sbrd_ctl_csr_parity_err_cnt),
+[C_TX_LAUNCH_CSR_PARITY_ERR] = CNTR_ELEM("TxLaunchCsrParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_launch_csr_parity_err_cnt),
+[C_TX_ILLEGAL_CL_ERR] = CNTR_ELEM("TxIllegalVLErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_illegal_vl_err_cnt),
+[C_TX_SBRD_CTL_STATE_MACHINE_PARITY_ERR] = CNTR_ELEM(
+ "TxSbrdCtlStateMachineParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_sbrd_ctl_state_machine_parity_err_cnt),
+[C_TX_RESERVED_10] = CNTR_ELEM("Tx Egress Reserved 10", 0, 0,
+ CNTR_NORMAL,
+ access_egress_reserved_10_err_cnt),
+[C_TX_RESERVED_9] = CNTR_ELEM("Tx Egress Reserved 9", 0, 0,
+ CNTR_NORMAL,
+ access_egress_reserved_9_err_cnt),
+[C_TX_SDMA_LAUNCH_INTF_PARITY_ERR] = CNTR_ELEM("TxSdmaLaunchIntfParityErr",
+ 0, 0, CNTR_NORMAL,
+ access_tx_sdma_launch_intf_parity_err_cnt),
+[C_TX_PIO_LAUNCH_INTF_PARITY_ERR] = CNTR_ELEM("TxPioLaunchIntfParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_pio_launch_intf_parity_err_cnt),
+[C_TX_RESERVED_6] = CNTR_ELEM("Tx Egress Reserved 6", 0, 0,
+ CNTR_NORMAL,
+ access_egress_reserved_6_err_cnt),
+[C_TX_INCORRECT_LINK_STATE_ERR] = CNTR_ELEM("TxIncorrectLinkStateErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_incorrect_link_state_err_cnt),
+[C_TX_LINK_DOWN_ERR] = CNTR_ELEM("TxLinkdownErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_linkdown_err_cnt),
+[C_TX_EGRESS_FIFO_UNDERRUN_OR_PARITY_ERR] = CNTR_ELEM(
+ "EgressFifoUnderrunOrParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_egress_fifi_underrun_or_parity_err_cnt),
+[C_TX_RESERVED_2] = CNTR_ELEM("Tx Egress Reserved 2", 0, 0,
+ CNTR_NORMAL,
+ access_egress_reserved_2_err_cnt),
+[C_TX_PKT_INTEGRITY_MEM_UNC_ERR] = CNTR_ELEM("TxPktIntegrityMemUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_pkt_integrity_mem_unc_err_cnt),
+[C_TX_PKT_INTEGRITY_MEM_COR_ERR] = CNTR_ELEM("TxPktIntegrityMemCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_tx_pkt_integrity_mem_cor_err_cnt),
+/* SendErrStatus */
+[C_SEND_CSR_WRITE_BAD_ADDR_ERR] = CNTR_ELEM("SendCsrWriteBadAddrErr", 0, 0,
+ CNTR_NORMAL,
+ access_send_csr_write_bad_addr_err_cnt),
+[C_SEND_CSR_READ_BAD_ADD_ERR] = CNTR_ELEM("SendCsrReadBadAddrErr", 0, 0,
+ CNTR_NORMAL,
+ access_send_csr_read_bad_addr_err_cnt),
+[C_SEND_CSR_PARITY_ERR] = CNTR_ELEM("SendCsrParityErr", 0, 0,
+ CNTR_NORMAL,
+ access_send_csr_parity_cnt),
+/* SendCtxtErrStatus */
+[C_PIO_WRITE_OUT_OF_BOUNDS_ERR] = CNTR_ELEM("PioWriteOutOfBoundsErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_write_out_of_bounds_err_cnt),
+[C_PIO_WRITE_OVERFLOW_ERR] = CNTR_ELEM("PioWriteOverflowErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_write_overflow_err_cnt),
+[C_PIO_WRITE_CROSSES_BOUNDARY_ERR] = CNTR_ELEM("PioWriteCrossesBoundaryErr",
+ 0, 0, CNTR_NORMAL,
+ access_pio_write_crosses_boundary_err_cnt),
+[C_PIO_DISALLOWED_PACKET_ERR] = CNTR_ELEM("PioDisallowedPacketErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_disallowed_packet_err_cnt),
+[C_PIO_INCONSISTENT_SOP_ERR] = CNTR_ELEM("PioInconsistentSopErr", 0, 0,
+ CNTR_NORMAL,
+ access_pio_inconsistent_sop_err_cnt),
+/* SendDmaEngErrStatus */
+[C_SDMA_HEADER_REQUEST_FIFO_COR_ERR] = CNTR_ELEM("SDmaHeaderRequestFifoCorErr",
+ 0, 0, CNTR_NORMAL,
+ access_sdma_header_request_fifo_cor_err_cnt),
+[C_SDMA_HEADER_STORAGE_COR_ERR] = CNTR_ELEM("SDmaHeaderStorageCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_header_storage_cor_err_cnt),
+[C_SDMA_PACKET_TRACKING_COR_ERR] = CNTR_ELEM("SDmaPacketTrackingCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_packet_tracking_cor_err_cnt),
+[C_SDMA_ASSEMBLY_COR_ERR] = CNTR_ELEM("SDmaAssemblyCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_assembly_cor_err_cnt),
+[C_SDMA_DESC_TABLE_COR_ERR] = CNTR_ELEM("SDmaDescTableCorErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_desc_table_cor_err_cnt),
+[C_SDMA_HEADER_REQUEST_FIFO_UNC_ERR] = CNTR_ELEM("SDmaHeaderRequestFifoUncErr",
+ 0, 0, CNTR_NORMAL,
+ access_sdma_header_request_fifo_unc_err_cnt),
+[C_SDMA_HEADER_STORAGE_UNC_ERR] = CNTR_ELEM("SDmaHeaderStorageUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_header_storage_unc_err_cnt),
+[C_SDMA_PACKET_TRACKING_UNC_ERR] = CNTR_ELEM("SDmaPacketTrackingUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_packet_tracking_unc_err_cnt),
+[C_SDMA_ASSEMBLY_UNC_ERR] = CNTR_ELEM("SDmaAssemblyUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_assembly_unc_err_cnt),
+[C_SDMA_DESC_TABLE_UNC_ERR] = CNTR_ELEM("SDmaDescTableUncErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_desc_table_unc_err_cnt),
+[C_SDMA_TIMEOUT_ERR] = CNTR_ELEM("SDmaTimeoutErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_timeout_err_cnt),
+[C_SDMA_HEADER_LENGTH_ERR] = CNTR_ELEM("SDmaHeaderLengthErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_header_length_err_cnt),
+[C_SDMA_HEADER_ADDRESS_ERR] = CNTR_ELEM("SDmaHeaderAddressErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_header_address_err_cnt),
+[C_SDMA_HEADER_SELECT_ERR] = CNTR_ELEM("SDmaHeaderSelectErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_header_select_err_cnt),
+[C_SMDA_RESERVED_9] = CNTR_ELEM("SDma Reserved 9", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_reserved_9_err_cnt),
+[C_SDMA_PACKET_DESC_OVERFLOW_ERR] = CNTR_ELEM("SDmaPacketDescOverflowErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_packet_desc_overflow_err_cnt),
+[C_SDMA_LENGTH_MISMATCH_ERR] = CNTR_ELEM("SDmaLengthMismatchErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_length_mismatch_err_cnt),
+[C_SDMA_HALT_ERR] = CNTR_ELEM("SDmaHaltErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_halt_err_cnt),
+[C_SDMA_MEM_READ_ERR] = CNTR_ELEM("SDmaMemReadErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_mem_read_err_cnt),
+[C_SDMA_FIRST_DESC_ERR] = CNTR_ELEM("SDmaFirstDescErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_first_desc_err_cnt),
+[C_SDMA_TAIL_OUT_OF_BOUNDS_ERR] = CNTR_ELEM("SDmaTailOutOfBoundsErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_tail_out_of_bounds_err_cnt),
+[C_SDMA_TOO_LONG_ERR] = CNTR_ELEM("SDmaTooLongErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_too_long_err_cnt),
+[C_SDMA_GEN_MISMATCH_ERR] = CNTR_ELEM("SDmaGenMismatchErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_gen_mismatch_err_cnt),
+[C_SDMA_WRONG_DW_ERR] = CNTR_ELEM("SDmaWrongDwErr", 0, 0,
+ CNTR_NORMAL,
+ access_sdma_wrong_dw_err_cnt),
};
static struct cntr_entry port_cntrs[PORT_CNTR_LAST] = {
@@ -1762,6 +4890,8 @@ static struct cntr_entry port_cntrs[PORT_CNTR_LAST] = {
access_sw_link_dn_cnt),
[C_SW_LINK_UP] = CNTR_ELEM("SwLinkUp", 0, 0, CNTR_SYNTH | CNTR_32BIT,
access_sw_link_up_cnt),
+[C_SW_UNKNOWN_FRAME] = CNTR_ELEM("UnknownFrame", 0, 0, CNTR_NORMAL,
+ access_sw_unknown_frame_cnt),
[C_SW_XMIT_DSCD] = CNTR_ELEM("XmitDscd", 0, 0, CNTR_SYNTH | CNTR_32BIT,
access_sw_xmit_discards),
[C_SW_XMIT_DSCD_VL] = CNTR_ELEM("XmitDscdVl", 0, 0,
@@ -1873,13 +5003,6 @@ static struct cntr_entry port_cntrs[PORT_CNTR_LAST] = {
/* ======================================================================== */
-/* return true if this is chip revision revision a0 */
-int is_a0(struct hfi1_devdata *dd)
-{
- return ((dd->revision >> CCE_REVISION_CHIP_REV_MINOR_SHIFT)
- & CCE_REVISION_CHIP_REV_MINOR_MASK) == 0;
-}
-
/* return true if this is chip revision revision a */
int is_ax(struct hfi1_devdata *dd)
{
@@ -1895,7 +5018,7 @@ int is_bx(struct hfi1_devdata *dd)
u8 chip_rev_minor =
dd->revision >> CCE_REVISION_CHIP_REV_MINOR_SHIFT
& CCE_REVISION_CHIP_REV_MINOR_MASK;
- return !!(chip_rev_minor & 0x10);
+ return (chip_rev_minor & 0xF0) == 0x10;
}
/*
@@ -2182,6 +5305,7 @@ static char *send_err_status_string(char *buf, int buf_len, u64 flags)
static void handle_cce_err(struct hfi1_devdata *dd, u32 unused, u64 reg)
{
char buf[96];
+ int i = 0;
/*
* For most these errors, there is nothing that can be done except
@@ -2190,13 +5314,20 @@ static void handle_cce_err(struct hfi1_devdata *dd, u32 unused, u64 reg)
dd_dev_info(dd, "CCE Error: %s\n",
cce_err_status_string(buf, sizeof(buf), reg));
- if ((reg & CCE_ERR_STATUS_CCE_CLI2_ASYNC_FIFO_PARITY_ERR_SMASK)
- && is_a0(dd)
- && (dd->icode != ICODE_FUNCTIONAL_SIMULATOR)) {
+ if ((reg & CCE_ERR_STATUS_CCE_CLI2_ASYNC_FIFO_PARITY_ERR_SMASK) &&
+ is_ax(dd) && (dd->icode != ICODE_FUNCTIONAL_SIMULATOR)) {
/* this error requires a manual drop into SPC freeze mode */
/* then a fix up */
start_freeze_handling(dd->pport, FREEZE_SELF);
}
+
+ for (i = 0; i < NUM_CCE_ERR_STATUS_COUNTERS; i++) {
+ if (reg & (1ull << i)) {
+ incr_cntr64(&dd->cce_err_status_cnt[i]);
+ /* maintain a counter over all cce_err_status errors */
+ incr_cntr64(&dd->sw_cce_err_status_aggregate);
+ }
+ }
}
/*
@@ -2241,6 +5372,7 @@ static void free_rcverr(struct hfi1_devdata *dd)
static void handle_rxe_err(struct hfi1_devdata *dd, u32 unused, u64 reg)
{
char buf[96];
+ int i = 0;
dd_dev_info(dd, "Receive Error: %s\n",
rxe_err_status_string(buf, sizeof(buf), reg));
@@ -2252,41 +5384,63 @@ static void handle_rxe_err(struct hfi1_devdata *dd, u32 unused, u64 reg)
* Freeze mode recovery is disabled for the errors
* in RXE_FREEZE_ABORT_MASK
*/
- if (is_a0(dd) && (reg & RXE_FREEZE_ABORT_MASK))
+ if (is_ax(dd) && (reg & RXE_FREEZE_ABORT_MASK))
flags = FREEZE_ABORT;
start_freeze_handling(dd->pport, flags);
}
+
+ for (i = 0; i < NUM_RCV_ERR_STATUS_COUNTERS; i++) {
+ if (reg & (1ull << i))
+ incr_cntr64(&dd->rcv_err_status_cnt[i]);
+ }
}
static void handle_misc_err(struct hfi1_devdata *dd, u32 unused, u64 reg)
{
char buf[96];
+ int i = 0;
dd_dev_info(dd, "Misc Error: %s",
misc_err_status_string(buf, sizeof(buf), reg));
+ for (i = 0; i < NUM_MISC_ERR_STATUS_COUNTERS; i++) {
+ if (reg & (1ull << i))
+ incr_cntr64(&dd->misc_err_status_cnt[i]);
+ }
}
static void handle_pio_err(struct hfi1_devdata *dd, u32 unused, u64 reg)
{
char buf[96];
+ int i = 0;
dd_dev_info(dd, "PIO Error: %s\n",
pio_err_status_string(buf, sizeof(buf), reg));
if (reg & ALL_PIO_FREEZE_ERR)
start_freeze_handling(dd->pport, 0);
+
+ for (i = 0; i < NUM_SEND_PIO_ERR_STATUS_COUNTERS; i++) {
+ if (reg & (1ull << i))
+ incr_cntr64(&dd->send_pio_err_status_cnt[i]);
+ }
}
static void handle_sdma_err(struct hfi1_devdata *dd, u32 unused, u64 reg)
{
char buf[96];
+ int i = 0;
dd_dev_info(dd, "SDMA Error: %s\n",
sdma_err_status_string(buf, sizeof(buf), reg));
if (reg & ALL_SDMA_FREEZE_ERR)
start_freeze_handling(dd->pport, 0);
+
+ for (i = 0; i < NUM_SEND_DMA_ERR_STATUS_COUNTERS; i++) {
+ if (reg & (1ull << i))
+ incr_cntr64(&dd->send_dma_err_status_cnt[i]);
+ }
}
static void count_port_inactive(struct hfi1_devdata *dd)
@@ -2352,10 +5506,11 @@ static void handle_egress_err(struct hfi1_devdata *dd, u32 unused, u64 reg)
{
u64 reg_copy = reg, handled = 0;
char buf[96];
+ int i = 0;
if (reg & ALL_TXE_EGRESS_FREEZE_ERR)
start_freeze_handling(dd->pport, 0);
- if (is_a0(dd) && (reg &
+ if (is_ax(dd) && (reg &
SEND_EGRESS_ERR_STATUS_TX_CREDIT_RETURN_VL_ERR_SMASK)
&& (dd->icode != ICODE_FUNCTIONAL_SIMULATOR))
start_freeze_handling(dd->pport, 0);
@@ -2383,15 +5538,25 @@ static void handle_egress_err(struct hfi1_devdata *dd, u32 unused, u64 reg)
if (reg)
dd_dev_info(dd, "Egress Error: %s\n",
egress_err_status_string(buf, sizeof(buf), reg));
+
+ for (i = 0; i < NUM_SEND_EGRESS_ERR_STATUS_COUNTERS; i++) {
+ if (reg & (1ull << i))
+ incr_cntr64(&dd->send_egress_err_status_cnt[i]);
+ }
}
static void handle_txe_err(struct hfi1_devdata *dd, u32 unused, u64 reg)
{
char buf[96];
+ int i = 0;
dd_dev_info(dd, "Send Error: %s\n",
send_err_status_string(buf, sizeof(buf), reg));
+ for (i = 0; i < NUM_SEND_ERR_STATUS_COUNTERS; i++) {
+ if (reg & (1ull << i))
+ incr_cntr64(&dd->send_err_status_cnt[i]);
+ }
}
/*
@@ -2483,6 +5648,7 @@ static void is_sendctxt_err_int(struct hfi1_devdata *dd,
char flags[96];
u64 status;
u32 sw_index;
+ int i = 0;
sw_index = dd->hw_to_sw[hw_context];
if (sw_index >= dd->num_send_contexts) {
@@ -2516,12 +5682,23 @@ static void is_sendctxt_err_int(struct hfi1_devdata *dd,
*/
if (sc->type != SC_USER)
queue_work(dd->pport->hfi1_wq, &sc->halt_work);
+
+ /*
+ * Update the counters for the corresponding status bits.
+ * Note that these particular counters are aggregated over all
+ * 160 contexts.
+ */
+ for (i = 0; i < NUM_SEND_CTXT_ERR_STATUS_COUNTERS; i++) {
+ if (status & (1ull << i))
+ incr_cntr64(&dd->sw_ctxt_err_status_cnt[i]);
+ }
}
static void handle_sdma_eng_err(struct hfi1_devdata *dd,
unsigned int source, u64 status)
{
struct sdma_engine *sde;
+ int i = 0;
sde = &dd->per_sdma[source];
#ifdef CONFIG_SDMA_VERBOSITY
@@ -2531,6 +5708,16 @@ static void handle_sdma_eng_err(struct hfi1_devdata *dd,
sde->this_idx, source, (unsigned long long)status);
#endif
sdma_engine_error(sde, status);
+
+ /*
+ * Update the counters for the corresponding status bits.
+ * Note that these particular counters are aggregated over
+ * all 16 DMA engines.
+ */
+ for (i = 0; i < NUM_SEND_DMA_ENG_ERR_STATUS_COUNTERS; i++) {
+ if (status & (1ull << i))
+ incr_cntr64(&dd->sw_send_dma_eng_err_status_cnt[i]);
+ }
}
/*
@@ -3050,7 +6237,7 @@ static void adjust_lcb_for_fpga_serdes(struct hfi1_devdata *dd)
/* else this is _p */
version = emulator_rev(dd);
- if (!is_a0(dd))
+ if (!is_ax(dd))
version = 0x2d; /* all B0 use 0x2d or higher settings */
if (version <= 0x12) {
@@ -3313,7 +6500,6 @@ void handle_freeze(struct work_struct *work)
struct hfi1_devdata *dd = ppd->dd;
/* wait for freeze indicators on all affected blocks */
- dd_dev_info(dd, "Entering SPC freeze\n");
wait_for_freeze_status(dd, 1);
/* SPC is now frozen */
@@ -3336,7 +6522,7 @@ void handle_freeze(struct work_struct *work)
write_csr(dd, CCE_CTRL, CCE_CTRL_SPC_UNFREEZE_SMASK);
wait_for_freeze_status(dd, 0);
- if (is_a0(dd)) {
+ if (is_ax(dd)) {
write_csr(dd, CCE_CTRL, CCE_CTRL_SPC_FREEZE_SMASK);
wait_for_freeze_status(dd, 1);
write_csr(dd, CCE_CTRL, CCE_CTRL_SPC_UNFREEZE_SMASK);
@@ -3371,7 +6557,6 @@ void handle_freeze(struct work_struct *work)
wake_up(&dd->event_queue);
/* no longer frozen */
- dd_dev_err(dd, "Exiting SPC freeze\n");
}
/*
@@ -3542,10 +6727,10 @@ static void add_full_mgmt_pkey(struct hfi1_pportdata *ppd)
{
struct hfi1_devdata *dd = ppd->dd;
- /* Sanity check - ppd->pkeys[2] should be 0 */
- if (ppd->pkeys[2] != 0)
- dd_dev_err(dd, "%s pkey[2] already set to 0x%x, resetting it to 0x%x\n",
- __func__, ppd->pkeys[2], FULL_MGMT_P_KEY);
+ /* Sanity check - ppd->pkeys[2] should be 0, or already initalized */
+ if (!((ppd->pkeys[2] == 0) || (ppd->pkeys[2] == FULL_MGMT_P_KEY)))
+ dd_dev_warn(dd, "%s pkey[2] already set to 0x%x, resetting it to 0x%x\n",
+ __func__, ppd->pkeys[2], FULL_MGMT_P_KEY);
ppd->pkeys[2] = FULL_MGMT_P_KEY;
(void)hfi1_set_ib_cfg(ppd, HFI1_IB_CFG_PKEYS, 0);
}
@@ -3864,7 +7049,7 @@ void handle_verify_cap(struct work_struct *work)
* REPLAY_BUF_MBE_SMASK
* FLIT_INPUT_BUF_MBE_SMASK
*/
- if (is_a0(dd)) { /* fixed in B0 */
+ if (is_ax(dd)) { /* fixed in B0 */
reg = read_csr(dd, DC_LCB_CFG_LINK_KILL_EN);
reg |= DC_LCB_CFG_LINK_KILL_EN_REPLAY_BUF_MBE_SMASK
| DC_LCB_CFG_LINK_KILL_EN_FLIT_INPUT_BUF_MBE_SMASK;
@@ -3907,18 +7092,32 @@ void handle_verify_cap(struct work_struct *work)
*/
void apply_link_downgrade_policy(struct hfi1_pportdata *ppd, int refresh_widths)
{
- int skip = 1;
int do_bounce = 0;
- u16 lwde = ppd->link_width_downgrade_enabled;
+ int tries;
+ u16 lwde;
u16 tx, rx;
+ /* use the hls lock to avoid a race with actual link up */
+ tries = 0;
+retry:
mutex_lock(&ppd->hls_lock);
/* only apply if the link is up */
- if (ppd->host_link_state & HLS_UP)
- skip = 0;
- mutex_unlock(&ppd->hls_lock);
- if (skip)
- return;
+ if (!(ppd->host_link_state & HLS_UP)) {
+ /* still going up..wait and retry */
+ if (ppd->host_link_state & HLS_GOING_UP) {
+ if (++tries < 1000) {
+ mutex_unlock(&ppd->hls_lock);
+ usleep_range(100, 120); /* arbitrary */
+ goto retry;
+ }
+ dd_dev_err(ppd->dd,
+ "%s: giving up waiting for link state change\n",
+ __func__);
+ }
+ goto done;
+ }
+
+ lwde = ppd->link_width_downgrade_enabled;
if (refresh_widths) {
get_link_widths(ppd->dd, &tx, &rx);
@@ -3956,6 +7155,9 @@ void apply_link_downgrade_policy(struct hfi1_pportdata *ppd, int refresh_widths)
do_bounce = 1;
}
+done:
+ mutex_unlock(&ppd->hls_lock);
+
if (do_bounce) {
set_link_down_reason(ppd, OPA_LINKDOWN_REASON_WIDTH_POLICY, 0,
OPA_LINKDOWN_REASON_WIDTH_POLICY);
@@ -4046,6 +7248,11 @@ static void handle_8051_interrupt(struct hfi1_devdata *dd, u32 unused, u64 reg)
}
err &= ~(u64)FAILED_LNI;
}
+ /* unknown frames can happen durning LNI, just count */
+ if (err & UNKNOWN_FRAME) {
+ ppd->unknown_frame_count++;
+ err &= ~(u64)UNKNOWN_FRAME;
+ }
if (err) {
/* report remaining errors, but do not do anything */
dd_dev_err(dd, "8051 info error: %s\n",
@@ -4774,13 +7981,25 @@ int read_lcb_csr(struct hfi1_devdata *dd, u32 addr, u64 *data)
*/
static int write_lcb_via_8051(struct hfi1_devdata *dd, u32 addr, u64 data)
{
+ u32 regno;
+ int ret;
- if (acquire_lcb_access(dd, 0) == 0) {
- write_csr(dd, addr, data);
- release_lcb_access(dd, 0);
- return 0;
+ if (dd->icode == ICODE_FUNCTIONAL_SIMULATOR ||
+ (dd->dc8051_ver < dc8051_ver(0, 20))) {
+ if (acquire_lcb_access(dd, 0) == 0) {
+ write_csr(dd, addr, data);
+ release_lcb_access(dd, 0);
+ return 0;
+ }
+ return -EBUSY;
}
- return -EBUSY;
+
+ /* register is an index of LCB registers: (offset - base) / 8 */
+ regno = (addr - DC_LCB_CFG_RUN) >> 3;
+ ret = do_8051_command(dd, HCMD_WRITE_LCB_CSR, regno, &data);
+ if (ret != HCMD_SUCCESS)
+ return -EBUSY;
+ return 0;
}
/*
@@ -4862,6 +8081,26 @@ static int do_8051_command(
*/
/*
+ * When writing a LCB CSR, out_data contains the full value to
+ * to be written, while in_data contains the relative LCB
+ * address in 7:0. Do the work here, rather than the caller,
+ * of distrubting the write data to where it needs to go:
+ *
+ * Write data
+ * 39:00 -> in_data[47:8]
+ * 47:40 -> DC8051_CFG_EXT_DEV_0.RETURN_CODE
+ * 63:48 -> DC8051_CFG_EXT_DEV_0.RSP_DATA
+ */
+ if (type == HCMD_WRITE_LCB_CSR) {
+ in_data |= ((*out_data) & 0xffffffffffull) << 8;
+ reg = ((((*out_data) >> 40) & 0xff) <<
+ DC_DC8051_CFG_EXT_DEV_0_RETURN_CODE_SHIFT)
+ | ((((*out_data) >> 48) & 0xffff) <<
+ DC_DC8051_CFG_EXT_DEV_0_RSP_DATA_SHIFT);
+ write_csr(dd, DC_DC8051_CFG_EXT_DEV_0, reg);
+ }
+
+ /*
* Do two writes: the first to stabilize the type and req_data, the
* second to activate.
*/
@@ -5856,6 +9095,23 @@ void init_qsfp(struct hfi1_pportdata *ppd)
}
}
+/*
+ * Do a one-time initialize of the LCB block.
+ */
+static void init_lcb(struct hfi1_devdata *dd)
+{
+ /* the DC has been reset earlier in the driver load */
+
+ /* set LCB for cclk loopback on the port */
+ write_csr(dd, DC_LCB_CFG_TX_FIFOS_RESET, 0x01);
+ write_csr(dd, DC_LCB_CFG_LANE_WIDTH, 0x00);
+ write_csr(dd, DC_LCB_CFG_REINIT_AS_SLAVE, 0x00);
+ write_csr(dd, DC_LCB_CFG_CNT_FOR_SKIP_STALL, 0x110);
+ write_csr(dd, DC_LCB_CFG_CLK_CNTR, 0x08);
+ write_csr(dd, DC_LCB_CFG_LOOPBACK, 0x02);
+ write_csr(dd, DC_LCB_CFG_TX_FIFOS_RESET, 0x00);
+}
+
int bringup_serdes(struct hfi1_pportdata *ppd)
{
struct hfi1_devdata *dd = ppd->dd;
@@ -5877,6 +9133,9 @@ int bringup_serdes(struct hfi1_pportdata *ppd)
/* Set linkinit_reason on power up per OPA spec */
ppd->linkinit_reason = OPA_LINKINIT_REASON_LINKUP;
+ /* one-time init of the LCB */
+ init_lcb(dd);
+
if (loopback) {
ret = init_loopback(dd);
if (ret < 0)
@@ -6148,7 +9407,8 @@ u32 lrh_max_header_bytes(struct hfi1_devdata *dd)
static void set_send_length(struct hfi1_pportdata *ppd)
{
struct hfi1_devdata *dd = ppd->dd;
- u32 max_hb = lrh_max_header_bytes(dd), maxvlmtu = 0, dcmtu;
+ u32 max_hb = lrh_max_header_bytes(dd), dcmtu;
+ u32 maxvlmtu = dd->vld[15].mtu;
u64 len1 = 0, len2 = (((dd->vld[15].mtu + max_hb) >> 2)
& SEND_LEN_CHECK1_LEN_VL15_MASK) <<
SEND_LEN_CHECK1_LEN_VL15_SHIFT;
@@ -6319,9 +9579,10 @@ static int goto_offline(struct hfi1_pportdata *ppd, u8 rem_reason)
* depending on how the link went down. The 8051 firmware
* will observe the needed wait time and only move to ready
* when that is completed. The largest of the quiet timeouts
- * is 2.5s, so wait that long and then a bit more.
+ * is 6s, so wait that long and then at least 0.5s more for
+ * other transitions, and another 0.5s for a buffer.
*/
- ret = wait_fm_ready(dd, 3000);
+ ret = wait_fm_ready(dd, 7000);
if (ret) {
dd_dev_err(dd,
"After going offline, timed out waiting for the 8051 to become ready to accept host requests\n");
@@ -7235,8 +10496,7 @@ static int set_buffer_control(struct hfi1_devdata *dd,
new_bc->vl[i].shared = 0;
}
new_total += be16_to_cpu(new_bc->overall_shared_limit);
- if (new_total > (u32)dd->link_credits)
- return -EINVAL;
+
/* fetch the current values */
get_buffer_control(dd, &cur_bc, &cur_total);
@@ -7282,8 +10542,8 @@ static int set_buffer_control(struct hfi1_devdata *dd,
*/
use_all_mask = 0;
if ((be16_to_cpu(new_bc->overall_shared_limit) <
- be16_to_cpu(cur_bc.overall_shared_limit))
- || (is_a0(dd) && any_shared_limit_changing)) {
+ be16_to_cpu(cur_bc.overall_shared_limit)) ||
+ (is_ax(dd) && any_shared_limit_changing)) {
set_global_shared(dd, 0);
cur_bc.overall_shared_limit = 0;
use_all_mask = 1;
@@ -7457,7 +10717,7 @@ int fm_set_table(struct hfi1_pportdata *ppd, int which, void *t)
*/
static int disable_data_vls(struct hfi1_devdata *dd)
{
- if (is_a0(dd))
+ if (is_ax(dd))
return 1;
pio_send_control(dd, PSC_DATA_VL_DISABLE);
@@ -7475,7 +10735,7 @@ static int disable_data_vls(struct hfi1_devdata *dd)
*/
int open_fill_data_vls(struct hfi1_devdata *dd)
{
- if (is_a0(dd))
+ if (is_ax(dd))
return 1;
pio_send_control(dd, PSC_DATA_VL_ENABLE);
@@ -7748,11 +11008,22 @@ void hfi1_rcvctrl(struct hfi1_devdata *dd, unsigned int op, int ctxt)
& RCV_TID_CTRL_TID_BASE_INDEX_MASK)
<< RCV_TID_CTRL_TID_BASE_INDEX_SHIFT);
write_kctxt_csr(dd, ctxt, RCV_TID_CTRL, reg);
- if (ctxt == VL15CTXT)
- write_csr(dd, RCV_VL15, VL15CTXT);
+ if (ctxt == HFI1_CTRL_CTXT)
+ write_csr(dd, RCV_VL15, HFI1_CTRL_CTXT);
}
if (op & HFI1_RCVCTRL_CTXT_DIS) {
write_csr(dd, RCV_VL15, 0);
+ /*
+ * When receive context is being disabled turn on tail
+ * update with a dummy tail address and then disable
+ * receive context.
+ */
+ if (dd->rcvhdrtail_dummy_physaddr) {
+ write_kctxt_csr(dd, ctxt, RCV_HDR_TAIL_ADDR,
+ dd->rcvhdrtail_dummy_physaddr);
+ rcvctrl |= RCV_CTXT_CTRL_TAIL_UPD_SMASK;
+ }
+
rcvctrl &= ~RCV_CTXT_CTRL_ENABLE_SMASK;
}
if (op & HFI1_RCVCTRL_INTRAVAIL_ENB)
@@ -7822,10 +11093,11 @@ void hfi1_rcvctrl(struct hfi1_devdata *dd, unsigned int op, int ctxt)
if (op & (HFI1_RCVCTRL_TAILUPD_DIS | HFI1_RCVCTRL_CTXT_DIS))
/*
* If the context has been disabled and the Tail Update has
- * been cleared, clear the RCV_HDR_TAIL_ADDR CSR so
- * it doesn't contain an address that is invalid.
+ * been cleared, set the RCV_HDR_TAIL_ADDR CSR to dummy address
+ * so it doesn't contain an address that is invalid.
*/
- write_kctxt_csr(dd, ctxt, RCV_HDR_TAIL_ADDR, 0);
+ write_kctxt_csr(dd, ctxt, RCV_HDR_TAIL_ADDR,
+ dd->rcvhdrtail_dummy_physaddr);
}
u32 hfi1_read_cntrs(struct hfi1_devdata *dd, loff_t pos, char **namep,
@@ -8785,7 +12057,7 @@ static void clean_up_interrupts(struct hfi1_devdata *dd)
/* turn off interrupts */
if (dd->num_msix_entries) {
/* MSI-X */
- hfi1_nomsix(dd);
+ pci_disable_msix(dd->pcidev);
} else {
/* INTx */
disable_intx(dd->pcidev);
@@ -8840,18 +12112,12 @@ static void remap_sdma_interrupts(struct hfi1_devdata *dd,
msix_intr);
}
-static void remap_receive_available_interrupt(struct hfi1_devdata *dd,
- int rx, int msix_intr)
-{
- remap_intr(dd, IS_RCVAVAIL_START + rx, msix_intr);
-}
-
static int request_intx_irq(struct hfi1_devdata *dd)
{
int ret;
- snprintf(dd->intx_name, sizeof(dd->intx_name), DRIVER_NAME"_%d",
- dd->unit);
+ snprintf(dd->intx_name, sizeof(dd->intx_name), DRIVER_NAME "_%d",
+ dd->unit);
ret = request_irq(dd->pcidev->irq, general_interrupt,
IRQF_SHARED, dd->intx_name, dd);
if (ret)
@@ -8870,7 +12136,7 @@ static int request_msix_irqs(struct hfi1_devdata *dd)
int first_general, last_general;
int first_sdma, last_sdma;
int first_rx, last_rx;
- int first_cpu, restart_cpu, curr_cpu;
+ int first_cpu, curr_cpu;
int rcv_cpu, sdma_cpu;
int i, ret = 0, possible;
int ht;
@@ -8909,22 +12175,19 @@ static int request_msix_irqs(struct hfi1_devdata *dd)
topology_sibling_cpumask(cpumask_first(local_mask)));
for (i = possible/ht; i < possible; i++)
cpumask_clear_cpu(i, def);
- /* reset possible */
- possible = cpumask_weight(def);
/* def now has full cores on chosen node*/
first_cpu = cpumask_first(def);
if (nr_cpu_ids >= first_cpu)
first_cpu++;
- restart_cpu = first_cpu;
- curr_cpu = restart_cpu;
+ curr_cpu = first_cpu;
- for (i = first_cpu; i < dd->n_krcv_queues + first_cpu; i++) {
+ /* One context is reserved as control context */
+ for (i = first_cpu; i < dd->n_krcv_queues + first_cpu - 1; i++) {
cpumask_clear_cpu(curr_cpu, def);
cpumask_set_cpu(curr_cpu, rcv);
- if (curr_cpu >= possible)
- curr_cpu = restart_cpu;
- else
- curr_cpu++;
+ curr_cpu = cpumask_next(curr_cpu, def);
+ if (curr_cpu >= nr_cpu_ids)
+ break;
}
/* def mask has non-rcv, rcv has recv mask */
rcv_cpu = cpumask_first(rcv);
@@ -8953,7 +12216,7 @@ static int request_msix_irqs(struct hfi1_devdata *dd)
handler = general_interrupt;
arg = dd;
snprintf(me->name, sizeof(me->name),
- DRIVER_NAME"_%d", dd->unit);
+ DRIVER_NAME "_%d", dd->unit);
err_info = "general";
} else if (first_sdma <= i && i < last_sdma) {
idx = i - first_sdma;
@@ -8961,7 +12224,7 @@ static int request_msix_irqs(struct hfi1_devdata *dd)
handler = sdma_interrupt;
arg = sde;
snprintf(me->name, sizeof(me->name),
- DRIVER_NAME"_%d sdma%d", dd->unit, idx);
+ DRIVER_NAME "_%d sdma%d", dd->unit, idx);
err_info = "sdma";
remap_sdma_interrupts(dd, idx, i);
} else if (first_rx <= i && i < last_rx) {
@@ -8981,9 +12244,9 @@ static int request_msix_irqs(struct hfi1_devdata *dd)
thread = receive_context_thread;
arg = rcd;
snprintf(me->name, sizeof(me->name),
- DRIVER_NAME"_%d kctxt%d", dd->unit, idx);
+ DRIVER_NAME "_%d kctxt%d", dd->unit, idx);
err_info = "receive context";
- remap_receive_available_interrupt(dd, idx, i);
+ remap_intr(dd, IS_RCVAVAIL_START + idx, i);
} else {
/* not in our expected range - complain, then
ignore it */
@@ -9018,17 +12281,26 @@ static int request_msix_irqs(struct hfi1_devdata *dd)
if (handler == sdma_interrupt) {
dd_dev_info(dd, "sdma engine %d cpu %d\n",
sde->this_idx, sdma_cpu);
+ sde->cpu = sdma_cpu;
cpumask_set_cpu(sdma_cpu, dd->msix_entries[i].mask);
sdma_cpu = cpumask_next(sdma_cpu, def);
if (sdma_cpu >= nr_cpu_ids)
sdma_cpu = cpumask_first(def);
} else if (handler == receive_context_interrupt) {
- dd_dev_info(dd, "rcv ctxt %d cpu %d\n",
- rcd->ctxt, rcv_cpu);
- cpumask_set_cpu(rcv_cpu, dd->msix_entries[i].mask);
- rcv_cpu = cpumask_next(rcv_cpu, rcv);
- if (rcv_cpu >= nr_cpu_ids)
- rcv_cpu = cpumask_first(rcv);
+ dd_dev_info(dd, "rcv ctxt %d cpu %d\n", rcd->ctxt,
+ (rcd->ctxt == HFI1_CTRL_CTXT) ?
+ cpumask_first(def) : rcv_cpu);
+ if (rcd->ctxt == HFI1_CTRL_CTXT) {
+ /* map to first default */
+ cpumask_set_cpu(cpumask_first(def),
+ dd->msix_entries[i].mask);
+ } else {
+ cpumask_set_cpu(rcv_cpu,
+ dd->msix_entries[i].mask);
+ rcv_cpu = cpumask_next(rcv_cpu, rcv);
+ if (rcv_cpu >= nr_cpu_ids)
+ rcv_cpu = cpumask_first(rcv);
+ }
} else {
/* otherwise first def */
dd_dev_info(dd, "%s cpu %d\n",
@@ -9153,7 +12425,6 @@ fail:
static int set_up_context_variables(struct hfi1_devdata *dd)
{
int num_kernel_contexts;
- int num_user_contexts;
int total_contexts;
int ret;
unsigned ngroups;
@@ -9161,11 +12432,18 @@ static int set_up_context_variables(struct hfi1_devdata *dd)
/*
* Kernel contexts: (to be fixed later):
* - min or 2 or 1 context/numa
- * - Context 0 - default/errors
- * - Context 1 - VL15
+ * - Context 0 - control context (VL15/multicast/error)
+ * - Context 1 - default context
*/
if (n_krcvqs)
- num_kernel_contexts = n_krcvqs + MIN_KERNEL_KCTXTS;
+ /*
+ * Don't count context 0 in n_krcvqs since
+ * is isn't used for normal verbs traffic.
+ *
+ * krcvqs will reflect number of kernel
+ * receive contexts above 0.
+ */
+ num_kernel_contexts = n_krcvqs + MIN_KERNEL_KCTXTS - 1;
else
num_kernel_contexts = num_online_nodes();
num_kernel_contexts =
@@ -9183,12 +12461,10 @@ static int set_up_context_variables(struct hfi1_devdata *dd)
}
/*
* User contexts: (to be fixed later)
- * - set to num_rcv_contexts if non-zero
- * - default to 1 user context per CPU
+ * - default to 1 user context per CPU if num_user_contexts is
+ * negative
*/
- if (num_rcv_contexts)
- num_user_contexts = num_rcv_contexts;
- else
+ if (num_user_contexts < 0)
num_user_contexts = num_online_cpus();
total_contexts = num_kernel_contexts + num_user_contexts;
@@ -9455,7 +12731,7 @@ static void reset_asic_csrs(struct hfi1_devdata *dd)
/* We might want to retain this state across FLR if we ever use it */
write_csr(dd, ASIC_CFG_DRV_STR, 0);
- write_csr(dd, ASIC_CFG_THERM_POLL_EN, 0);
+ /* ASIC_CFG_THERM_POLL_EN leave alone */
/* ASIC_STS_THERM read-only */
/* ASIC_CFG_RESET leave alone */
@@ -9906,7 +13182,7 @@ static void init_chip(struct hfi1_devdata *dd)
/* restore command and BARs */
restore_pci_variables(dd);
- if (is_a0(dd)) {
+ if (is_ax(dd)) {
dd_dev_info(dd, "Resetting CSRs with FLR\n");
hfi1_pcie_flr(dd);
restore_pci_variables(dd);
@@ -9925,23 +13201,20 @@ static void init_chip(struct hfi1_devdata *dd)
write_csr(dd, CCE_DC_CTRL, 0);
/* Set the LED off */
- if (is_a0(dd))
+ if (is_ax(dd))
setextled(dd, 0);
/*
* Clear the QSFP reset.
- * A0 leaves the out lines floating on power on, then on an FLR
- * enforces a 0 on all out pins. The driver does not touch
+ * An FLR enforces a 0 on all out pins. The driver does not touch
* ASIC_QSFPn_OUT otherwise. This leaves RESET_N low and
- * anything plugged constantly in reset, if it pays attention
+ * anything plugged constantly in reset, if it pays attention
* to RESET_N.
- * A prime example of this is SiPh. For now, set all pins high.
+ * Prime examples of this are optical cables. Set all pins high.
* I2CCLK and I2CDAT will change per direction, and INT_N and
* MODPRS_N are input only and their value is ignored.
*/
- if (is_a0(dd)) {
- write_csr(dd, ASIC_QSFP1_OUT, 0x1f);
- write_csr(dd, ASIC_QSFP2_OUT, 0x1f);
- }
+ write_csr(dd, ASIC_QSFP1_OUT, 0x1f);
+ write_csr(dd, ASIC_QSFP2_OUT, 0x1f);
}
static void init_early_variables(struct hfi1_devdata *dd)
@@ -9951,7 +13224,7 @@ static void init_early_variables(struct hfi1_devdata *dd)
/* assign link credit variables */
dd->vau = CM_VAU;
dd->link_credits = CM_GLOBAL_CREDITS;
- if (is_a0(dd))
+ if (is_ax(dd))
dd->link_credits--;
dd->vcu = cu_to_vcu(hfi1_cu);
/* enough room for 8 MAD packets plus header - 17K */
@@ -10017,12 +13290,6 @@ static void init_qpmap_table(struct hfi1_devdata *dd,
u64 ctxt = first_ctxt;
for (i = 0; i < 256;) {
- if (ctxt == VL15CTXT) {
- ctxt++;
- if (ctxt > last_ctxt)
- ctxt = first_ctxt;
- continue;
- }
reg |= ctxt << (8 * (i % 8));
i++;
ctxt++;
@@ -10065,7 +13332,7 @@ static void init_qos(struct hfi1_devdata *dd, u32 first_ctxt)
unsigned qpns_per_vl, ctxt, i, qpn, n = 1, m;
u64 *rsmmap;
u64 reg;
- u8 rxcontext = is_a0(dd) ? 0 : 0xff; /* 0 is default if a0 ver. */
+ u8 rxcontext = is_ax(dd) ? 0 : 0xff; /* 0 is default if a0 ver. */
/* validate */
if (dd->n_krcv_queues <= MIN_KERNEL_KCTXTS ||
@@ -10087,6 +13354,8 @@ static void init_qos(struct hfi1_devdata *dd, u32 first_ctxt)
if (num_vls * qpns_per_vl > dd->chip_rcv_contexts)
goto bail;
rsmmap = kmalloc_array(NUM_MAP_REGS, sizeof(u64), GFP_KERNEL);
+ if (!rsmmap)
+ goto bail;
memset(rsmmap, rxcontext, NUM_MAP_REGS * sizeof(u64));
/* init the local copy of the table */
for (i = 0, ctxt = first_ctxt; i < num_vls; i++) {
@@ -10135,19 +13404,13 @@ static void init_qos(struct hfi1_devdata *dd, u32 first_ctxt)
/* Enable RSM */
add_rcvctrl(dd, RCV_CTRL_RCV_RSM_ENABLE_SMASK);
kfree(rsmmap);
- /* map everything else (non-VL15) to context 0 */
- init_qpmap_table(
- dd,
- 0,
- 0);
+ /* map everything else to first context */
+ init_qpmap_table(dd, FIRST_KERNEL_KCTXT, MIN_KERNEL_KCTXTS - 1);
dd->qos_shift = n + 1;
return;
bail:
dd->qos_shift = 1;
- init_qpmap_table(
- dd,
- dd->n_krcv_queues > MIN_KERNEL_KCTXTS ? MIN_KERNEL_KCTXTS : 0,
- dd->n_krcv_queues - 1);
+ init_qpmap_table(dd, FIRST_KERNEL_KCTXT, dd->n_krcv_queues - 1);
}
static void init_rxe(struct hfi1_devdata *dd)
@@ -10276,7 +13539,7 @@ int hfi1_set_ctxt_jkey(struct hfi1_devdata *dd, unsigned ctxt, u16 jkey)
* Enable send-side J_KEY integrity check, unless this is A0 h/w
* (due to A0 erratum).
*/
- if (!is_a0(dd)) {
+ if (!is_ax(dd)) {
reg = read_kctxt_csr(dd, sctxt, SEND_CTXT_CHECK_ENABLE);
reg |= SEND_CTXT_CHECK_ENABLE_CHECK_JOB_KEY_SMASK;
write_kctxt_csr(dd, sctxt, SEND_CTXT_CHECK_ENABLE, reg);
@@ -10309,7 +13572,7 @@ int hfi1_clear_ctxt_jkey(struct hfi1_devdata *dd, unsigned ctxt)
* This check would not have been enabled for A0 h/w, see
* set_ctxt_jkey().
*/
- if (!is_a0(dd)) {
+ if (!is_ax(dd)) {
reg = read_kctxt_csr(dd, sctxt, SEND_CTXT_CHECK_ENABLE);
reg &= ~SEND_CTXT_CHECK_ENABLE_CHECK_JOB_KEY_SMASK;
write_kctxt_csr(dd, sctxt, SEND_CTXT_CHECK_ENABLE, reg);
@@ -10418,6 +13681,32 @@ static void asic_should_init(struct hfi1_devdata *dd)
spin_unlock_irqrestore(&hfi1_devs_lock, flags);
}
+/*
+ * Set dd->boardname. Use a generic name if a name is not returned from
+ * EFI variable space.
+ *
+ * Return 0 on success, -ENOMEM if space could not be allocated.
+ */
+static int obtain_boardname(struct hfi1_devdata *dd)
+{
+ /* generic board description */
+ const char generic[] =
+ "Intel Omni-Path Host Fabric Interface Adapter 100 Series";
+ unsigned long size;
+ int ret;
+
+ ret = read_hfi1_efi_var(dd, "description", &size,
+ (void **)&dd->boardname);
+ if (ret) {
+ dd_dev_err(dd, "Board description not found\n");
+ /* use generic description */
+ dd->boardname = kstrdup(generic, GFP_KERNEL);
+ if (!dd->boardname)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
/**
* Allocate and initialize the device structure for the hfi.
* @dev: the pci_dev for hfi1_ib device
@@ -10554,9 +13843,9 @@ struct hfi1_devdata *hfi1_init_dd(struct pci_dev *pdev,
/* insure num_vls isn't larger than number of sdma engines */
if (HFI1_CAP_IS_KSET(SDMA) && num_vls > dd->chip_sdma_engines) {
dd_dev_err(dd, "num_vls %u too large, using %u VLs\n",
- num_vls, HFI1_MAX_VLS_SUPPORTED);
- ppd->vls_supported = num_vls = HFI1_MAX_VLS_SUPPORTED;
- ppd->vls_operational = ppd->vls_supported;
+ num_vls, dd->chip_sdma_engines);
+ num_vls = dd->chip_sdma_engines;
+ ppd->vls_supported = dd->chip_sdma_engines;
}
/*
@@ -10615,18 +13904,13 @@ struct hfi1_devdata *hfi1_init_dd(struct pci_dev *pdev,
parse_platform_config(dd);
- /* add board names as they are defined */
- dd->boardname = kmalloc(64, GFP_KERNEL);
- if (!dd->boardname)
+ ret = obtain_boardname(dd);
+ if (ret)
goto bail_cleanup;
- snprintf(dd->boardname, 64, "Board ID 0x%llx",
- dd->revision >> CCE_REVISION_BOARD_ID_LOWER_NIBBLE_SHIFT
- & CCE_REVISION_BOARD_ID_LOWER_NIBBLE_MASK);
snprintf(dd->boardversion, BOARD_VERS_MAX,
- "ChipABI %u.%u, %s, ChipRev %u.%u, SW Compat %llu\n",
+ "ChipABI %u.%u, ChipRev %u.%u, SW Compat %llu\n",
HFI1_CHIP_VERS_MAJ, HFI1_CHIP_VERS_MIN,
- dd->boardname,
(u32)dd->majrev,
(u32)dd->minrev,
(dd->revision >> CCE_REVISION_SW_SHIFT)
@@ -10803,7 +14087,9 @@ static int thermal_init(struct hfi1_devdata *dd)
acquire_hw_mutex(dd);
dd_dev_info(dd, "Initializing thermal sensor\n");
-
+ /* Disable polling of thermal readings */
+ write_csr(dd, ASIC_CFG_THERM_POLL_EN, 0x0);
+ msleep(100);
/* Thermal Sensor Initialization */
/* Step 1: Reset the Thermal SBus Receiver */
ret = sbus_request_slow(dd, SBUS_THERMAL, 0x0,
diff --git a/drivers/staging/rdma/hfi1/chip.h b/drivers/staging/rdma/hfi1/chip.h
index ebf9041a1c5e..5b375ddc345d 100644
--- a/drivers/staging/rdma/hfi1/chip.h
+++ b/drivers/staging/rdma/hfi1/chip.h
@@ -235,6 +235,7 @@
#define HCMD_MISC 0x05
#define HCMD_READ_LCB_IDLE_MSG 0x06
#define HCMD_READ_LCB_CSR 0x07
+#define HCMD_WRITE_LCB_CSR 0x08
#define HCMD_INTERFACE_TEST 0xff
/* DC_DC8051_CFG_HOST_CMD_1.RETURN_CODE - 8051 host command return */
@@ -663,7 +664,6 @@ void get_linkup_link_widths(struct hfi1_pportdata *ppd);
void read_ltp_rtt(struct hfi1_devdata *dd);
void clear_linkup_counters(struct hfi1_devdata *dd);
u32 hdrqempty(struct hfi1_ctxtdata *rcd);
-int is_a0(struct hfi1_devdata *dd);
int is_ax(struct hfi1_devdata *dd);
int is_bx(struct hfi1_devdata *dd);
u32 read_physical_state(struct hfi1_devdata *dd);
@@ -721,7 +721,6 @@ enum {
C_RX_TID_FULL,
C_RX_TID_INVALID,
C_RX_TID_FLGMS,
- C_RX_CTX_RHQS,
C_RX_CTX_EGRS,
C_RCV_TID_FLSMS,
C_CCE_PCI_CR_ST,
@@ -788,6 +787,275 @@ enum {
C_SW_PIO_WAIT,
C_SW_KMEM_WAIT,
C_SW_SEND_SCHED,
+/* MISC_ERR_STATUS */
+ C_MISC_PLL_LOCK_FAIL_ERR,
+ C_MISC_MBIST_FAIL_ERR,
+ C_MISC_INVALID_EEP_CMD_ERR,
+ C_MISC_EFUSE_DONE_PARITY_ERR,
+ C_MISC_EFUSE_WRITE_ERR,
+ C_MISC_EFUSE_READ_BAD_ADDR_ERR,
+ C_MISC_EFUSE_CSR_PARITY_ERR,
+ C_MISC_FW_AUTH_FAILED_ERR,
+ C_MISC_KEY_MISMATCH_ERR,
+ C_MISC_SBUS_WRITE_FAILED_ERR,
+ C_MISC_CSR_WRITE_BAD_ADDR_ERR,
+ C_MISC_CSR_READ_BAD_ADDR_ERR,
+ C_MISC_CSR_PARITY_ERR,
+/* CceErrStatus */
+ /*
+ * A special counter that is the aggregate count
+ * of all the cce_err_status errors. The remainder
+ * are actual bits in the CceErrStatus register.
+ */
+ C_CCE_ERR_STATUS_AGGREGATED_CNT,
+ C_CCE_MSIX_CSR_PARITY_ERR,
+ C_CCE_INT_MAP_UNC_ERR,
+ C_CCE_INT_MAP_COR_ERR,
+ C_CCE_MSIX_TABLE_UNC_ERR,
+ C_CCE_MSIX_TABLE_COR_ERR,
+ C_CCE_RXDMA_CONV_FIFO_PARITY_ERR,
+ C_CCE_RCPL_ASYNC_FIFO_PARITY_ERR,
+ C_CCE_SEG_WRITE_BAD_ADDR_ERR,
+ C_CCE_SEG_READ_BAD_ADDR_ERR,
+ C_LA_TRIGGERED,
+ C_CCE_TRGT_CPL_TIMEOUT_ERR,
+ C_PCIC_RECEIVE_PARITY_ERR,
+ C_PCIC_TRANSMIT_BACK_PARITY_ERR,
+ C_PCIC_TRANSMIT_FRONT_PARITY_ERR,
+ C_PCIC_CPL_DAT_Q_UNC_ERR,
+ C_PCIC_CPL_HD_Q_UNC_ERR,
+ C_PCIC_POST_DAT_Q_UNC_ERR,
+ C_PCIC_POST_HD_Q_UNC_ERR,
+ C_PCIC_RETRY_SOT_MEM_UNC_ERR,
+ C_PCIC_RETRY_MEM_UNC_ERR,
+ C_PCIC_N_POST_DAT_Q_PARITY_ERR,
+ C_PCIC_N_POST_H_Q_PARITY_ERR,
+ C_PCIC_CPL_DAT_Q_COR_ERR,
+ C_PCIC_CPL_HD_Q_COR_ERR,
+ C_PCIC_POST_DAT_Q_COR_ERR,
+ C_PCIC_POST_HD_Q_COR_ERR,
+ C_PCIC_RETRY_SOT_MEM_COR_ERR,
+ C_PCIC_RETRY_MEM_COR_ERR,
+ C_CCE_CLI1_ASYNC_FIFO_DBG_PARITY_ERR,
+ C_CCE_CLI1_ASYNC_FIFO_RXDMA_PARITY_ERR,
+ C_CCE_CLI1_ASYNC_FIFO_SDMA_HD_PARITY_ERR,
+ C_CCE_CLI1_ASYNC_FIFO_PIO_CRDT_PARITY_ERR,
+ C_CCE_CLI2_ASYNC_FIFO_PARITY_ERR,
+ C_CCE_CSR_CFG_BUS_PARITY_ERR,
+ C_CCE_CLI0_ASYNC_FIFO_PARTIY_ERR,
+ C_CCE_RSPD_DATA_PARITY_ERR,
+ C_CCE_TRGT_ACCESS_ERR,
+ C_CCE_TRGT_ASYNC_FIFO_PARITY_ERR,
+ C_CCE_CSR_WRITE_BAD_ADDR_ERR,
+ C_CCE_CSR_READ_BAD_ADDR_ERR,
+ C_CCE_CSR_PARITY_ERR,
+/* RcvErrStatus */
+ C_RX_CSR_PARITY_ERR,
+ C_RX_CSR_WRITE_BAD_ADDR_ERR,
+ C_RX_CSR_READ_BAD_ADDR_ERR,
+ C_RX_DMA_CSR_UNC_ERR,
+ C_RX_DMA_DQ_FSM_ENCODING_ERR,
+ C_RX_DMA_EQ_FSM_ENCODING_ERR,
+ C_RX_DMA_CSR_PARITY_ERR,
+ C_RX_RBUF_DATA_COR_ERR,
+ C_RX_RBUF_DATA_UNC_ERR,
+ C_RX_DMA_DATA_FIFO_RD_COR_ERR,
+ C_RX_DMA_DATA_FIFO_RD_UNC_ERR,
+ C_RX_DMA_HDR_FIFO_RD_COR_ERR,
+ C_RX_DMA_HDR_FIFO_RD_UNC_ERR,
+ C_RX_RBUF_DESC_PART2_COR_ERR,
+ C_RX_RBUF_DESC_PART2_UNC_ERR,
+ C_RX_RBUF_DESC_PART1_COR_ERR,
+ C_RX_RBUF_DESC_PART1_UNC_ERR,
+ C_RX_HQ_INTR_FSM_ERR,
+ C_RX_HQ_INTR_CSR_PARITY_ERR,
+ C_RX_LOOKUP_CSR_PARITY_ERR,
+ C_RX_LOOKUP_RCV_ARRAY_COR_ERR,
+ C_RX_LOOKUP_RCV_ARRAY_UNC_ERR,
+ C_RX_LOOKUP_DES_PART2_PARITY_ERR,
+ C_RX_LOOKUP_DES_PART1_UNC_COR_ERR,
+ C_RX_LOOKUP_DES_PART1_UNC_ERR,
+ C_RX_RBUF_NEXT_FREE_BUF_COR_ERR,
+ C_RX_RBUF_NEXT_FREE_BUF_UNC_ERR,
+ C_RX_RBUF_FL_INIT_WR_ADDR_PARITY_ERR,
+ C_RX_RBUF_FL_INITDONE_PARITY_ERR,
+ C_RX_RBUF_FL_WRITE_ADDR_PARITY_ERR,
+ C_RX_RBUF_FL_RD_ADDR_PARITY_ERR,
+ C_RX_RBUF_EMPTY_ERR,
+ C_RX_RBUF_FULL_ERR,
+ C_RX_RBUF_BAD_LOOKUP_ERR,
+ C_RX_RBUF_CTX_ID_PARITY_ERR,
+ C_RX_RBUF_CSR_QEOPDW_PARITY_ERR,
+ C_RX_RBUF_CSR_Q_NUM_OF_PKT_PARITY_ERR,
+ C_RX_RBUF_CSR_Q_T1_PTR_PARITY_ERR,
+ C_RX_RBUF_CSR_Q_HD_PTR_PARITY_ERR,
+ C_RX_RBUF_CSR_Q_VLD_BIT_PARITY_ERR,
+ C_RX_RBUF_CSR_Q_NEXT_BUF_PARITY_ERR,
+ C_RX_RBUF_CSR_Q_ENT_CNT_PARITY_ERR,
+ C_RX_RBUF_CSR_Q_HEAD_BUF_NUM_PARITY_ERR,
+ C_RX_RBUF_BLOCK_LIST_READ_COR_ERR,
+ C_RX_RBUF_BLOCK_LIST_READ_UNC_ERR,
+ C_RX_RBUF_LOOKUP_DES_COR_ERR,
+ C_RX_RBUF_LOOKUP_DES_UNC_ERR,
+ C_RX_RBUF_LOOKUP_DES_REG_UNC_COR_ERR,
+ C_RX_RBUF_LOOKUP_DES_REG_UNC_ERR,
+ C_RX_RBUF_FREE_LIST_COR_ERR,
+ C_RX_RBUF_FREE_LIST_UNC_ERR,
+ C_RX_RCV_FSM_ENCODING_ERR,
+ C_RX_DMA_FLAG_COR_ERR,
+ C_RX_DMA_FLAG_UNC_ERR,
+ C_RX_DC_SOP_EOP_PARITY_ERR,
+ C_RX_RCV_CSR_PARITY_ERR,
+ C_RX_RCV_QP_MAP_TABLE_COR_ERR,
+ C_RX_RCV_QP_MAP_TABLE_UNC_ERR,
+ C_RX_RCV_DATA_COR_ERR,
+ C_RX_RCV_DATA_UNC_ERR,
+ C_RX_RCV_HDR_COR_ERR,
+ C_RX_RCV_HDR_UNC_ERR,
+ C_RX_DC_INTF_PARITY_ERR,
+ C_RX_DMA_CSR_COR_ERR,
+/* SendPioErrStatus */
+ C_PIO_PEC_SOP_HEAD_PARITY_ERR,
+ C_PIO_PCC_SOP_HEAD_PARITY_ERR,
+ C_PIO_LAST_RETURNED_CNT_PARITY_ERR,
+ C_PIO_CURRENT_FREE_CNT_PARITY_ERR,
+ C_PIO_RSVD_31_ERR,
+ C_PIO_RSVD_30_ERR,
+ C_PIO_PPMC_SOP_LEN_ERR,
+ C_PIO_PPMC_BQC_MEM_PARITY_ERR,
+ C_PIO_VL_FIFO_PARITY_ERR,
+ C_PIO_VLF_SOP_PARITY_ERR,
+ C_PIO_VLF_V1_LEN_PARITY_ERR,
+ C_PIO_BLOCK_QW_COUNT_PARITY_ERR,
+ C_PIO_WRITE_QW_VALID_PARITY_ERR,
+ C_PIO_STATE_MACHINE_ERR,
+ C_PIO_WRITE_DATA_PARITY_ERR,
+ C_PIO_HOST_ADDR_MEM_COR_ERR,
+ C_PIO_HOST_ADDR_MEM_UNC_ERR,
+ C_PIO_PKT_EVICT_SM_OR_ARM_SM_ERR,
+ C_PIO_INIT_SM_IN_ERR,
+ C_PIO_PPMC_PBL_FIFO_ERR,
+ C_PIO_CREDIT_RET_FIFO_PARITY_ERR,
+ C_PIO_V1_LEN_MEM_BANK1_COR_ERR,
+ C_PIO_V1_LEN_MEM_BANK0_COR_ERR,
+ C_PIO_V1_LEN_MEM_BANK1_UNC_ERR,
+ C_PIO_V1_LEN_MEM_BANK0_UNC_ERR,
+ C_PIO_SM_PKT_RESET_PARITY_ERR,
+ C_PIO_PKT_EVICT_FIFO_PARITY_ERR,
+ C_PIO_SBRDCTRL_CRREL_FIFO_PARITY_ERR,
+ C_PIO_SBRDCTL_CRREL_PARITY_ERR,
+ C_PIO_PEC_FIFO_PARITY_ERR,
+ C_PIO_PCC_FIFO_PARITY_ERR,
+ C_PIO_SB_MEM_FIFO1_ERR,
+ C_PIO_SB_MEM_FIFO0_ERR,
+ C_PIO_CSR_PARITY_ERR,
+ C_PIO_WRITE_ADDR_PARITY_ERR,
+ C_PIO_WRITE_BAD_CTXT_ERR,
+/* SendDmaErrStatus */
+ C_SDMA_PCIE_REQ_TRACKING_COR_ERR,
+ C_SDMA_PCIE_REQ_TRACKING_UNC_ERR,
+ C_SDMA_CSR_PARITY_ERR,
+ C_SDMA_RPY_TAG_ERR,
+/* SendEgressErrStatus */
+ C_TX_READ_PIO_MEMORY_CSR_UNC_ERR,
+ C_TX_READ_SDMA_MEMORY_CSR_UNC_ERR,
+ C_TX_EGRESS_FIFO_COR_ERR,
+ C_TX_READ_PIO_MEMORY_COR_ERR,
+ C_TX_READ_SDMA_MEMORY_COR_ERR,
+ C_TX_SB_HDR_COR_ERR,
+ C_TX_CREDIT_OVERRUN_ERR,
+ C_TX_LAUNCH_FIFO8_COR_ERR,
+ C_TX_LAUNCH_FIFO7_COR_ERR,
+ C_TX_LAUNCH_FIFO6_COR_ERR,
+ C_TX_LAUNCH_FIFO5_COR_ERR,
+ C_TX_LAUNCH_FIFO4_COR_ERR,
+ C_TX_LAUNCH_FIFO3_COR_ERR,
+ C_TX_LAUNCH_FIFO2_COR_ERR,
+ C_TX_LAUNCH_FIFO1_COR_ERR,
+ C_TX_LAUNCH_FIFO0_COR_ERR,
+ C_TX_CREDIT_RETURN_VL_ERR,
+ C_TX_HCRC_INSERTION_ERR,
+ C_TX_EGRESS_FIFI_UNC_ERR,
+ C_TX_READ_PIO_MEMORY_UNC_ERR,
+ C_TX_READ_SDMA_MEMORY_UNC_ERR,
+ C_TX_SB_HDR_UNC_ERR,
+ C_TX_CREDIT_RETURN_PARITY_ERR,
+ C_TX_LAUNCH_FIFO8_UNC_OR_PARITY_ERR,
+ C_TX_LAUNCH_FIFO7_UNC_OR_PARITY_ERR,
+ C_TX_LAUNCH_FIFO6_UNC_OR_PARITY_ERR,
+ C_TX_LAUNCH_FIFO5_UNC_OR_PARITY_ERR,
+ C_TX_LAUNCH_FIFO4_UNC_OR_PARITY_ERR,
+ C_TX_LAUNCH_FIFO3_UNC_OR_PARITY_ERR,
+ C_TX_LAUNCH_FIFO2_UNC_OR_PARITY_ERR,
+ C_TX_LAUNCH_FIFO1_UNC_OR_PARITY_ERR,
+ C_TX_LAUNCH_FIFO0_UNC_OR_PARITY_ERR,
+ C_TX_SDMA15_DISALLOWED_PACKET_ERR,
+ C_TX_SDMA14_DISALLOWED_PACKET_ERR,
+ C_TX_SDMA13_DISALLOWED_PACKET_ERR,
+ C_TX_SDMA12_DISALLOWED_PACKET_ERR,
+ C_TX_SDMA11_DISALLOWED_PACKET_ERR,
+ C_TX_SDMA10_DISALLOWED_PACKET_ERR,
+ C_TX_SDMA9_DISALLOWED_PACKET_ERR,
+ C_TX_SDMA8_DISALLOWED_PACKET_ERR,
+ C_TX_SDMA7_DISALLOWED_PACKET_ERR,
+ C_TX_SDMA6_DISALLOWED_PACKET_ERR,
+ C_TX_SDMA5_DISALLOWED_PACKET_ERR,
+ C_TX_SDMA4_DISALLOWED_PACKET_ERR,
+ C_TX_SDMA3_DISALLOWED_PACKET_ERR,
+ C_TX_SDMA2_DISALLOWED_PACKET_ERR,
+ C_TX_SDMA1_DISALLOWED_PACKET_ERR,
+ C_TX_SDMA0_DISALLOWED_PACKET_ERR,
+ C_TX_CONFIG_PARITY_ERR,
+ C_TX_SBRD_CTL_CSR_PARITY_ERR,
+ C_TX_LAUNCH_CSR_PARITY_ERR,
+ C_TX_ILLEGAL_CL_ERR,
+ C_TX_SBRD_CTL_STATE_MACHINE_PARITY_ERR,
+ C_TX_RESERVED_10,
+ C_TX_RESERVED_9,
+ C_TX_SDMA_LAUNCH_INTF_PARITY_ERR,
+ C_TX_PIO_LAUNCH_INTF_PARITY_ERR,
+ C_TX_RESERVED_6,
+ C_TX_INCORRECT_LINK_STATE_ERR,
+ C_TX_LINK_DOWN_ERR,
+ C_TX_EGRESS_FIFO_UNDERRUN_OR_PARITY_ERR,
+ C_TX_RESERVED_2,
+ C_TX_PKT_INTEGRITY_MEM_UNC_ERR,
+ C_TX_PKT_INTEGRITY_MEM_COR_ERR,
+/* SendErrStatus */
+ C_SEND_CSR_WRITE_BAD_ADDR_ERR,
+ C_SEND_CSR_READ_BAD_ADD_ERR,
+ C_SEND_CSR_PARITY_ERR,
+/* SendCtxtErrStatus */
+ C_PIO_WRITE_OUT_OF_BOUNDS_ERR,
+ C_PIO_WRITE_OVERFLOW_ERR,
+ C_PIO_WRITE_CROSSES_BOUNDARY_ERR,
+ C_PIO_DISALLOWED_PACKET_ERR,
+ C_PIO_INCONSISTENT_SOP_ERR,
+/*SendDmaEngErrStatus */
+ C_SDMA_HEADER_REQUEST_FIFO_COR_ERR,
+ C_SDMA_HEADER_STORAGE_COR_ERR,
+ C_SDMA_PACKET_TRACKING_COR_ERR,
+ C_SDMA_ASSEMBLY_COR_ERR,
+ C_SDMA_DESC_TABLE_COR_ERR,
+ C_SDMA_HEADER_REQUEST_FIFO_UNC_ERR,
+ C_SDMA_HEADER_STORAGE_UNC_ERR,
+ C_SDMA_PACKET_TRACKING_UNC_ERR,
+ C_SDMA_ASSEMBLY_UNC_ERR,
+ C_SDMA_DESC_TABLE_UNC_ERR,
+ C_SDMA_TIMEOUT_ERR,
+ C_SDMA_HEADER_LENGTH_ERR,
+ C_SDMA_HEADER_ADDRESS_ERR,
+ C_SDMA_HEADER_SELECT_ERR,
+ C_SMDA_RESERVED_9,
+ C_SDMA_PACKET_DESC_OVERFLOW_ERR,
+ C_SDMA_LENGTH_MISMATCH_ERR,
+ C_SDMA_HALT_ERR,
+ C_SDMA_MEM_READ_ERR,
+ C_SDMA_FIRST_DESC_ERR,
+ C_SDMA_TAIL_OUT_OF_BOUNDS_ERR,
+ C_SDMA_TOO_LONG_ERR,
+ C_SDMA_GEN_MISMATCH_ERR,
+ C_SDMA_WRONG_DW_ERR,
DEV_CNTR_LAST /* Must be kept last */
};
@@ -810,6 +1078,7 @@ enum {
C_RX_WORDS,
C_SW_LINK_DOWN,
C_SW_LINK_UP,
+ C_SW_UNKNOWN_FRAME,
C_SW_XMIT_DSCD,
C_SW_XMIT_DSCD_VL,
C_SW_XMIT_CSTR_ERR,
diff --git a/drivers/staging/rdma/hfi1/chip_registers.h b/drivers/staging/rdma/hfi1/chip_registers.h
index bf45de29d8bd..701e9e1012a6 100644
--- a/drivers/staging/rdma/hfi1/chip_registers.h
+++ b/drivers/staging/rdma/hfi1/chip_registers.h
@@ -318,6 +318,9 @@
#define DC_LCB_CFG_TX_FIFOS_RADR_RST_VAL_SHIFT 0
#define DC_LCB_CFG_TX_FIFOS_RESET (DC_LCB_CSRS + 0x000000000008)
#define DC_LCB_CFG_TX_FIFOS_RESET_VAL_SHIFT 0
+#define DC_LCB_CFG_REINIT_AS_SLAVE (DC_LCB_CSRS + 0x000000000030)
+#define DC_LCB_CFG_CNT_FOR_SKIP_STALL (DC_LCB_CSRS + 0x000000000040)
+#define DC_LCB_CFG_CLK_CNTR (DC_LCB_CSRS + 0x000000000110)
#define DC_LCB_ERR_CLR (DC_LCB_CSRS + 0x000000000308)
#define DC_LCB_ERR_EN (DC_LCB_CSRS + 0x000000000310)
#define DC_LCB_ERR_FLG (DC_LCB_CSRS + 0x000000000300)
@@ -379,7 +382,6 @@
#define DC_LCB_STS_ROUND_TRIP_LTP_CNT (DC_LCB_CSRS + 0x0000000004B0)
#define RCV_BUF_OVFL_CNT 10
#define RCV_CONTEXT_EGR_STALL 22
-#define RCV_CONTEXT_RHQ_STALL 21
#define RCV_DATA_PKT_CNT 0
#define RCV_DWORD_CNT 1
#define RCV_TID_FLOW_GEN_MISMATCH_CNT 20
diff --git a/drivers/staging/rdma/hfi1/common.h b/drivers/staging/rdma/hfi1/common.h
index 5e203239c5b0..5dd92720faae 100644
--- a/drivers/staging/rdma/hfi1/common.h
+++ b/drivers/staging/rdma/hfi1/common.h
@@ -132,13 +132,14 @@
* HFI1_CAP_RESERVED_MASK bits.
*/
#define HFI1_CAP_WRITABLE_MASK (HFI1_CAP_SDMA_AHG | \
- HFI1_CAP_HDRSUPP | \
- HFI1_CAP_MULTI_PKT_EGR | \
- HFI1_CAP_NODROP_RHQ_FULL | \
- HFI1_CAP_NODROP_EGR_FULL | \
- HFI1_CAP_ALLOW_PERM_JKEY | \
- HFI1_CAP_STATIC_RATE_CTRL | \
- HFI1_CAP_PRINT_UNIMPL)
+ HFI1_CAP_HDRSUPP | \
+ HFI1_CAP_MULTI_PKT_EGR | \
+ HFI1_CAP_NODROP_RHQ_FULL | \
+ HFI1_CAP_NODROP_EGR_FULL | \
+ HFI1_CAP_ALLOW_PERM_JKEY | \
+ HFI1_CAP_STATIC_RATE_CTRL | \
+ HFI1_CAP_PRINT_UNIMPL | \
+ HFI1_CAP_TID_UNMAP)
/*
* A set of capability bits that are "global" and are not allowed to be
* set in the user bitmask.
diff --git a/drivers/staging/rdma/hfi1/diag.c b/drivers/staging/rdma/hfi1/diag.c
index 88414d720469..0c8831705664 100644
--- a/drivers/staging/rdma/hfi1/diag.c
+++ b/drivers/staging/rdma/hfi1/diag.c
@@ -78,8 +78,8 @@
hfi1_cdbg(SNOOP, fmt, ##__VA_ARGS__)
/* Snoop option mask */
-#define SNOOP_DROP_SEND (1 << 0)
-#define SNOOP_USE_METADATA (1 << 1)
+#define SNOOP_DROP_SEND BIT(0)
+#define SNOOP_USE_METADATA BIT(1)
static u8 snoop_flags;
@@ -135,7 +135,7 @@ static struct cdev diagpkt_cdev;
static struct device *diagpkt_device;
static ssize_t diagpkt_write(struct file *fp, const char __user *data,
- size_t count, loff_t *off);
+ size_t count, loff_t *off);
static const struct file_operations diagpkt_file_ops = {
.owner = THIS_MODULE,
@@ -177,37 +177,37 @@ struct hfi1_link_info {
#define HFI1_SNOOP_IOCGETLINKSTATE \
_IO(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ)
#define HFI1_SNOOP_IOCSETLINKSTATE \
- _IO(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ+1)
+ _IO(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ + 1)
#define HFI1_SNOOP_IOCCLEARQUEUE \
- _IO(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ+2)
+ _IO(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ + 2)
#define HFI1_SNOOP_IOCCLEARFILTER \
- _IO(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ+3)
+ _IO(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ + 3)
#define HFI1_SNOOP_IOCSETFILTER \
- _IO(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ+4)
+ _IO(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ + 4)
#define HFI1_SNOOP_IOCGETVERSION \
- _IO(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ+5)
+ _IO(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ + 5)
#define HFI1_SNOOP_IOCSET_OPTS \
- _IO(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ+6)
+ _IO(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ + 6)
/*
* These offsets +6/+7 could change, but these are already known and used
* IOCTL numbers so don't change them without a good reason.
*/
#define HFI1_SNOOP_IOCGETLINKSTATE_EXTRA \
- _IOWR(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ+6, \
+ _IOWR(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ + 6, \
struct hfi1_link_info)
#define HFI1_SNOOP_IOCSETLINKSTATE_EXTRA \
- _IOWR(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ+7, \
+ _IOWR(HFI1_SNOOP_IOC_MAGIC, HFI1_SNOOP_IOC_BASE_SEQ + 7, \
struct hfi1_link_info)
static int hfi1_snoop_open(struct inode *in, struct file *fp);
static ssize_t hfi1_snoop_read(struct file *fp, char __user *data,
- size_t pkt_len, loff_t *off);
+ size_t pkt_len, loff_t *off);
static ssize_t hfi1_snoop_write(struct file *fp, const char __user *data,
- size_t count, loff_t *off);
+ size_t count, loff_t *off);
static long hfi1_ioctl(struct file *fp, unsigned int cmd, unsigned long arg);
static unsigned int hfi1_snoop_poll(struct file *fp,
- struct poll_table_struct *wait);
+ struct poll_table_struct *wait);
static int hfi1_snoop_release(struct inode *in, struct file *fp);
struct hfi1_packet_filter_command {
@@ -323,14 +323,12 @@ static void hfi1_snoop_remove(struct hfi1_devdata *dd)
void hfi1_diag_remove(struct hfi1_devdata *dd)
{
-
hfi1_snoop_remove(dd);
if (atomic_dec_and_test(&diagpkt_count))
hfi1_cdev_cleanup(&diagpkt_cdev, &diagpkt_device);
hfi1_cdev_cleanup(&dd->diag_cdev, &dd->diag_device);
}
-
/*
* Allocated structure shared between the credit return mechanism and
* diagpkt_send().
@@ -379,6 +377,7 @@ static ssize_t diagpkt_send(struct diag_pkt *dp)
pio_release_cb credit_cb = NULL;
void *credit_arg = NULL;
struct diagpkt_wait *wait = NULL;
+ int trycount = 0;
dd = hfi1_lookup(dp->unit);
if (!dd || !(dd->flags & HFI1_PRESENT) || !dd->kregbase) {
@@ -393,7 +392,7 @@ static ssize_t diagpkt_send(struct diag_pkt *dp)
if (dp->version != _DIAG_PKT_VERS) {
dd_dev_err(dd, "Invalid version %u for diagpkt_write\n",
- dp->version);
+ dp->version);
ret = -EINVAL;
goto bail;
}
@@ -440,7 +439,7 @@ static ssize_t diagpkt_send(struct diag_pkt *dp)
}
if (copy_from_user(tmpbuf,
- (const void __user *) (unsigned long) dp->data,
+ (const void __user *)(unsigned long)dp->data,
dp->len)) {
ret = -EFAULT;
goto bail;
@@ -493,8 +492,15 @@ static ssize_t diagpkt_send(struct diag_pkt *dp)
credit_arg = wait;
}
+retry:
pbuf = sc_buffer_alloc(sc, total_len, credit_cb, credit_arg);
if (!pbuf) {
+ if (trycount == 0) {
+ /* force a credit return and try again */
+ sc_return_credits(sc);
+ trycount = 1;
+ goto retry;
+ }
/*
* No send buffer means no credit callback. Undo
* the wait set-up that was done above. We free wait
@@ -530,9 +536,9 @@ static ssize_t diagpkt_send(struct diag_pkt *dp)
* NOTE: PRC_FILL_ERR is at best informational and cannot
* be depended on.
*/
- if (!ret && (((wait->code & PRC_STATUS_ERR)
- || (wait->code & PRC_FILL_ERR)
- || (wait->code & PRC_SC_DISABLE))))
+ if (!ret && (((wait->code & PRC_STATUS_ERR) ||
+ (wait->code & PRC_FILL_ERR) ||
+ (wait->code & PRC_SC_DISABLE))))
ret = -EIO;
put_diagpkt_wait(wait); /* finished with the structure */
@@ -545,7 +551,7 @@ bail:
}
static ssize_t diagpkt_write(struct file *fp, const char __user *data,
- size_t count, loff_t *off)
+ size_t count, loff_t *off)
{
struct hfi1_devdata *dd;
struct send_context *sc;
@@ -565,7 +571,7 @@ static ssize_t diagpkt_write(struct file *fp, const char __user *data,
*/
if (dp.pbc) {
dd = hfi1_lookup(dp.unit);
- if (dd == NULL)
+ if (!dd)
return -ENODEV;
vl = (dp.pbc >> PBC_VL_SHIFT) & PBC_VL_MASK;
sc = dd->vld[vl].sc;
@@ -598,7 +604,7 @@ static int hfi1_snoop_add(struct hfi1_devdata *dd, const char *name)
if (ret) {
dd_dev_err(dd, "Couldn't create %s device: %d", name, ret);
hfi1_cdev_cleanup(&dd->hfi1_snoop.cdev,
- &dd->hfi1_snoop.class_dev);
+ &dd->hfi1_snoop.class_dev);
}
return ret;
@@ -611,7 +617,6 @@ static struct hfi1_devdata *hfi1_dd_from_sc_inode(struct inode *in)
dd = hfi1_lookup(unit);
return dd;
-
}
/* clear or restore send context integrity checks */
@@ -652,7 +657,7 @@ static int hfi1_snoop_open(struct inode *in, struct file *fp)
mutex_lock(&hfi1_mutex);
dd = hfi1_dd_from_sc_inode(in);
- if (dd == NULL) {
+ if (!dd) {
ret = -ENODEV;
goto bail;
}
@@ -739,7 +744,7 @@ static int hfi1_snoop_release(struct inode *in, struct file *fp)
int mode_flag;
dd = hfi1_dd_from_sc_inode(in);
- if (dd == NULL)
+ if (!dd)
return -ENODEV;
spin_lock_irqsave(&dd->hfi1_snoop.snoop_lock, flags);
@@ -794,7 +799,7 @@ static unsigned int hfi1_snoop_poll(struct file *fp,
struct hfi1_devdata *dd;
dd = hfi1_dd_from_sc_inode(fp->f_inode);
- if (dd == NULL)
+ if (!dd)
return -ENODEV;
spin_lock_irqsave(&dd->hfi1_snoop.snoop_lock, flags);
@@ -805,7 +810,6 @@ static unsigned int hfi1_snoop_poll(struct file *fp,
spin_unlock_irqrestore(&dd->hfi1_snoop.snoop_lock, flags);
return ret;
-
}
static ssize_t hfi1_snoop_write(struct file *fp, const char __user *data,
@@ -822,7 +826,7 @@ static ssize_t hfi1_snoop_write(struct file *fp, const char __user *data,
struct hfi1_pportdata *ppd;
dd = hfi1_dd_from_sc_inode(fp->f_inode);
- if (dd == NULL)
+ if (!dd)
return -ENODEV;
ppd = dd->pport;
@@ -847,7 +851,7 @@ static ssize_t hfi1_snoop_write(struct file *fp, const char __user *data,
if (copy_from_user(&byte_one, data, 1))
return -EINVAL;
- if (copy_from_user(&byte_two, data+1, 1))
+ if (copy_from_user(&byte_two, data + 1, 1))
return -EINVAL;
sc4 = (byte_one >> 4) & 0xf;
@@ -920,7 +924,7 @@ static ssize_t hfi1_snoop_read(struct file *fp, char __user *data,
struct hfi1_devdata *dd;
dd = hfi1_dd_from_sc_inode(fp->f_inode);
- if (dd == NULL)
+ if (!dd)
return -ENODEV;
spin_lock_irqsave(&dd->hfi1_snoop.snoop_lock, flags);
@@ -946,16 +950,18 @@ static ssize_t hfi1_snoop_read(struct file *fp, char __user *data,
spin_unlock_irqrestore(&dd->hfi1_snoop.snoop_lock, flags);
if (pkt_len >= packet->total_len) {
if (copy_to_user(data, packet->data,
- packet->total_len))
+ packet->total_len))
ret = -EFAULT;
else
ret = packet->total_len;
- } else
+ } else {
ret = -EINVAL;
+ }
kfree(packet);
- } else
+ } else {
spin_unlock_irqrestore(&dd->hfi1_snoop.snoop_lock, flags);
+ }
return ret;
}
@@ -966,9 +972,9 @@ static long hfi1_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
void *filter_value = NULL;
long ret = 0;
int value = 0;
- u8 physState = 0;
- u8 linkState = 0;
- u16 devState = 0;
+ u8 phys_state = 0;
+ u8 link_state = 0;
+ u16 dev_state = 0;
unsigned long flags = 0;
unsigned long *argp = NULL;
struct hfi1_packet_filter_command filter_cmd = {0};
@@ -976,237 +982,221 @@ static long hfi1_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
struct hfi1_pportdata *ppd = NULL;
unsigned int index;
struct hfi1_link_info link_info;
+ int read_cmd, write_cmd, read_ok, write_ok;
dd = hfi1_dd_from_sc_inode(fp->f_inode);
- if (dd == NULL)
+ if (!dd)
return -ENODEV;
- spin_lock_irqsave(&dd->hfi1_snoop.snoop_lock, flags);
-
mode_flag = dd->hfi1_snoop.mode_flag;
+ read_cmd = _IOC_DIR(cmd) & _IOC_READ;
+ write_cmd = _IOC_DIR(cmd) & _IOC_WRITE;
+ write_ok = access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
+ read_ok = access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
- if (((_IOC_DIR(cmd) & _IOC_READ)
- && !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)))
- || ((_IOC_DIR(cmd) & _IOC_WRITE)
- && !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)))) {
- ret = -EFAULT;
- } else if (!capable(CAP_SYS_ADMIN)) {
- ret = -EPERM;
- } else if ((mode_flag & HFI1_PORT_CAPTURE_MODE) &&
- (cmd != HFI1_SNOOP_IOCCLEARQUEUE) &&
- (cmd != HFI1_SNOOP_IOCCLEARFILTER) &&
- (cmd != HFI1_SNOOP_IOCSETFILTER)) {
+ if ((read_cmd && !write_ok) || (write_cmd && !read_ok))
+ return -EFAULT;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if ((mode_flag & HFI1_PORT_CAPTURE_MODE) &&
+ (cmd != HFI1_SNOOP_IOCCLEARQUEUE) &&
+ (cmd != HFI1_SNOOP_IOCCLEARFILTER) &&
+ (cmd != HFI1_SNOOP_IOCSETFILTER))
/* Capture devices are allowed only 3 operations
* 1.Clear capture queue
* 2.Clear capture filter
* 3.Set capture filter
* Other are invalid.
*/
- ret = -EINVAL;
- } else {
- switch (cmd) {
- case HFI1_SNOOP_IOCSETLINKSTATE:
- snoop_dbg("HFI1_SNOOP_IOCSETLINKSTATE is not valid");
- ret = -EINVAL;
- break;
+ return -EINVAL;
- case HFI1_SNOOP_IOCSETLINKSTATE_EXTRA:
- memset(&link_info, 0, sizeof(link_info));
+ switch (cmd) {
+ case HFI1_SNOOP_IOCSETLINKSTATE_EXTRA:
+ memset(&link_info, 0, sizeof(link_info));
- if (copy_from_user(&link_info,
- (struct hfi1_link_info __user *)arg,
- sizeof(link_info)))
- ret = -EFAULT;
+ if (copy_from_user(&link_info,
+ (struct hfi1_link_info __user *)arg,
+ sizeof(link_info)))
+ return -EFAULT;
- value = link_info.port_state;
- index = link_info.port_number;
- if (index > dd->num_pports - 1) {
- ret = -EINVAL;
- break;
- }
+ value = link_info.port_state;
+ index = link_info.port_number;
+ if (index > dd->num_pports - 1)
+ return -EINVAL;
- ppd = &dd->pport[index];
- if (!ppd) {
- ret = -EINVAL;
- break;
- }
+ ppd = &dd->pport[index];
+ if (!ppd)
+ return -EINVAL;
- /* What we want to transition to */
- physState = (value >> 4) & 0xF;
- linkState = value & 0xF;
- snoop_dbg("Setting link state 0x%x", value);
-
- switch (linkState) {
- case IB_PORT_NOP:
- if (physState == 0)
- break;
- /* fall through */
- case IB_PORT_DOWN:
- switch (physState) {
- case 0:
- devState = HLS_DN_DOWNDEF;
- break;
- case 2:
- devState = HLS_DN_POLL;
- break;
- case 3:
- devState = HLS_DN_DISABLE;
- break;
- default:
- ret = -EINVAL;
- goto done;
- }
- ret = set_link_state(ppd, devState);
- break;
- case IB_PORT_ARMED:
- ret = set_link_state(ppd, HLS_UP_ARMED);
- if (!ret)
- send_idle_sma(dd, SMA_IDLE_ARM);
- break;
- case IB_PORT_ACTIVE:
- ret = set_link_state(ppd, HLS_UP_ACTIVE);
- if (!ret)
- send_idle_sma(dd, SMA_IDLE_ACTIVE);
- break;
- default:
- ret = -EINVAL;
- break;
- }
+ /* What we want to transition to */
+ phys_state = (value >> 4) & 0xF;
+ link_state = value & 0xF;
+ snoop_dbg("Setting link state 0x%x", value);
- if (ret)
+ switch (link_state) {
+ case IB_PORT_NOP:
+ if (phys_state == 0)
break;
- /* fall through */
- case HFI1_SNOOP_IOCGETLINKSTATE:
- case HFI1_SNOOP_IOCGETLINKSTATE_EXTRA:
- if (cmd == HFI1_SNOOP_IOCGETLINKSTATE_EXTRA) {
- memset(&link_info, 0, sizeof(link_info));
- if (copy_from_user(&link_info,
- (struct hfi1_link_info __user *)arg,
- sizeof(link_info)))
- ret = -EFAULT;
- index = link_info.port_number;
- } else {
- ret = __get_user(index, (int __user *) arg);
- if (ret != 0)
- break;
- }
-
- if (index > dd->num_pports - 1) {
- ret = -EINVAL;
+ /* fall through */
+ case IB_PORT_DOWN:
+ switch (phys_state) {
+ case 0:
+ dev_state = HLS_DN_DOWNDEF;
break;
- }
-
- ppd = &dd->pport[index];
- if (!ppd) {
- ret = -EINVAL;
+ case 2:
+ dev_state = HLS_DN_POLL;
break;
+ case 3:
+ dev_state = HLS_DN_DISABLE;
+ break;
+ default:
+ return -EINVAL;
}
- value = hfi1_ibphys_portstate(ppd);
- value <<= 4;
- value |= driver_lstate(ppd);
-
- snoop_dbg("Link port | Link State: %d", value);
-
- if ((cmd == HFI1_SNOOP_IOCGETLINKSTATE_EXTRA) ||
- (cmd == HFI1_SNOOP_IOCSETLINKSTATE_EXTRA)) {
- link_info.port_state = value;
- link_info.node_guid = cpu_to_be64(ppd->guid);
- link_info.link_speed_active =
- ppd->link_speed_active;
- link_info.link_width_active =
- ppd->link_width_active;
- if (copy_to_user(
- (struct hfi1_link_info __user *)arg,
- &link_info, sizeof(link_info)))
- ret = -EFAULT;
- } else {
- ret = __put_user(value, (int __user *)arg);
- }
+ ret = set_link_state(ppd, dev_state);
break;
-
- case HFI1_SNOOP_IOCCLEARQUEUE:
- snoop_dbg("Clearing snoop queue");
- drain_snoop_list(&dd->hfi1_snoop.queue);
+ case IB_PORT_ARMED:
+ ret = set_link_state(ppd, HLS_UP_ARMED);
+ if (!ret)
+ send_idle_sma(dd, SMA_IDLE_ARM);
break;
-
- case HFI1_SNOOP_IOCCLEARFILTER:
- snoop_dbg("Clearing filter");
- if (dd->hfi1_snoop.filter_callback) {
- /* Drain packets first */
- drain_snoop_list(&dd->hfi1_snoop.queue);
- dd->hfi1_snoop.filter_callback = NULL;
- }
- kfree(dd->hfi1_snoop.filter_value);
- dd->hfi1_snoop.filter_value = NULL;
+ case IB_PORT_ACTIVE:
+ ret = set_link_state(ppd, HLS_UP_ACTIVE);
+ if (!ret)
+ send_idle_sma(dd, SMA_IDLE_ACTIVE);
break;
+ default:
+ return -EINVAL;
+ }
- case HFI1_SNOOP_IOCSETFILTER:
- snoop_dbg("Setting filter");
- /* just copy command structure */
- argp = (unsigned long *)arg;
- if (copy_from_user(&filter_cmd, (void __user *)argp,
- sizeof(filter_cmd))) {
- ret = -EFAULT;
- break;
- }
- if (filter_cmd.opcode >= HFI1_MAX_FILTERS) {
- pr_alert("Invalid opcode in request\n");
- ret = -EINVAL;
+ if (ret)
+ break;
+ /* fall through */
+ case HFI1_SNOOP_IOCGETLINKSTATE:
+ case HFI1_SNOOP_IOCGETLINKSTATE_EXTRA:
+ if (cmd == HFI1_SNOOP_IOCGETLINKSTATE_EXTRA) {
+ memset(&link_info, 0, sizeof(link_info));
+ if (copy_from_user(&link_info,
+ (struct hfi1_link_info __user *)arg,
+ sizeof(link_info)))
+ return -EFAULT;
+ index = link_info.port_number;
+ } else {
+ ret = __get_user(index, (int __user *)arg);
+ if (ret != 0)
break;
- }
+ }
- snoop_dbg("Opcode %d Len %d Ptr %p",
- filter_cmd.opcode, filter_cmd.length,
- filter_cmd.value_ptr);
+ if (index > dd->num_pports - 1)
+ return -EINVAL;
- filter_value = kcalloc(filter_cmd.length, sizeof(u8),
- GFP_KERNEL);
- if (!filter_value) {
- pr_alert("Not enough memory\n");
- ret = -ENOMEM;
- break;
- }
- /* copy remaining data from userspace */
- if (copy_from_user((u8 *)filter_value,
- (void __user *)filter_cmd.value_ptr,
- filter_cmd.length)) {
- kfree(filter_value);
- ret = -EFAULT;
- break;
- }
+ ppd = &dd->pport[index];
+ if (!ppd)
+ return -EINVAL;
+
+ value = hfi1_ibphys_portstate(ppd);
+ value <<= 4;
+ value |= driver_lstate(ppd);
+
+ snoop_dbg("Link port | Link State: %d", value);
+
+ if ((cmd == HFI1_SNOOP_IOCGETLINKSTATE_EXTRA) ||
+ (cmd == HFI1_SNOOP_IOCSETLINKSTATE_EXTRA)) {
+ link_info.port_state = value;
+ link_info.node_guid = cpu_to_be64(ppd->guid);
+ link_info.link_speed_active =
+ ppd->link_speed_active;
+ link_info.link_width_active =
+ ppd->link_width_active;
+ if (copy_to_user((struct hfi1_link_info __user *)arg,
+ &link_info, sizeof(link_info)))
+ return -EFAULT;
+ } else {
+ ret = __put_user(value, (int __user *)arg);
+ }
+ break;
+
+ case HFI1_SNOOP_IOCCLEARQUEUE:
+ snoop_dbg("Clearing snoop queue");
+ spin_lock_irqsave(&dd->hfi1_snoop.snoop_lock, flags);
+ drain_snoop_list(&dd->hfi1_snoop.queue);
+ spin_unlock_irqrestore(&dd->hfi1_snoop.snoop_lock, flags);
+ break;
+
+ case HFI1_SNOOP_IOCCLEARFILTER:
+ snoop_dbg("Clearing filter");
+ spin_lock_irqsave(&dd->hfi1_snoop.snoop_lock, flags);
+ if (dd->hfi1_snoop.filter_callback) {
/* Drain packets first */
drain_snoop_list(&dd->hfi1_snoop.queue);
- dd->hfi1_snoop.filter_callback =
- hfi1_filters[filter_cmd.opcode].filter;
- /* just in case we see back to back sets */
- kfree(dd->hfi1_snoop.filter_value);
- dd->hfi1_snoop.filter_value = filter_value;
+ dd->hfi1_snoop.filter_callback = NULL;
+ }
+ kfree(dd->hfi1_snoop.filter_value);
+ dd->hfi1_snoop.filter_value = NULL;
+ spin_unlock_irqrestore(&dd->hfi1_snoop.snoop_lock, flags);
+ break;
- break;
- case HFI1_SNOOP_IOCGETVERSION:
- value = SNOOP_CAPTURE_VERSION;
- snoop_dbg("Getting version: %d", value);
- ret = __put_user(value, (int __user *)arg);
- break;
- case HFI1_SNOOP_IOCSET_OPTS:
- snoop_flags = 0;
- ret = __get_user(value, (int __user *) arg);
- if (ret != 0)
- break;
+ case HFI1_SNOOP_IOCSETFILTER:
+ snoop_dbg("Setting filter");
+ /* just copy command structure */
+ argp = (unsigned long *)arg;
+ if (copy_from_user(&filter_cmd, (void __user *)argp,
+ sizeof(filter_cmd)))
+ return -EFAULT;
- snoop_dbg("Setting snoop option %d", value);
- if (value & SNOOP_DROP_SEND)
- snoop_flags |= SNOOP_DROP_SEND;
- if (value & SNOOP_USE_METADATA)
- snoop_flags |= SNOOP_USE_METADATA;
- break;
- default:
- ret = -ENOTTY;
- break;
+ if (filter_cmd.opcode >= HFI1_MAX_FILTERS) {
+ pr_alert("Invalid opcode in request\n");
+ return -EINVAL;
+ }
+
+ snoop_dbg("Opcode %d Len %d Ptr %p",
+ filter_cmd.opcode, filter_cmd.length,
+ filter_cmd.value_ptr);
+
+ filter_value = kcalloc(filter_cmd.length, sizeof(u8),
+ GFP_KERNEL);
+ if (!filter_value)
+ return -ENOMEM;
+
+ /* copy remaining data from userspace */
+ if (copy_from_user((u8 *)filter_value,
+ (void __user *)filter_cmd.value_ptr,
+ filter_cmd.length)) {
+ kfree(filter_value);
+ return -EFAULT;
}
+ /* Drain packets first */
+ spin_lock_irqsave(&dd->hfi1_snoop.snoop_lock, flags);
+ drain_snoop_list(&dd->hfi1_snoop.queue);
+ dd->hfi1_snoop.filter_callback =
+ hfi1_filters[filter_cmd.opcode].filter;
+ /* just in case we see back to back sets */
+ kfree(dd->hfi1_snoop.filter_value);
+ dd->hfi1_snoop.filter_value = filter_value;
+ spin_unlock_irqrestore(&dd->hfi1_snoop.snoop_lock, flags);
+ break;
+ case HFI1_SNOOP_IOCGETVERSION:
+ value = SNOOP_CAPTURE_VERSION;
+ snoop_dbg("Getting version: %d", value);
+ ret = __put_user(value, (int __user *)arg);
+ break;
+ case HFI1_SNOOP_IOCSET_OPTS:
+ snoop_flags = 0;
+ ret = __get_user(value, (int __user *)arg);
+ if (ret != 0)
+ break;
+
+ snoop_dbg("Setting snoop option %d", value);
+ if (value & SNOOP_DROP_SEND)
+ snoop_flags |= SNOOP_DROP_SEND;
+ if (value & SNOOP_USE_METADATA)
+ snoop_flags |= SNOOP_USE_METADATA;
+ break;
+ default:
+ return -ENOTTY;
}
-done:
- spin_unlock_irqrestore(&dd->hfi1_snoop.snoop_lock, flags);
+
return ret;
}
@@ -1321,7 +1311,6 @@ static int hfi1_filter_mad_mgmt_class(void *ibhdr, void *packet_data,
static int hfi1_filter_qp_number(void *ibhdr, void *packet_data, void *value)
{
-
struct hfi1_ib_header *hdr;
struct hfi1_other_headers *ohdr = NULL;
int ret;
@@ -1404,7 +1393,6 @@ static int hfi1_filter_ib_service_level(void *ibhdr, void *packet_data,
static int hfi1_filter_ib_pkey(void *ibhdr, void *packet_data, void *value)
{
-
u32 lnh = 0;
struct hfi1_ib_header *hdr;
struct hfi1_other_headers *ohdr = NULL;
@@ -1476,16 +1464,14 @@ static struct snoop_packet *allocate_snoop_packet(u32 hdr_len,
u32 data_len,
u32 md_len)
{
-
struct snoop_packet *packet;
- packet = kzalloc(sizeof(struct snoop_packet) + hdr_len + data_len
+ packet = kzalloc(sizeof(*packet) + hdr_len + data_len
+ md_len,
GFP_ATOMIC | __GFP_NOWARN);
if (likely(packet))
INIT_LIST_HEAD(&packet->list);
-
return packet;
}
@@ -1542,12 +1528,11 @@ int snoop_recv_handler(struct hfi1_packet *packet)
unlikely(snoop_flags & SNOOP_USE_METADATA))
md_len = sizeof(struct capture_md);
-
s_packet = allocate_snoop_packet(header_size,
tlen - header_size,
md_len);
- if (unlikely(s_packet == NULL)) {
+ if (unlikely(!s_packet)) {
dd_dev_warn_ratelimited(ppd->dd, "Unable to allocate snoop/capture packet\n");
break;
}
@@ -1618,14 +1603,12 @@ int snoop_recv_handler(struct hfi1_packet *packet)
/*
* Handle snooping and capturing packets when sdma is being used.
*/
-int snoop_send_dma_handler(struct hfi1_qp *qp, struct ahg_ib_header *ibhdr,
- u32 hdrwords, struct hfi1_sge_state *ss, u32 len,
- u32 plen, u32 dwords, u64 pbc)
+int snoop_send_dma_handler(struct hfi1_qp *qp, struct hfi1_pkt_state *ps,
+ u64 pbc)
{
- pr_alert("Snooping/Capture of Send DMA Packets Is Not Supported!\n");
+ pr_alert("Snooping/Capture of Send DMA Packets Is Not Supported!\n");
snoop_dbg("Unsupported Operation");
- return hfi1_verbs_send_dma(qp, ibhdr, hdrwords, ss, len, plen, dwords,
- 0);
+ return hfi1_verbs_send_dma(qp, ps, 0);
}
/*
@@ -1633,12 +1616,16 @@ int snoop_send_dma_handler(struct hfi1_qp *qp, struct ahg_ib_header *ibhdr,
* bypass packets. The only way to send a bypass packet currently is to use the
* diagpkt interface. When that interface is enable snoop/capture is not.
*/
-int snoop_send_pio_handler(struct hfi1_qp *qp, struct ahg_ib_header *ahdr,
- u32 hdrwords, struct hfi1_sge_state *ss, u32 len,
- u32 plen, u32 dwords, u64 pbc)
+int snoop_send_pio_handler(struct hfi1_qp *qp, struct hfi1_pkt_state *ps,
+ u64 pbc)
{
- struct hfi1_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num);
- struct hfi1_pportdata *ppd = ppd_from_ibp(ibp);
+ struct ahg_ib_header *ahdr = qp->s_hdr;
+ u32 hdrwords = qp->s_hdrwords;
+ struct hfi1_sge_state *ss = qp->s_cur_sge;
+ u32 len = qp->s_cur_size;
+ u32 dwords = (len + 3) >> 2;
+ u32 plen = hdrwords + dwords + 2; /* includes pbc */
+ struct hfi1_pportdata *ppd = ps->ppd;
struct snoop_packet *s_packet = NULL;
u32 *hdr = (u32 *)&ahdr->ibh;
u32 length = 0;
@@ -1666,7 +1653,7 @@ int snoop_send_pio_handler(struct hfi1_qp *qp, struct ahg_ib_header *ahdr,
/* not using ss->total_len as arg 2 b/c that does not count CRC */
s_packet = allocate_snoop_packet(hdr_len, tlen - hdr_len, md_len);
- if (unlikely(s_packet == NULL)) {
+ if (unlikely(!s_packet)) {
dd_dev_warn_ratelimited(ppd->dd, "Unable to allocate snoop/capture packet\n");
goto out;
}
@@ -1783,8 +1770,7 @@ int snoop_send_pio_handler(struct hfi1_qp *qp, struct ahg_ib_header *ahdr,
break;
}
out:
- return hfi1_verbs_send_pio(qp, ahdr, hdrwords, ss, len, plen, dwords,
- md.u.pbc);
+ return hfi1_verbs_send_pio(qp, ps, md.u.pbc);
}
/*
@@ -1836,7 +1822,7 @@ void snoop_inline_pio_send(struct hfi1_devdata *dd, struct pio_buf *pbuf,
s_packet = allocate_snoop_packet(packet_len, 0, md_len);
- if (unlikely(s_packet == NULL)) {
+ if (unlikely(!s_packet)) {
dd_dev_warn_ratelimited(dd, "Unable to allocate snoop/capture packet\n");
goto inline_pio_out;
}
@@ -1868,5 +1854,4 @@ void snoop_inline_pio_send(struct hfi1_devdata *dd, struct pio_buf *pbuf,
inline_pio_out:
pio_copy(dd, pbuf, pbc, from, count);
-
}
diff --git a/drivers/staging/rdma/hfi1/driver.c b/drivers/staging/rdma/hfi1/driver.c
index ce69141b56cb..8485de1fce08 100644
--- a/drivers/staging/rdma/hfi1/driver.c
+++ b/drivers/staging/rdma/hfi1/driver.c
@@ -158,7 +158,7 @@ const char *get_unit_name(int unit)
{
static char iname[16];
- snprintf(iname, sizeof(iname), DRIVER_NAME"_%u", unit);
+ snprintf(iname, sizeof(iname), DRIVER_NAME "_%u", unit);
return iname;
}
@@ -436,59 +436,58 @@ static inline void init_packet(struct hfi1_ctxtdata *rcd,
#ifndef CONFIG_PRESCAN_RXQ
static void prescan_rxq(struct hfi1_packet *packet) {}
-#else /* CONFIG_PRESCAN_RXQ */
+#else /* !CONFIG_PRESCAN_RXQ */
static int prescan_receive_queue;
static void process_ecn(struct hfi1_qp *qp, struct hfi1_ib_header *hdr,
struct hfi1_other_headers *ohdr,
- u64 rhf, struct ib_grh *grh)
+ u64 rhf, u32 bth1, struct ib_grh *grh)
{
struct hfi1_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num);
- u32 bth1;
+ u32 rqpn = 0;
+ u16 rlid;
u8 sc5, svc_type;
- int is_fecn, is_becn;
switch (qp->ibqp.qp_type) {
+ case IB_QPT_SMI:
+ case IB_QPT_GSI:
case IB_QPT_UD:
+ rlid = be16_to_cpu(hdr->lrh[3]);
+ rqpn = be32_to_cpu(ohdr->u.ud.deth[1]) & HFI1_QPN_MASK;
svc_type = IB_CC_SVCTYPE_UD;
break;
- case IB_QPT_UC: /* LATER */
- case IB_QPT_RC: /* LATER */
+ case IB_QPT_UC:
+ rlid = qp->remote_ah_attr.dlid;
+ rqpn = qp->remote_qpn;
+ svc_type = IB_CC_SVCTYPE_UC;
+ break;
+ case IB_QPT_RC:
+ rlid = qp->remote_ah_attr.dlid;
+ rqpn = qp->remote_qpn;
+ svc_type = IB_CC_SVCTYPE_RC;
+ break;
default:
return;
}
- is_fecn = (be32_to_cpu(ohdr->bth[1]) >> HFI1_FECN_SHIFT) &
- HFI1_FECN_MASK;
- is_becn = (be32_to_cpu(ohdr->bth[1]) >> HFI1_BECN_SHIFT) &
- HFI1_BECN_MASK;
-
sc5 = (be16_to_cpu(hdr->lrh[0]) >> 12) & 0xf;
if (rhf_dc_info(rhf))
sc5 |= 0x10;
- if (is_fecn) {
- u32 src_qpn = be32_to_cpu(ohdr->u.ud.deth[1]) & HFI1_QPN_MASK;
+ if (bth1 & HFI1_FECN_SMASK) {
u16 pkey = (u16)be32_to_cpu(ohdr->bth[0]);
u16 dlid = be16_to_cpu(hdr->lrh[1]);
- u16 slid = be16_to_cpu(hdr->lrh[3]);
- return_cnp(ibp, qp, src_qpn, pkey, dlid, slid, sc5, grh);
+ return_cnp(ibp, qp, rqpn, pkey, dlid, rlid, sc5, grh);
}
- if (is_becn) {
+ if (bth1 & HFI1_BECN_SMASK) {
struct hfi1_pportdata *ppd = ppd_from_ibp(ibp);
- u32 lqpn = be32_to_cpu(ohdr->bth[1]) & HFI1_QPN_MASK;
+ u32 lqpn = bth1 & HFI1_QPN_MASK;
u8 sl = ibp->sc_to_sl[sc5];
- process_becn(ppd, sl, 0, lqpn, 0, svc_type);
+ process_becn(ppd, sl, rlid, lqpn, rqpn, svc_type);
}
-
- /* turn off BECN, or FECN */
- bth1 = be32_to_cpu(ohdr->bth[1]);
- bth1 &= ~(HFI1_FECN_MASK << HFI1_FECN_SHIFT);
- bth1 &= ~(HFI1_BECN_MASK << HFI1_BECN_SHIFT);
- ohdr->bth[1] = cpu_to_be32(bth1);
}
struct ps_mdata {
@@ -508,38 +507,51 @@ static inline void init_ps_mdata(struct ps_mdata *mdata,
mdata->rcd = rcd;
mdata->rsize = packet->rsize;
mdata->maxcnt = packet->maxcnt;
+ mdata->ps_head = packet->rhqoff;
- if (rcd->ps_state.initialized == 0) {
- mdata->ps_head = packet->rhqoff;
- rcd->ps_state.initialized++;
- } else
- mdata->ps_head = rcd->ps_state.ps_head;
-
- if (HFI1_CAP_IS_KSET(DMA_RTAIL)) {
- mdata->ps_tail = packet->hdrqtail;
- mdata->ps_seq = 0; /* not used with DMA_RTAIL */
+ if (HFI1_CAP_KGET_MASK(rcd->flags, DMA_RTAIL)) {
+ mdata->ps_tail = get_rcvhdrtail(rcd);
+ if (rcd->ctxt == HFI1_CTRL_CTXT)
+ mdata->ps_seq = rcd->seq_cnt;
+ else
+ mdata->ps_seq = 0; /* not used with DMA_RTAIL */
} else {
mdata->ps_tail = 0; /* used only with DMA_RTAIL*/
mdata->ps_seq = rcd->seq_cnt;
}
}
-static inline int ps_done(struct ps_mdata *mdata, u64 rhf)
+static inline int ps_done(struct ps_mdata *mdata, u64 rhf,
+ struct hfi1_ctxtdata *rcd)
{
- if (HFI1_CAP_IS_KSET(DMA_RTAIL))
+ if (HFI1_CAP_KGET_MASK(rcd->flags, DMA_RTAIL))
return mdata->ps_head == mdata->ps_tail;
return mdata->ps_seq != rhf_rcv_seq(rhf);
}
-static inline void update_ps_mdata(struct ps_mdata *mdata)
+static inline int ps_skip(struct ps_mdata *mdata, u64 rhf,
+ struct hfi1_ctxtdata *rcd)
{
- struct hfi1_ctxtdata *rcd = mdata->rcd;
+ /*
+ * Control context can potentially receive an invalid rhf.
+ * Drop such packets.
+ */
+ if ((rcd->ctxt == HFI1_CTRL_CTXT) && (mdata->ps_head != mdata->ps_tail))
+ return mdata->ps_seq != rhf_rcv_seq(rhf);
+
+ return 0;
+}
+static inline void update_ps_mdata(struct ps_mdata *mdata,
+ struct hfi1_ctxtdata *rcd)
+{
mdata->ps_head += mdata->rsize;
- if (mdata->ps_head > mdata->maxcnt)
+ if (mdata->ps_head >= mdata->maxcnt)
mdata->ps_head = 0;
- rcd->ps_state.ps_head = mdata->ps_head;
- if (!HFI1_CAP_IS_KSET(DMA_RTAIL)) {
+
+ /* Control context must do seq counting */
+ if (!HFI1_CAP_KGET_MASK(rcd->flags, DMA_RTAIL) ||
+ (rcd->ctxt == HFI1_CTRL_CTXT)) {
if (++mdata->ps_seq > 13)
mdata->ps_seq = 1;
}
@@ -571,13 +583,16 @@ static void prescan_rxq(struct hfi1_packet *packet)
struct hfi1_other_headers *ohdr;
struct ib_grh *grh = NULL;
u64 rhf = rhf_to_cpu(rhf_addr);
- u32 etype = rhf_rcv_type(rhf), qpn;
+ u32 etype = rhf_rcv_type(rhf), qpn, bth1;
int is_ecn = 0;
u8 lnh;
- if (ps_done(&mdata, rhf))
+ if (ps_done(&mdata, rhf, rcd))
break;
+ if (ps_skip(&mdata, rhf, rcd))
+ goto next;
+
if (etype != RHF_RCV_TYPE_IB)
goto next;
@@ -593,15 +608,13 @@ static void prescan_rxq(struct hfi1_packet *packet)
} else
goto next; /* just in case */
- is_ecn |= be32_to_cpu(ohdr->bth[1]) &
- (HFI1_FECN_MASK << HFI1_FECN_SHIFT);
- is_ecn |= be32_to_cpu(ohdr->bth[1]) &
- (HFI1_BECN_MASK << HFI1_BECN_SHIFT);
+ bth1 = be32_to_cpu(ohdr->bth[1]);
+ is_ecn = !!(bth1 & (HFI1_FECN_SMASK | HFI1_BECN_SMASK));
if (!is_ecn)
goto next;
- qpn = be32_to_cpu(ohdr->bth[1]) & HFI1_QPN_MASK;
+ qpn = bth1 & HFI1_QPN_MASK;
rcu_read_lock();
qp = hfi1_lookup_qpn(ibp, qpn);
@@ -610,14 +623,44 @@ static void prescan_rxq(struct hfi1_packet *packet)
goto next;
}
- process_ecn(qp, hdr, ohdr, rhf, grh);
+ process_ecn(qp, hdr, ohdr, rhf, bth1, grh);
rcu_read_unlock();
+
+ /* turn off BECN, FECN */
+ bth1 &= ~(HFI1_FECN_SMASK | HFI1_BECN_SMASK);
+ ohdr->bth[1] = cpu_to_be32(bth1);
next:
- update_ps_mdata(&mdata);
+ update_ps_mdata(&mdata, rcd);
}
}
#endif /* CONFIG_PRESCAN_RXQ */
+static inline int skip_rcv_packet(struct hfi1_packet *packet, int thread)
+{
+ int ret = RCV_PKT_OK;
+
+ /* Set up for the next packet */
+ packet->rhqoff += packet->rsize;
+ if (packet->rhqoff >= packet->maxcnt)
+ packet->rhqoff = 0;
+
+ packet->numpkt++;
+ if (unlikely((packet->numpkt & (MAX_PKT_RECV - 1)) == 0)) {
+ if (thread) {
+ cond_resched();
+ } else {
+ ret = RCV_PKT_LIMIT;
+ this_cpu_inc(*packet->rcd->dd->rcv_limit);
+ }
+ }
+
+ packet->rhf_addr = (__le32 *)packet->rcd->rcvhdrq + packet->rhqoff +
+ packet->rcd->dd->rhf_offset;
+ packet->rhf = rhf_to_cpu(packet->rhf_addr);
+
+ return ret;
+}
+
static inline int process_rcv_packet(struct hfi1_packet *packet, int thread)
{
int ret = RCV_PKT_OK;
@@ -721,8 +764,8 @@ static inline void process_rcv_qp_work(struct hfi1_packet *packet)
*/
list_for_each_entry_safe(qp, nqp, &rcd->qp_wait_list, rspwait) {
list_del_init(&qp->rspwait);
- if (qp->r_flags & HFI1_R_RSP_NAK) {
- qp->r_flags &= ~HFI1_R_RSP_NAK;
+ if (qp->r_flags & HFI1_R_RSP_DEFERED_ACK) {
+ qp->r_flags &= ~HFI1_R_RSP_DEFERED_ACK;
hfi1_send_rc_ack(rcd, qp, 0);
}
if (qp->r_flags & HFI1_R_RSP_SEND) {
@@ -791,7 +834,6 @@ int handle_receive_interrupt_dma_rtail(struct hfi1_ctxtdata *rcd, int thread)
while (last == RCV_PKT_OK) {
last = process_rcv_packet(&packet, thread);
- hdrqtail = get_rcvhdrtail(rcd);
if (packet.rhqoff == hdrqtail)
last = RCV_PKT_DONE;
process_rcv_update(last, &packet);
@@ -806,7 +848,7 @@ static inline void set_all_nodma_rtail(struct hfi1_devdata *dd)
{
int i;
- for (i = 0; i < dd->first_user_ctxt; i++)
+ for (i = HFI1_CTRL_CTXT + 1; i < dd->first_user_ctxt; i++)
dd->rcd[i]->do_interrupt =
&handle_receive_interrupt_nodma_rtail;
}
@@ -815,7 +857,7 @@ static inline void set_all_dma_rtail(struct hfi1_devdata *dd)
{
int i;
- for (i = 0; i < dd->first_user_ctxt; i++)
+ for (i = HFI1_CTRL_CTXT + 1; i < dd->first_user_ctxt; i++)
dd->rcd[i]->do_interrupt =
&handle_receive_interrupt_dma_rtail;
}
@@ -831,12 +873,16 @@ int handle_receive_interrupt(struct hfi1_ctxtdata *rcd, int thread)
{
struct hfi1_devdata *dd = rcd->dd;
u32 hdrqtail;
- int last = RCV_PKT_OK, needset = 1;
+ int needset, last = RCV_PKT_OK;
struct hfi1_packet packet;
+ int skip_pkt = 0;
+
+ /* Control context will always use the slow path interrupt handler */
+ needset = (rcd->ctxt == HFI1_CTRL_CTXT) ? 0 : 1;
init_packet(rcd, &packet);
- if (!HFI1_CAP_IS_KSET(DMA_RTAIL)) {
+ if (!HFI1_CAP_KGET_MASK(rcd->flags, DMA_RTAIL)) {
u32 seq = rhf_rcv_seq(packet.rhf);
if (seq != rcd->seq_cnt) {
@@ -851,6 +897,17 @@ int handle_receive_interrupt(struct hfi1_ctxtdata *rcd, int thread)
goto bail;
}
smp_rmb(); /* prevent speculative reads of dma'ed hdrq */
+
+ /*
+ * Control context can potentially receive an invalid
+ * rhf. Drop such packets.
+ */
+ if (rcd->ctxt == HFI1_CTRL_CTXT) {
+ u32 seq = rhf_rcv_seq(packet.rhf);
+
+ if (seq != rcd->seq_cnt)
+ skip_pkt = 1;
+ }
}
prescan_rxq(&packet);
@@ -868,11 +925,14 @@ int handle_receive_interrupt(struct hfi1_ctxtdata *rcd, int thread)
dd->rhf_offset;
packet.rhf = rhf_to_cpu(packet.rhf_addr);
+ } else if (skip_pkt) {
+ last = skip_rcv_packet(&packet, thread);
+ skip_pkt = 0;
} else {
last = process_rcv_packet(&packet, thread);
}
- if (!HFI1_CAP_IS_KSET(DMA_RTAIL)) {
+ if (!HFI1_CAP_KGET_MASK(rcd->flags, DMA_RTAIL)) {
u32 seq = rhf_rcv_seq(packet.rhf);
if (++rcd->seq_cnt > 13)
@@ -888,6 +948,19 @@ int handle_receive_interrupt(struct hfi1_ctxtdata *rcd, int thread)
} else {
if (packet.rhqoff == hdrqtail)
last = RCV_PKT_DONE;
+ /*
+ * Control context can potentially receive an invalid
+ * rhf. Drop such packets.
+ */
+ if (rcd->ctxt == HFI1_CTRL_CTXT) {
+ u32 seq = rhf_rcv_seq(packet.rhf);
+
+ if (++rcd->seq_cnt > 13)
+ rcd->seq_cnt = 1;
+ if (!last && (seq != rcd->seq_cnt))
+ skip_pkt = 1;
+ }
+
if (needset) {
dd_dev_info(dd,
"Switching to DMA_RTAIL\n");
@@ -1163,20 +1236,20 @@ void handle_eflags(struct hfi1_packet *packet)
struct hfi1_ctxtdata *rcd = packet->rcd;
u32 rte = rhf_rcv_type_err(packet->rhf);
- dd_dev_err(rcd->dd,
- "receive context %d: rhf 0x%016llx, errs [ %s%s%s%s%s%s%s%s] rte 0x%x\n",
- rcd->ctxt, packet->rhf,
- packet->rhf & RHF_K_HDR_LEN_ERR ? "k_hdr_len " : "",
- packet->rhf & RHF_DC_UNC_ERR ? "dc_unc " : "",
- packet->rhf & RHF_DC_ERR ? "dc " : "",
- packet->rhf & RHF_TID_ERR ? "tid " : "",
- packet->rhf & RHF_LEN_ERR ? "len " : "",
- packet->rhf & RHF_ECC_ERR ? "ecc " : "",
- packet->rhf & RHF_VCRC_ERR ? "vcrc " : "",
- packet->rhf & RHF_ICRC_ERR ? "icrc " : "",
- rte);
-
rcv_hdrerr(rcd, rcd->ppd, packet);
+ if (rhf_err_flags(packet->rhf))
+ dd_dev_err(rcd->dd,
+ "receive context %d: rhf 0x%016llx, errs [ %s%s%s%s%s%s%s%s] rte 0x%x\n",
+ rcd->ctxt, packet->rhf,
+ packet->rhf & RHF_K_HDR_LEN_ERR ? "k_hdr_len " : "",
+ packet->rhf & RHF_DC_UNC_ERR ? "dc_unc " : "",
+ packet->rhf & RHF_DC_ERR ? "dc " : "",
+ packet->rhf & RHF_TID_ERR ? "tid " : "",
+ packet->rhf & RHF_LEN_ERR ? "len " : "",
+ packet->rhf & RHF_ECC_ERR ? "ecc " : "",
+ packet->rhf & RHF_VCRC_ERR ? "vcrc " : "",
+ packet->rhf & RHF_ICRC_ERR ? "icrc " : "",
+ rte);
}
/*
diff --git a/drivers/staging/rdma/hfi1/efivar.c b/drivers/staging/rdma/hfi1/efivar.c
new file mode 100644
index 000000000000..7dc5bae220e0
--- /dev/null
+++ b/drivers/staging/rdma/hfi1/efivar.c
@@ -0,0 +1,169 @@
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2015 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015 Intel Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * - Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "efivar.h"
+
+/* GUID for HFI1 variables in EFI */
+#define HFI1_EFIVAR_GUID EFI_GUID(0xc50a953e, 0xa8b2, 0x42a6, \
+ 0xbf, 0x89, 0xd3, 0x33, 0xa6, 0xe9, 0xe6, 0xd4)
+/* largest EFI data size we expect */
+#define EFI_DATA_SIZE 4096
+
+/*
+ * Read the named EFI variable. Return the size of the actual data in *size
+ * and a kmalloc'ed buffer in *return_data. The caller must free the
+ * data. It is guaranteed that *return_data will be NULL and *size = 0
+ * if this routine fails.
+ *
+ * Return 0 on success, -errno on failure.
+ */
+static int read_efi_var(const char *name, unsigned long *size,
+ void **return_data)
+{
+ efi_status_t status;
+ efi_char16_t *uni_name;
+ efi_guid_t guid;
+ unsigned long temp_size;
+ void *temp_buffer;
+ void *data;
+ int i;
+ int ret;
+
+ /* set failure return values */
+ *size = 0;
+ *return_data = NULL;
+
+ if (!efi_enabled(EFI_RUNTIME_SERVICES))
+ return -EOPNOTSUPP;
+
+ uni_name = kzalloc(sizeof(efi_char16_t) * (strlen(name) + 1),
+ GFP_KERNEL);
+ temp_buffer = kzalloc(EFI_DATA_SIZE, GFP_KERNEL);
+
+ if (!uni_name || !temp_buffer) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ /* input: the size of the buffer */
+ temp_size = EFI_DATA_SIZE;
+
+ /* convert ASCII to unicode - it is a 1:1 mapping */
+ for (i = 0; name[i]; i++)
+ uni_name[i] = name[i];
+
+ /* need a variable for our GUID */
+ guid = HFI1_EFIVAR_GUID;
+
+ /* call into EFI runtime services */
+ status = efi.get_variable(
+ uni_name,
+ &guid,
+ NULL,
+ &temp_size,
+ temp_buffer);
+
+ /*
+ * It would be nice to call efi_status_to_err() here, but that
+ * is in the EFIVAR_FS code and may not be compiled in.
+ * However, even that is insufficient since it does not cover
+ * EFI_BUFFER_TOO_SMALL which could be an important return.
+ * For now, just split out succces or not found.
+ */
+ ret = status == EFI_SUCCESS ? 0 :
+ status == EFI_NOT_FOUND ? -ENOENT :
+ -EINVAL;
+ if (ret)
+ goto fail;
+
+ /*
+ * We have successfully read the EFI variable into our
+ * temporary buffer. Now allocate a correctly sized
+ * buffer.
+ */
+ data = kmalloc(temp_size, GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ memcpy(data, temp_buffer, temp_size);
+ *size = temp_size;
+ *return_data = data;
+
+fail:
+ kfree(uni_name);
+ kfree(temp_buffer);
+
+ return ret;
+}
+
+/*
+ * Read an HFI1 EFI variable of the form:
+ * <PCIe address>-<kind>
+ * Return an kalloc'ed array and size of the data.
+ *
+ * Returns 0 on success, -errno on failure.
+ */
+int read_hfi1_efi_var(struct hfi1_devdata *dd, const char *kind,
+ unsigned long *size, void **return_data)
+{
+ char name[64];
+
+ /* create a common prefix */
+ snprintf(name, sizeof(name), "%04x:%02x:%02x.%x-%s",
+ pci_domain_nr(dd->pcidev->bus),
+ dd->pcidev->bus->number,
+ PCI_SLOT(dd->pcidev->devfn),
+ PCI_FUNC(dd->pcidev->devfn),
+ kind);
+
+ return read_efi_var(name, size, return_data);
+}
diff --git a/drivers/staging/rdma/hfi1/efivar.h b/drivers/staging/rdma/hfi1/efivar.h
new file mode 100644
index 000000000000..070706225c51
--- /dev/null
+++ b/drivers/staging/rdma/hfi1/efivar.h
@@ -0,0 +1,60 @@
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2015 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015 Intel Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * - Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _HFI1_EFIVAR_H
+#define _HFI1_EFIVAR_H
+
+#include <linux/efi.h>
+
+#include "hfi.h"
+
+int read_hfi1_efi_var(struct hfi1_devdata *dd, const char *kind,
+ unsigned long *size, void **return_data);
+
+#endif /* _HFI1_EFIVAR_H */
diff --git a/drivers/staging/rdma/hfi1/eprom.c b/drivers/staging/rdma/hfi1/eprom.c
index b61d3ae93ed1..fb620c97f592 100644
--- a/drivers/staging/rdma/hfi1/eprom.c
+++ b/drivers/staging/rdma/hfi1/eprom.c
@@ -53,17 +53,26 @@
#include "eprom.h"
/*
- * The EPROM is logically divided into two partitions:
+ * The EPROM is logically divided into three partitions:
* partition 0: the first 128K, visible from PCI ROM BAR
- * partition 1: the rest
+ * partition 1: 4K config file (sector size)
+ * partition 2: the rest
*/
#define P0_SIZE (128 * 1024)
+#define P1_SIZE (4 * 1024)
#define P1_START P0_SIZE
+#define P2_START (P0_SIZE + P1_SIZE)
+
+/* erase sizes supported by the controller */
+#define SIZE_4KB (4 * 1024)
+#define MASK_4KB (SIZE_4KB - 1)
-/* largest erase size supported by the controller */
#define SIZE_32KB (32 * 1024)
#define MASK_32KB (SIZE_32KB - 1)
+#define SIZE_64KB (64 * 1024)
+#define MASK_64KB (SIZE_64KB - 1)
+
/* controller page size, in bytes */
#define EP_PAGE_SIZE 256
#define EEP_PAGE_MASK (EP_PAGE_SIZE - 1)
@@ -75,10 +84,12 @@
#define CMD_READ_DATA(addr) ((0x03 << CMD_SHIFT) | addr)
#define CMD_READ_SR1 ((0x05 << CMD_SHIFT))
#define CMD_WRITE_ENABLE ((0x06 << CMD_SHIFT))
+#define CMD_SECTOR_ERASE_4KB(addr) ((0x20 << CMD_SHIFT) | addr)
#define CMD_SECTOR_ERASE_32KB(addr) ((0x52 << CMD_SHIFT) | addr)
#define CMD_CHIP_ERASE ((0x60 << CMD_SHIFT))
#define CMD_READ_MANUF_DEV_ID ((0x90 << CMD_SHIFT))
#define CMD_RELEASE_POWERDOWN_NOID ((0xab << CMD_SHIFT))
+#define CMD_SECTOR_ERASE_64KB(addr) ((0xd8 << CMD_SHIFT) | addr)
/* controller interface speeds */
#define EP_SPEED_FULL 0x2 /* full speed */
@@ -188,28 +199,43 @@ static int erase_chip(struct hfi1_devdata *dd)
}
/*
- * Erase a range using the 32KB erase command.
+ * Erase a range.
*/
-static int erase_32kb_range(struct hfi1_devdata *dd, u32 start, u32 end)
+static int erase_range(struct hfi1_devdata *dd, u32 start, u32 len)
{
+ u32 end = start + len;
int ret = 0;
if (end < start)
return -EINVAL;
- if ((start & MASK_32KB) || (end & MASK_32KB)) {
+ /* check the end points for the minimum erase */
+ if ((start & MASK_4KB) || (end & MASK_4KB)) {
dd_dev_err(dd,
- "%s: non-aligned range (0x%x,0x%x) for a 32KB erase\n",
+ "%s: non-aligned range (0x%x,0x%x) for a 4KB erase\n",
__func__, start, end);
return -EINVAL;
}
write_enable(dd);
- for (; start < end; start += SIZE_32KB) {
+ while (start < end) {
write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_WRITE_ENABLE);
- write_csr(dd, ASIC_EEP_ADDR_CMD,
- CMD_SECTOR_ERASE_32KB(start));
+ /* check in order of largest to smallest */
+ if (((start & MASK_64KB) == 0) && (start + SIZE_64KB <= end)) {
+ write_csr(dd, ASIC_EEP_ADDR_CMD,
+ CMD_SECTOR_ERASE_64KB(start));
+ start += SIZE_64KB;
+ } else if (((start & MASK_32KB) == 0) &&
+ (start + SIZE_32KB <= end)) {
+ write_csr(dd, ASIC_EEP_ADDR_CMD,
+ CMD_SECTOR_ERASE_32KB(start));
+ start += SIZE_32KB;
+ } else { /* 4KB will work */
+ write_csr(dd, ASIC_EEP_ADDR_CMD,
+ CMD_SECTOR_ERASE_4KB(start));
+ start += SIZE_4KB;
+ }
ret = wait_for_not_busy(dd);
if (ret)
goto done;
@@ -309,6 +335,18 @@ done:
return ret;
}
+/* convert an range composite to a length, in bytes */
+static inline u32 extract_rlen(u32 composite)
+{
+ return (composite & 0xffff) * EP_PAGE_SIZE;
+}
+
+/* convert an range composite to a start, in bytes */
+static inline u32 extract_rstart(u32 composite)
+{
+ return (composite >> 16) * EP_PAGE_SIZE;
+}
+
/*
* Perform the given operation on the EPROM. Called from user space. The
* user credentials have already been checked.
@@ -319,6 +357,8 @@ int handle_eprom_command(const struct hfi1_cmd *cmd)
{
struct hfi1_devdata *dd;
u32 dev_id;
+ u32 rlen; /* range length */
+ u32 rstart; /* range start */
int ret = 0;
/*
@@ -364,54 +404,29 @@ int handle_eprom_command(const struct hfi1_cmd *cmd)
sizeof(u32)))
ret = -EFAULT;
break;
+
case HFI1_CMD_EP_ERASE_CHIP:
ret = erase_chip(dd);
break;
- case HFI1_CMD_EP_ERASE_P0:
- if (cmd->len != P0_SIZE) {
- ret = -ERANGE;
- break;
- }
- ret = erase_32kb_range(dd, 0, cmd->len);
- break;
- case HFI1_CMD_EP_ERASE_P1:
- /* check for overflow */
- if (P1_START + cmd->len > ASIC_EEP_ADDR_CMD_EP_ADDR_MASK) {
- ret = -ERANGE;
- break;
- }
- ret = erase_32kb_range(dd, P1_START, P1_START + cmd->len);
- break;
- case HFI1_CMD_EP_READ_P0:
- if (cmd->len != P0_SIZE) {
- ret = -ERANGE;
- break;
- }
- ret = read_length(dd, 0, cmd->len, cmd->addr);
- break;
- case HFI1_CMD_EP_READ_P1:
- /* check for overflow */
- if (P1_START + cmd->len > ASIC_EEP_ADDR_CMD_EP_ADDR_MASK) {
- ret = -ERANGE;
- break;
- }
- ret = read_length(dd, P1_START, cmd->len, cmd->addr);
+
+ case HFI1_CMD_EP_ERASE_RANGE:
+ rlen = extract_rlen(cmd->len);
+ rstart = extract_rstart(cmd->len);
+ ret = erase_range(dd, rstart, rlen);
break;
- case HFI1_CMD_EP_WRITE_P0:
- if (cmd->len > P0_SIZE) {
- ret = -ERANGE;
- break;
- }
- ret = write_length(dd, 0, cmd->len, cmd->addr);
+
+ case HFI1_CMD_EP_READ_RANGE:
+ rlen = extract_rlen(cmd->len);
+ rstart = extract_rstart(cmd->len);
+ ret = read_length(dd, rstart, rlen, cmd->addr);
break;
- case HFI1_CMD_EP_WRITE_P1:
- /* check for overflow */
- if (P1_START + cmd->len > ASIC_EEP_ADDR_CMD_EP_ADDR_MASK) {
- ret = -ERANGE;
- break;
- }
- ret = write_length(dd, P1_START, cmd->len, cmd->addr);
+
+ case HFI1_CMD_EP_WRITE_RANGE:
+ rlen = extract_rlen(cmd->len);
+ rstart = extract_rstart(cmd->len);
+ ret = write_length(dd, rstart, rlen, cmd->addr);
break;
+
default:
dd_dev_err(dd, "%s: unexpected command %d\n",
__func__, cmd->type);
diff --git a/drivers/staging/rdma/hfi1/file_ops.c b/drivers/staging/rdma/hfi1/file_ops.c
index aae9826ec62b..d57d549052c8 100644
--- a/drivers/staging/rdma/hfi1/file_ops.c
+++ b/drivers/staging/rdma/hfi1/file_ops.c
@@ -47,20 +47,10 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
-#include <linux/pci.h>
#include <linux/poll.h>
#include <linux/cdev.h>
-#include <linux/swap.h>
#include <linux/vmalloc.h>
-#include <linux/highmem.h>
#include <linux/io.h>
-#include <linux/jiffies.h>
-#include <asm/pgtable.h>
-#include <linux/delay.h>
-#include <linux/export.h>
-#include <linux/module.h>
-#include <linux/cred.h>
-#include <linux/uio.h>
#include "hfi.h"
#include "pio.h"
@@ -68,6 +58,7 @@
#include "common.h"
#include "trace.h"
#include "user_sdma.h"
+#include "user_exp_rcv.h"
#include "eprom.h"
#undef pr_fmt
@@ -170,18 +161,6 @@ enum mmap_types {
HFI1_MMAP_TOKEN_SET(SUBCTXT, subctxt) | \
HFI1_MMAP_TOKEN_SET(OFFSET, (offset_in_page(addr))))
-#define EXP_TID_SET(field, value) \
- (((value) & EXP_TID_TID##field##_MASK) << \
- EXP_TID_TID##field##_SHIFT)
-#define EXP_TID_CLEAR(tid, field) { \
- (tid) &= ~(EXP_TID_TID##field##_MASK << \
- EXP_TID_TID##field##_SHIFT); \
- }
-#define EXP_TID_RESET(tid, field, value) do { \
- EXP_TID_CLEAR(tid, field); \
- (tid) |= EXP_TID_SET(field, value); \
- } while (0)
-
#define dbg(fmt, ...) \
pr_info(fmt, ##__VA_ARGS__)
@@ -204,7 +183,8 @@ static ssize_t hfi1_file_write(struct file *fp, const char __user *data,
size_t count, loff_t *offset)
{
const struct hfi1_cmd __user *ucmd;
- struct hfi1_ctxtdata *uctxt = ctxt_fp(fp);
+ struct hfi1_filedata *fd = fp->private_data;
+ struct hfi1_ctxtdata *uctxt = fd->uctxt;
struct hfi1_cmd cmd;
struct hfi1_user_info uinfo;
struct hfi1_tid_info tinfo;
@@ -254,12 +234,9 @@ static ssize_t hfi1_file_write(struct file *fp, const char __user *data,
break;
case HFI1_CMD_EP_INFO:
case HFI1_CMD_EP_ERASE_CHIP:
- case HFI1_CMD_EP_ERASE_P0:
- case HFI1_CMD_EP_ERASE_P1:
- case HFI1_CMD_EP_READ_P0:
- case HFI1_CMD_EP_READ_P1:
- case HFI1_CMD_EP_WRITE_P0:
- case HFI1_CMD_EP_WRITE_P1:
+ case HFI1_CMD_EP_ERASE_RANGE:
+ case HFI1_CMD_EP_READ_RANGE:
+ case HFI1_CMD_EP_WRITE_RANGE:
uctxt_required = 0; /* assigned user context not required */
must_be_root = 1; /* validate user */
copy = 0;
@@ -338,17 +315,17 @@ static ssize_t hfi1_file_write(struct file *fp, const char __user *data,
ret = exp_tid_free(fp, &tinfo);
break;
case HFI1_CMD_RECV_CTRL:
- ret = manage_rcvq(uctxt, subctxt_fp(fp), (int)user_val);
+ ret = manage_rcvq(uctxt, fd->subctxt, (int)user_val);
break;
case HFI1_CMD_POLL_TYPE:
uctxt->poll_type = (typeof(uctxt->poll_type))user_val;
break;
case HFI1_CMD_ACK_EVENT:
- ret = user_event_ack(uctxt, subctxt_fp(fp), user_val);
+ ret = user_event_ack(uctxt, fd->subctxt, user_val);
break;
case HFI1_CMD_SET_PKEY:
if (HFI1_CAP_IS_USET(PKEY_CHECK))
- ret = set_ctxt_pkey(uctxt, subctxt_fp(fp), user_val);
+ ret = set_ctxt_pkey(uctxt, fd->subctxt, user_val);
else
ret = -EPERM;
break;
@@ -413,12 +390,9 @@ static ssize_t hfi1_file_write(struct file *fp, const char __user *data,
}
case HFI1_CMD_EP_INFO:
case HFI1_CMD_EP_ERASE_CHIP:
- case HFI1_CMD_EP_ERASE_P0:
- case HFI1_CMD_EP_ERASE_P1:
- case HFI1_CMD_EP_READ_P0:
- case HFI1_CMD_EP_READ_P1:
- case HFI1_CMD_EP_WRITE_P0:
- case HFI1_CMD_EP_WRITE_P1:
+ case HFI1_CMD_EP_ERASE_RANGE:
+ case HFI1_CMD_EP_READ_RANGE:
+ case HFI1_CMD_EP_WRITE_RANGE:
ret = handle_eprom_command(&cmd);
break;
}
@@ -431,13 +405,13 @@ bail:
static ssize_t hfi1_write_iter(struct kiocb *kiocb, struct iov_iter *from)
{
- struct hfi1_user_sdma_pkt_q *pq;
- struct hfi1_user_sdma_comp_q *cq;
+ struct hfi1_filedata *fd = kiocb->ki_filp->private_data;
+ struct hfi1_user_sdma_pkt_q *pq = fd->pq;
+ struct hfi1_user_sdma_comp_q *cq = fd->cq;
int ret = 0, done = 0, reqs = 0;
unsigned long dim = from->nr_segs;
- if (!user_sdma_comp_fp(kiocb->ki_filp) ||
- !user_sdma_pkt_fp(kiocb->ki_filp)) {
+ if (!cq || !pq) {
ret = -EIO;
goto done;
}
@@ -448,10 +422,7 @@ static ssize_t hfi1_write_iter(struct kiocb *kiocb, struct iov_iter *from)
}
hfi1_cdbg(SDMA, "SDMA request from %u:%u (%lu)",
- ctxt_fp(kiocb->ki_filp)->ctxt, subctxt_fp(kiocb->ki_filp),
- dim);
- pq = user_sdma_pkt_fp(kiocb->ki_filp);
- cq = user_sdma_comp_fp(kiocb->ki_filp);
+ fd->uctxt->ctxt, fd->subctxt, dim);
if (atomic_read(&pq->n_reqs) == pq->n_max_reqs) {
ret = -ENOSPC;
@@ -476,7 +447,8 @@ done:
static int hfi1_file_mmap(struct file *fp, struct vm_area_struct *vma)
{
- struct hfi1_ctxtdata *uctxt;
+ struct hfi1_filedata *fd = fp->private_data;
+ struct hfi1_ctxtdata *uctxt = fd->uctxt;
struct hfi1_devdata *dd;
unsigned long flags, pfn;
u64 token = vma->vm_pgoff << PAGE_SHIFT,
@@ -486,7 +458,6 @@ static int hfi1_file_mmap(struct file *fp, struct vm_area_struct *vma)
int ret = 0;
u16 ctxt;
- uctxt = ctxt_fp(fp);
if (!is_valid_mmap(token) || !uctxt ||
!(vma->vm_flags & VM_SHARED)) {
ret = -EINVAL;
@@ -496,7 +467,7 @@ static int hfi1_file_mmap(struct file *fp, struct vm_area_struct *vma)
ctxt = HFI1_MMAP_TOKEN_GET(CTXT, token);
subctxt = HFI1_MMAP_TOKEN_GET(SUBCTXT, token);
type = HFI1_MMAP_TOKEN_GET(TYPE, token);
- if (ctxt != uctxt->ctxt || subctxt != subctxt_fp(fp)) {
+ if (ctxt != uctxt->ctxt || subctxt != fd->subctxt) {
ret = -EINVAL;
goto done;
}
@@ -660,13 +631,12 @@ static int hfi1_file_mmap(struct file *fp, struct vm_area_struct *vma)
vmf = 1;
break;
case SDMA_COMP: {
- struct hfi1_user_sdma_comp_q *cq;
+ struct hfi1_user_sdma_comp_q *cq = fd->cq;
- if (!user_sdma_comp_fp(fp)) {
+ if (!cq) {
ret = -EFAULT;
goto done;
}
- cq = user_sdma_comp_fp(fp);
memaddr = (u64)cq->comps;
memlen = ALIGN(sizeof(*cq->comps) * cq->nentries, PAGE_SIZE);
flags |= VM_IO | VM_DONTEXPAND;
@@ -680,16 +650,16 @@ static int hfi1_file_mmap(struct file *fp, struct vm_area_struct *vma)
if ((vma->vm_end - vma->vm_start) != memlen) {
hfi1_cdbg(PROC, "%u:%u Memory size mismatch %lu:%lu",
- uctxt->ctxt, subctxt_fp(fp),
+ uctxt->ctxt, fd->subctxt,
(vma->vm_end - vma->vm_start), memlen);
ret = -EINVAL;
goto done;
}
vma->vm_flags = flags;
- dd_dev_info(dd,
- "%s: %u:%u type:%u io/vf:%d/%d, addr:0x%llx, len:%lu(%lu), flags:0x%lx\n",
- __func__, ctxt, subctxt, type, mapio, vmf, memaddr, memlen,
+ hfi1_cdbg(PROC,
+ "%u:%u type:%u io/vf:%d/%d, addr:0x%llx, len:%lu(%lu), flags:0x%lx\n",
+ ctxt, subctxt, type, mapio, vmf, memaddr, memlen,
vma->vm_end - vma->vm_start, vma->vm_flags);
pfn = (unsigned long)(memaddr >> PAGE_SHIFT);
if (vmf) {
@@ -730,7 +700,7 @@ static unsigned int hfi1_poll(struct file *fp, struct poll_table_struct *pt)
struct hfi1_ctxtdata *uctxt;
unsigned pollflag;
- uctxt = ctxt_fp(fp);
+ uctxt = ((struct hfi1_filedata *)fp->private_data)->uctxt;
if (!uctxt)
pollflag = POLLERR;
else if (uctxt->poll_type == HFI1_POLL_TYPE_URGENT)
@@ -761,8 +731,7 @@ static int hfi1_file_close(struct inode *inode, struct file *fp)
flush_wc();
/* drain user sdma queue */
- if (fdata->pq)
- hfi1_user_sdma_free_queues(fdata);
+ hfi1_user_sdma_free_queues(fdata);
/*
* Clear any left over, unhandled events so the next process that
@@ -874,6 +843,14 @@ done:
return ret;
}
+/* return true if the device available for general use */
+static int usable_device(struct hfi1_devdata *dd)
+{
+ struct hfi1_pportdata *ppd = dd->pport;
+
+ return driver_lstate(ppd) == IB_PORT_ACTIVE;
+}
+
static int get_user_context(struct file *fp, struct hfi1_user_info *uinfo,
int devno, unsigned alg)
{
@@ -903,7 +880,11 @@ static int get_user_context(struct file *fp, struct hfi1_user_info *uinfo,
for (dev = 0; dev < devmax; dev++) {
pdd = hfi1_lookup(dev);
- if (pdd && pdd->freectxts &&
+ if (!pdd)
+ continue;
+ if (!usable_device(pdd))
+ continue;
+ if (pdd->freectxts &&
pdd->freectxts > free) {
dd = pdd;
free = pdd->freectxts;
@@ -912,7 +893,11 @@ static int get_user_context(struct file *fp, struct hfi1_user_info *uinfo,
} else {
for (dev = 0; dev < devmax; dev++) {
pdd = hfi1_lookup(dev);
- if (pdd && pdd->freectxts) {
+ if (!pdd)
+ continue;
+ if (!usable_device(pdd))
+ continue;
+ if (pdd->freectxts) {
dd = pdd;
break;
}
@@ -930,13 +915,13 @@ static int find_shared_ctxt(struct file *fp,
{
int devmax, ndev, i;
int ret = 0;
+ struct hfi1_filedata *fd = fp->private_data;
devmax = hfi1_count_units(NULL, NULL);
for (ndev = 0; ndev < devmax; ndev++) {
struct hfi1_devdata *dd = hfi1_lookup(ndev);
- /* device portion of usable() */
if (!(dd && (dd->flags & HFI1_PRESENT) && dd->kregbase))
continue;
for (i = dd->first_user_ctxt; i < dd->num_rcv_contexts; i++) {
@@ -959,10 +944,10 @@ static int find_shared_ctxt(struct file *fp,
ret = -EINVAL;
goto done;
}
- ctxt_fp(fp) = uctxt;
- subctxt_fp(fp) = uctxt->cnt++;
- uctxt->subpid[subctxt_fp(fp)] = current->pid;
- uctxt->active_slaves |= 1 << subctxt_fp(fp);
+ fd->uctxt = uctxt;
+ fd->subctxt = uctxt->cnt++;
+ uctxt->subpid[fd->subctxt] = current->pid;
+ uctxt->active_slaves |= 1 << fd->subctxt;
ret = 1;
goto done;
}
@@ -975,6 +960,7 @@ done:
static int allocate_ctxt(struct file *fp, struct hfi1_devdata *dd,
struct hfi1_user_info *uinfo)
{
+ struct hfi1_filedata *fd = fp->private_data;
struct hfi1_ctxtdata *uctxt;
unsigned ctxt;
int ret;
@@ -1011,8 +997,8 @@ static int allocate_ctxt(struct file *fp, struct hfi1_devdata *dd,
if (!uctxt->sc)
return -ENOMEM;
- dbg("allocated send context %u(%u)\n", uctxt->sc->sw_index,
- uctxt->sc->hw_context);
+ hfi1_cdbg(PROC, "allocated send context %u(%u)\n", uctxt->sc->sw_index,
+ uctxt->sc->hw_context);
ret = sc_enable(uctxt->sc);
if (ret)
return ret;
@@ -1022,7 +1008,7 @@ static int allocate_ctxt(struct file *fp, struct hfi1_devdata *dd,
* This has to be done here so the rest of the sub-contexts find the
* proper master.
*/
- if (uinfo->subctxt_cnt && !subctxt_fp(fp)) {
+ if (uinfo->subctxt_cnt && !fd->subctxt) {
ret = init_subctxts(uctxt, uinfo);
/*
* On error, we don't need to disable and de-allocate the
@@ -1042,7 +1028,7 @@ static int allocate_ctxt(struct file *fp, struct hfi1_devdata *dd,
spin_lock_init(&uctxt->sdma_qlock);
hfi1_stats.sps_ctxts++;
dd->freectxts--;
- ctxt_fp(fp) = uctxt;
+ fd->uctxt = uctxt;
return 0;
}
@@ -1106,7 +1092,8 @@ static int user_init(struct file *fp)
{
int ret;
unsigned int rcvctrl_ops = 0;
- struct hfi1_ctxtdata *uctxt = ctxt_fp(fp);
+ struct hfi1_filedata *fd = fp->private_data;
+ struct hfi1_ctxtdata *uctxt = fd->uctxt;
/* make sure that the context has already been setup */
if (!test_bit(HFI1_CTXT_SETUP_DONE, &uctxt->event_flags)) {
@@ -1118,7 +1105,7 @@ static int user_init(struct file *fp)
* Subctxts don't need to initialize anything since master
* has done it.
*/
- if (subctxt_fp(fp)) {
+ if (fd->subctxt) {
ret = wait_event_interruptible(uctxt->wait,
!test_bit(HFI1_CTXT_MASTER_UNINIT,
&uctxt->event_flags));
@@ -1178,8 +1165,8 @@ done:
static int get_ctxt_info(struct file *fp, void __user *ubase, __u32 len)
{
struct hfi1_ctxt_info cinfo;
- struct hfi1_ctxtdata *uctxt = ctxt_fp(fp);
struct hfi1_filedata *fd = fp->private_data;
+ struct hfi1_ctxtdata *uctxt = fd->uctxt;
int ret = 0;
memset(&cinfo, 0, sizeof(cinfo));
@@ -1189,7 +1176,7 @@ static int get_ctxt_info(struct file *fp, void __user *ubase, __u32 len)
cinfo.num_active = hfi1_count_active_units();
cinfo.unit = uctxt->dd->unit;
cinfo.ctxt = uctxt->ctxt;
- cinfo.subctxt = subctxt_fp(fp);
+ cinfo.subctxt = fd->subctxt;
cinfo.rcvtids = roundup(uctxt->egrbufs.alloced,
uctxt->dd->rcv_entries.group_size) +
uctxt->expected_count;
@@ -1201,10 +1188,10 @@ static int get_ctxt_info(struct file *fp, void __user *ubase, __u32 len)
cinfo.egrtids = uctxt->egrbufs.alloced;
cinfo.rcvhdrq_cnt = uctxt->rcvhdrq_cnt;
cinfo.rcvhdrq_entsize = uctxt->rcvhdrqentsize << 2;
- cinfo.sdma_ring_size = user_sdma_comp_fp(fp)->nentries;
+ cinfo.sdma_ring_size = fd->cq->nentries;
cinfo.rcvegr_size = uctxt->egrbufs.rcvtid_size;
- trace_hfi1_ctxt_info(uctxt->dd, uctxt->ctxt, subctxt_fp(fp), cinfo);
+ trace_hfi1_ctxt_info(uctxt->dd, uctxt->ctxt, fd->subctxt, cinfo);
if (copy_to_user(ubase, &cinfo, sizeof(cinfo)))
ret = -EFAULT;
done:
@@ -1213,7 +1200,8 @@ done:
static int setup_ctxt(struct file *fp)
{
- struct hfi1_ctxtdata *uctxt = ctxt_fp(fp);
+ struct hfi1_filedata *fd = fp->private_data;
+ struct hfi1_ctxtdata *uctxt = fd->uctxt;
struct hfi1_devdata *dd = uctxt->dd;
int ret = 0;
@@ -1222,7 +1210,7 @@ static int setup_ctxt(struct file *fp)
* programming of eager buffers. This is done if context sharing
* is not requested or by the master process.
*/
- if (!uctxt->subctxt_cnt || !subctxt_fp(fp)) {
+ if (!uctxt->subctxt_cnt || !fd->subctxt) {
ret = hfi1_init_ctxt(uctxt->sc);
if (ret)
goto done;
@@ -1234,7 +1222,7 @@ static int setup_ctxt(struct file *fp)
ret = hfi1_setup_eagerbufs(uctxt);
if (ret)
goto done;
- if (uctxt->subctxt_cnt && !subctxt_fp(fp)) {
+ if (uctxt->subctxt_cnt && !fd->subctxt) {
ret = setup_subctxt(uctxt);
if (ret)
goto done;
@@ -1277,7 +1265,7 @@ static int setup_ctxt(struct file *fp)
uctxt->tidusemap[uctxt->tidmapcnt - 1] =
~((1ULL << (uctxt->numtidgroups %
BITS_PER_LONG)) - 1);
- trace_hfi1_exp_tid_map(uctxt->ctxt, subctxt_fp(fp), 0,
+ trace_hfi1_exp_tid_map(uctxt->ctxt, fd->subctxt, 0,
uctxt->tidusemap, uctxt->tidmapcnt);
}
ret = hfi1_user_sdma_alloc_queues(uctxt, fp);
@@ -1292,7 +1280,8 @@ done:
static int get_base_info(struct file *fp, void __user *ubase, __u32 len)
{
struct hfi1_base_info binfo;
- struct hfi1_ctxtdata *uctxt = ctxt_fp(fp);
+ struct hfi1_filedata *fd = fp->private_data;
+ struct hfi1_ctxtdata *uctxt = fd->uctxt;
struct hfi1_devdata *dd = uctxt->dd;
ssize_t sz;
unsigned offset;
@@ -1314,50 +1303,50 @@ static int get_base_info(struct file *fp, void __user *ubase, __u32 len)
offset = ((u64)uctxt->sc->hw_free -
(u64)dd->cr_base[uctxt->numa_id].va) % PAGE_SIZE;
binfo.sc_credits_addr = HFI1_MMAP_TOKEN(PIO_CRED, uctxt->ctxt,
- subctxt_fp(fp), offset);
+ fd->subctxt, offset);
binfo.pio_bufbase = HFI1_MMAP_TOKEN(PIO_BUFS, uctxt->ctxt,
- subctxt_fp(fp),
+ fd->subctxt,
uctxt->sc->base_addr);
binfo.pio_bufbase_sop = HFI1_MMAP_TOKEN(PIO_BUFS_SOP,
uctxt->ctxt,
- subctxt_fp(fp),
+ fd->subctxt,
uctxt->sc->base_addr);
binfo.rcvhdr_bufbase = HFI1_MMAP_TOKEN(RCV_HDRQ, uctxt->ctxt,
- subctxt_fp(fp),
+ fd->subctxt,
uctxt->rcvhdrq);
binfo.rcvegr_bufbase = HFI1_MMAP_TOKEN(RCV_EGRBUF, uctxt->ctxt,
- subctxt_fp(fp),
+ fd->subctxt,
uctxt->egrbufs.rcvtids[0].phys);
binfo.sdma_comp_bufbase = HFI1_MMAP_TOKEN(SDMA_COMP, uctxt->ctxt,
- subctxt_fp(fp), 0);
+ fd->subctxt, 0);
/*
* user regs are at
* (RXE_PER_CONTEXT_USER + (ctxt * RXE_PER_CONTEXT_SIZE))
*/
binfo.user_regbase = HFI1_MMAP_TOKEN(UREGS, uctxt->ctxt,
- subctxt_fp(fp), 0);
+ fd->subctxt, 0);
offset = offset_in_page((((uctxt->ctxt - dd->first_user_ctxt) *
- HFI1_MAX_SHARED_CTXTS) + subctxt_fp(fp)) *
+ HFI1_MAX_SHARED_CTXTS) + fd->subctxt) *
sizeof(*dd->events));
binfo.events_bufbase = HFI1_MMAP_TOKEN(EVENTS, uctxt->ctxt,
- subctxt_fp(fp),
+ fd->subctxt,
offset);
binfo.status_bufbase = HFI1_MMAP_TOKEN(STATUS, uctxt->ctxt,
- subctxt_fp(fp),
+ fd->subctxt,
dd->status);
if (HFI1_CAP_IS_USET(DMA_RTAIL))
binfo.rcvhdrtail_base = HFI1_MMAP_TOKEN(RTAIL, uctxt->ctxt,
- subctxt_fp(fp), 0);
+ fd->subctxt, 0);
if (uctxt->subctxt_cnt) {
binfo.subctxt_uregbase = HFI1_MMAP_TOKEN(SUBCTXT_UREGS,
uctxt->ctxt,
- subctxt_fp(fp), 0);
+ fd->subctxt, 0);
binfo.subctxt_rcvhdrbuf = HFI1_MMAP_TOKEN(SUBCTXT_RCV_HDRQ,
uctxt->ctxt,
- subctxt_fp(fp), 0);
+ fd->subctxt, 0);
binfo.subctxt_rcvegrbuf = HFI1_MMAP_TOKEN(SUBCTXT_EGRBUF,
uctxt->ctxt,
- subctxt_fp(fp), 0);
+ fd->subctxt, 0);
}
sz = (len < sizeof(binfo)) ? len : sizeof(binfo);
if (copy_to_user(ubase, &binfo, sz))
@@ -1368,7 +1357,8 @@ static int get_base_info(struct file *fp, void __user *ubase, __u32 len)
static unsigned int poll_urgent(struct file *fp,
struct poll_table_struct *pt)
{
- struct hfi1_ctxtdata *uctxt = ctxt_fp(fp);
+ struct hfi1_filedata *fd = fp->private_data;
+ struct hfi1_ctxtdata *uctxt = fd->uctxt;
struct hfi1_devdata *dd = uctxt->dd;
unsigned pollflag;
@@ -1390,7 +1380,8 @@ static unsigned int poll_urgent(struct file *fp,
static unsigned int poll_next(struct file *fp,
struct poll_table_struct *pt)
{
- struct hfi1_ctxtdata *uctxt = ctxt_fp(fp);
+ struct hfi1_filedata *fd = fp->private_data;
+ struct hfi1_ctxtdata *uctxt = fd->uctxt;
struct hfi1_devdata *dd = uctxt->dd;
unsigned pollflag;
@@ -1562,7 +1553,8 @@ static inline unsigned num_free_groups(unsigned long map, u16 *start)
static int exp_tid_setup(struct file *fp, struct hfi1_tid_info *tinfo)
{
int ret = 0;
- struct hfi1_ctxtdata *uctxt = ctxt_fp(fp);
+ struct hfi1_filedata *fd = fp->private_data;
+ struct hfi1_ctxtdata *uctxt = fd->uctxt;
struct hfi1_devdata *dd = uctxt->dd;
unsigned tid, mapped = 0, npages, ngroups, exp_groups,
tidpairs = uctxt->expected_count / 2;
@@ -1671,8 +1663,8 @@ static int exp_tid_setup(struct file *fp, struct hfi1_tid_info *tinfo)
* Now that we know how many free RcvArray entries we have,
* we can pin that many user pages.
*/
- ret = hfi1_get_user_pages(vaddr + (mapped * PAGE_SIZE),
- pinned, pages);
+ ret = hfi1_acquire_user_pages(vaddr + (mapped * PAGE_SIZE),
+ pinned, true, pages);
if (ret) {
/*
* We can't continue because the pages array won't be
@@ -1718,7 +1710,7 @@ static int exp_tid_setup(struct file *fp, struct hfi1_tid_info *tinfo)
pages[pmapped], 0,
tidsize, PCI_DMA_FROMDEVICE);
trace_hfi1_exp_rcv_set(uctxt->ctxt,
- subctxt_fp(fp),
+ fd->subctxt,
tid, vaddr,
phys[pmapped],
pages[pmapped]);
@@ -1763,7 +1755,7 @@ static int exp_tid_setup(struct file *fp, struct hfi1_tid_info *tinfo)
(((useidx & 0xffffff) << 16) |
((bitidx + bits_used) & 0xffffff)));
}
- trace_hfi1_exp_tid_map(uctxt->ctxt, subctxt_fp(fp), 0, uctxt->tidusemap,
+ trace_hfi1_exp_tid_map(uctxt->ctxt, fd->subctxt, 0, uctxt->tidusemap,
uctxt->tidmapcnt);
done:
@@ -1792,7 +1784,8 @@ bail:
static int exp_tid_free(struct file *fp, struct hfi1_tid_info *tinfo)
{
- struct hfi1_ctxtdata *uctxt = ctxt_fp(fp);
+ struct hfi1_filedata *fd = fp->private_data;
+ struct hfi1_ctxtdata *uctxt = fd->uctxt;
struct hfi1_devdata *dd = uctxt->dd;
unsigned long tidmap[uctxt->tidmapcnt];
struct page **pages;
@@ -1828,7 +1821,7 @@ static int exp_tid_free(struct file *fp, struct hfi1_tid_info *tinfo)
hfi1_put_tid(dd, tid, PT_INVALID,
0, 0);
trace_hfi1_exp_rcv_free(uctxt->ctxt,
- subctxt_fp(fp),
+ fd->subctxt,
tid, phys[i],
pages[i]);
pci_unmap_page(dd->pcidev, phys[i],
@@ -1840,12 +1833,12 @@ static int exp_tid_free(struct file *fp, struct hfi1_tid_info *tinfo)
}
}
flush_wc();
- hfi1_release_user_pages(pshadow, pcount);
+ hfi1_release_user_pages(pshadow, pcount, true);
clear_bit(bitidx, &uctxt->tidusemap[idx]);
map &= ~(1ULL<<bitidx);
}
}
- trace_hfi1_exp_tid_map(uctxt->ctxt, subctxt_fp(fp), 1, uctxt->tidusemap,
+ trace_hfi1_exp_tid_map(uctxt->ctxt, fd->subctxt, 1, uctxt->tidusemap,
uctxt->tidmapcnt);
done:
return ret;
@@ -1869,7 +1862,7 @@ static void unlock_exp_tids(struct hfi1_ctxtdata *uctxt)
uctxt->physshadow[tid] = 0;
uctxt->tid_pg_list[tid] = NULL;
pci_unmap_page(dd->pcidev, phys, PAGE_SIZE, PCI_DMA_FROMDEVICE);
- hfi1_release_user_pages(&p, 1);
+ hfi1_release_user_pages(&p, 1, true);
}
}
diff --git a/drivers/staging/rdma/hfi1/firmware.c b/drivers/staging/rdma/hfi1/firmware.c
index b4bdcf341aac..28ae42faa018 100644
--- a/drivers/staging/rdma/hfi1/firmware.c
+++ b/drivers/staging/rdma/hfi1/firmware.c
@@ -68,6 +68,10 @@
#define DEFAULT_FW_SBUS_NAME "hfi1_sbus.fw"
#define DEFAULT_FW_PCIE_NAME "hfi1_pcie.fw"
#define DEFAULT_PLATFORM_CONFIG_NAME "hfi1_platform.dat"
+#define ALT_FW_8051_NAME_ASIC "hfi1_dc8051_d.fw"
+#define ALT_FW_FABRIC_NAME "hfi1_fabric_d.fw"
+#define ALT_FW_SBUS_NAME "hfi1_sbus_d.fw"
+#define ALT_FW_PCIE_NAME "hfi1_pcie_d.fw"
static uint fw_8051_load = 1;
static uint fw_fabric_serdes_load = 1;
@@ -158,7 +162,8 @@ struct firmware_details {
static DEFINE_MUTEX(fw_mutex);
enum fw_state {
FW_EMPTY,
- FW_ACQUIRED,
+ FW_TRY,
+ FW_FINAL,
FW_ERR
};
static enum fw_state fw_state = FW_EMPTY;
@@ -428,8 +433,8 @@ static int obtain_one_firmware(struct hfi1_devdata *dd, const char *name,
ret = request_firmware(&fdet->fw, name, &dd->pcidev->dev);
if (ret) {
- dd_dev_err(dd, "cannot load firmware \"%s\", err %d\n",
- name, ret);
+ dd_dev_err(dd, "cannot find firmware \"%s\", err %d\n",
+ name, ret);
return ret;
}
@@ -539,28 +544,53 @@ done:
static void dispose_one_firmware(struct firmware_details *fdet)
{
release_firmware(fdet->fw);
- fdet->fw = NULL;
+ /* erase all previous information */
+ memset(fdet, 0, sizeof(*fdet));
}
/*
- * Called by all HFIs when loading their firmware - i.e. device probe time.
- * The first one will do the actual firmware load. Use a mutex to resolve
- * any possible race condition.
+ * Obtain the 4 firmwares from the OS. All must be obtained at once or not
+ * at all. If called with the firmware state in FW_TRY, use alternate names.
+ * On exit, this routine will have set the firmware state to one of FW_TRY,
+ * FW_FINAL, or FW_ERR.
*
- * The call to this routine cannot be moved to driver load because the kernel
- * call request_firmware() requires a device which is only available after
- * the first device probe.
+ * Must be holding fw_mutex.
*/
-static int obtain_firmware(struct hfi1_devdata *dd)
+static void __obtain_firmware(struct hfi1_devdata *dd)
{
int err = 0;
- mutex_lock(&fw_mutex);
- if (fw_state == FW_ACQUIRED) {
- goto done; /* already acquired */
- } else if (fw_state == FW_ERR) {
- err = fw_err;
- goto done; /* already tried and failed */
+ if (fw_state == FW_FINAL) /* nothing more to obtain */
+ return;
+ if (fw_state == FW_ERR) /* already in error */
+ return;
+
+ /* fw_state is FW_EMPTY or FW_TRY */
+retry:
+ if (fw_state == FW_TRY) {
+ /*
+ * We tried the original and it failed. Move to the
+ * alternate.
+ */
+ dd_dev_info(dd, "using alternate firmware names\n");
+ /*
+ * Let others run. Some systems, when missing firmware, does
+ * something that holds for 30 seconds. If we do that twice
+ * in a row it triggers task blocked warning.
+ */
+ cond_resched();
+ if (fw_8051_load)
+ dispose_one_firmware(&fw_8051);
+ if (fw_fabric_serdes_load)
+ dispose_one_firmware(&fw_fabric);
+ if (fw_sbus_load)
+ dispose_one_firmware(&fw_sbus);
+ if (fw_pcie_serdes_load)
+ dispose_one_firmware(&fw_pcie);
+ fw_8051_name = ALT_FW_8051_NAME_ASIC;
+ fw_fabric_serdes_name = ALT_FW_FABRIC_NAME;
+ fw_sbus_name = ALT_FW_SBUS_NAME;
+ fw_pcie_serdes_name = ALT_FW_PCIE_NAME;
}
if (fw_8051_load) {
@@ -588,27 +618,82 @@ static int obtain_firmware(struct hfi1_devdata *dd)
goto done;
}
+done:
+ if (err) {
+ /* oops, had problems obtaining a firmware */
+ if (fw_state == FW_EMPTY) {
+ /* retry with alternate */
+ fw_state = FW_TRY;
+ goto retry;
+ }
+ fw_state = FW_ERR;
+ fw_err = -ENOENT;
+ } else {
+ /* success */
+ if (fw_state == FW_EMPTY)
+ fw_state = FW_TRY; /* may retry later */
+ else
+ fw_state = FW_FINAL; /* cannot try again */
+ }
+}
+
+/*
+ * Called by all HFIs when loading their firmware - i.e. device probe time.
+ * The first one will do the actual firmware load. Use a mutex to resolve
+ * any possible race condition.
+ *
+ * The call to this routine cannot be moved to driver load because the kernel
+ * call request_firmware() requires a device which is only available after
+ * the first device probe.
+ */
+static int obtain_firmware(struct hfi1_devdata *dd)
+{
+ unsigned long timeout;
+ int err = 0;
+
+ mutex_lock(&fw_mutex);
+
+ /* 40s delay due to long delay on missing firmware on some systems */
+ timeout = jiffies + msecs_to_jiffies(40000);
+ while (fw_state == FW_TRY) {
+ /*
+ * Another device is trying the firmware. Wait until it
+ * decides what works (or not).
+ */
+ if (time_after(jiffies, timeout)) {
+ /* waited too long */
+ dd_dev_err(dd, "Timeout waiting for firmware try");
+ fw_state = FW_ERR;
+ fw_err = -ETIMEDOUT;
+ break;
+ }
+ mutex_unlock(&fw_mutex);
+ msleep(20); /* arbitrary delay */
+ mutex_lock(&fw_mutex);
+ }
+ /* not in FW_TRY state */
+
+ if (fw_state == FW_FINAL)
+ goto done; /* already acquired */
+ else if (fw_state == FW_ERR)
+ goto done; /* already tried and failed */
+ /* fw_state is FW_EMPTY */
+
+ /* set fw_state to FW_TRY, FW_FINAL, or FW_ERR, and fw_err */
+ __obtain_firmware(dd);
+
if (platform_config_load) {
platform_config = NULL;
err = request_firmware(&platform_config, platform_config_name,
&dd->pcidev->dev);
- if (err) {
- err = 0;
+ if (err)
platform_config = NULL;
- }
}
- /* success */
- fw_state = FW_ACQUIRED;
-
done:
- if (err) {
- fw_err = err;
- fw_state = FW_ERR;
- }
mutex_unlock(&fw_mutex);
- return err;
+ return fw_err;
}
/*
@@ -638,6 +723,38 @@ void dispose_firmware(void)
}
/*
+ * Called with the result of a firmware download.
+ *
+ * Return 1 to retry loading the firmware, 0 to stop.
+ */
+static int retry_firmware(struct hfi1_devdata *dd, int load_result)
+{
+ int retry;
+
+ mutex_lock(&fw_mutex);
+
+ if (load_result == 0) {
+ /*
+ * The load succeeded, so expect all others to do the same.
+ * Do not retry again.
+ */
+ if (fw_state == FW_TRY)
+ fw_state = FW_FINAL;
+ retry = 0; /* do NOT retry */
+ } else if (fw_state == FW_TRY) {
+ /* load failed, obtain alternate firmware */
+ __obtain_firmware(dd);
+ retry = (fw_state == FW_FINAL);
+ } else {
+ /* else in FW_FINAL or FW_ERR, no retry in either case */
+ retry = 0;
+ }
+
+ mutex_unlock(&fw_mutex);
+ return retry;
+}
+
+/*
* Write a block of data to a given array CSR. All calls will be in
* multiples of 8 bytes.
*/
@@ -951,7 +1068,7 @@ void sbus_request(struct hfi1_devdata *dd,
static void turn_off_spicos(struct hfi1_devdata *dd, int flags)
{
/* only needed on A0 */
- if (!is_a0(dd))
+ if (!is_ax(dd))
return;
dd_dev_info(dd, "Turning off spicos:%s%s\n",
@@ -1248,7 +1365,9 @@ int load_firmware(struct hfi1_devdata *dd)
fabric_serdes_addrs[dd->hfi1_id],
NUM_FABRIC_SERDES);
turn_off_spicos(dd, SPICO_FABRIC);
- ret = load_fabric_serdes_firmware(dd, &fw_fabric);
+ do {
+ ret = load_fabric_serdes_firmware(dd, &fw_fabric);
+ } while (retry_firmware(dd, ret));
clear_sbus_fast_mode(dd);
release_hw_mutex(dd);
@@ -1257,7 +1376,9 @@ int load_firmware(struct hfi1_devdata *dd)
}
if (fw_8051_load) {
- ret = load_8051_firmware(dd, &fw_8051);
+ do {
+ ret = load_8051_firmware(dd, &fw_8051);
+ } while (retry_firmware(dd, ret));
if (ret)
return ret;
}
@@ -1568,9 +1689,11 @@ int load_pcie_firmware(struct hfi1_devdata *dd)
/* both firmware loads below use the SBus */
set_sbus_fast_mode(dd);
- if (fw_sbus_load && (dd->flags & HFI1_DO_INIT_ASIC)) {
+ if (fw_sbus_load) {
turn_off_spicos(dd, SPICO_SBUS);
- ret = load_sbus_firmware(dd, &fw_sbus);
+ do {
+ ret = load_sbus_firmware(dd, &fw_sbus);
+ } while (retry_firmware(dd, ret));
if (ret)
goto done;
}
@@ -1581,7 +1704,9 @@ int load_pcie_firmware(struct hfi1_devdata *dd)
pcie_serdes_broadcast[dd->hfi1_id],
pcie_serdes_addrs[dd->hfi1_id],
NUM_PCIE_SERDES);
- ret = load_pcie_serdes_firmware(dd, &fw_pcie);
+ do {
+ ret = load_pcie_serdes_firmware(dd, &fw_pcie);
+ } while (retry_firmware(dd, ret));
if (ret)
goto done;
}
diff --git a/drivers/staging/rdma/hfi1/hfi.h b/drivers/staging/rdma/hfi1/hfi.h
index 190f7a2f6773..2611bb2e764d 100644
--- a/drivers/staging/rdma/hfi1/hfi.h
+++ b/drivers/staging/rdma/hfi1/hfi.h
@@ -100,6 +100,26 @@ extern unsigned long hfi1_cap_mask;
HFI1_CAP_MISC_MASK)
/*
+ * Control context is always 0 and handles the error packets.
+ * It also handles the VL15 and multicast packets.
+ */
+#define HFI1_CTRL_CTXT 0
+
+/*
+ * Driver context will store software counters for each of the events
+ * associated with these status registers
+ */
+#define NUM_CCE_ERR_STATUS_COUNTERS 41
+#define NUM_RCV_ERR_STATUS_COUNTERS 64
+#define NUM_MISC_ERR_STATUS_COUNTERS 13
+#define NUM_SEND_PIO_ERR_STATUS_COUNTERS 36
+#define NUM_SEND_DMA_ERR_STATUS_COUNTERS 4
+#define NUM_SEND_EGRESS_ERR_STATUS_COUNTERS 64
+#define NUM_SEND_ERR_STATUS_COUNTERS 3
+#define NUM_SEND_CTXT_ERR_STATUS_COUNTERS 5
+#define NUM_SEND_DMA_ENG_ERR_STATUS_COUNTERS 24
+
+/*
* per driver stats, either not device nor port-specific, or
* summed over all of the devices and ports.
* They are described by name via ipathfs filesystem, so layout
@@ -139,15 +159,6 @@ extern const struct pci_error_handlers hfi1_pci_err_handler;
struct hfi1_opcode_stats_perctx;
#endif
-/*
- * struct ps_state keeps state associated with RX queue "prescanning"
- * (prescanning for FECNs, and BECNs), if prescanning is in use.
- */
-struct ps_state {
- u32 ps_head;
- int initialized;
-};
-
struct ctxt_eager_bufs {
ssize_t size; /* total size of eager buffers */
u32 count; /* size of buffers array */
@@ -243,7 +254,7 @@ struct hfi1_ctxtdata {
/* chip offset of PIO buffers for this ctxt */
u32 piobufs;
/* per-context configuration flags */
- u16 flags;
+ u32 flags;
/* per-context event flags for fileops/intr communication */
unsigned long event_flags;
/* WAIT_RCV that timed out, no interrupt */
@@ -302,10 +313,6 @@ struct hfi1_ctxtdata {
struct list_head sdma_queues;
spinlock_t sdma_qlock;
-#ifdef CONFIG_PRESCAN_RXQ
- struct ps_state ps_state;
-#endif /* CONFIG_PRESCAN_RXQ */
-
/*
* The interrupt handler for a particular receive context can vary
* throughout it's lifetime. This is not a lock protected data member so
@@ -706,6 +713,8 @@ struct hfi1_pportdata {
u64 link_downed;
/* number of times link retrained successfully */
u64 link_up;
+ /* number of times a link unknown frame was reported */
+ u64 unknown_frame_count;
/* port_ltp_crc_mode is returned in 'portinfo' MADs */
u16 port_ltp_crc_mode;
/* port_crc_mode_enabled is the crc we support */
@@ -1053,6 +1062,26 @@ struct hfi1_devdata {
atomic_t drop_packet;
u8 do_drop;
+ /*
+ * Software counters for the status bits defined by the
+ * associated error status registers
+ */
+ u64 cce_err_status_cnt[NUM_CCE_ERR_STATUS_COUNTERS];
+ u64 rcv_err_status_cnt[NUM_RCV_ERR_STATUS_COUNTERS];
+ u64 misc_err_status_cnt[NUM_MISC_ERR_STATUS_COUNTERS];
+ u64 send_pio_err_status_cnt[NUM_SEND_PIO_ERR_STATUS_COUNTERS];
+ u64 send_dma_err_status_cnt[NUM_SEND_DMA_ERR_STATUS_COUNTERS];
+ u64 send_egress_err_status_cnt[NUM_SEND_EGRESS_ERR_STATUS_COUNTERS];
+ u64 send_err_status_cnt[NUM_SEND_ERR_STATUS_COUNTERS];
+
+ /* Software counter that spans all contexts */
+ u64 sw_ctxt_err_status_cnt[NUM_SEND_CTXT_ERR_STATUS_COUNTERS];
+ /* Software counter that spans all DMA engines */
+ u64 sw_send_dma_eng_err_status_cnt[
+ NUM_SEND_DMA_ENG_ERR_STATUS_COUNTERS];
+ /* Software counter that aggregates all cce_err_status errors */
+ u64 sw_cce_err_status_aggregate;
+
/* receive interrupt functions */
rhf_rcv_function_ptr *rhf_rcv_function_map;
rhf_rcv_function_ptr normal_rhf_rcv_functions[8];
@@ -1061,12 +1090,10 @@ struct hfi1_devdata {
* Handlers for outgoing data so that snoop/capture does not
* have to have its hooks in the send path
*/
- int (*process_pio_send)(struct hfi1_qp *qp, struct ahg_ib_header *ibhdr,
- u32 hdrwords, struct hfi1_sge_state *ss,
- u32 len, u32 plen, u32 dwords, u64 pbc);
- int (*process_dma_send)(struct hfi1_qp *qp, struct ahg_ib_header *ibhdr,
- u32 hdrwords, struct hfi1_sge_state *ss,
- u32 len, u32 plen, u32 dwords, u64 pbc);
+ int (*process_pio_send)(struct hfi1_qp *qp, struct hfi1_pkt_state *ps,
+ u64 pbc);
+ int (*process_dma_send)(struct hfi1_qp *qp, struct hfi1_pkt_state *ps,
+ u64 pbc);
void (*pio_inline_send)(struct hfi1_devdata *dd, struct pio_buf *pbuf,
u64 pbc, const void *from, size_t count);
@@ -1084,6 +1111,10 @@ struct hfi1_devdata {
/* Save the enabled LCB error bits */
u64 lcb_err_en;
u8 dc_shutdown;
+
+ /* receive context tail dummy address */
+ __le64 *rcvhdrtail_dummy_kvaddr;
+ dma_addr_t rcvhdrtail_dummy_physaddr;
};
/* 8051 firmware version helper */
@@ -1414,27 +1445,13 @@ void reset_link_credits(struct hfi1_devdata *dd);
void assign_remote_cm_au_table(struct hfi1_devdata *dd, u8 vcu);
int snoop_recv_handler(struct hfi1_packet *packet);
-int snoop_send_dma_handler(struct hfi1_qp *qp, struct ahg_ib_header *ibhdr,
- u32 hdrwords, struct hfi1_sge_state *ss, u32 len,
- u32 plen, u32 dwords, u64 pbc);
-int snoop_send_pio_handler(struct hfi1_qp *qp, struct ahg_ib_header *ibhdr,
- u32 hdrwords, struct hfi1_sge_state *ss, u32 len,
- u32 plen, u32 dwords, u64 pbc);
+int snoop_send_dma_handler(struct hfi1_qp *qp, struct hfi1_pkt_state *ps,
+ u64 pbc);
+int snoop_send_pio_handler(struct hfi1_qp *qp, struct hfi1_pkt_state *ps,
+ u64 pbc);
void snoop_inline_pio_send(struct hfi1_devdata *dd, struct pio_buf *pbuf,
u64 pbc, const void *from, size_t count);
-/* for use in system calls, where we want to know device type, etc. */
-#define ctxt_fp(fp) \
- (((struct hfi1_filedata *)(fp)->private_data)->uctxt)
-#define subctxt_fp(fp) \
- (((struct hfi1_filedata *)(fp)->private_data)->subctxt)
-#define tidcursor_fp(fp) \
- (((struct hfi1_filedata *)(fp)->private_data)->tidcursor)
-#define user_sdma_pkt_fp(fp) \
- (((struct hfi1_filedata *)(fp)->private_data)->pq)
-#define user_sdma_comp_fp(fp) \
- (((struct hfi1_filedata *)(fp)->private_data)->cq)
-
static inline struct hfi1_devdata *dd_from_ppd(struct hfi1_pportdata *ppd)
{
return ppd->dd;
@@ -1570,8 +1587,8 @@ void hfi1_set_led_override(struct hfi1_pportdata *ppd, unsigned int val);
*/
#define DEFAULT_RCVHDR_ENTSIZE 32
-int hfi1_get_user_pages(unsigned long, size_t, struct page **);
-void hfi1_release_user_pages(struct page **, size_t);
+int hfi1_acquire_user_pages(unsigned long, size_t, bool, struct page **);
+void hfi1_release_user_pages(struct page **, size_t, bool);
static inline void clear_rcvhdrtail(const struct hfi1_ctxtdata *rcd)
{
@@ -1612,7 +1629,6 @@ void hfi1_pcie_flr(struct hfi1_devdata *);
int pcie_speeds(struct hfi1_devdata *);
void request_msix(struct hfi1_devdata *, u32 *, struct hfi1_msix_entry *);
void hfi1_enable_intx(struct pci_dev *);
-void hfi1_nomsix(struct hfi1_devdata *);
void restore_pci_variables(struct hfi1_devdata *dd);
int do_pcie_gen3_transition(struct hfi1_devdata *dd);
int parse_platform_config(struct hfi1_devdata *dd);
@@ -1649,7 +1665,7 @@ void update_sge(struct hfi1_sge_state *ss, u32 length);
extern unsigned int hfi1_max_mtu;
extern unsigned int hfi1_cu;
extern unsigned int user_credit_return_threshold;
-extern uint num_rcv_contexts;
+extern int num_user_contexts;
extern unsigned n_krcvqs;
extern u8 krcvqs[];
extern int krcvqsset;
@@ -1713,7 +1729,7 @@ static inline u64 hfi1_pkt_default_send_ctxt_mask(struct hfi1_devdata *dd,
else
base_sc_integrity |= HFI1_PKT_KERNEL_SC_INTEGRITY;
- if (is_a0(dd))
+ if (is_ax(dd))
/* turn off send-side job key checks - A0 erratum */
return base_sc_integrity &
~SEND_CTXT_CHECK_ENABLE_CHECK_JOB_KEY_SMASK;
@@ -1740,7 +1756,7 @@ static inline u64 hfi1_pkt_base_sdma_integrity(struct hfi1_devdata *dd)
| SEND_DMA_CHECK_ENABLE_CHECK_VL_SMASK
| SEND_DMA_CHECK_ENABLE_CHECK_ENABLE_SMASK;
- if (is_a0(dd))
+ if (is_ax(dd))
/* turn off send-side job key checks - A0 erratum */
return base_sdma_integrity &
~SEND_DMA_CHECK_ENABLE_CHECK_JOB_KEY_SMASK;
diff --git a/drivers/staging/rdma/hfi1/init.c b/drivers/staging/rdma/hfi1/init.c
index 8666f3ad24e9..4dd8051aba7e 100644
--- a/drivers/staging/rdma/hfi1/init.c
+++ b/drivers/staging/rdma/hfi1/init.c
@@ -60,6 +60,7 @@
#include "hfi.h"
#include "device.h"
#include "common.h"
+#include "trace.h"
#include "mad.h"
#include "sdma.h"
#include "debugfs.h"
@@ -81,15 +82,15 @@
* Number of user receive contexts we are configured to use (to allow for more
* pio buffers per ctxt, etc.) Zero means use one user context per CPU.
*/
-uint num_rcv_contexts;
-module_param_named(num_rcv_contexts, num_rcv_contexts, uint, S_IRUGO);
+int num_user_contexts = -1;
+module_param_named(num_user_contexts, num_user_contexts, uint, S_IRUGO);
MODULE_PARM_DESC(
- num_rcv_contexts, "Set max number of user receive contexts to use");
+ num_user_contexts, "Set max number of user contexts to use");
u8 krcvqs[RXE_NUM_DATA_VL];
int krcvqsset;
module_param_array(krcvqs, byte, &krcvqsset, S_IRUGO);
-MODULE_PARM_DESC(krcvqs, "Array of the number of kernel receive queues by VL");
+MODULE_PARM_DESC(krcvqs, "Array of the number of non-control kernel receive queues by VL");
/* computed based on above array */
unsigned n_krcvqs;
@@ -112,7 +113,7 @@ MODULE_PARM_DESC(hdrq_entsize, "Size of header queue entries: 2 - 8B, 16 - 64B (
unsigned int user_credit_return_threshold = 33; /* default is 33% */
module_param(user_credit_return_threshold, uint, S_IRUGO);
-MODULE_PARM_DESC(user_credit_return_theshold, "Credit return threshold for user send contexts, return when unreturned credits passes this many blocks (in percent of allocated blocks, 0 is off)");
+MODULE_PARM_DESC(user_credit_return_threshold, "Credit return threshold for user send contexts, return when unreturned credits passes this many blocks (in percent of allocated blocks, 0 is off)");
static inline u64 encode_rcv_header_entry_size(u16);
@@ -129,6 +130,9 @@ int hfi1_create_ctxts(struct hfi1_devdata *dd)
int ret;
int local_node_id = pcibus_to_node(dd->pcidev->bus);
+ /* Control context has to be always 0 */
+ BUILD_BUG_ON(HFI1_CTRL_CTXT != 0);
+
if (local_node_id < 0)
local_node_id = numa_node_id();
dd->assigned_node_id = local_node_id;
@@ -158,6 +162,10 @@ int hfi1_create_ctxts(struct hfi1_devdata *dd)
HFI1_CAP_KGET(NODROP_RHQ_FULL) |
HFI1_CAP_KGET(NODROP_EGR_FULL) |
HFI1_CAP_KGET(DMA_RTAIL);
+
+ /* Control context must use DMA_RTAIL */
+ if (rcd->ctxt == HFI1_CTRL_CTXT)
+ rcd->flags |= HFI1_CAP_DMA_RTAIL;
rcd->seq_cnt = 1;
rcd->sc = sc_alloc(dd, SC_ACK, rcd->rcvhdrqentsize, dd->node);
@@ -208,7 +216,7 @@ struct hfi1_ctxtdata *hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, u32 ctxt)
if (rcd) {
u32 rcvtids, max_entries;
- dd_dev_info(dd, "%s: setting up context %u\n", __func__, ctxt);
+ hfi1_cdbg(PROC, "setting up context %u\n", ctxt);
INIT_LIST_HEAD(&rcd->qp_wait_list);
rcd->ppd = ppd;
@@ -279,8 +287,9 @@ struct hfi1_ctxtdata *hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, u32 ctxt)
rcd->ctxt);
rcd->egrbufs.count = MAX_EAGER_ENTRIES;
}
- dd_dev_info(dd, "ctxt%u: max Eager buffer RcvArray entries: %u\n",
- rcd->ctxt, rcd->egrbufs.count);
+ hfi1_cdbg(PROC,
+ "ctxt%u: max Eager buffer RcvArray entries: %u\n",
+ rcd->ctxt, rcd->egrbufs.count);
/*
* Allocate array that will hold the eager buffer accounting
@@ -308,8 +317,8 @@ struct hfi1_ctxtdata *hfi1_create_ctxtdata(struct hfi1_pportdata *ppd, u32 ctxt)
*/
if (rcd->egrbufs.size < hfi1_max_mtu) {
rcd->egrbufs.size = __roundup_pow_of_two(hfi1_max_mtu);
- dd_dev_info(dd,
- "ctxt%u: eager bufs size too small. Adjusting to %zu\n",
+ hfi1_cdbg(PROC,
+ "ctxt%u: eager bufs size too small. Adjusting to %zu\n",
rcd->ctxt, rcd->egrbufs.size);
}
rcd->egrbufs.rcvtid_size = HFI1_MAX_EAGER_BUFFER_SIZE;
@@ -601,20 +610,19 @@ static int create_workqueues(struct hfi1_devdata *dd)
for (pidx = 0; pidx < dd->num_pports; ++pidx) {
ppd = dd->pport + pidx;
if (!ppd->hfi1_wq) {
- char wq_name[8]; /* 3 + 2 + 1 + 1 + 1 */
-
- snprintf(wq_name, sizeof(wq_name), "hfi%d_%d",
- dd->unit, pidx);
ppd->hfi1_wq =
- create_singlethread_workqueue(wq_name);
+ alloc_workqueue(
+ "hfi%d_%d",
+ WQ_SYSFS | WQ_HIGHPRI | WQ_CPU_INTENSIVE,
+ dd->num_sdma,
+ dd->unit, pidx);
if (!ppd->hfi1_wq)
goto wq_error;
}
}
return 0;
wq_error:
- pr_err("create_singlethread_workqueue failed for port %d\n",
- pidx + 1);
+ pr_err("alloc_workqueue failed for port %d\n", pidx + 1);
for (pidx = 0; pidx < dd->num_pports; ++pidx) {
ppd = dd->pport + pidx;
if (ppd->hfi1_wq) {
@@ -670,7 +678,7 @@ int hfi1_init(struct hfi1_devdata *dd, int reinit)
dd->process_dma_send = hfi1_verbs_send_dma;
dd->pio_inline_send = pio_copy;
- if (is_a0(dd)) {
+ if (is_ax(dd)) {
atomic_set(&dd->drop_packet, DROP_PACKET_ON);
dd->do_drop = 1;
} else {
@@ -691,6 +699,18 @@ int hfi1_init(struct hfi1_devdata *dd, int reinit)
if (ret)
goto done;
+ /* allocate dummy tail memory for all receive contexts */
+ dd->rcvhdrtail_dummy_kvaddr = dma_zalloc_coherent(
+ &dd->pcidev->dev, sizeof(u64),
+ &dd->rcvhdrtail_dummy_physaddr,
+ GFP_KERNEL);
+
+ if (!dd->rcvhdrtail_dummy_kvaddr) {
+ dd_dev_err(dd, "cannot allocate dummy tail memory\n");
+ ret = -ENOMEM;
+ goto done;
+ }
+
/* dd->rcd can be NULL if early initialization failed */
for (i = 0; dd->rcd && i < dd->first_user_ctxt; ++i) {
/*
@@ -1266,6 +1286,14 @@ static void cleanup_device_data(struct hfi1_devdata *dd)
tmp = dd->rcd;
dd->rcd = NULL;
spin_unlock_irqrestore(&dd->uctxt_lock, flags);
+
+ if (dd->rcvhdrtail_dummy_kvaddr) {
+ dma_free_coherent(&dd->pcidev->dev, sizeof(u64),
+ (void *)dd->rcvhdrtail_dummy_kvaddr,
+ dd->rcvhdrtail_dummy_physaddr);
+ dd->rcvhdrtail_dummy_kvaddr = NULL;
+ }
+
for (ctxt = 0; tmp && ctxt < dd->num_rcv_contexts; ctxt++) {
struct hfi1_ctxtdata *rcd = tmp[ctxt];
@@ -1308,6 +1336,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int ret = 0, j, pidx, initfail;
struct hfi1_devdata *dd = NULL;
+ struct hfi1_pportdata *ppd;
/* First, lock the non-writable module parameters */
HFI1_CAP_LOCK();
@@ -1322,6 +1351,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (!encode_rcv_header_entry_size(hfi1_hdrq_entsize)) {
hfi1_early_err(&pdev->dev, "Invalid HdrQ Entry size %u\n",
hfi1_hdrq_entsize);
+ ret = -EINVAL;
goto bail;
}
@@ -1403,8 +1433,14 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (initfail || ret) {
stop_timers(dd);
flush_workqueue(ib_wq);
- for (pidx = 0; pidx < dd->num_pports; ++pidx)
+ for (pidx = 0; pidx < dd->num_pports; ++pidx) {
hfi1_quiet_serdes(dd->pport + pidx);
+ ppd = dd->pport + pidx;
+ if (ppd->hfi1_wq) {
+ destroy_workqueue(ppd->hfi1_wq);
+ ppd->hfi1_wq = NULL;
+ }
+ }
if (!j)
hfi1_device_remove(dd);
if (!ret)
@@ -1521,6 +1557,14 @@ int hfi1_create_rcvhdrq(struct hfi1_devdata *dd, struct hfi1_ctxtdata *rcd)
reg = (dd->rcvhdrsize & RCV_HDR_SIZE_HDR_SIZE_MASK)
<< RCV_HDR_SIZE_HDR_SIZE_SHIFT;
write_kctxt_csr(dd, rcd->ctxt, RCV_HDR_SIZE, reg);
+
+ /*
+ * Program dummy tail address for every receive context
+ * before enabling any receive context
+ */
+ write_kctxt_csr(dd, rcd->ctxt, RCV_HDR_TAIL_ADDR,
+ dd->rcvhdrtail_dummy_physaddr);
+
return 0;
bail_free:
@@ -1660,9 +1704,11 @@ int hfi1_setup_eagerbufs(struct hfi1_ctxtdata *rcd)
rcd->egrbufs.numbufs = idx;
rcd->egrbufs.size = alloced_bytes;
- dd_dev_info(dd, "ctxt%u: Alloced %u rcv tid entries @ %uKB, total %zuKB\n",
- rcd->ctxt, rcd->egrbufs.alloced, rcd->egrbufs.rcvtid_size,
- rcd->egrbufs.size);
+ hfi1_cdbg(PROC,
+ "ctxt%u: Alloced %u rcv tid entries @ %uKB, total %zuKB\n",
+ rcd->ctxt, rcd->egrbufs.alloced, rcd->egrbufs.rcvtid_size,
+ rcd->egrbufs.size);
+
/*
* Set the contexts rcv array head update threshold to the closest
@@ -1683,13 +1729,14 @@ int hfi1_setup_eagerbufs(struct hfi1_ctxtdata *rcd)
rcd->expected_count = MAX_TID_PAIR_ENTRIES * 2;
rcd->expected_base = rcd->eager_base + egrtop;
- dd_dev_info(dd, "ctxt%u: eager:%u, exp:%u, egrbase:%u, expbase:%u\n",
- rcd->ctxt, rcd->egrbufs.alloced, rcd->expected_count,
- rcd->eager_base, rcd->expected_base);
+ hfi1_cdbg(PROC, "ctxt%u: eager:%u, exp:%u, egrbase:%u, expbase:%u\n",
+ rcd->ctxt, rcd->egrbufs.alloced, rcd->expected_count,
+ rcd->eager_base, rcd->expected_base);
if (!hfi1_rcvbuf_validate(rcd->egrbufs.rcvtid_size, PT_EAGER, &order)) {
- dd_dev_err(dd, "ctxt%u: current Eager buffer size is invalid %u\n",
- rcd->ctxt, rcd->egrbufs.rcvtid_size);
+ hfi1_cdbg(PROC,
+ "ctxt%u: current Eager buffer size is invalid %u\n",
+ rcd->ctxt, rcd->egrbufs.rcvtid_size);
ret = -EINVAL;
goto bail;
}
diff --git a/drivers/staging/rdma/hfi1/iowait.h b/drivers/staging/rdma/hfi1/iowait.h
index fa361b405851..e8ba5606d08d 100644
--- a/drivers/staging/rdma/hfi1/iowait.h
+++ b/drivers/staging/rdma/hfi1/iowait.h
@@ -150,12 +150,14 @@ static inline void iowait_init(
* iowait_schedule() - initialize wait structure
* @wait: wait struct to schedule
* @wq: workqueue for schedule
+ * @cpu: cpu
*/
static inline void iowait_schedule(
struct iowait *wait,
- struct workqueue_struct *wq)
+ struct workqueue_struct *wq,
+ int cpu)
{
- queue_work(wq, &wait->iowork);
+ queue_work_on(cpu, wq, &wait->iowork);
}
/**
diff --git a/drivers/staging/rdma/hfi1/mad.c b/drivers/staging/rdma/hfi1/mad.c
index 32f703736185..4f5dbd14b5de 100644
--- a/drivers/staging/rdma/hfi1/mad.c
+++ b/drivers/staging/rdma/hfi1/mad.c
@@ -84,7 +84,7 @@ static void send_trap(struct hfi1_ibport *ibp, void *data, unsigned len)
{
struct ib_mad_send_buf *send_buf;
struct ib_mad_agent *agent;
- struct ib_smp *smp;
+ struct opa_smp *smp;
int ret;
unsigned long flags;
unsigned long timeout;
@@ -117,15 +117,15 @@ static void send_trap(struct hfi1_ibport *ibp, void *data, unsigned len)
return;
smp = send_buf->mad;
- smp->base_version = IB_MGMT_BASE_VERSION;
+ smp->base_version = OPA_MGMT_BASE_VERSION;
smp->mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
- smp->class_version = 1;
+ smp->class_version = OPA_SMI_CLASS_VERSION;
smp->method = IB_MGMT_METHOD_TRAP;
ibp->tid++;
smp->tid = cpu_to_be64(ibp->tid);
smp->attr_id = IB_SMP_ATTR_NOTICE;
/* o14-1: smp->mkey = 0; */
- memcpy(smp->data, data, len);
+ memcpy(smp->route.lid.data, data, len);
spin_lock_irqsave(&ibp->lock, flags);
if (!ibp->sm_ah) {
@@ -164,11 +164,16 @@ static void send_trap(struct hfi1_ibport *ibp, void *data, unsigned len)
* Send a bad [PQ]_Key trap (ch. 14.3.8).
*/
void hfi1_bad_pqkey(struct hfi1_ibport *ibp, __be16 trap_num, u32 key, u32 sl,
- u32 qp1, u32 qp2, __be16 lid1, __be16 lid2)
+ u32 qp1, u32 qp2, u16 lid1, u16 lid2)
{
- struct ib_mad_notice_attr data;
+ struct opa_mad_notice_attr data;
+ u32 lid = ppd_from_ibp(ibp)->lid;
+ u32 _lid1 = lid1;
+ u32 _lid2 = lid2;
- if (trap_num == IB_NOTICE_TRAP_BAD_PKEY)
+ memset(&data, 0, sizeof(data));
+
+ if (trap_num == OPA_TRAP_BAD_P_KEY)
ibp->pkey_violations++;
else
ibp->qkey_violations++;
@@ -176,17 +181,15 @@ void hfi1_bad_pqkey(struct hfi1_ibport *ibp, __be16 trap_num, u32 key, u32 sl,
/* Send violation trap */
data.generic_type = IB_NOTICE_TYPE_SECURITY;
- data.prod_type_msb = 0;
data.prod_type_lsb = IB_NOTICE_PROD_CA;
data.trap_num = trap_num;
- data.issuer_lid = cpu_to_be16(ppd_from_ibp(ibp)->lid);
- data.toggle_count = 0;
- memset(&data.details, 0, sizeof(data.details));
- data.details.ntc_257_258.lid1 = lid1;
- data.details.ntc_257_258.lid2 = lid2;
- data.details.ntc_257_258.key = cpu_to_be32(key);
- data.details.ntc_257_258.sl_qp1 = cpu_to_be32((sl << 28) | qp1);
- data.details.ntc_257_258.qp2 = cpu_to_be32(qp2);
+ data.issuer_lid = cpu_to_be32(lid);
+ data.ntc_257_258.lid1 = cpu_to_be32(_lid1);
+ data.ntc_257_258.lid2 = cpu_to_be32(_lid2);
+ data.ntc_257_258.key = cpu_to_be32(key);
+ data.ntc_257_258.sl = sl << 3;
+ data.ntc_257_258.qp1 = cpu_to_be32(qp1);
+ data.ntc_257_258.qp2 = cpu_to_be32(qp2);
send_trap(ibp, &data, sizeof(data));
}
@@ -197,32 +200,30 @@ void hfi1_bad_pqkey(struct hfi1_ibport *ibp, __be16 trap_num, u32 key, u32 sl,
static void bad_mkey(struct hfi1_ibport *ibp, struct ib_mad_hdr *mad,
__be64 mkey, __be32 dr_slid, u8 return_path[], u8 hop_cnt)
{
- struct ib_mad_notice_attr data;
+ struct opa_mad_notice_attr data;
+ u32 lid = ppd_from_ibp(ibp)->lid;
+ memset(&data, 0, sizeof(data));
/* Send violation trap */
data.generic_type = IB_NOTICE_TYPE_SECURITY;
- data.prod_type_msb = 0;
data.prod_type_lsb = IB_NOTICE_PROD_CA;
- data.trap_num = IB_NOTICE_TRAP_BAD_MKEY;
- data.issuer_lid = cpu_to_be16(ppd_from_ibp(ibp)->lid);
- data.toggle_count = 0;
- memset(&data.details, 0, sizeof(data.details));
- data.details.ntc_256.lid = data.issuer_lid;
- data.details.ntc_256.method = mad->method;
- data.details.ntc_256.attr_id = mad->attr_id;
- data.details.ntc_256.attr_mod = mad->attr_mod;
- data.details.ntc_256.mkey = mkey;
+ data.trap_num = OPA_TRAP_BAD_M_KEY;
+ data.issuer_lid = cpu_to_be32(lid);
+ data.ntc_256.lid = data.issuer_lid;
+ data.ntc_256.method = mad->method;
+ data.ntc_256.attr_id = mad->attr_id;
+ data.ntc_256.attr_mod = mad->attr_mod;
+ data.ntc_256.mkey = mkey;
if (mad->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) {
-
- data.details.ntc_256.dr_slid = (__force __be16)dr_slid;
- data.details.ntc_256.dr_trunc_hop = IB_NOTICE_TRAP_DR_NOTICE;
- if (hop_cnt > ARRAY_SIZE(data.details.ntc_256.dr_rtn_path)) {
- data.details.ntc_256.dr_trunc_hop |=
+ data.ntc_256.dr_slid = dr_slid;
+ data.ntc_256.dr_trunc_hop = IB_NOTICE_TRAP_DR_NOTICE;
+ if (hop_cnt > ARRAY_SIZE(data.ntc_256.dr_rtn_path)) {
+ data.ntc_256.dr_trunc_hop |=
IB_NOTICE_TRAP_DR_TRUNC;
- hop_cnt = ARRAY_SIZE(data.details.ntc_256.dr_rtn_path);
+ hop_cnt = ARRAY_SIZE(data.ntc_256.dr_rtn_path);
}
- data.details.ntc_256.dr_trunc_hop |= hop_cnt;
- memcpy(data.details.ntc_256.dr_rtn_path, return_path,
+ data.ntc_256.dr_trunc_hop |= hop_cnt;
+ memcpy(data.ntc_256.dr_rtn_path, return_path,
hop_cnt);
}
@@ -234,17 +235,17 @@ static void bad_mkey(struct hfi1_ibport *ibp, struct ib_mad_hdr *mad,
*/
void hfi1_cap_mask_chg(struct hfi1_ibport *ibp)
{
- struct ib_mad_notice_attr data;
+ struct opa_mad_notice_attr data;
+ u32 lid = ppd_from_ibp(ibp)->lid;
+
+ memset(&data, 0, sizeof(data));
data.generic_type = IB_NOTICE_TYPE_INFO;
- data.prod_type_msb = 0;
data.prod_type_lsb = IB_NOTICE_PROD_CA;
- data.trap_num = IB_NOTICE_TRAP_CAP_MASK_CHG;
- data.issuer_lid = cpu_to_be16(ppd_from_ibp(ibp)->lid);
- data.toggle_count = 0;
- memset(&data.details, 0, sizeof(data.details));
- data.details.ntc_144.lid = data.issuer_lid;
- data.details.ntc_144.new_cap_mask = cpu_to_be32(ibp->port_cap_flags);
+ data.trap_num = OPA_TRAP_CHANGE_CAPABILITY;
+ data.issuer_lid = cpu_to_be32(lid);
+ data.ntc_144.lid = data.issuer_lid;
+ data.ntc_144.new_cap_mask = cpu_to_be32(ibp->port_cap_flags);
send_trap(ibp, &data, sizeof(data));
}
@@ -254,17 +255,17 @@ void hfi1_cap_mask_chg(struct hfi1_ibport *ibp)
*/
void hfi1_sys_guid_chg(struct hfi1_ibport *ibp)
{
- struct ib_mad_notice_attr data;
+ struct opa_mad_notice_attr data;
+ u32 lid = ppd_from_ibp(ibp)->lid;
+
+ memset(&data, 0, sizeof(data));
data.generic_type = IB_NOTICE_TYPE_INFO;
- data.prod_type_msb = 0;
data.prod_type_lsb = IB_NOTICE_PROD_CA;
- data.trap_num = IB_NOTICE_TRAP_SYS_GUID_CHG;
- data.issuer_lid = cpu_to_be16(ppd_from_ibp(ibp)->lid);
- data.toggle_count = 0;
- memset(&data.details, 0, sizeof(data.details));
- data.details.ntc_145.lid = data.issuer_lid;
- data.details.ntc_145.new_sys_guid = ib_hfi1_sys_image_guid;
+ data.trap_num = OPA_TRAP_CHANGE_SYSGUID;
+ data.issuer_lid = cpu_to_be32(lid);
+ data.ntc_145.new_sys_guid = ib_hfi1_sys_image_guid;
+ data.ntc_145.lid = data.issuer_lid;
send_trap(ibp, &data, sizeof(data));
}
@@ -274,18 +275,18 @@ void hfi1_sys_guid_chg(struct hfi1_ibport *ibp)
*/
void hfi1_node_desc_chg(struct hfi1_ibport *ibp)
{
- struct ib_mad_notice_attr data;
+ struct opa_mad_notice_attr data;
+ u32 lid = ppd_from_ibp(ibp)->lid;
+
+ memset(&data, 0, sizeof(data));
data.generic_type = IB_NOTICE_TYPE_INFO;
- data.prod_type_msb = 0;
data.prod_type_lsb = IB_NOTICE_PROD_CA;
- data.trap_num = IB_NOTICE_TRAP_CAP_MASK_CHG;
- data.issuer_lid = cpu_to_be16(ppd_from_ibp(ibp)->lid);
- data.toggle_count = 0;
- memset(&data.details, 0, sizeof(data.details));
- data.details.ntc_144.lid = data.issuer_lid;
- data.details.ntc_144.local_changes = 1;
- data.details.ntc_144.change_flags = IB_NOTICE_TRAP_NODE_DESC_CHG;
+ data.trap_num = OPA_TRAP_CHANGE_CAPABILITY;
+ data.issuer_lid = cpu_to_be32(lid);
+ data.ntc_144.lid = data.issuer_lid;
+ data.ntc_144.change_flags =
+ cpu_to_be16(OPA_NOTICE_TRAP_NODE_DESC_CHG);
send_trap(ibp, &data, sizeof(data));
}
@@ -2076,13 +2077,20 @@ struct opa_aggregate {
u8 data[0];
};
-/* Request contains first two fields, response contains those plus the rest */
+#define MSK_LLI 0x000000f0
+#define MSK_LLI_SFT 4
+#define MSK_LER 0x0000000f
+#define MSK_LER_SFT 0
+#define ADD_LLI 8
+#define ADD_LER 2
+
+/* Request contains first three fields, response contains those plus the rest */
struct opa_port_data_counters_msg {
__be64 port_select_mask[4];
__be32 vl_select_mask;
+ __be32 resolution;
/* Response fields follow */
- __be32 reserved1;
struct _port_dctrs {
u8 port_number;
u8 reserved2[3];
@@ -2271,34 +2279,8 @@ static void a0_portstatus(struct hfi1_pportdata *ppd,
{
if (!is_bx(ppd->dd)) {
unsigned long vl;
- int vfi = 0;
u64 max_vl_xmit_wait = 0, tmp;
u32 vl_all_mask = VL_MASK_ALL;
- u64 rcv_data, rcv_bubble;
-
- rcv_data = be64_to_cpu(rsp->port_rcv_data);
- rcv_bubble = be64_to_cpu(rsp->port_rcv_bubble);
- /* In the measured time period, calculate the total number
- * of flits that were received. Subtract out one false
- * rcv_bubble increment for every 32 received flits but
- * don't let the number go negative.
- */
- if (rcv_bubble >= (rcv_data>>5)) {
- rcv_bubble -= (rcv_data>>5);
- rsp->port_rcv_bubble = cpu_to_be64(rcv_bubble);
- }
- for_each_set_bit(vl, (unsigned long *)&(vl_select_mask),
- 8 * sizeof(vl_select_mask)) {
- rcv_data = be64_to_cpu(rsp->vls[vfi].port_vl_rcv_data);
- rcv_bubble =
- be64_to_cpu(rsp->vls[vfi].port_vl_rcv_bubble);
- if (rcv_bubble >= (rcv_data>>5)) {
- rcv_bubble -= (rcv_data>>5);
- rsp->vls[vfi].port_vl_rcv_bubble =
- cpu_to_be64(rcv_bubble);
- }
- vfi++;
- }
for_each_set_bit(vl, (unsigned long *)&(vl_all_mask),
8 * sizeof(vl_all_mask)) {
@@ -2363,8 +2345,6 @@ static int pma_get_opa_portstatus(struct opa_pma_mad *pmp,
CNTR_INVALID_VL));
rsp->port_rcv_data = cpu_to_be64(read_dev_cntr(dd, C_DC_RCV_FLITS,
CNTR_INVALID_VL));
- rsp->port_rcv_bubble =
- cpu_to_be64(read_dev_cntr(dd, C_DC_RCV_BBL, CNTR_INVALID_VL));
rsp->port_xmit_pkts = cpu_to_be64(read_dev_cntr(dd, C_DC_XMIT_PKTS,
CNTR_INVALID_VL));
rsp->port_rcv_pkts = cpu_to_be64(read_dev_cntr(dd, C_DC_RCV_PKTS,
@@ -2434,9 +2414,6 @@ static int pma_get_opa_portstatus(struct opa_pma_mad *pmp,
tmp = read_dev_cntr(dd, C_DC_RX_FLIT_VL, idx_from_vl(vl));
rsp->vls[vfi].port_vl_rcv_data = cpu_to_be64(tmp);
- rsp->vls[vfi].port_vl_rcv_bubble =
- cpu_to_be64(read_dev_cntr(dd, C_DC_RCV_BBL_VL,
- idx_from_vl(vl)));
rsp->vls[vfi].port_vl_rcv_pkts =
cpu_to_be64(read_dev_cntr(dd, C_DC_RX_PKT_VL,
@@ -2474,7 +2451,8 @@ static int pma_get_opa_portstatus(struct opa_pma_mad *pmp,
return reply((struct ib_mad_hdr *)pmp);
}
-static u64 get_error_counter_summary(struct ib_device *ibdev, u8 port)
+static u64 get_error_counter_summary(struct ib_device *ibdev, u8 port,
+ u8 res_lli, u8 res_ler)
{
struct hfi1_devdata *dd = dd_from_ibdev(ibdev);
struct hfi1_ibport *ibp = to_iport(ibdev, port);
@@ -2490,14 +2468,14 @@ static u64 get_error_counter_summary(struct ib_device *ibdev, u8 port)
CNTR_INVALID_VL);
error_counter_summary += read_dev_cntr(dd, C_DC_RMT_PHY_ERR,
CNTR_INVALID_VL);
- error_counter_summary += read_dev_cntr(dd, C_DC_TX_REPLAY,
- CNTR_INVALID_VL);
- error_counter_summary += read_dev_cntr(dd, C_DC_RX_REPLAY,
- CNTR_INVALID_VL);
- error_counter_summary += read_dev_cntr(dd, C_DC_SEQ_CRC_CNT,
- CNTR_INVALID_VL);
- error_counter_summary += read_dev_cntr(dd, C_DC_REINIT_FROM_PEER_CNT,
- CNTR_INVALID_VL);
+ /* local link integrity must be right-shifted by the lli resolution */
+ tmp = read_dev_cntr(dd, C_DC_RX_REPLAY, CNTR_INVALID_VL);
+ tmp += read_dev_cntr(dd, C_DC_TX_REPLAY, CNTR_INVALID_VL);
+ error_counter_summary += (tmp >> res_lli);
+ /* link error recovery must b right-shifted by the ler resolution */
+ tmp = read_dev_cntr(dd, C_DC_SEQ_CRC_CNT, CNTR_INVALID_VL);
+ tmp += read_dev_cntr(dd, C_DC_REINIT_FROM_PEER_CNT, CNTR_INVALID_VL);
+ error_counter_summary += (tmp >> res_ler);
error_counter_summary += read_dev_cntr(dd, C_DC_RCV_ERR,
CNTR_INVALID_VL);
error_counter_summary += read_dev_cntr(dd, C_RCV_OVF, CNTR_INVALID_VL);
@@ -2519,32 +2497,8 @@ static void a0_datacounters(struct hfi1_devdata *dd, struct _port_dctrs *rsp,
if (!is_bx(dd)) {
unsigned long vl;
int vfi = 0;
- u64 rcv_data, rcv_bubble, sum_vl_xmit_wait = 0;
-
- rcv_data = be64_to_cpu(rsp->port_rcv_data);
- rcv_bubble = be64_to_cpu(rsp->port_rcv_bubble);
- /* In the measured time period, calculate the total number
- * of flits that were received. Subtract out one false
- * rcv_bubble increment for every 32 received flits but
- * don't let the number go negative.
- */
- if (rcv_bubble >= (rcv_data>>5)) {
- rcv_bubble -= (rcv_data>>5);
- rsp->port_rcv_bubble = cpu_to_be64(rcv_bubble);
- }
- for_each_set_bit(vl, (unsigned long *)&(vl_select_mask),
- 8 * sizeof(vl_select_mask)) {
- rcv_data = be64_to_cpu(rsp->vls[vfi].port_vl_rcv_data);
- rcv_bubble =
- be64_to_cpu(rsp->vls[vfi].port_vl_rcv_bubble);
- if (rcv_bubble >= (rcv_data>>5)) {
- rcv_bubble -= (rcv_data>>5);
- rsp->vls[vfi].port_vl_rcv_bubble =
- cpu_to_be64(rcv_bubble);
- }
- vfi++;
- }
- vfi = 0;
+ u64 sum_vl_xmit_wait = 0;
+
for_each_set_bit(vl, (unsigned long *)&(vl_select_mask),
8 * sizeof(vl_select_mask)) {
u64 tmp = sum_vl_xmit_wait +
@@ -2575,6 +2529,7 @@ static int pma_get_opa_datacounters(struct opa_pma_mad *pmp,
u32 num_ports;
u8 num_pslm;
u8 lq, num_vls;
+ u8 res_lli, res_ler;
u64 port_mask;
unsigned long port_num;
unsigned long vl;
@@ -2585,6 +2540,10 @@ static int pma_get_opa_datacounters(struct opa_pma_mad *pmp,
num_pslm = hweight64(be64_to_cpu(req->port_select_mask[3]));
num_vls = hweight32(be32_to_cpu(req->vl_select_mask));
vl_select_mask = be32_to_cpu(req->vl_select_mask);
+ res_lli = (u8)(be32_to_cpu(req->resolution) & MSK_LLI) >> MSK_LLI_SFT;
+ res_lli = res_lli ? res_lli + ADD_LLI : 0;
+ res_ler = (u8)(be32_to_cpu(req->resolution) & MSK_LER) >> MSK_LER_SFT;
+ res_ler = res_ler ? res_ler + ADD_LER : 0;
if (num_ports != 1 || (vl_select_mask & ~VL_MASK_ALL)) {
pmp->mad_hdr.status |= IB_SMP_INVALID_FIELD;
@@ -2635,8 +2594,6 @@ static int pma_get_opa_datacounters(struct opa_pma_mad *pmp,
CNTR_INVALID_VL));
rsp->port_rcv_data = cpu_to_be64(read_dev_cntr(dd, C_DC_RCV_FLITS,
CNTR_INVALID_VL));
- rsp->port_rcv_bubble =
- cpu_to_be64(read_dev_cntr(dd, C_DC_RCV_BBL, CNTR_INVALID_VL));
rsp->port_xmit_pkts = cpu_to_be64(read_dev_cntr(dd, C_DC_XMIT_PKTS,
CNTR_INVALID_VL));
rsp->port_rcv_pkts = cpu_to_be64(read_dev_cntr(dd, C_DC_RCV_PKTS,
@@ -2655,7 +2612,8 @@ static int pma_get_opa_datacounters(struct opa_pma_mad *pmp,
cpu_to_be64(read_dev_cntr(dd, C_DC_RCV_BCN, CNTR_INVALID_VL));
rsp->port_error_counter_summary =
- cpu_to_be64(get_error_counter_summary(ibdev, port));
+ cpu_to_be64(get_error_counter_summary(ibdev, port,
+ res_lli, res_ler));
vlinfo = &(rsp->vls[0]);
vfi = 0;
@@ -2675,9 +2633,6 @@ static int pma_get_opa_datacounters(struct opa_pma_mad *pmp,
rsp->vls[vfi].port_vl_rcv_data =
cpu_to_be64(read_dev_cntr(dd, C_DC_RX_FLIT_VL,
idx_from_vl(vl)));
- rsp->vls[vfi].port_vl_rcv_bubble =
- cpu_to_be64(read_dev_cntr(dd, C_DC_RCV_BBL_VL,
- idx_from_vl(vl)));
rsp->vls[vfi].port_vl_xmit_pkts =
cpu_to_be64(read_port_cntr(ppd, C_TX_PKT_VL,
@@ -3458,7 +3413,7 @@ static int __subn_get_opa_led_info(struct opa_smp *smp, u32 am, u8 *data,
u32 nport = OPA_AM_NPORT(am);
u64 reg;
- if (nport != 1 || OPA_AM_PORTNUM(am)) {
+ if (nport != 1) {
smp->status |= IB_SMP_INVALID_FIELD;
return reply((struct ib_mad_hdr *)smp);
}
@@ -3483,7 +3438,7 @@ static int __subn_set_opa_led_info(struct opa_smp *smp, u32 am, u8 *data,
u32 nport = OPA_AM_NPORT(am);
int on = !!(be32_to_cpu(p->rsvd_led_mask) & OPA_LED_MASK);
- if (nport != 1 || OPA_AM_PORTNUM(am)) {
+ if (nport != 1) {
smp->status |= IB_SMP_INVALID_FIELD;
return reply((struct ib_mad_hdr *)smp);
}
diff --git a/drivers/staging/rdma/hfi1/mad.h b/drivers/staging/rdma/hfi1/mad.h
index 47457501c044..f0317750e2fc 100644
--- a/drivers/staging/rdma/hfi1/mad.h
+++ b/drivers/staging/rdma/hfi1/mad.h
@@ -60,7 +60,121 @@
#endif
#include "opa_compat.h"
+/*
+ * OPA Traps
+ */
+#define OPA_TRAP_GID_NOW_IN_SERVICE cpu_to_be16(64)
+#define OPA_TRAP_GID_OUT_OF_SERVICE cpu_to_be16(65)
+#define OPA_TRAP_ADD_MULTICAST_GROUP cpu_to_be16(66)
+#define OPA_TRAL_DEL_MULTICAST_GROUP cpu_to_be16(67)
+#define OPA_TRAP_UNPATH cpu_to_be16(68)
+#define OPA_TRAP_REPATH cpu_to_be16(69)
+#define OPA_TRAP_PORT_CHANGE_STATE cpu_to_be16(128)
+#define OPA_TRAP_LINK_INTEGRITY cpu_to_be16(129)
+#define OPA_TRAP_EXCESSIVE_BUFFER_OVERRUN cpu_to_be16(130)
+#define OPA_TRAP_FLOW_WATCHDOG cpu_to_be16(131)
+#define OPA_TRAP_CHANGE_CAPABILITY cpu_to_be16(144)
+#define OPA_TRAP_CHANGE_SYSGUID cpu_to_be16(145)
+#define OPA_TRAP_BAD_M_KEY cpu_to_be16(256)
+#define OPA_TRAP_BAD_P_KEY cpu_to_be16(257)
+#define OPA_TRAP_BAD_Q_KEY cpu_to_be16(258)
+#define OPA_TRAP_SWITCH_BAD_PKEY cpu_to_be16(259)
+#define OPA_SMA_TRAP_DATA_LINK_WIDTH cpu_to_be16(2048)
+/*
+ * Generic trap/notice other local changes flags (trap 144).
+ */
+#define OPA_NOTICE_TRAP_LWDE_CHG 0x08 /* Link Width Downgrade Enable
+ * changed
+ */
+#define OPA_NOTICE_TRAP_LSE_CHG 0x04 /* Link Speed Enable changed */
+#define OPA_NOTICE_TRAP_LWE_CHG 0x02 /* Link Width Enable changed */
+#define OPA_NOTICE_TRAP_NODE_DESC_CHG 0x01
+
+struct opa_mad_notice_attr {
+ u8 generic_type;
+ u8 prod_type_msb;
+ __be16 prod_type_lsb;
+ __be16 trap_num;
+ __be16 toggle_count;
+ __be32 issuer_lid;
+ __be32 reserved1;
+ union ib_gid issuer_gid;
+
+ union {
+ struct {
+ u8 details[64];
+ } raw_data;
+
+ struct {
+ union ib_gid gid;
+ } __packed ntc_64_65_66_67;
+
+ struct {
+ __be32 lid;
+ } __packed ntc_128;
+
+ struct {
+ __be32 lid; /* where violation happened */
+ u8 port_num; /* where violation happened */
+ } __packed ntc_129_130_131;
+
+ struct {
+ __be32 lid; /* LID where change occurred */
+ __be32 new_cap_mask; /* new capability mask */
+ __be16 reserved2;
+ __be16 cap_mask;
+ __be16 change_flags; /* low 4 bits only */
+ } __packed ntc_144;
+
+ struct {
+ __be64 new_sys_guid;
+ __be32 lid; /* lid where sys guid changed */
+ } __packed ntc_145;
+
+ struct {
+ __be32 lid;
+ __be32 dr_slid;
+ u8 method;
+ u8 dr_trunc_hop;
+ __be16 attr_id;
+ __be32 attr_mod;
+ __be64 mkey;
+ u8 dr_rtn_path[30];
+ } __packed ntc_256;
+
+ struct {
+ __be32 lid1;
+ __be32 lid2;
+ __be32 key;
+ u8 sl; /* SL: high 5 bits */
+ u8 reserved3[3];
+ union ib_gid gid1;
+ union ib_gid gid2;
+ __be32 qp1; /* high 8 bits reserved */
+ __be32 qp2; /* high 8 bits reserved */
+ } __packed ntc_257_258;
+
+ struct {
+ __be16 flags; /* low 8 bits reserved */
+ __be16 pkey;
+ __be32 lid1;
+ __be32 lid2;
+ u8 sl; /* SL: high 5 bits */
+ u8 reserved4[3];
+ union ib_gid gid1;
+ union ib_gid gid2;
+ __be32 qp1; /* high 8 bits reserved */
+ __be32 qp2; /* high 8 bits reserved */
+ } __packed ntc_259;
+
+ struct {
+ __be32 lid;
+ } __packed ntc_2048;
+
+ };
+ u8 class_data[0];
+};
#define IB_VLARB_LOWPRI_0_31 1
#define IB_VLARB_LOWPRI_32_63 2
diff --git a/drivers/staging/rdma/hfi1/pcie.c b/drivers/staging/rdma/hfi1/pcie.c
index a956044459a2..8317b07d722a 100644
--- a/drivers/staging/rdma/hfi1/pcie.c
+++ b/drivers/staging/rdma/hfi1/pcie.c
@@ -426,14 +426,6 @@ void request_msix(struct hfi1_devdata *dd, u32 *nent,
tune_pcie_caps(dd);
}
-/*
- * Disable MSI-X.
- */
-void hfi1_nomsix(struct hfi1_devdata *dd)
-{
- pci_disable_msix(dd->pcidev);
-}
-
void hfi1_enable_intx(struct pci_dev *pdev)
{
/* first, turn on INTx */
@@ -475,8 +467,18 @@ static void tune_pcie_caps(struct hfi1_devdata *dd)
{
struct pci_dev *parent;
u16 rc_mpss, rc_mps, ep_mpss, ep_mps;
- u16 rc_mrrs, ep_mrrs, max_mrrs;
+ u16 rc_mrrs, ep_mrrs, max_mrrs, ectl;
+ /*
+ * Turn on extended tags in DevCtl in case the BIOS has turned it off
+ * to improve WFR SDMA bandwidth
+ */
+ pcie_capability_read_word(dd->pcidev, PCI_EXP_DEVCTL, &ectl);
+ if (!(ectl & PCI_EXP_DEVCTL_EXT_TAG)) {
+ dd_dev_info(dd, "Enabling PCIe extended tags\n");
+ ectl |= PCI_EXP_DEVCTL_EXT_TAG;
+ pcie_capability_write_word(dd->pcidev, PCI_EXP_DEVCTL, ectl);
+ }
/* Find out supported and configured values for parent (root) */
parent = dd->pcidev->bus->self;
if (!pci_is_root_bus(parent->bus)) {
@@ -915,7 +917,7 @@ int do_pcie_gen3_transition(struct hfi1_devdata *dd)
/*
* A0 needs an additional SBR
*/
- if (is_a0(dd))
+ if (is_ax(dd))
nsbr++;
/*
@@ -947,17 +949,7 @@ int do_pcie_gen3_transition(struct hfi1_devdata *dd)
}
retry:
-
- if (therm) {
- /*
- * toggle SPICO_ENABLE to get back to the state
- * just after the firmware load
- */
- sbus_request(dd, SBUS_MASTER_BROADCAST, 0x01,
- WRITE_SBUS_RECEIVER, 0x00000040);
- sbus_request(dd, SBUS_MASTER_BROADCAST, 0x01,
- WRITE_SBUS_RECEIVER, 0x00000140);
- }
+ /* the SBus download will reset the spico for thermal */
/* step 3: download SBus Master firmware */
/* step 4: download PCIe Gen3 SerDes firmware */
@@ -1201,7 +1193,7 @@ retry:
write_csr(dd, CCE_DC_CTRL, 0);
/* Set the LED off */
- if (is_a0(dd))
+ if (is_ax(dd))
setextled(dd, 0);
/* check for any per-lane errors */
diff --git a/drivers/staging/rdma/hfi1/pio.c b/drivers/staging/rdma/hfi1/pio.c
index e5c32db4bc67..b51a4416312b 100644
--- a/drivers/staging/rdma/hfi1/pio.c
+++ b/drivers/staging/rdma/hfi1/pio.c
@@ -660,6 +660,24 @@ void set_pio_integrity(struct send_context *sc)
write_kctxt_csr(dd, hw_context, SC(CHECK_ENABLE), reg);
}
+static u32 get_buffers_allocated(struct send_context *sc)
+{
+ int cpu;
+ u32 ret = 0;
+
+ for_each_possible_cpu(cpu)
+ ret += *per_cpu_ptr(sc->buffers_allocated, cpu);
+ return ret;
+}
+
+static void reset_buffers_allocated(struct send_context *sc)
+{
+ int cpu;
+
+ for_each_possible_cpu(cpu)
+ (*per_cpu_ptr(sc->buffers_allocated, cpu)) = 0;
+}
+
/*
* Allocate a NUMA relative send context structure of the given type along
* with a HW context.
@@ -668,7 +686,7 @@ struct send_context *sc_alloc(struct hfi1_devdata *dd, int type,
uint hdrqentsize, int numa)
{
struct send_context_info *sci;
- struct send_context *sc;
+ struct send_context *sc = NULL;
dma_addr_t pa;
unsigned long flags;
u64 reg;
@@ -686,10 +704,20 @@ struct send_context *sc_alloc(struct hfi1_devdata *dd, int type,
if (!sc)
return NULL;
+ sc->buffers_allocated = alloc_percpu(u32);
+ if (!sc->buffers_allocated) {
+ kfree(sc);
+ dd_dev_err(dd,
+ "Cannot allocate buffers_allocated per cpu counters\n"
+ );
+ return NULL;
+ }
+
spin_lock_irqsave(&dd->sc_lock, flags);
ret = sc_hw_alloc(dd, type, &sw_index, &hw_context);
if (ret) {
spin_unlock_irqrestore(&dd->sc_lock, flags);
+ free_percpu(sc->buffers_allocated);
kfree(sc);
return NULL;
}
@@ -705,7 +733,6 @@ struct send_context *sc_alloc(struct hfi1_devdata *dd, int type,
spin_lock_init(&sc->credit_ctrl_lock);
INIT_LIST_HEAD(&sc->piowait);
INIT_WORK(&sc->halt_work, sc_halted);
- atomic_set(&sc->buffers_allocated, 0);
init_waitqueue_head(&sc->halt_wait);
/* grouping is always single context for now */
@@ -815,15 +842,16 @@ struct send_context *sc_alloc(struct hfi1_devdata *dd, int type,
}
}
- dd_dev_info(dd,
- "Send context %u(%u) %s group %u credits %u credit_ctrl 0x%llx threshold %u\n",
- sw_index,
- hw_context,
- sc_type_name(type),
- sc->group,
- sc->credits,
- sc->credit_ctrl,
- thresh);
+ hfi1_cdbg(PIO,
+ "Send context %u(%u) %s group %u credits %u credit_ctrl 0x%llx threshold %u\n",
+ sw_index,
+ hw_context,
+ sc_type_name(type),
+ sc->group,
+ sc->credits,
+ sc->credit_ctrl,
+ thresh);
+
return sc;
}
@@ -865,6 +893,7 @@ void sc_free(struct send_context *sc)
spin_unlock_irqrestore(&dd->sc_lock, flags);
kfree(sc->sr);
+ free_percpu(sc->buffers_allocated);
kfree(sc);
}
@@ -1028,7 +1057,7 @@ int sc_restart(struct send_context *sc)
/* kernel context */
loop = 0;
while (1) {
- count = atomic_read(&sc->buffers_allocated);
+ count = get_buffers_allocated(sc);
if (count == 0)
break;
if (loop > 100) {
@@ -1196,7 +1225,8 @@ int sc_enable(struct send_context *sc)
sc->sr_head = 0;
sc->sr_tail = 0;
sc->flags = 0;
- atomic_set(&sc->buffers_allocated, 0);
+ /* the alloc lock insures no fast path allocation */
+ reset_buffers_allocated(sc);
/*
* Clear all per-context errors. Some of these will be set when
@@ -1372,7 +1402,8 @@ retry:
/* there is enough room */
- atomic_inc(&sc->buffers_allocated);
+ preempt_disable();
+ this_cpu_inc(*sc->buffers_allocated);
/* read this once */
head = sc->sr_head;
@@ -1564,6 +1595,7 @@ void sc_release_update(struct send_context *sc)
u64 hw_free;
u32 head, tail;
unsigned long old_free;
+ unsigned long free;
unsigned long extra;
unsigned long flags;
int code;
@@ -1578,7 +1610,7 @@ void sc_release_update(struct send_context *sc)
extra = (((hw_free & CR_COUNTER_SMASK) >> CR_COUNTER_SHIFT)
- (old_free & CR_COUNTER_MASK))
& CR_COUNTER_MASK;
- sc->free = old_free + extra;
+ free = old_free + extra;
trace_hfi1_piofree(sc, extra);
/* call sent buffer callbacks */
@@ -1588,7 +1620,7 @@ void sc_release_update(struct send_context *sc)
while (head != tail) {
pbuf = &sc->sr[tail].pbuf;
- if (sent_before(sc->free, pbuf->sent_at)) {
+ if (sent_before(free, pbuf->sent_at)) {
/* not sent yet */
break;
}
@@ -1602,8 +1634,10 @@ void sc_release_update(struct send_context *sc)
if (tail >= sc->sr_size)
tail = 0;
}
- /* update tail, in case we moved it */
sc->sr_tail = tail;
+ /* make sure tail is updated before free */
+ smp_wmb();
+ sc->free = free;
spin_unlock_irqrestore(&sc->release_lock, flags);
sc_piobufavail(sc);
}
diff --git a/drivers/staging/rdma/hfi1/pio.h b/drivers/staging/rdma/hfi1/pio.h
index 0bb885ca3cfb..53d3e0a79375 100644
--- a/drivers/staging/rdma/hfi1/pio.h
+++ b/drivers/staging/rdma/hfi1/pio.h
@@ -130,7 +130,7 @@ struct send_context {
spinlock_t credit_ctrl_lock ____cacheline_aligned_in_smp;
u64 credit_ctrl; /* cache for credit control */
u32 credit_intr_count; /* count of credit intr users */
- atomic_t buffers_allocated; /* count of buffers allocated */
+ u32 __percpu *buffers_allocated;/* count of buffers allocated */
wait_queue_head_t halt_wait; /* wait until kernel sees interrupt */
};
diff --git a/drivers/staging/rdma/hfi1/pio_copy.c b/drivers/staging/rdma/hfi1/pio_copy.c
index 8972bbc02038..ebb0bafc68cb 100644
--- a/drivers/staging/rdma/hfi1/pio_copy.c
+++ b/drivers/staging/rdma/hfi1/pio_copy.c
@@ -160,7 +160,8 @@ void pio_copy(struct hfi1_devdata *dd, struct pio_buf *pbuf, u64 pbc,
}
/* finished with this buffer */
- atomic_dec(&pbuf->sc->buffers_allocated);
+ this_cpu_dec(*pbuf->sc->buffers_allocated);
+ preempt_enable();
}
/* USE_SHIFTS is faster in user-space tests on a Xeon X5570 @ 2.93GHz */
@@ -854,5 +855,6 @@ void seg_pio_copy_end(struct pio_buf *pbuf)
}
/* finished with this buffer */
- atomic_dec(&pbuf->sc->buffers_allocated);
+ this_cpu_dec(*pbuf->sc->buffers_allocated);
+ preempt_enable();
}
diff --git a/drivers/staging/rdma/hfi1/qp.c b/drivers/staging/rdma/hfi1/qp.c
index f8c36166962f..ce036810d576 100644
--- a/drivers/staging/rdma/hfi1/qp.c
+++ b/drivers/staging/rdma/hfi1/qp.c
@@ -378,6 +378,7 @@ static void reset_qp(struct hfi1_qp *qp, enum ib_qp_type type)
}
qp->s_ack_state = IB_OPCODE_RC_ACKNOWLEDGE;
qp->r_nak_state = 0;
+ qp->r_adefered = 0;
qp->r_aflags = 0;
qp->r_flags = 0;
qp->s_head = 0;
@@ -617,7 +618,7 @@ int hfi1_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
int mig = 0;
int ret;
u32 pmtu = 0; /* for gcc warning only */
- struct hfi1_devdata *dd;
+ struct hfi1_devdata *dd = dd_from_dev(dev);
spin_lock_irq(&qp->r_lock);
spin_lock(&qp->s_lock);
@@ -631,23 +632,35 @@ int hfi1_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
goto inval;
if (attr_mask & IB_QP_AV) {
+ u8 sc;
+
if (attr->ah_attr.dlid >= HFI1_MULTICAST_LID_BASE)
goto inval;
if (hfi1_check_ah(qp->ibqp.device, &attr->ah_attr))
goto inval;
+ sc = ah_to_sc(ibqp->device, &attr->ah_attr);
+ if (!qp_to_sdma_engine(qp, sc) &&
+ dd->flags & HFI1_HAS_SEND_DMA)
+ goto inval;
}
if (attr_mask & IB_QP_ALT_PATH) {
+ u8 sc;
+
if (attr->alt_ah_attr.dlid >= HFI1_MULTICAST_LID_BASE)
goto inval;
if (hfi1_check_ah(qp->ibqp.device, &attr->alt_ah_attr))
goto inval;
- if (attr->alt_pkey_index >= hfi1_get_npkeys(dd_from_dev(dev)))
+ if (attr->alt_pkey_index >= hfi1_get_npkeys(dd))
+ goto inval;
+ sc = ah_to_sc(ibqp->device, &attr->alt_ah_attr);
+ if (!qp_to_sdma_engine(qp, sc) &&
+ dd->flags & HFI1_HAS_SEND_DMA)
goto inval;
}
if (attr_mask & IB_QP_PKEY_INDEX)
- if (attr->pkey_index >= hfi1_get_npkeys(dd_from_dev(dev)))
+ if (attr->pkey_index >= hfi1_get_npkeys(dd))
goto inval;
if (attr_mask & IB_QP_MIN_RNR_TIMER)
@@ -792,6 +805,8 @@ int hfi1_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
qp->remote_ah_attr = attr->ah_attr;
qp->s_srate = attr->ah_attr.static_rate;
qp->srate_mbps = ib_rate_to_mbps(qp->s_srate);
+ qp->s_sc = ah_to_sc(ibqp->device, &qp->remote_ah_attr);
+ qp->s_sde = qp_to_sdma_engine(qp, qp->s_sc);
}
if (attr_mask & IB_QP_ALT_PATH) {
@@ -806,6 +821,8 @@ int hfi1_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
qp->port_num = qp->alt_ah_attr.port_num;
qp->s_pkey_index = qp->s_alt_pkey_index;
qp->s_flags |= HFI1_S_AHG_CLEAR;
+ qp->s_sc = ah_to_sc(ibqp->device, &qp->remote_ah_attr);
+ qp->s_sde = qp_to_sdma_engine(qp, qp->s_sc);
}
}
@@ -1528,9 +1545,6 @@ struct sdma_engine *qp_to_sdma_engine(struct hfi1_qp *qp, u8 sc5)
if (!(dd->flags & HFI1_HAS_SEND_DMA))
return NULL;
switch (qp->ibqp.qp_type) {
- case IB_QPT_UC:
- case IB_QPT_RC:
- break;
case IB_QPT_SMI:
return NULL;
default:
@@ -1685,3 +1699,25 @@ void qp_comm_est(struct hfi1_qp *qp)
qp->ibqp.event_handler(&ev, qp->ibqp.qp_context);
}
}
+
+/*
+ * Switch to alternate path.
+ * The QP s_lock should be held and interrupts disabled.
+ */
+void hfi1_migrate_qp(struct hfi1_qp *qp)
+{
+ struct ib_event ev;
+
+ qp->s_mig_state = IB_MIG_MIGRATED;
+ qp->remote_ah_attr = qp->alt_ah_attr;
+ qp->port_num = qp->alt_ah_attr.port_num;
+ qp->s_pkey_index = qp->s_alt_pkey_index;
+ qp->s_flags |= HFI1_S_AHG_CLEAR;
+ qp->s_sc = ah_to_sc(qp->ibqp.device, &qp->remote_ah_attr);
+ qp->s_sde = qp_to_sdma_engine(qp, qp->s_sc);
+
+ ev.device = qp->ibqp.device;
+ ev.element.qp = &qp->ibqp;
+ ev.event = IB_EVENT_PATH_MIG;
+ qp->ibqp.event_handler(&ev, qp->ibqp.qp_context);
+}
diff --git a/drivers/staging/rdma/hfi1/qp.h b/drivers/staging/rdma/hfi1/qp.h
index b9c1575990aa..62a94c5d7dca 100644
--- a/drivers/staging/rdma/hfi1/qp.h
+++ b/drivers/staging/rdma/hfi1/qp.h
@@ -128,7 +128,6 @@ static inline void clear_ahg(struct hfi1_qp *qp)
if (qp->s_sde && qp->s_ahgidx >= 0)
sdma_ahg_free(qp->s_sde, qp->s_ahgidx);
qp->s_ahgidx = -1;
- qp->s_sde = NULL;
}
/**
@@ -212,7 +211,7 @@ int hfi1_qp_init(struct hfi1_ibdev *dev);
void hfi1_qp_exit(struct hfi1_ibdev *dev);
/**
- * hfi1_qp_waitup - wake up on the indicated event
+ * hfi1_qp_wakeup - wake up on the indicated event
* @qp: the QP
* @flag: flag the qp on which the qp is stalled
*/
@@ -223,19 +222,19 @@ struct sdma_engine *qp_to_sdma_engine(struct hfi1_qp *qp, u8 sc5);
struct qp_iter;
/**
- * qp_iter_init - wake up on the indicated event
+ * qp_iter_init - initialize the iterator for the qp hash list
* @dev: the hfi1_ibdev
*/
struct qp_iter *qp_iter_init(struct hfi1_ibdev *dev);
/**
- * qp_iter_next - wakeup on the indicated event
+ * qp_iter_next - Find the next qp in the hash list
* @iter: the iterator for the qp hash list
*/
int qp_iter_next(struct qp_iter *iter);
/**
- * qp_iter_next - wake up on the indicated event
+ * qp_iter_print - print the qp information to seq_file
* @s: the seq_file to emit the qp information on
* @iter: the iterator for the qp hash list
*/
@@ -247,4 +246,41 @@ void qp_iter_print(struct seq_file *s, struct qp_iter *iter);
*/
void qp_comm_est(struct hfi1_qp *qp);
+/**
+ * _hfi1_schedule_send - schedule progress
+ * @qp: the QP
+ *
+ * This schedules qp progress w/o regard to the s_flags.
+ *
+ * It is only used in the post send, which doesn't hold
+ * the s_lock.
+ */
+static inline void _hfi1_schedule_send(struct hfi1_qp *qp)
+{
+ struct hfi1_ibport *ibp =
+ to_iport(qp->ibqp.device, qp->port_num);
+ struct hfi1_pportdata *ppd = ppd_from_ibp(ibp);
+ struct hfi1_devdata *dd = dd_from_ibdev(qp->ibqp.device);
+
+ iowait_schedule(&qp->s_iowait, ppd->hfi1_wq,
+ qp->s_sde ?
+ qp->s_sde->cpu :
+ cpumask_first(cpumask_of_node(dd->assigned_node_id)));
+}
+
+/**
+ * hfi1_schedule_send - schedule progress
+ * @qp: the QP
+ *
+ * This schedules qp progress and caller should hold
+ * the s_lock.
+ */
+static inline void hfi1_schedule_send(struct hfi1_qp *qp)
+{
+ if (hfi1_send_ok(qp))
+ _hfi1_schedule_send(qp);
+}
+
+void hfi1_migrate_qp(struct hfi1_qp *qp);
+
#endif /* _QP_H */
diff --git a/drivers/staging/rdma/hfi1/qsfp.c b/drivers/staging/rdma/hfi1/qsfp.c
index ffdb1d787a80..6326a915d7fd 100644
--- a/drivers/staging/rdma/hfi1/qsfp.c
+++ b/drivers/staging/rdma/hfi1/qsfp.c
@@ -475,7 +475,7 @@ int qsfp_dump(struct hfi1_pportdata *ppd, char *buf, int len)
u8 *cache = &ppd->qsfp_info.cache[0];
u8 bin_buff[QSFP_DUMP_CHUNK];
char lenstr[6];
- int sofar, ret;
+ int sofar;
int bidx = 0;
u8 *atten = &cache[QSFP_ATTEN_OFFS];
u8 *vendor_oui = &cache[QSFP_VOUI_OFFS];
@@ -536,6 +536,5 @@ int qsfp_dump(struct hfi1_pportdata *ppd, char *buf, int len)
bidx += QSFP_DUMP_CHUNK;
}
}
- ret = sofar;
- return ret;
+ return sofar;
}
diff --git a/drivers/staging/rdma/hfi1/rc.c b/drivers/staging/rdma/hfi1/rc.c
index 5fc93bb312f1..6f4a155f7931 100644
--- a/drivers/staging/rdma/hfi1/rc.c
+++ b/drivers/staging/rdma/hfi1/rc.c
@@ -1608,6 +1608,27 @@ bail:
return;
}
+static inline void rc_defered_ack(struct hfi1_ctxtdata *rcd,
+ struct hfi1_qp *qp)
+{
+ if (list_empty(&qp->rspwait)) {
+ qp->r_flags |= HFI1_R_RSP_DEFERED_ACK;
+ atomic_inc(&qp->refcount);
+ list_add_tail(&qp->rspwait, &rcd->qp_wait_list);
+ }
+}
+
+static inline void rc_cancel_ack(struct hfi1_qp *qp)
+{
+ qp->r_adefered = 0;
+ if (list_empty(&qp->rspwait))
+ return;
+ list_del_init(&qp->rspwait);
+ qp->r_flags &= ~HFI1_R_RSP_DEFERED_ACK;
+ if (atomic_dec_and_test(&qp->refcount))
+ wake_up(&qp->wait);
+}
+
/**
* rc_rcv_error - process an incoming duplicate or error RC packet
* @ohdr: the other headers for this packet
@@ -1650,11 +1671,7 @@ static noinline int rc_rcv_error(struct hfi1_other_headers *ohdr, void *data,
* in the receive queue have been processed.
* Otherwise, we end up propagating congestion.
*/
- if (list_empty(&qp->rspwait)) {
- qp->r_flags |= HFI1_R_RSP_NAK;
- atomic_inc(&qp->refcount);
- list_add_tail(&qp->rspwait, &rcd->qp_wait_list);
- }
+ rc_defered_ack(rcd, qp);
}
goto done;
}
@@ -1978,7 +1995,7 @@ void hfi1_rc_rcv(struct hfi1_packet *packet)
}
psn = be32_to_cpu(ohdr->bth[2]);
- opcode = bth0 >> 24;
+ opcode = (bth0 >> 24) & 0xff;
/*
* Process responses (ACKs) before anything else. Note that the
@@ -2329,19 +2346,29 @@ send_last:
qp->r_ack_psn = psn;
qp->r_nak_state = 0;
/* Send an ACK if requested or required. */
- if (psn & (1 << 31))
- goto send_ack;
+ if (psn & IB_BTH_REQ_ACK) {
+ if (packet->numpkt == 0) {
+ rc_cancel_ack(qp);
+ goto send_ack;
+ }
+ if (qp->r_adefered >= HFI1_PSN_CREDIT) {
+ rc_cancel_ack(qp);
+ goto send_ack;
+ }
+ if (unlikely(is_fecn)) {
+ rc_cancel_ack(qp);
+ goto send_ack;
+ }
+ qp->r_adefered++;
+ rc_defered_ack(rcd, qp);
+ }
return;
rnr_nak:
qp->r_nak_state = IB_RNR_NAK | qp->r_min_rnr_timer;
qp->r_ack_psn = qp->r_psn;
/* Queue RNR NAK for later */
- if (list_empty(&qp->rspwait)) {
- qp->r_flags |= HFI1_R_RSP_NAK;
- atomic_inc(&qp->refcount);
- list_add_tail(&qp->rspwait, &rcd->qp_wait_list);
- }
+ rc_defered_ack(rcd, qp);
return;
nack_op_err:
@@ -2349,11 +2376,7 @@ nack_op_err:
qp->r_nak_state = IB_NAK_REMOTE_OPERATIONAL_ERROR;
qp->r_ack_psn = qp->r_psn;
/* Queue NAK for later */
- if (list_empty(&qp->rspwait)) {
- qp->r_flags |= HFI1_R_RSP_NAK;
- atomic_inc(&qp->refcount);
- list_add_tail(&qp->rspwait, &rcd->qp_wait_list);
- }
+ rc_defered_ack(rcd, qp);
return;
nack_inv_unlck:
@@ -2363,11 +2386,7 @@ nack_inv:
qp->r_nak_state = IB_NAK_INVALID_REQUEST;
qp->r_ack_psn = qp->r_psn;
/* Queue NAK for later */
- if (list_empty(&qp->rspwait)) {
- qp->r_flags |= HFI1_R_RSP_NAK;
- atomic_inc(&qp->refcount);
- list_add_tail(&qp->rspwait, &rcd->qp_wait_list);
- }
+ rc_defered_ack(rcd, qp);
return;
nack_acc_unlck:
@@ -2391,19 +2410,19 @@ void hfi1_rc_hdrerr(
struct hfi1_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num);
int diff;
u32 opcode;
- u32 psn;
+ u32 psn, bth0;
/* Check for GRH */
ohdr = &hdr->u.oth;
if (has_grh)
ohdr = &hdr->u.l.oth;
- opcode = be32_to_cpu(ohdr->bth[0]);
- if (hfi1_ruc_check_hdr(ibp, hdr, has_grh, qp, opcode))
+ bth0 = be32_to_cpu(ohdr->bth[0]);
+ if (hfi1_ruc_check_hdr(ibp, hdr, has_grh, qp, bth0))
return;
psn = be32_to_cpu(ohdr->bth[2]);
- opcode >>= 24;
+ opcode = (bth0 >> 24) & 0xff;
/* Only deal with RDMA Writes for now */
if (opcode < IB_OPCODE_RC_RDMA_READ_RESPONSE_FIRST) {
@@ -2421,13 +2440,7 @@ void hfi1_rc_hdrerr(
* Otherwise, we end up
* propagating congestion.
*/
- if (list_empty(&qp->rspwait)) {
- qp->r_flags |= HFI1_R_RSP_NAK;
- atomic_inc(&qp->refcount);
- list_add_tail(
- &qp->rspwait,
- &rcd->qp_wait_list);
- }
+ rc_defered_ack(rcd, qp);
} /* Out of sequence NAK */
} /* QP Request NAKs */
}
diff --git a/drivers/staging/rdma/hfi1/ruc.c b/drivers/staging/rdma/hfi1/ruc.c
index 49bc9fd7a51a..4a91975b68d7 100644
--- a/drivers/staging/rdma/hfi1/ruc.c
+++ b/drivers/staging/rdma/hfi1/ruc.c
@@ -241,26 +241,6 @@ bail:
return ret;
}
-/*
- * Switch to alternate path.
- * The QP s_lock should be held and interrupts disabled.
- */
-void hfi1_migrate_qp(struct hfi1_qp *qp)
-{
- struct ib_event ev;
-
- qp->s_mig_state = IB_MIG_MIGRATED;
- qp->remote_ah_attr = qp->alt_ah_attr;
- qp->port_num = qp->alt_ah_attr.port_num;
- qp->s_pkey_index = qp->s_alt_pkey_index;
- qp->s_flags |= HFI1_S_AHG_CLEAR;
-
- ev.device = qp->ibqp.device;
- ev.element.qp = &qp->ibqp;
- ev.event = IB_EVENT_PATH_MIG;
- qp->ibqp.event_handler(&ev, qp->ibqp.qp_context);
-}
-
static __be64 get_sguid(struct hfi1_ibport *ibp, unsigned index)
{
if (!index) {
@@ -308,11 +288,12 @@ int hfi1_ruc_check_hdr(struct hfi1_ibport *ibp, struct hfi1_ib_header *hdr,
}
if (unlikely(rcv_pkey_check(ppd_from_ibp(ibp), (u16)bth0,
sc5, be16_to_cpu(hdr->lrh[3])))) {
- hfi1_bad_pqkey(ibp, IB_NOTICE_TRAP_BAD_PKEY,
+ hfi1_bad_pqkey(ibp, OPA_TRAP_BAD_P_KEY,
(u16)bth0,
(be16_to_cpu(hdr->lrh[0]) >> 4) & 0xF,
0, qp->ibqp.qp_num,
- hdr->lrh[3], hdr->lrh[1]);
+ be16_to_cpu(hdr->lrh[3]),
+ be16_to_cpu(hdr->lrh[1]));
goto err;
}
/* Validate the SLID. See Ch. 9.6.1.5 and 17.2.8 */
@@ -340,11 +321,12 @@ int hfi1_ruc_check_hdr(struct hfi1_ibport *ibp, struct hfi1_ib_header *hdr,
}
if (unlikely(rcv_pkey_check(ppd_from_ibp(ibp), (u16)bth0,
sc5, be16_to_cpu(hdr->lrh[3])))) {
- hfi1_bad_pqkey(ibp, IB_NOTICE_TRAP_BAD_PKEY,
+ hfi1_bad_pqkey(ibp, OPA_TRAP_BAD_P_KEY,
(u16)bth0,
(be16_to_cpu(hdr->lrh[0]) >> 4) & 0xF,
0, qp->ibqp.qp_num,
- hdr->lrh[3], hdr->lrh[1]);
+ be16_to_cpu(hdr->lrh[3]),
+ be16_to_cpu(hdr->lrh[1]));
goto err;
}
/* Validate the SLID. See Ch. 9.6.1.5 */
@@ -714,11 +696,8 @@ static inline void build_ahg(struct hfi1_qp *qp, u32 npsn)
clear_ahg(qp);
if (!(qp->s_flags & HFI1_S_AHG_VALID)) {
/* first middle that needs copy */
- if (qp->s_ahgidx < 0) {
- if (!qp->s_sde)
- qp->s_sde = qp_to_sdma_engine(qp, qp->s_sc);
+ if (qp->s_ahgidx < 0)
qp->s_ahgidx = sdma_ahg_alloc(qp->s_sde);
- }
if (qp->s_ahgidx >= 0) {
qp->s_ahgpsn = npsn;
qp->s_hdr->tx_flags |= SDMA_TXREQ_F_AHG_COPY;
@@ -761,7 +740,6 @@ void hfi1_make_ruc_header(struct hfi1_qp *qp, struct hfi1_other_headers *ohdr,
u16 lrh0;
u32 nwords;
u32 extra_bytes;
- u8 sc5;
u32 bth1;
/* Construct the header. */
@@ -775,9 +753,7 @@ void hfi1_make_ruc_header(struct hfi1_qp *qp, struct hfi1_other_headers *ohdr,
lrh0 = HFI1_LRH_GRH;
middle = 0;
}
- sc5 = ibp->sl_to_sc[qp->remote_ah_attr.sl];
- lrh0 |= (sc5 & 0xf) << 12 | (qp->remote_ah_attr.sl & 0xf) << 4;
- qp->s_sc = sc5;
+ lrh0 |= (qp->s_sc & 0xf) << 12 | (qp->remote_ah_attr.sl & 0xf) << 4;
/*
* reset s_hdr/AHG fields
*
@@ -835,16 +811,20 @@ void hfi1_do_send(struct work_struct *work)
{
struct iowait *wait = container_of(work, struct iowait, iowork);
struct hfi1_qp *qp = container_of(wait, struct hfi1_qp, s_iowait);
- struct hfi1_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num);
- struct hfi1_pportdata *ppd = ppd_from_ibp(ibp);
+ struct hfi1_pkt_state ps;
int (*make_req)(struct hfi1_qp *qp);
unsigned long flags;
unsigned long timeout;
+ ps.dev = to_idev(qp->ibqp.device);
+ ps.ibp = to_iport(qp->ibqp.device, qp->port_num);
+ ps.ppd = ppd_from_ibp(ps.ibp);
+
if ((qp->ibqp.qp_type == IB_QPT_RC ||
qp->ibqp.qp_type == IB_QPT_UC) &&
!loopback &&
- (qp->remote_ah_attr.dlid & ~((1 << ppd->lmc) - 1)) == ppd->lid) {
+ (qp->remote_ah_attr.dlid & ~((1 << ps.ppd->lmc) - 1)) ==
+ ps.ppd->lid) {
ruc_loopback(qp);
return;
}
@@ -876,8 +856,7 @@ void hfi1_do_send(struct work_struct *work)
* If the packet cannot be sent now, return and
* the send tasklet will be woken up later.
*/
- if (hfi1_verbs_send(qp, qp->s_hdr, qp->s_hdrwords,
- qp->s_cur_sge, qp->s_cur_size))
+ if (hfi1_verbs_send(qp, &ps))
break;
/* Record that s_hdr is empty. */
qp->s_hdrwords = 0;
@@ -886,7 +865,7 @@ void hfi1_do_send(struct work_struct *work)
/* allow other tasks to run */
if (unlikely(time_after(jiffies, timeout))) {
cond_resched();
- ppd->dd->verbs_dev.n_send_schedule++;
+ ps.ppd->dd->verbs_dev.n_send_schedule++;
timeout = jiffies + SEND_RESCHED_TIMEOUT;
}
} while (make_req(qp));
diff --git a/drivers/staging/rdma/hfi1/sdma.c b/drivers/staging/rdma/hfi1/sdma.c
index 2a1da2189900..9a15f1f32b45 100644
--- a/drivers/staging/rdma/hfi1/sdma.c
+++ b/drivers/staging/rdma/hfi1/sdma.c
@@ -236,7 +236,6 @@ static void sdma_hw_clean_up_task(unsigned long);
static void sdma_put(struct sdma_state *);
static void sdma_set_state(struct sdma_engine *, enum sdma_states);
static void sdma_start_hw_clean_up(struct sdma_engine *);
-static void sdma_start_sw_clean_up(struct sdma_engine *);
static void sdma_sw_clean_up_task(unsigned long);
static void sdma_sendctrl(struct sdma_engine *, unsigned);
static void init_sdma_regs(struct sdma_engine *, u32, uint);
@@ -470,12 +469,6 @@ static void sdma_err_halt_wait(struct work_struct *work)
sdma_process_event(sde, sdma_event_e15_hw_halt_done);
}
-static void sdma_start_err_halt_wait(struct sdma_engine *sde)
-{
- schedule_work(&sde->err_halt_worker);
-}
-
-
static void sdma_err_progress_check_schedule(struct sdma_engine *sde)
{
if (!is_bx(sde->dd) && HFI1_CAP_IS_KSET(SDMA_AHG)) {
@@ -682,11 +675,6 @@ static void sdma_start_hw_clean_up(struct sdma_engine *sde)
tasklet_hi_schedule(&sde->sdma_hw_clean_up_task);
}
-static void sdma_start_sw_clean_up(struct sdma_engine *sde)
-{
- tasklet_hi_schedule(&sde->sdma_sw_clean_up_task);
-}
-
static void sdma_set_state(struct sdma_engine *sde,
enum sdma_states next_state)
{
@@ -777,19 +765,27 @@ struct sdma_engine *sdma_select_engine_vl(
struct sdma_map_elem *e;
struct sdma_engine *rval;
- if (WARN_ON(vl > 8))
- return NULL;
+ /* NOTE This should only happen if SC->VL changed after the initial
+ * checks on the QP/AH
+ * Default will return engine 0 below
+ */
+ if (vl >= num_vls) {
+ rval = NULL;
+ goto done;
+ }
rcu_read_lock();
m = rcu_dereference(dd->sdma_map);
if (unlikely(!m)) {
rcu_read_unlock();
- return NULL;
+ return &dd->per_sdma[0];
}
e = m->map[vl & m->mask];
rval = e->sde[selector & e->mask];
rcu_read_unlock();
+done:
+ rval = !rval ? &dd->per_sdma[0] : rval;
trace_hfi1_sdma_engine_select(dd, selector, vl, rval->this_idx);
return rval;
}
@@ -1874,7 +1870,7 @@ static void dump_sdma_state(struct sdma_engine *sde)
}
#define SDE_FMT \
- "SDE %u STE %s C 0x%llx S 0x%016llx E 0x%llx T(HW) 0x%llx T(SW) 0x%x H(HW) 0x%llx H(SW) 0x%x H(D) 0x%llx DM 0x%llx GL 0x%llx R 0x%llx LIS 0x%llx AHGI 0x%llx TXT %u TXH %u DT %u DH %u FLNE %d DQF %u SLC 0x%llx\n"
+ "SDE %u CPU %d STE %s C 0x%llx S 0x%016llx E 0x%llx T(HW) 0x%llx T(SW) 0x%x H(HW) 0x%llx H(SW) 0x%x H(D) 0x%llx DM 0x%llx GL 0x%llx R 0x%llx LIS 0x%llx AHGI 0x%llx TXT %u TXH %u DT %u DH %u FLNE %d DQF %u SLC 0x%llx\n"
/**
* sdma_seqfile_dump_sde() - debugfs dump of sde
* @s: seq file
@@ -1894,6 +1890,7 @@ void sdma_seqfile_dump_sde(struct seq_file *s, struct sdma_engine *sde)
head = sde->descq_head & sde->sdma_mask;
tail = ACCESS_ONCE(sde->descq_tail) & sde->sdma_mask;
seq_printf(s, SDE_FMT, sde->this_idx,
+ sde->cpu,
sdma_state_name(sde->state.current_state),
(unsigned long long)read_sde_csr(sde, SD(CTRL)),
(unsigned long long)read_sde_csr(sde, SD(STATUS)),
@@ -2308,7 +2305,7 @@ static void __sdma_process_event(struct sdma_engine *sde,
case sdma_event_e50_hw_cleaned:
break;
case sdma_event_e60_hw_halted:
- sdma_start_err_halt_wait(sde);
+ schedule_work(&sde->err_halt_worker);
break;
case sdma_event_e70_go_idle:
ss->go_s99_running = 0;
@@ -2389,7 +2386,7 @@ static void __sdma_process_event(struct sdma_engine *sde,
break;
case sdma_event_e60_hw_halted:
sdma_set_state(sde, sdma_state_s50_hw_halt_wait);
- sdma_start_err_halt_wait(sde);
+ schedule_work(&sde->err_halt_worker);
break;
case sdma_event_e70_go_idle:
break;
@@ -2452,7 +2449,7 @@ static void __sdma_process_event(struct sdma_engine *sde,
switch (event) {
case sdma_event_e00_go_hw_down:
sdma_set_state(sde, sdma_state_s00_hw_down);
- sdma_start_sw_clean_up(sde);
+ tasklet_hi_schedule(&sde->sdma_sw_clean_up_task);
break;
case sdma_event_e10_go_hw_start:
break;
@@ -2494,13 +2491,13 @@ static void __sdma_process_event(struct sdma_engine *sde,
switch (event) {
case sdma_event_e00_go_hw_down:
sdma_set_state(sde, sdma_state_s00_hw_down);
- sdma_start_sw_clean_up(sde);
+ tasklet_hi_schedule(&sde->sdma_sw_clean_up_task);
break;
case sdma_event_e10_go_hw_start:
break;
case sdma_event_e15_hw_halt_done:
sdma_set_state(sde, sdma_state_s30_sw_clean_up_wait);
- sdma_start_sw_clean_up(sde);
+ tasklet_hi_schedule(&sde->sdma_sw_clean_up_task);
break;
case sdma_event_e25_hw_clean_up_done:
break;
@@ -2512,7 +2509,7 @@ static void __sdma_process_event(struct sdma_engine *sde,
case sdma_event_e50_hw_cleaned:
break;
case sdma_event_e60_hw_halted:
- sdma_start_err_halt_wait(sde);
+ schedule_work(&sde->err_halt_worker);
break;
case sdma_event_e70_go_idle:
ss->go_s99_running = 0;
@@ -2535,13 +2532,13 @@ static void __sdma_process_event(struct sdma_engine *sde,
switch (event) {
case sdma_event_e00_go_hw_down:
sdma_set_state(sde, sdma_state_s00_hw_down);
- sdma_start_sw_clean_up(sde);
+ tasklet_hi_schedule(&sde->sdma_sw_clean_up_task);
break;
case sdma_event_e10_go_hw_start:
break;
case sdma_event_e15_hw_halt_done:
sdma_set_state(sde, sdma_state_s30_sw_clean_up_wait);
- sdma_start_sw_clean_up(sde);
+ tasklet_hi_schedule(&sde->sdma_sw_clean_up_task);
break;
case sdma_event_e25_hw_clean_up_done:
break;
@@ -2553,7 +2550,7 @@ static void __sdma_process_event(struct sdma_engine *sde,
case sdma_event_e50_hw_cleaned:
break;
case sdma_event_e60_hw_halted:
- sdma_start_err_halt_wait(sde);
+ schedule_work(&sde->err_halt_worker);
break;
case sdma_event_e70_go_idle:
ss->go_s99_running = 0;
@@ -2575,7 +2572,7 @@ static void __sdma_process_event(struct sdma_engine *sde,
switch (event) {
case sdma_event_e00_go_hw_down:
sdma_set_state(sde, sdma_state_s00_hw_down);
- sdma_start_sw_clean_up(sde);
+ tasklet_hi_schedule(&sde->sdma_sw_clean_up_task);
break;
case sdma_event_e10_go_hw_start:
break;
@@ -2599,7 +2596,7 @@ static void __sdma_process_event(struct sdma_engine *sde,
break;
case sdma_event_e81_hw_frozen:
sdma_set_state(sde, sdma_state_s82_freeze_sw_clean);
- sdma_start_sw_clean_up(sde);
+ tasklet_hi_schedule(&sde->sdma_sw_clean_up_task);
break;
case sdma_event_e82_hw_unfreeze:
break;
@@ -2614,7 +2611,7 @@ static void __sdma_process_event(struct sdma_engine *sde,
switch (event) {
case sdma_event_e00_go_hw_down:
sdma_set_state(sde, sdma_state_s00_hw_down);
- sdma_start_sw_clean_up(sde);
+ tasklet_hi_schedule(&sde->sdma_sw_clean_up_task);
break;
case sdma_event_e10_go_hw_start:
break;
@@ -2658,7 +2655,7 @@ static void __sdma_process_event(struct sdma_engine *sde,
switch (event) {
case sdma_event_e00_go_hw_down:
sdma_set_state(sde, sdma_state_s00_hw_down);
- sdma_start_sw_clean_up(sde);
+ tasklet_hi_schedule(&sde->sdma_sw_clean_up_task);
break;
case sdma_event_e10_go_hw_start:
break;
@@ -2681,7 +2678,7 @@ static void __sdma_process_event(struct sdma_engine *sde,
* progress check
*/
sdma_set_state(sde, sdma_state_s50_hw_halt_wait);
- sdma_start_err_halt_wait(sde);
+ schedule_work(&sde->err_halt_worker);
break;
case sdma_event_e70_go_idle:
sdma_set_state(sde, sdma_state_s60_idle_halt_wait);
@@ -2734,22 +2731,21 @@ static int _extend_sdma_tx_descs(struct hfi1_devdata *dd, struct sdma_txreq *tx)
tx->coalesce_buf = kmalloc(tx->tlen + sizeof(u32),
GFP_ATOMIC);
if (!tx->coalesce_buf)
- return -ENOMEM;
-
+ goto enomem;
tx->coalesce_idx = 0;
}
return 0;
}
if (unlikely(tx->num_desc == MAX_DESC))
- return -ENOMEM;
+ goto enomem;
tx->descp = kmalloc_array(
MAX_DESC,
sizeof(struct sdma_desc),
GFP_ATOMIC);
if (!tx->descp)
- return -ENOMEM;
+ goto enomem;
/* reserve last descriptor for coalescing */
tx->desc_limit = MAX_DESC - 1;
@@ -2757,6 +2753,9 @@ static int _extend_sdma_tx_descs(struct hfi1_devdata *dd, struct sdma_txreq *tx)
for (i = 0; i < tx->num_desc; i++)
tx->descp[i] = tx->descs[i];
return 0;
+enomem:
+ sdma_txclean(dd, tx);
+ return -ENOMEM;
}
/*
diff --git a/drivers/staging/rdma/hfi1/sdma.h b/drivers/staging/rdma/hfi1/sdma.h
index cc22d2ee2054..da89e6458162 100644
--- a/drivers/staging/rdma/hfi1/sdma.h
+++ b/drivers/staging/rdma/hfi1/sdma.h
@@ -410,8 +410,6 @@ struct sdma_engine {
u64 idle_mask;
u64 progress_mask;
/* private: */
- struct workqueue_struct *wq;
- /* private: */
volatile __le64 *head_dma; /* DMA'ed by chip */
/* private: */
dma_addr_t head_phys;
@@ -426,6 +424,8 @@ struct sdma_engine {
u32 sdma_mask;
/* private */
struct sdma_state state;
+ /* private */
+ int cpu;
/* private: */
u8 sdma_shift;
/* private: */
@@ -774,10 +774,13 @@ static inline int _sdma_txadd_daddr(
tx->tlen -= len;
/* special cases for last */
if (!tx->tlen) {
- if (tx->packet_len & (sizeof(u32) - 1))
+ if (tx->packet_len & (sizeof(u32) - 1)) {
rval = _pad_sdma_tx_descs(dd, tx);
- else
+ if (rval)
+ return rval;
+ } else {
_sdma_close_tx(dd, tx);
+ }
}
tx->num_desc++;
return rval;
@@ -990,7 +993,9 @@ static inline void sdma_iowait_schedule(
struct sdma_engine *sde,
struct iowait *wait)
{
- iowait_schedule(wait, sde->wq);
+ struct hfi1_pportdata *ppd = sde->dd->pport;
+
+ iowait_schedule(wait, ppd->hfi1_wq, sde->cpu);
}
/* for use by interrupt handling */
diff --git a/drivers/staging/rdma/hfi1/trace.c b/drivers/staging/rdma/hfi1/trace.c
index f55b75194847..10122e84cb2f 100644
--- a/drivers/staging/rdma/hfi1/trace.c
+++ b/drivers/staging/rdma/hfi1/trace.c
@@ -67,7 +67,7 @@ u8 ibhdr_exhdr_len(struct hfi1_ib_header *hdr)
#define IMM_PRN "imm %d"
#define RETH_PRN "reth vaddr 0x%.16llx rkey 0x%.8x dlen 0x%.8x"
-#define AETH_PRN "aeth syn 0x%.2x msn 0x%.8x"
+#define AETH_PRN "aeth syn 0x%.2x %s msn 0x%.8x"
#define DETH_PRN "deth qkey 0x%.8x sqpn 0x%.6x"
#define ATOMICACKETH_PRN "origdata %lld"
#define ATOMICETH_PRN "vaddr 0x%llx rkey 0x%.8x sdata %lld cdata %lld"
@@ -79,6 +79,19 @@ static u64 ib_u64_get(__be32 *p)
return ((u64)be32_to_cpu(p[0]) << 32) | be32_to_cpu(p[1]);
}
+static const char *parse_syndrome(u8 syndrome)
+{
+ switch (syndrome >> 5) {
+ case 0:
+ return "ACK";
+ case 1:
+ return "RNRNAK";
+ case 3:
+ return "NAK";
+ }
+ return "";
+}
+
const char *parse_everbs_hdrs(
struct trace_seq *p,
u8 opcode,
@@ -124,16 +137,18 @@ const char *parse_everbs_hdrs(
case OP(RC, RDMA_READ_RESPONSE_LAST):
case OP(RC, RDMA_READ_RESPONSE_ONLY):
case OP(RC, ACKNOWLEDGE):
- trace_seq_printf(p, AETH_PRN,
- be32_to_cpu(eh->aeth) >> 24,
- be32_to_cpu(eh->aeth) & HFI1_MSN_MASK);
+ trace_seq_printf(p, AETH_PRN, be32_to_cpu(eh->aeth) >> 24,
+ parse_syndrome(be32_to_cpu(eh->aeth) >> 24),
+ be32_to_cpu(eh->aeth) & HFI1_MSN_MASK);
break;
/* aeth + atomicacketh */
case OP(RC, ATOMIC_ACKNOWLEDGE):
trace_seq_printf(p, AETH_PRN " " ATOMICACKETH_PRN,
- (be32_to_cpu(eh->at.aeth) >> 24) & 0xff,
- be32_to_cpu(eh->at.aeth) & HFI1_MSN_MASK,
- (unsigned long long)ib_u64_get(eh->at.atomic_ack_eth));
+ be32_to_cpu(eh->at.aeth) >> 24,
+ parse_syndrome(be32_to_cpu(eh->at.aeth) >> 24),
+ be32_to_cpu(eh->at.aeth) & HFI1_MSN_MASK,
+ (unsigned long long)
+ ib_u64_get(eh->at.atomic_ack_eth));
break;
/* atomiceth */
case OP(RC, COMPARE_SWAP):
diff --git a/drivers/staging/rdma/hfi1/trace.h b/drivers/staging/rdma/hfi1/trace.h
index 57430295c404..86c12ebfd4f0 100644
--- a/drivers/staging/rdma/hfi1/trace.h
+++ b/drivers/staging/rdma/hfi1/trace.h
@@ -417,7 +417,8 @@ __print_symbolic(opcode, \
ib_opcode_name(UC_RDMA_WRITE_ONLY), \
ib_opcode_name(UC_RDMA_WRITE_ONLY_WITH_IMMEDIATE), \
ib_opcode_name(UD_SEND_ONLY), \
- ib_opcode_name(UD_SEND_ONLY_WITH_IMMEDIATE))
+ ib_opcode_name(UD_SEND_ONLY_WITH_IMMEDIATE), \
+ ib_opcode_name(CNP))
#define LRH_PRN "vl %d lver %d sl %d lnh %d,%s dlid %.4x len %d slid %.4x"
diff --git a/drivers/staging/rdma/hfi1/uc.c b/drivers/staging/rdma/hfi1/uc.c
index 6095039c4485..4f2a7889a852 100644
--- a/drivers/staging/rdma/hfi1/uc.c
+++ b/drivers/staging/rdma/hfi1/uc.c
@@ -268,7 +268,7 @@ void hfi1_uc_rcv(struct hfi1_packet *packet)
u32 tlen = packet->tlen;
struct hfi1_qp *qp = packet->qp;
struct hfi1_other_headers *ohdr = packet->ohdr;
- u32 opcode;
+ u32 bth0, opcode;
u32 hdrsize = packet->hlen;
u32 psn;
u32 pad;
@@ -278,10 +278,9 @@ void hfi1_uc_rcv(struct hfi1_packet *packet)
int has_grh = rcv_flags & HFI1_HAS_GRH;
int ret;
u32 bth1;
- struct ib_grh *grh = NULL;
- opcode = be32_to_cpu(ohdr->bth[0]);
- if (hfi1_ruc_check_hdr(ibp, hdr, has_grh, qp, opcode))
+ bth0 = be32_to_cpu(ohdr->bth[0]);
+ if (hfi1_ruc_check_hdr(ibp, hdr, has_grh, qp, bth0))
return;
bth1 = be32_to_cpu(ohdr->bth[1]);
@@ -303,6 +302,7 @@ void hfi1_uc_rcv(struct hfi1_packet *packet)
}
if (bth1 & HFI1_FECN_SMASK) {
+ struct ib_grh *grh = NULL;
u16 pkey = (u16)be32_to_cpu(ohdr->bth[0]);
u16 slid = be16_to_cpu(hdr->lrh[3]);
u16 dlid = be16_to_cpu(hdr->lrh[1]);
@@ -310,13 +310,16 @@ void hfi1_uc_rcv(struct hfi1_packet *packet)
u8 sc5;
sc5 = ibp->sl_to_sc[qp->remote_ah_attr.sl];
+ if (has_grh)
+ grh = &hdr->u.l.grh;
- return_cnp(ibp, qp, src_qp, pkey, dlid, slid, sc5, grh);
+ return_cnp(ibp, qp, src_qp, pkey, dlid, slid, sc5,
+ grh);
}
}
psn = be32_to_cpu(ohdr->bth[2]);
- opcode >>= 24;
+ opcode = (bth0 >> 24) & 0xff;
/* Compare the PSN verses the expected PSN. */
if (unlikely(cmp_psn(psn, qp->r_psn) != 0)) {
diff --git a/drivers/staging/rdma/hfi1/ud.c b/drivers/staging/rdma/hfi1/ud.c
index 5a9c784bec04..bd1b402c1e14 100644
--- a/drivers/staging/rdma/hfi1/ud.c
+++ b/drivers/staging/rdma/hfi1/ud.c
@@ -111,11 +111,10 @@ static void ud_loopback(struct hfi1_qp *sqp, struct hfi1_swqe *swqe)
((1 << ppd->lmc) - 1));
if (unlikely(ingress_pkey_check(ppd, pkey, sc5,
qp->s_pkey_index, slid))) {
- hfi1_bad_pqkey(ibp, IB_NOTICE_TRAP_BAD_PKEY, pkey,
+ hfi1_bad_pqkey(ibp, OPA_TRAP_BAD_P_KEY, pkey,
ah_attr->sl,
sqp->ibqp.qp_num, qp->ibqp.qp_num,
- cpu_to_be16(slid),
- cpu_to_be16(ah_attr->dlid));
+ slid, ah_attr->dlid);
goto drop;
}
}
@@ -135,11 +134,11 @@ static void ud_loopback(struct hfi1_qp *sqp, struct hfi1_swqe *swqe)
lid = ppd->lid | (ah_attr->src_path_bits &
((1 << ppd->lmc) - 1));
- hfi1_bad_pqkey(ibp, IB_NOTICE_TRAP_BAD_QKEY, qkey,
+ hfi1_bad_pqkey(ibp, OPA_TRAP_BAD_Q_KEY, qkey,
ah_attr->sl,
sqp->ibqp.qp_num, qp->ibqp.qp_num,
- cpu_to_be16(lid),
- cpu_to_be16(ah_attr->dlid));
+ lid,
+ ah_attr->dlid);
goto drop;
}
}
@@ -383,6 +382,7 @@ int hfi1_make_ud_req(struct hfi1_qp *qp)
lrh0 |= (sc5 & 0xf) << 12;
qp->s_sc = sc5;
}
+ qp->s_sde = qp_to_sdma_engine(qp, qp->s_sc);
qp->s_hdr->ibh.lrh[0] = cpu_to_be16(lrh0);
qp->s_hdr->ibh.lrh[1] = cpu_to_be16(ah_attr->dlid); /* DEST LID */
qp->s_hdr->ibh.lrh[2] =
@@ -736,12 +736,13 @@ void hfi1_ud_rcv(struct hfi1_packet *packet)
* for invalid pkeys is optional according to
* IB spec (release 1.3, section 10.9.4)
*/
- hfi1_bad_pqkey(ibp, IB_NOTICE_TRAP_BAD_PKEY,
+ hfi1_bad_pqkey(ibp, OPA_TRAP_BAD_P_KEY,
pkey,
(be16_to_cpu(hdr->lrh[0]) >> 4) &
0xF,
src_qp, qp->ibqp.qp_num,
- hdr->lrh[3], hdr->lrh[1]);
+ be16_to_cpu(hdr->lrh[3]),
+ be16_to_cpu(hdr->lrh[1]));
return;
}
} else {
@@ -752,10 +753,11 @@ void hfi1_ud_rcv(struct hfi1_packet *packet)
}
if (unlikely(qkey != qp->qkey)) {
- hfi1_bad_pqkey(ibp, IB_NOTICE_TRAP_BAD_QKEY, qkey,
+ hfi1_bad_pqkey(ibp, OPA_TRAP_BAD_Q_KEY, qkey,
(be16_to_cpu(hdr->lrh[0]) >> 4) & 0xF,
src_qp, qp->ibqp.qp_num,
- hdr->lrh[3], hdr->lrh[1]);
+ be16_to_cpu(hdr->lrh[3]),
+ be16_to_cpu(hdr->lrh[1]));
return;
}
/* Drop invalid MAD packets (see 13.5.3.1). */
diff --git a/drivers/staging/rdma/hfi1/user_exp_rcv.h b/drivers/staging/rdma/hfi1/user_exp_rcv.h
new file mode 100644
index 000000000000..4f4876e1d353
--- /dev/null
+++ b/drivers/staging/rdma/hfi1/user_exp_rcv.h
@@ -0,0 +1,74 @@
+#ifndef _HFI1_USER_EXP_RCV_H
+#define _HFI1_USER_EXP_RCV_H
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2015 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015 Intel Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * - Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#define EXP_TID_TIDLEN_MASK 0x7FFULL
+#define EXP_TID_TIDLEN_SHIFT 0
+#define EXP_TID_TIDCTRL_MASK 0x3ULL
+#define EXP_TID_TIDCTRL_SHIFT 20
+#define EXP_TID_TIDIDX_MASK 0x3FFULL
+#define EXP_TID_TIDIDX_SHIFT 22
+#define EXP_TID_GET(tid, field) \
+ (((tid) >> EXP_TID_TID##field##_SHIFT) & EXP_TID_TID##field##_MASK)
+
+#define EXP_TID_SET(field, value) \
+ (((value) & EXP_TID_TID##field##_MASK) << \
+ EXP_TID_TID##field##_SHIFT)
+#define EXP_TID_CLEAR(tid, field) ({ \
+ (tid) &= ~(EXP_TID_TID##field##_MASK << \
+ EXP_TID_TID##field##_SHIFT); \
+ })
+#define EXP_TID_RESET(tid, field, value) do { \
+ EXP_TID_CLEAR(tid, field); \
+ (tid) |= EXP_TID_SET(field, (value)); \
+ } while (0)
+
+#endif /* _HFI1_USER_EXP_RCV_H */
diff --git a/drivers/staging/rdma/hfi1/user_pages.c b/drivers/staging/rdma/hfi1/user_pages.c
index 9071afbd7bf4..692de658f0dc 100644
--- a/drivers/staging/rdma/hfi1/user_pages.c
+++ b/drivers/staging/rdma/hfi1/user_pages.c
@@ -49,59 +49,11 @@
*/
#include <linux/mm.h>
+#include <linux/sched.h>
#include <linux/device.h>
#include "hfi.h"
-static void __hfi1_release_user_pages(struct page **p, size_t num_pages,
- int dirty)
-{
- size_t i;
-
- for (i = 0; i < num_pages; i++) {
- if (dirty)
- set_page_dirty_lock(p[i]);
- put_page(p[i]);
- }
-}
-
-/*
- * Call with current->mm->mmap_sem held.
- */
-static int __hfi1_get_user_pages(unsigned long start_page, size_t num_pages,
- struct page **p)
-{
- unsigned long lock_limit;
- size_t got;
- int ret;
-
- lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
-
- if (num_pages > lock_limit && !capable(CAP_IPC_LOCK)) {
- ret = -ENOMEM;
- goto bail;
- }
-
- for (got = 0; got < num_pages; got += ret) {
- ret = get_user_pages(current, current->mm,
- start_page + got * PAGE_SIZE,
- num_pages - got, 1, 1,
- p + got, NULL);
- if (ret < 0)
- goto bail_release;
- }
-
- current->mm->pinned_vm += num_pages;
-
- ret = 0;
- goto bail;
-
-bail_release:
- __hfi1_release_user_pages(p, got, 0);
-bail:
- return ret;
-}
-
/**
* hfi1_map_page - a safety wrapper around pci_map_page()
*
@@ -116,41 +68,44 @@ dma_addr_t hfi1_map_page(struct pci_dev *hwdev, struct page *page,
return phys;
}
-/**
- * hfi1_get_user_pages - lock user pages into memory
- * @start_page: the start page
- * @num_pages: the number of pages
- * @p: the output page structures
- *
- * This function takes a given start page (page aligned user virtual
- * address) and pins it and the following specified number of pages. For
- * now, num_pages is always 1, but that will probably change at some point
- * (because caller is doing expected sends on a single virtually contiguous
- * buffer, so we can do all pages at once).
- */
-int hfi1_get_user_pages(unsigned long start_page, size_t num_pages,
- struct page **p)
+int hfi1_acquire_user_pages(unsigned long vaddr, size_t npages, bool writable,
+ struct page **pages)
{
+ unsigned long pinned, lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+ bool can_lock = capable(CAP_IPC_LOCK);
int ret;
- down_write(&current->mm->mmap_sem);
+ down_read(&current->mm->mmap_sem);
+ pinned = current->mm->pinned_vm;
+ up_read(&current->mm->mmap_sem);
- ret = __hfi1_get_user_pages(start_page, num_pages, p);
+ if (pinned + npages > lock_limit && !can_lock)
+ return -ENOMEM;
+ ret = get_user_pages_fast(vaddr, npages, writable, pages);
+ if (ret < 0)
+ return ret;
+
+ down_write(&current->mm->mmap_sem);
+ current->mm->pinned_vm += ret;
up_write(&current->mm->mmap_sem);
return ret;
}
-void hfi1_release_user_pages(struct page **p, size_t num_pages)
+void hfi1_release_user_pages(struct page **p, size_t npages, bool dirty)
{
- if (current->mm) /* during close after signal, mm can be NULL */
- down_write(&current->mm->mmap_sem);
+ size_t i;
- __hfi1_release_user_pages(p, num_pages, 1);
+ for (i = 0; i < npages; i++) {
+ if (dirty)
+ set_page_dirty_lock(p[i]);
+ put_page(p[i]);
+ }
- if (current->mm) {
- current->mm->pinned_vm -= num_pages;
+ if (current->mm) { /* during close after signal, mm can be NULL */
+ down_write(&current->mm->mmap_sem);
+ current->mm->pinned_vm -= npages;
up_write(&current->mm->mmap_sem);
}
}
diff --git a/drivers/staging/rdma/hfi1/user_sdma.c b/drivers/staging/rdma/hfi1/user_sdma.c
index 36c838dcf023..d3de771a0770 100644
--- a/drivers/staging/rdma/hfi1/user_sdma.c
+++ b/drivers/staging/rdma/hfi1/user_sdma.c
@@ -146,8 +146,8 @@ MODULE_PARM_DESC(sdma_comp_size, "Size of User SDMA completion ring. Default: 12
#define KDETH_OM_MAX_SIZE (1 << ((KDETH_OM_LARGE / KDETH_OM_SMALL) + 1))
/* Last packet in the request */
-#define TXREQ_FLAGS_REQ_LAST_PKT (1 << 0)
-#define TXREQ_FLAGS_IOVEC_LAST_PKT (1 << 0)
+#define TXREQ_FLAGS_REQ_LAST_PKT BIT(0)
+#define TXREQ_FLAGS_IOVEC_LAST_PKT BIT(0)
#define SDMA_REQ_IN_USE 0
#define SDMA_REQ_FOR_THREAD 1
@@ -156,9 +156,9 @@ MODULE_PARM_DESC(sdma_comp_size, "Size of User SDMA completion ring. Default: 12
#define SDMA_REQ_HAS_ERROR 4
#define SDMA_REQ_DONE_ERROR 5
-#define SDMA_PKT_Q_INACTIVE (1 << 0)
-#define SDMA_PKT_Q_ACTIVE (1 << 1)
-#define SDMA_PKT_Q_DEFERRED (1 << 2)
+#define SDMA_PKT_Q_INACTIVE BIT(0)
+#define SDMA_PKT_Q_ACTIVE BIT(1)
+#define SDMA_PKT_Q_DEFERRED BIT(2)
/*
* Maximum retry attempts to submit a TX request
@@ -214,12 +214,6 @@ struct user_sdma_request {
*/
u8 omfactor;
/*
- * pointer to the user's task_struct. We are going to
- * get a reference to it so we can process io vectors
- * at a later time.
- */
- struct task_struct *user_proc;
- /*
* pointer to the user's mm_struct. We are going to
* get a reference to it so it doesn't get freed
* since we might not be in process context when we
@@ -245,9 +239,13 @@ struct user_sdma_request {
u16 tididx;
u32 sent;
u64 seqnum;
- spinlock_t list_lock;
struct list_head txps;
+ spinlock_t txcmp_lock; /* protect txcmp list */
+ struct list_head txcmp;
unsigned long flags;
+ /* status of the last txreq completed */
+ int status;
+ struct work_struct worker;
};
/*
@@ -260,6 +258,7 @@ struct user_sdma_txreq {
/* Packet header for the txreq */
struct hfi1_pkt_header hdr;
struct sdma_txreq txreq;
+ struct list_head list;
struct user_sdma_request *req;
struct {
struct user_sdma_iovec *vec;
@@ -282,10 +281,12 @@ struct user_sdma_txreq {
static int user_sdma_send_pkts(struct user_sdma_request *, unsigned);
static int num_user_pages(const struct iovec *);
static void user_sdma_txreq_cb(struct sdma_txreq *, int, int);
+static void user_sdma_delayed_completion(struct work_struct *);
static void user_sdma_free_request(struct user_sdma_request *);
static int pin_vector_pages(struct user_sdma_request *,
struct user_sdma_iovec *);
-static void unpin_vector_pages(struct user_sdma_iovec *);
+static void unpin_vector_pages(struct user_sdma_request *,
+ struct user_sdma_iovec *);
static int check_header_template(struct user_sdma_request *,
struct hfi1_pkt_header *, u32, u32);
static int set_txreq_header(struct user_sdma_request *,
@@ -352,6 +353,7 @@ static void sdma_kmem_cache_ctor(void *obj)
int hfi1_user_sdma_alloc_queues(struct hfi1_ctxtdata *uctxt, struct file *fp)
{
+ struct hfi1_filedata *fd;
int ret = 0;
unsigned memsize;
char buf[64];
@@ -365,6 +367,8 @@ int hfi1_user_sdma_alloc_queues(struct hfi1_ctxtdata *uctxt, struct file *fp)
goto done;
}
+ fd = fp->private_data;
+
if (!hfi1_sdma_comp_ring_size) {
ret = -EINVAL;
goto done;
@@ -384,16 +388,17 @@ int hfi1_user_sdma_alloc_queues(struct hfi1_ctxtdata *uctxt, struct file *fp)
INIT_LIST_HEAD(&pq->list);
pq->dd = dd;
pq->ctxt = uctxt->ctxt;
- pq->subctxt = subctxt_fp(fp);
+ pq->subctxt = fd->subctxt;
pq->n_max_reqs = hfi1_sdma_comp_ring_size;
pq->state = SDMA_PKT_Q_INACTIVE;
atomic_set(&pq->n_reqs, 0);
+ init_waitqueue_head(&pq->wait);
iowait_init(&pq->busy, 0, NULL, defer_packet_queue,
activate_packet_queue);
pq->reqidx = 0;
snprintf(buf, 64, "txreq-kmem-cache-%u-%u-%u", dd->unit, uctxt->ctxt,
- subctxt_fp(fp));
+ fd->subctxt);
pq->txreq_cache = kmem_cache_create(buf,
sizeof(struct user_sdma_txreq),
L1_CACHE_BYTES,
@@ -404,7 +409,7 @@ int hfi1_user_sdma_alloc_queues(struct hfi1_ctxtdata *uctxt, struct file *fp)
uctxt->ctxt);
goto pq_txreq_nomem;
}
- user_sdma_pkt_fp(fp) = pq;
+ fd->pq = pq;
cq = kzalloc(sizeof(*cq), GFP_KERNEL);
if (!cq)
goto cq_nomem;
@@ -416,7 +421,7 @@ int hfi1_user_sdma_alloc_queues(struct hfi1_ctxtdata *uctxt, struct file *fp)
goto cq_comps_nomem;
cq->nentries = hfi1_sdma_comp_ring_size;
- user_sdma_comp_fp(fp) = cq;
+ fd->cq = cq;
spin_lock_irqsave(&uctxt->sdma_qlock, flags);
list_add(&pq->list, &uctxt->sdma_queues);
@@ -431,7 +436,7 @@ pq_txreq_nomem:
kfree(pq->reqs);
pq_reqs_nomem:
kfree(pq);
- user_sdma_pkt_fp(fp) = NULL;
+ fd->pq = NULL;
pq_nomem:
ret = -ENOMEM;
done:
@@ -448,26 +453,16 @@ int hfi1_user_sdma_free_queues(struct hfi1_filedata *fd)
uctxt->ctxt, fd->subctxt);
pq = fd->pq;
if (pq) {
- u16 i, j;
-
spin_lock_irqsave(&uctxt->sdma_qlock, flags);
if (!list_empty(&pq->list))
list_del_init(&pq->list);
spin_unlock_irqrestore(&uctxt->sdma_qlock, flags);
iowait_sdma_drain(&pq->busy);
- if (pq->reqs) {
- for (i = 0, j = 0; i < atomic_read(&pq->n_reqs) &&
- j < pq->n_max_reqs; j++) {
- struct user_sdma_request *req = &pq->reqs[j];
-
- if (test_bit(SDMA_REQ_IN_USE, &req->flags)) {
- set_comp_state(req, ERROR, -ECOMM);
- user_sdma_free_request(req);
- i++;
- }
- }
- kfree(pq->reqs);
- }
+ /* Wait until all requests have been freed. */
+ wait_event_interruptible(
+ pq->wait,
+ (ACCESS_ONCE(pq->state) == SDMA_PKT_Q_INACTIVE));
+ kfree(pq->reqs);
kmem_cache_destroy(pq->txreq_cache);
kfree(pq);
fd->pq = NULL;
@@ -485,9 +480,10 @@ int hfi1_user_sdma_process_request(struct file *fp, struct iovec *iovec,
unsigned long dim, unsigned long *count)
{
int ret = 0, i = 0, sent;
- struct hfi1_ctxtdata *uctxt = ctxt_fp(fp);
- struct hfi1_user_sdma_pkt_q *pq = user_sdma_pkt_fp(fp);
- struct hfi1_user_sdma_comp_q *cq = user_sdma_comp_fp(fp);
+ struct hfi1_filedata *fd = fp->private_data;
+ struct hfi1_ctxtdata *uctxt = fd->uctxt;
+ struct hfi1_user_sdma_pkt_q *pq = fd->pq;
+ struct hfi1_user_sdma_comp_q *cq = fd->cq;
struct hfi1_devdata *dd = pq->dd;
unsigned long idx = 0;
u8 pcount = initial_pkt_count;
@@ -499,40 +495,36 @@ int hfi1_user_sdma_process_request(struct file *fp, struct iovec *iovec,
hfi1_cdbg(
SDMA,
"[%u:%u:%u] First vector not big enough for header %lu/%lu",
- dd->unit, uctxt->ctxt, subctxt_fp(fp),
+ dd->unit, uctxt->ctxt, fd->subctxt,
iovec[idx].iov_len, sizeof(info) + sizeof(req->hdr));
- ret = -EINVAL;
- goto done;
+ return -EINVAL;
}
ret = copy_from_user(&info, iovec[idx].iov_base, sizeof(info));
if (ret) {
hfi1_cdbg(SDMA, "[%u:%u:%u] Failed to copy info QW (%d)",
- dd->unit, uctxt->ctxt, subctxt_fp(fp), ret);
- ret = -EFAULT;
- goto done;
+ dd->unit, uctxt->ctxt, fd->subctxt, ret);
+ return -EFAULT;
}
- trace_hfi1_sdma_user_reqinfo(dd, uctxt->ctxt, subctxt_fp(fp),
+ trace_hfi1_sdma_user_reqinfo(dd, uctxt->ctxt, fd->subctxt,
(u16 *)&info);
if (cq->comps[info.comp_idx].status == QUEUED) {
hfi1_cdbg(SDMA, "[%u:%u:%u] Entry %u is in QUEUED state",
- dd->unit, uctxt->ctxt, subctxt_fp(fp),
+ dd->unit, uctxt->ctxt, fd->subctxt,
info.comp_idx);
- ret = -EBADSLT;
- goto done;
+ return -EBADSLT;
}
if (!info.fragsize) {
hfi1_cdbg(SDMA,
"[%u:%u:%u:%u] Request does not specify fragsize",
- dd->unit, uctxt->ctxt, subctxt_fp(fp), info.comp_idx);
- ret = -EINVAL;
- goto done;
+ dd->unit, uctxt->ctxt, fd->subctxt, info.comp_idx);
+ return -EINVAL;
}
/*
* We've done all the safety checks that we can up to this point,
* "allocate" the request entry.
*/
hfi1_cdbg(SDMA, "[%u:%u:%u] Using req/comp entry %u\n", dd->unit,
- uctxt->ctxt, subctxt_fp(fp), info.comp_idx);
+ uctxt->ctxt, fd->subctxt, info.comp_idx);
req = pq->reqs + info.comp_idx;
memset(req, 0, sizeof(*req));
/* Mark the request as IN_USE before we start filling it in. */
@@ -540,8 +532,12 @@ int hfi1_user_sdma_process_request(struct file *fp, struct iovec *iovec,
req->data_iovs = req_iovcnt(info.ctrl) - 1;
req->pq = pq;
req->cq = cq;
+ req->status = -1;
INIT_LIST_HEAD(&req->txps);
- spin_lock_init(&req->list_lock);
+ INIT_LIST_HEAD(&req->txcmp);
+ INIT_WORK(&req->worker, user_sdma_delayed_completion);
+
+ spin_lock_init(&req->txcmp_lock);
memcpy(&req->info, &info, sizeof(info));
if (req_opcode(info.ctrl) == EXPECTED)
@@ -550,8 +546,7 @@ int hfi1_user_sdma_process_request(struct file *fp, struct iovec *iovec,
if (!info.npkts || req->data_iovs > MAX_VECTORS_PER_REQ) {
SDMA_DBG(req, "Too many vectors (%u/%u)", req->data_iovs,
MAX_VECTORS_PER_REQ);
- ret = -EINVAL;
- goto done;
+ return -EINVAL;
}
/* Copy the header from the user buffer */
ret = copy_from_user(&req->hdr, iovec[idx].iov_base + sizeof(info),
@@ -659,7 +654,7 @@ int hfi1_user_sdma_process_request(struct file *fp, struct iovec *iovec,
/* Have to select the engine */
req->sde = sdma_select_engine_vl(dd,
- (u32)(uctxt->ctxt + subctxt_fp(fp)),
+ (u32)(uctxt->ctxt + fd->subctxt),
vl);
if (!req->sde || !sdma_running(req->sde)) {
ret = -ECOMM;
@@ -681,18 +676,16 @@ int hfi1_user_sdma_process_request(struct file *fp, struct iovec *iovec,
sent = user_sdma_send_pkts(req, pcount);
if (unlikely(sent < 0)) {
if (sent != -EBUSY) {
- ret = sent;
- goto send_err;
+ req->status = sent;
+ set_comp_state(req, ERROR, req->status);
+ return sent;
} else
sent = 0;
}
atomic_inc(&pq->n_reqs);
+ xchg(&pq->state, SDMA_PKT_Q_ACTIVE);
if (sent < req->info.npkts) {
- /* Take the references to the user's task and mm_struct */
- get_task_struct(current);
- req->user_proc = current;
-
/*
* This is a somewhat blocking send implementation.
* The driver will block the caller until all packets of the
@@ -702,8 +695,10 @@ int hfi1_user_sdma_process_request(struct file *fp, struct iovec *iovec,
while (!test_bit(SDMA_REQ_SEND_DONE, &req->flags)) {
ret = user_sdma_send_pkts(req, pcount);
if (ret < 0) {
- if (ret != -EBUSY)
- goto send_err;
+ if (ret != -EBUSY) {
+ req->status = ret;
+ return ret;
+ }
wait_event_interruptible_timeout(
pq->busy.wait_dma,
(pq->state == SDMA_PKT_Q_ACTIVE),
@@ -713,14 +708,10 @@ int hfi1_user_sdma_process_request(struct file *fp, struct iovec *iovec,
}
}
- ret = 0;
*count += idx;
- goto done;
-send_err:
- set_comp_state(req, ERROR, ret);
+ return 0;
free_req:
user_sdma_free_request(req);
-done:
return ret;
}
@@ -778,20 +769,24 @@ static int user_sdma_send_pkts(struct user_sdma_request *req, unsigned maxpkts)
struct hfi1_user_sdma_pkt_q *pq = NULL;
struct user_sdma_iovec *iovec = NULL;
- if (!req->pq) {
- ret = -EINVAL;
- goto done;
- }
+ if (!req->pq)
+ return -EINVAL;
pq = req->pq;
+ /* If tx completion has reported an error, we are done. */
+ if (test_bit(SDMA_REQ_HAS_ERROR, &req->flags)) {
+ set_bit(SDMA_REQ_DONE_ERROR, &req->flags);
+ return -EFAULT;
+ }
+
/*
* Check if we might have sent the entire request already
*/
if (unlikely(req->seqnum == req->info.npkts)) {
if (!list_empty(&req->txps))
goto dosend;
- goto done;
+ return ret;
}
if (!maxpkts || maxpkts > req->info.npkts - req->seqnum)
@@ -808,19 +803,18 @@ static int user_sdma_send_pkts(struct user_sdma_request *req, unsigned maxpkts)
*/
if (test_bit(SDMA_REQ_HAS_ERROR, &req->flags)) {
set_bit(SDMA_REQ_DONE_ERROR, &req->flags);
- ret = -EFAULT;
- goto done;
+ return -EFAULT;
}
tx = kmem_cache_alloc(pq->txreq_cache, GFP_KERNEL);
- if (!tx) {
- ret = -ENOMEM;
- goto done;
- }
+ if (!tx)
+ return -ENOMEM;
+
tx->flags = 0;
tx->req = req;
tx->busycount = 0;
tx->idx = -1;
+ INIT_LIST_HEAD(&tx->list);
memset(tx->iovecs, 0, sizeof(tx->iovecs));
if (req->seqnum == req->info.npkts - 1)
@@ -945,9 +939,8 @@ static int user_sdma_send_pkts(struct user_sdma_request *req, unsigned maxpkts)
if (ret) {
int i;
- dd_dev_err(pq->dd,
- "SDMA txreq add page failed %d\n",
- ret);
+ SDMA_DBG(req, "SDMA txreq add page failed %d\n",
+ ret);
/* Mark all assigned vectors as complete so they
* are unpinned in the callback. */
for (i = tx->idx; i >= 0; i--) {
@@ -1017,12 +1010,12 @@ dosend:
if (test_bit(SDMA_REQ_HAVE_AHG, &req->flags))
sdma_ahg_free(req->sde, req->ahg_idx);
}
- goto done;
+ return ret;
+
free_txreq:
sdma_txclean(pq->dd, &tx->txreq);
free_tx:
kmem_cache_free(pq->txreq_cache, tx);
-done:
return ret;
}
@@ -1041,52 +1034,58 @@ static inline int num_user_pages(const struct iovec *iov)
static int pin_vector_pages(struct user_sdma_request *req,
struct user_sdma_iovec *iovec) {
- int ret = 0;
- unsigned pinned;
+ int pinned, npages;
- iovec->npages = num_user_pages(&iovec->iov);
- iovec->pages = kcalloc(iovec->npages, sizeof(*iovec->pages),
- GFP_KERNEL);
+ npages = num_user_pages(&iovec->iov);
+ iovec->pages = kcalloc(npages, sizeof(*iovec->pages), GFP_KERNEL);
if (!iovec->pages) {
SDMA_DBG(req, "Failed page array alloc");
- ret = -ENOMEM;
- goto done;
+ return -ENOMEM;
}
- /* If called by the kernel thread, use the user's mm */
- if (current->flags & PF_KTHREAD)
- use_mm(req->user_proc->mm);
- pinned = get_user_pages_fast(
- (unsigned long)iovec->iov.iov_base,
- iovec->npages, 0, iovec->pages);
- /* If called by the kernel thread, unuse the user's mm */
- if (current->flags & PF_KTHREAD)
- unuse_mm(req->user_proc->mm);
- if (pinned != iovec->npages) {
- SDMA_DBG(req, "Failed to pin pages (%u/%u)", pinned,
- iovec->npages);
- ret = -EFAULT;
- goto pfree;
+
+ /*
+ * Get a reference to the process's mm so we can use it when
+ * unpinning the io vectors.
+ */
+ req->pq->user_mm = get_task_mm(current);
+
+ pinned = hfi1_acquire_user_pages((unsigned long)iovec->iov.iov_base,
+ npages, 0, iovec->pages);
+
+ if (pinned < 0)
+ return pinned;
+
+ iovec->npages = pinned;
+ if (pinned != npages) {
+ SDMA_DBG(req, "Failed to pin pages (%d/%u)", pinned, npages);
+ unpin_vector_pages(req, iovec);
+ return -EFAULT;
}
- goto done;
-pfree:
- unpin_vector_pages(iovec);
-done:
- return ret;
+ return 0;
}
-static void unpin_vector_pages(struct user_sdma_iovec *iovec)
+static void unpin_vector_pages(struct user_sdma_request *req,
+ struct user_sdma_iovec *iovec)
{
- unsigned i;
+ /*
+ * Unpinning is done through the workqueue so use the
+ * process's mm if we have a reference to it.
+ */
+ if ((current->flags & PF_KTHREAD) && req->pq->user_mm)
+ use_mm(req->pq->user_mm);
- if (ACCESS_ONCE(iovec->offset) != iovec->iov.iov_len) {
- hfi1_cdbg(SDMA,
- "the complete vector has not been sent yet %llu %zu",
- iovec->offset, iovec->iov.iov_len);
- return;
+ hfi1_release_user_pages(iovec->pages, iovec->npages, 0);
+
+ /*
+ * Unuse the user's mm (see above) and release the
+ * reference to it.
+ */
+ if (req->pq->user_mm) {
+ if (current->flags & PF_KTHREAD)
+ unuse_mm(req->pq->user_mm);
+ mmput(req->pq->user_mm);
}
- for (i = 0; i < iovec->npages; i++)
- if (iovec->pages[i])
- put_page(iovec->pages[i]);
+
kfree(iovec->pages);
iovec->pages = NULL;
iovec->npages = 0;
@@ -1354,54 +1353,116 @@ static int set_txreq_header_ahg(struct user_sdma_request *req,
return diff;
}
+/*
+ * SDMA tx request completion callback. Called when the SDMA progress
+ * state machine gets notification that the SDMA descriptors for this
+ * tx request have been processed by the DMA engine. Called in
+ * interrupt context.
+ */
static void user_sdma_txreq_cb(struct sdma_txreq *txreq, int status,
int drain)
{
struct user_sdma_txreq *tx =
container_of(txreq, struct user_sdma_txreq, txreq);
- struct user_sdma_request *req = tx->req;
- struct hfi1_user_sdma_pkt_q *pq = req ? req->pq : NULL;
- u64 tx_seqnum;
+ struct user_sdma_request *req;
+ bool defer;
+ int i;
- if (unlikely(!req || !pq))
+ if (!tx->req)
return;
- /* If we have any io vectors associated with this txreq,
- * check whether they need to be 'freed'. */
- if (tx->idx != -1) {
- int i;
+ req = tx->req;
+ /*
+ * If this is the callback for the last packet of the request,
+ * queue up the request for clean up.
+ */
+ defer = (tx->seqnum == req->info.npkts - 1);
- for (i = tx->idx; i >= 0; i--) {
- if (tx->iovecs[i].flags & TXREQ_FLAGS_IOVEC_LAST_PKT)
- unpin_vector_pages(tx->iovecs[i].vec);
+ /*
+ * If we have any io vectors associated with this txreq,
+ * check whether they need to be 'freed'. We can't free them
+ * here because the unpin function needs to be able to sleep.
+ */
+ for (i = tx->idx; i >= 0; i--) {
+ if (tx->iovecs[i].flags & TXREQ_FLAGS_IOVEC_LAST_PKT) {
+ defer = true;
+ break;
}
}
- tx_seqnum = tx->seqnum;
- kmem_cache_free(pq->txreq_cache, tx);
-
+ req->status = status;
if (status != SDMA_TXREQ_S_OK) {
- dd_dev_err(pq->dd, "SDMA completion with error %d", status);
- set_comp_state(req, ERROR, status);
+ SDMA_DBG(req, "SDMA completion with error %d",
+ status);
set_bit(SDMA_REQ_HAS_ERROR, &req->flags);
- /* Do not free the request until the sender loop has ack'ed
- * the error and we've seen all txreqs. */
- if (tx_seqnum == ACCESS_ONCE(req->seqnum) &&
- test_bit(SDMA_REQ_DONE_ERROR, &req->flags)) {
- atomic_dec(&pq->n_reqs);
- user_sdma_free_request(req);
- }
+ defer = true;
+ }
+
+ /*
+ * Defer the clean up of the iovectors and the request until later
+ * so it can be done outside of interrupt context.
+ */
+ if (defer) {
+ spin_lock(&req->txcmp_lock);
+ list_add_tail(&tx->list, &req->txcmp);
+ spin_unlock(&req->txcmp_lock);
+ schedule_work(&req->worker);
} else {
- if (tx_seqnum == req->info.npkts - 1) {
- /* We've sent and completed all packets in this
- * request. Signal completion to the user */
- atomic_dec(&pq->n_reqs);
- set_comp_state(req, COMPLETE, 0);
- user_sdma_free_request(req);
+ kmem_cache_free(req->pq->txreq_cache, tx);
+ }
+}
+
+static void user_sdma_delayed_completion(struct work_struct *work)
+{
+ struct user_sdma_request *req =
+ container_of(work, struct user_sdma_request, worker);
+ struct hfi1_user_sdma_pkt_q *pq = req->pq;
+ struct user_sdma_txreq *tx = NULL;
+ unsigned long flags;
+ u64 seqnum;
+ int i;
+
+ while (1) {
+ spin_lock_irqsave(&req->txcmp_lock, flags);
+ if (!list_empty(&req->txcmp)) {
+ tx = list_first_entry(&req->txcmp,
+ struct user_sdma_txreq, list);
+ list_del(&tx->list);
+ }
+ spin_unlock_irqrestore(&req->txcmp_lock, flags);
+ if (!tx)
+ break;
+
+ for (i = tx->idx; i >= 0; i--)
+ if (tx->iovecs[i].flags & TXREQ_FLAGS_IOVEC_LAST_PKT)
+ unpin_vector_pages(req, tx->iovecs[i].vec);
+
+ seqnum = tx->seqnum;
+ kmem_cache_free(pq->txreq_cache, tx);
+ tx = NULL;
+
+ if (req->status != SDMA_TXREQ_S_OK) {
+ if (seqnum == ACCESS_ONCE(req->seqnum) &&
+ test_bit(SDMA_REQ_DONE_ERROR, &req->flags)) {
+ atomic_dec(&pq->n_reqs);
+ set_comp_state(req, ERROR, req->status);
+ user_sdma_free_request(req);
+ break;
+ }
+ } else {
+ if (seqnum == req->info.npkts - 1) {
+ atomic_dec(&pq->n_reqs);
+ set_comp_state(req, COMPLETE, 0);
+ user_sdma_free_request(req);
+ break;
+ }
}
}
- if (!atomic_read(&pq->n_reqs))
+
+ if (!atomic_read(&pq->n_reqs)) {
xchg(&pq->state, SDMA_PKT_Q_INACTIVE);
+ wake_up(&pq->wait);
+ }
}
static void user_sdma_free_request(struct user_sdma_request *req)
@@ -1422,10 +1483,8 @@ static void user_sdma_free_request(struct user_sdma_request *req)
for (i = 0; i < req->data_iovs; i++)
if (req->iovs[i].npages && req->iovs[i].pages)
- unpin_vector_pages(&req->iovs[i]);
+ unpin_vector_pages(req, &req->iovs[i]);
}
- if (req->user_proc)
- put_task_struct(req->user_proc);
kfree(req->tids);
clear_bit(SDMA_REQ_IN_USE, &req->flags);
}
diff --git a/drivers/staging/rdma/hfi1/user_sdma.h b/drivers/staging/rdma/hfi1/user_sdma.h
index fa4422553e23..0afa28508a8a 100644
--- a/drivers/staging/rdma/hfi1/user_sdma.h
+++ b/drivers/staging/rdma/hfi1/user_sdma.h
@@ -52,15 +52,7 @@
#include "common.h"
#include "iowait.h"
-
-#define EXP_TID_TIDLEN_MASK 0x7FFULL
-#define EXP_TID_TIDLEN_SHIFT 0
-#define EXP_TID_TIDCTRL_MASK 0x3ULL
-#define EXP_TID_TIDCTRL_SHIFT 20
-#define EXP_TID_TIDIDX_MASK 0x7FFULL
-#define EXP_TID_TIDIDX_SHIFT 22
-#define EXP_TID_GET(tid, field) \
- (((tid) >> EXP_TID_TID##field##_SHIFT) & EXP_TID_TID##field##_MASK)
+#include "user_exp_rcv.h"
extern uint extended_psn;
@@ -76,6 +68,8 @@ struct hfi1_user_sdma_pkt_q {
struct user_sdma_request *reqs;
struct iowait busy;
unsigned state;
+ wait_queue_head_t wait;
+ struct mm_struct *user_mm;
};
struct hfi1_user_sdma_comp_q {
diff --git a/drivers/staging/rdma/hfi1/verbs.c b/drivers/staging/rdma/hfi1/verbs.c
index 9beb0aa876f0..ef0feaa684a4 100644
--- a/drivers/staging/rdma/hfi1/verbs.c
+++ b/drivers/staging/rdma/hfi1/verbs.c
@@ -162,6 +162,8 @@ static inline struct hfi1_ucontext *to_iucontext(struct ib_ucontext
return container_of(ibucontext, struct hfi1_ucontext, ibucontext);
}
+static inline void _hfi1_schedule_send(struct hfi1_qp *qp);
+
/*
* Translate ib_wr_opcode into ib_wc_opcode.
*/
@@ -509,9 +511,9 @@ static int post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
nreq++;
}
bail:
- if (nreq && !call_send)
- hfi1_schedule_send(qp);
spin_unlock_irqrestore(&qp->s_lock, flags);
+ if (nreq && !call_send)
+ _hfi1_schedule_send(qp);
if (nreq && call_send)
hfi1_do_send(&qp->s_iowait.iowork);
return err;
@@ -999,17 +1001,19 @@ bail_txadd:
return ret;
}
-int hfi1_verbs_send_dma(struct hfi1_qp *qp, struct ahg_ib_header *ahdr,
- u32 hdrwords, struct hfi1_sge_state *ss, u32 len,
- u32 plen, u32 dwords, u64 pbc)
+int hfi1_verbs_send_dma(struct hfi1_qp *qp, struct hfi1_pkt_state *ps,
+ u64 pbc)
{
- struct hfi1_ibdev *dev = to_idev(qp->ibqp.device);
- struct hfi1_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num);
- struct hfi1_pportdata *ppd = ppd_from_ibp(ibp);
+ struct ahg_ib_header *ahdr = qp->s_hdr;
+ u32 hdrwords = qp->s_hdrwords;
+ struct hfi1_sge_state *ss = qp->s_cur_sge;
+ u32 len = qp->s_cur_size;
+ u32 plen = hdrwords + ((len + 3) >> 2) + 2; /* includes pbc */
+ struct hfi1_ibdev *dev = ps->dev;
+ struct hfi1_pportdata *ppd = ps->ppd;
struct verbs_txreq *tx;
struct sdma_txreq *stx;
u64 pbc_flags = 0;
- struct sdma_engine *sde;
u8 sc5 = qp->s_sc;
int ret;
@@ -1030,12 +1034,7 @@ int hfi1_verbs_send_dma(struct hfi1_qp *qp, struct ahg_ib_header *ahdr,
if (IS_ERR(tx))
goto bail_tx;
- if (!qp->s_hdr->sde) {
- tx->sde = sde = qp_to_sdma_engine(qp, sc5);
- if (!sde)
- goto bail_no_sde;
- } else
- tx->sde = sde = qp->s_hdr->sde;
+ tx->sde = qp->s_sde;
if (likely(pbc == 0)) {
u32 vl = sc_to_vlt(dd_from_ibdev(qp->ibqp.device), sc5);
@@ -1050,17 +1049,15 @@ int hfi1_verbs_send_dma(struct hfi1_qp *qp, struct ahg_ib_header *ahdr,
if (qp->s_rdma_mr)
qp->s_rdma_mr = NULL;
tx->hdr_dwords = hdrwords + 2;
- ret = build_verbs_tx_desc(sde, ss, len, tx, ahdr, pbc);
+ ret = build_verbs_tx_desc(tx->sde, ss, len, tx, ahdr, pbc);
if (unlikely(ret))
goto bail_build;
trace_output_ibhdr(dd_from_ibdev(qp->ibqp.device), &ahdr->ibh);
- ret = sdma_send_txreq(sde, &qp->s_iowait, &tx->txreq);
+ ret = sdma_send_txreq(tx->sde, &qp->s_iowait, &tx->txreq);
if (unlikely(ret == -ECOMM))
goto bail_ecomm;
return ret;
-bail_no_sde:
- hfi1_put_txreq(tx);
bail_ecomm:
/* The current one got "sent" */
return 0;
@@ -1126,12 +1123,16 @@ struct send_context *qp_to_send_context(struct hfi1_qp *qp, u8 sc5)
return dd->vld[vl].sc;
}
-int hfi1_verbs_send_pio(struct hfi1_qp *qp, struct ahg_ib_header *ahdr,
- u32 hdrwords, struct hfi1_sge_state *ss, u32 len,
- u32 plen, u32 dwords, u64 pbc)
+int hfi1_verbs_send_pio(struct hfi1_qp *qp, struct hfi1_pkt_state *ps,
+ u64 pbc)
{
- struct hfi1_ibport *ibp = to_iport(qp->ibqp.device, qp->port_num);
- struct hfi1_pportdata *ppd = ppd_from_ibp(ibp);
+ struct ahg_ib_header *ahdr = qp->s_hdr;
+ u32 hdrwords = qp->s_hdrwords;
+ struct hfi1_sge_state *ss = qp->s_cur_sge;
+ u32 len = qp->s_cur_size;
+ u32 dwords = (len + 3) >> 2;
+ u32 plen = hdrwords + dwords + 2; /* includes pbc */
+ struct hfi1_pportdata *ppd = ps->ppd;
u32 *hdr = (u32 *)&ahdr->ibh;
u64 pbc_flags = 0;
u32 sc5;
@@ -1303,23 +1304,18 @@ bad:
/**
* hfi1_verbs_send - send a packet
* @qp: the QP to send on
- * @ahdr: the packet header
- * @hdrwords: the number of 32-bit words in the header
- * @ss: the SGE to send
- * @len: the length of the packet in bytes
+ * @ps: the state of the packet to send
*
* Return zero if packet is sent or queued OK.
* Return non-zero and clear qp->s_flags HFI1_S_BUSY otherwise.
*/
-int hfi1_verbs_send(struct hfi1_qp *qp, struct ahg_ib_header *ahdr,
- u32 hdrwords, struct hfi1_sge_state *ss, u32 len)
+int hfi1_verbs_send(struct hfi1_qp *qp, struct hfi1_pkt_state *ps)
{
struct hfi1_devdata *dd = dd_from_ibdev(qp->ibqp.device);
- u32 plen;
+ struct ahg_ib_header *ahdr = qp->s_hdr;
int ret;
int pio = 0;
unsigned long flags = 0;
- u32 dwords = (len + 3) >> 2;
/*
* VL15 packets (IB_QPT_SMI) will always use PIO, so we
@@ -1350,23 +1346,16 @@ int hfi1_verbs_send(struct hfi1_qp *qp, struct ahg_ib_header *ahdr,
return -EINVAL;
}
- /*
- * Calculate the send buffer trigger address.
- * The +2 counts for the pbc control qword
- */
- plen = hdrwords + dwords + 2;
-
if (pio) {
- ret = dd->process_pio_send(
- qp, ahdr, hdrwords, ss, len, plen, dwords, 0);
+ ret = dd->process_pio_send(qp, ps, 0);
} else {
#ifdef CONFIG_SDMA_VERBOSITY
dd_dev_err(dd, "CONFIG SDMA %s:%d %s()\n",
slashstrip(__FILE__), __LINE__, __func__);
- dd_dev_err(dd, "SDMA hdrwords = %u, len = %u\n", hdrwords, len);
+ dd_dev_err(dd, "SDMA hdrwords = %u, len = %u\n", qp->s_hdrwords,
+ qp->s_cur_size);
#endif
- ret = dd->process_dma_send(
- qp, ahdr, hdrwords, ss, len, plen, dwords, 0);
+ ret = dd->process_dma_send(qp, ps, 0);
}
return ret;
@@ -2135,28 +2124,43 @@ void hfi1_unregister_ib_device(struct hfi1_devdata *dd)
vfree(dev->lk_table.table);
}
-/*
- * This must be called with s_lock held.
- */
-void hfi1_schedule_send(struct hfi1_qp *qp)
-{
- if (hfi1_send_ok(qp)) {
- struct hfi1_ibport *ibp =
- to_iport(qp->ibqp.device, qp->port_num);
- struct hfi1_pportdata *ppd = ppd_from_ibp(ibp);
-
- iowait_schedule(&qp->s_iowait, ppd->hfi1_wq);
- }
-}
-
void hfi1_cnp_rcv(struct hfi1_packet *packet)
{
struct hfi1_ibport *ibp = &packet->rcd->ppd->ibport_data;
-
- if (packet->qp->ibqp.qp_type == IB_QPT_UC)
- hfi1_uc_rcv(packet);
- else if (packet->qp->ibqp.qp_type == IB_QPT_UD)
- hfi1_ud_rcv(packet);
- else
+ struct hfi1_pportdata *ppd = ppd_from_ibp(ibp);
+ struct hfi1_ib_header *hdr = packet->hdr;
+ struct hfi1_qp *qp = packet->qp;
+ u32 lqpn, rqpn = 0;
+ u16 rlid = 0;
+ u8 sl, sc5, sc4_bit, svc_type;
+ bool sc4_set = has_sc4_bit(packet);
+
+ switch (packet->qp->ibqp.qp_type) {
+ case IB_QPT_UC:
+ rlid = qp->remote_ah_attr.dlid;
+ rqpn = qp->remote_qpn;
+ svc_type = IB_CC_SVCTYPE_UC;
+ break;
+ case IB_QPT_RC:
+ rlid = qp->remote_ah_attr.dlid;
+ rqpn = qp->remote_qpn;
+ svc_type = IB_CC_SVCTYPE_RC;
+ break;
+ case IB_QPT_SMI:
+ case IB_QPT_GSI:
+ case IB_QPT_UD:
+ svc_type = IB_CC_SVCTYPE_UD;
+ break;
+ default:
ibp->n_pkt_drops++;
+ return;
+ }
+
+ sc4_bit = sc4_set << 4;
+ sc5 = (be16_to_cpu(hdr->lrh[0]) >> 12) & 0xf;
+ sc5 |= sc4_bit;
+ sl = ibp->sc_to_sl[sc5];
+ lqpn = qp->ibqp.qp_num;
+
+ process_becn(ppd, sl, rlid, lqpn, rqpn, svc_type);
}
diff --git a/drivers/staging/rdma/hfi1/verbs.h b/drivers/staging/rdma/hfi1/verbs.h
index 041ad07ee699..72106e5362b9 100644
--- a/drivers/staging/rdma/hfi1/verbs.h
+++ b/drivers/staging/rdma/hfi1/verbs.h
@@ -120,9 +120,9 @@ struct hfi1_packet;
#define HFI1_VENDOR_IPG cpu_to_be16(0xFFA0)
-#define IB_BTH_REQ_ACK (1 << 31)
-#define IB_BTH_SOLICITED (1 << 23)
-#define IB_BTH_MIG_REQ (1 << 22)
+#define IB_BTH_REQ_ACK BIT(31)
+#define IB_BTH_SOLICITED BIT(23)
+#define IB_BTH_MIG_REQ BIT(22)
#define IB_GRH_VERSION 6
#define IB_GRH_VERSION_MASK 0xF
@@ -441,7 +441,8 @@ struct hfi1_qp {
struct hfi1_swqe *s_wq; /* send work queue */
struct hfi1_mmap_info *ip;
struct ahg_ib_header *s_hdr; /* next packet header to send */
- u8 s_sc; /* SC[0..4] for next packet */
+ /* sc for UC/RC QPs - based on ah for UD */
+ u8 s_sc;
unsigned long timeout_jiffies; /* computed from timeout */
enum ib_mtu path_mtu;
@@ -489,6 +490,7 @@ struct hfi1_qp {
u32 r_psn; /* expected rcv packet sequence number */
u32 r_msn; /* message sequence number */
+ u8 r_adefered; /* number of acks defered */
u8 r_state; /* opcode of last packet received */
u8 r_flags;
u8 r_head_ack_queue; /* index into s_ack_queue[] */
@@ -544,6 +546,16 @@ struct hfi1_qp {
};
/*
+ * This structure is used to hold commonly lookedup and computed values during
+ * the send engine progress.
+ */
+struct hfi1_pkt_state {
+ struct hfi1_ibdev *dev;
+ struct hfi1_ibport *ibp;
+ struct hfi1_pportdata *ppd;
+};
+
+/*
* Atomic bit definitions for r_aflags.
*/
#define HFI1_R_WRID_VALID 0
@@ -552,11 +564,13 @@ struct hfi1_qp {
/*
* Bit definitions for r_flags.
*/
-#define HFI1_R_REUSE_SGE 0x01
-#define HFI1_R_RDMAR_SEQ 0x02
-#define HFI1_R_RSP_NAK 0x04
-#define HFI1_R_RSP_SEND 0x08
-#define HFI1_R_COMM_EST 0x10
+#define HFI1_R_REUSE_SGE 0x01
+#define HFI1_R_RDMAR_SEQ 0x02
+/* defer ack until end of interrupt session */
+#define HFI1_R_RSP_DEFERED_ACK 0x04
+/* relay ack to send engine */
+#define HFI1_R_RSP_SEND 0x08
+#define HFI1_R_COMM_EST 0x10
/*
* Bit definitions for s_flags.
@@ -846,9 +860,8 @@ static inline int hfi1_send_ok(struct hfi1_qp *qp)
/*
* This must be called with s_lock held.
*/
-void hfi1_schedule_send(struct hfi1_qp *qp);
void hfi1_bad_pqkey(struct hfi1_ibport *ibp, __be16 trap_num, u32 key, u32 sl,
- u32 qp1, u32 qp2, __be16 lid1, __be16 lid2);
+ u32 qp1, u32 qp2, u16 lid1, u16 lid2);
void hfi1_cap_mask_chg(struct hfi1_ibport *ibp);
void hfi1_sys_guid_chg(struct hfi1_ibport *ibp);
void hfi1_node_desc_chg(struct hfi1_ibport *ibp);
@@ -927,8 +940,7 @@ int hfi1_mcast_tree_empty(struct hfi1_ibport *ibp);
struct verbs_txreq;
void hfi1_put_txreq(struct verbs_txreq *tx);
-int hfi1_verbs_send(struct hfi1_qp *qp, struct ahg_ib_header *ahdr,
- u32 hdrwords, struct hfi1_sge_state *ss, u32 len);
+int hfi1_verbs_send(struct hfi1_qp *qp, struct hfi1_pkt_state *ps);
void hfi1_copy_sge(struct hfi1_sge_state *ss, void *data, u32 length,
int release);
@@ -1069,8 +1081,6 @@ int hfi1_mmap(struct ib_ucontext *context, struct vm_area_struct *vma);
int hfi1_get_rwqe(struct hfi1_qp *qp, int wr_id_only);
-void hfi1_migrate_qp(struct hfi1_qp *qp);
-
int hfi1_ruc_check_hdr(struct hfi1_ibport *ibp, struct hfi1_ib_header *hdr,
int has_grh, struct hfi1_qp *qp, u32 bth0);
@@ -1101,13 +1111,11 @@ void hfi1_ib_rcv(struct hfi1_packet *packet);
unsigned hfi1_get_npkeys(struct hfi1_devdata *);
-int hfi1_verbs_send_dma(struct hfi1_qp *qp, struct ahg_ib_header *hdr,
- u32 hdrwords, struct hfi1_sge_state *ss, u32 len,
- u32 plen, u32 dwords, u64 pbc);
+int hfi1_verbs_send_dma(struct hfi1_qp *qp, struct hfi1_pkt_state *ps,
+ u64 pbc);
-int hfi1_verbs_send_pio(struct hfi1_qp *qp, struct ahg_ib_header *hdr,
- u32 hdrwords, struct hfi1_sge_state *ss, u32 len,
- u32 plen, u32 dwords, u64 pbc);
+int hfi1_verbs_send_pio(struct hfi1_qp *qp, struct hfi1_pkt_state *ps,
+ u64 pbc);
struct send_context *qp_to_send_context(struct hfi1_qp *qp, u8 sc5);
diff --git a/drivers/staging/rdma/ipath/ipath_file_ops.c b/drivers/staging/rdma/ipath/ipath_file_ops.c
index 13c3cd11ab92..6187b848b3ca 100644
--- a/drivers/staging/rdma/ipath/ipath_file_ops.c
+++ b/drivers/staging/rdma/ipath/ipath_file_ops.c
@@ -917,15 +917,15 @@ static int ipath_create_user_egr(struct ipath_portdata *pd)
chunk = pd->port_rcvegrbuf_chunks;
egrperchunk = pd->port_rcvegrbufs_perchunk;
size = pd->port_rcvegrbuf_size;
- pd->port_rcvegrbuf = kmalloc(chunk * sizeof(pd->port_rcvegrbuf[0]),
- GFP_KERNEL);
+ pd->port_rcvegrbuf = kmalloc_array(chunk, sizeof(pd->port_rcvegrbuf[0]),
+ GFP_KERNEL);
if (!pd->port_rcvegrbuf) {
ret = -ENOMEM;
goto bail;
}
pd->port_rcvegrbuf_phys =
- kmalloc(chunk * sizeof(pd->port_rcvegrbuf_phys[0]),
- GFP_KERNEL);
+ kmalloc_array(chunk, sizeof(pd->port_rcvegrbuf_phys[0]),
+ GFP_KERNEL);
if (!pd->port_rcvegrbuf_phys) {
ret = -ENOMEM;
goto bail_rcvegrbuf;
diff --git a/drivers/staging/rtl8188eu/core/rtw_ap.c b/drivers/staging/rtl8188eu/core/rtw_ap.c
index 3cdb40fea5ee..e5d29fe9d446 100644
--- a/drivers/staging/rtl8188eu/core/rtw_ap.c
+++ b/drivers/staging/rtl8188eu/core/rtw_ap.c
@@ -1240,11 +1240,6 @@ int rtw_acl_remove_sta(struct adapter *padapter, u8 *addr)
return 0;
}
-static void update_bcn_fixed_ie(struct adapter *padapter)
-{
- DBG_88E("%s\n", __func__);
-}
-
static void update_bcn_erpinfo_ie(struct adapter *padapter)
{
struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
@@ -1279,31 +1274,6 @@ static void update_bcn_erpinfo_ie(struct adapter *padapter)
}
}
-static void update_bcn_htcap_ie(struct adapter *padapter)
-{
- DBG_88E("%s\n", __func__);
-}
-
-static void update_bcn_htinfo_ie(struct adapter *padapter)
-{
- DBG_88E("%s\n", __func__);
-}
-
-static void update_bcn_rsn_ie(struct adapter *padapter)
-{
- DBG_88E("%s\n", __func__);
-}
-
-static void update_bcn_wpa_ie(struct adapter *padapter)
-{
- DBG_88E("%s\n", __func__);
-}
-
-static void update_bcn_wmm_ie(struct adapter *padapter)
-{
- DBG_88E("%s\n", __func__);
-}
-
static void update_bcn_wps_ie(struct adapter *padapter)
{
u8 *pwps_ie = NULL, *pwps_ie_src;
@@ -1354,22 +1324,12 @@ static void update_bcn_wps_ie(struct adapter *padapter)
kfree(pbackup_remainder_ie);
}
-static void update_bcn_p2p_ie(struct adapter *padapter)
-{
-}
-
static void update_bcn_vendor_spec_ie(struct adapter *padapter, u8 *oui)
{
DBG_88E("%s\n", __func__);
- if (!memcmp(RTW_WPA_OUI, oui, 4))
- update_bcn_wpa_ie(padapter);
- else if (!memcmp(WMM_OUI, oui, 4))
- update_bcn_wmm_ie(padapter);
- else if (!memcmp(WPS_OUI, oui, 4))
+ if (!memcmp(WPS_OUI, oui, 4))
update_bcn_wps_ie(padapter);
- else if (!memcmp(P2P_OUI, oui, 4))
- update_bcn_p2p_ie(padapter);
else
DBG_88E("unknown OUI type!\n");
}
@@ -1391,24 +1351,12 @@ void update_beacon(struct adapter *padapter, u8 ie_id, u8 *oui, u8 tx)
spin_lock_bh(&pmlmepriv->bcn_update_lock);
switch (ie_id) {
- case 0xFF:
- update_bcn_fixed_ie(padapter);/* 8: TimeStamp, 2: Beacon Interval 2:Capability */
- break;
case _TIM_IE_:
update_BCNTIM(padapter);
break;
case _ERPINFO_IE_:
update_bcn_erpinfo_ie(padapter);
break;
- case _HT_CAPABILITY_IE_:
- update_bcn_htcap_ie(padapter);
- break;
- case _RSN_IE_2_:
- update_bcn_rsn_ie(padapter);
- break;
- case _HT_ADD_INFO_IE_:
- update_bcn_htinfo_ie(padapter);
- break;
case _VENDOR_SPECIFIC_IE_:
update_bcn_vendor_spec_ie(padapter, oui);
break;
diff --git a/drivers/staging/rtl8188eu/core/rtw_cmd.c b/drivers/staging/rtl8188eu/core/rtw_cmd.c
index 9b7026e7d55b..433b926ceae7 100644
--- a/drivers/staging/rtl8188eu/core/rtw_cmd.c
+++ b/drivers/staging/rtl8188eu/core/rtw_cmd.c
@@ -201,23 +201,20 @@ _next:
if (rtw_cmd_filter(pcmdpriv, pcmd) == _FAIL) {
pcmd->res = H2C_DROPPED;
- goto post_process;
- }
-
- if (pcmd->cmdcode < ARRAY_SIZE(wlancmds)) {
- cmd_hdl = wlancmds[pcmd->cmdcode].h2cfuns;
-
- if (cmd_hdl) {
- ret = cmd_hdl(pcmd->padapter, pcmd->parmbuf);
- pcmd->res = ret;
- }
} else {
- pcmd->res = H2C_PARAMETERS_ERROR;
- }
+ if (pcmd->cmdcode < ARRAY_SIZE(wlancmds)) {
+ cmd_hdl = wlancmds[pcmd->cmdcode].h2cfuns;
- cmd_hdl = NULL;
+ if (cmd_hdl) {
+ ret = cmd_hdl(pcmd->padapter, pcmd->parmbuf);
+ pcmd->res = ret;
+ }
+ } else {
+ pcmd->res = H2C_PARAMETERS_ERROR;
+ }
-post_process:
+ cmd_hdl = NULL;
+ }
/* call callback function for post-processed */
if (pcmd->cmdcode < ARRAY_SIZE(rtw_cmd_callback)) {
@@ -242,15 +239,11 @@ post_process:
pcmdpriv->cmdthd_running = false;
/* free all cmd_obj resources */
- do {
- pcmd = rtw_dequeue_cmd(&pcmdpriv->cmd_queue);
- if (pcmd == NULL)
- break;
-
+ while ((pcmd = rtw_dequeue_cmd(&pcmdpriv->cmd_queue))) {
/* DBG_88E("%s: leaving... drop cmdcode:%u\n", __func__, pcmd->cmdcode); */
rtw_free_cmd_obj(pcmd);
- } while (1);
+ }
up(&pcmdpriv->terminate_cmdthread_sema);
@@ -570,31 +563,19 @@ u8 rtw_setopmode_cmd(struct adapter *padapter, enum ndis_802_11_network_infra n
struct setopmode_parm *psetop;
struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
- u8 res = _SUCCESS;
-
ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
- if (ph2c == NULL) {
- res = false;
- goto exit;
- }
psetop = kzalloc(sizeof(struct setopmode_parm), GFP_KERNEL);
-
- if (psetop == NULL) {
+ if (!ph2c || !psetop) {
kfree(ph2c);
- res = false;
- goto exit;
+ kfree(psetop);
+ return false;
}
init_h2fwcmd_w_parm_no_rsp(ph2c, psetop, _SetOpMode_CMD_);
psetop->mode = (u8)networktype;
- res = rtw_enqueue_cmd(pcmdpriv, ph2c);
-
-exit:
-
-
- return res;
+ return rtw_enqueue_cmd(pcmdpriv, ph2c);
}
u8 rtw_setstakey_cmd(struct adapter *padapter, u8 *psta, u8 unicast_key)
@@ -607,28 +588,16 @@ u8 rtw_setstakey_cmd(struct adapter *padapter, u8 *psta, u8 unicast_key)
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
struct security_priv *psecuritypriv = &padapter->securitypriv;
struct sta_info *sta = (struct sta_info *)psta;
- u8 res = _SUCCESS;
-
ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
- if (ph2c == NULL) {
- res = _FAIL;
- goto exit;
- }
-
psetstakey_para = kzalloc(sizeof(struct set_stakey_parm), GFP_KERNEL);
- if (psetstakey_para == NULL) {
- kfree(ph2c);
- res = _FAIL;
- goto exit;
- }
-
psetstakey_rsp = kzalloc(sizeof(struct set_stakey_rsp), GFP_KERNEL);
- if (psetstakey_rsp == NULL) {
+
+ if (!ph2c || !psetstakey_para || !psetstakey_rsp) {
kfree(ph2c);
kfree(psetstakey_para);
- res = _FAIL;
- goto exit;
+ kfree(psetstakey_rsp);
+ return _FAIL;
}
init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, _SetStaKey_CMD_);
@@ -650,12 +619,7 @@ u8 rtw_setstakey_cmd(struct adapter *padapter, u8 *psta, u8 unicast_key)
/* jeff: set this because at least sw key is ready */
padapter->securitypriv.busetkipkey = true;
- res = rtw_enqueue_cmd(pcmdpriv, ph2c);
-
-exit:
-
-
- return res;
+ return rtw_enqueue_cmd(pcmdpriv, ph2c);
}
u8 rtw_clearstakey_cmd(struct adapter *padapter, u8 *psta, u8 entry, u8 enqueue)
@@ -1086,31 +1050,19 @@ u8 rtw_ps_cmd(struct adapter *padapter)
struct drvextra_cmd_parm *pdrvextra_cmd_parm;
struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
- u8 res = _SUCCESS;
-
ppscmd = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
- if (ppscmd == NULL) {
- res = _FAIL;
- goto exit;
- }
-
pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC);
- if (pdrvextra_cmd_parm == NULL) {
+ if (!ppscmd || !pdrvextra_cmd_parm) {
kfree(ppscmd);
- res = _FAIL;
- goto exit;
+ kfree(pdrvextra_cmd_parm);
+ return _FAIL;
}
pdrvextra_cmd_parm->ec_id = POWER_SAVING_CTRL_WK_CID;
pdrvextra_cmd_parm->pbuf = NULL;
init_h2fwcmd_w_parm_no_rsp(ppscmd, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra));
- res = rtw_enqueue_cmd(pcmdpriv, ppscmd);
-
-exit:
-
-
- return res;
+ return rtw_enqueue_cmd(pcmdpriv, ppscmd);
}
#ifdef CONFIG_88EU_AP_MODE
diff --git a/drivers/staging/rtl8188eu/core/rtw_efuse.c b/drivers/staging/rtl8188eu/core/rtw_efuse.c
index eb894233a785..2320fb11af24 100644
--- a/drivers/staging/rtl8188eu/core/rtw_efuse.c
+++ b/drivers/staging/rtl8188eu/core/rtw_efuse.c
@@ -224,7 +224,7 @@ static void efuse_read_phymap_from_txpktbuf(
)
{
u16 dbg_addr = 0;
- u32 start = 0, passing_time = 0;
+ unsigned long start = 0;
u8 reg_0x143 = 0;
u32 lo32 = 0, hi32 = 0;
u16 len = 0, count = 0;
@@ -248,7 +248,7 @@ static void efuse_read_phymap_from_txpktbuf(
usb_write8(adapter, REG_TXPKTBUF_DBG, 0);
start = jiffies;
while (!(reg_0x143 = usb_read8(adapter, REG_TXPKTBUF_DBG)) &&
- (passing_time = rtw_get_passing_time_ms(start)) < 1000) {
+ jiffies_to_msecs(jiffies - start) < 1000) {
DBG_88E("%s polling reg_0x143:0x%02x, reg_0x106:0x%02x\n", __func__, reg_0x143, usb_read8(adapter, 0x106));
usleep_range(1000, 2000);
}
diff --git a/drivers/staging/rtl8188eu/core/rtw_ioctl_set.c b/drivers/staging/rtl8188eu/core/rtw_ioctl_set.c
index 22f5b45f5f7f..cf60717a6c19 100644
--- a/drivers/staging/rtl8188eu/core/rtw_ioctl_set.c
+++ b/drivers/staging/rtl8188eu/core/rtw_ioctl_set.c
@@ -27,13 +27,6 @@
extern void indicate_wx_scan_complete_event(struct adapter *padapter);
-#define IS_MAC_ADDRESS_BROADCAST(addr) \
-(\
- ((addr[0] == 0xff) && (addr[1] == 0xff) && \
- (addr[2] == 0xff) && (addr[3] == 0xff) && \
- (addr[4] == 0xff) && (addr[5] == 0xff)) ? true : false \
-)
-
u8 rtw_do_join(struct adapter *padapter)
{
struct list_head *plist, *phead;
diff --git a/drivers/staging/rtl8188eu/core/rtw_mlme.c b/drivers/staging/rtl8188eu/core/rtw_mlme.c
index c1b82f71b682..abab854e6889 100644
--- a/drivers/staging/rtl8188eu/core/rtw_mlme.c
+++ b/drivers/staging/rtl8188eu/core/rtw_mlme.c
@@ -163,7 +163,8 @@ exit:
static void _rtw_free_network(struct mlme_priv *pmlmepriv, struct wlan_network *pnetwork, u8 isfreeall)
{
- u32 curr_time, delta_time;
+ unsigned long curr_time;
+ u32 delta_time;
u32 lifetime = SCANQUEUE_LIFETIME;
struct __queue *free_queue = &(pmlmepriv->free_bss_pool);
@@ -272,7 +273,7 @@ int rtw_if_up(struct adapter *padapter)
void rtw_generate_random_ibss(u8 *pibss)
{
- u32 curtime = jiffies;
+ unsigned long curtime = jiffies;
pibss[0] = 0x02; /* in ad-hoc mode bit1 must set to 1 */
pibss[1] = 0x11;
@@ -365,20 +366,13 @@ struct wlan_network *rtw_get_oldest_wlan_network(struct __queue *scanned_queue)
phead = get_list_head(scanned_queue);
- plist = phead->next;
-
- while (1) {
- if (phead == plist)
- break;
-
+ for (plist = phead->next; plist != phead; plist = plist->next) {
pwlan = container_of(plist, struct wlan_network, list);
if (!pwlan->fixed) {
if (oldest == NULL || time_after(oldest->last_scanned, pwlan->last_scanned))
oldest = pwlan;
}
-
- plist = plist->next;
}
return oldest;
}
@@ -878,14 +872,14 @@ inline void rtw_indicate_scan_done(struct adapter *padapter, bool aborted)
void rtw_scan_abort(struct adapter *adapter)
{
- u32 start;
+ unsigned long start;
struct mlme_priv *pmlmepriv = &(adapter->mlmepriv);
struct mlme_ext_priv *pmlmeext = &(adapter->mlmeextpriv);
start = jiffies;
pmlmeext->scan_abort = true;
while (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) &&
- rtw_get_passing_time_ms(start) <= 200) {
+ jiffies_to_msecs(jiffies - start) <= 200) {
if (adapter->bDriverStopped || adapter->bSurpriseRemoved)
break;
DBG_88E(FUNC_NDEV_FMT"fw_state=_FW_UNDER_SURVEY!\n", FUNC_NDEV_ARG(adapter->pnetdev));
@@ -1474,6 +1468,7 @@ static int rtw_check_join_candidate(struct mlme_priv *pmlmepriv
, struct wlan_network **candidate, struct wlan_network *competitor)
{
int updated = false;
+ unsigned long since_scan;
struct adapter *adapter = container_of(pmlmepriv, struct adapter, mlmepriv);
@@ -1494,7 +1489,8 @@ static int rtw_check_join_candidate(struct mlme_priv *pmlmepriv
goto exit;
if (pmlmepriv->to_roaming) {
- if (rtw_get_passing_time_ms((u32)competitor->last_scanned) >= RTW_SCAN_RESULT_EXPIRE ||
+ since_scan = jiffies - competitor->last_scanned;
+ if (jiffies_to_msecs(since_scan) >= RTW_SCAN_RESULT_EXPIRE ||
is_same_ess(&competitor->network, &pmlmepriv->cur_network.network) == false)
goto exit;
}
diff --git a/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c b/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c
index d900546b672f..3eca6874b6df 100644
--- a/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c
+++ b/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c
@@ -609,7 +609,7 @@ static void issue_probersp(struct adapter *padapter, unsigned char *da)
return;
}
-static int _issue_probereq(struct adapter *padapter, struct ndis_802_11_ssid *pssid, u8 *da, int wait_ack)
+static int issue_probereq(struct adapter *padapter, struct ndis_802_11_ssid *pssid, u8 *da, bool wait_ack)
{
int ret = _FAIL;
struct xmit_frame *pmgntframe;
@@ -702,22 +702,16 @@ exit:
return ret;
}
-static inline void issue_probereq(struct adapter *padapter,
- struct ndis_802_11_ssid *pssid, u8 *da)
-{
- _issue_probereq(padapter, pssid, da, false);
-}
-
static int issue_probereq_ex(struct adapter *padapter,
struct ndis_802_11_ssid *pssid, u8 *da,
int try_cnt, int wait_ms)
{
int ret;
int i = 0;
- u32 start = jiffies;
+ unsigned long start = jiffies;
do {
- ret = _issue_probereq(padapter, pssid, da, wait_ms > 0 ? true : false);
+ ret = issue_probereq(padapter, pssid, da, wait_ms > 0 ? true : false);
i++;
@@ -738,11 +732,13 @@ static int issue_probereq_ex(struct adapter *padapter,
if (da)
DBG_88E(FUNC_ADPT_FMT" to %pM, ch:%u%s, %d/%d in %u ms\n",
FUNC_ADPT_ARG(padapter), da, rtw_get_oper_ch(padapter),
- ret == _SUCCESS ? ", acked" : "", i, try_cnt, rtw_get_passing_time_ms(start));
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt,
+ jiffies_to_msecs(jiffies - start));
else
DBG_88E(FUNC_ADPT_FMT", ch:%u%s, %d/%d in %u ms\n",
FUNC_ADPT_ARG(padapter), rtw_get_oper_ch(padapter),
- ret == _SUCCESS ? ", acked" : "", i, try_cnt, rtw_get_passing_time_ms(start));
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt,
+ jiffies_to_msecs(jiffies - start));
}
exit:
return ret;
@@ -1299,7 +1295,7 @@ int issue_nulldata(struct adapter *padapter, unsigned char *da, unsigned int pow
{
int ret;
int i = 0;
- u32 start = jiffies;
+ unsigned long start = jiffies;
struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
struct wlan_bssid_ex *pnetwork = &(pmlmeinfo->network);
@@ -1329,11 +1325,13 @@ int issue_nulldata(struct adapter *padapter, unsigned char *da, unsigned int pow
if (da)
DBG_88E(FUNC_ADPT_FMT" to %pM, ch:%u%s, %d/%d in %u ms\n",
FUNC_ADPT_ARG(padapter), da, rtw_get_oper_ch(padapter),
- ret == _SUCCESS ? ", acked" : "", i, try_cnt, rtw_get_passing_time_ms(start));
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt,
+ jiffies_to_msecs(jiffies - start));
else
DBG_88E(FUNC_ADPT_FMT", ch:%u%s, %d/%d in %u ms\n",
FUNC_ADPT_ARG(padapter), rtw_get_oper_ch(padapter),
- ret == _SUCCESS ? ", acked" : "", i, try_cnt, rtw_get_passing_time_ms(start));
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt,
+ jiffies_to_msecs(jiffies - start));
}
exit:
return ret;
@@ -1424,7 +1422,7 @@ int issue_qos_nulldata(struct adapter *padapter, unsigned char *da, u16 tid, int
{
int ret;
int i = 0;
- u32 start = jiffies;
+ unsigned long start = jiffies;
struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
struct wlan_bssid_ex *pnetwork = &(pmlmeinfo->network);
@@ -1454,11 +1452,13 @@ int issue_qos_nulldata(struct adapter *padapter, unsigned char *da, u16 tid, int
if (da)
DBG_88E(FUNC_ADPT_FMT" to %pM, ch:%u%s, %d/%d in %u ms\n",
FUNC_ADPT_ARG(padapter), da, rtw_get_oper_ch(padapter),
- ret == _SUCCESS ? ", acked" : "", i, try_cnt, rtw_get_passing_time_ms(start));
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt,
+ jiffies_to_msecs(jiffies - start));
else
DBG_88E(FUNC_ADPT_FMT", ch:%u%s, %d/%d in %u ms\n",
FUNC_ADPT_ARG(padapter), rtw_get_oper_ch(padapter),
- ret == _SUCCESS ? ", acked" : "", i, try_cnt, rtw_get_passing_time_ms(start));
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt,
+ jiffies_to_msecs(jiffies - start));
}
exit:
return ret;
@@ -1536,7 +1536,7 @@ static int issue_deauth_ex(struct adapter *padapter, u8 *da,
{
int ret;
int i = 0;
- u32 start = jiffies;
+ unsigned long start = jiffies;
do {
ret = _issue_deauth(padapter, da, reason, wait_ms > 0 ? true : false);
@@ -1559,11 +1559,13 @@ static int issue_deauth_ex(struct adapter *padapter, u8 *da,
if (da)
DBG_88E(FUNC_ADPT_FMT" to %pM, ch:%u%s, %d/%d in %u ms\n",
FUNC_ADPT_ARG(padapter), da, rtw_get_oper_ch(padapter),
- ret == _SUCCESS ? ", acked" : "", i, try_cnt, rtw_get_passing_time_ms(start));
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt,
+ jiffies_to_msecs(jiffies - start));
else
DBG_88E(FUNC_ADPT_FMT", ch:%u%s, %d/%d in %u ms\n",
FUNC_ADPT_ARG(padapter), rtw_get_oper_ch(padapter),
- ret == _SUCCESS ? ", acked" : "", i, try_cnt, rtw_get_passing_time_ms(start));
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt,
+ jiffies_to_msecs(jiffies - start));
}
exit:
return ret;
@@ -1965,7 +1967,7 @@ unsigned int send_beacon(struct adapter *padapter)
int issue = 0;
int poll = 0;
- u32 start = jiffies;
+ unsigned long start = jiffies;
rtw_hal_set_hwreg(padapter, HW_VAR_BCN_VALID, NULL);
do {
@@ -1981,13 +1983,16 @@ unsigned int send_beacon(struct adapter *padapter)
if (padapter->bSurpriseRemoved || padapter->bDriverStopped)
return _FAIL;
if (!bxmitok) {
- DBG_88E("%s fail! %u ms\n", __func__, rtw_get_passing_time_ms(start));
+ DBG_88E("%s fail! %u ms\n", __func__,
+ jiffies_to_msecs(jiffies - start));
return _FAIL;
} else {
- u32 passing_time = rtw_get_passing_time_ms(start);
+ u32 passing_time = jiffies_to_msecs(jiffies - start);
if (passing_time > 100 || issue > 3)
- DBG_88E("%s success, issue:%d, poll:%d, %u ms\n", __func__, issue, poll, rtw_get_passing_time_ms(start));
+ DBG_88E("%s success, issue:%d, poll:%d, %u ms\n",
+ __func__, issue, poll,
+ jiffies_to_msecs(jiffies - start));
return _SUCCESS;
}
}
@@ -2029,24 +2034,28 @@ static void site_survey(struct adapter *padapter)
for (i = 0; i < RTW_SSID_SCAN_AMOUNT; i++) {
if (pmlmeext->sitesurvey_res.ssid[i].SsidLength) {
/* todo: to issue two probe req??? */
- issue_probereq(padapter, &(pmlmeext->sitesurvey_res.ssid[i]), NULL);
+ issue_probereq(padapter,
+ &(pmlmeext->sitesurvey_res.ssid[i]),
+ NULL, false);
/* msleep(SURVEY_TO>>1); */
- issue_probereq(padapter, &(pmlmeext->sitesurvey_res.ssid[i]), NULL);
+ issue_probereq(padapter,
+ &(pmlmeext->sitesurvey_res.ssid[i]),
+ NULL, false);
}
}
if (pmlmeext->sitesurvey_res.scan_mode == SCAN_ACTIVE) {
/* todo: to issue two probe req??? */
- issue_probereq(padapter, NULL, NULL);
+ issue_probereq(padapter, NULL, NULL, false);
/* msleep(SURVEY_TO>>1); */
- issue_probereq(padapter, NULL, NULL);
+ issue_probereq(padapter, NULL, NULL, false);
}
if (pmlmeext->sitesurvey_res.scan_mode == SCAN_ACTIVE) {
/* todo: to issue two probe req??? */
- issue_probereq(padapter, NULL, NULL);
+ issue_probereq(padapter, NULL, NULL, false);
/* msleep(SURVEY_TO>>1); */
- issue_probereq(padapter, NULL, NULL);
+ issue_probereq(padapter, NULL, NULL, false);
}
}
@@ -4820,9 +4829,18 @@ void linked_status_chk(struct adapter *padapter)
} else {
if (rx_chk != _SUCCESS) {
if (pmlmeext->retry == 0) {
- issue_probereq(padapter, &pmlmeinfo->network.Ssid, pmlmeinfo->network.MacAddress);
- issue_probereq(padapter, &pmlmeinfo->network.Ssid, pmlmeinfo->network.MacAddress);
- issue_probereq(padapter, &pmlmeinfo->network.Ssid, pmlmeinfo->network.MacAddress);
+ issue_probereq(padapter,
+ &pmlmeinfo->network.Ssid,
+ pmlmeinfo->network.MacAddress,
+ false);
+ issue_probereq(padapter,
+ &pmlmeinfo->network.Ssid,
+ pmlmeinfo->network.MacAddress,
+ false);
+ issue_probereq(padapter,
+ &pmlmeinfo->network.Ssid,
+ pmlmeinfo->network.MacAddress,
+ false);
}
}
diff --git a/drivers/staging/rtl8188eu/core/rtw_pwrctrl.c b/drivers/staging/rtl8188eu/core/rtw_pwrctrl.c
index 9765946466ab..5e1ef9fdcf47 100644
--- a/drivers/staging/rtl8188eu/core/rtw_pwrctrl.c
+++ b/drivers/staging/rtl8188eu/core/rtw_pwrctrl.c
@@ -348,7 +348,7 @@ void rtw_set_rpwm(struct adapter *padapter, u8 pslv)
static u8 PS_RDY_CHECK(struct adapter *padapter)
{
- u32 curr_time, delta_time;
+ unsigned long curr_time, delta_time;
struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
@@ -418,7 +418,7 @@ void rtw_set_ps_mode(struct adapter *padapter, u8 ps_mode, u8 smart_ps, u8 bcn_a
*/
s32 LPS_RF_ON_check(struct adapter *padapter, u32 delay_ms)
{
- u32 start_time;
+ unsigned long start_time;
u8 bAwake = false;
s32 err = 0;
@@ -435,7 +435,7 @@ s32 LPS_RF_ON_check(struct adapter *padapter, u32 delay_ms)
break;
}
- if (rtw_get_passing_time_ms(start_time) > delay_ms) {
+ if (jiffies_to_msecs(jiffies - start_time) > delay_ms) {
err = -1;
DBG_88E("%s: Wait for FW LPS leave more than %u ms!!!\n", __func__, delay_ms);
break;
@@ -561,24 +561,24 @@ int _rtw_pwr_wakeup(struct adapter *padapter, u32 ips_deffer_ms, const char *cal
struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
unsigned long expires;
+ unsigned long start;
int ret = _SUCCESS;
expires = jiffies + msecs_to_jiffies(ips_deffer_ms);
if (time_before(pwrpriv->ips_deny_time, expires))
pwrpriv->ips_deny_time = jiffies + msecs_to_jiffies(ips_deffer_ms);
-{
- u32 start = jiffies;
+ start = jiffies;
if (pwrpriv->ps_processing) {
DBG_88E("%s wait ps_processing...\n", __func__);
- while (pwrpriv->ps_processing && rtw_get_passing_time_ms(start) <= 3000)
+ while (pwrpriv->ps_processing &&
+ jiffies_to_msecs(jiffies - start) <= 3000)
usleep_range(1000, 3000);
if (pwrpriv->ps_processing)
DBG_88E("%s wait ps_processing timeout\n", __func__);
else
DBG_88E("%s wait ps_processing done\n", __func__);
}
-}
/* System suspend is not allowed to wakeup */
if ((!pwrpriv->bInternalAutoSuspend) && (pwrpriv->bInSuspend)) {
diff --git a/drivers/staging/rtl8188eu/core/rtw_xmit.c b/drivers/staging/rtl8188eu/core/rtw_xmit.c
index cabb810369bd..e778132b73dc 100644
--- a/drivers/staging/rtl8188eu/core/rtw_xmit.c
+++ b/drivers/staging/rtl8188eu/core/rtw_xmit.c
@@ -641,7 +641,7 @@ static s32 xmitframe_addmic(struct adapter *padapter, struct xmit_frame *pxmitfr
if (pattrib->psta)
stainfo = pattrib->psta;
else
- stainfo = rtw_get_stainfo(&padapter->stapriv , &pattrib->ra[0]);
+ stainfo = rtw_get_stainfo(&padapter->stapriv, &pattrib->ra[0]);
hw_hdr_offset = TXDESC_SIZE + (pxmitframe->pkt_offset * PACKET_OFFSET_SZ);
@@ -1702,12 +1702,12 @@ u32 rtw_get_ff_hwaddr(struct xmit_frame *pxmitframe)
return addr;
}
-static void do_queue_select(struct adapter *padapter, struct pkt_attrib *pattrib)
+static void do_queue_select(struct adapter *padapter, struct pkt_attrib *pattrib)
{
u8 qsel;
qsel = pattrib->priority;
- RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("### do_queue_select priority=%d , qsel = %d\n", pattrib->priority , qsel));
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("### do_queue_select priority=%d , qsel = %d\n", pattrib->priority, qsel));
pattrib->qsel = qsel;
}
@@ -2186,11 +2186,6 @@ void rtw_sctx_done_err(struct submit_ctx **sctx, int status)
}
}
-void rtw_sctx_done(struct submit_ctx **sctx)
-{
- rtw_sctx_done_err(sctx, RTW_SCTX_DONE_SUCCESS);
-}
-
int rtw_ack_tx_wait(struct xmit_priv *pxmitpriv, u32 timeout_ms)
{
struct submit_ctx *pack_tx_ops = &pxmitpriv->ack_tx_ops;
diff --git a/drivers/staging/rtl8188eu/hal/fw.c b/drivers/staging/rtl8188eu/hal/fw.c
index 23aa6d37acac..4d72537644b3 100644
--- a/drivers/staging/rtl8188eu/hal/fw.c
+++ b/drivers/staging/rtl8188eu/hal/fw.c
@@ -58,43 +58,31 @@ static void _rtl88e_fw_block_write(struct adapter *adapt,
const u8 *buffer, u32 size)
{
u32 blk_sz = sizeof(u32);
- u8 *buf_ptr = (u8 *)buffer;
- u32 *pu4BytePtr = (u32 *)buffer;
- u32 i, offset, blk_cnt, remain;
+ const u8 *byte_buffer;
+ const u32 *dword_buffer = (u32 *)buffer;
+ u32 i, write_address, blk_cnt, remain;
blk_cnt = size / blk_sz;
remain = size % blk_sz;
- for (i = 0; i < blk_cnt; i++) {
- offset = i * blk_sz;
- usb_write32(adapt, (FW_8192C_START_ADDRESS + offset),
- *(pu4BytePtr + i));
- }
+ write_address = FW_8192C_START_ADDRESS;
- if (remain) {
- offset = blk_cnt * blk_sz;
- buf_ptr += offset;
- for (i = 0; i < remain; i++) {
- usb_write8(adapt, (FW_8192C_START_ADDRESS +
- offset + i), *(buf_ptr + i));
- }
- }
+ for (i = 0; i < blk_cnt; i++, write_address += blk_sz)
+ usb_write32(adapt, write_address, dword_buffer[i]);
+
+ byte_buffer = buffer + blk_cnt * blk_sz;
+ for (i = 0; i < remain; i++, write_address++)
+ usb_write8(adapt, write_address, byte_buffer[i]);
}
static void _rtl88e_fill_dummy(u8 *pfwbuf, u32 *pfwlen)
{
- u32 fwlen = *pfwlen;
- u8 remain = (u8)(fwlen % 4);
-
- remain = (remain == 0) ? 0 : (4 - remain);
+ u32 i;
- while (remain > 0) {
- pfwbuf[fwlen] = 0;
- fwlen++;
- remain--;
- }
+ for (i = *pfwlen; i < roundup(*pfwlen, 4); i++)
+ pfwbuf[i] = 0;
- *pfwlen = fwlen;
+ *pfwlen = i;
}
static void _rtl88e_fw_page_write(struct adapter *adapt,
diff --git a/drivers/staging/rtl8188eu/hal/hal_com.c b/drivers/staging/rtl8188eu/hal/hal_com.c
index 38e9fdc312d3..3871cda2eec2 100644
--- a/drivers/staging/rtl8188eu/hal/hal_com.c
+++ b/drivers/staging/rtl8188eu/hal/hal_com.c
@@ -32,19 +32,19 @@ void dump_chip_info(struct HAL_VERSION chip_vers)
char buf[128];
cnt += sprintf((buf+cnt), "Chip Version Info: CHIP_8188E_");
- cnt += sprintf((buf+cnt), "%s_", IS_NORMAL_CHIP(chip_vers) ?
+ cnt += sprintf((buf+cnt), "%s_", chip_vers.ChipType == NORMAL_CHIP ?
"Normal_Chip" : "Test_Chip");
- cnt += sprintf((buf+cnt), "%s_", IS_CHIP_VENDOR_TSMC(chip_vers) ?
+ cnt += sprintf((buf+cnt), "%s_", chip_vers.VendorType == CHIP_VENDOR_TSMC ?
"TSMC" : "UMC");
- if (IS_A_CUT(chip_vers))
+ if (chip_vers.CUTVersion == A_CUT_VERSION)
cnt += sprintf((buf+cnt), "A_CUT_");
- else if (IS_B_CUT(chip_vers))
+ else if (chip_vers.CUTVersion == B_CUT_VERSION)
cnt += sprintf((buf+cnt), "B_CUT_");
- else if (IS_C_CUT(chip_vers))
+ else if (chip_vers.CUTVersion == C_CUT_VERSION)
cnt += sprintf((buf+cnt), "C_CUT_");
- else if (IS_D_CUT(chip_vers))
+ else if (chip_vers.CUTVersion == D_CUT_VERSION)
cnt += sprintf((buf+cnt), "D_CUT_");
- else if (IS_E_CUT(chip_vers))
+ else if (chip_vers.CUTVersion == E_CUT_VERSION)
cnt += sprintf((buf+cnt), "E_CUT_");
else
cnt += sprintf((buf+cnt), "UNKNOWN_CUT(%d)_",
diff --git a/drivers/staging/rtl8188eu/hal/rtl8188e_dm.c b/drivers/staging/rtl8188eu/hal/rtl8188e_dm.c
index fca590949409..199a77acd7a9 100644
--- a/drivers/staging/rtl8188eu/hal/rtl8188e_dm.c
+++ b/drivers/staging/rtl8188eu/hal/rtl8188e_dm.c
@@ -67,7 +67,7 @@ static void Init_ODM_ComInfo_88E(struct adapter *Adapter)
ODM_CmnInfoInit(dm_odm, ODM_CMNINFO_FAB_VER, fab_ver);
ODM_CmnInfoInit(dm_odm, ODM_CMNINFO_CUT_VER, cut_ver);
- ODM_CmnInfoInit(dm_odm, ODM_CMNINFO_MP_TEST_CHIP, IS_NORMAL_CHIP(hal_data->VersionID));
+ ODM_CmnInfoInit(dm_odm, ODM_CMNINFO_MP_TEST_CHIP, hal_data->VersionID.ChipType == NORMAL_CHIP ? true : false);
ODM_CmnInfoInit(dm_odm, ODM_CMNINFO_PATCH_ID, hal_data->CustomerID);
ODM_CmnInfoInit(dm_odm, ODM_CMNINFO_BWIFI_TEST, Adapter->registrypriv.wifi_spec);
diff --git a/drivers/staging/rtl8188eu/hal/rtl8188e_hal_init.c b/drivers/staging/rtl8188eu/hal/rtl8188e_hal_init.c
index e3e5d6f5d4f9..2592bc298f84 100644
--- a/drivers/staging/rtl8188eu/hal/rtl8188e_hal_init.c
+++ b/drivers/staging/rtl8188eu/hal/rtl8188e_hal_init.c
@@ -53,7 +53,7 @@ s32 iol_execute(struct adapter *padapter, u8 control)
{
s32 status = _FAIL;
u8 reg_0x88 = 0;
- u32 start = 0, passing_time = 0;
+ unsigned long start = 0;
control = control&0x0f;
reg_0x88 = usb_read8(padapter, REG_HMEBOX_E0);
@@ -61,8 +61,8 @@ s32 iol_execute(struct adapter *padapter, u8 control)
start = jiffies;
while ((reg_0x88 = usb_read8(padapter, REG_HMEBOX_E0)) & control &&
- (passing_time = rtw_get_passing_time_ms(start)) < 1000) {
- ;
+ jiffies_to_msecs(jiffies - start) < 1000) {
+ udelay(5);
}
reg_0x88 = usb_read8(padapter, REG_HMEBOX_E0);
@@ -242,6 +242,7 @@ static s32 _LLTWrite(struct adapter *padapter, u32 address, u32 data)
status = _FAIL;
break;
}
+ udelay(5);
} while (count++);
return status;
diff --git a/drivers/staging/rtl8188eu/hal/rtl8188eu_xmit.c b/drivers/staging/rtl8188eu/hal/rtl8188eu_xmit.c
index 7c5086ecff17..e04303ce80af 100644
--- a/drivers/staging/rtl8188eu/hal/rtl8188eu_xmit.c
+++ b/drivers/staging/rtl8188eu/hal/rtl8188eu_xmit.c
@@ -463,30 +463,26 @@ s32 rtl8188eu_xmitframe_complete(struct adapter *adapt, struct xmit_priv *pxmitp
}
/* 3 1. pick up first frame */
- do {
- rtw_free_xmitframe(pxmitpriv, pxmitframe);
-
- pxmitframe = rtw_dequeue_xframe(pxmitpriv, pxmitpriv->hwxmits, pxmitpriv->hwxmit_entry);
- if (pxmitframe == NULL) {
- /* no more xmit frame, release xmit buffer */
- rtw_free_xmitbuf(pxmitpriv, pxmitbuf);
- return false;
- }
+ rtw_free_xmitframe(pxmitpriv, pxmitframe);
- pxmitframe->pxmitbuf = pxmitbuf;
- pxmitframe->buf_addr = pxmitbuf->pbuf;
- pxmitbuf->priv_data = pxmitframe;
+ pxmitframe = rtw_dequeue_xframe(pxmitpriv, pxmitpriv->hwxmits, pxmitpriv->hwxmit_entry);
+ if (pxmitframe == NULL) {
+ /* no more xmit frame, release xmit buffer */
+ rtw_free_xmitbuf(pxmitpriv, pxmitbuf);
+ return false;
+ }
- pxmitframe->agg_num = 1; /* alloc xmitframe should assign to 1. */
- pxmitframe->pkt_offset = 1; /* first frame of aggregation, reserve offset */
+ pxmitframe->pxmitbuf = pxmitbuf;
+ pxmitframe->buf_addr = pxmitbuf->pbuf;
+ pxmitbuf->priv_data = pxmitframe;
- rtw_xmitframe_coalesce(adapt, pxmitframe->pkt, pxmitframe);
+ pxmitframe->agg_num = 1; /* alloc xmitframe should assign to 1. */
+ pxmitframe->pkt_offset = 1; /* first frame of aggregation, reserve offset */
- /* always return ndis_packet after rtw_xmitframe_coalesce */
- rtw_os_xmit_complete(adapt, pxmitframe);
+ rtw_xmitframe_coalesce(adapt, pxmitframe->pkt, pxmitframe);
- break;
- } while (1);
+ /* always return ndis_packet after rtw_xmitframe_coalesce */
+ rtw_os_xmit_complete(adapt, pxmitframe);
/* 3 2. aggregate same priority and same DA(AP or STA) frames */
pfirstframe = pxmitframe;
diff --git a/drivers/staging/rtl8188eu/hal/usb_halinit.c b/drivers/staging/rtl8188eu/hal/usb_halinit.c
index 7e72259f0e40..5789e1e23f0a 100644
--- a/drivers/staging/rtl8188eu/hal/usb_halinit.c
+++ b/drivers/staging/rtl8188eu/hal/usb_halinit.c
@@ -684,7 +684,7 @@ static u32 rtl8188eu_hal_init(struct adapter *Adapter)
struct hal_data_8188e *haldata = GET_HAL_DATA(Adapter);
struct pwrctrl_priv *pwrctrlpriv = &Adapter->pwrctrlpriv;
struct registry_priv *pregistrypriv = &Adapter->registrypriv;
- u32 init_start_time = jiffies;
+ unsigned long init_start_time = jiffies;
#define HAL_INIT_PROFILE_TAG(stage) do {} while (0)
@@ -903,7 +903,8 @@ HAL_INIT_PROFILE_TAG(HAL_INIT_STAGES_LCK);
exit:
HAL_INIT_PROFILE_TAG(HAL_INIT_STAGES_END);
- DBG_88E("%s in %dms\n", __func__, rtw_get_passing_time_ms(init_start_time));
+ DBG_88E("%s in %dms\n", __func__,
+ jiffies_to_msecs(jiffies - init_start_time));
return status;
@@ -1149,14 +1150,15 @@ static void _ReadRFType(struct adapter *Adapter)
static void _ReadAdapterInfo8188EU(struct adapter *Adapter)
{
- u32 start = jiffies;
+ unsigned long start = jiffies;
MSG_88E("====> %s\n", __func__);
_ReadRFType(Adapter);/* rf_chip -> _InitRFType() */
_ReadPROMContent(Adapter);
- MSG_88E("<==== %s in %d ms\n", __func__, rtw_get_passing_time_ms(start));
+ MSG_88E("<==== %s in %d ms\n", __func__,
+ jiffies_to_msecs(jiffies - start));
}
#define GPIO_DEBUG_PORT_NUM 0
diff --git a/drivers/staging/rtl8188eu/include/HalVerDef.h b/drivers/staging/rtl8188eu/include/HalVerDef.h
index 56b4ff08e509..6f2b2a436b04 100644
--- a/drivers/staging/rtl8188eu/include/HalVerDef.h
+++ b/drivers/staging/rtl8188eu/include/HalVerDef.h
@@ -47,37 +47,4 @@ struct HAL_VERSION {
enum HAL_VENDOR VendorType;
};
-/* Get element */
-#define GET_CVID_CHIP_TYPE(version) (((version).ChipType))
-#define GET_CVID_MANUFACTUER(version) (((version).VendorType))
-#define GET_CVID_CUT_VERSION(version) (((version).CUTVersion))
-
-/* Common Macro. -- */
-/* HAL_VERSION VersionID */
-
-/* HAL_CHIP_TYPE_E */
-#define IS_TEST_CHIP(version) \
- ((GET_CVID_CHIP_TYPE(version) == TEST_CHIP) ? true : false)
-#define IS_NORMAL_CHIP(version) \
- ((GET_CVID_CHIP_TYPE(version) == NORMAL_CHIP) ? true : false)
-
-/* HAL_CUT_VERSION_E */
-#define IS_A_CUT(version) \
- ((GET_CVID_CUT_VERSION(version) == A_CUT_VERSION) ? true : false)
-#define IS_B_CUT(version) \
- ((GET_CVID_CUT_VERSION(version) == B_CUT_VERSION) ? true : false)
-#define IS_C_CUT(version) \
- ((GET_CVID_CUT_VERSION(version) == C_CUT_VERSION) ? true : false)
-#define IS_D_CUT(version) \
- ((GET_CVID_CUT_VERSION(version) == D_CUT_VERSION) ? true : false)
-#define IS_E_CUT(version) \
- ((GET_CVID_CUT_VERSION(version) == E_CUT_VERSION) ? true : false)
-
-
-/* HAL_VENDOR_E */
-#define IS_CHIP_VENDOR_TSMC(version) \
- ((GET_CVID_MANUFACTUER(version) == CHIP_VENDOR_TSMC) ? true : false)
-#define IS_CHIP_VENDOR_UMC(version) \
- ((GET_CVID_MANUFACTUER(version) == CHIP_VENDOR_UMC) ? true : false)
-
#endif
diff --git a/drivers/staging/rtl8188eu/include/osdep_service.h b/drivers/staging/rtl8188eu/include/osdep_service.h
index e24fe8cc3d0b..22de53d6539a 100644
--- a/drivers/staging/rtl8188eu/include/osdep_service.h
+++ b/drivers/staging/rtl8188eu/include/osdep_service.h
@@ -87,8 +87,6 @@ u32 _rtw_down_sema(struct semaphore *sema);
void _rtw_init_queue(struct __queue *pqueue);
-s32 rtw_get_passing_time_ms(u32 start);
-
struct rtw_netdev_priv_indicator {
void *priv;
u32 sizeof_priv;
diff --git a/drivers/staging/rtl8188eu/include/rtw_xmit.h b/drivers/staging/rtl8188eu/include/rtw_xmit.h
index 62f5db169523..b7c20883d355 100644
--- a/drivers/staging/rtl8188eu/include/rtw_xmit.h
+++ b/drivers/staging/rtl8188eu/include/rtw_xmit.h
@@ -197,7 +197,6 @@ enum {
void rtw_sctx_init(struct submit_ctx *sctx, int timeout_ms);
int rtw_sctx_wait(struct submit_ctx *sctx);
void rtw_sctx_done_err(struct submit_ctx **sctx, int status);
-void rtw_sctx_done(struct submit_ctx **sctx);
struct xmit_buf {
struct list_head list;
diff --git a/drivers/staging/rtl8188eu/os_dep/os_intfs.c b/drivers/staging/rtl8188eu/os_dep/os_intfs.c
index d063d02db7f0..9201b94d017c 100644
--- a/drivers/staging/rtl8188eu/os_dep/os_intfs.c
+++ b/drivers/staging/rtl8188eu/os_dep/os_intfs.c
@@ -1100,7 +1100,7 @@ netdev_open_error:
int rtw_ips_pwr_up(struct adapter *padapter)
{
int result;
- u32 start_time = jiffies;
+ unsigned long start_time = jiffies;
DBG_88E("===> rtw_ips_pwr_up..............\n");
rtw_reset_drv_sw(padapter);
@@ -1109,13 +1109,14 @@ int rtw_ips_pwr_up(struct adapter *padapter)
rtw_led_control(padapter, LED_CTL_NO_LINK);
- DBG_88E("<=== rtw_ips_pwr_up.............. in %dms\n", rtw_get_passing_time_ms(start_time));
+ DBG_88E("<=== rtw_ips_pwr_up.............. in %dms\n",
+ jiffies_to_msecs(jiffies - start_time));
return result;
}
void rtw_ips_pwr_down(struct adapter *padapter)
{
- u32 start_time = jiffies;
+ unsigned long start_time = jiffies;
DBG_88E("===> rtw_ips_pwr_down...................\n");
@@ -1124,7 +1125,8 @@ void rtw_ips_pwr_down(struct adapter *padapter)
rtw_led_control(padapter, LED_CTL_POWER_OFF);
rtw_ips_dev_unload(padapter);
- DBG_88E("<=== rtw_ips_pwr_down..................... in %dms\n", rtw_get_passing_time_ms(start_time));
+ DBG_88E("<=== rtw_ips_pwr_down..................... in %dms\n",
+ jiffies_to_msecs(jiffies - start_time));
}
void rtw_ips_dev_unload(struct adapter *padapter)
diff --git a/drivers/staging/rtl8188eu/os_dep/osdep_service.c b/drivers/staging/rtl8188eu/os_dep/osdep_service.c
index 466cd76fc1c4..d87b54711c0d 100644
--- a/drivers/staging/rtl8188eu/os_dep/osdep_service.c
+++ b/drivers/staging/rtl8188eu/os_dep/osdep_service.c
@@ -77,12 +77,6 @@ void _rtw_init_queue(struct __queue *pqueue)
spin_lock_init(&(pqueue->lock));
}
-/* the input parameter start must be in jiffies */
-inline s32 rtw_get_passing_time_ms(u32 start)
-{
- return jiffies_to_msecs(jiffies-start);
-}
-
struct net_device *rtw_alloc_etherdev_with_old_priv(int sizeof_priv,
void *old_priv)
{
diff --git a/drivers/staging/rtl8188eu/os_dep/usb_intf.c b/drivers/staging/rtl8188eu/os_dep/usb_intf.c
index 82a7c27c517f..01d50f7c1667 100644
--- a/drivers/staging/rtl8188eu/os_dep/usb_intf.c
+++ b/drivers/staging/rtl8188eu/os_dep/usb_intf.c
@@ -227,7 +227,7 @@ static int rtw_suspend(struct usb_interface *pusb_intf, pm_message_t message)
struct net_device *pnetdev = padapter->pnetdev;
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
- u32 start_time = jiffies;
+ unsigned long start_time = jiffies;
pr_debug("==> %s (%s:%d)\n", __func__, current->comm, current->pid);
@@ -282,7 +282,7 @@ static int rtw_suspend(struct usb_interface *pusb_intf, pm_message_t message)
exit:
pr_debug("<=== %s .............. in %dms\n", __func__,
- rtw_get_passing_time_ms(start_time));
+ jiffies_to_msecs(jiffies - start_time));
return 0;
}
@@ -292,7 +292,7 @@ static int rtw_resume_process(struct adapter *padapter)
struct net_device *pnetdev;
struct pwrctrl_priv *pwrpriv = NULL;
int ret = -1;
- u32 start_time = jiffies;
+ unsigned long start_time = jiffies;
pr_debug("==> %s (%s:%d)\n", __func__, current->comm, current->pid);
@@ -323,7 +323,7 @@ exit:
if (pwrpriv)
pwrpriv->bInSuspend = false;
pr_debug("<=== %s return %d.............. in %dms\n", __func__,
- ret, rtw_get_passing_time_ms(start_time));
+ ret, jiffies_to_msecs(jiffies - start_time));
return ret;
}
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c
index 0b407feb5407..5e3bbe5c3ca4 100644
--- a/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c
+++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c
@@ -90,13 +90,12 @@ void rtl92e_set_bb_reg(struct net_device *dev, u32 dwRegAddr, u32 dwBitMask,
u32 rtl92e_get_bb_reg(struct net_device *dev, u32 dwRegAddr, u32 dwBitMask)
{
- u32 Ret = 0, OriginalValue, BitShift;
+ u32 OriginalValue, BitShift;
OriginalValue = rtl92e_readl(dev, dwRegAddr);
BitShift = _rtl92e_calculate_bit_shift(dwBitMask);
- Ret = (OriginalValue & dwBitMask) >> BitShift;
- return Ret;
+ return (OriginalValue & dwBitMask) >> BitShift;
}
static u32 _rtl92e_phy_rf_read(struct net_device *dev,
diff --git a/drivers/staging/rtl8192u/r8192U_core.c b/drivers/staging/rtl8192u/r8192U_core.c
index e06864f64beb..f4a4eae72aa4 100644
--- a/drivers/staging/rtl8192u/r8192U_core.c
+++ b/drivers/staging/rtl8192u/r8192U_core.c
@@ -5114,21 +5114,6 @@ static void __exit rtl8192_usb_module_exit(void)
RT_TRACE(COMP_DOWN, "Exiting");
}
-
-void rtl8192_try_wake_queue(struct net_device *dev, int pri)
-{
- unsigned long flags;
- short enough_desc;
- struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
-
- spin_lock_irqsave(&priv->tx_lock, flags);
- enough_desc = check_nic_enough_desc(dev, pri);
- spin_unlock_irqrestore(&priv->tx_lock, flags);
-
- if (enough_desc)
- ieee80211_wake_queue(priv->ieee80211);
-}
-
void EnableHWSecurityConfig8192(struct net_device *dev)
{
u8 SECR_value = 0x0;
diff --git a/drivers/staging/rtl8192u/r819xU_phy.c b/drivers/staging/rtl8192u/r819xU_phy.c
index 70656441c145..f264d88364a1 100644
--- a/drivers/staging/rtl8192u/r819xU_phy.c
+++ b/drivers/staging/rtl8192u/r819xU_phy.c
@@ -38,21 +38,6 @@ static u32 RF_CHANNEL_TABLE_ZEBRA[] = {
#define rtl819XAGCTAB_Array Rtl8192UsbAGCTAB_Array
/******************************************************************************
- * function: This function reads BB parameters from header file we generate,
- * and does register read/write
- * input: u32 bitmask //taget bit pos in the addr to be modified
- * output: none
- * return: u32 return the shift bit position of the mask
- ******************************************************************************/
-static u32 rtl8192_CalculateBitShift(u32 bitmask)
-{
- u32 i;
-
- i = ffs(bitmask) - 1;
- return i;
-}
-
-/******************************************************************************
* function: This function checks different RF type to execute legal judgement.
* If RF Path is illegal, we will return false.
* input: net_device *dev
@@ -94,7 +79,7 @@ void rtl8192_setBBreg(struct net_device *dev, u32 reg_addr, u32 bitmask,
if (bitmask != bMaskDWord) {
read_nic_dword(dev, reg_addr, &reg);
- bitshift = rtl8192_CalculateBitShift(bitmask);
+ bitshift = ffs(bitmask) - 1;
reg &= ~bitmask;
reg |= data << bitshift;
write_nic_dword(dev, reg_addr, reg);
@@ -117,7 +102,7 @@ u32 rtl8192_QueryBBReg(struct net_device *dev, u32 reg_addr, u32 bitmask)
u32 reg, bitshift;
read_nic_dword(dev, reg_addr, &reg);
- bitshift = rtl8192_CalculateBitShift(bitmask);
+ bitshift = ffs(bitmask) - 1;
return (reg & bitmask) >> bitshift;
}
@@ -306,7 +291,7 @@ void rtl8192_phy_SetRFReg(struct net_device *dev, RF90_RADIO_PATH_E eRFPath,
if (bitmask != bMask12Bits) {
/* RF data is 12 bits only */
reg = phy_FwRFSerialRead(dev, eRFPath, reg_addr);
- bitshift = rtl8192_CalculateBitShift(bitmask);
+ bitshift = ffs(bitmask) - 1;
reg &= ~bitmask;
reg |= data << bitshift;
@@ -321,7 +306,7 @@ void rtl8192_phy_SetRFReg(struct net_device *dev, RF90_RADIO_PATH_E eRFPath,
if (bitmask != bMask12Bits) {
/* RF data is 12 bits only */
reg = rtl8192_phy_RFSerialRead(dev, eRFPath, reg_addr);
- bitshift = rtl8192_CalculateBitShift(bitmask);
+ bitshift = ffs(bitmask) - 1;
reg &= ~bitmask;
reg |= data << bitshift;
@@ -356,7 +341,7 @@ u32 rtl8192_phy_QueryRFReg(struct net_device *dev, RF90_RADIO_PATH_E eRFPath,
} else {
reg = rtl8192_phy_RFSerialRead(dev, eRFPath, reg_addr);
}
- bitshift = rtl8192_CalculateBitShift(bitmask);
+ bitshift = ffs(bitmask) - 1;
reg = (reg & bitmask) >> bitshift;
return reg;
diff --git a/drivers/staging/rtl8712/rtl871x_mlme.c b/drivers/staging/rtl8712/rtl871x_mlme.c
index a4a002b55128..04f727fc95ea 100644
--- a/drivers/staging/rtl8712/rtl871x_mlme.c
+++ b/drivers/staging/rtl8712/rtl871x_mlme.c
@@ -123,7 +123,7 @@ static void _free_network(struct mlme_priv *pmlmepriv,
spin_unlock_irqrestore(&free_queue->lock, irqL);
}
-static void _free_network_nolock(struct mlme_priv *pmlmepriv,
+static void free_network_nolock(struct mlme_priv *pmlmepriv,
struct wlan_network *pnetwork)
{
struct __queue *free_queue = &pmlmepriv->free_bss_pool;
@@ -234,12 +234,6 @@ static struct wlan_network *alloc_network(struct mlme_priv *pmlmepriv)
return _r8712_alloc_network(pmlmepriv);
}
-static void free_network_nolock(struct mlme_priv *pmlmepriv,
- struct wlan_network *pnetwork)
-{
- _free_network_nolock(pmlmepriv, pnetwork);
-}
-
void r8712_free_network_queue(struct _adapter *dev)
{
_free_network_queue(dev);
diff --git a/drivers/staging/rtl8723au/core/rtw_efuse.c b/drivers/staging/rtl8723au/core/rtw_efuse.c
index 906b5782d165..f174b4d1a018 100644
--- a/drivers/staging/rtl8723au/core/rtw_efuse.c
+++ b/drivers/staging/rtl8723au/core/rtw_efuse.c
@@ -273,48 +273,6 @@ u8 EFUSE_Read1Byte23a(struct rtw_adapter *Adapter, u16 Address)
return 0xFF;
}
-/* Copy from WMAC fot EFUSE write 1 byte. */
-void EFUSE_Write1Byte(struct rtw_adapter *Adapter, u16 Address, u8 Value)
-{
- u8 Bytetemp = {0x00};
- u8 temp = {0x00};
- u32 k = 0;
- u16 contentLen = 0;
-
- EFUSE_GetEfuseDefinition23a(Adapter, EFUSE_WIFI,
- TYPE_EFUSE_REAL_CONTENT_LEN,
- (void *)&contentLen);
-
- if (Address < contentLen) { /* E-fuse 512Byte */
- rtl8723au_write8(Adapter, EFUSE_CTRL, Value);
-
- /* Write E-fuse Register address bit0~7 */
- temp = Address & 0xFF;
- rtl8723au_write8(Adapter, EFUSE_CTRL+1, temp);
- Bytetemp = rtl8723au_read8(Adapter, EFUSE_CTRL+2);
-
- /* Write E-fuse Register address bit8~9 */
- temp = ((Address >> 8) & 0x03) | (Bytetemp & 0xFC);
- rtl8723au_write8(Adapter, EFUSE_CTRL+2, temp);
-
- /* Write 0x30[31]= 1 */
- Bytetemp = rtl8723au_read8(Adapter, EFUSE_CTRL+3);
- temp = Bytetemp | 0x80;
- rtl8723au_write8(Adapter, EFUSE_CTRL+3, temp);
-
- /* Wait Write-ready (0x30[31]= 0) */
- Bytetemp = rtl8723au_read8(Adapter, EFUSE_CTRL+3);
- while (Bytetemp & 0x80) {
- Bytetemp = rtl8723au_read8(Adapter, EFUSE_CTRL+3);
- k++;
- if (k == 100) {
- k = 0;
- break;
- }
- }
- }
-}
-
/* Read one byte from real Efuse. */
int efuse_OneByteRead23a(struct rtw_adapter *pAdapter, u16 addr, u8 *data)
{
@@ -501,7 +459,8 @@ int rtw_BT_efuse_map_read23a(struct rtw_adapter *padapter,
}
/* Read All Efuse content */
-void Efuse_ReadAllMap(struct rtw_adapter *pAdapter, u8 efuseType, u8 *Efuse)
+static void Efuse_ReadAllMap(struct rtw_adapter *pAdapter, u8 efuseType,
+ u8 *Efuse)
{
u16 mapLen = 0;
diff --git a/drivers/staging/sm750fb/modedb.h b/drivers/staging/sm750fb/modedb.h
deleted file mode 100644
index 83cb2e2ae51a..000000000000
--- a/drivers/staging/sm750fb/modedb.h
+++ /dev/null
@@ -1,233 +0,0 @@
-
-static const struct fb_videomode modedb2[] = {
- {
- /* 640x400 @ 70 Hz, 31.5 kHz hsync */
- NULL, 70, 640, 400, 39721, 40, 24, 39, 9, 96, 2,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 640x480 @ 60 Hz, 31.5 kHz hsync */
- NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 800x600 @ 56 Hz, 35.15 kHz hsync */
- NULL, 56, 800, 600, 27777, 128, 24, 22, 1, 72, 2,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 1024x768 @ 87 Hz interlaced, 35.5 kHz hsync */
- NULL, 87, 1024, 768, 22271, 56, 24, 33, 8, 160, 8,
- 0, FB_VMODE_INTERLACED
- }, {
- /* 640x400 @ 85 Hz, 37.86 kHz hsync */
- NULL, 85, 640, 400, 31746, 96, 32, 41, 1, 64, 3,
- FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
- }, {
- /* 640x480 @ 72 Hz, 36.5 kHz hsync */
- NULL, 72, 640, 480, 31746, 144, 40, 30, 8, 40, 3,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 640x480 @ 75 Hz, 37.50 kHz hsync */
- NULL, 75, 640, 480, 31746, 120, 16, 16, 1, 64, 3,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 800x600 @ 60 Hz, 37.8 kHz hsync */
- NULL, 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,
- FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED
- }, {
- /* 640x480 @ 85 Hz, 43.27 kHz hsync */
- NULL, 85, 640, 480, 27777, 80, 56, 25, 1, 56, 3,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 1152x864 @ 89 Hz interlaced, 44 kHz hsync */
- NULL, 69, 1152, 864, 15384, 96, 16, 110, 1, 216, 10,
- 0, FB_VMODE_INTERLACED
- }, {
- /* 800x600 @ 72 Hz, 48.0 kHz hsync */
- NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6,
- FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED
- }, {
- /* 1024x768 @ 60 Hz, 48.4 kHz hsync */
- NULL, 60, 1024, 768, 15384, 168, 8, 29, 3, 144, 6,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 640x480 @ 100 Hz, 53.01 kHz hsync */
- NULL, 100, 640, 480, 21834, 96, 32, 36, 8, 96, 6,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 1152x864 @ 60 Hz, 53.5 kHz hsync */
- NULL, 60, 1152, 864, 11123, 208, 64, 16, 4, 256, 8,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 800x600 @ 85 Hz, 55.84 kHz hsync */
- NULL, 85, 800, 600, 16460, 160, 64, 36, 16, 64, 5,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 1024x768 @ 70 Hz, 56.5 kHz hsync */
- NULL, 70, 1024, 768, 13333, 144, 24, 29, 3, 136, 6,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 1280x960-60 VESA */
- NULL, 60, 1280, 960, 9259, 312, 96, 36, 1, 112, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA
- }, {
- /* 1280x1024-60 VESA */
- NULL, 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3,
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA
- }, {
- /* 1280x1024 @ 87 Hz interlaced, 51 kHz hsync */
- NULL, 87, 1280, 1024, 12500, 56, 16, 128, 1, 216, 12,
- 0, FB_VMODE_INTERLACED
- }, {
- /* 800x600 @ 100 Hz, 64.02 kHz hsync */
- NULL, 100, 800, 600, 14357, 160, 64, 30, 4, 64, 6,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 1024x768 @ 76 Hz, 62.5 kHz hsync */
- NULL, 76, 1024, 768, 11764, 208, 8, 36, 16, 120, 3,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 1152x864 @ 70 Hz, 62.4 kHz hsync */
- NULL, 70, 1152, 864, 10869, 106, 56, 20, 1, 160, 10,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 1280x1024 @ 61 Hz, 64.2 kHz hsync */
- NULL, 61, 1280, 1024, 9090, 200, 48, 26, 1, 184, 3,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 1400x1050 @ 60Hz, 63.9 kHz hsync */
- NULL, 68, 1400, 1050, 9259, 136, 40, 13, 1, 112, 3,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 1400x1050 @ 75,107 Hz, 82,392 kHz +hsync +vsync*/
- NULL, 75, 1400, 1050, 9271, 120, 56, 13, 0, 112, 3,
- FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED
- }, {
- /* 1400x1050 @ 60 Hz, ? kHz +hsync +vsync*/
- NULL, 60, 1400, 1050, 9259, 128, 40, 12, 0, 112, 3,
- FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED
- }, {
- /* 1024x768 @ 85 Hz, 70.24 kHz hsync */
- NULL, 85, 1024, 768, 10111, 192, 32, 34, 14, 160, 6,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 1152x864 @ 78 Hz, 70.8 kHz hsync */
- NULL, 78, 1152, 864, 9090, 228, 88, 32, 0, 84, 12,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 1280x1024 @ 70 Hz, 74.59 kHz hsync */
- NULL, 70, 1280, 1024, 7905, 224, 32, 28, 8, 160, 8,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 1600x1200 @ 60Hz, 75.00 kHz hsync */
- NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3,
- FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED
- }, {
- /* 1152x864 @ 84 Hz, 76.0 kHz hsync */
- NULL, 84, 1152, 864, 7407, 184, 312, 32, 0, 128, 12,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 1280x1024 @ 74 Hz, 78.85 kHz hsync */
- NULL, 74, 1280, 1024, 7407, 256, 32, 34, 3, 144, 3,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 1024x768 @ 100Hz, 80.21 kHz hsync */
- NULL, 100, 1024, 768, 8658, 192, 32, 21, 3, 192, 10,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 1280x1024 @ 76 Hz, 81.13 kHz hsync */
- NULL, 76, 1280, 1024, 7407, 248, 32, 34, 3, 104, 3,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 1600x1200 @ 70 Hz, 87.50 kHz hsync */
- NULL, 70, 1600, 1200, 5291, 304, 64, 46, 1, 192, 3,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 1152x864 @ 100 Hz, 89.62 kHz hsync */
- NULL, 100, 1152, 864, 7264, 224, 32, 17, 2, 128, 19,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 1280x1024 @ 85 Hz, 91.15 kHz hsync */
- NULL, 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3,
- FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED
- }, {
- /* 1600x1200 @ 75 Hz, 93.75 kHz hsync */
- NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3,
- FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED
- }, {
- /* 1600x1200 @ 85 Hz, 105.77 kHz hsync */
- NULL, 85, 1600, 1200, 4545, 272, 16, 37, 4, 192, 3,
- FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED
- }, {
- /* 1280x1024 @ 100 Hz, 107.16 kHz hsync */
- NULL, 100, 1280, 1024, 5502, 256, 32, 26, 7, 128, 15,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 1800x1440 @ 64Hz, 96.15 kHz hsync */
- NULL, 64, 1800, 1440, 4347, 304, 96, 46, 1, 192, 3,
- FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED
- }, {
- /* 1800x1440 @ 70Hz, 104.52 kHz hsync */
- NULL, 70, 1800, 1440, 4000, 304, 96, 46, 1, 192, 3,
- FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED
- }, {
- /* 512x384 @ 78 Hz, 31.50 kHz hsync */
- NULL, 78, 512, 384, 49603, 48, 16, 16, 1, 64, 3,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 512x384 @ 85 Hz, 34.38 kHz hsync */
- NULL, 85, 512, 384, 45454, 48, 16, 16, 1, 64, 3,
- 0, FB_VMODE_NONINTERLACED
- }, {
- /* 320x200 @ 70 Hz, 31.5 kHz hsync, 8:5 aspect ratio */
- NULL, 70, 320, 200, 79440, 16, 16, 20, 4, 48, 1,
- 0, FB_VMODE_DOUBLE
- }, {
- /* 320x240 @ 60 Hz, 31.5 kHz hsync, 4:3 aspect ratio */
- NULL, 60, 320, 240, 79440, 16, 16, 16, 5, 48, 1,
- 0, FB_VMODE_DOUBLE
- }, {
- /* 320x240 @ 72 Hz, 36.5 kHz hsync */
- NULL, 72, 320, 240, 63492, 16, 16, 16, 4, 48, 2,
- 0, FB_VMODE_DOUBLE
- }, {
- /* 400x300 @ 56 Hz, 35.2 kHz hsync, 4:3 aspect ratio */
- NULL, 56, 400, 300, 55555, 64, 16, 10, 1, 32, 1,
- 0, FB_VMODE_DOUBLE
- }, {
- /* 400x300 @ 60 Hz, 37.8 kHz hsync */
- NULL, 60, 400, 300, 50000, 48, 16, 11, 1, 64, 2,
- 0, FB_VMODE_DOUBLE
- }, {
- /* 400x300 @ 72 Hz, 48.0 kHz hsync */
- NULL, 72, 400, 300, 40000, 32, 24, 11, 19, 64, 3,
- 0, FB_VMODE_DOUBLE
- }, {
- /* 480x300 @ 56 Hz, 35.2 kHz hsync, 8:5 aspect ratio */
- NULL, 56, 480, 300, 46176, 80, 16, 10, 1, 40, 1,
- 0, FB_VMODE_DOUBLE
- }, {
- /* 480x300 @ 60 Hz, 37.8 kHz hsync */
- NULL, 60, 480, 300, 41858, 56, 16, 11, 1, 80, 2,
- 0, FB_VMODE_DOUBLE
- }, {
- /* 480x300 @ 63 Hz, 39.6 kHz hsync */
- NULL, 63, 480, 300, 40000, 56, 16, 11, 1, 80, 2,
- 0, FB_VMODE_DOUBLE
- }, {
- /* 480x300 @ 72 Hz, 48.0 kHz hsync */
- NULL, 72, 480, 300, 33386, 40, 24, 11, 19, 80, 3,
- 0, FB_VMODE_DOUBLE
- },
-};
-static const int nmodedb2 = sizeof(modedb2);
diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c
index 860e1c288ad5..c78421b5b0e7 100644
--- a/drivers/staging/sm750fb/sm750.c
+++ b/drivers/staging/sm750fb/sm750.c
@@ -21,8 +21,6 @@
#include "sm750_accel.h"
#include "sm750_cursor.h"
-#include "modedb.h"
-
/*
* #ifdef __BIG_ENDIAN
* ssize_t lynxfb_ops_write(struct fb_info *info, const char __user *buf,
diff --git a/drivers/staging/unisys/include/channel.h b/drivers/staging/unisys/include/channel.h
index c6c24423a7f0..5af59a5fce61 100644
--- a/drivers/staging/unisys/include/channel.h
+++ b/drivers/staging/unisys/include/channel.h
@@ -60,15 +60,19 @@ enum channel_clientstate {
CHANNELCLI_DISABLED = 1, /* client can see channel but is NOT
* allowed to use it unless given TBD
* explicit request (should actually be
- * < DETACHED) */
+ * < DETACHED)
+ */
CHANNELCLI_ATTACHING = 2, /* legacy EFI client request
- * for EFI server to attach */
+ * for EFI server to attach
+ */
CHANNELCLI_ATTACHED = 3, /* idle, but client may want
- * to use channel any time */
+ * to use channel any time
+ */
CHANNELCLI_BUSY = 4, /* client either wants to use or is
- * using channel */
- CHANNELCLI_OWNED = 5 /* "no worries" state - client can
- * access channel anytime */
+ * using channel
+ */
+ CHANNELCLI_OWNED = 5 /* "no worries" state - client can */
+ /* access channel anytime */
};
static inline const u8 *
@@ -116,11 +120,13 @@ ULTRA_CHANNELCLI_STRING(u32 v)
/* Values for ULTRA_CHANNEL_PROTOCOL.CliErrorBoot: */
/* throttling invalid boot channel statetransition error due to client
- * disabled */
+ * disabled
+ */
#define ULTRA_CLIERRORBOOT_THROTTLEMSG_DISABLED 0x01
/* throttling invalid boot channel statetransition error due to client
- * not attached */
+ * not attached
+ */
#define ULTRA_CLIERRORBOOT_THROTTLEMSG_NOTATTACHED 0x02
/* throttling invalid boot channel statetransition error due to busy channel */
@@ -128,24 +134,28 @@ ULTRA_CHANNELCLI_STRING(u32 v)
/* Values for ULTRA_CHANNEL_PROTOCOL.CliErrorOS: */
/* throttling invalid guest OS channel statetransition error due to
- * client disabled */
+ * client disabled
+ */
#define ULTRA_CLIERROROS_THROTTLEMSG_DISABLED 0x01
/* throttling invalid guest OS channel statetransition error due to
- * client not attached */
+ * client not attached
+ */
#define ULTRA_CLIERROROS_THROTTLEMSG_NOTATTACHED 0x02
/* throttling invalid guest OS channel statetransition error due to
- * busy channel */
+ * busy channel
+ */
#define ULTRA_CLIERROROS_THROTTLEMSG_BUSY 0x04
/* Values for ULTRA_CHANNEL_PROTOCOL.Features: This define exists so
-* that windows guest can look at the FeatureFlags in the io channel,
-* and configure the windows driver to use interrupts or not based on
-* this setting. This flag is set in uislib after the
-* ULTRA_VHBA_init_channel is called. All feature bits for all
-* channels should be defined here. The io channel feature bits are
-* defined right here */
+ * that windows guest can look at the FeatureFlags in the io channel,
+ * and configure the windows driver to use interrupts or not based on
+ * this setting. This flag is set in uislib after the
+ * ULTRA_VHBA_init_channel is called. All feature bits for all
+ * channels should be defined here. The io channel feature bits are
+ * defined right here
+ */
#define ULTRA_IO_DRIVER_ENABLES_INTS (0x1ULL << 1)
#define ULTRA_IO_CHANNEL_IS_POLLING (0x1ULL << 3)
#define ULTRA_IO_IOVM_IS_OK_WITH_DRIVER_DISABLING_INTS (0x1ULL << 4)
@@ -156,7 +166,7 @@ ULTRA_CHANNELCLI_STRING(u32 v)
struct channel_header {
u64 signature; /* Signature */
u32 legacy_state; /* DEPRECATED - being replaced by */
- /* / SrvState, CliStateBoot, and CliStateOS below */
+ /* SrvState, CliStateBoot, and CliStateOS below */
u32 header_size; /* sizeof(struct channel_header) */
u64 size; /* Total size of this channel in bytes */
u64 features; /* Flags to modify behavior */
@@ -169,25 +179,32 @@ struct channel_header {
uuid_le zone_uuid; /* Guid of Channel's zone */
u32 cli_str_offset; /* offset from channel header to
* nul-terminated ClientString (0 if
- * ClientString not present) */
+ * ClientString not present)
+ */
u32 cli_state_boot; /* CHANNEL_CLIENTSTATE of pre-boot
- * EFI client of this channel */
+ * EFI client of this channel
+ */
u32 cmd_state_cli; /* CHANNEL_COMMANDSTATE (overloaded in
* Windows drivers, see ServerStateUp,
- * ServerStateDown, etc) */
+ * ServerStateDown, etc)
+ */
u32 cli_state_os; /* CHANNEL_CLIENTSTATE of Guest OS
- * client of this channel */
+ * client of this channel
+ */
u32 ch_characteristic; /* CHANNEL_CHARACTERISTIC_<xxx> */
u32 cmd_state_srv; /* CHANNEL_COMMANDSTATE (overloaded in
* Windows drivers, see ServerStateUp,
- * ServerStateDown, etc) */
+ * ServerStateDown, etc)
+ */
u32 srv_state; /* CHANNEL_SERVERSTATE */
u8 cli_error_boot; /* bits to indicate err states for
* boot clients, so err messages can
- * be throttled */
+ * be throttled
+ */
u8 cli_error_os; /* bits to indicate err states for OS
* clients, so err messages can be
- * throttled */
+ * throttled
+ */
u8 filler[1]; /* Pad out to 128 byte cacheline */
/* Please add all new single-byte values below here */
u8 recover_channel;
@@ -205,29 +222,33 @@ struct signal_queue_header {
u64 features; /* Flags to modify behavior */
u64 num_sent; /* Total # of signals placed in this queue */
u64 num_overflows; /* Total # of inserts failed due to
- * full queue */
+ * full queue
+ */
u32 signal_size; /* Total size of a signal for this queue */
u32 max_slots; /* Max # of slots in queue, 1 slot is
- * always empty */
+ * always empty
+ */
u32 max_signals; /* Max # of signals in queue
- * (MaxSignalSlots-1) */
+ * (MaxSignalSlots-1)
+ */
u32 head; /* Queue head signal # */
/* 2nd cache line */
u64 num_received; /* Total # of signals removed from this queue */
- u32 tail; /* Queue tail signal # (on separate
- * cache line) */
+ u32 tail; /* Queue tail signal */
u32 reserved1; /* Reserved field */
u64 reserved2; /* Reserved field */
u64 client_queue;
u64 num_irq_received; /* Total # of Interrupts received. This
- * is incremented by the ISR in the
- * guest windows driver */
+ * is incremented by the ISR in the
+ * guest windows driver
+ */
u64 num_empty; /* Number of times that visor_signal_remove
- * is called and returned Empty
- * Status. */
+ * is called and returned Empty Status.
+ */
u32 errorflags; /* Error bits set during SignalReinit
* to denote trouble with client's
- * fields */
+ * fields
+ */
u8 filler[12]; /* Pad out to 64 byte cacheline */
} __packed;
@@ -272,8 +293,7 @@ spar_check_channel_client(void __iomem *ch,
return 0;
}
}
- if (expected_min_bytes > 0) { /* caller wants us to verify
- * channel size */
+ if (expected_min_bytes > 0) { /* verify channel size */
unsigned long long bytes =
readq(&((struct channel_header __iomem *)
(ch))->size);
@@ -284,8 +304,7 @@ spar_check_channel_client(void __iomem *ch,
return 0;
}
}
- if (expected_version > 0) { /* caller wants us to verify
- * channel version */
+ if (expected_version > 0) { /* verify channel version */
unsigned long ver = readl(&((struct channel_header __iomem *)
(ch))->version_id);
if (ver != expected_version) {
@@ -295,8 +314,7 @@ spar_check_channel_client(void __iomem *ch,
return 0;
}
}
- if (expected_signature > 0) { /* caller wants us to verify
- * channel signature */
+ if (expected_signature > 0) { /* verify channel signature */
unsigned long long sig =
readq(&((struct channel_header __iomem *)
(ch))->signature);
@@ -319,8 +337,7 @@ static inline int spar_check_channel_server(uuid_le typeuuid, char *name,
u64 expected_min_bytes,
u64 actual_bytes)
{
- if (expected_min_bytes > 0) /* caller wants us to verify
- * channel size */
+ if (expected_min_bytes > 0) /* verify channel size */
if (actual_bytes < expected_min_bytes) {
pr_err("Channel mismatch on channel=%s(%pUL) field=size expected=0x%-8.8llx actual=0x%-8.8llx\n",
name, &typeuuid, expected_min_bytes,
@@ -354,7 +371,8 @@ pathname_last_n_nodes(u8 *s, unsigned int n)
if (p == s)
break; /* should never happen, unless someone
* is changing the string while we are
- * looking at it!! */
+ * looking at it!!
+ */
if ((*p == '/') || (*p == '\\'))
n--;
}
@@ -395,7 +413,8 @@ spar_channel_client_acquire_os(void __iomem *ch, u8 *id)
if (readl(&hdr->cli_state_os) == CHANNELCLI_OWNED) {
if (readb(&hdr->cli_error_os) != 0) {
/* we are in an error msg throttling state;
- * come out of it */
+ * come out of it
+ */
pr_info("%s Channel OS client acquire now successful\n",
id);
writeb(0, &hdr->cli_error_os);
@@ -404,8 +423,9 @@ spar_channel_client_acquire_os(void __iomem *ch, u8 *id)
}
/* We have to do it the "hard way". We transition to BUSY,
- * and can use the channel iff our competitor has not also
- * transitioned to BUSY. */
+ * and can use the channel iff our competitor has not also
+ * transitioned to BUSY.
+ */
if (readl(&hdr->cli_state_os) != CHANNELCLI_ATTACHED) {
if ((readb(&hdr->cli_error_os)
& ULTRA_CLIERROROS_THROTTLEMSG_NOTATTACHED) == 0) {
diff --git a/drivers/staging/unisys/include/iochannel.h b/drivers/staging/unisys/include/iochannel.h
index 14e656ff73ec..162ca187a66b 100644
--- a/drivers/staging/unisys/include/iochannel.h
+++ b/drivers/staging/unisys/include/iochannel.h
@@ -6,7 +6,8 @@
/*
* Everything needed for IOPart-GuestPart communication is define in
* this file. Note: Everything is OS-independent because this file is
- * used by Windows, Linux and possible EFI drivers. */
+ * used by Windows, Linux and possible EFI drivers.
+ */
/*
* Communication flow between the IOPart and GuestPart uses the channel headers
@@ -66,21 +67,15 @@
* IO Partition is defined below.
*/
-/*
- * Defines and enums.
- */
-
+/* Defines and enums. */
#define MINNUM(a, b) (((a) < (b)) ? (a) : (b))
#define MAXNUM(a, b) (((a) > (b)) ? (a) : (b))
-/* these define the two queues per data channel between iopart and
- * ioguestparts
- */
-#define IOCHAN_TO_IOPART 0 /* used by ioguestpart to 'insert' signals to
- * iopart */
-
-#define IOCHAN_FROM_IOPART 1 /* used by ioguestpart to 'remove' signals from
- * iopart - same queue as previous queue */
+/* define the two queues per data channel between iopart and ioguestparts */
+/* used by ioguestpart to 'insert' signals to iopart */
+#define IOCHAN_TO_IOPART 0
+/* used by ioguestpart to 'remove' signals from iopart, same previous queue */
+#define IOCHAN_FROM_IOPART 1
/* size of cdb - i.e., scsi cmnd */
#define MAX_CMND_SIZE 16
@@ -92,26 +87,29 @@
/* various types of network packets that can be sent in cmdrsp */
enum net_types {
NET_RCV_POST = 0, /* submit buffer to hold receiving
- * incoming packet */
+ * incoming packet
+ */
/* virtnic -> uisnic */
NET_RCV, /* incoming packet received */
/* uisnic -> virtpci */
- NET_XMIT, /* for outgoing net packets */
+ NET_XMIT, /* for outgoing net packets */
/* virtnic -> uisnic */
NET_XMIT_DONE, /* outgoing packet xmitted */
/* uisnic -> virtpci */
NET_RCV_ENBDIS, /* enable/disable packet reception */
/* virtnic -> uisnic */
- NET_RCV_ENBDIS_ACK, /* acknowledge enable/disable packet
- * reception */
+ NET_RCV_ENBDIS_ACK, /* acknowledge enable/disable packet */
+ /* reception */
/* uisnic -> virtnic */
NET_RCV_PROMISC, /* enable/disable promiscuous mode */
/* virtnic -> uisnic */
NET_CONNECT_STATUS, /* indicate the loss or restoration of a network
- * connection */
+ * connection
+ */
/* uisnic -> virtnic */
NET_MACADDR, /* indicates the client has requested to update
- * its MAC addr */
+ * its MAC addr
+ */
NET_MACADDR_ACK, /* MAC address */
};
@@ -170,51 +168,43 @@ struct vhba_wwnn {
} __packed;
/* WARNING: Values stired in this structure must contain maximum counts (not
- * maximum values). */
-struct vhba_config_max { /* 20 bytes */
- u32 max_channel; /* maximum channel for devices attached to this
- * bus */
- u32 max_id; /* maximum SCSI ID for devices attached to this
- * bus */
- u32 max_lun; /* maximum SCSI LUN for devices attached to this
- * bus */
- u32 cmd_per_lun; /* maximum number of outstanding commands per
- * lun that are allowed at one time */
- u32 max_io_size; /* maximum io size for devices attached to this
- * bus */
+ * maximum values).
+ */
+struct vhba_config_max {/* 20 bytes */
+ u32 max_channel;/* maximum channel for devices attached to this bus */
+ u32 max_id; /* maximum SCSI ID for devices attached to bus */
+ u32 max_lun; /* maximum SCSI LUN for devices attached to bus */
+ u32 cmd_per_lun;/* maximum number of outstanding commands per LUN */
+ u32 max_io_size;/* maximum io size for devices attached to this bus */
/* max io size is often determined by the resource of the hba. e.g */
/* max scatter gather list length * page size / sector size */
} __packed;
struct uiscmdrsp_scsi {
- u64 handle; /* the handle to the cmd that was received -
- * send it back as is in the rsp packet. */
+ u64 handle; /* the handle to the cmd that was received */
+ /* send it back as is in the rsp packet. */
u8 cmnd[MAX_CMND_SIZE]; /* the cdb for the command */
u32 bufflen; /* length of data to be transferred out or in */
- u16 guest_phys_entries; /* Number of entries in scatter-gather (sg)
- * list */
+ u16 guest_phys_entries; /* Number of entries in scatter-gather list */
struct guest_phys_info gpi_list[MAX_PHYS_INFO]; /* physical address
* information for each
- * fragment */
+ * fragment
+ */
enum dma_data_direction data_dir; /* direction of the data, if any */
- struct uisscsi_dest vdest; /* identifies the virtual hba, id,
- * channel, lun to which cmd was sent */
+ struct uisscsi_dest vdest; /* identifies the virtual hba, id, */
+ /* channel, lun to which cmd was sent */
- /* the following fields are needed to queue the rsp back to cmd
- * originator */
- int linuxstat; /* the original Linux status - for use by linux
- * vdisk code */
+ /* Needed to queue the rsp back to cmd originator */
+ int linuxstat; /* original Linux status used by linux vdisk */
u8 scsistat; /* the scsi status */
- u8 addlstat; /* non-scsi status - covers cases like timeout
- * needed by windows guests */
+ u8 addlstat; /* non-scsi status */
#define ADDL_SEL_TIMEOUT 4
/* the following fields are need to determine the result of command */
u8 sensebuf[MAX_SENSE_SIZE]; /* sense info in case cmd failed; */
/* it holds the sense_data struct; */
/* see that struct for details. */
- void *vdisk; /* contains pointer to the vdisk so that we can clean up
- * when the IO completes. */
+ void *vdisk; /* pointer to the vdisk to clean up when IO completes. */
int no_disk_result;
/* used to return no disk inquiry result
* when no_disk_result is set to 1,
@@ -258,15 +248,15 @@ struct uiscmdrsp_scsi {
*/
#define NO_DISK_INQUIRY_RESULT_LEN 36
-#define MIN_INQUIRY_RESULT_LEN 5 /* we need at least 5 bytes minimum for inquiry
- * result */
+#define MIN_INQUIRY_RESULT_LEN 5 /* 5 bytes minimum for inquiry result */
/* SCSI device version for no disk inquiry result */
#define SCSI_SPC2_VER 4 /* indicates SCSI SPC2 (SPC3 is 5) */
/* Windows and Linux want different things for a non-existent lun. So, we'll let
* caller pass in the peripheral qualifier and type.
- * NOTE:[4] SCSI returns (n-4); so we return length-1-4 or length-5. */
+ * NOTE:[4] SCSI returns (n-4); so we return length-1-4 or length-5.
+ */
#define SET_NO_DISK_INQUIRY_RESULT(buf, len, lun, lun0notpresent, notpresent) \
do { \
@@ -305,9 +295,7 @@ struct uiscmdrsp_scsi {
} \
} while (0)
-/*
- * Struct & Defines to support sense information.
- */
+/* Struct & Defines to support sense information. */
/* The following struct is returned in sensebuf field in uiscmdrsp_scsi. It is
* initialized in exactly the manner that is recommended in Windows (hence the
@@ -342,13 +330,11 @@ struct sense_data {
struct net_pkt_xmt {
int len; /* full length of data in the packet */
int num_frags; /* number of fragments in frags containing data */
- struct phys_info frags[MAX_PHYS_INFO]; /* physical page information for
- * each fragment */
+ struct phys_info frags[MAX_PHYS_INFO]; /* physical page information */
char ethhdr[ETH_HEADER_SIZE]; /* the ethernet header */
struct {
- /* these are needed for csum at uisnic end */
- u8 valid; /* 1 = rest of this struct is valid - else
- * ignore */
+ /* these are needed for csum at uisnic end */
+ u8 valid; /* 1 = struct is valid - else ignore */
u8 hrawoffv; /* 1 = hwrafoff is valid */
u8 nhrawoffv; /* 1 = nhwrafoff is valid */
u16 protocol; /* specifies packet protocol */
@@ -380,16 +366,18 @@ struct net_pkt_xmtdone {
*/
#define RCVPOST_BUF_SIZE 4032
#define MAX_NET_RCV_CHAIN \
- ((ETH_MAX_MTU+ETH_HEADER_SIZE + RCVPOST_BUF_SIZE-1) / RCVPOST_BUF_SIZE)
+ ((ETH_MAX_MTU + ETH_HEADER_SIZE + RCVPOST_BUF_SIZE - 1) \
+ / RCVPOST_BUF_SIZE)
struct net_pkt_rcvpost {
/* rcv buf size must be large enough to include ethernet data len +
* ethernet header len - we are choosing 2K because it is guaranteed
- * to be describable */
- struct phys_info frag; /* physical page information for the
- * single fragment 2K rcv buf */
- u64 unique_num; /* This is used to make sure that
- * receive posts are returned to */
+ * to be describable
+ */
+ struct phys_info frag; /* physical page information for the */
+ /* single fragment 2K rcv buf */
+ u64 unique_num;
+ /* unique_num ensure that receive posts are returned to */
/* the Adapter which we sent them originally. */
} __packed;
@@ -399,8 +387,7 @@ struct net_pkt_rcv {
u32 rcv_done_len; /* length of received data */
u8 numrcvbufs; /* number of receive buffers that contain the */
/* incoming data; guest end MUST chain these together. */
- void *rcvbuf[MAX_NET_RCV_CHAIN]; /* the list of receive buffers
- * that must be chained; */
+ void *rcvbuf[MAX_NET_RCV_CHAIN]; /* list of chained rcvbufs */
/* each entry is a receive buffer provided by NET_RCV_POST. */
/* NOTE: first rcvbuf in the chain will also be provided in net.buf. */
u64 unique_num;
@@ -469,18 +456,17 @@ struct uiscmdrsp_scsitaskmgmt {
#define TASK_MGMT_FAILED 0
} __packed;
-/* The following is used by uissd to send disk add/remove notifications to
- * Guest */
+/* Used by uissd to send disk add/remove notifications to Guest */
/* Note that the vHba pointer is not used by the Client/Guest side. */
struct uiscmdrsp_disknotify {
u8 add; /* 0-remove, 1-add */
- void *v_hba; /* Pointer to vhba_info for channel info to
- * route msg */
+ void *v_hba; /* channel info to route msg */
u32 channel, id, lun; /* SCSI Path of Disk to added or removed */
} __packed;
/* The following is used by virthba/vSCSI to send the Acquire/Release commands
- * to the IOVM. */
+ * to the IOVM.
+ */
struct uiscmdrsp_vdiskmgmt {
enum vdisk_mgmt_types vdisktype;
@@ -533,8 +519,8 @@ struct uiscmdrsp {
struct uiscmdrsp_disknotify disknotify;
struct uiscmdrsp_vdiskmgmt vdiskmgmt;
};
- void *private_data; /* used to send the response when the cmd is
- * done (scsi & scsittaskmgmt). */
+ void *private_data; /* send the response when the cmd is */
+ /* done (scsi & scsittaskmgmt). */
struct uiscmdrsp *next; /* General Purpose Queue Link */
struct uiscmdrsp *activeQ_next; /* Used to track active commands */
struct uiscmdrsp *activeQ_prev; /* Used to track active commands */
@@ -564,15 +550,11 @@ struct spar_io_channel_protocol {
} __packed;
#define MAX_CLIENTSTRING_LEN 1024
- u8 client_string[MAX_CLIENTSTRING_LEN];/* NULL terminated - so holds
- * max - 1 bytes */
+ /* client_string is NULL termimated so holds max -1 bytes */
+ u8 client_string[MAX_CLIENTSTRING_LEN];
} __packed;
-
-/*
- * INLINE functions for initializing and accessing I/O data channels
- */
-
+/* INLINE functions for initializing and accessing I/O data channels */
#define SIZEOF_PROTOCOL (COVER(sizeof(struct spar_io_channel_protocol), 64))
#define SIZEOF_CMDRSP (COVER(sizeof(struct uiscmdrsp), 64))
@@ -584,8 +566,7 @@ struct spar_io_channel_protocol {
* pfn-off-size entires.
*/
-/* we deal with 4K page sizes when we it comes to passing page information
- * between */
+/* use 4K page sizes when we it comes to passing page information between */
/* Guest and IOPartition. */
#define PI_PAGE_SIZE 0x1000
#define PI_PAGE_MASK 0x0FFF
@@ -594,18 +575,8 @@ struct spar_io_channel_protocol {
* room)
*/
static inline u16
-add_physinfo_entries(u32 inp_pfn, /* input - specifies the pfn to be used
- * to add entries */
- u16 inp_off, /* input - specifies the off to be used
- * to add entries */
- u32 inp_len, /* input - specifies the len to be used
- * to add entries */
- u16 index, /* input - index in array at which new
- * entries are added */
- u16 max_pi_arr_entries, /* input - specifies the maximum
- * entries pi_arr can hold */
- struct phys_info pi_arr[]) /* input & output - array to
- * which entries are added */
+add_physinfo_entries(u32 inp_pfn, u16 inp_off, u32 inp_len, u16 index,
+ u16 max_pi_arr_entries, struct phys_info pi_arr[])
{
u32 len;
u16 i, firstlen;
diff --git a/drivers/staging/unisys/include/vbushelper.h b/drivers/staging/unisys/include/vbushelper.h
index f272975b2920..f1b6aacb79d7 100644
--- a/drivers/staging/unisys/include/vbushelper.h
+++ b/drivers/staging/unisys/include/vbushelper.h
@@ -19,7 +19,8 @@
#define __VBUSHELPER_H__
/* TARGET_HOSTNAME specified as -DTARGET_HOSTNAME=\"thename\" on the
- * command line */
+ * command line
+ */
#define TARGET_HOSTNAME "linuxguest"
diff --git a/drivers/staging/unisys/include/visorbus.h b/drivers/staging/unisys/include/visorbus.h
index 9235536fa75f..2a64a9ce0208 100644
--- a/drivers/staging/unisys/include/visorbus.h
+++ b/drivers/staging/unisys/include/visorbus.h
@@ -140,8 +140,8 @@ struct visor_device {
struct {
int major, minor;
void *attr; /* private use by devmajorminor_attr.c you can
- * change this constant to whatever you
- * want; */
+ * change this constant to whatever you want
+ */
} devnodes[5];
/* the code will detect and behave appropriately) */
struct semaphore visordriver_callback_lock;
diff --git a/drivers/staging/unisys/visorbus/Kconfig b/drivers/staging/unisys/visorbus/Kconfig
index 9b299ac86015..511388075ffa 100644
--- a/drivers/staging/unisys/visorbus/Kconfig
+++ b/drivers/staging/unisys/visorbus/Kconfig
@@ -6,4 +6,9 @@ config UNISYS_VISORBUS
tristate "Unisys visorbus driver"
depends on UNISYSSPAR
---help---
- If you say Y here, you will enable the Unisys visorbus driver.
+ The visorbus driver is a virtualized bus for the Unisys s-Par firmware.
+ Virtualized devices allow Linux guests on a system to share disks and
+ network cards that do not have SR-IOV support, and to be accessed using
+ the partition desktop application. The visorbus driver is required to
+ discover devices on an s-Par guest, and must be present for any other
+ s-Par guest driver to function correctly.
diff --git a/drivers/staging/unisys/visorbus/controlvmcompletionstatus.h b/drivers/staging/unisys/visorbus/controlvmcompletionstatus.h
index 3c97ebac4f32..23ad0ea6c9fc 100644
--- a/drivers/staging/unisys/visorbus/controlvmcompletionstatus.h
+++ b/drivers/staging/unisys/visorbus/controlvmcompletionstatus.h
@@ -39,7 +39,8 @@
#define CONTROLVM_RESP_ERROR_MAX_DEVICES 202 /* DEVICE_CREATE */
/* Payload and Parameter Related------------------------------------[400-499] */
#define CONTROLVM_RESP_ERROR_PAYLOAD_INVALID 400 /* SWITCH_ATTACHEXTPORT,
- * DEVICE_CONFIGURE */
+ * DEVICE_CONFIGURE
+ */
#define CONTROLVM_RESP_ERROR_INITIATOR_PARAMETER_INVALID 401 /* Multiple */
#define CONTROLVM_RESP_ERROR_TARGET_PARAMETER_INVALID 402 /* DEVICE_CONFIGURE */
#define CONTROLVM_RESP_ERROR_CLIENT_PARAMETER_INVALID 403 /* DEVICE_CONFIGURE */
@@ -48,36 +49,43 @@
* BUS_CONFIGURE,
* DEVICE_CREATE,
* DEVICE_CONFIG
- * DEVICE_DESTROY */
+ * DEVICE_DESTROY
+ */
#define CONTROLVM_RESP_ERROR_DEVICE_INVALID 501 /* SWITCH_ATTACHINTPORT */
/* DEVICE_CREATE,
* DEVICE_CONFIGURE,
- * DEVICE_DESTROY */
+ * DEVICE_DESTROY
+ */
#define CONTROLVM_RESP_ERROR_CHANNEL_INVALID 502 /* DEVICE_CREATE,
- * DEVICE_CONFIGURE */
+ * DEVICE_CONFIGURE
+ */
/* Partition Driver Callback Interface----------------------[600-699] */
#define CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE 604 /* BUS_CREATE,
* BUS_DESTROY,
* DEVICE_CREATE,
- * DEVICE_DESTROY */
+ * DEVICE_DESTROY
+ */
/* Unable to invoke VIRTPCI callback */
#define CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR 605
/* BUS_CREATE,
* BUS_DESTROY,
* DEVICE_CREATE,
- * DEVICE_DESTROY */
+ * DEVICE_DESTROY
+ */
/* VIRTPCI Callback returned error */
#define CONTROLVM_RESP_ERROR_GENERIC_DRIVER_CALLBACK_ERROR 606
/* SWITCH_ATTACHEXTPORT,
* SWITCH_DETACHEXTPORT
- * DEVICE_CONFIGURE */
+ * DEVICE_CONFIGURE
+ */
/* generic device callback returned error */
/* Bus Related------------------------------------------------------[700-799] */
#define CONTROLVM_RESP_ERROR_BUS_DEVICE_ATTACHED 700 /* BUS_DESTROY */
/* Channel Related--------------------------------------------------[800-899] */
#define CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN 800 /* GET_CHANNELINFO,
- * DEVICE_DESTROY */
+ * DEVICE_DESTROY
+ */
#define CONTROLVM_RESP_ERROR_CHANNEL_SIZE_TOO_SMALL 801 /* DEVICE_CREATE */
/* Chipset Shutdown Related---------------------------------------[1000-1099] */
#define CONTROLVM_RESP_ERROR_CHIPSET_SHUTDOWN_FAILED 1000
diff --git a/drivers/staging/unisys/visorbus/periodic_work.c b/drivers/staging/unisys/visorbus/periodic_work.c
index 115b7aa9560a..00b152764f84 100644
--- a/drivers/staging/unisys/visorbus/periodic_work.c
+++ b/drivers/staging/unisys/visorbus/periodic_work.c
@@ -43,11 +43,12 @@ static void periodic_work_func(struct work_struct *work)
(*pw->workfunc)(pw->workfuncarg);
}
-struct periodic_work *visor_periodic_work_create(ulong jiffy_interval,
- struct workqueue_struct *workqueue,
- void (*workfunc)(void *),
- void *workfuncarg,
- const char *devnam)
+struct periodic_work
+*visor_periodic_work_create(ulong jiffy_interval,
+ struct workqueue_struct *workqueue,
+ void (*workfunc)(void *),
+ void *workfuncarg,
+ const char *devnam)
{
struct periodic_work *pw;
diff --git a/drivers/staging/unisys/visorbus/vbuschannel.h b/drivers/staging/unisys/visorbus/vbuschannel.h
index 80e64477e547..90fa12e62f26 100644
--- a/drivers/staging/unisys/visorbus/vbuschannel.h
+++ b/drivers/staging/unisys/visorbus/vbuschannel.h
@@ -36,10 +36,11 @@ static const uuid_le spar_vbus_channel_protocol_uuid =
#define SPAR_VBUS_CHANNEL_PROTOCOL_SIGNATURE ULTRA_CHANNEL_PROTOCOL_SIGNATURE
/* Must increment this whenever you insert or delete fields within this channel
-* struct. Also increment whenever you change the meaning of fields within this
-* channel struct so as to break pre-existing software. Note that you can
-* usually add fields to the END of the channel struct withOUT needing to
-* increment this. */
+ * struct. Also increment whenever you change the meaning of fields within this
+ * channel struct so as to break pre-existing software. Note that you can
+ * usually add fields to the END of the channel struct withOUT needing to
+ * increment this.
+ */
#define SPAR_VBUS_CHANNEL_PROTOCOL_VERSIONID 1
#define SPAR_VBUS_CHANNEL_OK_CLIENT(ch) \
diff --git a/drivers/staging/unisys/visorbus/visorbus_main.c b/drivers/staging/unisys/visorbus/visorbus_main.c
index a272b48bab28..eac97d22278a 100644
--- a/drivers/staging/unisys/visorbus/visorbus_main.c
+++ b/drivers/staging/unisys/visorbus/visorbus_main.c
@@ -1078,7 +1078,8 @@ away:
}
/* Write the contents of <info> to the struct
- * spar_vbus_channel_protocol.chp_info. */
+ * spar_vbus_channel_protocol.chp_info.
+ */
static int
write_vbus_chp_info(struct visorchannel *chan,
@@ -1096,7 +1097,8 @@ write_vbus_chp_info(struct visorchannel *chan,
}
/* Write the contents of <info> to the struct
- * spar_vbus_channel_protocol.bus_info. */
+ * spar_vbus_channel_protocol.bus_info.
+ */
static int
write_vbus_bus_info(struct visorchannel *chan,
@@ -1370,7 +1372,8 @@ pause_state_change_complete(struct visor_device *dev, int status)
/* Notify the chipset driver that the pause is complete, which
* will presumably want to send some sort of response to the
- * initiator. */
+ * initiator.
+ */
(*chipset_responders.device_pause) (dev, status);
}
@@ -1390,7 +1393,8 @@ resume_state_change_complete(struct visor_device *dev, int status)
/* Notify the chipset driver that the resume is complete,
* which will presumably want to send some sort of response to
- * the initiator. */
+ * the initiator.
+ */
(*chipset_responders.device_resume) (dev, status);
}
@@ -1437,7 +1441,8 @@ initiate_chipset_device_pause_resume(struct visor_device *dev, bool is_pause)
* existing problem prevents us from ever getting a bus
* resume... This hack would fail to work should we
* ever have a bus that contains NO devices, since we
- * would never even get here in that case. */
+ * would never even get here in that case.
+ */
fix_vbus_dev_info(dev);
if (!drv->resume)
goto away;
diff --git a/drivers/staging/unisys/visorbus/visorchannel.c b/drivers/staging/unisys/visorbus/visorchannel.c
index a4e117f101cf..891b8db7c5ec 100644
--- a/drivers/staging/unisys/visorbus/visorchannel.c
+++ b/drivers/staging/unisys/visorbus/visorchannel.c
@@ -41,8 +41,8 @@ struct visorchannel {
struct channel_header chan_hdr;
uuid_le guid;
ulong size;
- bool needs_lock; /* channel creator knows if more than one
- * thread will be inserting or removing */
+ bool needs_lock; /* channel creator knows if more than one */
+ /* thread will be inserting or removing */
spinlock_t insert_lock; /* protect head writes in chan_hdr */
spinlock_t remove_lock; /* protect tail writes in chan_hdr */
diff --git a/drivers/staging/unisys/visorbus/vmcallinterface.h b/drivers/staging/unisys/visorbus/vmcallinterface.h
index c8d8483bd4df..c043fa41ceda 100644
--- a/drivers/staging/unisys/visorbus/vmcallinterface.h
+++ b/drivers/staging/unisys/visorbus/vmcallinterface.h
@@ -43,20 +43,15 @@ enum vmcall_monitor_interface_method_tuple { /* VMCALL identification tuples */
* - the 0x01 identifies it as the 1st instance of a VMCALL_VIRTPART
* type of VMCALL
*/
-
- VMCALL_IO_CONTROLVM_ADDR = 0x0501, /* used by all Guests, not just
- * IO */
- VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET = 0x0708, /* Allow caller to
- * query virtual time
- * offset */
- VMCALL_POST_CODE_LOGEVENT = 0x070B, /* LOGEVENT Post Code (RDX) with
- * specified subsystem mask (RCX
- * - monitor_subsystems.h) and
- * severity (RDX) */
- VMCALL_UPDATE_PHYSICAL_TIME = 0x0a02 /* Allow
- * ULTRA_SERVICE_CAPABILITY_TIME
- * capable guest to make
- * VMCALL */
+ /* used by all Guests, not just IO */
+ VMCALL_IO_CONTROLVM_ADDR = 0x0501,
+ /* Allow caller to query virtual time offset */
+ VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET = 0x0708,
+ /* LOGEVENT Post Code (RDX) with specified subsystem mask */
+ /* (RCX - monitor_subsystems.h) and severity (RDX) */
+ VMCALL_POST_CODE_LOGEVENT = 0x070B,
+ /* Allow ULTRA_SERVICE_CAPABILITY_TIME capable guest to make VMCALL */
+ VMCALL_UPDATE_PHYSICAL_TIME = 0x0a02
};
#define VMCALL_SUCCESS 0
@@ -74,7 +69,8 @@ enum vmcall_monitor_interface_method_tuple { /* VMCALL identification tuples */
unisys_extended_vmcall(method, param1, param2, param3)
/* The following uses VMCALL_POST_CODE_LOGEVENT interface but is currently
- * not used much */
+ * not used much
+ */
#define ISSUE_IO_VMCALL_POSTCODE_SEVERITY(postcode, severity) \
ISSUE_IO_EXTENDED_VMCALL(VMCALL_POST_CODE_LOGEVENT, severity, \
MDS_APPOS, postcode)
@@ -84,11 +80,11 @@ enum vmcall_monitor_interface_method_tuple { /* VMCALL identification tuples */
/* Parameters to VMCALL_IO_CONTROLVM_ADDR interface */
struct vmcall_io_controlvm_addr_params {
- /* The Guest-relative physical address of the ControlVm channel.
- * This VMCall fills this in with the appropriate address. */
+ /* The Guest-relative physical address of the ControlVm channel. */
+ /* This VMCall fills this in with the appropriate address. */
u64 address; /* contents provided by this VMCALL (OUT) */
- /* the size of the ControlVm channel in bytes This VMCall fills this
- * in with the appropriate address. */
+ /* the size of the ControlVm channel in bytes This VMCall fills this */
+ /* in with the appropriate address. */
u32 channel_bytes; /* contents provided by this VMCALL (OUT) */
u8 unused[4]; /* Unused Bytes in the 64-Bit Aligned Struct */
} __packed;
diff --git a/drivers/staging/unisys/visorhba/visorhba_main.c b/drivers/staging/unisys/visorhba/visorhba_main.c
index c119f20dfd44..d5178b44ba8c 100644
--- a/drivers/staging/unisys/visorhba/visorhba_main.c
+++ b/drivers/staging/unisys/visorhba/visorhba_main.c
@@ -453,7 +453,6 @@ visorhba_queue_command_lck(struct scsi_cmnd *scsicmd,
struct uiscmdrsp *cmdrsp;
struct scsi_device *scsidev = scsicmd->device;
int insert_location;
- unsigned char op;
unsigned char *cdb = scsicmd->cmnd;
struct Scsi_Host *scsihost = scsidev->host;
unsigned int i;
@@ -461,7 +460,6 @@ visorhba_queue_command_lck(struct scsi_cmnd *scsicmd,
(struct visorhba_devdata *)scsihost->hostdata;
struct scatterlist *sg = NULL;
struct scatterlist *sglist = NULL;
- int err = 0;
if (devdata->serverdown || devdata->serverchangingstate)
return SCSI_MLQUEUE_DEVICE_BUSY;
@@ -496,10 +494,8 @@ visorhba_queue_command_lck(struct scsi_cmnd *scsicmd,
if (cmdrsp->scsi.bufflen > devdata->max_buff_len)
devdata->max_buff_len = cmdrsp->scsi.bufflen;
- if (scsi_sg_count(scsicmd) > MAX_PHYS_INFO) {
- err = SCSI_MLQUEUE_DEVICE_BUSY;
+ if (scsi_sg_count(scsicmd) > MAX_PHYS_INFO)
goto err_del_scsipending_ent;
- }
/* convert buffer to phys information */
/* buffer is scatterlist - copy it out */
@@ -511,19 +507,17 @@ visorhba_queue_command_lck(struct scsi_cmnd *scsicmd,
}
cmdrsp->scsi.guest_phys_entries = scsi_sg_count(scsicmd);
- op = cdb[0];
if (!visorchannel_signalinsert(devdata->dev->visorchannel,
IOCHAN_TO_IOPART,
- cmdrsp)) {
+ cmdrsp))
/* queue must be full and we aren't going to wait */
- err = SCSI_MLQUEUE_DEVICE_BUSY;
goto err_del_scsipending_ent;
- }
+
return 0;
err_del_scsipending_ent:
del_scsipending_ent(devdata, insert_location);
- return err;
+ return SCSI_MLQUEUE_DEVICE_BUSY;
}
/**
@@ -759,11 +753,9 @@ do_scsi_linuxstat(struct uiscmdrsp *cmdrsp, struct scsi_cmnd *scsicmd)
struct visorhba_devdata *devdata;
struct visordisk_info *vdisk;
struct scsi_device *scsidev;
- struct sense_data *sd;
scsidev = scsicmd->device;
memcpy(scsicmd->sense_buffer, cmdrsp->scsi.sensebuf, MAX_SENSE_SIZE);
- sd = (struct sense_data *)scsicmd->sense_buffer;
/* Do not log errors for disk-not-present inquiries */
if ((cmdrsp->scsi.cmnd[0] == INQUIRY) &&
diff --git a/drivers/staging/unisys/visorinput/Kconfig b/drivers/staging/unisys/visorinput/Kconfig
index d83deb4137e8..3476d419d32c 100644
--- a/drivers/staging/unisys/visorinput/Kconfig
+++ b/drivers/staging/unisys/visorinput/Kconfig
@@ -6,5 +6,10 @@ config UNISYS_VISORINPUT
tristate "Unisys visorinput driver"
depends on UNISYSSPAR && UNISYS_VISORBUS && FB
---help---
- If you say Y here, you will enable the Unisys visorinput driver.
+ The Unisys s-Par visorinput driver provides a virtualized system
+ console (keyboard and mouse) that is accessible through the
+ s-Par firmware's user interface. s-Par provides video using the EFI
+ GOP protocol, so If this driver is not present, the Linux guest should
+ still boot with visible output in the partition desktop, but keyboard
+ and mouse interaction will not be available.
diff --git a/drivers/staging/unisys/visorinput/visorinput.c b/drivers/staging/unisys/visorinput/visorinput.c
index 5c16f6634368..38d4d5b884df 100644
--- a/drivers/staging/unisys/visorinput/visorinput.c
+++ b/drivers/staging/unisys/visorinput/visorinput.c
@@ -523,7 +523,7 @@ visorinput_channel_interrupt(struct visor_device *dev)
struct ultra_inputreport r;
int scancode, keycode;
struct input_dev *visorinput_dev;
- int xmotion, ymotion, zmotion, button;
+ int xmotion, ymotion, button;
int i;
struct visorinput_devdata *devdata = dev_get_drvdata(&dev->device);
@@ -604,12 +604,10 @@ visorinput_channel_interrupt(struct visor_device *dev)
}
break;
case inputaction_wheel_rotate_away:
- zmotion = r.activity.arg1;
input_report_rel(visorinput_dev, REL_WHEEL, 1);
input_sync(visorinput_dev);
break;
case inputaction_wheel_rotate_toward:
- zmotion = r.activity.arg1;
input_report_rel(visorinput_dev, REL_WHEEL, -1);
input_sync(visorinput_dev);
break;
diff --git a/drivers/staging/unisys/visornic/visornic_main.c b/drivers/staging/unisys/visornic/visornic_main.c
index 296b11cea247..05194707278a 100644
--- a/drivers/staging/unisys/visornic/visornic_main.c
+++ b/drivers/staging/unisys/visornic/visornic_main.c
@@ -1742,7 +1742,6 @@ poll_for_irq(unsigned long v)
atomic_set(&devdata->interrupt_rcvd, 0);
mod_timer(&devdata->irq_poll_timer, msecs_to_jiffies(2));
-
}
/**
diff --git a/drivers/staging/vme/devices/vme_pio2_cntr.c b/drivers/staging/vme/devices/vme_pio2_cntr.c
index 6335471faa36..486c30c4956f 100644
--- a/drivers/staging/vme/devices/vme_pio2_cntr.c
+++ b/drivers/staging/vme/devices/vme_pio2_cntr.c
@@ -61,7 +61,7 @@ int pio2_cntr_reset(struct pio2_card *card)
/* Ensure all counter interrupts are cleared */
do {
retval = vme_master_read(card->window, &reg, 1,
- PIO2_REGS_INT_STAT_CNTR);
+ PIO2_REGS_INT_STAT_CNTR);
if (retval < 0)
return retval;
} while (reg != 0);
diff --git a/drivers/staging/vme/devices/vme_pio2_core.c b/drivers/staging/vme/devices/vme_pio2_core.c
index 35c6ce5047de..4f3cdbcedb3e 100644
--- a/drivers/staging/vme/devices/vme_pio2_core.c
+++ b/drivers/staging/vme/devices/vme_pio2_core.c
@@ -90,7 +90,7 @@ static void pio2_int(int level, int vector, void *ptr)
case 4:
/* Channels 0 to 7 */
retval = vme_master_read(card->window, &reg, 1,
- PIO2_REGS_INT_STAT[vec - 1]);
+ PIO2_REGS_INT_STAT[vec - 1]);
if (retval < 0) {
dev_err(&card->vdev->dev,
"Unable to read IRQ status register\n");
@@ -100,8 +100,8 @@ static void pio2_int(int level, int vector, void *ptr)
channel = ((vec - 1) * 8) + i;
if (reg & PIO2_CHANNEL_BIT[channel])
dev_info(&card->vdev->dev,
- "Interrupt on I/O channel %d\n",
- channel);
+ "Interrupt on I/O channel %d\n",
+ channel);
}
break;
case 5:
@@ -215,7 +215,7 @@ static int pio2_probe(struct vme_dev *vdev)
u8 reg;
int vec;
- card = kzalloc(sizeof(struct pio2_card), GFP_KERNEL);
+ card = kzalloc(sizeof(*card), GFP_KERNEL);
if (!card) {
retval = -ENOMEM;
goto err_struct;
@@ -230,7 +230,7 @@ static int pio2_probe(struct vme_dev *vdev)
card->vdev = vdev;
for (i = 0; i < PIO2_VARIANT_LENGTH; i++) {
- if (isdigit(card->variant[i]) == 0) {
+ if (!isdigit(card->variant[i])) {
dev_err(&card->vdev->dev, "Variant invalid\n");
retval = -EINVAL;
goto err_variant;
@@ -289,7 +289,7 @@ static int pio2_probe(struct vme_dev *vdev)
}
retval = vme_master_set(card->window, 1, card->base, 0x10000, VME_A24,
- (VME_SCT | VME_USER | VME_DATA), VME_D16);
+ VME_SCT | VME_USER | VME_DATA, VME_D16);
if (retval) {
dev_err(&card->vdev->dev,
"Unable to configure VME master resource\n");
@@ -335,7 +335,7 @@ static int pio2_probe(struct vme_dev *vdev)
/* Set VME vector */
retval = vme_master_write(card->window, &card->irq_vector, 1,
- PIO2_REGS_VME_VECTOR);
+ PIO2_REGS_VME_VECTOR);
if (retval < 0)
return retval;
@@ -343,7 +343,7 @@ static int pio2_probe(struct vme_dev *vdev)
vec = card->irq_vector | PIO2_VME_VECTOR_SPUR;
retval = vme_irq_request(vdev, card->irq_level, vec,
- &pio2_int, (void *)card);
+ &pio2_int, card);
if (retval < 0) {
dev_err(&card->vdev->dev,
"Unable to attach VME interrupt vector0x%x, level 0x%x\n",
@@ -356,7 +356,7 @@ static int pio2_probe(struct vme_dev *vdev)
vec = card->irq_vector | PIO2_VECTOR_BANK[i];
retval = vme_irq_request(vdev, card->irq_level, vec,
- &pio2_int, (void *)card);
+ &pio2_int, card);
if (retval < 0) {
dev_err(&card->vdev->dev,
"Unable to attach VME interrupt vector0x%x, level 0x%x\n",
@@ -370,7 +370,7 @@ static int pio2_probe(struct vme_dev *vdev)
vec = card->irq_vector | PIO2_VECTOR_CNTR[i];
retval = vme_irq_request(vdev, card->irq_level, vec,
- &pio2_int, (void *)card);
+ &pio2_int, card);
if (retval < 0) {
dev_err(&card->vdev->dev,
"Unable to attach VME interrupt vector0x%x, level 0x%x\n",
@@ -397,7 +397,7 @@ static int pio2_probe(struct vme_dev *vdev)
dev_set_drvdata(&card->vdev->dev, card);
dev_info(&card->vdev->dev,
- "PIO2 (variant %s) configured at 0x%lx\n", card->variant,
+ "PIO2 (variant %s) configured at 0x%lx\n", card->variant,
card->base);
return 0;
diff --git a/drivers/staging/vme/devices/vme_pio2_gpio.c b/drivers/staging/vme/devices/vme_pio2_gpio.c
index 77901b345a71..df992c3cb5ce 100644
--- a/drivers/staging/vme/devices/vme_pio2_gpio.c
+++ b/drivers/staging/vme/devices/vme_pio2_gpio.c
@@ -37,14 +37,13 @@ static int pio2_gpio_get(struct gpio_chip *chip, unsigned int offset)
struct pio2_card *card = gpio_to_pio2_card(chip);
if ((card->bank[PIO2_CHANNEL_BANK[offset]].config == OUTPUT) |
- (card->bank[PIO2_CHANNEL_BANK[offset]].config == NOFIT)) {
-
+ (card->bank[PIO2_CHANNEL_BANK[offset]].config == NOFIT)) {
dev_err(&card->vdev->dev, "Channel not available as input\n");
return 0;
}
retval = vme_master_read(card->window, &reg, 1,
- PIO2_REGS_DATA[PIO2_CHANNEL_BANK[offset]]);
+ PIO2_REGS_DATA[PIO2_CHANNEL_BANK[offset]]);
if (retval < 0) {
dev_err(&card->vdev->dev, "Unable to read from GPIO\n");
return 0;
@@ -67,16 +66,15 @@ static int pio2_gpio_get(struct gpio_chip *chip, unsigned int offset)
return 0;
}
-static void pio2_gpio_set(struct gpio_chip *chip, unsigned int offset,
- int value)
+static void pio2_gpio_set(struct gpio_chip *chip,
+ unsigned int offset, int value)
{
u8 reg;
int retval;
struct pio2_card *card = gpio_to_pio2_card(chip);
if ((card->bank[PIO2_CHANNEL_BANK[offset]].config == INPUT) |
- (card->bank[PIO2_CHANNEL_BANK[offset]].config == NOFIT)) {
-
+ (card->bank[PIO2_CHANNEL_BANK[offset]].config == NOFIT)) {
dev_err(&card->vdev->dev, "Channel not available as output\n");
return;
}
@@ -89,7 +87,7 @@ static void pio2_gpio_set(struct gpio_chip *chip, unsigned int offset,
~PIO2_CHANNEL_BIT[offset];
retval = vme_master_write(card->window, &reg, 1,
- PIO2_REGS_DATA[PIO2_CHANNEL_BANK[offset]]);
+ PIO2_REGS_DATA[PIO2_CHANNEL_BANK[offset]]);
if (retval < 0) {
dev_err(&card->vdev->dev, "Unable to write to GPIO\n");
return;
@@ -105,7 +103,7 @@ static int pio2_gpio_dir_in(struct gpio_chip *chip, unsigned offset)
struct pio2_card *card = gpio_to_pio2_card(chip);
if ((card->bank[PIO2_CHANNEL_BANK[offset]].config == OUTPUT) |
- (card->bank[PIO2_CHANNEL_BANK[offset]].config == NOFIT)) {
+ (card->bank[PIO2_CHANNEL_BANK[offset]].config == NOFIT)) {
dev_err(&card->vdev->dev,
"Channel directionality not configurable at runtime\n");
@@ -124,7 +122,7 @@ static int pio2_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int value)
struct pio2_card *card = gpio_to_pio2_card(chip);
if ((card->bank[PIO2_CHANNEL_BANK[offset]].config == INPUT) |
- (card->bank[PIO2_CHANNEL_BANK[offset]].config == NOFIT)) {
+ (card->bank[PIO2_CHANNEL_BANK[offset]].config == NOFIT)) {
dev_err(&card->vdev->dev,
"Channel directionality not configurable at runtime\n");
@@ -150,7 +148,7 @@ int pio2_gpio_reset(struct pio2_card *card)
/* Zero output registers */
for (i = 0; i < 4; i++) {
retval = vme_master_write(card->window, &data, 1,
- PIO2_REGS_DATA[i]);
+ PIO2_REGS_DATA[i]);
if (retval < 0)
return retval;
card->bank[i].value = 0;
@@ -159,12 +157,12 @@ int pio2_gpio_reset(struct pio2_card *card)
/* Set input interrupt masks */
for (i = 0; i < 4; i++) {
retval = vme_master_write(card->window, &data, 1,
- PIO2_REGS_INT_MASK[i * 2]);
+ PIO2_REGS_INT_MASK[i * 2]);
if (retval < 0)
return retval;
retval = vme_master_write(card->window, &data, 1,
- PIO2_REGS_INT_MASK[(i * 2) + 1]);
+ PIO2_REGS_INT_MASK[(i * 2) + 1]);
if (retval < 0)
return retval;
@@ -176,7 +174,7 @@ int pio2_gpio_reset(struct pio2_card *card)
for (i = 0; i < 4; i++) {
do {
retval = vme_master_read(card->window, &data, 1,
- PIO2_REGS_INT_STAT[i]);
+ PIO2_REGS_INT_STAT[i]);
if (retval < 0)
return retval;
} while (data != 0);
@@ -192,7 +190,7 @@ int pio2_gpio_init(struct pio2_card *card)
label = kasprintf(GFP_KERNEL,
"%s@%s", driver_name, dev_name(&card->vdev->dev));
- if (label == NULL)
+ if (!label)
return -ENOMEM;
card->gc.label = label;
@@ -207,7 +205,7 @@ int pio2_gpio_init(struct pio2_card *card)
card->gc.set = pio2_gpio_set;
/* This function adds a memory mapped GPIO chip */
- retval = gpiochip_add(&(card->gc));
+ retval = gpiochip_add(&card->gc);
if (retval) {
dev_err(&card->vdev->dev, "Unable to register GPIO\n");
kfree(card->gc.label);
@@ -220,7 +218,7 @@ void pio2_gpio_exit(struct pio2_card *card)
{
const char *label = card->gc.label;
- gpiochip_remove(&(card->gc));
+ gpiochip_remove(&card->gc);
kfree(label);
}
diff --git a/drivers/staging/vme/devices/vme_user.c b/drivers/staging/vme/devices/vme_user.c
index 8e61a3b3e7e4..b95883bc68fe 100644
--- a/drivers/staging/vme/devices/vme_user.c
+++ b/drivers/staging/vme/devices/vme_user.c
@@ -308,8 +308,8 @@ static int vme_user_ioctl(struct inode *inode, struct file *file,
switch (cmd) {
case VME_IRQ_GEN:
copied = copy_from_user(&irq_req, argp,
- sizeof(struct vme_irq_id));
- if (copied != 0) {
+ sizeof(irq_req));
+ if (copied) {
pr_warn("Partial copy from userspace\n");
return -EFAULT;
}
@@ -322,7 +322,7 @@ static int vme_user_ioctl(struct inode *inode, struct file *file,
case MASTER_MINOR:
switch (cmd) {
case VME_GET_MASTER:
- memset(&master, 0, sizeof(struct vme_master));
+ memset(&master, 0, sizeof(master));
/* XXX We do not want to push aspace, cycle and width
* to userspace as they are
@@ -334,8 +334,8 @@ static int vme_user_ioctl(struct inode *inode, struct file *file,
&master.cycle, &master.dwidth);
copied = copy_to_user(argp, &master,
- sizeof(struct vme_master));
- if (copied != 0) {
+ sizeof(master));
+ if (copied) {
pr_warn("Partial copy to userspace\n");
return -EFAULT;
}
@@ -350,7 +350,7 @@ static int vme_user_ioctl(struct inode *inode, struct file *file,
}
copied = copy_from_user(&master, argp, sizeof(master));
- if (copied != 0) {
+ if (copied) {
pr_warn("Partial copy from userspace\n");
return -EFAULT;
}
@@ -368,7 +368,7 @@ static int vme_user_ioctl(struct inode *inode, struct file *file,
case SLAVE_MINOR:
switch (cmd) {
case VME_GET_SLAVE:
- memset(&slave, 0, sizeof(struct vme_slave));
+ memset(&slave, 0, sizeof(slave));
/* XXX We do not want to push aspace, cycle and width
* to userspace as they are
@@ -379,8 +379,8 @@ static int vme_user_ioctl(struct inode *inode, struct file *file,
&slave.aspace, &slave.cycle);
copied = copy_to_user(argp, &slave,
- sizeof(struct vme_slave));
- if (copied != 0) {
+ sizeof(slave));
+ if (copied) {
pr_warn("Partial copy to userspace\n");
return -EFAULT;
}
@@ -390,7 +390,7 @@ static int vme_user_ioctl(struct inode *inode, struct file *file,
case VME_SET_SLAVE:
copied = copy_from_user(&slave, argp, sizeof(slave));
- if (copied != 0) {
+ if (copied) {
pr_warn("Partial copy from userspace\n");
return -EFAULT;
}
@@ -757,7 +757,7 @@ static int __init vme_user_init(void)
* we just change the code in vme_user_match().
*/
retval = vme_register_driver(&vme_user_driver, VME_MAX_SLOTS);
- if (retval != 0)
+ if (retval)
goto err_reg;
return retval;
diff --git a/drivers/staging/vme/devices/vme_user.h b/drivers/staging/vme/devices/vme_user.h
index b8cc7bc78a73..a6cb75686fa4 100644
--- a/drivers/staging/vme/devices/vme_user.h
+++ b/drivers/staging/vme/devices/vme_user.h
@@ -20,7 +20,6 @@ struct vme_master {
#endif
} __packed;
-
/*
* IOCTL Commands and structures
*/
@@ -28,7 +27,6 @@ struct vme_master {
/* Magic number for use in ioctls */
#define VME_IOC_MAGIC 0xAE
-
/* VMEbus Slave Window Configuration Structure */
struct vme_slave {
__u32 enable; /* State of Window */
diff --git a/drivers/staging/vt6656/baseband.c b/drivers/staging/vt6656/baseband.c
index e5be261f2e69..9417c935fc30 100644
--- a/drivers/staging/vt6656/baseband.c
+++ b/drivers/staging/vt6656/baseband.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*
* File: baseband.c
*
diff --git a/drivers/staging/vt6656/baseband.h b/drivers/staging/vt6656/baseband.h
index 771ea4054174..807a5809b5d9 100644
--- a/drivers/staging/vt6656/baseband.h
+++ b/drivers/staging/vt6656/baseband.h
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*
* File: baseband.h
*
diff --git a/drivers/staging/vt6656/card.c b/drivers/staging/vt6656/card.c
index 927243ebcb56..a382fc6aa9d3 100644
--- a/drivers/staging/vt6656/card.c
+++ b/drivers/staging/vt6656/card.c
@@ -12,9 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* File: card.c
* Purpose: Provide functions to setup NIC operation mode
diff --git a/drivers/staging/vt6656/card.h b/drivers/staging/vt6656/card.h
index 03fc1678896b..c2cde7e92c8f 100644
--- a/drivers/staging/vt6656/card.h
+++ b/drivers/staging/vt6656/card.h
@@ -12,9 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* File: card.h
*
diff --git a/drivers/staging/vt6656/channel.c b/drivers/staging/vt6656/channel.c
index 8412d0532fb2..a0fe288c1322 100644
--- a/drivers/staging/vt6656/channel.c
+++ b/drivers/staging/vt6656/channel.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*
* File: channel.c
*
diff --git a/drivers/staging/vt6656/channel.h b/drivers/staging/vt6656/channel.h
index 21c080803714..fcea6995fe26 100644
--- a/drivers/staging/vt6656/channel.h
+++ b/drivers/staging/vt6656/channel.h
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*
* File: channel.h
*
diff --git a/drivers/staging/vt6656/desc.h b/drivers/staging/vt6656/desc.h
index f79af8513ff2..59e3071021bd 100644
--- a/drivers/staging/vt6656/desc.h
+++ b/drivers/staging/vt6656/desc.h
@@ -12,9 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* File: desc.h
*
diff --git a/drivers/staging/vt6656/device.h b/drivers/staging/vt6656/device.h
index dec36f296f3d..76b5f4127f95 100644
--- a/drivers/staging/vt6656/device.h
+++ b/drivers/staging/vt6656/device.h
@@ -12,9 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* File: device.h
*
diff --git a/drivers/staging/vt6656/dpc.c b/drivers/staging/vt6656/dpc.c
index e6367ed3b0bb..6019aac8bdd5 100644
--- a/drivers/staging/vt6656/dpc.c
+++ b/drivers/staging/vt6656/dpc.c
@@ -12,9 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* File: dpc.c
*
diff --git a/drivers/staging/vt6656/dpc.h b/drivers/staging/vt6656/dpc.h
index 95e0e83a487e..5a92bd86cee2 100644
--- a/drivers/staging/vt6656/dpc.h
+++ b/drivers/staging/vt6656/dpc.h
@@ -12,9 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* File: dpc.h
*
diff --git a/drivers/staging/vt6656/firmware.c b/drivers/staging/vt6656/firmware.c
index d440f284bf18..1b48f9c86f63 100644
--- a/drivers/staging/vt6656/firmware.c
+++ b/drivers/staging/vt6656/firmware.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*
* File: baseband.c
*
diff --git a/drivers/staging/vt6656/firmware.h b/drivers/staging/vt6656/firmware.h
index d594dbe1c147..e2b54acb8fdb 100644
--- a/drivers/staging/vt6656/firmware.h
+++ b/drivers/staging/vt6656/firmware.h
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*
* File: firmware.h
*
diff --git a/drivers/staging/vt6656/int.c b/drivers/staging/vt6656/int.c
index 14b8ebc6508d..8d05acbc0e23 100644
--- a/drivers/staging/vt6656/int.c
+++ b/drivers/staging/vt6656/int.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*
* File: int.c
*
diff --git a/drivers/staging/vt6656/int.h b/drivers/staging/vt6656/int.h
index 154605c63947..97e55bacbb7c 100644
--- a/drivers/staging/vt6656/int.h
+++ b/drivers/staging/vt6656/int.h
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*
* File: int.h
*
diff --git a/drivers/staging/vt6656/key.c b/drivers/staging/vt6656/key.c
index 181745d8e250..0246a8fc47fe 100644
--- a/drivers/staging/vt6656/key.c
+++ b/drivers/staging/vt6656/key.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*
* File: key.c
*
@@ -163,7 +159,6 @@ int vnt_set_keys(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
}
-
if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
vnt_set_keymode(hw, mac_addr, key, VNT_KEY_PAIRWISE,
key_dec_mode, true);
diff --git a/drivers/staging/vt6656/key.h b/drivers/staging/vt6656/key.h
index 3cb1291055ed..7861faf5138f 100644
--- a/drivers/staging/vt6656/key.h
+++ b/drivers/staging/vt6656/key.h
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*
* File: key.h
*
diff --git a/drivers/staging/vt6656/mac.c b/drivers/staging/vt6656/mac.c
index 5dfac05b9cf1..eeed16e9124e 100644
--- a/drivers/staging/vt6656/mac.c
+++ b/drivers/staging/vt6656/mac.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*
* File: mac.c
*
diff --git a/drivers/staging/vt6656/mac.h b/drivers/staging/vt6656/mac.h
index d53fcef87b4a..4c6e610f1bc1 100644
--- a/drivers/staging/vt6656/mac.h
+++ b/drivers/staging/vt6656/mac.h
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*
* File: mac.h
*
diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c
index 01e642db311e..ee8d1e1a24c2 100644
--- a/drivers/staging/vt6656/main_usb.c
+++ b/drivers/staging/vt6656/main_usb.c
@@ -12,9 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* File: main_usb.c
*
diff --git a/drivers/staging/vt6656/power.c b/drivers/staging/vt6656/power.c
index 13afce27951c..c025dab0f62c 100644
--- a/drivers/staging/vt6656/power.c
+++ b/drivers/staging/vt6656/power.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*
* File: power.c
*
diff --git a/drivers/staging/vt6656/power.h b/drivers/staging/vt6656/power.h
index 7696b714850c..9d1ebb695f9d 100644
--- a/drivers/staging/vt6656/power.h
+++ b/drivers/staging/vt6656/power.h
@@ -12,9 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* File: power.h
*
diff --git a/drivers/staging/vt6656/rf.c b/drivers/staging/vt6656/rf.c
index c4286ccac320..816206c92f57 100644
--- a/drivers/staging/vt6656/rf.c
+++ b/drivers/staging/vt6656/rf.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*
* File: rf.c
*
diff --git a/drivers/staging/vt6656/rf.h b/drivers/staging/vt6656/rf.h
index 3acdc65b1e56..c3d4f06d65f4 100644
--- a/drivers/staging/vt6656/rf.h
+++ b/drivers/staging/vt6656/rf.h
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*
* File: rf.h
*
diff --git a/drivers/staging/vt6656/rxtx.c b/drivers/staging/vt6656/rxtx.c
index efb54f53b4f9..a0c69b697901 100644
--- a/drivers/staging/vt6656/rxtx.c
+++ b/drivers/staging/vt6656/rxtx.c
@@ -12,9 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* File: rxtx.c
*
diff --git a/drivers/staging/vt6656/rxtx.h b/drivers/staging/vt6656/rxtx.h
index 90b34ab2f6ce..4a79c404275b 100644
--- a/drivers/staging/vt6656/rxtx.h
+++ b/drivers/staging/vt6656/rxtx.h
@@ -12,9 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* File: rxtx.h
*
diff --git a/drivers/staging/vt6656/usbpipe.c b/drivers/staging/vt6656/usbpipe.c
index c975c3b87093..351a99f3d684 100644
--- a/drivers/staging/vt6656/usbpipe.c
+++ b/drivers/staging/vt6656/usbpipe.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*
* File: usbpipe.c
*
diff --git a/drivers/staging/vt6656/usbpipe.h b/drivers/staging/vt6656/usbpipe.h
index e74aa0809928..8bafd9aee1fa 100644
--- a/drivers/staging/vt6656/usbpipe.h
+++ b/drivers/staging/vt6656/usbpipe.h
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*
* File: usbpipe.h
*
diff --git a/drivers/staging/vt6656/wcmd.c b/drivers/staging/vt6656/wcmd.c
index 3cbf4791bac1..4846a898d39b 100644
--- a/drivers/staging/vt6656/wcmd.c
+++ b/drivers/staging/vt6656/wcmd.c
@@ -12,9 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* File: wcmd.c
*
diff --git a/drivers/staging/vt6656/wcmd.h b/drivers/staging/vt6656/wcmd.h
index 2b0ee285eb0b..764c09ccd42d 100644
--- a/drivers/staging/vt6656/wcmd.h
+++ b/drivers/staging/vt6656/wcmd.h
@@ -12,9 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* File: wcmd.h
*
diff --git a/drivers/staging/wilc1000/Kconfig b/drivers/staging/wilc1000/Kconfig
index ee51b4278088..dce9cee9134a 100644
--- a/drivers/staging/wilc1000/Kconfig
+++ b/drivers/staging/wilc1000/Kconfig
@@ -1,41 +1,12 @@
-config WILC1000_DRIVER
- bool "WILC1000 support (WiFi only)"
- depends on CFG80211 && WEXT_CORE && INET
- ---help---
- This module only support IEEE 802.11n WiFi.
-
-if WILC1000_DRIVER
-
config WILC1000
tristate
-
-choice
- prompt "Memory Allocation"
- default WILC1000_PREALLOCATE_AT_LOADING_DRIVER
-
-config WILC1000_PREALLOCATE_AT_LOADING_DRIVER
- bool "Preallocate memory at loading driver"
+ select WIRELESS_EXT
---help---
- This choice supports static allocation of the memory
- for the receive buffer. The driver will allocate the RX buffer
- during initial time. The driver will also free the buffer
- by calling network device stop.
-
-config WILC1000_DYNAMICALLY_ALLOCATE_MEMROY
- bool "Dynamically allocate memory in real time"
- ---help---
- This choice supports dynamic allocation of the memory
- for the receive buffer. The driver will allocate the RX buffer
- when it is required.
-endchoice
-
-choice
- prompt "Bus Type"
- default WILC1000_SDIO
+ This module only support IEEE 802.11n WiFi.
config WILC1000_SDIO
- bool "SDIO support"
- depends on MMC
+ tristate "Atmel WILC1000 SDIO (WiFi only)"
+ depends on CFG80211 && INET && MMC
select WILC1000
---help---
This module adds support for the SDIO interface of adapters using
@@ -48,9 +19,9 @@ config WILC1000_SDIO
this if your platform is using the SDIO bus.
config WILC1000_SPI
- depends on SPI
+ tristate "Atmel WILC1000 SPI (WiFi only)"
+ depends on CFG80211 && INET && SPI
select WILC1000
- bool "SPI support"
---help---
This module adds support for the SPI interface of adapters using
WILC1000 chipset. The Atmel WILC1000 has a Serial Peripheral
@@ -59,10 +30,9 @@ config WILC1000_SPI
full-duplex slave synchronous serial interface that is available
immediately following reset when pin 9 (SDIO_SPI_CFG) is tied to
VDDIO. Select this if your platform is using the SPI bus.
-endchoice
config WILC1000_HW_OOB_INTR
- bool "Use out of band interrupt"
+ bool "WILC1000 out of band interrupt"
depends on WILC1000_SDIO
default n
---help---
@@ -71,5 +41,3 @@ config WILC1000_HW_OOB_INTR
mechanism for SDIO host controllers that don't support SDIO interrupt.
Select this option If the SDIO host controller in your platform
doesn't support SDIO time devision interrupt.
-
-endif
diff --git a/drivers/staging/wilc1000/Makefile b/drivers/staging/wilc1000/Makefile
index 64c2f1b83dfb..20a5cb9d4f4c 100644
--- a/drivers/staging/wilc1000/Makefile
+++ b/drivers/staging/wilc1000/Makefile
@@ -1,28 +1,20 @@
obj-$(CONFIG_WILC1000) += wilc1000.o
-ccflags-$(CONFIG_WILC1000_SDIO) += -DWILC_SDIO -DCOMPLEMENT_BOOT
-ccflags-$(CONFIG_WILC1000_HW_OOB_INTR) += -DWILC_SDIO_IRQ_GPIO
-ccflags-$(CONFIG_WILC1000_SPI) += -DWILC_SPI
-
ccflags-y += -DSTA_FIRMWARE=\"atmel/wilc1000_fw.bin\" \
-DAP_FIRMWARE=\"atmel/wilc1000_ap_fw.bin\" \
-DP2P_CONCURRENCY_FIRMWARE=\"atmel/wilc1000_p2p_fw.bin\"
-ccflags-y += -I$(src)/ -D__CHECK_ENDIAN__ -DWILC_ASIC_A0 \
- -Wno-unused-function -DWILC_DEBUGFS
+ccflags-y += -I$(src)/ -DWILC_ASIC_A0 -DWILC_DEBUGFS
#ccflags-y += -DTCP_ACK_FILTER
-ccflags-$(CONFIG_WILC1000_PREALLOCATE_AT_LOADING_DRIVER) += -DMEMORY_STATIC \
- -DWILC_PREALLOC_AT_INSMOD
-
-ccflags-$(CONFIG_WILC1000_DYNAMICALLY_ALLOCATE_MEMROY) += -DWILC_NORMAL_ALLOC
-
-
wilc1000-objs := wilc_wfi_cfgoperations.o linux_wlan.o linux_mon.o \
wilc_msgqueue.o \
coreconfigurator.o host_interface.o \
- wilc_sdio.o wilc_spi.o wilc_wlan_cfg.o wilc_debugfs.o \
+ wilc_wlan_cfg.o wilc_debugfs.o \
wilc_wlan.o
-wilc1000-$(CONFIG_WILC1000_SDIO) += linux_wlan_sdio.o
-wilc1000-$(CONFIG_WILC1000_SPI) += linux_wlan_spi.o
+obj-$(CONFIG_WILC1000_SDIO) += wilc1000-sdio.o
+wilc1000-sdio-objs += wilc_sdio.o
+
+obj-$(CONFIG_WILC1000_SPI) += wilc1000-spi.o
+wilc1000-spi-objs += wilc_spi.o
diff --git a/drivers/staging/wilc1000/coreconfigurator.c b/drivers/staging/wilc1000/coreconfigurator.c
index 9568bdb6319b..2d4d3f190c01 100644
--- a/drivers/staging/wilc1000/coreconfigurator.c
+++ b/drivers/staging/wilc1000/coreconfigurator.c
@@ -287,7 +287,7 @@ static inline u16 get_asoc_id(u8 *data)
return asoc_id;
}
-u8 *get_tim_elm(u8 *pu8msa, u16 u16RxLen, u16 u16TagParamOffset)
+static u8 *get_tim_elm(u8 *pu8msa, u16 u16RxLen, u16 u16TagParamOffset)
{
u16 u16index;
@@ -315,7 +315,7 @@ u8 *get_tim_elm(u8 *pu8msa, u16 u16RxLen, u16 u16TagParamOffset)
/* This function gets the current channel information from
* the 802.11n beacon/probe response frame */
-u8 get_current_channel_802_11n(u8 *pu8msa, u16 u16RxLen)
+static u8 get_current_channel_802_11n(u8 *pu8msa, u16 u16RxLen)
{
u16 index;
@@ -344,7 +344,7 @@ u8 get_current_channel_802_11n(u8 *pu8msa, u16 u16RxLen)
* @date 1 Mar 2012
* @version 1.0
*/
-s32 parse_network_info(u8 *pu8MsgBuffer, tstrNetworkInfo **ppstrNetworkInfo)
+s32 wilc_parse_network_info(u8 *pu8MsgBuffer, tstrNetworkInfo **ppstrNetworkInfo)
{
tstrNetworkInfo *pstrNetworkInfo = NULL;
u8 u8MsgType = 0;
@@ -436,7 +436,7 @@ s32 parse_network_info(u8 *pu8MsgBuffer, tstrNetworkInfo **ppstrNetworkInfo)
/* Get DTIM Period */
pu8TimElm = get_tim_elm(pu8msa, u16RxLen + FCS_LEN, u8index);
- if (pu8TimElm != NULL)
+ if (pu8TimElm)
pstrNetworkInfo->u8DtimPeriod = pu8TimElm[3];
pu8IEs = &pu8msa[MAC_HDR_LEN + TIME_STAMP_LEN + BEACON_INTERVAL_LEN + CAP_INFO_LEN];
u16IEsLen = u16RxLen - (MAC_HDR_LEN + TIME_STAMP_LEN + BEACON_INTERVAL_LEN + CAP_INFO_LEN);
@@ -466,12 +466,12 @@ s32 parse_network_info(u8 *pu8MsgBuffer, tstrNetworkInfo **ppstrNetworkInfo)
* @date 1 Mar 2012
* @version 1.0
*/
-s32 DeallocateNetworkInfo(tstrNetworkInfo *pstrNetworkInfo)
+s32 wilc_dealloc_network_info(tstrNetworkInfo *pstrNetworkInfo)
{
s32 s32Error = 0;
- if (pstrNetworkInfo != NULL) {
- if (pstrNetworkInfo->pu8IEs != NULL) {
+ if (pstrNetworkInfo) {
+ if (pstrNetworkInfo->pu8IEs) {
kfree(pstrNetworkInfo->pu8IEs);
pstrNetworkInfo->pu8IEs = NULL;
} else {
@@ -499,7 +499,7 @@ s32 DeallocateNetworkInfo(tstrNetworkInfo *pstrNetworkInfo)
* @date 2 Apr 2012
* @version 1.0
*/
-s32 ParseAssocRespInfo(u8 *pu8Buffer, u32 u32BufferLen,
+s32 wilc_parse_assoc_resp_info(u8 *pu8Buffer, u32 u32BufferLen,
tstrConnectRespInfo **ppstrConnectRespInfo)
{
s32 s32Error = 0;
@@ -551,12 +551,12 @@ s32 ParseAssocRespInfo(u8 *pu8Buffer, u32 u32BufferLen,
* @date 2 Apr 2012
* @version 1.0
*/
-s32 DeallocateAssocRespInfo(tstrConnectRespInfo *pstrConnectRespInfo)
+s32 wilc_dealloc_assoc_resp_info(tstrConnectRespInfo *pstrConnectRespInfo)
{
s32 s32Error = 0;
- if (pstrConnectRespInfo != NULL) {
- if (pstrConnectRespInfo->pu8RespIEs != NULL) {
+ if (pstrConnectRespInfo) {
+ if (pstrConnectRespInfo->pu8RespIEs) {
kfree(pstrConnectRespInfo->pu8RespIEs);
pstrConnectRespInfo->pu8RespIEs = NULL;
} else {
@@ -588,7 +588,8 @@ s32 DeallocateAssocRespInfo(tstrConnectRespInfo *pstrConnectRespInfo)
* @date 1 Mar 2012
* @version 1.0
*/
-s32 send_config_pkt(u8 mode, struct wid *wids, u32 count, u32 drv)
+s32 wilc_send_config_pkt(struct wilc *wilc, u8 mode, struct wid *wids,
+ u32 count, u32 drv)
{
s32 counter = 0, ret = 0;
@@ -596,11 +597,11 @@ s32 send_config_pkt(u8 mode, struct wid *wids, u32 count, u32 drv)
for (counter = 0; counter < count; counter++) {
PRINT_INFO(CORECONFIG_DBG, "Sending CFG packet [%d][%d]\n", !counter,
(counter == count - 1));
- if (!wilc_wlan_cfg_get(!counter,
+ if (!wilc_wlan_cfg_get(wilc, !counter,
wids[counter].id,
(counter == count - 1),
drv)) {
- ret = -1;
+ ret = -ETIMEDOUT;
printk("[Sendconfigpkt]Get Timed out\n");
break;
}
@@ -611,18 +612,17 @@ s32 send_config_pkt(u8 mode, struct wid *wids, u32 count, u32 drv)
wids[counter].id,
wids[counter].val,
wids[counter].size);
-
}
} else if (mode == SET_CFG) {
for (counter = 0; counter < count; counter++) {
PRINT_D(CORECONFIG_DBG, "Sending config SET PACKET WID:%x\n", wids[counter].id);
- if (!wilc_wlan_cfg_set(!counter,
+ if (!wilc_wlan_cfg_set(wilc, !counter,
wids[counter].id,
wids[counter].val,
wids[counter].size,
(counter == count - 1),
drv)) {
- ret = -1;
+ ret = -ETIMEDOUT;
printk("[Sendconfigpkt]Set Timed out\n");
break;
}
diff --git a/drivers/staging/wilc1000/coreconfigurator.h b/drivers/staging/wilc1000/coreconfigurator.h
index 6294d929a800..fc43d04ca1da 100644
--- a/drivers/staging/wilc1000/coreconfigurator.h
+++ b/drivers/staging/wilc1000/coreconfigurator.h
@@ -72,7 +72,7 @@ typedef enum {
struct wid {
u16 id;
- enum WID_TYPE type;
+ enum wid_type type;
s32 size;
s8 *val;
};
@@ -127,16 +127,18 @@ typedef struct {
size_t ie_len;
} tstrDisconnectNotifInfo;
-s32 send_config_pkt(u8 mode, struct wid *wids, u32 count, u32 drv);
-s32 parse_network_info(u8 *pu8MsgBuffer, tstrNetworkInfo **ppstrNetworkInfo);
-s32 DeallocateNetworkInfo(tstrNetworkInfo *pstrNetworkInfo);
+s32 wilc_send_config_pkt(struct wilc *wilc, u8 mode, struct wid *wids,
+ u32 count, u32 drv);
+s32 wilc_parse_network_info(u8 *pu8MsgBuffer, tstrNetworkInfo **ppstrNetworkInfo);
+s32 wilc_dealloc_network_info(tstrNetworkInfo *pstrNetworkInfo);
-s32 ParseAssocRespInfo(u8 *pu8Buffer, u32 u32BufferLen,
+s32 wilc_parse_assoc_resp_info(u8 *pu8Buffer, u32 u32BufferLen,
tstrConnectRespInfo **ppstrConnectRespInfo);
-s32 DeallocateAssocRespInfo(tstrConnectRespInfo *pstrConnectRespInfo);
-
-void NetworkInfoReceived(u8 *pu8Buffer, u32 u32Length);
-void GnrlAsyncInfoReceived(u8 *pu8Buffer, u32 u32Length);
-void host_int_ScanCompleteReceived(u8 *pu8Buffer, u32 u32Length);
-
+s32 wilc_dealloc_assoc_resp_info(tstrConnectRespInfo *pstrConnectRespInfo);
+void wilc_scan_complete_received(struct wilc *wilc, u8 *pu8Buffer,
+ u32 u32Length);
+void wilc_network_info_received(struct wilc *wilc, u8 *pu8Buffer,
+ u32 u32Length);
+void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *pu8Buffer,
+ u32 u32Length);
#endif
diff --git a/drivers/staging/wilc1000/host_interface.c b/drivers/staging/wilc1000/host_interface.c
index dbbe72c7e255..8c7752034032 100644
--- a/drivers/staging/wilc1000/host_interface.c
+++ b/drivers/staging/wilc1000/host_interface.c
@@ -4,17 +4,12 @@
#include <linux/delay.h>
#include "host_interface.h"
#include "coreconfigurator.h"
+#include "wilc_wlan.h"
#include "wilc_wlan_if.h"
#include "wilc_msgqueue.h"
#include <linux/etherdevice.h>
#include "wilc_wfi_netdevice.h"
-extern u8 connecting;
-
-extern struct timer_list hDuringIpTimer;
-
-extern u8 g_wilc_initialized;
-
#define HOST_IF_MSG_SCAN 0
#define HOST_IF_MSG_CONNECT 1
#define HOST_IF_MSG_RCVD_GNRL_ASYNC_INFO 2
@@ -48,7 +43,6 @@ extern u8 g_wilc_initialized;
#define HOST_IF_MSG_FLUSH_CONNECT 30
#define HOST_IF_MSG_GET_STATISTICS 31
#define HOST_IF_MSG_SET_MULTICAST_FILTER 32
-#define HOST_IF_MSG_ADD_BA_SESSION 33
#define HOST_IF_MSG_DEL_BA_SESSION 34
#define HOST_IF_MSG_Q_IDLE 35
#define HOST_IF_MSG_DEL_ALL_STA 36
@@ -199,7 +193,7 @@ union message_body {
struct host_if_msg {
u16 id;
union message_body body;
- struct host_if_drv *drv;
+ struct wilc_vif *vif;
};
struct join_bss_param {
@@ -231,10 +225,9 @@ struct join_bss_param {
u8 start_time[4];
};
-static struct host_if_drv *wfidrv_list[NUM_CONCURRENT_IFC + 1];
struct host_if_drv *terminated_handle;
-bool g_obtainingIP;
-u8 P2P_LISTEN_STATE;
+bool wilc_optaining_ip;
+static u8 P2P_LISTEN_STATE;
static struct task_struct *hif_thread_handler;
static WILC_MsgQueueHandle hif_msg_q;
static struct semaphore hif_sema_thread;
@@ -243,7 +236,7 @@ static struct semaphore hif_sema_wait_response;
static struct semaphore hif_sema_deinit;
static struct timer_list periodic_rssi;
-u8 gau8MulticastMacAddrList[WILC_MULTICAST_TABLE_SIZE][ETH_ALEN];
+u8 wilc_multicast_mac_addr_list[WILC_MULTICAST_TABLE_SIZE][ETH_ALEN];
static u8 rcv_assoc_resp[MAX_ASSOC_RESP_FRAME_SIZE];
@@ -259,86 +252,57 @@ static u8 del_beacon;
static u32 clients_count;
static u8 *join_req;
-u8 *info_element;
+static u8 *info_element;
static u8 mode_11i;
-u8 auth_type;
-u32 join_req_size;
+static u8 auth_type;
+static u32 join_req_size;
static u32 info_element_size;
-static struct host_if_drv *join_req_drv;
+static struct wilc_vif *join_req_vif;
#define REAL_JOIN_REQ 0
#define FLUSHED_JOIN_REQ 1
#define FLUSHED_BYTE_POS 79
static void *host_int_ParseJoinBssParam(tstrNetworkInfo *ptstrNetworkInfo);
-extern void chip_sleep_manually(u32 u32SleepTime);
-extern int linux_wlan_get_num_conn_ifcs(void);
-
-static int add_handler_in_list(struct host_if_drv *handler)
+/* The u8IfIdx starts from 0 to NUM_CONCURRENT_IFC -1, but 0 index used as
+ * special purpose in wilc device, so we add 1 to the index to starts from 1.
+ * As a result, the returned index will be 1 to NUM_CONCURRENT_IFC.
+ */
+int wilc_get_vif_idx(struct wilc_vif *vif)
{
- int i;
-
- for (i = 1; i < ARRAY_SIZE(wfidrv_list); i++) {
- if (!wfidrv_list[i]) {
- wfidrv_list[i] = handler;
- return 0;
- }
- }
-
- return -ENOBUFS;
+ return vif->u8IfIdx + 1;
}
-static int remove_handler_in_list(struct host_if_drv *handler)
+/* We need to minus 1 from idx which is from wilc device to get real index
+ * of wilc->vif[], because we add 1 when pass to wilc device in the function
+ * wilc_get_vif_idx.
+ * As a result, the index should be between 0 and NUM_CONCURRENT_IFC -1.
+ */
+static struct wilc_vif *wilc_get_vif_from_idx(struct wilc *wilc, int idx)
{
- int i;
+ int index = idx - 1;
- for (i = 1; i < ARRAY_SIZE(wfidrv_list); i++) {
- if (wfidrv_list[i] == handler) {
- wfidrv_list[i] = NULL;
- return 0;
- }
- }
-
- return -EINVAL;
-}
-
-static int get_id_from_handler(struct host_if_drv *handler)
-{
- int i;
-
- if (!handler)
- return 0;
-
- for (i = 1; i < ARRAY_SIZE(wfidrv_list); i++) {
- if (wfidrv_list[i] == handler)
- return i;
- }
-
- return 0;
-}
-
-static struct host_if_drv *get_handler_from_id(int id)
-{
- if (id <= 0 || id >= ARRAY_SIZE(wfidrv_list))
+ if (index < 0 || index >= NUM_CONCURRENT_IFC)
return NULL;
- return wfidrv_list[id];
+
+ return wilc->vif[index];
}
-static s32 Handle_SetChannel(struct host_if_drv *hif_drv,
- struct channel_attr *pstrHostIFSetChan)
+static s32 handle_set_channel(struct wilc_vif *vif,
+ struct channel_attr *hif_set_ch)
{
s32 result = 0;
struct wid wid;
wid.id = (u16)WID_CURRENT_CHANNEL;
wid.type = WID_CHAR;
- wid.val = (char *)&pstrHostIFSetChan->set_ch;
+ wid.val = (char *)&hif_set_ch->set_ch;
wid.size = sizeof(char);
PRINT_D(HOSTINF_DBG, "Setting channel\n");
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result) {
PRINT_ER("Failed to set channel\n");
@@ -348,21 +312,21 @@ static s32 Handle_SetChannel(struct host_if_drv *hif_drv,
return result;
}
-static s32 Handle_SetWfiDrvHandler(struct host_if_drv *hif_drv,
- struct drv_handler *pstrHostIfSetDrvHandler)
+static s32 handle_set_wfi_drv_handler(struct wilc_vif *vif,
+ struct drv_handler *hif_drv_handler)
{
s32 result = 0;
struct wid wid;
wid.id = (u16)WID_SET_DRV_HANDLER;
wid.type = WID_INT;
- wid.val = (s8 *)&pstrHostIfSetDrvHandler->handler;
+ wid.val = (s8 *)&hif_drv_handler->handler;
wid.size = sizeof(u32);
- result = send_config_pkt(SET_CFG, &wid, 1,
- pstrHostIfSetDrvHandler->handler);
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ hif_drv_handler->handler);
- if (!hif_drv)
+ if (!hif_drv_handler->handler)
up(&hif_sema_driver);
if (result) {
@@ -373,21 +337,21 @@ static s32 Handle_SetWfiDrvHandler(struct host_if_drv *hif_drv,
return result;
}
-static s32 Handle_SetOperationMode(struct host_if_drv *hif_drv,
- struct op_mode *pstrHostIfSetOperationMode)
+static s32 handle_set_operation_mode(struct wilc_vif *vif,
+ struct op_mode *hif_op_mode)
{
s32 result = 0;
struct wid wid;
wid.id = (u16)WID_SET_OPERATION_MODE;
wid.type = WID_INT;
- wid.val = (s8 *)&pstrHostIfSetOperationMode->mode;
+ wid.val = (s8 *)&hif_op_mode->mode;
wid.size = sizeof(u32);
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
- if ((pstrHostIfSetOperationMode->mode) == IDLE_MODE)
+ if ((hif_op_mode->mode) == IDLE_MODE)
up(&hif_sema_driver);
if (result) {
@@ -398,28 +362,34 @@ static s32 Handle_SetOperationMode(struct host_if_drv *hif_drv,
return result;
}
-s32 Handle_set_IPAddress(struct host_if_drv *hif_drv, u8 *pu8IPAddr, u8 idx)
+static s32 host_int_get_ipaddress(struct wilc_vif *vif,
+ struct host_if_drv *hif_drv,
+ u8 *u16ipadd, u8 idx);
+
+static s32 handle_set_ip_address(struct wilc_vif *vif, u8 *ip_addr, u8 idx)
{
s32 result = 0;
struct wid wid;
- char firmwareIPAddress[4] = {0};
+ char firmware_ip_addr[4] = {0};
+ struct host_if_drv *hif_drv = vif->hif_drv;
- if (pu8IPAddr[0] < 192)
- pu8IPAddr[0] = 0;
+ if (ip_addr[0] < 192)
+ ip_addr[0] = 0;
- PRINT_INFO(HOSTINF_DBG, "Indx = %d, Handling set IP = %pI4\n", idx, pu8IPAddr);
+ PRINT_INFO(HOSTINF_DBG, "Indx = %d, Handling set IP = %pI4\n",
+ idx, ip_addr);
- memcpy(set_ip[idx], pu8IPAddr, IP_ALEN);
+ memcpy(set_ip[idx], ip_addr, IP_ALEN);
wid.id = (u16)WID_IP_ADDRESS;
wid.type = WID_STR;
- wid.val = (u8 *)pu8IPAddr;
+ wid.val = (u8 *)ip_addr;
wid.size = IP_ALEN;
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
- host_int_get_ipaddress(hif_drv, firmwareIPAddress, idx);
+ host_int_get_ipaddress(vif, hif_drv, firmware_ip_addr, idx);
if (result) {
PRINT_ER("Failed to set IP address\n");
@@ -431,7 +401,7 @@ s32 Handle_set_IPAddress(struct host_if_drv *hif_drv, u8 *pu8IPAddr, u8 idx)
return result;
}
-s32 Handle_get_IPAddress(struct host_if_drv *hif_drv, u8 *pu8IPAddr, u8 idx)
+static s32 handle_get_ip_address(struct wilc_vif *vif, u8 idx)
{
s32 result = 0;
struct wid wid;
@@ -441,8 +411,8 @@ s32 Handle_get_IPAddress(struct host_if_drv *hif_drv, u8 *pu8IPAddr, u8 idx)
wid.val = kmalloc(IP_ALEN, GFP_KERNEL);
wid.size = IP_ALEN;
- result = send_config_pkt(GET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, GET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
PRINT_INFO(HOSTINF_DBG, "%pI4\n", wid.val);
@@ -451,7 +421,7 @@ s32 Handle_get_IPAddress(struct host_if_drv *hif_drv, u8 *pu8IPAddr, u8 idx)
kfree(wid.val);
if (memcmp(get_ip[idx], set_ip[idx], IP_ALEN) != 0)
- host_int_setup_ipaddress(hif_drv, set_ip[idx], idx);
+ wilc_setup_ipaddress(vif, set_ip[idx], idx);
if (result != 0) {
PRINT_ER("Failed to get IP address\n");
@@ -465,8 +435,8 @@ s32 Handle_get_IPAddress(struct host_if_drv *hif_drv, u8 *pu8IPAddr, u8 idx)
return result;
}
-static s32 Handle_SetMacAddress(struct host_if_drv *hif_drv,
- struct set_mac_addr *pstrHostIfSetMacAddress)
+static s32 handle_set_mac_address(struct wilc_vif *vif,
+ struct set_mac_addr *set_mac_addr)
{
s32 result = 0;
struct wid wid;
@@ -476,7 +446,7 @@ static s32 Handle_SetMacAddress(struct host_if_drv *hif_drv,
PRINT_ER("No buffer to send mac address\n");
return -EFAULT;
}
- memcpy(mac_buf, pstrHostIfSetMacAddress->mac_addr, ETH_ALEN);
+ memcpy(mac_buf, set_mac_addr->mac_addr, ETH_ALEN);
wid.id = (u16)WID_MAC_ADDR;
wid.type = WID_STR;
@@ -484,8 +454,8 @@ static s32 Handle_SetMacAddress(struct host_if_drv *hif_drv,
wid.size = ETH_ALEN;
PRINT_D(GENERIC_DBG, "mac addr = :%pM\n", wid.val);
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result) {
PRINT_ER("Failed to set mac address\n");
result = -EFAULT;
@@ -495,19 +465,19 @@ static s32 Handle_SetMacAddress(struct host_if_drv *hif_drv,
return result;
}
-static s32 Handle_GetMacAddress(struct host_if_drv *hif_drv,
- struct get_mac_addr *pstrHostIfGetMacAddress)
+static s32 handle_get_mac_address(struct wilc_vif *vif,
+ struct get_mac_addr *get_mac_addr)
{
s32 result = 0;
struct wid wid;
wid.id = (u16)WID_MAC_ADDR;
wid.type = WID_STR;
- wid.val = pstrHostIfGetMacAddress->mac_addr;
+ wid.val = get_mac_addr->mac_addr;
wid.size = ETH_ALEN;
- result = send_config_pkt(GET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, GET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result) {
PRINT_ER("Failed to get mac address\n");
@@ -518,258 +488,270 @@ static s32 Handle_GetMacAddress(struct host_if_drv *hif_drv,
return result;
}
-static s32 Handle_CfgParam(struct host_if_drv *hif_drv,
- struct cfg_param_attr *strHostIFCfgParamAttr)
+static s32 handle_cfg_param(struct wilc_vif *vif,
+ struct cfg_param_attr *cfg_param_attr)
{
s32 result = 0;
- struct wid strWIDList[32];
- u8 u8WidCnt = 0;
+ struct wid wid_list[32];
+ struct host_if_drv *hif_drv = vif->hif_drv;
+ u8 wid_cnt = 0;
- down(&hif_drv->gtOsCfgValuesSem);
+ down(&hif_drv->sem_cfg_values);
PRINT_D(HOSTINF_DBG, "Setting CFG params\n");
- if (strHostIFCfgParamAttr->cfg_attr_info.flag & BSS_TYPE) {
- if (strHostIFCfgParamAttr->cfg_attr_info.bss_type < 6) {
- strWIDList[u8WidCnt].id = WID_BSS_TYPE;
- strWIDList[u8WidCnt].val = (s8 *)&strHostIFCfgParamAttr->cfg_attr_info.bss_type;
- strWIDList[u8WidCnt].type = WID_CHAR;
- strWIDList[u8WidCnt].size = sizeof(char);
- hif_drv->strCfgValues.bss_type = (u8)strHostIFCfgParamAttr->cfg_attr_info.bss_type;
+ if (cfg_param_attr->cfg_attr_info.flag & BSS_TYPE) {
+ if (cfg_param_attr->cfg_attr_info.bss_type < 6) {
+ wid_list[wid_cnt].id = WID_BSS_TYPE;
+ wid_list[wid_cnt].val = (s8 *)&cfg_param_attr->cfg_attr_info.bss_type;
+ wid_list[wid_cnt].type = WID_CHAR;
+ wid_list[wid_cnt].size = sizeof(char);
+ hif_drv->cfg_values.bss_type = (u8)cfg_param_attr->cfg_attr_info.bss_type;
} else {
PRINT_ER("check value 6 over\n");
result = -EINVAL;
goto ERRORHANDLER;
}
- u8WidCnt++;
- }
- if (strHostIFCfgParamAttr->cfg_attr_info.flag & AUTH_TYPE) {
- if ((strHostIFCfgParamAttr->cfg_attr_info.auth_type) == 1 || (strHostIFCfgParamAttr->cfg_attr_info.auth_type) == 2 || (strHostIFCfgParamAttr->cfg_attr_info.auth_type) == 5) {
- strWIDList[u8WidCnt].id = WID_AUTH_TYPE;
- strWIDList[u8WidCnt].val = (s8 *)&strHostIFCfgParamAttr->cfg_attr_info.auth_type;
- strWIDList[u8WidCnt].type = WID_CHAR;
- strWIDList[u8WidCnt].size = sizeof(char);
- hif_drv->strCfgValues.auth_type = (u8)strHostIFCfgParamAttr->cfg_attr_info.auth_type;
+ wid_cnt++;
+ }
+ if (cfg_param_attr->cfg_attr_info.flag & AUTH_TYPE) {
+ if (cfg_param_attr->cfg_attr_info.auth_type == 1 ||
+ cfg_param_attr->cfg_attr_info.auth_type == 2 ||
+ cfg_param_attr->cfg_attr_info.auth_type == 5) {
+ wid_list[wid_cnt].id = WID_AUTH_TYPE;
+ wid_list[wid_cnt].val = (s8 *)&cfg_param_attr->cfg_attr_info.auth_type;
+ wid_list[wid_cnt].type = WID_CHAR;
+ wid_list[wid_cnt].size = sizeof(char);
+ hif_drv->cfg_values.auth_type = (u8)cfg_param_attr->cfg_attr_info.auth_type;
} else {
PRINT_ER("Impossible value \n");
result = -EINVAL;
goto ERRORHANDLER;
}
- u8WidCnt++;
- }
- if (strHostIFCfgParamAttr->cfg_attr_info.flag & AUTHEN_TIMEOUT) {
- if (strHostIFCfgParamAttr->cfg_attr_info.auth_timeout > 0 && strHostIFCfgParamAttr->cfg_attr_info.auth_timeout < 65536) {
- strWIDList[u8WidCnt].id = WID_AUTH_TIMEOUT;
- strWIDList[u8WidCnt].val = (s8 *)&strHostIFCfgParamAttr->cfg_attr_info.auth_timeout;
- strWIDList[u8WidCnt].type = WID_SHORT;
- strWIDList[u8WidCnt].size = sizeof(u16);
- hif_drv->strCfgValues.auth_timeout = strHostIFCfgParamAttr->cfg_attr_info.auth_timeout;
+ wid_cnt++;
+ }
+ if (cfg_param_attr->cfg_attr_info.flag & AUTHEN_TIMEOUT) {
+ if (cfg_param_attr->cfg_attr_info.auth_timeout > 0 &&
+ cfg_param_attr->cfg_attr_info.auth_timeout < 65536) {
+ wid_list[wid_cnt].id = WID_AUTH_TIMEOUT;
+ wid_list[wid_cnt].val = (s8 *)&cfg_param_attr->cfg_attr_info.auth_timeout;
+ wid_list[wid_cnt].type = WID_SHORT;
+ wid_list[wid_cnt].size = sizeof(u16);
+ hif_drv->cfg_values.auth_timeout = cfg_param_attr->cfg_attr_info.auth_timeout;
} else {
PRINT_ER("Range(1 ~ 65535) over\n");
result = -EINVAL;
goto ERRORHANDLER;
}
- u8WidCnt++;
- }
- if (strHostIFCfgParamAttr->cfg_attr_info.flag & POWER_MANAGEMENT) {
- if (strHostIFCfgParamAttr->cfg_attr_info.power_mgmt_mode < 5) {
- strWIDList[u8WidCnt].id = WID_POWER_MANAGEMENT;
- strWIDList[u8WidCnt].val = (s8 *)&strHostIFCfgParamAttr->cfg_attr_info.power_mgmt_mode;
- strWIDList[u8WidCnt].type = WID_CHAR;
- strWIDList[u8WidCnt].size = sizeof(char);
- hif_drv->strCfgValues.power_mgmt_mode = (u8)strHostIFCfgParamAttr->cfg_attr_info.power_mgmt_mode;
+ wid_cnt++;
+ }
+ if (cfg_param_attr->cfg_attr_info.flag & POWER_MANAGEMENT) {
+ if (cfg_param_attr->cfg_attr_info.power_mgmt_mode < 5) {
+ wid_list[wid_cnt].id = WID_POWER_MANAGEMENT;
+ wid_list[wid_cnt].val = (s8 *)&cfg_param_attr->cfg_attr_info.power_mgmt_mode;
+ wid_list[wid_cnt].type = WID_CHAR;
+ wid_list[wid_cnt].size = sizeof(char);
+ hif_drv->cfg_values.power_mgmt_mode = (u8)cfg_param_attr->cfg_attr_info.power_mgmt_mode;
} else {
PRINT_ER("Invalide power mode\n");
result = -EINVAL;
goto ERRORHANDLER;
}
- u8WidCnt++;
- }
- if (strHostIFCfgParamAttr->cfg_attr_info.flag & RETRY_SHORT) {
- if ((strHostIFCfgParamAttr->cfg_attr_info.short_retry_limit > 0) && (strHostIFCfgParamAttr->cfg_attr_info.short_retry_limit < 256)) {
- strWIDList[u8WidCnt].id = WID_SHORT_RETRY_LIMIT;
- strWIDList[u8WidCnt].val = (s8 *)&strHostIFCfgParamAttr->cfg_attr_info.short_retry_limit;
- strWIDList[u8WidCnt].type = WID_SHORT;
- strWIDList[u8WidCnt].size = sizeof(u16);
- hif_drv->strCfgValues.short_retry_limit = strHostIFCfgParamAttr->cfg_attr_info.short_retry_limit;
+ wid_cnt++;
+ }
+ if (cfg_param_attr->cfg_attr_info.flag & RETRY_SHORT) {
+ if (cfg_param_attr->cfg_attr_info.short_retry_limit > 0 &&
+ cfg_param_attr->cfg_attr_info.short_retry_limit < 256) {
+ wid_list[wid_cnt].id = WID_SHORT_RETRY_LIMIT;
+ wid_list[wid_cnt].val = (s8 *)&cfg_param_attr->cfg_attr_info.short_retry_limit;
+ wid_list[wid_cnt].type = WID_SHORT;
+ wid_list[wid_cnt].size = sizeof(u16);
+ hif_drv->cfg_values.short_retry_limit = cfg_param_attr->cfg_attr_info.short_retry_limit;
} else {
PRINT_ER("Range(1~256) over\n");
result = -EINVAL;
goto ERRORHANDLER;
}
- u8WidCnt++;
- }
- if (strHostIFCfgParamAttr->cfg_attr_info.flag & RETRY_LONG) {
- if ((strHostIFCfgParamAttr->cfg_attr_info.long_retry_limit > 0) && (strHostIFCfgParamAttr->cfg_attr_info.long_retry_limit < 256)) {
- strWIDList[u8WidCnt].id = WID_LONG_RETRY_LIMIT;
- strWIDList[u8WidCnt].val = (s8 *)&strHostIFCfgParamAttr->cfg_attr_info.long_retry_limit;
-
- strWIDList[u8WidCnt].type = WID_SHORT;
- strWIDList[u8WidCnt].size = sizeof(u16);
- hif_drv->strCfgValues.long_retry_limit = strHostIFCfgParamAttr->cfg_attr_info.long_retry_limit;
+ wid_cnt++;
+ }
+ if (cfg_param_attr->cfg_attr_info.flag & RETRY_LONG) {
+ if (cfg_param_attr->cfg_attr_info.long_retry_limit > 0 &&
+ cfg_param_attr->cfg_attr_info.long_retry_limit < 256) {
+ wid_list[wid_cnt].id = WID_LONG_RETRY_LIMIT;
+ wid_list[wid_cnt].val = (s8 *)&cfg_param_attr->cfg_attr_info.long_retry_limit;
+ wid_list[wid_cnt].type = WID_SHORT;
+ wid_list[wid_cnt].size = sizeof(u16);
+ hif_drv->cfg_values.long_retry_limit = cfg_param_attr->cfg_attr_info.long_retry_limit;
} else {
PRINT_ER("Range(1~256) over\n");
result = -EINVAL;
goto ERRORHANDLER;
}
- u8WidCnt++;
- }
- if (strHostIFCfgParamAttr->cfg_attr_info.flag & FRAG_THRESHOLD) {
- if (strHostIFCfgParamAttr->cfg_attr_info.frag_threshold > 255 && strHostIFCfgParamAttr->cfg_attr_info.frag_threshold < 7937) {
- strWIDList[u8WidCnt].id = WID_FRAG_THRESHOLD;
- strWIDList[u8WidCnt].val = (s8 *)&strHostIFCfgParamAttr->cfg_attr_info.frag_threshold;
- strWIDList[u8WidCnt].type = WID_SHORT;
- strWIDList[u8WidCnt].size = sizeof(u16);
- hif_drv->strCfgValues.frag_threshold = strHostIFCfgParamAttr->cfg_attr_info.frag_threshold;
+ wid_cnt++;
+ }
+ if (cfg_param_attr->cfg_attr_info.flag & FRAG_THRESHOLD) {
+ if (cfg_param_attr->cfg_attr_info.frag_threshold > 255 &&
+ cfg_param_attr->cfg_attr_info.frag_threshold < 7937) {
+ wid_list[wid_cnt].id = WID_FRAG_THRESHOLD;
+ wid_list[wid_cnt].val = (s8 *)&cfg_param_attr->cfg_attr_info.frag_threshold;
+ wid_list[wid_cnt].type = WID_SHORT;
+ wid_list[wid_cnt].size = sizeof(u16);
+ hif_drv->cfg_values.frag_threshold = cfg_param_attr->cfg_attr_info.frag_threshold;
} else {
PRINT_ER("Threshold Range fail\n");
result = -EINVAL;
goto ERRORHANDLER;
}
- u8WidCnt++;
- }
- if (strHostIFCfgParamAttr->cfg_attr_info.flag & RTS_THRESHOLD) {
- if (strHostIFCfgParamAttr->cfg_attr_info.rts_threshold > 255 && strHostIFCfgParamAttr->cfg_attr_info.rts_threshold < 65536) {
- strWIDList[u8WidCnt].id = WID_RTS_THRESHOLD;
- strWIDList[u8WidCnt].val = (s8 *)&strHostIFCfgParamAttr->cfg_attr_info.rts_threshold;
- strWIDList[u8WidCnt].type = WID_SHORT;
- strWIDList[u8WidCnt].size = sizeof(u16);
- hif_drv->strCfgValues.rts_threshold = strHostIFCfgParamAttr->cfg_attr_info.rts_threshold;
+ wid_cnt++;
+ }
+ if (cfg_param_attr->cfg_attr_info.flag & RTS_THRESHOLD) {
+ if (cfg_param_attr->cfg_attr_info.rts_threshold > 255 &&
+ cfg_param_attr->cfg_attr_info.rts_threshold < 65536) {
+ wid_list[wid_cnt].id = WID_RTS_THRESHOLD;
+ wid_list[wid_cnt].val = (s8 *)&cfg_param_attr->cfg_attr_info.rts_threshold;
+ wid_list[wid_cnt].type = WID_SHORT;
+ wid_list[wid_cnt].size = sizeof(u16);
+ hif_drv->cfg_values.rts_threshold = cfg_param_attr->cfg_attr_info.rts_threshold;
} else {
PRINT_ER("Threshold Range fail\n");
result = -EINVAL;
goto ERRORHANDLER;
}
- u8WidCnt++;
- }
- if (strHostIFCfgParamAttr->cfg_attr_info.flag & PREAMBLE) {
- if (strHostIFCfgParamAttr->cfg_attr_info.preamble_type < 3) {
- strWIDList[u8WidCnt].id = WID_PREAMBLE;
- strWIDList[u8WidCnt].val = (s8 *)&strHostIFCfgParamAttr->cfg_attr_info.preamble_type;
- strWIDList[u8WidCnt].type = WID_CHAR;
- strWIDList[u8WidCnt].size = sizeof(char);
- hif_drv->strCfgValues.preamble_type = strHostIFCfgParamAttr->cfg_attr_info.preamble_type;
+ wid_cnt++;
+ }
+ if (cfg_param_attr->cfg_attr_info.flag & PREAMBLE) {
+ if (cfg_param_attr->cfg_attr_info.preamble_type < 3) {
+ wid_list[wid_cnt].id = WID_PREAMBLE;
+ wid_list[wid_cnt].val = (s8 *)&cfg_param_attr->cfg_attr_info.preamble_type;
+ wid_list[wid_cnt].type = WID_CHAR;
+ wid_list[wid_cnt].size = sizeof(char);
+ hif_drv->cfg_values.preamble_type = cfg_param_attr->cfg_attr_info.preamble_type;
} else {
PRINT_ER("Preamle Range(0~2) over\n");
result = -EINVAL;
goto ERRORHANDLER;
}
- u8WidCnt++;
- }
- if (strHostIFCfgParamAttr->cfg_attr_info.flag & SHORT_SLOT_ALLOWED) {
- if (strHostIFCfgParamAttr->cfg_attr_info.short_slot_allowed < 2) {
- strWIDList[u8WidCnt].id = WID_SHORT_SLOT_ALLOWED;
- strWIDList[u8WidCnt].val = (s8 *)&strHostIFCfgParamAttr->cfg_attr_info.short_slot_allowed;
- strWIDList[u8WidCnt].type = WID_CHAR;
- strWIDList[u8WidCnt].size = sizeof(char);
- hif_drv->strCfgValues.short_slot_allowed = (u8)strHostIFCfgParamAttr->cfg_attr_info.short_slot_allowed;
+ wid_cnt++;
+ }
+ if (cfg_param_attr->cfg_attr_info.flag & SHORT_SLOT_ALLOWED) {
+ if (cfg_param_attr->cfg_attr_info.short_slot_allowed < 2) {
+ wid_list[wid_cnt].id = WID_SHORT_SLOT_ALLOWED;
+ wid_list[wid_cnt].val = (s8 *)&cfg_param_attr->cfg_attr_info.short_slot_allowed;
+ wid_list[wid_cnt].type = WID_CHAR;
+ wid_list[wid_cnt].size = sizeof(char);
+ hif_drv->cfg_values.short_slot_allowed = (u8)cfg_param_attr->cfg_attr_info.short_slot_allowed;
} else {
PRINT_ER("Short slot(2) over\n");
result = -EINVAL;
goto ERRORHANDLER;
}
- u8WidCnt++;
- }
- if (strHostIFCfgParamAttr->cfg_attr_info.flag & TXOP_PROT_DISABLE) {
- if (strHostIFCfgParamAttr->cfg_attr_info.txop_prot_disabled < 2) {
- strWIDList[u8WidCnt].id = WID_11N_TXOP_PROT_DISABLE;
- strWIDList[u8WidCnt].val = (s8 *)&strHostIFCfgParamAttr->cfg_attr_info.txop_prot_disabled;
- strWIDList[u8WidCnt].type = WID_CHAR;
- strWIDList[u8WidCnt].size = sizeof(char);
- hif_drv->strCfgValues.txop_prot_disabled = (u8)strHostIFCfgParamAttr->cfg_attr_info.txop_prot_disabled;
+ wid_cnt++;
+ }
+ if (cfg_param_attr->cfg_attr_info.flag & TXOP_PROT_DISABLE) {
+ if (cfg_param_attr->cfg_attr_info.txop_prot_disabled < 2) {
+ wid_list[wid_cnt].id = WID_11N_TXOP_PROT_DISABLE;
+ wid_list[wid_cnt].val = (s8 *)&cfg_param_attr->cfg_attr_info.txop_prot_disabled;
+ wid_list[wid_cnt].type = WID_CHAR;
+ wid_list[wid_cnt].size = sizeof(char);
+ hif_drv->cfg_values.txop_prot_disabled = (u8)cfg_param_attr->cfg_attr_info.txop_prot_disabled;
} else {
PRINT_ER("TXOP prot disable\n");
result = -EINVAL;
goto ERRORHANDLER;
}
- u8WidCnt++;
- }
- if (strHostIFCfgParamAttr->cfg_attr_info.flag & BEACON_INTERVAL) {
- if (strHostIFCfgParamAttr->cfg_attr_info.beacon_interval > 0 && strHostIFCfgParamAttr->cfg_attr_info.beacon_interval < 65536) {
- strWIDList[u8WidCnt].id = WID_BEACON_INTERVAL;
- strWIDList[u8WidCnt].val = (s8 *)&strHostIFCfgParamAttr->cfg_attr_info.beacon_interval;
- strWIDList[u8WidCnt].type = WID_SHORT;
- strWIDList[u8WidCnt].size = sizeof(u16);
- hif_drv->strCfgValues.beacon_interval = strHostIFCfgParamAttr->cfg_attr_info.beacon_interval;
+ wid_cnt++;
+ }
+ if (cfg_param_attr->cfg_attr_info.flag & BEACON_INTERVAL) {
+ if (cfg_param_attr->cfg_attr_info.beacon_interval > 0 &&
+ cfg_param_attr->cfg_attr_info.beacon_interval < 65536) {
+ wid_list[wid_cnt].id = WID_BEACON_INTERVAL;
+ wid_list[wid_cnt].val = (s8 *)&cfg_param_attr->cfg_attr_info.beacon_interval;
+ wid_list[wid_cnt].type = WID_SHORT;
+ wid_list[wid_cnt].size = sizeof(u16);
+ hif_drv->cfg_values.beacon_interval = cfg_param_attr->cfg_attr_info.beacon_interval;
} else {
PRINT_ER("Beacon interval(1~65535) fail\n");
result = -EINVAL;
goto ERRORHANDLER;
}
- u8WidCnt++;
- }
- if (strHostIFCfgParamAttr->cfg_attr_info.flag & DTIM_PERIOD) {
- if (strHostIFCfgParamAttr->cfg_attr_info.dtim_period > 0 && strHostIFCfgParamAttr->cfg_attr_info.dtim_period < 256) {
- strWIDList[u8WidCnt].id = WID_DTIM_PERIOD;
- strWIDList[u8WidCnt].val = (s8 *)&strHostIFCfgParamAttr->cfg_attr_info.dtim_period;
- strWIDList[u8WidCnt].type = WID_CHAR;
- strWIDList[u8WidCnt].size = sizeof(char);
- hif_drv->strCfgValues.dtim_period = strHostIFCfgParamAttr->cfg_attr_info.dtim_period;
+ wid_cnt++;
+ }
+ if (cfg_param_attr->cfg_attr_info.flag & DTIM_PERIOD) {
+ if (cfg_param_attr->cfg_attr_info.dtim_period > 0 &&
+ cfg_param_attr->cfg_attr_info.dtim_period < 256) {
+ wid_list[wid_cnt].id = WID_DTIM_PERIOD;
+ wid_list[wid_cnt].val = (s8 *)&cfg_param_attr->cfg_attr_info.dtim_period;
+ wid_list[wid_cnt].type = WID_CHAR;
+ wid_list[wid_cnt].size = sizeof(char);
+ hif_drv->cfg_values.dtim_period = cfg_param_attr->cfg_attr_info.dtim_period;
} else {
PRINT_ER("DTIM range(1~255) fail\n");
result = -EINVAL;
goto ERRORHANDLER;
}
- u8WidCnt++;
- }
- if (strHostIFCfgParamAttr->cfg_attr_info.flag & SITE_SURVEY) {
- if (strHostIFCfgParamAttr->cfg_attr_info.site_survey_enabled < 3) {
- strWIDList[u8WidCnt].id = WID_SITE_SURVEY;
- strWIDList[u8WidCnt].val = (s8 *)&strHostIFCfgParamAttr->cfg_attr_info.site_survey_enabled;
- strWIDList[u8WidCnt].type = WID_CHAR;
- strWIDList[u8WidCnt].size = sizeof(char);
- hif_drv->strCfgValues.site_survey_enabled = (u8)strHostIFCfgParamAttr->cfg_attr_info.site_survey_enabled;
+ wid_cnt++;
+ }
+ if (cfg_param_attr->cfg_attr_info.flag & SITE_SURVEY) {
+ if (cfg_param_attr->cfg_attr_info.site_survey_enabled < 3) {
+ wid_list[wid_cnt].id = WID_SITE_SURVEY;
+ wid_list[wid_cnt].val = (s8 *)&cfg_param_attr->cfg_attr_info.site_survey_enabled;
+ wid_list[wid_cnt].type = WID_CHAR;
+ wid_list[wid_cnt].size = sizeof(char);
+ hif_drv->cfg_values.site_survey_enabled = (u8)cfg_param_attr->cfg_attr_info.site_survey_enabled;
} else {
PRINT_ER("Site survey disable\n");
result = -EINVAL;
goto ERRORHANDLER;
}
- u8WidCnt++;
- }
- if (strHostIFCfgParamAttr->cfg_attr_info.flag & SITE_SURVEY_SCAN_TIME) {
- if (strHostIFCfgParamAttr->cfg_attr_info.site_survey_scan_time > 0 && strHostIFCfgParamAttr->cfg_attr_info.site_survey_scan_time < 65536) {
- strWIDList[u8WidCnt].id = WID_SITE_SURVEY_SCAN_TIME;
- strWIDList[u8WidCnt].val = (s8 *)&strHostIFCfgParamAttr->cfg_attr_info.site_survey_scan_time;
- strWIDList[u8WidCnt].type = WID_SHORT;
- strWIDList[u8WidCnt].size = sizeof(u16);
- hif_drv->strCfgValues.site_survey_scan_time = strHostIFCfgParamAttr->cfg_attr_info.site_survey_scan_time;
+ wid_cnt++;
+ }
+ if (cfg_param_attr->cfg_attr_info.flag & SITE_SURVEY_SCAN_TIME) {
+ if (cfg_param_attr->cfg_attr_info.site_survey_scan_time > 0 &&
+ cfg_param_attr->cfg_attr_info.site_survey_scan_time < 65536) {
+ wid_list[wid_cnt].id = WID_SITE_SURVEY_SCAN_TIME;
+ wid_list[wid_cnt].val = (s8 *)&cfg_param_attr->cfg_attr_info.site_survey_scan_time;
+ wid_list[wid_cnt].type = WID_SHORT;
+ wid_list[wid_cnt].size = sizeof(u16);
+ hif_drv->cfg_values.site_survey_scan_time = cfg_param_attr->cfg_attr_info.site_survey_scan_time;
} else {
PRINT_ER("Site survey scan time(1~65535) over\n");
result = -EINVAL;
goto ERRORHANDLER;
}
- u8WidCnt++;
- }
- if (strHostIFCfgParamAttr->cfg_attr_info.flag & ACTIVE_SCANTIME) {
- if (strHostIFCfgParamAttr->cfg_attr_info.active_scan_time > 0 && strHostIFCfgParamAttr->cfg_attr_info.active_scan_time < 65536) {
- strWIDList[u8WidCnt].id = WID_ACTIVE_SCAN_TIME;
- strWIDList[u8WidCnt].val = (s8 *)&strHostIFCfgParamAttr->cfg_attr_info.active_scan_time;
- strWIDList[u8WidCnt].type = WID_SHORT;
- strWIDList[u8WidCnt].size = sizeof(u16);
- hif_drv->strCfgValues.active_scan_time = strHostIFCfgParamAttr->cfg_attr_info.active_scan_time;
+ wid_cnt++;
+ }
+ if (cfg_param_attr->cfg_attr_info.flag & ACTIVE_SCANTIME) {
+ if (cfg_param_attr->cfg_attr_info.active_scan_time > 0 &&
+ cfg_param_attr->cfg_attr_info.active_scan_time < 65536) {
+ wid_list[wid_cnt].id = WID_ACTIVE_SCAN_TIME;
+ wid_list[wid_cnt].val = (s8 *)&cfg_param_attr->cfg_attr_info.active_scan_time;
+ wid_list[wid_cnt].type = WID_SHORT;
+ wid_list[wid_cnt].size = sizeof(u16);
+ hif_drv->cfg_values.active_scan_time = cfg_param_attr->cfg_attr_info.active_scan_time;
} else {
PRINT_ER("Active scan time(1~65535) over\n");
result = -EINVAL;
goto ERRORHANDLER;
}
- u8WidCnt++;
- }
- if (strHostIFCfgParamAttr->cfg_attr_info.flag & PASSIVE_SCANTIME) {
- if (strHostIFCfgParamAttr->cfg_attr_info.passive_scan_time > 0 && strHostIFCfgParamAttr->cfg_attr_info.passive_scan_time < 65536) {
- strWIDList[u8WidCnt].id = WID_PASSIVE_SCAN_TIME;
- strWIDList[u8WidCnt].val = (s8 *)&strHostIFCfgParamAttr->cfg_attr_info.passive_scan_time;
- strWIDList[u8WidCnt].type = WID_SHORT;
- strWIDList[u8WidCnt].size = sizeof(u16);
- hif_drv->strCfgValues.passive_scan_time = strHostIFCfgParamAttr->cfg_attr_info.passive_scan_time;
+ wid_cnt++;
+ }
+ if (cfg_param_attr->cfg_attr_info.flag & PASSIVE_SCANTIME) {
+ if (cfg_param_attr->cfg_attr_info.passive_scan_time > 0 &&
+ cfg_param_attr->cfg_attr_info.passive_scan_time < 65536) {
+ wid_list[wid_cnt].id = WID_PASSIVE_SCAN_TIME;
+ wid_list[wid_cnt].val = (s8 *)&cfg_param_attr->cfg_attr_info.passive_scan_time;
+ wid_list[wid_cnt].type = WID_SHORT;
+ wid_list[wid_cnt].size = sizeof(u16);
+ hif_drv->cfg_values.passive_scan_time = cfg_param_attr->cfg_attr_info.passive_scan_time;
} else {
PRINT_ER("Passive scan time(1~65535) over\n");
result = -EINVAL;
goto ERRORHANDLER;
}
- u8WidCnt++;
+ wid_cnt++;
}
- if (strHostIFCfgParamAttr->cfg_attr_info.flag & CURRENT_TX_RATE) {
- enum CURRENT_TXRATE curr_tx_rate = strHostIFCfgParamAttr->cfg_attr_info.curr_tx_rate;
+ if (cfg_param_attr->cfg_attr_info.flag & CURRENT_TX_RATE) {
+ enum CURRENT_TXRATE curr_tx_rate = cfg_param_attr->cfg_attr_info.curr_tx_rate;
if (curr_tx_rate == AUTORATE || curr_tx_rate == MBPS_1
|| curr_tx_rate == MBPS_2 || curr_tx_rate == MBPS_5_5
@@ -777,38 +759,40 @@ static s32 Handle_CfgParam(struct host_if_drv *hif_drv,
|| curr_tx_rate == MBPS_9 || curr_tx_rate == MBPS_12
|| curr_tx_rate == MBPS_18 || curr_tx_rate == MBPS_24
|| curr_tx_rate == MBPS_36 || curr_tx_rate == MBPS_48 || curr_tx_rate == MBPS_54) {
- strWIDList[u8WidCnt].id = WID_CURRENT_TX_RATE;
- strWIDList[u8WidCnt].val = (s8 *)&curr_tx_rate;
- strWIDList[u8WidCnt].type = WID_SHORT;
- strWIDList[u8WidCnt].size = sizeof(u16);
- hif_drv->strCfgValues.curr_tx_rate = (u8)curr_tx_rate;
+ wid_list[wid_cnt].id = WID_CURRENT_TX_RATE;
+ wid_list[wid_cnt].val = (s8 *)&curr_tx_rate;
+ wid_list[wid_cnt].type = WID_SHORT;
+ wid_list[wid_cnt].size = sizeof(u16);
+ hif_drv->cfg_values.curr_tx_rate = (u8)curr_tx_rate;
} else {
PRINT_ER("out of TX rate\n");
result = -EINVAL;
goto ERRORHANDLER;
}
- u8WidCnt++;
+ wid_cnt++;
}
- result = send_config_pkt(SET_CFG, strWIDList, u8WidCnt,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, wid_list,
+ wid_cnt, wilc_get_vif_idx(vif));
if (result)
PRINT_ER("Error in setting CFG params\n");
ERRORHANDLER:
- up(&hif_drv->gtOsCfgValuesSem);
+ up(&hif_drv->sem_cfg_values);
return result;
}
-static s32 Handle_wait_msg_q_empty(void)
+static void Handle_wait_msg_q_empty(void)
{
- g_wilc_initialized = 0;
+ wilc_initialized = 0;
up(&hif_sema_wait_response);
- return 0;
}
-static s32 Handle_Scan(struct host_if_drv *hif_drv,
+static s32 Handle_ScanDone(struct wilc_vif *vif,
+ enum scan_event enuEvent);
+
+static s32 Handle_Scan(struct wilc_vif *vif,
struct scan_attr *pstrHostIFscanAttr)
{
s32 result = 0;
@@ -818,21 +802,24 @@ static s32 Handle_Scan(struct host_if_drv *hif_drv,
u8 *pu8Buffer;
u8 valuesize = 0;
u8 *pu8HdnNtwrksWidVal = NULL;
+ struct host_if_drv *hif_drv = vif->hif_drv;
PRINT_D(HOSTINF_DBG, "Setting SCAN params\n");
- PRINT_D(HOSTINF_DBG, "Scanning: In [%d] state\n", hif_drv->enuHostIFstate);
+ PRINT_D(HOSTINF_DBG, "Scanning: In [%d] state\n", hif_drv->hif_state);
- hif_drv->usr_scan_req.pfUserScanResult = pstrHostIFscanAttr->result;
- hif_drv->usr_scan_req.u32UserScanPvoid = pstrHostIFscanAttr->arg;
+ hif_drv->usr_scan_req.scan_result = pstrHostIFscanAttr->result;
+ hif_drv->usr_scan_req.arg = pstrHostIFscanAttr->arg;
- if ((hif_drv->enuHostIFstate >= HOST_IF_SCANNING) && (hif_drv->enuHostIFstate < HOST_IF_CONNECTED)) {
- PRINT_D(GENERIC_DBG, "Don't scan we are already in [%d] state\n", hif_drv->enuHostIFstate);
+ if ((hif_drv->hif_state >= HOST_IF_SCANNING) &&
+ (hif_drv->hif_state < HOST_IF_CONNECTED)) {
+ PRINT_D(GENERIC_DBG, "Don't scan already in [%d] state\n",
+ hif_drv->hif_state);
PRINT_ER("Already scan\n");
result = -EBUSY;
goto ERRORHANDLER;
}
- if (g_obtainingIP || connecting) {
+ if (wilc_optaining_ip || wilc_connecting) {
PRINT_D(GENERIC_DBG, "[handle_scan]: Don't do obss scan until IP adresss is obtained\n");
PRINT_ER("Don't do obss scan\n");
result = -EBUSY;
@@ -841,7 +828,7 @@ static s32 Handle_Scan(struct host_if_drv *hif_drv,
PRINT_D(HOSTINF_DBG, "Setting SCAN params\n");
- hif_drv->usr_scan_req.u32RcvdChCount = 0;
+ hif_drv->usr_scan_req.rcvd_ch_cnt = 0;
strWIDList[u32WidsCount].id = (u16)WID_SSID_PROBE_REQ;
strWIDList[u32WidsCount].type = WID_STR;
@@ -904,13 +891,14 @@ static s32 Handle_Scan(struct host_if_drv *hif_drv,
strWIDList[u32WidsCount].val = (s8 *)&pstrHostIFscanAttr->src;
u32WidsCount++;
- if (hif_drv->enuHostIFstate == HOST_IF_CONNECTED)
+ if (hif_drv->hif_state == HOST_IF_CONNECTED)
scan_while_connected = true;
- else if (hif_drv->enuHostIFstate == HOST_IF_IDLE)
+ else if (hif_drv->hif_state == HOST_IF_IDLE)
scan_while_connected = false;
- result = send_config_pkt(SET_CFG, strWIDList, u32WidsCount,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, strWIDList,
+ u32WidsCount,
+ wilc_get_vif_idx(vif));
if (result)
PRINT_ER("Failed to send scan paramters config packet\n");
@@ -919,8 +907,8 @@ static s32 Handle_Scan(struct host_if_drv *hif_drv,
ERRORHANDLER:
if (result) {
- del_timer(&hif_drv->hScanTimer);
- Handle_ScanDone(hif_drv, SCAN_EVENT_ABORTED);
+ del_timer(&hif_drv->scan_timer);
+ Handle_ScanDone(vif, SCAN_EVENT_ABORTED);
}
kfree(pstrHostIFscanAttr->ch_freq_list);
@@ -936,12 +924,13 @@ ERRORHANDLER:
return result;
}
-static s32 Handle_ScanDone(struct host_if_drv *hif_drv,
+static s32 Handle_ScanDone(struct wilc_vif *vif,
enum scan_event enuEvent)
{
s32 result = 0;
u8 u8abort_running_scan;
struct wid wid;
+ struct host_if_drv *hif_drv = vif->hif_drv;
PRINT_D(HOSTINF_DBG, "in Handle_ScanDone()\n");
@@ -953,8 +942,8 @@ static s32 Handle_ScanDone(struct host_if_drv *hif_drv,
wid.val = (s8 *)&u8abort_running_scan;
wid.size = sizeof(char);
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result) {
PRINT_ER("Failed to set abort running scan\n");
@@ -967,17 +956,17 @@ static s32 Handle_ScanDone(struct host_if_drv *hif_drv,
return result;
}
- if (hif_drv->usr_scan_req.pfUserScanResult) {
- hif_drv->usr_scan_req.pfUserScanResult(enuEvent, NULL,
- hif_drv->usr_scan_req.u32UserScanPvoid, NULL);
- hif_drv->usr_scan_req.pfUserScanResult = NULL;
+ if (hif_drv->usr_scan_req.scan_result) {
+ hif_drv->usr_scan_req.scan_result(enuEvent, NULL,
+ hif_drv->usr_scan_req.arg, NULL);
+ hif_drv->usr_scan_req.scan_result = NULL;
}
return result;
}
-u8 u8ConnectedSSID[6] = {0};
-static s32 Handle_Connect(struct host_if_drv *hif_drv,
+u8 wilc_connected_ssid[6] = {0};
+static s32 Handle_Connect(struct wilc_vif *vif,
struct connect_attr *pstrHostIFconnectAttr)
{
s32 result = 0;
@@ -985,10 +974,11 @@ static s32 Handle_Connect(struct host_if_drv *hif_drv,
u32 u32WidsCount = 0, dummyval = 0;
u8 *pu8CurrByte = NULL;
struct join_bss_param *ptstrJoinBssParam;
+ struct host_if_drv *hif_drv = vif->hif_drv;
PRINT_D(GENERIC_DBG, "Handling connect request\n");
- if (memcmp(pstrHostIFconnectAttr->bssid, u8ConnectedSSID, ETH_ALEN) == 0) {
+ if (memcmp(pstrHostIFconnectAttr->bssid, wilc_connected_ssid, ETH_ALEN) == 0) {
result = 0;
PRINT_ER("Trying to connect to an already connected AP, Discard connect request\n");
return result;
@@ -1008,7 +998,7 @@ static s32 Handle_Connect(struct host_if_drv *hif_drv,
memcpy(hif_drv->usr_conn_req.pu8bssid, pstrHostIFconnectAttr->bssid, 6);
}
- hif_drv->usr_conn_req.ssidLen = pstrHostIFconnectAttr->ssid_len;
+ hif_drv->usr_conn_req.ssid_len = pstrHostIFconnectAttr->ssid_len;
if (pstrHostIFconnectAttr->ssid) {
hif_drv->usr_conn_req.pu8ssid = kmalloc(pstrHostIFconnectAttr->ssid_len + 1, GFP_KERNEL);
memcpy(hif_drv->usr_conn_req.pu8ssid,
@@ -1017,18 +1007,18 @@ static s32 Handle_Connect(struct host_if_drv *hif_drv,
hif_drv->usr_conn_req.pu8ssid[pstrHostIFconnectAttr->ssid_len] = '\0';
}
- hif_drv->usr_conn_req.ConnReqIEsLen = pstrHostIFconnectAttr->ies_len;
+ hif_drv->usr_conn_req.ies_len = pstrHostIFconnectAttr->ies_len;
if (pstrHostIFconnectAttr->ies) {
- hif_drv->usr_conn_req.pu8ConnReqIEs = kmalloc(pstrHostIFconnectAttr->ies_len, GFP_KERNEL);
- memcpy(hif_drv->usr_conn_req.pu8ConnReqIEs,
+ hif_drv->usr_conn_req.ies = kmalloc(pstrHostIFconnectAttr->ies_len, GFP_KERNEL);
+ memcpy(hif_drv->usr_conn_req.ies,
pstrHostIFconnectAttr->ies,
pstrHostIFconnectAttr->ies_len);
}
hif_drv->usr_conn_req.u8security = pstrHostIFconnectAttr->security;
- hif_drv->usr_conn_req.tenuAuth_type = pstrHostIFconnectAttr->auth_type;
- hif_drv->usr_conn_req.pfUserConnectResult = pstrHostIFconnectAttr->result;
- hif_drv->usr_conn_req.u32UserConnectPvoid = pstrHostIFconnectAttr->arg;
+ hif_drv->usr_conn_req.auth_type = pstrHostIFconnectAttr->auth_type;
+ hif_drv->usr_conn_req.conn_result = pstrHostIFconnectAttr->result;
+ hif_drv->usr_conn_req.arg = pstrHostIFconnectAttr->arg;
strWIDList[u32WidsCount].id = WID_SUCCESS_FRAME_COUNT;
strWIDList[u32WidsCount].type = WID_INT;
@@ -1051,14 +1041,14 @@ static s32 Handle_Connect(struct host_if_drv *hif_drv,
{
strWIDList[u32WidsCount].id = WID_INFO_ELEMENT_ASSOCIATE;
strWIDList[u32WidsCount].type = WID_BIN_DATA;
- strWIDList[u32WidsCount].val = hif_drv->usr_conn_req.pu8ConnReqIEs;
- strWIDList[u32WidsCount].size = hif_drv->usr_conn_req.ConnReqIEsLen;
+ strWIDList[u32WidsCount].val = hif_drv->usr_conn_req.ies;
+ strWIDList[u32WidsCount].size = hif_drv->usr_conn_req.ies_len;
u32WidsCount++;
if (memcmp("DIRECT-", pstrHostIFconnectAttr->ssid, 7)) {
- info_element_size = hif_drv->usr_conn_req.ConnReqIEsLen;
+ info_element_size = hif_drv->usr_conn_req.ies_len;
info_element = kmalloc(info_element_size, GFP_KERNEL);
- memcpy(info_element, hif_drv->usr_conn_req.pu8ConnReqIEs,
+ memcpy(info_element, hif_drv->usr_conn_req.ies,
info_element_size);
}
}
@@ -1076,13 +1066,14 @@ static s32 Handle_Connect(struct host_if_drv *hif_drv,
strWIDList[u32WidsCount].id = (u16)WID_AUTH_TYPE;
strWIDList[u32WidsCount].type = WID_CHAR;
strWIDList[u32WidsCount].size = sizeof(char);
- strWIDList[u32WidsCount].val = (s8 *)(&hif_drv->usr_conn_req.tenuAuth_type);
+ strWIDList[u32WidsCount].val = (s8 *)&hif_drv->usr_conn_req.auth_type;
u32WidsCount++;
if (memcmp("DIRECT-", pstrHostIFconnectAttr->ssid, 7))
- auth_type = (u8)hif_drv->usr_conn_req.tenuAuth_type;
+ auth_type = (u8)hif_drv->usr_conn_req.auth_type;
- PRINT_INFO(HOSTINF_DBG, "Authentication Type = %x\n", hif_drv->usr_conn_req.tenuAuth_type);
+ PRINT_INFO(HOSTINF_DBG, "Authentication Type = %x\n",
+ hif_drv->usr_conn_req.auth_type);
PRINT_D(HOSTINF_DBG, "Connecting to network of SSID %s on channel %d\n",
hif_drv->usr_conn_req.pu8ssid, pstrHostIFconnectAttr->ch);
@@ -1141,7 +1132,7 @@ static s32 Handle_Connect(struct host_if_drv *hif_drv,
*(pu8CurrByte++) = ptstrJoinBssParam->uapsd_cap;
*(pu8CurrByte++) = ptstrJoinBssParam->ht_capable;
- hif_drv->usr_conn_req.IsHTCapable = ptstrJoinBssParam->ht_capable;
+ hif_drv->usr_conn_req.ht_capable = ptstrJoinBssParam->ht_capable;
*(pu8CurrByte++) = ptstrJoinBssParam->rsn_found;
PRINT_D(HOSTINF_DBG, "* rsn found %d*\n", *(pu8CurrByte - 1));
@@ -1194,36 +1185,38 @@ static s32 Handle_Connect(struct host_if_drv *hif_drv,
if (memcmp("DIRECT-", pstrHostIFconnectAttr->ssid, 7)) {
memcpy(join_req, pu8CurrByte, join_req_size);
- join_req_drv = hif_drv;
+ join_req_vif = vif;
}
PRINT_D(GENERIC_DBG, "send HOST_IF_WAITING_CONN_RESP\n");
if (pstrHostIFconnectAttr->bssid) {
- memcpy(u8ConnectedSSID, pstrHostIFconnectAttr->bssid, ETH_ALEN);
-
- PRINT_D(GENERIC_DBG, "save Bssid = %pM\n", pstrHostIFconnectAttr->bssid);
- PRINT_D(GENERIC_DBG, "save bssid = %pM\n", u8ConnectedSSID);
+ memcpy(wilc_connected_ssid,
+ pstrHostIFconnectAttr->bssid, ETH_ALEN);
+ PRINT_D(GENERIC_DBG, "save Bssid = %pM\n",
+ pstrHostIFconnectAttr->bssid);
+ PRINT_D(GENERIC_DBG, "save bssid = %pM\n", wilc_connected_ssid);
}
- result = send_config_pkt(SET_CFG, strWIDList, u32WidsCount,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, strWIDList,
+ u32WidsCount,
+ wilc_get_vif_idx(vif));
if (result) {
PRINT_ER("failed to send config packet\n");
result = -EFAULT;
goto ERRORHANDLER;
} else {
PRINT_D(GENERIC_DBG, "set HOST_IF_WAITING_CONN_RESP\n");
- hif_drv->enuHostIFstate = HOST_IF_WAITING_CONN_RESP;
+ hif_drv->hif_state = HOST_IF_WAITING_CONN_RESP;
}
ERRORHANDLER:
if (result) {
tstrConnectInfo strConnectInfo;
- del_timer(&hif_drv->hConnectTimer);
+ del_timer(&hif_drv->connect_timer);
- PRINT_D(HOSTINF_DBG, "could not start connecting to the required network\n");
+ PRINT_D(HOSTINF_DBG, "could not start wilc_connecting to the required network\n");
memset(&strConnectInfo, 0, sizeof(tstrConnectInfo));
@@ -1244,7 +1237,7 @@ ERRORHANDLER:
MAC_DISCONNECTED,
NULL,
pstrHostIFconnectAttr->arg);
- hif_drv->enuHostIFstate = HOST_IF_IDLE;
+ hif_drv->hif_state = HOST_IF_IDLE;
kfree(strConnectInfo.pu8ReqIEs);
strConnectInfo.pu8ReqIEs = NULL;
@@ -1267,7 +1260,7 @@ ERRORHANDLER:
return result;
}
-static s32 Handle_FlushConnect(struct host_if_drv *hif_drv)
+static s32 Handle_FlushConnect(struct wilc_vif *vif)
{
s32 result = 0;
struct wid strWIDList[5];
@@ -1303,8 +1296,9 @@ static s32 Handle_FlushConnect(struct host_if_drv *hif_drv)
u32WidsCount++;
- result = send_config_pkt(SET_CFG, strWIDList, u32WidsCount,
- get_id_from_handler(join_req_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, strWIDList,
+ u32WidsCount,
+ wilc_get_vif_idx(join_req_vif));
if (result) {
PRINT_ER("failed to send config packet\n");
result = -EINVAL;
@@ -1313,43 +1307,44 @@ static s32 Handle_FlushConnect(struct host_if_drv *hif_drv)
return result;
}
-static s32 Handle_ConnectTimeout(struct host_if_drv *hif_drv)
+static s32 Handle_ConnectTimeout(struct wilc_vif *vif)
{
s32 result = 0;
tstrConnectInfo strConnectInfo;
struct wid wid;
u16 u16DummyReasonCode = 0;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
PRINT_ER("Driver handler is NULL\n");
return result;
}
- hif_drv->enuHostIFstate = HOST_IF_IDLE;
+ hif_drv->hif_state = HOST_IF_IDLE;
scan_while_connected = false;
memset(&strConnectInfo, 0, sizeof(tstrConnectInfo));
- if (hif_drv->usr_conn_req.pfUserConnectResult) {
+ if (hif_drv->usr_conn_req.conn_result) {
if (hif_drv->usr_conn_req.pu8bssid) {
memcpy(strConnectInfo.au8bssid,
hif_drv->usr_conn_req.pu8bssid, 6);
}
- if (hif_drv->usr_conn_req.pu8ConnReqIEs) {
- strConnectInfo.ReqIEsLen = hif_drv->usr_conn_req.ConnReqIEsLen;
- strConnectInfo.pu8ReqIEs = kmalloc(hif_drv->usr_conn_req.ConnReqIEsLen, GFP_KERNEL);
+ if (hif_drv->usr_conn_req.ies) {
+ strConnectInfo.ReqIEsLen = hif_drv->usr_conn_req.ies_len;
+ strConnectInfo.pu8ReqIEs = kmalloc(hif_drv->usr_conn_req.ies_len, GFP_KERNEL);
memcpy(strConnectInfo.pu8ReqIEs,
- hif_drv->usr_conn_req.pu8ConnReqIEs,
- hif_drv->usr_conn_req.ConnReqIEsLen);
+ hif_drv->usr_conn_req.ies,
+ hif_drv->usr_conn_req.ies_len);
}
- hif_drv->usr_conn_req.pfUserConnectResult(CONN_DISCONN_EVENT_CONN_RESP,
- &strConnectInfo,
- MAC_DISCONNECTED,
- NULL,
- hif_drv->usr_conn_req.u32UserConnectPvoid);
+ hif_drv->usr_conn_req.conn_result(CONN_DISCONN_EVENT_CONN_RESP,
+ &strConnectInfo,
+ MAC_DISCONNECTED,
+ NULL,
+ hif_drv->usr_conn_req.arg);
kfree(strConnectInfo.pu8ReqIEs);
strConnectInfo.pu8ReqIEs = NULL;
@@ -1364,25 +1359,28 @@ static s32 Handle_ConnectTimeout(struct host_if_drv *hif_drv)
PRINT_D(HOSTINF_DBG, "Sending disconnect request\n");
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result)
PRINT_ER("Failed to send dissconect config packet\n");
- hif_drv->usr_conn_req.ssidLen = 0;
+ hif_drv->usr_conn_req.ssid_len = 0;
kfree(hif_drv->usr_conn_req.pu8ssid);
+ hif_drv->usr_conn_req.pu8ssid = NULL;
kfree(hif_drv->usr_conn_req.pu8bssid);
- hif_drv->usr_conn_req.ConnReqIEsLen = 0;
- kfree(hif_drv->usr_conn_req.pu8ConnReqIEs);
+ hif_drv->usr_conn_req.pu8bssid = NULL;
+ hif_drv->usr_conn_req.ies_len = 0;
+ kfree(hif_drv->usr_conn_req.ies);
+ hif_drv->usr_conn_req.ies = NULL;
- eth_zero_addr(u8ConnectedSSID);
+ eth_zero_addr(wilc_connected_ssid);
- if (join_req && join_req_drv == hif_drv) {
+ if (join_req && join_req_vif == vif) {
kfree(join_req);
join_req = NULL;
}
- if (info_element && join_req_drv == hif_drv) {
+ if (info_element && join_req_vif == vif) {
kfree(info_element);
info_element = NULL;
}
@@ -1390,7 +1388,7 @@ static s32 Handle_ConnectTimeout(struct host_if_drv *hif_drv)
return result;
}
-static s32 Handle_RcvdNtwrkInfo(struct host_if_drv *hif_drv,
+static s32 Handle_RcvdNtwrkInfo(struct wilc_vif *vif,
struct rcvd_net_info *pstrRcvdNetworkInfo)
{
u32 i;
@@ -1398,30 +1396,31 @@ static s32 Handle_RcvdNtwrkInfo(struct host_if_drv *hif_drv,
s32 result = 0;
tstrNetworkInfo *pstrNetworkInfo = NULL;
void *pJoinParams = NULL;
+ struct host_if_drv *hif_drv = vif->hif_drv;
bNewNtwrkFound = true;
PRINT_INFO(HOSTINF_DBG, "Handling received network info\n");
- if (hif_drv->usr_scan_req.pfUserScanResult) {
+ if (hif_drv->usr_scan_req.scan_result) {
PRINT_D(HOSTINF_DBG, "State: Scanning, parsing network information received\n");
- parse_network_info(pstrRcvdNetworkInfo->buffer, &pstrNetworkInfo);
+ wilc_parse_network_info(pstrRcvdNetworkInfo->buffer, &pstrNetworkInfo);
if ((!pstrNetworkInfo) ||
- (!hif_drv->usr_scan_req.pfUserScanResult)) {
+ (!hif_drv->usr_scan_req.scan_result)) {
PRINT_ER("driver is null\n");
result = -EINVAL;
goto done;
}
- for (i = 0; i < hif_drv->usr_scan_req.u32RcvdChCount; i++) {
- if ((hif_drv->usr_scan_req.astrFoundNetworkInfo[i].au8bssid) &&
+ for (i = 0; i < hif_drv->usr_scan_req.rcvd_ch_cnt; i++) {
+ if ((hif_drv->usr_scan_req.net_info[i].au8bssid) &&
(pstrNetworkInfo->au8bssid)) {
- if (memcmp(hif_drv->usr_scan_req.astrFoundNetworkInfo[i].au8bssid,
+ if (memcmp(hif_drv->usr_scan_req.net_info[i].au8bssid,
pstrNetworkInfo->au8bssid, 6) == 0) {
- if (pstrNetworkInfo->s8rssi <= hif_drv->usr_scan_req.astrFoundNetworkInfo[i].s8rssi) {
+ if (pstrNetworkInfo->s8rssi <= hif_drv->usr_scan_req.net_info[i].s8rssi) {
PRINT_D(HOSTINF_DBG, "Network previously discovered\n");
goto done;
} else {
- hif_drv->usr_scan_req.astrFoundNetworkInfo[i].s8rssi = pstrNetworkInfo->s8rssi;
+ hif_drv->usr_scan_req.net_info[i].s8rssi = pstrNetworkInfo->s8rssi;
bNewNtwrkFound = false;
break;
}
@@ -1432,30 +1431,30 @@ static s32 Handle_RcvdNtwrkInfo(struct host_if_drv *hif_drv,
if (bNewNtwrkFound) {
PRINT_D(HOSTINF_DBG, "New network found\n");
- if (hif_drv->usr_scan_req.u32RcvdChCount < MAX_NUM_SCANNED_NETWORKS) {
- hif_drv->usr_scan_req.astrFoundNetworkInfo[hif_drv->usr_scan_req.u32RcvdChCount].s8rssi = pstrNetworkInfo->s8rssi;
+ if (hif_drv->usr_scan_req.rcvd_ch_cnt < MAX_NUM_SCANNED_NETWORKS) {
+ hif_drv->usr_scan_req.net_info[hif_drv->usr_scan_req.rcvd_ch_cnt].s8rssi = pstrNetworkInfo->s8rssi;
- if (hif_drv->usr_scan_req.astrFoundNetworkInfo[hif_drv->usr_scan_req.u32RcvdChCount].au8bssid &&
+ if (hif_drv->usr_scan_req.net_info[hif_drv->usr_scan_req.rcvd_ch_cnt].au8bssid &&
pstrNetworkInfo->au8bssid) {
- memcpy(hif_drv->usr_scan_req.astrFoundNetworkInfo[hif_drv->usr_scan_req.u32RcvdChCount].au8bssid,
+ memcpy(hif_drv->usr_scan_req.net_info[hif_drv->usr_scan_req.rcvd_ch_cnt].au8bssid,
pstrNetworkInfo->au8bssid, 6);
- hif_drv->usr_scan_req.u32RcvdChCount++;
+ hif_drv->usr_scan_req.rcvd_ch_cnt++;
pstrNetworkInfo->bNewNetwork = true;
pJoinParams = host_int_ParseJoinBssParam(pstrNetworkInfo);
- hif_drv->usr_scan_req.pfUserScanResult(SCAN_EVENT_NETWORK_FOUND, pstrNetworkInfo,
- hif_drv->usr_scan_req.u32UserScanPvoid,
- pJoinParams);
+ hif_drv->usr_scan_req.scan_result(SCAN_EVENT_NETWORK_FOUND, pstrNetworkInfo,
+ hif_drv->usr_scan_req.arg,
+ pJoinParams);
}
} else {
PRINT_WRN(HOSTINF_DBG, "Discovered networks exceeded max. limit\n");
}
} else {
pstrNetworkInfo->bNewNetwork = false;
- hif_drv->usr_scan_req.pfUserScanResult(SCAN_EVENT_NETWORK_FOUND, pstrNetworkInfo,
- hif_drv->usr_scan_req.u32UserScanPvoid, NULL);
+ hif_drv->usr_scan_req.scan_result(SCAN_EVENT_NETWORK_FOUND, pstrNetworkInfo,
+ hif_drv->usr_scan_req.arg, NULL);
}
}
@@ -1464,14 +1463,19 @@ done:
pstrRcvdNetworkInfo->buffer = NULL;
if (pstrNetworkInfo) {
- DeallocateNetworkInfo(pstrNetworkInfo);
+ wilc_dealloc_network_info(pstrNetworkInfo);
pstrNetworkInfo = NULL;
}
return result;
}
-static s32 Handle_RcvdGnrlAsyncInfo(struct host_if_drv *hif_drv,
+static s32 host_int_get_assoc_res_info(struct wilc_vif *vif,
+ u8 *pu8AssocRespInfo,
+ u32 u32MaxAssocRespInfoLen,
+ u32 *pu32RcvdAssocRespInfoLen);
+
+static s32 Handle_RcvdGnrlAsyncInfo(struct wilc_vif *vif,
struct rcvd_async_info *pstrRcvdGnrlAsyncInfo)
{
s32 result = 0;
@@ -1486,19 +1490,20 @@ static s32 Handle_RcvdGnrlAsyncInfo(struct host_if_drv *hif_drv,
tstrConnectInfo strConnectInfo;
tstrDisconnectNotifInfo strDisconnectNotifInfo;
s32 s32Err = 0;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
PRINT_ER("Driver handler is NULL\n");
return -ENODEV;
}
- PRINT_D(GENERIC_DBG, "Current State = %d,Received state = %d\n", hif_drv->enuHostIFstate,
- pstrRcvdGnrlAsyncInfo->buffer[7]);
+ PRINT_D(GENERIC_DBG, "Current State = %d,Received state = %d\n",
+ hif_drv->hif_state, pstrRcvdGnrlAsyncInfo->buffer[7]);
- if ((hif_drv->enuHostIFstate == HOST_IF_WAITING_CONN_RESP) ||
- (hif_drv->enuHostIFstate == HOST_IF_CONNECTED) ||
- hif_drv->usr_scan_req.pfUserScanResult) {
+ if ((hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP) ||
+ (hif_drv->hif_state == HOST_IF_CONNECTED) ||
+ hif_drv->usr_scan_req.scan_result) {
if (!pstrRcvdGnrlAsyncInfo->buffer ||
- !hif_drv->usr_conn_req.pfUserConnectResult) {
+ !hif_drv->usr_conn_req.conn_result) {
PRINT_ER("driver is null\n");
return -EINVAL;
}
@@ -1518,8 +1523,8 @@ static s32 Handle_RcvdGnrlAsyncInfo(struct host_if_drv *hif_drv,
u8MacStatusReasonCode = pstrRcvdGnrlAsyncInfo->buffer[8];
u8MacStatusAdditionalInfo = pstrRcvdGnrlAsyncInfo->buffer[9];
PRINT_INFO(HOSTINF_DBG, "Recieved MAC status = %d with Reason = %d , Info = %d\n", u8MacStatus, u8MacStatusReasonCode, u8MacStatusAdditionalInfo);
- if (hif_drv->enuHostIFstate == HOST_IF_WAITING_CONN_RESP) {
- u32 u32RcvdAssocRespInfoLen;
+ if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP) {
+ u32 u32RcvdAssocRespInfoLen = 0;
tstrConnectRespInfo *pstrConnectRespInfo = NULL;
PRINT_D(HOSTINF_DBG, "Recieved MAC status = %d with Reason = %d , Code = %d\n", u8MacStatus, u8MacStatusReasonCode, u8MacStatusAdditionalInfo);
@@ -1529,7 +1534,7 @@ static s32 Handle_RcvdGnrlAsyncInfo(struct host_if_drv *hif_drv,
if (u8MacStatus == MAC_CONNECTED) {
memset(rcv_assoc_resp, 0, MAX_ASSOC_RESP_FRAME_SIZE);
- host_int_get_assoc_res_info(hif_drv,
+ host_int_get_assoc_res_info(vif,
rcv_assoc_resp,
MAX_ASSOC_RESP_FRAME_SIZE,
&u32RcvdAssocRespInfoLen);
@@ -1538,10 +1543,10 @@ static s32 Handle_RcvdGnrlAsyncInfo(struct host_if_drv *hif_drv,
if (u32RcvdAssocRespInfoLen != 0) {
PRINT_D(HOSTINF_DBG, "Parsing association response\n");
- s32Err = ParseAssocRespInfo(rcv_assoc_resp, u32RcvdAssocRespInfoLen,
+ s32Err = wilc_parse_assoc_resp_info(rcv_assoc_resp, u32RcvdAssocRespInfoLen,
&pstrConnectRespInfo);
if (s32Err) {
- PRINT_ER("ParseAssocRespInfo() returned error %d\n", s32Err);
+ PRINT_ER("wilc_parse_assoc_resp_info() returned error %d\n", s32Err);
} else {
strConnectInfo.u16ConnectStatus = pstrConnectRespInfo->u16ConnectStatus;
@@ -1556,7 +1561,7 @@ static s32 Handle_RcvdGnrlAsyncInfo(struct host_if_drv *hif_drv,
}
if (pstrConnectRespInfo) {
- DeallocateAssocRespInfo(pstrConnectRespInfo);
+ wilc_dealloc_assoc_resp_info(pstrConnectRespInfo);
pstrConnectRespInfo = NULL;
}
}
@@ -1566,11 +1571,10 @@ static s32 Handle_RcvdGnrlAsyncInfo(struct host_if_drv *hif_drv,
if ((u8MacStatus == MAC_CONNECTED) &&
(strConnectInfo.u16ConnectStatus != SUCCESSFUL_STATUSCODE)) {
PRINT_ER("Received MAC status is MAC_CONNECTED while the received status code in Asoc Resp is not SUCCESSFUL_STATUSCODE\n");
- eth_zero_addr(u8ConnectedSSID);
-
+ eth_zero_addr(wilc_connected_ssid);
} else if (u8MacStatus == MAC_DISCONNECTED) {
PRINT_ER("Received MAC status is MAC_DISCONNECTED\n");
- eth_zero_addr(u8ConnectedSSID);
+ eth_zero_addr(wilc_connected_ssid);
}
if (hif_drv->usr_conn_req.pu8bssid) {
@@ -1579,40 +1583,40 @@ static s32 Handle_RcvdGnrlAsyncInfo(struct host_if_drv *hif_drv,
if ((u8MacStatus == MAC_CONNECTED) &&
(strConnectInfo.u16ConnectStatus == SUCCESSFUL_STATUSCODE)) {
- memcpy(hif_drv->au8AssociatedBSSID,
+ memcpy(hif_drv->assoc_bssid,
hif_drv->usr_conn_req.pu8bssid, ETH_ALEN);
}
}
- if (hif_drv->usr_conn_req.pu8ConnReqIEs) {
- strConnectInfo.ReqIEsLen = hif_drv->usr_conn_req.ConnReqIEsLen;
- strConnectInfo.pu8ReqIEs = kmalloc(hif_drv->usr_conn_req.ConnReqIEsLen, GFP_KERNEL);
+ if (hif_drv->usr_conn_req.ies) {
+ strConnectInfo.ReqIEsLen = hif_drv->usr_conn_req.ies_len;
+ strConnectInfo.pu8ReqIEs = kmalloc(hif_drv->usr_conn_req.ies_len, GFP_KERNEL);
memcpy(strConnectInfo.pu8ReqIEs,
- hif_drv->usr_conn_req.pu8ConnReqIEs,
- hif_drv->usr_conn_req.ConnReqIEsLen);
+ hif_drv->usr_conn_req.ies,
+ hif_drv->usr_conn_req.ies_len);
}
- del_timer(&hif_drv->hConnectTimer);
- hif_drv->usr_conn_req.pfUserConnectResult(CONN_DISCONN_EVENT_CONN_RESP,
- &strConnectInfo,
- u8MacStatus,
- NULL,
- hif_drv->usr_conn_req.u32UserConnectPvoid);
+ del_timer(&hif_drv->connect_timer);
+ hif_drv->usr_conn_req.conn_result(CONN_DISCONN_EVENT_CONN_RESP,
+ &strConnectInfo,
+ u8MacStatus,
+ NULL,
+ hif_drv->usr_conn_req.arg);
if ((u8MacStatus == MAC_CONNECTED) &&
(strConnectInfo.u16ConnectStatus == SUCCESSFUL_STATUSCODE)) {
- host_int_set_power_mgmt(hif_drv, 0, 0);
+ wilc_set_power_mgmt(vif, 0, 0);
PRINT_D(HOSTINF_DBG, "MAC status : CONNECTED and Connect Status : Successful\n");
- hif_drv->enuHostIFstate = HOST_IF_CONNECTED;
+ hif_drv->hif_state = HOST_IF_CONNECTED;
PRINT_D(GENERIC_DBG, "Obtaining an IP, Disable Scan\n");
- g_obtainingIP = true;
- mod_timer(&hDuringIpTimer,
+ wilc_optaining_ip = true;
+ mod_timer(&wilc_during_ip_timer,
jiffies + msecs_to_jiffies(10000));
} else {
PRINT_D(HOSTINF_DBG, "MAC status : %d and Connect Status : %d\n", u8MacStatus, strConnectInfo.u16ConnectStatus);
- hif_drv->enuHostIFstate = HOST_IF_IDLE;
+ hif_drv->hif_state = HOST_IF_IDLE;
scan_while_connected = false;
}
@@ -1621,69 +1625,75 @@ static s32 Handle_RcvdGnrlAsyncInfo(struct host_if_drv *hif_drv,
kfree(strConnectInfo.pu8ReqIEs);
strConnectInfo.pu8ReqIEs = NULL;
- hif_drv->usr_conn_req.ssidLen = 0;
+ hif_drv->usr_conn_req.ssid_len = 0;
kfree(hif_drv->usr_conn_req.pu8ssid);
+ hif_drv->usr_conn_req.pu8ssid = NULL;
kfree(hif_drv->usr_conn_req.pu8bssid);
- hif_drv->usr_conn_req.ConnReqIEsLen = 0;
- kfree(hif_drv->usr_conn_req.pu8ConnReqIEs);
+ hif_drv->usr_conn_req.pu8bssid = NULL;
+ hif_drv->usr_conn_req.ies_len = 0;
+ kfree(hif_drv->usr_conn_req.ies);
+ hif_drv->usr_conn_req.ies = NULL;
} else if ((u8MacStatus == MAC_DISCONNECTED) &&
- (hif_drv->enuHostIFstate == HOST_IF_CONNECTED)) {
+ (hif_drv->hif_state == HOST_IF_CONNECTED)) {
PRINT_D(HOSTINF_DBG, "Received MAC_DISCONNECTED from the FW\n");
memset(&strDisconnectNotifInfo, 0, sizeof(tstrDisconnectNotifInfo));
- if (hif_drv->usr_scan_req.pfUserScanResult) {
+ if (hif_drv->usr_scan_req.scan_result) {
PRINT_D(HOSTINF_DBG, "\n\n<< Abort the running OBSS Scan >>\n\n");
- del_timer(&hif_drv->hScanTimer);
- Handle_ScanDone((void *)hif_drv, SCAN_EVENT_ABORTED);
+ del_timer(&hif_drv->scan_timer);
+ Handle_ScanDone(vif, SCAN_EVENT_ABORTED);
}
strDisconnectNotifInfo.u16reason = 0;
strDisconnectNotifInfo.ie = NULL;
strDisconnectNotifInfo.ie_len = 0;
- if (hif_drv->usr_conn_req.pfUserConnectResult) {
- g_obtainingIP = false;
- host_int_set_power_mgmt(hif_drv, 0, 0);
+ if (hif_drv->usr_conn_req.conn_result) {
+ wilc_optaining_ip = false;
+ wilc_set_power_mgmt(vif, 0, 0);
- hif_drv->usr_conn_req.pfUserConnectResult(CONN_DISCONN_EVENT_DISCONN_NOTIF,
- NULL,
- 0,
- &strDisconnectNotifInfo,
- hif_drv->usr_conn_req.u32UserConnectPvoid);
+ hif_drv->usr_conn_req.conn_result(CONN_DISCONN_EVENT_DISCONN_NOTIF,
+ NULL,
+ 0,
+ &strDisconnectNotifInfo,
+ hif_drv->usr_conn_req.arg);
} else {
PRINT_ER("Connect result callback function is NULL\n");
}
- eth_zero_addr(hif_drv->au8AssociatedBSSID);
+ eth_zero_addr(hif_drv->assoc_bssid);
- hif_drv->usr_conn_req.ssidLen = 0;
+ hif_drv->usr_conn_req.ssid_len = 0;
kfree(hif_drv->usr_conn_req.pu8ssid);
+ hif_drv->usr_conn_req.pu8ssid = NULL;
kfree(hif_drv->usr_conn_req.pu8bssid);
- hif_drv->usr_conn_req.ConnReqIEsLen = 0;
- kfree(hif_drv->usr_conn_req.pu8ConnReqIEs);
+ hif_drv->usr_conn_req.pu8bssid = NULL;
+ hif_drv->usr_conn_req.ies_len = 0;
+ kfree(hif_drv->usr_conn_req.ies);
+ hif_drv->usr_conn_req.ies = NULL;
- if (join_req && join_req_drv == hif_drv) {
+ if (join_req && join_req_vif == vif) {
kfree(join_req);
join_req = NULL;
}
- if (info_element && join_req_drv == hif_drv) {
+ if (info_element && join_req_vif == vif) {
kfree(info_element);
info_element = NULL;
}
- hif_drv->enuHostIFstate = HOST_IF_IDLE;
+ hif_drv->hif_state = HOST_IF_IDLE;
scan_while_connected = false;
} else if ((u8MacStatus == MAC_DISCONNECTED) &&
- (hif_drv->usr_scan_req.pfUserScanResult)) {
+ (hif_drv->usr_scan_req.scan_result)) {
PRINT_D(HOSTINF_DBG, "Received MAC_DISCONNECTED from the FW while scanning\n");
PRINT_D(HOSTINF_DBG, "\n\n<< Abort the running Scan >>\n\n");
- del_timer(&hif_drv->hScanTimer);
- if (hif_drv->usr_scan_req.pfUserScanResult)
- Handle_ScanDone(hif_drv, SCAN_EVENT_ABORTED);
+ del_timer(&hif_drv->scan_timer);
+ if (hif_drv->usr_scan_req.scan_result)
+ Handle_ScanDone(vif, SCAN_EVENT_ABORTED);
}
}
@@ -1693,7 +1703,7 @@ static s32 Handle_RcvdGnrlAsyncInfo(struct host_if_drv *hif_drv,
return result;
}
-static int Handle_Key(struct host_if_drv *hif_drv,
+static int Handle_Key(struct wilc_vif *vif,
struct key_attr *pstrHostIFkeyAttr)
{
s32 result = 0;
@@ -1703,6 +1713,7 @@ static int Handle_Key(struct host_if_drv *hif_drv,
u8 *pu8keybuf;
s8 s8idxarray[1];
s8 ret = 0;
+ struct host_if_drv *hif_drv = vif->hif_drv;
switch (pstrHostIFkeyAttr->type) {
case WEP:
@@ -1742,12 +1753,11 @@ static int Handle_Key(struct host_if_drv *hif_drv,
strWIDList[3].size = pstrHostIFkeyAttr->attr.wep.key_len;
strWIDList[3].val = (s8 *)pu8keybuf;
- result = send_config_pkt(SET_CFG, strWIDList, 4,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG,
+ strWIDList, 4,
+ wilc_get_vif_idx(vif));
kfree(pu8keybuf);
- }
-
- if (pstrHostIFkeyAttr->action & ADDKEY) {
+ } else if (pstrHostIFkeyAttr->action & ADDKEY) {
PRINT_D(HOSTINF_DBG, "Handling WEP key\n");
pu8keybuf = kmalloc(pstrHostIFkeyAttr->attr.wep.key_len + 2, GFP_KERNEL);
if (!pu8keybuf) {
@@ -1765,8 +1775,9 @@ static int Handle_Key(struct host_if_drv *hif_drv,
wid.val = (s8 *)pu8keybuf;
wid.size = pstrHostIFkeyAttr->attr.wep.key_len + 2;
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG,
+ &wid, 1,
+ wilc_get_vif_idx(vif));
kfree(pu8keybuf);
} else if (pstrHostIFkeyAttr->action & REMOVEKEY) {
PRINT_D(HOSTINF_DBG, "Removing key\n");
@@ -1777,8 +1788,9 @@ static int Handle_Key(struct host_if_drv *hif_drv,
wid.val = s8idxarray;
wid.size = 1;
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG,
+ &wid, 1,
+ wilc_get_vif_idx(vif));
} else {
wid.id = (u16)WID_KEY_ID;
wid.type = WID_CHAR;
@@ -1787,13 +1799,14 @@ static int Handle_Key(struct host_if_drv *hif_drv,
PRINT_D(HOSTINF_DBG, "Setting default key index\n");
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG,
+ &wid, 1,
+ wilc_get_vif_idx(vif));
}
- up(&hif_drv->hSemTestKeyBlock);
+ up(&hif_drv->sem_test_key_block);
break;
- case WPARxGtk:
+ case WPA_RX_GTK:
if (pstrHostIFkeyAttr->action & ADDKEY_AP) {
pu8keybuf = kzalloc(RX_MIC_KEY_MSG_LEN, GFP_KERNEL);
if (!pu8keybuf) {
@@ -1820,14 +1833,13 @@ static int Handle_Key(struct host_if_drv *hif_drv,
strWIDList[1].val = (s8 *)pu8keybuf;
strWIDList[1].size = RX_MIC_KEY_MSG_LEN;
- result = send_config_pkt(SET_CFG, strWIDList, 2,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG,
+ strWIDList, 2,
+ wilc_get_vif_idx(vif));
kfree(pu8keybuf);
- up(&hif_drv->hSemTestKeyBlock);
- }
-
- if (pstrHostIFkeyAttr->action & ADDKEY) {
+ up(&hif_drv->sem_test_key_block);
+ } else if (pstrHostIFkeyAttr->action & ADDKEY) {
PRINT_D(HOSTINF_DBG, "Handling group key(Rx) function\n");
pu8keybuf = kzalloc(RX_MIC_KEY_MSG_LEN, GFP_KERNEL);
@@ -1837,10 +1849,10 @@ static int Handle_Key(struct host_if_drv *hif_drv,
goto _WPARxGtk_end_case_;
}
- if (hif_drv->enuHostIFstate == HOST_IF_CONNECTED)
- memcpy(pu8keybuf, hif_drv->au8AssociatedBSSID, ETH_ALEN);
+ if (hif_drv->hif_state == HOST_IF_CONNECTED)
+ memcpy(pu8keybuf, hif_drv->assoc_bssid, ETH_ALEN);
else
- PRINT_ER("Couldn't handle WPARxGtk while enuHostIFstate is not HOST_IF_CONNECTED\n");
+ PRINT_ER("Couldn't handle WPARxGtk while state is not HOST_IF_CONNECTED\n");
memcpy(pu8keybuf + 6, pstrHostIFkeyAttr->attr.wpa.seq, 8);
memcpy(pu8keybuf + 14, &pstrHostIFkeyAttr->attr.wpa.index, 1);
@@ -1853,11 +1865,12 @@ static int Handle_Key(struct host_if_drv *hif_drv,
wid.val = (s8 *)pu8keybuf;
wid.size = RX_MIC_KEY_MSG_LEN;
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG,
+ &wid, 1,
+ wilc_get_vif_idx(vif));
kfree(pu8keybuf);
- up(&hif_drv->hSemTestKeyBlock);
+ up(&hif_drv->sem_test_key_block);
}
_WPARxGtk_end_case_:
kfree(pstrHostIFkeyAttr->attr.wpa.key);
@@ -1867,7 +1880,7 @@ _WPARxGtk_end_case_:
break;
- case WPAPtk:
+ case WPA_PTK:
if (pstrHostIFkeyAttr->action & ADDKEY_AP) {
pu8keybuf = kmalloc(PTK_KEY_MSG_LEN + 1, GFP_KERNEL);
if (!pu8keybuf) {
@@ -1892,12 +1905,12 @@ _WPARxGtk_end_case_:
strWIDList[1].val = (s8 *)pu8keybuf;
strWIDList[1].size = PTK_KEY_MSG_LEN + 1;
- result = send_config_pkt(SET_CFG, strWIDList, 2,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG,
+ strWIDList, 2,
+ wilc_get_vif_idx(vif));
kfree(pu8keybuf);
- up(&hif_drv->hSemTestKeyBlock);
- }
- if (pstrHostIFkeyAttr->action & ADDKEY) {
+ up(&hif_drv->sem_test_key_block);
+ } else if (pstrHostIFkeyAttr->action & ADDKEY) {
pu8keybuf = kmalloc(PTK_KEY_MSG_LEN, GFP_KERNEL);
if (!pu8keybuf) {
PRINT_ER("No buffer to send PTK Key\n");
@@ -1915,10 +1928,11 @@ _WPARxGtk_end_case_:
wid.val = (s8 *)pu8keybuf;
wid.size = PTK_KEY_MSG_LEN;
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG,
+ &wid, 1,
+ wilc_get_vif_idx(vif));
kfree(pu8keybuf);
- up(&hif_drv->hSemTestKeyBlock);
+ up(&hif_drv->sem_test_key_block);
}
_WPAPtk_end_case_:
@@ -1950,8 +1964,8 @@ _WPAPtk_end_case_:
wid.val = (s8 *)pu8keybuf;
wid.size = (pstrHostIFkeyAttr->attr.pmkid.numpmkid * PMKSA_KEY_LEN) + 1;
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
kfree(pu8keybuf);
break;
@@ -1963,9 +1977,10 @@ _WPAPtk_end_case_:
return result;
}
-static void Handle_Disconnect(struct host_if_drv *hif_drv)
+static void Handle_Disconnect(struct wilc_vif *vif)
{
struct wid wid;
+ struct host_if_drv *hif_drv = vif->hif_drv;
s32 result = 0;
u16 u16DummyReasonCode = 0;
@@ -1977,13 +1992,13 @@ static void Handle_Disconnect(struct host_if_drv *hif_drv)
PRINT_D(HOSTINF_DBG, "Sending disconnect request\n");
- g_obtainingIP = false;
- host_int_set_power_mgmt(hif_drv, 0, 0);
+ wilc_optaining_ip = false;
+ wilc_set_power_mgmt(vif, 0, 0);
- eth_zero_addr(u8ConnectedSSID);
+ eth_zero_addr(wilc_connected_ssid);
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result) {
PRINT_ER("Failed to send dissconect config packet\n");
@@ -1996,66 +2011,75 @@ static void Handle_Disconnect(struct host_if_drv *hif_drv)
strDisconnectNotifInfo.ie = NULL;
strDisconnectNotifInfo.ie_len = 0;
- if (hif_drv->usr_scan_req.pfUserScanResult) {
- del_timer(&hif_drv->hScanTimer);
- hif_drv->usr_scan_req.pfUserScanResult(SCAN_EVENT_ABORTED, NULL,
- hif_drv->usr_scan_req.u32UserScanPvoid, NULL);
-
- hif_drv->usr_scan_req.pfUserScanResult = NULL;
+ if (hif_drv->usr_scan_req.scan_result) {
+ del_timer(&hif_drv->scan_timer);
+ hif_drv->usr_scan_req.scan_result(SCAN_EVENT_ABORTED,
+ NULL,
+ hif_drv->usr_scan_req.arg,
+ NULL);
+ hif_drv->usr_scan_req.scan_result = NULL;
}
- if (hif_drv->usr_conn_req.pfUserConnectResult) {
- if (hif_drv->enuHostIFstate == HOST_IF_WAITING_CONN_RESP) {
+ if (hif_drv->usr_conn_req.conn_result) {
+ if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP) {
PRINT_D(HOSTINF_DBG, "Upper layer requested termination of connection\n");
- del_timer(&hif_drv->hConnectTimer);
+ del_timer(&hif_drv->connect_timer);
}
- hif_drv->usr_conn_req.pfUserConnectResult(CONN_DISCONN_EVENT_DISCONN_NOTIF, NULL,
- 0, &strDisconnectNotifInfo, hif_drv->usr_conn_req.u32UserConnectPvoid);
+ hif_drv->usr_conn_req.conn_result(CONN_DISCONN_EVENT_DISCONN_NOTIF,
+ NULL,
+ 0,
+ &strDisconnectNotifInfo,
+ hif_drv->usr_conn_req.arg);
} else {
- PRINT_ER("usr_conn_req.pfUserConnectResult = NULL\n");
+ PRINT_ER("usr_conn_req.conn_result = NULL\n");
}
scan_while_connected = false;
- hif_drv->enuHostIFstate = HOST_IF_IDLE;
+ hif_drv->hif_state = HOST_IF_IDLE;
- eth_zero_addr(hif_drv->au8AssociatedBSSID);
+ eth_zero_addr(hif_drv->assoc_bssid);
- hif_drv->usr_conn_req.ssidLen = 0;
+ hif_drv->usr_conn_req.ssid_len = 0;
kfree(hif_drv->usr_conn_req.pu8ssid);
+ hif_drv->usr_conn_req.pu8ssid = NULL;
kfree(hif_drv->usr_conn_req.pu8bssid);
- hif_drv->usr_conn_req.ConnReqIEsLen = 0;
- kfree(hif_drv->usr_conn_req.pu8ConnReqIEs);
+ hif_drv->usr_conn_req.pu8bssid = NULL;
+ hif_drv->usr_conn_req.ies_len = 0;
+ kfree(hif_drv->usr_conn_req.ies);
+ hif_drv->usr_conn_req.ies = NULL;
- if (join_req && join_req_drv == hif_drv) {
+ if (join_req && join_req_vif == vif) {
kfree(join_req);
join_req = NULL;
}
- if (info_element && join_req_drv == hif_drv) {
+ if (info_element && join_req_vif == vif) {
kfree(info_element);
info_element = NULL;
}
}
- up(&hif_drv->hSemTestDisconnectBlock);
+ up(&hif_drv->sem_test_disconn_block);
}
-void resolve_disconnect_aberration(struct host_if_drv *hif_drv)
+void wilc_resolve_disconnect_aberration(struct wilc_vif *vif)
{
- if (!hif_drv)
+ if (!vif->hif_drv)
return;
- if ((hif_drv->enuHostIFstate == HOST_IF_WAITING_CONN_RESP) || (hif_drv->enuHostIFstate == HOST_IF_CONNECTING)) {
+ if ((vif->hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP) ||
+ (vif->hif_drv->hif_state == HOST_IF_CONNECTING)) {
PRINT_D(HOSTINF_DBG, "\n\n<< correcting Supplicant state machine >>\n\n");
- host_int_disconnect(hif_drv, 1);
+ wilc_disconnect(vif, 1);
}
}
-static s32 Handle_GetChnl(struct host_if_drv *hif_drv)
+static s32 Handle_GetChnl(struct wilc_vif *vif)
{
s32 result = 0;
struct wid wid;
+ struct host_if_drv *hif_drv = vif->hif_drv;
wid.id = (u16)WID_CURRENT_CHANNEL;
wid.type = WID_CHAR;
@@ -2064,20 +2088,20 @@ static s32 Handle_GetChnl(struct host_if_drv *hif_drv)
PRINT_D(HOSTINF_DBG, "Getting channel value\n");
- result = send_config_pkt(GET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, GET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result) {
PRINT_ER("Failed to get channel number\n");
result = -EFAULT;
}
- up(&hif_drv->hSemGetCHNL);
+ up(&hif_drv->sem_get_chnl);
return result;
}
-static void Handle_GetRssi(struct host_if_drv *hif_drv)
+static void Handle_GetRssi(struct wilc_vif *vif)
{
s32 result = 0;
struct wid wid;
@@ -2089,20 +2113,21 @@ static void Handle_GetRssi(struct host_if_drv *hif_drv)
PRINT_D(HOSTINF_DBG, "Getting RSSI value\n");
- result = send_config_pkt(GET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, GET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result) {
PRINT_ER("Failed to get RSSI value\n");
result = -EFAULT;
}
- up(&hif_drv->hSemGetRSSI);
+ up(&vif->hif_drv->sem_get_rssi);
}
-static void Handle_GetLinkspeed(struct host_if_drv *hif_drv)
+static void Handle_GetLinkspeed(struct wilc_vif *vif)
{
s32 result = 0;
struct wid wid;
+ struct host_if_drv *hif_drv = vif->hif_drv;
link_speed = 0;
@@ -2113,17 +2138,18 @@ static void Handle_GetLinkspeed(struct host_if_drv *hif_drv)
PRINT_D(HOSTINF_DBG, "Getting LINKSPEED value\n");
- result = send_config_pkt(GET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, GET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result) {
PRINT_ER("Failed to get LINKSPEED value\n");
result = -EFAULT;
}
- up(&hif_drv->hSemGetLINKSPEED);
+ up(&hif_drv->sem_get_link_speed);
}
-s32 Handle_GetStatistics(struct host_if_drv *hif_drv, struct rf_info *pstrStatistics)
+static s32 Handle_GetStatistics(struct wilc_vif *vif,
+ struct rf_info *pstrStatistics)
{
struct wid strWIDList[5];
u32 u32WidsCount = 0, result = 0;
@@ -2131,35 +2157,36 @@ s32 Handle_GetStatistics(struct host_if_drv *hif_drv, struct rf_info *pstrStatis
strWIDList[u32WidsCount].id = WID_LINKSPEED;
strWIDList[u32WidsCount].type = WID_CHAR;
strWIDList[u32WidsCount].size = sizeof(char);
- strWIDList[u32WidsCount].val = (s8 *)&pstrStatistics->u8LinkSpeed;
+ strWIDList[u32WidsCount].val = (s8 *)&pstrStatistics->link_speed;
u32WidsCount++;
strWIDList[u32WidsCount].id = WID_RSSI;
strWIDList[u32WidsCount].type = WID_CHAR;
strWIDList[u32WidsCount].size = sizeof(char);
- strWIDList[u32WidsCount].val = (s8 *)&pstrStatistics->s8RSSI;
+ strWIDList[u32WidsCount].val = (s8 *)&pstrStatistics->rssi;
u32WidsCount++;
strWIDList[u32WidsCount].id = WID_SUCCESS_FRAME_COUNT;
strWIDList[u32WidsCount].type = WID_INT;
strWIDList[u32WidsCount].size = sizeof(u32);
- strWIDList[u32WidsCount].val = (s8 *)&pstrStatistics->u32TxCount;
+ strWIDList[u32WidsCount].val = (s8 *)&pstrStatistics->tx_cnt;
u32WidsCount++;
strWIDList[u32WidsCount].id = WID_RECEIVED_FRAGMENT_COUNT;
strWIDList[u32WidsCount].type = WID_INT;
strWIDList[u32WidsCount].size = sizeof(u32);
- strWIDList[u32WidsCount].val = (s8 *)&pstrStatistics->u32RxCount;
+ strWIDList[u32WidsCount].val = (s8 *)&pstrStatistics->rx_cnt;
u32WidsCount++;
strWIDList[u32WidsCount].id = WID_FAILED_COUNT;
strWIDList[u32WidsCount].type = WID_INT;
strWIDList[u32WidsCount].size = sizeof(u32);
- strWIDList[u32WidsCount].val = (s8 *)&pstrStatistics->u32TxFailureCount;
+ strWIDList[u32WidsCount].val = (s8 *)&pstrStatistics->tx_fail_cnt;
u32WidsCount++;
- result = send_config_pkt(GET_CFG, strWIDList, u32WidsCount,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, GET_CFG, strWIDList,
+ u32WidsCount,
+ wilc_get_vif_idx(vif));
if (result)
PRINT_ER("Failed to send scan paramters config packet\n");
@@ -2168,12 +2195,13 @@ s32 Handle_GetStatistics(struct host_if_drv *hif_drv, struct rf_info *pstrStatis
return 0;
}
-static s32 Handle_Get_InActiveTime(struct host_if_drv *hif_drv,
+static s32 Handle_Get_InActiveTime(struct wilc_vif *vif,
struct sta_inactive_t *strHostIfStaInactiveT)
{
s32 result = 0;
u8 *stamac;
struct wid wid;
+ struct host_if_drv *hif_drv = vif->hif_drv;
wid.id = (u16)WID_SET_STA_MAC_INACTIVE_TIME;
wid.type = WID_STR;
@@ -2185,8 +2213,8 @@ static s32 Handle_Get_InActiveTime(struct host_if_drv *hif_drv,
PRINT_D(CFG80211_DBG, "SETING STA inactive time\n");
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result) {
PRINT_ER("Failed to SET incative time\n");
@@ -2198,8 +2226,8 @@ static s32 Handle_Get_InActiveTime(struct host_if_drv *hif_drv,
wid.val = (s8 *)&inactive_time;
wid.size = sizeof(u32);
- result = send_config_pkt(GET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, GET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result) {
PRINT_ER("Failed to get incative time\n");
@@ -2208,12 +2236,12 @@ static s32 Handle_Get_InActiveTime(struct host_if_drv *hif_drv,
PRINT_D(CFG80211_DBG, "Getting inactive time : %d\n", inactive_time);
- up(&hif_drv->hSemInactiveTime);
+ up(&hif_drv->sem_inactive_time);
return result;
}
-static void Handle_AddBeacon(struct host_if_drv *hif_drv,
+static void Handle_AddBeacon(struct wilc_vif *vif,
struct beacon_attr *pstrSetBeaconParam)
{
s32 result = 0;
@@ -2253,12 +2281,12 @@ static void Handle_AddBeacon(struct host_if_drv *hif_drv,
*pu8CurrByte++ = ((pstrSetBeaconParam->tail_len >> 16) & 0xFF);
*pu8CurrByte++ = ((pstrSetBeaconParam->tail_len >> 24) & 0xFF);
- if (pstrSetBeaconParam->tail > 0)
+ if (pstrSetBeaconParam->tail)
memcpy(pu8CurrByte, pstrSetBeaconParam->tail, pstrSetBeaconParam->tail_len);
pu8CurrByte += pstrSetBeaconParam->tail_len;
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result)
PRINT_ER("Failed to send add beacon config packet\n");
@@ -2268,7 +2296,7 @@ ERRORHANDLER:
kfree(pstrSetBeaconParam->tail);
}
-static void Handle_DelBeacon(struct host_if_drv *hif_drv)
+static void Handle_DelBeacon(struct wilc_vif *vif)
{
s32 result = 0;
struct wid wid;
@@ -2286,8 +2314,8 @@ static void Handle_DelBeacon(struct host_if_drv *hif_drv)
PRINT_D(HOSTINF_DBG, "Deleting BEACON\n");
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result)
PRINT_ER("Failed to send delete beacon config packet\n");
}
@@ -2300,45 +2328,47 @@ static u32 WILC_HostIf_PackStaParam(u8 *pu8Buffer,
pu8CurrByte = pu8Buffer;
PRINT_D(HOSTINF_DBG, "Packing STA params\n");
- memcpy(pu8CurrByte, pstrStationParam->au8BSSID, ETH_ALEN);
+ memcpy(pu8CurrByte, pstrStationParam->bssid, ETH_ALEN);
pu8CurrByte += ETH_ALEN;
- *pu8CurrByte++ = pstrStationParam->u16AssocID & 0xFF;
- *pu8CurrByte++ = (pstrStationParam->u16AssocID >> 8) & 0xFF;
+ *pu8CurrByte++ = pstrStationParam->aid & 0xFF;
+ *pu8CurrByte++ = (pstrStationParam->aid >> 8) & 0xFF;
- *pu8CurrByte++ = pstrStationParam->u8NumRates;
- if (pstrStationParam->u8NumRates > 0)
- memcpy(pu8CurrByte, pstrStationParam->pu8Rates, pstrStationParam->u8NumRates);
- pu8CurrByte += pstrStationParam->u8NumRates;
+ *pu8CurrByte++ = pstrStationParam->rates_len;
+ if (pstrStationParam->rates_len > 0)
+ memcpy(pu8CurrByte, pstrStationParam->rates,
+ pstrStationParam->rates_len);
+ pu8CurrByte += pstrStationParam->rates_len;
- *pu8CurrByte++ = pstrStationParam->bIsHTSupported;
- *pu8CurrByte++ = pstrStationParam->u16HTCapInfo & 0xFF;
- *pu8CurrByte++ = (pstrStationParam->u16HTCapInfo >> 8) & 0xFF;
+ *pu8CurrByte++ = pstrStationParam->ht_supported;
+ *pu8CurrByte++ = pstrStationParam->ht_capa_info & 0xFF;
+ *pu8CurrByte++ = (pstrStationParam->ht_capa_info >> 8) & 0xFF;
- *pu8CurrByte++ = pstrStationParam->u8AmpduParams;
- memcpy(pu8CurrByte, pstrStationParam->au8SuppMCsSet, WILC_SUPP_MCS_SET_SIZE);
+ *pu8CurrByte++ = pstrStationParam->ht_ampdu_params;
+ memcpy(pu8CurrByte, pstrStationParam->ht_supp_mcs_set,
+ WILC_SUPP_MCS_SET_SIZE);
pu8CurrByte += WILC_SUPP_MCS_SET_SIZE;
- *pu8CurrByte++ = pstrStationParam->u16HTExtParams & 0xFF;
- *pu8CurrByte++ = (pstrStationParam->u16HTExtParams >> 8) & 0xFF;
+ *pu8CurrByte++ = pstrStationParam->ht_ext_params & 0xFF;
+ *pu8CurrByte++ = (pstrStationParam->ht_ext_params >> 8) & 0xFF;
- *pu8CurrByte++ = pstrStationParam->u32TxBeamformingCap & 0xFF;
- *pu8CurrByte++ = (pstrStationParam->u32TxBeamformingCap >> 8) & 0xFF;
- *pu8CurrByte++ = (pstrStationParam->u32TxBeamformingCap >> 16) & 0xFF;
- *pu8CurrByte++ = (pstrStationParam->u32TxBeamformingCap >> 24) & 0xFF;
+ *pu8CurrByte++ = pstrStationParam->ht_tx_bf_cap & 0xFF;
+ *pu8CurrByte++ = (pstrStationParam->ht_tx_bf_cap >> 8) & 0xFF;
+ *pu8CurrByte++ = (pstrStationParam->ht_tx_bf_cap >> 16) & 0xFF;
+ *pu8CurrByte++ = (pstrStationParam->ht_tx_bf_cap >> 24) & 0xFF;
- *pu8CurrByte++ = pstrStationParam->u8ASELCap;
+ *pu8CurrByte++ = pstrStationParam->ht_ante_sel;
- *pu8CurrByte++ = pstrStationParam->u16FlagsMask & 0xFF;
- *pu8CurrByte++ = (pstrStationParam->u16FlagsMask >> 8) & 0xFF;
+ *pu8CurrByte++ = pstrStationParam->flags_mask & 0xFF;
+ *pu8CurrByte++ = (pstrStationParam->flags_mask >> 8) & 0xFF;
- *pu8CurrByte++ = pstrStationParam->u16FlagsSet & 0xFF;
- *pu8CurrByte++ = (pstrStationParam->u16FlagsSet >> 8) & 0xFF;
+ *pu8CurrByte++ = pstrStationParam->flags_set & 0xFF;
+ *pu8CurrByte++ = (pstrStationParam->flags_set >> 8) & 0xFF;
return pu8CurrByte - pu8Buffer;
}
-static void Handle_AddStation(struct host_if_drv *hif_drv,
+static void Handle_AddStation(struct wilc_vif *vif,
struct add_sta_param *pstrStationParam)
{
s32 result = 0;
@@ -2348,7 +2378,7 @@ static void Handle_AddStation(struct host_if_drv *hif_drv,
PRINT_D(HOSTINF_DBG, "Handling add station\n");
wid.id = (u16)WID_ADD_STA;
wid.type = WID_BIN;
- wid.size = WILC_ADD_STA_LENGTH + pstrStationParam->u8NumRates;
+ wid.size = WILC_ADD_STA_LENGTH + pstrStationParam->rates_len;
wid.val = kmalloc(wid.size, GFP_KERNEL);
if (!wid.val)
@@ -2357,17 +2387,17 @@ static void Handle_AddStation(struct host_if_drv *hif_drv,
pu8CurrByte = wid.val;
pu8CurrByte += WILC_HostIf_PackStaParam(pu8CurrByte, pstrStationParam);
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result != 0)
PRINT_ER("Failed to send add station config packet\n");
ERRORHANDLER:
- kfree(pstrStationParam->pu8Rates);
+ kfree(pstrStationParam->rates);
kfree(wid.val);
}
-static void Handle_DelAllSta(struct host_if_drv *hif_drv,
+static void Handle_DelAllSta(struct wilc_vif *vif,
struct del_all_sta *pstrDelAllStaParam)
{
s32 result = 0;
@@ -2399,8 +2429,8 @@ static void Handle_DelAllSta(struct host_if_drv *hif_drv,
pu8CurrByte += ETH_ALEN;
}
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result)
PRINT_ER("Failed to send add station config packet\n");
@@ -2410,7 +2440,7 @@ ERRORHANDLER:
up(&hif_sema_wait_response);
}
-static void Handle_DelStation(struct host_if_drv *hif_drv,
+static void Handle_DelStation(struct wilc_vif *vif,
struct del_sta *pstrDelStaParam)
{
s32 result = 0;
@@ -2431,8 +2461,8 @@ static void Handle_DelStation(struct host_if_drv *hif_drv,
memcpy(pu8CurrByte, pstrDelStaParam->mac_addr, ETH_ALEN);
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result)
PRINT_ER("Failed to send add station config packet\n");
@@ -2440,7 +2470,7 @@ ERRORHANDLER:
kfree(wid.val);
}
-static void Handle_EditStation(struct host_if_drv *hif_drv,
+static void Handle_EditStation(struct wilc_vif *vif,
struct add_sta_param *pstrStationParam)
{
s32 result = 0;
@@ -2449,7 +2479,7 @@ static void Handle_EditStation(struct host_if_drv *hif_drv,
wid.id = (u16)WID_EDIT_STA;
wid.type = WID_BIN;
- wid.size = WILC_ADD_STA_LENGTH + pstrStationParam->u8NumRates;
+ wid.size = WILC_ADD_STA_LENGTH + pstrStationParam->rates_len;
PRINT_D(HOSTINF_DBG, "Handling edit station\n");
wid.val = kmalloc(wid.size, GFP_KERNEL);
@@ -2459,52 +2489,54 @@ static void Handle_EditStation(struct host_if_drv *hif_drv,
pu8CurrByte = wid.val;
pu8CurrByte += WILC_HostIf_PackStaParam(pu8CurrByte, pstrStationParam);
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result)
PRINT_ER("Failed to send edit station config packet\n");
ERRORHANDLER:
- kfree(pstrStationParam->pu8Rates);
+ kfree(pstrStationParam->rates);
kfree(wid.val);
}
-static int Handle_RemainOnChan(struct host_if_drv *hif_drv,
+static int Handle_RemainOnChan(struct wilc_vif *vif,
struct remain_ch *pstrHostIfRemainOnChan)
{
s32 result = 0;
u8 u8remain_on_chan_flag;
struct wid wid;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv->remain_on_ch_pending) {
- hif_drv->remain_on_ch.pVoid = pstrHostIfRemainOnChan->pVoid;
- hif_drv->remain_on_ch.pRemainOnChanExpired = pstrHostIfRemainOnChan->pRemainOnChanExpired;
- hif_drv->remain_on_ch.pRemainOnChanReady = pstrHostIfRemainOnChan->pRemainOnChanReady;
- hif_drv->remain_on_ch.u16Channel = pstrHostIfRemainOnChan->u16Channel;
- hif_drv->remain_on_ch.u32ListenSessionID = pstrHostIfRemainOnChan->u32ListenSessionID;
+ hif_drv->remain_on_ch.arg = pstrHostIfRemainOnChan->arg;
+ hif_drv->remain_on_ch.expired = pstrHostIfRemainOnChan->expired;
+ hif_drv->remain_on_ch.ready = pstrHostIfRemainOnChan->ready;
+ hif_drv->remain_on_ch.ch = pstrHostIfRemainOnChan->ch;
+ hif_drv->remain_on_ch.id = pstrHostIfRemainOnChan->id;
} else {
- pstrHostIfRemainOnChan->u16Channel = hif_drv->remain_on_ch.u16Channel;
+ pstrHostIfRemainOnChan->ch = hif_drv->remain_on_ch.ch;
}
- if (hif_drv->usr_scan_req.pfUserScanResult) {
+ if (hif_drv->usr_scan_req.scan_result) {
PRINT_INFO(GENERIC_DBG, "Required to remain on chan while scanning return\n");
hif_drv->remain_on_ch_pending = 1;
result = -EBUSY;
goto ERRORHANDLER;
}
- if (hif_drv->enuHostIFstate == HOST_IF_WAITING_CONN_RESP) {
+ if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP) {
PRINT_INFO(GENERIC_DBG, "Required to remain on chan while connecting return\n");
result = -EBUSY;
goto ERRORHANDLER;
}
- if (g_obtainingIP || connecting) {
+ if (wilc_optaining_ip || wilc_connecting) {
PRINT_D(GENERIC_DBG, "[handle_scan]: Don't do obss scan until IP adresss is obtained\n");
result = -EBUSY;
goto ERRORHANDLER;
}
- PRINT_D(HOSTINF_DBG, "Setting channel :%d\n", pstrHostIfRemainOnChan->u16Channel);
+ PRINT_D(HOSTINF_DBG, "Setting channel :%d\n",
+ pstrHostIfRemainOnChan->ch);
u8remain_on_chan_flag = true;
wid.id = (u16)WID_REMAIN_ON_CHAN;
@@ -2517,23 +2549,23 @@ static int Handle_RemainOnChan(struct host_if_drv *hif_drv,
}
wid.val[0] = u8remain_on_chan_flag;
- wid.val[1] = (s8)pstrHostIfRemainOnChan->u16Channel;
+ wid.val[1] = (s8)pstrHostIfRemainOnChan->ch;
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result != 0)
PRINT_ER("Failed to set remain on channel\n");
ERRORHANDLER:
{
P2P_LISTEN_STATE = 1;
- hif_drv->hRemainOnChannel.data = (unsigned long)hif_drv;
- mod_timer(&hif_drv->hRemainOnChannel,
+ hif_drv->remain_on_ch_timer.data = (unsigned long)vif;
+ mod_timer(&hif_drv->remain_on_ch_timer,
jiffies +
msecs_to_jiffies(pstrHostIfRemainOnChan->u32duration));
- if (hif_drv->remain_on_ch.pRemainOnChanReady)
- hif_drv->remain_on_ch.pRemainOnChanReady(hif_drv->remain_on_ch.pVoid);
+ if (hif_drv->remain_on_ch.ready)
+ hif_drv->remain_on_ch.ready(hif_drv->remain_on_ch.arg);
if (hif_drv->remain_on_ch_pending)
hif_drv->remain_on_ch_pending = 0;
@@ -2542,14 +2574,16 @@ ERRORHANDLER:
return result;
}
-static int Handle_RegisterFrame(struct host_if_drv *hif_drv,
+static int Handle_RegisterFrame(struct wilc_vif *vif,
struct reg_frame *pstrHostIfRegisterFrame)
{
s32 result = 0;
struct wid wid;
u8 *pu8CurrByte;
- PRINT_D(HOSTINF_DBG, "Handling frame register Flag : %d FrameType: %d\n", pstrHostIfRegisterFrame->bReg, pstrHostIfRegisterFrame->u16FrameType);
+ PRINT_D(HOSTINF_DBG, "Handling frame register : %d FrameType: %d\n",
+ pstrHostIfRegisterFrame->reg,
+ pstrHostIfRegisterFrame->frame_type);
wid.id = (u16)WID_REGISTER_FRAME;
wid.type = WID_STR;
@@ -2559,15 +2593,14 @@ static int Handle_RegisterFrame(struct host_if_drv *hif_drv,
pu8CurrByte = wid.val;
- *pu8CurrByte++ = pstrHostIfRegisterFrame->bReg;
- *pu8CurrByte++ = pstrHostIfRegisterFrame->u8Regid;
- memcpy(pu8CurrByte, &pstrHostIfRegisterFrame->u16FrameType,
- sizeof(u16));
+ *pu8CurrByte++ = pstrHostIfRegisterFrame->reg;
+ *pu8CurrByte++ = pstrHostIfRegisterFrame->reg_id;
+ memcpy(pu8CurrByte, &pstrHostIfRegisterFrame->frame_type, sizeof(u16));
wid.size = sizeof(u16) + 2;
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result) {
PRINT_ER("Failed to frame register config packet\n");
result = -EINVAL;
@@ -2576,12 +2609,13 @@ static int Handle_RegisterFrame(struct host_if_drv *hif_drv,
return result;
}
-static u32 Handle_ListenStateExpired(struct host_if_drv *hif_drv,
+static u32 Handle_ListenStateExpired(struct wilc_vif *vif,
struct remain_ch *pstrHostIfRemainOnChan)
{
u8 u8remain_on_chan_flag;
struct wid wid;
s32 result = 0;
+ struct host_if_drv *hif_drv = vif->hif_drv;
PRINT_D(HOSTINF_DBG, "CANCEL REMAIN ON CHAN\n");
@@ -2592,22 +2626,24 @@ static u32 Handle_ListenStateExpired(struct host_if_drv *hif_drv,
wid.size = 2;
wid.val = kmalloc(wid.size, GFP_KERNEL);
- if (!wid.val)
+ if (!wid.val) {
PRINT_ER("Failed to allocate memory\n");
+ return -ENOMEM;
+ }
wid.val[0] = u8remain_on_chan_flag;
wid.val[1] = FALSE_FRMWR_CHANNEL;
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result != 0) {
PRINT_ER("Failed to set remain on channel\n");
goto _done_;
}
- if (hif_drv->remain_on_ch.pRemainOnChanExpired) {
- hif_drv->remain_on_ch.pRemainOnChanExpired(hif_drv->remain_on_ch.pVoid,
- pstrHostIfRemainOnChan->u32ListenSessionID);
+ if (hif_drv->remain_on_ch.expired) {
+ hif_drv->remain_on_ch.expired(hif_drv->remain_on_ch.arg,
+ pstrHostIfRemainOnChan->id);
}
P2P_LISTEN_STATE = 0;
} else {
@@ -2623,21 +2659,21 @@ static void ListenTimerCB(unsigned long arg)
{
s32 result = 0;
struct host_if_msg msg;
- struct host_if_drv *hif_drv = (struct host_if_drv *)arg;
+ struct wilc_vif *vif = (struct wilc_vif *)arg;
- del_timer(&hif_drv->hRemainOnChannel);
+ del_timer(&vif->hif_drv->remain_on_ch_timer);
memset(&msg, 0, sizeof(struct host_if_msg));
msg.id = HOST_IF_MSG_LISTEN_TIMER_FIRED;
- msg.drv = hif_drv;
- msg.body.remain_on_ch.u32ListenSessionID = hif_drv->remain_on_ch.u32ListenSessionID;
+ msg.vif = vif;
+ msg.body.remain_on_ch.id = vif->hif_drv->remain_on_ch.id;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result)
PRINT_ER("wilc_mq_send fail\n");
}
-static void Handle_PowerManagement(struct host_if_drv *hif_drv,
+static void Handle_PowerManagement(struct wilc_vif *vif,
struct power_mgmt_param *strPowerMgmtParam)
{
s32 result = 0;
@@ -2656,13 +2692,13 @@ static void Handle_PowerManagement(struct host_if_drv *hif_drv,
PRINT_D(HOSTINF_DBG, "Handling Power Management\n");
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result)
PRINT_ER("Failed to send power management config packet\n");
}
-static void Handle_SetMulticastFilter(struct host_if_drv *hif_drv,
+static void Handle_SetMulticastFilter(struct wilc_vif *vif,
struct set_multicast *strHostIfSetMulti)
{
s32 result = 0;
@@ -2680,9 +2716,9 @@ static void Handle_SetMulticastFilter(struct host_if_drv *hif_drv,
pu8CurrByte = wid.val;
*pu8CurrByte++ = (strHostIfSetMulti->enabled & 0xFF);
- *pu8CurrByte++ = ((strHostIfSetMulti->enabled >> 8) & 0xFF);
- *pu8CurrByte++ = ((strHostIfSetMulti->enabled >> 16) & 0xFF);
- *pu8CurrByte++ = ((strHostIfSetMulti->enabled >> 24) & 0xFF);
+ *pu8CurrByte++ = 0;
+ *pu8CurrByte++ = 0;
+ *pu8CurrByte++ = 0;
*pu8CurrByte++ = (strHostIfSetMulti->cnt & 0xFF);
*pu8CurrByte++ = ((strHostIfSetMulti->cnt >> 8) & 0xFF);
@@ -2690,10 +2726,11 @@ static void Handle_SetMulticastFilter(struct host_if_drv *hif_drv,
*pu8CurrByte++ = ((strHostIfSetMulti->cnt >> 24) & 0xFF);
if ((strHostIfSetMulti->cnt) > 0)
- memcpy(pu8CurrByte, gau8MulticastMacAddrList, ((strHostIfSetMulti->cnt) * ETH_ALEN));
+ memcpy(pu8CurrByte, wilc_multicast_mac_addr_list,
+ ((strHostIfSetMulti->cnt) * ETH_ALEN));
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result)
PRINT_ER("Failed to send setup multicast config packet\n");
@@ -2701,71 +2738,7 @@ ERRORHANDLER:
kfree(wid.val);
}
-static s32 Handle_AddBASession(struct host_if_drv *hif_drv,
- struct ba_session_info *strHostIfBASessionInfo)
-{
- s32 result = 0;
- struct wid wid;
- int AddbaTimeout = 100;
- char *ptr = NULL;
-
- PRINT_D(HOSTINF_DBG, "Opening Block Ack session with\nBSSID = %.2x:%.2x:%.2x\nTID=%d\nBufferSize == %d\nSessionTimeOut = %d\n",
- strHostIfBASessionInfo->au8Bssid[0],
- strHostIfBASessionInfo->au8Bssid[1],
- strHostIfBASessionInfo->au8Bssid[2],
- strHostIfBASessionInfo->u16BufferSize,
- strHostIfBASessionInfo->u16SessionTimeout,
- strHostIfBASessionInfo->u8Ted);
-
- wid.id = (u16)WID_11E_P_ACTION_REQ;
- wid.type = WID_STR;
- wid.val = kmalloc(BLOCK_ACK_REQ_SIZE, GFP_KERNEL);
- wid.size = BLOCK_ACK_REQ_SIZE;
- ptr = wid.val;
- *ptr++ = 0x14;
- *ptr++ = 0x3;
- *ptr++ = 0x0;
- memcpy(ptr, strHostIfBASessionInfo->au8Bssid, ETH_ALEN);
- ptr += ETH_ALEN;
- *ptr++ = strHostIfBASessionInfo->u8Ted;
- *ptr++ = 1;
- *ptr++ = (strHostIfBASessionInfo->u16BufferSize & 0xFF);
- *ptr++ = ((strHostIfBASessionInfo->u16BufferSize >> 16) & 0xFF);
- *ptr++ = (strHostIfBASessionInfo->u16SessionTimeout & 0xFF);
- *ptr++ = ((strHostIfBASessionInfo->u16SessionTimeout >> 16) & 0xFF);
- *ptr++ = (AddbaTimeout & 0xFF);
- *ptr++ = ((AddbaTimeout >> 16) & 0xFF);
- *ptr++ = 8;
- *ptr++ = 0;
-
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
- if (result)
- PRINT_D(HOSTINF_DBG, "Couldn't open BA Session\n");
-
- wid.id = (u16)WID_11E_P_ACTION_REQ;
- wid.type = WID_STR;
- wid.size = 15;
- ptr = wid.val;
- *ptr++ = 15;
- *ptr++ = 7;
- *ptr++ = 0x2;
- memcpy(ptr, strHostIfBASessionInfo->au8Bssid, ETH_ALEN);
- ptr += ETH_ALEN;
- *ptr++ = strHostIfBASessionInfo->u8Ted;
- *ptr++ = 8;
- *ptr++ = (strHostIfBASessionInfo->u16BufferSize & 0xFF);
- *ptr++ = ((strHostIfBASessionInfo->u16SessionTimeout >> 16) & 0xFF);
- *ptr++ = 3;
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
-
- kfree(wid.val);
-
- return result;
-}
-
-static s32 Handle_DelAllRxBASessions(struct host_if_drv *hif_drv,
+static s32 Handle_DelAllRxBASessions(struct wilc_vif *vif,
struct ba_session_info *strHostIfBASessionInfo)
{
s32 result = 0;
@@ -2773,10 +2746,10 @@ static s32 Handle_DelAllRxBASessions(struct host_if_drv *hif_drv,
char *ptr = NULL;
PRINT_D(GENERIC_DBG, "Delete Block Ack session with\nBSSID = %.2x:%.2x:%.2x\nTID=%d\n",
- strHostIfBASessionInfo->au8Bssid[0],
- strHostIfBASessionInfo->au8Bssid[1],
- strHostIfBASessionInfo->au8Bssid[2],
- strHostIfBASessionInfo->u8Ted);
+ strHostIfBASessionInfo->bssid[0],
+ strHostIfBASessionInfo->bssid[1],
+ strHostIfBASessionInfo->bssid[2],
+ strHostIfBASessionInfo->tid);
wid.id = (u16)WID_DEL_ALL_RX_BA;
wid.type = WID_STR;
@@ -2786,14 +2759,14 @@ static s32 Handle_DelAllRxBASessions(struct host_if_drv *hif_drv,
*ptr++ = 0x14;
*ptr++ = 0x3;
*ptr++ = 0x2;
- memcpy(ptr, strHostIfBASessionInfo->au8Bssid, ETH_ALEN);
+ memcpy(ptr, strHostIfBASessionInfo->bssid, ETH_ALEN);
ptr += ETH_ALEN;
- *ptr++ = strHostIfBASessionInfo->u8Ted;
+ *ptr++ = strHostIfBASessionInfo->tid;
*ptr++ = 0;
*ptr++ = 32;
- result = send_config_pkt(SET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, SET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result)
PRINT_D(HOSTINF_DBG, "Couldn't delete BA Session\n");
@@ -2808,19 +2781,20 @@ static int hostIFthread(void *pvArg)
{
u32 u32Ret;
struct host_if_msg msg;
- struct host_if_drv *hif_drv;
+ struct wilc *wilc = (struct wilc*)pvArg;
+ struct wilc_vif *vif;
memset(&msg, 0, sizeof(struct host_if_msg));
while (1) {
wilc_mq_recv(&hif_msg_q, &msg, sizeof(struct host_if_msg), &u32Ret);
- hif_drv = (struct host_if_drv *)msg.drv;
+ vif = msg.vif;
if (msg.id == HOST_IF_MSG_EXIT) {
PRINT_D(GENERIC_DBG, "THREAD: Exiting HostIfThread\n");
break;
}
- if ((!g_wilc_initialized)) {
+ if ((!wilc_initialized)) {
PRINT_D(GENERIC_DBG, "--WAIT--");
usleep_range(200 * 1000, 200 * 1000);
wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
@@ -2828,7 +2802,7 @@ static int hostIFthread(void *pvArg)
}
if (msg.id == HOST_IF_MSG_CONNECT &&
- hif_drv->usr_scan_req.pfUserScanResult) {
+ vif->hif_drv->usr_scan_req.scan_result) {
PRINT_D(HOSTINF_DBG, "Requeue connect request till scan done received\n");
wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
usleep_range(2 * 1000, 2 * 1000);
@@ -2841,167 +2815,169 @@ static int hostIFthread(void *pvArg)
break;
case HOST_IF_MSG_SCAN:
- Handle_Scan(msg.drv, &msg.body.scan_info);
+ Handle_Scan(msg.vif, &msg.body.scan_info);
break;
case HOST_IF_MSG_CONNECT:
- Handle_Connect(msg.drv, &msg.body.con_info);
+ Handle_Connect(msg.vif, &msg.body.con_info);
break;
case HOST_IF_MSG_FLUSH_CONNECT:
- Handle_FlushConnect(msg.drv);
+ Handle_FlushConnect(msg.vif);
break;
case HOST_IF_MSG_RCVD_NTWRK_INFO:
- Handle_RcvdNtwrkInfo(msg.drv, &msg.body.net_info);
+ Handle_RcvdNtwrkInfo(msg.vif, &msg.body.net_info);
break;
case HOST_IF_MSG_RCVD_GNRL_ASYNC_INFO:
- Handle_RcvdGnrlAsyncInfo(msg.drv, &msg.body.async_info);
+ Handle_RcvdGnrlAsyncInfo(vif,
+ &msg.body.async_info);
break;
case HOST_IF_MSG_KEY:
- Handle_Key(msg.drv, &msg.body.key_info);
+ Handle_Key(msg.vif, &msg.body.key_info);
break;
case HOST_IF_MSG_CFG_PARAMS:
-
- Handle_CfgParam(msg.drv, &msg.body.cfg_info);
+ handle_cfg_param(msg.vif, &msg.body.cfg_info);
break;
case HOST_IF_MSG_SET_CHANNEL:
- Handle_SetChannel(msg.drv, &msg.body.channel_info);
+ handle_set_channel(msg.vif, &msg.body.channel_info);
break;
case HOST_IF_MSG_DISCONNECT:
- Handle_Disconnect(msg.drv);
+ Handle_Disconnect(msg.vif);
break;
case HOST_IF_MSG_RCVD_SCAN_COMPLETE:
- del_timer(&hif_drv->hScanTimer);
+ del_timer(&vif->hif_drv->scan_timer);
PRINT_D(HOSTINF_DBG, "scan completed successfully\n");
- if (!linux_wlan_get_num_conn_ifcs())
- chip_sleep_manually(INFINITE_SLEEP_TIME);
+ if (!wilc_wlan_get_num_conn_ifcs(wilc))
+ wilc_chip_sleep_manually(wilc);
- Handle_ScanDone(msg.drv, SCAN_EVENT_DONE);
+ Handle_ScanDone(msg.vif, SCAN_EVENT_DONE);
- if (hif_drv->remain_on_ch_pending)
- Handle_RemainOnChan(msg.drv, &msg.body.remain_on_ch);
+ if (vif->hif_drv->remain_on_ch_pending)
+ Handle_RemainOnChan(msg.vif,
+ &msg.body.remain_on_ch);
break;
case HOST_IF_MSG_GET_RSSI:
- Handle_GetRssi(msg.drv);
+ Handle_GetRssi(msg.vif);
break;
case HOST_IF_MSG_GET_LINKSPEED:
- Handle_GetLinkspeed(msg.drv);
+ Handle_GetLinkspeed(msg.vif);
break;
case HOST_IF_MSG_GET_STATISTICS:
- Handle_GetStatistics(msg.drv, (struct rf_info *)msg.body.data);
+ Handle_GetStatistics(msg.vif,
+ (struct rf_info *)msg.body.data);
break;
case HOST_IF_MSG_GET_CHNL:
- Handle_GetChnl(msg.drv);
+ Handle_GetChnl(msg.vif);
break;
case HOST_IF_MSG_ADD_BEACON:
- Handle_AddBeacon(msg.drv, &msg.body.beacon_info);
+ Handle_AddBeacon(msg.vif, &msg.body.beacon_info);
break;
case HOST_IF_MSG_DEL_BEACON:
- Handle_DelBeacon(msg.drv);
+ Handle_DelBeacon(msg.vif);
break;
case HOST_IF_MSG_ADD_STATION:
- Handle_AddStation(msg.drv, &msg.body.add_sta_info);
+ Handle_AddStation(msg.vif, &msg.body.add_sta_info);
break;
case HOST_IF_MSG_DEL_STATION:
- Handle_DelStation(msg.drv, &msg.body.del_sta_info);
+ Handle_DelStation(msg.vif, &msg.body.del_sta_info);
break;
case HOST_IF_MSG_EDIT_STATION:
- Handle_EditStation(msg.drv, &msg.body.edit_sta_info);
+ Handle_EditStation(msg.vif, &msg.body.edit_sta_info);
break;
case HOST_IF_MSG_GET_INACTIVETIME:
- Handle_Get_InActiveTime(msg.drv, &msg.body.mac_info);
+ Handle_Get_InActiveTime(msg.vif, &msg.body.mac_info);
break;
case HOST_IF_MSG_SCAN_TIMER_FIRED:
PRINT_D(HOSTINF_DBG, "Scan Timeout\n");
- Handle_ScanDone(msg.drv, SCAN_EVENT_ABORTED);
+ Handle_ScanDone(msg.vif, SCAN_EVENT_ABORTED);
break;
case HOST_IF_MSG_CONNECT_TIMER_FIRED:
PRINT_D(HOSTINF_DBG, "Connect Timeout\n");
- Handle_ConnectTimeout(msg.drv);
+ Handle_ConnectTimeout(msg.vif);
break;
case HOST_IF_MSG_POWER_MGMT:
- Handle_PowerManagement(msg.drv, &msg.body.pwr_mgmt_info);
+ Handle_PowerManagement(msg.vif,
+ &msg.body.pwr_mgmt_info);
break;
case HOST_IF_MSG_SET_WFIDRV_HANDLER:
- Handle_SetWfiDrvHandler(msg.drv,
- &msg.body.drv);
+ handle_set_wfi_drv_handler(msg.vif, &msg.body.drv);
break;
case HOST_IF_MSG_SET_OPERATION_MODE:
- Handle_SetOperationMode(msg.drv, &msg.body.mode);
+ handle_set_operation_mode(msg.vif, &msg.body.mode);
break;
case HOST_IF_MSG_SET_IPADDRESS:
PRINT_D(HOSTINF_DBG, "HOST_IF_MSG_SET_IPADDRESS\n");
- Handle_set_IPAddress(msg.drv, msg.body.ip_info.ip_addr, msg.body.ip_info.idx);
+ handle_set_ip_address(vif,
+ msg.body.ip_info.ip_addr,
+ msg.body.ip_info.idx);
break;
case HOST_IF_MSG_GET_IPADDRESS:
PRINT_D(HOSTINF_DBG, "HOST_IF_MSG_SET_IPADDRESS\n");
- Handle_get_IPAddress(msg.drv, msg.body.ip_info.ip_addr, msg.body.ip_info.idx);
+ handle_get_ip_address(vif, msg.body.ip_info.idx);
break;
case HOST_IF_MSG_SET_MAC_ADDRESS:
- Handle_SetMacAddress(msg.drv, &msg.body.set_mac_info);
+ handle_set_mac_address(msg.vif,
+ &msg.body.set_mac_info);
break;
case HOST_IF_MSG_GET_MAC_ADDRESS:
- Handle_GetMacAddress(msg.drv, &msg.body.get_mac_info);
+ handle_get_mac_address(msg.vif,
+ &msg.body.get_mac_info);
break;
case HOST_IF_MSG_REMAIN_ON_CHAN:
PRINT_D(HOSTINF_DBG, "HOST_IF_MSG_REMAIN_ON_CHAN\n");
- Handle_RemainOnChan(msg.drv, &msg.body.remain_on_ch);
+ Handle_RemainOnChan(msg.vif, &msg.body.remain_on_ch);
break;
case HOST_IF_MSG_REGISTER_FRAME:
PRINT_D(HOSTINF_DBG, "HOST_IF_MSG_REGISTER_FRAME\n");
- Handle_RegisterFrame(msg.drv, &msg.body.reg_frame);
+ Handle_RegisterFrame(msg.vif, &msg.body.reg_frame);
break;
case HOST_IF_MSG_LISTEN_TIMER_FIRED:
- Handle_ListenStateExpired(msg.drv, &msg.body.remain_on_ch);
+ Handle_ListenStateExpired(msg.vif, &msg.body.remain_on_ch);
break;
case HOST_IF_MSG_SET_MULTICAST_FILTER:
PRINT_D(HOSTINF_DBG, "HOST_IF_MSG_SET_MULTICAST_FILTER\n");
- Handle_SetMulticastFilter(msg.drv, &msg.body.multicast_info);
- break;
-
- case HOST_IF_MSG_ADD_BA_SESSION:
- Handle_AddBASession(msg.drv, &msg.body.session_info);
+ Handle_SetMulticastFilter(msg.vif, &msg.body.multicast_info);
break;
case HOST_IF_MSG_DEL_ALL_RX_BA_SESSIONS:
- Handle_DelAllRxBASessions(msg.drv, &msg.body.session_info);
+ Handle_DelAllRxBASessions(msg.vif, &msg.body.session_info);
break;
case HOST_IF_MSG_DEL_ALL_STA:
- Handle_DelAllSta(msg.drv, &msg.body.del_all_sta_info);
+ Handle_DelAllSta(msg.vif, &msg.body.del_all_sta_info);
break;
default:
@@ -3017,11 +2993,11 @@ static int hostIFthread(void *pvArg)
static void TimerCB_Scan(unsigned long arg)
{
- void *pvArg = (void *)arg;
+ struct wilc_vif *vif = (struct wilc_vif *)arg;
struct host_if_msg msg;
memset(&msg, 0, sizeof(struct host_if_msg));
- msg.drv = pvArg;
+ msg.vif = vif;
msg.id = HOST_IF_MSG_SCAN_TIMER_FIRED;
wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
@@ -3029,17 +3005,17 @@ static void TimerCB_Scan(unsigned long arg)
static void TimerCB_Connect(unsigned long arg)
{
- void *pvArg = (void *)arg;
+ struct wilc_vif *vif = (struct wilc_vif *)arg;
struct host_if_msg msg;
memset(&msg, 0, sizeof(struct host_if_msg));
- msg.drv = pvArg;
+ msg.vif = vif;
msg.id = HOST_IF_MSG_CONNECT_TIMER_FIRED;
wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
}
-s32 host_int_remove_key(struct host_if_drv *hif_drv, const u8 *pu8StaAddress)
+s32 wilc_remove_key(struct host_if_drv *hif_drv, const u8 *pu8StaAddress)
{
struct wid wid;
@@ -3051,10 +3027,11 @@ s32 host_int_remove_key(struct host_if_drv *hif_drv, const u8 *pu8StaAddress)
return 0;
}
-int host_int_remove_wep_key(struct host_if_drv *hif_drv, u8 index)
+int wilc_remove_wep_key(struct wilc_vif *vif, u8 index)
{
int result = 0;
struct host_if_msg msg;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
result = -EFAULT;
@@ -3067,21 +3044,22 @@ int host_int_remove_wep_key(struct host_if_drv *hif_drv, u8 index)
msg.id = HOST_IF_MSG_KEY;
msg.body.key_info.type = WEP;
msg.body.key_info.action = REMOVEKEY;
- msg.drv = hif_drv;
+ msg.vif = vif;
msg.body.key_info.attr.wep.index = index;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result)
PRINT_ER("Error in sending message queue : Request to remove WEP key\n");
- down(&hif_drv->hSemTestKeyBlock);
+ down(&hif_drv->sem_test_key_block);
return result;
}
-int host_int_set_wep_default_key(struct host_if_drv *hif_drv, u8 index)
+int wilc_set_wep_default_keyid(struct wilc_vif *vif, u8 index)
{
int result = 0;
struct host_if_msg msg;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
result = -EFAULT;
@@ -3094,24 +3072,23 @@ int host_int_set_wep_default_key(struct host_if_drv *hif_drv, u8 index)
msg.id = HOST_IF_MSG_KEY;
msg.body.key_info.type = WEP;
msg.body.key_info.action = DEFAULTKEY;
- msg.drv = hif_drv;
+ msg.vif = vif;
msg.body.key_info.attr.wep.index = index;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result)
PRINT_ER("Error in sending message queue : Default key index\n");
- down(&hif_drv->hSemTestKeyBlock);
+ down(&hif_drv->sem_test_key_block);
return result;
}
-int host_int_add_wep_key_bss_sta(struct host_if_drv *hif_drv,
- const u8 *key,
- u8 len,
- u8 index)
+int wilc_add_wep_key_bss_sta(struct wilc_vif *vif, const u8 *key, u8 len,
+ u8 index)
{
int result = 0;
struct host_if_msg msg;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
PRINT_ER("driver is null\n");
@@ -3123,7 +3100,7 @@ int host_int_add_wep_key_bss_sta(struct host_if_drv *hif_drv,
msg.id = HOST_IF_MSG_KEY;
msg.body.key_info.type = WEP;
msg.body.key_info.action = ADDKEY;
- msg.drv = hif_drv;
+ msg.vif = vif;
msg.body.key_info.attr.wep.key = kmemdup(key, len, GFP_KERNEL);
if (!msg.body.key_info.attr.wep.key)
return -ENOMEM;
@@ -3134,20 +3111,17 @@ int host_int_add_wep_key_bss_sta(struct host_if_drv *hif_drv,
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result)
PRINT_ER("Error in sending message queue :WEP Key\n");
- down(&hif_drv->hSemTestKeyBlock);
+ down(&hif_drv->sem_test_key_block);
return result;
}
-int host_int_add_wep_key_bss_ap(struct host_if_drv *hif_drv,
- const u8 *key,
- u8 len,
- u8 index,
- u8 mode,
- enum AUTHTYPE auth_type)
+int wilc_add_wep_key_bss_ap(struct wilc_vif *vif, const u8 *key, u8 len,
+ u8 index, u8 mode, enum AUTHTYPE auth_type)
{
int result = 0;
struct host_if_msg msg;
+ struct host_if_drv *hif_drv = vif->hif_drv;
int i;
if (!hif_drv) {
@@ -3164,7 +3138,7 @@ int host_int_add_wep_key_bss_ap(struct host_if_drv *hif_drv,
msg.id = HOST_IF_MSG_KEY;
msg.body.key_info.type = WEP;
msg.body.key_info.action = ADDKEY_AP;
- msg.drv = hif_drv;
+ msg.vif = vif;
msg.body.key_info.attr.wep.key = kmemdup(key, len, GFP_KERNEL);
if (!msg.body.key_info.attr.wep.key)
return -ENOMEM;
@@ -3178,85 +3152,86 @@ int host_int_add_wep_key_bss_ap(struct host_if_drv *hif_drv,
if (result)
PRINT_ER("Error in sending message queue :WEP Key\n");
- down(&hif_drv->hSemTestKeyBlock);
+ down(&hif_drv->sem_test_key_block);
return result;
}
-s32 host_int_add_ptk(struct host_if_drv *hif_drv, const u8 *pu8Ptk,
- u8 u8PtkKeylen, const u8 *mac_addr,
- const u8 *pu8RxMic, const u8 *pu8TxMic,
- u8 mode, u8 u8Ciphermode, u8 u8Idx)
+int wilc_add_ptk(struct wilc_vif *vif, const u8 *ptk, u8 ptk_key_len,
+ const u8 *mac_addr, const u8 *rx_mic, const u8 *tx_mic,
+ u8 mode, u8 cipher_mode, u8 index)
{
- s32 result = 0;
+ int result = 0;
struct host_if_msg msg;
- u8 u8KeyLen = u8PtkKeylen;
- u32 i;
+ struct host_if_drv *hif_drv = vif->hif_drv;
+ u8 key_len = ptk_key_len;
+ int i;
if (!hif_drv) {
PRINT_ER("driver is null\n");
return -EFAULT;
}
- if (pu8RxMic)
- u8KeyLen += RX_MIC_KEY_LEN;
+ if (rx_mic)
+ key_len += RX_MIC_KEY_LEN;
- if (pu8TxMic)
- u8KeyLen += TX_MIC_KEY_LEN;
+ if (tx_mic)
+ key_len += TX_MIC_KEY_LEN;
memset(&msg, 0, sizeof(struct host_if_msg));
msg.id = HOST_IF_MSG_KEY;
- msg.body.key_info.type = WPAPtk;
+ msg.body.key_info.type = WPA_PTK;
if (mode == AP_MODE) {
msg.body.key_info.action = ADDKEY_AP;
- msg.body.key_info.attr.wpa.index = u8Idx;
+ msg.body.key_info.attr.wpa.index = index;
}
if (mode == STATION_MODE)
msg.body.key_info.action = ADDKEY;
- msg.body.key_info.attr.wpa.key = kmalloc(u8PtkKeylen, GFP_KERNEL);
- memcpy(msg.body.key_info.attr.wpa.key, pu8Ptk, u8PtkKeylen);
+ msg.body.key_info.attr.wpa.key = kmemdup(ptk, ptk_key_len, GFP_KERNEL);
+ if (!msg.body.key_info.attr.wpa.key)
+ return -ENOMEM;
- if (pu8RxMic) {
- memcpy(msg.body.key_info.attr.wpa.key + 16, pu8RxMic, RX_MIC_KEY_LEN);
+ if (rx_mic) {
+ memcpy(msg.body.key_info.attr.wpa.key + 16, rx_mic, RX_MIC_KEY_LEN);
if (INFO) {
for (i = 0; i < RX_MIC_KEY_LEN; i++)
- PRINT_INFO(CFG80211_DBG, "PairwiseRx[%d] = %x\n", i, pu8RxMic[i]);
+ PRINT_INFO(CFG80211_DBG, "PairwiseRx[%d] = %x\n", i, rx_mic[i]);
}
}
- if (pu8TxMic) {
- memcpy(msg.body.key_info.attr.wpa.key + 24, pu8TxMic, TX_MIC_KEY_LEN);
+ if (tx_mic) {
+ memcpy(msg.body.key_info.attr.wpa.key + 24, tx_mic, TX_MIC_KEY_LEN);
if (INFO) {
for (i = 0; i < TX_MIC_KEY_LEN; i++)
- PRINT_INFO(CFG80211_DBG, "PairwiseTx[%d] = %x\n", i, pu8TxMic[i]);
+ PRINT_INFO(CFG80211_DBG, "PairwiseTx[%d] = %x\n", i, tx_mic[i]);
}
}
- msg.body.key_info.attr.wpa.key_len = u8KeyLen;
+ msg.body.key_info.attr.wpa.key_len = key_len;
msg.body.key_info.attr.wpa.mac_addr = mac_addr;
- msg.body.key_info.attr.wpa.mode = u8Ciphermode;
- msg.drv = hif_drv;
+ msg.body.key_info.attr.wpa.mode = cipher_mode;
+ msg.vif = vif;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result)
PRINT_ER("Error in sending message queue: PTK Key\n");
- down(&hif_drv->hSemTestKeyBlock);
+ down(&hif_drv->sem_test_key_block);
return result;
}
-s32 host_int_add_rx_gtk(struct host_if_drv *hif_drv, const u8 *pu8RxGtk,
- u8 u8GtkKeylen, u8 u8KeyIdx,
- u32 u32KeyRSClen, const u8 *KeyRSC,
- const u8 *pu8RxMic, const u8 *pu8TxMic,
- u8 mode, u8 u8Ciphermode)
+int wilc_add_rx_gtk(struct wilc_vif *vif, const u8 *rx_gtk, u8 gtk_key_len,
+ u8 index, u32 key_rsc_len, const u8 *key_rsc,
+ const u8 *rx_mic, const u8 *tx_mic, u8 mode,
+ u8 cipher_mode)
{
- s32 result = 0;
+ int result = 0;
struct host_if_msg msg;
- u8 u8KeyLen = u8GtkKeylen;
+ struct host_if_drv *hif_drv = vif->hif_drv;
+ u8 key_len = gtk_key_len;
if (!hif_drv) {
PRINT_ER("driver is null\n");
@@ -3264,56 +3239,64 @@ s32 host_int_add_rx_gtk(struct host_if_drv *hif_drv, const u8 *pu8RxGtk,
}
memset(&msg, 0, sizeof(struct host_if_msg));
- if (pu8RxMic)
- u8KeyLen += RX_MIC_KEY_LEN;
+ if (rx_mic)
+ key_len += RX_MIC_KEY_LEN;
- if (pu8TxMic)
- u8KeyLen += TX_MIC_KEY_LEN;
+ if (tx_mic)
+ key_len += TX_MIC_KEY_LEN;
- if (KeyRSC) {
- msg.body.key_info.attr.wpa.seq = kmalloc(u32KeyRSClen, GFP_KERNEL);
- memcpy(msg.body.key_info.attr.wpa.seq, KeyRSC, u32KeyRSClen);
+ if (key_rsc) {
+ msg.body.key_info.attr.wpa.seq = kmemdup(key_rsc,
+ key_rsc_len,
+ GFP_KERNEL);
+ if (!msg.body.key_info.attr.wpa.seq)
+ return -ENOMEM;
}
msg.id = HOST_IF_MSG_KEY;
- msg.body.key_info.type = WPARxGtk;
- msg.drv = hif_drv;
+ msg.body.key_info.type = WPA_RX_GTK;
+ msg.vif = vif;
if (mode == AP_MODE) {
msg.body.key_info.action = ADDKEY_AP;
- msg.body.key_info.attr.wpa.mode = u8Ciphermode;
+ msg.body.key_info.attr.wpa.mode = cipher_mode;
}
if (mode == STATION_MODE)
msg.body.key_info.action = ADDKEY;
- msg.body.key_info.attr.wpa.key = kmalloc(u8KeyLen, GFP_KERNEL);
- memcpy(msg.body.key_info.attr.wpa.key, pu8RxGtk, u8GtkKeylen);
+ msg.body.key_info.attr.wpa.key = kmemdup(rx_gtk,
+ key_len,
+ GFP_KERNEL);
+ if (!msg.body.key_info.attr.wpa.key)
+ return -ENOMEM;
- if (pu8RxMic)
- memcpy(msg.body.key_info.attr.wpa.key + 16, pu8RxMic,
+ if (rx_mic)
+ memcpy(msg.body.key_info.attr.wpa.key + 16, rx_mic,
RX_MIC_KEY_LEN);
- if (pu8TxMic)
- memcpy(msg.body.key_info.attr.wpa.key + 24, pu8TxMic,
+ if (tx_mic)
+ memcpy(msg.body.key_info.attr.wpa.key + 24, tx_mic,
TX_MIC_KEY_LEN);
- msg.body.key_info.attr.wpa.index = u8KeyIdx;
- msg.body.key_info.attr.wpa.key_len = u8KeyLen;
- msg.body.key_info.attr.wpa.seq_len = u32KeyRSClen;
+ msg.body.key_info.attr.wpa.index = index;
+ msg.body.key_info.attr.wpa.key_len = key_len;
+ msg.body.key_info.attr.wpa.seq_len = key_rsc_len;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result)
PRINT_ER("Error in sending message queue: RX GTK\n");
- down(&hif_drv->hSemTestKeyBlock);
+ down(&hif_drv->sem_test_key_block);
return result;
}
-s32 host_int_set_pmkid_info(struct host_if_drv *hif_drv, struct host_if_pmkid_attr *pu8PmkidInfoArray)
+s32 wilc_set_pmkid_info(struct wilc_vif *vif,
+ struct host_if_pmkid_attr *pu8PmkidInfoArray)
{
s32 result = 0;
struct host_if_msg msg;
+ struct host_if_drv *hif_drv = vif->hif_drv;
u32 i;
if (!hif_drv) {
@@ -3326,7 +3309,7 @@ s32 host_int_set_pmkid_info(struct host_if_drv *hif_drv, struct host_if_pmkid_at
msg.id = HOST_IF_MSG_KEY;
msg.body.key_info.type = PMKSA;
msg.body.key_info.action = ADDKEY;
- msg.drv = hif_drv;
+ msg.vif = vif;
for (i = 0; i < pu8PmkidInfoArray->numpmkid; i++) {
memcpy(msg.body.key_info.attr.pmkid.pmkidlist[i].bssid,
@@ -3342,37 +3325,7 @@ s32 host_int_set_pmkid_info(struct host_if_drv *hif_drv, struct host_if_pmkid_at
return result;
}
-s32 host_int_get_pmkid_info(struct host_if_drv *hif_drv,
- u8 *pu8PmkidInfoArray,
- u32 u32PmkidInfoLen)
-{
- struct wid wid;
-
- wid.id = (u16)WID_PMKID_INFO;
- wid.type = WID_STR;
- wid.size = u32PmkidInfoLen;
- wid.val = pu8PmkidInfoArray;
-
- return 0;
-}
-
-s32 host_int_set_RSNAConfigPSKPassPhrase(struct host_if_drv *hif_drv,
- u8 *pu8PassPhrase,
- u8 u8Psklength)
-{
- struct wid wid;
-
- if ((u8Psklength > 7) && (u8Psklength < 65)) {
- wid.id = (u16)WID_11I_PSK;
- wid.type = WID_STR;
- wid.val = pu8PassPhrase;
- wid.size = u8Psklength;
- }
-
- return 0;
-}
-
-s32 host_int_get_MacAddress(struct host_if_drv *hif_drv, u8 *pu8MacAddress)
+s32 wilc_get_mac_address(struct wilc_vif *vif, u8 *pu8MacAddress)
{
s32 result = 0;
struct host_if_msg msg;
@@ -3381,7 +3334,7 @@ s32 host_int_get_MacAddress(struct host_if_drv *hif_drv, u8 *pu8MacAddress)
msg.id = HOST_IF_MSG_GET_MAC_ADDRESS;
msg.body.get_mac_info.mac_addr = pu8MacAddress;
- msg.drv = hif_drv;
+ msg.vif = vif;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result) {
@@ -3393,7 +3346,7 @@ s32 host_int_get_MacAddress(struct host_if_drv *hif_drv, u8 *pu8MacAddress)
return result;
}
-s32 host_int_set_MacAddress(struct host_if_drv *hif_drv, u8 *pu8MacAddress)
+s32 wilc_set_mac_address(struct wilc_vif *vif, u8 *pu8MacAddress)
{
s32 result = 0;
struct host_if_msg msg;
@@ -3403,7 +3356,7 @@ s32 host_int_set_MacAddress(struct host_if_drv *hif_drv, u8 *pu8MacAddress)
memset(&msg, 0, sizeof(struct host_if_msg));
msg.id = HOST_IF_MSG_SET_MAC_ADDRESS;
memcpy(msg.body.set_mac_info.mac_addr, pu8MacAddress, ETH_ALEN);
- msg.drv = hif_drv;
+ msg.vif = vif;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result)
@@ -3412,52 +3365,15 @@ s32 host_int_set_MacAddress(struct host_if_drv *hif_drv, u8 *pu8MacAddress)
return result;
}
-s32 host_int_get_RSNAConfigPSKPassPhrase(struct host_if_drv *hif_drv,
- u8 *pu8PassPhrase, u8 u8Psklength)
-{
- struct wid wid;
-
- wid.id = (u16)WID_11I_PSK;
- wid.type = WID_STR;
- wid.size = u8Psklength;
- wid.val = pu8PassPhrase;
-
- return 0;
-}
-
-s32 host_int_set_start_scan_req(struct host_if_drv *hif_drv, u8 scanSource)
-{
- struct wid wid;
-
- wid.id = (u16)WID_START_SCAN_REQ;
- wid.type = WID_CHAR;
- wid.val = (s8 *)&scanSource;
- wid.size = sizeof(char);
-
- return 0;
-}
-
-s32 host_int_get_start_scan_req(struct host_if_drv *hif_drv, u8 *pu8ScanSource)
-{
- struct wid wid;
-
- wid.id = (u16)WID_START_SCAN_REQ;
- wid.type = WID_CHAR;
- wid.val = (s8 *)pu8ScanSource;
- wid.size = sizeof(char);
-
- return 0;
-}
-
-s32 host_int_set_join_req(struct host_if_drv *hif_drv, u8 *pu8bssid,
- const u8 *pu8ssid, size_t ssidLen,
- const u8 *pu8IEs, size_t IEsLen,
- wilc_connect_result pfConnectResult, void *pvUserArg,
- u8 u8security, enum AUTHTYPE tenuAuth_type,
- u8 u8channel, void *pJoinParams)
+s32 wilc_set_join_req(struct wilc_vif *vif, u8 *pu8bssid, const u8 *pu8ssid,
+ size_t ssidLen, const u8 *pu8IEs, size_t IEsLen,
+ wilc_connect_result pfConnectResult, void *pvUserArg,
+ u8 u8security, enum AUTHTYPE tenuAuth_type,
+ u8 u8channel, void *pJoinParams)
{
s32 result = 0;
struct host_if_msg msg;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv || !pfConnectResult) {
PRINT_ER("Driver is null\n");
@@ -3479,7 +3395,7 @@ s32 host_int_set_join_req(struct host_if_drv *hif_drv, u8 *pu8bssid,
msg.body.con_info.result = pfConnectResult;
msg.body.con_info.arg = pvUserArg;
msg.body.con_info.params = pJoinParams;
- msg.drv = hif_drv ;
+ msg.vif = vif;
if (pu8bssid) {
msg.body.con_info.bssid = kmalloc(6, GFP_KERNEL);
@@ -3497,10 +3413,11 @@ s32 host_int_set_join_req(struct host_if_drv *hif_drv, u8 *pu8bssid,
msg.body.con_info.ies = kmalloc(IEsLen, GFP_KERNEL);
memcpy(msg.body.con_info.ies, pu8IEs, IEsLen);
}
- if (hif_drv->enuHostIFstate < HOST_IF_CONNECTING)
- hif_drv->enuHostIFstate = HOST_IF_CONNECTING;
+ if (hif_drv->hif_state < HOST_IF_CONNECTING)
+ hif_drv->hif_state = HOST_IF_CONNECTING;
else
- PRINT_D(GENERIC_DBG, "Don't set state to 'connecting' as state is %d\n", hif_drv->enuHostIFstate);
+ PRINT_D(GENERIC_DBG, "Don't set state to 'connecting' : %d\n",
+ hif_drv->hif_state);
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result) {
@@ -3508,17 +3425,18 @@ s32 host_int_set_join_req(struct host_if_drv *hif_drv, u8 *pu8bssid,
return -EFAULT;
}
- hif_drv->hConnectTimer.data = (unsigned long)hif_drv;
- mod_timer(&hif_drv->hConnectTimer,
+ hif_drv->connect_timer.data = (unsigned long)vif;
+ mod_timer(&hif_drv->connect_timer,
jiffies + msecs_to_jiffies(HOST_IF_CONNECT_TIMEOUT));
return result;
}
-s32 host_int_flush_join_req(struct host_if_drv *hif_drv)
+s32 wilc_flush_join_req(struct wilc_vif *vif)
{
s32 result = 0;
struct host_if_msg msg;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!join_req)
return -EFAULT;
@@ -3529,7 +3447,7 @@ s32 host_int_flush_join_req(struct host_if_drv *hif_drv)
}
msg.id = HOST_IF_MSG_FLUSH_CONNECT;
- msg.drv = hif_drv;
+ msg.vif = vif;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result) {
@@ -3540,10 +3458,11 @@ s32 host_int_flush_join_req(struct host_if_drv *hif_drv)
return result;
}
-s32 host_int_disconnect(struct host_if_drv *hif_drv, u16 u16ReasonCode)
+s32 wilc_disconnect(struct wilc_vif *vif, u16 u16ReasonCode)
{
s32 result = 0;
struct host_if_msg msg;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
PRINT_ER("Driver is null\n");
@@ -3553,50 +3472,25 @@ s32 host_int_disconnect(struct host_if_drv *hif_drv, u16 u16ReasonCode)
memset(&msg, 0, sizeof(struct host_if_msg));
msg.id = HOST_IF_MSG_DISCONNECT;
- msg.drv = hif_drv;
+ msg.vif = vif;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result)
PRINT_ER("Failed to send message queue: disconnect\n");
- down(&hif_drv->hSemTestDisconnectBlock);
+ down(&hif_drv->sem_test_disconn_block);
return result;
}
-s32 host_int_disconnect_station(struct host_if_drv *hif_drv, u8 assoc_id)
-{
- struct wid wid;
-
- wid.id = (u16)WID_DISCONNECT;
- wid.type = WID_CHAR;
- wid.val = (s8 *)&assoc_id;
- wid.size = sizeof(char);
-
- return 0;
-}
-
-s32 host_int_get_assoc_req_info(struct host_if_drv *hif_drv,
- u8 *pu8AssocReqInfo,
- u32 u32AssocReqInfoLen)
-{
- struct wid wid;
-
- wid.id = (u16)WID_ASSOC_REQ_INFO;
- wid.type = WID_STR;
- wid.val = pu8AssocReqInfo;
- wid.size = u32AssocReqInfoLen;
-
- return 0;
-}
-
-s32 host_int_get_assoc_res_info(struct host_if_drv *hif_drv,
- u8 *pu8AssocRespInfo,
- u32 u32MaxAssocRespInfoLen,
- u32 *pu32RcvdAssocRespInfoLen)
+static s32 host_int_get_assoc_res_info(struct wilc_vif *vif,
+ u8 *pu8AssocRespInfo,
+ u32 u32MaxAssocRespInfoLen,
+ u32 *pu32RcvdAssocRespInfoLen)
{
s32 result = 0;
struct wid wid;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
PRINT_ER("Driver is null\n");
@@ -3608,8 +3502,8 @@ s32 host_int_get_assoc_res_info(struct host_if_drv *hif_drv,
wid.val = pu8AssocRespInfo;
wid.size = u32MaxAssocRespInfoLen;
- result = send_config_pkt(GET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
+ result = wilc_send_config_pkt(vif->wilc, GET_CFG, &wid, 1,
+ wilc_get_vif_idx(vif));
if (result) {
*pu32RcvdAssocRespInfoLen = 0;
PRINT_ER("Failed to send association response config packet\n");
@@ -3621,24 +3515,11 @@ s32 host_int_get_assoc_res_info(struct host_if_drv *hif_drv,
return result;
}
-s32 host_int_get_rx_power_level(struct host_if_drv *hif_drv,
- u8 *pu8RxPowerLevel,
- u32 u32RxPowerLevelLen)
-{
- struct wid wid;
-
- wid.id = (u16)WID_RX_POWER_LEVEL;
- wid.type = WID_STR;
- wid.val = pu8RxPowerLevel;
- wid.size = u32RxPowerLevelLen;
-
- return 0;
-}
-
-int host_int_set_mac_chnl_num(struct host_if_drv *hif_drv, u8 channel)
+int wilc_set_mac_chnl_num(struct wilc_vif *vif, u8 channel)
{
int result;
struct host_if_msg msg;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
PRINT_ER("driver is null\n");
@@ -3648,7 +3529,7 @@ int host_int_set_mac_chnl_num(struct host_if_drv *hif_drv, u8 channel)
memset(&msg, 0, sizeof(struct host_if_msg));
msg.id = HOST_IF_MSG_SET_CHANNEL;
msg.body.channel_info.set_ch = channel;
- msg.drv = hif_drv;
+ msg.vif = vif;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result) {
@@ -3659,7 +3540,7 @@ int host_int_set_mac_chnl_num(struct host_if_drv *hif_drv, u8 channel)
return 0;
}
-int host_int_wait_msg_queue_idle(void)
+int wilc_wait_msg_queue_idle(void)
{
int result = 0;
struct host_if_msg msg;
@@ -3677,15 +3558,15 @@ int host_int_wait_msg_queue_idle(void)
return result;
}
-int host_int_set_wfi_drv_handler(struct host_if_drv *hif_drv)
+int wilc_set_wfi_drv_handler(struct wilc_vif *vif, int index)
{
int result = 0;
struct host_if_msg msg;
memset(&msg, 0, sizeof(struct host_if_msg));
msg.id = HOST_IF_MSG_SET_WFIDRV_HANDLER;
- msg.body.drv.handler = get_id_from_handler(hif_drv);
- msg.drv = hif_drv;
+ msg.body.drv.handler = index;
+ msg.vif = vif;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result) {
@@ -3696,7 +3577,7 @@ int host_int_set_wfi_drv_handler(struct host_if_drv *hif_drv)
return result;
}
-int host_int_set_operation_mode(struct host_if_drv *hif_drv, u32 mode)
+int wilc_set_operation_mode(struct wilc_vif *vif, u32 mode)
{
int result = 0;
struct host_if_msg msg;
@@ -3704,7 +3585,7 @@ int host_int_set_operation_mode(struct host_if_drv *hif_drv, u32 mode)
memset(&msg, 0, sizeof(struct host_if_msg));
msg.id = HOST_IF_MSG_SET_OPERATION_MODE;
msg.body.mode.mode = mode;
- msg.drv = hif_drv;
+ msg.vif = vif;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result) {
@@ -3715,36 +3596,12 @@ int host_int_set_operation_mode(struct host_if_drv *hif_drv, u32 mode)
return result;
}
-s32 host_int_get_host_chnl_num(struct host_if_drv *hif_drv, u8 *pu8ChNo)
-{
- s32 result = 0;
- struct host_if_msg msg;
-
- if (!hif_drv) {
- PRINT_ER("driver is null\n");
- return -EFAULT;
- }
-
- memset(&msg, 0, sizeof(struct host_if_msg));
-
- msg.id = HOST_IF_MSG_GET_CHNL;
- msg.drv = hif_drv;
-
- result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
- if (result)
- PRINT_ER("wilc mq send fail\n");
- down(&hif_drv->hSemGetCHNL);
-
- *pu8ChNo = ch_no;
-
- return result;
-}
-
-s32 host_int_get_inactive_time(struct host_if_drv *hif_drv,
- const u8 *mac, u32 *pu32InactiveTime)
+s32 wilc_get_inactive_time(struct wilc_vif *vif, const u8 *mac,
+ u32 *pu32InactiveTime)
{
s32 result = 0;
struct host_if_msg msg;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
PRINT_ER("driver is null\n");
@@ -3755,55 +3612,28 @@ s32 host_int_get_inactive_time(struct host_if_drv *hif_drv,
memcpy(msg.body.mac_info.mac, mac, ETH_ALEN);
msg.id = HOST_IF_MSG_GET_INACTIVETIME;
- msg.drv = hif_drv;
+ msg.vif = vif;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result)
PRINT_ER("Failed to send get host channel param's message queue ");
- down(&hif_drv->hSemInactiveTime);
+ down(&hif_drv->sem_inactive_time);
*pu32InactiveTime = inactive_time;
return result;
}
-s32 host_int_test_get_int_wid(struct host_if_drv *hif_drv, u32 *pu32TestMemAddr)
-{
- s32 result = 0;
- struct wid wid;
-
- if (!hif_drv) {
- PRINT_ER("driver is null\n");
- return -EFAULT;
- }
-
- wid.id = (u16)WID_MEMORY_ADDRESS;
- wid.type = WID_INT;
- wid.val = (s8 *)pu32TestMemAddr;
- wid.size = sizeof(u32);
-
- result = send_config_pkt(GET_CFG, &wid, 1,
- get_id_from_handler(hif_drv));
-
- if (result) {
- PRINT_ER("Failed to get wid value\n");
- return -EINVAL;
- } else {
- PRINT_D(HOSTINF_DBG, "Successfully got wid value\n");
- }
-
- return result;
-}
-
-s32 host_int_get_rssi(struct host_if_drv *hif_drv, s8 *ps8Rssi)
+s32 wilc_get_rssi(struct wilc_vif *vif, s8 *ps8Rssi)
{
s32 result = 0;
struct host_if_msg msg;
+ struct host_if_drv *hif_drv = vif->hif_drv;
memset(&msg, 0, sizeof(struct host_if_msg));
msg.id = HOST_IF_MSG_GET_RSSI;
- msg.drv = hif_drv;
+ msg.vif = vif;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result) {
@@ -3811,7 +3641,7 @@ s32 host_int_get_rssi(struct host_if_drv *hif_drv, s8 *ps8Rssi)
return -EFAULT;
}
- down(&hif_drv->hSemGetRSSI);
+ down(&hif_drv->sem_get_rssi);
if (!ps8Rssi) {
PRINT_ER("RSS pointer value is null");
@@ -3823,34 +3653,7 @@ s32 host_int_get_rssi(struct host_if_drv *hif_drv, s8 *ps8Rssi)
return result;
}
-s32 host_int_get_link_speed(struct host_if_drv *hif_drv, s8 *ps8lnkspd)
-{
- struct host_if_msg msg;
- s32 result = 0;
-
- memset(&msg, 0, sizeof(struct host_if_msg));
- msg.id = HOST_IF_MSG_GET_LINKSPEED;
- msg.drv = hif_drv;
-
- result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
- if (result) {
- PRINT_ER("Failed to send GET_LINKSPEED to message queue ");
- return -EFAULT;
- }
-
- down(&hif_drv->hSemGetLINKSPEED);
-
- if (!ps8lnkspd) {
- PRINT_ER("LINKSPEED pointer value is null");
- return -EFAULT;
- }
-
- *ps8lnkspd = link_speed;
-
- return result;
-}
-
-s32 host_int_get_statistics(struct host_if_drv *hif_drv, struct rf_info *pstrStatistics)
+s32 wilc_get_statistics(struct wilc_vif *vif, struct rf_info *pstrStatistics)
{
s32 result = 0;
struct host_if_msg msg;
@@ -3858,7 +3661,7 @@ s32 host_int_get_statistics(struct host_if_drv *hif_drv, struct rf_info *pstrSta
memset(&msg, 0, sizeof(struct host_if_msg));
msg.id = HOST_IF_MSG_GET_STATISTICS;
msg.body.data = (char *)pstrStatistics;
- msg.drv = hif_drv;
+ msg.vif = vif;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result) {
@@ -3870,14 +3673,14 @@ s32 host_int_get_statistics(struct host_if_drv *hif_drv, struct rf_info *pstrSta
return result;
}
-s32 host_int_scan(struct host_if_drv *hif_drv, u8 u8ScanSource,
- u8 u8ScanType, u8 *pu8ChnlFreqList,
- u8 u8ChnlListLen, const u8 *pu8IEs,
- size_t IEsLen, wilc_scan_result ScanResult,
- void *pvUserArg, struct hidden_network *pstrHiddenNetwork)
+s32 wilc_scan(struct wilc_vif *vif, u8 u8ScanSource, u8 u8ScanType,
+ u8 *pu8ChnlFreqList, u8 u8ChnlListLen, const u8 *pu8IEs,
+ size_t IEsLen, wilc_scan_result ScanResult, void *pvUserArg,
+ struct hidden_network *pstrHiddenNetwork)
{
s32 result = 0;
struct host_if_msg msg;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv || !ScanResult) {
PRINT_ER("hif_drv or ScanResult = NULL\n");
@@ -3895,7 +3698,7 @@ s32 host_int_scan(struct host_if_drv *hif_drv, u8 u8ScanSource,
} else
PRINT_D(HOSTINF_DBG, "pstrHiddenNetwork IS EQUAL TO NULL\n");
- msg.drv = hif_drv;
+ msg.vif = vif;
msg.body.scan_info.src = u8ScanSource;
msg.body.scan_info.type = u8ScanType;
msg.body.scan_info.result = ScanResult;
@@ -3916,18 +3719,19 @@ s32 host_int_scan(struct host_if_drv *hif_drv, u8 u8ScanSource,
}
PRINT_D(HOSTINF_DBG, ">> Starting the SCAN timer\n");
- hif_drv->hScanTimer.data = (unsigned long)hif_drv;
- mod_timer(&hif_drv->hScanTimer,
+ hif_drv->scan_timer.data = (unsigned long)vif;
+ mod_timer(&hif_drv->scan_timer,
jiffies + msecs_to_jiffies(HOST_IF_SCAN_TIMEOUT));
return result;
}
-s32 hif_set_cfg(struct host_if_drv *hif_drv,
- struct cfg_param_val *pstrCfgParamVal)
+s32 wilc_hif_set_cfg(struct wilc_vif *vif,
+ struct cfg_param_val *pstrCfgParamVal)
{
s32 result = 0;
struct host_if_msg msg;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
PRINT_ER("hif_drv NULL\n");
@@ -3937,123 +3741,30 @@ s32 hif_set_cfg(struct host_if_drv *hif_drv,
memset(&msg, 0, sizeof(struct host_if_msg));
msg.id = HOST_IF_MSG_CFG_PARAMS;
msg.body.cfg_info.cfg_attr_info = *pstrCfgParamVal;
- msg.drv = hif_drv;
+ msg.vif = vif;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
return result;
}
-s32 hif_get_cfg(struct host_if_drv *hif_drv, u16 u16WID, u16 *pu16WID_Value)
-{
- s32 result = 0;
-
- down(&hif_drv->gtOsCfgValuesSem);
-
- if (!hif_drv) {
- PRINT_ER("hif_drv NULL\n");
- return -EFAULT;
- }
- PRINT_D(HOSTINF_DBG, "Getting configuration parameters\n");
- switch (u16WID) {
- case WID_BSS_TYPE:
- *pu16WID_Value = (u16)hif_drv->strCfgValues.bss_type;
- break;
-
- case WID_AUTH_TYPE:
- *pu16WID_Value = (u16)hif_drv->strCfgValues.auth_type;
- break;
-
- case WID_AUTH_TIMEOUT:
- *pu16WID_Value = hif_drv->strCfgValues.auth_timeout;
- break;
-
- case WID_POWER_MANAGEMENT:
- *pu16WID_Value = (u16)hif_drv->strCfgValues.power_mgmt_mode;
- break;
-
- case WID_SHORT_RETRY_LIMIT:
- *pu16WID_Value = hif_drv->strCfgValues.short_retry_limit;
- break;
-
- case WID_LONG_RETRY_LIMIT:
- *pu16WID_Value = hif_drv->strCfgValues.long_retry_limit;
- break;
-
- case WID_FRAG_THRESHOLD:
- *pu16WID_Value = hif_drv->strCfgValues.frag_threshold;
- break;
-
- case WID_RTS_THRESHOLD:
- *pu16WID_Value = hif_drv->strCfgValues.rts_threshold;
- break;
-
- case WID_PREAMBLE:
- *pu16WID_Value = (u16)hif_drv->strCfgValues.preamble_type;
- break;
-
- case WID_SHORT_SLOT_ALLOWED:
- *pu16WID_Value = (u16) hif_drv->strCfgValues.short_slot_allowed;
- break;
-
- case WID_11N_TXOP_PROT_DISABLE:
- *pu16WID_Value = (u16)hif_drv->strCfgValues.txop_prot_disabled;
- break;
-
- case WID_BEACON_INTERVAL:
- *pu16WID_Value = hif_drv->strCfgValues.beacon_interval;
- break;
-
- case WID_DTIM_PERIOD:
- *pu16WID_Value = (u16)hif_drv->strCfgValues.dtim_period;
- break;
-
- case WID_SITE_SURVEY:
- *pu16WID_Value = (u16)hif_drv->strCfgValues.site_survey_enabled;
- break;
-
- case WID_SITE_SURVEY_SCAN_TIME:
- *pu16WID_Value = hif_drv->strCfgValues.site_survey_scan_time;
- break;
-
- case WID_ACTIVE_SCAN_TIME:
- *pu16WID_Value = hif_drv->strCfgValues.active_scan_time;
- break;
-
- case WID_PASSIVE_SCAN_TIME:
- *pu16WID_Value = hif_drv->strCfgValues.passive_scan_time;
- break;
-
- case WID_CURRENT_TX_RATE:
- *pu16WID_Value = hif_drv->strCfgValues.curr_tx_rate;
- break;
-
- default:
- break;
- }
-
- up(&hif_drv->gtOsCfgValuesSem);
-
- return result;
-}
-
static void GetPeriodicRSSI(unsigned long arg)
{
- struct host_if_drv *hif_drv = (struct host_if_drv *)arg;
+ struct wilc_vif *vif = (struct wilc_vif *)arg;
- if (!hif_drv) {
+ if (!vif->hif_drv) {
PRINT_ER("Driver handler is NULL\n");
return;
}
- if (hif_drv->enuHostIFstate == HOST_IF_CONNECTED) {
+ if (vif->hif_drv->hif_state == HOST_IF_CONNECTED) {
s32 result = 0;
struct host_if_msg msg;
memset(&msg, 0, sizeof(struct host_if_msg));
msg.id = HOST_IF_MSG_GET_RSSI;
- msg.drv = hif_drv;
+ msg.vif = vif;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result) {
@@ -4061,20 +3772,20 @@ static void GetPeriodicRSSI(unsigned long arg)
return;
}
}
- periodic_rssi.data = (unsigned long)hif_drv;
+ periodic_rssi.data = (unsigned long)vif;
mod_timer(&periodic_rssi, jiffies + msecs_to_jiffies(5000));
}
-s32 host_int_init(struct net_device *dev, struct host_if_drv **hif_drv_handler)
+s32 wilc_init(struct net_device *dev, struct host_if_drv **hif_drv_handler)
{
s32 result = 0;
struct host_if_drv *hif_drv;
- int err;
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct wilc *wilc;
+ int i;
- nic = netdev_priv(dev);
- wilc = nic->wilc;
+ vif = netdev_priv(dev);
+ wilc = vif->wilc;
PRINT_D(HOSTINF_DBG, "Initializing host interface for client %d\n", clients_count + 1);
@@ -4088,13 +3799,13 @@ s32 host_int_init(struct net_device *dev, struct host_if_drv **hif_drv_handler)
goto _fail_;
}
*hif_drv_handler = hif_drv;
- err = add_handler_in_list(hif_drv);
- if (err) {
- result = -EFAULT;
- goto _fail_timer_2;
- }
+ for (i = 0; i < wilc->vif_num; i++)
+ if (dev == wilc->vif[i]->ndev) {
+ wilc->vif[i]->hif_drv = hif_drv;
+ break;
+ }
- g_obtainingIP = false;
+ wilc_optaining_ip = false;
PRINT_D(HOSTINF_DBG, "Global handle pointer value=%p\n", hif_drv);
if (clients_count == 0) {
@@ -4103,12 +3814,12 @@ s32 host_int_init(struct net_device *dev, struct host_if_drv **hif_drv_handler)
sema_init(&hif_sema_deinit, 1);
}
- sema_init(&hif_drv->hSemTestKeyBlock, 0);
- sema_init(&hif_drv->hSemTestDisconnectBlock, 0);
- sema_init(&hif_drv->hSemGetRSSI, 0);
- sema_init(&hif_drv->hSemGetLINKSPEED, 0);
- sema_init(&hif_drv->hSemGetCHNL, 0);
- sema_init(&hif_drv->hSemInactiveTime, 0);
+ sema_init(&hif_drv->sem_test_key_block, 0);
+ sema_init(&hif_drv->sem_test_disconn_block, 0);
+ sema_init(&hif_drv->sem_get_rssi, 0);
+ sema_init(&hif_drv->sem_get_link_speed, 0);
+ sema_init(&hif_drv->sem_get_chnl, 0);
+ sema_init(&hif_drv->sem_inactive_time, 0);
PRINT_D(HOSTINF_DBG, "INIT: CLIENT COUNT %d\n", clients_count);
@@ -4129,56 +3840,50 @@ s32 host_int_init(struct net_device *dev, struct host_if_drv **hif_drv_handler)
goto _fail_mq_;
}
setup_timer(&periodic_rssi, GetPeriodicRSSI,
- (unsigned long)hif_drv);
+ (unsigned long)vif);
mod_timer(&periodic_rssi, jiffies + msecs_to_jiffies(5000));
}
- setup_timer(&hif_drv->hScanTimer, TimerCB_Scan, 0);
-
- setup_timer(&hif_drv->hConnectTimer, TimerCB_Connect, 0);
+ setup_timer(&hif_drv->scan_timer, TimerCB_Scan, 0);
+ setup_timer(&hif_drv->connect_timer, TimerCB_Connect, 0);
+ setup_timer(&hif_drv->remain_on_ch_timer, ListenTimerCB, 0);
- setup_timer(&hif_drv->hRemainOnChannel, ListenTimerCB, 0);
+ sema_init(&hif_drv->sem_cfg_values, 1);
+ down(&hif_drv->sem_cfg_values);
- sema_init(&hif_drv->gtOsCfgValuesSem, 1);
- down(&hif_drv->gtOsCfgValuesSem);
+ hif_drv->hif_state = HOST_IF_IDLE;
+ hif_drv->cfg_values.site_survey_enabled = SITE_SURVEY_OFF;
+ hif_drv->cfg_values.scan_source = DEFAULT_SCAN;
+ hif_drv->cfg_values.active_scan_time = ACTIVE_SCAN_TIME;
+ hif_drv->cfg_values.passive_scan_time = PASSIVE_SCAN_TIME;
+ hif_drv->cfg_values.curr_tx_rate = AUTORATE;
- hif_drv->enuHostIFstate = HOST_IF_IDLE;
- hif_drv->strCfgValues.site_survey_enabled = SITE_SURVEY_OFF;
- hif_drv->strCfgValues.scan_source = DEFAULT_SCAN;
- hif_drv->strCfgValues.active_scan_time = ACTIVE_SCAN_TIME;
- hif_drv->strCfgValues.passive_scan_time = PASSIVE_SCAN_TIME;
- hif_drv->strCfgValues.curr_tx_rate = AUTORATE;
-
- hif_drv->u64P2p_MgmtTimeout = 0;
+ hif_drv->p2p_timeout = 0;
PRINT_INFO(HOSTINF_DBG, "Initialization values, Site survey value: %d\n Scan source: %d\n Active scan time: %d\n Passive scan time: %d\nCurrent tx Rate = %d\n",
+ hif_drv->cfg_values.site_survey_enabled,
+ hif_drv->cfg_values.scan_source,
+ hif_drv->cfg_values.active_scan_time,
+ hif_drv->cfg_values.passive_scan_time,
+ hif_drv->cfg_values.curr_tx_rate);
- hif_drv->strCfgValues.site_survey_enabled, hif_drv->strCfgValues.scan_source,
- hif_drv->strCfgValues.active_scan_time, hif_drv->strCfgValues.passive_scan_time,
- hif_drv->strCfgValues.curr_tx_rate);
-
- up(&hif_drv->gtOsCfgValuesSem);
+ up(&hif_drv->sem_cfg_values);
clients_count++;
return result;
-_fail_timer_2:
- up(&hif_drv->gtOsCfgValuesSem);
- del_timer_sync(&hif_drv->hConnectTimer);
- del_timer_sync(&hif_drv->hScanTimer);
- kthread_stop(hif_thread_handler);
_fail_mq_:
wilc_mq_destroy(&hif_msg_q);
_fail_:
return result;
}
-s32 host_int_deinit(struct host_if_drv *hif_drv)
+s32 wilc_deinit(struct wilc_vif *vif)
{
s32 result = 0;
struct host_if_msg msg;
- int ret;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
PRINT_ER("hif_drv = NULL\n");
@@ -4190,28 +3895,27 @@ s32 host_int_deinit(struct host_if_drv *hif_drv)
terminated_handle = hif_drv;
PRINT_D(HOSTINF_DBG, "De-initializing host interface for client %d\n", clients_count);
- if (del_timer_sync(&hif_drv->hScanTimer))
+ if (del_timer_sync(&hif_drv->scan_timer))
PRINT_D(HOSTINF_DBG, ">> Scan timer is active\n");
- if (del_timer_sync(&hif_drv->hConnectTimer))
+ if (del_timer_sync(&hif_drv->connect_timer))
PRINT_D(HOSTINF_DBG, ">> Connect timer is active\n");
if (del_timer_sync(&periodic_rssi))
PRINT_D(HOSTINF_DBG, ">> Connect timer is active\n");
- del_timer_sync(&hif_drv->hRemainOnChannel);
+ del_timer_sync(&hif_drv->remain_on_ch_timer);
- host_int_set_wfi_drv_handler(NULL);
+ wilc_set_wfi_drv_handler(vif, 0);
down(&hif_sema_driver);
- if (hif_drv->usr_scan_req.pfUserScanResult) {
- hif_drv->usr_scan_req.pfUserScanResult(SCAN_EVENT_ABORTED, NULL,
- hif_drv->usr_scan_req.u32UserScanPvoid, NULL);
-
- hif_drv->usr_scan_req.pfUserScanResult = NULL;
+ if (hif_drv->usr_scan_req.scan_result) {
+ hif_drv->usr_scan_req.scan_result(SCAN_EVENT_ABORTED, NULL,
+ hif_drv->usr_scan_req.arg, NULL);
+ hif_drv->usr_scan_req.scan_result = NULL;
}
- hif_drv->enuHostIFstate = HOST_IF_IDLE;
+ hif_drv->hif_state = HOST_IF_IDLE;
scan_while_connected = false;
@@ -4222,7 +3926,7 @@ s32 host_int_deinit(struct host_if_drv *hif_drv)
PRINT_D(HOSTINF_DBG, ">> Connect timer is active\n");
msg.id = HOST_IF_MSG_EXIT;
- msg.drv = hif_drv;
+ msg.vif = vif;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result != 0)
@@ -4233,12 +3937,6 @@ s32 host_int_deinit(struct host_if_drv *hif_drv)
wilc_mq_destroy(&hif_msg_q);
}
- down(&hif_drv->gtOsCfgValuesSem);
-
- ret = remove_handler_in_list(hif_drv);
- if (ret)
- result = -ENOENT;
-
kfree(hif_drv);
clients_count--;
@@ -4247,15 +3945,20 @@ s32 host_int_deinit(struct host_if_drv *hif_drv)
return result;
}
-void NetworkInfoReceived(u8 *pu8Buffer, u32 u32Length)
+void wilc_network_info_received(struct wilc *wilc, u8 *pu8Buffer,
+ u32 u32Length)
{
s32 result = 0;
struct host_if_msg msg;
int id;
struct host_if_drv *hif_drv = NULL;
+ struct wilc_vif *vif;
id = ((pu8Buffer[u32Length - 4]) | (pu8Buffer[u32Length - 3] << 8) | (pu8Buffer[u32Length - 2] << 16) | (pu8Buffer[u32Length - 1] << 24));
- hif_drv = get_handler_from_id(id);
+ vif = wilc_get_vif_from_idx(wilc, id);
+ if (!vif)
+ return;
+ hif_drv = vif->hif_drv;
if (!hif_drv || hif_drv == terminated_handle) {
PRINT_ER("NetworkInfo received but driver not init[%p]\n", hif_drv);
@@ -4265,7 +3968,7 @@ void NetworkInfoReceived(u8 *pu8Buffer, u32 u32Length)
memset(&msg, 0, sizeof(struct host_if_msg));
msg.id = HOST_IF_MSG_RCVD_NTWRK_INFO;
- msg.drv = hif_drv;
+ msg.vif = vif;
msg.body.net_info.len = u32Length;
msg.body.net_info.buffer = kmalloc(u32Length, GFP_KERNEL);
@@ -4276,17 +3979,25 @@ void NetworkInfoReceived(u8 *pu8Buffer, u32 u32Length)
PRINT_ER("Error in sending network info message queue message parameters: Error(%d)\n", result);
}
-void GnrlAsyncInfoReceived(u8 *pu8Buffer, u32 u32Length)
+void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *pu8Buffer,
+ u32 u32Length)
{
s32 result = 0;
struct host_if_msg msg;
int id;
struct host_if_drv *hif_drv = NULL;
+ struct wilc_vif *vif;
down(&hif_sema_deinit);
id = ((pu8Buffer[u32Length - 4]) | (pu8Buffer[u32Length - 3] << 8) | (pu8Buffer[u32Length - 2] << 16) | (pu8Buffer[u32Length - 1] << 24));
- hif_drv = get_handler_from_id(id);
+ vif = wilc_get_vif_from_idx(wilc, id);
+ if (!vif) {
+ up(&hif_sema_deinit);
+ return;
+ }
+
+ hif_drv = vif->hif_drv;
PRINT_D(HOSTINF_DBG, "General asynchronous info packet received\n");
if (!hif_drv || hif_drv == terminated_handle) {
@@ -4295,7 +4006,7 @@ void GnrlAsyncInfoReceived(u8 *pu8Buffer, u32 u32Length)
return;
}
- if (!hif_drv->usr_conn_req.pfUserConnectResult) {
+ if (!hif_drv->usr_conn_req.conn_result) {
PRINT_ER("Received mac status is not needed when there is no current Connect Reques\n");
up(&hif_sema_deinit);
return;
@@ -4304,7 +4015,7 @@ void GnrlAsyncInfoReceived(u8 *pu8Buffer, u32 u32Length)
memset(&msg, 0, sizeof(struct host_if_msg));
msg.id = HOST_IF_MSG_RCVD_GNRL_ASYNC_INFO;
- msg.drv = hif_drv;
+ msg.vif = vif;
msg.body.async_info.len = u32Length;
msg.body.async_info.buffer = kmalloc(u32Length, GFP_KERNEL);
@@ -4317,26 +4028,31 @@ void GnrlAsyncInfoReceived(u8 *pu8Buffer, u32 u32Length)
up(&hif_sema_deinit);
}
-void host_int_ScanCompleteReceived(u8 *pu8Buffer, u32 u32Length)
+void wilc_scan_complete_received(struct wilc *wilc, u8 *pu8Buffer,
+ u32 u32Length)
{
s32 result = 0;
struct host_if_msg msg;
int id;
struct host_if_drv *hif_drv = NULL;
+ struct wilc_vif *vif;
id = ((pu8Buffer[u32Length - 4]) | (pu8Buffer[u32Length - 3] << 8) | (pu8Buffer[u32Length - 2] << 16) | (pu8Buffer[u32Length - 1] << 24));
- hif_drv = get_handler_from_id(id);
+ vif = wilc_get_vif_from_idx(wilc, id);
+ if (!vif)
+ return;
+ hif_drv = vif->hif_drv;
PRINT_D(GENERIC_DBG, "Scan notification received %p\n", hif_drv);
if (!hif_drv || hif_drv == terminated_handle)
return;
- if (hif_drv->usr_scan_req.pfUserScanResult) {
+ if (hif_drv->usr_scan_req.scan_result) {
memset(&msg, 0, sizeof(struct host_if_msg));
msg.id = HOST_IF_MSG_RCVD_SCAN_COMPLETE;
- msg.drv = hif_drv;
+ msg.vif = vif;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result)
@@ -4346,14 +4062,15 @@ void host_int_ScanCompleteReceived(u8 *pu8Buffer, u32 u32Length)
return;
}
-s32 host_int_remain_on_channel(struct host_if_drv *hif_drv, u32 u32SessionID,
- u32 u32duration, u16 chan,
- wilc_remain_on_chan_expired RemainOnChanExpired,
- wilc_remain_on_chan_ready RemainOnChanReady,
- void *pvUserArg)
+s32 wilc_remain_on_channel(struct wilc_vif *vif, u32 u32SessionID,
+ u32 u32duration, u16 chan,
+ wilc_remain_on_chan_expired RemainOnChanExpired,
+ wilc_remain_on_chan_ready RemainOnChanReady,
+ void *pvUserArg)
{
s32 result = 0;
struct host_if_msg msg;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
PRINT_ER("driver is null\n");
@@ -4363,13 +4080,13 @@ s32 host_int_remain_on_channel(struct host_if_drv *hif_drv, u32 u32SessionID,
memset(&msg, 0, sizeof(struct host_if_msg));
msg.id = HOST_IF_MSG_REMAIN_ON_CHAN;
- msg.body.remain_on_ch.u16Channel = chan;
- msg.body.remain_on_ch.pRemainOnChanExpired = RemainOnChanExpired;
- msg.body.remain_on_ch.pRemainOnChanReady = RemainOnChanReady;
- msg.body.remain_on_ch.pVoid = pvUserArg;
+ msg.body.remain_on_ch.ch = chan;
+ msg.body.remain_on_ch.expired = RemainOnChanExpired;
+ msg.body.remain_on_ch.ready = RemainOnChanReady;
+ msg.body.remain_on_ch.arg = pvUserArg;
msg.body.remain_on_ch.u32duration = u32duration;
- msg.body.remain_on_ch.u32ListenSessionID = u32SessionID;
- msg.drv = hif_drv;
+ msg.body.remain_on_ch.id = u32SessionID;
+ msg.vif = vif;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result)
@@ -4378,22 +4095,23 @@ s32 host_int_remain_on_channel(struct host_if_drv *hif_drv, u32 u32SessionID,
return result;
}
-s32 host_int_ListenStateExpired(struct host_if_drv *hif_drv, u32 u32SessionID)
+s32 wilc_listen_state_expired(struct wilc_vif *vif, u32 u32SessionID)
{
s32 result = 0;
struct host_if_msg msg;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
PRINT_ER("driver is null\n");
return -EFAULT;
}
- del_timer(&hif_drv->hRemainOnChannel);
+ del_timer(&hif_drv->remain_on_ch_timer);
memset(&msg, 0, sizeof(struct host_if_msg));
msg.id = HOST_IF_MSG_LISTEN_TIMER_FIRED;
- msg.drv = hif_drv;
- msg.body.remain_on_ch.u32ListenSessionID = u32SessionID;
+ msg.vif = vif;
+ msg.body.remain_on_ch.id = u32SessionID;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result)
@@ -4402,10 +4120,11 @@ s32 host_int_ListenStateExpired(struct host_if_drv *hif_drv, u32 u32SessionID)
return result;
}
-s32 host_int_frame_register(struct host_if_drv *hif_drv, u16 u16FrameType, bool bReg)
+s32 wilc_frame_register(struct wilc_vif *vif, u16 u16FrameType, bool bReg)
{
s32 result = 0;
struct host_if_msg msg;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
PRINT_ER("driver is null\n");
@@ -4418,21 +4137,21 @@ s32 host_int_frame_register(struct host_if_drv *hif_drv, u16 u16FrameType, bool
switch (u16FrameType) {
case ACTION:
PRINT_D(HOSTINF_DBG, "ACTION\n");
- msg.body.reg_frame.u8Regid = ACTION_FRM_IDX;
+ msg.body.reg_frame.reg_id = ACTION_FRM_IDX;
break;
case PROBE_REQ:
PRINT_D(HOSTINF_DBG, "PROBE REQ\n");
- msg.body.reg_frame.u8Regid = PROBE_REQ_IDX;
+ msg.body.reg_frame.reg_id = PROBE_REQ_IDX;
break;
default:
PRINT_D(HOSTINF_DBG, "Not valid frame type\n");
break;
}
- msg.body.reg_frame.u16FrameType = u16FrameType;
- msg.body.reg_frame.bReg = bReg;
- msg.drv = hif_drv;
+ msg.body.reg_frame.frame_type = u16FrameType;
+ msg.body.reg_frame.reg = bReg;
+ msg.vif = vif;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result)
@@ -4441,13 +4160,13 @@ s32 host_int_frame_register(struct host_if_drv *hif_drv, u16 u16FrameType, bool
return result;
}
-s32 host_int_add_beacon(struct host_if_drv *hif_drv, u32 u32Interval,
- u32 u32DTIMPeriod, u32 u32HeadLen, u8 *pu8Head,
- u32 u32TailLen, u8 *pu8Tail)
+s32 wilc_add_beacon(struct wilc_vif *vif, u32 u32Interval, u32 u32DTIMPeriod,
+ u32 u32HeadLen, u8 *pu8Head, u32 u32TailLen, u8 *pu8Tail)
{
s32 result = 0;
struct host_if_msg msg;
struct beacon_attr *pstrSetBeaconParam = &msg.body.beacon_info;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
PRINT_ER("driver is null\n");
@@ -4459,7 +4178,7 @@ s32 host_int_add_beacon(struct host_if_drv *hif_drv, u32 u32Interval,
PRINT_D(HOSTINF_DBG, "Setting adding beacon message queue params\n");
msg.id = HOST_IF_MSG_ADD_BEACON;
- msg.drv = hif_drv;
+ msg.vif = vif;
pstrSetBeaconParam->interval = u32Interval;
pstrSetBeaconParam->dtim_period = u32DTIMPeriod;
pstrSetBeaconParam->head_len = u32HeadLen;
@@ -4495,10 +4214,11 @@ ERRORHANDLER:
return result;
}
-s32 host_int_del_beacon(struct host_if_drv *hif_drv)
+int wilc_del_beacon(struct wilc_vif *vif)
{
- s32 result = 0;
+ int result = 0;
struct host_if_msg msg;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
PRINT_ER("driver is null\n");
@@ -4506,7 +4226,7 @@ s32 host_int_del_beacon(struct host_if_drv *hif_drv)
}
msg.id = HOST_IF_MSG_DEL_BEACON;
- msg.drv = hif_drv;
+ msg.vif = vif;
PRINT_D(HOSTINF_DBG, "Setting deleting beacon message queue params\n");
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
@@ -4516,12 +4236,12 @@ s32 host_int_del_beacon(struct host_if_drv *hif_drv)
return result;
}
-s32 host_int_add_station(struct host_if_drv *hif_drv,
- struct add_sta_param *pstrStaParams)
+int wilc_add_station(struct wilc_vif *vif, struct add_sta_param *sta_param)
{
- s32 result = 0;
+ int result = 0;
struct host_if_msg msg;
- struct add_sta_param *pstrAddStationMsg = &msg.body.add_sta_info;
+ struct add_sta_param *add_sta_info = &msg.body.add_sta_info;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
PRINT_ER("driver is null\n");
@@ -4533,17 +4253,15 @@ s32 host_int_add_station(struct host_if_drv *hif_drv,
PRINT_D(HOSTINF_DBG, "Setting adding station message queue params\n");
msg.id = HOST_IF_MSG_ADD_STATION;
- msg.drv = hif_drv;
-
- memcpy(pstrAddStationMsg, pstrStaParams, sizeof(struct add_sta_param));
- if (pstrAddStationMsg->u8NumRates > 0) {
- u8 *rates = kmalloc(pstrAddStationMsg->u8NumRates, GFP_KERNEL);
-
- if (!rates)
+ msg.vif = vif;
+
+ memcpy(add_sta_info, sta_param, sizeof(struct add_sta_param));
+ if (add_sta_info->rates_len > 0) {
+ add_sta_info->rates = kmemdup(sta_param->rates,
+ add_sta_info->rates_len,
+ GFP_KERNEL);
+ if (!add_sta_info->rates)
return -ENOMEM;
-
- memcpy(rates, pstrStaParams->pu8Rates, pstrAddStationMsg->u8NumRates);
- pstrAddStationMsg->pu8Rates = rates;
}
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
@@ -4552,11 +4270,12 @@ s32 host_int_add_station(struct host_if_drv *hif_drv,
return result;
}
-s32 host_int_del_station(struct host_if_drv *hif_drv, const u8 *pu8MacAddr)
+int wilc_del_station(struct wilc_vif *vif, const u8 *mac_addr)
{
- s32 result = 0;
+ int result = 0;
struct host_if_msg msg;
- struct del_sta *pstrDelStationMsg = &msg.body.del_sta_info;
+ struct del_sta *del_sta_info = &msg.body.del_sta_info;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
PRINT_ER("driver is null\n");
@@ -4568,12 +4287,12 @@ s32 host_int_del_station(struct host_if_drv *hif_drv, const u8 *pu8MacAddr)
PRINT_D(HOSTINF_DBG, "Setting deleting station message queue params\n");
msg.id = HOST_IF_MSG_DEL_STATION;
- msg.drv = hif_drv;
+ msg.vif = vif;
- if (!pu8MacAddr)
- eth_broadcast_addr(pstrDelStationMsg->mac_addr);
+ if (!mac_addr)
+ eth_broadcast_addr(del_sta_info->mac_addr);
else
- memcpy(pstrDelStationMsg->mac_addr, pu8MacAddr, ETH_ALEN);
+ memcpy(del_sta_info->mac_addr, mac_addr, ETH_ALEN);
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result)
@@ -4581,12 +4300,12 @@ s32 host_int_del_station(struct host_if_drv *hif_drv, const u8 *pu8MacAddr)
return result;
}
-s32 host_int_del_allstation(struct host_if_drv *hif_drv,
- u8 pu8MacAddr[][ETH_ALEN])
+s32 wilc_del_allstation(struct wilc_vif *vif, u8 pu8MacAddr[][ETH_ALEN])
{
s32 result = 0;
struct host_if_msg msg;
struct del_all_sta *pstrDelAllStationMsg = &msg.body.del_all_sta_info;
+ struct host_if_drv *hif_drv = vif->hif_drv;
u8 au8Zero_Buff[ETH_ALEN] = {0};
u32 i;
u8 u8AssocNumb = 0;
@@ -4601,7 +4320,7 @@ s32 host_int_del_allstation(struct host_if_drv *hif_drv,
PRINT_D(HOSTINF_DBG, "Setting deauthenticating station message queue params\n");
msg.id = HOST_IF_MSG_DEL_ALL_STA;
- msg.drv = hif_drv;
+ msg.vif = vif;
for (i = 0; i < MAX_NUM_STA; i++) {
if (memcmp(pu8MacAddr[i], au8Zero_Buff, ETH_ALEN)) {
@@ -4632,12 +4351,13 @@ s32 host_int_del_allstation(struct host_if_drv *hif_drv,
return result;
}
-s32 host_int_edit_station(struct host_if_drv *hif_drv,
- struct add_sta_param *pstrStaParams)
+s32 wilc_edit_station(struct wilc_vif *vif,
+ struct add_sta_param *pstrStaParams)
{
s32 result = 0;
struct host_if_msg msg;
struct add_sta_param *pstrAddStationMsg = &msg.body.add_sta_info;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
PRINT_ER("driver is null\n");
@@ -4649,17 +4369,18 @@ s32 host_int_edit_station(struct host_if_drv *hif_drv,
memset(&msg, 0, sizeof(struct host_if_msg));
msg.id = HOST_IF_MSG_EDIT_STATION;
- msg.drv = hif_drv;
+ msg.vif = vif;
memcpy(pstrAddStationMsg, pstrStaParams, sizeof(struct add_sta_param));
- if (pstrAddStationMsg->u8NumRates > 0) {
- u8 *rates = kmalloc(pstrAddStationMsg->u8NumRates, GFP_KERNEL);
+ if (pstrAddStationMsg->rates_len > 0) {
+ u8 *rates = kmalloc(pstrAddStationMsg->rates_len, GFP_KERNEL);
if (!rates)
return -ENOMEM;
- memcpy(rates, pstrStaParams->pu8Rates, pstrAddStationMsg->u8NumRates);
- pstrAddStationMsg->pu8Rates = rates;
+ memcpy(rates, pstrStaParams->rates,
+ pstrAddStationMsg->rates_len);
+ pstrAddStationMsg->rates = rates;
}
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
@@ -4669,13 +4390,12 @@ s32 host_int_edit_station(struct host_if_drv *hif_drv,
return result;
}
-s32 host_int_set_power_mgmt(struct host_if_drv *hif_drv,
- bool bIsEnabled,
- u32 u32Timeout)
+s32 wilc_set_power_mgmt(struct wilc_vif *vif, bool bIsEnabled, u32 u32Timeout)
{
s32 result = 0;
struct host_if_msg msg;
struct power_mgmt_param *pstrPowerMgmtParam = &msg.body.pwr_mgmt_info;
+ struct host_if_drv *hif_drv = vif->hif_drv;
PRINT_INFO(HOSTINF_DBG, "\n\n>> Setting PS to %d <<\n\n", bIsEnabled);
@@ -4689,7 +4409,7 @@ s32 host_int_set_power_mgmt(struct host_if_drv *hif_drv,
memset(&msg, 0, sizeof(struct host_if_msg));
msg.id = HOST_IF_MSG_POWER_MGMT;
- msg.drv = hif_drv;
+ msg.vif = vif;
pstrPowerMgmtParam->enabled = bIsEnabled;
pstrPowerMgmtParam->timeout = u32Timeout;
@@ -4700,13 +4420,13 @@ s32 host_int_set_power_mgmt(struct host_if_drv *hif_drv,
return result;
}
-s32 host_int_setup_multicast_filter(struct host_if_drv *hif_drv,
- bool bIsEnabled,
- u32 u32count)
+s32 wilc_setup_multicast_filter(struct wilc_vif *vif, bool bIsEnabled,
+ u32 u32count)
{
s32 result = 0;
struct host_if_msg msg;
struct set_multicast *pstrMulticastFilterParam = &msg.body.multicast_info;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
PRINT_ER("driver is null\n");
@@ -4718,7 +4438,7 @@ s32 host_int_setup_multicast_filter(struct host_if_drv *hif_drv,
memset(&msg, 0, sizeof(struct host_if_msg));
msg.id = HOST_IF_MSG_SET_MULTICAST_FILTER;
- msg.drv = hif_drv;
+ msg.vif = vif;
pstrMulticastFilterParam->enabled = bIsEnabled;
pstrMulticastFilterParam->cnt = u32count;
@@ -4886,7 +4606,7 @@ static void *host_int_ParseJoinBssParam(tstrNetworkInfo *ptstrNetworkInfo)
return (void *)pNewJoinBssParam;
}
-void host_int_freeJoinParams(void *pJoinParams)
+void wilc_free_join_params(void *pJoinParams)
{
if ((struct bss_param *)pJoinParams)
kfree((struct bss_param *)pJoinParams);
@@ -4894,41 +4614,12 @@ void host_int_freeJoinParams(void *pJoinParams)
PRINT_ER("Unable to FREE null pointer\n");
}
-s32 host_int_delBASession(struct host_if_drv *hif_drv, char *pBSSID, char TID)
-{
- s32 result = 0;
- struct host_if_msg msg;
- struct ba_session_info *pBASessionInfo = &msg.body.session_info;
-
- if (!hif_drv) {
- PRINT_ER("driver is null\n");
- return -EFAULT;
- }
-
- memset(&msg, 0, sizeof(struct host_if_msg));
-
- msg.id = HOST_IF_MSG_DEL_BA_SESSION;
-
- memcpy(pBASessionInfo->au8Bssid, pBSSID, ETH_ALEN);
- pBASessionInfo->u8Ted = TID;
- msg.drv = hif_drv;
-
- result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
- if (result)
- PRINT_ER("wilc_mq_send fail\n");
-
- down(&hif_sema_wait_response);
-
- return result;
-}
-
-s32 host_int_del_All_Rx_BASession(struct host_if_drv *hif_drv,
- char *pBSSID,
- char TID)
+s32 wilc_del_all_rx_ba_session(struct wilc_vif *vif, char *pBSSID, char TID)
{
s32 result = 0;
struct host_if_msg msg;
struct ba_session_info *pBASessionInfo = &msg.body.session_info;
+ struct host_if_drv *hif_drv = vif->hif_drv;
if (!hif_drv) {
PRINT_ER("driver is null\n");
@@ -4939,9 +4630,9 @@ s32 host_int_del_All_Rx_BASession(struct host_if_drv *hif_drv,
msg.id = HOST_IF_MSG_DEL_ALL_RX_BA_SESSIONS;
- memcpy(pBASessionInfo->au8Bssid, pBSSID, ETH_ALEN);
- pBASessionInfo->u8Ted = TID;
- msg.drv = hif_drv;
+ memcpy(pBASessionInfo->bssid, pBSSID, ETH_ALEN);
+ pBASessionInfo->tid = TID;
+ msg.vif = vif;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
if (result)
@@ -4952,10 +4643,11 @@ s32 host_int_del_All_Rx_BASession(struct host_if_drv *hif_drv,
return result;
}
-s32 host_int_setup_ipaddress(struct host_if_drv *hif_drv, u8 *u16ipadd, u8 idx)
+s32 wilc_setup_ipaddress(struct wilc_vif *vif, u8 *u16ipadd, u8 idx)
{
s32 result = 0;
struct host_if_msg msg;
+ struct host_if_drv *hif_drv = vif->hif_drv;
return 0;
@@ -4969,7 +4661,7 @@ s32 host_int_setup_ipaddress(struct host_if_drv *hif_drv, u8 *u16ipadd, u8 idx)
msg.id = HOST_IF_MSG_SET_IPADDRESS;
msg.body.ip_info.ip_addr = u16ipadd;
- msg.drv = hif_drv;
+ msg.vif = vif;
msg.body.ip_info.idx = idx;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
@@ -4979,7 +4671,9 @@ s32 host_int_setup_ipaddress(struct host_if_drv *hif_drv, u8 *u16ipadd, u8 idx)
return result;
}
-s32 host_int_get_ipaddress(struct host_if_drv *hif_drv, u8 *u16ipadd, u8 idx)
+static s32 host_int_get_ipaddress(struct wilc_vif *vif,
+ struct host_if_drv *hif_drv,
+ u8 *u16ipadd, u8 idx)
{
s32 result = 0;
struct host_if_msg msg;
@@ -4994,7 +4688,7 @@ s32 host_int_get_ipaddress(struct host_if_drv *hif_drv, u8 *u16ipadd, u8 idx)
msg.id = HOST_IF_MSG_GET_IPADDRESS;
msg.body.ip_info.ip_addr = u16ipadd;
- msg.drv = hif_drv;
+ msg.vif = vif;
msg.body.ip_info.idx = idx;
result = wilc_mq_send(&hif_msg_q, &msg, sizeof(struct host_if_msg));
diff --git a/drivers/staging/wilc1000/host_interface.h b/drivers/staging/wilc1000/host_interface.h
index b854db5ac932..8faac27002e9 100644
--- a/drivers/staging/wilc1000/host_interface.h
+++ b/drivers/staging/wilc1000/host_interface.h
@@ -1,12 +1,3 @@
-/*!
- * @file host_interface.h
- * @brief File containg host interface APIs
- * @author zsalah
- * @sa host_interface.c
- * @date 8 March 2012
- * @version 1.0
- */
-
#ifndef HOST_INT_H
#define HOST_INT_H
@@ -19,8 +10,12 @@
#define STATION_MODE 0x02
#define GO_MODE 0x03
#define CLIENT_MODE 0x04
+#define ACTION 0xD0
+#define PROBE_REQ 0x40
+#define PROBE_RESP 0x50
-
+#define ACTION_FRM_IDX 0
+#define PROBE_REQ_IDX 1
#define MAX_NUM_STA 9
#define ACTIVE_SCAN_TIME 10
#define PASSIVE_SCAN_TIME 1200
@@ -58,11 +53,11 @@
#define NUM_CONCURRENT_IFC 2
struct rf_info {
- u8 u8LinkSpeed;
- s8 s8RSSI;
- u32 u32TxCount;
- u32 u32RxCount;
- u32 u32TxFailureCount;
+ u8 link_speed;
+ s8 rssi;
+ u32 tx_cnt;
+ u32 rx_cnt;
+ u32 tx_fail_cnt;
};
enum host_if_state {
@@ -168,36 +163,23 @@ enum conn_event {
enum KEY_TYPE {
WEP,
- WPARxGtk,
- WPAPtk,
+ WPA_RX_GTK,
+ WPA_PTK,
PMKSA,
};
-
-/*Scan callBack function definition*/
typedef void (*wilc_scan_result)(enum scan_event, tstrNetworkInfo *,
void *, void *);
-/*Connect callBack function definition*/
typedef void (*wilc_connect_result)(enum conn_event,
tstrConnectInfo *,
u8,
tstrDisconnectNotifInfo *,
void *);
-typedef void (*wilc_remain_on_chan_expired)(void *, u32); /*Remain on channel expiration callback function*/
-typedef void (*wilc_remain_on_chan_ready)(void *); /*Remain on channel callback function*/
+typedef void (*wilc_remain_on_chan_expired)(void *, u32);
+typedef void (*wilc_remain_on_chan_ready)(void *);
-/*!
- * @struct rcvd_net_info
- * @brief Structure to hold Received Asynchronous Network info
- * @details
- * @todo
- * @sa
- * @author Mostafa Abu Bakr
- * @date 25 March 2012
- * @version 1.0
- */
struct rcvd_net_info {
u8 *buffer;
u32 len;
@@ -214,29 +196,23 @@ struct hidden_network {
};
struct user_scan_req {
- /* Scan user call back function */
- wilc_scan_result pfUserScanResult;
-
- /* User specific parameter to be delivered through the Scan User Callback function */
- void *u32UserScanPvoid;
-
- u32 u32RcvdChCount;
- struct found_net_info astrFoundNetworkInfo[MAX_NUM_SCANNED_NETWORKS];
+ wilc_scan_result scan_result;
+ void *arg;
+ u32 rcvd_ch_cnt;
+ struct found_net_info net_info[MAX_NUM_SCANNED_NETWORKS];
};
struct user_conn_req {
u8 *pu8bssid;
u8 *pu8ssid;
u8 u8security;
- enum AUTHTYPE tenuAuth_type;
- size_t ssidLen;
- u8 *pu8ConnReqIEs;
- size_t ConnReqIEsLen;
- /* Connect user call back function */
- wilc_connect_result pfUserConnectResult;
- bool IsHTCapable;
- /* User specific parameter to be delivered through the Connect User Callback function */
- void *u32UserConnectPvoid;
+ enum AUTHTYPE auth_type;
+ size_t ssid_len;
+ u8 *ies;
+ size_t ies_len;
+ wilc_connect_result conn_result;
+ bool ht_capable;
+ void *arg;
};
struct drv_handler {
@@ -256,875 +232,155 @@ struct get_mac_addr {
};
struct ba_session_info {
- u8 au8Bssid[ETH_ALEN];
- u8 u8Ted;
- u16 u16BufferSize;
- u16 u16SessionTimeout;
+ u8 bssid[ETH_ALEN];
+ u8 tid;
+ u16 buf_size;
+ u16 time_out;
};
struct remain_ch {
- u16 u16Channel;
+ u16 ch;
u32 u32duration;
- wilc_remain_on_chan_expired pRemainOnChanExpired;
- wilc_remain_on_chan_ready pRemainOnChanReady;
- void *pVoid;
- u32 u32ListenSessionID;
+ wilc_remain_on_chan_expired expired;
+ wilc_remain_on_chan_ready ready;
+ void *arg;
+ u32 id;
};
struct reg_frame {
- bool bReg;
- u16 u16FrameType;
- u8 u8Regid;
+ bool reg;
+ u16 frame_type;
+ u8 reg_id;
};
-
-#define ACTION 0xD0
-#define PROBE_REQ 0x40
-#define PROBE_RESP 0x50
-#define ACTION_FRM_IDX 0
-#define PROBE_REQ_IDX 1
-
-
enum p2p_listen_state {
P2P_IDLE,
P2P_LISTEN,
P2P_GRP_FORMATION
};
+struct wilc;
struct host_if_drv {
struct user_scan_req usr_scan_req;
struct user_conn_req usr_conn_req;
struct remain_ch remain_on_ch;
u8 remain_on_ch_pending;
- u64 u64P2p_MgmtTimeout;
- u8 u8P2PConnect;
+ u64 p2p_timeout;
+ u8 p2p_connect;
+
+ enum host_if_state hif_state;
- enum host_if_state enuHostIFstate;
+ u8 assoc_bssid[ETH_ALEN];
+ struct cfg_param_val cfg_values;
- u8 au8AssociatedBSSID[ETH_ALEN];
- struct cfg_param_val strCfgValues;
-/* semaphores */
- struct semaphore gtOsCfgValuesSem;
- struct semaphore hSemTestKeyBlock;
+ struct semaphore sem_cfg_values;
+ struct semaphore sem_test_key_block;
+ struct semaphore sem_test_disconn_block;
+ struct semaphore sem_get_rssi;
+ struct semaphore sem_get_link_speed;
+ struct semaphore sem_get_chnl;
+ struct semaphore sem_inactive_time;
- struct semaphore hSemTestDisconnectBlock;
- struct semaphore hSemGetRSSI;
- struct semaphore hSemGetLINKSPEED;
- struct semaphore hSemGetCHNL;
- struct semaphore hSemInactiveTime;
-/* timer handlers */
- struct timer_list hScanTimer;
- struct timer_list hConnectTimer;
- struct timer_list hRemainOnChannel;
+ struct timer_list scan_timer;
+ struct timer_list connect_timer;
+ struct timer_list remain_on_ch_timer;
bool IFC_UP;
};
struct add_sta_param {
- u8 au8BSSID[ETH_ALEN];
- u16 u16AssocID;
- u8 u8NumRates;
- const u8 *pu8Rates;
- bool bIsHTSupported;
- u16 u16HTCapInfo;
- u8 u8AmpduParams;
- u8 au8SuppMCsSet[16];
- u16 u16HTExtParams;
- u32 u32TxBeamformingCap;
- u8 u8ASELCap;
- u16 u16FlagsMask; /*<! Determines which of u16FlagsSet were changed>*/
- u16 u16FlagsSet; /*<! Decoded according to tenuWILC_StaFlag */
+ u8 bssid[ETH_ALEN];
+ u16 aid;
+ u8 rates_len;
+ const u8 *rates;
+ bool ht_supported;
+ u16 ht_capa_info;
+ u8 ht_ampdu_params;
+ u8 ht_supp_mcs_set[16];
+ u16 ht_ext_params;
+ u32 ht_tx_bf_cap;
+ u8 ht_ante_sel;
+ u16 flags_mask;
+ u16 flags_set;
};
-/*****************************************************************************/
-/* */
-/* Host Interface API */
-/* */
-/*****************************************************************************/
-
-/**
- * @brief removes wpa/wpa2 keys
- * @details only in BSS STA mode if External Supplicant support is enabled.
- * removes all WPA/WPA2 station key entries from MAC hardware.
- * @param[in,out] handle to the wifi driver
- * @param[in] 6 bytes of Station Adress in the station entry table
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-s32 host_int_remove_key(struct host_if_drv *hWFIDrv, const u8 *pu8StaAddress);
-/**
- * @brief removes WEP key
- * @details valid only in BSS STA mode if External Supplicant support is enabled.
- * remove a WEP key entry from MAC HW.
- * The BSS Station automatically finds the index of the entry using its
- * BSS ID and removes that entry from the MAC hardware.
- * @param[in,out] handle to the wifi driver
- * @param[in] 6 bytes of Station Adress in the station entry table
- * @return Error code indicating success/failure
- * @note NO need for the STA add since it is not used for processing
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-int host_int_remove_wep_key(struct host_if_drv *wfi_drv, u8 index);
-/**
- * @brief sets WEP deafault key
- * @details Sets the index of the WEP encryption key in use,
- * in the key table
- * @param[in,out] handle to the wifi driver
- * @param[in] key index ( 0, 1, 2, 3)
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-int host_int_set_wep_default_key(struct host_if_drv *hif_drv, u8 index);
-
-/**
- * @brief sets WEP deafault key
- * @details valid only in BSS STA mode if External Supplicant support is enabled.
- * sets WEP key entry into MAC hardware when it receives the
- * corresponding request from NDIS.
- * @param[in,out] handle to the wifi driver
- * @param[in] message containing WEP Key in the following format
- *|---------------------------------------|
- *|Key ID Value | Key Length | Key |
- *|-------------|------------|------------|
- | 1byte | 1byte | Key Length |
- ||---------------------------------------|
- |
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-int host_int_add_wep_key_bss_sta(struct host_if_drv *hif_drv,
- const u8 *key, u8 len, u8 index);
-/**
- * @brief host_int_add_wep_key_bss_ap
- * @details valid only in AP mode if External Supplicant support is enabled.
- * sets WEP key entry into MAC hardware when it receives the
- * corresponding request from NDIS.
- * @param[in,out] handle to the wifi driver
- *
- *
- * @return Error code indicating success/failure
- * @note
- * @author mdaftedar
- * @date 28 Feb 2013
- * @version 1.0
- */
-int host_int_add_wep_key_bss_ap(struct host_if_drv *hif_drv,
- const u8 *key, u8 len, u8 index, u8 mode,
- enum AUTHTYPE auth_type);
-
-/**
- * @brief adds ptk Key
- * @details
- * @param[in,out] handle to the wifi driver
- * @param[in] message containing PTK Key in the following format
- *|-------------------------------------------------------------------------|
- *|Sta Adress | Key Length | Temporal Key | Rx Michael Key |Tx Michael Key |
- *|-----------|------------|---------------|----------------|---------------|
- | 6 bytes | 1byte | 16 bytes | 8 bytes | 8 bytes |
- ||-------------------------------------------------------------------------|
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-s32 host_int_add_ptk(struct host_if_drv *hWFIDrv, const u8 *pu8Ptk, u8 u8PtkKeylen,
- const u8 *mac_addr, const u8 *pu8RxMic, const u8 *pu8TxMic, u8 mode, u8 u8Ciphermode, u8 u8Idx);
-
-/**
- * @brief host_int_get_inactive_time
- * @details
- * @param[in,out] handle to the wifi driver
- * @param[in] message containing inactive time
- *
- * @return Error code indicating success/failure
- * @note
- * @author mdaftedar
- * @date 15 April 2013
- * @version 1.0
- */
-s32 host_int_get_inactive_time(struct host_if_drv *hWFIDrv, const u8 *mac, u32 *pu32InactiveTime);
-
-/**
- * @brief adds Rx GTk Key
- * @details
- * @param[in,out] handle to the wifi driver
- * @param[in] message containing Rx GTK Key in the following format
- *|----------------------------------------------------------------------------|
- *|Sta Address | Key RSC | KeyID | Key Length | Temporal Key | Rx Michael Key |
- *|------------|---------|-------|------------|---------------|----------------|
- | 6 bytes | 8 byte |1 byte | 1 byte | 16 bytes | 8 bytes |
- ||----------------------------------------------------------------------------|
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-s32 host_int_add_rx_gtk(struct host_if_drv *hWFIDrv, const u8 *pu8RxGtk, u8 u8GtkKeylen,
- u8 u8KeyIdx, u32 u32KeyRSClen, const u8 *KeyRSC,
- const u8 *pu8RxMic, const u8 *pu8TxMic, u8 mode, u8 u8Ciphermode);
-
-
-/**
- * @brief adds Tx GTk Key
- * @details
- * @param[in,out] handle to the wifi driver
- * @param[in] message containing Tx GTK Key in the following format
- *|----------------------------------------------------|
- | KeyID | Key Length | Temporal Key | Tx Michael Key |
- ||-------|------------|--------------|----------------|
- ||1 byte | 1 byte | 16 bytes | 8 bytes |
- ||----------------------------------------------------|
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-s32 host_int_add_tx_gtk(struct host_if_drv *hWFIDrv, u8 u8KeyLen, u8 *pu8TxGtk, u8 u8KeyIdx);
-
-/**
- * @brief caches the pmkid
- * @details valid only in BSS STA mode if External Supplicant
- * support is enabled. This Function sets the PMKID in firmware
- * when host drivr receives the corresponding request from NDIS.
- * The firmware then includes theset PMKID in the appropriate
- * management frames
- * @param[in,out] handle to the wifi driver
- * @param[in] message containing PMKID Info in the following format
- *|-----------------------------------------------------------------|
- *|NumEntries | BSSID[1] | PMKID[1] | ... | BSSID[K] | PMKID[K] |
- *|-----------|------------|----------|-------|----------|----------|
- | 1 | 6 | 16 | ... | 6 | 16 |
- ||-----------------------------------------------------------------|
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-
-s32 host_int_set_pmkid_info(struct host_if_drv *hWFIDrv, struct host_if_pmkid_attr *pu8PmkidInfoArray);
-/**
- * @brief gets the cached the pmkid info
- * @details valid only in BSS STA mode if External Supplicant
- * support is enabled. This Function sets the PMKID in firmware
- * when host drivr receives the corresponding request from NDIS.
- * The firmware then includes theset PMKID in the appropriate
- * management frames
- * @param[in,out] handle to the wifi driver,
- *
- * message containing PMKID Info in the following format
- *|-----------------------------------------------------------------|
- *|NumEntries | BSSID[1] | PMKID[1] | ... | BSSID[K] | PMKID[K] |
- *|-----------|------------|----------|-------|----------|----------|
- | 1 | 6 | 16 | ... | 6 | 16 |
- ||-----------------------------------------------------------------|
- * @param[in]
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-
-s32 host_int_get_pmkid_info(struct host_if_drv *hWFIDrv, u8 *pu8PmkidInfoArray,
- u32 u32PmkidInfoLen);
-
-/**
- * @brief sets the pass phrase
- * @details AP/STA mode. This function gives the pass phrase used to
- * generate the Pre-Shared Key when WPA/WPA2 is enabled
- * The length of the field can vary from 8 to 64 bytes,
- * the lower layer should get the
- * @param[in,out] handle to the wifi driver,
- * @param[in] String containing PSK
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-s32 host_int_set_RSNAConfigPSKPassPhrase(struct host_if_drv *hWFIDrv, u8 *pu8PassPhrase,
- u8 u8Psklength);
-/**
- * @brief gets the pass phrase
- * @details AP/STA mode. This function gets the pass phrase used to
- * generate the Pre-Shared Key when WPA/WPA2 is enabled
- * The length of the field can vary from 8 to 64 bytes,
- * the lower layer should get the
- * @param[in,out] handle to the wifi driver,
- * String containing PSK
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-s32 host_int_get_RSNAConfigPSKPassPhrase(struct host_if_drv *hWFIDrv,
- u8 *pu8PassPhrase, u8 u8Psklength);
-
-/**
- * @brief gets mac address
- * @details
- * @param[in,out] handle to the wifi driver,
- *
- * @return Error code indicating success/failure
- * @note
- * @author mdaftedar
- * @date 19 April 2012
- * @version 1.0
- */
-s32 host_int_get_MacAddress(struct host_if_drv *hWFIDrv, u8 *pu8MacAddress);
-
-/**
- * @brief sets mac address
- * @details
- * @param[in,out] handle to the wifi driver,
- *
- * @return Error code indicating success/failure
- * @note
- * @author mabubakr
- * @date 16 July 2012
- * @version 1.0
- */
-s32 host_int_set_MacAddress(struct host_if_drv *hWFIDrv, u8 *pu8MacAddress);
-
-/**
- * @brief wait until msg q is empty
- * @details
- * @param[in,out]
- *
- * @return Error code indicating success/failure
- * @note
- * @author asobhy
- * @date 19 march 2014
- * @version 1.0
- */
-int host_int_wait_msg_queue_idle(void);
-
-/**
- * @brief sets a start scan request
- * @details
- * @param[in,out] handle to the wifi driver,
- * @param[in] Scan Source one of the following values
- * DEFAULT_SCAN 0
- * USER_SCAN BIT0
- * OBSS_PERIODIC_SCAN BIT1
- * OBSS_ONETIME_SCAN BIT2
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-
-s32 host_int_set_start_scan_req(struct host_if_drv *hWFIDrv, u8 scanSource);
-/**
- * @brief gets scan source of the last scan
- * @details
- * @param[in,out] handle to the wifi driver,
- * Scan Source one of the following values
- * DEFAULT_SCAN 0
- * USER_SCAN BIT0
- * OBSS_PERIODIC_SCAN BIT1
- * OBSS_ONETIME_SCAN BIT2
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-s32 host_int_get_start_scan_req(struct host_if_drv *hWFIDrv, u8 *pu8ScanSource);
-
-/**
- * @brief sets a join request
- * @details
- * @param[in,out] handle to the wifi driver,
- * @param[in] Index of the bss descriptor
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-
-s32 host_int_set_join_req(struct host_if_drv *hWFIDrv, u8 *pu8bssid,
- const u8 *pu8ssid, size_t ssidLen,
- const u8 *pu8IEs, size_t IEsLen,
- wilc_connect_result pfConnectResult, void *pvUserArg,
- u8 u8security, enum AUTHTYPE tenuAuth_type,
- u8 u8channel,
- void *pJoinParams);
-
-/**
- * @brief Flush a join request parameters to FW, but actual connection
- * @details The function is called in situation where WILC is connected to AP and
- * required to switch to hybrid FW for P2P connection
- * @param[in] handle to the wifi driver,
- * @return Error code indicating success/failure
- * @note
- * @author Amr Abdel-Moghny
- * @date 19 DEC 2013
- * @version 8.0
- */
-
-s32 host_int_flush_join_req(struct host_if_drv *hWFIDrv);
-
-
-/**
- * @brief disconnects from the currently associated network
- * @details
- * @param[in,out] handle to the wifi driver,
- * @param[in] Reason Code of the Disconnection
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-s32 host_int_disconnect(struct host_if_drv *hWFIDrv, u16 u16ReasonCode);
-
-/**
- * @brief disconnects a sta
- * @details
- * @param[in,out] handle to the wifi driver,
- * @param[in] Association Id of the station to be disconnected
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-s32 host_int_disconnect_station(struct host_if_drv *hWFIDrv, u8 assoc_id);
-/**
- * @brief gets a Association request info
- * @details
- * @param[in,out] handle to the wifi driver,
- * Message containg assoc. req info in the following format
- * ------------------------------------------------------------------------
- | Management Frame Format |
- ||-------------------------------------------------------------------|
- ||Frame Control|Duration|DA|SA|BSSID|Sequence Control|Frame Body|FCS |
- ||-------------|--------|--|--|-----|----------------|----------|----|
- | 2 |2 |6 |6 |6 | 2 |0 - 2312 | 4 |
- ||-------------------------------------------------------------------|
- | |
- | Association Request Frame - Frame Body |
- ||-------------------------------------------------------------------|
- | Capability Information | Listen Interval | SSID | Supported Rates |
- ||------------------------|-----------------|------|-----------------|
- | 2 | 2 | 2-34 | 3-10 |
- | ---------------------------------------------------------------------
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-
-s32 host_int_get_assoc_req_info(struct host_if_drv *hWFIDrv, u8 *pu8AssocReqInfo,
- u32 u32AssocReqInfoLen);
-/**
- * @brief gets a Association Response info
- * @details
- * @param[in,out] handle to the wifi driver,
- * Message containg assoc. resp info
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-
-s32 host_int_get_assoc_res_info(struct host_if_drv *hWFIDrv, u8 *pu8AssocRespInfo,
- u32 u32MaxAssocRespInfoLen, u32 *pu32RcvdAssocRespInfoLen);
-/**
- * @brief gets a Association Response info
- * @details Valid only in STA mode. This function gives the RSSI
- * values observed in all the channels at the time of scanning.
- * The length of the field is 1 greater that the total number of
- * channels supported. Byte 0 contains the number of channels while
- * each of Byte N contains the observed RSSI value for the channel index N.
- * @param[in,out] handle to the wifi driver,
- * array of scanned channels' RSSI
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-s32 host_int_get_rx_power_level(struct host_if_drv *hWFIDrv, u8 *pu8RxPowerLevel,
- u32 u32RxPowerLevelLen);
-
-/**
- * @brief sets a channel
- * @details
- * @param[in,out] handle to the wifi driver,
- * @param[in] Index of the channel to be set
- *|-------------------------------------------------------------------|
- | CHANNEL1 CHANNEL2 .... CHANNEL14 |
- | Input: 1 2 14 |
- ||-------------------------------------------------------------------|
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-int host_int_set_mac_chnl_num(struct host_if_drv *wfi_drv, u8 channel);
-
-/**
- * @brief gets the current channel index
- * @details
- * @param[in,out] handle to the wifi driver,
- * current channel index
- *|-----------------------------------------------------------------------|
- | CHANNEL1 CHANNEL2 .... CHANNEL14 |
- | Input: 1 2 14 |
- ||-----------------------------------------------------------------------|
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-s32 host_int_get_host_chnl_num(struct host_if_drv *hWFIDrv, u8 *pu8ChNo);
-/**
- * @brief gets the sta rssi
- * @details gets the currently maintained RSSI value for the station.
- * The received signal strength value in dB.
- * The range of valid values is -128 to 0.
- * @param[in,out] handle to the wifi driver,
- * rssi value in dB
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-s32 host_int_get_rssi(struct host_if_drv *hWFIDrv, s8 *ps8Rssi);
-s32 host_int_get_link_speed(struct host_if_drv *hWFIDrv, s8 *ps8lnkspd);
-/**
- * @brief scans a set of channels
- * @details
- * @param[in,out] handle to the wifi driver,
- * @param[in] Scan source
- * Scan Type PASSIVE_SCAN = 0,
- * ACTIVE_SCAN = 1
- * Channels Array
- * Channels Array length
- * Scan Callback function
- * User Argument to be delivered back through the Scan Cllback function
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-s32 host_int_scan(struct host_if_drv *hWFIDrv, u8 u8ScanSource,
- u8 u8ScanType, u8 *pu8ChnlFreqList,
- u8 u8ChnlListLen, const u8 *pu8IEs,
- size_t IEsLen, wilc_scan_result ScanResult,
- void *pvUserArg,
- struct hidden_network *pstrHiddenNetwork);
-/**
- * @brief sets configuration wids values
- * @details
- * @param[in,out] handle to the wifi driver,
- * @param[in] WID, WID value
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-s32 hif_set_cfg(struct host_if_drv *hWFIDrv, struct cfg_param_val *pstrCfgParamVal);
-
-/**
- * @brief gets configuration wids values
- * @details
- * @param[in,out] handle to the wifi driver,
- * WID value
- * @param[in] WID,
- * @return Error code indicating success/failure
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-s32 hif_get_cfg(struct host_if_drv *hWFIDrv, u16 u16WID, u16 *pu16WID_Value);
-/*****************************************************************************/
-/* Notification Functions */
-/*****************************************************************************/
-/**
- * @brief host interface initialization function
- * @details
- * @param[in,out] handle to the wifi driver,
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-s32 host_int_init(struct net_device *dev, struct host_if_drv **phWFIDrv);
-
-/**
- * @brief host interface initialization function
- * @details
- * @param[in,out] handle to the wifi driver,
- * @note
- * @author zsalah
- * @date 8 March 2012
- * @version 1.0
- */
-s32 host_int_deinit(struct host_if_drv *hWFIDrv);
-
-
-/*!
- * @fn s32 host_int_add_beacon(WILC_WFIDrvHandle hWFIDrv,u8 u8Index)
- * @brief Sends a beacon to the firmware to be transmitted over the air
- * @details
- * @param[in,out] hWFIDrv handle to the wifi driver
- * @param[in] u32Interval Beacon Interval. Period between two successive beacons on air
- * @param[in] u32DTIMPeriod DTIM Period. Indicates how many Beacon frames
- * (including the current frame) appear before the next DTIM
- * @param[in] u32Headlen Length of the head buffer in bytes
- * @param[in] pu8Head Pointer to the beacon's head buffer. Beacon's head
- * is the part from the beacon's start till the TIM element, NOT including the TIM
- * @param[in] u32Taillen Length of the tail buffer in bytes
- * @param[in] pu8Tail Pointer to the beacon's tail buffer. Beacon's tail
- * starts just after the TIM inormation element
- * @return 0 for Success, error otherwise
- * @todo
- * @sa
- * @author Adham Abozaeid
- * @date 10 Julys 2012
- * @version 1.0 Description
- *
- */
-s32 host_int_add_beacon(struct host_if_drv *hWFIDrv, u32 u32Interval,
- u32 u32DTIMPeriod,
- u32 u32HeadLen, u8 *pu8Head,
- u32 u32TailLen, u8 *pu8tail);
-
-
-/*!
- * @fn s32 host_int_del_beacon(WILC_WFIDrvHandle hWFIDrv)
- * @brief Removes the beacon and stops trawilctting it over the air
- * @details
- * @param[in,out] hWFIDrv handle to the wifi driver
- * @return 0 for Success, error otherwise
- * @todo
- * @sa
- * @author Adham Abozaeid
- * @date 10 Julys 2012
- * @version 1.0 Description
- */
-s32 host_int_del_beacon(struct host_if_drv *hWFIDrv);
-
-/*!
- * @fn s32 host_int_add_station(WILC_WFIDrvHandle hWFIDrv,
- * struct add_sta_param *pstrStaParams)
- * @brief Notifies the firmware with a new associated stations
- * @details
- * @param[in,out] hWFIDrv handle to the wifi driver
- * @param[in] pstrStaParams Station's parameters
- * @return 0 for Success, error otherwise
- * @todo
- * @sa
- * @author Adham Abozaeid
- * @date 12 July 2012
- * @version 1.0 Description
- */
-s32 host_int_add_station(struct host_if_drv *hWFIDrv,
- struct add_sta_param *pstrStaParams);
-
-/*!
- * @fn s32 host_int_del_allstation(WILC_WFIDrvHandle hWFIDrv, const u8* pu8MacAddr)
- * @brief Deauthenticates clients when group is terminating
- * @details
- * @param[in,out] hWFIDrv handle to the wifi driver
- * @param[in] pu8MacAddr Station's mac address
- * @return 0 for Success, error otherwise
- * @todo
- * @sa
- * @author Mai Daftedar
- * @date 09 April 2014
- * @version 1.0 Description
- */
-s32 host_int_del_allstation(struct host_if_drv *hWFIDrv, u8 pu8MacAddr[][ETH_ALEN]);
-
-/*!
- * @fn s32 host_int_del_station(WILC_WFIDrvHandle hWFIDrv, u8* pu8MacAddr)
- * @brief Notifies the firmware with a new deleted station
- * @details
- * @param[in,out] hWFIDrv handle to the wifi driver
- * @param[in] pu8MacAddr Station's mac address
- * @return 0 for Success, error otherwise
- * @todo
- * @sa
- * @author Adham Abozaeid
- * @date 15 July 2012
- * @version 1.0 Description
- */
-s32 host_int_del_station(struct host_if_drv *hWFIDrv, const u8 *pu8MacAddr);
-
-/*!
- * @fn s32 host_int_edit_station(WILC_WFIDrvHandle hWFIDrv,
- * struct add_sta_param *pstrStaParams)
- * @brief Notifies the firmware with new parameters of an already associated station
- * @details
- * @param[in,out] hWFIDrv handle to the wifi driver
- * @param[in] pstrStaParams Station's parameters
- * @return 0 for Success, error otherwise
- * @todo
- * @sa
- * @author Adham Abozaeid
- * @date 15 July 2012
- * @version 1.0 Description
- */
-s32 host_int_edit_station(struct host_if_drv *hWFIDrv,
- struct add_sta_param *pstrStaParams);
-
-/*!
- * @fn s32 host_int_set_power_mgmt(WILC_WFIDrvHandle hWFIDrv, bool bIsEnabled, u32 u32Timeout)
- * @brief Set the power management mode to enabled or disabled
- * @details
- * @param[in,out] hWFIDrv handle to the wifi driver
- * @param[in] bIsEnabled TRUE if enabled, FALSE otherwise
- * @param[in] u32Timeout A timeout value of -1 allows the driver to adjust
- * the dynamic ps timeout value
- * @return 0 for Success, error otherwise
- * @todo
- * @sa
- * @author Adham Abozaeid
- * @date 24 November 2012
- * @version 1.0 Description
- */
-s32 host_int_set_power_mgmt(struct host_if_drv *hWFIDrv, bool bIsEnabled, u32 u32Timeout);
-/* @param[in,out] hWFIDrv handle to the wifi driver
- * @param[in] bIsEnabled TRUE if enabled, FALSE otherwise
- * @param[in] u8count count of mac address entries in the filter table
- *
- * @return 0 for Success, error otherwise
- * @todo
- * @sa
- * @author Adham Abozaeid
- * @date 24 November 2012
- * @version 1.0 Description
- */
-s32 host_int_setup_multicast_filter(struct host_if_drv *hWFIDrv, bool bIsEnabled, u32 u32count);
-/**
- * @brief host_int_setup_ipaddress
- * @details set IP address on firmware
- * @param[in]
- * @return Error code.
- * @author Abdelrahman Sobhy
- * @date
- * @version 1.0
- */
-s32 host_int_setup_ipaddress(struct host_if_drv *hWFIDrv, u8 *pu8IPAddr, u8 idx);
-
-
-/**
- * @brief host_int_delBASession
- * @details Delete single Rx BA session
- * @param[in]
- * @return Error code.
- * @author Abdelrahman Sobhy
- * @date
- * @version 1.0
- */
-s32 host_int_delBASession(struct host_if_drv *hWFIDrv, char *pBSSID, char TID);
-
-/**
- * @brief host_int_delBASession
- * @details Delete all Rx BA session
- * @param[in]
- * @return Error code.
- * @author Abdelrahman Sobhy
- * @date
- * @version 1.0
- */
-s32 host_int_del_All_Rx_BASession(struct host_if_drv *hWFIDrv, char *pBSSID, char TID);
-
-
-/**
- * @brief host_int_get_ipaddress
- * @details get IP address on firmware
- * @param[in]
- * @return Error code.
- * @author Abdelrahman Sobhy
- * @date
- * @version 1.0
- */
-s32 host_int_get_ipaddress(struct host_if_drv *hWFIDrv, u8 *pu8IPAddr, u8 idx);
-
-/**
- * @brief host_int_remain_on_channel
- * @details
- * @param[in]
- * @return Error code.
- * @author
- * @date
- * @version 1.0
- */
-s32 host_int_remain_on_channel(struct host_if_drv *hWFIDrv, u32 u32SessionID, u32 u32duration, u16 chan, wilc_remain_on_chan_expired RemainOnChanExpired, wilc_remain_on_chan_ready RemainOnChanReady, void *pvUserArg);
-
-/**
- * @brief host_int_ListenStateExpired
- * @details
- * @param[in] Handle to wifi driver
- * Duration to remain on channel
- * Channel to remain on
- * Pointer to fn to be called on receive frames in listen state
- * Pointer to remain-on-channel expired fn
- * Priv
- * @return Error code.
- * @author
- * @date
- * @version 1.0
- */
-s32 host_int_ListenStateExpired(struct host_if_drv *hWFIDrv, u32 u32SessionID);
-
-/**
- * @brief host_int_frame_register
- * @details
- * @param[in]
- * @return Error code.
- * @author
- * @date
- * @version 1.0
- */
-s32 host_int_frame_register(struct host_if_drv *hWFIDrv, u16 u16FrameType, bool bReg);
-/**
- * @brief host_int_set_wfi_drv_handler
- * @details
- * @param[in]
- * @return Error code.
- * @author
- * @date
- * @version 1.0
- */
-int host_int_set_wfi_drv_handler(struct host_if_drv *address);
-int host_int_set_operation_mode(struct host_if_drv *wfi_drv, u32 mode);
-
-static s32 Handle_ScanDone(struct host_if_drv *drvHandler, enum scan_event enuEvent);
-
-void host_int_freeJoinParams(void *pJoinParams);
-
-s32 host_int_get_statistics(struct host_if_drv *hWFIDrv, struct rf_info *pstrStatistics);
+struct wilc_vif;
+s32 wilc_remove_key(struct host_if_drv *hWFIDrv, const u8 *pu8StaAddress);
+int wilc_remove_wep_key(struct wilc_vif *vif, u8 index);
+int wilc_set_wep_default_keyid(struct wilc_vif *vif, u8 index);
+int wilc_add_wep_key_bss_sta(struct wilc_vif *vif, const u8 *key, u8 len,
+ u8 index);
+int wilc_add_wep_key_bss_ap(struct wilc_vif *vif, const u8 *key, u8 len,
+ u8 index, u8 mode, enum AUTHTYPE auth_type);
+s32 wilc_add_ptk(struct wilc_vif *vif, const u8 *pu8Ptk, u8 u8PtkKeylen,
+ const u8 *mac_addr, const u8 *pu8RxMic, const u8 *pu8TxMic,
+ u8 mode, u8 u8Ciphermode, u8 u8Idx);
+s32 wilc_get_inactive_time(struct wilc_vif *vif, const u8 *mac,
+ u32 *pu32InactiveTime);
+s32 wilc_add_rx_gtk(struct wilc_vif *vif, const u8 *pu8RxGtk, u8 u8GtkKeylen,
+ u8 u8KeyIdx, u32 u32KeyRSClen, const u8 *KeyRSC,
+ const u8 *pu8RxMic, const u8 *pu8TxMic, u8 mode,
+ u8 u8Ciphermode);
+s32 wilc_add_tx_gtk(struct host_if_drv *hWFIDrv, u8 u8KeyLen,
+ u8 *pu8TxGtk, u8 u8KeyIdx);
+s32 wilc_set_pmkid_info(struct wilc_vif *vif,
+ struct host_if_pmkid_attr *pu8PmkidInfoArray);
+s32 wilc_get_mac_address(struct wilc_vif *vif, u8 *pu8MacAddress);
+s32 wilc_set_mac_address(struct wilc_vif *vif, u8 *pu8MacAddress);
+int wilc_wait_msg_queue_idle(void);
+s32 wilc_set_start_scan_req(struct host_if_drv *hWFIDrv, u8 scanSource);
+s32 wilc_set_join_req(struct wilc_vif *vif, u8 *pu8bssid, const u8 *pu8ssid,
+ size_t ssidLen, const u8 *pu8IEs, size_t IEsLen,
+ wilc_connect_result pfConnectResult, void *pvUserArg,
+ u8 u8security, enum AUTHTYPE tenuAuth_type,
+ u8 u8channel, void *pJoinParams);
+s32 wilc_flush_join_req(struct wilc_vif *vif);
+s32 wilc_disconnect(struct wilc_vif *vif, u16 u16ReasonCode);
+int wilc_set_mac_chnl_num(struct wilc_vif *vif, u8 channel);
+s32 wilc_get_rssi(struct wilc_vif *vif, s8 *ps8Rssi);
+s32 wilc_scan(struct wilc_vif *vif, u8 u8ScanSource, u8 u8ScanType,
+ u8 *pu8ChnlFreqList, u8 u8ChnlListLen, const u8 *pu8IEs,
+ size_t IEsLen, wilc_scan_result ScanResult, void *pvUserArg,
+ struct hidden_network *pstrHiddenNetwork);
+s32 wilc_hif_set_cfg(struct wilc_vif *vif,
+ struct cfg_param_val *pstrCfgParamVal);
+s32 wilc_init(struct net_device *dev, struct host_if_drv **phWFIDrv);
+s32 wilc_deinit(struct wilc_vif *vif);
+s32 wilc_add_beacon(struct wilc_vif *vif, u32 u32Interval, u32 u32DTIMPeriod,
+ u32 u32HeadLen, u8 *pu8Head, u32 u32TailLen, u8 *pu8Tail);
+int wilc_del_beacon(struct wilc_vif *vif);
+int wilc_add_station(struct wilc_vif *vif, struct add_sta_param *sta_param);
+s32 wilc_del_allstation(struct wilc_vif *vif, u8 pu8MacAddr[][ETH_ALEN]);
+int wilc_del_station(struct wilc_vif *vif, const u8 *mac_addr);
+s32 wilc_edit_station(struct wilc_vif *vif,
+ struct add_sta_param *pstrStaParams);
+s32 wilc_set_power_mgmt(struct wilc_vif *vif, bool bIsEnabled, u32 u32Timeout);
+s32 wilc_setup_multicast_filter(struct wilc_vif *vif, bool bIsEnabled,
+ u32 u32count);
+s32 wilc_setup_ipaddress(struct wilc_vif *vif, u8 *u16ipadd, u8 idx);
+s32 wilc_del_all_rx_ba_session(struct wilc_vif *vif, char *pBSSID, char TID);
+s32 wilc_remain_on_channel(struct wilc_vif *vif, u32 u32SessionID,
+ u32 u32duration, u16 chan,
+ wilc_remain_on_chan_expired RemainOnChanExpired,
+ wilc_remain_on_chan_ready RemainOnChanReady,
+ void *pvUserArg);
+s32 wilc_listen_state_expired(struct wilc_vif *vif, u32 u32SessionID);
+s32 wilc_frame_register(struct wilc_vif *vif, u16 u16FrameType, bool bReg);
+int wilc_set_wfi_drv_handler(struct wilc_vif *vif, int index);
+int wilc_set_operation_mode(struct wilc_vif *vif, u32 mode);
+
+void wilc_free_join_params(void *pJoinParams);
+
+s32 wilc_get_statistics(struct wilc_vif *vif, struct rf_info *pstrStatistics);
+void wilc_resolve_disconnect_aberration(struct wilc_vif *vif);
+int wilc_get_vif_idx(struct wilc_vif *vif);
+
+extern bool wilc_optaining_ip;
+extern u8 wilc_connected_ssid[6];
+extern u8 wilc_multicast_mac_addr_list[WILC_MULTICAST_TABLE_SIZE][ETH_ALEN];
+
+extern int wilc_connecting;
+extern u8 wilc_initialized;
+extern struct timer_list wilc_during_ip_timer;
#endif
diff --git a/drivers/staging/wilc1000/linux_mon.c b/drivers/staging/wilc1000/linux_mon.c
index 450af1b77f99..e550027645b7 100644
--- a/drivers/staging/wilc1000/linux_mon.c
+++ b/drivers/staging/wilc1000/linux_mon.c
@@ -26,12 +26,9 @@ struct wilc_wfi_radiotap_cb_hdr {
static struct net_device *wilc_wfi_mon; /* global monitor netdev */
-extern int mac_xmit(struct sk_buff *skb, struct net_device *dev);
-
-
-u8 srcAdd[6];
-u8 bssid[6];
-u8 broadcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+static u8 srcAdd[6];
+static u8 bssid[6];
+static u8 broadcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
/**
* @brief WILC_WFI_monitor_rx
* @details
@@ -195,7 +192,7 @@ static int mon_mgmt_tx(struct net_device *dev, const u8 *buf, size_t len)
mgmt_tx->size = len;
memcpy(mgmt_tx->buff, buf, len);
- wilc_wlan_txq_add_mgmt_pkt(mgmt_tx, mgmt_tx->buff, mgmt_tx->size,
+ wilc_wlan_txq_add_mgmt_pkt(dev, mgmt_tx, mgmt_tx->buff, mgmt_tx->size,
mgmt_tx_complete);
netif_wake_queue(dev);
@@ -298,7 +295,7 @@ static netdev_tx_t WILC_WFI_mon_xmit(struct sk_buff *skb,
mon_mgmt_tx(mon_priv->real_ndev, skb->data, skb->len);
dev_kfree_skb(skb);
} else
- ret = mac_xmit(skb, mon_priv->real_ndev);
+ ret = wilc_mac_xmit(skb, mon_priv->real_ndev);
return ret;
}
diff --git a/drivers/staging/wilc1000/linux_wlan.c b/drivers/staging/wilc1000/linux_wlan.c
index 2a5b36fd8b48..54fe9d74b780 100644
--- a/drivers/staging/wilc1000/linux_wlan.c
+++ b/drivers/staging/wilc1000/linux_wlan.c
@@ -23,61 +23,8 @@
#include <linux/kernel.h>
#include <linux/skbuff.h>
-#include <linux/version.h>
#include <linux/semaphore.h>
-#ifdef WILC_SDIO
-#include "linux_wlan_sdio.h"
-#else
-#include "linux_wlan_spi.h"
-#endif
-
-#if defined(CUSTOMER_PLATFORM)
-/*
- TODO : Write power control functions as customer platform.
- */
-#else
-
- #define _linux_wlan_device_power_on() {}
- #define _linux_wlan_device_power_off() {}
-
- #define _linux_wlan_device_detection() {}
- #define _linux_wlan_device_removal() {}
-#endif
-
-extern bool g_obtainingIP;
-extern void resolve_disconnect_aberration(void *drvHandler);
-extern u8 gau8MulticastMacAddrList[WILC_MULTICAST_TABLE_SIZE][ETH_ALEN];
-extern struct timer_list hDuringIpTimer;
-
-static int linux_wlan_device_power(int on_off)
-{
- PRINT_D(INIT_DBG, "linux_wlan_device_power.. (%d)\n", on_off);
-
- if (on_off) {
- _linux_wlan_device_power_on();
- } else {
- _linux_wlan_device_power_off();
- }
-
- return 0;
-}
-
-static int linux_wlan_device_detection(int on_off)
-{
- PRINT_D(INIT_DBG, "linux_wlan_device_detection.. (%d)\n", on_off);
-
-#ifdef WILC_SDIO
- if (on_off) {
- _linux_wlan_device_detection();
- } else {
- _linux_wlan_device_removal();
- }
-#endif
-
- return 0;
-}
-
static int dev_state_ev_handler(struct notifier_block *this, unsigned long event, void *ptr);
static struct notifier_block g_dev_notifier = {
@@ -86,40 +33,24 @@ static struct notifier_block g_dev_notifier = {
#define IRQ_WAIT 1
#define IRQ_NO_WAIT 0
-/*
- * to sync between mac_close and module exit.
- * don't initialize or de-initialize from init/deinitlocks
- * to be initialized from module wilc_netdev_init and
- * deinitialized from mdoule_exit
- */
static struct semaphore close_exit_sync;
static int wlan_deinit_locks(struct net_device *dev);
static void wlan_deinitialize_threads(struct net_device *dev);
-extern void WILC_WFI_monitor_rx(u8 *buff, u32 size);
-extern void WILC_WFI_p2p_rx(struct net_device *dev, u8 *buff, u32 size);
static void linux_wlan_tx_complete(void *priv, int status);
static int mac_init_fn(struct net_device *ndev);
-int mac_xmit(struct sk_buff *skb, struct net_device *dev);
-int mac_open(struct net_device *ndev);
-int mac_close(struct net_device *ndev);
static struct net_device_stats *mac_stats(struct net_device *dev);
static int mac_ioctl(struct net_device *ndev, struct ifreq *req, int cmd);
static void wilc_set_multicast_list(struct net_device *dev);
-/*
- * for now - in frmw_to_linux there should be private data to be passed to it
- * and this data should be pointer to net device
- */
-struct wilc *g_linux_wlan;
-bool bEnablePS = true;
+bool wilc_enable_ps = true;
static const struct net_device_ops wilc_netdev_ops = {
.ndo_init = mac_init_fn,
- .ndo_open = mac_open,
- .ndo_stop = mac_close,
- .ndo_start_xmit = mac_xmit,
+ .ndo_open = wilc_mac_open,
+ .ndo_stop = wilc_mac_close,
+ .ndo_start_xmit = wilc_mac_xmit,
.ndo_do_ioctl = mac_ioctl,
.ndo_get_stats = mac_stats,
.ndo_set_rx_mode = wilc_set_multicast_list,
@@ -130,130 +61,129 @@ static int dev_state_ev_handler(struct notifier_block *this, unsigned long event
{
struct in_ifaddr *dev_iface = (struct in_ifaddr *)ptr;
struct wilc_priv *priv;
- struct host_if_drv *pstrWFIDrv;
+ struct host_if_drv *hif_drv;
struct net_device *dev;
- u8 *pIP_Add_buff;
- perInterface_wlan_t *nic;
+ u8 *ip_addr_buf;
+ struct wilc_vif *vif;
u8 null_ip[4] = {0};
char wlan_dev_name[5] = "wlan0";
- if (dev_iface == NULL || dev_iface->ifa_dev == NULL || dev_iface->ifa_dev->dev == NULL) {
+ if (!dev_iface || !dev_iface->ifa_dev || !dev_iface->ifa_dev->dev) {
PRINT_D(GENERIC_DBG, "dev_iface = NULL\n");
return NOTIFY_DONE;
}
- if ((memcmp(dev_iface->ifa_label, "wlan0", 5)) && (memcmp(dev_iface->ifa_label, "p2p0", 4))) {
+ if (memcmp(dev_iface->ifa_label, "wlan0", 5) &&
+ memcmp(dev_iface->ifa_label, "p2p0", 4)) {
PRINT_D(GENERIC_DBG, "Interface is neither WLAN0 nor P2P0\n");
return NOTIFY_DONE;
}
dev = (struct net_device *)dev_iface->ifa_dev->dev;
- if (dev->ieee80211_ptr == NULL || dev->ieee80211_ptr->wiphy == NULL) {
+ if (!dev->ieee80211_ptr || !dev->ieee80211_ptr->wiphy) {
PRINT_D(GENERIC_DBG, "No Wireless registerd\n");
return NOTIFY_DONE;
}
priv = wiphy_priv(dev->ieee80211_ptr->wiphy);
- if (priv == NULL) {
+ if (!priv) {
PRINT_D(GENERIC_DBG, "No Wireless Priv\n");
return NOTIFY_DONE;
}
- pstrWFIDrv = (struct host_if_drv *)priv->hWILCWFIDrv;
- nic = netdev_priv(dev);
- if (nic == NULL || pstrWFIDrv == NULL) {
+ hif_drv = (struct host_if_drv *)priv->hWILCWFIDrv;
+ vif = netdev_priv(dev);
+ if (!vif || !hif_drv) {
PRINT_D(GENERIC_DBG, "No Wireless Priv\n");
return NOTIFY_DONE;
}
- PRINT_INFO(GENERIC_DBG, "dev_state_ev_handler +++\n"); /* tony */
+ PRINT_INFO(GENERIC_DBG, "dev_state_ev_handler +++\n");
switch (event) {
case NETDEV_UP:
- PRINT_D(GENERIC_DBG, "dev_state_ev_handler event=NETDEV_UP %p\n", dev); /* tony */
+ PRINT_D(GENERIC_DBG, "dev_state_ev_handler event=NETDEV_UP %p\n", dev);
PRINT_INFO(GENERIC_DBG, "\n ============== IP Address Obtained ===============\n\n");
- /*If we are in station mode or client mode*/
- if (nic->iftype == STATION_MODE || nic->iftype == CLIENT_MODE) {
- pstrWFIDrv->IFC_UP = 1;
- g_obtainingIP = false;
- del_timer(&hDuringIpTimer);
+ if (vif->iftype == STATION_MODE || vif->iftype == CLIENT_MODE) {
+ hif_drv->IFC_UP = 1;
+ wilc_optaining_ip = false;
+ del_timer(&wilc_during_ip_timer);
PRINT_D(GENERIC_DBG, "IP obtained , enable scan\n");
}
- if (bEnablePS)
- host_int_set_power_mgmt(pstrWFIDrv, 1, 0);
+ if (wilc_enable_ps)
+ wilc_set_power_mgmt(vif, 1, 0);
PRINT_D(GENERIC_DBG, "[%s] Up IP\n", dev_iface->ifa_label);
- pIP_Add_buff = (char *) (&(dev_iface->ifa_address));
- PRINT_D(GENERIC_DBG, "IP add=%d:%d:%d:%d\n", pIP_Add_buff[0], pIP_Add_buff[1], pIP_Add_buff[2], pIP_Add_buff[3]);
- host_int_setup_ipaddress(pstrWFIDrv, pIP_Add_buff, nic->u8IfIdx);
+ ip_addr_buf = (char *)&dev_iface->ifa_address;
+ PRINT_D(GENERIC_DBG, "IP add=%d:%d:%d:%d\n",
+ ip_addr_buf[0], ip_addr_buf[1],
+ ip_addr_buf[2], ip_addr_buf[3]);
+ wilc_setup_ipaddress(vif, ip_addr_buf, vif->u8IfIdx);
break;
case NETDEV_DOWN:
- PRINT_D(GENERIC_DBG, "dev_state_ev_handler event=NETDEV_DOWN %p\n", dev); /* tony */
+ PRINT_D(GENERIC_DBG, "dev_state_ev_handler event=NETDEV_DOWN %p\n", dev);
PRINT_INFO(GENERIC_DBG, "\n ============== IP Address Released ===============\n\n");
- if (nic->iftype == STATION_MODE || nic->iftype == CLIENT_MODE) {
- pstrWFIDrv->IFC_UP = 0;
- g_obtainingIP = false;
+ if (vif->iftype == STATION_MODE || vif->iftype == CLIENT_MODE) {
+ hif_drv->IFC_UP = 0;
+ wilc_optaining_ip = false;
}
if (memcmp(dev_iface->ifa_label, wlan_dev_name, 5) == 0)
- host_int_set_power_mgmt(pstrWFIDrv, 0, 0);
+ wilc_set_power_mgmt(vif, 0, 0);
- resolve_disconnect_aberration(pstrWFIDrv);
+ wilc_resolve_disconnect_aberration(vif);
PRINT_D(GENERIC_DBG, "[%s] Down IP\n", dev_iface->ifa_label);
- pIP_Add_buff = null_ip;
- PRINT_D(GENERIC_DBG, "IP add=%d:%d:%d:%d\n", pIP_Add_buff[0], pIP_Add_buff[1], pIP_Add_buff[2], pIP_Add_buff[3]);
+ ip_addr_buf = null_ip;
+ PRINT_D(GENERIC_DBG, "IP add=%d:%d:%d:%d\n",
+ ip_addr_buf[0], ip_addr_buf[1],
+ ip_addr_buf[2], ip_addr_buf[3]);
- host_int_setup_ipaddress(pstrWFIDrv, pIP_Add_buff, nic->u8IfIdx);
+ wilc_setup_ipaddress(vif, ip_addr_buf, vif->u8IfIdx);
break;
default:
- PRINT_INFO(GENERIC_DBG, "dev_state_ev_handler event=default\n"); /* tony */
+ PRINT_INFO(GENERIC_DBG, "dev_state_ev_handler event=default\n");
PRINT_INFO(GENERIC_DBG, "[%s] unknown dev event: %lu\n", dev_iface->ifa_label, event);
break;
}
return NOTIFY_DONE;
-
}
-#if (defined WILC_SPI) || (defined WILC_SDIO_IRQ_GPIO)
static irqreturn_t isr_uh_routine(int irq, void *user_data)
{
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct wilc *wilc;
struct net_device *dev = (struct net_device *)user_data;
- nic = netdev_priv(dev);
- wilc = nic->wilc;
+ vif = netdev_priv(dev);
+ wilc = vif->wilc;
PRINT_D(INT_DBG, "Interrupt received UH\n");
- /*While mac is closing cacncel the handling of any interrupts received*/
if (wilc->close) {
PRINT_ER("Driver is CLOSING: Can't handle UH interrupt\n");
return IRQ_HANDLED;
}
return IRQ_WAKE_THREAD;
}
-#endif
-irqreturn_t isr_bh_routine(int irq, void *userdata)
+static irqreturn_t isr_bh_routine(int irq, void *userdata)
{
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct wilc *wilc;
- nic = netdev_priv(userdata);
- wilc = nic->wilc;
+ vif = netdev_priv(userdata);
+ wilc = vif->wilc;
- /*While mac is closing cacncel the handling of any interrupts received*/
if (wilc->close) {
PRINT_ER("Driver is CLOSING: Can't handle BH interrupt\n");
return IRQ_HANDLED;
@@ -265,154 +195,131 @@ irqreturn_t isr_bh_routine(int irq, void *userdata)
return IRQ_HANDLED;
}
-#if (defined WILC_SPI) || (defined WILC_SDIO_IRQ_GPIO)
static int init_irq(struct net_device *dev)
{
int ret = 0;
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct wilc *wl;
- nic = netdev_priv(dev);
- wl = nic->wilc;
-
- /*initialize GPIO and register IRQ num*/
- /*GPIO request*/
- if ((gpio_request(GPIO_NUM, "WILC_INTR") == 0) &&
- (gpio_direction_input(GPIO_NUM) == 0)) {
-#if defined(CUSTOMER_PLATFORM)
-/*
- TODO : save the registerd irq number to the private wilc context in kernel.
- *
- * ex) nic->dev_irq_num = gpio_to_irq(GPIO_NUM);
- */
-#else
- wl->dev_irq_num = gpio_to_irq(GPIO_NUM);
-#endif
+ vif = netdev_priv(dev);
+ wl = vif->wilc;
+
+ if ((gpio_request(wl->gpio, "WILC_INTR") == 0) &&
+ (gpio_direction_input(wl->gpio) == 0)) {
+ wl->dev_irq_num = gpio_to_irq(wl->gpio);
} else {
ret = -1;
PRINT_ER("could not obtain gpio for WILC_INTR\n");
}
- if ((ret != -1) && (request_threaded_irq(wl->dev_irq_num, isr_uh_routine, isr_bh_routine,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT, /*Without IRQF_ONESHOT the uh will remain kicked in and dont gave a chance to bh*/
- "WILC_IRQ", dev)) < 0) {
-
- PRINT_ER("Failed to request IRQ for GPIO: %d\n", GPIO_NUM);
+ if (ret != -1 && request_threaded_irq(wl->dev_irq_num,
+ isr_uh_routine,
+ isr_bh_routine,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "WILC_IRQ", dev) < 0) {
+ PRINT_ER("Failed to request IRQ for GPIO: %d\n", wl->gpio);
+ gpio_free(wl->gpio);
ret = -1;
} else {
-
PRINT_D(INIT_DBG, "IRQ request succeeded IRQ-NUM= %d on GPIO: %d\n",
- wl->dev_irq_num, GPIO_NUM);
+ wl->dev_irq_num, wl->gpio);
}
return ret;
}
-#endif
static void deinit_irq(struct net_device *dev)
{
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct wilc *wilc;
- nic = netdev_priv(dev);
- wilc = nic->wilc;
+ vif = netdev_priv(dev);
+ wilc = vif->wilc;
-#if (defined WILC_SPI) || (defined WILC_SDIO_IRQ_GPIO)
/* Deintialize IRQ */
- if (&wilc->dev_irq_num != 0) {
+ if (wilc->dev_irq_num) {
free_irq(wilc->dev_irq_num, wilc);
-
- gpio_free(GPIO_NUM);
+ gpio_free(wilc->gpio);
}
-#endif
}
-/*
- * OS functions
- */
-void linux_wlan_dbg(u8 *buff)
+void wilc_dbg(u8 *buff)
{
PRINT_D(INIT_DBG, "%d\n", *buff);
}
-int linux_wlan_lock_timeout(void *vp, u32 timeout)
+int wilc_lock_timeout(struct wilc *nic, void *vp, u32 timeout)
{
+ /* FIXME: replace with mutex_lock or wait_for_completion */
int error = -1;
PRINT_D(LOCK_DBG, "Locking %p\n", vp);
- if (vp != NULL)
- error = down_timeout((struct semaphore *)vp, msecs_to_jiffies(timeout));
+ if (vp)
+ error = down_timeout((struct semaphore *)vp,
+ msecs_to_jiffies(timeout));
else
PRINT_ER("Failed, mutex is NULL\n");
return error;
}
-void linux_wlan_mac_indicate(struct wilc *wilc, int flag)
+void wilc_mac_indicate(struct wilc *wilc, int flag)
{
- /*I have to do it that way becuase there is no mean to encapsulate device pointer
- * as a parameter
- */
int status;
if (flag == WILC_MAC_INDICATE_STATUS) {
- wilc_wlan_cfg_get_val(WID_STATUS, (unsigned char *)&status, 4);
+ wilc_wlan_cfg_get_val(WID_STATUS,
+ (unsigned char *)&status, 4);
if (wilc->mac_status == WILC_MAC_STATUS_INIT) {
wilc->mac_status = status;
up(&wilc->sync_event);
} else {
wilc->mac_status = status;
}
-
- if (wilc->mac_status == WILC_MAC_STATUS_CONNECT) { /* Connect */
- }
-
} else if (flag == WILC_MAC_INDICATE_SCAN) {
PRINT_D(GENERIC_DBG, "Scanning ...\n");
-
}
-
}
-struct net_device *GetIfHandler(struct wilc *wilc, u8 *pMacHeader)
+static struct net_device *get_if_handler(struct wilc *wilc, u8 *mac_header)
{
- u8 *Bssid, *Bssid1;
+ u8 *bssid, *bssid1;
int i = 0;
- Bssid = pMacHeader + 10;
- Bssid1 = pMacHeader + 4;
+ bssid = mac_header + 10;
+ bssid1 = mac_header + 4;
for (i = 0; i < wilc->vif_num; i++)
- if (!memcmp(Bssid1, wilc->vif[i].bssid, ETH_ALEN) ||
- !memcmp(Bssid, wilc->vif[i].bssid, ETH_ALEN))
- return wilc->vif[i].ndev;
+ if (!memcmp(bssid1, wilc->vif[i]->bssid, ETH_ALEN) ||
+ !memcmp(bssid, wilc->vif[i]->bssid, ETH_ALEN))
+ return wilc->vif[i]->ndev;
PRINT_INFO(INIT_DBG, "Invalide handle\n");
for (i = 0; i < 25; i++)
- PRINT_D(INIT_DBG, "%02x ", pMacHeader[i]);
- Bssid = pMacHeader + 18;
- Bssid1 = pMacHeader + 12;
+ PRINT_D(INIT_DBG, "%02x ", mac_header[i]);
+ bssid = mac_header + 18;
+ bssid1 = mac_header + 12;
for (i = 0; i < wilc->vif_num; i++)
- if (!memcmp(Bssid1, wilc->vif[i].bssid, ETH_ALEN) ||
- !memcmp(Bssid, wilc->vif[i].bssid, ETH_ALEN))
- return wilc->vif[i].ndev;
+ if (!memcmp(bssid1, wilc->vif[i]->bssid, ETH_ALEN) ||
+ !memcmp(bssid, wilc->vif[i]->bssid, ETH_ALEN))
+ return wilc->vif[i]->ndev;
PRINT_INFO(INIT_DBG, "\n");
return NULL;
}
-int linux_wlan_set_bssid(struct net_device *wilc_netdev, u8 *pBSSID)
+int wilc_wlan_set_bssid(struct net_device *wilc_netdev, u8 *bssid)
{
int i = 0;
int ret = -1;
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct wilc *wilc;
- nic = netdev_priv(wilc_netdev);
- wilc = nic->wilc;
+ vif = netdev_priv(wilc_netdev);
+ wilc = vif->wilc;
for (i = 0; i < wilc->vif_num; i++)
- if (wilc->vif[i].ndev == wilc_netdev) {
- memcpy(wilc->vif[i].bssid, pBSSID, 6);
+ if (wilc->vif[i]->ndev == wilc_netdev) {
+ memcpy(wilc->vif[i]->bssid, bssid, 6);
ret = 0;
break;
}
@@ -420,15 +327,14 @@ int linux_wlan_set_bssid(struct net_device *wilc_netdev, u8 *pBSSID)
return ret;
}
-/*Function to get number of connected interfaces*/
-int linux_wlan_get_num_conn_ifcs(void)
+int wilc_wlan_get_num_conn_ifcs(struct wilc *wilc)
{
u8 i = 0;
u8 null_bssid[6] = {0};
u8 ret_val = 0;
- for (i = 0; i < g_linux_wlan->vif_num; i++)
- if (memcmp(g_linux_wlan->vif[i].bssid, null_bssid, 6))
+ for (i = 0; i < wilc->vif_num; i++)
+ if (memcmp(wilc->vif[i]->bssid, null_bssid, 6))
ret_val++;
return ret_val;
@@ -439,7 +345,7 @@ int linux_wlan_get_num_conn_ifcs(void)
static int linux_wlan_txq_task(void *vp)
{
int ret, txq_count;
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct wilc *wl;
struct net_device *dev = vp;
#if defined USE_TX_BACKOFF_DELAY_IF_NO_BUFFERS
@@ -451,20 +357,16 @@ static int linux_wlan_txq_task(void *vp)
int backoff_weight = TX_BACKOFF_WEIGHT_MIN;
#endif
- nic = netdev_priv(dev);
- wl = nic->wilc;
+ vif = netdev_priv(dev);
+ wl = vif->wilc;
- /* inform wilc1000_wlan_init that TXQ task is started. */
up(&wl->txq_thread_started);
while (1) {
-
PRINT_D(TX_DBG, "txq_task Taking a nap :)\n");
down(&wl->txq_event);
- /* wait_for_completion(&pd->txq_event); */
PRINT_D(TX_DBG, "txq_task Who waked me up :$\n");
if (wl->close) {
- /*Unlock the mutex in the mac_close function to indicate the exiting of the TX thread */
up(&wl->txq_thread_started);
while (!kthread_should_stop())
@@ -479,23 +381,19 @@ static int linux_wlan_txq_task(void *vp)
#else
do {
ret = wilc_wlan_handle_txq(dev, &txq_count);
- if (txq_count < FLOW_CONTROL_LOWER_THRESHOLD /* && netif_queue_stopped(pd->wilc_netdev)*/) {
+ if (txq_count < FLOW_CONTROL_LOWER_THRESHOLD) {
PRINT_D(TX_DBG, "Waking up queue\n");
- /* netif_wake_queue(pd->wilc_netdev); */
- if (netif_queue_stopped(wl->vif[0].ndev))
- netif_wake_queue(wl->vif[0].ndev);
- if (netif_queue_stopped(wl->vif[1].ndev))
- netif_wake_queue(wl->vif[1].ndev);
+
+ if (netif_queue_stopped(wl->vif[0]->ndev))
+ netif_wake_queue(wl->vif[0]->ndev);
+ if (netif_queue_stopped(wl->vif[1]->ndev))
+ netif_wake_queue(wl->vif[1]->ndev);
}
- if (ret == WILC_TX_ERR_NO_BUF) { /* failed to allocate buffers in chip. */
+ if (ret == WILC_TX_ERR_NO_BUF) {
do {
- /* Back off from sending packets for some time. */
- /* schedule_timeout will allow RX task to run and free buffers.*/
- /* set_current_state(TASK_UNINTERRUPTIBLE); */
- /* timeout = schedule_timeout(timeout); */
msleep(TX_BACKOFF_WEIGHT_UNIT_MS << backoff_weight);
- } while (/*timeout*/ 0);
+ } while (0);
backoff_weight += TX_BACKOFF_WEIGHT_INCR_STEP;
if (backoff_weight > TX_BACKOFF_WEIGHT_MAX)
backoff_weight = TX_BACKOFF_WEIGHT_MAX;
@@ -506,376 +404,326 @@ static int linux_wlan_txq_task(void *vp)
backoff_weight = TX_BACKOFF_WEIGHT_MIN;
}
}
- /*TODO: drop packets after a certain time/number of retry count. */
- } while (ret == WILC_TX_ERR_NO_BUF && !wl->close); /* retry sending packets if no more buffers in chip. */
+ } while (ret == WILC_TX_ERR_NO_BUF && !wl->close);
#endif
}
return 0;
}
-void linux_wlan_rx_complete(void)
+void wilc_rx_complete(struct wilc *nic)
{
PRINT_D(RX_DBG, "RX completed\n");
}
-int linux_wlan_get_firmware(perInterface_wlan_t *p_nic)
+int wilc_wlan_get_firmware(struct net_device *dev)
{
-
- perInterface_wlan_t *nic = p_nic;
+ struct wilc_vif *vif;
+ struct wilc *wilc;
int ret = 0;
const struct firmware *wilc_firmware;
char *firmware;
- if (nic->iftype == AP_MODE)
+ vif = netdev_priv(dev);
+ wilc = vif->wilc;
+
+ if (vif->iftype == AP_MODE) {
firmware = AP_FIRMWARE;
- else if (nic->iftype == STATION_MODE)
+ } else if (vif->iftype == STATION_MODE) {
firmware = STA_FIRMWARE;
-
- else {
+ } else {
PRINT_D(INIT_DBG, "Get P2P_CONCURRENCY_FIRMWARE\n");
firmware = P2P_CONCURRENCY_FIRMWARE;
}
- if (nic == NULL) {
- PRINT_ER("NIC is NULL\n");
+ if (!vif) {
+ PRINT_ER("vif is NULL\n");
goto _fail_;
}
- if (&nic->wilc_netdev->dev == NULL) {
- PRINT_ER("&nic->wilc_netdev->dev is NULL\n");
+ if (!(&vif->ndev->dev)) {
+ PRINT_ER("&vif->ndev->dev is NULL\n");
goto _fail_;
}
- /* the firmare should be located in /lib/firmware in
- * root file system with the name specified above */
-
-#ifdef WILC_SDIO
- if (request_firmware(&wilc_firmware, firmware, &g_linux_wlan->wilc_sdio_func->dev) != 0) {
+ if (request_firmware(&wilc_firmware, firmware, wilc->dev) != 0) {
PRINT_ER("%s - firmare not available\n", firmware);
ret = -1;
goto _fail_;
}
-#else
- if (request_firmware(&wilc_firmware, firmware, &g_linux_wlan->wilc_spidev->dev) != 0) {
- PRINT_ER("%s - firmare not available\n", firmware);
- ret = -1;
- goto _fail_;
- }
-#endif
- g_linux_wlan->firmware = wilc_firmware;
+ wilc->firmware = wilc_firmware;
_fail_:
return ret;
-
}
-static int linux_wlan_start_firmware(perInterface_wlan_t *nic)
+static int linux_wlan_start_firmware(struct net_device *dev)
{
-
+ struct wilc_vif *vif;
+ struct wilc *wilc;
int ret = 0;
- /* start firmware */
+
+ vif = netdev_priv(dev);
+ wilc = vif->wilc;
+
PRINT_D(INIT_DBG, "Starting Firmware ...\n");
- ret = wilc_wlan_start();
+ ret = wilc_wlan_start(wilc);
if (ret < 0) {
PRINT_ER("Failed to start Firmware\n");
- goto _fail_;
+ return ret;
}
- /* wait for mac ready */
PRINT_D(INIT_DBG, "Waiting for Firmware to get ready ...\n");
- ret = linux_wlan_lock_timeout(&g_linux_wlan->sync_event, 5000);
+ ret = wilc_lock_timeout(wilc, &wilc->sync_event, 5000);
if (ret) {
PRINT_D(INIT_DBG, "Firmware start timed out");
- goto _fail_;
+ return ret;
}
- /*
- * TODO: Driver shouoldn't wait forever for firmware to get started -
- * in case of timeout this should be handled properly
- */
PRINT_D(INIT_DBG, "Firmware successfully started\n");
-_fail_:
- return ret;
+ return 0;
}
-static int linux_wlan_firmware_download(struct wilc *p_nic)
-{
+static int wilc1000_firmware_download(struct net_device *dev)
+{
+ struct wilc_vif *vif;
+ struct wilc *wilc;
int ret = 0;
- if (!g_linux_wlan->firmware) {
+ vif = netdev_priv(dev);
+ wilc = vif->wilc;
+
+ if (!wilc->firmware) {
PRINT_ER("Firmware buffer is NULL\n");
- ret = -ENOBUFS;
- goto _FAIL_;
+ return -ENOBUFS;
}
- /**
- * do the firmware download
- **/
PRINT_D(INIT_DBG, "Downloading Firmware ...\n");
- ret = wilc_wlan_firmware_download(g_linux_wlan->firmware->data,
- g_linux_wlan->firmware->size);
+ ret = wilc_wlan_firmware_download(wilc, wilc->firmware->data,
+ wilc->firmware->size);
if (ret < 0)
- goto _FAIL_;
+ return ret;
- /* Freeing FW buffer */
PRINT_D(INIT_DBG, "Freeing FW buffer ...\n");
PRINT_D(INIT_DBG, "Releasing firmware\n");
- release_firmware(g_linux_wlan->firmware);
+ release_firmware(wilc->firmware);
+ wilc->firmware = NULL;
PRINT_D(INIT_DBG, "Download Succeeded\n");
-_FAIL_:
- return ret;
+ return 0;
}
-/* startup configuration - could be changed later using iconfig*/
-static int linux_wlan_init_test_config(struct net_device *dev, struct wilc *p_nic)
+static int linux_wlan_init_test_config(struct net_device *dev,
+ struct wilc *wilc)
{
-
unsigned char c_val[64];
unsigned char mac_add[] = {0x00, 0x80, 0xC2, 0x5E, 0xa2, 0xff};
struct wilc_priv *priv;
- struct host_if_drv *pstrWFIDrv;
+ struct host_if_drv *hif_drv;
PRINT_D(TX_DBG, "Start configuring Firmware\n");
get_random_bytes(&mac_add[5], 1);
get_random_bytes(&mac_add[4], 1);
priv = wiphy_priv(dev->ieee80211_ptr->wiphy);
- pstrWFIDrv = (struct host_if_drv *)priv->hWILCWFIDrv;
- PRINT_D(INIT_DBG, "Host = %p\n", pstrWFIDrv);
+ hif_drv = (struct host_if_drv *)priv->hWILCWFIDrv;
+ PRINT_D(INIT_DBG, "Host = %p\n", hif_drv);
- PRINT_D(INIT_DBG, "MAC address is : %02x-%02x-%02x-%02x-%02x-%02x\n", mac_add[0], mac_add[1], mac_add[2], mac_add[3], mac_add[4], mac_add[5]);
- wilc_get_chipid(0);
+ PRINT_D(INIT_DBG, "MAC address is : %02x-%02x-%02x-%02x-%02x-%02x\n",
+ mac_add[0], mac_add[1], mac_add[2],
+ mac_add[3], mac_add[4], mac_add[5]);
+ wilc_get_chipid(wilc, 0);
*(int *)c_val = 1;
- if (!wilc_wlan_cfg_set(1, WID_SET_DRV_HANDLER, c_val, 4, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 1, WID_SET_DRV_HANDLER, c_val, 4, 0, 0))
goto _fail_;
- /*to tell fw that we are going to use PC test - WILC specific*/
c_val[0] = 0;
- if (!wilc_wlan_cfg_set(0, WID_PC_TEST_MODE, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_PC_TEST_MODE, c_val, 1, 0, 0))
goto _fail_;
c_val[0] = INFRASTRUCTURE;
- if (!wilc_wlan_cfg_set(0, WID_BSS_TYPE, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_BSS_TYPE, c_val, 1, 0, 0))
goto _fail_;
- /* c_val[0] = RATE_AUTO; */
c_val[0] = RATE_AUTO;
- if (!wilc_wlan_cfg_set(0, WID_CURRENT_TX_RATE, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_CURRENT_TX_RATE, c_val, 1, 0, 0))
goto _fail_;
c_val[0] = G_MIXED_11B_2_MODE;
- if (!wilc_wlan_cfg_set(0, WID_11G_OPERATING_MODE, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_11G_OPERATING_MODE, c_val, 1, 0,
+ 0))
goto _fail_;
c_val[0] = 1;
- if (!wilc_wlan_cfg_set(0, WID_CURRENT_CHANNEL, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_CURRENT_CHANNEL, c_val, 1, 0, 0))
goto _fail_;
c_val[0] = G_SHORT_PREAMBLE;
- if (!wilc_wlan_cfg_set(0, WID_PREAMBLE, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_PREAMBLE, c_val, 1, 0, 0))
goto _fail_;
c_val[0] = AUTO_PROT;
- if (!wilc_wlan_cfg_set(0, WID_11N_PROT_MECH, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_11N_PROT_MECH, c_val, 1, 0, 0))
goto _fail_;
c_val[0] = ACTIVE_SCAN;
- if (!wilc_wlan_cfg_set(0, WID_SCAN_TYPE, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_SCAN_TYPE, c_val, 1, 0, 0))
goto _fail_;
c_val[0] = SITE_SURVEY_OFF;
- if (!wilc_wlan_cfg_set(0, WID_SITE_SURVEY, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_SITE_SURVEY, c_val, 1, 0, 0))
goto _fail_;
- *((int *)c_val) = 0xffff; /* Never use RTS-CTS */
- if (!wilc_wlan_cfg_set(0, WID_RTS_THRESHOLD, c_val, 2, 0, 0))
+ *((int *)c_val) = 0xffff;
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_RTS_THRESHOLD, c_val, 2, 0, 0))
goto _fail_;
*((int *)c_val) = 2346;
- if (!wilc_wlan_cfg_set(0, WID_FRAG_THRESHOLD, c_val, 2, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_FRAG_THRESHOLD, c_val, 2, 0, 0))
goto _fail_;
- /* SSID */
- /* -------------------------------------------------------------- */
- /* Configuration : String with length less than 32 bytes */
- /* Values to set : Any string with length less than 32 bytes */
- /* ( In BSS Station Set SSID to "" (null string) */
- /* to enable Broadcast SSID suppport ) */
- /* -------------------------------------------------------------- */
c_val[0] = 0;
- if (!wilc_wlan_cfg_set(0, WID_BCAST_SSID, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_BCAST_SSID, c_val, 1, 0, 0))
goto _fail_;
c_val[0] = 1;
- if (!wilc_wlan_cfg_set(0, WID_QOS_ENABLE, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_QOS_ENABLE, c_val, 1, 0, 0))
goto _fail_;
c_val[0] = NO_POWERSAVE;
- if (!wilc_wlan_cfg_set(0, WID_POWER_MANAGEMENT, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_POWER_MANAGEMENT, c_val, 1, 0, 0))
goto _fail_;
- c_val[0] = NO_ENCRYPT; /* NO_ENCRYPT, 0x79 */
- if (!wilc_wlan_cfg_set(0, WID_11I_MODE, c_val, 1, 0, 0))
+ c_val[0] = NO_SECURITY; /* NO_ENCRYPT, 0x79 */
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_11I_MODE, c_val, 1, 0, 0))
goto _fail_;
c_val[0] = OPEN_SYSTEM;
- if (!wilc_wlan_cfg_set(0, WID_AUTH_TYPE, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_AUTH_TYPE, c_val, 1, 0, 0))
goto _fail_;
- /* WEP/802 11I Configuration */
- /* ------------------------------------------------------------------ */
- /* Configuration : WEP Key */
- /* Values (0x) : 5 byte for WEP40 and 13 bytes for WEP104 */
- /* In case more than 5 bytes are passed on for WEP 40 */
- /* only first 5 bytes will be used as the key */
- /* ------------------------------------------------------------------ */
-
strcpy(c_val, "123456790abcdef1234567890");
- if (!wilc_wlan_cfg_set(0, WID_WEP_KEY_VALUE, c_val, (strlen(c_val) + 1), 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_WEP_KEY_VALUE, c_val,
+ (strlen(c_val) + 1), 0, 0))
goto _fail_;
- /* WEP/802 11I Configuration */
- /* ------------------------------------------------------------------ */
- /* Configuration : AES/TKIP WPA/RSNA Pre-Shared Key */
- /* Values to set : Any string with length greater than equal to 8 bytes */
- /* and less than 64 bytes */
- /* ------------------------------------------------------------------ */
strcpy(c_val, "12345678");
- if (!wilc_wlan_cfg_set(0, WID_11I_PSK, c_val, (strlen(c_val)), 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_11I_PSK, c_val, (strlen(c_val)), 0,
+ 0))
goto _fail_;
- /* IEEE802.1X Key Configuration */
- /* ------------------------------------------------------------------ */
- /* Configuration : Radius Server Access Secret Key */
- /* Values to set : Any string with length greater than equal to 8 bytes */
- /* and less than 65 bytes */
- /* ------------------------------------------------------------------ */
strcpy(c_val, "password");
- if (!wilc_wlan_cfg_set(0, WID_1X_KEY, c_val, (strlen(c_val) + 1), 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_1X_KEY, c_val, (strlen(c_val) + 1),
+ 0, 0))
goto _fail_;
- /* IEEE802.1X Server Address Configuration */
- /* ------------------------------------------------------------------ */
- /* Configuration : Radius Server IP Address */
- /* Values to set : Any valid IP Address */
- /* ------------------------------------------------------------------ */
c_val[0] = 192;
c_val[1] = 168;
c_val[2] = 1;
c_val[3] = 112;
- if (!wilc_wlan_cfg_set(0, WID_1X_SERV_ADDR, c_val, 4, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_1X_SERV_ADDR, c_val, 4, 0, 0))
goto _fail_;
c_val[0] = 3;
- if (!wilc_wlan_cfg_set(0, WID_LISTEN_INTERVAL, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_LISTEN_INTERVAL, c_val, 1, 0, 0))
goto _fail_;
c_val[0] = 3;
- if (!wilc_wlan_cfg_set(0, WID_DTIM_PERIOD, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_DTIM_PERIOD, c_val, 1, 0, 0))
goto _fail_;
c_val[0] = NORMAL_ACK;
- if (!wilc_wlan_cfg_set(0, WID_ACK_POLICY, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_ACK_POLICY, c_val, 1, 0, 0))
goto _fail_;
c_val[0] = 0;
- if (!wilc_wlan_cfg_set(0, WID_USER_CONTROL_ON_TX_POWER, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_USER_CONTROL_ON_TX_POWER, c_val, 1,
+ 0, 0))
goto _fail_;
c_val[0] = 48;
- if (!wilc_wlan_cfg_set(0, WID_TX_POWER_LEVEL_11A, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_TX_POWER_LEVEL_11A, c_val, 1, 0,
+ 0))
goto _fail_;
c_val[0] = 28;
- if (!wilc_wlan_cfg_set(0, WID_TX_POWER_LEVEL_11B, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_TX_POWER_LEVEL_11B, c_val, 1, 0,
+ 0))
goto _fail_;
- /* Beacon Interval */
- /* -------------------------------------------------------------------- */
- /* Configuration : Sets the beacon interval value */
- /* Values to set : Any 16-bit value */
- /* -------------------------------------------------------------------- */
-
*((int *)c_val) = 100;
- if (!wilc_wlan_cfg_set(0, WID_BEACON_INTERVAL, c_val, 2, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_BEACON_INTERVAL, c_val, 2, 0, 0))
goto _fail_;
c_val[0] = REKEY_DISABLE;
- if (!wilc_wlan_cfg_set(0, WID_REKEY_POLICY, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_REKEY_POLICY, c_val, 1, 0, 0))
goto _fail_;
- /* Rekey Time (s) (Used only when the Rekey policy is 2 or 4) */
- /* -------------------------------------------------------------------- */
- /* Configuration : Sets the Rekey Time (s) */
- /* Values to set : 32-bit value */
- /* -------------------------------------------------------------------- */
*((int *)c_val) = 84600;
- if (!wilc_wlan_cfg_set(0, WID_REKEY_PERIOD, c_val, 4, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_REKEY_PERIOD, c_val, 4, 0, 0))
goto _fail_;
- /* Rekey Packet Count (in 1000s; used when Rekey Policy is 3) */
- /* -------------------------------------------------------------------- */
- /* Configuration : Sets Rekey Group Packet count */
- /* Values to set : 32-bit Value */
- /* -------------------------------------------------------------------- */
*((int *)c_val) = 500;
- if (!wilc_wlan_cfg_set(0, WID_REKEY_PACKET_COUNT, c_val, 4, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_REKEY_PACKET_COUNT, c_val, 4, 0,
+ 0))
goto _fail_;
c_val[0] = 1;
- if (!wilc_wlan_cfg_set(0, WID_SHORT_SLOT_ALLOWED, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_SHORT_SLOT_ALLOWED, c_val, 1, 0,
+ 0))
goto _fail_;
c_val[0] = G_SELF_CTS_PROT;
- if (!wilc_wlan_cfg_set(0, WID_11N_ERP_PROT_TYPE, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_11N_ERP_PROT_TYPE, c_val, 1, 0, 0))
goto _fail_;
- c_val[0] = 1; /* Enable N */
- if (!wilc_wlan_cfg_set(0, WID_11N_ENABLE, c_val, 1, 0, 0))
+ c_val[0] = 1;
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_11N_ENABLE, c_val, 1, 0, 0))
goto _fail_;
c_val[0] = HT_MIXED_MODE;
- if (!wilc_wlan_cfg_set(0, WID_11N_OPERATING_MODE, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_11N_OPERATING_MODE, c_val, 1, 0,
+ 0))
goto _fail_;
- c_val[0] = 1; /* TXOP Prot disable in N mode: No RTS-CTS on TX A-MPDUs to save air-time. */
- if (!wilc_wlan_cfg_set(0, WID_11N_TXOP_PROT_DISABLE, c_val, 1, 0, 0))
+ c_val[0] = 1;
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_11N_TXOP_PROT_DISABLE, c_val, 1, 0,
+ 0))
goto _fail_;
memcpy(c_val, mac_add, 6);
- if (!wilc_wlan_cfg_set(0, WID_MAC_ADDR, c_val, 6, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_MAC_ADDR, c_val, 6, 0, 0))
goto _fail_;
- /**
- * AP only
- **/
c_val[0] = DETECT_PROTECT_REPORT;
- if (!wilc_wlan_cfg_set(0, WID_11N_OBSS_NONHT_DETECTION, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_11N_OBSS_NONHT_DETECTION, c_val, 1,
+ 0, 0))
goto _fail_;
c_val[0] = RTS_CTS_NONHT_PROT;
- if (!wilc_wlan_cfg_set(0, WID_11N_HT_PROT_TYPE, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_11N_HT_PROT_TYPE, c_val, 1, 0, 0))
goto _fail_;
c_val[0] = 0;
- if (!wilc_wlan_cfg_set(0, WID_11N_RIFS_PROT_ENABLE, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_11N_RIFS_PROT_ENABLE, c_val, 1, 0,
+ 0))
goto _fail_;
c_val[0] = MIMO_MODE;
- if (!wilc_wlan_cfg_set(0, WID_11N_SMPS_MODE, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_11N_SMPS_MODE, c_val, 1, 0, 0))
goto _fail_;
c_val[0] = 7;
- if (!wilc_wlan_cfg_set(0, WID_11N_CURRENT_TX_MCS, c_val, 1, 0, 0))
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_11N_CURRENT_TX_MCS, c_val, 1, 0,
+ 0))
goto _fail_;
- c_val[0] = 1; /* Enable N with immediate block ack. */
- if (!wilc_wlan_cfg_set(0, WID_11N_IMMEDIATE_BA_ENABLED, c_val, 1, 1, 1))
+ c_val[0] = 1;
+ if (!wilc_wlan_cfg_set(wilc, 0, WID_11N_IMMEDIATE_BA_ENABLED, c_val, 1,
+ 1, 1))
goto _fail_;
return 0;
@@ -884,14 +732,13 @@ _fail_:
return -1;
}
-/**************************/
void wilc1000_wlan_deinit(struct net_device *dev)
{
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct wilc *wl;
- nic = netdev_priv(dev);
- wl = nic->wilc;
+ vif = netdev_priv(dev);
+ wl = vif->wilc;
if (!wl) {
netdev_err(dev, "wl is NULL\n");
@@ -901,20 +748,14 @@ void wilc1000_wlan_deinit(struct net_device *dev)
if (wl->initialized) {
netdev_info(dev, "Deinitializing wilc1000...\n");
-#if defined(PLAT_ALLWINNER_A20) || defined(PLAT_ALLWINNER_A23) || defined(PLAT_ALLWINNER_A31)
- /* johnny : remove */
- PRINT_D(INIT_DBG, "skip wilc_bus_set_default_speed\n");
-#else
- wilc_bus_set_default_speed();
-#endif
-
PRINT_D(INIT_DBG, "Disabling IRQ\n");
-#ifdef WILC_SDIO
- mutex_lock(&wl->hif_cs);
- disable_sdio_interrupt();
- mutex_unlock(&wl->hif_cs);
-#endif
- if (&wl->txq_event != NULL)
+ if (!wl->dev_irq_num &&
+ wl->hif_func->disable_interrupt) {
+ mutex_lock(&wl->hif_cs);
+ wl->hif_func->disable_interrupt(wl);
+ mutex_unlock(&wl->hif_cs);
+ }
+ if (&wl->txq_event)
up(&wl->txq_event);
PRINT_D(INIT_DBG, "Deinitializing Threads\n");
@@ -923,25 +764,25 @@ void wilc1000_wlan_deinit(struct net_device *dev)
PRINT_D(INIT_DBG, "Deinitializing IRQ\n");
deinit_irq(dev);
- wilc_wlan_stop();
+ wilc_wlan_stop(wl);
PRINT_D(INIT_DBG, "Deinitializing WILC Wlan\n");
wilc_wlan_cleanup(dev);
-#if (defined WILC_SDIO) && (!defined WILC_SDIO_IRQ_GPIO)
- #if defined(PLAT_ALLWINNER_A20) || defined(PLAT_ALLWINNER_A23) || defined(PLAT_ALLWINNER_A31)
- PRINT_D(INIT_DBG, "Disabling IRQ 2\n");
-
- mutex_lock(&wl->hif_cs);
- disable_sdio_interrupt();
- mutex_unlock(&wl->hif_cs);
- #endif
+#if defined(PLAT_ALLWINNER_A20) || defined(PLAT_ALLWINNER_A23) || defined(PLAT_ALLWINNER_A31)
+ if (!wl->dev_irq_num &&
+ wl->hif_func->disable_interrupt) {
+
+ PRINT_D(INIT_DBG, "Disabling IRQ 2\n");
+
+ mutex_lock(&wl->hif_cs);
+ wl->hif_func->disable_interrupt(wl);
+ mutex_unlock(&wl->hif_cs);
+ }
#endif
- /*De-Initialize locks*/
PRINT_D(INIT_DBG, "Deinitializing Locks\n");
wlan_deinit_locks(dev);
- /* announce that wilc1000 is not initialized */
wl->initialized = false;
PRINT_D(INIT_DBG, "wilc1000 deinitialization Done\n");
@@ -951,13 +792,13 @@ void wilc1000_wlan_deinit(struct net_device *dev)
}
}
-int wlan_init_locks(struct net_device *dev)
+static int wlan_init_locks(struct net_device *dev)
{
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct wilc *wl;
- nic = netdev_priv(dev);
- wl = nic->wilc;
+ vif = netdev_priv(dev);
+ wl = vif->wilc;
PRINT_D(INIT_DBG, "Initializing Locks ...\n");
@@ -979,105 +820,68 @@ int wlan_init_locks(struct net_device *dev)
static int wlan_deinit_locks(struct net_device *dev)
{
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct wilc *wilc;
- nic = netdev_priv(dev);
- wilc = nic->wilc;
+ vif = netdev_priv(dev);
+ wilc = vif->wilc;
PRINT_D(INIT_DBG, "De-Initializing Locks\n");
- if (&wilc->hif_cs != NULL)
+ if (&wilc->hif_cs)
mutex_destroy(&wilc->hif_cs);
- if (&wilc->rxq_cs != NULL)
+ if (&wilc->rxq_cs)
mutex_destroy(&wilc->rxq_cs);
return 0;
}
-void linux_to_wlan(wilc_wlan_inp_t *nwi, struct wilc *nic)
-{
-
- PRINT_D(INIT_DBG, "Linux to Wlan services ...\n");
-
- nwi->os_context.os_private = (void *)nic;
-#ifdef WILC_SDIO
- nwi->io_func.io_type = HIF_SDIO;
- nwi->io_func.io_init = linux_sdio_init;
- nwi->io_func.io_deinit = linux_sdio_deinit;
- nwi->io_func.u.sdio.sdio_cmd52 = linux_sdio_cmd52;
- nwi->io_func.u.sdio.sdio_cmd53 = linux_sdio_cmd53;
- nwi->io_func.u.sdio.sdio_set_max_speed = linux_sdio_set_max_speed;
- nwi->io_func.u.sdio.sdio_set_default_speed = linux_sdio_set_default_speed;
-#else
- nwi->io_func.io_type = HIF_SPI;
- nwi->io_func.io_init = linux_spi_init;
- nwi->io_func.io_deinit = linux_spi_deinit;
- nwi->io_func.u.spi.spi_tx = linux_spi_write;
- nwi->io_func.u.spi.spi_rx = linux_spi_read;
- nwi->io_func.u.spi.spi_trx = linux_spi_write_read;
- nwi->io_func.u.spi.spi_max_speed = linux_spi_set_max_speed;
-#endif
-}
-
-int wlan_initialize_threads(struct net_device *dev)
+static int wlan_initialize_threads(struct net_device *dev)
{
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct wilc *wilc;
- int ret = 0;
- nic = netdev_priv(dev);
- wilc = nic->wilc;
+ vif = netdev_priv(dev);
+ wilc = vif->wilc;
PRINT_D(INIT_DBG, "Initializing Threads ...\n");
-
- /* create tx task */
PRINT_D(INIT_DBG, "Creating kthread for transmission\n");
wilc->txq_thread = kthread_run(linux_wlan_txq_task, (void *)dev,
"K_TXQ_TASK");
if (!wilc->txq_thread) {
PRINT_ER("couldn't create TXQ thread\n");
- ret = -ENOBUFS;
- goto _fail_2;
+ wilc->close = 0;
+ return -ENOBUFS;
}
- /* wait for TXQ task to start. */
down(&wilc->txq_thread_started);
return 0;
-
-_fail_2:
- /*De-Initialize 2nd thread*/
- wilc->close = 0;
- return ret;
}
static void wlan_deinitialize_threads(struct net_device *dev)
{
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct wilc *wl;
-
- nic = netdev_priv(dev);
- wl = nic->wilc;
+ vif = netdev_priv(dev);
+ wl = vif->wilc;
wl->close = 1;
PRINT_D(INIT_DBG, "Deinitializing Threads\n");
- if (&wl->txq_event != NULL)
+ if (&wl->txq_event)
up(&wl->txq_event);
- if (wl->txq_thread != NULL) {
+ if (wl->txq_thread) {
kthread_stop(wl->txq_thread);
wl->txq_thread = NULL;
}
}
-int wilc1000_wlan_init(struct net_device *dev, perInterface_wlan_t *p_nic)
+int wilc1000_wlan_init(struct net_device *dev, struct wilc_vif *vif)
{
- wilc_wlan_inp_t nwi;
- perInterface_wlan_t *nic = p_nic;
int ret = 0;
- struct wilc *wl = nic->wilc;
+ struct wilc *wl = vif->wilc;
if (!wl->initialized) {
wl->mac_status = WILC_MAC_STATUS_INIT;
@@ -1085,22 +889,18 @@ int wilc1000_wlan_init(struct net_device *dev, perInterface_wlan_t *p_nic)
wlan_init_locks(dev);
- linux_to_wlan(&nwi, wl);
-
- ret = wilc_wlan_init(&nwi);
+ ret = wilc_wlan_init(dev);
if (ret < 0) {
PRINT_ER("Initializing WILC_Wlan FAILED\n");
ret = -EIO;
goto _fail_locks_;
}
-#if (!defined WILC_SDIO) || (defined WILC_SDIO_IRQ_GPIO)
- if (init_irq(dev)) {
+ if (wl->gpio >= 0 && init_irq(dev)) {
PRINT_ER("couldn't initialize IRQ\n");
ret = -EIO;
goto _fail_locks_;
}
-#endif
ret = wlan_initialize_threads(dev);
if (ret < 0) {
@@ -1109,39 +909,35 @@ int wilc1000_wlan_init(struct net_device *dev, perInterface_wlan_t *p_nic)
goto _fail_wilc_wlan_;
}
-#if (defined WILC_SDIO) && (!defined WILC_SDIO_IRQ_GPIO)
- if (enable_sdio_interrupt()) {
+ if (!wl->dev_irq_num &&
+ wl->hif_func->enable_interrupt &&
+ wl->hif_func->enable_interrupt(wl)) {
PRINT_ER("couldn't initialize IRQ\n");
ret = -EIO;
goto _fail_irq_init_;
}
-#endif
- if (linux_wlan_get_firmware(nic)) {
+ if (wilc_wlan_get_firmware(dev)) {
PRINT_ER("Can't get firmware\n");
ret = -EIO;
goto _fail_irq_enable_;
}
- /*Download firmware*/
- ret = linux_wlan_firmware_download(wl);
+ ret = wilc1000_firmware_download(dev);
if (ret < 0) {
PRINT_ER("Failed to download firmware\n");
ret = -EIO;
goto _fail_irq_enable_;
}
- /* Start firmware*/
- ret = linux_wlan_start_firmware(nic);
+ ret = linux_wlan_start_firmware(dev);
if (ret < 0) {
PRINT_ER("Failed to start firmware\n");
ret = -EIO;
goto _fail_irq_enable_;
}
- wilc_bus_set_max_speed();
-
- if (wilc_wlan_cfg_get(1, WID_FIRMWARE_VERSION, 1, 0)) {
+ if (wilc_wlan_cfg_get(wl, 1, WID_FIRMWARE_VERSION, 1, 0)) {
int size;
char Firmware_ver[20];
@@ -1151,7 +947,6 @@ int wilc1000_wlan_init(struct net_device *dev, perInterface_wlan_t *p_nic)
Firmware_ver[size] = '\0';
PRINT_D(INIT_DBG, "***** Firmware Ver = %s *******\n", Firmware_ver);
}
- /* Initialize firmware with default configuration */
ret = linux_wlan_init_test_config(dev, wl);
if (ret < 0) {
@@ -1161,20 +956,19 @@ int wilc1000_wlan_init(struct net_device *dev, perInterface_wlan_t *p_nic)
}
wl->initialized = true;
- return 0; /*success*/
+ return 0;
_fail_fw_start_:
- wilc_wlan_stop();
+ wilc_wlan_stop(wl);
_fail_irq_enable_:
-#if (defined WILC_SDIO) && (!defined WILC_SDIO_IRQ_GPIO)
- disable_sdio_interrupt();
+ if (!wl->dev_irq_num &&
+ wl->hif_func->disable_interrupt)
+ wl->hif_func->disable_interrupt(wl);
_fail_irq_init_:
-#endif
-#if (!defined WILC_SDIO) || (defined WILC_SDIO_IRQ_GPIO)
- deinit_irq(dev);
+ if (wl->dev_irq_num)
+ deinit_irq(dev);
-#endif
wlan_deinitialize_threads(dev);
_fail_wilc_wlan_:
wilc_wlan_cleanup(dev);
@@ -1187,44 +981,36 @@ _fail_locks_:
return ret;
}
-/*
- * - this function will be called automatically by OS when module inserted.
- */
-
-int mac_init_fn(struct net_device *ndev)
+static int mac_init_fn(struct net_device *ndev)
{
-
- /*Why we do this !!!*/
- netif_start_queue(ndev); /* ma */
- netif_stop_queue(ndev); /* ma */
+ netif_start_queue(ndev);
+ netif_stop_queue(ndev);
return 0;
}
-/* This fn is called, when this device is setup using ifconfig */
-int mac_open(struct net_device *ndev)
+int wilc_mac_open(struct net_device *ndev)
{
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
+ struct wilc *wilc;
- /*No need for setting mac address here anymore,*/
- /*Just set it in init_test_config()*/
unsigned char mac_add[ETH_ALEN] = {0};
int ret = 0;
int i = 0;
struct wilc_priv *priv;
struct wilc *wl;
- nic = netdev_priv(ndev);
- wl = nic->wilc;
+ vif = netdev_priv(ndev);
+ wl = vif->wilc;
-#ifdef WILC_SPI
- if (!wl|| !wl->wilc_spidev) {
+ if (!wl|| !wl->dev) {
netdev_err(ndev, "wilc1000: SPI device not ready\n");
return -ENODEV;
}
-#endif
- nic = netdev_priv(ndev);
- priv = wiphy_priv(nic->wilc_netdev->ieee80211_ptr->wiphy);
+
+ vif = netdev_priv(ndev);
+ wilc = vif->wilc;
+ priv = wiphy_priv(vif->ndev->ieee80211_ptr->wiphy);
PRINT_D(INIT_DBG, "MAC OPEN[%p]\n", ndev);
ret = wilc_init_host_int(ndev);
@@ -1234,153 +1020,143 @@ int mac_open(struct net_device *ndev)
return ret;
}
- /*initialize platform*/
PRINT_D(INIT_DBG, "*** re-init ***\n");
- ret = wilc1000_wlan_init(ndev, nic);
+ ret = wilc1000_wlan_init(ndev, vif);
if (ret < 0) {
PRINT_ER("Failed to initialize wilc1000\n");
wilc_deinit_host_int(ndev);
return ret;
}
- Set_machw_change_vir_if(ndev, false);
+ wilc_set_machw_change_vir_if(ndev, false);
- host_int_get_MacAddress(priv->hWILCWFIDrv, mac_add);
+ wilc_get_mac_address(vif, mac_add);
PRINT_D(INIT_DBG, "Mac address: %pM\n", mac_add);
- /* loop through the NUM of supported devices and set the MAC address */
for (i = 0; i < wl->vif_num; i++) {
- if (ndev == wl->vif[i].ndev) {
- memcpy(wl->vif[i].src_addr, mac_add, ETH_ALEN);
- wl->vif[i].hif_drv = priv->hWILCWFIDrv;
+ if (ndev == wl->vif[i]->ndev) {
+ memcpy(wl->vif[i]->src_addr, mac_add, ETH_ALEN);
break;
}
}
- /* TODO: get MAC address whenever the source is EPROM - hardcoded and copy it to ndev*/
- memcpy(ndev->dev_addr, wl->vif[i].src_addr, ETH_ALEN);
+ memcpy(ndev->dev_addr, wl->vif[i]->src_addr, ETH_ALEN);
if (!is_valid_ether_addr(ndev->dev_addr)) {
PRINT_ER("Error: Wrong MAC address\n");
- ret = -EINVAL;
- goto _err_;
+ wilc_deinit_host_int(ndev);
+ wilc1000_wlan_deinit(ndev);
+ return -EINVAL;
}
- wilc_mgmt_frame_register(nic->wilc_netdev->ieee80211_ptr->wiphy, nic->wilc_netdev->ieee80211_ptr,
- nic->g_struct_frame_reg[0].frame_type, nic->g_struct_frame_reg[0].reg);
- wilc_mgmt_frame_register(nic->wilc_netdev->ieee80211_ptr->wiphy, nic->wilc_netdev->ieee80211_ptr,
- nic->g_struct_frame_reg[1].frame_type, nic->g_struct_frame_reg[1].reg);
+ wilc_mgmt_frame_register(vif->ndev->ieee80211_ptr->wiphy,
+ vif->ndev->ieee80211_ptr,
+ vif->g_struct_frame_reg[0].frame_type,
+ vif->g_struct_frame_reg[0].reg);
+ wilc_mgmt_frame_register(vif->ndev->ieee80211_ptr->wiphy,
+ vif->ndev->ieee80211_ptr,
+ vif->g_struct_frame_reg[1].frame_type,
+ vif->g_struct_frame_reg[1].reg);
netif_wake_queue(ndev);
wl->open_ifcs++;
- nic->mac_opened = 1;
+ vif->mac_opened = 1;
return 0;
-
-_err_:
- wilc_deinit_host_int(ndev);
- wilc1000_wlan_deinit(ndev);
- return ret;
}
-struct net_device_stats *mac_stats(struct net_device *dev)
+static struct net_device_stats *mac_stats(struct net_device *dev)
{
- perInterface_wlan_t *nic = netdev_priv(dev);
+ struct wilc_vif *vif= netdev_priv(dev);
- return &nic->netstats;
+ return &vif->netstats;
}
-/* Setup the multicast filter */
static void wilc_set_multicast_list(struct net_device *dev)
{
-
struct netdev_hw_addr *ha;
struct wilc_priv *priv;
- struct host_if_drv *pstrWFIDrv;
+ struct host_if_drv *hif_drv;
+ struct wilc_vif *vif;
int i = 0;
priv = wiphy_priv(dev->ieee80211_ptr->wiphy);
- pstrWFIDrv = (struct host_if_drv *)priv->hWILCWFIDrv;
+ vif = netdev_priv(dev);
+ hif_drv = (struct host_if_drv *)priv->hWILCWFIDrv;
if (!dev)
return;
- PRINT_D(INIT_DBG, "Setting Multicast List with count = %d.\n", dev->mc.count);
+ PRINT_D(INIT_DBG, "Setting Multicast List with count = %d.\n",
+ dev->mc.count);
if (dev->flags & IFF_PROMISC) {
- /* Normally, we should configure the chip to retrive all packets
- * but we don't wanna support this right now */
- /* TODO: add promiscuous mode support */
PRINT_D(INIT_DBG, "Set promiscuous mode ON, retrive all packets\n");
return;
}
- /* If there's more addresses than we handle, get all multicast
- * packets and sort them out in software. */
- if ((dev->flags & IFF_ALLMULTI) || (dev->mc.count) > WILC_MULTICAST_TABLE_SIZE) {
+ if ((dev->flags & IFF_ALLMULTI) ||
+ (dev->mc.count) > WILC_MULTICAST_TABLE_SIZE) {
PRINT_D(INIT_DBG, "Disable multicast filter, retrive all multicast packets\n");
- /* get all multicast packets */
- host_int_setup_multicast_filter(pstrWFIDrv, false, 0);
+ wilc_setup_multicast_filter(vif, false, 0);
return;
}
- /* No multicast? Just get our own stuff */
if ((dev->mc.count) == 0) {
PRINT_D(INIT_DBG, "Enable multicast filter, retrive directed packets only.\n");
- host_int_setup_multicast_filter(pstrWFIDrv, true, 0);
+ wilc_setup_multicast_filter(vif, true, 0);
return;
}
- /* Store all of the multicast addresses in the hardware filter */
- netdev_for_each_mc_addr(ha, dev)
- {
- memcpy(gau8MulticastMacAddrList[i], ha->addr, ETH_ALEN);
+ netdev_for_each_mc_addr(ha, dev) {
+ memcpy(wilc_multicast_mac_addr_list[i], ha->addr, ETH_ALEN);
PRINT_D(INIT_DBG, "Entry[%d]: %x:%x:%x:%x:%x:%x\n", i,
- gau8MulticastMacAddrList[i][0], gau8MulticastMacAddrList[i][1], gau8MulticastMacAddrList[i][2], gau8MulticastMacAddrList[i][3], gau8MulticastMacAddrList[i][4], gau8MulticastMacAddrList[i][5]);
+ wilc_multicast_mac_addr_list[i][0],
+ wilc_multicast_mac_addr_list[i][1],
+ wilc_multicast_mac_addr_list[i][2],
+ wilc_multicast_mac_addr_list[i][3],
+ wilc_multicast_mac_addr_list[i][4],
+ wilc_multicast_mac_addr_list[i][5]);
i++;
}
- host_int_setup_multicast_filter(pstrWFIDrv, true, (dev->mc.count));
+ wilc_setup_multicast_filter(vif, true, (dev->mc.count));
return;
-
}
static void linux_wlan_tx_complete(void *priv, int status)
{
-
struct tx_complete_data *pv_data = (struct tx_complete_data *)priv;
if (status == 1)
PRINT_D(TX_DBG, "Packet sent successfully - Size = %d - Address = %p - SKB = %p\n", pv_data->size, pv_data->buff, pv_data->skb);
else
PRINT_D(TX_DBG, "Couldn't send packet - Size = %d - Address = %p - SKB = %p\n", pv_data->size, pv_data->buff, pv_data->skb);
- /* Free the SK Buffer, its work is done */
dev_kfree_skb(pv_data->skb);
kfree(pv_data);
}
-int mac_xmit(struct sk_buff *skb, struct net_device *ndev)
+int wilc_mac_xmit(struct sk_buff *skb, struct net_device *ndev)
{
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct tx_complete_data *tx_data = NULL;
- int QueueCount;
- char *pu8UdpBuffer;
+ int queue_count;
+ char *udp_buf;
struct iphdr *ih;
struct ethhdr *eth_h;
struct wilc *wilc;
- nic = netdev_priv(ndev);
- wilc = nic->wilc;
+ vif = netdev_priv(ndev);
+ wilc = vif->wilc;
PRINT_D(TX_DBG, "Sending packet just received from TCP/IP\n");
- /* Stop the network interface queue */
if (skb->dev != ndev) {
PRINT_ER("Packet not destined to this device\n");
return 0;
}
- tx_data = kmalloc(sizeof(struct tx_complete_data), GFP_ATOMIC);
- if (tx_data == NULL) {
+ tx_data = kmalloc(sizeof(*tx_data), GFP_ATOMIC);
+ if (!tx_data) {
PRINT_ER("Failed to allocate memory for tx_data structure\n");
dev_kfree_skb(skb);
netif_wake_queue(ndev);
@@ -1395,59 +1171,55 @@ int mac_xmit(struct sk_buff *skb, struct net_device *ndev)
if (eth_h->h_proto == 0x8e88)
PRINT_D(INIT_DBG, "EAPOL transmitted\n");
- /*get source and dest ip addresses*/
ih = (struct iphdr *)(skb->data + sizeof(struct ethhdr));
- pu8UdpBuffer = (char *)ih + sizeof(struct iphdr);
- if ((pu8UdpBuffer[1] == 68 && pu8UdpBuffer[3] == 67) || (pu8UdpBuffer[1] == 67 && pu8UdpBuffer[3] == 68))
- PRINT_D(GENERIC_DBG, "DHCP Message transmitted, type:%x %x %x\n", pu8UdpBuffer[248], pu8UdpBuffer[249], pu8UdpBuffer[250]);
+ udp_buf = (char *)ih + sizeof(struct iphdr);
+ if ((udp_buf[1] == 68 && udp_buf[3] == 67) ||
+ (udp_buf[1] == 67 && udp_buf[3] == 68))
+ PRINT_D(GENERIC_DBG, "DHCP Message transmitted, type:%x %x %x\n",
+ udp_buf[248], udp_buf[249], udp_buf[250]);
PRINT_D(TX_DBG, "Sending packet - Size = %d - Address = %p - SKB = %p\n", tx_data->size, tx_data->buff, tx_data->skb);
-
- /* Send packet to MAC HW - for now the tx_complete function will be just status
- * indicator. still not sure if I need to suspend host transmission till the tx_complete
- * function called or not?
- * allocated buffer will be freed in tx_complete function.
- */
PRINT_D(TX_DBG, "Adding tx packet to TX Queue\n");
- nic->netstats.tx_packets++;
- nic->netstats.tx_bytes += tx_data->size;
- tx_data->pBssid = wilc->vif[nic->u8IfIdx].bssid;
- QueueCount = wilc_wlan_txq_add_net_pkt(ndev, (void *)tx_data,
- tx_data->buff, tx_data->size,
- linux_wlan_tx_complete);
-
- if (QueueCount > FLOW_CONTROL_UPPER_THRESHOLD) {
- netif_stop_queue(wilc->vif[0].ndev);
- netif_stop_queue(wilc->vif[1].ndev);
+ vif->netstats.tx_packets++;
+ vif->netstats.tx_bytes += tx_data->size;
+ tx_data->pBssid = wilc->vif[vif->u8IfIdx]->bssid;
+ queue_count = wilc_wlan_txq_add_net_pkt(ndev, (void *)tx_data,
+ tx_data->buff, tx_data->size,
+ linux_wlan_tx_complete);
+
+ if (queue_count > FLOW_CONTROL_UPPER_THRESHOLD) {
+ netif_stop_queue(wilc->vif[0]->ndev);
+ netif_stop_queue(wilc->vif[1]->ndev);
}
return 0;
}
-int mac_close(struct net_device *ndev)
+int wilc_mac_close(struct net_device *ndev)
{
struct wilc_priv *priv;
- perInterface_wlan_t *nic;
- struct host_if_drv *pstrWFIDrv;
+ struct wilc_vif *vif;
+ struct host_if_drv *hif_drv;
struct wilc *wl;
- nic = netdev_priv(ndev);
+ vif = netdev_priv(ndev);
- if ((nic == NULL) || (nic->wilc_netdev == NULL) || (nic->wilc_netdev->ieee80211_ptr == NULL) || (nic->wilc_netdev->ieee80211_ptr->wiphy == NULL)) {
- PRINT_ER("nic = NULL\n");
+ if (!vif || !vif->ndev || !vif->ndev->ieee80211_ptr ||
+ !vif->ndev->ieee80211_ptr->wiphy) {
+ PRINT_ER("vif = NULL\n");
return 0;
}
- priv = wiphy_priv(nic->wilc_netdev->ieee80211_ptr->wiphy);
- wl = nic->wilc;
+ priv = wiphy_priv(vif->ndev->ieee80211_ptr->wiphy);
+ wl = vif->wilc;
- if (priv == NULL) {
+ if (!priv) {
PRINT_ER("priv = NULL\n");
return 0;
}
- pstrWFIDrv = (struct host_if_drv *)priv->hWILCWFIDrv;
+ hif_drv = (struct host_if_drv *)priv->hWILCWFIDrv;
PRINT_D(GENERIC_DBG, "Mac close\n");
@@ -1456,8 +1228,8 @@ int mac_close(struct net_device *ndev)
return 0;
}
- if (pstrWFIDrv == NULL) {
- PRINT_ER("pstrWFIDrv = NULL\n");
+ if (!hif_drv) {
+ PRINT_ER("hif_drv = NULL\n");
return 0;
}
@@ -1468,11 +1240,10 @@ int mac_close(struct net_device *ndev)
return 0;
}
- if (nic->wilc_netdev != NULL) {
- /* Stop the network interface queue */
- netif_stop_queue(nic->wilc_netdev);
+ if (vif->ndev) {
+ netif_stop_queue(vif->ndev);
- wilc_deinit_host_int(nic->wilc_netdev);
+ wilc_deinit_host_int(vif->ndev);
}
if (wl->open_ifcs == 0) {
@@ -1483,59 +1254,54 @@ int mac_close(struct net_device *ndev)
}
up(&close_exit_sync);
- nic->mac_opened = 0;
+ vif->mac_opened = 0;
return 0;
}
-int mac_ioctl(struct net_device *ndev, struct ifreq *req, int cmd)
+static int mac_ioctl(struct net_device *ndev, struct ifreq *req, int cmd)
{
-
u8 *buff = NULL;
s8 rssi;
u32 size = 0, length = 0;
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct wilc_priv *priv;
- s32 s32Error = 0;
+ s32 ret = 0;
struct wilc *wilc;
- /* struct iwreq *wrq = (struct iwreq *) req; // tony moved to case SIOCSIWPRIV */
- nic = netdev_priv(ndev);
- wilc = nic->wilc;
+ vif = netdev_priv(ndev);
+ wilc = vif->wilc;
if (!wilc->initialized)
return 0;
switch (cmd) {
-
- /* ]] 2013-06-24 */
case SIOCSIWPRIV:
{
- struct iwreq *wrq = (struct iwreq *) req; /* added by tony */
+ struct iwreq *wrq = (struct iwreq *) req;
size = wrq->u.data.length;
if (size && wrq->u.data.pointer) {
-
- buff = memdup_user(wrq->u.data.pointer, wrq->u.data.length);
+ buff = memdup_user(wrq->u.data.pointer,
+ wrq->u.data.length);
if (IS_ERR(buff))
return PTR_ERR(buff);
if (strncasecmp(buff, "RSSI", length) == 0) {
- priv = wiphy_priv(nic->wilc_netdev->ieee80211_ptr->wiphy);
- s32Error = host_int_get_rssi(priv->hWILCWFIDrv, &(rssi));
- if (s32Error)
+ priv = wiphy_priv(vif->ndev->ieee80211_ptr->wiphy);
+ ret = wilc_get_rssi(vif, &rssi);
+ if (ret)
PRINT_ER("Failed to send get rssi param's message queue ");
PRINT_INFO(GENERIC_DBG, "RSSI :%d\n", rssi);
- /*Rounding up the rssi negative value*/
rssi += 5;
snprintf(buff, size, "rssi %d", rssi);
if (copy_to_user(wrq->u.data.pointer, buff, size)) {
PRINT_ER("%s: failed to copy data to user buffer\n", __func__);
- s32Error = -EFAULT;
+ ret = -EFAULT;
goto done;
}
}
@@ -1546,7 +1312,7 @@ int mac_ioctl(struct net_device *ndev, struct ifreq *req, int cmd)
default:
{
PRINT_INFO(GENERIC_DBG, "Command - %d - has been received\n", cmd);
- s32Error = -EOPNOTSUPP;
+ ret = -EOPNOTSUPP;
goto done;
}
}
@@ -1555,64 +1321,47 @@ done:
kfree(buff);
- return s32Error;
+ return ret;
}
-void frmw_to_linux(struct wilc *wilc, u8 *buff, u32 size, u32 pkt_offset)
+void wilc_frmw_to_linux(struct wilc *wilc, u8 *buff, u32 size, u32 pkt_offset)
{
-
unsigned int frame_len = 0;
int stats;
unsigned char *buff_to_send = NULL;
struct sk_buff *skb;
struct net_device *wilc_netdev;
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
- wilc_netdev = GetIfHandler(wilc, buff);
- if (wilc_netdev == NULL)
+ wilc_netdev = get_if_handler(wilc, buff);
+ if (!wilc_netdev)
return;
buff += pkt_offset;
- nic = netdev_priv(wilc_netdev);
+ vif = netdev_priv(wilc_netdev);
if (size > 0) {
-
frame_len = size;
buff_to_send = buff;
- /* Need to send the packet up to the host, allocate a skb buffer */
skb = dev_alloc_skb(frame_len);
- if (skb == NULL) {
+ if (!skb) {
PRINT_ER("Low memory - packet droped\n");
return;
}
- if (wilc == NULL || wilc_netdev == NULL)
+ if (!wilc || !wilc_netdev)
PRINT_ER("wilc_netdev in wilc is NULL");
skb->dev = wilc_netdev;
- if (skb->dev == NULL)
+ if (!skb->dev)
PRINT_ER("skb->dev is NULL\n");
- /*
- * for(i=0;i<40;i++)
- * {
- * if(i<frame_len)
- * WILC_PRINTF("buff_to_send[%d]=%2x\n",i,buff_to_send[i]);
- *
- * }*/
-
- /* skb_put(skb, frame_len); */
memcpy(skb_put(skb, frame_len), buff_to_send, frame_len);
- /* WILC_PRINTF("After MEM_CPY\n"); */
-
- /* nic = netdev_priv(wilc_netdev); */
-
skb->protocol = eth_type_trans(skb, wilc_netdev);
- /* Send the packet to the stack by giving it to the bridge */
- nic->netstats.rx_packets++;
- nic->netstats.rx_bytes += frame_len;
+ vif->netstats.rx_packets++;
+ vif->netstats.rx_bytes += frame_len;
skb->ip_summed = CHECKSUM_UNNECESSARY;
stats = netif_rx(skb);
PRINT_D(RX_DBG, "netif_rx ret value is: %d\n", stats);
@@ -1622,211 +1371,132 @@ void frmw_to_linux(struct wilc *wilc, u8 *buff, u32 size, u32 pkt_offset)
void WILC_WFI_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size)
{
int i = 0;
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
- /*Pass the frame on the monitor interface, if any.*/
- /*Otherwise, pass it on p2p0 netdev, if registered on it*/
for (i = 0; i < wilc->vif_num; i++) {
- nic = netdev_priv(wilc->vif[i].ndev);
- if (nic->monitor_flag) {
+ vif = netdev_priv(wilc->vif[i]->ndev);
+ if (vif->monitor_flag) {
WILC_WFI_monitor_rx(buff, size);
return;
}
}
- nic = netdev_priv(wilc->vif[1].ndev); /* p2p0 */
- if ((buff[0] == nic->g_struct_frame_reg[0].frame_type && nic->g_struct_frame_reg[0].reg) ||
- (buff[0] == nic->g_struct_frame_reg[1].frame_type && nic->g_struct_frame_reg[1].reg))
- WILC_WFI_p2p_rx(wilc->vif[1].ndev, buff, size);
+ vif = netdev_priv(wilc->vif[1]->ndev);
+ if ((buff[0] == vif->g_struct_frame_reg[0].frame_type && vif->g_struct_frame_reg[0].reg) ||
+ (buff[0] == vif->g_struct_frame_reg[1].frame_type && vif->g_struct_frame_reg[1].reg))
+ WILC_WFI_p2p_rx(wilc->vif[1]->ndev, buff, size);
}
-void wl_wlan_cleanup(void)
+void wilc_netdev_cleanup(struct wilc *wilc)
{
int i = 0;
- perInterface_wlan_t *nic[NUM_CONCURRENT_IFC];
+ struct wilc_vif *vif[NUM_CONCURRENT_IFC];
- if (g_linux_wlan &&
- (g_linux_wlan->vif[0].ndev || g_linux_wlan->vif[1].ndev)) {
+ if (wilc && (wilc->vif[0]->ndev || wilc->vif[1]->ndev)) {
unregister_inetaddr_notifier(&g_dev_notifier);
for (i = 0; i < NUM_CONCURRENT_IFC; i++)
- nic[i] = netdev_priv(g_linux_wlan->vif[i].ndev);
+ vif[i] = netdev_priv(wilc->vif[i]->ndev);
}
- if (g_linux_wlan && g_linux_wlan->firmware)
- release_firmware(g_linux_wlan->firmware);
+ if (wilc && wilc->firmware)
+ release_firmware(wilc->firmware);
- if (g_linux_wlan &&
- (g_linux_wlan->vif[0].ndev || g_linux_wlan->vif[1].ndev)) {
- linux_wlan_lock_timeout(&close_exit_sync, 12 * 1000);
+ if (wilc && (wilc->vif[0]->ndev || wilc->vif[1]->ndev)) {
+ wilc_lock_timeout(wilc, &close_exit_sync, 12 * 1000);
for (i = 0; i < NUM_CONCURRENT_IFC; i++)
- if (g_linux_wlan->vif[i].ndev)
- if (nic[i]->mac_opened)
- mac_close(g_linux_wlan->vif[i].ndev);
+ if (wilc->vif[i]->ndev)
+ if (vif[i]->mac_opened)
+ wilc_mac_close(wilc->vif[i]->ndev);
for (i = 0; i < NUM_CONCURRENT_IFC; i++) {
- unregister_netdev(g_linux_wlan->vif[i].ndev);
- wilc_free_wiphy(g_linux_wlan->vif[i].ndev);
- free_netdev(g_linux_wlan->vif[i].ndev);
+ unregister_netdev(wilc->vif[i]->ndev);
+ wilc_free_wiphy(wilc->vif[i]->ndev);
+ free_netdev(wilc->vif[i]->ndev);
}
}
- kfree(g_linux_wlan);
-
-#if defined(WILC_DEBUGFS)
- wilc_debugfs_remove();
-#endif
- linux_wlan_device_detection(0);
- linux_wlan_device_power(0);
+ kfree(wilc);
}
+EXPORT_SYMBOL_GPL(wilc_netdev_cleanup);
-int wilc_netdev_init(struct wilc **wilc)
+int wilc_netdev_init(struct wilc **wilc, struct device *dev, int io_type,
+ int gpio, const struct wilc_hif_func *ops)
{
int i;
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct net_device *ndev;
+ struct wilc *wl;
sema_init(&close_exit_sync, 0);
- /*create the common structure*/
- g_linux_wlan = kzalloc(sizeof(*g_linux_wlan), GFP_KERNEL);
- if (!g_linux_wlan)
+ wl = kzalloc(sizeof(*wl), GFP_KERNEL);
+ if (!wl)
return -ENOMEM;
- *wilc = g_linux_wlan;
+ *wilc = wl;
+ wl->io_type = io_type;
+ wl->gpio = gpio;
+ wl->hif_func = ops;
register_inetaddr_notifier(&g_dev_notifier);
for (i = 0; i < NUM_CONCURRENT_IFC; i++) {
- /*allocate first ethernet device with perinterface_wlan_t as its private data*/
- ndev = alloc_etherdev(sizeof(perInterface_wlan_t));
+ ndev = alloc_etherdev(sizeof(struct wilc_vif));
if (!ndev) {
PRINT_ER("Failed to allocate ethernet dev\n");
return -1;
}
- nic = netdev_priv(ndev);
- memset(nic, 0, sizeof(perInterface_wlan_t));
+ vif = netdev_priv(ndev);
+ memset(vif, 0, sizeof(struct wilc_vif));
- /*Name the Devices*/
- if (i == 0) {
- #if defined(NM73131) /* tony, 2012-09-20 */
- strcpy(ndev->name, "wilc_eth%d");
- #elif defined(PLAT_CLM9722) /* rachel */
- strcpy(ndev->name, "eth%d");
- #else /* PANDA_BOARD, PLAT_ALLWINNER_A10, PLAT_ALLWINNER_A20, PLAT_ALLWINNER_A31, PLAT_AML8726_M3 or PLAT_WMS8304 */
+ if (i == 0)
strcpy(ndev->name, "wlan%d");
- #endif
- } else
+ else
strcpy(ndev->name, "p2p%d");
- nic->u8IfIdx = g_linux_wlan->vif_num;
- nic->wilc_netdev = ndev;
- nic->wilc = *wilc;
- g_linux_wlan->vif[g_linux_wlan->vif_num].ndev = ndev;
- g_linux_wlan->vif_num++;
+ vif->u8IfIdx = wl->vif_num;
+ vif->wilc = *wilc;
+ wl->vif[i] = vif;
+ wl->vif[wl->vif_num]->ndev = ndev;
+ wl->vif_num++;
ndev->netdev_ops = &wilc_netdev_ops;
{
struct wireless_dev *wdev;
- /*Register WiFi*/
- wdev = wilc_create_wiphy(ndev);
+ wdev = wilc_create_wiphy(ndev, dev);
- #ifdef WILC_SDIO
- /* set netdev, tony */
- SET_NETDEV_DEV(ndev, &local_sdio_func->dev);
- #endif
+ if (dev)
+ SET_NETDEV_DEV(ndev, dev);
- if (wdev == NULL) {
+ if (!wdev) {
PRINT_ER("Can't register WILC Wiphy\n");
return -1;
}
- /*linking the wireless_dev structure with the netdevice*/
- nic->wilc_netdev->ieee80211_ptr = wdev;
- nic->wilc_netdev->ml_priv = nic;
- wdev->netdev = nic->wilc_netdev;
- nic->netstats.rx_packets = 0;
- nic->netstats.tx_packets = 0;
- nic->netstats.rx_bytes = 0;
- nic->netstats.tx_bytes = 0;
-
+ vif->ndev->ieee80211_ptr = wdev;
+ vif->ndev->ml_priv = vif;
+ wdev->netdev = vif->ndev;
+ vif->netstats.rx_packets = 0;
+ vif->netstats.tx_packets = 0;
+ vif->netstats.rx_bytes = 0;
+ vif->netstats.tx_bytes = 0;
}
if (register_netdev(ndev)) {
- PRINT_ER("Device couldn't be registered - %s\n", ndev->name);
- return -1; /* ERROR */
+ PRINT_ER("Device couldn't be registered - %s\n",
+ ndev->name);
+ return -1;
}
- nic->iftype = STATION_MODE;
- nic->mac_opened = 0;
-
- }
-
- #ifndef WILC_SDIO
- if (!linux_spi_init(&g_linux_wlan->wilc_spidev)) {
- PRINT_ER("Can't initialize SPI\n");
- return -1; /* ERROR */
+ vif->iftype = STATION_MODE;
+ vif->mac_opened = 0;
}
- g_linux_wlan->wilc_spidev = wilc_spi_dev;
- #else
- g_linux_wlan->wilc_sdio_func = local_sdio_func;
- #endif
return 0;
}
-
-/*The 1st function called after module inserted*/
-static int __init init_wilc_driver(void)
-{
-#ifdef WILC_SPI
- struct wilc *wilc;
-#endif
-
-#if defined(WILC_DEBUGFS)
- if (wilc_debugfs_init() < 0) {
- PRINT_D(GENERIC_DBG, "fail to create debugfs for wilc driver\n");
- return -1;
- }
-#endif
-
- printk("IN INIT FUNCTION\n");
- printk("*** WILC1000 driver VERSION=[10.2] FW_VER=[10.2] ***\n");
-
- linux_wlan_device_power(1);
- msleep(100);
- linux_wlan_device_detection(1);
-
-#ifdef WILC_SDIO
- {
- int ret;
-
- ret = sdio_register_driver(&wilc_bus);
- if (ret < 0)
- PRINT_D(INIT_DBG, "init_wilc_driver: Failed register sdio driver\n");
-
- return ret;
- }
-#else
- PRINT_D(INIT_DBG, "Initializing netdev\n");
- if (wilc_netdev_init(&wilc))
- PRINT_ER("Couldn't initialize netdev\n");
- return 0;
-#endif
-}
-late_initcall(init_wilc_driver);
-
-static void __exit exit_wilc_driver(void)
-{
-#ifndef WILC_SDIO
- PRINT_D(INIT_DBG, "SPI unregister...\n");
- spi_unregister_driver(&wilc_bus);
-#else
- PRINT_D(INIT_DBG, "SDIO unregister...\n");
- sdio_unregister_driver(&wilc_bus);
-#endif
-}
-module_exit(exit_wilc_driver);
+EXPORT_SYMBOL_GPL(wilc_netdev_init);
MODULE_LICENSE("GPL");
diff --git a/drivers/staging/wilc1000/linux_wlan_common.h b/drivers/staging/wilc1000/linux_wlan_common.h
index 2b76e41ebd4d..5d40f05124c1 100644
--- a/drivers/staging/wilc1000/linux_wlan_common.h
+++ b/drivers/staging/wilc1000/linux_wlan_common.h
@@ -38,11 +38,8 @@ enum debug_region {
#define FIRM_DBG (1 << Firmware_debug)
#if defined (WILC_DEBUGFS)
-int wilc_debugfs_init(void);
-void wilc_debugfs_remove(void);
-
-extern atomic_t REGION;
-extern atomic_t DEBUG_LEVEL;
+extern atomic_t WILC_REGION;
+extern atomic_t WILC_DEBUG_LEVEL;
#define DEBUG BIT(0)
#define INFO BIT(1)
@@ -51,8 +48,8 @@ extern atomic_t DEBUG_LEVEL;
#define PRINT_D(region, ...) \
do { \
- if ((atomic_read(&DEBUG_LEVEL) & DEBUG) && \
- ((atomic_read(&REGION)) & (region))) { \
+ if ((atomic_read(&WILC_DEBUG_LEVEL) & DEBUG) && \
+ ((atomic_read(&WILC_REGION)) & (region))) { \
printk("DBG [%s: %d]", __func__, __LINE__); \
printk(__VA_ARGS__); \
} \
@@ -60,8 +57,8 @@ extern atomic_t DEBUG_LEVEL;
#define PRINT_INFO(region, ...) \
do { \
- if ((atomic_read(&DEBUG_LEVEL) & INFO) && \
- ((atomic_read(&REGION)) & (region))) { \
+ if ((atomic_read(&WILC_DEBUG_LEVEL) & INFO) && \
+ ((atomic_read(&WILC_REGION)) & (region))) { \
printk("INFO [%s]", __func__); \
printk(__VA_ARGS__); \
} \
@@ -69,8 +66,8 @@ extern atomic_t DEBUG_LEVEL;
#define PRINT_WRN(region, ...) \
do { \
- if ((atomic_read(&DEBUG_LEVEL) & WRN) && \
- ((atomic_read(&REGION)) & (region))) { \
+ if ((atomic_read(&WILC_DEBUG_LEVEL) & WRN) && \
+ ((atomic_read(&WILC_REGION)) & (region))) { \
printk("WRN [%s: %d]", __func__, __LINE__); \
printk(__VA_ARGS__); \
} \
@@ -78,7 +75,7 @@ extern atomic_t DEBUG_LEVEL;
#define PRINT_ER(...) \
do { \
- if ((atomic_read(&DEBUG_LEVEL) & ERR)) { \
+ if ((atomic_read(&WILC_DEBUG_LEVEL) & ERR)) { \
printk("ERR [%s: %d]", __func__, __LINE__); \
printk(__VA_ARGS__); \
} \
@@ -121,14 +118,13 @@ extern atomic_t DEBUG_LEVEL;
printk("ERR [%s: %d]", __func__, __LINE__); \
printk(__VA_ARGS__); \
} while (0)
+
#endif
#define FN_IN /* PRINT_D(">>> \n") */
#define FN_OUT /* PRINT_D("<<<\n") */
-#ifdef MEMORY_STATIC
#define LINUX_RX_SIZE (96 * 1024)
-#endif
#define LINUX_TX_SIZE (64 * 1024)
diff --git a/drivers/staging/wilc1000/linux_wlan_sdio.c b/drivers/staging/wilc1000/linux_wlan_sdio.c
deleted file mode 100644
index 4aff953a88f1..000000000000
--- a/drivers/staging/wilc1000/linux_wlan_sdio.c
+++ /dev/null
@@ -1,251 +0,0 @@
-#include "wilc_wfi_netdevice.h"
-
-#include <linux/mmc/sdio_func.h>
-#include <linux/mmc/card.h>
-#include <linux/mmc/sdio_ids.h>
-#include <linux/mmc/sdio.h>
-#include <linux/mmc/host.h>
-
-
-
-#define SDIO_MODALIAS "wilc1000_sdio"
-
-#if defined(CUSTOMER_PLATFORM)
-/* TODO : User have to stable bus clock as user's environment. */
- #ifdef MAX_BUS_SPEED
- #define MAX_SPEED MAX_BUS_SPEED
- #else
- #define MAX_SPEED 50000000
- #endif
-#else
- #define MAX_SPEED (6 * 1000000) /* Max 50M */
-#endif
-
-struct wilc_sdio {
- struct sdio_func *func;
- struct wilc *wilc;
-};
-
-struct sdio_func *local_sdio_func;
-
-static unsigned int sdio_default_speed;
-
-#define SDIO_VENDOR_ID_WILC 0x0296
-#define SDIO_DEVICE_ID_WILC 0x5347
-
-static const struct sdio_device_id wilc_sdio_ids[] = {
- { SDIO_DEVICE(SDIO_VENDOR_ID_WILC, SDIO_DEVICE_ID_WILC) },
- { },
-};
-
-
-static void wilc_sdio_interrupt(struct sdio_func *func)
-{
- struct wilc_sdio *wl_sdio;
-
- wl_sdio = sdio_get_drvdata(func);
-
-#ifndef WILC_SDIO_IRQ_GPIO
- sdio_release_host(func);
- wilc_handle_isr(wl_sdio->wilc);
- sdio_claim_host(func);
-#endif
-}
-
-
-int linux_sdio_cmd52(sdio_cmd52_t *cmd)
-{
- struct sdio_func *func = g_linux_wlan->wilc_sdio_func;
- int ret;
- u8 data;
-
- sdio_claim_host(func);
-
- func->num = cmd->function;
- if (cmd->read_write) { /* write */
- if (cmd->raw) {
- sdio_writeb(func, cmd->data, cmd->address, &ret);
- data = sdio_readb(func, cmd->address, &ret);
- cmd->data = data;
- } else {
- sdio_writeb(func, cmd->data, cmd->address, &ret);
- }
- } else { /* read */
- data = sdio_readb(func, cmd->address, &ret);
- cmd->data = data;
- }
-
- sdio_release_host(func);
-
- if (ret < 0) {
- PRINT_ER("wilc_sdio_cmd52..failed, err(%d)\n", ret);
- return 0;
- }
- return 1;
-}
-
-
-int linux_sdio_cmd53(sdio_cmd53_t *cmd)
-{
- struct sdio_func *func = g_linux_wlan->wilc_sdio_func;
- int size, ret;
-
- sdio_claim_host(func);
-
- func->num = cmd->function;
- func->cur_blksize = cmd->block_size;
- if (cmd->block_mode)
- size = cmd->count * cmd->block_size;
- else
- size = cmd->count;
-
- if (cmd->read_write) { /* write */
- ret = sdio_memcpy_toio(func, cmd->address, (void *)cmd->buffer, size);
- } else { /* read */
- ret = sdio_memcpy_fromio(func, (void *)cmd->buffer, cmd->address, size);
- }
-
- sdio_release_host(func);
-
-
- if (ret < 0) {
- PRINT_ER("wilc_sdio_cmd53..failed, err(%d)\n", ret);
- return 0;
- }
-
- return 1;
-}
-
-static int linux_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
-{
- struct wilc_sdio *wl_sdio;
- struct wilc *wilc;
-
- PRINT_D(INIT_DBG, "probe function\n");
- wl_sdio = kzalloc(sizeof(struct wilc_sdio), GFP_KERNEL);
- if (!wl_sdio)
- return -ENOMEM;
-
- PRINT_D(INIT_DBG, "Initializing netdev\n");
- local_sdio_func = func;
- if (wilc_netdev_init(&wilc)) {
- PRINT_ER("Couldn't initialize netdev\n");
- kfree(wl_sdio);
- return -1;
- }
- wl_sdio->func = func;
- wl_sdio->wilc = wilc;
- sdio_set_drvdata(func, wl_sdio);
-
- printk("Driver Initializing success\n");
- return 0;
-}
-
-static void linux_sdio_remove(struct sdio_func *func)
-{
- struct wilc_sdio *wl_sdio;
-
- wl_sdio = sdio_get_drvdata(func);
- wl_wlan_cleanup();
- kfree(wl_sdio);
-}
-
-struct sdio_driver wilc_bus = {
- .name = SDIO_MODALIAS,
- .id_table = wilc_sdio_ids,
- .probe = linux_sdio_probe,
- .remove = linux_sdio_remove,
-};
-
-int enable_sdio_interrupt(void)
-{
- int ret = 0;
-#ifndef WILC_SDIO_IRQ_GPIO
-
- sdio_claim_host(local_sdio_func);
- ret = sdio_claim_irq(local_sdio_func, wilc_sdio_interrupt);
- sdio_release_host(local_sdio_func);
-
- if (ret < 0) {
- PRINT_ER("can't claim sdio_irq, err(%d)\n", ret);
- ret = -EIO;
- }
-#endif
- return ret;
-}
-
-void disable_sdio_interrupt(void)
-{
-
-#ifndef WILC_SDIO_IRQ_GPIO
- int ret;
-
- PRINT_D(INIT_DBG, "disable_sdio_interrupt IN\n");
-
- sdio_claim_host(local_sdio_func);
- ret = sdio_release_irq(local_sdio_func);
- if (ret < 0) {
- PRINT_ER("can't release sdio_irq, err(%d)\n", ret);
- }
- sdio_release_host(local_sdio_func);
-
- PRINT_D(INIT_DBG, "disable_sdio_interrupt OUT\n");
-#endif
-}
-
-static int linux_sdio_set_speed(int speed)
-{
- struct mmc_ios ios;
-
- sdio_claim_host(local_sdio_func);
-
- memcpy((void *)&ios, (void *)&local_sdio_func->card->host->ios, sizeof(struct mmc_ios));
- local_sdio_func->card->host->ios.clock = speed;
- ios.clock = speed;
- local_sdio_func->card->host->ops->set_ios(local_sdio_func->card->host, &ios);
- sdio_release_host(local_sdio_func);
- PRINT_INFO(INIT_DBG, "@@@@@@@@@@@@ change SDIO speed to %d @@@@@@@@@\n", speed);
-
- return 1;
-}
-
-static int linux_sdio_get_speed(void)
-{
- return local_sdio_func->card->host->ios.clock;
-}
-
-int linux_sdio_init(void *pv)
-{
-
- /**
- * TODO :
- **/
-
-
- sdio_default_speed = linux_sdio_get_speed();
- return 1;
-}
-
-void linux_sdio_deinit(void *pv)
-{
-
- /**
- * TODO :
- **/
-
-
- sdio_unregister_driver(&wilc_bus);
-}
-
-int linux_sdio_set_max_speed(void)
-{
- return linux_sdio_set_speed(MAX_SPEED);
-}
-
-int linux_sdio_set_default_speed(void)
-{
- return linux_sdio_set_speed(sdio_default_speed);
-}
-
-
-
diff --git a/drivers/staging/wilc1000/linux_wlan_sdio.h b/drivers/staging/wilc1000/linux_wlan_sdio.h
deleted file mode 100644
index 4b515f5108e7..000000000000
--- a/drivers/staging/wilc1000/linux_wlan_sdio.h
+++ /dev/null
@@ -1,14 +0,0 @@
-extern struct sdio_func *local_sdio_func;
-extern struct sdio_driver wilc_bus;
-
-#include <linux/mmc/sdio_func.h>
-
-int linux_sdio_init(void *);
-void linux_sdio_deinit(void *);
-int linux_sdio_cmd52(sdio_cmd52_t *cmd);
-int linux_sdio_cmd53(sdio_cmd53_t *cmd);
-int enable_sdio_interrupt(void);
-void disable_sdio_interrupt(void);
-int linux_sdio_set_max_speed(void);
-int linux_sdio_set_default_speed(void);
-
diff --git a/drivers/staging/wilc1000/linux_wlan_spi.c b/drivers/staging/wilc1000/linux_wlan_spi.c
deleted file mode 100644
index 039d06192d6b..000000000000
--- a/drivers/staging/wilc1000/linux_wlan_spi.c
+++ /dev/null
@@ -1,409 +0,0 @@
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/cdev.h>
-#include <linux/uaccess.h>
-#include <linux/device.h>
-#include <linux/spi/spi.h>
-
-#include "linux_wlan_common.h"
-#include "linux_wlan_spi.h"
-
-#define USE_SPI_DMA 0 /* johnny add */
-
-#ifdef WILC_ASIC_A0
- #if defined(PLAT_PANDA_ES_OMAP4460)
- #define MIN_SPEED 12000000
- #define MAX_SPEED 24000000
- #elif defined(PLAT_WMS8304)
- #define MIN_SPEED 12000000
- #define MAX_SPEED 24000000 /* 4000000 */
- #elif defined(CUSTOMER_PLATFORM)
-/*
- TODO : define Clock speed under 48M.
- *
- * ex)
- * #define MIN_SPEED 24000000
- * #define MAX_SPEED 48000000
- */
- #else
- #define MIN_SPEED 24000000
- #define MAX_SPEED 48000000
- #endif
-#else /* WILC_ASIC_A0 */
-/* Limit clk to 6MHz on FPGA. */
- #define MIN_SPEED 6000000
- #define MAX_SPEED 6000000
-#endif /* WILC_ASIC_A0 */
-
-static u32 SPEED = MIN_SPEED;
-
-struct spi_device *wilc_spi_dev;
-void linux_spi_deinit(void *vp);
-
-static int __init wilc_bus_probe(struct spi_device *spi)
-{
-
- PRINT_D(BUS_DBG, "spiModalias: %s\n", spi->modalias);
- PRINT_D(BUS_DBG, "spiMax-Speed: %d\n", spi->max_speed_hz);
- wilc_spi_dev = spi;
-
- printk("Driver Initializing success\n");
- return 0;
-}
-
-static int __exit wilc_bus_remove(struct spi_device *spi)
-{
-
- return 0;
-}
-
-#ifdef CONFIG_OF
-static const struct of_device_id wilc1000_of_match[] = {
- { .compatible = "atmel,wilc_spi", },
- {}
-};
-MODULE_DEVICE_TABLE(of, wilc1000_of_match);
-#endif
-
-struct spi_driver wilc_bus __refdata = {
- .driver = {
- .name = MODALIAS,
-#ifdef CONFIG_OF
- .of_match_table = wilc1000_of_match,
-#endif
- },
- .probe = wilc_bus_probe,
- .remove = __exit_p(wilc_bus_remove),
-};
-
-
-void linux_spi_deinit(void *vp)
-{
-
- spi_unregister_driver(&wilc_bus);
-
- SPEED = MIN_SPEED;
- PRINT_ER("@@@@@@@@@@@@ restore SPI speed to %d @@@@@@@@@\n", SPEED);
-
-}
-
-
-
-int linux_spi_init(void *vp)
-{
- int ret = 1;
- static int called;
-
-
- if (called == 0) {
- called++;
- ret = spi_register_driver(&wilc_bus);
- }
-
- /* change return value to match WILC interface */
- (ret < 0) ? (ret = 0) : (ret = 1);
-
- return ret;
-}
-
-#if defined(PLAT_WMS8304)
-#define TXRX_PHASE_SIZE (4096)
-#endif
-
-#if defined(TXRX_PHASE_SIZE)
-
-int linux_spi_write(u8 *b, u32 len)
-{
- int ret;
-
- if (len > 0 && b != NULL) {
- int i = 0;
- int blk = len / TXRX_PHASE_SIZE;
- int remainder = len % TXRX_PHASE_SIZE;
-
- char *r_buffer = kzalloc(TXRX_PHASE_SIZE, GFP_KERNEL);
- if (!r_buffer)
- return -ENOMEM;
-
- if (blk) {
- while (i < blk) {
- struct spi_message msg;
- struct spi_transfer tr = {
- .tx_buf = b + (i * TXRX_PHASE_SIZE),
- .len = TXRX_PHASE_SIZE,
- .speed_hz = SPEED,
- .bits_per_word = 8,
- .delay_usecs = 0,
- };
-
- tr.rx_buf = r_buffer;
-
- memset(&msg, 0, sizeof(msg));
- spi_message_init(&msg);
- msg.spi = wilc_spi_dev;
- msg.is_dma_mapped = USE_SPI_DMA;
-
- spi_message_add_tail(&tr, &msg);
- ret = spi_sync(wilc_spi_dev, &msg);
- if (ret < 0) {
- PRINT_ER("SPI transaction failed\n");
- }
- i++;
-
- }
- }
- if (remainder) {
- struct spi_message msg;
- struct spi_transfer tr = {
- .tx_buf = b + (blk * TXRX_PHASE_SIZE),
- .len = remainder,
- .speed_hz = SPEED,
- .bits_per_word = 8,
- .delay_usecs = 0,
- };
- tr.rx_buf = r_buffer;
-
- memset(&msg, 0, sizeof(msg));
- spi_message_init(&msg);
- msg.spi = wilc_spi_dev;
- msg.is_dma_mapped = USE_SPI_DMA; /* rachel */
-
- spi_message_add_tail(&tr, &msg);
- ret = spi_sync(wilc_spi_dev, &msg);
- if (ret < 0) {
- PRINT_ER("SPI transaction failed\n");
- }
- }
- kfree(r_buffer);
- } else {
- PRINT_ER("can't write data with the following length: %d\n", len);
- PRINT_ER("FAILED due to NULL buffer or ZERO length check the following length: %d\n", len);
- ret = -1;
- }
-
- /* change return value to match WILC interface */
- (ret < 0) ? (ret = 0) : (ret = 1);
-
- return ret;
-
-}
-
-#else
-int linux_spi_write(u8 *b, u32 len)
-{
-
- int ret;
- struct spi_message msg;
-
- if (len > 0 && b != NULL) {
- struct spi_transfer tr = {
- .tx_buf = b,
- .len = len,
- .speed_hz = SPEED,
- .delay_usecs = 0,
- };
- char *r_buffer = kzalloc(len, GFP_KERNEL);
- if (!r_buffer)
- return -ENOMEM;
-
- tr.rx_buf = r_buffer;
- PRINT_D(BUS_DBG, "Request writing %d bytes\n", len);
-
- memset(&msg, 0, sizeof(msg));
- spi_message_init(&msg);
-/* [[johnny add */
- msg.spi = wilc_spi_dev;
- msg.is_dma_mapped = USE_SPI_DMA;
-/* ]] */
- spi_message_add_tail(&tr, &msg);
-
- ret = spi_sync(wilc_spi_dev, &msg);
- if (ret < 0) {
- PRINT_ER("SPI transaction failed\n");
- }
-
- kfree(r_buffer);
- } else {
- PRINT_ER("can't write data with the following length: %d\n", len);
- PRINT_ER("FAILED due to NULL buffer or ZERO length check the following length: %d\n", len);
- ret = -1;
- }
-
- /* change return value to match WILC interface */
- (ret < 0) ? (ret = 0) : (ret = 1);
-
-
- return ret;
-}
-
-#endif
-
-#if defined(TXRX_PHASE_SIZE)
-
-int linux_spi_read(u8 *rb, u32 rlen)
-{
- int ret;
-
- if (rlen > 0) {
- int i = 0;
-
- int blk = rlen / TXRX_PHASE_SIZE;
- int remainder = rlen % TXRX_PHASE_SIZE;
-
- char *t_buffer = kzalloc(TXRX_PHASE_SIZE, GFP_KERNEL);
- if (!t_buffer)
- return -ENOMEM;
-
- if (blk) {
- while (i < blk) {
- struct spi_message msg;
- struct spi_transfer tr = {
- .rx_buf = rb + (i * TXRX_PHASE_SIZE),
- .len = TXRX_PHASE_SIZE,
- .speed_hz = SPEED,
- .bits_per_word = 8,
- .delay_usecs = 0,
- };
- tr.tx_buf = t_buffer;
-
- memset(&msg, 0, sizeof(msg));
- spi_message_init(&msg);
- msg.spi = wilc_spi_dev;
- msg.is_dma_mapped = USE_SPI_DMA;
-
- spi_message_add_tail(&tr, &msg);
- ret = spi_sync(wilc_spi_dev, &msg);
- if (ret < 0) {
- PRINT_ER("SPI transaction failed\n");
- }
- i++;
- }
- }
- if (remainder) {
- struct spi_message msg;
- struct spi_transfer tr = {
- .rx_buf = rb + (blk * TXRX_PHASE_SIZE),
- .len = remainder,
- .speed_hz = SPEED,
- .bits_per_word = 8,
- .delay_usecs = 0,
- };
- tr.tx_buf = t_buffer;
-
- memset(&msg, 0, sizeof(msg));
- spi_message_init(&msg);
- msg.spi = wilc_spi_dev;
- msg.is_dma_mapped = USE_SPI_DMA; /* rachel */
-
- spi_message_add_tail(&tr, &msg);
- ret = spi_sync(wilc_spi_dev, &msg);
- if (ret < 0) {
- PRINT_ER("SPI transaction failed\n");
- }
- }
-
- kfree(t_buffer);
- } else {
- PRINT_ER("can't read data with the following length: %u\n", rlen);
- ret = -1;
- }
- /* change return value to match WILC interface */
- (ret < 0) ? (ret = 0) : (ret = 1);
-
- return ret;
-}
-
-#else
-int linux_spi_read(u8 *rb, u32 rlen)
-{
-
- int ret;
-
- if (rlen > 0) {
- struct spi_message msg;
- struct spi_transfer tr = {
- .rx_buf = rb,
- .len = rlen,
- .speed_hz = SPEED,
- .delay_usecs = 0,
-
- };
- char *t_buffer = kzalloc(rlen, GFP_KERNEL);
- if (!t_buffer)
- return -ENOMEM;
-
- tr.tx_buf = t_buffer;
-
- memset(&msg, 0, sizeof(msg));
- spi_message_init(&msg);
-/* [[ johnny add */
- msg.spi = wilc_spi_dev;
- msg.is_dma_mapped = USE_SPI_DMA;
-/* ]] */
- spi_message_add_tail(&tr, &msg);
-
- ret = spi_sync(wilc_spi_dev, &msg);
- if (ret < 0) {
- PRINT_ER("SPI transaction failed\n");
- }
- kfree(t_buffer);
- } else {
- PRINT_ER("can't read data with the following length: %u\n", rlen);
- ret = -1;
- }
- /* change return value to match WILC interface */
- (ret < 0) ? (ret = 0) : (ret = 1);
-
- return ret;
-}
-
-#endif
-
-int linux_spi_write_read(u8 *wb, u8 *rb, u32 rlen)
-{
-
- int ret;
-
- if (rlen > 0) {
- struct spi_message msg;
- struct spi_transfer tr = {
- .rx_buf = rb,
- .tx_buf = wb,
- .len = rlen,
- .speed_hz = SPEED,
- .bits_per_word = 8,
- .delay_usecs = 0,
-
- };
-
- memset(&msg, 0, sizeof(msg));
- spi_message_init(&msg);
- msg.spi = wilc_spi_dev;
- msg.is_dma_mapped = USE_SPI_DMA;
-
- spi_message_add_tail(&tr, &msg);
- ret = spi_sync(wilc_spi_dev, &msg);
- if (ret < 0) {
- PRINT_ER("SPI transaction failed\n");
- }
- } else {
- PRINT_ER("can't read data with the following length: %u\n", rlen);
- ret = -1;
- }
- /* change return value to match WILC interface */
- (ret < 0) ? (ret = 0) : (ret = 1);
-
- return ret;
-}
-
-int linux_spi_set_max_speed(void)
-{
- SPEED = MAX_SPEED;
-
- PRINT_INFO(BUS_DBG, "@@@@@@@@@@@@ change SPI speed to %d @@@@@@@@@\n", SPEED);
- return 1;
-}
diff --git a/drivers/staging/wilc1000/linux_wlan_spi.h b/drivers/staging/wilc1000/linux_wlan_spi.h
deleted file mode 100644
index 7356785296f9..000000000000
--- a/drivers/staging/wilc1000/linux_wlan_spi.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef LINUX_WLAN_SPI_H
-#define LINUX_WLAN_SPI_H
-
-#include <linux/spi/spi.h>
-extern struct spi_device *wilc_spi_dev;
-extern struct spi_driver wilc_bus;
-
-int linux_spi_init(void *vp);
-void linux_spi_deinit(void *vp);
-int linux_spi_write(u8 *b, u32 len);
-int linux_spi_read(u8 *rb, u32 rlen);
-int linux_spi_write_read(u8 *wb, u8 *rb, u32 rlen);
-int linux_spi_set_max_speed(void);
-#endif
diff --git a/drivers/staging/wilc1000/wilc_debugfs.c b/drivers/staging/wilc1000/wilc_debugfs.c
index ae111862e7a9..27c653a0cdf9 100644
--- a/drivers/staging/wilc1000/wilc_debugfs.c
+++ b/drivers/staging/wilc1000/wilc_debugfs.c
@@ -26,8 +26,10 @@ static struct dentry *wilc_dir;
#define DBG_REGION_ALL (GENERIC_DBG | HOSTAPD_DBG | HOSTINF_DBG | CORECONFIG_DBG | CFG80211_DBG | INT_DBG | TX_DBG | RX_DBG | LOCK_DBG | INIT_DBG | BUS_DBG | MEM_DBG)
#define DBG_LEVEL_ALL (DEBUG | INFO | WRN | ERR)
-atomic_t REGION = ATOMIC_INIT(INIT_DBG | GENERIC_DBG | CFG80211_DBG | FIRM_DBG | HOSTAPD_DBG);
-atomic_t DEBUG_LEVEL = ATOMIC_INIT(ERR);
+atomic_t WILC_REGION = ATOMIC_INIT(INIT_DBG | GENERIC_DBG | CFG80211_DBG | FIRM_DBG | HOSTAPD_DBG);
+EXPORT_SYMBOL_GPL(WILC_REGION);
+atomic_t WILC_DEBUG_LEVEL = ATOMIC_INIT(ERR);
+EXPORT_SYMBOL_GPL(WILC_DEBUG_LEVEL);
/*
* --------------------------------------------------------------------------------
@@ -43,7 +45,7 @@ static ssize_t wilc_debug_level_read(struct file *file, char __user *userbuf, si
if (*ppos > 0)
return 0;
- res = scnprintf(buf, sizeof(buf), "Debug Level: %x\n", atomic_read(&DEBUG_LEVEL));
+ res = scnprintf(buf, sizeof(buf), "Debug Level: %x\n", atomic_read(&WILC_DEBUG_LEVEL));
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
@@ -59,11 +61,11 @@ static ssize_t wilc_debug_level_write(struct file *filp, const char __user *buf,
return ret;
if (flag > DBG_LEVEL_ALL) {
- printk("%s, value (0x%08x) is out of range, stay previous flag (0x%08x)\n", __func__, flag, atomic_read(&DEBUG_LEVEL));
+ printk("%s, value (0x%08x) is out of range, stay previous flag (0x%08x)\n", __func__, flag, atomic_read(&WILC_DEBUG_LEVEL));
return -EINVAL;
}
- atomic_set(&DEBUG_LEVEL, (int)flag);
+ atomic_set(&WILC_DEBUG_LEVEL, (int)flag);
if (flag == 0)
printk("Debug-level disabled\n");
@@ -82,7 +84,7 @@ static ssize_t wilc_debug_region_read(struct file *file, char __user *userbuf, s
if (*ppos > 0)
return 0;
- res = scnprintf(buf, sizeof(buf), "Debug region: %x\n", atomic_read(&REGION));
+ res = scnprintf(buf, sizeof(buf), "Debug region: %x\n", atomic_read(&WILC_REGION));
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
@@ -102,12 +104,12 @@ static ssize_t wilc_debug_region_write(struct file *filp, const char *buf, size_
flag = buffer[0] - '0';
if (flag > DBG_REGION_ALL) {
- printk("%s, value (0x%08x) is out of range, stay previous flag (0x%08x)\n", __func__, flag, atomic_read(&REGION));
+ printk("%s, value (0x%08x) is out of range, stay previous flag (0x%08x)\n", __func__, flag, atomic_read(&WILC_REGION));
return -EFAULT;
}
- atomic_set(&REGION, (int)flag);
- printk("new debug-region is %x\n", atomic_read(&REGION));
+ atomic_set(&WILC_REGION, (int)flag);
+ printk("new debug-region is %x\n", atomic_read(&WILC_REGION));
return count;
}
@@ -136,7 +138,7 @@ static struct wilc_debugfs_info_t debugfs_info[] = {
{ "wilc_debug_region", 0666, (INIT_DBG | GENERIC_DBG | CFG80211_DBG), FOPS(NULL, wilc_debug_region_read, wilc_debug_region_write, NULL), },
};
-int wilc_debugfs_init(void)
+static int __init wilc_debugfs_init(void)
{
int i;
@@ -171,11 +173,13 @@ int wilc_debugfs_init(void)
}
return 0;
}
+module_init(wilc_debugfs_init);
-void wilc_debugfs_remove(void)
+static void __exit wilc_debugfs_remove(void)
{
debugfs_remove_recursive(wilc_dir);
}
+module_exit(wilc_debugfs_remove);
#endif
diff --git a/drivers/staging/wilc1000/wilc_msgqueue.c b/drivers/staging/wilc1000/wilc_msgqueue.c
index 0eff121b8291..098390cdf319 100644
--- a/drivers/staging/wilc1000/wilc_msgqueue.c
+++ b/drivers/staging/wilc1000/wilc_msgqueue.c
@@ -115,7 +115,6 @@ int wilc_mq_recv(WILC_MsgQueueHandle *pHandle,
u32 *pu32ReceivedLength)
{
Message *pstrMessage;
- int result = 0;
unsigned long flags;
if ((!pHandle) || (u32RecvBufferSize == 0)
@@ -135,12 +134,6 @@ int wilc_mq_recv(WILC_MsgQueueHandle *pHandle,
down(&pHandle->hSem);
- /* other non-timeout scenarios */
- if (result) {
- PRINT_ER("Non-timeout\n");
- return result;
- }
-
if (pHandle->bExiting) {
PRINT_ER("pHandle fail\n");
return -EFAULT;
@@ -174,5 +167,5 @@ int wilc_mq_recv(WILC_MsgQueueHandle *pHandle,
spin_unlock_irqrestore(&pHandle->strCriticalSection, flags);
- return result;
+ return 0;
}
diff --git a/drivers/staging/wilc1000/wilc_msgqueue.h b/drivers/staging/wilc1000/wilc_msgqueue.h
index d231c334ed93..d7e0328bacee 100644
--- a/drivers/staging/wilc1000/wilc_msgqueue.h
+++ b/drivers/staging/wilc1000/wilc_msgqueue.h
@@ -35,7 +35,7 @@ typedef struct __MessageQueue_struct {
* any other message queue having the same name in the system
* @param[in,out] pHandle handle to the message queue object
* @param[in] pstrAttrs Optional attributes, NULL for default
- * @return Error code indicating sucess/failure
+ * @return Error code indicating success/failure
* @author syounan
* @date 30 Aug 2010
* @version 1.0
@@ -44,7 +44,7 @@ int wilc_mq_create(WILC_MsgQueueHandle *pHandle);
/*!
* @brief Sends a message
- * @details Sends a message, this API will block unil the message is
+ * @details Sends a message, this API will block until the message is
* actually sent or until it is timedout (as long as the feature
* CONFIG_WILC_MSG_QUEUE_TIMEOUT is enabled and pstrAttrs->u32Timeout
* is not set to WILC_OS_INFINITY), zero timeout is a valid value
@@ -52,7 +52,7 @@ int wilc_mq_create(WILC_MsgQueueHandle *pHandle);
* @param[in] pvSendBuffer pointer to the data to send
* @param[in] u32SendBufferSize the size of the data to send
* @param[in] pstrAttrs Optional attributes, NULL for default
- * @return Error code indicating sucess/failure
+ * @return Error code indicating success/failure
* @author syounan
* @date 30 Aug 2010
* @version 1.0
@@ -62,7 +62,7 @@ int wilc_mq_send(WILC_MsgQueueHandle *pHandle,
/*!
* @brief Receives a message
- * @details Receives a message, this API will block unil a message is
+ * @details Receives a message, this API will block until a message is
* received or until it is timedout (as long as the feature
* CONFIG_WILC_MSG_QUEUE_TIMEOUT is enabled and pstrAttrs->u32Timeout
* is not set to WILC_OS_INFINITY), zero timeout is a valid value
@@ -71,7 +71,7 @@ int wilc_mq_send(WILC_MsgQueueHandle *pHandle,
* @param[in] u32RecvBufferSize the size of the receive buffer
* @param[out] pu32ReceivedLength the length of received data
* @param[in] pstrAttrs Optional attributes, NULL for default
- * @return Error code indicating sucess/failure
+ * @return Error code indicating success/failure
* @author syounan
* @date 30 Aug 2010
* @version 1.0
@@ -84,7 +84,7 @@ int wilc_mq_recv(WILC_MsgQueueHandle *pHandle,
* @brief Destroys an existing Message queue
* @param[in] pHandle handle to the message queue object
* @param[in] pstrAttrs Optional attributes, NULL for default
- * @return Error code indicating sucess/failure
+ * @return Error code indicating success/failure
* @author syounan
* @date 30 Aug 2010
* @version 1.0
diff --git a/drivers/staging/wilc1000/wilc_sdio.c b/drivers/staging/wilc1000/wilc_sdio.c
index 300c571e4e2d..e961b5004902 100644
--- a/drivers/staging/wilc1000/wilc_sdio.c
+++ b/drivers/staging/wilc1000/wilc_sdio.c
@@ -10,17 +10,29 @@
#include <linux/string.h>
#include "wilc_wlan_if.h"
#include "wilc_wlan.h"
+#include "wilc_wfi_netdevice.h"
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/host.h>
+#include <linux/of_gpio.h>
+
+#define SDIO_MODALIAS "wilc1000_sdio"
+
+#define SDIO_VENDOR_ID_WILC 0x0296
+#define SDIO_DEVICE_ID_WILC 0x5347
+
+static const struct sdio_device_id wilc_sdio_ids[] = {
+ { SDIO_DEVICE(SDIO_VENDOR_ID_WILC, SDIO_DEVICE_ID_WILC) },
+ { },
+};
#define WILC_SDIO_BLOCK_SIZE 512
typedef struct {
- void *os_context;
+ bool irq_gpio;
u32 block_size;
- int (*sdio_cmd52)(sdio_cmd52_t *);
- int (*sdio_cmd53)(sdio_cmd53_t *);
- int (*sdio_set_max_speed)(void);
- int (*sdio_set_default_speed)(void);
- wilc_debug_func dPrint;
int nint;
#define MAX_NUN_INT_THRPT_ENH2 (5) /* Max num interrupts allowed in registers 0xf7, 0xf8 */
int has_thrpt_enh3;
@@ -28,10 +40,155 @@ typedef struct {
static wilc_sdio_t g_sdio;
-#ifdef WILC_SDIO_IRQ_GPIO
-static int sdio_write_reg(u32 addr, u32 data);
-static int sdio_read_reg(u32 addr, u32 *data);
-#endif
+static int sdio_write_reg(struct wilc *wilc, u32 addr, u32 data);
+static int sdio_read_reg(struct wilc *wilc, u32 addr, u32 *data);
+
+static void wilc_sdio_interrupt(struct sdio_func *func)
+{
+ sdio_release_host(func);
+ wilc_handle_isr(sdio_get_drvdata(func));
+ sdio_claim_host(func);
+}
+
+static int wilc_sdio_cmd52(struct wilc *wilc, sdio_cmd52_t *cmd)
+{
+ struct sdio_func *func = container_of(wilc->dev, struct sdio_func, dev);
+ int ret;
+ u8 data;
+
+ sdio_claim_host(func);
+
+ func->num = cmd->function;
+ if (cmd->read_write) { /* write */
+ if (cmd->raw) {
+ sdio_writeb(func, cmd->data, cmd->address, &ret);
+ data = sdio_readb(func, cmd->address, &ret);
+ cmd->data = data;
+ } else {
+ sdio_writeb(func, cmd->data, cmd->address, &ret);
+ }
+ } else { /* read */
+ data = sdio_readb(func, cmd->address, &ret);
+ cmd->data = data;
+ }
+
+ sdio_release_host(func);
+
+ if (ret)
+ dev_err(&func->dev, "wilc_sdio_cmd52..failed, err(%d)\n", ret);
+ return ret;
+}
+
+
+static int wilc_sdio_cmd53(struct wilc *wilc, sdio_cmd53_t *cmd)
+{
+ struct sdio_func *func = container_of(wilc->dev, struct sdio_func, dev);
+ int size, ret;
+
+ sdio_claim_host(func);
+
+ func->num = cmd->function;
+ func->cur_blksize = cmd->block_size;
+ if (cmd->block_mode)
+ size = cmd->count * cmd->block_size;
+ else
+ size = cmd->count;
+
+ if (cmd->read_write) { /* write */
+ ret = sdio_memcpy_toio(func, cmd->address,
+ (void *)cmd->buffer, size);
+ } else { /* read */
+ ret = sdio_memcpy_fromio(func, (void *)cmd->buffer,
+ cmd->address, size);
+ }
+
+ sdio_release_host(func);
+
+ if (ret)
+ dev_err(&func->dev, "wilc_sdio_cmd53..failed, err(%d)\n", ret);
+
+ return ret;
+}
+
+static int linux_sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ struct wilc *wilc;
+ int gpio, ret;
+
+ gpio = -1;
+ if (IS_ENABLED(CONFIG_WILC1000_HW_OOB_INTR)) {
+ gpio = of_get_gpio(func->dev.of_node, 0);
+ if (gpio < 0)
+ gpio = GPIO_NUM;
+ }
+
+ dev_dbg(&func->dev, "Initializing netdev\n");
+ ret = wilc_netdev_init(&wilc, &func->dev, HIF_SDIO, gpio,
+ &wilc_hif_sdio);
+ if (ret) {
+ dev_err(&func->dev, "Couldn't initialize netdev\n");
+ return ret;
+ }
+ sdio_set_drvdata(func, wilc);
+ wilc->dev = &func->dev;
+
+ dev_info(&func->dev, "Driver Initializing success\n");
+ return 0;
+}
+
+static void linux_sdio_remove(struct sdio_func *func)
+{
+ wilc_netdev_cleanup(sdio_get_drvdata(func));
+}
+
+static struct sdio_driver wilc1000_sdio_driver = {
+ .name = SDIO_MODALIAS,
+ .id_table = wilc_sdio_ids,
+ .probe = linux_sdio_probe,
+ .remove = linux_sdio_remove,
+};
+module_driver(wilc1000_sdio_driver,
+ sdio_register_driver,
+ sdio_unregister_driver);
+MODULE_LICENSE("GPL");
+
+static int wilc_sdio_enable_interrupt(struct wilc *dev)
+{
+ struct sdio_func *func = container_of(dev->dev, struct sdio_func, dev);
+ int ret = 0;
+
+ sdio_claim_host(func);
+ ret = sdio_claim_irq(func, wilc_sdio_interrupt);
+ sdio_release_host(func);
+
+ if (ret < 0) {
+ dev_err(&func->dev, "can't claim sdio_irq, err(%d)\n", ret);
+ ret = -EIO;
+ }
+ return ret;
+}
+
+static void wilc_sdio_disable_interrupt(struct wilc *dev)
+{
+ struct sdio_func *func = container_of(dev->dev, struct sdio_func, dev);
+ int ret;
+
+ dev_dbg(&func->dev, "wilc_sdio_disable_interrupt IN\n");
+
+ sdio_claim_host(func);
+ ret = sdio_release_irq(func);
+ if (ret < 0)
+ dev_err(&func->dev, "can't release sdio_irq, err(%d)\n", ret);
+ sdio_release_host(func);
+
+ dev_info(&func->dev, "wilc_sdio_disable_interrupt OUT\n");
+}
+
+static int wilc_sdio_init(void)
+{
+ return 1;
+}
/********************************************
*
@@ -39,9 +196,11 @@ static int sdio_read_reg(u32 addr, u32 *data);
*
********************************************/
-static int sdio_set_func0_csa_address(u32 adr)
+static int sdio_set_func0_csa_address(struct wilc *wilc, u32 adr)
{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
sdio_cmd52_t cmd;
+ int ret;
/**
* Review: BIG ENDIAN
@@ -51,22 +210,25 @@ static int sdio_set_func0_csa_address(u32 adr)
cmd.raw = 0;
cmd.address = 0x10c;
cmd.data = (u8)adr;
- if (!g_sdio.sdio_cmd52(&cmd)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed cmd52, set 0x10c data...\n");
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Failed cmd52, set 0x10c data...\n");
goto _fail_;
}
cmd.address = 0x10d;
cmd.data = (u8)(adr >> 8);
- if (!g_sdio.sdio_cmd52(&cmd)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed cmd52, set 0x10d data...\n");
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Failed cmd52, set 0x10d data...\n");
goto _fail_;
}
cmd.address = 0x10e;
cmd.data = (u8)(adr >> 16);
- if (!g_sdio.sdio_cmd52(&cmd)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed cmd52, set 0x10e data...\n");
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Failed cmd52, set 0x10e data...\n");
goto _fail_;
}
@@ -75,24 +237,28 @@ _fail_:
return 0;
}
-static int sdio_set_func0_block_size(u32 block_size)
+static int sdio_set_func0_block_size(struct wilc *wilc, u32 block_size)
{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
sdio_cmd52_t cmd;
+ int ret;
cmd.read_write = 1;
cmd.function = 0;
cmd.raw = 0;
cmd.address = 0x10;
cmd.data = (u8)block_size;
- if (!g_sdio.sdio_cmd52(&cmd)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed cmd52, set 0x10 data...\n");
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Failed cmd52, set 0x10 data...\n");
goto _fail_;
}
cmd.address = 0x11;
cmd.data = (u8)(block_size >> 8);
- if (!g_sdio.sdio_cmd52(&cmd)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed cmd52, set 0x11 data...\n");
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Failed cmd52, set 0x11 data...\n");
goto _fail_;
}
@@ -107,23 +273,27 @@ _fail_:
*
********************************************/
-static int sdio_set_func1_block_size(u32 block_size)
+static int sdio_set_func1_block_size(struct wilc *wilc, u32 block_size)
{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
sdio_cmd52_t cmd;
+ int ret;
cmd.read_write = 1;
cmd.function = 0;
cmd.raw = 0;
cmd.address = 0x110;
cmd.data = (u8)block_size;
- if (!g_sdio.sdio_cmd52(&cmd)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed cmd52, set 0x110 data...\n");
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Failed cmd52, set 0x110 data...\n");
goto _fail_;
}
cmd.address = 0x111;
cmd.data = (u8)(block_size >> 8);
- if (!g_sdio.sdio_cmd52(&cmd)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed cmd52, set 0x111 data...\n");
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Failed cmd52, set 0x111 data...\n");
goto _fail_;
}
@@ -132,100 +302,17 @@ _fail_:
return 0;
}
-static int sdio_clear_int(void)
-{
-#ifndef WILC_SDIO_IRQ_GPIO
- /* u32 sts; */
- sdio_cmd52_t cmd;
-
- cmd.read_write = 0;
- cmd.function = 1;
- cmd.raw = 0;
- cmd.address = 0x4;
- cmd.data = 0;
- g_sdio.sdio_cmd52(&cmd);
-
- return cmd.data;
-#else
- u32 reg;
-
- if (!sdio_read_reg(WILC_HOST_RX_CTRL_0, &reg)) {
- g_sdio.dPrint(N_ERR, "[wilc spi]: Failed read reg (%08x)...\n", WILC_HOST_RX_CTRL_0);
- return 0;
- }
- reg &= ~0x1;
- sdio_write_reg(WILC_HOST_RX_CTRL_0, reg);
- return 1;
-#endif
-
-}
-
-u32 sdio_xfer_cnt(void)
-{
- u32 cnt = 0;
- sdio_cmd52_t cmd;
-
- cmd.read_write = 0;
- cmd.function = 1;
- cmd.raw = 0;
- cmd.address = 0x1C;
- cmd.data = 0;
- g_sdio.sdio_cmd52(&cmd);
- cnt = cmd.data;
-
- cmd.read_write = 0;
- cmd.function = 1;
- cmd.raw = 0;
- cmd.address = 0x1D;
- cmd.data = 0;
- g_sdio.sdio_cmd52(&cmd);
- cnt |= (cmd.data << 8);
-
- cmd.read_write = 0;
- cmd.function = 1;
- cmd.raw = 0;
- cmd.address = 0x1E;
- cmd.data = 0;
- g_sdio.sdio_cmd52(&cmd);
- cnt |= (cmd.data << 16);
-
- return cnt;
-}
-
/********************************************
*
* Sdio interfaces
*
********************************************/
-int sdio_check_bs(void)
+static int sdio_write_reg(struct wilc *wilc, u32 addr, u32 data)
{
- sdio_cmd52_t cmd;
-
- /**
- * poll until BS is 0
- **/
- cmd.read_write = 0;
- cmd.function = 0;
- cmd.raw = 0;
- cmd.address = 0xc;
- cmd.data = 0;
- if (!g_sdio.sdio_cmd52(&cmd)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Fail cmd 52, get BS register...\n");
- goto _fail_;
- }
-
- return 1;
-
-_fail_:
-
- return 0;
-}
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+ int ret;
-static int sdio_write_reg(u32 addr, u32 data)
-{
-#ifdef BIG_ENDIAN
- data = BYTE_SWAP(data);
-#endif
+ data = cpu_to_le32(data);
if ((addr >= 0xf0) && (addr <= 0xff)) {
sdio_cmd52_t cmd;
@@ -235,8 +322,10 @@ static int sdio_write_reg(u32 addr, u32 data)
cmd.raw = 0;
cmd.address = addr;
cmd.data = data;
- if (!g_sdio.sdio_cmd52(&cmd)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed cmd 52, read reg (%08x) ...\n", addr);
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd 52, read reg (%08x) ...\n", addr);
goto _fail_;
}
} else {
@@ -245,7 +334,7 @@ static int sdio_write_reg(u32 addr, u32 data)
/**
* set the AHB address
**/
- if (!sdio_set_func0_csa_address(addr))
+ if (!sdio_set_func0_csa_address(wilc, addr))
goto _fail_;
cmd.read_write = 1;
@@ -256,9 +345,10 @@ static int sdio_write_reg(u32 addr, u32 data)
cmd.count = 4;
cmd.buffer = (u8 *)&data;
cmd.block_size = g_sdio.block_size; /* johnny : prevent it from setting unexpected value */
-
- if (!g_sdio.sdio_cmd53(&cmd)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed cmd53, write reg (%08x)...\n", addr);
+ ret = wilc_sdio_cmd53(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd53, write reg (%08x)...\n", addr);
goto _fail_;
}
}
@@ -270,11 +360,12 @@ _fail_:
return 0;
}
-static int sdio_write(u32 addr, u8 *buf, u32 size)
+static int sdio_write(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
u32 block_size = g_sdio.block_size;
sdio_cmd53_t cmd;
- int nblk, nleft;
+ int nblk, nleft, ret;
cmd.read_write = 1;
if (addr > 0) {
@@ -317,11 +408,13 @@ static int sdio_write(u32 addr, u8 *buf, u32 size)
cmd.buffer = buf;
cmd.block_size = block_size;
if (addr > 0) {
- if (!sdio_set_func0_csa_address(addr))
+ if (!sdio_set_func0_csa_address(wilc, addr))
goto _fail_;
}
- if (!g_sdio.sdio_cmd53(&cmd)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed cmd53 [%x], block send...\n", addr);
+ ret = wilc_sdio_cmd53(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd53 [%x], block send...\n", addr);
goto _fail_;
}
if (addr > 0)
@@ -338,11 +431,13 @@ static int sdio_write(u32 addr, u8 *buf, u32 size)
cmd.block_size = block_size; /* johnny : prevent it from setting unexpected value */
if (addr > 0) {
- if (!sdio_set_func0_csa_address(addr))
+ if (!sdio_set_func0_csa_address(wilc, addr))
goto _fail_;
}
- if (!g_sdio.sdio_cmd53(&cmd)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed cmd53 [%x], bytes send...\n", addr);
+ ret = wilc_sdio_cmd53(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd53 [%x], bytes send...\n", addr);
goto _fail_;
}
}
@@ -354,8 +449,11 @@ _fail_:
return 0;
}
-static int sdio_read_reg(u32 addr, u32 *data)
+static int sdio_read_reg(struct wilc *wilc, u32 addr, u32 *data)
{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
+ int ret;
+
if ((addr >= 0xf0) && (addr <= 0xff)) {
sdio_cmd52_t cmd;
@@ -363,15 +461,17 @@ static int sdio_read_reg(u32 addr, u32 *data)
cmd.function = 0;
cmd.raw = 0;
cmd.address = addr;
- if (!g_sdio.sdio_cmd52(&cmd)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed cmd 52, read reg (%08x) ...\n", addr);
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd 52, read reg (%08x) ...\n", addr);
goto _fail_;
}
*data = cmd.data;
} else {
sdio_cmd53_t cmd;
- if (!sdio_set_func0_csa_address(addr))
+ if (!sdio_set_func0_csa_address(wilc, addr))
goto _fail_;
cmd.read_write = 0;
@@ -383,16 +483,15 @@ static int sdio_read_reg(u32 addr, u32 *data)
cmd.buffer = (u8 *)data;
cmd.block_size = g_sdio.block_size; /* johnny : prevent it from setting unexpected value */
-
- if (!g_sdio.sdio_cmd53(&cmd)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed cmd53, read reg (%08x)...\n", addr);
+ ret = wilc_sdio_cmd53(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd53, read reg (%08x)...\n", addr);
goto _fail_;
}
}
-#ifdef BIG_ENDIAN
- *data = BYTE_SWAP(*data);
-#endif
+ *data = cpu_to_le32(*data);
return 1;
@@ -401,11 +500,12 @@ _fail_:
return 0;
}
-static int sdio_read(u32 addr, u8 *buf, u32 size)
+static int sdio_read(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
u32 block_size = g_sdio.block_size;
sdio_cmd53_t cmd;
- int nblk, nleft;
+ int nblk, nleft, ret;
cmd.read_write = 0;
if (addr > 0) {
@@ -448,11 +548,13 @@ static int sdio_read(u32 addr, u8 *buf, u32 size)
cmd.buffer = buf;
cmd.block_size = block_size;
if (addr > 0) {
- if (!sdio_set_func0_csa_address(addr))
+ if (!sdio_set_func0_csa_address(wilc, addr))
goto _fail_;
}
- if (!g_sdio.sdio_cmd53(&cmd)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed cmd53 [%x], block read...\n", addr);
+ ret = wilc_sdio_cmd53(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd53 [%x], block read...\n", addr);
goto _fail_;
}
if (addr > 0)
@@ -469,11 +571,13 @@ static int sdio_read(u32 addr, u8 *buf, u32 size)
cmd.block_size = block_size; /* johnny : prevent it from setting unexpected value */
if (addr > 0) {
- if (!sdio_set_func0_csa_address(addr))
+ if (!sdio_set_func0_csa_address(wilc, addr))
goto _fail_;
}
- if (!g_sdio.sdio_cmd53(&cmd)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed cmd53 [%x], bytes read...\n", addr);
+ ret = wilc_sdio_cmd53(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd53 [%x], bytes read...\n", addr);
goto _fail_;
}
}
@@ -491,94 +595,29 @@ _fail_:
*
********************************************/
-static int sdio_deinit(void *pv)
+static int sdio_deinit(struct wilc *wilc)
{
return 1;
}
-static int sdio_sync(void)
-{
- u32 reg;
-
- /**
- * Disable power sequencer
- **/
- if (!sdio_read_reg(WILC_MISC, &reg)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed read misc reg...\n");
- return 0;
- }
-
- reg &= ~BIT(8);
- if (!sdio_write_reg(WILC_MISC, reg)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed write misc reg...\n");
- return 0;
- }
-
-#ifdef WILC_SDIO_IRQ_GPIO
- {
- u32 reg;
- int ret;
-
- /**
- * interrupt pin mux select
- **/
- ret = sdio_read_reg(WILC_PIN_MUX_0, &reg);
- if (!ret) {
- g_sdio.dPrint(N_ERR, "[wilc spi]: Failed read reg (%08x)...\n", WILC_PIN_MUX_0);
- return 0;
- }
- reg |= BIT(8);
- ret = sdio_write_reg(WILC_PIN_MUX_0, reg);
- if (!ret) {
- g_sdio.dPrint(N_ERR, "[wilc spi]: Failed write reg (%08x)...\n", WILC_PIN_MUX_0);
- return 0;
- }
-
- /**
- * interrupt enable
- **/
- ret = sdio_read_reg(WILC_INTR_ENABLE, &reg);
- if (!ret) {
- g_sdio.dPrint(N_ERR, "[wilc spi]: Failed read reg (%08x)...\n", WILC_INTR_ENABLE);
- return 0;
- }
- reg |= BIT(16);
- ret = sdio_write_reg(WILC_INTR_ENABLE, reg);
- if (!ret) {
- g_sdio.dPrint(N_ERR, "[wilc spi]: Failed write reg (%08x)...\n", WILC_INTR_ENABLE);
- return 0;
- }
- }
-#endif
-
- return 1;
-}
-
-static int sdio_init(wilc_wlan_inp_t *inp, wilc_debug_func func)
+static int sdio_init(struct wilc *wilc)
{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
sdio_cmd52_t cmd;
- int loop;
+ int loop, ret;
u32 chipid;
memset(&g_sdio, 0, sizeof(wilc_sdio_t));
- g_sdio.dPrint = func;
- g_sdio.os_context = inp->os_context.os_private;
+ g_sdio.irq_gpio = (wilc->dev_irq_num);
- if (inp->io_func.io_init) {
- if (!inp->io_func.io_init(g_sdio.os_context)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed io init bus...\n");
- return 0;
- }
+ if (!wilc_sdio_init()) {
+ dev_err(&func->dev, "Failed io init bus...\n");
+ return 0;
} else {
return 0;
}
- g_sdio.sdio_cmd52 = inp->io_func.u.sdio.sdio_cmd52;
- g_sdio.sdio_cmd53 = inp->io_func.u.sdio.sdio_cmd53;
- g_sdio.sdio_set_max_speed = inp->io_func.u.sdio.sdio_set_max_speed;
- g_sdio.sdio_set_default_speed = inp->io_func.u.sdio.sdio_set_default_speed;
-
/**
* function 0 csa enable
**/
@@ -587,16 +626,17 @@ static int sdio_init(wilc_wlan_inp_t *inp, wilc_debug_func func)
cmd.raw = 1;
cmd.address = 0x100;
cmd.data = 0x80;
- if (!g_sdio.sdio_cmd52(&cmd)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Fail cmd 52, enable csa...\n");
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Fail cmd 52, enable csa...\n");
goto _fail_;
}
/**
* function 0 block size
**/
- if (!sdio_set_func0_block_size(WILC_SDIO_BLOCK_SIZE)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Fail cmd 52, set func 0 block size...\n");
+ if (!sdio_set_func0_block_size(wilc, WILC_SDIO_BLOCK_SIZE)) {
+ dev_err(&func->dev, "Fail cmd 52, set func 0 block size...\n");
goto _fail_;
}
g_sdio.block_size = WILC_SDIO_BLOCK_SIZE;
@@ -609,8 +649,10 @@ static int sdio_init(wilc_wlan_inp_t *inp, wilc_debug_func func)
cmd.raw = 1;
cmd.address = 0x2;
cmd.data = 0x2;
- if (!g_sdio.sdio_cmd52(&cmd)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio] Fail cmd 52, set IOE register...\n");
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Fail cmd 52, set IOE register...\n");
goto _fail_;
}
@@ -624,8 +666,10 @@ static int sdio_init(wilc_wlan_inp_t *inp, wilc_debug_func func)
loop = 3;
do {
cmd.data = 0;
- if (!g_sdio.sdio_cmd52(&cmd)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Fail cmd 52, get IOR register...\n");
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Fail cmd 52, get IOR register...\n");
goto _fail_;
}
if (cmd.data == 0x2)
@@ -633,15 +677,15 @@ static int sdio_init(wilc_wlan_inp_t *inp, wilc_debug_func func)
} while (loop--);
if (loop <= 0) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Fail func 1 is not ready...\n");
+ dev_err(&func->dev, "Fail func 1 is not ready...\n");
goto _fail_;
}
/**
* func 1 is ready, set func 1 block size
**/
- if (!sdio_set_func1_block_size(WILC_SDIO_BLOCK_SIZE)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Fail set func 1 block size...\n");
+ if (!sdio_set_func1_block_size(wilc, WILC_SDIO_BLOCK_SIZE)) {
+ dev_err(&func->dev, "Fail set func 1 block size...\n");
goto _fail_;
}
@@ -653,24 +697,25 @@ static int sdio_init(wilc_wlan_inp_t *inp, wilc_debug_func func)
cmd.raw = 1;
cmd.address = 0x4;
cmd.data = 0x3;
- if (!g_sdio.sdio_cmd52(&cmd)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Fail cmd 52, set IEN register...\n");
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev, "Fail cmd 52, set IEN register...\n");
goto _fail_;
}
/**
* make sure can read back chip id correctly
**/
- if (!sdio_read_reg(0x1000, &chipid)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Fail cmd read chip id...\n");
+ if (!sdio_read_reg(wilc, 0x1000, &chipid)) {
+ dev_err(&func->dev, "Fail cmd read chip id...\n");
goto _fail_;
}
- g_sdio.dPrint(N_ERR, "[wilc sdio]: chipid (%08x)\n", chipid);
+ dev_err(&func->dev, "chipid (%08x)\n", chipid);
if ((chipid & 0xfff) > 0x2a0)
g_sdio.has_thrpt_enh3 = 1;
else
g_sdio.has_thrpt_enh3 = 0;
- g_sdio.dPrint(N_ERR, "[wilc sdio]: has_thrpt_enh3 = %d...\n", g_sdio.has_thrpt_enh3);
+ dev_info(&func->dev, "has_thrpt_enh3 = %d...\n", g_sdio.has_thrpt_enh3);
return 1;
@@ -679,19 +724,8 @@ _fail_:
return 0;
}
-static void sdio_set_max_speed(void)
+static int sdio_read_size(struct wilc *wilc, u32 *size)
{
- g_sdio.sdio_set_max_speed();
-}
-
-static void sdio_set_default_speed(void)
-{
- g_sdio.sdio_set_default_speed();
-}
-
-static int sdio_read_size(u32 *size)
-{
-
u32 tmp;
sdio_cmd52_t cmd;
@@ -703,7 +737,7 @@ static int sdio_read_size(u32 *size)
cmd.raw = 0;
cmd.address = 0xf2;
cmd.data = 0;
- g_sdio.sdio_cmd52(&cmd);
+ wilc_sdio_cmd52(wilc, &cmd);
tmp = cmd.data;
/* cmd.read_write = 0; */
@@ -711,54 +745,53 @@ static int sdio_read_size(u32 *size)
/* cmd.raw = 0; */
cmd.address = 0xf3;
cmd.data = 0;
- g_sdio.sdio_cmd52(&cmd);
+ wilc_sdio_cmd52(wilc, &cmd);
tmp |= (cmd.data << 8);
*size = tmp;
return 1;
}
-static int sdio_read_int(u32 *int_status)
+static int sdio_read_int(struct wilc *wilc, u32 *int_status)
{
-
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
u32 tmp;
sdio_cmd52_t cmd;
- sdio_read_size(&tmp);
+ sdio_read_size(wilc, &tmp);
/**
* Read IRQ flags
**/
-#ifndef WILC_SDIO_IRQ_GPIO
- cmd.function = 1;
- cmd.address = 0x04;
- cmd.data = 0;
- g_sdio.sdio_cmd52(&cmd);
-
- if (cmd.data & BIT(0))
- tmp |= INT_0;
- if (cmd.data & BIT(2))
- tmp |= INT_1;
- if (cmd.data & BIT(3))
- tmp |= INT_2;
- if (cmd.data & BIT(4))
- tmp |= INT_3;
- if (cmd.data & BIT(5))
- tmp |= INT_4;
- if (cmd.data & BIT(6))
- tmp |= INT_5;
- {
+ if (!g_sdio.irq_gpio) {
int i;
+ cmd.function = 1;
+ cmd.address = 0x04;
+ cmd.data = 0;
+ wilc_sdio_cmd52(wilc, &cmd);
+
+ if (cmd.data & BIT(0))
+ tmp |= INT_0;
+ if (cmd.data & BIT(2))
+ tmp |= INT_1;
+ if (cmd.data & BIT(3))
+ tmp |= INT_2;
+ if (cmd.data & BIT(4))
+ tmp |= INT_3;
+ if (cmd.data & BIT(5))
+ tmp |= INT_4;
+ if (cmd.data & BIT(6))
+ tmp |= INT_5;
for (i = g_sdio.nint; i < MAX_NUM_INT; i++) {
if ((tmp >> (IRG_FLAGS_OFFSET + i)) & 0x1) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Unexpected interrupt (1) : tmp=%x, data=%x\n", tmp, cmd.data);
+ dev_err(&func->dev,
+ "Unexpected interrupt (1) : tmp=%x, data=%x\n",
+ tmp, cmd.data);
break;
}
}
- }
-#else
- {
+ } else {
u32 irq_flags;
cmd.read_write = 0;
@@ -766,35 +799,32 @@ static int sdio_read_int(u32 *int_status)
cmd.raw = 0;
cmd.address = 0xf7;
cmd.data = 0;
- g_sdio.sdio_cmd52(&cmd);
+ wilc_sdio_cmd52(wilc, &cmd);
irq_flags = cmd.data & 0x1f;
tmp |= ((irq_flags >> 0) << IRG_FLAGS_OFFSET);
}
-#endif
-
*int_status = tmp;
return 1;
}
-static int sdio_clear_int_ext(u32 val)
+static int sdio_clear_int_ext(struct wilc *wilc, u32 val)
{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
int ret;
if (g_sdio.has_thrpt_enh3) {
u32 reg;
-#ifdef WILC_SDIO_IRQ_GPIO
- {
+ if (g_sdio.irq_gpio) {
u32 flags;
flags = val & (BIT(MAX_NUN_INT_THRPT_ENH2) - 1);
reg = flags;
+ } else {
+ reg = 0;
}
-#else
- reg = 0;
-#endif
/* select VMM table 0 */
if ((val & SEL_VMM_TBL0) == SEL_VMM_TBL0)
reg |= BIT(5);
@@ -813,16 +843,17 @@ static int sdio_clear_int_ext(u32 val)
cmd.address = 0xf8;
cmd.data = reg;
- ret = g_sdio.sdio_cmd52(&cmd);
- if (!ret) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed cmd52, set 0xf8 data (%d) ...\n", __LINE__);
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd52, set 0xf8 data (%d) ...\n",
+ __LINE__);
goto _fail_;
}
}
} else {
-#ifdef WILC_SDIO_IRQ_GPIO
- {
+ if (g_sdio.irq_gpio) {
/* see below. has_thrpt_enh2 uses register 0xf8 to clear interrupts. */
/* Cannot clear multiple interrupts. Must clear each interrupt individually */
u32 flags;
@@ -842,9 +873,11 @@ static int sdio_clear_int_ext(u32 val)
cmd.address = 0xf8;
cmd.data = BIT(i);
- ret = g_sdio.sdio_cmd52(&cmd);
- if (!ret) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed cmd52, set 0xf8 data (%d) ...\n", __LINE__);
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd52, set 0xf8 data (%d) ...\n",
+ __LINE__);
goto _fail_;
}
@@ -857,12 +890,13 @@ static int sdio_clear_int_ext(u32 val)
goto _fail_;
for (i = g_sdio.nint; i < MAX_NUM_INT; i++) {
if (flags & 1)
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Unexpected interrupt cleared %d...\n", i);
+ dev_err(&func->dev,
+ "Unexpected interrupt cleared %d...\n",
+ i);
flags >>= 1;
}
}
}
-#endif /* WILC_SDIO_IRQ_GPIO */
{
u32 vmm_ctl;
@@ -886,9 +920,11 @@ static int sdio_clear_int_ext(u32 val)
cmd.raw = 0;
cmd.address = 0xf6;
cmd.data = vmm_ctl;
- ret = g_sdio.sdio_cmd52(&cmd);
- if (!ret) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed cmd52, set 0xf6 data (%d) ...\n", __LINE__);
+ ret = wilc_sdio_cmd52(wilc, &cmd);
+ if (ret) {
+ dev_err(&func->dev,
+ "Failed cmd52, set 0xf6 data (%d) ...\n",
+ __LINE__);
goto _fail_;
}
}
@@ -900,16 +936,18 @@ _fail_:
return 0;
}
-static int sdio_sync_ext(int nint /* how mant interrupts to enable. */)
+static int sdio_sync_ext(struct wilc *wilc, int nint)
{
+ struct sdio_func *func = dev_to_sdio_func(wilc->dev);
u32 reg;
if (nint > MAX_NUM_INT) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Too many interupts (%d)...\n", nint);
+ dev_err(&func->dev, "Too many interupts (%d)...\n", nint);
return 0;
}
if (nint > MAX_NUN_INT_THRPT_ENH2) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Error: Cannot support more than 5 interrupts when has_thrpt_enh2=1.\n");
+ dev_err(&func->dev,
+ "Cannot support more than 5 interrupts when has_thrpt_enh2=1.\n");
return 0;
}
@@ -918,71 +956,77 @@ static int sdio_sync_ext(int nint /* how mant interrupts to enable. */)
/**
* Disable power sequencer
**/
- if (!sdio_read_reg(WILC_MISC, &reg)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed read misc reg...\n");
+ if (!sdio_read_reg(wilc, WILC_MISC, &reg)) {
+ dev_err(&func->dev, "Failed read misc reg...\n");
return 0;
}
reg &= ~BIT(8);
- if (!sdio_write_reg(WILC_MISC, reg)) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed write misc reg...\n");
+ if (!sdio_write_reg(wilc, WILC_MISC, reg)) {
+ dev_err(&func->dev, "Failed write misc reg...\n");
return 0;
}
-#ifdef WILC_SDIO_IRQ_GPIO
- {
+ if (g_sdio.irq_gpio) {
u32 reg;
int ret, i;
/**
* interrupt pin mux select
**/
- ret = sdio_read_reg(WILC_PIN_MUX_0, &reg);
+ ret = sdio_read_reg(wilc, WILC_PIN_MUX_0, &reg);
if (!ret) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed read reg (%08x)...\n", WILC_PIN_MUX_0);
+ dev_err(&func->dev, "Failed read reg (%08x)...\n",
+ WILC_PIN_MUX_0);
return 0;
}
reg |= BIT(8);
- ret = sdio_write_reg(WILC_PIN_MUX_0, reg);
+ ret = sdio_write_reg(wilc, WILC_PIN_MUX_0, reg);
if (!ret) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed write reg (%08x)...\n", WILC_PIN_MUX_0);
+ dev_err(&func->dev, "Failed write reg (%08x)...\n",
+ WILC_PIN_MUX_0);
return 0;
}
/**
* interrupt enable
**/
- ret = sdio_read_reg(WILC_INTR_ENABLE, &reg);
+ ret = sdio_read_reg(wilc, WILC_INTR_ENABLE, &reg);
if (!ret) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed read reg (%08x)...\n", WILC_INTR_ENABLE);
+ dev_err(&func->dev, "Failed read reg (%08x)...\n",
+ WILC_INTR_ENABLE);
return 0;
}
for (i = 0; (i < 5) && (nint > 0); i++, nint--)
reg |= BIT((27 + i));
- ret = sdio_write_reg(WILC_INTR_ENABLE, reg);
+ ret = sdio_write_reg(wilc, WILC_INTR_ENABLE, reg);
if (!ret) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed write reg (%08x)...\n", WILC_INTR_ENABLE);
+ dev_err(&func->dev, "Failed write reg (%08x)...\n",
+ WILC_INTR_ENABLE);
return 0;
}
if (nint) {
- ret = sdio_read_reg(WILC_INTR2_ENABLE, &reg);
+ ret = sdio_read_reg(wilc, WILC_INTR2_ENABLE, &reg);
if (!ret) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed read reg (%08x)...\n", WILC_INTR2_ENABLE);
+ dev_err(&func->dev,
+ "Failed read reg (%08x)...\n",
+ WILC_INTR2_ENABLE);
return 0;
}
for (i = 0; (i < 3) && (nint > 0); i++, nint--)
reg |= BIT(i);
- ret = sdio_read_reg(WILC_INTR2_ENABLE, &reg);
+ ret = sdio_read_reg(wilc, WILC_INTR2_ENABLE, &reg);
if (!ret) {
- g_sdio.dPrint(N_ERR, "[wilc sdio]: Failed write reg (%08x)...\n", WILC_INTR2_ENABLE);
+ dev_err(&func->dev,
+ "Failed write reg (%08x)...\n",
+ WILC_INTR2_ENABLE);
return 0;
}
}
}
-#endif /* WILC_SDIO_IRQ_GPIO */
return 1;
}
@@ -992,23 +1036,20 @@ static int sdio_sync_ext(int nint /* how mant interrupts to enable. */)
*
********************************************/
-wilc_hif_func_t hif_sdio = {
- sdio_init,
- sdio_deinit,
- sdio_read_reg,
- sdio_write_reg,
- sdio_read,
- sdio_write,
- sdio_sync,
- sdio_clear_int,
- sdio_read_int,
- sdio_clear_int_ext,
- sdio_read_size,
- sdio_write,
- sdio_read,
- sdio_sync_ext,
-
- sdio_set_max_speed,
- sdio_set_default_speed,
+const struct wilc_hif_func wilc_hif_sdio = {
+ .hif_init = sdio_init,
+ .hif_deinit = sdio_deinit,
+ .hif_read_reg = sdio_read_reg,
+ .hif_write_reg = sdio_write_reg,
+ .hif_block_rx = sdio_read,
+ .hif_block_tx = sdio_write,
+ .hif_read_int = sdio_read_int,
+ .hif_clear_int_ext = sdio_clear_int_ext,
+ .hif_read_size = sdio_read_size,
+ .hif_block_tx_ext = sdio_write,
+ .hif_block_rx_ext = sdio_read,
+ .hif_sync_ext = sdio_sync_ext,
+ .enable_interrupt = wilc_sdio_enable_interrupt,
+ .disable_interrupt = wilc_sdio_disable_interrupt,
};
diff --git a/drivers/staging/wilc1000/wilc_spi.c b/drivers/staging/wilc1000/wilc_spi.c
index 599508beabf8..86de50c9f7f5 100644
--- a/drivers/staging/wilc1000/wilc_spi.c
+++ b/drivers/staging/wilc1000/wilc_spi.c
@@ -6,18 +6,25 @@
/* */
/* */
/* //////////////////////////////////////////////////////////////////////////// */
-
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/of_gpio.h>
+
+#include "linux_wlan_common.h"
#include <linux/string.h>
#include "wilc_wlan_if.h"
#include "wilc_wlan.h"
+#include "wilc_wfi_netdevice.h"
typedef struct {
- void *os_context;
- int (*spi_tx)(u8 *, u32);
- int (*spi_rx)(u8 *, u32);
- int (*spi_trx)(u8 *, u8 *, u32);
- int (*spi_max_speed)(void);
- wilc_debug_func dPrint;
int crc_off;
int nint;
int has_thrpt_enh;
@@ -25,8 +32,8 @@ typedef struct {
static wilc_spi_t g_spi;
-static int spi_read(u32, u8 *, u32);
-static int spi_write(u32, u8 *, u32);
+static int wilc_spi_read(struct wilc *wilc, u32, u8 *, u32);
+static int wilc_spi_write(struct wilc *wilc, u32, u8 *, u32);
/********************************************
*
@@ -111,165 +118,175 @@ static u8 crc7(u8 crc, const u8 *buffer, u32 len)
#define DATA_PKT_SZ_8K (8 * 1024)
#define DATA_PKT_SZ DATA_PKT_SZ_8K
-static int spi_cmd(u8 cmd, u32 adr, u32 data, u32 sz, u8 clockless)
+#define USE_SPI_DMA 0
+
+static const struct wilc1000_ops wilc1000_spi_ops;
+
+static int wilc_bus_probe(struct spi_device *spi)
{
- u8 bc[9];
- int len = 5;
- int result = N_OK;
+ int ret, gpio;
+ struct wilc *wilc;
- bc[0] = cmd;
- switch (cmd) {
- case CMD_SINGLE_READ: /* single word (4 bytes) read */
- bc[1] = (u8)(adr >> 16);
- bc[2] = (u8)(adr >> 8);
- bc[3] = (u8)adr;
- len = 5;
- break;
+ gpio = of_get_gpio(spi->dev.of_node, 0);
+ if (gpio < 0)
+ gpio = GPIO_NUM;
- case CMD_INTERNAL_READ: /* internal register read */
- bc[1] = (u8)(adr >> 8);
- if (clockless)
- bc[1] |= BIT(7);
- bc[2] = (u8)adr;
- bc[3] = 0x00;
- len = 5;
- break;
+ ret = wilc_netdev_init(&wilc, NULL, HIF_SPI, GPIO_NUM, &wilc_hif_spi);
+ if (ret)
+ return ret;
- case CMD_TERMINATE: /* termination */
- bc[1] = 0x00;
- bc[2] = 0x00;
- bc[3] = 0x00;
- len = 5;
- break;
+ spi_set_drvdata(spi, wilc);
+ wilc->dev = &spi->dev;
- case CMD_REPEAT: /* repeat */
- bc[1] = 0x00;
- bc[2] = 0x00;
- bc[3] = 0x00;
- len = 5;
- break;
+ return 0;
+}
- case CMD_RESET: /* reset */
- bc[1] = 0xff;
- bc[2] = 0xff;
- bc[3] = 0xff;
- len = 5;
- break;
+static int wilc_bus_remove(struct spi_device *spi)
+{
+ wilc_netdev_cleanup(spi_get_drvdata(spi));
+ return 0;
+}
- case CMD_DMA_WRITE: /* dma write */
- case CMD_DMA_READ: /* dma read */
- bc[1] = (u8)(adr >> 16);
- bc[2] = (u8)(adr >> 8);
- bc[3] = (u8)adr;
- bc[4] = (u8)(sz >> 8);
- bc[5] = (u8)(sz);
- len = 7;
- break;
+static const struct of_device_id wilc1000_of_match[] = {
+ { .compatible = "atmel,wilc_spi", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, wilc1000_of_match);
+
+struct spi_driver wilc1000_spi_driver = {
+ .driver = {
+ .name = MODALIAS,
+ .of_match_table = wilc1000_of_match,
+ },
+ .probe = wilc_bus_probe,
+ .remove = wilc_bus_remove,
+};
+module_spi_driver(wilc1000_spi_driver);
+MODULE_LICENSE("GPL");
- case CMD_DMA_EXT_WRITE: /* dma extended write */
- case CMD_DMA_EXT_READ: /* dma extended read */
- bc[1] = (u8)(adr >> 16);
- bc[2] = (u8)(adr >> 8);
- bc[3] = (u8)adr;
- bc[4] = (u8)(sz >> 16);
- bc[5] = (u8)(sz >> 8);
- bc[6] = (u8)(sz);
- len = 8;
- break;
+static int wilc_spi_tx(struct wilc *wilc, u8 *b, u32 len)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ int ret;
+ struct spi_message msg;
- case CMD_INTERNAL_WRITE: /* internal register write */
- bc[1] = (u8)(adr >> 8);
- if (clockless)
- bc[1] |= BIT(7);
- bc[2] = (u8)(adr);
- bc[3] = (u8)(data >> 24);
- bc[4] = (u8)(data >> 16);
- bc[5] = (u8)(data >> 8);
- bc[6] = (u8)(data);
- len = 8;
- break;
+ if (len > 0 && b) {
+ struct spi_transfer tr = {
+ .tx_buf = b,
+ .len = len,
+ .delay_usecs = 0,
+ };
+ char *r_buffer = kzalloc(len, GFP_KERNEL);
- case CMD_SINGLE_WRITE: /* single word write */
- bc[1] = (u8)(adr >> 16);
- bc[2] = (u8)(adr >> 8);
- bc[3] = (u8)(adr);
- bc[4] = (u8)(data >> 24);
- bc[5] = (u8)(data >> 16);
- bc[6] = (u8)(data >> 8);
- bc[7] = (u8)(data);
- len = 9;
- break;
+ if (!r_buffer)
+ return -ENOMEM;
- default:
- result = N_FAIL;
- break;
- }
+ tr.rx_buf = r_buffer;
+ dev_dbg(&spi->dev, "Request writing %d bytes\n", len);
- if (result) {
- if (!g_spi.crc_off)
- bc[len - 1] = (crc7(0x7f, (const u8 *)&bc[0], len - 1)) << 1;
- else
- len -= 1;
+ memset(&msg, 0, sizeof(msg));
+ spi_message_init(&msg);
+ msg.spi = spi;
+ msg.is_dma_mapped = USE_SPI_DMA;
+ spi_message_add_tail(&tr, &msg);
- if (!g_spi.spi_tx(bc, len)) {
- PRINT_ER("[wilc spi]: Failed cmd write, bus error...\n");
- result = N_FAIL;
- }
+ ret = spi_sync(spi, &msg);
+ if (ret < 0)
+ dev_err(&spi->dev, "SPI transaction failed\n");
+
+ kfree(r_buffer);
+ } else {
+ dev_err(&spi->dev,
+ "can't write data with the following length: %d\n",
+ len);
+ dev_err(&spi->dev,
+ "FAILED due to NULL buffer or ZERO length check the following length: %d\n",
+ len);
+ ret = -EINVAL;
}
- return result;
+ return ret;
}
-static int spi_cmd_rsp(u8 cmd)
+static int wilc_spi_rx(struct wilc *wilc, u8 *rb, u32 rlen)
{
- u8 rsp;
- int result = N_OK;
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ int ret;
- /**
- * Command/Control response
- **/
- if ((cmd == CMD_RESET) ||
- (cmd == CMD_TERMINATE) ||
- (cmd == CMD_REPEAT)) {
- if (!g_spi.spi_rx(&rsp, 1)) {
- result = N_FAIL;
- goto _fail_;
- }
- }
+ if (rlen > 0) {
+ struct spi_message msg;
+ struct spi_transfer tr = {
+ .rx_buf = rb,
+ .len = rlen,
+ .delay_usecs = 0,
- if (!g_spi.spi_rx(&rsp, 1)) {
- PRINT_ER("[wilc spi]: Failed cmd response read, bus error...\n");
- result = N_FAIL;
- goto _fail_;
- }
+ };
+ char *t_buffer = kzalloc(rlen, GFP_KERNEL);
- if (rsp != cmd) {
- PRINT_ER("[wilc spi]: Failed cmd response, cmd (%02x), resp (%02x)\n", cmd, rsp);
- result = N_FAIL;
- goto _fail_;
- }
+ if (!t_buffer)
+ return -ENOMEM;
- /**
- * State response
- **/
- if (!g_spi.spi_rx(&rsp, 1)) {
- PRINT_ER("[wilc spi]: Failed cmd state read, bus error...\n");
- result = N_FAIL;
- goto _fail_;
- }
+ tr.tx_buf = t_buffer;
- if (rsp != 0x00) {
- PRINT_ER("[wilc spi]: Failed cmd state response state (%02x)\n", rsp);
- result = N_FAIL;
+ memset(&msg, 0, sizeof(msg));
+ spi_message_init(&msg);
+ msg.spi = spi;
+ msg.is_dma_mapped = USE_SPI_DMA;
+ spi_message_add_tail(&tr, &msg);
+
+ ret = spi_sync(spi, &msg);
+ if (ret < 0)
+ dev_err(&spi->dev, "SPI transaction failed\n");
+ kfree(t_buffer);
+ } else {
+ dev_err(&spi->dev,
+ "can't read data with the following length: %u\n",
+ rlen);
+ ret = -EINVAL;
}
-_fail_:
+ return ret;
+}
- return result;
+static int wilc_spi_tx_rx(struct wilc *wilc, u8 *wb, u8 *rb, u32 rlen)
+{
+ struct spi_device *spi = to_spi_device(wilc->dev);
+ int ret;
+
+ if (rlen > 0) {
+ struct spi_message msg;
+ struct spi_transfer tr = {
+ .rx_buf = rb,
+ .tx_buf = wb,
+ .len = rlen,
+ .bits_per_word = 8,
+ .delay_usecs = 0,
+
+ };
+
+ memset(&msg, 0, sizeof(msg));
+ spi_message_init(&msg);
+ msg.spi = spi;
+ msg.is_dma_mapped = USE_SPI_DMA;
+
+ spi_message_add_tail(&tr, &msg);
+ ret = spi_sync(spi, &msg);
+ if (ret < 0)
+ dev_err(&spi->dev, "SPI transaction failed\n");
+ } else {
+ dev_err(&spi->dev,
+ "can't read data with the following length: %u\n",
+ rlen);
+ ret = -EINVAL;
+ }
+
+ return ret;
}
-static int spi_cmd_complete(u8 cmd, u32 adr, u8 *b, u32 sz, u8 clockless)
+static int spi_cmd_complete(struct wilc *wilc, u8 cmd, u32 adr, u8 *b, u32 sz,
+ u8 clockless)
{
+ struct spi_device *spi = to_spi_device(wilc->dev);
u8 wb[32], rb[32];
u8 wix, rix;
u32 len2;
@@ -398,7 +415,7 @@ static int spi_cmd_complete(u8 cmd, u32 adr, u8 *b, u32 sz, u8 clockless)
#undef NUM_DUMMY_BYTES
if (len2 > ARRAY_SIZE(wb)) {
- PRINT_ER("[wilc spi]: spi buffer size too small (%d) (%zu)\n",
+ dev_err(&spi->dev, "spi buffer size too small (%d) (%zu)\n",
len2, ARRAY_SIZE(wb));
result = N_FAIL;
return result;
@@ -409,8 +426,8 @@ static int spi_cmd_complete(u8 cmd, u32 adr, u8 *b, u32 sz, u8 clockless)
}
rix = len;
- if (!g_spi.spi_trx(wb, rb, len2)) {
- PRINT_ER("[wilc spi]: Failed cmd write, bus error...\n");
+ if (wilc_spi_tx_rx(wilc, wb, rb, len2)) {
+ dev_err(&spi->dev, "Failed cmd write, bus error...\n");
result = N_FAIL;
return result;
}
@@ -430,7 +447,7 @@ static int spi_cmd_complete(u8 cmd, u32 adr, u8 *b, u32 sz, u8 clockless)
/* } while(&rptr[1] <= &rb[len2]); */
if (rsp != cmd) {
- PRINT_ER("[wilc spi]: Failed cmd response, cmd (%02x)"
+ dev_err(&spi->dev, "Failed cmd response, cmd (%02x)"
", resp (%02x)\n", cmd, rsp);
result = N_FAIL;
return result;
@@ -441,8 +458,8 @@ static int spi_cmd_complete(u8 cmd, u32 adr, u8 *b, u32 sz, u8 clockless)
**/
rsp = rb[rix++];
if (rsp != 0x00) {
- PRINT_ER("[wilc spi]: Failed cmd state response "
- "state (%02x)\n", rsp);
+ dev_err(&spi->dev, "Failed cmd state response state (%02x)\n",
+ rsp);
result = N_FAIL;
return result;
}
@@ -469,8 +486,8 @@ static int spi_cmd_complete(u8 cmd, u32 adr, u8 *b, u32 sz, u8 clockless)
} while (retry--);
if (retry <= 0) {
- PRINT_ER("[wilc spi]: Error, data read "
- "response (%02x)\n", rsp);
+ dev_err(&spi->dev,
+ "Error, data read response (%02x)\n", rsp);
result = N_RESET;
return result;
}
@@ -485,7 +502,8 @@ static int spi_cmd_complete(u8 cmd, u32 adr, u8 *b, u32 sz, u8 clockless)
b[2] = rb[rix++];
b[3] = rb[rix++];
} else {
- PRINT_ER("[wilc spi]: buffer overrun when reading data.\n");
+ dev_err(&spi->dev,
+ "buffer overrun when reading data.\n");
result = N_FAIL;
return result;
}
@@ -498,7 +516,7 @@ static int spi_cmd_complete(u8 cmd, u32 adr, u8 *b, u32 sz, u8 clockless)
crc[0] = rb[rix++];
crc[1] = rb[rix++];
} else {
- PRINT_ER("[wilc spi]: buffer overrun when reading crc.\n");
+ dev_err(&spi->dev,"buffer overrun when reading crc.\n");
result = N_FAIL;
return result;
}
@@ -524,8 +542,8 @@ static int spi_cmd_complete(u8 cmd, u32 adr, u8 *b, u32 sz, u8 clockless)
/**
* Read bytes
**/
- if (!g_spi.spi_rx(&b[ix], nbytes)) {
- PRINT_ER("[wilc spi]: Failed data block read, bus error...\n");
+ if (wilc_spi_rx(wilc, &b[ix], nbytes)) {
+ dev_err(&spi->dev, "Failed data block read, bus error...\n");
result = N_FAIL;
goto _error_;
}
@@ -534,8 +552,8 @@ static int spi_cmd_complete(u8 cmd, u32 adr, u8 *b, u32 sz, u8 clockless)
* Read Crc
**/
if (!g_spi.crc_off) {
- if (!g_spi.spi_rx(crc, 2)) {
- PRINT_ER("[wilc spi]: Failed data block crc read, bus error...\n");
+ if (wilc_spi_rx(wilc, crc, 2)) {
+ dev_err(&spi->dev, "Failed data block crc read, bus error...\n");
result = N_FAIL;
goto _error_;
}
@@ -565,8 +583,8 @@ static int spi_cmd_complete(u8 cmd, u32 adr, u8 *b, u32 sz, u8 clockless)
**/
retry = 10;
do {
- if (!g_spi.spi_rx(&rsp, 1)) {
- PRINT_ER("[wilc spi]: Failed data response read, bus error...\n");
+ if (wilc_spi_rx(wilc, &rsp, 1)) {
+ dev_err(&spi->dev, "Failed data response read, bus error...\n");
result = N_FAIL;
break;
}
@@ -581,8 +599,8 @@ static int spi_cmd_complete(u8 cmd, u32 adr, u8 *b, u32 sz, u8 clockless)
/**
* Read bytes
**/
- if (!g_spi.spi_rx(&b[ix], nbytes)) {
- PRINT_ER("[wilc spi]: Failed data block read, bus error...\n");
+ if (wilc_spi_rx(wilc, &b[ix], nbytes)) {
+ dev_err(&spi->dev, "Failed data block read, bus error...\n");
result = N_FAIL;
break;
}
@@ -591,8 +609,8 @@ static int spi_cmd_complete(u8 cmd, u32 adr, u8 *b, u32 sz, u8 clockless)
* Read Crc
**/
if (!g_spi.crc_off) {
- if (!g_spi.spi_rx(crc, 2)) {
- PRINT_ER("[wilc spi]: Failed data block crc read, bus error...\n");
+ if (wilc_spi_rx(wilc, crc, 2)) {
+ dev_err(&spi->dev, "Failed data block crc read, bus error...\n");
result = N_FAIL;
break;
}
@@ -607,76 +625,9 @@ _error_:
return result;
}
-static int spi_data_read(u8 *b, u32 sz)
-{
- int retry, ix, nbytes;
- int result = N_OK;
- u8 crc[2];
- u8 rsp;
-
- /**
- * Data
- **/
- ix = 0;
- do {
- if (sz <= DATA_PKT_SZ)
- nbytes = sz;
- else
- nbytes = DATA_PKT_SZ;
-
- /**
- * Data Respnose header
- **/
- retry = 10;
- do {
- if (!g_spi.spi_rx(&rsp, 1)) {
- PRINT_ER("[wilc spi]: Failed data response read, bus error...\n");
- result = N_FAIL;
- break;
- }
- if (((rsp >> 4) & 0xf) == 0xf)
- break;
- } while (retry--);
-
- if (result == N_FAIL)
- break;
-
- if (retry <= 0) {
- PRINT_ER("[wilc spi]: Failed data response read...(%02x)\n", rsp);
- result = N_FAIL;
- break;
- }
-
- /**
- * Read bytes
- **/
- if (!g_spi.spi_rx(&b[ix], nbytes)) {
- PRINT_ER("[wilc spi]: Failed data block read, bus error...\n");
- result = N_FAIL;
- break;
- }
-
- /**
- * Read Crc
- **/
- if (!g_spi.crc_off) {
- if (!g_spi.spi_rx(crc, 2)) {
- PRINT_ER("[wilc spi]: Failed data block crc read, bus error...\n");
- result = N_FAIL;
- break;
- }
- }
-
- ix += nbytes;
- sz -= nbytes;
-
- } while (sz);
-
- return result;
-}
-
-static int spi_data_write(u8 *b, u32 sz)
+static int spi_data_write(struct wilc *wilc, u8 *b, u32 sz)
{
+ struct spi_device *spi = to_spi_device(wilc->dev);
int ix, nbytes;
int result = 1;
u8 cmd, order, crc[2] = {0};
@@ -709,8 +660,9 @@ static int spi_data_write(u8 *b, u32 sz)
order = 0x2;
}
cmd |= order;
- if (!g_spi.spi_tx(&cmd, 1)) {
- PRINT_ER("[wilc spi]: Failed data block cmd write, bus error...\n");
+ if (wilc_spi_tx(wilc, &cmd, 1)) {
+ dev_err(&spi->dev,
+ "Failed data block cmd write, bus error...\n");
result = N_FAIL;
break;
}
@@ -718,8 +670,9 @@ static int spi_data_write(u8 *b, u32 sz)
/**
* Write data
**/
- if (!g_spi.spi_tx(&b[ix], nbytes)) {
- PRINT_ER("[wilc spi]: Failed data block write, bus error...\n");
+ if (wilc_spi_tx(wilc, &b[ix], nbytes)) {
+ dev_err(&spi->dev,
+ "Failed data block write, bus error...\n");
result = N_FAIL;
break;
}
@@ -728,8 +681,8 @@ static int spi_data_write(u8 *b, u32 sz)
* Write Crc
**/
if (!g_spi.crc_off) {
- if (!g_spi.spi_tx(crc, 2)) {
- PRINT_ER("[wilc spi]: Failed data block crc write, bus error...\n");
+ if (wilc_spi_tx(wilc, crc, 2)) {
+ dev_err(&spi->dev,"Failed data block crc write, bus error...\n");
result = N_FAIL;
break;
}
@@ -752,34 +705,34 @@ static int spi_data_write(u8 *b, u32 sz)
*
********************************************/
-static int spi_internal_write(u32 adr, u32 dat)
+static int spi_internal_write(struct wilc *wilc, u32 adr, u32 dat)
{
+ struct spi_device *spi = to_spi_device(wilc->dev);
int result;
-#ifdef BIG_ENDIAN
- dat = BYTE_SWAP(dat);
-#endif
- result = spi_cmd_complete(CMD_INTERNAL_WRITE, adr, (u8 *)&dat, 4, 0);
+ dat = cpu_to_le32(dat);
+ result = spi_cmd_complete(wilc, CMD_INTERNAL_WRITE, adr, (u8 *)&dat, 4,
+ 0);
if (result != N_OK) {
- PRINT_ER("[wilc spi]: Failed internal write cmd...\n");
+ dev_err(&spi->dev, "Failed internal write cmd...\n");
}
return result;
}
-static int spi_internal_read(u32 adr, u32 *data)
+static int spi_internal_read(struct wilc *wilc, u32 adr, u32 *data)
{
+ struct spi_device *spi = to_spi_device(wilc->dev);
int result;
- result = spi_cmd_complete(CMD_INTERNAL_READ, adr, (u8 *)data, 4, 0);
+ result = spi_cmd_complete(wilc, CMD_INTERNAL_READ, adr, (u8 *)data, 4,
+ 0);
if (result != N_OK) {
- PRINT_ER("[wilc spi]: Failed internal read cmd...\n");
+ dev_err(&spi->dev, "Failed internal read cmd...\n");
return 0;
}
-#ifdef BIG_ENDIAN
- *data = BYTE_SWAP(*data);
-#endif
+ *data = cpu_to_le32(*data);
return 1;
}
@@ -790,31 +743,31 @@ static int spi_internal_read(u32 adr, u32 *data)
*
********************************************/
-static int spi_write_reg(u32 addr, u32 data)
+static int wilc_spi_write_reg(struct wilc *wilc, u32 addr, u32 data)
{
+ struct spi_device *spi = to_spi_device(wilc->dev);
int result = N_OK;
u8 cmd = CMD_SINGLE_WRITE;
u8 clockless = 0;
-#ifdef BIG_ENDIAN
- data = BYTE_SWAP(data);
-#endif
+ data = cpu_to_le32(data);
if (addr < 0x30) {
/* Clockless register*/
cmd = CMD_INTERNAL_WRITE;
clockless = 1;
}
- result = spi_cmd_complete(cmd, addr, (u8 *)&data, 4, clockless);
+ result = spi_cmd_complete(wilc, cmd, addr, (u8 *)&data, 4, clockless);
if (result != N_OK) {
- PRINT_ER("[wilc spi]: Failed cmd, write reg (%08x)...\n", addr);
+ dev_err(&spi->dev, "Failed cmd, write reg (%08x)...\n", addr);
}
return result;
}
-static int spi_write(u32 addr, u8 *buf, u32 size)
+static int wilc_spi_write(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
{
+ struct spi_device *spi = to_spi_device(wilc->dev);
int result;
u8 cmd = CMD_DMA_EXT_WRITE;
@@ -824,60 +777,61 @@ static int spi_write(u32 addr, u8 *buf, u32 size)
if (size <= 4)
return 0;
- result = spi_cmd_complete(cmd, addr, NULL, size, 0);
+ result = spi_cmd_complete(wilc, cmd, addr, NULL, size, 0);
if (result != N_OK) {
- PRINT_ER("[wilc spi]: Failed cmd, write block (%08x)...\n", addr);
+ dev_err(&spi->dev,
+ "Failed cmd, write block (%08x)...\n", addr);
return 0;
}
/**
* Data
**/
- result = spi_data_write(buf, size);
+ result = spi_data_write(wilc, buf, size);
if (result != N_OK) {
- PRINT_ER("[wilc spi]: Failed block data write...\n");
+ dev_err(&spi->dev, "Failed block data write...\n");
}
return 1;
}
-static int spi_read_reg(u32 addr, u32 *data)
+static int wilc_spi_read_reg(struct wilc *wilc, u32 addr, u32 *data)
{
+ struct spi_device *spi = to_spi_device(wilc->dev);
int result = N_OK;
u8 cmd = CMD_SINGLE_READ;
u8 clockless = 0;
if (addr < 0x30) {
- /* PRINT_ER("***** read addr %d\n\n", addr); */
+ /* dev_err(&spi->dev, "***** read addr %d\n\n", addr); */
/* Clockless register*/
cmd = CMD_INTERNAL_READ;
clockless = 1;
}
- result = spi_cmd_complete(cmd, addr, (u8 *)data, 4, clockless);
+ result = spi_cmd_complete(wilc, cmd, addr, (u8 *)data, 4, clockless);
if (result != N_OK) {
- PRINT_ER("[wilc spi]: Failed cmd, read reg (%08x)...\n", addr);
+ dev_err(&spi->dev, "Failed cmd, read reg (%08x)...\n", addr);
return 0;
}
-#ifdef BIG_ENDIAN
- *data = BYTE_SWAP(*data);
-#endif
+ *data = cpu_to_le32(*data);
return 1;
}
-static int spi_read(u32 addr, u8 *buf, u32 size)
+static int wilc_spi_read(struct wilc *wilc, u32 addr, u8 *buf, u32 size)
{
+ struct spi_device *spi = to_spi_device(wilc->dev);
u8 cmd = CMD_DMA_EXT_READ;
int result;
if (size <= 4)
return 0;
- result = spi_cmd_complete(cmd, addr, buf, size, 0);
+ result = spi_cmd_complete(wilc, cmd, addr, buf, size, 0);
if (result != N_OK) {
- PRINT_ER("[wilc spi]: Failed cmd, read block (%08x)...\n", addr);
+ dev_err(&spi->dev, "Failed cmd, read block (%08x)...\n", addr);
return 0;
}
@@ -890,20 +844,7 @@ static int spi_read(u32 addr, u8 *buf, u32 size)
*
********************************************/
-static int spi_clear_int(void)
-{
- u32 reg;
-
- if (!spi_read_reg(WILC_HOST_RX_CTRL_0, &reg)) {
- PRINT_ER("[wilc spi]: Failed read reg (%08x)...\n", WILC_HOST_RX_CTRL_0);
- return 0;
- }
- reg &= ~0x1;
- spi_write_reg(WILC_HOST_RX_CTRL_0, reg);
- return 1;
-}
-
-static int spi_deinit(void *pv)
+static int _wilc_spi_deinit(struct wilc *wilc)
{
/**
* TODO:
@@ -911,46 +852,9 @@ static int spi_deinit(void *pv)
return 1;
}
-static int spi_sync(void)
-{
- u32 reg;
- int ret;
-
- /**
- * interrupt pin mux select
- **/
- ret = spi_read_reg(WILC_PIN_MUX_0, &reg);
- if (!ret) {
- PRINT_ER("[wilc spi]: Failed read reg (%08x)...\n", WILC_PIN_MUX_0);
- return 0;
- }
- reg |= BIT(8);
- ret = spi_write_reg(WILC_PIN_MUX_0, reg);
- if (!ret) {
- PRINT_ER("[wilc spi]: Failed write reg (%08x)...\n", WILC_PIN_MUX_0);
- return 0;
- }
-
- /**
- * interrupt enable
- **/
- ret = spi_read_reg(WILC_INTR_ENABLE, &reg);
- if (!ret) {
- PRINT_ER("[wilc spi]: Failed read reg (%08x)...\n", WILC_INTR_ENABLE);
- return 0;
- }
- reg |= BIT(16);
- ret = spi_write_reg(WILC_INTR_ENABLE, reg);
- if (!ret) {
- PRINT_ER("[wilc spi]: Failed write reg (%08x)...\n", WILC_INTR_ENABLE);
- return 0;
- }
-
- return 1;
-}
-
-static int spi_init(wilc_wlan_inp_t *inp, wilc_debug_func func)
+static int wilc_spi_init(struct wilc *wilc)
{
+ struct spi_device *spi = to_spi_device(wilc->dev);
u32 reg;
u32 chipid;
@@ -958,8 +862,8 @@ static int spi_init(wilc_wlan_inp_t *inp, wilc_debug_func func)
if (isinit) {
- if (!spi_read_reg(0x1000, &chipid)) {
- PRINT_ER("[wilc spi]: Fail cmd read chip id...\n");
+ if (!wilc_spi_read_reg(wilc, 0x1000, &chipid)) {
+ dev_err(&spi->dev, "Fail cmd read chip id...\n");
return 0;
}
return 1;
@@ -967,21 +871,6 @@ static int spi_init(wilc_wlan_inp_t *inp, wilc_debug_func func)
memset(&g_spi, 0, sizeof(wilc_spi_t));
- g_spi.dPrint = func;
- g_spi.os_context = inp->os_context.os_private;
- if (inp->io_func.io_init) {
- if (!inp->io_func.io_init(g_spi.os_context)) {
- PRINT_ER("[wilc spi]: Failed io init bus...\n");
- return 0;
- }
- } else {
- return 0;
- }
- g_spi.spi_tx = inp->io_func.u.spi.spi_tx;
- g_spi.spi_rx = inp->io_func.u.spi.spi_rx;
- g_spi.spi_trx = inp->io_func.u.spi.spi_trx;
- g_spi.spi_max_speed = inp->io_func.u.spi.spi_max_speed;
-
/**
* configure protocol
**/
@@ -989,14 +878,15 @@ static int spi_init(wilc_wlan_inp_t *inp, wilc_debug_func func)
/* TODO: We can remove the CRC trials if there is a definite way to reset */
/* the SPI to it's initial value. */
- if (!spi_internal_read(WILC_SPI_PROTOCOL_OFFSET, &reg)) {
+ if (!spi_internal_read(wilc, WILC_SPI_PROTOCOL_OFFSET, &reg)) {
/* Read failed. Try with CRC off. This might happen when module
* is removed but chip isn't reset*/
g_spi.crc_off = 1;
- PRINT_ER("[wilc spi]: Failed internal read protocol with CRC on, retyring with CRC off...\n");
- if (!spi_internal_read(WILC_SPI_PROTOCOL_OFFSET, &reg)) {
+ dev_err(&spi->dev, "Failed internal read protocol with CRC on, retyring with CRC off...\n");
+ if (!spi_internal_read(wilc, WILC_SPI_PROTOCOL_OFFSET, &reg)) {
/* Reaad failed with both CRC on and off, something went bad */
- PRINT_ER("[wilc spi]: Failed internal read protocol...\n");
+ dev_err(&spi->dev,
+ "Failed internal read protocol...\n");
return 0;
}
}
@@ -1004,8 +894,8 @@ static int spi_init(wilc_wlan_inp_t *inp, wilc_debug_func func)
reg &= ~0xc; /* disable crc checking */
reg &= ~0x70;
reg |= (0x5 << 4);
- if (!spi_internal_write(WILC_SPI_PROTOCOL_OFFSET, reg)) {
- PRINT_ER("[wilc spi %d]: Failed internal write protocol reg...\n", __LINE__);
+ if (!spi_internal_write(wilc, WILC_SPI_PROTOCOL_OFFSET, reg)) {
+ dev_err(&spi->dev, "[wilc spi %d]: Failed internal write protocol reg...\n", __LINE__);
return 0;
}
g_spi.crc_off = 1;
@@ -1015,11 +905,11 @@ static int spi_init(wilc_wlan_inp_t *inp, wilc_debug_func func)
/**
* make sure can read back chip id correctly
**/
- if (!spi_read_reg(0x1000, &chipid)) {
- PRINT_ER("[wilc spi]: Fail cmd read chip id...\n");
+ if (!wilc_spi_read_reg(wilc, 0x1000, &chipid)) {
+ dev_err(&spi->dev, "Fail cmd read chip id...\n");
return 0;
}
- /* PRINT_ER("[wilc spi]: chipid (%08x)\n", chipid); */
+ /* dev_err(&spi->dev, "chipid (%08x)\n", chipid); */
g_spi.has_thrpt_enh = 1;
@@ -1028,29 +918,24 @@ static int spi_init(wilc_wlan_inp_t *inp, wilc_debug_func func)
return 1;
}
-static void spi_max_bus_speed(void)
-{
- g_spi.spi_max_speed();
-}
-
-static void spi_default_bus_speed(void)
-{
-}
-
-static int spi_read_size(u32 *size)
+static int wilc_spi_read_size(struct wilc *wilc, u32 *size)
{
+ struct spi_device *spi = to_spi_device(wilc->dev);
int ret;
if (g_spi.has_thrpt_enh) {
- ret = spi_internal_read(0xe840 - WILC_SPI_REG_BASE, size);
+ ret = spi_internal_read(wilc, 0xe840 - WILC_SPI_REG_BASE,
+ size);
*size = *size & IRQ_DMA_WD_CNT_MASK;
} else {
u32 tmp;
u32 byte_cnt;
- ret = spi_read_reg(WILC_VMM_TO_HOST_SIZE, &byte_cnt);
+ ret = wilc_spi_read_reg(wilc, WILC_VMM_TO_HOST_SIZE,
+ &byte_cnt);
if (!ret) {
- PRINT_ER("[wilc spi]: Failed read WILC_VMM_TO_HOST_SIZE ...\n");
+ dev_err(&spi->dev,
+ "Failed read WILC_VMM_TO_HOST_SIZE ...\n");
goto _fail_;
}
tmp = (byte_cnt >> 2) & IRQ_DMA_WD_CNT_MASK;
@@ -1065,19 +950,23 @@ _fail_:
-static int spi_read_int(u32 *int_status)
+static int wilc_spi_read_int(struct wilc *wilc, u32 *int_status)
{
+ struct spi_device *spi = to_spi_device(wilc->dev);
int ret;
if (g_spi.has_thrpt_enh) {
- ret = spi_internal_read(0xe840 - WILC_SPI_REG_BASE, int_status);
+ ret = spi_internal_read(wilc, 0xe840 - WILC_SPI_REG_BASE,
+ int_status);
} else {
u32 tmp;
u32 byte_cnt;
- ret = spi_read_reg(WILC_VMM_TO_HOST_SIZE, &byte_cnt);
+ ret = wilc_spi_read_reg(wilc, WILC_VMM_TO_HOST_SIZE,
+ &byte_cnt);
if (!ret) {
- PRINT_ER("[wilc spi]: Failed read WILC_VMM_TO_HOST_SIZE ...\n");
+ dev_err(&spi->dev,
+ "Failed read WILC_VMM_TO_HOST_SIZE ...\n");
goto _fail_;
}
tmp = (byte_cnt >> 2) & IRQ_DMA_WD_CNT_MASK;
@@ -1091,11 +980,12 @@ static int spi_read_int(u32 *int_status)
happended = 0;
- spi_read_reg(0x1a90, &irq_flags);
+ wilc_spi_read_reg(wilc, 0x1a90, &irq_flags);
tmp |= ((irq_flags >> 27) << IRG_FLAGS_OFFSET);
if (g_spi.nint > 5) {
- spi_read_reg(0x1a94, &irq_flags);
+ wilc_spi_read_reg(wilc, 0x1a94,
+ &irq_flags);
tmp |= (((irq_flags >> 0) & 0x7) << (IRG_FLAGS_OFFSET + 5));
}
@@ -1105,7 +995,7 @@ static int spi_read_int(u32 *int_status)
unkmown_mask = ~((1ul << g_spi.nint) - 1);
if ((tmp >> IRG_FLAGS_OFFSET) & unkmown_mask) {
- PRINT_ER("[wilc spi]: Unexpected interrupt (2): j=%d, tmp=%x, mask=%x\n", j, tmp, unkmown_mask);
+ dev_err(&spi->dev, "Unexpected interrupt (2): j=%d, tmp=%x, mask=%x\n", j, tmp, unkmown_mask);
happended = 1;
}
}
@@ -1121,12 +1011,14 @@ _fail_:
return ret;
}
-static int spi_clear_int_ext(u32 val)
+static int wilc_spi_clear_int_ext(struct wilc *wilc, u32 val)
{
+ struct spi_device *spi = to_spi_device(wilc->dev);
int ret;
if (g_spi.has_thrpt_enh) {
- ret = spi_internal_write(0xe844 - WILC_SPI_REG_BASE, val);
+ ret = spi_internal_write(wilc, 0xe844 - WILC_SPI_REG_BASE,
+ val);
} else {
u32 flags;
@@ -1138,18 +1030,22 @@ static int spi_clear_int_ext(u32 val)
for (i = 0; i < g_spi.nint; i++) {
/* No matter what you write 1 or 0, it will clear interrupt. */
if (flags & 1)
- ret = spi_write_reg(0x10c8 + i * 4, 1);
+ ret = wilc_spi_write_reg(wilc, 0x10c8 + i * 4, 1);
if (!ret)
break;
flags >>= 1;
}
if (!ret) {
- PRINT_ER("[wilc spi]: Failed spi_write_reg, set reg %x ...\n", 0x10c8 + i * 4);
+ dev_err(&spi->dev,
+ "Failed wilc_spi_write_reg, set reg %x ...\n",
+ 0x10c8 + i * 4);
goto _fail_;
}
for (i = g_spi.nint; i < MAX_NUM_INT; i++) {
if (flags & 1)
- PRINT_ER("[wilc spi]: Unexpected interrupt cleared %d...\n", i);
+ dev_err(&spi->dev,
+ "Unexpected interrupt cleared %d...\n",
+ i);
flags >>= 1;
}
}
@@ -1165,9 +1061,11 @@ static int spi_clear_int_ext(u32 val)
if ((val & SEL_VMM_TBL1) == SEL_VMM_TBL1)
tbl_ctl |= BIT(1);
- ret = spi_write_reg(WILC_VMM_TBL_CTL, tbl_ctl);
+ ret = wilc_spi_write_reg(wilc, WILC_VMM_TBL_CTL,
+ tbl_ctl);
if (!ret) {
- PRINT_ER("[wilc spi]: fail write reg vmm_tbl_ctl...\n");
+ dev_err(&spi->dev,
+ "fail write reg vmm_tbl_ctl...\n");
goto _fail_;
}
@@ -1175,9 +1073,10 @@ static int spi_clear_int_ext(u32 val)
/**
* enable vmm transfer.
**/
- ret = spi_write_reg(WILC_VMM_CORE_CTL, 1);
+ ret = wilc_spi_write_reg(wilc,
+ WILC_VMM_CORE_CTL, 1);
if (!ret) {
- PRINT_ER("[wilc spi]: fail write reg vmm_core_ctl...\n");
+ dev_err(&spi->dev,"fail write reg vmm_core_ctl...\n");
goto _fail_;
}
}
@@ -1187,13 +1086,14 @@ _fail_:
return ret;
}
-static int spi_sync_ext(int nint /* how mant interrupts to enable. */)
+static int wilc_spi_sync_ext(struct wilc *wilc, int nint)
{
+ struct spi_device *spi = to_spi_device(wilc->dev);
u32 reg;
int ret, i;
if (nint > MAX_NUM_INT) {
- PRINT_ER("[wilc spi]: Too many interupts (%d)...\n", nint);
+ dev_err(&spi->dev, "Too many interupts (%d)...\n", nint);
return 0;
}
@@ -1202,39 +1102,44 @@ static int spi_sync_ext(int nint /* how mant interrupts to enable. */)
/**
* interrupt pin mux select
**/
- ret = spi_read_reg(WILC_PIN_MUX_0, &reg);
+ ret = wilc_spi_read_reg(wilc, WILC_PIN_MUX_0, &reg);
if (!ret) {
- PRINT_ER("[wilc spi]: Failed read reg (%08x)...\n", WILC_PIN_MUX_0);
+ dev_err(&spi->dev, "Failed read reg (%08x)...\n",
+ WILC_PIN_MUX_0);
return 0;
}
reg |= BIT(8);
- ret = spi_write_reg(WILC_PIN_MUX_0, reg);
+ ret = wilc_spi_write_reg(wilc, WILC_PIN_MUX_0, reg);
if (!ret) {
- PRINT_ER("[wilc spi]: Failed write reg (%08x)...\n", WILC_PIN_MUX_0);
+ dev_err(&spi->dev, "Failed write reg (%08x)...\n",
+ WILC_PIN_MUX_0);
return 0;
}
/**
* interrupt enable
**/
- ret = spi_read_reg(WILC_INTR_ENABLE, &reg);
+ ret = wilc_spi_read_reg(wilc, WILC_INTR_ENABLE, &reg);
if (!ret) {
- PRINT_ER("[wilc spi]: Failed read reg (%08x)...\n", WILC_INTR_ENABLE);
+ dev_err(&spi->dev, "Failed read reg (%08x)...\n",
+ WILC_INTR_ENABLE);
return 0;
}
for (i = 0; (i < 5) && (nint > 0); i++, nint--) {
reg |= (BIT((27 + i)));
}
- ret = spi_write_reg(WILC_INTR_ENABLE, reg);
+ ret = wilc_spi_write_reg(wilc, WILC_INTR_ENABLE, reg);
if (!ret) {
- PRINT_ER("[wilc spi]: Failed write reg (%08x)...\n", WILC_INTR_ENABLE);
+ dev_err(&spi->dev, "Failed write reg (%08x)...\n",
+ WILC_INTR_ENABLE);
return 0;
}
if (nint) {
- ret = spi_read_reg(WILC_INTR2_ENABLE, &reg);
+ ret = wilc_spi_read_reg(wilc, WILC_INTR2_ENABLE, &reg);
if (!ret) {
- PRINT_ER("[wilc spi]: Failed read reg (%08x)...\n", WILC_INTR2_ENABLE);
+ dev_err(&spi->dev, "Failed read reg (%08x)...\n",
+ WILC_INTR2_ENABLE);
return 0;
}
@@ -1242,9 +1147,10 @@ static int spi_sync_ext(int nint /* how mant interrupts to enable. */)
reg |= BIT(i);
}
- ret = spi_read_reg(WILC_INTR2_ENABLE, &reg);
+ ret = wilc_spi_read_reg(wilc, WILC_INTR2_ENABLE, &reg);
if (!ret) {
- PRINT_ER("[wilc spi]: Failed write reg (%08x)...\n", WILC_INTR2_ENABLE);
+ dev_err(&spi->dev, "Failed write reg (%08x)...\n",
+ WILC_INTR2_ENABLE);
return 0;
}
}
@@ -1256,21 +1162,17 @@ static int spi_sync_ext(int nint /* how mant interrupts to enable. */)
* Global spi HIF function table
*
********************************************/
-wilc_hif_func_t hif_spi = {
- spi_init,
- spi_deinit,
- spi_read_reg,
- spi_write_reg,
- spi_read,
- spi_write,
- spi_sync,
- spi_clear_int,
- spi_read_int,
- spi_clear_int_ext,
- spi_read_size,
- spi_write,
- spi_read,
- spi_sync_ext,
- spi_max_bus_speed,
- spi_default_bus_speed,
+const struct wilc_hif_func wilc_hif_spi = {
+ .hif_init = wilc_spi_init,
+ .hif_deinit = _wilc_spi_deinit,
+ .hif_read_reg = wilc_spi_read_reg,
+ .hif_write_reg = wilc_spi_write_reg,
+ .hif_block_rx = wilc_spi_read,
+ .hif_block_tx = wilc_spi_write,
+ .hif_read_int = wilc_spi_read_int,
+ .hif_clear_int_ext = wilc_spi_clear_int_ext,
+ .hif_read_size = wilc_spi_read_size,
+ .hif_block_tx_ext = wilc_spi_write,
+ .hif_block_rx_ext = wilc_spi_read,
+ .hif_sync_ext = wilc_spi_sync_ext,
};
diff --git a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c
index 3e9501727812..53fb2d4bb0bd 100644
--- a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c
+++ b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c
@@ -1,40 +1,101 @@
-/*!
- * @file wilc_wfi_cfgopertaions.c
- * @brief CFG80211 Function Implementation functionality
- * @author aabouzaeid
- * mabubakr
- * mdaftedar
- * zsalah
- * @sa wilc_wfi_cfgopertaions.h top level OS wrapper file
- * @date 31 Aug 2010
- * @version 1.0
- */
-
#include "wilc_wfi_cfgoperations.h"
-#ifdef WILC_SDIO
-#include "linux_wlan_sdio.h"
-#endif
+#include "host_interface.h"
#include <linux/errno.h>
+#define NO_ENCRYPT 0
+#define ENCRYPT_ENABLED BIT(0)
+#define WEP BIT(1)
+#define WEP_EXTENDED BIT(2)
+#define WPA BIT(3)
+#define WPA2 BIT(4)
+#define AES BIT(5)
+#define TKIP BIT(6)
+
+#define FRAME_TYPE_ID 0
+#define ACTION_CAT_ID 24
+#define ACTION_SUBTYPE_ID 25
+#define P2P_PUB_ACTION_SUBTYPE 30
+
+#define ACTION_FRAME 0xd0
+#define GO_INTENT_ATTR_ID 0x04
+#define CHANLIST_ATTR_ID 0x0b
+#define OPERCHAN_ATTR_ID 0x11
+#define PUB_ACTION_ATTR_ID 0x04
+#define P2PELEM_ATTR_ID 0xdd
+
+#define GO_NEG_REQ 0x00
+#define GO_NEG_RSP 0x01
+#define GO_NEG_CONF 0x02
+#define P2P_INV_REQ 0x03
+#define P2P_INV_RSP 0x04
+#define PUBLIC_ACT_VENDORSPEC 0x09
+#define GAS_INTIAL_REQ 0x0a
+#define GAS_INTIAL_RSP 0x0b
+
+#define INVALID_CHANNEL 0
+
+#define nl80211_SCAN_RESULT_EXPIRE (3 * HZ)
+#define SCAN_RESULT_EXPIRE (40 * HZ)
+
+static const u32 cipher_suites[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+ WLAN_CIPHER_SUITE_AES_CMAC,
+};
+
+static const struct ieee80211_txrx_stypes
+ wilc_wfi_cfg80211_mgmt_types[NUM_NL80211_IFTYPES] = {
+ [NL80211_IFTYPE_STATION] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+ },
+ [NL80211_IFTYPE_AP] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_ACTION >> 4)
+ },
+ [NL80211_IFTYPE_P2P_CLIENT] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4)
+ }
+};
+
+#define WILC_WFI_DWELL_PASSIVE 100
+#define WILC_WFI_DWELL_ACTIVE 40
+
+#define TCP_ACK_FILTER_LINK_SPEED_THRESH 54
+#define DEFAULT_LINK_SPEED 72
+
+
#define IS_MANAGMEMENT 0x100
#define IS_MANAGMEMENT_CALLBACK 0x080
#define IS_MGMT_STATUS_SUCCES 0x040
#define GET_PKT_OFFSET(a) (((a) >> 22) & 0x1ff)
-extern int linux_wlan_get_firmware(perInterface_wlan_t *p_nic);
+extern int wilc_mac_open(struct net_device *ndev);
+extern int wilc_mac_close(struct net_device *ndev);
-extern int mac_open(struct net_device *ndev);
-extern int mac_close(struct net_device *ndev);
-
-tstrNetworkInfo astrLastScannedNtwrksShadow[MAX_NUM_SCANNED_NETWORKS_SHADOW];
-u32 u32LastScannedNtwrksCountShadow;
-struct timer_list hDuringIpTimer;
-struct timer_list hAgingTimer;
+static tstrNetworkInfo last_scanned_shadow[MAX_NUM_SCANNED_NETWORKS_SHADOW];
+static u32 last_scanned_cnt;
+struct timer_list wilc_during_ip_timer;
+static struct timer_list hAgingTimer;
static u8 op_ifcs;
-extern u8 u8ConnectedSSID[6];
-u8 g_wilc_initialized = 1;
-extern bool g_obtainingIP;
+u8 wilc_initialized = 1;
#define CHAN2G(_channel, _freq, _flags) { \
.band = IEEE80211_BAND_2GHZ, \
@@ -45,8 +106,7 @@ extern bool g_obtainingIP;
.max_power = 30, \
}
-/*Frequency range for channels*/
-static struct ieee80211_channel WILC_WFI_2ghz_channels[] = {
+static struct ieee80211_channel ieee80211_2ghz_channels[] = {
CHAN2G(1, 2412, 0),
CHAN2G(2, 2417, 0),
CHAN2G(3, 2422, 0),
@@ -69,9 +129,7 @@ static struct ieee80211_channel WILC_WFI_2ghz_channels[] = {
.flags = (_flags), \
}
-
-/* Table 6 in section 3.2.1.1 */
-static struct ieee80211_rate WILC_WFI_rates[] = {
+static struct ieee80211_rate ieee80211_bitrates[] = {
RATETAB_ENT(10, 0, 0),
RATETAB_ENT(20, 1, 0),
RATETAB_ENT(55, 2, 0),
@@ -91,22 +149,19 @@ struct p2p_mgmt_data {
u8 *buff;
};
-/*Global variable used to state the current connected STA channel*/
-u8 u8WLANChannel = INVALID_CHANNEL;
-
-u8 curr_channel;
-
-u8 u8P2P_oui[] = {0x50, 0x6f, 0x9A, 0x09};
-u8 u8P2Plocalrandom = 0x01;
-u8 u8P2Precvrandom = 0x00;
-u8 u8P2P_vendorspec[] = {0xdd, 0x05, 0x00, 0x08, 0x40, 0x03};
-bool bWilc_ie;
+static u8 wlan_channel = INVALID_CHANNEL;
+static u8 curr_channel;
+static u8 p2p_oui[] = {0x50, 0x6f, 0x9A, 0x09};
+static u8 p2p_local_random = 0x01;
+static u8 p2p_recv_random = 0x00;
+static u8 p2p_vendor_spec[] = {0xdd, 0x05, 0x00, 0x08, 0x40, 0x03};
+static bool wilc_ie;
static struct ieee80211_supported_band WILC_WFI_band_2ghz = {
- .channels = WILC_WFI_2ghz_channels,
- .n_channels = ARRAY_SIZE(WILC_WFI_2ghz_channels),
- .bitrates = WILC_WFI_rates,
- .n_bitrates = ARRAY_SIZE(WILC_WFI_rates),
+ .channels = ieee80211_2ghz_channels,
+ .n_channels = ARRAY_SIZE(ieee80211_2ghz_channels),
+ .bitrates = ieee80211_bitrates,
+ .n_bitrates = ARRAY_SIZE(ieee80211_bitrates),
};
@@ -115,19 +170,19 @@ struct add_key_params {
bool pairwise;
u8 *mac_addr;
};
-struct add_key_params g_add_gtk_key_params;
-struct wilc_wfi_key g_key_gtk_params;
-struct add_key_params g_add_ptk_key_params;
-struct wilc_wfi_key g_key_ptk_params;
-struct wilc_wfi_wep_key g_key_wep_params;
-bool g_ptk_keys_saved;
-bool g_gtk_keys_saved;
-bool g_wep_keys_saved;
+static struct add_key_params g_add_gtk_key_params;
+static struct wilc_wfi_key g_key_gtk_params;
+static struct add_key_params g_add_ptk_key_params;
+static struct wilc_wfi_key g_key_ptk_params;
+static struct wilc_wfi_wep_key g_key_wep_params;
+static bool g_ptk_keys_saved;
+static bool g_gtk_keys_saved;
+static bool g_wep_keys_saved;
#define AGING_TIME (9 * 1000)
-#define duringIP_TIME 15000
+#define during_ip_time 15000
-void clear_shadow_scan(void *pUserVoid)
+static void clear_shadow_scan(void)
{
int i;
@@ -135,34 +190,33 @@ void clear_shadow_scan(void *pUserVoid)
del_timer_sync(&hAgingTimer);
PRINT_INFO(CORECONFIG_DBG, "destroy aging timer\n");
- for (i = 0; i < u32LastScannedNtwrksCountShadow; i++) {
- if (astrLastScannedNtwrksShadow[u32LastScannedNtwrksCountShadow].pu8IEs != NULL) {
- kfree(astrLastScannedNtwrksShadow[i].pu8IEs);
- astrLastScannedNtwrksShadow[u32LastScannedNtwrksCountShadow].pu8IEs = NULL;
+ for (i = 0; i < last_scanned_cnt; i++) {
+ if (last_scanned_shadow[last_scanned_cnt].pu8IEs) {
+ kfree(last_scanned_shadow[i].pu8IEs);
+ last_scanned_shadow[last_scanned_cnt].pu8IEs = NULL;
}
- host_int_freeJoinParams(astrLastScannedNtwrksShadow[i].pJoinParams);
- astrLastScannedNtwrksShadow[i].pJoinParams = NULL;
+ wilc_free_join_params(last_scanned_shadow[i].pJoinParams);
+ last_scanned_shadow[i].pJoinParams = NULL;
}
- u32LastScannedNtwrksCountShadow = 0;
+ last_scanned_cnt = 0;
}
-
}
-u32 get_rssi_avg(tstrNetworkInfo *pstrNetworkInfo)
+static u32 get_rssi_avg(tstrNetworkInfo *network_info)
{
u8 i;
int rssi_v = 0;
- u8 num_rssi = (pstrNetworkInfo->strRssi.u8Full) ? NUM_RSSI : (pstrNetworkInfo->strRssi.u8Index);
+ u8 num_rssi = (network_info->strRssi.u8Full) ? NUM_RSSI : (network_info->strRssi.u8Index);
for (i = 0; i < num_rssi; i++)
- rssi_v += pstrNetworkInfo->strRssi.as8RSSI[i];
+ rssi_v += network_info->strRssi.as8RSSI[i];
rssi_v /= num_rssi;
return rssi_v;
}
-void refresh_scan(void *pUserVoid, u8 all, bool bDirectScan)
+static void refresh_scan(void *user_void, u8 all, bool direct_scan)
{
struct wilc_priv *priv;
struct wiphy *wiphy;
@@ -170,55 +224,49 @@ void refresh_scan(void *pUserVoid, u8 all, bool bDirectScan)
int i;
int rssi = 0;
- priv = (struct wilc_priv *)pUserVoid;
+ priv = (struct wilc_priv *)user_void;
wiphy = priv->dev->ieee80211_ptr->wiphy;
- for (i = 0; i < u32LastScannedNtwrksCountShadow; i++) {
- tstrNetworkInfo *pstrNetworkInfo;
+ for (i = 0; i < last_scanned_cnt; i++) {
+ tstrNetworkInfo *network_info;
- pstrNetworkInfo = &(astrLastScannedNtwrksShadow[i]);
+ network_info = &last_scanned_shadow[i];
-
- if ((!pstrNetworkInfo->u8Found) || all) {
- s32 s32Freq;
+ if (!network_info->u8Found || all) {
+ s32 freq;
struct ieee80211_channel *channel;
- if (pstrNetworkInfo != NULL) {
-
- s32Freq = ieee80211_channel_to_frequency((s32)pstrNetworkInfo->u8channel, IEEE80211_BAND_2GHZ);
- channel = ieee80211_get_channel(wiphy, s32Freq);
+ if (network_info) {
+ freq = ieee80211_channel_to_frequency((s32)network_info->u8channel, IEEE80211_BAND_2GHZ);
+ channel = ieee80211_get_channel(wiphy, freq);
- rssi = get_rssi_avg(pstrNetworkInfo);
- if (memcmp("DIRECT-", pstrNetworkInfo->au8ssid, 7) || bDirectScan) {
- bss = cfg80211_inform_bss(wiphy, channel, CFG80211_BSS_FTYPE_UNKNOWN, pstrNetworkInfo->au8bssid, pstrNetworkInfo->u64Tsf, pstrNetworkInfo->u16CapInfo,
- pstrNetworkInfo->u16BeaconPeriod, (const u8 *)pstrNetworkInfo->pu8IEs,
- (size_t)pstrNetworkInfo->u16IEsLen, (((s32)rssi) * 100), GFP_KERNEL);
+ rssi = get_rssi_avg(network_info);
+ if (memcmp("DIRECT-", network_info->au8ssid, 7) ||
+ direct_scan) {
+ bss = cfg80211_inform_bss(wiphy, channel, CFG80211_BSS_FTYPE_UNKNOWN, network_info->au8bssid, network_info->u64Tsf, network_info->u16CapInfo,
+ network_info->u16BeaconPeriod, (const u8 *)network_info->pu8IEs,
+ (size_t)network_info->u16IEsLen, (((s32)rssi) * 100), GFP_KERNEL);
cfg80211_put_bss(wiphy, bss);
}
}
-
}
}
-
}
-void reset_shadow_found(void *pUserVoid)
+static void reset_shadow_found(void)
{
int i;
- for (i = 0; i < u32LastScannedNtwrksCountShadow; i++) {
- astrLastScannedNtwrksShadow[i].u8Found = 0;
-
- }
+ for (i = 0; i < last_scanned_cnt; i++)
+ last_scanned_shadow[i].u8Found = 0;
}
-void update_scan_time(void *pUserVoid)
+static void update_scan_time(void)
{
int i;
- for (i = 0; i < u32LastScannedNtwrksCountShadow; i++) {
- astrLastScannedNtwrksShadow[i].u32TimeRcvdInScan = jiffies;
- }
+ for (i = 0; i < last_scanned_cnt; i++)
+ last_scanned_shadow[i].u32TimeRcvdInScan = jiffies;
}
static void remove_network_from_shadow(unsigned long arg)
@@ -227,24 +275,25 @@ static void remove_network_from_shadow(unsigned long arg)
int i, j;
- for (i = 0; i < u32LastScannedNtwrksCountShadow; i++) {
- if (time_after(now, astrLastScannedNtwrksShadow[i].u32TimeRcvdInScan + (unsigned long)(SCAN_RESULT_EXPIRE))) {
- PRINT_D(CFG80211_DBG, "Network expired in ScanShadow: %s\n", astrLastScannedNtwrksShadow[i].au8ssid);
+ for (i = 0; i < last_scanned_cnt; i++) {
+ if (time_after(now, last_scanned_shadow[i].u32TimeRcvdInScan + (unsigned long)(SCAN_RESULT_EXPIRE))) {
+ PRINT_D(CFG80211_DBG, "Network expired in ScanShadow: %s\n", last_scanned_shadow[i].au8ssid);
- kfree(astrLastScannedNtwrksShadow[i].pu8IEs);
- astrLastScannedNtwrksShadow[i].pu8IEs = NULL;
+ kfree(last_scanned_shadow[i].pu8IEs);
+ last_scanned_shadow[i].pu8IEs = NULL;
- host_int_freeJoinParams(astrLastScannedNtwrksShadow[i].pJoinParams);
+ wilc_free_join_params(last_scanned_shadow[i].pJoinParams);
- for (j = i; (j < u32LastScannedNtwrksCountShadow - 1); j++) {
- astrLastScannedNtwrksShadow[j] = astrLastScannedNtwrksShadow[j + 1];
- }
- u32LastScannedNtwrksCountShadow--;
+ for (j = i; (j < last_scanned_cnt - 1); j++)
+ last_scanned_shadow[j] = last_scanned_shadow[j + 1];
+
+ last_scanned_cnt--;
}
}
- PRINT_D(CFG80211_DBG, "Number of cached networks: %d\n", u32LastScannedNtwrksCountShadow);
- if (u32LastScannedNtwrksCountShadow != 0) {
+ PRINT_D(CFG80211_DBG, "Number of cached networks: %d\n",
+ last_scanned_cnt);
+ if (last_scanned_cnt != 0) {
hAgingTimer.data = arg;
mod_timer(&hAgingTimer, jiffies + msecs_to_jiffies(AGING_TIME));
} else {
@@ -255,24 +304,24 @@ static void remove_network_from_shadow(unsigned long arg)
static void clear_duringIP(unsigned long arg)
{
PRINT_D(GENERIC_DBG, "GO:IP Obtained , enable scan\n");
- g_obtainingIP = false;
+ wilc_optaining_ip = false;
}
-int is_network_in_shadow(tstrNetworkInfo *pstrNetworkInfo, void *pUserVoid)
+static int is_network_in_shadow(tstrNetworkInfo *pstrNetworkInfo,
+ void *user_void)
{
int state = -1;
int i;
- if (u32LastScannedNtwrksCountShadow == 0) {
+ if (last_scanned_cnt == 0) {
PRINT_D(CFG80211_DBG, "Starting Aging timer\n");
- hAgingTimer.data = (unsigned long)pUserVoid;
+ hAgingTimer.data = (unsigned long)user_void;
mod_timer(&hAgingTimer, jiffies + msecs_to_jiffies(AGING_TIME));
state = -1;
} else {
- /* Linear search for now */
- for (i = 0; i < u32LastScannedNtwrksCountShadow; i++) {
- if (memcmp(astrLastScannedNtwrksShadow[i].au8bssid,
- pstrNetworkInfo->au8bssid, 6) == 0) {
+ for (i = 0; i < last_scanned_cnt; i++) {
+ if (memcmp(last_scanned_shadow[i].au8bssid,
+ pstrNetworkInfo->au8bssid, 6) == 0) {
state = i;
break;
}
@@ -281,78 +330,60 @@ int is_network_in_shadow(tstrNetworkInfo *pstrNetworkInfo, void *pUserVoid)
return state;
}
-void add_network_to_shadow(tstrNetworkInfo *pstrNetworkInfo, void *pUserVoid, void *pJoinParams)
+static void add_network_to_shadow(tstrNetworkInfo *pstrNetworkInfo,
+ void *user_void, void *pJoinParams)
{
- int ap_found = is_network_in_shadow(pstrNetworkInfo, pUserVoid);
+ int ap_found = is_network_in_shadow(pstrNetworkInfo, user_void);
u32 ap_index = 0;
u8 rssi_index = 0;
- if (u32LastScannedNtwrksCountShadow >= MAX_NUM_SCANNED_NETWORKS_SHADOW) {
+ if (last_scanned_cnt >= MAX_NUM_SCANNED_NETWORKS_SHADOW) {
PRINT_D(CFG80211_DBG, "Shadow network reached its maximum limit\n");
return;
}
if (ap_found == -1) {
- ap_index = u32LastScannedNtwrksCountShadow;
- u32LastScannedNtwrksCountShadow++;
-
+ ap_index = last_scanned_cnt;
+ last_scanned_cnt++;
} else {
ap_index = ap_found;
}
- rssi_index = astrLastScannedNtwrksShadow[ap_index].strRssi.u8Index;
- astrLastScannedNtwrksShadow[ap_index].strRssi.as8RSSI[rssi_index++] = pstrNetworkInfo->s8rssi;
+ rssi_index = last_scanned_shadow[ap_index].strRssi.u8Index;
+ last_scanned_shadow[ap_index].strRssi.as8RSSI[rssi_index++] = pstrNetworkInfo->s8rssi;
if (rssi_index == NUM_RSSI) {
rssi_index = 0;
- astrLastScannedNtwrksShadow[ap_index].strRssi.u8Full = 1;
- }
- astrLastScannedNtwrksShadow[ap_index].strRssi.u8Index = rssi_index;
-
- astrLastScannedNtwrksShadow[ap_index].s8rssi = pstrNetworkInfo->s8rssi;
- astrLastScannedNtwrksShadow[ap_index].u16CapInfo = pstrNetworkInfo->u16CapInfo;
-
- astrLastScannedNtwrksShadow[ap_index].u8SsidLen = pstrNetworkInfo->u8SsidLen;
- memcpy(astrLastScannedNtwrksShadow[ap_index].au8ssid,
- pstrNetworkInfo->au8ssid, pstrNetworkInfo->u8SsidLen);
-
- memcpy(astrLastScannedNtwrksShadow[ap_index].au8bssid,
- pstrNetworkInfo->au8bssid, ETH_ALEN);
-
- astrLastScannedNtwrksShadow[ap_index].u16BeaconPeriod = pstrNetworkInfo->u16BeaconPeriod;
- astrLastScannedNtwrksShadow[ap_index].u8DtimPeriod = pstrNetworkInfo->u8DtimPeriod;
- astrLastScannedNtwrksShadow[ap_index].u8channel = pstrNetworkInfo->u8channel;
-
- astrLastScannedNtwrksShadow[ap_index].u16IEsLen = pstrNetworkInfo->u16IEsLen;
- astrLastScannedNtwrksShadow[ap_index].u64Tsf = pstrNetworkInfo->u64Tsf;
+ last_scanned_shadow[ap_index].strRssi.u8Full = 1;
+ }
+ last_scanned_shadow[ap_index].strRssi.u8Index = rssi_index;
+ last_scanned_shadow[ap_index].s8rssi = pstrNetworkInfo->s8rssi;
+ last_scanned_shadow[ap_index].u16CapInfo = pstrNetworkInfo->u16CapInfo;
+ last_scanned_shadow[ap_index].u8SsidLen = pstrNetworkInfo->u8SsidLen;
+ memcpy(last_scanned_shadow[ap_index].au8ssid,
+ pstrNetworkInfo->au8ssid, pstrNetworkInfo->u8SsidLen);
+ memcpy(last_scanned_shadow[ap_index].au8bssid,
+ pstrNetworkInfo->au8bssid, ETH_ALEN);
+ last_scanned_shadow[ap_index].u16BeaconPeriod = pstrNetworkInfo->u16BeaconPeriod;
+ last_scanned_shadow[ap_index].u8DtimPeriod = pstrNetworkInfo->u8DtimPeriod;
+ last_scanned_shadow[ap_index].u8channel = pstrNetworkInfo->u8channel;
+ last_scanned_shadow[ap_index].u16IEsLen = pstrNetworkInfo->u16IEsLen;
+ last_scanned_shadow[ap_index].u64Tsf = pstrNetworkInfo->u64Tsf;
if (ap_found != -1)
- kfree(astrLastScannedNtwrksShadow[ap_index].pu8IEs);
- astrLastScannedNtwrksShadow[ap_index].pu8IEs =
- kmalloc(pstrNetworkInfo->u16IEsLen, GFP_KERNEL); /* will be deallocated by the WILC_WFI_CfgScan() function */
- memcpy(astrLastScannedNtwrksShadow[ap_index].pu8IEs,
- pstrNetworkInfo->pu8IEs, pstrNetworkInfo->u16IEsLen);
-
- astrLastScannedNtwrksShadow[ap_index].u32TimeRcvdInScan = jiffies;
- astrLastScannedNtwrksShadow[ap_index].u32TimeRcvdInScanCached = jiffies;
- astrLastScannedNtwrksShadow[ap_index].u8Found = 1;
+ kfree(last_scanned_shadow[ap_index].pu8IEs);
+ last_scanned_shadow[ap_index].pu8IEs =
+ kmalloc(pstrNetworkInfo->u16IEsLen, GFP_KERNEL);
+ memcpy(last_scanned_shadow[ap_index].pu8IEs,
+ pstrNetworkInfo->pu8IEs, pstrNetworkInfo->u16IEsLen);
+ last_scanned_shadow[ap_index].u32TimeRcvdInScan = jiffies;
+ last_scanned_shadow[ap_index].u32TimeRcvdInScanCached = jiffies;
+ last_scanned_shadow[ap_index].u8Found = 1;
if (ap_found != -1)
- host_int_freeJoinParams(astrLastScannedNtwrksShadow[ap_index].pJoinParams);
- astrLastScannedNtwrksShadow[ap_index].pJoinParams = pJoinParams;
-
+ wilc_free_join_params(last_scanned_shadow[ap_index].pJoinParams);
+ last_scanned_shadow[ap_index].pJoinParams = pJoinParams;
}
-
-/**
- * @brief CfgScanResult
- * @details Callback function which returns the scan results found
- *
- * @param[in] tenuScanEvent enuScanEvent: enum, indicating the scan event triggered, whether that is
- * SCAN_EVENT_NETWORK_FOUND or SCAN_EVENT_DONE
- * tstrNetworkInfo* pstrNetworkInfo: structure holding the scan results information
- * void* pUserVoid: Private structure associated with the wireless interface
- * @return NONE
- * @author mabubakr
- * @date
- * @version 1.0
- */
-static void CfgScanResult(enum scan_event enuScanEvent, tstrNetworkInfo *pstrNetworkInfo, void *pUserVoid, void *pJoinParams)
+static void CfgScanResult(enum scan_event scan_event,
+ tstrNetworkInfo *network_info,
+ void *user_void,
+ void *join_params)
{
struct wilc_priv *priv;
struct wiphy *wiphy;
@@ -360,56 +391,45 @@ static void CfgScanResult(enum scan_event enuScanEvent, tstrNetworkInfo *pstrNet
struct ieee80211_channel *channel;
struct cfg80211_bss *bss = NULL;
- priv = (struct wilc_priv *)pUserVoid;
+ priv = (struct wilc_priv *)user_void;
if (priv->bCfgScanning) {
- if (enuScanEvent == SCAN_EVENT_NETWORK_FOUND) {
+ if (scan_event == SCAN_EVENT_NETWORK_FOUND) {
wiphy = priv->dev->ieee80211_ptr->wiphy;
if (!wiphy)
return;
- if (wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC
- &&
- ((((s32)pstrNetworkInfo->s8rssi) * 100) < 0
- ||
- (((s32)pstrNetworkInfo->s8rssi) * 100) > 100)
- ) {
+ if (wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
+ (((s32)network_info->s8rssi * 100) < 0 ||
+ ((s32)network_info->s8rssi * 100) > 100)) {
PRINT_ER("wiphy signal type fial\n");
return;
}
- if (pstrNetworkInfo != NULL) {
- s32Freq = ieee80211_channel_to_frequency((s32)pstrNetworkInfo->u8channel, IEEE80211_BAND_2GHZ);
+ if (network_info) {
+ s32Freq = ieee80211_channel_to_frequency((s32)network_info->u8channel, IEEE80211_BAND_2GHZ);
channel = ieee80211_get_channel(wiphy, s32Freq);
if (!channel)
return;
PRINT_INFO(CFG80211_DBG, "Network Info:: CHANNEL Frequency: %d, RSSI: %d, CapabilityInfo: %d,"
- "BeaconPeriod: %d\n", channel->center_freq, (((s32)pstrNetworkInfo->s8rssi) * 100),
- pstrNetworkInfo->u16CapInfo, pstrNetworkInfo->u16BeaconPeriod);
-
- if (pstrNetworkInfo->bNewNetwork) {
- if (priv->u32RcvdChCount < MAX_NUM_SCANNED_NETWORKS) { /* TODO: mostafa: to be replaced by */
- /* max_scan_ssids */
- PRINT_D(CFG80211_DBG, "Network %s found\n", pstrNetworkInfo->au8ssid);
-
+ "BeaconPeriod: %d\n", channel->center_freq, (((s32)network_info->s8rssi) * 100),
+ network_info->u16CapInfo, network_info->u16BeaconPeriod);
+ if (network_info->bNewNetwork) {
+ if (priv->u32RcvdChCount < MAX_NUM_SCANNED_NETWORKS) {
+ PRINT_D(CFG80211_DBG, "Network %s found\n", network_info->au8ssid);
priv->u32RcvdChCount++;
-
-
- if (pJoinParams == NULL) {
+ if (!join_params)
PRINT_INFO(CORECONFIG_DBG, ">> Something really bad happened\n");
- }
- add_network_to_shadow(pstrNetworkInfo, priv, pJoinParams);
-
- /*P2P peers are sent to WPA supplicant and added to shadow table*/
+ add_network_to_shadow(network_info, priv, join_params);
- if (!(memcmp("DIRECT-", pstrNetworkInfo->au8ssid, 7))) {
- bss = cfg80211_inform_bss(wiphy, channel, CFG80211_BSS_FTYPE_UNKNOWN, pstrNetworkInfo->au8bssid, pstrNetworkInfo->u64Tsf, pstrNetworkInfo->u16CapInfo,
- pstrNetworkInfo->u16BeaconPeriod, (const u8 *)pstrNetworkInfo->pu8IEs,
- (size_t)pstrNetworkInfo->u16IEsLen, (((s32)pstrNetworkInfo->s8rssi) * 100), GFP_KERNEL);
+ if (!(memcmp("DIRECT-", network_info->au8ssid, 7))) {
+ bss = cfg80211_inform_bss(wiphy, channel, CFG80211_BSS_FTYPE_UNKNOWN, network_info->au8bssid, network_info->u64Tsf, network_info->u16CapInfo,
+ network_info->u16BeaconPeriod, (const u8 *)network_info->pu8IEs,
+ (size_t)network_info->u16IEsLen, (((s32)network_info->s8rssi) * 100), GFP_KERNEL);
cfg80211_put_bss(wiphy, bss);
}
@@ -419,19 +439,19 @@ static void CfgScanResult(enum scan_event enuScanEvent, tstrNetworkInfo *pstrNet
}
} else {
u32 i;
- /* So this network is discovered before, we'll just update its RSSI */
+
for (i = 0; i < priv->u32RcvdChCount; i++) {
- if (memcmp(astrLastScannedNtwrksShadow[i].au8bssid, pstrNetworkInfo->au8bssid, 6) == 0) {
- PRINT_D(CFG80211_DBG, "Update RSSI of %s\n", astrLastScannedNtwrksShadow[i].au8ssid);
+ if (memcmp(last_scanned_shadow[i].au8bssid, network_info->au8bssid, 6) == 0) {
+ PRINT_D(CFG80211_DBG, "Update RSSI of %s\n", last_scanned_shadow[i].au8ssid);
- astrLastScannedNtwrksShadow[i].s8rssi = pstrNetworkInfo->s8rssi;
- astrLastScannedNtwrksShadow[i].u32TimeRcvdInScan = jiffies;
+ last_scanned_shadow[i].s8rssi = network_info->s8rssi;
+ last_scanned_shadow[i].u32TimeRcvdInScan = jiffies;
break;
}
}
}
}
- } else if (enuScanEvent == SCAN_EVENT_DONE) {
+ } else if (scan_event == SCAN_EVENT_DONE) {
PRINT_D(CFG80211_DBG, "Scan Done[%p]\n", priv->dev);
PRINT_D(CFG80211_DBG, "Refreshing Scan ...\n");
refresh_scan(priv, 1, false);
@@ -443,23 +463,19 @@ static void CfgScanResult(enum scan_event enuScanEvent, tstrNetworkInfo *pstrNet
down(&(priv->hSemScanReq));
- if (priv->pstrScanReq != NULL) {
+ if (priv->pstrScanReq) {
cfg80211_scan_done(priv->pstrScanReq, false);
priv->u32RcvdChCount = 0;
priv->bCfgScanning = false;
priv->pstrScanReq = NULL;
}
up(&(priv->hSemScanReq));
-
- }
- /*Aborting any scan operation during mac close*/
- else if (enuScanEvent == SCAN_EVENT_ABORTED) {
+ } else if (scan_event == SCAN_EVENT_ABORTED) {
down(&(priv->hSemScanReq));
PRINT_D(CFG80211_DBG, "Scan Aborted\n");
- if (priv->pstrScanReq != NULL) {
-
- update_scan_time(priv);
+ if (priv->pstrScanReq) {
+ update_scan_time();
refresh_scan(priv, 1, false);
cfg80211_scan_done(priv->pstrScanReq, false);
@@ -471,60 +487,7 @@ static void CfgScanResult(enum scan_event enuScanEvent, tstrNetworkInfo *pstrNet
}
}
-
-/**
- * @brief WILC_WFI_Set_PMKSA
- * @details Check if pmksa is cached and set it.
- * @param[in]
- * @return int : Return 0 on Success
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
-int WILC_WFI_Set_PMKSA(u8 *bssid, struct wilc_priv *priv)
-{
- u32 i;
- s32 s32Error = 0;
-
-
- for (i = 0; i < priv->pmkid_list.numpmkid; i++) {
-
- if (!memcmp(bssid, priv->pmkid_list.pmkidlist[i].bssid,
- ETH_ALEN)) {
- PRINT_D(CFG80211_DBG, "PMKID successful comparison");
-
- /*If bssid is found, set the values*/
- s32Error = host_int_set_pmkid_info(priv->hWILCWFIDrv, &priv->pmkid_list);
-
- if (s32Error != 0)
- PRINT_ER("Error in pmkid\n");
-
- break;
- }
- }
-
- return s32Error;
-
-
-}
-int linux_wlan_set_bssid(struct net_device *wilc_netdev, u8 *pBSSID);
-
-
-/**
- * @brief CfgConnectResult
- * @details
- * @param[in] tenuConnDisconnEvent enuConnDisconnEvent: Type of connection response either
- * connection response or disconnection notification.
- * tstrConnectInfo* pstrConnectInfo: COnnection information.
- * u8 u8MacStatus: Mac Status from firmware
- * tstrDisconnectNotifInfo* pstrDisconnectNotifInfo: Disconnection Notification
- * void* pUserVoid: Private data associated with wireless interface
- * @return NONE
- * @author mabubakr
- * @date 01 MAR 2012
- * @version 1.0
- */
-int connecting;
+int wilc_connecting;
static void CfgConnectResult(enum conn_event enuConnDisconnEvent,
tstrConnectInfo *pstrConnectInfo,
@@ -537,18 +500,17 @@ static void CfgConnectResult(enum conn_event enuConnDisconnEvent,
struct host_if_drv *pstrWFIDrv;
u8 NullBssid[ETH_ALEN] = {0};
struct wilc *wl;
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
- connecting = 0;
+ wilc_connecting = 0;
priv = (struct wilc_priv *)pUserVoid;
dev = priv->dev;
- nic = netdev_priv(dev);
- wl = nic->wilc;
+ vif = netdev_priv(dev);
+ wl = vif->wilc;
pstrWFIDrv = (struct host_if_drv *)priv->hWILCWFIDrv;
if (enuConnDisconnEvent == CONN_DISCONN_EVENT_CONN_RESP) {
- /*Initialization*/
u16 u16ConnectStatus;
u16ConnectStatus = pstrConnectInfo->u16ConnectStatus;
@@ -557,15 +519,12 @@ static void CfgConnectResult(enum conn_event enuConnDisconnEvent,
if ((u8MacStatus == MAC_DISCONNECTED) &&
(pstrConnectInfo->u16ConnectStatus == SUCCESSFUL_STATUSCODE)) {
- /* The case here is that our station was waiting for association response frame and has just received it containing status code
- * = SUCCESSFUL_STATUSCODE, while mac status is MAC_DISCONNECTED (which means something wrong happened) */
u16ConnectStatus = WLAN_STATUS_UNSPECIFIED_FAILURE;
- linux_wlan_set_bssid(priv->dev, NullBssid);
- eth_zero_addr(u8ConnectedSSID);
+ wilc_wlan_set_bssid(priv->dev, NullBssid);
+ eth_zero_addr(wilc_connected_ssid);
- /*Invalidate u8WLANChannel value on wlan0 disconnect*/
- if (!pstrWFIDrv->u8P2PConnect)
- u8WLANChannel = INVALID_CHANNEL;
+ if (!pstrWFIDrv->p2p_connect)
+ wlan_channel = INVALID_CHANNEL;
PRINT_ER("Unspecified failure: Connection status %d : MAC status = %d\n", u16ConnectStatus, u8MacStatus);
}
@@ -579,13 +538,13 @@ static void CfgConnectResult(enum conn_event enuConnDisconnEvent,
memcpy(priv->au8AssociatedBss, pstrConnectInfo->au8bssid, ETH_ALEN);
- for (i = 0; i < u32LastScannedNtwrksCountShadow; i++) {
- if (memcmp(astrLastScannedNtwrksShadow[i].au8bssid,
- pstrConnectInfo->au8bssid, ETH_ALEN) == 0) {
+ for (i = 0; i < last_scanned_cnt; i++) {
+ if (memcmp(last_scanned_shadow[i].au8bssid,
+ pstrConnectInfo->au8bssid, ETH_ALEN) == 0) {
unsigned long now = jiffies;
if (time_after(now,
- astrLastScannedNtwrksShadow[i].u32TimeRcvdInScanCached + (unsigned long)(nl80211_SCAN_RESULT_EXPIRE - (1 * HZ)))) {
+ last_scanned_shadow[i].u32TimeRcvdInScanCached + (unsigned long)(nl80211_SCAN_RESULT_EXPIRE - (1 * HZ)))) {
bNeedScanRefresh = true;
}
@@ -593,12 +552,8 @@ static void CfgConnectResult(enum conn_event enuConnDisconnEvent,
}
}
- if (bNeedScanRefresh) {
- /*Also, refrsh DIRECT- results if */
+ if (bNeedScanRefresh)
refresh_scan(priv, 1, true);
-
- }
-
}
@@ -609,68 +564,47 @@ static void CfgConnectResult(enum conn_event enuConnDisconnEvent,
cfg80211_connect_result(dev, pstrConnectInfo->au8bssid,
pstrConnectInfo->pu8ReqIEs, pstrConnectInfo->ReqIEsLen,
pstrConnectInfo->pu8RespIEs, pstrConnectInfo->u16RespIEsLen,
- u16ConnectStatus, GFP_KERNEL); /* TODO: mostafa: u16ConnectStatus to */
- /* be replaced by pstrConnectInfo->u16ConnectStatus */
+ u16ConnectStatus, GFP_KERNEL);
} else if (enuConnDisconnEvent == CONN_DISCONN_EVENT_DISCONN_NOTIF) {
- g_obtainingIP = false;
+ wilc_optaining_ip = false;
PRINT_ER("Received MAC_DISCONNECTED from firmware with reason %d on dev [%p]\n",
pstrDisconnectNotifInfo->u16reason, priv->dev);
- u8P2Plocalrandom = 0x01;
- u8P2Precvrandom = 0x00;
- bWilc_ie = false;
+ p2p_local_random = 0x01;
+ p2p_recv_random = 0x00;
+ wilc_ie = false;
eth_zero_addr(priv->au8AssociatedBss);
- linux_wlan_set_bssid(priv->dev, NullBssid);
- eth_zero_addr(u8ConnectedSSID);
-
- /*Invalidate u8WLANChannel value on wlan0 disconnect*/
- if (!pstrWFIDrv->u8P2PConnect)
- u8WLANChannel = INVALID_CHANNEL;
- /*Incase "P2P CLIENT Connected" send deauthentication reason by 3 to force the WPA_SUPPLICANT to directly change
- * virtual interface to station*/
- if ((pstrWFIDrv->IFC_UP) && (dev == wl->vif[1].ndev)) {
+ wilc_wlan_set_bssid(priv->dev, NullBssid);
+ eth_zero_addr(wilc_connected_ssid);
+
+ if (!pstrWFIDrv->p2p_connect)
+ wlan_channel = INVALID_CHANNEL;
+ if ((pstrWFIDrv->IFC_UP) && (dev == wl->vif[1]->ndev)) {
pstrDisconnectNotifInfo->u16reason = 3;
- }
- /*Incase "P2P CLIENT during connection(not connected)" send deauthentication reason by 1 to force the WPA_SUPPLICANT
- * to scan again and retry the connection*/
- else if ((!pstrWFIDrv->IFC_UP) && (dev == wl->vif[1].ndev)) {
+ } else if ((!pstrWFIDrv->IFC_UP) && (dev == wl->vif[1]->ndev)) {
pstrDisconnectNotifInfo->u16reason = 1;
}
cfg80211_disconnected(dev, pstrDisconnectNotifInfo->u16reason, pstrDisconnectNotifInfo->ie,
pstrDisconnectNotifInfo->ie_len, false,
GFP_KERNEL);
-
}
-
}
-
-/**
- * @brief set_channel
- * @details Set channel for a given wireless interface. Some devices
- * may support multi-channel operation (by channel hopping) so cfg80211
- * doesn't verify much. Note, however, that the passed netdev may be
- * %NULL as well if the user requested changing the channel for the
- * device itself, or for a monitor interface.
- * @param[in]
- * @return int : Return 0 on Success
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
static int set_channel(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef)
{
u32 channelnum = 0;
struct wilc_priv *priv;
int result = 0;
+ struct wilc_vif *vif;
priv = wiphy_priv(wiphy);
+ vif = netdev_priv(priv->dev);
channelnum = ieee80211_frequency_to_channel(chandef->chan->center_freq);
PRINT_D(CFG80211_DBG, "Setting channel %d with frequency %d\n", channelnum, chandef->chan->center_freq);
curr_channel = channelnum;
- result = host_int_set_mac_chnl_num(priv->hWILCWFIDrv, channelnum);
+ result = wilc_set_mac_chnl_num(vif, channelnum);
if (result != 0)
PRINT_ER("Error in setting channel %d\n", channelnum);
@@ -678,19 +612,6 @@ static int set_channel(struct wiphy *wiphy,
return result;
}
-/**
- * @brief scan
- * @details Request to do a scan. If returning zero, the scan request is given
- * the driver, and will be valid until passed to cfg80211_scan_done().
- * For scan results, call cfg80211_inform_bss(); you can call this outside
- * the scan/scan_done bracket too.
- * @param[in]
- * @return int : Return 0 on Success
- * @author mabubakr
- * @date 01 MAR 2012
- * @version 1.0
- */
-
static int scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
{
struct wilc_priv *priv;
@@ -698,21 +619,20 @@ static int scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
s32 s32Error = 0;
u8 au8ScanChanList[MAX_NUM_SCANNED_NETWORKS];
struct hidden_network strHiddenNetwork;
+ struct wilc_vif *vif;
priv = wiphy_priv(wiphy);
+ vif = netdev_priv(priv->dev);
priv->pstrScanReq = request;
priv->u32RcvdChCount = 0;
- host_int_set_wfi_drv_handler(priv->hWILCWFIDrv);
-
-
- reset_shadow_found(priv);
+ wilc_set_wfi_drv_handler(vif, wilc_get_vif_idx(vif));
+ reset_shadow_found();
priv->bCfgScanning = true;
- if (request->n_channels <= MAX_NUM_SCANNED_NETWORKS) { /* TODO: mostafa: to be replaced by */
- /* max_scan_ssids */
+ if (request->n_channels <= MAX_NUM_SCANNED_NETWORKS) {
for (i = 0; i < request->n_channels; i++) {
au8ScanChanList[i] = (u8)ieee80211_frequency_to_channel(request->channels[i]->center_freq);
PRINT_INFO(CFG80211_DBG, "ScanChannel List[%d] = %d,", i, au8ScanChanList[i]);
@@ -724,15 +644,13 @@ static int scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
PRINT_D(CFG80211_DBG, "Number of SSIDs %d\n", request->n_ssids);
if (request->n_ssids >= 1) {
-
-
strHiddenNetwork.pstrHiddenNetworkInfo = kmalloc(request->n_ssids * sizeof(struct hidden_network), GFP_KERNEL);
strHiddenNetwork.u8ssidnum = request->n_ssids;
for (i = 0; i < request->n_ssids; i++) {
-
- if (request->ssids[i].ssid != NULL && request->ssids[i].ssid_len != 0) {
+ if (request->ssids[i].ssid &&
+ request->ssids[i].ssid_len != 0) {
strHiddenNetwork.pstrHiddenNetworkInfo[i].pu8ssid = kmalloc(request->ssids[i].ssid_len, GFP_KERNEL);
memcpy(strHiddenNetwork.pstrHiddenNetworkInfo[i].pu8ssid, request->ssids[i].ssid, request->ssids[i].ssid_len);
strHiddenNetwork.pstrHiddenNetworkInfo[i].u8ssidlen = request->ssids[i].ssid_len;
@@ -742,18 +660,21 @@ static int scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
}
}
PRINT_D(CFG80211_DBG, "Trigger Scan Request\n");
- s32Error = host_int_scan(priv->hWILCWFIDrv, USER_SCAN, ACTIVE_SCAN,
- au8ScanChanList, request->n_channels,
- (const u8 *)request->ie, request->ie_len,
- CfgScanResult, (void *)priv, &strHiddenNetwork);
+ s32Error = wilc_scan(vif, USER_SCAN, ACTIVE_SCAN,
+ au8ScanChanList,
+ request->n_channels,
+ (const u8 *)request->ie,
+ request->ie_len, CfgScanResult,
+ (void *)priv, &strHiddenNetwork);
} else {
PRINT_D(CFG80211_DBG, "Trigger Scan Request\n");
- s32Error = host_int_scan(priv->hWILCWFIDrv, USER_SCAN, ACTIVE_SCAN,
- au8ScanChanList, request->n_channels,
- (const u8 *)request->ie, request->ie_len,
- CfgScanResult, (void *)priv, NULL);
+ s32Error = wilc_scan(vif, USER_SCAN, ACTIVE_SCAN,
+ au8ScanChanList,
+ request->n_channels,
+ (const u8 *)request->ie,
+ request->ie_len, CfgScanResult,
+ (void *)priv, NULL);
}
-
} else {
PRINT_ER("Requested num of scanned channels is greater than the max, supported"
" channels\n");
@@ -767,18 +688,6 @@ static int scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
return s32Error;
}
-/**
- * @brief connect
- * @details Connect to the ESS with the specified parameters. When connected,
- * call cfg80211_connect_result() with status code %WLAN_STATUS_SUCCESS.
- * If the connection fails for some reason, call cfg80211_connect_result()
- * with the status from the AP.
- * @param[in]
- * @return int : Return 0 on Success
- * @author mabubakr
- * @date 01 MAR 2012
- * @version 1.0
- */
static int connect(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_connect_params *sme)
{
@@ -793,39 +702,37 @@ static int connect(struct wiphy *wiphy, struct net_device *dev,
struct wilc_priv *priv;
struct host_if_drv *pstrWFIDrv;
tstrNetworkInfo *pstrNetworkInfo = NULL;
+ struct wilc_vif *vif;
-
- connecting = 1;
+ wilc_connecting = 1;
priv = wiphy_priv(wiphy);
+ vif = netdev_priv(priv->dev);
pstrWFIDrv = (struct host_if_drv *)(priv->hWILCWFIDrv);
- host_int_set_wfi_drv_handler(priv->hWILCWFIDrv);
+ wilc_set_wfi_drv_handler(vif, wilc_get_vif_idx(vif));
PRINT_D(CFG80211_DBG, "Connecting to SSID [%s] on netdev [%p] host if [%p]\n", sme->ssid, dev, priv->hWILCWFIDrv);
if (!(strncmp(sme->ssid, "DIRECT-", 7))) {
PRINT_D(CFG80211_DBG, "Connected to Direct network,OBSS disabled\n");
- pstrWFIDrv->u8P2PConnect = 1;
- } else
- pstrWFIDrv->u8P2PConnect = 0;
+ pstrWFIDrv->p2p_connect = 1;
+ } else {
+ pstrWFIDrv->p2p_connect = 0;
+ }
PRINT_INFO(CFG80211_DBG, "Required SSID = %s\n , AuthType = %d\n", sme->ssid, sme->auth_type);
- for (i = 0; i < u32LastScannedNtwrksCountShadow; i++) {
- if ((sme->ssid_len == astrLastScannedNtwrksShadow[i].u8SsidLen) &&
- memcmp(astrLastScannedNtwrksShadow[i].au8ssid,
- sme->ssid,
- sme->ssid_len) == 0) {
+ for (i = 0; i < last_scanned_cnt; i++) {
+ if ((sme->ssid_len == last_scanned_shadow[i].u8SsidLen) &&
+ memcmp(last_scanned_shadow[i].au8ssid,
+ sme->ssid,
+ sme->ssid_len) == 0) {
PRINT_INFO(CFG80211_DBG, "Network with required SSID is found %s\n", sme->ssid);
- if (sme->bssid == NULL) {
- /* BSSID is not passed from the user, so decision of matching
- * is done by SSID only */
+ if (!sme->bssid) {
PRINT_INFO(CFG80211_DBG, "BSSID is not passed from the user\n");
break;
} else {
- /* BSSID is also passed from the user, so decision of matching
- * should consider also this passed BSSID */
- if (memcmp(astrLastScannedNtwrksShadow[i].au8bssid,
- sme->bssid,
- ETH_ALEN) == 0) {
+ if (memcmp(last_scanned_shadow[i].au8bssid,
+ sme->bssid,
+ ETH_ALEN) == 0) {
PRINT_INFO(CFG80211_DBG, "BSSID is passed from the user and matched\n");
break;
}
@@ -833,10 +740,10 @@ static int connect(struct wiphy *wiphy, struct net_device *dev,
}
}
- if (i < u32LastScannedNtwrksCountShadow) {
+ if (i < last_scanned_cnt) {
PRINT_D(CFG80211_DBG, "Required bss is in scan results\n");
- pstrNetworkInfo = &(astrLastScannedNtwrksShadow[i]);
+ pstrNetworkInfo = &last_scanned_shadow[i];
PRINT_INFO(CFG80211_DBG, "network BSSID to be associated: %x%x%x%x%x%x\n",
pstrNetworkInfo->au8bssid[0], pstrNetworkInfo->au8bssid[1],
@@ -844,7 +751,7 @@ static int connect(struct wiphy *wiphy, struct net_device *dev,
pstrNetworkInfo->au8bssid[4], pstrNetworkInfo->au8bssid[5]);
} else {
s32Error = -ENOENT;
- if (u32LastScannedNtwrksCountShadow == 0)
+ if (last_scanned_cnt == 0)
PRINT_D(CFG80211_DBG, "No Scan results yet\n");
else
PRINT_D(CFG80211_DBG, "Required bss not in scan results: Error(%d)\n", s32Error);
@@ -867,8 +774,6 @@ static int connect(struct wiphy *wiphy, struct net_device *dev,
}
if (sme->crypto.cipher_group != NO_ENCRYPT) {
- /* To determine the u8security value, first we check the group cipher suite then {in case of WPA or WPA2}
- * we will add to it the pairwise cipher suite(s) */
pcwpa_version = "Default";
PRINT_D(CORECONFIG_DBG, ">> sme->crypto.wpa_versions: %x\n", sme->crypto.wpa_versions);
if (sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP40) {
@@ -891,8 +796,9 @@ static int connect(struct wiphy *wiphy, struct net_device *dev,
g_key_wep_params.key_idx = sme->key_idx;
g_wep_keys_saved = true;
- host_int_set_wep_default_key(priv->hWILCWFIDrv, sme->key_idx);
- host_int_add_wep_key_bss_sta(priv->hWILCWFIDrv, sme->key, sme->key_len, sme->key_idx);
+ wilc_set_wep_default_keyid(vif, sme->key_idx);
+ wilc_add_wep_key_bss_sta(vif, sme->key, sme->key_len,
+ sme->key_idx);
} else if (sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP104) {
u8security = ENCRYPT_ENABLED | WEP | WEP_EXTENDED;
pcgroup_encrypt_val = "WEP104";
@@ -908,15 +814,15 @@ static int connect(struct wiphy *wiphy, struct net_device *dev,
g_key_wep_params.key_idx = sme->key_idx;
g_wep_keys_saved = true;
- host_int_set_wep_default_key(priv->hWILCWFIDrv, sme->key_idx);
- host_int_add_wep_key_bss_sta(priv->hWILCWFIDrv, sme->key, sme->key_len, sme->key_idx);
+ wilc_set_wep_default_keyid(vif, sme->key_idx);
+ wilc_add_wep_key_bss_sta(vif, sme->key, sme->key_len,
+ sme->key_idx);
} else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) {
if (sme->crypto.cipher_group == WLAN_CIPHER_SUITE_TKIP) {
u8security = ENCRYPT_ENABLED | WPA2 | TKIP;
pcgroup_encrypt_val = "WPA2_TKIP";
pccipher_group = "TKIP";
- } else { /* TODO: mostafa: here we assume that any other encryption type is AES */
- /* tenuSecurity_t = WPA2_AES; */
+ } else {
u8security = ENCRYPT_ENABLED | WPA2 | AES;
pcgroup_encrypt_val = "WPA2_AES";
pccipher_group = "AES";
@@ -927,12 +833,10 @@ static int connect(struct wiphy *wiphy, struct net_device *dev,
u8security = ENCRYPT_ENABLED | WPA | TKIP;
pcgroup_encrypt_val = "WPA_TKIP";
pccipher_group = "TKIP";
- } else { /* TODO: mostafa: here we assume that any other encryption type is AES */
- /* tenuSecurity_t = WPA_AES; */
+ } else {
u8security = ENCRYPT_ENABLED | WPA | AES;
pcgroup_encrypt_val = "WPA_AES";
pccipher_group = "AES";
-
}
pcwpa_version = "WPA_VERSION_1";
@@ -942,17 +846,14 @@ static int connect(struct wiphy *wiphy, struct net_device *dev,
goto done;
}
-
}
- /* After we set the u8security value from checking the group cipher suite, {in case of WPA or WPA2} we will
- * add to it the pairwise cipher suite(s) */
if ((sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
|| (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)) {
for (i = 0; i < sme->crypto.n_ciphers_pairwise; i++) {
if (sme->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP) {
u8security = u8security | TKIP;
- } else { /* TODO: mostafa: here we assume that any other encryption type is AES */
+ } else {
u8security = u8security | AES;
}
}
@@ -976,8 +877,6 @@ static int connect(struct wiphy *wiphy, struct net_device *dev,
PRINT_D(CFG80211_DBG, "Automatic Authentation type = %d\n", sme->auth_type);
}
-
- /* ai: key_mgmt: enterprise case */
if (sme->crypto.n_akm_suites) {
switch (sme->crypto.akm_suites[0]) {
case WLAN_AKM_SUITE_8021X:
@@ -997,19 +896,19 @@ static int connect(struct wiphy *wiphy, struct net_device *dev,
curr_channel = pstrNetworkInfo->u8channel;
- if (!pstrWFIDrv->u8P2PConnect) {
- u8WLANChannel = pstrNetworkInfo->u8channel;
- }
+ if (!pstrWFIDrv->p2p_connect)
+ wlan_channel = pstrNetworkInfo->u8channel;
- linux_wlan_set_bssid(dev, pstrNetworkInfo->au8bssid);
+ wilc_wlan_set_bssid(dev, pstrNetworkInfo->au8bssid);
- s32Error = host_int_set_join_req(priv->hWILCWFIDrv, pstrNetworkInfo->au8bssid, sme->ssid,
- sme->ssid_len, sme->ie, sme->ie_len,
- CfgConnectResult, (void *)priv, u8security,
- tenuAuth_type, pstrNetworkInfo->u8channel,
- pstrNetworkInfo->pJoinParams);
+ s32Error = wilc_set_join_req(vif, pstrNetworkInfo->au8bssid, sme->ssid,
+ sme->ssid_len, sme->ie, sme->ie_len,
+ CfgConnectResult, (void *)priv,
+ u8security, tenuAuth_type,
+ pstrNetworkInfo->u8channel,
+ pstrNetworkInfo->pJoinParams);
if (s32Error != 0) {
- PRINT_ER("host_int_set_join_req(): Error(%d)\n", s32Error);
+ PRINT_ER("wilc_set_join_req(): Error(%d)\n", s32Error);
s32Error = -ENOENT;
goto done;
}
@@ -1019,40 +918,31 @@ done:
return s32Error;
}
-
-/**
- * @brief disconnect
- * @details Disconnect from the BSS/ESS.
- * @param[in]
- * @return int : Return 0 on Success
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
static int disconnect(struct wiphy *wiphy, struct net_device *dev, u16 reason_code)
{
s32 s32Error = 0;
struct wilc_priv *priv;
struct host_if_drv *pstrWFIDrv;
+ struct wilc_vif *vif;
u8 NullBssid[ETH_ALEN] = {0};
- connecting = 0;
+ wilc_connecting = 0;
priv = wiphy_priv(wiphy);
+ vif = netdev_priv(priv->dev);
- /*Invalidate u8WLANChannel value on wlan0 disconnect*/
pstrWFIDrv = (struct host_if_drv *)priv->hWILCWFIDrv;
- if (!pstrWFIDrv->u8P2PConnect)
- u8WLANChannel = INVALID_CHANNEL;
- linux_wlan_set_bssid(priv->dev, NullBssid);
+ if (!pstrWFIDrv->p2p_connect)
+ wlan_channel = INVALID_CHANNEL;
+ wilc_wlan_set_bssid(priv->dev, NullBssid);
PRINT_D(CFG80211_DBG, "Disconnecting with reason code(%d)\n", reason_code);
- u8P2Plocalrandom = 0x01;
- u8P2Precvrandom = 0x00;
- bWilc_ie = false;
- pstrWFIDrv->u64P2p_MgmtTimeout = 0;
+ p2p_local_random = 0x01;
+ p2p_recv_random = 0x00;
+ wilc_ie = false;
+ pstrWFIDrv->p2p_timeout = 0;
- s32Error = host_int_disconnect(priv->hWILCWFIDrv, reason_code);
+ s32Error = wilc_disconnect(vif, reason_code);
if (s32Error != 0) {
PRINT_ER("Error in disconnecting: Error(%d)\n", s32Error);
s32Error = -EINVAL;
@@ -1061,16 +951,6 @@ static int disconnect(struct wiphy *wiphy, struct net_device *dev, u16 reason_co
return s32Error;
}
-/**
- * @brief add_key
- * @details Add a key with the given parameters. @mac_addr will be %NULL
- * when adding a group key.
- * @param[in] key : key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key, 8-byte Rx Mic Key
- * @return int : Return 0 on Success
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
static int add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
bool pairwise,
const u8 *mac_addr, struct key_params *params)
@@ -1086,11 +966,11 @@ static int add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
u8 u8pmode = NO_ENCRYPT;
enum AUTHTYPE tenuAuth_type = ANY;
struct wilc *wl;
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
priv = wiphy_priv(wiphy);
- nic = netdev_priv(netdev);
- wl = nic->wilc;
+ vif = netdev_priv(netdev);
+ wl = vif->wilc;
PRINT_D(CFG80211_DBG, "Adding key with cipher suite = %x\n", params->cipher);
@@ -1105,7 +985,6 @@ static int add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
if (priv->wdev->iftype == NL80211_IFTYPE_AP) {
-
priv->WILC_WFI_wep_default = key_index;
priv->WILC_WFI_wep_key_len[key_index] = params->key_len;
memcpy(priv->WILC_WFI_wep_key[key_index], params->key, params->key_len);
@@ -1123,7 +1002,9 @@ static int add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
else
u8mode = ENCRYPT_ENABLED | WEP | WEP_EXTENDED;
- host_int_add_wep_key_bss_ap(priv->hWILCWFIDrv, params->key, params->key_len, key_index, u8mode, tenuAuth_type);
+ wilc_add_wep_key_bss_ap(vif, params->key,
+ params->key_len, key_index,
+ u8mode, tenuAuth_type);
break;
}
if (memcmp(params->key, priv->WILC_WFI_wep_key[key_index], params->key_len)) {
@@ -1137,7 +1018,8 @@ static int add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
for (i = 0; i < params->key_len; i++)
PRINT_INFO(CFG80211_DBG, "WEP key value[%d] = %d\n", i, params->key[i]);
}
- host_int_add_wep_key_bss_sta(priv->hWILCWFIDrv, params->key, params->key_len, key_index);
+ wilc_add_wep_key_bss_sta(vif, params->key,
+ params->key_len, key_index);
}
break;
@@ -1145,14 +1027,12 @@ static int add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
case WLAN_CIPHER_SUITE_TKIP:
case WLAN_CIPHER_SUITE_CCMP:
if (priv->wdev->iftype == NL80211_IFTYPE_AP || priv->wdev->iftype == NL80211_IFTYPE_P2P_GO) {
-
- if (priv->wilc_gtk[key_index] == NULL) {
+ if (!priv->wilc_gtk[key_index]) {
priv->wilc_gtk[key_index] = kmalloc(sizeof(struct wilc_wfi_key), GFP_KERNEL);
priv->wilc_gtk[key_index]->key = NULL;
priv->wilc_gtk[key_index]->seq = NULL;
-
}
- if (priv->wilc_ptk[key_index] == NULL) {
+ if (!priv->wilc_ptk[key_index]) {
priv->wilc_ptk[key_index] = kmalloc(sizeof(struct wilc_wfi_key), GFP_KERNEL);
priv->wilc_ptk[key_index]->key = NULL;
priv->wilc_ptk[key_index]->seq = NULL;
@@ -1169,18 +1049,14 @@ static int add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
priv->wilc_groupkey = u8gmode;
if (params->key_len > 16 && params->cipher == WLAN_CIPHER_SUITE_TKIP) {
-
pu8TxMic = params->key + 24;
pu8RxMic = params->key + 16;
KeyLen = params->key_len - 16;
}
- /* if there has been previous allocation for the same index through its key, free that memory and allocate again*/
kfree(priv->wilc_gtk[key_index]->key);
priv->wilc_gtk[key_index]->key = kmalloc(params->key_len, GFP_KERNEL);
memcpy(priv->wilc_gtk[key_index]->key, params->key, params->key_len);
-
- /* if there has been previous allocation for the same index through its seq, free that memory and allocate again*/
kfree(priv->wilc_gtk[key_index]->seq);
if ((params->seq_len) > 0) {
@@ -1200,8 +1076,10 @@ static int add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
}
- host_int_add_rx_gtk(priv->hWILCWFIDrv, params->key, KeyLen,
- key_index, params->seq_len, params->seq, pu8RxMic, pu8TxMic, AP_MODE, u8gmode);
+ wilc_add_rx_gtk(vif, params->key, KeyLen,
+ key_index, params->seq_len,
+ params->seq, pu8RxMic,
+ pu8TxMic, AP_MODE, u8gmode);
} else {
PRINT_INFO(CFG80211_DBG, "STA Address: %x%x%x%x%x\n", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4]);
@@ -1213,7 +1091,6 @@ static int add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
if (params->key_len > 16 && params->cipher == WLAN_CIPHER_SUITE_TKIP) {
-
pu8TxMic = params->key + 24;
pu8RxMic = params->key + 16;
KeyLen = params->key_len - 16;
@@ -1245,8 +1122,9 @@ static int add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
priv->wilc_ptk[key_index]->key_len = params->key_len;
priv->wilc_ptk[key_index]->seq_len = params->seq_len;
- host_int_add_ptk(priv->hWILCWFIDrv, params->key, KeyLen, mac_addr,
- pu8RxMic, pu8TxMic, AP_MODE, u8pmode, key_index);
+ wilc_add_ptk(vif, params->key, KeyLen,
+ mac_addr, pu8RxMic, pu8TxMic,
+ AP_MODE, u8pmode, key_index);
}
break;
}
@@ -1255,14 +1133,12 @@ static int add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
u8mode = 0;
if (!pairwise) {
if (params->key_len > 16 && params->cipher == WLAN_CIPHER_SUITE_TKIP) {
- /* swap the tx mic by rx mic */
pu8RxMic = params->key + 24;
pu8TxMic = params->key + 16;
KeyLen = params->key_len - 16;
}
- /*save keys only on interface 0 (wifi interface)*/
- if (!g_gtk_keys_saved && netdev == wl->vif[0].ndev) {
+ if (!g_gtk_keys_saved && netdev == wl->vif[0]->ndev) {
g_add_gtk_key_params.key_idx = key_index;
g_add_gtk_key_params.pairwise = pairwise;
if (!mac_addr) {
@@ -1287,18 +1163,19 @@ static int add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
g_gtk_keys_saved = true;
}
- host_int_add_rx_gtk(priv->hWILCWFIDrv, params->key, KeyLen,
- key_index, params->seq_len, params->seq, pu8RxMic, pu8TxMic, STATION_MODE, u8mode);
+ wilc_add_rx_gtk(vif, params->key, KeyLen,
+ key_index, params->seq_len,
+ params->seq, pu8RxMic,
+ pu8TxMic, STATION_MODE,
+ u8mode);
} else {
if (params->key_len > 16 && params->cipher == WLAN_CIPHER_SUITE_TKIP) {
- /* swap the tx mic by rx mic */
pu8RxMic = params->key + 24;
pu8TxMic = params->key + 16;
KeyLen = params->key_len - 16;
}
- /*save keys only on interface 0 (wifi interface)*/
- if (!g_ptk_keys_saved && netdev == wl->vif[0].ndev) {
+ if (!g_ptk_keys_saved && netdev == wl->vif[0]->ndev) {
g_add_ptk_key_params.key_idx = key_index;
g_add_ptk_key_params.pairwise = pairwise;
if (!mac_addr) {
@@ -1323,8 +1200,9 @@ static int add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
g_ptk_keys_saved = true;
}
- host_int_add_ptk(priv->hWILCWFIDrv, params->key, KeyLen, mac_addr,
- pu8RxMic, pu8TxMic, STATION_MODE, u8mode, key_index);
+ wilc_add_ptk(vif, params->key, KeyLen,
+ mac_addr, pu8RxMic, pu8TxMic,
+ STATION_MODE, u8mode, key_index);
PRINT_D(CFG80211_DBG, "Adding pairwise key\n");
if (INFO) {
for (i = 0; i < params->key_len; i++)
@@ -1337,22 +1215,11 @@ static int add_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
default:
PRINT_ER("Not supported cipher: Error(%d)\n", s32Error);
s32Error = -ENOTSUPP;
-
}
return s32Error;
}
-/**
- * @brief del_key
- * @details Remove a key given the @mac_addr (%NULL for a group key)
- * and @key_index, return -ENOENT if the key doesn't exist.
- * @param[in]
- * @return int : Return 0 on Success
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
static int del_key(struct wiphy *wiphy, struct net_device *netdev,
u8 key_index,
bool pairwise,
@@ -1360,26 +1227,21 @@ static int del_key(struct wiphy *wiphy, struct net_device *netdev,
{
struct wilc_priv *priv;
struct wilc *wl;
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
priv = wiphy_priv(wiphy);
- nic = netdev_priv(netdev);
- wl = nic->wilc;
+ vif = netdev_priv(netdev);
+ wl = vif->wilc;
- /*delete saved keys, if any*/
- if (netdev == wl->vif[0].ndev) {
+ if (netdev == wl->vif[0]->ndev) {
g_ptk_keys_saved = false;
g_gtk_keys_saved = false;
g_wep_keys_saved = false;
- /*Delete saved WEP keys params, if any*/
kfree(g_key_wep_params.key);
g_key_wep_params.key = NULL;
- /*freeing memory allocated by "wilc_gtk" and "wilc_ptk" in "WILC_WIFI_ADD_KEY"*/
-
if ((priv->wilc_gtk[key_index]) != NULL) {
-
kfree(priv->wilc_gtk[key_index]->key);
priv->wilc_gtk[key_index]->key = NULL;
kfree(priv->wilc_gtk[key_index]->seq);
@@ -1387,11 +1249,9 @@ static int del_key(struct wiphy *wiphy, struct net_device *netdev,
kfree(priv->wilc_gtk[key_index]);
priv->wilc_gtk[key_index] = NULL;
-
}
if ((priv->wilc_ptk[key_index]) != NULL) {
-
kfree(priv->wilc_ptk[key_index]->key);
priv->wilc_ptk[key_index]->key = NULL;
kfree(priv->wilc_ptk[key_index]->seq);
@@ -1400,7 +1260,6 @@ static int del_key(struct wiphy *wiphy, struct net_device *netdev,
priv->wilc_ptk[key_index] = NULL;
}
- /*Delete saved PTK and GTK keys params, if any*/
kfree(g_key_ptk_params.key);
g_key_ptk_params.key = NULL;
kfree(g_key_ptk_params.seq);
@@ -1411,8 +1270,7 @@ static int del_key(struct wiphy *wiphy, struct net_device *netdev,
kfree(g_key_gtk_params.seq);
g_key_gtk_params.seq = NULL;
- /*Reset WILC_CHANGING_VIR_IF register to allow adding futrue keys to CE H/W*/
- Set_machw_change_vir_if(netdev, false);
+ wilc_set_machw_change_vir_if(netdev, false);
}
if (key_index >= 0 && key_index <= 3) {
@@ -1420,28 +1278,15 @@ static int del_key(struct wiphy *wiphy, struct net_device *netdev,
priv->WILC_WFI_wep_key_len[key_index] = 0;
PRINT_D(CFG80211_DBG, "Removing WEP key with index = %d\n", key_index);
- host_int_remove_wep_key(priv->hWILCWFIDrv, key_index);
+ wilc_remove_wep_key(vif, key_index);
} else {
PRINT_D(CFG80211_DBG, "Removing all installed keys\n");
- host_int_remove_key(priv->hWILCWFIDrv, mac_addr);
+ wilc_remove_key(priv->hWILCWFIDrv, mac_addr);
}
return 0;
}
-/**
- * @brief get_key
- * @details Get information about the key with the given parameters.
- * @mac_addr will be %NULL when requesting information for a group
- * key. All pointers given to the @callback function need not be valid
- * after it returns. This function should return an error if it is
- * not possible to retrieve the key, -ENOENT if it doesn't exist.
- * @param[in]
- * @return int : Return 0 on Success
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
static int get_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
bool pairwise,
const u8 *mac_addr, void *cookie, void (*callback)(void *cookie, struct key_params *))
@@ -1477,69 +1322,48 @@ static int get_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
callback(cookie, &key_params);
- return 0; /* priv->wilc_gtk->key_len ?0 : -ENOENT; */
+ return 0;
}
-/**
- * @brief set_default_key
- * @details Set the default management frame key on an interface
- * @param[in]
- * @return int : Return 0 on Success.
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
static int set_default_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
bool unicast, bool multicast)
{
struct wilc_priv *priv;
-
+ struct wilc_vif *vif;
priv = wiphy_priv(wiphy);
+ vif = netdev_priv(priv->dev);
PRINT_D(CFG80211_DBG, "Setting default key with idx = %d\n", key_index);
if (key_index != priv->WILC_WFI_wep_default) {
-
- host_int_set_wep_default_key(priv->hWILCWFIDrv, key_index);
+ wilc_set_wep_default_keyid(vif, key_index);
}
return 0;
}
-/**
- * @brief get_station
- * @details Get station information for the station identified by @mac
- * @param[in] NONE
- * @return int : Return 0 on Success.
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
-
static int get_station(struct wiphy *wiphy, struct net_device *dev,
const u8 *mac, struct station_info *sinfo)
{
struct wilc_priv *priv;
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
u32 i = 0;
u32 associatedsta = 0;
u32 inactive_time = 0;
priv = wiphy_priv(wiphy);
- nic = netdev_priv(dev);
+ vif = netdev_priv(dev);
- if (nic->iftype == AP_MODE || nic->iftype == GO_MODE) {
+ if (vif->iftype == AP_MODE || vif->iftype == GO_MODE) {
PRINT_D(HOSTAPD_DBG, "Getting station parameters\n");
PRINT_INFO(HOSTAPD_DBG, ": %x%x%x%x%x\n", mac[0], mac[1], mac[2], mac[3], mac[4]);
for (i = 0; i < NUM_STA_ASSOCIATED; i++) {
-
if (!(memcmp(mac, priv->assoc_stainfo.au8Sta_AssociatedBss[i], ETH_ALEN))) {
associatedsta = i;
break;
}
-
}
if (associatedsta == -1) {
@@ -1549,16 +1373,15 @@ static int get_station(struct wiphy *wiphy, struct net_device *dev,
sinfo->filled |= BIT(NL80211_STA_INFO_INACTIVE_TIME);
- host_int_get_inactive_time(priv->hWILCWFIDrv, mac, &(inactive_time));
+ wilc_get_inactive_time(vif, mac, &inactive_time);
sinfo->inactive_time = 1000 * inactive_time;
PRINT_D(CFG80211_DBG, "Inactive time %d\n", sinfo->inactive_time);
-
}
- if (nic->iftype == STATION_MODE) {
+ if (vif->iftype == STATION_MODE) {
struct rf_info strStatistics;
- host_int_get_statistics(priv->hWILCWFIDrv, &strStatistics);
+ wilc_get_statistics(vif, &strStatistics);
sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL) |
BIT(NL80211_STA_INFO_RX_PACKETS) |
@@ -1566,16 +1389,17 @@ static int get_station(struct wiphy *wiphy, struct net_device *dev,
BIT(NL80211_STA_INFO_TX_FAILED) |
BIT(NL80211_STA_INFO_TX_BITRATE);
- sinfo->signal = strStatistics.s8RSSI;
- sinfo->rx_packets = strStatistics.u32RxCount;
- sinfo->tx_packets = strStatistics.u32TxCount + strStatistics.u32TxFailureCount;
- sinfo->tx_failed = strStatistics.u32TxFailureCount;
- sinfo->txrate.legacy = strStatistics.u8LinkSpeed * 10;
+ sinfo->signal = strStatistics.rssi;
+ sinfo->rx_packets = strStatistics.rx_cnt;
+ sinfo->tx_packets = strStatistics.tx_cnt + strStatistics.tx_fail_cnt;
+ sinfo->tx_failed = strStatistics.tx_fail_cnt;
+ sinfo->txrate.legacy = strStatistics.link_speed * 10;
- if ((strStatistics.u8LinkSpeed > TCP_ACK_FILTER_LINK_SPEED_THRESH) && (strStatistics.u8LinkSpeed != DEFAULT_LINK_SPEED))
- Enable_TCP_ACK_Filter(true);
- else if (strStatistics.u8LinkSpeed != DEFAULT_LINK_SPEED)
- Enable_TCP_ACK_Filter(false);
+ if ((strStatistics.link_speed > TCP_ACK_FILTER_LINK_SPEED_THRESH) &&
+ (strStatistics.link_speed != DEFAULT_LINK_SPEED))
+ wilc_enable_tcp_ack_filter(true);
+ else if (strStatistics.link_speed != DEFAULT_LINK_SPEED)
+ wilc_enable_tcp_ack_filter(false);
PRINT_D(CORECONFIG_DBG, "*** stats[%d][%d][%d][%d][%d]\n", sinfo->signal, sinfo->rx_packets, sinfo->tx_packets,
sinfo->tx_failed, sinfo->txrate.legacy);
@@ -1583,28 +1407,6 @@ static int get_station(struct wiphy *wiphy, struct net_device *dev,
return 0;
}
-
-/**
- * @brief change_bss
- * @details Modify parameters for a given BSS.
- * @param[in]
- * -use_cts_prot: Whether to use CTS protection
- * (0 = no, 1 = yes, -1 = do not change)
- * -use_short_preamble: Whether the use of short preambles is allowed
- * (0 = no, 1 = yes, -1 = do not change)
- * -use_short_slot_time: Whether the use of short slot time is allowed
- * (0 = no, 1 = yes, -1 = do not change)
- * -basic_rates: basic rates in IEEE 802.11 format
- * (or NULL for no change)
- * -basic_rates_len: number of basic rates
- * -ap_isolate: do not forward packets between connected stations
- * -ht_opmode: HT Operation mode
- * (u16 = opmode, -1 = do not change)
- * @return int : Return 0 on Success.
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
static int change_bss(struct wiphy *wiphy, struct net_device *dev,
struct bss_parameters *params)
{
@@ -1612,23 +1414,15 @@ static int change_bss(struct wiphy *wiphy, struct net_device *dev,
return 0;
}
-/**
- * @brief set_wiphy_params
- * @details Notify that wiphy parameters have changed;
- * @param[in] Changed bitfield (see &enum wiphy_params_flags) describes which values
- * have changed.
- * @return int : Return 0 on Success
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
static int set_wiphy_params(struct wiphy *wiphy, u32 changed)
{
s32 s32Error = 0;
struct cfg_param_val pstrCfgParamVal;
struct wilc_priv *priv;
+ struct wilc_vif *vif;
priv = wiphy_priv(wiphy);
+ vif = netdev_priv(priv->dev);
pstrCfgParamVal.flag = 0;
PRINT_D(CFG80211_DBG, "Setting Wiphy params\n");
@@ -1640,17 +1434,14 @@ static int set_wiphy_params(struct wiphy *wiphy, u32 changed)
pstrCfgParamVal.short_retry_limit = priv->dev->ieee80211_ptr->wiphy->retry_short;
}
if (changed & WIPHY_PARAM_RETRY_LONG) {
-
PRINT_D(CFG80211_DBG, "Setting WIPHY_PARAM_RETRY_LONG %d\n", priv->dev->ieee80211_ptr->wiphy->retry_long);
pstrCfgParamVal.flag |= RETRY_LONG;
pstrCfgParamVal.long_retry_limit = priv->dev->ieee80211_ptr->wiphy->retry_long;
-
}
if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
PRINT_D(CFG80211_DBG, "Setting WIPHY_PARAM_FRAG_THRESHOLD %d\n", priv->dev->ieee80211_ptr->wiphy->frag_threshold);
pstrCfgParamVal.flag |= FRAG_THRESHOLD;
pstrCfgParamVal.frag_threshold = priv->dev->ieee80211_ptr->wiphy->frag_threshold;
-
}
if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
@@ -1658,11 +1449,10 @@ static int set_wiphy_params(struct wiphy *wiphy, u32 changed)
pstrCfgParamVal.flag |= RTS_THRESHOLD;
pstrCfgParamVal.rts_threshold = priv->dev->ieee80211_ptr->wiphy->rts_threshold;
-
}
PRINT_D(CFG80211_DBG, "Setting CFG params in the host interface\n");
- s32Error = hif_set_cfg(priv->hWILCWFIDrv, &pstrCfgParamVal);
+ s32Error = wilc_hif_set_cfg(vif, &pstrCfgParamVal);
if (s32Error)
PRINT_ER("Error in setting WIPHY PARAMS\n");
@@ -1670,33 +1460,22 @@ static int set_wiphy_params(struct wiphy *wiphy, u32 changed)
return s32Error;
}
-/**
- * @brief set_pmksa
- * @details Cache a PMKID for a BSSID. This is mostly useful for fullmac
- * devices running firmwares capable of generating the (re) association
- * RSN IE. It allows for faster roaming between WPA2 BSSIDs.
- * @param[in]
- * @return int : Return 0 on Success
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
static int set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
struct cfg80211_pmksa *pmksa)
{
u32 i;
s32 s32Error = 0;
u8 flag = 0;
-
+ struct wilc_vif *vif;
struct wilc_priv *priv = wiphy_priv(wiphy);
+ vif = netdev_priv(priv->dev);
PRINT_D(CFG80211_DBG, "Setting PMKSA\n");
for (i = 0; i < priv->pmkid_list.numpmkid; i++) {
if (!memcmp(pmksa->bssid, priv->pmkid_list.pmkidlist[i].bssid,
ETH_ALEN)) {
- /*If bssid already exists and pmkid value needs to reset*/
flag = PMKID_FOUND;
PRINT_D(CFG80211_DBG, "PMKID already exists\n");
break;
@@ -1717,24 +1496,14 @@ static int set_pmksa(struct wiphy *wiphy, struct net_device *netdev,
if (!s32Error) {
PRINT_D(CFG80211_DBG, "Setting pmkid in the host interface\n");
- s32Error = host_int_set_pmkid_info(priv->hWILCWFIDrv, &priv->pmkid_list);
+ s32Error = wilc_set_pmkid_info(vif, &priv->pmkid_list);
}
return s32Error;
}
-/**
- * @brief del_pmksa
- * @details Delete a cached PMKID.
- * @param[in]
- * @return int : Return 0 on Success
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
static int del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
struct cfg80211_pmksa *pmksa)
{
-
u32 i;
s32 s32Error = 0;
@@ -1745,7 +1514,6 @@ static int del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
for (i = 0; i < priv->pmkid_list.numpmkid; i++) {
if (!memcmp(pmksa->bssid, priv->pmkid_list.pmkidlist[i].bssid,
ETH_ALEN)) {
- /*If bssid is found, reset the values*/
PRINT_D(CFG80211_DBG, "Reseting PMKID values\n");
memset(&priv->pmkid_list.pmkidlist[i], 0, sizeof(struct host_if_pmkid));
break;
@@ -1769,43 +1537,18 @@ static int del_pmksa(struct wiphy *wiphy, struct net_device *netdev,
return s32Error;
}
-/**
- * @brief flush_pmksa
- * @details Flush all cached PMKIDs.
- * @param[in]
- * @return int : Return 0 on Success
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
static int flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
{
struct wilc_priv *priv = wiphy_priv(wiphy);
PRINT_D(CFG80211_DBG, "Flushing PMKID key values\n");
- /*Get cashed Pmkids and set all with zeros*/
memset(&priv->pmkid_list, 0, sizeof(struct host_if_pmkid_attr));
return 0;
}
-
-/**
- * @brief WILC_WFI_CfgParseRxAction
- * @details Function parses the received frames and modifies the following attributes:
- * -GO Intent
- * -Channel list
- * -Operating Channel
- *
- * @param[in] u8* Buffer, u32 length
- * @return NONE.
- * @author mdaftedar
- * @date 12 DEC 2012
- * @version
- */
-
-void WILC_WFI_CfgParseRxAction(u8 *buf, u32 len)
+static void WILC_WFI_CfgParseRxAction(u8 *buf, u32 len)
{
u32 index = 0;
u32 i = 0, j = 0;
@@ -1822,42 +1565,30 @@ void WILC_WFI_CfgParseRxAction(u8 *buf, u32 len)
channel_list_attr_index = index;
else if (buf[index] == OPERCHAN_ATTR_ID)
op_channel_attr_index = index;
- index += buf[index + 1] + 3; /* ID,Length byte */
+ index += buf[index + 1] + 3;
}
- if (u8WLANChannel != INVALID_CHANNEL) {
-
- /*Modify channel list attribute*/
+ if (wlan_channel != INVALID_CHANNEL) {
if (channel_list_attr_index) {
PRINT_D(GENERIC_DBG, "Modify channel list attribute\n");
for (i = channel_list_attr_index + 3; i < ((channel_list_attr_index + 3) + buf[channel_list_attr_index + 1]); i++) {
if (buf[i] == 0x51) {
for (j = i + 2; j < ((i + 2) + buf[i + 1]); j++) {
- buf[j] = u8WLANChannel;
+ buf[j] = wlan_channel;
}
break;
}
}
}
- /*Modify operating channel attribute*/
+
if (op_channel_attr_index) {
PRINT_D(GENERIC_DBG, "Modify operating channel attribute\n");
buf[op_channel_attr_index + 6] = 0x51;
- buf[op_channel_attr_index + 7] = u8WLANChannel;
+ buf[op_channel_attr_index + 7] = wlan_channel;
}
}
}
-/**
- * @brief WILC_WFI_CfgParseTxAction
- * @details Function parses the transmitted action frames and modifies the
- * GO Intent attribute
- * @param[in] u8* Buffer, u32 length, bool bOperChan, u8 iftype
- * @return NONE.
- * @author mdaftedar
- * @date 12 DEC 2012
- * @version
- */
-void WILC_WFI_CfgParseTxAction(u8 *buf, u32 len, bool bOperChan, u8 iftype)
+static void WILC_WFI_CfgParseTxAction(u8 *buf, u32 len, bool bOperChan, u8 iftype)
{
u32 index = 0;
u32 i = 0, j = 0;
@@ -1876,44 +1607,31 @@ void WILC_WFI_CfgParseTxAction(u8 *buf, u32 len, bool bOperChan, u8 iftype)
channel_list_attr_index = index;
else if (buf[index] == OPERCHAN_ATTR_ID)
op_channel_attr_index = index;
- index += buf[index + 1] + 3; /* ID,Length byte */
+ index += buf[index + 1] + 3;
}
- if (u8WLANChannel != INVALID_CHANNEL && bOperChan) {
-
- /*Modify channel list attribute*/
+ if (wlan_channel != INVALID_CHANNEL && bOperChan) {
if (channel_list_attr_index) {
PRINT_D(GENERIC_DBG, "Modify channel list attribute\n");
for (i = channel_list_attr_index + 3; i < ((channel_list_attr_index + 3) + buf[channel_list_attr_index + 1]); i++) {
if (buf[i] == 0x51) {
for (j = i + 2; j < ((i + 2) + buf[i + 1]); j++) {
- buf[j] = u8WLANChannel;
+ buf[j] = wlan_channel;
}
break;
}
}
}
- /*Modify operating channel attribute*/
+
if (op_channel_attr_index) {
PRINT_D(GENERIC_DBG, "Modify operating channel attribute\n");
buf[op_channel_attr_index + 6] = 0x51;
- buf[op_channel_attr_index + 7] = u8WLANChannel;
+ buf[op_channel_attr_index + 7] = wlan_channel;
}
}
}
-/* @brief WILC_WFI_p2p_rx
- * @details
- * @param[in]
- *
- * @return None
- * @author Mai Daftedar
- * @date 2 JUN 2013
- * @version 1.0
- */
-
void WILC_WFI_p2p_rx (struct net_device *dev, u8 *buff, u32 size)
{
-
struct wilc_priv *priv;
u32 header, pkt_offset;
struct host_if_drv *pstrWFIDrv;
@@ -1923,11 +1641,8 @@ void WILC_WFI_p2p_rx (struct net_device *dev, u8 *buff, u32 size)
priv = wiphy_priv(dev->ieee80211_ptr->wiphy);
pstrWFIDrv = (struct host_if_drv *)priv->hWILCWFIDrv;
- /* Get WILC header */
memcpy(&header, (buff - HOST_HDR_OFFSET), HOST_HDR_OFFSET);
- /* The packet offset field conain info about what type of managment frame */
- /* we are dealing with and ack status */
pkt_offset = GET_PKT_OFFSET(header);
if (pkt_offset & IS_MANAGMEMENT_CALLBACK) {
@@ -1948,21 +1663,18 @@ void WILC_WFI_p2p_rx (struct net_device *dev, u8 *buff, u32 size)
return;
}
} else {
-
PRINT_D(GENERIC_DBG, "Rx Frame Type:%x\n", buff[FRAME_TYPE_ID]);
- /*Upper layer is informed that the frame is received on this freq*/
s32Freq = ieee80211_channel_to_frequency(curr_channel, IEEE80211_BAND_2GHZ);
if (ieee80211_is_action(buff[FRAME_TYPE_ID])) {
PRINT_D(GENERIC_DBG, "Rx Action Frame Type: %x %x\n", buff[ACTION_SUBTYPE_ID], buff[P2P_PUB_ACTION_SUBTYPE]);
- if (priv->bCfgScanning && time_after_eq(jiffies, (unsigned long)pstrWFIDrv->u64P2p_MgmtTimeout)) {
+ if (priv->bCfgScanning && time_after_eq(jiffies, (unsigned long)pstrWFIDrv->p2p_timeout)) {
PRINT_D(GENERIC_DBG, "Receiving action frames from wrong channels\n");
return;
}
if (buff[ACTION_CAT_ID] == PUB_ACTION_ATTR_ID) {
-
switch (buff[ACTION_SUBTYPE_ID]) {
case GAS_INTIAL_REQ:
PRINT_D(GENERIC_DBG, "GAS INITIAL REQ %x\n", buff[ACTION_SUBTYPE_ID]);
@@ -1973,39 +1685,37 @@ void WILC_WFI_p2p_rx (struct net_device *dev, u8 *buff, u32 size)
break;
case PUBLIC_ACT_VENDORSPEC:
- /*Now we have a public action vendor specific action frame, check if its a p2p public action frame
- * based on the standard its should have the p2p_oui attribute with the following values 50 6f 9A 09*/
- if (!memcmp(u8P2P_oui, &buff[ACTION_SUBTYPE_ID + 1], 4)) {
+ if (!memcmp(p2p_oui, &buff[ACTION_SUBTYPE_ID + 1], 4)) {
if ((buff[P2P_PUB_ACTION_SUBTYPE] == GO_NEG_REQ || buff[P2P_PUB_ACTION_SUBTYPE] == GO_NEG_RSP)) {
- if (!bWilc_ie) {
+ if (!wilc_ie) {
for (i = P2P_PUB_ACTION_SUBTYPE; i < size; i++) {
- if (!memcmp(u8P2P_vendorspec, &buff[i], 6)) {
- u8P2Precvrandom = buff[i + 6];
- bWilc_ie = true;
- PRINT_D(GENERIC_DBG, "WILC Vendor specific IE:%02x\n", u8P2Precvrandom);
+ if (!memcmp(p2p_vendor_spec, &buff[i], 6)) {
+ p2p_recv_random = buff[i + 6];
+ wilc_ie = true;
+ PRINT_D(GENERIC_DBG, "WILC Vendor specific IE:%02x\n", p2p_recv_random);
break;
}
}
}
}
- if (u8P2Plocalrandom > u8P2Precvrandom) {
+ if (p2p_local_random > p2p_recv_random) {
if ((buff[P2P_PUB_ACTION_SUBTYPE] == GO_NEG_REQ || buff[P2P_PUB_ACTION_SUBTYPE] == GO_NEG_RSP
|| buff[P2P_PUB_ACTION_SUBTYPE] == P2P_INV_REQ || buff[P2P_PUB_ACTION_SUBTYPE] == P2P_INV_RSP)) {
for (i = P2P_PUB_ACTION_SUBTYPE + 2; i < size; i++) {
- if (buff[i] == P2PELEM_ATTR_ID && !(memcmp(u8P2P_oui, &buff[i + 2], 4))) {
+ if (buff[i] == P2PELEM_ATTR_ID && !(memcmp(p2p_oui, &buff[i + 2], 4))) {
WILC_WFI_CfgParseRxAction(&buff[i + 6], size - (i + 6));
break;
}
}
}
- } else
- PRINT_D(GENERIC_DBG, "PEER WILL BE GO LocaRand=%02x RecvRand %02x\n", u8P2Plocalrandom, u8P2Precvrandom);
+ } else {
+ PRINT_D(GENERIC_DBG, "PEER WILL BE GO LocaRand=%02x RecvRand %02x\n", p2p_local_random, p2p_recv_random);
+ }
}
- if ((buff[P2P_PUB_ACTION_SUBTYPE] == GO_NEG_REQ || buff[P2P_PUB_ACTION_SUBTYPE] == GO_NEG_RSP) && (bWilc_ie)) {
+ if ((buff[P2P_PUB_ACTION_SUBTYPE] == GO_NEG_REQ || buff[P2P_PUB_ACTION_SUBTYPE] == GO_NEG_RSP) && (wilc_ie)) {
PRINT_D(GENERIC_DBG, "Sending P2P to host without extra elemnt\n");
- /* extra attribute for sig_dbm: signal strength in mBm, or 0 if unknown */
cfg80211_rx_mgmt(priv->wdev, s32Freq, 0, buff, size - 7, 0);
return;
}
@@ -2022,16 +1732,6 @@ void WILC_WFI_p2p_rx (struct net_device *dev, u8 *buff, u32 size)
}
}
-/**
- * @brief WILC_WFI_mgmt_tx_complete
- * @details Returns result of writing mgmt frame to VMM (Tx buffers are freed here)
- * @param[in] priv
- * transmitting status
- * @return None
- * @author Amr Abdelmoghny
- * @date 20 MAY 2013
- * @version 1.0
- */
static void WILC_WFI_mgmt_tx_complete(void *priv, int status)
{
struct p2p_mgmt_data *pv_data = (struct p2p_mgmt_data *)priv;
@@ -2041,16 +1741,6 @@ static void WILC_WFI_mgmt_tx_complete(void *priv, int status)
kfree(pv_data);
}
-/**
- * @brief WILC_WFI_RemainOnChannelReady
- * @details Callback function, called from handle_remain_on_channel on being ready on channel
- * @param
- * @return none
- * @author Amr abdelmoghny
- * @date 9 JUNE 2013
- * @version
- */
-
static void WILC_WFI_RemainOnChannelReady(void *pUserVoid)
{
struct wilc_priv *priv;
@@ -2068,16 +1758,6 @@ static void WILC_WFI_RemainOnChannelReady(void *pUserVoid)
GFP_KERNEL);
}
-/**
- * @brief WILC_WFI_RemainOnChannelExpired
- * @details Callback function, called on expiration of remain-on-channel duration
- * @param
- * @return none
- * @author Amr abdelmoghny
- * @date 15 MAY 2013
- * @version
- */
-
static void WILC_WFI_RemainOnChannelExpired(void *pUserVoid, u32 u32SessionID)
{
struct wilc_priv *priv;
@@ -2089,7 +1769,6 @@ static void WILC_WFI_RemainOnChannelExpired(void *pUserVoid, u32 u32SessionID)
priv->bInP2PlistenState = false;
- /*Inform wpas of remain-on-channel expiration*/
cfg80211_remain_on_channel_expired(priv->wdev,
priv->strRemainOnChanParams.u64ListenCookie,
priv->strRemainOnChanParams.pstrListenChan,
@@ -2100,20 +1779,6 @@ static void WILC_WFI_RemainOnChannelExpired(void *pUserVoid, u32 u32SessionID)
}
}
-
-/**
- * @brief remain_on_channel
- * @details Request the driver to remain awake on the specified
- * channel for the specified duration to complete an off-channel
- * operation (e.g., public action frame exchange). When the driver is
- * ready on the requested channel, it must indicate this with an event
- * notification by calling cfg80211_ready_on_channel().
- * @param[in]
- * @return int : Return 0 on Success
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
static int remain_on_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
struct ieee80211_channel *chan,
@@ -2121,8 +1786,10 @@ static int remain_on_channel(struct wiphy *wiphy,
{
s32 s32Error = 0;
struct wilc_priv *priv;
+ struct wilc_vif *vif;
priv = wiphy_priv(wiphy);
+ vif = netdev_priv(priv->dev);
PRINT_D(GENERIC_DBG, "Remaining on channel %d\n", chan->hw_value);
@@ -2134,61 +1801,37 @@ static int remain_on_channel(struct wiphy *wiphy,
curr_channel = chan->hw_value;
- /*Setting params needed by WILC_WFI_RemainOnChannelExpired()*/
priv->strRemainOnChanParams.pstrListenChan = chan;
priv->strRemainOnChanParams.u64ListenCookie = *cookie;
priv->strRemainOnChanParams.u32ListenDuration = duration;
priv->strRemainOnChanParams.u32ListenSessionID++;
- s32Error = host_int_remain_on_channel(priv->hWILCWFIDrv
- , priv->strRemainOnChanParams.u32ListenSessionID
- , duration
- , chan->hw_value
- , WILC_WFI_RemainOnChannelExpired
- , WILC_WFI_RemainOnChannelReady
- , (void *)priv);
+ s32Error = wilc_remain_on_channel(vif,
+ priv->strRemainOnChanParams.u32ListenSessionID,
+ duration, chan->hw_value,
+ WILC_WFI_RemainOnChannelExpired,
+ WILC_WFI_RemainOnChannelReady, (void *)priv);
return s32Error;
}
-/**
- * @brief cancel_remain_on_channel
- * @details Cancel an on-going remain-on-channel operation.
- * This allows the operation to be terminated prior to timeout based on
- * the duration value.
- * @param[in] struct wiphy *wiphy,
- * @param[in] struct net_device *dev
- * @param[in] u64 cookie,
- * @return int : Return 0 on Success
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
static int cancel_remain_on_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
u64 cookie)
{
s32 s32Error = 0;
struct wilc_priv *priv;
+ struct wilc_vif *vif;
priv = wiphy_priv(wiphy);
+ vif = netdev_priv(priv->dev);
PRINT_D(CFG80211_DBG, "Cancel remain on channel\n");
- s32Error = host_int_ListenStateExpired(priv->hWILCWFIDrv, priv->strRemainOnChanParams.u32ListenSessionID);
+ s32Error = wilc_listen_state_expired(vif, priv->strRemainOnChanParams.u32ListenSessionID);
return s32Error;
}
-/**
- * @brief WILC_WFI_mgmt_tx_frame
- * @details
- *
- * @param[in]
- * @return NONE.
- * @author mdaftedar
- * @date 01 JUL 2012
- * @version
- */
-extern bool bEnablePS;
+
static int mgmt_tx(struct wiphy *wiphy,
struct wireless_dev *wdev,
struct cfg80211_mgmt_tx_params *params,
@@ -2203,10 +1846,10 @@ static int mgmt_tx(struct wiphy *wiphy,
struct wilc_priv *priv;
struct host_if_drv *pstrWFIDrv;
u32 i;
- perInterface_wlan_t *nic;
- u32 buf_len = len + sizeof(u8P2P_vendorspec) + sizeof(u8P2Plocalrandom);
+ struct wilc_vif *vif;
+ u32 buf_len = len + sizeof(p2p_vendor_spec) + sizeof(p2p_local_random);
- nic = netdev_priv(wdev->netdev);
+ vif = netdev_priv(wdev->netdev);
priv = wiphy_priv(wiphy);
pstrWFIDrv = (struct host_if_drv *)priv->hWILCWFIDrv;
@@ -2215,15 +1858,13 @@ static int mgmt_tx(struct wiphy *wiphy,
mgmt = (const struct ieee80211_mgmt *) buf;
if (ieee80211_is_mgmt(mgmt->frame_control)) {
-
- /*mgmt frame allocation*/
mgmt_tx = kmalloc(sizeof(struct p2p_mgmt_data), GFP_KERNEL);
- if (mgmt_tx == NULL) {
+ if (!mgmt_tx) {
PRINT_ER("Failed to allocate memory for mgmt_tx structure\n");
return -EFAULT;
}
mgmt_tx->buff = kmalloc(buf_len, GFP_KERNEL);
- if (mgmt_tx->buff == NULL) {
+ if (!mgmt_tx->buff) {
PRINT_ER("Failed to allocate memory for mgmt_tx buff\n");
kfree(mgmt_tx);
return -EFAULT;
@@ -2235,23 +1876,18 @@ static int mgmt_tx(struct wiphy *wiphy,
if (ieee80211_is_probe_resp(mgmt->frame_control)) {
PRINT_D(GENERIC_DBG, "TX: Probe Response\n");
PRINT_D(GENERIC_DBG, "Setting channel: %d\n", chan->hw_value);
- host_int_set_mac_chnl_num(priv->hWILCWFIDrv, chan->hw_value);
- /*Save the current channel after we tune to it*/
+ wilc_set_mac_chnl_num(vif, chan->hw_value);
curr_channel = chan->hw_value;
} else if (ieee80211_is_action(mgmt->frame_control)) {
PRINT_D(GENERIC_DBG, "ACTION FRAME:%x\n", (u16)mgmt->frame_control);
if (buf[ACTION_CAT_ID] == PUB_ACTION_ATTR_ID) {
- /*Only set the channel, if not a negotiation confirmation frame
- * (If Negotiation confirmation frame, force it
- * to be transmitted on the same negotiation channel)*/
-
if (buf[ACTION_SUBTYPE_ID] != PUBLIC_ACT_VENDORSPEC ||
buf[P2P_PUB_ACTION_SUBTYPE] != GO_NEG_CONF) {
PRINT_D(GENERIC_DBG, "Setting channel: %d\n", chan->hw_value);
- host_int_set_mac_chnl_num(priv->hWILCWFIDrv, chan->hw_value);
- /*Save the current channel after we tune to it*/
+ wilc_set_mac_chnl_num(vif,
+ chan->hw_value);
curr_channel = chan->hw_value;
}
switch (buf[ACTION_SUBTYPE_ID]) {
@@ -2269,48 +1905,37 @@ static int mgmt_tx(struct wiphy *wiphy,
case PUBLIC_ACT_VENDORSPEC:
{
- /*Now we have a public action vendor specific action frame, check if its a p2p public action frame
- * based on the standard its should have the p2p_oui attribute with the following values 50 6f 9A 09*/
- if (!memcmp(u8P2P_oui, &buf[ACTION_SUBTYPE_ID + 1], 4)) {
- /*For the connection of two WILC's connection generate a rand number to determine who will be a GO*/
+ if (!memcmp(p2p_oui, &buf[ACTION_SUBTYPE_ID + 1], 4)) {
if ((buf[P2P_PUB_ACTION_SUBTYPE] == GO_NEG_REQ || buf[P2P_PUB_ACTION_SUBTYPE] == GO_NEG_RSP)) {
- if (u8P2Plocalrandom == 1 && u8P2Precvrandom < u8P2Plocalrandom) {
- get_random_bytes(&u8P2Plocalrandom, 1);
- /*Increment the number to prevent if its 0*/
- u8P2Plocalrandom++;
+ if (p2p_local_random == 1 && p2p_recv_random < p2p_local_random) {
+ get_random_bytes(&p2p_local_random, 1);
+ p2p_local_random++;
}
}
if ((buf[P2P_PUB_ACTION_SUBTYPE] == GO_NEG_REQ || buf[P2P_PUB_ACTION_SUBTYPE] == GO_NEG_RSP
|| buf[P2P_PUB_ACTION_SUBTYPE] == P2P_INV_REQ || buf[P2P_PUB_ACTION_SUBTYPE] == P2P_INV_RSP)) {
- if (u8P2Plocalrandom > u8P2Precvrandom) {
- PRINT_D(GENERIC_DBG, "LOCAL WILL BE GO LocaRand=%02x RecvRand %02x\n", u8P2Plocalrandom, u8P2Precvrandom);
+ if (p2p_local_random > p2p_recv_random) {
+ PRINT_D(GENERIC_DBG, "LOCAL WILL BE GO LocaRand=%02x RecvRand %02x\n", p2p_local_random, p2p_recv_random);
- /*Search for the p2p information information element , after the Public action subtype theres a byte for teh dialog token, skip that*/
for (i = P2P_PUB_ACTION_SUBTYPE + 2; i < len; i++) {
- if (buf[i] == P2PELEM_ATTR_ID && !(memcmp(u8P2P_oui, &buf[i + 2], 4))) {
+ if (buf[i] == P2PELEM_ATTR_ID && !(memcmp(p2p_oui, &buf[i + 2], 4))) {
if (buf[P2P_PUB_ACTION_SUBTYPE] == P2P_INV_REQ || buf[P2P_PUB_ACTION_SUBTYPE] == P2P_INV_RSP)
- WILC_WFI_CfgParseTxAction(&mgmt_tx->buff[i + 6], len - (i + 6), true, nic->iftype);
-
- /*If using supplicant go intent, no need at all*/
- /*to parse transmitted negotiation frames*/
+ WILC_WFI_CfgParseTxAction(&mgmt_tx->buff[i + 6], len - (i + 6), true, vif->iftype);
else
- WILC_WFI_CfgParseTxAction(&mgmt_tx->buff[i + 6], len - (i + 6), false, nic->iftype);
+ WILC_WFI_CfgParseTxAction(&mgmt_tx->buff[i + 6], len - (i + 6), false, vif->iftype);
break;
}
}
if (buf[P2P_PUB_ACTION_SUBTYPE] != P2P_INV_REQ && buf[P2P_PUB_ACTION_SUBTYPE] != P2P_INV_RSP) {
- /*
- * Adding WILC information element to allow two WILC devices to
- * identify each other and connect
- */
- memcpy(&mgmt_tx->buff[len], u8P2P_vendorspec, sizeof(u8P2P_vendorspec));
- mgmt_tx->buff[len + sizeof(u8P2P_vendorspec)] = u8P2Plocalrandom;
+ memcpy(&mgmt_tx->buff[len], p2p_vendor_spec, sizeof(p2p_vendor_spec));
+ mgmt_tx->buff[len + sizeof(p2p_vendor_spec)] = p2p_local_random;
mgmt_tx->size = buf_len;
}
- } else
- PRINT_D(GENERIC_DBG, "PEER WILL BE GO LocaRand=%02x RecvRand %02x\n", u8P2Plocalrandom, u8P2Precvrandom);
+ } else {
+ PRINT_D(GENERIC_DBG, "PEER WILL BE GO LocaRand=%02x RecvRand %02x\n", p2p_local_random, p2p_recv_random);
+ }
}
} else {
@@ -2326,18 +1951,17 @@ static int mgmt_tx(struct wiphy *wiphy,
break;
}
}
-
}
PRINT_D(GENERIC_DBG, "TX: ACTION FRAME Type:%x : Chan:%d\n", buf[ACTION_SUBTYPE_ID], chan->hw_value);
- pstrWFIDrv->u64P2p_MgmtTimeout = (jiffies + msecs_to_jiffies(wait));
-
- PRINT_D(GENERIC_DBG, "Current Jiffies: %lu Timeout:%llu\n", jiffies, pstrWFIDrv->u64P2p_MgmtTimeout);
+ pstrWFIDrv->p2p_timeout = (jiffies + msecs_to_jiffies(wait));
+ PRINT_D(GENERIC_DBG, "Current Jiffies: %lu Timeout:%llu\n",
+ jiffies, pstrWFIDrv->p2p_timeout);
}
- wilc_wlan_txq_add_mgmt_pkt(mgmt_tx, mgmt_tx->buff,
- mgmt_tx->size,
+ wilc_wlan_txq_add_mgmt_pkt(wdev->netdev, mgmt_tx,
+ mgmt_tx->buff, mgmt_tx->size,
WILC_WFI_mgmt_tx_complete);
} else {
PRINT_D(GENERIC_DBG, "This function transmits only management frames\n");
@@ -2357,7 +1981,7 @@ static int mgmt_tx_cancel_wait(struct wiphy *wiphy,
PRINT_D(GENERIC_DBG, "Tx Cancel wait :%lu\n", jiffies);
- pstrWFIDrv->u64P2p_MgmtTimeout = jiffies;
+ pstrWFIDrv->p2p_timeout = jiffies;
if (!priv->bInP2PlistenState) {
cfg80211_remain_on_channel_expired(priv->wdev,
@@ -2369,28 +1993,16 @@ static int mgmt_tx_cancel_wait(struct wiphy *wiphy,
return 0;
}
-/**
- * @brief wilc_mgmt_frame_register
- * @details Notify driver that a management frame type was
- * registered. Note that this callback may not sleep, and cannot run
- * concurrently with itself.
- * @param[in]
- * @return NONE.
- * @author mdaftedar
- * @date 01 JUL 2012
- * @version
- */
void wilc_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev,
u16 frame_type, bool reg)
{
-
struct wilc_priv *priv;
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct wilc *wl;
priv = wiphy_priv(wiphy);
- nic = netdev_priv(priv->wdev->netdev);
- wl = nic->wilc;
+ vif = netdev_priv(priv->wdev->netdev);
+ wl = vif->wilc;
if (!frame_type)
return;
@@ -2399,15 +2011,15 @@ void wilc_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev,
switch (frame_type) {
case PROBE_REQ:
{
- nic->g_struct_frame_reg[0].frame_type = frame_type;
- nic->g_struct_frame_reg[0].reg = reg;
+ vif->g_struct_frame_reg[0].frame_type = frame_type;
+ vif->g_struct_frame_reg[0].reg = reg;
}
break;
case ACTION:
{
- nic->g_struct_frame_reg[1].frame_type = frame_type;
- nic->g_struct_frame_reg[1].reg = reg;
+ vif->g_struct_frame_reg[1].frame_type = frame_type;
+ vif->g_struct_frame_reg[1].reg = reg;
}
break;
@@ -2415,54 +2027,27 @@ void wilc_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev,
{
break;
}
-
}
- /*If mac is closed, then return*/
+
if (!wl->initialized) {
PRINT_D(GENERIC_DBG, "Return since mac is closed\n");
return;
}
- host_int_frame_register(priv->hWILCWFIDrv, frame_type, reg);
-
-
+ wilc_frame_register(vif, frame_type, reg);
}
-/**
- * @brief set_cqm_rssi_config
- * @details Configure connection quality monitor RSSI threshold.
- * @param[in] struct wiphy *wiphy:
- * @param[in] struct net_device *dev:
- * @param[in] s32 rssi_thold:
- * @param[in] u32 rssi_hyst:
- * @return int : Return 0 on Success
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
static int set_cqm_rssi_config(struct wiphy *wiphy, struct net_device *dev,
s32 rssi_thold, u32 rssi_hyst)
{
PRINT_D(CFG80211_DBG, "Setting CQM RSSi Function\n");
return 0;
-
}
-/**
- * @brief dump_station
- * @details Configure connection quality monitor RSSI threshold.
- * @param[in] struct wiphy *wiphy:
- * @param[in] struct net_device *dev
- * @param[in] int idx
- * @param[in] u8 *mac
- * @param[in] struct station_info *sinfo
- * @return int : Return 0 on Success
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
+
static int dump_station(struct wiphy *wiphy, struct net_device *dev,
int idx, u8 *mac, struct station_info *sinfo)
{
struct wilc_priv *priv;
+ struct wilc_vif *vif;
PRINT_D(CFG80211_DBG, "Dumping station information\n");
@@ -2470,143 +2055,109 @@ static int dump_station(struct wiphy *wiphy, struct net_device *dev,
return -ENOENT;
priv = wiphy_priv(wiphy);
+ vif = netdev_priv(priv->dev);
sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
- host_int_get_rssi(priv->hWILCWFIDrv, &(sinfo->signal));
+ wilc_get_rssi(vif, &sinfo->signal);
return 0;
-
}
-
-/**
- * @brief set_power_mgmt
- * @details
- * @param[in]
- * @return int : Return 0 on Success.
- * @author mdaftedar
- * @date 01 JUL 2012
- * @version 1.0
- */
static int set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
bool enabled, int timeout)
{
struct wilc_priv *priv;
+ struct wilc_vif *vif;
PRINT_D(CFG80211_DBG, " Power save Enabled= %d , TimeOut = %d\n", enabled, timeout);
- if (wiphy == NULL)
+ if (!wiphy)
return -ENOENT;
priv = wiphy_priv(wiphy);
- if (priv->hWILCWFIDrv == NULL) {
+ vif = netdev_priv(priv->dev);
+ if (!priv->hWILCWFIDrv) {
PRINT_ER("Driver is NULL\n");
return -EIO;
}
- if (bEnablePS)
- host_int_set_power_mgmt(priv->hWILCWFIDrv, enabled, timeout);
+ if (wilc_enable_ps)
+ wilc_set_power_mgmt(vif, enabled, timeout);
return 0;
-
}
-/**
- * @brief change_virtual_intf
- * @details Change type/configuration of virtual interface,
- * keep the struct wireless_dev's iftype updated.
- * @param[in] NONE
- * @return int : Return 0 on Success.
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
-int wilc1000_wlan_init(struct net_device *dev, perInterface_wlan_t *p_nic);
-
static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev,
enum nl80211_iftype type, u32 *flags, struct vif_params *params)
{
struct wilc_priv *priv;
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
u8 interface_type;
u16 TID = 0;
u8 i;
struct wilc *wl;
- nic = netdev_priv(dev);
+ vif = netdev_priv(dev);
priv = wiphy_priv(wiphy);
- wl = nic->wilc;
+ wl = vif->wilc;
PRINT_D(HOSTAPD_DBG, "In Change virtual interface function\n");
PRINT_D(HOSTAPD_DBG, "Wireless interface name =%s\n", dev->name);
- u8P2Plocalrandom = 0x01;
- u8P2Precvrandom = 0x00;
-
- bWilc_ie = false;
-
- g_obtainingIP = false;
- del_timer(&hDuringIpTimer);
+ p2p_local_random = 0x01;
+ p2p_recv_random = 0x00;
+ wilc_ie = false;
+ wilc_optaining_ip = false;
+ del_timer(&wilc_during_ip_timer);
PRINT_D(GENERIC_DBG, "Changing virtual interface, enable scan\n");
- /*Set WILC_CHANGING_VIR_IF register to disallow adding futrue keys to CE H/W*/
+
if (g_ptk_keys_saved && g_gtk_keys_saved) {
- Set_machw_change_vir_if(dev, true);
+ wilc_set_machw_change_vir_if(dev, true);
}
switch (type) {
case NL80211_IFTYPE_STATION:
- connecting = 0;
+ wilc_connecting = 0;
PRINT_D(HOSTAPD_DBG, "Interface type = NL80211_IFTYPE_STATION\n");
- /* send delba over wlan interface */
-
-
dev->ieee80211_ptr->iftype = type;
priv->wdev->iftype = type;
- nic->monitor_flag = 0;
- nic->iftype = STATION_MODE;
+ vif->monitor_flag = 0;
+ vif->iftype = STATION_MODE;
- /*Remove the enteries of the previously connected clients*/
memset(priv->assoc_stainfo.au8Sta_AssociatedBss, 0, MAX_NUM_STA * ETH_ALEN);
- interface_type = nic->iftype;
- nic->iftype = STATION_MODE;
+ interface_type = vif->iftype;
+ vif->iftype = STATION_MODE;
if (wl->initialized) {
- host_int_del_All_Rx_BASession(priv->hWILCWFIDrv,
- wl->vif[0].bssid, TID);
- /* ensure that the message Q is empty */
- host_int_wait_msg_queue_idle();
+ wilc_del_all_rx_ba_session(vif, wl->vif[0]->bssid,
+ TID);
+ wilc_wait_msg_queue_idle();
- /*Eliminate host interface blocking state*/
up(&wl->cfg_event);
wilc1000_wlan_deinit(dev);
- wilc1000_wlan_init(dev, nic);
- g_wilc_initialized = 1;
- nic->iftype = interface_type;
+ wilc1000_wlan_init(dev, vif);
+ wilc_initialized = 1;
+ vif->iftype = interface_type;
- /*Setting interface 1 drv handler and mac address in newly downloaded FW*/
- host_int_set_wfi_drv_handler(wl->vif[0].hif_drv);
- host_int_set_MacAddress(wl->vif[0].hif_drv,
- wl->vif[0].src_addr);
- host_int_set_operation_mode(priv->hWILCWFIDrv, STATION_MODE);
+ wilc_set_wfi_drv_handler(vif,
+ wilc_get_vif_idx(wl->vif[0]));
+ wilc_set_mac_address(wl->vif[0], wl->vif[0]->src_addr);
+ wilc_set_operation_mode(vif, STATION_MODE);
- /*Add saved WEP keys, if any*/
if (g_wep_keys_saved) {
- host_int_set_wep_default_key(wl->vif[0].hif_drv,
- g_key_wep_params.key_idx);
- host_int_add_wep_key_bss_sta(wl->vif[0].hif_drv,
- g_key_wep_params.key,
- g_key_wep_params.key_len,
- g_key_wep_params.key_idx);
+ wilc_set_wep_default_keyid(wl->vif[0],
+ g_key_wep_params.key_idx);
+ wilc_add_wep_key_bss_sta(wl->vif[0],
+ g_key_wep_params.key,
+ g_key_wep_params.key_len,
+ g_key_wep_params.key_idx);
}
- /*No matter the driver handler passed here, it will be overwriiten*/
- /*in Handle_FlushConnect() with gu8FlushedJoinReqDrvHandler*/
- host_int_flush_join_req(priv->hWILCWFIDrv);
+ wilc_flush_join_req(vif);
- /*Add saved PTK and GTK keys, if any*/
if (g_ptk_keys_saved && g_gtk_keys_saved) {
PRINT_D(CFG80211_DBG, "ptk %x %x %x\n", g_key_ptk_params.key[0],
g_key_ptk_params.key[1],
@@ -2614,15 +2165,15 @@ static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev,
PRINT_D(CFG80211_DBG, "gtk %x %x %x\n", g_key_gtk_params.key[0],
g_key_gtk_params.key[1],
g_key_gtk_params.key[2]);
- add_key(wl->vif[0].ndev->ieee80211_ptr->wiphy,
- wl->vif[0].ndev,
+ add_key(wl->vif[0]->ndev->ieee80211_ptr->wiphy,
+ wl->vif[0]->ndev,
g_add_ptk_key_params.key_idx,
g_add_ptk_key_params.pairwise,
g_add_ptk_key_params.mac_addr,
(struct key_params *)(&g_key_ptk_params));
- add_key(wl->vif[0].ndev->ieee80211_ptr->wiphy,
- wl->vif[0].ndev,
+ add_key(wl->vif[0]->ndev->ieee80211_ptr->wiphy,
+ wl->vif[0]->ndev,
g_add_gtk_key_params.key_idx,
g_add_gtk_key_params.pairwise,
g_add_gtk_key_params.mac_addr,
@@ -2631,64 +2182,58 @@ static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev,
if (wl->initialized) {
for (i = 0; i < num_reg_frame; i++) {
- PRINT_D(INIT_DBG, "Frame registering Type: %x - Reg: %d\n", nic->g_struct_frame_reg[i].frame_type,
- nic->g_struct_frame_reg[i].reg);
- host_int_frame_register(priv->hWILCWFIDrv,
- nic->g_struct_frame_reg[i].frame_type,
- nic->g_struct_frame_reg[i].reg);
+ PRINT_D(INIT_DBG, "Frame registering Type: %x - Reg: %d\n", vif->g_struct_frame_reg[i].frame_type,
+ vif->g_struct_frame_reg[i].reg);
+ wilc_frame_register(vif,
+ vif->g_struct_frame_reg[i].frame_type,
+ vif->g_struct_frame_reg[i].reg);
}
}
- bEnablePS = true;
- host_int_set_power_mgmt(priv->hWILCWFIDrv, 1, 0);
+ wilc_enable_ps = true;
+ wilc_set_power_mgmt(vif, 1, 0);
}
break;
case NL80211_IFTYPE_P2P_CLIENT:
- bEnablePS = false;
- host_int_set_power_mgmt(priv->hWILCWFIDrv, 0, 0);
- connecting = 0;
+ wilc_enable_ps = false;
+ wilc_set_power_mgmt(vif, 0, 0);
+ wilc_connecting = 0;
PRINT_D(HOSTAPD_DBG, "Interface type = NL80211_IFTYPE_P2P_CLIENT\n");
- host_int_del_All_Rx_BASession(priv->hWILCWFIDrv,
- wl->vif[0].bssid, TID);
+ wilc_del_all_rx_ba_session(vif, wl->vif[0]->bssid, TID);
dev->ieee80211_ptr->iftype = type;
priv->wdev->iftype = type;
- nic->monitor_flag = 0;
+ vif->monitor_flag = 0;
PRINT_D(HOSTAPD_DBG, "Downloading P2P_CONCURRENCY_FIRMWARE\n");
- nic->iftype = CLIENT_MODE;
+ vif->iftype = CLIENT_MODE;
if (wl->initialized) {
- /* ensure that the message Q is empty */
- host_int_wait_msg_queue_idle();
+ wilc_wait_msg_queue_idle();
wilc1000_wlan_deinit(dev);
- wilc1000_wlan_init(dev, nic);
- g_wilc_initialized = 1;
+ wilc1000_wlan_init(dev, vif);
+ wilc_initialized = 1;
- host_int_set_wfi_drv_handler(wl->vif[0].hif_drv);
- host_int_set_MacAddress(wl->vif[0].hif_drv,
- wl->vif[0].src_addr);
- host_int_set_operation_mode(priv->hWILCWFIDrv, STATION_MODE);
+ wilc_set_wfi_drv_handler(vif,
+ wilc_get_vif_idx(wl->vif[0]));
+ wilc_set_mac_address(wl->vif[0], wl->vif[0]->src_addr);
+ wilc_set_operation_mode(vif, STATION_MODE);
- /*Add saved WEP keys, if any*/
if (g_wep_keys_saved) {
- host_int_set_wep_default_key(wl->vif[0].hif_drv,
- g_key_wep_params.key_idx);
- host_int_add_wep_key_bss_sta(wl->vif[0].hif_drv,
- g_key_wep_params.key,
- g_key_wep_params.key_len,
- g_key_wep_params.key_idx);
+ wilc_set_wep_default_keyid(wl->vif[0],
+ g_key_wep_params.key_idx);
+ wilc_add_wep_key_bss_sta(wl->vif[0],
+ g_key_wep_params.key,
+ g_key_wep_params.key_len,
+ g_key_wep_params.key_idx);
}
- /*No matter the driver handler passed here, it will be overwriiten*/
- /*in Handle_FlushConnect() with gu8FlushedJoinReqDrvHandler*/
- host_int_flush_join_req(priv->hWILCWFIDrv);
+ wilc_flush_join_req(vif);
- /*Add saved PTK and GTK keys, if any*/
if (g_ptk_keys_saved && g_gtk_keys_saved) {
PRINT_D(CFG80211_DBG, "ptk %x %x %x\n", g_key_ptk_params.key[0],
g_key_ptk_params.key[1],
@@ -2696,59 +2241,58 @@ static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev,
PRINT_D(CFG80211_DBG, "gtk %x %x %x\n", g_key_gtk_params.key[0],
g_key_gtk_params.key[1],
g_key_gtk_params.key[2]);
- add_key(wl->vif[0].ndev->ieee80211_ptr->wiphy,
- wl->vif[0].ndev,
+ add_key(wl->vif[0]->ndev->ieee80211_ptr->wiphy,
+ wl->vif[0]->ndev,
g_add_ptk_key_params.key_idx,
g_add_ptk_key_params.pairwise,
g_add_ptk_key_params.mac_addr,
(struct key_params *)(&g_key_ptk_params));
- add_key(wl->vif[0].ndev->ieee80211_ptr->wiphy,
- wl->vif[0].ndev,
+ add_key(wl->vif[0]->ndev->ieee80211_ptr->wiphy,
+ wl->vif[0]->ndev,
g_add_gtk_key_params.key_idx,
g_add_gtk_key_params.pairwise,
g_add_gtk_key_params.mac_addr,
(struct key_params *)(&g_key_gtk_params));
}
- /*Refresh scan, to refresh the scan results to the wpa_supplicant. Set MachHw to false to enable further key installments*/
refresh_scan(priv, 1, true);
- Set_machw_change_vir_if(dev, false);
+ wilc_set_machw_change_vir_if(dev, false);
if (wl->initialized) {
for (i = 0; i < num_reg_frame; i++) {
- PRINT_D(INIT_DBG, "Frame registering Type: %x - Reg: %d\n", nic->g_struct_frame_reg[i].frame_type,
- nic->g_struct_frame_reg[i].reg);
- host_int_frame_register(priv->hWILCWFIDrv,
- nic->g_struct_frame_reg[i].frame_type,
- nic->g_struct_frame_reg[i].reg);
+ PRINT_D(INIT_DBG, "Frame registering Type: %x - Reg: %d\n", vif->g_struct_frame_reg[i].frame_type,
+ vif->g_struct_frame_reg[i].reg);
+ wilc_frame_register(vif,
+ vif->g_struct_frame_reg[i].frame_type,
+ vif->g_struct_frame_reg[i].reg);
}
}
}
break;
case NL80211_IFTYPE_AP:
- bEnablePS = false;
+ wilc_enable_ps = false;
PRINT_D(HOSTAPD_DBG, "Interface type = NL80211_IFTYPE_AP %d\n", type);
dev->ieee80211_ptr->iftype = type;
priv->wdev->iftype = type;
- nic->iftype = AP_MODE;
+ vif->iftype = AP_MODE;
PRINT_D(CORECONFIG_DBG, "priv->hWILCWFIDrv[%p]\n", priv->hWILCWFIDrv);
PRINT_D(HOSTAPD_DBG, "Downloading AP firmware\n");
- linux_wlan_get_firmware(nic);
- /*If wilc is running, then close-open to actually get new firmware running (serves P2P)*/
+ wilc_wlan_get_firmware(dev);
+
if (wl->initialized) {
- nic->iftype = AP_MODE;
- mac_close(dev);
- mac_open(dev);
+ vif->iftype = AP_MODE;
+ wilc_mac_close(dev);
+ wilc_mac_open(dev);
for (i = 0; i < num_reg_frame; i++) {
- PRINT_D(INIT_DBG, "Frame registering Type: %x - Reg: %d\n", nic->g_struct_frame_reg[i].frame_type,
- nic->g_struct_frame_reg[i].reg);
- host_int_frame_register(priv->hWILCWFIDrv,
- nic->g_struct_frame_reg[i].frame_type,
- nic->g_struct_frame_reg[i].reg);
+ PRINT_D(INIT_DBG, "Frame registering Type: %x - Reg: %d\n", vif->g_struct_frame_reg[i].frame_type,
+ vif->g_struct_frame_reg[i].reg);
+ wilc_frame_register(vif,
+ vif->g_struct_frame_reg[i].frame_type,
+ vif->g_struct_frame_reg[i].reg);
}
}
break;
@@ -2756,16 +2300,12 @@ static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev,
case NL80211_IFTYPE_P2P_GO:
PRINT_D(GENERIC_DBG, "start duringIP timer\n");
- g_obtainingIP = true;
- mod_timer(&hDuringIpTimer, jiffies + msecs_to_jiffies(duringIP_TIME));
- host_int_set_power_mgmt(priv->hWILCWFIDrv, 0, 0);
- /*Delete block ack has to be the latest config packet*/
- /*sent before downloading new FW. This is because it blocks on*/
- /*hWaitResponse semaphore, which allows previous config*/
- /*packets to actually take action on old FW*/
- host_int_del_All_Rx_BASession(priv->hWILCWFIDrv,
- wl->vif[0].bssid, TID);
- bEnablePS = false;
+ wilc_optaining_ip = true;
+ mod_timer(&wilc_during_ip_timer,
+ jiffies + msecs_to_jiffies(during_ip_time));
+ wilc_set_power_mgmt(vif, 0, 0);
+ wilc_del_all_rx_ba_session(vif, wl->vif[0]->bssid, TID);
+ wilc_enable_ps = false;
PRINT_D(HOSTAPD_DBG, "Interface type = NL80211_IFTYPE_GO\n");
dev->ieee80211_ptr->iftype = type;
priv->wdev->iftype = type;
@@ -2775,36 +2315,28 @@ static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev,
PRINT_D(HOSTAPD_DBG, "Downloading P2P_CONCURRENCY_FIRMWARE\n");
- nic->iftype = GO_MODE;
+ vif->iftype = GO_MODE;
- /* ensure that the message Q is empty */
- host_int_wait_msg_queue_idle();
+ wilc_wait_msg_queue_idle();
wilc1000_wlan_deinit(dev);
- wilc1000_wlan_init(dev, nic);
- g_wilc_initialized = 1;
-
+ wilc1000_wlan_init(dev, vif);
+ wilc_initialized = 1;
- /*Setting interface 1 drv handler and mac address in newly downloaded FW*/
- host_int_set_wfi_drv_handler(wl->vif[0].hif_drv);
- host_int_set_MacAddress(wl->vif[0].hif_drv,
- wl->vif[0].src_addr);
- host_int_set_operation_mode(priv->hWILCWFIDrv, AP_MODE);
+ wilc_set_wfi_drv_handler(vif, wilc_get_vif_idx(wl->vif[0]));
+ wilc_set_mac_address(wl->vif[0], wl->vif[0]->src_addr);
+ wilc_set_operation_mode(vif, AP_MODE);
- /*Add saved WEP keys, if any*/
if (g_wep_keys_saved) {
- host_int_set_wep_default_key(wl->vif[0].hif_drv,
- g_key_wep_params.key_idx);
- host_int_add_wep_key_bss_sta(wl->vif[0].hif_drv,
- g_key_wep_params.key,
- g_key_wep_params.key_len,
- g_key_wep_params.key_idx);
+ wilc_set_wep_default_keyid(wl->vif[0],
+ g_key_wep_params.key_idx);
+ wilc_add_wep_key_bss_sta(wl->vif[0],
+ g_key_wep_params.key,
+ g_key_wep_params.key_len,
+ g_key_wep_params.key_idx);
}
- /*No matter the driver handler passed here, it will be overwriiten*/
- /*in Handle_FlushConnect() with gu8FlushedJoinReqDrvHandler*/
- host_int_flush_join_req(priv->hWILCWFIDrv);
+ wilc_flush_join_req(vif);
- /*Add saved PTK and GTK keys, if any*/
if (g_ptk_keys_saved && g_gtk_keys_saved) {
PRINT_D(CFG80211_DBG, "ptk %x %x %x cipher %x\n", g_key_ptk_params.key[0],
g_key_ptk_params.key[1],
@@ -2814,15 +2346,15 @@ static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev,
g_key_gtk_params.key[1],
g_key_gtk_params.key[2],
g_key_gtk_params.cipher);
- add_key(wl->vif[0].ndev->ieee80211_ptr->wiphy,
- wl->vif[0].ndev,
+ add_key(wl->vif[0]->ndev->ieee80211_ptr->wiphy,
+ wl->vif[0]->ndev,
g_add_ptk_key_params.key_idx,
g_add_ptk_key_params.pairwise,
g_add_ptk_key_params.mac_addr,
(struct key_params *)(&g_key_ptk_params));
- add_key(wl->vif[0].ndev->ieee80211_ptr->wiphy,
- wl->vif[0].ndev,
+ add_key(wl->vif[0]->ndev->ieee80211_ptr->wiphy,
+ wl->vif[0]->ndev,
g_add_gtk_key_params.key_idx,
g_add_gtk_key_params.pairwise,
g_add_gtk_key_params.mac_addr,
@@ -2831,11 +2363,11 @@ static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev,
if (wl->initialized) {
for (i = 0; i < num_reg_frame; i++) {
- PRINT_D(INIT_DBG, "Frame registering Type: %x - Reg: %d\n", nic->g_struct_frame_reg[i].frame_type,
- nic->g_struct_frame_reg[i].reg);
- host_int_frame_register(priv->hWILCWFIDrv,
- nic->g_struct_frame_reg[i].frame_type,
- nic->g_struct_frame_reg[i].reg);
+ PRINT_D(INIT_DBG, "Frame registering Type: %x - Reg: %d\n", vif->g_struct_frame_reg[i].frame_type,
+ vif->g_struct_frame_reg[i].reg);
+ wilc_frame_register(vif,
+ vif->g_struct_frame_reg[i].frame_type,
+ vif->g_struct_frame_reg[i].reg);
}
}
break;
@@ -2848,32 +2380,6 @@ static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev,
return 0;
}
-/* (austin.2013-07-23)
- *
- * To support revised cfg80211_ops
- *
- * add_beacon --> start_ap
- * set_beacon --> change_beacon
- * del_beacon --> stop_ap
- *
- * beacon_parameters --> cfg80211_ap_settings
- * cfg80211_beacon_data
- *
- * applicable for linux kernel 3.4+
- */
-
-/**
- * @brief start_ap
- * @details Add a beacon with given parameters, @head, @interval
- * and @dtim_period will be valid, @tail is optional.
- * @param[in] wiphy
- * @param[in] dev The net device structure
- * @param[in] settings cfg80211_ap_settings parameters for the beacon to be added
- * @return int : Return 0 on Success.
- * @author austin
- * @date 23 JUL 2013
- * @version 1.0
- */
static int start_ap(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_ap_settings *settings)
{
@@ -2881,11 +2387,11 @@ static int start_ap(struct wiphy *wiphy, struct net_device *dev,
struct wilc_priv *priv;
s32 s32Error = 0;
struct wilc *wl;
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
priv = wiphy_priv(wiphy);
- nic = netdev_priv(dev);
- wl = nic->wilc;
+ vif = netdev_priv(dev);
+ wl = vif ->wilc;
PRINT_D(HOSTAPD_DBG, "Starting ap\n");
PRINT_D(HOSTAPD_DBG, "Interval = %d\n DTIM period = %d\n Head length = %zu Tail length = %zu\n",
@@ -2896,73 +2402,53 @@ static int start_ap(struct wiphy *wiphy, struct net_device *dev,
if (s32Error != 0)
PRINT_ER("Error in setting channel\n");
- linux_wlan_set_bssid(dev, wl->vif[0].src_addr);
+ wilc_wlan_set_bssid(dev, wl->vif[0]->src_addr);
- s32Error = host_int_add_beacon(priv->hWILCWFIDrv,
- settings->beacon_interval,
- settings->dtim_period,
- beacon->head_len, (u8 *)beacon->head,
- beacon->tail_len, (u8 *)beacon->tail);
+ s32Error = wilc_add_beacon(vif, settings->beacon_interval,
+ settings->dtim_period, beacon->head_len,
+ (u8 *)beacon->head, beacon->tail_len,
+ (u8 *)beacon->tail);
return s32Error;
}
-/**
- * @brief change_beacon
- * @details Add a beacon with given parameters, @head, @interval
- * and @dtim_period will be valid, @tail is optional.
- * @param[in] wiphy
- * @param[in] dev The net device structure
- * @param[in] beacon cfg80211_beacon_data for the beacon to be changed
- * @return int : Return 0 on Success.
- * @author austin
- * @date 23 JUL 2013
- * @version 1.0
- */
static int change_beacon(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_beacon_data *beacon)
{
struct wilc_priv *priv;
+ struct wilc_vif *vif;
s32 s32Error = 0;
priv = wiphy_priv(wiphy);
+ vif = netdev_priv(priv->dev);
PRINT_D(HOSTAPD_DBG, "Setting beacon\n");
- s32Error = host_int_add_beacon(priv->hWILCWFIDrv,
- 0,
- 0,
- beacon->head_len, (u8 *)beacon->head,
- beacon->tail_len, (u8 *)beacon->tail);
+ s32Error = wilc_add_beacon(vif, 0, 0, beacon->head_len,
+ (u8 *)beacon->head, beacon->tail_len,
+ (u8 *)beacon->tail);
return s32Error;
}
-/**
- * @brief stop_ap
- * @details Remove beacon configuration and stop sending the beacon.
- * @param[in]
- * @return int : Return 0 on Success.
- * @author austin
- * @date 23 JUL 2013
- * @version 1.0
- */
static int stop_ap(struct wiphy *wiphy, struct net_device *dev)
{
s32 s32Error = 0;
struct wilc_priv *priv;
+ struct wilc_vif *vif;
u8 NullBssid[ETH_ALEN] = {0};
if (!wiphy)
return -EFAULT;
priv = wiphy_priv(wiphy);
+ vif = netdev_priv(priv->dev);
PRINT_D(HOSTAPD_DBG, "Deleting beacon\n");
- linux_wlan_set_bssid(dev, NullBssid);
+ wilc_wlan_set_bssid(dev, NullBssid);
- s32Error = host_int_del_beacon(priv->hWILCWFIDrv);
+ s32Error = wilc_del_beacon(vif);
if (s32Error)
PRINT_ER("Host delete beacon fail\n");
@@ -2970,68 +2456,70 @@ static int stop_ap(struct wiphy *wiphy, struct net_device *dev)
return s32Error;
}
-/**
- * @brief add_station
- * @details Add a new station.
- * @param[in]
- * @return int : Return 0 on Success.
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
static int add_station(struct wiphy *wiphy, struct net_device *dev,
const u8 *mac, struct station_parameters *params)
{
s32 s32Error = 0;
struct wilc_priv *priv;
struct add_sta_param strStaParams = { {0} };
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
if (!wiphy)
return -EFAULT;
priv = wiphy_priv(wiphy);
- nic = netdev_priv(dev);
+ vif = netdev_priv(dev);
- if (nic->iftype == AP_MODE || nic->iftype == GO_MODE) {
- memcpy(strStaParams.au8BSSID, mac, ETH_ALEN);
+ if (vif->iftype == AP_MODE || vif->iftype == GO_MODE) {
+ memcpy(strStaParams.bssid, mac, ETH_ALEN);
memcpy(priv->assoc_stainfo.au8Sta_AssociatedBss[params->aid], mac, ETH_ALEN);
- strStaParams.u16AssocID = params->aid;
- strStaParams.u8NumRates = params->supported_rates_len;
- strStaParams.pu8Rates = params->supported_rates;
+ strStaParams.aid = params->aid;
+ strStaParams.rates_len = params->supported_rates_len;
+ strStaParams.rates = params->supported_rates;
PRINT_D(CFG80211_DBG, "Adding station parameters %d\n", params->aid);
PRINT_D(CFG80211_DBG, "BSSID = %x%x%x%x%x%x\n", priv->assoc_stainfo.au8Sta_AssociatedBss[params->aid][0], priv->assoc_stainfo.au8Sta_AssociatedBss[params->aid][1], priv->assoc_stainfo.au8Sta_AssociatedBss[params->aid][2], priv->assoc_stainfo.au8Sta_AssociatedBss[params->aid][3], priv->assoc_stainfo.au8Sta_AssociatedBss[params->aid][4],
priv->assoc_stainfo.au8Sta_AssociatedBss[params->aid][5]);
- PRINT_D(HOSTAPD_DBG, "ASSOC ID = %d\n", strStaParams.u16AssocID);
- PRINT_D(HOSTAPD_DBG, "Number of supported rates = %d\n", strStaParams.u8NumRates);
+ PRINT_D(HOSTAPD_DBG, "ASSOC ID = %d\n", strStaParams.aid);
+ PRINT_D(HOSTAPD_DBG, "Number of supported rates = %d\n",
+ strStaParams.rates_len);
- if (params->ht_capa == NULL) {
- strStaParams.bIsHTSupported = false;
+ if (!params->ht_capa) {
+ strStaParams.ht_supported = false;
} else {
- strStaParams.bIsHTSupported = true;
- strStaParams.u16HTCapInfo = params->ht_capa->cap_info;
- strStaParams.u8AmpduParams = params->ht_capa->ampdu_params_info;
- memcpy(strStaParams.au8SuppMCsSet, &params->ht_capa->mcs, WILC_SUPP_MCS_SET_SIZE);
- strStaParams.u16HTExtParams = params->ht_capa->extended_ht_cap_info;
- strStaParams.u32TxBeamformingCap = params->ht_capa->tx_BF_cap_info;
- strStaParams.u8ASELCap = params->ht_capa->antenna_selection_info;
+ strStaParams.ht_supported = true;
+ strStaParams.ht_capa_info = params->ht_capa->cap_info;
+ strStaParams.ht_ampdu_params = params->ht_capa->ampdu_params_info;
+ memcpy(strStaParams.ht_supp_mcs_set,
+ &params->ht_capa->mcs,
+ WILC_SUPP_MCS_SET_SIZE);
+ strStaParams.ht_ext_params = params->ht_capa->extended_ht_cap_info;
+ strStaParams.ht_tx_bf_cap = params->ht_capa->tx_BF_cap_info;
+ strStaParams.ht_ante_sel = params->ht_capa->antenna_selection_info;
}
- strStaParams.u16FlagsMask = params->sta_flags_mask;
- strStaParams.u16FlagsSet = params->sta_flags_set;
-
- PRINT_D(HOSTAPD_DBG, "IS HT supported = %d\n", strStaParams.bIsHTSupported);
- PRINT_D(HOSTAPD_DBG, "Capability Info = %d\n", strStaParams.u16HTCapInfo);
- PRINT_D(HOSTAPD_DBG, "AMPDU Params = %d\n", strStaParams.u8AmpduParams);
- PRINT_D(HOSTAPD_DBG, "HT Extended params = %d\n", strStaParams.u16HTExtParams);
- PRINT_D(HOSTAPD_DBG, "Tx Beamforming Cap = %d\n", strStaParams.u32TxBeamformingCap);
- PRINT_D(HOSTAPD_DBG, "Antenna selection info = %d\n", strStaParams.u8ASELCap);
- PRINT_D(HOSTAPD_DBG, "Flag Mask = %d\n", strStaParams.u16FlagsMask);
- PRINT_D(HOSTAPD_DBG, "Flag Set = %d\n", strStaParams.u16FlagsSet);
-
- s32Error = host_int_add_station(priv->hWILCWFIDrv, &strStaParams);
+ strStaParams.flags_mask = params->sta_flags_mask;
+ strStaParams.flags_set = params->sta_flags_set;
+
+ PRINT_D(HOSTAPD_DBG, "IS HT supported = %d\n",
+ strStaParams.ht_supported);
+ PRINT_D(HOSTAPD_DBG, "Capability Info = %d\n",
+ strStaParams.ht_capa_info);
+ PRINT_D(HOSTAPD_DBG, "AMPDU Params = %d\n",
+ strStaParams.ht_ampdu_params);
+ PRINT_D(HOSTAPD_DBG, "HT Extended params = %d\n",
+ strStaParams.ht_ext_params);
+ PRINT_D(HOSTAPD_DBG, "Tx Beamforming Cap = %d\n",
+ strStaParams.ht_tx_bf_cap);
+ PRINT_D(HOSTAPD_DBG, "Antenna selection info = %d\n",
+ strStaParams.ht_ante_sel);
+ PRINT_D(HOSTAPD_DBG, "Flag Mask = %d\n",
+ strStaParams.flags_mask);
+ PRINT_D(HOSTAPD_DBG, "Flag Set = %d\n",
+ strStaParams.flags_set);
+
+ s32Error = wilc_add_station(vif, &strStaParams);
if (s32Error)
PRINT_ER("Host add station fail\n");
}
@@ -3039,41 +2527,33 @@ static int add_station(struct wiphy *wiphy, struct net_device *dev,
return s32Error;
}
-/**
- * @brief del_station
- * @details Remove a station; @mac may be NULL to remove all stations.
- * @param[in]
- * @return int : Return 0 on Success.
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
static int del_station(struct wiphy *wiphy, struct net_device *dev,
struct station_del_parameters *params)
{
const u8 *mac = params->mac;
s32 s32Error = 0;
struct wilc_priv *priv;
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
if (!wiphy)
return -EFAULT;
priv = wiphy_priv(wiphy);
- nic = netdev_priv(dev);
+ vif = netdev_priv(dev);
- if (nic->iftype == AP_MODE || nic->iftype == GO_MODE) {
+ if (vif->iftype == AP_MODE || vif->iftype == GO_MODE) {
PRINT_D(HOSTAPD_DBG, "Deleting station\n");
- if (mac == NULL) {
+ if (!mac) {
PRINT_D(HOSTAPD_DBG, "All associated stations\n");
- s32Error = host_int_del_allstation(priv->hWILCWFIDrv, priv->assoc_stainfo.au8Sta_AssociatedBss);
+ s32Error = wilc_del_allstation(vif,
+ priv->assoc_stainfo.au8Sta_AssociatedBss);
} else {
PRINT_D(HOSTAPD_DBG, "With mac address: %x%x%x%x%x%x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
- s32Error = host_int_del_station(priv->hWILCWFIDrv, mac);
+ s32Error = wilc_del_station(vif, mac);
if (s32Error)
PRINT_ER("Host delete station fail\n");
@@ -3081,22 +2561,13 @@ static int del_station(struct wiphy *wiphy, struct net_device *dev,
return s32Error;
}
-/**
- * @brief change_station
- * @details Modify a given station.
- * @param[in]
- * @return int : Return 0 on Success.
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
static int change_station(struct wiphy *wiphy, struct net_device *dev,
const u8 *mac, struct station_parameters *params)
{
s32 s32Error = 0;
struct wilc_priv *priv;
struct add_sta_param strStaParams = { {0} };
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
PRINT_D(HOSTAPD_DBG, "Change station paramters\n");
@@ -3105,61 +2576,63 @@ static int change_station(struct wiphy *wiphy, struct net_device *dev,
return -EFAULT;
priv = wiphy_priv(wiphy);
- nic = netdev_priv(dev);
-
- if (nic->iftype == AP_MODE || nic->iftype == GO_MODE) {
- memcpy(strStaParams.au8BSSID, mac, ETH_ALEN);
- strStaParams.u16AssocID = params->aid;
- strStaParams.u8NumRates = params->supported_rates_len;
- strStaParams.pu8Rates = params->supported_rates;
-
- PRINT_D(HOSTAPD_DBG, "BSSID = %x%x%x%x%x%x\n", strStaParams.au8BSSID[0], strStaParams.au8BSSID[1], strStaParams.au8BSSID[2], strStaParams.au8BSSID[3], strStaParams.au8BSSID[4],
- strStaParams.au8BSSID[5]);
- PRINT_D(HOSTAPD_DBG, "ASSOC ID = %d\n", strStaParams.u16AssocID);
- PRINT_D(HOSTAPD_DBG, "Number of supported rates = %d\n", strStaParams.u8NumRates);
-
- if (params->ht_capa == NULL) {
- strStaParams.bIsHTSupported = false;
+ vif = netdev_priv(dev);
+
+ if (vif->iftype == AP_MODE || vif->iftype == GO_MODE) {
+ memcpy(strStaParams.bssid, mac, ETH_ALEN);
+ strStaParams.aid = params->aid;
+ strStaParams.rates_len = params->supported_rates_len;
+ strStaParams.rates = params->supported_rates;
+
+ PRINT_D(HOSTAPD_DBG, "BSSID = %x%x%x%x%x%x\n",
+ strStaParams.bssid[0], strStaParams.bssid[1],
+ strStaParams.bssid[2], strStaParams.bssid[3],
+ strStaParams.bssid[4], strStaParams.bssid[5]);
+ PRINT_D(HOSTAPD_DBG, "ASSOC ID = %d\n", strStaParams.aid);
+ PRINT_D(HOSTAPD_DBG, "Number of supported rates = %d\n",
+ strStaParams.rates_len);
+
+ if (!params->ht_capa) {
+ strStaParams.ht_supported = false;
} else {
- strStaParams.bIsHTSupported = true;
- strStaParams.u16HTCapInfo = params->ht_capa->cap_info;
- strStaParams.u8AmpduParams = params->ht_capa->ampdu_params_info;
- memcpy(strStaParams.au8SuppMCsSet, &params->ht_capa->mcs, WILC_SUPP_MCS_SET_SIZE);
- strStaParams.u16HTExtParams = params->ht_capa->extended_ht_cap_info;
- strStaParams.u32TxBeamformingCap = params->ht_capa->tx_BF_cap_info;
- strStaParams.u8ASELCap = params->ht_capa->antenna_selection_info;
-
+ strStaParams.ht_supported = true;
+ strStaParams.ht_capa_info = params->ht_capa->cap_info;
+ strStaParams.ht_ampdu_params = params->ht_capa->ampdu_params_info;
+ memcpy(strStaParams.ht_supp_mcs_set,
+ &params->ht_capa->mcs,
+ WILC_SUPP_MCS_SET_SIZE);
+ strStaParams.ht_ext_params = params->ht_capa->extended_ht_cap_info;
+ strStaParams.ht_tx_bf_cap = params->ht_capa->tx_BF_cap_info;
+ strStaParams.ht_ante_sel = params->ht_capa->antenna_selection_info;
}
- strStaParams.u16FlagsMask = params->sta_flags_mask;
- strStaParams.u16FlagsSet = params->sta_flags_set;
-
- PRINT_D(HOSTAPD_DBG, "IS HT supported = %d\n", strStaParams.bIsHTSupported);
- PRINT_D(HOSTAPD_DBG, "Capability Info = %d\n", strStaParams.u16HTCapInfo);
- PRINT_D(HOSTAPD_DBG, "AMPDU Params = %d\n", strStaParams.u8AmpduParams);
- PRINT_D(HOSTAPD_DBG, "HT Extended params = %d\n", strStaParams.u16HTExtParams);
- PRINT_D(HOSTAPD_DBG, "Tx Beamforming Cap = %d\n", strStaParams.u32TxBeamformingCap);
- PRINT_D(HOSTAPD_DBG, "Antenna selection info = %d\n", strStaParams.u8ASELCap);
- PRINT_D(HOSTAPD_DBG, "Flag Mask = %d\n", strStaParams.u16FlagsMask);
- PRINT_D(HOSTAPD_DBG, "Flag Set = %d\n", strStaParams.u16FlagsSet);
-
- s32Error = host_int_edit_station(priv->hWILCWFIDrv, &strStaParams);
+ strStaParams.flags_mask = params->sta_flags_mask;
+ strStaParams.flags_set = params->sta_flags_set;
+
+ PRINT_D(HOSTAPD_DBG, "IS HT supported = %d\n",
+ strStaParams.ht_supported);
+ PRINT_D(HOSTAPD_DBG, "Capability Info = %d\n",
+ strStaParams.ht_capa_info);
+ PRINT_D(HOSTAPD_DBG, "AMPDU Params = %d\n",
+ strStaParams.ht_ampdu_params);
+ PRINT_D(HOSTAPD_DBG, "HT Extended params = %d\n",
+ strStaParams.ht_ext_params);
+ PRINT_D(HOSTAPD_DBG, "Tx Beamforming Cap = %d\n",
+ strStaParams.ht_tx_bf_cap);
+ PRINT_D(HOSTAPD_DBG, "Antenna selection info = %d\n",
+ strStaParams.ht_ante_sel);
+ PRINT_D(HOSTAPD_DBG, "Flag Mask = %d\n",
+ strStaParams.flags_mask);
+ PRINT_D(HOSTAPD_DBG, "Flag Set = %d\n",
+ strStaParams.flags_set);
+
+ s32Error = wilc_edit_station(vif, &strStaParams);
if (s32Error)
PRINT_ER("Host edit station fail\n");
}
return s32Error;
}
-
-/**
- * @brief add_virtual_intf
- * @details
- * @param[in]
- * @return int : Return 0 on Success.
- * @author mdaftedar
- * @date 01 JUL 2012
- * @version 1.0
- */
static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy,
const char *name,
unsigned char name_assign_type,
@@ -3167,7 +2640,7 @@ static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy,
u32 *flags,
struct vif_params *params)
{
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct wilc_priv *priv;
struct net_device *new_ifc = NULL;
@@ -3177,32 +2650,23 @@ static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy,
PRINT_D(HOSTAPD_DBG, "Adding monitor interface[%p]\n", priv->wdev->netdev);
- nic = netdev_priv(priv->wdev->netdev);
+ vif = netdev_priv(priv->wdev->netdev);
if (type == NL80211_IFTYPE_MONITOR) {
PRINT_D(HOSTAPD_DBG, "Monitor interface mode: Initializing mon interface virtual device driver\n");
- PRINT_D(HOSTAPD_DBG, "Adding monitor interface[%p]\n", nic->wilc_netdev);
- new_ifc = WILC_WFI_init_mon_interface(name, nic->wilc_netdev);
- if (new_ifc != NULL) {
+ PRINT_D(HOSTAPD_DBG, "Adding monitor interface[%p]\n", vif->ndev);
+ new_ifc = WILC_WFI_init_mon_interface(name, vif->ndev);
+ if (new_ifc) {
PRINT_D(HOSTAPD_DBG, "Setting monitor flag in private structure\n");
- nic = netdev_priv(priv->wdev->netdev);
- nic->monitor_flag = 1;
+ vif = netdev_priv(priv->wdev->netdev);
+ vif->monitor_flag = 1;
} else
PRINT_ER("Error in initializing monitor interface\n ");
}
return priv->wdev;
}
-/**
- * @brief del_virtual_intf
- * @details
- * @param[in]
- * @return int : Return 0 on Success.
- * @author mdaftedar
- * @date 01 JUL 2012
- * @version 1.0
- */
static int del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
{
PRINT_D(HOSTAPD_DBG, "Deleting virtual interface\n");
@@ -3210,7 +2674,6 @@ static int del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
}
static struct cfg80211_ops wilc_cfg80211_ops = {
-
.set_monitor_channel = set_channel,
.scan = scan,
.connect = connect,
@@ -3247,27 +2710,12 @@ static struct cfg80211_ops wilc_cfg80211_ops = {
};
-
-
-
-
-/**
- * @brief WILC_WFI_update_stats
- * @details Modify parameters for a given BSS.
- * @param[in]
- * @return int : Return 0 on Success.
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
int WILC_WFI_update_stats(struct wiphy *wiphy, u32 pktlen, u8 changed)
{
-
struct wilc_priv *priv;
priv = wiphy_priv(wiphy);
switch (changed) {
-
case WILC_WFI_RX_PKT:
{
priv->netstats.rx_packets++;
@@ -3291,46 +2739,31 @@ int WILC_WFI_update_stats(struct wiphy *wiphy, u32 pktlen, u8 changed)
return 0;
}
-/**
- * @brief WILC_WFI_CfgAlloc
- * @details Allocation of the wireless device structure and assigning it
- * to the cfg80211 operations structure.
- * @param[in] NONE
- * @return wireless_dev : Returns pointer to wireless_dev structure.
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
-struct wireless_dev *WILC_WFI_CfgAlloc(void)
+static struct wireless_dev *WILC_WFI_CfgAlloc(void)
{
-
struct wireless_dev *wdev;
PRINT_D(CFG80211_DBG, "Allocating wireless device\n");
- /*Allocating the wireless device structure*/
+
wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
if (!wdev) {
PRINT_ER("Cannot allocate wireless device\n");
goto _fail_;
}
- /*Creating a new wiphy, linking wireless structure with the wiphy structure*/
wdev->wiphy = wiphy_new(&wilc_cfg80211_ops, sizeof(struct wilc_priv));
if (!wdev->wiphy) {
PRINT_ER("Cannot allocate wiphy\n");
goto _fail_mem_;
-
}
- /* enable 802.11n HT */
WILC_WFI_band_2ghz.ht_cap.ht_supported = 1;
WILC_WFI_band_2ghz.ht_cap.cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
WILC_WFI_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
WILC_WFI_band_2ghz.ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K;
WILC_WFI_band_2ghz.ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
- /*wiphy bands*/
wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &WILC_WFI_band_2ghz;
return wdev;
@@ -3339,18 +2772,9 @@ _fail_mem_:
kfree(wdev);
_fail_:
return NULL;
-
}
-/**
- * @brief wilc_create_wiphy
- * @details Registering of the wiphy structure and interface modes
- * @param[in] NONE
- * @return NONE
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
-struct wireless_dev *wilc_create_wiphy(struct net_device *net)
+
+struct wireless_dev *wilc_create_wiphy(struct net_device *net, struct device *dev)
{
struct wilc_priv *priv;
struct wireless_dev *wdev;
@@ -3359,38 +2783,25 @@ struct wireless_dev *wilc_create_wiphy(struct net_device *net)
PRINT_D(CFG80211_DBG, "Registering wifi device\n");
wdev = WILC_WFI_CfgAlloc();
- if (wdev == NULL) {
+ if (!wdev) {
PRINT_ER("CfgAlloc Failed\n");
return NULL;
}
-
- /*Return hardware description structure (wiphy)'s priv*/
priv = wdev_priv(wdev);
sema_init(&(priv->SemHandleUpdateStats), 1);
-
- /*Link the wiphy with wireless structure*/
priv->wdev = wdev;
-
- /*Maximum number of probed ssid to be added by user for the scan request*/
wdev->wiphy->max_scan_ssids = MAX_NUM_PROBED_SSID;
- /*Maximum number of pmkids to be cashed*/
wdev->wiphy->max_num_pmkids = WILC_MAX_NUM_PMKIDS;
PRINT_INFO(CFG80211_DBG, "Max number of PMKIDs = %d\n", wdev->wiphy->max_num_pmkids);
wdev->wiphy->max_scan_ie_len = 1000;
-
- /*signal strength in mBm (100*dBm) */
wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
-
- /*Set the availaible cipher suites*/
wdev->wiphy->cipher_suites = cipher_suites;
wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
- /*Setting default managment types: for register action frame: */
wdev->wiphy->mgmt_stypes = wilc_wfi_cfg80211_mgmt_types;
wdev->wiphy->max_remain_on_channel_duration = 500;
- /*Setting the wiphy interfcae mode and type before registering the wiphy*/
wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MONITOR) | BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_P2P_CLIENT);
wdev->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
@@ -3402,36 +2813,21 @@ struct wireless_dev *wilc_create_wiphy(struct net_device *net)
wdev->wiphy->max_scan_ssids, wdev->wiphy->max_scan_ie_len, wdev->wiphy->signal_type,
wdev->wiphy->interface_modes, wdev->iftype);
- #ifdef WILC_SDIO
- set_wiphy_dev(wdev->wiphy, &local_sdio_func->dev);
- #endif
+ set_wiphy_dev(wdev->wiphy, dev);
- /*Register wiphy structure*/
s32Error = wiphy_register(wdev->wiphy);
if (s32Error) {
PRINT_ER("Cannot register wiphy device\n");
- /*should define what action to be taken in such failure*/
} else {
PRINT_D(CFG80211_DBG, "Successful Registering\n");
}
priv->dev = net;
return wdev;
-
-
}
-/**
- * @brief WILC_WFI_WiphyFree
- * @details Freeing allocation of the wireless device structure
- * @param[in] NONE
- * @return NONE
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
+
int wilc_init_host_int(struct net_device *net)
{
-
int s32Error = 0;
struct wilc_priv *priv;
@@ -3440,7 +2836,7 @@ int wilc_init_host_int(struct net_device *net)
priv = wdev_priv(net->ieee80211_ptr);
if (op_ifcs == 0) {
setup_timer(&hAgingTimer, remove_network_from_shadow, 0);
- setup_timer(&hDuringIpTimer, clear_duringIP, 0);
+ setup_timer(&wilc_during_ip_timer, clear_duringIP, 0);
}
op_ifcs++;
if (s32Error < 0) {
@@ -3453,29 +2849,21 @@ int wilc_init_host_int(struct net_device *net)
priv->bInP2PlistenState = false;
sema_init(&(priv->hSemScanReq), 1);
- s32Error = host_int_init(net, &priv->hWILCWFIDrv);
+ s32Error = wilc_init(net, &priv->hWILCWFIDrv);
if (s32Error)
PRINT_ER("Error while initializing hostinterface\n");
return s32Error;
}
-/**
- * @brief WILC_WFI_WiphyFree
- * @details Freeing allocation of the wireless device structure
- * @param[in] NONE
- * @return NONE
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
int wilc_deinit_host_int(struct net_device *net)
{
int s32Error = 0;
-
+ struct wilc_vif *vif;
struct wilc_priv *priv;
priv = wdev_priv(net->ieee80211_ptr);
+ vif = netdev_priv(priv->dev);
priv->gbAutoRateAdjusted = false;
@@ -3483,13 +2871,12 @@ int wilc_deinit_host_int(struct net_device *net)
op_ifcs--;
- s32Error = host_int_deinit(priv->hWILCWFIDrv);
+ s32Error = wilc_deinit(vif);
- /* Clear the Shadow scan */
- clear_shadow_scan(priv);
+ clear_shadow_scan();
if (op_ifcs == 0) {
PRINT_D(CORECONFIG_DBG, "destroy during ip\n");
- del_timer_sync(&hDuringIpTimer);
+ del_timer_sync(&wilc_during_ip_timer);
}
if (s32Error)
@@ -3498,16 +2885,6 @@ int wilc_deinit_host_int(struct net_device *net)
return s32Error;
}
-
-/**
- * @brief WILC_WFI_WiphyFree
- * @details Freeing allocation of the wireless device structure
- * @param[in] NONE
- * @return NONE
- * @author mdaftedar
- * @date 01 MAR 2012
- * @version 1.0
- */
void wilc_free_wiphy(struct net_device *net)
{
PRINT_D(CFG80211_DBG, "Unregistering wiphy\n");
diff --git a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.h b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.h
index 39cd8e1b5675..ab53d9d59081 100644
--- a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.h
+++ b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.h
@@ -10,88 +10,7 @@
#define NM_WFI_CFGOPERATIONS
#include "wilc_wfi_netdevice.h"
-/* The following macros describe the bitfield map used by the firmware to determine its 11i mode */
-#define NO_ENCRYPT 0
-#define ENCRYPT_ENABLED BIT(0)
-#define WEP BIT(1)
-#define WEP_EXTENDED BIT(2)
-#define WPA BIT(3)
-#define WPA2 BIT(4)
-#define AES BIT(5)
-#define TKIP BIT(6)
-
-/*Public action frame index IDs*/
-#define FRAME_TYPE_ID 0
-#define ACTION_CAT_ID 24
-#define ACTION_SUBTYPE_ID 25
-#define P2P_PUB_ACTION_SUBTYPE 30
-
-/*Public action frame Attribute IDs*/
-#define ACTION_FRAME 0xd0
-#define GO_INTENT_ATTR_ID 0x04
-#define CHANLIST_ATTR_ID 0x0b
-#define OPERCHAN_ATTR_ID 0x11
-#define PUB_ACTION_ATTR_ID 0x04
-#define P2PELEM_ATTR_ID 0xdd
-
-/*Public action subtype values*/
-#define GO_NEG_REQ 0x00
-#define GO_NEG_RSP 0x01
-#define GO_NEG_CONF 0x02
-#define P2P_INV_REQ 0x03
-#define P2P_INV_RSP 0x04
-#define PUBLIC_ACT_VENDORSPEC 0x09
-#define GAS_INTIAL_REQ 0x0a
-#define GAS_INTIAL_RSP 0x0b
-
-#define INVALID_CHANNEL 0
-
-#define nl80211_SCAN_RESULT_EXPIRE (3 * HZ)
-#define SCAN_RESULT_EXPIRE (40 * HZ)
-
-static const u32 cipher_suites[] = {
- WLAN_CIPHER_SUITE_WEP40,
- WLAN_CIPHER_SUITE_WEP104,
- WLAN_CIPHER_SUITE_TKIP,
- WLAN_CIPHER_SUITE_CCMP,
- WLAN_CIPHER_SUITE_AES_CMAC,
-};
-
-static const struct ieee80211_txrx_stypes
- wilc_wfi_cfg80211_mgmt_types[NUM_NL80211_IFTYPES] = {
- [NL80211_IFTYPE_STATION] = {
- .tx = 0xffff,
- .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
- BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
- },
- [NL80211_IFTYPE_AP] = {
- .tx = 0xffff,
- .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
- BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
- BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
- BIT(IEEE80211_STYPE_DISASSOC >> 4) |
- BIT(IEEE80211_STYPE_AUTH >> 4) |
- BIT(IEEE80211_STYPE_DEAUTH >> 4) |
- BIT(IEEE80211_STYPE_ACTION >> 4)
- },
- [NL80211_IFTYPE_P2P_CLIENT] = {
- .tx = 0xffff,
- .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
- BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
- BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
- BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
- BIT(IEEE80211_STYPE_DISASSOC >> 4) |
- BIT(IEEE80211_STYPE_AUTH >> 4) |
- BIT(IEEE80211_STYPE_DEAUTH >> 4)
- }
-};
-
-/* Time to stay on the channel */
-#define WILC_WFI_DWELL_PASSIVE 100
-#define WILC_WFI_DWELL_ACTIVE 40
-
-struct wireless_dev *WILC_WFI_CfgAlloc(void);
-struct wireless_dev *wilc_create_wiphy(struct net_device *net);
+struct wireless_dev *wilc_create_wiphy(struct net_device *net, struct device *dev);
void wilc_free_wiphy(struct net_device *net);
int WILC_WFI_update_stats(struct wiphy *wiphy, u32 pktlen, u8 changed);
int wilc_deinit_host_int(struct net_device *net);
@@ -102,8 +21,4 @@ struct net_device *WILC_WFI_init_mon_interface(const char *name, struct net_devi
void wilc_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev,
u16 frame_type, bool reg);
-#define TCP_ACK_FILTER_LINK_SPEED_THRESH 54
-#define DEFAULT_LINK_SPEED 72
-void Enable_TCP_ACK_Filter(bool value);
-
#endif
diff --git a/drivers/staging/wilc1000/wilc_wfi_netdevice.h b/drivers/staging/wilc1000/wilc_wfi_netdevice.h
index 0bfe7626ad2d..98ac8ed04a06 100644
--- a/drivers/staging/wilc1000/wilc_wfi_netdevice.h
+++ b/drivers/staging/wilc1000/wilc_wfi_netdevice.h
@@ -149,6 +149,13 @@ typedef struct {
} struct_frame_reg;
struct wilc_vif {
+ u8 u8IfIdx;
+ u8 iftype;
+ int monitor_flag;
+ int mac_opened;
+ struct_frame_reg g_struct_frame_reg[num_reg_frame];
+ struct net_device_stats netstats;
+ struct wilc *wilc;
u8 src_addr[ETH_ALEN];
u8 bssid[ETH_ALEN];
struct host_if_drv *hif_drv;
@@ -156,14 +163,15 @@ struct wilc_vif {
};
struct wilc {
+ const struct wilc_hif_func *hif_func;
+ int io_type;
int mac_status;
+ int gpio;
bool initialized;
- #if (!defined WILC_SDIO) || (defined WILC_SDIO_IRQ_GPIO)
- unsigned short dev_irq_num;
- #endif
+ int dev_irq_num;
int close;
u8 vif_num;
- struct wilc_vif vif[NUM_CONCURRENT_IFC];
+ struct wilc_vif *vif[NUM_CONCURRENT_IFC];
u8 open_ifcs;
struct semaphore txq_add_to_head_cs;
@@ -180,42 +188,54 @@ struct wilc {
struct task_struct *txq_thread;
+ int quit;
+ int cfg_frame_in_use;
+ struct wilc_cfg_frame cfg_frame;
+ u32 cfg_frame_offset;
+ int cfg_seq_no;
+
+ u8 *rx_buffer;
+ u32 rx_buffer_offset;
+ u8 *tx_buffer;
+
+ unsigned long txq_spinlock_flags;
+
+ struct txq_entry_t *txq_head;
+ struct txq_entry_t *txq_tail;
+ int txq_entries;
+ int txq_exit;
+
+ struct rxq_entry_t *rxq_head;
+ struct rxq_entry_t *rxq_tail;
+ int rxq_entries;
+ int rxq_exit;
+
unsigned char eth_src_address[NUM_CONCURRENT_IFC][6];
const struct firmware *firmware;
-#ifdef WILC_SDIO
- struct sdio_func *wilc_sdio_func;
-#else
- struct spi_device *wilc_spidev;
-#endif
+ struct device *dev;
};
-typedef struct {
- u8 u8IfIdx;
- u8 iftype;
- int monitor_flag;
- int mac_opened;
- struct_frame_reg g_struct_frame_reg[num_reg_frame];
- struct net_device *wilc_netdev;
- struct net_device_stats netstats;
- struct wilc *wilc;
-} perInterface_wlan_t;
-
struct WILC_WFI_mon_priv {
struct net_device *real_ndev;
};
-extern struct wilc *g_linux_wlan;
-extern struct net_device *WILC_WFI_devs[];
-void frmw_to_linux(struct wilc *wilc, u8 *buff, u32 size, u32 pkt_offset);
-void linux_wlan_mac_indicate(struct wilc *wilc, int flag);
-void linux_wlan_rx_complete(void);
-void linux_wlan_dbg(u8 *buff);
-int linux_wlan_lock_timeout(void *vp, u32 timeout);
-void wl_wlan_cleanup(void);
-int wilc_netdev_init(struct wilc **wilc);
+int wilc1000_wlan_init(struct net_device *dev, struct wilc_vif *vif);
+
+void wilc_frmw_to_linux(struct wilc *wilc, u8 *buff, u32 size, u32 pkt_offset);
+void wilc_mac_indicate(struct wilc *wilc, int flag);
+void wilc_rx_complete(struct wilc *wilc);
+void wilc_dbg(u8 *buff);
+
+int wilc_lock_timeout(struct wilc *wilc, void *, u32 timeout);
+void wilc_netdev_cleanup(struct wilc *wilc);
+int wilc_netdev_init(struct wilc **wilc, struct device *, int io_type, int gpio,
+ const struct wilc_hif_func *ops);
void wilc1000_wlan_deinit(struct net_device *dev);
void WILC_WFI_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size);
-u16 Set_machw_change_vir_if(struct net_device *dev, bool bValue);
+u16 wilc_set_machw_change_vir_if(struct net_device *dev, bool value);
+int wilc_wlan_get_firmware(struct net_device *dev);
+int wilc_wlan_set_bssid(struct net_device *wilc_netdev, u8 *bssid);
+
#endif
diff --git a/drivers/staging/wilc1000/wilc_wlan.c b/drivers/staging/wilc1000/wilc_wlan.c
index c02665747705..83af51bb83e8 100644
--- a/drivers/staging/wilc1000/wilc_wlan.c
+++ b/drivers/staging/wilc1000/wilc_wlan.c
@@ -1,95 +1,15 @@
-/* ////////////////////////////////////////////////////////////////////////// */
-/* */
-/* Copyright (c) Atmel Corporation. All rights reserved. */
-/* */
-/* Module Name: wilc_wlan.c */
-/* */
-/* */
-/* //////////////////////////////////////////////////////////////////////////// */
-
#include "wilc_wlan_if.h"
+#include "wilc_wlan.h"
#include "wilc_wfi_netdevice.h"
#include "wilc_wlan_cfg.h"
-/********************************************
- *
- * Global
- *
- ********************************************/
-extern wilc_hif_func_t hif_sdio;
-extern wilc_hif_func_t hif_spi;
-u32 wilc_get_chipid(u8 update);
-
-
-
-typedef struct {
- int quit;
-
- /**
- * input interface functions
- **/
- wilc_wlan_io_func_t io_func;
-
- /**
- * host interface functions
- **/
- wilc_hif_func_t hif_func;
-
- /**
- * configuration interface functions
- **/
- int cfg_frame_in_use;
- wilc_cfg_frame_t cfg_frame;
- u32 cfg_frame_offset;
- int cfg_seq_no;
-
- /**
- * RX buffer
- **/
- #ifdef MEMORY_STATIC
- u8 *rx_buffer;
- u32 rx_buffer_offset;
- #endif
- /**
- * TX buffer
- **/
- u8 *tx_buffer;
- u32 tx_buffer_offset;
-
- /**
- * TX queue
- **/
-
- unsigned long txq_spinlock_flags;
-
- struct txq_entry_t *txq_head;
- struct txq_entry_t *txq_tail;
- int txq_entries;
- int txq_exit;
-
- /**
- * RX queue
- **/
- struct rxq_entry_t *rxq_head;
- struct rxq_entry_t *rxq_tail;
- int rxq_entries;
- int rxq_exit;
-
-
-} wilc_wlan_dev_t;
-
-static wilc_wlan_dev_t g_wlan;
-
-static inline void chip_allow_sleep(void);
-static inline void chip_wakeup(void);
-/********************************************
- *
- * Debug
- *
- ********************************************/
-
+#ifdef WILC_OPTIMIZE_SLEEP_INT
+static inline void chip_allow_sleep(struct wilc *wilc);
+#endif
+static inline void chip_wakeup(struct wilc *wilc);
static u32 dbgflag = N_INIT | N_ERR | N_INTR | N_TXQ | N_RXQ;
+/* FIXME: replace with dev_debug() */
static void wilc_debug(u32 flag, char *fmt, ...)
{
char buf[256];
@@ -100,244 +20,214 @@ static void wilc_debug(u32 flag, char *fmt, ...)
vsprintf(buf, fmt, args);
va_end(args);
- linux_wlan_dbg(buf);
+ wilc_dbg(buf);
}
}
-static CHIP_PS_STATE_T genuChipPSstate = CHIP_WAKEDUP;
+static CHIP_PS_STATE_T chip_ps_state = CHIP_WAKEDUP;
-/*acquire_bus() and release_bus() are made static inline functions*/
-/*as a temporary workaround to fix a problem of receiving*/
-/*unknown interrupt from FW*/
-static inline void acquire_bus(BUS_ACQUIRE_T acquire)
+static inline void acquire_bus(struct wilc *wilc, BUS_ACQUIRE_T acquire)
{
-
- mutex_lock(&g_linux_wlan->hif_cs);
+ mutex_lock(&wilc->hif_cs);
#ifndef WILC_OPTIMIZE_SLEEP_INT
- if (genuChipPSstate != CHIP_WAKEDUP)
+ if (chip_ps_state != CHIP_WAKEDUP)
#endif
{
if (acquire == ACQUIRE_AND_WAKEUP)
- chip_wakeup();
+ chip_wakeup(wilc);
}
-
}
-static inline void release_bus(BUS_RELEASE_T release)
+
+static inline void release_bus(struct wilc *wilc, BUS_RELEASE_T release)
{
#ifdef WILC_OPTIMIZE_SLEEP_INT
if (release == RELEASE_ALLOW_SLEEP)
- chip_allow_sleep();
+ chip_allow_sleep(wilc);
#endif
- mutex_unlock(&g_linux_wlan->hif_cs);
+ mutex_unlock(&wilc->hif_cs);
}
-/********************************************
- *
- * Queue
- *
- ********************************************/
+#ifdef TCP_ACK_FILTER
static void wilc_wlan_txq_remove(struct txq_entry_t *tqe)
{
- wilc_wlan_dev_t *p = &g_wlan;
- if (tqe == p->txq_head) {
-
- p->txq_head = tqe->next;
- if (p->txq_head)
- p->txq_head->prev = NULL;
-
-
- } else if (tqe == p->txq_tail) {
- p->txq_tail = (tqe->prev);
- if (p->txq_tail)
- p->txq_tail->next = NULL;
+ if (tqe == wilc->txq_head) {
+ wilc->txq_head = tqe->next;
+ if (wilc->txq_head)
+ wilc->txq_head->prev = NULL;
+ } else if (tqe == wilc->txq_tail) {
+ wilc->txq_tail = (tqe->prev);
+ if (wilc->txq_tail)
+ wilc->txq_tail->next = NULL;
} else {
tqe->prev->next = tqe->next;
tqe->next->prev = tqe->prev;
}
- p->txq_entries -= 1;
-
+ wilc->txq_entries -= 1;
}
+#endif
-static struct txq_entry_t *wilc_wlan_txq_remove_from_head(void)
+static struct txq_entry_t *
+wilc_wlan_txq_remove_from_head(struct net_device *dev)
{
struct txq_entry_t *tqe;
- wilc_wlan_dev_t *p = &g_wlan;
unsigned long flags;
+ struct wilc_vif *vif;
+ struct wilc *wilc;
- spin_lock_irqsave(&g_linux_wlan->txq_spinlock, flags);
- if (p->txq_head) {
- tqe = p->txq_head;
- p->txq_head = tqe->next;
- if (p->txq_head)
- p->txq_head->prev = NULL;
-
- p->txq_entries -= 1;
-
-
+ vif = netdev_priv(dev);
+ wilc = vif->wilc;
+ spin_lock_irqsave(&wilc->txq_spinlock, flags);
+ if (wilc->txq_head) {
+ tqe = wilc->txq_head;
+ wilc->txq_head = tqe->next;
+ if (wilc->txq_head)
+ wilc->txq_head->prev = NULL;
+ wilc->txq_entries -= 1;
} else {
tqe = NULL;
}
- spin_unlock_irqrestore(&g_linux_wlan->txq_spinlock, flags);
+ spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
return tqe;
}
-static void wilc_wlan_txq_add_to_tail(struct txq_entry_t *tqe)
+static void wilc_wlan_txq_add_to_tail(struct net_device *dev,
+ struct txq_entry_t *tqe)
{
- wilc_wlan_dev_t *p = &g_wlan;
unsigned long flags;
- spin_lock_irqsave(&g_linux_wlan->txq_spinlock, flags);
+ struct wilc_vif *vif;
+ struct wilc *wilc;
+
+ vif = netdev_priv(dev);
+ wilc = vif->wilc;
- if (p->txq_head == NULL) {
+ spin_lock_irqsave(&wilc->txq_spinlock, flags);
+
+ if (!wilc->txq_head) {
tqe->next = NULL;
tqe->prev = NULL;
- p->txq_head = tqe;
- p->txq_tail = tqe;
+ wilc->txq_head = tqe;
+ wilc->txq_tail = tqe;
} else {
tqe->next = NULL;
- tqe->prev = p->txq_tail;
- p->txq_tail->next = tqe;
- p->txq_tail = tqe;
+ tqe->prev = wilc->txq_tail;
+ wilc->txq_tail->next = tqe;
+ wilc->txq_tail = tqe;
}
- p->txq_entries += 1;
- PRINT_D(TX_DBG, "Number of entries in TxQ = %d\n", p->txq_entries);
+ wilc->txq_entries += 1;
+ PRINT_D(TX_DBG, "Number of entries in TxQ = %d\n", wilc->txq_entries);
- spin_unlock_irqrestore(&g_linux_wlan->txq_spinlock, flags);
+ spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
- /**
- * wake up TX queue
- **/
PRINT_D(TX_DBG, "Wake the txq_handling\n");
- up(&g_linux_wlan->txq_event);
+ up(&wilc->txq_event);
}
-static int wilc_wlan_txq_add_to_head(struct txq_entry_t *tqe)
+static int wilc_wlan_txq_add_to_head(struct wilc *wilc, struct txq_entry_t *tqe)
{
- wilc_wlan_dev_t *p = &g_wlan;
unsigned long flags;
- if (linux_wlan_lock_timeout(&g_linux_wlan->txq_add_to_head_cs,
+ if (wilc_lock_timeout(wilc, &wilc->txq_add_to_head_cs,
CFG_PKTS_TIMEOUT))
return -1;
- spin_lock_irqsave(&g_linux_wlan->txq_spinlock, flags);
+ spin_lock_irqsave(&wilc->txq_spinlock, flags);
- if (p->txq_head == NULL) {
+ if (!wilc->txq_head) {
tqe->next = NULL;
tqe->prev = NULL;
- p->txq_head = tqe;
- p->txq_tail = tqe;
+ wilc->txq_head = tqe;
+ wilc->txq_tail = tqe;
} else {
- tqe->next = p->txq_head;
+ tqe->next = wilc->txq_head;
tqe->prev = NULL;
- p->txq_head->prev = tqe;
- p->txq_head = tqe;
+ wilc->txq_head->prev = tqe;
+ wilc->txq_head = tqe;
}
- p->txq_entries += 1;
- PRINT_D(TX_DBG, "Number of entries in TxQ = %d\n", p->txq_entries);
+ wilc->txq_entries += 1;
+ PRINT_D(TX_DBG, "Number of entries in TxQ = %d\n", wilc->txq_entries);
- spin_unlock_irqrestore(&g_linux_wlan->txq_spinlock, flags);
- up(&g_linux_wlan->txq_add_to_head_cs);
-
-
- /**
- * wake up TX queue
- **/
- up(&g_linux_wlan->txq_event);
+ spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
+ up(&wilc->txq_add_to_head_cs);
+ up(&wilc->txq_event);
PRINT_D(TX_DBG, "Wake up the txq_handler\n");
return 0;
-
}
-u32 Statisitcs_totalAcks = 0, Statisitcs_DroppedAcks = 0;
-
#ifdef TCP_ACK_FILTER
-struct Ack_session_info;
-struct Ack_session_info {
- u32 Ack_seq_num;
- u32 Bigger_Ack_num;
+struct ack_session_info;
+struct ack_session_info {
+ u32 seq_num;
+ u32 bigger_ack_num;
u16 src_port;
u16 dst_port;
u16 status;
};
-typedef struct {
+struct pending_acks_info {
u32 ack_num;
- u32 Session_index;
+ u32 session_index;
struct txq_entry_t *txqe;
-} Pending_Acks_info_t /*Ack_info_t*/;
-
-
-
+};
-struct Ack_session_info *Free_head;
-struct Ack_session_info *Alloc_head;
#define NOT_TCP_ACK (-1)
#define MAX_TCP_SESSION 25
#define MAX_PENDING_ACKS 256
-struct Ack_session_info Acks_keep_track_info[2 * MAX_TCP_SESSION];
-Pending_Acks_info_t Pending_Acks_info[MAX_PENDING_ACKS];
-
-u32 PendingAcks_arrBase;
-u32 Opened_TCP_session;
-u32 Pending_Acks;
-
+static struct ack_session_info ack_session_info[2 * MAX_TCP_SESSION];
+static struct pending_acks_info pending_acks_info[MAX_PENDING_ACKS];
+static u32 pending_base;
+static u32 tcp_session;
+static u32 pending_acks;
-static inline int Init_TCP_tracking(void)
+static inline int init_tcp_tracking(void)
{
-
return 0;
-
}
-static inline int add_TCP_track_session(u32 src_prt, u32 dst_prt, u32 seq)
+
+static inline int add_tcp_session(u32 src_prt, u32 dst_prt, u32 seq)
{
- Acks_keep_track_info[Opened_TCP_session].Ack_seq_num = seq;
- Acks_keep_track_info[Opened_TCP_session].Bigger_Ack_num = 0;
- Acks_keep_track_info[Opened_TCP_session].src_port = src_prt;
- Acks_keep_track_info[Opened_TCP_session].dst_port = dst_prt;
- Opened_TCP_session++;
+ ack_session_info[tcp_session].seq_num = seq;
+ ack_session_info[tcp_session].bigger_ack_num = 0;
+ ack_session_info[tcp_session].src_port = src_prt;
+ ack_session_info[tcp_session].dst_port = dst_prt;
+ tcp_session++;
- PRINT_D(TCP_ENH, "TCP Session %d to Ack %d\n", Opened_TCP_session, seq);
+ PRINT_D(TCP_ENH, "TCP Session %d to Ack %d\n", tcp_session, seq);
return 0;
}
-static inline int Update_TCP_track_session(u32 index, u32 Ack)
+static inline int update_tcp_session(u32 index, u32 ack)
{
-
- if (Ack > Acks_keep_track_info[index].Bigger_Ack_num)
- Acks_keep_track_info[index].Bigger_Ack_num = Ack;
+ if (ack > ack_session_info[index].bigger_ack_num)
+ ack_session_info[index].bigger_ack_num = ack;
return 0;
-
}
-static inline int add_TCP_Pending_Ack(u32 Ack, u32 Session_index, struct txq_entry_t *txqe)
-{
- Statisitcs_totalAcks++;
- if (Pending_Acks < MAX_PENDING_ACKS) {
- Pending_Acks_info[PendingAcks_arrBase + Pending_Acks].ack_num = Ack;
- Pending_Acks_info[PendingAcks_arrBase + Pending_Acks].txqe = txqe;
- Pending_Acks_info[PendingAcks_arrBase + Pending_Acks].Session_index = Session_index;
- txqe->tcp_PendingAck_index = PendingAcks_arrBase + Pending_Acks;
- Pending_Acks++;
-
- } else {
+static inline int add_tcp_pending_ack(u32 ack, u32 session_index,
+ struct txq_entry_t *txqe)
+{
+ if (pending_acks < MAX_PENDING_ACKS) {
+ pending_acks_info[pending_base + pending_acks].ack_num = ack;
+ pending_acks_info[pending_base + pending_acks].txqe = txqe;
+ pending_acks_info[pending_base + pending_acks].session_index = session_index;
+ txqe->tcp_pending_ack_idx = pending_base + pending_acks;
+ pending_acks++;
}
return 0;
}
-static inline int remove_TCP_related(void)
+static inline int remove_TCP_related(struct wilc *wilc)
{
- wilc_wlan_dev_t *p = &g_wlan;
unsigned long flags;
- spin_lock_irqsave(&g_linux_wlan->txq_spinlock, flags);
+ spin_lock_irqsave(&wilc->txq_spinlock, flags);
- spin_unlock_irqrestore(&g_linux_wlan->txq_spinlock, flags);
+ spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
return 0;
}
@@ -348,54 +238,55 @@ static inline int tcp_process(struct net_device *dev, struct txq_entry_t *tqe)
u8 *buffer = tqe->buffer;
unsigned short h_proto;
int i;
- wilc_wlan_dev_t *p = &g_wlan;
unsigned long flags;
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct wilc *wilc;
- nic = netdev_priv(dev);
- wilc = nic->wilc;
+ vif = netdev_priv(dev);
+ wilc = vif->wilc;
- spin_lock_irqsave(&wilc->txq_spinlock, flags);
eth_hdr_ptr = &buffer[0];
h_proto = ntohs(*((unsigned short *)&eth_hdr_ptr[12]));
- if (h_proto == 0x0800) { /* IP */
+ if (h_proto == 0x0800) {
u8 *ip_hdr_ptr;
u8 protocol;
ip_hdr_ptr = &buffer[ETHERNET_HDR_LEN];
protocol = ip_hdr_ptr[9];
-
if (protocol == 0x06) {
u8 *tcp_hdr_ptr;
- u32 IHL, Total_Length, Data_offset;
+ u32 IHL, total_length, data_offset;
tcp_hdr_ptr = &ip_hdr_ptr[IP_HDR_LEN];
IHL = (ip_hdr_ptr[0] & 0xf) << 2;
- Total_Length = (((u32)ip_hdr_ptr[2]) << 8) + ((u32)ip_hdr_ptr[3]);
- Data_offset = (((u32)tcp_hdr_ptr[12] & 0xf0) >> 2);
- if (Total_Length == (IHL + Data_offset)) { /*we want to recognize the clear Acks(packet only carry Ack infos not with data) so data size must be equal zero*/
- u32 seq_no, Ack_no;
-
- seq_no = (((u32)tcp_hdr_ptr[4]) << 24) + (((u32)tcp_hdr_ptr[5]) << 16) + (((u32)tcp_hdr_ptr[6]) << 8) + ((u32)tcp_hdr_ptr[7]);
-
- Ack_no = (((u32)tcp_hdr_ptr[8]) << 24) + (((u32)tcp_hdr_ptr[9]) << 16) + (((u32)tcp_hdr_ptr[10]) << 8) + ((u32)tcp_hdr_ptr[11]);
-
-
- for (i = 0; i < Opened_TCP_session; i++) {
- if (Acks_keep_track_info[i].Ack_seq_num == seq_no) {
- Update_TCP_track_session(i, Ack_no);
+ total_length = ((u32)ip_hdr_ptr[2] << 8) +
+ (u32)ip_hdr_ptr[3];
+ data_offset = ((u32)tcp_hdr_ptr[12] & 0xf0) >> 2;
+ if (total_length == (IHL + data_offset)) {
+ u32 seq_no, ack_no;
+
+ seq_no = ((u32)tcp_hdr_ptr[4] << 24) +
+ ((u32)tcp_hdr_ptr[5] << 16) +
+ ((u32)tcp_hdr_ptr[6] << 8) +
+ (u32)tcp_hdr_ptr[7];
+
+ ack_no = ((u32)tcp_hdr_ptr[8] << 24) +
+ ((u32)tcp_hdr_ptr[9] << 16) +
+ ((u32)tcp_hdr_ptr[10] << 8) +
+ (u32)tcp_hdr_ptr[11];
+
+ for (i = 0; i < tcp_session; i++) {
+ if (ack_session_info[i].seq_num == seq_no) {
+ update_tcp_session(i, ack_no);
break;
}
}
- if (i == Opened_TCP_session)
- add_TCP_track_session(0, 0, seq_no);
-
- add_TCP_Pending_Ack(Ack_no, i, tqe);
-
+ if (i == tcp_session)
+ add_tcp_session(0, 0, seq_no);
+ add_tcp_pending_ack(ack_no, i, tqe);
}
} else {
@@ -408,83 +299,81 @@ static inline int tcp_process(struct net_device *dev, struct txq_entry_t *tqe)
return ret;
}
-
static int wilc_wlan_txq_filter_dup_tcp_ack(struct net_device *dev)
{
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct wilc *wilc;
u32 i = 0;
- u32 Dropped = 0;
- wilc_wlan_dev_t *p = &g_wlan;
+ u32 dropped = 0;
- nic = netdev_priv(dev);
- wilc = nic->wilc;
+ vif = netdev_priv(dev);
+ wilc = vif->wilc;
- spin_lock_irqsave(&wilc->txq_spinlock, p->txq_spinlock_flags);
- for (i = PendingAcks_arrBase; i < (PendingAcks_arrBase + Pending_Acks); i++) {
- if (Pending_Acks_info[i].ack_num < Acks_keep_track_info[Pending_Acks_info[i].Session_index].Bigger_Ack_num) {
+ spin_lock_irqsave(&wilc->txq_spinlock, wilc->txq_spinlock_flags);
+ for (i = pending_base; i < (pending_base + pending_acks); i++) {
+ if (pending_acks_info[i].ack_num < ack_session_info[pending_acks_info[i].session_index].bigger_ack_num) {
struct txq_entry_t *tqe;
- PRINT_D(TCP_ENH, "DROP ACK: %u\n", Pending_Acks_info[i].ack_num);
- tqe = Pending_Acks_info[i].txqe;
+ PRINT_D(TCP_ENH, "DROP ACK: %u\n",
+ pending_acks_info[i].ack_num);
+ tqe = pending_acks_info[i].txqe;
if (tqe) {
wilc_wlan_txq_remove(tqe);
- Statisitcs_DroppedAcks++;
- tqe->status = 1; /* mark the packet send */
+ tqe->status = 1;
if (tqe->tx_complete_func)
- tqe->tx_complete_func(tqe->priv, tqe->status);
+ tqe->tx_complete_func(tqe->priv,
+ tqe->status);
kfree(tqe);
- Dropped++;
+ dropped++;
}
}
}
- Pending_Acks = 0;
- Opened_TCP_session = 0;
+ pending_acks = 0;
+ tcp_session = 0;
- if (PendingAcks_arrBase == 0)
- PendingAcks_arrBase = MAX_TCP_SESSION;
+ if (pending_base == 0)
+ pending_base = MAX_TCP_SESSION;
else
- PendingAcks_arrBase = 0;
-
+ pending_base = 0;
- spin_unlock_irqrestore(&wilc->txq_spinlock, p->txq_spinlock_flags);
+ spin_unlock_irqrestore(&wilc->txq_spinlock, wilc->txq_spinlock_flags);
- while (Dropped > 0) {
- /*consume the semaphore count of the removed packet*/
- linux_wlan_lock_timeout(&wilc->txq_event, 1);
- Dropped--;
+ while (dropped > 0) {
+ wilc_lock_timeout(wilc, &wilc->txq_event, 1);
+ dropped--;
}
return 1;
}
#endif
-bool EnableTCPAckFilter = false;
+static bool enabled = false;
-void Enable_TCP_ACK_Filter(bool value)
+void wilc_enable_tcp_ack_filter(bool value)
{
- EnableTCPAckFilter = value;
+ enabled = value;
}
-bool is_TCP_ACK_Filter_Enabled(void)
+#ifdef TCP_ACK_FILTER
+static bool is_tcp_ack_filter_enabled(void)
{
- return EnableTCPAckFilter;
+ return enabled;
}
+#endif
-static int wilc_wlan_txq_add_cfg_pkt(u8 *buffer, u32 buffer_size)
+static int wilc_wlan_txq_add_cfg_pkt(struct wilc *wilc, u8 *buffer, u32 buffer_size)
{
- wilc_wlan_dev_t *p = &g_wlan;
struct txq_entry_t *tqe;
PRINT_D(TX_DBG, "Adding config packet ...\n");
- if (p->quit) {
+ if (wilc->quit) {
PRINT_D(TX_DBG, "Return due to clear function\n");
- up(&g_linux_wlan->cfg_event);
+ up(&wilc->cfg_event);
return 0;
}
- tqe = kmalloc(sizeof(struct txq_entry_t), GFP_ATOMIC);
- if (tqe == NULL) {
+ tqe = kmalloc(sizeof(*tqe), GFP_ATOMIC);
+ if (!tqe) {
PRINT_ER("Failed to allocate memory\n");
return 0;
}
@@ -495,14 +384,11 @@ static int wilc_wlan_txq_add_cfg_pkt(u8 *buffer, u32 buffer_size)
tqe->tx_complete_func = NULL;
tqe->priv = NULL;
#ifdef TCP_ACK_FILTER
- tqe->tcp_PendingAck_index = NOT_TCP_ACK;
+ tqe->tcp_pending_ack_idx = NOT_TCP_ACK;
#endif
- /**
- * Configuration packet always at the front
- **/
PRINT_D(TX_DBG, "Adding the config packet at the Queue tail\n");
- if (wilc_wlan_txq_add_to_head(tqe))
+ if (wilc_wlan_txq_add_to_head(wilc, tqe))
return 0;
return 1;
}
@@ -510,15 +396,18 @@ static int wilc_wlan_txq_add_cfg_pkt(u8 *buffer, u32 buffer_size)
int wilc_wlan_txq_add_net_pkt(struct net_device *dev, void *priv, u8 *buffer,
u32 buffer_size, wilc_tx_complete_func_t func)
{
- wilc_wlan_dev_t *p = &g_wlan;
struct txq_entry_t *tqe;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc;
+
+ wilc = vif->wilc;
- if (p->quit)
+ if (wilc->quit)
return 0;
- tqe = kmalloc(sizeof(struct txq_entry_t), GFP_ATOMIC);
+ tqe = kmalloc(sizeof(*tqe), GFP_ATOMIC);
- if (tqe == NULL)
+ if (!tqe)
return 0;
tqe->type = WILC_NET_PKT;
tqe->buffer = buffer;
@@ -528,27 +417,29 @@ int wilc_wlan_txq_add_net_pkt(struct net_device *dev, void *priv, u8 *buffer,
PRINT_D(TX_DBG, "Adding mgmt packet at the Queue tail\n");
#ifdef TCP_ACK_FILTER
- tqe->tcp_PendingAck_index = NOT_TCP_ACK;
- if (is_TCP_ACK_Filter_Enabled())
+ tqe->tcp_pending_ack_idx = NOT_TCP_ACK;
+ if (is_tcp_ack_filter_enabled())
tcp_process(dev, tqe);
#endif
- wilc_wlan_txq_add_to_tail(tqe);
- /*return number of itemes in the queue*/
- return p->txq_entries;
+ wilc_wlan_txq_add_to_tail(dev, tqe);
+ return wilc->txq_entries;
}
-int wilc_wlan_txq_add_mgmt_pkt(void *priv, u8 *buffer, u32 buffer_size, wilc_tx_complete_func_t func)
+int wilc_wlan_txq_add_mgmt_pkt(struct net_device *dev, void *priv, u8 *buffer,
+ u32 buffer_size, wilc_tx_complete_func_t func)
{
-
- wilc_wlan_dev_t *p = &g_wlan;
struct txq_entry_t *tqe;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc;
+
+ wilc = vif->wilc;
- if (p->quit)
+ if (wilc->quit)
return 0;
- tqe = kmalloc(sizeof(struct txq_entry_t), GFP_KERNEL);
+ tqe = kmalloc(sizeof(*tqe), GFP_KERNEL);
- if (tqe == NULL)
+ if (!tqe)
return 0;
tqe->type = WILC_MGMT_PKT;
tqe->buffer = buffer;
@@ -556,25 +447,23 @@ int wilc_wlan_txq_add_mgmt_pkt(void *priv, u8 *buffer, u32 buffer_size, wilc_tx_
tqe->tx_complete_func = func;
tqe->priv = priv;
#ifdef TCP_ACK_FILTER
- tqe->tcp_PendingAck_index = NOT_TCP_ACK;
+ tqe->tcp_pending_ack_idx = NOT_TCP_ACK;
#endif
PRINT_D(TX_DBG, "Adding Network packet at the Queue tail\n");
- wilc_wlan_txq_add_to_tail(tqe);
+ wilc_wlan_txq_add_to_tail(dev, tqe);
return 1;
}
-static struct txq_entry_t *wilc_wlan_txq_get_first(void)
+static struct txq_entry_t *wilc_wlan_txq_get_first(struct wilc *wilc)
{
- wilc_wlan_dev_t *p = &g_wlan;
struct txq_entry_t *tqe;
unsigned long flags;
- spin_lock_irqsave(&g_linux_wlan->txq_spinlock, flags);
-
- tqe = p->txq_head;
+ spin_lock_irqsave(&wilc->txq_spinlock, flags);
- spin_unlock_irqrestore(&g_linux_wlan->txq_spinlock, flags);
+ tqe = wilc->txq_head;
+ spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
return tqe;
}
@@ -583,52 +472,50 @@ static struct txq_entry_t *wilc_wlan_txq_get_next(struct wilc *wilc,
struct txq_entry_t *tqe)
{
unsigned long flags;
+
spin_lock_irqsave(&wilc->txq_spinlock, flags);
tqe = tqe->next;
spin_unlock_irqrestore(&wilc->txq_spinlock, flags);
-
return tqe;
}
static int wilc_wlan_rxq_add(struct wilc *wilc, struct rxq_entry_t *rqe)
{
- wilc_wlan_dev_t *p = &g_wlan;
- if (p->quit)
+ if (wilc->quit)
return 0;
mutex_lock(&wilc->rxq_cs);
- if (p->rxq_head == NULL) {
+ if (!wilc->rxq_head) {
PRINT_D(RX_DBG, "Add to Queue head\n");
rqe->next = NULL;
- p->rxq_head = rqe;
- p->rxq_tail = rqe;
+ wilc->rxq_head = rqe;
+ wilc->rxq_tail = rqe;
} else {
PRINT_D(RX_DBG, "Add to Queue tail\n");
- p->rxq_tail->next = rqe;
+ wilc->rxq_tail->next = rqe;
rqe->next = NULL;
- p->rxq_tail = rqe;
+ wilc->rxq_tail = rqe;
}
- p->rxq_entries += 1;
- PRINT_D(RX_DBG, "Number of queue entries: %d\n", p->rxq_entries);
+ wilc->rxq_entries += 1;
+ PRINT_D(RX_DBG, "Number of queue entries: %d\n", wilc->rxq_entries);
mutex_unlock(&wilc->rxq_cs);
- return p->rxq_entries;
+ return wilc->rxq_entries;
}
static struct rxq_entry_t *wilc_wlan_rxq_remove(struct wilc *wilc)
{
- wilc_wlan_dev_t *p = &g_wlan;
PRINT_D(RX_DBG, "Getting rxQ element\n");
- if (p->rxq_head) {
+ if (wilc->rxq_head) {
struct rxq_entry_t *rqe;
mutex_lock(&wilc->rxq_cs);
- rqe = p->rxq_head;
- p->rxq_head = p->rxq_head->next;
- p->rxq_entries -= 1;
+ rqe = wilc->rxq_head;
+ wilc->rxq_head = wilc->rxq_head->next;
+ wilc->rxq_entries -= 1;
PRINT_D(RX_DBG, "RXQ entries decreased\n");
mutex_unlock(&wilc->rxq_cs);
return rqe;
@@ -637,197 +524,151 @@ static struct rxq_entry_t *wilc_wlan_rxq_remove(struct wilc *wilc)
return NULL;
}
-
-/********************************************
- *
- * Power Save handle functions
- *
- ********************************************/
-
-
-
#ifdef WILC_OPTIMIZE_SLEEP_INT
-static inline void chip_allow_sleep(void)
+static inline void chip_allow_sleep(struct wilc *wilc)
{
u32 reg = 0;
- /* Clear bit 1 */
- g_wlan.hif_func.hif_read_reg(0xf0, &reg);
+ wilc->hif_func->hif_read_reg(wilc, 0xf0, &reg);
- g_wlan.hif_func.hif_write_reg(0xf0, reg & ~BIT(0));
+ wilc->hif_func->hif_write_reg(wilc, 0xf0, reg & ~BIT(0));
}
-static inline void chip_wakeup(void)
+static inline void chip_wakeup(struct wilc *wilc)
{
u32 reg, clk_status_reg, trials = 0;
u32 sleep_time;
- if ((g_wlan.io_func.io_type & 0x1) == HIF_SPI) {
+ if ((wilc->io_type & 0x1) == HIF_SPI) {
do {
- g_wlan.hif_func.hif_read_reg(1, &reg);
- /* Set bit 1 */
- g_wlan.hif_func.hif_write_reg(1, reg | BIT(1));
-
- /* Clear bit 1*/
- g_wlan.hif_func.hif_write_reg(1, reg & ~BIT(1));
+ wilc->hif_func->hif_read_reg(wilc, 1, &reg);
+ wilc->hif_func->hif_write_reg(wilc, 1, reg | BIT(1));
+ wilc->hif_func->hif_write_reg(wilc, 1, reg & ~BIT(1));
do {
- /* Wait for the chip to stabilize*/
usleep_range(2 * 1000, 2 * 1000);
- /* Make sure chip is awake. This is an extra step that can be removed */
- /* later to avoid the bus access overhead */
- if ((wilc_get_chipid(true) == 0))
+ if ((wilc_get_chipid(wilc, true) == 0))
wilc_debug(N_ERR, "Couldn't read chip id. Wake up failed\n");
- } while ((wilc_get_chipid(true) == 0) && ((++trials % 3) == 0));
+ } while ((wilc_get_chipid(wilc, true) == 0) && ((++trials % 3) == 0));
- } while (wilc_get_chipid(true) == 0);
- } else if ((g_wlan.io_func.io_type & 0x1) == HIF_SDIO) {
- g_wlan.hif_func.hif_read_reg(0xf0, &reg);
+ } while (wilc_get_chipid(wilc, true) == 0);
+ } else if ((wilc->io_type & 0x1) == HIF_SDIO) {
+ wilc->hif_func->hif_read_reg(wilc, 0xf0, &reg);
do {
- /* Set bit 1 */
- g_wlan.hif_func.hif_write_reg(0xf0, reg | BIT(0));
+ wilc->hif_func->hif_write_reg(wilc, 0xf0,
+ reg | BIT(0));
+ wilc->hif_func->hif_read_reg(wilc, 0xf1,
+ &clk_status_reg);
- /* Check the clock status */
- g_wlan.hif_func.hif_read_reg(0xf1, &clk_status_reg);
-
- /* in case of clocks off, wait 2ms, and check it again. */
- /* if still off, wait for another 2ms, for a total wait of 6ms. */
- /* If still off, redo the wake up sequence */
while (((clk_status_reg & 0x1) == 0) && (((++trials) % 3) == 0)) {
- /* Wait for the chip to stabilize*/
usleep_range(2 * 1000, 2 * 1000);
- /* Make sure chip is awake. This is an extra step that can be removed */
- /* later to avoid the bus access overhead */
- g_wlan.hif_func.hif_read_reg(0xf1, &clk_status_reg);
+ wilc->hif_func->hif_read_reg(wilc, 0xf1,
+ &clk_status_reg);
if ((clk_status_reg & 0x1) == 0)
wilc_debug(N_ERR, "clocks still OFF. Wake up failed\n");
-
}
- /* in case of failure, Reset the wakeup bit to introduce a new edge on the next loop */
if ((clk_status_reg & 0x1) == 0) {
- /* Reset bit 0 */
- g_wlan.hif_func.hif_write_reg(0xf0, reg &
- (~BIT(0)));
+ wilc->hif_func->hif_write_reg(wilc, 0xf0,
+ reg & (~BIT(0)));
}
} while ((clk_status_reg & 0x1) == 0);
}
-
- if (genuChipPSstate == CHIP_SLEEPING_MANUAL) {
- g_wlan.hif_func.hif_read_reg(0x1C0C, &reg);
+ if (chip_ps_state == CHIP_SLEEPING_MANUAL) {
+ wilc->hif_func->hif_read_reg(wilc, 0x1C0C, &reg);
reg &= ~BIT(0);
- g_wlan.hif_func.hif_write_reg(0x1C0C, reg);
+ wilc->hif_func->hif_write_reg(wilc, 0x1C0C, reg);
- if (wilc_get_chipid(false) >= 0x1002b0) {
- /* Enable PALDO back right after wakeup */
+ if (wilc_get_chipid(wilc, false) >= 0x1002b0) {
u32 val32;
- g_wlan.hif_func.hif_read_reg(0x1e1c, &val32);
+ wilc->hif_func->hif_read_reg(wilc, 0x1e1c, &val32);
val32 |= BIT(6);
- g_wlan.hif_func.hif_write_reg(0x1e1c, val32);
+ wilc->hif_func->hif_write_reg(wilc, 0x1e1c, val32);
- g_wlan.hif_func.hif_read_reg(0x1e9c, &val32);
+ wilc->hif_func->hif_read_reg(wilc, 0x1e9c, &val32);
val32 |= BIT(6);
- g_wlan.hif_func.hif_write_reg(0x1e9c, val32);
+ wilc->hif_func->hif_write_reg(wilc, 0x1e9c, val32);
}
}
- genuChipPSstate = CHIP_WAKEDUP;
+ chip_ps_state = CHIP_WAKEDUP;
}
#else
-static inline void chip_wakeup(void)
+static inline void chip_wakeup(struct wilc *wilc)
{
u32 reg, trials = 0;
do {
- if ((g_wlan.io_func.io_type & 0x1) == HIF_SPI) {
- g_wlan.hif_func.hif_read_reg(1, &reg);
- /* Make sure bit 1 is 0 before we start. */
- g_wlan.hif_func.hif_write_reg(1, reg & ~BIT(1));
- /* Set bit 1 */
- g_wlan.hif_func.hif_write_reg(1, reg | BIT(1));
- /* Clear bit 1*/
- g_wlan.hif_func.hif_write_reg(1, reg & ~BIT(1));
- } else if ((g_wlan.io_func.io_type & 0x1) == HIF_SDIO) {
- /* Make sure bit 0 is 0 before we start. */
- g_wlan.hif_func.hif_read_reg(0xf0, &reg);
- g_wlan.hif_func.hif_write_reg(0xf0, reg & ~BIT(0));
- /* Set bit 1 */
- g_wlan.hif_func.hif_write_reg(0xf0, reg | BIT(0));
- /* Clear bit 1 */
- g_wlan.hif_func.hif_write_reg(0xf0, reg & ~BIT(0));
+ if ((wilc->io_type & 0x1) == HIF_SPI) {
+ wilc->hif_func->hif_read_reg(wilc, 1, &reg);
+ wilc->hif_func->hif_write_reg(wilc, 1, reg & ~BIT(1));
+ wilc->hif_func->hif_write_reg(wilc, 1, reg | BIT(1));
+ wilc->hif_func->hif_write_reg(wilc, 1, reg & ~BIT(1));
+ } else if ((wilc->io_type & 0x1) == HIF_SDIO) {
+ wilc->hif_func->hif_read_reg(wilc, 0xf0, &reg);
+ wilc->hif_func->hif_write_reg(wilc, 0xf0,
+ reg & ~BIT(0));
+ wilc->hif_func->hif_write_reg(wilc, 0xf0,
+ reg | BIT(0));
+ wilc->hif_func->hif_write_reg(wilc, 0xf0,
+ reg & ~BIT(0));
}
do {
- /* Wait for the chip to stabilize*/
mdelay(3);
- /* Make sure chip is awake. This is an extra step that can be removed */
- /* later to avoid the bus access overhead */
- if ((wilc_get_chipid(true) == 0))
+ if ((wilc_get_chipid(wilc, true) == 0))
wilc_debug(N_ERR, "Couldn't read chip id. Wake up failed\n");
- } while ((wilc_get_chipid(true) == 0) && ((++trials % 3) == 0));
+ } while ((wilc_get_chipid(wilc, true) == 0) && ((++trials % 3) == 0));
- } while (wilc_get_chipid(true) == 0);
+ } while (wilc_get_chipid(wilc, true) == 0);
- if (genuChipPSstate == CHIP_SLEEPING_MANUAL) {
- g_wlan.hif_func.hif_read_reg(0x1C0C, &reg);
+ if (chip_ps_state == CHIP_SLEEPING_MANUAL) {
+ wilc->hif_func->hif_read_reg(wilc, 0x1C0C, &reg);
reg &= ~BIT(0);
- g_wlan.hif_func.hif_write_reg(0x1C0C, reg);
+ wilc->hif_func->hif_write_reg(wilc, 0x1C0C, reg);
- if (wilc_get_chipid(false) >= 0x1002b0) {
- /* Enable PALDO back right after wakeup */
+ if (wilc_get_chipid(wilc, false) >= 0x1002b0) {
u32 val32;
- g_wlan.hif_func.hif_read_reg(0x1e1c, &val32);
+ wilc->hif_func->hif_read_reg(wilc, 0x1e1c, &val32);
val32 |= BIT(6);
- g_wlan.hif_func.hif_write_reg(0x1e1c, val32);
+ wilc->hif_func->hif_write_reg(wilc, 0x1e1c, val32);
- g_wlan.hif_func.hif_read_reg(0x1e9c, &val32);
+ wilc->hif_func->hif_read_reg(wilc, 0x1e9c, &val32);
val32 |= BIT(6);
- g_wlan.hif_func.hif_write_reg(0x1e9c, val32);
+ wilc->hif_func->hif_write_reg(wilc, 0x1e9c, val32);
}
}
- genuChipPSstate = CHIP_WAKEDUP;
+ chip_ps_state = CHIP_WAKEDUP;
}
#endif
-void chip_sleep_manually(u32 u32SleepTime)
+void wilc_chip_sleep_manually(struct wilc *wilc)
{
- if (genuChipPSstate != CHIP_WAKEDUP) {
- /* chip is already sleeping. Do nothing */
+ if (chip_ps_state != CHIP_WAKEDUP)
return;
- }
- acquire_bus(ACQUIRE_ONLY);
+ acquire_bus(wilc, ACQUIRE_ONLY);
#ifdef WILC_OPTIMIZE_SLEEP_INT
- chip_allow_sleep();
+ chip_allow_sleep(wilc);
#endif
+ wilc->hif_func->hif_write_reg(wilc, 0x10a8, 1);
- /* Trigger the manual sleep interrupt */
- g_wlan.hif_func.hif_write_reg(0x10a8, 1);
-
- genuChipPSstate = CHIP_SLEEPING_MANUAL;
- release_bus(RELEASE_ONLY);
-
+ chip_ps_state = CHIP_SLEEPING_MANUAL;
+ release_bus(wilc, RELEASE_ONLY);
}
-
-/********************************************
- *
- * Tx, Rx queue handle functions
- *
- ********************************************/
-int wilc_wlan_handle_txq(struct net_device *dev, u32 *pu32TxqCount)
+int wilc_wlan_handle_txq(struct net_device *dev, u32 *txq_count)
{
- wilc_wlan_dev_t *p = (wilc_wlan_dev_t *)&g_wlan;
int i, entries = 0;
u32 sum;
u32 reg;
- u8 *txb = p->tx_buffer;
+ u8 *txb;
u32 offset = 0;
int vmm_sz = 0;
struct txq_entry_t *tqe;
@@ -835,32 +676,29 @@ int wilc_wlan_handle_txq(struct net_device *dev, u32 *pu32TxqCount)
int counter;
int timeout;
u32 vmm_table[WILC_VMM_TBL_SIZE];
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct wilc *wilc;
- nic = netdev_priv(dev);
- wilc = nic->wilc;
+ vif = netdev_priv(dev);
+ wilc = vif->wilc;
- p->txq_exit = 0;
+ txb = wilc->tx_buffer;
+ wilc->txq_exit = 0;
do {
- if (p->quit)
+ if (wilc->quit)
break;
- linux_wlan_lock_timeout(&wilc->txq_add_to_head_cs,
+ wilc_lock_timeout(wilc, &wilc->txq_add_to_head_cs,
CFG_PKTS_TIMEOUT);
#ifdef TCP_ACK_FILTER
wilc_wlan_txq_filter_dup_tcp_ack(dev);
#endif
- /**
- * build the vmm list
- **/
PRINT_D(TX_DBG, "Getting the head of the TxQ\n");
- tqe = wilc_wlan_txq_get_first();
+ tqe = wilc_wlan_txq_get_first(wilc);
i = 0;
sum = 0;
do {
- if ((tqe != NULL) && (i < (WILC_VMM_TBL_SIZE - 1)) /* reserve last entry to 0 */) {
-
+ if (tqe && (i < (WILC_VMM_TBL_SIZE - 1))) {
if (tqe->type == WILC_CFG_PKT)
vmm_sz = ETH_CONFIG_PKT_HDR_OFFSET;
@@ -872,23 +710,22 @@ int wilc_wlan_handle_txq(struct net_device *dev, u32 *pu32TxqCount)
vmm_sz += tqe->buffer_size;
PRINT_D(TX_DBG, "VMM Size before alignment = %d\n", vmm_sz);
- if (vmm_sz & 0x3) { /* has to be word aligned */
+ if (vmm_sz & 0x3)
vmm_sz = (vmm_sz + 4) & ~0x3;
- }
+
if ((sum + vmm_sz) > LINUX_TX_SIZE)
break;
PRINT_D(TX_DBG, "VMM Size AFTER alignment = %d\n", vmm_sz);
- vmm_table[i] = vmm_sz / 4; /* table take the word size */
- PRINT_D(TX_DBG, "VMMTable entry size = %d\n", vmm_table[i]);
+ vmm_table[i] = vmm_sz / 4;
+ PRINT_D(TX_DBG, "VMMTable entry size = %d\n",
+ vmm_table[i]);
if (tqe->type == WILC_CFG_PKT) {
vmm_table[i] |= BIT(10);
PRINT_D(TX_DBG, "VMMTable entry changed for CFG packet = %d\n", vmm_table[i]);
}
-#ifdef BIG_ENDIAN
- vmm_table[i] = BYTE_SWAP(vmm_table[i]);
-#endif
+ vmm_table[i] = cpu_to_le32(vmm_table[i]);
i++;
sum += vmm_sz;
@@ -899,27 +736,24 @@ int wilc_wlan_handle_txq(struct net_device *dev, u32 *pu32TxqCount)
}
} while (1);
- if (i == 0) { /* nothing in the queue */
+ if (i == 0) {
PRINT_D(TX_DBG, "Nothing in TX-Q\n");
break;
} else {
PRINT_D(TX_DBG, "Mark the last entry in VMM table - number of previous entries = %d\n", i);
- vmm_table[i] = 0x0; /* mark the last element to 0 */
+ vmm_table[i] = 0x0;
}
- acquire_bus(ACQUIRE_AND_WAKEUP);
+ acquire_bus(wilc, ACQUIRE_AND_WAKEUP);
counter = 0;
do {
-
- ret = p->hif_func.hif_read_reg(WILC_HOST_TX_CTRL, &reg);
+ ret = wilc->hif_func->hif_read_reg(wilc, WILC_HOST_TX_CTRL,
+ &reg);
if (!ret) {
wilc_debug(N_ERR, "[wilc txq]: fail can't read reg vmm_tbl_entry..\n");
break;
}
if ((reg & 0x1) == 0) {
- /**
- * write to vmm table
- **/
PRINT_D(TX_DBG, "Writing VMM table ... with Size = %d\n", ((i + 1) * 4));
break;
} else {
@@ -927,69 +761,52 @@ int wilc_wlan_handle_txq(struct net_device *dev, u32 *pu32TxqCount)
if (counter > 200) {
counter = 0;
PRINT_D(TX_DBG, "Looping in tx ctrl , forcce quit\n");
- ret = p->hif_func.hif_write_reg(WILC_HOST_TX_CTRL, 0);
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_HOST_TX_CTRL, 0);
break;
}
- /**
- * wait for vmm table is ready
- **/
PRINT_WRN(GENERIC_DBG, "[wilc txq]: warn, vmm table not clear yet, wait...\n");
- release_bus(RELEASE_ALLOW_SLEEP);
+ release_bus(wilc, RELEASE_ALLOW_SLEEP);
usleep_range(3000, 3000);
- acquire_bus(ACQUIRE_AND_WAKEUP);
+ acquire_bus(wilc, ACQUIRE_AND_WAKEUP);
}
- } while (!p->quit);
+ } while (!wilc->quit);
if (!ret)
goto _end_;
timeout = 200;
do {
-
- /**
- * write to vmm table
- **/
- ret = p->hif_func.hif_block_tx(WILC_VMM_TBL_RX_SHADOW_BASE, (u8 *)vmm_table, ((i + 1) * 4));
+ ret = wilc->hif_func->hif_block_tx(wilc, WILC_VMM_TBL_RX_SHADOW_BASE, (u8 *)vmm_table, ((i + 1) * 4));
if (!ret) {
wilc_debug(N_ERR, "ERR block TX of VMM table.\n");
break;
}
-
- /**
- * interrupt firmware
- **/
- ret = p->hif_func.hif_write_reg(WILC_HOST_VMM_CTL, 0x2);
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_HOST_VMM_CTL,
+ 0x2);
if (!ret) {
wilc_debug(N_ERR, "[wilc txq]: fail can't write reg host_vmm_ctl..\n");
break;
}
- /**
- * wait for confirm...
- **/
-
do {
- ret = p->hif_func.hif_read_reg(WILC_HOST_VMM_CTL, &reg);
+ ret = wilc->hif_func->hif_read_reg(wilc, WILC_HOST_VMM_CTL, &reg);
if (!ret) {
wilc_debug(N_ERR, "[wilc txq]: fail can't read reg host_vmm_ctl..\n");
break;
}
if ((reg >> 2) & 0x1) {
- /**
- * Get the entries
- **/
entries = ((reg >> 3) & 0x3f);
break;
} else {
- release_bus(RELEASE_ALLOW_SLEEP);
+ release_bus(wilc, RELEASE_ALLOW_SLEEP);
usleep_range(3000, 3000);
- acquire_bus(ACQUIRE_AND_WAKEUP);
+ acquire_bus(wilc, ACQUIRE_AND_WAKEUP);
PRINT_WRN(GENERIC_DBG, "Can't get VMM entery - reg = %2x\n", reg);
}
} while (--timeout);
if (timeout <= 0) {
- ret = p->hif_func.hif_write_reg(WILC_HOST_VMM_CTL, 0x0);
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_HOST_VMM_CTL, 0x0);
break;
}
@@ -999,14 +816,13 @@ int wilc_wlan_handle_txq(struct net_device *dev, u32 *pu32TxqCount)
if (entries == 0) {
PRINT_WRN(GENERIC_DBG, "[wilc txq]: no more buffer in the chip (reg: %08x), retry later [[ %d, %x ]]\n", reg, i, vmm_table[i - 1]);
- /* undo the transaction. */
- ret = p->hif_func.hif_read_reg(WILC_HOST_TX_CTRL, &reg);
+ ret = wilc->hif_func->hif_read_reg(wilc, WILC_HOST_TX_CTRL, &reg);
if (!ret) {
wilc_debug(N_ERR, "[wilc txq]: fail can't read reg WILC_HOST_TX_CTRL..\n");
break;
}
reg &= ~BIT(0);
- ret = p->hif_func.hif_write_reg(WILC_HOST_TX_CTRL, reg);
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_HOST_TX_CTRL, reg);
if (!ret) {
wilc_debug(N_ERR, "[wilc txq]: fail can't write reg WILC_HOST_TX_CTRL..\n");
break;
@@ -1025,58 +841,50 @@ int wilc_wlan_handle_txq(struct net_device *dev, u32 *pu32TxqCount)
goto _end_;
}
- /* since copying data into txb takes some time, then
- * allow the bus lock to be released let the RX task go. */
- release_bus(RELEASE_ALLOW_SLEEP);
+ release_bus(wilc, RELEASE_ALLOW_SLEEP);
- /**
- * Copy data to the TX buffer
- **/
offset = 0;
i = 0;
do {
- tqe = wilc_wlan_txq_remove_from_head();
- if (tqe != NULL && (vmm_table[i] != 0)) {
+ tqe = wilc_wlan_txq_remove_from_head(dev);
+ if (tqe && (vmm_table[i] != 0)) {
u32 header, buffer_offset;
-#ifdef BIG_ENDIAN
- vmm_table[i] = BYTE_SWAP(vmm_table[i]);
-#endif
- vmm_sz = (vmm_table[i] & 0x3ff); /* in word unit */
+ vmm_table[i] = cpu_to_le32(vmm_table[i]);
+ vmm_sz = (vmm_table[i] & 0x3ff);
vmm_sz *= 4;
- header = (tqe->type << 31) | (tqe->buffer_size << 15) | vmm_sz;
+ header = (tqe->type << 31) |
+ (tqe->buffer_size << 15) |
+ vmm_sz;
if (tqe->type == WILC_MGMT_PKT)
header |= BIT(30);
else
header &= ~BIT(30);
-#ifdef BIG_ENDIAN
- header = BYTE_SWAP(header);
-#endif
+ header = cpu_to_le32(header);
memcpy(&txb[offset], &header, 4);
if (tqe->type == WILC_CFG_PKT) {
buffer_offset = ETH_CONFIG_PKT_HDR_OFFSET;
- }
- else if (tqe->type == WILC_NET_PKT) {
- char *pBSSID = ((struct tx_complete_data *)(tqe->priv))->pBssid;
+ } else if (tqe->type == WILC_NET_PKT) {
+ char *bssid = ((struct tx_complete_data *)(tqe->priv))->pBssid;
buffer_offset = ETH_ETHERNET_HDR_OFFSET;
- /* copy the bssid at the sart of the buffer */
- memcpy(&txb[offset + 4], pBSSID, 6);
- }
- else {
+ memcpy(&txb[offset + 4], bssid, 6);
+ } else {
buffer_offset = HOST_HDR_OFFSET;
}
- memcpy(&txb[offset + buffer_offset], tqe->buffer, tqe->buffer_size);
+ memcpy(&txb[offset + buffer_offset],
+ tqe->buffer, tqe->buffer_size);
offset += vmm_sz;
i++;
- tqe->status = 1; /* mark the packet send */
+ tqe->status = 1;
if (tqe->tx_complete_func)
- tqe->tx_complete_func(tqe->priv, tqe->status);
+ tqe->tx_complete_func(tqe->priv,
+ tqe->status);
#ifdef TCP_ACK_FILTER
- if (tqe->tcp_PendingAck_index != NOT_TCP_ACK)
- Pending_Acks_info[tqe->tcp_PendingAck_index].txqe = NULL;
+ if (tqe->tcp_pending_ack_idx != NOT_TCP_ACK)
+ pending_acks_info[tqe->tcp_pending_ack_idx].txqe = NULL;
#endif
kfree(tqe);
} else {
@@ -1084,21 +892,15 @@ int wilc_wlan_handle_txq(struct net_device *dev, u32 *pu32TxqCount)
}
} while (--entries);
- /**
- * lock the bus
- **/
- acquire_bus(ACQUIRE_AND_WAKEUP);
+ acquire_bus(wilc, ACQUIRE_AND_WAKEUP);
- ret = p->hif_func.hif_clear_int_ext(ENABLE_TX_VMM);
+ ret = wilc->hif_func->hif_clear_int_ext(wilc, ENABLE_TX_VMM);
if (!ret) {
wilc_debug(N_ERR, "[wilc txq]: fail can't start tx VMM ...\n");
goto _end_;
}
- /**
- * transfer
- **/
- ret = p->hif_func.hif_block_tx_ext(0, txb, offset);
+ ret = wilc->hif_func->hif_block_tx_ext(wilc, 0, txb, offset);
if (!ret) {
wilc_debug(N_ERR, "[wilc txq]: fail can't block tx ext...\n");
goto _end_;
@@ -1106,49 +908,43 @@ int wilc_wlan_handle_txq(struct net_device *dev, u32 *pu32TxqCount)
_end_:
- release_bus(RELEASE_ALLOW_SLEEP);
+ release_bus(wilc, RELEASE_ALLOW_SLEEP);
if (ret != 1)
break;
} while (0);
up(&wilc->txq_add_to_head_cs);
- p->txq_exit = 1;
+ wilc->txq_exit = 1;
PRINT_D(TX_DBG, "THREAD: Exiting txq\n");
- /* return tx[]q count */
- *pu32TxqCount = p->txq_entries;
+ *txq_count = wilc->txq_entries;
return ret;
}
static void wilc_wlan_handle_rxq(struct wilc *wilc)
{
- wilc_wlan_dev_t *p = &g_wlan;
int offset = 0, size, has_packet = 0;
u8 *buffer;
struct rxq_entry_t *rqe;
- p->rxq_exit = 0;
-
-
-
+ wilc->rxq_exit = 0;
do {
- if (p->quit) {
+ if (wilc->quit) {
PRINT_D(RX_DBG, "exit 1st do-while due to Clean_UP function\n");
up(&wilc->cfg_event);
break;
}
rqe = wilc_wlan_rxq_remove(wilc);
- if (rqe == NULL) {
+ if (!rqe) {
PRINT_D(RX_DBG, "nothing in the queue - exit 1st do-while\n");
break;
}
buffer = rqe->buffer;
size = rqe->buffer_size;
- PRINT_D(RX_DBG, "rxQ entery Size = %d - Address = %p\n", size, buffer);
+ PRINT_D(RX_DBG, "rxQ entery Size = %d - Address = %p\n",
+ size, buffer);
offset = 0;
-
-
do {
u32 header;
u32 pkt_len, pkt_offset, tp_len;
@@ -1156,12 +952,9 @@ static void wilc_wlan_handle_rxq(struct wilc *wilc)
PRINT_D(RX_DBG, "In the 2nd do-while\n");
memcpy(&header, &buffer[offset], 4);
-#ifdef BIG_ENDIAN
- header = BYTE_SWAP(header);
-#endif
- PRINT_D(RX_DBG, "Header = %04x - Offset = %d\n", header, offset);
-
-
+ header = cpu_to_le32(header);
+ PRINT_D(RX_DBG, "Header = %04x - Offset = %d\n",
+ header, offset);
is_cfg_packet = (header >> 31) & 0x1;
pkt_offset = (header >> 22) & 0x1ff;
@@ -1177,45 +970,34 @@ static void wilc_wlan_handle_rxq(struct wilc *wilc)
#define IS_MANAGMEMENT_CALLBACK 0x080
#define IS_MGMT_STATUS_SUCCES 0x040
-
if (pkt_offset & IS_MANAGMEMENT) {
- /* reset mgmt indicator bit, to use pkt_offeset in furthur calculations */
- pkt_offset &= ~(IS_MANAGMEMENT | IS_MANAGMEMENT_CALLBACK | IS_MGMT_STATUS_SUCCES);
+ pkt_offset &= ~(IS_MANAGMEMENT |
+ IS_MANAGMEMENT_CALLBACK |
+ IS_MGMT_STATUS_SUCCES);
WILC_WFI_mgmt_rx(wilc, &buffer[offset + HOST_HDR_OFFSET], pkt_len);
- }
- else
- {
-
+ } else {
if (!is_cfg_packet) {
if (pkt_len > 0) {
- frmw_to_linux(wilc,
+ wilc_frmw_to_linux(wilc,
&buffer[offset],
pkt_len,
pkt_offset);
has_packet = 1;
}
} else {
- wilc_cfg_rsp_t rsp;
-
-
+ struct wilc_cfg_rsp rsp;
- wilc_wlan_cfg_indicate_rx(&buffer[pkt_offset + offset], pkt_len, &rsp);
+ wilc_wlan_cfg_indicate_rx(wilc, &buffer[pkt_offset + offset], pkt_len, &rsp);
if (rsp.type == WILC_CFG_RSP) {
- /**
- * wake up the waiting task...
- **/
- PRINT_D(RX_DBG, "p->cfg_seq_no = %d - rsp.seq_no = %d\n", p->cfg_seq_no, rsp.seq_no);
- if (p->cfg_seq_no == rsp.seq_no)
+ PRINT_D(RX_DBG, "wilc->cfg_seq_no = %d - rsp.seq_no = %d\n", wilc->cfg_seq_no, rsp.seq_no);
+ if (wilc->cfg_seq_no == rsp.seq_no)
up(&wilc->cfg_event);
} else if (rsp.type == WILC_CFG_RSP_STATUS) {
- /**
- * Call back to indicate status...
- **/
- linux_wlan_mac_indicate(wilc, WILC_MAC_INDICATE_STATUS);
+ wilc_mac_indicate(wilc, WILC_MAC_INDICATE_STATUS);
} else if (rsp.type == WILC_CFG_RSP_SCAN) {
- linux_wlan_mac_indicate(wilc, WILC_MAC_INDICATE_SCAN);
+ wilc_mac_indicate(wilc, WILC_MAC_INDICATE_SCAN);
}
}
}
@@ -1223,224 +1005,163 @@ static void wilc_wlan_handle_rxq(struct wilc *wilc)
if (offset >= size)
break;
} while (1);
-
-
-#ifndef MEMORY_STATIC
- kfree(buffer);
-#endif
kfree(rqe);
if (has_packet)
- linux_wlan_rx_complete();
+ wilc_rx_complete(wilc);
} while (1);
- p->rxq_exit = 1;
+ wilc->rxq_exit = 1;
PRINT_D(RX_DBG, "THREAD: Exiting RX thread\n");
}
-/********************************************
- *
- * Fast DMA Isr
- *
- ********************************************/
-static void wilc_unknown_isr_ext(void)
+static void wilc_unknown_isr_ext(struct wilc *wilc)
{
- g_wlan.hif_func.hif_clear_int_ext(0);
+ wilc->hif_func->hif_clear_int_ext(wilc, 0);
}
-static void wilc_pllupdate_isr_ext(u32 int_stats)
-{
+static void wilc_pllupdate_isr_ext(struct wilc *wilc, u32 int_stats)
+{
int trials = 10;
- g_wlan.hif_func.hif_clear_int_ext(PLL_INT_CLR);
+ wilc->hif_func->hif_clear_int_ext(wilc, PLL_INT_CLR);
- /* Waiting for PLL */
- mdelay(WILC_PLL_TO);
+ if (wilc->io_type == HIF_SDIO)
+ mdelay(WILC_PLL_TO_SDIO);
+ else
+ mdelay(WILC_PLL_TO_SPI);
- /* poll till read a valid data */
- while (!(ISWILC1000(wilc_get_chipid(true)) && --trials)) {
+ while (!(ISWILC1000(wilc_get_chipid(wilc, true)) && --trials)) {
PRINT_D(TX_DBG, "PLL update retrying\n");
mdelay(1);
}
}
-static void wilc_sleeptimer_isr_ext(u32 int_stats1)
+static void wilc_sleeptimer_isr_ext(struct wilc *wilc, u32 int_stats1)
{
- g_wlan.hif_func.hif_clear_int_ext(SLEEP_INT_CLR);
+ wilc->hif_func->hif_clear_int_ext(wilc, SLEEP_INT_CLR);
#ifndef WILC_OPTIMIZE_SLEEP_INT
- genuChipPSstate = CHIP_SLEEPING_AUTO;
+ chip_ps_state = CHIP_SLEEPING_AUTO;
#endif
}
static void wilc_wlan_handle_isr_ext(struct wilc *wilc, u32 int_status)
{
- wilc_wlan_dev_t *p = &g_wlan;
-#ifdef MEMORY_STATIC
- u32 offset = p->rx_buffer_offset;
-#endif
+ u32 offset = wilc->rx_buffer_offset;
u8 *buffer = NULL;
u32 size;
u32 retries = 0;
int ret = 0;
struct rxq_entry_t *rqe;
-
- /**
- * Get the rx size
- **/
-
size = ((int_status & 0x7fff) << 2);
while (!size && retries < 10) {
u32 time = 0;
- /*looping more secure*/
- /*zero size make a crashe because the dma will not happen and that will block the firmware*/
+
wilc_debug(N_ERR, "RX Size equal zero ... Trying to read it again for %d time\n", time++);
- p->hif_func.hif_read_size(&size);
+ wilc->hif_func->hif_read_size(wilc, &size);
size = ((size & 0x7fff) << 2);
retries++;
-
}
if (size > 0) {
-#ifdef MEMORY_STATIC
if (LINUX_RX_SIZE - offset < size)
offset = 0;
- if (p->rx_buffer)
- buffer = &p->rx_buffer[offset];
- else {
+ if (wilc->rx_buffer) {
+ buffer = &wilc->rx_buffer[offset];
+ } else {
wilc_debug(N_ERR, "[wilc isr]: fail Rx Buffer is NULL...drop the packets (%d)\n", size);
goto _end_;
}
-#else
- buffer = kmalloc(size, GFP_KERNEL);
- if (buffer == NULL) {
- wilc_debug(N_ERR, "[wilc isr]: fail alloc host memory...drop the packets (%d)\n", size);
- usleep_range(100 * 1000, 100 * 1000);
- goto _end_;
- }
-#endif
-
- /**
- * clear the chip's interrupt after getting size some register getting corrupted after clear the interrupt
- **/
- p->hif_func.hif_clear_int_ext(DATA_INT_CLR | ENABLE_RX_VMM);
-
-
- /**
- * start transfer
- **/
- ret = p->hif_func.hif_block_rx_ext(0, buffer, size);
+ wilc->hif_func->hif_clear_int_ext(wilc,
+ DATA_INT_CLR | ENABLE_RX_VMM);
+ ret = wilc->hif_func->hif_block_rx_ext(wilc, 0, buffer, size);
if (!ret) {
wilc_debug(N_ERR, "[wilc isr]: fail block rx...\n");
goto _end_;
}
_end_:
-
-
if (ret) {
-#ifdef MEMORY_STATIC
offset += size;
- p->rx_buffer_offset = offset;
-#endif
- /**
- * add to rx queue
- **/
- rqe = kmalloc(sizeof(struct rxq_entry_t), GFP_KERNEL);
- if (rqe != NULL) {
+ wilc->rx_buffer_offset = offset;
+ rqe = kmalloc(sizeof(*rqe), GFP_KERNEL);
+ if (rqe) {
rqe->buffer = buffer;
rqe->buffer_size = size;
PRINT_D(RX_DBG, "rxq entery Size= %d - Address = %p\n", rqe->buffer_size, rqe->buffer);
wilc_wlan_rxq_add(wilc, rqe);
}
- } else {
-#ifndef MEMORY_STATIC
- kfree(buffer);
-#endif
}
}
wilc_wlan_handle_rxq(wilc);
}
-void wilc_handle_isr(void *wilc)
+void wilc_handle_isr(struct wilc *wilc)
{
u32 int_status;
- acquire_bus(ACQUIRE_AND_WAKEUP);
- g_wlan.hif_func.hif_read_int(&int_status);
+ acquire_bus(wilc, ACQUIRE_AND_WAKEUP);
+ wilc->hif_func->hif_read_int(wilc, &int_status);
if (int_status & PLL_INT_EXT)
- wilc_pllupdate_isr_ext(int_status);
+ wilc_pllupdate_isr_ext(wilc, int_status);
if (int_status & DATA_INT_EXT) {
wilc_wlan_handle_isr_ext(wilc, int_status);
#ifndef WILC_OPTIMIZE_SLEEP_INT
- /* Chip is up and talking*/
- genuChipPSstate = CHIP_WAKEDUP;
+ chip_ps_state = CHIP_WAKEDUP;
#endif
}
if (int_status & SLEEP_INT_EXT)
- wilc_sleeptimer_isr_ext(int_status);
+ wilc_sleeptimer_isr_ext(wilc, int_status);
if (!(int_status & (ALL_INT_EXT))) {
-#ifdef WILC_SDIO
- PRINT_D(TX_DBG, ">> UNKNOWN_INTERRUPT - 0x%08x\n", int_status);
-#endif
- wilc_unknown_isr_ext();
+ wilc_unknown_isr_ext(wilc);
}
- release_bus(RELEASE_ALLOW_SLEEP);
+ release_bus(wilc, RELEASE_ALLOW_SLEEP);
}
+EXPORT_SYMBOL_GPL(wilc_handle_isr);
-/********************************************
- *
- * Firmware download
- *
- ********************************************/
-int wilc_wlan_firmware_download(const u8 *buffer, u32 buffer_size)
+int wilc_wlan_firmware_download(struct wilc *wilc, const u8 *buffer, u32 buffer_size)
{
- wilc_wlan_dev_t *p = &g_wlan;
u32 offset;
u32 addr, size, size2, blksz;
u8 *dma_buffer;
int ret = 0;
blksz = BIT(12);
- /* Allocate a DMA coherent buffer. */
dma_buffer = kmalloc(blksz, GFP_KERNEL);
- if (dma_buffer == NULL) {
- /*EIO 5*/
- ret = -5;
+ if (!dma_buffer) {
+ ret = -EIO;
PRINT_ER("Can't allocate buffer for firmware download IO error\n ");
goto _fail_1;
}
PRINT_D(INIT_DBG, "Downloading firmware size = %d ...\n", buffer_size);
- /**
- * load the firmware
- **/
+
offset = 0;
do {
memcpy(&addr, &buffer[offset], 4);
memcpy(&size, &buffer[offset + 4], 4);
-#ifdef BIG_ENDIAN
- addr = BYTE_SWAP(addr);
- size = BYTE_SWAP(size);
-#endif
- acquire_bus(ACQUIRE_ONLY);
+ addr = cpu_to_le32(addr);
+ size = cpu_to_le32(size);
+ acquire_bus(wilc, ACQUIRE_ONLY);
offset += 8;
while (((int)size) && (offset < buffer_size)) {
if (size <= blksz)
size2 = size;
else
size2 = blksz;
- /* Copy firmware into a DMA coherent buffer */
+
memcpy(dma_buffer, &buffer[offset], size2);
- ret = p->hif_func.hif_block_tx(addr, dma_buffer, size2);
+ ret = wilc->hif_func->hif_block_tx(wilc, addr, dma_buffer,
+ size2);
if (!ret)
break;
@@ -1448,11 +1169,10 @@ int wilc_wlan_firmware_download(const u8 *buffer, u32 buffer_size)
offset += size2;
size -= size2;
}
- release_bus(RELEASE_ONLY);
+ release_bus(wilc, RELEASE_ONLY);
if (!ret) {
- /*EIO 5*/
- ret = -5;
+ ret = -EIO;
PRINT_ER("Can't download firmware IO error\n ");
goto _fail_;
}
@@ -1468,40 +1188,29 @@ _fail_1:
return (ret < 0) ? ret : 0;
}
-/********************************************
- *
- * Common
- *
- ********************************************/
-int wilc_wlan_start(void)
+int wilc_wlan_start(struct wilc *wilc)
{
- wilc_wlan_dev_t *p = &g_wlan;
u32 reg = 0;
int ret;
u32 chipid;
- /**
- * Set the host interface
- **/
- if (p->io_func.io_type == HIF_SDIO) {
+ if (wilc->io_type == HIF_SDIO) {
reg = 0;
- reg |= BIT(3); /* bug 4456 and 4557 */
- } else if (p->io_func.io_type == HIF_SPI) {
+ reg |= BIT(3);
+ } else if (wilc->io_type == HIF_SPI) {
reg = 1;
}
- acquire_bus(ACQUIRE_ONLY);
- ret = p->hif_func.hif_write_reg(WILC_VMM_CORE_CFG, reg);
+ acquire_bus(wilc, ACQUIRE_ONLY);
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_VMM_CORE_CFG, reg);
if (!ret) {
wilc_debug(N_ERR, "[wilc start]: fail write reg vmm_core_cfg...\n");
- release_bus(RELEASE_ONLY);
- /* EIO 5*/
- ret = -5;
+ release_bus(wilc, RELEASE_ONLY);
+ ret = -EIO;
return ret;
}
reg = 0;
-#ifdef WILC_SDIO_IRQ_GPIO
- reg |= WILC_HAVE_SDIO_IRQ_GPIO;
-#endif
+ if (wilc->io_type == HIF_SDIO && wilc->dev_irq_num)
+ reg |= WILC_HAVE_SDIO_IRQ_GPIO;
#ifdef WILC_DISABLE_PMU
#else
@@ -1519,123 +1228,103 @@ int wilc_wlan_start(void)
#endif
reg |= WILC_HAVE_LEGACY_RF_SETTINGS;
-
-
-/*Set oscillator frequency*/
#ifdef XTAL_24
reg |= WILC_HAVE_XTAL_24;
#endif
-
-/*Enable/Disable GPIO configuration for FW logs*/
#ifdef DISABLE_WILC_UART
reg |= WILC_HAVE_DISABLE_WILC_UART;
#endif
- ret = p->hif_func.hif_write_reg(WILC_GP_REG_1, reg);
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_GP_REG_1, reg);
if (!ret) {
wilc_debug(N_ERR, "[wilc start]: fail write WILC_GP_REG_1 ...\n");
- release_bus(RELEASE_ONLY);
- /* EIO 5*/
- ret = -5;
+ release_bus(wilc, RELEASE_ONLY);
+ ret = -EIO;
return ret;
}
- /**
- * Bus related
- **/
- p->hif_func.hif_sync_ext(NUM_INT_EXT);
+ wilc->hif_func->hif_sync_ext(wilc, NUM_INT_EXT);
- ret = p->hif_func.hif_read_reg(0x1000, &chipid);
+ ret = wilc->hif_func->hif_read_reg(wilc, 0x1000, &chipid);
if (!ret) {
wilc_debug(N_ERR, "[wilc start]: fail read reg 0x1000 ...\n");
- release_bus(RELEASE_ONLY);
- /* EIO 5*/
- ret = -5;
+ release_bus(wilc, RELEASE_ONLY);
+ ret = -EIO;
return ret;
}
- /**
- * Go...
- **/
-
-
- p->hif_func.hif_read_reg(WILC_GLB_RESET_0, &reg);
+ wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, &reg);
if ((reg & BIT(10)) == BIT(10)) {
reg &= ~BIT(10);
- p->hif_func.hif_write_reg(WILC_GLB_RESET_0, reg);
- p->hif_func.hif_read_reg(WILC_GLB_RESET_0, &reg);
+ wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg);
+ wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, &reg);
}
reg |= BIT(10);
- ret = p->hif_func.hif_write_reg(WILC_GLB_RESET_0, reg);
- p->hif_func.hif_read_reg(WILC_GLB_RESET_0, &reg);
- release_bus(RELEASE_ONLY);
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg);
+ wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, &reg);
+ release_bus(wilc, RELEASE_ONLY);
return (ret < 0) ? ret : 0;
}
-void wilc_wlan_global_reset(void)
+void wilc_wlan_global_reset(struct wilc *wilc)
{
-
- wilc_wlan_dev_t *p = &g_wlan;
-
- acquire_bus(ACQUIRE_AND_WAKEUP);
- p->hif_func.hif_write_reg(WILC_GLB_RESET_0, 0x0);
- release_bus(RELEASE_ONLY);
+ acquire_bus(wilc, ACQUIRE_AND_WAKEUP);
+ wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, 0x0);
+ release_bus(wilc, RELEASE_ONLY);
}
-int wilc_wlan_stop(void)
+int wilc_wlan_stop(struct wilc *wilc)
{
- wilc_wlan_dev_t *p = &g_wlan;
u32 reg = 0;
int ret;
u8 timeout = 10;
- /**
- * TODO: stop the firmware, need a re-download
- **/
- acquire_bus(ACQUIRE_AND_WAKEUP);
+ acquire_bus(wilc, ACQUIRE_AND_WAKEUP);
- ret = p->hif_func.hif_read_reg(WILC_GLB_RESET_0, &reg);
+ ret = wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, &reg);
if (!ret) {
PRINT_ER("Error while reading reg\n");
- release_bus(RELEASE_ALLOW_SLEEP);
+ release_bus(wilc, RELEASE_ALLOW_SLEEP);
return ret;
}
reg &= ~BIT(10);
-
-
- ret = p->hif_func.hif_write_reg(WILC_GLB_RESET_0, reg);
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg);
if (!ret) {
PRINT_ER("Error while writing reg\n");
- release_bus(RELEASE_ALLOW_SLEEP);
+ release_bus(wilc, RELEASE_ALLOW_SLEEP);
return ret;
}
-
-
do {
- ret = p->hif_func.hif_read_reg(WILC_GLB_RESET_0, &reg);
+ ret = wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, &reg);
if (!ret) {
PRINT_ER("Error while reading reg\n");
- release_bus(RELEASE_ALLOW_SLEEP);
+ release_bus(wilc, RELEASE_ALLOW_SLEEP);
return ret;
}
- PRINT_D(GENERIC_DBG, "Read RESET Reg %x : Retry%d\n", reg, timeout);
- /*Workaround to ensure that the chip is actually reset*/
+ PRINT_D(GENERIC_DBG, "Read RESET Reg %x : Retry%d\n",
+ reg, timeout);
+
if ((reg & BIT(10))) {
- PRINT_D(GENERIC_DBG, "Bit 10 not reset : Retry %d\n", timeout);
+ PRINT_D(GENERIC_DBG, "Bit 10 not reset : Retry %d\n",
+ timeout);
reg &= ~BIT(10);
- ret = p->hif_func.hif_write_reg(WILC_GLB_RESET_0, reg);
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0,
+ reg);
timeout--;
} else {
- PRINT_D(GENERIC_DBG, "Bit 10 reset after : Retry %d\n", timeout);
- ret = p->hif_func.hif_read_reg(WILC_GLB_RESET_0, &reg);
+ PRINT_D(GENERIC_DBG, "Bit 10 reset after : Retry %d\n",
+ timeout);
+ ret = wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0,
+ &reg);
if (!ret) {
PRINT_ER("Error while reading reg\n");
- release_bus(RELEASE_ALLOW_SLEEP);
+ release_bus(wilc, RELEASE_ALLOW_SLEEP);
return ret;
}
- PRINT_D(GENERIC_DBG, "Read RESET Reg %x : Retry%d\n", reg, timeout);
+ PRINT_D(GENERIC_DBG, "Read RESET Reg %x : Retry%d\n",
+ reg, timeout);
break;
}
@@ -1643,33 +1332,32 @@ int wilc_wlan_stop(void)
reg = (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(8) | BIT(9) | BIT(26) |
BIT(29) | BIT(30) | BIT(31));
- p->hif_func.hif_write_reg(WILC_GLB_RESET_0, reg);
+ wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg);
reg = (u32)~BIT(10);
- ret = p->hif_func.hif_write_reg(WILC_GLB_RESET_0, reg);
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg);
- release_bus(RELEASE_ALLOW_SLEEP);
+ release_bus(wilc, RELEASE_ALLOW_SLEEP);
return ret;
}
void wilc_wlan_cleanup(struct net_device *dev)
{
- wilc_wlan_dev_t *p = &g_wlan;
struct txq_entry_t *tqe;
struct rxq_entry_t *rqe;
u32 reg = 0;
int ret;
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct wilc *wilc;
- nic = netdev_priv(dev);
- wilc = nic->wilc;
+ vif = netdev_priv(dev);
+ wilc = vif->wilc;
- p->quit = 1;
+ wilc->quit = 1;
do {
- tqe = wilc_wlan_txq_remove_from_head();
- if (tqe == NULL)
+ tqe = wilc_wlan_txq_remove_from_head(dev);
+ if (!tqe)
break;
if (tqe->tx_complete_func)
tqe->tx_complete_func(tqe->priv, 0);
@@ -1678,157 +1366,133 @@ void wilc_wlan_cleanup(struct net_device *dev)
do {
rqe = wilc_wlan_rxq_remove(wilc);
- if (rqe == NULL)
+ if (!rqe)
break;
-#ifndef MEMORY_STATIC
- kfree(rqe->buffer);
-#endif
kfree(rqe);
} while (1);
- /**
- * clean up buffer
- **/
-
- #ifdef MEMORY_STATIC
- kfree(p->rx_buffer);
- p->rx_buffer = NULL;
- #endif
- kfree(p->tx_buffer);
+ kfree(wilc->rx_buffer);
+ wilc->rx_buffer = NULL;
+ kfree(wilc->tx_buffer);
+ wilc->tx_buffer = NULL;
- acquire_bus(ACQUIRE_AND_WAKEUP);
+ acquire_bus(wilc, ACQUIRE_AND_WAKEUP);
-
- ret = p->hif_func.hif_read_reg(WILC_GP_REG_0, &reg);
+ ret = wilc->hif_func->hif_read_reg(wilc, WILC_GP_REG_0, &reg);
if (!ret) {
PRINT_ER("Error while reading reg\n");
- release_bus(RELEASE_ALLOW_SLEEP);
+ release_bus(wilc, RELEASE_ALLOW_SLEEP);
}
PRINT_ER("Writing ABORT reg\n");
- ret = p->hif_func.hif_write_reg(WILC_GP_REG_0, (reg | ABORT_INT));
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_GP_REG_0,
+ (reg | ABORT_INT));
if (!ret) {
PRINT_ER("Error while writing reg\n");
- release_bus(RELEASE_ALLOW_SLEEP);
+ release_bus(wilc, RELEASE_ALLOW_SLEEP);
}
- release_bus(RELEASE_ALLOW_SLEEP);
- /**
- * io clean up
- **/
- p->hif_func.hif_deinit(NULL);
-
+ release_bus(wilc, RELEASE_ALLOW_SLEEP);
+ wilc->hif_func->hif_deinit(NULL);
}
-static int wilc_wlan_cfg_commit(int type, u32 drvHandler)
+static int wilc_wlan_cfg_commit(struct wilc *wilc, int type, u32 drv_handler)
{
- wilc_wlan_dev_t *p = &g_wlan;
- wilc_cfg_frame_t *cfg = &p->cfg_frame;
- int total_len = p->cfg_frame_offset + 4 + DRIVER_HANDLER_SIZE;
- int seq_no = p->cfg_seq_no % 256;
- int driver_handler = (u32)drvHandler;
-
+ struct wilc_cfg_frame *cfg = &wilc->cfg_frame;
+ int total_len = wilc->cfg_frame_offset + 4 + DRIVER_HANDLER_SIZE;
+ int seq_no = wilc->cfg_seq_no % 256;
+ int driver_handler = (u32)drv_handler;
- /**
- * Set up header
- **/
- if (type == WILC_CFG_SET) { /* Set */
+ if (type == WILC_CFG_SET)
cfg->wid_header[0] = 'W';
- } else { /* Query */
+ else
cfg->wid_header[0] = 'Q';
- }
- cfg->wid_header[1] = seq_no; /* sequence number */
+ cfg->wid_header[1] = seq_no;
cfg->wid_header[2] = (u8)total_len;
cfg->wid_header[3] = (u8)(total_len >> 8);
cfg->wid_header[4] = (u8)driver_handler;
cfg->wid_header[5] = (u8)(driver_handler >> 8);
cfg->wid_header[6] = (u8)(driver_handler >> 16);
cfg->wid_header[7] = (u8)(driver_handler >> 24);
- p->cfg_seq_no = seq_no;
-
- /**
- * Add to TX queue
- **/
+ wilc->cfg_seq_no = seq_no;
- if (!wilc_wlan_txq_add_cfg_pkt(&cfg->wid_header[0], total_len))
+ if (!wilc_wlan_txq_add_cfg_pkt(wilc, &cfg->wid_header[0], total_len))
return -1;
return 0;
}
-int wilc_wlan_cfg_set(int start, u32 wid, u8 *buffer, u32 buffer_size,
- int commit, u32 drvHandler)
+int wilc_wlan_cfg_set(struct wilc *wilc, int start, u32 wid, u8 *buffer,
+ u32 buffer_size, int commit, u32 drv_handler)
{
- wilc_wlan_dev_t *p = &g_wlan;
u32 offset;
int ret_size;
-
- if (p->cfg_frame_in_use)
+ if (wilc->cfg_frame_in_use)
return 0;
if (start)
- p->cfg_frame_offset = 0;
+ wilc->cfg_frame_offset = 0;
- offset = p->cfg_frame_offset;
- ret_size = wilc_wlan_cfg_set_wid(p->cfg_frame.frame, offset, (u16)wid,
- buffer, buffer_size);
+ offset = wilc->cfg_frame_offset;
+ ret_size = wilc_wlan_cfg_set_wid(wilc->cfg_frame.frame, offset,
+ (u16)wid, buffer, buffer_size);
offset += ret_size;
- p->cfg_frame_offset = offset;
+ wilc->cfg_frame_offset = offset;
if (commit) {
- PRINT_D(TX_DBG, "[WILC]PACKET Commit with sequence number %d\n", p->cfg_seq_no);
+ PRINT_D(TX_DBG, "[WILC]PACKET Commit with sequence number %d\n",
+ wilc->cfg_seq_no);
PRINT_D(RX_DBG, "Processing cfg_set()\n");
- p->cfg_frame_in_use = 1;
+ wilc->cfg_frame_in_use = 1;
- if (wilc_wlan_cfg_commit(WILC_CFG_SET, drvHandler))
+ if (wilc_wlan_cfg_commit(wilc, WILC_CFG_SET, drv_handler))
ret_size = 0;
- if (linux_wlan_lock_timeout(&g_linux_wlan->cfg_event,
+ if (wilc_lock_timeout(wilc, &wilc->cfg_event,
CFG_PKTS_TIMEOUT)) {
PRINT_D(TX_DBG, "Set Timed Out\n");
ret_size = 0;
}
- p->cfg_frame_in_use = 0;
- p->cfg_frame_offset = 0;
- p->cfg_seq_no += 1;
-
+ wilc->cfg_frame_in_use = 0;
+ wilc->cfg_frame_offset = 0;
+ wilc->cfg_seq_no += 1;
}
return ret_size;
}
-int wilc_wlan_cfg_get(int start, u32 wid, int commit, u32 drvHandler)
+
+int wilc_wlan_cfg_get(struct wilc *wilc, int start, u32 wid, int commit,
+ u32 drv_handler)
{
- wilc_wlan_dev_t *p = &g_wlan;
u32 offset;
int ret_size;
-
- if (p->cfg_frame_in_use)
+ if (wilc->cfg_frame_in_use)
return 0;
if (start)
- p->cfg_frame_offset = 0;
+ wilc->cfg_frame_offset = 0;
- offset = p->cfg_frame_offset;
- ret_size = wilc_wlan_cfg_get_wid(p->cfg_frame.frame, offset, (u16)wid);
+ offset = wilc->cfg_frame_offset;
+ ret_size = wilc_wlan_cfg_get_wid(wilc->cfg_frame.frame, offset,
+ (u16)wid);
offset += ret_size;
- p->cfg_frame_offset = offset;
+ wilc->cfg_frame_offset = offset;
if (commit) {
- p->cfg_frame_in_use = 1;
+ wilc->cfg_frame_in_use = 1;
- if (wilc_wlan_cfg_commit(WILC_CFG_QUERY, drvHandler))
+ if (wilc_wlan_cfg_commit(wilc, WILC_CFG_QUERY, drv_handler))
ret_size = 0;
-
- if (linux_wlan_lock_timeout(&g_linux_wlan->cfg_event,
+ if (wilc_lock_timeout(wilc, &wilc->cfg_event,
CFG_PKTS_TIMEOUT)) {
PRINT_D(TX_DBG, "Get Timed Out\n");
ret_size = 0;
}
PRINT_D(GENERIC_DBG, "[WILC]Get Response received\n");
- p->cfg_frame_in_use = 0;
- p->cfg_frame_offset = 0;
- p->cfg_seq_no += 1;
+ wilc->cfg_frame_in_use = 0;
+ wilc->cfg_frame_offset = 0;
+ wilc->cfg_seq_no += 1;
}
return ret_size;
@@ -1843,92 +1507,69 @@ int wilc_wlan_cfg_get_val(u32 wid, u8 *buffer, u32 buffer_size)
return ret;
}
-void wilc_bus_set_max_speed(void)
-{
-
- /* Increase bus speed to max possible. */
- g_wlan.hif_func.hif_set_max_bus_speed();
-}
-
-void wilc_bus_set_default_speed(void)
-{
-
- /* Restore bus speed to default. */
- g_wlan.hif_func.hif_set_default_bus_speed();
-}
-u32 init_chip(void)
+static u32 init_chip(struct net_device *dev)
{
u32 chipid;
u32 reg, ret = 0;
+ struct wilc_vif *vif;
+ struct wilc *wilc;
- acquire_bus(ACQUIRE_ONLY);
-
- chipid = wilc_get_chipid(true);
+ vif = netdev_priv(dev);
+ wilc = vif->wilc;
+ acquire_bus(wilc, ACQUIRE_ONLY);
+ chipid = wilc_get_chipid(wilc, true);
if ((chipid & 0xfff) != 0xa0) {
- /**
- * Avoid booting from boot ROM. Make sure that Drive IRQN [SDIO platform]
- * or SD_DAT3 [SPI platform] to ?1?
- **/
- /* Set cortus reset register to register control. */
- ret = g_wlan.hif_func.hif_read_reg(0x1118, &reg);
+ ret = wilc->hif_func->hif_read_reg(wilc, 0x1118, &reg);
if (!ret) {
wilc_debug(N_ERR, "[wilc start]: fail read reg 0x1118 ...\n");
return ret;
}
reg |= BIT(0);
- ret = g_wlan.hif_func.hif_write_reg(0x1118, reg);
+ ret = wilc->hif_func->hif_write_reg(wilc, 0x1118, reg);
if (!ret) {
wilc_debug(N_ERR, "[wilc start]: fail write reg 0x1118 ...\n");
return ret;
}
- /**
- * Write branch intruction to IRAM (0x71 trap) at location 0xFFFF0000
- * (Cortus map) or C0000 (AHB map).
- **/
- ret = g_wlan.hif_func.hif_write_reg(0xc0000, 0x71);
+ ret = wilc->hif_func->hif_write_reg(wilc, 0xc0000, 0x71);
if (!ret) {
wilc_debug(N_ERR, "[wilc start]: fail write reg 0xc0000 ...\n");
return ret;
}
}
- release_bus(RELEASE_ONLY);
+ release_bus(wilc, RELEASE_ONLY);
return ret;
-
}
-u32 wilc_get_chipid(u8 update)
+u32 wilc_get_chipid(struct wilc *wilc, u8 update)
{
static u32 chipid;
- /* SDIO can't read into global variables */
- /* Use this variable as a temp, then copy to the global */
u32 tempchipid = 0;
u32 rfrevid;
if (chipid == 0 || update != 0) {
- g_wlan.hif_func.hif_read_reg(0x1000, &tempchipid);
- g_wlan.hif_func.hif_read_reg(0x13f4, &rfrevid);
+ wilc->hif_func->hif_read_reg(wilc, 0x1000, &tempchipid);
+ wilc->hif_func->hif_read_reg(wilc, 0x13f4, &rfrevid);
if (!ISWILC1000(tempchipid)) {
chipid = 0;
goto _fail_;
}
if (tempchipid == 0x1002a0) {
- if (rfrevid == 0x1) { /* 1002A0 */
- } else { /* if (rfrevid == 0x2) */ /* 1002A1 */
+ if (rfrevid == 0x1) {
+ } else {
tempchipid = 0x1002a1;
}
} else if (tempchipid == 0x1002b0) {
- if (rfrevid == 3) { /* 1002B0 */
- } else if (rfrevid == 4) { /* 1002B1 */
+ if (rfrevid == 3) {
+ } else if (rfrevid == 4) {
tempchipid = 0x1002b1;
- } else { /* if(rfrevid == 5) */ /* 1002B2 */
+ } else {
tempchipid = 0x1002b2;
}
- } else {
}
chipid = tempchipid;
@@ -1937,129 +1578,88 @@ _fail_:
return chipid;
}
-int wilc_wlan_init(wilc_wlan_inp_t *inp)
+int wilc_wlan_init(struct net_device *dev)
{
-
int ret = 0;
+ struct wilc_vif *vif = netdev_priv(dev);
+ struct wilc *wilc;
+
+ wilc = vif->wilc;
PRINT_D(INIT_DBG, "Initializing WILC_Wlan ...\n");
- memset((void *)&g_wlan, 0, sizeof(wilc_wlan_dev_t));
-
- /**
- * store the input
- **/
- memcpy((void *)&g_wlan.io_func, (void *)&inp->io_func, sizeof(wilc_wlan_io_func_t));
- /***
- * host interface init
- **/
- if ((inp->io_func.io_type & 0x1) == HIF_SDIO) {
- if (!hif_sdio.hif_init(inp, wilc_debug)) {
- /* EIO 5 */
- ret = -5;
- goto _fail_;
- }
- memcpy((void *)&g_wlan.hif_func, &hif_sdio, sizeof(wilc_hif_func_t));
- } else {
- if ((inp->io_func.io_type & 0x1) == HIF_SPI) {
- /**
- * TODO:
- **/
- if (!hif_spi.hif_init(inp, wilc_debug)) {
- /* EIO 5 */
- ret = -5;
- goto _fail_;
- }
- memcpy((void *)&g_wlan.hif_func, &hif_spi, sizeof(wilc_hif_func_t));
- } else {
- /* EIO 5 */
- ret = -5;
- goto _fail_;
- }
+ if (!wilc->hif_func->hif_init(wilc)) {
+ ret = -EIO;
+ goto _fail_;
}
- /***
- * mac interface init
- **/
if (!wilc_wlan_cfg_init(wilc_debug)) {
- /* ENOBUFS 105 */
- ret = -105;
+ ret = -ENOBUFS;
goto _fail_;
}
- /**
- * alloc tx, rx buffer
- **/
- if (g_wlan.tx_buffer == NULL)
- g_wlan.tx_buffer = kmalloc(LINUX_TX_SIZE, GFP_KERNEL);
- PRINT_D(TX_DBG, "g_wlan.tx_buffer = %p\n", g_wlan.tx_buffer);
+ if (!wilc->tx_buffer)
+ wilc->tx_buffer = kmalloc(LINUX_TX_SIZE, GFP_KERNEL);
+ PRINT_D(TX_DBG, "wilc->tx_buffer = %p\n", wilc->tx_buffer);
- if (g_wlan.tx_buffer == NULL) {
- /* ENOBUFS 105 */
- ret = -105;
+ if (!wilc->tx_buffer) {
+ ret = -ENOBUFS;
PRINT_ER("Can't allocate Tx Buffer");
goto _fail_;
}
-/* rx_buffer is not used unless we activate USE_MEM STATIC which is not applicable, allocating such memory is useless*/
-#if defined (MEMORY_STATIC)
- if (g_wlan.rx_buffer == NULL)
- g_wlan.rx_buffer = kmalloc(LINUX_RX_SIZE, GFP_KERNEL);
- PRINT_D(TX_DBG, "g_wlan.rx_buffer =%p\n", g_wlan.rx_buffer);
- if (g_wlan.rx_buffer == NULL) {
- /* ENOBUFS 105 */
- ret = -105;
+ if (!wilc->rx_buffer)
+ wilc->rx_buffer = kmalloc(LINUX_RX_SIZE, GFP_KERNEL);
+ PRINT_D(TX_DBG, "wilc->rx_buffer =%p\n", wilc->rx_buffer);
+ if (!wilc->rx_buffer) {
+ ret = -ENOBUFS;
PRINT_ER("Can't allocate Rx Buffer");
goto _fail_;
}
-#endif
- if (!init_chip()) {
- /* EIO 5 */
- ret = -5;
+ if (!init_chip(dev)) {
+ ret = -EIO;
goto _fail_;
}
#ifdef TCP_ACK_FILTER
- Init_TCP_tracking();
+ init_tcp_tracking();
#endif
return 1;
_fail_:
- #ifdef MEMORY_STATIC
- kfree(g_wlan.rx_buffer);
- g_wlan.rx_buffer = NULL;
- #endif
- kfree(g_wlan.tx_buffer);
- g_wlan.tx_buffer = NULL;
+ kfree(wilc->rx_buffer);
+ wilc->rx_buffer = NULL;
+ kfree(wilc->tx_buffer);
+ wilc->tx_buffer = NULL;
return ret;
-
}
-u16 Set_machw_change_vir_if(struct net_device *dev, bool bValue)
+u16 wilc_set_machw_change_vir_if(struct net_device *dev, bool value)
{
u16 ret;
u32 reg;
- perInterface_wlan_t *nic;
+ struct wilc_vif *vif;
struct wilc *wilc;
- nic = netdev_priv(dev);
- wilc = nic->wilc;
+ vif = netdev_priv(dev);
+ wilc = vif->wilc;
- /*Reset WILC_CHANGING_VIR_IF register to allow adding futrue keys to CE H/W*/
mutex_lock(&wilc->hif_cs);
- ret = (&g_wlan)->hif_func.hif_read_reg(WILC_CHANGING_VIR_IF, &reg);
+ ret = wilc->hif_func->hif_read_reg(wilc, WILC_CHANGING_VIR_IF,
+ &reg);
if (!ret)
PRINT_ER("Error while Reading reg WILC_CHANGING_VIR_IF\n");
- if (bValue)
+ if (value)
reg |= BIT(31);
else
reg &= ~BIT(31);
- ret = (&g_wlan)->hif_func.hif_write_reg(WILC_CHANGING_VIR_IF, reg);
+ ret = wilc->hif_func->hif_write_reg(wilc, WILC_CHANGING_VIR_IF,
+ reg);
if (!ret)
PRINT_ER("Error while writing reg WILC_CHANGING_VIR_IF\n");
diff --git a/drivers/staging/wilc1000/wilc_wlan.h b/drivers/staging/wilc1000/wilc_wlan.h
index 57e1d5174050..2edd7445f4a3 100644
--- a/drivers/staging/wilc1000/wilc_wlan.h
+++ b/drivers/staging/wilc1000/wilc_wlan.h
@@ -1,155 +1,131 @@
#ifndef WILC_WLAN_H
#define WILC_WLAN_H
+#include <linux/types.h>
-
-#define ISWILC1000(id) (((id & 0xfffff000) == 0x100000) ? 1 : 0)
-
+#define ISWILC1000(id) ((id & 0xfffff000) == 0x100000 ? 1 : 0)
/********************************************
*
* Mac eth header length
*
********************************************/
-#define DRIVER_HANDLER_SIZE 4
-#define MAX_MAC_HDR_LEN 26 /* QOS_MAC_HDR_LEN */
-#define SUB_MSDU_HEADER_LENGTH 14
-#define SNAP_HDR_LEN 8
-#define ETHERNET_HDR_LEN 14
-#define WORD_ALIGNMENT_PAD 0
-
-#define ETH_ETHERNET_HDR_OFFSET (MAX_MAC_HDR_LEN + SUB_MSDU_HEADER_LENGTH + \
- SNAP_HDR_LEN - ETHERNET_HDR_LEN + WORD_ALIGNMENT_PAD)
-
-#define HOST_HDR_OFFSET 4
-#define ETHERNET_HDR_LEN 14
-#define IP_HDR_LEN 20
-#define IP_HDR_OFFSET ETHERNET_HDR_LEN
-#define UDP_HDR_OFFSET (IP_HDR_LEN + IP_HDR_OFFSET)
-#define UDP_HDR_LEN 8
-#define UDP_DATA_OFFSET (UDP_HDR_OFFSET + UDP_HDR_LEN)
-#define ETH_CONFIG_PKT_HDR_LEN UDP_DATA_OFFSET
-
-#define ETH_CONFIG_PKT_HDR_OFFSET (ETH_ETHERNET_HDR_OFFSET + \
- ETH_CONFIG_PKT_HDR_LEN)
-
-/********************************************
- *
- * Endian Conversion
- *
- ********************************************/
-
-#define BYTE_SWAP(val) ((((val) & 0x000000FF) << 24) + \
- (((val) & 0x0000FF00) << 8) + \
- (((val) & 0x00FF0000) >> 8) + \
- (((val) & 0xFF000000) >> 24))
+#define DRIVER_HANDLER_SIZE 4
+#define MAX_MAC_HDR_LEN 26 /* QOS_MAC_HDR_LEN */
+#define SUB_MSDU_HEADER_LENGTH 14
+#define SNAP_HDR_LEN 8
+#define ETHERNET_HDR_LEN 14
+#define WORD_ALIGNMENT_PAD 0
+
+#define ETH_ETHERNET_HDR_OFFSET (MAX_MAC_HDR_LEN + \
+ SUB_MSDU_HEADER_LENGTH + \
+ SNAP_HDR_LEN - \
+ ETHERNET_HDR_LEN + \
+ WORD_ALIGNMENT_PAD)
+
+#define HOST_HDR_OFFSET 4
+#define ETHERNET_HDR_LEN 14
+#define IP_HDR_LEN 20
+#define IP_HDR_OFFSET ETHERNET_HDR_LEN
+#define UDP_HDR_OFFSET (IP_HDR_LEN + IP_HDR_OFFSET)
+#define UDP_HDR_LEN 8
+#define UDP_DATA_OFFSET (UDP_HDR_OFFSET + UDP_HDR_LEN)
+#define ETH_CONFIG_PKT_HDR_LEN UDP_DATA_OFFSET
+
+#define ETH_CONFIG_PKT_HDR_OFFSET (ETH_ETHERNET_HDR_OFFSET + \
+ ETH_CONFIG_PKT_HDR_LEN)
/********************************************
*
* Register Defines
*
********************************************/
-#define WILC_PERIPH_REG_BASE 0x1000
-#define WILC_CHANGING_VIR_IF (0x108c)
-#define WILC_CHIPID (WILC_PERIPH_REG_BASE)
-#define WILC_GLB_RESET_0 (WILC_PERIPH_REG_BASE + 0x400)
-#define WILC_PIN_MUX_0 (WILC_PERIPH_REG_BASE + 0x408)
-#define WILC_HOST_TX_CTRL (WILC_PERIPH_REG_BASE + 0x6c)
-#define WILC_HOST_RX_CTRL_0 (WILC_PERIPH_REG_BASE + 0x70)
-#define WILC_HOST_RX_CTRL_1 (WILC_PERIPH_REG_BASE + 0x74)
-#define WILC_HOST_VMM_CTL (WILC_PERIPH_REG_BASE + 0x78)
-#define WILC_HOST_RX_CTRL (WILC_PERIPH_REG_BASE + 0x80)
-#define WILC_HOST_RX_EXTRA_SIZE (WILC_PERIPH_REG_BASE + 0x84)
-#define WILC_HOST_TX_CTRL_1 (WILC_PERIPH_REG_BASE + 0x88)
-#define WILC_MISC (WILC_PERIPH_REG_BASE + 0x428)
-#define WILC_INTR_REG_BASE (WILC_PERIPH_REG_BASE + 0xa00)
-#define WILC_INTR_ENABLE (WILC_INTR_REG_BASE)
-#define WILC_INTR2_ENABLE (WILC_INTR_REG_BASE + 4)
-
-#define WILC_INTR_POLARITY (WILC_INTR_REG_BASE + 0x10)
-#define WILC_INTR_TYPE (WILC_INTR_REG_BASE + 0x20)
-#define WILC_INTR_CLEAR (WILC_INTR_REG_BASE + 0x30)
-#define WILC_INTR_STATUS (WILC_INTR_REG_BASE + 0x40)
-
-#define WILC_VMM_TBL_SIZE 64
-#define WILC_VMM_TX_TBL_BASE (0x150400)
-#define WILC_VMM_RX_TBL_BASE (0x150500)
-
-#define WILC_VMM_BASE 0x150000
-#define WILC_VMM_CORE_CTL (WILC_VMM_BASE)
-#define WILC_VMM_TBL_CTL (WILC_VMM_BASE + 0x4)
-#define WILC_VMM_TBL_ENTRY (WILC_VMM_BASE + 0x8)
-#define WILC_VMM_TBL0_SIZE (WILC_VMM_BASE + 0xc)
-#define WILC_VMM_TO_HOST_SIZE (WILC_VMM_BASE + 0x10)
-#define WILC_VMM_CORE_CFG (WILC_VMM_BASE + 0x14)
-#define WILC_VMM_TBL_ACTIVE (WILC_VMM_BASE + 040)
-#define WILC_VMM_TBL_STATUS (WILC_VMM_BASE + 0x44)
-
-#define WILC_SPI_REG_BASE 0xe800
-#define WILC_SPI_CTL (WILC_SPI_REG_BASE)
-#define WILC_SPI_MASTER_DMA_ADDR (WILC_SPI_REG_BASE + 0x4)
-#define WILC_SPI_MASTER_DMA_COUNT (WILC_SPI_REG_BASE + 0x8)
-#define WILC_SPI_SLAVE_DMA_ADDR (WILC_SPI_REG_BASE + 0xc)
-#define WILC_SPI_SLAVE_DMA_COUNT (WILC_SPI_REG_BASE + 0x10)
-#define WILC_SPI_TX_MODE (WILC_SPI_REG_BASE + 0x20)
-#define WILC_SPI_PROTOCOL_CONFIG (WILC_SPI_REG_BASE + 0x24)
-#define WILC_SPI_INTR_CTL (WILC_SPI_REG_BASE + 0x2c)
-
-#define WILC_SPI_PROTOCOL_OFFSET (WILC_SPI_PROTOCOL_CONFIG - WILC_SPI_REG_BASE)
-
-#define WILC_AHB_DATA_MEM_BASE 0x30000
-#define WILC_AHB_SHARE_MEM_BASE 0xd0000
-
-#define WILC_VMM_TBL_RX_SHADOW_BASE WILC_AHB_SHARE_MEM_BASE
-#define WILC_VMM_TBL_RX_SHADOW_SIZE (256)
-
-#define WILC_GP_REG_0 0x149c
-#define WILC_GP_REG_1 0x14a0
-
-#define rHAVE_SDIO_IRQ_GPIO_BIT (0)
-#define rHAVE_USE_PMU_BIT (1)
-#define rHAVE_SLEEP_CLK_SRC_RTC_BIT (2)
-#define rHAVE_SLEEP_CLK_SRC_XO_BIT (3)
-#define rHAVE_EXT_PA_INV_TX_RX_BIT (4)
-#define rHAVE_LEGACY_RF_SETTINGS_BIT (5)
-#define rHAVE_XTAL_24_BIT (6)
-#define rHAVE_DISABLE_WILC_UART_BIT (7)
-
-
-#define WILC_HAVE_SDIO_IRQ_GPIO (1 << rHAVE_SDIO_IRQ_GPIO_BIT)
-#define WILC_HAVE_USE_PMU (1 << rHAVE_USE_PMU_BIT)
-#define WILC_HAVE_SLEEP_CLK_SRC_RTC (1 << rHAVE_SLEEP_CLK_SRC_RTC_BIT)
-#define WILC_HAVE_SLEEP_CLK_SRC_XO (1 << rHAVE_SLEEP_CLK_SRC_XO_BIT)
-#define WILC_HAVE_EXT_PA_INV_TX_RX (1 << rHAVE_EXT_PA_INV_TX_RX_BIT)
-#define WILC_HAVE_LEGACY_RF_SETTINGS (1 << rHAVE_LEGACY_RF_SETTINGS_BIT)
-#define WILC_HAVE_XTAL_24 (1 << rHAVE_XTAL_24_BIT)
-#define WILC_HAVE_DISABLE_WILC_UART (1 << rHAVE_DISABLE_WILC_UART_BIT)
-
+#define WILC_PERIPH_REG_BASE 0x1000
+#define WILC_CHANGING_VIR_IF 0x108c
+#define WILC_CHIPID WILC_PERIPH_REG_BASE
+#define WILC_GLB_RESET_0 (WILC_PERIPH_REG_BASE + 0x400)
+#define WILC_PIN_MUX_0 (WILC_PERIPH_REG_BASE + 0x408)
+#define WILC_HOST_TX_CTRL (WILC_PERIPH_REG_BASE + 0x6c)
+#define WILC_HOST_RX_CTRL_0 (WILC_PERIPH_REG_BASE + 0x70)
+#define WILC_HOST_RX_CTRL_1 (WILC_PERIPH_REG_BASE + 0x74)
+#define WILC_HOST_VMM_CTL (WILC_PERIPH_REG_BASE + 0x78)
+#define WILC_HOST_RX_CTRL (WILC_PERIPH_REG_BASE + 0x80)
+#define WILC_HOST_RX_EXTRA_SIZE (WILC_PERIPH_REG_BASE + 0x84)
+#define WILC_HOST_TX_CTRL_1 (WILC_PERIPH_REG_BASE + 0x88)
+#define WILC_MISC (WILC_PERIPH_REG_BASE + 0x428)
+#define WILC_INTR_REG_BASE (WILC_PERIPH_REG_BASE + 0xa00)
+#define WILC_INTR_ENABLE WILC_INTR_REG_BASE
+#define WILC_INTR2_ENABLE (WILC_INTR_REG_BASE + 4)
+
+#define WILC_INTR_POLARITY (WILC_INTR_REG_BASE + 0x10)
+#define WILC_INTR_TYPE (WILC_INTR_REG_BASE + 0x20)
+#define WILC_INTR_CLEAR (WILC_INTR_REG_BASE + 0x30)
+#define WILC_INTR_STATUS (WILC_INTR_REG_BASE + 0x40)
+
+#define WILC_VMM_TBL_SIZE 64
+#define WILC_VMM_TX_TBL_BASE 0x150400
+#define WILC_VMM_RX_TBL_BASE 0x150500
+
+#define WILC_VMM_BASE 0x150000
+#define WILC_VMM_CORE_CTL WILC_VMM_BASE
+#define WILC_VMM_TBL_CTL (WILC_VMM_BASE + 0x4)
+#define WILC_VMM_TBL_ENTRY (WILC_VMM_BASE + 0x8)
+#define WILC_VMM_TBL0_SIZE (WILC_VMM_BASE + 0xc)
+#define WILC_VMM_TO_HOST_SIZE (WILC_VMM_BASE + 0x10)
+#define WILC_VMM_CORE_CFG (WILC_VMM_BASE + 0x14)
+#define WILC_VMM_TBL_ACTIVE (WILC_VMM_BASE + 040)
+#define WILC_VMM_TBL_STATUS (WILC_VMM_BASE + 0x44)
+
+#define WILC_SPI_REG_BASE 0xe800
+#define WILC_SPI_CTL WILC_SPI_REG_BASE
+#define WILC_SPI_MASTER_DMA_ADDR (WILC_SPI_REG_BASE + 0x4)
+#define WILC_SPI_MASTER_DMA_COUNT (WILC_SPI_REG_BASE + 0x8)
+#define WILC_SPI_SLAVE_DMA_ADDR (WILC_SPI_REG_BASE + 0xc)
+#define WILC_SPI_SLAVE_DMA_COUNT (WILC_SPI_REG_BASE + 0x10)
+#define WILC_SPI_TX_MODE (WILC_SPI_REG_BASE + 0x20)
+#define WILC_SPI_PROTOCOL_CONFIG (WILC_SPI_REG_BASE + 0x24)
+#define WILC_SPI_INTR_CTL (WILC_SPI_REG_BASE + 0x2c)
+
+#define WILC_SPI_PROTOCOL_OFFSET (WILC_SPI_PROTOCOL_CONFIG - \
+ WILC_SPI_REG_BASE)
+
+#define WILC_AHB_DATA_MEM_BASE 0x30000
+#define WILC_AHB_SHARE_MEM_BASE 0xd0000
+
+#define WILC_VMM_TBL_RX_SHADOW_BASE WILC_AHB_SHARE_MEM_BASE
+#define WILC_VMM_TBL_RX_SHADOW_SIZE 256
+
+#define WILC_GP_REG_0 0x149c
+#define WILC_GP_REG_1 0x14a0
+
+#define WILC_HAVE_SDIO_IRQ_GPIO BIT(0)
+#define WILC_HAVE_USE_PMU BIT(1)
+#define WILC_HAVE_SLEEP_CLK_SRC_RTC BIT(2)
+#define WILC_HAVE_SLEEP_CLK_SRC_XO BIT(3)
+#define WILC_HAVE_EXT_PA_INV_TX_RX BIT(4)
+#define WILC_HAVE_LEGACY_RF_SETTINGS BIT(5)
+#define WILC_HAVE_XTAL_24 BIT(6)
+#define WILC_HAVE_DISABLE_WILC_UART BIT(7)
/********************************************
*
* Wlan Defines
*
********************************************/
-#define WILC_CFG_PKT 1
-#define WILC_NET_PKT 0
-#define WILC_MGMT_PKT 2
+#define WILC_CFG_PKT 1
+#define WILC_NET_PKT 0
+#define WILC_MGMT_PKT 2
-#define WILC_CFG_SET 1
-#define WILC_CFG_QUERY 0
-
-#define WILC_CFG_RSP 1
-#define WILC_CFG_RSP_STATUS 2
-#define WILC_CFG_RSP_SCAN 3
-
-#ifdef WILC_SDIO
-#define WILC_PLL_TO 4
-#else
-#define WILC_PLL_TO 2
-#endif
+#define WILC_CFG_SET 1
+#define WILC_CFG_QUERY 0
+#define WILC_CFG_RSP 1
+#define WILC_CFG_RSP_STATUS 2
+#define WILC_CFG_RSP_SCAN 3
-#define ABORT_INT BIT(31)
+#define WILC_PLL_TO_SDIO 4
+#define WILC_PLL_TO_SPI 2
+#define ABORT_INT BIT(31)
/*******************************************/
/* E0 and later Interrupt flags. */
@@ -165,15 +141,15 @@
/* 20: INT4 flag */
/* 21: INT5 flag */
/*******************************************/
-#define IRG_FLAGS_OFFSET 16
-#define IRQ_DMA_WD_CNT_MASK ((1ul << IRG_FLAGS_OFFSET) - 1)
-#define INT_0 (1 << (IRG_FLAGS_OFFSET))
-#define INT_1 (1 << (IRG_FLAGS_OFFSET + 1))
-#define INT_2 (1 << (IRG_FLAGS_OFFSET + 2))
-#define INT_3 (1 << (IRG_FLAGS_OFFSET + 3))
-#define INT_4 (1 << (IRG_FLAGS_OFFSET + 4))
-#define INT_5 (1 << (IRG_FLAGS_OFFSET + 5))
-#define MAX_NUM_INT (6)
+#define IRG_FLAGS_OFFSET 16
+#define IRQ_DMA_WD_CNT_MASK ((1ul << IRG_FLAGS_OFFSET) - 1)
+#define INT_0 BIT(IRG_FLAGS_OFFSET)
+#define INT_1 BIT(IRG_FLAGS_OFFSET + 1)
+#define INT_2 BIT(IRG_FLAGS_OFFSET + 2)
+#define INT_3 BIT(IRG_FLAGS_OFFSET + 3)
+#define INT_4 BIT(IRG_FLAGS_OFFSET + 4)
+#define INT_5 BIT(IRG_FLAGS_OFFSET + 5)
+#define MAX_NUM_INT 6
/*******************************************/
/* E0 and later Interrupt flags. */
@@ -188,30 +164,28 @@
/* 7: Select VMM table 2 */
/* 8: Enable VMM */
/*******************************************/
-#define CLR_INT0 BIT(0)
-#define CLR_INT1 BIT(1)
-#define CLR_INT2 BIT(2)
-#define CLR_INT3 BIT(3)
-#define CLR_INT4 BIT(4)
-#define CLR_INT5 BIT(5)
-#define SEL_VMM_TBL0 BIT(6)
-#define SEL_VMM_TBL1 BIT(7)
-#define EN_VMM BIT(8)
-
-#define DATA_INT_EXT INT_0
-#define PLL_INT_EXT INT_1
-#define SLEEP_INT_EXT INT_2
-#define ALL_INT_EXT (DATA_INT_EXT | PLL_INT_EXT | SLEEP_INT_EXT)
-#define NUM_INT_EXT (3)
-
-#define DATA_INT_CLR CLR_INT0
-#define PLL_INT_CLR CLR_INT1
-#define SLEEP_INT_CLR CLR_INT2
-
-#define ENABLE_RX_VMM (SEL_VMM_TBL1 | EN_VMM)
-#define ENABLE_TX_VMM (SEL_VMM_TBL0 | EN_VMM)
-
-
+#define CLR_INT0 BIT(0)
+#define CLR_INT1 BIT(1)
+#define CLR_INT2 BIT(2)
+#define CLR_INT3 BIT(3)
+#define CLR_INT4 BIT(4)
+#define CLR_INT5 BIT(5)
+#define SEL_VMM_TBL0 BIT(6)
+#define SEL_VMM_TBL1 BIT(7)
+#define EN_VMM BIT(8)
+
+#define DATA_INT_EXT INT_0
+#define PLL_INT_EXT INT_1
+#define SLEEP_INT_EXT INT_2
+#define ALL_INT_EXT (DATA_INT_EXT | PLL_INT_EXT | SLEEP_INT_EXT)
+#define NUM_INT_EXT 3
+
+#define DATA_INT_CLR CLR_INT0
+#define PLL_INT_CLR CLR_INT1
+#define SLEEP_INT_CLR CLR_INT2
+
+#define ENABLE_RX_VMM (SEL_VMM_TBL1 | EN_VMM)
+#define ENABLE_TX_VMM (SEL_VMM_TBL0 | EN_VMM)
/*time for expiring the semaphores of cfg packets*/
#define CFG_PKTS_TIMEOUT 2000
/********************************************
@@ -231,7 +205,7 @@ struct txq_entry_t {
struct txq_entry_t *next;
struct txq_entry_t *prev;
int type;
- int tcp_PendingAck_index;
+ int tcp_pending_ack_idx;
u8 *buffer;
int buffer_size;
void *priv;
@@ -250,25 +224,26 @@ struct rxq_entry_t {
* Host IF Structure
*
********************************************/
+struct wilc;
+struct wilc_hif_func {
+ int (*hif_init)(struct wilc *);
+ int (*hif_deinit)(struct wilc *);
+ int (*hif_read_reg)(struct wilc *, u32, u32 *);
+ int (*hif_write_reg)(struct wilc *, u32, u32);
+ int (*hif_block_rx)(struct wilc *, u32, u8 *, u32);
+ int (*hif_block_tx)(struct wilc *, u32, u8 *, u32);
+ int (*hif_read_int)(struct wilc *, u32 *);
+ int (*hif_clear_int_ext)(struct wilc *, u32);
+ int (*hif_read_size)(struct wilc *, u32 *);
+ int (*hif_block_tx_ext)(struct wilc *, u32, u8 *, u32);
+ int (*hif_block_rx_ext)(struct wilc *, u32, u8 *, u32);
+ int (*hif_sync_ext)(struct wilc *, int);
+ int (*enable_interrupt)(struct wilc *nic);
+ void (*disable_interrupt)(struct wilc *nic);
+};
-typedef struct {
- int (*hif_init)(wilc_wlan_inp_t *, wilc_debug_func);
- int (*hif_deinit)(void *);
- int (*hif_read_reg)(u32, u32 *);
- int (*hif_write_reg)(u32, u32);
- int (*hif_block_rx)(u32, u8 *, u32);
- int (*hif_block_tx)(u32, u8 *, u32);
- int (*hif_sync)(void);
- int (*hif_clear_int)(void);
- int (*hif_read_int)(u32 *);
- int (*hif_clear_int_ext)(u32);
- int (*hif_read_size)(u32 *);
- int (*hif_block_tx_ext)(u32, u8 *, u32);
- int (*hif_block_rx_ext)(u32, u8 *, u32);
- int (*hif_sync_ext)(int);
- void (*hif_set_max_bus_speed)(void);
- void (*hif_set_default_bus_speed)(void);
-} wilc_hif_func_t;
+extern const struct wilc_hif_func wilc_hif_spi;
+extern const struct wilc_hif_func wilc_hif_sdio;
/********************************************
*
@@ -276,37 +251,50 @@ typedef struct {
*
********************************************/
-#define MAX_CFG_FRAME_SIZE 1468
+#define MAX_CFG_FRAME_SIZE 1468
-typedef struct {
+struct wilc_cfg_frame {
u8 ether_header[14];
u8 ip_header[20];
u8 udp_header[8];
u8 wid_header[8];
u8 frame[MAX_CFG_FRAME_SIZE];
-} wilc_cfg_frame_t;
-
-typedef struct {
- int (*wlan_tx)(u8 *, u32, wilc_tx_complete_func_t);
-} wilc_wlan_cfg_func_t;
+};
-typedef struct {
+struct wilc_cfg_rsp {
int type;
u32 seq_no;
-} wilc_cfg_rsp_t;
+};
-int wilc_wlan_firmware_download(const u8 *buffer, u32 buffer_size);
-int wilc_wlan_start(void);
-int wilc_wlan_stop(void);
+struct wilc;
+
+int wilc_wlan_firmware_download(struct wilc *wilc, const u8 *buffer, u32 buffer_size);
+int wilc_wlan_start(struct wilc *);
+int wilc_wlan_stop(struct wilc *);
int wilc_wlan_txq_add_net_pkt(struct net_device *dev, void *priv, u8 *buffer,
u32 buffer_size, wilc_tx_complete_func_t func);
-int wilc_wlan_handle_txq(struct net_device *dev, u32 *pu32TxqCount);
-void wilc_handle_isr(void *wilc);
+int wilc_wlan_handle_txq(struct net_device *dev, u32 *txq_count);
+void wilc_handle_isr(struct wilc *wilc);
void wilc_wlan_cleanup(struct net_device *dev);
-int wilc_wlan_cfg_set(int start, u32 wid, u8 *buffer, u32 buffer_size,
- int commit, u32 drvHandler);
-int wilc_wlan_cfg_get(int start, u32 wid, int commit, u32 drvHandler);
+int wilc_wlan_cfg_set(struct wilc *wilc, int start, u32 wid, u8 *buffer,
+ u32 buffer_size, int commit, u32 drv_handler);
+int wilc_wlan_cfg_get(struct wilc *wilc, int start, u32 wid, int commit,
+ u32 drv_handler);
int wilc_wlan_cfg_get_val(u32 wid, u8 *buffer, u32 buffer_size);
-int wilc_wlan_txq_add_mgmt_pkt(void *priv, u8 *buffer, u32 buffer_size,
- wilc_tx_complete_func_t func);
+int wilc_wlan_txq_add_mgmt_pkt(struct net_device *dev, void *priv, u8 *buffer,
+ u32 buffer_size, wilc_tx_complete_func_t func);
+void wilc_chip_sleep_manually(struct wilc *wilc);
+
+void wilc_enable_tcp_ack_filter(bool value);
+int wilc_wlan_get_num_conn_ifcs(struct wilc *);
+int wilc_mac_xmit(struct sk_buff *skb, struct net_device *dev);
+
+int wilc_mac_open(struct net_device *ndev);
+int wilc_mac_close(struct net_device *ndev);
+
+int wilc_wlan_set_bssid(struct net_device *wilc_netdev, u8 *pBSSID);
+void WILC_WFI_p2p_rx(struct net_device *dev, u8 *buff, u32 size);
+
+extern bool wilc_enable_ps;
+
#endif
diff --git a/drivers/staging/wilc1000/wilc_wlan_cfg.c b/drivers/staging/wilc1000/wilc_wlan_cfg.c
index a34a81cdeb5e..b72c77bb35f1 100644
--- a/drivers/staging/wilc1000/wilc_wlan_cfg.c
+++ b/drivers/staging/wilc1000/wilc_wlan_cfg.c
@@ -275,9 +275,7 @@ static void wilc_wlan_parse_response_frame(u8 *info, int size)
while (size > 0) {
i = 0;
wid = info[0] | (info[1] << 8);
-#ifdef BIG_ENDIAN
- wid = BYTE_SWAP(wid);
-#endif
+ wid = cpu_to_le32(wid);
PRINT_INFO(GENERIC_DBG, "Processing response for %d seq %d\n", wid, seq++);
switch ((wid >> 12) & 0x7) {
case WID_CHAR:
@@ -300,11 +298,7 @@ static void wilc_wlan_parse_response_frame(u8 *info, int size)
break;
if (g_cfg_hword[i].id == wid) {
-#ifdef BIG_ENDIAN
- g_cfg_hword[i].val = (info[3] << 8) | (info[4]);
-#else
- g_cfg_hword[i].val = info[3] | (info[4] << 8);
-#endif
+ g_cfg_hword[i].val = cpu_to_le16(info[3] | (info[4] << 8));
break;
}
i++;
@@ -318,11 +312,7 @@ static void wilc_wlan_parse_response_frame(u8 *info, int size)
break;
if (g_cfg_word[i].id == wid) {
-#ifdef BIG_ENDIAN
- g_cfg_word[i].val = (info[3] << 24) | (info[4] << 16) | (info[5] << 8) | (info[6]);
-#else
- g_cfg_word[i].val = info[3] | (info[4] << 8) | (info[5] << 16) | (info[6] << 24);
-#endif
+ g_cfg_word[i].val = cpu_to_le32(info[3] | (info[4] << 8) | (info[5] << 16) | (info[6] << 24));
break;
}
i++;
@@ -505,7 +495,8 @@ int wilc_wlan_cfg_get_wid_value(u16 wid, u8 *buffer, u32 buffer_size)
return ret;
}
-int wilc_wlan_cfg_indicate_rx(u8 *frame, int size, wilc_cfg_rsp_t *rsp)
+int wilc_wlan_cfg_indicate_rx(struct wilc *wilc, u8 *frame, int size,
+ struct wilc_cfg_rsp *rsp)
{
int ret = 1;
u8 msg_type;
@@ -532,17 +523,17 @@ int wilc_wlan_cfg_indicate_rx(u8 *frame, int size, wilc_cfg_rsp_t *rsp)
rsp->seq_no = msg_id;
/*call host interface info parse as well*/
PRINT_INFO(RX_DBG, "Info message received\n");
- GnrlAsyncInfoReceived(frame - 4, size + 4);
+ wilc_gnrl_async_info_received(wilc, frame - 4, size + 4);
break;
case 'N':
- NetworkInfoReceived(frame - 4, size + 4);
+ wilc_network_info_received(wilc, frame - 4, size + 4);
rsp->type = 0;
break;
case 'S':
PRINT_INFO(RX_DBG, "Scan Notification Received\n");
- host_int_ScanCompleteReceived(frame - 4, size + 4);
+ wilc_scan_complete_received(wilc, frame - 4, size + 4);
break;
default:
diff --git a/drivers/staging/wilc1000/wilc_wlan_cfg.h b/drivers/staging/wilc1000/wilc_wlan_cfg.h
index 30e60ec4d29f..5f74eb83562f 100644
--- a/drivers/staging/wilc1000/wilc_wlan_cfg.h
+++ b/drivers/staging/wilc1000/wilc_wlan_cfg.h
@@ -30,10 +30,12 @@ typedef struct {
u8 *str;
} wilc_cfg_str_t;
+struct wilc;
int wilc_wlan_cfg_set_wid(u8 *frame, u32 offset, u16 id, u8 *buf, int size);
int wilc_wlan_cfg_get_wid(u8 *frame, u32 offset, u16 id);
int wilc_wlan_cfg_get_wid_value(u16 wid, u8 *buffer, u32 buffer_size);
-int wilc_wlan_cfg_indicate_rx(u8 *frame, int size, wilc_cfg_rsp_t *rsp);
+int wilc_wlan_cfg_indicate_rx(struct wilc *wilc, u8 *frame, int size,
+ struct wilc_cfg_rsp *rsp);
int wilc_wlan_cfg_init(wilc_debug_func func);
#endif
diff --git a/drivers/staging/wilc1000/wilc_wlan_if.h b/drivers/staging/wilc1000/wilc_wlan_if.h
index be972afe6e62..618903caff54 100644
--- a/drivers/staging/wilc1000/wilc_wlan_if.h
+++ b/drivers/staging/wilc1000/wilc_wlan_if.h
@@ -12,6 +12,7 @@
#include <linux/semaphore.h>
#include "linux_wlan_common.h"
+#include <linux/netdevice.h>
/********************************************
*
@@ -71,26 +72,6 @@ typedef struct {
u32 block_size;
} sdio_cmd53_t;
-typedef struct {
- int io_type;
- int (*io_init)(void *);
- void (*io_deinit)(void *);
- union {
- struct {
- int (*sdio_cmd52)(sdio_cmd52_t *);
- int (*sdio_cmd53)(sdio_cmd53_t *);
- int (*sdio_set_max_speed)(void);
- int (*sdio_set_default_speed)(void);
- } sdio;
- struct {
- int (*spi_max_speed)(void);
- int (*spi_tx)(u8 *, u32);
- int (*spi_rx)(u8 *, u32);
- int (*spi_trx)(u8 *, u8 *, u32);
- } spi;
- } u;
-} wilc_wlan_io_func_t;
-
#define WILC_MAC_INDICATE_STATUS 0x1
#define WILC_MAC_STATUS_INIT -1
#define WILC_MAC_STATUS_READY 0
@@ -98,15 +79,6 @@ typedef struct {
#define WILC_MAC_INDICATE_SCAN 0x2
-typedef struct {
- void *os_private;
-} wilc_wlan_os_context_t;
-
-typedef struct {
- wilc_wlan_os_context_t os_context;
- wilc_wlan_io_func_t io_func;
-} wilc_wlan_inp_t;
-
struct tx_complete_data {
int size;
void *buff;
@@ -315,7 +287,7 @@ typedef enum {
SW_TRIGGER_ABORT,
} TX_ABORT_OPTION_T;
-enum WID_TYPE {
+enum wid_type {
WID_CHAR = 0,
WID_SHORT = 1,
WID_INT = 2,
@@ -937,10 +909,10 @@ typedef enum {
WID_MAX = 0xFFFF
} WID_T;
-int wilc_wlan_init(wilc_wlan_inp_t *inp);
-
+struct wilc;
+int wilc_wlan_init(struct net_device *dev);
void wilc_bus_set_max_speed(void);
void wilc_bus_set_default_speed(void);
-u32 wilc_get_chipid(u8 update);
+u32 wilc_get_chipid(struct wilc *wilc, u8 update);
#endif
diff --git a/drivers/staging/wlan-ng/hfa384x_usb.c b/drivers/staging/wlan-ng/hfa384x_usb.c
index 444ebed7313a..7551ac25d89d 100644
--- a/drivers/staging/wlan-ng/hfa384x_usb.c
+++ b/drivers/staging/wlan-ng/hfa384x_usb.c
@@ -177,9 +177,6 @@ static void hfa384x_usbin_rx(wlandevice_t *wlandev, struct sk_buff *skb);
static void hfa384x_usbin_info(wlandevice_t *wlandev, hfa384x_usbin_t *usbin);
-static void
-hfa384x_usbout_tx(wlandevice_t *wlandev, hfa384x_usbout_t *usbout);
-
static void hfa384x_usbin_ctlx(hfa384x_t *hw, hfa384x_usbin_t *usbin,
int urb_status);
@@ -3504,7 +3501,7 @@ static void hfa384x_usbin_rx(wlandevice_t *wlandev, struct sk_buff *skb)
rxmeta->signal = usbin->rxfrm.desc.signal - hw->dbmadjust;
rxmeta->noise = usbin->rxfrm.desc.silence - hw->dbmadjust;
- prism2sta_ev_rx(wlandev, skb);
+ p80211netdev_rx(wlandev, skb);
break;
@@ -3628,7 +3625,7 @@ static void hfa384x_int_rxmonitor(wlandevice_t *wlandev,
}
/* pass it back up */
- prism2sta_ev_rx(wlandev, skb);
+ p80211netdev_rx(wlandev, skb);
}
/*----------------------------------------------------------------
@@ -3674,7 +3671,6 @@ static void hfa384x_usbin_info(wlandevice_t *wlandev, hfa384x_usbin_t *usbin)
static void hfa384x_usbout_callback(struct urb *urb)
{
wlandevice_t *wlandev = urb->context;
- hfa384x_usbout_t *usbout = urb->transfer_buffer;
#ifdef DEBUG_USB
dbprint_urb(urb);
@@ -3683,7 +3679,7 @@ static void hfa384x_usbout_callback(struct urb *urb)
if (wlandev && wlandev->netdev) {
switch (urb->status) {
case 0:
- hfa384x_usbout_tx(wlandev, usbout);
+ prism2sta_ev_alloc(wlandev);
break;
case -EPIPE:
@@ -4038,30 +4034,6 @@ static int hfa384x_usbctlx_submit(hfa384x_t *hw, hfa384x_usbctlx_t *ctlx)
}
/*----------------------------------------------------------------
-* hfa384x_usbout_tx
-*
-* At this point we have finished a send of a frame. Mark the URB
-* as available and call ev_alloc to notify higher layers we're
-* ready for more.
-*
-* Arguments:
-* wlandev wlan device
-* usbout ptr to the usb transfer buffer
-*
-* Returns:
-* nothing
-*
-* Side effects:
-*
-* Call context:
-* interrupt
-----------------------------------------------------------------*/
-static void hfa384x_usbout_tx(wlandevice_t *wlandev, hfa384x_usbout_t *usbout)
-{
- prism2sta_ev_alloc(wlandev);
-}
-
-/*----------------------------------------------------------------
* hfa384x_isgood_pdrcore
*
* Quick check of PDR codes.
diff --git a/drivers/staging/wlan-ng/p80211netdev.h b/drivers/staging/wlan-ng/p80211netdev.h
index c547e1cb4c0d..810ee68aa18e 100644
--- a/drivers/staging/wlan-ng/p80211netdev.h
+++ b/drivers/staging/wlan-ng/p80211netdev.h
@@ -141,7 +141,6 @@ typedef struct p80211_frmrx_t {
struct iw_statistics *p80211wext_get_wireless_stats(netdevice_t *dev);
/* wireless extensions' ioctls */
extern struct iw_handler_def p80211wext_handler_def;
-int p80211wext_event_associated(struct wlandevice *wlandev, int assoc);
/* WEP stuff */
#define NUM_WEPKEYS 4
diff --git a/drivers/staging/wlan-ng/prism2mgmt.h b/drivers/staging/wlan-ng/prism2mgmt.h
index 16f123959104..7a9f424607b7 100644
--- a/drivers/staging/wlan-ng/prism2mgmt.h
+++ b/drivers/staging/wlan-ng/prism2mgmt.h
@@ -68,7 +68,6 @@ u32 prism2sta_ifstate(wlandevice_t *wlandev, u32 ifstate);
void prism2sta_ev_info(wlandevice_t *wlandev, hfa384x_InfFrame_t *inf);
void prism2sta_ev_txexc(wlandevice_t *wlandev, u16 status);
void prism2sta_ev_tx(wlandevice_t *wlandev, u16 status);
-void prism2sta_ev_rx(wlandevice_t *wlandev, struct sk_buff *skb);
void prism2sta_ev_alloc(wlandevice_t *wlandev);
int prism2mgmt_mibset_mibget(wlandevice_t *wlandev, void *msgp);
diff --git a/drivers/staging/wlan-ng/prism2mib.c b/drivers/staging/wlan-ng/prism2mib.c
index b4a15ef3a405..cdda07d1c268 100644
--- a/drivers/staging/wlan-ng/prism2mib.c
+++ b/drivers/staging/wlan-ng/prism2mib.c
@@ -660,7 +660,6 @@ static int prism2mib_fragmentationthreshold(struct mibrec *mib,
struct p80211msg_dot11req_mibset *msg,
void *data)
{
- int result;
u32 *uint32 = (u32 *) data;
if (!isget)
@@ -672,9 +671,7 @@ static int prism2mib_fragmentationthreshold(struct mibrec *mib,
return 0;
}
- result = prism2mib_uint32(mib, isget, wlandev, hw, msg, data);
-
- return result;
+ return prism2mib_uint32(mib, isget, wlandev, hw, msg, data);
}
/*----------------------------------------------------------------
diff --git a/drivers/staging/wlan-ng/prism2sta.c b/drivers/staging/wlan-ng/prism2sta.c
index c57f48a1d8df..131223afd918 100644
--- a/drivers/staging/wlan-ng/prism2sta.c
+++ b/drivers/staging/wlan-ng/prism2sta.c
@@ -1837,27 +1837,6 @@ void prism2sta_ev_tx(wlandevice_t *wlandev, u16 status)
}
/*
- * prism2sta_ev_rx
- *
- * Handles the Rx event.
- *
- * Arguments:
- * wlandev wlan device structure
- *
- * Returns:
- * nothing
- *
- * Side effects:
- *
- * Call context:
- * interrupt
- */
-void prism2sta_ev_rx(wlandevice_t *wlandev, struct sk_buff *skb)
-{
- p80211netdev_rx(wlandev, skb);
-}
-
-/*
* prism2sta_ev_alloc
*
* Handles the Alloc event.
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index 342a07c58d89..72204fbf2bb1 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -4074,6 +4074,17 @@ reject:
return iscsit_add_reject(conn, ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
}
+static bool iscsi_target_check_conn_state(struct iscsi_conn *conn)
+{
+ bool ret;
+
+ spin_lock_bh(&conn->state_lock);
+ ret = (conn->conn_state != TARG_CONN_STATE_LOGGED_IN);
+ spin_unlock_bh(&conn->state_lock);
+
+ return ret;
+}
+
int iscsi_target_rx_thread(void *arg)
{
int ret, rc;
@@ -4091,7 +4102,7 @@ int iscsi_target_rx_thread(void *arg)
* incoming iscsi/tcp socket I/O, and/or failing the connection.
*/
rc = wait_for_completion_interruptible(&conn->rx_login_comp);
- if (rc < 0)
+ if (rc < 0 || iscsi_target_check_conn_state(conn))
return 0;
if (conn->conn_transport->transport_type == ISCSI_INFINIBAND) {
diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c
index 5c964c09c89f..9fc9117d0f22 100644
--- a/drivers/target/iscsi/iscsi_target_nego.c
+++ b/drivers/target/iscsi/iscsi_target_nego.c
@@ -388,6 +388,7 @@ err:
if (login->login_complete) {
if (conn->rx_thread && conn->rx_thread_active) {
send_sig(SIGINT, conn->rx_thread, 1);
+ complete(&conn->rx_login_comp);
kthread_stop(conn->rx_thread);
}
if (conn->tx_thread && conn->tx_thread_active) {
diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c
index 51d1734d5390..2cbea2af7cd0 100644
--- a/drivers/target/iscsi/iscsi_target_parameters.c
+++ b/drivers/target/iscsi/iscsi_target_parameters.c
@@ -208,7 +208,7 @@ int iscsi_create_default_params(struct iscsi_param_list **param_list_ptr)
if (!pl) {
pr_err("Unable to allocate memory for"
" struct iscsi_param_list.\n");
- return -1 ;
+ return -ENOMEM;
}
INIT_LIST_HEAD(&pl->param_list);
INIT_LIST_HEAD(&pl->extra_response_list);
@@ -578,7 +578,7 @@ int iscsi_copy_param_list(
param_list = kzalloc(sizeof(struct iscsi_param_list), GFP_KERNEL);
if (!param_list) {
pr_err("Unable to allocate memory for struct iscsi_param_list.\n");
- return -1;
+ return -ENOMEM;
}
INIT_LIST_HEAD(&param_list->param_list);
INIT_LIST_HEAD(&param_list->extra_response_list);
@@ -629,7 +629,7 @@ int iscsi_copy_param_list(
err_out:
iscsi_release_param_list(param_list);
- return -1;
+ return -ENOMEM;
}
static void iscsi_release_extra_responses(struct iscsi_param_list *param_list)
@@ -729,7 +729,7 @@ static int iscsi_add_notunderstood_response(
if (!extra_response) {
pr_err("Unable to allocate memory for"
" struct iscsi_extra_response.\n");
- return -1;
+ return -ENOMEM;
}
INIT_LIST_HEAD(&extra_response->er_list);
@@ -1370,7 +1370,7 @@ int iscsi_decode_text_input(
tmpbuf = kzalloc(length + 1, GFP_KERNEL);
if (!tmpbuf) {
pr_err("Unable to allocate %u + 1 bytes for tmpbuf.\n", length);
- return -1;
+ return -ENOMEM;
}
memcpy(tmpbuf, textbuf, length);
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
index 0b4b2a67d9f9..98698d875742 100644
--- a/drivers/target/target_core_sbc.c
+++ b/drivers/target/target_core_sbc.c
@@ -371,7 +371,8 @@ sbc_setup_write_same(struct se_cmd *cmd, unsigned char *flags, struct sbc_ops *o
return 0;
}
-static sense_reason_t xdreadwrite_callback(struct se_cmd *cmd, bool success)
+static sense_reason_t xdreadwrite_callback(struct se_cmd *cmd, bool success,
+ int *post_ret)
{
unsigned char *buf, *addr;
struct scatterlist *sg;
@@ -437,7 +438,8 @@ sbc_execute_rw(struct se_cmd *cmd)
cmd->data_direction);
}
-static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success)
+static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success,
+ int *post_ret)
{
struct se_device *dev = cmd->se_dev;
@@ -447,8 +449,10 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success)
* sent to the backend driver.
*/
spin_lock_irq(&cmd->t_state_lock);
- if ((cmd->transport_state & CMD_T_SENT) && !cmd->scsi_status)
+ if ((cmd->transport_state & CMD_T_SENT) && !cmd->scsi_status) {
cmd->se_cmd_flags |= SCF_COMPARE_AND_WRITE_POST;
+ *post_ret = 1;
+ }
spin_unlock_irq(&cmd->t_state_lock);
/*
@@ -460,7 +464,8 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success)
return TCM_NO_SENSE;
}
-static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool success)
+static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool success,
+ int *post_ret)
{
struct se_device *dev = cmd->se_dev;
struct scatterlist *write_sg = NULL, *sg;
@@ -556,11 +561,11 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool succes
if (block_size < PAGE_SIZE) {
sg_set_page(&write_sg[i], m.page, block_size,
- block_size);
+ m.piter.sg->offset + block_size);
} else {
sg_miter_next(&m);
sg_set_page(&write_sg[i], m.page, block_size,
- 0);
+ m.piter.sg->offset);
}
len -= block_size;
i++;
diff --git a/drivers/target/target_core_stat.c b/drivers/target/target_core_stat.c
index 273c72b2b83d..81a6b3e07687 100644
--- a/drivers/target/target_core_stat.c
+++ b/drivers/target/target_core_stat.c
@@ -246,7 +246,7 @@ static ssize_t target_stat_lu_prod_show(struct config_item *item, char *page)
char str[sizeof(dev->t10_wwn.model)+1];
/* scsiLuProductId */
- for (i = 0; i < sizeof(dev->t10_wwn.vendor); i++)
+ for (i = 0; i < sizeof(dev->t10_wwn.model); i++)
str[i] = ISPRINT(dev->t10_wwn.model[i]) ?
dev->t10_wwn.model[i] : ' ';
str[i] = '\0';
diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c
index 5b2820312310..28fb3016370f 100644
--- a/drivers/target/target_core_tmr.c
+++ b/drivers/target/target_core_tmr.c
@@ -130,6 +130,9 @@ void core_tmr_abort_task(
if (tmr->ref_task_tag != ref_tag)
continue;
+ if (!kref_get_unless_zero(&se_cmd->cmd_kref))
+ continue;
+
printk("ABORT_TASK: Found referenced %s task_tag: %llu\n",
se_cmd->se_tfo->get_fabric_name(), ref_tag);
@@ -139,13 +142,15 @@ void core_tmr_abort_task(
" skipping\n", ref_tag);
spin_unlock(&se_cmd->t_state_lock);
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
+
+ target_put_sess_cmd(se_cmd);
+
goto out;
}
se_cmd->transport_state |= CMD_T_ABORTED;
spin_unlock(&se_cmd->t_state_lock);
list_del_init(&se_cmd->se_cmd_list);
- kref_get(&se_cmd->cmd_kref);
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
cancel_work_sync(&se_cmd->work);
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 5bacc7b5ed6d..4fdcee2006d1 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -1658,7 +1658,7 @@ bool target_stop_cmd(struct se_cmd *cmd, unsigned long *flags)
void transport_generic_request_failure(struct se_cmd *cmd,
sense_reason_t sense_reason)
{
- int ret = 0;
+ int ret = 0, post_ret = 0;
pr_debug("-----[ Storage Engine Exception for cmd: %p ITT: 0x%08llx"
" CDB: 0x%02x\n", cmd, cmd->tag, cmd->t_task_cdb[0]);
@@ -1680,7 +1680,7 @@ void transport_generic_request_failure(struct se_cmd *cmd,
*/
if ((cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) &&
cmd->transport_complete_callback)
- cmd->transport_complete_callback(cmd, false);
+ cmd->transport_complete_callback(cmd, false, &post_ret);
switch (sense_reason) {
case TCM_NON_EXISTENT_LUN:
@@ -2068,11 +2068,13 @@ static void target_complete_ok_work(struct work_struct *work)
*/
if (cmd->transport_complete_callback) {
sense_reason_t rc;
+ bool caw = (cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE);
+ bool zero_dl = !(cmd->data_length);
+ int post_ret = 0;
- rc = cmd->transport_complete_callback(cmd, true);
- if (!rc && !(cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE_POST)) {
- if ((cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) &&
- !cmd->data_length)
+ rc = cmd->transport_complete_callback(cmd, true, &post_ret);
+ if (!rc && !post_ret) {
+ if (caw && zero_dl)
goto queue_rsp;
return;
@@ -2507,23 +2509,24 @@ out:
EXPORT_SYMBOL(target_get_sess_cmd);
static void target_release_cmd_kref(struct kref *kref)
- __releases(&se_cmd->se_sess->sess_cmd_lock)
{
struct se_cmd *se_cmd = container_of(kref, struct se_cmd, cmd_kref);
struct se_session *se_sess = se_cmd->se_sess;
+ unsigned long flags;
+ spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
if (list_empty(&se_cmd->se_cmd_list)) {
- spin_unlock(&se_sess->sess_cmd_lock);
+ spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
se_cmd->se_tfo->release_cmd(se_cmd);
return;
}
if (se_sess->sess_tearing_down && se_cmd->cmd_wait_set) {
- spin_unlock(&se_sess->sess_cmd_lock);
+ spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
complete(&se_cmd->cmd_wait_comp);
return;
}
list_del(&se_cmd->se_cmd_list);
- spin_unlock(&se_sess->sess_cmd_lock);
+ spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
se_cmd->se_tfo->release_cmd(se_cmd);
}
@@ -2539,8 +2542,7 @@ int target_put_sess_cmd(struct se_cmd *se_cmd)
se_cmd->se_tfo->release_cmd(se_cmd);
return 1;
}
- return kref_put_spinlock_irqsave(&se_cmd->cmd_kref, target_release_cmd_kref,
- &se_sess->sess_cmd_lock);
+ return kref_put(&se_cmd->cmd_kref, target_release_cmd_kref);
}
EXPORT_SYMBOL(target_put_sess_cmd);
diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c
index 937cebf76633..5e6d6cb348fc 100644
--- a/drivers/target/target_core_user.c
+++ b/drivers/target/target_core_user.c
@@ -638,7 +638,7 @@ static int tcmu_check_expired_cmd(int id, void *p, void *data)
if (test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags))
return 0;
- if (!time_after(cmd->deadline, jiffies))
+ if (!time_after(jiffies, cmd->deadline))
return 0;
set_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags);
@@ -1101,8 +1101,6 @@ tcmu_parse_cdb(struct se_cmd *cmd)
static const struct target_backend_ops tcmu_ops = {
.name = "user",
- .inquiry_prod = "USER",
- .inquiry_rev = TCMU_VERSION,
.owner = THIS_MODULE,
.transport_flags = TRANSPORT_FLAG_PASSTHROUGH,
.attach_hba = tcmu_attach_hba,
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index c463c89b90ef..8cc4ac64a91c 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -382,7 +382,7 @@ endmenu
config QCOM_SPMI_TEMP_ALARM
tristate "Qualcomm SPMI PMIC Temperature Alarm"
- depends on OF && (SPMI || COMPILE_TEST) && IIO
+ depends on OF && SPMI && IIO
select REGMAP_SPMI
help
This enables a thermal sysfs driver for Qualcomm plug-and-play (QPNP)
diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c
index c8fe3cac2e0e..c5547bd711db 100644
--- a/drivers/thermal/imx_thermal.c
+++ b/drivers/thermal/imx_thermal.c
@@ -55,6 +55,7 @@
#define TEMPSENSE2_PANIC_VALUE_SHIFT 16
#define TEMPSENSE2_PANIC_VALUE_MASK 0xfff0000
+#define OCOTP_MEM0 0x0480
#define OCOTP_ANA1 0x04e0
/* The driver supports 1 passive trip point and 1 critical trip point */
@@ -64,12 +65,6 @@ enum imx_thermal_trip {
IMX_TRIP_NUM,
};
-/*
- * It defines the temperature in millicelsius for passive trip point
- * that will trigger cooling action when crossed.
- */
-#define IMX_TEMP_PASSIVE 85000
-
#define IMX_POLLING_DELAY 2000 /* millisecond */
#define IMX_PASSIVE_DELAY 1000
@@ -100,12 +95,14 @@ struct imx_thermal_data {
u32 c1, c2; /* See formula in imx_get_sensor_data() */
int temp_passive;
int temp_critical;
+ int temp_max;
int alarm_temp;
int last_temp;
bool irq_enabled;
int irq;
struct clk *thermal_clk;
const struct thermal_soc_data *socdata;
+ const char *temp_grade;
};
static void imx_set_panic_temp(struct imx_thermal_data *data,
@@ -285,10 +282,12 @@ static int imx_set_trip_temp(struct thermal_zone_device *tz, int trip,
{
struct imx_thermal_data *data = tz->devdata;
+ /* do not allow changing critical threshold */
if (trip == IMX_TRIP_CRITICAL)
return -EPERM;
- if (temp < 0 || temp > IMX_TEMP_PASSIVE)
+ /* do not allow passive to be set higher than critical */
+ if (temp < 0 || temp > data->temp_critical)
return -EINVAL;
data->temp_passive = temp;
@@ -404,17 +403,39 @@ static int imx_get_sensor_data(struct platform_device *pdev)
data->c1 = temp64;
data->c2 = n1 * data->c1 + 1000 * t1;
- /*
- * Set the default passive cooling trip point,
- * can be changed from userspace.
- */
- data->temp_passive = IMX_TEMP_PASSIVE;
+ /* use OTP for thermal grade */
+ ret = regmap_read(map, OCOTP_MEM0, &val);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to read temp grade: %d\n", ret);
+ return ret;
+ }
+
+ /* The maximum die temp is specified by the Temperature Grade */
+ switch ((val >> 6) & 0x3) {
+ case 0: /* Commercial (0 to 95C) */
+ data->temp_grade = "Commercial";
+ data->temp_max = 95000;
+ break;
+ case 1: /* Extended Commercial (-20 to 105C) */
+ data->temp_grade = "Extended Commercial";
+ data->temp_max = 105000;
+ break;
+ case 2: /* Industrial (-40 to 105C) */
+ data->temp_grade = "Industrial";
+ data->temp_max = 105000;
+ break;
+ case 3: /* Automotive (-40 to 125C) */
+ data->temp_grade = "Automotive";
+ data->temp_max = 125000;
+ break;
+ }
/*
- * The maximum die temperature set to 20 C higher than
- * IMX_TEMP_PASSIVE.
+ * Set the critical trip point at 5C under max
+ * Set the passive trip point at 10C under max (can change via sysfs)
*/
- data->temp_critical = 1000 * 20 + data->temp_passive;
+ data->temp_critical = data->temp_max - (1000 * 5);
+ data->temp_passive = data->temp_max - (1000 * 10);
return 0;
}
@@ -551,6 +572,11 @@ static int imx_thermal_probe(struct platform_device *pdev)
return ret;
}
+ dev_info(&pdev->dev, "%s CPU temperature grade - max:%dC"
+ " critical:%dC passive:%dC\n", data->temp_grade,
+ data->temp_max / 1000, data->temp_critical / 1000,
+ data->temp_passive / 1000);
+
/* Enable measurements at ~ 10 Hz */
regmap_write(map, TEMPSENSE1 + REG_CLR, TEMPSENSE1_MEASURE_FREQ);
measure_freq = DIV_ROUND_UP(32768, 10); /* 10 Hz */
diff --git a/drivers/thermal/intel_quark_dts_thermal.c b/drivers/thermal/intel_quark_dts_thermal.c
index 5ed90e6c8a64..5d33b350da1c 100644
--- a/drivers/thermal/intel_quark_dts_thermal.c
+++ b/drivers/thermal/intel_quark_dts_thermal.c
@@ -125,8 +125,8 @@ static int soc_dts_enable(struct thermal_zone_device *tzd)
struct soc_sensor_entry *aux_entry = tzd->devdata;
int ret;
- ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_READ,
- QRK_DTS_REG_OFFSET_ENABLE, &out);
+ ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
+ QRK_DTS_REG_OFFSET_ENABLE, &out);
if (ret)
return ret;
@@ -137,8 +137,8 @@ static int soc_dts_enable(struct thermal_zone_device *tzd)
if (!aux_entry->locked) {
out |= QRK_DTS_ENABLE_BIT;
- ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_WRITE,
- QRK_DTS_REG_OFFSET_ENABLE, out);
+ ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE,
+ QRK_DTS_REG_OFFSET_ENABLE, out);
if (ret)
return ret;
@@ -158,8 +158,8 @@ static int soc_dts_disable(struct thermal_zone_device *tzd)
struct soc_sensor_entry *aux_entry = tzd->devdata;
int ret;
- ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_READ,
- QRK_DTS_REG_OFFSET_ENABLE, &out);
+ ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
+ QRK_DTS_REG_OFFSET_ENABLE, &out);
if (ret)
return ret;
@@ -170,8 +170,8 @@ static int soc_dts_disable(struct thermal_zone_device *tzd)
if (!aux_entry->locked) {
out &= ~QRK_DTS_ENABLE_BIT;
- ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_WRITE,
- QRK_DTS_REG_OFFSET_ENABLE, out);
+ ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE,
+ QRK_DTS_REG_OFFSET_ENABLE, out);
if (ret)
return ret;
@@ -192,8 +192,8 @@ static int _get_trip_temp(int trip, int *temp)
u32 out;
mutex_lock(&dts_update_mutex);
- status = iosf_mbi_read(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_READ,
- QRK_DTS_REG_OFFSET_PTPS, &out);
+ status = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
+ QRK_DTS_REG_OFFSET_PTPS, &out);
mutex_unlock(&dts_update_mutex);
if (status)
@@ -236,8 +236,8 @@ static int update_trip_temp(struct soc_sensor_entry *aux_entry,
goto failed;
}
- ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_READ,
- QRK_DTS_REG_OFFSET_PTPS, &store_ptps);
+ ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
+ QRK_DTS_REG_OFFSET_PTPS, &store_ptps);
if (ret)
goto failed;
@@ -262,8 +262,8 @@ static int update_trip_temp(struct soc_sensor_entry *aux_entry,
out |= (temp_out & QRK_DTS_MASK_TP_THRES) <<
(trip * QRK_DTS_SHIFT_TP);
- ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_WRITE,
- QRK_DTS_REG_OFFSET_PTPS, out);
+ ret = iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE,
+ QRK_DTS_REG_OFFSET_PTPS, out);
failed:
mutex_unlock(&dts_update_mutex);
@@ -294,8 +294,8 @@ static int sys_get_curr_temp(struct thermal_zone_device *tzd,
int ret;
mutex_lock(&dts_update_mutex);
- ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_READ,
- QRK_DTS_REG_OFFSET_TEMP, &out);
+ ret = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
+ QRK_DTS_REG_OFFSET_TEMP, &out);
mutex_unlock(&dts_update_mutex);
if (ret)
@@ -350,13 +350,13 @@ static void free_soc_dts(struct soc_sensor_entry *aux_entry)
if (aux_entry) {
if (!aux_entry->locked) {
mutex_lock(&dts_update_mutex);
- iosf_mbi_write(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_WRITE,
- QRK_DTS_REG_OFFSET_ENABLE,
- aux_entry->store_dts_enable);
+ iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE,
+ QRK_DTS_REG_OFFSET_ENABLE,
+ aux_entry->store_dts_enable);
- iosf_mbi_write(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_WRITE,
- QRK_DTS_REG_OFFSET_PTPS,
- aux_entry->store_ptps);
+ iosf_mbi_write(QRK_MBI_UNIT_RMU, MBI_REG_WRITE,
+ QRK_DTS_REG_OFFSET_PTPS,
+ aux_entry->store_ptps);
mutex_unlock(&dts_update_mutex);
}
thermal_zone_device_unregister(aux_entry->tzone);
@@ -378,9 +378,8 @@ static struct soc_sensor_entry *alloc_soc_dts(void)
}
/* Check if DTS register is locked */
- err = iosf_mbi_read(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_READ,
- QRK_DTS_REG_OFFSET_LOCK,
- &out);
+ err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
+ QRK_DTS_REG_OFFSET_LOCK, &out);
if (err)
goto err_ret;
@@ -395,16 +394,16 @@ static struct soc_sensor_entry *alloc_soc_dts(void)
/* Store DTS default state if DTS registers are not locked */
if (!aux_entry->locked) {
/* Store DTS default enable for restore on exit */
- err = iosf_mbi_read(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_READ,
- QRK_DTS_REG_OFFSET_ENABLE,
- &aux_entry->store_dts_enable);
+ err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
+ QRK_DTS_REG_OFFSET_ENABLE,
+ &aux_entry->store_dts_enable);
if (err)
goto err_ret;
/* Store DTS default PTPS register for restore on exit */
- err = iosf_mbi_read(QRK_MBI_UNIT_RMU, QRK_MBI_RMU_READ,
- QRK_DTS_REG_OFFSET_PTPS,
- &aux_entry->store_ptps);
+ err = iosf_mbi_read(QRK_MBI_UNIT_RMU, MBI_REG_READ,
+ QRK_DTS_REG_OFFSET_PTPS,
+ &aux_entry->store_ptps);
if (err)
goto err_ret;
}
diff --git a/drivers/thermal/intel_soc_dts_iosf.c b/drivers/thermal/intel_soc_dts_iosf.c
index 5841d1d72996..f72e1db3216f 100644
--- a/drivers/thermal/intel_soc_dts_iosf.c
+++ b/drivers/thermal/intel_soc_dts_iosf.c
@@ -90,7 +90,7 @@ static int sys_get_trip_temp(struct thermal_zone_device *tzd, int trip,
dts = tzd->devdata;
sensors = dts->sensors;
mutex_lock(&sensors->dts_update_lock);
- status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
+ status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
SOC_DTS_OFFSET_PTPS, &out);
mutex_unlock(&sensors->dts_update_lock);
if (status)
@@ -124,27 +124,27 @@ static int update_trip_temp(struct intel_soc_dts_sensor_entry *dts,
temp_out = (sensors->tj_max - temp) / 1000;
- status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
+ status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
SOC_DTS_OFFSET_PTPS, &store_ptps);
if (status)
return status;
out = (store_ptps & ~(0xFF << (thres_index * 8)));
out |= (temp_out & 0xFF) << (thres_index * 8);
- status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
+ status = iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
SOC_DTS_OFFSET_PTPS, out);
if (status)
return status;
pr_debug("update_trip_temp PTPS = %x\n", out);
- status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
+ status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
SOC_DTS_OFFSET_PTMC, &out);
if (status)
goto err_restore_ptps;
store_ptmc = out;
- status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
+ status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
SOC_DTS_TE_AUX0 + thres_index,
&te_out);
if (status)
@@ -167,12 +167,12 @@ static int update_trip_temp(struct intel_soc_dts_sensor_entry *dts,
out &= ~SOC_DTS_AUX0_ENABLE_BIT;
te_out &= ~int_enable_bit;
}
- status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
+ status = iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
SOC_DTS_OFFSET_PTMC, out);
if (status)
goto err_restore_te_out;
- status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
+ status = iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
SOC_DTS_TE_AUX0 + thres_index,
te_out);
if (status)
@@ -182,13 +182,13 @@ static int update_trip_temp(struct intel_soc_dts_sensor_entry *dts,
return 0;
err_restore_te_out:
- iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
+ iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
SOC_DTS_OFFSET_PTMC, store_te_out);
err_restore_ptmc:
- iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
+ iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
SOC_DTS_OFFSET_PTMC, store_ptmc);
err_restore_ptps:
- iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
+ iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
SOC_DTS_OFFSET_PTPS, store_ptps);
/* Nothing we can do if restore fails */
@@ -235,7 +235,7 @@ static int sys_get_curr_temp(struct thermal_zone_device *tzd,
dts = tzd->devdata;
sensors = dts->sensors;
- status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
+ status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
SOC_DTS_OFFSET_TEMP, &out);
if (status)
return status;
@@ -259,14 +259,14 @@ static int soc_dts_enable(int id)
u32 out;
int ret;
- ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
+ ret = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
SOC_DTS_OFFSET_ENABLE, &out);
if (ret)
return ret;
if (!(out & BIT(id))) {
out |= BIT(id);
- ret = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
+ ret = iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
SOC_DTS_OFFSET_ENABLE, out);
if (ret)
return ret;
@@ -278,7 +278,7 @@ static int soc_dts_enable(int id)
static void remove_dts_thermal_zone(struct intel_soc_dts_sensor_entry *dts)
{
if (dts) {
- iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
+ iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
SOC_DTS_OFFSET_ENABLE, dts->store_status);
thermal_zone_device_unregister(dts->tzone);
}
@@ -296,9 +296,8 @@ static int add_dts_thermal_zone(int id, struct intel_soc_dts_sensor_entry *dts,
int i;
/* Store status to restor on exit */
- ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
- SOC_DTS_OFFSET_ENABLE,
- &dts->store_status);
+ ret = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
+ SOC_DTS_OFFSET_ENABLE, &dts->store_status);
if (ret)
goto err_ret;
@@ -311,7 +310,7 @@ static int add_dts_thermal_zone(int id, struct intel_soc_dts_sensor_entry *dts,
}
/* Check if the writable trip we provide is not used by BIOS */
- ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
+ ret = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
SOC_DTS_OFFSET_PTPS, &store_ptps);
if (ret)
trip_mask = 0;
@@ -374,19 +373,19 @@ void intel_soc_dts_iosf_interrupt_handler(struct intel_soc_dts_sensors *sensors)
spin_lock_irqsave(&sensors->intr_notify_lock, flags);
- status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
+ status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
SOC_DTS_OFFSET_PTMC, &ptmc_out);
ptmc_out |= SOC_DTS_PTMC_APIC_DEASSERT_BIT;
- status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
+ status = iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
SOC_DTS_OFFSET_PTMC, ptmc_out);
- status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
+ status = iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ,
SOC_DTS_OFFSET_PTTSS, &sticky_out);
pr_debug("status %d PTTSS %x\n", status, sticky_out);
if (sticky_out & SOC_DTS_TRIP_MASK) {
int i;
/* reset sticky bit */
- status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
+ status = iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_REG_WRITE,
SOC_DTS_OFFSET_PTTSS, sticky_out);
spin_unlock_irqrestore(&sensors->intr_notify_lock, flags);
diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c
index 42b7d4253b94..be4eedcb839a 100644
--- a/drivers/thermal/of-thermal.c
+++ b/drivers/thermal/of-thermal.c
@@ -964,7 +964,7 @@ void of_thermal_destroy_zones(void)
np = of_find_node_by_name(NULL, "thermal-zones");
if (!np) {
- pr_err("unable to find thermal zones\n");
+ pr_debug("unable to find thermal zones\n");
return;
}
diff --git a/drivers/thermal/power_allocator.c b/drivers/thermal/power_allocator.c
index f0fbea386869..1246aa6fcab0 100644
--- a/drivers/thermal/power_allocator.c
+++ b/drivers/thermal/power_allocator.c
@@ -174,7 +174,6 @@ static void estimate_pid_constants(struct thermal_zone_device *tz,
/**
* pid_controller() - PID controller
* @tz: thermal zone we are operating in
- * @current_temp: the current temperature in millicelsius
* @control_temp: the target temperature in millicelsius
* @max_allocatable_power: maximum allocatable power for this thermal zone
*
@@ -191,7 +190,6 @@ static void estimate_pid_constants(struct thermal_zone_device *tz,
* Return: The power budget for the next period.
*/
static u32 pid_controller(struct thermal_zone_device *tz,
- int current_temp,
int control_temp,
u32 max_allocatable_power)
{
@@ -211,7 +209,7 @@ static u32 pid_controller(struct thermal_zone_device *tz,
true);
}
- err = control_temp - current_temp;
+ err = control_temp - tz->temperature;
err = int_to_frac(err);
/* Calculate the proportional term */
@@ -332,7 +330,6 @@ static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors,
}
static int allocate_power(struct thermal_zone_device *tz,
- int current_temp,
int control_temp)
{
struct thermal_instance *instance;
@@ -418,8 +415,7 @@ static int allocate_power(struct thermal_zone_device *tz,
i++;
}
- power_range = pid_controller(tz, current_temp, control_temp,
- max_allocatable_power);
+ power_range = pid_controller(tz, control_temp, max_allocatable_power);
divvy_up_power(weighted_req_power, max_power, num_actors,
total_weighted_req_power, power_range, granted_power,
@@ -444,8 +440,8 @@ static int allocate_power(struct thermal_zone_device *tz,
trace_thermal_power_allocator(tz, req_power, total_req_power,
granted_power, total_granted_power,
num_actors, power_range,
- max_allocatable_power, current_temp,
- control_temp - current_temp);
+ max_allocatable_power, tz->temperature,
+ control_temp - tz->temperature);
kfree(req_power);
unlock:
@@ -612,7 +608,7 @@ static void power_allocator_unbind(struct thermal_zone_device *tz)
static int power_allocator_throttle(struct thermal_zone_device *tz, int trip)
{
int ret;
- int switch_on_temp, control_temp, current_temp;
+ int switch_on_temp, control_temp;
struct power_allocator_params *params = tz->governor_data;
/*
@@ -622,15 +618,9 @@ static int power_allocator_throttle(struct thermal_zone_device *tz, int trip)
if (trip != params->trip_max_desired_temperature)
return 0;
- ret = thermal_zone_get_temp(tz, &current_temp);
- if (ret) {
- dev_warn(&tz->device, "Failed to get temperature: %d\n", ret);
- return ret;
- }
-
ret = tz->ops->get_trip_temp(tz, params->trip_switch_on,
&switch_on_temp);
- if (!ret && (current_temp < switch_on_temp)) {
+ if (!ret && (tz->temperature < switch_on_temp)) {
tz->passive = 0;
reset_pid_controller(params);
allow_maximum_power(tz);
@@ -648,7 +638,7 @@ static int power_allocator_throttle(struct thermal_zone_device *tz, int trip)
return ret;
}
- return allocate_power(tz, current_temp, control_temp);
+ return allocate_power(tz, control_temp);
}
static struct thermal_governor thermal_gov_power_allocator = {
diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c
index 5d4ae7d705e0..13d01edc7a04 100644
--- a/drivers/thermal/rcar_thermal.c
+++ b/drivers/thermal/rcar_thermal.c
@@ -361,6 +361,24 @@ static irqreturn_t rcar_thermal_irq(int irq, void *data)
/*
* platform functions
*/
+static int rcar_thermal_remove(struct platform_device *pdev)
+{
+ struct rcar_thermal_common *common = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+ struct rcar_thermal_priv *priv;
+
+ rcar_thermal_for_each_priv(priv, common) {
+ if (rcar_has_irq_support(priv))
+ rcar_thermal_irq_disable(priv);
+ thermal_zone_device_unregister(priv->zone);
+ }
+
+ pm_runtime_put(dev);
+ pm_runtime_disable(dev);
+
+ return 0;
+}
+
static int rcar_thermal_probe(struct platform_device *pdev)
{
struct rcar_thermal_common *common;
@@ -377,6 +395,8 @@ static int rcar_thermal_probe(struct platform_device *pdev)
if (!common)
return -ENOMEM;
+ platform_set_drvdata(pdev, common);
+
INIT_LIST_HEAD(&common->head);
spin_lock_init(&common->lock);
common->dev = dev;
@@ -454,43 +474,16 @@ static int rcar_thermal_probe(struct platform_device *pdev)
rcar_thermal_common_write(common, ENR, enr_bits);
}
- platform_set_drvdata(pdev, common);
-
dev_info(dev, "%d sensor probed\n", i);
return 0;
error_unregister:
- rcar_thermal_for_each_priv(priv, common) {
- if (rcar_has_irq_support(priv))
- rcar_thermal_irq_disable(priv);
- thermal_zone_device_unregister(priv->zone);
- }
-
- pm_runtime_put(dev);
- pm_runtime_disable(dev);
+ rcar_thermal_remove(pdev);
return ret;
}
-static int rcar_thermal_remove(struct platform_device *pdev)
-{
- struct rcar_thermal_common *common = platform_get_drvdata(pdev);
- struct device *dev = &pdev->dev;
- struct rcar_thermal_priv *priv;
-
- rcar_thermal_for_each_priv(priv, common) {
- if (rcar_has_irq_support(priv))
- rcar_thermal_irq_disable(priv);
- thermal_zone_device_unregister(priv->zone);
- }
-
- pm_runtime_put(dev);
- pm_runtime_disable(dev);
-
- return 0;
-}
-
static const struct of_device_id rcar_thermal_dt_ids[] = {
{ .compatible = "renesas,rcar-thermal", },
{},
diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c
index 9787e8aa509f..e845841ab036 100644
--- a/drivers/thermal/rockchip_thermal.c
+++ b/drivers/thermal/rockchip_thermal.c
@@ -1,6 +1,9 @@
/*
* Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
*
+ * Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd
+ * Caesar Wang <wxt@rock-chips.com>
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
@@ -45,17 +48,50 @@ enum tshut_polarity {
};
/**
- * The system has three Temperature Sensors. channel 0 is reserved,
- * channel 1 is for CPU, and channel 2 is for GPU.
+ * The system has two Temperature Sensors.
+ * sensor0 is for CPU, and sensor1 is for GPU.
*/
enum sensor_id {
- SENSOR_CPU = 1,
+ SENSOR_CPU = 0,
SENSOR_GPU,
};
+/**
+* The conversion table has the adc value and temperature.
+* ADC_DECREMENT is the adc value decremnet.(e.g. v2_code_table)
+* ADC_INCREMNET is the adc value incremnet.(e.g. v3_code_table)
+*/
+enum adc_sort_mode {
+ ADC_DECREMENT = 0,
+ ADC_INCREMENT,
+};
+
+/**
+ * The max sensors is two in rockchip SoCs.
+ * Two sensors: CPU and GPU sensor.
+ */
+#define SOC_MAX_SENSORS 2
+
+struct chip_tsadc_table {
+ const struct tsadc_table *id;
+
+ /* the array table size*/
+ unsigned int length;
+
+ /* that analogic mask data */
+ u32 data_mask;
+
+ /* the sort mode is adc value that increment or decrement in table */
+ enum adc_sort_mode mode;
+};
+
struct rockchip_tsadc_chip {
+ /* The sensor id of chip correspond to the ADC channel */
+ int chn_id[SOC_MAX_SENSORS];
+ int chn_num;
+
/* The hardware-controlled tshut property */
- long tshut_temp;
+ int tshut_temp;
enum tshut_mode tshut_mode;
enum tshut_polarity tshut_polarity;
@@ -65,37 +101,40 @@ struct rockchip_tsadc_chip {
void (*control)(void __iomem *reg, bool on);
/* Per-sensor methods */
- int (*get_temp)(int chn, void __iomem *reg, int *temp);
- void (*set_tshut_temp)(int chn, void __iomem *reg, long temp);
+ int (*get_temp)(struct chip_tsadc_table table,
+ int chn, void __iomem *reg, int *temp);
+ void (*set_tshut_temp)(struct chip_tsadc_table table,
+ int chn, void __iomem *reg, int temp);
void (*set_tshut_mode)(int chn, void __iomem *reg, enum tshut_mode m);
+
+ /* Per-table methods */
+ struct chip_tsadc_table table;
};
struct rockchip_thermal_sensor {
struct rockchip_thermal_data *thermal;
struct thermal_zone_device *tzd;
- enum sensor_id id;
+ int id;
};
-#define NUM_SENSORS 2 /* Ignore unused sensor 0 */
-
struct rockchip_thermal_data {
const struct rockchip_tsadc_chip *chip;
struct platform_device *pdev;
struct reset_control *reset;
- struct rockchip_thermal_sensor sensors[NUM_SENSORS];
+ struct rockchip_thermal_sensor sensors[SOC_MAX_SENSORS];
struct clk *clk;
struct clk *pclk;
void __iomem *regs;
- long tshut_temp;
+ int tshut_temp;
enum tshut_mode tshut_mode;
enum tshut_polarity tshut_polarity;
};
-/* TSADC V2 Sensor info define: */
+/* TSADC Sensor info define: */
#define TSADCV2_AUTO_CON 0x04
#define TSADCV2_INT_EN 0x08
#define TSADCV2_INT_PD 0x0c
@@ -117,6 +156,8 @@ struct rockchip_thermal_data {
#define TSADCV2_INT_PD_CLEAR_MASK ~BIT(8)
#define TSADCV2_DATA_MASK 0xfff
+#define TSADCV3_DATA_MASK 0x3ff
+
#define TSADCV2_HIGHT_INT_DEBOUNCE_COUNT 4
#define TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT 4
#define TSADCV2_AUTO_PERIOD_TIME 250 /* msec */
@@ -124,7 +165,7 @@ struct rockchip_thermal_data {
struct tsadc_table {
u32 code;
- long temp;
+ int temp;
};
static const struct tsadc_table v2_code_table[] = {
@@ -165,21 +206,61 @@ static const struct tsadc_table v2_code_table[] = {
{3421, 125000},
};
-static u32 rk_tsadcv2_temp_to_code(long temp)
+static const struct tsadc_table v3_code_table[] = {
+ {0, -40000},
+ {106, -40000},
+ {108, -35000},
+ {110, -30000},
+ {112, -25000},
+ {114, -20000},
+ {116, -15000},
+ {118, -10000},
+ {120, -5000},
+ {122, 0},
+ {124, 5000},
+ {126, 10000},
+ {128, 15000},
+ {130, 20000},
+ {132, 25000},
+ {134, 30000},
+ {136, 35000},
+ {138, 40000},
+ {140, 45000},
+ {142, 50000},
+ {144, 55000},
+ {146, 60000},
+ {148, 65000},
+ {150, 70000},
+ {152, 75000},
+ {154, 80000},
+ {156, 85000},
+ {158, 90000},
+ {160, 95000},
+ {162, 100000},
+ {163, 105000},
+ {165, 110000},
+ {167, 115000},
+ {169, 120000},
+ {171, 125000},
+ {TSADCV3_DATA_MASK, 125000},
+};
+
+static u32 rk_tsadcv2_temp_to_code(struct chip_tsadc_table table,
+ int temp)
{
int high, low, mid;
low = 0;
- high = ARRAY_SIZE(v2_code_table) - 1;
+ high = table.length - 1;
mid = (high + low) / 2;
- if (temp < v2_code_table[low].temp || temp > v2_code_table[high].temp)
+ if (temp < table.id[low].temp || temp > table.id[high].temp)
return 0;
while (low <= high) {
- if (temp == v2_code_table[mid].temp)
- return v2_code_table[mid].code;
- else if (temp < v2_code_table[mid].temp)
+ if (temp == table.id[mid].temp)
+ return table.id[mid].code;
+ else if (temp < table.id[mid].temp)
high = mid - 1;
else
low = mid + 1;
@@ -189,29 +270,54 @@ static u32 rk_tsadcv2_temp_to_code(long temp)
return 0;
}
-static int rk_tsadcv2_code_to_temp(u32 code, int *temp)
+static int rk_tsadcv2_code_to_temp(struct chip_tsadc_table table, u32 code,
+ int *temp)
{
unsigned int low = 1;
- unsigned int high = ARRAY_SIZE(v2_code_table) - 1;
+ unsigned int high = table.length - 1;
unsigned int mid = (low + high) / 2;
unsigned int num;
unsigned long denom;
- BUILD_BUG_ON(ARRAY_SIZE(v2_code_table) < 2);
-
- code &= TSADCV2_DATA_MASK;
- if (code < v2_code_table[high].code)
- return -EAGAIN; /* Incorrect reading */
-
- while (low <= high) {
- if (code >= v2_code_table[mid].code &&
- code < v2_code_table[mid - 1].code)
- break;
- else if (code < v2_code_table[mid].code)
- low = mid + 1;
- else
- high = mid - 1;
- mid = (low + high) / 2;
+ WARN_ON(table.length < 2);
+
+ switch (table.mode) {
+ case ADC_DECREMENT:
+ code &= table.data_mask;
+ if (code < table.id[high].code)
+ return -EAGAIN; /* Incorrect reading */
+
+ while (low <= high) {
+ if (code >= table.id[mid].code &&
+ code < table.id[mid - 1].code)
+ break;
+ else if (code < table.id[mid].code)
+ low = mid + 1;
+ else
+ high = mid - 1;
+
+ mid = (low + high) / 2;
+ }
+ break;
+ case ADC_INCREMENT:
+ code &= table.data_mask;
+ if (code < table.id[low].code)
+ return -EAGAIN; /* Incorrect reading */
+
+ while (low <= high) {
+ if (code >= table.id[mid - 1].code &&
+ code < table.id[mid].code)
+ break;
+ else if (code > table.id[mid].code)
+ low = mid + 1;
+ else
+ high = mid - 1;
+
+ mid = (low + high) / 2;
+ }
+ break;
+ default:
+ pr_err("Invalid the conversion table\n");
}
/*
@@ -220,24 +326,28 @@ static int rk_tsadcv2_code_to_temp(u32 code, int *temp)
* temperature between 2 table entries is linear and interpolate
* to produce less granular result.
*/
- num = v2_code_table[mid].temp - v2_code_table[mid - 1].temp;
- num *= v2_code_table[mid - 1].code - code;
- denom = v2_code_table[mid - 1].code - v2_code_table[mid].code;
- *temp = v2_code_table[mid - 1].temp + (num / denom);
+ num = table.id[mid].temp - v2_code_table[mid - 1].temp;
+ num *= abs(table.id[mid - 1].code - code);
+ denom = abs(table.id[mid - 1].code - table.id[mid].code);
+ *temp = table.id[mid - 1].temp + (num / denom);
return 0;
}
/**
- * rk_tsadcv2_initialize - initialize TASDC Controller
- * (1) Set TSADCV2_AUTO_PERIOD, configure the interleave between
- * every two accessing of TSADC in normal operation.
- * (2) Set TSADCV2_AUTO_PERIOD_HT, configure the interleave between
- * every two accessing of TSADC after the temperature is higher
- * than COM_SHUT or COM_INT.
- * (3) Set TSADCV2_HIGH_INT_DEBOUNCE and TSADC_HIGHT_TSHUT_DEBOUNCE,
- * if the temperature is higher than COMP_INT or COMP_SHUT for
- * "debounce" times, TSADC controller will generate interrupt or TSHUT.
+ * rk_tsadcv2_initialize - initialize TASDC Controller.
+ *
+ * (1) Set TSADC_V2_AUTO_PERIOD:
+ * Configure the interleave between every two accessing of
+ * TSADC in normal operation.
+ *
+ * (2) Set TSADCV2_AUTO_PERIOD_HT:
+ * Configure the interleave between every two accessing of
+ * TSADC after the temperature is higher than COM_SHUT or COM_INT.
+ *
+ * (3) Set TSADCV2_HIGH_INT_DEBOUNCE and TSADC_HIGHT_TSHUT_DEBOUNCE:
+ * If the temperature is higher than COMP_INT or COMP_SHUT for
+ * "debounce" times, TSADC controller will generate interrupt or TSHUT.
*/
static void rk_tsadcv2_initialize(void __iomem *regs,
enum tshut_polarity tshut_polarity)
@@ -279,20 +389,22 @@ static void rk_tsadcv2_control(void __iomem *regs, bool enable)
writel_relaxed(val, regs + TSADCV2_AUTO_CON);
}
-static int rk_tsadcv2_get_temp(int chn, void __iomem *regs, int *temp)
+static int rk_tsadcv2_get_temp(struct chip_tsadc_table table,
+ int chn, void __iomem *regs, int *temp)
{
u32 val;
val = readl_relaxed(regs + TSADCV2_DATA(chn));
- return rk_tsadcv2_code_to_temp(val, temp);
+ return rk_tsadcv2_code_to_temp(table, val, temp);
}
-static void rk_tsadcv2_tshut_temp(int chn, void __iomem *regs, long temp)
+static void rk_tsadcv2_tshut_temp(struct chip_tsadc_table table,
+ int chn, void __iomem *regs, int temp)
{
u32 tshut_value, val;
- tshut_value = rk_tsadcv2_temp_to_code(temp);
+ tshut_value = rk_tsadcv2_temp_to_code(table, temp);
writel_relaxed(tshut_value, regs + TSADCV2_COMP_SHUT(chn));
/* TSHUT will be valid */
@@ -318,6 +430,10 @@ static void rk_tsadcv2_tshut_mode(int chn, void __iomem *regs,
}
static const struct rockchip_tsadc_chip rk3288_tsadc_data = {
+ .chn_id[SENSOR_CPU] = 1, /* cpu sensor is channel 1 */
+ .chn_id[SENSOR_GPU] = 2, /* gpu sensor is channel 2 */
+ .chn_num = 2, /* two channels for tsadc */
+
.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
.tshut_temp = 95000,
@@ -328,6 +444,37 @@ static const struct rockchip_tsadc_chip rk3288_tsadc_data = {
.get_temp = rk_tsadcv2_get_temp,
.set_tshut_temp = rk_tsadcv2_tshut_temp,
.set_tshut_mode = rk_tsadcv2_tshut_mode,
+
+ .table = {
+ .id = v2_code_table,
+ .length = ARRAY_SIZE(v2_code_table),
+ .data_mask = TSADCV2_DATA_MASK,
+ .mode = ADC_DECREMENT,
+ },
+};
+
+static const struct rockchip_tsadc_chip rk3368_tsadc_data = {
+ .chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */
+ .chn_id[SENSOR_GPU] = 1, /* gpu sensor is channel 1 */
+ .chn_num = 2, /* two channels for tsadc */
+
+ .tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
+ .tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
+ .tshut_temp = 95000,
+
+ .initialize = rk_tsadcv2_initialize,
+ .irq_ack = rk_tsadcv2_irq_ack,
+ .control = rk_tsadcv2_control,
+ .get_temp = rk_tsadcv2_get_temp,
+ .set_tshut_temp = rk_tsadcv2_tshut_temp,
+ .set_tshut_mode = rk_tsadcv2_tshut_mode,
+
+ .table = {
+ .id = v3_code_table,
+ .length = ARRAY_SIZE(v3_code_table),
+ .data_mask = TSADCV3_DATA_MASK,
+ .mode = ADC_INCREMENT,
+ },
};
static const struct of_device_id of_rockchip_thermal_match[] = {
@@ -335,6 +482,10 @@ static const struct of_device_id of_rockchip_thermal_match[] = {
.compatible = "rockchip,rk3288-tsadc",
.data = (void *)&rk3288_tsadc_data,
},
+ {
+ .compatible = "rockchip,rk3368-tsadc",
+ .data = (void *)&rk3368_tsadc_data,
+ },
{ /* end */ },
};
MODULE_DEVICE_TABLE(of, of_rockchip_thermal_match);
@@ -357,7 +508,7 @@ static irqreturn_t rockchip_thermal_alarm_irq_thread(int irq, void *dev)
thermal->chip->irq_ack(thermal->regs);
- for (i = 0; i < ARRAY_SIZE(thermal->sensors); i++)
+ for (i = 0; i < thermal->chip->chn_num; i++)
thermal_zone_device_update(thermal->sensors[i].tzd);
return IRQ_HANDLED;
@@ -370,7 +521,8 @@ static int rockchip_thermal_get_temp(void *_sensor, int *out_temp)
const struct rockchip_tsadc_chip *tsadc = sensor->thermal->chip;
int retval;
- retval = tsadc->get_temp(sensor->id, thermal->regs, out_temp);
+ retval = tsadc->get_temp(tsadc->table,
+ sensor->id, thermal->regs, out_temp);
dev_dbg(&thermal->pdev->dev, "sensor %d - temp: %d, retval: %d\n",
sensor->id, *out_temp, retval);
@@ -389,7 +541,7 @@ static int rockchip_configure_from_dt(struct device *dev,
if (of_property_read_u32(np, "rockchip,hw-tshut-temp", &shut_temp)) {
dev_warn(dev,
- "Missing tshut temp property, using default %ld\n",
+ "Missing tshut temp property, using default %d\n",
thermal->chip->tshut_temp);
thermal->tshut_temp = thermal->chip->tshut_temp;
} else {
@@ -397,7 +549,7 @@ static int rockchip_configure_from_dt(struct device *dev,
}
if (thermal->tshut_temp > INT_MAX) {
- dev_err(dev, "Invalid tshut temperature specified: %ld\n",
+ dev_err(dev, "Invalid tshut temperature specified: %d\n",
thermal->tshut_temp);
return -ERANGE;
}
@@ -442,13 +594,14 @@ static int
rockchip_thermal_register_sensor(struct platform_device *pdev,
struct rockchip_thermal_data *thermal,
struct rockchip_thermal_sensor *sensor,
- enum sensor_id id)
+ int id)
{
const struct rockchip_tsadc_chip *tsadc = thermal->chip;
int error;
tsadc->set_tshut_mode(id, thermal->regs, thermal->tshut_mode);
- tsadc->set_tshut_temp(id, thermal->regs, thermal->tshut_temp);
+ tsadc->set_tshut_temp(tsadc->table, id, thermal->regs,
+ thermal->tshut_temp);
sensor->thermal = thermal;
sensor->id = id;
@@ -481,7 +634,7 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
const struct of_device_id *match;
struct resource *res;
int irq;
- int i;
+ int i, j;
int error;
match = of_match_node(of_rockchip_thermal_match, np);
@@ -556,22 +709,19 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
thermal->chip->initialize(thermal->regs, thermal->tshut_polarity);
- error = rockchip_thermal_register_sensor(pdev, thermal,
- &thermal->sensors[0],
- SENSOR_CPU);
- if (error) {
- dev_err(&pdev->dev,
- "failed to register CPU thermal sensor: %d\n", error);
- goto err_disable_pclk;
- }
-
- error = rockchip_thermal_register_sensor(pdev, thermal,
- &thermal->sensors[1],
- SENSOR_GPU);
- if (error) {
- dev_err(&pdev->dev,
- "failed to register GPU thermal sensor: %d\n", error);
- goto err_unregister_cpu_sensor;
+ for (i = 0; i < thermal->chip->chn_num; i++) {
+ error = rockchip_thermal_register_sensor(pdev, thermal,
+ &thermal->sensors[i],
+ thermal->chip->chn_id[i]);
+ if (error) {
+ dev_err(&pdev->dev,
+ "failed to register sensor[%d] : error = %d\n",
+ i, error);
+ for (j = 0; j < i; j++)
+ thermal_zone_of_sensor_unregister(&pdev->dev,
+ thermal->sensors[j].tzd);
+ goto err_disable_pclk;
+ }
}
error = devm_request_threaded_irq(&pdev->dev, irq, NULL,
@@ -581,22 +731,23 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
if (error) {
dev_err(&pdev->dev,
"failed to request tsadc irq: %d\n", error);
- goto err_unregister_gpu_sensor;
+ goto err_unregister_sensor;
}
thermal->chip->control(thermal->regs, true);
- for (i = 0; i < ARRAY_SIZE(thermal->sensors); i++)
+ for (i = 0; i < thermal->chip->chn_num; i++)
rockchip_thermal_toggle_sensor(&thermal->sensors[i], true);
platform_set_drvdata(pdev, thermal);
return 0;
-err_unregister_gpu_sensor:
- thermal_zone_of_sensor_unregister(&pdev->dev, thermal->sensors[1].tzd);
-err_unregister_cpu_sensor:
- thermal_zone_of_sensor_unregister(&pdev->dev, thermal->sensors[0].tzd);
+err_unregister_sensor:
+ while (i--)
+ thermal_zone_of_sensor_unregister(&pdev->dev,
+ thermal->sensors[i].tzd);
+
err_disable_pclk:
clk_disable_unprepare(thermal->pclk);
err_disable_clk:
@@ -610,7 +761,7 @@ static int rockchip_thermal_remove(struct platform_device *pdev)
struct rockchip_thermal_data *thermal = platform_get_drvdata(pdev);
int i;
- for (i = 0; i < ARRAY_SIZE(thermal->sensors); i++) {
+ for (i = 0; i < thermal->chip->chn_num; i++) {
struct rockchip_thermal_sensor *sensor = &thermal->sensors[i];
rockchip_thermal_toggle_sensor(sensor, false);
@@ -631,7 +782,7 @@ static int __maybe_unused rockchip_thermal_suspend(struct device *dev)
struct rockchip_thermal_data *thermal = platform_get_drvdata(pdev);
int i;
- for (i = 0; i < ARRAY_SIZE(thermal->sensors); i++)
+ for (i = 0; i < thermal->chip->chn_num; i++)
rockchip_thermal_toggle_sensor(&thermal->sensors[i], false);
thermal->chip->control(thermal->regs, false);
@@ -663,18 +814,19 @@ static int __maybe_unused rockchip_thermal_resume(struct device *dev)
thermal->chip->initialize(thermal->regs, thermal->tshut_polarity);
- for (i = 0; i < ARRAY_SIZE(thermal->sensors); i++) {
- enum sensor_id id = thermal->sensors[i].id;
+ for (i = 0; i < thermal->chip->chn_num; i++) {
+ int id = thermal->sensors[i].id;
thermal->chip->set_tshut_mode(id, thermal->regs,
thermal->tshut_mode);
- thermal->chip->set_tshut_temp(id, thermal->regs,
+ thermal->chip->set_tshut_temp(thermal->chip->table,
+ id, thermal->regs,
thermal->tshut_temp);
}
thermal->chip->control(thermal->regs, true);
- for (i = 0; i < ARRAY_SIZE(thermal->sensors); i++)
+ for (i = 0; i < thermal->chip->chn_num; i++)
rockchip_thermal_toggle_sensor(&thermal->sensors[i], true);
pinctrl_pm_select_default_state(dev);
diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c
index e53d9a512c6d..2caaf5a2516d 100644
--- a/drivers/tty/amiserial.c
+++ b/drivers/tty/amiserial.c
@@ -32,7 +32,6 @@
#include <linux/delay.h>
#undef SERIAL_PARANOIA_CHECK
-#define SERIAL_DO_RESTART
/* Set of debugging defines */
diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c
index d4a1331675ed..abbed201dc74 100644
--- a/drivers/tty/cyclades.c
+++ b/drivers/tty/cyclades.c
@@ -292,14 +292,14 @@ static void cyz_rx_restart(unsigned long);
static struct timer_list cyz_rx_full_timer[NR_PORTS];
#endif /* CONFIG_CYZ_INTR */
-static inline void cyy_writeb(struct cyclades_port *port, u32 reg, u8 val)
+static void cyy_writeb(struct cyclades_port *port, u32 reg, u8 val)
{
struct cyclades_card *card = port->card;
cy_writeb(port->u.cyy.base_addr + (reg << card->bus_index), val);
}
-static inline u8 cyy_readb(struct cyclades_port *port, u32 reg)
+static u8 cyy_readb(struct cyclades_port *port, u32 reg)
{
struct cyclades_card *card = port->card;
@@ -321,7 +321,7 @@ static inline bool cyz_fpga_loaded(struct cyclades_card *card)
return __cyz_fpga_loaded(card->ctl_addr.p9060);
}
-static inline bool cyz_is_loaded(struct cyclades_card *card)
+static bool cyz_is_loaded(struct cyclades_card *card)
{
struct FIRM_ID __iomem *fw_id = card->base_addr + ID_ADDRESS;
@@ -329,7 +329,7 @@ static inline bool cyz_is_loaded(struct cyclades_card *card)
readl(&fw_id->signature) == ZFIRM_ID;
}
-static inline int serial_paranoia_check(struct cyclades_port *info,
+static int serial_paranoia_check(struct cyclades_port *info,
const char *name, const char *routine)
{
#ifdef SERIAL_PARANOIA_CHECK
diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c
index 2054427992e0..99875949bfb7 100644
--- a/drivers/tty/isicom.c
+++ b/drivers/tty/isicom.c
@@ -220,7 +220,7 @@ static struct isi_port isi_ports[PORT_COUNT];
* it wants to talk.
*/
-static inline int WaitTillCardIsFree(unsigned long base)
+static int WaitTillCardIsFree(unsigned long base)
{
unsigned int count = 0;
unsigned int a = in_atomic(); /* do we run under spinlock? */
@@ -280,7 +280,7 @@ static void raise_dtr(struct isi_port *port)
}
/* card->lock HAS to be held */
-static inline void drop_dtr(struct isi_port *port)
+static void drop_dtr(struct isi_port *port)
{
struct isi_board *card = port->card;
unsigned long base = card->base;
diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c
index 14c54e041065..92982d7c0489 100644
--- a/drivers/tty/moxa.c
+++ b/drivers/tty/moxa.c
@@ -155,7 +155,6 @@ struct mon_str {
#define LOWWAIT 2
#define EMPTYWAIT 3
-#define SERIAL_DO_RESTART
#define WAKEUP_CHARS 256
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index ed776149261e..d9a5fc28fef4 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -162,12 +162,23 @@ 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)
+static int tty_copy_to_user(struct tty_struct *tty, void __user *to,
+ size_t tail, size_t n)
{
struct n_tty_data *ldata = tty->disc_data;
+ size_t size = N_TTY_BUF_SIZE - tail;
+ const void *from = read_buf_addr(ldata, tail);
+ int uncopied;
+
+ if (n > size) {
+ tty_audit_add_data(tty, from, size, ldata->icanon);
+ uncopied = copy_to_user(to, from, size);
+ if (uncopied)
+ return uncopied;
+ to += size;
+ n -= size;
+ from = ldata->read_buf;
+ }
tty_audit_add_data(tty, from, n, ldata->icanon);
return copy_to_user(to, from, n);
@@ -1201,9 +1212,7 @@ static void n_tty_receive_overrun(struct tty_struct *tty)
ldata->num_overrun++;
if (time_after(jiffies, ldata->overrun_time + HZ) ||
time_after(ldata->overrun_time, jiffies)) {
- printk(KERN_WARNING "%s: %d input overrun(s)\n",
- tty_name(tty),
- ldata->num_overrun);
+ tty_warn(tty, "%d input overrun(s)\n", ldata->num_overrun);
ldata->overrun_time = jiffies;
ldata->num_overrun = 0;
}
@@ -1486,8 +1495,7 @@ n_tty_receive_char_flagged(struct tty_struct *tty, unsigned char c, char flag)
n_tty_receive_overrun(tty);
break;
default:
- printk(KERN_ERR "%s: unknown flag %d\n",
- tty_name(tty), flag);
+ tty_err(tty, "unknown flag %d\n", flag);
break;
}
}
@@ -2006,11 +2014,11 @@ static int copy_from_read_buf(struct tty_struct *tty,
n = min(head - ldata->read_tail, N_TTY_BUF_SIZE - tail);
n = min(*nr, n);
if (n) {
- retval = copy_to_user(*b, read_buf_addr(ldata, tail), n);
+ const unsigned char *from = read_buf_addr(ldata, tail);
+ retval = copy_to_user(*b, from, n);
n -= retval;
- is_eof = n == 1 && read_buf(ldata, tail) == EOF_CHAR(tty);
- tty_audit_add_data(tty, read_buf_addr(ldata, tail), n,
- ldata->icanon);
+ is_eof = n == 1 && *from == EOF_CHAR(tty);
+ tty_audit_add_data(tty, from, n, ldata->icanon);
smp_store_release(&ldata->read_tail, ldata->read_tail + n);
/* Turn single EOF into zero-length read */
if (L_EXTPROC(tty) && ldata->icanon && is_eof &&
@@ -2054,13 +2062,13 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
size_t eol;
size_t tail;
int ret, found = 0;
- bool eof_push = 0;
/* N.B. avoid overrun if nr == 0 */
- n = min(*nr, smp_load_acquire(&ldata->canon_head) - ldata->read_tail);
- if (!n)
+ if (!*nr)
return 0;
+ n = min(*nr + 1, smp_load_acquire(&ldata->canon_head) - ldata->read_tail);
+
tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1);
size = min_t(size_t, tail + n, N_TTY_BUF_SIZE);
@@ -2072,34 +2080,24 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
if (eol == N_TTY_BUF_SIZE && more) {
/* scan wrapped without finding set bit */
eol = find_next_bit(ldata->read_flags, more, 0);
- if (eol != more)
- found = 1;
- } else if (eol != size)
- found = 1;
+ found = eol != more;
+ } else
+ found = eol != size;
- size = N_TTY_BUF_SIZE - tail;
n = eol - tail;
if (n > N_TTY_BUF_SIZE)
n += N_TTY_BUF_SIZE;
- n += found;
- c = n;
+ c = n + found;
- if (found && !ldata->push && read_buf(ldata, eol) == __DISABLED_CHAR) {
- n--;
- eof_push = !n && ldata->read_tail != ldata->line_start;
+ if (!found || read_buf(ldata, eol) != __DISABLED_CHAR) {
+ c = min(*nr, c);
+ n = c;
}
- n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu size:%zu more:%zu\n",
- __func__, eol, found, n, c, size, more);
-
- if (n > size) {
- ret = tty_copy_to_user(tty, *b, read_buf_addr(ldata, tail), size);
- if (ret)
- return -EFAULT;
- ret = tty_copy_to_user(tty, *b + size, ldata->read_buf, n - size);
- } else
- ret = tty_copy_to_user(tty, *b, read_buf_addr(ldata, tail), n);
+ n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu tail:%zu more:%zu\n",
+ __func__, eol, found, n, c, tail, more);
+ ret = tty_copy_to_user(tty, *b, tail, n);
if (ret)
return -EFAULT;
*b += n;
@@ -2116,7 +2114,7 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
ldata->push = 0;
tty_audit_push(tty);
}
- return eof_push ? -EAGAIN : 0;
+ return 0;
}
extern ssize_t redirected_tty_write(struct file *, const char __user *,
@@ -2273,10 +2271,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
if (ldata->icanon && !L_EXTPROC(tty)) {
retval = canon_copy_from_read_buf(tty, &b, &nr);
- if (retval == -EAGAIN) {
- retval = 0;
- continue;
- } else if (retval)
+ if (retval)
break;
} else {
int uncopied;
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index a45660f62db5..b3110040164a 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -788,7 +788,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
if (retval)
goto err_release;
- tty_debug_hangup(tty, "(tty count=%d)\n", tty->count);
+ tty_debug_hangup(tty, "opening (count=%d)\n", tty->count);
tty_unlock(tty);
return 0;
diff --git a/drivers/tty/serial/68328serial.c b/drivers/tty/serial/68328serial.c
index 0140ba4aacde..0982c1a44187 100644
--- a/drivers/tty/serial/68328serial.c
+++ b/drivers/tty/serial/68328serial.c
@@ -157,7 +157,7 @@ static void change_speed(struct m68k_serial *info, struct tty_struct *tty);
#endif
-static int m68328_console_initted = 0;
+static int m68328_console_initted;
static int m68328_console_baud = CONSOLE_BAUD_RATE;
static int m68328_console_cbaud = DEFAULT_CBAUD;
@@ -274,8 +274,8 @@ static void receive_chars(struct m68k_serial *info, unsigned short rx)
#endif
ch = GET_FIELD(rx, URX_RXDATA);
- if(info->is_cons) {
- if(URX_BREAK & rx) { /* whee, break received */
+ if (info->is_cons) {
+ if (URX_BREAK & rx) { /* whee, break received */
return;
#ifdef CONFIG_MAGIC_SYSRQ
} else if (ch == 0x10) { /* ^P */
@@ -302,7 +302,7 @@ static void receive_chars(struct m68k_serial *info, unsigned short rx)
tty_insert_flip_char(&info->tport, ch, flag);
#ifndef CONFIG_XCOPILOT_BUGS
- } while((rx = uart->urx.w) & URX_DATA_READY);
+ } while ((rx = uart->urx.w) & URX_DATA_READY);
#endif
tty_schedule_flip(&info->tport);
@@ -330,7 +330,7 @@ static void transmit_chars(struct m68k_serial *info, struct tty_struct *tty)
info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
info->xmit_cnt--;
- if(info->xmit_cnt <= 0) {
+ if (info->xmit_cnt <= 0) {
/* All done for now... TX ints off */
uart->ustcnt &= ~USTCNT_TX_INTR_MASK;
goto clear_and_return;
@@ -452,45 +452,45 @@ struct {
}
#ifndef CONFIG_M68VZ328
hw_baud_table[18] = {
- {0,0}, /* 0 */
- {0,0}, /* 50 */
- {0,0}, /* 75 */
- {0,0}, /* 110 */
- {0,0}, /* 134 */
- {0,0}, /* 150 */
- {0,0}, /* 200 */
- {7,0x26}, /* 300 */
- {6,0x26}, /* 600 */
- {5,0x26}, /* 1200 */
- {0,0}, /* 1800 */
- {4,0x26}, /* 2400 */
- {3,0x26}, /* 4800 */
- {2,0x26}, /* 9600 */
- {1,0x26}, /* 19200 */
- {0,0x26}, /* 38400 */
- {1,0x38}, /* 57600 */
- {0,0x38}, /* 115200 */
+ {0, 0}, /* 0 */
+ {0, 0}, /* 50 */
+ {0, 0}, /* 75 */
+ {0, 0}, /* 110 */
+ {0, 0}, /* 134 */
+ {0, 0}, /* 150 */
+ {0, 0}, /* 200 */
+ {7, 0x26}, /* 300 */
+ {6, 0x26}, /* 600 */
+ {5, 0x26}, /* 1200 */
+ {0, 0}, /* 1800 */
+ {4, 0x26}, /* 2400 */
+ {3, 0x26}, /* 4800 */
+ {2, 0x26}, /* 9600 */
+ {1, 0x26}, /* 19200 */
+ {0, 0x26}, /* 38400 */
+ {1, 0x38}, /* 57600 */
+ {0, 0x38}, /* 115200 */
};
#else
hw_baud_table[18] = {
- {0,0}, /* 0 */
- {0,0}, /* 50 */
- {0,0}, /* 75 */
- {0,0}, /* 110 */
- {0,0}, /* 134 */
- {0,0}, /* 150 */
- {0,0}, /* 200 */
- {0,0}, /* 300 */
- {7,0x26}, /* 600 */
- {6,0x26}, /* 1200 */
- {0,0}, /* 1800 */
- {5,0x26}, /* 2400 */
- {4,0x26}, /* 4800 */
- {3,0x26}, /* 9600 */
- {2,0x26}, /* 19200 */
- {1,0x26}, /* 38400 */
- {0,0x26}, /* 57600 */
- {1,0x38}, /* 115200 */
+ {0, 0}, /* 0 */
+ {0, 0}, /* 50 */
+ {0, 0}, /* 75 */
+ {0, 0}, /* 110 */
+ {0, 0}, /* 134 */
+ {0, 0}, /* 150 */
+ {0, 0}, /* 200 */
+ {0, 0}, /* 300 */
+ {7, 0x26}, /* 600 */
+ {6, 0x26}, /* 1200 */
+ {0, 0}, /* 1800 */
+ {5, 0x26}, /* 2400 */
+ {4, 0x26}, /* 4800 */
+ {3, 0x26}, /* 9600 */
+ {2, 0x26}, /* 19200 */
+ {1, 0x26}, /* 38400 */
+ {0, 0x26}, /* 57600 */
+ {1, 0x38}, /* 115200 */
};
#endif
/* rate = 1036800 / ((65 - prescale) * (1<<divider)) */
@@ -538,7 +538,7 @@ static void change_speed(struct m68k_serial *info, struct tty_struct *tty)
#ifdef CONFIG_SERIAL_68328_RTS_CTS
if (cflag & CRTSCTS) {
- uart->utx.w &= ~ UTX_NOCTS;
+ uart->utx.w &= ~UTX_NOCTS;
} else {
uart->utx.w |= UTX_NOCTS;
}
@@ -591,8 +591,8 @@ void console_print_68328(const char *p)
{
char c;
- while((c=*(p++)) != 0) {
- if(c == '\n')
+ while ((c = *(p++)) != 0) {
+ if (c == '\n')
rs_put_char('\r');
rs_put_char(c);
}
@@ -624,7 +624,7 @@ static void rs_flush_chars(struct tty_struct *tty)
if (serial_paranoia_check(info, tty->name, "rs_flush_chars"))
return;
#ifndef USE_INTS
- for(;;) {
+ for (;;) {
#endif
/* Enable transmitter */
@@ -659,9 +659,9 @@ static void rs_flush_chars(struct tty_struct *tty)
local_irq_restore(flags);
}
-extern void console_printn(const char * b, int count);
+extern void console_printn(const char *b, int count);
-static int rs_write(struct tty_struct * tty,
+static int rs_write(struct tty_struct *tty,
const unsigned char *buf, int count)
{
int c, total = 0;
@@ -700,7 +700,7 @@ static int rs_write(struct tty_struct * tty,
/* Enable transmitter */
local_irq_disable();
#ifndef USE_INTS
- while(info->xmit_cnt) {
+ while (info->xmit_cnt) {
#endif
uart->ustcnt |= USTCNT_TXEN;
@@ -767,7 +767,7 @@ static void rs_flush_buffer(struct tty_struct *tty)
* incoming characters should be throttled.
* ------------------------------------------------------------
*/
-static void rs_throttle(struct tty_struct * tty)
+static void rs_throttle(struct tty_struct *tty)
{
struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
@@ -780,7 +780,7 @@ static void rs_throttle(struct tty_struct * tty)
/* Turn off RTS line (do this atomic) */
}
-static void rs_unthrottle(struct tty_struct * tty)
+static void rs_unthrottle(struct tty_struct *tty)
{
struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
@@ -803,8 +803,8 @@ static void rs_unthrottle(struct tty_struct * tty)
* ------------------------------------------------------------
*/
-static int get_serial_info(struct m68k_serial * info,
- struct serial_struct * retinfo)
+static int get_serial_info(struct m68k_serial *info,
+ struct serial_struct *retinfo)
{
struct serial_struct tmp;
@@ -827,7 +827,7 @@ static int get_serial_info(struct m68k_serial * info,
}
static int set_serial_info(struct m68k_serial *info, struct tty_struct *tty,
- struct serial_struct * new_info)
+ struct serial_struct *new_info)
{
struct tty_port *port = &info->tport;
struct serial_struct new_serial;
@@ -883,7 +883,7 @@ check_and_exit:
* transmit holding register is empty. This functionality
* allows an RS485 driver to be written in user space.
*/
-static int get_lsr_info(struct m68k_serial * info, unsigned int *value)
+static int get_lsr_info(struct m68k_serial *info, unsigned int *value)
{
#ifdef CONFIG_SERIAL_68328_RTS_CTS
m68328_uart *uart = &uart_addr[info->line];
@@ -904,7 +904,7 @@ static int get_lsr_info(struct m68k_serial * info, unsigned int *value)
/*
* This routine sends a break character out the serial port.
*/
-static void send_break(struct m68k_serial * info, unsigned int duration)
+static void send_break(struct m68k_serial *info, unsigned int duration)
{
m68328_uart *uart = &uart_addr[info->line];
unsigned long flags;
@@ -922,7 +922,7 @@ static void send_break(struct m68k_serial * info, unsigned int duration)
static int rs_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
- struct m68k_serial * info = (struct m68k_serial *)tty->driver_data;
+ struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
int retval;
if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
@@ -992,9 +992,9 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
* that IRQ if nothing is left in the chain.
* ------------------------------------------------------------
*/
-static void rs_close(struct tty_struct *tty, struct file * filp)
+static void rs_close(struct tty_struct *tty, struct file *filp)
{
- struct m68k_serial * info = (struct m68k_serial *)tty->driver_data;
+ struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
struct tty_port *port = &info->tport;
m68328_uart *uart = &uart_addr[info->line];
unsigned long flags;
@@ -1079,7 +1079,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
*/
void rs_hangup(struct tty_struct *tty)
{
- struct m68k_serial * info = (struct m68k_serial *)tty->driver_data;
+ struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
if (serial_paranoia_check(info, tty->name, "rs_hangup"))
return;
@@ -1098,7 +1098,7 @@ void rs_hangup(struct tty_struct *tty)
* the IRQ chain. It also performs the serial-specific
* initialization for the tty structure.
*/
-int rs_open(struct tty_struct *tty, struct file * filp)
+int rs_open(struct tty_struct *tty, struct file *filp)
{
struct m68k_serial *info;
int retval;
@@ -1180,7 +1180,7 @@ rs68328_init(void)
local_irq_save(flags);
- for(i=0;i<NR_PORTS;i++) {
+ for (i = 0; i < NR_PORTS; i++) {
info = &m68k_soft[i];
tty_port_init(&info->tport);
@@ -1198,7 +1198,7 @@ rs68328_init(void)
printk(" is a builtin MC68328 UART\n");
#ifdef CONFIG_M68VZ328
- if (i > 0 )
+ if (i > 0)
PJSEL &= 0xCF; /* PSW enable second port output */
#endif
@@ -1263,7 +1263,7 @@ int m68328_console_setup(struct console *cp, char *arg)
return(-1);
if (arg)
- n = simple_strtoul(arg,NULL,0);
+ n = simple_strtoul(arg, NULL, 0);
for (i = 0; i < ARRAY_SIZE(baud_table); i++)
if (baud_table[i] == n)
@@ -1279,7 +1279,7 @@ int m68328_console_setup(struct console *cp, char *arg)
}
m68328_set_baud(); /* make sure baud rate changes */
- return(0);
+ return 0;
}
@@ -1298,7 +1298,7 @@ void m68328_console_write (struct console *co, const char *str,
while (count--) {
if (*str == '\n')
rs_put_char('\r');
- rs_put_char( *str++ );
+ rs_put_char(*str++);
}
}
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 39126460c1f5..c9720a97a977 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -620,7 +620,7 @@ static int univ8250_console_setup(struct console *co, char *options)
* @options: ptr to option string from console command line
*
* Only attempts to match console command lines of the form:
- * console=uart[8250],io|mmio|mmio32,<addr>[,<options>]
+ * console=uart[8250],io|mmio|mmio16|mmio32,<addr>[,<options>]
* console=uart[8250],0x<addr>[,<options>]
* This form is used to register an initial earlycon boot console and
* replace it with the serial8250_console at 8250 driver init.
@@ -650,8 +650,9 @@ static int univ8250_console_match(struct console *co, char *name, int idx,
if (port->iotype != iotype)
continue;
- if ((iotype == UPIO_MEM || iotype == UPIO_MEM32) &&
- (port->mapbase != addr))
+ if ((iotype == UPIO_MEM || iotype == UPIO_MEM16 ||
+ iotype == UPIO_MEM32 || iotype == UPIO_MEM32BE)
+ && (port->mapbase != addr))
continue;
if (iotype == UPIO_PORT && port->iobase != addr)
continue;
diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
index ceb85792a5cf..af62131af21e 100644
--- a/drivers/tty/serial/8250/8250_early.c
+++ b/drivers/tty/serial/8250/8250_early.c
@@ -42,6 +42,8 @@ static unsigned int __init serial8250_early_in(struct uart_port *port, int offse
switch (port->iotype) {
case UPIO_MEM:
return readb(port->membase + offset);
+ case UPIO_MEM16:
+ return readw(port->membase + (offset << 1));
case UPIO_MEM32:
return readl(port->membase + (offset << 2));
case UPIO_MEM32BE:
@@ -59,6 +61,9 @@ static void __init serial8250_early_out(struct uart_port *port, int offset, int
case UPIO_MEM:
writeb(value, port->membase + offset);
break;
+ case UPIO_MEM16:
+ writew(value, port->membase + (offset << 1));
+ break;
case UPIO_MEM32:
writel(value, port->membase + (offset << 2));
break;
@@ -73,43 +78,27 @@ static void __init serial8250_early_out(struct uart_port *port, int offset, int
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
-static void __init wait_for_xmitr(struct uart_port *port)
+static void __init serial_putc(struct uart_port *port, int c)
{
unsigned int status;
+ serial8250_early_out(port, UART_TX, c);
+
for (;;) {
status = serial8250_early_in(port, UART_LSR);
if ((status & BOTH_EMPTY) == BOTH_EMPTY)
- return;
+ break;
cpu_relax();
}
}
-static void __init serial_putc(struct uart_port *port, int c)
-{
- wait_for_xmitr(port);
- serial8250_early_out(port, UART_TX, c);
-}
-
static void __init early_serial8250_write(struct console *console,
const char *s, unsigned int count)
{
struct earlycon_device *device = console->data;
struct uart_port *port = &device->port;
- unsigned int ier;
-
- /* Save the IER and disable interrupts preserving the UUE bit */
- ier = serial8250_early_in(port, UART_IER);
- if (ier)
- serial8250_early_out(port, UART_IER, ier & UART_IER_UUE);
uart_console_write(port, s, count, serial_putc);
-
- /* Wait for transmitter to become empty and restore the IER */
- wait_for_xmitr(port);
-
- if (ier)
- serial8250_early_out(port, UART_IER, ier);
}
static void __init init_port(struct earlycon_device *device)
diff --git a/drivers/tty/serial/8250/8250_ingenic.c b/drivers/tty/serial/8250/8250_ingenic.c
index 49394b4c5cfd..d6e1ec9b4fde 100644
--- a/drivers/tty/serial/8250/8250_ingenic.c
+++ b/drivers/tty/serial/8250/8250_ingenic.c
@@ -48,6 +48,7 @@ static const struct of_device_id of_match[];
#define UART_MCR_MDCE BIT(7)
#define UART_MCR_FCM BIT(6)
+#ifdef CONFIG_SERIAL_EARLYCON
static struct earlycon_device *early_device;
static uint8_t __init early_in(struct uart_port *port, int offset)
@@ -140,6 +141,7 @@ OF_EARLYCON_DECLARE(jz4775_uart, "ingenic,jz4775-uart",
EARLYCON_DECLARE(jz4780_uart, ingenic_early_console_setup);
OF_EARLYCON_DECLARE(jz4780_uart, "ingenic,jz4780-uart",
ingenic_early_console_setup);
+#endif /* CONFIG_SERIAL_EARLYCON */
static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value)
{
diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c
index 78883ca64ddd..0e590b233f03 100644
--- a/drivers/tty/serial/8250/8250_mtk.c
+++ b/drivers/tty/serial/8250/8250_mtk.c
@@ -16,7 +16,7 @@
*/
#include <linux/clk.h>
#include <linux/io.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
@@ -245,23 +245,6 @@ static int mtk8250_probe(struct platform_device *pdev)
return 0;
}
-static int mtk8250_remove(struct platform_device *pdev)
-{
- struct mtk8250_data *data = platform_get_drvdata(pdev);
-
- pm_runtime_get_sync(&pdev->dev);
-
- serial8250_unregister_port(data->line);
-
- pm_runtime_disable(&pdev->dev);
- pm_runtime_put_noidle(&pdev->dev);
-
- if (!pm_runtime_status_suspended(&pdev->dev))
- mtk8250_runtime_suspend(&pdev->dev);
-
- return 0;
-}
-
#ifdef CONFIG_PM_SLEEP
static int mtk8250_suspend(struct device *dev)
{
@@ -292,18 +275,18 @@ static const struct of_device_id mtk8250_of_match[] = {
{ .compatible = "mediatek,mt6577-uart" },
{ /* Sentinel */ }
};
-MODULE_DEVICE_TABLE(of, mtk8250_of_match);
static struct platform_driver mtk8250_platform_driver = {
.driver = {
- .name = "mt6577-uart",
- .pm = &mtk8250_pm_ops,
- .of_match_table = mtk8250_of_match,
+ .name = "mt6577-uart",
+ .pm = &mtk8250_pm_ops,
+ .of_match_table = mtk8250_of_match,
+ .suppress_bind_attrs = true,
+
},
.probe = mtk8250_probe,
- .remove = mtk8250_remove,
};
-module_platform_driver(mtk8250_platform_driver);
+builtin_platform_driver(mtk8250_platform_driver);
#ifdef CONFIG_SERIAL_8250_CONSOLE
static int __init early_mtk8250_setup(struct earlycon_device *device,
@@ -319,7 +302,3 @@ static int __init early_mtk8250_setup(struct earlycon_device *device,
OF_EARLYCON_DECLARE(mtk8250, "mediatek,mt6577-uart", early_mtk8250_setup);
#endif
-
-MODULE_AUTHOR("Matthias Brugger");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Mediatek 8250 serial port driver");
diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/8250/8250_of.c
index de5029649795..33021c1f7d55 100644
--- a/drivers/tty/serial/of_serial.c
+++ b/drivers/tty/serial/8250/8250_of.c
@@ -18,14 +18,9 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
-#include <linux/nwpserial.h>
#include <linux/clk.h>
-#ifdef CONFIG_SERIAL_8250_MODULE
-#define CONFIG_SERIAL_8250 CONFIG_SERIAL_8250_MODULE
-#endif
-
-#include "8250/8250.h"
+#include "8250.h"
struct of_serial_info {
struct clk *clk;
@@ -122,6 +117,9 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
case 1:
port->iotype = UPIO_MEM;
break;
+ case 2:
+ port->iotype = UPIO_MEM16;
+ break;
case 4:
port->iotype = of_device_is_big_endian(np) ?
UPIO_MEM32BE : UPIO_MEM32;
@@ -195,7 +193,6 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
goto out;
switch (port_type) {
-#ifdef CONFIG_SERIAL_8250
case PORT_8250 ... PORT_MAX_8250:
{
struct uart_8250_port port8250;
@@ -212,12 +209,6 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
ret = serial8250_register_8250_port(&port8250);
break;
}
-#endif
-#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL
- case PORT_NWPSERIAL:
- ret = nwpserial_register_port(&port);
- break;
-#endif
default:
/* need to add code for these */
case PORT_UNKNOWN:
@@ -245,16 +236,9 @@ static int of_platform_serial_remove(struct platform_device *ofdev)
{
struct of_serial_info *info = platform_get_drvdata(ofdev);
switch (info->type) {
-#ifdef CONFIG_SERIAL_8250
case PORT_8250 ... PORT_MAX_8250:
serial8250_unregister_port(info->line);
break;
-#endif
-#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL
- case PORT_NWPSERIAL:
- nwpserial_unregister_port(info->line);
- break;
-#endif
default:
/* need to add code for these */
break;
@@ -267,7 +251,6 @@ static int of_platform_serial_remove(struct platform_device *ofdev)
}
#ifdef CONFIG_PM_SLEEP
-#ifdef CONFIG_SERIAL_8250
static void of_serial_suspend_8250(struct of_serial_info *info)
{
struct uart_8250_port *port8250 = serial8250_get_port(info->line);
@@ -288,15 +271,6 @@ static void of_serial_resume_8250(struct of_serial_info *info)
serial8250_resume_port(info->line);
}
-#else
-static inline void of_serial_suspend_8250(struct of_serial_info *info)
-{
-}
-
-static inline void of_serial_resume_8250(struct of_serial_info *info)
-{
-}
-#endif
static int of_serial_suspend(struct device *dev)
{
@@ -353,10 +327,6 @@ static const struct of_device_id of_platform_serial_table[] = {
.data = (void *)PORT_XSCALE, },
{ .compatible = "mrvl,pxa-uart",
.data = (void *)PORT_XSCALE, },
-#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL
- { .compatible = "ibm,qpace-nwp-serial",
- .data = (void *)PORT_NWPSERIAL, },
-#endif
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(of, of_platform_serial_table);
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 52d82d2ac726..8d262bce97e4 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -368,6 +368,18 @@ static void mem_serial_out(struct uart_port *p, int offset, int value)
writeb(value, p->membase + offset);
}
+static void mem16_serial_out(struct uart_port *p, int offset, int value)
+{
+ offset = offset << p->regshift;
+ writew(value, p->membase + offset);
+}
+
+static unsigned int mem16_serial_in(struct uart_port *p, int offset)
+{
+ offset = offset << p->regshift;
+ return readw(p->membase + offset);
+}
+
static void mem32_serial_out(struct uart_port *p, int offset, int value)
{
offset = offset << p->regshift;
@@ -425,6 +437,11 @@ static void set_io_from_upio(struct uart_port *p)
p->serial_out = mem_serial_out;
break;
+ case UPIO_MEM16:
+ p->serial_in = mem16_serial_in;
+ p->serial_out = mem16_serial_out;
+ break;
+
case UPIO_MEM32:
p->serial_in = mem32_serial_in;
p->serial_out = mem32_serial_out;
@@ -459,6 +476,7 @@ serial_port_out_sync(struct uart_port *p, int offset, int value)
{
switch (p->iotype) {
case UPIO_MEM:
+ case UPIO_MEM16:
case UPIO_MEM32:
case UPIO_MEM32BE:
case UPIO_AU:
@@ -2462,6 +2480,7 @@ static int serial8250_request_std_resource(struct uart_8250_port *up)
case UPIO_TSI:
case UPIO_MEM32:
case UPIO_MEM32BE:
+ case UPIO_MEM16:
case UPIO_MEM:
if (!port->mapbase)
break;
@@ -2499,6 +2518,7 @@ static void serial8250_release_std_resource(struct uart_8250_port *up)
case UPIO_TSI:
case UPIO_MEM32:
case UPIO_MEM32BE:
+ case UPIO_MEM16:
case UPIO_MEM:
if (!port->mapbase)
break;
diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c
index d11621e2cf1d..bab6b3ae2540 100644
--- a/drivers/tty/serial/8250/8250_uniphier.c
+++ b/drivers/tty/serial/8250/8250_uniphier.c
@@ -13,6 +13,7 @@
*/
#include <linux/clk.h>
+#include <linux/console.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -34,6 +35,29 @@ struct uniphier8250_priv {
spinlock_t atomic_write_lock;
};
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+static int __init uniphier_early_console_setup(struct earlycon_device *device,
+ const char *options)
+{
+ if (!device->port.membase)
+ return -ENODEV;
+
+ /* This hardware always expects MMIO32 register interface. */
+ device->port.iotype = UPIO_MEM32;
+ device->port.regshift = 2;
+
+ /*
+ * Do not touch the divisor register in early_serial8250_setup();
+ * we assume it has been initialized by a boot loader.
+ */
+ device->baud = 0;
+
+ return early_serial8250_setup(device, options);
+}
+OF_EARLYCON_DECLARE(uniphier, "socionext,uniphier-uart",
+ uniphier_early_console_setup);
+#endif
+
/*
* The register map is slightly different from that of 8250.
* IO callbacks must be overridden for correct access to FCR, LCR, and MCR.
@@ -115,12 +139,16 @@ static void uniphier_serial_out(struct uart_port *p, int offset, int value)
*/
static int uniphier_serial_dl_read(struct uart_8250_port *up)
{
- return readl(up->port.membase + UNIPHIER_UART_DLR);
+ int offset = UNIPHIER_UART_DLR << up->port.regshift;
+
+ return readl(up->port.membase + offset);
}
static void uniphier_serial_dl_write(struct uart_8250_port *up, int value)
{
- writel(value, up->port.membase + UNIPHIER_UART_DLR);
+ int offset = UNIPHIER_UART_DLR << up->port.regshift;
+
+ writel(value, up->port.membase + offset);
}
static int uniphier_of_serial_setup(struct device *dev, struct uart_port *port,
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index 6412f1455beb..b03cb5175113 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -361,9 +361,8 @@ config SERIAL_8250_UNIPHIER
config SERIAL_8250_INGENIC
bool "Support for Ingenic SoC serial ports"
- depends on SERIAL_8250_CONSOLE && OF_FLATTREE
+ depends on OF_FLATTREE
select LIBFDT
- select SERIAL_EARLYCON
help
If you have a system using an Ingenic SoC and wish to make use of
its UARTs, say Y to this option. If unsure, say N.
@@ -372,9 +371,18 @@ config SERIAL_8250_MID
tristate "Support for serial ports on Intel MID platforms"
depends on SERIAL_8250 && PCI
select HSU_DMA if SERIAL_8250_DMA
- select HSU_DMA_PCI if X86_INTEL_MID
+ select HSU_DMA_PCI if (HSU_DMA && X86_INTEL_MID)
select RATIONAL
help
Selecting this option will enable handling of the extra features
present on the UART found on Intel Medfield SOC and various other
Intel platforms.
+
+config SERIAL_OF_PLATFORM
+ tristate "Devicetree based probing for 8250 ports"
+ depends on SERIAL_8250 && OF
+ help
+ This option is used for all 8250 compatible serial ports that
+ are probed through devicetree, including Open Firmware based
+ PowerPC systems and embedded systems on architectures using the
+ flattened device tree format.
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index e177f8681ada..b9b9bca5b6c3 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -28,5 +28,6 @@ obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o
obj-$(CONFIG_SERIAL_8250_UNIPHIER) += 8250_uniphier.o
obj-$(CONFIG_SERIAL_8250_INGENIC) += 8250_ingenic.o
obj-$(CONFIG_SERIAL_8250_MID) += 8250_mid.o
+obj-$(CONFIG_SERIAL_OF_PLATFORM) += 8250_of.o
CFLAGS_8250_ingenic.o += -I$(srctree)/scripts/dtc/libfdt
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index f38beb28e7ae..d27a0c62a75f 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -115,6 +115,7 @@ config SERIAL_SB1250_DUART_CONSOLE
config SERIAL_ATMEL
bool "AT91 / AT32 on-chip serial port support"
+ depends on HAS_DMA
depends on ARCH_AT91 || AVR32 || COMPILE_TEST
select SERIAL_CORE
select SERIAL_MCTRL_GPIO if GPIOLIB
@@ -571,9 +572,11 @@ config BFIN_UART3_CTSRTS
config SERIAL_IMX
tristate "IMX serial port support"
+ depends on HAS_DMA
depends on ARCH_MXC || COMPILE_TEST
select SERIAL_CORE
select RATIONAL
+ select SERIAL_MCTRL_GPIO if GPIOLIB
help
If you have a machine based on a Motorola IMX CPU you
can enable its onboard serial port by enabling this option.
@@ -1094,16 +1097,6 @@ config SERIAL_NETX_CONSOLE
If you have enabled the serial port on the Hilscher NetX SoC
you can make it the console by answering Y to this option.
-config SERIAL_OF_PLATFORM
- tristate "Serial port on Open Firmware platform bus"
- depends on OF
- depends on SERIAL_8250 || SERIAL_OF_PLATFORM_NWPSERIAL
- help
- If you have a PowerPC based system that has serial ports
- on a platform specific bus, you should enable this option.
- Currently, only 8250 compatible ports are supported, but
- others can easily be added.
-
config SERIAL_OMAP
tristate "OMAP serial port support"
depends on ARCH_OMAP2PLUS
@@ -1131,23 +1124,6 @@ config SERIAL_OMAP_CONSOLE
your boot loader about how to pass options to the kernel at
boot time.)
-config SERIAL_OF_PLATFORM_NWPSERIAL
- tristate "NWP serial port driver"
- depends on PPC_DCR
- select SERIAL_OF_PLATFORM
- select SERIAL_CORE_CONSOLE
- select SERIAL_CORE
- help
- This driver supports the cell network processor nwp serial
- device.
-
-config SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE
- bool "Console on NWP serial port"
- depends on SERIAL_OF_PLATFORM_NWPSERIAL=y
- select SERIAL_CORE_CONSOLE
- help
- Support for Console on the NWP serial ports.
-
config SERIAL_LANTIQ
bool "Lantiq serial driver"
depends on LANTIQ
@@ -1409,8 +1385,9 @@ config SERIAL_PCH_UART_CONSOLE
warnings and which allows logins in single user mode).
config SERIAL_MXS_AUART
- depends on ARCH_MXS || COMPILE_TEST
tristate "MXS AUART support"
+ depends on HAS_DMA
+ depends on ARCH_MXS || COMPILE_TEST
select SERIAL_CORE
select SERIAL_MCTRL_GPIO if GPIOLIB
help
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 5ab41119b3dc..b391c9b31960 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -63,8 +63,6 @@ obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o
obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
obj-$(CONFIG_SERIAL_MSM) += msm_serial.o
obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
-obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
-obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o
obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o
obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 899a77187bde..c0da0ccbbcf5 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -60,6 +60,8 @@
#include <linux/io.h>
#include <linux/acpi.h>
+#include "amba-pl011.h"
+
#define UART_NR 14
#define SERIAL_AMBA_MAJOR 204
@@ -71,11 +73,27 @@
#define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE)
#define UART_DUMMY_DR_RX (1 << 16)
+static u16 pl011_std_offsets[REG_ARRAY_SIZE] = {
+ [REG_DR] = UART01x_DR,
+ [REG_FR] = UART01x_FR,
+ [REG_LCRH_RX] = UART011_LCRH,
+ [REG_LCRH_TX] = UART011_LCRH,
+ [REG_IBRD] = UART011_IBRD,
+ [REG_FBRD] = UART011_FBRD,
+ [REG_CR] = UART011_CR,
+ [REG_IFLS] = UART011_IFLS,
+ [REG_IMSC] = UART011_IMSC,
+ [REG_RIS] = UART011_RIS,
+ [REG_MIS] = UART011_MIS,
+ [REG_ICR] = UART011_ICR,
+ [REG_DMACR] = UART011_DMACR,
+};
+
/* There is by now at least one vendor with differing details, so handle it */
struct vendor_data {
+ const u16 *reg_offset;
unsigned int ifls;
- unsigned int lcrh_tx;
- unsigned int lcrh_rx;
+ bool access_32b;
bool oversampling;
bool dma_threshold;
bool cts_event_workaround;
@@ -91,9 +109,8 @@ static unsigned int get_fifosize_arm(struct amba_device *dev)
}
static struct vendor_data vendor_arm = {
+ .reg_offset = pl011_std_offsets,
.ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
- .lcrh_tx = UART011_LCRH,
- .lcrh_rx = UART011_LCRH,
.oversampling = false,
.dma_threshold = false,
.cts_event_workaround = false,
@@ -103,6 +120,7 @@ static struct vendor_data vendor_arm = {
};
static struct vendor_data vendor_sbsa = {
+ .reg_offset = pl011_std_offsets,
.oversampling = false,
.dma_threshold = false,
.cts_event_workaround = false,
@@ -110,15 +128,41 @@ static struct vendor_data vendor_sbsa = {
.fixed_options = true,
};
+static u16 pl011_st_offsets[REG_ARRAY_SIZE] = {
+ [REG_DR] = UART01x_DR,
+ [REG_ST_DMAWM] = ST_UART011_DMAWM,
+ [REG_ST_TIMEOUT] = ST_UART011_TIMEOUT,
+ [REG_FR] = UART01x_FR,
+ [REG_LCRH_RX] = ST_UART011_LCRH_RX,
+ [REG_LCRH_TX] = ST_UART011_LCRH_TX,
+ [REG_IBRD] = UART011_IBRD,
+ [REG_FBRD] = UART011_FBRD,
+ [REG_CR] = UART011_CR,
+ [REG_IFLS] = UART011_IFLS,
+ [REG_IMSC] = UART011_IMSC,
+ [REG_RIS] = UART011_RIS,
+ [REG_MIS] = UART011_MIS,
+ [REG_ICR] = UART011_ICR,
+ [REG_DMACR] = UART011_DMACR,
+ [REG_ST_XFCR] = ST_UART011_XFCR,
+ [REG_ST_XON1] = ST_UART011_XON1,
+ [REG_ST_XON2] = ST_UART011_XON2,
+ [REG_ST_XOFF1] = ST_UART011_XOFF1,
+ [REG_ST_XOFF2] = ST_UART011_XOFF2,
+ [REG_ST_ITCR] = ST_UART011_ITCR,
+ [REG_ST_ITIP] = ST_UART011_ITIP,
+ [REG_ST_ABCR] = ST_UART011_ABCR,
+ [REG_ST_ABIMSC] = ST_UART011_ABIMSC,
+};
+
static unsigned int get_fifosize_st(struct amba_device *dev)
{
return 64;
}
static struct vendor_data vendor_st = {
+ .reg_offset = pl011_st_offsets,
.ifls = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF,
- .lcrh_tx = ST_UART011_LCRH_TX,
- .lcrh_rx = ST_UART011_LCRH_RX,
.oversampling = true,
.dma_threshold = true,
.cts_event_workaround = true,
@@ -127,6 +171,29 @@ static struct vendor_data vendor_st = {
.get_fifosize = get_fifosize_st,
};
+static const u16 pl011_zte_offsets[REG_ARRAY_SIZE] = {
+ [REG_DR] = ZX_UART011_DR,
+ [REG_FR] = ZX_UART011_FR,
+ [REG_LCRH_RX] = ZX_UART011_LCRH,
+ [REG_LCRH_TX] = ZX_UART011_LCRH,
+ [REG_IBRD] = ZX_UART011_IBRD,
+ [REG_FBRD] = ZX_UART011_FBRD,
+ [REG_CR] = ZX_UART011_CR,
+ [REG_IFLS] = ZX_UART011_IFLS,
+ [REG_IMSC] = ZX_UART011_IMSC,
+ [REG_RIS] = ZX_UART011_RIS,
+ [REG_MIS] = ZX_UART011_MIS,
+ [REG_ICR] = ZX_UART011_ICR,
+ [REG_DMACR] = ZX_UART011_DMACR,
+};
+
+static struct vendor_data vendor_zte = {
+ .reg_offset = pl011_zte_offsets,
+ .access_32b = true,
+ .ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
+ .get_fifosize = get_fifosize_arm,
+};
+
/* Deals with DMA transactions */
struct pl011_sgbuf {
@@ -162,14 +229,13 @@ struct pl011_dmatx_data {
*/
struct uart_amba_port {
struct uart_port port;
+ const u16 *reg_offset;
struct clk *clk;
const struct vendor_data *vendor;
unsigned int dmacr; /* dma control reg */
unsigned int im; /* interrupt mask */
unsigned int old_status;
unsigned int fifosize; /* vendor-specific */
- unsigned int lcrh_tx; /* vendor-specific */
- unsigned int lcrh_rx; /* vendor-specific */
unsigned int old_cr; /* state during shutdown */
bool autorts;
unsigned int fixed_baud; /* vendor-set fixed baud rate */
@@ -184,6 +250,32 @@ struct uart_amba_port {
#endif
};
+static unsigned int pl011_reg_to_offset(const struct uart_amba_port *uap,
+ unsigned int reg)
+{
+ return uap->reg_offset[reg];
+}
+
+static unsigned int pl011_read(const struct uart_amba_port *uap,
+ unsigned int reg)
+{
+ void __iomem *addr = uap->port.membase + pl011_reg_to_offset(uap, reg);
+
+ return (uap->port.iotype == UPIO_MEM32) ?
+ readl_relaxed(addr) : readw_relaxed(addr);
+}
+
+static void pl011_write(unsigned int val, const struct uart_amba_port *uap,
+ unsigned int reg)
+{
+ void __iomem *addr = uap->port.membase + pl011_reg_to_offset(uap, reg);
+
+ if (uap->port.iotype == UPIO_MEM32)
+ writel_relaxed(val, addr);
+ else
+ writew_relaxed(val, addr);
+}
+
/*
* Reads up to 256 characters from the FIFO or until it's empty and
* inserts them into the TTY layer. Returns the number of characters
@@ -196,13 +288,12 @@ static int pl011_fifo_to_tty(struct uart_amba_port *uap)
int fifotaken = 0;
while (max_count--) {
- status = readw(uap->port.membase + UART01x_FR);
+ status = pl011_read(uap, REG_FR);
if (status & UART01x_FR_RXFE)
break;
/* Take chars from the FIFO and update status */
- ch = readw(uap->port.membase + UART01x_DR) |
- UART_DUMMY_DR_RX;
+ ch = pl011_read(uap, REG_DR) | UART_DUMMY_DR_RX;
flag = TTY_NORMAL;
uap->port.icount.rx++;
fifotaken++;
@@ -284,7 +375,8 @@ static void pl011_dma_probe(struct uart_amba_port *uap)
struct amba_pl011_data *plat = dev_get_platdata(uap->port.dev);
struct device *dev = uap->port.dev;
struct dma_slave_config tx_conf = {
- .dst_addr = uap->port.mapbase + UART01x_DR,
+ .dst_addr = uap->port.mapbase +
+ pl011_reg_to_offset(uap, REG_DR),
.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
.direction = DMA_MEM_TO_DEV,
.dst_maxburst = uap->fifosize >> 1,
@@ -339,7 +431,8 @@ static void pl011_dma_probe(struct uart_amba_port *uap)
if (chan) {
struct dma_slave_config rx_conf = {
- .src_addr = uap->port.mapbase + UART01x_DR,
+ .src_addr = uap->port.mapbase +
+ pl011_reg_to_offset(uap, REG_DR),
.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
.direction = DMA_DEV_TO_MEM,
.src_maxburst = uap->fifosize >> 2,
@@ -438,7 +531,7 @@ static void pl011_dma_tx_callback(void *data)
dmacr = uap->dmacr;
uap->dmacr = dmacr & ~UART011_TXDMAE;
- writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+ pl011_write(uap->dmacr, uap, REG_DMACR);
/*
* If TX DMA was disabled, it means that we've stopped the DMA for
@@ -552,7 +645,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap)
dma_dev->device_issue_pending(chan);
uap->dmacr |= UART011_TXDMAE;
- writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+ pl011_write(uap->dmacr, uap, REG_DMACR);
uap->dmatx.queued = true;
/*
@@ -588,9 +681,9 @@ static bool pl011_dma_tx_irq(struct uart_amba_port *uap)
*/
if (uap->dmatx.queued) {
uap->dmacr |= UART011_TXDMAE;
- writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+ pl011_write(uap->dmacr, uap, REG_DMACR);
uap->im &= ~UART011_TXIM;
- writew(uap->im, uap->port.membase + UART011_IMSC);
+ pl011_write(uap->im, uap, REG_IMSC);
return true;
}
@@ -600,7 +693,7 @@ static bool pl011_dma_tx_irq(struct uart_amba_port *uap)
*/
if (pl011_dma_tx_refill(uap) > 0) {
uap->im &= ~UART011_TXIM;
- writew(uap->im, uap->port.membase + UART011_IMSC);
+ pl011_write(uap->im, uap, REG_IMSC);
return true;
}
return false;
@@ -614,7 +707,7 @@ static inline void pl011_dma_tx_stop(struct uart_amba_port *uap)
{
if (uap->dmatx.queued) {
uap->dmacr &= ~UART011_TXDMAE;
- writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+ pl011_write(uap->dmacr, uap, REG_DMACR);
}
}
@@ -640,14 +733,12 @@ static inline bool pl011_dma_tx_start(struct uart_amba_port *uap)
if (!uap->dmatx.queued) {
if (pl011_dma_tx_refill(uap) > 0) {
uap->im &= ~UART011_TXIM;
- writew(uap->im, uap->port.membase +
- UART011_IMSC);
+ pl011_write(uap->im, uap, REG_IMSC);
} else
ret = false;
} else if (!(uap->dmacr & UART011_TXDMAE)) {
uap->dmacr |= UART011_TXDMAE;
- writew(uap->dmacr,
- uap->port.membase + UART011_DMACR);
+ pl011_write(uap->dmacr, uap, REG_DMACR);
}
return ret;
}
@@ -658,9 +749,9 @@ static inline bool pl011_dma_tx_start(struct uart_amba_port *uap)
*/
dmacr = uap->dmacr;
uap->dmacr &= ~UART011_TXDMAE;
- writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+ pl011_write(uap->dmacr, uap, REG_DMACR);
- if (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) {
+ if (pl011_read(uap, REG_FR) & UART01x_FR_TXFF) {
/*
* No space in the FIFO, so enable the transmit interrupt
* so we know when there is space. Note that once we've
@@ -669,13 +760,13 @@ static inline bool pl011_dma_tx_start(struct uart_amba_port *uap)
return false;
}
- writew(uap->port.x_char, uap->port.membase + UART01x_DR);
+ pl011_write(uap->port.x_char, uap, REG_DR);
uap->port.icount.tx++;
uap->port.x_char = 0;
/* Success - restore the DMA state */
uap->dmacr = dmacr;
- writew(dmacr, uap->port.membase + UART011_DMACR);
+ pl011_write(dmacr, uap, REG_DMACR);
return true;
}
@@ -703,7 +794,7 @@ __acquires(&uap->port.lock)
DMA_TO_DEVICE);
uap->dmatx.queued = false;
uap->dmacr &= ~UART011_TXDMAE;
- writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+ pl011_write(uap->dmacr, uap, REG_DMACR);
}
}
@@ -743,11 +834,11 @@ static int pl011_dma_rx_trigger_dma(struct uart_amba_port *uap)
dma_async_issue_pending(rxchan);
uap->dmacr |= UART011_RXDMAE;
- writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+ pl011_write(uap->dmacr, uap, REG_DMACR);
uap->dmarx.running = true;
uap->im &= ~UART011_RXIM;
- writew(uap->im, uap->port.membase + UART011_IMSC);
+ pl011_write(uap->im, uap, REG_IMSC);
return 0;
}
@@ -805,8 +896,8 @@ static void pl011_dma_rx_chars(struct uart_amba_port *uap,
*/
if (dma_count == pending && readfifo) {
/* Clear any error flags */
- writew(UART011_OEIS | UART011_BEIS | UART011_PEIS | UART011_FEIS,
- uap->port.membase + UART011_ICR);
+ pl011_write(UART011_OEIS | UART011_BEIS | UART011_PEIS |
+ UART011_FEIS, uap, REG_ICR);
/*
* If we read all the DMA'd characters, and we had an
@@ -854,7 +945,7 @@ static void pl011_dma_rx_irq(struct uart_amba_port *uap)
/* Disable RX DMA - incoming data will wait in the FIFO */
uap->dmacr &= ~UART011_RXDMAE;
- writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+ pl011_write(uap->dmacr, uap, REG_DMACR);
uap->dmarx.running = false;
pending = sgbuf->sg.length - state.residue;
@@ -874,7 +965,7 @@ static void pl011_dma_rx_irq(struct uart_amba_port *uap)
dev_dbg(uap->port.dev, "could not retrigger RX DMA job "
"fall back to interrupt mode\n");
uap->im |= UART011_RXIM;
- writew(uap->im, uap->port.membase + UART011_IMSC);
+ pl011_write(uap->im, uap, REG_IMSC);
}
}
@@ -922,7 +1013,7 @@ static void pl011_dma_rx_callback(void *data)
dev_dbg(uap->port.dev, "could not retrigger RX DMA job "
"fall back to interrupt mode\n");
uap->im |= UART011_RXIM;
- writew(uap->im, uap->port.membase + UART011_IMSC);
+ pl011_write(uap->im, uap, REG_IMSC);
}
}
@@ -935,7 +1026,7 @@ static inline void pl011_dma_rx_stop(struct uart_amba_port *uap)
{
/* FIXME. Just disable the DMA enable */
uap->dmacr &= ~UART011_RXDMAE;
- writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+ pl011_write(uap->dmacr, uap, REG_DMACR);
}
/*
@@ -979,7 +1070,7 @@ static void pl011_dma_rx_poll(unsigned long args)
spin_lock_irqsave(&uap->port.lock, flags);
pl011_dma_rx_stop(uap);
uap->im |= UART011_RXIM;
- writew(uap->im, uap->port.membase + UART011_IMSC);
+ pl011_write(uap->im, uap, REG_IMSC);
spin_unlock_irqrestore(&uap->port.lock, flags);
uap->dmarx.running = false;
@@ -1041,7 +1132,7 @@ static void pl011_dma_startup(struct uart_amba_port *uap)
skip_rx:
/* Turn on DMA error (RX/TX will be enabled on demand) */
uap->dmacr |= UART011_DMAONERR;
- writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+ pl011_write(uap->dmacr, uap, REG_DMACR);
/*
* ST Micro variants has some specific dma burst threshold
@@ -1049,8 +1140,8 @@ skip_rx:
* be issued above/below 16 bytes.
*/
if (uap->vendor->dma_threshold)
- writew(ST_UART011_DMAWM_RX_16 | ST_UART011_DMAWM_TX_16,
- uap->port.membase + ST_UART011_DMAWM);
+ pl011_write(ST_UART011_DMAWM_RX_16 | ST_UART011_DMAWM_TX_16,
+ uap, REG_ST_DMAWM);
if (uap->using_rx_dma) {
if (pl011_dma_rx_trigger_dma(uap))
@@ -1075,12 +1166,12 @@ static void pl011_dma_shutdown(struct uart_amba_port *uap)
return;
/* Disable RX and TX DMA */
- while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY)
+ while (pl011_read(uap, REG_FR) & UART01x_FR_BUSY)
barrier();
spin_lock_irq(&uap->port.lock);
uap->dmacr &= ~(UART011_DMAONERR | UART011_RXDMAE | UART011_TXDMAE);
- writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+ pl011_write(uap->dmacr, uap, REG_DMACR);
spin_unlock_irq(&uap->port.lock);
if (uap->using_tx_dma) {
@@ -1181,7 +1272,7 @@ static void pl011_stop_tx(struct uart_port *port)
container_of(port, struct uart_amba_port, port);
uap->im &= ~UART011_TXIM;
- writew(uap->im, uap->port.membase + UART011_IMSC);
+ pl011_write(uap->im, uap, REG_IMSC);
pl011_dma_tx_stop(uap);
}
@@ -1191,7 +1282,7 @@ static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq);
static void pl011_start_tx_pio(struct uart_amba_port *uap)
{
uap->im |= UART011_TXIM;
- writew(uap->im, uap->port.membase + UART011_IMSC);
+ pl011_write(uap->im, uap, REG_IMSC);
pl011_tx_chars(uap, false);
}
@@ -1211,7 +1302,7 @@ static void pl011_stop_rx(struct uart_port *port)
uap->im &= ~(UART011_RXIM|UART011_RTIM|UART011_FEIM|
UART011_PEIM|UART011_BEIM|UART011_OEIM);
- writew(uap->im, uap->port.membase + UART011_IMSC);
+ pl011_write(uap->im, uap, REG_IMSC);
pl011_dma_rx_stop(uap);
}
@@ -1222,7 +1313,7 @@ static void pl011_enable_ms(struct uart_port *port)
container_of(port, struct uart_amba_port, port);
uap->im |= UART011_RIMIM|UART011_CTSMIM|UART011_DCDMIM|UART011_DSRMIM;
- writew(uap->im, uap->port.membase + UART011_IMSC);
+ pl011_write(uap->im, uap, REG_IMSC);
}
static void pl011_rx_chars(struct uart_amba_port *uap)
@@ -1242,7 +1333,7 @@ __acquires(&uap->port.lock)
dev_dbg(uap->port.dev, "could not trigger RX DMA job "
"fall back to interrupt mode again\n");
uap->im |= UART011_RXIM;
- writew(uap->im, uap->port.membase + UART011_IMSC);
+ pl011_write(uap->im, uap, REG_IMSC);
} else {
#ifdef CONFIG_DMA_ENGINE
/* Start Rx DMA poll */
@@ -1263,10 +1354,10 @@ static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c,
bool from_irq)
{
if (unlikely(!from_irq) &&
- readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF)
+ pl011_read(uap, REG_FR) & UART01x_FR_TXFF)
return false; /* unable to transmit character */
- writew(c, uap->port.membase + UART01x_DR);
+ pl011_write(c, uap, REG_DR);
uap->port.icount.tx++;
return true;
@@ -1313,7 +1404,7 @@ static void pl011_modem_status(struct uart_amba_port *uap)
{
unsigned int status, delta;
- status = readw(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY;
+ status = pl011_read(uap, REG_FR) & UART01x_FR_MODEM_ANY;
delta = status ^ uap->old_status;
uap->old_status = status;
@@ -1341,15 +1432,15 @@ static void check_apply_cts_event_workaround(struct uart_amba_port *uap)
return;
/* workaround to make sure that all bits are unlocked.. */
- writew(0x00, uap->port.membase + UART011_ICR);
+ pl011_write(0x00, uap, REG_ICR);
/*
* WA: introduce 26ns(1 uart clk) delay before W1C;
* single apb access will incur 2 pclk(133.12Mhz) delay,
* so add 2 dummy reads
*/
- dummy_read = readw(uap->port.membase + UART011_ICR);
- dummy_read = readw(uap->port.membase + UART011_ICR);
+ dummy_read = pl011_read(uap, REG_ICR);
+ dummy_read = pl011_read(uap, REG_ICR);
}
static irqreturn_t pl011_int(int irq, void *dev_id)
@@ -1361,15 +1452,15 @@ static irqreturn_t pl011_int(int irq, void *dev_id)
int handled = 0;
spin_lock_irqsave(&uap->port.lock, flags);
- imsc = readw(uap->port.membase + UART011_IMSC);
- status = readw(uap->port.membase + UART011_RIS) & imsc;
+ imsc = pl011_read(uap, REG_IMSC);
+ status = pl011_read(uap, REG_RIS) & imsc;
if (status) {
do {
check_apply_cts_event_workaround(uap);
- writew(status & ~(UART011_TXIS|UART011_RTIS|
- UART011_RXIS),
- uap->port.membase + UART011_ICR);
+ pl011_write(status & ~(UART011_TXIS|UART011_RTIS|
+ UART011_RXIS),
+ uap, REG_ICR);
if (status & (UART011_RTIS|UART011_RXIS)) {
if (pl011_dma_rx_running(uap))
@@ -1386,7 +1477,7 @@ static irqreturn_t pl011_int(int irq, void *dev_id)
if (pass_counter-- == 0)
break;
- status = readw(uap->port.membase + UART011_RIS) & imsc;
+ status = pl011_read(uap, REG_RIS) & imsc;
} while (status != 0);
handled = 1;
}
@@ -1400,7 +1491,7 @@ static unsigned int pl011_tx_empty(struct uart_port *port)
{
struct uart_amba_port *uap =
container_of(port, struct uart_amba_port, port);
- unsigned int status = readw(uap->port.membase + UART01x_FR);
+ unsigned int status = pl011_read(uap, REG_FR);
return status & (UART01x_FR_BUSY|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT;
}
@@ -1409,7 +1500,7 @@ static unsigned int pl011_get_mctrl(struct uart_port *port)
struct uart_amba_port *uap =
container_of(port, struct uart_amba_port, port);
unsigned int result = 0;
- unsigned int status = readw(uap->port.membase + UART01x_FR);
+ unsigned int status = pl011_read(uap, REG_FR);
#define TIOCMBIT(uartbit, tiocmbit) \
if (status & uartbit) \
@@ -1429,7 +1520,7 @@ static void pl011_set_mctrl(struct uart_port *port, unsigned int mctrl)
container_of(port, struct uart_amba_port, port);
unsigned int cr;
- cr = readw(uap->port.membase + UART011_CR);
+ cr = pl011_read(uap, REG_CR);
#define TIOCMBIT(tiocmbit, uartbit) \
if (mctrl & tiocmbit) \
@@ -1449,7 +1540,7 @@ static void pl011_set_mctrl(struct uart_port *port, unsigned int mctrl)
}
#undef TIOCMBIT
- writew(cr, uap->port.membase + UART011_CR);
+ pl011_write(cr, uap, REG_CR);
}
static void pl011_break_ctl(struct uart_port *port, int break_state)
@@ -1460,12 +1551,12 @@ static void pl011_break_ctl(struct uart_port *port, int break_state)
unsigned int lcr_h;
spin_lock_irqsave(&uap->port.lock, flags);
- lcr_h = readw(uap->port.membase + uap->lcrh_tx);
+ lcr_h = pl011_read(uap, REG_LCRH_TX);
if (break_state == -1)
lcr_h |= UART01x_LCRH_BRK;
else
lcr_h &= ~UART01x_LCRH_BRK;
- writew(lcr_h, uap->port.membase + uap->lcrh_tx);
+ pl011_write(lcr_h, uap, REG_LCRH_TX);
spin_unlock_irqrestore(&uap->port.lock, flags);
}
@@ -1475,9 +1566,8 @@ static void pl011_quiesce_irqs(struct uart_port *port)
{
struct uart_amba_port *uap =
container_of(port, struct uart_amba_port, port);
- unsigned char __iomem *regs = uap->port.membase;
- writew(readw(regs + UART011_MIS), regs + UART011_ICR);
+ pl011_write(pl011_read(uap, REG_MIS), uap, REG_ICR);
/*
* There is no way to clear TXIM as this is "ready to transmit IRQ", so
* we simply mask it. start_tx() will unmask it.
@@ -1491,7 +1581,8 @@ static void pl011_quiesce_irqs(struct uart_port *port)
* (including tx queue), so we're also fine with start_tx()'s caller
* side.
*/
- writew(readw(regs + UART011_IMSC) & ~UART011_TXIM, regs + UART011_IMSC);
+ pl011_write(pl011_read(uap, REG_IMSC) & ~UART011_TXIM, uap,
+ REG_IMSC);
}
static int pl011_get_poll_char(struct uart_port *port)
@@ -1506,11 +1597,11 @@ static int pl011_get_poll_char(struct uart_port *port)
*/
pl011_quiesce_irqs(port);
- status = readw(uap->port.membase + UART01x_FR);
+ status = pl011_read(uap, REG_FR);
if (status & UART01x_FR_RXFE)
return NO_POLL_CHAR;
- return readw(uap->port.membase + UART01x_DR);
+ return pl011_read(uap, REG_DR);
}
static void pl011_put_poll_char(struct uart_port *port,
@@ -1519,10 +1610,10 @@ static void pl011_put_poll_char(struct uart_port *port,
struct uart_amba_port *uap =
container_of(port, struct uart_amba_port, port);
- while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF)
+ while (pl011_read(uap, REG_FR) & UART01x_FR_TXFF)
barrier();
- writew(ch, uap->port.membase + UART01x_DR);
+ pl011_write(ch, uap, REG_DR);
}
#endif /* CONFIG_CONSOLE_POLL */
@@ -1546,15 +1637,16 @@ static int pl011_hwinit(struct uart_port *port)
uap->port.uartclk = clk_get_rate(uap->clk);
/* Clear pending error and receive interrupts */
- writew(UART011_OEIS | UART011_BEIS | UART011_PEIS | UART011_FEIS |
- UART011_RTIS | UART011_RXIS, uap->port.membase + UART011_ICR);
+ pl011_write(UART011_OEIS | UART011_BEIS | UART011_PEIS |
+ UART011_FEIS | UART011_RTIS | UART011_RXIS,
+ uap, REG_ICR);
/*
* Save interrupts enable mask, and enable RX interrupts in case if
* the interrupt is used for NMI entry.
*/
- uap->im = readw(uap->port.membase + UART011_IMSC);
- writew(UART011_RTIM | UART011_RXIM, uap->port.membase + UART011_IMSC);
+ uap->im = pl011_read(uap, REG_IMSC);
+ pl011_write(UART011_RTIM | UART011_RXIM, uap, REG_IMSC);
if (dev_get_platdata(uap->port.dev)) {
struct amba_pl011_data *plat;
@@ -1566,24 +1658,30 @@ static int pl011_hwinit(struct uart_port *port)
return 0;
}
+static bool pl011_split_lcrh(const struct uart_amba_port *uap)
+{
+ return pl011_reg_to_offset(uap, REG_LCRH_RX) !=
+ pl011_reg_to_offset(uap, REG_LCRH_TX);
+}
+
static void pl011_write_lcr_h(struct uart_amba_port *uap, unsigned int lcr_h)
{
- writew(lcr_h, uap->port.membase + uap->lcrh_rx);
- if (uap->lcrh_rx != uap->lcrh_tx) {
+ pl011_write(lcr_h, uap, REG_LCRH_RX);
+ if (pl011_split_lcrh(uap)) {
int i;
/*
* Wait 10 PCLKs before writing LCRH_TX register,
* to get this delay write read only register 10 times
*/
for (i = 0; i < 10; ++i)
- writew(0xff, uap->port.membase + UART011_MIS);
- writew(lcr_h, uap->port.membase + uap->lcrh_tx);
+ pl011_write(0xff, uap, REG_MIS);
+ pl011_write(lcr_h, uap, REG_LCRH_TX);
}
}
static int pl011_allocate_irq(struct uart_amba_port *uap)
{
- writew(uap->im, uap->port.membase + UART011_IMSC);
+ pl011_write(uap->im, uap, REG_IMSC);
return request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap);
}
@@ -1598,12 +1696,11 @@ static void pl011_enable_interrupts(struct uart_amba_port *uap)
spin_lock_irq(&uap->port.lock);
/* Clear out any spuriously appearing RX interrupts */
- writew(UART011_RTIS | UART011_RXIS,
- uap->port.membase + UART011_ICR);
+ pl011_write(UART011_RTIS | UART011_RXIS, uap, REG_ICR);
uap->im = UART011_RTIM;
if (!pl011_dma_rx_running(uap))
uap->im |= UART011_RXIM;
- writew(uap->im, uap->port.membase + UART011_IMSC);
+ pl011_write(uap->im, uap, REG_IMSC);
spin_unlock_irq(&uap->port.lock);
}
@@ -1622,21 +1719,21 @@ static int pl011_startup(struct uart_port *port)
if (retval)
goto clk_dis;
- writew(uap->vendor->ifls, uap->port.membase + UART011_IFLS);
+ pl011_write(uap->vendor->ifls, uap, REG_IFLS);
spin_lock_irq(&uap->port.lock);
/* restore RTS and DTR */
cr = uap->old_cr & (UART011_CR_RTS | UART011_CR_DTR);
cr |= UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE;
- writew(cr, uap->port.membase + UART011_CR);
+ pl011_write(cr, uap, REG_CR);
spin_unlock_irq(&uap->port.lock);
/*
* initialise the old status of the modem signals
*/
- uap->old_status = readw(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY;
+ uap->old_status = pl011_read(uap, REG_FR) & UART01x_FR_MODEM_ANY;
/* Startup DMA */
pl011_dma_startup(uap);
@@ -1677,9 +1774,9 @@ static void pl011_shutdown_channel(struct uart_amba_port *uap,
{
unsigned long val;
- val = readw(uap->port.membase + lcrh);
+ val = pl011_read(uap, lcrh);
val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN);
- writew(val, uap->port.membase + lcrh);
+ pl011_write(val, uap, lcrh);
}
/*
@@ -1693,19 +1790,19 @@ static void pl011_disable_uart(struct uart_amba_port *uap)
uap->autorts = false;
spin_lock_irq(&uap->port.lock);
- cr = readw(uap->port.membase + UART011_CR);
+ cr = pl011_read(uap, REG_CR);
uap->old_cr = cr;
cr &= UART011_CR_RTS | UART011_CR_DTR;
cr |= UART01x_CR_UARTEN | UART011_CR_TXE;
- writew(cr, uap->port.membase + UART011_CR);
+ pl011_write(cr, uap, REG_CR);
spin_unlock_irq(&uap->port.lock);
/*
* disable break condition and fifos
*/
- pl011_shutdown_channel(uap, uap->lcrh_rx);
- if (uap->lcrh_rx != uap->lcrh_tx)
- pl011_shutdown_channel(uap, uap->lcrh_tx);
+ pl011_shutdown_channel(uap, REG_LCRH_RX);
+ if (pl011_split_lcrh(uap))
+ pl011_shutdown_channel(uap, REG_LCRH_TX);
}
static void pl011_disable_interrupts(struct uart_amba_port *uap)
@@ -1714,8 +1811,8 @@ static void pl011_disable_interrupts(struct uart_amba_port *uap)
/* mask all interrupts and clear all pending ones */
uap->im = 0;
- writew(uap->im, uap->port.membase + UART011_IMSC);
- writew(0xffff, uap->port.membase + UART011_ICR);
+ pl011_write(uap->im, uap, REG_IMSC);
+ pl011_write(0xffff, uap, REG_ICR);
spin_unlock_irq(&uap->port.lock);
}
@@ -1867,8 +1964,8 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
pl011_enable_ms(port);
/* first, disable everything */
- old_cr = readw(port->membase + UART011_CR);
- writew(0, port->membase + UART011_CR);
+ old_cr = pl011_read(uap, REG_CR);
+ pl011_write(0, uap, REG_CR);
if (termios->c_cflag & CRTSCTS) {
if (old_cr & UART011_CR_RTS)
@@ -1901,17 +1998,17 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
quot -= 2;
}
/* Set baud rate */
- writew(quot & 0x3f, port->membase + UART011_FBRD);
- writew(quot >> 6, port->membase + UART011_IBRD);
+ pl011_write(quot & 0x3f, uap, REG_FBRD);
+ pl011_write(quot >> 6, uap, REG_IBRD);
/*
* ----------v----------v----------v----------v-----
- * NOTE: lcrh_tx and lcrh_rx MUST BE WRITTEN AFTER
- * UART011_FBRD & UART011_IBRD.
+ * NOTE: REG_LCRH_TX and REG_LCRH_RX MUST BE WRITTEN AFTER
+ * REG_FBRD & REG_IBRD.
* ----------^----------^----------^----------^-----
*/
pl011_write_lcr_h(uap, lcr_h);
- writew(old_cr, port->membase + UART011_CR);
+ pl011_write(old_cr, uap, REG_CR);
spin_unlock_irqrestore(&port->lock, flags);
}
@@ -2052,9 +2149,9 @@ static void pl011_console_putchar(struct uart_port *port, int ch)
struct uart_amba_port *uap =
container_of(port, struct uart_amba_port, port);
- while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF)
+ while (pl011_read(uap, REG_FR) & UART01x_FR_TXFF)
barrier();
- writew(ch, uap->port.membase + UART01x_DR);
+ pl011_write(ch, uap, REG_DR);
}
static void
@@ -2079,10 +2176,10 @@ pl011_console_write(struct console *co, const char *s, unsigned int count)
* First save the CR then disable the interrupts
*/
if (!uap->vendor->always_enabled) {
- old_cr = readw(uap->port.membase + UART011_CR);
+ old_cr = pl011_read(uap, REG_CR);
new_cr = old_cr & ~UART011_CR_CTSEN;
new_cr |= UART01x_CR_UARTEN | UART011_CR_TXE;
- writew(new_cr, uap->port.membase + UART011_CR);
+ pl011_write(new_cr, uap, REG_CR);
}
uart_console_write(&uap->port, s, count, pl011_console_putchar);
@@ -2092,10 +2189,10 @@ pl011_console_write(struct console *co, const char *s, unsigned int count)
* and restore the TCR
*/
do {
- status = readw(uap->port.membase + UART01x_FR);
+ status = pl011_read(uap, REG_FR);
} while (status & UART01x_FR_BUSY);
if (!uap->vendor->always_enabled)
- writew(old_cr, uap->port.membase + UART011_CR);
+ pl011_write(old_cr, uap, REG_CR);
if (locked)
spin_unlock(&uap->port.lock);
@@ -2108,10 +2205,10 @@ static void __init
pl011_console_get_options(struct uart_amba_port *uap, int *baud,
int *parity, int *bits)
{
- if (readw(uap->port.membase + UART011_CR) & UART01x_CR_UARTEN) {
+ if (pl011_read(uap, REG_CR) & UART01x_CR_UARTEN) {
unsigned int lcr_h, ibrd, fbrd;
- lcr_h = readw(uap->port.membase + uap->lcrh_tx);
+ lcr_h = pl011_read(uap, REG_LCRH_TX);
*parity = 'n';
if (lcr_h & UART01x_LCRH_PEN) {
@@ -2126,13 +2223,13 @@ pl011_console_get_options(struct uart_amba_port *uap, int *baud,
else
*bits = 8;
- ibrd = readw(uap->port.membase + UART011_IBRD);
- fbrd = readw(uap->port.membase + UART011_FBRD);
+ ibrd = pl011_read(uap, REG_IBRD);
+ fbrd = pl011_read(uap, REG_FBRD);
*baud = uap->port.uartclk * 4 / (64 * ibrd + fbrd);
if (uap->vendor->oversampling) {
- if (readw(uap->port.membase + UART011_CR)
+ if (pl011_read(uap, REG_CR)
& ST_UART011_CR_OVSFACT)
*baud *= 2;
}
@@ -2206,7 +2303,10 @@ static void pl011_putc(struct uart_port *port, int c)
{
while (readl(port->membase + UART01x_FR) & UART01x_FR_TXFF)
;
- writeb(c, port->membase + UART01x_DR);
+ if (port->iotype == UPIO_MEM32)
+ writel(c, port->membase + UART01x_DR);
+ else
+ writeb(c, port->membase + UART01x_DR);
while (readl(port->membase + UART01x_FR) & UART01x_FR_BUSY)
;
}
@@ -2319,7 +2419,6 @@ static int pl011_setup_port(struct device *dev, struct uart_amba_port *uap,
uap->port.dev = dev;
uap->port.mapbase = mmiobase->start;
uap->port.membase = base;
- uap->port.iotype = UPIO_MEM;
uap->port.fifosize = uap->fifosize;
uap->port.flags = UPF_BOOT_AUTOCONF;
uap->port.line = index;
@@ -2334,8 +2433,8 @@ static int pl011_register_port(struct uart_amba_port *uap)
int ret;
/* Ensure interrupts from this UART are masked and cleared */
- writew(0, uap->port.membase + UART011_IMSC);
- writew(0xffff, uap->port.membase + UART011_ICR);
+ pl011_write(0, uap, REG_IMSC);
+ pl011_write(0xffff, uap, REG_ICR);
if (!amba_reg.state) {
ret = uart_register_driver(&amba_reg);
@@ -2372,10 +2471,10 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
if (IS_ERR(uap->clk))
return PTR_ERR(uap->clk);
+ uap->reg_offset = vendor->reg_offset;
uap->vendor = vendor;
- uap->lcrh_rx = vendor->lcrh_rx;
- uap->lcrh_tx = vendor->lcrh_tx;
uap->fifosize = vendor->get_fifosize(dev);
+ uap->port.iotype = vendor->access_32b ? UPIO_MEM32 : UPIO_MEM;
uap->port.irq = dev->irq[0];
uap->port.ops = &amba_pl011_pops;
@@ -2453,8 +2552,10 @@ static int sbsa_uart_probe(struct platform_device *pdev)
if (!uap)
return -ENOMEM;
+ uap->reg_offset = vendor_sbsa.reg_offset;
uap->vendor = &vendor_sbsa;
uap->fifosize = 32;
+ uap->port.iotype = vendor_sbsa.access_32b ? UPIO_MEM32 : UPIO_MEM;
uap->port.irq = platform_get_irq(pdev, 0);
uap->port.ops = &sbsa_uart_pops;
uap->fixed_baud = baudrate;
diff --git a/drivers/tty/serial/amba-pl011.h b/drivers/tty/serial/amba-pl011.h
new file mode 100644
index 000000000000..411c60e1f9a4
--- /dev/null
+++ b/drivers/tty/serial/amba-pl011.h
@@ -0,0 +1,34 @@
+#ifndef AMBA_PL011_H
+#define AMBA_PL011_H
+
+enum {
+ REG_DR,
+ REG_ST_DMAWM,
+ REG_ST_TIMEOUT,
+ REG_FR,
+ REG_LCRH_RX,
+ REG_LCRH_TX,
+ REG_IBRD,
+ REG_FBRD,
+ REG_CR,
+ REG_IFLS,
+ REG_IMSC,
+ REG_RIS,
+ REG_MIS,
+ REG_ICR,
+ REG_DMACR,
+ REG_ST_XFCR,
+ REG_ST_XON1,
+ REG_ST_XON2,
+ REG_ST_XOFF1,
+ REG_ST_XOFF2,
+ REG_ST_ITCR,
+ REG_ST_ITIP,
+ REG_ST_ABCR,
+ REG_ST_ABIMSC,
+
+ /* The size of the array - must be last */
+ REG_ARRAY_SIZE,
+};
+
+#endif
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 94294558943c..1c0884d8ef32 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -22,7 +22,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
-#include <linux/module.h>
#include <linux/tty.h>
#include <linux/ioport.h>
#include <linux/slab.h>
@@ -155,7 +154,6 @@ struct atmel_uart_port {
struct circ_buf rx_ring;
struct mctrl_gpios *gpios;
- int gpio_irq[UART_GPIO_MAX];
unsigned int tx_done_mask;
u32 fifo_size;
u32 rts_high;
@@ -190,8 +188,6 @@ static const struct of_device_id atmel_serial_dt_ids[] = {
{ .compatible = "atmel,at91sam9260-usart" },
{ /* sentinel */ }
};
-
-MODULE_DEVICE_TABLE(of, atmel_serial_dt_ids);
#endif
static inline struct atmel_uart_port *
@@ -550,27 +546,21 @@ static void atmel_enable_ms(struct uart_port *port)
atmel_port->ms_irq_enabled = true;
- if (atmel_port->gpio_irq[UART_GPIO_CTS] >= 0)
- enable_irq(atmel_port->gpio_irq[UART_GPIO_CTS]);
- else
+ if (!mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_CTS))
ier |= ATMEL_US_CTSIC;
- if (atmel_port->gpio_irq[UART_GPIO_DSR] >= 0)
- enable_irq(atmel_port->gpio_irq[UART_GPIO_DSR]);
- else
+ if (!mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_DSR))
ier |= ATMEL_US_DSRIC;
- if (atmel_port->gpio_irq[UART_GPIO_RI] >= 0)
- enable_irq(atmel_port->gpio_irq[UART_GPIO_RI]);
- else
+ if (!mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_RI))
ier |= ATMEL_US_RIIC;
- if (atmel_port->gpio_irq[UART_GPIO_DCD] >= 0)
- enable_irq(atmel_port->gpio_irq[UART_GPIO_DCD]);
- else
+ if (!mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_DCD))
ier |= ATMEL_US_DCDIC;
atmel_uart_writel(port, ATMEL_US_IER, ier);
+
+ mctrl_gpio_enable_ms(atmel_port->gpios);
}
/*
@@ -589,24 +579,18 @@ static void atmel_disable_ms(struct uart_port *port)
atmel_port->ms_irq_enabled = false;
- if (atmel_port->gpio_irq[UART_GPIO_CTS] >= 0)
- disable_irq(atmel_port->gpio_irq[UART_GPIO_CTS]);
- else
+ mctrl_gpio_disable_ms(atmel_port->gpios);
+
+ if (!mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_CTS))
idr |= ATMEL_US_CTSIC;
- if (atmel_port->gpio_irq[UART_GPIO_DSR] >= 0)
- disable_irq(atmel_port->gpio_irq[UART_GPIO_DSR]);
- else
+ if (!mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_DSR))
idr |= ATMEL_US_DSRIC;
- if (atmel_port->gpio_irq[UART_GPIO_RI] >= 0)
- disable_irq(atmel_port->gpio_irq[UART_GPIO_RI]);
- else
+ if (!mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_RI))
idr |= ATMEL_US_RIIC;
- if (atmel_port->gpio_irq[UART_GPIO_DCD] >= 0)
- disable_irq(atmel_port->gpio_irq[UART_GPIO_DCD]);
- else
+ if (!mctrl_gpio_to_gpiod(atmel_port->gpios, UART_GPIO_DCD))
idr |= ATMEL_US_DCDIC;
atmel_uart_writel(port, ATMEL_US_IDR, idr);
@@ -1264,7 +1248,6 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id)
struct uart_port *port = dev_id;
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
unsigned int status, pending, mask, pass_counter = 0;
- bool gpio_handled = false;
spin_lock(&atmel_port->lock_suspended);
@@ -1272,24 +1255,6 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id)
status = atmel_get_lines_status(port);
mask = atmel_uart_readl(port, ATMEL_US_IMR);
pending = status & mask;
- if (!gpio_handled) {
- /*
- * Dealing with GPIO interrupt
- */
- if (irq == atmel_port->gpio_irq[UART_GPIO_CTS])
- pending |= ATMEL_US_CTSIC;
-
- if (irq == atmel_port->gpio_irq[UART_GPIO_DSR])
- pending |= ATMEL_US_DSRIC;
-
- if (irq == atmel_port->gpio_irq[UART_GPIO_RI])
- pending |= ATMEL_US_RIIC;
-
- if (irq == atmel_port->gpio_irq[UART_GPIO_DCD])
- pending |= ATMEL_US_DCDIC;
-
- gpio_handled = true;
- }
if (!pending)
break;
@@ -1778,45 +1743,6 @@ static void atmel_get_ip_name(struct uart_port *port)
}
}
-static void atmel_free_gpio_irq(struct uart_port *port)
-{
- struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- enum mctrl_gpio_idx i;
-
- for (i = 0; i < UART_GPIO_MAX; i++)
- if (atmel_port->gpio_irq[i] >= 0)
- free_irq(atmel_port->gpio_irq[i], port);
-}
-
-static int atmel_request_gpio_irq(struct uart_port *port)
-{
- struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- int *irq = atmel_port->gpio_irq;
- enum mctrl_gpio_idx i;
- int err = 0;
-
- for (i = 0; (i < UART_GPIO_MAX) && !err; i++) {
- if (irq[i] < 0)
- continue;
-
- irq_set_status_flags(irq[i], IRQ_NOAUTOEN);
- err = request_irq(irq[i], atmel_interrupt, IRQ_TYPE_EDGE_BOTH,
- "atmel_serial", port);
- if (err)
- dev_err(port->dev, "atmel_startup - Can't get %d irq\n",
- irq[i]);
- }
-
- /*
- * If something went wrong, rollback.
- */
- while (err && (--i >= 0))
- if (irq[i] >= 0)
- free_irq(irq[i], port);
-
- return err;
-}
-
/*
* Perform initialization and enable port for reception
*/
@@ -1846,13 +1772,6 @@ static int atmel_startup(struct uart_port *port)
return retval;
}
- /*
- * Get the GPIO lines IRQ
- */
- retval = atmel_request_gpio_irq(port);
- if (retval)
- goto free_irq;
-
tasklet_enable(&atmel_port->tasklet);
/*
@@ -1948,11 +1867,6 @@ static int atmel_startup(struct uart_port *port)
}
return 0;
-
-free_irq:
- free_irq(port->irq, port);
-
- return retval;
}
/*
@@ -2018,7 +1932,6 @@ static void atmel_shutdown(struct uart_port *port)
* Free the interrupts
*/
free_irq(port->irq, port);
- atmel_free_gpio_irq(port);
atmel_port->ms_irq_enabled = false;
@@ -2686,26 +2599,6 @@ static int atmel_serial_resume(struct platform_device *pdev)
#define atmel_serial_resume NULL
#endif
-static int atmel_init_gpios(struct atmel_uart_port *p, struct device *dev)
-{
- enum mctrl_gpio_idx i;
- struct gpio_desc *gpiod;
-
- p->gpios = mctrl_gpio_init_noauto(dev, 0);
- if (IS_ERR(p->gpios))
- return PTR_ERR(p->gpios);
-
- for (i = 0; i < UART_GPIO_MAX; i++) {
- gpiod = mctrl_gpio_to_gpiod(p->gpios, i);
- if (gpiod && (gpiod_get_direction(gpiod) == GPIOF_DIR_IN))
- p->gpio_irq[i] = gpiod_to_irq(gpiod);
- else
- p->gpio_irq[i] = -EINVAL;
- }
-
- return 0;
-}
-
static void atmel_serial_probe_fifos(struct atmel_uart_port *port,
struct platform_device *pdev)
{
@@ -2788,16 +2681,16 @@ static int atmel_serial_probe(struct platform_device *pdev)
spin_lock_init(&port->lock_suspended);
- ret = atmel_init_gpios(port, &pdev->dev);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to initialize GPIOs.");
- goto err_clear_bit;
- }
-
ret = atmel_init_port(port, pdev);
if (ret)
goto err_clear_bit;
+ port->gpios = mctrl_gpio_init(&port->uart, 0);
+ if (IS_ERR(port->gpios)) {
+ ret = PTR_ERR(port->gpios);
+ goto err_clear_bit;
+ }
+
if (!atmel_use_pdc_rx(&port->uart)) {
ret = -ENOMEM;
data = kmalloc(sizeof(struct atmel_uart_char)
@@ -2866,37 +2759,14 @@ err:
return ret;
}
-static int atmel_serial_remove(struct platform_device *pdev)
-{
- struct uart_port *port = platform_get_drvdata(pdev);
- struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- int ret = 0;
-
- tasklet_kill(&atmel_port->tasklet);
-
- device_init_wakeup(&pdev->dev, 0);
-
- ret = uart_remove_one_port(&atmel_uart, port);
-
- kfree(atmel_port->rx_ring.buf);
-
- /* "port" is allocated statically, so we shouldn't free it */
-
- clear_bit(port->line, atmel_ports_in_use);
-
- clk_put(atmel_port->clk);
-
- return ret;
-}
-
static struct platform_driver atmel_serial_driver = {
.probe = atmel_serial_probe,
- .remove = atmel_serial_remove,
.suspend = atmel_serial_suspend,
.resume = atmel_serial_resume,
.driver = {
- .name = "atmel_usart",
- .of_match_table = of_match_ptr(atmel_serial_dt_ids),
+ .name = "atmel_usart",
+ .of_match_table = of_match_ptr(atmel_serial_dt_ids),
+ .suppress_bind_attrs = true,
},
};
@@ -2914,17 +2784,4 @@ static int __init atmel_serial_init(void)
return ret;
}
-
-static void __exit atmel_serial_exit(void)
-{
- platform_driver_unregister(&atmel_serial_driver);
- uart_unregister_driver(&atmel_uart);
-}
-
-module_init(atmel_serial_init);
-module_exit(atmel_serial_exit);
-
-MODULE_AUTHOR("Rick Bronson");
-MODULE_DESCRIPTION("Atmel AT91 / AT32 serial port driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:atmel_usart");
+device_initcall(atmel_serial_init);
diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c
index a1c0a89d9c7f..c28e5c24da16 100644
--- a/drivers/tty/serial/bcm63xx_uart.c
+++ b/drivers/tty/serial/bcm63xx_uart.c
@@ -653,7 +653,7 @@ static struct uart_ops bcm_uart_ops = {
#ifdef CONFIG_SERIAL_BCM63XX_CONSOLE
-static inline void wait_for_xmitr(struct uart_port *port)
+static void wait_for_xmitr(struct uart_port *port)
{
unsigned int tmout;
diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c
index ae3cf94b146b..293ecbb00684 100644
--- a/drivers/tty/serial/bfin_uart.c
+++ b/drivers/tty/serial/bfin_uart.c
@@ -213,7 +213,7 @@ static void bfin_serial_stop_rx(struct uart_port *port)
static void bfin_serial_rx_chars(struct bfin_serial_port *uart)
{
unsigned int status, ch, flg;
- static struct timeval anomaly_start = { .tv_sec = 0 };
+ static u64 anomaly_start;
status = UART_GET_LSR(uart);
UART_CLEAR_LSR(uart);
@@ -246,27 +246,24 @@ static void bfin_serial_rx_chars(struct bfin_serial_port *uart)
* character time +/- some percent. So 1.5 sounds good. All other
* Blackfin families operate properly. Woo.
*/
- if (anomaly_start.tv_sec) {
- struct timeval curr;
- suseconds_t usecs;
+ if (anomaly_start > 0) {
+ u64 curr, nsecs, threshold_ns;
if ((~ch & (~ch + 1)) & 0xff)
goto known_good_char;
- do_gettimeofday(&curr);
- if (curr.tv_sec - anomaly_start.tv_sec > 1)
+ curr = ktime_get_ns();
+ nsecs = curr - anomaly_start;
+ if (nsecs >> 32)
goto known_good_char;
- usecs = 0;
- if (curr.tv_sec != anomaly_start.tv_sec)
- usecs += USEC_PER_SEC;
- usecs += curr.tv_usec - anomaly_start.tv_usec;
-
- if (usecs > UART_GET_ANOMALY_THRESHOLD(uart))
+ threshold_ns = UART_GET_ANOMALY_THRESHOLD(uart)
+ * NSEC_PER_USEC;
+ if (nsecs > threshold_ns)
goto known_good_char;
if (ch)
- anomaly_start.tv_sec = 0;
+ anomaly_start = 0;
else
anomaly_start = curr;
@@ -274,14 +271,14 @@ static void bfin_serial_rx_chars(struct bfin_serial_port *uart)
known_good_char:
status &= ~BI;
- anomaly_start.tv_sec = 0;
+ anomaly_start = 0;
}
}
if (status & BI) {
if (ANOMALY_05000363)
if (bfin_revid() < 5)
- do_gettimeofday(&anomaly_start);
+ anomaly_start = ktime_get_ns();
uart->port.icount.brk++;
if (uart_handle_break(&uart->port))
goto ignore_char;
diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
index f09636083426..3f2423690d01 100644
--- a/drivers/tty/serial/earlycon.c
+++ b/drivers/tty/serial/earlycon.c
@@ -71,10 +71,16 @@ static int __init parse_options(struct earlycon_device *device, char *options)
return -EINVAL;
switch (port->iotype) {
+ case UPIO_MEM:
+ port->mapbase = addr;
+ break;
+ case UPIO_MEM16:
+ port->regshift = 1;
+ port->mapbase = addr;
+ break;
case UPIO_MEM32:
case UPIO_MEM32BE:
- port->regshift = 2; /* fall-through */
- case UPIO_MEM:
+ port->regshift = 2;
port->mapbase = addr;
break;
case UPIO_PORT:
@@ -91,10 +97,11 @@ static int __init parse_options(struct earlycon_device *device, char *options)
strlcpy(device->options, options, length);
}
- if (port->iotype == UPIO_MEM || port->iotype == UPIO_MEM32 ||
- port->iotype == UPIO_MEM32BE)
+ if (port->iotype == UPIO_MEM || port->iotype == UPIO_MEM16 ||
+ port->iotype == UPIO_MEM32 || port->iotype == UPIO_MEM32BE)
pr_info("Early serial console at MMIO%s 0x%llx (options '%s')\n",
(port->iotype == UPIO_MEM) ? "" :
+ (port->iotype == UPIO_MEM16) ? "16" :
(port->iotype == UPIO_MEM32) ? "32" : "32be",
(unsigned long long)port->mapbase,
device->options);
@@ -115,6 +122,7 @@ static int __init register_earlycon(char *buf, const struct earlycon_id *match)
if (buf && !parse_options(&early_console_dev, buf))
buf = NULL;
+ spin_lock_init(&port->lock);
port->uartclk = BASE_BAUD * 16;
if (port->mapbase)
port->membase = earlycon_map(port->mapbase, 64);
@@ -202,6 +210,7 @@ int __init of_setup_earlycon(unsigned long addr,
int err;
struct uart_port *port = &early_console_dev.port;
+ spin_lock_init(&port->lock);
port->iotype = UPIO_MEM;
port->mapbase = addr;
port->uartclk = BASE_BAUD * 16;
diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c
index ffc7cb2585a6..c60a8d5e4020 100644
--- a/drivers/tty/serial/icom.c
+++ b/drivers/tty/serial/icom.c
@@ -22,7 +22,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
-#define SERIAL_DO_RESTART
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 016e4be05cec..9362f54c816c 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -44,6 +44,8 @@
#include <linux/platform_data/serial-imx.h>
#include <linux/platform_data/dma-imx.h>
+#include "serial_mctrl_gpio.h"
+
/* Register definitions */
#define URXD0 0x0 /* Receiver Register */
#define URTX0 0x40 /* Transmitter Register */
@@ -148,8 +150,11 @@
#define USR2_TXFE (1<<14) /* Transmit buffer FIFO empty */
#define USR2_DTRF (1<<13) /* DTR edge interrupt flag */
#define USR2_IDLE (1<<12) /* Idle condition */
+#define USR2_RIDELT (1<<10) /* Ring Interrupt Delta */
+#define USR2_RIIN (1<<9) /* Ring Indicator Input */
#define USR2_IRINT (1<<8) /* Serial infrared interrupt flag */
#define USR2_WAKE (1<<7) /* Wake */
+#define USR2_DCDIN (1<<5) /* Data Carrier Detect Input */
#define USR2_RTSF (1<<4) /* RTS edge interrupt flag */
#define USR2_TXDC (1<<3) /* Transmitter complete */
#define USR2_BRCD (1<<2) /* Break condition */
@@ -206,6 +211,8 @@ struct imx_port {
struct clk *clk_per;
const struct imx_uart_data *devdata;
+ struct mctrl_gpios *gpios;
+
/* DMA fields */
unsigned int dma_is_inited:1;
unsigned int dma_is_enabled:1;
@@ -308,49 +315,24 @@ static void imx_port_ucrs_restore(struct uart_port *port,
}
#endif
-/*
- * Handle any change of modem status signal since we were last called.
- */
-static void imx_mctrl_check(struct imx_port *sport)
+static void imx_port_rts_active(struct imx_port *sport, unsigned long *ucr2)
{
- unsigned int status, changed;
-
- status = sport->port.ops->get_mctrl(&sport->port);
- changed = status ^ sport->old_status;
-
- if (changed == 0)
- return;
-
- sport->old_status = status;
+ *ucr2 &= ~UCR2_CTSC;
+ *ucr2 |= UCR2_CTS;
- if (changed & TIOCM_RI)
- sport->port.icount.rng++;
- if (changed & TIOCM_DSR)
- sport->port.icount.dsr++;
- if (changed & TIOCM_CAR)
- uart_handle_dcd_change(&sport->port, status & TIOCM_CAR);
- if (changed & TIOCM_CTS)
- uart_handle_cts_change(&sport->port, status & TIOCM_CTS);
-
- wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
+ mctrl_gpio_set(sport->gpios, sport->port.mctrl | TIOCM_RTS);
}
-/*
- * This is our per-port timeout handler, for checking the
- * modem status signals.
- */
-static void imx_timeout(unsigned long data)
+static void imx_port_rts_inactive(struct imx_port *sport, unsigned long *ucr2)
{
- struct imx_port *sport = (struct imx_port *)data;
- unsigned long flags;
+ *ucr2 &= ~(UCR2_CTSC | UCR2_CTS);
- if (sport->port.state) {
- spin_lock_irqsave(&sport->port.lock, flags);
- imx_mctrl_check(sport);
- spin_unlock_irqrestore(&sport->port.lock, flags);
+ mctrl_gpio_set(sport->gpios, sport->port.mctrl & ~TIOCM_RTS);
+}
- mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT);
- }
+static void imx_port_rts_auto(struct imx_port *sport, unsigned long *ucr2)
+{
+ *ucr2 |= UCR2_CTSC;
}
/*
@@ -376,9 +358,9 @@ static void imx_stop_tx(struct uart_port *port)
readl(port->membase + USR2) & USR2_TXDC) {
temp = readl(port->membase + UCR2);
if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
- temp &= ~UCR2_CTS;
+ imx_port_rts_inactive(sport, &temp);
else
- temp |= UCR2_CTS;
+ imx_port_rts_active(sport, &temp);
writel(temp, port->membase + UCR2);
temp = readl(port->membase + UCR4);
@@ -420,6 +402,8 @@ static void imx_enable_ms(struct uart_port *port)
struct imx_port *sport = (struct imx_port *)port;
mod_timer(&sport->timer, jiffies);
+
+ mctrl_gpio_enable_ms(sport->gpios);
}
static void imx_dma_tx(struct imx_port *sport);
@@ -579,14 +563,14 @@ static void imx_start_tx(struct uart_port *port)
unsigned long temp;
if (port->rs485.flags & SER_RS485_ENABLED) {
- /* enable transmitter and shifter empty irq */
temp = readl(port->membase + UCR2);
if (port->rs485.flags & SER_RS485_RTS_ON_SEND)
- temp &= ~UCR2_CTS;
+ imx_port_rts_inactive(sport, &temp);
else
- temp |= UCR2_CTS;
+ imx_port_rts_active(sport, &temp);
writel(temp, port->membase + UCR2);
+ /* enable transmitter and shifter empty irq */
temp = readl(port->membase + UCR4);
temp |= UCR4_TCEN;
writel(temp, port->membase + UCR4);
@@ -801,23 +785,35 @@ static unsigned int imx_tx_empty(struct uart_port *port)
/*
* We have a modem side uart, so the meanings of RTS and CTS are inverted.
*/
-static unsigned int imx_get_mctrl(struct uart_port *port)
+static unsigned int imx_get_hwmctrl(struct imx_port *sport)
{
- struct imx_port *sport = (struct imx_port *)port;
- unsigned int tmp = TIOCM_DSR | TIOCM_CAR;
+ unsigned int tmp = TIOCM_DSR;
+ unsigned usr1 = readl(sport->port.membase + USR1);
- if (readl(sport->port.membase + USR1) & USR1_RTSS)
+ if (usr1 & USR1_RTSS)
tmp |= TIOCM_CTS;
- if (readl(sport->port.membase + UCR2) & UCR2_CTS)
- tmp |= TIOCM_RTS;
+ /* in DCE mode DCDIN is always 0 */
+ if (!(usr1 & USR2_DCDIN))
+ tmp |= TIOCM_CAR;
- if (readl(sport->port.membase + uts_reg(sport)) & UTS_LOOP)
- tmp |= TIOCM_LOOP;
+ /* in DCE mode RIIN is always 0 */
+ if (readl(sport->port.membase + USR2) & USR2_RIIN)
+ tmp |= TIOCM_RI;
return tmp;
}
+static unsigned int imx_get_mctrl(struct uart_port *port)
+{
+ struct imx_port *sport = (struct imx_port *)port;
+ unsigned int ret = imx_get_hwmctrl(sport);
+
+ mctrl_gpio_get(sport->gpios, &ret);
+
+ return ret;
+}
+
static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct imx_port *sport = (struct imx_port *)port;
@@ -831,10 +827,17 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
writel(temp, sport->port.membase + UCR2);
}
+ temp = readl(sport->port.membase + UCR3) & ~UCR3_DSR;
+ if (!(mctrl & TIOCM_DTR))
+ temp |= UCR3_DSR;
+ writel(temp, sport->port.membase + UCR3);
+
temp = readl(sport->port.membase + uts_reg(sport)) & ~UTS_LOOP;
if (mctrl & TIOCM_LOOP)
temp |= UTS_LOOP;
writel(temp, sport->port.membase + uts_reg(sport));
+
+ mctrl_gpio_set(sport->gpios, mctrl);
}
/*
@@ -857,6 +860,51 @@ static void imx_break_ctl(struct uart_port *port, int break_state)
spin_unlock_irqrestore(&sport->port.lock, flags);
}
+/*
+ * Handle any change of modem status signal since we were last called.
+ */
+static void imx_mctrl_check(struct imx_port *sport)
+{
+ unsigned int status, changed;
+
+ status = imx_get_hwmctrl(sport);
+ changed = status ^ sport->old_status;
+
+ if (changed == 0)
+ return;
+
+ sport->old_status = status;
+
+ if (changed & TIOCM_RI)
+ sport->port.icount.rng++;
+ if (changed & TIOCM_DSR)
+ sport->port.icount.dsr++;
+ if (changed & TIOCM_CAR)
+ uart_handle_dcd_change(&sport->port, status & TIOCM_CAR);
+ if (changed & TIOCM_CTS)
+ uart_handle_cts_change(&sport->port, status & TIOCM_CTS);
+
+ wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
+}
+
+/*
+ * This is our per-port timeout handler, for checking the
+ * modem status signals.
+ */
+static void imx_timeout(unsigned long data)
+{
+ struct imx_port *sport = (struct imx_port *)data;
+ unsigned long flags;
+
+ if (sport->port.state) {
+ spin_lock_irqsave(&sport->port.lock, flags);
+ imx_mctrl_check(sport);
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+
+ mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT);
+ }
+}
+
#define RX_BUF_SIZE (PAGE_SIZE)
static void imx_rx_dma_done(struct imx_port *sport)
{
@@ -1207,6 +1255,8 @@ static void imx_shutdown(struct uart_port *port)
imx_uart_dma_exit(sport);
}
+ mctrl_gpio_disable_ms(sport->gpios);
+
spin_lock_irqsave(&sport->port.lock, flags);
temp = readl(sport->port.membase + UCR2);
temp &= ~(UCR2_TXEN);
@@ -1284,9 +1334,10 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long flags;
- unsigned int ucr2, old_ucr1, old_ucr2, baud, quot;
+ unsigned long ucr2, old_ucr1, old_ucr2;
+ unsigned int baud, quot;
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
- unsigned int div, ufcr;
+ unsigned long div, ufcr;
unsigned long num, denom;
uint64_t tdiv64;
@@ -1315,19 +1366,25 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
* it under manual control and keep transmitter
* disabled.
*/
- if (!(port->rs485.flags &
- SER_RS485_RTS_AFTER_SEND))
- ucr2 |= UCR2_CTS;
+ if (port->rs485.flags &
+ SER_RS485_RTS_AFTER_SEND)
+ imx_port_rts_inactive(sport, &ucr2);
+ else
+ imx_port_rts_active(sport, &ucr2);
} else {
- ucr2 |= UCR2_CTSC;
+ imx_port_rts_auto(sport, &ucr2);
}
} else {
termios->c_cflag &= ~CRTSCTS;
}
- } else if (port->rs485.flags & SER_RS485_ENABLED)
+ } else if (port->rs485.flags & SER_RS485_ENABLED) {
/* disable transmitter */
- if (!(port->rs485.flags & SER_RS485_RTS_AFTER_SEND))
- ucr2 |= UCR2_CTS;
+ if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
+ imx_port_rts_inactive(sport, &ucr2);
+ else
+ imx_port_rts_active(sport, &ucr2);
+ }
+
if (termios->c_cflag & CSTOPB)
ucr2 |= UCR2_STPB;
@@ -1568,11 +1625,10 @@ static int imx_rs485_config(struct uart_port *port,
/* disable transmitter */
temp = readl(sport->port.membase + UCR2);
- temp &= ~UCR2_CTSC;
if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND)
- temp &= ~UCR2_CTS;
+ imx_port_rts_inactive(sport, &temp);
else
- temp |= UCR2_CTS;
+ imx_port_rts_active(sport, &temp);
writel(temp, sport->port.membase + UCR2);
}
@@ -1857,11 +1913,10 @@ static int serial_imx_probe_dt(struct imx_port *sport,
struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- const struct of_device_id *of_id =
- of_match_device(imx_uart_dt_ids, &pdev->dev);
int ret;
- if (!np)
+ sport->devdata = of_device_get_match_data(&pdev->dev);
+ if (!sport->devdata)
/* no device tree device */
return 1;
@@ -1878,8 +1933,6 @@ static int serial_imx_probe_dt(struct imx_port *sport,
if (of_get_property(np, "fsl,dte-mode", NULL))
sport->dte_mode = 1;
- sport->devdata = of_id->data;
-
return 0;
}
#else
@@ -1948,6 +2001,10 @@ static int serial_imx_probe(struct platform_device *pdev)
sport->timer.function = imx_timeout;
sport->timer.data = (unsigned long)sport;
+ sport->gpios = mctrl_gpio_init(&sport->port, 0);
+ if (IS_ERR(sport->gpios))
+ return PTR_ERR(sport->gpios);
+
sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(sport->clk_ipg)) {
ret = PTR_ERR(sport->clk_ipg);
diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c
index efbd87a76656..a119f11bf2f4 100644
--- a/drivers/tty/serial/jsm/jsm_driver.c
+++ b/drivers/tty/serial/jsm/jsm_driver.c
@@ -70,7 +70,7 @@ static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)
goto out;
}
- rc = pci_request_regions(pdev, "jsm");
+ rc = pci_request_regions(pdev, JSM_DRIVER_NAME);
if (rc) {
dev_err(&pdev->dev, "pci_request_region FAILED\n");
goto out_disable_device;
@@ -328,7 +328,7 @@ static struct pci_device_id jsm_pci_tbl[] = {
MODULE_DEVICE_TABLE(pci, jsm_pci_tbl);
static struct pci_driver jsm_driver = {
- .name = "jsm",
+ .name = JSM_DRIVER_NAME,
.id_table = jsm_pci_tbl,
.probe = jsm_probe_one,
.remove = jsm_remove_one,
diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c
index 932b2accd06f..c6fdd6369534 100644
--- a/drivers/tty/serial/jsm/jsm_neo.c
+++ b/drivers/tty/serial/jsm/jsm_neo.c
@@ -714,7 +714,7 @@ static void neo_clear_break(struct jsm_channel *ch)
/*
* Parse the ISR register.
*/
-static inline void neo_parse_isr(struct jsm_board *brd, u32 port)
+static void neo_parse_isr(struct jsm_board *brd, u32 port)
{
struct jsm_channel *ch;
u8 isr;
diff --git a/drivers/tty/serial/m32r_sio.c b/drivers/tty/serial/m32r_sio.c
index 8f7f83a14c93..0eeb64f2499c 100644
--- a/drivers/tty/serial/m32r_sio.c
+++ b/drivers/tty/serial/m32r_sio.c
@@ -990,7 +990,7 @@ static void __init m32r_sio_register_ports(struct uart_driver *drv)
/*
* Wait for transmitter & holding register to empty
*/
-static inline void wait_for_xmitr(struct uart_sio_port *up)
+static void wait_for_xmitr(struct uart_sio_port *up)
{
unsigned int status, tmout = 10000;
diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c
index 3141aa20843d..a44290e9b5a8 100644
--- a/drivers/tty/serial/men_z135_uart.c
+++ b/drivers/tty/serial/men_z135_uart.c
@@ -158,7 +158,7 @@ static inline void men_z135_reg_set(struct men_z135_port *uart,
* @addr: Register address
* @val: value to clear
*/
-static inline void men_z135_reg_clr(struct men_z135_port *uart,
+static void men_z135_reg_clr(struct men_z135_port *uart,
u32 addr, u32 val)
{
struct uart_port *port = &uart->port;
diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c
index 0fc83c962d10..b12a37bd37b6 100644
--- a/drivers/tty/serial/meson_uart.c
+++ b/drivers/tty/serial/meson_uart.c
@@ -57,6 +57,7 @@
#define AML_UART_RX_EMPTY BIT(20)
#define AML_UART_TX_FULL BIT(21)
#define AML_UART_TX_EMPTY BIT(22)
+#define AML_UART_XMIT_BUSY BIT(25)
#define AML_UART_ERR (AML_UART_PARITY_ERR | \
AML_UART_FRAME_ERR | \
AML_UART_TX_FIFO_WERR)
@@ -100,7 +101,8 @@ static unsigned int meson_uart_tx_empty(struct uart_port *port)
u32 val;
val = readl(port->membase + AML_UART_STATUS);
- return (val & AML_UART_TX_EMPTY) ? TIOCSER_TEMT : 0;
+ val &= (AML_UART_TX_EMPTY | AML_UART_XMIT_BUSY);
+ return (val == AML_UART_TX_EMPTY) ? TIOCSER_TEMT : 0;
}
static void meson_uart_stop_tx(struct uart_port *port)
@@ -108,7 +110,7 @@ static void meson_uart_stop_tx(struct uart_port *port)
u32 val;
val = readl(port->membase + AML_UART_CONTROL);
- val &= ~AML_UART_TX_EN;
+ val &= ~AML_UART_TX_INT_EN;
writel(val, port->membase + AML_UART_CONTROL);
}
@@ -131,7 +133,7 @@ static void meson_uart_shutdown(struct uart_port *port)
spin_lock_irqsave(&port->lock, flags);
val = readl(port->membase + AML_UART_CONTROL);
- val &= ~(AML_UART_RX_EN | AML_UART_TX_EN);
+ val &= ~AML_UART_RX_EN;
val &= ~(AML_UART_RX_INT_EN | AML_UART_TX_INT_EN);
writel(val, port->membase + AML_UART_CONTROL);
@@ -142,6 +144,7 @@ static void meson_uart_start_tx(struct uart_port *port)
{
struct circ_buf *xmit = &port->state->xmit;
unsigned int ch;
+ u32 val;
if (uart_tx_stopped(port)) {
meson_uart_stop_tx(port);
@@ -165,6 +168,12 @@ static void meson_uart_start_tx(struct uart_port *port)
port->icount.tx++;
}
+ if (!uart_circ_empty(xmit)) {
+ val = readl(port->membase + AML_UART_CONTROL);
+ val |= AML_UART_TX_INT_EN;
+ writel(val, port->membase + AML_UART_CONTROL);
+ }
+
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
}
@@ -228,8 +237,10 @@ static irqreturn_t meson_uart_interrupt(int irq, void *dev_id)
if (!(readl(port->membase + AML_UART_STATUS) & AML_UART_RX_EMPTY))
meson_receive_chars(port);
- if (!(readl(port->membase + AML_UART_STATUS) & AML_UART_TX_FULL))
- meson_uart_start_tx(port);
+ if (!(readl(port->membase + AML_UART_STATUS) & AML_UART_TX_FULL)) {
+ if (readl(port->membase + AML_UART_CONTROL) & AML_UART_TX_INT_EN)
+ meson_uart_start_tx(port);
+ }
spin_unlock(&port->lock);
@@ -241,10 +252,9 @@ static const char *meson_uart_type(struct uart_port *port)
return (port->type == PORT_MESON) ? "meson_uart" : NULL;
}
-static int meson_uart_startup(struct uart_port *port)
+static void meson_uart_reset(struct uart_port *port)
{
u32 val;
- int ret = 0;
val = readl(port->membase + AML_UART_CONTROL);
val |= (AML_UART_RX_RST | AML_UART_TX_RST | AML_UART_CLR_ERR);
@@ -252,6 +262,18 @@ static int meson_uart_startup(struct uart_port *port)
val &= ~(AML_UART_RX_RST | AML_UART_TX_RST | AML_UART_CLR_ERR);
writel(val, port->membase + AML_UART_CONTROL);
+}
+
+static int meson_uart_startup(struct uart_port *port)
+{
+ u32 val;
+ int ret = 0;
+
+ val = readl(port->membase + AML_UART_CONTROL);
+ val |= AML_UART_CLR_ERR;
+ writel(val, port->membase + AML_UART_CONTROL);
+ val &= ~AML_UART_CLR_ERR;
+ writel(val, port->membase + AML_UART_CONTROL);
val |= (AML_UART_RX_EN | AML_UART_TX_EN);
writel(val, port->membase + AML_UART_CONTROL);
@@ -272,7 +294,7 @@ static void meson_uart_change_speed(struct uart_port *port, unsigned long baud)
{
u32 val;
- while (!(readl(port->membase + AML_UART_STATUS) & AML_UART_TX_EMPTY))
+ while (!meson_uart_tx_empty(port))
cpu_relax();
val = readl(port->membase + AML_UART_REG5);
@@ -367,9 +389,26 @@ static int meson_uart_verify_port(struct uart_port *port,
return ret;
}
+static int meson_uart_res_size(struct uart_port *port)
+{
+ struct platform_device *pdev = to_platform_device(port->dev);
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(port->dev, "cannot obtain I/O memory region");
+ return -ENODEV;
+ }
+
+ return resource_size(res);
+}
+
static void meson_uart_release_port(struct uart_port *port)
{
+ int size = meson_uart_res_size(port);
+
if (port->flags & UPF_IOREMAP) {
+ devm_release_mem_region(port->dev, port->mapbase, size);
devm_iounmap(port->dev, port->membase);
port->membase = NULL;
}
@@ -377,16 +416,10 @@ static void meson_uart_release_port(struct uart_port *port)
static int meson_uart_request_port(struct uart_port *port)
{
- struct platform_device *pdev = to_platform_device(port->dev);
- struct resource *res;
- int size;
+ int size = meson_uart_res_size(port);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "cannot obtain I/O memory region");
- return -ENODEV;
- }
- size = resource_size(res);
+ if (size < 0)
+ return size;
if (!devm_request_mem_region(port->dev, port->mapbase, size,
dev_name(port->dev))) {
@@ -448,6 +481,7 @@ static void meson_serial_console_write(struct console *co, const char *s,
struct uart_port *port;
unsigned long flags;
int locked;
+ u32 val, tmp;
port = meson_ports[co->index];
if (!port)
@@ -463,7 +497,13 @@ static void meson_serial_console_write(struct console *co, const char *s,
locked = 1;
}
+ val = readl(port->membase + AML_UART_CONTROL);
+ val |= AML_UART_TX_EN;
+ tmp = val & ~(AML_UART_TX_INT_EN | AML_UART_RX_INT_EN);
+ writel(tmp, port->membase + AML_UART_CONTROL);
+
uart_console_write(port, s, count, meson_console_putchar);
+ writel(val, port->membase + AML_UART_CONTROL);
if (locked)
spin_unlock(&port->lock);
@@ -570,6 +610,12 @@ static int meson_uart_probe(struct platform_device *pdev)
meson_ports[pdev->id] = port;
platform_set_drvdata(pdev, port);
+ /* reset port before registering (and possibly registering console) */
+ if (meson_uart_request_port(port) >= 0) {
+ meson_uart_reset(port);
+ meson_uart_release_port(port);
+ }
+
ret = uart_add_one_port(&meson_uart_driver, port);
if (ret)
meson_ports[pdev->id] = NULL;
diff --git a/drivers/tty/serial/nwpserial.c b/drivers/tty/serial/nwpserial.c
deleted file mode 100644
index 5da7622e88c3..000000000000
--- a/drivers/tty/serial/nwpserial.c
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * Serial Port driver for a NWP uart device
- *
- * Copyright (C) 2008 IBM Corp., Benjamin Krill <ben@codiert.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 of the License, or (at your option) any later version.
- *
- */
-#include <linux/init.h>
-#include <linux/export.h>
-#include <linux/console.h>
-#include <linux/serial.h>
-#include <linux/serial_reg.h>
-#include <linux/serial_core.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/irqreturn.h>
-#include <linux/mutex.h>
-#include <linux/of_platform.h>
-#include <linux/of_device.h>
-#include <linux/nwpserial.h>
-#include <linux/delay.h>
-#include <asm/prom.h>
-#include <asm/dcr.h>
-
-#define NWPSERIAL_NR 2
-
-#define NWPSERIAL_STATUS_RXVALID 0x1
-#define NWPSERIAL_STATUS_TXFULL 0x2
-
-struct nwpserial_port {
- struct uart_port port;
- dcr_host_t dcr_host;
- unsigned int ier;
- unsigned int mcr;
-};
-
-static DEFINE_MUTEX(nwpserial_mutex);
-static struct nwpserial_port nwpserial_ports[NWPSERIAL_NR];
-
-static void wait_for_bits(struct nwpserial_port *up, int bits)
-{
- unsigned int status, tmout = 10000;
-
- /* Wait up to 10ms for the character(s) to be sent. */
- do {
- status = dcr_read(up->dcr_host, UART_LSR);
-
- if (--tmout == 0)
- break;
- udelay(1);
- } while ((status & bits) != bits);
-}
-
-#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE
-static void nwpserial_console_putchar(struct uart_port *port, int c)
-{
- struct nwpserial_port *up;
- up = container_of(port, struct nwpserial_port, port);
- /* check if tx buffer is full */
- wait_for_bits(up, UART_LSR_THRE);
- dcr_write(up->dcr_host, UART_TX, c);
- up->port.icount.tx++;
-}
-
-static void
-nwpserial_console_write(struct console *co, const char *s, unsigned int count)
-{
- struct nwpserial_port *up = &nwpserial_ports[co->index];
- unsigned long flags;
- int locked = 1;
-
- if (oops_in_progress)
- locked = spin_trylock_irqsave(&up->port.lock, flags);
- else
- spin_lock_irqsave(&up->port.lock, flags);
-
- /* save and disable interrupt */
- up->ier = dcr_read(up->dcr_host, UART_IER);
- dcr_write(up->dcr_host, UART_IER, up->ier & ~UART_IER_RDI);
-
- uart_console_write(&up->port, s, count, nwpserial_console_putchar);
-
- /* wait for transmitter to become empty */
- while ((dcr_read(up->dcr_host, UART_LSR) & UART_LSR_THRE) == 0)
- cpu_relax();
-
- /* restore interrupt state */
- dcr_write(up->dcr_host, UART_IER, up->ier);
-
- if (locked)
- spin_unlock_irqrestore(&up->port.lock, flags);
-}
-
-static struct uart_driver nwpserial_reg;
-static struct console nwpserial_console = {
- .name = "ttySQ",
- .write = nwpserial_console_write,
- .device = uart_console_device,
- .flags = CON_PRINTBUFFER,
- .index = -1,
- .data = &nwpserial_reg,
-};
-#define NWPSERIAL_CONSOLE (&nwpserial_console)
-#else
-#define NWPSERIAL_CONSOLE NULL
-#endif /* CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE */
-
-/**************************************************************************/
-
-static int nwpserial_request_port(struct uart_port *port)
-{
- return 0;
-}
-
-static void nwpserial_release_port(struct uart_port *port)
-{
- /* N/A */
-}
-
-static void nwpserial_config_port(struct uart_port *port, int flags)
-{
- port->type = PORT_NWPSERIAL;
-}
-
-static irqreturn_t nwpserial_interrupt(int irq, void *dev_id)
-{
- struct nwpserial_port *up = dev_id;
- struct tty_port *port = &up->port.state->port;
- irqreturn_t ret;
- unsigned int iir;
- unsigned char ch;
-
- spin_lock(&up->port.lock);
-
- /* check if the uart was the interrupt source. */
- iir = dcr_read(up->dcr_host, UART_IIR);
- if (!iir) {
- ret = IRQ_NONE;
- goto out;
- }
-
- do {
- up->port.icount.rx++;
- ch = dcr_read(up->dcr_host, UART_RX);
- if (up->port.ignore_status_mask != NWPSERIAL_STATUS_RXVALID)
- tty_insert_flip_char(port, ch, TTY_NORMAL);
- } while (dcr_read(up->dcr_host, UART_LSR) & UART_LSR_DR);
-
- spin_unlock(&up->port.lock);
- tty_flip_buffer_push(port);
- spin_lock(&up->port.lock);
-
- ret = IRQ_HANDLED;
-
- /* clear interrupt */
- dcr_write(up->dcr_host, UART_IIR, 1);
-out:
- spin_unlock(&up->port.lock);
- return ret;
-}
-
-static int nwpserial_startup(struct uart_port *port)
-{
- struct nwpserial_port *up;
- int err;
-
- up = container_of(port, struct nwpserial_port, port);
-
- /* disable flow control by default */
- up->mcr = dcr_read(up->dcr_host, UART_MCR) & ~UART_MCR_AFE;
- dcr_write(up->dcr_host, UART_MCR, up->mcr);
-
- /* register interrupt handler */
- err = request_irq(up->port.irq, nwpserial_interrupt,
- IRQF_SHARED, "nwpserial", up);
- if (err)
- return err;
-
- /* enable interrupts */
- up->ier = UART_IER_RDI;
- dcr_write(up->dcr_host, UART_IER, up->ier);
-
- /* enable receiving */
- up->port.ignore_status_mask &= ~NWPSERIAL_STATUS_RXVALID;
-
- return 0;
-}
-
-static void nwpserial_shutdown(struct uart_port *port)
-{
- struct nwpserial_port *up;
- up = container_of(port, struct nwpserial_port, port);
-
- /* disable receiving */
- up->port.ignore_status_mask |= NWPSERIAL_STATUS_RXVALID;
-
- /* disable interrupts from this port */
- up->ier = 0;
- dcr_write(up->dcr_host, UART_IER, up->ier);
-
- /* free irq */
- free_irq(up->port.irq, up);
-}
-
-static int nwpserial_verify_port(struct uart_port *port,
- struct serial_struct *ser)
-{
- return -EINVAL;
-}
-
-static const char *nwpserial_type(struct uart_port *port)
-{
- return port->type == PORT_NWPSERIAL ? "nwpserial" : NULL;
-}
-
-static void nwpserial_set_termios(struct uart_port *port,
- struct ktermios *termios, struct ktermios *old)
-{
- struct nwpserial_port *up;
- up = container_of(port, struct nwpserial_port, port);
-
- up->port.read_status_mask = NWPSERIAL_STATUS_RXVALID
- | NWPSERIAL_STATUS_TXFULL;
-
- up->port.ignore_status_mask = 0;
- /* ignore all characters if CREAD is not set */
- if ((termios->c_cflag & CREAD) == 0)
- up->port.ignore_status_mask |= NWPSERIAL_STATUS_RXVALID;
-
- /* Copy back the old hardware settings */
- if (old)
- tty_termios_copy_hw(termios, old);
-}
-
-static void nwpserial_break_ctl(struct uart_port *port, int ctl)
-{
- /* N/A */
-}
-
-static void nwpserial_stop_rx(struct uart_port *port)
-{
- struct nwpserial_port *up;
- up = container_of(port, struct nwpserial_port, port);
- /* don't forward any more data (like !CREAD) */
- up->port.ignore_status_mask = NWPSERIAL_STATUS_RXVALID;
-}
-
-static void nwpserial_putchar(struct nwpserial_port *up, unsigned char c)
-{
- /* check if tx buffer is full */
- wait_for_bits(up, UART_LSR_THRE);
- dcr_write(up->dcr_host, UART_TX, c);
- up->port.icount.tx++;
-}
-
-static void nwpserial_start_tx(struct uart_port *port)
-{
- struct nwpserial_port *up;
- struct circ_buf *xmit;
- up = container_of(port, struct nwpserial_port, port);
- xmit = &up->port.state->xmit;
-
- if (port->x_char) {
- nwpserial_putchar(up, up->port.x_char);
- port->x_char = 0;
- }
-
- while (!(uart_circ_empty(xmit) || uart_tx_stopped(&up->port))) {
- nwpserial_putchar(up, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1);
- }
-}
-
-static unsigned int nwpserial_get_mctrl(struct uart_port *port)
-{
- return 0;
-}
-
-static void nwpserial_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
- /* N/A */
-}
-
-static void nwpserial_stop_tx(struct uart_port *port)
-{
- /* N/A */
-}
-
-static unsigned int nwpserial_tx_empty(struct uart_port *port)
-{
- struct nwpserial_port *up;
- unsigned long flags;
- int ret;
- up = container_of(port, struct nwpserial_port, port);
-
- spin_lock_irqsave(&up->port.lock, flags);
- ret = dcr_read(up->dcr_host, UART_LSR);
- spin_unlock_irqrestore(&up->port.lock, flags);
-
- return ret & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
-}
-
-static struct uart_ops nwpserial_pops = {
- .tx_empty = nwpserial_tx_empty,
- .set_mctrl = nwpserial_set_mctrl,
- .get_mctrl = nwpserial_get_mctrl,
- .stop_tx = nwpserial_stop_tx,
- .start_tx = nwpserial_start_tx,
- .stop_rx = nwpserial_stop_rx,
- .break_ctl = nwpserial_break_ctl,
- .startup = nwpserial_startup,
- .shutdown = nwpserial_shutdown,
- .set_termios = nwpserial_set_termios,
- .type = nwpserial_type,
- .release_port = nwpserial_release_port,
- .request_port = nwpserial_request_port,
- .config_port = nwpserial_config_port,
- .verify_port = nwpserial_verify_port,
-};
-
-static struct uart_driver nwpserial_reg = {
- .owner = THIS_MODULE,
- .driver_name = "nwpserial",
- .dev_name = "ttySQ",
- .major = TTY_MAJOR,
- .minor = 68,
- .nr = NWPSERIAL_NR,
- .cons = NWPSERIAL_CONSOLE,
-};
-
-int nwpserial_register_port(struct uart_port *port)
-{
- struct nwpserial_port *up = NULL;
- int ret = -1;
- int i;
- static int first = 1;
- int dcr_len;
- int dcr_base;
- struct device_node *dn;
-
- mutex_lock(&nwpserial_mutex);
-
- dn = port->dev->of_node;
- if (dn == NULL)
- goto out;
-
- /* get dcr base. */
- dcr_base = dcr_resource_start(dn, 0);
-
- /* find matching entry */
- for (i = 0; i < NWPSERIAL_NR; i++)
- if (nwpserial_ports[i].port.iobase == dcr_base) {
- up = &nwpserial_ports[i];
- break;
- }
-
- /* we didn't find a mtching entry, search for a free port */
- if (up == NULL)
- for (i = 0; i < NWPSERIAL_NR; i++)
- if (nwpserial_ports[i].port.type == PORT_UNKNOWN &&
- nwpserial_ports[i].port.iobase == 0) {
- up = &nwpserial_ports[i];
- break;
- }
-
- if (up == NULL) {
- ret = -EBUSY;
- goto out;
- }
-
- if (first)
- uart_register_driver(&nwpserial_reg);
- first = 0;
-
- up->port.membase = port->membase;
- up->port.irq = port->irq;
- up->port.uartclk = port->uartclk;
- up->port.fifosize = port->fifosize;
- up->port.regshift = port->regshift;
- up->port.iotype = port->iotype;
- up->port.flags = port->flags;
- up->port.mapbase = port->mapbase;
- up->port.private_data = port->private_data;
-
- if (port->dev)
- up->port.dev = port->dev;
-
- if (up->port.iobase != dcr_base) {
- up->port.ops = &nwpserial_pops;
- up->port.fifosize = 16;
-
- spin_lock_init(&up->port.lock);
-
- up->port.iobase = dcr_base;
- dcr_len = dcr_resource_len(dn, 0);
-
- up->dcr_host = dcr_map(dn, dcr_base, dcr_len);
- if (!DCR_MAP_OK(up->dcr_host)) {
- printk(KERN_ERR "Cannot map DCR resources for NWPSERIAL");
- goto out;
- }
- }
-
- ret = uart_add_one_port(&nwpserial_reg, &up->port);
- if (ret == 0)
- ret = up->port.line;
-
-out:
- mutex_unlock(&nwpserial_mutex);
-
- return ret;
-}
-EXPORT_SYMBOL(nwpserial_register_port);
-
-void nwpserial_unregister_port(int line)
-{
- struct nwpserial_port *up = &nwpserial_ports[line];
- mutex_lock(&nwpserial_mutex);
- uart_remove_one_port(&nwpserial_reg, &up->port);
-
- up->port.type = PORT_UNKNOWN;
-
- mutex_unlock(&nwpserial_mutex);
-}
-EXPORT_SYMBOL(nwpserial_unregister_port);
-
-#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE
-static int __init nwpserial_console_init(void)
-{
- struct nwpserial_port *up = NULL;
- struct device_node *dn;
- const char *name;
- int dcr_base;
- int dcr_len;
- int i;
-
- /* search for a free port */
- for (i = 0; i < NWPSERIAL_NR; i++)
- if (nwpserial_ports[i].port.type == PORT_UNKNOWN) {
- up = &nwpserial_ports[i];
- break;
- }
-
- if (up == NULL)
- return -1;
-
- name = of_get_property(of_chosen, "linux,stdout-path", NULL);
- if (name == NULL)
- return -1;
-
- dn = of_find_node_by_path(name);
- if (!dn)
- return -1;
-
- spin_lock_init(&up->port.lock);
- up->port.ops = &nwpserial_pops;
- up->port.type = PORT_NWPSERIAL;
- up->port.fifosize = 16;
-
- dcr_base = dcr_resource_start(dn, 0);
- dcr_len = dcr_resource_len(dn, 0);
- up->port.iobase = dcr_base;
-
- up->dcr_host = dcr_map(dn, dcr_base, dcr_len);
- if (!DCR_MAP_OK(up->dcr_host)) {
- printk("Cannot map DCR resources for SERIAL");
- return -1;
- }
- register_console(&nwpserial_console);
- return 0;
-}
-console_initcall(nwpserial_console_init);
-#endif /* CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE */
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index 9d4c84f7485f..b645f9228ed7 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -1165,7 +1165,7 @@ serial_omap_type(struct uart_port *port)
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
-static inline void wait_for_xmitr(struct uart_omap_port *up)
+static void wait_for_xmitr(struct uart_omap_port *up)
{
unsigned int status, tmout = 10000;
diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c
index 9becba654892..41eab75ba2af 100644
--- a/drivers/tty/serial/pxa.c
+++ b/drivers/tty/serial/pxa.c
@@ -603,7 +603,7 @@ static struct uart_driver serial_pxa_reg;
/*
* Wait for transmitter & holding register to empty
*/
-static inline void wait_for_xmitr(struct uart_pxa_port *up)
+static void wait_for_xmitr(struct uart_pxa_port *up)
{
unsigned int status, tmout = 10000;
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index edb5305b9d4d..5815bcbc55b2 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -389,6 +389,13 @@ static void sc16is7xx_fifo_write(struct uart_port *port, u8 to_send)
const u8 line = sc16is7xx_line(port);
u8 addr = (SC16IS7XX_THR_REG << SC16IS7XX_REG_SHIFT) | line;
+ /*
+ * Don't send zero-length data, at least on SPI it confuses the chip
+ * delivering wrong TXLVL data.
+ */
+ if (unlikely(!to_send))
+ return;
+
regcache_cache_bypass(s->regmap, true);
regmap_raw_write(s->regmap, addr, s->buf, to_send);
regcache_cache_bypass(s->regmap, false);
@@ -630,6 +637,12 @@ static void sc16is7xx_handle_tx(struct uart_port *port)
if (likely(to_send)) {
/* Limit to size of TX FIFO */
txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG);
+ if (txlen > SC16IS7XX_FIFO_SIZE) {
+ dev_err_ratelimited(port->dev,
+ "chip reports %d free bytes in TX fifo, but it only has %d",
+ txlen, SC16IS7XX_FIFO_SIZE);
+ txlen = 0;
+ }
to_send = (to_send > txlen) ? txlen : to_send;
/* Add data to send */
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index def5199ca004..b1f54ab1818c 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -110,7 +110,7 @@ static void uart_start(struct tty_struct *tty)
spin_unlock_irqrestore(&port->lock, flags);
}
-static inline void
+static void
uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear)
{
unsigned long flags;
@@ -1818,8 +1818,8 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co)
* @options: ptr for <options> field; NULL if not present (out)
*
* Decodes earlycon kernel command line parameters of the form
- * earlycon=<name>,io|mmio|mmio32|mmio32be|mmio32native,<addr>,<options>
- * console=<name>,io|mmio|mmio32|mmio32be|mmio32native,<addr>,<options>
+ * earlycon=<name>,io|mmio|mmio16|mmio32|mmio32be|mmio32native,<addr>,<options>
+ * console=<name>,io|mmio|mmio16|mmio32|mmio32be|mmio32native,<addr>,<options>
*
* The optional form
* earlycon=<name>,0x<addr>,<options>
@@ -1834,6 +1834,9 @@ int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr,
if (strncmp(p, "mmio,", 5) == 0) {
*iotype = UPIO_MEM;
p += 5;
+ } else if (strncmp(p, "mmio16,", 7) == 0) {
+ *iotype = UPIO_MEM16;
+ p += 7;
} else if (strncmp(p, "mmio32,", 7) == 0) {
*iotype = UPIO_MEM32;
p += 7;
@@ -2186,6 +2189,7 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port)
"I/O 0x%lx offset 0x%x", port->iobase, port->hub6);
break;
case UPIO_MEM:
+ case UPIO_MEM16:
case UPIO_MEM32:
case UPIO_MEM32BE:
case UPIO_AU:
@@ -2831,6 +2835,7 @@ int uart_match_port(struct uart_port *port1, struct uart_port *port2)
return (port1->iobase == port2->iobase) &&
(port1->hub6 == port2->hub6);
case UPIO_MEM:
+ case UPIO_MEM16:
case UPIO_MEM32:
case UPIO_MEM32BE:
case UPIO_AU:
diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c
index 3eb57eb532f1..226ad23b136c 100644
--- a/drivers/tty/serial/serial_mctrl_gpio.c
+++ b/drivers/tty/serial/serial_mctrl_gpio.c
@@ -193,6 +193,7 @@ struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx)
return gpios;
}
+EXPORT_SYMBOL_GPL(mctrl_gpio_init);
void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios)
{
@@ -247,3 +248,4 @@ void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios)
disable_irq(gpios->irq[i]);
}
}
+EXPORT_SYMBOL_GPL(mctrl_gpio_disable_ms);
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 960e50a97558..4646a9f531ad 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -2,6 +2,7 @@
* SuperH on-chip serial module support. (SCI with no FIFO / with FIFO)
*
* Copyright (C) 2002 - 2011 Paul Mundt
+ * Copyright (C) 2015 Glider bvba
* Modified to support SH7720 SCIF. Markus Brunner, Mark Jonas (Jul 2007).
*
* based off of the old drivers/char/sh-sci.c by:
@@ -38,7 +39,6 @@
#include <linux/major.h>
#include <linux/module.h>
#include <linux/mm.h>
-#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -76,6 +76,14 @@ enum {
((port)->irqs[SCIx_ERI_IRQ] && \
((port)->irqs[SCIx_RXI_IRQ] < 0))
+enum SCI_CLKS {
+ SCI_FCK, /* Functional Clock */
+ SCI_SCK, /* Optional External Clock */
+ SCI_BRG_INT, /* Optional BRG Internal Clock Source */
+ SCI_SCIF_CLK, /* Optional BRG External Clock Source */
+ SCI_NUM_CLKS
+};
+
struct sci_port {
struct uart_port port;
@@ -92,10 +100,9 @@ struct sci_port {
struct timer_list break_timer;
int break_flag;
- /* Interface clock */
- struct clk *iclk;
- /* Function clock */
- struct clk *fclk;
+ /* Clocks */
+ struct clk *clks[SCI_NUM_CLKS];
+ unsigned long clk_rates[SCI_NUM_CLKS];
int irqs[SCIx_NR_IRQS];
char *irqstr[SCIx_NR_IRQS];
@@ -116,8 +123,6 @@ struct sci_port {
struct timer_list rx_timer;
unsigned int rx_timeout;
#endif
-
- struct notifier_block freq_transition;
};
#define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS
@@ -163,6 +168,8 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[HSSRR] = sci_reg_invalid,
[SCPCR] = sci_reg_invalid,
[SCPDR] = sci_reg_invalid,
+ [SCDL] = sci_reg_invalid,
+ [SCCKS] = sci_reg_invalid,
},
/*
@@ -185,6 +192,8 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[HSSRR] = sci_reg_invalid,
[SCPCR] = sci_reg_invalid,
[SCPDR] = sci_reg_invalid,
+ [SCDL] = sci_reg_invalid,
+ [SCCKS] = sci_reg_invalid,
},
/*
@@ -206,6 +215,8 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[HSSRR] = sci_reg_invalid,
[SCPCR] = { 0x30, 16 },
[SCPDR] = { 0x34, 16 },
+ [SCDL] = sci_reg_invalid,
+ [SCCKS] = sci_reg_invalid,
},
/*
@@ -227,6 +238,8 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[HSSRR] = sci_reg_invalid,
[SCPCR] = { 0x30, 16 },
[SCPDR] = { 0x34, 16 },
+ [SCDL] = sci_reg_invalid,
+ [SCCKS] = sci_reg_invalid,
},
/*
@@ -249,6 +262,8 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[HSSRR] = sci_reg_invalid,
[SCPCR] = sci_reg_invalid,
[SCPDR] = sci_reg_invalid,
+ [SCDL] = sci_reg_invalid,
+ [SCCKS] = sci_reg_invalid,
},
/*
@@ -270,6 +285,8 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[HSSRR] = sci_reg_invalid,
[SCPCR] = sci_reg_invalid,
[SCPDR] = sci_reg_invalid,
+ [SCDL] = sci_reg_invalid,
+ [SCCKS] = sci_reg_invalid,
},
/*
@@ -291,6 +308,32 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[HSSRR] = sci_reg_invalid,
[SCPCR] = sci_reg_invalid,
[SCPDR] = sci_reg_invalid,
+ [SCDL] = sci_reg_invalid,
+ [SCCKS] = sci_reg_invalid,
+ },
+
+ /*
+ * Common SCIF definitions for ports with a Baud Rate Generator for
+ * External Clock (BRG).
+ */
+ [SCIx_SH4_SCIF_BRG_REGTYPE] = {
+ [SCSMR] = { 0x00, 16 },
+ [SCBRR] = { 0x04, 8 },
+ [SCSCR] = { 0x08, 16 },
+ [SCxTDR] = { 0x0c, 8 },
+ [SCxSR] = { 0x10, 16 },
+ [SCxRDR] = { 0x14, 8 },
+ [SCFCR] = { 0x18, 16 },
+ [SCFDR] = { 0x1c, 16 },
+ [SCTFDR] = sci_reg_invalid,
+ [SCRFDR] = sci_reg_invalid,
+ [SCSPTR] = { 0x20, 16 },
+ [SCLSR] = { 0x24, 16 },
+ [HSSRR] = sci_reg_invalid,
+ [SCPCR] = sci_reg_invalid,
+ [SCPDR] = sci_reg_invalid,
+ [SCDL] = { 0x30, 16 },
+ [SCCKS] = { 0x34, 16 },
},
/*
@@ -312,6 +355,8 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[HSSRR] = { 0x40, 16 },
[SCPCR] = sci_reg_invalid,
[SCPDR] = sci_reg_invalid,
+ [SCDL] = { 0x30, 16 },
+ [SCCKS] = { 0x34, 16 },
},
/*
@@ -334,6 +379,8 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[HSSRR] = sci_reg_invalid,
[SCPCR] = sci_reg_invalid,
[SCPDR] = sci_reg_invalid,
+ [SCDL] = sci_reg_invalid,
+ [SCCKS] = sci_reg_invalid,
},
/*
@@ -356,6 +403,8 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[HSSRR] = sci_reg_invalid,
[SCPCR] = sci_reg_invalid,
[SCPDR] = sci_reg_invalid,
+ [SCDL] = sci_reg_invalid,
+ [SCCKS] = sci_reg_invalid,
},
/*
@@ -378,6 +427,8 @@ static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
[HSSRR] = sci_reg_invalid,
[SCPCR] = sci_reg_invalid,
[SCPDR] = sci_reg_invalid,
+ [SCDL] = sci_reg_invalid,
+ [SCCKS] = sci_reg_invalid,
},
};
@@ -452,18 +503,24 @@ static int sci_probe_regmap(struct plat_sci_port *cfg)
static void sci_port_enable(struct sci_port *sci_port)
{
+ unsigned int i;
+
if (!sci_port->port.dev)
return;
pm_runtime_get_sync(sci_port->port.dev);
- clk_prepare_enable(sci_port->iclk);
- sci_port->port.uartclk = clk_get_rate(sci_port->iclk);
- clk_prepare_enable(sci_port->fclk);
+ for (i = 0; i < SCI_NUM_CLKS; i++) {
+ clk_prepare_enable(sci_port->clks[i]);
+ sci_port->clk_rates[i] = clk_get_rate(sci_port->clks[i]);
+ }
+ sci_port->port.uartclk = sci_port->clk_rates[SCI_FCK];
}
static void sci_port_disable(struct sci_port *sci_port)
{
+ unsigned int i;
+
if (!sci_port->port.dev)
return;
@@ -475,8 +532,8 @@ static void sci_port_disable(struct sci_port *sci_port)
del_timer_sync(&sci_port->break_timer);
sci_port->break_flag = 0;
- clk_disable_unprepare(sci_port->fclk);
- clk_disable_unprepare(sci_port->iclk);
+ for (i = SCI_NUM_CLKS; i-- > 0; )
+ clk_disable_unprepare(sci_port->clks[i]);
pm_runtime_put_sync(sci_port->port.dev);
}
@@ -1437,7 +1494,7 @@ static void sci_request_dma(struct uart_port *port)
sg_init_table(sg, 1);
s->rx_buf[i] = buf;
sg_dma_address(sg) = dma;
- sg->length = s->buf_len_rx;
+ sg_dma_len(sg) = s->buf_len_rx;
buf += s->buf_len_rx;
dma += s->buf_len_rx;
@@ -1606,29 +1663,6 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
return ret;
}
-/*
- * Here we define a transition notifier so that we can update all of our
- * ports' baud rate when the peripheral clock changes.
- */
-static int sci_notifier(struct notifier_block *self,
- unsigned long phase, void *p)
-{
- struct sci_port *sci_port;
- unsigned long flags;
-
- sci_port = container_of(self, struct sci_port, freq_transition);
-
- if (phase == CPUFREQ_POSTCHANGE) {
- struct uart_port *port = &sci_port->port;
-
- spin_lock_irqsave(&port->lock, flags);
- port->uartclk = clk_get_rate(sci_port->iclk);
- spin_unlock_irqrestore(&port->lock, flags);
- }
-
- return NOTIFY_OK;
-}
-
static const struct sci_irq_desc {
const char *desc;
irq_handler_t handler;
@@ -1864,90 +1898,149 @@ static void sci_shutdown(struct uart_port *port)
sci_free_irq(s);
}
-static unsigned int sci_scbrr_calc(struct sci_port *s, unsigned int bps,
- unsigned long freq)
+static int sci_sck_calc(struct sci_port *s, unsigned int bps,
+ unsigned int *srr)
{
- if (s->sampling_rate)
- return DIV_ROUND_CLOSEST(freq, s->sampling_rate * bps) - 1;
+ unsigned long freq = s->clk_rates[SCI_SCK];
+ unsigned int min_sr, max_sr, sr;
+ int err, min_err = INT_MAX;
+
+ if (s->sampling_rate) {
+ /* SCI(F) has a fixed sampling rate */
+ min_sr = max_sr = s->sampling_rate / 2;
+ } else {
+ /* HSCIF has a variable 1/(8..32) sampling rate */
+ min_sr = 8;
+ max_sr = 32;
+ }
+
+ for (sr = max_sr; sr >= min_sr; sr--) {
+ err = DIV_ROUND_CLOSEST(freq, sr) - bps;
+ if (abs(err) >= abs(min_err))
+ continue;
- /* Warn, but use a safe default */
- WARN_ON(1);
+ min_err = err;
+ *srr = sr - 1;
- return ((freq + 16 * bps) / (32 * bps) - 1);
+ if (!err)
+ break;
+ }
+
+ dev_dbg(s->port.dev, "SCK: %u%+d bps using SR %u\n", bps, min_err,
+ *srr + 1);
+ return min_err;
}
-/* calculate frame length from SMR */
-static int sci_baud_calc_frame_len(unsigned int smr_val)
+static int sci_brg_calc(struct sci_port *s, unsigned int bps,
+ unsigned long freq, unsigned int *dlr,
+ unsigned int *srr)
{
- int len = 10;
+ unsigned int min_sr, max_sr, sr, dl;
+ int err, min_err = INT_MAX;
- if (smr_val & SCSMR_CHR)
- len--;
- if (smr_val & SCSMR_PE)
- len++;
- if (smr_val & SCSMR_STOP)
- len++;
+ if (s->sampling_rate) {
+ /* SCIF has a fixed sampling rate */
+ min_sr = max_sr = s->sampling_rate / 2;
+ } else {
+ /* HSCIF has a variable 1/(8..32) sampling rate */
+ min_sr = 8;
+ max_sr = 32;
+ }
- return len;
-}
+ for (sr = max_sr; sr >= min_sr; sr--) {
+ dl = DIV_ROUND_CLOSEST(freq, sr * bps);
+ dl = clamp(dl, 1U, 65535U);
+
+ err = DIV_ROUND_CLOSEST(freq, sr * dl) - bps;
+ if (abs(err) >= abs(min_err))
+ continue;
+ min_err = err;
+ *dlr = dl;
+ *srr = sr - 1;
-/* calculate sample rate, BRR, and clock select for HSCIF */
-static void sci_baud_calc_hscif(unsigned int bps, unsigned long freq,
- int *brr, unsigned int *srr,
- unsigned int *cks, int frame_len)
+ if (!err)
+ break;
+ }
+
+ dev_dbg(s->port.dev, "BRG: %u%+d bps using DL %u SR %u\n", bps,
+ min_err, *dlr, *srr + 1);
+ return min_err;
+}
+
+/* calculate sample rate, BRR, and clock select */
+static int sci_scbrr_calc(struct sci_port *s, unsigned int bps,
+ unsigned int *brr, unsigned int *srr,
+ unsigned int *cks)
{
- int sr, c, br, err, recv_margin;
- int min_err = 1000; /* 100% */
- int recv_max_margin = 0;
+ unsigned int min_sr, max_sr, shift, sr, br, prediv, scrate, c;
+ unsigned long freq = s->clk_rates[SCI_FCK];
+ int err, min_err = INT_MAX;
- /* Find the combination of sample rate and clock select with the
- smallest deviation from the desired baud rate. */
- for (sr = 8; sr <= 32; sr++) {
+ if (s->sampling_rate) {
+ min_sr = max_sr = s->sampling_rate;
+ shift = 0;
+ } else {
+ /* HSCIF has a variable sample rate */
+ min_sr = 8;
+ max_sr = 32;
+ shift = 1;
+ }
+
+ /*
+ * Find the combination of sample rate and clock select with the
+ * smallest deviation from the desired baud rate.
+ * Prefer high sample rates to maximise the receive margin.
+ *
+ * M: Receive margin (%)
+ * N: Ratio of bit rate to clock (N = sampling rate)
+ * D: Clock duty (D = 0 to 1.0)
+ * L: Frame length (L = 9 to 12)
+ * F: Absolute value of clock frequency deviation
+ *
+ * M = |(0.5 - 1 / 2 * N) - ((L - 0.5) * F) -
+ * (|D - 0.5| / N * (1 + F))|
+ * NOTE: Usually, treat D for 0.5, F is 0 by this calculation.
+ */
+ for (sr = max_sr; sr >= min_sr; sr--) {
for (c = 0; c <= 3; c++) {
/* integerized formulas from HSCIF documentation */
- br = DIV_ROUND_CLOSEST(freq, (sr *
- (1 << (2 * c + 1)) * bps)) - 1;
- br = clamp(br, 0, 255);
- err = DIV_ROUND_CLOSEST(freq, ((br + 1) * bps * sr *
- (1 << (2 * c + 1)) / 1000)) -
- 1000;
- /* Calc recv margin
- * M: Receive margin (%)
- * N: Ratio of bit rate to clock (N = sampling rate)
- * D: Clock duty (D = 0 to 1.0)
- * L: Frame length (L = 9 to 12)
- * F: Absolute value of clock frequency deviation
+ prediv = sr * (1 << (2 * c + shift));
+
+ /*
+ * We need to calculate:
*
- * M = |(0.5 - 1 / 2 * N) - ((L - 0.5) * F) -
- * (|D - 0.5| / N * (1 + F))|
- * NOTE: Usually, treat D for 0.5, F is 0 by this
- * calculation.
+ * br = freq / (prediv * bps) clamped to [1..256]
+ * err = freq / (br * prediv) - bps
+ *
+ * Watch out for overflow when calculating the desired
+ * sampling clock rate!
*/
- recv_margin = abs((500 -
- DIV_ROUND_CLOSEST(1000, sr << 1)) / 10);
- if (abs(min_err) > abs(err)) {
- min_err = err;
- recv_max_margin = recv_margin;
- } else if ((min_err == err) &&
- (recv_margin > recv_max_margin))
- recv_max_margin = recv_margin;
- else
+ if (bps > UINT_MAX / prediv)
+ break;
+
+ scrate = prediv * bps;
+ br = DIV_ROUND_CLOSEST(freq, scrate);
+ br = clamp(br, 1U, 256U);
+
+ err = DIV_ROUND_CLOSEST(freq, br * prediv) - bps;
+ if (abs(err) >= abs(min_err))
continue;
- *brr = br;
+ min_err = err;
+ *brr = br - 1;
*srr = sr - 1;
*cks = c;
+
+ if (!err)
+ goto found;
}
}
- if (min_err == 1000) {
- WARN_ON(1);
- /* use defaults */
- *brr = 255;
- *srr = 15;
- *cks = 0;
- }
+found:
+ dev_dbg(s->port.dev, "BRR: %u%+d bps using N %u SR %u cks %u\n", bps,
+ min_err, *brr, *srr + 1, *cks);
+ return min_err;
}
static void sci_reset(struct uart_port *port)
@@ -1969,11 +2062,14 @@ static void sci_reset(struct uart_port *port)
static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
+ unsigned int baud, smr_val = 0, scr_val = 0, i;
+ unsigned int brr = 255, cks = 0, srr = 15, dl = 0, sccks = 0;
+ unsigned int brr1 = 255, cks1 = 0, srr1 = 15, dl1 = 0;
struct sci_port *s = to_sci_port(port);
const struct plat_sci_reg *reg;
- unsigned int baud, smr_val = 0, max_baud, cks = 0;
- int t = -1;
- unsigned int srr = 15;
+ int min_err = INT_MAX, err;
+ unsigned long max_freq = 0;
+ int best_clk = -1;
if ((termios->c_cflag & CSIZE) == CS7)
smr_val |= SCSMR_CHR;
@@ -1992,41 +2088,123 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
* that the previous boot loader has enabled required clocks and
* setup the baud rate generator hardware for us already.
*/
- max_baud = port->uartclk ? port->uartclk / 16 : 115200;
-
- baud = uart_get_baud_rate(port, termios, old, 0, max_baud);
- if (likely(baud && port->uartclk)) {
- if (s->cfg->type == PORT_HSCIF) {
- int frame_len = sci_baud_calc_frame_len(smr_val);
- sci_baud_calc_hscif(baud, port->uartclk, &t, &srr,
- &cks, frame_len);
- } else {
- t = sci_scbrr_calc(s, baud, port->uartclk);
- for (cks = 0; t >= 256 && cks <= 3; cks++)
- t >>= 2;
+ if (!port->uartclk) {
+ baud = uart_get_baud_rate(port, termios, old, 0, 115200);
+ goto done;
+ }
+
+ for (i = 0; i < SCI_NUM_CLKS; i++)
+ max_freq = max(max_freq, s->clk_rates[i]);
+
+ baud = uart_get_baud_rate(port, termios, old, 0,
+ max_freq / max(s->sampling_rate, 8U));
+ if (!baud)
+ goto done;
+
+ /*
+ * There can be multiple sources for the sampling clock. Find the one
+ * that gives us the smallest deviation from the desired baud rate.
+ */
+
+ /* Optional Undivided External Clock */
+ if (s->clk_rates[SCI_SCK] && port->type != PORT_SCIFA &&
+ port->type != PORT_SCIFB) {
+ err = sci_sck_calc(s, baud, &srr1);
+ if (abs(err) < abs(min_err)) {
+ best_clk = SCI_SCK;
+ scr_val = SCSCR_CKE1;
+ sccks = SCCKS_CKS;
+ min_err = err;
+ srr = srr1;
+ if (!err)
+ goto done;
}
}
+ /* Optional BRG Frequency Divided External Clock */
+ if (s->clk_rates[SCI_SCIF_CLK] && sci_getreg(port, SCDL)->size) {
+ err = sci_brg_calc(s, baud, s->clk_rates[SCI_SCIF_CLK], &dl1,
+ &srr1);
+ if (abs(err) < abs(min_err)) {
+ best_clk = SCI_SCIF_CLK;
+ scr_val = SCSCR_CKE1;
+ sccks = 0;
+ min_err = err;
+ dl = dl1;
+ srr = srr1;
+ if (!err)
+ goto done;
+ }
+ }
+
+ /* Optional BRG Frequency Divided Internal Clock */
+ if (s->clk_rates[SCI_BRG_INT] && sci_getreg(port, SCDL)->size) {
+ err = sci_brg_calc(s, baud, s->clk_rates[SCI_BRG_INT], &dl1,
+ &srr1);
+ if (abs(err) < abs(min_err)) {
+ best_clk = SCI_BRG_INT;
+ scr_val = SCSCR_CKE1;
+ sccks = SCCKS_XIN;
+ min_err = err;
+ dl = dl1;
+ srr = srr1;
+ if (!min_err)
+ goto done;
+ }
+ }
+
+ /* Divided Functional Clock using standard Bit Rate Register */
+ err = sci_scbrr_calc(s, baud, &brr1, &srr1, &cks1);
+ if (abs(err) < abs(min_err)) {
+ best_clk = SCI_FCK;
+ scr_val = 0;
+ min_err = err;
+ brr = brr1;
+ srr = srr1;
+ cks = cks1;
+ }
+
+done:
+ if (best_clk >= 0)
+ dev_dbg(port->dev, "Using clk %pC for %u%+d bps\n",
+ s->clks[best_clk], baud, min_err);
+
sci_port_enable(s);
- sci_reset(port);
+ /*
+ * Program the optional External Baud Rate Generator (BRG) first.
+ * It controls the mux to select (H)SCK or frequency divided clock.
+ */
+ if (best_clk >= 0 && sci_getreg(port, SCCKS)->size) {
+ serial_port_out(port, SCDL, dl);
+ serial_port_out(port, SCCKS, sccks);
+ }
- smr_val |= serial_port_in(port, SCSMR) & SCSMR_CKS;
+ sci_reset(port);
uart_update_timeout(port, termios->c_cflag, baud);
- dev_dbg(port->dev, "%s: SMR %x, cks %x, t %x, SCSCR %x\n",
- __func__, smr_val, cks, t, s->cfg->scscr);
-
- if (t >= 0) {
- serial_port_out(port, SCSMR, (smr_val & ~SCSMR_CKS) | cks);
- serial_port_out(port, SCBRR, t);
- reg = sci_getreg(port, HSSRR);
- if (reg->size)
+ if (best_clk >= 0) {
+ smr_val |= cks;
+ dev_dbg(port->dev,
+ "SCR 0x%x SMR 0x%x BRR %u CKS 0x%x DL %u SRR %u\n",
+ scr_val, smr_val, brr, sccks, dl, srr);
+ serial_port_out(port, SCSCR, scr_val);
+ serial_port_out(port, SCSMR, smr_val);
+ serial_port_out(port, SCBRR, brr);
+ if (sci_getreg(port, HSSRR)->size)
serial_port_out(port, HSSRR, srr | HSCIF_SRE);
- udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */
- } else
+
+ /* Wait one bit interval */
+ udelay((1000000 + (baud - 1)) / baud);
+ } else {
+ /* Don't touch the bit rate configuration */
+ scr_val = s->cfg->scscr & (SCSCR_CKE1 | SCSCR_CKE0);
+ smr_val |= serial_port_in(port, SCSMR) & SCSMR_CKS;
+ dev_dbg(port->dev, "SCR 0x%x SMR 0x%x\n", scr_val, smr_val);
+ serial_port_out(port, SCSCR, scr_val);
serial_port_out(port, SCSMR, smr_val);
+ }
sci_init_pins(port, termios->c_cflag);
@@ -2051,7 +2229,9 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
serial_port_out(port, SCFCR, ctrl);
}
- serial_port_out(port, SCSCR, s->cfg->scscr);
+ scr_val |= s->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0);
+ dev_dbg(port->dev, "SCSCR 0x%x\n", scr_val);
+ serial_port_out(port, SCSCR, scr_val);
#ifdef CONFIG_SERIAL_SH_SCI_DMA
/*
@@ -2241,6 +2421,63 @@ static struct uart_ops sci_uart_ops = {
#endif
};
+static int sci_init_clocks(struct sci_port *sci_port, struct device *dev)
+{
+ const char *clk_names[] = {
+ [SCI_FCK] = "fck",
+ [SCI_SCK] = "sck",
+ [SCI_BRG_INT] = "brg_int",
+ [SCI_SCIF_CLK] = "scif_clk",
+ };
+ struct clk *clk;
+ unsigned int i;
+
+ if (sci_port->cfg->type == PORT_HSCIF)
+ clk_names[SCI_SCK] = "hsck";
+
+ for (i = 0; i < SCI_NUM_CLKS; i++) {
+ clk = devm_clk_get(dev, clk_names[i]);
+ if (PTR_ERR(clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ if (IS_ERR(clk) && i == SCI_FCK) {
+ /*
+ * "fck" used to be called "sci_ick", and we need to
+ * maintain DT backward compatibility.
+ */
+ clk = devm_clk_get(dev, "sci_ick");
+ if (PTR_ERR(clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ if (!IS_ERR(clk))
+ goto found;
+
+ /*
+ * Not all SH platforms declare a clock lookup entry
+ * for SCI devices, in which case we need to get the
+ * global "peripheral_clk" clock.
+ */
+ clk = devm_clk_get(dev, "peripheral_clk");
+ if (!IS_ERR(clk))
+ goto found;
+
+ dev_err(dev, "failed to get %s (%ld)\n", clk_names[i],
+ PTR_ERR(clk));
+ return PTR_ERR(clk);
+ }
+
+found:
+ if (IS_ERR(clk))
+ dev_dbg(dev, "failed to get %s (%ld)\n", clk_names[i],
+ PTR_ERR(clk));
+ else
+ dev_dbg(dev, "clk %s is %pC rate %pCr\n", clk_names[i],
+ clk, clk);
+ sci_port->clks[i] = IS_ERR(clk) ? NULL : clk;
+ }
+ return 0;
+}
+
static int sci_init_single(struct platform_device *dev,
struct sci_port *sci_port, unsigned int index,
struct plat_sci_port *p, bool early)
@@ -2333,22 +2570,9 @@ static int sci_init_single(struct platform_device *dev,
sci_port->sampling_rate = p->sampling_rate;
if (!early) {
- sci_port->iclk = clk_get(&dev->dev, "sci_ick");
- if (IS_ERR(sci_port->iclk)) {
- sci_port->iclk = clk_get(&dev->dev, "peripheral_clk");
- if (IS_ERR(sci_port->iclk)) {
- dev_err(&dev->dev, "can't get iclk\n");
- return PTR_ERR(sci_port->iclk);
- }
- }
-
- /*
- * The function clock is optional, ignore it if we can't
- * find it.
- */
- sci_port->fclk = clk_get(&dev->dev, "sci_fck");
- if (IS_ERR(sci_port->fclk))
- sci_port->fclk = NULL;
+ ret = sci_init_clocks(sci_port, &dev->dev);
+ if (ret < 0)
+ return ret;
port->dev = &dev->dev;
@@ -2405,9 +2629,6 @@ static int sci_init_single(struct platform_device *dev,
static void sci_cleanup_single(struct sci_port *port)
{
- clk_put(port->iclk);
- clk_put(port->fclk);
-
pm_runtime_disable(port->port.dev);
}
@@ -2426,7 +2647,7 @@ static void serial_console_write(struct console *co, const char *s,
{
struct sci_port *sci_port = &sci_ports[co->index];
struct uart_port *port = &sci_port->port;
- unsigned short bits, ctrl;
+ unsigned short bits, ctrl, ctrl_temp;
unsigned long flags;
int locked = 1;
@@ -2438,9 +2659,11 @@ static void serial_console_write(struct console *co, const char *s,
else
spin_lock(&port->lock);
- /* first save the SCSCR then disable the interrupts */
+ /* first save SCSCR then disable interrupts, keep clock source */
ctrl = serial_port_in(port, SCSCR);
- serial_port_out(port, SCSCR, sci_port->cfg->scscr);
+ ctrl_temp = (sci_port->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0)) |
+ (ctrl & (SCSCR_CKE1 | SCSCR_CKE0));
+ serial_port_out(port, SCSCR, ctrl_temp);
uart_console_write(port, s, count, serial_console_putchar);
@@ -2559,9 +2782,6 @@ static int sci_remove(struct platform_device *dev)
{
struct sci_port *port = platform_get_drvdata(dev);
- cpufreq_unregister_notifier(&port->freq_transition,
- CPUFREQ_TRANSITION_NOTIFIER);
-
uart_remove_one_port(&sci_uart_driver, &port->port);
sci_cleanup_single(port);
@@ -2569,42 +2789,44 @@ static int sci_remove(struct platform_device *dev)
return 0;
}
-struct sci_port_info {
- unsigned int type;
- unsigned int regtype;
-};
+
+#define SCI_OF_DATA(type, regtype) (void *)((type) << 16 | (regtype))
+#define SCI_OF_TYPE(data) ((unsigned long)(data) >> 16)
+#define SCI_OF_REGTYPE(data) ((unsigned long)(data) & 0xffff)
static const struct of_device_id of_sci_match[] = {
+ /* SoC-specific types */
+ {
+ .compatible = "renesas,scif-r7s72100",
+ .data = SCI_OF_DATA(PORT_SCIF, SCIx_SH2_SCIF_FIFODATA_REGTYPE),
+ },
+ /* Family-specific types */
+ {
+ .compatible = "renesas,rcar-gen1-scif",
+ .data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_BRG_REGTYPE),
+ }, {
+ .compatible = "renesas,rcar-gen2-scif",
+ .data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_BRG_REGTYPE),
+ }, {
+ .compatible = "renesas,rcar-gen3-scif",
+ .data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_BRG_REGTYPE),
+ },
+ /* Generic types */
{
.compatible = "renesas,scif",
- .data = &(const struct sci_port_info) {
- .type = PORT_SCIF,
- .regtype = SCIx_SH4_SCIF_REGTYPE,
- },
+ .data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_REGTYPE),
}, {
.compatible = "renesas,scifa",
- .data = &(const struct sci_port_info) {
- .type = PORT_SCIFA,
- .regtype = SCIx_SCIFA_REGTYPE,
- },
+ .data = SCI_OF_DATA(PORT_SCIFA, SCIx_SCIFA_REGTYPE),
}, {
.compatible = "renesas,scifb",
- .data = &(const struct sci_port_info) {
- .type = PORT_SCIFB,
- .regtype = SCIx_SCIFB_REGTYPE,
- },
+ .data = SCI_OF_DATA(PORT_SCIFB, SCIx_SCIFB_REGTYPE),
}, {
.compatible = "renesas,hscif",
- .data = &(const struct sci_port_info) {
- .type = PORT_HSCIF,
- .regtype = SCIx_HSCIF_REGTYPE,
- },
+ .data = SCI_OF_DATA(PORT_HSCIF, SCIx_HSCIF_REGTYPE),
}, {
.compatible = "renesas,sci",
- .data = &(const struct sci_port_info) {
- .type = PORT_SCI,
- .regtype = SCIx_SCI_REGTYPE,
- },
+ .data = SCI_OF_DATA(PORT_SCI, SCIx_SCI_REGTYPE),
}, {
/* Terminator */
},
@@ -2616,24 +2838,21 @@ sci_parse_dt(struct platform_device *pdev, unsigned int *dev_id)
{
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *match;
- const struct sci_port_info *info;
struct plat_sci_port *p;
int id;
if (!IS_ENABLED(CONFIG_OF) || !np)
return NULL;
- match = of_match_node(of_sci_match, pdev->dev.of_node);
+ match = of_match_node(of_sci_match, np);
if (!match)
return NULL;
- info = match->data;
-
p = devm_kzalloc(&pdev->dev, sizeof(struct plat_sci_port), GFP_KERNEL);
if (!p)
return NULL;
- /* Get the line number for the aliases node. */
+ /* Get the line number from the aliases node. */
id = of_alias_get_id(np, "serial");
if (id < 0) {
dev_err(&pdev->dev, "failed to get alias id (%d)\n", id);
@@ -2643,8 +2862,8 @@ sci_parse_dt(struct platform_device *pdev, unsigned int *dev_id)
*dev_id = id;
p->flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
- p->type = info->type;
- p->regtype = info->regtype;
+ p->type = SCI_OF_TYPE(match->data);
+ p->regtype = SCI_OF_REGTYPE(match->data);
p->scscr = SCSCR_RE | SCSCR_TE;
return p;
@@ -2714,16 +2933,6 @@ static int sci_probe(struct platform_device *dev)
if (ret)
return ret;
- sp->freq_transition.notifier_call = sci_notifier;
-
- ret = cpufreq_register_notifier(&sp->freq_transition,
- CPUFREQ_TRANSITION_NOTIFIER);
- if (unlikely(ret < 0)) {
- uart_remove_one_port(&sci_uart_driver, &sp->port);
- sci_cleanup_single(sp);
- return ret;
- }
-
#ifdef CONFIG_SH_STANDARD_BIOS
sh_bios_gdb_detach();
#endif
diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h
index bf69bbdcc1f9..fb1760250421 100644
--- a/drivers/tty/serial/sh-sci.h
+++ b/drivers/tty/serial/sh-sci.h
@@ -27,6 +27,8 @@ enum {
HSSRR, /* Sampling Rate Register */
SCPCR, /* Serial Port Control Register */
SCPDR, /* Serial Port Data Register */
+ SCDL, /* BRG Frequency Division Register */
+ SCCKS, /* BRG Clock Select Register */
SCIx_NR_REGS,
};
@@ -109,6 +111,14 @@ enum {
#define SCPDR_RTSD BIT(4) /* Serial Port RTS Output Pin Data */
#define SCPDR_CTSD BIT(3) /* Serial Port CTS Input Pin Data */
+/*
+ * BRG Clock Select Register (Some SCIF and HSCIF)
+ * The Baud Rate Generator for external clock can provide a clock source for
+ * the sampling clock. It outputs either its frequency divided clock, or the
+ * (undivided) (H)SCK external clock.
+ */
+#define SCCKS_CKS BIT(15) /* Select (H)SCK (1) or divided SC_CLK (0) */
+#define SCCKS_XIN BIT(14) /* SC_CLK uses bus clock (1) or SCIF_CLK (0) */
#define SCxSR_TEND(port) (((port)->type == PORT_SCI) ? SCI_TEND : SCIF_TEND)
#define SCxSR_RDxF(port) (((port)->type == PORT_SCI) ? SCI_RDRF : SCIF_RDF)
diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c
index 9dbae01d41ce..ef26c4a60be4 100644
--- a/drivers/tty/serial/sprd_serial.c
+++ b/drivers/tty/serial/sprd_serial.c
@@ -517,7 +517,7 @@ static struct uart_ops serial_sprd_ops = {
};
#ifdef CONFIG_SERIAL_SPRD_CONSOLE
-static inline void wait_for_xmitr(struct uart_port *port)
+static void wait_for_xmitr(struct uart_port *port)
{
unsigned int status, tmout = 10000;
diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c
index 064031870ba0..ca0d3802f2af 100644
--- a/drivers/tty/serial/sunhv.c
+++ b/drivers/tty/serial/sunhv.c
@@ -148,8 +148,10 @@ static int receive_chars_read(struct uart_port *port)
uart_handle_dcd_change(port, 1);
}
- for (i = 0; i < bytes_read; i++)
- uart_handle_sysrq_char(port, con_read_page[i]);
+ if (port->sysrq != 0 && *con_read_page) {
+ for (i = 0; i < bytes_read; i++)
+ uart_handle_sysrq_char(port, con_read_page[i]);
+ }
if (port->state == NULL)
continue;
@@ -168,17 +170,17 @@ struct sunhv_ops {
int (*receive_chars)(struct uart_port *port);
};
-static struct sunhv_ops bychar_ops = {
+static const struct sunhv_ops bychar_ops = {
.transmit_chars = transmit_chars_putchar,
.receive_chars = receive_chars_getchar,
};
-static struct sunhv_ops bywrite_ops = {
+static const struct sunhv_ops bywrite_ops = {
.transmit_chars = transmit_chars_write,
.receive_chars = receive_chars_read,
};
-static struct sunhv_ops *sunhv_ops = &bychar_ops;
+static const struct sunhv_ops *sunhv_ops = &bychar_ops;
static struct tty_port *receive_chars(struct uart_port *port)
{
diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c
index e124d2e88996..9ad98eaa35bf 100644
--- a/drivers/tty/serial/sunsu.c
+++ b/drivers/tty/serial/sunsu.c
@@ -1262,7 +1262,7 @@ static int sunsu_kbd_ms_init(struct uart_sunsu_port *up)
/*
* Wait for transmitter & holding register to empty
*/
-static __inline__ void wait_for_xmitr(struct uart_sunsu_port *up)
+static void wait_for_xmitr(struct uart_sunsu_port *up)
{
unsigned int status, tmout = 10000;
diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
index 4079ec56f5f9..b384060e3b1f 100644
--- a/drivers/tty/serial/vt8500_serial.c
+++ b/drivers/tty/serial/vt8500_serial.c
@@ -485,7 +485,7 @@ static struct uart_driver vt8500_uart_driver;
#ifdef CONFIG_SERIAL_VT8500_CONSOLE
-static inline void wait_for_xmitr(struct uart_port *port)
+static void wait_for_xmitr(struct uart_port *port)
{
unsigned int status, tmout = 10000;
diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c
index 6fc39fbfc275..5505ea842179 100644
--- a/drivers/tty/synclink_gt.c
+++ b/drivers/tty/synclink_gt.c
@@ -89,7 +89,7 @@
* module identification
*/
static char *driver_name = "SyncLink GT";
-static char *tty_driver_name = "synclink_gt";
+static char *slgt_driver_name = "synclink_gt";
static char *tty_dev_prefix = "ttySLG";
MODULE_LICENSE("GPL");
#define MGSL_MAGIC 0x5401
@@ -3799,7 +3799,7 @@ static int __init slgt_init(void)
/* Initialize the tty_driver structure */
- serial_driver->driver_name = tty_driver_name;
+ serial_driver->driver_name = slgt_driver_name;
serial_driver->name = tty_dev_prefix;
serial_driver->major = ttymajor;
serial_driver->minor_start = 64;
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index 5381a728d23e..e5139402e7f8 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -133,6 +133,12 @@ static void sysrq_handle_crash(int key)
{
char *killer = NULL;
+ /* we need to release the RCU read lock here,
+ * otherwise we get an annoying
+ * 'BUG: sleeping function called from invalid context'
+ * complaint from the kernel before the panic.
+ */
+ rcu_read_unlock();
panic_on_oops = 1; /* force panic */
wmb();
*killer = 1;
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
index 9a479e61791a..3cd31e0d4bd9 100644
--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -450,7 +450,7 @@ receive_buf(struct tty_struct *tty, struct tty_buffer *head, int count)
count = disc->ops->receive_buf2(tty, p, f, count);
else {
count = min_t(int, count, tty->receive_room);
- if (count)
+ if (count && disc->ops->receive_buf)
disc->ops->receive_buf(tty, p, f, count);
}
return count;
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index bcc8e1e8bb72..892c92354745 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -256,19 +256,24 @@ const char *tty_name(const struct tty_struct *tty)
EXPORT_SYMBOL(tty_name);
-int tty_paranoia_check(struct tty_struct *tty, struct inode *inode,
+const char *tty_driver_name(const struct tty_struct *tty)
+{
+ if (!tty || !tty->driver)
+ return "";
+ return tty->driver->name;
+}
+
+static int tty_paranoia_check(struct tty_struct *tty, struct inode *inode,
const char *routine)
{
#ifdef TTY_PARANOIA_CHECK
if (!tty) {
- printk(KERN_WARNING
- "null TTY for (%d:%d) in %s\n",
+ pr_warn("(%d:%d): %s: NULL tty\n",
imajor(inode), iminor(inode), routine);
return 1;
}
if (tty->magic != TTY_MAGIC) {
- printk(KERN_WARNING
- "bad magic number for tty struct (%d:%d) in %s\n",
+ pr_warn("(%d:%d): %s: bad magic number\n",
imajor(inode), iminor(inode), routine);
return 1;
}
@@ -293,9 +298,8 @@ static int check_tty_count(struct tty_struct *tty, const char *routine)
tty->link && tty->link->count)
count++;
if (tty->count != count) {
- printk(KERN_WARNING "Warning: dev (%s) tty->count(%d) "
- "!= #fd's(%d) in %s\n",
- tty->name, tty->count, count, routine);
+ tty_warn(tty, "%s: tty->count(%d) != #fd's(%d)\n",
+ routine, tty->count, count);
return count;
}
#endif
@@ -420,10 +424,8 @@ int __tty_check_change(struct tty_struct *tty, int sig)
}
rcu_read_unlock();
- if (!tty_pgrp) {
- pr_warn("%s: tty_check_change: sig=%d, tty->pgrp == NULL!\n",
- tty_name(tty), sig);
- }
+ if (!tty_pgrp)
+ tty_warn(tty, "sig=%d, tty->pgrp == NULL!\n", sig);
return ret;
}
@@ -781,7 +783,7 @@ static void do_tty_hangup(struct work_struct *work)
void tty_hangup(struct tty_struct *tty)
{
- tty_debug_hangup(tty, "\n");
+ tty_debug_hangup(tty, "hangup\n");
schedule_work(&tty->hangup_work);
}
@@ -798,7 +800,7 @@ EXPORT_SYMBOL(tty_hangup);
void tty_vhangup(struct tty_struct *tty)
{
- tty_debug_hangup(tty, "\n");
+ tty_debug_hangup(tty, "vhangup\n");
__tty_hangup(tty, 0);
}
@@ -835,7 +837,7 @@ void tty_vhangup_self(void)
static void tty_vhangup_session(struct tty_struct *tty)
{
- tty_debug_hangup(tty, "\n");
+ tty_debug_hangup(tty, "session hangup\n");
__tty_hangup(tty, 1);
}
@@ -1239,8 +1241,7 @@ static ssize_t tty_write(struct file *file, const char __user *buf,
return -EIO;
/* Short term debug to catch buggy drivers */
if (tty->ops->write_room == NULL)
- printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
- tty->driver->name);
+ tty_err(tty, "missing write_room method\n");
ld = tty_ldisc_ref_wait(tty);
if (!ld->ops->write)
ret = -EIO;
@@ -1561,8 +1562,8 @@ err_module_put:
/* call the tty release_tty routine to clean out this slot */
err_release_tty:
tty_unlock(tty);
- printk_ratelimited(KERN_INFO "tty_init_dev: ldisc open failed, "
- "clearing slot %d\n", idx);
+ tty_info_ratelimited(tty, "ldisc open failed (%d), clearing slot %d\n",
+ retval, idx);
release_tty(tty, idx);
return ERR_PTR(retval);
}
@@ -1580,10 +1581,8 @@ void tty_free_termios(struct tty_struct *tty)
tp = tty->driver->termios[idx];
if (tp == NULL) {
tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL);
- if (tp == NULL) {
- pr_warn("tty: no memory to save termios state.\n");
+ if (tp == NULL)
return;
- }
tty->driver->termios[idx] = tp;
}
*tp = tty->termios;
@@ -1788,7 +1787,7 @@ int tty_release(struct inode *inode, struct file *filp)
return 0;
}
- tty_debug_hangup(tty, "(tty count=%d)...\n", tty->count);
+ tty_debug_hangup(tty, "releasing (count=%d)\n", tty->count);
if (tty->ops->close)
tty->ops->close(tty, filp);
@@ -1837,8 +1836,7 @@ int tty_release(struct inode *inode, struct file *filp)
if (once) {
once = 0;
- printk(KERN_WARNING "%s: %s: read/write wait queue active!\n",
- __func__, tty_name(tty));
+ tty_warn(tty, "read/write wait queue active!\n");
}
schedule_timeout_killable(timeout);
if (timeout < 120 * HZ)
@@ -1849,14 +1847,12 @@ int tty_release(struct inode *inode, struct file *filp)
if (o_tty) {
if (--o_tty->count < 0) {
- printk(KERN_WARNING "%s: bad pty slave count (%d) for %s\n",
- __func__, o_tty->count, tty_name(o_tty));
+ tty_warn(tty, "bad slave count (%d)\n", o_tty->count);
o_tty->count = 0;
}
}
if (--tty->count < 0) {
- printk(KERN_WARNING "%s: bad tty->count (%d) for %s\n",
- __func__, tty->count, tty_name(tty));
+ tty_warn(tty, "bad tty->count (%d)\n", tty->count);
tty->count = 0;
}
@@ -1907,7 +1903,7 @@ int tty_release(struct inode *inode, struct file *filp)
/* Wait for pending work before tty destruction commmences */
tty_flush_works(tty);
- tty_debug_hangup(tty, "freeing structure...\n");
+ tty_debug_hangup(tty, "freeing structure\n");
/*
* The release_tty function takes care of the details of clearing
* the slots and preserving the termios structure. The tty_unlock_pair
@@ -2097,7 +2093,7 @@ retry_open:
tty->driver->subtype == PTY_TYPE_MASTER)
noctty = 1;
- tty_debug_hangup(tty, "(tty count=%d)\n", tty->count);
+ tty_debug_hangup(tty, "opening (count=%d)\n", tty->count);
if (tty->ops->open)
retval = tty->ops->open(tty, filp);
@@ -2106,7 +2102,7 @@ retry_open:
filp->f_flags = saved_flags;
if (retval) {
- tty_debug_hangup(tty, "error %d, releasing...\n", retval);
+ tty_debug_hangup(tty, "open error %d, releasing\n", retval);
tty_unlock(tty); /* need to call tty_release without BTM */
tty_release(inode, filp);
@@ -2870,7 +2866,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
no_tty();
return 0;
case TIOCSCTTY:
- return tiocsctty(tty, file, arg);
+ return tiocsctty(real_tty, file, arg);
case TIOCGPGRP:
return tiocgpgrp(tty, real_tty, p);
case TIOCSPGRP:
@@ -3028,28 +3024,24 @@ void __do_SAK(struct tty_struct *tty)
read_lock(&tasklist_lock);
/* Kill the entire session */
do_each_pid_task(session, PIDTYPE_SID, p) {
- printk(KERN_NOTICE "SAK: killed process %d"
- " (%s): task_session(p)==tty->session\n",
- task_pid_nr(p), p->comm);
+ tty_notice(tty, "SAK: killed process %d (%s): by session\n",
+ task_pid_nr(p), p->comm);
send_sig(SIGKILL, p, 1);
} while_each_pid_task(session, PIDTYPE_SID, p);
- /* Now kill any processes that happen to have the
- * tty open.
- */
+
+ /* Now kill any processes that happen to have the tty open */
do_each_thread(g, p) {
if (p->signal->tty == tty) {
- printk(KERN_NOTICE "SAK: killed process %d"
- " (%s): task_session(p)==tty->session\n",
- task_pid_nr(p), p->comm);
+ tty_notice(tty, "SAK: killed process %d (%s): by controlling tty\n",
+ task_pid_nr(p), p->comm);
send_sig(SIGKILL, p, 1);
continue;
}
task_lock(p);
i = iterate_fd(p->files, 0, this_tty, tty);
if (i != 0) {
- printk(KERN_NOTICE "SAK: killed process %d"
- " (%s): fd#%d opened to the tty\n",
- task_pid_nr(p), p->comm, i - 1);
+ tty_notice(tty, "SAK: killed process %d (%s): by fd#%d\n",
+ task_pid_nr(p), p->comm, i - 1);
force_sig(SIGKILL, p);
}
task_unlock(p);
@@ -3219,7 +3211,7 @@ EXPORT_SYMBOL(tty_register_device);
static void tty_device_create_release(struct device *dev)
{
- pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
+ dev_dbg(dev, "releasing...\n");
kfree(dev);
}
@@ -3255,8 +3247,8 @@ struct device *tty_register_device_attr(struct tty_driver *driver,
bool cdev = false;
if (index >= driver->num) {
- printk(KERN_ERR "Attempt to register invalid tty line number "
- " (%d).\n", index);
+ pr_err("%s: Attempt to register invalid tty line number (%d)\n",
+ driver->name, index);
return ERR_PTR(-EINVAL);
}
diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c
index 1445dd39aa62..0ea351388724 100644
--- a/drivers/tty/tty_ioctl.c
+++ b/drivers/tty/tty_ioctl.c
@@ -216,7 +216,7 @@ int tty_unthrottle_safe(struct tty_struct *tty)
void tty_wait_until_sent(struct tty_struct *tty, long timeout)
{
- tty_debug_wait_until_sent(tty, "\n");
+ tty_debug_wait_until_sent(tty, "wait until sent, timeout=%ld\n", timeout);
if (!timeout)
timeout = MAX_SCHEDULE_TIMEOUT;
@@ -239,19 +239,14 @@ EXPORT_SYMBOL(tty_wait_until_sent);
* Termios Helper Methods
*/
-static void unset_locked_termios(struct ktermios *termios,
- struct ktermios *old,
- struct ktermios *locked)
+static void unset_locked_termios(struct tty_struct *tty, struct ktermios *old)
{
+ struct ktermios *termios = &tty->termios;
+ struct ktermios *locked = &tty->termios_locked;
int i;
#define NOSET_MASK(x, y, z) (x = ((x) & ~(z)) | ((y) & (z)))
- if (!locked) {
- printk(KERN_WARNING "Warning?!? termios_locked is NULL.\n");
- return;
- }
-
NOSET_MASK(termios->c_iflag, old->c_iflag, locked->c_iflag);
NOSET_MASK(termios->c_oflag, old->c_oflag, locked->c_oflag);
NOSET_MASK(termios->c_cflag, old->c_cflag, locked->c_cflag);
@@ -463,10 +458,8 @@ void tty_termios_encode_baud_rate(struct ktermios *termios,
if (ifound == -1 && (ibaud != obaud || ibinput))
termios->c_cflag |= (BOTHER << IBSHIFT);
#else
- if (ifound == -1 || ofound == -1) {
- printk_once(KERN_WARNING "tty: Unable to return correct "
- "speed data as your architecture needs updating.\n");
- }
+ if (ifound == -1 || ofound == -1)
+ pr_warn_once("tty: Unable to return correct speed data as your architecture needs updating.\n");
#endif
}
EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate);
@@ -556,7 +549,7 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
down_write(&tty->termios_rwsem);
old_termios = tty->termios;
tty->termios = *new_termios;
- unset_locked_termios(&tty->termios, &old_termios, &tty->termios_locked);
+ unset_locked_termios(tty, &old_termios);
if (tty->ops->set_termios)
tty->ops->set_termios(tty, &old_termios);
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index 629e3c865072..a054d03c22e7 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -185,7 +185,7 @@ static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc)
*
* Complement of tty_ldisc_get().
*/
-static inline void tty_ldisc_put(struct tty_ldisc *ld)
+static void tty_ldisc_put(struct tty_ldisc *ld)
{
if (WARN_ON_ONCE(!ld))
return;
@@ -417,6 +417,10 @@ EXPORT_SYMBOL_GPL(tty_ldisc_flush);
* they are not on hot paths so a little discipline won't do
* any harm.
*
+ * The line discipline-related tty_struct fields are reset to
+ * prevent the ldisc driver from re-using stale information for
+ * the new ldisc instance.
+ *
* Locking: takes termios_rwsem
*/
@@ -425,6 +429,9 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
down_write(&tty->termios_rwsem);
tty->termios.c_line = num;
up_write(&tty->termios_rwsem);
+
+ tty->disc_data = NULL;
+ tty->receive_room = 0;
}
/**
@@ -529,34 +536,21 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
tty_lock(tty);
retval = tty_ldisc_lock(tty, 5 * HZ);
- if (retval) {
- tty_ldisc_put(new_ldisc);
- tty_unlock(tty);
- return retval;
- }
+ if (retval)
+ goto err;
- /*
- * Check the no-op case
- */
+ /* Check the no-op case */
+ if (tty->ldisc->ops->num == ldisc)
+ goto out;
- if (tty->ldisc->ops->num == ldisc) {
- tty_ldisc_unlock(tty);
- tty_ldisc_put(new_ldisc);
- tty_unlock(tty);
- return 0;
+ if (test_bit(TTY_HUPPED, &tty->flags)) {
+ /* We were raced by hangup */
+ retval = -EIO;
+ goto out;
}
old_ldisc = tty->ldisc;
- if (test_bit(TTY_HUPPED, &tty->flags)) {
- /* We were raced by the hangup method. It will have stomped
- the ldisc data and closed the ldisc down */
- tty_ldisc_unlock(tty);
- tty_ldisc_put(new_ldisc);
- tty_unlock(tty);
- return -EIO;
- }
-
/* Shutdown the old discipline. */
tty_ldisc_close(tty, old_ldisc);
@@ -582,18 +576,15 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
the old ldisc (if it was restored as part of error cleanup
above). In either case, releasing a single reference from
the old ldisc is correct. */
-
- tty_ldisc_put(old_ldisc);
-
- /*
- * Allow ldisc referencing to occur again
- */
+ new_ldisc = old_ldisc;
+out:
tty_ldisc_unlock(tty);
/* Restart the work queue in case no characters kick it off. Safe if
already running */
tty_buffer_restart_work(tty->port);
-
+err:
+ tty_ldisc_put(new_ldisc); /* drop the extra reference */
tty_unlock(tty);
return retval;
}
diff --git a/drivers/tty/tty_ldsem.c b/drivers/tty/tty_ldsem.c
index ad7eba5ca380..1bf8ed13f827 100644
--- a/drivers/tty/tty_ldsem.c
+++ b/drivers/tty/tty_ldsem.c
@@ -319,7 +319,7 @@ down_write_failed(struct ld_semaphore *sem, long count, long timeout)
-static inline int __ldsem_down_read_nested(struct ld_semaphore *sem,
+static int __ldsem_down_read_nested(struct ld_semaphore *sem,
int subclass, long timeout)
{
long count;
@@ -338,7 +338,7 @@ static inline int __ldsem_down_read_nested(struct ld_semaphore *sem,
return 1;
}
-static inline int __ldsem_down_write_nested(struct ld_semaphore *sem,
+static int __ldsem_down_write_nested(struct ld_semaphore *sem,
int subclass, long timeout)
{
long count;
diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c
index 0efcf713b756..77703a391207 100644
--- a/drivers/tty/tty_mutex.c
+++ b/drivers/tty/tty_mutex.c
@@ -12,11 +12,8 @@
void __lockfunc tty_lock(struct tty_struct *tty)
{
- if (tty->magic != TTY_MAGIC) {
- pr_err("L Bad %p\n", tty);
- WARN_ON(1);
+ if (WARN(tty->magic != TTY_MAGIC, "L Bad %p\n", tty))
return;
- }
tty_kref_get(tty);
mutex_lock(&tty->legacy_mutex);
}
@@ -24,11 +21,8 @@ EXPORT_SYMBOL(tty_lock);
void __lockfunc tty_unlock(struct tty_struct *tty)
{
- if (tty->magic != TTY_MAGIC) {
- pr_err("U Bad %p\n", tty);
- WARN_ON(1);
+ if (WARN(tty->magic != TTY_MAGIC, "U Bad %p\n", tty))
return;
- }
mutex_unlock(&tty->legacy_mutex);
tty_kref_put(tty);
}
diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c
index 482f33f20043..846ed481c24f 100644
--- a/drivers/tty/tty_port.c
+++ b/drivers/tty/tty_port.c
@@ -462,14 +462,13 @@ int tty_port_close_start(struct tty_port *port,
spin_lock_irqsave(&port->lock, flags);
if (tty->count == 1 && port->count != 1) {
- printk(KERN_WARNING
- "tty_port_close_start: tty->count = 1 port count = %d.\n",
- port->count);
+ tty_warn(tty, "%s: tty->count = 1 port count = %d\n", __func__,
+ port->count);
port->count = 1;
}
if (--port->count < 0) {
- printk(KERN_WARNING "tty_port_close_start: count = %d\n",
- port->count);
+ tty_warn(tty, "%s: bad port count (%d)\n", __func__,
+ port->count);
port->count = 0;
}
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 4462d167900c..e7cbc44eef57 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -634,7 +634,7 @@ static void set_origin(struct vc_data *vc)
vc->vc_pos = vc->vc_origin + vc->vc_size_row * vc->vc_y + 2 * vc->vc_x;
}
-static inline void save_screen(struct vc_data *vc)
+static void save_screen(struct vc_data *vc)
{
WARN_CONSOLE_UNLOCKED();
diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig
index 5619b8ca3bf3..3644a3500b70 100644
--- a/drivers/usb/chipidea/Kconfig
+++ b/drivers/usb/chipidea/Kconfig
@@ -37,9 +37,4 @@ config USB_CHIPIDEA_HOST
Say Y here to enable host controller functionality of the
ChipIdea driver.
-config USB_CHIPIDEA_DEBUG
- bool "ChipIdea driver debug"
- help
- Say Y here to enable debugging output of the ChipIdea driver.
-
endif
diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile
index 4decb12f2578..518e445476c3 100644
--- a/drivers/usb/chipidea/Makefile
+++ b/drivers/usb/chipidea/Makefile
@@ -1,11 +1,8 @@
-ccflags-$(CONFIG_USB_CHIPIDEA_DEBUG) := -DDEBUG
-
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc.o
-ci_hdrc-y := core.o otg.o
+ci_hdrc-y := core.o otg.o debug.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o
ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o
-ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o
ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o
# Glue/Bridge layers go here
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index 41d7cf6d63ba..cd414559040f 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -433,4 +433,7 @@ int hw_wait_reg(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask,
void ci_platform_configure(struct ci_hdrc *ci);
+int dbg_create_files(struct ci_hdrc *ci);
+
+void dbg_remove_files(struct ci_hdrc *ci);
#endif /* __DRIVERS_USB_CHIPIDEA_CI_H */
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index 5a048b7b92e8..f14f4ab47ebb 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -345,6 +345,11 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
return 0;
}
+static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
+{
+ ci_hdrc_imx_remove(pdev);
+}
+
#ifdef CONFIG_PM
static int imx_controller_suspend(struct device *dev)
{
@@ -462,6 +467,7 @@ static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
static struct platform_driver ci_hdrc_imx_driver = {
.probe = ci_hdrc_imx_probe,
.remove = ci_hdrc_imx_remove,
+ .shutdown = ci_hdrc_imx_shutdown,
.driver = {
.name = "imx_usb",
.of_match_table = ci_hdrc_imx_dt_ids,
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c
index d79ecc08a1be..3889809fd0c4 100644
--- a/drivers/usb/chipidea/ci_hdrc_msm.c
+++ b/drivers/usb/chipidea/ci_hdrc_msm.c
@@ -25,7 +25,8 @@ static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
case CI_HDRC_CONTROLLER_RESET_EVENT:
dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n");
writel(0, USB_AHBBURST);
- writel(0, USB_AHBMODE);
+ /* use AHB transactor, allow posted data writes */
+ writel(0x8, USB_AHBMODE);
usb_phy_init(ci->usb_phy);
break;
case CI_HDRC_CONTROLLER_STOPPED_EVENT:
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 965d0e240dcb..7404064b9bbc 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -23,7 +23,6 @@
* - BUS: bus glue code, bus abstraction layer
*
* Compile Options
- * - CONFIG_USB_CHIPIDEA_DEBUG: enable debug facilities
* - STALL_IN: non-empty bulk-in pipes cannot be halted
* if defined mass storage compliance succeeds but with warnings
* => case 4: Hi > Dn
@@ -71,7 +70,6 @@
#include "udc.h"
#include "bits.h"
#include "host.h"
-#include "debug.h"
#include "otg.h"
#include "otg_fsm.h"
@@ -688,52 +686,39 @@ static int ci_get_platdata(struct device *dev,
if (usb_get_maximum_speed(dev) == USB_SPEED_FULL)
platdata->flags |= CI_HDRC_FORCE_FULLSPEED;
- if (of_find_property(dev->of_node, "phy-clkgate-delay-us", NULL))
- of_property_read_u32(dev->of_node, "phy-clkgate-delay-us",
+ of_property_read_u32(dev->of_node, "phy-clkgate-delay-us",
&platdata->phy_clkgate_delay_us);
platdata->itc_setting = 1;
- if (of_find_property(dev->of_node, "itc-setting", NULL)) {
- ret = of_property_read_u32(dev->of_node, "itc-setting",
- &platdata->itc_setting);
- if (ret) {
- dev_err(dev,
- "failed to get itc-setting\n");
- return ret;
- }
- }
- if (of_find_property(dev->of_node, "ahb-burst-config", NULL)) {
- ret = of_property_read_u32(dev->of_node, "ahb-burst-config",
- &platdata->ahb_burst_config);
- if (ret) {
- dev_err(dev,
- "failed to get ahb-burst-config\n");
- return ret;
- }
+ of_property_read_u32(dev->of_node, "itc-setting",
+ &platdata->itc_setting);
+
+ ret = of_property_read_u32(dev->of_node, "ahb-burst-config",
+ &platdata->ahb_burst_config);
+ if (!ret) {
platdata->flags |= CI_HDRC_OVERRIDE_AHB_BURST;
+ } else if (ret != -EINVAL) {
+ dev_err(dev, "failed to get ahb-burst-config\n");
+ return ret;
}
- if (of_find_property(dev->of_node, "tx-burst-size-dword", NULL)) {
- ret = of_property_read_u32(dev->of_node, "tx-burst-size-dword",
- &platdata->tx_burst_size);
- if (ret) {
- dev_err(dev,
- "failed to get tx-burst-size-dword\n");
- return ret;
- }
+ ret = of_property_read_u32(dev->of_node, "tx-burst-size-dword",
+ &platdata->tx_burst_size);
+ if (!ret) {
platdata->flags |= CI_HDRC_OVERRIDE_TX_BURST;
+ } else if (ret != -EINVAL) {
+ dev_err(dev, "failed to get tx-burst-size-dword\n");
+ return ret;
}
- if (of_find_property(dev->of_node, "rx-burst-size-dword", NULL)) {
- ret = of_property_read_u32(dev->of_node, "rx-burst-size-dword",
- &platdata->rx_burst_size);
- if (ret) {
- dev_err(dev,
- "failed to get rx-burst-size-dword\n");
- return ret;
- }
+ ret = of_property_read_u32(dev->of_node, "rx-burst-size-dword",
+ &platdata->rx_burst_size);
+ if (!ret) {
platdata->flags |= CI_HDRC_OVERRIDE_RX_BURST;
+ } else if (ret != -EINVAL) {
+ dev_err(dev, "failed to get rx-burst-size-dword\n");
+ return ret;
}
ext_id = ERR_PTR(-ENODEV);
diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c
index 58c8485a0715..a4f7db2e18dd 100644
--- a/drivers/usb/chipidea/debug.c
+++ b/drivers/usb/chipidea/debug.c
@@ -15,7 +15,6 @@
#include "ci.h"
#include "udc.h"
#include "bits.h"
-#include "debug.h"
#include "otg.h"
/**
diff --git a/drivers/usb/chipidea/debug.h b/drivers/usb/chipidea/debug.h
deleted file mode 100644
index e16478c4a943..000000000000
--- a/drivers/usb/chipidea/debug.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * debug.h - ChipIdea USB driver debug interfaces
- *
- * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
- *
- * Author: David Lopo
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef __DRIVERS_USB_CHIPIDEA_DEBUG_H
-#define __DRIVERS_USB_CHIPIDEA_DEBUG_H
-
-#ifdef CONFIG_USB_CHIPIDEA_DEBUG
-int dbg_create_files(struct ci_hdrc *ci);
-void dbg_remove_files(struct ci_hdrc *ci);
-#else
-static inline int dbg_create_files(struct ci_hdrc *ci)
-{
- return 0;
-}
-
-static inline void dbg_remove_files(struct ci_hdrc *ci)
-{
-}
-#endif
-
-#endif /* __DRIVERS_USB_CHIPIDEA_DEBUG_H */
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c
index 3d24304405b3..053bac9d983c 100644
--- a/drivers/usb/chipidea/host.c
+++ b/drivers/usb/chipidea/host.c
@@ -190,6 +190,8 @@ static void host_stop(struct ci_hdrc *ci)
(ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON))
regulator_disable(ci->platdata->reg_vbus);
}
+ ci->hcd = NULL;
+ ci->otg.host = NULL;
}
diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c
index 00ab59d45da1..ba90dc66703d 100644
--- a/drivers/usb/chipidea/otg_fsm.c
+++ b/drivers/usb/chipidea/otg_fsm.c
@@ -485,20 +485,30 @@ static void ci_otg_loc_conn(struct otg_fsm *fsm, int on)
/*
* Generate SOF by host.
- * This is controlled through suspend/resume the port.
* In host mode, controller will automatically send SOF.
* Suspend will block the data on the port.
+ *
+ * This is controlled through usbcore by usb autosuspend,
+ * so the usb device class driver need support autosuspend,
+ * otherwise the bus suspend will not happen.
*/
static void ci_otg_loc_sof(struct otg_fsm *fsm, int on)
{
- struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
+ struct usb_device *udev;
- if (on)
- hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_FPR,
- PORTSC_FPR);
- else
- hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_SUSP,
- PORTSC_SUSP);
+ if (!fsm->otg->host)
+ return;
+
+ udev = usb_hub_find_child(fsm->otg->host->root_hub, 1);
+ if (!udev)
+ return;
+
+ if (on) {
+ usb_disable_autosuspend(udev);
+ } else {
+ pm_runtime_set_autosuspend_delay(&udev->dev, 0);
+ usb_enable_autosuspend(udev);
+ }
}
/*
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 391a1225b0ba..3eafa2c9a2ba 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -26,7 +26,6 @@
#include "ci.h"
#include "udc.h"
#include "bits.h"
-#include "debug.h"
#include "otg.h"
#include "otg_fsm.h"
@@ -349,14 +348,13 @@ static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq,
if (node == NULL)
return -ENOMEM;
- node->ptr = dma_pool_alloc(hwep->td_pool, GFP_ATOMIC,
+ node->ptr = dma_pool_zalloc(hwep->td_pool, GFP_ATOMIC,
&node->dma);
if (node->ptr == NULL) {
kfree(node);
return -ENOMEM;
}
- memset(node->ptr, 0, sizeof(struct ci_hw_td));
node->ptr->token = cpu_to_le32(length << __ffs(TD_TOTAL_BYTES));
node->ptr->token &= cpu_to_le32(TD_TOTAL_BYTES);
node->ptr->token |= cpu_to_le32(TD_STATUS_ACTIVE);
@@ -404,9 +402,9 @@ static inline u8 _usb_addr(struct ci_hw_ep *ep)
}
/**
- * _hardware_queue: configures a request at hardware level
- * @gadget: gadget
+ * _hardware_enqueue: configures a request at hardware level
* @hwep: endpoint
+ * @hwreq: request
*
* This function returns an error code
*/
@@ -435,19 +433,28 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
if (hwreq->req.dma % PAGE_SIZE)
pages--;
- if (rest == 0)
- add_td_to_list(hwep, hwreq, 0);
+ if (rest == 0) {
+ ret = add_td_to_list(hwep, hwreq, 0);
+ if (ret < 0)
+ goto done;
+ }
while (rest > 0) {
unsigned count = min(hwreq->req.length - hwreq->req.actual,
(unsigned)(pages * CI_HDRC_PAGE_SIZE));
- add_td_to_list(hwep, hwreq, count);
+ ret = add_td_to_list(hwep, hwreq, count);
+ if (ret < 0)
+ goto done;
+
rest -= count;
}
if (hwreq->req.zero && hwreq->req.length && hwep->dir == TX
- && (hwreq->req.length % hwep->ep.maxpacket == 0))
- add_td_to_list(hwep, hwreq, 0);
+ && (hwreq->req.length % hwep->ep.maxpacket == 0)) {
+ ret = add_td_to_list(hwep, hwreq, 0);
+ if (ret < 0)
+ goto done;
+ }
firstnode = list_first_entry(&hwreq->tds, struct td_node, td);
@@ -788,8 +795,12 @@ static void isr_get_status_complete(struct usb_ep *ep, struct usb_request *req)
/**
* _ep_queue: queues (submits) an I/O request to an endpoint
+ * @ep: endpoint
+ * @req: request
+ * @gfp_flags: GFP flags (not used)
*
* Caller must hold lock
+ * This function returns an error code
*/
static int _ep_queue(struct usb_ep *ep, struct usb_request *req,
gfp_t __maybe_unused gfp_flags)
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index b30e7423549b..26ca4f910cb0 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1838,6 +1838,11 @@ static const struct usb_device_id acm_ids[] = {
},
#endif
+ /* Exclude Infineon Flash Loader utility */
+ { USB_DEVICE(0x058b, 0x0041),
+ .driver_info = IGNORE_DEVICE,
+ },
+
/* control interfaces without any protocol set */
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
USB_CDC_PROTO_NONE) },
diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
index 673d53038ed2..e6ec125e4485 100644
--- a/drivers/usb/common/common.c
+++ b/drivers/usb/common/common.c
@@ -17,6 +17,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/of.h>
#include <linux/usb/otg.h>
+#include <linux/of_platform.h>
const char *usb_otg_state_string(enum usb_otg_state state)
{
@@ -106,25 +107,72 @@ static const char *const usb_dr_modes[] = {
[USB_DR_MODE_OTG] = "otg",
};
+static enum usb_dr_mode usb_get_dr_mode_from_string(const char *str)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(usb_dr_modes); i++)
+ if (!strcmp(usb_dr_modes[i], str))
+ return i;
+
+ return USB_DR_MODE_UNKNOWN;
+}
+
enum usb_dr_mode usb_get_dr_mode(struct device *dev)
{
const char *dr_mode;
- int err, i;
+ int err;
err = device_property_read_string(dev, "dr_mode", &dr_mode);
if (err < 0)
return USB_DR_MODE_UNKNOWN;
- for (i = 0; i < ARRAY_SIZE(usb_dr_modes); i++)
- if (!strcmp(dr_mode, usb_dr_modes[i]))
- return i;
-
- return USB_DR_MODE_UNKNOWN;
+ return usb_get_dr_mode_from_string(dr_mode);
}
EXPORT_SYMBOL_GPL(usb_get_dr_mode);
#ifdef CONFIG_OF
/**
+ * of_usb_get_dr_mode_by_phy - Get dual role mode for the controller device
+ * which is associated with the given phy device_node
+ * @np: Pointer to the given phy device_node
+ *
+ * In dts a usb controller associates with phy devices. The function gets
+ * the string from property 'dr_mode' of the controller associated with the
+ * given phy device node, and returns the correspondig enum usb_dr_mode.
+ */
+enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *phy_np)
+{
+ struct device_node *controller = NULL;
+ struct device_node *phy;
+ const char *dr_mode;
+ int index;
+ int err;
+
+ do {
+ controller = of_find_node_with_property(controller, "phys");
+ index = 0;
+ do {
+ phy = of_parse_phandle(controller, "phys", index);
+ of_node_put(phy);
+ if (phy == phy_np)
+ goto finish;
+ index++;
+ } while (phy);
+ } while (controller);
+
+finish:
+ err = of_property_read_string(controller, "dr_mode", &dr_mode);
+ of_node_put(controller);
+
+ if (err < 0)
+ return USB_DR_MODE_UNKNOWN;
+
+ return usb_get_dr_mode_from_string(dr_mode);
+}
+EXPORT_SYMBOL_GPL(of_usb_get_dr_mode_by_phy);
+
+/**
* of_usb_host_tpl_support - to get if Targeted Peripheral List is supported
* for given targeted hosts (non-PC hosts)
* @np: Pointer to the given device_node
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 7caff020106e..5050760f5e17 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -115,7 +115,8 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
USB_SS_MULT(desc->bmAttributes) > 3) {
dev_warn(ddev, "Isoc endpoint has Mult of %d in "
"config %d interface %d altsetting %d ep %d: "
- "setting to 3\n", desc->bmAttributes + 1,
+ "setting to 3\n",
+ USB_SS_MULT(desc->bmAttributes),
cfgno, inum, asnum, ep->desc.bEndpointAddress);
ep->ss_ep_comp.bmAttributes = 2;
}
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index 2a3bbdf7eb94..cffa0a0d7de2 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -661,32 +661,8 @@ static unsigned int usb_device_poll(struct file *file,
return 0;
}
-static loff_t usb_device_lseek(struct file *file, loff_t offset, int orig)
-{
- loff_t ret;
-
- mutex_lock(&file_inode(file)->i_mutex);
-
- switch (orig) {
- case 0:
- file->f_pos = offset;
- ret = file->f_pos;
- break;
- case 1:
- file->f_pos += offset;
- ret = file->f_pos;
- break;
- case 2:
- default:
- ret = -EINVAL;
- }
-
- mutex_unlock(&file_inode(file)->i_mutex);
- return ret;
-}
-
const struct file_operations usbfs_devices_fops = {
- .llseek = usb_device_lseek,
+ .llseek = no_seek_end_llseek,
.read = usb_device_read,
.poll = usb_device_poll,
};
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 38ae877c46e3..59e7a3369084 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -100,6 +100,11 @@ static bool usbfs_snoop;
module_param(usbfs_snoop, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(usbfs_snoop, "true to log all usbfs traffic");
+static unsigned usbfs_snoop_max = 65536;
+module_param(usbfs_snoop_max, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(usbfs_snoop_max,
+ "maximum number of bytes to print while snooping");
+
#define snoop(dev, format, arg...) \
do { \
if (usbfs_snoop) \
@@ -157,30 +162,6 @@ static int connected(struct usb_dev_state *ps)
ps->dev->state != USB_STATE_NOTATTACHED);
}
-static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
-{
- loff_t ret;
-
- mutex_lock(&file_inode(file)->i_mutex);
-
- switch (orig) {
- case 0:
- file->f_pos = offset;
- ret = file->f_pos;
- break;
- case 1:
- file->f_pos += offset;
- ret = file->f_pos;
- break;
- case 2:
- default:
- ret = -EINVAL;
- }
-
- mutex_unlock(&file_inode(file)->i_mutex);
- return ret;
-}
-
static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes,
loff_t *ppos)
{
@@ -392,6 +373,7 @@ static void snoop_urb(struct usb_device *udev,
ep, t, d, length, timeout_or_status);
}
+ data_len = min(data_len, usbfs_snoop_max);
if (data && data_len > 0) {
print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1,
data, data_len, 1);
@@ -402,7 +384,8 @@ static void snoop_urb_data(struct urb *urb, unsigned len)
{
int i, size;
- if (!usbfs_snoop)
+ len = min(len, usbfs_snoop_max);
+ if (!usbfs_snoop || len == 0)
return;
if (urb->num_sgs == 0) {
@@ -1709,8 +1692,12 @@ static struct async *reap_as(struct usb_dev_state *ps)
static int proc_reapurb(struct usb_dev_state *ps, void __user *arg)
{
struct async *as = reap_as(ps);
+
if (as) {
- int retval = processcompl(as, (void __user * __user *)arg);
+ int retval;
+
+ snoop(&ps->dev->dev, "reap %p\n", as->userurb);
+ retval = processcompl(as, (void __user * __user *)arg);
free_async(as);
return retval;
}
@@ -1726,6 +1713,7 @@ static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg)
as = async_getcompleted(ps);
if (as) {
+ snoop(&ps->dev->dev, "reap %p\n", as->userurb);
retval = processcompl(as, (void __user * __user *)arg);
free_async(as);
} else {
@@ -1852,8 +1840,12 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
static int proc_reapurb_compat(struct usb_dev_state *ps, void __user *arg)
{
struct async *as = reap_as(ps);
+
if (as) {
- int retval = processcompl_compat(as, (void __user * __user *)arg);
+ int retval;
+
+ snoop(&ps->dev->dev, "reap %p\n", as->userurb);
+ retval = processcompl_compat(as, (void __user * __user *)arg);
free_async(as);
return retval;
}
@@ -1869,6 +1861,7 @@ static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *ar
as = async_getcompleted(ps);
if (as) {
+ snoop(&ps->dev->dev, "reap %p\n", as->userurb);
retval = processcompl_compat(as, (void __user * __user *)arg);
free_async(as);
} else {
@@ -2273,7 +2266,7 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
#endif
case USBDEVFS_DISCARDURB:
- snoop(&dev->dev, "%s: DISCARDURB\n", __func__);
+ snoop(&dev->dev, "%s: DISCARDURB %p\n", __func__, p);
ret = proc_unlinkurb(ps, p);
break;
@@ -2366,7 +2359,7 @@ static unsigned int usbdev_poll(struct file *file,
const struct file_operations usbdev_file_operations = {
.owner = THIS_MODULE,
- .llseek = usbdev_lseek,
+ .llseek = no_seek_end_llseek,
.read = usbdev_read,
.poll = usbdev_poll,
.unlocked_ioctl = usbdev_ioctl,
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 1c102d60cd9f..df0e3b92533a 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -3000,7 +3000,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_platform_shutdown);
#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
-struct usb_mon_operations *mon_ops;
+const struct usb_mon_operations *mon_ops;
/*
* The registration is unlocked.
@@ -3010,7 +3010,7 @@ struct usb_mon_operations *mon_ops;
* symbols from usbcore, usbcore gets referenced and cannot be unloaded first.
*/
-int usb_mon_register (struct usb_mon_operations *ops)
+int usb_mon_register(const struct usb_mon_operations *ops)
{
if (mon_ops)
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index bdeadc112d29..51b436918f78 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -124,6 +124,10 @@ struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev)
int usb_device_supports_lpm(struct usb_device *udev)
{
+ /* Some devices have trouble with LPM */
+ if (udev->quirks & USB_QUIRK_NO_LPM)
+ return 0;
+
/* USB 2.1 (and greater) devices indicate LPM support through
* their USB 2.0 Extended Capabilities BOS descriptor.
*/
@@ -1031,10 +1035,20 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
unsigned delay;
/* Continue a partial initialization */
- if (type == HUB_INIT2)
- goto init2;
- if (type == HUB_INIT3)
+ if (type == HUB_INIT2 || type == HUB_INIT3) {
+ device_lock(hub->intfdev);
+
+ /* Was the hub disconnected while we were waiting? */
+ if (hub->disconnected) {
+ device_unlock(hub->intfdev);
+ kref_put(&hub->kref, hub_release);
+ return;
+ }
+ if (type == HUB_INIT2)
+ goto init2;
goto init3;
+ }
+ kref_get(&hub->kref);
/* The superspeed hub except for root hub has to use Hub Depth
* value as an offset into the route string to locate the bits
@@ -1232,6 +1246,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
queue_delayed_work(system_power_efficient_wq,
&hub->init_work,
msecs_to_jiffies(delay));
+ device_unlock(hub->intfdev);
return; /* Continues at init3: below */
} else {
msleep(delay);
@@ -1253,6 +1268,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
/* Allow autosuspend if it was suppressed */
if (type <= HUB_INIT3)
usb_autopm_put_interface_async(to_usb_interface(hub->intfdev));
+
+ if (type == HUB_INIT2 || type == HUB_INIT3)
+ device_unlock(hub->intfdev);
+
+ kref_put(&hub->kref, hub_release);
}
/* Implement the continuations for the delays above */
@@ -3304,7 +3324,7 @@ static int finish_port_resume(struct usb_device *udev)
/*
* There are some SS USB devices which take longer time for link training.
* XHCI specs 4.19.4 says that when Link training is successful, port
- * sets CSC bit to 1. So if SW reads port status before successful link
+ * sets CCS bit to 1. So if SW reads port status before successful link
* training, then it will not find device to be present.
* USB Analyzer log with such buggy devices show that in some cases
* device switch on the RX termination after long delay of host enabling
@@ -3315,14 +3335,17 @@ static int finish_port_resume(struct usb_device *udev)
* routine implements a 2000 ms timeout for link training. If in a case
* link trains before timeout, loop will exit earlier.
*
+ * There are also some 2.0 hard drive based devices and 3.0 thumb
+ * drives that, when plugged into a 2.0 only port, take a long
+ * time to set CCS after VBUS enable.
+ *
* FIXME: If a device was connected before suspend, but was removed
* while system was asleep, then the loop in the following routine will
* only exit at timeout.
*
- * This routine should only be called when persist is enabled for a SS
- * device.
+ * This routine should only be called when persist is enabled.
*/
-static int wait_for_ss_port_enable(struct usb_device *udev,
+static int wait_for_connected(struct usb_device *udev,
struct usb_hub *hub, int *port1,
u16 *portchange, u16 *portstatus)
{
@@ -3335,6 +3358,7 @@ static int wait_for_ss_port_enable(struct usb_device *udev,
delay_ms += 20;
status = hub_port_status(hub, *port1, portstatus, portchange);
}
+ dev_dbg(&udev->dev, "Waited %dms for CONNECT\n", delay_ms);
return status;
}
@@ -3434,8 +3458,8 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
}
}
- if (udev->persist_enabled && hub_is_superspeed(hub->hdev))
- status = wait_for_ss_port_enable(udev, hub, &port1, &portchange,
+ if (udev->persist_enabled)
+ status = wait_for_connected(udev, hub, &port1, &portchange,
&portstatus);
status = check_port_resume_type(udev,
@@ -3875,17 +3899,30 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
return;
}
- if (usb_set_lpm_timeout(udev, state, timeout))
+ if (usb_set_lpm_timeout(udev, state, timeout)) {
/* If we can't set the parent hub U1/U2 timeout,
* device-initiated LPM won't be allowed either, so let the xHCI
* host know that this link state won't be enabled.
*/
hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state);
+ } else {
+ /* Only a configured device will accept the Set Feature
+ * U1/U2_ENABLE
+ */
+ if (udev->actconfig)
+ usb_set_device_initiated_lpm(udev, state, true);
- /* Only a configured device will accept the Set Feature U1/U2_ENABLE */
- else if (udev->actconfig)
- usb_set_device_initiated_lpm(udev, state, true);
-
+ /* As soon as usb_set_lpm_timeout(timeout) returns 0, the
+ * hub-initiated LPM is enabled. Thus, LPM is enabled no
+ * matter the result of usb_set_device_initiated_lpm().
+ * The only difference is whether device is able to initiate
+ * LPM.
+ */
+ if (state == USB3_LPM_U1)
+ udev->usb3_lpm_u1_enabled = 1;
+ else if (state == USB3_LPM_U2)
+ udev->usb3_lpm_u2_enabled = 1;
+ }
}
/*
@@ -3925,6 +3962,18 @@ static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
dev_warn(&udev->dev, "Could not disable xHCI %s timeout, "
"bus schedule bandwidth may be impacted.\n",
usb3_lpm_names[state]);
+
+ /* As soon as usb_set_lpm_timeout(0) return 0, hub initiated LPM
+ * is disabled. Hub will disallows link to enter U1/U2 as well,
+ * even device is initiating LPM. Hence LPM is disabled if hub LPM
+ * timeout set to 0, no matter device-initiated LPM is disabled or
+ * not.
+ */
+ if (state == USB3_LPM_U1)
+ udev->usb3_lpm_u1_enabled = 0;
+ else if (state == USB3_LPM_U2)
+ udev->usb3_lpm_u2_enabled = 0;
+
return 0;
}
@@ -3959,8 +4008,6 @@ int usb_disable_lpm(struct usb_device *udev)
if (usb_disable_link_state(hcd, udev, USB3_LPM_U2))
goto enable_lpm;
- udev->usb3_lpm_enabled = 0;
-
return 0;
enable_lpm:
@@ -3997,6 +4044,8 @@ EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm);
void usb_enable_lpm(struct usb_device *udev)
{
struct usb_hcd *hcd;
+ struct usb_hub *hub;
+ struct usb_port *port_dev;
if (!udev || !udev->parent ||
udev->speed != USB_SPEED_SUPER ||
@@ -4016,10 +4065,17 @@ void usb_enable_lpm(struct usb_device *udev)
if (udev->lpm_disable_count > 0)
return;
- usb_enable_link_state(hcd, udev, USB3_LPM_U1);
- usb_enable_link_state(hcd, udev, USB3_LPM_U2);
+ hub = usb_hub_to_struct_hub(udev->parent);
+ if (!hub)
+ return;
+
+ port_dev = hub->ports[udev->portnum - 1];
+
+ if (port_dev->usb3_lpm_u1_permit)
+ usb_enable_link_state(hcd, udev, USB3_LPM_U1);
- udev->usb3_lpm_enabled = 1;
+ if (port_dev->usb3_lpm_u2_permit)
+ usb_enable_link_state(hcd, udev, USB3_LPM_U2);
}
EXPORT_SYMBOL_GPL(usb_enable_lpm);
@@ -4512,6 +4568,8 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
goto fail;
}
+ usb_detect_quirks(udev);
+
if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) {
retval = usb_get_bos_descriptor(udev);
if (!retval) {
@@ -4710,7 +4768,6 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
if (status < 0)
goto loop;
- usb_detect_quirks(udev);
if (udev->quirks & USB_QUIRK_DELAY_INIT)
msleep(1000);
@@ -5326,9 +5383,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
if (udev->usb2_hw_lpm_enabled == 1)
usb_set_usb2_hardware_lpm(udev, 0);
- bos = udev->bos;
- udev->bos = NULL;
-
/* Disable LPM and LTM while we reset the device and reinstall the alt
* settings. Device-initiated LPM settings, and system exit latency
* settings are cleared when the device is reset, so we have to set
@@ -5337,15 +5391,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
ret = usb_unlocked_disable_lpm(udev);
if (ret) {
dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__);
- goto re_enumerate;
+ goto re_enumerate_no_bos;
}
ret = usb_disable_ltm(udev);
if (ret) {
dev_err(&udev->dev, "%s Failed to disable LTM\n.",
__func__);
- goto re_enumerate;
+ goto re_enumerate_no_bos;
}
+ bos = udev->bos;
+ udev->bos = NULL;
+
for (i = 0; i < SET_CONFIG_TRIES; ++i) {
/* ep0 maxpacket size may change; let the HCD know about it.
@@ -5442,10 +5499,11 @@ done:
return 0;
re_enumerate:
- /* LPM state doesn't matter when we're about to destroy the device. */
- hub_port_logical_disconnect(parent_hub, port1);
usb_release_bos_descriptor(udev);
udev->bos = bos;
+re_enumerate_no_bos:
+ /* LPM state doesn't matter when we're about to destroy the device. */
+ hub_port_logical_disconnect(parent_hub, port1);
return -ENODEV;
}
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 688817fb3246..45d070dd1d03 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -92,6 +92,8 @@ struct usb_hub {
* @status_lock: synchronize port_event() vs usb_port_{suspend|resume}
* @portnum: port index num based one
* @is_superspeed cache super-speed status
+ * @usb3_lpm_u1_permit: whether USB3 U1 LPM is permitted.
+ * @usb3_lpm_u2_permit: whether USB3 U2 LPM is permitted.
*/
struct usb_port {
struct usb_device *child;
@@ -104,6 +106,8 @@ struct usb_port {
struct mutex status_lock;
u8 portnum;
unsigned int is_superspeed:1;
+ unsigned int usb3_lpm_u1_permit:1;
+ unsigned int usb3_lpm_u2_permit:1;
};
#define to_usb_port(_dev) \
@@ -155,4 +159,3 @@ static inline int hub_port_debounce_be_stable(struct usb_hub *hub,
{
return hub_port_debounce(hub, port1, false);
}
-
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index 210618319f10..460c855be0d0 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -50,6 +50,72 @@ static ssize_t connect_type_show(struct device *dev,
}
static DEVICE_ATTR_RO(connect_type);
+static ssize_t usb3_lpm_permit_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_port *port_dev = to_usb_port(dev);
+ const char *p;
+
+ if (port_dev->usb3_lpm_u1_permit) {
+ if (port_dev->usb3_lpm_u2_permit)
+ p = "u1_u2";
+ else
+ p = "u1";
+ } else {
+ if (port_dev->usb3_lpm_u2_permit)
+ p = "u2";
+ else
+ p = "0";
+ }
+
+ return sprintf(buf, "%s\n", p);
+}
+
+static ssize_t usb3_lpm_permit_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_port *port_dev = to_usb_port(dev);
+ struct usb_device *udev = port_dev->child;
+ struct usb_hcd *hcd;
+
+ if (!strncmp(buf, "u1_u2", 5)) {
+ port_dev->usb3_lpm_u1_permit = 1;
+ port_dev->usb3_lpm_u2_permit = 1;
+
+ } else if (!strncmp(buf, "u1", 2)) {
+ port_dev->usb3_lpm_u1_permit = 1;
+ port_dev->usb3_lpm_u2_permit = 0;
+
+ } else if (!strncmp(buf, "u2", 2)) {
+ port_dev->usb3_lpm_u1_permit = 0;
+ port_dev->usb3_lpm_u2_permit = 1;
+
+ } else if (!strncmp(buf, "0", 1)) {
+ port_dev->usb3_lpm_u1_permit = 0;
+ port_dev->usb3_lpm_u2_permit = 0;
+ } else
+ return -EINVAL;
+
+ /* If device is connected to the port, disable or enable lpm
+ * to make new u1 u2 setting take effect immediately.
+ */
+ if (udev) {
+ hcd = bus_to_hcd(udev->bus);
+ if (!hcd)
+ return -EINVAL;
+ usb_lock_device(udev);
+ mutex_lock(hcd->bandwidth_mutex);
+ if (!usb_disable_lpm(udev))
+ usb_enable_lpm(udev);
+ mutex_unlock(hcd->bandwidth_mutex);
+ usb_unlock_device(udev);
+ }
+
+ return count;
+}
+static DEVICE_ATTR_RW(usb3_lpm_permit);
+
static struct attribute *port_dev_attrs[] = {
&dev_attr_connect_type.attr,
NULL,
@@ -64,6 +130,21 @@ static const struct attribute_group *port_dev_group[] = {
NULL,
};
+static struct attribute *port_dev_usb3_attrs[] = {
+ &dev_attr_usb3_lpm_permit.attr,
+ NULL,
+};
+
+static struct attribute_group port_dev_usb3_attr_grp = {
+ .attrs = port_dev_usb3_attrs,
+};
+
+static const struct attribute_group *port_dev_usb3_group[] = {
+ &port_dev_attr_grp,
+ &port_dev_usb3_attr_grp,
+ NULL,
+};
+
static void usb_port_device_release(struct device *dev)
{
struct usb_port *port_dev = to_usb_port(dev);
@@ -206,7 +287,7 @@ static int link_peers(struct usb_port *left, struct usb_port *right)
else
method = "default";
- pr_warn("usb: failed to peer %s and %s by %s (%s:%s) (%s:%s)\n",
+ pr_debug("usb: failed to peer %s and %s by %s (%s:%s) (%s:%s)\n",
dev_name(&left->dev), dev_name(&right->dev), method,
dev_name(&left->dev),
lpeer ? dev_name(&lpeer->dev) : "none",
@@ -265,7 +346,7 @@ static void link_peers_report(struct usb_port *left, struct usb_port *right)
if (rc == 0) {
dev_dbg(&left->dev, "peered to %s\n", dev_name(&right->dev));
} else {
- dev_warn(&left->dev, "failed to peer to %s (%d)\n",
+ dev_dbg(&left->dev, "failed to peer to %s (%d)\n",
dev_name(&right->dev), rc);
pr_warn_once("usb: port power management may be unreliable\n");
usb_port_block_power_off = 1;
@@ -401,6 +482,7 @@ static void find_and_link_peer(struct usb_hub *hub, int port1)
int usb_hub_create_port_device(struct usb_hub *hub, int port1)
{
struct usb_port *port_dev;
+ struct usb_device *hdev = hub->hdev;
int retval;
port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
@@ -417,7 +499,12 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
port_dev->portnum = port1;
set_bit(port1, hub->power_bits);
port_dev->dev.parent = hub->intfdev;
- port_dev->dev.groups = port_dev_group;
+ if (hub_is_superspeed(hdev)) {
+ port_dev->usb3_lpm_u1_permit = 1;
+ port_dev->usb3_lpm_u2_permit = 1;
+ port_dev->dev.groups = port_dev_usb3_group;
+ } else
+ port_dev->dev.groups = port_dev_group;
port_dev->dev.type = &usb_port_device_type;
port_dev->dev.driver = &usb_port_driver;
if (hub_is_superspeed(hub->hdev))
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index f5a381945db2..6dc810bce295 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -125,6 +125,9 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x04f3, 0x016f), .driver_info =
USB_QUIRK_DEVICE_QUALIFIER },
+ { USB_DEVICE(0x04f3, 0x21b8), .driver_info =
+ USB_QUIRK_DEVICE_QUALIFIER },
+
/* Roland SC-8820 */
{ USB_DEVICE(0x0582, 0x0007), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -199,6 +202,12 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x1a0a, 0x0200), .driver_info =
USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
+ /* Blackmagic Design Intensity Shuttle */
+ { USB_DEVICE(0x1edb, 0xbd3b), .driver_info = USB_QUIRK_NO_LPM },
+
+ /* Blackmagic Design UltraStudio SDI */
+ { USB_DEVICE(0x1edb, 0xbd4f), .driver_info = USB_QUIRK_NO_LPM },
+
{ } /* terminating entry must be last */
};
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index d9ec2de6c4cf..65b6e6b84043 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -531,7 +531,7 @@ static ssize_t usb2_lpm_besl_store(struct device *dev,
}
static DEVICE_ATTR_RW(usb2_lpm_besl);
-static ssize_t usb3_hardware_lpm_show(struct device *dev,
+static ssize_t usb3_hardware_lpm_u1_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_device *udev = to_usb_device(dev);
@@ -539,7 +539,7 @@ static ssize_t usb3_hardware_lpm_show(struct device *dev,
usb_lock_device(udev);
- if (udev->usb3_lpm_enabled)
+ if (udev->usb3_lpm_u1_enabled)
p = "enabled";
else
p = "disabled";
@@ -548,7 +548,26 @@ static ssize_t usb3_hardware_lpm_show(struct device *dev,
return sprintf(buf, "%s\n", p);
}
-static DEVICE_ATTR_RO(usb3_hardware_lpm);
+static DEVICE_ATTR_RO(usb3_hardware_lpm_u1);
+
+static ssize_t usb3_hardware_lpm_u2_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_device *udev = to_usb_device(dev);
+ const char *p;
+
+ usb_lock_device(udev);
+
+ if (udev->usb3_lpm_u2_enabled)
+ p = "enabled";
+ else
+ p = "disabled";
+
+ usb_unlock_device(udev);
+
+ return sprintf(buf, "%s\n", p);
+}
+static DEVICE_ATTR_RO(usb3_hardware_lpm_u2);
static struct attribute *usb2_hardware_lpm_attr[] = {
&dev_attr_usb2_hardware_lpm.attr,
@@ -562,7 +581,8 @@ static struct attribute_group usb2_hardware_lpm_attr_group = {
};
static struct attribute *usb3_hardware_lpm_attr[] = {
- &dev_attr_usb3_hardware_lpm.attr,
+ &dev_attr_usb3_hardware_lpm_u1.attr,
+ &dev_attr_usb3_hardware_lpm_u2.attr,
NULL,
};
static struct attribute_group usb3_hardware_lpm_attr_group = {
@@ -592,7 +612,8 @@ static int add_power_attributes(struct device *dev)
if (udev->usb2_hw_lpm_capable == 1)
rc = sysfs_merge_group(&dev->kobj,
&usb2_hardware_lpm_attr_group);
- if (udev->lpm_capable == 1)
+ if (udev->speed == USB_SPEED_SUPER &&
+ udev->lpm_capable == 1)
rc = sysfs_merge_group(&dev->kobj,
&usb3_hardware_lpm_attr_group);
}
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index f8bbd0b6d9fe..77e4c9bc0ab1 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -49,12 +49,7 @@ const char *usbcore_name = "usbcore";
static bool nousb; /* Disable USB when built into kernel image */
-/* To disable USB, kernel command line is 'nousb' not 'usbcore.nousb' */
-#ifdef MODULE
module_param(nousb, bool, 0444);
-#else
-core_param(nousb, nousb, bool, 0444);
-#endif
/*
* for external read access to <nousb>
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index ef73e498e98f..39a0fa8a4c0a 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -481,64 +481,168 @@ static void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg)
* Do core a soft reset of the core. Be careful with this because it
* resets all the internal state machines of the core.
*/
-static int dwc2_core_reset(struct dwc2_hsotg *hsotg)
+int dwc2_core_reset(struct dwc2_hsotg *hsotg)
{
u32 greset;
int count = 0;
- u32 gusbcfg;
dev_vdbg(hsotg->dev, "%s()\n", __func__);
- /* Wait for AHB master IDLE state */
+ /* Core Soft Reset */
+ greset = dwc2_readl(hsotg->regs + GRSTCTL);
+ greset |= GRSTCTL_CSFTRST;
+ dwc2_writel(greset, hsotg->regs + GRSTCTL);
do {
- usleep_range(20000, 40000);
+ udelay(1);
greset = dwc2_readl(hsotg->regs + GRSTCTL);
if (++count > 50) {
dev_warn(hsotg->dev,
- "%s() HANG! AHB Idle GRSTCTL=%0x\n",
+ "%s() HANG! Soft Reset GRSTCTL=%0x\n",
__func__, greset);
return -EBUSY;
}
- } while (!(greset & GRSTCTL_AHBIDLE));
+ } while (greset & GRSTCTL_CSFTRST);
- /* Core Soft Reset */
+ /* Wait for AHB master IDLE state */
count = 0;
- greset |= GRSTCTL_CSFTRST;
- dwc2_writel(greset, hsotg->regs + GRSTCTL);
do {
- usleep_range(20000, 40000);
+ udelay(1);
greset = dwc2_readl(hsotg->regs + GRSTCTL);
if (++count > 50) {
dev_warn(hsotg->dev,
- "%s() HANG! Soft Reset GRSTCTL=%0x\n",
+ "%s() HANG! AHB Idle GRSTCTL=%0x\n",
__func__, greset);
return -EBUSY;
}
- } while (greset & GRSTCTL_CSFTRST);
+ } while (!(greset & GRSTCTL_AHBIDLE));
- if (hsotg->dr_mode == USB_DR_MODE_HOST) {
- gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
- gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
- gusbcfg |= GUSBCFG_FORCEHOSTMODE;
- dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
- } else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) {
- gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
- gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
- gusbcfg |= GUSBCFG_FORCEDEVMODE;
- dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
- } else if (hsotg->dr_mode == USB_DR_MODE_OTG) {
- gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
- gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
- gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
- dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
- }
+ return 0;
+}
+
+/*
+ * Force the mode of the controller.
+ *
+ * Forcing the mode is needed for two cases:
+ *
+ * 1) If the dr_mode is set to either HOST or PERIPHERAL we force the
+ * controller to stay in a particular mode regardless of ID pin
+ * changes. We do this usually after a core reset.
+ *
+ * 2) During probe we want to read reset values of the hw
+ * configuration registers that are only available in either host or
+ * device mode. We may need to force the mode if the current mode does
+ * not allow us to access the register in the mode that we want.
+ *
+ * In either case it only makes sense to force the mode if the
+ * controller hardware is OTG capable.
+ *
+ * Checks are done in this function to determine whether doing a force
+ * would be valid or not.
+ *
+ * If a force is done, it requires a 25ms delay to take effect.
+ *
+ * Returns true if the mode was forced.
+ */
+static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
+{
+ u32 gusbcfg;
+ u32 set;
+ u32 clear;
+
+ dev_dbg(hsotg->dev, "Forcing mode to %s\n", host ? "host" : "device");
+
+ /*
+ * Force mode has no effect if the hardware is not OTG.
+ */
+ if (!dwc2_hw_is_otg(hsotg))
+ return false;
+
+ /*
+ * If dr_mode is either peripheral or host only, there is no
+ * need to ever force the mode to the opposite mode.
+ */
+ if (WARN_ON(host && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL))
+ return false;
+
+ if (WARN_ON(!host && hsotg->dr_mode == USB_DR_MODE_HOST))
+ return false;
+
+ gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+
+ set = host ? GUSBCFG_FORCEHOSTMODE : GUSBCFG_FORCEDEVMODE;
+ clear = host ? GUSBCFG_FORCEDEVMODE : GUSBCFG_FORCEHOSTMODE;
+
+ /*
+ * If the force mode bit is already set, don't set it.
+ */
+ if ((gusbcfg & set) && !(gusbcfg & clear))
+ return false;
+
+ gusbcfg &= ~clear;
+ gusbcfg |= set;
+ dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
+
+ msleep(25);
+ return true;
+}
+
+/*
+ * Clears the force mode bits.
+ */
+static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
+{
+ u32 gusbcfg;
+
+ gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
+ gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
+ dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
/*
* NOTE: This long sleep is _very_ important, otherwise the core will
* not stay in host mode after a connector ID change!
*/
- usleep_range(150000, 200000);
+ msleep(25);
+}
+
+/*
+ * Sets or clears force mode based on the dr_mode parameter.
+ */
+void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
+{
+ switch (hsotg->dr_mode) {
+ case USB_DR_MODE_HOST:
+ dwc2_force_mode(hsotg, true);
+ break;
+ case USB_DR_MODE_PERIPHERAL:
+ dwc2_force_mode(hsotg, false);
+ break;
+ case USB_DR_MODE_OTG:
+ dwc2_clear_force_mode(hsotg);
+ break;
+ default:
+ dev_warn(hsotg->dev, "%s() Invalid dr_mode=%d\n",
+ __func__, hsotg->dr_mode);
+ break;
+ }
+}
+
+/*
+ * Do core a soft reset of the core. Be careful with this because it
+ * resets all the internal state machines of the core.
+ *
+ * Additionally this will apply force mode as per the hsotg->dr_mode
+ * parameter.
+ */
+int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg)
+{
+ int retval;
+ retval = dwc2_core_reset(hsotg);
+ if (retval)
+ return retval;
+
+ dwc2_force_dr_mode(hsotg);
return 0;
}
@@ -553,16 +657,20 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
*/
if (select_phy) {
dev_dbg(hsotg->dev, "FS PHY selected\n");
+
usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
- usbcfg |= GUSBCFG_PHYSEL;
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+ if (!(usbcfg & GUSBCFG_PHYSEL)) {
+ usbcfg |= GUSBCFG_PHYSEL;
+ dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
- /* Reset after a PHY select */
- retval = dwc2_core_reset(hsotg);
- if (retval) {
- dev_err(hsotg->dev, "%s() Reset failed, aborting",
- __func__);
- return retval;
+ /* Reset after a PHY select */
+ retval = dwc2_core_reset_and_force_dr_mode(hsotg);
+
+ if (retval) {
+ dev_err(hsotg->dev,
+ "%s: Reset failed, aborting", __func__);
+ return retval;
+ }
}
}
@@ -597,13 +705,13 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
{
- u32 usbcfg;
+ u32 usbcfg, usbcfg_old;
int retval = 0;
if (!select_phy)
return 0;
- usbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+ usbcfg = usbcfg_old = dwc2_readl(hsotg->regs + GUSBCFG);
/*
* HS PHY parameters. These parameters are preserved during soft reset
@@ -631,14 +739,16 @@ static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
break;
}
- dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
+ if (usbcfg != usbcfg_old) {
+ dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
- /* Reset after setting the PHY parameters */
- retval = dwc2_core_reset(hsotg);
- if (retval) {
- dev_err(hsotg->dev, "%s() Reset failed, aborting",
- __func__);
- return retval;
+ /* Reset after setting the PHY parameters */
+ retval = dwc2_core_reset_and_force_dr_mode(hsotg);
+ if (retval) {
+ dev_err(hsotg->dev,
+ "%s: Reset failed, aborting", __func__);
+ return retval;
+ }
}
return retval;
@@ -765,11 +875,10 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg)
* dwc2_core_init() - Initializes the DWC_otg controller registers and
* prepares the core for device mode or host mode operation
*
- * @hsotg: Programming view of the DWC_otg controller
- * @select_phy: If true then also set the Phy type
- * @irq: If >= 0, the irq to register
+ * @hsotg: Programming view of the DWC_otg controller
+ * @initial_setup: If true then this is the first init for this instance.
*/
-int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq)
+int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup)
{
u32 usbcfg, otgctl;
int retval;
@@ -791,18 +900,26 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq)
dwc2_writel(usbcfg, hsotg->regs + GUSBCFG);
- /* Reset the Controller */
- retval = dwc2_core_reset(hsotg);
- if (retval) {
- dev_err(hsotg->dev, "%s(): Reset failed, aborting\n",
- __func__);
- return retval;
+ /*
+ * Reset the Controller
+ *
+ * We only need to reset the controller if this is a re-init.
+ * For the first init we know for sure that earlier code reset us (it
+ * needed to in order to properly detect various parameters).
+ */
+ if (!initial_setup) {
+ retval = dwc2_core_reset_and_force_dr_mode(hsotg);
+ if (retval) {
+ dev_err(hsotg->dev, "%s(): Reset failed, aborting\n",
+ __func__);
+ return retval;
+ }
}
/*
* This needs to happen in FS mode before any other programming occurs
*/
- retval = dwc2_phy_init(hsotg, select_phy);
+ retval = dwc2_phy_init(hsotg, initial_setup);
if (retval)
return retval;
@@ -1707,6 +1824,7 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
u32 hcchar;
u32 hctsiz = 0;
u16 num_packets;
+ u32 ec_mc;
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "%s()\n", __func__);
@@ -1743,6 +1861,13 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
hctsiz |= chan->xfer_len << TSIZ_XFERSIZE_SHIFT &
TSIZ_XFERSIZE_MASK;
+
+ /* For split set ec_mc for immediate retries */
+ if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
+ chan->ep_type == USB_ENDPOINT_XFER_ISOC)
+ ec_mc = 3;
+ else
+ ec_mc = 1;
} else {
if (dbg_hc(chan))
dev_vdbg(hsotg->dev, "no split\n");
@@ -1805,6 +1930,9 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
hctsiz |= chan->xfer_len << TSIZ_XFERSIZE_SHIFT &
TSIZ_XFERSIZE_MASK;
+
+ /* The ec_mc gets the multi_count for non-split */
+ ec_mc = chan->multi_count;
}
chan->start_pkt_count = num_packets;
@@ -1855,8 +1983,7 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg,
hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
hcchar &= ~HCCHAR_MULTICNT_MASK;
- hcchar |= chan->multi_count << HCCHAR_MULTICNT_SHIFT &
- HCCHAR_MULTICNT_MASK;
+ hcchar |= (ec_mc << HCCHAR_MULTICNT_SHIFT) & HCCHAR_MULTICNT_MASK;
dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar);
if (hcchar & HCCHAR_CHDIS)
@@ -1905,7 +2032,6 @@ void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
struct dwc2_host_chan *chan)
{
u32 hcchar;
- u32 hc_dma;
u32 hctsiz = 0;
if (chan->do_ping)
@@ -1934,14 +2060,14 @@ void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num));
- hc_dma = (u32)chan->desc_list_addr & HCDMA_DMA_ADDR_MASK;
+ dma_sync_single_for_device(hsotg->dev, chan->desc_list_addr,
+ chan->desc_list_sz, DMA_TO_DEVICE);
+
+ dwc2_writel(chan->desc_list_addr, hsotg->regs + HCDMA(chan->hc_num));
- /* Always start from first descriptor */
- hc_dma &= ~HCDMA_CTD_MASK;
- dwc2_writel(hc_dma, hsotg->regs + HCDMA(chan->hc_num));
if (dbg_hc(chan))
- dev_vdbg(hsotg->dev, "Wrote %08x to HCDMA(%d)\n",
- hc_dma, chan->hc_num);
+ dev_vdbg(hsotg->dev, "Wrote %pad to HCDMA(%d)\n",
+ &chan->desc_list_addr, chan->hc_num);
hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num));
hcchar &= ~HCCHAR_MULTICNT_MASK;
@@ -2485,6 +2611,29 @@ void dwc2_set_param_dma_desc_enable(struct dwc2_hsotg *hsotg, int val)
hsotg->core_params->dma_desc_enable = val;
}
+void dwc2_set_param_dma_desc_fs_enable(struct dwc2_hsotg *hsotg, int val)
+{
+ int valid = 1;
+
+ if (val > 0 && (hsotg->core_params->dma_enable <= 0 ||
+ !hsotg->hw_params.dma_desc_enable))
+ valid = 0;
+ if (val < 0)
+ valid = 0;
+
+ if (!valid) {
+ if (val >= 0)
+ dev_err(hsotg->dev,
+ "%d invalid for dma_desc_fs_enable parameter. Check HW configuration.\n",
+ val);
+ val = (hsotg->core_params->dma_enable > 0 &&
+ hsotg->hw_params.dma_desc_enable);
+ }
+
+ hsotg->core_params->dma_desc_fs_enable = val;
+ dev_dbg(hsotg->dev, "Setting dma_desc_fs_enable to %d\n", val);
+}
+
void dwc2_set_param_host_support_fs_ls_low_power(struct dwc2_hsotg *hsotg,
int val)
{
@@ -3016,6 +3165,7 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
dwc2_set_param_otg_cap(hsotg, params->otg_cap);
dwc2_set_param_dma_enable(hsotg, params->dma_enable);
dwc2_set_param_dma_desc_enable(hsotg, params->dma_desc_enable);
+ dwc2_set_param_dma_desc_fs_enable(hsotg, params->dma_desc_fs_enable);
dwc2_set_param_host_support_fs_ls_low_power(hsotg,
params->host_support_fs_ls_low_power);
dwc2_set_param_enable_dynamic_fifo(hsotg,
@@ -3052,17 +3202,93 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
dwc2_set_param_hibernation(hsotg, params->hibernation);
}
+/*
+ * Forces either host or device mode if the controller is not
+ * currently in that mode.
+ *
+ * Returns true if the mode was forced.
+ */
+static bool dwc2_force_mode_if_needed(struct dwc2_hsotg *hsotg, bool host)
+{
+ if (host && dwc2_is_host_mode(hsotg))
+ return false;
+ else if (!host && dwc2_is_device_mode(hsotg))
+ return false;
+
+ return dwc2_force_mode(hsotg, host);
+}
+
+/*
+ * Gets host hardware parameters. Forces host mode if not currently in
+ * host mode. Should be called immediately after a core soft reset in
+ * order to get the reset values.
+ */
+static void dwc2_get_host_hwparams(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_hw_params *hw = &hsotg->hw_params;
+ u32 gnptxfsiz;
+ u32 hptxfsiz;
+ bool forced;
+
+ if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
+ return;
+
+ forced = dwc2_force_mode_if_needed(hsotg, true);
+
+ gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
+ hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ);
+ dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
+ dev_dbg(hsotg->dev, "hptxfsiz=%08x\n", hptxfsiz);
+
+ if (forced)
+ dwc2_clear_force_mode(hsotg);
+
+ hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
+ FIFOSIZE_DEPTH_SHIFT;
+ hw->host_perio_tx_fifo_size = (hptxfsiz & FIFOSIZE_DEPTH_MASK) >>
+ FIFOSIZE_DEPTH_SHIFT;
+}
+
+/*
+ * Gets device hardware parameters. Forces device mode if not
+ * currently in device mode. Should be called immediately after a core
+ * soft reset in order to get the reset values.
+ */
+static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_hw_params *hw = &hsotg->hw_params;
+ bool forced;
+ u32 gnptxfsiz;
+
+ if (hsotg->dr_mode == USB_DR_MODE_HOST)
+ return;
+
+ forced = dwc2_force_mode_if_needed(hsotg, false);
+
+ gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
+ dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
+
+ if (forced)
+ dwc2_clear_force_mode(hsotg);
+
+ hw->dev_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
+ FIFOSIZE_DEPTH_SHIFT;
+}
+
/**
* During device initialization, read various hardware configuration
* registers and interpret the contents.
+ *
+ * This should be called during driver probe. It will perform a core
+ * soft reset in order to get the reset values of the parameters.
*/
int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
{
struct dwc2_hw_params *hw = &hsotg->hw_params;
unsigned width;
u32 hwcfg1, hwcfg2, hwcfg3, hwcfg4;
- u32 hptxfsiz, grxfsiz, gnptxfsiz;
- u32 gusbcfg;
+ u32 grxfsiz;
+ int retval;
/*
* Attempt to ensure this device is really a DWC_otg Controller.
@@ -3082,6 +3308,10 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
+ retval = dwc2_core_reset(hsotg);
+ if (retval)
+ return retval;
+
hwcfg1 = dwc2_readl(hsotg->regs + GHWCFG1);
hwcfg2 = dwc2_readl(hsotg->regs + GHWCFG2);
hwcfg3 = dwc2_readl(hsotg->regs + GHWCFG3);
@@ -3094,20 +3324,16 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "hwcfg4=%08x\n", hwcfg4);
dev_dbg(hsotg->dev, "grxfsiz=%08x\n", grxfsiz);
- /* Force host mode to get HPTXFSIZ / GNPTXFSIZ exact power on value */
- gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
- gusbcfg |= GUSBCFG_FORCEHOSTMODE;
- dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
- usleep_range(100000, 150000);
+ /*
+ * Host specific hardware parameters. Reading these parameters
+ * requires the controller to be in host mode. The mode will
+ * be forced, if necessary, to read these values.
+ */
+ dwc2_get_host_hwparams(hsotg);
+ dwc2_get_dev_hwparams(hsotg);
- gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
- hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ);
- dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz);
- dev_dbg(hsotg->dev, "hptxfsiz=%08x\n", hptxfsiz);
- gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
- gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
- dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
- usleep_range(100000, 150000);
+ /* hwcfg1 */
+ hw->dev_ep_dirs = hwcfg1;
/* hwcfg2 */
hw->op_mode = (hwcfg2 & GHWCFG2_OP_MODE_MASK) >>
@@ -3163,10 +3389,6 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
/* fifo sizes */
hw->host_rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >>
GRXFSIZ_DEPTH_SHIFT;
- hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
- FIFOSIZE_DEPTH_SHIFT;
- hw->host_perio_tx_fifo_size = (hptxfsiz & FIFOSIZE_DEPTH_MASK) >>
- FIFOSIZE_DEPTH_SHIFT;
dev_dbg(hsotg->dev, "Detected values from hardware:\n");
dev_dbg(hsotg->dev, " op_mode=%d\n",
@@ -3275,6 +3497,43 @@ void dwc2_disable_global_interrupts(struct dwc2_hsotg *hsotg)
dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG);
}
+/* Returns the controller's GHWCFG2.OTG_MODE. */
+unsigned dwc2_op_mode(struct dwc2_hsotg *hsotg)
+{
+ u32 ghwcfg2 = dwc2_readl(hsotg->regs + GHWCFG2);
+
+ return (ghwcfg2 & GHWCFG2_OP_MODE_MASK) >>
+ GHWCFG2_OP_MODE_SHIFT;
+}
+
+/* Returns true if the controller is capable of DRD. */
+bool dwc2_hw_is_otg(struct dwc2_hsotg *hsotg)
+{
+ unsigned op_mode = dwc2_op_mode(hsotg);
+
+ return (op_mode == GHWCFG2_OP_MODE_HNP_SRP_CAPABLE) ||
+ (op_mode == GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE) ||
+ (op_mode == GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE);
+}
+
+/* Returns true if the controller is host-only. */
+bool dwc2_hw_is_host(struct dwc2_hsotg *hsotg)
+{
+ unsigned op_mode = dwc2_op_mode(hsotg);
+
+ return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_HOST) ||
+ (op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST);
+}
+
+/* Returns true if the controller is device-only. */
+bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg)
+{
+ unsigned op_mode = dwc2_op_mode(hsotg);
+
+ return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) ||
+ (op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE);
+}
+
MODULE_DESCRIPTION("DESIGNWARE HS OTG Core");
MODULE_AUTHOR("Synopsys, Inc.");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index a66d3cb62b65..7fb6434f4639 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -246,6 +246,13 @@ enum dwc2_ep0_state {
* value for this if none is specified.
* 0 - Address DMA
* 1 - Descriptor DMA (default, if available)
+ * @dma_desc_fs_enable: When DMA mode is enabled, specifies whether to use
+ * address DMA mode or descriptor DMA mode for accessing
+ * the data FIFOs in Full Speed mode only. The driver
+ * will automatically detect the value for this if none is
+ * specified.
+ * 0 - Address DMA
+ * 1 - Descriptor DMA in FS (default, if available)
* @speed: Specifies the maximum speed of operation in host and
* device mode. The actual speed depends on the speed of
* the attached device and the value of phy_type.
@@ -375,6 +382,7 @@ struct dwc2_core_params {
int otg_ver;
int dma_enable;
int dma_desc_enable;
+ int dma_desc_fs_enable;
int speed;
int enable_dynamic_fifo;
int en_multiple_tx_fifo;
@@ -451,15 +459,18 @@ struct dwc2_core_params {
* 1 - 16 bits
* 2 - 8 or 16 bits
* @snpsid: Value from SNPSID register
+ * @dev_ep_dirs: Direction of device endpoints (GHWCFG1)
*/
struct dwc2_hw_params {
unsigned op_mode:3;
unsigned arch:2;
unsigned dma_desc_enable:1;
+ unsigned dma_desc_fs_enable:1;
unsigned enable_dynamic_fifo:1;
unsigned en_multiple_tx_fifo:1;
unsigned host_rx_fifo_size:16;
unsigned host_nperio_tx_fifo_size:16;
+ unsigned dev_nperio_tx_fifo_size:16;
unsigned host_perio_tx_fifo_size:16;
unsigned nperio_tx_q_depth:3;
unsigned host_perio_tx_q_depth:3;
@@ -476,6 +487,7 @@ struct dwc2_hw_params {
unsigned power_optimized:1;
unsigned utmi_phy_data_width:2;
u32 snpsid;
+ u32 dev_ep_dirs;
};
/* Size of control and EP0 buffers */
@@ -676,6 +688,9 @@ struct dwc2_hregs_backup {
* @otg_port: OTG port number
* @frame_list: Frame list
* @frame_list_dma: Frame list DMA address
+ * @frame_list_sz: Frame list size
+ * @desc_gen_cache: Kmem cache for generic descriptors
+ * @desc_hsisoc_cache: Kmem cache for hs isochronous descriptors
*
* These are for peripheral mode:
*
@@ -770,6 +785,7 @@ struct dwc2_hsotg {
u16 frame_number;
u16 periodic_qh_count;
bool bus_suspended;
+ bool new_connection;
#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
#define FRAME_NUM_ARRAY_SIZE 1000
@@ -794,6 +810,9 @@ struct dwc2_hsotg {
u8 otg_port;
u32 *frame_list;
dma_addr_t frame_list_dma;
+ u32 frame_list_sz;
+ struct kmem_cache *desc_gen_cache;
+ struct kmem_cache *desc_hsisoc_cache;
#ifdef DEBUG
u32 frrem_samples;
@@ -864,10 +883,14 @@ enum dwc2_halt_status {
* The following functions support initialization of the core driver component
* and the DWC_otg controller
*/
+extern int dwc2_core_reset(struct dwc2_hsotg *hsotg);
+extern int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg);
extern void dwc2_core_host_init(struct dwc2_hsotg *hsotg);
extern int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg);
extern int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore);
+void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg);
+
/*
* Host core Functions.
* The following functions support managing the DWC_otg controller in host
@@ -901,7 +924,7 @@ extern void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes);
extern void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num);
extern void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg);
-extern int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq);
+extern int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup);
extern void dwc2_enable_global_interrupts(struct dwc2_hsotg *hcd);
extern void dwc2_disable_global_interrupts(struct dwc2_hsotg *hcd);
@@ -942,6 +965,16 @@ extern void dwc2_set_param_dma_enable(struct dwc2_hsotg *hsotg, int val);
extern void dwc2_set_param_dma_desc_enable(struct dwc2_hsotg *hsotg, int val);
/*
+ * When DMA mode is enabled specifies whether to use
+ * address DMA or DMA Descritor mode with full speed devices
+ * for accessing the data FIFOs in host mode.
+ * 0 - address DMA
+ * 1 - FS DMA Descriptor(default, if available)
+ */
+extern void dwc2_set_param_dma_desc_fs_enable(struct dwc2_hsotg *hsotg,
+ int val);
+
+/*
* Specifies the maximum speed of operation in host and device mode.
* The actual speed depends on the speed of the attached device and
* the value of phy_type. The actual speed depends on the speed of the
@@ -1110,6 +1143,31 @@ extern int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg);
extern int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg);
/*
+ * The following functions check the controller's OTG operation mode
+ * capability (GHWCFG2.OTG_MODE).
+ *
+ * These functions can be used before the internal hsotg->hw_params
+ * are read in and cached so they always read directly from the
+ * GHWCFG2 register.
+ */
+unsigned dwc2_op_mode(struct dwc2_hsotg *hsotg);
+bool dwc2_hw_is_otg(struct dwc2_hsotg *hsotg);
+bool dwc2_hw_is_host(struct dwc2_hsotg *hsotg);
+bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg);
+
+/*
+ * Returns the mode of operation, host or device
+ */
+static inline int dwc2_is_host_mode(struct dwc2_hsotg *hsotg)
+{
+ return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) != 0;
+}
+static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg)
+{
+ return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) == 0;
+}
+
+/*
* Dump core registers and SPRAM
*/
extern void dwc2_dump_dev_registers(struct dwc2_hsotg *hsotg);
@@ -1154,12 +1212,14 @@ static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
-extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg);
+extern void dwc2_hcd_connect(struct dwc2_hsotg *hsotg);
+extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force);
extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
#else
static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
{ return 0; }
-static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) {}
+static inline void dwc2_hcd_connect(struct dwc2_hsotg *hsotg) {}
+static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force) {}
static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {}
static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c
index 27daa42788b1..d85c5c9f96c1 100644
--- a/drivers/usb/dwc2/core_intr.c
+++ b/drivers/usb/dwc2/core_intr.c
@@ -86,9 +86,6 @@ static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
hprt0 &= ~HPRT0_ENA;
dwc2_writel(hprt0, hsotg->regs + HPRT0);
}
-
- /* Clear interrupt */
- dwc2_writel(GINTSTS_PRTINT, hsotg->regs + GINTSTS);
}
/**
@@ -98,11 +95,11 @@ static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
*/
static void dwc2_handle_mode_mismatch_intr(struct dwc2_hsotg *hsotg)
{
- dev_warn(hsotg->dev, "Mode Mismatch Interrupt: currently in %s mode\n",
- dwc2_is_host_mode(hsotg) ? "Host" : "Device");
-
/* Clear interrupt */
dwc2_writel(GINTSTS_MODEMIS, hsotg->regs + GINTSTS);
+
+ dev_warn(hsotg->dev, "Mode Mismatch Interrupt: currently in %s mode\n",
+ dwc2_is_host_mode(hsotg) ? "Host" : "Device");
}
/**
@@ -239,7 +236,7 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "a_suspend->a_peripheral (%d)\n",
hsotg->op_state);
spin_unlock(&hsotg->lock);
- dwc2_hcd_disconnect(hsotg);
+ dwc2_hcd_disconnect(hsotg, false);
spin_lock(&hsotg->lock);
hsotg->op_state = OTG_STATE_A_PERIPHERAL;
} else {
@@ -276,9 +273,13 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
*/
static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
{
- u32 gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
+ u32 gintmsk;
+
+ /* Clear interrupt */
+ dwc2_writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS);
/* Need to disable SOF interrupt immediately */
+ gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
gintmsk &= ~GINTSTS_SOF;
dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
@@ -295,9 +296,6 @@ static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
queue_work(hsotg->wq_otg, &hsotg->wf_otg);
spin_lock(&hsotg->lock);
}
-
- /* Clear interrupt */
- dwc2_writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS);
}
/**
@@ -315,12 +313,12 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
{
int ret;
- dev_dbg(hsotg->dev, "Session request interrupt - lx_state=%d\n",
- hsotg->lx_state);
-
/* Clear interrupt */
dwc2_writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS);
+ dev_dbg(hsotg->dev, "Session request interrupt - lx_state=%d\n",
+ hsotg->lx_state);
+
if (dwc2_is_device_mode(hsotg)) {
if (hsotg->lx_state == DWC2_L2) {
ret = dwc2_exit_hibernation(hsotg, true);
@@ -347,6 +345,10 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
{
int ret;
+
+ /* Clear interrupt */
+ dwc2_writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
+
dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n");
dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);
@@ -368,10 +370,9 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
/* Change to L0 state */
hsotg->lx_state = DWC2_L0;
} else {
- if (hsotg->core_params->hibernation) {
- dwc2_writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
+ if (hsotg->core_params->hibernation)
return;
- }
+
if (hsotg->lx_state != DWC2_L1) {
u32 pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
@@ -385,9 +386,6 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
hsotg->lx_state = DWC2_L0;
}
}
-
- /* Clear interrupt */
- dwc2_writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
}
/*
@@ -396,14 +394,14 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
*/
static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg)
{
+ dwc2_writel(GINTSTS_DISCONNINT, hsotg->regs + GINTSTS);
+
dev_dbg(hsotg->dev, "++Disconnect Detected Interrupt++ (%s) %s\n",
dwc2_is_host_mode(hsotg) ? "Host" : "Device",
dwc2_op_state_str(hsotg));
if (hsotg->op_state == OTG_STATE_A_HOST)
- dwc2_hcd_disconnect(hsotg);
-
- dwc2_writel(GINTSTS_DISCONNINT, hsotg->regs + GINTSTS);
+ dwc2_hcd_disconnect(hsotg, false);
}
/*
@@ -419,6 +417,9 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
u32 dsts;
int ret;
+ /* Clear interrupt */
+ dwc2_writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);
+
dev_dbg(hsotg->dev, "USB SUSPEND\n");
if (dwc2_is_device_mode(hsotg)) {
@@ -437,7 +438,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
if (!dwc2_is_device_connected(hsotg)) {
dev_dbg(hsotg->dev,
"ignore suspend request before enumeration\n");
- goto clear_int;
+ return;
}
ret = dwc2_enter_hibernation(hsotg);
@@ -476,10 +477,6 @@ skip_power_saving:
hsotg->op_state = OTG_STATE_A_HOST;
}
}
-
-clear_int:
- /* Clear interrupt */
- dwc2_writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);
}
#define GINTMSK_COMMON (GINTSTS_WKUPINT | GINTSTS_SESSREQINT | \
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 0abf73c91beb..422ab7da4eb5 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -2095,7 +2095,7 @@ static void dwc2_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg)
*/
/* catch both EnumSpd_FS and EnumSpd_FS48 */
- switch (dsts & DSTS_ENUMSPD_MASK) {
+ switch ((dsts & DSTS_ENUMSPD_MASK) >> DSTS_ENUMSPD_SHIFT) {
case DSTS_ENUMSPD_FS:
case DSTS_ENUMSPD_FS48:
hsotg->gadget.speed = USB_SPEED_FULL;
@@ -2244,54 +2244,6 @@ static void dwc2_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic)
GINTSTS_RXFLVL)
/**
- * dwc2_hsotg_corereset - issue softreset to the core
- * @hsotg: The device state
- *
- * Issue a soft reset to the core, and await the core finishing it.
- */
-static int dwc2_hsotg_corereset(struct dwc2_hsotg *hsotg)
-{
- int timeout;
- u32 grstctl;
-
- dev_dbg(hsotg->dev, "resetting core\n");
-
- /* issue soft reset */
- dwc2_writel(GRSTCTL_CSFTRST, hsotg->regs + GRSTCTL);
-
- timeout = 10000;
- do {
- grstctl = dwc2_readl(hsotg->regs + GRSTCTL);
- } while ((grstctl & GRSTCTL_CSFTRST) && timeout-- > 0);
-
- if (grstctl & GRSTCTL_CSFTRST) {
- dev_err(hsotg->dev, "Failed to get CSftRst asserted\n");
- return -EINVAL;
- }
-
- timeout = 10000;
-
- while (1) {
- u32 grstctl = dwc2_readl(hsotg->regs + GRSTCTL);
-
- if (timeout-- < 0) {
- dev_info(hsotg->dev,
- "%s: reset failed, GRSTCTL=%08x\n",
- __func__, grstctl);
- return -ETIMEDOUT;
- }
-
- if (!(grstctl & GRSTCTL_AHBIDLE))
- continue;
-
- break; /* reset done */
- }
-
- dev_dbg(hsotg->dev, "reset successful\n");
- return 0;
-}
-
-/**
* dwc2_hsotg_core_init - issue softreset to the core
* @hsotg: The device state
*
@@ -2307,7 +2259,7 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET);
if (!is_usb_reset)
- if (dwc2_hsotg_corereset(hsotg))
+ if (dwc2_core_reset(hsotg))
return;
/*
@@ -2585,7 +2537,7 @@ irq_retry:
if (gintsts & GINTSTS_GOUTNAKEFF) {
dev_info(hsotg->dev, "GOUTNakEff triggered\n");
- dwc2_writel(DCTL_CGOUTNAK, hsotg->regs + DCTL);
+ __orr32(hsotg->regs + DCTL, DCTL_CGOUTNAK);
dwc2_hsotg_dump(hsotg);
}
@@ -2593,7 +2545,7 @@ irq_retry:
if (gintsts & GINTSTS_GINNAKEFF) {
dev_info(hsotg->dev, "GINNakEff triggered\n");
- dwc2_writel(DCTL_CGNPINNAK, hsotg->regs + DCTL);
+ __orr32(hsotg->regs + DCTL, DCTL_CGNPINNAK);
dwc2_hsotg_dump(hsotg);
}
@@ -2911,15 +2863,15 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
"%s: timeout DIEPINT.NAKEFF\n", __func__);
} else {
/* Clear any pending nak effect interrupt */
- dwc2_writel(GINTSTS_GINNAKEFF, hsotg->regs + GINTSTS);
+ dwc2_writel(GINTSTS_GOUTNAKEFF, hsotg->regs + GINTSTS);
- __orr32(hsotg->regs + DCTL, DCTL_SGNPINNAK);
+ __orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK);
/* Wait for global nak to take effect */
if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS,
- GINTSTS_GINNAKEFF, 100))
+ GINTSTS_GOUTNAKEFF, 100))
dev_warn(hsotg->dev,
- "%s: timeout GINTSTS.GINNAKEFF\n", __func__);
+ "%s: timeout GINTSTS.GOUTNAKEFF\n", __func__);
}
/* Disable ep */
@@ -2944,7 +2896,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
/* TODO: Flush shared tx fifo */
} else {
/* Remove global NAKs */
- __bic32(hsotg->regs + DCTL, DCTL_SGNPINNAK);
+ __bic32(hsotg->regs + DCTL, DCTL_SGOUTNAK);
}
}
@@ -3403,8 +3355,8 @@ static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
/* check hardware configuration */
- cfg = dwc2_readl(hsotg->regs + GHWCFG2);
- hsotg->num_of_eps = (cfg >> GHWCFG2_NUM_DEV_EP_SHIFT) & 0xF;
+ hsotg->num_of_eps = hsotg->hw_params.num_dev_ep;
+
/* Add ep0 */
hsotg->num_of_eps++;
@@ -3415,7 +3367,7 @@ static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
/* Same dwc2_hsotg_ep is used in both directions for ep0 */
hsotg->eps_out[0] = hsotg->eps_in[0];
- cfg = dwc2_readl(hsotg->regs + GHWCFG1);
+ cfg = hsotg->hw_params.dev_ep_dirs;
for (i = 1, cfg >>= 2; i < hsotg->num_of_eps; i++, cfg >>= 2) {
ep_type = cfg & 3;
/* Direction in or both */
@@ -3434,11 +3386,8 @@ static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
}
}
- cfg = dwc2_readl(hsotg->regs + GHWCFG3);
- hsotg->fifo_mem = (cfg >> GHWCFG3_DFIFO_DEPTH_SHIFT);
-
- cfg = dwc2_readl(hsotg->regs + GHWCFG4);
- hsotg->dedicated_fifos = (cfg >> GHWCFG4_DED_FIFO_SHIFT) & 1;
+ hsotg->fifo_mem = hsotg->hw_params.total_fifo_size;
+ hsotg->dedicated_fifos = hsotg->hw_params.en_multiple_tx_fifo;
dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n",
hsotg->num_of_eps,
@@ -3563,6 +3512,17 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
memcpy(&hsotg->g_tx_fifo_sz[1], p_tx_fifo, sizeof(p_tx_fifo));
/* Device tree specific probe */
dwc2_hsotg_of_probe(hsotg);
+
+ /* Check against largest possible value. */
+ if (hsotg->g_np_g_tx_fifo_sz >
+ hsotg->hw_params.dev_nperio_tx_fifo_size) {
+ dev_warn(dev, "Specified GNPTXFDEP=%d > %d\n",
+ hsotg->g_np_g_tx_fifo_sz,
+ hsotg->hw_params.dev_nperio_tx_fifo_size);
+ hsotg->g_np_g_tx_fifo_sz =
+ hsotg->hw_params.dev_nperio_tx_fifo_size;
+ }
+
/* Dump fifo information */
dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n",
hsotg->g_np_g_tx_fifo_sz);
@@ -3579,31 +3539,12 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
- /*
- * Force Device mode before initialization.
- * This allows correctly configuring fifo for device mode.
- */
- __bic32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEHOSTMODE);
- __orr32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEDEVMODE);
-
- /*
- * According to Synopsys databook, this sleep is needed for the force
- * device mode to take effect.
- */
- msleep(25);
-
- dwc2_hsotg_corereset(hsotg);
ret = dwc2_hsotg_hw_cfg(hsotg);
if (ret) {
dev_err(hsotg->dev, "Hardware configuration failed: %d\n", ret);
return ret;
}
- dwc2_hsotg_init(hsotg);
-
- /* Switch back to default configuration */
- __bic32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEDEVMODE);
-
hsotg->ctrl_buff = devm_kzalloc(hsotg->dev,
DWC2_CTRL_BUFF_SIZE, GFP_KERNEL);
if (!hsotg->ctrl_buff) {
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 571c21727ff9..8847c72e55f6 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -268,15 +268,33 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
}
/**
+ * dwc2_hcd_connect() - Handles connect of the HCD
+ *
+ * @hsotg: Pointer to struct dwc2_hsotg
+ *
+ * Must be called with interrupt disabled and spinlock held
+ */
+void dwc2_hcd_connect(struct dwc2_hsotg *hsotg)
+{
+ if (hsotg->lx_state != DWC2_L0)
+ usb_hcd_resume_root_hub(hsotg->priv);
+
+ hsotg->flags.b.port_connect_status_change = 1;
+ hsotg->flags.b.port_connect_status = 1;
+}
+
+/**
* dwc2_hcd_disconnect() - Handles disconnect of the HCD
*
* @hsotg: Pointer to struct dwc2_hsotg
+ * @force: If true, we won't try to reconnect even if we see device connected.
*
* Must be called with interrupt disabled and spinlock held
*/
-void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg)
+void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force)
{
u32 intr;
+ u32 hprt0;
/* Set status flags for the hub driver */
hsotg->flags.b.port_connect_status_change = 1;
@@ -315,6 +333,24 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg)
dwc2_hcd_cleanup_channels(hsotg);
dwc2_host_disconnect(hsotg);
+
+ /*
+ * Add an extra check here to see if we're actually connected but
+ * we don't have a detection interrupt pending. This can happen if:
+ * 1. hardware sees connect
+ * 2. hardware sees disconnect
+ * 3. hardware sees connect
+ * 4. dwc2_port_intr() - clears connect interrupt
+ * 5. dwc2_handle_common_intr() - calls here
+ *
+ * Without the extra check here we will end calling disconnect
+ * and won't get any future interrupts to handle the connect.
+ */
+ if (!force) {
+ hprt0 = dwc2_readl(hsotg->regs + HPRT0);
+ if (!(hprt0 & HPRT0_CONNDET) && (hprt0 & HPRT0_CONNSTS))
+ dwc2_hcd_connect(hsotg);
+ }
}
/**
@@ -881,8 +917,10 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
*/
chan->multi_count = dwc2_hb_mult(qh->maxp);
- if (hsotg->core_params->dma_desc_enable > 0)
+ if (hsotg->core_params->dma_desc_enable > 0) {
chan->desc_list_addr = qh->desc_list_dma;
+ chan->desc_list_sz = qh->desc_list_sz;
+ }
dwc2_hc_init(hsotg, chan);
chan->qh = qh;
@@ -1382,7 +1420,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
dev_err(hsotg->dev,
"Connection id status change timed out\n");
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
- dwc2_core_init(hsotg, false, -1);
+ dwc2_core_init(hsotg, false);
dwc2_enable_global_interrupts(hsotg);
spin_lock_irqsave(&hsotg->lock, flags);
dwc2_hsotg_core_init_disconnected(hsotg, false);
@@ -1405,7 +1443,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
hsotg->op_state = OTG_STATE_A_HOST;
/* Initialize the Core for Host mode */
- dwc2_core_init(hsotg, false, -1);
+ dwc2_core_init(hsotg, false);
dwc2_enable_global_interrupts(hsotg);
dwc2_hcd_start(hsotg);
}
@@ -1734,6 +1772,28 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
port_status |= USB_PORT_STAT_TEST;
/* USB_PORT_FEAT_INDICATOR unsupported always 0 */
+ if (hsotg->core_params->dma_desc_fs_enable) {
+ /*
+ * Enable descriptor DMA only if a full speed
+ * device is connected.
+ */
+ if (hsotg->new_connection &&
+ ((port_status &
+ (USB_PORT_STAT_CONNECTION |
+ USB_PORT_STAT_HIGH_SPEED |
+ USB_PORT_STAT_LOW_SPEED)) ==
+ USB_PORT_STAT_CONNECTION)) {
+ u32 hcfg;
+
+ dev_info(hsotg->dev, "Enabling descriptor DMA mode\n");
+ hsotg->core_params->dma_desc_enable = 1;
+ hcfg = dwc2_readl(hsotg->regs + HCFG);
+ hcfg |= HCFG_DESCDMA;
+ dwc2_writel(hcfg, hsotg->regs + HCFG);
+ hsotg->new_connection = false;
+ }
+ }
+
dev_vdbg(hsotg->dev, "port_status=%08x\n", port_status);
*(__le32 *)buf = cpu_to_le32(port_status);
break;
@@ -2298,13 +2358,19 @@ static void dwc2_hcd_reset_func(struct work_struct *work)
{
struct dwc2_hsotg *hsotg = container_of(work, struct dwc2_hsotg,
reset_work.work);
+ unsigned long flags;
u32 hprt0;
dev_dbg(hsotg->dev, "USB RESET function called\n");
+
+ spin_lock_irqsave(&hsotg->lock, flags);
+
hprt0 = dwc2_read_hprt0(hsotg);
hprt0 &= ~HPRT0_RST;
dwc2_writel(hprt0, hsotg->regs + HPRT0);
hsotg->flags.b.port_reset_change = 1;
+
+ spin_unlock_irqrestore(&hsotg->lock, flags);
}
/*
@@ -2366,7 +2432,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
spin_lock_irqsave(&hsotg->lock, flags);
/* Ensure hcd is disconnected */
- dwc2_hcd_disconnect(hsotg);
+ dwc2_hcd_disconnect(hsotg, true);
dwc2_hcd_stop(hsotg);
hsotg->lx_state = DWC2_L3;
hcd->state = HC_STATE_HALT;
@@ -3054,7 +3120,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
dwc2_disable_global_interrupts(hsotg);
/* Initialize the DWC_otg core, and select the Phy type */
- retval = dwc2_core_init(hsotg, true, irq);
+ retval = dwc2_core_init(hsotg, true);
if (retval)
goto error2;
@@ -3122,6 +3188,47 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
if (!hsotg->status_buf)
goto error3;
+ /*
+ * Create kmem caches to handle descriptor buffers in descriptor
+ * DMA mode.
+ * Alignment must be set to 512 bytes.
+ */
+ if (hsotg->core_params->dma_desc_enable ||
+ hsotg->core_params->dma_desc_fs_enable) {
+ hsotg->desc_gen_cache = kmem_cache_create("dwc2-gen-desc",
+ sizeof(struct dwc2_hcd_dma_desc) *
+ MAX_DMA_DESC_NUM_GENERIC, 512, SLAB_CACHE_DMA,
+ NULL);
+ if (!hsotg->desc_gen_cache) {
+ dev_err(hsotg->dev,
+ "unable to create dwc2 generic desc cache\n");
+
+ /*
+ * Disable descriptor dma mode since it will not be
+ * usable.
+ */
+ hsotg->core_params->dma_desc_enable = 0;
+ hsotg->core_params->dma_desc_fs_enable = 0;
+ }
+
+ hsotg->desc_hsisoc_cache = kmem_cache_create("dwc2-hsisoc-desc",
+ sizeof(struct dwc2_hcd_dma_desc) *
+ MAX_DMA_DESC_NUM_HS_ISOC, 512, 0, NULL);
+ if (!hsotg->desc_hsisoc_cache) {
+ dev_err(hsotg->dev,
+ "unable to create dwc2 hs isoc desc cache\n");
+
+ kmem_cache_destroy(hsotg->desc_gen_cache);
+
+ /*
+ * Disable descriptor dma mode since it will not be
+ * usable.
+ */
+ hsotg->core_params->dma_desc_enable = 0;
+ hsotg->core_params->dma_desc_fs_enable = 0;
+ }
+ }
+
hsotg->otg_port = 1;
hsotg->frame_list = NULL;
hsotg->frame_list_dma = 0;
@@ -3145,7 +3252,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
*/
retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (retval < 0)
- goto error3;
+ goto error4;
device_wakeup_enable(hcd->self.controller);
@@ -3155,6 +3262,9 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
return 0;
+error4:
+ kmem_cache_destroy(hsotg->desc_gen_cache);
+ kmem_cache_destroy(hsotg->desc_hsisoc_cache);
error3:
dwc2_hcd_release(hsotg);
error2:
@@ -3195,6 +3305,10 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg)
usb_remove_hcd(hcd);
hsotg->priv = NULL;
+
+ kmem_cache_destroy(hsotg->desc_gen_cache);
+ kmem_cache_destroy(hsotg->desc_hsisoc_cache);
+
dwc2_hcd_release(hsotg);
usb_put_hcd(hcd);
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index f105bada2fd1..8f0a29cefdf7 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -107,6 +107,7 @@ struct dwc2_qh;
* @qh: QH for the transfer being processed by this channel
* @hc_list_entry: For linking to list of host channels
* @desc_list_addr: Current QH's descriptor list DMA address
+ * @desc_list_sz: Current QH's descriptor list size
*
* This structure represents the state of a single host channel when acting in
* host mode. It contains the data items needed to transfer packets to an
@@ -159,6 +160,7 @@ struct dwc2_host_chan {
struct dwc2_qh *qh;
struct list_head hc_list_entry;
dma_addr_t desc_list_addr;
+ u32 desc_list_sz;
};
struct dwc2_hcd_pipe_info {
@@ -251,6 +253,7 @@ enum dwc2_transaction_type {
* schedule
* @desc_list: List of transfer descriptors
* @desc_list_dma: Physical address of desc_list
+ * @desc_list_sz: Size of descriptors list
* @n_bytes: Xfer Bytes array. Each element corresponds to a transfer
* descriptor and indicates original XferSize value for the
* descriptor
@@ -284,6 +287,7 @@ struct dwc2_qh {
struct list_head qh_list_entry;
struct dwc2_hcd_dma_desc *desc_list;
dma_addr_t desc_list_dma;
+ u32 desc_list_sz;
u32 *n_bytes;
unsigned tt_buffer_dirty:1;
};
@@ -340,6 +344,8 @@ struct dwc2_qtd {
u8 isoc_split_pos;
u16 isoc_frame_index;
u16 isoc_split_offset;
+ u16 isoc_td_last;
+ u16 isoc_td_first;
u32 ssplit_out_xfer_count;
u8 error_count;
u8 n_desc;
@@ -378,18 +384,6 @@ static inline void disable_hc_int(struct dwc2_hsotg *hsotg, int chnum, u32 intr)
}
/*
- * Returns the mode of operation, host or device
- */
-static inline int dwc2_is_host_mode(struct dwc2_hsotg *hsotg)
-{
- return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) != 0;
-}
-static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg)
-{
- return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) == 0;
-}
-
-/*
* Reads HPRT0 in preparation to modify. It keeps the WC bits 0 so that if they
* are read as 1, they won't clear when written back.
*/
@@ -535,6 +529,19 @@ static inline bool dbg_perio(void) { return false; }
#define dwc2_max_packet(wmaxpacketsize) ((wmaxpacketsize) & 0x07ff)
/*
+ * Returns true if frame1 index is greater than frame2 index. The comparison
+ * is done modulo FRLISTEN_64_SIZE. This accounts for the rollover of the
+ * frame number when the max index frame number is reached.
+ */
+static inline bool dwc2_frame_idx_num_gt(u16 fr_idx1, u16 fr_idx2)
+{
+ u16 diff = fr_idx1 - fr_idx2;
+ u16 sign = diff & (FRLISTEN_64_SIZE >> 1);
+
+ return diff && !sign;
+}
+
+/*
* Returns true if frame1 is less than or equal to frame2. The comparison is
* done modulo HFNUM_MAX_FRNUM. This accounts for the rollover of the
* frame number when the max frame number is reached.
diff --git a/drivers/usb/dwc2/hcd_ddma.c b/drivers/usb/dwc2/hcd_ddma.c
index 78993aba9335..36606fc33c0d 100644
--- a/drivers/usb/dwc2/hcd_ddma.c
+++ b/drivers/usb/dwc2/hcd_ddma.c
@@ -87,22 +87,31 @@ static u16 dwc2_frame_incr_val(struct dwc2_qh *qh)
static int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
gfp_t flags)
{
- qh->desc_list = dma_alloc_coherent(hsotg->dev,
- sizeof(struct dwc2_hcd_dma_desc) *
- dwc2_max_desc_num(qh), &qh->desc_list_dma,
- flags);
+ struct kmem_cache *desc_cache;
+ if (qh->ep_type == USB_ENDPOINT_XFER_ISOC
+ && qh->dev_speed == USB_SPEED_HIGH)
+ desc_cache = hsotg->desc_hsisoc_cache;
+ else
+ desc_cache = hsotg->desc_gen_cache;
+
+ qh->desc_list_sz = sizeof(struct dwc2_hcd_dma_desc) *
+ dwc2_max_desc_num(qh);
+
+ qh->desc_list = kmem_cache_zalloc(desc_cache, flags | GFP_DMA);
if (!qh->desc_list)
return -ENOMEM;
- memset(qh->desc_list, 0,
- sizeof(struct dwc2_hcd_dma_desc) * dwc2_max_desc_num(qh));
+ qh->desc_list_dma = dma_map_single(hsotg->dev, qh->desc_list,
+ qh->desc_list_sz,
+ DMA_TO_DEVICE);
qh->n_bytes = kzalloc(sizeof(u32) * dwc2_max_desc_num(qh), flags);
if (!qh->n_bytes) {
- dma_free_coherent(hsotg->dev, sizeof(struct dwc2_hcd_dma_desc)
- * dwc2_max_desc_num(qh), qh->desc_list,
- qh->desc_list_dma);
+ dma_unmap_single(hsotg->dev, qh->desc_list_dma,
+ qh->desc_list_sz,
+ DMA_FROM_DEVICE);
+ kfree(qh->desc_list);
qh->desc_list = NULL;
return -ENOMEM;
}
@@ -112,10 +121,18 @@ static int dwc2_desc_list_alloc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
static void dwc2_desc_list_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
{
+ struct kmem_cache *desc_cache;
+
+ if (qh->ep_type == USB_ENDPOINT_XFER_ISOC
+ && qh->dev_speed == USB_SPEED_HIGH)
+ desc_cache = hsotg->desc_hsisoc_cache;
+ else
+ desc_cache = hsotg->desc_gen_cache;
+
if (qh->desc_list) {
- dma_free_coherent(hsotg->dev, sizeof(struct dwc2_hcd_dma_desc)
- * dwc2_max_desc_num(qh), qh->desc_list,
- qh->desc_list_dma);
+ dma_unmap_single(hsotg->dev, qh->desc_list_dma,
+ qh->desc_list_sz, DMA_FROM_DEVICE);
+ kmem_cache_free(desc_cache, qh->desc_list);
qh->desc_list = NULL;
}
@@ -128,21 +145,20 @@ static int dwc2_frame_list_alloc(struct dwc2_hsotg *hsotg, gfp_t mem_flags)
if (hsotg->frame_list)
return 0;
- hsotg->frame_list = dma_alloc_coherent(hsotg->dev,
- 4 * FRLISTEN_64_SIZE,
- &hsotg->frame_list_dma,
- mem_flags);
+ hsotg->frame_list_sz = 4 * FRLISTEN_64_SIZE;
+ hsotg->frame_list = kzalloc(hsotg->frame_list_sz, GFP_ATOMIC | GFP_DMA);
if (!hsotg->frame_list)
return -ENOMEM;
- memset(hsotg->frame_list, 0, 4 * FRLISTEN_64_SIZE);
+ hsotg->frame_list_dma = dma_map_single(hsotg->dev, hsotg->frame_list,
+ hsotg->frame_list_sz,
+ DMA_TO_DEVICE);
+
return 0;
}
static void dwc2_frame_list_free(struct dwc2_hsotg *hsotg)
{
- u32 *frame_list;
- dma_addr_t frame_list_dma;
unsigned long flags;
spin_lock_irqsave(&hsotg->lock, flags);
@@ -152,14 +168,14 @@ static void dwc2_frame_list_free(struct dwc2_hsotg *hsotg)
return;
}
- frame_list = hsotg->frame_list;
- frame_list_dma = hsotg->frame_list_dma;
+ dma_unmap_single(hsotg->dev, hsotg->frame_list_dma,
+ hsotg->frame_list_sz, DMA_FROM_DEVICE);
+
+ kfree(hsotg->frame_list);
hsotg->frame_list = NULL;
spin_unlock_irqrestore(&hsotg->lock, flags);
- dma_free_coherent(hsotg->dev, 4 * FRLISTEN_64_SIZE, frame_list,
- frame_list_dma);
}
static void dwc2_per_sched_enable(struct dwc2_hsotg *hsotg, u32 fr_list_en)
@@ -249,6 +265,15 @@ static void dwc2_update_frame_list(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
j = (j + inc) & (FRLISTEN_64_SIZE - 1);
} while (j != i);
+ /*
+ * Sync frame list since controller will access it if periodic
+ * channel is currently enabled.
+ */
+ dma_sync_single_for_device(hsotg->dev,
+ hsotg->frame_list_dma,
+ hsotg->frame_list_sz,
+ DMA_TO_DEVICE);
+
if (!enable)
return;
@@ -278,6 +303,7 @@ static void dwc2_release_channel_ddma(struct dwc2_hsotg *hsotg,
hsotg->non_periodic_channels--;
} else {
dwc2_update_frame_list(hsotg, qh, 0);
+ hsotg->available_host_channels++;
}
/*
@@ -360,6 +386,8 @@ err0:
*/
void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
{
+ unsigned long flags;
+
dwc2_desc_list_free(hsotg, qh);
/*
@@ -369,8 +397,10 @@ void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
* when it comes here from endpoint disable routine
* channel remains assigned.
*/
+ spin_lock_irqsave(&hsotg->lock, flags);
if (qh->channel)
dwc2_release_channel_ddma(hsotg, qh);
+ spin_unlock_irqrestore(&hsotg->lock, flags);
if ((qh->ep_type == USB_ENDPOINT_XFER_ISOC ||
qh->ep_type == USB_ENDPOINT_XFER_INT) &&
@@ -524,14 +554,23 @@ static void dwc2_fill_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
dma_desc->status = qh->n_bytes[idx] << HOST_DMA_ISOC_NBYTES_SHIFT &
HOST_DMA_ISOC_NBYTES_MASK;
+ /* Set active bit */
+ dma_desc->status |= HOST_DMA_A;
+
+ qh->ntd++;
+ qtd->isoc_frame_index_last++;
+
#ifdef ISOC_URB_GIVEBACK_ASAP
/* Set IOC for each descriptor corresponding to last frame of URB */
if (qtd->isoc_frame_index_last == qtd->urb->packet_count)
dma_desc->status |= HOST_DMA_IOC;
#endif
- qh->ntd++;
- qtd->isoc_frame_index_last++;
+ dma_sync_single_for_device(hsotg->dev,
+ qh->desc_list_dma +
+ (idx * sizeof(struct dwc2_hcd_dma_desc)),
+ sizeof(struct dwc2_hcd_dma_desc),
+ DMA_TO_DEVICE);
}
static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
@@ -539,11 +578,32 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
{
struct dwc2_qtd *qtd;
u32 max_xfer_size;
- u16 idx, inc, n_desc, ntd_max = 0;
+ u16 idx, inc, n_desc = 0, ntd_max = 0;
+ u16 cur_idx;
+ u16 next_idx;
idx = qh->td_last;
inc = qh->interval;
- n_desc = 0;
+ hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
+ cur_idx = dwc2_frame_list_idx(hsotg->frame_number);
+ next_idx = dwc2_desclist_idx_inc(qh->td_last, inc, qh->dev_speed);
+
+ /*
+ * Ensure current frame number didn't overstep last scheduled
+ * descriptor. If it happens, the only way to recover is to move
+ * qh->td_last to current frame number + 1.
+ * So that next isoc descriptor will be scheduled on frame number + 1
+ * and not on a past frame.
+ */
+ if (dwc2_frame_idx_num_gt(cur_idx, next_idx) || (cur_idx == next_idx)) {
+ if (inc < 32) {
+ dev_vdbg(hsotg->dev,
+ "current frame number overstep last descriptor\n");
+ qh->td_last = dwc2_desclist_idx_inc(cur_idx, inc,
+ qh->dev_speed);
+ idx = qh->td_last;
+ }
+ }
if (qh->interval) {
ntd_max = (dwc2_max_desc_num(qh) + qh->interval - 1) /
@@ -556,15 +616,20 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
MAX_ISOC_XFER_SIZE_HS : MAX_ISOC_XFER_SIZE_FS;
list_for_each_entry(qtd, &qh->qtd_list, qtd_list_entry) {
+ if (qtd->in_process &&
+ qtd->isoc_frame_index_last ==
+ qtd->urb->packet_count)
+ continue;
+
+ qtd->isoc_td_first = idx;
while (qh->ntd < ntd_max && qtd->isoc_frame_index_last <
qtd->urb->packet_count) {
- if (n_desc > 1)
- qh->desc_list[n_desc - 1].status |= HOST_DMA_A;
dwc2_fill_host_isoc_dma_desc(hsotg, qtd, qh,
max_xfer_size, idx);
idx = dwc2_desclist_idx_inc(idx, inc, qh->dev_speed);
n_desc++;
}
+ qtd->isoc_td_last = idx;
qtd->in_process = 1;
}
@@ -575,6 +640,11 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
if (qh->ntd == ntd_max) {
idx = dwc2_desclist_idx_dec(qh->td_last, inc, qh->dev_speed);
qh->desc_list[idx].status |= HOST_DMA_IOC;
+ dma_sync_single_for_device(hsotg->dev,
+ qh->desc_list_dma + (idx *
+ sizeof(struct dwc2_hcd_dma_desc)),
+ sizeof(struct dwc2_hcd_dma_desc),
+ DMA_TO_DEVICE);
}
#else
/*
@@ -604,13 +674,12 @@ static void dwc2_init_isoc_dma_desc(struct dwc2_hsotg *hsotg,
idx = dwc2_desclist_idx_dec(qh->td_last, inc, qh->dev_speed);
qh->desc_list[idx].status |= HOST_DMA_IOC;
+ dma_sync_single_for_device(hsotg->dev,
+ qh->desc_list_dma +
+ (idx * sizeof(struct dwc2_hcd_dma_desc)),
+ sizeof(struct dwc2_hcd_dma_desc),
+ DMA_TO_DEVICE);
#endif
-
- if (n_desc) {
- qh->desc_list[n_desc - 1].status |= HOST_DMA_A;
- if (n_desc > 1)
- qh->desc_list[0].status |= HOST_DMA_A;
- }
}
static void dwc2_fill_host_dma_desc(struct dwc2_hsotg *hsotg,
@@ -647,6 +716,12 @@ static void dwc2_fill_host_dma_desc(struct dwc2_hsotg *hsotg,
dma_desc->buf = (u32)chan->xfer_dma;
+ dma_sync_single_for_device(hsotg->dev,
+ qh->desc_list_dma +
+ (n_desc * sizeof(struct dwc2_hcd_dma_desc)),
+ sizeof(struct dwc2_hcd_dma_desc),
+ DMA_TO_DEVICE);
+
/*
* Last (or only) descriptor of IN transfer with actual size less
* than MaxPacket
@@ -697,6 +772,12 @@ static void dwc2_init_non_isoc_dma_desc(struct dwc2_hsotg *hsotg,
"set A bit in desc %d (%p)\n",
n_desc - 1,
&qh->desc_list[n_desc - 1]);
+ dma_sync_single_for_device(hsotg->dev,
+ qh->desc_list_dma +
+ ((n_desc - 1) *
+ sizeof(struct dwc2_hcd_dma_desc)),
+ sizeof(struct dwc2_hcd_dma_desc),
+ DMA_TO_DEVICE);
}
dwc2_fill_host_dma_desc(hsotg, chan, qtd, qh, n_desc);
dev_vdbg(hsotg->dev,
@@ -722,10 +803,19 @@ static void dwc2_init_non_isoc_dma_desc(struct dwc2_hsotg *hsotg,
HOST_DMA_IOC | HOST_DMA_EOL | HOST_DMA_A;
dev_vdbg(hsotg->dev, "set IOC/EOL/A bits in desc %d (%p)\n",
n_desc - 1, &qh->desc_list[n_desc - 1]);
+ dma_sync_single_for_device(hsotg->dev,
+ qh->desc_list_dma + (n_desc - 1) *
+ sizeof(struct dwc2_hcd_dma_desc),
+ sizeof(struct dwc2_hcd_dma_desc),
+ DMA_TO_DEVICE);
if (n_desc > 1) {
qh->desc_list[0].status |= HOST_DMA_A;
dev_vdbg(hsotg->dev, "set A bit in desc 0 (%p)\n",
&qh->desc_list[0]);
+ dma_sync_single_for_device(hsotg->dev,
+ qh->desc_list_dma,
+ sizeof(struct dwc2_hcd_dma_desc),
+ DMA_TO_DEVICE);
}
chan->ntd = n_desc;
}
@@ -800,7 +890,7 @@ static int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
struct dwc2_qtd *qtd,
struct dwc2_qh *qh, u16 idx)
{
- struct dwc2_hcd_dma_desc *dma_desc = &qh->desc_list[idx];
+ struct dwc2_hcd_dma_desc *dma_desc;
struct dwc2_hcd_iso_packet_desc *frame_desc;
u16 remain = 0;
int rc = 0;
@@ -808,6 +898,13 @@ static int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg,
if (!qtd->urb)
return -EINVAL;
+ dma_sync_single_for_cpu(hsotg->dev, qh->desc_list_dma + (idx *
+ sizeof(struct dwc2_hcd_dma_desc)),
+ sizeof(struct dwc2_hcd_dma_desc),
+ DMA_FROM_DEVICE);
+
+ dma_desc = &qh->desc_list[idx];
+
frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index_last];
dma_desc->buf = (u32)(qtd->urb->dma + frame_desc->offset);
if (chan->ep_is_in)
@@ -911,17 +1008,51 @@ static void dwc2_complete_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) {
if (!qtd->in_process)
break;
+
+ /*
+ * Ensure idx corresponds to descriptor where first urb of this
+ * qtd was added. In fact, during isoc desc init, dwc2 may skip
+ * an index if current frame number is already over this index.
+ */
+ if (idx != qtd->isoc_td_first) {
+ dev_vdbg(hsotg->dev,
+ "try to complete %d instead of %d\n",
+ idx, qtd->isoc_td_first);
+ idx = qtd->isoc_td_first;
+ }
+
do {
+ struct dwc2_qtd *qtd_next;
+ u16 cur_idx;
+
rc = dwc2_cmpl_host_isoc_dma_desc(hsotg, chan, qtd, qh,
idx);
if (rc < 0)
return;
idx = dwc2_desclist_idx_inc(idx, qh->interval,
chan->speed);
- if (rc == DWC2_CMPL_STOP)
- goto stop_scan;
+ if (!rc)
+ continue;
+
if (rc == DWC2_CMPL_DONE)
break;
+
+ /* rc == DWC2_CMPL_STOP */
+
+ if (qh->interval >= 32)
+ goto stop_scan;
+
+ qh->td_first = idx;
+ cur_idx = dwc2_frame_list_idx(hsotg->frame_number);
+ qtd_next = list_first_entry(&qh->qtd_list,
+ struct dwc2_qtd,
+ qtd_list_entry);
+ if (dwc2_frame_idx_num_gt(cur_idx,
+ qtd_next->isoc_td_last))
+ break;
+
+ goto stop_scan;
+
} while (idx != qh->td_first);
}
@@ -1029,6 +1160,12 @@ static int dwc2_process_non_isoc_desc(struct dwc2_hsotg *hsotg,
if (!urb)
return -EINVAL;
+ dma_sync_single_for_cpu(hsotg->dev,
+ qh->desc_list_dma + (desc_num *
+ sizeof(struct dwc2_hcd_dma_desc)),
+ sizeof(struct dwc2_hcd_dma_desc),
+ DMA_FROM_DEVICE);
+
dma_desc = &qh->desc_list[desc_num];
n_bytes = qh->n_bytes[desc_num];
dev_vdbg(hsotg->dev,
@@ -1037,7 +1174,10 @@ static int dwc2_process_non_isoc_desc(struct dwc2_hsotg *hsotg,
failed = dwc2_update_non_isoc_urb_state_ddma(hsotg, chan, qtd, dma_desc,
halt_status, n_bytes,
xfer_done);
- if (failed || (*xfer_done && urb->status != -EINPROGRESS)) {
+ if (*xfer_done && urb->status != -EINPROGRESS)
+ failed = 1;
+
+ if (failed) {
dwc2_host_complete(hsotg, qtd, urb->status);
dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
dev_vdbg(hsotg->dev, "failed=%1x xfer_done=%1x status=%08x\n",
@@ -1165,6 +1305,21 @@ void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg,
/* Release the channel if halted or session completed */
if (halt_status != DWC2_HC_XFER_COMPLETE ||
list_empty(&qh->qtd_list)) {
+ struct dwc2_qtd *qtd, *qtd_tmp;
+
+ /*
+ * Kill all remainings QTDs since channel has been
+ * halted.
+ */
+ list_for_each_entry_safe(qtd, qtd_tmp,
+ &qh->qtd_list,
+ qtd_list_entry) {
+ dwc2_host_complete(hsotg, qtd,
+ -ECONNRESET);
+ dwc2_hcd_qtd_unlink_and_free(hsotg,
+ qtd, qh);
+ }
+
/* Halt the channel if session completed */
if (halt_status == DWC2_HC_XFER_COMPLETE)
dwc2_hc_halt(hsotg, chan, halt_status);
@@ -1174,7 +1329,12 @@ void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg,
/* Keep in assigned schedule to continue transfer */
list_move(&qh->qh_list_entry,
&hsotg->periodic_sched_assigned);
- continue_isoc_xfer = 1;
+ /*
+ * If channel has been halted during giveback of urb
+ * then prevent any new scheduling.
+ */
+ if (!chan->halt_status)
+ continue_isoc_xfer = 1;
}
/*
* Todo: Consider the case when period exceeds FrameList size.
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index bda0b21b850f..f8253803a050 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -122,6 +122,9 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
struct dwc2_qh *qh;
enum dwc2_transaction_type tr_type;
+ /* Clear interrupt */
+ dwc2_writel(GINTSTS_SOF, hsotg->regs + GINTSTS);
+
#ifdef DEBUG_SOF
dev_vdbg(hsotg->dev, "--Start of Frame Interrupt--\n");
#endif
@@ -146,9 +149,6 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
tr_type = dwc2_hcd_select_transactions(hsotg);
if (tr_type != DWC2_TRANSACTION_NONE)
dwc2_hcd_queue_transactions(hsotg, tr_type);
-
- /* Clear interrupt */
- dwc2_writel(GINTSTS_SOF, hsotg->regs + GINTSTS);
}
/*
@@ -312,6 +312,7 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0,
if (do_reset) {
*hprt0_modify |= HPRT0_RST;
+ dwc2_writel(*hprt0_modify, hsotg->regs + HPRT0);
queue_delayed_work(hsotg->wq_otg, &hsotg->reset_work,
msecs_to_jiffies(60));
} else {
@@ -347,15 +348,12 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg)
* Set flag and clear if detected
*/
if (hprt0 & HPRT0_CONNDET) {
+ dwc2_writel(hprt0_modify | HPRT0_CONNDET, hsotg->regs + HPRT0);
+
dev_vdbg(hsotg->dev,
"--Port Interrupt HPRT0=0x%08x Port Connect Detected--\n",
hprt0);
- if (hsotg->lx_state != DWC2_L0)
- usb_hcd_resume_root_hub(hsotg->priv);
-
- hsotg->flags.b.port_connect_status_change = 1;
- hsotg->flags.b.port_connect_status = 1;
- hprt0_modify |= HPRT0_CONNDET;
+ dwc2_hcd_connect(hsotg);
/*
* The Hub driver asserts a reset when it sees port connect
@@ -368,27 +366,36 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg)
* Clear if detected - Set internal flag if disabled
*/
if (hprt0 & HPRT0_ENACHG) {
+ dwc2_writel(hprt0_modify | HPRT0_ENACHG, hsotg->regs + HPRT0);
dev_vdbg(hsotg->dev,
" --Port Interrupt HPRT0=0x%08x Port Enable Changed (now %d)--\n",
hprt0, !!(hprt0 & HPRT0_ENA));
- hprt0_modify |= HPRT0_ENACHG;
- if (hprt0 & HPRT0_ENA)
+ if (hprt0 & HPRT0_ENA) {
+ hsotg->new_connection = true;
dwc2_hprt0_enable(hsotg, hprt0, &hprt0_modify);
- else
+ } else {
hsotg->flags.b.port_enable_change = 1;
+ if (hsotg->core_params->dma_desc_fs_enable) {
+ u32 hcfg;
+
+ hsotg->core_params->dma_desc_enable = 0;
+ hsotg->new_connection = false;
+ hcfg = dwc2_readl(hsotg->regs + HCFG);
+ hcfg &= ~HCFG_DESCDMA;
+ dwc2_writel(hcfg, hsotg->regs + HCFG);
+ }
+ }
}
/* Overcurrent Change Interrupt */
if (hprt0 & HPRT0_OVRCURRCHG) {
+ dwc2_writel(hprt0_modify | HPRT0_OVRCURRCHG,
+ hsotg->regs + HPRT0);
dev_vdbg(hsotg->dev,
" --Port Interrupt HPRT0=0x%08x Port Overcurrent Changed--\n",
hprt0);
hsotg->flags.b.port_over_current_change = 1;
- hprt0_modify |= HPRT0_OVRCURRCHG;
}
-
- /* Clear Port Interrupts */
- dwc2_writel(hprt0_modify, hsotg->regs + HPRT0);
}
/*
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 7d8d06cfe3c1..27d402f680a3 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -232,7 +232,7 @@ struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
*/
void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
{
- if (hsotg->core_params->dma_desc_enable > 0) {
+ if (qh->desc_list) {
dwc2_hcd_qh_free_ddma(hsotg, qh);
} else {
/* kfree(NULL) is safe */
diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h
index 553f24606c43..281b57b36ab4 100644
--- a/drivers/usb/dwc2/hw.h
+++ b/drivers/usb/dwc2/hw.h
@@ -769,10 +769,6 @@
#define TSIZ_XFERSIZE_SHIFT 0
#define HCDMA(_ch) HSOTG_REG(0x0514 + 0x20 * (_ch))
-#define HCDMA_DMA_ADDR_MASK (0x1fffff << 11)
-#define HCDMA_DMA_ADDR_SHIFT 11
-#define HCDMA_CTD_MASK (0xff << 3)
-#define HCDMA_CTD_SHIFT 3
#define HCDMAB(_ch) HSOTG_REG(0x051c + 0x20 * (_ch))
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index e61d773cf65e..510f787434b3 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -54,11 +54,44 @@
static const char dwc2_driver_name[] = "dwc2";
+static const struct dwc2_core_params params_hi6220 = {
+ .otg_cap = 2, /* No HNP/SRP capable */
+ .otg_ver = 0, /* 1.3 */
+ .dma_enable = 1,
+ .dma_desc_enable = 0,
+ .dma_desc_fs_enable = 0,
+ .speed = 0, /* High Speed */
+ .enable_dynamic_fifo = 1,
+ .en_multiple_tx_fifo = 1,
+ .host_rx_fifo_size = 512,
+ .host_nperio_tx_fifo_size = 512,
+ .host_perio_tx_fifo_size = 512,
+ .max_transfer_size = 65535,
+ .max_packet_count = 511,
+ .host_channels = 16,
+ .phy_type = 1, /* UTMI */
+ .phy_utmi_width = 8,
+ .phy_ulpi_ddr = 0, /* Single */
+ .phy_ulpi_ext_vbus = 0,
+ .i2c_enable = 0,
+ .ulpi_fs_ls = 0,
+ .host_support_fs_ls_low_power = 0,
+ .host_ls_low_power_phy_clk = 0, /* 48 MHz */
+ .ts_dline = 0,
+ .reload_ctl = 0,
+ .ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
+ GAHBCFG_HBSTLEN_SHIFT,
+ .uframe_sched = 0,
+ .external_id_pin_ctl = -1,
+ .hibernation = -1,
+};
+
static const struct dwc2_core_params params_bcm2835 = {
.otg_cap = 0, /* HNP/SRP capable */
.otg_ver = 0, /* 1.3 */
.dma_enable = 1,
.dma_desc_enable = 0,
+ .dma_desc_fs_enable = 0,
.speed = 0, /* High Speed */
.enable_dynamic_fifo = 1,
.en_multiple_tx_fifo = 1,
@@ -89,6 +122,7 @@ static const struct dwc2_core_params params_rk3066 = {
.otg_ver = -1,
.dma_enable = -1,
.dma_desc_enable = 0,
+ .dma_desc_fs_enable = 0,
.speed = -1,
.enable_dynamic_fifo = 1,
.en_multiple_tx_fifo = -1,
@@ -115,6 +149,71 @@ static const struct dwc2_core_params params_rk3066 = {
.hibernation = -1,
};
+/*
+ * Check the dr_mode against the module configuration and hardware
+ * capabilities.
+ *
+ * The hardware, module, and dr_mode, can each be set to host, device,
+ * or otg. Check that all these values are compatible and adjust the
+ * value of dr_mode if possible.
+ *
+ * actual
+ * HW MOD dr_mode dr_mode
+ * ------------------------------
+ * HST HST any : HST
+ * HST DEV any : ---
+ * HST OTG any : HST
+ *
+ * DEV HST any : ---
+ * DEV DEV any : DEV
+ * DEV OTG any : DEV
+ *
+ * OTG HST any : HST
+ * OTG DEV any : DEV
+ * OTG OTG any : dr_mode
+ */
+static int dwc2_get_dr_mode(struct dwc2_hsotg *hsotg)
+{
+ enum usb_dr_mode mode;
+
+ hsotg->dr_mode = usb_get_dr_mode(hsotg->dev);
+ if (hsotg->dr_mode == USB_DR_MODE_UNKNOWN)
+ hsotg->dr_mode = USB_DR_MODE_OTG;
+
+ mode = hsotg->dr_mode;
+
+ if (dwc2_hw_is_device(hsotg)) {
+ if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) {
+ dev_err(hsotg->dev,
+ "Controller does not support host mode.\n");
+ return -EINVAL;
+ }
+ mode = USB_DR_MODE_PERIPHERAL;
+ } else if (dwc2_hw_is_host(hsotg)) {
+ if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL)) {
+ dev_err(hsotg->dev,
+ "Controller does not support device mode.\n");
+ return -EINVAL;
+ }
+ mode = USB_DR_MODE_HOST;
+ } else {
+ if (IS_ENABLED(CONFIG_USB_DWC2_HOST))
+ mode = USB_DR_MODE_HOST;
+ else if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL))
+ mode = USB_DR_MODE_PERIPHERAL;
+ }
+
+ if (mode != hsotg->dr_mode) {
+ dev_warn(hsotg->dev,
+ "Configuration mismatch. dr_mode forced to %s\n",
+ mode == USB_DR_MODE_HOST ? "host" : "device");
+
+ hsotg->dr_mode = mode;
+ }
+
+ return 0;
+}
+
static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
{
struct platform_device *pdev = to_platform_device(hsotg->dev);
@@ -125,9 +224,11 @@ static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
if (ret)
return ret;
- ret = clk_prepare_enable(hsotg->clk);
- if (ret)
- return ret;
+ if (hsotg->clk) {
+ ret = clk_prepare_enable(hsotg->clk);
+ if (ret)
+ return ret;
+ }
if (hsotg->uphy)
ret = usb_phy_init(hsotg->uphy);
@@ -175,7 +276,8 @@ static int __dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg)
if (ret)
return ret;
- clk_disable_unprepare(hsotg->clk);
+ if (hsotg->clk)
+ clk_disable_unprepare(hsotg->clk);
ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
hsotg->supplies);
@@ -212,14 +314,41 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg)
*/
hsotg->phy = devm_phy_get(hsotg->dev, "usb2-phy");
if (IS_ERR(hsotg->phy)) {
- hsotg->phy = NULL;
+ ret = PTR_ERR(hsotg->phy);
+ switch (ret) {
+ case -ENODEV:
+ case -ENOSYS:
+ hsotg->phy = NULL;
+ break;
+ case -EPROBE_DEFER:
+ return ret;
+ default:
+ dev_err(hsotg->dev, "error getting phy %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (!hsotg->phy) {
hsotg->uphy = devm_usb_get_phy(hsotg->dev, USB_PHY_TYPE_USB2);
- if (IS_ERR(hsotg->uphy))
- hsotg->uphy = NULL;
- else
- hsotg->plat = dev_get_platdata(hsotg->dev);
+ if (IS_ERR(hsotg->uphy)) {
+ ret = PTR_ERR(hsotg->uphy);
+ switch (ret) {
+ case -ENODEV:
+ case -ENXIO:
+ hsotg->uphy = NULL;
+ break;
+ case -EPROBE_DEFER:
+ return ret;
+ default:
+ dev_err(hsotg->dev, "error getting usb phy %d\n",
+ ret);
+ return ret;
+ }
+ }
}
+ hsotg->plat = dev_get_platdata(hsotg->dev);
+
if (hsotg->phy) {
/*
* If using the generic PHY framework, check if the PHY bus
@@ -229,11 +358,6 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg)
hsotg->phyif = GUSBCFG_PHYIF8;
}
- if (!hsotg->phy && !hsotg->uphy && !hsotg->plat) {
- dev_err(hsotg->dev, "no platform data or transceiver defined\n");
- return -EPROBE_DEFER;
- }
-
/* Clock */
hsotg->clk = devm_clk_get(hsotg->dev, "otg");
if (IS_ERR(hsotg->clk)) {
@@ -281,8 +405,28 @@ static int dwc2_driver_remove(struct platform_device *dev)
return 0;
}
+/**
+ * dwc2_driver_shutdown() - Called on device shutdown
+ *
+ * @dev: Platform device
+ *
+ * In specific conditions (involving usb hubs) dwc2 devices can create a
+ * lot of interrupts, even to the point of overwhelming devices running
+ * at low frequencies. Some devices need to do special clock handling
+ * at shutdown-time which may bring the system clock below the threshold
+ * of being able to handle the dwc2 interrupts. Disabling dwc2-irqs
+ * prevents reboots/poweroffs from getting stuck in such cases.
+ */
+static void dwc2_driver_shutdown(struct platform_device *dev)
+{
+ struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
+
+ disable_irq(hsotg->irq);
+}
+
static const struct of_device_id dwc2_of_match_table[] = {
{ .compatible = "brcm,bcm2835-usb", .data = &params_bcm2835 },
+ { .compatible = "hisilicon,hi6220-usb", .data = &params_hi6220 },
{ .compatible = "rockchip,rk3066-usb", .data = &params_rk3066 },
{ .compatible = "snps,dwc2", .data = NULL },
{ .compatible = "samsung,s3c6400-hsotg", .data = NULL},
@@ -310,7 +454,6 @@ static int dwc2_driver_probe(struct platform_device *dev)
struct dwc2_hsotg *hsotg;
struct resource *res;
int retval;
- int irq;
match = of_match_device(dwc2_of_match_table, &dev->dev);
if (match && match->data) {
@@ -323,8 +466,10 @@ static int dwc2_driver_probe(struct platform_device *dev)
/*
* Disable descriptor dma mode by default as the HW can support
* it, but does not support it for SPLIT transactions.
+ * Disable it for FS devices as well.
*/
defparams.dma_desc_enable = 0;
+ defparams.dma_desc_fs_enable = 0;
}
hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL);
@@ -342,20 +487,6 @@ static int dwc2_driver_probe(struct platform_device *dev)
if (retval)
return retval;
- irq = platform_get_irq(dev, 0);
- if (irq < 0) {
- dev_err(&dev->dev, "missing IRQ resource\n");
- return irq;
- }
-
- dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
- irq);
- retval = devm_request_irq(hsotg->dev, irq,
- dwc2_handle_common_intr, IRQF_SHARED,
- dev_name(hsotg->dev), hsotg);
- if (retval)
- return retval;
-
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
hsotg->regs = devm_ioremap_resource(&dev->dev, res);
if (IS_ERR(hsotg->regs))
@@ -364,19 +495,6 @@ static int dwc2_driver_probe(struct platform_device *dev)
dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n",
(unsigned long)res->start, hsotg->regs);
- hsotg->dr_mode = usb_get_dr_mode(&dev->dev);
- if (IS_ENABLED(CONFIG_USB_DWC2_HOST) &&
- hsotg->dr_mode != USB_DR_MODE_HOST) {
- hsotg->dr_mode = USB_DR_MODE_HOST;
- dev_warn(hsotg->dev,
- "Configuration mismatch. Forcing host mode\n");
- } else if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) &&
- hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
- hsotg->dr_mode = USB_DR_MODE_PERIPHERAL;
- dev_warn(hsotg->dev,
- "Configuration mismatch. Forcing peripheral mode\n");
- }
-
retval = dwc2_lowlevel_hw_init(hsotg);
if (retval)
return retval;
@@ -390,11 +508,29 @@ static int dwc2_driver_probe(struct platform_device *dev)
dwc2_set_all_params(hsotg->core_params, -1);
+ hsotg->irq = platform_get_irq(dev, 0);
+ if (hsotg->irq < 0) {
+ dev_err(&dev->dev, "missing IRQ resource\n");
+ return hsotg->irq;
+ }
+
+ dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
+ hsotg->irq);
+ retval = devm_request_irq(hsotg->dev, hsotg->irq,
+ dwc2_handle_common_intr, IRQF_SHARED,
+ dev_name(hsotg->dev), hsotg);
+ if (retval)
+ return retval;
+
retval = dwc2_lowlevel_hw_enable(hsotg);
if (retval)
return retval;
- /* Detect config values from hardware */
+ retval = dwc2_get_dr_mode(hsotg);
+ if (retval)
+ return retval;
+
+ /* Reset the controller and detect hardware config values */
retval = dwc2_get_hwparams(hsotg);
if (retval)
goto error;
@@ -402,15 +538,17 @@ static int dwc2_driver_probe(struct platform_device *dev)
/* Validate parameter values */
dwc2_set_parameters(hsotg, params);
+ dwc2_force_dr_mode(hsotg);
+
if (hsotg->dr_mode != USB_DR_MODE_HOST) {
- retval = dwc2_gadget_init(hsotg, irq);
+ retval = dwc2_gadget_init(hsotg, hsotg->irq);
if (retval)
goto error;
hsotg->gadget_enabled = 1;
}
if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
- retval = dwc2_hcd_init(hsotg, irq);
+ retval = dwc2_hcd_init(hsotg, hsotg->irq);
if (retval) {
if (hsotg->gadget_enabled)
dwc2_hsotg_remove(hsotg);
@@ -477,6 +615,7 @@ static struct platform_driver dwc2_platform_driver = {
},
.probe = dwc2_driver_probe,
.remove = dwc2_driver_remove,
+ .shutdown = dwc2_driver_shutdown,
};
module_platform_driver(dwc2_platform_driver);
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 5a42c4590402..a64ce1c94d6d 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -87,6 +87,15 @@ config USB_DWC3_KEYSTONE
Support of USB2/3 functionality in TI Keystone2 platforms.
Say 'Y' or 'M' here if you have one such device
+config USB_DWC3_OF_SIMPLE
+ tristate "Generic OF Simple Glue Layer"
+ depends on OF && COMMON_CLK
+ default USB_DWC3
+ help
+ Support USB2/3 functionality in simple SoC integrations.
+ Currently supports Xilinx and Qualcomm DWC USB3 IP.
+ Say 'Y' or 'M' if you have one such device.
+
config USB_DWC3_ST
tristate "STMicroelectronics Platforms"
depends on ARCH_STI && OF
@@ -96,12 +105,4 @@ config USB_DWC3_ST
inside (i.e. STiH407).
Say 'Y' or 'M' if you have one such device.
-config USB_DWC3_QCOM
- tristate "Qualcomm Platforms"
- depends on ARCH_QCOM || COMPILE_TEST
- default USB_DWC3
- help
- Recent Qualcomm SoCs ship with one DesignWare Core USB3 IP inside,
- say 'Y' or 'M' if you have one such device.
-
endif
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index acc951d46c27..22420e17d68b 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -37,5 +37,5 @@ obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o
obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o
obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o
obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o
-obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o
+obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o
obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 22b4797383cd..de5e01f41bc2 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -272,7 +272,8 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc)
for (n = 0; n < dwc->num_event_buffers; n++) {
evt = dwc->ev_buffs[n];
- dev_dbg(dwc->dev, "Event buf %p dma %08llx length %d\n",
+ dwc3_trace(trace_dwc3_core,
+ "Event buf %p dma %08llx length %d\n",
evt->buf, (unsigned long long) evt->dma,
evt->length);
@@ -608,12 +609,13 @@ static int dwc3_core_init(struct dwc3 *dwc)
reg |= DWC3_GCTL_GBLHIBERNATIONEN;
break;
default:
- dev_dbg(dwc->dev, "No power optimization available\n");
+ dwc3_trace(trace_dwc3_core, "No power optimization available\n");
}
/* check if current dwc3 is on simulation board */
if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) {
- dev_dbg(dwc->dev, "it is on FPGA board\n");
+ dwc3_trace(trace_dwc3_core,
+ "running on FPGA platform\n");
dwc->is_fpga = true;
}
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 36f1cb74588c..29130682e547 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -37,6 +37,7 @@
#define DWC3_MSG_MAX 500
/* Global constants */
+#define DWC3_ZLP_BUF_SIZE 1024 /* size of a superspeed bulk */
#define DWC3_EP0_BOUNCE_SIZE 512
#define DWC3_ENDPOINTS_NUM 32
#define DWC3_XHCI_RESOURCES_NUM 2
@@ -647,6 +648,7 @@ struct dwc3_scratchpad_array {
* @ctrl_req: usb control request which is used for ep0
* @ep0_trb: trb which is used for the ctrl_req
* @ep0_bounce: bounce buffer for ep0
+ * @zlp_buf: used when request->zero is set
* @setup_buf: used while precessing STD USB requests
* @ctrl_req_addr: dma address of ctrl_req
* @ep0_trb: dma address of ep0_trb
@@ -734,6 +736,7 @@ struct dwc3 {
struct usb_ctrlrequest *ctrl_req;
struct dwc3_trb *ep0_trb;
void *ep0_bounce;
+ void *zlp_buf;
void *scratchbuf;
u8 *setup_buf;
dma_addr_t ctrl_req_addr;
diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
new file mode 100644
index 000000000000..9c9f74155066
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-of-simple.c
@@ -0,0 +1,180 @@
+/**
+ * dwc3-of-simple.c - OF glue layer for simple integrations
+ *
+ * Copyright (c) 2015 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Felipe Balbi <balbi@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * This is a combination of the old dwc3-qcom.c by Ivan T. Ivanov
+ * <iivanov@mm-sol.com> and the original patch adding support for Xilinx' SoC
+ * by Subbaraya Sundeep Bhatta <subbaraya.sundeep.bhatta@xilinx.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+
+struct dwc3_of_simple {
+ struct device *dev;
+ struct clk **clks;
+ int num_clocks;
+};
+
+static int dwc3_of_simple_probe(struct platform_device *pdev)
+{
+ struct dwc3_of_simple *simple;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+
+ int ret;
+ int i;
+
+ simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
+ if (!simple)
+ return -ENOMEM;
+
+ ret = of_clk_get_parent_count(np);
+ if (ret < 0)
+ return ret;
+
+ simple->num_clocks = ret;
+
+ simple->clks = devm_kcalloc(dev, simple->num_clocks,
+ sizeof(struct clk *), GFP_KERNEL);
+ if (!simple->clks)
+ return -ENOMEM;
+
+ simple->dev = dev;
+
+ for (i = 0; i < simple->num_clocks; i++) {
+ struct clk *clk;
+
+ clk = of_clk_get(np, i);
+ if (IS_ERR(clk)) {
+ while (--i >= 0)
+ clk_put(simple->clks[i]);
+ return PTR_ERR(clk);
+ }
+
+ ret = clk_prepare_enable(clk);
+ if (ret < 0) {
+ while (--i >= 0) {
+ clk_disable_unprepare(simple->clks[i]);
+ clk_put(simple->clks[i]);
+ }
+ clk_put(clk);
+
+ return ret;
+ }
+
+ simple->clks[i] = clk;
+ }
+
+ ret = of_platform_populate(np, NULL, NULL, dev);
+ if (ret) {
+ for (i = 0; i < simple->num_clocks; i++) {
+ clk_disable_unprepare(simple->clks[i]);
+ clk_put(simple->clks[i]);
+ }
+
+ return ret;
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+
+ return 0;
+}
+
+static int dwc3_of_simple_remove(struct platform_device *pdev)
+{
+ struct dwc3_of_simple *simple = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+ int i;
+
+ for (i = 0; i < simple->num_clocks; i++) {
+ clk_unprepare(simple->clks[i]);
+ clk_put(simple->clks[i]);
+ }
+
+ of_platform_depopulate(dev);
+
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int dwc3_of_simple_runtime_suspend(struct device *dev)
+{
+ struct dwc3_of_simple *simple = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < simple->num_clocks; i++)
+ clk_disable(simple->clks[i]);
+
+ return 0;
+}
+
+static int dwc3_of_simple_runtime_resume(struct device *dev)
+{
+ struct dwc3_of_simple *simple = dev_get_drvdata(dev);
+ int ret;
+ int i;
+
+ for (i = 0; i < simple->num_clocks; i++) {
+ ret = clk_enable(simple->clks[i]);
+ if (ret < 0) {
+ while (--i >= 0)
+ clk_disable(simple->clks[i]);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(dwc3_of_simple_runtime_suspend,
+ dwc3_of_simple_runtime_resume, NULL)
+};
+
+static const struct of_device_id of_dwc3_simple_match[] = {
+ { .compatible = "qcom,dwc3" },
+ { .compatible = "xlnx,zynqmp-dwc3" },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_dwc3_simple_match);
+
+static struct platform_driver dwc3_of_simple_driver = {
+ .probe = dwc3_of_simple_probe,
+ .remove = dwc3_of_simple_remove,
+ .driver = {
+ .name = "dwc3-of-simple",
+ .of_match_table = of_dwc3_simple_match,
+ },
+};
+
+module_platform_driver(dwc3_of_simple_driver);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("DesignWare USB3 OF Simple Glue Layer");
+MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
deleted file mode 100644
index 088026048f49..000000000000
--- a/drivers/usb/dwc3/dwc3-qcom.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/* Copyright (c) 2013-2014, 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/clk.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/platform_device.h>
-
-struct dwc3_qcom {
- struct device *dev;
-
- struct clk *core_clk;
- struct clk *iface_clk;
- struct clk *sleep_clk;
-};
-
-static int dwc3_qcom_probe(struct platform_device *pdev)
-{
- struct device_node *node = pdev->dev.of_node;
- struct dwc3_qcom *qdwc;
- int ret;
-
- qdwc = devm_kzalloc(&pdev->dev, sizeof(*qdwc), GFP_KERNEL);
- if (!qdwc)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, qdwc);
-
- qdwc->dev = &pdev->dev;
-
- qdwc->core_clk = devm_clk_get(qdwc->dev, "core");
- if (IS_ERR(qdwc->core_clk)) {
- dev_err(qdwc->dev, "failed to get core clock\n");
- return PTR_ERR(qdwc->core_clk);
- }
-
- qdwc->iface_clk = devm_clk_get(qdwc->dev, "iface");
- if (IS_ERR(qdwc->iface_clk)) {
- dev_info(qdwc->dev, "failed to get optional iface clock\n");
- qdwc->iface_clk = NULL;
- }
-
- qdwc->sleep_clk = devm_clk_get(qdwc->dev, "sleep");
- if (IS_ERR(qdwc->sleep_clk)) {
- dev_info(qdwc->dev, "failed to get optional sleep clock\n");
- qdwc->sleep_clk = NULL;
- }
-
- ret = clk_prepare_enable(qdwc->core_clk);
- if (ret) {
- dev_err(qdwc->dev, "failed to enable core clock\n");
- goto err_core;
- }
-
- ret = clk_prepare_enable(qdwc->iface_clk);
- if (ret) {
- dev_err(qdwc->dev, "failed to enable optional iface clock\n");
- goto err_iface;
- }
-
- ret = clk_prepare_enable(qdwc->sleep_clk);
- if (ret) {
- dev_err(qdwc->dev, "failed to enable optional sleep clock\n");
- goto err_sleep;
- }
-
- ret = of_platform_populate(node, NULL, NULL, qdwc->dev);
- if (ret) {
- dev_err(qdwc->dev, "failed to register core - %d\n", ret);
- goto err_clks;
- }
-
- return 0;
-
-err_clks:
- clk_disable_unprepare(qdwc->sleep_clk);
-err_sleep:
- clk_disable_unprepare(qdwc->iface_clk);
-err_iface:
- clk_disable_unprepare(qdwc->core_clk);
-err_core:
- return ret;
-}
-
-static int dwc3_qcom_remove(struct platform_device *pdev)
-{
- struct dwc3_qcom *qdwc = platform_get_drvdata(pdev);
-
- of_platform_depopulate(&pdev->dev);
-
- clk_disable_unprepare(qdwc->sleep_clk);
- clk_disable_unprepare(qdwc->iface_clk);
- clk_disable_unprepare(qdwc->core_clk);
-
- return 0;
-}
-
-static const struct of_device_id of_dwc3_match[] = {
- { .compatible = "qcom,dwc3" },
- { /* Sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, of_dwc3_match);
-
-static struct platform_driver dwc3_qcom_driver = {
- .probe = dwc3_qcom_probe,
- .remove = dwc3_qcom_remove,
- .driver = {
- .name = "qcom-dwc3",
- .of_match_table = of_dwc3_match,
- },
-};
-
-module_platform_driver(dwc3_qcom_driver);
-
-MODULE_ALIAS("platform:qcom-dwc3");
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("DesignWare USB3 QCOM Glue Layer");
-MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 5320e939e090..3a9354abcb68 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -817,6 +817,8 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
if (status == DWC3_TRBSTS_SETUP_PENDING) {
+ dwc->setup_packet_pending = true;
+
dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
if (r)
@@ -916,8 +918,10 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
}
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
- if (status == DWC3_TRBSTS_SETUP_PENDING)
+ if (status == DWC3_TRBSTS_SETUP_PENDING) {
+ dwc->setup_packet_pending = true;
dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
+ }
dwc->ep0state = EP0_SETUP_PHASE;
dwc3_ep0_out_start(dwc);
@@ -971,7 +975,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
dep->number);
if (ret) {
- dev_dbg(dwc->dev, "failed to map request\n");
+ dwc3_trace(trace_dwc3_ep0, "failed to map request\n");
return;
}
@@ -999,7 +1003,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
dep->number);
if (ret) {
- dev_dbg(dwc->dev, "failed to map request\n");
+ dwc3_trace(trace_dwc3_ep0, "failed to map request\n");
return;
}
@@ -1063,8 +1067,6 @@ static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
- dwc->setup_packet_pending = true;
-
switch (event->status) {
case DEPEVT_STATUS_CONTROL_DATA:
dwc3_trace(trace_dwc3_ep0, "Control Data");
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index e24a01cc98df..af023a81a0b0 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -265,9 +265,6 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
usb_gadget_unmap_request(&dwc->gadget, &req->request,
req->direction);
- dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
- req, dep->name, req->request.actual,
- req->request.length, status);
trace_dwc3_gadget_giveback(req);
spin_unlock(&dwc->lock);
@@ -664,11 +661,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
dep = to_dwc3_ep(ep);
dwc = dep->dwc;
- if (dep->flags & DWC3_EP_ENABLED) {
- dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n",
- dep->name);
+ if (dev_WARN_ONCE(dwc->dev, dep->flags & DWC3_EP_ENABLED,
+ "%s is already enabled\n",
+ dep->name))
return 0;
- }
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false, false);
@@ -692,11 +688,10 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep)
dep = to_dwc3_ep(ep);
dwc = dep->dwc;
- if (!(dep->flags & DWC3_EP_ENABLED)) {
- dev_WARN_ONCE(dwc->dev, true, "%s is already disabled\n",
- dep->name);
+ if (dev_WARN_ONCE(dwc->dev, !(dep->flags & DWC3_EP_ENABLED),
+ "%s is already disabled\n",
+ dep->name))
return 0;
- }
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep_disable(dep);
@@ -985,8 +980,6 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
cmd |= DWC3_DEPCMD_PARAM(cmd_param);
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
if (ret < 0) {
- dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n");
-
/*
* FIXME we need to iterate over the list of requests
* here and stop, unmap, free and del each of the linked
@@ -1044,6 +1037,20 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
struct dwc3 *dwc = dep->dwc;
int ret;
+ if (!dep->endpoint.desc) {
+ dwc3_trace(trace_dwc3_gadget,
+ "trying to queue request %p to disabled %s\n",
+ &req->request, dep->endpoint.name);
+ return -ESHUTDOWN;
+ }
+
+ if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
+ &req->request, req->dep->name)) {
+ dwc3_trace(trace_dwc3_gadget, "request %p belongs to '%s'\n",
+ &req->request, req->dep->name);
+ return -EINVAL;
+ }
+
req->request.actual = 0;
req->request.status = -EINPROGRESS;
req->direction = dep->direction;
@@ -1078,6 +1085,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* little bit faster.
*/
if (!usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
+ !usb_endpoint_xfer_int(dep->endpoint.desc) &&
!(dep->flags & DWC3_EP_BUSY)) {
ret = __dwc3_gadget_kick_transfer(dep, 0, true);
goto out;
@@ -1140,7 +1148,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
out:
if (ret && ret != -EBUSY)
- dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
+ dwc3_trace(trace_dwc3_gadget,
+ "%s: failed to kick transfers\n",
dep->name);
if (ret == -EBUSY)
ret = 0;
@@ -1148,6 +1157,32 @@ out:
return ret;
}
+static void __dwc3_gadget_ep_zlp_complete(struct usb_ep *ep,
+ struct usb_request *request)
+{
+ dwc3_gadget_ep_free_request(ep, request);
+}
+
+static int __dwc3_gadget_ep_queue_zlp(struct dwc3 *dwc, struct dwc3_ep *dep)
+{
+ struct dwc3_request *req;
+ struct usb_request *request;
+ struct usb_ep *ep = &dep->endpoint;
+
+ dwc3_trace(trace_dwc3_gadget, "queueing ZLP\n");
+ request = dwc3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
+ if (!request)
+ return -ENOMEM;
+
+ request->length = 0;
+ request->buf = dwc->zlp_buf;
+ request->complete = __dwc3_gadget_ep_zlp_complete;
+
+ req = to_dwc3_request(request);
+
+ return __dwc3_gadget_ep_queue(dep, req);
+}
+
static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
gfp_t gfp_flags)
{
@@ -1160,22 +1195,18 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
int ret;
spin_lock_irqsave(&dwc->lock, flags);
- if (!dep->endpoint.desc) {
- dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
- request, ep->name);
- ret = -ESHUTDOWN;
- goto out;
- }
-
- if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
- request, req->dep->name)) {
- ret = -EINVAL;
- goto out;
- }
-
ret = __dwc3_gadget_ep_queue(dep, req);
-out:
+ /*
+ * Okay, here's the thing, if gadget driver has requested for a ZLP by
+ * setting request->zero, instead of doing magic, we will just queue an
+ * extra usb_request ourselves so that it gets handled the same way as
+ * any other request.
+ */
+ if (ret == 0 && request->zero && request->length &&
+ (request->length % ep->maxpacket == 0))
+ ret = __dwc3_gadget_ep_queue_zlp(dwc, dep);
+
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -1245,7 +1276,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
if (!protocol && ((dep->direction && dep->flags & DWC3_EP_BUSY) ||
(!list_empty(&dep->req_queued) ||
!list_empty(&dep->request_list)))) {
- dev_dbg(dwc->dev, "%s: pending request, cannot halt\n",
+ dwc3_trace(trace_dwc3_gadget,
+ "%s: pending request, cannot halt\n",
dep->name);
return -EAGAIN;
}
@@ -1372,7 +1404,7 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
speed = reg & DWC3_DSTS_CONNECTSPD;
if (speed == DWC3_DSTS_SUPERSPEED) {
- dev_dbg(dwc->dev, "no wakeup on SuperSpeed\n");
+ dwc3_trace(trace_dwc3_gadget, "no wakeup on SuperSpeed\n");
ret = -EINVAL;
goto out;
}
@@ -1384,8 +1416,9 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */
break;
default:
- dev_dbg(dwc->dev, "can't wakeup from link state %d\n",
- link_state);
+ dwc3_trace(trace_dwc3_gadget,
+ "can't wakeup from '%s'\n",
+ dwc3_gadget_link_string(link_state));
ret = -EINVAL;
goto out;
}
@@ -1824,7 +1857,8 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
if (count) {
trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
- dev_dbg(dwc->dev, "incomplete IN transfer %s\n",
+ dwc3_trace(trace_dwc3_gadget,
+ "%s: incomplete IN transfer\n",
dep->name);
/*
* If missed isoc occurred and there is
@@ -1886,10 +1920,9 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
do {
req = next_request(&dep->req_queued);
- if (!req) {
- WARN_ON_ONCE(1);
+ if (WARN_ON_ONCE(!req))
return 1;
- }
+
i = 0;
do {
slot = req->start_slot + i;
@@ -2003,7 +2036,8 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dep->resource_index = 0;
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n",
+ dwc3_trace(trace_dwc3_gadget,
+ "%s is an Isochronous endpoint\n",
dep->name);
return;
}
@@ -2030,7 +2064,8 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
if (!ret || ret == -EBUSY)
return;
- dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
+ dwc3_trace(trace_dwc3_gadget,
+ "%s: failed to kick transfers\n",
dep->name);
}
@@ -2052,11 +2087,12 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
case DEPEVT_STREAMEVT_NOTFOUND:
/* FALLTHROUGH */
default:
- dev_dbg(dwc->dev, "Couldn't find suitable stream\n");
+ dwc3_trace(trace_dwc3_gadget,
+ "unable to find suitable stream\n");
}
break;
case DWC3_DEPEVT_RXTXFIFOEVT:
- dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);
+ dwc3_trace(trace_dwc3_gadget, "%s FIFO Overrun\n", dep->name);
break;
case DWC3_DEPEVT_EPCMDCMPLT:
dwc3_trace(trace_dwc3_gadget, "Endpoint Command Complete");
@@ -2229,8 +2265,8 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
*
* Our suggested workaround is to follow the Disconnect
* Event steps here, instead, based on a setup_packet_pending
- * flag. Such flag gets set whenever we have a XferNotReady
- * event on EP0 and gets cleared on XferComplete for the
+ * flag. Such flag gets set whenever we have a SETUP_PENDING
+ * status for EP0 TRBs and gets cleared on XferComplete for the
* same endpoint.
*
* Refers to:
@@ -2743,6 +2779,12 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err3;
}
+ dwc->zlp_buf = kzalloc(DWC3_ZLP_BUF_SIZE, GFP_KERNEL);
+ if (!dwc->zlp_buf) {
+ ret = -ENOMEM;
+ goto err4;
+ }
+
dwc->gadget.ops = &dwc3_gadget_ops;
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->gadget.sg_supported = true;
@@ -2784,16 +2826,19 @@ int dwc3_gadget_init(struct dwc3 *dwc)
ret = dwc3_gadget_init_endpoints(dwc);
if (ret)
- goto err4;
+ goto err5;
ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
if (ret) {
dev_err(dwc->dev, "failed to register udc\n");
- goto err4;
+ goto err5;
}
return 0;
+err5:
+ kfree(dwc->zlp_buf);
+
err4:
dwc3_gadget_free_endpoints(dwc);
dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
@@ -2826,6 +2871,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
dwc->ep0_bounce, dwc->ep0_bounce_addr);
kfree(dwc->setup_buf);
+ kfree(dwc->zlp_buf);
dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
dwc->ep0_trb, dwc->ep0_trb_addr);
diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h
index 9c10669ab91f..3ac7252f4427 100644
--- a/drivers/usb/dwc3/trace.h
+++ b/drivers/usb/dwc3/trace.h
@@ -117,6 +117,9 @@ DECLARE_EVENT_CLASS(dwc3_log_request,
__field(unsigned, actual)
__field(unsigned, length)
__field(int, status)
+ __field(int, zero)
+ __field(int, short_not_ok)
+ __field(int, no_interrupt)
),
TP_fast_assign(
snprintf(__get_str(name), DWC3_MSG_MAX, "%s", req->dep->name);
@@ -124,9 +127,15 @@ DECLARE_EVENT_CLASS(dwc3_log_request,
__entry->actual = req->request.actual;
__entry->length = req->request.length;
__entry->status = req->request.status;
+ __entry->zero = req->request.zero;
+ __entry->short_not_ok = req->request.short_not_ok;
+ __entry->no_interrupt = req->request.no_interrupt;
),
- TP_printk("%s: req %p length %u/%u ==> %d",
+ TP_printk("%s: req %p length %u/%u %s%s%s ==> %d",
__get_str(name), __entry->req, __entry->actual, __entry->length,
+ __entry->zero ? "Z" : "z",
+ __entry->short_not_ok ? "S" : "s",
+ __entry->no_interrupt ? "i" : "I",
__entry->status
)
);
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 33834aa09ed4..be5aab9c13f2 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -127,6 +127,12 @@ config USB_GADGET_STORAGE_NUM_BUFFERS
a module parameter as well.
If unsure, say 2.
+config U_SERIAL_CONSOLE
+ bool "Serial gadget console support"
+ depends on USB_G_SERIAL
+ help
+ It supports the serial gadget can be used as a console.
+
source "drivers/usb/gadget/udc/Kconfig"
#
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 163d305e1200..590c44989e5e 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -56,7 +56,6 @@ struct gadget_info {
struct list_head string_list;
struct list_head available_func;
- const char *udc_name;
struct usb_composite_driver composite;
struct usb_composite_dev cdev;
bool use_os_desc;
@@ -233,21 +232,23 @@ static ssize_t gadget_dev_desc_bcdUSB_store(struct config_item *item,
static ssize_t gadget_dev_desc_UDC_show(struct config_item *item, char *page)
{
- return sprintf(page, "%s\n", to_gadget_info(item)->udc_name ?: "");
+ char *udc_name = to_gadget_info(item)->composite.gadget_driver.udc_name;
+
+ return sprintf(page, "%s\n", udc_name ?: "");
}
static int unregister_gadget(struct gadget_info *gi)
{
int ret;
- if (!gi->udc_name)
+ if (!gi->composite.gadget_driver.udc_name)
return -ENODEV;
ret = usb_gadget_unregister_driver(&gi->composite.gadget_driver);
if (ret)
return ret;
- kfree(gi->udc_name);
- gi->udc_name = NULL;
+ kfree(gi->composite.gadget_driver.udc_name);
+ gi->composite.gadget_driver.udc_name = NULL;
return 0;
}
@@ -271,14 +272,16 @@ static ssize_t gadget_dev_desc_UDC_store(struct config_item *item,
if (ret)
goto err;
} else {
- if (gi->udc_name) {
+ if (gi->composite.gadget_driver.udc_name) {
ret = -EBUSY;
goto err;
}
- ret = usb_udc_attach_driver(name, &gi->composite.gadget_driver);
- if (ret)
+ gi->composite.gadget_driver.udc_name = name;
+ ret = usb_gadget_probe_driver(&gi->composite.gadget_driver);
+ if (ret) {
+ gi->composite.gadget_driver.udc_name = NULL;
goto err;
- gi->udc_name = name;
+ }
}
mutex_unlock(&gi->lock);
return len;
@@ -427,9 +430,9 @@ static int config_usb_cfg_unlink(
* remove the function.
*/
mutex_lock(&gi->lock);
- if (gi->udc_name)
+ if (gi->composite.gadget_driver.udc_name)
unregister_gadget(gi);
- WARN_ON(gi->udc_name);
+ WARN_ON(gi->composite.gadget_driver.udc_name);
list_for_each_entry(f, &cfg->func_list, list) {
if (f->fi == fi) {
@@ -873,10 +876,10 @@ static int os_desc_unlink(struct config_item *os_desc_ci,
struct usb_composite_dev *cdev = &gi->cdev;
mutex_lock(&gi->lock);
- if (gi->udc_name)
+ if (gi->composite.gadget_driver.udc_name)
unregister_gadget(gi);
cdev->os_desc_config = NULL;
- WARN_ON(gi->udc_name);
+ WARN_ON(gi->composite.gadget_driver.udc_name);
mutex_unlock(&gi->lock);
return 0;
}
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index adc6d52efa46..cf43e9e18368 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -423,7 +423,7 @@ static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
spin_unlock_irq(&ffs->ev.waitq.lock);
mutex_unlock(&ffs->mutex);
- return unlikely(__copy_to_user(buf, events, size)) ? -EFAULT : size;
+ return unlikely(copy_to_user(buf, events, size)) ? -EFAULT : size;
}
static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
@@ -513,7 +513,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
/* unlocks spinlock */
ret = __ffs_ep0_queue_wait(ffs, data, len);
- if (likely(ret > 0) && unlikely(__copy_to_user(buf, data, len)))
+ if (likely(ret > 0) && unlikely(copy_to_user(buf, data, len)))
ret = -EFAULT;
goto done_mutex;
@@ -3493,7 +3493,7 @@ static char *ffs_prepare_buffer(const char __user *buf, size_t len)
if (unlikely(!data))
return ERR_PTR(-ENOMEM);
- if (unlikely(__copy_from_user(data, buf, len))) {
+ if (unlikely(copy_from_user(data, buf, len))) {
kfree(data);
return ERR_PTR(-EFAULT);
}
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 42acb45e1ab4..fb1fe96d3215 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -23,6 +23,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/device.h>
+#include <linux/kfifo.h>
#include <sound/core.h>
#include <sound/initval.h>
@@ -75,6 +76,7 @@ struct f_midi {
struct usb_ep *in_ep, *out_ep;
struct snd_card *card;
struct snd_rawmidi *rmidi;
+ u8 ms_id;
struct snd_rawmidi_substream *in_substream[MAX_PORTS];
struct snd_rawmidi_substream *out_substream[MAX_PORTS];
@@ -87,6 +89,9 @@ struct f_midi {
int index;
char *id;
unsigned int buflen, qlen;
+ /* This fifo is used as a buffer ring for pre-allocated IN usb_requests */
+ DECLARE_KFIFO_PTR(in_req_fifo, struct usb_request *);
+ unsigned int in_last_port;
};
static inline struct f_midi *func_to_midi(struct usb_function *f)
@@ -94,7 +99,7 @@ static inline struct f_midi *func_to_midi(struct usb_function *f)
return container_of(f, struct f_midi, func);
}
-static void f_midi_transmit(struct f_midi *midi, struct usb_request *req);
+static void f_midi_transmit(struct f_midi *midi);
DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
@@ -201,12 +206,6 @@ static inline struct usb_request *midi_alloc_ep_req(struct usb_ep *ep,
return alloc_ep_req(ep, length, length);
}
-static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
-{
- kfree(req->buf);
- usb_ep_free_request(ep, req);
-}
-
static const uint8_t f_midi_cin_length[] = {
0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
};
@@ -258,7 +257,8 @@ f_midi_complete(struct usb_ep *ep, struct usb_request *req)
} else if (ep == midi->in_ep) {
/* Our transmit completed. See if there's more to go.
* f_midi_transmit eats req, don't queue it again. */
- f_midi_transmit(midi, req);
+ req->length = 0;
+ f_midi_transmit(midi);
return;
}
break;
@@ -269,10 +269,12 @@ f_midi_complete(struct usb_ep *ep, struct usb_request *req)
case -ESHUTDOWN: /* disconnect from host */
VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status,
req->actual, req->length);
- if (ep == midi->out_ep)
+ if (ep == midi->out_ep) {
f_midi_handle_out_data(ep, req);
-
- free_ep_req(ep, req);
+ /* We don't need to free IN requests because it's handled
+ * by the midi->in_req_fifo. */
+ free_ep_req(ep, req);
+ }
return;
case -EOVERFLOW: /* buffer overrun on read means that
@@ -324,12 +326,11 @@ static int f_midi_start_ep(struct f_midi *midi,
static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_midi *midi = func_to_midi(f);
- struct usb_composite_dev *cdev = f->config->cdev;
unsigned i;
int err;
- /* For Control Device interface we do nothing */
- if (intf == 0)
+ /* we only set alt for MIDIStreaming interface */
+ if (intf != midi->ms_id)
return 0;
err = f_midi_start_ep(midi, f, midi->in_ep);
@@ -340,23 +341,19 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (err)
return err;
- usb_ep_disable(midi->out_ep);
+ /* pre-allocate write usb requests to use on f_midi_transmit. */
+ while (kfifo_avail(&midi->in_req_fifo)) {
+ struct usb_request *req =
+ midi_alloc_ep_req(midi->in_ep, midi->buflen);
- err = config_ep_by_speed(midi->gadget, f, midi->out_ep);
- if (err) {
- ERROR(cdev, "can't configure %s: %d\n",
- midi->out_ep->name, err);
- return err;
- }
+ if (req == NULL)
+ return -ENOMEM;
- err = usb_ep_enable(midi->out_ep);
- if (err) {
- ERROR(cdev, "can't start %s: %d\n",
- midi->out_ep->name, err);
- return err;
- }
+ req->length = 0;
+ req->complete = f_midi_complete;
- midi->out_ep->driver_data = midi;
+ kfifo_put(&midi->in_req_fifo, req);
+ }
/* allocate a bunch of read buffers and queue them all at once. */
for (i = 0; i < midi->qlen && err == 0; i++) {
@@ -368,8 +365,10 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
req->complete = f_midi_complete;
err = usb_ep_queue(midi->out_ep, req, GFP_ATOMIC);
if (err) {
- ERROR(midi, "%s queue req: %d\n",
+ ERROR(midi, "%s: couldn't enqueue request: %d\n",
midi->out_ep->name, err);
+ free_ep_req(midi->out_ep, req);
+ return err;
}
}
@@ -380,6 +379,7 @@ static void f_midi_disable(struct usb_function *f)
{
struct f_midi *midi = func_to_midi(f);
struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = NULL;
DBG(cdev, "disable\n");
@@ -389,6 +389,10 @@ static void f_midi_disable(struct usb_function *f)
*/
usb_ep_disable(midi->in_ep);
usb_ep_disable(midi->out_ep);
+
+ /* release IN requests */
+ while (kfifo_get(&midi->in_req_fifo, &req))
+ free_ep_req(midi->in_ep, req);
}
static int f_midi_snd_free(struct snd_device *device)
@@ -510,57 +514,113 @@ static void f_midi_transmit_byte(struct usb_request *req,
}
}
-static void f_midi_transmit(struct f_midi *midi, struct usb_request *req)
+static void f_midi_drop_out_substreams(struct f_midi *midi)
{
- struct usb_ep *ep = midi->in_ep;
- int i;
-
- if (!ep)
- return;
-
- if (!req)
- req = midi_alloc_ep_req(ep, midi->buflen);
-
- if (!req) {
- ERROR(midi, "%s: alloc_ep_request failed\n", __func__);
- return;
- }
- req->length = 0;
- req->complete = f_midi_complete;
+ unsigned int i;
for (i = 0; i < MAX_PORTS; i++) {
struct gmidi_in_port *port = midi->in_port[i];
struct snd_rawmidi_substream *substream = midi->in_substream[i];
- if (!port || !port->active || !substream)
+ if (!port)
+ break;
+
+ if (!port->active || !substream)
continue;
- while (req->length + 3 < midi->buflen) {
- uint8_t b;
- if (snd_rawmidi_transmit(substream, &b, 1) != 1) {
- port->active = 0;
+ snd_rawmidi_drop_output(substream);
+ }
+}
+
+static void f_midi_transmit(struct f_midi *midi)
+{
+ struct usb_ep *ep = midi->in_ep;
+ bool active;
+
+ /* We only care about USB requests if IN endpoint is enabled */
+ if (!ep || !ep->enabled)
+ goto drop_out;
+
+ do {
+ struct usb_request *req = NULL;
+ unsigned int len, i;
+
+ active = false;
+
+ /* We peek the request in order to reuse it if it fails
+ * to enqueue on its endpoint */
+ len = kfifo_peek(&midi->in_req_fifo, &req);
+ if (len != 1) {
+ ERROR(midi, "%s: Couldn't get usb request\n", __func__);
+ goto drop_out;
+ }
+
+ /* If buffer overrun, then we ignore this transmission.
+ * IMPORTANT: This will cause the user-space rawmidi device to block until a) usb
+ * requests have been completed or b) snd_rawmidi_write() times out. */
+ if (req->length > 0)
+ return;
+
+ for (i = midi->in_last_port; i < MAX_PORTS; i++) {
+ struct gmidi_in_port *port = midi->in_port[i];
+ struct snd_rawmidi_substream *substream = midi->in_substream[i];
+
+ if (!port) {
+ /* Reset counter when we reach the last available port */
+ midi->in_last_port = 0;
+ break;
+ }
+
+ if (!port->active || !substream)
+ continue;
+
+ while (req->length + 3 < midi->buflen) {
+ uint8_t b;
+
+ if (snd_rawmidi_transmit(substream, &b, 1) != 1) {
+ port->active = 0;
+ break;
+ }
+ f_midi_transmit_byte(req, port, b);
+ }
+
+ active = !!port->active;
+ /* Check if last port is still active, which means that
+ * there is still data on that substream but this current
+ * request run out of space. */
+ if (active) {
+ midi->in_last_port = i;
+ /* There is no need to re-iterate though midi ports. */
break;
}
- f_midi_transmit_byte(req, port, b);
}
- }
- if (req->length > 0) {
- int err;
+ if (req->length > 0) {
+ int err;
- err = usb_ep_queue(ep, req, GFP_ATOMIC);
- if (err < 0)
- ERROR(midi, "%s queue req: %d\n",
- midi->in_ep->name, err);
- } else {
- free_ep_req(ep, req);
- }
+ err = usb_ep_queue(ep, req, GFP_ATOMIC);
+ if (err < 0) {
+ ERROR(midi, "%s failed to queue req: %d\n",
+ midi->in_ep->name, err);
+ req->length = 0; /* Re-use request next time. */
+ } else {
+ /* Upon success, put request at the back of the queue. */
+ kfifo_skip(&midi->in_req_fifo);
+ kfifo_put(&midi->in_req_fifo, req);
+ }
+ }
+ } while (active);
+
+ return;
+
+drop_out:
+ f_midi_drop_out_substreams(midi);
}
static void f_midi_in_tasklet(unsigned long data)
{
struct f_midi *midi = (struct f_midi *) data;
- f_midi_transmit(midi, NULL);
+ f_midi_transmit(midi);
}
static int f_midi_in_open(struct snd_rawmidi_substream *substream)
@@ -686,6 +746,7 @@ static int f_midi_register_card(struct f_midi *midi)
goto fail;
}
midi->rmidi = rmidi;
+ midi->in_last_port = 0;
strcpy(rmidi->name, card->shortname);
rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_INPUT |
@@ -754,6 +815,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
goto fail;
ms_interface_desc.bInterfaceNumber = status;
ac_header_desc.baInterfaceNr[0] = status;
+ midi->ms_id = status;
status = -ENODEV;
@@ -1074,6 +1136,7 @@ static void f_midi_free(struct usb_function *f)
mutex_lock(&opts->lock);
for (i = opts->in_ports - 1; i >= 0; --i)
kfree(midi->in_port[i]);
+ kfifo_free(&midi->in_req_fifo);
kfree(midi);
--opts->refcnt;
mutex_unlock(&opts->lock);
@@ -1147,6 +1210,12 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
midi->index = opts->index;
midi->buflen = opts->buflen;
midi->qlen = opts->qlen;
+ midi->in_last_port = 0;
+
+ status = kfifo_alloc(&midi->in_req_fifo, midi->qlen, GFP_KERNEL);
+ if (status)
+ goto setup_fail;
+
++opts->refcnt;
mutex_unlock(&opts->lock);
diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
index 9f3ced62d916..242ba5caffe5 100644
--- a/drivers/usb/gadget/function/f_sourcesink.c
+++ b/drivers/usb/gadget/function/f_sourcesink.c
@@ -34,13 +34,6 @@
* plus two that support control-OUT tests. If the optional "autoresume"
* mode is enabled, it provides good functional coverage for the "USBCV"
* test harness from USB-IF.
- *
- * Note that because this doesn't queue more than one request at a time,
- * some other function must be used to test queueing logic. The network
- * link (g_ether) is the best overall option for that, since its TX and RX
- * queues are relatively independent, will receive a range of packet sizes,
- * and can often be made to run out completely. Those issues are important
- * when stress testing peripheral controller drivers.
*/
struct f_sourcesink {
struct usb_function function;
@@ -57,6 +50,8 @@ struct f_sourcesink {
unsigned isoc_mult;
unsigned isoc_maxburst;
unsigned buflen;
+ unsigned bulk_qlen;
+ unsigned iso_qlen;
};
static inline struct f_sourcesink *func_to_ss(struct usb_function *f)
@@ -303,12 +298,6 @@ static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len)
return alloc_ep_req(ep, len, ss->buflen);
}
-void free_ep_req(struct usb_ep *ep, struct usb_request *req)
-{
- kfree(req->buf);
- usb_ep_free_request(ep, req);
-}
-
static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
{
int value;
@@ -595,31 +584,33 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
{
struct usb_ep *ep;
struct usb_request *req;
- int i, size, status;
-
- for (i = 0; i < 8; i++) {
- if (is_iso) {
- switch (speed) {
- case USB_SPEED_SUPER:
- size = ss->isoc_maxpacket *
- (ss->isoc_mult + 1) *
- (ss->isoc_maxburst + 1);
- break;
- case USB_SPEED_HIGH:
- size = ss->isoc_maxpacket * (ss->isoc_mult + 1);
- break;
- default:
- size = ss->isoc_maxpacket > 1023 ?
- 1023 : ss->isoc_maxpacket;
- break;
- }
- ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
- req = ss_alloc_ep_req(ep, size);
- } else {
- ep = is_in ? ss->in_ep : ss->out_ep;
- req = ss_alloc_ep_req(ep, 0);
+ int i, size, qlen, status = 0;
+
+ if (is_iso) {
+ switch (speed) {
+ case USB_SPEED_SUPER:
+ size = ss->isoc_maxpacket *
+ (ss->isoc_mult + 1) *
+ (ss->isoc_maxburst + 1);
+ break;
+ case USB_SPEED_HIGH:
+ size = ss->isoc_maxpacket * (ss->isoc_mult + 1);
+ break;
+ default:
+ size = ss->isoc_maxpacket > 1023 ?
+ 1023 : ss->isoc_maxpacket;
+ break;
}
+ ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
+ qlen = ss->iso_qlen;
+ } else {
+ ep = is_in ? ss->in_ep : ss->out_ep;
+ qlen = ss->bulk_qlen;
+ size = 0;
+ }
+ for (i = 0; i < qlen; i++) {
+ req = ss_alloc_ep_req(ep, size);
if (!req)
return -ENOMEM;
@@ -638,10 +629,8 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
is_iso ? "ISO-" : "", is_in ? "IN" : "OUT",
ep->name, status);
free_ep_req(ep, req);
+ return status;
}
-
- if (!is_iso)
- break;
}
return status;
@@ -869,6 +858,8 @@ static struct usb_function *source_sink_alloc_func(
ss->isoc_mult = ss_opts->isoc_mult;
ss->isoc_maxburst = ss_opts->isoc_maxburst;
ss->buflen = ss_opts->bulk_buflen;
+ ss->bulk_qlen = ss_opts->bulk_qlen;
+ ss->iso_qlen = ss_opts->iso_qlen;
ss->function.name = "source/sink";
ss->function.bind = sourcesink_bind;
@@ -1153,6 +1144,82 @@ end:
CONFIGFS_ATTR(f_ss_opts_, bulk_buflen);
+static ssize_t f_ss_opts_bulk_qlen_show(struct config_item *item, char *page)
+{
+ struct f_ss_opts *opts = to_f_ss_opts(item);
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%u\n", opts->bulk_qlen);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_ss_opts_bulk_qlen_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct f_ss_opts *opts = to_f_ss_opts(item);
+ int ret;
+ u32 num;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou32(page, 0, &num);
+ if (ret)
+ goto end;
+
+ opts->bulk_qlen = num;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+CONFIGFS_ATTR(f_ss_opts_, bulk_qlen);
+
+static ssize_t f_ss_opts_iso_qlen_show(struct config_item *item, char *page)
+{
+ struct f_ss_opts *opts = to_f_ss_opts(item);
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%u\n", opts->iso_qlen);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_ss_opts_iso_qlen_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct f_ss_opts *opts = to_f_ss_opts(item);
+ int ret;
+ u32 num;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou32(page, 0, &num);
+ if (ret)
+ goto end;
+
+ opts->iso_qlen = num;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+CONFIGFS_ATTR(f_ss_opts_, iso_qlen);
+
static struct configfs_attribute *ss_attrs[] = {
&f_ss_opts_attr_pattern,
&f_ss_opts_attr_isoc_interval,
@@ -1160,6 +1227,8 @@ static struct configfs_attribute *ss_attrs[] = {
&f_ss_opts_attr_isoc_mult,
&f_ss_opts_attr_isoc_maxburst,
&f_ss_opts_attr_bulk_buflen,
+ &f_ss_opts_attr_bulk_qlen,
+ &f_ss_opts_attr_iso_qlen,
NULL,
};
@@ -1189,6 +1258,8 @@ static struct usb_function_instance *source_sink_alloc_inst(void)
ss_opts->isoc_interval = GZERO_ISOC_INTERVAL;
ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET;
ss_opts->bulk_buflen = GZERO_BULK_BUFLEN;
+ ss_opts->bulk_qlen = GZERO_SS_BULK_QLEN;
+ ss_opts->iso_qlen = GZERO_SS_ISO_QLEN;
config_group_init_type_name(&ss_opts->func_inst.group, "",
&ss_func_type);
diff --git a/drivers/usb/gadget/function/g_zero.h b/drivers/usb/gadget/function/g_zero.h
index 15f180904f8a..492924d0d599 100644
--- a/drivers/usb/gadget/function/g_zero.h
+++ b/drivers/usb/gadget/function/g_zero.h
@@ -10,6 +10,8 @@
#define GZERO_QLEN 32
#define GZERO_ISOC_INTERVAL 4
#define GZERO_ISOC_MAXPACKET 1024
+#define GZERO_SS_BULK_QLEN 1
+#define GZERO_SS_ISO_QLEN 8
struct usb_zero_options {
unsigned pattern;
@@ -19,6 +21,8 @@ struct usb_zero_options {
unsigned isoc_maxburst;
unsigned bulk_buflen;
unsigned qlen;
+ unsigned ss_bulk_qlen;
+ unsigned ss_iso_qlen;
};
struct f_ss_opts {
@@ -29,6 +33,8 @@ struct f_ss_opts {
unsigned isoc_mult;
unsigned isoc_maxburst;
unsigned bulk_buflen;
+ unsigned bulk_qlen;
+ unsigned iso_qlen;
/*
* Read/write access to configfs attributes is handled by configfs.
@@ -59,7 +65,6 @@ void lb_modexit(void);
int lb_modinit(void);
/* common utilities */
-void free_ep_req(struct usb_ep *ep, struct usb_request *req);
void disable_endpoints(struct usb_composite_dev *cdev,
struct usb_ep *in, struct usb_ep *out,
struct usb_ep *iso_in, struct usb_ep *iso_out);
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 6554322af2c1..637809e3bd0d 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -143,21 +143,11 @@ static inline int qlen(struct usb_gadget *gadget, unsigned qmult)
static int ueth_change_mtu(struct net_device *net, int new_mtu)
{
- struct eth_dev *dev = netdev_priv(net);
- unsigned long flags;
- int status = 0;
+ if (new_mtu <= ETH_HLEN || new_mtu > GETHER_MAX_ETH_FRAME_LEN)
+ return -ERANGE;
+ net->mtu = new_mtu;
- /* don't change MTU on "live" link (peer won't know) */
- spin_lock_irqsave(&dev->lock, flags);
- if (dev->port_usb)
- status = -EBUSY;
- else if (new_mtu <= ETH_HLEN || new_mtu > GETHER_MAX_ETH_FRAME_LEN)
- status = -ERANGE;
- else
- net->mtu = new_mtu;
- spin_unlock_irqrestore(&dev->lock, flags);
-
- return status;
+ return 0;
}
static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index f7771d86ad6c..6af145f2a99d 100644
--- a/drivers/usb/gadget/function/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -27,6 +27,8 @@
#include <linux/slab.h>
#include <linux/export.h>
#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/kthread.h>
#include "u_serial.h"
@@ -79,6 +81,7 @@
*/
#define QUEUE_SIZE 16
#define WRITE_BUF_SIZE 8192 /* TX only */
+#define GS_CONSOLE_BUF_SIZE 8192
/* circular buffer */
struct gs_buf {
@@ -88,6 +91,17 @@ struct gs_buf {
char *buf_put;
};
+/* console info */
+struct gscons_info {
+ struct gs_port *port;
+ struct task_struct *console_thread;
+ struct gs_buf con_buf;
+ /* protect the buf and busy flag */
+ spinlock_t con_lock;
+ int req_busy;
+ struct usb_request *console_req;
+};
+
/*
* The port structure holds info for each port, one for each minor number
* (and thus for each /dev/ node).
@@ -1023,6 +1037,246 @@ static const struct tty_operations gs_tty_ops = {
static struct tty_driver *gs_tty_driver;
+#ifdef CONFIG_U_SERIAL_CONSOLE
+
+static struct gscons_info gscons_info;
+static struct console gserial_cons;
+
+static struct usb_request *gs_request_new(struct usb_ep *ep)
+{
+ struct usb_request *req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+ if (!req)
+ return NULL;
+
+ req->buf = kmalloc(ep->maxpacket, GFP_ATOMIC);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ return NULL;
+ }
+
+ return req;
+}
+
+static void gs_request_free(struct usb_request *req, struct usb_ep *ep)
+{
+ if (!req)
+ return;
+
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+}
+
+static void gs_complete_out(struct usb_ep *ep, struct usb_request *req)
+{
+ struct gscons_info *info = &gscons_info;
+
+ switch (req->status) {
+ default:
+ pr_warn("%s: unexpected %s status %d\n",
+ __func__, ep->name, req->status);
+ case 0:
+ /* normal completion */
+ spin_lock(&info->con_lock);
+ info->req_busy = 0;
+ spin_unlock(&info->con_lock);
+
+ wake_up_process(info->console_thread);
+ break;
+ case -ESHUTDOWN:
+ /* disconnect */
+ pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
+ break;
+ }
+}
+
+static int gs_console_connect(int port_num)
+{
+ struct gscons_info *info = &gscons_info;
+ struct gs_port *port;
+ struct usb_ep *ep;
+
+ if (port_num != gserial_cons.index) {
+ pr_err("%s: port num [%d] is not support console\n",
+ __func__, port_num);
+ return -ENXIO;
+ }
+
+ port = ports[port_num].port;
+ ep = port->port_usb->in;
+ if (!info->console_req) {
+ info->console_req = gs_request_new(ep);
+ if (!info->console_req)
+ return -ENOMEM;
+ info->console_req->complete = gs_complete_out;
+ }
+
+ info->port = port;
+ spin_lock(&info->con_lock);
+ info->req_busy = 0;
+ spin_unlock(&info->con_lock);
+ pr_vdebug("port[%d] console connect!\n", port_num);
+ return 0;
+}
+
+static void gs_console_disconnect(struct usb_ep *ep)
+{
+ struct gscons_info *info = &gscons_info;
+ struct usb_request *req = info->console_req;
+
+ gs_request_free(req, ep);
+ info->console_req = NULL;
+}
+
+static int gs_console_thread(void *data)
+{
+ struct gscons_info *info = &gscons_info;
+ struct gs_port *port;
+ struct usb_request *req;
+ struct usb_ep *ep;
+ int xfer, ret, count, size;
+
+ do {
+ port = info->port;
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (!port || !port->port_usb
+ || !port->port_usb->in || !info->console_req)
+ goto sched;
+
+ req = info->console_req;
+ ep = port->port_usb->in;
+
+ spin_lock_irq(&info->con_lock);
+ count = gs_buf_data_avail(&info->con_buf);
+ size = ep->maxpacket;
+
+ if (count > 0 && !info->req_busy) {
+ set_current_state(TASK_RUNNING);
+ if (count < size)
+ size = count;
+
+ xfer = gs_buf_get(&info->con_buf, req->buf, size);
+ req->length = xfer;
+
+ spin_unlock(&info->con_lock);
+ ret = usb_ep_queue(ep, req, GFP_ATOMIC);
+ spin_lock(&info->con_lock);
+ if (ret < 0)
+ info->req_busy = 0;
+ else
+ info->req_busy = 1;
+
+ spin_unlock_irq(&info->con_lock);
+ } else {
+ spin_unlock_irq(&info->con_lock);
+sched:
+ if (kthread_should_stop()) {
+ set_current_state(TASK_RUNNING);
+ break;
+ }
+ schedule();
+ }
+ } while (1);
+
+ return 0;
+}
+
+static int gs_console_setup(struct console *co, char *options)
+{
+ struct gscons_info *info = &gscons_info;
+ int status;
+
+ info->port = NULL;
+ info->console_req = NULL;
+ info->req_busy = 0;
+ spin_lock_init(&info->con_lock);
+
+ status = gs_buf_alloc(&info->con_buf, GS_CONSOLE_BUF_SIZE);
+ if (status) {
+ pr_err("%s: allocate console buffer failed\n", __func__);
+ return status;
+ }
+
+ info->console_thread = kthread_create(gs_console_thread,
+ co, "gs_console");
+ if (IS_ERR(info->console_thread)) {
+ pr_err("%s: cannot create console thread\n", __func__);
+ gs_buf_free(&info->con_buf);
+ return PTR_ERR(info->console_thread);
+ }
+ wake_up_process(info->console_thread);
+
+ return 0;
+}
+
+static void gs_console_write(struct console *co,
+ const char *buf, unsigned count)
+{
+ struct gscons_info *info = &gscons_info;
+ unsigned long flags;
+
+ spin_lock_irqsave(&info->con_lock, flags);
+ gs_buf_put(&info->con_buf, buf, count);
+ spin_unlock_irqrestore(&info->con_lock, flags);
+
+ wake_up_process(info->console_thread);
+}
+
+static struct tty_driver *gs_console_device(struct console *co, int *index)
+{
+ struct tty_driver **p = (struct tty_driver **)co->data;
+
+ if (!*p)
+ return NULL;
+
+ *index = co->index;
+ return *p;
+}
+
+static struct console gserial_cons = {
+ .name = "ttyGS",
+ .write = gs_console_write,
+ .device = gs_console_device,
+ .setup = gs_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &gs_tty_driver,
+};
+
+static void gserial_console_init(void)
+{
+ register_console(&gserial_cons);
+}
+
+static void gserial_console_exit(void)
+{
+ struct gscons_info *info = &gscons_info;
+
+ unregister_console(&gserial_cons);
+ kthread_stop(info->console_thread);
+ gs_buf_free(&info->con_buf);
+}
+
+#else
+
+static int gs_console_connect(int port_num)
+{
+ return 0;
+}
+
+static void gs_console_disconnect(struct usb_ep *ep)
+{
+}
+
+static void gserial_console_init(void)
+{
+}
+
+static void gserial_console_exit(void)
+{
+}
+
+#endif
+
static int
gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
{
@@ -1096,6 +1350,7 @@ void gserial_free_line(unsigned char port_num)
gserial_free_port(port);
tty_unregister_device(gs_tty_driver, port_num);
+ gserial_console_exit();
}
EXPORT_SYMBOL_GPL(gserial_free_line);
@@ -1138,6 +1393,7 @@ int gserial_alloc_line(unsigned char *line_num)
goto err;
}
*line_num = port_num;
+ gserial_console_init();
err:
return ret;
}
@@ -1219,6 +1475,7 @@ int gserial_connect(struct gserial *gser, u8 port_num)
gser->disconnect(gser);
}
+ status = gs_console_connect(port_num);
spin_unlock_irqrestore(&port->port_lock, flags);
return status;
@@ -1277,6 +1534,7 @@ void gserial_disconnect(struct gserial *gser)
port->read_allocated = port->read_started =
port->write_allocated = port->write_started = 0;
+ gs_console_disconnect(gser->in);
spin_unlock_irqrestore(&port->port_lock, flags);
}
EXPORT_SYMBOL_GPL(gserial_disconnect);
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
index 289ebca316d3..ad8c9b05572d 100644
--- a/drivers/usb/gadget/function/uvc_configfs.c
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -20,7 +20,7 @@
#define UVC_ATTR(prefix, cname, aname) \
static struct configfs_attribute prefix##attr_##cname = { \
.ca_name = __stringify(aname), \
- .ca_mode = S_IRUGO, \
+ .ca_mode = S_IRUGO | S_IWUGO, \
.ca_owner = THIS_MODULE, \
.show = prefix##cname##_show, \
.store = prefix##cname##_store, \
diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c
index 51d4a1703af2..912694f3d54e 100644
--- a/drivers/usb/gadget/function/uvc_queue.c
+++ b/drivers/usb/gadget/function/uvc_queue.c
@@ -41,7 +41,7 @@
* videobuf2 queue operations
*/
-static int uvc_queue_setup(struct vb2_queue *vq, const void *parg,
+static int uvc_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
@@ -329,7 +329,7 @@ struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue,
buf->buf.field = V4L2_FIELD_NONE;
buf->buf.sequence = queue->sequence++;
- v4l2_get_timestamp(&buf->buf.timestamp);
+ buf->buf.vb2_buf.timestamp = ktime_get_ns();
vb2_set_plane_payload(&buf->buf.vb2_buf, 0, buf->bytesused);
vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE);
diff --git a/drivers/usb/gadget/legacy/acm_ms.c b/drivers/usb/gadget/legacy/acm_ms.c
index 4b158e2d1e57..c16089efc322 100644
--- a/drivers/usb/gadget/legacy/acm_ms.c
+++ b/drivers/usb/gadget/legacy/acm_ms.c
@@ -40,7 +40,7 @@ static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = cpu_to_le16(0x0200),
+ /* .bcdUSB = DYNAMIC */
.bDeviceClass = USB_CLASS_MISC /* 0xEF */,
.bDeviceSubClass = 2,
diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c
index 685cf3b4b78f..5d7b3c6a422b 100644
--- a/drivers/usb/gadget/legacy/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -123,7 +123,7 @@ static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = cpu_to_le16(0x200),
+ /* .bcdUSB = DYNAMIC */
#ifdef CONFIG_GADGET_UAC1
.bDeviceClass = USB_CLASS_PER_INTERFACE,
diff --git a/drivers/usb/gadget/legacy/cdc2.c b/drivers/usb/gadget/legacy/cdc2.c
index ecd8c8d62f2e..51c08682de84 100644
--- a/drivers/usb/gadget/legacy/cdc2.c
+++ b/drivers/usb/gadget/legacy/cdc2.c
@@ -43,7 +43,7 @@ static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = cpu_to_le16(0x0200),
+ /* .bcdUSB = DYNAMIC */
.bDeviceClass = USB_CLASS_COMM,
.bDeviceSubClass = 0,
diff --git a/drivers/usb/gadget/legacy/ether.c b/drivers/usb/gadget/legacy/ether.c
index 31e9160223e9..25a2c2e48592 100644
--- a/drivers/usb/gadget/legacy/ether.c
+++ b/drivers/usb/gadget/legacy/ether.c
@@ -151,7 +151,7 @@ static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = cpu_to_le16 (0x0200),
+ /* .bcdUSB = DYNAMIC */
.bDeviceClass = USB_CLASS_COMM,
.bDeviceSubClass = 0,
diff --git a/drivers/usb/gadget/legacy/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c
index 320a81b2baa6..f85639ef8a8f 100644
--- a/drivers/usb/gadget/legacy/g_ffs.c
+++ b/drivers/usb/gadget/legacy/g_ffs.c
@@ -69,7 +69,7 @@ static struct usb_device_descriptor gfs_dev_desc = {
.bLength = sizeof gfs_dev_desc,
.bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = cpu_to_le16(0x0200),
+ /* .bcdUSB = DYNAMIC */
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.idVendor = cpu_to_le16(GFS_VENDOR_ID),
diff --git a/drivers/usb/gadget/legacy/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c
index 8a18348ae86e..fc2ac150f5ff 100644
--- a/drivers/usb/gadget/legacy/gmidi.c
+++ b/drivers/usb/gadget/legacy/gmidi.c
@@ -21,19 +21,12 @@
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
-#include <linux/slab.h>
#include <linux/module.h>
-#include <linux/device.h>
-#include <sound/core.h>
#include <sound/initval.h>
-#include <sound/rawmidi.h>
-#include <linux/usb/ch9.h>
#include <linux/usb/composite.h>
#include <linux/usb/gadget.h>
-#include <linux/usb/audio.h>
-#include <linux/usb/midi.h>
#include "u_midi.h"
@@ -42,7 +35,6 @@
MODULE_AUTHOR("Ben Williamson");
MODULE_LICENSE("GPL v2");
-static const char shortname[] = "g_midi";
static const char longname[] = "MIDI Gadget";
USB_GADGET_COMPOSITE_OPTIONS();
@@ -61,7 +53,7 @@ MODULE_PARM_DESC(buflen, "MIDI buffer length");
static unsigned int qlen = 32;
module_param(qlen, uint, S_IRUGO);
-MODULE_PARM_DESC(qlen, "USB read request queue length");
+MODULE_PARM_DESC(qlen, "USB read and write request queue length");
static unsigned int in_ports = 1;
module_param(in_ports, uint, S_IRUGO);
@@ -86,7 +78,7 @@ MODULE_PARM_DESC(out_ports, "Number of MIDI output ports");
static struct usb_device_descriptor device_desc = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = cpu_to_le16(0x0200),
+ /* .bcdUSB = DYNAMIC */
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.idVendor = cpu_to_le16(DRIVER_VENDOR_NUM),
.idProduct = cpu_to_le16(DRIVER_PRODUCT_NUM),
diff --git a/drivers/usb/gadget/legacy/hid.c b/drivers/usb/gadget/legacy/hid.c
index 7e5d2c48476e..a71a884f79fc 100644
--- a/drivers/usb/gadget/legacy/hid.c
+++ b/drivers/usb/gadget/legacy/hid.c
@@ -47,7 +47,7 @@ static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = cpu_to_le16(0x0200),
+ /* .bcdUSB = DYNAMIC */
/* .bDeviceClass = USB_CLASS_COMM, */
/* .bDeviceSubClass = 0, */
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index f454c7af489c..365afd7e14f8 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -1137,10 +1137,9 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
dev->gadget->ep0, dev->req,
GFP_KERNEL);
}
+ spin_lock_irq(&dev->lock);
if (retval < 0) {
- spin_lock_irq (&dev->lock);
clean_req (dev->gadget->ep0, dev->req);
- spin_unlock_irq (&dev->lock);
} else
retval = len;
diff --git a/drivers/usb/gadget/legacy/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c
index bda3c519110f..e61af53c7d2b 100644
--- a/drivers/usb/gadget/legacy/mass_storage.c
+++ b/drivers/usb/gadget/legacy/mass_storage.c
@@ -55,7 +55,7 @@ static struct usb_device_descriptor msg_device_desc = {
.bLength = sizeof msg_device_desc,
.bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = cpu_to_le16(0x0200),
+ /* .bcdUSB = DYNAMIC */
.bDeviceClass = USB_CLASS_PER_INTERFACE,
/* Vendor and product id can be overridden by module parameters. */
diff --git a/drivers/usb/gadget/legacy/multi.c b/drivers/usb/gadget/legacy/multi.c
index 4fe794ddcd49..229d704a620b 100644
--- a/drivers/usb/gadget/legacy/multi.c
+++ b/drivers/usb/gadget/legacy/multi.c
@@ -67,7 +67,7 @@ static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = cpu_to_le16(0x0200),
+ /* .bcdUSB = DYNAMIC */
.bDeviceClass = USB_CLASS_MISC /* 0xEF */,
.bDeviceSubClass = 2,
diff --git a/drivers/usb/gadget/legacy/ncm.c b/drivers/usb/gadget/legacy/ncm.c
index 2bae4381332d..0aba68253e3d 100644
--- a/drivers/usb/gadget/legacy/ncm.c
+++ b/drivers/usb/gadget/legacy/ncm.c
@@ -49,7 +49,7 @@ static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = cpu_to_le16 (0x0200),
+ /* .bcdUSB = DYNAMIC */
.bDeviceClass = USB_CLASS_COMM,
.bDeviceSubClass = 0,
diff --git a/drivers/usb/gadget/legacy/nokia.c b/drivers/usb/gadget/legacy/nokia.c
index 8b3f6fb1825d..09975046c694 100644
--- a/drivers/usb/gadget/legacy/nokia.c
+++ b/drivers/usb/gadget/legacy/nokia.c
@@ -89,7 +89,7 @@ static struct usb_gadget_strings *dev_strings[] = {
static struct usb_device_descriptor device_desc = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = cpu_to_le16(0x0200),
+ /* .bcdUSB = DYNAMIC */
.bDeviceClass = USB_CLASS_COMM,
.idVendor = cpu_to_le16(NOKIA_VENDOR_ID),
.idProduct = cpu_to_le16(NOKIA_PRODUCT_ID),
diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c
index a22d30a4def1..6f969a86175c 100644
--- a/drivers/usb/gadget/legacy/printer.c
+++ b/drivers/usb/gadget/legacy/printer.c
@@ -71,7 +71,7 @@ static struct usb_function *f_printer;
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = cpu_to_le16(0x0200),
+ /* .bcdUSB = DYNAMIC */
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
diff --git a/drivers/usb/gadget/legacy/serial.c b/drivers/usb/gadget/legacy/serial.c
index c5d42e0347a9..9d89adce756d 100644
--- a/drivers/usb/gadget/legacy/serial.c
+++ b/drivers/usb/gadget/legacy/serial.c
@@ -65,7 +65,7 @@ static struct usb_gadget_strings *dev_strings[] = {
static struct usb_device_descriptor device_desc = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = cpu_to_le16(0x0200),
+ /* .bcdUSB = DYNAMIC */
/* .bDeviceClass = f(use_acm) */
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
diff --git a/drivers/usb/gadget/legacy/tcm_usb_gadget.c b/drivers/usb/gadget/legacy/tcm_usb_gadget.c
index 22e56158d585..7857fa411636 100644
--- a/drivers/usb/gadget/legacy/tcm_usb_gadget.c
+++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.c
@@ -1974,7 +1974,7 @@ static struct usb_descriptor_header *uasp_ss_function_desc[] = {
static struct usb_device_descriptor usbg_device_desc = {
.bLength = sizeof(usbg_device_desc),
.bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = cpu_to_le16(0x0200),
+ /* .bcdUSB = DYNAMIC */
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.idVendor = cpu_to_le16(UAS_VENDOR_ID),
.idProduct = cpu_to_le16(UAS_PRODUCT_ID),
diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c
index 72c976bf3530..f9661cd627c8 100644
--- a/drivers/usb/gadget/legacy/webcam.c
+++ b/drivers/usb/gadget/legacy/webcam.c
@@ -77,7 +77,7 @@ static struct usb_function *f_uvc;
static struct usb_device_descriptor webcam_device_descriptor = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = cpu_to_le16(0x0200),
+ /* .bcdUSB = DYNAMIC */
.bDeviceClass = USB_CLASS_MISC,
.bDeviceSubClass = 0x02,
.bDeviceProtocol = 0x01,
diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c
index 37a410056fed..d02e2ce73ea5 100644
--- a/drivers/usb/gadget/legacy/zero.c
+++ b/drivers/usb/gadget/legacy/zero.c
@@ -68,6 +68,8 @@ static struct usb_zero_options gzero_options = {
.isoc_maxpacket = GZERO_ISOC_MAXPACKET,
.bulk_buflen = GZERO_BULK_BUFLEN,
.qlen = GZERO_QLEN,
+ .ss_bulk_qlen = GZERO_SS_BULK_QLEN,
+ .ss_iso_qlen = GZERO_SS_ISO_QLEN,
};
/*-------------------------------------------------------------------------*/
@@ -113,7 +115,7 @@ static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = cpu_to_le16(0x0200),
+ /* .bcdUSB = DYNAMIC */
.bDeviceClass = USB_CLASS_VENDOR_SPEC,
.idVendor = cpu_to_le16(DRIVER_VENDOR_NUM),
@@ -255,6 +257,14 @@ static struct usb_function_instance *func_inst_lb;
module_param_named(qlen, gzero_options.qlen, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(qlen, "depth of loopback queue");
+module_param_named(ss_bulk_qlen, gzero_options.ss_bulk_qlen, uint,
+ S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(bulk_qlen, "depth of sourcesink queue for bulk transfer");
+
+module_param_named(ss_iso_qlen, gzero_options.ss_iso_qlen, uint,
+ S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(iso_qlen, "depth of sourcesink queue for iso transfer");
+
static int zero_bind(struct usb_composite_dev *cdev)
{
struct f_ss_opts *ss_opts;
@@ -285,6 +295,8 @@ static int zero_bind(struct usb_composite_dev *cdev)
ss_opts->isoc_mult = gzero_options.isoc_mult;
ss_opts->isoc_maxburst = gzero_options.isoc_maxburst;
ss_opts->bulk_buflen = gzero_options.bulk_buflen;
+ ss_opts->bulk_qlen = gzero_options.ss_bulk_qlen;
+ ss_opts->iso_qlen = gzero_options.ss_iso_qlen;
func_ss = usb_get_function(func_inst_ss);
if (IS_ERR(func_ss)) {
diff --git a/drivers/usb/gadget/u_f.c b/drivers/usb/gadget/u_f.c
index c6276f0268ae..4bc7eea8bfc8 100644
--- a/drivers/usb/gadget/u_f.c
+++ b/drivers/usb/gadget/u_f.c
@@ -11,7 +11,6 @@
* published by the Free Software Foundation.
*/
-#include <linux/usb/gadget.h>
#include "u_f.h"
struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len)
diff --git a/drivers/usb/gadget/u_f.h b/drivers/usb/gadget/u_f.h
index 1d5f0eb68552..4247cc098a89 100644
--- a/drivers/usb/gadget/u_f.h
+++ b/drivers/usb/gadget/u_f.h
@@ -16,6 +16,8 @@
#ifndef __U_F_H__
#define __U_F_H__
+#include <linux/usb/gadget.h>
+
/* Variable Length Array Macros **********************************************/
#define vla_group(groupname) size_t groupname##__next = 0
#define vla_group_size(groupname) groupname##__next
@@ -45,8 +47,12 @@
struct usb_ep;
struct usb_request;
+/* Requests allocated via alloc_ep_req() must be freed by free_ep_req(). */
struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len);
+static inline void free_ep_req(struct usb_ep *ep, struct usb_request *req)
+{
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+}
#endif /* __U_F_H__ */
-
-
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
index cdbff54e07ac..753c29bd11ad 100644
--- a/drivers/usb/gadget/udc/Kconfig
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -174,6 +174,17 @@ config USB_RENESAS_USBHS_UDC
dynamically linked module called "renesas_usbhs" and force all
gadget drivers to also be dynamically linked.
+config USB_RENESAS_USB3
+ tristate 'Renesas USB3.0 Peripheral controller'
+ depends on ARCH_SHMOBILE || COMPILE_TEST
+ help
+ Renesas USB3.0 Peripheral controller is a USB peripheral controller
+ that supports super, high, and full speed USB 3.0 data transfers.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "renesas_usb3" and force all
+ gadget drivers to also be dynamically linked.
+
config USB_PXA27X
tristate "PXA 27x"
help
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
index fba2049bf985..dfee53446319 100644
--- a/drivers/usb/gadget/udc/Makefile
+++ b/drivers/usb/gadget/udc/Makefile
@@ -19,6 +19,7 @@ fsl_usb2_udc-y := fsl_udc_core.o
fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o
obj-$(CONFIG_USB_M66592) += m66592-udc.o
obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o
+obj-$(CONFIG_USB_RENESAS_USB3) += renesas_usb3.o
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o
obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o
diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c
index 8cbb00325824..f5fccb3e4152 100644
--- a/drivers/usb/gadget/udc/bcm63xx_udc.c
+++ b/drivers/usb/gadget/udc/bcm63xx_udc.c
@@ -1083,7 +1083,7 @@ static int bcm63xx_ep_disable(struct usb_ep *ep)
struct bcm63xx_ep *bep = our_ep(ep);
struct bcm63xx_udc *udc = bep->udc;
struct iudma_ch *iudma = bep->iudma;
- struct list_head *pos, *n;
+ struct bcm63xx_req *breq, *n;
unsigned long flags;
if (!ep || !ep->desc)
@@ -1099,10 +1099,7 @@ static int bcm63xx_ep_disable(struct usb_ep *ep)
iudma_reset_channel(udc, iudma);
if (!list_empty(&bep->queue)) {
- list_for_each_safe(pos, n, &bep->queue) {
- struct bcm63xx_req *breq =
- list_entry(pos, struct bcm63xx_req, queue);
-
+ list_for_each_entry_safe(breq, n, &bep->queue, queue) {
usb_gadget_unmap_request(&udc->gadget, &breq->req,
iudma->is_tx);
list_del(&breq->queue);
diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c
index b9429bc42511..39b7136d31d9 100644
--- a/drivers/usb/gadget/udc/gr_udc.c
+++ b/drivers/usb/gadget/udc/gr_udc.c
@@ -253,13 +253,12 @@ static struct gr_dma_desc *gr_alloc_dma_desc(struct gr_ep *ep, gfp_t gfp_flags)
dma_addr_t paddr;
struct gr_dma_desc *dma_desc;
- dma_desc = dma_pool_alloc(ep->dev->desc_pool, gfp_flags, &paddr);
+ dma_desc = dma_pool_zalloc(ep->dev->desc_pool, gfp_flags, &paddr);
if (!dma_desc) {
dev_err(ep->dev->dev, "Could not allocate from DMA pool\n");
return NULL;
}
- memset(dma_desc, 0, sizeof(*dma_desc));
dma_desc->paddr = paddr;
return dma_desc;
diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c
index 00b5006baf15..79fe6b77ee44 100644
--- a/drivers/usb/gadget/udc/lpc32xx_udc.c
+++ b/drivers/usb/gadget/udc/lpc32xx_udc.c
@@ -28,42 +28,29 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
+#include <linux/clk.h>
#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/list.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/i2c.h>
#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
#include <linux/proc_fs.h>
-#include <linux/clk.h>
+#include <linux/slab.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
-#include <linux/i2c.h>
-#include <linux/kthread.h>
-#include <linux/freezer.h>
-#include <linux/dma-mapping.h>
-#include <linux/dmapool.h>
-#include <linux/workqueue.h>
-#include <linux/of.h>
#include <linux/usb/isp1301.h>
-#include <asm/byteorder.h>
-#include <mach/hardware.h>
-#include <linux/io.h>
-#include <asm/irq.h>
-
-#include <mach/platform.h>
-#include <mach/irqs.h>
-#include <mach/board.h>
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#endif
+#include <mach/hardware.h>
+#include <mach/platform.h>
+
/*
* USB device configuration structure
*/
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index 670ac0b12f00..001a3b74a993 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -2536,6 +2536,9 @@ static int pxa_udc_suspend(struct platform_device *_dev, pm_message_t state)
udc->pullup_resume = udc->pullup_on;
dplus_pullup(udc, 0);
+ if (udc->driver)
+ udc->driver->disconnect(&udc->gadget);
+
return 0;
}
diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c
new file mode 100644
index 000000000000..93a3bec81df7
--- /dev/null
+++ b/drivers/usb/gadget/udc/renesas_usb3.c
@@ -0,0 +1,1975 @@
+/*
+ * Renesas USB3.0 Peripheral driver (USB gadget)
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+/* register definitions */
+#define USB3_AXI_INT_STA 0x008
+#define USB3_AXI_INT_ENA 0x00c
+#define USB3_DMA_INT_STA 0x010
+#define USB3_DMA_INT_ENA 0x014
+#define USB3_USB_COM_CON 0x200
+#define USB3_USB20_CON 0x204
+#define USB3_USB30_CON 0x208
+#define USB3_USB_STA 0x210
+#define USB3_DRD_CON 0x218
+#define USB3_USB_INT_STA_1 0x220
+#define USB3_USB_INT_STA_2 0x224
+#define USB3_USB_INT_ENA_1 0x228
+#define USB3_USB_INT_ENA_2 0x22c
+#define USB3_STUP_DAT_0 0x230
+#define USB3_STUP_DAT_1 0x234
+#define USB3_P0_MOD 0x280
+#define USB3_P0_CON 0x288
+#define USB3_P0_STA 0x28c
+#define USB3_P0_INT_STA 0x290
+#define USB3_P0_INT_ENA 0x294
+#define USB3_P0_LNG 0x2a0
+#define USB3_P0_READ 0x2a4
+#define USB3_P0_WRITE 0x2a8
+#define USB3_PIPE_COM 0x2b0
+#define USB3_PN_MOD 0x2c0
+#define USB3_PN_RAMMAP 0x2c4
+#define USB3_PN_CON 0x2c8
+#define USB3_PN_STA 0x2cc
+#define USB3_PN_INT_STA 0x2d0
+#define USB3_PN_INT_ENA 0x2d4
+#define USB3_PN_LNG 0x2e0
+#define USB3_PN_READ 0x2e4
+#define USB3_PN_WRITE 0x2e8
+#define USB3_SSIFCMD 0x340
+
+/* AXI_INT_ENA and AXI_INT_STA */
+#define AXI_INT_DMAINT BIT(31)
+#define AXI_INT_EPCINT BIT(30)
+
+/* LCLKSEL */
+#define LCLKSEL_LSEL BIT(18)
+
+/* USB_COM_CON */
+#define USB_COM_CON_CONF BIT(24)
+#define USB_COM_CON_SPD_MODE BIT(17)
+#define USB_COM_CON_EP0_EN BIT(16)
+#define USB_COM_CON_DEV_ADDR_SHIFT 8
+#define USB_COM_CON_DEV_ADDR_MASK GENMASK(14, USB_COM_CON_DEV_ADDR_SHIFT)
+#define USB_COM_CON_DEV_ADDR(n) (((n) << USB_COM_CON_DEV_ADDR_SHIFT) & \
+ USB_COM_CON_DEV_ADDR_MASK)
+#define USB_COM_CON_RX_DETECTION BIT(1)
+#define USB_COM_CON_PIPE_CLR BIT(0)
+
+/* USB20_CON */
+#define USB20_CON_B2_PUE BIT(31)
+#define USB20_CON_B2_SUSPEND BIT(24)
+#define USB20_CON_B2_CONNECT BIT(17)
+#define USB20_CON_B2_TSTMOD_SHIFT 8
+#define USB20_CON_B2_TSTMOD_MASK GENMASK(10, USB20_CON_B2_TSTMOD_SHIFT)
+#define USB20_CON_B2_TSTMOD(n) (((n) << USB20_CON_B2_TSTMOD_SHIFT) & \
+ USB20_CON_B2_TSTMOD_MASK)
+#define USB20_CON_B2_TSTMOD_EN BIT(0)
+
+/* USB30_CON */
+#define USB30_CON_POW_SEL_SHIFT 24
+#define USB30_CON_POW_SEL_MASK GENMASK(26, USB30_CON_POW_SEL_SHIFT)
+#define USB30_CON_POW_SEL_IN_U3 BIT(26)
+#define USB30_CON_POW_SEL_IN_DISCON 0
+#define USB30_CON_POW_SEL_P2_TO_P0 BIT(25)
+#define USB30_CON_POW_SEL_P0_TO_P3 BIT(24)
+#define USB30_CON_POW_SEL_P0_TO_P2 0
+#define USB30_CON_B3_PLLWAKE BIT(23)
+#define USB30_CON_B3_CONNECT BIT(17)
+#define USB30_CON_B3_HOTRST_CMP BIT(1)
+
+/* USB_STA */
+#define USB_STA_SPEED_MASK (BIT(2) | BIT(1))
+#define USB_STA_SPEED_HS BIT(2)
+#define USB_STA_SPEED_FS BIT(1)
+#define USB_STA_SPEED_SS 0
+#define USB_STA_VBUS_STA BIT(0)
+
+/* DRD_CON */
+#define DRD_CON_PERI_CON BIT(24)
+
+/* USB_INT_ENA_1 and USB_INT_STA_1 */
+#define USB_INT_1_B3_PLLWKUP BIT(31)
+#define USB_INT_1_B3_LUPSUCS BIT(30)
+#define USB_INT_1_B3_DISABLE BIT(27)
+#define USB_INT_1_B3_WRMRST BIT(21)
+#define USB_INT_1_B3_HOTRST BIT(20)
+#define USB_INT_1_B2_USBRST BIT(12)
+#define USB_INT_1_B2_L1SPND BIT(11)
+#define USB_INT_1_B2_SPND BIT(9)
+#define USB_INT_1_B2_RSUM BIT(8)
+#define USB_INT_1_SPEED BIT(1)
+#define USB_INT_1_VBUS_CNG BIT(0)
+
+/* USB_INT_ENA_2 and USB_INT_STA_2 */
+#define USB_INT_2_PIPE(n) BIT(n)
+
+/* P0_MOD */
+#define P0_MOD_DIR BIT(6)
+
+/* P0_CON and PN_CON */
+#define PX_CON_BYTE_EN_MASK (BIT(10) | BIT(9))
+#define PX_CON_BYTE_EN_SHIFT 9
+#define PX_CON_BYTE_EN_BYTES(n) (((n) << PX_CON_BYTE_EN_SHIFT) & \
+ PX_CON_BYTE_EN_MASK)
+#define PX_CON_SEND BIT(8)
+
+/* P0_CON */
+#define P0_CON_ST_RES_MASK (BIT(27) | BIT(26))
+#define P0_CON_ST_RES_FORCE_STALL BIT(27)
+#define P0_CON_ST_RES_NORMAL BIT(26)
+#define P0_CON_ST_RES_FORCE_NRDY 0
+#define P0_CON_OT_RES_MASK (BIT(25) | BIT(24))
+#define P0_CON_OT_RES_FORCE_STALL BIT(25)
+#define P0_CON_OT_RES_NORMAL BIT(24)
+#define P0_CON_OT_RES_FORCE_NRDY 0
+#define P0_CON_IN_RES_MASK (BIT(17) | BIT(16))
+#define P0_CON_IN_RES_FORCE_STALL BIT(17)
+#define P0_CON_IN_RES_NORMAL BIT(16)
+#define P0_CON_IN_RES_FORCE_NRDY 0
+#define P0_CON_RES_WEN BIT(7)
+#define P0_CON_BCLR BIT(1)
+
+/* P0_STA and PN_STA */
+#define PX_STA_BUFSTS BIT(0)
+
+/* P0_INT_ENA and P0_INT_STA */
+#define P0_INT_STSED BIT(18)
+#define P0_INT_STSST BIT(17)
+#define P0_INT_SETUP BIT(16)
+#define P0_INT_RCVNL BIT(8)
+#define P0_INT_ERDY BIT(7)
+#define P0_INT_FLOW BIT(6)
+#define P0_INT_STALL BIT(2)
+#define P0_INT_NRDY BIT(1)
+#define P0_INT_BFRDY BIT(0)
+#define P0_INT_ALL_BITS (P0_INT_STSED | P0_INT_SETUP | P0_INT_BFRDY)
+
+/* PN_MOD */
+#define PN_MOD_DIR BIT(6)
+#define PN_MOD_TYPE_SHIFT 4
+#define PN_MOD_TYPE_MASK GENMASK(5, PN_MOD_TYPE_SHIFT)
+#define PN_MOD_TYPE(n) (((n) << PN_MOD_TYPE_SHIFT) & \
+ PN_MOD_TYPE_MASK)
+#define PN_MOD_EPNUM_MASK GENMASK(3, 0)
+#define PN_MOD_EPNUM(n) ((n) & PN_MOD_EPNUM_MASK)
+
+/* PN_RAMMAP */
+#define PN_RAMMAP_RAMAREA_SHIFT 29
+#define PN_RAMMAP_RAMAREA_MASK GENMASK(31, PN_RAMMAP_RAMAREA_SHIFT)
+#define PN_RAMMAP_RAMAREA_16KB BIT(31)
+#define PN_RAMMAP_RAMAREA_8KB (BIT(30) | BIT(29))
+#define PN_RAMMAP_RAMAREA_4KB BIT(30)
+#define PN_RAMMAP_RAMAREA_2KB BIT(29)
+#define PN_RAMMAP_RAMAREA_1KB 0
+#define PN_RAMMAP_MPKT_SHIFT 16
+#define PN_RAMMAP_MPKT_MASK GENMASK(26, PN_RAMMAP_MPKT_SHIFT)
+#define PN_RAMMAP_MPKT(n) (((n) << PN_RAMMAP_MPKT_SHIFT) & \
+ PN_RAMMAP_MPKT_MASK)
+#define PN_RAMMAP_RAMIF_SHIFT 14
+#define PN_RAMMAP_RAMIF_MASK GENMASK(15, PN_RAMMAP_RAMIF_SHIFT)
+#define PN_RAMMAP_RAMIF(n) (((n) << PN_RAMMAP_RAMIF_SHIFT) & \
+ PN_RAMMAP_RAMIF_MASK)
+#define PN_RAMMAP_BASEAD_MASK GENMASK(13, 0)
+#define PN_RAMMAP_BASEAD(offs) (((offs) >> 3) & PN_RAMMAP_BASEAD_MASK)
+#define PN_RAMMAP_DATA(area, ramif, basead) ((PN_RAMMAP_##area) | \
+ (PN_RAMMAP_RAMIF(ramif)) | \
+ (PN_RAMMAP_BASEAD(basead)))
+
+/* PN_CON */
+#define PN_CON_EN BIT(31)
+#define PN_CON_DATAIF_EN BIT(30)
+#define PN_CON_RES_MASK (BIT(17) | BIT(16))
+#define PN_CON_RES_FORCE_STALL BIT(17)
+#define PN_CON_RES_NORMAL BIT(16)
+#define PN_CON_RES_FORCE_NRDY 0
+#define PN_CON_LAST BIT(11)
+#define PN_CON_RES_WEN BIT(7)
+#define PN_CON_CLR BIT(0)
+
+/* PN_INT_STA and PN_INT_ENA */
+#define PN_INT_LSTTR BIT(4)
+#define PN_INT_BFRDY BIT(0)
+
+/* USB3_SSIFCMD */
+#define SSIFCMD_URES_U2 BIT(9)
+#define SSIFCMD_URES_U1 BIT(8)
+#define SSIFCMD_UDIR_U2 BIT(7)
+#define SSIFCMD_UDIR_U1 BIT(6)
+#define SSIFCMD_UREQ_U2 BIT(5)
+#define SSIFCMD_UREQ_U1 BIT(4)
+
+#define USB3_EP0_SS_MAX_PACKET_SIZE 512
+#define USB3_EP0_HSFS_MAX_PACKET_SIZE 64
+#define USB3_EP0_BUF_SIZE 8
+#define USB3_MAX_NUM_PIPES 30
+#define USB3_WAIT_US 3
+
+struct renesas_usb3;
+struct renesas_usb3_request {
+ struct usb_request req;
+ struct list_head queue;
+};
+
+#define USB3_EP_NAME_SIZE 8
+struct renesas_usb3_ep {
+ struct usb_ep ep;
+ struct renesas_usb3 *usb3;
+ int num;
+ char ep_name[USB3_EP_NAME_SIZE];
+ struct list_head queue;
+ u32 rammap_val;
+ bool dir_in;
+ bool halt;
+ bool wedge;
+ bool started;
+};
+
+struct renesas_usb3_priv {
+ int ramsize_per_ramif; /* unit = bytes */
+ int num_ramif;
+ int ramsize_per_pipe; /* unit = bytes */
+ bool workaround_for_vbus; /* if true, don't check vbus signal */
+};
+
+struct renesas_usb3 {
+ void __iomem *reg;
+
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+
+ struct renesas_usb3_ep *usb3_ep;
+ int num_usb3_eps;
+
+ spinlock_t lock;
+ int disabled_count;
+
+ struct usb_request *ep0_req;
+ u16 test_mode;
+ u8 ep0_buf[USB3_EP0_BUF_SIZE];
+ bool softconnect;
+ bool workaround_for_vbus;
+};
+
+#define gadget_to_renesas_usb3(_gadget) \
+ container_of(_gadget, struct renesas_usb3, gadget)
+#define renesas_usb3_to_gadget(renesas_usb3) (&renesas_usb3->gadget)
+#define usb3_to_dev(_usb3) (_usb3->gadget.dev.parent)
+
+#define usb_ep_to_usb3_ep(_ep) container_of(_ep, struct renesas_usb3_ep, ep)
+#define usb3_ep_to_usb3(_usb3_ep) (_usb3_ep->usb3)
+#define usb_req_to_usb3_req(_req) container_of(_req, \
+ struct renesas_usb3_request, req)
+
+#define usb3_get_ep(usb3, n) ((usb3)->usb3_ep + (n))
+#define usb3_for_each_ep(usb3_ep, usb3, i) \
+ for ((i) = 0, usb3_ep = usb3_get_ep(usb3, (i)); \
+ (i) < (usb3)->num_usb3_eps; \
+ (i)++, usb3_ep = usb3_get_ep(usb3, (i)))
+
+static const char udc_name[] = "renesas_usb3";
+
+static void usb3_write(struct renesas_usb3 *usb3, u32 data, u32 offs)
+{
+ iowrite32(data, usb3->reg + offs);
+}
+
+static u32 usb3_read(struct renesas_usb3 *usb3, u32 offs)
+{
+ return ioread32(usb3->reg + offs);
+}
+
+static void usb3_set_bit(struct renesas_usb3 *usb3, u32 bits, u32 offs)
+{
+ u32 val = usb3_read(usb3, offs);
+
+ val |= bits;
+ usb3_write(usb3, val, offs);
+}
+
+static void usb3_clear_bit(struct renesas_usb3 *usb3, u32 bits, u32 offs)
+{
+ u32 val = usb3_read(usb3, offs);
+
+ val &= ~bits;
+ usb3_write(usb3, val, offs);
+}
+
+static int usb3_wait(struct renesas_usb3 *usb3, u32 reg, u32 mask,
+ u32 expected)
+{
+ int i;
+
+ for (i = 0; i < USB3_WAIT_US; i++) {
+ if ((usb3_read(usb3, reg) & mask) == expected)
+ return 0;
+ udelay(1);
+ }
+
+ dev_dbg(usb3_to_dev(usb3), "%s: timed out (%8x, %08x, %08x)\n",
+ __func__, reg, mask, expected);
+
+ return -EBUSY;
+}
+
+static void usb3_enable_irq_1(struct renesas_usb3 *usb3, u32 bits)
+{
+ usb3_set_bit(usb3, bits, USB3_USB_INT_ENA_1);
+}
+
+static void usb3_disable_irq_1(struct renesas_usb3 *usb3, u32 bits)
+{
+ usb3_clear_bit(usb3, bits, USB3_USB_INT_ENA_1);
+}
+
+static void usb3_enable_pipe_irq(struct renesas_usb3 *usb3, int num)
+{
+ usb3_set_bit(usb3, USB_INT_2_PIPE(num), USB3_USB_INT_ENA_2);
+}
+
+static void usb3_disable_pipe_irq(struct renesas_usb3 *usb3, int num)
+{
+ usb3_clear_bit(usb3, USB_INT_2_PIPE(num), USB3_USB_INT_ENA_2);
+}
+
+static void usb3_init_axi_bridge(struct renesas_usb3 *usb3)
+{
+ /* Set AXI_INT */
+ usb3_write(usb3, ~0, USB3_DMA_INT_STA);
+ usb3_write(usb3, 0, USB3_DMA_INT_ENA);
+ usb3_set_bit(usb3, AXI_INT_DMAINT | AXI_INT_EPCINT, USB3_AXI_INT_ENA);
+}
+
+static void usb3_init_epc_registers(struct renesas_usb3 *usb3)
+{
+ /* FIXME: How to change host / peripheral mode as well? */
+ usb3_set_bit(usb3, DRD_CON_PERI_CON, USB3_DRD_CON);
+
+ usb3_write(usb3, ~0, USB3_USB_INT_STA_1);
+ usb3_enable_irq_1(usb3, USB_INT_1_VBUS_CNG);
+}
+
+static bool usb3_wakeup_usb2_phy(struct renesas_usb3 *usb3)
+{
+ if (!(usb3_read(usb3, USB3_USB20_CON) & USB20_CON_B2_SUSPEND))
+ return true; /* already waked it up */
+
+ usb3_clear_bit(usb3, USB20_CON_B2_SUSPEND, USB3_USB20_CON);
+ usb3_enable_irq_1(usb3, USB_INT_1_B2_RSUM);
+
+ return false;
+}
+
+static void usb3_usb2_pullup(struct renesas_usb3 *usb3, int pullup)
+{
+ u32 bits = USB20_CON_B2_PUE | USB20_CON_B2_CONNECT;
+
+ if (usb3->softconnect && pullup)
+ usb3_set_bit(usb3, bits, USB3_USB20_CON);
+ else
+ usb3_clear_bit(usb3, bits, USB3_USB20_CON);
+}
+
+static void usb3_set_test_mode(struct renesas_usb3 *usb3)
+{
+ u32 val = usb3_read(usb3, USB3_USB20_CON);
+
+ val &= ~USB20_CON_B2_TSTMOD_MASK;
+ val |= USB20_CON_B2_TSTMOD(usb3->test_mode);
+ usb3_write(usb3, val | USB20_CON_B2_TSTMOD_EN, USB3_USB20_CON);
+ if (!usb3->test_mode)
+ usb3_clear_bit(usb3, USB20_CON_B2_TSTMOD_EN, USB3_USB20_CON);
+}
+
+static void usb3_start_usb2_connection(struct renesas_usb3 *usb3)
+{
+ usb3->disabled_count++;
+ usb3_set_bit(usb3, USB_COM_CON_EP0_EN, USB3_USB_COM_CON);
+ usb3_set_bit(usb3, USB_COM_CON_SPD_MODE, USB3_USB_COM_CON);
+ usb3_usb2_pullup(usb3, 1);
+}
+
+static int usb3_is_usb3_phy_in_u3(struct renesas_usb3 *usb3)
+{
+ return usb3_read(usb3, USB3_USB30_CON) & USB30_CON_POW_SEL_IN_U3;
+}
+
+static bool usb3_wakeup_usb3_phy(struct renesas_usb3 *usb3)
+{
+ if (!usb3_is_usb3_phy_in_u3(usb3))
+ return true; /* already waked it up */
+
+ usb3_set_bit(usb3, USB30_CON_B3_PLLWAKE, USB3_USB30_CON);
+ usb3_enable_irq_1(usb3, USB_INT_1_B3_PLLWKUP);
+
+ return false;
+}
+
+static u16 usb3_feature_get_un_enabled(struct renesas_usb3 *usb3)
+{
+ u32 mask_u2 = SSIFCMD_UDIR_U2 | SSIFCMD_UREQ_U2;
+ u32 mask_u1 = SSIFCMD_UDIR_U1 | SSIFCMD_UREQ_U1;
+ u32 val = usb3_read(usb3, USB3_SSIFCMD);
+ u16 ret = 0;
+
+ /* Enables {U2,U1} if the bits of UDIR and UREQ are set to 0 */
+ if (!(val & mask_u2))
+ ret |= 1 << USB_DEV_STAT_U2_ENABLED;
+ if (!(val & mask_u1))
+ ret |= 1 << USB_DEV_STAT_U1_ENABLED;
+
+ return ret;
+}
+
+static void usb3_feature_u2_enable(struct renesas_usb3 *usb3, bool enable)
+{
+ u32 bits = SSIFCMD_UDIR_U2 | SSIFCMD_UREQ_U2;
+
+ /* Enables U2 if the bits of UDIR and UREQ are set to 0 */
+ if (enable)
+ usb3_clear_bit(usb3, bits, USB3_SSIFCMD);
+ else
+ usb3_set_bit(usb3, bits, USB3_SSIFCMD);
+}
+
+static void usb3_feature_u1_enable(struct renesas_usb3 *usb3, bool enable)
+{
+ u32 bits = SSIFCMD_UDIR_U1 | SSIFCMD_UREQ_U1;
+
+ /* Enables U1 if the bits of UDIR and UREQ are set to 0 */
+ if (enable)
+ usb3_clear_bit(usb3, bits, USB3_SSIFCMD);
+ else
+ usb3_set_bit(usb3, bits, USB3_SSIFCMD);
+}
+
+static void usb3_start_operation_for_usb3(struct renesas_usb3 *usb3)
+{
+ usb3_set_bit(usb3, USB_COM_CON_EP0_EN, USB3_USB_COM_CON);
+ usb3_clear_bit(usb3, USB_COM_CON_SPD_MODE, USB3_USB_COM_CON);
+ usb3_set_bit(usb3, USB30_CON_B3_CONNECT, USB3_USB30_CON);
+}
+
+static void usb3_start_usb3_connection(struct renesas_usb3 *usb3)
+{
+ usb3_start_operation_for_usb3(usb3);
+ usb3_set_bit(usb3, USB_COM_CON_RX_DETECTION, USB3_USB_COM_CON);
+
+ usb3_enable_irq_1(usb3, USB_INT_1_B3_LUPSUCS | USB_INT_1_B3_DISABLE |
+ USB_INT_1_SPEED);
+}
+
+static void usb3_stop_usb3_connection(struct renesas_usb3 *usb3)
+{
+ usb3_clear_bit(usb3, USB30_CON_B3_CONNECT, USB3_USB30_CON);
+}
+
+static void usb3_transition_to_default_state(struct renesas_usb3 *usb3,
+ bool is_usb3)
+{
+ usb3_set_bit(usb3, USB_INT_2_PIPE(0), USB3_USB_INT_ENA_2);
+ usb3_write(usb3, P0_INT_ALL_BITS, USB3_P0_INT_STA);
+ usb3_set_bit(usb3, P0_INT_ALL_BITS, USB3_P0_INT_ENA);
+
+ if (is_usb3)
+ usb3_enable_irq_1(usb3, USB_INT_1_B3_WRMRST |
+ USB_INT_1_B3_HOTRST);
+ else
+ usb3_enable_irq_1(usb3, USB_INT_1_B2_SPND |
+ USB_INT_1_B2_L1SPND | USB_INT_1_B2_USBRST);
+}
+
+static void usb3_connect(struct renesas_usb3 *usb3)
+{
+ if (usb3_wakeup_usb3_phy(usb3))
+ usb3_start_usb3_connection(usb3);
+}
+
+static void usb3_reset_epc(struct renesas_usb3 *usb3)
+{
+ usb3_clear_bit(usb3, USB_COM_CON_CONF, USB3_USB_COM_CON);
+ usb3_clear_bit(usb3, USB_COM_CON_EP0_EN, USB3_USB_COM_CON);
+ usb3_set_bit(usb3, USB_COM_CON_PIPE_CLR, USB3_USB_COM_CON);
+ usb3->test_mode = 0;
+ usb3_set_test_mode(usb3);
+}
+
+static void usb3_disconnect(struct renesas_usb3 *usb3)
+{
+ usb3->disabled_count = 0;
+ usb3_usb2_pullup(usb3, 0);
+ usb3_clear_bit(usb3, USB30_CON_B3_CONNECT, USB3_USB30_CON);
+ usb3_reset_epc(usb3);
+
+ if (usb3->driver)
+ usb3->driver->disconnect(&usb3->gadget);
+}
+
+static void usb3_check_vbus(struct renesas_usb3 *usb3)
+{
+ if (usb3->workaround_for_vbus) {
+ usb3_connect(usb3);
+ } else {
+ if (usb3_read(usb3, USB3_USB_STA) & USB_STA_VBUS_STA)
+ usb3_connect(usb3);
+ else
+ usb3_disconnect(usb3);
+ }
+}
+
+static void renesas_usb3_init_controller(struct renesas_usb3 *usb3)
+{
+ usb3_init_axi_bridge(usb3);
+ usb3_init_epc_registers(usb3);
+
+ usb3_check_vbus(usb3);
+}
+
+static void renesas_usb3_stop_controller(struct renesas_usb3 *usb3)
+{
+ usb3_disconnect(usb3);
+ usb3_write(usb3, 0, USB3_P0_INT_ENA);
+ usb3_write(usb3, 0, USB3_PN_INT_ENA);
+ usb3_write(usb3, 0, USB3_USB_INT_ENA_1);
+ usb3_write(usb3, 0, USB3_USB_INT_ENA_2);
+ usb3_write(usb3, 0, USB3_AXI_INT_ENA);
+}
+
+static void usb3_irq_epc_int_1_pll_wakeup(struct renesas_usb3 *usb3)
+{
+ usb3_disable_irq_1(usb3, USB_INT_1_B3_PLLWKUP);
+ usb3_clear_bit(usb3, USB30_CON_B3_PLLWAKE, USB3_USB30_CON);
+ usb3_start_usb3_connection(usb3);
+}
+
+static void usb3_irq_epc_int_1_linkup_success(struct renesas_usb3 *usb3)
+{
+ usb3_transition_to_default_state(usb3, true);
+}
+
+static void usb3_irq_epc_int_1_resume(struct renesas_usb3 *usb3)
+{
+ usb3_disable_irq_1(usb3, USB_INT_1_B2_RSUM);
+ usb3_start_usb2_connection(usb3);
+ usb3_transition_to_default_state(usb3, false);
+}
+
+static void usb3_irq_epc_int_1_disable(struct renesas_usb3 *usb3)
+{
+ usb3_stop_usb3_connection(usb3);
+ if (usb3_wakeup_usb2_phy(usb3))
+ usb3_irq_epc_int_1_resume(usb3);
+}
+
+static void usb3_irq_epc_int_1_bus_reset(struct renesas_usb3 *usb3)
+{
+ usb3_reset_epc(usb3);
+ if (usb3->disabled_count < 3)
+ usb3_start_usb3_connection(usb3);
+ else
+ usb3_start_usb2_connection(usb3);
+}
+
+static void usb3_irq_epc_int_1_vbus_change(struct renesas_usb3 *usb3)
+{
+ usb3_check_vbus(usb3);
+}
+
+static void usb3_irq_epc_int_1_hot_reset(struct renesas_usb3 *usb3)
+{
+ usb3_reset_epc(usb3);
+ usb3_set_bit(usb3, USB_COM_CON_EP0_EN, USB3_USB_COM_CON);
+
+ /* This bit shall be set within 12ms from the start of HotReset */
+ usb3_set_bit(usb3, USB30_CON_B3_HOTRST_CMP, USB3_USB30_CON);
+}
+
+static void usb3_irq_epc_int_1_warm_reset(struct renesas_usb3 *usb3)
+{
+ usb3_reset_epc(usb3);
+ usb3_set_bit(usb3, USB_COM_CON_EP0_EN, USB3_USB_COM_CON);
+
+ usb3_start_operation_for_usb3(usb3);
+ usb3_enable_irq_1(usb3, USB_INT_1_SPEED);
+}
+
+static void usb3_irq_epc_int_1_speed(struct renesas_usb3 *usb3)
+{
+ u32 speed = usb3_read(usb3, USB3_USB_STA) & USB_STA_SPEED_MASK;
+
+ switch (speed) {
+ case USB_STA_SPEED_SS:
+ usb3->gadget.speed = USB_SPEED_SUPER;
+ break;
+ case USB_STA_SPEED_HS:
+ usb3->gadget.speed = USB_SPEED_HIGH;
+ break;
+ case USB_STA_SPEED_FS:
+ usb3->gadget.speed = USB_SPEED_FULL;
+ break;
+ default:
+ usb3->gadget.speed = USB_SPEED_UNKNOWN;
+ break;
+ }
+}
+
+static void usb3_irq_epc_int_1(struct renesas_usb3 *usb3, u32 int_sta_1)
+{
+ if (int_sta_1 & USB_INT_1_B3_PLLWKUP)
+ usb3_irq_epc_int_1_pll_wakeup(usb3);
+
+ if (int_sta_1 & USB_INT_1_B3_LUPSUCS)
+ usb3_irq_epc_int_1_linkup_success(usb3);
+
+ if (int_sta_1 & USB_INT_1_B3_HOTRST)
+ usb3_irq_epc_int_1_hot_reset(usb3);
+
+ if (int_sta_1 & USB_INT_1_B3_WRMRST)
+ usb3_irq_epc_int_1_warm_reset(usb3);
+
+ if (int_sta_1 & USB_INT_1_B3_DISABLE)
+ usb3_irq_epc_int_1_disable(usb3);
+
+ if (int_sta_1 & USB_INT_1_B2_USBRST)
+ usb3_irq_epc_int_1_bus_reset(usb3);
+
+ if (int_sta_1 & USB_INT_1_B2_RSUM)
+ usb3_irq_epc_int_1_resume(usb3);
+
+ if (int_sta_1 & USB_INT_1_SPEED)
+ usb3_irq_epc_int_1_speed(usb3);
+
+ if (int_sta_1 & USB_INT_1_VBUS_CNG)
+ usb3_irq_epc_int_1_vbus_change(usb3);
+}
+
+static struct renesas_usb3_request *__usb3_get_request(struct renesas_usb3_ep
+ *usb3_ep)
+{
+ return list_first_entry_or_null(&usb3_ep->queue,
+ struct renesas_usb3_request, queue);
+}
+
+static struct renesas_usb3_request *usb3_get_request(struct renesas_usb3_ep
+ *usb3_ep)
+{
+ struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+ struct renesas_usb3_request *usb3_req;
+ unsigned long flags;
+
+ spin_lock_irqsave(&usb3->lock, flags);
+ usb3_req = __usb3_get_request(usb3_ep);
+ spin_unlock_irqrestore(&usb3->lock, flags);
+
+ return usb3_req;
+}
+
+static void usb3_request_done(struct renesas_usb3_ep *usb3_ep,
+ struct renesas_usb3_request *usb3_req, int status)
+{
+ struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+ unsigned long flags;
+
+ dev_dbg(usb3_to_dev(usb3), "giveback: ep%2d, %u, %u, %d\n",
+ usb3_ep->num, usb3_req->req.length, usb3_req->req.actual,
+ status);
+ usb3_req->req.status = status;
+ spin_lock_irqsave(&usb3->lock, flags);
+ usb3_ep->started = false;
+ list_del_init(&usb3_req->queue);
+ spin_unlock_irqrestore(&usb3->lock, flags);
+ usb_gadget_giveback_request(&usb3_ep->ep, &usb3_req->req);
+}
+
+static void usb3_irq_epc_pipe0_status_end(struct renesas_usb3 *usb3)
+{
+ struct renesas_usb3_ep *usb3_ep = usb3_get_ep(usb3, 0);
+ struct renesas_usb3_request *usb3_req = usb3_get_request(usb3_ep);
+
+ if (usb3_req)
+ usb3_request_done(usb3_ep, usb3_req, 0);
+ if (usb3->test_mode)
+ usb3_set_test_mode(usb3);
+}
+
+static void usb3_get_setup_data(struct renesas_usb3 *usb3,
+ struct usb_ctrlrequest *ctrl)
+{
+ struct renesas_usb3_ep *usb3_ep = usb3_get_ep(usb3, 0);
+ u32 *data = (u32 *)ctrl;
+
+ *data++ = usb3_read(usb3, USB3_STUP_DAT_0);
+ *data = usb3_read(usb3, USB3_STUP_DAT_1);
+
+ /* update this driver's flag */
+ usb3_ep->dir_in = !!(ctrl->bRequestType & USB_DIR_IN);
+}
+
+static void usb3_set_p0_con_update_res(struct renesas_usb3 *usb3, u32 res)
+{
+ u32 val = usb3_read(usb3, USB3_P0_CON);
+
+ val &= ~(P0_CON_ST_RES_MASK | P0_CON_OT_RES_MASK | P0_CON_IN_RES_MASK);
+ val |= res | P0_CON_RES_WEN;
+ usb3_write(usb3, val, USB3_P0_CON);
+}
+
+static void usb3_set_p0_con_for_ctrl_read_data(struct renesas_usb3 *usb3)
+{
+ usb3_set_p0_con_update_res(usb3, P0_CON_ST_RES_FORCE_NRDY |
+ P0_CON_OT_RES_FORCE_STALL |
+ P0_CON_IN_RES_NORMAL);
+}
+
+static void usb3_set_p0_con_for_ctrl_read_status(struct renesas_usb3 *usb3)
+{
+ usb3_set_p0_con_update_res(usb3, P0_CON_ST_RES_NORMAL |
+ P0_CON_OT_RES_FORCE_STALL |
+ P0_CON_IN_RES_NORMAL);
+}
+
+static void usb3_set_p0_con_for_ctrl_write_data(struct renesas_usb3 *usb3)
+{
+ usb3_set_p0_con_update_res(usb3, P0_CON_ST_RES_FORCE_NRDY |
+ P0_CON_OT_RES_NORMAL |
+ P0_CON_IN_RES_FORCE_STALL);
+}
+
+static void usb3_set_p0_con_for_ctrl_write_status(struct renesas_usb3 *usb3)
+{
+ usb3_set_p0_con_update_res(usb3, P0_CON_ST_RES_NORMAL |
+ P0_CON_OT_RES_NORMAL |
+ P0_CON_IN_RES_FORCE_STALL);
+}
+
+static void usb3_set_p0_con_for_no_data(struct renesas_usb3 *usb3)
+{
+ usb3_set_p0_con_update_res(usb3, P0_CON_ST_RES_NORMAL |
+ P0_CON_OT_RES_FORCE_STALL |
+ P0_CON_IN_RES_FORCE_STALL);
+}
+
+static void usb3_set_p0_con_stall(struct renesas_usb3 *usb3)
+{
+ usb3_set_p0_con_update_res(usb3, P0_CON_ST_RES_FORCE_STALL |
+ P0_CON_OT_RES_FORCE_STALL |
+ P0_CON_IN_RES_FORCE_STALL);
+}
+
+static void usb3_set_p0_con_stop(struct renesas_usb3 *usb3)
+{
+ usb3_set_p0_con_update_res(usb3, P0_CON_ST_RES_FORCE_NRDY |
+ P0_CON_OT_RES_FORCE_NRDY |
+ P0_CON_IN_RES_FORCE_NRDY);
+}
+
+static int usb3_pn_change(struct renesas_usb3 *usb3, int num)
+{
+ if (num == 0 || num > usb3->num_usb3_eps)
+ return -ENXIO;
+
+ usb3_write(usb3, num, USB3_PIPE_COM);
+
+ return 0;
+}
+
+static void usb3_set_pn_con_update_res(struct renesas_usb3 *usb3, u32 res)
+{
+ u32 val = usb3_read(usb3, USB3_PN_CON);
+
+ val &= ~PN_CON_RES_MASK;
+ val |= res & PN_CON_RES_MASK;
+ val |= PN_CON_RES_WEN;
+ usb3_write(usb3, val, USB3_PN_CON);
+}
+
+static void usb3_pn_start(struct renesas_usb3 *usb3)
+{
+ usb3_set_pn_con_update_res(usb3, PN_CON_RES_NORMAL);
+}
+
+static void usb3_pn_stop(struct renesas_usb3 *usb3)
+{
+ usb3_set_pn_con_update_res(usb3, PN_CON_RES_FORCE_NRDY);
+}
+
+static void usb3_pn_stall(struct renesas_usb3 *usb3)
+{
+ usb3_set_pn_con_update_res(usb3, PN_CON_RES_FORCE_STALL);
+}
+
+static int usb3_pn_con_clear(struct renesas_usb3 *usb3)
+{
+ usb3_set_bit(usb3, PN_CON_CLR, USB3_PN_CON);
+
+ return usb3_wait(usb3, USB3_PN_CON, PN_CON_CLR, 0);
+}
+
+static bool usb3_is_transfer_complete(struct renesas_usb3_ep *usb3_ep,
+ struct renesas_usb3_request *usb3_req)
+{
+ struct usb_request *req = &usb3_req->req;
+
+ if ((!req->zero && req->actual == req->length) ||
+ (req->actual % usb3_ep->ep.maxpacket) || (req->length == 0))
+ return true;
+ else
+ return false;
+}
+
+static int usb3_wait_pipe_status(struct renesas_usb3_ep *usb3_ep, u32 mask)
+{
+ struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+ u32 sta_reg = usb3_ep->num ? USB3_PN_STA : USB3_P0_STA;
+
+ return usb3_wait(usb3, sta_reg, mask, mask);
+}
+
+static void usb3_set_px_con_send(struct renesas_usb3_ep *usb3_ep, int bytes,
+ bool last)
+{
+ struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+ u32 con_reg = usb3_ep->num ? USB3_PN_CON : USB3_P0_CON;
+ u32 val = usb3_read(usb3, con_reg);
+
+ val |= PX_CON_SEND | PX_CON_BYTE_EN_BYTES(bytes);
+ val |= (usb3_ep->num && last) ? PN_CON_LAST : 0;
+ usb3_write(usb3, val, con_reg);
+}
+
+static int usb3_write_pipe(struct renesas_usb3_ep *usb3_ep,
+ struct renesas_usb3_request *usb3_req,
+ u32 fifo_reg)
+{
+ struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+ int i;
+ int len = min_t(unsigned, usb3_req->req.length - usb3_req->req.actual,
+ usb3_ep->ep.maxpacket);
+ u8 *buf = usb3_req->req.buf + usb3_req->req.actual;
+ u32 tmp = 0;
+ bool is_last;
+
+ if (usb3_wait_pipe_status(usb3_ep, PX_STA_BUFSTS) < 0)
+ return -EBUSY;
+
+ /* Update gadget driver parameter */
+ usb3_req->req.actual += len;
+
+ /* Write data to the register */
+ if (len >= 4) {
+ iowrite32_rep(usb3->reg + fifo_reg, buf, len / 4);
+ buf += (len / 4) * 4;
+ len %= 4; /* update len to use usb3_set_pX_con_send() */
+ }
+
+ if (len) {
+ for (i = 0; i < len; i++)
+ tmp |= buf[i] << (8 * i);
+ usb3_write(usb3, tmp, fifo_reg);
+ }
+
+ is_last = usb3_is_transfer_complete(usb3_ep, usb3_req);
+ /* Send the data */
+ usb3_set_px_con_send(usb3_ep, len, is_last);
+
+ return is_last ? 0 : -EAGAIN;
+}
+
+static u32 usb3_get_received_length(struct renesas_usb3_ep *usb3_ep)
+{
+ struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+ u32 lng_reg = usb3_ep->num ? USB3_PN_LNG : USB3_P0_LNG;
+
+ return usb3_read(usb3, lng_reg);
+}
+
+static int usb3_read_pipe(struct renesas_usb3_ep *usb3_ep,
+ struct renesas_usb3_request *usb3_req, u32 fifo_reg)
+{
+ struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+ int i;
+ int len = min_t(unsigned, usb3_req->req.length - usb3_req->req.actual,
+ usb3_get_received_length(usb3_ep));
+ u8 *buf = usb3_req->req.buf + usb3_req->req.actual;
+ u32 tmp = 0;
+
+ if (!len)
+ return 0;
+
+ /* Update gadget driver parameter */
+ usb3_req->req.actual += len;
+
+ /* Read data from the register */
+ if (len >= 4) {
+ ioread32_rep(usb3->reg + fifo_reg, buf, len / 4);
+ buf += (len / 4) * 4;
+ len %= 4;
+ }
+
+ if (len) {
+ tmp = usb3_read(usb3, fifo_reg);
+ for (i = 0; i < len; i++)
+ buf[i] = (tmp >> (8 * i)) & 0xff;
+ }
+
+ return usb3_is_transfer_complete(usb3_ep, usb3_req) ? 0 : -EAGAIN;
+}
+
+static void usb3_set_status_stage(struct renesas_usb3_ep *usb3_ep,
+ struct renesas_usb3_request *usb3_req)
+{
+ struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+
+ if (usb3_ep->dir_in) {
+ usb3_set_p0_con_for_ctrl_read_status(usb3);
+ } else {
+ if (!usb3_req->req.length)
+ usb3_set_p0_con_for_no_data(usb3);
+ else
+ usb3_set_p0_con_for_ctrl_write_status(usb3);
+ }
+}
+
+static void usb3_p0_xfer(struct renesas_usb3_ep *usb3_ep,
+ struct renesas_usb3_request *usb3_req)
+{
+ int ret = -EAGAIN;
+
+ if (usb3_ep->dir_in)
+ ret = usb3_write_pipe(usb3_ep, usb3_req, USB3_P0_WRITE);
+ else
+ ret = usb3_read_pipe(usb3_ep, usb3_req, USB3_P0_READ);
+
+ if (!ret)
+ usb3_set_status_stage(usb3_ep, usb3_req);
+}
+
+static void usb3_start_pipe0(struct renesas_usb3_ep *usb3_ep,
+ struct renesas_usb3_request *usb3_req)
+{
+ struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+
+ if (usb3_ep->started)
+ return;
+
+ usb3_ep->started = true;
+
+ if (usb3_ep->dir_in) {
+ usb3_set_bit(usb3, P0_MOD_DIR, USB3_P0_MOD);
+ usb3_set_p0_con_for_ctrl_read_data(usb3);
+ } else {
+ usb3_clear_bit(usb3, P0_MOD_DIR, USB3_P0_MOD);
+ usb3_set_p0_con_for_ctrl_write_data(usb3);
+ }
+
+ usb3_p0_xfer(usb3_ep, usb3_req);
+}
+
+static void usb3_start_pipen(struct renesas_usb3_ep *usb3_ep,
+ struct renesas_usb3_request *usb3_req)
+{
+ struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+ struct renesas_usb3_request *usb3_req_first = usb3_get_request(usb3_ep);
+ unsigned long flags;
+ int ret = -EAGAIN;
+ u32 enable_bits = 0;
+
+ if (usb3_ep->halt || usb3_ep->started)
+ return;
+ if (usb3_req != usb3_req_first)
+ return;
+
+ spin_lock_irqsave(&usb3->lock, flags);
+ if (usb3_pn_change(usb3, usb3_ep->num) < 0)
+ goto out;
+
+ usb3_ep->started = true;
+ usb3_pn_start(usb3);
+
+ if (usb3_ep->dir_in) {
+ ret = usb3_write_pipe(usb3_ep, usb3_req, USB3_PN_WRITE);
+ enable_bits |= PN_INT_LSTTR;
+ }
+
+ if (ret < 0)
+ enable_bits |= PN_INT_BFRDY;
+
+ if (enable_bits) {
+ usb3_set_bit(usb3, enable_bits, USB3_PN_INT_ENA);
+ usb3_enable_pipe_irq(usb3, usb3_ep->num);
+ }
+out:
+ spin_unlock_irqrestore(&usb3->lock, flags);
+}
+
+static int renesas_usb3_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+ gfp_t gfp_flags)
+{
+ struct renesas_usb3_ep *usb3_ep = usb_ep_to_usb3_ep(_ep);
+ struct renesas_usb3_request *usb3_req = usb_req_to_usb3_req(_req);
+ struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+ unsigned long flags;
+
+ dev_dbg(usb3_to_dev(usb3), "ep_queue: ep%2d, %u\n", usb3_ep->num,
+ _req->length);
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+ spin_lock_irqsave(&usb3->lock, flags);
+ list_add_tail(&usb3_req->queue, &usb3_ep->queue);
+ spin_unlock_irqrestore(&usb3->lock, flags);
+
+ if (!usb3_ep->num)
+ usb3_start_pipe0(usb3_ep, usb3_req);
+ else
+ usb3_start_pipen(usb3_ep, usb3_req);
+
+ return 0;
+}
+
+static void usb3_set_device_address(struct renesas_usb3 *usb3, u16 addr)
+{
+ /* DEV_ADDR bit field is cleared by WarmReset, HotReset and BusReset */
+ usb3_set_bit(usb3, USB_COM_CON_DEV_ADDR(addr), USB3_USB_COM_CON);
+}
+
+static bool usb3_std_req_set_address(struct renesas_usb3 *usb3,
+ struct usb_ctrlrequest *ctrl)
+{
+ if (ctrl->wValue >= 128)
+ return true; /* stall */
+
+ usb3_set_device_address(usb3, ctrl->wValue);
+ usb3_set_p0_con_for_no_data(usb3);
+
+ return false;
+}
+
+static void usb3_pipe0_internal_xfer(struct renesas_usb3 *usb3,
+ void *tx_data, size_t len,
+ void (*complete)(struct usb_ep *ep,
+ struct usb_request *req))
+{
+ struct renesas_usb3_ep *usb3_ep = usb3_get_ep(usb3, 0);
+
+ if (tx_data)
+ memcpy(usb3->ep0_buf, tx_data,
+ min_t(size_t, len, USB3_EP0_BUF_SIZE));
+
+ usb3->ep0_req->buf = &usb3->ep0_buf;
+ usb3->ep0_req->length = len;
+ usb3->ep0_req->complete = complete;
+ renesas_usb3_ep_queue(&usb3_ep->ep, usb3->ep0_req, GFP_ATOMIC);
+}
+
+static void usb3_pipe0_get_status_completion(struct usb_ep *ep,
+ struct usb_request *req)
+{
+}
+
+static bool usb3_std_req_get_status(struct renesas_usb3 *usb3,
+ struct usb_ctrlrequest *ctrl)
+{
+ bool stall = false;
+ struct renesas_usb3_ep *usb3_ep;
+ int num;
+ u16 status = 0;
+
+ switch (ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ if (usb3->gadget.is_selfpowered)
+ status |= 1 << USB_DEVICE_SELF_POWERED;
+ if (usb3->gadget.speed == USB_SPEED_SUPER)
+ status |= usb3_feature_get_un_enabled(usb3);
+ break;
+ case USB_RECIP_INTERFACE:
+ break;
+ case USB_RECIP_ENDPOINT:
+ num = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+ usb3_ep = usb3_get_ep(usb3, num);
+ if (usb3_ep->halt)
+ status |= 1 << USB_ENDPOINT_HALT;
+ break;
+ default:
+ stall = true;
+ break;
+ }
+
+ if (!stall) {
+ status = cpu_to_le16(status);
+ dev_dbg(usb3_to_dev(usb3), "get_status: req = %p\n",
+ usb_req_to_usb3_req(usb3->ep0_req));
+ usb3_pipe0_internal_xfer(usb3, &status, sizeof(status),
+ usb3_pipe0_get_status_completion);
+ }
+
+ return stall;
+}
+
+static bool usb3_std_req_feature_device(struct renesas_usb3 *usb3,
+ struct usb_ctrlrequest *ctrl, bool set)
+{
+ bool stall = true;
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+
+ switch (w_value) {
+ case USB_DEVICE_TEST_MODE:
+ if (!set)
+ break;
+ usb3->test_mode = le16_to_cpu(ctrl->wIndex) >> 8;
+ stall = false;
+ break;
+ case USB_DEVICE_U1_ENABLE:
+ case USB_DEVICE_U2_ENABLE:
+ if (usb3->gadget.speed != USB_SPEED_SUPER)
+ break;
+ if (w_value == USB_DEVICE_U1_ENABLE)
+ usb3_feature_u1_enable(usb3, set);
+ if (w_value == USB_DEVICE_U2_ENABLE)
+ usb3_feature_u2_enable(usb3, set);
+ stall = false;
+ break;
+ default:
+ break;
+ }
+
+ return stall;
+}
+
+static int usb3_set_halt_p0(struct renesas_usb3_ep *usb3_ep, bool halt)
+{
+ struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+
+ if (unlikely(usb3_ep->num))
+ return -EINVAL;
+
+ usb3_ep->halt = halt;
+ if (halt)
+ usb3_set_p0_con_stall(usb3);
+ else
+ usb3_set_p0_con_stop(usb3);
+
+ return 0;
+}
+
+static int usb3_set_halt_pn(struct renesas_usb3_ep *usb3_ep, bool halt,
+ bool is_clear_feature)
+{
+ struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+ unsigned long flags;
+
+ spin_lock_irqsave(&usb3->lock, flags);
+ if (!usb3_pn_change(usb3, usb3_ep->num)) {
+ usb3_ep->halt = halt;
+ if (halt) {
+ usb3_pn_stall(usb3);
+ } else if (!is_clear_feature || !usb3_ep->wedge) {
+ usb3_pn_con_clear(usb3);
+ usb3_set_bit(usb3, PN_CON_EN, USB3_PN_CON);
+ usb3_pn_stop(usb3);
+ }
+ }
+ spin_unlock_irqrestore(&usb3->lock, flags);
+
+ return 0;
+}
+
+static int usb3_set_halt(struct renesas_usb3_ep *usb3_ep, bool halt,
+ bool is_clear_feature)
+{
+ int ret = 0;
+
+ if (halt && usb3_ep->started)
+ return -EAGAIN;
+
+ if (usb3_ep->num)
+ ret = usb3_set_halt_pn(usb3_ep, halt, is_clear_feature);
+ else
+ ret = usb3_set_halt_p0(usb3_ep, halt);
+
+ return ret;
+}
+
+static bool usb3_std_req_feature_endpoint(struct renesas_usb3 *usb3,
+ struct usb_ctrlrequest *ctrl,
+ bool set)
+{
+ int num = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+ struct renesas_usb3_ep *usb3_ep;
+ struct renesas_usb3_request *usb3_req;
+
+ if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT)
+ return true; /* stall */
+
+ usb3_ep = usb3_get_ep(usb3, num);
+ usb3_set_halt(usb3_ep, set, true);
+
+ /* Restarts a queue if clear feature */
+ if (!set) {
+ usb3_ep->started = false;
+ usb3_req = usb3_get_request(usb3_ep);
+ if (usb3_req)
+ usb3_start_pipen(usb3_ep, usb3_req);
+ }
+
+ return false;
+}
+
+static bool usb3_std_req_feature(struct renesas_usb3 *usb3,
+ struct usb_ctrlrequest *ctrl, bool set)
+{
+ bool stall = false;
+
+ switch (ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ stall = usb3_std_req_feature_device(usb3, ctrl, set);
+ break;
+ case USB_RECIP_INTERFACE:
+ break;
+ case USB_RECIP_ENDPOINT:
+ stall = usb3_std_req_feature_endpoint(usb3, ctrl, set);
+ break;
+ default:
+ stall = true;
+ break;
+ }
+
+ if (!stall)
+ usb3_set_p0_con_for_no_data(usb3);
+
+ return stall;
+}
+
+static void usb3_pipe0_set_sel_completion(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ /* TODO */
+}
+
+static bool usb3_std_req_set_sel(struct renesas_usb3 *usb3,
+ struct usb_ctrlrequest *ctrl)
+{
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+ if (w_length != 6)
+ return true; /* stall */
+
+ dev_dbg(usb3_to_dev(usb3), "set_sel: req = %p\n",
+ usb_req_to_usb3_req(usb3->ep0_req));
+ usb3_pipe0_internal_xfer(usb3, NULL, 6, usb3_pipe0_set_sel_completion);
+
+ return false;
+}
+
+static bool usb3_std_req_set_configuration(struct renesas_usb3 *usb3,
+ struct usb_ctrlrequest *ctrl)
+{
+ if (ctrl->wValue > 0)
+ usb3_set_bit(usb3, USB_COM_CON_CONF, USB3_USB_COM_CON);
+ else
+ usb3_clear_bit(usb3, USB_COM_CON_CONF, USB3_USB_COM_CON);
+
+ return false;
+}
+
+/**
+ * usb3_handle_standard_request - handle some standard requests
+ * @usb3: the renesas_usb3 pointer
+ * @ctrl: a pointer of setup data
+ *
+ * Returns true if this function handled a standard request
+ */
+static bool usb3_handle_standard_request(struct renesas_usb3 *usb3,
+ struct usb_ctrlrequest *ctrl)
+{
+ bool ret = false;
+ bool stall = false;
+
+ if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+ switch (ctrl->bRequest) {
+ case USB_REQ_SET_ADDRESS:
+ stall = usb3_std_req_set_address(usb3, ctrl);
+ ret = true;
+ break;
+ case USB_REQ_GET_STATUS:
+ stall = usb3_std_req_get_status(usb3, ctrl);
+ ret = true;
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ stall = usb3_std_req_feature(usb3, ctrl, false);
+ ret = true;
+ break;
+ case USB_REQ_SET_FEATURE:
+ stall = usb3_std_req_feature(usb3, ctrl, true);
+ ret = true;
+ break;
+ case USB_REQ_SET_SEL:
+ stall = usb3_std_req_set_sel(usb3, ctrl);
+ ret = true;
+ break;
+ case USB_REQ_SET_ISOCH_DELAY:
+ /* This hardware doesn't support Isochronous xfer */
+ stall = true;
+ ret = true;
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ usb3_std_req_set_configuration(usb3, ctrl);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (stall)
+ usb3_set_p0_con_stall(usb3);
+
+ return ret;
+}
+
+static int usb3_p0_con_clear_buffer(struct renesas_usb3 *usb3)
+{
+ usb3_set_bit(usb3, P0_CON_BCLR, USB3_P0_CON);
+
+ return usb3_wait(usb3, USB3_P0_CON, P0_CON_BCLR, 0);
+}
+
+static void usb3_irq_epc_pipe0_setup(struct renesas_usb3 *usb3)
+{
+ struct usb_ctrlrequest ctrl;
+ struct renesas_usb3_ep *usb3_ep = usb3_get_ep(usb3, 0);
+
+ /* Call giveback function if previous transfer is not completed */
+ if (usb3_ep->started)
+ usb3_request_done(usb3_ep, usb3_get_request(usb3_ep),
+ -ECONNRESET);
+
+ usb3_p0_con_clear_buffer(usb3);
+ usb3_get_setup_data(usb3, &ctrl);
+ if (!usb3_handle_standard_request(usb3, &ctrl))
+ if (usb3->driver->setup(&usb3->gadget, &ctrl) < 0)
+ usb3_set_p0_con_stall(usb3);
+}
+
+static void usb3_irq_epc_pipe0_bfrdy(struct renesas_usb3 *usb3)
+{
+ struct renesas_usb3_ep *usb3_ep = usb3_get_ep(usb3, 0);
+ struct renesas_usb3_request *usb3_req = usb3_get_request(usb3_ep);
+
+ if (!usb3_req)
+ return;
+
+ usb3_p0_xfer(usb3_ep, usb3_req);
+}
+
+static void usb3_irq_epc_pipe0(struct renesas_usb3 *usb3)
+{
+ u32 p0_int_sta = usb3_read(usb3, USB3_P0_INT_STA);
+
+ p0_int_sta &= usb3_read(usb3, USB3_P0_INT_ENA);
+ usb3_write(usb3, p0_int_sta, USB3_P0_INT_STA);
+ if (p0_int_sta & P0_INT_STSED)
+ usb3_irq_epc_pipe0_status_end(usb3);
+ if (p0_int_sta & P0_INT_SETUP)
+ usb3_irq_epc_pipe0_setup(usb3);
+ if (p0_int_sta & P0_INT_BFRDY)
+ usb3_irq_epc_pipe0_bfrdy(usb3);
+}
+
+static void usb3_request_done_pipen(struct renesas_usb3 *usb3,
+ struct renesas_usb3_ep *usb3_ep,
+ struct renesas_usb3_request *usb3_req,
+ int status)
+{
+ usb3_pn_stop(usb3);
+ usb3_disable_pipe_irq(usb3, usb3_ep->num);
+ usb3_request_done(usb3_ep, usb3_req, status);
+
+ /* get next usb3_req */
+ usb3_req = usb3_get_request(usb3_ep);
+ if (usb3_req)
+ usb3_start_pipen(usb3_ep, usb3_req);
+}
+
+static void usb3_irq_epc_pipen_lsttr(struct renesas_usb3 *usb3, int num)
+{
+ struct renesas_usb3_ep *usb3_ep = usb3_get_ep(usb3, num);
+ struct renesas_usb3_request *usb3_req = usb3_get_request(usb3_ep);
+
+ if (!usb3_req)
+ return;
+
+ if (usb3_ep->dir_in) {
+ dev_dbg(usb3_to_dev(usb3), "%s: len = %u, actual = %u\n",
+ __func__, usb3_req->req.length, usb3_req->req.actual);
+ usb3_request_done_pipen(usb3, usb3_ep, usb3_req, 0);
+ }
+}
+
+static void usb3_irq_epc_pipen_bfrdy(struct renesas_usb3 *usb3, int num)
+{
+ struct renesas_usb3_ep *usb3_ep = usb3_get_ep(usb3, num);
+ struct renesas_usb3_request *usb3_req = usb3_get_request(usb3_ep);
+
+ if (!usb3_req)
+ return;
+
+ if (usb3_ep->dir_in) {
+ /* Do not stop the IN pipe here to detect LSTTR interrupt */
+ if (!usb3_write_pipe(usb3_ep, usb3_req, USB3_PN_WRITE))
+ usb3_clear_bit(usb3, PN_INT_BFRDY, USB3_PN_INT_ENA);
+ } else {
+ if (!usb3_read_pipe(usb3_ep, usb3_req, USB3_PN_READ))
+ usb3_request_done_pipen(usb3, usb3_ep, usb3_req, 0);
+ }
+}
+
+static void usb3_irq_epc_pipen(struct renesas_usb3 *usb3, int num)
+{
+ u32 pn_int_sta;
+
+ if (usb3_pn_change(usb3, num) < 0)
+ return;
+
+ pn_int_sta = usb3_read(usb3, USB3_PN_INT_STA);
+ pn_int_sta &= usb3_read(usb3, USB3_PN_INT_ENA);
+ usb3_write(usb3, pn_int_sta, USB3_PN_INT_STA);
+ if (pn_int_sta & PN_INT_LSTTR)
+ usb3_irq_epc_pipen_lsttr(usb3, num);
+ if (pn_int_sta & PN_INT_BFRDY)
+ usb3_irq_epc_pipen_bfrdy(usb3, num);
+}
+
+static void usb3_irq_epc_int_2(struct renesas_usb3 *usb3, u32 int_sta_2)
+{
+ int i;
+
+ for (i = 0; i < usb3->num_usb3_eps; i++) {
+ if (int_sta_2 & USB_INT_2_PIPE(i)) {
+ if (!i)
+ usb3_irq_epc_pipe0(usb3);
+ else
+ usb3_irq_epc_pipen(usb3, i);
+ }
+ }
+}
+
+static void usb3_irq_epc(struct renesas_usb3 *usb3)
+{
+ u32 int_sta_1 = usb3_read(usb3, USB3_USB_INT_STA_1);
+ u32 int_sta_2 = usb3_read(usb3, USB3_USB_INT_STA_2);
+
+ int_sta_1 &= usb3_read(usb3, USB3_USB_INT_ENA_1);
+ if (int_sta_1) {
+ usb3_write(usb3, int_sta_1, USB3_USB_INT_STA_1);
+ usb3_irq_epc_int_1(usb3, int_sta_1);
+ }
+
+ int_sta_2 &= usb3_read(usb3, USB3_USB_INT_ENA_2);
+ if (int_sta_2)
+ usb3_irq_epc_int_2(usb3, int_sta_2);
+}
+
+static irqreturn_t renesas_usb3_irq(int irq, void *_usb3)
+{
+ struct renesas_usb3 *usb3 = _usb3;
+ irqreturn_t ret = IRQ_NONE;
+ u32 axi_int_sta = usb3_read(usb3, USB3_AXI_INT_STA);
+
+ if (axi_int_sta & AXI_INT_EPCINT) {
+ usb3_irq_epc(usb3);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static void usb3_write_pn_mod(struct renesas_usb3_ep *usb3_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+ u32 val = 0;
+
+ val |= usb3_ep->dir_in ? PN_MOD_DIR : 0;
+ val |= PN_MOD_TYPE(usb_endpoint_type(desc));
+ val |= PN_MOD_EPNUM(usb_endpoint_num(desc));
+ usb3_write(usb3, val, USB3_PN_MOD);
+}
+
+static u32 usb3_calc_ramarea(int ram_size)
+{
+ WARN_ON(ram_size > SZ_16K);
+
+ if (ram_size <= SZ_1K)
+ return PN_RAMMAP_RAMAREA_1KB;
+ else if (ram_size <= SZ_2K)
+ return PN_RAMMAP_RAMAREA_2KB;
+ else if (ram_size <= SZ_4K)
+ return PN_RAMMAP_RAMAREA_4KB;
+ else if (ram_size <= SZ_8K)
+ return PN_RAMMAP_RAMAREA_8KB;
+ else
+ return PN_RAMMAP_RAMAREA_16KB;
+}
+
+static u32 usb3_calc_rammap_val(struct renesas_usb3_ep *usb3_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ return usb3_ep->rammap_val | PN_RAMMAP_MPKT(usb_endpoint_maxp(desc));
+}
+
+static int usb3_enable_pipe_n(struct renesas_usb3_ep *usb3_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+ unsigned long flags;
+
+ usb3_ep->dir_in = usb_endpoint_dir_in(desc);
+
+ spin_lock_irqsave(&usb3->lock, flags);
+ if (!usb3_pn_change(usb3, usb3_ep->num)) {
+ usb3_write_pn_mod(usb3_ep, desc);
+ usb3_write(usb3, usb3_calc_rammap_val(usb3_ep, desc),
+ USB3_PN_RAMMAP);
+ usb3_pn_con_clear(usb3);
+ usb3_set_bit(usb3, PN_CON_EN, USB3_PN_CON);
+ }
+ spin_unlock_irqrestore(&usb3->lock, flags);
+
+ return 0;
+}
+
+static int usb3_disable_pipe_n(struct renesas_usb3_ep *usb3_ep)
+{
+ struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+ unsigned long flags;
+
+ usb3_ep->halt = false;
+
+ spin_lock_irqsave(&usb3->lock, flags);
+ if (!usb3_pn_change(usb3, usb3_ep->num)) {
+ usb3_write(usb3, 0, USB3_PN_RAMMAP);
+ usb3_clear_bit(usb3, PN_CON_EN, USB3_PN_CON);
+ }
+ spin_unlock_irqrestore(&usb3->lock, flags);
+
+ return 0;
+}
+
+/*------- usb_ep_ops -----------------------------------------------------*/
+static int renesas_usb3_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct renesas_usb3_ep *usb3_ep = usb_ep_to_usb3_ep(_ep);
+
+ return usb3_enable_pipe_n(usb3_ep, desc);
+}
+
+static int renesas_usb3_ep_disable(struct usb_ep *_ep)
+{
+ struct renesas_usb3_ep *usb3_ep = usb_ep_to_usb3_ep(_ep);
+ struct renesas_usb3_request *usb3_req;
+
+ do {
+ usb3_req = usb3_get_request(usb3_ep);
+ if (!usb3_req)
+ break;
+ usb3_request_done(usb3_ep, usb3_req, -ESHUTDOWN);
+ } while (1);
+
+ return usb3_disable_pipe_n(usb3_ep);
+}
+
+static struct usb_request *__renesas_usb3_ep_alloc_request(gfp_t gfp_flags)
+{
+ struct renesas_usb3_request *usb3_req;
+
+ usb3_req = kzalloc(sizeof(struct renesas_usb3_request), gfp_flags);
+ if (!usb3_req)
+ return NULL;
+
+ INIT_LIST_HEAD(&usb3_req->queue);
+
+ return &usb3_req->req;
+}
+
+static void __renesas_usb3_ep_free_request(struct usb_request *_req)
+{
+ struct renesas_usb3_request *usb3_req = usb_req_to_usb3_req(_req);
+
+ kfree(usb3_req);
+}
+
+static struct usb_request *renesas_usb3_ep_alloc_request(struct usb_ep *_ep,
+ gfp_t gfp_flags)
+{
+ return __renesas_usb3_ep_alloc_request(gfp_flags);
+}
+
+static void renesas_usb3_ep_free_request(struct usb_ep *_ep,
+ struct usb_request *_req)
+{
+ __renesas_usb3_ep_free_request(_req);
+}
+
+static int renesas_usb3_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct renesas_usb3_ep *usb3_ep = usb_ep_to_usb3_ep(_ep);
+ struct renesas_usb3_request *usb3_req = usb_req_to_usb3_req(_req);
+ struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+
+ dev_dbg(usb3_to_dev(usb3), "ep_dequeue: ep%2d, %u\n", usb3_ep->num,
+ _req->length);
+
+ usb3_request_done_pipen(usb3, usb3_ep, usb3_req, -ECONNRESET);
+
+ return 0;
+}
+
+static int renesas_usb3_ep_set_halt(struct usb_ep *_ep, int value)
+{
+ return usb3_set_halt(usb_ep_to_usb3_ep(_ep), !!value, false);
+}
+
+static int renesas_usb3_ep_set_wedge(struct usb_ep *_ep)
+{
+ struct renesas_usb3_ep *usb3_ep = usb_ep_to_usb3_ep(_ep);
+
+ usb3_ep->wedge = true;
+ return usb3_set_halt(usb3_ep, true, false);
+}
+
+static void renesas_usb3_ep_fifo_flush(struct usb_ep *_ep)
+{
+ struct renesas_usb3_ep *usb3_ep = usb_ep_to_usb3_ep(_ep);
+ struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+ unsigned long flags;
+
+ if (usb3_ep->num) {
+ spin_lock_irqsave(&usb3->lock, flags);
+ if (!usb3_pn_change(usb3, usb3_ep->num)) {
+ usb3_pn_con_clear(usb3);
+ usb3_set_bit(usb3, PN_CON_EN, USB3_PN_CON);
+ }
+ spin_unlock_irqrestore(&usb3->lock, flags);
+ } else {
+ usb3_p0_con_clear_buffer(usb3);
+ }
+}
+
+static struct usb_ep_ops renesas_usb3_ep_ops = {
+ .enable = renesas_usb3_ep_enable,
+ .disable = renesas_usb3_ep_disable,
+
+ .alloc_request = renesas_usb3_ep_alloc_request,
+ .free_request = renesas_usb3_ep_free_request,
+
+ .queue = renesas_usb3_ep_queue,
+ .dequeue = renesas_usb3_ep_dequeue,
+
+ .set_halt = renesas_usb3_ep_set_halt,
+ .set_wedge = renesas_usb3_ep_set_wedge,
+ .fifo_flush = renesas_usb3_ep_fifo_flush,
+};
+
+/*------- usb_gadget_ops -------------------------------------------------*/
+static int renesas_usb3_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct renesas_usb3 *usb3;
+
+ if (!driver || driver->max_speed < USB_SPEED_FULL ||
+ !driver->setup)
+ return -EINVAL;
+
+ usb3 = gadget_to_renesas_usb3(gadget);
+
+ /* hook up the driver */
+ usb3->driver = driver;
+
+ renesas_usb3_init_controller(usb3);
+
+ return 0;
+}
+
+static int renesas_usb3_stop(struct usb_gadget *gadget)
+{
+ struct renesas_usb3 *usb3 = gadget_to_renesas_usb3(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&usb3->lock, flags);
+ usb3->softconnect = false;
+ usb3->gadget.speed = USB_SPEED_UNKNOWN;
+ usb3->driver = NULL;
+ renesas_usb3_stop_controller(usb3);
+ spin_unlock_irqrestore(&usb3->lock, flags);
+
+ return 0;
+}
+
+static int renesas_usb3_get_frame(struct usb_gadget *_gadget)
+{
+ return -EOPNOTSUPP;
+}
+
+static int renesas_usb3_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct renesas_usb3 *usb3 = gadget_to_renesas_usb3(gadget);
+
+ usb3->softconnect = !!is_on;
+
+ return 0;
+}
+
+static int renesas_usb3_set_selfpowered(struct usb_gadget *gadget, int is_self)
+{
+ gadget->is_selfpowered = !!is_self;
+
+ return 0;
+}
+
+static const struct usb_gadget_ops renesas_usb3_gadget_ops = {
+ .get_frame = renesas_usb3_get_frame,
+ .udc_start = renesas_usb3_start,
+ .udc_stop = renesas_usb3_stop,
+ .pullup = renesas_usb3_pullup,
+ .set_selfpowered = renesas_usb3_set_selfpowered,
+};
+
+/*------- platform_driver ------------------------------------------------*/
+static int renesas_usb3_remove(struct platform_device *pdev)
+{
+ struct renesas_usb3 *usb3 = platform_get_drvdata(pdev);
+
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ usb_del_gadget_udc(&usb3->gadget);
+
+ __renesas_usb3_ep_free_request(usb3->ep0_req);
+
+ return 0;
+}
+
+static int renesas_usb3_init_ep(struct renesas_usb3 *usb3, struct device *dev,
+ const struct renesas_usb3_priv *priv)
+{
+ struct renesas_usb3_ep *usb3_ep;
+ int i;
+
+ /* calculate num_usb3_eps from renesas_usb3_priv */
+ usb3->num_usb3_eps = priv->ramsize_per_ramif * priv->num_ramif * 2 /
+ priv->ramsize_per_pipe + 1;
+
+ if (usb3->num_usb3_eps > USB3_MAX_NUM_PIPES)
+ usb3->num_usb3_eps = USB3_MAX_NUM_PIPES;
+
+ usb3->usb3_ep = devm_kzalloc(dev, sizeof(*usb3_ep) * usb3->num_usb3_eps,
+ GFP_KERNEL);
+ if (!usb3->usb3_ep)
+ return -ENOMEM;
+
+ dev_dbg(dev, "%s: num_usb3_eps = %d\n", __func__, usb3->num_usb3_eps);
+ /*
+ * This driver prepares pipes as the followings:
+ * - odd pipes = IN pipe
+ * - even pipes = OUT pipe (except pipe 0)
+ */
+ usb3_for_each_ep(usb3_ep, usb3, i) {
+ snprintf(usb3_ep->ep_name, sizeof(usb3_ep->ep_name), "ep%d", i);
+ usb3_ep->usb3 = usb3;
+ usb3_ep->num = i;
+ usb3_ep->ep.name = usb3_ep->ep_name;
+ usb3_ep->ep.ops = &renesas_usb3_ep_ops;
+ INIT_LIST_HEAD(&usb3_ep->queue);
+ INIT_LIST_HEAD(&usb3_ep->ep.ep_list);
+ if (!i) {
+ /* for control pipe */
+ usb3->gadget.ep0 = &usb3_ep->ep;
+ usb_ep_set_maxpacket_limit(&usb3_ep->ep,
+ USB3_EP0_HSFS_MAX_PACKET_SIZE);
+ usb3_ep->ep.caps.type_control = true;
+ usb3_ep->ep.caps.dir_in = true;
+ usb3_ep->ep.caps.dir_out = true;
+ continue;
+ }
+
+ /* for bulk or interrupt pipe */
+ usb_ep_set_maxpacket_limit(&usb3_ep->ep, ~0);
+ list_add_tail(&usb3_ep->ep.ep_list, &usb3->gadget.ep_list);
+ usb3_ep->ep.caps.type_bulk = true;
+ usb3_ep->ep.caps.type_int = true;
+ if (i & 1)
+ usb3_ep->ep.caps.dir_in = true;
+ else
+ usb3_ep->ep.caps.dir_out = true;
+ }
+
+ return 0;
+}
+
+static void renesas_usb3_init_ram(struct renesas_usb3 *usb3, struct device *dev,
+ const struct renesas_usb3_priv *priv)
+{
+ struct renesas_usb3_ep *usb3_ep;
+ int i;
+ u32 ramif[2], basead[2]; /* index 0 = for IN pipes */
+ u32 *cur_ramif, *cur_basead;
+ u32 val;
+
+ memset(ramif, 0, sizeof(ramif));
+ memset(basead, 0, sizeof(basead));
+
+ /*
+ * This driver prepares pipes as the followings:
+ * - all pipes = the same size as "ramsize_per_pipe"
+ * Please refer to the "Method of Specifying RAM Mapping"
+ */
+ usb3_for_each_ep(usb3_ep, usb3, i) {
+ if (!i)
+ continue; /* out of scope if ep num = 0 */
+ if (usb3_ep->ep.caps.dir_in) {
+ cur_ramif = &ramif[0];
+ cur_basead = &basead[0];
+ } else {
+ cur_ramif = &ramif[1];
+ cur_basead = &basead[1];
+ }
+
+ if (*cur_basead > priv->ramsize_per_ramif)
+ continue; /* out of memory for IN or OUT pipe */
+
+ /* calculate rammap_val */
+ val = PN_RAMMAP_RAMIF(*cur_ramif);
+ val |= usb3_calc_ramarea(priv->ramsize_per_pipe);
+ val |= PN_RAMMAP_BASEAD(*cur_basead);
+ usb3_ep->rammap_val = val;
+
+ dev_dbg(dev, "ep%2d: val = %08x, ramif = %d, base = %x\n",
+ i, val, *cur_ramif, *cur_basead);
+
+ /* update current ramif */
+ if (*cur_ramif + 1 == priv->num_ramif) {
+ *cur_ramif = 0;
+ *cur_basead += priv->ramsize_per_pipe;
+ } else {
+ (*cur_ramif)++;
+ }
+ }
+}
+
+static const struct renesas_usb3_priv renesas_usb3_priv_r8a7795 = {
+ .ramsize_per_ramif = SZ_16K,
+ .num_ramif = 2,
+ .ramsize_per_pipe = SZ_4K,
+ .workaround_for_vbus = true,
+};
+
+static const struct of_device_id usb3_of_match[] = {
+ {
+ .compatible = "renesas,r8a7795-usb3-peri",
+ .data = &renesas_usb3_priv_r8a7795,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, usb3_of_match);
+
+static int renesas_usb3_probe(struct platform_device *pdev)
+{
+ struct renesas_usb3 *usb3;
+ struct resource *res;
+ const struct of_device_id *match;
+ int irq, ret;
+ const struct renesas_usb3_priv *priv;
+
+ match = of_match_node(usb3_of_match, pdev->dev.of_node);
+ if (!match)
+ return -ENODEV;
+ priv = match->data;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return -ENODEV;
+
+ usb3 = devm_kzalloc(&pdev->dev, sizeof(*usb3), GFP_KERNEL);
+ if (!usb3)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ usb3->reg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(usb3->reg))
+ return PTR_ERR(usb3->reg);
+
+ platform_set_drvdata(pdev, usb3);
+ spin_lock_init(&usb3->lock);
+
+ usb3->gadget.ops = &renesas_usb3_gadget_ops;
+ usb3->gadget.name = udc_name;
+ usb3->gadget.max_speed = USB_SPEED_SUPER;
+ INIT_LIST_HEAD(&usb3->gadget.ep_list);
+ ret = renesas_usb3_init_ep(usb3, &pdev->dev, priv);
+ if (ret < 0)
+ return ret;
+ renesas_usb3_init_ram(usb3, &pdev->dev, priv);
+
+ ret = devm_request_irq(&pdev->dev, irq, renesas_usb3_irq, 0,
+ dev_name(&pdev->dev), usb3);
+ if (ret < 0)
+ return ret;
+
+ /* for ep0 handling */
+ usb3->ep0_req = __renesas_usb3_ep_alloc_request(GFP_KERNEL);
+ if (!usb3->ep0_req)
+ return -ENOMEM;
+
+ ret = usb_add_gadget_udc(&pdev->dev, &usb3->gadget);
+ if (ret < 0)
+ goto err_add_udc;
+
+ usb3->workaround_for_vbus = priv->workaround_for_vbus;
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
+
+ dev_info(&pdev->dev, "probed\n");
+
+ return 0;
+
+err_add_udc:
+ __renesas_usb3_ep_free_request(usb3->ep0_req);
+
+ return ret;
+}
+
+static struct platform_driver renesas_usb3_driver = {
+ .probe = renesas_usb3_probe,
+ .remove = renesas_usb3_remove,
+ .driver = {
+ .name = (char *)udc_name,
+ .of_match_table = of_match_ptr(usb3_of_match),
+ },
+};
+module_platform_driver(renesas_usb3_driver);
+
+MODULE_DESCRIPTION("Renesas USB3.0 Peripheral driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>");
+MODULE_ALIAS("platform:renesas_usb3");
diff --git a/drivers/usb/gadget/udc/s3c-hsudc.c b/drivers/usb/gadget/udc/s3c-hsudc.c
index e9def42ce50d..82a9e2a3bedc 100644
--- a/drivers/usb/gadget/udc/s3c-hsudc.c
+++ b/drivers/usb/gadget/udc/s3c-hsudc.c
@@ -569,7 +569,7 @@ static int s3c_hsudc_handle_reqfeat(struct s3c_hsudc *hsudc,
hsep = &hsudc->ep[ep_num];
switch (le16_to_cpu(ctrl->wValue)) {
case USB_ENDPOINT_HALT:
- if (set || (!set && !hsep->wedge))
+ if (set || !hsep->wedge)
s3c_hsudc_set_halt(&hsep->ep, set);
return 0;
}
diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
index f660afba715d..fd73a3ea07c2 100644
--- a/drivers/usb/gadget/udc/udc-core.c
+++ b/drivers/usb/gadget/udc/udc-core.c
@@ -51,8 +51,12 @@ struct usb_udc {
static struct class *udc_class;
static LIST_HEAD(udc_list);
+static LIST_HEAD(gadget_driver_pending_list);
static DEFINE_MUTEX(udc_lock);
+static int udc_bind_to_driver(struct usb_udc *udc,
+ struct usb_gadget_driver *driver);
+
/* ------------------------------------------------------------------------- */
#ifdef CONFIG_HAS_DMA
@@ -356,6 +360,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
void (*release)(struct device *dev))
{
struct usb_udc *udc;
+ struct usb_gadget_driver *driver;
int ret = -ENOMEM;
udc = kzalloc(sizeof(*udc), GFP_KERNEL);
@@ -403,6 +408,18 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
udc->vbus = true;
+ /* pick up one of pending gadget drivers */
+ list_for_each_entry(driver, &gadget_driver_pending_list, pending) {
+ if (!driver->udc_name || strcmp(driver->udc_name,
+ dev_name(&udc->dev)) == 0) {
+ ret = udc_bind_to_driver(udc, driver);
+ if (ret)
+ goto err4;
+ list_del(&driver->pending);
+ break;
+ }
+ }
+
mutex_unlock(&udc_lock);
return 0;
@@ -473,10 +490,14 @@ void usb_del_gadget_udc(struct usb_gadget *gadget)
mutex_lock(&udc_lock);
list_del(&udc->list);
- mutex_unlock(&udc_lock);
- if (udc->driver)
+ if (udc->driver) {
+ struct usb_gadget_driver *driver = udc->driver;
+
usb_gadget_remove_driver(udc);
+ list_add(&driver->pending, &gadget_driver_pending_list);
+ }
+ mutex_unlock(&udc_lock);
kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);
flush_work(&gadget->work);
@@ -520,50 +541,36 @@ err1:
return ret;
}
-int usb_udc_attach_driver(const char *name, struct usb_gadget_driver *driver)
-{
- struct usb_udc *udc = NULL;
- int ret = -ENODEV;
-
- mutex_lock(&udc_lock);
- list_for_each_entry(udc, &udc_list, list) {
- ret = strcmp(name, dev_name(&udc->dev));
- if (!ret)
- break;
- }
- if (ret) {
- ret = -ENODEV;
- goto out;
- }
- if (udc->driver) {
- ret = -EBUSY;
- goto out;
- }
- ret = udc_bind_to_driver(udc, driver);
-out:
- mutex_unlock(&udc_lock);
- return ret;
-}
-EXPORT_SYMBOL_GPL(usb_udc_attach_driver);
-
int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
{
struct usb_udc *udc = NULL;
- int ret;
+ int ret = -ENODEV;
if (!driver || !driver->bind || !driver->setup)
return -EINVAL;
mutex_lock(&udc_lock);
- list_for_each_entry(udc, &udc_list, list) {
- /* For now we take the first one */
- if (!udc->driver)
+ if (driver->udc_name) {
+ list_for_each_entry(udc, &udc_list, list) {
+ ret = strcmp(driver->udc_name, dev_name(&udc->dev));
+ if (!ret)
+ break;
+ }
+ if (!ret && !udc->driver)
goto found;
+ } else {
+ list_for_each_entry(udc, &udc_list, list) {
+ /* For now we take the first one */
+ if (!udc->driver)
+ goto found;
+ }
}
- pr_debug("couldn't find an available UDC\n");
+ list_add_tail(&driver->pending, &gadget_driver_pending_list);
+ pr_info("udc-core: couldn't find an available UDC - added [%s] to list of pending drivers\n",
+ driver->function);
mutex_unlock(&udc_lock);
- return -ENODEV;
+ return 0;
found:
ret = udc_bind_to_driver(udc, driver);
mutex_unlock(&udc_lock);
@@ -589,6 +596,10 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
break;
}
+ if (ret) {
+ list_del(&driver->pending);
+ ret = 0;
+ }
mutex_unlock(&udc_lock);
return ret;
}
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 3bb08870148f..daa563ff1fa0 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -41,6 +41,15 @@ config USB_XHCI_PLATFORM
If unsure, say N.
+config USB_XHCI_MTK
+ tristate "xHCI support for Mediatek MT65xx"
+ select MFD_SYSCON
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ ---help---
+ Say 'Y' to enable the support for the xHCI host controller
+ found in Mediatek MT65xx SoCs.
+ If unsure, say N.
+
config USB_XHCI_MVEBU
tristate "xHCI support for Marvell Armada 375/38x"
select USB_XHCI_PLATFORM
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index e7558abc994d..65a06b4382bf 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -13,6 +13,9 @@ fhci-$(CONFIG_FHCI_DEBUG) += fhci-dbg.o
xhci-hcd-y := xhci.o xhci-mem.o
xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o
xhci-hcd-y += xhci-trace.o
+ifneq ($(CONFIG_USB_XHCI_MTK), )
+ xhci-hcd-y += xhci-mtk-sch.o
+endif
xhci-plat-hcd-y := xhci-plat.o
ifneq ($(CONFIG_USB_XHCI_MVEBU), )
@@ -64,6 +67,7 @@ obj-$(CONFIG_USB_FHCI_HCD) += fhci.o
obj-$(CONFIG_USB_XHCI_HCD) += xhci-hcd.o
obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o
obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o
+obj-$(CONFIG_USB_XHCI_MTK) += xhci-mtk.o
obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
diff --git a/drivers/usb/host/bcma-hcd.c b/drivers/usb/host/bcma-hcd.c
index 5398e3d42822..291aaa2baed8 100644
--- a/drivers/usb/host/bcma-hcd.c
+++ b/drivers/usb/host/bcma-hcd.c
@@ -21,6 +21,7 @@
*/
#include <linux/bcma/bcma.h>
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/slab.h>
@@ -36,6 +37,7 @@ MODULE_LICENSE("GPL");
struct bcma_hcd_device {
struct platform_device *ehci_dev;
struct platform_device *ohci_dev;
+ struct gpio_desc *gpio_desc;
};
/* Wait for bitmask in a register to get set or cleared.
@@ -228,19 +230,12 @@ static void bcma_hcd_init_chip_arm(struct bcma_device *dev)
static void bcma_hci_platform_power_gpio(struct bcma_device *dev, bool val)
{
- int gpio;
+ struct bcma_hcd_device *usb_dev = bcma_get_drvdata(dev);
- gpio = of_get_named_gpio(dev->dev.of_node, "vcc-gpio", 0);
- if (!gpio_is_valid(gpio))
+ if (IS_ERR_OR_NULL(usb_dev->gpio_desc))
return;
- if (val) {
- gpio_request(gpio, "bcma-hcd-gpio");
- gpio_set_value(gpio, 1);
- } else {
- gpio_set_value(gpio, 0);
- gpio_free(gpio);
- }
+ gpiod_set_value(usb_dev->gpio_desc, val);
}
static const struct usb_ehci_pdata ehci_pdata = {
@@ -314,7 +309,11 @@ static int bcma_hcd_probe(struct bcma_device *dev)
if (!usb_dev)
return -ENOMEM;
- bcma_hci_platform_power_gpio(dev, true);
+ if (dev->dev.of_node)
+ usb_dev->gpio_desc = devm_get_gpiod_from_child(&dev->dev, "vcc",
+ &dev->dev.of_node->fwnode);
+ if (!IS_ERR_OR_NULL(usb_dev->gpio_desc))
+ gpiod_direction_output(usb_dev->gpio_desc, 1);
switch (dev->id.id) {
case BCMA_CORE_NS_USB20:
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index b26b96e25a13..b7d623f1523c 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -436,7 +436,8 @@ static void qh_lines (
scratch = hc32_to_cpup(ehci, &hw->hw_info1);
hw_curr = (mark == '*') ? hc32_to_cpup(ehci, &hw->hw_current) : 0;
temp = scnprintf (next, size,
- "qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)",
+ "qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)"
+ " [cur %08x next %08x buf[0] %08x]",
qh, scratch & 0x007f,
speed_char (scratch),
(scratch >> 8) & 0x000f,
@@ -444,7 +445,10 @@ static void qh_lines (
hc32_to_cpup(ehci, &hw->hw_token), mark,
(cpu_to_hc32(ehci, QTD_TOGGLE) & hw->hw_token)
? "data1" : "data0",
- (hc32_to_cpup(ehci, &hw->hw_alt_next) >> 1) & 0x0f);
+ (hc32_to_cpup(ehci, &hw->hw_alt_next) >> 1) & 0x0f,
+ hc32_to_cpup(ehci, &hw->hw_current),
+ hc32_to_cpup(ehci, &hw->hw_qtd_next),
+ hc32_to_cpup(ehci, &hw->hw_buf[0]));
size -= temp;
next += temp;
@@ -464,7 +468,8 @@ static void qh_lines (
mark = '/';
}
temp = snprintf (next, size,
- "\n\t%p%c%s len=%d %08x urb %p",
+ "\n\t%p%c%s len=%d %08x urb %p"
+ " [td %08x buf[0] %08x]",
td, mark, ({ char *tmp;
switch ((scratch>>8)&0x03) {
case 0: tmp = "out"; break;
@@ -474,7 +479,9 @@ static void qh_lines (
} tmp;}),
(scratch >> 16) & 0x7fff,
scratch,
- td->urb);
+ td->urb,
+ (u32) td->qtd_dma,
+ hc32_to_cpup(ehci, &td->hw_buf[0]));
if (size < temp)
temp = size;
size -= temp;
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 48c92bf78bd0..14178bbf0694 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -98,7 +98,7 @@ module_param (park, uint, S_IRUGO);
MODULE_PARM_DESC (park, "park setting; 1-3 back-to-back async packets");
/* for flakey hardware, ignore overcurrent indicators */
-static bool ignore_oc = 0;
+static bool ignore_oc;
module_param (ignore_oc, bool, S_IRUGO);
MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications");
diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
index b6205fac3567..4de43011df23 100644
--- a/drivers/usb/host/ehci-mem.c
+++ b/drivers/usb/host/ehci-mem.c
@@ -128,21 +128,13 @@ static void ehci_mem_cleanup (struct ehci_hcd *ehci)
ehci->dummy = NULL;
/* DMA consistent memory and pools */
- if (ehci->qtd_pool)
- dma_pool_destroy (ehci->qtd_pool);
+ dma_pool_destroy(ehci->qtd_pool);
ehci->qtd_pool = NULL;
-
- if (ehci->qh_pool) {
- dma_pool_destroy (ehci->qh_pool);
- ehci->qh_pool = NULL;
- }
-
- if (ehci->itd_pool)
- dma_pool_destroy (ehci->itd_pool);
+ dma_pool_destroy(ehci->qh_pool);
+ ehci->qh_pool = NULL;
+ dma_pool_destroy(ehci->itd_pool);
ehci->itd_pool = NULL;
-
- if (ehci->sitd_pool)
- dma_pool_destroy (ehci->sitd_pool);
+ dma_pool_destroy(ehci->sitd_pool);
ehci->sitd_pool = NULL;
if (ehci->periodic)
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c
index c4f84c81de01..c23e2858c815 100644
--- a/drivers/usb/host/ehci-msm.c
+++ b/drivers/usb/host/ehci-msm.c
@@ -57,8 +57,8 @@ static int ehci_msm_reset(struct usb_hcd *hcd)
/* bursts of unspecified length. */
writel(0, USB_AHBBURST);
- /* Use the AHB transactor */
- writel(0, USB_AHBMODE);
+ /* Use the AHB transactor, allow posted data writes */
+ writel(0x8, USB_AHBMODE);
/* Disable streaming mode and select host mode */
writel(0x13, USB_USBMODE);
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 54f5332f814d..aad0777240d3 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -132,10 +132,14 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh)
* qtd is updated in qh_completions(). Update the QH
* overlay here.
*/
- if (qh->hw->hw_token & ACTIVE_BIT(ehci))
+ if (qh->hw->hw_token & ACTIVE_BIT(ehci)) {
qh->hw->hw_qtd_next = qtd->hw_next;
- else
+ if (qh->should_be_inactive)
+ ehci_warn(ehci, "qh %p should be inactive!\n", qh);
+ } else {
qh_update(ehci, qh, qtd);
+ }
+ qh->should_be_inactive = 0;
}
/*-------------------------------------------------------------------------*/
@@ -438,6 +442,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
(hw->hw_token & ACTIVE_BIT(ehci))) {
token = hc32_to_cpu(ehci, hw->hw_token);
hw->hw_token &= ~ACTIVE_BIT(ehci);
+ qh->should_be_inactive = 1;
/* An unlink may leave an incomplete
* async transaction in the TT buffer.
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 46f62e41bcde..ec61aedb0067 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -439,6 +439,7 @@ struct ehci_qh {
unsigned dequeue_during_giveback:1;
unsigned exception:1; /* got a fault, or an unlink
was requested */
+ unsigned should_be_inactive:1;
};
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/fhci-tds.c b/drivers/usb/host/fhci-tds.c
index 1498061f0aea..f82ad5df1b0d 100644
--- a/drivers/usb/host/fhci-tds.c
+++ b/drivers/usb/host/fhci-tds.c
@@ -85,7 +85,7 @@ static struct usb_td __iomem *next_bd(struct usb_td __iomem *base,
void fhci_push_dummy_bd(struct endpoint *ep)
{
- if (ep->already_pushed_dummy_bd == false) {
+ if (!ep->already_pushed_dummy_bd) {
u16 td_status = in_be16(&ep->empty_td->status);
out_be32(&ep->empty_td->buf_ptr, DUMMY_BD_BUFFER);
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index 342ffd140122..8c6e15bd6ff0 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -473,6 +473,8 @@ static int ohci_hcd_at91_drv_probe(struct platform_device *pdev)
if (!pdata)
return -ENOMEM;
+ pdev->dev.platform_data = pdata;
+
if (!of_property_read_u32(np, "num-ports", &ports))
pdata->ports = ports;
@@ -483,6 +485,7 @@ static int ohci_hcd_at91_drv_probe(struct platform_device *pdev)
*/
if (i >= pdata->ports) {
pdata->vbus_pin[i] = -EINVAL;
+ pdata->overcurrent_pin[i] = -EINVAL;
continue;
}
@@ -513,10 +516,8 @@ static int ohci_hcd_at91_drv_probe(struct platform_device *pdev)
}
at91_for_each_port(i) {
- if (i >= pdata->ports) {
- pdata->overcurrent_pin[i] = -EINVAL;
- continue;
- }
+ if (i >= pdata->ports)
+ break;
pdata->overcurrent_pin[i] =
of_get_named_gpio_flags(np, "atmel,oc-gpio", i, &flags);
@@ -552,8 +553,6 @@ static int ohci_hcd_at91_drv_probe(struct platform_device *pdev)
}
}
- pdev->dev.platform_data = pdata;
-
device_init_wakeup(&pdev->dev, 1);
return usb_hcd_at91_probe(&ohci_at91_hc_driver, pdev);
}
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 760cb57e954e..04dcedfdebf8 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -99,13 +99,13 @@ static void io_watchdog_func(unsigned long _ohci);
/* Some boards misreport power switching/overcurrent */
-static bool distrust_firmware = 1;
+static bool distrust_firmware = true;
module_param (distrust_firmware, bool, 0);
MODULE_PARM_DESC (distrust_firmware,
"true to distrust firmware power/overcurrent setup");
/* Some boards leave IR set wrongly, since they fail BIOS/SMM handshakes */
-static bool no_handshake = 0;
+static bool no_handshake;
module_param (no_handshake, bool, 0);
MODULE_PARM_DESC (no_handshake, "true (not default) disables BIOS handshake");
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
index ba1bec7db026..e8c006e7a960 100644
--- a/drivers/usb/host/ohci-pxa27x.c
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -365,19 +365,19 @@ static int ohci_pxa_of_init(struct platform_device *pdev)
if (!pdata)
return -ENOMEM;
- if (of_get_property(np, "marvell,enable-port1", NULL))
+ if (of_property_read_bool(np, "marvell,enable-port1"))
pdata->flags |= ENABLE_PORT1;
- if (of_get_property(np, "marvell,enable-port2", NULL))
+ if (of_property_read_bool(np, "marvell,enable-port2"))
pdata->flags |= ENABLE_PORT2;
- if (of_get_property(np, "marvell,enable-port3", NULL))
+ if (of_property_read_bool(np, "marvell,enable-port3"))
pdata->flags |= ENABLE_PORT3;
- if (of_get_property(np, "marvell,port-sense-low", NULL))
+ if (of_property_read_bool(np, "marvell,port-sense-low"))
pdata->flags |= POWER_SENSE_LOW;
- if (of_get_property(np, "marvell,power-control-low", NULL))
+ if (of_property_read_bool(np, "marvell,power-control-low"))
pdata->flags |= POWER_CONTROL_LOW;
- if (of_get_property(np, "marvell,no-oc-protection", NULL))
+ if (of_property_read_bool(np, "marvell,no-oc-protection"))
pdata->flags |= NO_OC_PROTECTION;
- if (of_get_property(np, "marvell,oc-mode-perport", NULL))
+ if (of_property_read_bool(np, "marvell,oc-mode-perport"))
pdata->flags |= OC_MODE_PERPORT;
if (!of_property_read_u32(np, "marvell,power-on-delay", &tmp))
pdata->power_on_delay = tmp;
diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
index 1f139d82cee0..bc74aca8a54c 100644
--- a/drivers/usb/host/oxu210hp-hcd.c
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -394,8 +394,7 @@ static void ehci_quiesce(struct oxu_hcd *oxu)
u32 temp;
#ifdef DEBUG
- if (!HC_IS_RUNNING(oxu_to_hcd(oxu)->state))
- BUG();
+ BUG_ON(!HC_IS_RUNNING(oxu_to_hcd(oxu)->state));
#endif
/* wait for any schedule enables/disables to take effect */
@@ -1709,9 +1708,8 @@ static void start_unlink_async(struct oxu_hcd *oxu, struct ehci_qh *qh)
#ifdef DEBUG
assert_spin_locked(&oxu->lock);
- if (oxu->reclaim || (qh->qh_state != QH_STATE_LINKED
- && qh->qh_state != QH_STATE_UNLINK_WAIT))
- BUG();
+ BUG_ON(oxu->reclaim || (qh->qh_state != QH_STATE_LINKED
+ && qh->qh_state != QH_STATE_UNLINK_WAIT));
#endif
/* stop async schedule right now? */
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index f9400564cb72..26cb8c861e6e 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -984,24 +984,17 @@ static void quirk_usb_handoff_xhci(struct pci_dev *pdev)
* Find the Legacy Support Capability register -
* this is optional for xHCI host controllers.
*/
- ext_cap_offset = xhci_find_next_cap_offset(base, XHCI_HCC_PARAMS_OFFSET);
- do {
- if ((ext_cap_offset + sizeof(val)) > len) {
- /* We're reading garbage from the controller */
- dev_warn(&pdev->dev,
- "xHCI controller failing to respond");
- return;
- }
+ ext_cap_offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_LEGACY);
- if (!ext_cap_offset)
- /* We've reached the end of the extended capabilities */
- goto hc_init;
+ if (!ext_cap_offset)
+ goto hc_init;
- val = readl(base + ext_cap_offset);
- if (XHCI_EXT_CAPS_ID(val) == XHCI_EXT_CAPS_LEGACY)
- break;
- ext_cap_offset = xhci_find_next_cap_offset(base, ext_cap_offset);
- } while (1);
+ if ((ext_cap_offset + sizeof(val)) > len) {
+ /* We're reading garbage from the controller */
+ dev_warn(&pdev->dev, "xHCI controller failing to respond");
+ return;
+ }
+ val = readl(base + ext_cap_offset);
/* If the BIOS owns the HC, signal that the OS wants it, and wait */
if (val & XHCI_HC_BIOS_OWNED) {
diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c
index 692ccc69345e..05c85c7baf84 100644
--- a/drivers/usb/host/u132-hcd.c
+++ b/drivers/usb/host/u132-hcd.c
@@ -73,7 +73,7 @@ MODULE_LICENSE("GPL");
#define INT_MODULE_PARM(n, v) static int n = v;module_param(n, int, 0444)
INT_MODULE_PARM(testing, 0);
/* Some boards misreport power switching/overcurrent*/
-static bool distrust_firmware = 1;
+static bool distrust_firmware = true;
module_param(distrust_firmware, bool, 0);
MODULE_PARM_DESC(distrust_firmware, "true to distrust firmware power/overcurren"
"t setup");
diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c
index 1b28a000d5c6..9c6635d43db0 100644
--- a/drivers/usb/host/uhci-debug.c
+++ b/drivers/usb/host/uhci-debug.c
@@ -584,27 +584,8 @@ static int uhci_debug_open(struct inode *inode, struct file *file)
static loff_t uhci_debug_lseek(struct file *file, loff_t off, int whence)
{
- struct uhci_debug *up;
- loff_t new = -1;
-
- up = file->private_data;
-
- /*
- * XXX: atomic 64bit seek access, but that needs to be fixed in the VFS
- */
- switch (whence) {
- case 0:
- new = off;
- break;
- case 1:
- new = file->f_pos + off;
- break;
- }
-
- if (new < 0 || new > up->size)
- return -EINVAL;
-
- return (file->f_pos = new);
+ struct uhci_debug *up = file->private_data;
+ return no_seek_end_llseek_size(file, off, whence, up->size);
}
static ssize_t uhci_debug_read(struct file *file, char __user *buf,
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index da6f56d996ce..c17ea1589b83 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -248,11 +248,10 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci,
dma_addr_t dma_handle;
struct uhci_qh *qh;
- qh = dma_pool_alloc(uhci->qh_pool, GFP_ATOMIC, &dma_handle);
+ qh = dma_pool_zalloc(uhci->qh_pool, GFP_ATOMIC, &dma_handle);
if (!qh)
return NULL;
- memset(qh, 0, sizeof(*qh));
qh->dma_handle = dma_handle;
qh->element = UHCI_PTR_TERM(uhci);
diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c
index dc31c425ce01..1a8e960d073b 100644
--- a/drivers/usb/host/whci/qset.c
+++ b/drivers/usb/host/whci/qset.c
@@ -30,10 +30,9 @@ struct whc_qset *qset_alloc(struct whc *whc, gfp_t mem_flags)
struct whc_qset *qset;
dma_addr_t dma;
- qset = dma_pool_alloc(whc->qset_pool, mem_flags, &dma);
+ qset = dma_pool_zalloc(whc->qset_pool, mem_flags, &dma);
if (qset == NULL)
return NULL;
- memset(qset, 0, sizeof(struct whc_qset));
qset->qset_dma = dma;
qset->whc = whc;
@@ -377,6 +376,10 @@ static int qset_fill_page_list(struct whc *whc, struct whc_std *std, gfp_t mem_f
if (std->pl_virt == NULL)
return -ENOMEM;
std->dma_addr = dma_map_single(whc->wusbhc.dev, std->pl_virt, pl_len, DMA_TO_DEVICE);
+ if (dma_mapping_error(whc->wusbhc.dev, std->dma_addr)) {
+ kfree(std->pl_virt);
+ return -EFAULT;
+ }
for (p = 0; p < std->num_pointers; p++) {
std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr);
@@ -396,7 +399,7 @@ static void urb_dequeue_work(struct work_struct *work)
struct whc *whc = qset->whc;
unsigned long flags;
- if (wurb->is_async == true)
+ if (wurb->is_async)
asl_update(whc, WUSBCMD_ASYNC_UPDATED
| WUSBCMD_ASYNC_SYNCED_DB
| WUSBCMD_ASYNC_QSET_RM);
diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h
index 9fe3225e6c61..04ce6b156b35 100644
--- a/drivers/usb/host/xhci-ext-caps.h
+++ b/drivers/usb/host/xhci-ext-caps.h
@@ -91,66 +91,39 @@
#include <linux/io.h>
/**
- * Return the next extended capability pointer register.
- *
- * @base PCI register base address.
- *
- * @ext_offset Offset of the 32-bit register that contains the extended
- * capabilites pointer. If searching for the first extended capability, pass
- * in XHCI_HCC_PARAMS_OFFSET. If searching for the next extended capability,
- * pass in the offset of the current extended capability register.
- *
- * Returns 0 if there is no next extended capability register or returns the register offset
- * from the PCI registers base address.
- */
-static inline int xhci_find_next_cap_offset(void __iomem *base, int ext_offset)
-{
- u32 next;
-
- next = readl(base + ext_offset);
-
- if (ext_offset == XHCI_HCC_PARAMS_OFFSET) {
- /* Find the first extended capability */
- next = XHCI_HCC_EXT_CAPS(next);
- ext_offset = 0;
- } else {
- /* Find the next extended capability */
- next = XHCI_EXT_CAPS_NEXT(next);
- }
-
- if (!next)
- return 0;
- /*
- * Address calculation from offset of extended capabilities
- * (or HCCPARAMS) register - see section 5.3.6 and section 7.
- */
- return ext_offset + (next << 2);
-}
-
-/**
* Find the offset of the extended capabilities with capability ID id.
*
- * @base PCI MMIO registers base address.
- * @ext_offset Offset from base of the first extended capability to look at,
- * or the address of HCCPARAMS.
- * @id Extended capability ID to search for.
+ * @base PCI MMIO registers base address.
+ * @start address at which to start looking, (0 or HCC_PARAMS to start at
+ * beginning of list)
+ * @id Extended capability ID to search for.
*
- * This uses an arbitrary limit of XHCI_MAX_EXT_CAPS extended capabilities
- * to make sure that the list doesn't contain a loop.
+ * Returns the offset of the next matching extended capability structure.
+ * Some capabilities can occur several times, e.g., the XHCI_EXT_CAPS_PROTOCOL,
+ * and this provides a way to find them all.
*/
-static inline int xhci_find_ext_cap_by_id(void __iomem *base, int ext_offset, int id)
+
+static inline int xhci_find_next_ext_cap(void __iomem *base, u32 start, int id)
{
u32 val;
- int limit = XHCI_MAX_EXT_CAPS;
-
- while (ext_offset && limit > 0) {
- val = readl(base + ext_offset);
- if (XHCI_EXT_CAPS_ID(val) == id)
- break;
- ext_offset = xhci_find_next_cap_offset(base, ext_offset);
- limit--;
- }
- if (limit > 0)
- return ext_offset;
+ u32 next;
+ u32 offset;
+
+ offset = start;
+ if (!start || start == XHCI_HCC_PARAMS_OFFSET) {
+ val = readl(base + XHCI_HCC_PARAMS_OFFSET);
+ offset = XHCI_HCC_EXT_CAPS(val) << 2;
+ if (!offset)
+ return 0;
+ };
+ do {
+ val = readl(base + offset);
+ if (XHCI_EXT_CAPS_ID(val) == id && offset != start)
+ return offset;
+
+ next = XHCI_EXT_CAPS_NEXT(val);
+ offset += next << 2;
+ } while (next);
+
return 0;
}
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 0230965fb78c..b30b4ce294d3 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -733,8 +733,30 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
if ((raw_port_status & PORT_RESET) ||
!(raw_port_status & PORT_PE))
return 0xffffffff;
- if (time_after_eq(jiffies,
- bus_state->resume_done[wIndex])) {
+ /* did port event handler already start resume timing? */
+ if (!bus_state->resume_done[wIndex]) {
+ /* If not, maybe we are in a host initated resume? */
+ if (test_bit(wIndex, &bus_state->resuming_ports)) {
+ /* Host initated resume doesn't time the resume
+ * signalling using resume_done[].
+ * It manually sets RESUME state, sleeps 20ms
+ * and sets U0 state. This should probably be
+ * changed, but not right now.
+ */
+ } else {
+ /* port resume was discovered now and here,
+ * start resume timing
+ */
+ unsigned long timeout = jiffies +
+ msecs_to_jiffies(USB_RESUME_TIMEOUT);
+
+ set_bit(wIndex, &bus_state->resuming_ports);
+ bus_state->resume_done[wIndex] = timeout;
+ mod_timer(&hcd->rh_timer, timeout);
+ }
+ /* Has resume been signalled for USB_RESUME_TIME yet? */
+ } else if (time_after_eq(jiffies,
+ bus_state->resume_done[wIndex])) {
int time_left;
xhci_dbg(xhci, "Resume USB2 port %d\n",
@@ -775,13 +797,26 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
} else {
/*
* The resume has been signaling for less than
- * 20ms. Report the port status as SUSPEND,
- * let the usbcore check port status again
- * and clear resume signaling later.
+ * USB_RESUME_TIME. Report the port status as SUSPEND,
+ * let the usbcore check port status again and clear
+ * resume signaling later.
*/
status |= USB_PORT_STAT_SUSPEND;
}
}
+ /*
+ * Clear stale usb2 resume signalling variables in case port changed
+ * state during resume signalling. For example on error
+ */
+ if ((bus_state->resume_done[wIndex] ||
+ test_bit(wIndex, &bus_state->resuming_ports)) &&
+ (raw_port_status & PORT_PLS_MASK) != XDEV_U3 &&
+ (raw_port_status & PORT_PLS_MASK) != XDEV_RESUME) {
+ bus_state->resume_done[wIndex] = 0;
+ clear_bit(wIndex, &bus_state->resuming_ports);
+ }
+
+
if ((raw_port_status & PORT_PLS_MASK) == XDEV_U0 &&
(raw_port_status & PORT_POWER)) {
if (bus_state->suspended_ports & (1 << wIndex)) {
@@ -820,7 +855,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
xhci_hub_report_usb2_link_state(&status, raw_port_status);
}
if (bus_state->port_c_suspend & (1 << wIndex))
- status |= 1 << USB_PORT_FEAT_C_SUSPEND;
+ status |= USB_PORT_STAT_C_SUSPEND << 16;
return status;
}
@@ -1115,6 +1150,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
if ((temp & PORT_PE) == 0)
goto error;
+ set_bit(wIndex, &bus_state->resuming_ports);
xhci_set_link_state(xhci, port_array, wIndex,
XDEV_RESUME);
spin_unlock_irqrestore(&xhci->lock, flags);
@@ -1122,6 +1158,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
spin_lock_irqsave(&xhci->lock, flags);
xhci_set_link_state(xhci, port_array, wIndex,
XDEV_U0);
+ clear_bit(wIndex, &bus_state->resuming_ports);
}
bus_state->port_c_suspend |= 1 << wIndex;
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index c48cbe731356..5cd080e0a685 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -47,13 +47,12 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci,
if (!seg)
return NULL;
- seg->trbs = dma_pool_alloc(xhci->segment_pool, flags, &dma);
+ seg->trbs = dma_pool_zalloc(xhci->segment_pool, flags, &dma);
if (!seg->trbs) {
kfree(seg);
return NULL;
}
- memset(seg->trbs, 0, TRB_SEGMENT_SIZE);
/* If the cycle state is 0, set the cycle bit to 1 for all the TRBs */
if (cycle_state == 0) {
for (i = 0; i < TRBS_PER_SEGMENT; i++)
@@ -517,12 +516,11 @@ static struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci
if (type == XHCI_CTX_TYPE_INPUT)
ctx->size += CTX_SIZE(xhci->hcc_params);
- ctx->bytes = dma_pool_alloc(xhci->device_pool, flags, &ctx->dma);
+ ctx->bytes = dma_pool_zalloc(xhci->device_pool, flags, &ctx->dma);
if (!ctx->bytes) {
kfree(ctx);
return NULL;
}
- memset(ctx->bytes, 0, ctx->size);
return ctx;
}
@@ -1245,7 +1243,7 @@ static unsigned int xhci_microframes_to_exponent(struct usb_device *udev,
interval = fls(desc_interval) - 1;
interval = clamp_val(interval, min_exponent, max_exponent);
if ((1 << interval) != desc_interval)
- dev_warn(&udev->dev,
+ dev_dbg(&udev->dev,
"ep %#x - rounding interval to %d microframes, ep desc says %d microframes\n",
ep->desc.bEndpointAddress,
1 << interval,
@@ -2064,17 +2062,19 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
}
static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
- __le32 __iomem *addr, u8 major_revision, int max_caps)
+ __le32 __iomem *addr, int max_caps)
{
u32 temp, port_offset, port_count;
int i;
+ u8 major_revision;
struct xhci_hub *rhub;
temp = readl(addr);
+ major_revision = XHCI_EXT_PORT_MAJOR(temp);
- if (XHCI_EXT_PORT_MAJOR(temp) == 0x03) {
+ if (major_revision == 0x03) {
rhub = &xhci->usb3_rhub;
- } else if (XHCI_EXT_PORT_MAJOR(temp) <= 0x02) {
+ } else if (major_revision <= 0x02) {
rhub = &xhci->usb2_rhub;
} else {
xhci_warn(xhci, "Ignoring unknown port speed, "
@@ -2190,19 +2190,12 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
*/
static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
{
- __le32 __iomem *addr, *tmp_addr;
- u32 offset, tmp_offset;
+ void __iomem *base;
+ u32 offset;
unsigned int num_ports;
int i, j, port_index;
int cap_count = 0;
-
- addr = &xhci->cap_regs->hcc_params;
- offset = XHCI_HCC_EXT_CAPS(readl(addr));
- if (offset == 0) {
- xhci_err(xhci, "No Extended Capability registers, "
- "unable to set up roothub.\n");
- return -ENODEV;
- }
+ u32 cap_start;
num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
xhci->port_array = kzalloc(sizeof(*xhci->port_array)*num_ports, flags);
@@ -2220,48 +2213,34 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
for (j = 0; j < XHCI_MAX_INTERVAL; j++)
INIT_LIST_HEAD(&bw_table->interval_bw[j].endpoints);
}
+ base = &xhci->cap_regs->hc_capbase;
- /*
- * For whatever reason, the first capability offset is from the
- * capability register base, not from the HCCPARAMS register.
- * See section 5.3.6 for offset calculation.
- */
- addr = &xhci->cap_regs->hc_capbase + offset;
-
- tmp_addr = addr;
- tmp_offset = offset;
+ cap_start = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_PROTOCOL);
+ if (!cap_start) {
+ xhci_err(xhci, "No Extended Capability registers, unable to set up roothub\n");
+ return -ENODEV;
+ }
+ offset = cap_start;
/* count extended protocol capability entries for later caching */
- do {
- u32 cap_id;
- cap_id = readl(tmp_addr);
- if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL)
- cap_count++;
- tmp_offset = XHCI_EXT_CAPS_NEXT(cap_id);
- tmp_addr += tmp_offset;
- } while (tmp_offset);
+ while (offset) {
+ cap_count++;
+ offset = xhci_find_next_ext_cap(base, offset,
+ XHCI_EXT_CAPS_PROTOCOL);
+ }
xhci->ext_caps = kzalloc(sizeof(*xhci->ext_caps) * cap_count, flags);
if (!xhci->ext_caps)
return -ENOMEM;
- while (1) {
- u32 cap_id;
-
- cap_id = readl(addr);
- if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL)
- xhci_add_in_port(xhci, num_ports, addr,
- (u8) XHCI_EXT_PORT_MAJOR(cap_id),
- cap_count);
- offset = XHCI_EXT_CAPS_NEXT(cap_id);
- if (!offset || (xhci->num_usb2_ports + xhci->num_usb3_ports)
- == num_ports)
+ offset = cap_start;
+
+ while (offset) {
+ xhci_add_in_port(xhci, num_ports, base + offset, cap_count);
+ if (xhci->num_usb2_ports + xhci->num_usb3_ports == num_ports)
break;
- /*
- * Once you're into the Extended Capabilities, the offset is
- * always relative to the register holding the offset.
- */
- addr += offset;
+ offset = xhci_find_next_ext_cap(base, offset,
+ XHCI_EXT_CAPS_PROTOCOL);
}
if (xhci->num_usb2_ports == 0 && xhci->num_usb3_ports == 0) {
diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c
new file mode 100644
index 000000000000..c30de7c39f44
--- /dev/null
+++ b/drivers/usb/host/xhci-mtk-sch.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author:
+ * Zhigang.Wei <zhigang.wei@mediatek.com>
+ * Chunfeng.Yun <chunfeng.yun@mediatek.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/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "xhci.h"
+#include "xhci-mtk.h"
+
+#define SS_BW_BOUNDARY 51000
+/* table 5-5. High-speed Isoc Transaction Limits in usb_20 spec */
+#define HS_BW_BOUNDARY 6144
+/* usb2 spec section11.18.1: at most 188 FS bytes per microframe */
+#define FS_PAYLOAD_MAX 188
+
+/* mtk scheduler bitmasks */
+#define EP_BPKTS(p) ((p) & 0x3f)
+#define EP_BCSCOUNT(p) (((p) & 0x7) << 8)
+#define EP_BBM(p) ((p) << 11)
+#define EP_BOFFSET(p) ((p) & 0x3fff)
+#define EP_BREPEAT(p) (((p) & 0x7fff) << 16)
+
+static int is_fs_or_ls(enum usb_device_speed speed)
+{
+ return speed == USB_SPEED_FULL || speed == USB_SPEED_LOW;
+}
+
+/*
+* get the index of bandwidth domains array which @ep belongs to.
+*
+* the bandwidth domain array is saved to @sch_array of struct xhci_hcd_mtk,
+* each HS root port is treated as a single bandwidth domain,
+* but each SS root port is treated as two bandwidth domains, one for IN eps,
+* one for OUT eps.
+* @real_port value is defined as follow according to xHCI spec:
+* 1 for SSport0, ..., N+1 for SSportN, N+2 for HSport0, N+3 for HSport1, etc
+* so the bandwidth domain array is organized as follow for simplification:
+* SSport0-OUT, SSport0-IN, ..., SSportX-OUT, SSportX-IN, HSport0, ..., HSportY
+*/
+static int get_bw_index(struct xhci_hcd *xhci, struct usb_device *udev,
+ struct usb_host_endpoint *ep)
+{
+ struct xhci_virt_device *virt_dev;
+ int bw_index;
+
+ virt_dev = xhci->devs[udev->slot_id];
+
+ if (udev->speed == USB_SPEED_SUPER) {
+ if (usb_endpoint_dir_out(&ep->desc))
+ bw_index = (virt_dev->real_port - 1) * 2;
+ else
+ bw_index = (virt_dev->real_port - 1) * 2 + 1;
+ } else {
+ /* add one more for each SS port */
+ bw_index = virt_dev->real_port + xhci->num_usb3_ports - 1;
+ }
+
+ return bw_index;
+}
+
+static void setup_sch_info(struct usb_device *udev,
+ struct xhci_ep_ctx *ep_ctx, struct mu3h_sch_ep_info *sch_ep)
+{
+ u32 ep_type;
+ u32 ep_interval;
+ u32 max_packet_size;
+ u32 max_burst;
+ u32 mult;
+ u32 esit_pkts;
+
+ ep_type = CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2));
+ ep_interval = CTX_TO_EP_INTERVAL(le32_to_cpu(ep_ctx->ep_info));
+ max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2));
+ max_burst = CTX_TO_MAX_BURST(le32_to_cpu(ep_ctx->ep_info2));
+ mult = CTX_TO_EP_MULT(le32_to_cpu(ep_ctx->ep_info));
+
+ sch_ep->esit = 1 << ep_interval;
+ sch_ep->offset = 0;
+ sch_ep->burst_mode = 0;
+
+ if (udev->speed == USB_SPEED_HIGH) {
+ sch_ep->cs_count = 0;
+
+ /*
+ * usb_20 spec section5.9
+ * a single microframe is enough for HS synchromous endpoints
+ * in a interval
+ */
+ sch_ep->num_budget_microframes = 1;
+ sch_ep->repeat = 0;
+
+ /*
+ * xHCI spec section6.2.3.4
+ * @max_burst is the number of additional transactions
+ * opportunities per microframe
+ */
+ sch_ep->pkts = max_burst + 1;
+ sch_ep->bw_cost_per_microframe = max_packet_size * sch_ep->pkts;
+ } else if (udev->speed == USB_SPEED_SUPER) {
+ /* usb3_r1 spec section4.4.7 & 4.4.8 */
+ sch_ep->cs_count = 0;
+ esit_pkts = (mult + 1) * (max_burst + 1);
+ if (ep_type == INT_IN_EP || ep_type == INT_OUT_EP) {
+ sch_ep->pkts = esit_pkts;
+ sch_ep->num_budget_microframes = 1;
+ sch_ep->repeat = 0;
+ }
+
+ if (ep_type == ISOC_IN_EP || ep_type == ISOC_OUT_EP) {
+ if (esit_pkts <= sch_ep->esit)
+ sch_ep->pkts = 1;
+ else
+ sch_ep->pkts = roundup_pow_of_two(esit_pkts)
+ / sch_ep->esit;
+
+ sch_ep->num_budget_microframes =
+ DIV_ROUND_UP(esit_pkts, sch_ep->pkts);
+
+ if (sch_ep->num_budget_microframes > 1)
+ sch_ep->repeat = 1;
+ else
+ sch_ep->repeat = 0;
+ }
+ sch_ep->bw_cost_per_microframe = max_packet_size * sch_ep->pkts;
+ } else if (is_fs_or_ls(udev->speed)) {
+
+ /*
+ * usb_20 spec section11.18.4
+ * assume worst cases
+ */
+ sch_ep->repeat = 0;
+ sch_ep->pkts = 1; /* at most one packet for each microframe */
+ if (ep_type == INT_IN_EP || ep_type == INT_OUT_EP) {
+ sch_ep->cs_count = 3; /* at most need 3 CS*/
+ /* one for SS and one for budgeted transaction */
+ sch_ep->num_budget_microframes = sch_ep->cs_count + 2;
+ sch_ep->bw_cost_per_microframe = max_packet_size;
+ }
+ if (ep_type == ISOC_OUT_EP) {
+
+ /*
+ * the best case FS budget assumes that 188 FS bytes
+ * occur in each microframe
+ */
+ sch_ep->num_budget_microframes = DIV_ROUND_UP(
+ max_packet_size, FS_PAYLOAD_MAX);
+ sch_ep->bw_cost_per_microframe = FS_PAYLOAD_MAX;
+ sch_ep->cs_count = sch_ep->num_budget_microframes;
+ }
+ if (ep_type == ISOC_IN_EP) {
+ /* at most need additional two CS. */
+ sch_ep->cs_count = DIV_ROUND_UP(
+ max_packet_size, FS_PAYLOAD_MAX) + 2;
+ sch_ep->num_budget_microframes = sch_ep->cs_count + 2;
+ sch_ep->bw_cost_per_microframe = FS_PAYLOAD_MAX;
+ }
+ }
+}
+
+/* Get maximum bandwidth when we schedule at offset slot. */
+static u32 get_max_bw(struct mu3h_sch_bw_info *sch_bw,
+ struct mu3h_sch_ep_info *sch_ep, u32 offset)
+{
+ u32 num_esit;
+ u32 max_bw = 0;
+ int i;
+ int j;
+
+ num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit;
+ for (i = 0; i < num_esit; i++) {
+ u32 base = offset + i * sch_ep->esit;
+
+ for (j = 0; j < sch_ep->num_budget_microframes; j++) {
+ if (sch_bw->bus_bw[base + j] > max_bw)
+ max_bw = sch_bw->bus_bw[base + j];
+ }
+ }
+ return max_bw;
+}
+
+static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw,
+ struct mu3h_sch_ep_info *sch_ep, int bw_cost)
+{
+ u32 num_esit;
+ u32 base;
+ int i;
+ int j;
+
+ num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit;
+ for (i = 0; i < num_esit; i++) {
+ base = sch_ep->offset + i * sch_ep->esit;
+ for (j = 0; j < sch_ep->num_budget_microframes; j++)
+ sch_bw->bus_bw[base + j] += bw_cost;
+ }
+}
+
+static int check_sch_bw(struct usb_device *udev,
+ struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep)
+{
+ u32 offset;
+ u32 esit;
+ u32 num_budget_microframes;
+ u32 min_bw;
+ u32 min_index;
+ u32 worst_bw;
+ u32 bw_boundary;
+
+ if (sch_ep->esit > XHCI_MTK_MAX_ESIT)
+ sch_ep->esit = XHCI_MTK_MAX_ESIT;
+
+ esit = sch_ep->esit;
+ num_budget_microframes = sch_ep->num_budget_microframes;
+
+ /*
+ * Search through all possible schedule microframes.
+ * and find a microframe where its worst bandwidth is minimum.
+ */
+ min_bw = ~0;
+ min_index = 0;
+ for (offset = 0; offset < esit; offset++) {
+ if ((offset + num_budget_microframes) > sch_ep->esit)
+ break;
+
+ /*
+ * usb_20 spec section11.18:
+ * must never schedule Start-Split in Y6
+ */
+ if (is_fs_or_ls(udev->speed) && (offset % 8 == 6))
+ continue;
+
+ worst_bw = get_max_bw(sch_bw, sch_ep, offset);
+ if (min_bw > worst_bw) {
+ min_bw = worst_bw;
+ min_index = offset;
+ }
+ if (min_bw == 0)
+ break;
+ }
+ sch_ep->offset = min_index;
+
+ bw_boundary = (udev->speed == USB_SPEED_SUPER)
+ ? SS_BW_BOUNDARY : HS_BW_BOUNDARY;
+
+ /* check bandwidth */
+ if (min_bw + sch_ep->bw_cost_per_microframe > bw_boundary)
+ return -ERANGE;
+
+ /* update bus bandwidth info */
+ update_bus_bw(sch_bw, sch_ep, sch_ep->bw_cost_per_microframe);
+
+ return 0;
+}
+
+static bool need_bw_sch(struct usb_host_endpoint *ep,
+ enum usb_device_speed speed, int has_tt)
+{
+ /* only for periodic endpoints */
+ if (usb_endpoint_xfer_control(&ep->desc)
+ || usb_endpoint_xfer_bulk(&ep->desc))
+ return false;
+
+ /*
+ * for LS & FS periodic endpoints which its device don't attach
+ * to TT are also ignored, root-hub will schedule them directly
+ */
+ if (is_fs_or_ls(speed) && !has_tt)
+ return false;
+
+ return true;
+}
+
+int xhci_mtk_sch_init(struct xhci_hcd_mtk *mtk)
+{
+ struct mu3h_sch_bw_info *sch_array;
+ int num_usb_bus;
+ int i;
+
+ /* ss IN and OUT are separated */
+ num_usb_bus = mtk->num_u3_ports * 2 + mtk->num_u2_ports;
+
+ sch_array = kcalloc(num_usb_bus, sizeof(*sch_array), GFP_KERNEL);
+ if (sch_array == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < num_usb_bus; i++)
+ INIT_LIST_HEAD(&sch_array[i].bw_ep_list);
+
+ mtk->sch_array = sch_array;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(xhci_mtk_sch_init);
+
+void xhci_mtk_sch_exit(struct xhci_hcd_mtk *mtk)
+{
+ kfree(mtk->sch_array);
+}
+EXPORT_SYMBOL_GPL(xhci_mtk_sch_exit);
+
+int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
+ struct usb_host_endpoint *ep)
+{
+ struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
+ struct xhci_hcd *xhci;
+ struct xhci_ep_ctx *ep_ctx;
+ struct xhci_slot_ctx *slot_ctx;
+ struct xhci_virt_device *virt_dev;
+ struct mu3h_sch_bw_info *sch_bw;
+ struct mu3h_sch_ep_info *sch_ep;
+ struct mu3h_sch_bw_info *sch_array;
+ unsigned int ep_index;
+ int bw_index;
+ int ret = 0;
+
+ xhci = hcd_to_xhci(hcd);
+ virt_dev = xhci->devs[udev->slot_id];
+ ep_index = xhci_get_endpoint_index(&ep->desc);
+ slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
+ ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index);
+ sch_array = mtk->sch_array;
+
+ xhci_dbg(xhci, "%s() type:%d, speed:%d, mpkt:%d, dir:%d, ep:%p\n",
+ __func__, usb_endpoint_type(&ep->desc), udev->speed,
+ GET_MAX_PACKET(usb_endpoint_maxp(&ep->desc)),
+ usb_endpoint_dir_in(&ep->desc), ep);
+
+ if (!need_bw_sch(ep, udev->speed, slot_ctx->tt_info & TT_SLOT))
+ return 0;
+
+ bw_index = get_bw_index(xhci, udev, ep);
+ sch_bw = &sch_array[bw_index];
+
+ sch_ep = kzalloc(sizeof(struct mu3h_sch_ep_info), GFP_NOIO);
+ if (!sch_ep)
+ return -ENOMEM;
+
+ setup_sch_info(udev, ep_ctx, sch_ep);
+
+ ret = check_sch_bw(udev, sch_bw, sch_ep);
+ if (ret) {
+ xhci_err(xhci, "Not enough bandwidth!\n");
+ kfree(sch_ep);
+ return -ENOSPC;
+ }
+
+ list_add_tail(&sch_ep->endpoint, &sch_bw->bw_ep_list);
+ sch_ep->ep = ep;
+
+ ep_ctx->reserved[0] |= cpu_to_le32(EP_BPKTS(sch_ep->pkts)
+ | EP_BCSCOUNT(sch_ep->cs_count) | EP_BBM(sch_ep->burst_mode));
+ ep_ctx->reserved[1] |= cpu_to_le32(EP_BOFFSET(sch_ep->offset)
+ | EP_BREPEAT(sch_ep->repeat));
+
+ xhci_dbg(xhci, " PKTS:%x, CSCOUNT:%x, BM:%x, OFFSET:%x, REPEAT:%x\n",
+ sch_ep->pkts, sch_ep->cs_count, sch_ep->burst_mode,
+ sch_ep->offset, sch_ep->repeat);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(xhci_mtk_add_ep_quirk);
+
+void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
+ struct usb_host_endpoint *ep)
+{
+ struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
+ struct xhci_hcd *xhci;
+ struct xhci_slot_ctx *slot_ctx;
+ struct xhci_virt_device *virt_dev;
+ struct mu3h_sch_bw_info *sch_array;
+ struct mu3h_sch_bw_info *sch_bw;
+ struct mu3h_sch_ep_info *sch_ep;
+ int bw_index;
+
+ xhci = hcd_to_xhci(hcd);
+ virt_dev = xhci->devs[udev->slot_id];
+ slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
+ sch_array = mtk->sch_array;
+
+ xhci_dbg(xhci, "%s() type:%d, speed:%d, mpks:%d, dir:%d, ep:%p\n",
+ __func__, usb_endpoint_type(&ep->desc), udev->speed,
+ GET_MAX_PACKET(usb_endpoint_maxp(&ep->desc)),
+ usb_endpoint_dir_in(&ep->desc), ep);
+
+ if (!need_bw_sch(ep, udev->speed, slot_ctx->tt_info & TT_SLOT))
+ return;
+
+ bw_index = get_bw_index(xhci, udev, ep);
+ sch_bw = &sch_array[bw_index];
+
+ list_for_each_entry(sch_ep, &sch_bw->bw_ep_list, endpoint) {
+ if (sch_ep->ep == ep) {
+ update_bus_bw(sch_bw, sch_ep,
+ -sch_ep->bw_cost_per_microframe);
+ list_del(&sch_ep->endpoint);
+ kfree(sch_ep);
+ break;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(xhci_mtk_drop_ep_quirk);
diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
new file mode 100644
index 000000000000..c9ab6a44c34a
--- /dev/null
+++ b/drivers/usb/host/xhci-mtk.c
@@ -0,0 +1,763 @@
+/*
+ * MediaTek xHCI Host Controller Driver
+ *
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author:
+ * Chunfeng Yun <chunfeng.yun@mediatek.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include "xhci.h"
+#include "xhci-mtk.h"
+
+/* ip_pw_ctrl0 register */
+#define CTRL0_IP_SW_RST BIT(0)
+
+/* ip_pw_ctrl1 register */
+#define CTRL1_IP_HOST_PDN BIT(0)
+
+/* ip_pw_ctrl2 register */
+#define CTRL2_IP_DEV_PDN BIT(0)
+
+/* ip_pw_sts1 register */
+#define STS1_IP_SLEEP_STS BIT(30)
+#define STS1_XHCI_RST BIT(11)
+#define STS1_SYS125_RST BIT(10)
+#define STS1_REF_RST BIT(8)
+#define STS1_SYSPLL_STABLE BIT(0)
+
+/* ip_xhci_cap register */
+#define CAP_U3_PORT_NUM(p) ((p) & 0xff)
+#define CAP_U2_PORT_NUM(p) (((p) >> 8) & 0xff)
+
+/* u3_ctrl_p register */
+#define CTRL_U3_PORT_HOST_SEL BIT(2)
+#define CTRL_U3_PORT_PDN BIT(1)
+#define CTRL_U3_PORT_DIS BIT(0)
+
+/* u2_ctrl_p register */
+#define CTRL_U2_PORT_HOST_SEL BIT(2)
+#define CTRL_U2_PORT_PDN BIT(1)
+#define CTRL_U2_PORT_DIS BIT(0)
+
+/* u2_phy_pll register */
+#define CTRL_U2_FORCE_PLL_STB BIT(28)
+
+#define PERI_WK_CTRL0 0x400
+#define UWK_CTR0_0P_LS_PE BIT(8) /* posedge */
+#define UWK_CTR0_0P_LS_NE BIT(7) /* negedge for 0p linestate*/
+#define UWK_CTL1_1P_LS_C(x) (((x) & 0xf) << 1)
+#define UWK_CTL1_1P_LS_E BIT(0)
+
+#define PERI_WK_CTRL1 0x404
+#define UWK_CTL1_IS_C(x) (((x) & 0xf) << 26)
+#define UWK_CTL1_IS_E BIT(25)
+#define UWK_CTL1_0P_LS_C(x) (((x) & 0xf) << 21)
+#define UWK_CTL1_0P_LS_E BIT(20)
+#define UWK_CTL1_IDDIG_C(x) (((x) & 0xf) << 11) /* cycle debounce */
+#define UWK_CTL1_IDDIG_E BIT(10) /* enable debounce */
+#define UWK_CTL1_IDDIG_P BIT(9) /* polarity */
+#define UWK_CTL1_0P_LS_P BIT(7)
+#define UWK_CTL1_IS_P BIT(6) /* polarity for ip sleep */
+
+enum ssusb_wakeup_src {
+ SSUSB_WK_IP_SLEEP = 1,
+ SSUSB_WK_LINE_STATE = 2,
+};
+
+static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk)
+{
+ struct mu3c_ippc_regs __iomem *ippc = mtk->ippc_regs;
+ u32 value, check_val;
+ int ret;
+ int i;
+
+ /* power on host ip */
+ value = readl(&ippc->ip_pw_ctr1);
+ value &= ~CTRL1_IP_HOST_PDN;
+ writel(value, &ippc->ip_pw_ctr1);
+
+ /* power on and enable all u3 ports */
+ for (i = 0; i < mtk->num_u3_ports; i++) {
+ value = readl(&ippc->u3_ctrl_p[i]);
+ value &= ~(CTRL_U3_PORT_PDN | CTRL_U3_PORT_DIS);
+ value |= CTRL_U3_PORT_HOST_SEL;
+ writel(value, &ippc->u3_ctrl_p[i]);
+ }
+
+ /* power on and enable all u2 ports */
+ for (i = 0; i < mtk->num_u2_ports; i++) {
+ value = readl(&ippc->u2_ctrl_p[i]);
+ value &= ~(CTRL_U2_PORT_PDN | CTRL_U2_PORT_DIS);
+ value |= CTRL_U2_PORT_HOST_SEL;
+ writel(value, &ippc->u2_ctrl_p[i]);
+ }
+
+ /*
+ * wait for clocks to be stable, and clock domains reset to
+ * be inactive after power on and enable ports
+ */
+ check_val = STS1_SYSPLL_STABLE | STS1_REF_RST |
+ STS1_SYS125_RST | STS1_XHCI_RST;
+
+ ret = readl_poll_timeout(&ippc->ip_pw_sts1, value,
+ (check_val == (value & check_val)), 100, 20000);
+ if (ret) {
+ dev_err(mtk->dev, "clocks are not stable (0x%x)\n", value);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int xhci_mtk_host_disable(struct xhci_hcd_mtk *mtk)
+{
+ struct mu3c_ippc_regs __iomem *ippc = mtk->ippc_regs;
+ u32 value;
+ int ret;
+ int i;
+
+ /* power down all u3 ports */
+ for (i = 0; i < mtk->num_u3_ports; i++) {
+ value = readl(&ippc->u3_ctrl_p[i]);
+ value |= CTRL_U3_PORT_PDN;
+ writel(value, &ippc->u3_ctrl_p[i]);
+ }
+
+ /* power down all u2 ports */
+ for (i = 0; i < mtk->num_u2_ports; i++) {
+ value = readl(&ippc->u2_ctrl_p[i]);
+ value |= CTRL_U2_PORT_PDN;
+ writel(value, &ippc->u2_ctrl_p[i]);
+ }
+
+ /* power down host ip */
+ value = readl(&ippc->ip_pw_ctr1);
+ value |= CTRL1_IP_HOST_PDN;
+ writel(value, &ippc->ip_pw_ctr1);
+
+ /* wait for host ip to sleep */
+ ret = readl_poll_timeout(&ippc->ip_pw_sts1, value,
+ (value & STS1_IP_SLEEP_STS), 100, 100000);
+ if (ret) {
+ dev_err(mtk->dev, "ip sleep failed!!!\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int xhci_mtk_ssusb_config(struct xhci_hcd_mtk *mtk)
+{
+ struct mu3c_ippc_regs __iomem *ippc = mtk->ippc_regs;
+ u32 value;
+
+ /* reset whole ip */
+ value = readl(&ippc->ip_pw_ctr0);
+ value |= CTRL0_IP_SW_RST;
+ writel(value, &ippc->ip_pw_ctr0);
+ udelay(1);
+ value = readl(&ippc->ip_pw_ctr0);
+ value &= ~CTRL0_IP_SW_RST;
+ writel(value, &ippc->ip_pw_ctr0);
+
+ /*
+ * device ip is default power-on in fact
+ * power down device ip, otherwise ip-sleep will fail
+ */
+ value = readl(&ippc->ip_pw_ctr2);
+ value |= CTRL2_IP_DEV_PDN;
+ writel(value, &ippc->ip_pw_ctr2);
+
+ value = readl(&ippc->ip_xhci_cap);
+ mtk->num_u3_ports = CAP_U3_PORT_NUM(value);
+ mtk->num_u2_ports = CAP_U2_PORT_NUM(value);
+ dev_dbg(mtk->dev, "%s u2p:%d, u3p:%d\n", __func__,
+ mtk->num_u2_ports, mtk->num_u3_ports);
+
+ return xhci_mtk_host_enable(mtk);
+}
+
+static int xhci_mtk_clks_enable(struct xhci_hcd_mtk *mtk)
+{
+ int ret;
+
+ ret = clk_prepare_enable(mtk->sys_clk);
+ if (ret) {
+ dev_err(mtk->dev, "failed to enable sys_clk\n");
+ goto sys_clk_err;
+ }
+
+ if (mtk->wakeup_src) {
+ ret = clk_prepare_enable(mtk->wk_deb_p0);
+ if (ret) {
+ dev_err(mtk->dev, "failed to enable wk_deb_p0\n");
+ goto usb_p0_err;
+ }
+
+ ret = clk_prepare_enable(mtk->wk_deb_p1);
+ if (ret) {
+ dev_err(mtk->dev, "failed to enable wk_deb_p1\n");
+ goto usb_p1_err;
+ }
+ }
+ return 0;
+
+usb_p1_err:
+ clk_disable_unprepare(mtk->wk_deb_p0);
+usb_p0_err:
+ clk_disable_unprepare(mtk->sys_clk);
+sys_clk_err:
+ return -EINVAL;
+}
+
+static void xhci_mtk_clks_disable(struct xhci_hcd_mtk *mtk)
+{
+ if (mtk->wakeup_src) {
+ clk_disable_unprepare(mtk->wk_deb_p1);
+ clk_disable_unprepare(mtk->wk_deb_p0);
+ }
+ clk_disable_unprepare(mtk->sys_clk);
+}
+
+/* only clocks can be turn off for ip-sleep wakeup mode */
+static void usb_wakeup_ip_sleep_en(struct xhci_hcd_mtk *mtk)
+{
+ u32 tmp;
+ struct regmap *pericfg = mtk->pericfg;
+
+ regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
+ tmp &= ~UWK_CTL1_IS_P;
+ tmp &= ~(UWK_CTL1_IS_C(0xf));
+ tmp |= UWK_CTL1_IS_C(0x8);
+ regmap_write(pericfg, PERI_WK_CTRL1, tmp);
+ regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_IS_E);
+
+ regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
+ dev_dbg(mtk->dev, "%s(): WK_CTRL1[P6,E25,C26:29]=%#x\n",
+ __func__, tmp);
+}
+
+static void usb_wakeup_ip_sleep_dis(struct xhci_hcd_mtk *mtk)
+{
+ u32 tmp;
+
+ regmap_read(mtk->pericfg, PERI_WK_CTRL1, &tmp);
+ tmp &= ~UWK_CTL1_IS_E;
+ regmap_write(mtk->pericfg, PERI_WK_CTRL1, tmp);
+}
+
+/*
+* for line-state wakeup mode, phy's power should not power-down
+* and only support cable plug in/out
+*/
+static void usb_wakeup_line_state_en(struct xhci_hcd_mtk *mtk)
+{
+ u32 tmp;
+ struct regmap *pericfg = mtk->pericfg;
+
+ /* line-state of u2-port0 */
+ regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
+ tmp &= ~UWK_CTL1_0P_LS_P;
+ tmp &= ~(UWK_CTL1_0P_LS_C(0xf));
+ tmp |= UWK_CTL1_0P_LS_C(0x8);
+ regmap_write(pericfg, PERI_WK_CTRL1, tmp);
+ regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
+ regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_0P_LS_E);
+
+ /* line-state of u2-port1 */
+ regmap_read(pericfg, PERI_WK_CTRL0, &tmp);
+ tmp &= ~(UWK_CTL1_1P_LS_C(0xf));
+ tmp |= UWK_CTL1_1P_LS_C(0x8);
+ regmap_write(pericfg, PERI_WK_CTRL0, tmp);
+ regmap_write(pericfg, PERI_WK_CTRL0, tmp | UWK_CTL1_1P_LS_E);
+}
+
+static void usb_wakeup_line_state_dis(struct xhci_hcd_mtk *mtk)
+{
+ u32 tmp;
+ struct regmap *pericfg = mtk->pericfg;
+
+ /* line-state of u2-port0 */
+ regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
+ tmp &= ~UWK_CTL1_0P_LS_E;
+ regmap_write(pericfg, PERI_WK_CTRL1, tmp);
+
+ /* line-state of u2-port1 */
+ regmap_read(pericfg, PERI_WK_CTRL0, &tmp);
+ tmp &= ~UWK_CTL1_1P_LS_E;
+ regmap_write(pericfg, PERI_WK_CTRL0, tmp);
+}
+
+static void usb_wakeup_enable(struct xhci_hcd_mtk *mtk)
+{
+ if (mtk->wakeup_src == SSUSB_WK_IP_SLEEP)
+ usb_wakeup_ip_sleep_en(mtk);
+ else if (mtk->wakeup_src == SSUSB_WK_LINE_STATE)
+ usb_wakeup_line_state_en(mtk);
+}
+
+static void usb_wakeup_disable(struct xhci_hcd_mtk *mtk)
+{
+ if (mtk->wakeup_src == SSUSB_WK_IP_SLEEP)
+ usb_wakeup_ip_sleep_dis(mtk);
+ else if (mtk->wakeup_src == SSUSB_WK_LINE_STATE)
+ usb_wakeup_line_state_dis(mtk);
+}
+
+static int usb_wakeup_of_property_parse(struct xhci_hcd_mtk *mtk,
+ struct device_node *dn)
+{
+ struct device *dev = mtk->dev;
+
+ /*
+ * wakeup function is optional, so it is not an error if this property
+ * does not exist, and in such case, no need to get relative
+ * properties anymore.
+ */
+ of_property_read_u32(dn, "mediatek,wakeup-src", &mtk->wakeup_src);
+ if (!mtk->wakeup_src)
+ return 0;
+
+ mtk->wk_deb_p0 = devm_clk_get(dev, "wakeup_deb_p0");
+ if (IS_ERR(mtk->wk_deb_p0)) {
+ dev_err(dev, "fail to get wakeup_deb_p0\n");
+ return PTR_ERR(mtk->wk_deb_p0);
+ }
+
+ mtk->wk_deb_p1 = devm_clk_get(dev, "wakeup_deb_p1");
+ if (IS_ERR(mtk->wk_deb_p1)) {
+ dev_err(dev, "fail to get wakeup_deb_p1\n");
+ return PTR_ERR(mtk->wk_deb_p1);
+ }
+
+ mtk->pericfg = syscon_regmap_lookup_by_phandle(dn,
+ "mediatek,syscon-wakeup");
+ if (IS_ERR(mtk->pericfg)) {
+ dev_err(dev, "fail to get pericfg regs\n");
+ return PTR_ERR(mtk->pericfg);
+ }
+
+ return 0;
+}
+
+static int xhci_mtk_setup(struct usb_hcd *hcd);
+static const struct xhci_driver_overrides xhci_mtk_overrides __initconst = {
+ .extra_priv_size = sizeof(struct xhci_hcd),
+ .reset = xhci_mtk_setup,
+};
+
+static struct hc_driver __read_mostly xhci_mtk_hc_driver;
+
+static int xhci_mtk_phy_init(struct xhci_hcd_mtk *mtk)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < mtk->num_phys; i++) {
+ ret = phy_init(mtk->phys[i]);
+ if (ret)
+ goto exit_phy;
+ }
+ return 0;
+
+exit_phy:
+ for (; i > 0; i--)
+ phy_exit(mtk->phys[i - 1]);
+
+ return ret;
+}
+
+static int xhci_mtk_phy_exit(struct xhci_hcd_mtk *mtk)
+{
+ int i;
+
+ for (i = 0; i < mtk->num_phys; i++)
+ phy_exit(mtk->phys[i]);
+
+ return 0;
+}
+
+static int xhci_mtk_phy_power_on(struct xhci_hcd_mtk *mtk)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < mtk->num_phys; i++) {
+ ret = phy_power_on(mtk->phys[i]);
+ if (ret)
+ goto power_off_phy;
+ }
+ return 0;
+
+power_off_phy:
+ for (; i > 0; i--)
+ phy_power_off(mtk->phys[i - 1]);
+
+ return ret;
+}
+
+static void xhci_mtk_phy_power_off(struct xhci_hcd_mtk *mtk)
+{
+ unsigned int i;
+
+ for (i = 0; i < mtk->num_phys; i++)
+ phy_power_off(mtk->phys[i]);
+}
+
+static int xhci_mtk_ldos_enable(struct xhci_hcd_mtk *mtk)
+{
+ int ret;
+
+ ret = regulator_enable(mtk->vbus);
+ if (ret) {
+ dev_err(mtk->dev, "failed to enable vbus\n");
+ return ret;
+ }
+
+ ret = regulator_enable(mtk->vusb33);
+ if (ret) {
+ dev_err(mtk->dev, "failed to enable vusb33\n");
+ regulator_disable(mtk->vbus);
+ return ret;
+ }
+ return 0;
+}
+
+static void xhci_mtk_ldos_disable(struct xhci_hcd_mtk *mtk)
+{
+ regulator_disable(mtk->vbus);
+ regulator_disable(mtk->vusb33);
+}
+
+static void xhci_mtk_quirks(struct device *dev, struct xhci_hcd *xhci)
+{
+ struct usb_hcd *hcd = xhci_to_hcd(xhci);
+ struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
+
+ /*
+ * As of now platform drivers don't provide MSI support so we ensure
+ * here that the generic code does not try to make a pci_dev from our
+ * dev struct in order to setup MSI
+ */
+ xhci->quirks |= XHCI_PLAT;
+ xhci->quirks |= XHCI_MTK_HOST;
+ /*
+ * MTK host controller gives a spurious successful event after a
+ * short transfer. Ignore it.
+ */
+ xhci->quirks |= XHCI_SPURIOUS_SUCCESS;
+ if (mtk->lpm_support)
+ xhci->quirks |= XHCI_LPM_SUPPORT;
+}
+
+/* called during probe() after chip reset completes */
+static int xhci_mtk_setup(struct usb_hcd *hcd)
+{
+ struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
+ int ret;
+
+ if (usb_hcd_is_primary_hcd(hcd)) {
+ ret = xhci_mtk_ssusb_config(mtk);
+ if (ret)
+ return ret;
+ ret = xhci_mtk_sch_init(mtk);
+ if (ret)
+ return ret;
+ }
+
+ return xhci_gen_setup(hcd, xhci_mtk_quirks);
+}
+
+static int xhci_mtk_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct xhci_hcd_mtk *mtk;
+ const struct hc_driver *driver;
+ struct xhci_hcd *xhci;
+ struct resource *res;
+ struct usb_hcd *hcd;
+ struct phy *phy;
+ int phy_num;
+ int ret = -ENODEV;
+ int irq;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ driver = &xhci_mtk_hc_driver;
+ mtk = devm_kzalloc(dev, sizeof(*mtk), GFP_KERNEL);
+ if (!mtk)
+ return -ENOMEM;
+
+ mtk->dev = dev;
+ mtk->vbus = devm_regulator_get(dev, "vbus");
+ if (IS_ERR(mtk->vbus)) {
+ dev_err(dev, "fail to get vbus\n");
+ return PTR_ERR(mtk->vbus);
+ }
+
+ mtk->vusb33 = devm_regulator_get(dev, "vusb33");
+ if (IS_ERR(mtk->vusb33)) {
+ dev_err(dev, "fail to get vusb33\n");
+ return PTR_ERR(mtk->vusb33);
+ }
+
+ mtk->sys_clk = devm_clk_get(dev, "sys_ck");
+ if (IS_ERR(mtk->sys_clk)) {
+ dev_err(dev, "fail to get sys_ck\n");
+ return PTR_ERR(mtk->sys_clk);
+ }
+
+ mtk->lpm_support = of_property_read_bool(node, "usb3-lpm-capable");
+
+ ret = usb_wakeup_of_property_parse(mtk, node);
+ if (ret)
+ return ret;
+
+ mtk->num_phys = of_count_phandle_with_args(node,
+ "phys", "#phy-cells");
+ if (mtk->num_phys > 0) {
+ mtk->phys = devm_kcalloc(dev, mtk->num_phys,
+ sizeof(*mtk->phys), GFP_KERNEL);
+ if (!mtk->phys)
+ return -ENOMEM;
+ } else {
+ mtk->num_phys = 0;
+ }
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+ device_enable_async_suspend(dev);
+
+ ret = xhci_mtk_ldos_enable(mtk);
+ if (ret)
+ goto disable_pm;
+
+ ret = xhci_mtk_clks_enable(mtk);
+ if (ret)
+ goto disable_ldos;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ goto disable_clk;
+
+ /* Initialize dma_mask and coherent_dma_mask to 32-bits */
+ ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+ if (ret)
+ goto disable_clk;
+
+ if (!dev->dma_mask)
+ dev->dma_mask = &dev->coherent_dma_mask;
+ else
+ dma_set_mask(dev, DMA_BIT_MASK(32));
+
+ hcd = usb_create_hcd(driver, dev, dev_name(dev));
+ if (!hcd) {
+ ret = -ENOMEM;
+ goto disable_clk;
+ }
+
+ /*
+ * USB 2.0 roothub is stored in the platform_device.
+ * Swap it with mtk HCD.
+ */
+ mtk->hcd = platform_get_drvdata(pdev);
+ platform_set_drvdata(pdev, mtk);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(hcd->regs)) {
+ ret = PTR_ERR(hcd->regs);
+ goto put_usb2_hcd;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ mtk->ippc_regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(mtk->ippc_regs)) {
+ ret = PTR_ERR(mtk->ippc_regs);
+ goto put_usb2_hcd;
+ }
+
+ for (phy_num = 0; phy_num < mtk->num_phys; phy_num++) {
+ phy = devm_of_phy_get_by_index(dev, node, phy_num);
+ if (IS_ERR(phy)) {
+ ret = PTR_ERR(phy);
+ goto put_usb2_hcd;
+ }
+ mtk->phys[phy_num] = phy;
+ }
+
+ ret = xhci_mtk_phy_init(mtk);
+ if (ret)
+ goto put_usb2_hcd;
+
+ ret = xhci_mtk_phy_power_on(mtk);
+ if (ret)
+ goto exit_phys;
+
+ device_init_wakeup(dev, true);
+
+ xhci = hcd_to_xhci(hcd);
+ xhci->main_hcd = hcd;
+ xhci->shared_hcd = usb_create_shared_hcd(driver, dev,
+ dev_name(dev), hcd);
+ if (!xhci->shared_hcd) {
+ ret = -ENOMEM;
+ goto power_off_phys;
+ }
+
+ if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
+ xhci->shared_hcd->can_do_streams = 1;
+
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (ret)
+ goto put_usb3_hcd;
+
+ ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
+ if (ret)
+ goto dealloc_usb2_hcd;
+
+ return 0;
+
+dealloc_usb2_hcd:
+ usb_remove_hcd(hcd);
+
+put_usb3_hcd:
+ xhci_mtk_sch_exit(mtk);
+ usb_put_hcd(xhci->shared_hcd);
+
+power_off_phys:
+ xhci_mtk_phy_power_off(mtk);
+ device_init_wakeup(dev, false);
+
+exit_phys:
+ xhci_mtk_phy_exit(mtk);
+
+put_usb2_hcd:
+ usb_put_hcd(hcd);
+
+disable_clk:
+ xhci_mtk_clks_disable(mtk);
+
+disable_ldos:
+ xhci_mtk_ldos_disable(mtk);
+
+disable_pm:
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+ return ret;
+}
+
+static int xhci_mtk_remove(struct platform_device *dev)
+{
+ struct xhci_hcd_mtk *mtk = platform_get_drvdata(dev);
+ struct usb_hcd *hcd = mtk->hcd;
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+ usb_remove_hcd(xhci->shared_hcd);
+ xhci_mtk_phy_power_off(mtk);
+ xhci_mtk_phy_exit(mtk);
+ device_init_wakeup(&dev->dev, false);
+
+ usb_remove_hcd(hcd);
+ usb_put_hcd(xhci->shared_hcd);
+ usb_put_hcd(hcd);
+ xhci_mtk_sch_exit(mtk);
+ xhci_mtk_clks_disable(mtk);
+ xhci_mtk_ldos_disable(mtk);
+ pm_runtime_put_sync(&dev->dev);
+ pm_runtime_disable(&dev->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int xhci_mtk_suspend(struct device *dev)
+{
+ struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev);
+
+ xhci_mtk_host_disable(mtk);
+ xhci_mtk_phy_power_off(mtk);
+ xhci_mtk_clks_disable(mtk);
+ usb_wakeup_enable(mtk);
+ return 0;
+}
+
+static int xhci_mtk_resume(struct device *dev)
+{
+ struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev);
+
+ usb_wakeup_disable(mtk);
+ xhci_mtk_clks_enable(mtk);
+ xhci_mtk_phy_power_on(mtk);
+ xhci_mtk_host_enable(mtk);
+ return 0;
+}
+
+static const struct dev_pm_ops xhci_mtk_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(xhci_mtk_suspend, xhci_mtk_resume)
+};
+#define DEV_PM_OPS (&xhci_mtk_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_OF
+static const struct of_device_id mtk_xhci_of_match[] = {
+ { .compatible = "mediatek,mt8173-xhci"},
+ { },
+};
+MODULE_DEVICE_TABLE(of, mtk_xhci_of_match);
+#endif
+
+static struct platform_driver mtk_xhci_driver = {
+ .probe = xhci_mtk_probe,
+ .remove = xhci_mtk_remove,
+ .driver = {
+ .name = "xhci-mtk",
+ .pm = DEV_PM_OPS,
+ .of_match_table = of_match_ptr(mtk_xhci_of_match),
+ },
+};
+MODULE_ALIAS("platform:xhci-mtk");
+
+static int __init xhci_mtk_init(void)
+{
+ xhci_init_driver(&xhci_mtk_hc_driver, &xhci_mtk_overrides);
+ return platform_driver_register(&mtk_xhci_driver);
+}
+module_init(xhci_mtk_init);
+
+static void __exit xhci_mtk_exit(void)
+{
+ platform_driver_unregister(&mtk_xhci_driver);
+}
+module_exit(xhci_mtk_exit);
+
+MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek xHCI Host Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h
new file mode 100644
index 000000000000..7da677c79ea8
--- /dev/null
+++ b/drivers/usb/host/xhci-mtk.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author:
+ * Zhigang.Wei <zhigang.wei@mediatek.com>
+ * Chunfeng.Yun <chunfeng.yun@mediatek.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.
+ *
+ */
+
+#ifndef _XHCI_MTK_H_
+#define _XHCI_MTK_H_
+
+#include "xhci.h"
+
+/**
+ * To simplify scheduler algorithm, set a upper limit for ESIT,
+ * if a synchromous ep's ESIT is larger than @XHCI_MTK_MAX_ESIT,
+ * round down to the limit value, that means allocating more
+ * bandwidth to it.
+ */
+#define XHCI_MTK_MAX_ESIT 64
+
+/**
+ * struct mu3h_sch_bw_info: schedule information for bandwidth domain
+ *
+ * @bus_bw: array to keep track of bandwidth already used at each uframes
+ * @bw_ep_list: eps in the bandwidth domain
+ *
+ * treat a HS root port as a bandwidth domain, but treat a SS root port as
+ * two bandwidth domains, one for IN eps and another for OUT eps.
+ */
+struct mu3h_sch_bw_info {
+ u32 bus_bw[XHCI_MTK_MAX_ESIT];
+ struct list_head bw_ep_list;
+};
+
+/**
+ * struct mu3h_sch_ep_info: schedule information for endpoint
+ *
+ * @esit: unit is 125us, equal to 2 << Interval field in ep-context
+ * @num_budget_microframes: number of continuous uframes
+ * (@repeat==1) scheduled within the interval
+ * @bw_cost_per_microframe: bandwidth cost per microframe
+ * @endpoint: linked into bandwidth domain which it belongs to
+ * @ep: address of usb_host_endpoint struct
+ * @offset: which uframe of the interval that transfer should be
+ * scheduled first time within the interval
+ * @repeat: the time gap between two uframes that transfers are
+ * scheduled within a interval. in the simple algorithm, only
+ * assign 0 or 1 to it; 0 means using only one uframe in a
+ * interval, and 1 means using @num_budget_microframes
+ * continuous uframes
+ * @pkts: number of packets to be transferred in the scheduled uframes
+ * @cs_count: number of CS that host will trigger
+ * @burst_mode: burst mode for scheduling. 0: normal burst mode,
+ * distribute the bMaxBurst+1 packets for a single burst
+ * according to @pkts and @repeat, repeate the burst multiple
+ * times; 1: distribute the (bMaxBurst+1)*(Mult+1) packets
+ * according to @pkts and @repeat. normal mode is used by
+ * default
+ */
+struct mu3h_sch_ep_info {
+ u32 esit;
+ u32 num_budget_microframes;
+ u32 bw_cost_per_microframe;
+ struct list_head endpoint;
+ void *ep;
+ /*
+ * mtk xHCI scheduling information put into reserved DWs
+ * in ep context
+ */
+ u32 offset;
+ u32 repeat;
+ u32 pkts;
+ u32 cs_count;
+ u32 burst_mode;
+};
+
+#define MU3C_U3_PORT_MAX 4
+#define MU3C_U2_PORT_MAX 5
+
+/**
+ * struct mu3c_ippc_regs: MTK ssusb ip port control registers
+ * @ip_pw_ctr0~3: ip power and clock control registers
+ * @ip_pw_sts1~2: ip power and clock status registers
+ * @ip_xhci_cap: ip xHCI capability register
+ * @u3_ctrl_p[x]: ip usb3 port x control register, only low 4bytes are used
+ * @u2_ctrl_p[x]: ip usb2 port x control register, only low 4bytes are used
+ * @u2_phy_pll: usb2 phy pll control register
+ */
+struct mu3c_ippc_regs {
+ __le32 ip_pw_ctr0;
+ __le32 ip_pw_ctr1;
+ __le32 ip_pw_ctr2;
+ __le32 ip_pw_ctr3;
+ __le32 ip_pw_sts1;
+ __le32 ip_pw_sts2;
+ __le32 reserved0[3];
+ __le32 ip_xhci_cap;
+ __le32 reserved1[2];
+ __le64 u3_ctrl_p[MU3C_U3_PORT_MAX];
+ __le64 u2_ctrl_p[MU3C_U2_PORT_MAX];
+ __le32 reserved2;
+ __le32 u2_phy_pll;
+ __le32 reserved3[33]; /* 0x80 ~ 0xff */
+};
+
+struct xhci_hcd_mtk {
+ struct device *dev;
+ struct usb_hcd *hcd;
+ struct mu3h_sch_bw_info *sch_array;
+ struct mu3c_ippc_regs __iomem *ippc_regs;
+ int num_u2_ports;
+ int num_u3_ports;
+ struct regulator *vusb33;
+ struct regulator *vbus;
+ struct clk *sys_clk; /* sys and mac clock */
+ struct clk *wk_deb_p0; /* port0's wakeup debounce clock */
+ struct clk *wk_deb_p1;
+ struct regmap *pericfg;
+ struct phy **phys;
+ int num_phys;
+ int wakeup_src;
+ bool lpm_support;
+};
+
+static inline struct xhci_hcd_mtk *hcd_to_mtk(struct usb_hcd *hcd)
+{
+ return dev_get_drvdata(hcd->self.controller);
+}
+
+#if IS_ENABLED(CONFIG_USB_XHCI_MTK)
+int xhci_mtk_sch_init(struct xhci_hcd_mtk *mtk);
+void xhci_mtk_sch_exit(struct xhci_hcd_mtk *mtk);
+int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
+ struct usb_host_endpoint *ep);
+void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
+ struct usb_host_endpoint *ep);
+
+#else
+static inline int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd,
+ struct usb_device *udev, struct usb_host_endpoint *ep)
+{
+ return 0;
+}
+
+static inline void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd,
+ struct usb_device *udev, struct usb_host_endpoint *ep)
+{
+}
+
+#endif
+
+#endif /* _XHCI_MTK_H_ */
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 17f6897acde2..58c43ed7ff3b 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -53,7 +53,6 @@ static struct hc_driver __read_mostly xhci_pci_hc_driver;
static int xhci_pci_setup(struct usb_hcd *hcd);
static const struct xhci_driver_overrides xhci_pci_overrides __initconst = {
- .extra_priv_size = sizeof(struct xhci_hcd),
.reset = xhci_pci_setup,
};
@@ -188,10 +187,14 @@ static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev)
0xb7, 0x0c, 0x34, 0xac, 0x01, 0xe9, 0xbf, 0x45,
0xb7, 0xe6, 0x2b, 0x34, 0xec, 0x93, 0x1e, 0x23,
};
- acpi_evaluate_dsm(ACPI_HANDLE(&dev->dev), intel_dsm_uuid, 3, 1, NULL);
+ union acpi_object *obj;
+
+ obj = acpi_evaluate_dsm(ACPI_HANDLE(&dev->dev), intel_dsm_uuid, 3, 1,
+ NULL);
+ ACPI_FREE(obj);
}
#else
- static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev) { }
+static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev) { }
#endif /* CONFIG_ACPI */
/* called during probe() after chip reset completes */
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 05647e6753cd..770b6b088797 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -22,6 +22,7 @@
#include <linux/acpi.h>
#include "xhci.h"
+#include "xhci-plat.h"
#include "xhci-mvebu.h"
#include "xhci-rcar.h"
@@ -31,7 +32,7 @@ static int xhci_plat_setup(struct usb_hcd *hcd);
static int xhci_plat_start(struct usb_hcd *hcd);
static const struct xhci_driver_overrides xhci_plat_overrides __initconst = {
- .extra_priv_size = sizeof(struct xhci_hcd),
+ .extra_priv_size = sizeof(struct xhci_plat_priv),
.reset = xhci_plat_setup,
.start = xhci_plat_start,
};
@@ -49,11 +50,10 @@ static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
/* called during probe() after chip reset completes */
static int xhci_plat_setup(struct usb_hcd *hcd)
{
- struct device_node *of_node = hcd->self.controller->of_node;
int ret;
- if (of_device_is_compatible(of_node, "renesas,xhci-r8a7790") ||
- of_device_is_compatible(of_node, "renesas,xhci-r8a7791")) {
+ if (xhci_plat_type_is(hcd, XHCI_PLAT_TYPE_RENESAS_RCAR_GEN2) ||
+ xhci_plat_type_is(hcd, XHCI_PLAT_TYPE_RENESAS_RCAR_GEN3)) {
ret = xhci_rcar_init_quirk(hcd);
if (ret)
return ret;
@@ -64,19 +64,62 @@ static int xhci_plat_setup(struct usb_hcd *hcd)
static int xhci_plat_start(struct usb_hcd *hcd)
{
- struct device_node *of_node = hcd->self.controller->of_node;
-
- if (of_device_is_compatible(of_node, "renesas,xhci-r8a7790") ||
- of_device_is_compatible(of_node, "renesas,xhci-r8a7791"))
+ if (xhci_plat_type_is(hcd, XHCI_PLAT_TYPE_RENESAS_RCAR_GEN2) ||
+ xhci_plat_type_is(hcd, XHCI_PLAT_TYPE_RENESAS_RCAR_GEN3))
xhci_rcar_start(hcd);
return xhci_run(hcd);
}
+#ifdef CONFIG_OF
+static const struct xhci_plat_priv xhci_plat_marvell_armada = {
+ .type = XHCI_PLAT_TYPE_MARVELL_ARMADA,
+};
+
+static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen2 = {
+ .type = XHCI_PLAT_TYPE_RENESAS_RCAR_GEN2,
+ .firmware_name = XHCI_RCAR_FIRMWARE_NAME_V1,
+};
+
+static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = {
+ .type = XHCI_PLAT_TYPE_RENESAS_RCAR_GEN3,
+ .firmware_name = XHCI_RCAR_FIRMWARE_NAME_V2,
+};
+
+static const struct of_device_id usb_xhci_of_match[] = {
+ {
+ .compatible = "generic-xhci",
+ }, {
+ .compatible = "xhci-platform",
+ }, {
+ .compatible = "marvell,armada-375-xhci",
+ .data = &xhci_plat_marvell_armada,
+ }, {
+ .compatible = "marvell,armada-380-xhci",
+ .data = &xhci_plat_marvell_armada,
+ }, {
+ .compatible = "renesas,xhci-r8a7790",
+ .data = &xhci_plat_renesas_rcar_gen2,
+ }, {
+ .compatible = "renesas,xhci-r8a7791",
+ .data = &xhci_plat_renesas_rcar_gen2,
+ }, {
+ .compatible = "renesas,xhci-r8a7793",
+ .data = &xhci_plat_renesas_rcar_gen2,
+ }, {
+ .compatible = "renesas,xhci-r8a7795",
+ .data = &xhci_plat_renesas_rcar_gen3,
+ }, {
+ },
+};
+MODULE_DEVICE_TABLE(of, usb_xhci_of_match);
+#endif
+
static int xhci_plat_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct usb_xhci_pdata *pdata = dev_get_platdata(&pdev->dev);
+ const struct of_device_id *match;
const struct hc_driver *driver;
struct xhci_hcd *xhci;
struct resource *res;
@@ -134,10 +177,17 @@ static int xhci_plat_probe(struct platform_device *pdev)
goto put_hcd;
}
- if (of_device_is_compatible(pdev->dev.of_node,
- "marvell,armada-375-xhci") ||
- of_device_is_compatible(pdev->dev.of_node,
- "marvell,armada-380-xhci")) {
+ xhci = hcd_to_xhci(hcd);
+ match = of_match_node(usb_xhci_of_match, node);
+ if (match) {
+ const struct xhci_plat_priv *priv_match = match->data;
+ struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
+
+ /* Just copy data for now */
+ *priv = *priv_match;
+ }
+
+ if (xhci_plat_type_is(hcd, XHCI_PLAT_TYPE_MARVELL_ARMADA)) {
ret = xhci_mvebu_mbus_init_quirk(pdev);
if (ret)
goto disable_clk;
@@ -145,7 +195,6 @@ static int xhci_plat_probe(struct platform_device *pdev)
device_wakeup_enable(hcd->self.controller);
- xhci = hcd_to_xhci(hcd);
xhci->clk = clk;
xhci->main_hcd = hcd;
xhci->shared_hcd = usb_create_shared_hcd(driver, &pdev->dev,
@@ -256,19 +305,6 @@ static const struct dev_pm_ops xhci_plat_pm_ops = {
#define DEV_PM_OPS NULL
#endif /* CONFIG_PM */
-#ifdef CONFIG_OF
-static const struct of_device_id usb_xhci_of_match[] = {
- { .compatible = "generic-xhci" },
- { .compatible = "xhci-platform" },
- { .compatible = "marvell,armada-375-xhci"},
- { .compatible = "marvell,armada-380-xhci"},
- { .compatible = "renesas,xhci-r8a7790"},
- { .compatible = "renesas,xhci-r8a7791"},
- { },
-};
-MODULE_DEVICE_TABLE(of, usb_xhci_of_match);
-#endif
-
static const struct acpi_device_id usb_xhci_acpi_match[] = {
/* XHCI-compliant USB Controller */
{ "PNP0D10", },
diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h
new file mode 100644
index 000000000000..5a2e2e3936c4
--- /dev/null
+++ b/drivers/usb/host/xhci-plat.h
@@ -0,0 +1,39 @@
+/*
+ * xhci-plat.h - xHCI host controller driver platform Bus Glue.
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#ifndef _XHCI_PLAT_H
+#define _XHCI_PLAT_H
+
+#include "xhci.h" /* for hcd_to_xhci() */
+
+enum xhci_plat_type {
+ XHCI_PLAT_TYPE_MARVELL_ARMADA,
+ XHCI_PLAT_TYPE_RENESAS_RCAR_GEN2,
+ XHCI_PLAT_TYPE_RENESAS_RCAR_GEN3,
+};
+
+struct xhci_plat_priv {
+ enum xhci_plat_type type;
+ const char *firmware_name;
+};
+
+#define hcd_to_xhci_priv(h) ((struct xhci_plat_priv *)hcd_to_xhci(h)->priv)
+
+static inline bool xhci_plat_type_is(struct usb_hcd *hcd,
+ enum xhci_plat_type type)
+{
+ struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
+
+ if (priv && priv->type == type)
+ return true;
+ else
+ return false;
+}
+#endif /* _XHCI_PLAT_H */
diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c
index ff0d1b44ea58..623100e9385e 100644
--- a/drivers/usb/host/xhci-rcar.c
+++ b/drivers/usb/host/xhci-rcar.c
@@ -14,10 +14,17 @@
#include <linux/usb/phy.h>
#include "xhci.h"
+#include "xhci-plat.h"
#include "xhci-rcar.h"
-#define FIRMWARE_NAME "r8a779x_usb3_v1.dlmem"
-MODULE_FIRMWARE(FIRMWARE_NAME);
+/*
+* - The V2 firmware is possible to use on R-Car Gen2. However, the V2 causes
+* performance degradation. So, this driver continues to use the V1 if R-Car
+* Gen2.
+* - The V1 firmware is impossible to use on R-Car Gen3.
+*/
+MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V1);
+MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V2);
/*** Register Offset ***/
#define RCAR_USB3_INT_ENA 0x224 /* Interrupt Enable */
@@ -56,6 +63,19 @@ MODULE_FIRMWARE(FIRMWARE_NAME);
#define RCAR_USB3_RX_POL_VAL BIT(21)
#define RCAR_USB3_TX_POL_VAL BIT(4)
+static void xhci_rcar_start_gen2(struct usb_hcd *hcd)
+{
+ /* LCLK Select */
+ writel(RCAR_USB3_LCLK_ENA_VAL, hcd->regs + RCAR_USB3_LCLK);
+ /* USB3.0 Configuration */
+ writel(RCAR_USB3_CONF1_VAL, hcd->regs + RCAR_USB3_CONF1);
+ writel(RCAR_USB3_CONF2_VAL, hcd->regs + RCAR_USB3_CONF2);
+ writel(RCAR_USB3_CONF3_VAL, hcd->regs + RCAR_USB3_CONF3);
+ /* USB3.0 Polarity */
+ writel(RCAR_USB3_RX_POL_VAL, hcd->regs + RCAR_USB3_RX_POL);
+ writel(RCAR_USB3_TX_POL_VAL, hcd->regs + RCAR_USB3_TX_POL);
+}
+
void xhci_rcar_start(struct usb_hcd *hcd)
{
u32 temp;
@@ -65,27 +85,23 @@ void xhci_rcar_start(struct usb_hcd *hcd)
temp = readl(hcd->regs + RCAR_USB3_INT_ENA);
temp |= RCAR_USB3_INT_ENA_VAL;
writel(temp, hcd->regs + RCAR_USB3_INT_ENA);
- /* LCLK Select */
- writel(RCAR_USB3_LCLK_ENA_VAL, hcd->regs + RCAR_USB3_LCLK);
- /* USB3.0 Configuration */
- writel(RCAR_USB3_CONF1_VAL, hcd->regs + RCAR_USB3_CONF1);
- writel(RCAR_USB3_CONF2_VAL, hcd->regs + RCAR_USB3_CONF2);
- writel(RCAR_USB3_CONF3_VAL, hcd->regs + RCAR_USB3_CONF3);
- /* USB3.0 Polarity */
- writel(RCAR_USB3_RX_POL_VAL, hcd->regs + RCAR_USB3_RX_POL);
- writel(RCAR_USB3_TX_POL_VAL, hcd->regs + RCAR_USB3_TX_POL);
+ if (xhci_plat_type_is(hcd, XHCI_PLAT_TYPE_RENESAS_RCAR_GEN2))
+ xhci_rcar_start_gen2(hcd);
}
}
-static int xhci_rcar_download_firmware(struct device *dev, void __iomem *regs)
+static int xhci_rcar_download_firmware(struct usb_hcd *hcd)
{
+ struct device *dev = hcd->self.controller;
+ void __iomem *regs = hcd->regs;
+ struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
const struct firmware *fw;
int retval, index, j, time;
int timeout = 10000;
u32 data, val, temp;
/* request R-Car USB3.0 firmware */
- retval = request_firmware(&fw, FIRMWARE_NAME, dev);
+ retval = request_firmware(&fw, priv->firmware_name, dev);
if (retval)
return retval;
@@ -144,5 +160,5 @@ int xhci_rcar_init_quirk(struct usb_hcd *hcd)
if (!hcd->regs)
return 0;
- return xhci_rcar_download_firmware(hcd->self.controller, hcd->regs);
+ return xhci_rcar_download_firmware(hcd);
}
diff --git a/drivers/usb/host/xhci-rcar.h b/drivers/usb/host/xhci-rcar.h
index 58501256715d..2941a25cfe98 100644
--- a/drivers/usb/host/xhci-rcar.h
+++ b/drivers/usb/host/xhci-rcar.h
@@ -11,6 +11,9 @@
#ifndef _XHCI_RCAR_H
#define _XHCI_RCAR_H
+#define XHCI_RCAR_FIRMWARE_NAME_V1 "r8a779x_usb3_v1.dlmem"
+#define XHCI_RCAR_FIRMWARE_NAME_V2 "r8a779x_usb3_v2.dlmem"
+
#if IS_ENABLED(CONFIG_USB_XHCI_RCAR)
void xhci_rcar_start(struct usb_hcd *hcd);
int xhci_rcar_init_quirk(struct usb_hcd *hcd);
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 6c5e8133cf87..f1c21c40b4a6 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -68,6 +68,7 @@
#include <linux/slab.h>
#include "xhci.h"
#include "xhci-trace.h"
+#include "xhci-mtk.h"
/*
* Returns zero if the TRB isn't in this segment, otherwise it returns the DMA
@@ -1583,7 +1584,8 @@ static void handle_port_status(struct xhci_hcd *xhci,
*/
bogus_port_status = true;
goto cleanup;
- } else {
+ } else if (!test_bit(faked_port_index,
+ &bus_state->resuming_ports)) {
xhci_dbg(xhci, "resume HS port %d\n", port_id);
bus_state->resume_done[faked_port_index] = jiffies +
msecs_to_jiffies(USB_RESUME_TIMEOUT);
@@ -3074,17 +3076,22 @@ static u32 xhci_td_remainder(struct xhci_hcd *xhci, int transferred,
{
u32 maxp, total_packet_count;
- if (xhci->hci_version < 0x100)
+ /* MTK xHCI is mostly 0.97 but contains some features from 1.0 */
+ if (xhci->hci_version < 0x100 && !(xhci->quirks & XHCI_MTK_HOST))
return ((td_total_len - transferred) >> 10);
- maxp = GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc));
- total_packet_count = DIV_ROUND_UP(td_total_len, maxp);
-
/* One TRB with a zero-length data packet. */
if (num_trbs_left == 0 || (transferred == 0 && trb_buff_len == 0) ||
trb_buff_len == td_total_len)
return 0;
+ /* for MTK xHCI, TD size doesn't include this TRB */
+ if (xhci->quirks & XHCI_MTK_HOST)
+ trb_buff_len = 0;
+
+ maxp = GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc));
+ total_packet_count = DIV_ROUND_UP(td_total_len, maxp);
+
/* Queueing functions don't count the current TRB into transferred */
return (total_packet_count - ((transferred + trb_buff_len) / maxp));
}
@@ -3472,7 +3479,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
field |= 0x1;
/* xHCI 1.0/1.1 6.4.1.2.1: Transfer Type field */
- if (xhci->hci_version >= 0x100) {
+ if ((xhci->hci_version >= 0x100) || (xhci->quirks & XHCI_MTK_HOST)) {
if (urb->transfer_buffer_length > 0) {
if (setup->bRequestType & USB_DIR_IN)
field |= TRB_TX_TYPE(TRB_DATA_IN);
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index dfa44d3e8eee..26a44c0e969e 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -31,6 +31,7 @@
#include "xhci.h"
#include "xhci-trace.h"
+#include "xhci-mtk.h"
#define DRIVER_AUTHOR "Sarah Sharp"
#define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver"
@@ -634,7 +635,11 @@ int xhci_run(struct usb_hcd *hcd)
"// Set the interrupt modulation register");
temp = readl(&xhci->ir_set->irq_control);
temp &= ~ER_IRQ_INTERVAL_MASK;
- temp |= (u32) 160;
+ /*
+ * the increment interval is 8 times as much as that defined
+ * in xHCI spec on MTK's controller
+ */
+ temp |= (u32) ((xhci->quirks & XHCI_MTK_HOST) ? 20 : 160);
writel(temp, &xhci->ir_set->irq_control);
/* Set the HCD state before we enable the irqs */
@@ -1698,6 +1703,9 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
xhci_endpoint_zero(xhci, xhci->devs[udev->slot_id], ep);
+ if (xhci->quirks & XHCI_MTK_HOST)
+ xhci_mtk_drop_ep_quirk(hcd, udev, ep);
+
xhci_dbg(xhci, "drop ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x\n",
(unsigned int) ep->desc.bEndpointAddress,
udev->slot_id,
@@ -1793,6 +1801,15 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
return -ENOMEM;
}
+ if (xhci->quirks & XHCI_MTK_HOST) {
+ ret = xhci_mtk_add_ep_quirk(hcd, udev, ep);
+ if (ret < 0) {
+ xhci_free_or_cache_endpoint_ring(xhci,
+ virt_dev, ep_index);
+ return ret;
+ }
+ }
+
ctrl_ctx->add_flags |= cpu_to_le32(added_ctxs);
new_add_flags = le32_to_cpu(ctrl_ctx->add_flags);
@@ -4778,8 +4795,16 @@ int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
slot_ctx = xhci_get_slot_ctx(xhci, config_cmd->in_ctx);
slot_ctx->dev_info |= cpu_to_le32(DEV_HUB);
+ /*
+ * refer to section 6.2.2: MTT should be 0 for full speed hub,
+ * but it may be already set to 1 when setup an xHCI virtual
+ * device, so clear it anyway.
+ */
if (tt->multi)
slot_ctx->dev_info |= cpu_to_le32(DEV_MTT);
+ else if (hdev->speed == USB_SPEED_FULL)
+ slot_ctx->dev_info &= cpu_to_le32(~DEV_MTT);
+
if (xhci->hci_version > 0x95) {
xhci_dbg(xhci, "xHCI version %x needs hub "
"TT think time and number of ports\n",
@@ -4952,7 +4977,7 @@ EXPORT_SYMBOL_GPL(xhci_gen_setup);
static const struct hc_driver xhci_hc_driver = {
.description = "xhci-hcd",
.product_desc = "xHCI Host Controller",
- .hcd_priv_size = sizeof(struct xhci_hcd *),
+ .hcd_priv_size = sizeof(struct xhci_hcd),
/*
* generic hardware linkage
@@ -5051,6 +5076,10 @@ static int __init xhci_hcd_init(void)
BUILD_BUG_ON(sizeof(struct xhci_intr_reg) != 8*32/8);
/* xhci_run_regs has eight fields and embeds 128 xhci_intr_regs */
BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8);
+
+ if (usb_disabled())
+ return -ENODEV;
+
return 0;
}
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 0b9451250e33..9be7348872ba 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1630,6 +1630,7 @@ struct xhci_hcd {
/* For controllers with a broken beyond repair streams implementation */
#define XHCI_BROKEN_STREAMS (1 << 19)
#define XHCI_PME_STUCK_QUIRK (1 << 20)
+#define XHCI_MTK_HOST (1 << 21)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
@@ -1656,6 +1657,9 @@ struct xhci_hcd {
u32 port_status_u0;
/* Compliance Mode Timer Triggered every 2 seconds */
#define COMP_MODE_RCVRY_MSECS 2000
+
+ /* platform-specific data -- must come last */
+ unsigned long priv[0] __aligned(sizeof(s64));
};
/* Platform specific overrides to generic XHCI hc_driver ops */
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c
index 306d6852ebc7..8efbabacc84e 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.c
+++ b/drivers/usb/misc/sisusbvga/sisusb.c
@@ -2825,21 +2825,7 @@ sisusb_lseek(struct file *file, loff_t offset, int orig)
return -ENODEV;
}
- switch (orig) {
- case 0:
- file->f_pos = offset;
- ret = file->f_pos;
- /* never negative, no force_successful_syscall needed */
- break;
- case 1:
- file->f_pos += offset;
- ret = file->f_pos;
- /* never negative, no force_successful_syscall needed */
- break;
- default:
- /* seeking relative to "end of file" is not supported */
- ret = -EINVAL;
- }
+ ret = no_seek_end_llseek(file, offset, orig);
mutex_unlock(&sisusb->lock);
return ret;
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index 637f3f7cfce8..92fdb6e9faff 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -22,18 +22,42 @@ static void complicated_callback(struct urb *urb);
/*-------------------------------------------------------------------------*/
/* FIXME make these public somewhere; usbdevfs.h? */
-struct usbtest_param {
+
+/* Parameter for usbtest driver. */
+struct usbtest_param_32 {
/* inputs */
- unsigned test_num; /* 0..(TEST_CASES-1) */
- unsigned iterations;
- unsigned length;
- unsigned vary;
- unsigned sglen;
+ __u32 test_num; /* 0..(TEST_CASES-1) */
+ __u32 iterations;
+ __u32 length;
+ __u32 vary;
+ __u32 sglen;
/* outputs */
- struct timeval duration;
+ __s32 duration_sec;
+ __s32 duration_usec;
};
-#define USBTEST_REQUEST _IOWR('U', 100, struct usbtest_param)
+
+/*
+ * Compat parameter to the usbtest driver.
+ * This supports older user space binaries compiled with 64 bit compiler.
+ */
+struct usbtest_param_64 {
+ /* inputs */
+ __u32 test_num; /* 0..(TEST_CASES-1) */
+ __u32 iterations;
+ __u32 length;
+ __u32 vary;
+ __u32 sglen;
+
+ /* outputs */
+ __s64 duration_sec;
+ __s64 duration_usec;
+};
+
+/* IOCTL interface to the driver. */
+#define USBTEST_REQUEST_32 _IOWR('U', 100, struct usbtest_param_32)
+/* COMPAT IOCTL interface to the driver. */
+#define USBTEST_REQUEST_64 _IOWR('U', 100, struct usbtest_param_64)
/*-------------------------------------------------------------------------*/
@@ -1030,7 +1054,7 @@ struct ctrl_ctx {
unsigned pending;
int status;
struct urb **urb;
- struct usbtest_param *param;
+ struct usbtest_param_32 *param;
int last;
};
@@ -1155,7 +1179,7 @@ error:
}
static int
-test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param *param)
+test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param)
{
struct usb_device *udev = testdev_to_usbdev(dev);
struct urb **urb;
@@ -1849,7 +1873,7 @@ static void complicated_callback(struct urb *urb)
goto done;
default:
dev_err(&ctx->dev->intf->dev,
- "iso resubmit err %d\n",
+ "resubmit err %d\n",
status);
/* FALLTHROUGH */
case -ENODEV: /* disconnected */
@@ -1863,7 +1887,7 @@ static void complicated_callback(struct urb *urb)
if (ctx->pending == 0) {
if (ctx->errors)
dev_err(&ctx->dev->intf->dev,
- "iso test, %lu errors out of %lu\n",
+ "during the test, %lu errors out of %lu\n",
ctx->errors, ctx->packet_count);
complete(&ctx->done);
}
@@ -1930,7 +1954,7 @@ static struct urb *iso_alloc_urb(
}
static int
-test_queue(struct usbtest_dev *dev, struct usbtest_param *param,
+test_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param,
int pipe, struct usb_endpoint_descriptor *desc, unsigned offset)
{
struct transfer_context context;
@@ -2049,81 +2073,20 @@ static int test_unaligned_bulk(
return retval;
}
-/*-------------------------------------------------------------------------*/
-
-/* We only have this one interface to user space, through usbfs.
- * User mode code can scan usbfs to find N different devices (maybe on
- * different busses) to use when testing, and allocate one thread per
- * test. So discovery is simplified, and we have no device naming issues.
- *
- * Don't use these only as stress/load tests. Use them along with with
- * other USB bus activity: plugging, unplugging, mousing, mp3 playback,
- * video capture, and so on. Run different tests at different times, in
- * different sequences. Nothing here should interact with other devices,
- * except indirectly by consuming USB bandwidth and CPU resources for test
- * threads and request completion. But the only way to know that for sure
- * is to test when HC queues are in use by many devices.
- *
- * WARNING: Because usbfs grabs udev->dev.sem before calling this ioctl(),
- * it locks out usbcore in certain code paths. Notably, if you disconnect
- * the device-under-test, hub_wq will wait block forever waiting for the
- * ioctl to complete ... so that usb_disconnect() can abort the pending
- * urbs and then call usbtest_disconnect(). To abort a test, you're best
- * off just killing the userspace task and waiting for it to exit.
- */
-
+/* Run tests. */
static int
-usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
+usbtest_do_ioctl(struct usb_interface *intf, struct usbtest_param_32 *param)
{
struct usbtest_dev *dev = usb_get_intfdata(intf);
struct usb_device *udev = testdev_to_usbdev(dev);
- struct usbtest_param *param = buf;
- int retval = -EOPNOTSUPP;
struct urb *urb;
struct scatterlist *sg;
struct usb_sg_request req;
- struct timeval start;
unsigned i;
-
- /* FIXME USBDEVFS_CONNECTINFO doesn't say how fast the device is. */
-
- pattern = mod_pattern;
-
- if (code != USBTEST_REQUEST)
- return -EOPNOTSUPP;
+ int retval = -EOPNOTSUPP;
if (param->iterations <= 0)
return -EINVAL;
-
- if (param->sglen > MAX_SGLEN)
- return -EINVAL;
-
- if (mutex_lock_interruptible(&dev->lock))
- return -ERESTARTSYS;
-
- /* FIXME: What if a system sleep starts while a test is running? */
-
- /* some devices, like ez-usb default devices, need a non-default
- * altsetting to have any active endpoints. some tests change
- * altsettings; force a default so most tests don't need to check.
- */
- if (dev->info->alt >= 0) {
- int res;
-
- if (intf->altsetting->desc.bInterfaceNumber) {
- mutex_unlock(&dev->lock);
- return -ENODEV;
- }
- res = set_altsetting(dev, dev->info->alt);
- if (res) {
- dev_err(&intf->dev,
- "set altsetting to %d failed, %d\n",
- dev->info->alt, res);
- mutex_unlock(&dev->lock);
- return res;
- }
- }
-
/*
* Just a bunch of test cases that every HCD is expected to handle.
*
@@ -2133,7 +2096,6 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
* FIXME add more tests! cancel requests, verify the data, control
* queueing, concurrent read+write threads, and so on.
*/
- do_gettimeofday(&start);
switch (param->test_num) {
case 0:
@@ -2548,13 +2510,116 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
dev->in_pipe, NULL, 0);
break;
}
- do_gettimeofday(&param->duration);
- param->duration.tv_sec -= start.tv_sec;
- param->duration.tv_usec -= start.tv_usec;
- if (param->duration.tv_usec < 0) {
- param->duration.tv_usec += 1000 * 1000;
- param->duration.tv_sec -= 1;
+ return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* We only have this one interface to user space, through usbfs.
+ * User mode code can scan usbfs to find N different devices (maybe on
+ * different busses) to use when testing, and allocate one thread per
+ * test. So discovery is simplified, and we have no device naming issues.
+ *
+ * Don't use these only as stress/load tests. Use them along with with
+ * other USB bus activity: plugging, unplugging, mousing, mp3 playback,
+ * video capture, and so on. Run different tests at different times, in
+ * different sequences. Nothing here should interact with other devices,
+ * except indirectly by consuming USB bandwidth and CPU resources for test
+ * threads and request completion. But the only way to know that for sure
+ * is to test when HC queues are in use by many devices.
+ *
+ * WARNING: Because usbfs grabs udev->dev.sem before calling this ioctl(),
+ * it locks out usbcore in certain code paths. Notably, if you disconnect
+ * the device-under-test, hub_wq will wait block forever waiting for the
+ * ioctl to complete ... so that usb_disconnect() can abort the pending
+ * urbs and then call usbtest_disconnect(). To abort a test, you're best
+ * off just killing the userspace task and waiting for it to exit.
+ */
+
+static int
+usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf)
+{
+
+ struct usbtest_dev *dev = usb_get_intfdata(intf);
+ struct usbtest_param_64 *param_64 = buf;
+ struct usbtest_param_32 temp;
+ struct usbtest_param_32 *param_32 = buf;
+ struct timespec64 start;
+ struct timespec64 end;
+ struct timespec64 duration;
+ int retval = -EOPNOTSUPP;
+
+ /* FIXME USBDEVFS_CONNECTINFO doesn't say how fast the device is. */
+
+ pattern = mod_pattern;
+
+ if (mutex_lock_interruptible(&dev->lock))
+ return -ERESTARTSYS;
+
+ /* FIXME: What if a system sleep starts while a test is running? */
+
+ /* some devices, like ez-usb default devices, need a non-default
+ * altsetting to have any active endpoints. some tests change
+ * altsettings; force a default so most tests don't need to check.
+ */
+ if (dev->info->alt >= 0) {
+ if (intf->altsetting->desc.bInterfaceNumber) {
+ retval = -ENODEV;
+ goto free_mutex;
+ }
+ retval = set_altsetting(dev, dev->info->alt);
+ if (retval) {
+ dev_err(&intf->dev,
+ "set altsetting to %d failed, %d\n",
+ dev->info->alt, retval);
+ goto free_mutex;
+ }
+ }
+
+ switch (code) {
+ case USBTEST_REQUEST_64:
+ temp.test_num = param_64->test_num;
+ temp.iterations = param_64->iterations;
+ temp.length = param_64->length;
+ temp.sglen = param_64->sglen;
+ temp.vary = param_64->vary;
+ param_32 = &temp;
+ break;
+
+ case USBTEST_REQUEST_32:
+ break;
+
+ default:
+ retval = -EOPNOTSUPP;
+ goto free_mutex;
+ }
+
+ ktime_get_ts64(&start);
+
+ retval = usbtest_do_ioctl(intf, param_32);
+ if (retval)
+ goto free_mutex;
+
+ ktime_get_ts64(&end);
+
+ duration = timespec64_sub(end, start);
+
+ temp.duration_sec = duration.tv_sec;
+ temp.duration_usec = duration.tv_nsec/NSEC_PER_USEC;
+
+ switch (code) {
+ case USBTEST_REQUEST_32:
+ param_32->duration_sec = temp.duration_sec;
+ param_32->duration_usec = temp.duration_usec;
+ break;
+
+ case USBTEST_REQUEST_64:
+ param_64->duration_sec = temp.duration_sec;
+ param_64->duration_usec = temp.duration_usec;
+ break;
}
+
+free_mutex:
mutex_unlock(&dev->lock);
return retval;
}
diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c
index 3598f1a62673..1a874a1f3890 100644
--- a/drivers/usb/mon/mon_bin.c
+++ b/drivers/usb/mon/mon_bin.c
@@ -18,6 +18,7 @@
#include <linux/mm.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
+#include <linux/time64.h>
#include <asm/uaccess.h>
@@ -92,8 +93,8 @@ struct mon_bin_hdr {
unsigned short busnum; /* Bus number */
char flag_setup;
char flag_data;
- s64 ts_sec; /* gettimeofday */
- s32 ts_usec; /* gettimeofday */
+ s64 ts_sec; /* getnstimeofday64 */
+ s32 ts_usec; /* getnstimeofday64 */
int status;
unsigned int len_urb; /* Length of data (submitted or actual) */
unsigned int len_cap; /* Delivered length */
@@ -483,7 +484,7 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
char ev_type, int status)
{
const struct usb_endpoint_descriptor *epd = &urb->ep->desc;
- struct timeval ts;
+ struct timespec64 ts;
unsigned long flags;
unsigned int urb_length;
unsigned int offset;
@@ -494,7 +495,7 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
struct mon_bin_hdr *ep;
char data_tag = 0;
- do_gettimeofday(&ts);
+ getnstimeofday64(&ts);
spin_lock_irqsave(&rp->b_lock, flags);
@@ -568,7 +569,7 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
ep->busnum = urb->dev->bus->busnum;
ep->id = (unsigned long) urb;
ep->ts_sec = ts.tv_sec;
- ep->ts_usec = ts.tv_usec;
+ ep->ts_usec = ts.tv_nsec / NSEC_PER_USEC;
ep->status = status;
ep->len_urb = urb_length;
ep->len_cap = length + lendesc;
@@ -629,12 +630,12 @@ static void mon_bin_complete(void *data, struct urb *urb, int status)
static void mon_bin_error(void *data, struct urb *urb, int error)
{
struct mon_reader_bin *rp = data;
- struct timeval ts;
+ struct timespec64 ts;
unsigned long flags;
unsigned int offset;
struct mon_bin_hdr *ep;
- do_gettimeofday(&ts);
+ getnstimeofday64(&ts);
spin_lock_irqsave(&rp->b_lock, flags);
@@ -656,7 +657,7 @@ static void mon_bin_error(void *data, struct urb *urb, int error)
ep->busnum = urb->dev->bus->busnum;
ep->id = (unsigned long) urb;
ep->ts_sec = ts.tv_sec;
- ep->ts_usec = ts.tv_usec;
+ ep->ts_usec = ts.tv_nsec / NSEC_PER_USEC;
ep->status = error;
ep->flag_setup = '-';
diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c
index f7c292f4891e..fec3f1128fdc 100644
--- a/drivers/usb/mon/mon_main.c
+++ b/drivers/usb/mon/mon_main.c
@@ -241,7 +241,7 @@ static struct notifier_block mon_nb = {
/*
* Ops
*/
-static struct usb_mon_operations mon_ops_0 = {
+static const struct usb_mon_operations mon_ops_0 = {
.urb_submit = mon_submit,
.urb_submit_error = mon_submit_error,
.urb_complete = mon_complete,
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index ad408251d955..e59334b09c41 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -9,6 +9,7 @@
#include <linux/usb.h>
#include <linux/slab.h>
#include <linux/time.h>
+#include <linux/ktime.h>
#include <linux/export.h>
#include <linux/mutex.h>
#include <linux/debugfs.h>
@@ -176,12 +177,12 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
static inline unsigned int mon_get_timestamp(void)
{
- struct timeval tval;
+ struct timespec64 now;
unsigned int stamp;
- do_gettimeofday(&tval);
- stamp = tval.tv_sec & 0xFFF; /* 2^32 = 4294967296. Limit to 4096s. */
- stamp = stamp * 1000000 + tval.tv_usec;
+ ktime_get_ts64(&now);
+ stamp = now.tv_sec & 0xFFF; /* 2^32 = 4294967296. Limit to 4096s. */
+ stamp = stamp * USEC_PER_SEC + now.tv_nsec / NSEC_PER_USEC;
return stamp;
}
@@ -386,7 +387,8 @@ static ssize_t mon_text_read_t(struct file *file, char __user *buf,
struct mon_event_text *ep;
struct mon_text_ptr ptr;
- if (IS_ERR(ep = mon_text_read_wait(rp, file)))
+ ep = mon_text_read_wait(rp, file);
+ if (IS_ERR(ep))
return PTR_ERR(ep);
mutex_lock(&rp->printf_lock);
ptr.cnt = 0;
@@ -413,7 +415,8 @@ static ssize_t mon_text_read_u(struct file *file, char __user *buf,
struct mon_event_text *ep;
struct mon_text_ptr ptr;
- if (IS_ERR(ep = mon_text_read_wait(rp, file)))
+ ep = mon_text_read_wait(rp, file);
+ if (IS_ERR(ep))
return PTR_ERR(ep);
mutex_lock(&rp->printf_lock);
ptr.cnt = 0;
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index 1f2037bbeb0d..45c83baf675d 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -159,7 +159,7 @@ config USB_TI_CPPI_DMA
config USB_TI_CPPI41_DMA
bool 'TI CPPI 4.1 (AM335x)'
- depends on ARCH_OMAP
+ depends on ARCH_OMAP && DMADEVICES
select TI_CPPI41
config USB_TUSB_OMAP_DMA
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 18cfc0a361cb..c3791a01ab31 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -1360,8 +1360,7 @@ static int ep_config_from_table(struct musb *musb)
break;
}
- printk(KERN_DEBUG "%s: setup fifo_mode %d\n",
- musb_driver_name, fifo_mode);
+ pr_debug("%s: setup fifo_mode %d\n", musb_driver_name, fifo_mode);
done:
@@ -1390,7 +1389,7 @@ done:
musb->nr_endpoints = max(epn, musb->nr_endpoints);
}
- printk(KERN_DEBUG "%s: %d/%d max ep, %d/%d memory\n",
+ pr_debug("%s: %d/%d max ep, %d/%d memory\n",
musb_driver_name,
n + 1, musb->config->num_eps * 2 - 1,
offset, (1 << (musb->config->ram_bits + 2)));
@@ -1491,8 +1490,7 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
if (reg & MUSB_CONFIGDATA_SOFTCONE)
strcat(aInfo, ", SoftConn");
- printk(KERN_DEBUG "%s: ConfigData=0x%02x (%s)\n",
- musb_driver_name, reg, aInfo);
+ pr_debug("%s: ConfigData=0x%02x (%s)\n", musb_driver_name, reg, aInfo);
aDate[0] = 0;
if (MUSB_CONTROLLER_MHDRC == musb_type) {
@@ -1502,9 +1500,8 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
musb->is_multipoint = 0;
type = "";
#ifndef CONFIG_USB_OTG_BLACKLIST_HUB
- printk(KERN_ERR
- "%s: kernel must blacklist external hubs\n",
- musb_driver_name);
+ pr_err("%s: kernel must blacklist external hubs\n",
+ musb_driver_name);
#endif
}
@@ -1513,8 +1510,8 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
snprintf(aRevision, 32, "%d.%d%s", MUSB_HWVERS_MAJOR(musb->hwvers),
MUSB_HWVERS_MINOR(musb->hwvers),
(musb->hwvers & MUSB_HWVERS_RC) ? "RC" : "");
- printk(KERN_DEBUG "%s: %sHDRC RTL version %s %s\n",
- musb_driver_name, type, aRevision, aDate);
+ pr_debug("%s: %sHDRC RTL version %s %s\n",
+ musb_driver_name, type, aRevision, aDate);
/* configure ep0 */
musb_configure_ep0(musb);
@@ -1705,6 +1702,23 @@ EXPORT_SYMBOL_GPL(musb_dma_completion);
#define use_dma 0
#endif
+static void (*musb_phy_callback)(enum musb_vbus_id_status status);
+
+/*
+ * musb_mailbox - optional phy notifier function
+ * @status phy state change
+ *
+ * Optionally gets called from the USB PHY. Note that the USB PHY must be
+ * disabled at the point the phy_callback is registered or unregistered.
+ */
+void musb_mailbox(enum musb_vbus_id_status status)
+{
+ if (musb_phy_callback)
+ musb_phy_callback(status);
+
+};
+EXPORT_SYMBOL_GPL(musb_mailbox);
+
/*-------------------------------------------------------------------------*/
static ssize_t
@@ -2017,7 +2031,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
/* We need musb_read/write functions initialized for PM */
pm_runtime_use_autosuspend(musb->controller);
pm_runtime_set_autosuspend_delay(musb->controller, 200);
- pm_runtime_irq_safe(musb->controller);
pm_runtime_enable(musb->controller);
/* The musb_platform_init() call:
@@ -2095,6 +2108,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
#ifndef CONFIG_MUSB_PIO_ONLY
if (!musb->ops->dma_init || !musb->ops->dma_exit) {
dev_err(dev, "DMA controller not set\n");
+ status = -ENODEV;
goto fail2;
}
musb_dma_controller_create = musb->ops->dma_init;
@@ -2117,8 +2131,15 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
musb->xceiv->io_ops = &musb_ulpi_access;
}
+ if (musb->ops->phy_callback)
+ musb_phy_callback = musb->ops->phy_callback;
+
pm_runtime_get_sync(musb->controller);
+ status = usb_phy_init(musb->xceiv);
+ if (status < 0)
+ goto err_usb_phy_init;
+
if (use_dma && dev->dma_mask) {
musb->dma_controller =
musb_dma_controller_create(musb, musb->mregs);
@@ -2218,6 +2239,12 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
pm_runtime_put(musb->controller);
+ /*
+ * For why this is currently needed, see commit 3e43a0725637
+ * ("usb: musb: core: add pm_runtime_irq_safe()")
+ */
+ pm_runtime_irq_safe(musb->controller);
+
return 0;
fail5:
@@ -2233,7 +2260,11 @@ fail3:
cancel_delayed_work_sync(&musb->deassert_reset_work);
if (musb->dma_controller)
musb_dma_controller_destroy(musb->dma_controller);
+
fail2_5:
+ usb_phy_shutdown(musb->xceiv);
+
+err_usb_phy_init:
pm_runtime_put_sync(musb->controller);
fail2:
@@ -2289,10 +2320,13 @@ static int musb_remove(struct platform_device *pdev)
*/
musb_exit_debugfs(musb);
musb_shutdown(pdev);
+ musb_phy_callback = NULL;
if (musb->dma_controller)
musb_dma_controller_destroy(musb->dma_controller);
+ usb_phy_shutdown(musb->xceiv);
+
cancel_work_sync(&musb->irq_work);
cancel_delayed_work_sync(&musb->finish_resume_work);
cancel_delayed_work_sync(&musb->deassert_reset_work);
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 2337d7a7d62d..fd215fb45fd4 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -168,6 +168,7 @@ struct musb_io;
* @adjust_channel_params: pre check for standard dma channel_program func
* @pre_root_reset_end: called before the root usb port reset flag gets cleared
* @post_root_reset_end: called after the root usb port reset flag gets cleared
+ * @phy_callback: optional callback function for the phy to call
*/
struct musb_platform_ops {
@@ -214,6 +215,7 @@ struct musb_platform_ops {
dma_addr_t *dma_addr, u32 *len);
void (*pre_root_reset_end)(struct musb *musb);
void (*post_root_reset_end)(struct musb *musb);
+ void (*phy_callback)(enum musb_vbus_id_status status);
};
/*
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 67ad630c86c9..87bd578799a8 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -353,9 +353,8 @@ static void txstate(struct musb *musb, struct musb_request *req)
* 1 >0 Yes(FS bulk)
*/
if (!musb_ep->hb_mult ||
- (musb_ep->hb_mult &&
- can_bulk_split(musb,
- musb_ep->type)))
+ can_bulk_split(musb,
+ musb_ep->type))
csr |= MUSB_TXCSR_AUTOSET;
}
csr &= ~MUSB_TXCSR_P_UNDERRUN;
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index 1bd9232ff76f..c84e0322c108 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -36,7 +36,7 @@
#include <linux/pm_runtime.h>
#include <linux/err.h>
#include <linux/delay.h>
-#include <linux/usb/musb-omap.h>
+#include <linux/usb/musb.h>
#include <linux/phy/omap_control_phy.h>
#include <linux/of_platform.h>
@@ -46,7 +46,7 @@
struct omap2430_glue {
struct device *dev;
struct platform_device *musb;
- enum omap_musb_vbus_id_status status;
+ enum musb_vbus_id_status status;
struct work_struct omap_musb_mailbox_work;
struct device *control_otghs;
};
@@ -234,7 +234,7 @@ static inline void omap2430_low_level_init(struct musb *musb)
musb_writel(musb->mregs, OTG_FORCESTDBY, l);
}
-void omap_musb_mailbox(enum omap_musb_vbus_id_status status)
+static void omap2430_musb_mailbox(enum musb_vbus_id_status status)
{
struct omap2430_glue *glue = _glue;
@@ -251,7 +251,6 @@ void omap_musb_mailbox(enum omap_musb_vbus_id_status status)
schedule_work(&glue->omap_musb_mailbox_work);
}
-EXPORT_SYMBOL_GPL(omap_musb_mailbox);
static void omap_musb_set_mailbox(struct omap2430_glue *glue)
{
@@ -262,7 +261,7 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
struct usb_otg *otg = musb->xceiv->otg;
switch (glue->status) {
- case OMAP_MUSB_ID_GROUND:
+ case MUSB_ID_GROUND:
dev_dbg(dev, "ID GND\n");
otg->default_a = true;
@@ -276,7 +275,7 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
}
break;
- case OMAP_MUSB_VBUS_VALID:
+ case MUSB_VBUS_VALID:
dev_dbg(dev, "VBUS Connect\n");
otg->default_a = false;
@@ -287,8 +286,8 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
omap_control_usb_set_mode(glue->control_otghs, USB_MODE_DEVICE);
break;
- case OMAP_MUSB_ID_FLOAT:
- case OMAP_MUSB_VBUS_OFF:
+ case MUSB_ID_FLOAT:
+ case MUSB_VBUS_OFF:
dev_dbg(dev, "VBUS Disconnect\n");
musb->xceiv->last_event = USB_EVENT_NONE;
@@ -430,7 +429,7 @@ static int omap2430_musb_init(struct musb *musb)
setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
- if (glue->status != OMAP_MUSB_UNKNOWN)
+ if (glue->status != MUSB_UNKNOWN)
omap_musb_set_mailbox(glue);
phy_init(musb->phy);
@@ -455,7 +454,7 @@ static void omap2430_musb_enable(struct musb *musb)
switch (glue->status) {
- case OMAP_MUSB_ID_GROUND:
+ case MUSB_ID_GROUND:
omap_control_usb_set_mode(glue->control_otghs, USB_MODE_HOST);
if (data->interface_type != MUSB_INTERFACE_UTMI)
break;
@@ -474,7 +473,7 @@ static void omap2430_musb_enable(struct musb *musb)
}
break;
- case OMAP_MUSB_VBUS_VALID:
+ case MUSB_VBUS_VALID:
omap_control_usb_set_mode(glue->control_otghs, USB_MODE_DEVICE);
break;
@@ -488,7 +487,7 @@ static void omap2430_musb_disable(struct musb *musb)
struct device *dev = musb->controller;
struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
- if (glue->status != OMAP_MUSB_UNKNOWN)
+ if (glue->status != MUSB_UNKNOWN)
omap_control_usb_set_mode(glue->control_otghs,
USB_MODE_DISCONNECT);
}
@@ -520,6 +519,8 @@ static const struct musb_platform_ops omap2430_ops = {
.enable = omap2430_musb_enable,
.disable = omap2430_musb_disable,
+
+ .phy_callback = omap2430_musb_mailbox,
};
static u64 omap2430_dmamask = DMA_BIT_MASK(32);
@@ -551,7 +552,7 @@ static int omap2430_probe(struct platform_device *pdev)
glue->dev = &pdev->dev;
glue->musb = musb;
- glue->status = OMAP_MUSB_UNKNOWN;
+ glue->status = MUSB_UNKNOWN;
glue->control_otghs = ERR_PTR(-ENODEV);
if (np) {
@@ -663,8 +664,11 @@ static int omap2430_remove(struct platform_device *pdev)
{
struct omap2430_glue *glue = platform_get_drvdata(pdev);
+ pm_runtime_get_sync(glue->dev);
cancel_work_sync(&glue->omap_musb_mailbox_work);
platform_device_unregister(glue->musb);
+ pm_runtime_put_sync(glue->dev);
+ pm_runtime_disable(glue->dev);
return 0;
}
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 22e8ecb6bfbd..c6904742e2aa 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -66,6 +66,7 @@ config AM335X_PHY_USB
select USB_PHY
select AM335X_CONTROL_USB
select NOP_USB_XCEIV
+ select USB_COMMON
help
This driver provides PHY support for that phy which part for the
AM335x SoC.
@@ -186,19 +187,6 @@ config USB_MXS_PHY
MXS Phy is used by some of the i.MX SoCs, for example imx23/28/6x.
-config USB_RCAR_PHY
- tristate "Renesas R-Car USB PHY support"
- depends on USB || USB_GADGET
- depends on ARCH_R8A7778 || ARCH_R8A7779 || COMPILE_TEST
- select USB_PHY
- help
- Say Y here to add support for the Renesas R-Car USB common PHY driver.
- This chip is typically used as USB PHY for USB host, gadget.
- This driver supports R8A7778 and R8A7779.
-
- To compile this driver as a module, choose M here: the
- module will be called phy-rcar-usb.
-
config USB_ULPI
bool "Generic ULPI Transceiver Driver"
depends on ARM || ARM64
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index 19c0dccbb116..b433e5d89be4 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -23,7 +23,6 @@ obj-$(CONFIG_USB_MSM_OTG) += phy-msm-usb.o
obj-$(CONFIG_USB_QCOM_8X16_PHY) += phy-qcom-8x16-usb.o
obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o
obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o
-obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o
obj-$(CONFIG_USB_ULPI) += phy-ulpi.o
obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o
obj-$(CONFIG_KEYSTONE_USB_PHY) += phy-keystone.o
diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c
index 7b3035ff9434..42a1afe36a90 100644
--- a/drivers/usb/phy/phy-am335x-control.c
+++ b/drivers/usb/phy/phy-am335x-control.c
@@ -4,7 +4,8 @@
#include <linux/of.h>
#include <linux/io.h>
#include <linux/delay.h>
-#include "am35x-phy-control.h"
+#include <linux/usb/otg.h>
+#include "phy-am335x-control.h"
struct am335x_control_usb {
struct device *dev;
@@ -58,7 +59,8 @@ static void am335x_phy_wkup(struct phy_control *phy_ctrl, u32 id, bool on)
spin_unlock(&usb_ctrl->lock);
}
-static void am335x_phy_power(struct phy_control *phy_ctrl, u32 id, bool on)
+static void am335x_phy_power(struct phy_control *phy_ctrl, u32 id,
+ enum usb_dr_mode dr_mode, bool on)
{
struct am335x_control_usb *usb_ctrl;
u32 val;
@@ -80,8 +82,14 @@ static void am335x_phy_power(struct phy_control *phy_ctrl, u32 id, bool on)
val = readl(usb_ctrl->phy_reg + reg);
if (on) {
- val &= ~(USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN);
- val |= USBPHY_OTGVDET_EN | USBPHY_OTGSESSEND_EN;
+ if (dr_mode == USB_DR_MODE_HOST) {
+ val &= ~(USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN |
+ USBPHY_OTGVDET_EN);
+ val |= USBPHY_OTGSESSEND_EN;
+ } else {
+ val &= ~(USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN);
+ val |= USBPHY_OTGVDET_EN | USBPHY_OTGSESSEND_EN;
+ }
} else {
val |= USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN;
}
diff --git a/drivers/usb/phy/am35x-phy-control.h b/drivers/usb/phy/phy-am335x-control.h
index b96594d1962c..e86b3165d69d 100644
--- a/drivers/usb/phy/am35x-phy-control.h
+++ b/drivers/usb/phy/phy-am335x-control.h
@@ -2,13 +2,15 @@
#define _AM335x_PHY_CONTROL_H_
struct phy_control {
- void (*phy_power)(struct phy_control *phy_ctrl, u32 id, bool on);
+ void (*phy_power)(struct phy_control *phy_ctrl, u32 id,
+ enum usb_dr_mode dr_mode, bool on);
void (*phy_wkup)(struct phy_control *phy_ctrl, u32 id, bool on);
};
-static inline void phy_ctrl_power(struct phy_control *phy_ctrl, u32 id, bool on)
+static inline void phy_ctrl_power(struct phy_control *phy_ctrl, u32 id,
+ enum usb_dr_mode dr_mode, bool on)
{
- phy_ctrl->phy_power(phy_ctrl, id, on);
+ phy_ctrl->phy_power(phy_ctrl, id, dr_mode, on);
}
static inline void phy_ctrl_wkup(struct phy_control *phy_ctrl, u32 id, bool on)
diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c
index 90b67a4ca221..39b424f7f629 100644
--- a/drivers/usb/phy/phy-am335x.c
+++ b/drivers/usb/phy/phy-am335x.c
@@ -8,21 +8,23 @@
#include <linux/regulator/consumer.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/usb/of.h>
-#include "am35x-phy-control.h"
+#include "phy-am335x-control.h"
#include "phy-generic.h"
struct am335x_phy {
struct usb_phy_generic usb_phy_gen;
struct phy_control *phy_ctrl;
int id;
+ enum usb_dr_mode dr_mode;
};
static int am335x_init(struct usb_phy *phy)
{
struct am335x_phy *am_phy = dev_get_drvdata(phy->dev);
- phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, true);
+ phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, true);
return 0;
}
@@ -30,7 +32,7 @@ static void am335x_shutdown(struct usb_phy *phy)
{
struct am335x_phy *am_phy = dev_get_drvdata(phy->dev);
- phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, false);
+ phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, false);
}
static int am335x_phy_probe(struct platform_device *pdev)
@@ -46,12 +48,15 @@ static int am335x_phy_probe(struct platform_device *pdev)
am_phy->phy_ctrl = am335x_get_phy_control(dev);
if (!am_phy->phy_ctrl)
return -EPROBE_DEFER;
+
am_phy->id = of_alias_get_id(pdev->dev.of_node, "phy");
if (am_phy->id < 0) {
dev_err(&pdev->dev, "Missing PHY id: %d\n", am_phy->id);
return am_phy->id;
}
+ am_phy->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node);
+
ret = usb_phy_gen_create_phy(dev, &am_phy->usb_phy_gen, NULL);
if (ret)
return ret;
@@ -75,7 +80,7 @@ static int am335x_phy_probe(struct platform_device *pdev)
*/
device_set_wakeup_enable(dev, false);
- phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, false);
+ phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, false);
return 0;
}
@@ -105,7 +110,7 @@ static int am335x_phy_suspend(struct device *dev)
if (device_may_wakeup(dev))
phy_ctrl_wkup(am_phy->phy_ctrl, am_phy->id, true);
- phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, false);
+ phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, false);
return 0;
}
@@ -115,7 +120,7 @@ static int am335x_phy_resume(struct device *dev)
struct platform_device *pdev = to_platform_device(dev);
struct am335x_phy *am_phy = platform_get_drvdata(pdev);
- phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, true);
+ phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, true);
if (device_may_wakeup(dev))
phy_ctrl_wkup(am_phy->phy_ctrl, am_phy->id, false);
diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index 80eb991c2506..0d19a6d61a71 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -1506,7 +1506,6 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
{
struct msm_otg_platform_data *pdata;
struct extcon_dev *ext_id, *ext_vbus;
- const struct of_device_id *id;
struct device_node *node = pdev->dev.of_node;
struct property *prop;
int len, ret, words;
@@ -1518,8 +1517,9 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
motg->pdata = pdata;
- id = of_match_device(msm_otg_dt_match, &pdev->dev);
- pdata->phy_type = (enum msm_usb_phy_type) id->data;
+ pdata->phy_type = (enum msm_usb_phy_type)of_device_get_match_data(&pdev->dev);
+ if (!pdata->phy_type)
+ return 1;
motg->link_rst = devm_reset_control_get(&pdev->dev, "link");
if (IS_ERR(motg->link_rst))
diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c
index b7536af777ab..c2936dc48ca7 100644
--- a/drivers/usb/phy/phy-mxs-usb.c
+++ b/drivers/usb/phy/phy-mxs-usb.c
@@ -143,12 +143,17 @@ static const struct mxs_phy_data imx6sx_phy_data = {
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
};
+static const struct mxs_phy_data imx6ul_phy_data = {
+ .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
+};
+
static const struct of_device_id mxs_phy_dt_ids[] = {
{ .compatible = "fsl,imx6sx-usbphy", .data = &imx6sx_phy_data, },
{ .compatible = "fsl,imx6sl-usbphy", .data = &imx6sl_phy_data, },
{ .compatible = "fsl,imx6q-usbphy", .data = &imx6q_phy_data, },
{ .compatible = "fsl,imx23-usbphy", .data = &imx23_phy_data, },
{ .compatible = "fsl,vf610-usbphy", .data = &vf610_phy_data, },
+ { .compatible = "fsl,imx6ul-usbphy", .data = &imx6ul_phy_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids);
diff --git a/drivers/usb/phy/phy-rcar-usb.c b/drivers/usb/phy/phy-rcar-usb.c
deleted file mode 100644
index 1e09b8377885..000000000000
--- a/drivers/usb/phy/phy-rcar-usb.c
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Renesas R-Car USB phy driver
- *
- * Copyright (C) 2012-2013 Renesas Solutions Corp.
- * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
- * Copyright (C) 2013 Cogent Embedded, 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.
- */
-
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/usb/otg.h>
-#include <linux/platform_device.h>
-#include <linux/spinlock.h>
-#include <linux/module.h>
-#include <linux/platform_data/usb-rcar-phy.h>
-
-/* REGS block */
-#define USBPCTRL0 0x00
-#define USBPCTRL1 0x04
-#define USBST 0x08
-#define USBEH0 0x0C
-#define USBOH0 0x1C
-#define USBCTL0 0x58
-
-/* High-speed signal quality characteristic control registers (R8A7778 only) */
-#define HSQCTL1 0x24
-#define HSQCTL2 0x28
-
-/* USBPCTRL0 */
-#define OVC2 (1 << 10) /* (R8A7779 only) */
- /* Switches the OVC input pin for port 2: */
- /* 1: USB_OVC2, 0: OVC2 */
-#define OVC1_VBUS1 (1 << 9) /* Switches the OVC input pin for port 1: */
- /* 1: USB_OVC1, 0: OVC1/VBUS1 */
- /* Function mode: set to 0 */
-#define OVC0 (1 << 8) /* Switches the OVC input pin for port 0: */
- /* 1: USB_OVC0 pin, 0: OVC0 */
-#define OVC2_ACT (1 << 6) /* (R8A7779 only) */
- /* Host mode: OVC2 polarity: */
- /* 1: active-high, 0: active-low */
-#define PENC (1 << 4) /* Function mode: output level of PENC1 pin: */
- /* 1: high, 0: low */
-#define OVC0_ACT (1 << 3) /* Host mode: OVC0 polarity: */
- /* 1: active-high, 0: active-low */
-#define OVC1_ACT (1 << 1) /* Host mode: OVC1 polarity: */
- /* 1: active-high, 0: active-low */
- /* Function mode: be sure to set to 1 */
-#define PORT1 (1 << 0) /* Selects port 1 mode: */
- /* 1: function, 0: host */
-/* USBPCTRL1 */
-#define PHY_RST (1 << 2)
-#define PLL_ENB (1 << 1)
-#define PHY_ENB (1 << 0)
-
-/* USBST */
-#define ST_ACT (1 << 31)
-#define ST_PLL (1 << 30)
-
-struct rcar_usb_phy_priv {
- struct usb_phy phy;
- spinlock_t lock;
-
- void __iomem *reg0;
- void __iomem *reg1;
- int counter;
-};
-
-#define usb_phy_to_priv(p) container_of(p, struct rcar_usb_phy_priv, phy)
-
-
-/*
- * USB initial/install operation.
- *
- * This function setup USB phy.
- * The used value and setting order came from
- * [USB :: Initial setting] on datasheet.
- */
-static int rcar_usb_phy_init(struct usb_phy *phy)
-{
- struct rcar_usb_phy_priv *priv = usb_phy_to_priv(phy);
- struct device *dev = phy->dev;
- struct rcar_phy_platform_data *pdata = dev_get_platdata(dev);
- void __iomem *reg0 = priv->reg0;
- void __iomem *reg1 = priv->reg1;
- static const u8 ovcn_act[] = { OVC0_ACT, OVC1_ACT, OVC2_ACT };
- int i;
- u32 val;
- unsigned long flags;
-
- spin_lock_irqsave(&priv->lock, flags);
- if (priv->counter++ == 0) {
-
- /*
- * USB phy start-up
- */
-
- /* (1) USB-PHY standby release */
- iowrite32(PHY_ENB, (reg0 + USBPCTRL1));
-
- /* (2) start USB-PHY internal PLL */
- iowrite32(PHY_ENB | PLL_ENB, (reg0 + USBPCTRL1));
-
- /* (3) set USB-PHY in accord with the conditions of usage */
- if (reg1) {
- u32 hsqctl1 = pdata->ferrite_bead ? 0x41 : 0;
- u32 hsqctl2 = pdata->ferrite_bead ? 0x0d : 7;
-
- iowrite32(hsqctl1, reg1 + HSQCTL1);
- iowrite32(hsqctl2, reg1 + HSQCTL2);
- }
-
- /* (4) USB module status check */
- for (i = 0; i < 1024; i++) {
- udelay(10);
- val = ioread32(reg0 + USBST);
- if (val == (ST_ACT | ST_PLL))
- break;
- }
-
- if (val != (ST_ACT | ST_PLL)) {
- dev_err(dev, "USB phy not ready\n");
- goto phy_init_end;
- }
-
- /* (5) USB-PHY reset clear */
- iowrite32(PHY_ENB | PLL_ENB | PHY_RST, (reg0 + USBPCTRL1));
-
- /* Board specific port settings */
- val = 0;
- if (pdata->port1_func)
- val |= PORT1;
- if (pdata->penc1)
- val |= PENC;
- for (i = 0; i < 3; i++) {
- /* OVCn bits follow each other in the right order */
- if (pdata->ovc_pin[i].select_3_3v)
- val |= OVC0 << i;
- /* OVCn_ACT bits are spaced by irregular intervals */
- if (pdata->ovc_pin[i].active_high)
- val |= ovcn_act[i];
- }
- iowrite32(val, (reg0 + USBPCTRL0));
-
- /*
- * Bus alignment settings
- */
-
- /* (1) EHCI bus alignment (little endian) */
- iowrite32(0x00000000, (reg0 + USBEH0));
-
- /* (1) OHCI bus alignment (little endian) */
- iowrite32(0x00000000, (reg0 + USBOH0));
- }
-
-phy_init_end:
- spin_unlock_irqrestore(&priv->lock, flags);
-
- return 0;
-}
-
-static void rcar_usb_phy_shutdown(struct usb_phy *phy)
-{
- struct rcar_usb_phy_priv *priv = usb_phy_to_priv(phy);
- void __iomem *reg0 = priv->reg0;
- unsigned long flags;
-
- spin_lock_irqsave(&priv->lock, flags);
-
- if (priv->counter-- == 1) /* last user */
- iowrite32(0x00000000, (reg0 + USBPCTRL1));
-
- spin_unlock_irqrestore(&priv->lock, flags);
-}
-
-static int rcar_usb_phy_probe(struct platform_device *pdev)
-{
- struct rcar_usb_phy_priv *priv;
- struct resource *res0, *res1;
- struct device *dev = &pdev->dev;
- void __iomem *reg0, *reg1 = NULL;
- int ret;
-
- if (!dev_get_platdata(&pdev->dev)) {
- dev_err(dev, "No platform data\n");
- return -EINVAL;
- }
-
- res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- reg0 = devm_ioremap_resource(dev, res0);
- if (IS_ERR(reg0))
- return PTR_ERR(reg0);
-
- res1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- reg1 = devm_ioremap_resource(dev, res1);
- if (IS_ERR(reg1))
- return PTR_ERR(reg1);
-
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->reg0 = reg0;
- priv->reg1 = reg1;
- priv->counter = 0;
- priv->phy.dev = dev;
- priv->phy.label = dev_name(dev);
- priv->phy.init = rcar_usb_phy_init;
- priv->phy.shutdown = rcar_usb_phy_shutdown;
- spin_lock_init(&priv->lock);
-
- ret = usb_add_phy(&priv->phy, USB_PHY_TYPE_USB2);
- if (ret < 0) {
- dev_err(dev, "usb phy addition error\n");
- return ret;
- }
-
- platform_set_drvdata(pdev, priv);
-
- return ret;
-}
-
-static int rcar_usb_phy_remove(struct platform_device *pdev)
-{
- struct rcar_usb_phy_priv *priv = platform_get_drvdata(pdev);
-
- usb_remove_phy(&priv->phy);
-
- return 0;
-}
-
-static struct platform_driver rcar_usb_phy_driver = {
- .driver = {
- .name = "rcar_usb_phy",
- },
- .probe = rcar_usb_phy_probe,
- .remove = rcar_usb_phy_remove,
-};
-
-module_platform_driver(rcar_usb_phy_driver);
-
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("Renesas R-Car USB phy");
-MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c
index 12741856a75c..014dbbd72132 100644
--- a/drivers/usb/phy/phy-twl6030-usb.c
+++ b/drivers/usb/phy/phy-twl6030-usb.c
@@ -25,7 +25,7 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
-#include <linux/usb/musb-omap.h>
+#include <linux/usb/musb.h>
#include <linux/usb/phy_companion.h>
#include <linux/phy/omap_usb.h>
#include <linux/i2c/twl.h>
@@ -102,7 +102,7 @@ struct twl6030_usb {
int irq1;
int irq2;
- enum omap_musb_vbus_id_status linkstat;
+ enum musb_vbus_id_status linkstat;
u8 asleep;
bool vbus_enable;
const char *regulator;
@@ -189,13 +189,13 @@ static ssize_t twl6030_usb_vbus_show(struct device *dev,
spin_lock_irqsave(&twl->lock, flags);
switch (twl->linkstat) {
- case OMAP_MUSB_VBUS_VALID:
+ case MUSB_VBUS_VALID:
ret = snprintf(buf, PAGE_SIZE, "vbus\n");
break;
- case OMAP_MUSB_ID_GROUND:
+ case MUSB_ID_GROUND:
ret = snprintf(buf, PAGE_SIZE, "id\n");
break;
- case OMAP_MUSB_VBUS_OFF:
+ case MUSB_VBUS_OFF:
ret = snprintf(buf, PAGE_SIZE, "none\n");
break;
default:
@@ -210,7 +210,7 @@ static DEVICE_ATTR(vbus, 0444, twl6030_usb_vbus_show, NULL);
static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
{
struct twl6030_usb *twl = _twl;
- enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN;
+ enum musb_vbus_id_status status = MUSB_UNKNOWN;
u8 vbus_state, hw_state;
int ret;
@@ -225,14 +225,14 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
dev_err(twl->dev, "Failed to enable usb3v3\n");
twl->asleep = 1;
- status = OMAP_MUSB_VBUS_VALID;
+ status = MUSB_VBUS_VALID;
twl->linkstat = status;
- omap_musb_mailbox(status);
+ musb_mailbox(status);
} else {
- if (twl->linkstat != OMAP_MUSB_UNKNOWN) {
- status = OMAP_MUSB_VBUS_OFF;
+ if (twl->linkstat != MUSB_UNKNOWN) {
+ status = MUSB_VBUS_OFF;
twl->linkstat = status;
- omap_musb_mailbox(status);
+ musb_mailbox(status);
if (twl->asleep) {
regulator_disable(twl->usb3v3);
twl->asleep = 0;
@@ -248,7 +248,7 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl)
{
struct twl6030_usb *twl = _twl;
- enum omap_musb_vbus_id_status status = OMAP_MUSB_UNKNOWN;
+ enum musb_vbus_id_status status = MUSB_UNKNOWN;
u8 hw_state;
int ret;
@@ -262,9 +262,9 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl)
twl->asleep = 1;
twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_CLR);
twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_SET);
- status = OMAP_MUSB_ID_GROUND;
+ status = MUSB_ID_GROUND;
twl->linkstat = status;
- omap_musb_mailbox(status);
+ musb_mailbox(status);
} else {
twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_CLR);
twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET);
@@ -334,7 +334,7 @@ static int twl6030_usb_probe(struct platform_device *pdev)
twl->dev = &pdev->dev;
twl->irq1 = platform_get_irq(pdev, 0);
twl->irq2 = platform_get_irq(pdev, 1);
- twl->linkstat = OMAP_MUSB_UNKNOWN;
+ twl->linkstat = MUSB_UNKNOWN;
twl->comparator.set_vbus = twl6030_set_vbus;
twl->comparator.start_srp = twl6030_start_srp;
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index d82fa36c3465..5af9ca5d54ab 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -302,37 +302,37 @@ static void usbhsc_set_buswait(struct usbhs_priv *priv)
*/
/* commonly used on old SH-Mobile SoCs */
-static u32 usbhsc_default_pipe_type[] = {
- USB_ENDPOINT_XFER_CONTROL,
- USB_ENDPOINT_XFER_ISOC,
- USB_ENDPOINT_XFER_ISOC,
- USB_ENDPOINT_XFER_BULK,
- USB_ENDPOINT_XFER_BULK,
- USB_ENDPOINT_XFER_BULK,
- USB_ENDPOINT_XFER_INT,
- USB_ENDPOINT_XFER_INT,
- USB_ENDPOINT_XFER_INT,
- USB_ENDPOINT_XFER_INT,
+static struct renesas_usbhs_driver_pipe_config usbhsc_default_pipe[] = {
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_CONTROL, 64, 0x00, false),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x08, false),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x18, false),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x28, true),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x38, true),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x48, true),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x04, false),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x05, false),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x06, false),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x07, false),
};
/* commonly used on newer SH-Mobile and R-Car SoCs */
-static u32 usbhsc_new_pipe_type[] = {
- USB_ENDPOINT_XFER_CONTROL,
- USB_ENDPOINT_XFER_ISOC,
- USB_ENDPOINT_XFER_ISOC,
- USB_ENDPOINT_XFER_BULK,
- USB_ENDPOINT_XFER_BULK,
- USB_ENDPOINT_XFER_BULK,
- USB_ENDPOINT_XFER_INT,
- USB_ENDPOINT_XFER_INT,
- USB_ENDPOINT_XFER_INT,
- USB_ENDPOINT_XFER_BULK,
- USB_ENDPOINT_XFER_BULK,
- USB_ENDPOINT_XFER_BULK,
- USB_ENDPOINT_XFER_BULK,
- USB_ENDPOINT_XFER_BULK,
- USB_ENDPOINT_XFER_BULK,
- USB_ENDPOINT_XFER_BULK,
+static struct renesas_usbhs_driver_pipe_config usbhsc_new_pipe[] = {
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_CONTROL, 64, 0x00, false),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x08, true),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x28, true),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x48, true),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x58, true),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x68, true),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x04, false),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x05, false),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x06, false),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x78, true),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x88, true),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x98, true),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xa8, true),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xb8, true),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xc8, true),
+ RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xd8, true),
};
/*
@@ -481,6 +481,15 @@ static const struct of_device_id usbhs_of_match[] = {
.compatible = "renesas,usbhs-r8a7795",
.data = (void *)USBHS_TYPE_RCAR_GEN2,
},
+ {
+ .compatible = "renesas,rcar-gen2-usbhs",
+ .data = (void *)USBHS_TYPE_RCAR_GEN2,
+ },
+ {
+ /* Gen3 is compatible with Gen2 */
+ .compatible = "renesas,rcar-gen3-usbhs",
+ .data = (void *)USBHS_TYPE_RCAR_GEN2,
+ },
{ },
};
MODULE_DEVICE_TABLE(of, usbhs_of_match);
@@ -564,10 +573,9 @@ static int usbhs_probe(struct platform_device *pdev)
switch (priv->dparam.type) {
case USBHS_TYPE_RCAR_GEN2:
priv->pfunc = usbhs_rcar2_ops;
- if (!priv->dparam.pipe_type) {
- priv->dparam.pipe_type = usbhsc_new_pipe_type;
- priv->dparam.pipe_size =
- ARRAY_SIZE(usbhsc_new_pipe_type);
+ if (!priv->dparam.pipe_configs) {
+ priv->dparam.pipe_configs = usbhsc_new_pipe;
+ priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_new_pipe);
}
break;
default:
@@ -586,9 +594,9 @@ static int usbhs_probe(struct platform_device *pdev)
dfunc->notify_hotplug = usbhsc_drvcllbck_notify_hotplug;
/* set default param if platform doesn't have */
- if (!priv->dparam.pipe_type) {
- priv->dparam.pipe_type = usbhsc_default_pipe_type;
- priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type);
+ if (!priv->dparam.pipe_configs) {
+ priv->dparam.pipe_configs = usbhsc_default_pipe;
+ priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_default_pipe);
}
if (!priv->dparam.pio_dma_border)
priv->dparam.pio_dma_border = 64; /* 64byte */
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index de4f97d84a82..657f9672ceba 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -131,7 +131,8 @@ static void __usbhsg_queue_pop(struct usbhsg_uep *uep,
struct device *dev = usbhsg_gpriv_to_dev(gpriv);
struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
- dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe));
+ if (pipe)
+ dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe));
ureq->req.status = status;
spin_unlock(usbhs_priv_to_lock(priv));
@@ -685,7 +686,13 @@ static int usbhsg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
struct usbhsg_request *ureq = usbhsg_req_to_ureq(req);
struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
- usbhs_pkt_pop(pipe, usbhsg_ureq_to_pkt(ureq));
+ if (pipe)
+ usbhs_pkt_pop(pipe, usbhsg_ureq_to_pkt(ureq));
+
+ /*
+ * To dequeue a request, this driver should call the usbhsg_queue_pop()
+ * even if the pipe is NULL.
+ */
usbhsg_queue_pop(uep, ureq, -ECONNRESET);
return 0;
@@ -1035,6 +1042,8 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
struct usbhsg_gpriv *gpriv;
struct usbhsg_uep *uep;
struct device *dev = usbhs_priv_to_dev(priv);
+ struct renesas_usbhs_driver_pipe_config *pipe_configs =
+ usbhs_get_dparam(priv, pipe_configs);
int pipe_size = usbhs_get_dparam(priv, pipe_size);
int i;
int ret;
@@ -1104,13 +1113,16 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
gpriv->gadget.ep0 = &uep->ep;
usb_ep_set_maxpacket_limit(&uep->ep, 64);
uep->ep.caps.type_control = true;
- }
- /* init normal pipe */
- else {
- usb_ep_set_maxpacket_limit(&uep->ep, 512);
- uep->ep.caps.type_iso = true;
- uep->ep.caps.type_bulk = true;
- uep->ep.caps.type_int = true;
+ } else {
+ /* init normal pipe */
+ if (pipe_configs[i].type == USB_ENDPOINT_XFER_ISOC)
+ uep->ep.caps.type_iso = true;
+ if (pipe_configs[i].type == USB_ENDPOINT_XFER_BULK)
+ uep->ep.caps.type_bulk = true;
+ if (pipe_configs[i].type == USB_ENDPOINT_XFER_INT)
+ uep->ep.caps.type_int = true;
+ usb_ep_set_maxpacket_limit(&uep->ep,
+ pipe_configs[i].bufsize);
list_add_tail(&uep->ep.ep_list, &gpriv->gadget.ep_list);
}
uep->ep.caps.dir_in = true;
diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c
index bd050359926c..1a8e4c45c4c5 100644
--- a/drivers/usb/renesas_usbhs/mod_host.c
+++ b/drivers/usb/renesas_usbhs/mod_host.c
@@ -1414,7 +1414,8 @@ static void usbhsh_pipe_init_for_host(struct usbhs_priv *priv)
{
struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
struct usbhs_pipe *pipe;
- u32 *pipe_type = usbhs_get_dparam(priv, pipe_type);
+ struct renesas_usbhs_driver_pipe_config *pipe_configs =
+ usbhs_get_dparam(priv, pipe_configs);
int pipe_size = usbhs_get_dparam(priv, pipe_size);
int old_type, dir_in, i;
@@ -1442,15 +1443,15 @@ static void usbhsh_pipe_init_for_host(struct usbhs_priv *priv)
* USB_ENDPOINT_XFER_BULK -> dir in
* ...
*/
- dir_in = (pipe_type[i] == old_type);
- old_type = pipe_type[i];
+ dir_in = (pipe_configs[i].type == old_type);
+ old_type = pipe_configs[i].type;
- if (USB_ENDPOINT_XFER_CONTROL == pipe_type[i]) {
+ if (USB_ENDPOINT_XFER_CONTROL == pipe_configs[i].type) {
pipe = usbhs_dcp_malloc(priv);
usbhsh_hpriv_to_dcp(hpriv) = pipe;
} else {
pipe = usbhs_pipe_malloc(priv,
- pipe_type[i],
+ pipe_configs[i].type,
dir_in);
}
diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c
index 4f9c3356127a..0e95d2925dc5 100644
--- a/drivers/usb/renesas_usbhs/pipe.c
+++ b/drivers/usb/renesas_usbhs/pipe.c
@@ -44,6 +44,15 @@ char *usbhs_pipe_name(struct usbhs_pipe *pipe)
return usbhsp_pipe_name[usbhs_pipe_type(pipe)];
}
+static struct renesas_usbhs_driver_pipe_config
+*usbhsp_get_pipe_config(struct usbhs_priv *priv, int pipe_num)
+{
+ struct renesas_usbhs_driver_pipe_config *pipe_configs =
+ usbhs_get_dparam(priv, pipe_configs);
+
+ return &pipe_configs[pipe_num];
+}
+
/*
* DCPCTR/PIPEnCTR functions
*/
@@ -384,18 +393,6 @@ void usbhs_pipe_set_trans_count_if_bulk(struct usbhs_pipe *pipe, int len)
/*
* pipe setup
*/
-static int usbhsp_possible_double_buffer(struct usbhs_pipe *pipe)
-{
- /*
- * only ISO / BULK pipe can use double buffer
- */
- if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK) ||
- usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC))
- return 1;
-
- return 0;
-}
-
static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe,
int is_host,
int dir_in)
@@ -412,7 +409,6 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe,
[USB_ENDPOINT_XFER_INT] = TYPE_INT,
[USB_ENDPOINT_XFER_ISOC] = TYPE_ISO,
};
- int is_double = usbhsp_possible_double_buffer(pipe);
if (usbhs_pipe_is_dcp(pipe))
return -EINVAL;
@@ -434,10 +430,7 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe,
usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK))
bfre = 0; /* FIXME */
- /* DBLB */
- if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC) ||
- usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK))
- dblb = (is_double) ? DBLB : 0;
+ /* DBLB: see usbhs_pipe_config_update() */
/* CNTMD */
if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK))
@@ -473,13 +466,13 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe,
static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe)
{
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
- struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
struct device *dev = usbhs_priv_to_dev(priv);
int pipe_num = usbhs_pipe_number(pipe);
- int is_double = usbhsp_possible_double_buffer(pipe);
u16 buff_size;
u16 bufnmb;
u16 bufnmb_cnt;
+ struct renesas_usbhs_driver_pipe_config *pipe_config =
+ usbhsp_get_pipe_config(priv, pipe_num);
/*
* PIPEBUF
@@ -489,56 +482,13 @@ static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe)
* - "Features" - "Pipe configuration"
* - "Operation" - "FIFO Buffer Memory"
* - "Operation" - "Pipe Control"
- *
- * ex) if pipe6 - pipe9 are USB_ENDPOINT_XFER_INT (SH7724)
- *
- * BUFNMB: PIPE
- * 0: pipe0 (DCP 256byte)
- * 1: -
- * 2: -
- * 3: -
- * 4: pipe6 (INT 64byte)
- * 5: pipe7 (INT 64byte)
- * 6: pipe8 (INT 64byte)
- * 7: pipe9 (INT 64byte)
- * 8 - xx: free (for BULK, ISOC)
*/
-
- /*
- * FIXME
- *
- * it doesn't have good buffer allocator
- *
- * DCP : 256 byte
- * BULK: 512 byte
- * INT : 64 byte
- * ISOC: 512 byte
- */
- if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_CONTROL))
- buff_size = 256;
- else if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT))
- buff_size = 64;
- else
- buff_size = 512;
+ buff_size = pipe_config->bufsize;
+ bufnmb = pipe_config->bufnum;
/* change buff_size to register value */
bufnmb_cnt = (buff_size / 64) - 1;
- /* BUFNMB has been reserved for INT pipe
- * see above */
- if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT)) {
- bufnmb = pipe_num - 2;
- } else {
- bufnmb = info->bufnmb_last;
- info->bufnmb_last += bufnmb_cnt + 1;
-
- /*
- * double buffer
- */
- if (is_double)
- info->bufnmb_last += bufnmb_cnt + 1;
- }
-
dev_dbg(dev, "pipe : %d : buff_size 0x%x: bufnmb 0x%x\n",
pipe_num, buff_size, bufnmb);
@@ -549,8 +499,13 @@ static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe)
void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel,
u16 epnum, u16 maxp)
{
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+ int pipe_num = usbhs_pipe_number(pipe);
+ struct renesas_usbhs_driver_pipe_config *pipe_config =
+ usbhsp_get_pipe_config(priv, pipe_num);
+ u16 dblb = pipe_config->double_buf ? DBLB : 0;
+
if (devsel > 0xA) {
- struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
struct device *dev = usbhs_priv_to_dev(priv);
dev_err(dev, "devsel error %d\n", devsel);
@@ -568,7 +523,7 @@ void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel,
maxp);
if (!usbhs_pipe_is_dcp(pipe))
- usbhsp_pipe_cfg_set(pipe, 0x000F, epnum);
+ usbhsp_pipe_cfg_set(pipe, 0x000F | DBLB, epnum | dblb);
}
/*
@@ -708,23 +663,7 @@ void usbhs_pipe_init(struct usbhs_priv *priv,
struct usbhs_pipe *pipe;
int i;
- /*
- * FIXME
- *
- * driver needs good allocator.
- *
- * find first free buffer area (BULK, ISOC)
- * (DCP, INT area is fixed)
- *
- * buffer number 0 - 3 have been reserved for DCP
- * see
- * usbhsp_to_bufnmb
- */
- info->bufnmb_last = 4;
usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
- if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT))
- info->bufnmb_last++;
-
usbhsp_flags_init(pipe);
pipe->fifo = NULL;
pipe->mod_private = NULL;
@@ -851,12 +790,13 @@ int usbhs_pipe_probe(struct usbhs_priv *priv)
struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
struct usbhs_pipe *pipe;
struct device *dev = usbhs_priv_to_dev(priv);
- u32 *pipe_type = usbhs_get_dparam(priv, pipe_type);
+ struct renesas_usbhs_driver_pipe_config *pipe_configs =
+ usbhs_get_dparam(priv, pipe_configs);
int pipe_size = usbhs_get_dparam(priv, pipe_size);
int i;
/* This driver expects 1st pipe is DCP */
- if (pipe_type[0] != USB_ENDPOINT_XFER_CONTROL) {
+ if (pipe_configs[0].type != USB_ENDPOINT_XFER_CONTROL) {
dev_err(dev, "1st PIPE is not DCP\n");
return -EINVAL;
}
@@ -876,10 +816,10 @@ int usbhs_pipe_probe(struct usbhs_priv *priv)
pipe->priv = priv;
usbhs_pipe_type(pipe) =
- pipe_type[i] & USB_ENDPOINT_XFERTYPE_MASK;
+ pipe_configs[i].type & USB_ENDPOINT_XFERTYPE_MASK;
dev_dbg(dev, "pipe %x\t: %s\n",
- i, usbhsp_pipe_name[pipe_type[i]]);
+ i, usbhsp_pipe_name[pipe_configs[i].type]);
}
return 0;
diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h
index b0bc7b603016..3212ab51e844 100644
--- a/drivers/usb/renesas_usbhs/pipe.h
+++ b/drivers/usb/renesas_usbhs/pipe.h
@@ -46,7 +46,6 @@ struct usbhs_pipe {
struct usbhs_pipe_info {
struct usbhs_pipe *pipe;
int size; /* array size of "pipe" */
- int bufnmb_last; /* FIXME : driver needs good allocator */
int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map);
};
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index 56ecb8b5115d..f612dda9c977 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -475,6 +475,22 @@ config USB_SERIAL_MOS7840
To compile this driver as a module, choose M here: the
module will be called mos7840. If unsure, choose N.
+config USB_SERIAL_MXUPORT11
+ tristate "USB Moxa UPORT 11x0 Serial Driver"
+ ---help---
+ Say Y here if you want to use a MOXA UPort 11x0 Serial hub.
+
+ This driver supports:
+
+ - UPort 1110 : 1 port RS-232 USB to Serial Hub.
+ - UPort 1130 : 1 port RS-422/485 USB to Serial Hub.
+ - UPort 1130I : 1 port RS-422/485 USB to Serial Hub with Isolation.
+ - UPort 1150 : 1 port RS-232/422/485 USB to Serial Hub.
+ - UPort 1150I : 1 port RS-232/422/485 USB to Serial Hub with Isolation.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mxu11x0.
+
config USB_SERIAL_MXUPORT
tristate "USB Moxa UPORT Serial Driver"
---help---
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index 349d9df0895f..f3fa5e53702d 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_USB_SERIAL_METRO) += metro-usb.o
obj-$(CONFIG_USB_SERIAL_MOS7720) += mos7720.o
obj-$(CONFIG_USB_SERIAL_MOS7840) += mos7840.o
obj-$(CONFIG_USB_SERIAL_MXUPORT) += mxuport.o
+obj-$(CONFIG_USB_SERIAL_MXUPORT11) += mxu11x0.o
obj-$(CONFIG_USB_SERIAL_NAVMAN) += navman.o
obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o
obj-$(CONFIG_USB_SERIAL_OPTICON) += opticon.o
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index eac7ccaa3c85..9b90ad747d87 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -38,13 +38,14 @@ static void cp210x_change_speed(struct tty_struct *, struct usb_serial_port *,
struct ktermios *);
static void cp210x_set_termios(struct tty_struct *, struct usb_serial_port *,
struct ktermios*);
+static bool cp210x_tx_empty(struct usb_serial_port *port);
static int cp210x_tiocmget(struct tty_struct *);
static int cp210x_tiocmset(struct tty_struct *, unsigned int, unsigned int);
static int cp210x_tiocmset_port(struct usb_serial_port *port,
unsigned int, unsigned int);
static void cp210x_break_ctl(struct tty_struct *, int);
-static int cp210x_startup(struct usb_serial *);
-static void cp210x_release(struct usb_serial *);
+static int cp210x_port_probe(struct usb_serial_port *);
+static int cp210x_port_remove(struct usb_serial_port *);
static void cp210x_dtr_rts(struct usb_serial_port *p, int on);
static const struct usb_device_id id_table[] = {
@@ -132,7 +133,6 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
{ USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */
{ USB_DEVICE(0x10C4, 0xEA70) }, /* Silicon Labs factory default */
- { USB_DEVICE(0x10C4, 0xEA80) }, /* Silicon Labs factory default */
{ USB_DEVICE(0x10C4, 0xEA71) }, /* Infinity GPS-MIC-1 Radio Monophone */
{ USB_DEVICE(0x10C4, 0xF001) }, /* Elan Digital Systems USBscope50 */
{ USB_DEVICE(0x10C4, 0xF002) }, /* Elan Digital Systems USBwave12 */
@@ -161,6 +161,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x17F4, 0xAAAA) }, /* Wavesense Jazz blood glucose meter */
{ USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */
{ USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */
+ { USB_DEVICE(0x18EF, 0xE025) }, /* ELV Marble Sound Board 1 */
{ USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */
{ USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */
{ USB_DEVICE(0x1BA4, 0x0002) }, /* Silicon Labs 358x factory default */
@@ -197,8 +198,9 @@ static const struct usb_device_id id_table[] = {
MODULE_DEVICE_TABLE(usb, id_table);
-struct cp210x_serial_private {
+struct cp210x_port_private {
__u8 bInterfaceNumber;
+ bool has_swapped_line_ctl;
};
static struct usb_serial_driver cp210x_device = {
@@ -214,10 +216,11 @@ static struct usb_serial_driver cp210x_device = {
.close = cp210x_close,
.break_ctl = cp210x_break_ctl,
.set_termios = cp210x_set_termios,
+ .tx_empty = cp210x_tx_empty,
.tiocmget = cp210x_tiocmget,
.tiocmset = cp210x_tiocmset,
- .attach = cp210x_startup,
- .release = cp210x_release,
+ .port_probe = cp210x_port_probe,
+ .port_remove = cp210x_port_remove,
.dtr_rts = cp210x_dtr_rts
};
@@ -300,6 +303,25 @@ static struct usb_serial_driver * const serial_drivers[] = {
#define CONTROL_WRITE_DTR 0x0100
#define CONTROL_WRITE_RTS 0x0200
+/* CP210X_GET_COMM_STATUS returns these 0x13 bytes */
+struct cp210x_comm_status {
+ __le32 ulErrors;
+ __le32 ulHoldReasons;
+ __le32 ulAmountInInQueue;
+ __le32 ulAmountInOutQueue;
+ u8 bEofReceived;
+ u8 bWaitForImmediate;
+ u8 bReserved;
+} __packed;
+
+/*
+ * CP210X_PURGE - 16 bits passed in wValue of USB request.
+ * SiLabs app note AN571 gives a strange description of the 4 bits:
+ * bit 0 or bit 2 clears the transmit queue and 1 or 3 receive.
+ * writing 1 to all, however, purges cp2108 well enough to avoid the hang.
+ */
+#define PURGE_ALL 0x000f
+
/*
* cp210x_get_config
* Reads from the CP210x configuration registers
@@ -311,7 +333,7 @@ static int cp210x_get_config(struct usb_serial_port *port, u8 request,
unsigned int *data, int size)
{
struct usb_serial *serial = port->serial;
- struct cp210x_serial_private *spriv = usb_get_serial_data(serial);
+ struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
__le32 *buf;
int result, i, length;
@@ -325,7 +347,7 @@ static int cp210x_get_config(struct usb_serial_port *port, u8 request,
/* Issue the request, attempting to read 'size' bytes */
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
request, REQTYPE_INTERFACE_TO_HOST, 0x0000,
- spriv->bInterfaceNumber, buf, size,
+ port_priv->bInterfaceNumber, buf, size,
USB_CTRL_GET_TIMEOUT);
/* Convert data into an array of integers */
@@ -356,7 +378,7 @@ static int cp210x_set_config(struct usb_serial_port *port, u8 request,
unsigned int *data, int size)
{
struct usb_serial *serial = port->serial;
- struct cp210x_serial_private *spriv = usb_get_serial_data(serial);
+ struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
__le32 *buf;
int result, i, length;
@@ -375,13 +397,13 @@ static int cp210x_set_config(struct usb_serial_port *port, u8 request,
result = usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0),
request, REQTYPE_HOST_TO_INTERFACE, 0x0000,
- spriv->bInterfaceNumber, buf, size,
+ port_priv->bInterfaceNumber, buf, size,
USB_CTRL_SET_TIMEOUT);
} else {
result = usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0),
request, REQTYPE_HOST_TO_INTERFACE, data[0],
- spriv->bInterfaceNumber, NULL, 0,
+ port_priv->bInterfaceNumber, NULL, 0,
USB_CTRL_SET_TIMEOUT);
}
@@ -411,6 +433,60 @@ static inline int cp210x_set_config_single(struct usb_serial_port *port,
}
/*
+ * Detect CP2108 GET_LINE_CTL bug and activate workaround.
+ * Write a known good value 0x800, read it back.
+ * If it comes back swapped the bug is detected.
+ * Preserve the original register value.
+ */
+static int cp210x_detect_swapped_line_ctl(struct usb_serial_port *port)
+{
+ struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
+ unsigned int line_ctl_save;
+ unsigned int line_ctl_test;
+ int err;
+
+ err = cp210x_get_config(port, CP210X_GET_LINE_CTL, &line_ctl_save, 2);
+ if (err)
+ return err;
+
+ line_ctl_test = 0x800;
+ err = cp210x_set_config(port, CP210X_SET_LINE_CTL, &line_ctl_test, 2);
+ if (err)
+ return err;
+
+ err = cp210x_get_config(port, CP210X_GET_LINE_CTL, &line_ctl_test, 2);
+ if (err)
+ return err;
+
+ if (line_ctl_test == 8) {
+ port_priv->has_swapped_line_ctl = true;
+ line_ctl_save = swab16((u16)line_ctl_save);
+ }
+
+ return cp210x_set_config(port, CP210X_SET_LINE_CTL, &line_ctl_save, 2);
+}
+
+/*
+ * Must always be called instead of cp210x_get_config(CP210X_GET_LINE_CTL)
+ * to workaround cp2108 bug and get correct value.
+ */
+static int cp210x_get_line_ctl(struct usb_serial_port *port, unsigned int *ctl)
+{
+ struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
+ int err;
+
+ err = cp210x_get_config(port, CP210X_GET_LINE_CTL, ctl, 2);
+ if (err)
+ return err;
+
+ /* Workaround swapped bytes in 16-bit value from CP210X_GET_LINE_CTL */
+ if (port_priv->has_swapped_line_ctl)
+ *ctl = swab16((u16)(*ctl));
+
+ return 0;
+}
+
+/*
* cp210x_quantise_baudrate
* Quantises the baud rate as per AN205 Table 1
*/
@@ -475,11 +551,63 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port)
static void cp210x_close(struct usb_serial_port *port)
{
+ unsigned int purge_ctl;
+
usb_serial_generic_close(port);
+
+ /* Clear both queues; cp2108 needs this to avoid an occasional hang */
+ purge_ctl = PURGE_ALL;
+ cp210x_set_config(port, CP210X_PURGE, &purge_ctl, 2);
+
cp210x_set_config_single(port, CP210X_IFC_ENABLE, UART_DISABLE);
}
/*
+ * Read how many bytes are waiting in the TX queue.
+ */
+static int cp210x_get_tx_queue_byte_count(struct usb_serial_port *port,
+ u32 *count)
+{
+ struct usb_serial *serial = port->serial;
+ struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
+ struct cp210x_comm_status *sts;
+ int result;
+
+ sts = kmalloc(sizeof(*sts), GFP_KERNEL);
+ if (!sts)
+ return -ENOMEM;
+
+ result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+ CP210X_GET_COMM_STATUS, REQTYPE_INTERFACE_TO_HOST,
+ 0, port_priv->bInterfaceNumber, sts, sizeof(*sts),
+ USB_CTRL_GET_TIMEOUT);
+ if (result == sizeof(*sts)) {
+ *count = le32_to_cpu(sts->ulAmountInOutQueue);
+ result = 0;
+ } else {
+ dev_err(&port->dev, "failed to get comm status: %d\n", result);
+ if (result >= 0)
+ result = -EPROTO;
+ }
+
+ kfree(sts);
+
+ return result;
+}
+
+static bool cp210x_tx_empty(struct usb_serial_port *port)
+{
+ int err;
+ u32 count;
+
+ err = cp210x_get_tx_queue_byte_count(port, &count);
+ if (err)
+ return true;
+
+ return !count;
+}
+
+/*
* cp210x_get_termios
* Reads the baud rate, data bits, parity, stop bits and flow control mode
* from the device, corrects any unsupported values, and configures the
@@ -520,7 +648,7 @@ static void cp210x_get_termios_port(struct usb_serial_port *port,
cflag = *cflagp;
- cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
+ cp210x_get_line_ctl(port, &bits);
cflag &= ~CSIZE;
switch (bits & BITS_DATA_MASK) {
case BITS_DATA_5:
@@ -688,7 +816,7 @@ static void cp210x_set_termios(struct tty_struct *tty,
/* If the number of data bits is to be updated */
if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
- cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
+ cp210x_get_line_ctl(port, &bits);
bits &= ~BITS_DATA_MASK;
switch (cflag & CSIZE) {
case CS5:
@@ -722,7 +850,7 @@ static void cp210x_set_termios(struct tty_struct *tty,
if ((cflag & (PARENB|PARODD|CMSPAR)) !=
(old_cflag & (PARENB|PARODD|CMSPAR))) {
- cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
+ cp210x_get_line_ctl(port, &bits);
bits &= ~BITS_PARITY_MASK;
if (cflag & PARENB) {
if (cflag & CMSPAR) {
@@ -748,7 +876,7 @@ static void cp210x_set_termios(struct tty_struct *tty,
}
if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) {
- cp210x_get_config(port, CP210X_GET_LINE_CTL, &bits, 2);
+ cp210x_get_line_ctl(port, &bits);
bits &= ~BITS_STOP_MASK;
if (cflag & CSTOPB) {
bits |= BITS_STOP_2;
@@ -863,29 +991,39 @@ static void cp210x_break_ctl(struct tty_struct *tty, int break_state)
cp210x_set_config(port, CP210X_SET_BREAK, &state, 2);
}
-static int cp210x_startup(struct usb_serial *serial)
+static int cp210x_port_probe(struct usb_serial_port *port)
{
+ struct usb_serial *serial = port->serial;
struct usb_host_interface *cur_altsetting;
- struct cp210x_serial_private *spriv;
+ struct cp210x_port_private *port_priv;
+ int ret;
- spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
- if (!spriv)
+ port_priv = kzalloc(sizeof(*port_priv), GFP_KERNEL);
+ if (!port_priv)
return -ENOMEM;
cur_altsetting = serial->interface->cur_altsetting;
- spriv->bInterfaceNumber = cur_altsetting->desc.bInterfaceNumber;
+ port_priv->bInterfaceNumber = cur_altsetting->desc.bInterfaceNumber;
+
+ usb_set_serial_port_data(port, port_priv);
- usb_set_serial_data(serial, spriv);
+ ret = cp210x_detect_swapped_line_ctl(port);
+ if (ret) {
+ kfree(port_priv);
+ return ret;
+ }
return 0;
}
-static void cp210x_release(struct usb_serial *serial)
+static int cp210x_port_remove(struct usb_serial_port *port)
{
- struct cp210x_serial_private *spriv;
+ struct cp210x_port_private *port_priv;
+
+ port_priv = usb_get_serial_port_data(port);
+ kfree(port_priv);
- spriv = usb_get_serial_data(serial);
- kfree(spriv);
+ return 0;
}
module_usb_serial_driver(serial_drivers, id_table);
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index c0866971db2b..f49327d20ee8 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -1046,9 +1046,8 @@ static void edge_close(struct usb_serial_port *port)
edge_port->closePending = true;
- if ((!edge_serial->is_epic) ||
- ((edge_serial->is_epic) &&
- (edge_serial->epic_descriptor.Supports.IOSPChase))) {
+ if (!edge_serial->is_epic ||
+ edge_serial->epic_descriptor.Supports.IOSPChase) {
/* flush and chase */
edge_port->chaseResponsePending = true;
@@ -1061,9 +1060,8 @@ static void edge_close(struct usb_serial_port *port)
edge_port->chaseResponsePending = false;
}
- if ((!edge_serial->is_epic) ||
- ((edge_serial->is_epic) &&
- (edge_serial->epic_descriptor.Supports.IOSPClose))) {
+ if (!edge_serial->is_epic ||
+ edge_serial->epic_descriptor.Supports.IOSPClose) {
/* close the port */
dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CLOSE_PORT\n", __func__);
send_iosp_ext_cmd(edge_port, IOSP_CMD_CLOSE_PORT, 0);
@@ -1612,9 +1610,8 @@ static void edge_break(struct tty_struct *tty, int break_state)
struct edgeport_serial *edge_serial = usb_get_serial_data(port->serial);
int status;
- if ((!edge_serial->is_epic) ||
- ((edge_serial->is_epic) &&
- (edge_serial->epic_descriptor.Supports.IOSPChase))) {
+ if (!edge_serial->is_epic ||
+ edge_serial->epic_descriptor.Supports.IOSPChase) {
/* flush and chase */
edge_port->chaseResponsePending = true;
@@ -1628,9 +1625,8 @@ static void edge_break(struct tty_struct *tty, int break_state)
}
}
- if ((!edge_serial->is_epic) ||
- ((edge_serial->is_epic) &&
- (edge_serial->epic_descriptor.Supports.IOSPSetClrBreak))) {
+ if (!edge_serial->is_epic ||
+ edge_serial->epic_descriptor.Supports.IOSPSetClrBreak) {
if (break_state == -1) {
dev_dbg(&port->dev, "%s - Sending IOSP_CMD_SET_BREAK\n", __func__);
status = send_iosp_ext_cmd(edge_port,
@@ -2465,9 +2461,8 @@ static void change_port_settings(struct tty_struct *tty,
unsigned char stop_char = STOP_CHAR(tty);
unsigned char start_char = START_CHAR(tty);
- if ((!edge_serial->is_epic) ||
- ((edge_serial->is_epic) &&
- (edge_serial->epic_descriptor.Supports.IOSPSetXChar))) {
+ if (!edge_serial->is_epic ||
+ edge_serial->epic_descriptor.Supports.IOSPSetXChar) {
send_iosp_ext_cmd(edge_port,
IOSP_CMD_SET_XON_CHAR, start_char);
send_iosp_ext_cmd(edge_port,
@@ -2494,13 +2489,11 @@ static void change_port_settings(struct tty_struct *tty,
}
/* Set flow control to the configured value */
- if ((!edge_serial->is_epic) ||
- ((edge_serial->is_epic) &&
- (edge_serial->epic_descriptor.Supports.IOSPSetRxFlow)))
+ if (!edge_serial->is_epic ||
+ edge_serial->epic_descriptor.Supports.IOSPSetRxFlow)
send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_RX_FLOW, rxFlow);
- if ((!edge_serial->is_epic) ||
- ((edge_serial->is_epic) &&
- (edge_serial->epic_descriptor.Supports.IOSPSetTxFlow)))
+ if (!edge_serial->is_epic ||
+ edge_serial->epic_descriptor.Supports.IOSPSetTxFlow)
send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_TX_FLOW, txFlow);
diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
index f51a5d52c0ed..ec1b8f2c1183 100644
--- a/drivers/usb/serial/ipaq.c
+++ b/drivers/usb/serial/ipaq.c
@@ -531,7 +531,8 @@ static int ipaq_open(struct tty_struct *tty,
* through. Since this has a reasonably high failure rate, we retry
* several times.
*/
- while (retries--) {
+ while (retries) {
+ retries--;
result = usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0), 0x22, 0x21,
0x1, 0, NULL, 0, 100);
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 8ac9b55f05af..2c69bfcdacc6 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -635,7 +635,7 @@ static void mos7840_interrupt_callback(struct urb *urb)
* Byte 4 IIR Port 4 (port.number is 3)
* Byte 5 FIFO status for both */
- if (length && length > 5) {
+ if (length > 5) {
dev_dbg(&urb->dev->dev, "%s", "Wrong data !!!\n");
return;
}
diff --git a/drivers/usb/serial/mxu11x0.c b/drivers/usb/serial/mxu11x0.c
new file mode 100644
index 000000000000..e3c3f57c2d82
--- /dev/null
+++ b/drivers/usb/serial/mxu11x0.c
@@ -0,0 +1,986 @@
+/*
+ * USB Moxa UPORT 11x0 Serial Driver
+ *
+ * Copyright (C) 2007 MOXA Technologies Co., Ltd.
+ * Copyright (C) 2015 Mathieu Othacehe <m.othacehe@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *
+ * Supports the following Moxa USB to serial converters:
+ * UPort 1110, 1 port RS-232 USB to Serial Hub.
+ * UPort 1130, 1 port RS-422/485 USB to Serial Hub.
+ * UPort 1130I, 1 port RS-422/485 USB to Serial Hub with isolation
+ * protection.
+ * UPort 1150, 1 port RS-232/422/485 USB to Serial Hub.
+ * UPort 1150I, 1 port RS-232/422/485 USB to Serial Hub with isolation
+ * protection.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/jiffies.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+/* Vendor and product ids */
+#define MXU1_VENDOR_ID 0x110a
+#define MXU1_1110_PRODUCT_ID 0x1110
+#define MXU1_1130_PRODUCT_ID 0x1130
+#define MXU1_1150_PRODUCT_ID 0x1150
+#define MXU1_1151_PRODUCT_ID 0x1151
+#define MXU1_1131_PRODUCT_ID 0x1131
+
+/* Commands */
+#define MXU1_GET_VERSION 0x01
+#define MXU1_GET_PORT_STATUS 0x02
+#define MXU1_GET_PORT_DEV_INFO 0x03
+#define MXU1_GET_CONFIG 0x04
+#define MXU1_SET_CONFIG 0x05
+#define MXU1_OPEN_PORT 0x06
+#define MXU1_CLOSE_PORT 0x07
+#define MXU1_START_PORT 0x08
+#define MXU1_STOP_PORT 0x09
+#define MXU1_TEST_PORT 0x0A
+#define MXU1_PURGE_PORT 0x0B
+#define MXU1_RESET_EXT_DEVICE 0x0C
+#define MXU1_GET_OUTQUEUE 0x0D
+#define MXU1_WRITE_DATA 0x80
+#define MXU1_READ_DATA 0x81
+#define MXU1_REQ_TYPE_CLASS 0x82
+
+/* Module identifiers */
+#define MXU1_I2C_PORT 0x01
+#define MXU1_IEEE1284_PORT 0x02
+#define MXU1_UART1_PORT 0x03
+#define MXU1_UART2_PORT 0x04
+#define MXU1_RAM_PORT 0x05
+
+/* Modem status */
+#define MXU1_MSR_DELTA_CTS 0x01
+#define MXU1_MSR_DELTA_DSR 0x02
+#define MXU1_MSR_DELTA_RI 0x04
+#define MXU1_MSR_DELTA_CD 0x08
+#define MXU1_MSR_CTS 0x10
+#define MXU1_MSR_DSR 0x20
+#define MXU1_MSR_RI 0x40
+#define MXU1_MSR_CD 0x80
+#define MXU1_MSR_DELTA_MASK 0x0F
+#define MXU1_MSR_MASK 0xF0
+
+/* Line status */
+#define MXU1_LSR_OVERRUN_ERROR 0x01
+#define MXU1_LSR_PARITY_ERROR 0x02
+#define MXU1_LSR_FRAMING_ERROR 0x04
+#define MXU1_LSR_BREAK 0x08
+#define MXU1_LSR_ERROR 0x0F
+#define MXU1_LSR_RX_FULL 0x10
+#define MXU1_LSR_TX_EMPTY 0x20
+
+/* Modem control */
+#define MXU1_MCR_LOOP 0x04
+#define MXU1_MCR_DTR 0x10
+#define MXU1_MCR_RTS 0x20
+
+/* Mask settings */
+#define MXU1_UART_ENABLE_RTS_IN 0x0001
+#define MXU1_UART_DISABLE_RTS 0x0002
+#define MXU1_UART_ENABLE_PARITY_CHECKING 0x0008
+#define MXU1_UART_ENABLE_DSR_OUT 0x0010
+#define MXU1_UART_ENABLE_CTS_OUT 0x0020
+#define MXU1_UART_ENABLE_X_OUT 0x0040
+#define MXU1_UART_ENABLE_XA_OUT 0x0080
+#define MXU1_UART_ENABLE_X_IN 0x0100
+#define MXU1_UART_ENABLE_DTR_IN 0x0800
+#define MXU1_UART_DISABLE_DTR 0x1000
+#define MXU1_UART_ENABLE_MS_INTS 0x2000
+#define MXU1_UART_ENABLE_AUTO_START_DMA 0x4000
+#define MXU1_UART_SEND_BREAK_SIGNAL 0x8000
+
+/* Parity */
+#define MXU1_UART_NO_PARITY 0x00
+#define MXU1_UART_ODD_PARITY 0x01
+#define MXU1_UART_EVEN_PARITY 0x02
+#define MXU1_UART_MARK_PARITY 0x03
+#define MXU1_UART_SPACE_PARITY 0x04
+
+/* Stop bits */
+#define MXU1_UART_1_STOP_BITS 0x00
+#define MXU1_UART_1_5_STOP_BITS 0x01
+#define MXU1_UART_2_STOP_BITS 0x02
+
+/* Bits per character */
+#define MXU1_UART_5_DATA_BITS 0x00
+#define MXU1_UART_6_DATA_BITS 0x01
+#define MXU1_UART_7_DATA_BITS 0x02
+#define MXU1_UART_8_DATA_BITS 0x03
+
+/* Operation modes */
+#define MXU1_UART_232 0x00
+#define MXU1_UART_485_RECEIVER_DISABLED 0x01
+#define MXU1_UART_485_RECEIVER_ENABLED 0x02
+
+/* Pipe transfer mode and timeout */
+#define MXU1_PIPE_MODE_CONTINUOUS 0x01
+#define MXU1_PIPE_MODE_MASK 0x03
+#define MXU1_PIPE_TIMEOUT_MASK 0x7C
+#define MXU1_PIPE_TIMEOUT_ENABLE 0x80
+
+/* Config struct */
+struct mxu1_uart_config {
+ __be16 wBaudRate;
+ __be16 wFlags;
+ u8 bDataBits;
+ u8 bParity;
+ u8 bStopBits;
+ char cXon;
+ char cXoff;
+ u8 bUartMode;
+} __packed;
+
+/* Purge modes */
+#define MXU1_PURGE_OUTPUT 0x00
+#define MXU1_PURGE_INPUT 0x80
+
+/* Read/Write data */
+#define MXU1_RW_DATA_ADDR_SFR 0x10
+#define MXU1_RW_DATA_ADDR_IDATA 0x20
+#define MXU1_RW_DATA_ADDR_XDATA 0x30
+#define MXU1_RW_DATA_ADDR_CODE 0x40
+#define MXU1_RW_DATA_ADDR_GPIO 0x50
+#define MXU1_RW_DATA_ADDR_I2C 0x60
+#define MXU1_RW_DATA_ADDR_FLASH 0x70
+#define MXU1_RW_DATA_ADDR_DSP 0x80
+
+#define MXU1_RW_DATA_UNSPECIFIED 0x00
+#define MXU1_RW_DATA_BYTE 0x01
+#define MXU1_RW_DATA_WORD 0x02
+#define MXU1_RW_DATA_DOUBLE_WORD 0x04
+
+struct mxu1_write_data_bytes {
+ u8 bAddrType;
+ u8 bDataType;
+ u8 bDataCounter;
+ __be16 wBaseAddrHi;
+ __be16 wBaseAddrLo;
+ u8 bData[0];
+} __packed;
+
+/* Interrupt codes */
+#define MXU1_CODE_HARDWARE_ERROR 0xFF
+#define MXU1_CODE_DATA_ERROR 0x03
+#define MXU1_CODE_MODEM_STATUS 0x04
+
+static inline int mxu1_get_func_from_code(unsigned char code)
+{
+ return code & 0x0f;
+}
+
+/* Download firmware max packet size */
+#define MXU1_DOWNLOAD_MAX_PACKET_SIZE 64
+
+/* Firmware image header */
+struct mxu1_firmware_header {
+ __le16 wLength;
+ u8 bCheckSum;
+} __packed;
+
+#define MXU1_UART_BASE_ADDR 0xFFA0
+#define MXU1_UART_OFFSET_MCR 0x0004
+
+#define MXU1_BAUD_BASE 923077
+
+#define MXU1_TRANSFER_TIMEOUT 2
+#define MXU1_DOWNLOAD_TIMEOUT 1000
+#define MXU1_DEFAULT_CLOSING_WAIT 4000 /* in .01 secs */
+
+struct mxu1_port {
+ u8 msr;
+ u8 mcr;
+ u8 uart_mode;
+ spinlock_t spinlock; /* Protects msr */
+ struct mutex mutex; /* Protects mcr */
+ bool send_break;
+};
+
+struct mxu1_device {
+ u16 mxd_model;
+};
+
+static const struct usb_device_id mxu1_idtable[] = {
+ { USB_DEVICE(MXU1_VENDOR_ID, MXU1_1110_PRODUCT_ID) },
+ { USB_DEVICE(MXU1_VENDOR_ID, MXU1_1130_PRODUCT_ID) },
+ { USB_DEVICE(MXU1_VENDOR_ID, MXU1_1150_PRODUCT_ID) },
+ { USB_DEVICE(MXU1_VENDOR_ID, MXU1_1151_PRODUCT_ID) },
+ { USB_DEVICE(MXU1_VENDOR_ID, MXU1_1131_PRODUCT_ID) },
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, mxu1_idtable);
+
+/* Write the given buffer out to the control pipe. */
+static int mxu1_send_ctrl_data_urb(struct usb_serial *serial,
+ u8 request,
+ u16 value, u16 index,
+ void *data, size_t size)
+{
+ int status;
+
+ status = usb_control_msg(serial->dev,
+ usb_sndctrlpipe(serial->dev, 0),
+ request,
+ (USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE), value, index,
+ data, size,
+ USB_CTRL_SET_TIMEOUT);
+ if (status < 0) {
+ dev_err(&serial->interface->dev,
+ "%s - usb_control_msg failed: %d\n",
+ __func__, status);
+ return status;
+ }
+
+ if (status != size) {
+ dev_err(&serial->interface->dev,
+ "%s - short write (%d / %zd)\n",
+ __func__, status, size);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/* Send a vendor request without any data */
+static int mxu1_send_ctrl_urb(struct usb_serial *serial,
+ u8 request, u16 value, u16 index)
+{
+ return mxu1_send_ctrl_data_urb(serial, request, value, index,
+ NULL, 0);
+}
+
+static int mxu1_download_firmware(struct usb_serial *serial,
+ const struct firmware *fw_p)
+{
+ int status = 0;
+ int buffer_size;
+ int pos;
+ int len;
+ int done;
+ u8 cs = 0;
+ u8 *buffer;
+ struct usb_device *dev = serial->dev;
+ struct mxu1_firmware_header *header;
+ unsigned int pipe;
+
+ pipe = usb_sndbulkpipe(dev, serial->port[0]->bulk_out_endpointAddress);
+
+ buffer_size = fw_p->size + sizeof(*header);
+ buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ memcpy(buffer, fw_p->data, fw_p->size);
+ memset(buffer + fw_p->size, 0xff, buffer_size - fw_p->size);
+
+ for (pos = sizeof(*header); pos < buffer_size; pos++)
+ cs = (u8)(cs + buffer[pos]);
+
+ header = (struct mxu1_firmware_header *)buffer;
+ header->wLength = cpu_to_le16(buffer_size - sizeof(*header));
+ header->bCheckSum = cs;
+
+ dev_dbg(&dev->dev, "%s - downloading firmware\n", __func__);
+
+ for (pos = 0; pos < buffer_size; pos += done) {
+ len = min(buffer_size - pos, MXU1_DOWNLOAD_MAX_PACKET_SIZE);
+
+ status = usb_bulk_msg(dev, pipe, buffer + pos, len, &done,
+ MXU1_DOWNLOAD_TIMEOUT);
+ if (status)
+ break;
+ }
+
+ kfree(buffer);
+
+ if (status) {
+ dev_err(&dev->dev, "failed to download firmware: %d\n", status);
+ return status;
+ }
+
+ msleep_interruptible(100);
+ usb_reset_device(dev);
+
+ dev_dbg(&dev->dev, "%s - download successful\n", __func__);
+
+ return 0;
+}
+
+static int mxu1_port_probe(struct usb_serial_port *port)
+{
+ struct mxu1_port *mxport;
+ struct mxu1_device *mxdev;
+
+ if (!port->interrupt_in_urb) {
+ dev_err(&port->dev, "no interrupt urb\n");
+ return -ENODEV;
+ }
+
+ mxport = kzalloc(sizeof(struct mxu1_port), GFP_KERNEL);
+ if (!mxport)
+ return -ENOMEM;
+
+ spin_lock_init(&mxport->spinlock);
+ mutex_init(&mxport->mutex);
+
+ mxdev = usb_get_serial_data(port->serial);
+
+ switch (mxdev->mxd_model) {
+ case MXU1_1110_PRODUCT_ID:
+ case MXU1_1150_PRODUCT_ID:
+ case MXU1_1151_PRODUCT_ID:
+ mxport->uart_mode = MXU1_UART_232;
+ break;
+ case MXU1_1130_PRODUCT_ID:
+ case MXU1_1131_PRODUCT_ID:
+ mxport->uart_mode = MXU1_UART_485_RECEIVER_DISABLED;
+ break;
+ }
+
+ usb_set_serial_port_data(port, mxport);
+
+ port->port.closing_wait =
+ msecs_to_jiffies(MXU1_DEFAULT_CLOSING_WAIT * 10);
+ port->port.drain_delay = 1;
+
+ return 0;
+}
+
+static int mxu1_startup(struct usb_serial *serial)
+{
+ struct mxu1_device *mxdev;
+ struct usb_device *dev = serial->dev;
+ struct usb_host_interface *cur_altsetting;
+ char fw_name[32];
+ const struct firmware *fw_p = NULL;
+ int err;
+
+ dev_dbg(&serial->interface->dev, "%s - product 0x%04X, num configurations %d, configuration value %d\n",
+ __func__, le16_to_cpu(dev->descriptor.idProduct),
+ dev->descriptor.bNumConfigurations,
+ dev->actconfig->desc.bConfigurationValue);
+
+ /* create device structure */
+ mxdev = kzalloc(sizeof(struct mxu1_device), GFP_KERNEL);
+ if (!mxdev)
+ return -ENOMEM;
+
+ usb_set_serial_data(serial, mxdev);
+
+ mxdev->mxd_model = le16_to_cpu(dev->descriptor.idProduct);
+
+ cur_altsetting = serial->interface->cur_altsetting;
+
+ /* if we have only 1 configuration, download firmware */
+ if (cur_altsetting->desc.bNumEndpoints == 1) {
+
+ snprintf(fw_name,
+ sizeof(fw_name),
+ "moxa/moxa-%04x.fw",
+ mxdev->mxd_model);
+
+ err = request_firmware(&fw_p, fw_name, &serial->interface->dev);
+ if (err) {
+ dev_err(&serial->interface->dev, "failed to request firmware: %d\n",
+ err);
+ goto err_free_mxdev;
+ }
+
+ err = mxu1_download_firmware(serial, fw_p);
+ if (err)
+ goto err_release_firmware;
+
+ /* device is being reset */
+ err = -ENODEV;
+ goto err_release_firmware;
+ }
+
+ return 0;
+
+err_release_firmware:
+ release_firmware(fw_p);
+err_free_mxdev:
+ kfree(mxdev);
+
+ return err;
+}
+
+static int mxu1_write_byte(struct usb_serial_port *port, u32 addr,
+ u8 mask, u8 byte)
+{
+ int status;
+ size_t size;
+ struct mxu1_write_data_bytes *data;
+
+ dev_dbg(&port->dev, "%s - addr 0x%08X, mask 0x%02X, byte 0x%02X\n",
+ __func__, addr, mask, byte);
+
+ size = sizeof(struct mxu1_write_data_bytes) + 2;
+ data = kzalloc(size, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->bAddrType = MXU1_RW_DATA_ADDR_XDATA;
+ data->bDataType = MXU1_RW_DATA_BYTE;
+ data->bDataCounter = 1;
+ data->wBaseAddrHi = cpu_to_be16(addr >> 16);
+ data->wBaseAddrLo = cpu_to_be16(addr);
+ data->bData[0] = mask;
+ data->bData[1] = byte;
+
+ status = mxu1_send_ctrl_data_urb(port->serial, MXU1_WRITE_DATA, 0,
+ MXU1_RAM_PORT, data, size);
+ if (status < 0)
+ dev_err(&port->dev, "%s - failed: %d\n", __func__, status);
+
+ kfree(data);
+
+ return status;
+}
+
+static int mxu1_set_mcr(struct usb_serial_port *port, unsigned int mcr)
+{
+ int status;
+
+ status = mxu1_write_byte(port,
+ MXU1_UART_BASE_ADDR + MXU1_UART_OFFSET_MCR,
+ MXU1_MCR_RTS | MXU1_MCR_DTR | MXU1_MCR_LOOP,
+ mcr);
+ return status;
+}
+
+static void mxu1_set_termios(struct tty_struct *tty,
+ struct usb_serial_port *port,
+ struct ktermios *old_termios)
+{
+ struct mxu1_port *mxport = usb_get_serial_port_data(port);
+ struct mxu1_uart_config *config;
+ tcflag_t cflag, iflag;
+ speed_t baud;
+ int status;
+ unsigned int mcr;
+
+ cflag = tty->termios.c_cflag;
+ iflag = tty->termios.c_iflag;
+
+ if (old_termios &&
+ !tty_termios_hw_change(&tty->termios, old_termios) &&
+ tty->termios.c_iflag == old_termios->c_iflag) {
+ dev_dbg(&port->dev, "%s - nothing to change\n", __func__);
+ return;
+ }
+
+ dev_dbg(&port->dev,
+ "%s - cflag 0x%08x, iflag 0x%08x\n", __func__, cflag, iflag);
+
+ if (old_termios) {
+ dev_dbg(&port->dev, "%s - old cflag 0x%08x, old iflag 0x%08x\n",
+ __func__,
+ old_termios->c_cflag,
+ old_termios->c_iflag);
+ }
+
+ config = kzalloc(sizeof(*config), GFP_KERNEL);
+ if (!config)
+ return;
+
+ /* these flags must be set */
+ config->wFlags |= MXU1_UART_ENABLE_MS_INTS;
+ config->wFlags |= MXU1_UART_ENABLE_AUTO_START_DMA;
+ if (mxport->send_break)
+ config->wFlags |= MXU1_UART_SEND_BREAK_SIGNAL;
+ config->bUartMode = mxport->uart_mode;
+
+ switch (C_CSIZE(tty)) {
+ case CS5:
+ config->bDataBits = MXU1_UART_5_DATA_BITS;
+ break;
+ case CS6:
+ config->bDataBits = MXU1_UART_6_DATA_BITS;
+ break;
+ case CS7:
+ config->bDataBits = MXU1_UART_7_DATA_BITS;
+ break;
+ default:
+ case CS8:
+ config->bDataBits = MXU1_UART_8_DATA_BITS;
+ break;
+ }
+
+ if (C_PARENB(tty)) {
+ config->wFlags |= MXU1_UART_ENABLE_PARITY_CHECKING;
+ if (C_CMSPAR(tty)) {
+ if (C_PARODD(tty))
+ config->bParity = MXU1_UART_MARK_PARITY;
+ else
+ config->bParity = MXU1_UART_SPACE_PARITY;
+ } else {
+ if (C_PARODD(tty))
+ config->bParity = MXU1_UART_ODD_PARITY;
+ else
+ config->bParity = MXU1_UART_EVEN_PARITY;
+ }
+ } else {
+ config->bParity = MXU1_UART_NO_PARITY;
+ }
+
+ if (C_CSTOPB(tty))
+ config->bStopBits = MXU1_UART_2_STOP_BITS;
+ else
+ config->bStopBits = MXU1_UART_1_STOP_BITS;
+
+ if (C_CRTSCTS(tty)) {
+ /* RTS flow control must be off to drop RTS for baud rate B0 */
+ if (C_BAUD(tty) != B0)
+ config->wFlags |= MXU1_UART_ENABLE_RTS_IN;
+ config->wFlags |= MXU1_UART_ENABLE_CTS_OUT;
+ }
+
+ if (I_IXOFF(tty) || I_IXON(tty)) {
+ config->cXon = START_CHAR(tty);
+ config->cXoff = STOP_CHAR(tty);
+
+ if (I_IXOFF(tty))
+ config->wFlags |= MXU1_UART_ENABLE_X_IN;
+
+ if (I_IXON(tty))
+ config->wFlags |= MXU1_UART_ENABLE_X_OUT;
+ }
+
+ baud = tty_get_baud_rate(tty);
+ if (!baud)
+ baud = 9600;
+ config->wBaudRate = MXU1_BAUD_BASE / baud;
+
+ dev_dbg(&port->dev, "%s - BaudRate=%d, wBaudRate=%d, wFlags=0x%04X, bDataBits=%d, bParity=%d, bStopBits=%d, cXon=%d, cXoff=%d, bUartMode=%d\n",
+ __func__, baud, config->wBaudRate, config->wFlags,
+ config->bDataBits, config->bParity, config->bStopBits,
+ config->cXon, config->cXoff, config->bUartMode);
+
+ cpu_to_be16s(&config->wBaudRate);
+ cpu_to_be16s(&config->wFlags);
+
+ status = mxu1_send_ctrl_data_urb(port->serial, MXU1_SET_CONFIG, 0,
+ MXU1_UART1_PORT, config,
+ sizeof(*config));
+ if (status)
+ dev_err(&port->dev, "cannot set config: %d\n", status);
+
+ mutex_lock(&mxport->mutex);
+ mcr = mxport->mcr;
+
+ if (C_BAUD(tty) == B0)
+ mcr &= ~(MXU1_MCR_DTR | MXU1_MCR_RTS);
+ else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
+ mcr |= MXU1_MCR_DTR | MXU1_MCR_RTS;
+
+ status = mxu1_set_mcr(port, mcr);
+ if (status)
+ dev_err(&port->dev, "cannot set modem control: %d\n", status);
+ else
+ mxport->mcr = mcr;
+
+ mutex_unlock(&mxport->mutex);
+
+ kfree(config);
+}
+
+static int mxu1_get_serial_info(struct usb_serial_port *port,
+ struct serial_struct __user *ret_arg)
+{
+ struct serial_struct ret_serial;
+ unsigned cwait;
+
+ if (!ret_arg)
+ return -EFAULT;
+
+ cwait = port->port.closing_wait;
+ if (cwait != ASYNC_CLOSING_WAIT_NONE)
+ cwait = jiffies_to_msecs(cwait) / 10;
+
+ memset(&ret_serial, 0, sizeof(ret_serial));
+
+ ret_serial.type = PORT_16550A;
+ ret_serial.line = port->minor;
+ ret_serial.port = 0;
+ ret_serial.xmit_fifo_size = port->bulk_out_size;
+ ret_serial.baud_base = MXU1_BAUD_BASE;
+ ret_serial.close_delay = 5*HZ;
+ ret_serial.closing_wait = cwait;
+
+ if (copy_to_user(ret_arg, &ret_serial, sizeof(*ret_arg)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int mxu1_set_serial_info(struct usb_serial_port *port,
+ struct serial_struct __user *new_arg)
+{
+ struct serial_struct new_serial;
+ unsigned cwait;
+
+ if (copy_from_user(&new_serial, new_arg, sizeof(new_serial)))
+ return -EFAULT;
+
+ cwait = new_serial.closing_wait;
+ if (cwait != ASYNC_CLOSING_WAIT_NONE)
+ cwait = msecs_to_jiffies(10 * new_serial.closing_wait);
+
+ port->port.closing_wait = cwait;
+
+ return 0;
+}
+
+static int mxu1_ioctl(struct tty_struct *tty,
+ unsigned int cmd, unsigned long arg)
+{
+ struct usb_serial_port *port = tty->driver_data;
+
+ switch (cmd) {
+ case TIOCGSERIAL:
+ return mxu1_get_serial_info(port,
+ (struct serial_struct __user *)arg);
+ case TIOCSSERIAL:
+ return mxu1_set_serial_info(port,
+ (struct serial_struct __user *)arg);
+ }
+
+ return -ENOIOCTLCMD;
+}
+
+static int mxu1_tiocmget(struct tty_struct *tty)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct mxu1_port *mxport = usb_get_serial_port_data(port);
+ unsigned int result;
+ unsigned int msr;
+ unsigned int mcr;
+ unsigned long flags;
+
+ mutex_lock(&mxport->mutex);
+ spin_lock_irqsave(&mxport->spinlock, flags);
+
+ msr = mxport->msr;
+ mcr = mxport->mcr;
+
+ spin_unlock_irqrestore(&mxport->spinlock, flags);
+ mutex_unlock(&mxport->mutex);
+
+ result = ((mcr & MXU1_MCR_DTR) ? TIOCM_DTR : 0) |
+ ((mcr & MXU1_MCR_RTS) ? TIOCM_RTS : 0) |
+ ((mcr & MXU1_MCR_LOOP) ? TIOCM_LOOP : 0) |
+ ((msr & MXU1_MSR_CTS) ? TIOCM_CTS : 0) |
+ ((msr & MXU1_MSR_CD) ? TIOCM_CAR : 0) |
+ ((msr & MXU1_MSR_RI) ? TIOCM_RI : 0) |
+ ((msr & MXU1_MSR_DSR) ? TIOCM_DSR : 0);
+
+ dev_dbg(&port->dev, "%s - 0x%04X\n", __func__, result);
+
+ return result;
+}
+
+static int mxu1_tiocmset(struct tty_struct *tty,
+ unsigned int set, unsigned int clear)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct mxu1_port *mxport = usb_get_serial_port_data(port);
+ int err;
+ unsigned int mcr;
+
+ mutex_lock(&mxport->mutex);
+ mcr = mxport->mcr;
+
+ if (set & TIOCM_RTS)
+ mcr |= MXU1_MCR_RTS;
+ if (set & TIOCM_DTR)
+ mcr |= MXU1_MCR_DTR;
+ if (set & TIOCM_LOOP)
+ mcr |= MXU1_MCR_LOOP;
+
+ if (clear & TIOCM_RTS)
+ mcr &= ~MXU1_MCR_RTS;
+ if (clear & TIOCM_DTR)
+ mcr &= ~MXU1_MCR_DTR;
+ if (clear & TIOCM_LOOP)
+ mcr &= ~MXU1_MCR_LOOP;
+
+ err = mxu1_set_mcr(port, mcr);
+ if (!err)
+ mxport->mcr = mcr;
+
+ mutex_unlock(&mxport->mutex);
+
+ return err;
+}
+
+static void mxu1_break(struct tty_struct *tty, int break_state)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct mxu1_port *mxport = usb_get_serial_port_data(port);
+
+ if (break_state == -1)
+ mxport->send_break = true;
+ else
+ mxport->send_break = false;
+
+ mxu1_set_termios(tty, port, NULL);
+}
+
+static int mxu1_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+ struct mxu1_port *mxport = usb_get_serial_port_data(port);
+ struct usb_serial *serial = port->serial;
+ int status;
+ u16 open_settings;
+
+ open_settings = (MXU1_PIPE_MODE_CONTINUOUS |
+ MXU1_PIPE_TIMEOUT_ENABLE |
+ (MXU1_TRANSFER_TIMEOUT << 2));
+
+ mxport->msr = 0;
+
+ status = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+ if (status) {
+ dev_err(&port->dev, "failed to submit interrupt urb: %d\n",
+ status);
+ return status;
+ }
+
+ if (tty)
+ mxu1_set_termios(tty, port, NULL);
+
+ status = mxu1_send_ctrl_urb(serial, MXU1_OPEN_PORT,
+ open_settings, MXU1_UART1_PORT);
+ if (status) {
+ dev_err(&port->dev, "cannot send open command: %d\n", status);
+ goto unlink_int_urb;
+ }
+
+ status = mxu1_send_ctrl_urb(serial, MXU1_START_PORT,
+ 0, MXU1_UART1_PORT);
+ if (status) {
+ dev_err(&port->dev, "cannot send start command: %d\n", status);
+ goto unlink_int_urb;
+ }
+
+ status = mxu1_send_ctrl_urb(serial, MXU1_PURGE_PORT,
+ MXU1_PURGE_INPUT, MXU1_UART1_PORT);
+ if (status) {
+ dev_err(&port->dev, "cannot clear input buffers: %d\n",
+ status);
+
+ goto unlink_int_urb;
+ }
+
+ status = mxu1_send_ctrl_urb(serial, MXU1_PURGE_PORT,
+ MXU1_PURGE_OUTPUT, MXU1_UART1_PORT);
+ if (status) {
+ dev_err(&port->dev, "cannot clear output buffers: %d\n",
+ status);
+
+ goto unlink_int_urb;
+ }
+
+ /*
+ * reset the data toggle on the bulk endpoints to work around bug in
+ * host controllers where things get out of sync some times
+ */
+ usb_clear_halt(serial->dev, port->write_urb->pipe);
+ usb_clear_halt(serial->dev, port->read_urb->pipe);
+
+ if (tty)
+ mxu1_set_termios(tty, port, NULL);
+
+ status = mxu1_send_ctrl_urb(serial, MXU1_OPEN_PORT,
+ open_settings, MXU1_UART1_PORT);
+ if (status) {
+ dev_err(&port->dev, "cannot send open command: %d\n", status);
+ goto unlink_int_urb;
+ }
+
+ status = mxu1_send_ctrl_urb(serial, MXU1_START_PORT,
+ 0, MXU1_UART1_PORT);
+ if (status) {
+ dev_err(&port->dev, "cannot send start command: %d\n", status);
+ goto unlink_int_urb;
+ }
+
+ status = usb_serial_generic_open(tty, port);
+ if (status)
+ goto unlink_int_urb;
+
+ return 0;
+
+unlink_int_urb:
+ usb_kill_urb(port->interrupt_in_urb);
+
+ return status;
+}
+
+static void mxu1_close(struct usb_serial_port *port)
+{
+ int status;
+
+ usb_serial_generic_close(port);
+ usb_kill_urb(port->interrupt_in_urb);
+
+ status = mxu1_send_ctrl_urb(port->serial, MXU1_CLOSE_PORT,
+ 0, MXU1_UART1_PORT);
+ if (status) {
+ dev_err(&port->dev, "failed to send close port command: %d\n",
+ status);
+ }
+}
+
+static void mxu1_handle_new_msr(struct usb_serial_port *port, u8 msr)
+{
+ struct mxu1_port *mxport = usb_get_serial_port_data(port);
+ struct async_icount *icount;
+ unsigned long flags;
+
+ dev_dbg(&port->dev, "%s - msr 0x%02X\n", __func__, msr);
+
+ spin_lock_irqsave(&mxport->spinlock, flags);
+ mxport->msr = msr & MXU1_MSR_MASK;
+ spin_unlock_irqrestore(&mxport->spinlock, flags);
+
+ if (msr & MXU1_MSR_DELTA_MASK) {
+ icount = &port->icount;
+ if (msr & MXU1_MSR_DELTA_CTS)
+ icount->cts++;
+ if (msr & MXU1_MSR_DELTA_DSR)
+ icount->dsr++;
+ if (msr & MXU1_MSR_DELTA_CD)
+ icount->dcd++;
+ if (msr & MXU1_MSR_DELTA_RI)
+ icount->rng++;
+
+ wake_up_interruptible(&port->port.delta_msr_wait);
+ }
+}
+
+static void mxu1_interrupt_callback(struct urb *urb)
+{
+ struct usb_serial_port *port = urb->context;
+ unsigned char *data = urb->transfer_buffer;
+ int length = urb->actual_length;
+ int function;
+ int status;
+ u8 msr;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ dev_dbg(&port->dev, "%s - urb shutting down: %d\n",
+ __func__, urb->status);
+ return;
+ default:
+ dev_dbg(&port->dev, "%s - nonzero urb status: %d\n",
+ __func__, urb->status);
+ goto exit;
+ }
+
+ if (length != 2) {
+ dev_dbg(&port->dev, "%s - bad packet size: %d\n",
+ __func__, length);
+ goto exit;
+ }
+
+ if (data[0] == MXU1_CODE_HARDWARE_ERROR) {
+ dev_err(&port->dev, "hardware error: %d\n", data[1]);
+ goto exit;
+ }
+
+ function = mxu1_get_func_from_code(data[0]);
+
+ dev_dbg(&port->dev, "%s - function %d, data 0x%02X\n",
+ __func__, function, data[1]);
+
+ switch (function) {
+ case MXU1_CODE_DATA_ERROR:
+ dev_dbg(&port->dev, "%s - DATA ERROR, data 0x%02X\n",
+ __func__, data[1]);
+ break;
+
+ case MXU1_CODE_MODEM_STATUS:
+ msr = data[1];
+ mxu1_handle_new_msr(port, msr);
+ break;
+
+ default:
+ dev_err(&port->dev, "unknown interrupt code: 0x%02X\n",
+ data[1]);
+ break;
+ }
+
+exit:
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status) {
+ dev_err(&port->dev, "resubmit interrupt urb failed: %d\n",
+ status);
+ }
+}
+
+static struct usb_serial_driver mxu11x0_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mxu11x0",
+ },
+ .description = "MOXA UPort 11x0",
+ .id_table = mxu1_idtable,
+ .num_ports = 1,
+ .port_probe = mxu1_port_probe,
+ .attach = mxu1_startup,
+ .open = mxu1_open,
+ .close = mxu1_close,
+ .ioctl = mxu1_ioctl,
+ .set_termios = mxu1_set_termios,
+ .tiocmget = mxu1_tiocmget,
+ .tiocmset = mxu1_tiocmset,
+ .tiocmiwait = usb_serial_generic_tiocmiwait,
+ .get_icount = usb_serial_generic_get_icount,
+ .break_ctl = mxu1_break,
+ .read_int_callback = mxu1_interrupt_callback,
+};
+
+static struct usb_serial_driver *const serial_drivers[] = {
+ &mxu11x0_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, mxu1_idtable);
+
+MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>");
+MODULE_DESCRIPTION("MOXA UPort 11x0 USB to Serial Hub Driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("moxa/moxa-1110.fw");
+MODULE_FIRMWARE("moxa/moxa-1130.fw");
+MODULE_FIRMWARE("moxa/moxa-1131.fw");
+MODULE_FIRMWARE("moxa/moxa-1150.fw");
+MODULE_FIRMWARE("moxa/moxa-1151.fw");
diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c
index 3658662898fc..a204782ae530 100644
--- a/drivers/usb/serial/usb-serial-simple.c
+++ b/drivers/usb/serial/usb-serial-simple.c
@@ -53,6 +53,7 @@ DEVICE(funsoft, FUNSOFT_IDS);
/* Infineon Flashloader driver */
#define FLASHLOADER_IDS() \
+ { USB_DEVICE_INTERFACE_CLASS(0x058b, 0x0041, USB_CLASS_CDC_DATA) }, \
{ USB_DEVICE(0x8087, 0x0716) }
DEVICE(flashloader, FLASHLOADER_IDS);
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index e69151664436..9ff9404f99d7 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -49,18 +49,18 @@ struct uas_dev_info {
};
enum {
- SUBMIT_STATUS_URB = (1 << 1),
- ALLOC_DATA_IN_URB = (1 << 2),
- SUBMIT_DATA_IN_URB = (1 << 3),
- ALLOC_DATA_OUT_URB = (1 << 4),
- SUBMIT_DATA_OUT_URB = (1 << 5),
- ALLOC_CMD_URB = (1 << 6),
- SUBMIT_CMD_URB = (1 << 7),
- COMMAND_INFLIGHT = (1 << 8),
- DATA_IN_URB_INFLIGHT = (1 << 9),
- DATA_OUT_URB_INFLIGHT = (1 << 10),
- COMMAND_ABORTED = (1 << 11),
- IS_IN_WORK_LIST = (1 << 12),
+ SUBMIT_STATUS_URB = BIT(1),
+ ALLOC_DATA_IN_URB = BIT(2),
+ SUBMIT_DATA_IN_URB = BIT(3),
+ ALLOC_DATA_OUT_URB = BIT(4),
+ SUBMIT_DATA_OUT_URB = BIT(5),
+ ALLOC_CMD_URB = BIT(6),
+ SUBMIT_CMD_URB = BIT(7),
+ COMMAND_INFLIGHT = BIT(8),
+ DATA_IN_URB_INFLIGHT = BIT(9),
+ DATA_OUT_URB_INFLIGHT = BIT(10),
+ COMMAND_ABORTED = BIT(11),
+ IS_IN_WORK_LIST = BIT(12),
};
/* Overrides scsi_pointer */
@@ -74,7 +74,7 @@ struct uas_cmd_info {
/* I hate forward declarations, but I actually have a loop */
static int uas_submit_urbs(struct scsi_cmnd *cmnd,
- struct uas_dev_info *devinfo, gfp_t gfp);
+ struct uas_dev_info *devinfo);
static void uas_do_work(struct work_struct *work);
static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller);
static void uas_free_streams(struct uas_dev_info *devinfo);
@@ -105,7 +105,7 @@ static void uas_do_work(struct work_struct *work)
if (!(cmdinfo->state & IS_IN_WORK_LIST))
continue;
- err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC);
+ err = uas_submit_urbs(cmnd, cmnd->device->hostdata);
if (!err)
cmdinfo->state &= ~IS_IN_WORK_LIST;
else
@@ -240,7 +240,7 @@ static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd,
int err;
cmdinfo->state |= direction | SUBMIT_STATUS_URB;
- err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC);
+ err = uas_submit_urbs(cmnd, cmnd->device->hostdata);
if (err) {
uas_add_work(cmdinfo);
}
@@ -512,7 +512,7 @@ static struct urb *uas_submit_sense_urb(struct scsi_cmnd *cmnd, gfp_t gfp)
}
static int uas_submit_urbs(struct scsi_cmnd *cmnd,
- struct uas_dev_info *devinfo, gfp_t gfp)
+ struct uas_dev_info *devinfo)
{
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
struct urb *urb;
@@ -520,14 +520,14 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
lockdep_assert_held(&devinfo->lock);
if (cmdinfo->state & SUBMIT_STATUS_URB) {
- urb = uas_submit_sense_urb(cmnd, gfp);
+ urb = uas_submit_sense_urb(cmnd, GFP_ATOMIC);
if (!urb)
return SCSI_MLQUEUE_DEVICE_BUSY;
cmdinfo->state &= ~SUBMIT_STATUS_URB;
}
if (cmdinfo->state & ALLOC_DATA_IN_URB) {
- cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, gfp,
+ cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, GFP_ATOMIC,
cmnd, DMA_FROM_DEVICE);
if (!cmdinfo->data_in_urb)
return SCSI_MLQUEUE_DEVICE_BUSY;
@@ -536,7 +536,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
if (cmdinfo->state & SUBMIT_DATA_IN_URB) {
usb_anchor_urb(cmdinfo->data_in_urb, &devinfo->data_urbs);
- err = usb_submit_urb(cmdinfo->data_in_urb, gfp);
+ err = usb_submit_urb(cmdinfo->data_in_urb, GFP_ATOMIC);
if (err) {
usb_unanchor_urb(cmdinfo->data_in_urb);
uas_log_cmd_state(cmnd, "data in submit err", err);
@@ -547,7 +547,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
}
if (cmdinfo->state & ALLOC_DATA_OUT_URB) {
- cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, gfp,
+ cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, GFP_ATOMIC,
cmnd, DMA_TO_DEVICE);
if (!cmdinfo->data_out_urb)
return SCSI_MLQUEUE_DEVICE_BUSY;
@@ -556,7 +556,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
if (cmdinfo->state & SUBMIT_DATA_OUT_URB) {
usb_anchor_urb(cmdinfo->data_out_urb, &devinfo->data_urbs);
- err = usb_submit_urb(cmdinfo->data_out_urb, gfp);
+ err = usb_submit_urb(cmdinfo->data_out_urb, GFP_ATOMIC);
if (err) {
usb_unanchor_urb(cmdinfo->data_out_urb);
uas_log_cmd_state(cmnd, "data out submit err", err);
@@ -567,7 +567,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
}
if (cmdinfo->state & ALLOC_CMD_URB) {
- cmdinfo->cmd_urb = uas_alloc_cmd_urb(devinfo, gfp, cmnd);
+ cmdinfo->cmd_urb = uas_alloc_cmd_urb(devinfo, GFP_ATOMIC, cmnd);
if (!cmdinfo->cmd_urb)
return SCSI_MLQUEUE_DEVICE_BUSY;
cmdinfo->state &= ~ALLOC_CMD_URB;
@@ -575,7 +575,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
if (cmdinfo->state & SUBMIT_CMD_URB) {
usb_anchor_urb(cmdinfo->cmd_urb, &devinfo->cmd_urbs);
- err = usb_submit_urb(cmdinfo->cmd_urb, gfp);
+ err = usb_submit_urb(cmdinfo->cmd_urb, GFP_ATOMIC);
if (err) {
usb_unanchor_urb(cmdinfo->cmd_urb);
uas_log_cmd_state(cmnd, "cmd submit err", err);
@@ -653,7 +653,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
if (!devinfo->use_streams)
cmdinfo->state &= ~(SUBMIT_DATA_IN_URB | SUBMIT_DATA_OUT_URB);
- err = uas_submit_urbs(cmnd, devinfo, GFP_ATOMIC);
+ err = uas_submit_urbs(cmnd, devinfo);
if (err) {
/* If we did nothing, give up now */
if (cmdinfo->state & SUBMIT_STATUS_URB) {
@@ -796,6 +796,10 @@ static int uas_slave_configure(struct scsi_device *sdev)
if (devinfo->flags & US_FL_NO_REPORT_OPCODES)
sdev->no_report_opcodes = 1;
+ /* A few buggy USB-ATA bridges don't understand FUA */
+ if (devinfo->flags & US_FL_BROKEN_FUA)
+ sdev->broken_fua = 1;
+
scsi_change_queue_depth(sdev, devinfo->qdepth - 2);
return 0;
}
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 6b2479123de7..7ffe4209067b 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -1987,7 +1987,7 @@ UNUSUAL_DEV( 0x14cd, 0x6600, 0x0201, 0x0201,
US_FL_IGNORE_RESIDUE ),
/* Reported by Michael Büsch <m@bues.ch> */
-UNUSUAL_DEV( 0x152d, 0x0567, 0x0114, 0x0114,
+UNUSUAL_DEV( 0x152d, 0x0567, 0x0114, 0x0116,
"JMicron",
"USB to ATA/ATAPI Bridge",
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h
index c85ea530085f..ccc113e83d88 100644
--- a/drivers/usb/storage/unusual_uas.h
+++ b/drivers/usb/storage/unusual_uas.h
@@ -132,7 +132,7 @@ UNUSUAL_DEV(0x152d, 0x0567, 0x0000, 0x9999,
"JMicron",
"JMS567",
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
- US_FL_NO_REPORT_OPCODES),
+ US_FL_BROKEN_FUA | US_FL_NO_REPORT_OPCODES),
/* Reported-by: Hans de Goede <hdegoede@redhat.com> */
UNUSUAL_DEV(0x2109, 0x0711, 0x0000, 0x9999,
diff --git a/drivers/uwb/uwbd.c b/drivers/uwb/uwbd.c
index bdcb13cc1d54..01c20a260a8b 100644
--- a/drivers/uwb/uwbd.c
+++ b/drivers/uwb/uwbd.c
@@ -279,7 +279,6 @@ static int uwbd(void *param)
HZ);
if (should_stop)
break;
- try_to_freeze();
spin_lock_irqsave(&rc->uwbd.event_list_lock, flags);
if (!list_empty(&rc->uwbd.event_list)) {
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 32b88bd2c82c..2760a7ba3f30 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -1035,7 +1035,7 @@ static pci_ers_result_t vfio_pci_aer_err_detected(struct pci_dev *pdev,
return PCI_ERS_RESULT_CAN_RECOVER;
}
-static struct pci_error_handlers vfio_err_handlers = {
+static const struct pci_error_handlers vfio_err_handlers = {
.error_detected = vfio_pci_aer_err_detected,
};
diff --git a/drivers/vfio/platform/reset/vfio_platform_amdxgbe.c b/drivers/vfio/platform/reset/vfio_platform_amdxgbe.c
index da5356f48d0b..d4030d0c38e9 100644
--- a/drivers/vfio/platform/reset/vfio_platform_amdxgbe.c
+++ b/drivers/vfio/platform/reset/vfio_platform_amdxgbe.c
@@ -110,7 +110,7 @@ int vfio_platform_amdxgbe_reset(struct vfio_platform_device *vdev)
usleep_range(10, 15);
count = 2000;
- while (count-- && (ioread32(xgmac_regs->ioaddr + DMA_MR) & 1))
+ while (--count && (ioread32(xgmac_regs->ioaddr + DMA_MR) & 1))
usleep_range(500, 600);
if (!count)
diff --git a/drivers/vfio/platform/vfio_platform.c b/drivers/vfio/platform/vfio_platform.c
index f1625dcfbb23..b1cc3a768784 100644
--- a/drivers/vfio/platform/vfio_platform.c
+++ b/drivers/vfio/platform/vfio_platform.c
@@ -92,7 +92,6 @@ static struct platform_driver vfio_platform_driver = {
.remove = vfio_platform_remove,
.driver = {
.name = "vfio-platform",
- .owner = THIS_MODULE,
},
};
diff --git a/drivers/vfio/platform/vfio_platform_common.c b/drivers/vfio/platform/vfio_platform_common.c
index a1c50d630792..418cdd9ba3f4 100644
--- a/drivers/vfio/platform/vfio_platform_common.c
+++ b/drivers/vfio/platform/vfio_platform_common.c
@@ -51,13 +51,10 @@ static vfio_platform_reset_fn_t vfio_platform_lookup_reset(const char *compat,
static void vfio_platform_get_reset(struct vfio_platform_device *vdev)
{
- char modname[256];
-
vdev->reset = vfio_platform_lookup_reset(vdev->compat,
&vdev->reset_module);
if (!vdev->reset) {
- snprintf(modname, 256, "vfio-reset:%s", vdev->compat);
- request_module(modname);
+ request_module("vfio-reset:%s", vdev->compat);
vdev->reset = vfio_platform_lookup_reset(vdev->compat,
&vdev->reset_module);
}
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
index de632da2e22f..82f25cc1c460 100644
--- a/drivers/vfio/vfio.c
+++ b/drivers/vfio/vfio.c
@@ -99,7 +99,7 @@ struct vfio_device {
#ifdef CONFIG_VFIO_NOIOMMU
static bool noiommu __read_mostly;
-module_param_named(enable_unsafe_noiommu_support,
+module_param_named(enable_unsafe_noiommu_mode,
noiommu, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(enable_unsafe_noiommu_mode, "Enable UNSAFE, no-IOMMU mode. This mode provides no device isolation, no DMA translation, no host kernel protection, cannot be used for device assignment to virtual machines, requires RAWIO permissions, and will taint the kernel. If you do not know what this is for, step away. (default: false)");
#endif
@@ -185,7 +185,7 @@ static long vfio_noiommu_ioctl(void *iommu_data,
unsigned int cmd, unsigned long arg)
{
if (cmd == VFIO_CHECK_EXTENSION)
- return arg == VFIO_NOIOMMU_IOMMU ? 1 : 0;
+ return noiommu && (arg == VFIO_NOIOMMU_IOMMU) ? 1 : 0;
return -ENOTTY;
}
@@ -207,7 +207,7 @@ static void vfio_noiommu_detach_group(void *iommu_data,
{
}
-static struct vfio_iommu_driver_ops vfio_noiommu_ops = {
+static const struct vfio_iommu_driver_ops vfio_noiommu_ops = {
.name = "vfio-noiommu",
.owner = THIS_MODULE,
.open = vfio_noiommu_open,
@@ -216,25 +216,6 @@ static struct vfio_iommu_driver_ops vfio_noiommu_ops = {
.attach_group = vfio_noiommu_attach_group,
.detach_group = vfio_noiommu_detach_group,
};
-
-static struct vfio_iommu_driver vfio_noiommu_driver = {
- .ops = &vfio_noiommu_ops,
-};
-
-/*
- * Wrap IOMMU drivers, the noiommu driver is the one and only driver for
- * noiommu groups (and thus containers) and not available for normal groups.
- */
-#define vfio_for_each_iommu_driver(con, pos) \
- for (pos = con->noiommu ? &vfio_noiommu_driver : \
- list_first_entry(&vfio.iommu_drivers_list, \
- struct vfio_iommu_driver, vfio_next); \
- (con->noiommu ? pos != NULL : \
- &pos->vfio_next != &vfio.iommu_drivers_list); \
- pos = con->noiommu ? NULL : list_next_entry(pos, vfio_next))
-#else
-#define vfio_for_each_iommu_driver(con, pos) \
- list_for_each_entry(pos, &vfio.iommu_drivers_list, vfio_next)
#endif
@@ -343,7 +324,7 @@ static void vfio_group_unlock_and_free(struct vfio_group *group)
* Group objects - create, release, get, put, search
*/
static struct vfio_group *vfio_create_group(struct iommu_group *iommu_group,
- bool noiommu)
+ bool iommu_present)
{
struct vfio_group *group, *tmp;
struct device *dev;
@@ -361,7 +342,7 @@ static struct vfio_group *vfio_create_group(struct iommu_group *iommu_group,
atomic_set(&group->container_users, 0);
atomic_set(&group->opened, 0);
group->iommu_group = iommu_group;
- group->noiommu = noiommu;
+ group->noiommu = !iommu_present;
group->nb.notifier_call = vfio_iommu_group_notifier;
@@ -397,7 +378,7 @@ static struct vfio_group *vfio_create_group(struct iommu_group *iommu_group,
dev = device_create(vfio.class, NULL,
MKDEV(MAJOR(vfio.group_devt), minor),
- group, "%s%d", noiommu ? "noiommu-" : "",
+ group, "%s%d", group->noiommu ? "noiommu-" : "",
iommu_group_id(iommu_group));
if (IS_ERR(dev)) {
vfio_free_group_minor(minor);
@@ -682,7 +663,7 @@ static int vfio_group_nb_add_dev(struct vfio_group *group, struct device *dev)
return 0;
/* TODO Prevent device auto probing */
- WARN("Device %s added to live group %d!\n", dev_name(dev),
+ WARN(1, "Device %s added to live group %d!\n", dev_name(dev),
iommu_group_id(group->iommu_group));
return 0;
@@ -786,8 +767,7 @@ int vfio_add_group_dev(struct device *dev,
group = vfio_group_get_from_iommu(iommu_group);
if (!group) {
- group = vfio_create_group(iommu_group,
- !iommu_present(dev->bus));
+ group = vfio_create_group(iommu_group, iommu_present(dev->bus));
if (IS_ERR(group)) {
iommu_group_put(iommu_group);
return PTR_ERR(group);
@@ -999,7 +979,16 @@ static long vfio_ioctl_check_extension(struct vfio_container *container,
*/
if (!driver) {
mutex_lock(&vfio.iommu_drivers_lock);
- vfio_for_each_iommu_driver(container, driver) {
+ list_for_each_entry(driver, &vfio.iommu_drivers_list,
+ vfio_next) {
+
+#ifdef CONFIG_VFIO_NOIOMMU
+ if (!list_empty(&container->group_list) &&
+ (container->noiommu !=
+ (driver->ops == &vfio_noiommu_ops)))
+ continue;
+#endif
+
if (!try_module_get(driver->ops->owner))
continue;
@@ -1068,9 +1057,18 @@ static long vfio_ioctl_set_iommu(struct vfio_container *container,
}
mutex_lock(&vfio.iommu_drivers_lock);
- vfio_for_each_iommu_driver(container, driver) {
+ list_for_each_entry(driver, &vfio.iommu_drivers_list, vfio_next) {
void *data;
+#ifdef CONFIG_VFIO_NOIOMMU
+ /*
+ * Only noiommu containers can use vfio-noiommu and noiommu
+ * containers can only use vfio-noiommu.
+ */
+ if (container->noiommu != (driver->ops == &vfio_noiommu_ops))
+ continue;
+#endif
+
if (!try_module_get(driver->ops->owner))
continue;
@@ -1799,6 +1797,9 @@ static int __init vfio_init(void)
request_module_nowait("vfio_iommu_type1");
request_module_nowait("vfio_iommu_spapr_tce");
+#ifdef CONFIG_VFIO_NOIOMMU
+ vfio_register_iommu_driver(&vfio_noiommu_ops);
+#endif
return 0;
err_cdev_add:
@@ -1815,6 +1816,9 @@ static void __exit vfio_cleanup(void)
{
WARN_ON(!list_empty(&vfio.group_list));
+#ifdef CONFIG_VFIO_NOIOMMU
+ vfio_unregister_iommu_driver(&vfio_noiommu_ops);
+#endif
idr_destroy(&vfio.group_idr);
cdev_del(&vfio.group_cdev);
unregister_chrdev_region(vfio.group_devt, MINORMASK);
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index 59d47cb638d5..6f1ea3dddbad 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -995,7 +995,7 @@ static long vfio_iommu_type1_ioctl(void *iommu_data,
if (info.argsz < minsz)
return -EINVAL;
- info.flags = 0;
+ info.flags = VFIO_IOMMU_INFO_PGSIZES;
info.iova_pgsizes = vfio_pgsize_bitmap(iommu);
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index eec2f11809ff..ad2146a9ab2d 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -819,7 +819,7 @@ long vhost_vring_ioctl(struct vhost_dev *d, int ioctl, void __user *argp)
BUILD_BUG_ON(__alignof__ *vq->used > VRING_USED_ALIGN_SIZE);
if ((a.avail_user_addr & (VRING_AVAIL_ALIGN_SIZE - 1)) ||
(a.used_user_addr & (VRING_USED_ALIGN_SIZE - 1)) ||
- (a.log_guest_addr & (sizeof(u64) - 1))) {
+ (a.log_guest_addr & (VRING_USED_ALIGN_SIZE - 1))) {
r = -EINVAL;
break;
}
@@ -1369,7 +1369,7 @@ int vhost_get_vq_desc(struct vhost_virtqueue *vq,
/* Grab the next descriptor number they're advertising, and increment
* the index we've seen. */
if (unlikely(__get_user(ring_head,
- &vq->avail->ring[last_avail_idx % vq->num]))) {
+ &vq->avail->ring[last_avail_idx & (vq->num - 1)]))) {
vq_err(vq, "Failed to read head: idx %d address %p\n",
last_avail_idx,
&vq->avail->ring[last_avail_idx % vq->num]);
@@ -1489,7 +1489,7 @@ static int __vhost_add_used_n(struct vhost_virtqueue *vq,
u16 old, new;
int start;
- start = vq->last_used_idx % vq->num;
+ start = vq->last_used_idx & (vq->num - 1);
used = vq->used->ring + start;
if (count == 1) {
if (__put_user(heads[0].id, &used->id)) {
@@ -1531,7 +1531,7 @@ int vhost_add_used_n(struct vhost_virtqueue *vq, struct vring_used_elem *heads,
{
int start, n, r;
- start = vq->last_used_idx % vq->num;
+ start = vq->last_used_idx & (vq->num - 1);
n = vq->num - start;
if (n < count) {
r = __vhost_add_used_n(vq, heads, n);
diff --git a/drivers/video/backlight/adp8860_bl.c b/drivers/video/backlight/adp8860_bl.c
index 98ffe71e8af2..510e559c060e 100644
--- a/drivers/video/backlight/adp8860_bl.c
+++ b/drivers/video/backlight/adp8860_bl.c
@@ -566,11 +566,13 @@ static ssize_t adp8860_bl_ambient_light_level_show(struct device *dev,
mutex_lock(&data->lock);
error = adp8860_read(data->client, ADP8860_PH1LEVL, &reg_val);
- ret_val = reg_val;
- error |= adp8860_read(data->client, ADP8860_PH1LEVH, &reg_val);
+ if (!error) {
+ ret_val = reg_val;
+ error = adp8860_read(data->client, ADP8860_PH1LEVH, &reg_val);
+ }
mutex_unlock(&data->lock);
- if (error < 0)
+ if (error)
return error;
/* Return 13-bit conversion value for the first light sensor */
@@ -621,10 +623,12 @@ static ssize_t adp8860_bl_ambient_light_zone_store(struct device *dev,
/* Set user supplied ambient light zone */
mutex_lock(&data->lock);
- adp8860_read(data->client, ADP8860_CFGR, &reg_val);
- reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT);
- reg_val |= (val - 1) << CFGR_BLV_SHIFT;
- adp8860_write(data->client, ADP8860_CFGR, reg_val);
+ ret = adp8860_read(data->client, ADP8860_CFGR, &reg_val);
+ if (!ret) {
+ reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT);
+ reg_val |= (val - 1) << CFGR_BLV_SHIFT;
+ adp8860_write(data->client, ADP8860_CFGR, reg_val);
+ }
mutex_unlock(&data->lock);
}
diff --git a/drivers/video/backlight/adp8870_bl.c b/drivers/video/backlight/adp8870_bl.c
index 9d738352d7d4..21acac90fd77 100644
--- a/drivers/video/backlight/adp8870_bl.c
+++ b/drivers/video/backlight/adp8870_bl.c
@@ -807,10 +807,12 @@ static ssize_t adp8870_bl_ambient_light_zone_store(struct device *dev,
/* Set user supplied ambient light zone */
mutex_lock(&data->lock);
- adp8870_read(data->client, ADP8870_CFGR, &reg_val);
- reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT);
- reg_val |= (val - 1) << CFGR_BLV_SHIFT;
- adp8870_write(data->client, ADP8870_CFGR, reg_val);
+ ret = adp8870_read(data->client, ADP8870_CFGR, &reg_val);
+ if (!ret) {
+ reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT);
+ reg_val |= (val - 1) << CFGR_BLV_SHIFT;
+ adp8870_write(data->client, ADP8870_CFGR, reg_val);
+ }
mutex_unlock(&data->lock);
}
diff --git a/drivers/video/backlight/gpio_backlight.c b/drivers/video/backlight/gpio_backlight.c
index 5fbbc2ebdf93..18134416b154 100644
--- a/drivers/video/backlight/gpio_backlight.c
+++ b/drivers/video/backlight/gpio_backlight.c
@@ -89,6 +89,7 @@ static int gpio_backlight_probe(struct platform_device *pdev)
struct backlight_device *bl;
struct gpio_backlight *gbl;
struct device_node *np = pdev->dev.of_node;
+ unsigned long flags = GPIOF_DIR_OUT;
int ret;
if (!pdata && !np) {
@@ -114,9 +115,12 @@ static int gpio_backlight_probe(struct platform_device *pdev)
gbl->def_value = pdata->def_value;
}
- ret = devm_gpio_request_one(gbl->dev, gbl->gpio, GPIOF_DIR_OUT |
- (gbl->active ? GPIOF_INIT_LOW
- : GPIOF_INIT_HIGH),
+ if (gbl->active)
+ flags |= gbl->def_value ? GPIOF_INIT_HIGH : GPIOF_INIT_LOW;
+ else
+ flags |= gbl->def_value ? GPIOF_INIT_LOW : GPIOF_INIT_HIGH;
+
+ ret = devm_gpio_request_one(gbl->dev, gbl->gpio, flags,
pdata ? pdata->name : "backlight");
if (ret < 0) {
dev_err(&pdev->dev, "unable to request GPIO\n");
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index ae3c6b6fd5db..64f9e1b8655f 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -198,7 +198,9 @@ static int pwm_backlight_probe(struct platform_device *pdev)
struct platform_pwm_backlight_data defdata;
struct backlight_properties props;
struct backlight_device *bl;
+ struct device_node *node = pdev->dev.of_node;
struct pwm_bl_data *pb;
+ int initial_blank = FB_BLANK_UNBLANK;
int ret;
if (!data) {
@@ -242,7 +244,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
pb->enabled = false;
pb->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable",
- GPIOD_OUT_HIGH);
+ GPIOD_ASIS);
if (IS_ERR(pb->enable_gpio)) {
ret = PTR_ERR(pb->enable_gpio);
goto err_alloc;
@@ -264,15 +266,32 @@ static int pwm_backlight_probe(struct platform_device *pdev)
pb->enable_gpio = gpio_to_desc(data->enable_gpio);
}
+ if (pb->enable_gpio) {
+ /*
+ * If the driver is probed from the device tree and there is a
+ * phandle link pointing to the backlight node, it is safe to
+ * assume that another driver will enable the backlight at the
+ * appropriate time. Therefore, if it is disabled, keep it so.
+ */
+ if (node && node->phandle &&
+ gpiod_get_direction(pb->enable_gpio) == GPIOF_DIR_OUT &&
+ gpiod_get_value(pb->enable_gpio) == 0)
+ initial_blank = FB_BLANK_POWERDOWN;
+ else
+ gpiod_direction_output(pb->enable_gpio, 1);
+ }
+
pb->power_supply = devm_regulator_get(&pdev->dev, "power");
if (IS_ERR(pb->power_supply)) {
ret = PTR_ERR(pb->power_supply);
goto err_alloc;
}
+ if (node && node->phandle && !regulator_is_enabled(pb->power_supply))
+ initial_blank = FB_BLANK_POWERDOWN;
+
pb->pwm = devm_pwm_get(&pdev->dev, NULL);
- if (IS_ERR(pb->pwm) && PTR_ERR(pb->pwm) != -EPROBE_DEFER
- && !pdev->dev.of_node) {
+ if (IS_ERR(pb->pwm) && PTR_ERR(pb->pwm) != -EPROBE_DEFER && !node) {
dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");
pb->legacy = true;
pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
@@ -309,6 +328,8 @@ static int pwm_backlight_probe(struct platform_device *pdev)
if (IS_ERR(bl)) {
dev_err(&pdev->dev, "failed to register backlight\n");
ret = PTR_ERR(bl);
+ if (pb->legacy)
+ pwm_free(pb->pwm);
goto err_alloc;
}
@@ -320,6 +341,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
}
bl->props.brightness = data->dft_brightness;
+ bl->props.power = initial_blank;
backlight_update_status(bl);
platform_set_drvdata(pdev, bl);
diff --git a/drivers/video/backlight/tps65217_bl.c b/drivers/video/backlight/tps65217_bl.c
index 61d72bffd402..fd524ad860a5 100644
--- a/drivers/video/backlight/tps65217_bl.c
+++ b/drivers/video/backlight/tps65217_bl.c
@@ -320,10 +320,19 @@ static int tps65217_bl_probe(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id tps65217_bl_of_match[] = {
+ { .compatible = "ti,tps65217-bl", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, tps65217_bl_of_match);
+#endif
+
static struct platform_driver tps65217_bl_driver = {
.probe = tps65217_bl_probe,
.driver = {
.name = "tps65217-bl",
+ .of_match_table = of_match_ptr(tps65217_bl_of_match),
},
};
diff --git a/drivers/video/fbdev/fsl-diu-fb.c b/drivers/video/fbdev/fsl-diu-fb.c
index b335c1ae8625..fe00a07c122e 100644
--- a/drivers/video/fbdev/fsl-diu-fb.c
+++ b/drivers/video/fbdev/fsl-diu-fb.c
@@ -479,7 +479,10 @@ static enum fsl_diu_monitor_port fsl_diu_name_to_port(const char *s)
port = FSL_DIU_PORT_DLVDS;
}
- return diu_ops.valid_monitor_port(port);
+ if (diu_ops.valid_monitor_port)
+ port = diu_ops.valid_monitor_port(port);
+
+ return port;
}
/*
@@ -1915,6 +1918,14 @@ static int __init fsl_diu_init(void)
#else
monitor_port = fsl_diu_name_to_port(monitor_string);
#endif
+
+ /*
+ * Must to verify set_pixel_clock. If not implement on platform,
+ * then that means that there is no platform support for the DIU.
+ */
+ if (!diu_ops.set_pixel_clock)
+ return -ENODEV;
+
pr_info("Freescale Display Interface Unit (DIU) framebuffer driver\n");
#ifdef CONFIG_NOT_COHERENT_CACHE
diff --git a/drivers/video/fbdev/omap2/dss/venc.c b/drivers/video/fbdev/omap2/dss/venc.c
index 99ca268c1cdd..d05a54922ba6 100644
--- a/drivers/video/fbdev/omap2/dss/venc.c
+++ b/drivers/video/fbdev/omap2/dss/venc.c
@@ -275,6 +275,12 @@ const struct omap_video_timings omap_dss_pal_timings = {
.vbp = 41,
.interlace = true,
+
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
};
EXPORT_SYMBOL(omap_dss_pal_timings);
@@ -290,6 +296,12 @@ const struct omap_video_timings omap_dss_ntsc_timings = {
.vbp = 31,
.interlace = true,
+
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
};
EXPORT_SYMBOL(omap_dss_ntsc_timings);
diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c
index b1877d73fa56..7062bb0975a5 100644
--- a/drivers/virtio/virtio.c
+++ b/drivers/virtio/virtio.c
@@ -412,6 +412,7 @@ static int virtio_init(void)
static void __exit virtio_exit(void)
{
bus_unregister(&virtio_bus);
+ ida_destroy(&virtio_index_ida);
}
core_initcall(virtio_init);
module_exit(virtio_exit);
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index 096b857e7b75..ee663c458b20 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -80,6 +80,12 @@ struct vring_virtqueue {
/* Last used index we've seen. */
u16 last_used_idx;
+ /* Last written value to avail->flags */
+ u16 avail_flags_shadow;
+
+ /* Last written value to avail->idx in guest byte order */
+ u16 avail_idx_shadow;
+
/* How to notify other side. FIXME: commonalize hcalls! */
bool (*notify)(struct virtqueue *vq);
@@ -109,7 +115,7 @@ static struct vring_desc *alloc_indirect(struct virtqueue *_vq,
* otherwise virt_to_phys will give us bogus addresses in the
* virtqueue.
*/
- gfp &= ~(__GFP_HIGHMEM | __GFP_HIGH);
+ gfp &= ~__GFP_HIGHMEM;
desc = kmalloc(total_sg * sizeof(struct vring_desc), gfp);
if (!desc)
@@ -235,13 +241,14 @@ static inline int virtqueue_add(struct virtqueue *_vq,
/* Put entry in available array (but don't update avail->idx until they
* do sync). */
- avail = virtio16_to_cpu(_vq->vdev, vq->vring.avail->idx) & (vq->vring.num - 1);
+ avail = vq->avail_idx_shadow & (vq->vring.num - 1);
vq->vring.avail->ring[avail] = cpu_to_virtio16(_vq->vdev, head);
/* Descriptors and available array need to be set before we expose the
* new available array entries. */
virtio_wmb(vq->weak_barriers);
- vq->vring.avail->idx = cpu_to_virtio16(_vq->vdev, virtio16_to_cpu(_vq->vdev, vq->vring.avail->idx) + 1);
+ vq->avail_idx_shadow++;
+ vq->vring.avail->idx = cpu_to_virtio16(_vq->vdev, vq->avail_idx_shadow);
vq->num_added++;
pr_debug("Added buffer head %i to %p\n", head, vq);
@@ -354,8 +361,8 @@ bool virtqueue_kick_prepare(struct virtqueue *_vq)
* event. */
virtio_mb(vq->weak_barriers);
- old = virtio16_to_cpu(_vq->vdev, vq->vring.avail->idx) - vq->num_added;
- new = virtio16_to_cpu(_vq->vdev, vq->vring.avail->idx);
+ old = vq->avail_idx_shadow - vq->num_added;
+ new = vq->avail_idx_shadow;
vq->num_added = 0;
#ifdef DEBUG
@@ -510,7 +517,7 @@ void *virtqueue_get_buf(struct virtqueue *_vq, unsigned int *len)
/* If we expect an interrupt for the next entry, tell host
* by writing event index and flush out the write before
* the read in the next get_buf call. */
- if (!(vq->vring.avail->flags & cpu_to_virtio16(_vq->vdev, VRING_AVAIL_F_NO_INTERRUPT))) {
+ if (!(vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT)) {
vring_used_event(&vq->vring) = cpu_to_virtio16(_vq->vdev, vq->last_used_idx);
virtio_mb(vq->weak_barriers);
}
@@ -537,7 +544,11 @@ void virtqueue_disable_cb(struct virtqueue *_vq)
{
struct vring_virtqueue *vq = to_vvq(_vq);
- vq->vring.avail->flags |= cpu_to_virtio16(_vq->vdev, VRING_AVAIL_F_NO_INTERRUPT);
+ if (!(vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT)) {
+ vq->avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT;
+ vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
+ }
+
}
EXPORT_SYMBOL_GPL(virtqueue_disable_cb);
@@ -565,7 +576,10 @@ unsigned virtqueue_enable_cb_prepare(struct virtqueue *_vq)
/* Depending on the VIRTIO_RING_F_EVENT_IDX feature, we need to
* either clear the flags bit or point the event index at the next
* entry. Always do both to keep code simple. */
- vq->vring.avail->flags &= cpu_to_virtio16(_vq->vdev, ~VRING_AVAIL_F_NO_INTERRUPT);
+ if (vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT) {
+ vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT;
+ vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
+ }
vring_used_event(&vq->vring) = cpu_to_virtio16(_vq->vdev, last_used_idx = vq->last_used_idx);
END_USE(vq);
return last_used_idx;
@@ -633,9 +647,12 @@ bool virtqueue_enable_cb_delayed(struct virtqueue *_vq)
/* Depending on the VIRTIO_RING_F_USED_EVENT_IDX feature, we need to
* either clear the flags bit or point the event index at the next
* entry. Always do both to keep code simple. */
- vq->vring.avail->flags &= cpu_to_virtio16(_vq->vdev, ~VRING_AVAIL_F_NO_INTERRUPT);
+ if (vq->avail_flags_shadow & VRING_AVAIL_F_NO_INTERRUPT) {
+ vq->avail_flags_shadow &= ~VRING_AVAIL_F_NO_INTERRUPT;
+ vq->vring.avail->flags = cpu_to_virtio16(_vq->vdev, vq->avail_flags_shadow);
+ }
/* TODO: tune this threshold */
- bufs = (u16)(virtio16_to_cpu(_vq->vdev, vq->vring.avail->idx) - vq->last_used_idx) * 3 / 4;
+ bufs = (u16)(vq->avail_idx_shadow - vq->last_used_idx) * 3 / 4;
vring_used_event(&vq->vring) = cpu_to_virtio16(_vq->vdev, vq->last_used_idx + bufs);
virtio_mb(vq->weak_barriers);
if (unlikely((u16)(virtio16_to_cpu(_vq->vdev, vq->vring.used->idx) - vq->last_used_idx) > bufs)) {
@@ -670,7 +687,8 @@ void *virtqueue_detach_unused_buf(struct virtqueue *_vq)
/* detach_buf clears data, so grab it now. */
buf = vq->data[i];
detach_buf(vq, i);
- vq->vring.avail->idx = cpu_to_virtio16(_vq->vdev, virtio16_to_cpu(_vq->vdev, vq->vring.avail->idx) - 1);
+ vq->avail_idx_shadow--;
+ vq->vring.avail->idx = cpu_to_virtio16(_vq->vdev, vq->avail_idx_shadow);
END_USE(vq);
return buf;
}
@@ -735,6 +753,8 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,
vq->weak_barriers = weak_barriers;
vq->broken = false;
vq->last_used_idx = 0;
+ vq->avail_flags_shadow = 0;
+ vq->avail_idx_shadow = 0;
vq->num_added = 0;
list_add_tail(&vq->vq.list, &vdev->vqs);
#ifdef DEBUG
@@ -746,8 +766,10 @@ struct virtqueue *vring_new_virtqueue(unsigned int index,
vq->event = virtio_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX);
/* No callback? Tell other side not to bother us. */
- if (!callback)
- vq->vring.avail->flags |= cpu_to_virtio16(vdev, VRING_AVAIL_F_NO_INTERRUPT);
+ if (!callback) {
+ vq->avail_flags_shadow |= VRING_AVAIL_F_NO_INTERRUPT;
+ vq->vring.avail->flags = cpu_to_virtio16(vdev, vq->avail_flags_shadow);
+ }
/* Put everything in free lists. */
vq->free_head = 0;
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 7a8a6c6952e9..1c427beffadd 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -446,7 +446,7 @@ config MAX63XX_WATCHDOG
config IMX2_WDT
tristate "IMX2+ Watchdog"
- depends on ARCH_MXC
+ depends on ARCH_MXC || ARCH_LAYERSCAPE
select REGMAP_MMIO
select WATCHDOG_CORE
help
diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c
index 6ad9df948711..b751f43d76ed 100644
--- a/drivers/watchdog/mtk_wdt.c
+++ b/drivers/watchdog/mtk_wdt.c
@@ -123,6 +123,7 @@ static int mtk_wdt_stop(struct watchdog_device *wdt_dev)
reg = readl(wdt_base + WDT_MODE);
reg &= ~WDT_MODE_EN;
+ reg |= WDT_MODE_KEY;
iowrite32(reg, wdt_base + WDT_MODE);
return 0;
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index d96bee017fd3..6f17c935a6cf 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -205,7 +205,7 @@ static int omap_wdt_set_timeout(struct watchdog_device *wdog,
static unsigned int omap_wdt_get_timeleft(struct watchdog_device *wdog)
{
- struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
+ struct omap_wdt_dev *wdev = to_omap_wdt_dev(wdog);
void __iomem *base = wdev->base;
u32 value;
diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c
index 4224b3ec83a5..313cd1c6fda0 100644
--- a/drivers/watchdog/pnx4008_wdt.c
+++ b/drivers/watchdog/pnx4008_wdt.c
@@ -80,7 +80,7 @@ static unsigned int heartbeat = DEFAULT_HEARTBEAT;
static DEFINE_SPINLOCK(io_lock);
static void __iomem *wdt_base;
-struct clk *wdt_clk;
+static struct clk *wdt_clk;
static int pnx4008_wdt_start(struct watchdog_device *wdd)
{
@@ -161,7 +161,7 @@ static int pnx4008_wdt_probe(struct platform_device *pdev)
if (IS_ERR(wdt_clk))
return PTR_ERR(wdt_clk);
- ret = clk_enable(wdt_clk);
+ ret = clk_prepare_enable(wdt_clk);
if (ret)
return ret;
@@ -184,7 +184,7 @@ static int pnx4008_wdt_probe(struct platform_device *pdev)
return 0;
disable_clk:
- clk_disable(wdt_clk);
+ clk_disable_unprepare(wdt_clk);
return ret;
}
@@ -192,7 +192,7 @@ static int pnx4008_wdt_remove(struct platform_device *pdev)
{
watchdog_unregister_device(&pnx4008_wdd);
- clk_disable(wdt_clk);
+ clk_disable_unprepare(wdt_clk);
return 0;
}
diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c
index 7f97cdd53f29..9ec57608da82 100644
--- a/drivers/watchdog/tegra_wdt.c
+++ b/drivers/watchdog/tegra_wdt.c
@@ -140,8 +140,10 @@ static int tegra_wdt_set_timeout(struct watchdog_device *wdd,
{
wdd->timeout = timeout;
- if (watchdog_active(wdd))
+ if (watchdog_active(wdd)) {
+ tegra_wdt_stop(wdd);
return tegra_wdt_start(wdd);
+ }
return 0;
}
diff --git a/drivers/watchdog/w83977f_wdt.c b/drivers/watchdog/w83977f_wdt.c
index 91bf55a20024..20e2bba10400 100644
--- a/drivers/watchdog/w83977f_wdt.c
+++ b/drivers/watchdog/w83977f_wdt.c
@@ -224,7 +224,7 @@ static int wdt_keepalive(void)
static int wdt_set_timeout(int t)
{
- int tmrval;
+ unsigned int tmrval;
/*
* Convert seconds to watchdog counter time units, rounding up.
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index aa8a7f71f310..9b7a35c9e51d 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -1,6 +1,6 @@
obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o
obj-$(CONFIG_X86) += fallback.o
-obj-y += grant-table.o features.o balloon.o manage.o preempt.o
+obj-y += grant-table.o features.o balloon.o manage.o preempt.o time.o
obj-y += events/
obj-y += xenbus/
diff --git a/drivers/xen/acpi.c b/drivers/xen/acpi.c
index 90307c0b630c..6893c79fd2a1 100644
--- a/drivers/xen/acpi.c
+++ b/drivers/xen/acpi.c
@@ -58,7 +58,7 @@ static int xen_acpi_notify_hypervisor_state(u8 sleep_state,
bits, val_a, val_b))
return -1;
- HYPERVISOR_dom0_op(&op);
+ HYPERVISOR_platform_op(&op);
return 1;
}
diff --git a/drivers/xen/efi.c b/drivers/xen/efi.c
index f745db270171..be7e56a338e8 100644
--- a/drivers/xen/efi.c
+++ b/drivers/xen/efi.c
@@ -42,7 +42,7 @@ static efi_status_t xen_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc)
{
struct xen_platform_op op = INIT_EFI_OP(get_time);
- if (HYPERVISOR_dom0_op(&op) < 0)
+ if (HYPERVISOR_platform_op(&op) < 0)
return EFI_UNSUPPORTED;
if (tm) {
@@ -67,7 +67,7 @@ static efi_status_t xen_efi_set_time(efi_time_t *tm)
BUILD_BUG_ON(sizeof(*tm) != sizeof(efi_data(op).u.set_time));
memcpy(&efi_data(op).u.set_time, tm, sizeof(*tm));
- if (HYPERVISOR_dom0_op(&op) < 0)
+ if (HYPERVISOR_platform_op(&op) < 0)
return EFI_UNSUPPORTED;
return efi_data(op).status;
@@ -79,7 +79,7 @@ static efi_status_t xen_efi_get_wakeup_time(efi_bool_t *enabled,
{
struct xen_platform_op op = INIT_EFI_OP(get_wakeup_time);
- if (HYPERVISOR_dom0_op(&op) < 0)
+ if (HYPERVISOR_platform_op(&op) < 0)
return EFI_UNSUPPORTED;
if (tm) {
@@ -108,7 +108,7 @@ static efi_status_t xen_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm)
else
efi_data(op).misc |= XEN_EFI_SET_WAKEUP_TIME_ENABLE_ONLY;
- if (HYPERVISOR_dom0_op(&op) < 0)
+ if (HYPERVISOR_platform_op(&op) < 0)
return EFI_UNSUPPORTED;
return efi_data(op).status;
@@ -129,7 +129,7 @@ static efi_status_t xen_efi_get_variable(efi_char16_t *name,
efi_data(op).u.get_variable.size = *data_size;
set_xen_guest_handle(efi_data(op).u.get_variable.data, data);
- if (HYPERVISOR_dom0_op(&op) < 0)
+ if (HYPERVISOR_platform_op(&op) < 0)
return EFI_UNSUPPORTED;
*data_size = efi_data(op).u.get_variable.size;
@@ -152,7 +152,7 @@ static efi_status_t xen_efi_get_next_variable(unsigned long *name_size,
memcpy(&efi_data(op).u.get_next_variable_name.vendor_guid, vendor,
sizeof(*vendor));
- if (HYPERVISOR_dom0_op(&op) < 0)
+ if (HYPERVISOR_platform_op(&op) < 0)
return EFI_UNSUPPORTED;
*name_size = efi_data(op).u.get_next_variable_name.size;
@@ -178,7 +178,7 @@ static efi_status_t xen_efi_set_variable(efi_char16_t *name,
efi_data(op).u.set_variable.size = data_size;
set_xen_guest_handle(efi_data(op).u.set_variable.data, data);
- if (HYPERVISOR_dom0_op(&op) < 0)
+ if (HYPERVISOR_platform_op(&op) < 0)
return EFI_UNSUPPORTED;
return efi_data(op).status;
@@ -196,7 +196,7 @@ static efi_status_t xen_efi_query_variable_info(u32 attr,
efi_data(op).u.query_variable_info.attr = attr;
- if (HYPERVISOR_dom0_op(&op) < 0)
+ if (HYPERVISOR_platform_op(&op) < 0)
return EFI_UNSUPPORTED;
*storage_space = efi_data(op).u.query_variable_info.max_store_size;
@@ -210,7 +210,7 @@ static efi_status_t xen_efi_get_next_high_mono_count(u32 *count)
{
struct xen_platform_op op = INIT_EFI_OP(get_next_high_monotonic_count);
- if (HYPERVISOR_dom0_op(&op) < 0)
+ if (HYPERVISOR_platform_op(&op) < 0)
return EFI_UNSUPPORTED;
*count = efi_data(op).misc;
@@ -232,7 +232,7 @@ static efi_status_t xen_efi_update_capsule(efi_capsule_header_t **capsules,
efi_data(op).u.update_capsule.capsule_count = count;
efi_data(op).u.update_capsule.sg_list = sg_list;
- if (HYPERVISOR_dom0_op(&op) < 0)
+ if (HYPERVISOR_platform_op(&op) < 0)
return EFI_UNSUPPORTED;
return efi_data(op).status;
@@ -252,7 +252,7 @@ static efi_status_t xen_efi_query_capsule_caps(efi_capsule_header_t **capsules,
capsules);
efi_data(op).u.query_capsule_capabilities.capsule_count = count;
- if (HYPERVISOR_dom0_op(&op) < 0)
+ if (HYPERVISOR_platform_op(&op) < 0)
return EFI_UNSUPPORTED;
*max_size = efi_data(op).u.query_capsule_capabilities.max_capsule_size;
@@ -331,7 +331,7 @@ efi_system_table_t __init *xen_efi_probe(void)
};
union xenpf_efi_info *info = &op.u.firmware_info.u.efi_info;
- if (!xen_initial_domain() || HYPERVISOR_dom0_op(&op) < 0)
+ if (!xen_initial_domain() || HYPERVISOR_platform_op(&op) < 0)
return NULL;
/* Here we know that Xen runs on EFI platform. */
@@ -347,7 +347,7 @@ efi_system_table_t __init *xen_efi_probe(void)
info->vendor.bufsz = sizeof(vendor);
set_xen_guest_handle(info->vendor.name, vendor);
- if (HYPERVISOR_dom0_op(&op) == 0) {
+ if (HYPERVISOR_platform_op(&op) == 0) {
efi_systab_xen.fw_vendor = __pa_symbol(vendor);
efi_systab_xen.fw_revision = info->vendor.revision;
} else
@@ -357,14 +357,14 @@ efi_system_table_t __init *xen_efi_probe(void)
op.u.firmware_info.type = XEN_FW_EFI_INFO;
op.u.firmware_info.index = XEN_FW_EFI_VERSION;
- if (HYPERVISOR_dom0_op(&op) == 0)
+ if (HYPERVISOR_platform_op(&op) == 0)
efi_systab_xen.hdr.revision = info->version;
op.cmd = XENPF_firmware_info;
op.u.firmware_info.type = XEN_FW_EFI_INFO;
op.u.firmware_info.index = XEN_FW_EFI_RT_VERSION;
- if (HYPERVISOR_dom0_op(&op) == 0)
+ if (HYPERVISOR_platform_op(&op) == 0)
efi.runtime_version = info->version;
return &efi_systab_xen;
diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c
index 849500e4e14d..524c22146429 100644
--- a/drivers/xen/events/events_base.c
+++ b/drivers/xen/events/events_base.c
@@ -39,6 +39,7 @@
#include <asm/irq.h>
#include <asm/idle.h>
#include <asm/io_apic.h>
+#include <asm/i8259.h>
#include <asm/xen/pci.h>
#endif
#include <asm/sync_bitops.h>
@@ -420,7 +421,7 @@ static int __must_check xen_allocate_irq_gsi(unsigned gsi)
return xen_allocate_irq_dynamic();
/* Legacy IRQ descriptors are already allocated by the arch. */
- if (gsi < NR_IRQS_LEGACY)
+ if (gsi < nr_legacy_irqs())
irq = gsi;
else
irq = irq_alloc_desc_at(gsi, -1);
@@ -446,7 +447,7 @@ static void xen_free_irq(unsigned irq)
kfree(info);
/* Legacy IRQ descriptors are managed by the arch. */
- if (irq < NR_IRQS_LEGACY)
+ if (irq < nr_legacy_irqs())
return;
irq_free_desc(irq);
diff --git a/drivers/xen/events/events_fifo.c b/drivers/xen/events/events_fifo.c
index e3e9e3d46d1b..96a1b8da5371 100644
--- a/drivers/xen/events/events_fifo.c
+++ b/drivers/xen/events/events_fifo.c
@@ -281,7 +281,8 @@ static void handle_irq_for_port(unsigned port)
static void consume_one_event(unsigned cpu,
struct evtchn_fifo_control_block *control_block,
- unsigned priority, unsigned long *ready)
+ unsigned priority, unsigned long *ready,
+ bool drop)
{
struct evtchn_fifo_queue *q = &per_cpu(cpu_queue, cpu);
uint32_t head;
@@ -313,13 +314,17 @@ static void consume_one_event(unsigned cpu,
if (head == 0)
clear_bit(priority, ready);
- if (evtchn_fifo_is_pending(port) && !evtchn_fifo_is_masked(port))
- handle_irq_for_port(port);
+ if (evtchn_fifo_is_pending(port) && !evtchn_fifo_is_masked(port)) {
+ if (unlikely(drop))
+ pr_warn("Dropping pending event for port %u\n", port);
+ else
+ handle_irq_for_port(port);
+ }
q->head[priority] = head;
}
-static void evtchn_fifo_handle_events(unsigned cpu)
+static void __evtchn_fifo_handle_events(unsigned cpu, bool drop)
{
struct evtchn_fifo_control_block *control_block;
unsigned long ready;
@@ -331,11 +336,16 @@ static void evtchn_fifo_handle_events(unsigned cpu)
while (ready) {
q = find_first_bit(&ready, EVTCHN_FIFO_MAX_QUEUES);
- consume_one_event(cpu, control_block, q, &ready);
+ consume_one_event(cpu, control_block, q, &ready, drop);
ready |= xchg(&control_block->ready, 0);
}
}
+static void evtchn_fifo_handle_events(unsigned cpu)
+{
+ __evtchn_fifo_handle_events(cpu, false);
+}
+
static void evtchn_fifo_resume(void)
{
unsigned cpu;
@@ -420,6 +430,9 @@ static int evtchn_fifo_cpu_notification(struct notifier_block *self,
if (!per_cpu(cpu_control_block, cpu))
ret = evtchn_fifo_alloc_control_block(cpu);
break;
+ case CPU_DEAD:
+ __evtchn_fifo_handle_events(cpu, true);
+ break;
default:
break;
}
diff --git a/drivers/xen/evtchn.c b/drivers/xen/evtchn.c
index 00f40f051d95..38272ad24551 100644
--- a/drivers/xen/evtchn.c
+++ b/drivers/xen/evtchn.c
@@ -49,6 +49,8 @@
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/cpu.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
#include <xen/xen.h>
#include <xen/events.h>
@@ -58,10 +60,10 @@
struct per_user_data {
struct mutex bind_mutex; /* serialize bind/unbind operations */
struct rb_root evtchns;
+ unsigned int nr_evtchns;
/* Notification ring, accessed via /dev/xen/evtchn. */
-#define EVTCHN_RING_SIZE (PAGE_SIZE / sizeof(evtchn_port_t))
-#define EVTCHN_RING_MASK(_i) ((_i)&(EVTCHN_RING_SIZE-1))
+ unsigned int ring_size;
evtchn_port_t *ring;
unsigned int ring_cons, ring_prod, ring_overflow;
struct mutex ring_cons_mutex; /* protect against concurrent readers */
@@ -80,10 +82,41 @@ struct user_evtchn {
bool enabled;
};
+static evtchn_port_t *evtchn_alloc_ring(unsigned int size)
+{
+ evtchn_port_t *ring;
+ size_t s = size * sizeof(*ring);
+
+ ring = kmalloc(s, GFP_KERNEL);
+ if (!ring)
+ ring = vmalloc(s);
+
+ return ring;
+}
+
+static void evtchn_free_ring(evtchn_port_t *ring)
+{
+ kvfree(ring);
+}
+
+static unsigned int evtchn_ring_offset(struct per_user_data *u,
+ unsigned int idx)
+{
+ return idx & (u->ring_size - 1);
+}
+
+static evtchn_port_t *evtchn_ring_entry(struct per_user_data *u,
+ unsigned int idx)
+{
+ return u->ring + evtchn_ring_offset(u, idx);
+}
+
static int add_evtchn(struct per_user_data *u, struct user_evtchn *evtchn)
{
struct rb_node **new = &(u->evtchns.rb_node), *parent = NULL;
+ u->nr_evtchns++;
+
while (*new) {
struct user_evtchn *this;
@@ -107,6 +140,7 @@ static int add_evtchn(struct per_user_data *u, struct user_evtchn *evtchn)
static void del_evtchn(struct per_user_data *u, struct user_evtchn *evtchn)
{
+ u->nr_evtchns--;
rb_erase(&evtchn->node, &u->evtchns);
kfree(evtchn);
}
@@ -144,8 +178,8 @@ static irqreturn_t evtchn_interrupt(int irq, void *data)
spin_lock(&u->ring_prod_lock);
- if ((u->ring_prod - u->ring_cons) < EVTCHN_RING_SIZE) {
- u->ring[EVTCHN_RING_MASK(u->ring_prod)] = evtchn->port;
+ if ((u->ring_prod - u->ring_cons) < u->ring_size) {
+ *evtchn_ring_entry(u, u->ring_prod) = evtchn->port;
wmb(); /* Ensure ring contents visible */
if (u->ring_cons == u->ring_prod++) {
wake_up_interruptible(&u->evtchn_wait);
@@ -200,10 +234,10 @@ static ssize_t evtchn_read(struct file *file, char __user *buf,
}
/* Byte lengths of two chunks. Chunk split (if any) is at ring wrap. */
- if (((c ^ p) & EVTCHN_RING_SIZE) != 0) {
- bytes1 = (EVTCHN_RING_SIZE - EVTCHN_RING_MASK(c)) *
+ if (((c ^ p) & u->ring_size) != 0) {
+ bytes1 = (u->ring_size - evtchn_ring_offset(u, c)) *
sizeof(evtchn_port_t);
- bytes2 = EVTCHN_RING_MASK(p) * sizeof(evtchn_port_t);
+ bytes2 = evtchn_ring_offset(u, p) * sizeof(evtchn_port_t);
} else {
bytes1 = (p - c) * sizeof(evtchn_port_t);
bytes2 = 0;
@@ -219,7 +253,7 @@ static ssize_t evtchn_read(struct file *file, char __user *buf,
rc = -EFAULT;
rmb(); /* Ensure that we see the port before we copy it. */
- if (copy_to_user(buf, &u->ring[EVTCHN_RING_MASK(c)], bytes1) ||
+ if (copy_to_user(buf, evtchn_ring_entry(u, c), bytes1) ||
((bytes2 != 0) &&
copy_to_user(&buf[bytes1], &u->ring[0], bytes2)))
goto unlock_out;
@@ -278,6 +312,66 @@ static ssize_t evtchn_write(struct file *file, const char __user *buf,
return rc;
}
+static int evtchn_resize_ring(struct per_user_data *u)
+{
+ unsigned int new_size;
+ evtchn_port_t *new_ring, *old_ring;
+ unsigned int p, c;
+
+ /*
+ * Ensure the ring is large enough to capture all possible
+ * events. i.e., one free slot for each bound event.
+ */
+ if (u->nr_evtchns <= u->ring_size)
+ return 0;
+
+ if (u->ring_size == 0)
+ new_size = 64;
+ else
+ new_size = 2 * u->ring_size;
+
+ new_ring = evtchn_alloc_ring(new_size);
+ if (!new_ring)
+ return -ENOMEM;
+
+ old_ring = u->ring;
+
+ /*
+ * Access to the ring contents is serialized by either the
+ * prod /or/ cons lock so take both when resizing.
+ */
+ mutex_lock(&u->ring_cons_mutex);
+ spin_lock_irq(&u->ring_prod_lock);
+
+ /*
+ * Copy the old ring contents to the new ring.
+ *
+ * If the ring contents crosses the end of the current ring,
+ * it needs to be copied in two chunks.
+ *
+ * +---------+ +------------------+
+ * |34567 12| -> | 1234567 |
+ * +-----p-c-+ +------------------+
+ */
+ p = evtchn_ring_offset(u, u->ring_prod);
+ c = evtchn_ring_offset(u, u->ring_cons);
+ if (p < c) {
+ memcpy(new_ring + c, u->ring + c, (u->ring_size - c) * sizeof(*u->ring));
+ memcpy(new_ring + u->ring_size, u->ring, p * sizeof(*u->ring));
+ } else
+ memcpy(new_ring + c, u->ring + c, (p - c) * sizeof(*u->ring));
+
+ u->ring = new_ring;
+ u->ring_size = new_size;
+
+ spin_unlock_irq(&u->ring_prod_lock);
+ mutex_unlock(&u->ring_cons_mutex);
+
+ evtchn_free_ring(old_ring);
+
+ return 0;
+}
+
static int evtchn_bind_to_user(struct per_user_data *u, int port)
{
struct user_evtchn *evtchn;
@@ -305,6 +399,10 @@ static int evtchn_bind_to_user(struct per_user_data *u, int port)
if (rc < 0)
goto err;
+ rc = evtchn_resize_ring(u);
+ if (rc < 0)
+ goto err;
+
rc = bind_evtchn_to_irqhandler(port, evtchn_interrupt, 0,
u->name, evtchn);
if (rc < 0)
@@ -503,13 +601,6 @@ static int evtchn_open(struct inode *inode, struct file *filp)
init_waitqueue_head(&u->evtchn_wait);
- u->ring = (evtchn_port_t *)__get_free_page(GFP_KERNEL);
- if (u->ring == NULL) {
- kfree(u->name);
- kfree(u);
- return -ENOMEM;
- }
-
mutex_init(&u->bind_mutex);
mutex_init(&u->ring_cons_mutex);
spin_lock_init(&u->ring_prod_lock);
@@ -532,7 +623,7 @@ static int evtchn_release(struct inode *inode, struct file *filp)
evtchn_unbind_from_user(u, evtchn);
}
- free_page((unsigned long)u->ring);
+ evtchn_free_ring(u->ring);
kfree(u->name);
kfree(u);
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index 2ea0b3b2a91d..dc495383ad73 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -518,7 +518,7 @@ static void mn_release(struct mmu_notifier *mn,
mutex_unlock(&priv->lock);
}
-static struct mmu_notifier_ops gntdev_mmu_ops = {
+static const struct mmu_notifier_ops gntdev_mmu_ops = {
.release = mn_release,
.invalidate_page = mn_invl_page,
.invalidate_range_start = mn_invl_range_start,
@@ -748,6 +748,206 @@ static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u)
return rc;
}
+#define GNTDEV_COPY_BATCH 24
+
+struct gntdev_copy_batch {
+ struct gnttab_copy ops[GNTDEV_COPY_BATCH];
+ struct page *pages[GNTDEV_COPY_BATCH];
+ s16 __user *status[GNTDEV_COPY_BATCH];
+ unsigned int nr_ops;
+ unsigned int nr_pages;
+};
+
+static int gntdev_get_page(struct gntdev_copy_batch *batch, void __user *virt,
+ bool writeable, unsigned long *gfn)
+{
+ unsigned long addr = (unsigned long)virt;
+ struct page *page;
+ unsigned long xen_pfn;
+ int ret;
+
+ ret = get_user_pages_fast(addr, 1, writeable, &page);
+ if (ret < 0)
+ return ret;
+
+ batch->pages[batch->nr_pages++] = page;
+
+ xen_pfn = page_to_xen_pfn(page) + XEN_PFN_DOWN(addr & ~PAGE_MASK);
+ *gfn = pfn_to_gfn(xen_pfn);
+
+ return 0;
+}
+
+static void gntdev_put_pages(struct gntdev_copy_batch *batch)
+{
+ unsigned int i;
+
+ for (i = 0; i < batch->nr_pages; i++)
+ put_page(batch->pages[i]);
+ batch->nr_pages = 0;
+}
+
+static int gntdev_copy(struct gntdev_copy_batch *batch)
+{
+ unsigned int i;
+
+ gnttab_batch_copy(batch->ops, batch->nr_ops);
+ gntdev_put_pages(batch);
+
+ /*
+ * For each completed op, update the status if the op failed
+ * and all previous ops for the segment were successful.
+ */
+ for (i = 0; i < batch->nr_ops; i++) {
+ s16 status = batch->ops[i].status;
+ s16 old_status;
+
+ if (status == GNTST_okay)
+ continue;
+
+ if (__get_user(old_status, batch->status[i]))
+ return -EFAULT;
+
+ if (old_status != GNTST_okay)
+ continue;
+
+ if (__put_user(status, batch->status[i]))
+ return -EFAULT;
+ }
+
+ batch->nr_ops = 0;
+ return 0;
+}
+
+static int gntdev_grant_copy_seg(struct gntdev_copy_batch *batch,
+ struct gntdev_grant_copy_segment *seg,
+ s16 __user *status)
+{
+ uint16_t copied = 0;
+
+ /*
+ * Disallow local -> local copies since there is only space in
+ * batch->pages for one page per-op and this would be a very
+ * expensive memcpy().
+ */
+ if (!(seg->flags & (GNTCOPY_source_gref | GNTCOPY_dest_gref)))
+ return -EINVAL;
+
+ /* Can't cross page if source/dest is a grant ref. */
+ if (seg->flags & GNTCOPY_source_gref) {
+ if (seg->source.foreign.offset + seg->len > XEN_PAGE_SIZE)
+ return -EINVAL;
+ }
+ if (seg->flags & GNTCOPY_dest_gref) {
+ if (seg->dest.foreign.offset + seg->len > XEN_PAGE_SIZE)
+ return -EINVAL;
+ }
+
+ if (put_user(GNTST_okay, status))
+ return -EFAULT;
+
+ while (copied < seg->len) {
+ struct gnttab_copy *op;
+ void __user *virt;
+ size_t len, off;
+ unsigned long gfn;
+ int ret;
+
+ if (batch->nr_ops >= GNTDEV_COPY_BATCH) {
+ ret = gntdev_copy(batch);
+ if (ret < 0)
+ return ret;
+ }
+
+ len = seg->len - copied;
+
+ op = &batch->ops[batch->nr_ops];
+ op->flags = 0;
+
+ if (seg->flags & GNTCOPY_source_gref) {
+ op->source.u.ref = seg->source.foreign.ref;
+ op->source.domid = seg->source.foreign.domid;
+ op->source.offset = seg->source.foreign.offset + copied;
+ op->flags |= GNTCOPY_source_gref;
+ } else {
+ virt = seg->source.virt + copied;
+ off = (unsigned long)virt & ~XEN_PAGE_MASK;
+ len = min(len, (size_t)XEN_PAGE_SIZE - off);
+
+ ret = gntdev_get_page(batch, virt, false, &gfn);
+ if (ret < 0)
+ return ret;
+
+ op->source.u.gmfn = gfn;
+ op->source.domid = DOMID_SELF;
+ op->source.offset = off;
+ }
+
+ if (seg->flags & GNTCOPY_dest_gref) {
+ op->dest.u.ref = seg->dest.foreign.ref;
+ op->dest.domid = seg->dest.foreign.domid;
+ op->dest.offset = seg->dest.foreign.offset + copied;
+ op->flags |= GNTCOPY_dest_gref;
+ } else {
+ virt = seg->dest.virt + copied;
+ off = (unsigned long)virt & ~XEN_PAGE_MASK;
+ len = min(len, (size_t)XEN_PAGE_SIZE - off);
+
+ ret = gntdev_get_page(batch, virt, true, &gfn);
+ if (ret < 0)
+ return ret;
+
+ op->dest.u.gmfn = gfn;
+ op->dest.domid = DOMID_SELF;
+ op->dest.offset = off;
+ }
+
+ op->len = len;
+ copied += len;
+
+ batch->status[batch->nr_ops] = status;
+ batch->nr_ops++;
+ }
+
+ return 0;
+}
+
+static long gntdev_ioctl_grant_copy(struct gntdev_priv *priv, void __user *u)
+{
+ struct ioctl_gntdev_grant_copy copy;
+ struct gntdev_copy_batch batch;
+ unsigned int i;
+ int ret = 0;
+
+ if (copy_from_user(&copy, u, sizeof(copy)))
+ return -EFAULT;
+
+ batch.nr_ops = 0;
+ batch.nr_pages = 0;
+
+ for (i = 0; i < copy.count; i++) {
+ struct gntdev_grant_copy_segment seg;
+
+ if (copy_from_user(&seg, &copy.segments[i], sizeof(seg))) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ ret = gntdev_grant_copy_seg(&batch, &seg, &copy.segments[i].status);
+ if (ret < 0)
+ goto out;
+
+ cond_resched();
+ }
+ if (batch.nr_ops)
+ ret = gntdev_copy(&batch);
+ return ret;
+
+ out:
+ gntdev_put_pages(&batch);
+ return ret;
+}
+
static long gntdev_ioctl(struct file *flip,
unsigned int cmd, unsigned long arg)
{
@@ -767,6 +967,9 @@ static long gntdev_ioctl(struct file *flip,
case IOCTL_GNTDEV_SET_UNMAP_NOTIFY:
return gntdev_ioctl_notify(priv, ptr);
+ case IOCTL_GNTDEV_GRANT_COPY:
+ return gntdev_ioctl_grant_copy(priv, ptr);
+
default:
pr_debug("priv %p, unknown cmd %x\n", priv, cmd);
return -ENOIOCTLCMD;
@@ -804,7 +1007,7 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma)
vma->vm_ops = &gntdev_vmops;
- vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+ vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
if (use_ptemod)
vma->vm_flags |= VM_DONTCOPY;
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index c49f79ed58c5..effbaf91791f 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -128,7 +128,7 @@ struct unmap_refs_callback_data {
int result;
};
-static struct gnttab_ops *gnttab_interface;
+static const struct gnttab_ops *gnttab_interface;
static int grant_table_version;
static int grefs_per_grant_frame;
@@ -1013,7 +1013,7 @@ static int gnttab_map(unsigned int start_idx, unsigned int end_idx)
return rc;
}
-static struct gnttab_ops gnttab_v1_ops = {
+static const struct gnttab_ops gnttab_v1_ops = {
.map_frames = gnttab_map_frames_v1,
.unmap_frames = gnttab_unmap_frames_v1,
.update_entry = gnttab_update_entry_v1,
diff --git a/drivers/xen/pcpu.c b/drivers/xen/pcpu.c
index 49e88f2ce7a1..cdc6daa7a9f6 100644
--- a/drivers/xen/pcpu.c
+++ b/drivers/xen/pcpu.c
@@ -78,7 +78,7 @@ static int xen_pcpu_down(uint32_t cpu_id)
.u.cpu_ol.cpuid = cpu_id,
};
- return HYPERVISOR_dom0_op(&op);
+ return HYPERVISOR_platform_op(&op);
}
static int xen_pcpu_up(uint32_t cpu_id)
@@ -89,7 +89,7 @@ static int xen_pcpu_up(uint32_t cpu_id)
.u.cpu_ol.cpuid = cpu_id,
};
- return HYPERVISOR_dom0_op(&op);
+ return HYPERVISOR_platform_op(&op);
}
static ssize_t show_online(struct device *dev,
@@ -277,7 +277,7 @@ static int sync_pcpu(uint32_t cpu, uint32_t *max_cpu)
.u.pcpu_info.xen_cpuid = cpu,
};
- ret = HYPERVISOR_dom0_op(&op);
+ ret = HYPERVISOR_platform_op(&op);
if (ret)
return ret;
@@ -364,7 +364,7 @@ int xen_pcpu_id(uint32_t acpi_id)
op.cmd = XENPF_get_cpuinfo;
while (cpu_id <= max_id) {
op.u.pcpu_info.xen_cpuid = cpu_id;
- if (HYPERVISOR_dom0_op(&op)) {
+ if (HYPERVISOR_platform_op(&op)) {
cpu_id++;
continue;
}
diff --git a/drivers/xen/time.c b/drivers/xen/time.c
new file mode 100644
index 000000000000..71078425c9ea
--- /dev/null
+++ b/drivers/xen/time.c
@@ -0,0 +1,88 @@
+/*
+ * Xen stolen ticks accounting.
+ */
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/math64.h>
+#include <linux/gfp.h>
+
+#include <asm/xen/hypervisor.h>
+#include <asm/xen/hypercall.h>
+
+#include <xen/events.h>
+#include <xen/features.h>
+#include <xen/interface/xen.h>
+#include <xen/interface/vcpu.h>
+#include <xen/xen-ops.h>
+
+/* runstate info updated by Xen */
+static DEFINE_PER_CPU(struct vcpu_runstate_info, xen_runstate);
+
+/* return an consistent snapshot of 64-bit time/counter value */
+static u64 get64(const u64 *p)
+{
+ u64 ret;
+
+ if (BITS_PER_LONG < 64) {
+ u32 *p32 = (u32 *)p;
+ u32 h, l, h2;
+
+ /*
+ * Read high then low, and then make sure high is
+ * still the same; this will only loop if low wraps
+ * and carries into high.
+ * XXX some clean way to make this endian-proof?
+ */
+ do {
+ h = READ_ONCE(p32[1]);
+ l = READ_ONCE(p32[0]);
+ h2 = READ_ONCE(p32[1]);
+ } while(h2 != h);
+
+ ret = (((u64)h) << 32) | l;
+ } else
+ ret = READ_ONCE(*p);
+
+ return ret;
+}
+
+/*
+ * Runstate accounting
+ */
+void xen_get_runstate_snapshot(struct vcpu_runstate_info *res)
+{
+ u64 state_time;
+ struct vcpu_runstate_info *state;
+
+ BUG_ON(preemptible());
+
+ state = this_cpu_ptr(&xen_runstate);
+
+ /*
+ * The runstate info is always updated by the hypervisor on
+ * the current CPU, so there's no need to use anything
+ * stronger than a compiler barrier when fetching it.
+ */
+ do {
+ state_time = get64(&state->state_entry_time);
+ *res = READ_ONCE(*state);
+ } while (get64(&state->state_entry_time) != state_time);
+}
+
+/* return true when a vcpu could run but has no real cpu to run on */
+bool xen_vcpu_stolen(int vcpu)
+{
+ return per_cpu(xen_runstate, vcpu).state == RUNSTATE_runnable;
+}
+
+void xen_setup_runstate_info(int cpu)
+{
+ struct vcpu_register_runstate_memory_area area;
+
+ area.addr.v = &per_cpu(xen_runstate, cpu);
+
+ if (HYPERVISOR_vcpu_op(VCPUOP_register_runstate_memory_area,
+ cpu, &area))
+ BUG();
+}
+
diff --git a/drivers/xen/xen-acpi-cpuhotplug.c b/drivers/xen/xen-acpi-cpuhotplug.c
index f4a369429553..fdc9e67b842d 100644
--- a/drivers/xen/xen-acpi-cpuhotplug.c
+++ b/drivers/xen/xen-acpi-cpuhotplug.c
@@ -206,7 +206,7 @@ static int xen_hotadd_cpu(struct acpi_processor *pr)
op.u.cpu_add.acpi_id = pr->acpi_id;
op.u.cpu_add.pxm = pxm;
- cpu_id = HYPERVISOR_dom0_op(&op);
+ cpu_id = HYPERVISOR_platform_op(&op);
if (cpu_id < 0)
pr_err(PREFIX "Failed to hotadd CPU for acpi_id %d\n",
pr->acpi_id);
diff --git a/drivers/xen/xen-acpi-pad.c b/drivers/xen/xen-acpi-pad.c
index f83b754505f8..23d1808fe027 100644
--- a/drivers/xen/xen-acpi-pad.c
+++ b/drivers/xen/xen-acpi-pad.c
@@ -36,7 +36,7 @@ static int xen_acpi_pad_idle_cpus(unsigned int idle_nums)
op.u.core_parking.type = XEN_CORE_PARKING_SET;
op.u.core_parking.idle_nums = idle_nums;
- return HYPERVISOR_dom0_op(&op);
+ return HYPERVISOR_platform_op(&op);
}
static int xen_acpi_pad_idle_cpus_num(void)
@@ -46,7 +46,7 @@ static int xen_acpi_pad_idle_cpus_num(void)
op.cmd = XENPF_core_parking;
op.u.core_parking.type = XEN_CORE_PARKING_GET;
- return HYPERVISOR_dom0_op(&op)
+ return HYPERVISOR_platform_op(&op)
?: op.u.core_parking.idle_nums;
}
diff --git a/drivers/xen/xen-acpi-processor.c b/drivers/xen/xen-acpi-processor.c
index 70fa438000af..076970a54f89 100644
--- a/drivers/xen/xen-acpi-processor.c
+++ b/drivers/xen/xen-acpi-processor.c
@@ -116,7 +116,7 @@ static int push_cxx_to_hypervisor(struct acpi_processor *_pr)
set_xen_guest_handle(op.u.set_pminfo.power.states, dst_cx_states);
if (!no_hypercall)
- ret = HYPERVISOR_dom0_op(&op);
+ ret = HYPERVISOR_platform_op(&op);
if (!ret) {
pr_debug("ACPI CPU%u - C-states uploaded.\n", _pr->acpi_id);
@@ -244,7 +244,7 @@ static int push_pxx_to_hypervisor(struct acpi_processor *_pr)
}
if (!no_hypercall)
- ret = HYPERVISOR_dom0_op(&op);
+ ret = HYPERVISOR_platform_op(&op);
if (!ret) {
struct acpi_processor_performance *perf;
@@ -302,7 +302,7 @@ static unsigned int __init get_max_acpi_id(void)
info = &op.u.pcpu_info;
info->xen_cpuid = 0;
- ret = HYPERVISOR_dom0_op(&op);
+ ret = HYPERVISOR_platform_op(&op);
if (ret)
return NR_CPUS;
@@ -310,7 +310,7 @@ static unsigned int __init get_max_acpi_id(void)
last_cpu = op.u.pcpu_info.max_present;
for (i = 0; i <= last_cpu; i++) {
info->xen_cpuid = i;
- ret = HYPERVISOR_dom0_op(&op);
+ ret = HYPERVISOR_platform_op(&op);
if (ret)
continue;
max_acpi_id = max(info->acpi_id, max_acpi_id);
diff --git a/drivers/xen/xen-pciback/pciback.h b/drivers/xen/xen-pciback/pciback.h
index 58e38d586f52..4d529f3e40df 100644
--- a/drivers/xen/xen-pciback/pciback.h
+++ b/drivers/xen/xen-pciback/pciback.h
@@ -37,6 +37,7 @@ struct xen_pcibk_device {
struct xen_pci_sharedinfo *sh_info;
unsigned long flags;
struct work_struct op_work;
+ struct xen_pci_op op;
};
struct xen_pcibk_dev_data {
diff --git a/drivers/xen/xen-pciback/pciback_ops.c b/drivers/xen/xen-pciback/pciback_ops.c
index c4a0666de6f5..73dafdc494aa 100644
--- a/drivers/xen/xen-pciback/pciback_ops.c
+++ b/drivers/xen/xen-pciback/pciback_ops.c
@@ -70,6 +70,13 @@ static void xen_pcibk_control_isr(struct pci_dev *dev, int reset)
enable ? "enable" : "disable");
if (enable) {
+ /*
+ * The MSI or MSI-X should not have an IRQ handler. Otherwise
+ * if the guest terminates we BUG_ON in free_msi_irqs.
+ */
+ if (dev->msi_enabled || dev->msix_enabled)
+ goto out;
+
rc = request_irq(dev_data->irq,
xen_pcibk_guest_interrupt, IRQF_SHARED,
dev_data->irq_name, dev);
@@ -144,7 +151,12 @@ int xen_pcibk_enable_msi(struct xen_pcibk_device *pdev,
if (unlikely(verbose_request))
printk(KERN_DEBUG DRV_NAME ": %s: enable MSI\n", pci_name(dev));
- status = pci_enable_msi(dev);
+ if (dev->msi_enabled)
+ status = -EALREADY;
+ else if (dev->msix_enabled)
+ status = -ENXIO;
+ else
+ status = pci_enable_msi(dev);
if (status) {
pr_warn_ratelimited("%s: error enabling MSI for guest %u: err %d\n",
@@ -173,20 +185,23 @@ static
int xen_pcibk_disable_msi(struct xen_pcibk_device *pdev,
struct pci_dev *dev, struct xen_pci_op *op)
{
- struct xen_pcibk_dev_data *dev_data;
-
if (unlikely(verbose_request))
printk(KERN_DEBUG DRV_NAME ": %s: disable MSI\n",
pci_name(dev));
- pci_disable_msi(dev);
+ if (dev->msi_enabled) {
+ struct xen_pcibk_dev_data *dev_data;
+
+ pci_disable_msi(dev);
+
+ dev_data = pci_get_drvdata(dev);
+ if (dev_data)
+ dev_data->ack_intr = 1;
+ }
op->value = dev->irq ? xen_pirq_from_irq(dev->irq) : 0;
if (unlikely(verbose_request))
printk(KERN_DEBUG DRV_NAME ": %s: MSI: %d\n", pci_name(dev),
op->value);
- dev_data = pci_get_drvdata(dev);
- if (dev_data)
- dev_data->ack_intr = 1;
return 0;
}
@@ -197,13 +212,26 @@ int xen_pcibk_enable_msix(struct xen_pcibk_device *pdev,
struct xen_pcibk_dev_data *dev_data;
int i, result;
struct msix_entry *entries;
+ u16 cmd;
if (unlikely(verbose_request))
printk(KERN_DEBUG DRV_NAME ": %s: enable MSI-X\n",
pci_name(dev));
+
if (op->value > SH_INFO_MAX_VEC)
return -EINVAL;
+ if (dev->msix_enabled)
+ return -EALREADY;
+
+ /*
+ * PCI_COMMAND_MEMORY must be enabled, otherwise we may not be able
+ * to access the BARs where the MSI-X entries reside.
+ */
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ if (dev->msi_enabled || !(cmd & PCI_COMMAND_MEMORY))
+ return -ENXIO;
+
entries = kmalloc(op->value * sizeof(*entries), GFP_KERNEL);
if (entries == NULL)
return -ENOMEM;
@@ -245,23 +273,27 @@ static
int xen_pcibk_disable_msix(struct xen_pcibk_device *pdev,
struct pci_dev *dev, struct xen_pci_op *op)
{
- struct xen_pcibk_dev_data *dev_data;
if (unlikely(verbose_request))
printk(KERN_DEBUG DRV_NAME ": %s: disable MSI-X\n",
pci_name(dev));
- pci_disable_msix(dev);
+ if (dev->msix_enabled) {
+ struct xen_pcibk_dev_data *dev_data;
+
+ pci_disable_msix(dev);
+
+ dev_data = pci_get_drvdata(dev);
+ if (dev_data)
+ dev_data->ack_intr = 1;
+ }
/*
* SR-IOV devices (which don't have any legacy IRQ) have
* an undefined IRQ value of zero.
*/
op->value = dev->irq ? xen_pirq_from_irq(dev->irq) : 0;
if (unlikely(verbose_request))
- printk(KERN_DEBUG DRV_NAME ": %s: MSI-X: %d\n", pci_name(dev),
- op->value);
- dev_data = pci_get_drvdata(dev);
- if (dev_data)
- dev_data->ack_intr = 1;
+ printk(KERN_DEBUG DRV_NAME ": %s: MSI-X: %d\n",
+ pci_name(dev), op->value);
return 0;
}
#endif
@@ -298,9 +330,11 @@ void xen_pcibk_do_op(struct work_struct *data)
container_of(data, struct xen_pcibk_device, op_work);
struct pci_dev *dev;
struct xen_pcibk_dev_data *dev_data = NULL;
- struct xen_pci_op *op = &pdev->sh_info->op;
+ struct xen_pci_op *op = &pdev->op;
int test_intx = 0;
+ *op = pdev->sh_info->op;
+ barrier();
dev = xen_pcibk_get_pci_dev(pdev, op->domain, op->bus, op->devfn);
if (dev == NULL)
@@ -342,6 +376,17 @@ void xen_pcibk_do_op(struct work_struct *data)
if ((dev_data->enable_intx != test_intx))
xen_pcibk_control_isr(dev, 0 /* no reset */);
}
+ pdev->sh_info->op.err = op->err;
+ pdev->sh_info->op.value = op->value;
+#ifdef CONFIG_PCI_MSI
+ if (op->cmd == XEN_PCI_OP_enable_msix && op->err == 0) {
+ unsigned int i;
+
+ for (i = 0; i < op->value; i++)
+ pdev->sh_info->op.msix_entries[i].vector =
+ op->msix_entries[i].vector;
+ }
+#endif
/* Tell the driver domain that we're done. */
wmb();
clear_bit(_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags);
diff --git a/drivers/xen/xen-pciback/xenbus.c b/drivers/xen/xen-pciback/xenbus.c
index 98bc345f296e..4843741e703a 100644
--- a/drivers/xen/xen-pciback/xenbus.c
+++ b/drivers/xen/xen-pciback/xenbus.c
@@ -44,7 +44,6 @@ static struct xen_pcibk_device *alloc_pdev(struct xenbus_device *xdev)
dev_dbg(&xdev->dev, "allocated pdev @ 0x%p\n", pdev);
pdev->xdev = xdev;
- dev_set_drvdata(&xdev->dev, pdev);
mutex_init(&pdev->dev_lock);
@@ -58,6 +57,9 @@ static struct xen_pcibk_device *alloc_pdev(struct xenbus_device *xdev)
kfree(pdev);
pdev = NULL;
}
+
+ dev_set_drvdata(&xdev->dev, pdev);
+
out:
return pdev;
}
diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c
index 43bcae852546..ad4eb1024d1f 100644
--- a/drivers/xen/xen-scsiback.c
+++ b/drivers/xen/xen-scsiback.c
@@ -726,7 +726,7 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info)
if (!pending_req)
return 1;
- ring_req = *RING_GET_REQUEST(ring, rc);
+ RING_COPY_REQUEST(ring, rc, &ring_req);
ring->req_cons = ++rc;
err = prepare_pending_reqs(info, &ring_req, pending_req);
diff --git a/drivers/xen/xenfs/xensyms.c b/drivers/xen/xenfs/xensyms.c
index f8b12856753f..a03f261b12d8 100644
--- a/drivers/xen/xenfs/xensyms.c
+++ b/drivers/xen/xenfs/xensyms.c
@@ -31,7 +31,7 @@ static int xensyms_next_sym(struct xensyms *xs)
symnum = symdata->symnum;
- ret = HYPERVISOR_dom0_op(&xs->op);
+ ret = HYPERVISOR_platform_op(&xs->op);
if (ret < 0)
return ret;
@@ -50,7 +50,7 @@ static int xensyms_next_sym(struct xensyms *xs)
set_xen_guest_handle(symdata->name, xs->name);
symdata->symnum--; /* Rewind */
- ret = HYPERVISOR_dom0_op(&xs->op);
+ ret = HYPERVISOR_platform_op(&xs->op);
if (ret < 0)
return ret;
}
diff --git a/firmware/WHENCE b/firmware/WHENCE
index 0c4d96dee9b6..de6f22e008f1 100644
--- a/firmware/WHENCE
+++ b/firmware/WHENCE
@@ -677,7 +677,7 @@ File: av7110/bootcode.bin
Licence: GPLv2 or later
-ARM assembly source code available at http://www.linuxtv.org/downloads/firmware/Boot.S
+ARM assembly source code available at https://linuxtv.org/downloads/firmware/Boot.S
--------------------------------------------------------------------------
diff --git a/fs/9p/acl.c b/fs/9p/acl.c
index a7e28890f5ef..9da967f38387 100644
--- a/fs/9p/acl.c
+++ b/fs/9p/acl.c
@@ -67,8 +67,8 @@ int v9fs_get_acl(struct inode *inode, struct p9_fid *fid)
return 0;
}
/* get the default/access acl values and cache them */
- dacl = __v9fs_get_acl(fid, POSIX_ACL_XATTR_DEFAULT);
- pacl = __v9fs_get_acl(fid, POSIX_ACL_XATTR_ACCESS);
+ dacl = __v9fs_get_acl(fid, XATTR_NAME_POSIX_ACL_DEFAULT);
+ pacl = __v9fs_get_acl(fid, XATTR_NAME_POSIX_ACL_ACCESS);
if (!IS_ERR(dacl) && !IS_ERR(pacl)) {
set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl);
@@ -133,10 +133,10 @@ static int v9fs_set_acl(struct p9_fid *fid, int type, struct posix_acl *acl)
goto err_free_out;
switch (type) {
case ACL_TYPE_ACCESS:
- name = POSIX_ACL_XATTR_ACCESS;
+ name = XATTR_NAME_POSIX_ACL_ACCESS;
break;
case ACL_TYPE_DEFAULT:
- name = POSIX_ACL_XATTR_DEFAULT;
+ name = XATTR_NAME_POSIX_ACL_DEFAULT;
break;
default:
BUG();
@@ -220,15 +220,12 @@ static int v9fs_xattr_get_acl(const struct xattr_handler *handler,
struct posix_acl *acl;
int error;
- if (strcmp(name, "") != 0)
- return -EINVAL;
-
v9ses = v9fs_dentry2v9ses(dentry);
/*
* We allow set/get/list of acl when access=client is not specified
*/
if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT)
- return v9fs_xattr_get(dentry, handler->prefix, buffer, size);
+ return v9fs_xattr_get(dentry, handler->name, buffer, size);
acl = v9fs_get_cached_acl(d_inode(dentry), handler->flags);
if (IS_ERR(acl))
@@ -250,16 +247,13 @@ static int v9fs_xattr_set_acl(const struct xattr_handler *handler,
struct v9fs_session_info *v9ses;
struct inode *inode = d_inode(dentry);
- if (strcmp(name, "") != 0)
- return -EINVAL;
-
v9ses = v9fs_dentry2v9ses(dentry);
/*
* set the attribute on the remote. Without even looking at the
* xattr value. We leave it to the server to validate
*/
if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT)
- return v9fs_xattr_set(dentry, handler->prefix, value, size,
+ return v9fs_xattr_set(dentry, handler->name, value, size,
flags);
if (S_ISLNK(inode->i_mode))
@@ -319,7 +313,7 @@ static int v9fs_xattr_set_acl(const struct xattr_handler *handler,
default:
BUG();
}
- retval = v9fs_xattr_set(dentry, handler->prefix, value, size, flags);
+ retval = v9fs_xattr_set(dentry, handler->name, value, size, flags);
if (!retval)
set_cached_acl(inode, handler->flags, acl);
err_out:
@@ -328,14 +322,14 @@ err_out:
}
const struct xattr_handler v9fs_xattr_acl_access_handler = {
- .prefix = POSIX_ACL_XATTR_ACCESS,
+ .name = XATTR_NAME_POSIX_ACL_ACCESS,
.flags = ACL_TYPE_ACCESS,
.get = v9fs_xattr_get_acl,
.set = v9fs_xattr_set_acl,
};
const struct xattr_handler v9fs_xattr_acl_default_handler = {
- .prefix = POSIX_ACL_XATTR_DEFAULT,
+ .name = XATTR_NAME_POSIX_ACL_DEFAULT,
.flags = ACL_TYPE_DEFAULT,
.get = v9fs_xattr_get_acl,
.set = v9fs_xattr_set_acl,
diff --git a/fs/9p/cache.c b/fs/9p/cache.c
index a69260f27555..103ca5e1267b 100644
--- a/fs/9p/cache.c
+++ b/fs/9p/cache.c
@@ -243,14 +243,14 @@ void v9fs_cache_inode_set_cookie(struct inode *inode, struct file *filp)
if (!v9inode->fscache)
return;
- spin_lock(&v9inode->fscache_lock);
+ mutex_lock(&v9inode->fscache_lock);
if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
v9fs_cache_inode_flush_cookie(inode);
else
v9fs_cache_inode_get_cookie(inode);
- spin_unlock(&v9inode->fscache_lock);
+ mutex_unlock(&v9inode->fscache_lock);
}
void v9fs_cache_inode_reset_cookie(struct inode *inode)
@@ -264,7 +264,7 @@ void v9fs_cache_inode_reset_cookie(struct inode *inode)
old = v9inode->fscache;
- spin_lock(&v9inode->fscache_lock);
+ mutex_lock(&v9inode->fscache_lock);
fscache_relinquish_cookie(v9inode->fscache, 1);
v9ses = v9fs_inode2v9ses(inode);
@@ -274,7 +274,7 @@ void v9fs_cache_inode_reset_cookie(struct inode *inode)
p9_debug(P9_DEBUG_FSC, "inode %p revalidating cookie old %p new %p\n",
inode, old, v9inode->fscache);
- spin_unlock(&v9inode->fscache_lock);
+ mutex_unlock(&v9inode->fscache_lock);
}
int __v9fs_fscache_release_page(struct page *page, gfp_t gfp)
diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c
index 6caca025019d..072e7599583a 100644
--- a/fs/9p/v9fs.c
+++ b/fs/9p/v9fs.c
@@ -575,7 +575,7 @@ static int v9fs_init_inode_cache(void)
v9fs_inode_cache = kmem_cache_create("v9fs_inode_cache",
sizeof(struct v9fs_inode),
0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
v9fs_inode_init_once);
if (!v9fs_inode_cache)
return -ENOMEM;
diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h
index 0923f2cf3c80..6877050384a1 100644
--- a/fs/9p/v9fs.h
+++ b/fs/9p/v9fs.h
@@ -123,7 +123,7 @@ struct v9fs_session_info {
struct v9fs_inode {
#ifdef CONFIG_9P_FSCACHE
- spinlock_t fscache_lock;
+ struct mutex fscache_lock;
struct fscache_cookie *fscache;
#endif
struct p9_qid qid;
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index 699941e90667..3a08b3e6ff1d 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -244,7 +244,7 @@ struct inode *v9fs_alloc_inode(struct super_block *sb)
return NULL;
#ifdef CONFIG_9P_FSCACHE
v9inode->fscache = NULL;
- spin_lock_init(&v9inode->fscache_lock);
+ mutex_init(&v9inode->fscache_lock);
#endif
v9inode->writeback_fid = NULL;
v9inode->cache_validity = 0;
@@ -451,9 +451,9 @@ void v9fs_evict_inode(struct inode *inode)
{
struct v9fs_inode *v9inode = V9FS_I(inode);
- truncate_inode_pages_final(inode->i_mapping);
+ truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
- filemap_fdatawrite(inode->i_mapping);
+ filemap_fdatawrite(&inode->i_data);
v9fs_cache_inode_put_cookie(inode);
/* clunk the fid stashed in writeback_fid */
@@ -1223,18 +1223,26 @@ ino_t v9fs_qid2ino(struct p9_qid *qid)
}
/**
- * v9fs_vfs_follow_link - follow a symlink path
+ * v9fs_vfs_get_link - follow a symlink path
* @dentry: dentry for symlink
- * @cookie: place to pass the data to put_link()
+ * @inode: inode for symlink
+ * @done: delayed call for when we are done with the return value
*/
-static const char *v9fs_vfs_follow_link(struct dentry *dentry, void **cookie)
+static const char *v9fs_vfs_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
- struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry);
- struct p9_fid *fid = v9fs_fid_lookup(dentry);
+ struct v9fs_session_info *v9ses;
+ struct p9_fid *fid;
struct p9_wstat *st;
char *res;
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
+
+ v9ses = v9fs_dentry2v9ses(dentry);
+ fid = v9fs_fid_lookup(dentry);
p9_debug(P9_DEBUG_VFS, "%pd\n", dentry);
if (IS_ERR(fid))
@@ -1259,7 +1267,8 @@ static const char *v9fs_vfs_follow_link(struct dentry *dentry, void **cookie)
p9stat_free(st);
kfree(st);
- return *cookie = res;
+ set_delayed_call(done, kfree_link, res);
+ return res;
}
/**
@@ -1452,8 +1461,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 = kfree_put_link,
+ .get_link = v9fs_vfs_get_link,
.getattr = v9fs_vfs_getattr,
.setattr = v9fs_vfs_setattr,
};
diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c
index cb899af1babc..a34702c998f5 100644
--- a/fs/9p/vfs_inode_dotl.c
+++ b/fs/9p/vfs_inode_dotl.c
@@ -899,26 +899,34 @@ error:
}
/**
- * v9fs_vfs_follow_link_dotl - follow a symlink path
+ * v9fs_vfs_get_link_dotl - follow a symlink path
* @dentry: dentry for symlink
- * @cookie: place to pass the data to put_link()
+ * @inode: inode for symlink
+ * @done: destructor for return value
*/
static const char *
-v9fs_vfs_follow_link_dotl(struct dentry *dentry, void **cookie)
+v9fs_vfs_get_link_dotl(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
- struct p9_fid *fid = v9fs_fid_lookup(dentry);
+ struct p9_fid *fid;
char *target;
int retval;
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
+
p9_debug(P9_DEBUG_VFS, "%pd\n", dentry);
+ fid = v9fs_fid_lookup(dentry);
if (IS_ERR(fid))
return ERR_CAST(fid);
retval = p9_client_readlink(fid, &target);
if (retval)
return ERR_PTR(retval);
- return *cookie = target;
+ set_delayed_call(done, kfree_link, target);
+ return target;
}
int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode)
@@ -984,8 +992,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 = kfree_put_link,
+ .get_link = v9fs_vfs_get_link_dotl,
.getattr = v9fs_vfs_getattr_dotl,
.setattr = v9fs_vfs_setattr_dotl,
.setxattr = generic_setxattr,
diff --git a/fs/9p/xattr.c b/fs/9p/xattr.c
index e3d026ac382e..9dd9b47a6c1a 100644
--- a/fs/9p/xattr.c
+++ b/fs/9p/xattr.c
@@ -143,8 +143,6 @@ static int v9fs_xattr_handler_get(const struct xattr_handler *handler,
{
const char *full_name = xattr_full_name(handler, name);
- if (strcmp(name, "") == 0)
- return -EINVAL;
return v9fs_xattr_get(dentry, full_name, buffer, size);
}
@@ -154,8 +152,6 @@ static int v9fs_xattr_handler_set(const struct xattr_handler *handler,
{
const char *full_name = xattr_full_name(handler, name);
- if (strcmp(name, "") == 0)
- return -EINVAL;
return v9fs_xattr_set(dentry, full_name, value, size, flags);
}
diff --git a/fs/Kconfig b/fs/Kconfig
index 6ce72d8d1ee1..2bb1ef86c411 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -73,6 +73,16 @@ config FILE_LOCKING
for filesystems like NFS and for the flock() system
call. Disabling this option saves about 11k.
+config MANDATORY_FILE_LOCKING
+ bool "Enable Mandatory file locking"
+ depends on FILE_LOCKING
+ default y
+ help
+ This option enables files appropriately marked files on appropriely
+ mounted filesystems to support mandatory locking.
+
+ To the best of my knowledge this is dead code that no one cares about.
+
source "fs/notify/Kconfig"
source "fs/quota/Kconfig"
diff --git a/fs/adfs/adfs.h b/fs/adfs/adfs.h
index 24575d9d882d..ea4aba56f29d 100644
--- a/fs/adfs/adfs.h
+++ b/fs/adfs/adfs.h
@@ -45,7 +45,7 @@ struct adfs_dir_ops;
struct adfs_sb_info {
union { struct {
struct adfs_discmap *s_map; /* bh list containing map */
- struct adfs_dir_ops *s_dir; /* directory operations */
+ const struct adfs_dir_ops *s_dir; /* directory operations */
};
struct rcu_head rcu; /* used only at shutdown time */
};
@@ -168,8 +168,8 @@ void __adfs_error(struct super_block *sb, const char *function,
extern const struct inode_operations adfs_dir_inode_operations;
extern const struct file_operations adfs_dir_operations;
extern const struct dentry_operations adfs_dentry_operations;
-extern struct adfs_dir_ops adfs_f_dir_ops;
-extern struct adfs_dir_ops adfs_fplus_dir_ops;
+extern const struct adfs_dir_ops adfs_f_dir_ops;
+extern const struct adfs_dir_ops adfs_fplus_dir_ops;
extern int adfs_dir_update(struct super_block *sb, struct object_info *obj,
int wait);
diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c
index 51c279a29845..fd4cf2c48e48 100644
--- a/fs/adfs/dir.c
+++ b/fs/adfs/dir.c
@@ -21,7 +21,7 @@ adfs_readdir(struct file *file, struct dir_context *ctx)
{
struct inode *inode = file_inode(file);
struct super_block *sb = inode->i_sb;
- struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
+ const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
struct object_info obj;
struct adfs_dir dir;
int ret = 0;
@@ -69,7 +69,7 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
{
int ret = -EINVAL;
#ifdef CONFIG_ADFS_FS_RW
- struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
+ const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
struct adfs_dir dir;
printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n",
@@ -129,7 +129,7 @@ static int
adfs_dir_lookup_byname(struct inode *inode, struct qstr *name, struct object_info *obj)
{
struct super_block *sb = inode->i_sb;
- struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
+ const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
struct adfs_dir dir;
int ret;
diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c
index 4bbe853ee50a..0fbfd0b04ae0 100644
--- a/fs/adfs/dir_f.c
+++ b/fs/adfs/dir_f.c
@@ -476,7 +476,7 @@ adfs_f_free(struct adfs_dir *dir)
dir->sb = NULL;
}
-struct adfs_dir_ops adfs_f_dir_ops = {
+const struct adfs_dir_ops adfs_f_dir_ops = {
.read = adfs_f_read,
.setpos = adfs_f_setpos,
.getnext = adfs_f_getnext,
diff --git a/fs/adfs/dir_fplus.c b/fs/adfs/dir_fplus.c
index 82d14cdf70f9..c92cfb638c18 100644
--- a/fs/adfs/dir_fplus.c
+++ b/fs/adfs/dir_fplus.c
@@ -256,7 +256,7 @@ adfs_fplus_free(struct adfs_dir *dir)
dir->sb = NULL;
}
-struct adfs_dir_ops adfs_fplus_dir_ops = {
+const struct adfs_dir_ops adfs_fplus_dir_ops = {
.read = adfs_fplus_read,
.setpos = adfs_fplus_setpos,
.getnext = adfs_fplus_getnext,
diff --git a/fs/adfs/super.c b/fs/adfs/super.c
index 4d4a0df8344f..c9fdfb112933 100644
--- a/fs/adfs/super.c
+++ b/fs/adfs/super.c
@@ -271,7 +271,7 @@ static int __init init_inodecache(void)
adfs_inode_cachep = kmem_cache_create("adfs_inode_cache",
sizeof(struct adfs_inode_info),
0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
init_once);
if (adfs_inode_cachep == NULL)
return -ENOMEM;
diff --git a/fs/affs/affs.h b/fs/affs/affs.h
index c69a87eaf57d..cc2b2efc9211 100644
--- a/fs/affs/affs.h
+++ b/fs/affs/affs.h
@@ -138,7 +138,7 @@ extern int affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh);
extern int affs_remove_header(struct dentry *dentry);
extern u32 affs_checksum_block(struct super_block *sb, struct buffer_head *bh);
extern void affs_fix_checksum(struct super_block *sb, struct buffer_head *bh);
-extern void secs_to_datestamp(time_t secs, struct affs_date *ds);
+extern void secs_to_datestamp(time64_t secs, struct affs_date *ds);
extern umode_t prot_to_mode(u32 prot);
extern void mode_to_prot(struct inode *inode);
__printf(3, 4)
diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c
index 5fa92bc790ef..d6c7a51c93e4 100644
--- a/fs/affs/amigaffs.c
+++ b/fs/affs/amigaffs.c
@@ -8,6 +8,7 @@
* Please send bug reports to: hjw@zvw.de
*/
+#include <linux/math64.h>
#include "affs.h"
/*
@@ -366,22 +367,22 @@ affs_fix_checksum(struct super_block *sb, struct buffer_head *bh)
}
void
-secs_to_datestamp(time_t secs, struct affs_date *ds)
+secs_to_datestamp(time64_t secs, struct affs_date *ds)
{
u32 days;
u32 minute;
+ s32 rem;
secs -= sys_tz.tz_minuteswest * 60 + ((8 * 365 + 2) * 24 * 60 * 60);
if (secs < 0)
secs = 0;
- days = secs / 86400;
- secs -= days * 86400;
- minute = secs / 60;
- secs -= minute * 60;
+ days = div_s64_rem(secs, 86400, &rem);
+ minute = rem / 60;
+ rem -= minute * 60;
ds->days = cpu_to_be32(days);
ds->mins = cpu_to_be32(minute);
- ds->ticks = cpu_to_be32(secs * 50);
+ ds->ticks = cpu_to_be32(rem * 50);
}
umode_t
diff --git a/fs/affs/inode.c b/fs/affs/inode.c
index 17349500592d..0fdb0f5b2239 100644
--- a/fs/affs/inode.c
+++ b/fs/affs/inode.c
@@ -140,6 +140,7 @@ struct inode *affs_iget(struct super_block *sb, unsigned long ino)
break;
case ST_SOFTLINK:
inode->i_mode |= S_IFLNK;
+ inode_nohighmem(inode);
inode->i_op = &affs_symlink_inode_operations;
inode->i_data.a_ops = &affs_symlink_aops;
break;
diff --git a/fs/affs/namei.c b/fs/affs/namei.c
index 181e05b46e72..00d3002a6780 100644
--- a/fs/affs/namei.c
+++ b/fs/affs/namei.c
@@ -344,6 +344,7 @@ affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
return -ENOSPC;
inode->i_op = &affs_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_data.a_ops = &affs_symlink_aops;
inode->i_mode = S_IFLNK | 0777;
mode_to_prot(inode);
diff --git a/fs/affs/super.c b/fs/affs/super.c
index 5b50c4ca43a7..2a6713b6b9f4 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -32,7 +32,7 @@ affs_commit_super(struct super_block *sb, int wait)
struct affs_root_tail *tail = AFFS_ROOT_TAIL(sb, bh);
lock_buffer(bh);
- secs_to_datestamp(get_seconds(), &tail->disk_change);
+ secs_to_datestamp(ktime_get_real_seconds(), &tail->disk_change);
affs_fix_checksum(sb, bh);
unlock_buffer(bh);
@@ -132,7 +132,7 @@ static int __init init_inodecache(void)
affs_inode_cachep = kmem_cache_create("affs_inode_cache",
sizeof(struct affs_inode_info),
0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
init_once);
if (affs_inode_cachep == NULL)
return -ENOMEM;
diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c
index ea5b69a18ba9..69b03dbb792f 100644
--- a/fs/affs/symlink.c
+++ b/fs/affs/symlink.c
@@ -14,13 +14,13 @@ static int affs_symlink_readpage(struct file *file, struct page *page)
{
struct buffer_head *bh;
struct inode *inode = page->mapping->host;
- char *link = kmap(page);
+ char *link = page_address(page);
struct slink_front *lf;
int i, j;
char c;
char lc;
- pr_debug("follow_link(ino=%lu)\n", inode->i_ino);
+ pr_debug("get_link(ino=%lu)\n", inode->i_ino);
bh = affs_bread(inode->i_sb, inode->i_ino);
if (!bh)
@@ -57,12 +57,10 @@ static int affs_symlink_readpage(struct file *file, struct page *page)
link[i] = '\0';
affs_brelse(bh);
SetPageUptodate(page);
- kunmap(page);
unlock_page(page);
return 0;
fail:
SetPageError(page);
- kunmap(page);
unlock_page(page);
return -EIO;
}
@@ -73,7 +71,6 @@ const struct address_space_operations affs_symlink_aops = {
const struct inode_operations affs_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = page_follow_link_light,
- .put_link = page_put_link,
+ .get_link = page_get_link,
.setattr = affs_notify_change,
};
diff --git a/fs/afs/inode.c b/fs/afs/inode.c
index e06f5a23352a..86cc7264c21c 100644
--- a/fs/afs/inode.c
+++ b/fs/afs/inode.c
@@ -56,6 +56,7 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
case AFS_FTYPE_SYMLINK:
inode->i_mode = S_IFLNK | vnode->status.mode;
inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
break;
default:
printk("kAFS: AFS vnode with undefined type\n");
diff --git a/fs/afs/proc.c b/fs/afs/proc.c
index 24a905b076fd..2853b4095344 100644
--- a/fs/afs/proc.c
+++ b/fs/afs/proc.c
@@ -230,14 +230,9 @@ static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
if (size <= 1 || size >= PAGE_SIZE)
return -EINVAL;
- kbuf = kmalloc(size + 1, GFP_KERNEL);
- if (!kbuf)
- return -ENOMEM;
-
- ret = -EFAULT;
- if (copy_from_user(kbuf, buf, size) != 0)
- goto done;
- kbuf[size] = 0;
+ kbuf = memdup_user_nul(buf, size);
+ if (IS_ERR(kbuf))
+ return PTR_ERR(kbuf);
/* trim to first NL */
name = memchr(kbuf, '\n', size);
@@ -315,15 +310,9 @@ static ssize_t afs_proc_rootcell_write(struct file *file,
if (size <= 1 || size >= PAGE_SIZE)
return -EINVAL;
- ret = -ENOMEM;
- kbuf = kmalloc(size + 1, GFP_KERNEL);
- if (!kbuf)
- goto nomem;
-
- ret = -EFAULT;
- if (copy_from_user(kbuf, buf, size) != 0)
- goto infault;
- kbuf[size] = 0;
+ kbuf = memdup_user_nul(buf, size);
+ if (IS_ERR(kbuf))
+ return PTR_ERR(kbuf);
/* trim to first NL */
s = memchr(kbuf, '\n', size);
@@ -337,9 +326,7 @@ static ssize_t afs_proc_rootcell_write(struct file *file,
if (ret >= 0)
ret = size; /* consume everything, always */
-infault:
kfree(kbuf);
-nomem:
_leave(" = %d", ret);
return ret;
}
diff --git a/fs/afs/super.c b/fs/afs/super.c
index 1fb4a5129f7d..81afefe7d8a6 100644
--- a/fs/afs/super.c
+++ b/fs/afs/super.c
@@ -91,7 +91,7 @@ int __init afs_fs_init(void)
afs_inode_cachep = kmem_cache_create("afs_inode_cache",
sizeof(struct afs_vnode),
0,
- SLAB_HWCACHE_ALIGN,
+ SLAB_HWCACHE_ALIGN|SLAB_ACCOUNT,
afs_i_init_once);
if (!afs_inode_cachep) {
printk(KERN_NOTICE "kAFS: Failed to allocate inode cache\n");
diff --git a/fs/autofs4/symlink.c b/fs/autofs4/symlink.c
index da0c33481bc0..84e037d1d129 100644
--- a/fs/autofs4/symlink.c
+++ b/fs/autofs4/symlink.c
@@ -12,10 +12,16 @@
#include "autofs_i.h"
-static const char *autofs4_follow_link(struct dentry *dentry, void **cookie)
+static const char *autofs4_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
- struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
- struct autofs_info *ino = autofs4_dentry_ino(dentry);
+ struct autofs_sb_info *sbi;
+ struct autofs_info *ino;
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
+ sbi = autofs4_sbi(dentry->d_sb);
+ ino = autofs4_dentry_ino(dentry);
if (ino && !autofs4_oz_mode(sbi))
ino->last_used = jiffies;
return d_inode(dentry)->i_private;
@@ -23,5 +29,5 @@ static const char *autofs4_follow_link(struct dentry *dentry, void **cookie)
const struct inode_operations autofs4_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = autofs4_follow_link
+ .get_link = autofs4_get_link
};
diff --git a/fs/bad_inode.c b/fs/bad_inode.c
index 861b1e1c4777..103f5d7c3083 100644
--- a/fs/bad_inode.c
+++ b/fs/bad_inode.c
@@ -192,7 +192,7 @@ EXPORT_SYMBOL(make_bad_inode);
* Returns true if the inode in question has been marked as bad.
*/
-int is_bad_inode(struct inode *inode)
+bool is_bad_inode(struct inode *inode)
{
return (inode->i_op == &bad_inode_ops);
}
diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c
index 46aedacfa6a8..cc0e08252913 100644
--- a/fs/befs/linuxvfs.c
+++ b/fs/befs/linuxvfs.c
@@ -42,7 +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 const char *befs_follow_link(struct dentry *, void **);
+static int befs_symlink_readpage(struct file *, struct page *);
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,
@@ -79,10 +79,8 @@ static const struct address_space_operations befs_aops = {
.bmap = befs_bmap,
};
-static const struct inode_operations befs_symlink_inode_operations = {
- .readlink = generic_readlink,
- .follow_link = befs_follow_link,
- .put_link = kfree_put_link,
+static const struct address_space_operations befs_symlink_aops = {
+ .readpage = befs_symlink_readpage,
};
/*
@@ -398,7 +396,9 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino)
inode->i_fop = &befs_dir_operations;
} else if (S_ISLNK(inode->i_mode)) {
if (befs_ino->i_flags & BEFS_LONG_SYMLINK) {
- inode->i_op = &befs_symlink_inode_operations;
+ inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
+ inode->i_mapping->a_ops = &befs_symlink_aops;
} else {
inode->i_link = befs_ino->i_data.symlink;
inode->i_op = &simple_symlink_inode_operations;
@@ -434,7 +434,7 @@ befs_init_inodecache(void)
befs_inode_cachep = kmem_cache_create("befs_inode_cache",
sizeof (struct befs_inode_info),
0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
init_once);
if (befs_inode_cachep == NULL) {
pr_err("%s: Couldn't initialize inode slabcache\n", __func__);
@@ -463,31 +463,33 @@ befs_destroy_inodecache(void)
* The data stream become link name. Unless the LONG_SYMLINK
* flag is set.
*/
-static const char *
-befs_follow_link(struct dentry *dentry, void **cookie)
+static int befs_symlink_readpage(struct file *unused, struct page *page)
{
- struct super_block *sb = dentry->d_sb;
- struct befs_inode_info *befs_ino = BEFS_I(d_inode(dentry));
+ struct inode *inode = page->mapping->host;
+ struct super_block *sb = inode->i_sb;
+ struct befs_inode_info *befs_ino = BEFS_I(inode);
befs_data_stream *data = &befs_ino->i_data.ds;
befs_off_t len = data->size;
- char *link;
+ char *link = page_address(page);
- if (len == 0) {
+ if (len == 0 || len > PAGE_SIZE) {
befs_error(sb, "Long symlink with illegal length");
- return ERR_PTR(-EIO);
+ goto fail;
}
befs_debug(sb, "Follow long symlink");
- 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);
+ goto fail;
}
link[len - 1] = '\0';
- return *cookie = link;
+ SetPageUptodate(page);
+ unlock_page(page);
+ return 0;
+fail:
+ SetPageError(page);
+ unlock_page(page);
+ return -EIO;
}
/*
diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c
index fdcb4d69f430..1e5c896f6b79 100644
--- a/fs/bfs/inode.c
+++ b/fs/bfs/inode.c
@@ -270,7 +270,7 @@ static int __init init_inodecache(void)
bfs_inode_cachep = kmem_cache_create("bfs_inode_cache",
sizeof(struct bfs_inode_info),
0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
init_once);
if (bfs_inode_cachep == NULL)
return -ENOMEM;
diff --git a/fs/block_dev.c b/fs/block_dev.c
index c25639e907bd..81c0705558be 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -156,11 +156,16 @@ blkdev_get_block(struct inode *inode, sector_t iblock,
return 0;
}
+static struct inode *bdev_file_inode(struct file *file)
+{
+ return file->f_mapping->host;
+}
+
static ssize_t
blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter, loff_t offset)
{
struct file *file = iocb->ki_filp;
- struct inode *inode = file->f_mapping->host;
+ struct inode *inode = bdev_file_inode(file);
if (IS_DAX(inode))
return dax_do_io(iocb, inode, iter, offset, blkdev_get_block,
@@ -338,7 +343,7 @@ static int blkdev_write_end(struct file *file, struct address_space *mapping,
*/
static loff_t block_llseek(struct file *file, loff_t offset, int whence)
{
- struct inode *bd_inode = file->f_mapping->host;
+ struct inode *bd_inode = bdev_file_inode(file);
loff_t retval;
mutex_lock(&bd_inode->i_mutex);
@@ -349,7 +354,7 @@ static loff_t block_llseek(struct file *file, loff_t offset, int whence)
int blkdev_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
{
- struct inode *bd_inode = filp->f_mapping->host;
+ struct inode *bd_inode = bdev_file_inode(filp);
struct block_device *bdev = I_BDEV(bd_inode);
int error;
@@ -432,7 +437,7 @@ int bdev_write_page(struct block_device *bdev, sector_t sector,
if (!ops->rw_page || bdev_get_integrity(bdev))
return -EOPNOTSUPP;
- result = blk_queue_enter(bdev->bd_queue, GFP_KERNEL);
+ result = blk_queue_enter(bdev->bd_queue, GFP_NOIO);
if (result)
return result;
@@ -590,7 +595,7 @@ void __init bdev_cache_init(void)
bdev_cachep = kmem_cache_create("bdev_cache", sizeof(struct bdev_inode),
0, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD|SLAB_PANIC),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT|SLAB_PANIC),
init_once);
err = register_filesystem(&bd_type);
if (err)
@@ -1042,12 +1047,9 @@ EXPORT_SYMBOL_GPL(bd_unlink_disk_holder);
static void flush_disk(struct block_device *bdev, bool kill_dirty)
{
if (__invalidate_device(bdev, kill_dirty)) {
- char name[BDEVNAME_SIZE] = "";
-
- if (bdev->bd_disk)
- disk_name(bdev->bd_disk, 0, name);
printk(KERN_WARNING "VFS: busy inodes on changed media or "
- "resized disk %s\n", name);
+ "resized disk %s\n",
+ bdev->bd_disk ? bdev->bd_disk->disk_name : "");
}
if (!bdev->bd_disk)
@@ -1071,12 +1073,9 @@ void check_disk_size_change(struct gendisk *disk, struct block_device *bdev)
disk_size = (loff_t)get_capacity(disk) << 9;
bdev_size = i_size_read(bdev->bd_inode);
if (disk_size != bdev_size) {
- char name[BDEVNAME_SIZE];
-
- disk_name(disk, 0, name);
printk(KERN_INFO
"%s: detected capacity change from %lld to %lld\n",
- name, bdev_size, disk_size);
+ disk->disk_name, bdev_size, disk_size);
i_size_write(bdev->bd_inode, disk_size);
flush_disk(bdev, false);
}
@@ -1230,8 +1229,11 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
}
}
- if (!ret)
+ if (!ret) {
bd_set_size(bdev,(loff_t)get_capacity(disk)<<9);
+ if (!blkdev_dax_capable(bdev))
+ bdev->bd_inode->i_flags &= ~S_DAX;
+ }
/*
* If the device is invalidated, rescan partition
@@ -1245,6 +1247,7 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
else if (ret == -ENOMEDIUM)
invalidate_partitions(disk, bdev);
}
+
if (ret)
goto out_clear;
} else {
@@ -1265,12 +1268,7 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
goto out_clear;
}
bd_set_size(bdev, (loff_t)bdev->bd_part->nr_sects << 9);
- /*
- * If the partition is not aligned on a page
- * boundary, we can't do dax I/O to it.
- */
- if ((bdev->bd_part->start_sect % (PAGE_SIZE / 512)) ||
- (bdev->bd_part->nr_sects % (PAGE_SIZE / 512)))
+ if (!blkdev_dax_capable(bdev))
bdev->bd_inode->i_flags &= ~S_DAX;
}
} else {
@@ -1523,11 +1521,14 @@ static void __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part)
WARN_ON_ONCE(bdev->bd_holders);
sync_blockdev(bdev);
kill_bdev(bdev);
+
+ bdev_write_inode(bdev);
/*
- * ->release can cause the queue to disappear, so flush all
- * dirty data before.
+ * Detaching bdev inode from its wb in __destroy_inode()
+ * is too late: the queue which embeds its bdi (along with
+ * root wb) can be gone as soon as we put_disk() below.
*/
- bdev_write_inode(bdev);
+ inode_detach_wb(bdev->bd_inode);
}
if (bdev->bd_contains == bdev) {
if (disk->fops->release)
@@ -1602,14 +1603,14 @@ EXPORT_SYMBOL(blkdev_put);
static int blkdev_close(struct inode * inode, struct file * filp)
{
- struct block_device *bdev = I_BDEV(filp->f_mapping->host);
+ struct block_device *bdev = I_BDEV(bdev_file_inode(filp));
blkdev_put(bdev, filp->f_mode);
return 0;
}
static long block_ioctl(struct file *file, unsigned cmd, unsigned long arg)
{
- struct block_device *bdev = I_BDEV(file->f_mapping->host);
+ struct block_device *bdev = I_BDEV(bdev_file_inode(file));
fmode_t mode = file->f_mode;
/*
@@ -1634,7 +1635,7 @@ static long block_ioctl(struct file *file, unsigned cmd, unsigned long arg)
ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
struct file *file = iocb->ki_filp;
- struct inode *bd_inode = file->f_mapping->host;
+ struct inode *bd_inode = bdev_file_inode(file);
loff_t size = i_size_read(bd_inode);
struct blk_plug plug;
ssize_t ret;
@@ -1666,7 +1667,7 @@ EXPORT_SYMBOL_GPL(blkdev_write_iter);
ssize_t blkdev_read_iter(struct kiocb *iocb, struct iov_iter *to)
{
struct file *file = iocb->ki_filp;
- struct inode *bd_inode = file->f_mapping->host;
+ struct inode *bd_inode = bdev_file_inode(file);
loff_t size = i_size_read(bd_inode);
loff_t pos = iocb->ki_pos;
@@ -1705,13 +1706,101 @@ static const struct address_space_operations def_blk_aops = {
.is_dirty_writeback = buffer_check_dirty_writeback,
};
+#ifdef CONFIG_FS_DAX
+/*
+ * In the raw block case we do not need to contend with truncation nor
+ * unwritten file extents. Without those concerns there is no need for
+ * additional locking beyond the mmap_sem context that these routines
+ * are already executing under.
+ *
+ * Note, there is no protection if the block device is dynamically
+ * resized (partition grow/shrink) during a fault. A stable block device
+ * size is already not enforced in the blkdev_direct_IO path.
+ *
+ * For DAX, it is the responsibility of the block device driver to
+ * ensure the whole-disk device size is stable while requests are in
+ * flight.
+ *
+ * Finally, unlike the filemap_page_mkwrite() case there is no
+ * filesystem superblock to sync against freezing. We still include a
+ * pfn_mkwrite callback for dax drivers to receive write fault
+ * notifications.
+ */
+static int blkdev_dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ return __dax_fault(vma, vmf, blkdev_get_block, NULL);
+}
+
+static int blkdev_dax_pmd_fault(struct vm_area_struct *vma, unsigned long addr,
+ pmd_t *pmd, unsigned int flags)
+{
+ return __dax_pmd_fault(vma, addr, pmd, flags, blkdev_get_block, NULL);
+}
+
+static void blkdev_vm_open(struct vm_area_struct *vma)
+{
+ struct inode *bd_inode = bdev_file_inode(vma->vm_file);
+ struct block_device *bdev = I_BDEV(bd_inode);
+
+ mutex_lock(&bd_inode->i_mutex);
+ bdev->bd_map_count++;
+ mutex_unlock(&bd_inode->i_mutex);
+}
+
+static void blkdev_vm_close(struct vm_area_struct *vma)
+{
+ struct inode *bd_inode = bdev_file_inode(vma->vm_file);
+ struct block_device *bdev = I_BDEV(bd_inode);
+
+ mutex_lock(&bd_inode->i_mutex);
+ bdev->bd_map_count--;
+ mutex_unlock(&bd_inode->i_mutex);
+}
+
+static const struct vm_operations_struct blkdev_dax_vm_ops = {
+ .open = blkdev_vm_open,
+ .close = blkdev_vm_close,
+ .fault = blkdev_dax_fault,
+ .pmd_fault = blkdev_dax_pmd_fault,
+ .pfn_mkwrite = blkdev_dax_fault,
+};
+
+static const struct vm_operations_struct blkdev_default_vm_ops = {
+ .open = blkdev_vm_open,
+ .close = blkdev_vm_close,
+ .fault = filemap_fault,
+ .map_pages = filemap_map_pages,
+};
+
+static int blkdev_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct inode *bd_inode = bdev_file_inode(file);
+ struct block_device *bdev = I_BDEV(bd_inode);
+
+ file_accessed(file);
+ mutex_lock(&bd_inode->i_mutex);
+ bdev->bd_map_count++;
+ if (IS_DAX(bd_inode)) {
+ vma->vm_ops = &blkdev_dax_vm_ops;
+ vma->vm_flags |= VM_MIXEDMAP | VM_HUGEPAGE;
+ } else {
+ vma->vm_ops = &blkdev_default_vm_ops;
+ }
+ mutex_unlock(&bd_inode->i_mutex);
+
+ return 0;
+}
+#else
+#define blkdev_mmap generic_file_mmap
+#endif
+
const struct file_operations def_blk_fops = {
.open = blkdev_open,
.release = blkdev_close,
.llseek = block_llseek,
.read_iter = blkdev_read_iter,
.write_iter = blkdev_write_iter,
- .mmap = generic_file_mmap,
+ .mmap = blkdev_mmap,
.fsync = blkdev_fsync,
.unlocked_ioctl = block_ioctl,
#ifdef CONFIG_COMPAT
diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c
index 9a0124a95851..f89db0c21b51 100644
--- a/fs/btrfs/acl.c
+++ b/fs/btrfs/acl.c
@@ -37,10 +37,10 @@ struct posix_acl *btrfs_get_acl(struct inode *inode, int type)
switch (type) {
case ACL_TYPE_ACCESS:
- name = POSIX_ACL_XATTR_ACCESS;
+ name = XATTR_NAME_POSIX_ACL_ACCESS;
break;
case ACL_TYPE_DEFAULT:
- name = POSIX_ACL_XATTR_DEFAULT;
+ name = XATTR_NAME_POSIX_ACL_DEFAULT;
break;
default:
BUG();
@@ -81,7 +81,7 @@ static int __btrfs_set_acl(struct btrfs_trans_handle *trans,
switch (type) {
case ACL_TYPE_ACCESS:
- name = POSIX_ACL_XATTR_ACCESS;
+ name = XATTR_NAME_POSIX_ACL_ACCESS;
if (acl) {
ret = posix_acl_equiv_mode(acl, &inode->i_mode);
if (ret < 0)
@@ -94,7 +94,7 @@ static int __btrfs_set_acl(struct btrfs_trans_handle *trans,
case ACL_TYPE_DEFAULT:
if (!S_ISDIR(inode->i_mode))
return acl ? -EINVAL : 0;
- name = POSIX_ACL_XATTR_DEFAULT;
+ name = XATTR_NAME_POSIX_ACL_DEFAULT;
break;
default:
return -EINVAL;
diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
index 6dcdb2ec9211..d453d62ab0c6 100644
--- a/fs/btrfs/backref.c
+++ b/fs/btrfs/backref.c
@@ -355,7 +355,7 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
index = srcu_read_lock(&fs_info->subvol_srcu);
- root = btrfs_read_fs_root_no_name(fs_info, &root_key);
+ root = btrfs_get_fs_root(fs_info, &root_key, false);
if (IS_ERR(root)) {
srcu_read_unlock(&fs_info->subvol_srcu, index);
ret = PTR_ERR(root);
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 8c58191249cc..b7e4e344e8e0 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -3416,6 +3416,7 @@ int btrfs_cross_ref_exist(struct btrfs_trans_handle *trans,
struct btrfs_block_group_cache *btrfs_lookup_block_group(
struct btrfs_fs_info *info,
u64 bytenr);
+void btrfs_get_block_group(struct btrfs_block_group_cache *cache);
void btrfs_put_block_group(struct btrfs_block_group_cache *cache);
int get_block_group_index(struct btrfs_block_group_cache *cache);
struct extent_buffer *btrfs_alloc_tree_block(struct btrfs_trans_handle *trans,
@@ -3479,6 +3480,9 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 bytes_used,
u64 type, u64 chunk_objectid, u64 chunk_offset,
u64 size);
+struct btrfs_trans_handle *btrfs_start_trans_remove_block_group(
+ struct btrfs_fs_info *fs_info,
+ const u64 chunk_offset);
int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 group_start,
struct extent_map *em);
@@ -4020,7 +4024,8 @@ void btrfs_get_block_group_info(struct list_head *groups_list,
struct btrfs_ioctl_space_info *space);
void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, int lock,
struct btrfs_ioctl_balance_args *bargs);
-
+ssize_t btrfs_dedupe_file_range(struct file *src_file, u64 loff, u64 olen,
+ struct file *dst_file, u64 dst_loff);
/* file.c */
int btrfs_auto_defrag_init(void);
@@ -4051,6 +4056,11 @@ int btrfs_dirty_pages(struct btrfs_root *root, struct inode *inode,
loff_t pos, size_t write_bytes,
struct extent_state **cached);
int btrfs_fdatawrite_range(struct inode *inode, loff_t start, loff_t end);
+ssize_t btrfs_copy_file_range(struct file *file_in, loff_t pos_in,
+ struct file *file_out, loff_t pos_out,
+ size_t len, unsigned int flags);
+int btrfs_clone_file_range(struct file *file_in, loff_t pos_in,
+ struct file *file_out, loff_t pos_out, u64 len);
/* tree-defrag.c */
int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 974be09e7556..42a378a4eefb 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -923,7 +923,7 @@ static int check_async_write(struct inode *inode, unsigned long bio_flags)
if (bio_flags & EXTENT_BIO_TREE_LOG)
return 0;
#ifdef CONFIG_X86
- if (cpu_has_xmm4_2)
+ if (static_cpu_has_safe(X86_FEATURE_XMM4_2))
return 0;
#endif
return 1;
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index acf3ed11cfb6..c4661db2b72a 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -124,7 +124,7 @@ static int block_group_bits(struct btrfs_block_group_cache *cache, u64 bits)
return (cache->flags & bits) == bits;
}
-static void btrfs_get_block_group(struct btrfs_block_group_cache *cache)
+void btrfs_get_block_group(struct btrfs_block_group_cache *cache)
{
atomic_inc(&cache->count);
}
@@ -5915,19 +5915,6 @@ static int update_block_group(struct btrfs_trans_handle *trans,
set_extent_dirty(info->pinned_extents,
bytenr, bytenr + num_bytes - 1,
GFP_NOFS | __GFP_NOFAIL);
- /*
- * No longer have used bytes in this block group, queue
- * it for deletion.
- */
- if (old_val == 0) {
- spin_lock(&info->unused_bgs_lock);
- if (list_empty(&cache->bg_list)) {
- btrfs_get_block_group(cache);
- list_add_tail(&cache->bg_list,
- &info->unused_bgs);
- }
- spin_unlock(&info->unused_bgs_lock);
- }
}
spin_lock(&trans->transaction->dirty_bgs_lock);
@@ -5939,6 +5926,22 @@ static int update_block_group(struct btrfs_trans_handle *trans,
}
spin_unlock(&trans->transaction->dirty_bgs_lock);
+ /*
+ * No longer have used bytes in this block group, queue it for
+ * deletion. We do this after adding the block group to the
+ * dirty list to avoid races between cleaner kthread and space
+ * cache writeout.
+ */
+ if (!alloc && old_val == 0) {
+ spin_lock(&info->unused_bgs_lock);
+ if (list_empty(&cache->bg_list)) {
+ btrfs_get_block_group(cache);
+ list_add_tail(&cache->bg_list,
+ &info->unused_bgs);
+ }
+ spin_unlock(&info->unused_bgs_lock);
+ }
+
btrfs_put_block_group(cache);
total -= num_bytes;
bytenr += num_bytes;
@@ -8105,21 +8108,47 @@ reada:
}
/*
- * TODO: Modify related function to add related node/leaf to dirty_extent_root,
- * for later qgroup accounting.
- *
- * Current, this function does nothing.
+ * These may not be seen by the usual inc/dec ref code so we have to
+ * add them here.
*/
+static int record_one_subtree_extent(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, u64 bytenr,
+ u64 num_bytes)
+{
+ struct btrfs_qgroup_extent_record *qrecord;
+ struct btrfs_delayed_ref_root *delayed_refs;
+
+ qrecord = kmalloc(sizeof(*qrecord), GFP_NOFS);
+ if (!qrecord)
+ return -ENOMEM;
+
+ qrecord->bytenr = bytenr;
+ qrecord->num_bytes = num_bytes;
+ qrecord->old_roots = NULL;
+
+ delayed_refs = &trans->transaction->delayed_refs;
+ spin_lock(&delayed_refs->lock);
+ if (btrfs_qgroup_insert_dirty_extent(delayed_refs, qrecord))
+ kfree(qrecord);
+ spin_unlock(&delayed_refs->lock);
+
+ return 0;
+}
+
static int account_leaf_items(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct extent_buffer *eb)
{
int nr = btrfs_header_nritems(eb);
- int i, extent_type;
+ int i, extent_type, ret;
struct btrfs_key key;
struct btrfs_file_extent_item *fi;
u64 bytenr, num_bytes;
+ /* We can be called directly from walk_up_proc() */
+ if (!root->fs_info->quota_enabled)
+ return 0;
+
for (i = 0; i < nr; i++) {
btrfs_item_key_to_cpu(eb, &key, i);
@@ -8138,6 +8167,10 @@ static int account_leaf_items(struct btrfs_trans_handle *trans,
continue;
num_bytes = btrfs_file_extent_disk_num_bytes(eb, fi);
+
+ ret = record_one_subtree_extent(trans, root, bytenr, num_bytes);
+ if (ret)
+ return ret;
}
return 0;
}
@@ -8206,8 +8239,6 @@ static int adjust_slots_upwards(struct btrfs_root *root,
/*
* root_eb is the subtree root and is locked before this function is called.
- * TODO: Modify this function to mark all (including complete shared node)
- * to dirty_extent_root to allow it get accounted in qgroup.
*/
static int account_shared_subtree(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
@@ -8285,6 +8316,11 @@ walk_down:
btrfs_tree_read_lock(eb);
btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK);
path->locks[level] = BTRFS_READ_LOCK_BLOCKING;
+
+ ret = record_one_subtree_extent(trans, root, child_bytenr,
+ root->nodesize);
+ if (ret)
+ goto out;
}
if (level == 0) {
@@ -10256,6 +10292,47 @@ out:
return ret;
}
+struct btrfs_trans_handle *
+btrfs_start_trans_remove_block_group(struct btrfs_fs_info *fs_info,
+ const u64 chunk_offset)
+{
+ struct extent_map_tree *em_tree = &fs_info->mapping_tree.map_tree;
+ struct extent_map *em;
+ struct map_lookup *map;
+ unsigned int num_items;
+
+ read_lock(&em_tree->lock);
+ em = lookup_extent_mapping(em_tree, chunk_offset, 1);
+ read_unlock(&em_tree->lock);
+ ASSERT(em && em->start == chunk_offset);
+
+ /*
+ * We need to reserve 3 + N units from the metadata space info in order
+ * to remove a block group (done at btrfs_remove_chunk() and at
+ * btrfs_remove_block_group()), which are used for:
+ *
+ * 1 unit for adding the free space inode's orphan (located in the tree
+ * of tree roots).
+ * 1 unit for deleting the block group item (located in the extent
+ * tree).
+ * 1 unit for deleting the free space item (located in tree of tree
+ * roots).
+ * N units for deleting N device extent items corresponding to each
+ * stripe (located in the device tree).
+ *
+ * In order to remove a block group we also need to reserve units in the
+ * system space info in order to update the chunk tree (update one or
+ * more device items and remove one chunk item), but this is done at
+ * btrfs_remove_chunk() through a call to check_system_chunk().
+ */
+ map = (struct map_lookup *)em->bdev;
+ num_items = 3 + map->num_stripes;
+ free_extent_map(em);
+
+ return btrfs_start_transaction_fallback_global_rsv(fs_info->extent_root,
+ num_items, 1);
+}
+
/*
* Process the unused_bgs list and remove any that don't have any allocated
* space inside of them.
@@ -10322,8 +10399,8 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
* Want to do this before we do anything else so we can recover
* properly if we fail to join the transaction.
*/
- /* 1 for btrfs_orphan_reserve_metadata() */
- trans = btrfs_start_transaction(root, 1);
+ trans = btrfs_start_trans_remove_block_group(fs_info,
+ block_group->key.objectid);
if (IS_ERR(trans)) {
btrfs_dec_block_group_ro(root, block_group);
ret = PTR_ERR(trans);
@@ -10403,11 +10480,15 @@ void btrfs_delete_unused_bgs(struct btrfs_fs_info *fs_info)
* until transaction commit to do the actual discard.
*/
if (trimming) {
- WARN_ON(!list_empty(&block_group->bg_list));
- spin_lock(&trans->transaction->deleted_bgs_lock);
+ spin_lock(&fs_info->unused_bgs_lock);
+ /*
+ * A concurrent scrub might have added us to the list
+ * fs_info->unused_bgs, so use a list_move operation
+ * to add the block group to the deleted_bgs list.
+ */
list_move(&block_group->bg_list,
&trans->transaction->deleted_bgs);
- spin_unlock(&trans->transaction->deleted_bgs_lock);
+ spin_unlock(&fs_info->unused_bgs_lock);
btrfs_get_block_group(block_group);
}
end_trans:
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 977e715f0bf2..e3d9022bfd4e 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1291,7 +1291,8 @@ out:
* on error we return an unlocked page and the error value
* on success we return a locked page and 0
*/
-static int prepare_uptodate_page(struct page *page, u64 pos,
+static int prepare_uptodate_page(struct inode *inode,
+ struct page *page, u64 pos,
bool force_uptodate)
{
int ret = 0;
@@ -1306,6 +1307,10 @@ static int prepare_uptodate_page(struct page *page, u64 pos,
unlock_page(page);
return -EIO;
}
+ if (page->mapping != inode->i_mapping) {
+ unlock_page(page);
+ return -EAGAIN;
+ }
}
return 0;
}
@@ -1324,6 +1329,7 @@ static noinline int prepare_pages(struct inode *inode, struct page **pages,
int faili;
for (i = 0; i < num_pages; i++) {
+again:
pages[i] = find_or_create_page(inode->i_mapping, index + i,
mask | __GFP_WRITE);
if (!pages[i]) {
@@ -1333,13 +1339,17 @@ static noinline int prepare_pages(struct inode *inode, struct page **pages,
}
if (i == 0)
- err = prepare_uptodate_page(pages[i], pos,
+ err = prepare_uptodate_page(inode, pages[i], pos,
force_uptodate);
- if (i == num_pages - 1)
- err = prepare_uptodate_page(pages[i],
+ if (!err && i == num_pages - 1)
+ err = prepare_uptodate_page(inode, pages[i],
pos + write_bytes, false);
if (err) {
page_cache_release(pages[i]);
+ if (err == -EAGAIN) {
+ err = 0;
+ goto again;
+ }
faili = i - 1;
goto fail;
}
@@ -1882,8 +1892,13 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
struct btrfs_log_ctx ctx;
int ret = 0;
bool full_sync = 0;
- const u64 len = end - start + 1;
+ u64 len;
+ /*
+ * The range length can be represented by u64, we have to do the typecasts
+ * to avoid signed overflow if it's [0, LLONG_MAX] eg. from fsync()
+ */
+ len = (u64)end - (u64)start + 1;
trace_btrfs_sync_file(file, datasync);
/*
@@ -2071,8 +2086,7 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
}
}
if (!full_sync) {
- ret = btrfs_wait_ordered_range(inode, start,
- end - start + 1);
+ ret = btrfs_wait_ordered_range(inode, start, len);
if (ret) {
btrfs_end_transaction(trans, root);
goto out;
@@ -2920,6 +2934,9 @@ const struct file_operations btrfs_file_operations = {
#ifdef CONFIG_COMPAT
.compat_ioctl = btrfs_ioctl,
#endif
+ .copy_file_range = btrfs_copy_file_range,
+ .clone_file_range = btrfs_clone_file_range,
+ .dedupe_file_range = btrfs_dedupe_file_range,
};
void btrfs_auto_defrag_exit(void)
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c
index 85a1f8621b51..cfe99bec49de 100644
--- a/fs/btrfs/free-space-cache.c
+++ b/fs/btrfs/free-space-cache.c
@@ -891,7 +891,7 @@ out:
spin_unlock(&block_group->lock);
ret = 0;
- btrfs_warn(fs_info, "failed to load free space cache for block group %llu, rebuild it now",
+ btrfs_warn(fs_info, "failed to load free space cache for block group %llu, rebuilding it now",
block_group->key.objectid);
}
@@ -2972,7 +2972,7 @@ setup_cluster_bitmap(struct btrfs_block_group_cache *block_group,
u64 cont1_bytes, u64 min_bytes)
{
struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl;
- struct btrfs_free_space *entry;
+ struct btrfs_free_space *entry = NULL;
int ret = -ENOSPC;
u64 bitmap_offset = offset_to_bitmap(ctl, offset);
@@ -2983,8 +2983,10 @@ setup_cluster_bitmap(struct btrfs_block_group_cache *block_group,
* The bitmap that covers offset won't be in the list unless offset
* is just its start offset.
*/
- entry = list_first_entry(bitmaps, struct btrfs_free_space, list);
- if (entry->offset != bitmap_offset) {
+ if (!list_empty(bitmaps))
+ entry = list_first_entry(bitmaps, struct btrfs_free_space, list);
+
+ if (!entry || entry->offset != bitmap_offset) {
entry = tree_search_offset(ctl, bitmap_offset, 1, 0);
if (entry && list_empty(&entry->list))
list_add(&entry->list, bitmaps);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 994490d5fa64..394017831692 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3550,10 +3550,10 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf,
int scanned = 0;
if (!xattr_access) {
- xattr_access = btrfs_name_hash(POSIX_ACL_XATTR_ACCESS,
- strlen(POSIX_ACL_XATTR_ACCESS));
- xattr_default = btrfs_name_hash(POSIX_ACL_XATTR_DEFAULT,
- strlen(POSIX_ACL_XATTR_DEFAULT));
+ xattr_access = btrfs_name_hash(XATTR_NAME_POSIX_ACL_ACCESS,
+ strlen(XATTR_NAME_POSIX_ACL_ACCESS));
+ xattr_default = btrfs_name_hash(XATTR_NAME_POSIX_ACL_DEFAULT,
+ strlen(XATTR_NAME_POSIX_ACL_DEFAULT));
}
slot++;
@@ -3774,6 +3774,7 @@ cache_acl:
break;
case S_IFLNK:
inode->i_op = &btrfs_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_mapping->a_ops = &btrfs_symlink_aops;
break;
default:
@@ -4046,9 +4047,7 @@ int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
*/
static struct btrfs_trans_handle *__unlink_start_trans(struct inode *dir)
{
- struct btrfs_trans_handle *trans;
struct btrfs_root *root = BTRFS_I(dir)->root;
- int ret;
/*
* 1 for the possible orphan item
@@ -4057,27 +4056,7 @@ static struct btrfs_trans_handle *__unlink_start_trans(struct inode *dir)
* 1 for the inode ref
* 1 for the inode
*/
- trans = btrfs_start_transaction(root, 5);
- if (!IS_ERR(trans) || PTR_ERR(trans) != -ENOSPC)
- return trans;
-
- if (PTR_ERR(trans) == -ENOSPC) {
- u64 num_bytes = btrfs_calc_trans_metadata_size(root, 5);
-
- trans = btrfs_start_transaction(root, 0);
- if (IS_ERR(trans))
- return trans;
- ret = btrfs_cond_migrate_bytes(root->fs_info,
- &root->fs_info->trans_block_rsv,
- num_bytes, 5);
- if (ret) {
- btrfs_end_transaction(trans, root);
- return ERR_PTR(ret);
- }
- trans->block_rsv = &root->fs_info->trans_block_rsv;
- trans->bytes_reserved = num_bytes;
- }
- return trans;
+ return btrfs_start_transaction_fallback_global_rsv(root, 5, 5);
}
static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
@@ -9182,7 +9161,8 @@ int btrfs_init_cachep(void)
{
btrfs_inode_cachep = kmem_cache_create("btrfs_inode",
sizeof(struct btrfs_inode), 0,
- SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, init_once);
+ SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD | SLAB_ACCOUNT,
+ init_once);
if (!btrfs_inode_cachep)
goto fail;
@@ -9727,6 +9707,7 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
btrfs_free_path(path);
inode->i_op = &btrfs_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_mapping->a_ops = &btrfs_symlink_aops;
inode_set_bytes(inode, name_len);
btrfs_i_size_write(inode, name_len);
@@ -10016,7 +9997,7 @@ static const struct inode_operations btrfs_dir_inode_operations = {
.setattr = btrfs_setattr,
.mknod = btrfs_mknod,
.setxattr = btrfs_setxattr,
- .getxattr = btrfs_getxattr,
+ .getxattr = generic_getxattr,
.listxattr = btrfs_listxattr,
.removexattr = btrfs_removexattr,
.permission = btrfs_permission,
@@ -10093,7 +10074,7 @@ static const struct inode_operations btrfs_file_inode_operations = {
.getattr = btrfs_getattr,
.setattr = btrfs_setattr,
.setxattr = btrfs_setxattr,
- .getxattr = btrfs_getxattr,
+ .getxattr = generic_getxattr,
.listxattr = btrfs_listxattr,
.removexattr = btrfs_removexattr,
.permission = btrfs_permission,
@@ -10107,7 +10088,7 @@ static const struct inode_operations btrfs_special_inode_operations = {
.setattr = btrfs_setattr,
.permission = btrfs_permission,
.setxattr = btrfs_setxattr,
- .getxattr = btrfs_getxattr,
+ .getxattr = generic_getxattr,
.listxattr = btrfs_listxattr,
.removexattr = btrfs_removexattr,
.get_acl = btrfs_get_acl,
@@ -10116,13 +10097,12 @@ static const struct inode_operations btrfs_special_inode_operations = {
};
static const struct inode_operations btrfs_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = page_follow_link_light,
- .put_link = page_put_link,
+ .get_link = page_get_link,
.getattr = btrfs_getattr,
.setattr = btrfs_setattr,
.permission = btrfs_permission,
.setxattr = btrfs_setxattr,
- .getxattr = btrfs_getxattr,
+ .getxattr = generic_getxattr,
.listxattr = btrfs_listxattr,
.removexattr = btrfs_removexattr,
.update_time = btrfs_update_time,
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index da94138eb85e..e21997385d14 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -2962,7 +2962,7 @@ static int btrfs_cmp_data(struct inode *src, u64 loff, struct inode *dst,
flush_dcache_page(dst_page);
if (memcmp(addr, dst_addr, cmp_len))
- ret = BTRFS_SAME_DATA_DIFFERS;
+ ret = -EBADE;
kunmap_atomic(addr);
kunmap_atomic(dst_addr);
@@ -3098,53 +3098,16 @@ out_unlock:
#define BTRFS_MAX_DEDUPE_LEN (16 * 1024 * 1024)
-static long btrfs_ioctl_file_extent_same(struct file *file,
- struct btrfs_ioctl_same_args __user *argp)
+ssize_t btrfs_dedupe_file_range(struct file *src_file, u64 loff, u64 olen,
+ struct file *dst_file, u64 dst_loff)
{
- struct btrfs_ioctl_same_args *same = NULL;
- struct btrfs_ioctl_same_extent_info *info;
- struct inode *src = file_inode(file);
- u64 off;
- u64 len;
- int i;
- int ret;
- unsigned long size;
+ struct inode *src = file_inode(src_file);
+ struct inode *dst = file_inode(dst_file);
u64 bs = BTRFS_I(src)->root->fs_info->sb->s_blocksize;
- bool is_admin = capable(CAP_SYS_ADMIN);
- u16 count;
-
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
-
- ret = mnt_want_write_file(file);
- if (ret)
- return ret;
-
- if (get_user(count, &argp->dest_count)) {
- ret = -EFAULT;
- goto out;
- }
-
- size = offsetof(struct btrfs_ioctl_same_args __user, info[count]);
-
- same = memdup_user(argp, size);
-
- if (IS_ERR(same)) {
- ret = PTR_ERR(same);
- same = NULL;
- goto out;
- }
+ ssize_t res;
- off = same->logical_offset;
- len = same->length;
-
- /*
- * Limit the total length we will dedupe for each operation.
- * This is intended to bound the total time spent in this
- * ioctl to something sane.
- */
- if (len > BTRFS_MAX_DEDUPE_LEN)
- len = BTRFS_MAX_DEDUPE_LEN;
+ if (olen > BTRFS_MAX_DEDUPE_LEN)
+ olen = BTRFS_MAX_DEDUPE_LEN;
if (WARN_ON_ONCE(bs < PAGE_CACHE_SIZE)) {
/*
@@ -3152,58 +3115,13 @@ static long btrfs_ioctl_file_extent_same(struct file *file,
* result, btrfs_cmp_data() won't correctly handle
* this situation without an update.
*/
- ret = -EINVAL;
- goto out;
- }
-
- ret = -EISDIR;
- if (S_ISDIR(src->i_mode))
- goto out;
-
- ret = -EACCES;
- if (!S_ISREG(src->i_mode))
- goto out;
-
- /* pre-format output fields to sane values */
- for (i = 0; i < count; i++) {
- same->info[i].bytes_deduped = 0ULL;
- same->info[i].status = 0;
- }
-
- for (i = 0, info = same->info; i < count; i++, info++) {
- struct inode *dst;
- struct fd dst_file = fdget(info->fd);
- if (!dst_file.file) {
- info->status = -EBADF;
- continue;
- }
- dst = file_inode(dst_file.file);
-
- if (!(is_admin || (dst_file.file->f_mode & FMODE_WRITE))) {
- info->status = -EINVAL;
- } else if (file->f_path.mnt != dst_file.file->f_path.mnt) {
- info->status = -EXDEV;
- } else if (S_ISDIR(dst->i_mode)) {
- info->status = -EISDIR;
- } else if (!S_ISREG(dst->i_mode)) {
- info->status = -EACCES;
- } else {
- info->status = btrfs_extent_same(src, off, len, dst,
- info->logical_offset);
- if (info->status == 0)
- info->bytes_deduped += len;
- }
- fdput(dst_file);
+ return -EINVAL;
}
- ret = copy_to_user(argp, same, size);
- if (ret)
- ret = -EFAULT;
-
-out:
- mnt_drop_write_file(file);
- kfree(same);
- return ret;
+ res = btrfs_extent_same(src, loff, olen, dst, dst_loff);
+ if (res)
+ return res;
+ return olen;
}
static int clone_finish_inode_update(struct btrfs_trans_handle *trans,
@@ -3779,17 +3697,16 @@ out:
return ret;
}
-static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
- u64 off, u64 olen, u64 destoff)
+static noinline int btrfs_clone_files(struct file *file, struct file *file_src,
+ u64 off, u64 olen, u64 destoff)
{
struct inode *inode = file_inode(file);
+ struct inode *src = file_inode(file_src);
struct btrfs_root *root = BTRFS_I(inode)->root;
- struct fd src_file;
- struct inode *src;
int ret;
u64 len = olen;
u64 bs = root->fs_info->sb->s_blocksize;
- int same_inode = 0;
+ int same_inode = src == inode;
/*
* TODO:
@@ -3802,49 +3719,20 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
* be either compressed or non-compressed.
*/
- /* the destination must be opened for writing */
- if (!(file->f_mode & FMODE_WRITE) || (file->f_flags & O_APPEND))
- return -EINVAL;
-
if (btrfs_root_readonly(root))
return -EROFS;
- ret = mnt_want_write_file(file);
- if (ret)
- return ret;
-
- src_file = fdget(srcfd);
- if (!src_file.file) {
- ret = -EBADF;
- goto out_drop_write;
- }
-
- ret = -EXDEV;
- if (src_file.file->f_path.mnt != file->f_path.mnt)
- goto out_fput;
-
- src = file_inode(src_file.file);
-
- ret = -EINVAL;
- if (src == inode)
- same_inode = 1;
-
- /* the src must be open for reading */
- if (!(src_file.file->f_mode & FMODE_READ))
- goto out_fput;
+ if (file_src->f_path.mnt != file->f_path.mnt ||
+ src->i_sb != inode->i_sb)
+ return -EXDEV;
/* don't make the dst file partly checksummed */
if ((BTRFS_I(src)->flags & BTRFS_INODE_NODATASUM) !=
(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM))
- goto out_fput;
+ return -EINVAL;
- ret = -EISDIR;
if (S_ISDIR(src->i_mode) || S_ISDIR(inode->i_mode))
- goto out_fput;
-
- ret = -EXDEV;
- if (src->i_sb != inode->i_sb)
- goto out_fput;
+ return -EISDIR;
if (!same_inode) {
btrfs_double_inode_lock(src, inode);
@@ -3921,21 +3809,25 @@ out_unlock:
btrfs_double_inode_unlock(src, inode);
else
mutex_unlock(&src->i_mutex);
-out_fput:
- fdput(src_file);
-out_drop_write:
- mnt_drop_write_file(file);
return ret;
}
-static long btrfs_ioctl_clone_range(struct file *file, void __user *argp)
+ssize_t btrfs_copy_file_range(struct file *file_in, loff_t pos_in,
+ struct file *file_out, loff_t pos_out,
+ size_t len, unsigned int flags)
{
- struct btrfs_ioctl_clone_range_args args;
+ ssize_t ret;
- if (copy_from_user(&args, argp, sizeof(args)))
- return -EFAULT;
- return btrfs_ioctl_clone(file, args.src_fd, args.src_offset,
- args.src_length, args.dest_offset);
+ ret = btrfs_clone_files(file_out, file_in, pos_in, len, pos_out);
+ if (ret == 0)
+ ret = len;
+ return ret;
+}
+
+int btrfs_clone_file_range(struct file *src_file, loff_t off,
+ struct file *dst_file, loff_t destoff, u64 len)
+{
+ return btrfs_clone_files(dst_file, src_file, off, len, destoff);
}
/*
@@ -5485,10 +5377,6 @@ long btrfs_ioctl(struct file *file, unsigned int
return btrfs_ioctl_dev_info(root, argp);
case BTRFS_IOC_BALANCE:
return btrfs_ioctl_balance(file, NULL);
- case BTRFS_IOC_CLONE:
- return btrfs_ioctl_clone(file, arg, 0, 0, 0);
- case BTRFS_IOC_CLONE_RANGE:
- return btrfs_ioctl_clone_range(file, argp);
case BTRFS_IOC_TRANS_START:
return btrfs_ioctl_trans_start(file);
case BTRFS_IOC_TRANS_END:
@@ -5566,8 +5454,6 @@ long btrfs_ioctl(struct file *file, unsigned int
return btrfs_ioctl_get_fslabel(file, argp);
case BTRFS_IOC_SET_FSLABEL:
return btrfs_ioctl_set_fslabel(file, argp);
- case BTRFS_IOC_FILE_EXTENT_SAME:
- return btrfs_ioctl_file_extent_same(file, argp);
case BTRFS_IOC_GET_SUPPORTED_FEATURES:
return btrfs_ioctl_get_supported_features(file, argp);
case BTRFS_IOC_GET_FEATURES:
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index 93e12c18ffd7..5279fdae7142 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -993,9 +993,10 @@ int btrfs_quota_disable(struct btrfs_trans_handle *trans,
mutex_lock(&fs_info->qgroup_ioctl_lock);
if (!fs_info->quota_root)
goto out;
- spin_lock(&fs_info->qgroup_lock);
fs_info->quota_enabled = 0;
fs_info->pending_quota_state = 0;
+ btrfs_qgroup_wait_for_completion(fs_info);
+ spin_lock(&fs_info->qgroup_lock);
quota_root = fs_info->quota_root;
fs_info->quota_root = NULL;
fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_ON;
@@ -1461,6 +1462,8 @@ struct btrfs_qgroup_extent_record
struct btrfs_qgroup_extent_record *entry;
u64 bytenr = record->bytenr;
+ assert_spin_locked(&delayed_refs->lock);
+
while (*p) {
parent_node = *p;
entry = rb_entry(parent_node, struct btrfs_qgroup_extent_record,
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c
index 2907a77fb1f6..b091d94ceef6 100644
--- a/fs/btrfs/scrub.c
+++ b/fs/btrfs/scrub.c
@@ -3432,7 +3432,9 @@ out:
static noinline_for_stack int scrub_chunk(struct scrub_ctx *sctx,
struct btrfs_device *scrub_dev,
u64 chunk_offset, u64 length,
- u64 dev_offset, int is_dev_replace)
+ u64 dev_offset,
+ struct btrfs_block_group_cache *cache,
+ int is_dev_replace)
{
struct btrfs_mapping_tree *map_tree =
&sctx->dev_root->fs_info->mapping_tree;
@@ -3445,8 +3447,18 @@ static noinline_for_stack int scrub_chunk(struct scrub_ctx *sctx,
em = lookup_extent_mapping(&map_tree->map_tree, chunk_offset, 1);
read_unlock(&map_tree->map_tree.lock);
- if (!em)
- return -EINVAL;
+ if (!em) {
+ /*
+ * Might have been an unused block group deleted by the cleaner
+ * kthread or relocation.
+ */
+ spin_lock(&cache->lock);
+ if (!cache->removed)
+ ret = -EINVAL;
+ spin_unlock(&cache->lock);
+
+ return ret;
+ }
map = (struct map_lookup *)em->bdev;
if (em->start != chunk_offset)
@@ -3483,6 +3495,7 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx,
u64 length;
u64 chunk_offset;
int ret = 0;
+ int ro_set;
int slot;
struct extent_buffer *l;
struct btrfs_key key;
@@ -3568,7 +3581,21 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx,
scrub_pause_on(fs_info);
ret = btrfs_inc_block_group_ro(root, cache);
scrub_pause_off(fs_info);
- if (ret) {
+
+ if (ret == 0) {
+ ro_set = 1;
+ } else if (ret == -ENOSPC) {
+ /*
+ * btrfs_inc_block_group_ro return -ENOSPC when it
+ * failed in creating new chunk for metadata.
+ * It is not a problem for scrub/replace, because
+ * metadata are always cowed, and our scrub paused
+ * commit_transactions.
+ */
+ ro_set = 0;
+ } else {
+ btrfs_warn(fs_info, "failed setting block group ro, ret=%d\n",
+ ret);
btrfs_put_block_group(cache);
break;
}
@@ -3577,7 +3604,7 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx,
dev_replace->cursor_left = found_key.offset;
dev_replace->item_needs_writeback = 1;
ret = scrub_chunk(sctx, scrub_dev, chunk_offset, length,
- found_key.offset, is_dev_replace);
+ found_key.offset, cache, is_dev_replace);
/*
* flush, submit all pending read and write bios, afterwards
@@ -3611,7 +3638,30 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx,
scrub_pause_off(fs_info);
- btrfs_dec_block_group_ro(root, cache);
+ if (ro_set)
+ btrfs_dec_block_group_ro(root, cache);
+
+ /*
+ * We might have prevented the cleaner kthread from deleting
+ * this block group if it was already unused because we raced
+ * and set it to RO mode first. So add it back to the unused
+ * list, otherwise it might not ever be deleted unless a manual
+ * balance is triggered or it becomes used and unused again.
+ */
+ spin_lock(&cache->lock);
+ if (!cache->removed && !cache->ro && cache->reserved == 0 &&
+ btrfs_block_group_used(&cache->item) == 0) {
+ spin_unlock(&cache->lock);
+ spin_lock(&fs_info->unused_bgs_lock);
+ if (list_empty(&cache->bg_list)) {
+ btrfs_get_block_group(cache);
+ list_add_tail(&cache->bg_list,
+ &fs_info->unused_bgs);
+ }
+ spin_unlock(&fs_info->unused_bgs_lock);
+ } else {
+ spin_unlock(&cache->lock);
+ }
btrfs_put_block_group(cache);
if (ret)
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 24154e422945..a0434c179ea9 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -1514,9 +1514,7 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
if ((flags ^ s->s_flags) & MS_RDONLY)
error = -EBUSY;
} else {
- char b[BDEVNAME_SIZE];
-
- strlcpy(s->s_id, bdevname(bdev, b), sizeof(s->s_id));
+ snprintf(s->s_id, sizeof(s->s_id), "%pg", bdev);
btrfs_sb(s)->bdev_holder = fs_type;
error = btrfs_fill_super(s, fs_devices, data,
flags & MS_SILENT ? 1 : 0);
diff --git a/fs/btrfs/tests/free-space-tests.c b/fs/btrfs/tests/free-space-tests.c
index c8c3d70c31ff..8b72b005bfb9 100644
--- a/fs/btrfs/tests/free-space-tests.c
+++ b/fs/btrfs/tests/free-space-tests.c
@@ -898,8 +898,10 @@ int btrfs_test_free_space_cache(void)
}
root = btrfs_alloc_dummy_root();
- if (!root)
+ if (IS_ERR(root)) {
+ ret = PTR_ERR(root);
goto out;
+ }
root->fs_info = btrfs_alloc_dummy_fs_info();
if (!root->fs_info)
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 418c6a2ad7d8..be8eae80ff65 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -274,7 +274,6 @@ loop:
cur_trans->num_dirty_bgs = 0;
spin_lock_init(&cur_trans->dirty_bgs_lock);
INIT_LIST_HEAD(&cur_trans->deleted_bgs);
- spin_lock_init(&cur_trans->deleted_bgs_lock);
spin_lock_init(&cur_trans->dropped_roots_lock);
list_add_tail(&cur_trans->list, &fs_info->trans_list);
extent_io_tree_init(&cur_trans->dirty_pages,
@@ -592,6 +591,38 @@ struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root,
return start_transaction(root, num_items, TRANS_START,
BTRFS_RESERVE_FLUSH_ALL);
}
+struct btrfs_trans_handle *btrfs_start_transaction_fallback_global_rsv(
+ struct btrfs_root *root,
+ unsigned int num_items,
+ int min_factor)
+{
+ struct btrfs_trans_handle *trans;
+ u64 num_bytes;
+ int ret;
+
+ trans = btrfs_start_transaction(root, num_items);
+ if (!IS_ERR(trans) || PTR_ERR(trans) != -ENOSPC)
+ return trans;
+
+ trans = btrfs_start_transaction(root, 0);
+ if (IS_ERR(trans))
+ return trans;
+
+ num_bytes = btrfs_calc_trans_metadata_size(root, num_items);
+ ret = btrfs_cond_migrate_bytes(root->fs_info,
+ &root->fs_info->trans_block_rsv,
+ num_bytes,
+ min_factor);
+ if (ret) {
+ btrfs_end_transaction(trans, root);
+ return ERR_PTR(ret);
+ }
+
+ trans->block_rsv = &root->fs_info->trans_block_rsv;
+ trans->bytes_reserved = num_bytes;
+
+ return trans;
+}
struct btrfs_trans_handle *btrfs_start_transaction_lflush(
struct btrfs_root *root,
diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h
index b05b2f64d913..64c8221b6165 100644
--- a/fs/btrfs/transaction.h
+++ b/fs/btrfs/transaction.h
@@ -77,8 +77,8 @@ struct btrfs_transaction {
*/
struct mutex cache_write_mutex;
spinlock_t dirty_bgs_lock;
+ /* Protected by spin lock fs_info->unused_bgs_lock. */
struct list_head deleted_bgs;
- spinlock_t deleted_bgs_lock;
spinlock_t dropped_roots_lock;
struct btrfs_delayed_ref_root delayed_refs;
int aborted;
@@ -185,6 +185,10 @@ int btrfs_end_transaction(struct btrfs_trans_handle *trans,
struct btrfs_root *root);
struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root,
unsigned int num_items);
+struct btrfs_trans_handle *btrfs_start_transaction_fallback_global_rsv(
+ struct btrfs_root *root,
+ unsigned int num_items,
+ int min_factor);
struct btrfs_trans_handle *btrfs_start_transaction_lflush(
struct btrfs_root *root,
unsigned int num_items);
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index a6df8fdc1312..a23399e8e3ab 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -1973,8 +1973,7 @@ void btrfs_rm_dev_replace_remove_srcdev(struct btrfs_fs_info *fs_info,
if (srcdev->writeable) {
fs_devices->rw_devices--;
/* zero out the old super if it is writable */
- btrfs_scratch_superblocks(srcdev->bdev,
- rcu_str_deref(srcdev->name));
+ btrfs_scratch_superblocks(srcdev->bdev, srcdev->name->str);
}
if (srcdev->bdev)
@@ -2024,8 +2023,7 @@ void btrfs_destroy_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
btrfs_sysfs_rm_device_link(fs_info->fs_devices, tgtdev);
if (tgtdev->bdev) {
- btrfs_scratch_superblocks(tgtdev->bdev,
- rcu_str_deref(tgtdev->name));
+ btrfs_scratch_superblocks(tgtdev->bdev, tgtdev->name->str);
fs_info->fs_devices->open_devices--;
}
fs_info->fs_devices->num_devices--;
@@ -2853,7 +2851,8 @@ static int btrfs_relocate_chunk(struct btrfs_root *root, u64 chunk_offset)
if (ret)
return ret;
- trans = btrfs_start_transaction(root, 0);
+ trans = btrfs_start_trans_remove_block_group(root->fs_info,
+ chunk_offset);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
btrfs_std_error(root->fs_info, ret, NULL);
@@ -3123,7 +3122,7 @@ static int chunk_profiles_filter(u64 chunk_type,
return 1;
}
-static int chunk_usage_filter(struct btrfs_fs_info *fs_info, u64 chunk_offset,
+static int chunk_usage_range_filter(struct btrfs_fs_info *fs_info, u64 chunk_offset,
struct btrfs_balance_args *bargs)
{
struct btrfs_block_group_cache *cache;
@@ -3156,7 +3155,7 @@ static int chunk_usage_filter(struct btrfs_fs_info *fs_info, u64 chunk_offset,
return ret;
}
-static int chunk_usage_range_filter(struct btrfs_fs_info *fs_info,
+static int chunk_usage_filter(struct btrfs_fs_info *fs_info,
u64 chunk_offset, struct btrfs_balance_args *bargs)
{
struct btrfs_block_group_cache *cache;
@@ -3549,12 +3548,11 @@ again:
ret = btrfs_force_chunk_alloc(trans, chunk_root,
BTRFS_BLOCK_GROUP_DATA);
+ btrfs_end_transaction(trans, chunk_root);
if (ret < 0) {
mutex_unlock(&fs_info->delete_unused_bgs_mutex);
goto error;
}
-
- btrfs_end_transaction(trans, chunk_root);
chunk_reserved = 1;
}
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index ec5712372732..d5c84f6b1353 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -382,7 +382,7 @@ struct map_lookup {
#define BTRFS_BALANCE_ARGS_LIMIT (1ULL << 5)
#define BTRFS_BALANCE_ARGS_LIMIT_RANGE (1ULL << 6)
#define BTRFS_BALANCE_ARGS_STRIPES_RANGE (1ULL << 7)
-#define BTRFS_BALANCE_ARGS_USAGE_RANGE (1ULL << 8)
+#define BTRFS_BALANCE_ARGS_USAGE_RANGE (1ULL << 10)
#define BTRFS_BALANCE_ARGS_MASK \
(BTRFS_BALANCE_ARGS_PROFILES | \
diff --git a/fs/btrfs/xattr.c b/fs/btrfs/xattr.c
index 1fcd7b6e7564..7cbef1a14fe1 100644
--- a/fs/btrfs/xattr.c
+++ b/fs/btrfs/xattr.c
@@ -351,137 +351,89 @@ err:
return ret;
}
-/*
- * List of handlers for synthetic system.* attributes. All real ondisk
- * attributes are handled directly.
- */
-const struct xattr_handler *btrfs_xattr_handlers[] = {
-#ifdef CONFIG_BTRFS_FS_POSIX_ACL
- &posix_acl_access_xattr_handler,
- &posix_acl_default_xattr_handler,
-#endif
- NULL,
-};
-
-/*
- * Check if the attribute is in a supported namespace.
- *
- * This is applied after the check for the synthetic attributes in the system
- * namespace.
- */
-static int btrfs_is_valid_xattr(const char *name)
+static int btrfs_xattr_handler_get(const struct xattr_handler *handler,
+ struct dentry *dentry, const char *name,
+ void *buffer, size_t size)
{
- int len = strlen(name);
- int prefixlen = 0;
-
- if (!strncmp(name, XATTR_SECURITY_PREFIX,
- XATTR_SECURITY_PREFIX_LEN))
- prefixlen = XATTR_SECURITY_PREFIX_LEN;
- else if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
- prefixlen = XATTR_SYSTEM_PREFIX_LEN;
- else if (!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN))
- prefixlen = XATTR_TRUSTED_PREFIX_LEN;
- else if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
- prefixlen = XATTR_USER_PREFIX_LEN;
- else if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN))
- prefixlen = XATTR_BTRFS_PREFIX_LEN;
- else
- return -EOPNOTSUPP;
-
- /*
- * The name cannot consist of just prefix
- */
- if (len <= prefixlen)
- return -EINVAL;
+ struct inode *inode = d_inode(dentry);
- return 0;
+ name = xattr_full_name(handler, name);
+ return __btrfs_getxattr(inode, name, buffer, size);
}
-ssize_t btrfs_getxattr(struct dentry *dentry, const char *name,
- void *buffer, size_t size)
+static int btrfs_xattr_handler_set(const struct xattr_handler *handler,
+ struct dentry *dentry, const char *name,
+ const void *buffer, size_t size,
+ int flags)
{
- int ret;
+ struct inode *inode = d_inode(dentry);
- /*
- * If this is a request for a synthetic attribute in the system.*
- * namespace use the generic infrastructure to resolve a handler
- * for it via sb->s_xattr.
- */
- if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
- return generic_getxattr(dentry, name, buffer, size);
+ name = xattr_full_name(handler, name);
+ return __btrfs_setxattr(NULL, inode, name, buffer, size, flags);
+}
- ret = btrfs_is_valid_xattr(name);
- if (ret)
- return ret;
- return __btrfs_getxattr(d_inode(dentry), name, buffer, size);
+static int btrfs_xattr_handler_set_prop(const struct xattr_handler *handler,
+ struct dentry *dentry,
+ const char *name, const void *value,
+ size_t size, int flags)
+{
+ name = xattr_full_name(handler, name);
+ return btrfs_set_prop(d_inode(dentry), name, value, size, flags);
}
+static const struct xattr_handler btrfs_security_xattr_handler = {
+ .prefix = XATTR_SECURITY_PREFIX,
+ .get = btrfs_xattr_handler_get,
+ .set = btrfs_xattr_handler_set,
+};
+
+static const struct xattr_handler btrfs_trusted_xattr_handler = {
+ .prefix = XATTR_TRUSTED_PREFIX,
+ .get = btrfs_xattr_handler_get,
+ .set = btrfs_xattr_handler_set,
+};
+
+static const struct xattr_handler btrfs_user_xattr_handler = {
+ .prefix = XATTR_USER_PREFIX,
+ .get = btrfs_xattr_handler_get,
+ .set = btrfs_xattr_handler_set,
+};
+
+static const struct xattr_handler btrfs_btrfs_xattr_handler = {
+ .prefix = XATTR_BTRFS_PREFIX,
+ .get = btrfs_xattr_handler_get,
+ .set = btrfs_xattr_handler_set_prop,
+};
+
+const struct xattr_handler *btrfs_xattr_handlers[] = {
+ &btrfs_security_xattr_handler,
+#ifdef CONFIG_BTRFS_FS_POSIX_ACL
+ &posix_acl_access_xattr_handler,
+ &posix_acl_default_xattr_handler,
+#endif
+ &btrfs_trusted_xattr_handler,
+ &btrfs_user_xattr_handler,
+ &btrfs_btrfs_xattr_handler,
+ NULL,
+};
+
int btrfs_setxattr(struct dentry *dentry, const char *name, const void *value,
size_t size, int flags)
{
struct btrfs_root *root = BTRFS_I(d_inode(dentry))->root;
- int ret;
- /*
- * The permission on security.* and system.* is not checked
- * in permission().
- */
if (btrfs_root_readonly(root))
return -EROFS;
-
- /*
- * If this is a request for a synthetic attribute in the system.*
- * namespace use the generic infrastructure to resolve a handler
- * for it via sb->s_xattr.
- */
- if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
- return generic_setxattr(dentry, name, value, size, flags);
-
- ret = btrfs_is_valid_xattr(name);
- if (ret)
- return ret;
-
- if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN))
- return btrfs_set_prop(d_inode(dentry), name,
- value, size, flags);
-
- if (size == 0)
- value = ""; /* empty EA, do not remove */
-
- return __btrfs_setxattr(NULL, d_inode(dentry), name, value, size,
- flags);
+ return generic_setxattr(dentry, name, value, size, flags);
}
int btrfs_removexattr(struct dentry *dentry, const char *name)
{
struct btrfs_root *root = BTRFS_I(d_inode(dentry))->root;
- int ret;
- /*
- * The permission on security.* and system.* is not checked
- * in permission().
- */
if (btrfs_root_readonly(root))
return -EROFS;
-
- /*
- * If this is a request for a synthetic attribute in the system.*
- * namespace use the generic infrastructure to resolve a handler
- * for it via sb->s_xattr.
- */
- if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
- return generic_removexattr(dentry, name);
-
- ret = btrfs_is_valid_xattr(name);
- if (ret)
- return ret;
-
- if (!strncmp(name, XATTR_BTRFS_PREFIX, XATTR_BTRFS_PREFIX_LEN))
- return btrfs_set_prop(d_inode(dentry), name,
- NULL, 0, XATTR_REPLACE);
-
- return __btrfs_setxattr(NULL, d_inode(dentry), name, NULL, 0,
- XATTR_REPLACE);
+ return generic_removexattr(dentry, name);
}
static int btrfs_initxattrs(struct inode *inode,
diff --git a/fs/btrfs/xattr.h b/fs/btrfs/xattr.h
index 5049608d1388..96807b3d22f5 100644
--- a/fs/btrfs/xattr.h
+++ b/fs/btrfs/xattr.h
@@ -28,8 +28,6 @@ extern ssize_t __btrfs_getxattr(struct inode *inode, const char *name,
extern int __btrfs_setxattr(struct btrfs_trans_handle *trans,
struct inode *inode, const char *name,
const void *value, size_t size, int flags);
-extern ssize_t btrfs_getxattr(struct dentry *dentry, const char *name,
- void *buffer, size_t size);
extern int btrfs_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags);
extern int btrfs_removexattr(struct dentry *dentry, const char *name);
diff --git a/fs/buffer.c b/fs/buffer.c
index 4f4cd959da7c..e1632abb4ca9 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -134,13 +134,10 @@ __clear_page_buffers(struct page *page)
static void buffer_io_error(struct buffer_head *bh, char *msg)
{
- char b[BDEVNAME_SIZE];
-
if (!test_bit(BH_Quiet, &bh->b_state))
printk_ratelimited(KERN_ERR
- "Buffer I/O error on dev %s, logical block %llu%s\n",
- bdevname(bh->b_bdev, b),
- (unsigned long long)bh->b_blocknr, msg);
+ "Buffer I/O error on dev %pg, logical block %llu%s\n",
+ bh->b_bdev, (unsigned long long)bh->b_blocknr, msg);
}
/*
@@ -237,15 +234,13 @@ __find_get_block_slow(struct block_device *bdev, sector_t block)
* elsewhere, don't buffer_error if we had some unmapped buffers
*/
if (all_mapped) {
- char b[BDEVNAME_SIZE];
-
printk("__find_get_block_slow() failed. "
"block=%llu, b_blocknr=%llu\n",
(unsigned long long)block,
(unsigned long long)bh->b_blocknr);
printk("b_state=0x%08lx, b_size=%zu\n",
bh->b_state, bh->b_size);
- printk("device %s blocksize: %d\n", bdevname(bdev, b),
+ printk("device %pg blocksize: %d\n", bdev,
1 << bd_inode->i_blkbits);
}
out_unlock:
@@ -531,10 +526,8 @@ repeat:
static void do_thaw_one(struct super_block *sb, void *unused)
{
- char b[BDEVNAME_SIZE];
while (sb->s_bdev && !thaw_bdev(sb->s_bdev, sb))
- printk(KERN_WARNING "Emergency Thaw on %s\n",
- bdevname(sb->s_bdev, b));
+ printk(KERN_WARNING "Emergency Thaw on %pg\n", sb->s_bdev);
}
static void do_thaw_all(struct work_struct *work)
@@ -1074,12 +1067,10 @@ grow_buffers(struct block_device *bdev, sector_t block, int size, gfp_t gfp)
* pagecache index. (this comparison is done using sector_t types).
*/
if (unlikely(index != block >> sizebits)) {
- char b[BDEVNAME_SIZE];
-
printk(KERN_ERR "%s: requested out-of-range block %llu for "
- "device %s\n",
+ "device %pg\n",
__func__, (unsigned long long)block,
- bdevname(bdev, b));
+ bdev);
return -EIO;
}
diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c
index f601def05bdf..452e98dd7560 100644
--- a/fs/cachefiles/daemon.c
+++ b/fs/cachefiles/daemon.c
@@ -226,15 +226,9 @@ static ssize_t cachefiles_daemon_write(struct file *file,
return -EOPNOTSUPP;
/* drag the command string into the kernel so we can parse it */
- data = kmalloc(datalen + 1, GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- ret = -EFAULT;
- if (copy_from_user(data, _data, datalen) != 0)
- goto error;
-
- data[datalen] = '\0';
+ data = memdup_user_nul(_data, datalen);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
ret = -EINVAL;
if (memchr(data, '\0', datalen))
diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c
index 8f84646f10e9..f19708487e2f 100644
--- a/fs/ceph/acl.c
+++ b/fs/ceph/acl.c
@@ -49,10 +49,10 @@ struct posix_acl *ceph_get_acl(struct inode *inode, int type)
switch (type) {
case ACL_TYPE_ACCESS:
- name = POSIX_ACL_XATTR_ACCESS;
+ name = XATTR_NAME_POSIX_ACL_ACCESS;
break;
case ACL_TYPE_DEFAULT:
- name = POSIX_ACL_XATTR_DEFAULT;
+ name = XATTR_NAME_POSIX_ACL_DEFAULT;
break;
default:
BUG();
@@ -92,7 +92,7 @@ int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type)
switch (type) {
case ACL_TYPE_ACCESS:
- name = POSIX_ACL_XATTR_ACCESS;
+ name = XATTR_NAME_POSIX_ACL_ACCESS;
if (acl) {
ret = posix_acl_equiv_mode(acl, &new_mode);
if (ret < 0)
@@ -106,7 +106,7 @@ int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type)
ret = acl ? -EINVAL : 0;
goto out;
}
- name = POSIX_ACL_XATTR_DEFAULT;
+ name = XATTR_NAME_POSIX_ACL_DEFAULT;
break;
default:
ret = -EINVAL;
@@ -202,11 +202,11 @@ int ceph_pre_init_acls(struct inode *dir, umode_t *mode,
ceph_pagelist_encode_32(pagelist, acl && default_acl ? 2 : 1);
if (acl) {
- size_t len = strlen(POSIX_ACL_XATTR_ACCESS);
+ size_t len = strlen(XATTR_NAME_POSIX_ACL_ACCESS);
err = ceph_pagelist_reserve(pagelist, len + val_size1 + 8);
if (err)
goto out_err;
- ceph_pagelist_encode_string(pagelist, POSIX_ACL_XATTR_ACCESS,
+ ceph_pagelist_encode_string(pagelist, XATTR_NAME_POSIX_ACL_ACCESS,
len);
err = posix_acl_to_xattr(&init_user_ns, acl,
tmp_buf, val_size1);
@@ -216,12 +216,12 @@ int ceph_pre_init_acls(struct inode *dir, umode_t *mode,
ceph_pagelist_append(pagelist, tmp_buf, val_size1);
}
if (default_acl) {
- size_t len = strlen(POSIX_ACL_XATTR_DEFAULT);
+ size_t len = strlen(XATTR_NAME_POSIX_ACL_DEFAULT);
err = ceph_pagelist_reserve(pagelist, len + val_size2 + 8);
if (err)
goto out_err;
err = ceph_pagelist_encode_string(pagelist,
- POSIX_ACL_XATTR_DEFAULT, len);
+ XATTR_NAME_POSIX_ACL_DEFAULT, len);
err = posix_acl_to_xattr(&init_user_ns, default_acl,
tmp_buf, val_size2);
if (err < 0)
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 498dcfa2dcdb..da55eb8bcffa 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -1756,7 +1756,7 @@ retry:
*/
static const struct inode_operations ceph_symlink_iops = {
.readlink = generic_readlink,
- .follow_link = simple_follow_link,
+ .get_link = simple_get_link,
.setattr = ceph_setattr,
.getattr = ceph_getattr,
.setxattr = ceph_setxattr,
diff --git a/fs/ceph/super.c b/fs/ceph/super.c
index f446afada328..ca4d5e8457f1 100644
--- a/fs/ceph/super.c
+++ b/fs/ceph/super.c
@@ -639,8 +639,8 @@ static int __init init_caches(void)
ceph_inode_cachep = kmem_cache_create("ceph_inode_info",
sizeof(struct ceph_inode_info),
__alignof__(struct ceph_inode_info),
- (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD),
- ceph_inode_init_once);
+ SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|
+ SLAB_ACCOUNT, ceph_inode_init_once);
if (ceph_inode_cachep == NULL)
return -ENOMEM;
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index cbc0f4bca0c0..c4c1169814b2 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -900,8 +900,7 @@ const struct inode_operations cifs_file_inode_ops = {
const struct inode_operations cifs_symlink_inode_ops = {
.readlink = generic_readlink,
- .follow_link = cifs_follow_link,
- .put_link = kfree_put_link,
+ .get_link = cifs_get_link,
.permission = cifs_permission,
/* BB add the following two eventually */
/* revalidate: cifs_revalidate,
@@ -914,6 +913,59 @@ const struct inode_operations cifs_symlink_inode_ops = {
#endif
};
+static int cifs_clone_file_range(struct file *src_file, loff_t off,
+ struct file *dst_file, loff_t destoff, u64 len)
+{
+ struct inode *src_inode = file_inode(src_file);
+ struct inode *target_inode = file_inode(dst_file);
+ struct cifsFileInfo *smb_file_src = src_file->private_data;
+ struct cifsFileInfo *smb_file_target = dst_file->private_data;
+ struct cifs_tcon *target_tcon = tlink_tcon(smb_file_target->tlink);
+ unsigned int xid;
+ int rc;
+
+ cifs_dbg(FYI, "clone range\n");
+
+ xid = get_xid();
+
+ if (!src_file->private_data || !dst_file->private_data) {
+ rc = -EBADF;
+ cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n");
+ goto out;
+ }
+
+ /*
+ * Note: cifs case is easier than btrfs since server responsible for
+ * checks for proper open modes and file type and if it wants
+ * server could even support copy of range where source = target
+ */
+ lock_two_nondirectories(target_inode, src_inode);
+
+ if (len == 0)
+ len = src_inode->i_size - off;
+
+ cifs_dbg(FYI, "about to flush pages\n");
+ /* should we flush first and last page first */
+ truncate_inode_pages_range(&target_inode->i_data, destoff,
+ PAGE_CACHE_ALIGN(destoff + len)-1);
+
+ if (target_tcon->ses->server->ops->duplicate_extents)
+ rc = target_tcon->ses->server->ops->duplicate_extents(xid,
+ smb_file_src, smb_file_target, off, len, destoff);
+ else
+ rc = -EOPNOTSUPP;
+
+ /* force revalidate of size and timestamps of target file now
+ that target is updated on the server */
+ CIFS_I(target_inode)->time = 0;
+ /* although unlocking in the reverse order from locking is not
+ strictly necessary here it is a little cleaner to be consistent */
+ unlock_two_nondirectories(src_inode, target_inode);
+out:
+ free_xid(xid);
+ return rc;
+}
+
const struct file_operations cifs_file_ops = {
.read_iter = cifs_loose_read_iter,
.write_iter = cifs_file_write_iter,
@@ -926,6 +978,7 @@ const struct file_operations cifs_file_ops = {
.splice_read = generic_file_splice_read,
.llseek = cifs_llseek,
.unlocked_ioctl = cifs_ioctl,
+ .clone_file_range = cifs_clone_file_range,
.setlease = cifs_setlease,
.fallocate = cifs_fallocate,
};
@@ -942,6 +995,8 @@ const struct file_operations cifs_file_strict_ops = {
.splice_read = generic_file_splice_read,
.llseek = cifs_llseek,
.unlocked_ioctl = cifs_ioctl,
+ .clone_file_range = cifs_clone_file_range,
+ .clone_file_range = cifs_clone_file_range,
.setlease = cifs_setlease,
.fallocate = cifs_fallocate,
};
@@ -958,6 +1013,7 @@ const struct file_operations cifs_file_direct_ops = {
.mmap = cifs_file_mmap,
.splice_read = generic_file_splice_read,
.unlocked_ioctl = cifs_ioctl,
+ .clone_file_range = cifs_clone_file_range,
.llseek = cifs_llseek,
.setlease = cifs_setlease,
.fallocate = cifs_fallocate,
@@ -974,6 +1030,7 @@ const struct file_operations cifs_file_nobrl_ops = {
.splice_read = generic_file_splice_read,
.llseek = cifs_llseek,
.unlocked_ioctl = cifs_ioctl,
+ .clone_file_range = cifs_clone_file_range,
.setlease = cifs_setlease,
.fallocate = cifs_fallocate,
};
@@ -989,6 +1046,7 @@ const struct file_operations cifs_file_strict_nobrl_ops = {
.splice_read = generic_file_splice_read,
.llseek = cifs_llseek,
.unlocked_ioctl = cifs_ioctl,
+ .clone_file_range = cifs_clone_file_range,
.setlease = cifs_setlease,
.fallocate = cifs_fallocate,
};
@@ -1004,6 +1062,7 @@ const struct file_operations cifs_file_direct_nobrl_ops = {
.mmap = cifs_file_mmap,
.splice_read = generic_file_splice_read,
.unlocked_ioctl = cifs_ioctl,
+ .clone_file_range = cifs_clone_file_range,
.llseek = cifs_llseek,
.setlease = cifs_setlease,
.fallocate = cifs_fallocate,
@@ -1014,6 +1073,7 @@ const struct file_operations cifs_dir_ops = {
.release = cifs_closedir,
.read = generic_read_dir,
.unlocked_ioctl = cifs_ioctl,
+ .clone_file_range = cifs_clone_file_range,
.llseek = generic_file_llseek,
};
@@ -1032,7 +1092,7 @@ cifs_init_inodecache(void)
cifs_inode_cachep = kmem_cache_create("cifs_inode_cache",
sizeof(struct cifsInodeInfo),
0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
cifs_init_once);
if (cifs_inode_cachep == NULL)
return -ENOMEM;
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index c3cc1609025f..68c4547528c4 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -120,9 +120,8 @@ extern struct vfsmount *cifs_dfs_d_automount(struct path *path);
#endif
/* Functions related to symlinks */
-extern const char *cifs_follow_link(struct dentry *direntry, void **cookie);
-extern int cifs_readlink(struct dentry *direntry, char __user *buffer,
- int buflen);
+extern const char *cifs_get_link(struct dentry *, struct inode *,
+ struct delayed_call *);
extern int cifs_symlink(struct inode *inode, struct dentry *direntry,
const char *symname);
extern int cifs_removexattr(struct dentry *, const char *);
@@ -131,7 +130,6 @@ extern int cifs_setxattr(struct dentry *, const char *, const void *,
extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t);
extern ssize_t cifs_listxattr(struct dentry *, char *, size_t);
extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
-
#ifdef CONFIG_CIFS_NFSD_EXPORT
extern const struct export_operations cifs_export_ops;
#endif /* CONFIG_CIFS_NFSD_EXPORT */
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 6b66dd5d1540..a329f5ba35aa 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -1831,11 +1831,11 @@ cifs_invalidate_mapping(struct inode *inode)
* @word: long word containing the bit lock
*/
static int
-cifs_wait_bit_killable(struct wait_bit_key *key)
+cifs_wait_bit_killable(struct wait_bit_key *key, int mode)
{
- if (fatal_signal_pending(current))
- return -ERESTARTSYS;
freezable_schedule_unsafe();
+ if (signal_pending_state(mode, current))
+ return -ERESTARTSYS;
return 0;
}
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
index 35cf990f87d3..7a3b84e300f8 100644
--- a/fs/cifs/ioctl.c
+++ b/fs/cifs/ioctl.c
@@ -34,73 +34,36 @@
#include "cifs_ioctl.h"
#include <linux/btrfs.h>
-static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file,
- unsigned long srcfd, u64 off, u64 len, u64 destoff,
- bool dup_extents)
+static int cifs_file_clone_range(unsigned int xid, struct file *src_file,
+ struct file *dst_file)
{
- int rc;
- struct cifsFileInfo *smb_file_target = dst_file->private_data;
+ struct inode *src_inode = file_inode(src_file);
struct inode *target_inode = file_inode(dst_file);
- struct cifs_tcon *target_tcon;
- struct fd src_file;
struct cifsFileInfo *smb_file_src;
- struct inode *src_inode;
+ struct cifsFileInfo *smb_file_target;
struct cifs_tcon *src_tcon;
+ struct cifs_tcon *target_tcon;
+ int rc;
cifs_dbg(FYI, "ioctl clone range\n");
- /* the destination must be opened for writing */
- if (!(dst_file->f_mode & FMODE_WRITE)) {
- cifs_dbg(FYI, "file target not open for write\n");
- return -EINVAL;
- }
- /* check if target volume is readonly and take reference */
- rc = mnt_want_write_file(dst_file);
- if (rc) {
- cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc);
- return rc;
- }
-
- src_file = fdget(srcfd);
- if (!src_file.file) {
- rc = -EBADF;
- goto out_drop_write;
- }
-
- if (src_file.file->f_op->unlocked_ioctl != cifs_ioctl) {
- rc = -EBADF;
- cifs_dbg(VFS, "src file seems to be from a different filesystem type\n");
- goto out_fput;
- }
-
- if ((!src_file.file->private_data) || (!dst_file->private_data)) {
+ if (!src_file->private_data || !dst_file->private_data) {
rc = -EBADF;
cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n");
- goto out_fput;
+ goto out;
}
rc = -EXDEV;
smb_file_target = dst_file->private_data;
- smb_file_src = src_file.file->private_data;
+ smb_file_src = src_file->private_data;
src_tcon = tlink_tcon(smb_file_src->tlink);
target_tcon = tlink_tcon(smb_file_target->tlink);
- /* check source and target on same server (or volume if dup_extents) */
- if (dup_extents && (src_tcon != target_tcon)) {
- cifs_dbg(VFS, "source and target of copy not on same share\n");
- goto out_fput;
- }
-
- if (!dup_extents && (src_tcon->ses != target_tcon->ses)) {
+ if (src_tcon->ses != target_tcon->ses) {
cifs_dbg(VFS, "source and target of copy not on same server\n");
- goto out_fput;
+ goto out;
}
- src_inode = file_inode(src_file.file);
- rc = -EINVAL;
- if (S_ISDIR(src_inode->i_mode))
- goto out_fput;
-
/*
* Note: cifs case is easier than btrfs since server responsible for
* checks for proper open modes and file type and if it wants
@@ -108,34 +71,66 @@ static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file,
*/
lock_two_nondirectories(target_inode, src_inode);
- /* determine range to clone */
- rc = -EINVAL;
- if (off + len > src_inode->i_size || off + len < off)
- goto out_unlock;
- if (len == 0)
- len = src_inode->i_size - off;
-
cifs_dbg(FYI, "about to flush pages\n");
/* should we flush first and last page first */
- truncate_inode_pages_range(&target_inode->i_data, destoff,
- PAGE_CACHE_ALIGN(destoff + len)-1);
+ truncate_inode_pages(&target_inode->i_data, 0);
- if (dup_extents && target_tcon->ses->server->ops->duplicate_extents)
- rc = target_tcon->ses->server->ops->duplicate_extents(xid,
- smb_file_src, smb_file_target, off, len, destoff);
- else if (!dup_extents && target_tcon->ses->server->ops->clone_range)
+ if (target_tcon->ses->server->ops->clone_range)
rc = target_tcon->ses->server->ops->clone_range(xid,
- smb_file_src, smb_file_target, off, len, destoff);
+ smb_file_src, smb_file_target, 0, src_inode->i_size, 0);
else
rc = -EOPNOTSUPP;
/* force revalidate of size and timestamps of target file now
that target is updated on the server */
CIFS_I(target_inode)->time = 0;
-out_unlock:
/* although unlocking in the reverse order from locking is not
strictly necessary here it is a little cleaner to be consistent */
unlock_two_nondirectories(src_inode, target_inode);
+out:
+ return rc;
+}
+
+static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file,
+ unsigned long srcfd)
+{
+ int rc;
+ struct fd src_file;
+ struct inode *src_inode;
+
+ cifs_dbg(FYI, "ioctl clone range\n");
+ /* the destination must be opened for writing */
+ if (!(dst_file->f_mode & FMODE_WRITE)) {
+ cifs_dbg(FYI, "file target not open for write\n");
+ return -EINVAL;
+ }
+
+ /* check if target volume is readonly and take reference */
+ rc = mnt_want_write_file(dst_file);
+ if (rc) {
+ cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc);
+ return rc;
+ }
+
+ src_file = fdget(srcfd);
+ if (!src_file.file) {
+ rc = -EBADF;
+ goto out_drop_write;
+ }
+
+ if (src_file.file->f_op->unlocked_ioctl != cifs_ioctl) {
+ rc = -EBADF;
+ cifs_dbg(VFS, "src file seems to be from a different filesystem type\n");
+ goto out_fput;
+ }
+
+ src_inode = file_inode(src_file.file);
+ rc = -EINVAL;
+ if (S_ISDIR(src_inode->i_mode))
+ goto out_fput;
+
+ rc = cifs_file_clone_range(xid, src_file.file, dst_file);
+
out_fput:
fdput(src_file);
out_drop_write:
@@ -256,10 +251,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
}
break;
case CIFS_IOC_COPYCHUNK_FILE:
- rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0, false);
- break;
- case BTRFS_IOC_CLONE:
- rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0, true);
+ rc = cifs_ioctl_clone(xid, filep, arg);
break;
case CIFS_IOC_SET_INTEGRITY:
if (pSMBFile == NULL)
diff --git a/fs/cifs/link.c b/fs/cifs/link.c
index e3548f73bdea..062c2375549a 100644
--- a/fs/cifs/link.c
+++ b/fs/cifs/link.c
@@ -627,9 +627,9 @@ cifs_hl_exit:
}
const char *
-cifs_follow_link(struct dentry *direntry, void **cookie)
+cifs_get_link(struct dentry *direntry, struct inode *inode,
+ struct delayed_call *done)
{
- struct inode *inode = d_inode(direntry);
int rc = -ENOMEM;
unsigned int xid;
char *full_path = NULL;
@@ -639,6 +639,9 @@ cifs_follow_link(struct dentry *direntry, void **cookie)
struct cifs_tcon *tcon;
struct TCP_Server_Info *server;
+ if (!direntry)
+ return ERR_PTR(-ECHILD);
+
xid = get_xid();
tlink = cifs_sb_tlink(cifs_sb);
@@ -678,7 +681,8 @@ cifs_follow_link(struct dentry *direntry, void **cookie)
kfree(target_path);
return ERR_PTR(rc);
}
- return *cookie = target_path;
+ set_delayed_call(done, kfree_link, target_path);
+ return target_path;
}
int
diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c
index ff9e1f8b16a4..f5dc2f0df4ad 100644
--- a/fs/cifs/xattr.c
+++ b/fs/cifs/xattr.c
@@ -190,8 +190,8 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,
#endif /* CONFIG_CIFS_ACL */
} else {
int temp;
- temp = strncmp(ea_name, POSIX_ACL_XATTR_ACCESS,
- strlen(POSIX_ACL_XATTR_ACCESS));
+ temp = strncmp(ea_name, XATTR_NAME_POSIX_ACL_ACCESS,
+ strlen(XATTR_NAME_POSIX_ACL_ACCESS));
if (temp == 0) {
#ifdef CONFIG_CIFS_POSIX
if (sb->s_flags & MS_POSIXACL)
@@ -203,8 +203,8 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,
#else
cifs_dbg(FYI, "set POSIX ACL not supported\n");
#endif
- } else if (strncmp(ea_name, POSIX_ACL_XATTR_DEFAULT,
- strlen(POSIX_ACL_XATTR_DEFAULT)) == 0) {
+ } else if (strncmp(ea_name, XATTR_NAME_POSIX_ACL_DEFAULT,
+ strlen(XATTR_NAME_POSIX_ACL_DEFAULT)) == 0) {
#ifdef CONFIG_CIFS_POSIX
if (sb->s_flags & MS_POSIXACL)
rc = CIFSSMBSetPosixACL(xid, pTcon, full_path,
@@ -292,8 +292,8 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon,
full_path, ea_name, ea_value, buf_size,
cifs_sb->local_nls, cifs_remap(cifs_sb));
- } else if (strncmp(ea_name, POSIX_ACL_XATTR_ACCESS,
- strlen(POSIX_ACL_XATTR_ACCESS)) == 0) {
+ } else if (strncmp(ea_name, XATTR_NAME_POSIX_ACL_ACCESS,
+ strlen(XATTR_NAME_POSIX_ACL_ACCESS)) == 0) {
#ifdef CONFIG_CIFS_POSIX
if (sb->s_flags & MS_POSIXACL)
rc = CIFSSMBGetPosixACL(xid, pTcon, full_path,
@@ -303,8 +303,8 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
#else
cifs_dbg(FYI, "Query POSIX ACL not supported yet\n");
#endif /* CONFIG_CIFS_POSIX */
- } else if (strncmp(ea_name, POSIX_ACL_XATTR_DEFAULT,
- strlen(POSIX_ACL_XATTR_DEFAULT)) == 0) {
+ } else if (strncmp(ea_name, XATTR_NAME_POSIX_ACL_DEFAULT,
+ strlen(XATTR_NAME_POSIX_ACL_DEFAULT)) == 0) {
#ifdef CONFIG_CIFS_POSIX
if (sb->s_flags & MS_POSIXACL)
rc = CIFSSMBGetPosixACL(xid, pTcon, full_path,
diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c
index 7740b1c871c1..1bfb7ba4e85e 100644
--- a/fs/coda/cnode.c
+++ b/fs/coda/cnode.c
@@ -8,6 +8,7 @@
#include <linux/coda.h>
#include <linux/coda_psdev.h>
+#include <linux/pagemap.h>
#include "coda_linux.h"
static inline int coda_fideq(struct CodaFid *fid1, struct CodaFid *fid2)
@@ -17,8 +18,7 @@ static inline int coda_fideq(struct CodaFid *fid1, struct CodaFid *fid2)
static const struct inode_operations coda_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = page_follow_link_light,
- .put_link = page_put_link,
+ .get_link = page_get_link,
.setattr = coda_setattr,
};
@@ -35,6 +35,7 @@ static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr)
inode->i_fop = &coda_dir_operations;
} else if (S_ISLNK(inode->i_mode)) {
inode->i_op = &coda_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_data.a_ops = &coda_symlink_aops;
inode->i_mapping = &inode->i_data;
} else
diff --git a/fs/coda/inode.c b/fs/coda/inode.c
index cac1390b87a3..57e81cbba0fa 100644
--- a/fs/coda/inode.c
+++ b/fs/coda/inode.c
@@ -74,9 +74,9 @@ static void init_once(void *foo)
int __init coda_init_inodecache(void)
{
coda_inode_cachep = kmem_cache_create("coda_inode_cache",
- sizeof(struct coda_inode_info),
- 0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD,
- init_once);
+ sizeof(struct coda_inode_info), 0,
+ SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|
+ SLAB_ACCOUNT, init_once);
if (coda_inode_cachep == NULL)
return -ENOMEM;
return 0;
diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c
index ab94ef63caef..03736e20d720 100644
--- a/fs/coda/symlink.c
+++ b/fs/coda/symlink.c
@@ -26,7 +26,7 @@ static int coda_symlink_filler(struct file *file, struct page *page)
int error;
struct coda_inode_info *cii;
unsigned int len = PAGE_SIZE;
- char *p = kmap(page);
+ char *p = page_address(page);
cii = ITOC(inode);
@@ -34,13 +34,11 @@ static int coda_symlink_filler(struct file *file, struct page *page)
if (error)
goto fail;
SetPageUptodate(page);
- kunmap(page);
unlock_page(page);
return 0;
fail:
SetPageError(page);
- kunmap(page);
unlock_page(page);
return error;
}
diff --git a/fs/compat.c b/fs/compat.c
index 6fd272d455e4..a71936a3f4cb 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -792,7 +792,7 @@ COMPAT_SYSCALL_DEFINE5(mount, const char __user *, dev_name,
const void __user *, data)
{
char *kernel_type;
- unsigned long data_page;
+ void *options;
char *kernel_dev;
int retval;
@@ -806,26 +806,25 @@ COMPAT_SYSCALL_DEFINE5(mount, const char __user *, dev_name,
if (IS_ERR(kernel_dev))
goto out1;
- retval = copy_mount_options(data, &data_page);
- if (retval < 0)
+ options = copy_mount_options(data);
+ retval = PTR_ERR(options);
+ if (IS_ERR(options))
goto out2;
- retval = -EINVAL;
-
- if (kernel_type && data_page) {
+ if (kernel_type && options) {
if (!strcmp(kernel_type, NCPFS_NAME)) {
- do_ncp_super_data_conv((void *)data_page);
+ do_ncp_super_data_conv(options);
} else if (!strcmp(kernel_type, NFS4_NAME)) {
- if (do_nfs4_super_data_conv((void *) data_page))
+ retval = -EINVAL;
+ if (do_nfs4_super_data_conv(options))
goto out3;
}
}
- retval = do_mount(kernel_dev, dir_name, kernel_type,
- flags, (void*)data_page);
+ retval = do_mount(kernel_dev, dir_name, kernel_type, flags, options);
out3:
- free_page(data_page);
+ kfree(options);
out2:
kfree(kernel_dev);
out1:
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c
index dcf26537c935..a5b8eb69a8f4 100644
--- a/fs/compat_ioctl.c
+++ b/fs/compat_ioctl.c
@@ -58,6 +58,8 @@
#include <linux/atalk.h>
#include <linux/gfp.h>
+#include "internal.h"
+
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_sock.h>
#include <net/bluetooth/rfcomm.h>
@@ -115,19 +117,38 @@
#include <asm/fbio.h>
#endif
-static int w_long(unsigned int fd, unsigned int cmd,
- compat_ulong_t __user *argp)
+#define convert_in_user(srcptr, dstptr) \
+({ \
+ typeof(*srcptr) val; \
+ \
+ get_user(val, srcptr) || put_user(val, dstptr); \
+})
+
+static int do_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- mm_segment_t old_fs = get_fs();
int err;
- unsigned long val;
- set_fs (KERNEL_DS);
- err = sys_ioctl(fd, cmd, (unsigned long)&val);
- set_fs (old_fs);
- if (!err && put_user(val, argp))
+ err = security_file_ioctl(file, cmd, arg);
+ if (err)
+ return err;
+
+ return vfs_ioctl(file, cmd, arg);
+}
+
+static int w_long(struct file *file,
+ unsigned int cmd, compat_ulong_t __user *argp)
+{
+ int err;
+ unsigned long __user *valp = compat_alloc_user_space(sizeof(*valp));
+
+ if (valp == NULL)
return -EFAULT;
- return err;
+ err = do_ioctl(file, cmd, (unsigned long)valp);
+ if (err)
+ return err;
+ if (convert_in_user(valp, argp))
+ return -EFAULT;
+ return 0;
}
struct compat_video_event {
@@ -139,23 +160,23 @@ struct compat_video_event {
} u;
};
-static int do_video_get_event(unsigned int fd, unsigned int cmd,
- struct compat_video_event __user *up)
+static int do_video_get_event(struct file *file,
+ unsigned int cmd, struct compat_video_event __user *up)
{
- struct video_event kevent;
- mm_segment_t old_fs = get_fs();
+ struct video_event __user *kevent =
+ compat_alloc_user_space(sizeof(*kevent));
int err;
- set_fs(KERNEL_DS);
- err = sys_ioctl(fd, cmd, (unsigned long) &kevent);
- set_fs(old_fs);
+ if (kevent == NULL)
+ return -EFAULT;
+ err = do_ioctl(file, cmd, (unsigned long)kevent);
if (!err) {
- err = put_user(kevent.type, &up->type);
- err |= put_user(kevent.timestamp, &up->timestamp);
- err |= put_user(kevent.u.size.w, &up->u.size.w);
- err |= put_user(kevent.u.size.h, &up->u.size.h);
- err |= put_user(kevent.u.size.aspect_ratio,
+ err = convert_in_user(&kevent->type, &up->type);
+ err |= convert_in_user(&kevent->timestamp, &up->timestamp);
+ err |= convert_in_user(&kevent->u.size.w, &up->u.size.w);
+ err |= convert_in_user(&kevent->u.size.h, &up->u.size.h);
+ err |= convert_in_user(&kevent->u.size.aspect_ratio,
&up->u.size.aspect_ratio);
if (err)
err = -EFAULT;
@@ -169,8 +190,8 @@ struct compat_video_still_picture {
int32_t size;
};
-static int do_video_stillpicture(unsigned int fd, unsigned int cmd,
- struct compat_video_still_picture __user *up)
+static int do_video_stillpicture(struct file *file,
+ unsigned int cmd, struct compat_video_still_picture __user *up)
{
struct video_still_picture __user *up_native;
compat_uptr_t fp;
@@ -190,7 +211,7 @@ static int do_video_stillpicture(unsigned int fd, unsigned int cmd,
if (err)
return -EFAULT;
- err = sys_ioctl(fd, cmd, (unsigned long) up_native);
+ err = do_ioctl(file, cmd, (unsigned long) up_native);
return err;
}
@@ -200,8 +221,8 @@ struct compat_video_spu_palette {
compat_uptr_t palette;
};
-static int do_video_set_spu_palette(unsigned int fd, unsigned int cmd,
- struct compat_video_spu_palette __user *up)
+static int do_video_set_spu_palette(struct file *file,
+ unsigned int cmd, struct compat_video_spu_palette __user *up)
{
struct video_spu_palette __user *up_native;
compat_uptr_t palp;
@@ -218,7 +239,7 @@ static int do_video_set_spu_palette(unsigned int fd, unsigned int cmd,
if (err)
return -EFAULT;
- err = sys_ioctl(fd, cmd, (unsigned long) up_native);
+ err = do_ioctl(file, cmd, (unsigned long) up_native);
return err;
}
@@ -276,7 +297,7 @@ static int sg_build_iovec(sg_io_hdr_t __user *sgio, void __user *dxferp, u16 iov
return 0;
}
-static int sg_ioctl_trans(unsigned int fd, unsigned int cmd,
+static int sg_ioctl_trans(struct file *file, unsigned int cmd,
sg_io_hdr32_t __user *sgio32)
{
sg_io_hdr_t __user *sgio;
@@ -289,7 +310,7 @@ static int sg_ioctl_trans(unsigned int fd, unsigned int cmd,
if (get_user(interface_id, &sgio32->interface_id))
return -EFAULT;
if (interface_id != 'S')
- return sys_ioctl(fd, cmd, (unsigned long)sgio32);
+ return do_ioctl(file, cmd, (unsigned long)sgio32);
if (get_user(iovec_count, &sgio32->iovec_count))
return -EFAULT;
@@ -349,7 +370,7 @@ static int sg_ioctl_trans(unsigned int fd, unsigned int cmd,
if (put_user(compat_ptr(data), &sgio->usr_ptr))
return -EFAULT;
- err = sys_ioctl(fd, cmd, (unsigned long) sgio);
+ err = do_ioctl(file, cmd, (unsigned long) sgio);
if (err >= 0) {
void __user *datap;
@@ -380,13 +401,13 @@ struct compat_sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */
int unused;
};
-static int sg_grt_trans(unsigned int fd, unsigned int cmd, struct
- compat_sg_req_info __user *o)
+static int sg_grt_trans(struct file *file,
+ unsigned int cmd, struct compat_sg_req_info __user *o)
{
int err, i;
sg_req_info_t __user *r;
r = compat_alloc_user_space(sizeof(sg_req_info_t)*SG_MAX_QUEUE);
- err = sys_ioctl(fd,cmd,(unsigned long)r);
+ err = do_ioctl(file, cmd, (unsigned long)r);
if (err < 0)
return err;
for (i = 0; i < SG_MAX_QUEUE; i++) {
@@ -412,8 +433,8 @@ struct sock_fprog32 {
#define PPPIOCSPASS32 _IOW('t', 71, struct sock_fprog32)
#define PPPIOCSACTIVE32 _IOW('t', 70, struct sock_fprog32)
-static int ppp_sock_fprog_ioctl_trans(unsigned int fd, unsigned int cmd,
- struct sock_fprog32 __user *u_fprog32)
+static int ppp_sock_fprog_ioctl_trans(struct file *file,
+ unsigned int cmd, struct sock_fprog32 __user *u_fprog32)
{
struct sock_fprog __user *u_fprog64 = compat_alloc_user_space(sizeof(struct sock_fprog));
void __user *fptr64;
@@ -435,7 +456,7 @@ static int ppp_sock_fprog_ioctl_trans(unsigned int fd, unsigned int cmd,
else
cmd = PPPIOCSACTIVE;
- return sys_ioctl(fd, cmd, (unsigned long) u_fprog64);
+ return do_ioctl(file, cmd, (unsigned long) u_fprog64);
}
struct ppp_option_data32 {
@@ -451,7 +472,7 @@ struct ppp_idle32 {
};
#define PPPIOCGIDLE32 _IOR('t', 63, struct ppp_idle32)
-static int ppp_gidle(unsigned int fd, unsigned int cmd,
+static int ppp_gidle(struct file *file, unsigned int cmd,
struct ppp_idle32 __user *idle32)
{
struct ppp_idle __user *idle;
@@ -460,7 +481,7 @@ static int ppp_gidle(unsigned int fd, unsigned int cmd,
idle = compat_alloc_user_space(sizeof(*idle));
- err = sys_ioctl(fd, PPPIOCGIDLE, (unsigned long) idle);
+ err = do_ioctl(file, PPPIOCGIDLE, (unsigned long) idle);
if (!err) {
if (get_user(xmit, &idle->xmit_idle) ||
@@ -472,7 +493,7 @@ static int ppp_gidle(unsigned int fd, unsigned int cmd,
return err;
}
-static int ppp_scompress(unsigned int fd, unsigned int cmd,
+static int ppp_scompress(struct file *file, unsigned int cmd,
struct ppp_option_data32 __user *odata32)
{
struct ppp_option_data __user *odata;
@@ -492,7 +513,7 @@ static int ppp_scompress(unsigned int fd, unsigned int cmd,
sizeof(__u32) + sizeof(int)))
return -EFAULT;
- return sys_ioctl(fd, PPPIOCSCOMPRESS, (unsigned long) odata);
+ return do_ioctl(file, PPPIOCSCOMPRESS, (unsigned long) odata);
}
#ifdef CONFIG_BLOCK
@@ -512,12 +533,13 @@ struct mtpos32 {
};
#define MTIOCPOS32 _IOR('m', 3, struct mtpos32)
-static int mt_ioctl_trans(unsigned int fd, unsigned int cmd, void __user *argp)
+static int mt_ioctl_trans(struct file *file,
+ unsigned int cmd, void __user *argp)
{
- mm_segment_t old_fs = get_fs();
- struct mtget get;
+ /* NULL initialization to make gcc shut up */
+ struct mtget __user *get = NULL;
struct mtget32 __user *umget32;
- struct mtpos pos;
+ struct mtpos __user *pos = NULL;
struct mtpos32 __user *upos32;
unsigned long kcmd;
void *karg;
@@ -526,32 +548,34 @@ static int mt_ioctl_trans(unsigned int fd, unsigned int cmd, void __user *argp)
switch(cmd) {
case MTIOCPOS32:
kcmd = MTIOCPOS;
- karg = &pos;
+ pos = compat_alloc_user_space(sizeof(*pos));
+ karg = pos;
break;
default: /* MTIOCGET32 */
kcmd = MTIOCGET;
- karg = &get;
+ get = compat_alloc_user_space(sizeof(*get));
+ karg = get;
break;
}
- set_fs (KERNEL_DS);
- err = sys_ioctl (fd, kcmd, (unsigned long)karg);
- set_fs (old_fs);
+ if (karg == NULL)
+ return -EFAULT;
+ err = do_ioctl(file, kcmd, (unsigned long)karg);
if (err)
return err;
switch (cmd) {
case MTIOCPOS32:
upos32 = argp;
- err = __put_user(pos.mt_blkno, &upos32->mt_blkno);
+ err = convert_in_user(&pos->mt_blkno, &upos32->mt_blkno);
break;
case MTIOCGET32:
umget32 = argp;
- err = __put_user(get.mt_type, &umget32->mt_type);
- err |= __put_user(get.mt_resid, &umget32->mt_resid);
- err |= __put_user(get.mt_dsreg, &umget32->mt_dsreg);
- err |= __put_user(get.mt_gstat, &umget32->mt_gstat);
- err |= __put_user(get.mt_erreg, &umget32->mt_erreg);
- err |= __put_user(get.mt_fileno, &umget32->mt_fileno);
- err |= __put_user(get.mt_blkno, &umget32->mt_blkno);
+ err = convert_in_user(&get->mt_type, &umget32->mt_type);
+ err |= convert_in_user(&get->mt_resid, &umget32->mt_resid);
+ err |= convert_in_user(&get->mt_dsreg, &umget32->mt_dsreg);
+ err |= convert_in_user(&get->mt_gstat, &umget32->mt_gstat);
+ err |= convert_in_user(&get->mt_erreg, &umget32->mt_erreg);
+ err |= convert_in_user(&get->mt_fileno, &umget32->mt_fileno);
+ err |= convert_in_user(&get->mt_blkno, &umget32->mt_blkno);
break;
}
return err ? -EFAULT: 0;
@@ -605,42 +629,41 @@ struct serial_struct32 {
compat_int_t reserved[1];
};
-static int serial_struct_ioctl(unsigned fd, unsigned cmd,
- struct serial_struct32 __user *ss32)
+static int serial_struct_ioctl(struct file *file,
+ unsigned cmd, struct serial_struct32 __user *ss32)
{
typedef struct serial_struct32 SS32;
int err;
- struct serial_struct ss;
- mm_segment_t oldseg = get_fs();
+ struct serial_struct __user *ss = compat_alloc_user_space(sizeof(*ss));
__u32 udata;
unsigned int base;
+ unsigned char *iomem_base;
+ if (ss == NULL)
+ return -EFAULT;
if (cmd == TIOCSSERIAL) {
- if (!access_ok(VERIFY_READ, ss32, sizeof(SS32)))
- return -EFAULT;
- if (__copy_from_user(&ss, ss32, offsetof(SS32, iomem_base)))
- return -EFAULT;
- if (__get_user(udata, &ss32->iomem_base))
+ if (copy_in_user(ss, ss32, offsetof(SS32, iomem_base)) ||
+ get_user(udata, &ss32->iomem_base))
return -EFAULT;
- ss.iomem_base = compat_ptr(udata);
- if (__get_user(ss.iomem_reg_shift, &ss32->iomem_reg_shift) ||
- __get_user(ss.port_high, &ss32->port_high))
+ iomem_base = compat_ptr(udata);
+ if (put_user(iomem_base, &ss->iomem_base) ||
+ convert_in_user(&ss32->iomem_reg_shift,
+ &ss->iomem_reg_shift) ||
+ convert_in_user(&ss32->port_high, &ss->port_high) ||
+ put_user(0UL, &ss->iomap_base))
return -EFAULT;
- ss.iomap_base = 0UL;
}
- set_fs(KERNEL_DS);
- err = sys_ioctl(fd,cmd,(unsigned long)(&ss));
- set_fs(oldseg);
+ err = do_ioctl(file, cmd, (unsigned long)ss);
if (cmd == TIOCGSERIAL && err >= 0) {
- if (!access_ok(VERIFY_WRITE, ss32, sizeof(SS32)))
- return -EFAULT;
- if (__copy_to_user(ss32,&ss,offsetof(SS32,iomem_base)))
+ if (copy_in_user(ss32, ss, offsetof(SS32, iomem_base)) ||
+ get_user(iomem_base, &ss->iomem_base))
return -EFAULT;
- base = (unsigned long)ss.iomem_base >> 32 ?
- 0xffffffff : (unsigned)(unsigned long)ss.iomem_base;
- if (__put_user(base, &ss32->iomem_base) ||
- __put_user(ss.iomem_reg_shift, &ss32->iomem_reg_shift) ||
- __put_user(ss.port_high, &ss32->port_high))
+ base = (unsigned long)iomem_base >> 32 ?
+ 0xffffffff : (unsigned)(unsigned long)iomem_base;
+ if (put_user(base, &ss32->iomem_base) ||
+ convert_in_user(&ss->iomem_reg_shift,
+ &ss32->iomem_reg_shift) ||
+ convert_in_user(&ss->port_high, &ss32->port_high))
return -EFAULT;
}
return err;
@@ -674,8 +697,8 @@ struct i2c_rdwr_aligned {
struct i2c_msg msgs[0];
};
-static int do_i2c_rdwr_ioctl(unsigned int fd, unsigned int cmd,
- struct i2c_rdwr_ioctl_data32 __user *udata)
+static int do_i2c_rdwr_ioctl(struct file *file,
+ unsigned int cmd, struct i2c_rdwr_ioctl_data32 __user *udata)
{
struct i2c_rdwr_aligned __user *tdata;
struct i2c_msg __user *tmsgs;
@@ -708,11 +731,11 @@ static int do_i2c_rdwr_ioctl(unsigned int fd, unsigned int cmd,
put_user(compat_ptr(datap), &tmsgs[i].buf))
return -EFAULT;
}
- return sys_ioctl(fd, cmd, (unsigned long)tdata);
+ return do_ioctl(file, cmd, (unsigned long)tdata);
}
-static int do_i2c_smbus_ioctl(unsigned int fd, unsigned int cmd,
- struct i2c_smbus_ioctl_data32 __user *udata)
+static int do_i2c_smbus_ioctl(struct file *file,
+ unsigned int cmd, struct i2c_smbus_ioctl_data32 __user *udata)
{
struct i2c_smbus_ioctl_data __user *tdata;
compat_caddr_t datap;
@@ -734,7 +757,7 @@ static int do_i2c_smbus_ioctl(unsigned int fd, unsigned int cmd,
__put_user(compat_ptr(datap), &tdata->data))
return -EFAULT;
- return sys_ioctl(fd, cmd, (unsigned long)tdata);
+ return do_ioctl(file, cmd, (unsigned long)tdata);
}
#define RTC_IRQP_READ32 _IOR('p', 0x0b, compat_ulong_t)
@@ -742,29 +765,27 @@ static int do_i2c_smbus_ioctl(unsigned int fd, unsigned int cmd,
#define RTC_EPOCH_READ32 _IOR('p', 0x0d, compat_ulong_t)
#define RTC_EPOCH_SET32 _IOW('p', 0x0e, compat_ulong_t)
-static int rtc_ioctl(unsigned fd, unsigned cmd, void __user *argp)
+static int rtc_ioctl(struct file *file,
+ unsigned cmd, void __user *argp)
{
- mm_segment_t oldfs = get_fs();
- compat_ulong_t val32;
- unsigned long kval;
+ unsigned long __user *valp = compat_alloc_user_space(sizeof(*valp));
int ret;
+ if (valp == NULL)
+ return -EFAULT;
switch (cmd) {
case RTC_IRQP_READ32:
case RTC_EPOCH_READ32:
- set_fs(KERNEL_DS);
- ret = sys_ioctl(fd, (cmd == RTC_IRQP_READ32) ?
+ ret = do_ioctl(file, (cmd == RTC_IRQP_READ32) ?
RTC_IRQP_READ : RTC_EPOCH_READ,
- (unsigned long)&kval);
- set_fs(oldfs);
+ (unsigned long)valp);
if (ret)
return ret;
- val32 = kval;
- return put_user(val32, (unsigned int __user *)argp);
+ return convert_in_user(valp, (unsigned int __user *)argp);
case RTC_IRQP_SET32:
- return sys_ioctl(fd, RTC_IRQP_SET, (unsigned long)argp);
+ return do_ioctl(file, RTC_IRQP_SET, (unsigned long)argp);
case RTC_EPOCH_SET32:
- return sys_ioctl(fd, RTC_EPOCH_SET, (unsigned long)argp);
+ return do_ioctl(file, RTC_EPOCH_SET, (unsigned long)argp);
}
return -ENOIOCTLCMD;
@@ -1284,12 +1305,6 @@ COMPATIBLE_IOCTL(PCIIOC_CONTROLLER)
COMPATIBLE_IOCTL(PCIIOC_MMAP_IS_IO)
COMPATIBLE_IOCTL(PCIIOC_MMAP_IS_MEM)
COMPATIBLE_IOCTL(PCIIOC_WRITE_COMBINE)
-/* NBD */
-COMPATIBLE_IOCTL(NBD_DO_IT)
-COMPATIBLE_IOCTL(NBD_CLEAR_SOCK)
-COMPATIBLE_IOCTL(NBD_CLEAR_QUE)
-COMPATIBLE_IOCTL(NBD_PRINT_DEBUG)
-COMPATIBLE_IOCTL(NBD_DISCONNECT)
/* i2c */
COMPATIBLE_IOCTL(I2C_SLAVE)
COMPATIBLE_IOCTL(I2C_SLAVE_FORCE)
@@ -1436,53 +1451,53 @@ IGNORE_IOCTL(FBIOGCURSOR32)
* a compat_ioctl operation in the place that handleѕ the
* ioctl for the native case.
*/
-static long do_ioctl_trans(int fd, unsigned int cmd,
+static long do_ioctl_trans(unsigned int cmd,
unsigned long arg, struct file *file)
{
void __user *argp = compat_ptr(arg);
switch (cmd) {
case PPPIOCGIDLE32:
- return ppp_gidle(fd, cmd, argp);
+ return ppp_gidle(file, cmd, argp);
case PPPIOCSCOMPRESS32:
- return ppp_scompress(fd, cmd, argp);
+ return ppp_scompress(file, cmd, argp);
case PPPIOCSPASS32:
case PPPIOCSACTIVE32:
- return ppp_sock_fprog_ioctl_trans(fd, cmd, argp);
+ return ppp_sock_fprog_ioctl_trans(file, cmd, argp);
#ifdef CONFIG_BLOCK
case SG_IO:
- return sg_ioctl_trans(fd, cmd, argp);
+ return sg_ioctl_trans(file, cmd, argp);
case SG_GET_REQUEST_TABLE:
- return sg_grt_trans(fd, cmd, argp);
+ return sg_grt_trans(file, cmd, argp);
case MTIOCGET32:
case MTIOCPOS32:
- return mt_ioctl_trans(fd, cmd, argp);
+ return mt_ioctl_trans(file, cmd, argp);
#endif
/* Serial */
case TIOCGSERIAL:
case TIOCSSERIAL:
- return serial_struct_ioctl(fd, cmd, argp);
+ return serial_struct_ioctl(file, cmd, argp);
/* i2c */
case I2C_FUNCS:
- return w_long(fd, cmd, argp);
+ return w_long(file, cmd, argp);
case I2C_RDWR:
- return do_i2c_rdwr_ioctl(fd, cmd, argp);
+ return do_i2c_rdwr_ioctl(file, cmd, argp);
case I2C_SMBUS:
- return do_i2c_smbus_ioctl(fd, cmd, argp);
+ return do_i2c_smbus_ioctl(file, cmd, argp);
/* Not implemented in the native kernel */
case RTC_IRQP_READ32:
case RTC_IRQP_SET32:
case RTC_EPOCH_READ32:
case RTC_EPOCH_SET32:
- return rtc_ioctl(fd, cmd, argp);
+ return rtc_ioctl(file, cmd, argp);
/* dvb */
case VIDEO_GET_EVENT:
- return do_video_get_event(fd, cmd, argp);
+ return do_video_get_event(file, cmd, argp);
case VIDEO_STILLPICTURE:
- return do_video_stillpicture(fd, cmd, argp);
+ return do_video_stillpicture(file, cmd, argp);
case VIDEO_SET_SPU_PALETTE:
- return do_video_set_spu_palette(fd, cmd, argp);
+ return do_video_set_spu_palette(file, cmd, argp);
}
/*
@@ -1508,12 +1523,7 @@ static long do_ioctl_trans(int fd, unsigned int cmd,
case KDSKBMETA:
case KDSKBLED:
case KDSETLED:
- /* NBD */
- case NBD_SET_SOCK:
- case NBD_SET_BLKSIZE:
- case NBD_SET_SIZE:
- case NBD_SET_SIZE_BLOCKS:
- return do_vfs_ioctl(file, fd, cmd, arg);
+ return vfs_ioctl(file, cmd, arg);
}
return -ENOIOCTLCMD;
@@ -1580,6 +1590,11 @@ COMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd,
goto out_fput;
#endif
+ case FICLONE:
+ case FICLONERANGE:
+ case FIDEDUPERANGE:
+ goto do_ioctl;
+
case FIBMAP:
case FIGETBSZ:
case FIONREAD:
@@ -1602,7 +1617,7 @@ COMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd,
if (compat_ioctl_check_table(XFORM(cmd)))
goto found_handler;
- error = do_ioctl_trans(fd, cmd, arg, f.file);
+ error = do_ioctl_trans(cmd, arg, f.file);
if (error == -ENOIOCTLCMD)
error = -ENOTTY;
diff --git a/fs/configfs/configfs_internal.h b/fs/configfs/configfs_internal.h
index b65d1ef532d5..ccc31fa6f1a7 100644
--- a/fs/configfs/configfs_internal.h
+++ b/fs/configfs/configfs_internal.h
@@ -53,13 +53,14 @@ struct configfs_dirent {
#define CONFIGFS_ROOT 0x0001
#define CONFIGFS_DIR 0x0002
#define CONFIGFS_ITEM_ATTR 0x0004
+#define CONFIGFS_ITEM_BIN_ATTR 0x0008
#define CONFIGFS_ITEM_LINK 0x0020
#define CONFIGFS_USET_DIR 0x0040
#define CONFIGFS_USET_DEFAULT 0x0080
#define CONFIGFS_USET_DROPPING 0x0100
#define CONFIGFS_USET_IN_MKDIR 0x0200
#define CONFIGFS_USET_CREATING 0x0400
-#define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR)
+#define CONFIGFS_NOT_PINNED (CONFIGFS_ITEM_ATTR | CONFIGFS_ITEM_BIN_ATTR)
extern struct mutex configfs_symlink_mutex;
extern spinlock_t configfs_dirent_lock;
@@ -72,6 +73,8 @@ extern struct inode * configfs_new_inode(umode_t mode, struct configfs_dirent *,
extern int configfs_create(struct dentry *, umode_t mode, void (*init)(struct inode *));
extern int configfs_create_file(struct config_item *, const struct configfs_attribute *);
+extern int configfs_create_bin_file(struct config_item *,
+ const struct configfs_bin_attribute *);
extern int configfs_make_dirent(struct configfs_dirent *,
struct dentry *, void *, umode_t, int);
extern int configfs_dirent_is_ready(struct configfs_dirent *);
@@ -88,7 +91,7 @@ extern void configfs_release_fs(void);
extern struct rw_semaphore configfs_rename_sem;
extern const struct file_operations configfs_dir_operations;
extern const struct file_operations configfs_file_operations;
-extern const struct file_operations bin_fops;
+extern const struct file_operations configfs_bin_file_operations;
extern const struct inode_operations configfs_dir_inode_operations;
extern const struct inode_operations configfs_root_inode_operations;
extern const struct inode_operations configfs_symlink_inode_operations;
@@ -119,6 +122,13 @@ static inline struct configfs_attribute * to_attr(struct dentry * dentry)
return ((struct configfs_attribute *) sd->s_element);
}
+static inline struct configfs_bin_attribute *to_bin_attr(struct dentry *dentry)
+{
+ struct configfs_attribute *attr = to_attr(dentry);
+
+ return container_of(attr, struct configfs_bin_attribute, cb_attr);
+}
+
static inline struct config_item *configfs_get_config_item(struct dentry *dentry)
{
struct config_item * item = NULL;
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c
index a7a1b218f308..7ae97e83f121 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -255,6 +255,12 @@ static void configfs_init_file(struct inode * inode)
inode->i_fop = &configfs_file_operations;
}
+static void configfs_init_bin_file(struct inode *inode)
+{
+ inode->i_size = 0;
+ inode->i_fop = &configfs_bin_file_operations;
+}
+
static void init_symlink(struct inode * inode)
{
inode->i_op = &configfs_symlink_inode_operations;
@@ -423,7 +429,9 @@ static int configfs_attach_attr(struct configfs_dirent * sd, struct dentry * den
spin_unlock(&configfs_dirent_lock);
error = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG,
- configfs_init_file);
+ (sd->s_type & CONFIGFS_ITEM_BIN_ATTR) ?
+ configfs_init_bin_file :
+ configfs_init_file);
if (error) {
configfs_put(sd);
return error;
@@ -583,6 +591,7 @@ static int populate_attrs(struct config_item *item)
{
struct config_item_type *t = item->ci_type;
struct configfs_attribute *attr;
+ struct configfs_bin_attribute *bin_attr;
int error = 0;
int i;
@@ -594,6 +603,13 @@ static int populate_attrs(struct config_item *item)
break;
}
}
+ if (t->ct_bin_attrs) {
+ for (i = 0; (bin_attr = t->ct_bin_attrs[i]) != NULL; i++) {
+ error = configfs_create_bin_file(item, bin_attr);
+ if (error)
+ break;
+ }
+ }
if (error)
detach_attrs(item);
diff --git a/fs/configfs/file.c b/fs/configfs/file.c
index d39099ea7df7..3687187c8ea5 100644
--- a/fs/configfs/file.c
+++ b/fs/configfs/file.c
@@ -28,6 +28,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/mutex.h>
+#include <linux/vmalloc.h>
#include <asm/uaccess.h>
#include <linux/configfs.h>
@@ -48,6 +49,10 @@ struct configfs_buffer {
struct configfs_item_operations * ops;
struct mutex mutex;
int needs_read_fill;
+ bool read_in_progress;
+ bool write_in_progress;
+ char *bin_buffer;
+ int bin_buffer_size;
};
@@ -123,6 +128,87 @@ out:
return retval;
}
+/**
+ * configfs_read_bin_file - read a binary attribute.
+ * @file: file pointer.
+ * @buf: buffer to fill.
+ * @count: number of bytes to read.
+ * @ppos: starting offset in file.
+ *
+ * Userspace wants to read a binary attribute file. The attribute
+ * descriptor is in the file's ->d_fsdata. The target item is in the
+ * directory's ->d_fsdata.
+ *
+ * We check whether we need to refill the buffer. If so we will
+ * call the attributes' attr->read() twice. The first time we
+ * will pass a NULL as a buffer pointer, which the attributes' method
+ * will use to return the size of the buffer required. If no error
+ * occurs we will allocate the buffer using vmalloc and call
+ * attr->read() again passing that buffer as an argument.
+ * Then we just copy to user-space using simple_read_from_buffer.
+ */
+
+static ssize_t
+configfs_read_bin_file(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct configfs_buffer *buffer = file->private_data;
+ struct dentry *dentry = file->f_path.dentry;
+ struct config_item *item = to_item(dentry->d_parent);
+ struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry);
+ ssize_t retval = 0;
+ ssize_t len = min_t(size_t, count, PAGE_SIZE);
+
+ mutex_lock(&buffer->mutex);
+
+ /* we don't support switching read/write modes */
+ if (buffer->write_in_progress) {
+ retval = -ETXTBSY;
+ goto out;
+ }
+ buffer->read_in_progress = 1;
+
+ if (buffer->needs_read_fill) {
+ /* perform first read with buf == NULL to get extent */
+ len = bin_attr->read(item, NULL, 0);
+ if (len <= 0) {
+ retval = len;
+ goto out;
+ }
+
+ /* do not exceed the maximum value */
+ if (bin_attr->cb_max_size && len > bin_attr->cb_max_size) {
+ retval = -EFBIG;
+ goto out;
+ }
+
+ buffer->bin_buffer = vmalloc(len);
+ if (buffer->bin_buffer == NULL) {
+ retval = -ENOMEM;
+ goto out;
+ }
+ buffer->bin_buffer_size = len;
+
+ /* perform second read to fill buffer */
+ len = bin_attr->read(item, buffer->bin_buffer, len);
+ if (len < 0) {
+ retval = len;
+ vfree(buffer->bin_buffer);
+ buffer->bin_buffer_size = 0;
+ buffer->bin_buffer = NULL;
+ goto out;
+ }
+
+ buffer->needs_read_fill = 0;
+ }
+
+ retval = simple_read_from_buffer(buf, count, ppos, buffer->bin_buffer,
+ buffer->bin_buffer_size);
+out:
+ mutex_unlock(&buffer->mutex);
+ return retval;
+}
+
/**
* fill_write_buffer - copy buffer from userspace.
@@ -209,10 +295,80 @@ configfs_write_file(struct file *file, const char __user *buf, size_t count, lof
return len;
}
-static int check_perm(struct inode * inode, struct file * file)
+/**
+ * configfs_write_bin_file - write a binary attribute.
+ * @file: file pointer
+ * @buf: data to write
+ * @count: number of bytes
+ * @ppos: starting offset
+ *
+ * Writing to a binary attribute file is similar to a normal read.
+ * We buffer the consecutive writes (binary attribute files do not
+ * support lseek) in a continuously growing buffer, but we don't
+ * commit until the close of the file.
+ */
+
+static ssize_t
+configfs_write_bin_file(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct configfs_buffer *buffer = file->private_data;
+ struct dentry *dentry = file->f_path.dentry;
+ struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry);
+ void *tbuf = NULL;
+ ssize_t len;
+
+ mutex_lock(&buffer->mutex);
+
+ /* we don't support switching read/write modes */
+ if (buffer->read_in_progress) {
+ len = -ETXTBSY;
+ goto out;
+ }
+ buffer->write_in_progress = 1;
+
+ /* buffer grows? */
+ if (*ppos + count > buffer->bin_buffer_size) {
+
+ if (bin_attr->cb_max_size &&
+ *ppos + count > bin_attr->cb_max_size) {
+ len = -EFBIG;
+ }
+
+ tbuf = vmalloc(*ppos + count);
+ if (tbuf == NULL) {
+ len = -ENOMEM;
+ goto out;
+ }
+
+ /* copy old contents */
+ if (buffer->bin_buffer) {
+ memcpy(tbuf, buffer->bin_buffer,
+ buffer->bin_buffer_size);
+ vfree(buffer->bin_buffer);
+ }
+
+ /* clear the new area */
+ memset(tbuf + buffer->bin_buffer_size, 0,
+ *ppos + count - buffer->bin_buffer_size);
+ buffer->bin_buffer = tbuf;
+ buffer->bin_buffer_size = *ppos + count;
+ }
+
+ len = simple_write_to_buffer(buffer->bin_buffer,
+ buffer->bin_buffer_size, ppos, buf, count);
+ if (len > 0)
+ *ppos += len;
+out:
+ mutex_unlock(&buffer->mutex);
+ return len;
+}
+
+static int check_perm(struct inode * inode, struct file * file, int type)
{
struct config_item *item = configfs_get_config_item(file->f_path.dentry->d_parent);
struct configfs_attribute * attr = to_attr(file->f_path.dentry);
+ struct configfs_bin_attribute *bin_attr = NULL;
struct configfs_buffer * buffer;
struct configfs_item_operations * ops = NULL;
int error = 0;
@@ -220,6 +376,9 @@ static int check_perm(struct inode * inode, struct file * file)
if (!item || !attr)
goto Einval;
+ if (type & CONFIGFS_ITEM_BIN_ATTR)
+ bin_attr = to_bin_attr(file->f_path.dentry);
+
/* Grab the module reference for this attribute if we have one */
if (!try_module_get(attr->ca_owner)) {
error = -ENODEV;
@@ -236,9 +395,14 @@ static int check_perm(struct inode * inode, struct file * file)
* and we must have a store method.
*/
if (file->f_mode & FMODE_WRITE) {
- if (!(inode->i_mode & S_IWUGO) || !attr->store)
+ if (!(inode->i_mode & S_IWUGO))
+ goto Eaccess;
+
+ if ((type & CONFIGFS_ITEM_ATTR) && !attr->store)
goto Eaccess;
+ if ((type & CONFIGFS_ITEM_BIN_ATTR) && !bin_attr->write)
+ goto Eaccess;
}
/* File needs read support.
@@ -246,7 +410,13 @@ static int check_perm(struct inode * inode, struct file * file)
* must be a show method for it.
*/
if (file->f_mode & FMODE_READ) {
- if (!(inode->i_mode & S_IRUGO) || !attr->show)
+ if (!(inode->i_mode & S_IRUGO))
+ goto Eaccess;
+
+ if ((type & CONFIGFS_ITEM_ATTR) && !attr->show)
+ goto Eaccess;
+
+ if ((type & CONFIGFS_ITEM_BIN_ATTR) && !bin_attr->read)
goto Eaccess;
}
@@ -260,6 +430,8 @@ static int check_perm(struct inode * inode, struct file * file)
}
mutex_init(&buffer->mutex);
buffer->needs_read_fill = 1;
+ buffer->read_in_progress = 0;
+ buffer->write_in_progress = 0;
buffer->ops = ops;
file->private_data = buffer;
goto Done;
@@ -277,12 +449,7 @@ static int check_perm(struct inode * inode, struct file * file)
return error;
}
-static int configfs_open_file(struct inode * inode, struct file * filp)
-{
- return check_perm(inode,filp);
-}
-
-static int configfs_release(struct inode * inode, struct file * filp)
+static int configfs_release(struct inode *inode, struct file *filp)
{
struct config_item * item = to_item(filp->f_path.dentry->d_parent);
struct configfs_attribute * attr = to_attr(filp->f_path.dentry);
@@ -303,6 +470,47 @@ static int configfs_release(struct inode * inode, struct file * filp)
return 0;
}
+static int configfs_open_file(struct inode *inode, struct file *filp)
+{
+ return check_perm(inode, filp, CONFIGFS_ITEM_ATTR);
+}
+
+static int configfs_open_bin_file(struct inode *inode, struct file *filp)
+{
+ return check_perm(inode, filp, CONFIGFS_ITEM_BIN_ATTR);
+}
+
+static int configfs_release_bin_file(struct inode *inode, struct file *filp)
+{
+ struct configfs_buffer *buffer = filp->private_data;
+ struct dentry *dentry = filp->f_path.dentry;
+ struct config_item *item = to_item(dentry->d_parent);
+ struct configfs_bin_attribute *bin_attr = to_bin_attr(dentry);
+ ssize_t len = 0;
+ int ret;
+
+ buffer->read_in_progress = 0;
+
+ if (buffer->write_in_progress) {
+ buffer->write_in_progress = 0;
+
+ len = bin_attr->write(item, buffer->bin_buffer,
+ buffer->bin_buffer_size);
+
+ /* vfree on NULL is safe */
+ vfree(buffer->bin_buffer);
+ buffer->bin_buffer = NULL;
+ buffer->bin_buffer_size = 0;
+ buffer->needs_read_fill = 1;
+ }
+
+ ret = configfs_release(inode, filp);
+ if (len < 0)
+ return len;
+ return ret;
+}
+
+
const struct file_operations configfs_file_operations = {
.read = configfs_read_file,
.write = configfs_write_file,
@@ -311,6 +519,14 @@ const struct file_operations configfs_file_operations = {
.release = configfs_release,
};
+const struct file_operations configfs_bin_file_operations = {
+ .read = configfs_read_bin_file,
+ .write = configfs_write_bin_file,
+ .llseek = NULL, /* bin file is not seekable */
+ .open = configfs_open_bin_file,
+ .release = configfs_release_bin_file,
+};
+
/**
* configfs_create_file - create an attribute file for an item.
* @item: item we're creating for.
@@ -332,3 +548,24 @@ int configfs_create_file(struct config_item * item, const struct configfs_attrib
return error;
}
+/**
+ * configfs_create_bin_file - create a binary attribute file for an item.
+ * @item: item we're creating for.
+ * @attr: atrribute descriptor.
+ */
+
+int configfs_create_bin_file(struct config_item *item,
+ const struct configfs_bin_attribute *bin_attr)
+{
+ struct dentry *dir = item->ci_dentry;
+ struct configfs_dirent *parent_sd = dir->d_fsdata;
+ umode_t mode = (bin_attr->cb_attr.ca_mode & S_IALLUGO) | S_IFREG;
+ int error = 0;
+
+ mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_NORMAL);
+ error = configfs_make_dirent(parent_sd, NULL, (void *) bin_attr, mode,
+ CONFIGFS_ITEM_BIN_ATTR);
+ mutex_unlock(&dir->d_inode->i_mutex);
+
+ return error;
+}
diff --git a/fs/configfs/inode.c b/fs/configfs/inode.c
index eae87575e681..0cc810e9dccc 100644
--- a/fs/configfs/inode.c
+++ b/fs/configfs/inode.c
@@ -218,7 +218,7 @@ const unsigned char * configfs_get_name(struct configfs_dirent *sd)
if (sd->s_type & (CONFIGFS_DIR | CONFIGFS_ITEM_LINK))
return sd->s_dentry->d_name.name;
- if (sd->s_type & CONFIGFS_ITEM_ATTR) {
+ if (sd->s_type & (CONFIGFS_ITEM_ATTR | CONFIGFS_ITEM_BIN_ATTR)) {
attr = sd->s_element;
return attr->ca_name;
}
diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c
index ec5c8325b503..db6d69289608 100644
--- a/fs/configfs/symlink.c
+++ b/fs/configfs/symlink.c
@@ -279,27 +279,33 @@ static int configfs_getlink(struct dentry *dentry, char * path)
}
-static const char *configfs_follow_link(struct dentry *dentry, void **cookie)
+static const char *configfs_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
- unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *body;
int error;
- if (!page)
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
+
+ body = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!body)
return ERR_PTR(-ENOMEM);
- error = configfs_getlink(dentry, (char *)page);
+ error = configfs_getlink(dentry, body);
if (!error) {
- return *cookie = (void *)page;
+ set_delayed_call(done, kfree_link, body);
+ return body;
}
- free_page(page);
+ kfree(body);
return ERR_PTR(error);
}
const struct inode_operations configfs_symlink_inode_operations = {
- .follow_link = configfs_follow_link,
+ .get_link = configfs_get_link,
.readlink = generic_readlink,
- .put_link = free_page_put_link,
.setattr = configfs_setattr,
};
diff --git a/fs/coredump.c b/fs/coredump.c
index 1777331eee76..b3c153ca435d 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -32,6 +32,7 @@
#include <linux/pipe_fs_i.h>
#include <linux/oom.h>
#include <linux/compat.h>
+#include <linux/timekeeping.h>
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
@@ -232,9 +233,10 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm)
break;
/* UNIX time of coredump */
case 't': {
- struct timeval tv;
- do_gettimeofday(&tv);
- err = cn_printf(cn, "%lu", tv.tv_sec);
+ time64_t time;
+
+ time = ktime_get_real_seconds();
+ err = cn_printf(cn, "%lld", time);
break;
}
/* hostname */
diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
index 355c522f3585..b862bc219cd7 100644
--- a/fs/cramfs/inode.c
+++ b/fs/cramfs/inode.c
@@ -100,6 +100,7 @@ static struct inode *get_cramfs_inode(struct super_block *sb,
break;
case S_IFLNK:
inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_data.a_ops = &cramfs_aops;
break;
default:
diff --git a/fs/dcache.c b/fs/dcache.c
index 5c33aeb0f68f..b4539e84e577 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1571,7 +1571,8 @@ struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
dentry->d_iname[DNAME_INLINE_LEN-1] = 0;
if (name->len > DNAME_INLINE_LEN-1) {
size_t size = offsetof(struct external_name, name[1]);
- struct external_name *p = kmalloc(size + name->len, GFP_KERNEL);
+ struct external_name *p = kmalloc(size + name->len,
+ GFP_KERNEL_ACCOUNT);
if (!p) {
kmem_cache_free(dentry_cache, dentry);
return NULL;
@@ -1734,7 +1735,7 @@ static unsigned d_flags_for_inode(struct inode *inode)
}
if (unlikely(!(inode->i_opflags & IOP_NOFOLLOW))) {
- if (unlikely(inode->i_op->follow_link)) {
+ if (unlikely(inode->i_op->get_link)) {
add_flags = DCACHE_SYMLINK_TYPE;
goto type_determined;
}
@@ -3303,18 +3304,18 @@ out:
* @new_dentry: new dentry
* @old_dentry: old dentry
*
- * Returns 1 if new_dentry is a subdirectory of the parent (at any depth).
- * Returns 0 otherwise.
+ * Returns true if new_dentry is a subdirectory of the parent (at any depth).
+ * Returns false otherwise.
* Caller must ensure that "new_dentry" is pinned before calling is_subdir()
*/
-int is_subdir(struct dentry *new_dentry, struct dentry *old_dentry)
+bool is_subdir(struct dentry *new_dentry, struct dentry *old_dentry)
{
- int result;
+ bool result;
unsigned seq;
if (new_dentry == old_dentry)
- return 1;
+ return true;
do {
/* for restarting inner loop in case of seq retry */
@@ -3325,9 +3326,9 @@ int is_subdir(struct dentry *new_dentry, struct dentry *old_dentry)
*/
rcu_read_lock();
if (d_ancestor(old_dentry, new_dentry))
- result = 1;
+ result = true;
else
- result = 0;
+ result = false;
rcu_read_unlock();
} while (read_seqretry(&rename_lock, seq));
@@ -3415,7 +3416,7 @@ static void __init dcache_init(void)
* of the dcache.
*/
dentry_cache = KMEM_CACHE(dentry,
- SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_MEM_SPREAD);
+ SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_MEM_SPREAD|SLAB_ACCOUNT);
/* Hash may have been set up in dcache_init_early */
if (!hashdist)
diff --git a/fs/direct-io.c b/fs/direct-io.c
index cb5337d8c273..602e8441bc0f 100644
--- a/fs/direct-io.c
+++ b/fs/direct-io.c
@@ -1169,6 +1169,16 @@ do_blockdev_direct_IO(struct kiocb *iocb, struct inode *inode,
}
}
+ /* Once we sampled i_size check for reads beyond EOF */
+ dio->i_size = i_size_read(inode);
+ if (iov_iter_rw(iter) == READ && offset >= dio->i_size) {
+ if (dio->flags & DIO_LOCKING)
+ mutex_unlock(&inode->i_mutex);
+ kmem_cache_free(dio_cache, dio);
+ retval = 0;
+ goto out;
+ }
+
/*
* For file extending writes updating i_size before data writeouts
* complete can expose uninitialized blocks in dumb filesystems.
@@ -1222,7 +1232,6 @@ do_blockdev_direct_IO(struct kiocb *iocb, struct inode *inode,
sdio.next_block_for_io = -1;
dio->iocb = iocb;
- dio->i_size = i_size_read(inode);
spin_lock_init(&dio->bio_lock);
dio->refcount = 1;
diff --git a/fs/dlm/lowcomms.c b/fs/dlm/lowcomms.c
index 87e9d796cf7d..3a37bd3f9637 100644
--- a/fs/dlm/lowcomms.c
+++ b/fs/dlm/lowcomms.c
@@ -421,7 +421,7 @@ static void lowcomms_write_space(struct sock *sk)
if (test_and_clear_bit(CF_APP_LIMITED, &con->flags)) {
con->sock->sk->sk_write_pending--;
- clear_bit(SOCK_ASYNC_NOSPACE, &con->sock->flags);
+ clear_bit(SOCKWQ_ASYNC_NOSPACE, &con->sock->flags);
}
if (!test_and_set_bit(CF_WRITE_PENDING, &con->flags))
@@ -1448,7 +1448,7 @@ static void send_to_sock(struct connection *con)
msg_flags);
if (ret == -EAGAIN || ret == 0) {
if (ret == -EAGAIN &&
- test_bit(SOCK_ASYNC_NOSPACE, &con->sock->flags) &&
+ test_bit(SOCKWQ_ASYNC_NOSPACE, &con->sock->flags) &&
!test_and_set_bit(CF_APP_LIMITED, &con->flags)) {
/* Notify TCP that we're limited by the
* application window size.
diff --git a/fs/dlm/user.c b/fs/dlm/user.c
index 173b3873a4f4..1925d6d222b8 100644
--- a/fs/dlm/user.c
+++ b/fs/dlm/user.c
@@ -515,14 +515,9 @@ static ssize_t device_write(struct file *file, const char __user *buf,
if (count > sizeof(struct dlm_write_request) + DLM_RESNAME_MAXLEN)
return -EINVAL;
- kbuf = kzalloc(count + 1, GFP_NOFS);
- if (!kbuf)
- return -ENOMEM;
-
- if (copy_from_user(kbuf, buf, count)) {
- error = -EFAULT;
- goto out_free;
- }
+ kbuf = memdup_user_nul(buf, count);
+ if (!IS_ERR(kbuf))
+ return PTR_ERR(kbuf);
if (check_version(kbuf)) {
error = -EBADE;
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index e2e47ba5d313..040aa879d634 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -282,9 +282,7 @@ ecryptfs_create(struct inode *directory_inode, struct dentry *ecryptfs_dentry,
if (rc) {
ecryptfs_do_unlink(directory_inode, ecryptfs_dentry,
ecryptfs_inode);
- make_bad_inode(ecryptfs_inode);
- unlock_new_inode(ecryptfs_inode);
- iput(ecryptfs_inode);
+ iget_failed(ecryptfs_inode);
goto out;
}
unlock_new_inode(ecryptfs_inode);
@@ -674,16 +672,24 @@ out:
return rc ? ERR_PTR(rc) : buf;
}
-static const char *ecryptfs_follow_link(struct dentry *dentry, void **cookie)
+static const char *ecryptfs_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
size_t len;
- char *buf = ecryptfs_readlink_lower(dentry, &len);
+ char *buf;
+
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
+
+ buf = ecryptfs_readlink_lower(dentry, &len);
if (IS_ERR(buf))
return buf;
fsstack_copy_attr_atime(d_inode(dentry),
d_inode(ecryptfs_dentry_to_lower(dentry)));
buf[len] = '\0';
- return *cookie = buf;
+ set_delayed_call(done, kfree_link, buf);
+ return buf;
}
/**
@@ -1095,8 +1101,7 @@ out:
const struct inode_operations ecryptfs_symlink_iops = {
.readlink = generic_readlink,
- .follow_link = ecryptfs_follow_link,
- .put_link = kfree_put_link,
+ .get_link = ecryptfs_get_link,
.permission = ecryptfs_permission,
.setattr = ecryptfs_setattr,
.getattr = ecryptfs_getattr_link,
diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
index 4f4d0474bee9..e25b6b06bacf 100644
--- a/fs/ecryptfs/main.c
+++ b/fs/ecryptfs/main.c
@@ -663,6 +663,7 @@ static struct ecryptfs_cache_info {
struct kmem_cache **cache;
const char *name;
size_t size;
+ unsigned long flags;
void (*ctor)(void *obj);
} ecryptfs_cache_infos[] = {
{
@@ -684,6 +685,7 @@ static struct ecryptfs_cache_info {
.cache = &ecryptfs_inode_info_cache,
.name = "ecryptfs_inode_cache",
.size = sizeof(struct ecryptfs_inode_info),
+ .flags = SLAB_ACCOUNT,
.ctor = inode_info_init_once,
},
{
@@ -755,8 +757,8 @@ static int ecryptfs_init_kmem_caches(void)
struct ecryptfs_cache_info *info;
info = &ecryptfs_cache_infos[i];
- *(info->cache) = kmem_cache_create(info->name, info->size,
- 0, SLAB_HWCACHE_ALIGN, info->ctor);
+ *(info->cache) = kmem_cache_create(info->name, info->size, 0,
+ SLAB_HWCACHE_ALIGN | info->flags, info->ctor);
if (!*(info->cache)) {
ecryptfs_free_kmem_caches();
ecryptfs_printk(KERN_WARNING, "%s: "
diff --git a/fs/efs/inode.c b/fs/efs/inode.c
index 079d20306ee1..cdf0872382af 100644
--- a/fs/efs/inode.c
+++ b/fs/efs/inode.c
@@ -151,6 +151,7 @@ struct inode *efs_iget(struct super_block *super, unsigned long ino)
break;
case S_IFLNK:
inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_data.a_ops = &efs_symlink_aops;
break;
case S_IFCHR:
diff --git a/fs/efs/super.c b/fs/efs/super.c
index c8411a30f7da..cb68dac4f9d3 100644
--- a/fs/efs/super.c
+++ b/fs/efs/super.c
@@ -94,9 +94,9 @@ static void init_once(void *foo)
static int __init init_inodecache(void)
{
efs_inode_cachep = kmem_cache_create("efs_inode_cache",
- sizeof(struct efs_inode_info),
- 0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD,
- init_once);
+ sizeof(struct efs_inode_info), 0,
+ SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|
+ SLAB_ACCOUNT, init_once);
if (efs_inode_cachep == NULL)
return -ENOMEM;
return 0;
diff --git a/fs/efs/symlink.c b/fs/efs/symlink.c
index 75117d0dac2b..4870cc82deb0 100644
--- a/fs/efs/symlink.c
+++ b/fs/efs/symlink.c
@@ -13,7 +13,7 @@
static int efs_symlink_readpage(struct file *file, struct page *page)
{
- char *link = kmap(page);
+ char *link = page_address(page);
struct buffer_head * bh;
struct inode * inode = page->mapping->host;
efs_block_t size = inode->i_size;
@@ -39,12 +39,10 @@ static int efs_symlink_readpage(struct file *file, struct page *page)
}
link[size] = '\0';
SetPageUptodate(page);
- kunmap(page);
unlock_page(page);
return 0;
fail:
SetPageError(page);
- kunmap(page);
unlock_page(page);
return err;
}
diff --git a/fs/eventfd.c b/fs/eventfd.c
index 8d0c0df01854..ed70cf9fdc7b 100644
--- a/fs/eventfd.c
+++ b/fs/eventfd.c
@@ -45,10 +45,10 @@ struct eventfd_ctx {
*
* This function is supposed to be called by the kernel in paths that do not
* allow sleeping. In this function we allow the counter to reach the ULLONG_MAX
- * value, and we signal this as overflow condition by returining a POLLERR
+ * value, and we signal this as overflow condition by returning a POLLERR
* to poll(2).
*
- * Returns the amount by which the counter was incrememnted. This will be less
+ * Returns the amount by which the counter was incremented. This will be less
* than @n if the counter has overflowed.
*/
__u64 eventfd_signal(struct eventfd_ctx *ctx, __u64 n)
diff --git a/fs/exec.c b/fs/exec.c
index b06623a9347f..828ec5f07de0 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -119,7 +119,7 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
int error = PTR_ERR(tmp);
static const struct open_flags uselib_flags = {
.open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,
- .acc_mode = MAY_READ | MAY_EXEC | MAY_OPEN,
+ .acc_mode = MAY_READ | MAY_EXEC,
.intent = LOOKUP_OPEN,
.lookup_flags = LOOKUP_FOLLOW,
};
@@ -763,7 +763,7 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
int err;
struct open_flags open_exec_flags = {
.open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,
- .acc_mode = MAY_EXEC | MAY_OPEN,
+ .acc_mode = MAY_EXEC,
.intent = LOOKUP_OPEN,
.lookup_flags = LOOKUP_FOLLOW,
};
diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c
index 73c64daa0f55..9eaf595aeaf8 100644
--- a/fs/exofs/inode.c
+++ b/fs/exofs/inode.c
@@ -592,10 +592,7 @@ static struct page *__r4w_get_page(void *priv, u64 offset, bool *uptodate)
}
unlock_page(page);
}
- if (PageDirty(page) || PageWriteback(page))
- *uptodate = true;
- else
- *uptodate = PageUptodate(page);
+ *uptodate = PageUptodate(page);
EXOFS_DBGMSG2("index=0x%lx uptodate=%d\n", index, *uptodate);
return page;
} else {
@@ -1227,6 +1224,7 @@ struct inode *exofs_iget(struct super_block *sb, unsigned long ino)
inode->i_link = (char *)oi->i_data;
} else {
inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_mapping->a_ops = &exofs_aops;
}
} else {
diff --git a/fs/exofs/namei.c b/fs/exofs/namei.c
index 994e078da4bb..c20d77df2679 100644
--- a/fs/exofs/namei.c
+++ b/fs/exofs/namei.c
@@ -111,6 +111,7 @@ static int exofs_symlink(struct inode *dir, struct dentry *dentry,
if (l > sizeof(oi->i_data)) {
/* slow symlink */
inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_mapping->a_ops = &exofs_aops;
memset(oi->i_data, 0, sizeof(oi->i_data));
diff --git a/fs/exofs/super.c b/fs/exofs/super.c
index b795c567b5e1..6658a50530a0 100644
--- a/fs/exofs/super.c
+++ b/fs/exofs/super.c
@@ -194,8 +194,8 @@ static int init_inodecache(void)
{
exofs_inode_cachep = kmem_cache_create("exofs_inode_cache",
sizeof(struct exofs_i_info), 0,
- SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD,
- exofs_init_once);
+ SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD |
+ SLAB_ACCOUNT, exofs_init_once);
if (exofs_inode_cachep == NULL)
return -ENOMEM;
return 0;
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 0aa9bf6e6e53..338eefda70c6 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -1420,6 +1420,7 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
sizeof(ei->i_data) - 1);
} else {
inode->i_op = &ext2_symlink_inode_operations;
+ inode_nohighmem(inode);
if (test_opt(inode->i_sb, NOBH))
inode->i_mapping->a_ops = &ext2_nobh_aops;
else
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index 3267a80dbbe2..7a2be8f7f3c3 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -183,6 +183,7 @@ static int ext2_symlink (struct inode * dir, struct dentry * dentry,
if (l > sizeof (EXT2_I(inode)->i_data)) {
/* slow symlink */
inode->i_op = &ext2_symlink_inode_operations;
+ inode_nohighmem(inode);
if (test_opt(inode->i_sb, NOBH))
inode->i_mapping->a_ops = &ext2_nobh_aops;
else
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index 748d35afc902..2a188413a2b0 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -203,7 +203,7 @@ static int __init init_inodecache(void)
ext2_inode_cachep = kmem_cache_create("ext2_inode_cache",
sizeof(struct ext2_inode_info),
0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
init_once);
if (ext2_inode_cachep == NULL)
return -ENOMEM;
diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c
index ae17179f3810..3495d8ae4b33 100644
--- a/fs/ext2/symlink.c
+++ b/fs/ext2/symlink.c
@@ -22,8 +22,7 @@
const struct inode_operations ext2_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = page_follow_link_light,
- .put_link = page_put_link,
+ .get_link = page_get_link,
.setattr = ext2_setattr,
#ifdef CONFIG_EXT2_FS_XATTR
.setxattr = generic_setxattr,
@@ -35,7 +34,7 @@ const struct inode_operations ext2_symlink_inode_operations = {
const struct inode_operations ext2_fast_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = simple_follow_link,
+ .get_link = simple_get_link,
.setattr = ext2_setattr,
#ifdef CONFIG_EXT2_FS_XATTR
.setxattr = generic_setxattr,
diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c
index fa70848afa8f..f57a7aba32eb 100644
--- a/fs/ext2/xattr.c
+++ b/fs/ext2/xattr.c
@@ -77,10 +77,8 @@
printk("\n"); \
} while (0)
# define ea_bdebug(bh, f...) do { \
- char b[BDEVNAME_SIZE]; \
- printk(KERN_DEBUG "block %s:%lu: ", \
- bdevname(bh->b_bdev, b), \
- (unsigned long) bh->b_blocknr); \
+ printk(KERN_DEBUG "block %pg:%lu: ", \
+ bh->b_bdev, (unsigned long) bh->b_blocknr); \
printk(f); \
printk("\n"); \
} while (0)
@@ -292,16 +290,21 @@ bad_block: ext2_error(inode->i_sb, "ext2_xattr_list",
const struct xattr_handler *handler =
ext2_xattr_handler(entry->e_name_index);
- if (handler) {
- size_t size = handler->list(handler, dentry, buffer,
- rest, entry->e_name,
- entry->e_name_len);
+ if (handler && (!handler->list || handler->list(dentry))) {
+ const char *prefix = handler->prefix ?: handler->name;
+ size_t prefix_len = strlen(prefix);
+ size_t size = prefix_len + entry->e_name_len + 1;
+
if (buffer) {
if (size > rest) {
error = -ERANGE;
goto cleanup;
}
- buffer += size;
+ memcpy(buffer, prefix, prefix_len);
+ buffer += prefix_len;
+ memcpy(buffer, entry->e_name, entry->e_name_len);
+ buffer += entry->e_name_len;
+ *buffer++ = 0;
}
rest -= size;
}
diff --git a/fs/ext2/xattr_security.c b/fs/ext2/xattr_security.c
index dfb08750370d..ba97f243b050 100644
--- a/fs/ext2/xattr_security.c
+++ b/fs/ext2/xattr_security.c
@@ -7,29 +7,11 @@
#include <linux/security.h>
#include "xattr.h"
-static size_t
-ext2_xattr_security_list(const struct xattr_handler *handler,
- struct dentry *dentry, char *list, size_t list_size,
- const char *name, size_t name_len)
-{
- const int prefix_len = XATTR_SECURITY_PREFIX_LEN;
- const size_t total_len = prefix_len + name_len + 1;
-
- if (list && total_len <= list_size) {
- memcpy(list, XATTR_SECURITY_PREFIX, prefix_len);
- memcpy(list+prefix_len, name, name_len);
- list[prefix_len + name_len] = '\0';
- }
- return total_len;
-}
-
static int
ext2_xattr_security_get(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
void *buffer, size_t size)
{
- if (strcmp(name, "") == 0)
- return -EINVAL;
return ext2_xattr_get(d_inode(dentry), EXT2_XATTR_INDEX_SECURITY, name,
buffer, size);
}
@@ -39,8 +21,6 @@ ext2_xattr_security_set(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
- if (strcmp(name, "") == 0)
- return -EINVAL;
return ext2_xattr_set(d_inode(dentry), EXT2_XATTR_INDEX_SECURITY, name,
value, size, flags);
}
@@ -71,7 +51,6 @@ ext2_init_security(struct inode *inode, struct inode *dir,
const struct xattr_handler ext2_xattr_security_handler = {
.prefix = XATTR_SECURITY_PREFIX,
- .list = ext2_xattr_security_list,
.get = ext2_xattr_security_get,
.set = ext2_xattr_security_set,
};
diff --git a/fs/ext2/xattr_trusted.c b/fs/ext2/xattr_trusted.c
index 3150dd3a7859..2c94d1930626 100644
--- a/fs/ext2/xattr_trusted.c
+++ b/fs/ext2/xattr_trusted.c
@@ -8,23 +8,10 @@
#include "ext2.h"
#include "xattr.h"
-static size_t
-ext2_xattr_trusted_list(const struct xattr_handler *handler,
- struct dentry *dentry, char *list, size_t list_size,
- const char *name, size_t name_len)
+static bool
+ext2_xattr_trusted_list(struct dentry *dentry)
{
- const int prefix_len = XATTR_TRUSTED_PREFIX_LEN;
- const size_t total_len = prefix_len + name_len + 1;
-
- if (!capable(CAP_SYS_ADMIN))
- return 0;
-
- if (list && total_len <= list_size) {
- memcpy(list, XATTR_TRUSTED_PREFIX, prefix_len);
- memcpy(list+prefix_len, name, name_len);
- list[prefix_len + name_len] = '\0';
- }
- return total_len;
+ return capable(CAP_SYS_ADMIN);
}
static int
@@ -32,8 +19,6 @@ ext2_xattr_trusted_get(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
void *buffer, size_t size)
{
- if (strcmp(name, "") == 0)
- return -EINVAL;
return ext2_xattr_get(d_inode(dentry), EXT2_XATTR_INDEX_TRUSTED, name,
buffer, size);
}
@@ -43,8 +28,6 @@ ext2_xattr_trusted_set(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
- if (strcmp(name, "") == 0)
- return -EINVAL;
return ext2_xattr_set(d_inode(dentry), EXT2_XATTR_INDEX_TRUSTED, name,
value, size, flags);
}
diff --git a/fs/ext2/xattr_user.c b/fs/ext2/xattr_user.c
index 339a49bbb8ef..72a2a96d677f 100644
--- a/fs/ext2/xattr_user.c
+++ b/fs/ext2/xattr_user.c
@@ -10,23 +10,10 @@
#include "ext2.h"
#include "xattr.h"
-static size_t
-ext2_xattr_user_list(const struct xattr_handler *handler,
- struct dentry *dentry, char *list, size_t list_size,
- const char *name, size_t name_len)
+static bool
+ext2_xattr_user_list(struct dentry *dentry)
{
- const size_t prefix_len = XATTR_USER_PREFIX_LEN;
- const size_t total_len = prefix_len + name_len + 1;
-
- if (!test_opt(dentry->d_sb, XATTR_USER))
- return 0;
-
- if (list && total_len <= list_size) {
- memcpy(list, XATTR_USER_PREFIX, prefix_len);
- memcpy(list+prefix_len, name, name_len);
- list[prefix_len + name_len] = '\0';
- }
- return total_len;
+ return test_opt(dentry->d_sb, XATTR_USER);
}
static int
@@ -34,8 +21,6 @@ ext2_xattr_user_get(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
void *buffer, size_t size)
{
- if (strcmp(name, "") == 0)
- return -EINVAL;
if (!test_opt(dentry->d_sb, XATTR_USER))
return -EOPNOTSUPP;
return ext2_xattr_get(d_inode(dentry), EXT2_XATTR_INDEX_USER,
@@ -47,8 +32,6 @@ ext2_xattr_user_set(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
- if (strcmp(name, "") == 0)
- return -EINVAL;
if (!test_opt(dentry->d_sb, XATTR_USER))
return -EOPNOTSUPP;
diff --git a/fs/ext4/crypto.c b/fs/ext4/crypto.c
index af06830bfc00..1a0835073663 100644
--- a/fs/ext4/crypto.c
+++ b/fs/ext4/crypto.c
@@ -389,7 +389,7 @@ int ext4_encrypted_zeroout(struct inode *inode, struct ext4_extent *ex)
struct ext4_crypto_ctx *ctx;
struct page *ciphertext_page = NULL;
struct bio *bio;
- ext4_lblk_t lblk = ex->ee_block;
+ ext4_lblk_t lblk = le32_to_cpu(ex->ee_block);
ext4_fsblk_t pblk = ext4_ext_pblock(ex);
unsigned int len = ext4_ext_get_actual_len(ex);
int ret, err = 0;
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 750063f7a50c..cc7ca4e87144 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -26,6 +26,7 @@
#include <linux/seqlock.h>
#include <linux/mutex.h>
#include <linux/timer.h>
+#include <linux/version.h>
#include <linux/wait.h>
#include <linux/blockgroup_lock.h>
#include <linux/percpu_counter.h>
@@ -727,19 +728,55 @@ struct move_extent {
<= (EXT4_GOOD_OLD_INODE_SIZE + \
(einode)->i_extra_isize)) \
+/*
+ * We use an encoding that preserves the times for extra epoch "00":
+ *
+ * extra msb of adjust for signed
+ * epoch 32-bit 32-bit tv_sec to
+ * bits time decoded 64-bit tv_sec 64-bit tv_sec valid time range
+ * 0 0 1 -0x80000000..-0x00000001 0x000000000 1901-12-13..1969-12-31
+ * 0 0 0 0x000000000..0x07fffffff 0x000000000 1970-01-01..2038-01-19
+ * 0 1 1 0x080000000..0x0ffffffff 0x100000000 2038-01-19..2106-02-07
+ * 0 1 0 0x100000000..0x17fffffff 0x100000000 2106-02-07..2174-02-25
+ * 1 0 1 0x180000000..0x1ffffffff 0x200000000 2174-02-25..2242-03-16
+ * 1 0 0 0x200000000..0x27fffffff 0x200000000 2242-03-16..2310-04-04
+ * 1 1 1 0x280000000..0x2ffffffff 0x300000000 2310-04-04..2378-04-22
+ * 1 1 0 0x300000000..0x37fffffff 0x300000000 2378-04-22..2446-05-10
+ *
+ * Note that previous versions of the kernel on 64-bit systems would
+ * incorrectly use extra epoch bits 1,1 for dates between 1901 and
+ * 1970. e2fsck will correct this, assuming that it is run on the
+ * affected filesystem before 2242.
+ */
+
static inline __le32 ext4_encode_extra_time(struct timespec *time)
{
- return cpu_to_le32((sizeof(time->tv_sec) > 4 ?
- (time->tv_sec >> 32) & EXT4_EPOCH_MASK : 0) |
- ((time->tv_nsec << EXT4_EPOCH_BITS) & EXT4_NSEC_MASK));
+ u32 extra = sizeof(time->tv_sec) > 4 ?
+ ((time->tv_sec - (s32)time->tv_sec) >> 32) & EXT4_EPOCH_MASK : 0;
+ return cpu_to_le32(extra | (time->tv_nsec << EXT4_EPOCH_BITS));
}
static inline void ext4_decode_extra_time(struct timespec *time, __le32 extra)
{
- if (sizeof(time->tv_sec) > 4)
- time->tv_sec |= (__u64)(le32_to_cpu(extra) & EXT4_EPOCH_MASK)
- << 32;
- time->tv_nsec = (le32_to_cpu(extra) & EXT4_NSEC_MASK) >> EXT4_EPOCH_BITS;
+ if (unlikely(sizeof(time->tv_sec) > 4 &&
+ (extra & cpu_to_le32(EXT4_EPOCH_MASK)))) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,20,0)
+ /* Handle legacy encoding of pre-1970 dates with epoch
+ * bits 1,1. We assume that by kernel version 4.20,
+ * everyone will have run fsck over the affected
+ * filesystems to correct the problem. (This
+ * backwards compatibility may be removed before this
+ * time, at the discretion of the ext4 developers.)
+ */
+ u64 extra_bits = le32_to_cpu(extra) & EXT4_EPOCH_MASK;
+ if (extra_bits == 3 && ((time->tv_sec) & 0x80000000) != 0)
+ extra_bits = 0;
+ time->tv_sec += extra_bits << 32;
+#else
+ time->tv_sec += (u64)(le32_to_cpu(extra) & EXT4_EPOCH_MASK) << 32;
+#endif
+ }
+ time->tv_nsec = (le32_to_cpu(extra) & EXT4_NSEC_MASK) >> EXT4_EPOCH_BITS;
}
#define EXT4_INODE_SET_XTIME(xtime, inode, raw_inode) \
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index ea433a7f4bca..b3bd912df6bf 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -4283,6 +4283,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
inode->i_op = &ext4_symlink_inode_operations;
ext4_set_aops(inode);
}
+ inode_nohighmem(inode);
} else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
inode->i_op = &ext4_special_inode_operations;
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index a969ab39f302..f27e0c2598c5 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -3132,6 +3132,7 @@ static int ext4_symlink(struct inode *dir,
if ((disk_link.len > EXT4_N_BLOCKS * 4)) {
if (!encryption_required)
inode->i_op = &ext4_symlink_inode_operations;
+ inode_nohighmem(inode);
ext4_set_aops(inode);
/*
* We cannot call page_symlink() with transaction started
diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
index 17fbe3882b8e..090b3498638e 100644
--- a/fs/ext4/page-io.c
+++ b/fs/ext4/page-io.c
@@ -52,9 +52,8 @@ void ext4_exit_pageio(void)
*/
static void buffer_io_error(struct buffer_head *bh)
{
- char b[BDEVNAME_SIZE];
- printk_ratelimited(KERN_ERR "Buffer I/O error on device %s, logical block %llu\n",
- bdevname(bh->b_bdev, b),
+ printk_ratelimited(KERN_ERR "Buffer I/O error on device %pg, logical block %llu\n",
+ bh->b_bdev,
(unsigned long long)bh->b_blocknr);
}
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index c9ab67da6e5a..f1b56ff01208 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -966,7 +966,7 @@ static int __init init_inodecache(void)
ext4_inode_cachep = kmem_cache_create("ext4_inode_cache",
sizeof(struct ext4_inode_info),
0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
init_once);
if (ext4_inode_cachep == NULL)
return -ENOMEM;
diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c
index abe2401ce405..6f7ee30a89ce 100644
--- a/fs/ext4/symlink.c
+++ b/fs/ext4/symlink.c
@@ -23,17 +23,21 @@
#include "xattr.h"
#ifdef CONFIG_EXT4_FS_ENCRYPTION
-static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cookie)
+static const char *ext4_encrypted_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
struct page *cpage = NULL;
char *caddr, *paddr = NULL;
struct ext4_str cstr, pstr;
- struct inode *inode = d_inode(dentry);
struct ext4_encrypted_symlink_data *sd;
loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1);
int res;
u32 plen, max_size = inode->i_sb->s_blocksize;
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
+
res = ext4_get_encryption_info(inode);
if (res)
return ERR_PTR(res);
@@ -45,14 +49,14 @@ static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cook
cpage = read_mapping_page(inode->i_mapping, 0, NULL);
if (IS_ERR(cpage))
return ERR_CAST(cpage);
- caddr = kmap(cpage);
+ caddr = page_address(cpage);
caddr[size] = 0;
}
/* Symlink is encrypted */
sd = (struct ext4_encrypted_symlink_data *)caddr;
cstr.name = sd->encrypted_path;
- cstr.len = le32_to_cpu(sd->len);
+ cstr.len = le16_to_cpu(sd->len);
if ((cstr.len +
sizeof(struct ext4_encrypted_symlink_data) - 1) >
max_size) {
@@ -75,24 +79,20 @@ static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cook
/* Null-terminate the name */
if (res <= plen)
paddr[res] = '\0';
- if (cpage) {
- kunmap(cpage);
+ if (cpage)
page_cache_release(cpage);
- }
- return *cookie = paddr;
+ set_delayed_call(done, kfree_link, paddr);
+ return paddr;
errout:
- if (cpage) {
- kunmap(cpage);
+ if (cpage)
page_cache_release(cpage);
- }
kfree(paddr);
return ERR_PTR(res);
}
const struct inode_operations ext4_encrypted_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = ext4_encrypted_follow_link,
- .put_link = kfree_put_link,
+ .get_link = ext4_encrypted_get_link,
.setattr = ext4_setattr,
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
@@ -103,8 +103,7 @@ const struct inode_operations ext4_encrypted_symlink_inode_operations = {
const struct inode_operations ext4_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = page_follow_link_light,
- .put_link = page_put_link,
+ .get_link = page_get_link,
.setattr = ext4_setattr,
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
@@ -114,7 +113,7 @@ const struct inode_operations ext4_symlink_inode_operations = {
const struct inode_operations ext4_fast_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = simple_follow_link,
+ .get_link = simple_get_link,
.setattr = ext4_setattr,
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
diff --git a/fs/ext4/sysfs.c b/fs/ext4/sysfs.c
index 1b57c72f4a00..1420a3c614af 100644
--- a/fs/ext4/sysfs.c
+++ b/fs/ext4/sysfs.c
@@ -358,7 +358,7 @@ static int name##_open(struct inode *inode, struct file *file) \
return single_open(file, ext4_seq_##name##_show, PDE_DATA(inode)); \
} \
\
-const struct file_operations ext4_seq_##name##_fops = { \
+static const struct file_operations ext4_seq_##name##_fops = { \
.owner = THIS_MODULE, \
.open = name##_open, \
.read = seq_read, \
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 6b6b3e751f8c..a95151e875bd 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -68,10 +68,8 @@
printk("\n"); \
} while (0)
# define ea_bdebug(bh, f...) do { \
- char b[BDEVNAME_SIZE]; \
- printk(KERN_DEBUG "block %s:%lu: ", \
- bdevname(bh->b_bdev, b), \
- (unsigned long) bh->b_blocknr); \
+ printk(KERN_DEBUG "block %pg:%lu: ", \
+ bh->b_bdev, (unsigned long) bh->b_blocknr); \
printk(f); \
printk("\n"); \
} while (0)
@@ -404,19 +402,24 @@ ext4_xattr_list_entries(struct dentry *dentry, struct ext4_xattr_entry *entry,
const struct xattr_handler *handler =
ext4_xattr_handler(entry->e_name_index);
- if (handler) {
- size_t size = handler->list(handler, dentry, buffer,
- rest, entry->e_name,
- entry->e_name_len);
+ if (handler && (!handler->list || handler->list(dentry))) {
+ const char *prefix = handler->prefix ?: handler->name;
+ size_t prefix_len = strlen(prefix);
+ size_t size = prefix_len + entry->e_name_len + 1;
+
if (buffer) {
if (size > rest)
return -ERANGE;
- buffer += size;
+ memcpy(buffer, prefix, prefix_len);
+ buffer += prefix_len;
+ memcpy(buffer, entry->e_name, entry->e_name_len);
+ buffer += entry->e_name_len;
+ *buffer++ = 0;
}
rest -= size;
}
}
- return buffer_size - rest;
+ return buffer_size - rest; /* total size */
}
static int
diff --git a/fs/ext4/xattr_security.c b/fs/ext4/xattr_security.c
index 36f4c1a84c21..3e81bdca071a 100644
--- a/fs/ext4/xattr_security.c
+++ b/fs/ext4/xattr_security.c
@@ -11,30 +11,11 @@
#include "ext4.h"
#include "xattr.h"
-static size_t
-ext4_xattr_security_list(const struct xattr_handler *handler,
- struct dentry *dentry, char *list, size_t list_size,
- const char *name, size_t name_len)
-{
- const size_t prefix_len = sizeof(XATTR_SECURITY_PREFIX)-1;
- const size_t total_len = prefix_len + name_len + 1;
-
-
- if (list && total_len <= list_size) {
- memcpy(list, XATTR_SECURITY_PREFIX, prefix_len);
- memcpy(list+prefix_len, name, name_len);
- list[prefix_len + name_len] = '\0';
- }
- return total_len;
-}
-
static int
ext4_xattr_security_get(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
void *buffer, size_t size)
{
- if (strcmp(name, "") == 0)
- return -EINVAL;
return ext4_xattr_get(d_inode(dentry), EXT4_XATTR_INDEX_SECURITY,
name, buffer, size);
}
@@ -44,8 +25,6 @@ ext4_xattr_security_set(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
- if (strcmp(name, "") == 0)
- return -EINVAL;
return ext4_xattr_set(d_inode(dentry), EXT4_XATTR_INDEX_SECURITY,
name, value, size, flags);
}
@@ -79,7 +58,6 @@ ext4_init_security(handle_t *handle, struct inode *inode, struct inode *dir,
const struct xattr_handler ext4_xattr_security_handler = {
.prefix = XATTR_SECURITY_PREFIX,
- .list = ext4_xattr_security_list,
.get = ext4_xattr_security_get,
.set = ext4_xattr_security_set,
};
diff --git a/fs/ext4/xattr_trusted.c b/fs/ext4/xattr_trusted.c
index 488089053342..2a3c6f9b8cb8 100644
--- a/fs/ext4/xattr_trusted.c
+++ b/fs/ext4/xattr_trusted.c
@@ -12,23 +12,10 @@
#include "ext4.h"
#include "xattr.h"
-static size_t
-ext4_xattr_trusted_list(const struct xattr_handler *handler,
- struct dentry *dentry, char *list, size_t list_size,
- const char *name, size_t name_len)
+static bool
+ext4_xattr_trusted_list(struct dentry *dentry)
{
- const size_t prefix_len = XATTR_TRUSTED_PREFIX_LEN;
- const size_t total_len = prefix_len + name_len + 1;
-
- if (!capable(CAP_SYS_ADMIN))
- return 0;
-
- if (list && total_len <= list_size) {
- memcpy(list, XATTR_TRUSTED_PREFIX, prefix_len);
- memcpy(list+prefix_len, name, name_len);
- list[prefix_len + name_len] = '\0';
- }
- return total_len;
+ return capable(CAP_SYS_ADMIN);
}
static int
@@ -36,8 +23,6 @@ ext4_xattr_trusted_get(const struct xattr_handler *handler,
struct dentry *dentry, const char *name, void *buffer,
size_t size)
{
- if (strcmp(name, "") == 0)
- return -EINVAL;
return ext4_xattr_get(d_inode(dentry), EXT4_XATTR_INDEX_TRUSTED,
name, buffer, size);
}
@@ -47,8 +32,6 @@ ext4_xattr_trusted_set(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
- if (strcmp(name, "") == 0)
- return -EINVAL;
return ext4_xattr_set(d_inode(dentry), EXT4_XATTR_INDEX_TRUSTED,
name, value, size, flags);
}
diff --git a/fs/ext4/xattr_user.c b/fs/ext4/xattr_user.c
index d2dec3364062..d152f431e432 100644
--- a/fs/ext4/xattr_user.c
+++ b/fs/ext4/xattr_user.c
@@ -11,23 +11,10 @@
#include "ext4.h"
#include "xattr.h"
-static size_t
-ext4_xattr_user_list(const struct xattr_handler *handler,
- struct dentry *dentry, char *list, size_t list_size,
- const char *name, size_t name_len)
+static bool
+ext4_xattr_user_list(struct dentry *dentry)
{
- const size_t prefix_len = XATTR_USER_PREFIX_LEN;
- const size_t total_len = prefix_len + name_len + 1;
-
- if (!test_opt(dentry->d_sb, XATTR_USER))
- return 0;
-
- if (list && total_len <= list_size) {
- memcpy(list, XATTR_USER_PREFIX, prefix_len);
- memcpy(list+prefix_len, name, name_len);
- list[prefix_len + name_len] = '\0';
- }
- return total_len;
+ return test_opt(dentry->d_sb, XATTR_USER);
}
static int
@@ -35,8 +22,6 @@ ext4_xattr_user_get(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
void *buffer, size_t size)
{
- if (strcmp(name, "") == 0)
- return -EINVAL;
if (!test_opt(dentry->d_sb, XATTR_USER))
return -EOPNOTSUPP;
return ext4_xattr_get(d_inode(dentry), EXT4_XATTR_INDEX_USER,
@@ -48,8 +33,6 @@ ext4_xattr_user_set(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
- if (strcmp(name, "") == 0)
- return -EINVAL;
if (!test_opt(dentry->d_sb, XATTR_USER))
return -EOPNOTSUPP;
return ext4_xattr_set(d_inode(dentry), EXT4_XATTR_INDEX_USER,
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index f661d80474be..3842af954cd5 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -237,7 +237,7 @@ static int f2fs_write_meta_page(struct page *page,
dec_page_count(sbi, F2FS_DIRTY_META);
unlock_page(page);
- if (wbc->for_reclaim)
+ if (wbc->for_reclaim || unlikely(f2fs_cp_error(sbi)))
f2fs_submit_merged_bio(sbi, META, WRITE);
return 0;
@@ -410,13 +410,13 @@ static void __remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type)
spin_unlock(&im->ino_lock);
}
-void add_dirty_inode(struct f2fs_sb_info *sbi, nid_t ino, int type)
+void add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type)
{
/* add new dirty ino entry into list */
__add_ino_entry(sbi, ino, type);
}
-void remove_dirty_inode(struct f2fs_sb_info *sbi, nid_t ino, int type)
+void remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type)
{
/* remove dirty ino entry from list */
__remove_ino_entry(sbi, ino, type);
@@ -434,7 +434,7 @@ bool exist_written_data(struct f2fs_sb_info *sbi, nid_t ino, int mode)
return e ? true : false;
}
-void release_dirty_inode(struct f2fs_sb_info *sbi)
+void release_ino_entry(struct f2fs_sb_info *sbi)
{
struct ino_entry *e, *tmp;
int i;
@@ -722,47 +722,48 @@ fail_no_cp:
return -EINVAL;
}
-static int __add_dirty_inode(struct inode *inode, struct inode_entry *new)
+static void __add_dirty_inode(struct inode *inode, enum inode_type type)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+ struct f2fs_inode_info *fi = F2FS_I(inode);
+ int flag = (type == DIR_INODE) ? FI_DIRTY_DIR : FI_DIRTY_FILE;
- if (is_inode_flag_set(F2FS_I(inode), FI_DIRTY_DIR))
- return -EEXIST;
+ if (is_inode_flag_set(fi, flag))
+ return;
- set_inode_flag(F2FS_I(inode), FI_DIRTY_DIR);
- F2FS_I(inode)->dirty_dir = new;
- list_add_tail(&new->list, &sbi->dir_inode_list);
- stat_inc_dirty_dir(sbi);
- return 0;
+ set_inode_flag(fi, flag);
+ list_add_tail(&fi->dirty_list, &sbi->inode_list[type]);
+ stat_inc_dirty_inode(sbi, type);
+}
+
+static void __remove_dirty_inode(struct inode *inode, enum inode_type type)
+{
+ struct f2fs_inode_info *fi = F2FS_I(inode);
+ int flag = (type == DIR_INODE) ? FI_DIRTY_DIR : FI_DIRTY_FILE;
+
+ if (get_dirty_pages(inode) ||
+ !is_inode_flag_set(F2FS_I(inode), flag))
+ return;
+
+ list_del_init(&fi->dirty_list);
+ clear_inode_flag(fi, flag);
+ stat_dec_dirty_inode(F2FS_I_SB(inode), type);
}
void update_dirty_page(struct inode *inode, struct page *page)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
- struct inode_entry *new;
- int ret = 0;
+ enum inode_type type = S_ISDIR(inode->i_mode) ? DIR_INODE : FILE_INODE;
if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) &&
!S_ISLNK(inode->i_mode))
return;
- if (!S_ISDIR(inode->i_mode)) {
- inode_inc_dirty_pages(inode);
- goto out;
- }
-
- new = f2fs_kmem_cache_alloc(inode_entry_slab, GFP_NOFS);
- new->inode = inode;
- INIT_LIST_HEAD(&new->list);
-
- spin_lock(&sbi->dir_inode_lock);
- ret = __add_dirty_inode(inode, new);
+ spin_lock(&sbi->inode_lock[type]);
+ __add_dirty_inode(inode, type);
inode_inc_dirty_pages(inode);
- spin_unlock(&sbi->dir_inode_lock);
+ spin_unlock(&sbi->inode_lock[type]);
- if (ret)
- kmem_cache_free(inode_entry_slab, new);
-out:
SetPagePrivate(page);
f2fs_trace_pid(page);
}
@@ -770,70 +771,60 @@ out:
void add_dirty_dir_inode(struct inode *inode)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
- struct inode_entry *new =
- f2fs_kmem_cache_alloc(inode_entry_slab, GFP_NOFS);
- int ret = 0;
-
- new->inode = inode;
- INIT_LIST_HEAD(&new->list);
- spin_lock(&sbi->dir_inode_lock);
- ret = __add_dirty_inode(inode, new);
- spin_unlock(&sbi->dir_inode_lock);
-
- if (ret)
- kmem_cache_free(inode_entry_slab, new);
+ spin_lock(&sbi->inode_lock[DIR_INODE]);
+ __add_dirty_inode(inode, DIR_INODE);
+ spin_unlock(&sbi->inode_lock[DIR_INODE]);
}
-void remove_dirty_dir_inode(struct inode *inode)
+void remove_dirty_inode(struct inode *inode)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
- struct inode_entry *entry;
-
- if (!S_ISDIR(inode->i_mode))
- return;
+ struct f2fs_inode_info *fi = F2FS_I(inode);
+ enum inode_type type = S_ISDIR(inode->i_mode) ? DIR_INODE : FILE_INODE;
- spin_lock(&sbi->dir_inode_lock);
- if (get_dirty_pages(inode) ||
- !is_inode_flag_set(F2FS_I(inode), FI_DIRTY_DIR)) {
- spin_unlock(&sbi->dir_inode_lock);
+ if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) &&
+ !S_ISLNK(inode->i_mode))
return;
- }
- entry = F2FS_I(inode)->dirty_dir;
- list_del(&entry->list);
- F2FS_I(inode)->dirty_dir = NULL;
- clear_inode_flag(F2FS_I(inode), FI_DIRTY_DIR);
- stat_dec_dirty_dir(sbi);
- spin_unlock(&sbi->dir_inode_lock);
- kmem_cache_free(inode_entry_slab, entry);
+ spin_lock(&sbi->inode_lock[type]);
+ __remove_dirty_inode(inode, type);
+ spin_unlock(&sbi->inode_lock[type]);
/* Only from the recovery routine */
- if (is_inode_flag_set(F2FS_I(inode), FI_DELAY_IPUT)) {
- clear_inode_flag(F2FS_I(inode), FI_DELAY_IPUT);
+ if (is_inode_flag_set(fi, FI_DELAY_IPUT)) {
+ clear_inode_flag(fi, FI_DELAY_IPUT);
iput(inode);
}
}
-void sync_dirty_dir_inodes(struct f2fs_sb_info *sbi)
+int sync_dirty_inodes(struct f2fs_sb_info *sbi, enum inode_type type)
{
struct list_head *head;
- struct inode_entry *entry;
struct inode *inode;
+ struct f2fs_inode_info *fi;
+ bool is_dir = (type == DIR_INODE);
+
+ trace_f2fs_sync_dirty_inodes_enter(sbi->sb, is_dir,
+ get_pages(sbi, is_dir ?
+ F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA));
retry:
if (unlikely(f2fs_cp_error(sbi)))
- return;
+ return -EIO;
- spin_lock(&sbi->dir_inode_lock);
+ spin_lock(&sbi->inode_lock[type]);
- head = &sbi->dir_inode_list;
+ head = &sbi->inode_list[type];
if (list_empty(head)) {
- spin_unlock(&sbi->dir_inode_lock);
- return;
+ spin_unlock(&sbi->inode_lock[type]);
+ trace_f2fs_sync_dirty_inodes_exit(sbi->sb, is_dir,
+ get_pages(sbi, is_dir ?
+ F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA));
+ return 0;
}
- entry = list_entry(head->next, struct inode_entry, list);
- inode = igrab(entry->inode);
- spin_unlock(&sbi->dir_inode_lock);
+ fi = list_entry(head->next, struct f2fs_inode_info, dirty_list);
+ inode = igrab(&fi->vfs_inode);
+ spin_unlock(&sbi->inode_lock[type]);
if (inode) {
filemap_fdatawrite(inode->i_mapping);
iput(inode);
@@ -868,11 +859,9 @@ retry_flush_dents:
/* write all the dirty dentry pages */
if (get_pages(sbi, F2FS_DIRTY_DENTS)) {
f2fs_unlock_all(sbi);
- sync_dirty_dir_inodes(sbi);
- if (unlikely(f2fs_cp_error(sbi))) {
- err = -EIO;
+ err = sync_dirty_inodes(sbi, DIR_INODE);
+ if (err)
goto out;
- }
goto retry_flush_dents;
}
@@ -885,10 +874,9 @@ retry_flush_nodes:
if (get_pages(sbi, F2FS_DIRTY_NODES)) {
up_write(&sbi->node_write);
- sync_node_pages(sbi, 0, &wbc);
- if (unlikely(f2fs_cp_error(sbi))) {
+ err = sync_node_pages(sbi, 0, &wbc);
+ if (err) {
f2fs_unlock_all(sbi);
- err = -EIO;
goto out;
}
goto retry_flush_nodes;
@@ -919,7 +907,7 @@ static void wait_on_all_pages_writeback(struct f2fs_sb_info *sbi)
finish_wait(&sbi->cp_wait, &wait);
}
-static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
+static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
{
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_WARM_NODE);
@@ -945,7 +933,7 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
while (get_pages(sbi, F2FS_DIRTY_META)) {
sync_meta_pages(sbi, META, LONG_MAX);
if (unlikely(f2fs_cp_error(sbi)))
- return;
+ return -EIO;
}
next_free_nid(sbi, &last_nid);
@@ -1030,7 +1018,7 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
/* need to wait for end_io results */
wait_on_all_pages_writeback(sbi);
if (unlikely(f2fs_cp_error(sbi)))
- return;
+ return -EIO;
/* write out checkpoint buffer at block 0 */
update_meta_page(sbi, ckpt, start_blk++);
@@ -1058,7 +1046,7 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
wait_on_all_pages_writeback(sbi);
if (unlikely(f2fs_cp_error(sbi)))
- return;
+ return -EIO;
filemap_fdatawait_range(NODE_MAPPING(sbi), 0, LONG_MAX);
filemap_fdatawait_range(META_MAPPING(sbi), 0, LONG_MAX);
@@ -1081,22 +1069,25 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
invalidate_mapping_pages(META_MAPPING(sbi), discard_blk,
discard_blk);
- release_dirty_inode(sbi);
+ release_ino_entry(sbi);
if (unlikely(f2fs_cp_error(sbi)))
- return;
+ return -EIO;
clear_prefree_segments(sbi, cpc);
clear_sbi_flag(sbi, SBI_IS_DIRTY);
+
+ return 0;
}
/*
* We guarantee that this checkpoint procedure will not fail.
*/
-void write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
+int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
{
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
unsigned long long ckpt_ver;
+ int err = 0;
mutex_lock(&sbi->cp_mutex);
@@ -1104,14 +1095,19 @@ void write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
(cpc->reason == CP_FASTBOOT || cpc->reason == CP_SYNC ||
(cpc->reason == CP_DISCARD && !sbi->discard_blks)))
goto out;
- if (unlikely(f2fs_cp_error(sbi)))
+ if (unlikely(f2fs_cp_error(sbi))) {
+ err = -EIO;
goto out;
- if (f2fs_readonly(sbi->sb))
+ }
+ if (f2fs_readonly(sbi->sb)) {
+ err = -EROFS;
goto out;
+ }
trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "start block_ops");
- if (block_operations(sbi))
+ err = block_operations(sbi);
+ if (err)
goto out;
trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish block_ops");
@@ -1133,7 +1129,7 @@ void write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
flush_sit_entries(sbi, cpc);
/* unlock all the fs_lock[] in do_checkpoint() */
- do_checkpoint(sbi, cpc);
+ err = do_checkpoint(sbi, cpc);
unblock_operations(sbi);
stat_inc_cp_count(sbi->stat_info);
@@ -1143,10 +1139,11 @@ void write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
"checkpoint: version = %llx", ckpt_ver);
/* do checkpoint periodically */
- sbi->cp_expires = round_jiffies_up(jiffies + HZ * sbi->cp_interval);
+ f2fs_update_time(sbi, CP_TIME);
+ trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish checkpoint");
out:
mutex_unlock(&sbi->cp_mutex);
- trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish checkpoint");
+ return err;
}
void init_ino_entry_info(struct f2fs_sb_info *sbi)
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 972eab7ac071..ac9e7c6aac74 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -225,7 +225,8 @@ void set_data_blkaddr(struct dnode_of_data *dn)
/* Get physical address of data block */
addr_array = blkaddr_in_node(rn);
addr_array[ofs_in_node] = cpu_to_le32(dn->data_blkaddr);
- set_page_dirty(node_page);
+ if (set_page_dirty(node_page))
+ dn->node_changed = true;
}
int reserve_new_block(struct dnode_of_data *dn)
@@ -412,7 +413,7 @@ struct page *get_new_data_page(struct inode *inode,
struct page *page;
struct dnode_of_data dn;
int err;
-repeat:
+
page = f2fs_grab_cache_page(mapping, index, true);
if (!page) {
/*
@@ -441,12 +442,11 @@ repeat:
} else {
f2fs_put_page(page, 1);
- page = get_read_data_page(inode, index, READ_SYNC, true);
+ /* if ipage exists, blkaddr should be NEW_ADDR */
+ f2fs_bug_on(F2FS_I_SB(inode), ipage);
+ page = get_lock_data_page(inode, index, true);
if (IS_ERR(page))
- goto repeat;
-
- /* wait for read completion */
- lock_page(page);
+ return page;
}
got_it:
if (new_i_size && i_size_read(inode) <
@@ -494,14 +494,10 @@ alloc:
if (i_size_read(dn->inode) < ((loff_t)(fofs + 1) << PAGE_CACHE_SHIFT))
i_size_write(dn->inode,
((loff_t)(fofs + 1) << PAGE_CACHE_SHIFT));
-
- /* direct IO doesn't use extent cache to maximize the performance */
- f2fs_drop_largest_extent(dn->inode, fofs);
-
return 0;
}
-static void __allocate_data_blocks(struct inode *inode, loff_t offset,
+static int __allocate_data_blocks(struct inode *inode, loff_t offset,
size_t count)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
@@ -510,14 +506,15 @@ static void __allocate_data_blocks(struct inode *inode, loff_t offset,
u64 len = F2FS_BYTES_TO_BLK(count);
bool allocated;
u64 end_offset;
+ int err = 0;
while (len) {
- f2fs_balance_fs(sbi);
f2fs_lock_op(sbi);
/* When reading holes, we need its node page */
set_new_dnode(&dn, inode, NULL, NULL, 0);
- if (get_dnode_of_data(&dn, start, ALLOC_NODE))
+ err = get_dnode_of_data(&dn, start, ALLOC_NODE);
+ if (err)
goto out;
allocated = false;
@@ -526,12 +523,15 @@ static void __allocate_data_blocks(struct inode *inode, loff_t offset,
while (dn.ofs_in_node < end_offset && len) {
block_t blkaddr;
- if (unlikely(f2fs_cp_error(sbi)))
+ if (unlikely(f2fs_cp_error(sbi))) {
+ err = -EIO;
goto sync_out;
+ }
blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node);
if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR) {
- if (__allocate_data_block(&dn))
+ err = __allocate_data_block(&dn);
+ if (err)
goto sync_out;
allocated = true;
}
@@ -545,8 +545,10 @@ static void __allocate_data_blocks(struct inode *inode, loff_t offset,
f2fs_put_dnode(&dn);
f2fs_unlock_op(sbi);
+
+ f2fs_balance_fs(sbi, dn.node_changed);
}
- return;
+ return err;
sync_out:
if (allocated)
@@ -554,7 +556,8 @@ sync_out:
f2fs_put_dnode(&dn);
out:
f2fs_unlock_op(sbi);
- return;
+ f2fs_balance_fs(sbi, dn.node_changed);
+ return err;
}
/*
@@ -566,7 +569,7 @@ out:
* b. do not use extent cache for better performance
* c. give the block addresses to blockdev
*/
-static int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map,
+int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map,
int create, int flag)
{
unsigned int maxblocks = map->m_len;
@@ -577,6 +580,7 @@ static int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map,
int err = 0, ofs = 1;
struct extent_info ei;
bool allocated = false;
+ block_t blkaddr;
map->m_len = 0;
map->m_flags = 0;
@@ -592,7 +596,7 @@ static int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map,
}
if (create)
- f2fs_lock_op(F2FS_I_SB(inode));
+ f2fs_lock_op(sbi);
/* When reading holes, we need its node page */
set_new_dnode(&dn, inode, NULL, NULL, 0);
@@ -640,12 +644,21 @@ static int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map,
pgofs++;
get_next:
+ if (map->m_len >= maxblocks)
+ goto sync_out;
+
if (dn.ofs_in_node >= end_offset) {
if (allocated)
sync_inode_page(&dn);
allocated = false;
f2fs_put_dnode(&dn);
+ if (create) {
+ f2fs_unlock_op(sbi);
+ f2fs_balance_fs(sbi, dn.node_changed);
+ f2fs_lock_op(sbi);
+ }
+
set_new_dnode(&dn, inode, NULL, NULL, 0);
err = get_dnode_of_data(&dn, pgofs, mode);
if (err) {
@@ -657,52 +670,53 @@ get_next:
end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode));
}
- if (maxblocks > map->m_len) {
- block_t blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node);
+ blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node);
- if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR) {
- if (create) {
- if (unlikely(f2fs_cp_error(sbi))) {
- err = -EIO;
- goto sync_out;
- }
- err = __allocate_data_block(&dn);
- if (err)
- goto sync_out;
- allocated = true;
- map->m_flags |= F2FS_MAP_NEW;
- blkaddr = dn.data_blkaddr;
- } else {
- /*
- * we only merge preallocated unwritten blocks
- * for fiemap.
- */
- if (flag != F2FS_GET_BLOCK_FIEMAP ||
- blkaddr != NEW_ADDR)
- goto sync_out;
+ if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR) {
+ if (create) {
+ if (unlikely(f2fs_cp_error(sbi))) {
+ err = -EIO;
+ goto sync_out;
}
+ err = __allocate_data_block(&dn);
+ if (err)
+ goto sync_out;
+ allocated = true;
+ map->m_flags |= F2FS_MAP_NEW;
+ blkaddr = dn.data_blkaddr;
+ } else {
+ /*
+ * we only merge preallocated unwritten blocks
+ * for fiemap.
+ */
+ if (flag != F2FS_GET_BLOCK_FIEMAP ||
+ blkaddr != NEW_ADDR)
+ goto sync_out;
}
+ }
- /* Give more consecutive addresses for the readahead */
- if ((map->m_pblk != NEW_ADDR &&
- blkaddr == (map->m_pblk + ofs)) ||
- (map->m_pblk == NEW_ADDR &&
- blkaddr == NEW_ADDR)) {
- ofs++;
- dn.ofs_in_node++;
- pgofs++;
- map->m_len++;
- goto get_next;
- }
+ /* Give more consecutive addresses for the readahead */
+ if ((map->m_pblk != NEW_ADDR &&
+ blkaddr == (map->m_pblk + ofs)) ||
+ (map->m_pblk == NEW_ADDR &&
+ blkaddr == NEW_ADDR)) {
+ ofs++;
+ dn.ofs_in_node++;
+ pgofs++;
+ map->m_len++;
+ goto get_next;
}
+
sync_out:
if (allocated)
sync_inode_page(&dn);
put_out:
f2fs_put_dnode(&dn);
unlock_out:
- if (create)
- f2fs_unlock_op(F2FS_I_SB(inode));
+ if (create) {
+ f2fs_unlock_op(sbi);
+ f2fs_balance_fs(sbi, dn.node_changed);
+ }
out:
trace_f2fs_map_blocks(inode, map, err);
return err;
@@ -742,6 +756,10 @@ static int get_data_block_dio(struct inode *inode, sector_t iblock,
static int get_data_block_bmap(struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int create)
{
+ /* Block number less than F2FS MAX BLOCKS */
+ if (unlikely(iblock >= F2FS_I_SB(inode)->max_file_blocks))
+ return -EFBIG;
+
return __get_data_block(inode, iblock, bh_result, create,
F2FS_GET_BLOCK_BMAP);
}
@@ -761,10 +779,9 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
{
struct buffer_head map_bh;
sector_t start_blk, last_blk;
- loff_t isize = i_size_read(inode);
+ loff_t isize;
u64 logical = 0, phys = 0, size = 0;
u32 flags = 0;
- bool past_eof = false, whole_file = false;
int ret = 0;
ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC);
@@ -779,16 +796,19 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
mutex_lock(&inode->i_mutex);
- if (len >= isize) {
- whole_file = true;
- len = isize;
- }
+ isize = i_size_read(inode);
+ if (start >= isize)
+ goto out;
+
+ if (start + len > isize)
+ len = isize - start;
if (logical_to_blk(inode, len) == 0)
len = blk_to_logical(inode, 1);
start_blk = logical_to_blk(inode, start);
last_blk = logical_to_blk(inode, start + len - 1);
+
next:
memset(&map_bh, 0, sizeof(struct buffer_head));
map_bh.b_size = len;
@@ -800,59 +820,37 @@ next:
/* HOLE */
if (!buffer_mapped(&map_bh)) {
- start_blk++;
-
- if (!past_eof && blk_to_logical(inode, start_blk) >= isize)
- past_eof = 1;
-
- if (past_eof && size) {
- flags |= FIEMAP_EXTENT_LAST;
- ret = fiemap_fill_next_extent(fieinfo, logical,
- phys, size, flags);
- } else if (size) {
- ret = fiemap_fill_next_extent(fieinfo, logical,
- phys, size, flags);
- size = 0;
- }
+ /* Go through holes util pass the EOF */
+ if (blk_to_logical(inode, start_blk++) < isize)
+ goto prep_next;
+ /* Found a hole beyond isize means no more extents.
+ * Note that the premise is that filesystems don't
+ * punch holes beyond isize and keep size unchanged.
+ */
+ flags |= FIEMAP_EXTENT_LAST;
+ }
- /* if we have holes up to/past EOF then we're done */
- if (start_blk > last_blk || past_eof || ret)
- goto out;
- } else {
- if (start_blk > last_blk && !whole_file) {
- ret = fiemap_fill_next_extent(fieinfo, logical,
- phys, size, flags);
- goto out;
- }
+ if (size) {
+ if (f2fs_encrypted_inode(inode))
+ flags |= FIEMAP_EXTENT_DATA_ENCRYPTED;
- /*
- * if size != 0 then we know we already have an extent
- * to add, so add it.
- */
- if (size) {
- ret = fiemap_fill_next_extent(fieinfo, logical,
- phys, size, flags);
- if (ret)
- goto out;
- }
+ ret = fiemap_fill_next_extent(fieinfo, logical,
+ phys, size, flags);
+ }
- logical = blk_to_logical(inode, start_blk);
- phys = blk_to_logical(inode, map_bh.b_blocknr);
- size = map_bh.b_size;
- flags = 0;
- if (buffer_unwritten(&map_bh))
- flags = FIEMAP_EXTENT_UNWRITTEN;
+ if (start_blk > last_blk || ret)
+ goto out;
- start_blk += logical_to_blk(inode, size);
+ logical = blk_to_logical(inode, start_blk);
+ phys = blk_to_logical(inode, map_bh.b_blocknr);
+ size = map_bh.b_size;
+ flags = 0;
+ if (buffer_unwritten(&map_bh))
+ flags = FIEMAP_EXTENT_UNWRITTEN;
- /*
- * If we are past the EOF, then we need to make sure as
- * soon as we find a hole that the last extent we found
- * is marked with FIEMAP_EXTENT_LAST
- */
- if (!past_eof && logical + size >= isize)
- past_eof = true;
- }
+ start_blk += logical_to_blk(inode, size);
+
+prep_next:
cond_resched();
if (fatal_signal_pending(current))
ret = -EINTR;
@@ -1083,6 +1081,7 @@ int do_write_data_page(struct f2fs_io_info *fio)
*/
if (unlikely(fio->blk_addr != NEW_ADDR &&
!is_cold_data(page) &&
+ !IS_ATOMIC_WRITTEN_PAGE(page) &&
need_inplace_update(inode))) {
rewrite_data_page(fio);
set_inode_flag(F2FS_I(inode), FI_UPDATE_WRITE);
@@ -1179,10 +1178,11 @@ out:
if (err)
ClearPageUptodate(page);
unlock_page(page);
- if (need_balance_fs)
- f2fs_balance_fs(sbi);
- if (wbc->for_reclaim)
+ f2fs_balance_fs(sbi, need_balance_fs);
+ if (wbc->for_reclaim || unlikely(f2fs_cp_error(sbi))) {
f2fs_submit_merged_bio(sbi, DATA, WRITE);
+ remove_dirty_inode(inode);
+ }
return 0;
redirty_out:
@@ -1354,6 +1354,10 @@ static int f2fs_write_data_pages(struct address_space *mapping,
available_free_memory(sbi, DIRTY_DENTS))
goto skip_write;
+ /* skip writing during file defragment */
+ if (is_inode_flag_set(F2FS_I(inode), FI_DO_DEFRAG))
+ goto skip_write;
+
/* during POR, we don't need to trigger writepage at all. */
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
goto skip_write;
@@ -1369,7 +1373,7 @@ static int f2fs_write_data_pages(struct address_space *mapping,
if (locked)
mutex_unlock(&sbi->writepages);
- remove_dirty_dir_inode(inode);
+ remove_dirty_inode(inode);
wbc->nr_to_write = max((long)0, wbc->nr_to_write - diff);
return ret;
@@ -1382,13 +1386,85 @@ skip_write:
static void f2fs_write_failed(struct address_space *mapping, loff_t to)
{
struct inode *inode = mapping->host;
+ loff_t i_size = i_size_read(inode);
- if (to > inode->i_size) {
- truncate_pagecache(inode, inode->i_size);
- truncate_blocks(inode, inode->i_size, true);
+ if (to > i_size) {
+ truncate_pagecache(inode, i_size);
+ truncate_blocks(inode, i_size, true);
}
}
+static int prepare_write_begin(struct f2fs_sb_info *sbi,
+ struct page *page, loff_t pos, unsigned len,
+ block_t *blk_addr, bool *node_changed)
+{
+ struct inode *inode = page->mapping->host;
+ pgoff_t index = page->index;
+ struct dnode_of_data dn;
+ struct page *ipage;
+ bool locked = false;
+ struct extent_info ei;
+ int err = 0;
+
+ if (f2fs_has_inline_data(inode) ||
+ (pos & PAGE_CACHE_MASK) >= i_size_read(inode)) {
+ f2fs_lock_op(sbi);
+ locked = true;
+ }
+restart:
+ /* check inline_data */
+ ipage = get_node_page(sbi, inode->i_ino);
+ if (IS_ERR(ipage)) {
+ err = PTR_ERR(ipage);
+ goto unlock_out;
+ }
+
+ set_new_dnode(&dn, inode, ipage, ipage, 0);
+
+ if (f2fs_has_inline_data(inode)) {
+ if (pos + len <= MAX_INLINE_DATA) {
+ read_inline_data(page, ipage);
+ set_inode_flag(F2FS_I(inode), FI_DATA_EXIST);
+ sync_inode_page(&dn);
+ } else {
+ err = f2fs_convert_inline_page(&dn, page);
+ if (err)
+ goto out;
+ if (dn.data_blkaddr == NULL_ADDR)
+ err = f2fs_get_block(&dn, index);
+ }
+ } else if (locked) {
+ err = f2fs_get_block(&dn, index);
+ } else {
+ if (f2fs_lookup_extent_cache(inode, index, &ei)) {
+ dn.data_blkaddr = ei.blk + index - ei.fofs;
+ } else {
+ bool restart = false;
+
+ /* hole case */
+ err = get_dnode_of_data(&dn, index, LOOKUP_NODE);
+ if (err || (!err && dn.data_blkaddr == NULL_ADDR))
+ restart = true;
+ if (restart) {
+ f2fs_put_dnode(&dn);
+ f2fs_lock_op(sbi);
+ locked = true;
+ goto restart;
+ }
+ }
+ }
+
+ /* convert_inline_page can make node_changed */
+ *blk_addr = dn.data_blkaddr;
+ *node_changed = dn.node_changed;
+out:
+ f2fs_put_dnode(&dn);
+unlock_out:
+ if (locked)
+ f2fs_unlock_op(sbi);
+ return err;
+}
+
static int f2fs_write_begin(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned flags,
struct page **pagep, void **fsdata)
@@ -1396,15 +1472,13 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
struct inode *inode = mapping->host;
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct page *page = NULL;
- struct page *ipage;
pgoff_t index = ((unsigned long long) pos) >> PAGE_CACHE_SHIFT;
- struct dnode_of_data dn;
+ bool need_balance = false;
+ block_t blkaddr = NULL_ADDR;
int err = 0;
trace_f2fs_write_begin(inode, pos, len, flags);
- f2fs_balance_fs(sbi);
-
/*
* We should check this at this moment to avoid deadlock on inode page
* and #0 page. The locking rule for inline_data conversion should be:
@@ -1424,41 +1498,27 @@ repeat:
*pagep = page;
- f2fs_lock_op(sbi);
-
- /* check inline_data */
- ipage = get_node_page(sbi, inode->i_ino);
- if (IS_ERR(ipage)) {
- err = PTR_ERR(ipage);
- goto unlock_fail;
- }
-
- set_new_dnode(&dn, inode, ipage, ipage, 0);
+ err = prepare_write_begin(sbi, page, pos, len,
+ &blkaddr, &need_balance);
+ if (err)
+ goto fail;
- if (f2fs_has_inline_data(inode)) {
- if (pos + len <= MAX_INLINE_DATA) {
- read_inline_data(page, ipage);
- set_inode_flag(F2FS_I(inode), FI_DATA_EXIST);
- sync_inode_page(&dn);
- goto put_next;
+ if (need_balance && has_not_enough_free_secs(sbi, 0)) {
+ unlock_page(page);
+ f2fs_balance_fs(sbi, true);
+ lock_page(page);
+ if (page->mapping != mapping) {
+ /* The page got truncated from under us */
+ f2fs_put_page(page, 1);
+ goto repeat;
}
- err = f2fs_convert_inline_page(&dn, page);
- if (err)
- goto put_fail;
}
- err = f2fs_get_block(&dn, index);
- if (err)
- goto put_fail;
-put_next:
- f2fs_put_dnode(&dn);
- f2fs_unlock_op(sbi);
-
f2fs_wait_on_page_writeback(page, DATA);
/* wait for GCed encrypted page writeback */
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
- f2fs_wait_on_encrypted_page_writeback(sbi, dn.data_blkaddr);
+ f2fs_wait_on_encrypted_page_writeback(sbi, blkaddr);
if (len == PAGE_CACHE_SIZE)
goto out_update;
@@ -1474,14 +1534,14 @@ put_next:
goto out_update;
}
- if (dn.data_blkaddr == NEW_ADDR) {
+ if (blkaddr == NEW_ADDR) {
zero_user_segment(page, 0, PAGE_CACHE_SIZE);
} else {
struct f2fs_io_info fio = {
.sbi = sbi,
.type = DATA,
.rw = READ_SYNC,
- .blk_addr = dn.data_blkaddr,
+ .blk_addr = blkaddr,
.page = page,
.encrypted_page = NULL,
};
@@ -1512,10 +1572,6 @@ out_clear:
clear_cold_data(page);
return 0;
-put_fail:
- f2fs_put_dnode(&dn);
-unlock_fail:
- f2fs_unlock_op(sbi);
fail:
f2fs_put_page(page, 1);
f2fs_write_failed(mapping, pos + len);
@@ -1540,6 +1596,7 @@ static int f2fs_write_end(struct file *file,
}
f2fs_put_page(page, 1);
+ f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
return copied;
}
@@ -1567,11 +1624,9 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
int err;
/* we don't need to use inline_data strictly */
- if (f2fs_has_inline_data(inode)) {
- err = f2fs_convert_inline_inode(inode);
- if (err)
- return err;
- }
+ err = f2fs_convert_inline_inode(inode);
+ if (err)
+ return err;
if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
return 0;
@@ -1583,11 +1638,9 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
trace_f2fs_direct_IO_enter(inode, offset, count, iov_iter_rw(iter));
if (iov_iter_rw(iter) == WRITE) {
- __allocate_data_blocks(inode, offset, count);
- if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) {
- err = -EIO;
+ err = __allocate_data_blocks(inode, offset, count);
+ if (err)
goto out;
- }
}
err = blockdev_direct_IO(iocb, inode, iter, offset, get_data_block_dio);
diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c
index 478e5d54154f..4fb6ef88a34f 100644
--- a/fs/f2fs/debug.c
+++ b/fs/f2fs/debug.c
@@ -38,12 +38,15 @@ static void update_general_status(struct f2fs_sb_info *sbi)
si->hit_rbtree = atomic64_read(&sbi->read_hit_rbtree);
si->hit_total = si->hit_largest + si->hit_cached + si->hit_rbtree;
si->total_ext = atomic64_read(&sbi->total_hit_ext);
- si->ext_tree = sbi->total_ext_tree;
+ si->ext_tree = atomic_read(&sbi->total_ext_tree);
+ si->zombie_tree = atomic_read(&sbi->total_zombie_tree);
si->ext_node = atomic_read(&sbi->total_ext_node);
si->ndirty_node = get_pages(sbi, F2FS_DIRTY_NODES);
si->ndirty_dent = get_pages(sbi, F2FS_DIRTY_DENTS);
- si->ndirty_dirs = sbi->n_dirty_dirs;
si->ndirty_meta = get_pages(sbi, F2FS_DIRTY_META);
+ si->ndirty_data = get_pages(sbi, F2FS_DIRTY_DATA);
+ si->ndirty_dirs = sbi->ndirty_inode[DIR_INODE];
+ si->ndirty_files = sbi->ndirty_inode[FILE_INODE];
si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES);
si->wb_pages = get_pages(sbi, F2FS_WRITEBACK);
si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg;
@@ -105,7 +108,7 @@ static void update_sit_info(struct f2fs_sb_info *sbi)
bimodal = 0;
total_vblocks = 0;
- blks_per_sec = sbi->segs_per_sec * (1 << sbi->log_blocks_per_seg);
+ blks_per_sec = sbi->segs_per_sec * sbi->blocks_per_seg;
hblks_per_sec = blks_per_sec / 2;
for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) {
vblocks = get_valid_blocks(sbi, segno, sbi->segs_per_sec);
@@ -189,10 +192,10 @@ get_cache:
si->cache_mem += NM_I(sbi)->dirty_nat_cnt *
sizeof(struct nat_entry_set);
si->cache_mem += si->inmem_pages * sizeof(struct inmem_pages);
- si->cache_mem += sbi->n_dirty_dirs * sizeof(struct inode_entry);
for (i = 0; i <= UPDATE_INO; i++)
si->cache_mem += sbi->im[i].ino_num * sizeof(struct ino_entry);
- si->cache_mem += sbi->total_ext_tree * sizeof(struct extent_tree);
+ si->cache_mem += atomic_read(&sbi->total_ext_tree) *
+ sizeof(struct extent_tree);
si->cache_mem += atomic_read(&sbi->total_ext_node) *
sizeof(struct extent_node);
@@ -211,12 +214,10 @@ static int stat_show(struct seq_file *s, void *v)
mutex_lock(&f2fs_stat_mutex);
list_for_each_entry(si, &f2fs_stat_list, stat_list) {
- char devname[BDEVNAME_SIZE];
-
update_general_status(si->sbi);
- seq_printf(s, "\n=====[ partition info(%s). #%d ]=====\n",
- bdevname(si->sbi->sb->s_bdev, devname), i++);
+ seq_printf(s, "\n=====[ partition info(%pg). #%d ]=====\n",
+ si->sbi->sb->s_bdev, i++);
seq_printf(s, "[SB: 1] [CP: 2] [SIT: %d] [NAT: %d] ",
si->sit_area_segs, si->nat_area_segs);
seq_printf(s, "[SSA: %d] [MAIN: %d",
@@ -269,7 +270,8 @@ static int stat_show(struct seq_file *s, void *v)
si->dirty_count);
seq_printf(s, " - Prefree: %d\n - Free: %d (%d)\n\n",
si->prefree_count, si->free_segs, si->free_secs);
- seq_printf(s, "CP calls: %d\n", si->cp_count);
+ seq_printf(s, "CP calls: %d (BG: %d)\n",
+ si->cp_count, si->bg_cp_count);
seq_printf(s, "GC calls: %d (BG: %d)\n",
si->call_count, si->bg_gc);
seq_printf(s, " - data segments : %d (%d)\n",
@@ -290,8 +292,8 @@ static int stat_show(struct seq_file *s, void *v)
!si->total_ext ? 0 :
div64_u64(si->hit_total * 100, si->total_ext),
si->hit_total, si->total_ext);
- seq_printf(s, " - Inner Struct Count: tree: %d, node: %d\n",
- si->ext_tree, si->ext_node);
+ seq_printf(s, " - Inner Struct Count: tree: %d(%d), node: %d\n",
+ si->ext_tree, si->zombie_tree, si->ext_node);
seq_puts(s, "\nBalancing F2FS Async:\n");
seq_printf(s, " - inmem: %4d, wb: %4d\n",
si->inmem_pages, si->wb_pages);
@@ -299,6 +301,8 @@ static int stat_show(struct seq_file *s, void *v)
si->ndirty_node, si->node_pages);
seq_printf(s, " - dents: %4d in dirs:%4d\n",
si->ndirty_dent, si->ndirty_dirs);
+ seq_printf(s, " - datas: %4d in files:%4d\n",
+ si->ndirty_data, si->ndirty_files);
seq_printf(s, " - meta: %4d in %4d\n",
si->ndirty_meta, si->meta_pages);
seq_printf(s, " - NATs: %9d/%9d\n - SITs: %9d/%9d\n",
@@ -406,20 +410,23 @@ void f2fs_destroy_stats(struct f2fs_sb_info *sbi)
kfree(si);
}
-void __init f2fs_create_root_stats(void)
+int __init f2fs_create_root_stats(void)
{
struct dentry *file;
f2fs_debugfs_root = debugfs_create_dir("f2fs", NULL);
if (!f2fs_debugfs_root)
- return;
+ return -ENOMEM;
file = debugfs_create_file("status", S_IRUGO, f2fs_debugfs_root,
NULL, &stat_fops);
if (!file) {
debugfs_remove(f2fs_debugfs_root);
f2fs_debugfs_root = NULL;
+ return -ENOMEM;
}
+
+ return 0;
}
void f2fs_destroy_root_stats(void)
diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index 7c1678ba8f92..faa7495e2d7e 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -172,8 +172,6 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
namehash = f2fs_dentry_hash(&name);
- f2fs_bug_on(F2FS_I_SB(dir), level > MAX_DIR_HASH_DEPTH);
-
nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level);
nblock = bucket_blocks(level);
@@ -238,6 +236,14 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
goto out;
max_depth = F2FS_I(dir)->i_current_depth;
+ if (unlikely(max_depth > MAX_DIR_HASH_DEPTH)) {
+ f2fs_msg(F2FS_I_SB(dir)->sb, KERN_WARNING,
+ "Corrupted max_depth of %lu: %u",
+ dir->i_ino, max_depth);
+ max_depth = MAX_DIR_HASH_DEPTH;
+ F2FS_I(dir)->i_current_depth = max_depth;
+ mark_inode_dirty(dir);
+ }
for (level = 0; level < max_depth; level++) {
de = find_in_level(dir, level, &fname, res_page);
@@ -444,7 +450,7 @@ error:
/* once the failed inode becomes a bad inode, i_mode is S_IFREG */
truncate_inode_pages(&inode->i_data, 0);
truncate_blocks(inode, 0, false);
- remove_dirty_dir_inode(inode);
+ remove_dirty_inode(inode);
remove_inode_page(inode);
return ERR_PTR(err);
}
@@ -630,6 +636,7 @@ fail:
f2fs_put_page(dentry_page, 1);
out:
f2fs_fname_free_filename(&fname);
+ f2fs_update_time(F2FS_I_SB(dir), REQ_TIME);
return err;
}
@@ -651,6 +658,7 @@ int f2fs_do_tmpfile(struct inode *inode, struct inode *dir)
clear_inode_flag(F2FS_I(inode), FI_NEW_INODE);
fail:
up_write(&F2FS_I(inode)->i_sem);
+ f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
return err;
}
@@ -695,6 +703,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len));
int i;
+ f2fs_update_time(F2FS_I_SB(dir), REQ_TIME);
+
if (f2fs_has_inline_dentry(dir))
return f2fs_delete_inline_entry(dentry, page, dir, inode);
@@ -855,25 +865,27 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx)
for (; n < npages; n++) {
dentry_page = get_lock_data_page(inode, n, false);
- if (IS_ERR(dentry_page))
- continue;
+ if (IS_ERR(dentry_page)) {
+ err = PTR_ERR(dentry_page);
+ if (err == -ENOENT)
+ continue;
+ else
+ goto out;
+ }
dentry_blk = kmap(dentry_page);
make_dentry_ptr(inode, &d, (void *)dentry_blk, 1);
- if (f2fs_fill_dentries(ctx, &d, n * NR_DENTRY_IN_BLOCK, &fstr))
- goto stop;
+ if (f2fs_fill_dentries(ctx, &d, n * NR_DENTRY_IN_BLOCK, &fstr)) {
+ kunmap(dentry_page);
+ f2fs_put_page(dentry_page, 1);
+ break;
+ }
ctx->pos = (n + 1) * NR_DENTRY_IN_BLOCK;
kunmap(dentry_page);
f2fs_put_page(dentry_page, 1);
- dentry_page = NULL;
- }
-stop:
- if (dentry_page && !IS_ERR(dentry_page)) {
- kunmap(dentry_page);
- f2fs_put_page(dentry_page, 1);
}
out:
f2fs_fname_crypto_free_buffer(&fstr);
diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
index 7ddba812e11b..ccd5c636d3fe 100644
--- a/fs/f2fs/extent_cache.c
+++ b/fs/f2fs/extent_cache.c
@@ -36,7 +36,7 @@ static struct extent_node *__attach_extent_node(struct f2fs_sb_info *sbi,
rb_link_node(&en->rb_node, parent, p);
rb_insert_color(&en->rb_node, &et->root);
- et->count++;
+ atomic_inc(&et->node_cnt);
atomic_inc(&sbi->total_ext_node);
return en;
}
@@ -45,7 +45,7 @@ static void __detach_extent_node(struct f2fs_sb_info *sbi,
struct extent_tree *et, struct extent_node *en)
{
rb_erase(&en->rb_node, &et->root);
- et->count--;
+ atomic_dec(&et->node_cnt);
atomic_dec(&sbi->total_ext_node);
if (et->cached_en == en)
@@ -68,11 +68,13 @@ static struct extent_tree *__grab_extent_tree(struct inode *inode)
et->root = RB_ROOT;
et->cached_en = NULL;
rwlock_init(&et->lock);
- atomic_set(&et->refcount, 0);
- et->count = 0;
- sbi->total_ext_tree++;
+ INIT_LIST_HEAD(&et->list);
+ atomic_set(&et->node_cnt, 0);
+ atomic_inc(&sbi->total_ext_tree);
+ } else {
+ atomic_dec(&sbi->total_zombie_tree);
+ list_del_init(&et->list);
}
- atomic_inc(&et->refcount);
up_write(&sbi->extent_tree_lock);
/* never died until evict_inode */
@@ -131,7 +133,7 @@ static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi,
{
struct rb_node *node, *next;
struct extent_node *en;
- unsigned int count = et->count;
+ unsigned int count = atomic_read(&et->node_cnt);
node = rb_first(&et->root);
while (node) {
@@ -152,7 +154,7 @@ static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi,
node = next;
}
- return count - et->count;
+ return count - atomic_read(&et->node_cnt);
}
static void __drop_largest_extent(struct inode *inode,
@@ -164,34 +166,33 @@ static void __drop_largest_extent(struct inode *inode,
largest->len = 0;
}
-void f2fs_drop_largest_extent(struct inode *inode, pgoff_t fofs)
-{
- if (!f2fs_may_extent_tree(inode))
- return;
-
- __drop_largest_extent(inode, fofs, 1);
-}
-
-void f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext)
+/* return true, if inode page is changed */
+bool f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct extent_tree *et;
struct extent_node *en;
struct extent_info ei;
- if (!f2fs_may_extent_tree(inode))
- return;
+ if (!f2fs_may_extent_tree(inode)) {
+ /* drop largest extent */
+ if (i_ext && i_ext->len) {
+ i_ext->len = 0;
+ return true;
+ }
+ return false;
+ }
et = __grab_extent_tree(inode);
- if (!i_ext || le32_to_cpu(i_ext->len) < F2FS_MIN_EXTENT_LEN)
- return;
+ if (!i_ext || !i_ext->len)
+ return false;
set_extent_info(&ei, le32_to_cpu(i_ext->fofs),
le32_to_cpu(i_ext->blk), le32_to_cpu(i_ext->len));
write_lock(&et->lock);
- if (et->count)
+ if (atomic_read(&et->node_cnt))
goto out;
en = __init_extent_tree(sbi, et, &ei);
@@ -202,6 +203,7 @@ void f2fs_init_extent_tree(struct inode *inode, struct f2fs_extent *i_ext)
}
out:
write_unlock(&et->lock);
+ return false;
}
static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs,
@@ -549,45 +551,44 @@ static unsigned int f2fs_update_extent_tree_range(struct inode *inode,
unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink)
{
struct extent_tree *treevec[EXT_TREE_VEC_SIZE];
+ struct extent_tree *et, *next;
struct extent_node *en, *tmp;
unsigned long ino = F2FS_ROOT_INO(sbi);
- struct radix_tree_root *root = &sbi->extent_tree_root;
unsigned int found;
unsigned int node_cnt = 0, tree_cnt = 0;
int remained;
+ bool do_free = false;
if (!test_opt(sbi, EXTENT_CACHE))
return 0;
+ if (!atomic_read(&sbi->total_zombie_tree))
+ goto free_node;
+
if (!down_write_trylock(&sbi->extent_tree_lock))
goto out;
/* 1. remove unreferenced extent tree */
- while ((found = radix_tree_gang_lookup(root,
- (void **)treevec, ino, EXT_TREE_VEC_SIZE))) {
- unsigned i;
-
- ino = treevec[found - 1]->ino + 1;
- for (i = 0; i < found; i++) {
- struct extent_tree *et = treevec[i];
-
- if (!atomic_read(&et->refcount)) {
- write_lock(&et->lock);
- node_cnt += __free_extent_tree(sbi, et, true);
- write_unlock(&et->lock);
+ list_for_each_entry_safe(et, next, &sbi->zombie_list, list) {
+ if (atomic_read(&et->node_cnt)) {
+ write_lock(&et->lock);
+ node_cnt += __free_extent_tree(sbi, et, true);
+ write_unlock(&et->lock);
+ }
- radix_tree_delete(root, et->ino);
- kmem_cache_free(extent_tree_slab, et);
- sbi->total_ext_tree--;
- tree_cnt++;
+ list_del_init(&et->list);
+ radix_tree_delete(&sbi->extent_tree_root, et->ino);
+ kmem_cache_free(extent_tree_slab, et);
+ atomic_dec(&sbi->total_ext_tree);
+ atomic_dec(&sbi->total_zombie_tree);
+ tree_cnt++;
- if (node_cnt + tree_cnt >= nr_shrink)
- goto unlock_out;
- }
- }
+ if (node_cnt + tree_cnt >= nr_shrink)
+ goto unlock_out;
}
up_write(&sbi->extent_tree_lock);
+free_node:
/* 2. remove LRU extent entries */
if (!down_write_trylock(&sbi->extent_tree_lock))
goto out;
@@ -599,15 +600,19 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink)
if (!remained--)
break;
list_del_init(&en->list);
+ do_free = true;
}
spin_unlock(&sbi->extent_lock);
+ if (do_free == false)
+ goto unlock_out;
+
/*
* reset ino for searching victims from beginning of global extent tree.
*/
ino = F2FS_ROOT_INO(sbi);
- while ((found = radix_tree_gang_lookup(root,
+ while ((found = radix_tree_gang_lookup(&sbi->extent_tree_root,
(void **)treevec, ino, EXT_TREE_VEC_SIZE))) {
unsigned i;
@@ -615,9 +620,13 @@ unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink)
for (i = 0; i < found; i++) {
struct extent_tree *et = treevec[i];
- write_lock(&et->lock);
- node_cnt += __free_extent_tree(sbi, et, false);
- write_unlock(&et->lock);
+ if (!atomic_read(&et->node_cnt))
+ continue;
+
+ if (write_trylock(&et->lock)) {
+ node_cnt += __free_extent_tree(sbi, et, false);
+ write_unlock(&et->lock);
+ }
if (node_cnt + tree_cnt >= nr_shrink)
goto unlock_out;
@@ -637,7 +646,7 @@ unsigned int f2fs_destroy_extent_node(struct inode *inode)
struct extent_tree *et = F2FS_I(inode)->extent_tree;
unsigned int node_cnt = 0;
- if (!et)
+ if (!et || !atomic_read(&et->node_cnt))
return 0;
write_lock(&et->lock);
@@ -656,8 +665,12 @@ void f2fs_destroy_extent_tree(struct inode *inode)
if (!et)
return;
- if (inode->i_nlink && !is_bad_inode(inode) && et->count) {
- atomic_dec(&et->refcount);
+ if (inode->i_nlink && !is_bad_inode(inode) &&
+ atomic_read(&et->node_cnt)) {
+ down_write(&sbi->extent_tree_lock);
+ list_add_tail(&et->list, &sbi->zombie_list);
+ atomic_inc(&sbi->total_zombie_tree);
+ up_write(&sbi->extent_tree_lock);
return;
}
@@ -666,11 +679,10 @@ void f2fs_destroy_extent_tree(struct inode *inode)
/* delete extent tree entry in radix tree */
down_write(&sbi->extent_tree_lock);
- atomic_dec(&et->refcount);
- f2fs_bug_on(sbi, atomic_read(&et->refcount) || et->count);
+ f2fs_bug_on(sbi, atomic_read(&et->node_cnt));
radix_tree_delete(&sbi->extent_tree_root, inode->i_ino);
kmem_cache_free(extent_tree_slab, et);
- sbi->total_ext_tree--;
+ atomic_dec(&sbi->total_ext_tree);
up_write(&sbi->extent_tree_lock);
F2FS_I(inode)->extent_tree = NULL;
@@ -722,7 +734,9 @@ void init_extent_cache_info(struct f2fs_sb_info *sbi)
init_rwsem(&sbi->extent_tree_lock);
INIT_LIST_HEAD(&sbi->extent_list);
spin_lock_init(&sbi->extent_lock);
- sbi->total_ext_tree = 0;
+ atomic_set(&sbi->total_ext_tree, 0);
+ INIT_LIST_HEAD(&sbi->zombie_list);
+ atomic_set(&sbi->total_zombie_tree, 0);
atomic_set(&sbi->total_ext_node, 0);
}
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 9db5500d63d9..ff79054c6cf6 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -21,6 +21,7 @@
#include <linux/sched.h>
#include <linux/vmalloc.h>
#include <linux/bio.h>
+#include <linux/blkdev.h>
#ifdef CONFIG_F2FS_CHECK_FS
#define f2fs_bug_on(sbi, condition) BUG_ON(condition)
@@ -54,6 +55,7 @@
#define F2FS_MOUNT_FASTBOOT 0x00001000
#define F2FS_MOUNT_EXTENT_CACHE 0x00002000
#define F2FS_MOUNT_FORCE_FG_GC 0x00004000
+#define F2FS_MOUNT_DATA_FLUSH 0x00008000
#define clear_opt(sbi, option) (sbi->mount_opt.opt &= ~F2FS_MOUNT_##option)
#define set_opt(sbi, option) (sbi->mount_opt.opt |= F2FS_MOUNT_##option)
@@ -125,6 +127,7 @@ enum {
#define BATCHED_TRIM_BLOCKS(sbi) \
(BATCHED_TRIM_SEGMENTS(sbi) << (sbi)->log_blocks_per_seg)
#define DEF_CP_INTERVAL 60 /* 60 secs */
+#define DEF_IDLE_INTERVAL 120 /* 2 mins */
struct cp_control {
int reason;
@@ -158,13 +161,7 @@ struct ino_entry {
nid_t ino; /* inode number */
};
-/*
- * for the list of directory inodes or gc inodes.
- * NOTE: there are two slab users for this structure, if we add/modify/delete
- * fields in structure for one of slab users, it may affect fields or size of
- * other one, in this condition, it's better to split both of slab and related
- * data structure.
- */
+/* for the list of inodes to be GCed */
struct inode_entry {
struct list_head list; /* list head */
struct inode *inode; /* vfs inode pointer */
@@ -234,6 +231,7 @@ static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size,
#define F2FS_IOC_ABORT_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 5)
#define F2FS_IOC_GARBAGE_COLLECT _IO(F2FS_IOCTL_MAGIC, 6)
#define F2FS_IOC_WRITE_CHECKPOINT _IO(F2FS_IOCTL_MAGIC, 7)
+#define F2FS_IOC_DEFRAGMENT _IO(F2FS_IOCTL_MAGIC, 8)
#define F2FS_IOC_SET_ENCRYPTION_POLICY \
_IOR('f', 19, struct f2fs_encryption_policy)
@@ -256,10 +254,16 @@ static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size,
/*
* ioctl commands in 32 bit emulation
*/
-#define F2FS_IOC32_GETFLAGS FS_IOC32_GETFLAGS
-#define F2FS_IOC32_SETFLAGS FS_IOC32_SETFLAGS
+#define F2FS_IOC32_GETFLAGS FS_IOC32_GETFLAGS
+#define F2FS_IOC32_SETFLAGS FS_IOC32_SETFLAGS
+#define F2FS_IOC32_GETVERSION FS_IOC32_GETVERSION
#endif
+struct f2fs_defragment {
+ u64 start;
+ u64 len;
+};
+
/*
* For INODE and NODE manager
*/
@@ -357,9 +361,9 @@ struct extent_tree {
struct rb_root root; /* root of extent info rb-tree */
struct extent_node *cached_en; /* recently accessed extent node */
struct extent_info largest; /* largested extent info */
+ struct list_head list; /* to be used by sbi->zombie_list */
rwlock_t lock; /* protect extent info rb-tree */
- atomic_t refcount; /* reference count of rb-tree */
- unsigned int count; /* # of extent node in rb-tree*/
+ atomic_t node_cnt; /* # of extent node in rb-tree*/
};
/*
@@ -434,8 +438,8 @@ struct f2fs_inode_info {
unsigned int clevel; /* maximum level of given file name */
nid_t i_xattr_nid; /* node id that contains xattrs */
unsigned long long xattr_ver; /* cp version of xattr modification */
- struct inode_entry *dirty_dir; /* the pointer of dirty dir */
+ struct list_head dirty_list; /* linked in global dirty list */
struct list_head inmem_pages; /* inmemory pages managed by f2fs */
struct mutex inmem_lock; /* lock for inmemory pages */
@@ -544,6 +548,7 @@ struct dnode_of_data {
nid_t nid; /* node id of the direct node block */
unsigned int ofs_in_node; /* data offset in the node page */
bool inode_page_locked; /* inode page is locked or not */
+ bool node_changed; /* is node block changed */
block_t data_blkaddr; /* block address of the node block */
};
@@ -647,6 +652,7 @@ struct f2fs_sm_info {
enum count_type {
F2FS_WRITEBACK,
F2FS_DIRTY_DENTS,
+ F2FS_DIRTY_DATA,
F2FS_DIRTY_NODES,
F2FS_DIRTY_META,
F2FS_INMEM_PAGES,
@@ -695,6 +701,12 @@ struct f2fs_bio_info {
struct rw_semaphore io_rwsem; /* blocking op for bio */
};
+enum inode_type {
+ DIR_INODE, /* for dirty dir inode */
+ FILE_INODE, /* for dirty regular/symlink inode */
+ NR_INODE_TYPE,
+};
+
/* for inner inode cache management */
struct inode_management {
struct radix_tree_root ino_root; /* ino entry array */
@@ -711,11 +723,17 @@ enum {
SBI_POR_DOING, /* recovery is doing or not */
};
+enum {
+ CP_TIME,
+ REQ_TIME,
+ MAX_TIME,
+};
+
struct f2fs_sb_info {
struct super_block *sb; /* pointer to VFS super block */
struct proc_dir_entry *s_proc; /* proc entry */
- struct buffer_head *raw_super_buf; /* buffer head of raw sb */
struct f2fs_super_block *raw_super; /* raw super block pointer */
+ int valid_super_block; /* valid super block no */
int s_flag; /* flags for sbi */
/* for node-related operations */
@@ -737,23 +755,26 @@ struct f2fs_sb_info {
struct rw_semaphore node_write; /* locking node writes */
struct mutex writepages; /* mutex for writepages() */
wait_queue_head_t cp_wait;
- long cp_expires, cp_interval; /* next expected periodic cp */
+ unsigned long last_time[MAX_TIME]; /* to store time in jiffies */
+ long interval_time[MAX_TIME]; /* to store thresholds */
struct inode_management im[MAX_INO_ENTRY]; /* manage inode cache */
/* for orphan inode, use 0'th array */
unsigned int max_orphans; /* max orphan inodes */
- /* for directory inode management */
- struct list_head dir_inode_list; /* dir inode list */
- spinlock_t dir_inode_lock; /* for dir inode list lock */
+ /* for inode management */
+ struct list_head inode_list[NR_INODE_TYPE]; /* dirty inode list */
+ spinlock_t inode_lock[NR_INODE_TYPE]; /* for dirty inode list lock */
/* for extent tree cache */
struct radix_tree_root extent_tree_root;/* cache extent cache entries */
struct rw_semaphore extent_tree_lock; /* locking extent radix tree */
struct list_head extent_list; /* lru list for shrinker */
spinlock_t extent_lock; /* locking extent lru list */
- int total_ext_tree; /* extent tree count */
+ atomic_t total_ext_tree; /* extent tree count */
+ struct list_head zombie_list; /* extent zombie tree list */
+ atomic_t total_zombie_tree; /* extent zombie tree count */
atomic_t total_ext_node; /* extent info count */
/* basic filesystem units */
@@ -771,6 +792,7 @@ struct f2fs_sb_info {
unsigned int total_node_count; /* total node block count */
unsigned int total_valid_node_count; /* valid node block count */
unsigned int total_valid_inode_count; /* valid inode count */
+ loff_t max_file_blocks; /* max block index of file */
int active_logs; /* # of active logs */
int dir_level; /* directory level */
@@ -809,7 +831,7 @@ struct f2fs_sb_info {
atomic_t inline_inode; /* # of inline_data inodes */
atomic_t inline_dir; /* # of inline_dentry inodes */
int bg_gc; /* background gc calls */
- unsigned int n_dirty_dirs; /* # of dir inodes */
+ unsigned int ndirty_inode[NR_INODE_TYPE]; /* # of dirty inodes */
#endif
unsigned int last_victim[2]; /* last victim segment # */
spinlock_t stat_lock; /* lock for stat operations */
@@ -824,6 +846,31 @@ struct f2fs_sb_info {
unsigned int shrinker_run_no;
};
+static inline void f2fs_update_time(struct f2fs_sb_info *sbi, int type)
+{
+ sbi->last_time[type] = jiffies;
+}
+
+static inline bool f2fs_time_over(struct f2fs_sb_info *sbi, int type)
+{
+ struct timespec ts = {sbi->interval_time[type], 0};
+ unsigned long interval = timespec_to_jiffies(&ts);
+
+ return time_after(jiffies, sbi->last_time[type] + interval);
+}
+
+static inline bool is_idle(struct f2fs_sb_info *sbi)
+{
+ struct block_device *bdev = sbi->sb->s_bdev;
+ struct request_queue *q = bdev_get_queue(bdev);
+ struct request_list *rl = &q->root_rl;
+
+ if (rl->count[BLK_RW_SYNC] || rl->count[BLK_RW_ASYNC])
+ return 0;
+
+ return f2fs_time_over(sbi, REQ_TIME);
+}
+
/*
* Inline functions
*/
@@ -1059,8 +1106,8 @@ static inline void inc_page_count(struct f2fs_sb_info *sbi, int count_type)
static inline void inode_inc_dirty_pages(struct inode *inode)
{
atomic_inc(&F2FS_I(inode)->dirty_pages);
- if (S_ISDIR(inode->i_mode))
- inc_page_count(F2FS_I_SB(inode), F2FS_DIRTY_DENTS);
+ inc_page_count(F2FS_I_SB(inode), S_ISDIR(inode->i_mode) ?
+ F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA);
}
static inline void dec_page_count(struct f2fs_sb_info *sbi, int count_type)
@@ -1075,9 +1122,8 @@ static inline void inode_dec_dirty_pages(struct inode *inode)
return;
atomic_dec(&F2FS_I(inode)->dirty_pages);
-
- if (S_ISDIR(inode->i_mode))
- dec_page_count(F2FS_I_SB(inode), F2FS_DIRTY_DENTS);
+ dec_page_count(F2FS_I_SB(inode), S_ISDIR(inode->i_mode) ?
+ F2FS_DIRTY_DENTS : F2FS_DIRTY_DATA);
}
static inline int get_pages(struct f2fs_sb_info *sbi, int count_type)
@@ -1092,8 +1138,7 @@ static inline int get_dirty_pages(struct inode *inode)
static inline int get_blocktype_secs(struct f2fs_sb_info *sbi, int block_type)
{
- unsigned int pages_per_sec = sbi->segs_per_sec *
- (1 << sbi->log_blocks_per_seg);
+ unsigned int pages_per_sec = sbi->segs_per_sec * sbi->blocks_per_seg;
return ((get_pages(sbi, block_type) + pages_per_sec - 1)
>> sbi->log_blocks_per_seg) / sbi->segs_per_sec;
}
@@ -1416,6 +1461,8 @@ enum {
FI_DROP_CACHE, /* drop dirty page cache */
FI_DATA_EXIST, /* indicate data exists */
FI_INLINE_DOTS, /* indicate inline dot dentries */
+ FI_DO_DEFRAG, /* indicate defragment is running */
+ FI_DIRTY_FILE, /* indicate regular/symlink has dirty pages */
};
static inline void set_inode_flag(struct f2fs_inode_info *fi, int flag)
@@ -1602,13 +1649,11 @@ static inline bool is_dot_dotdot(const struct qstr *str)
static inline bool f2fs_may_extent_tree(struct inode *inode)
{
- mode_t mode = inode->i_mode;
-
if (!test_opt(F2FS_I_SB(inode), EXTENT_CACHE) ||
is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT))
return false;
- return S_ISREG(mode);
+ return S_ISREG(inode->i_mode);
}
static inline void *f2fs_kvmalloc(size_t size, gfp_t flags)
@@ -1661,8 +1706,8 @@ long f2fs_compat_ioctl(struct file *, unsigned int, unsigned long);
void f2fs_set_inode_flags(struct inode *);
struct inode *f2fs_iget(struct super_block *, unsigned long);
int try_to_free_nats(struct f2fs_sb_info *, int);
-void update_inode(struct inode *, struct page *);
-void update_inode_page(struct inode *);
+int update_inode(struct inode *, struct page *);
+int update_inode_page(struct inode *);
int f2fs_write_inode(struct inode *, struct writeback_control *);
void f2fs_evict_inode(struct inode *);
void handle_failed_inode(struct inode *);
@@ -1767,7 +1812,7 @@ void destroy_node_manager_caches(void);
*/
void register_inmem_page(struct inode *, struct page *);
int commit_inmem_pages(struct inode *, bool);
-void f2fs_balance_fs(struct f2fs_sb_info *);
+void f2fs_balance_fs(struct f2fs_sb_info *, bool);
void f2fs_balance_fs_bg(struct f2fs_sb_info *);
int f2fs_issue_flush(struct f2fs_sb_info *);
int create_flush_cmd_control(struct f2fs_sb_info *);
@@ -1813,9 +1858,9 @@ bool is_valid_blkaddr(struct f2fs_sb_info *, block_t, int);
int ra_meta_pages(struct f2fs_sb_info *, block_t, int, int, bool);
void ra_meta_pages_cond(struct f2fs_sb_info *, pgoff_t);
long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long);
-void add_dirty_inode(struct f2fs_sb_info *, nid_t, int type);
-void remove_dirty_inode(struct f2fs_sb_info *, nid_t, int type);
-void release_dirty_inode(struct f2fs_sb_info *);
+void add_ino_entry(struct f2fs_sb_info *, nid_t, int type);
+void remove_ino_entry(struct f2fs_sb_info *, nid_t, int type);
+void release_ino_entry(struct f2fs_sb_info *);
bool exist_written_data(struct f2fs_sb_info *, nid_t, int);
int acquire_orphan_inode(struct f2fs_sb_info *);
void release_orphan_inode(struct f2fs_sb_info *);
@@ -1825,9 +1870,9 @@ int recover_orphan_inodes(struct f2fs_sb_info *);
int get_valid_checkpoint(struct f2fs_sb_info *);
void update_dirty_page(struct inode *, struct page *);
void add_dirty_dir_inode(struct inode *);
-void remove_dirty_dir_inode(struct inode *);
-void sync_dirty_dir_inodes(struct f2fs_sb_info *);
-void write_checkpoint(struct f2fs_sb_info *, struct cp_control *);
+void remove_dirty_inode(struct inode *);
+int sync_dirty_inodes(struct f2fs_sb_info *, enum inode_type);
+int write_checkpoint(struct f2fs_sb_info *, struct cp_control *);
void init_ino_entry_info(struct f2fs_sb_info *);
int __init create_checkpoint_caches(void);
void destroy_checkpoint_caches(void);
@@ -1847,6 +1892,7 @@ struct page *find_data_page(struct inode *, pgoff_t);
struct page *get_lock_data_page(struct inode *, pgoff_t, bool);
struct page *get_new_data_page(struct inode *, struct page *, pgoff_t, bool);
int do_write_data_page(struct f2fs_io_info *);
+int f2fs_map_blocks(struct inode *, struct f2fs_map_blocks *, int, int);
int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *, u64, u64);
void f2fs_invalidate_page(struct page *, unsigned int, unsigned int);
int f2fs_release_page(struct page *, gfp_t);
@@ -1877,8 +1923,9 @@ struct f2fs_stat_info {
int main_area_segs, main_area_sections, main_area_zones;
unsigned long long hit_largest, hit_cached, hit_rbtree;
unsigned long long hit_total, total_ext;
- int ext_tree, ext_node;
- int ndirty_node, ndirty_dent, ndirty_dirs, ndirty_meta;
+ int ext_tree, zombie_tree, ext_node;
+ int ndirty_node, ndirty_meta;
+ int ndirty_dent, ndirty_dirs, ndirty_data, ndirty_files;
int nats, dirty_nats, sits, dirty_sits, fnids;
int total_count, utilization;
int bg_gc, inmem_pages, wb_pages;
@@ -1888,7 +1935,7 @@ struct f2fs_stat_info {
int util_free, util_valid, util_invalid;
int rsvd_segs, overp_segs;
int dirty_count, node_pages, meta_pages;
- int prefree_count, call_count, cp_count;
+ int prefree_count, call_count, cp_count, bg_cp_count;
int tot_segs, node_segs, data_segs, free_segs, free_secs;
int bg_node_segs, bg_data_segs;
int tot_blks, data_blks, node_blks;
@@ -1909,10 +1956,11 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi)
}
#define stat_inc_cp_count(si) ((si)->cp_count++)
+#define stat_inc_bg_cp_count(si) ((si)->bg_cp_count++)
#define stat_inc_call_count(si) ((si)->call_count++)
#define stat_inc_bggc_count(sbi) ((sbi)->bg_gc++)
-#define stat_inc_dirty_dir(sbi) ((sbi)->n_dirty_dirs++)
-#define stat_dec_dirty_dir(sbi) ((sbi)->n_dirty_dirs--)
+#define stat_inc_dirty_inode(sbi, type) ((sbi)->ndirty_inode[type]++)
+#define stat_dec_dirty_inode(sbi, type) ((sbi)->ndirty_inode[type]--)
#define stat_inc_total_hit(sbi) (atomic64_inc(&(sbi)->total_hit_ext))
#define stat_inc_rbtree_node_hit(sbi) (atomic64_inc(&(sbi)->read_hit_rbtree))
#define stat_inc_largest_node_hit(sbi) (atomic64_inc(&(sbi)->read_hit_largest))
@@ -1987,14 +2035,15 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi)
int f2fs_build_stats(struct f2fs_sb_info *);
void f2fs_destroy_stats(struct f2fs_sb_info *);
-void __init f2fs_create_root_stats(void);
+int __init f2fs_create_root_stats(void);
void f2fs_destroy_root_stats(void);
#else
#define stat_inc_cp_count(si)
+#define stat_inc_bg_cp_count(si)
#define stat_inc_call_count(si)
#define stat_inc_bggc_count(si)
-#define stat_inc_dirty_dir(sbi)
-#define stat_dec_dirty_dir(sbi)
+#define stat_inc_dirty_inode(sbi, type)
+#define stat_dec_dirty_inode(sbi, type)
#define stat_inc_total_hit(sb)
#define stat_inc_rbtree_node_hit(sb)
#define stat_inc_largest_node_hit(sbi)
@@ -2015,7 +2064,7 @@ void f2fs_destroy_root_stats(void);
static inline int f2fs_build_stats(struct f2fs_sb_info *sbi) { return 0; }
static inline void f2fs_destroy_stats(struct f2fs_sb_info *sbi) { }
-static inline void __init f2fs_create_root_stats(void) { }
+static inline int __init f2fs_create_root_stats(void) { return 0; }
static inline void f2fs_destroy_root_stats(void) { }
#endif
@@ -2069,8 +2118,7 @@ void f2fs_leave_shrinker(struct f2fs_sb_info *);
* extent_cache.c
*/
unsigned int f2fs_shrink_extent_tree(struct f2fs_sb_info *, int);
-void f2fs_drop_largest_extent(struct inode *, pgoff_t);
-void f2fs_init_extent_tree(struct inode *, struct f2fs_extent *);
+bool f2fs_init_extent_tree(struct inode *, struct f2fs_extent *);
unsigned int f2fs_destroy_extent_node(struct inode *);
void f2fs_destroy_extent_tree(struct inode *);
bool f2fs_lookup_extent_cache(struct inode *, pgoff_t, struct extent_info *);
@@ -2121,7 +2169,7 @@ static inline int f2fs_sb_has_crypto(struct super_block *sb)
static inline bool f2fs_may_encrypt(struct inode *inode)
{
#ifdef CONFIG_F2FS_FS_ENCRYPTION
- mode_t mode = inode->i_mode;
+ umode_t mode = inode->i_mode;
return (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode));
#else
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index a197215ad52b..18ddb1e5182a 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -40,8 +40,6 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma,
struct dnode_of_data dn;
int err;
- f2fs_balance_fs(sbi);
-
sb_start_pagefault(inode->i_sb);
f2fs_bug_on(sbi, f2fs_has_inline_data(inode));
@@ -57,6 +55,8 @@ static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma,
f2fs_put_dnode(&dn);
f2fs_unlock_op(sbi);
+ f2fs_balance_fs(sbi, dn.node_changed);
+
file_update_time(vma->vm_file);
lock_page(page);
if (unlikely(page->mapping != inode->i_mapping ||
@@ -96,6 +96,7 @@ mapped:
clear_cold_data(page);
out:
sb_end_pagefault(inode->i_sb);
+ f2fs_update_time(sbi, REQ_TIME);
return block_page_mkwrite_return(err);
}
@@ -201,7 +202,7 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
trace_f2fs_sync_file_enter(inode);
/* if fdatasync is triggered, let's do in-place-update */
- if (get_dirty_pages(inode) <= SM_I(sbi)->min_fsync_blocks)
+ if (datasync || get_dirty_pages(inode) <= SM_I(sbi)->min_fsync_blocks)
set_inode_flag(fi, FI_NEED_IPU);
ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
clear_inode_flag(fi, FI_NEED_IPU);
@@ -233,9 +234,6 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
goto out;
}
go_write:
- /* guarantee free sections for fsync */
- f2fs_balance_fs(sbi);
-
/*
* Both of fdatasync() and fsync() are able to be recovered from
* sudden-power-off.
@@ -261,8 +259,10 @@ sync_nodes:
sync_node_pages(sbi, ino, &wbc);
/* if cp_error was enabled, we should avoid infinite loop */
- if (unlikely(f2fs_cp_error(sbi)))
+ if (unlikely(f2fs_cp_error(sbi))) {
+ ret = -EIO;
goto out;
+ }
if (need_inode_block_update(sbi, ino)) {
mark_inode_dirty_sync(inode);
@@ -275,12 +275,13 @@ sync_nodes:
goto out;
/* once recovery info is written, don't need to tack this */
- remove_dirty_inode(sbi, ino, APPEND_INO);
+ remove_ino_entry(sbi, ino, APPEND_INO);
clear_inode_flag(fi, FI_APPEND_WRITE);
flush_out:
- remove_dirty_inode(sbi, ino, UPDATE_INO);
+ remove_ino_entry(sbi, ino, UPDATE_INO);
clear_inode_flag(fi, FI_UPDATE_WRITE);
ret = f2fs_issue_flush(sbi);
+ f2fs_update_time(sbi, REQ_TIME);
out:
trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret);
f2fs_trace_ios(NULL, 1);
@@ -418,19 +419,18 @@ static loff_t f2fs_llseek(struct file *file, loff_t offset, int whence)
static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma)
{
struct inode *inode = file_inode(file);
+ int err;
if (f2fs_encrypted_inode(inode)) {
- int err = f2fs_get_encryption_info(inode);
+ err = f2fs_get_encryption_info(inode);
if (err)
return 0;
}
/* we don't need to use inline_data strictly */
- if (f2fs_has_inline_data(inode)) {
- int err = f2fs_convert_inline_inode(inode);
- if (err)
- return err;
- }
+ err = f2fs_convert_inline_inode(inode);
+ if (err)
+ return err;
file_accessed(file);
vma->vm_ops = &f2fs_file_vm_ops;
@@ -483,11 +483,11 @@ int truncate_data_blocks_range(struct dnode_of_data *dn, int count)
F2FS_I(dn->inode)) + ofs;
f2fs_update_extent_cache_range(dn, fofs, 0, len);
dec_valid_block_count(sbi, dn->inode, nr_free);
- set_page_dirty(dn->node_page);
sync_inode_page(dn);
}
dn->ofs_in_node = ofs;
+ f2fs_update_time(sbi, REQ_TIME);
trace_f2fs_truncate_data_blocks_range(dn->inode, dn->nid,
dn->ofs_in_node, nr_free);
return nr_free;
@@ -604,7 +604,7 @@ int f2fs_truncate(struct inode *inode, bool lock)
trace_f2fs_truncate(inode);
/* we should check inline_data size */
- if (f2fs_has_inline_data(inode) && !f2fs_may_inline_data(inode)) {
+ if (!f2fs_may_inline_data(inode)) {
err = f2fs_convert_inline_inode(inode);
if (err)
return err;
@@ -679,13 +679,20 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
err = f2fs_truncate(inode, true);
if (err)
return err;
- f2fs_balance_fs(F2FS_I_SB(inode));
+ f2fs_balance_fs(F2FS_I_SB(inode), true);
} else {
/*
* do not trim all blocks after i_size if target size is
* larger than i_size.
*/
truncate_setsize(inode, attr->ia_size);
+
+ /* should convert inline inode here */
+ if (!f2fs_may_inline_data(inode)) {
+ err = f2fs_convert_inline_inode(inode);
+ if (err)
+ return err;
+ }
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
}
}
@@ -727,7 +734,7 @@ static int fill_zero(struct inode *inode, pgoff_t index,
if (!len)
return 0;
- f2fs_balance_fs(sbi);
+ f2fs_balance_fs(sbi, true);
f2fs_lock_op(sbi);
page = get_new_data_page(inode, NULL, index, false);
@@ -778,13 +785,11 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len)
{
pgoff_t pg_start, pg_end;
loff_t off_start, off_end;
- int ret = 0;
+ int ret;
- if (f2fs_has_inline_data(inode)) {
- ret = f2fs_convert_inline_inode(inode);
- if (ret)
- return ret;
- }
+ ret = f2fs_convert_inline_inode(inode);
+ if (ret)
+ return ret;
pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT;
pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT;
@@ -815,7 +820,7 @@ static int punch_hole(struct inode *inode, loff_t offset, loff_t len)
loff_t blk_start, blk_end;
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
- f2fs_balance_fs(sbi);
+ f2fs_balance_fs(sbi, true);
blk_start = (loff_t)pg_start << PAGE_CACHE_SHIFT;
blk_end = (loff_t)pg_end << PAGE_CACHE_SHIFT;
@@ -918,7 +923,7 @@ static int f2fs_do_collapse(struct inode *inode, pgoff_t start, pgoff_t end)
int ret = 0;
for (; end < nrpages; start++, end++) {
- f2fs_balance_fs(sbi);
+ f2fs_balance_fs(sbi, true);
f2fs_lock_op(sbi);
ret = __exchange_data_block(inode, end, start, true);
f2fs_unlock_op(sbi);
@@ -941,13 +946,9 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len)
if (offset & (F2FS_BLKSIZE - 1) || len & (F2FS_BLKSIZE - 1))
return -EINVAL;
- f2fs_balance_fs(F2FS_I_SB(inode));
-
- if (f2fs_has_inline_data(inode)) {
- ret = f2fs_convert_inline_inode(inode);
- if (ret)
- return ret;
- }
+ ret = f2fs_convert_inline_inode(inode);
+ if (ret)
+ return ret;
pg_start = offset >> PAGE_CACHE_SHIFT;
pg_end = (offset + len) >> PAGE_CACHE_SHIFT;
@@ -991,13 +992,9 @@ static int f2fs_zero_range(struct inode *inode, loff_t offset, loff_t len,
if (ret)
return ret;
- f2fs_balance_fs(sbi);
-
- if (f2fs_has_inline_data(inode)) {
- ret = f2fs_convert_inline_inode(inode);
- if (ret)
- return ret;
- }
+ ret = f2fs_convert_inline_inode(inode);
+ if (ret)
+ return ret;
ret = filemap_write_and_wait_range(mapping, offset, offset + len - 1);
if (ret)
@@ -1104,13 +1101,11 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len)
if (offset & (F2FS_BLKSIZE - 1) || len & (F2FS_BLKSIZE - 1))
return -EINVAL;
- f2fs_balance_fs(sbi);
+ ret = f2fs_convert_inline_inode(inode);
+ if (ret)
+ return ret;
- if (f2fs_has_inline_data(inode)) {
- ret = f2fs_convert_inline_inode(inode);
- if (ret)
- return ret;
- }
+ f2fs_balance_fs(sbi, true);
ret = truncate_blocks(inode, i_size_read(inode), true);
if (ret)
@@ -1154,17 +1149,15 @@ static int expand_inode_data(struct inode *inode, loff_t offset,
loff_t off_start, off_end;
int ret = 0;
- f2fs_balance_fs(sbi);
-
ret = inode_newsize_ok(inode, (len + offset));
if (ret)
return ret;
- if (f2fs_has_inline_data(inode)) {
- ret = f2fs_convert_inline_inode(inode);
- if (ret)
- return ret;
- }
+ ret = f2fs_convert_inline_inode(inode);
+ if (ret)
+ return ret;
+
+ f2fs_balance_fs(sbi, true);
pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT;
pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT;
@@ -1246,6 +1239,7 @@ static long f2fs_fallocate(struct file *file, int mode,
if (!ret) {
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode);
+ f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
}
out:
@@ -1353,8 +1347,6 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
if (!inode_owner_or_capable(inode))
return -EACCES;
- f2fs_balance_fs(F2FS_I_SB(inode));
-
if (f2fs_is_atomic_file(inode))
return 0;
@@ -1363,6 +1355,8 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
return ret;
set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
+ f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
+
return 0;
}
@@ -1384,8 +1378,10 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp)
if (f2fs_is_atomic_file(inode)) {
clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
ret = commit_inmem_pages(inode, false);
- if (ret)
+ if (ret) {
+ set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
goto err_out;
+ }
}
ret = f2fs_sync_file(filp, 0, LLONG_MAX, 0);
@@ -1410,6 +1406,7 @@ static int f2fs_ioc_start_volatile_write(struct file *filp)
return ret;
set_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE);
+ f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
return 0;
}
@@ -1441,13 +1438,17 @@ static int f2fs_ioc_abort_volatile_write(struct file *filp)
if (ret)
return ret;
- f2fs_balance_fs(F2FS_I_SB(inode));
-
- clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
- clear_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE);
- commit_inmem_pages(inode, true);
+ if (f2fs_is_atomic_file(inode)) {
+ clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE);
+ commit_inmem_pages(inode, true);
+ }
+ if (f2fs_is_volatile_file(inode)) {
+ clear_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE);
+ ret = f2fs_sync_file(filp, 0, LLONG_MAX, 0);
+ }
mnt_drop_write_file(filp);
+ f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
return ret;
}
@@ -1487,6 +1488,7 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg)
default:
return -EINVAL;
}
+ f2fs_update_time(sbi, REQ_TIME);
return 0;
}
@@ -1517,6 +1519,7 @@ static int f2fs_ioc_fitrim(struct file *filp, unsigned long arg)
if (copy_to_user((struct fstrim_range __user *)arg, &range,
sizeof(range)))
return -EFAULT;
+ f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
return 0;
}
@@ -1540,6 +1543,7 @@ static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg)
sizeof(policy)))
return -EFAULT;
+ f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
return f2fs_process_policy(&policy, inode);
#else
return -EOPNOTSUPP;
@@ -1586,13 +1590,13 @@ static int f2fs_ioc_get_encryption_pwsalt(struct file *filp, unsigned long arg)
generate_random_uuid(sbi->raw_super->encrypt_pw_salt);
err = f2fs_commit_super(sbi, false);
-
- mnt_drop_write_file(filp);
if (err) {
/* undo new data */
memset(sbi->raw_super->encrypt_pw_salt, 0, 16);
+ mnt_drop_write_file(filp);
return err;
}
+ mnt_drop_write_file(filp);
got_it:
if (copy_to_user((__u8 __user *)arg, sbi->raw_super->encrypt_pw_salt,
16))
@@ -1629,7 +1633,6 @@ static int f2fs_ioc_write_checkpoint(struct file *filp, unsigned long arg)
{
struct inode *inode = file_inode(filp);
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
- struct cp_control cpc;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
@@ -1637,13 +1640,196 @@ static int f2fs_ioc_write_checkpoint(struct file *filp, unsigned long arg)
if (f2fs_readonly(sbi->sb))
return -EROFS;
- cpc.reason = __get_cp_reason(sbi);
+ return f2fs_sync_fs(sbi->sb, 1);
+}
- mutex_lock(&sbi->gc_mutex);
- write_checkpoint(sbi, &cpc);
- mutex_unlock(&sbi->gc_mutex);
+static int f2fs_defragment_range(struct f2fs_sb_info *sbi,
+ struct file *filp,
+ struct f2fs_defragment *range)
+{
+ struct inode *inode = file_inode(filp);
+ struct f2fs_map_blocks map;
+ struct extent_info ei;
+ pgoff_t pg_start, pg_end;
+ unsigned int blk_per_seg = sbi->blocks_per_seg;
+ unsigned int total = 0, sec_num;
+ unsigned int pages_per_sec = sbi->segs_per_sec * blk_per_seg;
+ block_t blk_end = 0;
+ bool fragmented = false;
+ int err;
- return 0;
+ /* if in-place-update policy is enabled, don't waste time here */
+ if (need_inplace_update(inode))
+ return -EINVAL;
+
+ pg_start = range->start >> PAGE_CACHE_SHIFT;
+ pg_end = (range->start + range->len) >> PAGE_CACHE_SHIFT;
+
+ f2fs_balance_fs(sbi, true);
+
+ mutex_lock(&inode->i_mutex);
+
+ /* writeback all dirty pages in the range */
+ err = filemap_write_and_wait_range(inode->i_mapping, range->start,
+ range->start + range->len - 1);
+ if (err)
+ goto out;
+
+ /*
+ * lookup mapping info in extent cache, skip defragmenting if physical
+ * block addresses are continuous.
+ */
+ if (f2fs_lookup_extent_cache(inode, pg_start, &ei)) {
+ if (ei.fofs + ei.len >= pg_end)
+ goto out;
+ }
+
+ map.m_lblk = pg_start;
+
+ /*
+ * lookup mapping info in dnode page cache, skip defragmenting if all
+ * physical block addresses are continuous even if there are hole(s)
+ * in logical blocks.
+ */
+ while (map.m_lblk < pg_end) {
+ map.m_len = pg_end - map.m_lblk;
+ err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_READ);
+ if (err)
+ goto out;
+
+ if (!(map.m_flags & F2FS_MAP_FLAGS)) {
+ map.m_lblk++;
+ continue;
+ }
+
+ if (blk_end && blk_end != map.m_pblk) {
+ fragmented = true;
+ break;
+ }
+ blk_end = map.m_pblk + map.m_len;
+
+ map.m_lblk += map.m_len;
+ }
+
+ if (!fragmented)
+ goto out;
+
+ map.m_lblk = pg_start;
+ map.m_len = pg_end - pg_start;
+
+ sec_num = (map.m_len + pages_per_sec - 1) / pages_per_sec;
+
+ /*
+ * make sure there are enough free section for LFS allocation, this can
+ * avoid defragment running in SSR mode when free section are allocated
+ * intensively
+ */
+ if (has_not_enough_free_secs(sbi, sec_num)) {
+ err = -EAGAIN;
+ goto out;
+ }
+
+ while (map.m_lblk < pg_end) {
+ pgoff_t idx;
+ int cnt = 0;
+
+do_map:
+ map.m_len = pg_end - map.m_lblk;
+ err = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_READ);
+ if (err)
+ goto clear_out;
+
+ if (!(map.m_flags & F2FS_MAP_FLAGS)) {
+ map.m_lblk++;
+ continue;
+ }
+
+ set_inode_flag(F2FS_I(inode), FI_DO_DEFRAG);
+
+ idx = map.m_lblk;
+ while (idx < map.m_lblk + map.m_len && cnt < blk_per_seg) {
+ struct page *page;
+
+ page = get_lock_data_page(inode, idx, true);
+ if (IS_ERR(page)) {
+ err = PTR_ERR(page);
+ goto clear_out;
+ }
+
+ set_page_dirty(page);
+ f2fs_put_page(page, 1);
+
+ idx++;
+ cnt++;
+ total++;
+ }
+
+ map.m_lblk = idx;
+
+ if (idx < pg_end && cnt < blk_per_seg)
+ goto do_map;
+
+ clear_inode_flag(F2FS_I(inode), FI_DO_DEFRAG);
+
+ err = filemap_fdatawrite(inode->i_mapping);
+ if (err)
+ goto out;
+ }
+clear_out:
+ clear_inode_flag(F2FS_I(inode), FI_DO_DEFRAG);
+out:
+ mutex_unlock(&inode->i_mutex);
+ if (!err)
+ range->len = (u64)total << PAGE_CACHE_SHIFT;
+ return err;
+}
+
+static int f2fs_ioc_defragment(struct file *filp, unsigned long arg)
+{
+ struct inode *inode = file_inode(filp);
+ struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+ struct f2fs_defragment range;
+ int err;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (!S_ISREG(inode->i_mode))
+ return -EINVAL;
+
+ err = mnt_want_write_file(filp);
+ if (err)
+ return err;
+
+ if (f2fs_readonly(sbi->sb)) {
+ err = -EROFS;
+ goto out;
+ }
+
+ if (copy_from_user(&range, (struct f2fs_defragment __user *)arg,
+ sizeof(range))) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ /* verify alignment of offset & size */
+ if (range.start & (F2FS_BLKSIZE - 1) ||
+ range.len & (F2FS_BLKSIZE - 1)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = f2fs_defragment_range(sbi, filp, &range);
+ f2fs_update_time(sbi, REQ_TIME);
+ if (err < 0)
+ goto out;
+
+ if (copy_to_user((struct f2fs_defragment __user *)arg, &range,
+ sizeof(range)))
+ err = -EFAULT;
+out:
+ mnt_drop_write_file(filp);
+ return err;
}
long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
@@ -1679,6 +1865,8 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return f2fs_ioc_gc(filp, arg);
case F2FS_IOC_WRITE_CHECKPOINT:
return f2fs_ioc_write_checkpoint(filp, arg);
+ case F2FS_IOC_DEFRAGMENT:
+ return f2fs_ioc_defragment(filp, arg);
default:
return -ENOTTY;
}
@@ -1706,6 +1894,22 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case F2FS_IOC32_SETFLAGS:
cmd = F2FS_IOC_SETFLAGS;
break;
+ case F2FS_IOC32_GETVERSION:
+ cmd = F2FS_IOC_GETVERSION;
+ break;
+ case F2FS_IOC_START_ATOMIC_WRITE:
+ case F2FS_IOC_COMMIT_ATOMIC_WRITE:
+ case F2FS_IOC_START_VOLATILE_WRITE:
+ case F2FS_IOC_RELEASE_VOLATILE_WRITE:
+ case F2FS_IOC_ABORT_VOLATILE_WRITE:
+ case F2FS_IOC_SHUTDOWN:
+ case F2FS_IOC_SET_ENCRYPTION_POLICY:
+ case F2FS_IOC_GET_ENCRYPTION_PWSALT:
+ case F2FS_IOC_GET_ENCRYPTION_POLICY:
+ case F2FS_IOC_GARBAGE_COLLECT:
+ case F2FS_IOC_WRITE_CHECKPOINT:
+ case F2FS_IOC_DEFRAGMENT:
+ break;
default:
return -ENOIOCTLCMD;
}
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index fedbf67a0842..f610c2a9bdde 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -16,7 +16,6 @@
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/freezer.h>
-#include <linux/blkdev.h>
#include "f2fs.h"
#include "node.h"
@@ -173,9 +172,9 @@ static unsigned int get_max_cost(struct f2fs_sb_info *sbi,
{
/* SSR allocates in a segment unit */
if (p->alloc_mode == SSR)
- return 1 << sbi->log_blocks_per_seg;
+ return sbi->blocks_per_seg;
if (p->gc_mode == GC_GREEDY)
- return (1 << sbi->log_blocks_per_seg) * p->ofs_unit;
+ return sbi->blocks_per_seg * p->ofs_unit;
else if (p->gc_mode == GC_CB)
return UINT_MAX;
else /* No other gc_mode */
@@ -832,8 +831,10 @@ gc_more:
if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE)))
goto stop;
- if (unlikely(f2fs_cp_error(sbi)))
+ if (unlikely(f2fs_cp_error(sbi))) {
+ ret = -EIO;
goto stop;
+ }
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, sec_freed)) {
gc_type = FG_GC;
diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h
index b4a65be9f7d3..a993967dcdb9 100644
--- a/fs/f2fs/gc.h
+++ b/fs/f2fs/gc.h
@@ -100,11 +100,3 @@ static inline bool has_enough_invalid_blocks(struct f2fs_sb_info *sbi)
return true;
return false;
}
-
-static inline int is_idle(struct f2fs_sb_info *sbi)
-{
- struct block_device *bdev = sbi->sb->s_bdev;
- struct request_queue *q = bdev_get_queue(bdev);
- struct request_list *rl = &q->root_rl;
- return !(rl->count[BLK_RW_SYNC]) && !(rl->count[BLK_RW_ASYNC]);
-}
diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
index bda7126466c0..c3f0b7d4cfca 100644
--- a/fs/f2fs/inline.c
+++ b/fs/f2fs/inline.c
@@ -16,9 +16,6 @@
bool f2fs_may_inline_data(struct inode *inode)
{
- if (!test_opt(F2FS_I_SB(inode), INLINE_DATA))
- return false;
-
if (f2fs_is_atomic_file(inode))
return false;
@@ -177,6 +174,9 @@ int f2fs_convert_inline_inode(struct inode *inode)
struct page *ipage, *page;
int err = 0;
+ if (!f2fs_has_inline_data(inode))
+ return 0;
+
page = grab_cache_page(inode->i_mapping, 0);
if (!page)
return -ENOMEM;
@@ -199,6 +199,9 @@ out:
f2fs_unlock_op(sbi);
f2fs_put_page(page, 1);
+
+ f2fs_balance_fs(sbi, dn.node_changed);
+
return err;
}
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index 97e20decacb4..2adeff26be11 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -138,7 +138,8 @@ static int do_read_inode(struct inode *inode)
fi->i_pino = le32_to_cpu(ri->i_pino);
fi->i_dir_level = ri->i_dir_level;
- f2fs_init_extent_tree(inode, &ri->i_ext);
+ if (f2fs_init_extent_tree(inode, &ri->i_ext))
+ set_page_dirty(node_page);
get_inline_info(fi, ri);
@@ -202,6 +203,7 @@ make_now:
inode->i_op = &f2fs_encrypted_symlink_inode_operations;
else
inode->i_op = &f2fs_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_mapping->a_ops = &f2fs_dblock_aops;
} else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
@@ -221,7 +223,7 @@ bad_inode:
return ERR_PTR(ret);
}
-void update_inode(struct inode *inode, struct page *node_page)
+int update_inode(struct inode *inode, struct page *node_page)
{
struct f2fs_inode *ri;
@@ -259,15 +261,16 @@ void update_inode(struct inode *inode, struct page *node_page)
__set_inode_rdev(inode, ri);
set_cold_node(inode, node_page);
- set_page_dirty(node_page);
-
clear_inode_flag(F2FS_I(inode), FI_DIRTY_INODE);
+
+ return set_page_dirty(node_page);
}
-void update_inode_page(struct inode *inode)
+int update_inode_page(struct inode *inode)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct page *node_page;
+ int ret = 0;
retry:
node_page = get_node_page(sbi, inode->i_ino);
if (IS_ERR(node_page)) {
@@ -278,10 +281,11 @@ retry:
} else if (err != -ENOENT) {
f2fs_stop_checkpoint(sbi);
}
- return;
+ return 0;
}
- update_inode(inode, node_page);
+ ret = update_inode(inode, node_page);
f2fs_put_page(node_page, 1);
+ return ret;
}
int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
@@ -299,9 +303,8 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
* We need to balance fs here to prevent from producing dirty node pages
* during the urgent cleaning time when runing out of free sections.
*/
- update_inode_page(inode);
-
- f2fs_balance_fs(sbi);
+ if (update_inode_page(inode))
+ f2fs_balance_fs(sbi, true);
return 0;
}
@@ -327,7 +330,7 @@ void f2fs_evict_inode(struct inode *inode)
goto out_clear;
f2fs_bug_on(sbi, get_dirty_pages(inode));
- remove_dirty_dir_inode(inode);
+ remove_dirty_inode(inode);
f2fs_destroy_extent_tree(inode);
@@ -357,9 +360,9 @@ no_delete:
if (xnid)
invalidate_mapping_pages(NODE_MAPPING(sbi), xnid, xnid);
if (is_inode_flag_set(fi, FI_APPEND_WRITE))
- add_dirty_inode(sbi, inode->i_ino, APPEND_INO);
+ add_ino_entry(sbi, inode->i_ino, APPEND_INO);
if (is_inode_flag_set(fi, FI_UPDATE_WRITE))
- add_dirty_inode(sbi, inode->i_ino, UPDATE_INO);
+ add_ino_entry(sbi, inode->i_ino, UPDATE_INO);
if (is_inode_flag_set(fi, FI_FREE_NID)) {
if (err && err != -ENOENT)
alloc_nid_done(sbi, inode->i_ino);
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index 2c32110f9fc0..6f944e5eb76e 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -60,7 +60,7 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode))
f2fs_set_encrypted_inode(inode);
- if (f2fs_may_inline_data(inode))
+ if (test_opt(sbi, INLINE_DATA) && f2fs_may_inline_data(inode))
set_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
if (f2fs_may_inline_dentry(inode))
set_inode_flag(F2FS_I(inode), FI_INLINE_DENTRY);
@@ -128,8 +128,6 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
nid_t ino = 0;
int err;
- f2fs_balance_fs(sbi);
-
inode = f2fs_new_inode(dir, mode);
if (IS_ERR(inode))
return PTR_ERR(inode);
@@ -142,6 +140,8 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
inode->i_mapping->a_ops = &f2fs_dblock_aops;
ino = inode->i_ino;
+ f2fs_balance_fs(sbi, true);
+
f2fs_lock_op(sbi);
err = f2fs_add_link(dentry, inode);
if (err)
@@ -172,7 +172,7 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
!f2fs_is_child_context_consistent_with_parent(dir, inode))
return -EPERM;
- f2fs_balance_fs(sbi);
+ f2fs_balance_fs(sbi, true);
inode->i_ctime = CURRENT_TIME;
ihold(inode);
@@ -214,6 +214,15 @@ static int __recover_dot_dentries(struct inode *dir, nid_t pino)
struct page *page;
int err = 0;
+ if (f2fs_readonly(sbi->sb)) {
+ f2fs_msg(sbi->sb, KERN_INFO,
+ "skip recovering inline_dots inode (ino:%lu, pino:%u) "
+ "in readonly mountpoint", dir->i_ino, pino);
+ return 0;
+ }
+
+ f2fs_balance_fs(sbi, true);
+
f2fs_lock_op(sbi);
de = f2fs_find_entry(dir, &dot, &page);
@@ -288,12 +297,13 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
int err = -ENOENT;
trace_f2fs_unlink_enter(dir, dentry);
- f2fs_balance_fs(sbi);
de = f2fs_find_entry(dir, &dentry->d_name, &page);
if (!de)
goto fail;
+ f2fs_balance_fs(sbi, true);
+
f2fs_lock_op(sbi);
err = acquire_orphan_inode(sbi);
if (err) {
@@ -315,12 +325,15 @@ fail:
return err;
}
-static const char *f2fs_follow_link(struct dentry *dentry, void **cookie)
+static const char *f2fs_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
- const char *link = page_follow_link_light(dentry, cookie);
+ const char *link = page_get_link(dentry, inode, done);
if (!IS_ERR(link) && !*link) {
/* this is broken symlink case */
- page_put_link(NULL, *cookie);
+ do_delayed_call(done);
+ clear_delayed_call(done);
link = ERR_PTR(-ENOENT);
}
return link;
@@ -341,8 +354,6 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
if (len > dir->i_sb->s_blocksize)
return -ENAMETOOLONG;
- f2fs_balance_fs(sbi);
-
inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO);
if (IS_ERR(inode))
return PTR_ERR(inode);
@@ -351,8 +362,11 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
inode->i_op = &f2fs_encrypted_symlink_inode_operations;
else
inode->i_op = &f2fs_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_mapping->a_ops = &f2fs_dblock_aops;
+ f2fs_balance_fs(sbi, true);
+
f2fs_lock_op(sbi);
err = f2fs_add_link(dentry, inode);
if (err)
@@ -433,8 +447,6 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
struct inode *inode;
int err;
- f2fs_balance_fs(sbi);
-
inode = f2fs_new_inode(dir, S_IFDIR | mode);
if (IS_ERR(inode))
return PTR_ERR(inode);
@@ -444,6 +456,8 @@ static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
inode->i_mapping->a_ops = &f2fs_dblock_aops;
mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO);
+ f2fs_balance_fs(sbi, true);
+
set_inode_flag(F2FS_I(inode), FI_INC_LINK);
f2fs_lock_op(sbi);
err = f2fs_add_link(dentry, inode);
@@ -481,8 +495,6 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
struct inode *inode;
int err = 0;
- f2fs_balance_fs(sbi);
-
inode = f2fs_new_inode(dir, mode);
if (IS_ERR(inode))
return PTR_ERR(inode);
@@ -490,6 +502,8 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
init_special_inode(inode, inode->i_mode, rdev);
inode->i_op = &f2fs_special_inode_operations;
+ f2fs_balance_fs(sbi, true);
+
f2fs_lock_op(sbi);
err = f2fs_add_link(dentry, inode);
if (err)
@@ -516,9 +530,6 @@ static int __f2fs_tmpfile(struct inode *dir, struct dentry *dentry,
struct inode *inode;
int err;
- if (!whiteout)
- f2fs_balance_fs(sbi);
-
inode = f2fs_new_inode(dir, mode);
if (IS_ERR(inode))
return PTR_ERR(inode);
@@ -532,6 +543,8 @@ static int __f2fs_tmpfile(struct inode *dir, struct dentry *dentry,
inode->i_mapping->a_ops = &f2fs_dblock_aops;
}
+ f2fs_balance_fs(sbi, true);
+
f2fs_lock_op(sbi);
err = acquire_orphan_inode(sbi);
if (err)
@@ -604,8 +617,6 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
goto out;
}
- f2fs_balance_fs(sbi);
-
old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page);
if (!old_entry)
goto out;
@@ -635,6 +646,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (!new_entry)
goto out_whiteout;
+ f2fs_balance_fs(sbi, true);
+
f2fs_lock_op(sbi);
err = acquire_orphan_inode(sbi);
@@ -666,6 +679,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
update_inode_page(old_inode);
update_inode_page(new_inode);
} else {
+ f2fs_balance_fs(sbi, true);
+
f2fs_lock_op(sbi);
err = f2fs_add_link(new_dentry, old_inode);
@@ -763,8 +778,6 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
new_inode)))
return -EPERM;
- f2fs_balance_fs(sbi);
-
old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page);
if (!old_entry)
goto out;
@@ -807,6 +820,8 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
goto out_new_dir;
}
+ f2fs_balance_fs(sbi, true);
+
f2fs_lock_op(sbi);
err = update_dent_inode(old_inode, new_inode, &new_dentry->d_name);
@@ -923,18 +938,22 @@ static int f2fs_rename2(struct inode *old_dir, struct dentry *old_dentry,
}
#ifdef CONFIG_F2FS_FS_ENCRYPTION
-static const char *f2fs_encrypted_follow_link(struct dentry *dentry, void **cookie)
+static const char *f2fs_encrypted_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
struct page *cpage = NULL;
char *caddr, *paddr = NULL;
- struct f2fs_str cstr;
+ struct f2fs_str cstr = FSTR_INIT(NULL, 0);
struct f2fs_str pstr = FSTR_INIT(NULL, 0);
- struct inode *inode = d_inode(dentry);
struct f2fs_encrypted_symlink_data *sd;
loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1);
u32 max_size = inode->i_sb->s_blocksize;
int res;
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
+
res = f2fs_get_encryption_info(inode);
if (res)
return ERR_PTR(res);
@@ -942,12 +961,18 @@ static const char *f2fs_encrypted_follow_link(struct dentry *dentry, void **cook
cpage = read_mapping_page(inode->i_mapping, 0, NULL);
if (IS_ERR(cpage))
return ERR_CAST(cpage);
- caddr = kmap(cpage);
+ caddr = page_address(cpage);
caddr[size] = 0;
/* Symlink is encrypted */
sd = (struct f2fs_encrypted_symlink_data *)caddr;
cstr.len = le16_to_cpu(sd->len);
+
+ /* this is broken symlink case */
+ if (unlikely(cstr.len == 0)) {
+ res = -ENOENT;
+ goto errout;
+ }
cstr.name = kmalloc(cstr.len, GFP_NOFS);
if (!cstr.name) {
res = -ENOMEM;
@@ -956,7 +981,7 @@ static const char *f2fs_encrypted_follow_link(struct dentry *dentry, void **cook
memcpy(cstr.name, sd->encrypted_path, cstr.len);
/* this is broken symlink case */
- if (cstr.name[0] == 0 && cstr.len == 0) {
+ if (unlikely(cstr.name[0] == 0)) {
res = -ENOENT;
goto errout;
}
@@ -982,27 +1007,27 @@ static const char *f2fs_encrypted_follow_link(struct dentry *dentry, void **cook
/* Null-terminate the name */
paddr[res] = '\0';
- kunmap(cpage);
page_cache_release(cpage);
- return *cookie = paddr;
+ set_delayed_call(done, kfree_link, paddr);
+ return paddr;
errout:
kfree(cstr.name);
f2fs_fname_crypto_free_buffer(&pstr);
- kunmap(cpage);
page_cache_release(cpage);
return ERR_PTR(res);
}
const struct inode_operations f2fs_encrypted_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = f2fs_encrypted_follow_link,
- .put_link = kfree_put_link,
+ .get_link = f2fs_encrypted_get_link,
.getattr = f2fs_getattr,
.setattr = f2fs_setattr,
+#ifdef CONFIG_F2FS_FS_XATTR
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.listxattr = f2fs_listxattr,
.removexattr = generic_removexattr,
+#endif
};
#endif
@@ -1031,8 +1056,7 @@ const struct inode_operations f2fs_dir_inode_operations = {
const struct inode_operations f2fs_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = f2fs_follow_link,
- .put_link = page_put_link,
+ .get_link = f2fs_get_link,
.getattr = f2fs_getattr,
.setattr = f2fs_setattr,
#ifdef CONFIG_F2FS_FS_XATTR
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index 7bcbc6e9c40d..342597a5897f 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -65,13 +65,14 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type)
sizeof(struct ino_entry)) >> PAGE_CACHE_SHIFT;
res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1);
} else if (type == EXTENT_CACHE) {
- mem_size = (sbi->total_ext_tree * sizeof(struct extent_tree) +
+ mem_size = (atomic_read(&sbi->total_ext_tree) *
+ sizeof(struct extent_tree) +
atomic_read(&sbi->total_ext_node) *
sizeof(struct extent_node)) >> PAGE_CACHE_SHIFT;
res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1);
} else {
- if (sbi->sb->s_bdi->wb.dirty_exceeded)
- return false;
+ if (!sbi->sb->s_bdi->wb.dirty_exceeded)
+ return true;
}
return res;
}
@@ -261,13 +262,11 @@ static void cache_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid,
{
struct nat_entry *e;
- down_write(&nm_i->nat_tree_lock);
e = __lookup_nat_cache(nm_i, nid);
if (!e) {
e = grab_nat_entry(nm_i, nid);
node_info_from_raw_nat(&e->ni, ne);
}
- up_write(&nm_i->nat_tree_lock);
}
static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni,
@@ -379,6 +378,8 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni)
memset(&ne, 0, sizeof(struct f2fs_nat_entry));
+ down_write(&nm_i->nat_tree_lock);
+
/* Check current segment summary */
mutex_lock(&curseg->curseg_mutex);
i = lookup_journal_in_cursum(sum, NAT_JOURNAL, nid, 0);
@@ -399,6 +400,7 @@ void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni)
cache:
/* cache nat entry */
cache_nat_entry(NM_I(sbi), nid, &ne);
+ up_write(&nm_i->nat_tree_lock);
}
/*
@@ -676,7 +678,8 @@ static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs,
ret = truncate_dnode(&rdn);
if (ret < 0)
goto out_err;
- set_nid(page, i, 0, false);
+ if (set_nid(page, i, 0, false))
+ dn->node_changed = true;
}
} else {
child_nofs = nofs + ofs * (NIDS_PER_BLOCK + 1) + 1;
@@ -689,7 +692,8 @@ static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs,
rdn.nid = child_nid;
ret = truncate_nodes(&rdn, child_nofs, 0, depth - 1);
if (ret == (NIDS_PER_BLOCK + 1)) {
- set_nid(page, i, 0, false);
+ if (set_nid(page, i, 0, false))
+ dn->node_changed = true;
child_nofs += ret;
} else if (ret < 0 && ret != -ENOENT) {
goto out_err;
@@ -750,7 +754,8 @@ static int truncate_partial_nodes(struct dnode_of_data *dn,
err = truncate_dnode(dn);
if (err < 0)
goto fail;
- set_nid(pages[idx], i, 0, false);
+ if (set_nid(pages[idx], i, 0, false))
+ dn->node_changed = true;
}
if (offset[idx + 1] == 0) {
@@ -975,7 +980,8 @@ struct page *new_node_page(struct dnode_of_data *dn,
fill_node_footer(page, dn->nid, dn->inode->i_ino, ofs, true);
set_cold_node(dn->inode, page);
SetPageUptodate(page);
- set_page_dirty(page);
+ if (set_page_dirty(page))
+ dn->node_changed = true;
if (f2fs_has_xattr_block(ofs))
F2FS_I(dn->inode)->i_xattr_nid = dn->nid;
@@ -1035,6 +1041,10 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid)
struct page *apage;
int err;
+ if (!nid)
+ return;
+ f2fs_bug_on(sbi, check_nid_range(sbi, nid));
+
apage = find_get_page(NODE_MAPPING(sbi), nid);
if (apage && PageUptodate(apage)) {
f2fs_put_page(apage, 0);
@@ -1050,51 +1060,38 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid)
f2fs_put_page(apage, err ? 1 : 0);
}
-struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid)
+/*
+ * readahead MAX_RA_NODE number of node pages.
+ */
+void ra_node_pages(struct page *parent, int start)
{
- struct page *page;
- int err;
-repeat:
- page = grab_cache_page(NODE_MAPPING(sbi), nid);
- if (!page)
- return ERR_PTR(-ENOMEM);
+ struct f2fs_sb_info *sbi = F2FS_P_SB(parent);
+ struct blk_plug plug;
+ int i, end;
+ nid_t nid;
- err = read_node_page(page, READ_SYNC);
- if (err < 0) {
- f2fs_put_page(page, 1);
- return ERR_PTR(err);
- } else if (err != LOCKED_PAGE) {
- lock_page(page);
- }
+ blk_start_plug(&plug);
- if (unlikely(!PageUptodate(page) || nid != nid_of_node(page))) {
- ClearPageUptodate(page);
- f2fs_put_page(page, 1);
- return ERR_PTR(-EIO);
- }
- if (unlikely(page->mapping != NODE_MAPPING(sbi))) {
- f2fs_put_page(page, 1);
- goto repeat;
+ /* Then, try readahead for siblings of the desired node */
+ end = start + MAX_RA_NODE;
+ end = min(end, NIDS_PER_BLOCK);
+ for (i = start; i < end; i++) {
+ nid = get_nid(parent, i, false);
+ ra_node_page(sbi, nid);
}
- return page;
+
+ blk_finish_plug(&plug);
}
-/*
- * Return a locked page for the desired node page.
- * And, readahead MAX_RA_NODE number of node pages.
- */
-struct page *get_node_page_ra(struct page *parent, int start)
+struct page *__get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid,
+ struct page *parent, int start)
{
- struct f2fs_sb_info *sbi = F2FS_P_SB(parent);
- struct blk_plug plug;
struct page *page;
- int err, i, end;
- nid_t nid;
+ int err;
- /* First, try getting the desired direct node. */
- nid = get_nid(parent, start, false);
if (!nid)
return ERR_PTR(-ENOENT);
+ f2fs_bug_on(sbi, check_nid_range(sbi, nid));
repeat:
page = grab_cache_page(NODE_MAPPING(sbi), nid);
if (!page)
@@ -1108,46 +1105,53 @@ repeat:
goto page_hit;
}
- blk_start_plug(&plug);
-
- /* Then, try readahead for siblings of the desired node */
- end = start + MAX_RA_NODE;
- end = min(end, NIDS_PER_BLOCK);
- for (i = start + 1; i < end; i++) {
- nid = get_nid(parent, i, false);
- if (!nid)
- continue;
- ra_node_page(sbi, nid);
- }
-
- blk_finish_plug(&plug);
+ if (parent)
+ ra_node_pages(parent, start + 1);
lock_page(page);
+
+ if (unlikely(!PageUptodate(page))) {
+ f2fs_put_page(page, 1);
+ return ERR_PTR(-EIO);
+ }
if (unlikely(page->mapping != NODE_MAPPING(sbi))) {
f2fs_put_page(page, 1);
goto repeat;
}
page_hit:
- if (unlikely(!PageUptodate(page))) {
- f2fs_put_page(page, 1);
- return ERR_PTR(-EIO);
- }
+ f2fs_bug_on(sbi, nid != nid_of_node(page));
return page;
}
+struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid)
+{
+ return __get_node_page(sbi, nid, NULL, 0);
+}
+
+struct page *get_node_page_ra(struct page *parent, int start)
+{
+ struct f2fs_sb_info *sbi = F2FS_P_SB(parent);
+ nid_t nid = get_nid(parent, start, false);
+
+ return __get_node_page(sbi, nid, parent, start);
+}
+
void sync_inode_page(struct dnode_of_data *dn)
{
+ int ret = 0;
+
if (IS_INODE(dn->node_page) || dn->inode_page == dn->node_page) {
- update_inode(dn->inode, dn->node_page);
+ ret = update_inode(dn->inode, dn->node_page);
} else if (dn->inode_page) {
if (!dn->inode_page_locked)
lock_page(dn->inode_page);
- update_inode(dn->inode, dn->inode_page);
+ ret = update_inode(dn->inode, dn->inode_page);
if (!dn->inode_page_locked)
unlock_page(dn->inode_page);
} else {
- update_inode_page(dn->inode);
+ ret = update_inode_page(dn->inode);
}
+ dn->node_changed = ret ? true: false;
}
int sync_node_pages(struct f2fs_sb_info *sbi, nid_t ino,
@@ -1175,6 +1179,11 @@ next_step:
for (i = 0; i < nr_pages; i++) {
struct page *page = pvec.pages[i];
+ if (unlikely(f2fs_cp_error(sbi))) {
+ pagevec_release(&pvec);
+ return -EIO;
+ }
+
/*
* flushing sequence with step:
* 0. indirect nodes
@@ -1349,7 +1358,7 @@ static int f2fs_write_node_page(struct page *page,
up_read(&sbi->node_write);
unlock_page(page);
- if (wbc->for_reclaim)
+ if (wbc->for_reclaim || unlikely(f2fs_cp_error(sbi)))
f2fs_submit_merged_bio(sbi, NODE, WRITE);
return 0;
@@ -1440,13 +1449,10 @@ static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build)
if (build) {
/* do not add allocated nids */
- down_read(&nm_i->nat_tree_lock);
ne = __lookup_nat_cache(nm_i, nid);
- if (ne &&
- (!get_nat_flag(ne, IS_CHECKPOINTED) ||
+ if (ne && (!get_nat_flag(ne, IS_CHECKPOINTED) ||
nat_get_blkaddr(ne) != NULL_ADDR))
allocated = true;
- up_read(&nm_i->nat_tree_lock);
if (allocated)
return 0;
}
@@ -1532,6 +1538,8 @@ static void build_free_nids(struct f2fs_sb_info *sbi)
ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nid), FREE_NID_PAGES,
META_NAT, true);
+ down_read(&nm_i->nat_tree_lock);
+
while (1) {
struct page *page = get_current_nat_page(sbi, nid);
@@ -1560,6 +1568,7 @@ static void build_free_nids(struct f2fs_sb_info *sbi)
remove_free_nid(nm_i, nid);
}
mutex_unlock(&curseg->curseg_mutex);
+ up_read(&nm_i->nat_tree_lock);
ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nm_i->next_scan_nid),
nm_i->ra_nid_pages, META_NAT, false);
@@ -1582,8 +1591,6 @@ retry:
/* We should not use stale free nids created by build_free_nids */
if (nm_i->fcnt && !on_build_free_nids(nm_i)) {
- struct node_info ni;
-
f2fs_bug_on(sbi, list_empty(&nm_i->free_nid_list));
list_for_each_entry(i, &nm_i->free_nid_list, list)
if (i->state == NID_NEW)
@@ -1594,13 +1601,6 @@ retry:
i->state = NID_ALLOC;
nm_i->fcnt--;
spin_unlock(&nm_i->free_nid_list_lock);
-
- /* check nid is allocated already */
- get_node_info(sbi, *nid, &ni);
- if (ni.blk_addr != NULL_ADDR) {
- alloc_nid_done(sbi, *nid);
- goto retry;
- }
return true;
}
spin_unlock(&nm_i->free_nid_list_lock);
@@ -1842,14 +1842,12 @@ static void remove_nats_in_journal(struct f2fs_sb_info *sbi)
raw_ne = nat_in_journal(sum, i);
- down_write(&nm_i->nat_tree_lock);
ne = __lookup_nat_cache(nm_i, nid);
if (!ne) {
ne = grab_nat_entry(nm_i, nid);
node_info_from_raw_nat(&ne->ni, &raw_ne);
}
__set_nat_cache_dirty(nm_i, ne);
- up_write(&nm_i->nat_tree_lock);
}
update_nats_in_cursum(sum, -i);
mutex_unlock(&curseg->curseg_mutex);
@@ -1883,7 +1881,6 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi,
struct f2fs_nat_block *nat_blk;
struct nat_entry *ne, *cur;
struct page *page = NULL;
- struct f2fs_nm_info *nm_i = NM_I(sbi);
/*
* there are two steps to flush nat entries:
@@ -1920,12 +1917,8 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi,
raw_ne = &nat_blk->entries[nid - start_nid];
}
raw_nat_from_node_info(raw_ne, &ne->ni);
-
- down_write(&NM_I(sbi)->nat_tree_lock);
nat_reset_flag(ne);
__clear_nat_cache_dirty(NM_I(sbi), ne);
- up_write(&NM_I(sbi)->nat_tree_lock);
-
if (nat_get_blkaddr(ne) == NULL_ADDR)
add_free_nid(sbi, nid, false);
}
@@ -1937,9 +1930,7 @@ static void __flush_nat_entry_set(struct f2fs_sb_info *sbi,
f2fs_bug_on(sbi, set->entry_cnt);
- down_write(&nm_i->nat_tree_lock);
radix_tree_delete(&NM_I(sbi)->nat_set_root, set->set);
- up_write(&nm_i->nat_tree_lock);
kmem_cache_free(nat_entry_set_slab, set);
}
@@ -1959,6 +1950,9 @@ void flush_nat_entries(struct f2fs_sb_info *sbi)
if (!nm_i->dirty_nat_cnt)
return;
+
+ down_write(&nm_i->nat_tree_lock);
+
/*
* if there are no enough space in journal to store dirty nat
* entries, remove all entries from journal and merge them
@@ -1967,7 +1961,6 @@ void flush_nat_entries(struct f2fs_sb_info *sbi)
if (!__has_cursum_space(sum, nm_i->dirty_nat_cnt, NAT_JOURNAL))
remove_nats_in_journal(sbi);
- down_write(&nm_i->nat_tree_lock);
while ((found = __gang_lookup_nat_set(nm_i,
set_idx, SETVEC_SIZE, setvec))) {
unsigned idx;
@@ -1976,12 +1969,13 @@ void flush_nat_entries(struct f2fs_sb_info *sbi)
__adjust_nat_entry_set(setvec[idx], &sets,
MAX_NAT_JENTRIES(sum));
}
- up_write(&nm_i->nat_tree_lock);
/* flush dirty nats in nat entry set */
list_for_each_entry_safe(set, tmp, &sets, set_list)
__flush_nat_entry_set(sbi, set);
+ up_write(&nm_i->nat_tree_lock);
+
f2fs_bug_on(sbi, nm_i->dirty_nat_cnt);
}
diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h
index e4fffd2d98c4..d4d1f636fe1c 100644
--- a/fs/f2fs/node.h
+++ b/fs/f2fs/node.h
@@ -183,7 +183,7 @@ static inline pgoff_t current_nat_addr(struct f2fs_sb_info *sbi, nid_t start)
block_addr = (pgoff_t)(nm_i->nat_blkaddr +
(seg_off << sbi->log_blocks_per_seg << 1) +
- (block_off & ((1 << sbi->log_blocks_per_seg) - 1)));
+ (block_off & (sbi->blocks_per_seg - 1)));
if (f2fs_test_bit(block_off, nm_i->nat_bitmap))
block_addr += sbi->blocks_per_seg;
@@ -317,7 +317,7 @@ static inline bool IS_DNODE(struct page *node_page)
return true;
}
-static inline void set_nid(struct page *p, int off, nid_t nid, bool i)
+static inline int set_nid(struct page *p, int off, nid_t nid, bool i)
{
struct f2fs_node *rn = F2FS_NODE(p);
@@ -327,7 +327,7 @@ static inline void set_nid(struct page *p, int off, nid_t nid, bool i)
rn->i.i_nid[off - NODE_DIR1_BLOCK] = cpu_to_le32(nid);
else
rn->in.nid[off] = cpu_to_le32(nid);
- set_page_dirty(p);
+ return set_page_dirty(p);
}
static inline nid_t get_nid(struct page *p, int off, bool i)
diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c
index cbf74f47cce8..589b20b8677b 100644
--- a/fs/f2fs/recovery.c
+++ b/fs/f2fs/recovery.c
@@ -168,6 +168,32 @@ static void recover_inode(struct inode *inode, struct page *page)
ino_of_node(page), name);
}
+static bool is_same_inode(struct inode *inode, struct page *ipage)
+{
+ struct f2fs_inode *ri = F2FS_INODE(ipage);
+ struct timespec disk;
+
+ if (!IS_INODE(ipage))
+ return true;
+
+ disk.tv_sec = le64_to_cpu(ri->i_ctime);
+ disk.tv_nsec = le32_to_cpu(ri->i_ctime_nsec);
+ if (timespec_compare(&inode->i_ctime, &disk) > 0)
+ return false;
+
+ disk.tv_sec = le64_to_cpu(ri->i_atime);
+ disk.tv_nsec = le32_to_cpu(ri->i_atime_nsec);
+ if (timespec_compare(&inode->i_atime, &disk) > 0)
+ return false;
+
+ disk.tv_sec = le64_to_cpu(ri->i_mtime);
+ disk.tv_nsec = le32_to_cpu(ri->i_mtime_nsec);
+ if (timespec_compare(&inode->i_mtime, &disk) > 0)
+ return false;
+
+ return true;
+}
+
static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
{
unsigned long long cp_ver = cur_cp_version(F2FS_CKPT(sbi));
@@ -197,7 +223,10 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
goto next;
entry = get_fsync_inode(head, ino_of_node(page));
- if (!entry) {
+ if (entry) {
+ if (!is_same_inode(entry->inode, page))
+ goto next;
+ } else {
if (IS_INODE(page) && is_dent_dnode(page)) {
err = recover_inode_page(sbi, page);
if (err)
@@ -459,8 +488,7 @@ out:
return err;
}
-static int recover_data(struct f2fs_sb_info *sbi,
- struct list_head *head, int type)
+static int recover_data(struct f2fs_sb_info *sbi, struct list_head *head)
{
unsigned long long cp_ver = cur_cp_version(F2FS_CKPT(sbi));
struct curseg_info *curseg;
@@ -469,7 +497,7 @@ static int recover_data(struct f2fs_sb_info *sbi,
block_t blkaddr;
/* get node pages in the current segment */
- curseg = CURSEG_I(sbi, type);
+ curseg = CURSEG_I(sbi, CURSEG_WARM_NODE);
blkaddr = NEXT_FREE_BLKADDR(sbi, curseg);
while (1) {
@@ -556,7 +584,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi)
need_writecp = true;
/* step #2: recover data */
- err = recover_data(sbi, &inode_list, CURSEG_WARM_NODE);
+ err = recover_data(sbi, &inode_list);
if (!err)
f2fs_bug_on(sbi, !list_empty(&inode_list));
out:
@@ -595,7 +623,7 @@ out:
.reason = CP_RECOVERY,
};
mutex_unlock(&sbi->cp_mutex);
- write_checkpoint(sbi, &cpc);
+ err = write_checkpoint(sbi, &cpc);
} else {
mutex_unlock(&sbi->cp_mutex);
}
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index f77b3258454a..5904a411c86f 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -86,6 +86,7 @@ static inline unsigned long __reverse_ffs(unsigned long word)
/*
* __find_rev_next(_zero)_bit is copied from lib/find_next_bit.c because
* f2fs_set_bit makes MSB and LSB reversed in a byte.
+ * @size must be integral times of unsigned long.
* Example:
* MSB <--> LSB
* f2fs_set_bit(0, bitmap) => 1000 0000
@@ -95,94 +96,73 @@ static unsigned long __find_rev_next_bit(const unsigned long *addr,
unsigned long size, unsigned long offset)
{
const unsigned long *p = addr + BIT_WORD(offset);
- unsigned long result = offset & ~(BITS_PER_LONG - 1);
+ unsigned long result = size;
unsigned long tmp;
if (offset >= size)
return size;
- size -= result;
+ size -= (offset & ~(BITS_PER_LONG - 1));
offset %= BITS_PER_LONG;
- if (!offset)
- goto aligned;
-
- tmp = __reverse_ulong((unsigned char *)p);
- tmp &= ~0UL >> offset;
-
- if (size < BITS_PER_LONG)
- goto found_first;
- if (tmp)
- goto found_middle;
-
- size -= BITS_PER_LONG;
- result += BITS_PER_LONG;
- p++;
-aligned:
- while (size & ~(BITS_PER_LONG-1)) {
+
+ while (1) {
+ if (*p == 0)
+ goto pass;
+
tmp = __reverse_ulong((unsigned char *)p);
+
+ tmp &= ~0UL >> offset;
+ if (size < BITS_PER_LONG)
+ tmp &= (~0UL << (BITS_PER_LONG - size));
if (tmp)
- goto found_middle;
- result += BITS_PER_LONG;
+ goto found;
+pass:
+ if (size <= BITS_PER_LONG)
+ break;
size -= BITS_PER_LONG;
+ offset = 0;
p++;
}
- if (!size)
- return result;
-
- tmp = __reverse_ulong((unsigned char *)p);
-found_first:
- tmp &= (~0UL << (BITS_PER_LONG - size));
- if (!tmp) /* Are any bits set? */
- return result + size; /* Nope. */
-found_middle:
- return result + __reverse_ffs(tmp);
+ return result;
+found:
+ return result - size + __reverse_ffs(tmp);
}
static unsigned long __find_rev_next_zero_bit(const unsigned long *addr,
unsigned long size, unsigned long offset)
{
const unsigned long *p = addr + BIT_WORD(offset);
- unsigned long result = offset & ~(BITS_PER_LONG - 1);
+ unsigned long result = size;
unsigned long tmp;
if (offset >= size)
return size;
- size -= result;
+ size -= (offset & ~(BITS_PER_LONG - 1));
offset %= BITS_PER_LONG;
- if (!offset)
- goto aligned;
-
- tmp = __reverse_ulong((unsigned char *)p);
- tmp |= ~((~0UL << offset) >> offset);
-
- if (size < BITS_PER_LONG)
- goto found_first;
- if (tmp != ~0UL)
- goto found_middle;
-
- size -= BITS_PER_LONG;
- result += BITS_PER_LONG;
- p++;
-aligned:
- while (size & ~(BITS_PER_LONG - 1)) {
+
+ while (1) {
+ if (*p == ~0UL)
+ goto pass;
+
tmp = __reverse_ulong((unsigned char *)p);
+
+ if (offset)
+ tmp |= ~0UL << (BITS_PER_LONG - offset);
+ if (size < BITS_PER_LONG)
+ tmp |= ~0UL >> size;
if (tmp != ~0UL)
- goto found_middle;
- result += BITS_PER_LONG;
+ goto found;
+pass:
+ if (size <= BITS_PER_LONG)
+ break;
size -= BITS_PER_LONG;
+ offset = 0;
p++;
}
- if (!size)
- return result;
-
- tmp = __reverse_ulong((unsigned char *)p);
-found_first:
- tmp |= ~(~0UL << (BITS_PER_LONG - size));
- if (tmp == ~0UL) /* Are any bits zero? */
- return result + size; /* Nope. */
-found_middle:
- return result + __reverse_ffz(tmp);
+ return result;
+found:
+ return result - size + __reverse_ffz(tmp);
}
void register_inmem_page(struct inode *inode, struct page *page)
@@ -233,7 +213,7 @@ int commit_inmem_pages(struct inode *inode, bool abort)
* inode becomes free by iget_locked in f2fs_iget.
*/
if (!abort) {
- f2fs_balance_fs(sbi);
+ f2fs_balance_fs(sbi, true);
f2fs_lock_op(sbi);
}
@@ -257,6 +237,7 @@ int commit_inmem_pages(struct inode *inode, bool abort)
submit_bio = true;
}
} else {
+ ClearPageUptodate(cur->page);
trace_f2fs_commit_inmem_page(cur->page, INMEM_DROP);
}
set_page_private(cur->page, 0);
@@ -281,8 +262,10 @@ int commit_inmem_pages(struct inode *inode, bool abort)
* This function balances dirty node and dentry pages.
* In addition, it controls garbage collection.
*/
-void f2fs_balance_fs(struct f2fs_sb_info *sbi)
+void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need)
{
+ if (!need)
+ return;
/*
* We should do GC or end up with checkpoint, if there are so many dirty
* dir/node pages without enough free segments.
@@ -310,8 +293,12 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi)
if (!available_free_memory(sbi, NAT_ENTRIES) ||
excess_prefree_segs(sbi) ||
!available_free_memory(sbi, INO_ENTRIES) ||
- jiffies > sbi->cp_expires)
+ (is_idle(sbi) && f2fs_time_over(sbi, CP_TIME))) {
+ if (test_opt(sbi, DATA_FLUSH))
+ sync_dirty_inodes(sbi, FILE_INODE);
f2fs_sync_fs(sbi->sb, true);
+ stat_inc_bg_cp_count(sbi->stat_info);
+ }
}
static int issue_flush_thread(void *data)
@@ -1134,6 +1121,7 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
__u64 end = start + F2FS_BYTES_TO_BLK(range->len) - 1;
unsigned int start_segno, end_segno;
struct cp_control cpc;
+ int err = 0;
if (start >= MAX_BLKADDR(sbi) || range->len < sbi->blocksize)
return -EINVAL;
@@ -1164,12 +1152,12 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
sbi->segs_per_sec) - 1, end_segno);
mutex_lock(&sbi->gc_mutex);
- write_checkpoint(sbi, &cpc);
+ err = write_checkpoint(sbi, &cpc);
mutex_unlock(&sbi->gc_mutex);
}
out:
range->len = F2FS_BLK_TO_BYTES(cpc.trimmed);
- return 0;
+ return err;
}
static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type)
@@ -1749,13 +1737,13 @@ int lookup_journal_in_cursum(struct f2fs_summary_block *sum, int type,
if (le32_to_cpu(nid_in_journal(sum, i)) == val)
return i;
}
- if (alloc && nats_in_cursum(sum) < NAT_JOURNAL_ENTRIES)
+ if (alloc && __has_cursum_space(sum, 1, NAT_JOURNAL))
return update_nats_in_cursum(sum, 1);
} else if (type == SIT_JOURNAL) {
for (i = 0; i < sits_in_cursum(sum); i++)
if (le32_to_cpu(segno_in_journal(sum, i)) == val)
return i;
- if (alloc && sits_in_cursum(sum) < SIT_JOURNAL_ENTRIES)
+ if (alloc && __has_cursum_space(sum, 1, SIT_JOURNAL))
return update_sits_in_cursum(sum, 1);
}
return -1;
diff --git a/fs/f2fs/shrinker.c b/fs/f2fs/shrinker.c
index da0d8e0b55a5..93606f281bf9 100644
--- a/fs/f2fs/shrinker.c
+++ b/fs/f2fs/shrinker.c
@@ -32,7 +32,8 @@ static unsigned long __count_free_nids(struct f2fs_sb_info *sbi)
static unsigned long __count_extent_cache(struct f2fs_sb_info *sbi)
{
- return sbi->total_ext_tree + atomic_read(&sbi->total_ext_node);
+ return atomic_read(&sbi->total_zombie_tree) +
+ atomic_read(&sbi->total_ext_node);
}
unsigned long f2fs_shrink_count(struct shrinker *shrink,
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 3a65e0132352..6134832baaaf 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -67,6 +67,7 @@ enum {
Opt_extent_cache,
Opt_noextent_cache,
Opt_noinline_data,
+ Opt_data_flush,
Opt_err,
};
@@ -91,6 +92,7 @@ static match_table_t f2fs_tokens = {
{Opt_extent_cache, "extent_cache"},
{Opt_noextent_cache, "noextent_cache"},
{Opt_noinline_data, "noinline_data"},
+ {Opt_data_flush, "data_flush"},
{Opt_err, NULL},
};
@@ -216,7 +218,8 @@ F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ram_thresh, ram_thresh);
F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ra_nid_pages, ra_nid_pages);
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_victim_search, max_victim_search);
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, dir_level, dir_level);
-F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, cp_interval);
+F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
+F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
#define ATTR_LIST(name) (&f2fs_attr_##name.attr)
static struct attribute *f2fs_attrs[] = {
@@ -235,6 +238,7 @@ static struct attribute *f2fs_attrs[] = {
ATTR_LIST(ram_thresh),
ATTR_LIST(ra_nid_pages),
ATTR_LIST(cp_interval),
+ ATTR_LIST(idle_interval),
NULL,
};
@@ -406,6 +410,9 @@ static int parse_options(struct super_block *sb, char *options)
case Opt_noinline_data:
clear_opt(sbi, INLINE_DATA);
break;
+ case Opt_data_flush:
+ set_opt(sbi, DATA_FLUSH);
+ break;
default:
f2fs_msg(sb, KERN_ERR,
"Unrecognized mount option \"%s\" or missing value",
@@ -432,6 +439,7 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
fi->i_current_depth = 1;
fi->i_advise = 0;
init_rwsem(&fi->i_sem);
+ INIT_LIST_HEAD(&fi->dirty_list);
INIT_LIST_HEAD(&fi->inmem_pages);
mutex_init(&fi->inmem_lock);
@@ -548,7 +556,7 @@ static void f2fs_put_super(struct super_block *sb)
* normally superblock is clean, so we need to release this.
* In addition, EIO will skip do checkpoint, we need this as well.
*/
- release_dirty_inode(sbi);
+ release_ino_entry(sbi);
release_discard_addrs(sbi);
f2fs_leave_shrinker(sbi);
@@ -566,13 +574,14 @@ static void f2fs_put_super(struct super_block *sb)
wait_for_completion(&sbi->s_kobj_unregister);
sb->s_fs_info = NULL;
- brelse(sbi->raw_super_buf);
+ kfree(sbi->raw_super);
kfree(sbi);
}
int f2fs_sync_fs(struct super_block *sb, int sync)
{
struct f2fs_sb_info *sbi = F2FS_SB(sb);
+ int err = 0;
trace_f2fs_sync_fs(sb, sync);
@@ -582,14 +591,12 @@ int f2fs_sync_fs(struct super_block *sb, int sync)
cpc.reason = __get_cp_reason(sbi);
mutex_lock(&sbi->gc_mutex);
- write_checkpoint(sbi, &cpc);
+ err = write_checkpoint(sbi, &cpc);
mutex_unlock(&sbi->gc_mutex);
- } else {
- f2fs_balance_fs(sbi);
}
f2fs_trace_ios(NULL, 1);
- return 0;
+ return err;
}
static int f2fs_freeze(struct super_block *sb)
@@ -686,6 +693,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
seq_puts(seq, ",extent_cache");
else
seq_puts(seq, ",noextent_cache");
+ if (test_opt(sbi, DATA_FLUSH))
+ seq_puts(seq, ",data_flush");
seq_printf(seq, ",active_logs=%u", sbi->active_logs);
return 0;
@@ -898,7 +907,7 @@ static const struct export_operations f2fs_export_ops = {
.get_parent = f2fs_get_parent,
};
-static loff_t max_file_size(unsigned bits)
+static loff_t max_file_blocks(void)
{
loff_t result = (DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS);
loff_t leaf_count = ADDRS_PER_BLOCK;
@@ -914,10 +923,82 @@ static loff_t max_file_size(unsigned bits)
leaf_count *= NIDS_PER_BLOCK;
result += leaf_count;
- result <<= bits;
return result;
}
+static inline bool sanity_check_area_boundary(struct super_block *sb,
+ struct f2fs_super_block *raw_super)
+{
+ u32 segment0_blkaddr = le32_to_cpu(raw_super->segment0_blkaddr);
+ u32 cp_blkaddr = le32_to_cpu(raw_super->cp_blkaddr);
+ u32 sit_blkaddr = le32_to_cpu(raw_super->sit_blkaddr);
+ u32 nat_blkaddr = le32_to_cpu(raw_super->nat_blkaddr);
+ u32 ssa_blkaddr = le32_to_cpu(raw_super->ssa_blkaddr);
+ u32 main_blkaddr = le32_to_cpu(raw_super->main_blkaddr);
+ u32 segment_count_ckpt = le32_to_cpu(raw_super->segment_count_ckpt);
+ u32 segment_count_sit = le32_to_cpu(raw_super->segment_count_sit);
+ u32 segment_count_nat = le32_to_cpu(raw_super->segment_count_nat);
+ u32 segment_count_ssa = le32_to_cpu(raw_super->segment_count_ssa);
+ u32 segment_count_main = le32_to_cpu(raw_super->segment_count_main);
+ u32 segment_count = le32_to_cpu(raw_super->segment_count);
+ u32 log_blocks_per_seg = le32_to_cpu(raw_super->log_blocks_per_seg);
+
+ if (segment0_blkaddr != cp_blkaddr) {
+ f2fs_msg(sb, KERN_INFO,
+ "Mismatch start address, segment0(%u) cp_blkaddr(%u)",
+ segment0_blkaddr, cp_blkaddr);
+ return true;
+ }
+
+ if (cp_blkaddr + (segment_count_ckpt << log_blocks_per_seg) !=
+ sit_blkaddr) {
+ f2fs_msg(sb, KERN_INFO,
+ "Wrong CP boundary, start(%u) end(%u) blocks(%u)",
+ cp_blkaddr, sit_blkaddr,
+ segment_count_ckpt << log_blocks_per_seg);
+ return true;
+ }
+
+ if (sit_blkaddr + (segment_count_sit << log_blocks_per_seg) !=
+ nat_blkaddr) {
+ f2fs_msg(sb, KERN_INFO,
+ "Wrong SIT boundary, start(%u) end(%u) blocks(%u)",
+ sit_blkaddr, nat_blkaddr,
+ segment_count_sit << log_blocks_per_seg);
+ return true;
+ }
+
+ if (nat_blkaddr + (segment_count_nat << log_blocks_per_seg) !=
+ ssa_blkaddr) {
+ f2fs_msg(sb, KERN_INFO,
+ "Wrong NAT boundary, start(%u) end(%u) blocks(%u)",
+ nat_blkaddr, ssa_blkaddr,
+ segment_count_nat << log_blocks_per_seg);
+ return true;
+ }
+
+ if (ssa_blkaddr + (segment_count_ssa << log_blocks_per_seg) !=
+ main_blkaddr) {
+ f2fs_msg(sb, KERN_INFO,
+ "Wrong SSA boundary, start(%u) end(%u) blocks(%u)",
+ ssa_blkaddr, main_blkaddr,
+ segment_count_ssa << log_blocks_per_seg);
+ return true;
+ }
+
+ if (main_blkaddr + (segment_count_main << log_blocks_per_seg) !=
+ segment0_blkaddr + (segment_count << log_blocks_per_seg)) {
+ f2fs_msg(sb, KERN_INFO,
+ "Wrong MAIN_AREA boundary, start(%u) end(%u) blocks(%u)",
+ main_blkaddr,
+ segment0_blkaddr + (segment_count << log_blocks_per_seg),
+ segment_count_main << log_blocks_per_seg);
+ return true;
+ }
+
+ return false;
+}
+
static int sanity_check_raw_super(struct super_block *sb,
struct f2fs_super_block *raw_super)
{
@@ -947,6 +1028,14 @@ static int sanity_check_raw_super(struct super_block *sb,
return 1;
}
+ /* check log blocks per segment */
+ if (le32_to_cpu(raw_super->log_blocks_per_seg) != 9) {
+ f2fs_msg(sb, KERN_INFO,
+ "Invalid log blocks per segment (%u)\n",
+ le32_to_cpu(raw_super->log_blocks_per_seg));
+ return 1;
+ }
+
/* Currently, support 512/1024/2048/4096 bytes sector size */
if (le32_to_cpu(raw_super->log_sectorsize) >
F2FS_MAX_LOG_SECTOR_SIZE ||
@@ -965,6 +1054,23 @@ static int sanity_check_raw_super(struct super_block *sb,
le32_to_cpu(raw_super->log_sectorsize));
return 1;
}
+
+ /* check reserved ino info */
+ if (le32_to_cpu(raw_super->node_ino) != 1 ||
+ le32_to_cpu(raw_super->meta_ino) != 2 ||
+ le32_to_cpu(raw_super->root_ino) != 3) {
+ f2fs_msg(sb, KERN_INFO,
+ "Invalid Fs Meta Ino: node(%u) meta(%u) root(%u)",
+ le32_to_cpu(raw_super->node_ino),
+ le32_to_cpu(raw_super->meta_ino),
+ le32_to_cpu(raw_super->root_ino));
+ return 1;
+ }
+
+ /* check CP/SIT/NAT/SSA/MAIN_AREA area boundary */
+ if (sanity_check_area_boundary(sb, raw_super))
+ return 1;
+
return 0;
}
@@ -1018,7 +1124,8 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
atomic_set(&sbi->nr_pages[i], 0);
sbi->dir_level = DEF_DIR_LEVEL;
- sbi->cp_interval = DEF_CP_INTERVAL;
+ sbi->interval_time[CP_TIME] = DEF_CP_INTERVAL;
+ sbi->interval_time[REQ_TIME] = DEF_IDLE_INTERVAL;
clear_sbi_flag(sbi, SBI_NEED_FSCK);
INIT_LIST_HEAD(&sbi->s_list);
@@ -1032,111 +1139,114 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
*/
static int read_raw_super_block(struct super_block *sb,
struct f2fs_super_block **raw_super,
- struct buffer_head **raw_super_buf,
- int *recovery)
+ int *valid_super_block, int *recovery)
{
int block = 0;
- struct buffer_head *buffer;
- struct f2fs_super_block *super;
+ struct buffer_head *bh;
+ struct f2fs_super_block *super, *buf;
int err = 0;
+ super = kzalloc(sizeof(struct f2fs_super_block), GFP_KERNEL);
+ if (!super)
+ return -ENOMEM;
retry:
- buffer = sb_bread(sb, block);
- if (!buffer) {
+ bh = sb_bread(sb, block);
+ if (!bh) {
*recovery = 1;
f2fs_msg(sb, KERN_ERR, "Unable to read %dth superblock",
block + 1);
- if (block == 0) {
- block++;
- goto retry;
- } else {
- err = -EIO;
- goto out;
- }
+ err = -EIO;
+ goto next;
}
- super = (struct f2fs_super_block *)
- ((char *)(buffer)->b_data + F2FS_SUPER_OFFSET);
+ buf = (struct f2fs_super_block *)(bh->b_data + F2FS_SUPER_OFFSET);
/* sanity checking of raw super */
- if (sanity_check_raw_super(sb, super)) {
- brelse(buffer);
+ if (sanity_check_raw_super(sb, buf)) {
+ brelse(bh);
*recovery = 1;
f2fs_msg(sb, KERN_ERR,
"Can't find valid F2FS filesystem in %dth superblock",
block + 1);
- if (block == 0) {
- block++;
- goto retry;
- } else {
- err = -EINVAL;
- goto out;
- }
+ err = -EINVAL;
+ goto next;
}
if (!*raw_super) {
- *raw_super_buf = buffer;
+ memcpy(super, buf, sizeof(*super));
+ *valid_super_block = block;
*raw_super = super;
- } else {
- /* already have a valid superblock */
- brelse(buffer);
}
+ brelse(bh);
+next:
/* check the validity of the second superblock */
if (block == 0) {
block++;
goto retry;
}
-out:
/* No valid superblock */
- if (!*raw_super)
+ if (!*raw_super) {
+ kfree(super);
return err;
+ }
return 0;
}
+static int __f2fs_commit_super(struct f2fs_sb_info *sbi, int block)
+{
+ struct f2fs_super_block *super = F2FS_RAW_SUPER(sbi);
+ struct buffer_head *bh;
+ int err;
+
+ bh = sb_getblk(sbi->sb, block);
+ if (!bh)
+ return -EIO;
+
+ lock_buffer(bh);
+ memcpy(bh->b_data + F2FS_SUPER_OFFSET, super, sizeof(*super));
+ set_buffer_uptodate(bh);
+ set_buffer_dirty(bh);
+ unlock_buffer(bh);
+
+ /* it's rare case, we can do fua all the time */
+ err = __sync_dirty_buffer(bh, WRITE_FLUSH_FUA);
+ brelse(bh);
+
+ return err;
+}
+
int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover)
{
- struct buffer_head *sbh = sbi->raw_super_buf;
- sector_t block = sbh->b_blocknr;
int err;
/* write back-up superblock first */
- sbh->b_blocknr = block ? 0 : 1;
- mark_buffer_dirty(sbh);
- err = sync_dirty_buffer(sbh);
-
- sbh->b_blocknr = block;
+ err = __f2fs_commit_super(sbi, sbi->valid_super_block ? 0 : 1);
/* if we are in recovery path, skip writing valid superblock */
if (recover || err)
- goto out;
+ return err;
/* write current valid superblock */
- mark_buffer_dirty(sbh);
- err = sync_dirty_buffer(sbh);
-out:
- clear_buffer_write_io_error(sbh);
- set_buffer_uptodate(sbh);
- return err;
+ return __f2fs_commit_super(sbi, sbi->valid_super_block);
}
static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
{
struct f2fs_sb_info *sbi;
struct f2fs_super_block *raw_super;
- struct buffer_head *raw_super_buf;
struct inode *root;
long err;
bool retry = true, need_fsck = false;
char *options = NULL;
- int recovery, i;
+ int recovery, i, valid_super_block;
try_onemore:
err = -EINVAL;
raw_super = NULL;
- raw_super_buf = NULL;
+ valid_super_block = -1;
recovery = 0;
/* allocate memory for f2fs-specific super block info */
@@ -1150,7 +1260,8 @@ try_onemore:
goto free_sbi;
}
- err = read_raw_super_block(sb, &raw_super, &raw_super_buf, &recovery);
+ err = read_raw_super_block(sb, &raw_super, &valid_super_block,
+ &recovery);
if (err)
goto free_sbi;
@@ -1167,7 +1278,9 @@ try_onemore:
if (err)
goto free_options;
- sb->s_maxbytes = max_file_size(le32_to_cpu(raw_super->log_blocksize));
+ sbi->max_file_blocks = max_file_blocks();
+ sb->s_maxbytes = sbi->max_file_blocks <<
+ le32_to_cpu(raw_super->log_blocksize);
sb->s_max_links = F2FS_LINK_MAX;
get_random_bytes(&sbi->s_next_generation, sizeof(u32));
@@ -1183,7 +1296,7 @@ try_onemore:
/* init f2fs-specific super block info */
sbi->sb = sb;
sbi->raw_super = raw_super;
- sbi->raw_super_buf = raw_super_buf;
+ sbi->valid_super_block = valid_super_block;
mutex_init(&sbi->gc_mutex);
mutex_init(&sbi->writepages);
mutex_init(&sbi->cp_mutex);
@@ -1236,8 +1349,10 @@ try_onemore:
le64_to_cpu(sbi->ckpt->valid_block_count);
sbi->last_valid_block_count = sbi->total_valid_block_count;
sbi->alloc_valid_block_count = 0;
- INIT_LIST_HEAD(&sbi->dir_inode_list);
- spin_lock_init(&sbi->dir_inode_lock);
+ for (i = 0; i < NR_INODE_TYPE; i++) {
+ INIT_LIST_HEAD(&sbi->inode_list[i]);
+ spin_lock_init(&sbi->inode_lock[i]);
+ }
init_extent_cache_info(sbi);
@@ -1355,12 +1470,14 @@ try_onemore:
f2fs_commit_super(sbi, true);
}
- sbi->cp_expires = round_jiffies_up(jiffies);
-
+ f2fs_update_time(sbi, CP_TIME);
+ f2fs_update_time(sbi, REQ_TIME);
return 0;
free_kobj:
kobject_del(&sbi->s_kobj);
+ kobject_put(&sbi->s_kobj);
+ wait_for_completion(&sbi->s_kobj_unregister);
free_proc:
if (sbi->s_proc) {
remove_proc_entry("segment_info", sbi->s_proc);
@@ -1387,7 +1504,7 @@ free_meta_inode:
free_options:
kfree(options);
free_sb_buf:
- brelse(raw_super_buf);
+ kfree(raw_super);
free_sbi:
kfree(sbi);
@@ -1424,8 +1541,9 @@ MODULE_ALIAS_FS("f2fs");
static int __init init_inodecache(void)
{
- f2fs_inode_cachep = f2fs_kmem_cache_create("f2fs_inode_cache",
- sizeof(struct f2fs_inode_info));
+ f2fs_inode_cachep = kmem_cache_create("f2fs_inode_cache",
+ sizeof(struct f2fs_inode_info), 0,
+ SLAB_RECLAIM_ACCOUNT|SLAB_ACCOUNT, NULL);
if (!f2fs_inode_cachep)
return -ENOMEM;
return 0;
@@ -1478,10 +1596,14 @@ static int __init init_f2fs_fs(void)
err = register_filesystem(&f2fs_fs_type);
if (err)
goto free_shrinker;
- f2fs_create_root_stats();
+ err = f2fs_create_root_stats();
+ if (err)
+ goto free_filesystem;
f2fs_proc_root = proc_mkdir("fs/f2fs", NULL);
return 0;
+free_filesystem:
+ unregister_filesystem(&f2fs_fs_type);
free_shrinker:
unregister_shrinker(&f2fs_shrinker_info);
free_crypto:
diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c
index 862368a32e53..10f1e784fa23 100644
--- a/fs/f2fs/xattr.c
+++ b/fs/f2fs/xattr.c
@@ -25,38 +25,6 @@
#include "f2fs.h"
#include "xattr.h"
-static size_t f2fs_xattr_generic_list(const struct xattr_handler *handler,
- struct dentry *dentry, char *list, size_t list_size,
- const char *name, size_t len)
-{
- struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb);
- int total_len, prefix_len;
-
- switch (handler->flags) {
- case F2FS_XATTR_INDEX_USER:
- if (!test_opt(sbi, XATTR_USER))
- return -EOPNOTSUPP;
- break;
- case F2FS_XATTR_INDEX_TRUSTED:
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- break;
- case F2FS_XATTR_INDEX_SECURITY:
- break;
- default:
- return -EINVAL;
- }
-
- prefix_len = strlen(handler->prefix);
- total_len = prefix_len + len + 1;
- if (list && total_len <= list_size) {
- memcpy(list, handler->prefix, prefix_len);
- memcpy(list + prefix_len, name, len);
- list[prefix_len + len] = '\0';
- }
- return total_len;
-}
-
static int f2fs_xattr_generic_get(const struct xattr_handler *handler,
struct dentry *dentry, const char *name, void *buffer,
size_t size)
@@ -77,8 +45,6 @@ static int f2fs_xattr_generic_get(const struct xattr_handler *handler,
default:
return -EINVAL;
}
- if (strcmp(name, "") == 0)
- return -EINVAL;
return f2fs_getxattr(d_inode(dentry), handler->flags, name,
buffer, size, NULL);
}
@@ -103,24 +69,20 @@ static int f2fs_xattr_generic_set(const struct xattr_handler *handler,
default:
return -EINVAL;
}
- if (strcmp(name, "") == 0)
- return -EINVAL;
-
return f2fs_setxattr(d_inode(dentry), handler->flags, name,
value, size, NULL, flags);
}
-static size_t f2fs_xattr_advise_list(const struct xattr_handler *handler,
- struct dentry *dentry, char *list, size_t list_size,
- const char *name, size_t len)
+static bool f2fs_xattr_user_list(struct dentry *dentry)
{
- const char *xname = F2FS_SYSTEM_ADVISE_PREFIX;
- size_t size;
+ struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb);
- size = strlen(xname) + 1;
- if (list && size <= list_size)
- memcpy(list, xname, size);
- return size;
+ return test_opt(sbi, XATTR_USER);
+}
+
+static bool f2fs_xattr_trusted_list(struct dentry *dentry)
+{
+ return capable(CAP_SYS_ADMIN);
}
static int f2fs_xattr_advise_get(const struct xattr_handler *handler,
@@ -129,9 +91,6 @@ static int f2fs_xattr_advise_get(const struct xattr_handler *handler,
{
struct inode *inode = d_inode(dentry);
- if (strcmp(name, "") != 0)
- return -EINVAL;
-
if (buffer)
*((char *)buffer) = F2FS_I(inode)->i_advise;
return sizeof(char);
@@ -143,8 +102,6 @@ static int f2fs_xattr_advise_set(const struct xattr_handler *handler,
{
struct inode *inode = d_inode(dentry);
- if (strcmp(name, "") != 0)
- return -EINVAL;
if (!inode_owner_or_capable(inode))
return -EPERM;
if (value == NULL)
@@ -183,7 +140,7 @@ int f2fs_init_security(struct inode *inode, struct inode *dir,
const struct xattr_handler f2fs_xattr_user_handler = {
.prefix = XATTR_USER_PREFIX,
.flags = F2FS_XATTR_INDEX_USER,
- .list = f2fs_xattr_generic_list,
+ .list = f2fs_xattr_user_list,
.get = f2fs_xattr_generic_get,
.set = f2fs_xattr_generic_set,
};
@@ -191,15 +148,14 @@ const struct xattr_handler f2fs_xattr_user_handler = {
const struct xattr_handler f2fs_xattr_trusted_handler = {
.prefix = XATTR_TRUSTED_PREFIX,
.flags = F2FS_XATTR_INDEX_TRUSTED,
- .list = f2fs_xattr_generic_list,
+ .list = f2fs_xattr_trusted_list,
.get = f2fs_xattr_generic_get,
.set = f2fs_xattr_generic_set,
};
const struct xattr_handler f2fs_xattr_advise_handler = {
- .prefix = F2FS_SYSTEM_ADVISE_PREFIX,
+ .name = F2FS_SYSTEM_ADVISE_NAME,
.flags = F2FS_XATTR_INDEX_ADVISE,
- .list = f2fs_xattr_advise_list,
.get = f2fs_xattr_advise_get,
.set = f2fs_xattr_advise_set,
};
@@ -207,7 +163,6 @@ const struct xattr_handler f2fs_xattr_advise_handler = {
const struct xattr_handler f2fs_xattr_security_handler = {
.prefix = XATTR_SECURITY_PREFIX,
.flags = F2FS_XATTR_INDEX_SECURITY,
- .list = f2fs_xattr_generic_list,
.get = f2fs_xattr_generic_get,
.set = f2fs_xattr_generic_set,
};
@@ -455,20 +410,27 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
list_for_each_xattr(entry, base_addr) {
const struct xattr_handler *handler =
f2fs_xattr_handler(entry->e_name_index);
+ const char *prefix;
+ size_t prefix_len;
size_t size;
- if (!handler)
+ if (!handler || (handler->list && !handler->list(dentry)))
continue;
- size = handler->list(handler, dentry, buffer, rest,
- entry->e_name, entry->e_name_len);
- if (buffer && size > rest) {
- error = -ERANGE;
- goto cleanup;
+ prefix = handler->prefix ?: handler->name;
+ prefix_len = strlen(prefix);
+ size = prefix_len + entry->e_name_len + 1;
+ if (buffer) {
+ if (size > rest) {
+ error = -ERANGE;
+ goto cleanup;
+ }
+ memcpy(buffer, prefix, prefix_len);
+ buffer += prefix_len;
+ memcpy(buffer, entry->e_name, entry->e_name_len);
+ buffer += entry->e_name_len;
+ *buffer++ = 0;
}
-
- if (buffer)
- buffer += size;
rest -= size;
}
error = buffer_size - rest;
@@ -609,7 +571,7 @@ int f2fs_setxattr(struct inode *inode, int index, const char *name,
if (ipage)
return __f2fs_setxattr(inode, index, name, value,
size, ipage, flags);
- f2fs_balance_fs(sbi);
+ f2fs_balance_fs(sbi, true);
f2fs_lock_op(sbi);
/* protect xattr_ver */
@@ -618,5 +580,6 @@ int f2fs_setxattr(struct inode *inode, int index, const char *name,
up_write(&F2FS_I(inode)->i_sem);
f2fs_unlock_op(sbi);
+ f2fs_update_time(sbi, REQ_TIME);
return err;
}
diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h
index 71a7100d5492..79dccc8252dd 100644
--- a/fs/f2fs/xattr.h
+++ b/fs/f2fs/xattr.h
@@ -27,7 +27,7 @@
#define F2FS_XATTR_REFCOUNT_MAX 1024
/* Name indexes */
-#define F2FS_SYSTEM_ADVISE_PREFIX "system.advise"
+#define F2FS_SYSTEM_ADVISE_NAME "system.advise"
#define F2FS_XATTR_INDEX_USER 1
#define F2FS_XATTR_INDEX_POSIX_ACL_ACCESS 2
#define F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT 3
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 509411dd3698..6aece96df19f 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -677,7 +677,7 @@ static int __init fat_init_inodecache(void)
fat_inode_cachep = kmem_cache_create("fat_inode_cache",
sizeof(struct msdos_inode_info),
0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
init_once);
if (fat_inode_cachep == NULL)
return -ENOMEM;
diff --git a/fs/fcntl.c b/fs/fcntl.c
index ee85cd4e136a..350a2c8cfd28 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -51,7 +51,8 @@ static int setfl(int fd, struct file * filp, unsigned long arg)
if (arg & O_NDELAY)
arg |= O_NONBLOCK;
- if (arg & O_DIRECT) {
+ /* Pipe packetized mode is controlled by O_DIRECT flag */
+ if (!S_ISFIFO(filp->f_inode->i_mode) && (arg & O_DIRECT)) {
if (!filp->f_mapping || !filp->f_mapping->a_ops ||
!filp->f_mapping->a_ops->direct_IO)
return -EINVAL;
diff --git a/fs/file.c b/fs/file.c
index 39f8f15921da..1fbc5c0555a9 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -25,9 +25,9 @@
int sysctl_nr_open __read_mostly = 1024*1024;
int sysctl_nr_open_min = BITS_PER_LONG;
-/* our max() is unusable in constant expressions ;-/ */
-#define __const_max(x, y) ((x) < (y) ? (x) : (y))
-int sysctl_nr_open_max = __const_max(INT_MAX, ~(size_t)0/sizeof(void *)) &
+/* our min() is unusable in constant expressions ;-/ */
+#define __const_min(x, y) ((x) < (y) ? (x) : (y))
+int sysctl_nr_open_max = __const_min(INT_MAX, ~(size_t)0/sizeof(void *)) &
-BITS_PER_LONG;
static void *alloc_fdmem(size_t size)
@@ -37,11 +37,12 @@ static void *alloc_fdmem(size_t size)
* vmalloc() if the allocation size will be considered "large" by the VM.
*/
if (size <= (PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER)) {
- void *data = kmalloc(size, GFP_KERNEL|__GFP_NOWARN|__GFP_NORETRY);
+ void *data = kmalloc(size, GFP_KERNEL_ACCOUNT |
+ __GFP_NOWARN | __GFP_NORETRY);
if (data != NULL)
return data;
}
- return vmalloc(size);
+ return __vmalloc(size, GFP_KERNEL_ACCOUNT | __GFP_HIGHMEM, PAGE_KERNEL);
}
static void __free_fdtable(struct fdtable *fdt)
@@ -126,7 +127,7 @@ static struct fdtable * alloc_fdtable(unsigned int nr)
if (unlikely(nr > sysctl_nr_open))
nr = ((sysctl_nr_open - 1) | (BITS_PER_LONG - 1)) + 1;
- fdt = kmalloc(sizeof(struct fdtable), GFP_KERNEL);
+ fdt = kmalloc(sizeof(struct fdtable), GFP_KERNEL_ACCOUNT);
if (!fdt)
goto out;
fdt->max_fds = nr;
diff --git a/fs/freevxfs/vxfs_inode.c b/fs/freevxfs/vxfs_inode.c
index ef73ed674a27..3e2ccade61ed 100644
--- a/fs/freevxfs/vxfs_inode.c
+++ b/fs/freevxfs/vxfs_inode.c
@@ -326,6 +326,7 @@ vxfs_iget(struct super_block *sbp, ino_t ino)
} else if (S_ISLNK(ip->i_mode)) {
if (!VXFS_ISIMMED(vip)) {
ip->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(ip);
ip->i_mapping->a_ops = &vxfs_aops;
} else {
ip->i_op = &simple_symlink_inode_operations;
diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c
index eae2c11268bc..8e3ee1936c7e 100644
--- a/fs/fuse/cuse.c
+++ b/fs/fuse/cuse.c
@@ -549,6 +549,8 @@ static int cuse_channel_release(struct inode *inode, struct file *file)
unregister_chrdev_region(cc->cdev->dev, 1);
cdev_del(cc->cdev);
}
+ /* Base reference is now owned by "fud" */
+ fuse_conn_put(&cc->fc);
rc = fuse_dev_release(inode, file); /* puts the base reference */
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 5e2e08712d3b..712601f299b8 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1365,15 +1365,19 @@ static int fuse_readdir(struct file *file, struct dir_context *ctx)
return err;
}
-static const char *fuse_follow_link(struct dentry *dentry, void **cookie)
+static const char *fuse_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
- struct inode *inode = d_inode(dentry);
struct fuse_conn *fc = get_fuse_conn(inode);
FUSE_ARGS(args);
char *link;
ssize_t ret;
- link = (char *) __get_free_page(GFP_KERNEL);
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
+
+ link = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!link)
return ERR_PTR(-ENOMEM);
@@ -1385,11 +1389,11 @@ static const char *fuse_follow_link(struct dentry *dentry, void **cookie)
args.out.args[0].value = link;
ret = fuse_simple_request(fc, &args);
if (ret < 0) {
- free_page((unsigned long) link);
+ kfree(link);
link = ERR_PTR(ret);
} else {
link[ret] = '\0';
- *cookie = link;
+ set_delayed_call(done, kfree_link, link);
}
fuse_invalidate_atime(inode);
return link;
@@ -1909,8 +1913,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 = free_page_put_link,
+ .get_link = fuse_get_link,
.readlink = generic_readlink,
.getattr = fuse_getattr,
.setxattr = fuse_setxattr,
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index e0faf8f2c868..570ca4053c80 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1049,6 +1049,7 @@ static ssize_t fuse_fill_write_pages(struct fuse_req *req,
tmp = iov_iter_copy_from_user_atomic(page, ii, offset, bytes);
flush_dcache_page(page);
+ iov_iter_advance(ii, tmp);
if (!tmp) {
unlock_page(page);
page_cache_release(page);
@@ -1061,7 +1062,6 @@ static ssize_t fuse_fill_write_pages(struct fuse_req *req,
req->page_descs[req->num_pages].length = tmp;
req->num_pages++;
- iov_iter_advance(ii, tmp);
count += tmp;
pos += tmp;
offset += tmp;
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 2913db2a5b99..4d69d5c0bedc 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -1255,8 +1255,8 @@ static int __init fuse_fs_init(void)
int err;
fuse_inode_cachep = kmem_cache_create("fuse_inode",
- sizeof(struct fuse_inode),
- 0, SLAB_HWCACHE_ALIGN,
+ sizeof(struct fuse_inode), 0,
+ SLAB_HWCACHE_ALIGN|SLAB_ACCOUNT,
fuse_inode_init_once);
err = -ENOMEM;
if (!fuse_inode_cachep)
diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c
index 1be3b061c05c..791932617d1a 100644
--- a/fs/gfs2/acl.c
+++ b/fs/gfs2/acl.c
@@ -31,9 +31,9 @@ static const char *gfs2_acl_name(int type)
{
switch (type) {
case ACL_TYPE_ACCESS:
- return GFS2_POSIX_ACL_ACCESS;
+ return XATTR_POSIX_ACL_ACCESS;
case ACL_TYPE_DEFAULT:
- return GFS2_POSIX_ACL_DEFAULT;
+ return XATTR_POSIX_ACL_DEFAULT;
}
return NULL;
}
diff --git a/fs/gfs2/acl.h b/fs/gfs2/acl.h
index 2d65ec4cd4be..3af4f407a483 100644
--- a/fs/gfs2/acl.h
+++ b/fs/gfs2/acl.h
@@ -12,8 +12,6 @@
#include "incore.h"
-#define GFS2_POSIX_ACL_ACCESS "posix_acl_access"
-#define GFS2_POSIX_ACL_DEFAULT "posix_acl_default"
#define GFS2_ACL_MAX_ENTRIES(sdp) ((300 << (sdp)->sd_sb.sb_bsize_shift) >> 12)
extern struct posix_acl *gfs2_get_acl(struct inode *inode, int type);
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c
index 1caee0534587..93f07465e5a6 100644
--- a/fs/gfs2/aops.c
+++ b/fs/gfs2/aops.c
@@ -914,7 +914,7 @@ static int gfs2_write_end(struct file *file, struct address_space *mapping,
failed:
gfs2_trans_end(sdp);
gfs2_inplace_release(ip);
- if (ip->i_res->rs_qa_qd_num)
+ if (ip->i_qadata && ip->i_qadata->qa_qd_num)
gfs2_quota_unlock(ip);
if (inode == sdp->sd_rindex) {
gfs2_glock_dq(&m_ip->i_gh);
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index 61296ecbd0e2..0860f0b5b3f1 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -787,8 +787,8 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh,
if (error)
goto out_rlist;
- if (gfs2_rs_active(ip->i_res)) /* needs to be done with the rgrp glock held */
- gfs2_rs_deltree(ip->i_res);
+ if (gfs2_rs_active(&ip->i_res)) /* needs to be done with the rgrp glock held */
+ gfs2_rs_deltree(&ip->i_res);
error = gfs2_trans_begin(sdp, rg_blocks + RES_DINODE +
RES_INDIRECT + RES_STATFS + RES_QUOTA,
@@ -1291,13 +1291,9 @@ int gfs2_setattr_size(struct inode *inode, u64 newsize)
if (ret)
return ret;
- ret = get_write_access(inode);
- if (ret)
- return ret;
-
inode_dio_wait(inode);
- ret = gfs2_rs_alloc(ip);
+ ret = gfs2_rsqa_alloc(ip);
if (ret)
goto out;
@@ -1307,10 +1303,9 @@ int gfs2_setattr_size(struct inode *inode, u64 newsize)
goto out;
}
- gfs2_rs_deltree(ip->i_res);
ret = do_shrink(inode, oldsize, newsize);
out:
- put_write_access(inode);
+ gfs2_rsqa_delete(ip, NULL);
return ret;
}
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index ad8a5b757cc7..6a92592304fb 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -82,6 +82,8 @@
#define gfs2_disk_hash2offset(h) (((u64)(h)) >> 1)
#define gfs2_dir_offset2hash(p) ((u32)(((u64)(p)) << 1))
+#define GFS2_HASH_INDEX_MASK 0xffffc000
+#define GFS2_USE_HASH_FLAG 0x2000
struct qstr gfs2_qdot __read_mostly;
struct qstr gfs2_qdotdot __read_mostly;
@@ -108,7 +110,7 @@ static int gfs2_dir_get_existing_buffer(struct gfs2_inode *ip, u64 block,
struct buffer_head *bh;
int error;
- error = gfs2_meta_read(ip->i_gl, block, DIO_WAIT, &bh);
+ error = gfs2_meta_read(ip->i_gl, block, DIO_WAIT, 0, &bh);
if (error)
return error;
if (gfs2_metatype_check(GFS2_SB(&ip->i_inode), bh, GFS2_METATYPE_JD)) {
@@ -305,7 +307,7 @@ static int gfs2_dir_read_data(struct gfs2_inode *ip, __be64 *buf,
BUG_ON(extlen < 1);
bh = gfs2_meta_ra(ip->i_gl, dblock, extlen);
} else {
- error = gfs2_meta_read(ip->i_gl, dblock, DIO_WAIT, &bh);
+ error = gfs2_meta_read(ip->i_gl, dblock, DIO_WAIT, 0, &bh);
if (error)
goto fail;
}
@@ -443,6 +445,27 @@ static int gfs2_dirent_last(const struct gfs2_dirent *dent,
return 0;
}
+/* Look for the dirent that contains the offset specified in data. Once we
+ * find that dirent, there must be space available there for the new dirent */
+static int gfs2_dirent_find_offset(const struct gfs2_dirent *dent,
+ const struct qstr *name,
+ void *ptr)
+{
+ unsigned required = GFS2_DIRENT_SIZE(name->len);
+ unsigned actual = GFS2_DIRENT_SIZE(be16_to_cpu(dent->de_name_len));
+ unsigned totlen = be16_to_cpu(dent->de_rec_len);
+
+ if (ptr < (void *)dent || ptr >= (void *)dent + totlen)
+ return 0;
+ if (gfs2_dirent_sentinel(dent))
+ actual = 0;
+ if (ptr < (void *)dent + actual)
+ return -1;
+ if ((void *)dent + totlen >= ptr + required)
+ return 1;
+ return -1;
+}
+
static int gfs2_dirent_find_space(const struct gfs2_dirent *dent,
const struct qstr *name,
void *opaque)
@@ -682,6 +705,27 @@ static void dirent_del(struct gfs2_inode *dip, struct buffer_head *bh,
prev->de_rec_len = cpu_to_be16(prev_rec_len);
}
+
+static struct gfs2_dirent *do_init_dirent(struct inode *inode,
+ struct gfs2_dirent *dent,
+ const struct qstr *name,
+ struct buffer_head *bh,
+ unsigned offset)
+{
+ struct gfs2_inode *ip = GFS2_I(inode);
+ struct gfs2_dirent *ndent;
+ unsigned totlen;
+
+ totlen = be16_to_cpu(dent->de_rec_len);
+ BUG_ON(offset + name->len > totlen);
+ gfs2_trans_add_meta(ip->i_gl, bh);
+ ndent = (struct gfs2_dirent *)((char *)dent + offset);
+ dent->de_rec_len = cpu_to_be16(offset);
+ gfs2_qstr2dirent(name, totlen - offset, ndent);
+ return ndent;
+}
+
+
/*
* Takes a dent from which to grab space as an argument. Returns the
* newly created dent.
@@ -691,31 +735,25 @@ static struct gfs2_dirent *gfs2_init_dirent(struct inode *inode,
const struct qstr *name,
struct buffer_head *bh)
{
- struct gfs2_inode *ip = GFS2_I(inode);
- struct gfs2_dirent *ndent;
- unsigned offset = 0, totlen;
+ unsigned offset = 0;
if (!gfs2_dirent_sentinel(dent))
offset = GFS2_DIRENT_SIZE(be16_to_cpu(dent->de_name_len));
- totlen = be16_to_cpu(dent->de_rec_len);
- BUG_ON(offset + name->len > totlen);
- gfs2_trans_add_meta(ip->i_gl, bh);
- ndent = (struct gfs2_dirent *)((char *)dent + offset);
- dent->de_rec_len = cpu_to_be16(offset);
- gfs2_qstr2dirent(name, totlen - offset, ndent);
- return ndent;
+ return do_init_dirent(inode, dent, name, bh, offset);
}
-static struct gfs2_dirent *gfs2_dirent_alloc(struct inode *inode,
- struct buffer_head *bh,
- const struct qstr *name)
+static struct gfs2_dirent *gfs2_dirent_split_alloc(struct inode *inode,
+ struct buffer_head *bh,
+ const struct qstr *name,
+ void *ptr)
{
struct gfs2_dirent *dent;
dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size,
- gfs2_dirent_find_space, name, NULL);
+ gfs2_dirent_find_offset, name, ptr);
if (!dent || IS_ERR(dent))
return dent;
- return gfs2_init_dirent(inode, dent, name, bh);
+ return do_init_dirent(inode, dent, name, bh,
+ (unsigned)(ptr - (void *)dent));
}
static int get_leaf(struct gfs2_inode *dip, u64 leaf_no,
@@ -723,7 +761,7 @@ static int get_leaf(struct gfs2_inode *dip, u64 leaf_no,
{
int error;
- error = gfs2_meta_read(dip->i_gl, leaf_no, DIO_WAIT, bhp);
+ error = gfs2_meta_read(dip->i_gl, leaf_no, DIO_WAIT, 0, bhp);
if (!error && gfs2_metatype_check(GFS2_SB(&dip->i_inode), *bhp, GFS2_METATYPE_LF)) {
/* pr_info("block num=%llu\n", leaf_no); */
error = -EIO;
@@ -1051,10 +1089,11 @@ static int dir_split_leaf(struct inode *inode, const struct qstr *name)
if (!gfs2_dirent_sentinel(dent) &&
be32_to_cpu(dent->de_hash) < divider) {
struct qstr str;
+ void *ptr = ((char *)dent - obh->b_data) + nbh->b_data;
str.name = (char*)(dent+1);
str.len = be16_to_cpu(dent->de_name_len);
str.hash = be32_to_cpu(dent->de_hash);
- new = gfs2_dirent_alloc(inode, nbh, &str);
+ new = gfs2_dirent_split_alloc(inode, nbh, &str, ptr);
if (IS_ERR(new)) {
error = PTR_ERR(new);
break;
@@ -1186,10 +1225,10 @@ static int compare_dents(const void *a, const void *b)
int ret = 0;
dent_a = *(const struct gfs2_dirent **)a;
- hash_a = be32_to_cpu(dent_a->de_hash);
+ hash_a = dent_a->de_cookie;
dent_b = *(const struct gfs2_dirent **)b;
- hash_b = be32_to_cpu(dent_b->de_hash);
+ hash_b = dent_b->de_cookie;
if (hash_a > hash_b)
ret = 1;
@@ -1227,19 +1266,20 @@ static int compare_dents(const void *a, const void *b)
*/
static int do_filldir_main(struct gfs2_inode *dip, struct dir_context *ctx,
- const struct gfs2_dirent **darr, u32 entries,
- int *copied)
+ struct gfs2_dirent **darr, u32 entries,
+ u32 sort_start, int *copied)
{
const struct gfs2_dirent *dent, *dent_next;
u64 off, off_next;
unsigned int x, y;
int run = 0;
- sort(darr, entries, sizeof(struct gfs2_dirent *), compare_dents, NULL);
+ if (sort_start < entries)
+ sort(&darr[sort_start], entries - sort_start,
+ sizeof(struct gfs2_dirent *), compare_dents, NULL);
dent_next = darr[0];
- off_next = be32_to_cpu(dent_next->de_hash);
- off_next = gfs2_disk_hash2offset(off_next);
+ off_next = dent_next->de_cookie;
for (x = 0, y = 1; x < entries; x++, y++) {
dent = dent_next;
@@ -1247,8 +1287,7 @@ static int do_filldir_main(struct gfs2_inode *dip, struct dir_context *ctx,
if (y < entries) {
dent_next = darr[y];
- off_next = be32_to_cpu(dent_next->de_hash);
- off_next = gfs2_disk_hash2offset(off_next);
+ off_next = dent_next->de_cookie;
if (off < ctx->pos)
continue;
@@ -1295,6 +1334,40 @@ static void *gfs2_alloc_sort_buffer(unsigned size)
return ptr;
}
+
+static int gfs2_set_cookies(struct gfs2_sbd *sdp, struct buffer_head *bh,
+ unsigned leaf_nr, struct gfs2_dirent **darr,
+ unsigned entries)
+{
+ int sort_id = -1;
+ int i;
+
+ for (i = 0; i < entries; i++) {
+ unsigned offset;
+
+ darr[i]->de_cookie = be32_to_cpu(darr[i]->de_hash);
+ darr[i]->de_cookie = gfs2_disk_hash2offset(darr[i]->de_cookie);
+
+ if (!sdp->sd_args.ar_loccookie)
+ continue;
+ offset = (char *)(darr[i]) -
+ (bh->b_data + gfs2_dirent_offset(bh->b_data));
+ offset /= GFS2_MIN_DIRENT_SIZE;
+ offset += leaf_nr * sdp->sd_max_dents_per_leaf;
+ if (offset >= GFS2_USE_HASH_FLAG ||
+ leaf_nr >= GFS2_USE_HASH_FLAG) {
+ darr[i]->de_cookie |= GFS2_USE_HASH_FLAG;
+ if (sort_id < 0)
+ sort_id = i;
+ continue;
+ }
+ darr[i]->de_cookie &= GFS2_HASH_INDEX_MASK;
+ darr[i]->de_cookie |= offset;
+ }
+ return sort_id;
+}
+
+
static int gfs2_dir_read_leaf(struct inode *inode, struct dir_context *ctx,
int *copied, unsigned *depth,
u64 leaf_no)
@@ -1304,12 +1377,11 @@ static int gfs2_dir_read_leaf(struct inode *inode, struct dir_context *ctx,
struct buffer_head *bh;
struct gfs2_leaf *lf;
unsigned entries = 0, entries2 = 0;
- unsigned leaves = 0;
- const struct gfs2_dirent **darr, *dent;
+ unsigned leaves = 0, leaf = 0, offset, sort_offset;
+ struct gfs2_dirent **darr, *dent;
struct dirent_gather g;
struct buffer_head **larr;
- int leaf = 0;
- int error, i;
+ int error, i, need_sort = 0, sort_id;
u64 lfn = leaf_no;
do {
@@ -1325,6 +1397,11 @@ static int gfs2_dir_read_leaf(struct inode *inode, struct dir_context *ctx,
brelse(bh);
} while(lfn);
+ if (*depth < GFS2_DIR_MAX_DEPTH || !sdp->sd_args.ar_loccookie) {
+ need_sort = 1;
+ sort_offset = 0;
+ }
+
if (!entries)
return 0;
@@ -1338,8 +1415,8 @@ static int gfs2_dir_read_leaf(struct inode *inode, struct dir_context *ctx,
larr = gfs2_alloc_sort_buffer((leaves + entries + 99) * sizeof(void *));
if (!larr)
goto out;
- darr = (const struct gfs2_dirent **)(larr + leaves);
- g.pdent = darr;
+ darr = (struct gfs2_dirent **)(larr + leaves);
+ g.pdent = (const struct gfs2_dirent **)darr;
g.offset = 0;
lfn = leaf_no;
@@ -1350,6 +1427,7 @@ static int gfs2_dir_read_leaf(struct inode *inode, struct dir_context *ctx,
lf = (struct gfs2_leaf *)bh->b_data;
lfn = be64_to_cpu(lf->lf_next);
if (lf->lf_entries) {
+ offset = g.offset;
entries2 += be16_to_cpu(lf->lf_entries);
dent = gfs2_dirent_scan(inode, bh->b_data, bh->b_size,
gfs2_dirent_gather, NULL, &g);
@@ -1367,17 +1445,26 @@ static int gfs2_dir_read_leaf(struct inode *inode, struct dir_context *ctx,
goto out_free;
}
error = 0;
+ sort_id = gfs2_set_cookies(sdp, bh, leaf, &darr[offset],
+ be16_to_cpu(lf->lf_entries));
+ if (!need_sort && sort_id >= 0) {
+ need_sort = 1;
+ sort_offset = offset + sort_id;
+ }
larr[leaf++] = bh;
} else {
+ larr[leaf++] = NULL;
brelse(bh);
}
} while(lfn);
BUG_ON(entries2 != entries);
- error = do_filldir_main(ip, ctx, darr, entries, copied);
+ error = do_filldir_main(ip, ctx, darr, entries, need_sort ?
+ sort_offset : entries, copied);
out_free:
for(i = 0; i < leaf; i++)
- brelse(larr[i]);
+ if (larr[i])
+ brelse(larr[i]);
kvfree(larr);
out:
return error;
@@ -1483,7 +1570,7 @@ int gfs2_dir_read(struct inode *inode, struct dir_context *ctx,
struct gfs2_inode *dip = GFS2_I(inode);
struct gfs2_sbd *sdp = GFS2_SB(inode);
struct dirent_gather g;
- const struct gfs2_dirent **darr, *dent;
+ struct gfs2_dirent **darr, *dent;
struct buffer_head *dibh;
int copied = 0;
int error;
@@ -1507,7 +1594,7 @@ int gfs2_dir_read(struct inode *inode, struct dir_context *ctx,
/* 96 is max number of dirents which can be stuffed into an inode */
darr = kmalloc(96 * sizeof(struct gfs2_dirent *), GFP_NOFS);
if (darr) {
- g.pdent = darr;
+ g.pdent = (const struct gfs2_dirent **)darr;
g.offset = 0;
dent = gfs2_dirent_scan(inode, dibh->b_data, dibh->b_size,
gfs2_dirent_gather, NULL, &g);
@@ -1524,8 +1611,9 @@ int gfs2_dir_read(struct inode *inode, struct dir_context *ctx,
error = -EIO;
goto out;
}
+ gfs2_set_cookies(sdp, dibh, 0, darr, dip->i_entries);
error = do_filldir_main(dip, ctx, darr,
- dip->i_entries, &copied);
+ dip->i_entries, 0, &copied);
out:
kfree(darr);
}
@@ -1560,15 +1648,22 @@ struct inode *gfs2_dir_search(struct inode *dir, const struct qstr *name,
dent = gfs2_dirent_search(dir, name, gfs2_dirent_find, &bh);
if (dent) {
+ struct inode *inode;
+ u16 rahead;
+
if (IS_ERR(dent))
return ERR_CAST(dent);
dtype = be16_to_cpu(dent->de_type);
+ rahead = be16_to_cpu(dent->de_rahead);
addr = be64_to_cpu(dent->de_inum.no_addr);
formal_ino = be64_to_cpu(dent->de_inum.no_formal_ino);
brelse(bh);
if (fail_on_exist)
return ERR_PTR(-EEXIST);
- return gfs2_inode_lookup(dir->i_sb, dtype, addr, formal_ino, 0);
+ inode = gfs2_inode_lookup(dir->i_sb, dtype, addr, formal_ino, 0);
+ if (!IS_ERR(inode))
+ GFS2_I(inode)->i_rahead = rahead;
+ return inode;
}
return ERR_PTR(-ENOENT);
}
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index 5e425469f0c2..7412863cda1e 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -298,9 +298,9 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
gfsflags &= ~GFS2_DIF_TOPDIR;
if (gfsflags & GFS2_DIF_INHERIT_JDATA)
gfsflags ^= (GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA);
- return do_gfs2_set_flags(filp, gfsflags, ~0);
+ return do_gfs2_set_flags(filp, gfsflags, ~GFS2_DIF_SYSTEM);
}
- return do_gfs2_set_flags(filp, gfsflags, ~GFS2_DIF_JDATA);
+ return do_gfs2_set_flags(filp, gfsflags, ~(GFS2_DIF_SYSTEM | GFS2_DIF_JDATA));
}
static long gfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
@@ -336,8 +336,8 @@ static void gfs2_size_hint(struct file *filep, loff_t offset, size_t size)
size_t blks = (size + sdp->sd_sb.sb_bsize - 1) >> sdp->sd_sb.sb_bsize_shift;
int hint = min_t(size_t, INT_MAX, blks);
- if (hint > atomic_read(&ip->i_res->rs_sizehint))
- atomic_set(&ip->i_res->rs_sizehint, hint);
+ if (hint > atomic_read(&ip->i_res.rs_sizehint))
+ atomic_set(&ip->i_res.rs_sizehint, hint);
}
/**
@@ -397,14 +397,10 @@ static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
/* Update file times before taking page lock */
file_update_time(vma->vm_file);
- ret = get_write_access(inode);
+ ret = gfs2_rsqa_alloc(ip);
if (ret)
goto out;
- ret = gfs2_rs_alloc(ip);
- if (ret)
- goto out_write_access;
-
gfs2_size_hint(vma->vm_file, pos, PAGE_CACHE_SIZE);
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
@@ -486,8 +482,6 @@ out_uninit:
set_page_dirty(page);
wait_for_stable_page(page);
}
-out_write_access:
- put_write_access(inode);
out:
sb_end_pagefault(inode->i_sb);
return block_page_mkwrite_return(ret);
@@ -623,7 +617,7 @@ static int gfs2_release(struct inode *inode, struct file *file)
if (!(file->f_mode & FMODE_WRITE))
return 0;
- gfs2_rs_delete(ip, &inode->i_writecount);
+ gfs2_rsqa_delete(ip, &inode->i_writecount);
return 0;
}
@@ -703,7 +697,7 @@ static ssize_t gfs2_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
struct gfs2_inode *ip = GFS2_I(file_inode(file));
int ret;
- ret = gfs2_rs_alloc(ip);
+ ret = gfs2_rsqa_alloc(ip);
if (ret)
return ret;
@@ -938,13 +932,14 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t le
if (ret)
goto out_unlock;
- ret = gfs2_rs_alloc(ip);
+ ret = gfs2_rsqa_alloc(ip);
if (ret)
goto out_putw;
ret = __gfs2_fallocate(file, mode, offset, len);
if (ret)
- gfs2_rs_deltree(ip->i_res);
+ gfs2_rs_deltree(&ip->i_res);
+
out_putw:
put_write_access(inode);
out_unlock:
@@ -962,7 +957,7 @@ static ssize_t gfs2_file_splice_write(struct pipe_inode_info *pipe,
int error;
struct gfs2_inode *ip = GFS2_I(out->f_mapping->host);
- error = gfs2_rs_alloc(ip);
+ error = gfs2_rsqa_alloc(ip);
if (error)
return (ssize_t)error;
@@ -1018,7 +1013,7 @@ static int do_flock(struct file *file, int cmd, struct file_lock *fl)
struct gfs2_inode *ip = GFS2_I(file_inode(file));
struct gfs2_glock *gl;
unsigned int state;
- int flags;
+ u16 flags;
int error = 0;
int sleeptime;
@@ -1032,7 +1027,10 @@ static int do_flock(struct file *file, int cmd, struct file_lock *fl)
if (fl_gh->gh_state == state)
goto out;
locks_lock_file_wait(file,
- &(struct file_lock){.fl_type = F_UNLCK});
+ &(struct file_lock) {
+ .fl_type = F_UNLCK,
+ .fl_flags = FL_FLOCK
+ });
gfs2_glock_dq(fl_gh);
gfs2_holder_reinit(state, flags, fl_gh);
} else {
diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index 32e74710b1aa..a4ff7b56f5cd 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -446,7 +446,7 @@ __acquires(&gl->gl_lockref.lock)
{
const struct gfs2_glock_operations *glops = gl->gl_ops;
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
- unsigned int lck_flags = gh ? gh->gh_flags : 0;
+ unsigned int lck_flags = (unsigned int)(gh ? gh->gh_flags : 0);
int ret;
lck_flags &= (LM_FLAG_TRY | LM_FLAG_TRY_1CB | LM_FLAG_NOEXP |
@@ -750,7 +750,7 @@ again:
*
*/
-void gfs2_holder_init(struct gfs2_glock *gl, unsigned int state, unsigned flags,
+void gfs2_holder_init(struct gfs2_glock *gl, unsigned int state, u16 flags,
struct gfs2_holder *gh)
{
INIT_LIST_HEAD(&gh->gh_list);
@@ -774,7 +774,7 @@ void gfs2_holder_init(struct gfs2_glock *gl, unsigned int state, unsigned flags,
*
*/
-void gfs2_holder_reinit(unsigned int state, unsigned flags, struct gfs2_holder *gh)
+void gfs2_holder_reinit(unsigned int state, u16 flags, struct gfs2_holder *gh)
{
gh->gh_state = state;
gh->gh_flags = flags;
@@ -1080,7 +1080,7 @@ void gfs2_glock_dq_uninit(struct gfs2_holder *gh)
int gfs2_glock_nq_num(struct gfs2_sbd *sdp, u64 number,
const struct gfs2_glock_operations *glops,
- unsigned int state, int flags, struct gfs2_holder *gh)
+ unsigned int state, u16 flags, struct gfs2_holder *gh)
{
struct gfs2_glock *gl;
int error;
@@ -1417,14 +1417,14 @@ static struct shrinker glock_shrinker = {
static void glock_hash_walk(glock_examiner examiner, const struct gfs2_sbd *sdp)
{
struct gfs2_glock *gl;
- struct rhash_head *pos, *next;
+ struct rhash_head *pos;
const struct bucket_table *tbl;
int i;
rcu_read_lock();
tbl = rht_dereference_rcu(gl_hash_table.tbl, &gl_hash_table);
for (i = 0; i < tbl->size; i++) {
- rht_for_each_entry_safe(gl, pos, next, tbl, i, gl_node) {
+ rht_for_each_entry_rcu(gl, pos, tbl, i, gl_node) {
if ((gl->gl_name.ln_sbd == sdp) &&
lockref_get_not_dead(&gl->gl_lockref))
examiner(gl);
@@ -1506,7 +1506,9 @@ void gfs2_gl_hash_clear(struct gfs2_sbd *sdp)
flush_workqueue(glock_workqueue);
glock_hash_walk(clear_glock, sdp);
flush_workqueue(glock_workqueue);
- wait_event(sdp->sd_glock_wait, atomic_read(&sdp->sd_glock_disposal) == 0);
+ wait_event_timeout(sdp->sd_glock_wait,
+ atomic_read(&sdp->sd_glock_disposal) == 0,
+ HZ * 600);
glock_hash_walk(dump_glock_func, sdp);
}
@@ -1539,7 +1541,7 @@ static const char *state2str(unsigned state)
return "??";
}
-static const char *hflags2str(char *buf, unsigned flags, unsigned long iflags)
+static const char *hflags2str(char *buf, u16 flags, unsigned long iflags)
{
char *p = buf;
if (flags & LM_FLAG_TRY)
diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h
index f7cdaa8b4c83..46ab67fc16da 100644
--- a/fs/gfs2/glock.h
+++ b/fs/gfs2/glock.h
@@ -79,15 +79,15 @@ enum {
* requested had acquired and released the lock.
*/
-#define LM_FLAG_TRY 0x00000001
-#define LM_FLAG_TRY_1CB 0x00000002
-#define LM_FLAG_NOEXP 0x00000004
-#define LM_FLAG_ANY 0x00000008
-#define LM_FLAG_PRIORITY 0x00000010
-#define GL_ASYNC 0x00000040
-#define GL_EXACT 0x00000080
-#define GL_SKIP 0x00000100
-#define GL_NOCACHE 0x00000400
+#define LM_FLAG_TRY 0x0001
+#define LM_FLAG_TRY_1CB 0x0002
+#define LM_FLAG_NOEXP 0x0004
+#define LM_FLAG_ANY 0x0008
+#define LM_FLAG_PRIORITY 0x0010
+#define GL_ASYNC 0x0040
+#define GL_EXACT 0x0080
+#define GL_SKIP 0x0100
+#define GL_NOCACHE 0x0400
/*
* lm_async_cb return flags
@@ -183,8 +183,8 @@ extern int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number,
int create, struct gfs2_glock **glp);
extern void gfs2_glock_put(struct gfs2_glock *gl);
extern void gfs2_holder_init(struct gfs2_glock *gl, unsigned int state,
- unsigned flags, struct gfs2_holder *gh);
-extern void gfs2_holder_reinit(unsigned int state, unsigned flags,
+ u16 flags, struct gfs2_holder *gh);
+extern void gfs2_holder_reinit(unsigned int state, u16 flags,
struct gfs2_holder *gh);
extern void gfs2_holder_uninit(struct gfs2_holder *gh);
extern int gfs2_glock_nq(struct gfs2_holder *gh);
@@ -195,7 +195,7 @@ extern void gfs2_glock_dq_wait(struct gfs2_holder *gh);
extern void gfs2_glock_dq_uninit(struct gfs2_holder *gh);
extern int gfs2_glock_nq_num(struct gfs2_sbd *sdp, u64 number,
const struct gfs2_glock_operations *glops,
- unsigned int state, int flags,
+ unsigned int state, u16 flags,
struct gfs2_holder *gh);
extern int gfs2_glock_nq_m(unsigned int num_gh, struct gfs2_holder *ghs);
extern void gfs2_glock_dq_m(unsigned int num_gh, struct gfs2_holder *ghs);
@@ -215,7 +215,7 @@ void gfs2_print_dbg(struct seq_file *seq, const char *fmt, ...);
*/
static inline int gfs2_glock_nq_init(struct gfs2_glock *gl,
- unsigned int state, int flags,
+ unsigned int state, u16 flags,
struct gfs2_holder *gh)
{
int error;
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index de7b4f97ac75..845fb09cc606 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -259,8 +259,8 @@ struct gfs2_holder {
struct gfs2_glock *gh_gl;
struct pid *gh_owner_pid;
- unsigned int gh_state;
- unsigned gh_flags;
+ u16 gh_flags;
+ u16 gh_state;
int gh_error;
unsigned long gh_iflags; /* HIF_... */
@@ -270,6 +270,13 @@ struct gfs2_holder {
/* Number of quota types we support */
#define GFS2_MAXQUOTAS 2
+struct gfs2_qadata { /* quota allocation data */
+ /* Quota stuff */
+ struct gfs2_quota_data *qa_qd[2 * GFS2_MAXQUOTAS];
+ struct gfs2_holder qa_qd_ghs[2 * GFS2_MAXQUOTAS];
+ unsigned int qa_qd_num;
+};
+
/* Resource group multi-block reservation, in order of appearance:
Step 1. Function prepares to write, allocates a mb, sets the size hint.
@@ -288,11 +295,6 @@ struct gfs2_blkreserv {
struct gfs2_rbm rs_rbm; /* Start of reservation */
u32 rs_free; /* how many blocks are still free */
u64 rs_inum; /* Inode number for reservation */
-
- /* ancillary quota stuff */
- struct gfs2_quota_data *rs_qa_qd[2 * GFS2_MAXQUOTAS];
- struct gfs2_holder rs_qa_qd_ghs[2 * GFS2_MAXQUOTAS];
- unsigned int rs_qa_qd_num;
};
/*
@@ -391,7 +393,8 @@ struct gfs2_inode {
struct gfs2_glock *i_gl; /* Move into i_gh? */
struct gfs2_holder i_iopen_gh;
struct gfs2_holder i_gh; /* for prepare/commit_write only */
- struct gfs2_blkreserv *i_res; /* rgrp multi-block reservation */
+ struct gfs2_qadata *i_qadata; /* quota allocation data */
+ struct gfs2_blkreserv i_res; /* rgrp multi-block reservation */
struct gfs2_rgrpd *i_rgd;
u64 i_goal; /* goal block for allocations */
struct rw_semaphore i_rw_mutex;
@@ -402,6 +405,7 @@ struct gfs2_inode {
u32 i_diskflags;
u8 i_height;
u8 i_depth;
+ u16 i_rahead;
};
/*
@@ -558,6 +562,8 @@ struct gfs2_args {
unsigned int ar_errors:2; /* errors=withdraw | panic */
unsigned int ar_nobarrier:1; /* do not send barriers */
unsigned int ar_rgrplvb:1; /* use lvbs for rgrp info */
+ unsigned int ar_loccookie:1; /* use location based readdir
+ cookies */
int ar_commit; /* Commit interval */
int ar_statfs_quantum; /* The fast statfs interval */
int ar_quota_quantum; /* The quota interval */
@@ -685,6 +691,7 @@ struct gfs2_sbd {
u64 sd_heightsize[GFS2_MAX_META_HEIGHT + 1];
u32 sd_max_jheight; /* Max height of journaled file's meta tree */
u64 sd_jheightsize[GFS2_MAX_META_HEIGHT + 1];
+ u32 sd_max_dents_per_leaf; /* Max number of dirents in a leaf block */
struct gfs2_args sd_args; /* Mount arguments */
struct gfs2_tune sd_tune; /* Filesystem tuning structure */
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 063fdfcf8275..3e94400d587c 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -191,13 +191,13 @@ struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned int type,
fail_refresh:
ip->i_iopen_gh.gh_flags |= GL_NOCACHE;
ip->i_iopen_gh.gh_gl->gl_object = NULL;
- gfs2_glock_dq_uninit(&ip->i_iopen_gh);
+ gfs2_glock_dq_wait(&ip->i_iopen_gh);
+ gfs2_holder_uninit(&ip->i_iopen_gh);
fail_iopen:
if (io_gl)
gfs2_glock_put(io_gl);
fail_put:
ip->i_gl->gl_object = NULL;
- gfs2_glock_put(ip->i_gl);
fail:
iget_failed(inode);
return ERR_PTR(error);
@@ -593,7 +593,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
struct gfs2_inode *dip = GFS2_I(dir), *ip;
struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
struct gfs2_glock *io_gl;
- int error, free_vfs_inode = 0;
+ int error, free_vfs_inode = 1;
u32 aflags = 0;
unsigned blocks = 1;
struct gfs2_diradd da = { .bh = NULL, .save_loc = 1, };
@@ -601,7 +601,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
if (!name->len || name->len > GFS2_FNAMESIZE)
return -ENAMETOOLONG;
- error = gfs2_rs_alloc(dip);
+ error = gfs2_rsqa_alloc(dip);
if (error)
return error;
@@ -650,10 +650,10 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
error = posix_acl_create(dir, &mode, &default_acl, &acl);
if (error)
- goto fail_free_vfs_inode;
+ goto fail_gunlock;
ip = GFS2_I(inode);
- error = gfs2_rs_alloc(ip);
+ error = gfs2_rsqa_alloc(ip);
if (error)
goto fail_free_acls;
@@ -685,6 +685,11 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
ip->i_entries = 2;
break;
}
+
+ /* Force SYSTEM flag on all files and subdirs of a SYSTEM directory */
+ if (dip->i_diskflags & GFS2_DIF_SYSTEM)
+ ip->i_diskflags |= GFS2_DIF_SYSTEM;
+
gfs2_set_inode_flags(inode);
if ((GFS2_I(d_inode(sdp->sd_root_dir)) == dip) ||
@@ -733,6 +738,9 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
gfs2_set_iop(inode);
insert_inode_hash(inode);
+ free_vfs_inode = 0; /* After this point, the inode is no longer
+ considered free. Any failures need to undo
+ the gfs2 structures. */
if (default_acl) {
error = gfs2_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
posix_acl_release(default_acl);
@@ -766,24 +774,19 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
return error;
fail_gunlock3:
- gfs2_glock_dq_uninit(ghs + 1);
- if (ip->i_gl)
- gfs2_glock_put(ip->i_gl);
- goto fail_gunlock;
-
+ gfs2_glock_dq_uninit(&ip->i_iopen_gh);
+ gfs2_glock_put(io_gl);
fail_gunlock2:
gfs2_glock_dq_uninit(ghs + 1);
fail_free_inode:
if (ip->i_gl)
gfs2_glock_put(ip->i_gl);
- gfs2_rs_delete(ip, NULL);
+ gfs2_rsqa_delete(ip, NULL);
fail_free_acls:
if (default_acl)
posix_acl_release(default_acl);
if (acl)
posix_acl_release(acl);
-fail_free_vfs_inode:
- free_vfs_inode = 1;
fail_gunlock:
gfs2_dir_no_add(&da);
gfs2_glock_dq_uninit(ghs);
@@ -898,7 +901,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
if (S_ISDIR(inode->i_mode))
return -EPERM;
- error = gfs2_rs_alloc(dip);
+ error = gfs2_rsqa_alloc(dip);
if (error)
return error;
@@ -1371,7 +1374,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
if (error)
return error;
- error = gfs2_rs_alloc(ndip);
+ error = gfs2_rsqa_alloc(ndip);
if (error)
return error;
@@ -1712,24 +1715,30 @@ static int gfs2_rename2(struct inode *odir, struct dentry *odentry,
}
/**
- * gfs2_follow_link - Follow a symbolic link
+ * gfs2_get_link - Follow a symbolic link
* @dentry: The dentry of the link
- * @nd: Data that we pass to vfs_follow_link()
+ * @inode: The inode of the link
+ * @done: destructor for return value
*
* This can handle symlinks of any size.
*
* Returns: 0 on success or error code
*/
-static const char *gfs2_follow_link(struct dentry *dentry, void **cookie)
+static const char *gfs2_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
- struct gfs2_inode *ip = GFS2_I(d_inode(dentry));
+ struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder i_gh;
struct buffer_head *dibh;
unsigned int size;
char *buf;
int error;
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
+
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &i_gh);
error = gfs2_glock_nq(&i_gh);
if (error) {
@@ -1759,7 +1768,7 @@ static const char *gfs2_follow_link(struct dentry *dentry, void **cookie)
out:
gfs2_glock_dq_uninit(&i_gh);
if (!IS_ERR(buf))
- *cookie = buf;
+ set_delayed_call(done, kfree_link, buf);
return buf;
}
@@ -1854,11 +1863,7 @@ static int setattr_chown(struct inode *inode, struct iattr *attr)
if (!(attr->ia_valid & ATTR_GID) || gid_eq(ogid, ngid))
ogid = ngid = NO_GID_QUOTA_CHANGE;
- error = get_write_access(inode);
- if (error)
- return error;
-
- error = gfs2_rs_alloc(ip);
+ error = gfs2_rsqa_alloc(ip);
if (error)
goto out;
@@ -1898,7 +1903,6 @@ out_end_trans:
out_gunlock_q:
gfs2_quota_unlock(ip);
out:
- put_write_access(inode);
return error;
}
@@ -1920,7 +1924,7 @@ static int gfs2_setattr(struct dentry *dentry, struct iattr *attr)
struct gfs2_holder i_gh;
int error;
- error = gfs2_rs_alloc(ip);
+ error = gfs2_rsqa_alloc(ip);
if (error)
return error;
@@ -2002,7 +2006,7 @@ static int gfs2_setxattr(struct dentry *dentry, const char *name,
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
ret = gfs2_glock_nq(&gh);
if (ret == 0) {
- ret = gfs2_rs_alloc(ip);
+ ret = gfs2_rsqa_alloc(ip);
if (ret == 0)
ret = generic_setxattr(dentry, name, data, size, flags);
gfs2_glock_dq(&gh);
@@ -2043,7 +2047,7 @@ static int gfs2_removexattr(struct dentry *dentry, const char *name)
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
ret = gfs2_glock_nq(&gh);
if (ret == 0) {
- ret = gfs2_rs_alloc(ip);
+ ret = gfs2_rsqa_alloc(ip);
if (ret == 0)
ret = generic_removexattr(dentry, name);
gfs2_glock_dq(&gh);
@@ -2132,8 +2136,7 @@ const struct inode_operations gfs2_dir_iops = {
const struct inode_operations gfs2_symlink_iops = {
.readlink = generic_readlink,
- .follow_link = gfs2_follow_link,
- .put_link = kfree_put_link,
+ .get_link = gfs2_get_link,
.permission = gfs2_permission,
.setattr = gfs2_setattr,
.getattr = gfs2_getattr,
diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c
index 536e7a6252cd..0ff028c15199 100644
--- a/fs/gfs2/log.c
+++ b/fs/gfs2/log.c
@@ -716,6 +716,9 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl,
}
trace_gfs2_log_flush(sdp, 1);
+ if (type == SHUTDOWN_FLUSH)
+ clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
+
sdp->sd_log_flush_head = sdp->sd_log_head;
sdp->sd_log_flush_wrapped = 0;
tr = sdp->sd_log_tr;
diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c
index fb2b42cf46b5..f99f8e94de3f 100644
--- a/fs/gfs2/main.c
+++ b/fs/gfs2/main.c
@@ -41,7 +41,9 @@ static void gfs2_init_inode_once(void *foo)
inode_init_once(&ip->i_inode);
init_rwsem(&ip->i_rw_mutex);
INIT_LIST_HEAD(&ip->i_trunc_list);
- ip->i_res = NULL;
+ ip->i_qadata = NULL;
+ memset(&ip->i_res, 0, sizeof(ip->i_res));
+ RB_CLEAR_NODE(&ip->i_res.rs_node);
ip->i_hash_cache = NULL;
}
@@ -112,7 +114,8 @@ static int __init init_gfs2_fs(void)
gfs2_inode_cachep = kmem_cache_create("gfs2_inode",
sizeof(struct gfs2_inode),
0, SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD,
+ SLAB_MEM_SPREAD|
+ SLAB_ACCOUNT,
gfs2_init_inode_once);
if (!gfs2_inode_cachep)
goto fail;
@@ -135,10 +138,10 @@ static int __init init_gfs2_fs(void)
if (!gfs2_quotad_cachep)
goto fail;
- gfs2_rsrv_cachep = kmem_cache_create("gfs2_mblk",
- sizeof(struct gfs2_blkreserv),
+ gfs2_qadata_cachep = kmem_cache_create("gfs2_qadata",
+ sizeof(struct gfs2_qadata),
0, 0, NULL);
- if (!gfs2_rsrv_cachep)
+ if (!gfs2_qadata_cachep)
goto fail;
register_shrinker(&gfs2_qd_shrinker);
@@ -193,8 +196,8 @@ fail_lru:
unregister_shrinker(&gfs2_qd_shrinker);
gfs2_glock_exit();
- if (gfs2_rsrv_cachep)
- kmem_cache_destroy(gfs2_rsrv_cachep);
+ if (gfs2_qadata_cachep)
+ kmem_cache_destroy(gfs2_qadata_cachep);
if (gfs2_quotad_cachep)
kmem_cache_destroy(gfs2_quotad_cachep);
@@ -238,7 +241,7 @@ static void __exit exit_gfs2_fs(void)
rcu_barrier();
mempool_destroy(gfs2_page_pool);
- kmem_cache_destroy(gfs2_rsrv_cachep);
+ kmem_cache_destroy(gfs2_qadata_cachep);
kmem_cache_destroy(gfs2_quotad_cachep);
kmem_cache_destroy(gfs2_rgrpd_cachep);
kmem_cache_destroy(gfs2_bufdata_cachep);
diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
index 0e1d4be5865a..e137d96f1b17 100644
--- a/fs/gfs2/meta_io.c
+++ b/fs/gfs2/meta_io.c
@@ -187,6 +187,52 @@ struct buffer_head *gfs2_meta_new(struct gfs2_glock *gl, u64 blkno)
return bh;
}
+static void gfs2_meta_read_endio(struct bio *bio)
+{
+ struct bio_vec *bvec;
+ int i;
+
+ bio_for_each_segment_all(bvec, bio, i) {
+ struct page *page = bvec->bv_page;
+ struct buffer_head *bh = page_buffers(page);
+ unsigned int len = bvec->bv_len;
+
+ while (bh_offset(bh) < bvec->bv_offset)
+ bh = bh->b_this_page;
+ do {
+ struct buffer_head *next = bh->b_this_page;
+ len -= bh->b_size;
+ bh->b_end_io(bh, !bio->bi_error);
+ bh = next;
+ } while (bh && len);
+ }
+ bio_put(bio);
+}
+
+/*
+ * Submit several consecutive buffer head I/O requests as a single bio I/O
+ * request. (See submit_bh_wbc.)
+ */
+static void gfs2_submit_bhs(int rw, struct buffer_head *bhs[], int num)
+{
+ struct buffer_head *bh = bhs[0];
+ struct bio *bio;
+ int i;
+
+ if (!num)
+ return;
+
+ bio = bio_alloc(GFP_NOIO, num);
+ bio->bi_iter.bi_sector = bh->b_blocknr * (bh->b_size >> 9);
+ bio->bi_bdev = bh->b_bdev;
+ for (i = 0; i < num; i++) {
+ bh = bhs[i];
+ bio_add_page(bio, bh->b_page, bh->b_size, bh_offset(bh));
+ }
+ bio->bi_end_io = gfs2_meta_read_endio;
+ submit_bio(rw, bio);
+}
+
/**
* gfs2_meta_read - Read a block from disk
* @gl: The glock covering the block
@@ -198,10 +244,11 @@ struct buffer_head *gfs2_meta_new(struct gfs2_glock *gl, u64 blkno)
*/
int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
- struct buffer_head **bhp)
+ int rahead, struct buffer_head **bhp)
{
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
- struct buffer_head *bh;
+ struct buffer_head *bh, *bhs[2];
+ int num = 0;
if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) {
*bhp = NULL;
@@ -213,14 +260,31 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
lock_buffer(bh);
if (buffer_uptodate(bh)) {
unlock_buffer(bh);
- return 0;
+ flags &= ~DIO_WAIT;
+ } else {
+ bh->b_end_io = end_buffer_read_sync;
+ get_bh(bh);
+ bhs[num++] = bh;
}
- bh->b_end_io = end_buffer_read_sync;
- get_bh(bh);
- submit_bh(READ_SYNC | REQ_META | REQ_PRIO, bh);
+
+ if (rahead) {
+ bh = gfs2_getbuf(gl, blkno + 1, CREATE);
+
+ lock_buffer(bh);
+ if (buffer_uptodate(bh)) {
+ unlock_buffer(bh);
+ brelse(bh);
+ } else {
+ bh->b_end_io = end_buffer_read_sync;
+ bhs[num++] = bh;
+ }
+ }
+
+ gfs2_submit_bhs(READ_SYNC | REQ_META | REQ_PRIO, bhs, num);
if (!(flags & DIO_WAIT))
return 0;
+ bh = *bhp;
wait_on_buffer(bh);
if (unlikely(!buffer_uptodate(bh))) {
struct gfs2_trans *tr = current->journal_info;
@@ -341,8 +405,12 @@ int gfs2_meta_indirect_buffer(struct gfs2_inode *ip, int height, u64 num,
struct buffer_head *bh;
int ret = 0;
u32 mtype = height ? GFS2_METATYPE_IN : GFS2_METATYPE_DI;
+ int rahead = 0;
+
+ if (num == ip->i_no_addr)
+ rahead = ip->i_rahead;
- ret = gfs2_meta_read(gl, num, DIO_WAIT, &bh);
+ ret = gfs2_meta_read(gl, num, DIO_WAIT, rahead, &bh);
if (ret == 0 && gfs2_metatype_check(sdp, bh, mtype)) {
brelse(bh);
ret = -EIO;
diff --git a/fs/gfs2/meta_io.h b/fs/gfs2/meta_io.h
index 8ca161567a93..c5086c8af5ed 100644
--- a/fs/gfs2/meta_io.h
+++ b/fs/gfs2/meta_io.h
@@ -53,7 +53,7 @@ static inline struct gfs2_sbd *gfs2_mapping2sbd(struct address_space *mapping)
extern struct buffer_head *gfs2_meta_new(struct gfs2_glock *gl, u64 blkno);
extern int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
- struct buffer_head **bhp);
+ int rahead, struct buffer_head **bhp);
extern int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh);
extern struct buffer_head *gfs2_getbuf(struct gfs2_glock *gl, u64 blkno,
int create);
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
index baab99b69d8a..dbed9e243ea2 100644
--- a/fs/gfs2/ops_fstype.c
+++ b/fs/gfs2/ops_fstype.c
@@ -352,6 +352,9 @@ static int gfs2_read_sb(struct gfs2_sbd *sdp, int silent)
sdp->sd_jheightsize[x] = ~0;
gfs2_assert(sdp, sdp->sd_max_jheight <= GFS2_MAX_META_HEIGHT);
+ sdp->sd_max_dents_per_leaf = (sdp->sd_sb.sb_bsize -
+ sizeof(struct gfs2_leaf)) /
+ GFS2_MIN_DIRENT_SIZE;
return 0;
}
@@ -910,8 +913,7 @@ fail_qc_i:
fail_ut_i:
iput(sdp->sd_sc_inode);
fail:
- if (pn)
- iput(pn);
+ iput(pn);
return error;
}
@@ -1315,9 +1317,7 @@ static struct dentry *gfs2_mount(struct file_system_type *fs_type, int flags,
if ((flags ^ s->s_flags) & MS_RDONLY)
goto error_super;
} else {
- char b[BDEVNAME_SIZE];
-
- strlcpy(s->s_id, bdevname(bdev, b), sizeof(s->s_id));
+ snprintf(s->s_id, sizeof(s->s_id), "%pg", bdev);
sb_set_blocksize(s, block_size(bdev));
error = fill_super(s, &args, flags & MS_SILENT ? 1 : 0);
if (error)
diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c
index 3a31226531ea..be6d9c450b22 100644
--- a/fs/gfs2/quota.c
+++ b/fs/gfs2/quota.c
@@ -388,7 +388,7 @@ static int bh_get(struct gfs2_quota_data *qd)
error = gfs2_block_map(&ip->i_inode, block, &bh_map, 0);
if (error)
goto fail;
- error = gfs2_meta_read(ip->i_gl, bh_map.b_blocknr, DIO_WAIT, &bh);
+ error = gfs2_meta_read(ip->i_gl, bh_map.b_blocknr, DIO_WAIT, 0, &bh);
if (error)
goto fail;
error = -EIO;
@@ -527,37 +527,70 @@ static void qdsb_put(struct gfs2_quota_data *qd)
qd_put(qd);
}
+/**
+ * gfs2_qa_alloc - make sure we have a quota allocations data structure,
+ * if necessary
+ * @ip: the inode for this reservation
+ */
+int gfs2_qa_alloc(struct gfs2_inode *ip)
+{
+ int error = 0;
+ struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
+
+ if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF)
+ return 0;
+
+ down_write(&ip->i_rw_mutex);
+ if (ip->i_qadata == NULL) {
+ ip->i_qadata = kmem_cache_zalloc(gfs2_qadata_cachep, GFP_NOFS);
+ if (!ip->i_qadata)
+ error = -ENOMEM;
+ }
+ up_write(&ip->i_rw_mutex);
+ return error;
+}
+
+void gfs2_qa_delete(struct gfs2_inode *ip, atomic_t *wcount)
+{
+ down_write(&ip->i_rw_mutex);
+ if (ip->i_qadata && ((wcount == NULL) || (atomic_read(wcount) <= 1))) {
+ kmem_cache_free(gfs2_qadata_cachep, ip->i_qadata);
+ ip->i_qadata = NULL;
+ }
+ up_write(&ip->i_rw_mutex);
+}
+
int gfs2_quota_hold(struct gfs2_inode *ip, kuid_t uid, kgid_t gid)
{
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
struct gfs2_quota_data **qd;
int error;
- if (ip->i_res == NULL) {
- error = gfs2_rs_alloc(ip);
+ if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF)
+ return 0;
+
+ if (ip->i_qadata == NULL) {
+ error = gfs2_rsqa_alloc(ip);
if (error)
return error;
}
- qd = ip->i_res->rs_qa_qd;
+ qd = ip->i_qadata->qa_qd;
- if (gfs2_assert_warn(sdp, !ip->i_res->rs_qa_qd_num) ||
+ if (gfs2_assert_warn(sdp, !ip->i_qadata->qa_qd_num) ||
gfs2_assert_warn(sdp, !test_bit(GIF_QD_LOCKED, &ip->i_flags)))
return -EIO;
- if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF)
- return 0;
-
error = qdsb_get(sdp, make_kqid_uid(ip->i_inode.i_uid), qd);
if (error)
goto out;
- ip->i_res->rs_qa_qd_num++;
+ ip->i_qadata->qa_qd_num++;
qd++;
error = qdsb_get(sdp, make_kqid_gid(ip->i_inode.i_gid), qd);
if (error)
goto out;
- ip->i_res->rs_qa_qd_num++;
+ ip->i_qadata->qa_qd_num++;
qd++;
if (!uid_eq(uid, NO_UID_QUOTA_CHANGE) &&
@@ -565,7 +598,7 @@ int gfs2_quota_hold(struct gfs2_inode *ip, kuid_t uid, kgid_t gid)
error = qdsb_get(sdp, make_kqid_uid(uid), qd);
if (error)
goto out;
- ip->i_res->rs_qa_qd_num++;
+ ip->i_qadata->qa_qd_num++;
qd++;
}
@@ -574,7 +607,7 @@ int gfs2_quota_hold(struct gfs2_inode *ip, kuid_t uid, kgid_t gid)
error = qdsb_get(sdp, make_kqid_gid(gid), qd);
if (error)
goto out;
- ip->i_res->rs_qa_qd_num++;
+ ip->i_qadata->qa_qd_num++;
qd++;
}
@@ -587,17 +620,17 @@ out:
void gfs2_quota_unhold(struct gfs2_inode *ip)
{
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
- unsigned int x;
+ u32 x;
- if (ip->i_res == NULL)
+ if (ip->i_qadata == NULL)
return;
gfs2_assert_warn(sdp, !test_bit(GIF_QD_LOCKED, &ip->i_flags));
- for (x = 0; x < ip->i_res->rs_qa_qd_num; x++) {
- qdsb_put(ip->i_res->rs_qa_qd[x]);
- ip->i_res->rs_qa_qd[x] = NULL;
+ for (x = 0; x < ip->i_qadata->qa_qd_num; x++) {
+ qdsb_put(ip->i_qadata->qa_qd[x]);
+ ip->i_qadata->qa_qd[x] = NULL;
}
- ip->i_res->rs_qa_qd_num = 0;
+ ip->i_qadata->qa_qd_num = 0;
}
static int sort_qd(const void *a, const void *b)
@@ -843,7 +876,7 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda)
unsigned int nalloc = 0, blocks;
int error;
- error = gfs2_rs_alloc(ip);
+ error = gfs2_rsqa_alloc(ip);
if (error)
return error;
@@ -1003,23 +1036,23 @@ int gfs2_quota_lock(struct gfs2_inode *ip, kuid_t uid, kgid_t gid)
{
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
struct gfs2_quota_data *qd;
- unsigned int x;
+ u32 x;
int error = 0;
- error = gfs2_quota_hold(ip, uid, gid);
- if (error)
- return error;
-
if (capable(CAP_SYS_RESOURCE) ||
sdp->sd_args.ar_quota != GFS2_QUOTA_ON)
return 0;
- sort(ip->i_res->rs_qa_qd, ip->i_res->rs_qa_qd_num,
+ error = gfs2_quota_hold(ip, uid, gid);
+ if (error)
+ return error;
+
+ sort(ip->i_qadata->qa_qd, ip->i_qadata->qa_qd_num,
sizeof(struct gfs2_quota_data *), sort_qd, NULL);
- for (x = 0; x < ip->i_res->rs_qa_qd_num; x++) {
- qd = ip->i_res->rs_qa_qd[x];
- error = do_glock(qd, NO_FORCE, &ip->i_res->rs_qa_qd_ghs[x]);
+ for (x = 0; x < ip->i_qadata->qa_qd_num; x++) {
+ qd = ip->i_qadata->qa_qd[x];
+ error = do_glock(qd, NO_FORCE, &ip->i_qadata->qa_qd_ghs[x]);
if (error)
break;
}
@@ -1028,7 +1061,7 @@ int gfs2_quota_lock(struct gfs2_inode *ip, kuid_t uid, kgid_t gid)
set_bit(GIF_QD_LOCKED, &ip->i_flags);
else {
while (x--)
- gfs2_glock_dq_uninit(&ip->i_res->rs_qa_qd_ghs[x]);
+ gfs2_glock_dq_uninit(&ip->i_qadata->qa_qd_ghs[x]);
gfs2_quota_unhold(ip);
}
@@ -1076,20 +1109,20 @@ void gfs2_quota_unlock(struct gfs2_inode *ip)
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
struct gfs2_quota_data *qda[4];
unsigned int count = 0;
- unsigned int x;
+ u32 x;
int found;
if (!test_and_clear_bit(GIF_QD_LOCKED, &ip->i_flags))
goto out;
- for (x = 0; x < ip->i_res->rs_qa_qd_num; x++) {
+ for (x = 0; x < ip->i_qadata->qa_qd_num; x++) {
struct gfs2_quota_data *qd;
int sync;
- qd = ip->i_res->rs_qa_qd[x];
+ qd = ip->i_qadata->qa_qd[x];
sync = need_sync(qd);
- gfs2_glock_dq_uninit(&ip->i_res->rs_qa_qd_ghs[x]);
+ gfs2_glock_dq_uninit(&ip->i_qadata->qa_qd_ghs[x]);
if (!sync)
continue;
@@ -1158,7 +1191,7 @@ int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid,
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
struct gfs2_quota_data *qd;
s64 value, warn, limit;
- unsigned int x;
+ u32 x;
int error = 0;
ap->allowed = UINT_MAX; /* Assume we are permitted a whole lot */
@@ -1168,8 +1201,8 @@ int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid,
if (sdp->sd_args.ar_quota != GFS2_QUOTA_ON)
return 0;
- for (x = 0; x < ip->i_res->rs_qa_qd_num; x++) {
- qd = ip->i_res->rs_qa_qd[x];
+ for (x = 0; x < ip->i_qadata->qa_qd_num; x++) {
+ qd = ip->i_qadata->qa_qd[x];
if (!(qid_eq(qd->qd_id, make_kqid_uid(uid)) ||
qid_eq(qd->qd_id, make_kqid_gid(gid))))
@@ -1216,15 +1249,17 @@ void gfs2_quota_change(struct gfs2_inode *ip, s64 change,
kuid_t uid, kgid_t gid)
{
struct gfs2_quota_data *qd;
- unsigned int x;
+ u32 x;
+ struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
- if (gfs2_assert_warn(GFS2_SB(&ip->i_inode), change))
+ if (sdp->sd_args.ar_quota != GFS2_QUOTA_ON ||
+ gfs2_assert_warn(sdp, change))
return;
if (ip->i_diskflags & GFS2_DIF_SYSTEM)
return;
- for (x = 0; x < ip->i_res->rs_qa_qd_num; x++) {
- qd = ip->i_res->rs_qa_qd[x];
+ for (x = 0; x < ip->i_qadata->qa_qd_num; x++) {
+ qd = ip->i_qadata->qa_qd[x];
if (qid_eq(qd->qd_id, make_kqid_uid(uid)) ||
qid_eq(qd->qd_id, make_kqid_gid(gid))) {
@@ -1635,7 +1670,7 @@ static int gfs2_set_dqblk(struct super_block *sb, struct kqid qid,
if (error)
return error;
- error = gfs2_rs_alloc(ip);
+ error = gfs2_rsqa_alloc(ip);
if (error)
goto out_put;
diff --git a/fs/gfs2/quota.h b/fs/gfs2/quota.h
index ad04b3acae2b..5e47c935a515 100644
--- a/fs/gfs2/quota.h
+++ b/fs/gfs2/quota.h
@@ -18,6 +18,8 @@ struct gfs2_sbd;
#define NO_UID_QUOTA_CHANGE INVALID_UID
#define NO_GID_QUOTA_CHANGE INVALID_GID
+extern int gfs2_qa_alloc(struct gfs2_inode *ip);
+extern void gfs2_qa_delete(struct gfs2_inode *ip, atomic_t *wcount);
extern int gfs2_quota_hold(struct gfs2_inode *ip, kuid_t uid, kgid_t gid);
extern void gfs2_quota_unhold(struct gfs2_inode *ip);
diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c
index c134c0462cee..07c0265aa195 100644
--- a/fs/gfs2/rgrp.c
+++ b/fs/gfs2/rgrp.c
@@ -596,27 +596,13 @@ void gfs2_free_clones(struct gfs2_rgrpd *rgd)
}
/**
- * gfs2_rs_alloc - make sure we have a reservation assigned to the inode
+ * gfs2_rsqa_alloc - make sure we have a reservation assigned to the inode
+ * plus a quota allocations data structure, if necessary
* @ip: the inode for this reservation
*/
-int gfs2_rs_alloc(struct gfs2_inode *ip)
+int gfs2_rsqa_alloc(struct gfs2_inode *ip)
{
- int error = 0;
-
- down_write(&ip->i_rw_mutex);
- if (ip->i_res)
- goto out;
-
- ip->i_res = kmem_cache_zalloc(gfs2_rsrv_cachep, GFP_NOFS);
- if (!ip->i_res) {
- error = -ENOMEM;
- goto out;
- }
-
- RB_CLEAR_NODE(&ip->i_res->rs_node);
-out:
- up_write(&ip->i_rw_mutex);
- return error;
+ return gfs2_qa_alloc(ip);
}
static void dump_rs(struct seq_file *seq, const struct gfs2_blkreserv *rs)
@@ -678,21 +664,20 @@ void gfs2_rs_deltree(struct gfs2_blkreserv *rs)
}
/**
- * gfs2_rs_delete - delete a multi-block reservation
+ * gfs2_rsqa_delete - delete a multi-block reservation and quota allocation
* @ip: The inode for this reservation
* @wcount: The inode's write count, or NULL
*
*/
-void gfs2_rs_delete(struct gfs2_inode *ip, atomic_t *wcount)
+void gfs2_rsqa_delete(struct gfs2_inode *ip, atomic_t *wcount)
{
down_write(&ip->i_rw_mutex);
- if (ip->i_res && ((wcount == NULL) || (atomic_read(wcount) <= 1))) {
- gfs2_rs_deltree(ip->i_res);
- BUG_ON(ip->i_res->rs_free);
- kmem_cache_free(gfs2_rsrv_cachep, ip->i_res);
- ip->i_res = NULL;
+ if ((wcount == NULL) || (atomic_read(wcount) <= 1)) {
+ gfs2_rs_deltree(&ip->i_res);
+ BUG_ON(ip->i_res.rs_free);
}
up_write(&ip->i_rw_mutex);
+ gfs2_qa_delete(ip, wcount);
}
/**
@@ -1158,7 +1143,7 @@ static int gfs2_rgrp_bh_get(struct gfs2_rgrpd *rgd)
for (x = 0; x < length; x++) {
bi = rgd->rd_bits + x;
- error = gfs2_meta_read(gl, rgd->rd_addr + x, 0, &bi->bi_bh);
+ error = gfs2_meta_read(gl, rgd->rd_addr + x, 0, 0, &bi->bi_bh);
if (error)
goto fail;
}
@@ -1456,7 +1441,7 @@ static void rs_insert(struct gfs2_inode *ip)
{
struct rb_node **newn, *parent = NULL;
int rc;
- struct gfs2_blkreserv *rs = ip->i_res;
+ struct gfs2_blkreserv *rs = &ip->i_res;
struct gfs2_rgrpd *rgd = rs->rs_rbm.rgd;
u64 fsblock = gfs2_rbm_to_block(&rs->rs_rbm);
@@ -1503,7 +1488,7 @@ static void rg_mblk_search(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip,
{
struct gfs2_rbm rbm = { .rgd = rgd, };
u64 goal;
- struct gfs2_blkreserv *rs = ip->i_res;
+ struct gfs2_blkreserv *rs = &ip->i_res;
u32 extlen;
u32 free_blocks = rgd->rd_free_clone - rgd->rd_reserved;
int ret;
@@ -1574,7 +1559,7 @@ static u64 gfs2_next_unreserved_block(struct gfs2_rgrpd *rgd, u64 block,
}
if (n) {
- while ((rs_cmp(block, length, rs) == 0) && (ip->i_res != rs)) {
+ while ((rs_cmp(block, length, rs) == 0) && (&ip->i_res != rs)) {
block = gfs2_rbm_to_block(&rs->rs_rbm) + rs->rs_free;
n = n->rb_right;
if (n == NULL)
@@ -1804,7 +1789,7 @@ static void try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked, u64 skip
continue;
*last_unlinked = block;
- error = gfs2_glock_get(sdp, block, &gfs2_inode_glops, CREATE, &gl);
+ error = gfs2_glock_get(sdp, block, &gfs2_iopen_glops, CREATE, &gl);
if (error)
continue;
@@ -1984,7 +1969,7 @@ int gfs2_inplace_reserve(struct gfs2_inode *ip, struct gfs2_alloc_parms *ap)
{
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
struct gfs2_rgrpd *begin = NULL;
- struct gfs2_blkreserv *rs = ip->i_res;
+ struct gfs2_blkreserv *rs = &ip->i_res;
int error = 0, rg_locked, flags = 0;
u64 last_unlinked = NO_BLOCK;
int loops = 0;
@@ -2113,7 +2098,7 @@ next_rgrp:
void gfs2_inplace_release(struct gfs2_inode *ip)
{
- struct gfs2_blkreserv *rs = ip->i_res;
+ struct gfs2_blkreserv *rs = &ip->i_res;
if (rs->rs_rgd_gh.gh_gl)
gfs2_glock_dq_uninit(&rs->rs_rgd_gh);
@@ -2267,7 +2252,7 @@ static void gfs2_rgrp_error(struct gfs2_rgrpd *rgd)
static void gfs2_adjust_reservation(struct gfs2_inode *ip,
const struct gfs2_rbm *rbm, unsigned len)
{
- struct gfs2_blkreserv *rs = ip->i_res;
+ struct gfs2_blkreserv *rs = &ip->i_res;
struct gfs2_rgrpd *rgd = rbm->rgd;
unsigned rlen;
u64 block;
@@ -2310,8 +2295,8 @@ static void gfs2_set_alloc_start(struct gfs2_rbm *rbm,
{
u64 goal;
- if (gfs2_rs_active(ip->i_res)) {
- *rbm = ip->i_res->rs_rbm;
+ if (gfs2_rs_active(&ip->i_res)) {
+ *rbm = ip->i_res.rs_rbm;
return;
}
@@ -2365,7 +2350,7 @@ int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *nblocks,
gfs2_alloc_extent(&rbm, dinode, nblocks);
block = gfs2_rbm_to_block(&rbm);
rbm.rgd->rd_last_alloc = block - rbm.rgd->rd_data0;
- if (gfs2_rs_active(ip->i_res))
+ if (gfs2_rs_active(&ip->i_res))
gfs2_adjust_reservation(ip, &rbm, *nblocks);
ndata = *nblocks;
if (dinode)
diff --git a/fs/gfs2/rgrp.h b/fs/gfs2/rgrp.h
index c0ab33fa3eed..66b51cf66dfa 100644
--- a/fs/gfs2/rgrp.h
+++ b/fs/gfs2/rgrp.h
@@ -49,9 +49,9 @@ extern void gfs2_inplace_release(struct gfs2_inode *ip);
extern int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *n,
bool dinode, u64 *generation);
-extern int gfs2_rs_alloc(struct gfs2_inode *ip);
+extern int gfs2_rsqa_alloc(struct gfs2_inode *ip);
extern void gfs2_rs_deltree(struct gfs2_blkreserv *rs);
-extern void gfs2_rs_delete(struct gfs2_inode *ip, atomic_t *wcount);
+extern void gfs2_rsqa_delete(struct gfs2_inode *ip, atomic_t *wcount);
extern void __gfs2_free_blocks(struct gfs2_inode *ip, u64 bstart, u32 blen, int meta);
extern void gfs2_free_meta(struct gfs2_inode *ip, u64 bstart, u32 blen);
extern void gfs2_free_di(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip);
@@ -78,7 +78,7 @@ extern int gfs2_rgrp_send_discards(struct gfs2_sbd *sdp, u64 offset,
extern int gfs2_fitrim(struct file *filp, void __user *argp);
/* This is how to tell if a reservation is in the rgrp tree: */
-static inline bool gfs2_rs_active(struct gfs2_blkreserv *rs)
+static inline bool gfs2_rs_active(const struct gfs2_blkreserv *rs)
{
return rs && !RB_EMPTY_NODE(&rs->rs_node);
}
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index 894fb01a91da..8f960a51a9a0 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -83,6 +83,8 @@ enum {
Opt_nobarrier,
Opt_rgrplvb,
Opt_norgrplvb,
+ Opt_loccookie,
+ Opt_noloccookie,
Opt_error,
};
@@ -122,6 +124,8 @@ static const match_table_t tokens = {
{Opt_nobarrier, "nobarrier"},
{Opt_rgrplvb, "rgrplvb"},
{Opt_norgrplvb, "norgrplvb"},
+ {Opt_loccookie, "loccookie"},
+ {Opt_noloccookie, "noloccookie"},
{Opt_error, NULL}
};
@@ -278,6 +282,12 @@ int gfs2_mount_args(struct gfs2_args *args, char *options)
case Opt_norgrplvb:
args->ar_rgrplvb = 0;
break;
+ case Opt_loccookie:
+ args->ar_loccookie = 1;
+ break;
+ case Opt_noloccookie:
+ args->ar_loccookie = 0;
+ break;
case Opt_error:
default:
pr_warn("invalid mount option: %s\n", o);
@@ -556,6 +566,7 @@ void update_statfs(struct gfs2_sbd *sdp, struct buffer_head *m_bh,
struct gfs2_statfs_change_host *l_sc = &sdp->sd_statfs_local;
gfs2_trans_add_meta(l_ip->i_gl, l_bh);
+ gfs2_trans_add_meta(m_ip->i_gl, m_bh);
spin_lock(&sdp->sd_statfs_spin);
m_sc->sc_total += l_sc->sc_total;
@@ -564,10 +575,8 @@ void update_statfs(struct gfs2_sbd *sdp, struct buffer_head *m_bh,
memset(l_sc, 0, sizeof(struct gfs2_statfs_change));
memset(l_bh->b_data + sizeof(struct gfs2_dinode),
0, sizeof(struct gfs2_statfs_change));
- spin_unlock(&sdp->sd_statfs_spin);
-
- gfs2_trans_add_meta(m_ip->i_gl, m_bh);
gfs2_statfs_change_out(m_sc, m_bh->b_data + sizeof(struct gfs2_dinode));
+ spin_unlock(&sdp->sd_statfs_spin);
}
int gfs2_statfs_sync(struct super_block *sb, int type)
@@ -842,10 +851,6 @@ static int gfs2_make_fs_ro(struct gfs2_sbd *sdp)
gfs2_quota_sync(sdp->sd_vfs, 0);
gfs2_statfs_sync(sdp->sd_vfs, 0);
- down_write(&sdp->sd_log_flush_lock);
- clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
- up_write(&sdp->sd_log_flush_lock);
-
gfs2_log_flush(sdp, NULL, SHUTDOWN_FLUSH);
wait_event(sdp->sd_reserving_log_wait, atomic_read(&sdp->sd_reserving_log) == 0);
gfs2_assert_warn(sdp, atomic_read(&sdp->sd_log_blks_free) == sdp->sd_jdesc->jd_blocks);
@@ -1419,6 +1424,8 @@ static int gfs2_show_options(struct seq_file *s, struct dentry *root)
seq_puts(s, ",demote_interface_used");
if (args->ar_rgrplvb)
seq_puts(s, ",rgrplvb");
+ if (args->ar_loccookie)
+ seq_puts(s, ",loccookie");
return 0;
}
@@ -1512,6 +1519,7 @@ static void gfs2_evict_inode(struct inode *inode)
struct gfs2_sbd *sdp = sb->s_fs_info;
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
+ struct address_space *metamapping;
int error;
if (test_bit(GIF_FREE_VFS_INODE, &ip->i_flags)) {
@@ -1526,7 +1534,8 @@ static void gfs2_evict_inode(struct inode *inode)
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, GL_SKIP, &gh);
if (unlikely(error)) {
ip->i_iopen_gh.gh_flags |= GL_NOCACHE;
- gfs2_glock_dq_uninit(&ip->i_iopen_gh);
+ gfs2_glock_dq_wait(&ip->i_iopen_gh);
+ gfs2_holder_uninit(&ip->i_iopen_gh);
goto out;
}
@@ -1575,8 +1584,8 @@ static void gfs2_evict_inode(struct inode *inode)
out_truncate:
gfs2_log_flush(sdp, ip->i_gl, NORMAL_FLUSH);
+ metamapping = gfs2_glock2aspace(ip->i_gl);
if (test_bit(GLF_DIRTY, &ip->i_gl->gl_flags)) {
- struct address_space *metamapping = gfs2_glock2aspace(ip->i_gl);
filemap_fdatawrite(metamapping);
filemap_fdatawait(metamapping);
}
@@ -1589,16 +1598,17 @@ out_truncate:
goto out_unlock;
/* Needs to be done before glock release & also in a transaction */
truncate_inode_pages(&inode->i_data, 0);
+ truncate_inode_pages(metamapping, 0);
gfs2_trans_end(sdp);
out_unlock:
/* Error path for case 1 */
- if (gfs2_rs_active(ip->i_res))
- gfs2_rs_deltree(ip->i_res);
+ if (gfs2_rs_active(&ip->i_res))
+ gfs2_rs_deltree(&ip->i_res);
if (test_bit(HIF_HOLDER, &ip->i_iopen_gh.gh_iflags)) {
ip->i_iopen_gh.gh_flags |= GL_NOCACHE;
- gfs2_glock_dq(&ip->i_iopen_gh);
+ gfs2_glock_dq_wait(&ip->i_iopen_gh);
}
gfs2_holder_uninit(&ip->i_iopen_gh);
gfs2_glock_dq_uninit(&gh);
@@ -1607,7 +1617,7 @@ out_unlock:
out:
/* Case 3 starts here */
truncate_inode_pages_final(&inode->i_data);
- gfs2_rs_delete(ip, NULL);
+ gfs2_rsqa_delete(ip, NULL);
gfs2_ordered_del_inode(ip);
clear_inode(inode);
gfs2_dir_hash_inval(ip);
@@ -1619,7 +1629,8 @@ out:
if (ip->i_iopen_gh.gh_gl) {
ip->i_iopen_gh.gh_gl->gl_object = NULL;
ip->i_iopen_gh.gh_flags |= GL_NOCACHE;
- gfs2_glock_dq_uninit(&ip->i_iopen_gh);
+ gfs2_glock_dq_wait(&ip->i_iopen_gh);
+ gfs2_holder_uninit(&ip->i_iopen_gh);
}
}
@@ -1632,7 +1643,9 @@ static struct inode *gfs2_alloc_inode(struct super_block *sb)
ip->i_flags = 0;
ip->i_gl = NULL;
ip->i_rgd = NULL;
- ip->i_res = NULL;
+ memset(&ip->i_res, 0, sizeof(ip->i_res));
+ RB_CLEAR_NODE(&ip->i_res.rs_node);
+ ip->i_rahead = 0;
}
return &ip->i_inode;
}
diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c
index 86d2035ac669..cf645835710f 100644
--- a/fs/gfs2/util.c
+++ b/fs/gfs2/util.c
@@ -27,7 +27,7 @@ struct kmem_cache *gfs2_inode_cachep __read_mostly;
struct kmem_cache *gfs2_bufdata_cachep __read_mostly;
struct kmem_cache *gfs2_rgrpd_cachep __read_mostly;
struct kmem_cache *gfs2_quotad_cachep __read_mostly;
-struct kmem_cache *gfs2_rsrv_cachep __read_mostly;
+struct kmem_cache *gfs2_qadata_cachep __read_mostly;
mempool_t *gfs2_page_pool __read_mostly;
void gfs2_assert_i(struct gfs2_sbd *sdp)
diff --git a/fs/gfs2/util.h b/fs/gfs2/util.h
index cbdcbdf39614..c81295f407f6 100644
--- a/fs/gfs2/util.h
+++ b/fs/gfs2/util.h
@@ -149,7 +149,7 @@ extern struct kmem_cache *gfs2_inode_cachep;
extern struct kmem_cache *gfs2_bufdata_cachep;
extern struct kmem_cache *gfs2_rgrpd_cachep;
extern struct kmem_cache *gfs2_quotad_cachep;
-extern struct kmem_cache *gfs2_rsrv_cachep;
+extern struct kmem_cache *gfs2_qadata_cachep;
extern mempool_t *gfs2_page_pool;
static inline unsigned int gfs2_tune_get_i(struct gfs2_tune *gt,
diff --git a/fs/gfs2/xattr.c b/fs/gfs2/xattr.c
index 53ce76a374fe..e8dfb4740c04 100644
--- a/fs/gfs2/xattr.c
+++ b/fs/gfs2/xattr.c
@@ -119,7 +119,7 @@ static int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data)
__be64 *eablk, *end;
int error;
- error = gfs2_meta_read(ip->i_gl, ip->i_eattr, DIO_WAIT, &bh);
+ error = gfs2_meta_read(ip->i_gl, ip->i_eattr, DIO_WAIT, 0, &bh);
if (error)
return error;
@@ -143,7 +143,7 @@ static int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data)
break;
bn = be64_to_cpu(*eablk);
- error = gfs2_meta_read(ip->i_gl, bn, DIO_WAIT, &eabh);
+ error = gfs2_meta_read(ip->i_gl, bn, DIO_WAIT, 0, &eabh);
if (error)
break;
error = ea_foreach_i(ip, eabh, ea_call, data);
@@ -477,7 +477,7 @@ static int gfs2_iter_unstuffed(struct gfs2_inode *ip, struct gfs2_ea_header *ea,
return -ENOMEM;
for (x = 0; x < nptrs; x++) {
- error = gfs2_meta_read(ip->i_gl, be64_to_cpu(*dataptrs), 0,
+ error = gfs2_meta_read(ip->i_gl, be64_to_cpu(*dataptrs), 0, 0,
bh + x);
if (error) {
while (x--)
@@ -979,7 +979,7 @@ static int ea_set_block(struct gfs2_inode *ip, struct gfs2_ea_request *er,
if (ip->i_diskflags & GFS2_DIF_EA_INDIRECT) {
__be64 *end;
- error = gfs2_meta_read(ip->i_gl, ip->i_eattr, DIO_WAIT,
+ error = gfs2_meta_read(ip->i_gl, ip->i_eattr, DIO_WAIT, 0,
&indbh);
if (error)
return error;
@@ -1237,56 +1237,6 @@ static int gfs2_xattr_set(const struct xattr_handler *handler,
size, flags, handler->flags);
}
-
-static int ea_acl_chmod_unstuffed(struct gfs2_inode *ip,
- struct gfs2_ea_header *ea, char *data)
-{
- struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
- unsigned int amount = GFS2_EA_DATA_LEN(ea);
- unsigned int nptrs = DIV_ROUND_UP(amount, sdp->sd_jbsize);
- int ret;
-
- ret = gfs2_trans_begin(sdp, nptrs + RES_DINODE, 0);
- if (ret)
- return ret;
-
- ret = gfs2_iter_unstuffed(ip, ea, data, NULL);
- gfs2_trans_end(sdp);
-
- return ret;
-}
-
-int gfs2_xattr_acl_chmod(struct gfs2_inode *ip, struct iattr *attr, char *data)
-{
- struct inode *inode = &ip->i_inode;
- struct gfs2_sbd *sdp = GFS2_SB(inode);
- struct gfs2_ea_location el;
- int error;
-
- error = gfs2_ea_find(ip, GFS2_EATYPE_SYS, GFS2_POSIX_ACL_ACCESS, &el);
- if (error)
- return error;
-
- if (GFS2_EA_IS_STUFFED(el.el_ea)) {
- error = gfs2_trans_begin(sdp, RES_DINODE + RES_EATTR, 0);
- if (error == 0) {
- gfs2_trans_add_meta(ip->i_gl, el.el_bh);
- memcpy(GFS2_EA2DATA(el.el_ea), data,
- GFS2_EA_DATA_LEN(el.el_ea));
- }
- } else {
- error = ea_acl_chmod_unstuffed(ip, el.el_ea, data);
- }
-
- brelse(el.el_bh);
- if (error)
- return error;
-
- error = gfs2_setattr_simple(inode, attr);
- gfs2_trans_end(sdp);
- return error;
-}
-
static int ea_dealloc_indirect(struct gfs2_inode *ip)
{
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
@@ -1306,7 +1256,7 @@ static int ea_dealloc_indirect(struct gfs2_inode *ip)
memset(&rlist, 0, sizeof(struct gfs2_rgrp_list));
- error = gfs2_meta_read(ip->i_gl, ip->i_eattr, DIO_WAIT, &indbh);
+ error = gfs2_meta_read(ip->i_gl, ip->i_eattr, DIO_WAIT, 0, &indbh);
if (error)
return error;
diff --git a/fs/gfs2/xattr.h b/fs/gfs2/xattr.h
index d392f8358f2f..2d887c88eb49 100644
--- a/fs/gfs2/xattr.h
+++ b/fs/gfs2/xattr.h
@@ -62,6 +62,5 @@ extern int gfs2_ea_dealloc(struct gfs2_inode *ip);
/* Exported to acl.c */
extern int gfs2_xattr_acl_get(struct gfs2_inode *ip, const char *name, char **data);
-extern int gfs2_xattr_acl_chmod(struct gfs2_inode *ip, struct iattr *attr, char *data);
#endif /* __EATTR_DOT_H__ */
diff --git a/fs/hfs/mdb.c b/fs/hfs/mdb.c
index aa3f0d6d043c..a3ec3ae7d347 100644
--- a/fs/hfs/mdb.c
+++ b/fs/hfs/mdb.c
@@ -166,7 +166,7 @@ int hfs_mdb_get(struct super_block *sb)
pr_warn("continuing without an alternate MDB\n");
}
- HFS_SB(sb)->bitmap = (__be32 *)__get_free_pages(GFP_KERNEL, PAGE_SIZE < 8192 ? 1 : 0);
+ HFS_SB(sb)->bitmap = kmalloc(8192, GFP_KERNEL);
if (!HFS_SB(sb)->bitmap)
goto out;
@@ -360,7 +360,7 @@ void hfs_mdb_put(struct super_block *sb)
unload_nls(HFS_SB(sb)->nls_io);
unload_nls(HFS_SB(sb)->nls_disk);
- free_pages((unsigned long)HFS_SB(sb)->bitmap, PAGE_SIZE < 8192 ? 1 : 0);
+ kfree(HFS_SB(sb)->bitmap);
kfree(HFS_SB(sb));
sb->s_fs_info = NULL;
}
diff --git a/fs/hfs/super.c b/fs/hfs/super.c
index 4574fdd3d421..1ca95c232bb5 100644
--- a/fs/hfs/super.c
+++ b/fs/hfs/super.c
@@ -483,8 +483,8 @@ static int __init init_hfs_fs(void)
int err;
hfs_inode_cachep = kmem_cache_create("hfs_inode_cache",
- sizeof(struct hfs_inode_info), 0, SLAB_HWCACHE_ALIGN,
- hfs_init_once);
+ sizeof(struct hfs_inode_info), 0,
+ SLAB_HWCACHE_ALIGN|SLAB_ACCOUNT, hfs_init_once);
if (!hfs_inode_cachep)
return -ENOMEM;
err = register_filesystem(&hfs_fs_type);
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index 6dd107d7421e..19b33f8151f1 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -403,6 +403,7 @@ struct inode *hfsplus_new_inode(struct super_block *sb, umode_t mode)
} else if (S_ISLNK(inode->i_mode)) {
sbi->file_count++;
inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_mapping->a_ops = &hfsplus_aops;
hip->clump_blocks = 1;
} else
@@ -526,6 +527,7 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)
inode->i_mapping->a_ops = &hfsplus_aops;
} else if (S_ISLNK(inode->i_mode)) {
inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_mapping->a_ops = &hfsplus_aops;
} else {
init_special_inode(inode, inode->i_mode,
diff --git a/fs/hfsplus/posix_acl.c b/fs/hfsplus/posix_acl.c
index df0c9af68d05..afb33eda6d7d 100644
--- a/fs/hfsplus/posix_acl.c
+++ b/fs/hfsplus/posix_acl.c
@@ -21,10 +21,10 @@ struct posix_acl *hfsplus_get_posix_acl(struct inode *inode, int type)
switch (type) {
case ACL_TYPE_ACCESS:
- xattr_name = POSIX_ACL_XATTR_ACCESS;
+ xattr_name = XATTR_NAME_POSIX_ACL_ACCESS;
break;
case ACL_TYPE_DEFAULT:
- xattr_name = POSIX_ACL_XATTR_DEFAULT;
+ xattr_name = XATTR_NAME_POSIX_ACL_DEFAULT;
break;
default:
return ERR_PTR(-EINVAL);
@@ -66,7 +66,7 @@ int hfsplus_set_posix_acl(struct inode *inode, struct posix_acl *acl,
switch (type) {
case ACL_TYPE_ACCESS:
- xattr_name = POSIX_ACL_XATTR_ACCESS;
+ xattr_name = XATTR_NAME_POSIX_ACL_ACCESS;
if (acl) {
err = posix_acl_equiv_mode(acl, &inode->i_mode);
if (err < 0)
@@ -76,7 +76,7 @@ int hfsplus_set_posix_acl(struct inode *inode, struct posix_acl *acl,
break;
case ACL_TYPE_DEFAULT:
- xattr_name = POSIX_ACL_XATTR_DEFAULT;
+ xattr_name = XATTR_NAME_POSIX_ACL_DEFAULT;
if (!S_ISDIR(inode->i_mode))
return acl ? -EACCES : 0;
break;
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index 7302d96ae8bf..5d54490a136d 100644
--- a/fs/hfsplus/super.c
+++ b/fs/hfsplus/super.c
@@ -663,7 +663,7 @@ static int __init init_hfsplus_fs(void)
int err;
hfsplus_inode_cachep = kmem_cache_create("hfsplus_icache",
- HFSPLUS_INODE_SIZE, 0, SLAB_HWCACHE_ALIGN,
+ HFSPLUS_INODE_SIZE, 0, SLAB_HWCACHE_ALIGN|SLAB_ACCOUNT,
hfsplus_init_once);
if (!hfsplus_inode_cachep)
return -ENOMEM;
diff --git a/fs/hfsplus/xattr.c b/fs/hfsplus/xattr.c
index e41a010cd89c..ab01530b4930 100644
--- a/fs/hfsplus/xattr.c
+++ b/fs/hfsplus/xattr.c
@@ -431,9 +431,6 @@ int hfsplus_setxattr(struct dentry *dentry, const char *name,
char *xattr_name;
int res;
- if (!strcmp(name, ""))
- return -EINVAL;
-
xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1,
GFP_KERNEL);
if (!xattr_name)
@@ -589,9 +586,6 @@ ssize_t hfsplus_getxattr(struct dentry *dentry, const char *name,
int res;
char *xattr_name;
- if (!strcmp(name, ""))
- return -EINVAL;
-
xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1,
GFP_KERNEL);
if (!xattr_name)
@@ -853,9 +847,6 @@ static int hfsplus_osx_getxattr(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
void *buffer, size_t size)
{
- if (!strcmp(name, ""))
- return -EINVAL;
-
/*
* Don't allow retrieving properly prefixed attributes
* by prepending them with "osx."
@@ -876,9 +867,6 @@ static int hfsplus_osx_setxattr(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
const void *buffer, size_t size, int flags)
{
- if (!strcmp(name, ""))
- return -EINVAL;
-
/*
* Don't allow setting properly prefixed attributes
* by prepending them with "osx."
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index 2ac99db3750e..cfaa18c7a337 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -223,7 +223,7 @@ static struct inode *hostfs_alloc_inode(struct super_block *sb)
{
struct hostfs_inode_info *hi;
- hi = kmalloc(sizeof(*hi), GFP_KERNEL);
+ hi = kmalloc(sizeof(*hi), GFP_KERNEL_ACCOUNT);
if (hi == NULL)
return NULL;
hi->fd = -1;
@@ -730,15 +730,13 @@ static int hostfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
init_special_inode(inode, mode, dev);
err = do_mknod(name, mode, MAJOR(dev), MINOR(dev));
- if (!err)
+ if (err)
goto out_free;
err = read_name(inode, name);
__putname(name);
if (err)
goto out_put;
- if (err)
- goto out_put;
d_instantiate(dentry, inode);
return 0;
@@ -892,9 +890,14 @@ static const struct inode_operations hostfs_dir_iops = {
.setattr = hostfs_setattr,
};
-static const char *hostfs_follow_link(struct dentry *dentry, void **cookie)
+static const char *hostfs_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
- char *link = __getname();
+ char *link;
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
+ link = kmalloc(PATH_MAX, GFP_KERNEL);
if (link) {
char *path = dentry_name(dentry);
int err = -ENOMEM;
@@ -905,25 +908,20 @@ static const char *hostfs_follow_link(struct dentry *dentry, void **cookie)
__putname(path);
}
if (err < 0) {
- __putname(link);
+ kfree(link);
return ERR_PTR(err);
}
} else {
return ERR_PTR(-ENOMEM);
}
- return *cookie = link;
-}
-
-static void hostfs_put_link(struct inode *unused, void *cookie)
-{
- __putname(cookie);
+ set_delayed_call(done, kfree_link, link);
+ return link;
}
static const struct inode_operations hostfs_link_iops = {
.readlink = generic_readlink,
- .follow_link = hostfs_follow_link,
- .put_link = hostfs_put_link,
+ .get_link = hostfs_get_link,
};
static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent)
diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c
index 933c73780813..1f3c6d76200b 100644
--- a/fs/hpfs/inode.c
+++ b/fs/hpfs/inode.c
@@ -77,6 +77,7 @@ void hpfs_read_inode(struct inode *i)
kfree(ea);
i->i_mode = S_IFLNK | 0777;
i->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(i);
i->i_data.a_ops = &hpfs_symlink_aops;
set_nlink(i, 1);
i->i_size = ea_size;
diff --git a/fs/hpfs/map.c b/fs/hpfs/map.c
index a69bbc1e87f8..a136929189f0 100644
--- a/fs/hpfs/map.c
+++ b/fs/hpfs/map.c
@@ -133,7 +133,7 @@ __le32 *hpfs_load_bitmap_directory(struct super_block *s, secno bmp)
void hpfs_load_hotfix_map(struct super_block *s, struct hpfs_spare_block *spareblock)
{
struct quad_buffer_head qbh;
- u32 *directory;
+ __le32 *directory;
u32 n_hotfixes, n_used_hotfixes;
unsigned i;
diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c
index ae4d5a1fa4c9..506765afa1a3 100644
--- a/fs/hpfs/namei.c
+++ b/fs/hpfs/namei.c
@@ -332,6 +332,7 @@ static int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *sy
result->i_blocks = 1;
set_nlink(result, 1);
result->i_size = strlen(symlink);
+ inode_nohighmem(result);
result->i_op = &page_symlink_inode_operations;
result->i_data.a_ops = &hpfs_symlink_aops;
@@ -500,7 +501,7 @@ out:
static int hpfs_symlink_readpage(struct file *file, struct page *page)
{
- char *link = kmap(page);
+ char *link = page_address(page);
struct inode *i = page->mapping->host;
struct fnode *fnode;
struct buffer_head *bh;
@@ -516,14 +517,12 @@ static int hpfs_symlink_readpage(struct file *file, struct page *page)
goto fail;
hpfs_unlock(i->i_sb);
SetPageUptodate(page);
- kunmap(page);
unlock_page(page);
return 0;
fail:
hpfs_unlock(i->i_sb);
SetPageError(page);
- kunmap(page);
unlock_page(page);
return err;
}
diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c
index a561591896bd..458cf463047b 100644
--- a/fs/hpfs/super.c
+++ b/fs/hpfs/super.c
@@ -261,7 +261,7 @@ static int init_inodecache(void)
hpfs_inode_cachep = kmem_cache_create("hpfs_inode_cache",
sizeof(struct hpfs_inode_info),
0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
init_once);
if (hpfs_inode_cachep == NULL)
return -ENOMEM;
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index de4bdfac0cec..47789292a582 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -4,11 +4,11 @@
* Nadia Yvette Chambers, 2002
*
* Copyright (C) 2002 Linus Torvalds.
+ * License: GPL
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/module.h>
#include <linux/thread_info.h>
#include <asm/current.h>
#include <linux/sched.h> /* remove ASAP */
@@ -738,7 +738,7 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb,
/*
* The policy is initialized here even if we are creating a
* private inode because initialization simply creates an
- * an empty rb tree and calls spin_lock_init(), later when we
+ * an empty rb tree and calls rwlock_init(), later when we
* call mpol_free_shared_policy() it will just return because
* the rb tree will still be empty.
*/
@@ -760,6 +760,7 @@ static struct inode *hugetlbfs_get_inode(struct super_block *sb,
break;
case S_IFLNK:
inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
break;
}
lockdep_annotate_inode_mutex_key(inode);
@@ -1201,7 +1202,6 @@ static struct file_system_type hugetlbfs_fs_type = {
.mount = hugetlbfs_mount,
.kill_sb = kill_litter_super,
};
-MODULE_ALIAS_FS("hugetlbfs");
static struct vfsmount *hugetlbfs_vfsmount[HUGE_MAX_HSTATE];
@@ -1321,7 +1321,7 @@ static int __init init_hugetlbfs_fs(void)
error = -ENOMEM;
hugetlbfs_inode_cachep = kmem_cache_create("hugetlbfs_inode_cache",
sizeof(struct hugetlbfs_inode_info),
- 0, 0, init_once);
+ 0, SLAB_ACCOUNT, init_once);
if (hugetlbfs_inode_cachep == NULL)
goto out2;
@@ -1355,26 +1355,4 @@ static int __init init_hugetlbfs_fs(void)
out2:
return error;
}
-
-static void __exit exit_hugetlbfs_fs(void)
-{
- struct hstate *h;
- int i;
-
-
- /*
- * Make sure all delayed rcu free inodes are flushed before we
- * destroy cache.
- */
- rcu_barrier();
- kmem_cache_destroy(hugetlbfs_inode_cachep);
- i = 0;
- for_each_hstate(h)
- kern_unmount(hugetlbfs_vfsmount[i++]);
- unregister_filesystem(&hugetlbfs_fs_type);
-}
-
-module_init(init_hugetlbfs_fs)
-module_exit(exit_hugetlbfs_fs)
-
-MODULE_LICENSE("GPL");
+fs_initcall(init_hugetlbfs_fs)
diff --git a/fs/inode.c b/fs/inode.c
index 1be5f9003eb3..e491e54d2430 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -225,7 +225,7 @@ void __destroy_inode(struct inode *inode)
inode_detach_wb(inode);
security_inode_free(inode);
fsnotify_inode_delete(inode);
- locks_free_lock_context(inode->i_flctx);
+ locks_free_lock_context(inode);
if (!inode->i_nlink) {
WARN_ON(atomic_long_read(&inode->i_sb->s_remove_count) == 0);
atomic_long_dec(&inode->i_sb->s_remove_count);
@@ -1883,7 +1883,7 @@ void __init inode_init(void)
sizeof(struct inode),
0,
(SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
init_once);
/* Hash may have been set up in inode_init_early */
@@ -2028,3 +2028,9 @@ void inode_set_flags(struct inode *inode, unsigned int flags,
new_flags) != old_flags));
}
EXPORT_SYMBOL(inode_set_flags);
+
+void inode_nohighmem(struct inode *inode)
+{
+ mapping_set_gfp_mask(inode->i_mapping, GFP_USER);
+}
+EXPORT_SYMBOL(inode_nohighmem);
diff --git a/fs/internal.h b/fs/internal.h
index 71859c4d0b41..b71deeecea17 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -55,7 +55,7 @@ extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
/*
* namespace.c
*/
-extern int copy_mount_options(const void __user *, unsigned long *);
+extern void *copy_mount_options(const void __user *);
extern char *copy_mount_string(const void __user *);
extern struct vfsmount *lookup_mnt(struct path *);
@@ -151,3 +151,10 @@ extern void mnt_pin_kill(struct mount *m);
* fs/nsfs.c
*/
extern struct dentry_operations ns_dentry_operations;
+
+/*
+ * fs/ioctl.c
+ */
+extern int do_vfs_ioctl(struct file *file, unsigned int fd, unsigned int cmd,
+ unsigned long arg);
+extern long vfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
diff --git a/fs/ioctl.c b/fs/ioctl.c
index 5d01d2638ca5..29466c380958 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -15,6 +15,7 @@
#include <linux/writeback.h>
#include <linux/buffer_head.h>
#include <linux/falloc.h>
+#include "internal.h"
#include <asm/ioctls.h>
@@ -32,8 +33,7 @@
*
* Returns 0 on success, -errno on error.
*/
-static long vfs_ioctl(struct file *filp, unsigned int cmd,
- unsigned long arg)
+long vfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int error = -ENOTTY;
@@ -215,6 +215,29 @@ static int ioctl_fiemap(struct file *filp, unsigned long arg)
return error;
}
+static long ioctl_file_clone(struct file *dst_file, unsigned long srcfd,
+ u64 off, u64 olen, u64 destoff)
+{
+ struct fd src_file = fdget(srcfd);
+ int ret;
+
+ if (!src_file.file)
+ return -EBADF;
+ ret = vfs_clone_file_range(src_file.file, off, dst_file, destoff, olen);
+ fdput(src_file);
+ return ret;
+}
+
+static long ioctl_file_clone_range(struct file *file, void __user *argp)
+{
+ struct file_clone_range args;
+
+ if (copy_from_user(&args, argp, sizeof(args)))
+ return -EFAULT;
+ return ioctl_file_clone(file, args.src_fd, args.src_offset,
+ args.src_length, args.dest_offset);
+}
+
#ifdef CONFIG_BLOCK
static inline sector_t logical_to_blk(struct inode *inode, loff_t offset)
@@ -545,6 +568,41 @@ static int ioctl_fsthaw(struct file *filp)
return thaw_super(sb);
}
+static long ioctl_file_dedupe_range(struct file *file, void __user *arg)
+{
+ struct file_dedupe_range __user *argp = arg;
+ struct file_dedupe_range *same = NULL;
+ int ret;
+ unsigned long size;
+ u16 count;
+
+ if (get_user(count, &argp->dest_count)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ size = offsetof(struct file_dedupe_range __user, info[count]);
+
+ same = memdup_user(argp, size);
+ if (IS_ERR(same)) {
+ ret = PTR_ERR(same);
+ same = NULL;
+ goto out;
+ }
+
+ ret = vfs_dedupe_file_range(file, same);
+ if (ret)
+ goto out;
+
+ ret = copy_to_user(argp, same, size);
+ if (ret)
+ ret = -EFAULT;
+
+out:
+ kfree(same);
+ return ret;
+}
+
/*
* When you add any new common ioctls to the switches above and below
* please update compat_sys_ioctl() too.
@@ -600,6 +658,15 @@ int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
case FIGETBSZ:
return put_user(inode->i_sb->s_blocksize, argp);
+ case FICLONE:
+ return ioctl_file_clone(filp, arg, 0, 0, 0);
+
+ case FICLONERANGE:
+ return ioctl_file_clone_range(filp, argp);
+
+ case FIDEDUPERANGE:
+ return ioctl_file_dedupe_range(filp, argp);
+
default:
if (S_ISREG(inode->i_mode))
error = file_ioctl(filp, cmd, arg);
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index d67a16f2a45d..bcd2d41b318a 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -94,7 +94,7 @@ static int __init init_inodecache(void)
isofs_inode_cachep = kmem_cache_create("isofs_inode_cache",
sizeof(struct iso_inode_info),
0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
init_once);
if (isofs_inode_cachep == NULL)
return -ENOMEM;
@@ -1417,6 +1417,7 @@ static int isofs_read_inode(struct inode *inode, int relocated)
inode->i_fop = &isofs_dir_operations;
} else if (S_ISLNK(inode->i_mode)) {
inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_data.a_ops = &isofs_symlink_aops;
} else
/* XXX - parse_rock_ridge_inode() had already set i_rdev. */
diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c
index 735d7522a3a9..5384ceb35b1c 100644
--- a/fs/isofs/rock.c
+++ b/fs/isofs/rock.c
@@ -687,7 +687,7 @@ static int rock_ridge_symlink_readpage(struct file *file, struct page *page)
struct inode *inode = page->mapping->host;
struct iso_inode_info *ei = ISOFS_I(inode);
struct isofs_sb_info *sbi = ISOFS_SB(inode->i_sb);
- char *link = kmap(page);
+ char *link = page_address(page);
unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
struct buffer_head *bh;
char *rpnt = link;
@@ -774,7 +774,6 @@ repeat:
brelse(bh);
*rpnt = '\0';
SetPageUptodate(page);
- kunmap(page);
unlock_page(page);
return 0;
@@ -791,7 +790,6 @@ fail:
brelse(bh);
error:
SetPageError(page);
- kunmap(page);
unlock_page(page);
return -EIO;
}
diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c
index 89463eee6791..081dff087fc0 100644
--- a/fs/jbd2/transaction.c
+++ b/fs/jbd2/transaction.c
@@ -764,13 +764,11 @@ void jbd2_journal_unlock_updates (journal_t *journal)
static void warn_dirty_buffer(struct buffer_head *bh)
{
- char b[BDEVNAME_SIZE];
-
printk(KERN_WARNING
- "JBD2: Spotted dirty metadata buffer (dev = %s, blocknr = %llu). "
+ "JBD2: Spotted dirty metadata buffer (dev = %pg, blocknr = %llu). "
"There's a risk of filesystem corruption in case of system "
"crash.\n",
- bdevname(bh->b_bdev, b), (unsigned long long)bh->b_blocknr);
+ bh->b_bdev, (unsigned long long)bh->b_blocknr);
}
/* Call t_frozen trigger and copy buffer data into jh->b_frozen_data. */
@@ -1009,7 +1007,8 @@ out:
}
/* Fast check whether buffer is already attached to the required transaction */
-static bool jbd2_write_access_granted(handle_t *handle, struct buffer_head *bh)
+static bool jbd2_write_access_granted(handle_t *handle, struct buffer_head *bh,
+ bool undo)
{
struct journal_head *jh;
bool ret = false;
@@ -1036,6 +1035,9 @@ static bool jbd2_write_access_granted(handle_t *handle, struct buffer_head *bh)
jh = READ_ONCE(bh->b_private);
if (!jh)
goto out;
+ /* For undo access buffer must have data copied */
+ if (undo && !jh->b_committed_data)
+ goto out;
if (jh->b_transaction != handle->h_transaction &&
jh->b_next_transaction != handle->h_transaction)
goto out;
@@ -1073,7 +1075,7 @@ int jbd2_journal_get_write_access(handle_t *handle, struct buffer_head *bh)
struct journal_head *jh;
int rc;
- if (jbd2_write_access_granted(handle, bh))
+ if (jbd2_write_access_granted(handle, bh, false))
return 0;
jh = jbd2_journal_add_journal_head(bh);
@@ -1210,7 +1212,7 @@ int jbd2_journal_get_undo_access(handle_t *handle, struct buffer_head *bh)
char *committed_data = NULL;
JBUFFER_TRACE(jh, "entry");
- if (jbd2_write_access_granted(handle, bh))
+ if (jbd2_write_access_granted(handle, bh, true))
return 0;
jh = jbd2_journal_add_journal_head(bh);
@@ -2152,6 +2154,7 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh,
if (!buffer_dirty(bh)) {
/* bdflush has written it. We can drop it now */
+ __jbd2_journal_remove_checkpoint(jh);
goto zap_buffer;
}
@@ -2181,6 +2184,7 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh,
/* The orphan record's transaction has
* committed. We can cleanse this buffer */
clear_buffer_jbddirty(bh);
+ __jbd2_journal_remove_checkpoint(jh);
goto zap_buffer;
}
}
diff --git a/fs/jffs2/security.c b/fs/jffs2/security.c
index bf12fe5f83d7..7a28facd7175 100644
--- a/fs/jffs2/security.c
+++ b/fs/jffs2/security.c
@@ -52,9 +52,6 @@ static int jffs2_security_getxattr(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
void *buffer, size_t size)
{
- if (!strcmp(name, ""))
- return -EINVAL;
-
return do_jffs2_getxattr(d_inode(dentry), JFFS2_XPREFIX_SECURITY,
name, buffer, size);
}
@@ -63,31 +60,12 @@ static int jffs2_security_setxattr(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
const void *buffer, size_t size, int flags)
{
- if (!strcmp(name, ""))
- return -EINVAL;
-
return do_jffs2_setxattr(d_inode(dentry), JFFS2_XPREFIX_SECURITY,
name, buffer, size, flags);
}
-static size_t jffs2_security_listxattr(const struct xattr_handler *handler,
- struct dentry *dentry, char *list,
- size_t list_size, const char *name,
- size_t name_len)
-{
- size_t retlen = XATTR_SECURITY_PREFIX_LEN + name_len + 1;
-
- if (list && retlen <= list_size) {
- strcpy(list, XATTR_SECURITY_PREFIX);
- strcpy(list + XATTR_SECURITY_PREFIX_LEN, name);
- }
-
- return retlen;
-}
-
const struct xattr_handler jffs2_security_xattr_handler = {
.prefix = XATTR_SECURITY_PREFIX,
- .list = jffs2_security_listxattr,
.set = jffs2_security_setxattr,
.get = jffs2_security_getxattr
};
diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
index d86c5e3176a1..bb080c272149 100644
--- a/fs/jffs2/super.c
+++ b/fs/jffs2/super.c
@@ -387,7 +387,7 @@ static int __init init_jffs2_fs(void)
jffs2_inode_cachep = kmem_cache_create("jffs2_i",
sizeof(struct jffs2_inode_info),
0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
jffs2_i_init_once);
if (!jffs2_inode_cachep) {
pr_err("error: Failed to initialise inode cache\n");
diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c
index 8ce2f240125b..2cabd649d4fb 100644
--- a/fs/jffs2/symlink.c
+++ b/fs/jffs2/symlink.c
@@ -14,7 +14,7 @@
const struct inode_operations jffs2_symlink_inode_operations =
{
.readlink = generic_readlink,
- .follow_link = simple_follow_link,
+ .get_link = simple_get_link,
.setattr = jffs2_setattr,
.setxattr = jffs2_setxattr,
.getxattr = jffs2_getxattr,
diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c
index f3a4857ff071..5a3da3f52908 100644
--- a/fs/jffs2/wbuf.c
+++ b/fs/jffs2/wbuf.c
@@ -1153,7 +1153,7 @@ static struct jffs2_sb_info *work_to_sb(struct work_struct *work)
{
struct delayed_work *dwork;
- dwork = container_of(work, struct delayed_work, work);
+ dwork = to_delayed_work(work);
return container_of(dwork, struct jffs2_sb_info, wbuf_dwork);
}
diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c
index 4c2c03663533..da3e18503c65 100644
--- a/fs/jffs2/xattr.c
+++ b/fs/jffs2/xattr.c
@@ -967,7 +967,8 @@ ssize_t jffs2_listxattr(struct dentry *dentry, char *buffer, size_t size)
struct jffs2_xattr_ref *ref, **pref;
struct jffs2_xattr_datum *xd;
const struct xattr_handler *xhandle;
- ssize_t len, rc;
+ const char *prefix;
+ ssize_t prefix_len, len, rc;
int retry = 0;
rc = check_xattr_ref_inode(c, ic);
@@ -998,18 +999,23 @@ ssize_t jffs2_listxattr(struct dentry *dentry, char *buffer, size_t size)
}
}
xhandle = xprefix_to_handler(xd->xprefix);
- if (!xhandle)
+ if (!xhandle || (xhandle->list && !xhandle->list(dentry)))
continue;
+ prefix = xhandle->prefix ?: xhandle->name;
+ prefix_len = strlen(prefix);
+ rc = prefix_len + xd->name_len + 1;
+
if (buffer) {
- rc = xhandle->list(xhandle, dentry, buffer + len,
- size - len, xd->xname,
- xd->name_len);
- } else {
- rc = xhandle->list(xhandle, dentry, NULL, 0,
- xd->xname, xd->name_len);
+ if (rc > size - len) {
+ rc = -ERANGE;
+ goto out;
+ }
+ memcpy(buffer, prefix, prefix_len);
+ buffer += prefix_len;
+ memcpy(buffer, xd->xname, xd->name_len);
+ buffer += xd->name_len;
+ *buffer++ = 0;
}
- if (rc < 0)
- goto out;
len += rc;
}
rc = len;
diff --git a/fs/jffs2/xattr_trusted.c b/fs/jffs2/xattr_trusted.c
index a562da0d6a26..b2555ef07a12 100644
--- a/fs/jffs2/xattr_trusted.c
+++ b/fs/jffs2/xattr_trusted.c
@@ -20,8 +20,6 @@ static int jffs2_trusted_getxattr(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
void *buffer, size_t size)
{
- if (!strcmp(name, ""))
- return -EINVAL;
return do_jffs2_getxattr(d_inode(dentry), JFFS2_XPREFIX_TRUSTED,
name, buffer, size);
}
@@ -30,28 +28,13 @@ static int jffs2_trusted_setxattr(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
const void *buffer, size_t size, int flags)
{
- if (!strcmp(name, ""))
- return -EINVAL;
return do_jffs2_setxattr(d_inode(dentry), JFFS2_XPREFIX_TRUSTED,
name, buffer, size, flags);
}
-static size_t jffs2_trusted_listxattr(const struct xattr_handler *handler,
- struct dentry *dentry, char *list,
- size_t list_size, const char *name,
- size_t name_len)
+static bool jffs2_trusted_listxattr(struct dentry *dentry)
{
- size_t retlen = XATTR_TRUSTED_PREFIX_LEN + name_len + 1;
-
- if (!capable(CAP_SYS_ADMIN))
- return 0;
-
- if (list && retlen<=list_size) {
- strcpy(list, XATTR_TRUSTED_PREFIX);
- strcpy(list + XATTR_TRUSTED_PREFIX_LEN, name);
- }
-
- return retlen;
+ return capable(CAP_SYS_ADMIN);
}
const struct xattr_handler jffs2_trusted_xattr_handler = {
diff --git a/fs/jffs2/xattr_user.c b/fs/jffs2/xattr_user.c
index cbc0472e59a8..539bd630b5e4 100644
--- a/fs/jffs2/xattr_user.c
+++ b/fs/jffs2/xattr_user.c
@@ -20,8 +20,6 @@ static int jffs2_user_getxattr(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
void *buffer, size_t size)
{
- if (!strcmp(name, ""))
- return -EINVAL;
return do_jffs2_getxattr(d_inode(dentry), JFFS2_XPREFIX_USER,
name, buffer, size);
}
@@ -30,30 +28,12 @@ static int jffs2_user_setxattr(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
const void *buffer, size_t size, int flags)
{
- if (!strcmp(name, ""))
- return -EINVAL;
return do_jffs2_setxattr(d_inode(dentry), JFFS2_XPREFIX_USER,
name, buffer, size, flags);
}
-static size_t jffs2_user_listxattr(const struct xattr_handler *handler,
- struct dentry *dentry, char *list,
- size_t list_size, const char *name,
- size_t name_len)
-{
- size_t retlen = XATTR_USER_PREFIX_LEN + name_len + 1;
-
- if (list && retlen <= list_size) {
- strcpy(list, XATTR_USER_PREFIX);
- strcpy(list + XATTR_USER_PREFIX_LEN, name);
- }
-
- return retlen;
-}
-
const struct xattr_handler jffs2_user_xattr_handler = {
.prefix = XATTR_USER_PREFIX,
- .list = jffs2_user_listxattr,
.set = jffs2_user_setxattr,
.get = jffs2_user_getxattr
};
diff --git a/fs/jfs/acl.c b/fs/jfs/acl.c
index 0c8ca830b113..49456853e9de 100644
--- a/fs/jfs/acl.c
+++ b/fs/jfs/acl.c
@@ -40,10 +40,10 @@ struct posix_acl *jfs_get_acl(struct inode *inode, int type)
switch(type) {
case ACL_TYPE_ACCESS:
- ea_name = POSIX_ACL_XATTR_ACCESS;
+ ea_name = XATTR_NAME_POSIX_ACL_ACCESS;
break;
case ACL_TYPE_DEFAULT:
- ea_name = POSIX_ACL_XATTR_DEFAULT;
+ ea_name = XATTR_NAME_POSIX_ACL_DEFAULT;
break;
default:
return ERR_PTR(-EINVAL);
@@ -82,7 +82,7 @@ static int __jfs_set_acl(tid_t tid, struct inode *inode, int type,
switch (type) {
case ACL_TYPE_ACCESS:
- ea_name = POSIX_ACL_XATTR_ACCESS;
+ ea_name = XATTR_NAME_POSIX_ACL_ACCESS;
if (acl) {
rc = posix_acl_equiv_mode(acl, &inode->i_mode);
if (rc < 0)
@@ -94,7 +94,7 @@ static int __jfs_set_acl(tid_t tid, struct inode *inode, int type,
}
break;
case ACL_TYPE_DEFAULT:
- ea_name = POSIX_ACL_XATTR_DEFAULT;
+ ea_name = XATTR_NAME_POSIX_ACL_DEFAULT;
break;
default:
return -EINVAL;
diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c
index 41aa3ca6a6a4..9d9bae63ae2a 100644
--- a/fs/jfs/inode.c
+++ b/fs/jfs/inode.c
@@ -60,6 +60,7 @@ struct inode *jfs_iget(struct super_block *sb, unsigned long ino)
} else if (S_ISLNK(inode->i_mode)) {
if (inode->i_size >= IDATASIZE) {
inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_mapping->a_ops = &jfs_aops;
} else {
inode->i_op = &jfs_fast_symlink_inode_operations;
diff --git a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c
index a69bdf2a1085..a270cb7ff4e0 100644
--- a/fs/jfs/jfs_logmgr.c
+++ b/fs/jfs/jfs_logmgr.c
@@ -1835,17 +1835,16 @@ static int lbmLogInit(struct jfs_log * log)
for (i = 0; i < LOGPAGES;) {
char *buffer;
uint offset;
- struct page *page;
+ struct page *page = alloc_page(GFP_KERNEL | __GFP_ZERO);
- buffer = (char *) get_zeroed_page(GFP_KERNEL);
- if (buffer == NULL)
+ if (!page)
goto error;
- page = virt_to_page(buffer);
+ buffer = page_address(page);
for (offset = 0; offset < PAGE_SIZE; offset += LOGPSIZE) {
lbuf = kmalloc(sizeof(struct lbuf), GFP_KERNEL);
if (lbuf == NULL) {
if (offset == 0)
- free_page((unsigned long) buffer);
+ __free_page(page);
goto error;
}
if (offset) /* we already have one reference */
diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c
index 9d7551f5c32a..701f89370de7 100644
--- a/fs/jfs/namei.c
+++ b/fs/jfs/namei.c
@@ -983,6 +983,7 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry,
jfs_info("jfs_symlink: allocate extent ip:0x%p", ip);
ip->i_op = &jfs_symlink_inode_operations;
+ inode_nohighmem(ip);
ip->i_mapping->a_ops = &jfs_aops;
/*
diff --git a/fs/jfs/super.c b/fs/jfs/super.c
index 8f9176caf098..900925b5eb8c 100644
--- a/fs/jfs/super.c
+++ b/fs/jfs/super.c
@@ -898,7 +898,7 @@ static int __init init_jfs_fs(void)
jfs_inode_cachep =
kmem_cache_create("jfs_ip", sizeof(struct jfs_inode_info), 0,
- SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD,
+ SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|SLAB_ACCOUNT,
init_once);
if (jfs_inode_cachep == NULL)
return -ENOMEM;
diff --git a/fs/jfs/symlink.c b/fs/jfs/symlink.c
index 5929e2363cb8..f8db4fde0b0b 100644
--- a/fs/jfs/symlink.c
+++ b/fs/jfs/symlink.c
@@ -23,7 +23,7 @@
const struct inode_operations jfs_fast_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = simple_follow_link,
+ .get_link = simple_get_link,
.setattr = jfs_setattr,
.setxattr = jfs_setxattr,
.getxattr = jfs_getxattr,
@@ -33,8 +33,7 @@ const struct inode_operations jfs_fast_symlink_inode_operations = {
const struct inode_operations jfs_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = page_follow_link_light,
- .put_link = page_put_link,
+ .get_link = page_get_link,
.setattr = jfs_setattr,
.setxattr = jfs_setxattr,
.getxattr = jfs_getxattr,
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 91e004518237..821973853340 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -541,14 +541,7 @@ static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root,
if (!kn)
goto err_out1;
- /*
- * If the ino of the sysfs entry created for a kmem cache gets
- * allocated from an ida layer, which is accounted to the memcg that
- * owns the cache, the memcg will get pinned forever. So do not account
- * ino ida allocations.
- */
- ret = ida_simple_get(&root->ino_ida, 1, 0,
- GFP_KERNEL | __GFP_NOACCOUNT);
+ ret = ida_simple_get(&root->ino_ida, 1, 0, GFP_KERNEL);
if (ret < 0)
goto err_out2;
kn->ino = ret;
@@ -694,6 +687,29 @@ static struct kernfs_node *kernfs_find_ns(struct kernfs_node *parent,
return NULL;
}
+static struct kernfs_node *kernfs_walk_ns(struct kernfs_node *parent,
+ const unsigned char *path,
+ const void *ns)
+{
+ static char path_buf[PATH_MAX]; /* protected by kernfs_mutex */
+ size_t len = strlcpy(path_buf, path, PATH_MAX);
+ char *p = path_buf;
+ char *name;
+
+ lockdep_assert_held(&kernfs_mutex);
+
+ if (len >= PATH_MAX)
+ return NULL;
+
+ while ((name = strsep(&p, "/")) && parent) {
+ if (*name == '\0')
+ continue;
+ parent = kernfs_find_ns(parent, name, ns);
+ }
+
+ return parent;
+}
+
/**
* kernfs_find_and_get_ns - find and get kernfs_node with the given name
* @parent: kernfs_node to search under
@@ -719,6 +735,29 @@ struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent,
EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns);
/**
+ * kernfs_walk_and_get_ns - find and get kernfs_node with the given path
+ * @parent: kernfs_node to search under
+ * @path: path to look for
+ * @ns: the namespace tag to use
+ *
+ * Look for kernfs_node with path @path under @parent and get a reference
+ * if found. This function may sleep and returns pointer to the found
+ * kernfs_node on success, %NULL on failure.
+ */
+struct kernfs_node *kernfs_walk_and_get_ns(struct kernfs_node *parent,
+ const char *path, const void *ns)
+{
+ struct kernfs_node *kn;
+
+ mutex_lock(&kernfs_mutex);
+ kn = kernfs_walk_ns(parent, path, ns);
+ kernfs_get(kn);
+ mutex_unlock(&kernfs_mutex);
+
+ return kn;
+}
+
+/**
* kernfs_create_root - create a new kernfs hierarchy
* @scops: optional syscall operations for the hierarchy
* @flags: KERNFS_ROOT_* flags
diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c
index 756dd56aaf60..16405ae88d2d 100644
--- a/fs/kernfs/inode.c
+++ b/fs/kernfs/inode.c
@@ -205,7 +205,7 @@ int kernfs_iop_removexattr(struct dentry *dentry, const char *name)
if (!attrs)
return -ENOMEM;
- return simple_xattr_remove(&attrs->xattrs, name);
+ return simple_xattr_set(&attrs->xattrs, name, NULL, 0, XATTR_REPLACE);
}
ssize_t kernfs_iop_getxattr(struct dentry *dentry, const char *name, void *buf,
@@ -230,7 +230,7 @@ ssize_t kernfs_iop_listxattr(struct dentry *dentry, char *buf, size_t size)
if (!attrs)
return -ENOMEM;
- return simple_xattr_list(&attrs->xattrs, buf, size);
+ return simple_xattr_list(d_inode(dentry), &attrs->xattrs, buf, size);
}
static inline void set_default_inode_attr(struct inode *inode, umode_t mode)
diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c
index db272528ab5b..117b8b3416f9 100644
--- a/fs/kernfs/symlink.c
+++ b/fs/kernfs/symlink.c
@@ -112,18 +112,25 @@ static int kernfs_getlink(struct dentry *dentry, char *path)
return error;
}
-static const char *kernfs_iop_follow_link(struct dentry *dentry, void **cookie)
+static const char *kernfs_iop_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
- int error = -ENOMEM;
- unsigned long page = get_zeroed_page(GFP_KERNEL);
- if (!page)
+ char *body;
+ int error;
+
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
+ body = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!body)
return ERR_PTR(-ENOMEM);
- error = kernfs_getlink(dentry, (char *)page);
+ error = kernfs_getlink(dentry, body);
if (unlikely(error < 0)) {
- free_page((unsigned long)page);
+ kfree(body);
return ERR_PTR(error);
}
- return *cookie = (char *)page;
+ set_delayed_call(done, kfree_link, body);
+ return body;
}
const struct inode_operations kernfs_symlink_iops = {
@@ -132,8 +139,7 @@ const struct inode_operations kernfs_symlink_iops = {
.getxattr = kernfs_iop_getxattr,
.listxattr = kernfs_iop_listxattr,
.readlink = generic_readlink,
- .follow_link = kernfs_iop_follow_link,
- .put_link = free_page_put_link,
+ .get_link = kernfs_iop_get_link,
.setattr = kernfs_iop_setattr,
.getattr = kernfs_iop_getattr,
.permission = kernfs_iop_permission,
diff --git a/fs/libfs.c b/fs/libfs.c
index c7cbfb092e94..01491299f348 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -1019,17 +1019,12 @@ int noop_fsync(struct file *file, loff_t start, loff_t end, int datasync)
}
EXPORT_SYMBOL(noop_fsync);
-void kfree_put_link(struct inode *unused, void *cookie)
+/* Because kfree isn't assignment-compatible with void(void*) ;-/ */
+void kfree_link(void *p)
{
- kfree(cookie);
+ kfree(p);
}
-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);
+EXPORT_SYMBOL(kfree_link);
/*
* nop .set_page_dirty method so that people can use .page_mkwrite on
@@ -1092,14 +1087,15 @@ simple_nosetlease(struct file *filp, long arg, struct file_lock **flp,
}
EXPORT_SYMBOL(simple_nosetlease);
-const char *simple_follow_link(struct dentry *dentry, void **cookie)
+const char *simple_get_link(struct dentry *dentry, struct inode *inode,
+ struct delayed_call *done)
{
- return d_inode(dentry)->i_link;
+ return inode->i_link;
}
-EXPORT_SYMBOL(simple_follow_link);
+EXPORT_SYMBOL(simple_get_link);
const struct inode_operations simple_symlink_inode_operations = {
- .follow_link = simple_follow_link,
+ .get_link = simple_get_link,
.readlink = generic_readlink
};
EXPORT_SYMBOL(simple_symlink_inode_operations);
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index 5f31ebd96c06..154a107cd376 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -25,13 +25,17 @@
#include <linux/mutex.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
+#include <linux/inetdevice.h>
#include <linux/sunrpc/types.h>
#include <linux/sunrpc/stats.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svcsock.h>
+#include <linux/sunrpc/svc_xprt.h>
#include <net/ip.h>
+#include <net/addrconf.h>
+#include <net/ipv6.h>
#include <linux/lockd/lockd.h>
#include <linux/nfs.h>
@@ -44,7 +48,7 @@
static struct svc_program nlmsvc_program;
-struct nlmsvc_binding * nlmsvc_ops;
+const struct nlmsvc_binding *nlmsvc_ops;
EXPORT_SYMBOL_GPL(nlmsvc_ops);
static DEFINE_MUTEX(nlmsvc_mutex);
@@ -90,8 +94,7 @@ static unsigned long get_lockd_grace_period(void)
static void grace_ender(struct work_struct *grace)
{
- struct delayed_work *dwork = container_of(grace, struct delayed_work,
- work);
+ struct delayed_work *dwork = to_delayed_work(grace);
struct lockd_net *ln = container_of(dwork, struct lockd_net,
grace_period_end);
@@ -279,6 +282,68 @@ static void lockd_down_net(struct svc_serv *serv, struct net *net)
}
}
+static int lockd_inetaddr_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
+ struct sockaddr_in sin;
+
+ if (event != NETDEV_DOWN)
+ goto out;
+
+ if (nlmsvc_rqst) {
+ dprintk("lockd_inetaddr_event: removed %pI4\n",
+ &ifa->ifa_local);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = ifa->ifa_local;
+ svc_age_temp_xprts_now(nlmsvc_rqst->rq_server,
+ (struct sockaddr *)&sin);
+ }
+
+out:
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block lockd_inetaddr_notifier = {
+ .notifier_call = lockd_inetaddr_event,
+};
+
+#if IS_ENABLED(CONFIG_IPV6)
+static int lockd_inet6addr_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
+ struct sockaddr_in6 sin6;
+
+ if (event != NETDEV_DOWN)
+ goto out;
+
+ if (nlmsvc_rqst) {
+ dprintk("lockd_inet6addr_event: removed %pI6\n", &ifa->addr);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = ifa->addr;
+ svc_age_temp_xprts_now(nlmsvc_rqst->rq_server,
+ (struct sockaddr *)&sin6);
+ }
+
+out:
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block lockd_inet6addr_notifier = {
+ .notifier_call = lockd_inet6addr_event,
+};
+#endif
+
+static void lockd_svc_exit_thread(void)
+{
+ unregister_inetaddr_notifier(&lockd_inetaddr_notifier);
+#if IS_ENABLED(CONFIG_IPV6)
+ unregister_inet6addr_notifier(&lockd_inet6addr_notifier);
+#endif
+ svc_exit_thread(nlmsvc_rqst);
+}
+
static int lockd_start_svc(struct svc_serv *serv)
{
int error;
@@ -315,7 +380,7 @@ static int lockd_start_svc(struct svc_serv *serv)
return 0;
out_task:
- svc_exit_thread(nlmsvc_rqst);
+ lockd_svc_exit_thread();
nlmsvc_task = NULL;
out_rqst:
nlmsvc_rqst = NULL;
@@ -360,6 +425,10 @@ static struct svc_serv *lockd_create_svc(void)
printk(KERN_WARNING "lockd_up: create service failed\n");
return ERR_PTR(-ENOMEM);
}
+ register_inetaddr_notifier(&lockd_inetaddr_notifier);
+#if IS_ENABLED(CONFIG_IPV6)
+ register_inet6addr_notifier(&lockd_inet6addr_notifier);
+#endif
dprintk("lockd_up: service created\n");
return serv;
}
@@ -428,7 +497,7 @@ lockd_down(struct net *net)
}
kthread_stop(nlmsvc_task);
dprintk("lockd_down: service stopped\n");
- svc_exit_thread(nlmsvc_rqst);
+ lockd_svc_exit_thread();
dprintk("lockd_down: service destroyed\n");
nlmsvc_task = NULL;
nlmsvc_rqst = NULL;
diff --git a/fs/locks.c b/fs/locks.c
index 0d2b3267e2a3..af1ed74a657f 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -119,7 +119,6 @@
#include <linux/fdtable.h>
#include <linux/fs.h>
#include <linux/init.h>
-#include <linux/module.h>
#include <linux/security.h>
#include <linux/slab.h>
#include <linux/syscalls.h>
@@ -230,16 +229,44 @@ locks_get_lock_context(struct inode *inode, int type)
ctx = smp_load_acquire(&inode->i_flctx);
}
out:
+ trace_locks_get_lock_context(inode, type, ctx);
return ctx;
}
+static void
+locks_dump_ctx_list(struct list_head *list, char *list_type)
+{
+ struct file_lock *fl;
+
+ list_for_each_entry(fl, list, fl_list) {
+ pr_warn("%s: fl_owner=%p fl_flags=0x%x fl_type=0x%x fl_pid=%u\n", list_type, fl->fl_owner, fl->fl_flags, fl->fl_type, fl->fl_pid);
+ }
+}
+
+static void
+locks_check_ctx_lists(struct inode *inode)
+{
+ struct file_lock_context *ctx = inode->i_flctx;
+
+ if (unlikely(!list_empty(&ctx->flc_flock) ||
+ !list_empty(&ctx->flc_posix) ||
+ !list_empty(&ctx->flc_lease))) {
+ pr_warn("Leaked locks on dev=0x%x:0x%x ino=0x%lx:\n",
+ MAJOR(inode->i_sb->s_dev), MINOR(inode->i_sb->s_dev),
+ inode->i_ino);
+ locks_dump_ctx_list(&ctx->flc_flock, "FLOCK");
+ locks_dump_ctx_list(&ctx->flc_posix, "POSIX");
+ locks_dump_ctx_list(&ctx->flc_lease, "LEASE");
+ }
+}
+
void
-locks_free_lock_context(struct file_lock_context *ctx)
+locks_free_lock_context(struct inode *inode)
{
- if (ctx) {
- WARN_ON_ONCE(!list_empty(&ctx->flc_flock));
- WARN_ON_ONCE(!list_empty(&ctx->flc_posix));
- WARN_ON_ONCE(!list_empty(&ctx->flc_lease));
+ struct file_lock_context *ctx = inode->i_flctx;
+
+ if (unlikely(ctx)) {
+ locks_check_ctx_lists(inode);
kmem_cache_free(flctx_cache, ctx);
}
}
@@ -934,7 +961,8 @@ out:
return error;
}
-static int __posix_lock_file(struct inode *inode, struct file_lock *request, struct file_lock *conflock)
+static int posix_lock_inode(struct inode *inode, struct file_lock *request,
+ struct file_lock *conflock)
{
struct file_lock *fl, *tmp;
struct file_lock *new_fl = NULL;
@@ -1142,6 +1170,8 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
if (new_fl2)
locks_free_lock(new_fl2);
locks_dispose_list(&dispose);
+ trace_posix_lock_inode(inode, request, error);
+
return error;
}
@@ -1162,7 +1192,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
int posix_lock_file(struct file *filp, struct file_lock *fl,
struct file_lock *conflock)
{
- return __posix_lock_file(file_inode(filp), fl, conflock);
+ return posix_lock_inode(file_inode(filp), fl, conflock);
}
EXPORT_SYMBOL(posix_lock_file);
@@ -1178,7 +1208,7 @@ static int posix_lock_inode_wait(struct inode *inode, struct file_lock *fl)
int error;
might_sleep ();
for (;;) {
- error = __posix_lock_file(inode, fl, NULL);
+ error = posix_lock_inode(inode, fl, NULL);
if (error != FILE_LOCK_DEFERRED)
break;
error = wait_event_interruptible(fl->fl_wait, !fl->fl_next);
@@ -1191,6 +1221,7 @@ static int posix_lock_inode_wait(struct inode *inode, struct file_lock *fl)
return error;
}
+#ifdef CONFIG_MANDATORY_FILE_LOCKING
/**
* locks_mandatory_locked - Check for an active lock
* @file: the file to check
@@ -1227,20 +1258,16 @@ int locks_mandatory_locked(struct file *file)
/**
* locks_mandatory_area - Check for a conflicting lock
- * @read_write: %FLOCK_VERIFY_WRITE for exclusive access, %FLOCK_VERIFY_READ
- * for shared
- * @inode: the file to check
+ * @inode: the file to check
* @filp: how the file was opened (if it was)
- * @offset: start of area to check
- * @count: length of area to check
+ * @start: first byte in the file to check
+ * @end: lastbyte in the file to check
+ * @type: %F_WRLCK for a write lock, else %F_RDLCK
*
* Searches the inode's list of locks to find any POSIX locks which conflict.
- * This function is called from rw_verify_area() and
- * locks_verify_truncate().
*/
-int locks_mandatory_area(int read_write, struct inode *inode,
- struct file *filp, loff_t offset,
- size_t count)
+int locks_mandatory_area(struct inode *inode, struct file *filp, loff_t start,
+ loff_t end, unsigned char type)
{
struct file_lock fl;
int error;
@@ -1252,15 +1279,15 @@ int locks_mandatory_area(int read_write, struct inode *inode,
fl.fl_flags = FL_POSIX | FL_ACCESS;
if (filp && !(filp->f_flags & O_NONBLOCK))
sleep = true;
- fl.fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK;
- fl.fl_start = offset;
- fl.fl_end = offset + count - 1;
+ fl.fl_type = type;
+ fl.fl_start = start;
+ fl.fl_end = end;
for (;;) {
if (filp) {
fl.fl_owner = filp;
fl.fl_flags &= ~FL_SLEEP;
- error = __posix_lock_file(inode, &fl, NULL);
+ error = posix_lock_inode(inode, &fl, NULL);
if (!error)
break;
}
@@ -1268,7 +1295,7 @@ int locks_mandatory_area(int read_write, struct inode *inode,
if (sleep)
fl.fl_flags |= FL_SLEEP;
fl.fl_owner = current->files;
- error = __posix_lock_file(inode, &fl, NULL);
+ error = posix_lock_inode(inode, &fl, NULL);
if (error != FILE_LOCK_DEFERRED)
break;
error = wait_event_interruptible(fl.fl_wait, !fl.fl_next);
@@ -1289,6 +1316,7 @@ int locks_mandatory_area(int read_write, struct inode *inode,
}
EXPORT_SYMBOL(locks_mandatory_area);
+#endif /* CONFIG_MANDATORY_FILE_LOCKING */
static void lease_clear_pending(struct file_lock *fl, int arg)
{
@@ -1503,12 +1531,10 @@ void lease_get_mtime(struct inode *inode, struct timespec *time)
ctx = smp_load_acquire(&inode->i_flctx);
if (ctx && !list_empty_careful(&ctx->flc_lease)) {
spin_lock(&ctx->flc_lock);
- if (!list_empty(&ctx->flc_lease)) {
- fl = list_first_entry(&ctx->flc_lease,
- struct file_lock, fl_list);
- if (fl->fl_type == F_WRLCK)
- has_lease = true;
- }
+ fl = list_first_entry_or_null(&ctx->flc_lease,
+ struct file_lock, fl_list);
+ if (fl && (fl->fl_type == F_WRLCK))
+ has_lease = true;
spin_unlock(&ctx->flc_lock);
}
@@ -2165,6 +2191,8 @@ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
if (file_lock == NULL)
return -ENOLCK;
+ inode = file_inode(filp);
+
/*
* This might block, so we do it before checking the inode.
*/
@@ -2172,8 +2200,6 @@ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
if (copy_from_user(&flock, l, sizeof(flock)))
goto out;
- inode = file_inode(filp);
-
/* Don't allow mandatory locks on files that may be memory mapped
* and shared.
*/
@@ -2182,7 +2208,6 @@ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
goto out;
}
-again:
error = flock_to_posix_lock(filp, file_lock, &flock);
if (error)
goto out;
@@ -2221,23 +2246,29 @@ again:
error = do_lock_file_wait(filp, cmd, file_lock);
/*
- * Attempt to detect a close/fcntl race and recover by
- * releasing the lock that was just acquired.
- */
- /*
- * we need that spin_lock here - it prevents reordering between
- * update of i_flctx->flc_posix and check for it done in close().
- * rcu_read_lock() wouldn't do.
+ * Attempt to detect a close/fcntl race and recover by releasing the
+ * lock that was just acquired. There is no need to do that when we're
+ * unlocking though, or for OFD locks.
*/
- spin_lock(&current->files->file_lock);
- f = fcheck(fd);
- spin_unlock(&current->files->file_lock);
- if (!error && f != filp && flock.l_type != F_UNLCK) {
- flock.l_type = F_UNLCK;
- goto again;
+ if (!error && file_lock->fl_type != F_UNLCK &&
+ !(file_lock->fl_flags & FL_OFDLCK)) {
+ /*
+ * We need that spin_lock here - it prevents reordering between
+ * update of i_flctx->flc_posix and check for it done in
+ * close(). rcu_read_lock() wouldn't do.
+ */
+ spin_lock(&current->files->file_lock);
+ f = fcheck(fd);
+ spin_unlock(&current->files->file_lock);
+ if (f != filp) {
+ file_lock->fl_type = F_UNLCK;
+ error = do_lock_file_wait(filp, cmd, file_lock);
+ WARN_ON_ONCE(error);
+ error = -EBADF;
+ }
}
-
out:
+ trace_fcntl_setlk(inode, file_lock, error);
locks_free_lock(file_lock);
return error;
}
@@ -2322,7 +2353,6 @@ int fcntl_setlk64(unsigned int fd, struct file *filp, unsigned int cmd,
goto out;
}
-again:
error = flock64_to_posix_lock(filp, file_lock, &flock);
if (error)
goto out;
@@ -2361,17 +2391,27 @@ again:
error = do_lock_file_wait(filp, cmd, file_lock);
/*
- * Attempt to detect a close/fcntl race and recover by
- * releasing the lock that was just acquired.
+ * Attempt to detect a close/fcntl race and recover by releasing the
+ * lock that was just acquired. There is no need to do that when we're
+ * unlocking though, or for OFD locks.
*/
- spin_lock(&current->files->file_lock);
- f = fcheck(fd);
- spin_unlock(&current->files->file_lock);
- if (!error && f != filp && flock.l_type != F_UNLCK) {
- flock.l_type = F_UNLCK;
- goto again;
+ if (!error && file_lock->fl_type != F_UNLCK &&
+ !(file_lock->fl_flags & FL_OFDLCK)) {
+ /*
+ * We need that spin_lock here - it prevents reordering between
+ * update of i_flctx->flc_posix and check for it done in
+ * close(). rcu_read_lock() wouldn't do.
+ */
+ spin_lock(&current->files->file_lock);
+ f = fcheck(fd);
+ spin_unlock(&current->files->file_lock);
+ if (f != filp) {
+ file_lock->fl_type = F_UNLCK;
+ error = do_lock_file_wait(filp, cmd, file_lock);
+ WARN_ON_ONCE(error);
+ error = -EBADF;
+ }
}
-
out:
locks_free_lock(file_lock);
return error;
@@ -2385,6 +2425,7 @@ out:
*/
void locks_remove_posix(struct file *filp, fl_owner_t owner)
{
+ int error;
struct file_lock lock;
struct file_lock_context *ctx;
@@ -2407,10 +2448,11 @@ void locks_remove_posix(struct file *filp, fl_owner_t owner)
lock.fl_ops = NULL;
lock.fl_lmops = NULL;
- vfs_lock_file(filp, F_SETLK, &lock, NULL);
+ error = vfs_lock_file(filp, F_SETLK, &lock, NULL);
if (lock.fl_ops && lock.fl_ops->fl_release_private)
lock.fl_ops->fl_release_private(&lock);
+ trace_locks_remove_posix(file_inode(filp), &lock, error);
}
EXPORT_SYMBOL(locks_remove_posix);
@@ -2706,7 +2748,7 @@ static int __init proc_locks_init(void)
proc_create("locks", 0, NULL, &proc_locks_operations);
return 0;
}
-module_init(proc_locks_init);
+fs_initcall(proc_locks_init);
#endif
static int __init filelock_init(void)
diff --git a/fs/logfs/Kconfig b/fs/logfs/Kconfig
index 09ed066c0221..2b4503163930 100644
--- a/fs/logfs/Kconfig
+++ b/fs/logfs/Kconfig
@@ -1,6 +1,6 @@
config LOGFS
tristate "LogFS file system"
- depends on (MTD || BLOCK)
+ depends on MTD || (!MTD && BLOCK)
select ZLIB_INFLATE
select ZLIB_DEFLATE
select CRC32
diff --git a/fs/logfs/dir.c b/fs/logfs/dir.c
index f9b45d46d4c4..542468e9bfb4 100644
--- a/fs/logfs/dir.c
+++ b/fs/logfs/dir.c
@@ -528,7 +528,8 @@ static int logfs_symlink(struct inode *dir, struct dentry *dentry,
if (IS_ERR(inode))
return PTR_ERR(inode);
- inode->i_op = &logfs_symlink_iops;
+ inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_mapping->a_ops = &logfs_reg_aops;
return __logfs_create(dir, dentry, inode, target, destlen);
@@ -776,12 +777,6 @@ fail:
return -EIO;
}
-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 = {
.create = logfs_create,
.link = logfs_link,
diff --git a/fs/logfs/inode.c b/fs/logfs/inode.c
index af49e2d6941a..db9cfc598883 100644
--- a/fs/logfs/inode.c
+++ b/fs/logfs/inode.c
@@ -64,7 +64,8 @@ static void logfs_inode_setops(struct inode *inode)
inode->i_mapping->a_ops = &logfs_reg_aops;
break;
case S_IFLNK:
- inode->i_op = &logfs_symlink_iops;
+ inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_mapping->a_ops = &logfs_reg_aops;
break;
case S_IFSOCK: /* fall through */
@@ -408,7 +409,8 @@ const struct super_operations logfs_super_operations = {
int logfs_init_inode_cache(void)
{
logfs_inode_cache = kmem_cache_create("logfs_inode_cache",
- sizeof(struct logfs_inode), 0, SLAB_RECLAIM_ACCOUNT,
+ sizeof(struct logfs_inode), 0,
+ SLAB_RECLAIM_ACCOUNT|SLAB_ACCOUNT,
logfs_init_once);
if (!logfs_inode_cache)
return -ENOMEM;
diff --git a/fs/logfs/logfs.h b/fs/logfs/logfs.h
index 5f0937609465..39d91f86cd35 100644
--- a/fs/logfs/logfs.h
+++ b/fs/logfs/logfs.h
@@ -302,7 +302,7 @@ struct logfs_block {
struct inode *inode;
struct logfs_transaction *ta;
unsigned long alias_map[LOGFS_BLOCK_FACTOR / BITS_PER_LONG];
- struct logfs_block_ops *ops;
+ const struct logfs_block_ops *ops;
int full;
int partial;
int reserved_bytes;
@@ -495,7 +495,6 @@ static inline int logfs_get_sb_mtd(struct logfs_super *s, int mtdnr)
#endif
/* dir.c */
-extern const struct inode_operations logfs_symlink_iops;
extern const struct inode_operations logfs_dir_iops;
extern const struct file_operations logfs_dir_fops;
int logfs_replay_journal(struct super_block *sb);
@@ -579,7 +578,7 @@ int logfs_exist_block(struct inode *inode, u64 bix);
int get_page_reserve(struct inode *inode, struct page *page);
void logfs_get_wblocks(struct super_block *sb, struct page *page, int lock);
void logfs_put_wblocks(struct super_block *sb, struct page *page, int lock);
-extern struct logfs_block_ops indirect_block_ops;
+extern const struct logfs_block_ops indirect_block_ops;
/* segment.c */
int logfs_erase_segment(struct super_block *sb, u32 ofs, int ensure_erase);
diff --git a/fs/logfs/readwrite.c b/fs/logfs/readwrite.c
index 380d86e1ab45..20973c9e52f8 100644
--- a/fs/logfs/readwrite.c
+++ b/fs/logfs/readwrite.c
@@ -569,13 +569,13 @@ static void indirect_free_block(struct super_block *sb,
}
-static struct logfs_block_ops inode_block_ops = {
+static const struct logfs_block_ops inode_block_ops = {
.write_block = inode_write_block,
.free_block = inode_free_block,
.write_alias = inode_write_alias,
};
-struct logfs_block_ops indirect_block_ops = {
+const struct logfs_block_ops indirect_block_ops = {
.write_block = indirect_write_block,
.free_block = indirect_free_block,
.write_alias = indirect_write_alias,
diff --git a/fs/logfs/segment.c b/fs/logfs/segment.c
index 6de0fbfc6c00..d270e4b2ab6b 100644
--- a/fs/logfs/segment.c
+++ b/fs/logfs/segment.c
@@ -197,7 +197,7 @@ static int btree_write_alias(struct super_block *sb, struct logfs_block *block,
return 0;
}
-static struct logfs_block_ops btree_block_ops = {
+static const struct logfs_block_ops btree_block_ops = {
.write_block = btree_write_block,
.free_block = __free_block,
.write_alias = btree_write_alias,
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index 086cd0a61e80..f975d667c539 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -91,7 +91,7 @@ static int __init init_inodecache(void)
minix_inode_cachep = kmem_cache_create("minix_inode_cache",
sizeof(struct minix_inode_info),
0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
init_once);
if (minix_inode_cachep == NULL)
return -ENOMEM;
@@ -435,8 +435,7 @@ static const struct address_space_operations minix_aops = {
static const struct inode_operations minix_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = page_follow_link_light,
- .put_link = page_put_link,
+ .get_link = page_get_link,
.getattr = minix_getattr,
};
@@ -452,6 +451,7 @@ void minix_set_inode(struct inode *inode, dev_t rdev)
inode->i_mapping->a_ops = &minix_aops;
} else if (S_ISLNK(inode->i_mode)) {
inode->i_op = &minix_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_mapping->a_ops = &minix_aops;
} else
init_special_inode(inode, inode->i_mode, rdev);
diff --git a/fs/minix/itree_v1.c b/fs/minix/itree_v1.c
index 282e15ad8cd8..46ca39d6c735 100644
--- a/fs/minix/itree_v1.c
+++ b/fs/minix/itree_v1.c
@@ -24,16 +24,15 @@ static inline block_t *i_data(struct inode *inode)
static int block_to_path(struct inode * inode, long block, int offsets[DEPTH])
{
int n = 0;
- char b[BDEVNAME_SIZE];
if (block < 0) {
- printk("MINIX-fs: block_to_path: block %ld < 0 on dev %s\n",
- block, bdevname(inode->i_sb->s_bdev, b));
+ printk("MINIX-fs: block_to_path: block %ld < 0 on dev %pg\n",
+ block, inode->i_sb->s_bdev);
} else if (block >= (minix_sb(inode->i_sb)->s_max_size/BLOCK_SIZE)) {
if (printk_ratelimit())
printk("MINIX-fs: block_to_path: "
- "block %ld too big on dev %s\n",
- block, bdevname(inode->i_sb->s_bdev, b));
+ "block %ld too big on dev %pg\n",
+ block, inode->i_sb->s_bdev);
} else if (block < 7) {
offsets[n++] = block;
} else if ((block -= 7) < 512) {
diff --git a/fs/minix/itree_v2.c b/fs/minix/itree_v2.c
index 78e2d93e5c83..1ee101352586 100644
--- a/fs/minix/itree_v2.c
+++ b/fs/minix/itree_v2.c
@@ -26,18 +26,17 @@ static inline block_t *i_data(struct inode *inode)
static int block_to_path(struct inode * inode, long block, int offsets[DEPTH])
{
int n = 0;
- char b[BDEVNAME_SIZE];
struct super_block *sb = inode->i_sb;
if (block < 0) {
- printk("MINIX-fs: block_to_path: block %ld < 0 on dev %s\n",
- block, bdevname(sb->s_bdev, b));
+ printk("MINIX-fs: block_to_path: block %ld < 0 on dev %pg\n",
+ block, sb->s_bdev);
} else if ((u64)block * (u64)sb->s_blocksize >=
minix_sb(sb)->s_max_size) {
if (printk_ratelimit())
printk("MINIX-fs: block_to_path: "
- "block %ld too big on dev %s\n",
- block, bdevname(sb->s_bdev, b));
+ "block %ld too big on dev %pg\n",
+ block, sb->s_bdev);
} else if (block < DIRCOUNT) {
offsets[n++] = block;
} else if ((block -= DIRCOUNT) < INDIRCOUNT(sb)) {
diff --git a/fs/namei.c b/fs/namei.c
index d84d7c7515fc..bceefd5588a2 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -505,13 +505,13 @@ struct nameidata {
int total_link_count;
struct saved {
struct path link;
- void *cookie;
+ struct delayed_call done;
const char *name;
- struct inode *inode;
unsigned seq;
} *stack, internal[EMBEDDED_LEVELS];
struct filename *name;
struct nameidata *saved;
+ struct inode *link_inode;
unsigned root_seq;
int dfd;
};
@@ -534,10 +534,8 @@ static void restore_nameidata(void)
current->nameidata = old;
if (old)
old->total_link_count = now->total_link_count;
- if (now->stack != now->internal) {
+ if (now->stack != now->internal)
kfree(now->stack);
- now->stack = now->internal;
- }
}
static int __nd_alloc_stack(struct nameidata *nd)
@@ -592,11 +590,8 @@ 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;
- }
+ do_delayed_call(&last->done);
+ clear_delayed_call(&last->done);
}
}
@@ -657,7 +652,7 @@ static bool legitimize_links(struct nameidata *nd)
* Path walking has 2 modes, rcu-walk and ref-walk (see
* Documentation/filesystems/path-lookup.txt). In situations when we can't
* continue in RCU mode, we attempt to drop out of rcu-walk mode and grab
- * normal reference counts on dentries and vfsmounts to transition to rcu-walk
+ * normal reference counts on dentries and vfsmounts to transition to ref-walk
* mode. Refcounts are grabbed at the last known good point before rcu-walk
* got stuck, so ref-walk may continue from there. If this is not successful
* (eg. a seqcount has changed), then failure is returned and it's up to caller
@@ -807,19 +802,19 @@ static int complete_walk(struct nameidata *nd)
static void set_root(struct nameidata *nd)
{
- get_fs_root(current->fs, &nd->root);
-}
-
-static void set_root_rcu(struct nameidata *nd)
-{
struct fs_struct *fs = current->fs;
- unsigned seq;
- do {
- seq = read_seqcount_begin(&fs->seq);
- nd->root = fs->root;
- nd->root_seq = __read_seqcount_begin(&nd->root.dentry->d_seq);
- } while (read_seqcount_retry(&fs->seq, seq));
+ if (nd->flags & LOOKUP_RCU) {
+ unsigned seq;
+
+ do {
+ seq = read_seqcount_begin(&fs->seq);
+ nd->root = fs->root;
+ nd->root_seq = __read_seqcount_begin(&nd->root.dentry->d_seq);
+ } while (read_seqcount_retry(&fs->seq, seq));
+ } else {
+ get_fs_root(fs, &nd->root);
+ }
}
static void path_put_conditional(struct path *path, struct nameidata *nd)
@@ -841,8 +836,28 @@ static inline void path_to_nameidata(const struct path *path,
nd->path.dentry = path->dentry;
}
+static int nd_jump_root(struct nameidata *nd)
+{
+ if (nd->flags & LOOKUP_RCU) {
+ struct dentry *d;
+ 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 -ECHILD;
+ } else {
+ path_put(&nd->path);
+ nd->path = nd->root;
+ path_get(&nd->path);
+ nd->inode = nd->path.dentry->d_inode;
+ }
+ nd->flags |= LOOKUP_JUMPED;
+ return 0;
+}
+
/*
- * Helper to directly jump to a known parsed path from ->follow_link,
+ * Helper to directly jump to a known parsed path from ->get_link,
* caller must have taken a reference to path beforehand.
*/
void nd_jump_link(struct path *path)
@@ -858,9 +873,7 @@ void nd_jump_link(struct path *path)
static inline void put_link(struct nameidata *nd)
{
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);
+ do_delayed_call(&last->done);
if (!(nd->flags & LOOKUP_RCU))
path_put(&last->link);
}
@@ -892,7 +905,7 @@ static inline int may_follow_link(struct nameidata *nd)
return 0;
/* Allowed if owner and follower match. */
- inode = nd->stack[0].inode;
+ inode = nd->link_inode;
if (uid_eq(current_cred()->fsuid, inode->i_uid))
return 0;
@@ -983,7 +996,7 @@ const char *get_link(struct nameidata *nd)
{
struct saved *last = nd->stack + nd->depth - 1;
struct dentry *dentry = last->link.dentry;
- struct inode *inode = last->inode;
+ struct inode *inode = nd->link_inode;
int error;
const char *res;
@@ -1004,36 +1017,27 @@ const char *get_link(struct nameidata *nd)
nd->last_type = LAST_BIND;
res = inode->i_link;
if (!res) {
+ const char * (*get)(struct dentry *, struct inode *,
+ struct delayed_call *);
+ get = inode->i_op->get_link;
if (nd->flags & LOOKUP_RCU) {
- if (unlikely(unlazy_walk(nd, NULL, 0)))
- return ERR_PTR(-ECHILD);
+ res = get(NULL, inode, &last->done);
+ if (res == ERR_PTR(-ECHILD)) {
+ if (unlikely(unlazy_walk(nd, NULL, 0)))
+ return ERR_PTR(-ECHILD);
+ res = get(dentry, inode, &last->done);
+ }
+ } else {
+ res = get(dentry, inode, &last->done);
}
- res = inode->i_op->follow_link(dentry, &last->cookie);
- if (IS_ERR_OR_NULL(res)) {
- last->cookie = NULL;
+ if (IS_ERR_OR_NULL(res))
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->inode = nd->path.dentry->d_inode;
- }
- nd->flags |= LOOKUP_JUMPED;
+ if (!nd->root.mnt)
+ set_root(nd);
+ if (unlikely(nd_jump_root(nd)))
+ return ERR_PTR(-ECHILD);
while (unlikely(*++res == '/'))
;
}
@@ -1294,8 +1298,6 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
static int follow_dotdot_rcu(struct nameidata *nd)
{
struct inode *inode = nd->inode;
- if (!nd->root.mnt)
- set_root_rcu(nd);
while (1) {
if (path_equal(&nd->path, &nd->root))
@@ -1415,9 +1417,6 @@ static void follow_mount(struct path *path)
static int follow_dotdot(struct nameidata *nd)
{
- if (!nd->root.mnt)
- set_root(nd);
-
while(1) {
struct dentry *old = nd->path.dentry;
@@ -1655,6 +1654,8 @@ static inline int may_lookup(struct nameidata *nd)
static inline int handle_dots(struct nameidata *nd, int type)
{
if (type == LAST_DOTDOT) {
+ if (!nd->root.mnt)
+ set_root(nd);
if (nd->flags & LOOKUP_RCU) {
return follow_dotdot_rcu(nd);
} else
@@ -1691,8 +1692,8 @@ static int pick_link(struct nameidata *nd, struct path *link,
last = nd->stack + nd->depth++;
last->link = *link;
- last->cookie = NULL;
- last->inode = inode;
+ clear_delayed_call(&last->done);
+ nd->link_inode = inode;
last->seq = seq;
return 1;
}
@@ -1996,7 +1997,6 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
nd->last_type = LAST_ROOT; /* if there are only slashes... */
nd->flags = flags | LOOKUP_JUMPED | LOOKUP_PARENT;
nd->depth = 0;
- nd->total_link_count = 0;
if (flags & LOOKUP_ROOT) {
struct dentry *root = nd->root.dentry;
struct inode *inode = root->d_inode;
@@ -2021,18 +2021,19 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
}
nd->root.mnt = NULL;
+ nd->path.mnt = NULL;
+ nd->path.dentry = NULL;
nd->m_seq = read_seqbegin(&mount_lock);
if (*s == '/') {
- if (flags & LOOKUP_RCU) {
+ if (flags & LOOKUP_RCU)
rcu_read_lock();
- set_root_rcu(nd);
- nd->seq = nd->root_seq;
- } else {
- set_root(nd);
- path_get(&nd->root);
- }
- nd->path = nd->root;
+ set_root(nd);
+ if (likely(!nd_jump_root(nd)))
+ return s;
+ nd->root.mnt = NULL;
+ rcu_read_unlock();
+ return ERR_PTR(-ECHILD);
} else if (nd->dfd == AT_FDCWD) {
if (flags & LOOKUP_RCU) {
struct fs_struct *fs = current->fs;
@@ -2043,11 +2044,14 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
do {
seq = read_seqcount_begin(&fs->seq);
nd->path = fs->pwd;
+ nd->inode = nd->path.dentry->d_inode;
nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
} while (read_seqcount_retry(&fs->seq, seq));
} else {
get_fs_pwd(current->fs, &nd->path);
+ nd->inode = nd->path.dentry->d_inode;
}
+ return s;
} else {
/* Caller must check execute permissions on the starting path component */
struct fd f = fdget_raw(nd->dfd);
@@ -2077,16 +2081,6 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
fdput(f);
return s;
}
-
- nd->inode = nd->path.dentry->d_inode;
- if (!(flags & LOOKUP_RCU))
- return s;
- if (likely(!read_seqcount_retry(&nd->path.dentry->d_seq, nd->seq)))
- return s;
- if (!(nd->flags & LOOKUP_ROOT))
- nd->root.mnt = NULL;
- rcu_read_unlock();
- return ERR_PTR(-ECHILD);
}
static const char *trailing_symlink(struct nameidata *nd)
@@ -2279,6 +2273,8 @@ EXPORT_SYMBOL(vfs_path_lookup);
*
* Note that this routine is purely a helper for filesystem usage and should
* not be called by generic code.
+ *
+ * The caller must hold base->i_mutex.
*/
struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
{
@@ -2322,6 +2318,75 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
}
EXPORT_SYMBOL(lookup_one_len);
+/**
+ * lookup_one_len_unlocked - filesystem helper to lookup single pathname component
+ * @name: pathname component to lookup
+ * @base: base directory to lookup from
+ * @len: maximum length @len should be interpreted to
+ *
+ * Note that this routine is purely a helper for filesystem usage and should
+ * not be called by generic code.
+ *
+ * Unlike lookup_one_len, it should be called without the parent
+ * i_mutex held, and will take the i_mutex itself if necessary.
+ */
+struct dentry *lookup_one_len_unlocked(const char *name,
+ struct dentry *base, int len)
+{
+ struct qstr this;
+ unsigned int c;
+ int err;
+ struct dentry *ret;
+
+ this.name = name;
+ this.len = len;
+ this.hash = full_name_hash(name, len);
+ if (!len)
+ return ERR_PTR(-EACCES);
+
+ if (unlikely(name[0] == '.')) {
+ if (len < 2 || (len == 2 && name[1] == '.'))
+ return ERR_PTR(-EACCES);
+ }
+
+ while (len--) {
+ c = *(const unsigned char *)name++;
+ if (c == '/' || c == '\0')
+ return ERR_PTR(-EACCES);
+ }
+ /*
+ * See if the low-level filesystem might want
+ * to use its own hash..
+ */
+ if (base->d_flags & DCACHE_OP_HASH) {
+ int err = base->d_op->d_hash(base, &this);
+ if (err < 0)
+ return ERR_PTR(err);
+ }
+
+ err = inode_permission(base->d_inode, MAY_EXEC);
+ if (err)
+ return ERR_PTR(err);
+
+ /*
+ * __d_lookup() is used to try to get a quick answer and avoid the
+ * mutex. A false-negative does no harm.
+ */
+ ret = __d_lookup(base, &this);
+ if (ret && unlikely(ret->d_flags & DCACHE_OP_REVALIDATE)) {
+ dput(ret);
+ ret = NULL;
+ }
+ if (ret)
+ return ret;
+
+ mutex_lock(&base->d_inode->i_mutex);
+ ret = __lookup_hash(&this, base, 0);
+ mutex_unlock(&base->d_inode->i_mutex);
+ return ret;
+}
+EXPORT_SYMBOL(lookup_one_len_unlocked);
+
int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
struct path *path, int *empty)
{
@@ -2670,10 +2735,6 @@ static int may_open(struct path *path, int acc_mode, int flag)
struct inode *inode = dentry->d_inode;
int error;
- /* O_PATH? */
- if (!acc_mode)
- return 0;
-
if (!inode)
return -ENOENT;
@@ -2695,7 +2756,7 @@ static int may_open(struct path *path, int acc_mode, int flag)
break;
}
- error = inode_permission(inode, acc_mode);
+ error = inode_permission(inode, MAY_OPEN | acc_mode);
if (error)
return error;
@@ -2887,7 +2948,7 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
if (*opened & FILE_CREATED) {
WARN_ON(!(open_flag & O_CREAT));
fsnotify_create(dir, dentry);
- acc_mode = MAY_OPEN;
+ acc_mode = 0;
}
error = may_open(&file->f_path, acc_mode, open_flag);
if (error)
@@ -3100,7 +3161,7 @@ retry_lookup:
/* Don't check for write permission, don't truncate */
open_flag &= ~O_TRUNC;
will_truncate = false;
- acc_mode = MAY_OPEN;
+ acc_mode = 0;
path_to_nameidata(&path, nd);
goto finish_open_created;
}
@@ -3184,10 +3245,11 @@ finish_open:
got_write = true;
}
finish_open_created:
- error = may_open(&nd->path, acc_mode, open_flag);
- if (error)
- goto out;
-
+ if (likely(!(open_flag & O_PATH))) {
+ error = may_open(&nd->path, acc_mode, open_flag);
+ if (error)
+ goto out;
+ }
BUG_ON(*opened & FILE_OPENED); /* once it's opened, it's opened */
error = vfs_open(&nd->path, file, current_cred());
if (!error) {
@@ -3274,7 +3336,7 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags,
goto out2;
audit_inode(nd->name, child, 0);
/* Don't check for other permissions, the inode was just created */
- error = may_open(&path, MAY_OPEN, op->open_flag);
+ error = may_open(&path, 0, op->open_flag);
if (error)
goto out2;
file->f_path.mnt = path.mnt;
@@ -4496,72 +4558,73 @@ EXPORT_SYMBOL(readlink_copy);
/*
* A helper for ->readlink(). This should be used *ONLY* for symlinks that
- * have ->follow_link() touching nd only in nd_set_link(). Using (or not
- * using) it for any given inode is up to filesystem.
+ * have ->get_link() not calling nd_jump_link(). Using (or not using) it
+ * for any given inode is up to filesystem.
*/
int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
{
- void *cookie;
+ DEFINE_DELAYED_CALL(done);
struct inode *inode = d_inode(dentry);
const char *link = inode->i_link;
int res;
if (!link) {
- link = inode->i_op->follow_link(dentry, &cookie);
+ link = inode->i_op->get_link(dentry, inode, &done);
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);
+ do_delayed_call(&done);
return res;
}
EXPORT_SYMBOL(generic_readlink);
/* get the link contents into pagecache */
-static char *page_getlink(struct dentry * dentry, struct page **ppage)
+const char *page_get_link(struct dentry *dentry, struct inode *inode,
+ struct delayed_call *callback)
{
char *kaddr;
struct page *page;
- struct address_space *mapping = dentry->d_inode->i_mapping;
- page = read_mapping_page(mapping, 0, NULL);
- if (IS_ERR(page))
- return (char*)page;
- *ppage = page;
- kaddr = kmap(page);
- nd_terminate_link(kaddr, dentry->d_inode->i_size, PAGE_SIZE - 1);
+ struct address_space *mapping = inode->i_mapping;
+
+ if (!dentry) {
+ page = find_get_page(mapping, 0);
+ if (!page)
+ return ERR_PTR(-ECHILD);
+ if (!PageUptodate(page)) {
+ put_page(page);
+ return ERR_PTR(-ECHILD);
+ }
+ } else {
+ page = read_mapping_page(mapping, 0, NULL);
+ if (IS_ERR(page))
+ return (char*)page;
+ }
+ set_delayed_call(callback, page_put_link, page);
+ BUG_ON(mapping_gfp_mask(mapping) & __GFP_HIGHMEM);
+ kaddr = page_address(page);
+ nd_terminate_link(kaddr, inode->i_size, PAGE_SIZE - 1);
return kaddr;
}
-int page_readlink(struct dentry *dentry, char __user *buffer, int buflen)
-{
- struct page *page = NULL;
- int res = readlink_copy(buffer, buflen, page_getlink(dentry, &page));
- if (page) {
- kunmap(page);
- page_cache_release(page);
- }
- return res;
-}
-EXPORT_SYMBOL(page_readlink);
+EXPORT_SYMBOL(page_get_link);
-const char *page_follow_link_light(struct dentry *dentry, void **cookie)
+void page_put_link(void *arg)
{
- struct page *page = NULL;
- char *res = page_getlink(dentry, &page);
- if (!IS_ERR(res))
- *cookie = page;
- return res;
+ put_page(arg);
}
-EXPORT_SYMBOL(page_follow_link_light);
+EXPORT_SYMBOL(page_put_link);
-void page_put_link(struct inode *unused, void *cookie)
+int page_readlink(struct dentry *dentry, char __user *buffer, int buflen)
{
- struct page *page = cookie;
- kunmap(page);
- page_cache_release(page);
+ DEFINE_DELAYED_CALL(done);
+ int res = readlink_copy(buffer, buflen,
+ page_get_link(dentry, d_inode(dentry),
+ &done));
+ do_delayed_call(&done);
+ return res;
}
-EXPORT_SYMBOL(page_put_link);
+EXPORT_SYMBOL(page_readlink);
/*
* The nofs argument instructs pagecache_write_begin to pass AOP_FLAG_NOFS
@@ -4572,7 +4635,6 @@ int __page_symlink(struct inode *inode, const char *symname, int len, int nofs)
struct page *page;
void *fsdata;
int err;
- char *kaddr;
unsigned int flags = AOP_FLAG_UNINTERRUPTIBLE;
if (nofs)
flags |= AOP_FLAG_NOFS;
@@ -4583,9 +4645,7 @@ retry:
if (err)
goto fail;
- kaddr = kmap_atomic(page);
- memcpy(kaddr, symname, len-1);
- kunmap_atomic(kaddr);
+ memcpy(page_address(page), symname, len-1);
err = pagecache_write_end(NULL, mapping, 0, len-1, len-1,
page, fsdata);
@@ -4610,7 +4670,6 @@ EXPORT_SYMBOL(page_symlink);
const struct inode_operations page_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = page_follow_link_light,
- .put_link = page_put_link,
+ .get_link = page_get_link,
};
EXPORT_SYMBOL(page_symlink_inode_operations);
diff --git a/fs/namespace.c b/fs/namespace.c
index 0570729c87fd..a830e1463704 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1584,6 +1584,14 @@ static inline bool may_mount(void)
return ns_capable(current->nsproxy->mnt_ns->user_ns, CAP_SYS_ADMIN);
}
+static inline bool may_mandlock(void)
+{
+#ifndef CONFIG_MANDATORY_FILE_LOCKING
+ return false;
+#endif
+ return capable(CAP_SYS_ADMIN);
+}
+
/*
* Now umount can handle mount points as well as block devices.
* This is important for filesystems which use unnamed block devices.
@@ -2601,18 +2609,18 @@ static long exact_copy_from_user(void *to, const void __user * from,
return n;
}
-int copy_mount_options(const void __user * data, unsigned long *where)
+void *copy_mount_options(const void __user * data)
{
int i;
- unsigned long page;
unsigned long size;
+ char *copy;
- *where = 0;
if (!data)
- return 0;
+ return NULL;
- if (!(page = __get_free_page(GFP_KERNEL)))
- return -ENOMEM;
+ copy = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!copy)
+ return ERR_PTR(-ENOMEM);
/* We only care that *some* data at the address the user
* gave us is valid. Just in case, we'll zero
@@ -2623,15 +2631,14 @@ int copy_mount_options(const void __user * data, unsigned long *where)
if (size > PAGE_SIZE)
size = PAGE_SIZE;
- i = size - exact_copy_from_user((void *)page, data, size);
+ i = size - exact_copy_from_user(copy, data, size);
if (!i) {
- free_page(page);
- return -EFAULT;
+ kfree(copy);
+ return ERR_PTR(-EFAULT);
}
if (i != PAGE_SIZE)
- memset((char *)page + i, 0, PAGE_SIZE - i);
- *where = page;
- return 0;
+ memset(copy + i, 0, PAGE_SIZE - i);
+ return copy;
}
char *copy_mount_string(const void __user *data)
@@ -2677,6 +2684,8 @@ long do_mount(const char *dev_name, const char __user *dir_name,
type_page, flags, data_page);
if (!retval && !may_mount())
retval = -EPERM;
+ if (!retval && (flags & MS_MANDLOCK) && !may_mandlock())
+ retval = -EPERM;
if (retval)
goto dput_out;
@@ -2896,7 +2905,7 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
int ret;
char *kernel_type;
char *kernel_dev;
- unsigned long data_page;
+ void *options;
kernel_type = copy_mount_string(type);
ret = PTR_ERR(kernel_type);
@@ -2908,14 +2917,14 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
if (IS_ERR(kernel_dev))
goto out_dev;
- ret = copy_mount_options(data, &data_page);
- if (ret < 0)
+ options = copy_mount_options(data);
+ ret = PTR_ERR(options);
+ if (IS_ERR(options))
goto out_data;
- ret = do_mount(kernel_dev, dir_name, kernel_type, flags,
- (void *) data_page);
+ ret = do_mount(kernel_dev, dir_name, kernel_type, flags, options);
- free_page(data_page);
+ kfree(options);
out_data:
kfree(kernel_dev);
out_dev:
@@ -2939,9 +2948,9 @@ bool is_path_reachable(struct mount *mnt, struct dentry *dentry,
return &mnt->mnt == root->mnt && is_subdir(dentry, root->dentry);
}
-int path_is_under(struct path *path1, struct path *path2)
+bool path_is_under(struct path *path1, struct path *path2)
{
- int res;
+ bool res;
read_seqlock_excl(&mount_lock);
res = is_path_reachable(real_mount(path1->mnt), path1->dentry, path2);
read_sequnlock_excl(&mount_lock);
diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c
index 9605a2f63549..1af15fcbe57b 100644
--- a/fs/ncpfs/inode.c
+++ b/fs/ncpfs/inode.c
@@ -82,7 +82,7 @@ static int init_inodecache(void)
ncp_inode_cachep = kmem_cache_create("ncp_inode_cache",
sizeof(struct ncp_inode_info),
0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
init_once);
if (ncp_inode_cachep == NULL)
return -ENOMEM;
@@ -244,8 +244,7 @@ static void ncp_set_attr(struct inode *inode, struct ncp_entry_info *nwinfo)
#if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
static const struct inode_operations ncp_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = page_follow_link_light,
- .put_link = page_put_link,
+ .get_link = page_get_link,
.setattr = ncp_notify_change,
};
#endif
@@ -283,6 +282,7 @@ ncp_iget(struct super_block *sb, struct ncp_entry_info *info)
#if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS)
} else if (S_ISLNK(inode->i_mode)) {
inode->i_op = &ncp_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_data.a_ops = &ncp_symlink_aops;
#endif
} else {
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index 807eb6ef4f91..f0939d097406 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -83,8 +83,11 @@ __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy,
res = htonl(NFS4ERR_BADHANDLE);
inode = nfs_delegation_find_inode(cps->clp, &args->fh);
- if (inode == NULL)
+ if (inode == NULL) {
+ trace_nfs4_cb_recall(cps->clp, &args->fh, NULL,
+ &args->stateid, -ntohl(res));
goto out;
+ }
/* Set up a helper thread to actually return the delegation */
switch (nfs_async_inode_return_delegation(inode, &args->stateid)) {
case 0:
@@ -96,7 +99,8 @@ __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy,
default:
res = htonl(NFS4ERR_RESOURCE);
}
- trace_nfs4_recall_delegation(inode, -ntohl(res));
+ trace_nfs4_cb_recall(cps->clp, &args->fh, inode,
+ &args->stateid, -ntohl(res));
iput(inode);
out:
dprintk("%s: exit with status = %d\n", __func__, ntohl(res));
@@ -160,6 +164,22 @@ static struct pnfs_layout_hdr * get_layout_by_fh(struct nfs_client *clp,
return lo;
}
+/*
+ * Enforce RFC5661 section 12.5.5.2.1. (Layout Recall and Return Sequencing)
+ */
+static bool pnfs_check_stateid_sequence(struct pnfs_layout_hdr *lo,
+ const nfs4_stateid *new)
+{
+ u32 oldseq, newseq;
+
+ oldseq = be32_to_cpu(lo->plh_stateid.seqid);
+ newseq = be32_to_cpu(new->seqid);
+
+ if (newseq > oldseq + 1)
+ return false;
+ return true;
+}
+
static u32 initiate_file_draining(struct nfs_client *clp,
struct cb_layoutrecallargs *args)
{
@@ -169,34 +189,52 @@ static u32 initiate_file_draining(struct nfs_client *clp,
LIST_HEAD(free_me_list);
lo = get_layout_by_fh(clp, &args->cbl_fh, &args->cbl_stateid);
- if (!lo)
+ if (!lo) {
+ trace_nfs4_cb_layoutrecall_file(clp, &args->cbl_fh, NULL,
+ &args->cbl_stateid, -rv);
goto out;
+ }
ino = lo->plh_inode;
spin_lock(&ino->i_lock);
+ if (!pnfs_check_stateid_sequence(lo, &args->cbl_stateid)) {
+ rv = NFS4ERR_DELAY;
+ goto unlock;
+ }
pnfs_set_layout_stateid(lo, &args->cbl_stateid, true);
spin_unlock(&ino->i_lock);
pnfs_layoutcommit_inode(ino, false);
spin_lock(&ino->i_lock);
- if (test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags) ||
- pnfs_mark_matching_lsegs_invalid(lo, &free_me_list,
- &args->cbl_range)) {
+ /*
+ * Enforce RFC5661 Section 12.5.5.2.1.5 (Bulk Recall and Return)
+ */
+ if (test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags)) {
rv = NFS4ERR_DELAY;
goto unlock;
}
+ if (pnfs_mark_matching_lsegs_return(lo, &free_me_list,
+ &args->cbl_range)) {
+ rv = NFS4_OK;
+ goto unlock;
+ }
+
if (NFS_SERVER(ino)->pnfs_curr_ld->return_range) {
NFS_SERVER(ino)->pnfs_curr_ld->return_range(lo,
&args->cbl_range);
}
+ pnfs_mark_layout_returned_if_empty(lo);
unlock:
spin_unlock(&ino->i_lock);
pnfs_free_lseg_list(&free_me_list);
+ /* Free all lsegs that are attached to commit buckets */
+ nfs_commit_inode(ino, 0);
pnfs_put_layout_hdr(lo);
- trace_nfs4_cb_layoutrecall_inode(clp, &args->cbl_fh, ino, -rv);
+ trace_nfs4_cb_layoutrecall_file(clp, &args->cbl_fh, ino,
+ &args->cbl_stateid, -rv);
iput(ino);
out:
return rv;
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index ce5a21861074..c82a21228a34 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -1894,15 +1894,14 @@ int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
attr.ia_mode = S_IFLNK | S_IRWXUGO;
attr.ia_valid = ATTR_MODE;
- page = alloc_page(GFP_HIGHUSER);
+ page = alloc_page(GFP_USER);
if (!page)
return -ENOMEM;
- kaddr = kmap_atomic(page);
+ kaddr = page_address(page);
memcpy(kaddr, symname, pathlen);
if (pathlen < PAGE_SIZE)
memset(kaddr + pathlen, 0, PAGE_SIZE - pathlen);
- kunmap_atomic(kaddr);
trace_nfs_symlink_enter(dir, dentry);
error = NFS_PROTO(dir)->symlink(dir, dentry, page, pathlen, &attr);
@@ -2432,6 +2431,20 @@ int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags)
}
EXPORT_SYMBOL_GPL(nfs_may_open);
+static int nfs_execute_ok(struct inode *inode, int mask)
+{
+ struct nfs_server *server = NFS_SERVER(inode);
+ int ret;
+
+ if (mask & MAY_NOT_BLOCK)
+ ret = nfs_revalidate_inode_rcu(server, inode);
+ else
+ ret = nfs_revalidate_inode(server, inode);
+ if (ret == 0 && !execute_ok(inode))
+ ret = -EACCES;
+ return ret;
+}
+
int nfs_permission(struct inode *inode, int mask)
{
struct rpc_cred *cred;
@@ -2449,6 +2462,9 @@ int nfs_permission(struct inode *inode, int mask)
case S_IFLNK:
goto out;
case S_IFREG:
+ if ((mask & MAY_OPEN) &&
+ nfs_server_capable(inode, NFS_CAP_ATOMIC_OPEN))
+ return 0;
break;
case S_IFDIR:
/*
@@ -2481,8 +2497,8 @@ force_lookup:
res = PTR_ERR(cred);
}
out:
- if (!res && (mask & MAY_EXEC) && !execute_ok(inode))
- res = -EACCES;
+ if (!res && (mask & MAY_EXEC))
+ res = nfs_execute_ok(inode, mask);
dfprintk(VFS, "NFS: permission(%s/%lu), mask=0x%x, res=%d\n",
inode->i_sb->s_id, inode->i_ino, mask, res);
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index 4b1d08f56aba..7ab7ec9f4eed 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -117,12 +117,6 @@ static inline int put_dreq(struct nfs_direct_req *dreq)
return atomic_dec_and_test(&dreq->io_count);
}
-void nfs_direct_set_resched_writes(struct nfs_direct_req *dreq)
-{
- dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
-}
-EXPORT_SYMBOL_GPL(nfs_direct_set_resched_writes);
-
static void
nfs_direct_good_bytes(struct nfs_direct_req *dreq, struct nfs_pgio_header *hdr)
{
@@ -670,6 +664,10 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
req = nfs_list_entry(reqs.next);
nfs_direct_setup_mirroring(dreq, &desc, req);
+ if (desc.pg_error < 0) {
+ list_splice_init(&reqs, &failed);
+ goto out_failed;
+ }
list_for_each_entry_safe(req, tmp, &reqs, wb_list) {
if (!nfs_pageio_add_request(&desc, req)) {
@@ -677,13 +675,17 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
nfs_list_add_request(req, &failed);
spin_lock(cinfo.lock);
dreq->flags = 0;
- dreq->error = -EIO;
+ if (desc.pg_error < 0)
+ dreq->error = desc.pg_error;
+ else
+ dreq->error = -EIO;
spin_unlock(cinfo.lock);
}
nfs_release_request(req);
}
nfs_pageio_complete(&desc);
+out_failed:
while (!list_empty(&failed)) {
req = nfs_list_entry(failed.next);
nfs_list_remove_request(req);
@@ -727,14 +729,20 @@ static void nfs_direct_commit_complete(struct nfs_commit_data *data)
nfs_direct_write_complete(dreq, data->inode);
}
-static void nfs_direct_error_cleanup(struct nfs_inode *nfsi)
+static void nfs_direct_resched_write(struct nfs_commit_info *cinfo,
+ struct nfs_page *req)
{
- /* There is no lock to clear */
+ struct nfs_direct_req *dreq = cinfo->dreq;
+
+ spin_lock(&dreq->lock);
+ dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
+ spin_unlock(&dreq->lock);
+ nfs_mark_request_commit(req, NULL, cinfo, 0);
}
static const struct nfs_commit_completion_ops nfs_direct_commit_completion_ops = {
.completion = nfs_direct_commit_complete,
- .error_cleanup = nfs_direct_error_cleanup,
+ .resched_write = nfs_direct_resched_write,
};
static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq)
@@ -839,10 +847,25 @@ static void nfs_write_sync_pgio_error(struct list_head *head)
}
}
+static void nfs_direct_write_reschedule_io(struct nfs_pgio_header *hdr)
+{
+ struct nfs_direct_req *dreq = hdr->dreq;
+
+ spin_lock(&dreq->lock);
+ if (dreq->error == 0) {
+ dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
+ /* fake unstable write to let common nfs resend pages */
+ hdr->verf.committed = NFS_UNSTABLE;
+ hdr->good_bytes = hdr->args.count;
+ }
+ spin_unlock(&dreq->lock);
+}
+
static const struct nfs_pgio_completion_ops nfs_direct_write_completion_ops = {
.error_cleanup = nfs_write_sync_pgio_error,
.init_hdr = nfs_direct_pgio_init,
.completion = nfs_direct_write_completion,
+ .reschedule_io = nfs_direct_write_reschedule_io,
};
@@ -900,6 +923,11 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
}
nfs_direct_setup_mirroring(dreq, &desc, req);
+ if (desc.pg_error < 0) {
+ nfs_free_request(req);
+ result = desc.pg_error;
+ break;
+ }
nfs_lock_request(req);
req->wb_index = pos >> PAGE_SHIFT;
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 93e236429c5d..4ef8f5addcad 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -514,7 +514,7 @@ static void nfs_check_dirty_writeback(struct page *page,
* so it will not block due to pages that will shortly be freeable.
*/
nfsi = NFS_I(mapping->host);
- if (test_bit(NFS_INO_COMMIT, &nfsi->flags)) {
+ if (atomic_read(&nfsi->commit_info.rpcs_out)) {
*writeback = true;
return;
}
@@ -545,7 +545,7 @@ static int nfs_launder_page(struct page *page)
inode->i_ino, (long long)page_offset(page));
nfs_fscache_wait_on_page_write(nfsi, page);
- return nfs_wb_page(inode, page);
+ return nfs_wb_launder_page(inode, page);
}
static int nfs_swap_activate(struct swap_info_struct *sis, struct file *file,
@@ -756,7 +756,7 @@ do_unlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
l_ctx = nfs_get_lock_context(nfs_file_open_context(filp));
if (!IS_ERR(l_ctx)) {
- status = nfs_iocounter_wait(&l_ctx->io_count);
+ status = nfs_iocounter_wait(l_ctx);
nfs_put_lock_context(l_ctx);
if (status < 0)
return status;
diff --git a/fs/nfs/filelayout/filelayout.c b/fs/nfs/filelayout/filelayout.c
index 02ec07973bc4..bb1f4e7a3270 100644
--- a/fs/nfs/filelayout/filelayout.c
+++ b/fs/nfs/filelayout/filelayout.c
@@ -202,6 +202,7 @@ static int filelayout_async_handle_error(struct rpc_task *task,
task->tk_status);
nfs4_mark_deviceid_unavailable(devid);
pnfs_error_mark_layout_for_return(inode, lseg);
+ pnfs_set_lo_fail(lseg);
rpc_wake_up(&tbl->slot_tbl_waitq);
/* fall through */
default:
@@ -883,13 +884,19 @@ static void
filelayout_pg_init_read(struct nfs_pageio_descriptor *pgio,
struct nfs_page *req)
{
- if (!pgio->pg_lseg)
+ if (!pgio->pg_lseg) {
pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
req->wb_context,
0,
NFS4_MAX_UINT64,
IOMODE_READ,
GFP_KERNEL);
+ if (IS_ERR(pgio->pg_lseg)) {
+ pgio->pg_error = PTR_ERR(pgio->pg_lseg);
+ pgio->pg_lseg = NULL;
+ return;
+ }
+ }
/* If no lseg, fall back to read through mds */
if (pgio->pg_lseg == NULL)
nfs_pageio_reset_read_mds(pgio);
@@ -902,13 +909,20 @@ filelayout_pg_init_write(struct nfs_pageio_descriptor *pgio,
struct nfs_commit_info cinfo;
int status;
- if (!pgio->pg_lseg)
+ if (!pgio->pg_lseg) {
pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
req->wb_context,
0,
NFS4_MAX_UINT64,
IOMODE_RW,
GFP_NOFS);
+ if (IS_ERR(pgio->pg_lseg)) {
+ pgio->pg_error = PTR_ERR(pgio->pg_lseg);
+ pgio->pg_lseg = NULL;
+ return;
+ }
+ }
+
/* If no lseg, fall back to write through mds */
if (pgio->pg_lseg == NULL)
goto out_mds;
diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c
index 03516c80855a..6594e9f903a0 100644
--- a/fs/nfs/flexfilelayout/flexfilelayout.c
+++ b/fs/nfs/flexfilelayout/flexfilelayout.c
@@ -145,7 +145,7 @@ static bool ff_mirror_match_fh(const struct nfs4_ff_layout_mirror *m1,
return false;
for (i = 0; i < m1->fh_versions_cnt; i++) {
bool found_fh = false;
- for (j = 0; j < m2->fh_versions_cnt; i++) {
+ for (j = 0; j < m2->fh_versions_cnt; j++) {
if (nfs_compare_fh(&m1->fh_versions[i],
&m2->fh_versions[j]) == 0) {
found_fh = true;
@@ -505,9 +505,17 @@ ff_layout_alloc_lseg(struct pnfs_layout_hdr *lh,
}
p = xdr_inline_decode(&stream, 4);
- if (p)
- fls->flags = be32_to_cpup(p);
+ if (!p)
+ goto out_sort_mirrors;
+ fls->flags = be32_to_cpup(p);
+
+ p = xdr_inline_decode(&stream, 4);
+ if (!p)
+ goto out_sort_mirrors;
+ for (i=0; i < fls->mirror_array_cnt; i++)
+ fls->mirror_array[i]->report_interval = be32_to_cpup(p);
+out_sort_mirrors:
ff_layout_sort_mirrors(fls);
rc = ff_layout_check_layout(lgr);
if (rc)
@@ -603,7 +611,9 @@ nfs4_ff_layoutstat_start_io(struct nfs4_ff_layout_mirror *mirror,
mirror->start_time = now;
if (ktime_equal(mirror->last_report_time, notime))
mirror->last_report_time = now;
- if (layoutstats_timer != 0)
+ if (mirror->report_interval != 0)
+ report_interval = (s64)mirror->report_interval * 1000LL;
+ else if (layoutstats_timer != 0)
report_interval = (s64)layoutstats_timer * 1000LL;
if (ktime_to_ms(ktime_sub(now, mirror->last_report_time)) >=
report_interval) {
@@ -785,13 +795,19 @@ ff_layout_pg_init_read(struct nfs_pageio_descriptor *pgio,
int ds_idx;
/* Use full layout for now */
- if (!pgio->pg_lseg)
+ if (!pgio->pg_lseg) {
pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
req->wb_context,
0,
NFS4_MAX_UINT64,
IOMODE_READ,
GFP_KERNEL);
+ if (IS_ERR(pgio->pg_lseg)) {
+ pgio->pg_error = PTR_ERR(pgio->pg_lseg);
+ pgio->pg_lseg = NULL;
+ return;
+ }
+ }
/* If no lseg, fall back to read through mds */
if (pgio->pg_lseg == NULL)
goto out_mds;
@@ -825,13 +841,19 @@ ff_layout_pg_init_write(struct nfs_pageio_descriptor *pgio,
int i;
int status;
- if (!pgio->pg_lseg)
+ if (!pgio->pg_lseg) {
pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
req->wb_context,
0,
NFS4_MAX_UINT64,
IOMODE_RW,
GFP_NOFS);
+ if (IS_ERR(pgio->pg_lseg)) {
+ pgio->pg_error = PTR_ERR(pgio->pg_lseg);
+ pgio->pg_lseg = NULL;
+ return;
+ }
+ }
/* If no lseg, fall back to write through mds */
if (pgio->pg_lseg == NULL)
goto out_mds;
@@ -867,18 +889,25 @@ static unsigned int
ff_layout_pg_get_mirror_count_write(struct nfs_pageio_descriptor *pgio,
struct nfs_page *req)
{
- if (!pgio->pg_lseg)
+ if (!pgio->pg_lseg) {
pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
req->wb_context,
0,
NFS4_MAX_UINT64,
IOMODE_RW,
GFP_NOFS);
+ if (IS_ERR(pgio->pg_lseg)) {
+ pgio->pg_error = PTR_ERR(pgio->pg_lseg);
+ pgio->pg_lseg = NULL;
+ goto out;
+ }
+ }
if (pgio->pg_lseg)
return FF_LAYOUT_MIRROR_COUNT(pgio->pg_lseg);
/* no lseg means that pnfs is not in use, so no mirroring here */
nfs_pageio_reset_write_mds(pgio);
+out:
return 1;
}
@@ -912,18 +941,7 @@ static void ff_layout_reset_write(struct nfs_pgio_header *hdr, bool retry_pnfs)
hdr->args.count,
(unsigned long long)hdr->args.offset);
- if (!hdr->dreq) {
- struct nfs_open_context *ctx;
-
- ctx = nfs_list_entry(hdr->pages.next)->wb_context;
- set_bit(NFS_CONTEXT_RESEND_WRITES, &ctx->flags);
- hdr->completion_ops->error_cleanup(&hdr->pages);
- } else {
- nfs_direct_set_resched_writes(hdr->dreq);
- /* fake unstable write to let common nfs resend pages */
- hdr->verf.committed = NFS_UNSTABLE;
- hdr->good_bytes = hdr->args.count;
- }
+ hdr->completion_ops->reschedule_io(hdr);
return;
}
@@ -1101,7 +1119,7 @@ static int ff_layout_async_handle_error_v3(struct rpc_task *task,
return -NFS4ERR_RESET_TO_PNFS;
out_retry:
task->tk_status = 0;
- rpc_restart_call(task);
+ rpc_restart_call_prepare(task);
rpc_delay(task, NFS_JUKEBOX_RETRY_TIME);
return -EAGAIN;
}
@@ -1159,6 +1177,14 @@ static void ff_layout_io_track_ds_error(struct pnfs_layout_segment *lseg,
}
}
+ switch (status) {
+ case NFS4ERR_DELAY:
+ case NFS4ERR_GRACE:
+ return;
+ default:
+ break;
+ }
+
mirror = FF_LAYOUT_COMP(lseg, idx);
err = ff_layout_track_ds_error(FF_LAYOUT_FROM_HDR(lseg->pls_layout),
mirror, offset, length, status, opnum,
@@ -1242,14 +1268,31 @@ ff_layout_reset_to_mds(struct pnfs_layout_segment *lseg, int idx)
return ff_layout_test_devid_unavailable(node);
}
-static int ff_layout_read_prepare_common(struct rpc_task *task,
- struct nfs_pgio_header *hdr)
+static void ff_layout_read_record_layoutstats_start(struct rpc_task *task,
+ struct nfs_pgio_header *hdr)
{
+ if (test_and_set_bit(NFS_IOHDR_STAT, &hdr->flags))
+ return;
nfs4_ff_layout_stat_io_start_read(hdr->inode,
FF_LAYOUT_COMP(hdr->lseg, hdr->pgio_mirror_idx),
hdr->args.count,
task->tk_start);
+}
+
+static void ff_layout_read_record_layoutstats_done(struct rpc_task *task,
+ struct nfs_pgio_header *hdr)
+{
+ if (!test_and_clear_bit(NFS_IOHDR_STAT, &hdr->flags))
+ return;
+ nfs4_ff_layout_stat_io_end_read(task,
+ FF_LAYOUT_COMP(hdr->lseg, hdr->pgio_mirror_idx),
+ hdr->args.count,
+ hdr->res.count);
+}
+static int ff_layout_read_prepare_common(struct rpc_task *task,
+ struct nfs_pgio_header *hdr)
+{
if (unlikely(test_bit(NFS_CONTEXT_BAD, &hdr->args.context->flags))) {
rpc_exit(task, -EIO);
return -EIO;
@@ -1265,6 +1308,7 @@ static int ff_layout_read_prepare_common(struct rpc_task *task,
}
hdr->pgio_done_cb = ff_layout_read_done_cb;
+ ff_layout_read_record_layoutstats_start(task, hdr);
return 0;
}
@@ -1323,10 +1367,6 @@ static void ff_layout_read_call_done(struct rpc_task *task, void *data)
dprintk("--> %s task->tk_status %d\n", __func__, task->tk_status);
- nfs4_ff_layout_stat_io_end_read(task,
- FF_LAYOUT_COMP(hdr->lseg, hdr->pgio_mirror_idx),
- hdr->args.count, hdr->res.count);
-
if (test_bit(NFS_IOHDR_REDO, &hdr->flags) &&
task->tk_status == 0) {
nfs4_sequence_done(task, &hdr->res.seq_res);
@@ -1341,10 +1381,20 @@ static void ff_layout_read_count_stats(struct rpc_task *task, void *data)
{
struct nfs_pgio_header *hdr = data;
+ ff_layout_read_record_layoutstats_done(task, hdr);
rpc_count_iostats_metrics(task,
&NFS_CLIENT(hdr->inode)->cl_metrics[NFSPROC4_CLNT_READ]);
}
+static void ff_layout_read_release(void *data)
+{
+ struct nfs_pgio_header *hdr = data;
+
+ ff_layout_read_record_layoutstats_done(&hdr->task, hdr);
+ pnfs_generic_rw_release(data);
+}
+
+
static int ff_layout_write_done_cb(struct rpc_task *task,
struct nfs_pgio_header *hdr)
{
@@ -1362,15 +1412,12 @@ static int ff_layout_write_done_cb(struct rpc_task *task,
switch (err) {
case -NFS4ERR_RESET_TO_PNFS:
- pnfs_set_retry_layoutget(hdr->lseg->pls_layout);
ff_layout_reset_write(hdr, true);
return task->tk_status;
case -NFS4ERR_RESET_TO_MDS:
- pnfs_clear_retry_layoutget(hdr->lseg->pls_layout);
ff_layout_reset_write(hdr, false);
return task->tk_status;
case -EAGAIN:
- rpc_restart_call_prepare(task);
return -EAGAIN;
}
@@ -1402,11 +1449,9 @@ static int ff_layout_commit_done_cb(struct rpc_task *task,
switch (err) {
case -NFS4ERR_RESET_TO_PNFS:
- pnfs_set_retry_layoutget(data->lseg->pls_layout);
pnfs_generic_prepare_to_resend_writes(data);
return -EAGAIN;
case -NFS4ERR_RESET_TO_MDS:
- pnfs_clear_retry_layoutget(data->lseg->pls_layout);
pnfs_generic_prepare_to_resend_writes(data);
return -EAGAIN;
case -EAGAIN:
@@ -1421,14 +1466,31 @@ static int ff_layout_commit_done_cb(struct rpc_task *task,
return 0;
}
-static int ff_layout_write_prepare_common(struct rpc_task *task,
- struct nfs_pgio_header *hdr)
+static void ff_layout_write_record_layoutstats_start(struct rpc_task *task,
+ struct nfs_pgio_header *hdr)
{
+ if (test_and_set_bit(NFS_IOHDR_STAT, &hdr->flags))
+ return;
nfs4_ff_layout_stat_io_start_write(hdr->inode,
FF_LAYOUT_COMP(hdr->lseg, hdr->pgio_mirror_idx),
hdr->args.count,
task->tk_start);
+}
+
+static void ff_layout_write_record_layoutstats_done(struct rpc_task *task,
+ struct nfs_pgio_header *hdr)
+{
+ if (!test_and_clear_bit(NFS_IOHDR_STAT, &hdr->flags))
+ return;
+ nfs4_ff_layout_stat_io_end_write(task,
+ FF_LAYOUT_COMP(hdr->lseg, hdr->pgio_mirror_idx),
+ hdr->args.count, hdr->res.count,
+ hdr->res.verf->committed);
+}
+static int ff_layout_write_prepare_common(struct rpc_task *task,
+ struct nfs_pgio_header *hdr)
+{
if (unlikely(test_bit(NFS_CONTEXT_BAD, &hdr->args.context->flags))) {
rpc_exit(task, -EIO);
return -EIO;
@@ -1445,6 +1507,7 @@ static int ff_layout_write_prepare_common(struct rpc_task *task,
return -EAGAIN;
}
+ ff_layout_write_record_layoutstats_start(task, hdr);
return 0;
}
@@ -1480,11 +1543,6 @@ static void ff_layout_write_call_done(struct rpc_task *task, void *data)
{
struct nfs_pgio_header *hdr = data;
- nfs4_ff_layout_stat_io_end_write(task,
- FF_LAYOUT_COMP(hdr->lseg, hdr->pgio_mirror_idx),
- hdr->args.count, hdr->res.count,
- hdr->res.verf->committed);
-
if (test_bit(NFS_IOHDR_REDO, &hdr->flags) &&
task->tk_status == 0) {
nfs4_sequence_done(task, &hdr->res.seq_res);
@@ -1499,18 +1557,53 @@ static void ff_layout_write_count_stats(struct rpc_task *task, void *data)
{
struct nfs_pgio_header *hdr = data;
+ ff_layout_write_record_layoutstats_done(task, hdr);
rpc_count_iostats_metrics(task,
&NFS_CLIENT(hdr->inode)->cl_metrics[NFSPROC4_CLNT_WRITE]);
}
-static void ff_layout_commit_prepare_common(struct rpc_task *task,
+static void ff_layout_write_release(void *data)
+{
+ struct nfs_pgio_header *hdr = data;
+
+ ff_layout_write_record_layoutstats_done(&hdr->task, hdr);
+ pnfs_generic_rw_release(data);
+}
+
+static void ff_layout_commit_record_layoutstats_start(struct rpc_task *task,
struct nfs_commit_data *cdata)
{
+ if (test_and_set_bit(NFS_IOHDR_STAT, &cdata->flags))
+ return;
nfs4_ff_layout_stat_io_start_write(cdata->inode,
FF_LAYOUT_COMP(cdata->lseg, cdata->ds_commit_index),
0, task->tk_start);
}
+static void ff_layout_commit_record_layoutstats_done(struct rpc_task *task,
+ struct nfs_commit_data *cdata)
+{
+ struct nfs_page *req;
+ __u64 count = 0;
+
+ if (!test_and_clear_bit(NFS_IOHDR_STAT, &cdata->flags))
+ return;
+
+ if (task->tk_status == 0) {
+ list_for_each_entry(req, &cdata->pages, wb_list)
+ count += req->wb_bytes;
+ }
+ nfs4_ff_layout_stat_io_end_write(task,
+ FF_LAYOUT_COMP(cdata->lseg, cdata->ds_commit_index),
+ count, count, NFS_FILE_SYNC);
+}
+
+static void ff_layout_commit_prepare_common(struct rpc_task *task,
+ struct nfs_commit_data *cdata)
+{
+ ff_layout_commit_record_layoutstats_start(task, cdata);
+}
+
static void ff_layout_commit_prepare_v3(struct rpc_task *task, void *data)
{
ff_layout_commit_prepare_common(task, data);
@@ -1531,19 +1624,6 @@ static void ff_layout_commit_prepare_v4(struct rpc_task *task, void *data)
static void ff_layout_commit_done(struct rpc_task *task, void *data)
{
- struct nfs_commit_data *cdata = data;
- struct nfs_page *req;
- __u64 count = 0;
-
- if (task->tk_status == 0) {
- list_for_each_entry(req, &cdata->pages, wb_list)
- count += req->wb_bytes;
- }
-
- nfs4_ff_layout_stat_io_end_write(task,
- FF_LAYOUT_COMP(cdata->lseg, cdata->ds_commit_index),
- count, count, NFS_FILE_SYNC);
-
pnfs_generic_write_commit_done(task, data);
}
@@ -1551,50 +1631,59 @@ static void ff_layout_commit_count_stats(struct rpc_task *task, void *data)
{
struct nfs_commit_data *cdata = data;
+ ff_layout_commit_record_layoutstats_done(task, cdata);
rpc_count_iostats_metrics(task,
&NFS_CLIENT(cdata->inode)->cl_metrics[NFSPROC4_CLNT_COMMIT]);
}
+static void ff_layout_commit_release(void *data)
+{
+ struct nfs_commit_data *cdata = data;
+
+ ff_layout_commit_record_layoutstats_done(&cdata->task, cdata);
+ pnfs_generic_commit_release(data);
+}
+
static const struct rpc_call_ops ff_layout_read_call_ops_v3 = {
.rpc_call_prepare = ff_layout_read_prepare_v3,
.rpc_call_done = ff_layout_read_call_done,
.rpc_count_stats = ff_layout_read_count_stats,
- .rpc_release = pnfs_generic_rw_release,
+ .rpc_release = ff_layout_read_release,
};
static const struct rpc_call_ops ff_layout_read_call_ops_v4 = {
.rpc_call_prepare = ff_layout_read_prepare_v4,
.rpc_call_done = ff_layout_read_call_done,
.rpc_count_stats = ff_layout_read_count_stats,
- .rpc_release = pnfs_generic_rw_release,
+ .rpc_release = ff_layout_read_release,
};
static const struct rpc_call_ops ff_layout_write_call_ops_v3 = {
.rpc_call_prepare = ff_layout_write_prepare_v3,
.rpc_call_done = ff_layout_write_call_done,
.rpc_count_stats = ff_layout_write_count_stats,
- .rpc_release = pnfs_generic_rw_release,
+ .rpc_release = ff_layout_write_release,
};
static const struct rpc_call_ops ff_layout_write_call_ops_v4 = {
.rpc_call_prepare = ff_layout_write_prepare_v4,
.rpc_call_done = ff_layout_write_call_done,
.rpc_count_stats = ff_layout_write_count_stats,
- .rpc_release = pnfs_generic_rw_release,
+ .rpc_release = ff_layout_write_release,
};
static const struct rpc_call_ops ff_layout_commit_call_ops_v3 = {
.rpc_call_prepare = ff_layout_commit_prepare_v3,
.rpc_call_done = ff_layout_commit_done,
.rpc_count_stats = ff_layout_commit_count_stats,
- .rpc_release = pnfs_generic_commit_release,
+ .rpc_release = ff_layout_commit_release,
};
static const struct rpc_call_ops ff_layout_commit_call_ops_v4 = {
.rpc_call_prepare = ff_layout_commit_prepare_v4,
.rpc_call_done = ff_layout_commit_done,
.rpc_count_stats = ff_layout_commit_count_stats,
- .rpc_release = pnfs_generic_commit_release,
+ .rpc_release = ff_layout_commit_release,
};
static enum pnfs_try_status
diff --git a/fs/nfs/flexfilelayout/flexfilelayout.h b/fs/nfs/flexfilelayout/flexfilelayout.h
index 2bb08bc6aaf0..dd353bb7dc0a 100644
--- a/fs/nfs/flexfilelayout/flexfilelayout.h
+++ b/fs/nfs/flexfilelayout/flexfilelayout.h
@@ -85,6 +85,7 @@ struct nfs4_ff_layout_mirror {
struct nfs4_ff_layoutstat write_stat;
ktime_t start_time;
ktime_t last_report_time;
+ u32 report_interval;
};
struct nfs4_ff_layout_segment {
diff --git a/fs/nfs/flexfilelayout/flexfilelayoutdev.c b/fs/nfs/flexfilelayout/flexfilelayoutdev.c
index e125e55de86d..bd0327541366 100644
--- a/fs/nfs/flexfilelayout/flexfilelayoutdev.c
+++ b/fs/nfs/flexfilelayout/flexfilelayoutdev.c
@@ -429,22 +429,14 @@ nfs4_ff_layout_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx,
mirror, lseg->pls_range.offset,
lseg->pls_range.length, NFS4ERR_NXIO,
OP_ILLEGAL, GFP_NOIO);
- if (fail_return) {
- pnfs_error_mark_layout_for_return(ino, lseg);
- if (ff_layout_has_available_ds(lseg))
- pnfs_set_retry_layoutget(lseg->pls_layout);
- else
- pnfs_clear_retry_layoutget(lseg->pls_layout);
-
- } else {
+ if (!fail_return) {
if (ff_layout_has_available_ds(lseg))
set_bit(NFS_LAYOUT_RETURN_BEFORE_CLOSE,
&lseg->pls_layout->plh_flags);
- else {
+ else
pnfs_error_mark_layout_for_return(ino, lseg);
- pnfs_clear_retry_layoutget(lseg->pls_layout);
- }
- }
+ } else
+ pnfs_error_mark_layout_for_return(ino, lseg);
}
out_update_creds:
if (ff_layout_update_mirror_cred(mirror, ds))
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 326d9e10d833..8e24d886d2c5 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -71,19 +71,25 @@ nfs_fattr_to_ino_t(struct nfs_fattr *fattr)
return nfs_fileid_to_ino_t(fattr->fileid);
}
-/**
- * nfs_wait_bit_killable - helper for functions that are sleeping on bit locks
- * @word: long word containing the bit lock
- */
-int nfs_wait_bit_killable(struct wait_bit_key *key)
+static int nfs_wait_killable(int mode)
{
- if (fatal_signal_pending(current))
- return -ERESTARTSYS;
freezable_schedule_unsafe();
+ if (signal_pending_state(mode, current))
+ return -ERESTARTSYS;
return 0;
}
+
+int nfs_wait_bit_killable(struct wait_bit_key *key, int mode)
+{
+ return nfs_wait_killable(mode);
+}
EXPORT_SYMBOL_GPL(nfs_wait_bit_killable);
+int nfs_wait_atomic_killable(atomic_t *p)
+{
+ return nfs_wait_killable(TASK_KILLABLE);
+}
+
/**
* nfs_compat_user_ino64 - returns the user-visible inode number
* @fileid: 64-bit fileid
@@ -408,9 +414,10 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr, st
inode->i_fop = NULL;
inode->i_flags |= S_AUTOMOUNT;
}
- } else if (S_ISLNK(inode->i_mode))
+ } else if (S_ISLNK(inode->i_mode)) {
inode->i_op = &nfs_symlink_inode_operations;
- else
+ inode_nohighmem(inode);
+ } else
init_special_inode(inode, inode->i_mode, fattr->rdev);
memset(&inode->i_atime, 0, sizeof(inode->i_atime));
@@ -618,7 +625,10 @@ void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr,
nfs_inc_stats(inode, NFSIOS_SETATTRTRUNC);
nfs_vmtruncate(inode, attr->ia_size);
}
- nfs_update_inode(inode, fattr);
+ if (fattr->valid)
+ nfs_update_inode(inode, fattr);
+ else
+ NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATTR;
spin_unlock(&inode->i_lock);
}
EXPORT_SYMBOL_GPL(nfs_setattr_update_inode);
@@ -696,7 +706,7 @@ static void nfs_init_lock_context(struct nfs_lock_context *l_ctx)
l_ctx->lockowner.l_owner = current->files;
l_ctx->lockowner.l_pid = current->tgid;
INIT_LIST_HEAD(&l_ctx->list);
- nfs_iocounter_init(&l_ctx->io_count);
+ atomic_set(&l_ctx->io_count, 0);
}
static struct nfs_lock_context *__nfs_find_lock_context(struct nfs_open_context *ctx)
@@ -909,6 +919,12 @@ void nfs_file_clear_open_context(struct file *filp)
if (ctx) {
struct inode *inode = d_inode(ctx->dentry);
+ /*
+ * We fatal error on write before. Try to writeback
+ * every page again.
+ */
+ if (ctx->error < 0)
+ invalidate_inode_pages2(inode->i_mapping);
filp->private_data = NULL;
spin_lock(&inode->i_lock);
list_move_tail(&ctx->list, &NFS_I(inode)->open_files);
@@ -1083,6 +1099,27 @@ static bool nfs_mapping_need_revalidate_inode(struct inode *inode)
|| NFS_STALE(inode);
}
+int nfs_revalidate_mapping_rcu(struct inode *inode)
+{
+ struct nfs_inode *nfsi = NFS_I(inode);
+ unsigned long *bitlock = &nfsi->flags;
+ int ret = 0;
+
+ if (IS_SWAPFILE(inode))
+ goto out;
+ if (nfs_mapping_need_revalidate_inode(inode)) {
+ ret = -ECHILD;
+ goto out;
+ }
+ spin_lock(&inode->i_lock);
+ if (test_bit(NFS_INO_INVALIDATING, bitlock) ||
+ (nfsi->cache_validity & NFS_INO_INVALID_DATA))
+ ret = -ECHILD;
+ spin_unlock(&inode->i_lock);
+out:
+ return ret;
+}
+
/**
* __nfs_revalidate_mapping - Revalidate the pagecache
* @inode - pointer to host inode
@@ -1638,6 +1675,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
unsigned long invalid = 0;
unsigned long now = jiffies;
unsigned long save_cache_validity;
+ bool cache_revalidated = true;
dfprintk(VFS, "NFS: %s(%s/%lu fh_crc=0x%08x ct=%d info=0x%x)\n",
__func__, inode->i_sb->s_id, inode->i_ino,
@@ -1699,22 +1737,28 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
nfs_force_lookup_revalidate(inode);
inode->i_version = fattr->change_attr;
}
- } else
+ } else {
nfsi->cache_validity |= save_cache_validity;
+ cache_revalidated = false;
+ }
if (fattr->valid & NFS_ATTR_FATTR_MTIME) {
memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
- } else if (server->caps & NFS_CAP_MTIME)
+ } else if (server->caps & NFS_CAP_MTIME) {
nfsi->cache_validity |= save_cache_validity &
(NFS_INO_INVALID_ATTR
| NFS_INO_REVAL_FORCED);
+ cache_revalidated = false;
+ }
if (fattr->valid & NFS_ATTR_FATTR_CTIME) {
memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
- } else if (server->caps & NFS_CAP_CTIME)
+ } else if (server->caps & NFS_CAP_CTIME) {
nfsi->cache_validity |= save_cache_validity &
(NFS_INO_INVALID_ATTR
| NFS_INO_REVAL_FORCED);
+ cache_revalidated = false;
+ }
/* Check if our cached file size is stale */
if (fattr->valid & NFS_ATTR_FATTR_SIZE) {
@@ -1734,19 +1778,23 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
(long long)cur_isize,
(long long)new_isize);
}
- } else
+ } else {
nfsi->cache_validity |= save_cache_validity &
(NFS_INO_INVALID_ATTR
| NFS_INO_REVAL_PAGECACHE
| NFS_INO_REVAL_FORCED);
+ cache_revalidated = false;
+ }
if (fattr->valid & NFS_ATTR_FATTR_ATIME)
memcpy(&inode->i_atime, &fattr->atime, sizeof(inode->i_atime));
- else if (server->caps & NFS_CAP_ATIME)
+ else if (server->caps & NFS_CAP_ATIME) {
nfsi->cache_validity |= save_cache_validity &
(NFS_INO_INVALID_ATIME
| NFS_INO_REVAL_FORCED);
+ cache_revalidated = false;
+ }
if (fattr->valid & NFS_ATTR_FATTR_MODE) {
if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO)) {
@@ -1755,36 +1803,42 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
inode->i_mode = newmode;
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
}
- } else if (server->caps & NFS_CAP_MODE)
+ } else if (server->caps & NFS_CAP_MODE) {
nfsi->cache_validity |= save_cache_validity &
(NFS_INO_INVALID_ATTR
| NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACL
| NFS_INO_REVAL_FORCED);
+ cache_revalidated = false;
+ }
if (fattr->valid & NFS_ATTR_FATTR_OWNER) {
if (!uid_eq(inode->i_uid, fattr->uid)) {
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
inode->i_uid = fattr->uid;
}
- } else if (server->caps & NFS_CAP_OWNER)
+ } else if (server->caps & NFS_CAP_OWNER) {
nfsi->cache_validity |= save_cache_validity &
(NFS_INO_INVALID_ATTR
| NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACL
| NFS_INO_REVAL_FORCED);
+ cache_revalidated = false;
+ }
if (fattr->valid & NFS_ATTR_FATTR_GROUP) {
if (!gid_eq(inode->i_gid, fattr->gid)) {
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
inode->i_gid = fattr->gid;
}
- } else if (server->caps & NFS_CAP_OWNER_GROUP)
+ } else if (server->caps & NFS_CAP_OWNER_GROUP) {
nfsi->cache_validity |= save_cache_validity &
(NFS_INO_INVALID_ATTR
| NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACL
| NFS_INO_REVAL_FORCED);
+ cache_revalidated = false;
+ }
if (fattr->valid & NFS_ATTR_FATTR_NLINK) {
if (inode->i_nlink != fattr->nlink) {
@@ -1793,19 +1847,22 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
invalid |= NFS_INO_INVALID_DATA;
set_nlink(inode, fattr->nlink);
}
- } else if (server->caps & NFS_CAP_NLINK)
+ } else if (server->caps & NFS_CAP_NLINK) {
nfsi->cache_validity |= save_cache_validity &
(NFS_INO_INVALID_ATTR
| NFS_INO_REVAL_FORCED);
+ cache_revalidated = false;
+ }
if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) {
/*
* report the blocks in 512byte units
*/
inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used);
- }
- if (fattr->valid & NFS_ATTR_FATTR_BLOCKS_USED)
+ } else if (fattr->valid & NFS_ATTR_FATTR_BLOCKS_USED)
inode->i_blocks = fattr->du.nfs2.blocks;
+ else
+ cache_revalidated = false;
/* Update attrtimeo value if we're out of the unstable period */
if (invalid & NFS_INO_INVALID_ATTR) {
@@ -1815,16 +1872,24 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
/* Set barrier to be more recent than all outstanding updates */
nfsi->attr_gencount = nfs_inc_attr_generation_counter();
} else {
- if (!time_in_range_open(now, nfsi->attrtimeo_timestamp, nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) {
- if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode))
- nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode);
+ if (cache_revalidated) {
+ if (!time_in_range_open(now, nfsi->attrtimeo_timestamp,
+ nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) {
+ nfsi->attrtimeo <<= 1;
+ if (nfsi->attrtimeo > NFS_MAXATTRTIMEO(inode))
+ nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode);
+ }
nfsi->attrtimeo_timestamp = now;
}
/* Set the barrier to be more recent than this fattr */
if ((long)fattr->gencount - (long)nfsi->attr_gencount > 0)
nfsi->attr_gencount = fattr->gencount;
}
- invalid &= ~NFS_INO_INVALID_ATTR;
+
+ /* Don't declare attrcache up to date if there were no attrs! */
+ if (cache_revalidated)
+ invalid &= ~NFS_INO_INVALID_ATTR;
+
/* Don't invalidate the data if we were to blame */
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
|| S_ISLNK(inode->i_mode)))
@@ -1904,7 +1969,7 @@ static int __init nfs_init_inodecache(void)
nfs_inode_cachep = kmem_cache_create("nfs_inode_cache",
sizeof(struct nfs_inode),
0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
init_once);
if (nfs_inode_cachep == NULL)
return -ENOMEM;
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 56cfde26fb9c..4e8cc942336c 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -238,7 +238,7 @@ extern void nfs_pgheader_init(struct nfs_pageio_descriptor *desc,
struct nfs_pgio_header *hdr,
void (*release)(struct nfs_pgio_header *hdr));
void nfs_set_pgio_error(struct nfs_pgio_header *hdr, int error, loff_t pos);
-int nfs_iocounter_wait(struct nfs_io_counter *c);
+int nfs_iocounter_wait(struct nfs_lock_context *l_ctx);
extern const struct nfs_pageio_ops nfs_pgio_rw_ops;
struct nfs_pgio_header *nfs_pgio_header_alloc(const struct nfs_rw_ops *);
@@ -252,18 +252,18 @@ void nfs_free_request(struct nfs_page *req);
struct nfs_pgio_mirror *
nfs_pgio_current_mirror(struct nfs_pageio_descriptor *desc);
-static inline void nfs_iocounter_init(struct nfs_io_counter *c)
-{
- c->flags = 0;
- atomic_set(&c->io_count, 0);
-}
-
static inline bool nfs_pgio_has_mirroring(struct nfs_pageio_descriptor *desc)
{
WARN_ON_ONCE(desc->pg_mirror_count < 1);
return desc->pg_mirror_count > 1;
}
+static inline bool nfs_match_open_context(const struct nfs_open_context *ctx1,
+ const struct nfs_open_context *ctx2)
+{
+ return ctx1->cred == ctx2->cred && ctx1->state == ctx2->state;
+}
+
/* nfs2xdr.c */
extern struct rpc_procinfo nfs_procedures[];
extern int nfs2_decode_dirent(struct xdr_stream *,
@@ -379,7 +379,8 @@ extern int nfs_drop_inode(struct inode *);
extern void nfs_clear_inode(struct inode *);
extern void nfs_evict_inode(struct inode *);
void nfs_zap_acl_cache(struct inode *inode);
-extern int nfs_wait_bit_killable(struct wait_bit_key *key);
+extern int nfs_wait_bit_killable(struct wait_bit_key *key, int mode);
+extern int nfs_wait_atomic_killable(atomic_t *p);
/* super.c */
extern const struct super_operations nfs_sops;
@@ -519,7 +520,6 @@ static inline void nfs_inode_dio_wait(struct inode *inode)
inode_dio_wait(inode);
}
extern ssize_t nfs_dreq_bytes_left(struct nfs_direct_req *dreq);
-extern void nfs_direct_set_resched_writes(struct nfs_direct_req *dreq);
/* nfs4proc.c */
extern void __nfs4_read_done_cb(struct nfs_pgio_header *);
@@ -696,9 +696,32 @@ static inline u32 nfs_fhandle_hash(const struct nfs_fh *fh)
{
return ~crc32_le(0xFFFFFFFF, &fh->data[0], fh->size);
}
+static inline u32 nfs_stateid_hash(const nfs4_stateid *stateid)
+{
+ return ~crc32_le(0xFFFFFFFF, &stateid->other[0],
+ NFS4_STATEID_OTHER_SIZE);
+}
#else
static inline u32 nfs_fhandle_hash(const struct nfs_fh *fh)
{
return 0;
}
+static inline u32 nfs_stateid_hash(nfs4_stateid *stateid)
+{
+ return 0;
+}
#endif
+
+static inline bool nfs_error_is_fatal(int err)
+{
+ switch (err) {
+ case -ERESTARTSYS:
+ case -EIO:
+ case -ENOSPC:
+ case -EROFS:
+ case -E2BIG:
+ return true;
+ default:
+ return false;
+ }
+}
diff --git a/fs/nfs/nfs3acl.c b/fs/nfs/nfs3acl.c
index 1ebe2fc7cda2..17c0fa1eccfa 100644
--- a/fs/nfs/nfs3acl.c
+++ b/fs/nfs/nfs3acl.c
@@ -284,12 +284,12 @@ nfs3_listxattr(struct dentry *dentry, char *data, size_t size)
int error;
error = nfs3_list_one_acl(inode, ACL_TYPE_ACCESS,
- POSIX_ACL_XATTR_ACCESS, data, size, &result);
+ XATTR_NAME_POSIX_ACL_ACCESS, data, size, &result);
if (error)
return error;
error = nfs3_list_one_acl(inode, ACL_TYPE_DEFAULT,
- POSIX_ACL_XATTR_DEFAULT, data, size, &result);
+ XATTR_NAME_POSIX_ACL_DEFAULT, data, size, &result);
if (error)
return error;
return result;
diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c
index 3e92a3cde15d..6e8174930a48 100644
--- a/fs/nfs/nfs42proc.c
+++ b/fs/nfs/nfs42proc.c
@@ -14,7 +14,7 @@
#include "pnfs.h"
#include "internal.h"
-#define NFSDBG_FACILITY NFSDBG_PNFS
+#define NFSDBG_FACILITY NFSDBG_PROC
static int nfs42_set_rw_stateid(nfs4_stateid *dst, struct file *file,
fmode_t fmode)
@@ -204,6 +204,8 @@ static void
nfs42_layoutstat_done(struct rpc_task *task, void *calldata)
{
struct nfs42_layoutstat_data *data = calldata;
+ struct inode *inode = data->inode;
+ struct pnfs_layout_hdr *lo;
if (!nfs4_sequence_done(task, &data->res.seq_res))
return;
@@ -211,12 +213,35 @@ nfs42_layoutstat_done(struct rpc_task *task, void *calldata)
switch (task->tk_status) {
case 0:
break;
+ case -NFS4ERR_EXPIRED:
+ case -NFS4ERR_STALE_STATEID:
+ case -NFS4ERR_OLD_STATEID:
+ case -NFS4ERR_BAD_STATEID:
+ spin_lock(&inode->i_lock);
+ lo = NFS_I(inode)->layout;
+ if (lo && nfs4_stateid_match(&data->args.stateid,
+ &lo->plh_stateid)) {
+ LIST_HEAD(head);
+
+ /*
+ * Mark the bad layout state as invalid, then retry
+ * with the current stateid.
+ */
+ set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags);
+ pnfs_mark_matching_lsegs_invalid(lo, &head, NULL);
+ spin_unlock(&inode->i_lock);
+ pnfs_free_lseg_list(&head);
+ } else
+ spin_unlock(&inode->i_lock);
+ break;
case -ENOTSUPP:
case -EOPNOTSUPP:
- NFS_SERVER(data->inode)->caps &= ~NFS_CAP_LAYOUTSTATS;
+ NFS_SERVER(inode)->caps &= ~NFS_CAP_LAYOUTSTATS;
default:
- dprintk("%s server returns %d\n", __func__, task->tk_status);
+ break;
}
+
+ dprintk("%s server returns %d\n", __func__, task->tk_status);
}
static void
@@ -284,6 +309,7 @@ static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f,
.dst_fh = NFS_FH(dst_inode),
.src_offset = src_offset,
.dst_offset = dst_offset,
+ .count = count,
.dst_bitmask = server->cache_consistency_bitmask,
};
struct nfs42_clone_res res = {
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
index 223bedda64ae..10410e8b5853 100644
--- a/fs/nfs/nfs4client.c
+++ b/fs/nfs/nfs4client.c
@@ -33,7 +33,7 @@ static int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion)
return ret;
idr_preload(GFP_KERNEL);
spin_lock(&nn->nfs_client_lock);
- ret = idr_alloc(&nn->cb_ident_idr, clp, 0, 0, GFP_NOWAIT);
+ ret = idr_alloc(&nn->cb_ident_idr, clp, 1, 0, GFP_NOWAIT);
if (ret >= 0)
clp->cl_cb_ident = ret;
spin_unlock(&nn->nfs_client_lock);
diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
index 4aa571956cd6..26f9a23e2b25 100644
--- a/fs/nfs/nfs4file.c
+++ b/fs/nfs/nfs4file.c
@@ -7,6 +7,7 @@
#include <linux/file.h>
#include <linux/falloc.h>
#include <linux/nfs_fs.h>
+#include <uapi/linux/btrfs.h> /* BTRFS_IOC_CLONE/BTRFS_IOC_CLONE_RANGE */
#include "delegation.h"
#include "internal.h"
#include "iostat.h"
@@ -194,63 +195,32 @@ static long nfs42_fallocate(struct file *filep, int mode, loff_t offset, loff_t
return nfs42_proc_allocate(filep, offset, len);
}
-static noinline long
-nfs42_ioctl_clone(struct file *dst_file, unsigned long srcfd,
- u64 src_off, u64 dst_off, u64 count)
+static int nfs42_clone_file_range(struct file *src_file, loff_t src_off,
+ struct file *dst_file, loff_t dst_off, u64 count)
{
struct inode *dst_inode = file_inode(dst_file);
struct nfs_server *server = NFS_SERVER(dst_inode);
- struct fd src_file;
- struct inode *src_inode;
+ struct inode *src_inode = file_inode(src_file);
unsigned int bs = server->clone_blksize;
+ bool same_inode = false;
int ret;
- /* dst file must be opened for writing */
- if (!(dst_file->f_mode & FMODE_WRITE))
- return -EINVAL;
-
- ret = mnt_want_write_file(dst_file);
- if (ret)
- return ret;
-
- src_file = fdget(srcfd);
- if (!src_file.file) {
- ret = -EBADF;
- goto out_drop_write;
- }
-
- src_inode = file_inode(src_file.file);
-
- /* src and dst must be different files */
- ret = -EINVAL;
- if (src_inode == dst_inode)
- goto out_fput;
-
- /* src file must be opened for reading */
- if (!(src_file.file->f_mode & FMODE_READ))
- goto out_fput;
-
- /* src and dst must be regular files */
- ret = -EISDIR;
- if (!S_ISREG(src_inode->i_mode) || !S_ISREG(dst_inode->i_mode))
- goto out_fput;
-
- ret = -EXDEV;
- if (src_file.file->f_path.mnt != dst_file->f_path.mnt ||
- src_inode->i_sb != dst_inode->i_sb)
- goto out_fput;
-
/* check alignment w.r.t. clone_blksize */
ret = -EINVAL;
if (bs) {
if (!IS_ALIGNED(src_off, bs) || !IS_ALIGNED(dst_off, bs))
- goto out_fput;
+ goto out;
if (!IS_ALIGNED(count, bs) && i_size_read(src_inode) != (src_off + count))
- goto out_fput;
+ goto out;
}
+ if (src_inode == dst_inode)
+ same_inode = true;
+
/* XXX: do we lock at all? what if server needs CB_RECALL_LAYOUT? */
- if (dst_inode < src_inode) {
+ if (same_inode) {
+ mutex_lock(&src_inode->i_mutex);
+ } else if (dst_inode < src_inode) {
mutex_lock_nested(&dst_inode->i_mutex, I_MUTEX_PARENT);
mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_CHILD);
} else {
@@ -267,7 +237,7 @@ nfs42_ioctl_clone(struct file *dst_file, unsigned long srcfd,
if (ret)
goto out_unlock;
- ret = nfs42_proc_clone(src_file.file, dst_file, src_off, dst_off, count);
+ ret = nfs42_proc_clone(src_file, dst_file, src_off, dst_off, count);
/* truncate inode page cache of the dst range so that future reads can fetch
* new data from server */
@@ -275,62 +245,21 @@ nfs42_ioctl_clone(struct file *dst_file, unsigned long srcfd,
truncate_inode_pages_range(&dst_inode->i_data, dst_off, dst_off + count - 1);
out_unlock:
- if (dst_inode < src_inode) {
+ if (same_inode) {
+ mutex_unlock(&src_inode->i_mutex);
+ } else if (dst_inode < src_inode) {
mutex_unlock(&src_inode->i_mutex);
mutex_unlock(&dst_inode->i_mutex);
} else {
mutex_unlock(&dst_inode->i_mutex);
mutex_unlock(&src_inode->i_mutex);
}
-out_fput:
- fdput(src_file);
-out_drop_write:
- mnt_drop_write_file(dst_file);
+out:
return ret;
}
-
-static long nfs42_ioctl_clone_range(struct file *dst_file, void __user *argp)
-{
- struct nfs_ioctl_clone_range_args args;
-
- if (copy_from_user(&args, argp, sizeof(args)))
- return -EFAULT;
-
- return nfs42_ioctl_clone(dst_file, args.src_fd, args.src_off, args.dst_off, args.count);
-}
-#else
-static long nfs42_ioctl_clone(struct file *dst_file, unsigned long srcfd,
- u64 src_off, u64 dst_off, u64 count)
-{
- return -ENOTTY;
-}
-
-static long nfs42_ioctl_clone_range(struct file *dst_file, void __user *argp)
-{
- return -ENOTTY;
-}
#endif /* CONFIG_NFS_V4_2 */
-long nfs4_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- void __user *argp = (void __user *)arg;
-
- switch (cmd) {
- case NFS_IOC_CLONE:
- return nfs42_ioctl_clone(file, arg, 0, 0, 0);
- case NFS_IOC_CLONE_RANGE:
- return nfs42_ioctl_clone_range(file, argp);
- }
-
- return -ENOTTY;
-}
-
const struct file_operations nfs4_file_operations = {
-#ifdef CONFIG_NFS_V4_2
- .llseek = nfs4_file_llseek,
-#else
- .llseek = nfs_file_llseek,
-#endif
.read_iter = nfs_file_read,
.write_iter = nfs_file_write,
.mmap = nfs_file_mmap,
@@ -342,14 +271,13 @@ const struct file_operations nfs4_file_operations = {
.flock = nfs_flock,
.splice_read = nfs_file_splice_read,
.splice_write = iter_file_splice_write,
-#ifdef CONFIG_NFS_V4_2
- .fallocate = nfs42_fallocate,
-#endif /* CONFIG_NFS_V4_2 */
.check_flags = nfs_check_flags,
.setlease = simple_nosetlease,
-#ifdef CONFIG_COMPAT
- .unlocked_ioctl = nfs4_ioctl,
+#ifdef CONFIG_NFS_V4_2
+ .llseek = nfs4_file_llseek,
+ .fallocate = nfs42_fallocate,
+ .clone_file_range = nfs42_clone_file_range,
#else
- .compat_ioctl = nfs4_ioctl,
-#endif /* CONFIG_COMPAT */
+ .llseek = nfs_file_llseek,
+#endif
};
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 765a03559363..4bfc33ad0563 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -208,6 +208,9 @@ static const u32 nfs4_pnfs_open_bitmap[3] = {
| FATTR4_WORD1_TIME_METADATA
| FATTR4_WORD1_TIME_MODIFY,
FATTR4_WORD2_MDSTHRESHOLD
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ | FATTR4_WORD2_SECURITY_LABEL
+#endif
};
static const u32 nfs4_open_noattr_bitmap[3] = {
@@ -1385,6 +1388,7 @@ static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_s
* Protect the call to nfs4_state_set_mode_locked and
* serialise the stateid update
*/
+ spin_lock(&state->owner->so_lock);
write_seqlock(&state->seqlock);
if (deleg_stateid != NULL) {
nfs4_stateid_copy(&state->stateid, deleg_stateid);
@@ -1393,7 +1397,6 @@ static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_s
if (open_stateid != NULL)
nfs_set_open_stateid_locked(state, open_stateid, fmode);
write_sequnlock(&state->seqlock);
- spin_lock(&state->owner->so_lock);
update_open_stateflags(state, fmode);
spin_unlock(&state->owner->so_lock);
}
@@ -1598,6 +1601,7 @@ _nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data)
if (!data->rpc_done) {
state = nfs4_try_open_cached(data);
+ trace_nfs4_cached_open(data->state);
goto out;
}
@@ -2015,6 +2019,7 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
}
return;
unlock_no_action:
+ trace_nfs4_cached_open(data->state);
rcu_read_unlock();
out_no_action:
task->tk_action = NULL;
@@ -2703,6 +2708,7 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
if (status == 0 && state != NULL)
renew_lease(server, timestamp);
+ trace_nfs4_setattr(inode, &arg.stateid, status);
return status;
}
@@ -2719,7 +2725,6 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
int err;
do {
err = _nfs4_do_setattr(inode, cred, fattr, sattr, state, ilabel, olabel);
- trace_nfs4_setattr(inode, err);
switch (err) {
case -NFS4ERR_OPENMODE:
if (!(sattr->ia_valid & ATTR_SIZE)) {
@@ -5048,7 +5053,6 @@ static void nfs4_init_boot_verifier(const struct nfs_client *clp,
static int
nfs4_init_nonuniform_client_string(struct nfs_client *clp)
{
- int result;
size_t len;
char *str;
@@ -5076,7 +5080,7 @@ nfs4_init_nonuniform_client_string(struct nfs_client *clp)
return -ENOMEM;
rcu_read_lock();
- result = scnprintf(str, len, "Linux NFSv4.0 %s/%s %s",
+ scnprintf(str, len, "Linux NFSv4.0 %s/%s %s",
clp->cl_ipaddr,
rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR),
rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_PROTO));
@@ -5089,7 +5093,6 @@ nfs4_init_nonuniform_client_string(struct nfs_client *clp)
static int
nfs4_init_uniquifier_client_string(struct nfs_client *clp)
{
- int result;
size_t len;
char *str;
@@ -5109,7 +5112,7 @@ nfs4_init_uniquifier_client_string(struct nfs_client *clp)
if (!str)
return -ENOMEM;
- result = scnprintf(str, len, "Linux NFSv%u.%u %s/%s",
+ scnprintf(str, len, "Linux NFSv%u.%u %s/%s",
clp->rpc_ops->version, clp->cl_minorversion,
nfs4_client_id_uniquifier,
clp->cl_rpcclient->cl_nodename);
@@ -5120,7 +5123,6 @@ nfs4_init_uniquifier_client_string(struct nfs_client *clp)
static int
nfs4_init_uniform_client_string(struct nfs_client *clp)
{
- int result;
size_t len;
char *str;
@@ -5145,7 +5147,7 @@ nfs4_init_uniform_client_string(struct nfs_client *clp)
if (!str)
return -ENOMEM;
- result = scnprintf(str, len, "Linux NFSv%u.%u %s",
+ scnprintf(str, len, "Linux NFSv%u.%u %s",
clp->rpc_ops->version, clp->cl_minorversion,
clp->cl_rpcclient->cl_nodename);
clp->cl_owner_id = str;
@@ -5384,6 +5386,11 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
if (data == NULL)
return -ENOMEM;
nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1);
+
+ nfs4_state_protect(server->nfs_client,
+ NFS_SP4_MACH_CRED_CLEANUP,
+ &task_setup_data.rpc_client, &msg);
+
data->args.fhandle = &data->fh;
data->args.stateid = &data->stateid;
data->args.bitmask = server->cache_consistency_bitmask;
@@ -5426,7 +5433,7 @@ int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4
int err;
do {
err = _nfs4_proc_delegreturn(inode, cred, stateid, issync);
- trace_nfs4_delegreturn(inode, err);
+ trace_nfs4_delegreturn(inode, stateid, err);
switch (err) {
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
@@ -5936,6 +5943,7 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f
data->cancelled = 1;
rpc_put_task(task);
dprintk("%s: done, ret = %d!\n", __func__, ret);
+ trace_nfs4_set_lock(fl, state, &data->res.stateid, cmd, ret);
return ret;
}
@@ -5952,7 +5960,6 @@ static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request
if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
return 0;
err = _nfs4_do_setlk(state, F_SETLK, request, NFS_LOCK_RECLAIM);
- trace_nfs4_lock_reclaim(request, state, F_SETLK, err);
if (err != -NFS4ERR_DELAY)
break;
nfs4_handle_exception(server, err, &exception);
@@ -5979,7 +5986,6 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request
if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
return 0;
err = _nfs4_do_setlk(state, F_SETLK, request, NFS_LOCK_EXPIRED);
- trace_nfs4_lock_expired(request, state, F_SETLK, err);
switch (err) {
default:
goto out;
@@ -6087,7 +6093,6 @@ static int nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *
do {
err = _nfs4_proc_setlk(state, cmd, request);
- trace_nfs4_set_lock(request, state, cmd, err);
if (err == -NFS4ERR_DENIED)
err = -EAGAIN;
err = nfs4_handle_exception(NFS_SERVER(state->inode),
@@ -6253,9 +6258,6 @@ static int nfs4_xattr_set_nfs4_acl(const struct xattr_handler *handler,
const void *buf, size_t buflen,
int flags)
{
- if (strcmp(key, "") != 0)
- return -EINVAL;
-
return nfs4_proc_set_acl(d_inode(dentry), buf, buflen);
}
@@ -6263,32 +6265,15 @@ static int nfs4_xattr_get_nfs4_acl(const struct xattr_handler *handler,
struct dentry *dentry, const char *key,
void *buf, size_t buflen)
{
- if (strcmp(key, "") != 0)
- return -EINVAL;
-
return nfs4_proc_get_acl(d_inode(dentry), buf, buflen);
}
-static size_t nfs4_xattr_list_nfs4_acl(const struct xattr_handler *handler,
- struct dentry *dentry, char *list,
- size_t list_len, const char *name,
- size_t name_len)
+static bool nfs4_xattr_list_nfs4_acl(struct dentry *dentry)
{
- size_t len = sizeof(XATTR_NAME_NFSV4_ACL);
-
- if (!nfs4_server_supports_acls(NFS_SERVER(d_inode(dentry))))
- return 0;
-
- if (list && len <= list_len)
- memcpy(list, XATTR_NAME_NFSV4_ACL, len);
- return len;
+ return nfs4_server_supports_acls(NFS_SERVER(d_inode(dentry)));
}
#ifdef CONFIG_NFS_V4_SECURITY_LABEL
-static inline int nfs4_server_supports_labels(struct nfs_server *server)
-{
- return server->caps & NFS_CAP_SECURITY_LABEL;
-}
static int nfs4_xattr_set_nfs4_label(const struct xattr_handler *handler,
struct dentry *dentry, const char *key,
@@ -6310,29 +6295,34 @@ static int nfs4_xattr_get_nfs4_label(const struct xattr_handler *handler,
return -EOPNOTSUPP;
}
-static size_t nfs4_xattr_list_nfs4_label(const struct xattr_handler *handler,
- struct dentry *dentry, char *list,
- size_t list_len, const char *name,
- size_t name_len)
+static ssize_t
+nfs4_listxattr_nfs4_label(struct inode *inode, char *list, size_t list_len)
{
- size_t len = 0;
+ int len = 0;
- if (nfs_server_capable(d_inode(dentry), NFS_CAP_SECURITY_LABEL)) {
- len = security_inode_listsecurity(d_inode(dentry), NULL, 0);
- if (list && len <= list_len)
- security_inode_listsecurity(d_inode(dentry), list, len);
+ if (nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL)) {
+ len = security_inode_listsecurity(inode, list, list_len);
+ if (list_len && len > list_len)
+ return -ERANGE;
}
return len;
}
static const struct xattr_handler nfs4_xattr_nfs4_label_handler = {
.prefix = XATTR_SECURITY_PREFIX,
- .list = nfs4_xattr_list_nfs4_label,
.get = nfs4_xattr_get_nfs4_label,
.set = nfs4_xattr_set_nfs4_label,
};
-#endif
+#else
+
+static ssize_t
+nfs4_listxattr_nfs4_label(struct inode *inode, char *list, size_t list_len)
+{
+ return 0;
+}
+
+#endif
/*
* nfs_fhget will use either the mounted_on_fileid or the fileid
@@ -6862,10 +6852,13 @@ static const struct nfs41_state_protection nfs4_sp4_mach_cred_request = {
},
.allow.u.words = {
[0] = 1 << (OP_CLOSE) |
+ 1 << (OP_OPEN_DOWNGRADE) |
1 << (OP_LOCKU) |
+ 1 << (OP_DELEGRETURN) |
1 << (OP_COMMIT),
[1] = 1 << (OP_SECINFO - 32) |
1 << (OP_SECINFO_NO_NAME - 32) |
+ 1 << (OP_LAYOUTRETURN - 32) |
1 << (OP_TEST_STATEID - 32) |
1 << (OP_FREE_STATEID - 32) |
1 << (OP_WRITE - 32)
@@ -6930,11 +6923,19 @@ static int nfs4_sp4_select_mode(struct nfs_client *clp,
}
if (test_bit(OP_CLOSE, sp->allow.u.longs) &&
+ test_bit(OP_OPEN_DOWNGRADE, sp->allow.u.longs) &&
+ test_bit(OP_DELEGRETURN, sp->allow.u.longs) &&
test_bit(OP_LOCKU, sp->allow.u.longs)) {
dfprintk(MOUNT, " cleanup mode enabled\n");
set_bit(NFS_SP4_MACH_CRED_CLEANUP, &clp->cl_sp4_flags);
}
+ if (test_bit(OP_LAYOUTRETURN, sp->allow.u.longs)) {
+ dfprintk(MOUNT, " pnfs cleanup mode enabled\n");
+ set_bit(NFS_SP4_MACH_CRED_PNFS_CLEANUP,
+ &clp->cl_sp4_flags);
+ }
+
if (test_bit(OP_SECINFO, sp->allow.u.longs) &&
test_bit(OP_SECINFO_NO_NAME, sp->allow.u.longs)) {
dfprintk(MOUNT, " secinfo mode enabled\n");
@@ -7763,6 +7764,7 @@ nfs4_layoutget_prepare(struct rpc_task *task, void *calldata)
struct nfs4_layoutget *lgp = calldata;
struct nfs_server *server = NFS_SERVER(lgp->args.inode);
struct nfs4_session *session = nfs4_get_session(server);
+ int ret;
dprintk("--> %s\n", __func__);
/* Note the is a race here, where a CB_LAYOUTRECALL can come in
@@ -7773,12 +7775,12 @@ nfs4_layoutget_prepare(struct rpc_task *task, void *calldata)
if (nfs41_setup_sequence(session, &lgp->args.seq_args,
&lgp->res.seq_res, task))
return;
- if (pnfs_choose_layoutget_stateid(&lgp->args.stateid,
+ ret = pnfs_choose_layoutget_stateid(&lgp->args.stateid,
NFS_I(lgp->args.inode)->layout,
&lgp->args.range,
- lgp->args.ctx->state)) {
- rpc_exit(task, NFS4_OK);
- }
+ lgp->args.ctx->state);
+ if (ret < 0)
+ rpc_exit(task, ret);
}
static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)
@@ -7798,6 +7800,15 @@ static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)
switch (task->tk_status) {
case 0:
goto out;
+
+ /*
+ * NFS4ERR_LAYOUTUNAVAILABLE means we are not supposed to use pnfs
+ * on the file. set tk_status to -ENODATA to tell upper layer to
+ * retry go inband.
+ */
+ case -NFS4ERR_LAYOUTUNAVAILABLE:
+ task->tk_status = -ENODATA;
+ goto out;
/*
* NFS4ERR_BADLAYOUT means the MDS cannot return a layout of
* length lgp->args.minlength != 0 (see RFC5661 section 18.43.3).
@@ -7866,7 +7877,7 @@ static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)
spin_unlock(&inode->i_lock);
goto out_restart;
}
- if (nfs4_async_handle_error(task, server, state, NULL) == -EAGAIN)
+ if (nfs4_async_handle_error(task, server, state, &lgp->timeout) == -EAGAIN)
goto out_restart;
out:
dprintk("<-- %s\n", __func__);
@@ -7994,6 +8005,7 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags)
trace_nfs4_layoutget(lgp->args.ctx,
&lgp->args.range,
&lgp->res.range,
+ &lgp->res.stateid,
status);
/* if layoutp->len is 0, nfs4_layoutget_prepare called rpc_exit */
if (status == 0 && lgp->res.layoutp->len)
@@ -8050,11 +8062,11 @@ static void nfs4_layoutreturn_release(void *calldata)
dprintk("--> %s\n", __func__);
spin_lock(&lo->plh_inode->i_lock);
+ pnfs_mark_matching_lsegs_invalid(lo, &freeme, &lrp->args.range);
+ pnfs_mark_layout_returned_if_empty(lo);
if (lrp->res.lrs_present)
pnfs_set_layout_stateid(lo, &lrp->res.stateid, true);
- pnfs_mark_matching_lsegs_invalid(lo, &freeme, &lrp->args.range);
pnfs_clear_layoutreturn_waitbit(lo);
- lo->plh_block_lgets--;
spin_unlock(&lo->plh_inode->i_lock);
pnfs_free_lseg_list(&freeme);
pnfs_put_layout_hdr(lrp->args.layout);
@@ -8086,6 +8098,10 @@ int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp, bool sync)
};
int status = 0;
+ nfs4_state_protect(NFS_SERVER(lrp->args.inode)->nfs_client,
+ NFS_SP4_MACH_CRED_PNFS_CLEANUP,
+ &task_setup_data.rpc_client, &msg);
+
dprintk("--> %s\n", __func__);
if (!sync) {
lrp->inode = nfs_igrab_and_active(lrp->args.inode);
@@ -8101,7 +8117,7 @@ int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp, bool sync)
return PTR_ERR(task);
if (sync)
status = task->tk_status;
- trace_nfs4_layoutreturn(lrp->args.inode, status);
+ trace_nfs4_layoutreturn(lrp->args.inode, &lrp->args.stateid, status);
dprintk("<-- %s status=%d\n", __func__, status);
rpc_put_task(task);
return status;
@@ -8249,7 +8265,7 @@ nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data, bool sync)
return PTR_ERR(task);
if (sync)
status = task->tk_status;
- trace_nfs4_layoutcommit(data->args.inode, status);
+ trace_nfs4_layoutcommit(data->args.inode, &data->args.stateid, status);
dprintk("%s: status %d\n", __func__, status);
rpc_put_task(task);
return status;
@@ -8749,6 +8765,24 @@ const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = {
#endif
};
+ssize_t nfs4_listxattr(struct dentry *dentry, char *list, size_t size)
+{
+ ssize_t error, error2;
+
+ error = generic_listxattr(dentry, list, size);
+ if (error < 0)
+ return error;
+ if (list) {
+ list += error;
+ size -= error;
+ }
+
+ error2 = nfs4_listxattr_nfs4_label(d_inode(dentry), list, size);
+ if (error2 < 0)
+ return error2;
+ return error + error2;
+}
+
static const struct inode_operations nfs4_dir_inode_operations = {
.create = nfs_create,
.lookup = nfs_lookup,
@@ -8765,7 +8799,7 @@ static const struct inode_operations nfs4_dir_inode_operations = {
.setattr = nfs_setattr,
.getxattr = generic_getxattr,
.setxattr = generic_setxattr,
- .listxattr = generic_listxattr,
+ .listxattr = nfs4_listxattr,
.removexattr = generic_removexattr,
};
@@ -8775,7 +8809,7 @@ static const struct inode_operations nfs4_file_inode_operations = {
.setattr = nfs_setattr,
.getxattr = generic_getxattr,
.setxattr = generic_setxattr,
- .listxattr = generic_listxattr,
+ .listxattr = nfs4_listxattr,
.removexattr = generic_removexattr,
};
@@ -8834,7 +8868,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
};
static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
- .prefix = XATTR_NAME_NFSV4_ACL,
+ .name = XATTR_NAME_NFSV4_ACL,
.list = nfs4_xattr_list_nfs4_acl,
.get = nfs4_xattr_get_nfs4_acl,
.set = nfs4_xattr_set_nfs4_acl,
diff --git a/fs/nfs/nfs4sysctl.c b/fs/nfs/nfs4sysctl.c
index 0fbd3ab1be22..8693d77c45ea 100644
--- a/fs/nfs/nfs4sysctl.c
+++ b/fs/nfs/nfs4sysctl.c
@@ -12,7 +12,7 @@
#include "nfs4idmap.h"
#include "callback.h"
-static const int nfs_set_port_min = 0;
+static const int nfs_set_port_min;
static const int nfs_set_port_max = 65535;
static struct ctl_table_header *nfs4_callback_sysctl_table;
diff --git a/fs/nfs/nfs4trace.c b/fs/nfs/nfs4trace.c
index d774335cc8bc..2850bce19244 100644
--- a/fs/nfs/nfs4trace.c
+++ b/fs/nfs/nfs4trace.c
@@ -6,6 +6,7 @@
#include "internal.h"
#include "nfs4session.h"
#include "callback.h"
+#include "pnfs.h"
#define CREATE_TRACE_POINTS
#include "nfs4trace.h"
diff --git a/fs/nfs/nfs4trace.h b/fs/nfs/nfs4trace.h
index 671cf68fe56b..2c8d05dae5b1 100644
--- a/fs/nfs/nfs4trace.h
+++ b/fs/nfs/nfs4trace.h
@@ -321,6 +321,7 @@ TRACE_EVENT(nfs4_sequence_done,
__entry->highest_slotid = res->sr_highest_slotid;
__entry->target_highest_slotid =
res->sr_target_highest_slotid;
+ __entry->status_flags = res->sr_status_flags;
__entry->error = res->sr_status;
),
TP_printk(
@@ -399,6 +400,10 @@ DECLARE_EVENT_CLASS(nfs4_open_event,
__field(u64, fileid)
__field(u64, dir)
__string(name, ctx->dentry->d_name.name)
+ __field(int, stateid_seq)
+ __field(u32, stateid_hash)
+ __field(int, openstateid_seq)
+ __field(u32, openstateid_hash)
),
TP_fast_assign(
@@ -409,8 +414,22 @@ DECLARE_EVENT_CLASS(nfs4_open_event,
__entry->flags = flags;
__entry->fmode = (__force unsigned int)ctx->mode;
__entry->dev = ctx->dentry->d_sb->s_dev;
- if (!IS_ERR_OR_NULL(state))
+ if (!IS_ERR_OR_NULL(state)) {
inode = state->inode;
+ __entry->stateid_seq =
+ be32_to_cpu(state->stateid.seqid);
+ __entry->stateid_hash =
+ nfs_stateid_hash(&state->stateid);
+ __entry->openstateid_seq =
+ be32_to_cpu(state->open_stateid.seqid);
+ __entry->openstateid_hash =
+ nfs_stateid_hash(&state->open_stateid);
+ } else {
+ __entry->stateid_seq = 0;
+ __entry->stateid_hash = 0;
+ __entry->openstateid_seq = 0;
+ __entry->openstateid_hash = 0;
+ }
if (inode != NULL) {
__entry->fileid = NFS_FILEID(inode);
__entry->fhandle = nfs_fhandle_hash(NFS_FH(inode));
@@ -425,7 +444,8 @@ DECLARE_EVENT_CLASS(nfs4_open_event,
TP_printk(
"error=%d (%s) flags=%d (%s) fmode=%s "
"fileid=%02x:%02x:%llu fhandle=0x%08x "
- "name=%02x:%02x:%llu/%s",
+ "name=%02x:%02x:%llu/%s stateid=%d:0x%08x "
+ "openstateid=%d:0x%08x",
__entry->error,
show_nfsv4_errors(__entry->error),
__entry->flags,
@@ -436,7 +456,9 @@ DECLARE_EVENT_CLASS(nfs4_open_event,
__entry->fhandle,
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->dir,
- __get_str(name)
+ __get_str(name),
+ __entry->stateid_seq, __entry->stateid_hash,
+ __entry->openstateid_seq, __entry->openstateid_hash
)
);
@@ -452,6 +474,45 @@ DEFINE_NFS4_OPEN_EVENT(nfs4_open_reclaim);
DEFINE_NFS4_OPEN_EVENT(nfs4_open_expired);
DEFINE_NFS4_OPEN_EVENT(nfs4_open_file);
+TRACE_EVENT(nfs4_cached_open,
+ TP_PROTO(
+ const struct nfs4_state *state
+ ),
+ TP_ARGS(state),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(u32, fhandle)
+ __field(u64, fileid)
+ __field(unsigned int, fmode)
+ __field(int, stateid_seq)
+ __field(u32, stateid_hash)
+ ),
+
+ TP_fast_assign(
+ const struct inode *inode = state->inode;
+
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->fileid = NFS_FILEID(inode);
+ __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode));
+ __entry->fmode = (__force unsigned int)state->state;
+ __entry->stateid_seq =
+ be32_to_cpu(state->stateid.seqid);
+ __entry->stateid_hash =
+ nfs_stateid_hash(&state->stateid);
+ ),
+
+ TP_printk(
+ "fmode=%s fileid=%02x:%02x:%llu "
+ "fhandle=0x%08x stateid=%d:0x%08x",
+ __entry->fmode ? show_fmode_flags(__entry->fmode) :
+ "closed",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ (unsigned long long)__entry->fileid,
+ __entry->fhandle,
+ __entry->stateid_seq, __entry->stateid_hash
+ )
+);
+
TRACE_EVENT(nfs4_close,
TP_PROTO(
const struct nfs4_state *state,
@@ -468,6 +529,8 @@ TRACE_EVENT(nfs4_close,
__field(u64, fileid)
__field(unsigned int, fmode)
__field(int, error)
+ __field(int, stateid_seq)
+ __field(u32, stateid_hash)
),
TP_fast_assign(
@@ -478,18 +541,23 @@ TRACE_EVENT(nfs4_close,
__entry->fhandle = nfs_fhandle_hash(NFS_FH(inode));
__entry->fmode = (__force unsigned int)state->state;
__entry->error = error;
+ __entry->stateid_seq =
+ be32_to_cpu(args->stateid.seqid);
+ __entry->stateid_hash =
+ nfs_stateid_hash(&args->stateid);
),
TP_printk(
"error=%d (%s) fmode=%s fileid=%02x:%02x:%llu "
- "fhandle=0x%08x",
+ "fhandle=0x%08x openstateid=%d:0x%08x",
__entry->error,
show_nfsv4_errors(__entry->error),
__entry->fmode ? show_fmode_flags(__entry->fmode) :
"closed",
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid,
- __entry->fhandle
+ __entry->fhandle,
+ __entry->stateid_seq, __entry->stateid_hash
)
);
@@ -523,6 +591,8 @@ DECLARE_EVENT_CLASS(nfs4_lock_event,
__field(dev_t, dev)
__field(u32, fhandle)
__field(u64, fileid)
+ __field(int, stateid_seq)
+ __field(u32, stateid_hash)
),
TP_fast_assign(
@@ -536,11 +606,16 @@ DECLARE_EVENT_CLASS(nfs4_lock_event,
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = NFS_FILEID(inode);
__entry->fhandle = nfs_fhandle_hash(NFS_FH(inode));
+ __entry->stateid_seq =
+ be32_to_cpu(state->stateid.seqid);
+ __entry->stateid_hash =
+ nfs_stateid_hash(&state->stateid);
),
TP_printk(
"error=%d (%s) cmd=%s:%s range=%lld:%lld "
- "fileid=%02x:%02x:%llu fhandle=0x%08x",
+ "fileid=%02x:%02x:%llu fhandle=0x%08x "
+ "stateid=%d:0x%08x",
__entry->error,
show_nfsv4_errors(__entry->error),
show_lock_cmd(__entry->cmd),
@@ -549,7 +624,8 @@ DECLARE_EVENT_CLASS(nfs4_lock_event,
(long long)__entry->end,
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid,
- __entry->fhandle
+ __entry->fhandle,
+ __entry->stateid_seq, __entry->stateid_hash
)
);
@@ -563,11 +639,73 @@ DECLARE_EVENT_CLASS(nfs4_lock_event,
), \
TP_ARGS(request, state, cmd, error))
DEFINE_NFS4_LOCK_EVENT(nfs4_get_lock);
-DEFINE_NFS4_LOCK_EVENT(nfs4_set_lock);
-DEFINE_NFS4_LOCK_EVENT(nfs4_lock_reclaim);
-DEFINE_NFS4_LOCK_EVENT(nfs4_lock_expired);
DEFINE_NFS4_LOCK_EVENT(nfs4_unlock);
+TRACE_EVENT(nfs4_set_lock,
+ TP_PROTO(
+ const struct file_lock *request,
+ const struct nfs4_state *state,
+ const nfs4_stateid *lockstateid,
+ int cmd,
+ int error
+ ),
+
+ TP_ARGS(request, state, lockstateid, cmd, error),
+
+ TP_STRUCT__entry(
+ __field(int, error)
+ __field(int, cmd)
+ __field(char, type)
+ __field(loff_t, start)
+ __field(loff_t, end)
+ __field(dev_t, dev)
+ __field(u32, fhandle)
+ __field(u64, fileid)
+ __field(int, stateid_seq)
+ __field(u32, stateid_hash)
+ __field(int, lockstateid_seq)
+ __field(u32, lockstateid_hash)
+ ),
+
+ TP_fast_assign(
+ const struct inode *inode = state->inode;
+
+ __entry->error = error;
+ __entry->cmd = cmd;
+ __entry->type = request->fl_type;
+ __entry->start = request->fl_start;
+ __entry->end = request->fl_end;
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->fileid = NFS_FILEID(inode);
+ __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode));
+ __entry->stateid_seq =
+ be32_to_cpu(state->stateid.seqid);
+ __entry->stateid_hash =
+ nfs_stateid_hash(&state->stateid);
+ __entry->lockstateid_seq =
+ be32_to_cpu(lockstateid->seqid);
+ __entry->lockstateid_hash =
+ nfs_stateid_hash(lockstateid);
+ ),
+
+ TP_printk(
+ "error=%d (%s) cmd=%s:%s range=%lld:%lld "
+ "fileid=%02x:%02x:%llu fhandle=0x%08x "
+ "stateid=%d:0x%08x lockstateid=%d:0x%08x",
+ __entry->error,
+ show_nfsv4_errors(__entry->error),
+ show_lock_cmd(__entry->cmd),
+ show_lock_type(__entry->type),
+ (long long)__entry->start,
+ (long long)__entry->end,
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ (unsigned long long)__entry->fileid,
+ __entry->fhandle,
+ __entry->stateid_seq, __entry->stateid_hash,
+ __entry->lockstateid_seq, __entry->lockstateid_hash
+ )
+);
+
DECLARE_EVENT_CLASS(nfs4_set_delegation_event,
TP_PROTO(
const struct inode *inode,
@@ -621,20 +759,28 @@ TRACE_EVENT(nfs4_delegreturn_exit,
__field(dev_t, dev)
__field(u32, fhandle)
__field(int, error)
+ __field(int, stateid_seq)
+ __field(u32, stateid_hash)
),
TP_fast_assign(
__entry->dev = res->server->s_dev;
__entry->fhandle = nfs_fhandle_hash(args->fhandle);
__entry->error = error;
+ __entry->stateid_seq =
+ be32_to_cpu(args->stateid->seqid);
+ __entry->stateid_hash =
+ nfs_stateid_hash(args->stateid);
),
TP_printk(
- "error=%d (%s) dev=%02x:%02x fhandle=0x%08x",
+ "error=%d (%s) dev=%02x:%02x fhandle=0x%08x "
+ "stateid=%d:0x%08x",
__entry->error,
show_nfsv4_errors(__entry->error),
MAJOR(__entry->dev), MINOR(__entry->dev),
- __entry->fhandle
+ __entry->fhandle,
+ __entry->stateid_seq, __entry->stateid_hash
)
);
@@ -653,6 +799,8 @@ DECLARE_EVENT_CLASS(nfs4_test_stateid_event,
__field(dev_t, dev)
__field(u32, fhandle)
__field(u64, fileid)
+ __field(int, stateid_seq)
+ __field(u32, stateid_hash)
),
TP_fast_assign(
@@ -662,15 +810,21 @@ DECLARE_EVENT_CLASS(nfs4_test_stateid_event,
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = NFS_FILEID(inode);
__entry->fhandle = nfs_fhandle_hash(NFS_FH(inode));
+ __entry->stateid_seq =
+ be32_to_cpu(state->stateid.seqid);
+ __entry->stateid_hash =
+ nfs_stateid_hash(&state->stateid);
),
TP_printk(
- "error=%d (%s) fileid=%02x:%02x:%llu fhandle=0x%08x",
+ "error=%d (%s) fileid=%02x:%02x:%llu fhandle=0x%08x "
+ "stateid=%d:0x%08x",
__entry->error,
show_nfsv4_errors(__entry->error),
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid,
- __entry->fhandle
+ __entry->fhandle,
+ __entry->stateid_seq, __entry->stateid_hash
)
);
@@ -820,7 +974,6 @@ DECLARE_EVENT_CLASS(nfs4_inode_event,
), \
TP_ARGS(inode, error))
-DEFINE_NFS4_INODE_EVENT(nfs4_setattr);
DEFINE_NFS4_INODE_EVENT(nfs4_access);
DEFINE_NFS4_INODE_EVENT(nfs4_readlink);
DEFINE_NFS4_INODE_EVENT(nfs4_readdir);
@@ -830,8 +983,59 @@ DEFINE_NFS4_INODE_EVENT(nfs4_set_acl);
DEFINE_NFS4_INODE_EVENT(nfs4_get_security_label);
DEFINE_NFS4_INODE_EVENT(nfs4_set_security_label);
#endif /* CONFIG_NFS_V4_SECURITY_LABEL */
-DEFINE_NFS4_INODE_EVENT(nfs4_recall_delegation);
-DEFINE_NFS4_INODE_EVENT(nfs4_delegreturn);
+
+DECLARE_EVENT_CLASS(nfs4_inode_stateid_event,
+ TP_PROTO(
+ const struct inode *inode,
+ const nfs4_stateid *stateid,
+ int error
+ ),
+
+ TP_ARGS(inode, stateid, error),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(u32, fhandle)
+ __field(u64, fileid)
+ __field(int, error)
+ __field(int, stateid_seq)
+ __field(u32, stateid_hash)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->fileid = NFS_FILEID(inode);
+ __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode));
+ __entry->error = error;
+ __entry->stateid_seq =
+ be32_to_cpu(stateid->seqid);
+ __entry->stateid_hash =
+ nfs_stateid_hash(stateid);
+ ),
+
+ TP_printk(
+ "error=%d (%s) fileid=%02x:%02x:%llu fhandle=0x%08x "
+ "stateid=%d:0x%08x",
+ __entry->error,
+ show_nfsv4_errors(__entry->error),
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ (unsigned long long)__entry->fileid,
+ __entry->fhandle,
+ __entry->stateid_seq, __entry->stateid_hash
+ )
+);
+
+#define DEFINE_NFS4_INODE_STATEID_EVENT(name) \
+ DEFINE_EVENT(nfs4_inode_stateid_event, name, \
+ TP_PROTO( \
+ const struct inode *inode, \
+ const nfs4_stateid *stateid, \
+ int error \
+ ), \
+ TP_ARGS(inode, stateid, error))
+
+DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_setattr);
+DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_delegreturn);
DECLARE_EVENT_CLASS(nfs4_getattr_event,
TP_PROTO(
@@ -941,8 +1145,74 @@ DECLARE_EVENT_CLASS(nfs4_inode_callback_event,
), \
TP_ARGS(clp, fhandle, inode, error))
DEFINE_NFS4_INODE_CALLBACK_EVENT(nfs4_cb_getattr);
-DEFINE_NFS4_INODE_CALLBACK_EVENT(nfs4_cb_layoutrecall_inode);
+DECLARE_EVENT_CLASS(nfs4_inode_stateid_callback_event,
+ TP_PROTO(
+ const struct nfs_client *clp,
+ const struct nfs_fh *fhandle,
+ const struct inode *inode,
+ const nfs4_stateid *stateid,
+ int error
+ ),
+
+ TP_ARGS(clp, fhandle, inode, stateid, error),
+
+ TP_STRUCT__entry(
+ __field(int, error)
+ __field(dev_t, dev)
+ __field(u32, fhandle)
+ __field(u64, fileid)
+ __string(dstaddr, clp ?
+ rpc_peeraddr2str(clp->cl_rpcclient,
+ RPC_DISPLAY_ADDR) : "unknown")
+ __field(int, stateid_seq)
+ __field(u32, stateid_hash)
+ ),
+
+ TP_fast_assign(
+ __entry->error = error;
+ __entry->fhandle = nfs_fhandle_hash(fhandle);
+ if (inode != NULL) {
+ __entry->fileid = NFS_FILEID(inode);
+ __entry->dev = inode->i_sb->s_dev;
+ } else {
+ __entry->fileid = 0;
+ __entry->dev = 0;
+ }
+ __assign_str(dstaddr, clp ?
+ rpc_peeraddr2str(clp->cl_rpcclient,
+ RPC_DISPLAY_ADDR) : "unknown")
+ __entry->stateid_seq =
+ be32_to_cpu(stateid->seqid);
+ __entry->stateid_hash =
+ nfs_stateid_hash(stateid);
+ ),
+
+ TP_printk(
+ "error=%d (%s) fileid=%02x:%02x:%llu fhandle=0x%08x "
+ "stateid=%d:0x%08x dstaddr=%s",
+ __entry->error,
+ show_nfsv4_errors(__entry->error),
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ (unsigned long long)__entry->fileid,
+ __entry->fhandle,
+ __entry->stateid_seq, __entry->stateid_hash,
+ __get_str(dstaddr)
+ )
+);
+
+#define DEFINE_NFS4_INODE_STATEID_CALLBACK_EVENT(name) \
+ DEFINE_EVENT(nfs4_inode_stateid_callback_event, name, \
+ TP_PROTO( \
+ const struct nfs_client *clp, \
+ const struct nfs_fh *fhandle, \
+ const struct inode *inode, \
+ const nfs4_stateid *stateid, \
+ int error \
+ ), \
+ TP_ARGS(clp, fhandle, inode, stateid, error))
+DEFINE_NFS4_INODE_STATEID_CALLBACK_EVENT(nfs4_cb_recall);
+DEFINE_NFS4_INODE_STATEID_CALLBACK_EVENT(nfs4_cb_layoutrecall_file);
DECLARE_EVENT_CLASS(nfs4_idmap_event,
TP_PROTO(
@@ -1005,28 +1275,37 @@ DECLARE_EVENT_CLASS(nfs4_read_event,
__field(loff_t, offset)
__field(size_t, count)
__field(int, error)
+ __field(int, stateid_seq)
+ __field(u32, stateid_hash)
),
TP_fast_assign(
const struct inode *inode = hdr->inode;
+ const struct nfs4_state *state =
+ hdr->args.context->state;
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = NFS_FILEID(inode);
__entry->fhandle = nfs_fhandle_hash(NFS_FH(inode));
__entry->offset = hdr->args.offset;
__entry->count = hdr->args.count;
__entry->error = error;
+ __entry->stateid_seq =
+ be32_to_cpu(state->stateid.seqid);
+ __entry->stateid_hash =
+ nfs_stateid_hash(&state->stateid);
),
TP_printk(
"error=%d (%s) fileid=%02x:%02x:%llu fhandle=0x%08x "
- "offset=%lld count=%zu",
+ "offset=%lld count=%zu stateid=%d:0x%08x",
__entry->error,
show_nfsv4_errors(__entry->error),
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid,
__entry->fhandle,
(long long)__entry->offset,
- __entry->count
+ __entry->count,
+ __entry->stateid_seq, __entry->stateid_hash
)
);
#define DEFINE_NFS4_READ_EVENT(name) \
@@ -1056,28 +1335,37 @@ DECLARE_EVENT_CLASS(nfs4_write_event,
__field(loff_t, offset)
__field(size_t, count)
__field(int, error)
+ __field(int, stateid_seq)
+ __field(u32, stateid_hash)
),
TP_fast_assign(
const struct inode *inode = hdr->inode;
+ const struct nfs4_state *state =
+ hdr->args.context->state;
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = NFS_FILEID(inode);
__entry->fhandle = nfs_fhandle_hash(NFS_FH(inode));
__entry->offset = hdr->args.offset;
__entry->count = hdr->args.count;
__entry->error = error;
+ __entry->stateid_seq =
+ be32_to_cpu(state->stateid.seqid);
+ __entry->stateid_hash =
+ nfs_stateid_hash(&state->stateid);
),
TP_printk(
"error=%d (%s) fileid=%02x:%02x:%llu fhandle=0x%08x "
- "offset=%lld count=%zu",
+ "offset=%lld count=%zu stateid=%d:0x%08x",
__entry->error,
show_nfsv4_errors(__entry->error),
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->fileid,
__entry->fhandle,
(long long)__entry->offset,
- __entry->count
+ __entry->count,
+ __entry->stateid_seq, __entry->stateid_hash
)
);
@@ -1154,10 +1442,11 @@ TRACE_EVENT(nfs4_layoutget,
const struct nfs_open_context *ctx,
const struct pnfs_layout_range *args,
const struct pnfs_layout_range *res,
+ const nfs4_stateid *layout_stateid,
int error
),
- TP_ARGS(ctx, args, res, error),
+ TP_ARGS(ctx, args, res, layout_stateid, error),
TP_STRUCT__entry(
__field(dev_t, dev)
@@ -1167,10 +1456,15 @@ TRACE_EVENT(nfs4_layoutget,
__field(u64, offset)
__field(u64, count)
__field(int, error)
+ __field(int, stateid_seq)
+ __field(u32, stateid_hash)
+ __field(int, layoutstateid_seq)
+ __field(u32, layoutstateid_hash)
),
TP_fast_assign(
const struct inode *inode = d_inode(ctx->dentry);
+ const struct nfs4_state *state = ctx->state;
__entry->dev = inode->i_sb->s_dev;
__entry->fileid = NFS_FILEID(inode);
__entry->fhandle = nfs_fhandle_hash(NFS_FH(inode));
@@ -1178,11 +1472,25 @@ TRACE_EVENT(nfs4_layoutget,
__entry->offset = args->offset;
__entry->count = args->length;
__entry->error = error;
+ __entry->stateid_seq =
+ be32_to_cpu(state->stateid.seqid);
+ __entry->stateid_hash =
+ nfs_stateid_hash(&state->stateid);
+ if (!error) {
+ __entry->layoutstateid_seq =
+ be32_to_cpu(layout_stateid->seqid);
+ __entry->layoutstateid_hash =
+ nfs_stateid_hash(layout_stateid);
+ } else {
+ __entry->layoutstateid_seq = 0;
+ __entry->layoutstateid_hash = 0;
+ }
),
TP_printk(
"error=%d (%s) fileid=%02x:%02x:%llu fhandle=0x%08x "
- "iomode=%s offset=%llu count=%llu",
+ "iomode=%s offset=%llu count=%llu stateid=%d:0x%08x "
+ "layoutstateid=%d:0x%08x",
__entry->error,
show_nfsv4_errors(__entry->error),
MAJOR(__entry->dev), MINOR(__entry->dev),
@@ -1190,14 +1498,83 @@ TRACE_EVENT(nfs4_layoutget,
__entry->fhandle,
show_pnfs_iomode(__entry->iomode),
(unsigned long long)__entry->offset,
- (unsigned long long)__entry->count
+ (unsigned long long)__entry->count,
+ __entry->stateid_seq, __entry->stateid_hash,
+ __entry->layoutstateid_seq, __entry->layoutstateid_hash
)
);
-DEFINE_NFS4_INODE_EVENT(nfs4_layoutcommit);
-DEFINE_NFS4_INODE_EVENT(nfs4_layoutreturn);
+DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_layoutcommit);
+DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_layoutreturn);
DEFINE_NFS4_INODE_EVENT(nfs4_layoutreturn_on_close);
+#define show_pnfs_update_layout_reason(reason) \
+ __print_symbolic(reason, \
+ { PNFS_UPDATE_LAYOUT_UNKNOWN, "unknown" }, \
+ { PNFS_UPDATE_LAYOUT_NO_PNFS, "no pnfs" }, \
+ { PNFS_UPDATE_LAYOUT_RD_ZEROLEN, "read+zerolen" }, \
+ { PNFS_UPDATE_LAYOUT_MDSTHRESH, "mdsthresh" }, \
+ { PNFS_UPDATE_LAYOUT_NOMEM, "nomem" }, \
+ { PNFS_UPDATE_LAYOUT_BULK_RECALL, "bulk recall" }, \
+ { PNFS_UPDATE_LAYOUT_IO_TEST_FAIL, "io test fail" }, \
+ { PNFS_UPDATE_LAYOUT_FOUND_CACHED, "found cached" }, \
+ { PNFS_UPDATE_LAYOUT_RETURN, "layoutreturn" }, \
+ { PNFS_UPDATE_LAYOUT_BLOCKED, "layouts blocked" }, \
+ { PNFS_UPDATE_LAYOUT_SEND_LAYOUTGET, "sent layoutget" })
+
+TRACE_EVENT(pnfs_update_layout,
+ TP_PROTO(struct inode *inode,
+ loff_t pos,
+ u64 count,
+ enum pnfs_iomode iomode,
+ struct pnfs_layout_hdr *lo,
+ enum pnfs_update_layout_reason reason
+ ),
+ TP_ARGS(inode, pos, count, iomode, lo, reason),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(u64, fileid)
+ __field(u32, fhandle)
+ __field(loff_t, pos)
+ __field(u64, count)
+ __field(enum pnfs_iomode, iomode)
+ __field(int, layoutstateid_seq)
+ __field(u32, layoutstateid_hash)
+ __field(enum pnfs_update_layout_reason, reason)
+ ),
+ TP_fast_assign(
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->fileid = NFS_FILEID(inode);
+ __entry->fhandle = nfs_fhandle_hash(NFS_FH(inode));
+ __entry->pos = pos;
+ __entry->count = count;
+ __entry->iomode = iomode;
+ __entry->reason = reason;
+ if (lo != NULL) {
+ __entry->layoutstateid_seq =
+ be32_to_cpu(lo->plh_stateid.seqid);
+ __entry->layoutstateid_hash =
+ nfs_stateid_hash(&lo->plh_stateid);
+ } else {
+ __entry->layoutstateid_seq = 0;
+ __entry->layoutstateid_hash = 0;
+ }
+ ),
+ TP_printk(
+ "fileid=%02x:%02x:%llu fhandle=0x%08x "
+ "iomode=%s pos=%llu count=%llu "
+ "layoutstateid=%d:0x%08x (%s)",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ (unsigned long long)__entry->fileid,
+ __entry->fhandle,
+ show_pnfs_iomode(__entry->iomode),
+ (unsigned long long)__entry->pos,
+ (unsigned long long)__entry->count,
+ __entry->layoutstateid_seq, __entry->layoutstateid_hash,
+ show_pnfs_update_layout_reason(__entry->reason)
+ )
+);
+
#endif /* CONFIG_NFS_V4_1 */
#endif /* _TRACE_NFS4_H */
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index dfed4f5c8fcc..4e4441216804 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -3615,6 +3615,7 @@ static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, st
status = 0;
if (unlikely(!(bitmap[0] & FATTR4_WORD0_FS_LOCATIONS)))
goto out;
+ bitmap[0] &= ~FATTR4_WORD0_FS_LOCATIONS;
status = -EIO;
/* Ignore borken servers that return unrequested attrs */
if (unlikely(res == NULL))
diff --git a/fs/nfs/nfstrace.h b/fs/nfs/nfstrace.h
index 59f838cdc009..9f80a086b612 100644
--- a/fs/nfs/nfstrace.h
+++ b/fs/nfs/nfstrace.h
@@ -39,7 +39,6 @@
{ 1 << NFS_INO_INVALIDATING, "INVALIDATING" }, \
{ 1 << NFS_INO_FLUSHING, "FLUSHING" }, \
{ 1 << NFS_INO_FSCACHE, "FSCACHE" }, \
- { 1 << NFS_INO_COMMIT, "COMMIT" }, \
{ 1 << NFS_INO_LAYOUTCOMMIT, "NEED_LAYOUTCOMMIT" }, \
{ 1 << NFS_INO_LAYOUTCOMMITTING, "LAYOUTCOMMIT" })
diff --git a/fs/nfs/objlayout/objio_osd.c b/fs/nfs/objlayout/objio_osd.c
index 5c0c6b58157f..9aebffb40505 100644
--- a/fs/nfs/objlayout/objio_osd.c
+++ b/fs/nfs/objlayout/objio_osd.c
@@ -476,10 +476,7 @@ static struct page *__r4w_get_page(void *priv, u64 offset, bool *uptodate)
}
unlock_page(page);
}
- if (PageDirty(page) || PageWriteback(page))
- *uptodate = true;
- else
- *uptodate = PageUptodate(page);
+ *uptodate = PageUptodate(page);
dprintk("%s: index=0x%lx uptodate=%d\n", __func__, index, *uptodate);
return page;
}
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index fe3ddd20ff89..8ce4f61cbaa5 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -101,53 +101,18 @@ nfs_page_free(struct nfs_page *p)
kmem_cache_free(nfs_page_cachep, p);
}
-static void
-nfs_iocounter_inc(struct nfs_io_counter *c)
-{
- atomic_inc(&c->io_count);
-}
-
-static void
-nfs_iocounter_dec(struct nfs_io_counter *c)
-{
- if (atomic_dec_and_test(&c->io_count)) {
- clear_bit(NFS_IO_INPROGRESS, &c->flags);
- smp_mb__after_atomic();
- wake_up_bit(&c->flags, NFS_IO_INPROGRESS);
- }
-}
-
-static int
-__nfs_iocounter_wait(struct nfs_io_counter *c)
-{
- wait_queue_head_t *wq = bit_waitqueue(&c->flags, NFS_IO_INPROGRESS);
- DEFINE_WAIT_BIT(q, &c->flags, NFS_IO_INPROGRESS);
- int ret = 0;
-
- do {
- prepare_to_wait(wq, &q.wait, TASK_KILLABLE);
- set_bit(NFS_IO_INPROGRESS, &c->flags);
- if (atomic_read(&c->io_count) == 0)
- break;
- ret = nfs_wait_bit_killable(&q.key);
- } while (atomic_read(&c->io_count) != 0 && !ret);
- finish_wait(wq, &q.wait);
- return ret;
-}
-
/**
* nfs_iocounter_wait - wait for i/o to complete
- * @c: nfs_io_counter to use
+ * @l_ctx: nfs_lock_context with io_counter to use
*
* returns -ERESTARTSYS if interrupted by a fatal signal.
* Otherwise returns 0 once the io_count hits 0.
*/
int
-nfs_iocounter_wait(struct nfs_io_counter *c)
+nfs_iocounter_wait(struct nfs_lock_context *l_ctx)
{
- if (atomic_read(&c->io_count) == 0)
- return 0;
- return __nfs_iocounter_wait(c);
+ return wait_on_atomic_t(&l_ctx->io_count, nfs_wait_atomic_killable,
+ TASK_KILLABLE);
}
/*
@@ -370,7 +335,7 @@ nfs_create_request(struct nfs_open_context *ctx, struct page *page,
return ERR_CAST(l_ctx);
}
req->wb_lock_context = l_ctx;
- nfs_iocounter_inc(&l_ctx->io_count);
+ atomic_inc(&l_ctx->io_count);
/* Initialize the request struct. Initially, we assume a
* long write-back delay. This will be adjusted in
@@ -431,7 +396,8 @@ static void nfs_clear_request(struct nfs_page *req)
req->wb_page = NULL;
}
if (l_ctx != NULL) {
- nfs_iocounter_dec(&l_ctx->io_count);
+ if (atomic_dec_and_test(&l_ctx->io_count))
+ wake_up_atomic_t(&l_ctx->io_count);
nfs_put_lock_context(l_ctx);
req->wb_lock_context = NULL;
}
@@ -664,22 +630,11 @@ EXPORT_SYMBOL_GPL(nfs_initiate_pgio);
* @desc: IO descriptor
* @hdr: pageio header
*/
-static int nfs_pgio_error(struct nfs_pageio_descriptor *desc,
- struct nfs_pgio_header *hdr)
+static void nfs_pgio_error(struct nfs_pgio_header *hdr)
{
- struct nfs_pgio_mirror *mirror;
- u32 midx;
-
set_bit(NFS_IOHDR_REDO, &hdr->flags);
nfs_pgio_data_destroy(hdr);
hdr->completion_ops->completion(hdr);
- /* TODO: Make sure it's right to clean up all mirrors here
- * and not just hdr->pgio_mirror_idx */
- for (midx = 0; midx < desc->pg_mirror_count; midx++) {
- mirror = &desc->pg_mirrors[midx];
- desc->pg_completion_ops->error_cleanup(&mirror->pg_list);
- }
- return -ENOMEM;
}
/**
@@ -800,8 +755,11 @@ int nfs_generic_pgio(struct nfs_pageio_descriptor *desc,
unsigned int pagecount, pageused;
pagecount = nfs_page_array_len(mirror->pg_base, mirror->pg_count);
- if (!nfs_pgarray_set(&hdr->page_array, pagecount))
- return nfs_pgio_error(desc, hdr);
+ if (!nfs_pgarray_set(&hdr->page_array, pagecount)) {
+ nfs_pgio_error(hdr);
+ desc->pg_error = -ENOMEM;
+ return desc->pg_error;
+ }
nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq);
pages = hdr->page_array.pagevec;
@@ -819,8 +777,11 @@ int nfs_generic_pgio(struct nfs_pageio_descriptor *desc,
*pages++ = last_page = req->wb_page;
}
}
- if (WARN_ON_ONCE(pageused != pagecount))
- return nfs_pgio_error(desc, hdr);
+ if (WARN_ON_ONCE(pageused != pagecount)) {
+ nfs_pgio_error(hdr);
+ desc->pg_error = -EINVAL;
+ return desc->pg_error;
+ }
if ((desc->pg_ioflags & FLUSH_COND_STABLE) &&
(desc->pg_moreio || nfs_reqs_to_commit(&cinfo)))
@@ -835,18 +796,13 @@ EXPORT_SYMBOL_GPL(nfs_generic_pgio);
static int nfs_generic_pg_pgios(struct nfs_pageio_descriptor *desc)
{
- struct nfs_pgio_mirror *mirror;
struct nfs_pgio_header *hdr;
int ret;
- mirror = nfs_pgio_current_mirror(desc);
-
hdr = nfs_pgio_header_alloc(desc->pg_rw_ops);
if (!hdr) {
- /* TODO: make sure this is right with mirroring - or
- * should it back out all mirrors? */
- desc->pg_completion_ops->error_cleanup(&mirror->pg_list);
- return -ENOMEM;
+ desc->pg_error = -ENOMEM;
+ return desc->pg_error;
}
nfs_pgheader_init(desc, hdr, nfs_pgio_header_free);
ret = nfs_generic_pgio(desc, hdr);
@@ -874,6 +830,9 @@ static int nfs_pageio_setup_mirroring(struct nfs_pageio_descriptor *pgio,
mirror_count = pgio->pg_ops->pg_get_mirror_count(pgio, req);
+ if (pgio->pg_error < 0)
+ return pgio->pg_error;
+
if (!mirror_count || mirror_count > NFS_PAGEIO_DESCRIPTOR_MIRROR_MAX)
return -EINVAL;
@@ -903,12 +862,6 @@ static void nfs_pageio_cleanup_mirroring(struct nfs_pageio_descriptor *pgio)
pgio->pg_mirrors_dynamic = NULL;
}
-static bool nfs_match_open_context(const struct nfs_open_context *ctx1,
- const struct nfs_open_context *ctx2)
-{
- return ctx1->cred == ctx2->cred && ctx1->state == ctx2->state;
-}
-
static bool nfs_match_lock_context(const struct nfs_lock_context *l1,
const struct nfs_lock_context *l2)
{
@@ -982,6 +935,8 @@ static int nfs_pageio_do_add_request(struct nfs_pageio_descriptor *desc,
} else {
if (desc->pg_ops->pg_init)
desc->pg_ops->pg_init(desc, req);
+ if (desc->pg_error < 0)
+ return 0;
mirror->pg_base = req->wb_pgbase;
}
if (!nfs_can_coalesce_requests(prev, req, desc))
@@ -1147,6 +1102,8 @@ int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
bytes = req->wb_bytes;
nfs_pageio_setup_mirroring(desc, req);
+ if (desc->pg_error < 0)
+ goto out_failed;
for (midx = 0; midx < desc->pg_mirror_count; midx++) {
if (midx) {
@@ -1163,7 +1120,8 @@ int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
if (IS_ERR(dupreq)) {
nfs_page_group_unlock(req);
- return 0;
+ desc->pg_error = PTR_ERR(dupreq);
+ goto out_failed;
}
nfs_lock_request(dupreq);
@@ -1176,10 +1134,32 @@ int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
if (nfs_pgio_has_mirroring(desc))
desc->pg_mirror_idx = midx;
if (!nfs_pageio_add_request_mirror(desc, dupreq))
- return 0;
+ goto out_failed;
}
return 1;
+
+out_failed:
+ /*
+ * We might have failed before sending any reqs over wire.
+ * Clean up rest of the reqs in mirror pg_list.
+ */
+ if (desc->pg_error) {
+ struct nfs_pgio_mirror *mirror;
+ void (*func)(struct list_head *);
+
+ /* remember fatal errors */
+ if (nfs_error_is_fatal(desc->pg_error))
+ mapping_set_error(desc->pg_inode->i_mapping,
+ desc->pg_error);
+
+ func = desc->pg_completion_ops->error_cleanup;
+ for (midx = 0; midx < desc->pg_mirror_count; midx++) {
+ mirror = &desc->pg_mirrors[midx];
+ func(&mirror->pg_list);
+ }
+ }
+ return 0;
}
/*
@@ -1232,7 +1212,7 @@ int nfs_pageio_resend(struct nfs_pageio_descriptor *desc,
nfs_pageio_complete(desc);
if (!list_empty(&failed)) {
list_move(&failed, &hdr->pages);
- return -EIO;
+ return desc->pg_error < 0 ? desc->pg_error : -EIO;
}
return 0;
}
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index 93496c059837..a3592cc34a20 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -53,7 +53,7 @@ static DEFINE_SPINLOCK(pnfs_spinlock);
static LIST_HEAD(pnfs_modules_tbl);
static int
-pnfs_send_layoutreturn(struct pnfs_layout_hdr *lo, nfs4_stateid stateid,
+pnfs_send_layoutreturn(struct pnfs_layout_hdr *lo, const nfs4_stateid *stateid,
enum pnfs_iomode iomode, bool sync);
/* Return the registered pnfs layout driver module matching given id */
@@ -385,13 +385,13 @@ static void pnfs_layoutreturn_before_put_lseg(struct pnfs_layout_segment *lseg,
enum pnfs_iomode iomode;
bool send;
- stateid = lo->plh_stateid;
+ nfs4_stateid_copy(&stateid, &lo->plh_stateid);
iomode = lo->plh_return_iomode;
send = pnfs_prepare_layoutreturn(lo);
spin_unlock(&inode->i_lock);
if (send) {
/* Send an async layoutreturn so we dont deadlock */
- pnfs_send_layoutreturn(lo, stateid, iomode, false);
+ pnfs_send_layoutreturn(lo, &stateid, iomode, false);
}
} else
spin_unlock(&inode->i_lock);
@@ -566,10 +566,10 @@ static int mark_lseg_invalid(struct pnfs_layout_segment *lseg,
int
pnfs_mark_matching_lsegs_invalid(struct pnfs_layout_hdr *lo,
struct list_head *tmp_list,
- struct pnfs_layout_range *recall_range)
+ const struct pnfs_layout_range *recall_range)
{
struct pnfs_layout_segment *lseg, *next;
- int invalid = 0, removed = 0;
+ int remaining = 0;
dprintk("%s:Begin lo %p\n", __func__, lo);
@@ -582,11 +582,11 @@ pnfs_mark_matching_lsegs_invalid(struct pnfs_layout_hdr *lo,
"offset %llu length %llu\n", __func__,
lseg, lseg->pls_range.iomode, lseg->pls_range.offset,
lseg->pls_range.length);
- invalid++;
- removed += mark_lseg_invalid(lseg, tmp_list);
+ if (!mark_lseg_invalid(lseg, tmp_list))
+ remaining++;
}
- dprintk("%s:Return %i\n", __func__, invalid - removed);
- return invalid - removed;
+ dprintk("%s:Return %i\n", __func__, remaining);
+ return remaining;
}
/* note free_me must contain lsegs from a single layout_hdr */
@@ -618,7 +618,6 @@ pnfs_destroy_layout(struct nfs_inode *nfsi)
pnfs_get_layout_hdr(lo);
pnfs_layout_clear_fail_bit(lo, NFS_LAYOUT_RO_FAILED);
pnfs_layout_clear_fail_bit(lo, NFS_LAYOUT_RW_FAILED);
- pnfs_clear_retry_layoutget(lo);
spin_unlock(&nfsi->vfs_inode.i_lock);
pnfs_free_lseg_list(&tmp_list);
pnfs_put_layout_hdr(lo);
@@ -703,6 +702,8 @@ pnfs_layout_free_bulk_destroy_list(struct list_head *layout_list,
ret = -EAGAIN;
spin_unlock(&inode->i_lock);
pnfs_free_lseg_list(&lseg_list);
+ /* Free all lsegs that are attached to commit buckets */
+ nfs_commit_inode(inode, 0);
pnfs_put_layout_hdr(lo);
iput(inode);
}
@@ -826,7 +827,7 @@ pnfs_layoutgets_blocked(const struct pnfs_layout_hdr *lo)
int
pnfs_choose_layoutget_stateid(nfs4_stateid *dst, struct pnfs_layout_hdr *lo,
- struct pnfs_layout_range *range,
+ const struct pnfs_layout_range *range,
struct nfs4_state *open_state)
{
int status = 0;
@@ -861,7 +862,7 @@ pnfs_choose_layoutget_stateid(nfs4_stateid *dst, struct pnfs_layout_hdr *lo,
static struct pnfs_layout_segment *
send_layoutget(struct pnfs_layout_hdr *lo,
struct nfs_open_context *ctx,
- struct pnfs_layout_range *range,
+ const struct pnfs_layout_range *range,
gfp_t gfp_flags)
{
struct inode *ino = lo->plh_inode;
@@ -872,44 +873,41 @@ send_layoutget(struct pnfs_layout_hdr *lo,
dprintk("--> %s\n", __func__);
- lgp = kzalloc(sizeof(*lgp), gfp_flags);
- if (lgp == NULL)
- return NULL;
-
- i_size = i_size_read(ino);
-
- lgp->args.minlength = PAGE_CACHE_SIZE;
- if (lgp->args.minlength > range->length)
- lgp->args.minlength = range->length;
- if (range->iomode == IOMODE_READ) {
- if (range->offset >= i_size)
- lgp->args.minlength = 0;
- else if (i_size - range->offset < lgp->args.minlength)
- lgp->args.minlength = i_size - range->offset;
- }
- lgp->args.maxcount = PNFS_LAYOUT_MAXSIZE;
- lgp->args.range = *range;
- lgp->args.type = server->pnfs_curr_ld->id;
- lgp->args.inode = ino;
- lgp->args.ctx = get_nfs_open_context(ctx);
- lgp->gfp_flags = gfp_flags;
- lgp->cred = lo->plh_lc_cred;
-
- /* Synchronously retrieve layout information from server and
- * store in lseg.
+ /*
+ * Synchronously retrieve layout information from server and
+ * store in lseg. If we race with a concurrent seqid morphing
+ * op, then re-send the LAYOUTGET.
*/
- lseg = nfs4_proc_layoutget(lgp, gfp_flags);
- if (IS_ERR(lseg)) {
- switch (PTR_ERR(lseg)) {
- case -ENOMEM:
- case -ERESTARTSYS:
- break;
- default:
- /* remember that LAYOUTGET failed and suspend trying */
- pnfs_layout_io_set_failed(lo, range->iomode);
+ do {
+ lgp = kzalloc(sizeof(*lgp), gfp_flags);
+ if (lgp == NULL)
+ return NULL;
+
+ i_size = i_size_read(ino);
+
+ lgp->args.minlength = PAGE_CACHE_SIZE;
+ if (lgp->args.minlength > range->length)
+ lgp->args.minlength = range->length;
+ if (range->iomode == IOMODE_READ) {
+ if (range->offset >= i_size)
+ lgp->args.minlength = 0;
+ else if (i_size - range->offset < lgp->args.minlength)
+ lgp->args.minlength = i_size - range->offset;
}
- return NULL;
- } else
+ lgp->args.maxcount = PNFS_LAYOUT_MAXSIZE;
+ pnfs_copy_range(&lgp->args.range, range);
+ lgp->args.type = server->pnfs_curr_ld->id;
+ lgp->args.inode = ino;
+ lgp->args.ctx = get_nfs_open_context(ctx);
+ lgp->gfp_flags = gfp_flags;
+ lgp->cred = lo->plh_lc_cred;
+
+ lseg = nfs4_proc_layoutget(lgp, gfp_flags);
+ } while (lseg == ERR_PTR(-EAGAIN));
+
+ if (IS_ERR(lseg) && !nfs_error_is_fatal(PTR_ERR(lseg)))
+ lseg = NULL;
+ else
pnfs_layout_clear_fail_bit(lo,
pnfs_iomode_to_fail_bit(range->iomode));
@@ -940,7 +938,7 @@ void pnfs_clear_layoutreturn_waitbit(struct pnfs_layout_hdr *lo)
}
static int
-pnfs_send_layoutreturn(struct pnfs_layout_hdr *lo, nfs4_stateid stateid,
+pnfs_send_layoutreturn(struct pnfs_layout_hdr *lo, const nfs4_stateid *stateid,
enum pnfs_iomode iomode, bool sync)
{
struct inode *ino = lo->plh_inode;
@@ -957,7 +955,7 @@ pnfs_send_layoutreturn(struct pnfs_layout_hdr *lo, nfs4_stateid stateid,
goto out;
}
- lrp->args.stateid = stateid;
+ nfs4_stateid_copy(&lrp->args.stateid, stateid);
lrp->args.layout_type = NFS_SERVER(ino)->pnfs_curr_ld->id;
lrp->args.inode = ino;
lrp->args.range.iomode = iomode;
@@ -1000,7 +998,7 @@ _pnfs_return_layout(struct inode *ino)
dprintk("NFS: %s no layout to return\n", __func__);
goto out;
}
- stateid = nfsi->layout->plh_stateid;
+ nfs4_stateid_copy(&stateid, &nfsi->layout->plh_stateid);
/* Reference matched in nfs4_layoutreturn_release */
pnfs_get_layout_hdr(lo);
empty = list_empty(&lo->plh_segs);
@@ -1028,7 +1026,7 @@ _pnfs_return_layout(struct inode *ino)
spin_unlock(&ino->i_lock);
pnfs_free_lseg_list(&tmp_list);
if (send)
- status = pnfs_send_layoutreturn(lo, stateid, IOMODE_ANY, true);
+ status = pnfs_send_layoutreturn(lo, &stateid, IOMODE_ANY, true);
out_put_layout_hdr:
pnfs_put_layout_hdr(lo);
out:
@@ -1091,13 +1089,12 @@ bool pnfs_roc(struct inode *ino)
goto out_noroc;
}
- stateid = lo->plh_stateid;
+ nfs4_stateid_copy(&stateid, &lo->plh_stateid);
/* always send layoutreturn if being marked so */
if (test_and_clear_bit(NFS_LAYOUT_RETURN_BEFORE_CLOSE,
&lo->plh_flags))
layoutreturn = pnfs_prepare_layoutreturn(lo);
- pnfs_clear_retry_layoutget(lo);
list_for_each_entry_safe(lseg, tmp, &lo->plh_segs, pls_list)
/* If we are sending layoutreturn, invalidate all valid lsegs */
if (layoutreturn || test_bit(NFS_LSEG_ROC, &lseg->pls_flags)) {
@@ -1119,7 +1116,7 @@ out_noroc:
pnfs_free_lseg_list(&tmp_list);
pnfs_layoutcommit_inode(ino, true);
if (layoutreturn)
- pnfs_send_layoutreturn(lo, stateid, IOMODE_ANY, true);
+ pnfs_send_layoutreturn(lo, &stateid, IOMODE_ANY, true);
return roc;
}
@@ -1144,6 +1141,7 @@ void pnfs_roc_set_barrier(struct inode *ino, u32 barrier)
spin_lock(&ino->i_lock);
lo = NFS_I(ino)->layout;
+ pnfs_mark_layout_returned_if_empty(lo);
if (pnfs_seqid_is_newer(barrier, lo->plh_barrier))
lo->plh_barrier = barrier;
spin_unlock(&ino->i_lock);
@@ -1460,25 +1458,15 @@ static bool pnfs_within_mdsthreshold(struct nfs_open_context *ctx,
return ret;
}
-/* stop waiting if someone clears NFS_LAYOUT_RETRY_LAYOUTGET bit. */
-static int pnfs_layoutget_retry_bit_wait(struct wait_bit_key *key)
-{
- if (!test_bit(NFS_LAYOUT_RETRY_LAYOUTGET, key->flags))
- return 1;
- return nfs_wait_bit_killable(key);
-}
-
static bool pnfs_prepare_to_retry_layoutget(struct pnfs_layout_hdr *lo)
{
- if (!pnfs_should_retry_layoutget(lo))
- return false;
/*
* send layoutcommit as it can hold up layoutreturn due to lseg
* reference
*/
pnfs_layoutcommit_inode(lo->plh_inode, false);
return !wait_on_bit_action(&lo->plh_flags, NFS_LAYOUT_RETURN,
- pnfs_layoutget_retry_bit_wait,
+ nfs_wait_bit_killable,
TASK_UNINTERRUPTIBLE);
}
@@ -1515,14 +1503,23 @@ pnfs_update_layout(struct inode *ino,
struct pnfs_layout_segment *lseg = NULL;
bool first;
- if (!pnfs_enabled_sb(NFS_SERVER(ino)))
+ if (!pnfs_enabled_sb(NFS_SERVER(ino))) {
+ trace_pnfs_update_layout(ino, pos, count, iomode, NULL,
+ PNFS_UPDATE_LAYOUT_NO_PNFS);
goto out;
+ }
- if (iomode == IOMODE_READ && i_size_read(ino) == 0)
+ if (iomode == IOMODE_READ && i_size_read(ino) == 0) {
+ trace_pnfs_update_layout(ino, pos, count, iomode, NULL,
+ PNFS_UPDATE_LAYOUT_RD_ZEROLEN);
goto out;
+ }
- if (pnfs_within_mdsthreshold(ctx, ino, iomode))
+ if (pnfs_within_mdsthreshold(ctx, ino, iomode)) {
+ trace_pnfs_update_layout(ino, pos, count, iomode, NULL,
+ PNFS_UPDATE_LAYOUT_MDSTHRESH);
goto out;
+ }
lookup_again:
first = false;
@@ -1530,19 +1527,25 @@ lookup_again:
lo = pnfs_find_alloc_layout(ino, ctx, gfp_flags);
if (lo == NULL) {
spin_unlock(&ino->i_lock);
+ trace_pnfs_update_layout(ino, pos, count, iomode, NULL,
+ PNFS_UPDATE_LAYOUT_NOMEM);
goto out;
}
/* Do we even need to bother with this? */
if (test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags)) {
+ trace_pnfs_update_layout(ino, pos, count, iomode, lo,
+ PNFS_UPDATE_LAYOUT_BULK_RECALL);
dprintk("%s matches recall, use MDS\n", __func__);
goto out_unlock;
}
/* if LAYOUTGET already failed once we don't try again */
- if (pnfs_layout_io_test_failed(lo, iomode) &&
- !pnfs_should_retry_layoutget(lo))
+ if (pnfs_layout_io_test_failed(lo, iomode)) {
+ trace_pnfs_update_layout(ino, pos, count, iomode, lo,
+ PNFS_UPDATE_LAYOUT_IO_TEST_FAIL);
goto out_unlock;
+ }
first = list_empty(&lo->plh_segs);
if (first) {
@@ -1562,8 +1565,11 @@ lookup_again:
* already exists
*/
lseg = pnfs_find_lseg(lo, &arg);
- if (lseg)
+ if (lseg) {
+ trace_pnfs_update_layout(ino, pos, count, iomode, lo,
+ PNFS_UPDATE_LAYOUT_FOUND_CACHED);
goto out_unlock;
+ }
}
/*
@@ -1580,11 +1586,16 @@ lookup_again:
dprintk("%s retrying\n", __func__);
goto lookup_again;
}
+ trace_pnfs_update_layout(ino, pos, count, iomode, lo,
+ PNFS_UPDATE_LAYOUT_RETURN);
goto out_put_layout_hdr;
}
- if (pnfs_layoutgets_blocked(lo))
+ if (pnfs_layoutgets_blocked(lo)) {
+ trace_pnfs_update_layout(ino, pos, count, iomode, lo,
+ PNFS_UPDATE_LAYOUT_BLOCKED);
goto out_unlock;
+ }
atomic_inc(&lo->plh_outstanding);
spin_unlock(&ino->i_lock);
@@ -1607,8 +1618,9 @@ lookup_again:
arg.length = PAGE_CACHE_ALIGN(arg.length);
lseg = send_layoutget(lo, ctx, &arg, gfp_flags);
- pnfs_clear_retry_layoutget(lo);
atomic_dec(&lo->plh_outstanding);
+ trace_pnfs_update_layout(ino, pos, count, iomode, lo,
+ PNFS_UPDATE_LAYOUT_SEND_LAYOUTGET);
out_put_layout_hdr:
if (first)
pnfs_clear_first_layoutget(lo);
@@ -1618,7 +1630,7 @@ out:
"(%s, offset: %llu, length: %llu)\n",
__func__, ino->i_sb->s_id,
(unsigned long long)NFS_FILEID(ino),
- lseg == NULL ? "not found" : "found",
+ IS_ERR_OR_NULL(lseg) ? "not found" : "found",
iomode==IOMODE_RW ? "read/write" : "read-only",
(unsigned long long)pos,
(unsigned long long)count);
@@ -1687,6 +1699,7 @@ pnfs_layout_process(struct nfs4_layoutget *lgp)
/* existing state ID, make sure the sequence number matches. */
if (pnfs_layout_stateid_blocked(lo, &res->stateid)) {
dprintk("%s forget reply due to sequence\n", __func__);
+ status = -EAGAIN;
goto out_forget_reply;
}
pnfs_set_layout_stateid(lo, &res->stateid, false);
@@ -1724,16 +1737,29 @@ out_forget_reply:
}
static void
+pnfs_set_plh_return_iomode(struct pnfs_layout_hdr *lo, enum pnfs_iomode iomode)
+{
+ if (lo->plh_return_iomode == iomode)
+ return;
+ if (lo->plh_return_iomode != 0)
+ iomode = IOMODE_ANY;
+ lo->plh_return_iomode = iomode;
+}
+
+int
pnfs_mark_matching_lsegs_return(struct pnfs_layout_hdr *lo,
struct list_head *tmp_list,
- struct pnfs_layout_range *return_range)
+ const struct pnfs_layout_range *return_range)
{
struct pnfs_layout_segment *lseg, *next;
+ int remaining = 0;
dprintk("%s:Begin lo %p\n", __func__, lo);
if (list_empty(&lo->plh_segs))
- return;
+ return 0;
+
+ assert_spin_locked(&lo->plh_inode->i_lock);
list_for_each_entry_safe(lseg, next, &lo->plh_segs, pls_list)
if (should_free_lseg(&lseg->pls_range, return_range)) {
@@ -1743,38 +1769,47 @@ pnfs_mark_matching_lsegs_return(struct pnfs_layout_hdr *lo,
lseg->pls_range.offset,
lseg->pls_range.length);
set_bit(NFS_LSEG_LAYOUTRETURN, &lseg->pls_flags);
- mark_lseg_invalid(lseg, tmp_list);
+ pnfs_set_plh_return_iomode(lo, return_range->iomode);
+ if (!mark_lseg_invalid(lseg, tmp_list))
+ remaining++;
set_bit(NFS_LAYOUT_RETURN_BEFORE_CLOSE,
&lo->plh_flags);
}
+ return remaining;
}
void pnfs_error_mark_layout_for_return(struct inode *inode,
struct pnfs_layout_segment *lseg)
{
struct pnfs_layout_hdr *lo = NFS_I(inode)->layout;
- int iomode = pnfs_iomode_to_fail_bit(lseg->pls_range.iomode);
struct pnfs_layout_range range = {
.iomode = lseg->pls_range.iomode,
.offset = 0,
.length = NFS4_MAX_UINT64,
};
LIST_HEAD(free_me);
+ bool return_now = false;
spin_lock(&inode->i_lock);
- /* set failure bit so that pnfs path will be retried later */
- pnfs_layout_set_fail_bit(lo, iomode);
- if (lo->plh_return_iomode == 0)
- lo->plh_return_iomode = range.iomode;
- else if (lo->plh_return_iomode != range.iomode)
- lo->plh_return_iomode = IOMODE_ANY;
+ pnfs_set_plh_return_iomode(lo, range.iomode);
/*
* mark all matching lsegs so that we are sure to have no live
* segments at hand when sending layoutreturn. See pnfs_put_lseg()
* for how it works.
*/
- pnfs_mark_matching_lsegs_return(lo, &free_me, &range);
- spin_unlock(&inode->i_lock);
+ if (!pnfs_mark_matching_lsegs_return(lo, &free_me, &range)) {
+ nfs4_stateid stateid;
+ enum pnfs_iomode iomode = lo->plh_return_iomode;
+
+ nfs4_stateid_copy(&stateid, &lo->plh_stateid);
+ return_now = pnfs_prepare_layoutreturn(lo);
+ spin_unlock(&inode->i_lock);
+ if (return_now)
+ pnfs_send_layoutreturn(lo, &stateid, iomode, false);
+ } else {
+ spin_unlock(&inode->i_lock);
+ nfs_commit_inode(inode, 0);
+ }
pnfs_free_lseg_list(&free_me);
}
EXPORT_SYMBOL_GPL(pnfs_error_mark_layout_for_return);
@@ -1796,6 +1831,11 @@ pnfs_generic_pg_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *r
rd_size,
IOMODE_READ,
GFP_KERNEL);
+ if (IS_ERR(pgio->pg_lseg)) {
+ pgio->pg_error = PTR_ERR(pgio->pg_lseg);
+ pgio->pg_lseg = NULL;
+ return;
+ }
}
/* If no lseg, fall back to read through mds */
if (pgio->pg_lseg == NULL)
@@ -1808,13 +1848,19 @@ void
pnfs_generic_pg_init_write(struct nfs_pageio_descriptor *pgio,
struct nfs_page *req, u64 wb_size)
{
- if (pgio->pg_lseg == NULL)
+ if (pgio->pg_lseg == NULL) {
pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode,
req->wb_context,
req_offset(req),
wb_size,
IOMODE_RW,
GFP_NOFS);
+ if (IS_ERR(pgio->pg_lseg)) {
+ pgio->pg_error = PTR_ERR(pgio->pg_lseg);
+ pgio->pg_lseg = NULL;
+ return;
+ }
+ }
/* If no lseg, fall back to write through mds */
if (pgio->pg_lseg == NULL)
nfs_pageio_reset_write_mds(pgio);
@@ -1982,15 +2028,13 @@ static void pnfs_writehdr_free(struct nfs_pgio_header *hdr)
int
pnfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc)
{
- struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc);
-
struct nfs_pgio_header *hdr;
int ret;
hdr = nfs_pgio_header_alloc(desc->pg_rw_ops);
if (!hdr) {
- desc->pg_completion_ops->error_cleanup(&mirror->pg_list);
- return -ENOMEM;
+ desc->pg_error = -ENOMEM;
+ return desc->pg_error;
}
nfs_pgheader_init(desc, hdr, pnfs_writehdr_free);
@@ -2113,15 +2157,13 @@ static void pnfs_readhdr_free(struct nfs_pgio_header *hdr)
int
pnfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc)
{
- struct nfs_pgio_mirror *mirror = nfs_pgio_current_mirror(desc);
-
struct nfs_pgio_header *hdr;
int ret;
hdr = nfs_pgio_header_alloc(desc->pg_rw_ops);
if (!hdr) {
- desc->pg_completion_ops->error_cleanup(&mirror->pg_list);
- return -ENOMEM;
+ desc->pg_error = -ENOMEM;
+ return desc->pg_error;
}
nfs_pgheader_init(desc, hdr, pnfs_readhdr_free);
hdr->lseg = pnfs_get_lseg(desc->pg_lseg);
diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h
index d1990e90e7a0..9f4e2a47f4aa 100644
--- a/fs/nfs/pnfs.h
+++ b/fs/nfs/pnfs.h
@@ -98,7 +98,6 @@ enum {
NFS_LAYOUT_RETURN_BEFORE_CLOSE, /* Return this layout before close */
NFS_LAYOUT_INVALID_STID, /* layout stateid id is invalid */
NFS_LAYOUT_FIRST_LAYOUTGET, /* Serialize first layoutget */
- NFS_LAYOUT_RETRY_LAYOUTGET, /* Retry layoutget */
};
enum layoutdriver_policy_flags {
@@ -261,11 +260,14 @@ void pnfs_set_layout_stateid(struct pnfs_layout_hdr *lo,
bool update_barrier);
int pnfs_choose_layoutget_stateid(nfs4_stateid *dst,
struct pnfs_layout_hdr *lo,
- struct pnfs_layout_range *range,
+ const struct pnfs_layout_range *range,
struct nfs4_state *open_state);
int pnfs_mark_matching_lsegs_invalid(struct pnfs_layout_hdr *lo,
struct list_head *tmp_list,
- struct pnfs_layout_range *recall_range);
+ const struct pnfs_layout_range *recall_range);
+int pnfs_mark_matching_lsegs_return(struct pnfs_layout_hdr *lo,
+ struct list_head *tmp_list,
+ const struct pnfs_layout_range *recall_range);
bool pnfs_roc(struct inode *ino);
void pnfs_roc_release(struct inode *ino);
void pnfs_roc_set_barrier(struct inode *ino, u32 barrier);
@@ -379,26 +381,6 @@ nfs4_get_deviceid(struct nfs4_deviceid_node *d)
return d;
}
-static inline void pnfs_set_retry_layoutget(struct pnfs_layout_hdr *lo)
-{
- if (!test_and_set_bit(NFS_LAYOUT_RETRY_LAYOUTGET, &lo->plh_flags))
- atomic_inc(&lo->plh_refcount);
-}
-
-static inline void pnfs_clear_retry_layoutget(struct pnfs_layout_hdr *lo)
-{
- if (test_and_clear_bit(NFS_LAYOUT_RETRY_LAYOUTGET, &lo->plh_flags)) {
- atomic_dec(&lo->plh_refcount);
- /* wake up waiters for LAYOUTRETURN as that is not needed */
- wake_up_bit(&lo->plh_flags, NFS_LAYOUT_RETURN);
- }
-}
-
-static inline bool pnfs_should_retry_layoutget(struct pnfs_layout_hdr *lo)
-{
- return test_bit(NFS_LAYOUT_RETRY_LAYOUTGET, &lo->plh_flags);
-}
-
static inline struct pnfs_layout_segment *
pnfs_get_lseg(struct pnfs_layout_segment *lseg)
{
@@ -409,6 +391,12 @@ pnfs_get_lseg(struct pnfs_layout_segment *lseg)
return lseg;
}
+static inline bool
+pnfs_is_valid_lseg(struct pnfs_layout_segment *lseg)
+{
+ return test_bit(NFS_LSEG_VALID, &lseg->pls_flags) != 0;
+}
+
/* Return true if a layout driver is being used for this mountpoint */
static inline int pnfs_enabled_sb(struct nfs_server *nfss)
{
@@ -556,6 +544,26 @@ pnfs_calc_offset_length(u64 offset, u64 end)
return 1 + end - offset;
}
+/**
+ * pnfs_mark_layout_returned_if_empty - marks the layout as returned
+ * @lo: layout header
+ *
+ * Note: Caller must hold inode->i_lock
+ */
+static inline void
+pnfs_mark_layout_returned_if_empty(struct pnfs_layout_hdr *lo)
+{
+ if (list_empty(&lo->plh_segs))
+ set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags);
+}
+
+static inline void
+pnfs_copy_range(struct pnfs_layout_range *dst,
+ const struct pnfs_layout_range *src)
+{
+ memcpy(dst, src, sizeof(*dst));
+}
+
extern unsigned int layoutstats_timer;
#ifdef NFS_DEBUG
diff --git a/fs/nfs/pnfs_nfs.c b/fs/nfs/pnfs_nfs.c
index 24655b807d44..81ac6480f9e7 100644
--- a/fs/nfs/pnfs_nfs.c
+++ b/fs/nfs/pnfs_nfs.c
@@ -266,17 +266,14 @@ pnfs_generic_commit_pagelist(struct inode *inode, struct list_head *mds_pages,
} else {
nfs_retry_commit(mds_pages, NULL, cinfo, 0);
pnfs_generic_retry_commit(cinfo, 0);
- cinfo->completion_ops->error_cleanup(NFS_I(inode));
return -ENOMEM;
}
}
nreq += pnfs_generic_alloc_ds_commits(cinfo, &list);
- if (nreq == 0) {
- cinfo->completion_ops->error_cleanup(NFS_I(inode));
+ if (nreq == 0)
goto out;
- }
atomic_add(nreq, &cinfo->mds->rpcs_out);
@@ -871,6 +868,11 @@ pnfs_layout_mark_request_commit(struct nfs_page *req,
buckets = cinfo->ds->buckets;
list = &buckets[ds_commit_idx].written;
if (list_empty(list)) {
+ if (!pnfs_is_valid_lseg(lseg)) {
+ spin_unlock(cinfo->lock);
+ cinfo->completion_ops->resched_write(cinfo, req);
+ return;
+ }
/* Non-empty buckets hold a reference on the lseg. That ref
* is normally transferred to the COMMIT call and released
* there. It could also be released if the last req is pulled
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 0a5e33f33b5c..eb31e23e7def 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -85,6 +85,23 @@ void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio)
}
EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds);
+static void nfs_readpage_release(struct nfs_page *req)
+{
+ struct inode *inode = d_inode(req->wb_context->dentry);
+
+ dprintk("NFS: read done (%s/%llu %d@%lld)\n", inode->i_sb->s_id,
+ (unsigned long long)NFS_FILEID(inode), req->wb_bytes,
+ (long long)req_offset(req));
+
+ if (nfs_page_group_sync_on_bit(req, PG_UNLOCKPAGE)) {
+ if (PageUptodate(req->wb_page))
+ nfs_readpage_to_fscache(inode, req->wb_page, 0);
+
+ unlock_page(req->wb_page);
+ }
+ nfs_release_request(req);
+}
+
int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
struct page *page)
{
@@ -106,7 +123,10 @@ int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
nfs_pageio_init_read(&pgio, inode, false,
&nfs_async_read_completion_ops);
- nfs_pageio_add_request(&pgio, new);
+ if (!nfs_pageio_add_request(&pgio, new)) {
+ nfs_list_remove_request(new);
+ nfs_readpage_release(new);
+ }
nfs_pageio_complete(&pgio);
/* It doesn't make sense to do mirrored reads! */
@@ -115,24 +135,7 @@ int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
pgm = &pgio.pg_mirrors[0];
NFS_I(inode)->read_io += pgm->pg_bytes_written;
- return 0;
-}
-
-static void nfs_readpage_release(struct nfs_page *req)
-{
- struct inode *inode = d_inode(req->wb_context->dentry);
-
- dprintk("NFS: read done (%s/%llu %d@%lld)\n", inode->i_sb->s_id,
- (unsigned long long)NFS_FILEID(inode), req->wb_bytes,
- (long long)req_offset(req));
-
- if (nfs_page_group_sync_on_bit(req, PG_UNLOCKPAGE)) {
- if (PageUptodate(req->wb_page))
- nfs_readpage_to_fscache(inode, req->wb_page, 0);
-
- unlock_page(req->wb_page);
- }
- nfs_release_request(req);
+ return pgio.pg_error < 0 ? pgio.pg_error : 0;
}
static void nfs_page_group_set_uptodate(struct nfs_page *req)
@@ -361,6 +364,8 @@ readpage_async_filler(void *data, struct page *page)
if (len < PAGE_CACHE_SIZE)
zero_user_segment(page, len, PAGE_CACHE_SIZE);
if (!nfs_pageio_add_request(desc->pgio, new)) {
+ nfs_list_remove_request(new);
+ nfs_readpage_release(new);
error = desc->pgio->pg_error;
goto out_unlock;
}
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
index b6de433da5db..4fe3eead3868 100644
--- a/fs/nfs/symlink.c
+++ b/fs/nfs/symlink.c
@@ -42,21 +42,35 @@ error:
return -EIO;
}
-static const char *nfs_follow_link(struct dentry *dentry, void **cookie)
+static const char *nfs_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
- struct inode *inode = d_inode(dentry);
struct page *page;
void *err;
- err = ERR_PTR(nfs_revalidate_mapping(inode, inode->i_mapping));
- if (err)
- return err;
- page = read_cache_page(&inode->i_data, 0,
- (filler_t *)nfs_symlink_filler, inode);
- if (IS_ERR(page))
- return ERR_CAST(page);
- *cookie = page;
- return kmap(page);
+ if (!dentry) {
+ err = ERR_PTR(nfs_revalidate_mapping_rcu(inode));
+ if (err)
+ return err;
+ page = find_get_page(inode->i_mapping, 0);
+ if (!page)
+ return ERR_PTR(-ECHILD);
+ if (!PageUptodate(page)) {
+ put_page(page);
+ return ERR_PTR(-ECHILD);
+ }
+ } else {
+ err = ERR_PTR(nfs_revalidate_mapping(inode, inode->i_mapping));
+ if (err)
+ return err;
+ page = read_cache_page(&inode->i_data, 0,
+ (filler_t *)nfs_symlink_filler, inode);
+ if (IS_ERR(page))
+ return ERR_CAST(page);
+ }
+ set_delayed_call(done, page_put_link, page);
+ return page_address(page);
}
/*
@@ -64,8 +78,7 @@ static const char *nfs_follow_link(struct dentry *dentry, void **cookie)
*/
const struct inode_operations nfs_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = nfs_follow_link,
- .put_link = page_put_link,
+ .get_link = nfs_get_link,
.getattr = nfs_getattr,
.setattr = nfs_setattr,
};
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 7b9316406930..ce43cd6d88c6 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -21,6 +21,8 @@
#include <linux/nfs_page.h>
#include <linux/backing-dev.h>
#include <linux/export.h>
+#include <linux/freezer.h>
+#include <linux/wait.h>
#include <asm/uaccess.h>
@@ -244,11 +246,9 @@ static int wb_priority(struct writeback_control *wbc)
{
int ret = 0;
if (wbc->for_reclaim)
- return FLUSH_HIGHPRI | FLUSH_STABLE;
+ return FLUSH_HIGHPRI | FLUSH_COND_STABLE;
if (wbc->sync_mode == WB_SYNC_ALL)
ret = FLUSH_COND_STABLE;
- if (wbc->for_kupdate || wbc->for_background)
- ret |= FLUSH_LOWPRI;
return ret;
}
@@ -545,12 +545,22 @@ try_again:
return head;
}
+static void nfs_write_error_remove_page(struct nfs_page *req)
+{
+ nfs_unlock_request(req);
+ nfs_end_page_writeback(req);
+ nfs_release_request(req);
+ generic_error_remove_page(page_file_mapping(req->wb_page),
+ req->wb_page);
+}
+
/*
* Find an associated nfs write request, and prepare to flush it out
* May return an error if the user signalled nfs_wait_on_request().
*/
static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
- struct page *page, bool nonblock)
+ struct page *page, bool nonblock,
+ bool launder)
{
struct nfs_page *req;
int ret = 0;
@@ -567,8 +577,21 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
ret = 0;
if (!nfs_pageio_add_request(pgio, req)) {
- nfs_redirty_request(req);
ret = pgio->pg_error;
+ /*
+ * Remove the problematic req upon fatal errors
+ * in launder case, while other dirty pages can
+ * still be around until they get flushed.
+ */
+ if (nfs_error_is_fatal(ret)) {
+ nfs_context_set_write_error(req->wb_context, ret);
+ if (launder) {
+ nfs_write_error_remove_page(req);
+ goto out;
+ }
+ }
+ nfs_redirty_request(req);
+ ret = -EAGAIN;
} else
nfs_add_stats(page_file_mapping(page)->host,
NFSIOS_WRITEPAGES, 1);
@@ -576,12 +599,14 @@ out:
return ret;
}
-static int nfs_do_writepage(struct page *page, struct writeback_control *wbc, struct nfs_pageio_descriptor *pgio)
+static int nfs_do_writepage(struct page *page, struct writeback_control *wbc,
+ struct nfs_pageio_descriptor *pgio, bool launder)
{
int ret;
nfs_pageio_cond_complete(pgio, page_file_index(page));
- ret = nfs_page_async_flush(pgio, page, wbc->sync_mode == WB_SYNC_NONE);
+ ret = nfs_page_async_flush(pgio, page, wbc->sync_mode == WB_SYNC_NONE,
+ launder);
if (ret == -EAGAIN) {
redirty_page_for_writepage(wbc, page);
ret = 0;
@@ -592,7 +617,9 @@ static int nfs_do_writepage(struct page *page, struct writeback_control *wbc, st
/*
* Write an mmapped page to the server.
*/
-static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc)
+static int nfs_writepage_locked(struct page *page,
+ struct writeback_control *wbc,
+ bool launder)
{
struct nfs_pageio_descriptor pgio;
struct inode *inode = page_file_mapping(page)->host;
@@ -601,7 +628,7 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc
nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGE);
nfs_pageio_init_write(&pgio, inode, wb_priority(wbc),
false, &nfs_async_write_completion_ops);
- err = nfs_do_writepage(page, wbc, &pgio);
+ err = nfs_do_writepage(page, wbc, &pgio, launder);
nfs_pageio_complete(&pgio);
if (err < 0)
return err;
@@ -614,7 +641,7 @@ int nfs_writepage(struct page *page, struct writeback_control *wbc)
{
int ret;
- ret = nfs_writepage_locked(page, wbc);
+ ret = nfs_writepage_locked(page, wbc, false);
unlock_page(page);
return ret;
}
@@ -623,7 +650,7 @@ static int nfs_writepages_callback(struct page *page, struct writeback_control *
{
int ret;
- ret = nfs_do_writepage(page, wbc, data);
+ ret = nfs_do_writepage(page, wbc, data, false);
unlock_page(page);
return ret;
}
@@ -1128,7 +1155,8 @@ int nfs_flush_incompatible(struct file *file, struct page *page)
if (req == NULL)
return 0;
l_ctx = req->wb_lock_context;
- do_flush = req->wb_page != page || req->wb_context != ctx;
+ do_flush = req->wb_page != page ||
+ !nfs_match_open_context(req->wb_context, ctx);
/* for now, flush if more than 1 request in page_group */
do_flush |= req->wb_this_page != req;
if (l_ctx && flctx &&
@@ -1326,9 +1354,15 @@ static void nfs_async_write_error(struct list_head *head)
}
}
+static void nfs_async_write_reschedule_io(struct nfs_pgio_header *hdr)
+{
+ nfs_async_write_error(&hdr->pages);
+}
+
static const struct nfs_pgio_completion_ops nfs_async_write_completion_ops = {
.error_cleanup = nfs_async_write_error,
.completion = nfs_write_completion,
+ .reschedule_io = nfs_async_write_reschedule_io,
};
void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
@@ -1529,27 +1563,21 @@ static void nfs_writeback_result(struct rpc_task *task,
}
}
-
-static int nfs_commit_set_lock(struct nfs_inode *nfsi, int may_wait)
+static int wait_on_commit(struct nfs_mds_commit_info *cinfo)
{
- int ret;
+ return wait_on_atomic_t(&cinfo->rpcs_out,
+ nfs_wait_atomic_killable, TASK_KILLABLE);
+}
- if (!test_and_set_bit(NFS_INO_COMMIT, &nfsi->flags))
- return 1;
- if (!may_wait)
- return 0;
- ret = out_of_line_wait_on_bit_lock(&nfsi->flags,
- NFS_INO_COMMIT,
- nfs_wait_bit_killable,
- TASK_KILLABLE);
- return (ret < 0) ? ret : 1;
+static void nfs_commit_begin(struct nfs_mds_commit_info *cinfo)
+{
+ atomic_inc(&cinfo->rpcs_out);
}
-static void nfs_commit_clear_lock(struct nfs_inode *nfsi)
+static void nfs_commit_end(struct nfs_mds_commit_info *cinfo)
{
- clear_bit(NFS_INO_COMMIT, &nfsi->flags);
- smp_mb__after_atomic();
- wake_up_bit(&nfsi->flags, NFS_INO_COMMIT);
+ if (atomic_dec_and_test(&cinfo->rpcs_out))
+ wake_up_atomic_t(&cinfo->rpcs_out);
}
void nfs_commitdata_release(struct nfs_commit_data *data)
@@ -1666,6 +1694,13 @@ void nfs_retry_commit(struct list_head *page_list,
}
EXPORT_SYMBOL_GPL(nfs_retry_commit);
+static void
+nfs_commit_resched_write(struct nfs_commit_info *cinfo,
+ struct nfs_page *req)
+{
+ __set_page_dirty_nobuffers(req->wb_page);
+}
+
/*
* Commit dirty pages
*/
@@ -1687,7 +1722,6 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how,
data->mds_ops, how, 0);
out_bad:
nfs_retry_commit(head, NULL, cinfo, 0);
- cinfo->completion_ops->error_cleanup(NFS_I(inode));
return -ENOMEM;
}
@@ -1749,8 +1783,7 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data)
clear_bdi_congested(&nfss->backing_dev_info, BLK_RW_ASYNC);
nfs_init_cinfo(&cinfo, data->inode, data->dreq);
- if (atomic_dec_and_test(&cinfo.mds->rpcs_out))
- nfs_commit_clear_lock(NFS_I(data->inode));
+ nfs_commit_end(cinfo.mds);
}
static void nfs_commit_release(void *calldata)
@@ -1769,7 +1802,7 @@ static const struct rpc_call_ops nfs_commit_ops = {
static const struct nfs_commit_completion_ops nfs_commit_completion_ops = {
.completion = nfs_commit_release_pages,
- .error_cleanup = nfs_commit_clear_lock,
+ .resched_write = nfs_commit_resched_write,
};
int nfs_generic_commit_list(struct inode *inode, struct list_head *head,
@@ -1788,30 +1821,25 @@ int nfs_commit_inode(struct inode *inode, int how)
LIST_HEAD(head);
struct nfs_commit_info cinfo;
int may_wait = how & FLUSH_SYNC;
+ int error = 0;
int res;
- res = nfs_commit_set_lock(NFS_I(inode), may_wait);
- if (res <= 0)
- goto out_mark_dirty;
nfs_init_cinfo_from_inode(&cinfo, inode);
+ nfs_commit_begin(cinfo.mds);
res = nfs_scan_commit(inode, &head, &cinfo);
- if (res) {
- int error;
-
+ if (res)
error = nfs_generic_commit_list(inode, &head, how, &cinfo);
- if (error < 0)
- return error;
- if (!may_wait)
- goto out_mark_dirty;
- error = wait_on_bit_action(&NFS_I(inode)->flags,
- NFS_INO_COMMIT,
- nfs_wait_bit_killable,
- TASK_KILLABLE);
- if (error < 0)
- return error;
- } else
- nfs_commit_clear_lock(NFS_I(inode));
+ nfs_commit_end(cinfo.mds);
+ if (error < 0)
+ goto out_error;
+ if (!may_wait)
+ goto out_mark_dirty;
+ error = wait_on_commit(cinfo.mds);
+ if (error < 0)
+ return error;
return res;
+out_error:
+ res = error;
/* Note: If we exit without ensuring that the commit is complete,
* we must mark the inode as dirty. Otherwise, future calls to
* sync_inode() with the WB_SYNC_ALL flag set will fail to ensure
@@ -1821,6 +1849,7 @@ out_mark_dirty:
__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
return res;
}
+EXPORT_SYMBOL_GPL(nfs_commit_inode);
int nfs_write_inode(struct inode *inode, struct writeback_control *wbc)
{
@@ -1911,7 +1940,7 @@ int nfs_wb_page_cancel(struct inode *inode, struct page *page)
/*
* Write back all requests on one page - we do this before reading it.
*/
-int nfs_wb_page(struct inode *inode, struct page *page)
+int nfs_wb_single_page(struct inode *inode, struct page *page, bool launder)
{
loff_t range_start = page_file_offset(page);
loff_t range_end = range_start + (loff_t)(PAGE_CACHE_SIZE - 1);
@@ -1928,7 +1957,7 @@ int nfs_wb_page(struct inode *inode, struct page *page)
for (;;) {
wait_on_page_writeback(page);
if (clear_page_dirty_for_io(page)) {
- ret = nfs_writepage_locked(page, &wbc);
+ ret = nfs_writepage_locked(page, &wbc, launder);
if (ret < 0)
goto out_error;
continue;
diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c
index 77e7a5cca888..1a03bc3059e8 100644
--- a/fs/nfsd/lockd.c
+++ b/fs/nfsd/lockd.c
@@ -58,7 +58,7 @@ nlm_fclose(struct file *filp)
fput(filp);
}
-static struct nlmsvc_binding nfsd_nlm_ops = {
+static const struct nlmsvc_binding nfsd_nlm_ops = {
.fopen = nlm_fopen, /* open file for locking */
.fclose = nlm_fclose, /* close file */
};
diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
index d8b16c2568f3..5fbf3bbd00d0 100644
--- a/fs/nfsd/netns.h
+++ b/fs/nfsd/netns.h
@@ -92,7 +92,7 @@ struct nfsd_net {
struct file *rec_file;
bool in_grace;
- struct nfsd4_client_tracking_ops *client_tracking_ops;
+ const struct nfsd4_client_tracking_ops *client_tracking_ops;
time_t nfsd4_lease;
time_t nfsd4_grace;
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c
index 00575d776d91..2246454dec76 100644
--- a/fs/nfsd/nfs3xdr.c
+++ b/fs/nfsd/nfs3xdr.c
@@ -823,7 +823,7 @@ compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp,
} else
dchild = dget(dparent);
} else
- dchild = lookup_one_len(name, dparent, namlen);
+ dchild = lookup_one_len_unlocked(name, dparent, namlen);
if (IS_ERR(dchild))
return rv;
if (d_mountpoint(dchild))
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index e7f50c4081d6..7389cb1d7409 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -792,12 +792,16 @@ static void warn_no_callback_path(struct nfs4_client *clp, int reason)
static void nfsd4_mark_cb_down(struct nfs4_client *clp, int reason)
{
+ if (test_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_flags))
+ return;
clp->cl_cb_state = NFSD4_CB_DOWN;
warn_no_callback_path(clp, reason);
}
static void nfsd4_mark_cb_fault(struct nfs4_client *clp, int reason)
{
+ if (test_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_flags))
+ return;
clp->cl_cb_state = NFSD4_CB_FAULT;
warn_no_callback_path(clp, reason);
}
@@ -1143,7 +1147,7 @@ nfsd4_run_cb_work(struct work_struct *work)
}
void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,
- struct nfsd4_callback_ops *ops, enum nfsd4_cb_op op)
+ const struct nfsd4_callback_ops *ops, enum nfsd4_cb_op op)
{
cb->cb_clp = clp;
cb->cb_msg.rpc_proc = &nfs4_cb_procedures[op];
diff --git a/fs/nfsd/nfs4layouts.c b/fs/nfsd/nfs4layouts.c
index 9ffef06b30d5..ce2d010d3b17 100644
--- a/fs/nfsd/nfs4layouts.c
+++ b/fs/nfsd/nfs4layouts.c
@@ -22,7 +22,7 @@ struct nfs4_layout {
static struct kmem_cache *nfs4_layout_cache;
static struct kmem_cache *nfs4_layout_stateid_cache;
-static struct nfsd4_callback_ops nfsd4_cb_layout_ops;
+static const struct nfsd4_callback_ops nfsd4_cb_layout_ops;
static const struct lock_manager_operations nfsd4_layouts_lm_ops;
const struct nfsd4_layout_ops *nfsd4_layout_ops[LAYOUT_TYPE_MAX] = {
@@ -616,6 +616,7 @@ nfsd4_cb_layout_prepare(struct nfsd4_callback *cb)
mutex_lock(&ls->ls_mutex);
nfs4_inc_and_copy_stateid(&ls->ls_recall_sid, &ls->ls_stid);
+ mutex_unlock(&ls->ls_mutex);
}
static int
@@ -623,24 +624,39 @@ nfsd4_cb_layout_done(struct nfsd4_callback *cb, struct rpc_task *task)
{
struct nfs4_layout_stateid *ls =
container_of(cb, struct nfs4_layout_stateid, ls_recall);
+ struct nfsd_net *nn;
+ ktime_t now, cutoff;
LIST_HEAD(reaplist);
+
switch (task->tk_status) {
case 0:
- return 1;
+ case -NFS4ERR_DELAY:
+ /*
+ * Anything left? If not, then call it done. Note that we don't
+ * take the spinlock since this is an optimization and nothing
+ * should get added until the cb counter goes to zero.
+ */
+ if (list_empty(&ls->ls_layouts))
+ return 1;
+
+ /* Poll the client until it's done with the layout */
+ now = ktime_get();
+ nn = net_generic(ls->ls_stid.sc_client->net, nfsd_net_id);
+
+ /* Client gets 2 lease periods to return it */
+ cutoff = ktime_add_ns(task->tk_start,
+ nn->nfsd4_lease * NSEC_PER_SEC * 2);
+
+ if (ktime_before(now, cutoff)) {
+ rpc_delay(task, HZ/100); /* 10 mili-seconds */
+ return 0;
+ }
+ /* Fallthrough */
case -NFS4ERR_NOMATCHING_LAYOUT:
trace_layout_recall_done(&ls->ls_stid.sc_stateid);
task->tk_status = 0;
return 1;
- case -NFS4ERR_DELAY:
- /* Poll the client until it's done with the layout */
- /* FIXME: cap number of retries.
- * The pnfs standard states that we need to only expire
- * the client after at-least "lease time" .eg lease-time * 2
- * when failing to communicate a recall
- */
- rpc_delay(task, HZ/100); /* 10 mili-seconds */
- return 0;
default:
/*
* Unknown error or non-responding client, we'll need to fence.
@@ -659,13 +675,12 @@ nfsd4_cb_layout_release(struct nfsd4_callback *cb)
trace_layout_recall_release(&ls->ls_stid.sc_stateid);
- mutex_unlock(&ls->ls_mutex);
nfsd4_return_all_layouts(ls, &reaplist);
nfsd4_free_layouts(&reaplist);
nfs4_put_stid(&ls->ls_stid);
}
-static struct nfsd4_callback_ops nfsd4_cb_layout_ops = {
+static const struct nfsd4_callback_ops nfsd4_cb_layout_ops = {
.prepare = nfsd4_cb_layout_prepare,
.done = nfsd4_cb_layout_done,
.release = nfsd4_cb_layout_release,
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index a9f096c7e99f..819ad812c71b 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -774,8 +774,9 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
/* check stateid */
- status = nfs4_preprocess_stateid_op(rqstp, cstate, &read->rd_stateid,
- RD_STATE, &read->rd_filp, &read->rd_tmp_file);
+ status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
+ &read->rd_stateid, RD_STATE,
+ &read->rd_filp, &read->rd_tmp_file);
if (status) {
dprintk("NFSD: nfsd4_read: couldn't process stateid!\n");
goto out;
@@ -921,7 +922,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (setattr->sa_iattr.ia_valid & ATTR_SIZE) {
status = nfs4_preprocess_stateid_op(rqstp, cstate,
- &setattr->sa_stateid, WR_STATE, NULL, NULL);
+ &cstate->current_fh, &setattr->sa_stateid,
+ WR_STATE, NULL, NULL);
if (status) {
dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n");
return status;
@@ -985,8 +987,8 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (write->wr_offset >= OFFSET_MAX)
return nfserr_inval;
- status = nfs4_preprocess_stateid_op(rqstp, cstate, stateid, WR_STATE,
- &filp, NULL);
+ status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
+ stateid, WR_STATE, &filp, NULL);
if (status) {
dprintk("NFSD: nfsd4_write: couldn't process stateid!\n");
return status;
@@ -1010,13 +1012,54 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
}
static __be32
+nfsd4_clone(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+ struct nfsd4_clone *clone)
+{
+ struct file *src, *dst;
+ __be32 status;
+
+ status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->save_fh,
+ &clone->cl_src_stateid, RD_STATE,
+ &src, NULL);
+ if (status) {
+ dprintk("NFSD: %s: couldn't process src stateid!\n", __func__);
+ goto out;
+ }
+
+ status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
+ &clone->cl_dst_stateid, WR_STATE,
+ &dst, NULL);
+ if (status) {
+ dprintk("NFSD: %s: couldn't process dst stateid!\n", __func__);
+ goto out_put_src;
+ }
+
+ /* fix up for NFS-specific error code */
+ if (!S_ISREG(file_inode(src)->i_mode) ||
+ !S_ISREG(file_inode(dst)->i_mode)) {
+ status = nfserr_wrong_type;
+ goto out_put_dst;
+ }
+
+ status = nfsd4_clone_file_range(src, clone->cl_src_pos,
+ dst, clone->cl_dst_pos, clone->cl_count);
+
+out_put_dst:
+ fput(dst);
+out_put_src:
+ fput(src);
+out:
+ return status;
+}
+
+static __be32
nfsd4_fallocate(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_fallocate *fallocate, int flags)
{
__be32 status = nfserr_notsupp;
struct file *file;
- status = nfs4_preprocess_stateid_op(rqstp, cstate,
+ status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
&fallocate->falloc_stateid,
WR_STATE, &file, NULL);
if (status != nfs_ok) {
@@ -1055,7 +1098,7 @@ nfsd4_seek(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
__be32 status;
struct file *file;
- status = nfs4_preprocess_stateid_op(rqstp, cstate,
+ status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
&seek->seek_stateid,
RD_STATE, &file, NULL);
if (status) {
@@ -2279,6 +2322,12 @@ static struct nfsd4_operation nfsd4_ops[] = {
.op_name = "OP_DEALLOCATE",
.op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
},
+ [OP_CLONE] = {
+ .op_func = (nfsd4op_func)nfsd4_clone,
+ .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME,
+ .op_name = "OP_CLONE",
+ .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
+ },
[OP_SEEK] = {
.op_func = (nfsd4op_func)nfsd4_seek,
.op_name = "OP_SEEK",
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
index e3d47091b191..79f0307a5ec8 100644
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -631,7 +631,7 @@ nfsd4_check_legacy_client(struct nfs4_client *clp)
return -ENOENT;
}
-static struct nfsd4_client_tracking_ops nfsd4_legacy_tracking_ops = {
+static const struct nfsd4_client_tracking_ops nfsd4_legacy_tracking_ops = {
.init = nfsd4_legacy_tracking_init,
.exit = nfsd4_legacy_tracking_exit,
.create = nfsd4_create_clid_dir,
@@ -1050,7 +1050,7 @@ out_err:
printk(KERN_ERR "NFSD: Unable to end grace period: %d\n", ret);
}
-static struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops = {
+static const struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops = {
.init = nfsd4_init_cld_pipe,
.exit = nfsd4_remove_cld_pipe,
.create = nfsd4_cld_create,
@@ -1394,7 +1394,7 @@ nfsd4_umh_cltrack_grace_done(struct nfsd_net *nn)
kfree(legacy);
}
-static struct nfsd4_client_tracking_ops nfsd4_umh_tracking_ops = {
+static const struct nfsd4_client_tracking_ops nfsd4_umh_tracking_ops = {
.init = nfsd4_umh_cltrack_init,
.exit = NULL,
.create = nfsd4_umh_cltrack_create,
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 6b800b5b8fed..c484a2b6cd10 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -98,7 +98,7 @@ static struct kmem_cache *odstate_slab;
static void free_session(struct nfsd4_session *);
-static struct nfsd4_callback_ops nfsd4_cb_recall_ops;
+static const struct nfsd4_callback_ops nfsd4_cb_recall_ops;
static bool is_session_dead(struct nfsd4_session *ses)
{
@@ -1857,15 +1857,28 @@ static void copy_clid(struct nfs4_client *target, struct nfs4_client *source)
target->cl_clientid.cl_id = source->cl_clientid.cl_id;
}
-static int copy_cred(struct svc_cred *target, struct svc_cred *source)
+int strdup_if_nonnull(char **target, char *source)
{
- if (source->cr_principal) {
- target->cr_principal =
- kstrdup(source->cr_principal, GFP_KERNEL);
- if (target->cr_principal == NULL)
+ if (source) {
+ *target = kstrdup(source, GFP_KERNEL);
+ if (!*target)
return -ENOMEM;
} else
- target->cr_principal = NULL;
+ *target = NULL;
+ return 0;
+}
+
+static int copy_cred(struct svc_cred *target, struct svc_cred *source)
+{
+ int ret;
+
+ ret = strdup_if_nonnull(&target->cr_principal, source->cr_principal);
+ if (ret)
+ return ret;
+ ret = strdup_if_nonnull(&target->cr_raw_principal,
+ source->cr_raw_principal);
+ if (ret)
+ return ret;
target->cr_flavor = source->cr_flavor;
target->cr_uid = source->cr_uid;
target->cr_gid = source->cr_gid;
@@ -1969,6 +1982,9 @@ static bool mach_creds_match(struct nfs4_client *cl, struct svc_rqst *rqstp)
return false;
if (!svc_rqst_integrity_protected(rqstp))
return false;
+ if (cl->cl_cred.cr_raw_principal)
+ return 0 == strcmp(cl->cl_cred.cr_raw_principal,
+ cr->cr_raw_principal);
if (!cr->cr_principal)
return false;
return 0 == strcmp(cl->cl_cred.cr_principal, cr->cr_principal);
@@ -2240,7 +2256,8 @@ nfsd4_store_cache_entry(struct nfsd4_compoundres *resp)
base = resp->cstate.data_offset;
slot->sl_datalen = buf->len - base;
if (read_bytes_from_xdr_buf(buf, base, slot->sl_data, slot->sl_datalen))
- WARN("%s: sessions DRC could not cache compound\n", __func__);
+ WARN(1, "%s: sessions DRC could not cache compound\n",
+ __func__);
return;
}
@@ -2365,10 +2382,27 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
if (exid->flags & ~EXCHGID4_FLAG_MASK_A)
return nfserr_inval;
+ new = create_client(exid->clname, rqstp, &verf);
+ if (new == NULL)
+ return nfserr_jukebox;
+
switch (exid->spa_how) {
case SP4_MACH_CRED:
- if (!svc_rqst_integrity_protected(rqstp))
- return nfserr_inval;
+ if (!svc_rqst_integrity_protected(rqstp)) {
+ status = nfserr_inval;
+ goto out_nolock;
+ }
+ /*
+ * Sometimes userspace doesn't give us a principal.
+ * Which is a bug, really. Anyway, we can't enforce
+ * MACH_CRED in that case, better to give up now:
+ */
+ if (!new->cl_cred.cr_principal &&
+ !new->cl_cred.cr_raw_principal) {
+ status = nfserr_serverfault;
+ goto out_nolock;
+ }
+ new->cl_mach_cred = true;
case SP4_NONE:
break;
default: /* checked by xdr code */
@@ -2377,10 +2411,6 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
return nfserr_encr_alg_unsupp;
}
- new = create_client(exid->clname, rqstp, &verf);
- if (new == NULL)
- return nfserr_jukebox;
-
/* Cases below refer to rfc 5661 section 18.35.4: */
spin_lock(&nn->client_lock);
conf = find_confirmed_client_by_name(&exid->clname, nn);
@@ -2442,7 +2472,6 @@ out_new:
goto out;
}
new->cl_minorversion = cstate->minorversion;
- new->cl_mach_cred = (exid->spa_how == SP4_MACH_CRED);
gen_clid(new, nn);
add_to_unconfirmed(new);
@@ -2460,6 +2489,7 @@ out_copy:
out:
spin_unlock(&nn->client_lock);
+out_nolock:
if (new)
expire_client(new);
if (unconf)
@@ -3648,7 +3678,7 @@ static void nfsd4_cb_recall_release(struct nfsd4_callback *cb)
nfs4_put_stid(&dp->dl_stid);
}
-static struct nfsd4_callback_ops nfsd4_cb_recall_ops = {
+static const struct nfsd4_callback_ops nfsd4_cb_recall_ops = {
.prepare = nfsd4_cb_recall_prepare,
.done = nfsd4_cb_recall_done,
.release = nfsd4_cb_recall_release,
@@ -4541,8 +4571,7 @@ static void
laundromat_main(struct work_struct *laundry)
{
time_t t;
- struct delayed_work *dwork = container_of(laundry, struct delayed_work,
- work);
+ struct delayed_work *dwork = to_delayed_work(laundry);
struct nfsd_net *nn = container_of(dwork, struct nfsd_net,
laundromat_work);
@@ -4797,10 +4826,9 @@ nfs4_check_file(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfs4_stid *s,
*/
__be32
nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
- struct nfsd4_compound_state *cstate, stateid_t *stateid,
- int flags, struct file **filpp, bool *tmp_file)
+ struct nfsd4_compound_state *cstate, struct svc_fh *fhp,
+ stateid_t *stateid, int flags, struct file **filpp, bool *tmp_file)
{
- struct svc_fh *fhp = &cstate->current_fh;
struct inode *ino = d_inode(fhp->fh_dentry);
struct net *net = SVC_NET(rqstp);
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 51c9e9ca39a4..d6ef0955a979 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -1675,6 +1675,25 @@ nfsd4_decode_fallocate(struct nfsd4_compoundargs *argp,
}
static __be32
+nfsd4_decode_clone(struct nfsd4_compoundargs *argp, struct nfsd4_clone *clone)
+{
+ DECODE_HEAD;
+
+ status = nfsd4_decode_stateid(argp, &clone->cl_src_stateid);
+ if (status)
+ return status;
+ status = nfsd4_decode_stateid(argp, &clone->cl_dst_stateid);
+ if (status)
+ return status;
+
+ READ_BUF(8 + 8 + 8);
+ p = xdr_decode_hyper(p, &clone->cl_src_pos);
+ p = xdr_decode_hyper(p, &clone->cl_dst_pos);
+ p = xdr_decode_hyper(p, &clone->cl_count);
+ DECODE_TAIL;
+}
+
+static __be32
nfsd4_decode_seek(struct nfsd4_compoundargs *argp, struct nfsd4_seek *seek)
{
DECODE_HEAD;
@@ -1785,6 +1804,7 @@ static nfsd4_dec nfsd4_dec_ops[] = {
[OP_READ_PLUS] = (nfsd4_dec)nfsd4_decode_notsupp,
[OP_SEEK] = (nfsd4_dec)nfsd4_decode_seek,
[OP_WRITE_SAME] = (nfsd4_dec)nfsd4_decode_notsupp,
+ [OP_CLONE] = (nfsd4_dec)nfsd4_decode_clone,
};
static inline bool
@@ -2838,14 +2858,14 @@ nfsd4_encode_dirent_fattr(struct xdr_stream *xdr, struct nfsd4_readdir *cd,
__be32 nfserr;
int ignore_crossmnt = 0;
- dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen);
+ dentry = lookup_one_len_unlocked(name, cd->rd_fhp->fh_dentry, namlen);
if (IS_ERR(dentry))
return nfserrno(PTR_ERR(dentry));
if (d_really_is_negative(dentry)) {
/*
- * nfsd_buffered_readdir drops the i_mutex between
- * readdir and calling this callback, leaving a window
- * where this directory entry could have gone away.
+ * we're not holding the i_mutex here, so there's
+ * a window where this directory entry could have gone
+ * away.
*/
dput(dentry);
return nfserr_noent;
@@ -4292,6 +4312,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
[OP_READ_PLUS] = (nfsd4_enc)nfsd4_encode_noop,
[OP_SEEK] = (nfsd4_enc)nfsd4_encode_seek,
[OP_WRITE_SAME] = (nfsd4_enc)nfsd4_encode_noop,
+ [OP_CLONE] = (nfsd4_enc)nfsd4_encode_noop,
};
/*
diff --git a/fs/nfsd/nfsfh.h b/fs/nfsd/nfsfh.h
index 2087bae17582..0770bcb543c8 100644
--- a/fs/nfsd/nfsfh.h
+++ b/fs/nfsd/nfsfh.h
@@ -7,6 +7,7 @@
#ifndef _LINUX_NFSD_NFSFH_H
#define _LINUX_NFSD_NFSFH_H
+#include <linux/crc32.h>
#include <linux/sunrpc/svc.h>
#include <uapi/linux/nfsd/nfsfh.h>
@@ -205,6 +206,28 @@ static inline bool fh_fsid_match(struct knfsd_fh *fh1, struct knfsd_fh *fh2)
return true;
}
+#ifdef CONFIG_CRC32
+/**
+ * knfsd_fh_hash - calculate the crc32 hash for the filehandle
+ * @fh - pointer to filehandle
+ *
+ * returns a crc32 hash for the filehandle that is compatible with
+ * the one displayed by "wireshark".
+ */
+
+static inline u32
+knfsd_fh_hash(struct knfsd_fh *fh)
+{
+ return ~crc32_le(0xFFFFFFFF, (unsigned char *)&fh->fh_base, fh->fh_size);
+}
+#else
+static inline u32
+knfsd_fh_hash(struct knfsd_fh *fh)
+{
+ return 0;
+}
+#endif
+
#ifdef CONFIG_NFSD_V3
/*
* The wcc data stored in current_fh should be cleared
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index ad4e2377dd63..45007acaf364 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -14,9 +14,13 @@
#include <linux/sunrpc/stats.h>
#include <linux/sunrpc/svcsock.h>
+#include <linux/sunrpc/svc_xprt.h>
#include <linux/lockd/bind.h>
#include <linux/nfsacl.h>
#include <linux/seq_file.h>
+#include <linux/inetdevice.h>
+#include <net/addrconf.h>
+#include <net/ipv6.h>
#include <net/net_namespace.h>
#include "nfsd.h"
#include "cache.h"
@@ -306,22 +310,81 @@ static void nfsd_shutdown_net(struct net *net)
nfsd_shutdown_generic();
}
+static int nfsd_inetaddr_event(struct notifier_block *this, unsigned long event,
+ void *ptr)
+{
+ struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
+ struct net_device *dev = ifa->ifa_dev->dev;
+ struct net *net = dev_net(dev);
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+ struct sockaddr_in sin;
+
+ if (event != NETDEV_DOWN)
+ goto out;
+
+ if (nn->nfsd_serv) {
+ dprintk("nfsd_inetaddr_event: removed %pI4\n", &ifa->ifa_local);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = ifa->ifa_local;
+ svc_age_temp_xprts_now(nn->nfsd_serv, (struct sockaddr *)&sin);
+ }
+
+out:
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block nfsd_inetaddr_notifier = {
+ .notifier_call = nfsd_inetaddr_event,
+};
+
+#if IS_ENABLED(CONFIG_IPV6)
+static int nfsd_inet6addr_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
+ struct net_device *dev = ifa->idev->dev;
+ struct net *net = dev_net(dev);
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+ struct sockaddr_in6 sin6;
+
+ if (event != NETDEV_DOWN)
+ goto out;
+
+ if (nn->nfsd_serv) {
+ dprintk("nfsd_inet6addr_event: removed %pI6\n", &ifa->addr);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = ifa->addr;
+ svc_age_temp_xprts_now(nn->nfsd_serv, (struct sockaddr *)&sin6);
+ }
+
+out:
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block nfsd_inet6addr_notifier = {
+ .notifier_call = nfsd_inet6addr_event,
+};
+#endif
+
static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+ unregister_inetaddr_notifier(&nfsd_inetaddr_notifier);
+#if IS_ENABLED(CONFIG_IPV6)
+ unregister_inet6addr_notifier(&nfsd_inet6addr_notifier);
+#endif
/*
* write_ports can create the server without actually starting
* any threads--if we get shut down before any threads are
* started, then nfsd_last_thread will be run before any of this
- * other initialization has been done.
+ * other initialization has been done except the rpcb information.
*/
+ svc_rpcb_cleanup(serv, net);
if (!nn->nfsd_net_up)
return;
- nfsd_shutdown_net(net);
-
- svc_rpcb_cleanup(serv, net);
+ nfsd_shutdown_net(net);
printk(KERN_WARNING "nfsd: last server has exited, flushing export "
"cache\n");
nfsd_export_flush(net);
@@ -425,6 +488,10 @@ int nfsd_create_serv(struct net *net)
}
set_max_drc();
+ register_inetaddr_notifier(&nfsd_inetaddr_notifier);
+#if IS_ENABLED(CONFIG_IPV6)
+ register_inet6addr_notifier(&nfsd_inet6addr_notifier);
+#endif
do_gettimeofday(&nn->nfssvc_boot); /* record boot time */
return 0;
}
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 77fdf4de91ba..c050c53036a6 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -65,7 +65,7 @@ struct nfsd4_callback {
struct nfs4_client *cb_clp;
u32 cb_minorversion;
struct rpc_message cb_msg;
- struct nfsd4_callback_ops *cb_ops;
+ const struct nfsd4_callback_ops *cb_ops;
struct work_struct cb_work;
int cb_seq_status;
int cb_status;
@@ -578,8 +578,8 @@ struct nfsd4_compound_state;
struct nfsd_net;
extern __be32 nfs4_preprocess_stateid_op(struct svc_rqst *rqstp,
- struct nfsd4_compound_state *cstate, stateid_t *stateid,
- int flags, struct file **filp, bool *tmp_file);
+ struct nfsd4_compound_state *cstate, struct svc_fh *fhp,
+ stateid_t *stateid, int flags, struct file **filp, bool *tmp_file);
__be32 nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
stateid_t *stateid, unsigned char typemask,
struct nfs4_stid **s, struct nfsd_net *nn);
@@ -599,7 +599,7 @@ extern void nfsd4_probe_callback(struct nfs4_client *clp);
extern void nfsd4_probe_callback_sync(struct nfs4_client *clp);
extern void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *);
extern void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,
- struct nfsd4_callback_ops *ops, enum nfsd4_cb_op op);
+ const struct nfsd4_callback_ops *ops, enum nfsd4_cb_op op);
extern void nfsd4_run_cb(struct nfsd4_callback *cb);
extern int nfsd4_create_callback_queue(void);
extern void nfsd4_destroy_callback_queue(void);
diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h
index 0befe762762b..3287041905da 100644
--- a/fs/nfsd/trace.h
+++ b/fs/nfsd/trace.h
@@ -8,6 +8,47 @@
#define _NFSD_TRACE_H
#include <linux/tracepoint.h>
+#include "nfsfh.h"
+
+DECLARE_EVENT_CLASS(nfsd_io_class,
+ TP_PROTO(struct svc_rqst *rqstp,
+ struct svc_fh *fhp,
+ loff_t offset,
+ int len),
+ TP_ARGS(rqstp, fhp, offset, len),
+ TP_STRUCT__entry(
+ __field(__be32, xid)
+ __field_struct(struct knfsd_fh, fh)
+ __field(loff_t, offset)
+ __field(int, len)
+ ),
+ TP_fast_assign(
+ __entry->xid = rqstp->rq_xid,
+ fh_copy_shallow(&__entry->fh, &fhp->fh_handle);
+ __entry->offset = offset;
+ __entry->len = len;
+ ),
+ TP_printk("xid=0x%x fh=0x%x offset=%lld len=%d",
+ __be32_to_cpu(__entry->xid), knfsd_fh_hash(&__entry->fh),
+ __entry->offset, __entry->len)
+)
+
+#define DEFINE_NFSD_IO_EVENT(name) \
+DEFINE_EVENT(nfsd_io_class, name, \
+ TP_PROTO(struct svc_rqst *rqstp, \
+ struct svc_fh *fhp, \
+ loff_t offset, \
+ int len), \
+ TP_ARGS(rqstp, fhp, offset, len))
+
+DEFINE_NFSD_IO_EVENT(read_start);
+DEFINE_NFSD_IO_EVENT(read_opened);
+DEFINE_NFSD_IO_EVENT(read_io_done);
+DEFINE_NFSD_IO_EVENT(read_done);
+DEFINE_NFSD_IO_EVENT(write_start);
+DEFINE_NFSD_IO_EVENT(write_opened);
+DEFINE_NFSD_IO_EVENT(write_io_done);
+DEFINE_NFSD_IO_EVENT(write_done);
#include "state.h"
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 994d66fbb446..6739077f17fe 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -36,12 +36,14 @@
#endif /* CONFIG_NFSD_V3 */
#ifdef CONFIG_NFSD_V4
+#include "../internal.h"
#include "acl.h"
#include "idmap.h"
#endif /* CONFIG_NFSD_V4 */
#include "nfsd.h"
#include "vfs.h"
+#include "trace.h"
#define NFSDDBG_FACILITY NFSDDBG_FILEOP
@@ -217,10 +219,16 @@ nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp,
host_err = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto out_nfserr;
- /*
- * check if we have crossed a mount point ...
- */
if (nfsd_mountpoint(dentry, exp)) {
+ /*
+ * We don't need the i_mutex after all. It's
+ * still possible we could open this (regular
+ * files can be mountpoints too), but the
+ * i_mutex is just there to prevent renames of
+ * something that we might be about to delegate,
+ * and a mountpoint won't be renamed:
+ */
+ fh_unlock(fhp);
if ((host_err = nfsd_cross_mnt(rqstp, &dentry, &exp))) {
dput(dentry);
goto out_nfserr;
@@ -498,6 +506,13 @@ __be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp,
}
#endif
+__be32 nfsd4_clone_file_range(struct file *src, u64 src_pos, struct file *dst,
+ u64 dst_pos, u64 count)
+{
+ return nfserrno(vfs_clone_file_range(src, src_pos, dst, dst_pos,
+ count));
+}
+
__be32 nfsd4_vfs_fallocate(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct file *file, loff_t offset, loff_t len,
int flags)
@@ -983,16 +998,23 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct raparms *ra;
__be32 err;
+ trace_read_start(rqstp, fhp, offset, vlen);
err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_READ, &file);
if (err)
return err;
ra = nfsd_init_raparms(file);
+
+ trace_read_opened(rqstp, fhp, offset, vlen);
err = nfsd_vfs_read(rqstp, file, offset, vec, vlen, count);
+ trace_read_io_done(rqstp, fhp, offset, vlen);
+
if (ra)
nfsd_put_raparams(file, ra);
fput(file);
+ trace_read_done(rqstp, fhp, offset, vlen);
+
return err;
}
@@ -1008,24 +1030,31 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
{
__be32 err = 0;
+ trace_write_start(rqstp, fhp, offset, vlen);
+
if (file) {
err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry,
NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE);
if (err)
goto out;
+ trace_write_opened(rqstp, fhp, offset, vlen);
err = nfsd_vfs_write(rqstp, fhp, file, offset, vec, vlen, cnt,
stablep);
+ trace_write_io_done(rqstp, fhp, offset, vlen);
} else {
err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_WRITE, &file);
if (err)
goto out;
+ trace_write_opened(rqstp, fhp, offset, vlen);
if (cnt)
err = nfsd_vfs_write(rqstp, fhp, file, offset, vec, vlen,
cnt, stablep);
+ trace_write_io_done(rqstp, fhp, offset, vlen);
fput(file);
}
out:
+ trace_write_done(rqstp, fhp, offset, vlen);
return err;
}
@@ -1809,7 +1838,6 @@ static __be32 nfsd_buffered_readdir(struct file *file, nfsd_filldir_t func,
offset = *offsetp;
while (1) {
- struct inode *dir_inode = file_inode(file);
unsigned int reclen;
cdp->err = nfserr_eof; /* will be cleared on successful read */
@@ -1828,15 +1856,6 @@ static __be32 nfsd_buffered_readdir(struct file *file, nfsd_filldir_t func,
if (!size)
break;
- /*
- * Various filldir functions may end up calling back into
- * lookup_one_len() and the file system's ->lookup() method.
- * These expect i_mutex to be held, as it would within readdir.
- */
- host_err = mutex_lock_killable(&dir_inode->i_mutex);
- if (host_err)
- break;
-
de = (struct buffered_dirent *)buf.dirent;
while (size > 0) {
offset = de->offset;
@@ -1853,7 +1872,6 @@ static __be32 nfsd_buffered_readdir(struct file *file, nfsd_filldir_t func,
size -= reclen;
de = (struct buffered_dirent *)((char *)de + reclen);
}
- mutex_unlock(&dir_inode->i_mutex);
if (size > 0) /* We bailed out early */
break;
diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h
index fcfc48cbe136..c11ba316f23f 100644
--- a/fs/nfsd/vfs.h
+++ b/fs/nfsd/vfs.h
@@ -56,6 +56,8 @@ __be32 nfsd4_set_nfs4_label(struct svc_rqst *, struct svc_fh *,
struct xdr_netobj *);
__be32 nfsd4_vfs_fallocate(struct svc_rqst *, struct svc_fh *,
struct file *, loff_t, loff_t, int);
+__be32 nfsd4_clone_file_range(struct file *, u64, struct file *,
+ u64, u64);
#endif /* CONFIG_NFSD_V4 */
__be32 nfsd_create(struct svc_rqst *, struct svc_fh *,
char *name, int len, struct iattr *attrs,
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index ce7362c88b48..d9554813e58a 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -491,6 +491,15 @@ struct nfsd4_fallocate {
u64 falloc_length;
};
+struct nfsd4_clone {
+ /* request */
+ stateid_t cl_src_stateid;
+ stateid_t cl_dst_stateid;
+ u64 cl_src_pos;
+ u64 cl_dst_pos;
+ u64 cl_count;
+};
+
struct nfsd4_seek {
/* request */
stateid_t seek_stateid;
@@ -555,6 +564,7 @@ struct nfsd4_op {
/* NFSv4.2 */
struct nfsd4_fallocate allocate;
struct nfsd4_fallocate deallocate;
+ struct nfsd4_clone clone;
struct nfsd4_seek seek;
} u;
struct nfs4_replay * replay;
diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c
index ac2f64943ff4..10b22527a617 100644
--- a/fs/nilfs2/inode.c
+++ b/fs/nilfs2/inode.c
@@ -510,6 +510,7 @@ static int __nilfs_read_inode(struct super_block *sb,
inode->i_mapping->a_ops = &nilfs_aops;
} else if (S_ISLNK(inode->i_mode)) {
inode->i_op = &nilfs_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_mapping->a_ops = &nilfs_aops;
} else {
inode->i_op = &nilfs_special_inode_operations;
diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c
index c9a1a491aa91..7ccdb961eea9 100644
--- a/fs/nilfs2/namei.c
+++ b/fs/nilfs2/namei.c
@@ -161,6 +161,7 @@ static int nilfs_symlink(struct inode *dir, struct dentry *dentry,
/* slow symlink */
inode->i_op = &nilfs_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_mapping->a_ops = &nilfs_aops;
err = page_symlink(inode, symname, l);
if (err)
@@ -568,8 +569,7 @@ const struct inode_operations nilfs_special_inode_operations = {
const struct inode_operations nilfs_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = page_follow_link_light,
- .put_link = page_put_link,
+ .get_link = page_get_link,
.permission = nilfs_permission,
};
diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c
index 354013ea22ec..7f5d3d9f1c37 100644
--- a/fs/nilfs2/super.c
+++ b/fs/nilfs2/super.c
@@ -1316,13 +1316,11 @@ nilfs_mount(struct file_system_type *fs_type, int flags,
}
if (!s->s_root) {
- char b[BDEVNAME_SIZE];
-
- s_new = true;
+ s_new = true;
/* New superblock instance created */
s->s_mode = mode;
- strlcpy(s->s_id, bdevname(sd.bdev, b), sizeof(s->s_id));
+ snprintf(s->s_id, sizeof(s->s_id), "%pg", sd.bdev);
sb_set_blocksize(s, block_size(sd.bdev));
err = nilfs_fill_super(s, data, flags & MS_SILENT ? 1 : 0);
@@ -1418,7 +1416,8 @@ static int __init nilfs_init_cachep(void)
{
nilfs_inode_cachep = kmem_cache_create("nilfs2_inode_cache",
sizeof(struct nilfs_inode_info), 0,
- SLAB_RECLAIM_ACCOUNT, nilfs_inode_init_once);
+ SLAB_RECLAIM_ACCOUNT|SLAB_ACCOUNT,
+ nilfs_inode_init_once);
if (!nilfs_inode_cachep)
goto fail;
diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c
index e785fd954c30..741077deef3b 100644
--- a/fs/notify/inode_mark.c
+++ b/fs/notify/inode_mark.c
@@ -199,8 +199,7 @@ void fsnotify_unmount_inodes(struct super_block *sb)
break;
}
spin_unlock(&next_i->i_lock);
- next_i = list_entry(next_i->i_sb_list.next,
- struct inode, i_sb_list);
+ next_i = list_next_entry(next_i, i_sb_list);
}
/*
diff --git a/fs/notify/mark.c b/fs/notify/mark.c
index fc0df4442f7b..cfcbf114676e 100644
--- a/fs/notify/mark.c
+++ b/fs/notify/mark.c
@@ -92,9 +92,6 @@
#include "fsnotify.h"
struct srcu_struct fsnotify_mark_srcu;
-static DEFINE_SPINLOCK(destroy_lock);
-static LIST_HEAD(destroy_list);
-static DECLARE_WAIT_QUEUE_HEAD(destroy_waitq);
void fsnotify_get_mark(struct fsnotify_mark *mark)
{
@@ -168,10 +165,19 @@ void fsnotify_detach_mark(struct fsnotify_mark *mark)
atomic_dec(&group->num_marks);
}
+static void
+fsnotify_mark_free_rcu(struct rcu_head *rcu)
+{
+ struct fsnotify_mark *mark;
+
+ mark = container_of(rcu, struct fsnotify_mark, g_rcu);
+ fsnotify_put_mark(mark);
+}
+
/*
- * Free fsnotify mark. The freeing is actually happening from a kthread which
- * first waits for srcu period end. Caller must have a reference to the mark
- * or be protected by fsnotify_mark_srcu.
+ * Free fsnotify mark. The freeing is actually happening from a call_srcu
+ * callback. Caller must have a reference to the mark or be protected by
+ * fsnotify_mark_srcu.
*/
void fsnotify_free_mark(struct fsnotify_mark *mark)
{
@@ -186,10 +192,7 @@ void fsnotify_free_mark(struct fsnotify_mark *mark)
mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE;
spin_unlock(&mark->lock);
- spin_lock(&destroy_lock);
- list_add(&mark->g_list, &destroy_list);
- spin_unlock(&destroy_lock);
- wake_up(&destroy_waitq);
+ call_srcu(&fsnotify_mark_srcu, &mark->g_rcu, fsnotify_mark_free_rcu);
/*
* Some groups like to know that marks are being freed. This is a
@@ -385,11 +388,7 @@ err:
spin_unlock(&mark->lock);
- spin_lock(&destroy_lock);
- list_add(&mark->g_list, &destroy_list);
- spin_unlock(&destroy_lock);
- wake_up(&destroy_waitq);
-
+ call_srcu(&fsnotify_mark_srcu, &mark->g_rcu, fsnotify_mark_free_rcu);
return ret;
}
@@ -492,40 +491,3 @@ void fsnotify_init_mark(struct fsnotify_mark *mark,
atomic_set(&mark->refcnt, 1);
mark->free_mark = free_mark;
}
-
-static int fsnotify_mark_destroy(void *ignored)
-{
- struct fsnotify_mark *mark, *next;
- struct list_head private_destroy_list;
-
- for (;;) {
- spin_lock(&destroy_lock);
- /* exchange the list head */
- list_replace_init(&destroy_list, &private_destroy_list);
- spin_unlock(&destroy_lock);
-
- synchronize_srcu(&fsnotify_mark_srcu);
-
- list_for_each_entry_safe(mark, next, &private_destroy_list, g_list) {
- list_del_init(&mark->g_list);
- fsnotify_put_mark(mark);
- }
-
- wait_event_interruptible(destroy_waitq, !list_empty(&destroy_list));
- }
-
- return 0;
-}
-
-static int __init fsnotify_mark_init(void)
-{
- struct task_struct *thread;
-
- thread = kthread_run(fsnotify_mark_destroy, NULL,
- "fsnotify_mark");
- if (IS_ERR(thread))
- panic("unable to start fsnotify mark destruction thread.");
-
- return 0;
-}
-device_initcall(fsnotify_mark_init);
diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
index d1a853585b53..2f77f8dfb861 100644
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -3139,8 +3139,8 @@ static int __init init_ntfs_fs(void)
ntfs_big_inode_cache = kmem_cache_create(ntfs_big_inode_cache_name,
sizeof(big_ntfs_inode), 0,
- SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD,
- ntfs_big_inode_init_once);
+ SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|
+ SLAB_ACCOUNT, ntfs_big_inode_init_once);
if (!ntfs_big_inode_cache) {
pr_crit("Failed to create %s!\n", ntfs_big_inode_cache_name);
goto big_inode_err_out;
diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c
index 86181d6526dc..a3ded88718c9 100644
--- a/fs/ocfs2/alloc.c
+++ b/fs/ocfs2/alloc.c
@@ -164,7 +164,7 @@ static int ocfs2_dinode_insert_check(struct ocfs2_extent_tree *et,
struct ocfs2_extent_rec *rec);
static int ocfs2_dinode_sanity_check(struct ocfs2_extent_tree *et);
static void ocfs2_dinode_fill_root_el(struct ocfs2_extent_tree *et);
-static struct ocfs2_extent_tree_operations ocfs2_dinode_et_ops = {
+static const struct ocfs2_extent_tree_operations ocfs2_dinode_et_ops = {
.eo_set_last_eb_blk = ocfs2_dinode_set_last_eb_blk,
.eo_get_last_eb_blk = ocfs2_dinode_get_last_eb_blk,
.eo_update_clusters = ocfs2_dinode_update_clusters,
@@ -286,7 +286,7 @@ static void ocfs2_xattr_value_update_clusters(struct ocfs2_extent_tree *et,
le32_add_cpu(&vb->vb_xv->xr_clusters, clusters);
}
-static struct ocfs2_extent_tree_operations ocfs2_xattr_value_et_ops = {
+static const struct ocfs2_extent_tree_operations ocfs2_xattr_value_et_ops = {
.eo_set_last_eb_blk = ocfs2_xattr_value_set_last_eb_blk,
.eo_get_last_eb_blk = ocfs2_xattr_value_get_last_eb_blk,
.eo_update_clusters = ocfs2_xattr_value_update_clusters,
@@ -332,7 +332,7 @@ static void ocfs2_xattr_tree_update_clusters(struct ocfs2_extent_tree *et,
le32_add_cpu(&xb->xb_attrs.xb_root.xt_clusters, clusters);
}
-static struct ocfs2_extent_tree_operations ocfs2_xattr_tree_et_ops = {
+static const struct ocfs2_extent_tree_operations ocfs2_xattr_tree_et_ops = {
.eo_set_last_eb_blk = ocfs2_xattr_tree_set_last_eb_blk,
.eo_get_last_eb_blk = ocfs2_xattr_tree_get_last_eb_blk,
.eo_update_clusters = ocfs2_xattr_tree_update_clusters,
@@ -379,7 +379,7 @@ static void ocfs2_dx_root_fill_root_el(struct ocfs2_extent_tree *et)
et->et_root_el = &dx_root->dr_list;
}
-static struct ocfs2_extent_tree_operations ocfs2_dx_root_et_ops = {
+static const struct ocfs2_extent_tree_operations ocfs2_dx_root_et_ops = {
.eo_set_last_eb_blk = ocfs2_dx_root_set_last_eb_blk,
.eo_get_last_eb_blk = ocfs2_dx_root_get_last_eb_blk,
.eo_update_clusters = ocfs2_dx_root_update_clusters,
@@ -425,7 +425,7 @@ ocfs2_refcount_tree_extent_contig(struct ocfs2_extent_tree *et,
return CONTIG_NONE;
}
-static struct ocfs2_extent_tree_operations ocfs2_refcount_tree_et_ops = {
+static const struct ocfs2_extent_tree_operations ocfs2_refcount_tree_et_ops = {
.eo_set_last_eb_blk = ocfs2_refcount_tree_set_last_eb_blk,
.eo_get_last_eb_blk = ocfs2_refcount_tree_get_last_eb_blk,
.eo_update_clusters = ocfs2_refcount_tree_update_clusters,
@@ -438,7 +438,7 @@ static void __ocfs2_init_extent_tree(struct ocfs2_extent_tree *et,
struct buffer_head *bh,
ocfs2_journal_access_func access,
void *obj,
- struct ocfs2_extent_tree_operations *ops)
+ const struct ocfs2_extent_tree_operations *ops)
{
et->et_ops = ops;
et->et_root_bh = bh;
@@ -6174,8 +6174,7 @@ int ocfs2_begin_truncate_log_recovery(struct ocfs2_super *osb,
}
bail:
- if (tl_inode)
- iput(tl_inode);
+ iput(tl_inode);
brelse(tl_bh);
if (status < 0) {
diff --git a/fs/ocfs2/alloc.h b/fs/ocfs2/alloc.h
index fb09b97db162..f3dc1b0dfffc 100644
--- a/fs/ocfs2/alloc.h
+++ b/fs/ocfs2/alloc.h
@@ -54,7 +54,7 @@
*/
struct ocfs2_extent_tree_operations;
struct ocfs2_extent_tree {
- struct ocfs2_extent_tree_operations *et_ops;
+ const struct ocfs2_extent_tree_operations *et_ops;
struct buffer_head *et_root_bh;
struct ocfs2_extent_list *et_root_el;
struct ocfs2_caching_info *et_ci;
diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c
index 709fbbd44c65..a3cc6d2fc896 100644
--- a/fs/ocfs2/cluster/heartbeat.c
+++ b/fs/ocfs2/cluster/heartbeat.c
@@ -1780,8 +1780,8 @@ static ssize_t o2hb_region_dev_store(struct config_item *item,
}
++live_threshold;
atomic_set(&reg->hr_steady_iterations, live_threshold);
- /* unsteady_iterations is double the steady_iterations */
- atomic_set(&reg->hr_unsteady_iterations, (live_threshold << 1));
+ /* unsteady_iterations is triple the steady_iterations */
+ atomic_set(&reg->hr_unsteady_iterations, (live_threshold * 3));
hb_task = kthread_run(o2hb_thread, reg, "o2hb-%s",
reg->hr_item.ci_name);
diff --git a/fs/ocfs2/dlm/dlmcommon.h b/fs/ocfs2/dlm/dlmcommon.h
index e88ccf8c83ff..68c607e63ff6 100644
--- a/fs/ocfs2/dlm/dlmcommon.h
+++ b/fs/ocfs2/dlm/dlmcommon.h
@@ -376,17 +376,6 @@ struct dlm_lock
lksb_kernel_allocated:1;
};
-
-#define DLM_LKSB_UNUSED1 0x01
-#define DLM_LKSB_PUT_LVB 0x02
-#define DLM_LKSB_GET_LVB 0x04
-#define DLM_LKSB_UNUSED2 0x08
-#define DLM_LKSB_UNUSED3 0x10
-#define DLM_LKSB_UNUSED4 0x20
-#define DLM_LKSB_UNUSED5 0x40
-#define DLM_LKSB_UNUSED6 0x80
-
-
enum dlm_lockres_list {
DLM_GRANTED_LIST = 0,
DLM_CONVERTING_LIST = 1,
diff --git a/fs/ocfs2/dlm/dlmmaster.c b/fs/ocfs2/dlm/dlmmaster.c
index ce38b4ccc9ab..9477d6e1de37 100644
--- a/fs/ocfs2/dlm/dlmmaster.c
+++ b/fs/ocfs2/dlm/dlmmaster.c
@@ -2388,8 +2388,8 @@ static void dlm_deref_lockres_worker(struct dlm_work_item *item, void *data)
spin_lock(&res->spinlock);
BUG_ON(res->state & DLM_LOCK_RES_DROPPING_REF);
+ __dlm_wait_on_lockres_flags(res, DLM_LOCK_RES_SETREF_INPROG);
if (test_bit(node, res->refmap)) {
- __dlm_wait_on_lockres_flags(res, DLM_LOCK_RES_SETREF_INPROG);
dlm_lockres_clear_refmap_bit(dlm, res, node);
cleared = 1;
}
@@ -2519,6 +2519,11 @@ static int dlm_migrate_lockres(struct dlm_ctxt *dlm,
spin_lock(&dlm->master_lock);
ret = dlm_add_migration_mle(dlm, res, mle, &oldmle, name,
namelen, target, dlm->node_num);
+ /* get an extra reference on the mle.
+ * otherwise the assert_master from the new
+ * master will destroy this.
+ */
+ dlm_get_mle_inuse(mle);
spin_unlock(&dlm->master_lock);
spin_unlock(&dlm->spinlock);
@@ -2544,7 +2549,7 @@ static int dlm_migrate_lockres(struct dlm_ctxt *dlm,
}
fail:
- if (oldmle) {
+ if (ret != -EEXIST && oldmle) {
/* master is known, detach if not already detached */
dlm_mle_detach_hb_events(dlm, oldmle);
dlm_put_mle(oldmle);
@@ -2554,6 +2559,7 @@ fail:
if (mle_added) {
dlm_mle_detach_hb_events(dlm, mle);
dlm_put_mle(mle);
+ dlm_put_mle_inuse(mle);
} else if (mle) {
kmem_cache_free(dlm_mle_cache, mle);
mle = NULL;
@@ -2571,17 +2577,6 @@ fail:
* ensure that all assert_master work is flushed. */
flush_workqueue(dlm->dlm_worker);
- /* get an extra reference on the mle.
- * otherwise the assert_master from the new
- * master will destroy this.
- * also, make sure that all callers of dlm_get_mle
- * take both dlm->spinlock and dlm->master_lock */
- spin_lock(&dlm->spinlock);
- spin_lock(&dlm->master_lock);
- dlm_get_mle_inuse(mle);
- spin_unlock(&dlm->master_lock);
- spin_unlock(&dlm->spinlock);
-
/* notify new node and send all lock state */
/* call send_one_lockres with migration flag.
* this serves as notice to the target node that a
@@ -2843,6 +2838,8 @@ again:
res->state &= ~DLM_LOCK_RES_BLOCK_DIRTY;
if (!ret)
BUG_ON(!(res->state & DLM_LOCK_RES_MIGRATING));
+ else
+ res->migration_pending = 0;
spin_unlock(&res->spinlock);
/*
@@ -3048,7 +3045,7 @@ int dlm_migrate_request_handler(struct o2net_msg *msg, u32 len, void *data,
int ret = 0;
if (!dlm_grab(dlm))
- return -EINVAL;
+ return 0;
name = migrate->name;
namelen = migrate->namelen;
@@ -3139,7 +3136,8 @@ static int dlm_add_migration_mle(struct dlm_ctxt *dlm,
mlog(0, "tried to migrate %.*s, but some "
"process beat me to it\n",
namelen, name);
- ret = -EEXIST;
+ spin_unlock(&tmp->spinlock);
+ return -EEXIST;
} else {
/* bad. 2 NODES are trying to migrate! */
mlog(ML_ERROR, "migration error mle: "
@@ -3310,6 +3308,15 @@ top:
mle->new_master != dead_node)
continue;
+ if (mle->new_master == dead_node && mle->inuse) {
+ mlog(ML_NOTICE, "%s: target %u died during "
+ "migration from %u, the MLE is "
+ "still keep used, ignore it!\n",
+ dlm->name, dead_node,
+ mle->master);
+ continue;
+ }
+
/* If we have reached this point, this mle needs to be
* removed from the list and freed. */
dlm_clean_migration_mle(dlm, mle);
diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c
index 9e4f862d20fe..c5bdf02c213b 100644
--- a/fs/ocfs2/dlm/dlmrecovery.c
+++ b/fs/ocfs2/dlm/dlmrecovery.c
@@ -1373,6 +1373,7 @@ int dlm_mig_lockres_handler(struct o2net_msg *msg, u32 len, void *data,
char *buf = NULL;
struct dlm_work_item *item = NULL;
struct dlm_lock_resource *res = NULL;
+ unsigned int hash;
if (!dlm_grab(dlm))
return -EINVAL;
@@ -1400,7 +1401,10 @@ int dlm_mig_lockres_handler(struct o2net_msg *msg, u32 len, void *data,
/* lookup the lock to see if we have a secondary queue for this
* already... just add the locks in and this will have its owner
* and RECOVERY flag changed when it completes. */
- res = dlm_lookup_lockres(dlm, mres->lockname, mres->lockname_len);
+ hash = dlm_lockid_hash(mres->lockname, mres->lockname_len);
+ spin_lock(&dlm->spinlock);
+ res = __dlm_lookup_lockres(dlm, mres->lockname, mres->lockname_len,
+ hash);
if (res) {
/* this will get a ref on res */
/* mark it as recovering/migrating and hash it */
@@ -1421,13 +1425,16 @@ int dlm_mig_lockres_handler(struct o2net_msg *msg, u32 len, void *data,
mres->lockname_len, mres->lockname);
ret = -EFAULT;
spin_unlock(&res->spinlock);
+ spin_unlock(&dlm->spinlock);
dlm_lockres_put(res);
goto leave;
}
res->state |= DLM_LOCK_RES_MIGRATING;
}
spin_unlock(&res->spinlock);
+ spin_unlock(&dlm->spinlock);
} else {
+ spin_unlock(&dlm->spinlock);
/* need to allocate, just like if it was
* mastered here normally */
res = dlm_new_lockres(dlm, mres->lockname, mres->lockname_len);
@@ -2450,11 +2457,7 @@ static void __dlm_hb_node_down(struct dlm_ctxt *dlm, int idx)
* perhaps later we can genericize this for other waiters. */
wake_up(&dlm->migration_wq);
- if (test_bit(idx, dlm->recovery_map))
- mlog(0, "domain %s, node %u already added "
- "to recovery map!\n", dlm->name, idx);
- else
- set_bit(idx, dlm->recovery_map);
+ set_bit(idx, dlm->recovery_map);
}
void dlm_hb_node_down_cb(struct o2nm_node *node, int idx, void *data)
diff --git a/fs/ocfs2/dlm/dlmunlock.c b/fs/ocfs2/dlm/dlmunlock.c
index 2e3c9dbab68c..1082b2c3014b 100644
--- a/fs/ocfs2/dlm/dlmunlock.c
+++ b/fs/ocfs2/dlm/dlmunlock.c
@@ -421,7 +421,7 @@ int dlm_unlock_lock_handler(struct o2net_msg *msg, u32 len, void *data,
}
if (!dlm_grab(dlm))
- return DLM_REJECTED;
+ return DLM_FORWARD;
mlog_bug_on_msg(!dlm_domain_fully_joined(dlm),
"Domain %s not fully joined!\n", dlm->name);
diff --git a/fs/ocfs2/dlmfs/dlmfs.c b/fs/ocfs2/dlmfs/dlmfs.c
index b5cf27dcb18a..03768bb3aab1 100644
--- a/fs/ocfs2/dlmfs/dlmfs.c
+++ b/fs/ocfs2/dlmfs/dlmfs.c
@@ -638,7 +638,7 @@ static int __init init_dlmfs_fs(void)
dlmfs_inode_cache = kmem_cache_create("dlmfs_inode_cache",
sizeof(struct dlmfs_inode_private),
0, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
dlmfs_init_once);
if (!dlmfs_inode_cache) {
status = -ENOMEM;
diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
index 20276e340339..f92612e4b9d6 100644
--- a/fs/ocfs2/dlmglue.c
+++ b/fs/ocfs2/dlmglue.c
@@ -2432,12 +2432,6 @@ bail:
* done this we have to return AOP_TRUNCATED_PAGE so the aop method
* that called us can bubble that back up into the VFS who will then
* immediately retry the aop call.
- *
- * We do a blocking lock and immediate unlock before returning, though, so that
- * the lock has a great chance of being cached on this node by the time the VFS
- * calls back to retry the aop. This has a potential to livelock as nodes
- * ping locks back and forth, but that's a risk we're willing to take to avoid
- * the lock inversion simply.
*/
int ocfs2_inode_lock_with_page(struct inode *inode,
struct buffer_head **ret_bh,
@@ -2449,8 +2443,6 @@ int ocfs2_inode_lock_with_page(struct inode *inode,
ret = ocfs2_inode_lock_full(inode, ret_bh, ex, OCFS2_LOCK_NONBLOCK);
if (ret == -EAGAIN) {
unlock_page(page);
- if (ocfs2_inode_lock(inode, ret_bh, ex) == 0)
- ocfs2_inode_unlock(inode, ex);
ret = AOP_TRUNCATED_PAGE;
}
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index 0e5b4515f92e..d63127932509 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -1302,6 +1302,14 @@ int ocfs2_getattr(struct vfsmount *mnt,
}
generic_fillattr(inode, stat);
+ /*
+ * If there is inline data in the inode, the inode will normally not
+ * have data blocks allocated (it may have an external xattr block).
+ * Report at least one sector for such files, so tools like tar, rsync,
+ * others don't incorrectly think the file is completely sparse.
+ */
+ if (unlikely(OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL))
+ stat->blocks += (stat->size + 511)>>9;
/* We set the blksize from the cluster size for performance */
stat->blksize = osb->s_clustersize;
diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c
index 8f87e05ee25d..97a563bab9a8 100644
--- a/fs/ocfs2/inode.c
+++ b/fs/ocfs2/inode.c
@@ -361,6 +361,7 @@ void ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe,
break;
case S_IFLNK:
inode->i_op = &ocfs2_symlink_inode_operations;
+ inode_nohighmem(inode);
i_size_write(inode, le64_to_cpu(fe->i_size));
break;
default:
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 3cb097ccce60..16b0bb482ea7 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -606,9 +606,7 @@ bail:
if (gb_inode)
mutex_unlock(&gb_inode->i_mutex);
- if (gb_inode)
- iput(gb_inode);
-
+ iput(gb_inode);
brelse(bh);
return status;
diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c
index 13534f4fe5b5..3772a2dbb980 100644
--- a/fs/ocfs2/journal.c
+++ b/fs/ocfs2/journal.c
@@ -1042,8 +1042,7 @@ void ocfs2_journal_shutdown(struct ocfs2_super *osb)
// up_write(&journal->j_trans_barrier);
done:
- if (inode)
- iput(inode);
+ iput(inode);
}
static void ocfs2_clear_journal_error(struct super_block *sb,
@@ -1687,9 +1686,7 @@ done:
if (got_lock)
ocfs2_inode_unlock(inode, 1);
- if (inode)
- iput(inode);
-
+ iput(inode);
brelse(bh);
return status;
@@ -1796,8 +1793,7 @@ static int ocfs2_trylock_journal(struct ocfs2_super *osb,
ocfs2_inode_unlock(inode, 1);
bail:
- if (inode)
- iput(inode);
+ iput(inode);
return status;
}
diff --git a/fs/ocfs2/localalloc.c b/fs/ocfs2/localalloc.c
index 0a4457fb0711..e9c99e35f5ea 100644
--- a/fs/ocfs2/localalloc.c
+++ b/fs/ocfs2/localalloc.c
@@ -358,8 +358,7 @@ int ocfs2_load_local_alloc(struct ocfs2_super *osb)
bail:
if (status < 0)
brelse(alloc_bh);
- if (inode)
- iput(inode);
+ iput(inode);
trace_ocfs2_load_local_alloc(osb->local_alloc_bits);
@@ -473,8 +472,7 @@ out_mutex:
iput(main_bm_inode);
out:
- if (local_alloc_inode)
- iput(local_alloc_inode);
+ iput(local_alloc_inode);
kfree(alloc_copy);
}
@@ -1327,9 +1325,7 @@ bail:
brelse(main_bm_bh);
- if (main_bm_inode)
- iput(main_bm_inode);
-
+ iput(main_bm_inode);
kfree(alloc_copy);
if (ac)
diff --git a/fs/ocfs2/locks.c b/fs/ocfs2/locks.c
index 652ece4a9d9e..d56f0079b858 100644
--- a/fs/ocfs2/locks.c
+++ b/fs/ocfs2/locks.c
@@ -67,7 +67,10 @@ static int ocfs2_do_flock(struct file *file, struct inode *inode,
*/
locks_lock_file_wait(file,
- &(struct file_lock){.fl_type = F_UNLCK});
+ &(struct file_lock) {
+ .fl_type = F_UNLCK,
+ .fl_flags = FL_FLOCK
+ });
ocfs2_file_unlock(file);
}
diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c
index a03f6f433075..ab42c38031b1 100644
--- a/fs/ocfs2/namei.c
+++ b/fs/ocfs2/namei.c
@@ -367,13 +367,11 @@ static int ocfs2_mknod(struct inode *dir,
goto leave;
}
- status = posix_acl_create(dir, &mode, &default_acl, &acl);
+ status = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
if (status) {
mlog_errno(status);
goto leave;
}
- /* update inode->i_mode after mask with "umask". */
- inode->i_mode = mode;
handle = ocfs2_start_trans(osb, ocfs2_mknod_credits(osb->sb,
S_ISDIR(mode),
@@ -1685,8 +1683,7 @@ bail:
if (new_inode)
sync_mapping_buffers(old_inode->i_mapping);
- if (new_inode)
- iput(new_inode);
+ iput(new_inode);
ocfs2_free_dir_lookup_result(&target_lookup_res);
ocfs2_free_dir_lookup_result(&old_entry_lookup);
@@ -1960,6 +1957,7 @@ static int ocfs2_symlink(struct inode *dir,
inode->i_rdev = 0;
newsize = l - 1;
inode->i_op = &ocfs2_symlink_inode_operations;
+ inode_nohighmem(inode);
if (l > ocfs2_fast_symlink_chars(sb)) {
u32 offset = 0;
@@ -2374,6 +2372,15 @@ int ocfs2_orphan_del(struct ocfs2_super *osb,
(unsigned long long)OCFS2_I(orphan_dir_inode)->ip_blkno,
name, strlen(name));
+ status = ocfs2_journal_access_di(handle,
+ INODE_CACHE(orphan_dir_inode),
+ orphan_dir_bh,
+ OCFS2_JOURNAL_ACCESS_WRITE);
+ if (status < 0) {
+ mlog_errno(status);
+ goto leave;
+ }
+
/* find it's spot in the orphan directory */
status = ocfs2_find_entry(name, strlen(name), orphan_dir_inode,
&lookup);
@@ -2389,15 +2396,6 @@ int ocfs2_orphan_del(struct ocfs2_super *osb,
goto leave;
}
- status = ocfs2_journal_access_di(handle,
- INODE_CACHE(orphan_dir_inode),
- orphan_dir_bh,
- OCFS2_JOURNAL_ACCESS_WRITE);
- if (status < 0) {
- mlog_errno(status);
- goto leave;
- }
-
/* do the i_nlink dance! :) */
orphan_fe = (struct ocfs2_dinode *) orphan_dir_bh->b_data;
if (S_ISDIR(inode->i_mode))
diff --git a/fs/ocfs2/quota.h b/fs/ocfs2/quota.h
index b6d51333ad02..d153e6e31529 100644
--- a/fs/ocfs2/quota.h
+++ b/fs/ocfs2/quota.h
@@ -82,7 +82,7 @@ struct ocfs2_quota_chunk {
extern struct kmem_cache *ocfs2_dquot_cachep;
extern struct kmem_cache *ocfs2_qf_chunk_cachep;
-extern struct qtree_fmt_operations ocfs2_global_ops;
+extern const struct qtree_fmt_operations ocfs2_global_ops;
struct ocfs2_quota_recovery *ocfs2_begin_quota_recovery(
struct ocfs2_super *osb, int slot_num);
diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c
index c93d67220887..fde9ef18cff3 100644
--- a/fs/ocfs2/quota_global.c
+++ b/fs/ocfs2/quota_global.c
@@ -123,7 +123,7 @@ static int ocfs2_global_is_id(void *dp, struct dquot *dquot)
dquot->dq_id);
}
-struct qtree_fmt_operations ocfs2_global_ops = {
+const struct qtree_fmt_operations ocfs2_global_ops = {
.mem2disk_dqblk = ocfs2_global_mem2diskdqb,
.disk2mem_dqblk = ocfs2_global_disk2memdqb,
.is_id = ocfs2_global_is_id,
diff --git a/fs/ocfs2/resize.c b/fs/ocfs2/resize.c
index d5da6f624142..79b8021302b3 100644
--- a/fs/ocfs2/resize.c
+++ b/fs/ocfs2/resize.c
@@ -54,11 +54,12 @@
static u16 ocfs2_calc_new_backup_super(struct inode *inode,
struct ocfs2_group_desc *gd,
u16 cl_cpg,
+ u16 old_bg_clusters,
int set)
{
int i;
u16 backups = 0;
- u32 cluster;
+ u32 cluster, lgd_cluster;
u64 blkno, gd_blkno, lgd_blkno = le64_to_cpu(gd->bg_blkno);
for (i = 0; i < OCFS2_MAX_BACKUP_SUPERBLOCKS; i++) {
@@ -71,6 +72,12 @@ static u16 ocfs2_calc_new_backup_super(struct inode *inode,
else if (gd_blkno > lgd_blkno)
break;
+ /* check if already done backup super */
+ lgd_cluster = ocfs2_blocks_to_clusters(inode->i_sb, lgd_blkno);
+ lgd_cluster += old_bg_clusters;
+ if (lgd_cluster >= cluster)
+ continue;
+
if (set)
ocfs2_set_bit(cluster % cl_cpg,
(unsigned long *)gd->bg_bitmap);
@@ -99,6 +106,7 @@ static int ocfs2_update_last_group_and_inode(handle_t *handle,
u16 chain, num_bits, backups = 0;
u16 cl_bpc = le16_to_cpu(cl->cl_bpc);
u16 cl_cpg = le16_to_cpu(cl->cl_cpg);
+ u16 old_bg_clusters;
trace_ocfs2_update_last_group_and_inode(new_clusters,
first_new_cluster);
@@ -112,6 +120,7 @@ static int ocfs2_update_last_group_and_inode(handle_t *handle,
group = (struct ocfs2_group_desc *)group_bh->b_data;
+ old_bg_clusters = le16_to_cpu(group->bg_bits) / cl_bpc;
/* update the group first. */
num_bits = new_clusters * cl_bpc;
le16_add_cpu(&group->bg_bits, num_bits);
@@ -125,7 +134,7 @@ static int ocfs2_update_last_group_and_inode(handle_t *handle,
OCFS2_FEATURE_COMPAT_BACKUP_SB)) {
backups = ocfs2_calc_new_backup_super(bm_inode,
group,
- cl_cpg, 1);
+ cl_cpg, old_bg_clusters, 1);
le16_add_cpu(&group->bg_free_bits_count, -1 * backups);
}
@@ -163,7 +172,7 @@ out_rollback:
if (ret < 0) {
ocfs2_calc_new_backup_super(bm_inode,
group,
- cl_cpg, 0);
+ cl_cpg, old_bg_clusters, 0);
le16_add_cpu(&group->bg_free_bits_count, backups);
le16_add_cpu(&group->bg_bits, -1 * num_bits);
le16_add_cpu(&group->bg_free_bits_count, -1 * num_bits);
diff --git a/fs/ocfs2/slot_map.c b/fs/ocfs2/slot_map.c
index e78a203d44c8..1e09592148ad 100644
--- a/fs/ocfs2/slot_map.c
+++ b/fs/ocfs2/slot_map.c
@@ -322,8 +322,7 @@ static void __ocfs2_free_slot_info(struct ocfs2_slot_info *si)
if (si == NULL)
return;
- if (si->si_inode)
- iput(si->si_inode);
+ iput(si->si_inode);
if (si->si_bh) {
for (i = 0; i < si->si_blocks; i++) {
if (si->si_bh[i]) {
@@ -503,8 +502,17 @@ int ocfs2_find_slot(struct ocfs2_super *osb)
trace_ocfs2_find_slot(osb->slot_num);
status = ocfs2_update_disk_slot(osb, si, osb->slot_num);
- if (status < 0)
+ if (status < 0) {
mlog_errno(status);
+ /*
+ * if write block failed, invalidate slot to avoid overwrite
+ * slot during dismount in case another node rightly has mounted
+ */
+ spin_lock(&osb->osb_lock);
+ ocfs2_invalidate_slot(si, osb->slot_num);
+ osb->slot_num = OCFS2_INVALID_SLOT;
+ spin_unlock(&osb->osb_lock);
+ }
bail:
return status;
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index 2de4c8a9340c..faa1365097bc 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -1280,6 +1280,8 @@ static int ocfs2_parse_options(struct super_block *sb,
int status, user_stack = 0;
char *p;
u32 tmp;
+ int token, option;
+ substring_t args[MAX_OPT_ARGS];
trace_ocfs2_parse_options(is_remount, options ? options : "(none)");
@@ -1298,9 +1300,6 @@ static int ocfs2_parse_options(struct super_block *sb,
}
while ((p = strsep(&options, ",")) != NULL) {
- int token, option;
- substring_t args[MAX_OPT_ARGS];
-
if (!*p)
continue;
@@ -1367,7 +1366,6 @@ static int ocfs2_parse_options(struct super_block *sb,
mopt->atime_quantum = option;
break;
case Opt_slot:
- option = 0;
if (match_int(&args[0], &option)) {
status = 0;
goto bail;
@@ -1376,7 +1374,6 @@ static int ocfs2_parse_options(struct super_block *sb,
mopt->slot = (s16)option;
break;
case Opt_commit:
- option = 0;
if (match_int(&args[0], &option)) {
status = 0;
goto bail;
@@ -1388,7 +1385,6 @@ static int ocfs2_parse_options(struct super_block *sb,
mopt->commit_interval = HZ * option;
break;
case Opt_localalloc:
- option = 0;
if (match_int(&args[0], &option)) {
status = 0;
goto bail;
@@ -1726,8 +1722,7 @@ static int ocfs2_statfs(struct dentry *dentry, struct kstatfs *buf)
ocfs2_inode_unlock(inode, 0);
status = 0;
bail:
- if (inode)
- iput(inode);
+ iput(inode);
if (status)
mlog_errno(status);
@@ -1771,7 +1766,7 @@ static int ocfs2_initialize_mem_caches(void)
sizeof(struct ocfs2_inode_info),
0,
(SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
ocfs2_inode_init_once);
ocfs2_dquot_cachep = kmem_cache_create("ocfs2_dquot_cache",
sizeof(struct ocfs2_dquot),
diff --git a/fs/ocfs2/symlink.c b/fs/ocfs2/symlink.c
index 66edce7ecfd7..6c2a3e3c521c 100644
--- a/fs/ocfs2/symlink.c
+++ b/fs/ocfs2/symlink.c
@@ -88,8 +88,7 @@ const struct address_space_operations ocfs2_fast_symlink_aops = {
const struct inode_operations ocfs2_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = page_follow_link_light,
- .put_link = page_put_link,
+ .get_link = page_get_link,
.getattr = ocfs2_getattr,
.setattr = ocfs2_setattr,
.setxattr = generic_setxattr,
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
index e9164f09841b..f0e241ffd94f 100644
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -544,8 +544,7 @@ static inline const char *ocfs2_xattr_prefix(int name_index)
if (name_index > 0 && name_index < OCFS2_XATTR_MAX)
handler = ocfs2_xattr_handler_map[name_index];
-
- return handler ? handler->prefix : NULL;
+ return handler ? xattr_prefix(handler) : NULL;
}
static u32 ocfs2_xattr_name_hash(struct inode *inode,
@@ -884,14 +883,39 @@ static int ocfs2_xattr_value_truncate(struct inode *inode,
return ret;
}
-static int ocfs2_xattr_list_entry(char *buffer, size_t size,
- size_t *result, const char *prefix,
+static int ocfs2_xattr_list_entry(struct super_block *sb,
+ char *buffer, size_t size,
+ size_t *result, int type,
const char *name, int name_len)
{
char *p = buffer + *result;
- int prefix_len = strlen(prefix);
- int total_len = prefix_len + name_len + 1;
+ const char *prefix;
+ int prefix_len;
+ int total_len;
+ switch(type) {
+ case OCFS2_XATTR_INDEX_USER:
+ if (OCFS2_SB(sb)->s_mount_opt & OCFS2_MOUNT_NOUSERXATTR)
+ return 0;
+ break;
+
+ case OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS:
+ case OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT:
+ if (!(sb->s_flags & MS_POSIXACL))
+ return 0;
+ break;
+
+ case OCFS2_XATTR_INDEX_TRUSTED:
+ if (!capable(CAP_SYS_ADMIN))
+ return 0;
+ break;
+ }
+
+ prefix = ocfs2_xattr_prefix(type);
+ if (!prefix)
+ return 0;
+ prefix_len = strlen(prefix);
+ total_len = prefix_len + name_len + 1;
*result += total_len;
/* we are just looking for how big our buffer needs to be */
@@ -914,23 +938,20 @@ static int ocfs2_xattr_list_entries(struct inode *inode,
{
size_t result = 0;
int i, type, ret;
- const char *prefix, *name;
+ const char *name;
for (i = 0 ; i < le16_to_cpu(header->xh_count); i++) {
struct ocfs2_xattr_entry *entry = &header->xh_entries[i];
type = ocfs2_xattr_get_type(entry);
- prefix = ocfs2_xattr_prefix(type);
-
- if (prefix) {
- name = (const char *)header +
- le16_to_cpu(entry->xe_name_offset);
+ name = (const char *)header +
+ le16_to_cpu(entry->xe_name_offset);
- ret = ocfs2_xattr_list_entry(buffer, buffer_size,
- &result, prefix, name,
- entry->xe_name_len);
- if (ret)
- return ret;
- }
+ ret = ocfs2_xattr_list_entry(inode->i_sb,
+ buffer, buffer_size,
+ &result, type, name,
+ entry->xe_name_len);
+ if (ret)
+ return ret;
}
return result;
@@ -4033,32 +4054,30 @@ static int ocfs2_list_xattr_bucket(struct inode *inode,
int ret = 0, type;
struct ocfs2_xattr_tree_list *xl = (struct ocfs2_xattr_tree_list *)para;
int i, block_off, new_offset;
- const char *prefix, *name;
+ const char *name;
for (i = 0 ; i < le16_to_cpu(bucket_xh(bucket)->xh_count); i++) {
struct ocfs2_xattr_entry *entry = &bucket_xh(bucket)->xh_entries[i];
type = ocfs2_xattr_get_type(entry);
- prefix = ocfs2_xattr_prefix(type);
- if (prefix) {
- ret = ocfs2_xattr_bucket_get_name_value(inode->i_sb,
- bucket_xh(bucket),
- i,
- &block_off,
- &new_offset);
- if (ret)
- break;
+ ret = ocfs2_xattr_bucket_get_name_value(inode->i_sb,
+ bucket_xh(bucket),
+ i,
+ &block_off,
+ &new_offset);
+ if (ret)
+ break;
- name = (const char *)bucket_block(bucket, block_off) +
- new_offset;
- ret = ocfs2_xattr_list_entry(xl->buffer,
- xl->buffer_size,
- &xl->result,
- prefix, name,
- entry->xe_name_len);
- if (ret)
- break;
- }
+ name = (const char *)bucket_block(bucket, block_off) +
+ new_offset;
+ ret = ocfs2_xattr_list_entry(inode->i_sb,
+ xl->buffer,
+ xl->buffer_size,
+ &xl->result,
+ type, name,
+ entry->xe_name_len);
+ if (ret)
+ break;
}
return ret;
@@ -7226,31 +7245,14 @@ int ocfs2_init_security_and_acl(struct inode *dir,
leave:
return ret;
}
+
/*
* 'security' attributes support
*/
-static size_t ocfs2_xattr_security_list(const struct xattr_handler *handler,
- struct dentry *dentry, char *list,
- size_t list_size, const char *name,
- size_t name_len)
-{
- const size_t prefix_len = XATTR_SECURITY_PREFIX_LEN;
- const size_t total_len = prefix_len + name_len + 1;
-
- if (list && total_len <= list_size) {
- memcpy(list, XATTR_SECURITY_PREFIX, prefix_len);
- memcpy(list + prefix_len, name, name_len);
- list[prefix_len + name_len] = '\0';
- }
- return total_len;
-}
-
static int ocfs2_xattr_security_get(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
void *buffer, size_t size)
{
- if (strcmp(name, "") == 0)
- return -EINVAL;
return ocfs2_xattr_get(d_inode(dentry), OCFS2_XATTR_INDEX_SECURITY,
name, buffer, size);
}
@@ -7259,9 +7261,6 @@ static int ocfs2_xattr_security_set(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
- if (strcmp(name, "") == 0)
- return -EINVAL;
-
return ocfs2_xattr_set(d_inode(dentry), OCFS2_XATTR_INDEX_SECURITY,
name, value, size, flags);
}
@@ -7314,7 +7313,6 @@ int ocfs2_init_security_set(handle_t *handle,
const struct xattr_handler ocfs2_xattr_security_handler = {
.prefix = XATTR_SECURITY_PREFIX,
- .list = ocfs2_xattr_security_list,
.get = ocfs2_xattr_security_get,
.set = ocfs2_xattr_security_set,
};
@@ -7322,31 +7320,10 @@ const struct xattr_handler ocfs2_xattr_security_handler = {
/*
* 'trusted' attributes support
*/
-static size_t ocfs2_xattr_trusted_list(const struct xattr_handler *handler,
- struct dentry *dentry, char *list,
- size_t list_size, const char *name,
- size_t name_len)
-{
- const size_t prefix_len = XATTR_TRUSTED_PREFIX_LEN;
- const size_t total_len = prefix_len + name_len + 1;
-
- if (!capable(CAP_SYS_ADMIN))
- return 0;
-
- if (list && total_len <= list_size) {
- memcpy(list, XATTR_TRUSTED_PREFIX, prefix_len);
- memcpy(list + prefix_len, name, name_len);
- list[prefix_len + name_len] = '\0';
- }
- return total_len;
-}
-
static int ocfs2_xattr_trusted_get(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
void *buffer, size_t size)
{
- if (strcmp(name, "") == 0)
- return -EINVAL;
return ocfs2_xattr_get(d_inode(dentry), OCFS2_XATTR_INDEX_TRUSTED,
name, buffer, size);
}
@@ -7355,16 +7332,12 @@ static int ocfs2_xattr_trusted_set(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
- if (strcmp(name, "") == 0)
- return -EINVAL;
-
return ocfs2_xattr_set(d_inode(dentry), OCFS2_XATTR_INDEX_TRUSTED,
name, value, size, flags);
}
const struct xattr_handler ocfs2_xattr_trusted_handler = {
.prefix = XATTR_TRUSTED_PREFIX,
- .list = ocfs2_xattr_trusted_list,
.get = ocfs2_xattr_trusted_get,
.set = ocfs2_xattr_trusted_set,
};
@@ -7372,34 +7345,12 @@ const struct xattr_handler ocfs2_xattr_trusted_handler = {
/*
* 'user' attributes support
*/
-static size_t ocfs2_xattr_user_list(const struct xattr_handler *handler,
- struct dentry *dentry, char *list,
- size_t list_size, const char *name,
- size_t name_len)
-{
- const size_t prefix_len = XATTR_USER_PREFIX_LEN;
- const size_t total_len = prefix_len + name_len + 1;
- struct ocfs2_super *osb = OCFS2_SB(dentry->d_sb);
-
- if (osb->s_mount_opt & OCFS2_MOUNT_NOUSERXATTR)
- return 0;
-
- if (list && total_len <= list_size) {
- memcpy(list, XATTR_USER_PREFIX, prefix_len);
- memcpy(list + prefix_len, name, name_len);
- list[prefix_len + name_len] = '\0';
- }
- return total_len;
-}
-
static int ocfs2_xattr_user_get(const struct xattr_handler *handler,
struct dentry *dentry, const char *name,
void *buffer, size_t size)
{
struct ocfs2_super *osb = OCFS2_SB(dentry->d_sb);
- if (strcmp(name, "") == 0)
- return -EINVAL;
if (osb->s_mount_opt & OCFS2_MOUNT_NOUSERXATTR)
return -EOPNOTSUPP;
return ocfs2_xattr_get(d_inode(dentry), OCFS2_XATTR_INDEX_USER, name,
@@ -7412,8 +7363,6 @@ static int ocfs2_xattr_user_set(const struct xattr_handler *handler,
{
struct ocfs2_super *osb = OCFS2_SB(dentry->d_sb);
- if (strcmp(name, "") == 0)
- return -EINVAL;
if (osb->s_mount_opt & OCFS2_MOUNT_NOUSERXATTR)
return -EOPNOTSUPP;
@@ -7423,7 +7372,6 @@ static int ocfs2_xattr_user_set(const struct xattr_handler *handler,
const struct xattr_handler ocfs2_xattr_user_handler = {
.prefix = XATTR_USER_PREFIX,
- .list = ocfs2_xattr_user_list,
.get = ocfs2_xattr_user_get,
.set = ocfs2_xattr_user_set,
};
diff --git a/fs/open.c b/fs/open.c
index b6f1e96a7c0b..b25b1542c530 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -887,7 +887,7 @@ EXPORT_SYMBOL(dentry_open);
static inline int build_open_flags(int flags, umode_t mode, struct open_flags *op)
{
int lookup_flags = 0;
- int acc_mode;
+ int acc_mode = ACC_MODE(flags);
if (flags & (O_CREAT | __O_TMPFILE))
op->mode = (mode & S_IALLUGO) | S_IFREG;
@@ -909,7 +909,6 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o
if (flags & __O_TMPFILE) {
if ((flags & O_TMPFILE_MASK) != O_TMPFILE)
return -EINVAL;
- acc_mode = MAY_OPEN | ACC_MODE(flags);
if (!(acc_mode & MAY_WRITE))
return -EINVAL;
} else if (flags & O_PATH) {
@@ -919,8 +918,6 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o
*/
flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH;
acc_mode = 0;
- } else {
- acc_mode = MAY_OPEN | ACC_MODE(flags);
}
op->open_flag = flags;
diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c
index 15e4500cda3e..b61b883c8ff8 100644
--- a/fs/openpromfs/inode.c
+++ b/fs/openpromfs/inode.c
@@ -443,7 +443,7 @@ static int __init init_openprom_fs(void)
sizeof(struct op_inode_info),
0,
(SLAB_RECLAIM_ACCOUNT |
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD | SLAB_ACCOUNT),
op_inode_init_once);
if (!op_inode_cachep)
return -ENOMEM;
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index 871fcb67be97..0a8983492d91 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -195,8 +195,7 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)
static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir,
struct dentry *dentry, struct path *lowerpath,
- struct kstat *stat, struct iattr *attr,
- const char *link)
+ struct kstat *stat, const char *link)
{
struct inode *wdir = workdir->d_inode;
struct inode *udir = upperdir->d_inode;
@@ -240,8 +239,6 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir,
mutex_lock(&newdentry->d_inode->i_mutex);
err = ovl_set_attr(newdentry, stat);
- if (!err && attr)
- err = notify_change(newdentry, attr, NULL);
mutex_unlock(&newdentry->d_inode->i_mutex);
if (err)
goto out_cleanup;
@@ -286,8 +283,7 @@ out_cleanup:
* that point the file will have already been copied up anyway.
*/
int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
- struct path *lowerpath, struct kstat *stat,
- struct iattr *attr)
+ struct path *lowerpath, struct kstat *stat)
{
struct dentry *workdir = ovl_workdir(dentry);
int err;
@@ -345,26 +341,19 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
}
upperdentry = ovl_dentry_upper(dentry);
if (upperdentry) {
- unlock_rename(workdir, upperdir);
+ /* Raced with another copy-up? Nothing to do, then... */
err = 0;
- /* Raced with another copy-up? Do the setattr here */
- if (attr) {
- mutex_lock(&upperdentry->d_inode->i_mutex);
- err = notify_change(upperdentry, attr, NULL);
- mutex_unlock(&upperdentry->d_inode->i_mutex);
- }
- goto out_put_cred;
+ goto out_unlock;
}
err = ovl_copy_up_locked(workdir, upperdir, dentry, lowerpath,
- stat, attr, link);
+ stat, link);
if (!err) {
/* Restore timestamps on parent (best effort) */
ovl_set_timestamps(upperdir, &pstat);
}
out_unlock:
unlock_rename(workdir, upperdir);
-out_put_cred:
revert_creds(old_cred);
put_cred(override_cred);
@@ -406,7 +395,7 @@ int ovl_copy_up(struct dentry *dentry)
ovl_path_lower(next, &lowerpath);
err = vfs_getattr(&lowerpath, &stat);
if (!err)
- err = ovl_copy_up_one(parent, next, &lowerpath, &stat, NULL);
+ err = ovl_copy_up_one(parent, next, &lowerpath, &stat);
dput(parent);
dput(next);
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index ec0c2a050043..964a60fa7afc 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -12,8 +12,7 @@
#include <linux/xattr.h>
#include "overlayfs.h"
-static int ovl_copy_up_last(struct dentry *dentry, struct iattr *attr,
- bool no_data)
+static int ovl_copy_up_truncate(struct dentry *dentry)
{
int err;
struct dentry *parent;
@@ -30,10 +29,8 @@ static int ovl_copy_up_last(struct dentry *dentry, struct iattr *attr,
if (err)
goto out_dput_parent;
- if (no_data)
- stat.size = 0;
-
- err = ovl_copy_up_one(parent, dentry, &lowerpath, &stat, attr);
+ stat.size = 0;
+ err = ovl_copy_up_one(parent, dentry, &lowerpath, &stat);
out_dput_parent:
dput(parent);
@@ -49,13 +46,13 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr)
if (err)
goto out;
- upperdentry = ovl_dentry_upper(dentry);
- if (upperdentry) {
+ err = ovl_copy_up(dentry);
+ if (!err) {
+ upperdentry = ovl_dentry_upper(dentry);
+
mutex_lock(&upperdentry->d_inode->i_mutex);
err = notify_change(upperdentry, attr, NULL);
mutex_unlock(&upperdentry->d_inode->i_mutex);
- } else {
- err = ovl_copy_up_last(dentry, attr, false);
}
ovl_drop_write(dentry);
out:
@@ -134,57 +131,23 @@ out_dput:
return err;
}
-
-struct ovl_link_data {
- struct dentry *realdentry;
- void *cookie;
-};
-
-static const char *ovl_follow_link(struct dentry *dentry, void **cookie)
+static const char *ovl_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
struct dentry *realdentry;
struct inode *realinode;
- struct ovl_link_data *data = NULL;
- const char *ret;
+
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
realdentry = ovl_dentry_real(dentry);
realinode = realdentry->d_inode;
- if (WARN_ON(!realinode->i_op->follow_link))
+ if (WARN_ON(!realinode->i_op->get_link))
return ERR_PTR(-EPERM);
- if (realinode->i_op->put_link) {
- data = kmalloc(sizeof(struct ovl_link_data), GFP_KERNEL);
- if (!data)
- return ERR_PTR(-ENOMEM);
- data->realdentry = realdentry;
- }
-
- 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 inode *unused, void *c)
-{
- struct inode *realinode;
- struct ovl_link_data *data = c;
-
- if (!data)
- return;
-
- realinode = data->realdentry->d_inode;
- realinode->i_op->put_link(realinode, data->cookie);
- kfree(data);
+ return realinode->i_op->get_link(realdentry, realinode, done);
}
static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
@@ -353,7 +316,7 @@ struct inode *ovl_d_select_inode(struct dentry *dentry, unsigned file_flags)
return ERR_PTR(err);
if (file_flags & O_TRUNC)
- err = ovl_copy_up_last(dentry, NULL, true);
+ err = ovl_copy_up_truncate(dentry);
else
err = ovl_copy_up(dentry);
ovl_drop_write(dentry);
@@ -381,8 +344,7 @@ static const struct inode_operations ovl_file_inode_operations = {
static const struct inode_operations ovl_symlink_inode_operations = {
.setattr = ovl_setattr,
- .follow_link = ovl_follow_link,
- .put_link = ovl_put_link,
+ .get_link = ovl_get_link,
.readlink = ovl_readlink,
.getattr = ovl_getattr,
.setxattr = ovl_setxattr,
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index ea5a40b06e3a..e17154aeaae4 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -194,7 +194,6 @@ void ovl_cleanup(struct inode *dir, struct dentry *dentry);
/* copy_up.c */
int ovl_copy_up(struct dentry *dentry);
int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
- struct path *lowerpath, struct kstat *stat,
- struct iattr *attr);
+ struct path *lowerpath, struct kstat *stat);
int ovl_copy_xattr(struct dentry *old, struct dentry *new);
int ovl_set_attr(struct dentry *upper, struct kstat *stat);
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 4adde1e2cbec..711dd5170376 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -769,8 +769,6 @@ posix_acl_xattr_get(const struct xattr_handler *handler,
struct posix_acl *acl;
int error;
- if (strcmp(name, "") != 0)
- return -EINVAL;
if (!IS_POSIXACL(d_backing_inode(dentry)))
return -EOPNOTSUPP;
if (d_is_symlink(dentry))
@@ -797,8 +795,6 @@ posix_acl_xattr_set(const struct xattr_handler *handler,
struct posix_acl *acl = NULL;
int ret;
- if (strcmp(name, "") != 0)
- return -EINVAL;
if (!IS_POSIXACL(inode))
return -EOPNOTSUPP;
if (!inode->i_op->set_acl)
@@ -827,25 +823,14 @@ out:
return ret;
}
-static size_t
-posix_acl_xattr_list(const struct xattr_handler *handler,
- struct dentry *dentry, char *list, size_t list_size,
- const char *name, size_t name_len)
+static bool
+posix_acl_xattr_list(struct dentry *dentry)
{
- const char *xname = handler->prefix;
- size_t size;
-
- if (!IS_POSIXACL(d_backing_inode(dentry)))
- return 0;
-
- size = strlen(xname) + 1;
- if (list && size <= list_size)
- memcpy(list, xname, size);
- return size;
+ return IS_POSIXACL(d_backing_inode(dentry));
}
const struct xattr_handler posix_acl_access_xattr_handler = {
- .prefix = POSIX_ACL_XATTR_ACCESS,
+ .name = XATTR_NAME_POSIX_ACL_ACCESS,
.flags = ACL_TYPE_ACCESS,
.list = posix_acl_xattr_list,
.get = posix_acl_xattr_get,
@@ -854,7 +839,7 @@ const struct xattr_handler posix_acl_access_xattr_handler = {
EXPORT_SYMBOL_GPL(posix_acl_access_xattr_handler);
const struct xattr_handler posix_acl_default_xattr_handler = {
- .prefix = POSIX_ACL_XATTR_DEFAULT,
+ .name = XATTR_NAME_POSIX_ACL_DEFAULT,
.flags = ACL_TYPE_DEFAULT,
.list = posix_acl_xattr_list,
.get = posix_acl_xattr_get,
diff --git a/fs/proc/base.c b/fs/proc/base.c
index bd3e9e68125b..2cf5d7e37375 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -1564,12 +1564,16 @@ static int proc_exe_link(struct dentry *dentry, struct path *exe_path)
return -ENOENT;
}
-static const char *proc_pid_follow_link(struct dentry *dentry, void **cookie)
+static const char *proc_pid_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
- struct inode *inode = d_inode(dentry);
struct path path;
int error = -EACCES;
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
+
/* Are we allowed to snoop on the tasks file descriptors? */
if (!proc_fd_access_allowed(inode))
goto out;
@@ -1630,7 +1634,7 @@ out:
const struct inode_operations proc_pid_link_inode_operations = {
.readlink = proc_pid_readlink,
- .follow_link = proc_pid_follow_link,
+ .get_link = proc_pid_get_link,
.setattr = proc_setattr,
};
@@ -1895,7 +1899,7 @@ static const struct dentry_operations tid_map_files_dentry_operations = {
.d_delete = pid_delete_dentry,
};
-static int proc_map_files_get_link(struct dentry *dentry, struct path *path)
+static int map_files_get_link(struct dentry *dentry, struct path *path)
{
unsigned long vm_start, vm_end;
struct vm_area_struct *vma;
@@ -1945,20 +1949,22 @@ struct map_files_info {
* path to the file in question.
*/
static const char *
-proc_map_files_follow_link(struct dentry *dentry, void **cookie)
+proc_map_files_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
if (!capable(CAP_SYS_ADMIN))
return ERR_PTR(-EPERM);
- return proc_pid_follow_link(dentry, NULL);
+ return proc_pid_get_link(dentry, inode, done);
}
/*
- * Identical to proc_pid_link_inode_operations except for follow_link()
+ * Identical to proc_pid_link_inode_operations except for get_link()
*/
static const struct inode_operations proc_map_files_link_inode_operations = {
.readlink = proc_pid_readlink,
- .follow_link = proc_map_files_follow_link,
+ .get_link = proc_map_files_get_link,
.setattr = proc_setattr,
};
@@ -1975,7 +1981,7 @@ proc_map_files_instantiate(struct inode *dir, struct dentry *dentry,
return -ENOENT;
ei = PROC_I(inode);
- ei->op.proc_get_link = proc_map_files_get_link;
+ ei->op.proc_get_link = map_files_get_link;
inode->i_op = &proc_map_files_link_inode_operations;
inode->i_size = 64;
@@ -2359,7 +2365,7 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
size_t count, loff_t *ppos)
{
struct inode * inode = file_inode(file);
- char *page;
+ void *page;
ssize_t length;
struct task_struct *task = get_proc_task(inode);
@@ -2374,14 +2380,11 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
if (*ppos != 0)
goto out;
- length = -ENOMEM;
- page = (char*)__get_free_page(GFP_TEMPORARY);
- if (!page)
+ page = memdup_user(buf, count);
+ if (IS_ERR(page)) {
+ length = PTR_ERR(page);
goto out;
-
- length = -EFAULT;
- if (copy_from_user(page, buf, count))
- goto out_free;
+ }
/* Guard against adverse ptrace interaction */
length = mutex_lock_interruptible(&task->signal->cred_guard_mutex);
@@ -2390,10 +2393,10 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
length = security_setprocattr(task,
(char*)file->f_path.dentry->d_name.name,
- (void*)page, count);
+ page, count);
mutex_unlock(&task->signal->cred_guard_mutex);
out_free:
- free_page((unsigned long) page);
+ kfree(page);
out:
put_task_struct(task);
out_no_task:
@@ -2494,6 +2497,7 @@ static ssize_t proc_coredump_filter_write(struct file *file,
mm = get_task_mm(task);
if (!mm)
goto out_no_mm;
+ ret = 0;
for (i = 0, mask = 1; i < MMF_DUMP_FILTER_BITS; i++, mask <<= 1) {
if (val & mask)
diff --git a/fs/proc/fd.c b/fs/proc/fd.c
index 3c2a915c695a..56afa5ef08f2 100644
--- a/fs/proc/fd.c
+++ b/fs/proc/fd.c
@@ -258,6 +258,7 @@ static int proc_readfd_common(struct file *file, struct dir_context *ctx,
name, len, instantiate, p,
(void *)(unsigned long)fd))
goto out_fd_loop;
+ cond_resched();
rcu_read_lock();
}
rcu_read_unlock();
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index bd95b9fdebb0..42305ddcbaa0 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -95,7 +95,8 @@ void __init proc_init_inodecache(void)
proc_inode_cachep = kmem_cache_create("proc_inode_cache",
sizeof(struct proc_inode),
0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD|SLAB_PANIC),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT|
+ SLAB_PANIC),
init_once);
}
@@ -393,24 +394,25 @@ static const struct file_operations proc_reg_file_ops_no_compat = {
};
#endif
-static const char *proc_follow_link(struct dentry *dentry, void **cookie)
+static void proc_put_link(void *p)
{
- struct proc_dir_entry *pde = PDE(d_inode(dentry));
- if (unlikely(!use_pde(pde)))
- return ERR_PTR(-EINVAL);
- *cookie = pde;
- return pde->data;
+ unuse_pde(p);
}
-static void proc_put_link(struct inode *unused, void *p)
+static const char *proc_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
- unuse_pde(p);
+ struct proc_dir_entry *pde = PDE(inode);
+ if (unlikely(!use_pde(pde)))
+ return ERR_PTR(-EINVAL);
+ set_delayed_call(done, proc_put_link, pde);
+ return pde->data;
}
const struct inode_operations proc_link_inode_operations = {
.readlink = generic_readlink,
- .follow_link = proc_follow_link,
- .put_link = proc_put_link,
+ .get_link = proc_get_link,
};
struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de)
diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c
index 9155a5a0d3b9..df4661abadc4 100644
--- a/fs/proc/meminfo.c
+++ b/fs/proc/meminfo.c
@@ -57,11 +57,8 @@ static int meminfo_proc_show(struct seq_file *m, void *v)
/*
* Estimate the amount of memory available for userspace allocations,
* without causing swapping.
- *
- * Free memory cannot be taken below the low watermark, before the
- * system starts swapping.
*/
- available = i.freeram - wmark_low;
+ available = i.freeram - totalreserve_pages;
/*
* Not all the page cache can be freed, otherwise the system will
diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c
index f6e8354b8cea..1dece8781f91 100644
--- a/fs/proc/namespaces.c
+++ b/fs/proc/namespaces.c
@@ -30,14 +30,18 @@ static const struct proc_ns_operations *ns_entries[] = {
&mntns_operations,
};
-static const char *proc_ns_follow_link(struct dentry *dentry, void **cookie)
+static const char *proc_ns_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
- struct inode *inode = d_inode(dentry);
const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops;
struct task_struct *task;
struct path ns_path;
void *error = ERR_PTR(-EACCES);
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
+
task = get_proc_task(inode);
if (!task)
return error;
@@ -74,7 +78,7 @@ static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int bufl
static const struct inode_operations proc_ns_link_inode_operations = {
.readlink = proc_ns_readlink,
- .follow_link = proc_ns_follow_link,
+ .get_link = proc_ns_get_link,
.setattr = proc_setattr,
};
diff --git a/fs/proc/self.c b/fs/proc/self.c
index 113b8d061fc0..67e8db442cf0 100644
--- a/fs/proc/self.c
+++ b/fs/proc/self.c
@@ -18,26 +18,28 @@ static int proc_self_readlink(struct dentry *dentry, char __user *buffer,
return readlink_copy(buffer, buflen, tmp);
}
-static const char *proc_self_follow_link(struct dentry *dentry, void **cookie)
+static const char *proc_self_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
- struct pid_namespace *ns = dentry->d_sb->s_fs_info;
+ struct pid_namespace *ns = inode->i_sb->s_fs_info;
pid_t tgid = task_tgid_nr_ns(current, ns);
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);
+ name = kmalloc(12, dentry ? GFP_KERNEL : GFP_ATOMIC);
+ if (unlikely(!name))
+ return dentry ? ERR_PTR(-ENOMEM) : ERR_PTR(-ECHILD);
sprintf(name, "%d", tgid);
- return *cookie = name;
+ set_delayed_call(done, kfree_link, name);
+ return name;
}
static const struct inode_operations proc_self_inode_operations = {
.readlink = proc_self_readlink,
- .follow_link = proc_self_follow_link,
- .put_link = kfree_put_link,
+ .get_link = proc_self_get_link,
};
static unsigned self_inum;
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 187b3b5f242e..a353b4c6e86e 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -14,6 +14,7 @@
#include <linux/swapops.h>
#include <linux/mmu_notifier.h>
#include <linux/page_idle.h>
+#include <linux/shmem_fs.h>
#include <asm/elf.h>
#include <asm/uaccess.h>
@@ -22,9 +23,13 @@
void task_mem(struct seq_file *m, struct mm_struct *mm)
{
- unsigned long data, text, lib, swap, ptes, pmds;
+ unsigned long text, lib, swap, ptes, pmds, anon, file, shmem;
unsigned long hiwater_vm, total_vm, hiwater_rss, total_rss;
+ anon = get_mm_counter(mm, MM_ANONPAGES);
+ file = get_mm_counter(mm, MM_FILEPAGES);
+ shmem = get_mm_counter(mm, MM_SHMEMPAGES);
+
/*
* Note: to minimize their overhead, mm maintains hiwater_vm and
* hiwater_rss only when about to *lower* total_vm or rss. Any
@@ -35,11 +40,10 @@ void task_mem(struct seq_file *m, struct mm_struct *mm)
hiwater_vm = total_vm = mm->total_vm;
if (hiwater_vm < mm->hiwater_vm)
hiwater_vm = mm->hiwater_vm;
- hiwater_rss = total_rss = get_mm_rss(mm);
+ hiwater_rss = total_rss = anon + file + shmem;
if (hiwater_rss < mm->hiwater_rss)
hiwater_rss = mm->hiwater_rss;
- data = mm->total_vm - mm->shared_vm - mm->stack_vm;
text = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK)) >> 10;
lib = (mm->exec_vm << (PAGE_SHIFT-10)) - text;
swap = get_mm_counter(mm, MM_SWAPENTS);
@@ -52,6 +56,9 @@ void task_mem(struct seq_file *m, struct mm_struct *mm)
"VmPin:\t%8lu kB\n"
"VmHWM:\t%8lu kB\n"
"VmRSS:\t%8lu kB\n"
+ "RssAnon:\t%8lu kB\n"
+ "RssFile:\t%8lu kB\n"
+ "RssShmem:\t%8lu kB\n"
"VmData:\t%8lu kB\n"
"VmStk:\t%8lu kB\n"
"VmExe:\t%8lu kB\n"
@@ -65,7 +72,10 @@ void task_mem(struct seq_file *m, struct mm_struct *mm)
mm->pinned_vm << (PAGE_SHIFT-10),
hiwater_rss << (PAGE_SHIFT-10),
total_rss << (PAGE_SHIFT-10),
- data << (PAGE_SHIFT-10),
+ anon << (PAGE_SHIFT-10),
+ file << (PAGE_SHIFT-10),
+ shmem << (PAGE_SHIFT-10),
+ mm->data_vm << (PAGE_SHIFT-10),
mm->stack_vm << (PAGE_SHIFT-10), text, lib,
ptes >> 10,
pmds >> 10,
@@ -82,10 +92,11 @@ unsigned long task_statm(struct mm_struct *mm,
unsigned long *shared, unsigned long *text,
unsigned long *data, unsigned long *resident)
{
- *shared = get_mm_counter(mm, MM_FILEPAGES);
+ *shared = get_mm_counter(mm, MM_FILEPAGES) +
+ get_mm_counter(mm, MM_SHMEMPAGES);
*text = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK))
>> PAGE_SHIFT;
- *data = mm->total_vm - mm->shared_vm;
+ *data = mm->data_vm + mm->stack_vm;
*resident = *shared + get_mm_counter(mm, MM_ANONPAGES);
return mm->total_vm;
}
@@ -451,6 +462,7 @@ struct mem_size_stats {
unsigned long private_hugetlb;
u64 pss;
u64 swap_pss;
+ bool check_shmem_swap;
};
static void smaps_account(struct mem_size_stats *mss, struct page *page,
@@ -485,6 +497,19 @@ static void smaps_account(struct mem_size_stats *mss, struct page *page,
}
}
+#ifdef CONFIG_SHMEM
+static int smaps_pte_hole(unsigned long addr, unsigned long end,
+ struct mm_walk *walk)
+{
+ struct mem_size_stats *mss = walk->private;
+
+ mss->swap += shmem_partial_swap_usage(
+ walk->vma->vm_file->f_mapping, addr, end);
+
+ return 0;
+}
+#endif
+
static void smaps_pte_entry(pte_t *pte, unsigned long addr,
struct mm_walk *walk)
{
@@ -512,6 +537,19 @@ static void smaps_pte_entry(pte_t *pte, unsigned long addr,
}
} else if (is_migration_entry(swpent))
page = migration_entry_to_page(swpent);
+ } else if (unlikely(IS_ENABLED(CONFIG_SHMEM) && mss->check_shmem_swap
+ && pte_none(*pte))) {
+ page = find_get_entry(vma->vm_file->f_mapping,
+ linear_page_index(vma, addr));
+ if (!page)
+ return;
+
+ if (radix_tree_exceptional_entry(page))
+ mss->swap += PAGE_SIZE;
+ else
+ page_cache_release(page);
+
+ return;
}
if (!page)
@@ -671,6 +709,31 @@ static int show_smap(struct seq_file *m, void *v, int is_pid)
};
memset(&mss, 0, sizeof mss);
+
+#ifdef CONFIG_SHMEM
+ if (vma->vm_file && shmem_mapping(vma->vm_file->f_mapping)) {
+ /*
+ * For shared or readonly shmem mappings we know that all
+ * swapped out pages belong to the shmem object, and we can
+ * obtain the swap value much more efficiently. For private
+ * writable mappings, we might have COW pages that are
+ * not affected by the parent swapped out pages of the shmem
+ * object, so we have to distinguish them during the page walk.
+ * Unless we know that the shmem object (or the part mapped by
+ * our VMA) has no swapped out pages at all.
+ */
+ unsigned long shmem_swapped = shmem_swap_usage(vma);
+
+ if (!shmem_swapped || (vma->vm_flags & VM_SHARED) ||
+ !(vma->vm_flags & VM_WRITE)) {
+ mss.swap = shmem_swapped;
+ } else {
+ mss.check_shmem_swap = true;
+ smaps_walk.pte_hole = smaps_pte_hole;
+ }
+ }
+#endif
+
/* mmap_sem is held in m_start */
walk_page_vma(vma, &smaps_walk);
@@ -817,9 +880,6 @@ static inline void clear_soft_dirty_pmd(struct vm_area_struct *vma,
pmd = pmd_wrprotect(pmd);
pmd = pmd_clear_soft_dirty(pmd);
- if (vma->vm_flags & VM_SOFTDIRTY)
- vma->vm_flags &= ~VM_SOFTDIRTY;
-
set_pmd_at(vma->vm_mm, addr, pmdp, pmd);
}
#else
diff --git a/fs/proc/thread_self.c b/fs/proc/thread_self.c
index 947b0f4fd0a1..9eacd59e0360 100644
--- a/fs/proc/thread_self.c
+++ b/fs/proc/thread_self.c
@@ -19,26 +19,29 @@ static int proc_thread_self_readlink(struct dentry *dentry, char __user *buffer,
return readlink_copy(buffer, buflen, tmp);
}
-static const char *proc_thread_self_follow_link(struct dentry *dentry, void **cookie)
+static const char *proc_thread_self_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
- struct pid_namespace *ns = dentry->d_sb->s_fs_info;
+ struct pid_namespace *ns = inode->i_sb->s_fs_info;
pid_t tgid = task_tgid_nr_ns(current, ns);
pid_t pid = task_pid_nr_ns(current, ns);
char *name;
if (!pid)
return ERR_PTR(-ENOENT);
- name = kmalloc(PROC_NUMBUF + 6 + PROC_NUMBUF, GFP_KERNEL);
- if (!name)
- return ERR_PTR(-ENOMEM);
+ name = kmalloc(PROC_NUMBUF + 6 + PROC_NUMBUF,
+ dentry ? GFP_KERNEL : GFP_ATOMIC);
+ if (unlikely(!name))
+ return dentry ? ERR_PTR(-ENOMEM) : ERR_PTR(-ECHILD);
sprintf(name, "%d/task/%d", tgid, pid);
- return *cookie = name;
+ set_delayed_call(done, kfree_link, name);
+ return name;
}
static const struct inode_operations proc_thread_self_inode_operations = {
.readlink = proc_thread_self_readlink,
- .follow_link = proc_thread_self_follow_link,
- .put_link = kfree_put_link,
+ .get_link = proc_thread_self_get_link,
};
static unsigned thread_self_inum;
diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c
index 8ebd9a334085..2256e7e23e67 100644
--- a/fs/proc_namespace.c
+++ b/fs/proc_namespace.c
@@ -95,9 +95,9 @@ static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt)
{
struct proc_mounts *p = m->private;
struct mount *r = real_mount(mnt);
- int err = 0;
struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
struct super_block *sb = mnt_path.dentry->d_sb;
+ int err;
if (sb->s_op->show_devname) {
err = sb->s_op->show_devname(m, mnt_path.dentry);
@@ -131,16 +131,17 @@ static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt)
struct mount *r = real_mount(mnt);
struct super_block *sb = mnt->mnt_sb;
struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
- int err = 0;
+ int err;
seq_printf(m, "%i %i %u:%u ", r->mnt_id, r->mnt_parent->mnt_id,
MAJOR(sb->s_dev), MINOR(sb->s_dev));
- if (sb->s_op->show_path)
+ if (sb->s_op->show_path) {
err = sb->s_op->show_path(m, mnt->mnt_root);
- else
+ if (err)
+ goto out;
+ } else {
seq_dentry(m, mnt->mnt_root, " \t\n\\");
- if (err)
- goto out;
+ }
seq_putc(m, ' ');
/* mountpoints outside of chroot jail will give SEQ_SKIP on this */
@@ -168,12 +169,13 @@ static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt)
seq_puts(m, " - ");
show_type(m, sb);
seq_putc(m, ' ');
- if (sb->s_op->show_devname)
+ if (sb->s_op->show_devname) {
err = sb->s_op->show_devname(m, mnt->mnt_root);
- else
+ if (err)
+ goto out;
+ } else {
mangle(m, r->mnt_devname ? r->mnt_devname : "none");
- if (err)
- goto out;
+ }
seq_puts(m, sb->s_flags & MS_RDONLY ? " ro" : " rw");
err = show_sb_opts(m, sb);
if (err)
@@ -191,7 +193,7 @@ static int show_vfsstat(struct seq_file *m, struct vfsmount *mnt)
struct mount *r = real_mount(mnt);
struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
struct super_block *sb = mnt_path.dentry->d_sb;
- int err = 0;
+ int err;
/* device */
if (sb->s_op->show_devname) {
@@ -220,8 +222,7 @@ static int show_vfsstat(struct seq_file *m, struct vfsmount *mnt)
/* optional statistics */
if (sb->s_op->show_stats) {
seq_putc(m, ' ');
- if (!err)
- err = sb->s_op->show_stats(m, mnt_path.dentry);
+ err = sb->s_op->show_stats(m, mnt_path.dentry);
}
seq_putc(m, '\n');
diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c
index c4bcb778886e..3a67cfb142d8 100644
--- a/fs/qnx4/inode.c
+++ b/fs/qnx4/inode.c
@@ -316,6 +316,7 @@ struct inode *qnx4_iget(struct super_block *sb, unsigned long ino)
inode->i_fop = &qnx4_dir_operations;
} else if (S_ISLNK(inode->i_mode)) {
inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_mapping->a_ops = &qnx4_aops;
qnx4_i(inode)->mmu_private = inode->i_size;
} else {
@@ -364,7 +365,7 @@ static int init_inodecache(void)
qnx4_inode_cachep = kmem_cache_create("qnx4_inode_cache",
sizeof(struct qnx4_inode_info),
0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
init_once);
if (qnx4_inode_cachep == NULL)
return -ENOMEM;
diff --git a/fs/qnx6/inode.c b/fs/qnx6/inode.c
index 32d2e1a9774c..47bb1de07155 100644
--- a/fs/qnx6/inode.c
+++ b/fs/qnx6/inode.c
@@ -582,6 +582,7 @@ struct inode *qnx6_iget(struct super_block *sb, unsigned ino)
inode->i_mapping->a_ops = &qnx6_aops;
} else if (S_ISLNK(inode->i_mode)) {
inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_mapping->a_ops = &qnx6_aops;
} else
init_special_inode(inode, inode->i_mode, 0);
@@ -624,7 +625,7 @@ static int init_inodecache(void)
qnx6_inode_cachep = kmem_cache_create("qnx6_inode_cache",
sizeof(struct qnx6_inode_info),
0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
init_once);
if (!qnx6_inode_cachep)
return -ENOMEM;
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index ef0d64b2a6d9..fbd70af98820 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -2924,4 +2924,4 @@ static int __init dquot_init(void)
return 0;
}
-module_init(dquot_init);
+fs_initcall(dquot_init);
diff --git a/fs/quota/netlink.c b/fs/quota/netlink.c
index bb2869f5dfd8..d07a2f91d858 100644
--- a/fs/quota/netlink.c
+++ b/fs/quota/netlink.c
@@ -1,7 +1,5 @@
-
#include <linux/cred.h>
#include <linux/init.h>
-#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/quotaops.h>
#include <linux/sched.h>
@@ -105,5 +103,4 @@ static int __init quota_init(void)
"VFS: Failed to create quota netlink interface.\n");
return 0;
};
-
-module_init(quota_init);
+fs_initcall(quota_init);
diff --git a/fs/quota/quota_v2.c b/fs/quota/quota_v2.c
index 2aa012a68e90..ed85d4f35c04 100644
--- a/fs/quota/quota_v2.c
+++ b/fs/quota/quota_v2.c
@@ -30,13 +30,13 @@ static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot);
static void v2r1_disk2memdqb(struct dquot *dquot, void *dp);
static int v2r1_is_id(void *dp, struct dquot *dquot);
-static struct qtree_fmt_operations v2r0_qtree_ops = {
+static const struct qtree_fmt_operations v2r0_qtree_ops = {
.mem2disk_dqblk = v2r0_mem2diskdqb,
.disk2mem_dqblk = v2r0_disk2memdqb,
.is_id = v2r0_is_id,
};
-static struct qtree_fmt_operations v2r1_qtree_ops = {
+static const struct qtree_fmt_operations v2r1_qtree_ops = {
.mem2disk_dqblk = v2r1_mem2diskdqb,
.disk2mem_dqblk = v2r1_disk2memdqb,
.is_id = v2r1_is_id,
diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c
index 889d558b4e05..38981b037524 100644
--- a/fs/ramfs/inode.c
+++ b/fs/ramfs/inode.c
@@ -79,6 +79,7 @@ struct inode *ramfs_get_inode(struct super_block *sb,
break;
case S_IFLNK:
inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
break;
}
}
diff --git a/fs/read_write.c b/fs/read_write.c
index 819ef3faf1bb..06b07d5a08fe 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -16,6 +16,7 @@
#include <linux/pagemap.h>
#include <linux/splice.h>
#include <linux/compat.h>
+#include <linux/mount.h>
#include "internal.h"
#include <asm/uaccess.h>
@@ -171,6 +172,45 @@ loff_t fixed_size_llseek(struct file *file, loff_t offset, int whence, loff_t si
EXPORT_SYMBOL(fixed_size_llseek);
/**
+ * no_seek_end_llseek - llseek implementation for fixed-sized devices
+ * @file: file structure to seek on
+ * @offset: file offset to seek to
+ * @whence: type of seek
+ *
+ */
+loff_t no_seek_end_llseek(struct file *file, loff_t offset, int whence)
+{
+ switch (whence) {
+ case SEEK_SET: case SEEK_CUR:
+ return generic_file_llseek_size(file, offset, whence,
+ ~0ULL, 0);
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(no_seek_end_llseek);
+
+/**
+ * no_seek_end_llseek_size - llseek implementation for fixed-sized devices
+ * @file: file structure to seek on
+ * @offset: file offset to seek to
+ * @whence: type of seek
+ * @size: maximal offset allowed
+ *
+ */
+loff_t no_seek_end_llseek_size(struct file *file, loff_t offset, int whence, loff_t size)
+{
+ switch (whence) {
+ case SEEK_SET: case SEEK_CUR:
+ return generic_file_llseek_size(file, offset, whence,
+ size, 0);
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(no_seek_end_llseek_size);
+
+/**
* noop_llseek - No Operation Performed llseek implementation
* @file: file structure to seek on
* @offset: file offset to seek to
@@ -395,9 +435,8 @@ int rw_verify_area(int read_write, struct file *file, const loff_t *ppos, size_t
}
if (unlikely(inode->i_flctx && mandatory_lock(inode))) {
- retval = locks_mandatory_area(
- read_write == READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,
- inode, file, pos, count);
+ retval = locks_mandatory_area(inode, file, pos, pos + count - 1,
+ read_write == READ ? F_RDLCK : F_WRLCK);
if (retval < 0)
return retval;
}
@@ -1327,3 +1366,299 @@ COMPAT_SYSCALL_DEFINE4(sendfile64, int, out_fd, int, in_fd,
return do_sendfile(out_fd, in_fd, NULL, count, 0);
}
#endif
+
+/*
+ * copy_file_range() differs from regular file read and write in that it
+ * specifically allows return partial success. When it does so is up to
+ * the copy_file_range method.
+ */
+ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
+ struct file *file_out, loff_t pos_out,
+ size_t len, unsigned int flags)
+{
+ struct inode *inode_in = file_inode(file_in);
+ struct inode *inode_out = file_inode(file_out);
+ ssize_t ret;
+
+ if (flags != 0)
+ return -EINVAL;
+
+ /* copy_file_range allows full ssize_t len, ignoring MAX_RW_COUNT */
+ ret = rw_verify_area(READ, file_in, &pos_in, len);
+ if (ret >= 0)
+ ret = rw_verify_area(WRITE, file_out, &pos_out, len);
+ if (ret < 0)
+ return ret;
+
+ if (!(file_in->f_mode & FMODE_READ) ||
+ !(file_out->f_mode & FMODE_WRITE) ||
+ (file_out->f_flags & O_APPEND))
+ return -EBADF;
+
+ /* this could be relaxed once a method supports cross-fs copies */
+ if (inode_in->i_sb != inode_out->i_sb)
+ return -EXDEV;
+
+ if (len == 0)
+ return 0;
+
+ ret = mnt_want_write_file(file_out);
+ if (ret)
+ return ret;
+
+ ret = -EOPNOTSUPP;
+ if (file_out->f_op->copy_file_range)
+ ret = file_out->f_op->copy_file_range(file_in, pos_in, file_out,
+ pos_out, len, flags);
+ if (ret == -EOPNOTSUPP)
+ ret = do_splice_direct(file_in, &pos_in, file_out, &pos_out,
+ len > MAX_RW_COUNT ? MAX_RW_COUNT : len, 0);
+
+ if (ret > 0) {
+ fsnotify_access(file_in);
+ add_rchar(current, ret);
+ fsnotify_modify(file_out);
+ add_wchar(current, ret);
+ }
+ inc_syscr(current);
+ inc_syscw(current);
+
+ mnt_drop_write_file(file_out);
+
+ return ret;
+}
+EXPORT_SYMBOL(vfs_copy_file_range);
+
+SYSCALL_DEFINE6(copy_file_range, int, fd_in, loff_t __user *, off_in,
+ int, fd_out, loff_t __user *, off_out,
+ size_t, len, unsigned int, flags)
+{
+ loff_t pos_in;
+ loff_t pos_out;
+ struct fd f_in;
+ struct fd f_out;
+ ssize_t ret = -EBADF;
+
+ f_in = fdget(fd_in);
+ if (!f_in.file)
+ goto out2;
+
+ f_out = fdget(fd_out);
+ if (!f_out.file)
+ goto out1;
+
+ ret = -EFAULT;
+ if (off_in) {
+ if (copy_from_user(&pos_in, off_in, sizeof(loff_t)))
+ goto out;
+ } else {
+ pos_in = f_in.file->f_pos;
+ }
+
+ if (off_out) {
+ if (copy_from_user(&pos_out, off_out, sizeof(loff_t)))
+ goto out;
+ } else {
+ pos_out = f_out.file->f_pos;
+ }
+
+ ret = vfs_copy_file_range(f_in.file, pos_in, f_out.file, pos_out, len,
+ flags);
+ if (ret > 0) {
+ pos_in += ret;
+ pos_out += ret;
+
+ if (off_in) {
+ if (copy_to_user(off_in, &pos_in, sizeof(loff_t)))
+ ret = -EFAULT;
+ } else {
+ f_in.file->f_pos = pos_in;
+ }
+
+ if (off_out) {
+ if (copy_to_user(off_out, &pos_out, sizeof(loff_t)))
+ ret = -EFAULT;
+ } else {
+ f_out.file->f_pos = pos_out;
+ }
+ }
+
+out:
+ fdput(f_out);
+out1:
+ fdput(f_in);
+out2:
+ return ret;
+}
+
+static int clone_verify_area(struct file *file, loff_t pos, u64 len, bool write)
+{
+ struct inode *inode = file_inode(file);
+
+ if (unlikely(pos < 0))
+ return -EINVAL;
+
+ if (unlikely((loff_t) (pos + len) < 0))
+ return -EINVAL;
+
+ if (unlikely(inode->i_flctx && mandatory_lock(inode))) {
+ loff_t end = len ? pos + len - 1 : OFFSET_MAX;
+ int retval;
+
+ retval = locks_mandatory_area(inode, file, pos, end,
+ write ? F_WRLCK : F_RDLCK);
+ if (retval < 0)
+ return retval;
+ }
+
+ return security_file_permission(file, write ? MAY_WRITE : MAY_READ);
+}
+
+int vfs_clone_file_range(struct file *file_in, loff_t pos_in,
+ struct file *file_out, loff_t pos_out, u64 len)
+{
+ struct inode *inode_in = file_inode(file_in);
+ struct inode *inode_out = file_inode(file_out);
+ int ret;
+
+ if (inode_in->i_sb != inode_out->i_sb ||
+ file_in->f_path.mnt != file_out->f_path.mnt)
+ return -EXDEV;
+
+ if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode))
+ return -EISDIR;
+ if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode))
+ return -EINVAL;
+
+ if (!(file_in->f_mode & FMODE_READ) ||
+ !(file_out->f_mode & FMODE_WRITE) ||
+ (file_out->f_flags & O_APPEND) ||
+ !file_in->f_op->clone_file_range)
+ return -EBADF;
+
+ ret = clone_verify_area(file_in, pos_in, len, false);
+ if (ret)
+ return ret;
+
+ ret = clone_verify_area(file_out, pos_out, len, true);
+ if (ret)
+ return ret;
+
+ if (pos_in + len > i_size_read(inode_in))
+ return -EINVAL;
+
+ ret = mnt_want_write_file(file_out);
+ if (ret)
+ return ret;
+
+ ret = file_in->f_op->clone_file_range(file_in, pos_in,
+ file_out, pos_out, len);
+ if (!ret) {
+ fsnotify_access(file_in);
+ fsnotify_modify(file_out);
+ }
+
+ mnt_drop_write_file(file_out);
+ return ret;
+}
+EXPORT_SYMBOL(vfs_clone_file_range);
+
+int vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same)
+{
+ struct file_dedupe_range_info *info;
+ struct inode *src = file_inode(file);
+ u64 off;
+ u64 len;
+ int i;
+ int ret;
+ bool is_admin = capable(CAP_SYS_ADMIN);
+ u16 count = same->dest_count;
+ struct file *dst_file;
+ loff_t dst_off;
+ ssize_t deduped;
+
+ if (!(file->f_mode & FMODE_READ))
+ return -EINVAL;
+
+ if (same->reserved1 || same->reserved2)
+ return -EINVAL;
+
+ off = same->src_offset;
+ len = same->src_length;
+
+ ret = -EISDIR;
+ if (S_ISDIR(src->i_mode))
+ goto out;
+
+ ret = -EINVAL;
+ if (!S_ISREG(src->i_mode))
+ goto out;
+
+ ret = clone_verify_area(file, off, len, false);
+ if (ret < 0)
+ goto out;
+ ret = 0;
+
+ /* pre-format output fields to sane values */
+ for (i = 0; i < count; i++) {
+ same->info[i].bytes_deduped = 0ULL;
+ same->info[i].status = FILE_DEDUPE_RANGE_SAME;
+ }
+
+ for (i = 0, info = same->info; i < count; i++, info++) {
+ struct inode *dst;
+ struct fd dst_fd = fdget(info->dest_fd);
+
+ dst_file = dst_fd.file;
+ if (!dst_file) {
+ info->status = -EBADF;
+ goto next_loop;
+ }
+ dst = file_inode(dst_file);
+
+ ret = mnt_want_write_file(dst_file);
+ if (ret) {
+ info->status = ret;
+ goto next_loop;
+ }
+
+ dst_off = info->dest_offset;
+ ret = clone_verify_area(dst_file, dst_off, len, true);
+ if (ret < 0) {
+ info->status = ret;
+ goto next_file;
+ }
+ ret = 0;
+
+ if (info->reserved) {
+ info->status = -EINVAL;
+ } else if (!(is_admin || (dst_file->f_mode & FMODE_WRITE))) {
+ info->status = -EINVAL;
+ } else if (file->f_path.mnt != dst_file->f_path.mnt) {
+ info->status = -EXDEV;
+ } else if (S_ISDIR(dst->i_mode)) {
+ info->status = -EISDIR;
+ } else if (dst_file->f_op->dedupe_file_range == NULL) {
+ info->status = -EINVAL;
+ } else {
+ deduped = dst_file->f_op->dedupe_file_range(file, off,
+ len, dst_file,
+ info->dest_offset);
+ if (deduped == -EBADE)
+ info->status = FILE_DEDUPE_RANGE_DIFFERS;
+ else if (deduped < 0)
+ info->status = deduped;
+ else
+ info->bytes_deduped += deduped;
+ }
+
+next_file:
+ mnt_drop_write_file(dst_file);
+next_loop:
+ fdput(dst_fd);
+ }
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL(vfs_dedupe_file_range);
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index 3d8e7e671d5b..ae9e5b308cf9 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -1361,6 +1361,7 @@ static void init_inode(struct inode *inode, struct treepath *path)
inode->i_fop = &reiserfs_dir_operations;
} else if (S_ISLNK(inode->i_mode)) {
inode->i_op = &reiserfs_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_mapping->a_ops = &reiserfs_address_space_operations;
} else {
inode->i_blocks = 0;
diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c
index 9d6486d416a3..44c2bdced1c8 100644
--- a/fs/reiserfs/journal.c
+++ b/fs/reiserfs/journal.c
@@ -618,12 +618,10 @@ static void release_buffer_page(struct buffer_head *bh)
static void reiserfs_end_buffer_io_sync(struct buffer_head *bh, int uptodate)
{
- char b[BDEVNAME_SIZE];
-
if (buffer_journaled(bh)) {
reiserfs_warning(NULL, "clm-2084",
- "pinned buffer %lu:%s sent to disk",
- bh->b_blocknr, bdevname(bh->b_bdev, b));
+ "pinned buffer %lu:%pg sent to disk",
+ bh->b_blocknr, bh->b_bdev);
}
if (uptodate)
set_buffer_uptodate(bh);
@@ -2387,11 +2385,10 @@ static int journal_read(struct super_block *sb)
int replay_count = 0;
int continue_replay = 1;
int ret;
- char b[BDEVNAME_SIZE];
cur_dblock = SB_ONDISK_JOURNAL_1st_BLOCK(sb);
- reiserfs_info(sb, "checking transaction log (%s)\n",
- bdevname(journal->j_dev_bd, b));
+ reiserfs_info(sb, "checking transaction log (%pg)\n",
+ journal->j_dev_bd);
start = get_seconds();
/*
@@ -2651,8 +2648,8 @@ static int journal_init_dev(struct super_block *super,
set_blocksize(journal->j_dev_bd, super->s_blocksize);
reiserfs_info(super,
- "journal_init_dev: journal device: %s\n",
- bdevname(journal->j_dev_bd, b));
+ "journal_init_dev: journal device: %pg\n",
+ journal->j_dev_bd);
return 0;
}
@@ -2724,7 +2721,6 @@ int journal_init(struct super_block *sb, const char *j_dev_name,
struct reiserfs_journal_header *jh;
struct reiserfs_journal *journal;
struct reiserfs_journal_list *jl;
- char b[BDEVNAME_SIZE];
int ret;
journal = SB_JOURNAL(sb) = vzalloc(sizeof(struct reiserfs_journal));
@@ -2794,10 +2790,10 @@ int journal_init(struct super_block *sb, const char *j_dev_name,
&& (le32_to_cpu(jh->jh_journal.jp_journal_magic) !=
sb_jp_journal_magic(rs))) {
reiserfs_warning(sb, "sh-460",
- "journal header magic %x (device %s) does "
+ "journal header magic %x (device %pg) does "
"not match to magic found in super block %x",
jh->jh_journal.jp_journal_magic,
- bdevname(journal->j_dev_bd, b),
+ journal->j_dev_bd,
sb_jp_journal_magic(rs));
brelse(bhjh);
goto free_and_return;
@@ -2818,10 +2814,10 @@ int journal_init(struct super_block *sb, const char *j_dev_name,
journal->j_max_trans_age = commit_max_age;
}
- reiserfs_info(sb, "journal params: device %s, size %u, "
+ reiserfs_info(sb, "journal params: device %pg, size %u, "
"journal first block %u, max trans len %u, max batch %u, "
"max commit age %u, max trans age %u\n",
- bdevname(journal->j_dev_bd, b),
+ journal->j_dev_bd,
SB_ONDISK_JOURNAL_SIZE(sb),
SB_ONDISK_JOURNAL_1st_BLOCK(sb),
journal->j_trans_max,
diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c
index 47f96988fdd4..2a12d46d7fb4 100644
--- a/fs/reiserfs/namei.c
+++ b/fs/reiserfs/namei.c
@@ -1170,6 +1170,7 @@ static int reiserfs_symlink(struct inode *parent_dir,
reiserfs_update_inode_transaction(parent_dir);
inode->i_op = &reiserfs_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_mapping->a_ops = &reiserfs_address_space_operations;
retval = reiserfs_add_entry(&th, parent_dir, dentry->d_name.name,
@@ -1664,8 +1665,7 @@ const struct inode_operations reiserfs_dir_inode_operations = {
*/
const struct inode_operations reiserfs_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = page_follow_link_light,
- .put_link = page_put_link,
+ .get_link = page_get_link,
.setattr = reiserfs_setattr,
.setxattr = reiserfs_setxattr,
.getxattr = reiserfs_getxattr,
diff --git a/fs/reiserfs/prints.c b/fs/reiserfs/prints.c
index ae1dc841db3a..4f3f928076f3 100644
--- a/fs/reiserfs/prints.c
+++ b/fs/reiserfs/prints.c
@@ -139,11 +139,9 @@ static void sprintf_block_head(char *buf, struct buffer_head *bh)
static void sprintf_buffer_head(char *buf, struct buffer_head *bh)
{
- char b[BDEVNAME_SIZE];
-
sprintf(buf,
- "dev %s, size %zd, blocknr %llu, count %d, state 0x%lx, page %p, (%s, %s, %s)",
- bdevname(bh->b_bdev, b), bh->b_size,
+ "dev %pg, size %zd, blocknr %llu, count %d, state 0x%lx, page %p, (%s, %s, %s)",
+ bh->b_bdev, bh->b_size,
(unsigned long long)bh->b_blocknr, atomic_read(&(bh->b_count)),
bh->b_state, bh->b_page,
buffer_uptodate(bh) ? "UPTODATE" : "!UPTODATE",
@@ -530,7 +528,6 @@ static int print_super_block(struct buffer_head *bh)
(struct reiserfs_super_block *)(bh->b_data);
int skipped, data_blocks;
char *version;
- char b[BDEVNAME_SIZE];
if (is_reiserfs_3_5(rs)) {
version = "3.5";
@@ -543,7 +540,7 @@ static int print_super_block(struct buffer_head *bh)
return 1;
}
- printk("%s\'s super block is in block %llu\n", bdevname(bh->b_bdev, b),
+ printk("%pg\'s super block is in block %llu\n", bh->b_bdev,
(unsigned long long)bh->b_blocknr);
printk("Reiserfs version %s\n", version);
printk("Block count %u\n", sb_block_count(rs));
diff --git a/fs/reiserfs/procfs.c b/fs/reiserfs/procfs.c
index 621b9f381fe1..fe999157dd97 100644
--- a/fs/reiserfs/procfs.c
+++ b/fs/reiserfs/procfs.c
@@ -303,11 +303,10 @@ static int show_journal(struct seq_file *m, void *unused)
struct reiserfs_sb_info *r = REISERFS_SB(sb);
struct reiserfs_super_block *rs = r->s_rs;
struct journal_params *jp = &rs->s_v1.s_journal;
- char b[BDEVNAME_SIZE];
seq_printf(m, /* on-disk fields */
"jp_journal_1st_block: \t%i\n"
- "jp_journal_dev: \t%s[%x]\n"
+ "jp_journal_dev: \t%pg[%x]\n"
"jp_journal_size: \t%i\n"
"jp_journal_trans_max: \t%i\n"
"jp_journal_magic: \t%i\n"
@@ -348,7 +347,7 @@ static int show_journal(struct seq_file *m, void *unused)
"prepare: \t%12lu\n"
"prepare_retry: \t%12lu\n",
DJP(jp_journal_1st_block),
- bdevname(SB_JOURNAL(sb)->j_dev_bd, b),
+ SB_JOURNAL(sb)->j_dev_bd,
DJP(jp_journal_dev),
DJP(jp_journal_size),
DJP(jp_journal_trans_max),
diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c
index 4a62fe8cc3bf..05db7473bcb5 100644
--- a/fs/reiserfs/super.c
+++ b/fs/reiserfs/super.c
@@ -626,7 +626,8 @@ static int __init init_inodecache(void)
sizeof(struct
reiserfs_inode_info),
0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|
+ SLAB_ACCOUNT),
init_once);
if (reiserfs_inode_cachep == NULL)
return -ENOMEM;
diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c
index 66b26fdfff8d..e5ddb4e5ea94 100644
--- a/fs/reiserfs/xattr.c
+++ b/fs/reiserfs/xattr.c
@@ -756,7 +756,8 @@ find_xattr_handler_prefix(const struct xattr_handler **handlers,
return NULL;
for_each_xattr_handler(handlers, xah) {
- if (strncmp(xah->prefix, name, strlen(xah->prefix)) == 0)
+ const char *prefix = xattr_prefix(xah);
+ if (strncmp(prefix, name, strlen(prefix)) == 0)
break;
}
@@ -839,19 +840,16 @@ static int listxattr_filler(struct dir_context *ctx, const char *name,
handler = find_xattr_handler_prefix(b->dentry->d_sb->s_xattr,
name);
- if (!handler) /* Unsupported xattr name */
+ if (!handler /* Unsupported xattr name */ ||
+ (handler->list && !handler->list(b->dentry)))
return 0;
+ size = namelen + 1;
if (b->buf) {
- size = handler->list(handler, b->dentry,
- b->buf + b->pos, b->size, name,
- namelen);
if (size > b->size)
return -ERANGE;
- } else {
- size = handler->list(handler, b->dentry,
- NULL, 0, name, namelen);
+ memcpy(b->buf + b->pos, name, namelen);
+ b->buf[b->pos + namelen] = 0;
}
-
b->pos += size;
}
return 0;
diff --git a/fs/reiserfs/xattr_acl.c b/fs/reiserfs/xattr_acl.c
index 4b34b9dc03dd..558a16beaacb 100644
--- a/fs/reiserfs/xattr_acl.c
+++ b/fs/reiserfs/xattr_acl.c
@@ -186,10 +186,10 @@ struct posix_acl *reiserfs_get_acl(struct inode *inode, int type)
switch (type) {
case ACL_TYPE_ACCESS:
- name = POSIX_ACL_XATTR_ACCESS;
+ name = XATTR_NAME_POSIX_ACL_ACCESS;
break;
case ACL_TYPE_DEFAULT:
- name = POSIX_ACL_XATTR_DEFAULT;
+ name = XATTR_NAME_POSIX_ACL_DEFAULT;
break;
default:
BUG();
@@ -244,7 +244,7 @@ __reiserfs_set_acl(struct reiserfs_transaction_handle *th, struct inode *inode,
switch (type) {
case ACL_TYPE_ACCESS:
- name = POSIX_ACL_XATTR_ACCESS;
+ name = XATTR_NAME_POSIX_ACL_ACCESS;
if (acl) {
error = posix_acl_equiv_mode(acl, &inode->i_mode);
if (error < 0)
@@ -256,7 +256,7 @@ __reiserfs_set_acl(struct reiserfs_transaction_handle *th, struct inode *inode,
}
break;
case ACL_TYPE_DEFAULT:
- name = POSIX_ACL_XATTR_DEFAULT;
+ name = XATTR_NAME_POSIX_ACL_DEFAULT;
if (!S_ISDIR(inode->i_mode))
return acl ? -EACCES : 0;
break;
diff --git a/fs/reiserfs/xattr_security.c b/fs/reiserfs/xattr_security.c
index ac659af431ae..ab0217d32039 100644
--- a/fs/reiserfs/xattr_security.c
+++ b/fs/reiserfs/xattr_security.c
@@ -34,21 +34,9 @@ security_set(const struct xattr_handler *handler, struct dentry *dentry,
return reiserfs_xattr_set(d_inode(dentry), name, buffer, size, flags);
}
-static size_t security_list(const struct xattr_handler *handler,
- struct dentry *dentry, char *list, size_t list_len,
- const char *name, size_t namelen)
+static bool security_list(struct dentry *dentry)
{
- const size_t len = namelen + 1;
-
- if (IS_PRIVATE(d_inode(dentry)))
- return 0;
-
- if (list && len <= list_len) {
- memcpy(list, name, namelen);
- list[namelen] = '\0';
- }
-
- return len;
+ return !IS_PRIVATE(d_inode(dentry));
}
/* Initializes the security context for a new inode and returns the number
diff --git a/fs/reiserfs/xattr_trusted.c b/fs/reiserfs/xattr_trusted.c
index a338adf1b8b4..64b67aa643a9 100644
--- a/fs/reiserfs/xattr_trusted.c
+++ b/fs/reiserfs/xattr_trusted.c
@@ -33,20 +33,9 @@ trusted_set(const struct xattr_handler *handler, struct dentry *dentry,
return reiserfs_xattr_set(d_inode(dentry), name, buffer, size, flags);
}
-static size_t trusted_list(const struct xattr_handler *handler,
- struct dentry *dentry, char *list, size_t list_size,
- const char *name, size_t name_len)
+static bool trusted_list(struct dentry *dentry)
{
- const size_t len = name_len + 1;
-
- if (!capable(CAP_SYS_ADMIN) || IS_PRIVATE(d_inode(dentry)))
- return 0;
-
- if (list && len <= list_size) {
- memcpy(list, name, name_len);
- list[name_len] = '\0';
- }
- return len;
+ return capable(CAP_SYS_ADMIN) && !IS_PRIVATE(d_inode(dentry));
}
const struct xattr_handler reiserfs_xattr_trusted_handler = {
diff --git a/fs/reiserfs/xattr_user.c b/fs/reiserfs/xattr_user.c
index 39c9667191c5..12e6306f562a 100644
--- a/fs/reiserfs/xattr_user.c
+++ b/fs/reiserfs/xattr_user.c
@@ -30,19 +30,9 @@ user_set(const struct xattr_handler *handler, struct dentry *dentry,
return reiserfs_xattr_set(d_inode(dentry), name, buffer, size, flags);
}
-static size_t user_list(const struct xattr_handler *handler,
- struct dentry *dentry, char *list, size_t list_size,
- const char *name, size_t name_len)
+static bool user_list(struct dentry *dentry)
{
- const size_t len = name_len + 1;
-
- if (!reiserfs_xattrs_user(dentry->d_sb))
- return 0;
- if (list && len <= list_size) {
- memcpy(list, name, name_len);
- list[name_len] = '\0';
- }
- return len;
+ return reiserfs_xattrs_user(dentry->d_sb);
}
const struct xattr_handler reiserfs_xattr_user_handler = {
diff --git a/fs/romfs/super.c b/fs/romfs/super.c
index 268733cda397..6b00ca357c58 100644
--- a/fs/romfs/super.c
+++ b/fs/romfs/super.c
@@ -360,6 +360,7 @@ static struct inode *romfs_iget(struct super_block *sb, unsigned long pos)
break;
case ROMFH_SYM:
i->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(i);
i->i_data.a_ops = &romfs_aops;
mode |= S_IRWXUGO;
break;
@@ -618,8 +619,8 @@ static int __init init_romfs_fs(void)
romfs_inode_cachep =
kmem_cache_create("romfs_i",
sizeof(struct romfs_inode_info), 0,
- SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD,
- romfs_i_init_once);
+ SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD |
+ SLAB_ACCOUNT, romfs_i_init_once);
if (!romfs_inode_cachep) {
pr_err("Failed to initialise inode cache\n");
diff --git a/fs/select.c b/fs/select.c
index 015547330e88..79d0d4953cad 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -778,8 +778,8 @@ static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait,
return mask;
}
-static int do_poll(unsigned int nfds, struct poll_list *list,
- struct poll_wqueues *wait, struct timespec *end_time)
+static int do_poll(struct poll_list *list, struct poll_wqueues *wait,
+ struct timespec *end_time)
{
poll_table* pt = &wait->pt;
ktime_t expire, *to = NULL;
@@ -908,7 +908,7 @@ int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds,
}
poll_initwait(&table);
- fdcount = do_poll(nfds, head, &table, end_time);
+ fdcount = do_poll(head, &table, end_time);
poll_freewait(&table);
for (walk = head; walk; walk = walk->next) {
diff --git a/fs/splice.c b/fs/splice.c
index 801c21cd77fe..82bc0d64fc38 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -415,6 +415,7 @@ __generic_file_splice_read(struct file *in, loff_t *ppos,
*/
if (!page->mapping) {
unlock_page(page);
+retry_lookup:
page = find_or_create_page(mapping, index,
mapping_gfp_mask(mapping));
@@ -439,13 +440,10 @@ __generic_file_splice_read(struct file *in, loff_t *ppos,
error = mapping->a_ops->readpage(in, page);
if (unlikely(error)) {
/*
- * We really should re-lookup the page here,
- * but it complicates things a lot. Instead
- * lets just do what we already stored, and
- * we'll get it the next time we are called.
+ * Re-lookup the page
*/
if (error == AOP_TRUNCATED_PAGE)
- error = 0;
+ goto retry_lookup;
break;
}
@@ -809,6 +807,13 @@ static int splice_from_pipe_feed(struct pipe_inode_info *pipe, struct splice_des
*/
static int splice_from_pipe_next(struct pipe_inode_info *pipe, struct splice_desc *sd)
{
+ /*
+ * Check for signal early to make process killable when there are
+ * always buffers available
+ */
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+
while (!pipe->nrbufs) {
if (!pipe->writers)
return 0;
@@ -884,6 +889,7 @@ ssize_t __splice_from_pipe(struct pipe_inode_info *pipe, struct splice_desc *sd,
splice_from_pipe_begin(sd);
do {
+ cond_resched();
ret = splice_from_pipe_next(pipe, sd);
if (ret > 0)
ret = splice_from_pipe_feed(pipe, sd, actor);
diff --git a/fs/squashfs/inode.c b/fs/squashfs/inode.c
index a1ce5ce60632..0927b1e80ab6 100644
--- a/fs/squashfs/inode.c
+++ b/fs/squashfs/inode.c
@@ -41,6 +41,7 @@
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/xattr.h>
+#include <linux/pagemap.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
@@ -291,6 +292,7 @@ int squashfs_read_inode(struct inode *inode, long long ino)
set_nlink(inode, le32_to_cpu(sqsh_ino->nlink));
inode->i_size = le32_to_cpu(sqsh_ino->symlink_size);
inode->i_op = &squashfs_symlink_inode_ops;
+ inode_nohighmem(inode);
inode->i_data.a_ops = &squashfs_symlink_aops;
inode->i_mode |= S_IFLNK;
squashfs_i(inode)->start = block;
diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
index 5056babe00df..5e79bfa4f260 100644
--- a/fs/squashfs/super.c
+++ b/fs/squashfs/super.c
@@ -80,7 +80,6 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
{
struct squashfs_sb_info *msblk;
struct squashfs_super_block *sblk = NULL;
- char b[BDEVNAME_SIZE];
struct inode *root;
long long root_inode;
unsigned short flags;
@@ -124,8 +123,8 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
sb->s_magic = le32_to_cpu(sblk->s_magic);
if (sb->s_magic != SQUASHFS_MAGIC) {
if (!silent)
- ERROR("Can't find a SQUASHFS superblock on %s\n",
- bdevname(sb->s_bdev, b));
+ ERROR("Can't find a SQUASHFS superblock on %pg\n",
+ sb->s_bdev);
goto failed_mount;
}
@@ -178,7 +177,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
msblk->inodes = le32_to_cpu(sblk->inodes);
flags = le16_to_cpu(sblk->flags);
- TRACE("Found valid superblock on %s\n", bdevname(sb->s_bdev, b));
+ TRACE("Found valid superblock on %pg\n", sb->s_bdev);
TRACE("Inodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(flags)
? "un" : "");
TRACE("Data is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(flags)
@@ -420,7 +419,8 @@ static int __init init_inodecache(void)
{
squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache",
sizeof(struct squashfs_inode_info), 0,
- SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, init_once);
+ SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|SLAB_ACCOUNT,
+ init_once);
return squashfs_inode_cachep ? 0 : -ENOMEM;
}
diff --git a/fs/squashfs/symlink.c b/fs/squashfs/symlink.c
index 12806dffb345..dbcc2f54bad4 100644
--- a/fs/squashfs/symlink.c
+++ b/fs/squashfs/symlink.c
@@ -119,8 +119,7 @@ const struct address_space_operations squashfs_symlink_aops = {
const struct inode_operations squashfs_symlink_inode_ops = {
.readlink = generic_readlink,
- .follow_link = page_follow_link_light,
- .put_link = page_put_link,
+ .get_link = page_get_link,
.getxattr = generic_getxattr,
.listxattr = squashfs_listxattr
};
diff --git a/fs/squashfs/xattr.c b/fs/squashfs/xattr.c
index 6a4cc344085c..1e9de96288d8 100644
--- a/fs/squashfs/xattr.c
+++ b/fs/squashfs/xattr.c
@@ -58,7 +58,7 @@ ssize_t squashfs_listxattr(struct dentry *d, char *buffer,
struct squashfs_xattr_entry entry;
struct squashfs_xattr_val val;
const struct xattr_handler *handler;
- int name_size, prefix_size = 0;
+ int name_size;
err = squashfs_read_metadata(sb, &entry, &start, &offset,
sizeof(entry));
@@ -67,15 +67,16 @@ ssize_t squashfs_listxattr(struct dentry *d, char *buffer,
name_size = le16_to_cpu(entry.size);
handler = squashfs_xattr_handler(le16_to_cpu(entry.type));
- if (handler)
- prefix_size = handler->list(handler, d, buffer, rest,
- NULL, name_size);
- if (prefix_size) {
+ if (handler && (!handler->list || handler->list(d))) {
+ const char *prefix = handler->prefix ?: handler->name;
+ size_t prefix_size = strlen(prefix);
+
if (buffer) {
if (prefix_size + name_size + 1 > rest) {
err = -ERANGE;
goto failed;
}
+ memcpy(buffer, prefix, prefix_size);
buffer += prefix_size;
}
err = squashfs_read_metadata(sb, buffer, &start,
@@ -212,25 +213,10 @@ failed:
}
-static size_t squashfs_xattr_handler_list(const struct xattr_handler *handler,
- struct dentry *d, char *list,
- size_t list_size, const char *name,
- size_t name_len)
-{
- int len = strlen(handler->prefix);
-
- if (list && len <= list_size)
- memcpy(list, handler->prefix, len);
- return len;
-}
-
static int squashfs_xattr_handler_get(const struct xattr_handler *handler,
struct dentry *d, const char *name,
void *buffer, size_t size)
{
- if (name[0] == '\0')
- return -EINVAL;
-
return squashfs_xattr_get(d_inode(d), handler->flags, name,
buffer, size);
}
@@ -241,22 +227,15 @@ static int squashfs_xattr_handler_get(const struct xattr_handler *handler,
static const struct xattr_handler squashfs_xattr_user_handler = {
.prefix = XATTR_USER_PREFIX,
.flags = SQUASHFS_XATTR_USER,
- .list = squashfs_xattr_handler_list,
.get = squashfs_xattr_handler_get
};
/*
* Trusted namespace support
*/
-static size_t squashfs_trusted_xattr_handler_list(const struct xattr_handler *handler,
- struct dentry *d, char *list,
- size_t list_size, const char *name,
- size_t name_len)
+static bool squashfs_trusted_xattr_handler_list(struct dentry *d)
{
- if (!capable(CAP_SYS_ADMIN))
- return 0;
- return squashfs_xattr_handler_list(handler, d, list, list_size, name,
- name_len);
+ return capable(CAP_SYS_ADMIN);
}
static const struct xattr_handler squashfs_xattr_trusted_handler = {
@@ -272,7 +251,6 @@ static const struct xattr_handler squashfs_xattr_trusted_handler = {
static const struct xattr_handler squashfs_xattr_security_handler = {
.prefix = XATTR_SECURITY_PREFIX,
.flags = SQUASHFS_XATTR_SECURITY,
- .list = squashfs_xattr_handler_list,
.get = squashfs_xattr_handler_get
};
diff --git a/fs/super.c b/fs/super.c
index 954aeb80e202..1182af8fd5ff 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -1012,10 +1012,8 @@ struct dentry *mount_bdev(struct file_system_type *fs_type,
blkdev_put(bdev, mode);
down_write(&s->s_umount);
} else {
- char b[BDEVNAME_SIZE];
-
s->s_mode = mode;
- strlcpy(s->s_id, bdevname(bdev, b), sizeof(s->s_id));
+ snprintf(s->s_id, sizeof(s->s_id), "%pg", bdev);
sb_set_blocksize(s, block_size(bdev));
error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
if (error) {
@@ -1199,7 +1197,7 @@ int __sb_start_write(struct super_block *sb, int level, bool wait)
else
ret = percpu_down_read_trylock(sb->s_writers.rw_sem + level-1);
- WARN_ON(force_trylock & !ret);
+ WARN_ON(force_trylock && !ret);
return ret;
}
EXPORT_SYMBOL(__sb_start_write);
diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c
index 590ad9206e3f..d62c423a5a2d 100644
--- a/fs/sysv/inode.c
+++ b/fs/sysv/inode.c
@@ -146,8 +146,7 @@ static inline void write3byte(struct sysv_sb_info *sbi,
static const struct inode_operations sysv_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = page_follow_link_light,
- .put_link = page_put_link,
+ .get_link = page_get_link,
.getattr = sysv_getattr,
};
@@ -162,15 +161,9 @@ void sysv_set_inode(struct inode *inode, dev_t rdev)
inode->i_fop = &sysv_dir_operations;
inode->i_mapping->a_ops = &sysv_aops;
} else if (S_ISLNK(inode->i_mode)) {
- if (inode->i_blocks) {
- inode->i_op = &sysv_symlink_inode_operations;
- inode->i_mapping->a_ops = &sysv_aops;
- } else {
- 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);
- }
+ inode->i_op = &sysv_symlink_inode_operations;
+ inode_nohighmem(inode);
+ inode->i_mapping->a_ops = &sysv_aops;
} else
init_special_inode(inode, inode->i_mode, rdev);
}
@@ -353,7 +346,7 @@ int __init sysv_init_icache(void)
{
sysv_inode_cachep = kmem_cache_create("sysv_inode_cache",
sizeof(struct sysv_inode_info), 0,
- SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD,
+ SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|SLAB_ACCOUNT,
init_once);
if (!sysv_inode_cachep)
return -ENOMEM;
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index 0edc12856147..eff62801acbf 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -1608,7 +1608,7 @@ const struct inode_operations ubifs_file_inode_operations = {
const struct inode_operations ubifs_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = simple_follow_link,
+ .get_link = simple_get_link,
.setattr = ubifs_setattr,
.getattr = ubifs_getattr,
.setxattr = ubifs_setxattr,
diff --git a/fs/ubifs/key.h b/fs/ubifs/key.h
index 92a8491a8f8c..c0a95e393347 100644
--- a/fs/ubifs/key.h
+++ b/fs/ubifs/key.h
@@ -34,6 +34,12 @@
* node. We use "r5" hash borrowed from reiserfs.
*/
+/*
+ * Lot's of the key helpers require a struct ubifs_info *c as the first parameter.
+ * But we are not using it at all currently. That's designed for future extensions of
+ * different c->key_format. But right now, there is only one key type, UBIFS_SIMPLE_KEY_FMT.
+ */
+
#ifndef __UBIFS_KEY_H__
#define __UBIFS_KEY_H__
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index 1fd90c079537..a233ba913be4 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -2248,8 +2248,8 @@ static int __init ubifs_init(void)
ubifs_inode_slab = kmem_cache_create("ubifs_inode_slab",
sizeof(struct ubifs_inode), 0,
- SLAB_MEM_SPREAD | SLAB_RECLAIM_ACCOUNT,
- &inode_slab_ctor);
+ SLAB_MEM_SPREAD | SLAB_RECLAIM_ACCOUNT |
+ SLAB_ACCOUNT, &inode_slab_ctor);
if (!ubifs_inode_slab)
return -ENOMEM;
diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c
index e8b01b721e99..e53292d0c21b 100644
--- a/fs/ubifs/xattr.c
+++ b/fs/ubifs/xattr.c
@@ -267,7 +267,7 @@ static int check_namespace(const struct qstr *nm)
if (!strncmp(nm->name, XATTR_TRUSTED_PREFIX,
XATTR_TRUSTED_PREFIX_LEN)) {
- if (nm->name[sizeof(XATTR_TRUSTED_PREFIX) - 1] == '\0')
+ if (nm->name[XATTR_TRUSTED_PREFIX_LEN] == '\0')
return -EINVAL;
type = TRUSTED_XATTR;
} else if (!strncmp(nm->name, XATTR_USER_PREFIX,
@@ -277,7 +277,7 @@ static int check_namespace(const struct qstr *nm)
type = USER_XATTR;
} else if (!strncmp(nm->name, XATTR_SECURITY_PREFIX,
XATTR_SECURITY_PREFIX_LEN)) {
- if (nm->name[sizeof(XATTR_SECURITY_PREFIX) - 1] == '\0')
+ if (nm->name[XATTR_SECURITY_PREFIX_LEN] == '\0')
return -EINVAL;
type = SECURITY_XATTR;
} else
diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c
index 6d6a96b4e73f..e0fd65fe73e8 100644
--- a/fs/udf/balloc.c
+++ b/fs/udf/balloc.c
@@ -447,9 +447,6 @@ static void udf_table_free_blocks(struct super_block *sb,
*/
int adsize;
- struct short_ad *sad = NULL;
- struct long_ad *lad = NULL;
- struct allocExtDesc *aed;
eloc.logicalBlockNum = start;
elen = EXT_RECORDED_ALLOCATED |
@@ -466,102 +463,17 @@ static void udf_table_free_blocks(struct super_block *sb,
}
if (epos.offset + (2 * adsize) > sb->s_blocksize) {
- unsigned char *sptr, *dptr;
- int loffset;
-
- brelse(oepos.bh);
- oepos = epos;
-
/* Steal a block from the extent being free'd */
- epos.block.logicalBlockNum = eloc.logicalBlockNum;
+ udf_setup_indirect_aext(table, eloc.logicalBlockNum,
+ &epos);
+
eloc.logicalBlockNum++;
elen -= sb->s_blocksize;
-
- epos.bh = udf_tread(sb,
- udf_get_lb_pblock(sb, &epos.block, 0));
- if (!epos.bh) {
- brelse(oepos.bh);
- goto error_return;
- }
- aed = (struct allocExtDesc *)(epos.bh->b_data);
- aed->previousAllocExtLocation =
- cpu_to_le32(oepos.block.logicalBlockNum);
- if (epos.offset + adsize > sb->s_blocksize) {
- loffset = epos.offset;
- aed->lengthAllocDescs = cpu_to_le32(adsize);
- sptr = iinfo->i_ext.i_data + epos.offset
- - adsize;
- dptr = epos.bh->b_data +
- sizeof(struct allocExtDesc);
- memcpy(dptr, sptr, adsize);
- epos.offset = sizeof(struct allocExtDesc) +
- adsize;
- } else {
- loffset = epos.offset + adsize;
- aed->lengthAllocDescs = cpu_to_le32(0);
- if (oepos.bh) {
- sptr = oepos.bh->b_data + epos.offset;
- aed = (struct allocExtDesc *)
- oepos.bh->b_data;
- le32_add_cpu(&aed->lengthAllocDescs,
- adsize);
- } else {
- sptr = iinfo->i_ext.i_data +
- epos.offset;
- iinfo->i_lenAlloc += adsize;
- mark_inode_dirty(table);
- }
- epos.offset = sizeof(struct allocExtDesc);
- }
- if (sbi->s_udfrev >= 0x0200)
- udf_new_tag(epos.bh->b_data, TAG_IDENT_AED,
- 3, 1, epos.block.logicalBlockNum,
- sizeof(struct tag));
- else
- udf_new_tag(epos.bh->b_data, TAG_IDENT_AED,
- 2, 1, epos.block.logicalBlockNum,
- sizeof(struct tag));
-
- switch (iinfo->i_alloc_type) {
- case ICBTAG_FLAG_AD_SHORT:
- sad = (struct short_ad *)sptr;
- sad->extLength = cpu_to_le32(
- EXT_NEXT_EXTENT_ALLOCDECS |
- sb->s_blocksize);
- sad->extPosition =
- cpu_to_le32(epos.block.logicalBlockNum);
- break;
- case ICBTAG_FLAG_AD_LONG:
- lad = (struct long_ad *)sptr;
- lad->extLength = cpu_to_le32(
- EXT_NEXT_EXTENT_ALLOCDECS |
- sb->s_blocksize);
- lad->extLocation =
- cpu_to_lelb(epos.block);
- break;
- }
- if (oepos.bh) {
- udf_update_tag(oepos.bh->b_data, loffset);
- mark_buffer_dirty(oepos.bh);
- } else {
- mark_inode_dirty(table);
- }
}
/* It's possible that stealing the block emptied the extent */
- if (elen) {
- udf_write_aext(table, &epos, &eloc, elen, 1);
-
- if (!epos.bh) {
- iinfo->i_lenAlloc += adsize;
- mark_inode_dirty(table);
- } else {
- aed = (struct allocExtDesc *)epos.bh->b_data;
- le32_add_cpu(&aed->lengthAllocDescs, adsize);
- udf_update_tag(epos.bh->b_data, epos.offset);
- mark_buffer_dirty(epos.bh);
- }
- }
+ if (elen)
+ __udf_add_aext(table, &epos, &eloc, elen, 1);
}
brelse(epos.bh);
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index 8d0b3ade0ff0..87dc16d15572 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -539,9 +539,18 @@ static int udf_do_extend_file(struct inode *inode,
udf_add_aext(inode, last_pos, &last_ext->extLocation,
last_ext->extLength, 1);
count++;
- } else
+ } else {
+ struct kernel_lb_addr tmploc;
+ uint32_t tmplen;
+
udf_write_aext(inode, last_pos, &last_ext->extLocation,
last_ext->extLength, 1);
+ /*
+ * We've rewritten the last extent but there may be empty
+ * indirect extent after it - enter it.
+ */
+ udf_next_aext(inode, last_pos, &tmploc, &tmplen, 0);
+ }
/* Managed to do everything necessary? */
if (!blocks)
@@ -1540,7 +1549,8 @@ reread:
break;
case ICBTAG_FILE_TYPE_SYMLINK:
inode->i_data.a_ops = &udf_symlink_aops;
- inode->i_op = &udf_symlink_inode_operations;
+ inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_mode = S_IFLNK | S_IRWXUGO;
break;
case ICBTAG_FILE_TYPE_MAIN:
@@ -1866,22 +1876,90 @@ struct inode *__udf_iget(struct super_block *sb, struct kernel_lb_addr *ino,
return inode;
}
-int udf_add_aext(struct inode *inode, struct extent_position *epos,
- struct kernel_lb_addr *eloc, uint32_t elen, int inc)
+int udf_setup_indirect_aext(struct inode *inode, int block,
+ struct extent_position *epos)
{
- int adsize;
- struct short_ad *sad = NULL;
- struct long_ad *lad = NULL;
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh;
struct allocExtDesc *aed;
- uint8_t *ptr;
- struct udf_inode_info *iinfo = UDF_I(inode);
+ struct extent_position nepos;
+ struct kernel_lb_addr neloc;
+ int ver, adsize;
- if (!epos->bh)
- ptr = iinfo->i_ext.i_data + epos->offset -
- udf_file_entry_alloc_offset(inode) +
- iinfo->i_lenEAttr;
+ if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+ adsize = sizeof(struct short_ad);
+ else if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_LONG)
+ adsize = sizeof(struct long_ad);
else
- ptr = epos->bh->b_data + epos->offset;
+ return -EIO;
+
+ neloc.logicalBlockNum = block;
+ neloc.partitionReferenceNum = epos->block.partitionReferenceNum;
+
+ bh = udf_tgetblk(sb, udf_get_lb_pblock(sb, &neloc, 0));
+ if (!bh)
+ return -EIO;
+ lock_buffer(bh);
+ memset(bh->b_data, 0x00, sb->s_blocksize);
+ set_buffer_uptodate(bh);
+ unlock_buffer(bh);
+ mark_buffer_dirty_inode(bh, inode);
+
+ aed = (struct allocExtDesc *)(bh->b_data);
+ if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT)) {
+ aed->previousAllocExtLocation =
+ cpu_to_le32(epos->block.logicalBlockNum);
+ }
+ aed->lengthAllocDescs = cpu_to_le32(0);
+ if (UDF_SB(sb)->s_udfrev >= 0x0200)
+ ver = 3;
+ else
+ ver = 2;
+ udf_new_tag(bh->b_data, TAG_IDENT_AED, ver, 1, block,
+ sizeof(struct tag));
+
+ nepos.block = neloc;
+ nepos.offset = sizeof(struct allocExtDesc);
+ nepos.bh = bh;
+
+ /*
+ * Do we have to copy current last extent to make space for indirect
+ * one?
+ */
+ if (epos->offset + adsize > sb->s_blocksize) {
+ struct kernel_lb_addr cp_loc;
+ uint32_t cp_len;
+ int cp_type;
+
+ epos->offset -= adsize;
+ cp_type = udf_current_aext(inode, epos, &cp_loc, &cp_len, 0);
+ cp_len |= ((uint32_t)cp_type) << 30;
+
+ __udf_add_aext(inode, &nepos, &cp_loc, cp_len, 1);
+ udf_write_aext(inode, epos, &nepos.block,
+ sb->s_blocksize | EXT_NEXT_EXTENT_ALLOCDECS, 0);
+ } else {
+ __udf_add_aext(inode, epos, &nepos.block,
+ sb->s_blocksize | EXT_NEXT_EXTENT_ALLOCDECS, 0);
+ }
+
+ brelse(epos->bh);
+ *epos = nepos;
+
+ return 0;
+}
+
+/*
+ * Append extent at the given position - should be the first free one in inode
+ * / indirect extent. This function assumes there is enough space in the inode
+ * or indirect extent. Use udf_add_aext() if you didn't check for this before.
+ */
+int __udf_add_aext(struct inode *inode, struct extent_position *epos,
+ struct kernel_lb_addr *eloc, uint32_t elen, int inc)
+{
+ struct udf_inode_info *iinfo = UDF_I(inode);
+ struct allocExtDesc *aed;
+ int adsize;
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
adsize = sizeof(struct short_ad);
@@ -1890,88 +1968,14 @@ int udf_add_aext(struct inode *inode, struct extent_position *epos,
else
return -EIO;
- if (epos->offset + (2 * adsize) > inode->i_sb->s_blocksize) {
- unsigned char *sptr, *dptr;
- struct buffer_head *nbh;
- int err, loffset;
- struct kernel_lb_addr obloc = epos->block;
-
- epos->block.logicalBlockNum = udf_new_block(inode->i_sb, NULL,
- obloc.partitionReferenceNum,
- obloc.logicalBlockNum, &err);
- if (!epos->block.logicalBlockNum)
- return -ENOSPC;
- nbh = udf_tgetblk(inode->i_sb, udf_get_lb_pblock(inode->i_sb,
- &epos->block,
- 0));
- if (!nbh)
- return -EIO;
- lock_buffer(nbh);
- memset(nbh->b_data, 0x00, inode->i_sb->s_blocksize);
- set_buffer_uptodate(nbh);
- unlock_buffer(nbh);
- mark_buffer_dirty_inode(nbh, inode);
-
- aed = (struct allocExtDesc *)(nbh->b_data);
- if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT))
- aed->previousAllocExtLocation =
- cpu_to_le32(obloc.logicalBlockNum);
- if (epos->offset + adsize > inode->i_sb->s_blocksize) {
- loffset = epos->offset;
- aed->lengthAllocDescs = cpu_to_le32(adsize);
- sptr = ptr - adsize;
- dptr = nbh->b_data + sizeof(struct allocExtDesc);
- memcpy(dptr, sptr, adsize);
- epos->offset = sizeof(struct allocExtDesc) + adsize;
- } else {
- loffset = epos->offset + adsize;
- aed->lengthAllocDescs = cpu_to_le32(0);
- sptr = ptr;
- epos->offset = sizeof(struct allocExtDesc);
-
- if (epos->bh) {
- aed = (struct allocExtDesc *)epos->bh->b_data;
- le32_add_cpu(&aed->lengthAllocDescs, adsize);
- } else {
- iinfo->i_lenAlloc += adsize;
- mark_inode_dirty(inode);
- }
- }
- if (UDF_SB(inode->i_sb)->s_udfrev >= 0x0200)
- udf_new_tag(nbh->b_data, TAG_IDENT_AED, 3, 1,
- epos->block.logicalBlockNum, sizeof(struct tag));
- else
- udf_new_tag(nbh->b_data, TAG_IDENT_AED, 2, 1,
- epos->block.logicalBlockNum, sizeof(struct tag));
- switch (iinfo->i_alloc_type) {
- case ICBTAG_FLAG_AD_SHORT:
- sad = (struct short_ad *)sptr;
- sad->extLength = cpu_to_le32(EXT_NEXT_EXTENT_ALLOCDECS |
- inode->i_sb->s_blocksize);
- sad->extPosition =
- cpu_to_le32(epos->block.logicalBlockNum);
- break;
- case ICBTAG_FLAG_AD_LONG:
- lad = (struct long_ad *)sptr;
- lad->extLength = cpu_to_le32(EXT_NEXT_EXTENT_ALLOCDECS |
- inode->i_sb->s_blocksize);
- lad->extLocation = cpu_to_lelb(epos->block);
- memset(lad->impUse, 0x00, sizeof(lad->impUse));
- break;
- }
- if (epos->bh) {
- if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) ||
- UDF_SB(inode->i_sb)->s_udfrev >= 0x0201)
- udf_update_tag(epos->bh->b_data, loffset);
- else
- udf_update_tag(epos->bh->b_data,
- sizeof(struct allocExtDesc));
- mark_buffer_dirty_inode(epos->bh, inode);
- brelse(epos->bh);
- } else {
- mark_inode_dirty(inode);
- }
- epos->bh = nbh;
+ if (!epos->bh) {
+ WARN_ON(iinfo->i_lenAlloc !=
+ epos->offset - udf_file_entry_alloc_offset(inode));
+ } else {
+ aed = (struct allocExtDesc *)epos->bh->b_data;
+ WARN_ON(le32_to_cpu(aed->lengthAllocDescs) !=
+ epos->offset - sizeof(struct allocExtDesc));
+ WARN_ON(epos->offset + adsize > inode->i_sb->s_blocksize);
}
udf_write_aext(inode, epos, eloc, elen, inc);
@@ -1995,6 +1999,41 @@ int udf_add_aext(struct inode *inode, struct extent_position *epos,
return 0;
}
+/*
+ * Append extent at given position - should be the first free one in inode
+ * / indirect extent. Takes care of allocating and linking indirect blocks.
+ */
+int udf_add_aext(struct inode *inode, struct extent_position *epos,
+ struct kernel_lb_addr *eloc, uint32_t elen, int inc)
+{
+ int adsize;
+ struct super_block *sb = inode->i_sb;
+
+ if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+ adsize = sizeof(struct short_ad);
+ else if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_LONG)
+ adsize = sizeof(struct long_ad);
+ else
+ return -EIO;
+
+ if (epos->offset + (2 * adsize) > sb->s_blocksize) {
+ int err;
+ int new_block;
+
+ new_block = udf_new_block(sb, NULL,
+ epos->block.partitionReferenceNum,
+ epos->block.logicalBlockNum, &err);
+ if (!new_block)
+ return -ENOSPC;
+
+ err = udf_setup_indirect_aext(inode, new_block, epos);
+ if (err)
+ return err;
+ }
+
+ return __udf_add_aext(inode, epos, eloc, elen, inc);
+}
+
void udf_write_aext(struct inode *inode, struct extent_position *epos,
struct kernel_lb_addr *eloc, uint32_t elen, int inc)
{
@@ -2047,14 +2086,29 @@ void udf_write_aext(struct inode *inode, struct extent_position *epos,
epos->offset += adsize;
}
+/*
+ * Only 1 indirect extent in a row really makes sense but allow upto 16 in case
+ * someone does some weird stuff.
+ */
+#define UDF_MAX_INDIR_EXTS 16
+
int8_t udf_next_aext(struct inode *inode, struct extent_position *epos,
struct kernel_lb_addr *eloc, uint32_t *elen, int inc)
{
int8_t etype;
+ unsigned int indirections = 0;
while ((etype = udf_current_aext(inode, epos, eloc, elen, inc)) ==
(EXT_NEXT_EXTENT_ALLOCDECS >> 30)) {
int block;
+
+ if (++indirections > UDF_MAX_INDIR_EXTS) {
+ udf_err(inode->i_sb,
+ "too many indirect extents in inode %lu\n",
+ inode->i_ino);
+ return -1;
+ }
+
epos->block = *eloc;
epos->offset = sizeof(struct allocExtDesc);
brelse(epos->bh);
diff --git a/fs/udf/namei.c b/fs/udf/namei.c
index c97b5a8d1e24..42eafb91f7ff 100644
--- a/fs/udf/namei.c
+++ b/fs/udf/namei.c
@@ -921,7 +921,8 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry,
}
inode->i_data.a_ops = &udf_symlink_aops;
- inode->i_op = &udf_symlink_inode_operations;
+ inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
struct kernel_lb_addr eloc;
@@ -1344,8 +1345,3 @@ const struct inode_operations udf_dir_inode_operations = {
.rename = udf_rename,
.tmpfile = udf_tmpfile,
};
-const struct inode_operations udf_symlink_inode_operations = {
- .readlink = generic_readlink,
- .follow_link = page_follow_link_light,
- .put_link = page_put_link,
-};
diff --git a/fs/udf/super.c b/fs/udf/super.c
index 81155b9b445b..0fbb4c7c72e8 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -179,7 +179,8 @@ static int __init init_inodecache(void)
udf_inode_cachep = kmem_cache_create("udf_inode_cache",
sizeof(struct udf_inode_info),
0, (SLAB_RECLAIM_ACCOUNT |
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD |
+ SLAB_ACCOUNT),
init_once);
if (!udf_inode_cachep)
return -ENOMEM;
@@ -1586,6 +1587,13 @@ static void udf_load_logicalvolint(struct super_block *sb, struct kernel_extent_
}
/*
+ * Maximum number of Terminating Descriptor redirections. The chosen number is
+ * arbitrary - just that we hopefully don't limit any real use of rewritten
+ * inode on write-once media but avoid looping for too long on corrupted media.
+ */
+#define UDF_MAX_TD_NESTING 64
+
+/*
* Process a main/reserve volume descriptor sequence.
* @block First block of first extent of the sequence.
* @lastblock Lastblock of first extent of the sequence.
@@ -1609,6 +1617,7 @@ static noinline int udf_process_sequence(
uint16_t ident;
long next_s = 0, next_e = 0;
int ret;
+ unsigned int indirections = 0;
memset(vds, 0, sizeof(struct udf_vds_record) * VDS_POS_LENGTH);
@@ -1679,6 +1688,12 @@ static noinline int udf_process_sequence(
}
break;
case TAG_IDENT_TD: /* ISO 13346 3/10.9 */
+ if (++indirections > UDF_MAX_TD_NESTING) {
+ udf_err(sb, "too many TDs (max %u supported)\n", UDF_MAX_TD_NESTING);
+ brelse(bh);
+ return -EIO;
+ }
+
vds[VDS_POS_TERMINATING_DESC].block = block;
if (next_e) {
block = next_s;
diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c
index 862535b3ba58..8d619773056b 100644
--- a/fs/udf/symlink.c
+++ b/fs/udf/symlink.c
@@ -107,7 +107,7 @@ static int udf_symlink_filler(struct file *file, struct page *page)
struct buffer_head *bh = NULL;
unsigned char *symlink;
int err;
- unsigned char *p = kmap(page);
+ unsigned char *p = page_address(page);
struct udf_inode_info *iinfo;
uint32_t pos;
@@ -141,7 +141,6 @@ static int udf_symlink_filler(struct file *file, struct page *page)
up_read(&iinfo->i_data_sem);
SetPageUptodate(page);
- kunmap(page);
unlock_page(page);
return 0;
@@ -149,7 +148,6 @@ out_unlock_inode:
up_read(&iinfo->i_data_sem);
SetPageError(page);
out_unmap:
- kunmap(page);
unlock_page(page);
return err;
}
diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h
index 47bb3f5ca360..fa0044b6b81d 100644
--- a/fs/udf/udfdecl.h
+++ b/fs/udf/udfdecl.h
@@ -85,7 +85,6 @@ extern const struct inode_operations udf_dir_inode_operations;
extern const struct file_operations udf_dir_operations;
extern const struct inode_operations udf_file_inode_operations;
extern const struct file_operations udf_file_operations;
-extern const struct inode_operations udf_symlink_inode_operations;
extern const struct address_space_operations udf_aops;
extern const struct address_space_operations udf_adinicb_aops;
extern const struct address_space_operations udf_symlink_aops;
@@ -159,6 +158,10 @@ extern int udf_write_inode(struct inode *, struct writeback_control *wbc);
extern long udf_block_map(struct inode *, sector_t);
extern int8_t inode_bmap(struct inode *, sector_t, struct extent_position *,
struct kernel_lb_addr *, uint32_t *, sector_t *);
+extern int udf_setup_indirect_aext(struct inode *inode, int block,
+ struct extent_position *epos);
+extern int __udf_add_aext(struct inode *inode, struct extent_position *epos,
+ struct kernel_lb_addr *eloc, uint32_t elen, int inc);
extern int udf_add_aext(struct inode *, struct extent_position *,
struct kernel_lb_addr *, uint32_t, int);
extern void udf_write_aext(struct inode *, struct extent_position *,
diff --git a/fs/udf/unicode.c b/fs/udf/unicode.c
index ab478e62baae..e788a05aab83 100644
--- a/fs/udf/unicode.c
+++ b/fs/udf/unicode.c
@@ -128,11 +128,15 @@ int udf_CS0toUTF8(struct ustr *utf_o, const struct ustr *ocu_i)
if (c < 0x80U)
utf_o->u_name[utf_o->u_len++] = (uint8_t)c;
else if (c < 0x800U) {
+ if (utf_o->u_len > (UDF_NAME_LEN - 4))
+ break;
utf_o->u_name[utf_o->u_len++] =
(uint8_t)(0xc0 | (c >> 6));
utf_o->u_name[utf_o->u_len++] =
(uint8_t)(0x80 | (c & 0x3f));
} else {
+ if (utf_o->u_len > (UDF_NAME_LEN - 5))
+ break;
utf_o->u_name[utf_o->u_len++] =
(uint8_t)(0xe0 | (c >> 12));
utf_o->u_name[utf_o->u_len++] =
@@ -173,17 +177,22 @@ int udf_CS0toUTF8(struct ustr *utf_o, const struct ustr *ocu_i)
static int udf_UTF8toCS0(dstring *ocu, struct ustr *utf, int length)
{
unsigned c, i, max_val, utf_char;
- int utf_cnt, u_len;
+ int utf_cnt, u_len, u_ch;
memset(ocu, 0, sizeof(dstring) * length);
ocu[0] = 8;
max_val = 0xffU;
+ u_ch = 1;
try_again:
u_len = 0U;
utf_char = 0U;
utf_cnt = 0U;
for (i = 0U; i < utf->u_len; i++) {
+ /* Name didn't fit? */
+ if (u_len + 1 + u_ch >= length)
+ return 0;
+
c = (uint8_t)utf->u_name[i];
/* Complete a multi-byte UTF-8 character */
@@ -225,6 +234,7 @@ try_again:
if (max_val == 0xffU) {
max_val = 0xffffU;
ocu[0] = (uint8_t)0x10U;
+ u_ch = 2;
goto try_again;
}
goto error_out;
@@ -277,7 +287,7 @@ static int udf_CS0toNLS(struct nls_table *nls, struct ustr *utf_o,
c = (c << 8) | ocu[i++];
len = nls->uni2char(c, &utf_o->u_name[utf_o->u_len],
- UDF_NAME_LEN - utf_o->u_len);
+ UDF_NAME_LEN - 2 - utf_o->u_len);
/* Valid character? */
if (len >= 0)
utf_o->u_len += len;
@@ -295,15 +305,19 @@ static int udf_NLStoCS0(struct nls_table *nls, dstring *ocu, struct ustr *uni,
int len;
unsigned i, max_val;
uint16_t uni_char;
- int u_len;
+ int u_len, u_ch;
memset(ocu, 0, sizeof(dstring) * length);
ocu[0] = 8;
max_val = 0xffU;
+ u_ch = 1;
try_again:
u_len = 0U;
for (i = 0U; i < uni->u_len; i++) {
+ /* Name didn't fit? */
+ if (u_len + 1 + u_ch >= length)
+ return 0;
len = nls->char2uni(&uni->u_name[i], uni->u_len - i, &uni_char);
if (!len)
continue;
@@ -316,6 +330,7 @@ try_again:
if (uni_char > max_val) {
max_val = 0xffffU;
ocu[0] = (uint8_t)0x10U;
+ u_ch = 2;
goto try_again;
}
diff --git a/fs/ufs/Makefile b/fs/ufs/Makefile
index 392db25c0b56..ec4a6b49fa13 100644
--- a/fs/ufs/Makefile
+++ b/fs/ufs/Makefile
@@ -5,5 +5,5 @@
obj-$(CONFIG_UFS_FS) += ufs.o
ufs-objs := balloc.o cylinder.o dir.o file.o ialloc.o inode.o \
- namei.o super.o symlink.o util.o
+ namei.o super.o util.o
ccflags-$(CONFIG_UFS_DEBUG) += -DDEBUG
diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c
index a064cf44b143..d897e169ab9c 100644
--- a/fs/ufs/inode.c
+++ b/fs/ufs/inode.c
@@ -528,11 +528,12 @@ static void ufs_set_inode_ops(struct inode *inode)
inode->i_mapping->a_ops = &ufs_aops;
} else if (S_ISLNK(inode->i_mode)) {
if (!inode->i_blocks) {
- inode->i_op = &ufs_fast_symlink_inode_operations;
inode->i_link = (char *)UFS_I(inode)->i_u1.i_symlink;
+ inode->i_op = &simple_symlink_inode_operations;
} else {
- inode->i_op = &ufs_symlink_inode_operations;
inode->i_mapping->a_ops = &ufs_aops;
+ inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
}
} else
init_special_inode(inode, inode->i_mode,
diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c
index 47966554317c..acf4a3b61b81 100644
--- a/fs/ufs/namei.c
+++ b/fs/ufs/namei.c
@@ -123,14 +123,15 @@ static int ufs_symlink (struct inode * dir, struct dentry * dentry,
if (l > UFS_SB(sb)->s_uspi->s_maxsymlinklen) {
/* slow symlink */
- inode->i_op = &ufs_symlink_inode_operations;
+ inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
inode->i_mapping->a_ops = &ufs_aops;
err = page_symlink(inode, symname, l);
if (err)
goto out_fail;
} else {
/* fast symlink */
- inode->i_op = &ufs_fast_symlink_inode_operations;
+ inode->i_op = &simple_symlink_inode_operations;
inode->i_link = (char *)UFS_I(inode)->i_u1.i_symlink;
memcpy(inode->i_link, symname, l);
inode->i_size = l-1;
diff --git a/fs/ufs/super.c b/fs/ufs/super.c
index f6390eec02ca..442fd52ebffe 100644
--- a/fs/ufs/super.c
+++ b/fs/ufs/super.c
@@ -1427,7 +1427,7 @@ static int __init init_inodecache(void)
ufs_inode_cachep = kmem_cache_create("ufs_inode_cache",
sizeof(struct ufs_inode_info),
0, (SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
init_once);
if (ufs_inode_cachep == NULL)
return -ENOMEM;
diff --git a/fs/ufs/symlink.c b/fs/ufs/symlink.c
deleted file mode 100644
index 874480bb43e9..000000000000
--- a/fs/ufs/symlink.c
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * linux/fs/ufs/symlink.c
- *
- * Only fast symlinks left here - the rest is done by generic code. AV, 1999
- *
- * Copyright (C) 1998
- * Daniel Pirkl <daniel.pirkl@emai.cz>
- * Charles University, Faculty of Mathematics and Physics
- *
- * from
- *
- * linux/fs/ext2/symlink.c
- *
- * 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/symlink.c
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- *
- * ext2 symlink handling code
- */
-
-#include "ufs_fs.h"
-#include "ufs.h"
-
-const struct inode_operations ufs_fast_symlink_inode_operations = {
- .readlink = generic_readlink,
- .follow_link = simple_follow_link,
- .setattr = ufs_setattr,
-};
-
-const struct inode_operations ufs_symlink_inode_operations = {
- .readlink = generic_readlink,
- .follow_link = page_follow_link_light,
- .put_link = page_put_link,
- .setattr = ufs_setattr,
-};
diff --git a/fs/ufs/ufs.h b/fs/ufs/ufs.h
index 7da4aca868c0..c87f4c3fa9dd 100644
--- a/fs/ufs/ufs.h
+++ b/fs/ufs/ufs.h
@@ -136,10 +136,6 @@ extern __printf(3, 4)
void ufs_panic(struct super_block *, const char *, const char *, ...);
void ufs_mark_sb_dirty(struct super_block *sb);
-/* symlink.c */
-extern const struct inode_operations ufs_fast_symlink_inode_operations;
-extern const struct inode_operations ufs_symlink_inode_operations;
-
static inline struct ufs_sb_info *UFS_SB(struct super_block *sb)
{
return sb->s_fs_info;
diff --git a/fs/xattr.c b/fs/xattr.c
index 9b932b95d74e..d5dd6c8b82a7 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -208,25 +208,6 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value,
return error;
}
-/* Compare an extended attribute value with the given value */
-int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name,
- const char *value, size_t size, gfp_t flags)
-{
- char *xattr_value = NULL;
- int rc;
-
- rc = vfs_getxattr_alloc(dentry, xattr_name, &xattr_value, 0, flags);
- if (rc < 0)
- return rc;
-
- if ((rc != size) || (memcmp(xattr_value, value, rc) != 0))
- rc = -EINVAL;
- else
- rc = 0;
- kfree(xattr_value);
- return rc;
-}
-
ssize_t
vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size)
{
@@ -324,7 +305,6 @@ setxattr(struct dentry *d, const char __user *name, const void __user *value,
{
int error;
void *kvalue = NULL;
- void *vvalue = NULL; /* If non-NULL, we used vmalloc() */
char kname[XATTR_NAME_MAX + 1];
if (flags & ~(XATTR_CREATE|XATTR_REPLACE))
@@ -341,10 +321,9 @@ setxattr(struct dentry *d, const char __user *name, const void __user *value,
return -E2BIG;
kvalue = kmalloc(size, GFP_KERNEL | __GFP_NOWARN);
if (!kvalue) {
- vvalue = vmalloc(size);
- if (!vvalue)
+ kvalue = vmalloc(size);
+ if (!kvalue)
return -ENOMEM;
- kvalue = vvalue;
}
if (copy_from_user(kvalue, value, size)) {
error = -EFAULT;
@@ -357,10 +336,8 @@ setxattr(struct dentry *d, const char __user *name, const void __user *value,
error = vfs_setxattr(d, kname, kvalue, size, flags);
out:
- if (vvalue)
- vfree(vvalue);
- else
- kfree(kvalue);
+ kvfree(kvalue);
+
return error;
}
@@ -428,7 +405,6 @@ getxattr(struct dentry *d, const char __user *name, void __user *value,
{
ssize_t error;
void *kvalue = NULL;
- void *vvalue = NULL;
char kname[XATTR_NAME_MAX + 1];
error = strncpy_from_user(kname, name, sizeof(kname));
@@ -442,10 +418,9 @@ getxattr(struct dentry *d, const char __user *name, void __user *value,
size = XATTR_SIZE_MAX;
kvalue = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
if (!kvalue) {
- vvalue = vmalloc(size);
- if (!vvalue)
+ kvalue = vmalloc(size);
+ if (!kvalue)
return -ENOMEM;
- kvalue = vvalue;
}
}
@@ -461,10 +436,9 @@ getxattr(struct dentry *d, const char __user *name, void __user *value,
than XATTR_SIZE_MAX bytes. Not possible. */
error = -E2BIG;
}
- if (vvalue)
- vfree(vvalue);
- else
- kfree(kvalue);
+
+ kvfree(kvalue);
+
return error;
}
@@ -521,17 +495,15 @@ listxattr(struct dentry *d, char __user *list, size_t size)
{
ssize_t error;
char *klist = NULL;
- char *vlist = NULL; /* If non-NULL, we used vmalloc() */
if (size) {
if (size > XATTR_LIST_MAX)
size = XATTR_LIST_MAX;
klist = kmalloc(size, __GFP_NOWARN | GFP_KERNEL);
if (!klist) {
- vlist = vmalloc(size);
- if (!vlist)
+ klist = vmalloc(size);
+ if (!klist)
return -ENOMEM;
- klist = vlist;
}
}
@@ -544,10 +516,9 @@ listxattr(struct dentry *d, char __user *list, size_t size)
than XATTR_LIST_MAX bytes. Not possible. */
error = -E2BIG;
}
- if (vlist)
- vfree(vlist);
- else
- kfree(klist);
+
+ kvfree(klist);
+
return error;
}
@@ -700,13 +671,20 @@ xattr_resolve_name(const struct xattr_handler **handlers, const char **name)
return NULL;
for_each_xattr_handler(handlers, handler) {
- const char *n = strcmp_prefix(*name, handler->prefix);
+ const char *n;
+
+ n = strcmp_prefix(*name, xattr_prefix(handler));
if (n) {
+ if (!handler->prefix ^ !*n) {
+ if (*n)
+ continue;
+ return ERR_PTR(-EINVAL);
+ }
*name = n;
- break;
+ return handler;
}
}
- return handler;
+ return ERR_PTR(-EOPNOTSUPP);
}
/*
@@ -718,8 +696,8 @@ generic_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t s
const struct xattr_handler *handler;
handler = xattr_resolve_name(dentry->d_sb->s_xattr, &name);
- if (!handler)
- return -EOPNOTSUPP;
+ if (IS_ERR(handler))
+ return PTR_ERR(handler);
return handler->get(handler, dentry, name, buffer, size);
}
@@ -735,19 +713,25 @@ generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
if (!buffer) {
for_each_xattr_handler(handlers, handler) {
- size += handler->list(handler, dentry, NULL, 0,
- NULL, 0);
+ if (!handler->name ||
+ (handler->list && !handler->list(dentry)))
+ continue;
+ size += strlen(handler->name) + 1;
}
} else {
char *buf = buffer;
+ size_t len;
for_each_xattr_handler(handlers, handler) {
- size = handler->list(handler, dentry, buf, buffer_size,
- NULL, 0);
- if (size > buffer_size)
+ if (!handler->name ||
+ (handler->list && !handler->list(dentry)))
+ continue;
+ len = strlen(handler->name);
+ if (len + 1 > buffer_size)
return -ERANGE;
- buf += size;
- buffer_size -= size;
+ memcpy(buf, handler->name, len + 1);
+ buf += len + 1;
+ buffer_size -= len + 1;
}
size = buf - buffer;
}
@@ -765,8 +749,8 @@ generic_setxattr(struct dentry *dentry, const char *name, const void *value, siz
if (size == 0)
value = ""; /* empty EA, do not remove */
handler = xattr_resolve_name(dentry->d_sb->s_xattr, &name);
- if (!handler)
- return -EOPNOTSUPP;
+ if (IS_ERR(handler))
+ return PTR_ERR(handler);
return handler->set(handler, dentry, name, value, size, flags);
}
@@ -780,8 +764,8 @@ generic_removexattr(struct dentry *dentry, const char *name)
const struct xattr_handler *handler;
handler = xattr_resolve_name(dentry->d_sb->s_xattr, &name);
- if (!handler)
- return -EOPNOTSUPP;
+ if (IS_ERR(handler))
+ return PTR_ERR(handler);
return handler->set(handler, dentry, name, NULL, 0, XATTR_REPLACE);
}
@@ -808,7 +792,7 @@ EXPORT_SYMBOL(generic_removexattr);
const char *xattr_full_name(const struct xattr_handler *handler,
const char *name)
{
- size_t prefix_len = strlen(handler->prefix);
+ size_t prefix_len = strlen(xattr_prefix(handler));
return name - prefix_len;
}
@@ -863,8 +847,22 @@ int simple_xattr_get(struct simple_xattrs *xattrs, const char *name,
return ret;
}
-static int __simple_xattr_set(struct simple_xattrs *xattrs, const char *name,
- const void *value, size_t size, int flags)
+/**
+ * simple_xattr_set - xattr SET operation for in-memory/pseudo filesystems
+ * @xattrs: target simple_xattr list
+ * @name: name of the extended attribute
+ * @value: value of the xattr. If %NULL, will remove the attribute.
+ * @size: size of the new xattr
+ * @flags: %XATTR_{CREATE|REPLACE}
+ *
+ * %XATTR_CREATE is set, the xattr shouldn't exist already; otherwise fails
+ * with -EEXIST. If %XATTR_REPLACE is set, the xattr should exist;
+ * otherwise, fails with -ENODATA.
+ *
+ * Returns 0 on success, -errno on failure.
+ */
+int simple_xattr_set(struct simple_xattrs *xattrs, const char *name,
+ const void *value, size_t size, int flags)
{
struct simple_xattr *xattr;
struct simple_xattr *new_xattr = NULL;
@@ -914,73 +912,64 @@ out:
}
-/**
- * simple_xattr_set - xattr SET operation for in-memory/pseudo filesystems
- * @xattrs: target simple_xattr list
- * @name: name of the new extended attribute
- * @value: value of the new xattr. If %NULL, will remove the attribute
- * @size: size of the new xattr
- * @flags: %XATTR_{CREATE|REPLACE}
- *
- * %XATTR_CREATE is set, the xattr shouldn't exist already; otherwise fails
- * with -EEXIST. If %XATTR_REPLACE is set, the xattr should exist;
- * otherwise, fails with -ENODATA.
- *
- * Returns 0 on success, -errno on failure.
- */
-int simple_xattr_set(struct simple_xattrs *xattrs, const char *name,
- const void *value, size_t size, int flags)
-{
- if (size == 0)
- value = ""; /* empty EA, do not remove */
- return __simple_xattr_set(xattrs, name, value, size, flags);
-}
-
-/*
- * xattr REMOVE operation for in-memory/pseudo filesystems
- */
-int simple_xattr_remove(struct simple_xattrs *xattrs, const char *name)
+static bool xattr_is_trusted(const char *name)
{
- return __simple_xattr_set(xattrs, name, NULL, 0, XATTR_REPLACE);
+ return !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN);
}
-static bool xattr_is_trusted(const char *name)
+static int xattr_list_one(char **buffer, ssize_t *remaining_size,
+ const char *name)
{
- return !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN);
+ size_t len = strlen(name) + 1;
+ if (*buffer) {
+ if (*remaining_size < len)
+ return -ERANGE;
+ memcpy(*buffer, name, len);
+ *buffer += len;
+ }
+ *remaining_size -= len;
+ return 0;
}
/*
* xattr LIST operation for in-memory/pseudo filesystems
*/
-ssize_t simple_xattr_list(struct simple_xattrs *xattrs, char *buffer,
- size_t size)
+ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs *xattrs,
+ char *buffer, size_t size)
{
bool trusted = capable(CAP_SYS_ADMIN);
struct simple_xattr *xattr;
- size_t used = 0;
+ ssize_t remaining_size = size;
+ int err;
+
+#ifdef CONFIG_FS_POSIX_ACL
+ if (inode->i_acl) {
+ err = xattr_list_one(&buffer, &remaining_size,
+ XATTR_NAME_POSIX_ACL_ACCESS);
+ if (err)
+ return err;
+ }
+ if (inode->i_default_acl) {
+ err = xattr_list_one(&buffer, &remaining_size,
+ XATTR_NAME_POSIX_ACL_DEFAULT);
+ if (err)
+ return err;
+ }
+#endif
spin_lock(&xattrs->lock);
list_for_each_entry(xattr, &xattrs->head, list) {
- size_t len;
-
/* skip "trusted." attributes for unprivileged callers */
if (!trusted && xattr_is_trusted(xattr->name))
continue;
- len = strlen(xattr->name) + 1;
- used += len;
- if (buffer) {
- if (size < used) {
- used = -ERANGE;
- break;
- }
- memcpy(buffer, xattr->name, len);
- buffer += len;
- }
+ err = xattr_list_one(&buffer, &remaining_size, xattr->name);
+ if (err)
+ return err;
}
spin_unlock(&xattrs->lock);
- return used;
+ return size - remaining_size;
}
/*
diff --git a/fs/xfs/kmem.h b/fs/xfs/kmem.h
index cc6b768fc068..d1c66e465ca5 100644
--- a/fs/xfs/kmem.h
+++ b/fs/xfs/kmem.h
@@ -84,6 +84,7 @@ kmem_zalloc(size_t size, xfs_km_flags_t flags)
#define KM_ZONE_HWALIGN SLAB_HWCACHE_ALIGN
#define KM_ZONE_RECLAIM SLAB_RECLAIM_ACCOUNT
#define KM_ZONE_SPREAD SLAB_MEM_SPREAD
+#define KM_ZONE_ACCOUNT SLAB_ACCOUNT
#define kmem_zone kmem_cache
#define kmem_zone_t struct kmem_cache
diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
index 3479294c1d58..a708e38b494c 100644
--- a/fs/xfs/libxfs/xfs_alloc.c
+++ b/fs/xfs/libxfs/xfs_alloc.c
@@ -535,6 +535,7 @@ xfs_agfl_write_verify(
}
const struct xfs_buf_ops xfs_agfl_buf_ops = {
+ .name = "xfs_agfl",
.verify_read = xfs_agfl_read_verify,
.verify_write = xfs_agfl_write_verify,
};
@@ -1926,7 +1927,7 @@ xfs_alloc_space_available(
* Decide whether to use this allocation group for this allocation.
* If so, fix up the btree freelist's size.
*/
-STATIC int /* error */
+int /* error */
xfs_alloc_fix_freelist(
struct xfs_alloc_arg *args, /* allocation argument structure */
int flags) /* XFS_ALLOC_FLAG_... */
@@ -2339,6 +2340,7 @@ xfs_agf_write_verify(
}
const struct xfs_buf_ops xfs_agf_buf_ops = {
+ .name = "xfs_agf",
.verify_read = xfs_agf_read_verify,
.verify_write = xfs_agf_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_alloc.h b/fs/xfs/libxfs/xfs_alloc.h
index 0ecde4d5cac8..135eb3d24db7 100644
--- a/fs/xfs/libxfs/xfs_alloc.h
+++ b/fs/xfs/libxfs/xfs_alloc.h
@@ -235,5 +235,6 @@ xfs_alloc_get_rec(
int xfs_read_agf(struct xfs_mount *mp, struct xfs_trans *tp,
xfs_agnumber_t agno, int flags, struct xfs_buf **bpp);
+int xfs_alloc_fix_freelist(struct xfs_alloc_arg *args, int flags);
#endif /* __XFS_ALLOC_H__ */
diff --git a/fs/xfs/libxfs/xfs_alloc_btree.c b/fs/xfs/libxfs/xfs_alloc_btree.c
index 90de071dd4c2..444626ddbd1b 100644
--- a/fs/xfs/libxfs/xfs_alloc_btree.c
+++ b/fs/xfs/libxfs/xfs_alloc_btree.c
@@ -293,14 +293,7 @@ xfs_allocbt_verify(
level = be16_to_cpu(block->bb_level);
switch (block->bb_magic) {
case cpu_to_be32(XFS_ABTB_CRC_MAGIC):
- if (!xfs_sb_version_hascrc(&mp->m_sb))
- return false;
- if (!uuid_equal(&block->bb_u.s.bb_uuid, &mp->m_sb.sb_meta_uuid))
- return false;
- if (block->bb_u.s.bb_blkno != cpu_to_be64(bp->b_bn))
- return false;
- if (pag &&
- be32_to_cpu(block->bb_u.s.bb_owner) != pag->pag_agno)
+ if (!xfs_btree_sblock_v5hdr_verify(bp))
return false;
/* fall through */
case cpu_to_be32(XFS_ABTB_MAGIC):
@@ -311,14 +304,7 @@ xfs_allocbt_verify(
return false;
break;
case cpu_to_be32(XFS_ABTC_CRC_MAGIC):
- if (!xfs_sb_version_hascrc(&mp->m_sb))
- return false;
- if (!uuid_equal(&block->bb_u.s.bb_uuid, &mp->m_sb.sb_meta_uuid))
- return false;
- if (block->bb_u.s.bb_blkno != cpu_to_be64(bp->b_bn))
- return false;
- if (pag &&
- be32_to_cpu(block->bb_u.s.bb_owner) != pag->pag_agno)
+ if (!xfs_btree_sblock_v5hdr_verify(bp))
return false;
/* fall through */
case cpu_to_be32(XFS_ABTC_MAGIC):
@@ -332,21 +318,7 @@ xfs_allocbt_verify(
return false;
}
- /* numrecs verification */
- if (be16_to_cpu(block->bb_numrecs) > mp->m_alloc_mxr[level != 0])
- return false;
-
- /* sibling pointer verification */
- if (!block->bb_u.s.bb_leftsib ||
- (be32_to_cpu(block->bb_u.s.bb_leftsib) >= mp->m_sb.sb_agblocks &&
- block->bb_u.s.bb_leftsib != cpu_to_be32(NULLAGBLOCK)))
- return false;
- if (!block->bb_u.s.bb_rightsib ||
- (be32_to_cpu(block->bb_u.s.bb_rightsib) >= mp->m_sb.sb_agblocks &&
- block->bb_u.s.bb_rightsib != cpu_to_be32(NULLAGBLOCK)))
- return false;
-
- return true;
+ return xfs_btree_sblock_verify(bp, mp->m_alloc_mxr[level != 0]);
}
static void
@@ -379,6 +351,7 @@ xfs_allocbt_write_verify(
}
const struct xfs_buf_ops xfs_allocbt_buf_ops = {
+ .name = "xfs_allocbt",
.verify_read = xfs_allocbt_read_verify,
.verify_write = xfs_allocbt_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index f949818fa1c7..fa3b948ef9c2 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -207,7 +207,7 @@ xfs_attr_set(
struct xfs_trans_res tres;
xfs_fsblock_t firstblock;
int rsvd = (flags & ATTR_ROOT) != 0;
- int error, err2, committed, local;
+ int error, err2, local;
XFS_STATS_INC(mp, xs_attr_set);
@@ -334,25 +334,15 @@ xfs_attr_set(
*/
xfs_bmap_init(args.flist, args.firstblock);
error = xfs_attr_shortform_to_leaf(&args);
- if (!error) {
- error = xfs_bmap_finish(&args.trans, args.flist,
- &committed);
- }
+ if (!error)
+ error = xfs_bmap_finish(&args.trans, args.flist, dp);
if (error) {
- ASSERT(committed);
args.trans = NULL;
xfs_bmap_cancel(&flist);
goto out;
}
/*
- * bmap_finish() may have committed the last trans and started
- * a new one. We need the inode to be in all transactions.
- */
- if (committed)
- xfs_trans_ijoin(args.trans, dp, 0);
-
- /*
* Commit the leaf transformation. We'll need another (linked)
* transaction to add the new attribute to the leaf.
*/
@@ -568,7 +558,7 @@ xfs_attr_leaf_addname(xfs_da_args_t *args)
{
xfs_inode_t *dp;
struct xfs_buf *bp;
- int retval, error, committed, forkoff;
+ int retval, error, forkoff;
trace_xfs_attr_leaf_addname(args);
@@ -628,25 +618,15 @@ xfs_attr_leaf_addname(xfs_da_args_t *args)
*/
xfs_bmap_init(args->flist, args->firstblock);
error = xfs_attr3_leaf_to_node(args);
- if (!error) {
- error = xfs_bmap_finish(&args->trans, args->flist,
- &committed);
- }
+ if (!error)
+ error = xfs_bmap_finish(&args->trans, args->flist, dp);
if (error) {
- ASSERT(committed);
args->trans = NULL;
xfs_bmap_cancel(args->flist);
return error;
}
/*
- * bmap_finish() may have committed the last trans and started
- * a new one. We need the inode to be in all transactions.
- */
- if (committed)
- xfs_trans_ijoin(args->trans, dp, 0);
-
- /*
* Commit the current trans (including the inode) and start
* a new one.
*/
@@ -729,25 +709,14 @@ xfs_attr_leaf_addname(xfs_da_args_t *args)
xfs_bmap_init(args->flist, args->firstblock);
error = xfs_attr3_leaf_to_shortform(bp, args, forkoff);
/* bp is gone due to xfs_da_shrink_inode */
- if (!error) {
+ if (!error)
error = xfs_bmap_finish(&args->trans,
- args->flist,
- &committed);
- }
+ args->flist, dp);
if (error) {
- ASSERT(committed);
args->trans = NULL;
xfs_bmap_cancel(args->flist);
return error;
}
-
- /*
- * bmap_finish() may have committed the last trans
- * and started a new one. We need the inode to be
- * in all transactions.
- */
- if (committed)
- xfs_trans_ijoin(args->trans, dp, 0);
}
/*
@@ -775,7 +744,7 @@ xfs_attr_leaf_removename(xfs_da_args_t *args)
{
xfs_inode_t *dp;
struct xfs_buf *bp;
- int error, committed, forkoff;
+ int error, forkoff;
trace_xfs_attr_leaf_removename(args);
@@ -803,23 +772,13 @@ xfs_attr_leaf_removename(xfs_da_args_t *args)
xfs_bmap_init(args->flist, args->firstblock);
error = xfs_attr3_leaf_to_shortform(bp, args, forkoff);
/* bp is gone due to xfs_da_shrink_inode */
- if (!error) {
- error = xfs_bmap_finish(&args->trans, args->flist,
- &committed);
- }
+ if (!error)
+ error = xfs_bmap_finish(&args->trans, args->flist, dp);
if (error) {
- ASSERT(committed);
args->trans = NULL;
xfs_bmap_cancel(args->flist);
return error;
}
-
- /*
- * bmap_finish() may have committed the last trans and started
- * a new one. We need the inode to be in all transactions.
- */
- if (committed)
- xfs_trans_ijoin(args->trans, dp, 0);
}
return 0;
}
@@ -877,7 +836,7 @@ xfs_attr_node_addname(xfs_da_args_t *args)
xfs_da_state_blk_t *blk;
xfs_inode_t *dp;
xfs_mount_t *mp;
- int committed, retval, error;
+ int retval, error;
trace_xfs_attr_node_addname(args);
@@ -938,27 +897,16 @@ restart:
state = NULL;
xfs_bmap_init(args->flist, args->firstblock);
error = xfs_attr3_leaf_to_node(args);
- if (!error) {
+ if (!error)
error = xfs_bmap_finish(&args->trans,
- args->flist,
- &committed);
- }
+ args->flist, dp);
if (error) {
- ASSERT(committed);
args->trans = NULL;
xfs_bmap_cancel(args->flist);
goto out;
}
/*
- * bmap_finish() may have committed the last trans
- * and started a new one. We need the inode to be
- * in all transactions.
- */
- if (committed)
- xfs_trans_ijoin(args->trans, dp, 0);
-
- /*
* Commit the node conversion and start the next
* trans in the chain.
*/
@@ -977,23 +925,13 @@ restart:
*/
xfs_bmap_init(args->flist, args->firstblock);
error = xfs_da3_split(state);
- if (!error) {
- error = xfs_bmap_finish(&args->trans, args->flist,
- &committed);
- }
+ if (!error)
+ error = xfs_bmap_finish(&args->trans, args->flist, dp);
if (error) {
- ASSERT(committed);
args->trans = NULL;
xfs_bmap_cancel(args->flist);
goto out;
}
-
- /*
- * bmap_finish() may have committed the last trans and started
- * a new one. We need the inode to be in all transactions.
- */
- if (committed)
- xfs_trans_ijoin(args->trans, dp, 0);
} else {
/*
* Addition succeeded, update Btree hashvals.
@@ -1086,25 +1024,14 @@ restart:
if (retval && (state->path.active > 1)) {
xfs_bmap_init(args->flist, args->firstblock);
error = xfs_da3_join(state);
- if (!error) {
+ if (!error)
error = xfs_bmap_finish(&args->trans,
- args->flist,
- &committed);
- }
+ args->flist, dp);
if (error) {
- ASSERT(committed);
args->trans = NULL;
xfs_bmap_cancel(args->flist);
goto out;
}
-
- /*
- * bmap_finish() may have committed the last trans
- * and started a new one. We need the inode to be
- * in all transactions.
- */
- if (committed)
- xfs_trans_ijoin(args->trans, dp, 0);
}
/*
@@ -1146,7 +1073,7 @@ xfs_attr_node_removename(xfs_da_args_t *args)
xfs_da_state_blk_t *blk;
xfs_inode_t *dp;
struct xfs_buf *bp;
- int retval, error, committed, forkoff;
+ int retval, error, forkoff;
trace_xfs_attr_node_removename(args);
@@ -1220,24 +1147,13 @@ xfs_attr_node_removename(xfs_da_args_t *args)
if (retval && (state->path.active > 1)) {
xfs_bmap_init(args->flist, args->firstblock);
error = xfs_da3_join(state);
- if (!error) {
- error = xfs_bmap_finish(&args->trans, args->flist,
- &committed);
- }
+ if (!error)
+ error = xfs_bmap_finish(&args->trans, args->flist, dp);
if (error) {
- ASSERT(committed);
args->trans = NULL;
xfs_bmap_cancel(args->flist);
goto out;
}
-
- /*
- * bmap_finish() may have committed the last trans and started
- * a new one. We need the inode to be in all transactions.
- */
- if (committed)
- xfs_trans_ijoin(args->trans, dp, 0);
-
/*
* Commit the Btree join operation and start a new trans.
*/
@@ -1265,25 +1181,14 @@ xfs_attr_node_removename(xfs_da_args_t *args)
xfs_bmap_init(args->flist, args->firstblock);
error = xfs_attr3_leaf_to_shortform(bp, args, forkoff);
/* bp is gone due to xfs_da_shrink_inode */
- if (!error) {
+ if (!error)
error = xfs_bmap_finish(&args->trans,
- args->flist,
- &committed);
- }
+ args->flist, dp);
if (error) {
- ASSERT(committed);
args->trans = NULL;
xfs_bmap_cancel(args->flist);
goto out;
}
-
- /*
- * bmap_finish() may have committed the last trans
- * and started a new one. We need the inode to be
- * in all transactions.
- */
- if (committed)
- xfs_trans_ijoin(args->trans, dp, 0);
} else
xfs_trans_brelse(args->trans, bp);
}
diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
index aa187f7ba2dd..01a5ecfedfcf 100644
--- a/fs/xfs/libxfs/xfs_attr_leaf.c
+++ b/fs/xfs/libxfs/xfs_attr_leaf.c
@@ -328,6 +328,7 @@ xfs_attr3_leaf_read_verify(
}
const struct xfs_buf_ops xfs_attr3_leaf_buf_ops = {
+ .name = "xfs_attr3_leaf",
.verify_read = xfs_attr3_leaf_read_verify,
.verify_write = xfs_attr3_leaf_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
index 5ab95ffa4ae9..a572532a55cd 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.c
+++ b/fs/xfs/libxfs/xfs_attr_remote.c
@@ -201,6 +201,7 @@ xfs_attr3_rmt_write_verify(
}
const struct xfs_buf_ops xfs_attr3_rmt_buf_ops = {
+ .name = "xfs_attr3_rmt",
.verify_read = xfs_attr3_rmt_read_verify,
.verify_write = xfs_attr3_rmt_write_verify,
};
@@ -447,8 +448,6 @@ xfs_attr_rmtval_set(
* Roll through the "value", allocating blocks on disk as required.
*/
while (blkcnt > 0) {
- int committed;
-
/*
* Allocate a single extent, up to the size of the value.
*
@@ -466,24 +465,14 @@ xfs_attr_rmtval_set(
error = xfs_bmapi_write(args->trans, dp, (xfs_fileoff_t)lblkno,
blkcnt, XFS_BMAPI_ATTRFORK, args->firstblock,
args->total, &map, &nmap, args->flist);
- if (!error) {
- error = xfs_bmap_finish(&args->trans, args->flist,
- &committed);
- }
+ if (!error)
+ error = xfs_bmap_finish(&args->trans, args->flist, dp);
if (error) {
- ASSERT(committed);
args->trans = NULL;
xfs_bmap_cancel(args->flist);
return error;
}
- /*
- * bmap_finish() may have committed the last trans and started
- * a new one. We need the inode to be in all transactions.
- */
- if (committed)
- xfs_trans_ijoin(args->trans, dp, 0);
-
ASSERT(nmap == 1);
ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
(map.br_startblock != HOLESTARTBLOCK));
@@ -614,31 +603,20 @@ xfs_attr_rmtval_remove(
blkcnt = args->rmtblkcnt;
done = 0;
while (!done) {
- int committed;
-
xfs_bmap_init(args->flist, args->firstblock);
error = xfs_bunmapi(args->trans, args->dp, lblkno, blkcnt,
XFS_BMAPI_ATTRFORK, 1, args->firstblock,
args->flist, &done);
- if (!error) {
+ if (!error)
error = xfs_bmap_finish(&args->trans, args->flist,
- &committed);
- }
+ args->dp);
if (error) {
- ASSERT(committed);
args->trans = NULL;
xfs_bmap_cancel(args->flist);
return error;
}
/*
- * bmap_finish() may have committed the last trans and started
- * a new one. We need the inode to be in all transactions.
- */
- if (committed)
- xfs_trans_ijoin(args->trans, args->dp, 0);
-
- /*
* Close out trans and start the next one in the chain.
*/
error = xfs_trans_roll(&args->trans, args->dp);
diff --git a/fs/xfs/libxfs/xfs_bit.c b/fs/xfs/libxfs/xfs_bit.c
index 0e8885a59646..0a94cce5ea35 100644
--- a/fs/xfs/libxfs/xfs_bit.c
+++ b/fs/xfs/libxfs/xfs_bit.c
@@ -32,13 +32,13 @@ int
xfs_bitmap_empty(uint *map, uint size)
{
uint i;
- uint ret = 0;
for (i = 0; i < size; i++) {
- ret |= map[i];
+ if (map[i] != 0)
+ return 0;
}
- return (ret == 0);
+ return 1;
}
/*
diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 119c2422aac7..ef00156f4f96 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -325,9 +325,11 @@ xfs_check_block(
/*
* Check that the extents for the inode ip are in the right order in all
- * btree leaves.
+ * btree leaves. THis becomes prohibitively expensive for large extent count
+ * files, so don't bother with inodes that have more than 10,000 extents in
+ * them. The btree record ordering checks will still be done, so for such large
+ * bmapbt constructs that is going to catch most corruptions.
*/
-
STATIC void
xfs_bmap_check_leaf_extents(
xfs_btree_cur_t *cur, /* btree cursor or null */
@@ -352,6 +354,10 @@ xfs_bmap_check_leaf_extents(
return;
}
+ /* skip large extent count inodes */
+ if (ip->i_d.di_nextents > 10000)
+ return;
+
bno = NULLFSBLOCK;
mp = ip->i_mount;
ifp = XFS_IFORK_PTR(ip, whichfork);
@@ -1111,7 +1117,6 @@ xfs_bmap_add_attrfork(
xfs_trans_t *tp; /* transaction pointer */
int blks; /* space reservation */
int version = 1; /* superblock attr version */
- int committed; /* xaction was committed */
int logflags; /* logging flags */
int error; /* error return value */
@@ -1214,7 +1219,7 @@ xfs_bmap_add_attrfork(
xfs_log_sb(tp);
}
- error = xfs_bmap_finish(&tp, &flist, &committed);
+ error = xfs_bmap_finish(&tp, &flist, NULL);
if (error)
goto bmap_cancel;
error = xfs_trans_commit(tp);
@@ -1723,10 +1728,11 @@ xfs_bmap_add_extent_delay_real(
xfs_filblks_t temp=0; /* value for da_new calculations */
xfs_filblks_t temp2=0;/* value for da_new calculations */
int tmp_rval; /* partial logging flags */
+ int whichfork = XFS_DATA_FORK;
struct xfs_mount *mp;
- mp = bma->tp ? bma->tp->t_mountp : NULL;
- ifp = XFS_IFORK_PTR(bma->ip, XFS_DATA_FORK);
+ mp = bma->ip->i_mount;
+ ifp = XFS_IFORK_PTR(bma->ip, whichfork);
ASSERT(bma->idx >= 0);
ASSERT(bma->idx <= ifp->if_bytes / sizeof(struct xfs_bmbt_rec));
@@ -1785,7 +1791,7 @@ xfs_bmap_add_extent_delay_real(
* Don't set contiguous if the combined extent would be too large.
* Also check for all-three-contiguous being too large.
*/
- if (bma->idx < bma->ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t) - 1) {
+ if (bma->idx < ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t) - 1) {
state |= BMAP_RIGHT_VALID;
xfs_bmbt_get_all(xfs_iext_get_ext(ifp, bma->idx + 1), &RIGHT);
@@ -2016,10 +2022,10 @@ xfs_bmap_add_extent_delay_real(
XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
}
- if (xfs_bmap_needs_btree(bma->ip, XFS_DATA_FORK)) {
+ if (xfs_bmap_needs_btree(bma->ip, whichfork)) {
error = xfs_bmap_extents_to_btree(bma->tp, bma->ip,
bma->firstblock, bma->flist,
- &bma->cur, 1, &tmp_rval, XFS_DATA_FORK);
+ &bma->cur, 1, &tmp_rval, whichfork);
rval |= tmp_rval;
if (error)
goto done;
@@ -2100,10 +2106,10 @@ xfs_bmap_add_extent_delay_real(
XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
}
- if (xfs_bmap_needs_btree(bma->ip, XFS_DATA_FORK)) {
+ if (xfs_bmap_needs_btree(bma->ip, whichfork)) {
error = xfs_bmap_extents_to_btree(bma->tp, bma->ip,
bma->firstblock, bma->flist, &bma->cur, 1,
- &tmp_rval, XFS_DATA_FORK);
+ &tmp_rval, whichfork);
rval |= tmp_rval;
if (error)
goto done;
@@ -2169,10 +2175,10 @@ xfs_bmap_add_extent_delay_real(
XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
}
- if (xfs_bmap_needs_btree(bma->ip, XFS_DATA_FORK)) {
+ if (xfs_bmap_needs_btree(bma->ip, whichfork)) {
error = xfs_bmap_extents_to_btree(bma->tp, bma->ip,
bma->firstblock, bma->flist, &bma->cur,
- 1, &tmp_rval, XFS_DATA_FORK);
+ 1, &tmp_rval, whichfork);
rval |= tmp_rval;
if (error)
goto done;
@@ -2215,13 +2221,13 @@ xfs_bmap_add_extent_delay_real(
}
/* convert to a btree if necessary */
- if (xfs_bmap_needs_btree(bma->ip, XFS_DATA_FORK)) {
+ if (xfs_bmap_needs_btree(bma->ip, whichfork)) {
int tmp_logflags; /* partial log flag return val */
ASSERT(bma->cur == NULL);
error = xfs_bmap_extents_to_btree(bma->tp, bma->ip,
bma->firstblock, bma->flist, &bma->cur,
- da_old > 0, &tmp_logflags, XFS_DATA_FORK);
+ da_old > 0, &tmp_logflags, whichfork);
bma->logflags |= tmp_logflags;
if (error)
goto done;
@@ -2242,7 +2248,7 @@ xfs_bmap_add_extent_delay_real(
if (bma->cur)
bma->cur->bc_private.b.allocated = 0;
- xfs_bmap_check_leaf_extents(bma->cur, bma->ip, XFS_DATA_FORK);
+ xfs_bmap_check_leaf_extents(bma->cur, bma->ip, whichfork);
done:
bma->logflags |= rval;
return error;
@@ -2939,7 +2945,7 @@ xfs_bmap_add_extent_hole_real(
int state; /* state bits, accessed thru macros */
struct xfs_mount *mp;
- mp = bma->tp ? bma->tp->t_mountp : NULL;
+ mp = bma->ip->i_mount;
ifp = XFS_IFORK_PTR(bma->ip, whichfork);
ASSERT(bma->idx >= 0);
@@ -5950,7 +5956,6 @@ xfs_bmap_split_extent(
struct xfs_trans *tp;
struct xfs_bmap_free free_list;
xfs_fsblock_t firstfsb;
- int committed;
int error;
tp = xfs_trans_alloc(mp, XFS_TRANS_DIOSTRAT);
@@ -5971,7 +5976,7 @@ xfs_bmap_split_extent(
if (error)
goto out;
- error = xfs_bmap_finish(&tp, &free_list, &committed);
+ error = xfs_bmap_finish(&tp, &free_list, NULL);
if (error)
goto out;
diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
index a160f8a5a3fc..423a34e832bd 100644
--- a/fs/xfs/libxfs/xfs_bmap.h
+++ b/fs/xfs/libxfs/xfs_bmap.h
@@ -195,7 +195,7 @@ void xfs_bmap_add_free(xfs_fsblock_t bno, xfs_filblks_t len,
struct xfs_bmap_free *flist, struct xfs_mount *mp);
void xfs_bmap_cancel(struct xfs_bmap_free *flist);
int xfs_bmap_finish(struct xfs_trans **tp, struct xfs_bmap_free *flist,
- int *committed);
+ struct xfs_inode *ip);
void xfs_bmap_compute_maxlevels(struct xfs_mount *mp, int whichfork);
int xfs_bmap_first_unused(struct xfs_trans *tp, struct xfs_inode *ip,
xfs_extlen_t len, xfs_fileoff_t *unused, int whichfork);
diff --git a/fs/xfs/libxfs/xfs_bmap_btree.c b/fs/xfs/libxfs/xfs_bmap_btree.c
index 6b0cf6546a82..1637c37bfbaa 100644
--- a/fs/xfs/libxfs/xfs_bmap_btree.c
+++ b/fs/xfs/libxfs/xfs_bmap_btree.c
@@ -720,6 +720,7 @@ xfs_bmbt_write_verify(
}
const struct xfs_buf_ops xfs_bmbt_buf_ops = {
+ .name = "xfs_bmbt",
.verify_read = xfs_bmbt_read_verify,
.verify_write = xfs_bmbt_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_btree.c b/fs/xfs/libxfs/xfs_btree.c
index af1bbee5586e..a0eb18ce3ad3 100644
--- a/fs/xfs/libxfs/xfs_btree.c
+++ b/fs/xfs/libxfs/xfs_btree.c
@@ -4080,3 +4080,61 @@ xfs_btree_change_owner(
return 0;
}
+
+/**
+ * xfs_btree_sblock_v5hdr_verify() -- verify the v5 fields of a short-format
+ * btree block
+ *
+ * @bp: buffer containing the btree block
+ * @max_recs: pointer to the m_*_mxr max records field in the xfs mount
+ * @pag_max_level: pointer to the per-ag max level field
+ */
+bool
+xfs_btree_sblock_v5hdr_verify(
+ struct xfs_buf *bp)
+{
+ struct xfs_mount *mp = bp->b_target->bt_mount;
+ struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp);
+ struct xfs_perag *pag = bp->b_pag;
+
+ if (!xfs_sb_version_hascrc(&mp->m_sb))
+ return false;
+ if (!uuid_equal(&block->bb_u.s.bb_uuid, &mp->m_sb.sb_meta_uuid))
+ return false;
+ if (block->bb_u.s.bb_blkno != cpu_to_be64(bp->b_bn))
+ return false;
+ if (pag && be32_to_cpu(block->bb_u.s.bb_owner) != pag->pag_agno)
+ return false;
+ return true;
+}
+
+/**
+ * xfs_btree_sblock_verify() -- verify a short-format btree block
+ *
+ * @bp: buffer containing the btree block
+ * @max_recs: maximum records allowed in this btree node
+ */
+bool
+xfs_btree_sblock_verify(
+ struct xfs_buf *bp,
+ unsigned int max_recs)
+{
+ struct xfs_mount *mp = bp->b_target->bt_mount;
+ struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp);
+
+ /* numrecs verification */
+ if (be16_to_cpu(block->bb_numrecs) > max_recs)
+ return false;
+
+ /* sibling pointer verification */
+ if (!block->bb_u.s.bb_leftsib ||
+ (be32_to_cpu(block->bb_u.s.bb_leftsib) >= mp->m_sb.sb_agblocks &&
+ block->bb_u.s.bb_leftsib != cpu_to_be32(NULLAGBLOCK)))
+ return false;
+ if (!block->bb_u.s.bb_rightsib ||
+ (be32_to_cpu(block->bb_u.s.bb_rightsib) >= mp->m_sb.sb_agblocks &&
+ block->bb_u.s.bb_rightsib != cpu_to_be32(NULLAGBLOCK)))
+ return false;
+
+ return true;
+}
diff --git a/fs/xfs/libxfs/xfs_btree.h b/fs/xfs/libxfs/xfs_btree.h
index 992dec0638f3..2e874be70209 100644
--- a/fs/xfs/libxfs/xfs_btree.h
+++ b/fs/xfs/libxfs/xfs_btree.h
@@ -472,4 +472,7 @@ static inline int xfs_btree_get_level(struct xfs_btree_block *block)
#define XFS_BTREE_TRACE_ARGR(c, r)
#define XFS_BTREE_TRACE_CURSOR(c, t)
+bool xfs_btree_sblock_v5hdr_verify(struct xfs_buf *bp);
+bool xfs_btree_sblock_verify(struct xfs_buf *bp, unsigned int max_recs);
+
#endif /* __XFS_BTREE_H__ */
diff --git a/fs/xfs/libxfs/xfs_da_btree.c b/fs/xfs/libxfs/xfs_da_btree.c
index e89a0f8f827c..097bf7717d80 100644
--- a/fs/xfs/libxfs/xfs_da_btree.c
+++ b/fs/xfs/libxfs/xfs_da_btree.c
@@ -245,6 +245,7 @@ xfs_da3_node_read_verify(
}
const struct xfs_buf_ops xfs_da3_node_buf_ops = {
+ .name = "xfs_da3_node",
.verify_read = xfs_da3_node_read_verify,
.verify_write = xfs_da3_node_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c
index 9c10e2b8cfcb..aa17cb788946 100644
--- a/fs/xfs/libxfs/xfs_dir2_block.c
+++ b/fs/xfs/libxfs/xfs_dir2_block.c
@@ -123,6 +123,7 @@ xfs_dir3_block_write_verify(
}
const struct xfs_buf_ops xfs_dir3_block_buf_ops = {
+ .name = "xfs_dir3_block",
.verify_read = xfs_dir3_block_read_verify,
.verify_write = xfs_dir3_block_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_dir2_data.c b/fs/xfs/libxfs/xfs_dir2_data.c
index af71a84f343c..725fc7841fde 100644
--- a/fs/xfs/libxfs/xfs_dir2_data.c
+++ b/fs/xfs/libxfs/xfs_dir2_data.c
@@ -305,11 +305,13 @@ xfs_dir3_data_write_verify(
}
const struct xfs_buf_ops xfs_dir3_data_buf_ops = {
+ .name = "xfs_dir3_data",
.verify_read = xfs_dir3_data_read_verify,
.verify_write = xfs_dir3_data_write_verify,
};
static const struct xfs_buf_ops xfs_dir3_data_reada_buf_ops = {
+ .name = "xfs_dir3_data_reada",
.verify_read = xfs_dir3_data_reada_verify,
.verify_write = xfs_dir3_data_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_dir2_leaf.c b/fs/xfs/libxfs/xfs_dir2_leaf.c
index 3923e1f94697..b887fb2a2bcf 100644
--- a/fs/xfs/libxfs/xfs_dir2_leaf.c
+++ b/fs/xfs/libxfs/xfs_dir2_leaf.c
@@ -245,11 +245,13 @@ xfs_dir3_leafn_write_verify(
}
const struct xfs_buf_ops xfs_dir3_leaf1_buf_ops = {
+ .name = "xfs_dir3_leaf1",
.verify_read = xfs_dir3_leaf1_read_verify,
.verify_write = xfs_dir3_leaf1_write_verify,
};
const struct xfs_buf_ops xfs_dir3_leafn_buf_ops = {
+ .name = "xfs_dir3_leafn",
.verify_read = xfs_dir3_leafn_read_verify,
.verify_write = xfs_dir3_leafn_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c
index 70b0cb2fd556..63ee03db796c 100644
--- a/fs/xfs/libxfs/xfs_dir2_node.c
+++ b/fs/xfs/libxfs/xfs_dir2_node.c
@@ -150,6 +150,7 @@ xfs_dir3_free_write_verify(
}
const struct xfs_buf_ops xfs_dir3_free_buf_ops = {
+ .name = "xfs_dir3_free",
.verify_read = xfs_dir3_free_read_verify,
.verify_write = xfs_dir3_free_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_dquot_buf.c b/fs/xfs/libxfs/xfs_dquot_buf.c
index 5331b7f0460c..3cc3cf767474 100644
--- a/fs/xfs/libxfs/xfs_dquot_buf.c
+++ b/fs/xfs/libxfs/xfs_dquot_buf.c
@@ -54,7 +54,7 @@ xfs_dqcheck(
xfs_dqid_t id,
uint type, /* used only when IO_dorepair is true */
uint flags,
- char *str)
+ const char *str)
{
xfs_dqblk_t *d = (xfs_dqblk_t *)ddq;
int errs = 0;
@@ -207,7 +207,8 @@ xfs_dquot_buf_verify_crc(
STATIC bool
xfs_dquot_buf_verify(
struct xfs_mount *mp,
- struct xfs_buf *bp)
+ struct xfs_buf *bp,
+ int warn)
{
struct xfs_dqblk *d = (struct xfs_dqblk *)bp->b_addr;
xfs_dqid_t id = 0;
@@ -240,8 +241,7 @@ xfs_dquot_buf_verify(
if (i == 0)
id = be32_to_cpu(ddq->d_id);
- error = xfs_dqcheck(mp, ddq, id + i, 0, XFS_QMOPT_DOWARN,
- "xfs_dquot_buf_verify");
+ error = xfs_dqcheck(mp, ddq, id + i, 0, warn, __func__);
if (error)
return false;
}
@@ -256,7 +256,7 @@ xfs_dquot_buf_read_verify(
if (!xfs_dquot_buf_verify_crc(mp, bp))
xfs_buf_ioerror(bp, -EFSBADCRC);
- else if (!xfs_dquot_buf_verify(mp, bp))
+ else if (!xfs_dquot_buf_verify(mp, bp, XFS_QMOPT_DOWARN))
xfs_buf_ioerror(bp, -EFSCORRUPTED);
if (bp->b_error)
@@ -264,6 +264,25 @@ xfs_dquot_buf_read_verify(
}
/*
+ * readahead errors are silent and simply leave the buffer as !done so a real
+ * read will then be run with the xfs_dquot_buf_ops verifier. See
+ * xfs_inode_buf_verify() for why we use EIO and ~XBF_DONE here rather than
+ * reporting the failure.
+ */
+static void
+xfs_dquot_buf_readahead_verify(
+ struct xfs_buf *bp)
+{
+ struct xfs_mount *mp = bp->b_target->bt_mount;
+
+ if (!xfs_dquot_buf_verify_crc(mp, bp) ||
+ !xfs_dquot_buf_verify(mp, bp, 0)) {
+ xfs_buf_ioerror(bp, -EIO);
+ bp->b_flags &= ~XBF_DONE;
+ }
+}
+
+/*
* we don't calculate the CRC here as that is done when the dquot is flushed to
* the buffer after the update is done. This ensures that the dquot in the
* buffer always has an up-to-date CRC value.
@@ -274,7 +293,7 @@ xfs_dquot_buf_write_verify(
{
struct xfs_mount *mp = bp->b_target->bt_mount;
- if (!xfs_dquot_buf_verify(mp, bp)) {
+ if (!xfs_dquot_buf_verify(mp, bp, XFS_QMOPT_DOWARN)) {
xfs_buf_ioerror(bp, -EFSCORRUPTED);
xfs_verifier_error(bp);
return;
@@ -282,7 +301,13 @@ xfs_dquot_buf_write_verify(
}
const struct xfs_buf_ops xfs_dquot_buf_ops = {
+ .name = "xfs_dquot",
.verify_read = xfs_dquot_buf_read_verify,
.verify_write = xfs_dquot_buf_write_verify,
};
+const struct xfs_buf_ops xfs_dquot_buf_ra_ops = {
+ .name = "xfs_dquot_ra",
+ .verify_read = xfs_dquot_buf_readahead_verify,
+ .verify_write = xfs_dquot_buf_write_verify,
+};
diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
index 8774498ce0ff..e2536bb1c760 100644
--- a/fs/xfs/libxfs/xfs_format.h
+++ b/fs/xfs/libxfs/xfs_format.h
@@ -786,7 +786,7 @@ typedef struct xfs_agfl {
__be64 agfl_lsn;
__be32 agfl_crc;
__be32 agfl_bno[]; /* actually XFS_AGFL_SIZE(mp) */
-} xfs_agfl_t;
+} __attribute__((packed)) xfs_agfl_t;
#define XFS_AGFL_CRC_OFF offsetof(struct xfs_agfl, agfl_crc)
diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c
index 70c1db99f6a7..66d702e6b9ff 100644
--- a/fs/xfs/libxfs/xfs_ialloc.c
+++ b/fs/xfs/libxfs/xfs_ialloc.c
@@ -2572,6 +2572,7 @@ xfs_agi_write_verify(
}
const struct xfs_buf_ops xfs_agi_buf_ops = {
+ .name = "xfs_agi",
.verify_read = xfs_agi_read_verify,
.verify_write = xfs_agi_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_ialloc_btree.c b/fs/xfs/libxfs/xfs_ialloc_btree.c
index f39b285beb19..c679f3c05b63 100644
--- a/fs/xfs/libxfs/xfs_ialloc_btree.c
+++ b/fs/xfs/libxfs/xfs_ialloc_btree.c
@@ -221,7 +221,6 @@ xfs_inobt_verify(
{
struct xfs_mount *mp = bp->b_target->bt_mount;
struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp);
- struct xfs_perag *pag = bp->b_pag;
unsigned int level;
/*
@@ -237,14 +236,7 @@ xfs_inobt_verify(
switch (block->bb_magic) {
case cpu_to_be32(XFS_IBT_CRC_MAGIC):
case cpu_to_be32(XFS_FIBT_CRC_MAGIC):
- if (!xfs_sb_version_hascrc(&mp->m_sb))
- return false;
- if (!uuid_equal(&block->bb_u.s.bb_uuid, &mp->m_sb.sb_meta_uuid))
- return false;
- if (block->bb_u.s.bb_blkno != cpu_to_be64(bp->b_bn))
- return false;
- if (pag &&
- be32_to_cpu(block->bb_u.s.bb_owner) != pag->pag_agno)
+ if (!xfs_btree_sblock_v5hdr_verify(bp))
return false;
/* fall through */
case cpu_to_be32(XFS_IBT_MAGIC):
@@ -254,24 +246,12 @@ xfs_inobt_verify(
return 0;
}
- /* numrecs and level verification */
+ /* level verification */
level = be16_to_cpu(block->bb_level);
if (level >= mp->m_in_maxlevels)
return false;
- if (be16_to_cpu(block->bb_numrecs) > mp->m_inobt_mxr[level != 0])
- return false;
-
- /* sibling pointer verification */
- if (!block->bb_u.s.bb_leftsib ||
- (be32_to_cpu(block->bb_u.s.bb_leftsib) >= mp->m_sb.sb_agblocks &&
- block->bb_u.s.bb_leftsib != cpu_to_be32(NULLAGBLOCK)))
- return false;
- if (!block->bb_u.s.bb_rightsib ||
- (be32_to_cpu(block->bb_u.s.bb_rightsib) >= mp->m_sb.sb_agblocks &&
- block->bb_u.s.bb_rightsib != cpu_to_be32(NULLAGBLOCK)))
- return false;
- return true;
+ return xfs_btree_sblock_verify(bp, mp->m_inobt_mxr[level != 0]);
}
static void
@@ -304,6 +284,7 @@ xfs_inobt_write_verify(
}
const struct xfs_buf_ops xfs_inobt_buf_ops = {
+ .name = "xfs_inobt",
.verify_read = xfs_inobt_read_verify,
.verify_write = xfs_inobt_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
index 268c00f4f83a..1aabfda669b0 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.c
+++ b/fs/xfs/libxfs/xfs_inode_buf.c
@@ -62,11 +62,14 @@ xfs_inobp_check(
* has not had the inode cores stamped into it. Hence for readahead, the buffer
* may be potentially invalid.
*
- * If the readahead buffer is invalid, we don't want to mark it with an error,
- * but we do want to clear the DONE status of the buffer so that a followup read
- * will re-read it from disk. This will ensure that we don't get an unnecessary
- * warnings during log recovery and we don't get unnecssary panics on debug
- * kernels.
+ * If the readahead buffer is invalid, we need to mark it with an error and
+ * clear the DONE status of the buffer so that a followup read will re-read it
+ * from disk. We don't report the error otherwise to avoid warnings during log
+ * recovery and we don't get unnecssary panics on debug kernels. We use EIO here
+ * because all we want to do is say readahead failed; there is no-one to report
+ * the error to, so this will distinguish it from a non-ra verifier failure.
+ * Changes to this readahead error behavour also need to be reflected in
+ * xfs_dquot_buf_readahead_verify().
*/
static void
xfs_inode_buf_verify(
@@ -93,6 +96,7 @@ xfs_inode_buf_verify(
XFS_RANDOM_ITOBP_INOTOBP))) {
if (readahead) {
bp->b_flags &= ~XBF_DONE;
+ xfs_buf_ioerror(bp, -EIO);
return;
}
@@ -132,11 +136,13 @@ xfs_inode_buf_write_verify(
}
const struct xfs_buf_ops xfs_inode_buf_ops = {
+ .name = "xfs_inode",
.verify_read = xfs_inode_buf_read_verify,
.verify_write = xfs_inode_buf_write_verify,
};
const struct xfs_buf_ops xfs_inode_buf_ra_ops = {
+ .name = "xxfs_inode_ra",
.verify_read = xfs_inode_buf_readahead_verify,
.verify_write = xfs_inode_buf_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_log_recover.h b/fs/xfs/libxfs/xfs_log_recover.h
index 1c55ccbb379d..8e385f91d660 100644
--- a/fs/xfs/libxfs/xfs_log_recover.h
+++ b/fs/xfs/libxfs/xfs_log_recover.h
@@ -60,6 +60,7 @@ typedef struct xlog_recover {
*/
#define XLOG_BC_TABLE_SIZE 64
+#define XLOG_RECOVER_CRCPASS 0
#define XLOG_RECOVER_PASS1 1
#define XLOG_RECOVER_PASS2 2
diff --git a/fs/xfs/libxfs/xfs_quota_defs.h b/fs/xfs/libxfs/xfs_quota_defs.h
index 1b0a08379759..f51078f1e92a 100644
--- a/fs/xfs/libxfs/xfs_quota_defs.h
+++ b/fs/xfs/libxfs/xfs_quota_defs.h
@@ -153,7 +153,7 @@ typedef __uint16_t xfs_qwarncnt_t;
#define XFS_QMOPT_RESBLK_MASK (XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_RES_RTBLKS)
extern int xfs_dqcheck(struct xfs_mount *mp, xfs_disk_dquot_t *ddq,
- xfs_dqid_t id, uint type, uint flags, char *str);
+ xfs_dqid_t id, uint type, uint flags, const char *str);
extern int xfs_calc_dquots_per_chunk(unsigned int nbblks);
#endif /* __XFS_QUOTA_H__ */
diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c
index a0b071d881a0..8a53eaa349f4 100644
--- a/fs/xfs/libxfs/xfs_sb.c
+++ b/fs/xfs/libxfs/xfs_sb.c
@@ -679,11 +679,13 @@ xfs_sb_write_verify(
}
const struct xfs_buf_ops xfs_sb_buf_ops = {
+ .name = "xfs_sb",
.verify_read = xfs_sb_read_verify,
.verify_write = xfs_sb_write_verify,
};
const struct xfs_buf_ops xfs_sb_quiet_buf_ops = {
+ .name = "xfs_sb_quiet",
.verify_read = xfs_sb_quiet_read_verify,
.verify_write = xfs_sb_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_shared.h b/fs/xfs/libxfs/xfs_shared.h
index 5be529707903..15c3ceb845b9 100644
--- a/fs/xfs/libxfs/xfs_shared.h
+++ b/fs/xfs/libxfs/xfs_shared.h
@@ -49,6 +49,7 @@ extern const struct xfs_buf_ops xfs_inobt_buf_ops;
extern const struct xfs_buf_ops xfs_inode_buf_ops;
extern const struct xfs_buf_ops xfs_inode_buf_ra_ops;
extern const struct xfs_buf_ops xfs_dquot_buf_ops;
+extern const struct xfs_buf_ops xfs_dquot_buf_ra_ops;
extern const struct xfs_buf_ops xfs_sb_buf_ops;
extern const struct xfs_buf_ops xfs_sb_quiet_buf_ops;
extern const struct xfs_buf_ops xfs_symlink_buf_ops;
diff --git a/fs/xfs/libxfs/xfs_symlink_remote.c b/fs/xfs/libxfs/xfs_symlink_remote.c
index cb6fd20a4d3d..2e2c6716b623 100644
--- a/fs/xfs/libxfs/xfs_symlink_remote.c
+++ b/fs/xfs/libxfs/xfs_symlink_remote.c
@@ -168,6 +168,7 @@ xfs_symlink_write_verify(
}
const struct xfs_buf_ops xfs_symlink_buf_ops = {
+ .name = "xfs_symlink",
.verify_read = xfs_symlink_read_verify,
.verify_write = xfs_symlink_write_verify,
};
diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c
index 6bb470fbb8e8..2d5df1f23bbc 100644
--- a/fs/xfs/xfs_acl.c
+++ b/fs/xfs/xfs_acl.c
@@ -252,29 +252,6 @@ xfs_set_mode(struct inode *inode, umode_t mode)
return error;
}
-static int
-xfs_acl_exists(struct inode *inode, unsigned char *name)
-{
- int len = XFS_ACL_MAX_SIZE(XFS_M(inode->i_sb));
-
- return (xfs_attr_get(XFS_I(inode), name, NULL, &len,
- ATTR_ROOT|ATTR_KERNOVAL) == 0);
-}
-
-int
-posix_acl_access_exists(struct inode *inode)
-{
- return xfs_acl_exists(inode, SGI_ACL_FILE);
-}
-
-int
-posix_acl_default_exists(struct inode *inode)
-{
- if (!S_ISDIR(inode->i_mode))
- return 0;
- return xfs_acl_exists(inode, SGI_ACL_DEFAULT);
-}
-
int
xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
{
diff --git a/fs/xfs/xfs_acl.h b/fs/xfs/xfs_acl.h
index 52f8255d6bdf..286fa89217f5 100644
--- a/fs/xfs/xfs_acl.h
+++ b/fs/xfs/xfs_acl.h
@@ -24,16 +24,12 @@ struct posix_acl;
#ifdef CONFIG_XFS_POSIX_ACL
extern struct posix_acl *xfs_get_acl(struct inode *inode, int type);
extern int xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type);
-extern int posix_acl_access_exists(struct inode *inode);
-extern int posix_acl_default_exists(struct inode *inode);
#else
static inline struct posix_acl *xfs_get_acl(struct inode *inode, int type)
{
return NULL;
}
# define xfs_set_acl NULL
-# define posix_acl_access_exists(inode) 0
-# define posix_acl_default_exists(inode) 0
#endif /* CONFIG_XFS_POSIX_ACL */
extern void xfs_forget_acl(struct inode *inode, const char *name, int xflags);
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 29e7e5dd5178..379c089fb051 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -1917,6 +1917,7 @@ xfs_vm_readpage(
struct file *unused,
struct page *page)
{
+ trace_xfs_vm_readpage(page->mapping->host, 1);
return mpage_readpage(page, xfs_get_blocks);
}
@@ -1927,6 +1928,7 @@ xfs_vm_readpages(
struct list_head *pages,
unsigned nr_pages)
{
+ trace_xfs_vm_readpages(mapping->host, nr_pages);
return mpage_readpages(mapping, pages, nr_pages, xfs_get_blocks);
}
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index dbae6490a79a..45ec9e40150c 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -91,32 +91,32 @@ xfs_zero_extent(
* last due to locking considerations. We never free any extents in
* the first transaction.
*
- * Return 1 if the given transaction was committed and a new one
- * started, and 0 otherwise in the committed parameter.
+ * If an inode *ip is provided, rejoin it to the transaction if
+ * the transaction was committed.
*/
int /* error */
xfs_bmap_finish(
struct xfs_trans **tp, /* transaction pointer addr */
struct xfs_bmap_free *flist, /* i/o: list extents to free */
- int *committed)/* xact committed or not */
+ struct xfs_inode *ip)
{
struct xfs_efd_log_item *efd; /* extent free data */
struct xfs_efi_log_item *efi; /* extent free intention */
int error; /* error return value */
+ int committed;/* xact committed or not */
struct xfs_bmap_free_item *free; /* free extent item */
struct xfs_bmap_free_item *next; /* next item on free list */
ASSERT((*tp)->t_flags & XFS_TRANS_PERM_LOG_RES);
- if (flist->xbf_count == 0) {
- *committed = 0;
+ if (flist->xbf_count == 0)
return 0;
- }
+
efi = xfs_trans_get_efi(*tp, flist->xbf_count);
for (free = flist->xbf_first; free; free = free->xbfi_next)
xfs_trans_log_efi_extent(*tp, efi, free->xbfi_startblock,
free->xbfi_blockcount);
- error = __xfs_trans_roll(tp, NULL, committed);
+ error = __xfs_trans_roll(tp, ip, &committed);
if (error) {
/*
* If the transaction was committed, drop the EFD reference
@@ -128,16 +128,13 @@ xfs_bmap_finish(
* transaction so we should return committed=1 even though we're
* returning an error.
*/
- if (*committed) {
+ if (committed) {
xfs_efi_release(efi);
xfs_force_shutdown((*tp)->t_mountp,
(error == -EFSCORRUPTED) ?
SHUTDOWN_CORRUPT_INCORE :
SHUTDOWN_META_IO_ERROR);
- } else {
- *committed = 1;
}
-
return error;
}
@@ -969,7 +966,6 @@ xfs_alloc_file_space(
xfs_bmbt_irec_t imaps[1], *imapp;
xfs_bmap_free_t free_list;
uint qblocks, resblks, resrtextents;
- int committed;
int error;
trace_xfs_alloc_file_space(ip);
@@ -1064,23 +1060,20 @@ xfs_alloc_file_space(
error = xfs_bmapi_write(tp, ip, startoffset_fsb,
allocatesize_fsb, alloc_type, &firstfsb,
resblks, imapp, &nimaps, &free_list);
- if (error) {
+ if (error)
goto error0;
- }
/*
* Complete the transaction
*/
- error = xfs_bmap_finish(&tp, &free_list, &committed);
- if (error) {
+ error = xfs_bmap_finish(&tp, &free_list, NULL);
+ if (error)
goto error0;
- }
error = xfs_trans_commit(tp);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
- if (error) {
+ if (error)
break;
- }
allocated_fsb = imapp->br_blockcount;
@@ -1206,7 +1199,6 @@ xfs_free_file_space(
xfs_off_t offset,
xfs_off_t len)
{
- int committed;
int done;
xfs_fileoff_t endoffset_fsb;
int error;
@@ -1346,17 +1338,15 @@ xfs_free_file_space(
error = xfs_bunmapi(tp, ip, startoffset_fsb,
endoffset_fsb - startoffset_fsb,
0, 2, &firstfsb, &free_list, &done);
- if (error) {
+ if (error)
goto error0;
- }
/*
* complete the transaction
*/
- error = xfs_bmap_finish(&tp, &free_list, &committed);
- if (error) {
+ error = xfs_bmap_finish(&tp, &free_list, NULL);
+ if (error)
goto error0;
- }
error = xfs_trans_commit(tp);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
@@ -1434,7 +1424,6 @@ xfs_shift_file_space(
int error;
struct xfs_bmap_free free_list;
xfs_fsblock_t first_block;
- int committed;
xfs_fileoff_t stop_fsb;
xfs_fileoff_t next_fsb;
xfs_fileoff_t shift_fsb;
@@ -1526,7 +1515,7 @@ xfs_shift_file_space(
if (error)
goto out_bmap_cancel;
- error = xfs_bmap_finish(&tp, &free_list, &committed);
+ error = xfs_bmap_finish(&tp, &free_list, NULL);
if (error)
goto out_bmap_cancel;
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index 3243cdf97f33..daed4bfb85b2 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -604,6 +604,13 @@ found:
}
}
+ /*
+ * Clear b_error if this is a lookup from a caller that doesn't expect
+ * valid data to be found in the buffer.
+ */
+ if (!(flags & XBF_READ))
+ xfs_buf_ioerror(bp, 0);
+
XFS_STATS_INC(target->bt_mount, xb_get);
trace_xfs_buf_get(bp, flags, _RET_IP_);
return bp;
@@ -1045,7 +1052,7 @@ xfs_buf_ioend_work(
xfs_buf_ioend(bp);
}
-void
+static void
xfs_buf_ioend_async(
struct xfs_buf *bp)
{
@@ -1632,13 +1639,9 @@ xfs_setsize_buftarg(
btp->bt_meta_sectormask = sectorsize - 1;
if (set_blocksize(btp->bt_bdev, sectorsize)) {
- char name[BDEVNAME_SIZE];
-
- bdevname(btp->bt_bdev, name);
-
xfs_warn(btp->bt_mount,
- "Cannot set_blocksize to %u on device %s",
- sectorsize, name);
+ "Cannot set_blocksize to %u on device %pg",
+ sectorsize, btp->bt_bdev);
return -EINVAL;
}
diff --git a/fs/xfs/xfs_buf.h b/fs/xfs/xfs_buf.h
index c79b717d9b88..c75721acd867 100644
--- a/fs/xfs/xfs_buf.h
+++ b/fs/xfs/xfs_buf.h
@@ -132,6 +132,7 @@ struct xfs_buf_map {
struct xfs_buf_map (map) = { .bm_bn = (blkno), .bm_len = (numblk) };
struct xfs_buf_ops {
+ char *name;
void (*verify_read)(struct xfs_buf *);
void (*verify_write)(struct xfs_buf *);
};
diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c
index 7ac6c5c586cb..9c44d38dcd1f 100644
--- a/fs/xfs/xfs_dquot.c
+++ b/fs/xfs/xfs_dquot.c
@@ -306,7 +306,7 @@ xfs_qm_dqalloc(
xfs_fsblock_t firstblock;
xfs_bmap_free_t flist;
xfs_bmbt_irec_t map;
- int nmaps, error, committed;
+ int nmaps, error;
xfs_buf_t *bp;
xfs_trans_t *tp = *tpp;
@@ -379,11 +379,12 @@ xfs_qm_dqalloc(
xfs_trans_bhold(tp, bp);
- if ((error = xfs_bmap_finish(tpp, &flist, &committed))) {
+ error = xfs_bmap_finish(tpp, &flist, NULL);
+ if (error)
goto error1;
- }
- if (committed) {
+ /* Transaction was committed? */
+ if (*tpp != tp) {
tp = *tpp;
xfs_trans_bjoin(tp, bp);
} else {
@@ -393,9 +394,9 @@ xfs_qm_dqalloc(
*O_bpp = bp;
return 0;
- error1:
+error1:
xfs_bmap_cancel(&flist);
- error0:
+error0:
xfs_iunlock(quotip, XFS_ILOCK_EXCL);
return error;
diff --git a/fs/xfs/xfs_error.c b/fs/xfs/xfs_error.c
index 74d0e5966ebc..88693a98fac5 100644
--- a/fs/xfs/xfs_error.c
+++ b/fs/xfs/xfs_error.c
@@ -164,9 +164,9 @@ xfs_verifier_error(
{
struct xfs_mount *mp = bp->b_target->bt_mount;
- xfs_alert(mp, "Metadata %s detected at %pF, block 0x%llx",
+ xfs_alert(mp, "Metadata %s detected at %pF, %s block 0x%llx",
bp->b_error == -EFSBADCRC ? "CRC error" : "corruption",
- __return_address, bp->b_bn);
+ __return_address, bp->b_ops->name, bp->b_bn);
xfs_alert(mp, "Unmount and run xfs_repair");
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index f5392ab2def1..ebe9b8290a70 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -402,19 +402,26 @@ xfs_file_splice_read(
if (XFS_FORCED_SHUTDOWN(ip->i_mount))
return -EIO;
- xfs_rw_ilock(ip, XFS_IOLOCK_SHARED);
-
trace_xfs_file_splice_read(ip, count, *ppos, ioflags);
- /* for dax, we need to avoid the page cache */
- if (IS_DAX(VFS_I(ip)))
- ret = default_file_splice_read(infilp, ppos, pipe, count, flags);
- else
- ret = generic_file_splice_read(infilp, ppos, pipe, count, flags);
- if (ret > 0)
- XFS_STATS_ADD(ip->i_mount, xs_read_bytes, ret);
+ /*
+ * DAX inodes cannot ues the page cache for splice, so we have to push
+ * them through the VFS IO path. This means it goes through
+ * ->read_iter, which for us takes the XFS_IOLOCK_SHARED. Hence we
+ * cannot lock the splice operation at this level for DAX inodes.
+ */
+ if (IS_DAX(VFS_I(ip))) {
+ ret = default_file_splice_read(infilp, ppos, pipe, count,
+ flags);
+ goto out;
+ }
+ xfs_rw_ilock(ip, XFS_IOLOCK_SHARED);
+ ret = generic_file_splice_read(infilp, ppos, pipe, count, flags);
xfs_rw_iunlock(ip, XFS_IOLOCK_SHARED);
+out:
+ if (ret > 0)
+ XFS_STATS_ADD(ip->i_mount, xs_read_bytes, ret);
return ret;
}
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 8ee393996b7d..ae3758a90ed6 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -1143,7 +1143,6 @@ xfs_create(
xfs_bmap_free_t free_list;
xfs_fsblock_t first_block;
bool unlock_dp_on_error = false;
- int committed;
prid_t prid;
struct xfs_dquot *udqp = NULL;
struct xfs_dquot *gdqp = NULL;
@@ -1226,7 +1225,7 @@ xfs_create(
* pointing to itself.
*/
error = xfs_dir_ialloc(&tp, dp, mode, is_dir ? 2 : 1, rdev,
- prid, resblks > 0, &ip, &committed);
+ prid, resblks > 0, &ip, NULL);
if (error)
goto out_trans_cancel;
@@ -1275,7 +1274,7 @@ xfs_create(
*/
xfs_qm_vop_create_dqattach(tp, ip, udqp, gdqp, pdqp);
- error = xfs_bmap_finish(&tp, &free_list, &committed);
+ error = xfs_bmap_finish(&tp, &free_list, NULL);
if (error)
goto out_bmap_cancel;
@@ -1427,7 +1426,6 @@ xfs_link(
int error;
xfs_bmap_free_t free_list;
xfs_fsblock_t first_block;
- int committed;
int resblks;
trace_xfs_link(tdp, target_name);
@@ -1502,11 +1500,10 @@ xfs_link(
* link transaction goes to disk before returning to
* the user.
*/
- if (mp->m_flags & (XFS_MOUNT_WSYNC|XFS_MOUNT_DIRSYNC)) {
+ if (mp->m_flags & (XFS_MOUNT_WSYNC|XFS_MOUNT_DIRSYNC))
xfs_trans_set_sync(tp);
- }
- error = xfs_bmap_finish (&tp, &free_list, &committed);
+ error = xfs_bmap_finish(&tp, &free_list, NULL);
if (error) {
xfs_bmap_cancel(&free_list);
goto error_return;
@@ -1555,7 +1552,6 @@ xfs_itruncate_extents(
xfs_fileoff_t first_unmap_block;
xfs_fileoff_t last_block;
xfs_filblks_t unmap_len;
- int committed;
int error = 0;
int done = 0;
@@ -1601,9 +1597,7 @@ xfs_itruncate_extents(
* Duplicate the transaction that has the permanent
* reservation and commit the old transaction.
*/
- error = xfs_bmap_finish(&tp, &free_list, &committed);
- if (committed)
- xfs_trans_ijoin(tp, ip, 0);
+ error = xfs_bmap_finish(&tp, &free_list, ip);
if (error)
goto out_bmap_cancel;
@@ -1774,7 +1768,6 @@ xfs_inactive_ifree(
{
xfs_bmap_free_t free_list;
xfs_fsblock_t first_block;
- int committed;
struct xfs_mount *mp = ip->i_mount;
struct xfs_trans *tp;
int error;
@@ -1841,7 +1834,7 @@ xfs_inactive_ifree(
* Just ignore errors at this point. There is nothing we can do except
* to try to keep going. Make sure it's not a silent error.
*/
- error = xfs_bmap_finish(&tp, &free_list, &committed);
+ error = xfs_bmap_finish(&tp, &free_list, NULL);
if (error) {
xfs_notice(mp, "%s: xfs_bmap_finish returned error %d",
__func__, error);
@@ -2523,7 +2516,6 @@ xfs_remove(
int error = 0;
xfs_bmap_free_t free_list;
xfs_fsblock_t first_block;
- int committed;
uint resblks;
trace_xfs_remove(dp, name);
@@ -2624,7 +2616,7 @@ xfs_remove(
if (mp->m_flags & (XFS_MOUNT_WSYNC|XFS_MOUNT_DIRSYNC))
xfs_trans_set_sync(tp);
- error = xfs_bmap_finish(&tp, &free_list, &committed);
+ error = xfs_bmap_finish(&tp, &free_list, NULL);
if (error)
goto out_bmap_cancel;
@@ -2701,7 +2693,6 @@ xfs_finish_rename(
struct xfs_trans *tp,
struct xfs_bmap_free *free_list)
{
- int committed = 0;
int error;
/*
@@ -2711,7 +2702,7 @@ xfs_finish_rename(
if (tp->t_mountp->m_flags & (XFS_MOUNT_WSYNC|XFS_MOUNT_DIRSYNC))
xfs_trans_set_sync(tp);
- error = xfs_bmap_finish(&tp, free_list, &committed);
+ error = xfs_bmap_finish(&tp, free_list, NULL);
if (error) {
xfs_bmap_cancel(free_list);
xfs_trans_cancel(tp);
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index f4f5b43cf647..d81bdc080370 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -129,7 +129,6 @@ xfs_iomap_write_direct(
xfs_trans_t *tp;
xfs_bmap_free_t free_list;
uint qblocks, resblks, resrtextents;
- int committed;
int error;
int lockmode;
int bmapi_flags = XFS_BMAPI_PREALLOC;
@@ -203,15 +202,20 @@ xfs_iomap_write_direct(
* this outside the transaction context, but if we commit and then crash
* we may not have zeroed the blocks and this will be exposed on
* recovery of the allocation. Hence we must zero before commit.
+ *
* Further, if we are mapping unwritten extents here, we need to zero
* and convert them to written so that we don't need an unwritten extent
* callback for DAX. This also means that we need to be able to dip into
- * the reserve block pool if there is no space left but we need to do
- * unwritten extent conversion.
+ * the reserve block pool for bmbt block allocation if there is no space
+ * left but we need to do unwritten extent conversion.
*/
+
if (IS_DAX(VFS_I(ip))) {
bmapi_flags = XFS_BMAPI_CONVERT | XFS_BMAPI_ZERO;
- tp->t_flags |= XFS_TRANS_RESERVE;
+ if (ISUNWRITTEN(imap)) {
+ tp->t_flags |= XFS_TRANS_RESERVE;
+ resblks = XFS_DIOSTRAT_SPACE_RES(mp, 0) << 1;
+ }
}
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_write,
resblks, resrtextents);
@@ -247,7 +251,7 @@ xfs_iomap_write_direct(
/*
* Complete the transaction
*/
- error = xfs_bmap_finish(&tp, &free_list, &committed);
+ error = xfs_bmap_finish(&tp, &free_list, NULL);
if (error)
goto out_bmap_cancel;
@@ -693,7 +697,7 @@ xfs_iomap_write_allocate(
xfs_bmap_free_t free_list;
xfs_filblks_t count_fsb;
xfs_trans_t *tp;
- int nimaps, committed;
+ int nimaps;
int error = 0;
int nres;
@@ -794,7 +798,7 @@ xfs_iomap_write_allocate(
if (error)
goto trans_cancel;
- error = xfs_bmap_finish(&tp, &free_list, &committed);
+ error = xfs_bmap_finish(&tp, &free_list, NULL);
if (error)
goto trans_cancel;
@@ -852,7 +856,6 @@ xfs_iomap_write_unwritten(
xfs_bmap_free_t free_list;
xfs_fsize_t i_size;
uint resblks;
- int committed;
int error;
trace_xfs_unwritten_convert(ip, offset, count);
@@ -924,7 +927,7 @@ xfs_iomap_write_unwritten(
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
}
- error = xfs_bmap_finish(&tp, &free_list, &committed);
+ error = xfs_bmap_finish(&tp, &free_list, NULL);
if (error)
goto error_on_bmapi_transaction;
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index 245268a0cdf0..06eafafe636e 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -414,13 +414,17 @@ xfs_vn_rename(
* uio is kmalloced for this reason...
*/
STATIC const char *
-xfs_vn_follow_link(
+xfs_vn_get_link(
struct dentry *dentry,
- void **cookie)
+ struct inode *inode,
+ struct delayed_call *done)
{
char *link;
int error = -ENOMEM;
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
+
link = kmalloc(MAXPATHLEN+1, GFP_KERNEL);
if (!link)
goto out_err;
@@ -429,7 +433,8 @@ xfs_vn_follow_link(
if (unlikely(error))
goto out_kfree;
- return *cookie = link;
+ set_delayed_call(done, kfree_link, link);
+ return link;
out_kfree:
kfree(link);
@@ -1172,8 +1177,7 @@ static const struct inode_operations xfs_dir_ci_inode_operations = {
static const struct inode_operations xfs_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = xfs_vn_follow_link,
- .put_link = kfree_put_link,
+ .get_link = xfs_vn_get_link,
.getattr = xfs_vn_getattr,
.setattr = xfs_vn_setattr,
.setxattr = generic_setxattr,
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index f52c72a1a06f..9c9a1c9bcc7f 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -1188,10 +1188,16 @@ xlog_iodone(xfs_buf_t *bp)
int aborted = 0;
/*
- * Race to shutdown the filesystem if we see an error.
+ * Race to shutdown the filesystem if we see an error or the iclog is in
+ * IOABORT state. The IOABORT state is only set in DEBUG mode to inject
+ * CRC errors into log recovery.
*/
- if (XFS_TEST_ERROR(bp->b_error, l->l_mp,
- XFS_ERRTAG_IODONE_IOERR, XFS_RANDOM_IODONE_IOERR)) {
+ if (XFS_TEST_ERROR(bp->b_error, l->l_mp, XFS_ERRTAG_IODONE_IOERR,
+ XFS_RANDOM_IODONE_IOERR) ||
+ iclog->ic_state & XLOG_STATE_IOABORT) {
+ if (iclog->ic_state & XLOG_STATE_IOABORT)
+ iclog->ic_state &= ~XLOG_STATE_IOABORT;
+
xfs_buf_ioerror_alert(bp, __func__);
xfs_buf_stale(bp);
xfs_force_shutdown(l->l_mp, SHUTDOWN_LOG_IO_ERROR);
@@ -1838,6 +1844,23 @@ xlog_sync(
/* calculcate the checksum */
iclog->ic_header.h_crc = xlog_cksum(log, &iclog->ic_header,
iclog->ic_datap, size);
+#ifdef DEBUG
+ /*
+ * Intentionally corrupt the log record CRC based on the error injection
+ * frequency, if defined. This facilitates testing log recovery in the
+ * event of torn writes. Hence, set the IOABORT state to abort the log
+ * write on I/O completion and shutdown the fs. The subsequent mount
+ * detects the bad CRC and attempts to recover.
+ */
+ if (log->l_badcrc_factor &&
+ (prandom_u32() % log->l_badcrc_factor == 0)) {
+ iclog->ic_header.h_crc &= 0xAAAAAAAA;
+ iclog->ic_state |= XLOG_STATE_IOABORT;
+ xfs_warn(log->l_mp,
+ "Intentionally corrupted log record at LSN 0x%llx. Shutdown imminent.",
+ be64_to_cpu(iclog->ic_header.h_lsn));
+ }
+#endif
bp->b_io_length = BTOBB(count);
bp->b_fspriv = iclog;
@@ -2045,12 +2068,14 @@ xlog_print_tic_res(
"QM_DQCLUSTER",
"QM_QINOCREATE",
"QM_QUOTAOFF_END",
- "SB_UNIT",
"FSYNC_TS",
"GROWFSRT_ALLOC",
"GROWFSRT_ZERO",
"GROWFSRT_FREE",
- "SWAPEXT"
+ "SWAPEXT",
+ "CHECKPOINT",
+ "ICREATE",
+ "CREATE_TMPFILE"
};
xfs_warn(mp, "xlog_write: reservation summary:");
@@ -2791,11 +2816,19 @@ xlog_state_do_callback(
}
} while (!ioerrors && loopdidcallbacks);
+#ifdef DEBUG
/*
- * make one last gasp attempt to see if iclogs are being left in
- * limbo..
+ * Make one last gasp attempt to see if iclogs are being left in limbo.
+ * If the above loop finds an iclog earlier than the current iclog and
+ * in one of the syncing states, the current iclog is put into
+ * DO_CALLBACK and the callbacks are deferred to the completion of the
+ * earlier iclog. Walk the iclogs in order and make sure that no iclog
+ * is in DO_CALLBACK unless an earlier iclog is in one of the syncing
+ * states.
+ *
+ * Note that SYNCING|IOABORT is a valid state so we cannot just check
+ * for ic_state == SYNCING.
*/
-#ifdef DEBUG
if (funcdidcallbacks) {
first_iclog = iclog = log->l_iclog;
do {
@@ -2810,7 +2843,7 @@ xlog_state_do_callback(
* IOERROR - give up hope all ye who enter here
*/
if (iclog->ic_state == XLOG_STATE_WANT_SYNC ||
- iclog->ic_state == XLOG_STATE_SYNCING ||
+ iclog->ic_state & XLOG_STATE_SYNCING ||
iclog->ic_state == XLOG_STATE_DONE_SYNC ||
iclog->ic_state == XLOG_STATE_IOERROR )
break;
diff --git a/fs/xfs/xfs_log_priv.h b/fs/xfs/xfs_log_priv.h
index 8daba7491b13..ed8896310c00 100644
--- a/fs/xfs/xfs_log_priv.h
+++ b/fs/xfs/xfs_log_priv.h
@@ -62,6 +62,7 @@ static inline uint xlog_get_client_id(__be32 i)
#define XLOG_STATE_CALLBACK 0x0020 /* Callback functions now */
#define XLOG_STATE_DIRTY 0x0040 /* Dirty IC log, not ready for ACTIVE status*/
#define XLOG_STATE_IOERROR 0x0080 /* IO error happened in sync'ing log */
+#define XLOG_STATE_IOABORT 0x0100 /* force abort on I/O completion (debug) */
#define XLOG_STATE_ALL 0x7FFF /* All possible valid flags */
#define XLOG_STATE_NOTUSED 0x8000 /* This IC log not being used */
@@ -410,6 +411,8 @@ struct xlog {
/* The following field are used for debugging; need to hold icloglock */
#ifdef DEBUG
void *l_iclog_bak[XLOG_MAX_ICLOGS];
+ /* log record crc error injection factor */
+ uint32_t l_badcrc_factor;
#endif
};
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index c5ecaacdd218..da37beb76f6e 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -61,6 +61,9 @@ xlog_recover_check_summary(
#else
#define xlog_recover_check_summary(log)
#endif
+STATIC int
+xlog_do_recovery_pass(
+ struct xlog *, xfs_daddr_t, xfs_daddr_t, int, xfs_daddr_t *);
/*
* This structure is used during recovery to record the buf log items which
@@ -868,6 +871,351 @@ validate_head:
}
/*
+ * Seek backwards in the log for log record headers.
+ *
+ * Given a starting log block, walk backwards until we find the provided number
+ * of records or hit the provided tail block. The return value is the number of
+ * records encountered or a negative error code. The log block and buffer
+ * pointer of the last record seen are returned in rblk and rhead respectively.
+ */
+STATIC int
+xlog_rseek_logrec_hdr(
+ struct xlog *log,
+ xfs_daddr_t head_blk,
+ xfs_daddr_t tail_blk,
+ int count,
+ struct xfs_buf *bp,
+ xfs_daddr_t *rblk,
+ struct xlog_rec_header **rhead,
+ bool *wrapped)
+{
+ int i;
+ int error;
+ int found = 0;
+ char *offset = NULL;
+ xfs_daddr_t end_blk;
+
+ *wrapped = false;
+
+ /*
+ * Walk backwards from the head block until we hit the tail or the first
+ * block in the log.
+ */
+ end_blk = head_blk > tail_blk ? tail_blk : 0;
+ for (i = (int) head_blk - 1; i >= end_blk; i--) {
+ error = xlog_bread(log, i, 1, bp, &offset);
+ if (error)
+ goto out_error;
+
+ if (*(__be32 *) offset == cpu_to_be32(XLOG_HEADER_MAGIC_NUM)) {
+ *rblk = i;
+ *rhead = (struct xlog_rec_header *) offset;
+ if (++found == count)
+ break;
+ }
+ }
+
+ /*
+ * If we haven't hit the tail block or the log record header count,
+ * start looking again from the end of the physical log. Note that
+ * callers can pass head == tail if the tail is not yet known.
+ */
+ if (tail_blk >= head_blk && found != count) {
+ for (i = log->l_logBBsize - 1; i >= (int) tail_blk; i--) {
+ error = xlog_bread(log, i, 1, bp, &offset);
+ if (error)
+ goto out_error;
+
+ if (*(__be32 *)offset ==
+ cpu_to_be32(XLOG_HEADER_MAGIC_NUM)) {
+ *wrapped = true;
+ *rblk = i;
+ *rhead = (struct xlog_rec_header *) offset;
+ if (++found == count)
+ break;
+ }
+ }
+ }
+
+ return found;
+
+out_error:
+ return error;
+}
+
+/*
+ * Seek forward in the log for log record headers.
+ *
+ * Given head and tail blocks, walk forward from the tail block until we find
+ * the provided number of records or hit the head block. The return value is the
+ * number of records encountered or a negative error code. The log block and
+ * buffer pointer of the last record seen are returned in rblk and rhead
+ * respectively.
+ */
+STATIC int
+xlog_seek_logrec_hdr(
+ struct xlog *log,
+ xfs_daddr_t head_blk,
+ xfs_daddr_t tail_blk,
+ int count,
+ struct xfs_buf *bp,
+ xfs_daddr_t *rblk,
+ struct xlog_rec_header **rhead,
+ bool *wrapped)
+{
+ int i;
+ int error;
+ int found = 0;
+ char *offset = NULL;
+ xfs_daddr_t end_blk;
+
+ *wrapped = false;
+
+ /*
+ * Walk forward from the tail block until we hit the head or the last
+ * block in the log.
+ */
+ end_blk = head_blk > tail_blk ? head_blk : log->l_logBBsize - 1;
+ for (i = (int) tail_blk; i <= end_blk; i++) {
+ error = xlog_bread(log, i, 1, bp, &offset);
+ if (error)
+ goto out_error;
+
+ if (*(__be32 *) offset == cpu_to_be32(XLOG_HEADER_MAGIC_NUM)) {
+ *rblk = i;
+ *rhead = (struct xlog_rec_header *) offset;
+ if (++found == count)
+ break;
+ }
+ }
+
+ /*
+ * If we haven't hit the head block or the log record header count,
+ * start looking again from the start of the physical log.
+ */
+ if (tail_blk > head_blk && found != count) {
+ for (i = 0; i < (int) head_blk; i++) {
+ error = xlog_bread(log, i, 1, bp, &offset);
+ if (error)
+ goto out_error;
+
+ if (*(__be32 *)offset ==
+ cpu_to_be32(XLOG_HEADER_MAGIC_NUM)) {
+ *wrapped = true;
+ *rblk = i;
+ *rhead = (struct xlog_rec_header *) offset;
+ if (++found == count)
+ break;
+ }
+ }
+ }
+
+ return found;
+
+out_error:
+ return error;
+}
+
+/*
+ * Check the log tail for torn writes. This is required when torn writes are
+ * detected at the head and the head had to be walked back to a previous record.
+ * The tail of the previous record must now be verified to ensure the torn
+ * writes didn't corrupt the previous tail.
+ *
+ * Return an error if CRC verification fails as recovery cannot proceed.
+ */
+STATIC int
+xlog_verify_tail(
+ struct xlog *log,
+ xfs_daddr_t head_blk,
+ xfs_daddr_t tail_blk)
+{
+ struct xlog_rec_header *thead;
+ struct xfs_buf *bp;
+ xfs_daddr_t first_bad;
+ int count;
+ int error = 0;
+ bool wrapped;
+ xfs_daddr_t tmp_head;
+
+ bp = xlog_get_bp(log, 1);
+ if (!bp)
+ return -ENOMEM;
+
+ /*
+ * Seek XLOG_MAX_ICLOGS + 1 records past the current tail record to get
+ * a temporary head block that points after the last possible
+ * concurrently written record of the tail.
+ */
+ count = xlog_seek_logrec_hdr(log, head_blk, tail_blk,
+ XLOG_MAX_ICLOGS + 1, bp, &tmp_head, &thead,
+ &wrapped);
+ if (count < 0) {
+ error = count;
+ goto out;
+ }
+
+ /*
+ * If the call above didn't find XLOG_MAX_ICLOGS + 1 records, we ran
+ * into the actual log head. tmp_head points to the start of the record
+ * so update it to the actual head block.
+ */
+ if (count < XLOG_MAX_ICLOGS + 1)
+ tmp_head = head_blk;
+
+ /*
+ * We now have a tail and temporary head block that covers at least
+ * XLOG_MAX_ICLOGS records from the tail. We need to verify that these
+ * records were completely written. Run a CRC verification pass from
+ * tail to head and return the result.
+ */
+ error = xlog_do_recovery_pass(log, tmp_head, tail_blk,
+ XLOG_RECOVER_CRCPASS, &first_bad);
+
+out:
+ xlog_put_bp(bp);
+ return error;
+}
+
+/*
+ * Detect and trim torn writes from the head of the log.
+ *
+ * Storage without sector atomicity guarantees can result in torn writes in the
+ * log in the event of a crash. Our only means to detect this scenario is via
+ * CRC verification. While we can't always be certain that CRC verification
+ * failure is due to a torn write vs. an unrelated corruption, we do know that
+ * only a certain number (XLOG_MAX_ICLOGS) of log records can be written out at
+ * one time. Therefore, CRC verify up to XLOG_MAX_ICLOGS records at the head of
+ * the log and treat failures in this range as torn writes as a matter of
+ * policy. In the event of CRC failure, the head is walked back to the last good
+ * record in the log and the tail is updated from that record and verified.
+ */
+STATIC int
+xlog_verify_head(
+ struct xlog *log,
+ xfs_daddr_t *head_blk, /* in/out: unverified head */
+ xfs_daddr_t *tail_blk, /* out: tail block */
+ struct xfs_buf *bp,
+ xfs_daddr_t *rhead_blk, /* start blk of last record */
+ struct xlog_rec_header **rhead, /* ptr to last record */
+ bool *wrapped) /* last rec. wraps phys. log */
+{
+ struct xlog_rec_header *tmp_rhead;
+ struct xfs_buf *tmp_bp;
+ xfs_daddr_t first_bad;
+ xfs_daddr_t tmp_rhead_blk;
+ int found;
+ int error;
+ bool tmp_wrapped;
+
+ /*
+ * Search backwards through the log looking for the log record header
+ * block. This wraps all the way back around to the head so something is
+ * seriously wrong if we can't find it.
+ */
+ found = xlog_rseek_logrec_hdr(log, *head_blk, *head_blk, 1, bp, rhead_blk,
+ rhead, wrapped);
+ if (found < 0)
+ return found;
+ if (!found) {
+ xfs_warn(log->l_mp, "%s: couldn't find sync record", __func__);
+ return -EIO;
+ }
+
+ *tail_blk = BLOCK_LSN(be64_to_cpu((*rhead)->h_tail_lsn));
+
+ /*
+ * Now that we have a tail block, check the head of the log for torn
+ * writes. Search again until we hit the tail or the maximum number of
+ * log record I/Os that could have been in flight at one time. Use a
+ * temporary buffer so we don't trash the rhead/bp pointer from the
+ * call above.
+ */
+ tmp_bp = xlog_get_bp(log, 1);
+ if (!tmp_bp)
+ return -ENOMEM;
+ error = xlog_rseek_logrec_hdr(log, *head_blk, *tail_blk,
+ XLOG_MAX_ICLOGS, tmp_bp, &tmp_rhead_blk,
+ &tmp_rhead, &tmp_wrapped);
+ xlog_put_bp(tmp_bp);
+ if (error < 0)
+ return error;
+
+ /*
+ * Now run a CRC verification pass over the records starting at the
+ * block found above to the current head. If a CRC failure occurs, the
+ * log block of the first bad record is saved in first_bad.
+ */
+ error = xlog_do_recovery_pass(log, *head_blk, tmp_rhead_blk,
+ XLOG_RECOVER_CRCPASS, &first_bad);
+ if (error == -EFSBADCRC) {
+ /*
+ * We've hit a potential torn write. Reset the error and warn
+ * about it.
+ */
+ error = 0;
+ xfs_warn(log->l_mp,
+"Torn write (CRC failure) detected at log block 0x%llx. Truncating head block from 0x%llx.",
+ first_bad, *head_blk);
+
+ /*
+ * Get the header block and buffer pointer for the last good
+ * record before the bad record.
+ *
+ * Note that xlog_find_tail() clears the blocks at the new head
+ * (i.e., the records with invalid CRC) if the cycle number
+ * matches the the current cycle.
+ */
+ found = xlog_rseek_logrec_hdr(log, first_bad, *tail_blk, 1, bp,
+ rhead_blk, rhead, wrapped);
+ if (found < 0)
+ return found;
+ if (found == 0) /* XXX: right thing to do here? */
+ return -EIO;
+
+ /*
+ * Reset the head block to the starting block of the first bad
+ * log record and set the tail block based on the last good
+ * record.
+ *
+ * Bail out if the updated head/tail match as this indicates
+ * possible corruption outside of the acceptable
+ * (XLOG_MAX_ICLOGS) range. This is a job for xfs_repair...
+ */
+ *head_blk = first_bad;
+ *tail_blk = BLOCK_LSN(be64_to_cpu((*rhead)->h_tail_lsn));
+ if (*head_blk == *tail_blk) {
+ ASSERT(0);
+ return 0;
+ }
+
+ /*
+ * Now verify the tail based on the updated head. This is
+ * required because the torn writes trimmed from the head could
+ * have been written over the tail of a previous record. Return
+ * any errors since recovery cannot proceed if the tail is
+ * corrupt.
+ *
+ * XXX: This leaves a gap in truly robust protection from torn
+ * writes in the log. If the head is behind the tail, the tail
+ * pushes forward to create some space and then a crash occurs
+ * causing the writes into the previous record's tail region to
+ * tear, log recovery isn't able to recover.
+ *
+ * How likely is this to occur? If possible, can we do something
+ * more intelligent here? Is it safe to push the tail forward if
+ * we can determine that the tail is within the range of the
+ * torn write (e.g., the kernel can only overwrite the tail if
+ * it has actually been pushed forward)? Alternatively, could we
+ * somehow prevent this condition at runtime?
+ */
+ error = xlog_verify_tail(log, *head_blk, *tail_blk);
+ }
+
+ return error;
+}
+
+/*
* Find the sync block number or the tail of the log.
*
* This will be the block number of the last record to have its
@@ -893,13 +1241,13 @@ xlog_find_tail(
xlog_op_header_t *op_head;
char *offset = NULL;
xfs_buf_t *bp;
- int error, i, found;
+ int error;
xfs_daddr_t umount_data_blk;
xfs_daddr_t after_umount_blk;
+ xfs_daddr_t rhead_blk;
xfs_lsn_t tail_lsn;
int hblks;
-
- found = 0;
+ bool wrapped = false;
/*
* Find previous log record
@@ -923,48 +1271,16 @@ xlog_find_tail(
}
/*
- * Search backwards looking for log record header block
+ * Trim the head block back to skip over torn records. We can have
+ * multiple log I/Os in flight at any time, so we assume CRC failures
+ * back through the previous several records are torn writes and skip
+ * them.
*/
ASSERT(*head_blk < INT_MAX);
- for (i = (int)(*head_blk) - 1; i >= 0; i--) {
- error = xlog_bread(log, i, 1, bp, &offset);
- if (error)
- goto done;
-
- if (*(__be32 *)offset == cpu_to_be32(XLOG_HEADER_MAGIC_NUM)) {
- found = 1;
- break;
- }
- }
- /*
- * If we haven't found the log record header block, start looking
- * again from the end of the physical log. XXXmiken: There should be
- * a check here to make sure we didn't search more than N blocks in
- * the previous code.
- */
- if (!found) {
- for (i = log->l_logBBsize - 1; i >= (int)(*head_blk); i--) {
- error = xlog_bread(log, i, 1, bp, &offset);
- if (error)
- goto done;
-
- if (*(__be32 *)offset ==
- cpu_to_be32(XLOG_HEADER_MAGIC_NUM)) {
- found = 2;
- break;
- }
- }
- }
- if (!found) {
- xfs_warn(log->l_mp, "%s: couldn't find sync record", __func__);
- xlog_put_bp(bp);
- ASSERT(0);
- return -EIO;
- }
-
- /* find blk_no of tail of log */
- rhead = (xlog_rec_header_t *)offset;
- *tail_blk = BLOCK_LSN(be64_to_cpu(rhead->h_tail_lsn));
+ error = xlog_verify_head(log, head_blk, tail_blk, bp, &rhead_blk,
+ &rhead, &wrapped);
+ if (error)
+ goto done;
/*
* Reset log values according to the state of the log when we
@@ -976,10 +1292,10 @@ xlog_find_tail(
* written was complete and ended exactly on the end boundary
* of the physical log.
*/
- log->l_prev_block = i;
+ log->l_prev_block = rhead_blk;
log->l_curr_block = (int)*head_blk;
log->l_curr_cycle = be32_to_cpu(rhead->h_cycle);
- if (found == 2)
+ if (wrapped)
log->l_curr_cycle++;
atomic64_set(&log->l_tail_lsn, be64_to_cpu(rhead->h_tail_lsn));
atomic64_set(&log->l_last_sync_lsn, be64_to_cpu(rhead->h_lsn));
@@ -1014,12 +1330,13 @@ xlog_find_tail(
} else {
hblks = 1;
}
- after_umount_blk = (i + hblks + (int)
- BTOBB(be32_to_cpu(rhead->h_len))) % log->l_logBBsize;
+ after_umount_blk = rhead_blk + hblks + BTOBB(be32_to_cpu(rhead->h_len));
+ after_umount_blk = do_mod(after_umount_blk, log->l_logBBsize);
tail_lsn = atomic64_read(&log->l_tail_lsn);
if (*head_blk == after_umount_blk &&
be32_to_cpu(rhead->h_num_logops) == 1) {
- umount_data_blk = (i + hblks) % log->l_logBBsize;
+ umount_data_blk = rhead_blk + hblks;
+ umount_data_blk = do_mod(umount_data_blk, log->l_logBBsize);
error = xlog_bread(log, umount_data_blk, 1, bp, &offset);
if (error)
goto done;
@@ -3204,6 +3521,7 @@ xlog_recover_dquot_ra_pass2(
struct xfs_disk_dquot *recddq;
struct xfs_dq_logformat *dq_f;
uint type;
+ int len;
if (mp->m_qflags == 0)
@@ -3224,8 +3542,12 @@ xlog_recover_dquot_ra_pass2(
ASSERT(dq_f);
ASSERT(dq_f->qlf_len == 1);
- xfs_buf_readahead(mp->m_ddev_targp, dq_f->qlf_blkno,
- XFS_FSB_TO_BB(mp, dq_f->qlf_len), NULL);
+ len = XFS_FSB_TO_BB(mp, dq_f->qlf_len);
+ if (xlog_peek_buffer_cancelled(log, dq_f->qlf_blkno, len, 0))
+ return;
+
+ xfs_buf_readahead(mp->m_ddev_targp, dq_f->qlf_blkno, len,
+ &xfs_dquot_buf_ra_ops);
}
STATIC void
@@ -4118,26 +4440,69 @@ xlog_recover_process_iunlinks(
mp->m_dmevmask = mp_dmevmask;
}
+STATIC int
+xlog_unpack_data(
+ struct xlog_rec_header *rhead,
+ char *dp,
+ struct xlog *log)
+{
+ int i, j, k;
+
+ for (i = 0; i < BTOBB(be32_to_cpu(rhead->h_len)) &&
+ i < (XLOG_HEADER_CYCLE_SIZE / BBSIZE); i++) {
+ *(__be32 *)dp = *(__be32 *)&rhead->h_cycle_data[i];
+ dp += BBSIZE;
+ }
+
+ if (xfs_sb_version_haslogv2(&log->l_mp->m_sb)) {
+ xlog_in_core_2_t *xhdr = (xlog_in_core_2_t *)rhead;
+ for ( ; i < BTOBB(be32_to_cpu(rhead->h_len)); i++) {
+ j = i / (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
+ k = i % (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
+ *(__be32 *)dp = xhdr[j].hic_xheader.xh_cycle_data[k];
+ dp += BBSIZE;
+ }
+ }
+
+ return 0;
+}
+
/*
- * Upack the log buffer data and crc check it. If the check fails, issue a
- * warning if and only if the CRC in the header is non-zero. This makes the
- * check an advisory warning, and the zero CRC check will prevent failure
- * warnings from being emitted when upgrading the kernel from one that does not
- * add CRCs by default.
- *
- * When filesystems are CRC enabled, this CRC mismatch becomes a fatal log
- * corruption failure
+ * CRC check, unpack and process a log record.
*/
STATIC int
-xlog_unpack_data_crc(
+xlog_recover_process(
+ struct xlog *log,
+ struct hlist_head rhash[],
struct xlog_rec_header *rhead,
char *dp,
- struct xlog *log)
+ int pass)
{
+ int error;
__le32 crc;
crc = xlog_cksum(log, rhead, dp, be32_to_cpu(rhead->h_len));
- if (crc != rhead->h_crc) {
+
+ /*
+ * Nothing else to do if this is a CRC verification pass. Just return
+ * if this a record with a non-zero crc. Unfortunately, mkfs always
+ * sets h_crc to 0 so we must consider this valid even on v5 supers.
+ * Otherwise, return EFSBADCRC on failure so the callers up the stack
+ * know precisely what failed.
+ */
+ if (pass == XLOG_RECOVER_CRCPASS) {
+ if (rhead->h_crc && crc != le32_to_cpu(rhead->h_crc))
+ return -EFSBADCRC;
+ return 0;
+ }
+
+ /*
+ * We're in the normal recovery path. Issue a warning if and only if the
+ * CRC in the header is non-zero. This is an advisory warning and the
+ * zero CRC check prevents warnings from being emitted when upgrading
+ * the kernel from one that does not add CRCs by default.
+ */
+ if (crc != le32_to_cpu(rhead->h_crc)) {
if (rhead->h_crc || xfs_sb_version_hascrc(&log->l_mp->m_sb)) {
xfs_alert(log->l_mp,
"log record CRC mismatch: found 0x%x, expected 0x%x.",
@@ -4147,47 +4512,18 @@ xlog_unpack_data_crc(
}
/*
- * If we've detected a log record corruption, then we can't
- * recover past this point. Abort recovery if we are enforcing
- * CRC protection by punting an error back up the stack.
+ * If the filesystem is CRC enabled, this mismatch becomes a
+ * fatal log corruption failure.
*/
if (xfs_sb_version_hascrc(&log->l_mp->m_sb))
return -EFSCORRUPTED;
}
- return 0;
-}
-
-STATIC int
-xlog_unpack_data(
- struct xlog_rec_header *rhead,
- char *dp,
- struct xlog *log)
-{
- int i, j, k;
- int error;
-
- error = xlog_unpack_data_crc(rhead, dp, log);
+ error = xlog_unpack_data(rhead, dp, log);
if (error)
return error;
- for (i = 0; i < BTOBB(be32_to_cpu(rhead->h_len)) &&
- i < (XLOG_HEADER_CYCLE_SIZE / BBSIZE); i++) {
- *(__be32 *)dp = *(__be32 *)&rhead->h_cycle_data[i];
- dp += BBSIZE;
- }
-
- if (xfs_sb_version_haslogv2(&log->l_mp->m_sb)) {
- xlog_in_core_2_t *xhdr = (xlog_in_core_2_t *)rhead;
- for ( ; i < BTOBB(be32_to_cpu(rhead->h_len)); i++) {
- j = i / (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
- k = i % (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
- *(__be32 *)dp = xhdr[j].hic_xheader.xh_cycle_data[k];
- dp += BBSIZE;
- }
- }
-
- return 0;
+ return xlog_recover_process_data(log, rhash, rhead, dp, pass);
}
STATIC int
@@ -4239,18 +4575,21 @@ xlog_do_recovery_pass(
struct xlog *log,
xfs_daddr_t head_blk,
xfs_daddr_t tail_blk,
- int pass)
+ int pass,
+ xfs_daddr_t *first_bad) /* out: first bad log rec */
{
xlog_rec_header_t *rhead;
xfs_daddr_t blk_no;
+ xfs_daddr_t rhead_blk;
char *offset;
xfs_buf_t *hbp, *dbp;
- int error = 0, h_size;
+ int error = 0, h_size, h_len;
int bblks, split_bblks;
int hblks, split_hblks, wrapped_hblks;
struct hlist_head rhash[XLOG_RHASH_SIZE];
ASSERT(head_blk != tail_blk);
+ rhead_blk = 0;
/*
* Read the header of the tail block and get the iclog buffer size from
@@ -4274,7 +4613,31 @@ xlog_do_recovery_pass(
error = xlog_valid_rec_header(log, rhead, tail_blk);
if (error)
goto bread_err1;
+
+ /*
+ * xfsprogs has a bug where record length is based on lsunit but
+ * h_size (iclog size) is hardcoded to 32k. Now that we
+ * unconditionally CRC verify the unmount record, this means the
+ * log buffer can be too small for the record and cause an
+ * overrun.
+ *
+ * Detect this condition here. Use lsunit for the buffer size as
+ * long as this looks like the mkfs case. Otherwise, return an
+ * error to avoid a buffer overrun.
+ */
h_size = be32_to_cpu(rhead->h_size);
+ h_len = be32_to_cpu(rhead->h_len);
+ if (h_len > h_size) {
+ if (h_len <= log->l_mp->m_logbsize &&
+ be32_to_cpu(rhead->h_num_logops) == 1) {
+ xfs_warn(log->l_mp,
+ "invalid iclog size (%d bytes), using lsunit (%d bytes)",
+ h_size, log->l_mp->m_logbsize);
+ h_size = log->l_mp->m_logbsize;
+ } else
+ return -EFSCORRUPTED;
+ }
+
if ((be32_to_cpu(rhead->h_version) & XLOG_VERSION_2) &&
(h_size > XLOG_HEADER_CYCLE_SIZE)) {
hblks = h_size / XLOG_HEADER_CYCLE_SIZE;
@@ -4301,7 +4664,7 @@ xlog_do_recovery_pass(
}
memset(rhash, 0, sizeof(rhash));
- blk_no = tail_blk;
+ blk_no = rhead_blk = tail_blk;
if (tail_blk > head_blk) {
/*
* Perform recovery around the end of the physical log.
@@ -4408,19 +4771,18 @@ xlog_do_recovery_pass(
goto bread_err2;
}
- error = xlog_unpack_data(rhead, offset, log);
+ error = xlog_recover_process(log, rhash, rhead, offset,
+ pass);
if (error)
goto bread_err2;
- error = xlog_recover_process_data(log, rhash,
- rhead, offset, pass);
- if (error)
- goto bread_err2;
blk_no += bblks;
+ rhead_blk = blk_no;
}
ASSERT(blk_no >= log->l_logBBsize);
blk_no -= log->l_logBBsize;
+ rhead_blk = blk_no;
}
/* read first part of physical log */
@@ -4441,21 +4803,22 @@ xlog_do_recovery_pass(
if (error)
goto bread_err2;
- error = xlog_unpack_data(rhead, offset, log);
+ error = xlog_recover_process(log, rhash, rhead, offset, pass);
if (error)
goto bread_err2;
- error = xlog_recover_process_data(log, rhash,
- rhead, offset, pass);
- if (error)
- goto bread_err2;
blk_no += bblks + hblks;
+ rhead_blk = blk_no;
}
bread_err2:
xlog_put_bp(dbp);
bread_err1:
xlog_put_bp(hbp);
+
+ if (error && first_bad)
+ *first_bad = rhead_blk;
+
return error;
}
@@ -4493,7 +4856,7 @@ xlog_do_log_recovery(
INIT_LIST_HEAD(&log->l_buf_cancel_table[i]);
error = xlog_do_recovery_pass(log, head_blk, tail_blk,
- XLOG_RECOVER_PASS1);
+ XLOG_RECOVER_PASS1, NULL);
if (error != 0) {
kmem_free(log->l_buf_cancel_table);
log->l_buf_cancel_table = NULL;
@@ -4504,7 +4867,7 @@ xlog_do_log_recovery(
* When it is complete free the table of buf cancel items.
*/
error = xlog_do_recovery_pass(log, head_blk, tail_blk,
- XLOG_RECOVER_PASS2);
+ XLOG_RECOVER_PASS2, NULL);
#ifdef DEBUG
if (!error) {
int i;
diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c
index ab1bac6a3a1c..be02a68b2fe2 100644
--- a/fs/xfs/xfs_rtalloc.c
+++ b/fs/xfs/xfs_rtalloc.c
@@ -766,7 +766,6 @@ xfs_growfs_rt_alloc(
{
xfs_fileoff_t bno; /* block number in file */
struct xfs_buf *bp; /* temporary buffer for zeroing */
- int committed; /* transaction committed flag */
xfs_daddr_t d; /* disk block address */
int error; /* error return value */
xfs_fsblock_t firstblock;/* first block allocated in xaction */
@@ -811,7 +810,7 @@ xfs_growfs_rt_alloc(
/*
* Free any blocks freed up in the transaction, then commit.
*/
- error = xfs_bmap_finish(&tp, &flist, &committed);
+ error = xfs_bmap_finish(&tp, &flist, NULL);
if (error)
goto out_bmap_cancel;
error = xfs_trans_commit(tp);
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 36bd8825bfb0..59c9b7bd958d 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -137,7 +137,7 @@ static const match_table_t tokens = {
};
-STATIC unsigned long
+STATIC int
suffix_kstrtoint(char *s, unsigned int base, int *res)
{
int last, shift_left_factor = 0, _res;
@@ -1714,8 +1714,8 @@ xfs_init_zones(void)
xfs_inode_zone =
kmem_zone_init_flags(sizeof(xfs_inode_t), "xfs_inode",
- KM_ZONE_HWALIGN | KM_ZONE_RECLAIM | KM_ZONE_SPREAD,
- xfs_fs_inode_init_once);
+ KM_ZONE_HWALIGN | KM_ZONE_RECLAIM | KM_ZONE_SPREAD |
+ KM_ZONE_ACCOUNT, xfs_fs_inode_init_once);
if (!xfs_inode_zone)
goto out_destroy_efi_zone;
diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c
index 996481eeb491..b44284c1adda 100644
--- a/fs/xfs/xfs_symlink.c
+++ b/fs/xfs/xfs_symlink.c
@@ -178,7 +178,6 @@ xfs_symlink(
struct xfs_bmap_free free_list;
xfs_fsblock_t first_block;
bool unlock_dp_on_error = false;
- int committed;
xfs_fileoff_t first_fsb;
xfs_filblks_t fs_blocks;
int nmaps;
@@ -387,7 +386,7 @@ xfs_symlink(
xfs_trans_set_sync(tp);
}
- error = xfs_bmap_finish(&tp, &free_list, &committed);
+ error = xfs_bmap_finish(&tp, &free_list, NULL);
if (error)
goto out_bmap_cancel;
@@ -434,7 +433,6 @@ xfs_inactive_symlink_rmt(
struct xfs_inode *ip)
{
xfs_buf_t *bp;
- int committed;
int done;
int error;
xfs_fsblock_t first_block;
@@ -510,16 +508,10 @@ xfs_inactive_symlink_rmt(
/*
* Commit the first transaction. This logs the EFI and the inode.
*/
- error = xfs_bmap_finish(&tp, &free_list, &committed);
+ error = xfs_bmap_finish(&tp, &free_list, ip);
if (error)
goto error_bmap_cancel;
/*
- * The transaction must have been committed, since there were
- * actually extents freed by xfs_bunmapi. See xfs_bmap_finish.
- * The new tp has the extent freeing and EFDs.
- */
- ASSERT(committed);
- /*
* The first xact was committed, so add the inode to the new one.
* Mark it dirty so it will be logged and moved forward in the log as
* part of every commit.
diff --git a/fs/xfs/xfs_sysfs.c b/fs/xfs/xfs_sysfs.c
index ee70f5dec9dc..641d625eb334 100644
--- a/fs/xfs/xfs_sysfs.c
+++ b/fs/xfs/xfs_sysfs.c
@@ -255,11 +255,47 @@ write_grant_head_show(
}
XFS_SYSFS_ATTR_RO(write_grant_head);
+#ifdef DEBUG
+STATIC ssize_t
+log_badcrc_factor_store(
+ struct kobject *kobject,
+ const char *buf,
+ size_t count)
+{
+ struct xlog *log = to_xlog(kobject);
+ int ret;
+ uint32_t val;
+
+ ret = kstrtouint(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ log->l_badcrc_factor = val;
+
+ return count;
+}
+
+STATIC ssize_t
+log_badcrc_factor_show(
+ struct kobject *kobject,
+ char *buf)
+{
+ struct xlog *log = to_xlog(kobject);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", log->l_badcrc_factor);
+}
+
+XFS_SYSFS_ATTR_RW(log_badcrc_factor);
+#endif /* DEBUG */
+
static struct attribute *xfs_log_attrs[] = {
ATTR_LIST(log_head_lsn),
ATTR_LIST(log_tail_lsn),
ATTR_LIST(reserve_grant_head),
ATTR_LIST(write_grant_head),
+#ifdef DEBUG
+ ATTR_LIST(log_badcrc_factor),
+#endif
NULL,
};
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index 877079eb0f8f..391d797cb53f 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -1222,6 +1222,32 @@ DEFINE_PAGE_EVENT(xfs_writepage);
DEFINE_PAGE_EVENT(xfs_releasepage);
DEFINE_PAGE_EVENT(xfs_invalidatepage);
+DECLARE_EVENT_CLASS(xfs_readpage_class,
+ TP_PROTO(struct inode *inode, int nr_pages),
+ TP_ARGS(inode, nr_pages),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(xfs_ino_t, ino)
+ __field(int, nr_pages)
+ ),
+ TP_fast_assign(
+ __entry->dev = inode->i_sb->s_dev;
+ __entry->ino = inode->i_ino;
+ __entry->nr_pages = nr_pages;
+ ),
+ TP_printk("dev %d:%d ino 0x%llx nr_pages %d",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->ino,
+ __entry->nr_pages)
+)
+
+#define DEFINE_READPAGE_EVENT(name) \
+DEFINE_EVENT(xfs_readpage_class, name, \
+ TP_PROTO(struct inode *inode, int nr_pages), \
+ TP_ARGS(inode, nr_pages))
+DEFINE_READPAGE_EVENT(xfs_vm_readpage);
+DEFINE_READPAGE_EVENT(xfs_vm_readpages);
+
DECLARE_EVENT_CLASS(xfs_imap_class,
TP_PROTO(struct xfs_inode *ip, xfs_off_t offset, ssize_t count,
int type, struct xfs_bmbt_irec *irec),
diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c
index ce78534a047e..995170194df0 100644
--- a/fs/xfs/xfs_trans_dquot.c
+++ b/fs/xfs/xfs_trans_dquot.c
@@ -572,12 +572,16 @@ xfs_quota_warn(
struct xfs_dquot *dqp,
int type)
{
- /* no warnings for project quotas - we just return ENOSPC later */
+ enum quota_type qtype;
+
if (dqp->dq_flags & XFS_DQ_PROJ)
- return;
- quota_send_warning(make_kqid(&init_user_ns,
- (dqp->dq_flags & XFS_DQ_USER) ?
- USRQUOTA : GRPQUOTA,
+ qtype = PRJQUOTA;
+ else if (dqp->dq_flags & XFS_DQ_USER)
+ qtype = USRQUOTA;
+ else
+ qtype = GRPQUOTA;
+
+ quota_send_warning(make_kqid(&init_user_ns, qtype,
be32_to_cpu(dqp->q_core.d_id)),
mp->m_super->s_dev, type);
}
diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c
index 839b35ca21c6..110f1d7d86b0 100644
--- a/fs/xfs/xfs_xattr.c
+++ b/fs/xfs/xfs_xattr.c
@@ -39,9 +39,6 @@ xfs_xattr_get(const struct xattr_handler *handler, struct dentry *dentry,
struct xfs_inode *ip = XFS_I(d_inode(dentry));
int error, asize = size;
- if (strcmp(name, "") == 0)
- return -EINVAL;
-
/* Convert Linux syscall to XFS internal ATTR flags */
if (!size) {
xflags |= ATTR_KERNOVAL;
@@ -84,9 +81,6 @@ xfs_xattr_set(const struct xattr_handler *handler, struct dentry *dentry,
struct xfs_inode *ip = XFS_I(d_inode(dentry));
int error;
- if (strcmp(name, "") == 0)
- return -EINVAL;
-
/* Convert Linux syscall to XFS internal ATTR flags */
if (flags & XATTR_CREATE)
xflags |= ATTR_CREATE;
@@ -135,47 +129,19 @@ const struct xattr_handler *xfs_xattr_handlers[] = {
NULL
};
-static unsigned int xfs_xattr_prefix_len(int flags)
-{
- if (flags & XFS_ATTR_SECURE)
- return sizeof("security");
- else if (flags & XFS_ATTR_ROOT)
- return sizeof("trusted");
- else
- return sizeof("user");
-}
-
-static const char *xfs_xattr_prefix(int flags)
-{
- if (flags & XFS_ATTR_SECURE)
- return xfs_xattr_security_handler.prefix;
- else if (flags & XFS_ATTR_ROOT)
- return xfs_xattr_trusted_handler.prefix;
- else
- return xfs_xattr_user_handler.prefix;
-}
-
static int
-xfs_xattr_put_listent(
+__xfs_xattr_put_listent(
struct xfs_attr_list_context *context,
- int flags,
- unsigned char *name,
- int namelen,
- int valuelen,
- unsigned char *value)
+ char *prefix,
+ int prefix_len,
+ unsigned char *name,
+ int namelen)
{
- unsigned int prefix_len = xfs_xattr_prefix_len(flags);
char *offset;
int arraytop;
- ASSERT(context->count >= 0);
-
- /*
- * Only show root namespace entries if we are actually allowed to
- * see them.
- */
- if ((flags & XFS_ATTR_ROOT) && !capable(CAP_SYS_ADMIN))
- return 0;
+ if (!context->alist)
+ goto compute_size;
arraytop = context->count + prefix_len + namelen + 1;
if (arraytop > context->firstu) {
@@ -183,17 +149,19 @@ xfs_xattr_put_listent(
return 1;
}
offset = (char *)context->alist + context->count;
- strncpy(offset, xfs_xattr_prefix(flags), prefix_len);
+ strncpy(offset, prefix, prefix_len);
offset += prefix_len;
strncpy(offset, (char *)name, namelen); /* real name */
offset += namelen;
*offset = '\0';
+
+compute_size:
context->count += prefix_len + namelen + 1;
return 0;
}
static int
-xfs_xattr_put_listent_sizes(
+xfs_xattr_put_listent(
struct xfs_attr_list_context *context,
int flags,
unsigned char *name,
@@ -201,24 +169,55 @@ xfs_xattr_put_listent_sizes(
int valuelen,
unsigned char *value)
{
- context->count += xfs_xattr_prefix_len(flags) + namelen + 1;
- return 0;
-}
+ char *prefix;
+ int prefix_len;
-static int
-list_one_attr(const char *name, const size_t len, void *data,
- size_t size, ssize_t *result)
-{
- char *p = data + *result;
+ ASSERT(context->count >= 0);
- *result += len;
- if (!size)
- return 0;
- if (*result > size)
- return -ERANGE;
+ if (flags & XFS_ATTR_ROOT) {
+#ifdef CONFIG_XFS_POSIX_ACL
+ if (namelen == SGI_ACL_FILE_SIZE &&
+ strncmp(name, SGI_ACL_FILE,
+ SGI_ACL_FILE_SIZE) == 0) {
+ int ret = __xfs_xattr_put_listent(
+ context, XATTR_SYSTEM_PREFIX,
+ XATTR_SYSTEM_PREFIX_LEN,
+ XATTR_POSIX_ACL_ACCESS,
+ strlen(XATTR_POSIX_ACL_ACCESS));
+ if (ret)
+ return ret;
+ } else if (namelen == SGI_ACL_DEFAULT_SIZE &&
+ strncmp(name, SGI_ACL_DEFAULT,
+ SGI_ACL_DEFAULT_SIZE) == 0) {
+ int ret = __xfs_xattr_put_listent(
+ context, XATTR_SYSTEM_PREFIX,
+ XATTR_SYSTEM_PREFIX_LEN,
+ XATTR_POSIX_ACL_DEFAULT,
+ strlen(XATTR_POSIX_ACL_DEFAULT));
+ if (ret)
+ return ret;
+ }
+#endif
- strcpy(p, name);
- return 0;
+ /*
+ * Only show root namespace entries if we are actually allowed to
+ * see them.
+ */
+ if (!capable(CAP_SYS_ADMIN))
+ return 0;
+
+ prefix = XATTR_TRUSTED_PREFIX;
+ prefix_len = XATTR_TRUSTED_PREFIX_LEN;
+ } else if (flags & XFS_ATTR_SECURE) {
+ prefix = XATTR_SECURITY_PREFIX;
+ prefix_len = XATTR_SECURITY_PREFIX_LEN;
+ } else {
+ prefix = XATTR_USER_PREFIX;
+ prefix_len = XATTR_USER_PREFIX_LEN;
+ }
+
+ return __xfs_xattr_put_listent(context, prefix, prefix_len, name,
+ namelen);
}
ssize_t
@@ -227,7 +226,6 @@ xfs_vn_listxattr(struct dentry *dentry, char *data, size_t size)
struct xfs_attr_list_context context;
struct attrlist_cursor_kern cursor = { 0 };
struct inode *inode = d_inode(dentry);
- int error;
/*
* First read the regular on-disk attributes.
@@ -236,37 +234,14 @@ xfs_vn_listxattr(struct dentry *dentry, char *data, size_t size)
context.dp = XFS_I(inode);
context.cursor = &cursor;
context.resynch = 1;
- context.alist = data;
+ context.alist = size ? data : NULL;
context.bufsize = size;
context.firstu = context.bufsize;
-
- if (size)
- context.put_listent = xfs_xattr_put_listent;
- else
- context.put_listent = xfs_xattr_put_listent_sizes;
+ context.put_listent = xfs_xattr_put_listent;
xfs_attr_list_int(&context);
if (context.count < 0)
return -ERANGE;
- /*
- * Then add the two synthetic ACL attributes.
- */
- if (posix_acl_access_exists(inode)) {
- error = list_one_attr(POSIX_ACL_XATTR_ACCESS,
- strlen(POSIX_ACL_XATTR_ACCESS) + 1,
- data, size, &context.count);
- if (error)
- return error;
- }
-
- if (posix_acl_default_exists(inode)) {
- error = list_one_attr(POSIX_ACL_XATTR_DEFAULT,
- strlen(POSIX_ACL_XATTR_DEFAULT) + 1,
- data, size, &context.count);
- if (error)
- return error;
- }
-
return context.count;
}
diff --git a/include/acpi/acexcep.h b/include/acpi/acexcep.h
index 204f5819d464..cd84b12d1e60 100644
--- a/include/acpi/acexcep.h
+++ b/include/acpi/acexcep.h
@@ -126,8 +126,9 @@ struct acpi_exception_info {
#define AE_OWNER_ID_LIMIT EXCEP_ENV (0x001B)
#define AE_NOT_CONFIGURED EXCEP_ENV (0x001C)
#define AE_ACCESS EXCEP_ENV (0x001D)
+#define AE_IO_ERROR EXCEP_ENV (0x001E)
-#define AE_CODE_ENV_MAX 0x001D
+#define AE_CODE_ENV_MAX 0x001E
/*
* Programmer exceptions
@@ -263,7 +264,8 @@ static const struct acpi_exception_info acpi_gbl_exception_names_env[] = {
"There are no more Owner IDs available for ACPI tables or control methods"),
EXCEP_TXT("AE_NOT_CONFIGURED",
"The interface is not part of the current subsystem configuration"),
- EXCEP_TXT("AE_ACCESS", "Permission denied for the requested operation")
+ EXCEP_TXT("AE_ACCESS", "Permission denied for the requested operation"),
+ EXCEP_TXT("AE_IO_ERROR", "An I/O error occurred")
};
static const struct acpi_exception_info acpi_gbl_exception_names_pgm[] = {
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index ad0a5ff3d4cd..14362a84c78e 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -87,6 +87,8 @@ acpi_evaluate_dsm_typed(acpi_handle handle, const u8 *uuid, int rev, int func,
.package.elements = (eles) \
}
+bool acpi_dev_present(const char *hid);
+
#ifdef CONFIG_ACPI
#include <linux/proc_fs.h>
@@ -631,7 +633,9 @@ 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.valid;
+ return adev->power.states[ACPI_STATE_D3_COLD].flags.valid ||
+ ((acpi_gbl_FADT.header.revision < 6) &&
+ adev->power.states[ACPI_STATE_D3_HOT].flags.explicit_set);
}
#else /* CONFIG_ACPI */
diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h
index fbc2baf2b9dc..0d824a28522d 100644
--- a/include/acpi/acpiosxf.h
+++ b/include/acpi/acpiosxf.h
@@ -349,12 +349,28 @@ void acpi_os_redirect_output(void *destination);
#endif
/*
- * Debug input
+ * Debug IO
*/
#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_line
acpi_status acpi_os_get_line(char *buffer, u32 buffer_length, u32 *bytes_read);
#endif
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_initialize_command_signals
+acpi_status acpi_os_initialize_command_signals(void);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_terminate_command_signals
+void acpi_os_terminate_command_signals(void);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_wait_command_ready
+acpi_status acpi_os_wait_command_ready(void);
+#endif
+
+#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_notify_command_complete
+acpi_status acpi_os_notify_command_complete(void);
+#endif
+
/*
* Obtain ACPI table(s)
*/
diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h
index 3aaaa8630735..012b2eed7a93 100644
--- a/include/acpi/acpixf.h
+++ b/include/acpi/acpixf.h
@@ -46,7 +46,7 @@
/* Current ACPICA subsystem version in YYYYMMDD format */
-#define ACPI_CA_VERSION 0x20150930
+#define ACPI_CA_VERSION 0x20151218
#include <acpi/acconfig.h>
#include <acpi/actypes.h>
@@ -190,6 +190,11 @@ ACPI_INIT_GLOBAL(u8, acpi_gbl_copy_dsdt_locally, FALSE);
ACPI_INIT_GLOBAL(u8, acpi_gbl_do_not_use_xsdt, FALSE);
/*
+ * Optionally support group module level code.
+ */
+ACPI_INIT_GLOBAL(u8, acpi_gbl_group_module_level_code, TRUE);
+
+/*
* Optionally use 32-bit FADT addresses if and when there is a conflict
* (address mismatch) between the 32-bit and 64-bit versions of the
* address. Although ACPICA adheres to the ACPI specification which
@@ -263,6 +268,19 @@ ACPI_INIT_GLOBAL(u32, acpi_gbl_trace_dbg_layer, ACPI_TRACE_LAYER_DEFAULT);
ACPI_INIT_GLOBAL(u32, acpi_dbg_level, ACPI_DEBUG_DEFAULT);
ACPI_INIT_GLOBAL(u32, acpi_dbg_layer, 0);
+/* Optionally enable timer output with Debug Object output */
+
+ACPI_INIT_GLOBAL(u8, acpi_gbl_display_debug_timer, FALSE);
+
+/*
+ * Debugger command handshake globals. Host OSes need to access these
+ * variables to implement their own command handshake mechanism.
+ */
+#ifdef ACPI_DEBUGGER
+ACPI_INIT_GLOBAL(u8, acpi_gbl_method_executing, FALSE);
+ACPI_GLOBAL(char, acpi_gbl_db_line_buf[ACPI_DB_LINE_BUFFER_SIZE]);
+#endif
+
/*
* Other miscellaneous globals
*/
@@ -366,6 +384,29 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running);
#endif /* ACPI_APPLICATION */
+/*
+ * Debugger prototypes
+ *
+ * All interfaces used by debugger will be configured
+ * out of the ACPICA build unless the ACPI_DEBUGGER
+ * flag is defined.
+ */
+#ifdef ACPI_DEBUGGER
+#define ACPI_DBR_DEPENDENT_RETURN_OK(prototype) \
+ ACPI_EXTERNAL_RETURN_OK(prototype)
+
+#define ACPI_DBR_DEPENDENT_RETURN_VOID(prototype) \
+ ACPI_EXTERNAL_RETURN_VOID(prototype)
+
+#else
+#define ACPI_DBR_DEPENDENT_RETURN_OK(prototype) \
+ static ACPI_INLINE prototype {return(AE_OK);}
+
+#define ACPI_DBR_DEPENDENT_RETURN_VOID(prototype) \
+ static ACPI_INLINE prototype {return;}
+
+#endif /* ACPI_DEBUGGER */
+
/*****************************************************************************
*
* ACPICA public interface prototypes
@@ -822,17 +863,9 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status
ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_leave_sleep_state(u8 sleep_state))
ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
- acpi_set_firmware_waking_vectors
+ acpi_set_firmware_waking_vector
(acpi_physical_address physical_address,
acpi_physical_address physical_address64))
-ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
- acpi_set_firmware_waking_vector(u32
- physical_address))
-#if ACPI_MACHINE_WIDTH == 64
-ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
- acpi_set_firmware_waking_vector64(u64
- physical_address))
-#endif
/*
* ACPI Timer interfaces
*/
@@ -929,6 +962,8 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status
void **data,
void (*callback)(void *)))
+void acpi_run_debugger(char *batch_buffer);
+
void acpi_set_debugger_thread_id(acpi_thread_id thread_id);
#endif /* __ACXFACE_H__ */
diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h
index f914958c4adb..9633f606d89e 100644
--- a/include/acpi/actypes.h
+++ b/include/acpi/actypes.h
@@ -1148,7 +1148,7 @@ u32 (*acpi_interface_handler) (acpi_string interface_name, u32 supported);
#define ACPI_PCICLS_STRING_SIZE 7 /* Includes null terminator */
-/* Structures used for device/processor HID, UID, CID, and SUB */
+/* Structures used for device/processor HID, UID, CID */
struct acpi_pnp_device_id {
u32 length; /* Length of string + null */
@@ -1178,7 +1178,6 @@ struct acpi_device_info {
u64 address; /* _ADR value */
struct acpi_pnp_device_id hardware_id; /* _HID value */
struct acpi_pnp_device_id unique_id; /* _UID value */
- struct acpi_pnp_device_id subsystem_id; /* _SUB value */
struct acpi_pnp_device_id class_code; /* _CLS value */
struct acpi_pnp_device_id_list compatible_id_list; /* _CID list <must be last> */
};
@@ -1193,13 +1192,12 @@ struct acpi_device_info {
#define ACPI_VALID_ADR 0x0002
#define ACPI_VALID_HID 0x0004
#define ACPI_VALID_UID 0x0008
-#define ACPI_VALID_SUB 0x0010
#define ACPI_VALID_CID 0x0020
#define ACPI_VALID_CLS 0x0040
#define ACPI_VALID_SXDS 0x0100
#define ACPI_VALID_SXWS 0x0200
-/* Flags for _STA return value (current_status above) */
+/* Flags for _STA method */
#define ACPI_STA_DEVICE_PRESENT 0x01
#define ACPI_STA_DEVICE_ENABLED 0x02
diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h
index 323e5daece54..e21857d2ec05 100644
--- a/include/acpi/platform/aclinux.h
+++ b/include/acpi/platform/aclinux.h
@@ -150,6 +150,8 @@
*/
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_readable
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_writable
+#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_initialize_command_signals
+#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_terminate_command_signals
/*
* OSL interfaces used by utilities
diff --git a/include/acpi/platform/aclinuxex.h b/include/acpi/platform/aclinuxex.h
index fd6d70fe1219..f903fe64259a 100644
--- a/include/acpi/platform/aclinuxex.h
+++ b/include/acpi/platform/aclinuxex.h
@@ -129,6 +129,16 @@ static inline u8 acpi_os_readable(void *pointer, acpi_size length)
return TRUE;
}
+static inline acpi_status acpi_os_initialize_command_signals(void)
+{
+ return AE_OK;
+}
+
+static inline void acpi_os_terminate_command_signals(void)
+{
+ return;
+}
+
/*
* OSL interfaces added by Linux
*/
diff --git a/include/acpi/video.h b/include/acpi/video.h
index c62392d9b52a..f11d342b4567 100644
--- a/include/acpi/video.h
+++ b/include/acpi/video.h
@@ -2,6 +2,7 @@
#define __ACPI_VIDEO_H
#include <linux/errno.h> /* for ENODEV */
+#include <linux/types.h> /* for bool */
struct acpi_device;
@@ -31,6 +32,7 @@ extern int acpi_video_get_edid(struct acpi_device *device, int type,
int device_id, void **edid);
extern enum acpi_backlight_type acpi_video_get_backlight_type(void);
extern void acpi_video_set_dmi_backlight_type(enum acpi_backlight_type type);
+extern bool acpi_video_handles_brightness_key_presses(void);
#else
static inline int acpi_video_register(void) { return 0; }
static inline void acpi_video_unregister(void) { return; }
@@ -46,6 +48,10 @@ static inline enum acpi_backlight_type acpi_video_get_backlight_type(void)
static inline void acpi_video_set_dmi_backlight_type(enum acpi_backlight_type type)
{
}
+static inline bool acpi_video_handles_brightness_key_presses(void)
+{
+ return false;
+}
#endif
#endif
diff --git a/include/asm-generic/barrier.h b/include/asm-generic/barrier.h
index b42afada1280..0f45f93ef692 100644
--- a/include/asm-generic/barrier.h
+++ b/include/asm-generic/barrier.h
@@ -93,7 +93,7 @@
#endif /* CONFIG_SMP */
#ifndef smp_store_mb
-#define smp_store_mb(var, value) do { WRITE_ONCE(var, value); mb(); } while (0)
+#define smp_store_mb(var, value) do { WRITE_ONCE(var, value); smp_mb(); } while (0)
#endif
#ifndef smp_mb__before_atomic
diff --git a/include/asm-generic/memory_model.h b/include/asm-generic/memory_model.h
index 4b4b056a6eb0..5148150cc80b 100644
--- a/include/asm-generic/memory_model.h
+++ b/include/asm-generic/memory_model.h
@@ -1,6 +1,8 @@
#ifndef __ASM_MEMORY_MODEL_H
#define __ASM_MEMORY_MODEL_H
+#include <linux/pfn.h>
+
#ifndef __ASSEMBLY__
#if defined(CONFIG_FLATMEM)
@@ -72,7 +74,7 @@
/*
* Convert a physical address to a Page Frame Number and back
*/
-#define __phys_to_pfn(paddr) ((unsigned long)((paddr) >> PAGE_SHIFT))
+#define __phys_to_pfn(paddr) PHYS_PFN(paddr)
#define __pfn_to_phys(pfn) PFN_PHYS(pfn)
#define page_to_pfn __page_to_pfn
diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h
index 14b0ff32fb9f..3a6803cb0ec9 100644
--- a/include/asm-generic/pgtable.h
+++ b/include/asm-generic/pgtable.h
@@ -569,7 +569,7 @@ static inline int track_pfn_copy(struct vm_area_struct *vma)
}
/*
- * untrack_pfn_vma is called while unmapping a pfnmap for a region.
+ * untrack_pfn is called while unmapping a pfnmap for a region.
* untrack can be called for a specific region indicated by pfn and size or
* can be for the entire vma (in which case pfn, size are zero).
*/
@@ -577,6 +577,13 @@ static inline void untrack_pfn(struct vm_area_struct *vma,
unsigned long pfn, unsigned long size)
{
}
+
+/*
+ * untrack_pfn_moved is called while mremapping a pfnmap for a new region.
+ */
+static inline void untrack_pfn_moved(struct vm_area_struct *vma)
+{
+}
#else
extern int track_pfn_remap(struct vm_area_struct *vma, pgprot_t *prot,
unsigned long pfn, unsigned long addr,
@@ -586,6 +593,7 @@ extern int track_pfn_insert(struct vm_area_struct *vma, pgprot_t *prot,
extern int track_pfn_copy(struct vm_area_struct *vma);
extern void untrack_pfn(struct vm_area_struct *vma, unsigned long pfn,
unsigned long size);
+extern void untrack_pfn_moved(struct vm_area_struct *vma);
#endif
#ifdef __HAVE_COLOR_ZERO_PAGE
diff --git a/include/asm-generic/qspinlock.h b/include/asm-generic/qspinlock.h
index e2aadbc7151f..39e1cb201b8e 100644
--- a/include/asm-generic/qspinlock.h
+++ b/include/asm-generic/qspinlock.h
@@ -12,8 +12,9 @@
* GNU General Public License for more details.
*
* (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.
+ * (C) Copyright 2015 Hewlett-Packard Enterprise Development LP
*
- * Authors: Waiman Long <waiman.long@hp.com>
+ * Authors: Waiman Long <waiman.long@hpe.com>
*/
#ifndef __ASM_GENERIC_QSPINLOCK_H
#define __ASM_GENERIC_QSPINLOCK_H
@@ -62,7 +63,7 @@ static __always_inline int queued_spin_is_contended(struct qspinlock *lock)
static __always_inline int queued_spin_trylock(struct qspinlock *lock)
{
if (!atomic_read(&lock->val) &&
- (atomic_cmpxchg(&lock->val, 0, _Q_LOCKED_VAL) == 0))
+ (atomic_cmpxchg_acquire(&lock->val, 0, _Q_LOCKED_VAL) == 0))
return 1;
return 0;
}
@@ -77,7 +78,7 @@ static __always_inline void queued_spin_lock(struct qspinlock *lock)
{
u32 val;
- val = atomic_cmpxchg(&lock->val, 0, _Q_LOCKED_VAL);
+ val = atomic_cmpxchg_acquire(&lock->val, 0, _Q_LOCKED_VAL);
if (likely(val == 0))
return;
queued_spin_lock_slowpath(lock, val);
@@ -93,7 +94,7 @@ 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();
+ smp_mb__before_atomic();
atomic_sub(_Q_LOCKED_VAL, &lock->val);
}
#endif
diff --git a/include/asm-generic/tlb.h b/include/asm-generic/tlb.h
index db284bff29dc..9dbb739cafa0 100644
--- a/include/asm-generic/tlb.h
+++ b/include/asm-generic/tlb.h
@@ -5,7 +5,7 @@
* Copyright 2001 Red Hat, Inc.
* Based on code from mm/memory.c Copyright Linus Torvalds and others.
*
- * Copyright 2011 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright 2011 Red Hat, Inc., Peter Zijlstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h
index 9916d0e4eff5..25d0914481a2 100644
--- a/include/clocksource/arm_arch_timer.h
+++ b/include/clocksource/arm_arch_timer.h
@@ -23,6 +23,12 @@
#define ARCH_TIMER_CTRL_IT_MASK (1 << 1)
#define ARCH_TIMER_CTRL_IT_STAT (1 << 2)
+#define CNTHCTL_EL1PCTEN (1 << 0)
+#define CNTHCTL_EL1PCEN (1 << 1)
+#define CNTHCTL_EVNTEN (1 << 2)
+#define CNTHCTL_EVNTDIR (1 << 3)
+#define CNTHCTL_EVNTI (0xF << 4)
+
enum arch_timer_reg {
ARCH_TIMER_REG_CTRL,
ARCH_TIMER_REG_TVAL,
diff --git a/include/crypto/aead.h b/include/crypto/aead.h
index 077cae1e6b51..84d13b11ad7b 100644
--- a/include/crypto/aead.h
+++ b/include/crypto/aead.h
@@ -128,6 +128,7 @@ struct aead_request {
* @exit: Deinitialize the cryptographic transformation object. This is a
* counterpart to @init, used to remove various changes set in
* @init.
+ * @base: Definition of a generic crypto cipher algorithm.
*
* All fields except @ivsize is mandatory and must be filled.
*/
diff --git a/include/crypto/akcipher.h b/include/crypto/akcipher.h
index 45cd5b328040..354de15cea6b 100644
--- a/include/crypto/akcipher.h
+++ b/include/crypto/akcipher.h
@@ -21,9 +21,9 @@
* @src: Source data
* @dst: Destination data
* @src_len: Size of the input buffer
- * @dst_len: Size of the output buffer. It needs to be at leaset
+ * @dst_len: Size of the output buffer. It needs to be at least
* as big as the expected result depending on the operation
- * After operation it will be updated with the acctual size of the
+ * After operation it will be updated with the actual size of the
* result.
* In case of error where the dst sgl size was insufficient,
* it will be updated to the size required for the operation.
@@ -59,7 +59,7 @@ struct crypto_akcipher {
* algorithm. In case of error, where the dst_len was insufficient,
* the req->dst_len will be updated to the size required for the
* operation
- * @encrypt: Function performs an encrytp operation as defined by public key
+ * @encrypt: Function performs an encrypt 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
@@ -73,7 +73,7 @@ struct crypto_akcipher {
* @set_priv_key: Function invokes the algorithm specific set private key
* function, which knows how to decode and interpret
* the BER encoded private key
- * @max_size: Function returns dest buffer size reqired for a given key.
+ * @max_size: Function returns dest buffer size required for a given key.
* @init: Initialize the cryptographic transformation object.
* This function is used to initialize the cryptographic
* transformation object. This function is called only once at
@@ -232,7 +232,7 @@ static inline void akcipher_request_set_callback(struct akcipher_request *req,
}
/**
- * akcipher_request_set_crypt() -- Sets reqest parameters
+ * akcipher_request_set_crypt() -- Sets request parameters
*
* Sets parameters required by crypto operation
*
diff --git a/include/crypto/internal/akcipher.h b/include/crypto/internal/akcipher.h
index 9a2bda15e454..479a0078f0f7 100644
--- a/include/crypto/internal/akcipher.h
+++ b/include/crypto/internal/akcipher.h
@@ -13,6 +13,22 @@
#ifndef _CRYPTO_AKCIPHER_INT_H
#define _CRYPTO_AKCIPHER_INT_H
#include <crypto/akcipher.h>
+#include <crypto/algapi.h>
+
+struct akcipher_instance {
+ void (*free)(struct akcipher_instance *inst);
+ union {
+ struct {
+ char head[offsetof(struct akcipher_alg, base)];
+ struct crypto_instance base;
+ } s;
+ struct akcipher_alg alg;
+ };
+};
+
+struct crypto_akcipher_spawn {
+ struct crypto_spawn base;
+};
/*
* Transform internal helpers.
@@ -38,6 +54,56 @@ static inline const char *akcipher_alg_name(struct crypto_akcipher *tfm)
return crypto_akcipher_tfm(tfm)->__crt_alg->cra_name;
}
+static inline struct crypto_instance *akcipher_crypto_instance(
+ struct akcipher_instance *inst)
+{
+ return container_of(&inst->alg.base, struct crypto_instance, alg);
+}
+
+static inline struct akcipher_instance *akcipher_instance(
+ struct crypto_instance *inst)
+{
+ return container_of(&inst->alg, struct akcipher_instance, alg.base);
+}
+
+static inline struct akcipher_instance *akcipher_alg_instance(
+ struct crypto_akcipher *akcipher)
+{
+ return akcipher_instance(crypto_tfm_alg_instance(&akcipher->base));
+}
+
+static inline void *akcipher_instance_ctx(struct akcipher_instance *inst)
+{
+ return crypto_instance_ctx(akcipher_crypto_instance(inst));
+}
+
+static inline void crypto_set_akcipher_spawn(
+ struct crypto_akcipher_spawn *spawn,
+ struct crypto_instance *inst)
+{
+ crypto_set_spawn(&spawn->base, inst);
+}
+
+int crypto_grab_akcipher(struct crypto_akcipher_spawn *spawn, const char *name,
+ u32 type, u32 mask);
+
+static inline struct crypto_akcipher *crypto_spawn_akcipher(
+ struct crypto_akcipher_spawn *spawn)
+{
+ return crypto_spawn_tfm2(&spawn->base);
+}
+
+static inline void crypto_drop_akcipher(struct crypto_akcipher_spawn *spawn)
+{
+ crypto_drop_spawn(&spawn->base);
+}
+
+static inline struct akcipher_alg *crypto_spawn_akcipher_alg(
+ struct crypto_akcipher_spawn *spawn)
+{
+ return container_of(spawn->base.alg, struct akcipher_alg, base);
+}
+
/**
* crypto_register_akcipher() -- Register public key algorithm
*
@@ -57,4 +123,16 @@ int crypto_register_akcipher(struct akcipher_alg *alg);
* @alg: algorithm definition
*/
void crypto_unregister_akcipher(struct akcipher_alg *alg);
+
+/**
+ * akcipher_register_instance() -- Unregister public key template instance
+ *
+ * Function registers an implementation of an asymmetric key algorithm
+ * created from a template
+ *
+ * @tmpl: the template from which the algorithm was created
+ * @inst: the template instance
+ */
+int akcipher_register_instance(struct crypto_template *tmpl,
+ struct akcipher_instance *inst);
#endif
diff --git a/include/crypto/internal/rsa.h b/include/crypto/internal/rsa.h
index f997e2d29b5a..c7585bdecbc2 100644
--- a/include/crypto/internal/rsa.h
+++ b/include/crypto/internal/rsa.h
@@ -27,4 +27,6 @@ int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key,
unsigned int key_len);
void rsa_free_key(struct rsa_key *rsa_key);
+
+extern struct crypto_template rsa_pkcs1pad_tmpl;
#endif
diff --git a/include/crypto/md5.h b/include/crypto/md5.h
index 146af825eedb..327deac963c0 100644
--- a/include/crypto/md5.h
+++ b/include/crypto/md5.h
@@ -13,6 +13,8 @@
#define MD5_H2 0x98badcfeUL
#define MD5_H3 0x10325476UL
+extern const u8 md5_zero_message_hash[MD5_DIGEST_SIZE];
+
struct md5_state {
u32 hash[MD5_HASH_WORDS];
u32 block[MD5_BLOCK_WORDS];
diff --git a/include/crypto/sha.h b/include/crypto/sha.h
index dd7905a3c22e..c94d3eb1cefd 100644
--- a/include/crypto/sha.h
+++ b/include/crypto/sha.h
@@ -64,6 +64,12 @@
#define SHA512_H6 0x1f83d9abfb41bd6bULL
#define SHA512_H7 0x5be0cd19137e2179ULL
+extern const u8 sha1_zero_message_hash[SHA1_DIGEST_SIZE];
+
+extern const u8 sha224_zero_message_hash[SHA224_DIGEST_SIZE];
+
+extern const u8 sha256_zero_message_hash[SHA256_DIGEST_SIZE];
+
struct sha1_state {
u32 state[SHA1_DIGEST_SIZE / 4];
u64 count;
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 0b921ae06cd8..0a271ca1f7c7 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -309,6 +309,11 @@ struct drm_file {
unsigned universal_planes:1;
/* true if client understands atomic properties */
unsigned atomic:1;
+ /*
+ * This client is allowed to gain master privileges for @master.
+ * Protected by struct drm_device::master_mutex.
+ */
+ unsigned allowed_master:1;
struct pid *pid;
kuid_t uid;
@@ -910,6 +915,7 @@ extern int drm_open(struct inode *inode, struct file *filp);
extern ssize_t drm_read(struct file *filp, char __user *buffer,
size_t count, loff_t *offset);
extern int drm_release(struct inode *inode, struct file *filp);
+extern int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv);
/* Mapping support (drm_vm.h) */
extern unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait);
@@ -947,6 +953,10 @@ extern void drm_send_vblank_event(struct drm_device *dev, unsigned int pipe,
struct drm_pending_vblank_event *e);
extern void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
struct drm_pending_vblank_event *e);
+extern void drm_arm_vblank_event(struct drm_device *dev, unsigned int pipe,
+ struct drm_pending_vblank_event *e);
+extern void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
+ struct drm_pending_vblank_event *e);
extern bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe);
extern bool drm_crtc_handle_vblank(struct drm_crtc *crtc);
extern int drm_vblank_get(struct drm_device *dev, unsigned int pipe);
diff --git a/include/dt-bindings/leds/common.h b/include/dt-bindings/leds/common.h
index 79fcef72ef57..7958bec7de8c 100644
--- a/include/dt-bindings/leds/common.h
+++ b/include/dt-bindings/leds/common.h
@@ -6,7 +6,7 @@
* Author: Jacek Anaszewski <j.anaszewski@samsung.com>
*/
-#ifndef __DT_BINDINGS_LEDS_H__
+#ifndef __DT_BINDINGS_LEDS_H
#define __DT_BINDINGS_LEDS_H
/* External trigger type */
diff --git a/include/dt-bindings/mfd/palmas.h b/include/dt-bindings/mfd/palmas.h
index 2c8ac4841385..cdb075aae4e1 100644
--- a/include/dt-bindings/mfd/palmas.h
+++ b/include/dt-bindings/mfd/palmas.h
@@ -7,7 +7,7 @@
*
*/
-#ifndef __DT_BINDINGS_PALMAS_H__
+#ifndef __DT_BINDINGS_PALMAS_H
#define __DT_BINDINGS_PALMAS_H
/* External control pins */
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 9c747cb14ad8..13a3d537811b 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -279,6 +279,12 @@ struct vgic_v2_cpu_if {
u32 vgic_lr[VGIC_V2_MAX_LRS];
};
+/*
+ * LRs are stored in reverse order in memory. make sure we index them
+ * correctly.
+ */
+#define VGIC_V3_LR_INDEX(lr) (VGIC_V3_MAX_LRS - 1 - lr)
+
struct vgic_v3_cpu_if {
#ifdef CONFIG_KVM_ARM_VGIC_V3
u32 vgic_hcr;
@@ -342,10 +348,10 @@ int kvm_vgic_inject_mapped_irq(struct kvm *kvm, int cpuid,
struct irq_phys_map *map, bool level);
void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg);
int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu);
-int kvm_vgic_vcpu_active_irq(struct kvm_vcpu *vcpu);
struct irq_phys_map *kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu,
int virt_irq, int irq);
int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, struct irq_phys_map *map);
+bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, struct irq_phys_map *map);
#define irqchip_in_kernel(k) (!!((k)->arch.vgic.in_kernel))
#define vgic_initialized(k) (!!((k)->arch.vgic.nr_cpus))
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 054833939995..06ed7e54033e 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -37,6 +37,8 @@
#include <linux/list.h>
#include <linux/mod_devicetable.h>
#include <linux/dynamic_debug.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
@@ -119,6 +121,75 @@ typedef int (*acpi_tbl_table_handler)(struct acpi_table_header *table);
typedef int (*acpi_tbl_entry_handler)(struct acpi_subtable_header *header,
const unsigned long end);
+/* Debugger support */
+
+struct acpi_debugger_ops {
+ int (*create_thread)(acpi_osd_exec_callback function, void *context);
+ ssize_t (*write_log)(const char *msg);
+ ssize_t (*read_cmd)(char *buffer, size_t length);
+ int (*wait_command_ready)(bool single_step, char *buffer, size_t length);
+ int (*notify_command_complete)(void);
+};
+
+struct acpi_debugger {
+ const struct acpi_debugger_ops *ops;
+ struct module *owner;
+ struct mutex lock;
+};
+
+#ifdef CONFIG_ACPI_DEBUGGER
+int __init acpi_debugger_init(void);
+int acpi_register_debugger(struct module *owner,
+ const struct acpi_debugger_ops *ops);
+void acpi_unregister_debugger(const struct acpi_debugger_ops *ops);
+int acpi_debugger_create_thread(acpi_osd_exec_callback function, void *context);
+ssize_t acpi_debugger_write_log(const char *msg);
+ssize_t acpi_debugger_read_cmd(char *buffer, size_t buffer_length);
+int acpi_debugger_wait_command_ready(void);
+int acpi_debugger_notify_command_complete(void);
+#else
+static inline int acpi_debugger_init(void)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_register_debugger(struct module *owner,
+ const struct acpi_debugger_ops *ops)
+{
+ return -ENODEV;
+}
+
+static inline void acpi_unregister_debugger(const struct acpi_debugger_ops *ops)
+{
+}
+
+static inline int acpi_debugger_create_thread(acpi_osd_exec_callback function,
+ void *context)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_debugger_write_log(const char *msg)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_debugger_read_cmd(char *buffer, u32 buffer_length)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_debugger_wait_command_ready(void)
+{
+ return -ENODEV;
+}
+
+static inline int acpi_debugger_notify_command_complete(void)
+{
+ return -ENODEV;
+}
+#endif
+
#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
void acpi_initrd_override(void *data, size_t size);
#else
@@ -318,6 +389,7 @@ bool acpi_dev_resource_address_space(struct acpi_resource *ares,
bool acpi_dev_resource_ext_address_space(struct acpi_resource *ares,
struct resource_win *win);
unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable);
+unsigned int acpi_dev_get_irq_type(int triggering, int polarity);
bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index,
struct resource *res);
@@ -870,8 +942,8 @@ static inline int acpi_dev_get_property(struct acpi_device *adev,
}
static inline int acpi_node_get_property_reference(struct fwnode_handle *fwnode,
- const char *name, const char *cells_name,
- size_t index, struct acpi_reference_args *args)
+ const char *name, size_t index,
+ struct acpi_reference_args *args)
{
return -ENXIO;
}
diff --git a/include/linux/amba/serial.h b/include/linux/amba/serial.h
index 0ddb5c02ad8b..d76a19ba2cff 100644
--- a/include/linux/amba/serial.h
+++ b/include/linux/amba/serial.h
@@ -65,6 +65,24 @@
#define ST_UART011_ABCR 0x100 /* Autobaud control register. */
#define ST_UART011_ABIMSC 0x15C /* Autobaud interrupt mask/clear register. */
+/*
+ * ZTE UART register offsets. This UART has a radically different address
+ * allocation from the ARM and ST variants, so we list all registers here.
+ * We assume unlisted registers do not exist.
+ */
+#define ZX_UART011_DR 0x04
+#define ZX_UART011_FR 0x14
+#define ZX_UART011_IBRD 0x24
+#define ZX_UART011_FBRD 0x28
+#define ZX_UART011_LCRH 0x30
+#define ZX_UART011_CR 0x34
+#define ZX_UART011_IFLS 0x38
+#define ZX_UART011_IMSC 0x40
+#define ZX_UART011_RIS 0x44
+#define ZX_UART011_MIS 0x48
+#define ZX_UART011_ICR 0x4c
+#define ZX_UART011_DMACR 0x50
+
#define UART011_DR_OE (1 << 11)
#define UART011_DR_BE (1 << 10)
#define UART011_DR_PE (1 << 9)
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
new file mode 100644
index 000000000000..b5abfda80465
--- /dev/null
+++ b/include/linux/arm-smccc.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ *
+ * 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 __LINUX_ARM_SMCCC_H
+#define __LINUX_ARM_SMCCC_H
+
+#include <linux/linkage.h>
+#include <linux/types.h>
+
+/*
+ * This file provides common defines for ARM SMC Calling Convention as
+ * specified in
+ * http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html
+ */
+
+#define ARM_SMCCC_STD_CALL 0
+#define ARM_SMCCC_FAST_CALL 1
+#define ARM_SMCCC_TYPE_SHIFT 31
+
+#define ARM_SMCCC_SMC_32 0
+#define ARM_SMCCC_SMC_64 1
+#define ARM_SMCCC_CALL_CONV_SHIFT 30
+
+#define ARM_SMCCC_OWNER_MASK 0x3F
+#define ARM_SMCCC_OWNER_SHIFT 24
+
+#define ARM_SMCCC_FUNC_MASK 0xFFFF
+
+#define ARM_SMCCC_IS_FAST_CALL(smc_val) \
+ ((smc_val) & (ARM_SMCCC_FAST_CALL << ARM_SMCCC_TYPE_SHIFT))
+#define ARM_SMCCC_IS_64(smc_val) \
+ ((smc_val) & (ARM_SMCCC_SMC_64 << ARM_SMCCC_CALL_CONV_SHIFT))
+#define ARM_SMCCC_FUNC_NUM(smc_val) ((smc_val) & ARM_SMCCC_FUNC_MASK)
+#define ARM_SMCCC_OWNER_NUM(smc_val) \
+ (((smc_val) >> ARM_SMCCC_OWNER_SHIFT) & ARM_SMCCC_OWNER_MASK)
+
+#define ARM_SMCCC_CALL_VAL(type, calling_convention, owner, func_num) \
+ (((type) << ARM_SMCCC_TYPE_SHIFT) | \
+ ((calling_convention) << ARM_SMCCC_CALL_CONV_SHIFT) | \
+ (((owner) & ARM_SMCCC_OWNER_MASK) << ARM_SMCCC_OWNER_SHIFT) | \
+ ((func_num) & ARM_SMCCC_FUNC_MASK))
+
+#define ARM_SMCCC_OWNER_ARCH 0
+#define ARM_SMCCC_OWNER_CPU 1
+#define ARM_SMCCC_OWNER_SIP 2
+#define ARM_SMCCC_OWNER_OEM 3
+#define ARM_SMCCC_OWNER_STANDARD 4
+#define ARM_SMCCC_OWNER_TRUSTED_APP 48
+#define ARM_SMCCC_OWNER_TRUSTED_APP_END 49
+#define ARM_SMCCC_OWNER_TRUSTED_OS 50
+#define ARM_SMCCC_OWNER_TRUSTED_OS_END 63
+
+/**
+ * struct arm_smccc_res - Result from SMC/HVC call
+ * @a0-a3 result values from registers 0 to 3
+ */
+struct arm_smccc_res {
+ unsigned long a0;
+ unsigned long a1;
+ unsigned long a2;
+ unsigned long a3;
+};
+
+/**
+ * arm_smccc_smc() - make SMC calls
+ * @a0-a7: arguments passed in registers 0 to 7
+ * @res: result values from registers 0 to 3
+ *
+ * This function is used to make SMC calls following SMC Calling Convention.
+ * The content of the supplied param are copied to registers 0 to 7 prior
+ * to the SMC instruction. The return values are updated with the content
+ * from register 0 to 3 on return from the SMC instruction.
+ */
+asmlinkage void arm_smccc_smc(unsigned long a0, unsigned long a1,
+ unsigned long a2, unsigned long a3, unsigned long a4,
+ unsigned long a5, unsigned long a6, unsigned long a7,
+ struct arm_smccc_res *res);
+
+/**
+ * arm_smccc_hvc() - make HVC calls
+ * @a0-a7: arguments passed in registers 0 to 7
+ * @res: result values from registers 0 to 3
+ *
+ * This function is used to make HVC calls following SMC Calling
+ * Convention. The content of the supplied param are copied to registers 0
+ * to 7 prior to the HVC instruction. The return values are updated with
+ * the content from register 0 to 3 on return from the HVC instruction.
+ */
+asmlinkage void arm_smccc_hvc(unsigned long a0, unsigned long a1,
+ unsigned long a2, unsigned long a3, unsigned long a4,
+ unsigned long a5, unsigned long a6, unsigned long a7,
+ struct arm_smccc_res *res);
+
+#endif /*__LINUX_ARM_SMCCC_H*/
diff --git a/include/linux/badblocks.h b/include/linux/badblocks.h
new file mode 100644
index 000000000000..c3bdf8c59480
--- /dev/null
+++ b/include/linux/badblocks.h
@@ -0,0 +1,65 @@
+#ifndef _LINUX_BADBLOCKS_H
+#define _LINUX_BADBLOCKS_H
+
+#include <linux/seqlock.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/types.h>
+
+#define BB_LEN_MASK (0x00000000000001FFULL)
+#define BB_OFFSET_MASK (0x7FFFFFFFFFFFFE00ULL)
+#define BB_ACK_MASK (0x8000000000000000ULL)
+#define BB_MAX_LEN 512
+#define BB_OFFSET(x) (((x) & BB_OFFSET_MASK) >> 9)
+#define BB_LEN(x) (((x) & BB_LEN_MASK) + 1)
+#define BB_ACK(x) (!!((x) & BB_ACK_MASK))
+#define BB_MAKE(a, l, ack) (((a)<<9) | ((l)-1) | ((u64)(!!(ack)) << 63))
+
+/* Bad block numbers are stored sorted in a single page.
+ * 64bits is used for each block or extent.
+ * 54 bits are sector number, 9 bits are extent size,
+ * 1 bit is an 'acknowledged' flag.
+ */
+#define MAX_BADBLOCKS (PAGE_SIZE/8)
+
+struct badblocks {
+ struct device *dev; /* set by devm_init_badblocks */
+ int count; /* count of bad blocks */
+ int unacked_exist; /* there probably are unacknowledged
+ * bad blocks. This is only cleared
+ * when a read discovers none
+ */
+ int shift; /* shift from sectors to block size
+ * a -ve shift means badblocks are
+ * disabled.*/
+ u64 *page; /* badblock list */
+ int changed;
+ seqlock_t lock;
+ sector_t sector;
+ sector_t size; /* in sectors */
+};
+
+int badblocks_check(struct badblocks *bb, sector_t s, int sectors,
+ sector_t *first_bad, int *bad_sectors);
+int badblocks_set(struct badblocks *bb, sector_t s, int sectors,
+ int acknowledged);
+int badblocks_clear(struct badblocks *bb, sector_t s, int sectors);
+void ack_all_badblocks(struct badblocks *bb);
+ssize_t badblocks_show(struct badblocks *bb, char *page, int unack);
+ssize_t badblocks_store(struct badblocks *bb, const char *page, size_t len,
+ int unack);
+int badblocks_init(struct badblocks *bb, int enable);
+void badblocks_exit(struct badblocks *bb);
+struct device;
+int devm_init_badblocks(struct device *dev, struct badblocks *bb);
+static inline void devm_exit_badblocks(struct device *dev, struct badblocks *bb)
+{
+ if (bb->dev != dev) {
+ dev_WARN_ONCE(dev, 1, "%s: badblocks instance not associated\n",
+ __func__);
+ return;
+ }
+ badblocks_exit(bb);
+}
+#endif
diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h
index cf038431a5cc..db51a6ffb7d6 100644
--- a/include/linux/bcma/bcma_driver_chipcommon.h
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -579,6 +579,8 @@ struct bcma_pflash {
};
#ifdef CONFIG_BCMA_SFLASH
+struct mtd_info;
+
struct bcma_sflash {
bool present;
u32 window;
@@ -592,13 +594,9 @@ struct bcma_sflash {
#endif
#ifdef CONFIG_BCMA_NFLASH
-struct mtd_info;
-
struct bcma_nflash {
bool present;
bool boot; /* This is the flash the SoC boots from */
-
- struct mtd_info *mtd;
};
#endif
diff --git a/include/linux/bitops.h b/include/linux/bitops.h
index 2b8ed123ad36..defeaac0745f 100644
--- a/include/linux/bitops.h
+++ b/include/linux/bitops.h
@@ -107,7 +107,7 @@ static inline __u64 ror64(__u64 word, unsigned int shift)
*/
static inline __u32 rol32(__u32 word, unsigned int shift)
{
- return (word << shift) | (word >> (32 - shift));
+ return (word << shift) | (word >> ((-shift) & 31));
}
/**
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index c0d2b7927c1f..c70e3588a48c 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -254,6 +254,7 @@ struct queue_limits {
unsigned long virt_boundary_mask;
unsigned int max_hw_sectors;
+ unsigned int max_dev_sectors;
unsigned int chunk_sectors;
unsigned int max_sectors;
unsigned int max_segment_size;
@@ -773,7 +774,6 @@ extern void blk_rq_set_block_pc(struct request *);
extern void blk_requeue_request(struct request_queue *, struct request *);
extern void blk_add_request_payload(struct request *rq, struct page *page,
unsigned int len);
-extern int blk_rq_check_limits(struct request_queue *q, struct request *rq);
extern int blk_lld_busy(struct request_queue *q);
extern int blk_rq_prep_clone(struct request *rq, struct request *rq_src,
struct bio_set *bs, gfp_t gfp_mask,
@@ -797,6 +797,7 @@ extern int sg_scsi_ioctl(struct request_queue *, struct gendisk *, fmode_t,
extern int blk_queue_enter(struct request_queue *q, gfp_t gfp);
extern void blk_queue_exit(struct request_queue *q);
extern void blk_start_queue(struct request_queue *q);
+extern void blk_start_queue_async(struct request_queue *q);
extern void blk_stop_queue(struct request_queue *q);
extern void blk_sync_queue(struct request_queue *q);
extern void __blk_stop_queue(struct request_queue *q);
@@ -960,7 +961,6 @@ extern struct request_queue *blk_init_allocated_queue(struct request_queue *,
extern void blk_cleanup_queue(struct request_queue *);
extern void blk_queue_make_request(struct request_queue *, make_request_fn *);
extern void blk_queue_bounce_limit(struct request_queue *, u64);
-extern void blk_limits_max_hw_sectors(struct queue_limits *, unsigned int);
extern void blk_queue_max_hw_sectors(struct request_queue *, unsigned int);
extern void blk_queue_chunk_sectors(struct request_queue *, unsigned int);
extern void blk_queue_max_segments(struct request_queue *, unsigned short);
diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h
index f589222bfa87..35b22f94d2d2 100644
--- a/include/linux/bootmem.h
+++ b/include/linux/bootmem.h
@@ -19,6 +19,10 @@ extern unsigned long min_low_pfn;
* highest page
*/
extern unsigned long max_pfn;
+/*
+ * highest possible page
+ */
+extern unsigned long long max_possible_pfn;
#ifndef CONFIG_NO_BOOTMEM
/*
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index de464e6683b6..83d1926c61e4 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -40,6 +40,7 @@ struct bpf_map {
struct user_struct *user;
const struct bpf_map_ops *ops;
struct work_struct work;
+ atomic_t usercnt;
};
struct bpf_map_type_list {
@@ -167,8 +168,10 @@ struct bpf_prog *bpf_prog_get(u32 ufd);
void bpf_prog_put(struct bpf_prog *prog);
void bpf_prog_put_rcu(struct bpf_prog *prog);
-struct bpf_map *bpf_map_get(u32 ufd);
+struct bpf_map *bpf_map_get_with_uref(u32 ufd);
struct bpf_map *__bpf_map_get(struct fd f);
+void bpf_map_inc(struct bpf_map *map, bool uref);
+void bpf_map_put_with_uref(struct bpf_map *map);
void bpf_map_put(struct bpf_map *map);
extern int sysctl_unprivileged_bpf_disabled;
diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h
index 59f4a7304419..f0ba9c2ec639 100644
--- a/include/linux/brcmphy.h
+++ b/include/linux/brcmphy.h
@@ -26,6 +26,7 @@
#define PHY_ID_BCM7366 0x600d8490
#define PHY_ID_BCM7425 0x600d86b0
#define PHY_ID_BCM7429 0x600d8730
+#define PHY_ID_BCM7435 0x600d8750
#define PHY_ID_BCM7439 0x600d8480
#define PHY_ID_BCM7439_2 0xae025080
#define PHY_ID_BCM7445 0x600d8510
diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h
index 60d44b26276d..7f540f7f588d 100644
--- a/include/linux/cgroup-defs.h
+++ b/include/linux/cgroup-defs.h
@@ -34,17 +34,12 @@ struct seq_file;
/* define the enumeration of all cgroup subsystems */
#define SUBSYS(_x) _x ## _cgrp_id,
-#define SUBSYS_TAG(_t) CGROUP_ ## _t, \
- __unused_tag_ ## _t = CGROUP_ ## _t - 1,
enum cgroup_subsys_id {
#include <linux/cgroup_subsys.h>
CGROUP_SUBSYS_COUNT,
};
-#undef SUBSYS_TAG
#undef SUBSYS
-#define CGROUP_CANFORK_COUNT (CGROUP_CANFORK_END - CGROUP_CANFORK_START)
-
/* bits in struct cgroup_subsys_state flags field */
enum {
CSS_NO_REF = (1 << 0), /* no reference counting for this css */
@@ -66,7 +61,6 @@ enum {
/* cgroup_root->flags */
enum {
- CGRP_ROOT_SANE_BEHAVIOR = (1 << 0), /* __DEVEL__sane_behavior specified */
CGRP_ROOT_NOPREFIX = (1 << 1), /* mounted subsystems have no named prefix */
CGRP_ROOT_XATTR = (1 << 2), /* supports extended attributes */
};
@@ -90,7 +84,6 @@ enum {
*/
struct cgroup_file {
/* do not access any fields from outside cgroup core */
- struct list_head node; /* anchored at css->files */
struct kernfs_node *kn;
};
@@ -134,9 +127,6 @@ struct cgroup_subsys_state {
*/
u64 serial_nr;
- /* all cgroup_files associated with this css */
- struct list_head files;
-
/* percpu_ref killing and RCU release */
struct rcu_head rcu_head;
struct work_struct destroy_work;
@@ -235,6 +225,14 @@ struct cgroup {
int id;
/*
+ * The depth this cgroup is at. The root is at depth zero and each
+ * step down the hierarchy increments the level. This along with
+ * ancestor_ids[] can determine whether a given cgroup is a
+ * descendant of another without traversing the hierarchy.
+ */
+ int level;
+
+ /*
* Each non-empty css_set associated with this cgroup contributes
* one to populated_cnt. All children with non-zero popuplated_cnt
* of their own contribute one. The count is zero iff there's no
@@ -289,6 +287,9 @@ struct cgroup {
/* used to schedule release agent */
struct work_struct release_agent_work;
+
+ /* ids of the ancestors at each level including self */
+ int ancestor_ids[];
};
/*
@@ -308,6 +309,9 @@ struct cgroup_root {
/* The root cgroup. Root is destroyed on its release. */
struct cgroup cgrp;
+ /* for cgrp->ancestor_ids[0] */
+ int cgrp_ancestor_id_storage;
+
/* Number of cgroups in the hierarchy, used only for /proc/cgroups */
atomic_t nr_cgrps;
@@ -426,15 +430,12 @@ struct cgroup_subsys {
void (*css_reset)(struct cgroup_subsys_state *css);
void (*css_e_css_changed)(struct cgroup_subsys_state *css);
- int (*can_attach)(struct cgroup_subsys_state *css,
- struct cgroup_taskset *tset);
- void (*cancel_attach)(struct cgroup_subsys_state *css,
- struct cgroup_taskset *tset);
- void (*attach)(struct cgroup_subsys_state *css,
- struct cgroup_taskset *tset);
- int (*can_fork)(struct task_struct *task, void **priv_p);
- void (*cancel_fork)(struct task_struct *task, void *priv);
- void (*fork)(struct task_struct *task, void *priv);
+ int (*can_attach)(struct cgroup_taskset *tset);
+ void (*cancel_attach)(struct cgroup_taskset *tset);
+ void (*attach)(struct cgroup_taskset *tset);
+ int (*can_fork)(struct task_struct *task);
+ void (*cancel_fork)(struct task_struct *task);
+ void (*fork)(struct task_struct *task);
void (*exit)(struct task_struct *task);
void (*free)(struct task_struct *task);
void (*bind)(struct cgroup_subsys_state *root_css);
@@ -520,7 +521,6 @@ static inline void cgroup_threadgroup_change_end(struct task_struct *tsk)
#else /* CONFIG_CGROUPS */
-#define CGROUP_CANFORK_COUNT 0
#define CGROUP_SUBSYS_COUNT 0
static inline void cgroup_threadgroup_change_begin(struct task_struct *tsk) {}
@@ -528,4 +528,116 @@ static inline void cgroup_threadgroup_change_end(struct task_struct *tsk) {}
#endif /* CONFIG_CGROUPS */
+#ifdef CONFIG_SOCK_CGROUP_DATA
+
+/*
+ * sock_cgroup_data is embedded at sock->sk_cgrp_data and contains
+ * per-socket cgroup information except for memcg association.
+ *
+ * On legacy hierarchies, net_prio and net_cls controllers directly set
+ * attributes on each sock which can then be tested by the network layer.
+ * On the default hierarchy, each sock is associated with the cgroup it was
+ * created in and the networking layer can match the cgroup directly.
+ *
+ * To avoid carrying all three cgroup related fields separately in sock,
+ * sock_cgroup_data overloads (prioidx, classid) and the cgroup pointer.
+ * On boot, sock_cgroup_data records the cgroup that the sock was created
+ * in so that cgroup2 matches can be made; however, once either net_prio or
+ * net_cls starts being used, the area is overriden to carry prioidx and/or
+ * classid. The two modes are distinguished by whether the lowest bit is
+ * set. Clear bit indicates cgroup pointer while set bit prioidx and
+ * classid.
+ *
+ * While userland may start using net_prio or net_cls at any time, once
+ * either is used, cgroup2 matching no longer works. There is no reason to
+ * mix the two and this is in line with how legacy and v2 compatibility is
+ * handled. On mode switch, cgroup references which are already being
+ * pointed to by socks may be leaked. While this can be remedied by adding
+ * synchronization around sock_cgroup_data, given that the number of leaked
+ * cgroups is bound and highly unlikely to be high, this seems to be the
+ * better trade-off.
+ */
+struct sock_cgroup_data {
+ union {
+#ifdef __LITTLE_ENDIAN
+ struct {
+ u8 is_data;
+ u8 padding;
+ u16 prioidx;
+ u32 classid;
+ } __packed;
+#else
+ struct {
+ u32 classid;
+ u16 prioidx;
+ u8 padding;
+ u8 is_data;
+ } __packed;
+#endif
+ u64 val;
+ };
+};
+
+/*
+ * There's a theoretical window where the following accessors race with
+ * updaters and return part of the previous pointer as the prioidx or
+ * classid. Such races are short-lived and the result isn't critical.
+ */
+static inline u16 sock_cgroup_prioidx(struct sock_cgroup_data *skcd)
+{
+ /* fallback to 1 which is always the ID of the root cgroup */
+ return (skcd->is_data & 1) ? skcd->prioidx : 1;
+}
+
+static inline u32 sock_cgroup_classid(struct sock_cgroup_data *skcd)
+{
+ /* fallback to 0 which is the unconfigured default classid */
+ return (skcd->is_data & 1) ? skcd->classid : 0;
+}
+
+/*
+ * If invoked concurrently, the updaters may clobber each other. The
+ * caller is responsible for synchronization.
+ */
+static inline void sock_cgroup_set_prioidx(struct sock_cgroup_data *skcd,
+ u16 prioidx)
+{
+ struct sock_cgroup_data skcd_buf = {{ .val = READ_ONCE(skcd->val) }};
+
+ if (sock_cgroup_prioidx(&skcd_buf) == prioidx)
+ return;
+
+ if (!(skcd_buf.is_data & 1)) {
+ skcd_buf.val = 0;
+ skcd_buf.is_data = 1;
+ }
+
+ skcd_buf.prioidx = prioidx;
+ WRITE_ONCE(skcd->val, skcd_buf.val); /* see sock_cgroup_ptr() */
+}
+
+static inline void sock_cgroup_set_classid(struct sock_cgroup_data *skcd,
+ u32 classid)
+{
+ struct sock_cgroup_data skcd_buf = {{ .val = READ_ONCE(skcd->val) }};
+
+ if (sock_cgroup_classid(&skcd_buf) == classid)
+ return;
+
+ if (!(skcd_buf.is_data & 1)) {
+ skcd_buf.val = 0;
+ skcd_buf.is_data = 1;
+ }
+
+ skcd_buf.classid = classid;
+ WRITE_ONCE(skcd->val, skcd_buf.val); /* see sock_cgroup_ptr() */
+}
+
+#else /* CONFIG_SOCK_CGROUP_DATA */
+
+struct sock_cgroup_data {
+};
+
+#endif /* CONFIG_SOCK_CGROUP_DATA */
+
#endif /* _LINUX_CGROUP_DEFS_H */
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index 22e3754f89c5..2162dca88dc0 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -81,13 +81,15 @@ struct cgroup_subsys_state *cgroup_get_e_css(struct cgroup *cgroup,
struct cgroup_subsys_state *css_tryget_online_from_dir(struct dentry *dentry,
struct cgroup_subsys *ss);
-bool cgroup_is_descendant(struct cgroup *cgrp, struct cgroup *ancestor);
+struct cgroup *cgroup_get_from_path(const char *path);
+
int cgroup_attach_task_all(struct task_struct *from, struct task_struct *);
int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from);
int cgroup_add_dfl_cftypes(struct cgroup_subsys *ss, struct cftype *cfts);
int cgroup_add_legacy_cftypes(struct cgroup_subsys *ss, struct cftype *cfts);
int cgroup_rm_cftypes(struct cftype *cfts);
+void cgroup_file_notify(struct cgroup_file *cfile);
char *task_cgroup_path(struct task_struct *task, char *buf, size_t buflen);
int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry);
@@ -95,12 +97,9 @@ int proc_cgroup_show(struct seq_file *m, struct pid_namespace *ns,
struct pid *pid, struct task_struct *tsk);
void cgroup_fork(struct task_struct *p);
-extern int cgroup_can_fork(struct task_struct *p,
- void *ss_priv[CGROUP_CANFORK_COUNT]);
-extern void cgroup_cancel_fork(struct task_struct *p,
- void *ss_priv[CGROUP_CANFORK_COUNT]);
-extern void cgroup_post_fork(struct task_struct *p,
- void *old_ss_priv[CGROUP_CANFORK_COUNT]);
+extern int cgroup_can_fork(struct task_struct *p);
+extern void cgroup_cancel_fork(struct task_struct *p);
+extern void cgroup_post_fork(struct task_struct *p);
void cgroup_exit(struct task_struct *p);
void cgroup_free(struct task_struct *p);
@@ -119,8 +118,10 @@ struct cgroup_subsys_state *css_rightmost_descendant(struct cgroup_subsys_state
struct cgroup_subsys_state *css_next_descendant_post(struct cgroup_subsys_state *pos,
struct cgroup_subsys_state *css);
-struct task_struct *cgroup_taskset_first(struct cgroup_taskset *tset);
-struct task_struct *cgroup_taskset_next(struct cgroup_taskset *tset);
+struct task_struct *cgroup_taskset_first(struct cgroup_taskset *tset,
+ struct cgroup_subsys_state **dst_cssp);
+struct task_struct *cgroup_taskset_next(struct cgroup_taskset *tset,
+ struct cgroup_subsys_state **dst_cssp);
void css_task_iter_start(struct cgroup_subsys_state *css,
struct css_task_iter *it);
@@ -235,30 +236,39 @@ void css_task_iter_end(struct css_task_iter *it);
/**
* cgroup_taskset_for_each - iterate cgroup_taskset
* @task: the loop cursor
+ * @dst_css: the destination css
* @tset: taskset to iterate
*
* @tset may contain multiple tasks and they may belong to multiple
- * processes. When there are multiple tasks in @tset, if a task of a
- * process is in @tset, all tasks of the process are in @tset. Also, all
- * are guaranteed to share the same source and destination csses.
+ * processes.
+ *
+ * On the v2 hierarchy, there may be tasks from multiple processes and they
+ * may not share the source or destination csses.
+ *
+ * On traditional hierarchies, when there are multiple tasks in @tset, if a
+ * task of a process is in @tset, all tasks of the process are in @tset.
+ * Also, all are guaranteed to share the same source and destination csses.
*
* Iteration is not in any specific order.
*/
-#define cgroup_taskset_for_each(task, tset) \
- for ((task) = cgroup_taskset_first((tset)); (task); \
- (task) = cgroup_taskset_next((tset)))
+#define cgroup_taskset_for_each(task, dst_css, tset) \
+ for ((task) = cgroup_taskset_first((tset), &(dst_css)); \
+ (task); \
+ (task) = cgroup_taskset_next((tset), &(dst_css)))
/**
* cgroup_taskset_for_each_leader - iterate group leaders in a cgroup_taskset
* @leader: the loop cursor
+ * @dst_css: the destination css
* @tset: takset to iterate
*
* Iterate threadgroup leaders of @tset. For single-task migrations, @tset
* may not contain any.
*/
-#define cgroup_taskset_for_each_leader(leader, tset) \
- for ((leader) = cgroup_taskset_first((tset)); (leader); \
- (leader) = cgroup_taskset_next((tset))) \
+#define cgroup_taskset_for_each_leader(leader, dst_css, tset) \
+ for ((leader) = cgroup_taskset_first((tset), &(dst_css)); \
+ (leader); \
+ (leader) = cgroup_taskset_next((tset), &(dst_css))) \
if ((leader) != (leader)->group_leader) \
; \
else
@@ -352,6 +362,11 @@ static inline void css_put_many(struct cgroup_subsys_state *css, unsigned int n)
percpu_ref_put_many(&css->refcnt, n);
}
+static inline void cgroup_put(struct cgroup *cgrp)
+{
+ css_put(&cgrp->self);
+}
+
/**
* task_css_set_check - obtain a task's css_set with extra access conditions
* @task: the task to obtain css_set for
@@ -459,6 +474,23 @@ static inline struct cgroup *task_cgroup(struct task_struct *task,
return task_css(task, subsys_id)->cgroup;
}
+/**
+ * cgroup_is_descendant - test ancestry
+ * @cgrp: the cgroup to be tested
+ * @ancestor: possible ancestor of @cgrp
+ *
+ * Test whether @cgrp is a descendant of @ancestor. It also returns %true
+ * if @cgrp == @ancestor. This function is safe to call as long as @cgrp
+ * and @ancestor are accessible.
+ */
+static inline bool cgroup_is_descendant(struct cgroup *cgrp,
+ struct cgroup *ancestor)
+{
+ if (cgrp->root != ancestor->root || cgrp->level < ancestor->level)
+ return false;
+ return cgrp->ancestor_ids[ancestor->level] == ancestor->id;
+}
+
/* no synchronization, the result can only be used as a hint */
static inline bool cgroup_is_populated(struct cgroup *cgrp)
{
@@ -516,19 +548,6 @@ static inline void pr_cont_cgroup_path(struct cgroup *cgrp)
pr_cont_kernfs_path(cgrp->kn);
}
-/**
- * cgroup_file_notify - generate a file modified event for a cgroup_file
- * @cfile: target cgroup_file
- *
- * @cfile must have been obtained by setting cftype->file_offset.
- */
-static inline void cgroup_file_notify(struct cgroup_file *cfile)
-{
- /* might not have been created due to one of the CFTYPE selector flags */
- if (cfile->kn)
- kernfs_notify(cfile->kn);
-}
-
#else /* !CONFIG_CGROUPS */
struct cgroup_subsys_state;
@@ -540,13 +559,9 @@ static inline int cgroupstats_build(struct cgroupstats *stats,
struct dentry *dentry) { return -EINVAL; }
static inline void cgroup_fork(struct task_struct *p) {}
-static inline int cgroup_can_fork(struct task_struct *p,
- void *ss_priv[CGROUP_CANFORK_COUNT])
-{ return 0; }
-static inline void cgroup_cancel_fork(struct task_struct *p,
- void *ss_priv[CGROUP_CANFORK_COUNT]) {}
-static inline void cgroup_post_fork(struct task_struct *p,
- void *ss_priv[CGROUP_CANFORK_COUNT]) {}
+static inline int cgroup_can_fork(struct task_struct *p) { return 0; }
+static inline void cgroup_cancel_fork(struct task_struct *p) {}
+static inline void cgroup_post_fork(struct task_struct *p) {}
static inline void cgroup_exit(struct task_struct *p) {}
static inline void cgroup_free(struct task_struct *p) {}
@@ -555,4 +570,45 @@ static inline int cgroup_init(void) { return 0; }
#endif /* !CONFIG_CGROUPS */
+/*
+ * sock->sk_cgrp_data handling. For more info, see sock_cgroup_data
+ * definition in cgroup-defs.h.
+ */
+#ifdef CONFIG_SOCK_CGROUP_DATA
+
+#if defined(CONFIG_CGROUP_NET_PRIO) || defined(CONFIG_CGROUP_NET_CLASSID)
+extern spinlock_t cgroup_sk_update_lock;
+#endif
+
+void cgroup_sk_alloc_disable(void);
+void cgroup_sk_alloc(struct sock_cgroup_data *skcd);
+void cgroup_sk_free(struct sock_cgroup_data *skcd);
+
+static inline struct cgroup *sock_cgroup_ptr(struct sock_cgroup_data *skcd)
+{
+#if defined(CONFIG_CGROUP_NET_PRIO) || defined(CONFIG_CGROUP_NET_CLASSID)
+ unsigned long v;
+
+ /*
+ * @skcd->val is 64bit but the following is safe on 32bit too as we
+ * just need the lower ulong to be written and read atomically.
+ */
+ v = READ_ONCE(skcd->val);
+
+ if (v & 1)
+ return &cgrp_dfl_root.cgrp;
+
+ return (struct cgroup *)(unsigned long)v ?: &cgrp_dfl_root.cgrp;
+#else
+ return (struct cgroup *)(unsigned long)skcd->val;
+#endif
+}
+
+#else /* CONFIG_CGROUP_DATA */
+
+static inline void cgroup_sk_alloc(struct sock_cgroup_data *skcd) {}
+static inline void cgroup_sk_free(struct sock_cgroup_data *skcd) {}
+
+#endif /* CONFIG_CGROUP_DATA */
+
#endif /* _LINUX_CGROUP_H */
diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h
index 1a96fdaa33d5..0df0336acee9 100644
--- a/include/linux/cgroup_subsys.h
+++ b/include/linux/cgroup_subsys.h
@@ -6,14 +6,8 @@
/*
* This file *must* be included with SUBSYS() defined.
- * SUBSYS_TAG() is a noop if undefined.
*/
-#ifndef SUBSYS_TAG
-#define __TMP_SUBSYS_TAG
-#define SUBSYS_TAG(_x)
-#endif
-
#if IS_ENABLED(CONFIG_CPUSETS)
SUBSYS(cpuset)
#endif
@@ -58,17 +52,10 @@ SUBSYS(net_prio)
SUBSYS(hugetlb)
#endif
-/*
- * Subsystems that implement the can_fork() family of callbacks.
- */
-SUBSYS_TAG(CANFORK_START)
-
#if IS_ENABLED(CONFIG_CGROUP_PIDS)
SUBSYS(pids)
#endif
-SUBSYS_TAG(CANFORK_END)
-
/*
* The following subsystems are not supported on the default hierarchy.
*/
@@ -76,11 +63,6 @@ SUBSYS_TAG(CANFORK_END)
SUBSYS(debug)
#endif
-#ifdef __TMP_SUBSYS_TAG
-#undef __TMP_SUBSYS_TAG
-#undef SUBSYS_TAG
-#endif
-
/*
* DO NOT ADD ANY SUBSYSTEM WITHOUT EXPLICIT ACKS FROM CGROUP MAINTAINERS.
*/
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
index 7784b597e959..6013021a3b39 100644
--- a/include/linux/clocksource.h
+++ b/include/linux/clocksource.h
@@ -62,12 +62,18 @@ struct module;
* @suspend: suspend function for the clocksource, if necessary
* @resume: resume function for the clocksource, if necessary
* @owner: module reference, must be set by clocksource in modules
+ *
+ * Note: This struct is not used in hotpathes of the timekeeping code
+ * because the timekeeper caches the hot path fields in its own data
+ * structure, so no line cache alignment is required,
+ *
+ * The pointer to the clocksource itself is handed to the read
+ * callback. If you need extra information there you can wrap struct
+ * clocksource into your own struct. Depending on the amount of
+ * information you need you should consider to cache line align that
+ * structure.
*/
struct clocksource {
- /*
- * Hotpath data, fits in a single cache line when the
- * clocksource itself is cacheline aligned.
- */
cycle_t (*read)(struct clocksource *cs);
cycle_t mask;
u32 mult;
@@ -95,7 +101,7 @@ struct clocksource {
cycle_t wd_last;
#endif
struct module *owner;
-} ____cacheline_aligned;
+};
/*
* Clock source flags bits::
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index 4dac1036594f..00b042c49ccd 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -299,6 +299,23 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s
__u.__val; \
})
+/**
+ * smp_cond_acquire() - Spin wait for cond with ACQUIRE ordering
+ * @cond: boolean expression to wait for
+ *
+ * Equivalent to using smp_load_acquire() on the condition variable but employs
+ * the control dependency of the wait to reduce the barrier on many platforms.
+ *
+ * The control dependency provides a LOAD->STORE order, the additional RMB
+ * provides LOAD->LOAD order, together they provide LOAD->{LOAD,STORE} order,
+ * aka. ACQUIRE.
+ */
+#define smp_cond_acquire(cond) do { \
+ while (!(cond)) \
+ cpu_relax(); \
+ smp_rmb(); /* ctrl + rmb := acquire */ \
+} while (0)
+
#endif /* __KERNEL__ */
#endif /* __ASSEMBLY__ */
diff --git a/include/linux/component.h b/include/linux/component.h
index c00dcc302611..a559eebc0e0f 100644
--- a/include/linux/component.h
+++ b/include/linux/component.h
@@ -1,39 +1,48 @@
#ifndef COMPONENT_H
#define COMPONENT_H
+#include <linux/stddef.h>
+
struct device;
struct component_ops {
- int (*bind)(struct device *, struct device *, void *);
- void (*unbind)(struct device *, struct device *, void *);
+ int (*bind)(struct device *comp, struct device *master,
+ void *master_data);
+ void (*unbind)(struct device *comp, struct device *master,
+ void *master_data);
};
int component_add(struct device *, const struct component_ops *);
void component_del(struct device *, const struct component_ops *);
-int component_bind_all(struct device *, void *);
-void component_unbind_all(struct device *, void *);
+int component_bind_all(struct device *master, void *master_data);
+void component_unbind_all(struct device *master, void *master_data);
struct master;
struct component_master_ops {
- int (*add_components)(struct device *, struct master *);
- int (*bind)(struct device *);
- void (*unbind)(struct device *);
+ int (*bind)(struct device *master);
+ void (*unbind)(struct device *master);
};
-int component_master_add(struct device *, const struct component_master_ops *);
void component_master_del(struct device *,
const struct component_master_ops *);
-int component_master_add_child(struct master *master,
- int (*compare)(struct device *, void *), void *compare_data);
-
struct component_match;
int component_master_add_with_match(struct device *,
const struct component_master_ops *, struct component_match *);
-void component_match_add(struct device *, struct component_match **,
+void component_match_add_release(struct device *master,
+ struct component_match **matchptr,
+ void (*release)(struct device *, void *),
int (*compare)(struct device *, void *), void *compare_data);
+static inline void component_match_add(struct device *master,
+ struct component_match **matchptr,
+ int (*compare)(struct device *, void *), void *compare_data)
+{
+ component_match_add_release(master, matchptr, NULL, compare,
+ compare_data);
+}
+
#endif
diff --git a/include/linux/configfs.h b/include/linux/configfs.h
index 758a029011b1..f7300d023dbe 100644
--- a/include/linux/configfs.h
+++ b/include/linux/configfs.h
@@ -51,6 +51,7 @@ struct module;
struct configfs_item_operations;
struct configfs_group_operations;
struct configfs_attribute;
+struct configfs_bin_attribute;
struct configfs_subsystem;
struct config_item {
@@ -84,6 +85,7 @@ struct config_item_type {
struct configfs_item_operations *ct_item_ops;
struct configfs_group_operations *ct_group_ops;
struct configfs_attribute **ct_attrs;
+ struct configfs_bin_attribute **ct_bin_attrs;
};
/**
@@ -154,6 +156,54 @@ static struct configfs_attribute _pfx##attr_##_name = { \
.store = _pfx##_name##_store, \
}
+struct file;
+struct vm_area_struct;
+
+struct configfs_bin_attribute {
+ struct configfs_attribute cb_attr; /* std. attribute */
+ void *cb_private; /* for user */
+ size_t cb_max_size; /* max core size */
+ ssize_t (*read)(struct config_item *, void *, size_t);
+ ssize_t (*write)(struct config_item *, const void *, size_t);
+};
+
+#define CONFIGFS_BIN_ATTR(_pfx, _name, _priv, _maxsz) \
+static struct configfs_bin_attribute _pfx##attr_##_name = { \
+ .cb_attr = { \
+ .ca_name = __stringify(_name), \
+ .ca_mode = S_IRUGO | S_IWUSR, \
+ .ca_owner = THIS_MODULE, \
+ }, \
+ .cb_private = _priv, \
+ .cb_max_size = _maxsz, \
+ .read = _pfx##_name##_read, \
+ .write = _pfx##_name##_write, \
+}
+
+#define CONFIGFS_BIN_ATTR_RO(_pfx, _name, _priv, _maxsz) \
+static struct configfs_attribute _pfx##attr_##_name = { \
+ .cb_attr = { \
+ .ca_name = __stringify(_name), \
+ .ca_mode = S_IRUGO, \
+ .ca_owner = THIS_MODULE, \
+ }, \
+ .cb_private = _priv, \
+ .cb_max_size = _maxsz, \
+ .read = _pfx##_name##_read, \
+}
+
+#define CONFIGFS_BIN_ATTR_WO(_pfx, _name, _priv, _maxsz) \
+static struct configfs_attribute _pfx##attr_##_name = { \
+ .cb_attr = { \
+ .ca_name = __stringify(_name), \
+ .ca_mode = S_IWUSR, \
+ .ca_owner = THIS_MODULE, \
+ }, \
+ .cb_private = _priv, \
+ .cb_max_size = _maxsz, \
+ .write = _pfx##_name##_write, \
+}
+
/*
* If allow_link() exists, the item can symlink(2) out to other
* items. If the item is a group, it may support mkdir(2).
diff --git a/include/linux/context_tracking.h b/include/linux/context_tracking.h
index 68b575afe5f5..d259274238db 100644
--- a/include/linux/context_tracking.h
+++ b/include/linux/context_tracking.h
@@ -86,7 +86,7 @@ static inline void context_tracking_init(void) { }
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
static inline void guest_enter(void)
{
- if (vtime_accounting_enabled())
+ if (vtime_accounting_cpu_enabled())
vtime_guest_enter(current);
else
current->flags |= PF_VCPU;
@@ -100,7 +100,7 @@ static inline void guest_exit(void)
if (context_tracking_is_enabled())
__context_tracking_exit(CONTEXT_GUEST);
- if (vtime_accounting_enabled())
+ if (vtime_accounting_cpu_enabled())
vtime_guest_exit(current);
else
current->flags &= ~PF_VCPU;
diff --git a/include/linux/context_tracking_state.h b/include/linux/context_tracking_state.h
index ee956c528fab..1d34fe68f48a 100644
--- a/include/linux/context_tracking_state.h
+++ b/include/linux/context_tracking_state.h
@@ -22,12 +22,12 @@ struct context_tracking {
};
#ifdef CONFIG_CONTEXT_TRACKING
-extern struct static_key context_tracking_enabled;
+extern struct static_key_false context_tracking_enabled;
DECLARE_PER_CPU(struct context_tracking, context_tracking);
static inline bool context_tracking_is_enabled(void)
{
- return static_key_false(&context_tracking_enabled);
+ return static_branch_unlikely(&context_tracking_enabled);
}
static inline bool context_tracking_cpu_is_enabled(void)
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index ef4c5b1a860f..88a4215125bc 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -77,6 +77,7 @@ struct cpufreq_policy {
unsigned int suspend_freq; /* freq to set during suspend */
unsigned int policy; /* see above */
+ unsigned int last_policy; /* policy before unplug */
struct cpufreq_governor *governor; /* see below */
void *governor_data;
bool governor_enabled; /* governor start/stop flag */
@@ -277,7 +278,6 @@ struct cpufreq_driver {
struct freq_attr **attr;
/* platform specific boost support code */
- bool boost_supported;
bool boost_enabled;
int (*set_boost)(int state);
};
@@ -573,7 +573,6 @@ ssize_t cpufreq_show_cpus(const struct cpumask *mask, char *buf);
#ifdef CONFIG_CPU_FREQ
int cpufreq_boost_trigger_state(int state);
-int cpufreq_boost_supported(void);
int cpufreq_boost_enabled(void);
int cpufreq_enable_boost_support(void);
bool policy_has_boost_freq(struct cpufreq_policy *policy);
@@ -582,10 +581,6 @@ static inline int cpufreq_boost_trigger_state(int state)
{
return 0;
}
-static inline int cpufreq_boost_supported(void)
-{
- return 0;
-}
static inline int cpufreq_boost_enabled(void)
{
return 0;
diff --git a/include/linux/dca.h b/include/linux/dca.h
index d27a7a05718d..ad956c2e07a8 100644
--- a/include/linux/dca.h
+++ b/include/linux/dca.h
@@ -34,7 +34,7 @@ void dca_unregister_notify(struct notifier_block *nb);
struct dca_provider {
struct list_head node;
- struct dca_ops *ops;
+ const struct dca_ops *ops;
struct device *cd;
int id;
};
@@ -53,7 +53,8 @@ struct dca_ops {
int (*dev_managed) (struct dca_provider *, struct device *);
};
-struct dca_provider *alloc_dca_provider(struct dca_ops *ops, int priv_size);
+struct dca_provider *alloc_dca_provider(const struct dca_ops *ops,
+ int priv_size);
void free_dca_provider(struct dca_provider *dca);
int register_dca_provider(struct dca_provider *dca, struct device *dev);
void unregister_dca_provider(struct dca_provider *dca, struct device *dev);
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index d67ae119cf4e..7781ce110503 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -27,10 +27,10 @@ struct vfsmount;
/* The hash is always the low bits of hash_len */
#ifdef __LITTLE_ENDIAN
- #define HASH_LEN_DECLARE u32 hash; u32 len;
+ #define HASH_LEN_DECLARE u32 hash; u32 len
#define bytemask_from_count(cnt) (~(~0ul << (cnt)*8))
#else
- #define HASH_LEN_DECLARE u32 len; u32 hash;
+ #define HASH_LEN_DECLARE u32 len; u32 hash
#define bytemask_from_count(cnt) (~(~0ul >> (cnt)*8))
#endif
diff --git a/include/linux/delayed_call.h b/include/linux/delayed_call.h
new file mode 100644
index 000000000000..f7fa76ae1a9b
--- /dev/null
+++ b/include/linux/delayed_call.h
@@ -0,0 +1,34 @@
+#ifndef _DELAYED_CALL_H
+#define _DELAYED_CALL_H
+
+/*
+ * Poor man's closures; I wish we could've done them sanely polymorphic,
+ * but...
+ */
+
+struct delayed_call {
+ void (*fn)(void *);
+ void *arg;
+};
+
+#define DEFINE_DELAYED_CALL(name) struct delayed_call name = {NULL, NULL}
+
+/* I really wish we had closures with sane typechecking... */
+static inline void set_delayed_call(struct delayed_call *call,
+ void (*fn)(void *), void *arg)
+{
+ call->fn = fn;
+ call->arg = arg;
+}
+
+static inline void do_delayed_call(struct delayed_call *call)
+{
+ if (call->fn)
+ call->fn(call->arg);
+}
+
+static inline void clear_delayed_call(struct delayed_call *call)
+{
+ call->fn = NULL;
+}
+#endif
diff --git a/include/linux/device.h b/include/linux/device.h
index b8f411b57dcb..f627ba20a46c 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -191,6 +191,7 @@ extern int bus_unregister_notifier(struct bus_type *bus,
unbound */
#define BUS_NOTIFY_UNBOUND_DRIVER 0x00000007 /* driver is unbound
from the device */
+#define BUS_NOTIFY_DRIVER_NOT_BOUND 0x00000008 /* driver fails to be bound */
extern struct kset *bus_get_kset(struct bus_type *bus);
extern struct klist *bus_get_device_klist(struct bus_type *bus);
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index c47c68e535e8..16a1cad30c33 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -607,11 +607,38 @@ enum dmaengine_alignment {
};
/**
+ * struct dma_slave_map - associates slave device and it's slave channel with
+ * parameter to be used by a filter function
+ * @devname: name of the device
+ * @slave: slave channel name
+ * @param: opaque parameter to pass to struct dma_filter.fn
+ */
+struct dma_slave_map {
+ const char *devname;
+ const char *slave;
+ void *param;
+};
+
+/**
+ * struct dma_filter - information for slave device/channel to filter_fn/param
+ * mapping
+ * @fn: filter function callback
+ * @mapcnt: number of slave device/channel in the map
+ * @map: array of channel to filter mapping data
+ */
+struct dma_filter {
+ dma_filter_fn fn;
+ int mapcnt;
+ const struct dma_slave_map *map;
+};
+
+/**
* struct dma_device - info on the entity supplying DMA services
* @chancnt: how many DMA channels are supported
* @privatecnt: how many DMA channels are requested by dma_request_channel
* @channels: the list of struct dma_chan
* @global_node: list_head for global dma_device_list
+ * @filter: information for device/slave to filter function/param mapping
* @cap_mask: one or more dma_capability flags
* @max_xor: maximum number of xor sources, 0 if no capability
* @max_pq: maximum number of PQ sources and PQ-continue capability
@@ -654,11 +681,14 @@ enum dmaengine_alignment {
* paused. Returns 0 or an error code
* @device_terminate_all: Aborts all transfers on a channel. Returns 0
* or an error code
+ * @device_synchronize: Synchronizes the termination of a transfers to the
+ * current context.
* @device_tx_status: poll for transaction completion, the optional
* txstate parameter can be supplied with a pointer to get a
* struct with auxiliary transfer status information, otherwise the call
* will just return a simple status code
* @device_issue_pending: push pending transactions to hardware
+ * @descriptor_reuse: a submitted transfer can be resubmitted after completion
*/
struct dma_device {
@@ -666,6 +696,7 @@ struct dma_device {
unsigned int privatecnt;
struct list_head channels;
struct list_head global_node;
+ struct dma_filter filter;
dma_cap_mask_t cap_mask;
unsigned short max_xor;
unsigned short max_pq;
@@ -681,6 +712,7 @@ struct dma_device {
u32 src_addr_widths;
u32 dst_addr_widths;
u32 directions;
+ bool descriptor_reuse;
enum dma_residue_granularity residue_granularity;
int (*device_alloc_chan_resources)(struct dma_chan *chan);
@@ -737,6 +769,7 @@ struct dma_device {
int (*device_pause)(struct dma_chan *chan);
int (*device_resume)(struct dma_chan *chan);
int (*device_terminate_all)(struct dma_chan *chan);
+ void (*device_synchronize)(struct dma_chan *chan);
enum dma_status (*device_tx_status)(struct dma_chan *chan,
dma_cookie_t cookie,
@@ -828,6 +861,13 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_sg(
src_sg, src_nents, flags);
}
+/**
+ * dmaengine_terminate_all() - Terminate all active DMA transfers
+ * @chan: The channel for which to terminate the transfers
+ *
+ * This function is DEPRECATED use either dmaengine_terminate_sync() or
+ * dmaengine_terminate_async() instead.
+ */
static inline int dmaengine_terminate_all(struct dma_chan *chan)
{
if (chan->device->device_terminate_all)
@@ -836,6 +876,88 @@ static inline int dmaengine_terminate_all(struct dma_chan *chan)
return -ENOSYS;
}
+/**
+ * dmaengine_terminate_async() - Terminate all active DMA transfers
+ * @chan: The channel for which to terminate the transfers
+ *
+ * Calling this function will terminate all active and pending descriptors
+ * that have previously been submitted to the channel. It is not guaranteed
+ * though that the transfer for the active descriptor has stopped when the
+ * function returns. Furthermore it is possible the complete callback of a
+ * submitted transfer is still running when this function returns.
+ *
+ * dmaengine_synchronize() needs to be called before it is safe to free
+ * any memory that is accessed by previously submitted descriptors or before
+ * freeing any resources accessed from within the completion callback of any
+ * perviously submitted descriptors.
+ *
+ * This function can be called from atomic context as well as from within a
+ * complete callback of a descriptor submitted on the same channel.
+ *
+ * If none of the two conditions above apply consider using
+ * dmaengine_terminate_sync() instead.
+ */
+static inline int dmaengine_terminate_async(struct dma_chan *chan)
+{
+ if (chan->device->device_terminate_all)
+ return chan->device->device_terminate_all(chan);
+
+ return -EINVAL;
+}
+
+/**
+ * dmaengine_synchronize() - Synchronize DMA channel termination
+ * @chan: The channel to synchronize
+ *
+ * Synchronizes to the DMA channel termination to the current context. When this
+ * function returns it is guaranteed that all transfers for previously issued
+ * descriptors have stopped and and it is safe to free the memory assoicated
+ * with them. Furthermore it is guaranteed that all complete callback functions
+ * for a previously submitted descriptor have finished running and it is safe to
+ * free resources accessed from within the complete callbacks.
+ *
+ * The behavior of this function is undefined if dma_async_issue_pending() has
+ * been called between dmaengine_terminate_async() and this function.
+ *
+ * This function must only be called from non-atomic context and must not be
+ * called from within a complete callback of a descriptor submitted on the same
+ * channel.
+ */
+static inline void dmaengine_synchronize(struct dma_chan *chan)
+{
+ might_sleep();
+
+ if (chan->device->device_synchronize)
+ chan->device->device_synchronize(chan);
+}
+
+/**
+ * dmaengine_terminate_sync() - Terminate all active DMA transfers
+ * @chan: The channel for which to terminate the transfers
+ *
+ * Calling this function will terminate all active and pending transfers
+ * that have previously been submitted to the channel. It is similar to
+ * dmaengine_terminate_async() but guarantees that the DMA transfer has actually
+ * stopped and that all complete callbacks have finished running when the
+ * function returns.
+ *
+ * This function must only be called from non-atomic context and must not be
+ * called from within a complete callback of a descriptor submitted on the same
+ * channel.
+ */
+static inline int dmaengine_terminate_sync(struct dma_chan *chan)
+{
+ int ret;
+
+ ret = dmaengine_terminate_async(chan);
+ if (ret)
+ return ret;
+
+ dmaengine_synchronize(chan);
+
+ return 0;
+}
+
static inline int dmaengine_pause(struct dma_chan *chan)
{
if (chan->device->device_pause)
@@ -1140,9 +1262,11 @@ enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx);
void dma_issue_pending_all(void);
struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask,
dma_filter_fn fn, void *fn_param);
-struct dma_chan *dma_request_slave_channel_reason(struct device *dev,
- const char *name);
struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name);
+
+struct dma_chan *dma_request_chan(struct device *dev, const char *name);
+struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask);
+
void dma_release_channel(struct dma_chan *chan);
int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps);
#else
@@ -1166,16 +1290,21 @@ static inline struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask,
{
return NULL;
}
-static inline struct dma_chan *dma_request_slave_channel_reason(
- struct device *dev, const char *name)
-{
- return ERR_PTR(-ENODEV);
-}
static inline struct dma_chan *dma_request_slave_channel(struct device *dev,
const char *name)
{
return NULL;
}
+static inline struct dma_chan *dma_request_chan(struct device *dev,
+ const char *name)
+{
+ return ERR_PTR(-ENODEV);
+}
+static inline struct dma_chan *dma_request_chan_by_mask(
+ const dma_cap_mask_t *mask)
+{
+ return ERR_PTR(-ENODEV);
+}
static inline void dma_release_channel(struct dma_chan *chan)
{
}
@@ -1186,6 +1315,8 @@ static inline int dma_get_slave_caps(struct dma_chan *chan,
}
#endif
+#define dma_request_slave_channel_reason(dev, name) dma_request_chan(dev, name)
+
static inline int dmaengine_desc_set_reuse(struct dma_async_tx_descriptor *tx)
{
struct dma_slave_caps caps;
diff --git a/include/linux/dns_resolver.h b/include/linux/dns_resolver.h
index cc92268af89a..6ac3cad9aef1 100644
--- a/include/linux/dns_resolver.h
+++ b/include/linux/dns_resolver.h
@@ -27,7 +27,7 @@
#ifdef __KERNEL__
extern int dns_query(const char *type, const char *name, size_t namelen,
- const char *options, char **_result, time_t *_expiry);
+ const char *options, char **_result, time64_t *_expiry);
#endif /* KERNEL */
diff --git a/include/linux/dqblk_qtree.h b/include/linux/dqblk_qtree.h
index 82a16527b367..ff8b55359648 100644
--- a/include/linux/dqblk_qtree.h
+++ b/include/linux/dqblk_qtree.h
@@ -34,7 +34,7 @@ struct qtree_mem_dqinfo {
unsigned int dqi_entry_size; /* Size of quota entry in quota file */
unsigned int dqi_usable_bs; /* Space usable in block for quota data */
unsigned int dqi_qtree_depth; /* Precomputed depth of quota tree */
- struct qtree_fmt_operations *dqi_ops; /* Operations for entry manipulation */
+ const struct qtree_fmt_operations *dqi_ops; /* Operations for entry manipulation */
};
int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot);
diff --git a/include/linux/edac.h b/include/linux/edac.h
index 4fe67b853de0..9e0d78966552 100644
--- a/include/linux/edac.h
+++ b/include/linux/edac.h
@@ -28,12 +28,10 @@ struct device;
extern int edac_op_state;
extern int edac_err_assert;
extern atomic_t edac_handlers;
-extern struct bus_type edac_subsys;
extern int edac_handler_set(void);
extern void edac_atomic_assert_error(void);
extern struct bus_type *edac_get_sysfs_subsys(void);
-extern void edac_put_sysfs_subsys(void);
enum {
EDAC_REPORTING_ENABLED,
@@ -237,8 +235,10 @@ enum mem_type {
#define MEM_FLAG_FB_DDR2 BIT(MEM_FB_DDR2)
#define MEM_FLAG_RDDR2 BIT(MEM_RDDR2)
#define MEM_FLAG_XDR BIT(MEM_XDR)
-#define MEM_FLAG_DDR3 BIT(MEM_DDR3)
-#define MEM_FLAG_RDDR3 BIT(MEM_RDDR3)
+#define MEM_FLAG_DDR3 BIT(MEM_DDR3)
+#define MEM_FLAG_RDDR3 BIT(MEM_RDDR3)
+#define MEM_FLAG_DDR4 BIT(MEM_DDR4)
+#define MEM_FLAG_RDDR4 BIT(MEM_RDDR4)
/**
* enum edac-type - Error Detection and Correction capabilities and mode
diff --git a/include/linux/enclosure.h b/include/linux/enclosure.h
index 7be22da321f3..a4cf57cd0f75 100644
--- a/include/linux/enclosure.h
+++ b/include/linux/enclosure.h
@@ -29,7 +29,11 @@
/* A few generic types ... taken from ses-2 */
enum enclosure_component_type {
ENCLOSURE_COMPONENT_DEVICE = 0x01,
+ ENCLOSURE_COMPONENT_CONTROLLER_ELECTRONICS = 0x07,
+ ENCLOSURE_COMPONENT_SCSI_TARGET_PORT = 0x14,
+ ENCLOSURE_COMPONENT_SCSI_INITIATOR_PORT = 0x15,
ENCLOSURE_COMPONENT_ARRAY_DEVICE = 0x17,
+ ENCLOSURE_COMPONENT_SAS_EXPANDER = 0x18,
};
/* ses-2 common element status */
diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h
index eb049c622208..37ff4a6faa9a 100644
--- a/include/linux/etherdevice.h
+++ b/include/linux/etherdevice.h
@@ -29,6 +29,9 @@
#include <asm/bitsperlong.h>
#ifdef __KERNEL__
+struct device;
+int eth_platform_get_mac_address(struct device *dev, u8 *mac_addr);
+unsigned char *arch_get_platform_get_mac_address(void);
u32 eth_get_headlen(void *data, unsigned int max_len);
__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev);
extern const struct header_ops eth_header_ops;
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index 25c6324a0dd0..e59c3be92106 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -51,6 +51,7 @@
#define MAX_ACTIVE_DATA_LOGS 8
#define VERSION_LEN 256
+#define MAX_VOLUME_NAME 512
/*
* For superblock
@@ -84,7 +85,7 @@ struct f2fs_super_block {
__le32 node_ino; /* node inode number */
__le32 meta_ino; /* meta inode number */
__u8 uuid[16]; /* 128-bit uuid for volume */
- __le16 volume_name[512]; /* volume name */
+ __le16 volume_name[MAX_VOLUME_NAME]; /* volume name */
__le32 extension_count; /* # of extensions below */
__u8 extension_list[F2FS_MAX_EXTENSION][8]; /* extension array */
__le32 cp_payload;
diff --git a/include/linux/filter.h b/include/linux/filter.h
index 4165e9ac9e36..43aa1f8855c7 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -350,25 +350,43 @@ struct sk_filter {
#define BPF_PROG_RUN(filter, ctx) (*filter->bpf_func)(ctx, filter->insnsi)
+#define BPF_SKB_CB_LEN QDISC_CB_PRIV_LEN
+
+static inline u8 *bpf_skb_cb(struct sk_buff *skb)
+{
+ /* eBPF programs may read/write skb->cb[] area to transfer meta
+ * data between tail calls. Since this also needs to work with
+ * tc, that scratch memory is mapped to qdisc_skb_cb's data area.
+ *
+ * In some socket filter cases, the cb unfortunately needs to be
+ * saved/restored so that protocol specific skb->cb[] data won't
+ * be lost. In any case, due to unpriviledged eBPF programs
+ * attached to sockets, we need to clear the bpf_skb_cb() area
+ * to not leak previous contents to user space.
+ */
+ BUILD_BUG_ON(FIELD_SIZEOF(struct __sk_buff, cb) != BPF_SKB_CB_LEN);
+ BUILD_BUG_ON(FIELD_SIZEOF(struct __sk_buff, cb) !=
+ FIELD_SIZEOF(struct qdisc_skb_cb, data));
+
+ return qdisc_skb_cb(skb)->data;
+}
+
static inline u32 bpf_prog_run_save_cb(const struct bpf_prog *prog,
struct sk_buff *skb)
{
- u8 *cb_data = qdisc_skb_cb(skb)->data;
- u8 saved_cb[QDISC_CB_PRIV_LEN];
+ u8 *cb_data = bpf_skb_cb(skb);
+ u8 cb_saved[BPF_SKB_CB_LEN];
u32 res;
- BUILD_BUG_ON(FIELD_SIZEOF(struct __sk_buff, cb) !=
- QDISC_CB_PRIV_LEN);
-
if (unlikely(prog->cb_access)) {
- memcpy(saved_cb, cb_data, sizeof(saved_cb));
- memset(cb_data, 0, sizeof(saved_cb));
+ memcpy(cb_saved, cb_data, sizeof(cb_saved));
+ memset(cb_data, 0, sizeof(cb_saved));
}
res = BPF_PROG_RUN(prog, skb);
if (unlikely(prog->cb_access))
- memcpy(cb_data, saved_cb, sizeof(saved_cb));
+ memcpy(cb_data, cb_saved, sizeof(cb_saved));
return res;
}
@@ -376,10 +394,11 @@ static inline u32 bpf_prog_run_save_cb(const struct bpf_prog *prog,
static inline u32 bpf_prog_run_clear_cb(const struct bpf_prog *prog,
struct sk_buff *skb)
{
- u8 *cb_data = qdisc_skb_cb(skb)->data;
+ u8 *cb_data = bpf_skb_cb(skb);
if (unlikely(prog->cb_access))
- memset(cb_data, 0, QDISC_CB_PRIV_LEN);
+ memset(cb_data, 0, BPF_SKB_CB_LEN);
+
return BPF_PROG_RUN(prog, skb);
}
@@ -447,6 +466,8 @@ void bpf_prog_destroy(struct bpf_prog *fp);
int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
int sk_attach_bpf(u32 ufd, struct sock *sk);
+int sk_reuseport_attach_filter(struct sock_fprog *fprog, struct sock *sk);
+int sk_reuseport_attach_bpf(u32 ufd, struct sock *sk);
int sk_detach_filter(struct sock *sk);
int sk_get_filter(struct sock *sk, struct sock_filter __user *filter,
unsigned int len);
@@ -493,6 +514,25 @@ static inline void bpf_jit_free(struct bpf_prog *fp)
#define BPF_ANC BIT(15)
+static inline bool bpf_needs_clear_a(const struct sock_filter *first)
+{
+ switch (first->code) {
+ case BPF_RET | BPF_K:
+ case BPF_LD | BPF_W | BPF_LEN:
+ return false;
+
+ case BPF_LD | BPF_W | BPF_ABS:
+ case BPF_LD | BPF_H | BPF_ABS:
+ case BPF_LD | BPF_B | BPF_ABS:
+ if (first->k == SKF_AD_OFF + SKF_AD_ALU_XOR_X)
+ return true;
+ return false;
+
+ default:
+ return true;
+ }
+}
+
static inline u16 bpf_anc_helper(const struct sock_filter *ftest)
{
BUG_ON(ftest->code & BPF_ANC);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 3aa514254161..eb73d74ed992 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -31,6 +31,7 @@
#include <linux/blk_types.h>
#include <linux/workqueue.h>
#include <linux/percpu-rwsem.h>
+#include <linux/delayed_call.h>
#include <asm/byteorder.h>
#include <uapi/linux/fs.h>
@@ -482,6 +483,9 @@ struct block_device {
int bd_fsfreeze_count;
/* Mutex for freeze */
struct mutex bd_fsfreeze_mutex;
+#ifdef CONFIG_FS_DAX
+ int bd_map_count;
+#endif
};
/*
@@ -1042,7 +1046,7 @@ extern int fcntl_setlease(unsigned int fd, struct file *filp, long arg);
extern int fcntl_getlease(struct file *filp);
/* fs/locks.c */
-void locks_free_lock_context(struct file_lock_context *ctx);
+void locks_free_lock_context(struct inode *inode);
void locks_free_lock(struct file_lock *fl);
extern void locks_init_lock(struct file_lock *);
extern struct file_lock * locks_alloc_lock(void);
@@ -1103,7 +1107,7 @@ static inline int fcntl_getlease(struct file *filp)
}
static inline void
-locks_free_lock_context(struct file_lock_context *ctx)
+locks_free_lock_context(struct inode *inode)
{
}
@@ -1629,16 +1633,21 @@ struct file_operations {
#ifndef CONFIG_MMU
unsigned (*mmap_capabilities)(struct file *);
#endif
+ ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
+ loff_t, size_t, unsigned int);
+ int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,
+ u64);
+ ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *,
+ u64);
};
struct inode_operations {
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
- const char * (*follow_link) (struct dentry *, void **);
+ const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *);
int (*permission) (struct inode *, int);
struct posix_acl * (*get_acl)(struct inode *, int);
int (*readlink) (struct dentry *, char __user *,int);
- void (*put_link) (struct inode *, void *);
int (*create) (struct inode *,struct dentry *, umode_t, bool);
int (*link) (struct dentry *,struct inode *,struct dentry *);
@@ -1680,6 +1689,12 @@ extern ssize_t vfs_readv(struct file *, const struct iovec __user *,
unsigned long, loff_t *);
extern ssize_t vfs_writev(struct file *, const struct iovec __user *,
unsigned long, loff_t *);
+extern ssize_t vfs_copy_file_range(struct file *, loff_t , struct file *,
+ loff_t, size_t, unsigned int);
+extern int vfs_clone_file_range(struct file *file_in, loff_t pos_in,
+ struct file *file_out, loff_t pos_out, u64 len);
+extern int vfs_dedupe_file_range(struct file *file,
+ struct file_dedupe_range *same);
struct super_operations {
struct inode *(*alloc_inode)(struct super_block *sb);
@@ -2027,12 +2042,9 @@ extern struct kobject *fs_kobj;
#define MAX_RW_COUNT (INT_MAX & PAGE_CACHE_MASK)
-#define FLOCK_VERIFY_READ 1
-#define FLOCK_VERIFY_WRITE 2
-
-#ifdef CONFIG_FILE_LOCKING
+#ifdef CONFIG_MANDATORY_FILE_LOCKING
extern int locks_mandatory_locked(struct file *);
-extern int locks_mandatory_area(int, struct inode *, struct file *, loff_t, size_t);
+extern int locks_mandatory_area(struct inode *, struct file *, loff_t, loff_t, unsigned char);
/*
* Candidates for mandatory locking have the setgid bit set
@@ -2062,19 +2074,59 @@ static inline int locks_verify_locked(struct file *file)
}
static inline int locks_verify_truncate(struct inode *inode,
- struct file *filp,
+ struct file *f,
loff_t size)
{
- if (inode->i_flctx && mandatory_lock(inode))
- return locks_mandatory_area(
- FLOCK_VERIFY_WRITE, inode, filp,
- size < inode->i_size ? size : inode->i_size,
- (size < inode->i_size ? inode->i_size - size
- : size - inode->i_size)
- );
+ if (!inode->i_flctx || !mandatory_lock(inode))
+ return 0;
+
+ if (size < inode->i_size) {
+ return locks_mandatory_area(inode, f, size, inode->i_size - 1,
+ F_WRLCK);
+ } else {
+ return locks_mandatory_area(inode, f, inode->i_size, size - 1,
+ F_WRLCK);
+ }
+}
+
+#else /* !CONFIG_MANDATORY_FILE_LOCKING */
+
+static inline int locks_mandatory_locked(struct file *file)
+{
+ return 0;
+}
+
+static inline int locks_mandatory_area(struct inode *inode, struct file *filp,
+ loff_t start, loff_t end, unsigned char type)
+{
return 0;
}
+static inline int __mandatory_lock(struct inode *inode)
+{
+ return 0;
+}
+
+static inline int mandatory_lock(struct inode *inode)
+{
+ return 0;
+}
+
+static inline int locks_verify_locked(struct file *file)
+{
+ return 0;
+}
+
+static inline int locks_verify_truncate(struct inode *inode, struct file *filp,
+ size_t size)
+{
+ return 0;
+}
+
+#endif /* CONFIG_MANDATORY_FILE_LOCKING */
+
+
+#ifdef CONFIG_FILE_LOCKING
static inline int break_lease(struct inode *inode, unsigned int mode)
{
/*
@@ -2136,39 +2188,6 @@ static inline int break_layout(struct inode *inode, bool wait)
}
#else /* !CONFIG_FILE_LOCKING */
-static inline int locks_mandatory_locked(struct file *file)
-{
- return 0;
-}
-
-static inline int locks_mandatory_area(int rw, struct inode *inode,
- struct file *filp, loff_t offset,
- size_t count)
-{
- return 0;
-}
-
-static inline int __mandatory_lock(struct inode *inode)
-{
- return 0;
-}
-
-static inline int mandatory_lock(struct inode *inode)
-{
- return 0;
-}
-
-static inline int locks_verify_locked(struct file *file)
-{
- return 0;
-}
-
-static inline int locks_verify_truncate(struct inode *inode, struct file *filp,
- size_t size)
-{
- return 0;
-}
-
static inline int break_lease(struct inode *inode, unsigned int mode)
{
return 0;
@@ -2264,6 +2283,14 @@ extern struct super_block *freeze_bdev(struct block_device *);
extern void emergency_thaw_all(void);
extern int thaw_bdev(struct block_device *bdev, struct super_block *sb);
extern int fsync_bdev(struct block_device *);
+#ifdef CONFIG_FS_DAX
+extern bool blkdev_dax_capable(struct block_device *bdev);
+#else
+static inline bool blkdev_dax_capable(struct block_device *bdev)
+{
+ return false;
+}
+#endif
extern struct super_block *blockdev_superblock;
@@ -2291,9 +2318,9 @@ static inline void iterate_bdevs(void (*f)(struct block_device *, void *), void
{
}
-static inline int sb_is_blkdev_sb(struct super_block *sb)
+static inline bool sb_is_blkdev_sb(struct super_block *sb)
{
- return 0;
+ return false;
}
#endif
extern int sync_filesystem(struct super_block *);
@@ -2371,7 +2398,7 @@ extern void init_special_inode(struct inode *, umode_t, dev_t);
/* Invalid inode operations -- fs/bad_inode.c */
extern void make_bad_inode(struct inode *);
-extern int is_bad_inode(struct inode *);
+extern bool is_bad_inode(struct inode *);
#ifdef CONFIG_BLOCK
/*
@@ -2532,8 +2559,8 @@ extern ssize_t __kernel_write(struct file *, const char *, size_t, loff_t *);
extern struct file * open_exec(const char *);
/* fs/dcache.c -- generic fs support functions */
-extern int is_subdir(struct dentry *, struct dentry *);
-extern int path_is_under(struct path *, struct path *);
+extern bool is_subdir(struct dentry *, struct dentry *);
+extern bool path_is_under(struct path *, struct path *);
extern char *file_path(struct file *, char *, int);
@@ -2660,6 +2687,8 @@ extern loff_t generic_file_llseek_size(struct file *file, loff_t offset,
int whence, loff_t maxsize, loff_t eof);
extern loff_t fixed_size_llseek(struct file *file, loff_t offset,
int whence, loff_t size);
+extern loff_t no_seek_end_llseek_size(struct file *, loff_t, int, loff_t);
+extern loff_t no_seek_end_llseek(struct file *, loff_t, int);
extern int generic_file_open(struct inode * inode, struct file * filp);
extern int nonseekable_open(struct inode * inode, struct file * filp);
@@ -2736,14 +2765,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 const char *page_follow_link_light(struct dentry *, void **);
-extern void page_put_link(struct inode *, void *);
+extern const char *page_get_link(struct dentry *, struct inode *,
+ struct delayed_call *);
+extern void page_put_link(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 inode *, void *);
-extern void free_page_put_link(struct inode *, void *);
+extern void kfree_link(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);
@@ -2754,7 +2783,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 **);
+const char *simple_get_link(struct dentry *, struct inode *,
+ struct delayed_call *);
extern const struct inode_operations simple_symlink_inode_operations;
extern int iterate_dir(struct file *, struct dir_context *);
@@ -2764,8 +2794,6 @@ extern int vfs_lstat(const char __user *, struct kstat *);
extern int vfs_fstat(unsigned int, struct kstat *);
extern int vfs_fstatat(int , const char __user *, struct kstat *, int);
-extern int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
- unsigned long arg);
extern int __generic_block_fiemap(struct inode *inode,
struct fiemap_extent_info *fieinfo,
loff_t start, loff_t len,
@@ -2963,7 +2991,7 @@ int __init get_filesystem_list(char *buf);
#define OPEN_FMODE(flag) ((__force fmode_t)(((flag + 1) & O_ACCMODE) | \
(flag & __FMODE_NONOTIFY)))
-static inline int is_sxid(umode_t mode)
+static inline bool is_sxid(umode_t mode)
{
return (mode & S_ISUID) || ((mode & S_ISGID) && (mode & S_IXGRP));
}
@@ -3025,5 +3053,6 @@ static inline bool dir_relax(struct inode *inode)
}
extern bool path_noexec(const struct path *path);
+extern void inode_nohighmem(struct inode *inode);
#endif /* _LINUX_FS_H */
diff --git a/include/linux/fsl/edac.h b/include/linux/fsl/edac.h
new file mode 100644
index 000000000000..90d64d4ec1a9
--- /dev/null
+++ b/include/linux/fsl/edac.h
@@ -0,0 +1,8 @@
+#ifndef FSL_EDAC_H
+#define FSL_EDAC_H
+
+struct mpc85xx_edac_pci_plat_data {
+ struct device_node *of_node;
+};
+
+#endif
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index 533c4408529a..6b7e89f45aa4 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -220,7 +220,10 @@ struct fsnotify_mark {
/* List of marks by group->i_fsnotify_marks. Also reused for queueing
* mark into destroy_list when it's waiting for the end of SRCU period
* before it can be freed. [group->mark_mutex] */
- struct list_head g_list;
+ union {
+ struct list_head g_list;
+ struct rcu_head g_rcu;
+ };
/* Protects inode / mnt pointers, flags, masks */
spinlock_t lock;
/* List of marks for inode / vfsmount [obj_lock] */
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index eae6548efbf0..0639dcc98195 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -76,8 +76,8 @@ ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops);
* ENABLED - set/unset when ftrace_ops is registered/unregistered
* DYNAMIC - set when ftrace_ops is registered to denote dynamically
* allocated ftrace_ops which need special care
- * CONTROL - set manualy by ftrace_ops user to denote the ftrace_ops
- * could be controled by following calls:
+ * PER_CPU - set manualy by ftrace_ops user to denote the ftrace_ops
+ * could be controlled by following calls:
* ftrace_function_local_enable
* ftrace_function_local_disable
* SAVE_REGS - The ftrace_ops wants regs saved at each function called
@@ -121,7 +121,7 @@ ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops);
enum {
FTRACE_OPS_FL_ENABLED = 1 << 0,
FTRACE_OPS_FL_DYNAMIC = 1 << 1,
- FTRACE_OPS_FL_CONTROL = 1 << 2,
+ FTRACE_OPS_FL_PER_CPU = 1 << 2,
FTRACE_OPS_FL_SAVE_REGS = 1 << 3,
FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED = 1 << 4,
FTRACE_OPS_FL_RECURSION_SAFE = 1 << 5,
@@ -134,6 +134,7 @@ enum {
FTRACE_OPS_FL_ALLOC_TRAMP = 1 << 12,
FTRACE_OPS_FL_IPMODIFY = 1 << 13,
FTRACE_OPS_FL_PID = 1 << 14,
+ FTRACE_OPS_FL_RCU = 1 << 15,
};
#ifdef CONFIG_DYNAMIC_FTRACE
@@ -146,11 +147,11 @@ struct ftrace_ops_hash {
#endif
/*
- * Note, ftrace_ops can be referenced outside of RCU protection.
- * (Although, for perf, the control ops prevent that). If ftrace_ops is
- * allocated and not part of kernel core data, the unregistering of it will
- * perform a scheduling on all CPUs to make sure that there are no more users.
- * Depending on the load of the system that may take a bit of time.
+ * Note, ftrace_ops can be referenced outside of RCU protection, unless
+ * the RCU flag is set. If ftrace_ops is allocated and not part of kernel
+ * core data, the unregistering of it will perform a scheduling on all CPUs
+ * to make sure that there are no more users. Depending on the load of the
+ * system that may take a bit of time.
*
* Any private data added must also take care not to be freed and if private
* data is added to a ftrace_ops that is in core code, the user of the
@@ -196,34 +197,34 @@ int unregister_ftrace_function(struct ftrace_ops *ops);
void clear_ftrace_function(void);
/**
- * ftrace_function_local_enable - enable controlled ftrace_ops on current cpu
+ * ftrace_function_local_enable - enable ftrace_ops on current cpu
*
* This function enables tracing on current cpu by decreasing
* the per cpu control variable.
* It must be called with preemption disabled and only on ftrace_ops
- * registered with FTRACE_OPS_FL_CONTROL. If called without preemption
+ * registered with FTRACE_OPS_FL_PER_CPU. If called without preemption
* disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled.
*/
static inline void ftrace_function_local_enable(struct ftrace_ops *ops)
{
- if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_CONTROL)))
+ if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_PER_CPU)))
return;
(*this_cpu_ptr(ops->disabled))--;
}
/**
- * ftrace_function_local_disable - enable controlled ftrace_ops on current cpu
+ * ftrace_function_local_disable - disable ftrace_ops on current cpu
*
- * This function enables tracing on current cpu by decreasing
+ * This function disables tracing on current cpu by increasing
* the per cpu control variable.
* It must be called with preemption disabled and only on ftrace_ops
- * registered with FTRACE_OPS_FL_CONTROL. If called without preemption
+ * registered with FTRACE_OPS_FL_PER_CPU. If called without preemption
* disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled.
*/
static inline void ftrace_function_local_disable(struct ftrace_ops *ops)
{
- if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_CONTROL)))
+ if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_PER_CPU)))
return;
(*this_cpu_ptr(ops->disabled))++;
@@ -235,12 +236,12 @@ static inline void ftrace_function_local_disable(struct ftrace_ops *ops)
*
* This function returns value of ftrace_ops::disabled on current cpu.
* It must be called with preemption disabled and only on ftrace_ops
- * registered with FTRACE_OPS_FL_CONTROL. If called without preemption
+ * registered with FTRACE_OPS_FL_PER_CPU. If called without preemption
* disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled.
*/
static inline int ftrace_function_local_disabled(struct ftrace_ops *ops)
{
- WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_CONTROL));
+ WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_PER_CPU));
return *this_cpu_ptr(ops->disabled);
}
@@ -296,6 +297,21 @@ int ftrace_arch_code_modify_post_process(void);
struct dyn_ftrace;
+enum ftrace_bug_type {
+ FTRACE_BUG_UNKNOWN,
+ FTRACE_BUG_INIT,
+ FTRACE_BUG_NOP,
+ FTRACE_BUG_CALL,
+ FTRACE_BUG_UPDATE,
+};
+extern enum ftrace_bug_type ftrace_bug_type;
+
+/*
+ * Archs can set this to point to a variable that holds the value that was
+ * expected at the call site before calling ftrace_bug().
+ */
+extern const void *ftrace_expected;
+
void ftrace_bug(int err, struct dyn_ftrace *rec);
struct seq_file;
@@ -341,6 +357,7 @@ bool is_ftrace_trampoline(unsigned long addr);
* REGS - the record wants the function to save regs
* REGS_EN - the function is set up to save regs.
* IPMODIFY - the record allows for the IP address to be changed.
+ * DISABLED - the record is not ready to be touched yet
*
* When a new ftrace_ops is registered and wants a function to save
* pt_regs, the rec->flag REGS is set. When the function has been
@@ -355,10 +372,11 @@ enum {
FTRACE_FL_TRAMP = (1UL << 28),
FTRACE_FL_TRAMP_EN = (1UL << 27),
FTRACE_FL_IPMODIFY = (1UL << 26),
+ FTRACE_FL_DISABLED = (1UL << 25),
};
-#define FTRACE_REF_MAX_SHIFT 26
-#define FTRACE_FL_BITS 6
+#define FTRACE_REF_MAX_SHIFT 25
+#define FTRACE_FL_BITS 7
#define FTRACE_FL_MASKED_BITS ((1UL << FTRACE_FL_BITS) - 1)
#define FTRACE_FL_MASK (FTRACE_FL_MASKED_BITS << FTRACE_REF_MAX_SHIFT)
#define FTRACE_REF_MAX ((1UL << FTRACE_REF_MAX_SHIFT) - 1)
@@ -586,6 +604,7 @@ extern int ftrace_arch_read_dyn_info(char *buf, int size);
extern int skip_trace(unsigned long ip);
extern void ftrace_module_init(struct module *mod);
+extern void ftrace_release_mod(struct module *mod);
extern void ftrace_disable_daemon(void);
extern void ftrace_enable_daemon(void);
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index 847cc1d91634..5c706765404a 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -162,6 +162,7 @@ struct disk_part_tbl {
};
struct disk_events;
+struct badblocks;
#if defined(CONFIG_BLK_DEV_INTEGRITY)
@@ -213,6 +214,7 @@ struct gendisk {
struct kobject integrity_kobj;
#endif /* CONFIG_BLK_DEV_INTEGRITY */
int node_id;
+ struct badblocks *bb;
};
static inline struct gendisk *part_to_disk(struct hd_struct *part)
diff --git a/include/linux/gfp.h b/include/linux/gfp.h
index 8942af0813e3..28ad5f6494b0 100644
--- a/include/linux/gfp.h
+++ b/include/linux/gfp.h
@@ -30,7 +30,7 @@ struct vm_area_struct;
#define ___GFP_HARDWALL 0x20000u
#define ___GFP_THISNODE 0x40000u
#define ___GFP_ATOMIC 0x80000u
-#define ___GFP_NOACCOUNT 0x100000u
+#define ___GFP_ACCOUNT 0x100000u
#define ___GFP_NOTRACK 0x200000u
#define ___GFP_DIRECT_RECLAIM 0x400000u
#define ___GFP_OTHER_NODE 0x800000u
@@ -73,11 +73,15 @@ struct vm_area_struct;
*
* __GFP_THISNODE forces the allocation to be satisified from the requested
* node with no fallbacks or placement policy enforcements.
+ *
+ * __GFP_ACCOUNT causes the allocation to be accounted to kmemcg (only relevant
+ * to kmem allocations).
*/
#define __GFP_RECLAIMABLE ((__force gfp_t)___GFP_RECLAIMABLE)
#define __GFP_WRITE ((__force gfp_t)___GFP_WRITE)
#define __GFP_HARDWALL ((__force gfp_t)___GFP_HARDWALL)
#define __GFP_THISNODE ((__force gfp_t)___GFP_THISNODE)
+#define __GFP_ACCOUNT ((__force gfp_t)___GFP_ACCOUNT)
/*
* Watermark modifiers -- controls access to emergency reserves
@@ -104,7 +108,6 @@ struct vm_area_struct;
#define __GFP_HIGH ((__force gfp_t)___GFP_HIGH)
#define __GFP_MEMALLOC ((__force gfp_t)___GFP_MEMALLOC)
#define __GFP_NOMEMALLOC ((__force gfp_t)___GFP_NOMEMALLOC)
-#define __GFP_NOACCOUNT ((__force gfp_t)___GFP_NOACCOUNT)
/*
* Reclaim modifiers
@@ -197,6 +200,9 @@ struct vm_area_struct;
* GFP_KERNEL is typical for kernel-internal allocations. The caller requires
* ZONE_NORMAL or a lower zone for direct access but can direct reclaim.
*
+ * GFP_KERNEL_ACCOUNT is the same as GFP_KERNEL, except the allocation is
+ * accounted to kmemcg.
+ *
* GFP_NOWAIT is for kernel allocations that should not stall for direct
* reclaim, start physical IO or use any filesystem callback.
*
@@ -236,6 +242,7 @@ struct vm_area_struct;
*/
#define GFP_ATOMIC (__GFP_HIGH|__GFP_ATOMIC|__GFP_KSWAPD_RECLAIM)
#define GFP_KERNEL (__GFP_RECLAIM | __GFP_IO | __GFP_FS)
+#define GFP_KERNEL_ACCOUNT (GFP_KERNEL | __GFP_ACCOUNT)
#define GFP_NOWAIT (__GFP_KSWAPD_RECLAIM)
#define GFP_NOIO (__GFP_RECLAIM)
#define GFP_NOFS (__GFP_RECLAIM | __GFP_IO)
@@ -271,7 +278,7 @@ static inline int gfpflags_to_migratetype(const gfp_t gfp_flags)
static inline bool gfpflags_allow_blocking(const gfp_t gfp_flags)
{
- return (bool __force)(gfp_flags & __GFP_DIRECT_RECLAIM);
+ return !!(gfp_flags & __GFP_DIRECT_RECLAIM);
}
#ifdef CONFIG_HIGHMEM
@@ -377,10 +384,11 @@ static inline enum zone_type gfp_zone(gfp_t flags)
static inline int gfp_zonelist(gfp_t flags)
{
- if (IS_ENABLED(CONFIG_NUMA) && unlikely(flags & __GFP_THISNODE))
- return 1;
-
- return 0;
+#ifdef CONFIG_NUMA
+ if (unlikely(flags & __GFP_THISNODE))
+ return ZONELIST_NOFALLBACK;
+#endif
+ return ZONELIST_FALLBACK;
}
/*
diff --git a/include/linux/hashtable.h b/include/linux/hashtable.h
index 519b6e2d769e..661e5c2a8e2a 100644
--- a/include/linux/hashtable.h
+++ b/include/linux/hashtable.h
@@ -16,6 +16,10 @@
struct hlist_head name[1 << (bits)] = \
{ [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT }
+#define DEFINE_READ_MOSTLY_HASHTABLE(name, bits) \
+ struct hlist_head name[1 << (bits)] __read_mostly = \
+ { [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT }
+
#define DECLARE_HASHTABLE(name, bits) \
struct hlist_head name[1 << (bits)]
diff --git a/include/linux/hdlc.h b/include/linux/hdlc.h
index 1acb1445e05f..e31bcd4c7859 100644
--- a/include/linux/hdlc.h
+++ b/include/linux/hdlc.h
@@ -101,7 +101,7 @@ netdev_tx_t hdlc_start_xmit(struct sk_buff *skb, struct net_device *dev);
int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto,
size_t size);
/* May be used by hardware driver to gain control over HDLC device */
-void detach_hdlc_protocol(struct net_device *dev);
+int detach_hdlc_protocol(struct net_device *dev);
static __inline__ __be16 hdlc_type_trans(struct sk_buff *skb,
struct net_device *dev)
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 251a1d382e23..75b66eccc692 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -168,6 +168,8 @@ struct hid_item {
#define HID_UP_MSVENDOR 0xff000000
#define HID_UP_CUSTOM 0x00ff0000
#define HID_UP_LOGIVENDOR 0xffbc0000
+#define HID_UP_LOGIVENDOR2 0xff090000
+#define HID_UP_LOGIVENDOR3 0xff430000
#define HID_UP_LNVENDOR 0xffa00000
#define HID_UP_SENSOR 0x00200000
@@ -563,6 +565,9 @@ struct hid_device { /* device report descriptor */
wait_queue_head_t debug_wait;
};
+#define to_hid_device(pdev) \
+ container_of(pdev, struct hid_device, dev)
+
static inline void *hid_get_drvdata(struct hid_device *hdev)
{
return dev_get_drvdata(&hdev->dev);
@@ -712,6 +717,9 @@ struct hid_driver {
struct device_driver driver;
};
+#define to_hid_driver(pdrv) \
+ container_of(pdrv, struct hid_driver, driver)
+
/**
* hid_ll_driver - low level driver callbacks
* @start: called on probe to start the device
diff --git a/include/linux/hsi/hsi.h b/include/linux/hsi/hsi.h
index 5dd60c2e120f..2790591c77cf 100644
--- a/include/linux/hsi/hsi.h
+++ b/include/linux/hsi/hsi.h
@@ -135,9 +135,6 @@ static inline int hsi_register_board_info(struct hsi_board_info const *info,
* @device: Driver model representation of the device
* @tx_cfg: HSI TX configuration
* @rx_cfg: HSI RX configuration
- * @e_handler: Callback for handling port events (RX Wake High/Low)
- * @pclaimed: Keeps tracks if the clients claimed its associated HSI port
- * @nb: Notifier block for port events
*/
struct hsi_client {
struct device device;
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 685c262e0be8..e76574d8f9b5 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -96,9 +96,7 @@ u32 hugetlb_fault_mutex_hash(struct hstate *h, struct mm_struct *mm,
struct address_space *mapping,
pgoff_t idx, unsigned long address);
-#ifdef CONFIG_ARCH_WANT_HUGE_PMD_SHARE
pte_t *huge_pmd_share(struct mm_struct *mm, unsigned long addr, pud_t *pud);
-#endif
extern int hugepages_treat_as_movable;
extern int sysctl_hugetlb_shm_group;
@@ -265,20 +263,18 @@ struct file *hugetlb_file_setup(const char *name, size_t size, vm_flags_t acct,
struct user_struct **user, int creat_flags,
int page_size_log);
-static inline int is_file_hugepages(struct file *file)
+static inline bool is_file_hugepages(struct file *file)
{
if (file->f_op == &hugetlbfs_file_operations)
- return 1;
- if (is_file_shm_hugepages(file))
- return 1;
+ return true;
- return 0;
+ return is_file_shm_hugepages(file);
}
#else /* !CONFIG_HUGETLBFS */
-#define is_file_hugepages(file) 0
+#define is_file_hugepages(file) false
static inline struct file *
hugetlb_file_setup(const char *name, size_t size, vm_flags_t acctflag,
struct user_struct **user, int creat_flags,
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index 8fdc17b84739..753dbad0bf94 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -141,8 +141,6 @@ hv_get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi,
{
u32 read_loc, write_loc, dsize;
- smp_read_barrier_depends();
-
/* Capture the read/write indices before they changed */
read_loc = rbi->ring_buffer->read_index;
write_loc = rbi->ring_buffer->write_index;
@@ -630,6 +628,11 @@ struct hv_input_signal_event_buffer {
struct hv_input_signal_event event;
};
+enum hv_signal_policy {
+ HV_SIGNAL_POLICY_DEFAULT = 0,
+ HV_SIGNAL_POLICY_EXPLICIT,
+};
+
struct vmbus_channel {
/* Unique channel id */
int id;
@@ -757,8 +760,21 @@ struct vmbus_channel {
* link up channels based on their CPU affinity.
*/
struct list_head percpu_list;
+ /*
+ * Host signaling policy: The default policy will be
+ * based on the ring buffer state. We will also support
+ * a policy where the client driver can have explicit
+ * signaling control.
+ */
+ enum hv_signal_policy signal_policy;
};
+static inline void set_channel_signal_state(struct vmbus_channel *c,
+ enum hv_signal_policy policy)
+{
+ c->signal_policy = policy;
+}
+
static inline void set_channel_read_state(struct vmbus_channel *c, bool state)
{
c->batched_reading = state;
@@ -983,16 +999,8 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
resource_size_t size, resource_size_t align,
bool fb_overlap_ok);
-/**
- * VMBUS_DEVICE - macro used to describe a specific hyperv vmbus device
- *
- * This macro is used to create a struct hv_vmbus_device_id that matches a
- * specific device.
- */
-#define VMBUS_DEVICE(g0, g1, g2, g3, g4, g5, g6, g7, \
- g8, g9, ga, gb, gc, gd, ge, gf) \
- .guid = { g0, g1, g2, g3, g4, g5, g6, g7, \
- g8, g9, ga, gb, gc, gd, ge, gf },
+int vmbus_cpu_number_to_vp_number(int cpu_number);
+u64 hv_do_hypercall(u64 control, void *input, void *output);
/*
* GUID definitions of various offer types - services offered to the guest.
@@ -1003,118 +1011,102 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
* {f8615163-df3e-46c5-913f-f2d2f965ed0e}
*/
#define HV_NIC_GUID \
- .guid = { \
- 0x63, 0x51, 0x61, 0xf8, 0x3e, 0xdf, 0xc5, 0x46, \
- 0x91, 0x3f, 0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e \
- }
+ .guid = UUID_LE(0xf8615163, 0xdf3e, 0x46c5, 0x91, 0x3f, \
+ 0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e)
/*
* IDE GUID
* {32412632-86cb-44a2-9b5c-50d1417354f5}
*/
#define HV_IDE_GUID \
- .guid = { \
- 0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, \
- 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5 \
- }
+ .guid = UUID_LE(0x32412632, 0x86cb, 0x44a2, 0x9b, 0x5c, \
+ 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5)
/*
* SCSI GUID
* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f}
*/
#define HV_SCSI_GUID \
- .guid = { \
- 0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, \
- 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f \
- }
+ .guid = UUID_LE(0xba6163d9, 0x04a1, 0x4d29, 0xb6, 0x05, \
+ 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f)
/*
* Shutdown GUID
* {0e0b6031-5213-4934-818b-38d90ced39db}
*/
#define HV_SHUTDOWN_GUID \
- .guid = { \
- 0x31, 0x60, 0x0b, 0x0e, 0x13, 0x52, 0x34, 0x49, \
- 0x81, 0x8b, 0x38, 0xd9, 0x0c, 0xed, 0x39, 0xdb \
- }
+ .guid = UUID_LE(0x0e0b6031, 0x5213, 0x4934, 0x81, 0x8b, \
+ 0x38, 0xd9, 0x0c, 0xed, 0x39, 0xdb)
/*
* Time Synch GUID
* {9527E630-D0AE-497b-ADCE-E80AB0175CAF}
*/
#define HV_TS_GUID \
- .guid = { \
- 0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, \
- 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf \
- }
+ .guid = UUID_LE(0x9527e630, 0xd0ae, 0x497b, 0xad, 0xce, \
+ 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf)
/*
* Heartbeat GUID
* {57164f39-9115-4e78-ab55-382f3bd5422d}
*/
#define HV_HEART_BEAT_GUID \
- .guid = { \
- 0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, \
- 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d \
- }
+ .guid = UUID_LE(0x57164f39, 0x9115, 0x4e78, 0xab, 0x55, \
+ 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d)
/*
* KVP GUID
* {a9a0f4e7-5a45-4d96-b827-8a841e8c03e6}
*/
#define HV_KVP_GUID \
- .guid = { \
- 0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, \
- 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6 \
- }
+ .guid = UUID_LE(0xa9a0f4e7, 0x5a45, 0x4d96, 0xb8, 0x27, \
+ 0x8a, 0x84, 0x1e, 0x8c, 0x03, 0xe6)
/*
* Dynamic memory GUID
* {525074dc-8985-46e2-8057-a307dc18a502}
*/
#define HV_DM_GUID \
- .guid = { \
- 0xdc, 0x74, 0x50, 0X52, 0x85, 0x89, 0xe2, 0x46, \
- 0x80, 0x57, 0xa3, 0x07, 0xdc, 0x18, 0xa5, 0x02 \
- }
+ .guid = UUID_LE(0x525074dc, 0x8985, 0x46e2, 0x80, 0x57, \
+ 0xa3, 0x07, 0xdc, 0x18, 0xa5, 0x02)
/*
* Mouse GUID
* {cfa8b69e-5b4a-4cc0-b98b-8ba1a1f3f95a}
*/
#define HV_MOUSE_GUID \
- .guid = { \
- 0x9e, 0xb6, 0xa8, 0xcf, 0x4a, 0x5b, 0xc0, 0x4c, \
- 0xb9, 0x8b, 0x8b, 0xa1, 0xa1, 0xf3, 0xf9, 0x5a \
- }
+ .guid = UUID_LE(0xcfa8b69e, 0x5b4a, 0x4cc0, 0xb9, 0x8b, \
+ 0x8b, 0xa1, 0xa1, 0xf3, 0xf9, 0x5a)
+
+/*
+ * Keyboard GUID
+ * {f912ad6d-2b17-48ea-bd65-f927a61c7684}
+ */
+#define HV_KBD_GUID \
+ .guid = UUID_LE(0xf912ad6d, 0x2b17, 0x48ea, 0xbd, 0x65, \
+ 0xf9, 0x27, 0xa6, 0x1c, 0x76, 0x84)
/*
* VSS (Backup/Restore) GUID
*/
#define HV_VSS_GUID \
- .guid = { \
- 0x29, 0x2e, 0xfa, 0x35, 0x23, 0xea, 0x36, 0x42, \
- 0x96, 0xae, 0x3a, 0x6e, 0xba, 0xcb, 0xa4, 0x40 \
- }
+ .guid = UUID_LE(0x35fa2e29, 0xea23, 0x4236, 0x96, 0xae, \
+ 0x3a, 0x6e, 0xba, 0xcb, 0xa4, 0x40)
/*
* Synthetic Video GUID
* {DA0A7802-E377-4aac-8E77-0558EB1073F8}
*/
#define HV_SYNTHVID_GUID \
- .guid = { \
- 0x02, 0x78, 0x0a, 0xda, 0x77, 0xe3, 0xac, 0x4a, \
- 0x8e, 0x77, 0x05, 0x58, 0xeb, 0x10, 0x73, 0xf8 \
- }
+ .guid = UUID_LE(0xda0a7802, 0xe377, 0x4aac, 0x8e, 0x77, \
+ 0x05, 0x58, 0xeb, 0x10, 0x73, 0xf8)
/*
* Synthetic FC GUID
* {2f9bcc4a-0069-4af3-b76b-6fd0be528cda}
*/
#define HV_SYNTHFC_GUID \
- .guid = { \
- 0x4A, 0xCC, 0x9B, 0x2F, 0x69, 0x00, 0xF3, 0x4A, \
- 0xB7, 0x6B, 0x6F, 0xD0, 0xBE, 0x52, 0x8C, 0xDA \
- }
+ .guid = UUID_LE(0x2f9bcc4a, 0x0069, 0x4af3, 0xb7, 0x6b, \
+ 0x6f, 0xd0, 0xbe, 0x52, 0x8c, 0xda)
/*
* Guest File Copy Service
@@ -1122,20 +1114,25 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj,
*/
#define HV_FCOPY_GUID \
- .guid = { \
- 0xE3, 0x4B, 0xD1, 0x34, 0xE4, 0xDE, 0xC8, 0x41, \
- 0x9A, 0xE7, 0x6B, 0x17, 0x49, 0x77, 0xC1, 0x92 \
- }
+ .guid = UUID_LE(0x34d14be3, 0xdee4, 0x41c8, 0x9a, 0xe7, \
+ 0x6b, 0x17, 0x49, 0x77, 0xc1, 0x92)
/*
* NetworkDirect. This is the guest RDMA service.
* {8c2eaf3d-32a7-4b09-ab99-bd1f1c86b501}
*/
#define HV_ND_GUID \
- .guid = { \
- 0x3d, 0xaf, 0x2e, 0x8c, 0xa7, 0x32, 0x09, 0x4b, \
- 0xab, 0x99, 0xbd, 0x1f, 0x1c, 0x86, 0xb5, 0x01 \
- }
+ .guid = UUID_LE(0x8c2eaf3d, 0x32a7, 0x4b09, 0xab, 0x99, \
+ 0xbd, 0x1f, 0x1c, 0x86, 0xb5, 0x01)
+
+/*
+ * PCI Express Pass Through
+ * {44C4F61D-4444-4400-9D52-802E27EDE19F}
+ */
+
+#define HV_PCIE_GUID \
+ .guid = UUID_LE(0x44c4f61d, 0x4444, 0x4400, 0x9d, 0x52, \
+ 0x80, 0x2e, 0x27, 0xed, 0xe1, 0x9f)
/*
* Common header for Hyper-V ICs
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 768063baafbf..200cf13b00f6 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -414,6 +414,22 @@ struct i2c_algorithm {
};
/**
+ * struct i2c_timings - I2C timing information
+ * @bus_freq_hz: the bus frequency in Hz
+ * @scl_rise_ns: time SCL signal takes to rise in ns; t(r) in the I2C specification
+ * @scl_fall_ns: time SCL signal takes to fall in ns; t(f) in the I2C specification
+ * @scl_int_delay_ns: time IP core additionally needs to setup SCL in ns
+ * @sda_fall_ns: time SDA signal takes to fall in ns; t(f) in the I2C specification
+ */
+struct i2c_timings {
+ u32 bus_freq_hz;
+ u32 scl_rise_ns;
+ u32 scl_fall_ns;
+ u32 scl_int_delay_ns;
+ u32 sda_fall_ns;
+};
+
+/**
* struct i2c_bus_recovery_info - I2C bus recovery information
* @recover_bus: Recover routine. Either pass driver's recover_bus() routine, or
* i2c_generic_scl_recovery() or i2c_generic_gpio_recovery().
@@ -493,6 +509,8 @@ struct i2c_adapter_quirks {
/* convenience macro for typical write-then read case */
#define I2C_AQ_COMB_WRITE_THEN_READ (I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | \
I2C_AQ_COMB_READ_SECOND | I2C_AQ_COMB_SAME_ADDR)
+/* clock stretching is not supported */
+#define I2C_AQ_NO_CLK_STRETCH BIT(4)
/*
* i2c_adapter is the structure used to identify a physical i2c bus along
@@ -602,6 +620,7 @@ extern void i2c_clients_command(struct i2c_adapter *adap,
extern struct i2c_adapter *i2c_get_adapter(int nr);
extern void i2c_put_adapter(struct i2c_adapter *adap);
+void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_defaults);
/* Return the functionality mask */
static inline u32 i2c_get_functionality(struct i2c_adapter *adap)
@@ -615,6 +634,20 @@ static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func)
return (func & i2c_get_functionality(adap)) == func;
}
+/**
+ * i2c_check_quirks() - Function for checking the quirk flags in an i2c adapter
+ * @adap: i2c adapter
+ * @quirks: quirk flags
+ *
+ * Return: true if the adapter has all the specified quirk flags, false if not
+ */
+static inline bool i2c_check_quirks(struct i2c_adapter *adap, u64 quirks)
+{
+ if (!adap->quirks)
+ return false;
+ return (adap->quirks->flags & quirks) == quirks;
+}
+
/* Return the adapter number for a specific adapter */
static inline int i2c_adapter_id(struct i2c_adapter *adap)
{
@@ -622,7 +655,7 @@ static inline int i2c_adapter_id(struct i2c_adapter *adap)
}
/**
- * module_i2c_driver() - Helper macro for registering a I2C driver
+ * module_i2c_driver() - Helper macro for registering a modular I2C driver
* @__i2c_driver: i2c_driver struct
*
* Helper macro for I2C drivers which do not do anything special in module
@@ -633,6 +666,17 @@ static inline int i2c_adapter_id(struct i2c_adapter *adap)
module_driver(__i2c_driver, i2c_add_driver, \
i2c_del_driver)
+/**
+ * builtin_i2c_driver() - Helper macro for registering a builtin I2C driver
+ * @__i2c_driver: i2c_driver struct
+ *
+ * Helper macro for I2C drivers which do not do anything special in their
+ * init. This eliminates a lot of boilerplate. Each driver may only
+ * use this macro once, and calling it replaces device_initcall().
+ */
+#define builtin_i2c_driver(__i2c_driver) \
+ builtin_driver(__i2c_driver, i2c_add_driver)
+
#endif /* I2C */
#if IS_ENABLED(CONFIG_OF)
@@ -644,6 +688,7 @@ extern struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
/* must call i2c_put_adapter() when done with returned i2c_adapter device */
struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node);
+
#else
static inline struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h
index b49cf923becc..ba7a9b0c7c57 100644
--- a/include/linux/if_pppox.h
+++ b/include/linux/if_pppox.h
@@ -91,7 +91,6 @@ enum {
PPPOX_CONNECTED = 1, /* connection established ==TCP_ESTABLISHED */
PPPOX_BOUND = 2, /* bound to ppp device */
PPPOX_RELAY = 4, /* forwarding is enabled */
- PPPOX_ZOMBIE = 8, /* dead, but still bound to ppp device */
PPPOX_DEAD = 16 /* dead, useless, please clean me up!*/
};
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
index a6aa970758a2..b84e49c3a738 100644
--- a/include/linux/if_team.h
+++ b/include/linux/if_team.h
@@ -164,6 +164,7 @@ struct team_mode {
size_t priv_size;
size_t port_priv_size;
const struct team_mode_ops *ops;
+ enum netdev_lag_tx_type lag_tx_type;
};
#define TEAM_PORT_HASHBITS 4
diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h
index 67ce5bd3b56a..a5f6ce6b578c 100644
--- a/include/linux/if_vlan.h
+++ b/include/linux/if_vlan.h
@@ -73,7 +73,7 @@ static inline struct vlan_ethhdr *vlan_eth_hdr(const struct sk_buff *skb)
/* found in socket.c */
extern void vlan_ioctl_set(int (*hook)(struct net *, void __user *));
-static inline bool is_vlan_dev(struct net_device *dev)
+static inline bool is_vlan_dev(const struct net_device *dev)
{
return dev->priv_flags & IFF_802_1Q_VLAN;
}
@@ -621,7 +621,7 @@ static inline netdev_features_t vlan_features_check(const struct sk_buff *skb,
NETIF_F_SG |
NETIF_F_HIGHDMA |
NETIF_F_FRAGLIST |
- NETIF_F_GEN_CSUM |
+ NETIF_F_HW_CSUM |
NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_STAG_TX);
diff --git a/include/linux/iio/buffer-dma.h b/include/linux/iio/buffer-dma.h
new file mode 100644
index 000000000000..767467d886de
--- /dev/null
+++ b/include/linux/iio/buffer-dma.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2013-2015 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef __INDUSTRIALIO_DMA_BUFFER_H__
+#define __INDUSTRIALIO_DMA_BUFFER_H__
+
+#include <linux/list.h>
+#include <linux/kref.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/iio/buffer.h>
+
+struct iio_dma_buffer_queue;
+struct iio_dma_buffer_ops;
+struct device;
+
+struct iio_buffer_block {
+ u32 size;
+ u32 bytes_used;
+};
+
+/**
+ * enum iio_block_state - State of a struct iio_dma_buffer_block
+ * @IIO_BLOCK_STATE_DEQUEUED: Block is not queued
+ * @IIO_BLOCK_STATE_QUEUED: Block is on the incoming queue
+ * @IIO_BLOCK_STATE_ACTIVE: Block is currently being processed by the DMA
+ * @IIO_BLOCK_STATE_DONE: Block is on the outgoing queue
+ * @IIO_BLOCK_STATE_DEAD: Block has been marked as to be freed
+ */
+enum iio_block_state {
+ IIO_BLOCK_STATE_DEQUEUED,
+ IIO_BLOCK_STATE_QUEUED,
+ IIO_BLOCK_STATE_ACTIVE,
+ IIO_BLOCK_STATE_DONE,
+ IIO_BLOCK_STATE_DEAD,
+};
+
+/**
+ * struct iio_dma_buffer_block - IIO buffer block
+ * @head: List head
+ * @size: Total size of the block in bytes
+ * @bytes_used: Number of bytes that contain valid data
+ * @vaddr: Virutal address of the blocks memory
+ * @phys_addr: Physical address of the blocks memory
+ * @queue: Parent DMA buffer queue
+ * @kref: kref used to manage the lifetime of block
+ * @state: Current state of the block
+ */
+struct iio_dma_buffer_block {
+ /* May only be accessed by the owner of the block */
+ struct list_head head;
+ size_t bytes_used;
+
+ /*
+ * Set during allocation, constant thereafter. May be accessed read-only
+ * by anybody holding a reference to the block.
+ */
+ void *vaddr;
+ dma_addr_t phys_addr;
+ size_t size;
+ struct iio_dma_buffer_queue *queue;
+
+ /* Must not be accessed outside the core. */
+ struct kref kref;
+ /*
+ * Must not be accessed outside the core. Access needs to hold
+ * queue->list_lock if the block is not owned by the core.
+ */
+ enum iio_block_state state;
+};
+
+/**
+ * struct iio_dma_buffer_queue_fileio - FileIO state for the DMA buffer
+ * @blocks: Buffer blocks used for fileio
+ * @active_block: Block being used in read()
+ * @pos: Read offset in the active block
+ * @block_size: Size of each block
+ */
+struct iio_dma_buffer_queue_fileio {
+ struct iio_dma_buffer_block *blocks[2];
+ struct iio_dma_buffer_block *active_block;
+ size_t pos;
+ size_t block_size;
+};
+
+/**
+ * struct iio_dma_buffer_queue - DMA buffer base structure
+ * @buffer: IIO buffer base structure
+ * @dev: Parent device
+ * @ops: DMA buffer callbacks
+ * @lock: Protects the incoming list, active and the fields in the fileio
+ * substruct
+ * @list_lock: Protects lists that contain blocks which can be modified in
+ * atomic context as well as blocks on those lists. This is the outgoing queue
+ * list and typically also a list of active blocks in the part that handles
+ * the DMA controller
+ * @incoming: List of buffers on the incoming queue
+ * @outgoing: List of buffers on the outgoing queue
+ * @active: Whether the buffer is currently active
+ * @fileio: FileIO state
+ */
+struct iio_dma_buffer_queue {
+ struct iio_buffer buffer;
+ struct device *dev;
+ const struct iio_dma_buffer_ops *ops;
+
+ struct mutex lock;
+ spinlock_t list_lock;
+ struct list_head incoming;
+ struct list_head outgoing;
+
+ bool active;
+
+ struct iio_dma_buffer_queue_fileio fileio;
+};
+
+/**
+ * struct iio_dma_buffer_ops - DMA buffer callback operations
+ * @submit: Called when a block is submitted to the DMA controller
+ * @abort: Should abort all pending transfers
+ */
+struct iio_dma_buffer_ops {
+ int (*submit)(struct iio_dma_buffer_queue *queue,
+ struct iio_dma_buffer_block *block);
+ void (*abort)(struct iio_dma_buffer_queue *queue);
+};
+
+void iio_dma_buffer_block_done(struct iio_dma_buffer_block *block);
+void iio_dma_buffer_block_list_abort(struct iio_dma_buffer_queue *queue,
+ struct list_head *list);
+
+int iio_dma_buffer_enable(struct iio_buffer *buffer,
+ struct iio_dev *indio_dev);
+int iio_dma_buffer_disable(struct iio_buffer *buffer,
+ struct iio_dev *indio_dev);
+int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
+ char __user *user_buffer);
+size_t iio_dma_buffer_data_available(struct iio_buffer *buffer);
+int iio_dma_buffer_set_bytes_per_datum(struct iio_buffer *buffer, size_t bpd);
+int iio_dma_buffer_set_length(struct iio_buffer *buffer, int length);
+int iio_dma_buffer_request_update(struct iio_buffer *buffer);
+
+int iio_dma_buffer_init(struct iio_dma_buffer_queue *queue,
+ struct device *dma_dev, const struct iio_dma_buffer_ops *ops);
+void iio_dma_buffer_exit(struct iio_dma_buffer_queue *queue);
+void iio_dma_buffer_release(struct iio_dma_buffer_queue *queue);
+
+#endif
diff --git a/include/linux/iio/buffer-dmaengine.h b/include/linux/iio/buffer-dmaengine.h
new file mode 100644
index 000000000000..5dcddf427bb0
--- /dev/null
+++ b/include/linux/iio/buffer-dmaengine.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2014-2015 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __IIO_DMAENGINE_H__
+#define __IIO_DMAENGINE_H__
+
+struct iio_buffer;
+struct device;
+
+struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
+ const char *channel);
+void iio_dmaengine_buffer_free(struct iio_buffer *buffer);
+
+#endif
diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h
index 1600c55828e0..2ec3ad58e8a0 100644
--- a/include/linux/iio/buffer.h
+++ b/include/linux/iio/buffer.h
@@ -18,6 +18,12 @@
struct iio_buffer;
/**
+ * INDIO_BUFFER_FLAG_FIXED_WATERMARK - Watermark level of the buffer can not be
+ * configured. It has a fixed value which will be buffer specific.
+ */
+#define INDIO_BUFFER_FLAG_FIXED_WATERMARK BIT(0)
+
+/**
* struct iio_buffer_access_funcs - access functions for buffers.
* @store_to: actually store stuff to the buffer
* @read_first_n: try to get a specified number of bytes (must exist)
@@ -27,9 +33,15 @@ struct iio_buffer;
* storage.
* @set_bytes_per_datum:set number of bytes per datum
* @set_length: set number of datums in buffer
+ * @enable: called if the buffer is attached to a device and the
+ * device starts sampling. Calls are balanced with
+ * @disable.
+ * @disable: called if the buffer is attached to a device and the
+ * device stops sampling. Calles are balanced with @enable.
* @release: called when the last reference to the buffer is dropped,
* should free all resources allocated by the buffer.
* @modes: Supported operating modes by this buffer type
+ * @flags: A bitmask combination of INDIO_BUFFER_FLAG_*
*
* The purpose of this structure is to make the buffer element
* modular as event for a given driver, different usecases may require
@@ -51,9 +63,13 @@ struct iio_buffer_access_funcs {
int (*set_bytes_per_datum)(struct iio_buffer *buffer, size_t bpd);
int (*set_length)(struct iio_buffer *buffer, int length);
+ int (*enable)(struct iio_buffer *buffer, struct iio_dev *indio_dev);
+ int (*disable)(struct iio_buffer *buffer, struct iio_dev *indio_dev);
+
void (*release)(struct iio_buffer *buffer);
unsigned int modes;
+ unsigned int flags;
};
/**
diff --git a/include/linux/iio/configfs.h b/include/linux/iio/configfs.h
new file mode 100644
index 000000000000..93befd67c15c
--- /dev/null
+++ b/include/linux/iio/configfs.h
@@ -0,0 +1,15 @@
+/*
+ * Industrial I/O configfs support
+ *
+ * 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 version 2 as published by
+ * the Free Software Foundation.
+ */
+#ifndef __IIO_CONFIGFS
+#define __IIO_CONFIGFS
+
+extern struct configfs_subsystem iio_configfs_subsys;
+
+#endif /* __IIO_CONFIGFS */
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index 19c94c9acc81..b5894118755f 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -636,6 +636,8 @@ static inline struct dentry *iio_get_debugfs_dentry(struct iio_dev *indio_dev)
}
#endif
+ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals);
+
int iio_str_to_fixpoint(const char *str, int fract_mult, int *integer,
int *fract);
diff --git a/include/linux/iio/sw_trigger.h b/include/linux/iio/sw_trigger.h
new file mode 100644
index 000000000000..5198f8ed08a4
--- /dev/null
+++ b/include/linux/iio/sw_trigger.h
@@ -0,0 +1,70 @@
+/*
+ * Industrial I/O software trigger interface
+ *
+ * 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 version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef __IIO_SW_TRIGGER
+#define __IIO_SW_TRIGGER
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/iio/iio.h>
+#include <linux/configfs.h>
+
+#define module_iio_sw_trigger_driver(__iio_sw_trigger_type) \
+ module_driver(__iio_sw_trigger_type, iio_register_sw_trigger_type, \
+ iio_unregister_sw_trigger_type)
+
+struct iio_sw_trigger_ops;
+
+struct iio_sw_trigger_type {
+ const char *name;
+ struct module *owner;
+ const struct iio_sw_trigger_ops *ops;
+ struct list_head list;
+ struct config_group *group;
+};
+
+struct iio_sw_trigger {
+ struct iio_trigger *trigger;
+ struct iio_sw_trigger_type *trigger_type;
+ struct config_group group;
+};
+
+struct iio_sw_trigger_ops {
+ struct iio_sw_trigger* (*probe)(const char *);
+ int (*remove)(struct iio_sw_trigger *);
+};
+
+static inline
+struct iio_sw_trigger *to_iio_sw_trigger(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct iio_sw_trigger,
+ group);
+}
+
+int iio_register_sw_trigger_type(struct iio_sw_trigger_type *tt);
+void iio_unregister_sw_trigger_type(struct iio_sw_trigger_type *tt);
+
+struct iio_sw_trigger *iio_sw_trigger_create(const char *, const char *);
+void iio_sw_trigger_destroy(struct iio_sw_trigger *);
+
+int iio_sw_trigger_type_configfs_register(struct iio_sw_trigger_type *tt);
+void iio_sw_trigger_type_configfs_unregister(struct iio_sw_trigger_type *tt);
+
+static inline
+void iio_swt_group_init_type_name(struct iio_sw_trigger *t,
+ const char *name,
+ struct config_item_type *type)
+{
+#ifdef CONFIG_CONFIGFS_FS
+ config_group_init_type_name(&t->group, name, type);
+#endif
+}
+
+#endif /* __IIO_SW_TRIGGER */
diff --git a/include/linux/inet_diag.h b/include/linux/inet_diag.h
index 0e707f0c1a3e..7c27fa1030e8 100644
--- a/include/linux/inet_diag.h
+++ b/include/linux/inet_diag.h
@@ -3,6 +3,7 @@
#include <uapi/linux/inet_diag.h>
+struct net;
struct sock;
struct inet_hashinfo;
struct nlattr;
@@ -23,6 +24,10 @@ struct inet_diag_handler {
void (*idiag_get_info)(struct sock *sk,
struct inet_diag_msg *r,
void *info);
+
+ int (*destroy)(struct sk_buff *in_skb,
+ const struct inet_diag_req_v2 *req);
+
__u16 idiag_type;
__u16 idiag_info_size;
};
@@ -41,6 +46,10 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo,
struct sk_buff *in_skb, const struct nlmsghdr *nlh,
const struct inet_diag_req_v2 *req);
+struct sock *inet_diag_find_one_icsk(struct net *net,
+ struct inet_hashinfo *hashinfo,
+ const struct inet_diag_req_v2 *req);
+
int inet_diag_bc_sk(const struct nlattr *_bc, struct sock *sk);
extern int inet_diag_register(const struct inet_diag_handler *handler);
diff --git a/include/linux/init_task.h b/include/linux/init_task.h
index 1c1ff7e4faa4..f2cb8d45513d 100644
--- a/include/linux/init_task.h
+++ b/include/linux/init_task.h
@@ -150,7 +150,7 @@ extern struct task_group root_task_group;
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
# define INIT_VTIME(tsk) \
- .vtime_seqlock = __SEQLOCK_UNLOCKED(tsk.vtime_seqlock), \
+ .vtime_seqcount = SEQCNT_ZERO(tsk.vtime_seqcount), \
.vtime_snap = 0, \
.vtime_snap_whence = VTIME_SYS,
#else
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index ad16809c8596..cb30edbfe9fc 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -195,6 +195,7 @@ extern void disable_irq(unsigned int irq);
extern void disable_percpu_irq(unsigned int irq);
extern void enable_irq(unsigned int irq);
extern void enable_percpu_irq(unsigned int irq, unsigned int type);
+extern bool irq_percpu_is_enabled(unsigned int irq);
extern void irq_wake_thread(unsigned int irq, void *dev_id);
/* The following three functions are for the core kernel use only. */
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 0ef2a97ccdb5..402753bccafa 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -227,7 +227,7 @@ struct ipv6_pinfo {
struct ipv6_ac_socklist *ipv6_ac_list;
struct ipv6_fl_socklist __rcu *ipv6_fl_list;
- struct ipv6_txoptions *opt;
+ struct ipv6_txoptions __rcu *opt;
struct sk_buff *pktoptions;
struct sk_buff *rxpmtu;
struct inet6_cork cork;
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index c9ae0c6ec050..d5d798b35c1f 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -330,6 +330,7 @@ struct rdists {
};
struct irq_domain;
+struct device_node;
int its_cpu_init(void);
int its_init(struct device_node *node, struct rdists *rdists,
struct irq_domain *domain);
diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h
index bae69e5d693c..9c940263ca23 100644
--- a/include/linux/irqchip/arm-gic.h
+++ b/include/linux/irqchip/arm-gic.h
@@ -103,10 +103,21 @@ struct device_node;
void gic_cascade_irq(unsigned int gic_nr, unsigned int irq);
int gic_cpu_if_down(unsigned int gic_nr);
+/*
+ * Subdrivers that need some preparatory work can initialize their
+ * chips and call this to register their GICs.
+ */
+int gic_of_init(struct device_node *node, struct device_node *parent);
+
+/*
+ * Legacy platforms not converted to DT yet must use this to init
+ * their GIC
+ */
void gic_init(unsigned int nr, int start,
void __iomem *dist , void __iomem *cpu);
-int gicv2m_of_init(struct device_node *node, struct irq_domain *parent);
+int gicv2m_init(struct fwnode_handle *parent_handle,
+ struct irq_domain *parent);
void gic_send_sgi(unsigned int cpu_id, unsigned int irq);
int gic_get_cpu_id(unsigned int cpu);
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index a587a33363c7..dcca77c4b9d2 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -1,6 +1,8 @@
#ifndef _LINUX_IRQDESC_H
#define _LINUX_IRQDESC_H
+#include <linux/rcupdate.h>
+
/*
* Core internal functions to deal with irq descriptors
*/
@@ -40,6 +42,7 @@ struct pt_regs;
* IRQF_NO_SUSPEND set
* @force_resume_depth: number of irqactions on a irq descriptor with
* IRQF_FORCE_RESUME set
+ * @rcu: rcu head for delayed free
* @dir: /proc/irq/ procfs entry
* @name: flow handler name for /proc/interrupts output
*/
@@ -82,6 +85,9 @@ struct irq_desc {
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
+#ifdef CONFIG_SPARSE_IRQ
+ struct rcu_head rcu;
+#endif
int parent_irq;
struct module *owner;
const char *name;
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index d5e5c5bef28c..f64622ad02c1 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -211,6 +211,11 @@ static inline struct fwnode_handle *of_node_to_fwnode(struct device_node *node)
return node ? &node->fwnode : NULL;
}
+static inline bool is_fwnode_irqchip(struct fwnode_handle *fwnode)
+{
+ return fwnode && fwnode->type == FWNODE_IRQCHIP;
+}
+
static inline struct irq_domain *irq_find_matching_host(struct device_node *node,
enum irq_domain_bus_token bus_token)
{
@@ -367,6 +372,9 @@ static inline int irq_domain_alloc_irqs(struct irq_domain *domain,
return __irq_domain_alloc_irqs(domain, -1, nr_irqs, node, arg, false);
}
+extern int irq_domain_alloc_irqs_recursive(struct irq_domain *domain,
+ unsigned int irq_base,
+ unsigned int nr_irqs, void *arg);
extern int irq_domain_set_hwirq_and_chip(struct irq_domain *domain,
unsigned int virq,
irq_hw_number_t hwirq,
@@ -410,6 +418,11 @@ static inline bool irq_domain_is_hierarchy(struct irq_domain *domain)
static inline void irq_dispose_mapping(unsigned int virq) { }
static inline void irq_domain_activate_irq(struct irq_data *data) { }
static inline void irq_domain_deactivate_irq(struct irq_data *data) { }
+static inline struct irq_domain *irq_find_matching_fwnode(
+ struct fwnode_handle *fwnode, enum irq_domain_bus_token bus_token)
+{
+ return NULL;
+}
#endif /* !CONFIG_IRQ_DOMAIN */
#endif /* _LINUX_IRQDOMAIN_H */
diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h
index 8dde55974f18..0536524bb9eb 100644
--- a/include/linux/jump_label.h
+++ b/include/linux/jump_label.h
@@ -5,7 +5,7 @@
* Jump label support
*
* Copyright (C) 2009-2012 Jason Baron <jbaron@redhat.com>
- * Copyright (C) 2011-2012 Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2011-2012 Red Hat, Inc., Peter Zijlstra
*
* DEPRECATED API:
*
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 350dfb08aee3..7311c3294e25 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -255,6 +255,7 @@ extern long (*panic_blink)(int state);
__printf(1, 2)
void panic(const char *fmt, ...)
__noreturn __cold;
+void nmi_panic_self_stop(struct pt_regs *);
extern void oops_enter(void);
extern void oops_exit(void);
void print_oops_end_marker(void);
@@ -446,6 +447,33 @@ extern int sysctl_panic_on_stackoverflow;
extern bool crash_kexec_post_notifiers;
/*
+ * panic_cpu is used for synchronizing panic() and crash_kexec() execution. It
+ * holds a CPU number which is executing panic() currently. A value of
+ * PANIC_CPU_INVALID means no CPU has entered panic() or crash_kexec().
+ */
+extern atomic_t panic_cpu;
+#define PANIC_CPU_INVALID -1
+
+/*
+ * A variant of panic() called from NMI context. We return if we've already
+ * panicked on this CPU. If another CPU already panicked, loop in
+ * nmi_panic_self_stop() which can provide architecture dependent code such
+ * as saving register state for crash dump.
+ */
+#define nmi_panic(regs, fmt, ...) \
+do { \
+ int old_cpu, cpu; \
+ \
+ cpu = raw_smp_processor_id(); \
+ old_cpu = atomic_cmpxchg(&panic_cpu, PANIC_CPU_INVALID, cpu); \
+ \
+ if (old_cpu == PANIC_CPU_INVALID) \
+ panic(fmt, ##__VA_ARGS__); \
+ else if (old_cpu != cpu) \
+ nmi_panic_self_stop(regs); \
+} while (0)
+
+/*
* Only to be used by arch init code. If the user over-wrote the default
* CONFIG_PANIC_TIMEOUT, honor it.
*/
diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h
index 5d4e9c4b821d..af51df35d749 100644
--- a/include/linux/kernfs.h
+++ b/include/linux/kernfs.h
@@ -274,6 +274,8 @@ void pr_cont_kernfs_path(struct kernfs_node *kn);
struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn);
struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent,
const char *name, const void *ns);
+struct kernfs_node *kernfs_walk_and_get_ns(struct kernfs_node *parent,
+ const char *path, const void *ns);
void kernfs_get(struct kernfs_node *kn);
void kernfs_put(struct kernfs_node *kn);
@@ -350,6 +352,10 @@ static inline struct kernfs_node *
kernfs_find_and_get_ns(struct kernfs_node *parent, const char *name,
const void *ns)
{ return NULL; }
+static inline struct kernfs_node *
+kernfs_walk_and_get_ns(struct kernfs_node *parent, const char *path,
+ const void *ns)
+{ return NULL; }
static inline void kernfs_get(struct kernfs_node *kn) { }
static inline void kernfs_put(struct kernfs_node *kn) { }
@@ -431,6 +437,12 @@ kernfs_find_and_get(struct kernfs_node *kn, const char *name)
}
static inline struct kernfs_node *
+kernfs_walk_and_get(struct kernfs_node *kn, const char *path)
+{
+ return kernfs_walk_and_get_ns(kn, path, NULL);
+}
+
+static inline struct kernfs_node *
kernfs_create_dir(struct kernfs_node *parent, const char *name, umode_t mode,
void *priv)
{
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index d140b1e9faa7..7b68d2788a56 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -237,6 +237,7 @@ extern int kexec_purgatory_get_set_symbol(struct kimage *image,
unsigned int size, bool get_value);
extern void *kexec_purgatory_get_symbol_addr(struct kimage *image,
const char *name);
+extern void __crash_kexec(struct pt_regs *);
extern void crash_kexec(struct pt_regs *);
int kexec_should_crash(struct task_struct *);
void crash_save_cpu(struct pt_regs *regs, int cpu);
@@ -332,6 +333,7 @@ int __weak arch_kexec_apply_relocations(const Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
#else /* !CONFIG_KEXEC_CORE */
struct pt_regs;
struct task_struct;
+static inline void __crash_kexec(struct pt_regs *regs) { }
static inline void crash_kexec(struct pt_regs *regs) { }
static inline int kexec_should_crash(struct task_struct *p) { return 0; }
#define kexec_in_progress false
diff --git a/include/linux/kmemleak.h b/include/linux/kmemleak.h
index d0a1f99e24e3..4894c6888bc6 100644
--- a/include/linux/kmemleak.h
+++ b/include/linux/kmemleak.h
@@ -25,7 +25,7 @@
#ifdef CONFIG_DEBUG_KMEMLEAK
-extern void kmemleak_init(void) __ref;
+extern void kmemleak_init(void) __init;
extern void kmemleak_alloc(const void *ptr, size_t size, int min_count,
gfp_t gfp) __ref;
extern void kmemleak_alloc_percpu(const void __percpu *ptr, size_t size,
diff --git a/include/linux/kref.h b/include/linux/kref.h
index 484604d184be..e15828fd71f1 100644
--- a/include/linux/kref.h
+++ b/include/linux/kref.h
@@ -19,7 +19,6 @@
#include <linux/atomic.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
-#include <linux/spinlock.h>
struct kref {
atomic_t refcount;
@@ -99,38 +98,6 @@ static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref)
return kref_sub(kref, 1, release);
}
-/**
- * kref_put_spinlock_irqsave - decrement refcount for object.
- * @kref: object.
- * @release: pointer to the function that will clean up the object when the
- * last reference to the object is released.
- * This pointer is required, and it is not acceptable to pass kfree
- * in as this function.
- * @lock: lock to take in release case
- *
- * Behaves identical to kref_put with one exception. If the reference count
- * drops to zero, the lock will be taken atomically wrt dropping the reference
- * count. The release function has to call spin_unlock() without _irqrestore.
- */
-static inline int kref_put_spinlock_irqsave(struct kref *kref,
- void (*release)(struct kref *kref),
- spinlock_t *lock)
-{
- unsigned long flags;
-
- WARN_ON(release == NULL);
- if (atomic_add_unless(&kref->refcount, -1, 1))
- return 0;
- spin_lock_irqsave(lock, flags);
- if (atomic_dec_and_test(&kref->refcount)) {
- release(kref);
- local_irq_restore(flags);
- return 1;
- }
- spin_unlock_irqrestore(lock, flags);
- return 0;
-}
-
static inline int kref_put_mutex(struct kref *kref,
void (*release)(struct kref *kref),
struct mutex *lock)
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 5706a2108f0a..f707f74055c3 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -111,38 +111,13 @@ static inline bool is_error_page(struct page *page)
}
/*
- * vcpu->requests bit members
+ * Architecture-independent vcpu->requests bit members
+ * Bits 4-7 are reserved for more arch-independent bits.
*/
#define KVM_REQ_TLB_FLUSH 0
-#define KVM_REQ_MIGRATE_TIMER 1
-#define KVM_REQ_REPORT_TPR_ACCESS 2
-#define KVM_REQ_MMU_RELOAD 3
-#define KVM_REQ_TRIPLE_FAULT 4
-#define KVM_REQ_PENDING_TIMER 5
-#define KVM_REQ_UNHALT 6
-#define KVM_REQ_MMU_SYNC 7
-#define KVM_REQ_CLOCK_UPDATE 8
-#define KVM_REQ_KICK 9
-#define KVM_REQ_DEACTIVATE_FPU 10
-#define KVM_REQ_EVENT 11
-#define KVM_REQ_APF_HALT 12
-#define KVM_REQ_STEAL_UPDATE 13
-#define KVM_REQ_NMI 14
-#define KVM_REQ_PMU 15
-#define KVM_REQ_PMI 16
-#define KVM_REQ_WATCHDOG 17
-#define KVM_REQ_MASTERCLOCK_UPDATE 18
-#define KVM_REQ_MCLOCK_INPROGRESS 19
-#define KVM_REQ_EPR_EXIT 20
-#define KVM_REQ_SCAN_IOAPIC 21
-#define KVM_REQ_GLOBAL_CLOCK_UPDATE 22
-#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_REQ_HV_CRASH 27
-#define KVM_REQ_IOAPIC_EOI_EXIT 28
-#define KVM_REQ_HV_RESET 29
+#define KVM_REQ_MMU_RELOAD 1
+#define KVM_REQ_PENDING_TIMER 2
+#define KVM_REQ_UNHALT 3
#define KVM_USERSPACE_IRQ_SOURCE_ID 0
#define KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID 1
@@ -318,6 +293,11 @@ struct kvm_s390_adapter_int {
u32 adapter_id;
};
+struct kvm_hv_sint {
+ u32 vcpu;
+ u32 sint;
+};
+
struct kvm_kernel_irq_routing_entry {
u32 gsi;
u32 type;
@@ -331,6 +311,7 @@ struct kvm_kernel_irq_routing_entry {
} irqchip;
struct msi_msg msi;
struct kvm_s390_adapter_int adapter;
+ struct kvm_hv_sint hv_sint;
};
struct hlist_node link;
};
@@ -439,10 +420,13 @@ struct kvm {
/* The guest did something we don't support. */
#define vcpu_unimpl(vcpu, fmt, ...) \
- kvm_pr_unimpl("vcpu%i " fmt, (vcpu)->vcpu_id, ## __VA_ARGS__)
+ kvm_pr_unimpl("vcpu%i, guest rIP: 0x%lx " fmt, \
+ (vcpu)->vcpu_id, kvm_rip_read(vcpu), ## __VA_ARGS__)
#define vcpu_debug(vcpu, fmt, ...) \
kvm_debug("vcpu%i " fmt, (vcpu)->vcpu_id, ## __VA_ARGS__)
+#define vcpu_err(vcpu, fmt, ...) \
+ kvm_err("vcpu%i " fmt, (vcpu)->vcpu_id, ## __VA_ARGS__)
static inline struct kvm_vcpu *kvm_get_vcpu(struct kvm *kvm, int i)
{
@@ -460,6 +444,22 @@ static inline struct kvm_vcpu *kvm_get_vcpu(struct kvm *kvm, int i)
(vcpup = kvm_get_vcpu(kvm, idx)) != NULL; \
idx++)
+static inline struct kvm_vcpu *kvm_get_vcpu_by_id(struct kvm *kvm, int id)
+{
+ struct kvm_vcpu *vcpu;
+ int i;
+
+ if (id < 0 || id >= KVM_MAX_VCPUS)
+ return NULL;
+ vcpu = kvm_get_vcpu(kvm, id);
+ if (vcpu && vcpu->vcpu_id == id)
+ return vcpu;
+ kvm_for_each_vcpu(i, vcpu, kvm)
+ if (vcpu->vcpu_id == id)
+ return vcpu;
+ return NULL;
+}
+
#define kvm_for_each_memslot(memslot, slots) \
for (memslot = &slots->memslots[0]; \
memslot < slots->memslots + KVM_MEM_SLOTS_NUM && memslot->npages;\
@@ -473,12 +473,12 @@ void vcpu_put(struct kvm_vcpu *vcpu);
#ifdef __KVM_HAVE_IOAPIC
void kvm_vcpu_request_scan_ioapic(struct kvm *kvm);
-void kvm_arch_irq_routing_update(struct kvm *kvm);
+void kvm_arch_post_irq_routing_update(struct kvm *kvm);
#else
static inline void kvm_vcpu_request_scan_ioapic(struct kvm *kvm)
{
}
-static inline void kvm_arch_irq_routing_update(struct kvm *kvm)
+static inline void kvm_arch_post_irq_routing_update(struct kvm *kvm)
{
}
#endif
@@ -623,7 +623,7 @@ int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc,
int kvm_clear_guest_page(struct kvm *kvm, gfn_t gfn, int offset, int len);
int kvm_clear_guest(struct kvm *kvm, gpa_t gpa, unsigned long len);
struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn);
-int kvm_is_visible_gfn(struct kvm *kvm, gfn_t gfn);
+bool 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);
@@ -657,8 +657,6 @@ void kvm_put_guest_fpu(struct kvm_vcpu *vcpu);
void kvm_flush_remote_tlbs(struct kvm *kvm);
void kvm_reload_remote_mmus(struct kvm *kvm);
-void kvm_make_mclock_inprogress_request(struct kvm *kvm);
-void kvm_make_scan_ioapic_request(struct kvm *kvm);
bool kvm_make_all_cpus_request(struct kvm *kvm, unsigned int req);
long kvm_arch_dev_ioctl(struct file *filp,
@@ -979,11 +977,6 @@ static inline bool kvm_is_error_gpa(struct kvm *kvm, gpa_t gpa)
return kvm_is_error_hva(hva);
}
-static inline void kvm_migrate_timers(struct kvm_vcpu *vcpu)
-{
- set_bit(KVM_REQ_MIGRATE_TIMER, &vcpu->requests);
-}
-
enum kvm_stat_kind {
KVM_STAT_VM,
KVM_STAT_VCPU,
@@ -993,7 +986,6 @@ struct kvm_stats_debugfs_item {
const char *name;
int offset;
enum kvm_stat_kind kind;
- struct dentry *dentry;
};
extern struct kvm_stats_debugfs_item debugfs_entries[];
extern struct dentry *kvm_debugfs_dir;
@@ -1080,6 +1072,7 @@ static inline void kvm_irq_routing_update(struct kvm *kvm)
{
}
#endif
+void kvm_arch_irq_routing_update(struct kvm *kvm);
static inline int kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
{
diff --git a/include/linux/kvm_para.h b/include/linux/kvm_para.h
index 00a97bb905db..35e568f04b1e 100644
--- a/include/linux/kvm_para.h
+++ b/include/linux/kvm_para.h
@@ -4,10 +4,8 @@
#include <uapi/linux/kvm_para.h>
-static inline int kvm_para_has_feature(unsigned int feature)
+static inline bool kvm_para_has_feature(unsigned int feature)
{
- if (kvm_arch_para_features() & (1UL << feature))
- return 1;
- return 0;
+ return !!(kvm_arch_para_features() & (1UL << feature));
}
#endif /* __LINUX_KVM_PARA_H */
diff --git a/include/linux/leds.h b/include/linux/leds.h
index fa359c79c825..bc1476fda96e 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -44,9 +44,9 @@ struct led_classdev {
#define LED_BLINK_ONESHOT (1 << 17)
#define LED_BLINK_ONESHOT_STOP (1 << 18)
#define LED_BLINK_INVERT (1 << 19)
-#define LED_SYSFS_DISABLE (1 << 20)
-#define SET_BRIGHTNESS_ASYNC (1 << 21)
-#define SET_BRIGHTNESS_SYNC (1 << 22)
+#define LED_BLINK_BRIGHTNESS_CHANGE (1 << 20)
+#define LED_BLINK_DISABLE (1 << 21)
+#define LED_SYSFS_DISABLE (1 << 22)
#define LED_DEV_CAP_FLASH (1 << 23)
/* Set LED brightness level */
@@ -57,8 +57,8 @@ struct led_classdev {
* Set LED brightness level immediately - it can block the caller for
* the time required for accessing a LED device register.
*/
- int (*brightness_set_sync)(struct led_classdev *led_cdev,
- enum led_brightness brightness);
+ int (*brightness_set_blocking)(struct led_classdev *led_cdev,
+ enum led_brightness brightness);
/* Get LED brightness level */
enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
@@ -156,10 +156,25 @@ extern void led_blink_set_oneshot(struct led_classdev *led_cdev,
*
* Set an LED's brightness, and, if necessary, cancel the
* software blink timer that implements blinking when the
- * hardware doesn't.
+ * hardware doesn't. This function is guaranteed not to sleep.
*/
extern void led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness brightness);
+
+/**
+ * led_set_brightness_sync - set LED brightness synchronously
+ * @led_cdev: the LED to set
+ * @brightness: the brightness to set it to
+ *
+ * Set an LED's brightness immediately. This function will block
+ * the caller for the time required for accessing device registers,
+ * and it can sleep.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+extern int led_set_brightness_sync(struct led_classdev *led_cdev,
+ enum led_brightness value);
+
/**
* led_update_brightness - update LED brightness
* @led_cdev: the LED to query
@@ -231,6 +246,8 @@ ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
/* Registration functions for complex triggers */
extern int led_trigger_register(struct led_trigger *trigger);
extern void led_trigger_unregister(struct led_trigger *trigger);
+extern int devm_led_trigger_register(struct device *dev,
+ struct led_trigger *trigger);
extern void led_trigger_register_simple(const char *name,
struct led_trigger **trigger);
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 83577f8fd15b..851821bfd553 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -205,11 +205,13 @@ enum {
ATA_LFLAG_NO_LPM = (1 << 8), /* disable LPM on this link */
ATA_LFLAG_RST_ONCE = (1 << 9), /* limit recovery to one reset */
ATA_LFLAG_CHANGED = (1 << 10), /* LPM state changed on this link */
+ ATA_LFLAG_NO_DB_DELAY = (1 << 11), /* no debounce delay on link resume */
/* struct ata_port flags */
ATA_FLAG_SLAVE_POSS = (1 << 0), /* host supports slave dev */
/* (doesn't imply presence) */
ATA_FLAG_SATA = (1 << 1),
+ ATA_FLAG_NO_LOG_PAGE = (1 << 5), /* do not issue log page read */
ATA_FLAG_NO_ATAPI = (1 << 6), /* No ATAPI support */
ATA_FLAG_PIO_DMA = (1 << 7), /* PIO cmds via DMA */
ATA_FLAG_PIO_LBA48 = (1 << 8), /* Host DMA engine is LBA28 only */
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
index 3f021dc5da8c..bed40dff0e86 100644
--- a/include/linux/libnvdimm.h
+++ b/include/linux/libnvdimm.h
@@ -116,6 +116,7 @@ static inline struct nd_blk_region_desc *to_blk_region_desc(
}
+int nvdimm_bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length);
struct nvdimm_bus *__nvdimm_bus_register(struct device *parent,
struct nvdimm_bus_descriptor *nfit_desc, struct module *module);
#define nvdimm_bus_register(parent, desc) \
diff --git a/include/linux/lightnvm.h b/include/linux/lightnvm.h
index 69c9057e1ab8..034117b3be5f 100644
--- a/include/linux/lightnvm.h
+++ b/include/linux/lightnvm.h
@@ -50,15 +50,21 @@ enum {
NVM_IO_DUAL_ACCESS = 0x1,
NVM_IO_QUAD_ACCESS = 0x2,
+ /* NAND Access Modes */
NVM_IO_SUSPEND = 0x80,
NVM_IO_SLC_MODE = 0x100,
NVM_IO_SCRAMBLE_DISABLE = 0x200,
+
+ /* Block Types */
+ NVM_BLK_T_FREE = 0x0,
+ NVM_BLK_T_BAD = 0x1,
+ NVM_BLK_T_DEV = 0x2,
+ NVM_BLK_T_HOST = 0x4,
};
struct nvm_id_group {
u8 mtype;
u8 fmtype;
- u16 res16;
u8 num_ch;
u8 num_lun;
u8 num_pln;
@@ -74,9 +80,9 @@ struct nvm_id_group {
u32 tbet;
u32 tbem;
u32 mpos;
+ u32 mccap;
u16 cpar;
- u8 res[913];
-} __packed;
+};
struct nvm_addr_format {
u8 ch_offset;
@@ -91,19 +97,15 @@ struct nvm_addr_format {
u8 pg_len;
u8 sect_offset;
u8 sect_len;
- u8 res[4];
};
struct nvm_id {
u8 ver_id;
u8 vmnt;
u8 cgrps;
- u8 res[5];
u32 cap;
u32 dom;
struct nvm_addr_format ppaf;
- u8 ppat;
- u8 resv[224];
struct nvm_id_group groups[4];
} __packed;
@@ -123,39 +125,28 @@ struct nvm_tgt_instance {
#define NVM_VERSION_MINOR 0
#define NVM_VERSION_PATCH 0
-#define NVM_SEC_BITS (8)
-#define NVM_PL_BITS (6)
-#define NVM_PG_BITS (16)
#define NVM_BLK_BITS (16)
-#define NVM_LUN_BITS (10)
+#define NVM_PG_BITS (16)
+#define NVM_SEC_BITS (8)
+#define NVM_PL_BITS (8)
+#define NVM_LUN_BITS (8)
#define NVM_CH_BITS (8)
struct ppa_addr {
+ /* Generic structure for all addresses */
union {
- /* Channel-based PPA format in nand 4x2x2x2x8x10 */
- struct {
- u64 ch : 4;
- u64 sec : 2; /* 4 sectors per page */
- u64 pl : 2; /* 4 planes per LUN */
- u64 lun : 2; /* 4 LUNs per channel */
- u64 pg : 8; /* 256 pages per block */
- u64 blk : 10;/* 1024 blocks per plane */
- u64 resved : 36;
- } chnl;
-
- /* Generic structure for all addresses */
struct {
+ u64 blk : NVM_BLK_BITS;
+ u64 pg : NVM_PG_BITS;
u64 sec : NVM_SEC_BITS;
u64 pl : NVM_PL_BITS;
- u64 pg : NVM_PG_BITS;
- u64 blk : NVM_BLK_BITS;
u64 lun : NVM_LUN_BITS;
u64 ch : NVM_CH_BITS;
} g;
u64 ppa;
};
-} __packed;
+};
struct nvm_rq {
struct nvm_tgt_instance *ins;
@@ -191,18 +182,18 @@ static inline void *nvm_rq_to_pdu(struct nvm_rq *rqdata)
struct nvm_block;
typedef int (nvm_l2p_update_fn)(u64, u32, __le64 *, void *);
-typedef int (nvm_bb_update_fn)(u32, void *, unsigned int, void *);
-typedef int (nvm_id_fn)(struct request_queue *, struct nvm_id *);
-typedef int (nvm_get_l2p_tbl_fn)(struct request_queue *, u64, u32,
+typedef int (nvm_bb_update_fn)(struct ppa_addr, int, u8 *, void *);
+typedef int (nvm_id_fn)(struct nvm_dev *, struct nvm_id *);
+typedef int (nvm_get_l2p_tbl_fn)(struct nvm_dev *, u64, u32,
nvm_l2p_update_fn *, void *);
-typedef int (nvm_op_bb_tbl_fn)(struct request_queue *, int, unsigned int,
+typedef int (nvm_op_bb_tbl_fn)(struct nvm_dev *, struct ppa_addr, int,
nvm_bb_update_fn *, void *);
-typedef int (nvm_op_set_bb_fn)(struct request_queue *, struct nvm_rq *, int);
-typedef int (nvm_submit_io_fn)(struct request_queue *, struct nvm_rq *);
-typedef int (nvm_erase_blk_fn)(struct request_queue *, struct nvm_rq *);
-typedef void *(nvm_create_dma_pool_fn)(struct request_queue *, char *);
+typedef int (nvm_op_set_bb_fn)(struct nvm_dev *, struct nvm_rq *, int);
+typedef int (nvm_submit_io_fn)(struct nvm_dev *, struct nvm_rq *);
+typedef int (nvm_erase_blk_fn)(struct nvm_dev *, struct nvm_rq *);
+typedef void *(nvm_create_dma_pool_fn)(struct nvm_dev *, char *);
typedef void (nvm_destroy_dma_pool_fn)(void *);
-typedef void *(nvm_dev_dma_alloc_fn)(struct request_queue *, void *, gfp_t,
+typedef void *(nvm_dev_dma_alloc_fn)(struct nvm_dev *, void *, gfp_t,
dma_addr_t *);
typedef void (nvm_dev_dma_free_fn)(void *, void*, dma_addr_t);
@@ -210,7 +201,7 @@ struct nvm_dev_ops {
nvm_id_fn *identity;
nvm_get_l2p_tbl_fn *get_l2p_tbl;
nvm_op_bb_tbl_fn *get_bb_tbl;
- nvm_op_set_bb_fn *set_bb;
+ nvm_op_set_bb_fn *set_bb_tbl;
nvm_submit_io_fn *submit_io;
nvm_erase_blk_fn *erase_block;
@@ -220,7 +211,7 @@ struct nvm_dev_ops {
nvm_dev_dma_alloc_fn *dev_dma_alloc;
nvm_dev_dma_free_fn *dev_dma_free;
- uint8_t max_phys_sect;
+ unsigned int max_phys_sect;
};
struct nvm_lun {
@@ -229,7 +220,9 @@ struct nvm_lun {
int lun_id;
int chnl_id;
+ unsigned int nr_inuse_blocks; /* Number of used blocks */
unsigned int nr_free_blocks; /* Number of unused blocks */
+ unsigned int nr_bad_blocks; /* Number of bad blocks */
struct nvm_block *blocks;
spinlock_t lock;
@@ -263,8 +256,7 @@ struct nvm_dev {
int blks_per_lun;
int sec_size;
int oob_size;
- int addr_mode;
- struct nvm_addr_format addr_format;
+ struct nvm_addr_format ppaf;
/* Calculated/Cached values. These do not reflect the actual usable
* blocks at run-time.
@@ -290,118 +282,45 @@ struct nvm_dev {
char name[DISK_NAME_LEN];
};
-/* fallback conversion */
-static struct ppa_addr __generic_to_linear_addr(struct nvm_dev *dev,
- struct ppa_addr r)
+static inline struct ppa_addr generic_to_dev_addr(struct nvm_dev *dev,
+ struct ppa_addr r)
{
struct ppa_addr l;
- l.ppa = r.g.sec +
- r.g.pg * dev->sec_per_pg +
- r.g.blk * (dev->pgs_per_blk *
- dev->sec_per_pg) +
- r.g.lun * (dev->blks_per_lun *
- dev->pgs_per_blk *
- dev->sec_per_pg) +
- r.g.ch * (dev->blks_per_lun *
- dev->pgs_per_blk *
- dev->luns_per_chnl *
- dev->sec_per_pg);
+ l.ppa = ((u64)r.g.blk) << dev->ppaf.blk_offset;
+ l.ppa |= ((u64)r.g.pg) << dev->ppaf.pg_offset;
+ l.ppa |= ((u64)r.g.sec) << dev->ppaf.sect_offset;
+ l.ppa |= ((u64)r.g.pl) << dev->ppaf.pln_offset;
+ l.ppa |= ((u64)r.g.lun) << dev->ppaf.lun_offset;
+ l.ppa |= ((u64)r.g.ch) << dev->ppaf.ch_offset;
return l;
}
-/* fallback conversion */
-static struct ppa_addr __linear_to_generic_addr(struct nvm_dev *dev,
- struct ppa_addr r)
+static inline struct ppa_addr dev_to_generic_addr(struct nvm_dev *dev,
+ struct ppa_addr r)
{
struct ppa_addr l;
- int secs, pgs, blks, luns;
- sector_t ppa = r.ppa;
-
- l.ppa = 0;
-
- div_u64_rem(ppa, dev->sec_per_pg, &secs);
- l.g.sec = secs;
- sector_div(ppa, dev->sec_per_pg);
- div_u64_rem(ppa, dev->sec_per_blk, &pgs);
- l.g.pg = pgs;
-
- sector_div(ppa, dev->pgs_per_blk);
- div_u64_rem(ppa, dev->blks_per_lun, &blks);
- l.g.blk = blks;
-
- sector_div(ppa, dev->blks_per_lun);
- div_u64_rem(ppa, dev->luns_per_chnl, &luns);
- l.g.lun = luns;
-
- sector_div(ppa, dev->luns_per_chnl);
- l.g.ch = ppa;
-
- return l;
-}
-
-static struct ppa_addr __generic_to_chnl_addr(struct ppa_addr r)
-{
- struct ppa_addr l;
-
- l.ppa = 0;
-
- l.chnl.sec = r.g.sec;
- l.chnl.pl = r.g.pl;
- l.chnl.pg = r.g.pg;
- l.chnl.blk = r.g.blk;
- l.chnl.lun = r.g.lun;
- l.chnl.ch = r.g.ch;
-
- return l;
-}
-
-static struct ppa_addr __chnl_to_generic_addr(struct ppa_addr r)
-{
- struct ppa_addr l;
-
- l.ppa = 0;
-
- l.g.sec = r.chnl.sec;
- l.g.pl = r.chnl.pl;
- l.g.pg = r.chnl.pg;
- l.g.blk = r.chnl.blk;
- l.g.lun = r.chnl.lun;
- l.g.ch = r.chnl.ch;
+ /*
+ * (r.ppa << X offset) & X len bitmask. X eq. blk, pg, etc.
+ */
+ l.g.blk = (r.ppa >> dev->ppaf.blk_offset) &
+ (((1 << dev->ppaf.blk_len) - 1));
+ l.g.pg |= (r.ppa >> dev->ppaf.pg_offset) &
+ (((1 << dev->ppaf.pg_len) - 1));
+ l.g.sec |= (r.ppa >> dev->ppaf.sect_offset) &
+ (((1 << dev->ppaf.sect_len) - 1));
+ l.g.pl |= (r.ppa >> dev->ppaf.pln_offset) &
+ (((1 << dev->ppaf.pln_len) - 1));
+ l.g.lun |= (r.ppa >> dev->ppaf.lun_offset) &
+ (((1 << dev->ppaf.lun_len) - 1));
+ l.g.ch |= (r.ppa >> dev->ppaf.ch_offset) &
+ (((1 << dev->ppaf.ch_len) - 1));
return l;
}
-static inline struct ppa_addr addr_to_generic_mode(struct nvm_dev *dev,
- struct ppa_addr gppa)
-{
- switch (dev->addr_mode) {
- case NVM_ADDRMODE_LINEAR:
- return __linear_to_generic_addr(dev, gppa);
- case NVM_ADDRMODE_CHANNEL:
- return __chnl_to_generic_addr(gppa);
- default:
- BUG();
- }
- return gppa;
-}
-
-static inline struct ppa_addr generic_to_addr_mode(struct nvm_dev *dev,
- struct ppa_addr gppa)
-{
- switch (dev->addr_mode) {
- case NVM_ADDRMODE_LINEAR:
- return __generic_to_linear_addr(dev, gppa);
- case NVM_ADDRMODE_CHANNEL:
- return __generic_to_chnl_addr(gppa);
- default:
- BUG();
- }
- return gppa;
-}
-
static inline int ppa_empty(struct ppa_addr ppa_addr)
{
return (ppa_addr.ppa == ADDR_EMPTY);
@@ -468,7 +387,7 @@ typedef int (nvmm_end_io_fn)(struct nvm_rq *, int);
typedef int (nvmm_erase_blk_fn)(struct nvm_dev *, struct nvm_block *,
unsigned long);
typedef struct nvm_lun *(nvmm_get_lun_fn)(struct nvm_dev *, int);
-typedef void (nvmm_free_blocks_print_fn)(struct nvm_dev *);
+typedef void (nvmm_lun_info_print_fn)(struct nvm_dev *);
struct nvmm_type {
const char *name;
@@ -492,7 +411,7 @@ struct nvmm_type {
nvmm_get_lun_fn *get_lun;
/* Statistics */
- nvmm_free_blocks_print_fn *free_blocks_print;
+ nvmm_lun_info_print_fn *lun_info_print;
struct list_head list;
};
diff --git a/include/linux/list.h b/include/linux/list.h
index 993395a2e55c..5356f4d661a7 100644
--- a/include/linux/list.h
+++ b/include/linux/list.h
@@ -24,7 +24,7 @@
static inline void INIT_LIST_HEAD(struct list_head *list)
{
- list->next = list;
+ WRITE_ONCE(list->next, list);
list->prev = list;
}
@@ -42,7 +42,7 @@ static inline void __list_add(struct list_head *new,
next->prev = new;
new->next = next;
new->prev = prev;
- prev->next = new;
+ WRITE_ONCE(prev->next, new);
}
#else
extern void __list_add(struct list_head *new,
@@ -186,7 +186,7 @@ static inline int list_is_last(const struct list_head *list,
*/
static inline int list_empty(const struct list_head *head)
{
- return head->next == head;
+ return READ_ONCE(head->next) == head;
}
/**
@@ -608,7 +608,7 @@ static inline int hlist_unhashed(const struct hlist_node *h)
static inline int hlist_empty(const struct hlist_head *h)
{
- return !h->first;
+ return !READ_ONCE(h->first);
}
static inline void __hlist_del(struct hlist_node *n)
@@ -642,7 +642,7 @@ static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
n->next = first;
if (first)
first->pprev = &n->next;
- h->first = n;
+ WRITE_ONCE(h->first, n);
n->pprev = &h->first;
}
@@ -653,14 +653,14 @@ static inline void hlist_add_before(struct hlist_node *n,
n->pprev = next->pprev;
n->next = next;
next->pprev = &n->next;
- *(n->pprev) = n;
+ WRITE_ONCE(*(n->pprev), n);
}
static inline void hlist_add_behind(struct hlist_node *n,
struct hlist_node *prev)
{
n->next = prev->next;
- prev->next = n;
+ WRITE_ONCE(prev->next, n);
n->pprev = &prev->next;
if (n->next)
diff --git a/include/linux/list_bl.h b/include/linux/list_bl.h
index 8132214e8efd..ee7229a6c06a 100644
--- a/include/linux/list_bl.h
+++ b/include/linux/list_bl.h
@@ -70,7 +70,7 @@ static inline void hlist_bl_set_first(struct hlist_bl_head *h,
static inline int hlist_bl_empty(const struct hlist_bl_head *h)
{
- return !((unsigned long)h->first & ~LIST_BL_LOCKMASK);
+ return !((unsigned long)READ_ONCE(h->first) & ~LIST_BL_LOCKMASK);
}
static inline void hlist_bl_add_head(struct hlist_bl_node *n,
diff --git a/include/linux/list_nulls.h b/include/linux/list_nulls.h
index 444d2b1313bd..b01fe1009084 100644
--- a/include/linux/list_nulls.h
+++ b/include/linux/list_nulls.h
@@ -57,7 +57,7 @@ static inline int hlist_nulls_unhashed(const struct hlist_nulls_node *h)
static inline int hlist_nulls_empty(const struct hlist_nulls_head *h)
{
- return is_a_nulls(h->first);
+ return is_a_nulls(READ_ONCE(h->first));
}
static inline void hlist_nulls_add_head(struct hlist_nulls_node *n,
diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index 31db7a05dd36..a8828652f794 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -37,8 +37,9 @@ enum klp_state {
* struct klp_func - function structure for live patching
* @old_name: name of the function to be patched
* @new_func: pointer to the patched function code
- * @old_addr: a hint conveying at what address the old function
- * can be found (optional, vmlinux patches only)
+ * @old_sympos: a hint indicating which symbol position the old function
+ * can be found (optional)
+ * @old_addr: the address of the function being patched
* @kobj: kobject for sysfs resources
* @state: tracks function-level patch application state
* @stack_node: list node for klp_ops func_stack list
@@ -48,16 +49,16 @@ struct klp_func {
const char *old_name;
void *new_func;
/*
- * The old_addr field is optional and can be used to resolve
- * duplicate symbol names in the vmlinux object. If this
- * information is not present, the symbol is located by name
- * with kallsyms. If the name is not unique and old_addr is
- * not provided, the patch application fails as there is no
- * way to resolve the ambiguity.
+ * The old_sympos field is optional and can be used to resolve
+ * duplicate symbol names in livepatch objects. If this field is zero,
+ * it is expected the symbol is unique, otherwise patching fails. If
+ * this value is greater than zero then that occurrence of the symbol
+ * in kallsyms for the given object is used.
*/
- unsigned long old_addr;
+ unsigned long old_sympos;
/* internal */
+ unsigned long old_addr;
struct kobject kobj;
enum klp_state state;
struct list_head stack_node;
@@ -66,8 +67,7 @@ struct klp_func {
/**
* struct klp_reloc - relocation structure for live patching
* @loc: address where the relocation will be written
- * @val: address of the referenced symbol (optional,
- * vmlinux patches only)
+ * @sympos: position in kallsyms to disambiguate symbols (optional)
* @type: ELF relocation type
* @name: name of the referenced symbol (for lookup/verification)
* @addend: offset from the referenced symbol
@@ -75,7 +75,7 @@ struct klp_func {
*/
struct klp_reloc {
unsigned long loc;
- unsigned long val;
+ unsigned long sympos;
unsigned long type;
const char *name;
int addend;
diff --git a/include/linux/lockd/bind.h b/include/linux/lockd/bind.h
index 4d24d64578c4..140edab64446 100644
--- a/include/linux/lockd/bind.h
+++ b/include/linux/lockd/bind.h
@@ -29,7 +29,7 @@ struct nlmsvc_binding {
void (*fclose)(struct file *);
};
-extern struct nlmsvc_binding * nlmsvc_ops;
+extern const struct nlmsvc_binding *nlmsvc_ops;
/*
* Similar to nfs_client_initdata, but without the NFS-specific
diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h
index 70400dc7660f..c57e424d914b 100644
--- a/include/linux/lockdep.h
+++ b/include/linux/lockdep.h
@@ -2,7 +2,7 @@
* Runtime locking correctness validator
*
* Copyright (C) 2006,2007 Red Hat, Inc., Ingo Molnar <mingo@redhat.com>
- * Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra
*
* see Documentation/locking/lockdep-design.txt for more details.
*/
diff --git a/include/linux/mdio.h b/include/linux/mdio.h
index b42963bc81dd..5bfd99d1a40a 100644
--- a/include/linux/mdio.h
+++ b/include/linux/mdio.h
@@ -11,6 +11,55 @@
#include <uapi/linux/mdio.h>
+struct mii_bus;
+
+struct mdio_device {
+ struct device dev;
+
+ const struct dev_pm_ops *pm_ops;
+ struct mii_bus *bus;
+
+ int (*bus_match)(struct device *dev, struct device_driver *drv);
+ void (*device_free)(struct mdio_device *mdiodev);
+ void (*device_remove)(struct mdio_device *mdiodev);
+
+ /* Bus address of the MDIO device (0-31) */
+ int addr;
+ int flags;
+};
+#define to_mdio_device(d) container_of(d, struct mdio_device, dev)
+
+/* struct mdio_driver_common: Common to all MDIO drivers */
+struct mdio_driver_common {
+ struct device_driver driver;
+ int flags;
+};
+#define MDIO_DEVICE_FLAG_PHY 1
+#define to_mdio_common_driver(d) \
+ container_of(d, struct mdio_driver_common, driver)
+
+/* struct mdio_driver: Generic MDIO driver */
+struct mdio_driver {
+ struct mdio_driver_common mdiodrv;
+
+ /*
+ * Called during discovery. Used to set
+ * up device-specific structures, if any
+ */
+ int (*probe)(struct mdio_device *mdiodev);
+
+ /* Clears up any memory if needed */
+ void (*remove)(struct mdio_device *mdiodev);
+};
+#define to_mdio_driver(d) \
+ container_of(to_mdio_common_driver(d), struct mdio_driver, mdiodrv)
+
+void mdio_device_free(struct mdio_device *mdiodev);
+struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr);
+int mdio_device_register(struct mdio_device *mdiodev);
+void mdio_device_remove(struct mdio_device *mdiodev);
+int mdio_driver_register(struct mdio_driver *drv);
+void mdio_driver_unregister(struct mdio_driver *drv);
static inline bool mdio_phy_id_is_c45(int phy_id)
{
@@ -173,4 +222,33 @@ static inline u16 ethtool_adv_to_mmd_eee_adv_t(u32 adv)
return reg;
}
+int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
+int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum);
+int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
+int mdiobus_write_nested(struct mii_bus *bus, int addr, u32 regnum, u16 val);
+
+int mdiobus_register_device(struct mdio_device *mdiodev);
+int mdiobus_unregister_device(struct mdio_device *mdiodev);
+bool mdiobus_is_registered_device(struct mii_bus *bus, int addr);
+struct phy_device *mdiobus_get_phy(struct mii_bus *bus, int addr);
+
+/**
+ * module_mdio_driver() - Helper macro for registering mdio drivers
+ *
+ * Helper macro for MDIO drivers which do not do anything special in module
+ * init/exit. Each module may only use this macro once, and calling it
+ * replaces module_init() and module_exit().
+ */
+#define mdio_module_driver(_mdio_driver) \
+static int __init mdio_module_init(void) \
+{ \
+ return mdio_driver_register(&_mdio_driver); \
+} \
+module_init(mdio_module_init); \
+static void __exit mdio_module_exit(void) \
+{ \
+ mdio_driver_unregister(&_mdio_driver); \
+} \
+module_exit(mdio_module_exit)
+
#endif /* __LINUX_MDIO_H__ */
diff --git a/include/linux/memblock.h b/include/linux/memblock.h
index 24daf8fc4d7c..173fb44e22f1 100644
--- a/include/linux/memblock.h
+++ b/include/linux/memblock.h
@@ -25,6 +25,7 @@ enum {
MEMBLOCK_NONE = 0x0, /* No special request */
MEMBLOCK_HOTPLUG = 0x1, /* hotpluggable region */
MEMBLOCK_MIRROR = 0x2, /* mirrored region */
+ MEMBLOCK_NOMAP = 0x4, /* don't add to kernel direct mapping */
};
struct memblock_region {
@@ -82,6 +83,7 @@ bool memblock_overlaps_region(struct memblock_type *type,
int memblock_mark_hotplug(phys_addr_t base, phys_addr_t size);
int memblock_clear_hotplug(phys_addr_t base, phys_addr_t size);
int memblock_mark_mirror(phys_addr_t base, phys_addr_t size);
+int memblock_mark_nomap(phys_addr_t base, phys_addr_t size);
ulong choose_memblock_flags(void);
/* Low level functions */
@@ -184,6 +186,11 @@ static inline bool memblock_is_mirror(struct memblock_region *m)
return m->flags & MEMBLOCK_MIRROR;
}
+static inline bool memblock_is_nomap(struct memblock_region *m)
+{
+ return m->flags & MEMBLOCK_NOMAP;
+}
+
#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
int memblock_search_pfn_nid(unsigned long pfn, unsigned long *start_pfn,
unsigned long *end_pfn);
@@ -209,10 +216,10 @@ void __next_mem_pfn_range(int *idx, int nid, unsigned long *out_start_pfn,
* for_each_free_mem_range - iterate through free memblock areas
* @i: u64 used as loop variable
* @nid: node selector, %NUMA_NO_NODE for all nodes
+ * @flags: pick from blocks based on memory attributes
* @p_start: ptr to phys_addr_t for start address of the range, can be %NULL
* @p_end: ptr to phys_addr_t for end address of the range, can be %NULL
* @p_nid: ptr to int for nid of the range, can be %NULL
- * @flags: pick from blocks based on memory attributes
*
* Walks over free (memory && !reserved) areas of memblock. Available as
* soon as memblock is initialized.
@@ -225,10 +232,10 @@ void __next_mem_pfn_range(int *idx, int nid, unsigned long *out_start_pfn,
* for_each_free_mem_range_reverse - rev-iterate through free memblock areas
* @i: u64 used as loop variable
* @nid: node selector, %NUMA_NO_NODE for all nodes
+ * @flags: pick from blocks based on memory attributes
* @p_start: ptr to phys_addr_t for start address of the range, can be %NULL
* @p_end: ptr to phys_addr_t for end address of the range, can be %NULL
* @p_nid: ptr to int for nid of the range, can be %NULL
- * @flags: pick from blocks based on memory attributes
*
* Walks over free (memory && !reserved) areas of memblock in reverse
* order. Available as soon as memblock is initialized.
@@ -318,9 +325,10 @@ phys_addr_t memblock_mem_size(unsigned long limit_pfn);
phys_addr_t memblock_start_of_DRAM(void);
phys_addr_t memblock_end_of_DRAM(void);
void memblock_enforce_memory_limit(phys_addr_t memory_limit);
-int memblock_is_memory(phys_addr_t addr);
+bool memblock_is_memory(phys_addr_t addr);
+int memblock_is_map_memory(phys_addr_t addr);
int memblock_is_region_memory(phys_addr_t base, phys_addr_t size);
-int memblock_is_reserved(phys_addr_t addr);
+bool memblock_is_reserved(phys_addr_t addr);
bool memblock_is_region_reserved(phys_addr_t base, phys_addr_t size);
extern void __memblock_dump_all(void);
@@ -391,6 +399,11 @@ static inline unsigned long memblock_region_reserved_end_pfn(const struct memblo
region < (memblock.memblock_type.regions + memblock.memblock_type.cnt); \
region++)
+#define for_each_memblock_type(memblock_type, rgn) \
+ idx = 0; \
+ rgn = &memblock_type->regions[idx]; \
+ for (idx = 0; idx < memblock_type->cnt; \
+ idx++,rgn = &memblock_type->regions[idx])
#ifdef CONFIG_ARCH_DISCARD_MEMBLOCK
#define __init_memblock __meminit
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index cd0e2413c358..2292468f2a30 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -85,32 +85,10 @@ enum mem_cgroup_events_target {
MEM_CGROUP_NTARGETS,
};
-/*
- * Bits in struct cg_proto.flags
- */
-enum cg_proto_flags {
- /* Currently active and new sockets should be assigned to cgroups */
- MEMCG_SOCK_ACTIVE,
- /* It was ever activated; we must disarm static keys on destruction */
- MEMCG_SOCK_ACTIVATED,
-};
-
struct cg_proto {
struct page_counter memory_allocated; /* Current allocated memory. */
- struct percpu_counter sockets_allocated; /* Current number of sockets. */
int memory_pressure;
- long sysctl_mem[3];
- unsigned long flags;
- /*
- * memcg field is used to find which memcg we belong directly
- * Each memcg struct can hold more than one cg_proto, so container_of
- * won't really cut.
- *
- * The elegant solution would be having an inverse function to
- * proto_cgroup in struct proto, but that means polluting the structure
- * for everybody, instead of just for memcg users.
- */
- struct mem_cgroup *memcg;
+ bool active;
};
#ifdef CONFIG_MEMCG
@@ -192,6 +170,9 @@ struct mem_cgroup {
unsigned long low;
unsigned long high;
+ /* Range enforcement for interrupt charges */
+ struct work_struct high_work;
+
unsigned long soft_limit;
/* vmpressure notifications */
@@ -268,6 +249,10 @@ struct mem_cgroup {
struct wb_domain cgwb_domain;
#endif
+#ifdef CONFIG_INET
+ unsigned long socket_pressure;
+#endif
+
/* List of events which userspace want to receive */
struct list_head event_list;
spinlock_t event_list_lock;
@@ -275,7 +260,8 @@ struct mem_cgroup {
struct mem_cgroup_per_node *nodeinfo[0];
/* WARNING: nodeinfo must be the last member here */
};
-extern struct cgroup_subsys_state *mem_cgroup_root_css;
+
+extern struct mem_cgroup *root_mem_cgroup;
/**
* mem_cgroup_events - count memory events against a cgroup
@@ -308,18 +294,34 @@ struct lruvec *mem_cgroup_page_lruvec(struct page *, struct zone *);
bool task_in_mem_cgroup(struct task_struct *task, struct mem_cgroup *memcg);
struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p);
-struct mem_cgroup *parent_mem_cgroup(struct mem_cgroup *memcg);
static inline
struct mem_cgroup *mem_cgroup_from_css(struct cgroup_subsys_state *css){
return css ? container_of(css, struct mem_cgroup, css) : NULL;
}
+#define mem_cgroup_from_counter(counter, member) \
+ container_of(counter, struct mem_cgroup, member)
+
struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *,
struct mem_cgroup *,
struct mem_cgroup_reclaim_cookie *);
void mem_cgroup_iter_break(struct mem_cgroup *, struct mem_cgroup *);
+/**
+ * parent_mem_cgroup - find the accounting parent of a memcg
+ * @memcg: memcg whose parent to find
+ *
+ * Returns the parent memcg, or NULL if this is the root or the memory
+ * controller is in legacy no-hierarchy mode.
+ */
+static inline struct mem_cgroup *parent_mem_cgroup(struct mem_cgroup *memcg)
+{
+ if (!memcg->memory.parent)
+ return NULL;
+ return mem_cgroup_from_counter(memcg->memory.parent, memory);
+}
+
static inline bool mem_cgroup_is_descendant(struct mem_cgroup *memcg,
struct mem_cgroup *root)
{
@@ -671,12 +673,6 @@ void mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx)
}
#endif /* CONFIG_MEMCG */
-enum {
- UNDER_LIMIT,
- SOFT_LIMIT,
- OVER_LIMIT,
-};
-
#ifdef CONFIG_CGROUP_WRITEBACK
struct list_head *mem_cgroup_cgwb_list(struct mem_cgroup *memcg);
@@ -703,20 +699,35 @@ static inline void mem_cgroup_wb_stats(struct bdi_writeback *wb,
#endif /* CONFIG_CGROUP_WRITEBACK */
struct sock;
-#if defined(CONFIG_INET) && defined(CONFIG_MEMCG_KMEM)
void sock_update_memcg(struct sock *sk);
void sock_release_memcg(struct sock *sk);
-#else
-static inline void sock_update_memcg(struct sock *sk)
+bool mem_cgroup_charge_skmem(struct mem_cgroup *memcg, unsigned int nr_pages);
+void mem_cgroup_uncharge_skmem(struct mem_cgroup *memcg, unsigned int nr_pages);
+#if defined(CONFIG_MEMCG) && defined(CONFIG_INET)
+extern struct static_key_false memcg_sockets_enabled_key;
+#define mem_cgroup_sockets_enabled static_branch_unlikely(&memcg_sockets_enabled_key)
+static inline bool mem_cgroup_under_socket_pressure(struct mem_cgroup *memcg)
{
+#ifdef CONFIG_MEMCG_KMEM
+ if (memcg->tcp_mem.memory_pressure)
+ return true;
+#endif
+ do {
+ if (time_before(jiffies, memcg->socket_pressure))
+ return true;
+ } while ((memcg = parent_mem_cgroup(memcg)));
+ return false;
}
-static inline void sock_release_memcg(struct sock *sk)
+#else
+#define mem_cgroup_sockets_enabled 0
+static inline bool mem_cgroup_under_socket_pressure(struct mem_cgroup *memcg)
{
+ return false;
}
-#endif /* CONFIG_INET && CONFIG_MEMCG_KMEM */
+#endif
#ifdef CONFIG_MEMCG_KMEM
-extern struct static_key memcg_kmem_enabled_key;
+extern struct static_key_false memcg_kmem_enabled_key;
extern int memcg_nr_cache_ids;
void memcg_get_cache_ids(void);
@@ -732,7 +743,7 @@ void memcg_put_cache_ids(void);
static inline bool memcg_kmem_enabled(void)
{
- return static_key_false(&memcg_kmem_enabled_key);
+ return static_branch_unlikely(&memcg_kmem_enabled_key);
}
static inline bool memcg_kmem_is_active(struct mem_cgroup *memcg)
@@ -766,15 +777,13 @@ static inline int memcg_cache_id(struct mem_cgroup *memcg)
return memcg ? memcg->kmemcg_id : -1;
}
-struct kmem_cache *__memcg_kmem_get_cache(struct kmem_cache *cachep);
+struct kmem_cache *__memcg_kmem_get_cache(struct kmem_cache *cachep, gfp_t gfp);
void __memcg_kmem_put_cache(struct kmem_cache *cachep);
-static inline bool __memcg_kmem_bypass(gfp_t gfp)
+static inline bool __memcg_kmem_bypass(void)
{
if (!memcg_kmem_enabled())
return true;
- if (gfp & __GFP_NOACCOUNT)
- return true;
if (in_interrupt() || (!current->mm) || (current->flags & PF_KTHREAD))
return true;
return false;
@@ -791,7 +800,9 @@ static inline bool __memcg_kmem_bypass(gfp_t gfp)
static __always_inline int memcg_kmem_charge(struct page *page,
gfp_t gfp, int order)
{
- if (__memcg_kmem_bypass(gfp))
+ if (__memcg_kmem_bypass())
+ return 0;
+ if (!(gfp & __GFP_ACCOUNT))
return 0;
return __memcg_kmem_charge(page, gfp, order);
}
@@ -810,16 +821,15 @@ static __always_inline void memcg_kmem_uncharge(struct page *page, int order)
/**
* memcg_kmem_get_cache: selects the correct per-memcg cache for allocation
* @cachep: the original global kmem cache
- * @gfp: allocation flags.
*
* All memory allocated from a per-memcg cache is charged to the owner memcg.
*/
static __always_inline struct kmem_cache *
memcg_kmem_get_cache(struct kmem_cache *cachep, gfp_t gfp)
{
- if (__memcg_kmem_bypass(gfp))
+ if (__memcg_kmem_bypass())
return cachep;
- return __memcg_kmem_get_cache(cachep);
+ return __memcg_kmem_get_cache(cachep, gfp);
}
static __always_inline void memcg_kmem_put_cache(struct kmem_cache *cachep)
diff --git a/include/linux/mempolicy.h b/include/linux/mempolicy.h
index 3d385c81c153..2696c1f05ed1 100644
--- a/include/linux/mempolicy.h
+++ b/include/linux/mempolicy.h
@@ -122,7 +122,7 @@ struct sp_node {
struct shared_policy {
struct rb_root root;
- spinlock_t lock;
+ rwlock_t lock;
};
int vma_dup_policy(struct vm_area_struct *src, struct vm_area_struct *dst);
diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h
index 79e607e2f081..d55a42297d49 100644
--- a/include/linux/mfd/arizona/core.h
+++ b/include/linux/mfd/arizona/core.h
@@ -27,6 +27,8 @@ enum arizona_type {
WM8280 = 4,
WM8998 = 5,
WM1814 = 6,
+ WM1831 = 7,
+ CS47L24 = 8,
};
#define ARIZONA_IRQ_GP1 0
@@ -166,6 +168,7 @@ static inline int wm5102_patch(struct arizona *arizona)
#endif
int wm5110_patch(struct arizona *arizona);
+int cs47l24_patch(struct arizona *arizona);
int wm8997_patch(struct arizona *arizona);
int wm8998_patch(struct arizona *arizona);
diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h
index 57b45caaea80..64faeeff698c 100644
--- a/include/linux/mfd/arizona/pdata.h
+++ b/include/linux/mfd/arizona/pdata.h
@@ -171,7 +171,7 @@ struct arizona_pdata {
int inmode[ARIZONA_MAX_INPUT];
/** Mode for outputs */
- bool out_mono[ARIZONA_MAX_OUTPUT];
+ int out_mono[ARIZONA_MAX_OUTPUT];
/** PDM speaker mute setting */
unsigned int spk_mute[ARIZONA_MAX_PDM_SPK];
diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h
index 27dac3ff18b9..bc6f7e00fb3d 100644
--- a/include/linux/mfd/core.h
+++ b/include/linux/mfd/core.h
@@ -17,6 +17,7 @@
#include <linux/platform_device.h>
struct irq_domain;
+struct property_set;
/* Matches ACPI PNP id, either _HID or _CID, or ACPI _ADR */
struct mfd_cell_acpi_match {
@@ -44,6 +45,10 @@ struct mfd_cell {
/* platform data passed to the sub devices drivers */
void *platform_data;
size_t pdata_size;
+
+ /* device properties passed to the sub devices drivers */
+ const struct property_set *pset;
+
/*
* Device Tree compatible string
* See: Documentation/devicetree/usage-model.txt Chapter 2.2 for details
diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h
index 13e1d96935ed..c800dbc42079 100644
--- a/include/linux/mfd/palmas.h
+++ b/include/linux/mfd/palmas.h
@@ -134,21 +134,32 @@ struct palmas_pmic_driver_data {
struct regulator_config config);
};
+struct palmas_adc_wakeup_property {
+ int adc_channel_number;
+ int adc_high_threshold;
+ int adc_low_threshold;
+};
+
struct palmas_gpadc_platform_data {
/* Channel 3 current source is only enabled during conversion */
- int ch3_current;
+ int ch3_current; /* 0: off; 1: 10uA; 2: 400uA; 3: 800 uA */
/* Channel 0 current source can be used for battery detection.
* If used for battery detection this will cause a permanent current
* consumption depending on current level set here.
*/
- int ch0_current;
+ int ch0_current; /* 0: off; 1: 5uA; 2: 15uA; 3: 20 uA */
+ bool extended_delay; /* use extended delay for conversion */
/* default BAT_REMOVAL_DAT setting on device probe */
int bat_removal;
/* Sets the START_POLARITY bit in the RT_CTRL register */
int start_polarity;
+
+ int auto_conversion_period_ms;
+ struct palmas_adc_wakeup_property *adc_wakeup1_data;
+ struct palmas_adc_wakeup_property *adc_wakeup2_data;
};
struct palmas_reg_init {
@@ -405,28 +416,7 @@ struct palmas_gpadc_calibration {
s32 offset_error;
};
-struct palmas_gpadc {
- struct device *dev;
- struct palmas *palmas;
-
- int ch3_current;
- int ch0_current;
-
- int gpadc_force;
-
- int bat_removal;
-
- struct mutex reading_lock;
- struct completion irq_complete;
-
- int eoc_sw_irq;
-
- struct palmas_gpadc_calibration *palmas_cal_tbl;
-
- int conv0_channel;
- int conv1_channel;
- int rt_channel;
-};
+#define PALMAS_DATASHEET_NAME(_name) "palmas-gpadc-chan-"#_name
struct palmas_gpadc_result {
s32 raw_code;
@@ -520,6 +510,43 @@ enum palmas_irqs {
PALMAS_NUM_IRQ,
};
+/* Palmas GPADC Channels */
+enum {
+ PALMAS_ADC_CH_IN0,
+ PALMAS_ADC_CH_IN1,
+ PALMAS_ADC_CH_IN2,
+ PALMAS_ADC_CH_IN3,
+ PALMAS_ADC_CH_IN4,
+ PALMAS_ADC_CH_IN5,
+ PALMAS_ADC_CH_IN6,
+ PALMAS_ADC_CH_IN7,
+ PALMAS_ADC_CH_IN8,
+ PALMAS_ADC_CH_IN9,
+ PALMAS_ADC_CH_IN10,
+ PALMAS_ADC_CH_IN11,
+ PALMAS_ADC_CH_IN12,
+ PALMAS_ADC_CH_IN13,
+ PALMAS_ADC_CH_IN14,
+ PALMAS_ADC_CH_IN15,
+ PALMAS_ADC_CH_MAX,
+};
+
+/* Palmas GPADC Channel0 Current Source */
+enum {
+ PALMAS_ADC_CH0_CURRENT_SRC_0,
+ PALMAS_ADC_CH0_CURRENT_SRC_5,
+ PALMAS_ADC_CH0_CURRENT_SRC_15,
+ PALMAS_ADC_CH0_CURRENT_SRC_20,
+};
+
+/* Palmas GPADC Channel3 Current Source */
+enum {
+ PALMAS_ADC_CH3_CURRENT_SRC_0,
+ PALMAS_ADC_CH3_CURRENT_SRC_10,
+ PALMAS_ADC_CH3_CURRENT_SRC_400,
+ PALMAS_ADC_CH3_CURRENT_SRC_800,
+};
+
struct palmas_pmic {
struct palmas *palmas;
struct device *dev;
diff --git a/include/linux/mfd/samsung/core.h b/include/linux/mfd/samsung/core.h
index a06098639399..6bc4bcd488ac 100644
--- a/include/linux/mfd/samsung/core.h
+++ b/include/linux/mfd/samsung/core.h
@@ -44,6 +44,7 @@ enum sec_device_type {
S2MPS11X,
S2MPS13X,
S2MPS14X,
+ S2MPS15X,
S2MPU02,
};
diff --git a/include/linux/mfd/samsung/rtc.h b/include/linux/mfd/samsung/rtc.h
index 29c30ac36020..a65e4655d470 100644
--- a/include/linux/mfd/samsung/rtc.h
+++ b/include/linux/mfd/samsung/rtc.h
@@ -107,6 +107,8 @@ enum s2mps_rtc_reg {
#define S2MPS_RTC_WUDR_MASK (1 << S2MPS_RTC_WUDR_SHIFT)
#define S2MPS13_RTC_AUDR_SHIFT 1
#define S2MPS13_RTC_AUDR_MASK (1 << S2MPS13_RTC_AUDR_SHIFT)
+#define S2MPS15_RTC_WUDR_SHIFT 1
+#define S2MPS15_RTC_WUDR_MASK (1 << S2MPS15_RTC_WUDR_SHIFT)
#define S2MPS_RTC_RUDR_SHIFT 0
#define S2MPS_RTC_RUDR_MASK (1 << S2MPS_RTC_RUDR_SHIFT)
#define RTC_TCON_SHIFT 1
diff --git a/include/linux/mfd/samsung/s2mps15.h b/include/linux/mfd/samsung/s2mps15.h
new file mode 100644
index 000000000000..36d35287c3c0
--- /dev/null
+++ b/include/linux/mfd/samsung/s2mps15.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd
+ * http://www.samsung.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.
+ */
+
+#ifndef __LINUX_MFD_S2MPS15_H
+#define __LINUX_MFD_S2MPS15_H
+
+/* S2MPS15 registers */
+enum s2mps15_reg {
+ S2MPS15_REG_ID,
+ S2MPS15_REG_INT1,
+ S2MPS15_REG_INT2,
+ S2MPS15_REG_INT3,
+ S2MPS15_REG_INT1M,
+ S2MPS15_REG_INT2M,
+ S2MPS15_REG_INT3M,
+ S2MPS15_REG_ST1,
+ S2MPS15_REG_ST2,
+ S2MPS15_REG_PWRONSRC,
+ S2MPS15_REG_OFFSRC,
+ S2MPS15_REG_BU_CHG,
+ S2MPS15_REG_RTC_BUF,
+ S2MPS15_REG_CTRL1,
+ S2MPS15_REG_CTRL2,
+ S2MPS15_REG_RSVD1,
+ S2MPS15_REG_RSVD2,
+ S2MPS15_REG_RSVD3,
+ S2MPS15_REG_RSVD4,
+ S2MPS15_REG_RSVD5,
+ S2MPS15_REG_RSVD6,
+ S2MPS15_REG_CTRL3,
+ S2MPS15_REG_RSVD7,
+ S2MPS15_REG_RSVD8,
+ S2MPS15_REG_RSVD9,
+ S2MPS15_REG_B1CTRL1,
+ S2MPS15_REG_B1CTRL2,
+ S2MPS15_REG_B2CTRL1,
+ S2MPS15_REG_B2CTRL2,
+ S2MPS15_REG_B3CTRL1,
+ S2MPS15_REG_B3CTRL2,
+ S2MPS15_REG_B4CTRL1,
+ S2MPS15_REG_B4CTRL2,
+ S2MPS15_REG_B5CTRL1,
+ S2MPS15_REG_B5CTRL2,
+ S2MPS15_REG_B6CTRL1,
+ S2MPS15_REG_B6CTRL2,
+ S2MPS15_REG_B7CTRL1,
+ S2MPS15_REG_B7CTRL2,
+ S2MPS15_REG_B8CTRL1,
+ S2MPS15_REG_B8CTRL2,
+ S2MPS15_REG_B9CTRL1,
+ S2MPS15_REG_B9CTRL2,
+ S2MPS15_REG_B10CTRL1,
+ S2MPS15_REG_B10CTRL2,
+ S2MPS15_REG_BBCTRL1,
+ S2MPS15_REG_BBCTRL2,
+ S2MPS15_REG_BRAMP,
+ S2MPS15_REG_LDODVS1,
+ S2MPS15_REG_LDODVS2,
+ S2MPS15_REG_LDODVS3,
+ S2MPS15_REG_LDODVS4,
+ S2MPS15_REG_L1CTRL,
+ S2MPS15_REG_L2CTRL,
+ S2MPS15_REG_L3CTRL,
+ S2MPS15_REG_L4CTRL,
+ S2MPS15_REG_L5CTRL,
+ S2MPS15_REG_L6CTRL,
+ S2MPS15_REG_L7CTRL,
+ S2MPS15_REG_L8CTRL,
+ S2MPS15_REG_L9CTRL,
+ S2MPS15_REG_L10CTRL,
+ S2MPS15_REG_L11CTRL,
+ S2MPS15_REG_L12CTRL,
+ S2MPS15_REG_L13CTRL,
+ S2MPS15_REG_L14CTRL,
+ S2MPS15_REG_L15CTRL,
+ S2MPS15_REG_L16CTRL,
+ S2MPS15_REG_L17CTRL,
+ S2MPS15_REG_L18CTRL,
+ S2MPS15_REG_L19CTRL,
+ S2MPS15_REG_L20CTRL,
+ S2MPS15_REG_L21CTRL,
+ S2MPS15_REG_L22CTRL,
+ S2MPS15_REG_L23CTRL,
+ S2MPS15_REG_L24CTRL,
+ S2MPS15_REG_L25CTRL,
+ S2MPS15_REG_L26CTRL,
+ S2MPS15_REG_L27CTRL,
+ S2MPS15_REG_LDODSCH1,
+ S2MPS15_REG_LDODSCH2,
+ S2MPS15_REG_LDODSCH3,
+ S2MPS15_REG_LDODSCH4,
+};
+
+/* S2MPS15 regulator ids */
+enum s2mps15_regulators {
+ S2MPS15_LDO1,
+ S2MPS15_LDO2,
+ S2MPS15_LDO3,
+ S2MPS15_LDO4,
+ S2MPS15_LDO5,
+ S2MPS15_LDO6,
+ S2MPS15_LDO7,
+ S2MPS15_LDO8,
+ S2MPS15_LDO9,
+ S2MPS15_LDO10,
+ S2MPS15_LDO11,
+ S2MPS15_LDO12,
+ S2MPS15_LDO13,
+ S2MPS15_LDO14,
+ S2MPS15_LDO15,
+ S2MPS15_LDO16,
+ S2MPS15_LDO17,
+ S2MPS15_LDO18,
+ S2MPS15_LDO19,
+ S2MPS15_LDO20,
+ S2MPS15_LDO21,
+ S2MPS15_LDO22,
+ S2MPS15_LDO23,
+ S2MPS15_LDO24,
+ S2MPS15_LDO25,
+ S2MPS15_LDO26,
+ S2MPS15_LDO27,
+ S2MPS15_BUCK1,
+ S2MPS15_BUCK2,
+ S2MPS15_BUCK3,
+ S2MPS15_BUCK4,
+ S2MPS15_BUCK5,
+ S2MPS15_BUCK6,
+ S2MPS15_BUCK7,
+ S2MPS15_BUCK8,
+ S2MPS15_BUCK9,
+ S2MPS15_BUCK10,
+ S2MPS15_BUCK11,
+ S2MPS15_REGULATOR_MAX,
+};
+
+#define S2MPS15_LDO_VSEL_MASK (0x3F)
+#define S2MPS15_BUCK_VSEL_MASK (0xFF)
+
+#define S2MPS15_ENABLE_SHIFT (0x06)
+#define S2MPS15_ENABLE_MASK (0x03 << S2MPS15_ENABLE_SHIFT)
+
+#define S2MPS15_LDO_N_VOLTAGES (S2MPS15_LDO_VSEL_MASK + 1)
+#define S2MPS15_BUCK_N_VOLTAGES (S2MPS15_BUCK_VSEL_MASK + 1)
+
+#endif /* __LINUX_MFD_S2MPS15_H */
diff --git a/include/linux/mfd/tps65218.h b/include/linux/mfd/tps65218.h
index 2f9b593246ee..d58f3b5f585a 100644
--- a/include/linux/mfd/tps65218.h
+++ b/include/linux/mfd/tps65218.h
@@ -200,6 +200,8 @@ enum tps65218_regulator_id {
TPS65218_DCDC_4,
TPS65218_DCDC_5,
TPS65218_DCDC_6,
+ /* LS's */
+ TPS65218_LS_3,
/* LDOs */
TPS65218_LDO_1,
};
@@ -210,8 +212,11 @@ enum tps65218_regulator_id {
#define TPS65218_NUM_DCDC 6
/* Number of LDO voltage regulators available */
#define TPS65218_NUM_LDO 1
+/* Number of total LS current regulators available */
+#define TPS65218_NUM_LS 1
/* Number of total regulators available */
-#define TPS65218_NUM_REGULATOR (TPS65218_NUM_DCDC + TPS65218_NUM_LDO)
+#define TPS65218_NUM_REGULATOR (TPS65218_NUM_DCDC + TPS65218_NUM_LDO \
+ + TPS65218_NUM_LS)
/* Define the TPS65218 IRQ numbers */
enum tps65218_irqs {
diff --git a/include/linux/mfd/wm8350/pmic.h b/include/linux/mfd/wm8350/pmic.h
index 579b50ca2e02..7a09e7f1f984 100644
--- a/include/linux/mfd/wm8350/pmic.h
+++ b/include/linux/mfd/wm8350/pmic.h
@@ -715,7 +715,6 @@ struct wm8350_led_platform_data {
struct wm8350_led {
struct platform_device *pdev;
- struct mutex mutex;
struct work_struct work;
spinlock_t value_lock;
enum led_brightness value;
diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h
index 7501626ab529..d3133be12d92 100644
--- a/include/linux/mlx4/device.h
+++ b/include/linux/mlx4/device.h
@@ -427,6 +427,17 @@ enum {
};
enum {
+ /*
+ * Max wqe size for rdma read is 512 bytes, so this
+ * limits our max_sge_rd as the wqe needs to fit:
+ * - ctrl segment (16 bytes)
+ * - rdma segment (16 bytes)
+ * - scatter elements (16 bytes each)
+ */
+ MLX4_MAX_SGE_RD = (512 - 16 - 16) / 16
+};
+
+enum {
MLX4_DEV_PMC_SUBTYPE_GUID_INFO = 0x14,
MLX4_DEV_PMC_SUBTYPE_PORT_INFO = 0x15,
MLX4_DEV_PMC_SUBTYPE_PKEY_TABLE = 0x16,
diff --git a/include/linux/mlx4/driver.h b/include/linux/mlx4/driver.h
index 5a06d969338e..2e8af001c5da 100644
--- a/include/linux/mlx4/driver.h
+++ b/include/linux/mlx4/driver.h
@@ -75,6 +75,11 @@ static inline int mlx4_is_bonded(struct mlx4_dev *dev)
return !!(dev->flags & MLX4_FLAG_BONDED);
}
+static inline int mlx4_is_mf_bonded(struct mlx4_dev *dev)
+{
+ return (mlx4_is_bonded(dev) && mlx4_is_mfunc(dev));
+}
+
struct mlx4_port_map {
u8 port1;
u8 port2;
diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h
index 0b473cbfa7ef..7be845e30689 100644
--- a/include/linux/mlx5/device.h
+++ b/include/linux/mlx5/device.h
@@ -251,6 +251,7 @@ enum mlx5_event {
MLX5_EVENT_TYPE_PAGE_REQUEST = 0xb,
MLX5_EVENT_TYPE_PAGE_FAULT = 0xc,
+ MLX5_EVENT_TYPE_NIC_VPORT_CHANGE = 0xd,
};
enum {
@@ -442,9 +443,12 @@ struct mlx5_init_seg {
__be32 rsvd1[120];
__be32 initializing;
struct health_buffer health;
- __be32 rsvd2[884];
+ __be32 rsvd2[880];
+ __be32 internal_timer_h;
+ __be32 internal_timer_l;
+ __be32 rsrv3[2];
__be32 health_counter;
- __be32 rsvd3[1019];
+ __be32 rsvd4[1019];
__be64 ieee1588_clk;
__be32 ieee1588_clk_type;
__be32 clr_intx;
@@ -520,6 +524,12 @@ struct mlx5_eqe_page_fault {
__be32 flags_qpn;
} __packed;
+struct mlx5_eqe_vport_change {
+ u8 rsvd0[2];
+ __be16 vport_num;
+ __be32 rsvd1[6];
+} __packed;
+
union ev_data {
__be32 raw[7];
struct mlx5_eqe_cmd cmd;
@@ -532,6 +542,7 @@ union ev_data {
struct mlx5_eqe_stall_vl stall_vl;
struct mlx5_eqe_page_req req_pages;
struct mlx5_eqe_page_fault page_fault;
+ struct mlx5_eqe_vport_change vport_change;
} __packed;
struct mlx5_eqe {
@@ -593,7 +604,8 @@ struct mlx5_cqe64 {
__be32 imm_inval_pkey;
u8 rsvd40[4];
__be32 byte_cnt;
- __be64 timestamp;
+ __be32 timestamp_h;
+ __be32 timestamp_l;
__be32 sop_drop_qpn;
__be16 wqe_counter;
u8 signature;
@@ -615,6 +627,16 @@ static inline int cqe_has_vlan(struct mlx5_cqe64 *cqe)
return !!(cqe->l4_hdr_type_etc & 0x1);
}
+static inline u64 get_cqe_ts(struct mlx5_cqe64 *cqe)
+{
+ u32 hi, lo;
+
+ hi = be32_to_cpu(cqe->timestamp_h);
+ lo = be32_to_cpu(cqe->timestamp_l);
+
+ return (u64)lo | ((u64)hi << 32);
+}
+
enum {
CQE_L4_HDR_TYPE_NONE = 0x0,
CQE_L4_HDR_TYPE_TCP_NO_ACK = 0x1,
@@ -1067,6 +1089,12 @@ enum {
};
enum {
+ MLX5_ESW_VPORT_ADMIN_STATE_DOWN = 0x0,
+ MLX5_ESW_VPORT_ADMIN_STATE_UP = 0x1,
+ MLX5_ESW_VPORT_ADMIN_STATE_AUTO = 0x2,
+};
+
+enum {
MLX5_L3_PROT_TYPE_IPV4 = 0,
MLX5_L3_PROT_TYPE_IPV6 = 1,
};
@@ -1102,6 +1130,12 @@ enum {
MLX5_FLOW_CONTEXT_DEST_TYPE_TIR = 2,
};
+enum mlx5_list_type {
+ MLX5_NVPRT_LIST_TYPE_UC = 0x0,
+ MLX5_NVPRT_LIST_TYPE_MC = 0x1,
+ MLX5_NVPRT_LIST_TYPE_VLAN = 0x2,
+};
+
enum {
MLX5_RQC_RQ_TYPE_MEMORY_RQ_INLINE = 0x0,
MLX5_RQC_RQ_TYPE_MEMORY_RQ_RPM = 0x1,
@@ -1124,6 +1158,8 @@ enum mlx5_cap_type {
MLX5_CAP_IPOIB_OFFLOADS,
MLX5_CAP_EOIB_OFFLOADS,
MLX5_CAP_FLOW_TABLE,
+ MLX5_CAP_ESWITCH_FLOW_TABLE,
+ MLX5_CAP_ESWITCH,
/* NUM OF CAP Types */
MLX5_CAP_NUM
};
@@ -1161,6 +1197,28 @@ enum mlx5_cap_type {
#define MLX5_CAP_FLOWTABLE_MAX(mdev, cap) \
MLX5_GET(flow_table_nic_cap, mdev->hca_caps_max[MLX5_CAP_FLOW_TABLE], cap)
+#define MLX5_CAP_ESW_FLOWTABLE(mdev, cap) \
+ MLX5_GET(flow_table_eswitch_cap, \
+ mdev->hca_caps_cur[MLX5_CAP_ESWITCH_FLOW_TABLE], cap)
+
+#define MLX5_CAP_ESW_FLOWTABLE_MAX(mdev, cap) \
+ MLX5_GET(flow_table_eswitch_cap, \
+ mdev->hca_caps_max[MLX5_CAP_ESWITCH_FLOW_TABLE], cap)
+
+#define MLX5_CAP_ESW_FLOWTABLE_FDB(mdev, cap) \
+ MLX5_CAP_ESW_FLOWTABLE(mdev, flow_table_properties_nic_esw_fdb.cap)
+
+#define MLX5_CAP_ESW_FLOWTABLE_FDB_MAX(mdev, cap) \
+ MLX5_CAP_ESW_FLOWTABLE_MAX(mdev, flow_table_properties_nic_esw_fdb.cap)
+
+#define MLX5_CAP_ESW(mdev, cap) \
+ MLX5_GET(e_switch_cap, \
+ mdev->hca_caps_cur[MLX5_CAP_ESWITCH], cap)
+
+#define MLX5_CAP_ESW_MAX(mdev, cap) \
+ MLX5_GET(e_switch_cap, \
+ mdev->hca_caps_max[MLX5_CAP_ESWITCH], cap)
+
#define MLX5_CAP_ODP(mdev, cap)\
MLX5_GET(odp_cap, mdev->hca_caps_cur[MLX5_CAP_ODP], cap)
@@ -1200,4 +1258,6 @@ static inline u16 mlx5_to_sw_pkey_sz(int pkey_sz)
return MLX5_MIN_PKEY_TABLE_SIZE << pkey_sz;
}
+#define MLX5_BY_PASS_NUM_PRIOS 9
+
#endif /* MLX5_DEVICE_H */
diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h
index 5c857f2a20d7..2fd7019f69db 100644
--- a/include/linux/mlx5/driver.h
+++ b/include/linux/mlx5/driver.h
@@ -426,11 +426,23 @@ struct mlx5_mr_table {
struct radix_tree_root tree;
};
+struct mlx5_vf_context {
+ int enabled;
+};
+
+struct mlx5_core_sriov {
+ struct mlx5_vf_context *vfs_ctx;
+ int num_vfs;
+ int enabled_vfs;
+};
+
struct mlx5_irq_info {
cpumask_var_t mask;
char name[MLX5_MAX_IRQ_NAME];
};
+struct mlx5_eswitch;
+
struct mlx5_priv {
char name[MLX5_MAX_NAME_LEN];
struct mlx5_eq_table eq_table;
@@ -447,6 +459,7 @@ struct mlx5_priv {
int fw_pages;
atomic_t reg_pages;
struct list_head free_list;
+ int vfs_pages;
struct mlx5_core_health health;
@@ -485,6 +498,12 @@ struct mlx5_priv {
struct list_head dev_list;
struct list_head ctx_list;
spinlock_t ctx_lock;
+
+ struct mlx5_eswitch *eswitch;
+ struct mlx5_core_sriov sriov;
+ unsigned long pci_dev_data;
+ struct mlx5_flow_root_namespace *root_ns;
+ struct mlx5_flow_root_namespace *fdb_root_ns;
};
enum mlx5_device_state {
@@ -739,6 +758,8 @@ void mlx5_pagealloc_init(struct mlx5_core_dev *dev);
void mlx5_pagealloc_cleanup(struct mlx5_core_dev *dev);
int mlx5_pagealloc_start(struct mlx5_core_dev *dev);
void mlx5_pagealloc_stop(struct mlx5_core_dev *dev);
+int mlx5_sriov_init(struct mlx5_core_dev *dev);
+int mlx5_sriov_cleanup(struct mlx5_core_dev *dev);
void mlx5_core_req_pages_handler(struct mlx5_core_dev *dev, u16 func_id,
s32 npages);
int mlx5_satisfy_startup_pages(struct mlx5_core_dev *dev, int boot);
@@ -884,6 +905,15 @@ struct mlx5_profile {
} mr_cache[MAX_MR_CACHE_ENTRIES];
};
+enum {
+ MLX5_PCI_DEV_IS_VF = 1 << 0,
+};
+
+static inline int mlx5_core_is_pf(struct mlx5_core_dev *dev)
+{
+ return !(dev->priv.pci_dev_data & MLX5_PCI_DEV_IS_VF);
+}
+
static inline int mlx5_get_gid_table_len(u16 param)
{
if (param > 4) {
diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h
new file mode 100644
index 000000000000..8230caa3fb6e
--- /dev/null
+++ b/include/linux/mlx5/fs.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2015, Mellanox Technologies. 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 _MLX5_FS_
+#define _MLX5_FS_
+
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/mlx5_ifc.h>
+
+#define MLX5_FS_DEFAULT_FLOW_TAG 0x0
+
+#define LEFTOVERS_RULE_NUM 2
+static inline void build_leftovers_ft_param(int *priority,
+ int *n_ent,
+ int *n_grp)
+{
+ *priority = 0; /* Priority of leftovers_prio-0 */
+ *n_ent = LEFTOVERS_RULE_NUM;
+ *n_grp = LEFTOVERS_RULE_NUM;
+}
+
+enum mlx5_flow_namespace_type {
+ MLX5_FLOW_NAMESPACE_BYPASS,
+ MLX5_FLOW_NAMESPACE_KERNEL,
+ MLX5_FLOW_NAMESPACE_LEFTOVERS,
+ MLX5_FLOW_NAMESPACE_FDB,
+};
+
+struct mlx5_flow_table;
+struct mlx5_flow_group;
+struct mlx5_flow_rule;
+struct mlx5_flow_namespace;
+
+struct mlx5_flow_destination {
+ enum mlx5_flow_destination_type type;
+ union {
+ u32 tir_num;
+ struct mlx5_flow_table *ft;
+ u32 vport_num;
+ };
+};
+
+struct mlx5_flow_namespace *
+mlx5_get_flow_namespace(struct mlx5_core_dev *dev,
+ enum mlx5_flow_namespace_type type);
+
+struct mlx5_flow_table *
+mlx5_create_auto_grouped_flow_table(struct mlx5_flow_namespace *ns,
+ int prio,
+ int num_flow_table_entries,
+ int max_num_groups);
+
+struct mlx5_flow_table *
+mlx5_create_flow_table(struct mlx5_flow_namespace *ns,
+ int prio,
+ int num_flow_table_entries);
+int mlx5_destroy_flow_table(struct mlx5_flow_table *ft);
+
+/* inbox should be set with the following values:
+ * start_flow_index
+ * end_flow_index
+ * match_criteria_enable
+ * match_criteria
+ */
+struct mlx5_flow_group *
+mlx5_create_flow_group(struct mlx5_flow_table *ft, u32 *in);
+void mlx5_destroy_flow_group(struct mlx5_flow_group *fg);
+
+/* Single destination per rule.
+ * Group ID is implied by the match criteria.
+ */
+struct mlx5_flow_rule *
+mlx5_add_flow_rule(struct mlx5_flow_table *ft,
+ u8 match_criteria_enable,
+ u32 *match_criteria,
+ u32 *match_value,
+ u32 action,
+ u32 flow_tag,
+ struct mlx5_flow_destination *dest);
+void mlx5_del_flow_rule(struct mlx5_flow_rule *fr);
+
+#endif
diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h
index 1565324eb620..68d73f82e009 100644
--- a/include/linux/mlx5/mlx5_ifc.h
+++ b/include/linux/mlx5/mlx5_ifc.h
@@ -185,6 +185,7 @@ enum {
MLX5_CMD_OP_MODIFY_RQT = 0x917,
MLX5_CMD_OP_DESTROY_RQT = 0x918,
MLX5_CMD_OP_QUERY_RQT = 0x919,
+ MLX5_CMD_OP_SET_FLOW_TABLE_ROOT = 0x92f,
MLX5_CMD_OP_CREATE_FLOW_TABLE = 0x930,
MLX5_CMD_OP_DESTROY_FLOW_TABLE = 0x931,
MLX5_CMD_OP_QUERY_FLOW_TABLE = 0x932,
@@ -193,7 +194,8 @@ enum {
MLX5_CMD_OP_QUERY_FLOW_GROUP = 0x935,
MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY = 0x936,
MLX5_CMD_OP_QUERY_FLOW_TABLE_ENTRY = 0x937,
- MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY = 0x938
+ MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY = 0x938,
+ MLX5_CMD_OP_MODIFY_FLOW_TABLE = 0x93c
};
struct mlx5_ifc_flow_table_fields_supported_bits {
@@ -256,25 +258,30 @@ struct mlx5_ifc_flow_table_fields_supported_bits {
struct mlx5_ifc_flow_table_prop_layout_bits {
u8 ft_support[0x1];
- u8 reserved_0[0x1f];
+ u8 reserved_0[0x2];
+ u8 flow_modify_en[0x1];
+ u8 modify_root[0x1];
+ u8 identified_miss_table_mode[0x1];
+ u8 flow_table_modify[0x1];
+ u8 reserved_1[0x19];
- u8 reserved_1[0x2];
+ u8 reserved_2[0x2];
u8 log_max_ft_size[0x6];
- u8 reserved_2[0x10];
+ u8 reserved_3[0x10];
u8 max_ft_level[0x8];
- u8 reserved_3[0x20];
+ u8 reserved_4[0x20];
- u8 reserved_4[0x18];
+ u8 reserved_5[0x18];
u8 log_max_ft_num[0x8];
- u8 reserved_5[0x18];
+ u8 reserved_6[0x18];
u8 log_max_destination[0x8];
- u8 reserved_6[0x18];
+ u8 reserved_7[0x18];
u8 log_max_flow[0x8];
- u8 reserved_7[0x40];
+ u8 reserved_8[0x40];
struct mlx5_ifc_flow_table_fields_supported_bits ft_field_support;
@@ -291,6 +298,22 @@ struct mlx5_ifc_odp_per_transport_service_cap_bits {
u8 reserved_1[0x1a];
};
+struct mlx5_ifc_ipv4_layout_bits {
+ u8 reserved_0[0x60];
+
+ u8 ipv4[0x20];
+};
+
+struct mlx5_ifc_ipv6_layout_bits {
+ u8 ipv6[16][0x8];
+};
+
+union mlx5_ifc_ipv6_layout_ipv4_layout_auto_bits {
+ struct mlx5_ifc_ipv6_layout_bits ipv6_layout;
+ struct mlx5_ifc_ipv4_layout_bits ipv4_layout;
+ u8 reserved_0[0x80];
+};
+
struct mlx5_ifc_fte_match_set_lyr_2_4_bits {
u8 smac_47_16[0x20];
@@ -321,9 +344,9 @@ struct mlx5_ifc_fte_match_set_lyr_2_4_bits {
u8 udp_sport[0x10];
u8 udp_dport[0x10];
- u8 src_ip[4][0x20];
+ union mlx5_ifc_ipv6_layout_ipv4_layout_auto_bits src_ipv4_src_ipv6;
- u8 dst_ip[4][0x20];
+ union mlx5_ifc_ipv6_layout_ipv4_layout_auto_bits dst_ipv4_dst_ipv6;
};
struct mlx5_ifc_fte_match_set_misc_bits {
@@ -447,6 +470,29 @@ struct mlx5_ifc_flow_table_nic_cap_bits {
u8 reserved_3[0x7200];
};
+struct mlx5_ifc_flow_table_eswitch_cap_bits {
+ u8 reserved_0[0x200];
+
+ struct mlx5_ifc_flow_table_prop_layout_bits flow_table_properties_nic_esw_fdb;
+
+ struct mlx5_ifc_flow_table_prop_layout_bits flow_table_properties_esw_acl_ingress;
+
+ struct mlx5_ifc_flow_table_prop_layout_bits flow_table_properties_esw_acl_egress;
+
+ u8 reserved_1[0x7800];
+};
+
+struct mlx5_ifc_e_switch_cap_bits {
+ u8 vport_svlan_strip[0x1];
+ u8 vport_cvlan_strip[0x1];
+ u8 vport_svlan_insert[0x1];
+ u8 vport_cvlan_insert_if_not_exist[0x1];
+ u8 vport_cvlan_insert_overwrite[0x1];
+ u8 reserved_0[0x1b];
+
+ u8 reserved_1[0x7e0];
+};
+
struct mlx5_ifc_per_protocol_networking_offload_caps_bits {
u8 csum_cap[0x1];
u8 vlan_cap[0x1];
@@ -665,7 +711,9 @@ struct mlx5_ifc_cmd_hca_cap_bits {
u8 reserved_17[0x1];
u8 ets[0x1];
u8 nic_flow_table[0x1];
- u8 reserved_18[0x4];
+ u8 eswitch_flow_table[0x1];
+ u8 early_vf_enable;
+ u8 reserved_18[0x2];
u8 local_ca_ack_delay[0x5];
u8 reserved_19[0x6];
u8 port_type[0x2];
@@ -787,27 +835,36 @@ struct mlx5_ifc_cmd_hca_cap_bits {
u8 reserved_60[0x1b];
u8 log_max_wq_sz[0x5];
- u8 reserved_61[0xa0];
-
+ u8 nic_vport_change_event[0x1];
+ u8 reserved_61[0xa];
+ u8 log_max_vlan_list[0x5];
u8 reserved_62[0x3];
+ u8 log_max_current_mc_list[0x5];
+ u8 reserved_63[0x3];
+ u8 log_max_current_uc_list[0x5];
+
+ u8 reserved_64[0x80];
+
+ u8 reserved_65[0x3];
u8 log_max_l2_table[0x5];
- u8 reserved_63[0x8];
+ u8 reserved_66[0x8];
u8 log_uar_page_sz[0x10];
- u8 reserved_64[0x100];
-
- u8 reserved_65[0x1f];
+ u8 reserved_67[0x40];
+ u8 device_frequency_khz[0x20];
+ u8 reserved_68[0x5f];
u8 cqe_zip[0x1];
u8 cqe_zip_timeout[0x10];
u8 cqe_zip_max_num[0x10];
- u8 reserved_66[0x220];
+ u8 reserved_69[0x220];
};
-enum {
- MLX5_DEST_FORMAT_STRUCT_DESTINATION_TYPE_FLOW_TABLE_ = 0x1,
- MLX5_DEST_FORMAT_STRUCT_DESTINATION_TYPE_TIR = 0x2,
+enum mlx5_flow_destination_type {
+ MLX5_FLOW_DESTINATION_TYPE_VPORT = 0x0,
+ MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE = 0x1,
+ MLX5_FLOW_DESTINATION_TYPE_TIR = 0x2,
};
struct mlx5_ifc_dest_format_struct_bits {
@@ -900,6 +957,13 @@ struct mlx5_ifc_mac_address_layout_bits {
u8 mac_addr_31_0[0x20];
};
+struct mlx5_ifc_vlan_layout_bits {
+ u8 reserved_0[0x14];
+ u8 vlan[0x0c];
+
+ u8 reserved_1[0x20];
+};
+
struct mlx5_ifc_cong_control_r_roce_ecn_np_bits {
u8 reserved_0[0xa0];
@@ -1829,6 +1893,8 @@ union mlx5_ifc_hca_cap_union_bits {
struct mlx5_ifc_roce_cap_bits roce_cap;
struct mlx5_ifc_per_protocol_networking_offload_caps_bits per_protocol_networking_offload_caps;
struct mlx5_ifc_flow_table_nic_cap_bits flow_table_nic_cap;
+ struct mlx5_ifc_flow_table_eswitch_cap_bits flow_table_eswitch_cap;
+ struct mlx5_ifc_e_switch_cap_bits e_switch_cap;
u8 reserved_0[0x8000];
};
@@ -2133,24 +2199,35 @@ struct mlx5_ifc_rmpc_bits {
struct mlx5_ifc_wq_bits wq;
};
-enum {
- MLX5_NIC_VPORT_CONTEXT_ALLOWED_LIST_TYPE_CURRENT_UC_MAC_ADDRESS = 0x0,
-};
-
struct mlx5_ifc_nic_vport_context_bits {
u8 reserved_0[0x1f];
u8 roce_en[0x1];
- u8 reserved_1[0x760];
+ u8 arm_change_event[0x1];
+ u8 reserved_1[0x1a];
+ u8 event_on_mtu[0x1];
+ u8 event_on_promisc_change[0x1];
+ u8 event_on_vlan_change[0x1];
+ u8 event_on_mc_address_change[0x1];
+ u8 event_on_uc_address_change[0x1];
- u8 reserved_2[0x5];
+ u8 reserved_2[0xf0];
+
+ u8 mtu[0x10];
+
+ u8 reserved_3[0x640];
+
+ u8 promisc_uc[0x1];
+ u8 promisc_mc[0x1];
+ u8 promisc_all[0x1];
+ u8 reserved_4[0x2];
u8 allowed_list_type[0x3];
- u8 reserved_3[0xc];
+ u8 reserved_5[0xc];
u8 allowed_list_size[0xc];
struct mlx5_ifc_mac_address_layout_bits permanent_address;
- u8 reserved_4[0x20];
+ u8 reserved_6[0x20];
u8 current_uc_mac_address[0][0x40];
};
@@ -2263,6 +2340,26 @@ struct mlx5_ifc_hca_vport_context_bits {
u8 reserved_6[0xca0];
};
+struct mlx5_ifc_esw_vport_context_bits {
+ u8 reserved_0[0x3];
+ u8 vport_svlan_strip[0x1];
+ u8 vport_cvlan_strip[0x1];
+ u8 vport_svlan_insert[0x1];
+ u8 vport_cvlan_insert[0x2];
+ u8 reserved_1[0x18];
+
+ u8 reserved_2[0x20];
+
+ u8 svlan_cfi[0x1];
+ u8 svlan_pcp[0x3];
+ u8 svlan_id[0xc];
+ u8 cvlan_cfi[0x1];
+ u8 cvlan_pcp[0x3];
+ u8 cvlan_id[0xc];
+
+ u8 reserved_3[0x7a0];
+};
+
enum {
MLX5_EQC_STATUS_OK = 0x0,
MLX5_EQC_STATUS_EQ_WRITE_FAILURE = 0xa,
@@ -2769,6 +2866,13 @@ struct mlx5_ifc_set_hca_cap_in_bits {
union mlx5_ifc_hca_cap_union_bits capability;
};
+enum {
+ MLX5_SET_FTE_MODIFY_ENABLE_MASK_ACTION = 0x0,
+ MLX5_SET_FTE_MODIFY_ENABLE_MASK_FLOW_TAG = 0x1,
+ MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST = 0x2,
+ MLX5_SET_FTE_MODIFY_ENABLE_MASK_FLOW_COUNTERS = 0x3
+};
+
struct mlx5_ifc_set_fte_out_bits {
u8 status[0x8];
u8 reserved_0[0x18];
@@ -2793,11 +2897,14 @@ struct mlx5_ifc_set_fte_in_bits {
u8 reserved_4[0x8];
u8 table_id[0x18];
- u8 reserved_5[0x40];
+ u8 reserved_5[0x18];
+ u8 modify_enable_mask[0x8];
+
+ u8 reserved_6[0x20];
u8 flow_index[0x20];
- u8 reserved_6[0xe0];
+ u8 reserved_7[0xe0];
struct mlx5_ifc_flow_context_bits flow_context;
};
@@ -2940,6 +3047,7 @@ struct mlx5_ifc_query_vport_state_out_bits {
enum {
MLX5_QUERY_VPORT_STATE_IN_OP_MOD_VNIC_VPORT = 0x0,
+ MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT = 0x1,
};
struct mlx5_ifc_query_vport_state_in_bits {
@@ -3700,6 +3808,64 @@ struct mlx5_ifc_query_flow_group_in_bits {
u8 reserved_5[0x120];
};
+struct mlx5_ifc_query_esw_vport_context_out_bits {
+ u8 status[0x8];
+ u8 reserved_0[0x18];
+
+ u8 syndrome[0x20];
+
+ u8 reserved_1[0x40];
+
+ struct mlx5_ifc_esw_vport_context_bits esw_vport_context;
+};
+
+struct mlx5_ifc_query_esw_vport_context_in_bits {
+ u8 opcode[0x10];
+ u8 reserved_0[0x10];
+
+ u8 reserved_1[0x10];
+ u8 op_mod[0x10];
+
+ u8 other_vport[0x1];
+ u8 reserved_2[0xf];
+ u8 vport_number[0x10];
+
+ u8 reserved_3[0x20];
+};
+
+struct mlx5_ifc_modify_esw_vport_context_out_bits {
+ u8 status[0x8];
+ u8 reserved_0[0x18];
+
+ u8 syndrome[0x20];
+
+ u8 reserved_1[0x40];
+};
+
+struct mlx5_ifc_esw_vport_context_fields_select_bits {
+ u8 reserved[0x1c];
+ u8 vport_cvlan_insert[0x1];
+ u8 vport_svlan_insert[0x1];
+ u8 vport_cvlan_strip[0x1];
+ u8 vport_svlan_strip[0x1];
+};
+
+struct mlx5_ifc_modify_esw_vport_context_in_bits {
+ u8 opcode[0x10];
+ u8 reserved_0[0x10];
+
+ u8 reserved_1[0x10];
+ u8 op_mod[0x10];
+
+ u8 other_vport[0x1];
+ u8 reserved_2[0xf];
+ u8 vport_number[0x10];
+
+ struct mlx5_ifc_esw_vport_context_fields_select_bits field_select;
+
+ struct mlx5_ifc_esw_vport_context_bits esw_vport_context;
+};
+
struct mlx5_ifc_query_eq_out_bits {
u8 status[0x8];
u8 reserved_0[0x18];
@@ -4228,7 +4394,10 @@ struct mlx5_ifc_modify_nic_vport_context_out_bits {
};
struct mlx5_ifc_modify_nic_vport_field_select_bits {
- u8 reserved_0[0x1c];
+ u8 reserved_0[0x19];
+ u8 mtu[0x1];
+ u8 change_event[0x1];
+ u8 promisc[0x1];
u8 permanent_address[0x1];
u8 addresses_list[0x1];
u8 roce_en[0x1];
@@ -5519,12 +5688,16 @@ struct mlx5_ifc_create_flow_table_in_bits {
u8 reserved_4[0x20];
- u8 reserved_5[0x8];
+ u8 reserved_5[0x4];
+ u8 table_miss_mode[0x4];
u8 level[0x8];
u8 reserved_6[0x8];
u8 log_size[0x8];
- u8 reserved_7[0x120];
+ u8 reserved_7[0x8];
+ u8 table_miss_id[0x18];
+
+ u8 reserved_8[0x100];
};
struct mlx5_ifc_create_flow_group_out_bits {
@@ -6798,4 +6971,72 @@ union mlx5_ifc_uplink_pci_interface_document_bits {
u8 reserved_0[0x20060];
};
+struct mlx5_ifc_set_flow_table_root_out_bits {
+ u8 status[0x8];
+ u8 reserved_0[0x18];
+
+ u8 syndrome[0x20];
+
+ u8 reserved_1[0x40];
+};
+
+struct mlx5_ifc_set_flow_table_root_in_bits {
+ u8 opcode[0x10];
+ u8 reserved_0[0x10];
+
+ u8 reserved_1[0x10];
+ u8 op_mod[0x10];
+
+ u8 reserved_2[0x40];
+
+ u8 table_type[0x8];
+ u8 reserved_3[0x18];
+
+ u8 reserved_4[0x8];
+ u8 table_id[0x18];
+
+ u8 reserved_5[0x140];
+};
+
+enum {
+ MLX5_MODIFY_FLOW_TABLE_MISS_TABLE_ID = 0x1,
+};
+
+struct mlx5_ifc_modify_flow_table_out_bits {
+ u8 status[0x8];
+ u8 reserved_0[0x18];
+
+ u8 syndrome[0x20];
+
+ u8 reserved_1[0x40];
+};
+
+struct mlx5_ifc_modify_flow_table_in_bits {
+ u8 opcode[0x10];
+ u8 reserved_0[0x10];
+
+ u8 reserved_1[0x10];
+ u8 op_mod[0x10];
+
+ u8 reserved_2[0x20];
+
+ u8 reserved_3[0x10];
+ u8 modify_field_select[0x10];
+
+ u8 table_type[0x8];
+ u8 reserved_4[0x18];
+
+ u8 reserved_5[0x8];
+ u8 table_id[0x18];
+
+ u8 reserved_6[0x4];
+ u8 table_miss_mode[0x4];
+ u8 reserved_7[0x18];
+
+ u8 reserved_8[0x8];
+ u8 table_miss_id[0x18];
+
+ u8 reserved_9[0x100];
+};
+
#endif /* MLX5_IFC_H */
diff --git a/include/linux/mlx5/vport.h b/include/linux/mlx5/vport.h
index 967e0fd06e89..638f2ca7a527 100644
--- a/include/linux/mlx5/vport.h
+++ b/include/linux/mlx5/vport.h
@@ -34,9 +34,17 @@
#define __MLX5_VPORT_H__
#include <linux/mlx5/driver.h>
+#include <linux/mlx5/device.h>
-u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod);
-void mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev, u8 *addr);
+u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport);
+u8 mlx5_query_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod,
+ u16 vport);
+int mlx5_modify_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod,
+ u16 vport, u8 state);
+int mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev,
+ u16 vport, u8 *addr);
+int mlx5_modify_nic_vport_mac_address(struct mlx5_core_dev *dev,
+ u16 vport, u8 *addr);
int mlx5_query_hca_vport_gid(struct mlx5_core_dev *dev, u8 other_vport,
u8 port_num, u16 vf_num, u16 gid_index,
union ib_gid *gid);
@@ -51,5 +59,30 @@ int mlx5_query_hca_vport_system_image_guid(struct mlx5_core_dev *dev,
u64 *sys_image_guid);
int mlx5_query_hca_vport_node_guid(struct mlx5_core_dev *dev,
u64 *node_guid);
+int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev,
+ u32 vport,
+ enum mlx5_list_type list_type,
+ u8 addr_list[][ETH_ALEN],
+ int *list_size);
+int mlx5_modify_nic_vport_mac_list(struct mlx5_core_dev *dev,
+ enum mlx5_list_type list_type,
+ u8 addr_list[][ETH_ALEN],
+ int list_size);
+int mlx5_query_nic_vport_promisc(struct mlx5_core_dev *mdev,
+ u32 vport,
+ int *promisc_uc,
+ int *promisc_mc,
+ int *promisc_all);
+int mlx5_modify_nic_vport_promisc(struct mlx5_core_dev *mdev,
+ int promisc_uc,
+ int promisc_mc,
+ int promisc_all);
+int mlx5_query_nic_vport_vlans(struct mlx5_core_dev *dev,
+ u32 vport,
+ u16 vlans[],
+ int *size);
+int mlx5_modify_nic_vport_vlans(struct mlx5_core_dev *dev,
+ u16 vlans[],
+ int list_size);
#endif /* __MLX5_VPORT_H__ */
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 00bad7793788..839d9e9a1c38 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -51,6 +51,17 @@ extern int sysctl_legacy_va_layout;
#define sysctl_legacy_va_layout 0
#endif
+#ifdef CONFIG_HAVE_ARCH_MMAP_RND_BITS
+extern const int mmap_rnd_bits_min;
+extern const int mmap_rnd_bits_max;
+extern int mmap_rnd_bits __read_mostly;
+#endif
+#ifdef CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS
+extern const int mmap_rnd_compat_bits_min;
+extern const int mmap_rnd_compat_bits_max;
+extern int mmap_rnd_compat_bits __read_mostly;
+#endif
+
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
@@ -225,10 +236,14 @@ extern pgprot_t protection_map[16];
* ->fault function. The vma's ->fault is responsible for returning a bitmask
* of VM_FAULT_xxx flags that give details about how the fault was handled.
*
+ * MM layer fills up gfp_mask for page allocations but fault handler might
+ * alter it if its implementation requires a different allocation context.
+ *
* pgoff should be used in favour of virtual_address, if possible.
*/
struct vm_fault {
unsigned int flags; /* FAULT_FLAG_xxx flags */
+ gfp_t gfp_mask; /* gfp mask to be used for allocations */
pgoff_t pgoff; /* Logical page offset based on vma */
void __user *virtual_address; /* Faulting virtual address */
@@ -1361,10 +1376,26 @@ static inline void dec_mm_counter(struct mm_struct *mm, int member)
atomic_long_dec(&mm->rss_stat.count[member]);
}
+/* Optimized variant when page is already known not to be PageAnon */
+static inline int mm_counter_file(struct page *page)
+{
+ if (PageSwapBacked(page))
+ return MM_SHMEMPAGES;
+ return MM_FILEPAGES;
+}
+
+static inline int mm_counter(struct page *page)
+{
+ if (PageAnon(page))
+ return MM_ANONPAGES;
+ return mm_counter_file(page);
+}
+
static inline unsigned long get_mm_rss(struct mm_struct *mm)
{
return get_mm_counter(mm, MM_FILEPAGES) +
- get_mm_counter(mm, MM_ANONPAGES);
+ get_mm_counter(mm, MM_ANONPAGES) +
+ get_mm_counter(mm, MM_SHMEMPAGES);
}
static inline unsigned long get_mm_hiwater_rss(struct mm_struct *mm)
@@ -1898,7 +1929,9 @@ extern void mm_drop_all_locks(struct mm_struct *mm);
extern void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file);
extern struct file *get_mm_exe_file(struct mm_struct *mm);
-extern int may_expand_vm(struct mm_struct *mm, unsigned long npages);
+extern bool may_expand_vm(struct mm_struct *, vm_flags_t, unsigned long npages);
+extern void vm_stat_account(struct mm_struct *, vm_flags_t, long npages);
+
extern struct vm_area_struct *_install_special_mapping(struct mm_struct *mm,
unsigned long addr, unsigned long len,
unsigned long flags,
@@ -2116,15 +2149,6 @@ typedef int (*pte_fn_t)(pte_t *pte, pgtable_t token, unsigned long addr,
extern int apply_to_page_range(struct mm_struct *mm, unsigned long address,
unsigned long size, pte_fn_t fn, void *data);
-#ifdef CONFIG_PROC_FS
-void vm_stat_account(struct mm_struct *, unsigned long, struct file *, long);
-#else
-static inline void vm_stat_account(struct mm_struct *mm,
- unsigned long flags, struct file *file, long pages)
-{
- mm->total_vm += pages;
-}
-#endif /* CONFIG_PROC_FS */
#ifdef CONFIG_DEBUG_PAGEALLOC
extern bool _debug_pagealloc_enabled;
diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h
index cf55945c83fb..712e8c37a200 100644
--- a/include/linux/mm_inline.h
+++ b/include/linux/mm_inline.h
@@ -100,4 +100,6 @@ static __always_inline enum lru_list page_lru(struct page *page)
return lru;
}
+#define lru_to_page(head) (list_entry((head)->prev, struct page, lru))
+
#endif
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index f8d1492a114f..6bc9a0ce2253 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -369,9 +369,10 @@ struct core_state {
};
enum {
- MM_FILEPAGES,
- MM_ANONPAGES,
- MM_SWAPENTS,
+ MM_FILEPAGES, /* Resident file mapping pages */
+ MM_ANONPAGES, /* Resident anonymous pages */
+ MM_SWAPENTS, /* Anonymous swap entries */
+ MM_SHMEMPAGES, /* Resident shared memory pages */
NR_MM_COUNTERS
};
@@ -426,7 +427,7 @@ struct mm_struct {
unsigned long total_vm; /* Total pages mapped */
unsigned long locked_vm; /* Pages that have PG_mlocked set */
unsigned long pinned_vm; /* Refcount permanently increased */
- unsigned long shared_vm; /* Shared pages (files) */
+ unsigned long data_vm; /* VM_WRITE & ~VM_SHARED/GROWSDOWN */
unsigned long exec_vm; /* VM_EXEC & ~VM_WRITE */
unsigned long stack_vm; /* VM_GROWSUP/DOWN */
unsigned long def_flags;
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
index f67b2ec18e6d..89df7abedd67 100644
--- a/include/linux/mmc/dw_mmc.h
+++ b/include/linux/mmc/dw_mmc.h
@@ -172,7 +172,7 @@ struct dw_mci {
/* For edmac */
struct dw_mci_dma_slave *dms;
/* Registers's physical base address */
- void *phy_regs;
+ resource_size_t phy_regs;
u32 cmd_status;
u32 data_status;
@@ -235,16 +235,10 @@ struct dw_mci_dma_ops {
};
/* IP Quirks/flags. */
-/* DTO fix for command transmission with IDMAC configured */
-#define DW_MCI_QUIRK_IDMAC_DTO BIT(0)
-/* delay needed between retries on some 2.11a implementations */
-#define DW_MCI_QUIRK_RETRY_DELAY BIT(1)
-/* High Speed Capable - Supports HS cards (up to 50MHz) */
-#define DW_MCI_QUIRK_HIGHSPEED BIT(2)
/* Unreliable card detection */
-#define DW_MCI_QUIRK_BROKEN_CARD_DETECTION BIT(3)
+#define DW_MCI_QUIRK_BROKEN_CARD_DETECTION BIT(0)
/* Timer for broken data transfer over scheme */
-#define DW_MCI_QUIRK_BROKEN_DTO BIT(4)
+#define DW_MCI_QUIRK_BROKEN_DTO BIT(1)
struct dma_pdata;
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 8673ffe3d86e..8dd4d290ab0d 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -212,7 +212,9 @@ struct mmc_host {
u32 ocr_avail_sdio; /* SDIO-specific OCR */
u32 ocr_avail_sd; /* SD-specific OCR */
u32 ocr_avail_mmc; /* MMC-specific OCR */
+#ifdef CONFIG_PM_SLEEP
struct notifier_block pm_notify;
+#endif
u32 max_current_330;
u32 max_current_300;
u32 max_current_180;
@@ -259,7 +261,6 @@ struct mmc_host {
#define MMC_CAP_UHS_SDR50 (1 << 17) /* Host supports UHS SDR50 mode */
#define MMC_CAP_UHS_SDR104 (1 << 18) /* Host supports UHS SDR104 mode */
#define MMC_CAP_UHS_DDR50 (1 << 19) /* Host supports UHS DDR50 mode */
-#define MMC_CAP_RUNTIME_RESUME (1 << 20) /* Resume at runtime_resume. */
#define MMC_CAP_DRIVER_TYPE_A (1 << 23) /* Host supports Driver Type A */
#define MMC_CAP_DRIVER_TYPE_C (1 << 24) /* Host supports Driver Type C */
#define MMC_CAP_DRIVER_TYPE_D (1 << 25) /* Host supports Driver Type D */
@@ -289,6 +290,7 @@ struct mmc_host {
#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 */
+#define MMC_CAP2_NO_SDIO (1 << 19) /* Do not send SDIO commands during initialization */
mmc_pm_flag_t pm_caps; /* supported pm features */
@@ -434,8 +436,6 @@ static inline int mmc_regulator_set_vqmmc(struct mmc_host *mmc,
int mmc_regulator_get_supply(struct mmc_host *mmc);
-int mmc_pm_notify(struct notifier_block *notify_block, unsigned long, void *);
-
static inline int mmc_card_is_removable(struct mmc_host *host)
{
return !(host->caps & MMC_CAP_NONREMOVABLE);
diff --git a/include/linux/mmdebug.h b/include/linux/mmdebug.h
index 877ef226f90f..772362adf471 100644
--- a/include/linux/mmdebug.h
+++ b/include/linux/mmdebug.h
@@ -1,6 +1,7 @@
#ifndef LINUX_MM_DEBUG_H
#define LINUX_MM_DEBUG_H 1
+#include <linux/bug.h>
#include <linux/stringify.h>
struct page;
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index e23a9e704536..33bb1b19273e 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -195,11 +195,6 @@ static inline int is_active_lru(enum lru_list lru)
return (lru == LRU_ACTIVE_ANON || lru == LRU_ACTIVE_FILE);
}
-static inline int is_unevictable_lru(enum lru_list lru)
-{
- return (lru == LRU_UNEVICTABLE);
-}
-
struct zone_reclaim_stat {
/*
* The pageout code in vmscan.c keeps track of how many of the
@@ -361,10 +356,10 @@ struct zone {
struct per_cpu_pageset __percpu *pageset;
/*
- * This is a per-zone reserve of pages that should not be
- * considered dirtyable memory.
+ * This is a per-zone reserve of pages that are not available
+ * to userspace allocations.
*/
- unsigned long dirty_balance_reserve;
+ unsigned long totalreserve_pages;
#ifndef CONFIG_SPARSEMEM
/*
@@ -576,19 +571,17 @@ static inline bool zone_is_empty(struct zone *zone)
/* Maximum number of zones on a zonelist */
#define MAX_ZONES_PER_ZONELIST (MAX_NUMNODES * MAX_NR_ZONES)
+enum {
+ ZONELIST_FALLBACK, /* zonelist with fallback */
#ifdef CONFIG_NUMA
-
-/*
- * The NUMA zonelists are doubled because we need zonelists that restrict the
- * allocations to a single node for __GFP_THISNODE.
- *
- * [0] : Zonelist with fallback
- * [1] : No fallback (__GFP_THISNODE)
- */
-#define MAX_ZONELISTS 2
-#else
-#define MAX_ZONELISTS 1
+ /*
+ * The NUMA zonelists are doubled because we need zonelists that
+ * restrict the allocations to a single node for __GFP_THISNODE.
+ */
+ ZONELIST_NOFALLBACK, /* zonelist without fallback (__GFP_THISNODE) */
#endif
+ MAX_ZONELISTS
+};
/*
* This struct contains information about a zone in a zonelist. It is stored
@@ -1207,13 +1200,13 @@ unsigned long __init node_memmap_size_bytes(int, unsigned long, unsigned long);
* the zone and PFN linkages are still valid. This is expensive, but walkers
* of the full memmap are extremely rare.
*/
-int memmap_valid_within(unsigned long pfn,
+bool memmap_valid_within(unsigned long pfn,
struct page *page, struct zone *zone);
#else
-static inline int memmap_valid_within(unsigned long pfn,
+static inline bool memmap_valid_within(unsigned long pfn,
struct page *page, struct zone *zone)
{
- return 1;
+ return true;
}
#endif /* CONFIG_ARCH_HAS_HOLES_MEMORYMODEL */
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index 64f36e09a790..6e4c645e1c0d 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -404,7 +404,7 @@ struct virtio_device_id {
* For Hyper-V devices we use the device guid as the id.
*/
struct hv_vmbus_device_id {
- __u8 guid[16];
+ uuid_le guid;
kernel_ulong_t driver_data; /* Data private to the driver */
};
diff --git a/include/linux/module.h b/include/linux/module.h
index 3a19c79918e0..4560d8f1545d 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -302,6 +302,28 @@ struct mod_tree_node {
struct latch_tree_node node;
};
+struct module_layout {
+ /* The actual code + data. */
+ void *base;
+ /* Total size. */
+ unsigned int size;
+ /* The size of the executable code. */
+ unsigned int text_size;
+ /* Size of RO section of the module (text+rodata) */
+ unsigned int ro_size;
+
+#ifdef CONFIG_MODULES_TREE_LOOKUP
+ struct mod_tree_node mtn;
+#endif
+};
+
+#ifdef CONFIG_MODULES_TREE_LOOKUP
+/* Only touch one cacheline for common rbtree-for-core-layout case. */
+#define __module_layout_align ____cacheline_aligned
+#else
+#define __module_layout_align
+#endif
+
struct module {
enum module_state state;
@@ -366,37 +388,9 @@ struct module {
/* Startup function. */
int (*init)(void);
- /*
- * If this is non-NULL, vfree() after init() returns.
- *
- * Cacheline align here, such that:
- * module_init, module_core, init_size, core_size,
- * init_text_size, core_text_size and mtn_core::{mod,node[0]}
- * are on the same cacheline.
- */
- void *module_init ____cacheline_aligned;
-
- /* Here is the actual code + data, vfree'd on unload. */
- void *module_core;
-
- /* Here are the sizes of the init and core sections */
- unsigned int init_size, core_size;
-
- /* The size of the executable code in each section. */
- unsigned int init_text_size, core_text_size;
-
-#ifdef CONFIG_MODULES_TREE_LOOKUP
- /*
- * We want mtn_core::{mod,node[0]} to be in the same cacheline as the
- * above entries such that a regular lookup will only touch one
- * cacheline.
- */
- struct mod_tree_node mtn_core;
- struct mod_tree_node mtn_init;
-#endif
-
- /* Size of RO sections of the module (text+rodata) */
- unsigned int init_ro_size, core_ro_size;
+ /* Core layout: rbtree is accessed frequently, so keep together. */
+ struct module_layout core_layout __module_layout_align;
+ struct module_layout init_layout;
/* Arch-specific module values */
struct mod_arch_specific arch;
@@ -505,15 +499,15 @@ bool is_module_text_address(unsigned long addr);
static inline bool within_module_core(unsigned long addr,
const struct module *mod)
{
- return (unsigned long)mod->module_core <= addr &&
- addr < (unsigned long)mod->module_core + mod->core_size;
+ return (unsigned long)mod->core_layout.base <= addr &&
+ addr < (unsigned long)mod->core_layout.base + mod->core_layout.size;
}
static inline bool within_module_init(unsigned long addr,
const struct module *mod)
{
- return (unsigned long)mod->module_init <= addr &&
- addr < (unsigned long)mod->module_init + mod->init_size;
+ return (unsigned long)mod->init_layout.base <= addr &&
+ addr < (unsigned long)mod->init_layout.base + mod->init_layout.size;
}
static inline bool within_module(unsigned long addr, const struct module *mod)
@@ -768,9 +762,13 @@ extern int module_sysfs_initialized;
#ifdef CONFIG_DEBUG_SET_MODULE_RONX
extern void set_all_modules_text_rw(void);
extern void set_all_modules_text_ro(void);
+extern void module_enable_ro(const struct module *mod);
+extern void module_disable_ro(const struct module *mod);
#else
static inline void set_all_modules_text_rw(void) { }
static inline void set_all_modules_text_ro(void) { }
+static inline void module_enable_ro(const struct module *mod) { }
+static inline void module_disable_ro(const struct module *mod) { }
#endif
#ifdef CONFIG_GENERIC_BUG
diff --git a/include/linux/mroute.h b/include/linux/mroute.h
index 79aaa9fc1a15..bf9b322cb0b0 100644
--- a/include/linux/mroute.h
+++ b/include/linux/mroute.h
@@ -9,38 +9,28 @@
#ifdef CONFIG_IP_MROUTE
static inline int ip_mroute_opt(int opt)
{
- return (opt >= MRT_BASE) && (opt <= MRT_MAX);
+ return opt >= MRT_BASE && opt <= MRT_MAX;
}
-#else
-static inline int ip_mroute_opt(int opt)
-{
- return 0;
-}
-#endif
-#ifdef CONFIG_IP_MROUTE
-extern int ip_mroute_setsockopt(struct sock *, int, char __user *, unsigned int);
-extern int ip_mroute_getsockopt(struct sock *, int, char __user *, int __user *);
-extern int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg);
-extern int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg);
-extern int ip_mr_init(void);
+int ip_mroute_setsockopt(struct sock *, int, char __user *, unsigned int);
+int ip_mroute_getsockopt(struct sock *, int, char __user *, int __user *);
+int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg);
+int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg);
+int ip_mr_init(void);
#else
-static inline
-int ip_mroute_setsockopt(struct sock *sock,
- int optname, char __user *optval, unsigned int optlen)
+static inline int ip_mroute_setsockopt(struct sock *sock, int optname,
+ char __user *optval, unsigned int optlen)
{
return -ENOPROTOOPT;
}
-static inline
-int ip_mroute_getsockopt(struct sock *sock,
- int optname, char __user *optval, int __user *optlen)
+static inline int ip_mroute_getsockopt(struct sock *sock, int optname,
+ char __user *optval, int __user *optlen)
{
return -ENOPROTOOPT;
}
-static inline
-int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
+static inline int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
{
return -ENOIOCTLCMD;
}
@@ -49,6 +39,11 @@ static inline int ip_mr_init(void)
{
return 0;
}
+
+static inline int ip_mroute_opt(int opt)
+{
+ return 0;
+}
#endif
struct vif_device {
@@ -64,6 +59,32 @@ struct vif_device {
#define VIFF_STATIC 0x8000
+#define VIF_EXISTS(_mrt, _idx) ((_mrt)->vif_table[_idx].dev != NULL)
+#define MFC_LINES 64
+
+struct mr_table {
+ struct list_head list;
+ possible_net_t net;
+ u32 id;
+ struct sock __rcu *mroute_sk;
+ struct timer_list ipmr_expire_timer;
+ struct list_head mfc_unres_queue;
+ struct list_head mfc_cache_array[MFC_LINES];
+ struct vif_device vif_table[MAXVIFS];
+ int maxvif;
+ atomic_t cache_resolve_queue_len;
+ bool mroute_do_assert;
+ bool mroute_do_pim;
+ int mroute_reg_vif_num;
+};
+
+/* mfc_flags:
+ * MFC_STATIC - the entry was added statically (not by a routing daemon)
+ */
+enum {
+ MFC_STATIC = BIT(0),
+};
+
struct mfc_cache {
struct list_head list;
__be32 mfc_mcastgrp; /* Group the entry belongs to */
@@ -89,19 +110,14 @@ struct mfc_cache {
struct rcu_head rcu;
};
-#define MFC_STATIC 1
-#define MFC_NOTIFY 2
-
-#define MFC_LINES 64
-
#ifdef __BIG_ENDIAN
#define MFC_HASH(a,b) (((((__force u32)(__be32)a)>>24)^(((__force u32)(__be32)b)>>26))&(MFC_LINES-1))
#else
#define MFC_HASH(a,b) ((((__force u32)(__be32)a)^(((__force u32)(__be32)b)>>2))&(MFC_LINES-1))
-#endif
+#endif
struct rtmsg;
-extern int ipmr_get_route(struct net *net, struct sk_buff *skb,
- __be32 saddr, __be32 daddr,
- struct rtmsg *rtm, int nowait);
+int ipmr_get_route(struct net *net, struct sk_buff *skb,
+ __be32 saddr, __be32 daddr,
+ struct rtmsg *rtm, int nowait);
#endif
diff --git a/include/linux/msi.h b/include/linux/msi.h
index f71a25e5fd25..1c6342ab8c0e 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -174,6 +174,7 @@ struct msi_controller {
#include <asm/msi.h>
struct irq_domain;
+struct irq_domain_ops;
struct irq_chip;
struct device_node;
struct fwnode_handle;
@@ -279,6 +280,23 @@ struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
irq_write_msi_msg_t write_msi_msg);
void platform_msi_domain_free_irqs(struct device *dev);
+
+/* When an MSI domain is used as an intermediate domain */
+int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
+ int nvec, msi_alloc_info_t *args);
+int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev,
+ int virq, int nvec, msi_alloc_info_t *args);
+struct irq_domain *
+platform_msi_create_device_domain(struct device *dev,
+ unsigned int nvec,
+ irq_write_msi_msg_t write_msi_msg,
+ const struct irq_domain_ops *ops,
+ void *host_data);
+int platform_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs);
+void platform_msi_domain_free(struct irq_domain *domain, unsigned int virq,
+ unsigned int nvec);
+void *platform_msi_get_host_data(struct irq_domain *domain);
#endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */
#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN
diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h
index 366cf77953b5..58f3ba709ade 100644
--- a/include/linux/mtd/map.h
+++ b/include/linux/mtd/map.h
@@ -142,7 +142,9 @@
#endif
#ifndef map_bankwidth
+#ifdef CONFIG_MTD
#warning "No CONFIG_MTD_MAP_BANK_WIDTH_xx selected. No NOR chip support can work"
+#endif
static inline int map_bankwidth(void *map)
{
BUG();
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index f17fa75809aa..cc84923011c0 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -254,6 +254,17 @@ struct mtd_info {
int usecount;
};
+static inline void mtd_set_of_node(struct mtd_info *mtd,
+ struct device_node *np)
+{
+ mtd->dev.of_node = np;
+}
+
+static inline struct device_node *mtd_get_of_node(struct mtd_info *mtd)
+{
+ return mtd->dev.of_node;
+}
+
int mtd_erase(struct mtd_info *mtd, struct erase_info *instr);
int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
void **virt, resource_size_t *phys);
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 5a9d1d4c2487..bdd68e22b5a5 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -129,6 +129,14 @@ typedef enum {
/* Enable Hardware ECC before syndrome is read back from flash */
#define NAND_ECC_READSYN 2
+/*
+ * Enable generic NAND 'page erased' check. This check is only done when
+ * ecc.correct() returns -EBADMSG.
+ * Set this flag if your implementation does not fix bitflips in erased
+ * pages and you want to rely on the default implementation.
+ */
+#define NAND_ECC_GENERIC_ERASED_CHECK BIT(0)
+
/* Bit mask for flags passed to do_nand_read_ecc */
#define NAND_GET_DEVICE 0x80
@@ -276,15 +284,15 @@ struct nand_onfi_params {
__le16 t_r;
__le16 t_ccs;
__le16 src_sync_timing_mode;
- __le16 src_ssync_features;
+ u8 src_ssync_features;
__le16 clk_pin_capacitance_typ;
__le16 io_pin_capacitance_typ;
__le16 input_pin_capacitance_typ;
u8 input_pin_capacitance_max;
u8 driver_strength_support;
__le16 t_int_r;
- __le16 t_ald;
- u8 reserved4[7];
+ __le16 t_adl;
+ u8 reserved4[8];
/* vendor */
__le16 vendor_revision;
@@ -407,7 +415,7 @@ struct nand_jedec_params {
__le16 input_pin_capacitance_typ;
__le16 clk_pin_capacitance_typ;
u8 driver_strength_support;
- __le16 t_ald;
+ __le16 t_adl;
u8 reserved4[36];
/* ECC and endurance block */
@@ -451,12 +459,19 @@ struct nand_hw_control {
* @total: total number of ECC bytes per page
* @prepad: padding information for syndrome based ECC generators
* @postpad: padding information for syndrome based ECC generators
+ * @options: ECC specific options (see NAND_ECC_XXX flags defined above)
* @layout: ECC layout control struct pointer
* @priv: pointer to private ECC control data
* @hwctl: function to control hardware ECC generator. Must only
* be provided if an hardware ECC is available
* @calculate: function for ECC calculation or readback from ECC hardware
- * @correct: function for ECC correction, matching to ECC generator (sw/hw)
+ * @correct: function for ECC correction, matching to ECC generator (sw/hw).
+ * Should return a positive number representing the number of
+ * corrected bitflips, -EBADMSG if the number of bitflips exceed
+ * ECC strength, or any other error code if the error is not
+ * directly related to correction.
+ * If -EBADMSG is returned the input buffers should be left
+ * untouched.
* @read_page_raw: function to read a raw page without ECC. This function
* should hide the specific layout used by the ECC
* controller and always return contiguous in-band and
@@ -494,6 +509,7 @@ struct nand_ecc_ctrl {
int strength;
int prepad;
int postpad;
+ unsigned int options;
struct nand_ecclayout *layout;
void *priv;
void (*hwctl)(struct mtd_info *mtd, int mode);
@@ -540,11 +556,11 @@ struct nand_buffers {
/**
* struct nand_chip - NAND Private Flash Chip Data
+ * @mtd: MTD device registered to the MTD framework
* @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the
* flash device
* @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the
* flash device.
- * @flash_node: [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
@@ -640,11 +656,10 @@ struct nand_buffers {
*/
struct nand_chip {
+ struct mtd_info mtd;
void __iomem *IO_ADDR_R;
void __iomem *IO_ADDR_W;
- struct device_node *flash_node;
-
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);
@@ -719,6 +734,37 @@ struct nand_chip {
void *priv;
};
+static inline void nand_set_flash_node(struct nand_chip *chip,
+ struct device_node *np)
+{
+ mtd_set_of_node(&chip->mtd, np);
+}
+
+static inline struct device_node *nand_get_flash_node(struct nand_chip *chip)
+{
+ return mtd_get_of_node(&chip->mtd);
+}
+
+static inline struct nand_chip *mtd_to_nand(struct mtd_info *mtd)
+{
+ return container_of(mtd, struct nand_chip, mtd);
+}
+
+static inline struct mtd_info *nand_to_mtd(struct nand_chip *chip)
+{
+ return &chip->mtd;
+}
+
+static inline void *nand_get_controller_data(struct nand_chip *chip)
+{
+ return chip->priv;
+}
+
+static inline void nand_set_controller_data(struct nand_chip *chip, void *priv)
+{
+ chip->priv = priv;
+}
+
/*
* NAND Flash Manufacturer ID Codes
*/
@@ -907,15 +953,6 @@ struct platform_nand_data {
struct platform_nand_ctrl ctrl;
};
-/* Some helpers to access the data structures */
-static inline
-struct platform_nand_chip *get_platform_nandchip(struct mtd_info *mtd)
-{
- struct nand_chip *chip = mtd->priv;
-
- return chip->priv;
-}
-
/* return the supported features. */
static inline int onfi_feature(struct nand_chip *chip)
{
diff --git a/include/linux/mtd/nand_bch.h b/include/linux/mtd/nand_bch.h
index 74acf5367556..fb0bc3420a10 100644
--- a/include/linux/mtd/nand_bch.h
+++ b/include/linux/mtd/nand_bch.h
@@ -55,7 +55,7 @@ static inline int
nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf,
unsigned char *read_ecc, unsigned char *calc_ecc)
{
- return -1;
+ return -ENOTSUPP;
}
static inline struct nand_bch_control *
diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h
index 6a35e6de5da1..70736e1e6c8f 100644
--- a/include/linux/mtd/partitions.h
+++ b/include/linux/mtd/partitions.h
@@ -41,7 +41,6 @@ struct mtd_partition {
uint64_t size; /* partition size */
uint64_t offset; /* offset within the master MTD space */
uint32_t mask_flags; /* master MTD flags to mask out for this partition */
- struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only) */
};
#define MTDPART_OFS_RETAIN (-3)
@@ -56,11 +55,9 @@ struct device_node;
/**
* struct mtd_part_parser_data - used to pass data to MTD partition parsers.
* @origin: for RedBoot, start address of MTD device
- * @of_node: for OF parsers, device node containing partitioning information
*/
struct mtd_part_parser_data {
unsigned long origin;
- struct device_node *of_node;
};
@@ -72,13 +69,33 @@ struct mtd_part_parser {
struct list_head list;
struct module *owner;
const char *name;
- int (*parse_fn)(struct mtd_info *, struct mtd_partition **,
+ int (*parse_fn)(struct mtd_info *, const struct mtd_partition **,
struct mtd_part_parser_data *);
+ void (*cleanup)(const struct mtd_partition *pparts, int nr_parts);
};
-extern void register_mtd_parser(struct mtd_part_parser *parser);
+/* Container for passing around a set of parsed partitions */
+struct mtd_partitions {
+ const struct mtd_partition *parts;
+ int nr_parts;
+ const struct mtd_part_parser *parser;
+};
+
+extern int __register_mtd_parser(struct mtd_part_parser *parser,
+ struct module *owner);
+#define register_mtd_parser(parser) __register_mtd_parser(parser, THIS_MODULE)
+
extern void deregister_mtd_parser(struct mtd_part_parser *parser);
+/*
+ * module_mtd_part_parser() - Helper macro for MTD partition parsers that don't
+ * do anything special in module init/exit. Each driver may only use this macro
+ * once, and calling it replaces module_init() and module_exit().
+ */
+#define module_mtd_part_parser(__mtd_part_parser) \
+ module_driver(__mtd_part_parser, register_mtd_parser, \
+ deregister_mtd_parser)
+
int mtd_is_partition(const struct mtd_info *mtd);
int mtd_add_partition(struct mtd_info *master, const char *name,
long long offset, long long length);
diff --git a/include/linux/mtd/sh_flctl.h b/include/linux/mtd/sh_flctl.h
index 1c28f8879b1c..2251add65fa7 100644
--- a/include/linux/mtd/sh_flctl.h
+++ b/include/linux/mtd/sh_flctl.h
@@ -143,11 +143,11 @@ enum flctl_ecc_res_t {
struct dma_chan;
struct sh_flctl {
- struct mtd_info mtd;
struct nand_chip chip;
struct platform_device *pdev;
struct dev_pm_qos_request pm_qos;
void __iomem *reg;
+ resource_size_t fifo;
uint8_t done_buff[2048 + 64]; /* max size 2048 + 64 */
int read_bytes;
@@ -186,7 +186,7 @@ struct sh_flctl_platform_data {
static inline struct sh_flctl *mtd_to_flctl(struct mtd_info *mtdinfo)
{
- return container_of(mtdinfo, struct sh_flctl, mtd);
+ return container_of(mtd_to_nand(mtdinfo), struct sh_flctl, chip);
}
#endif /* __SH_FLCTL_H__ */
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index c8723b62c4cd..62356d50815b 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -12,6 +12,7 @@
#include <linux/bitops.h>
#include <linux/mtd/cfi.h>
+#include <linux/mtd/mtd.h>
/*
* Manufacturer IDs
@@ -25,7 +26,7 @@
#define SNOR_MFR_MACRONIX CFI_MFR_MACRONIX
#define SNOR_MFR_SPANSION CFI_MFR_AMD
#define SNOR_MFR_SST CFI_MFR_SST
-#define SNOR_MFR_WINBOND 0xef
+#define SNOR_MFR_WINBOND 0xef /* Also used by some Spansion */
/*
* Note on opcode nomenclature: some opcodes have a format like
@@ -117,14 +118,11 @@ enum spi_nor_option_flags {
SNOR_F_USE_FSR = BIT(0),
};
-struct mtd_info;
-
/**
* struct spi_nor - Structure for defining a the SPI NOR layer
* @mtd: point to a mtd_info structure
* @lock: the lock for the read/write/erase/lock/unlock operations
* @dev: point to a spi device, or a spi nor controller device.
- * @flash_node: point to a device node describing this flash instance.
* @page_size: the page size of the SPI NOR
* @addr_width: number of address bytes
* @erase_opcode: the opcode for erasing a sector
@@ -144,7 +142,8 @@ struct mtd_info;
* @read: [DRIVER-SPECIFIC] read data from the SPI NOR
* @write: [DRIVER-SPECIFIC] write data to the SPI NOR
* @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR
- * at the offset @offs
+ * at the offset @offs; if not provided by the driver,
+ * spi-nor will send the erase opcode via write_reg()
* @flash_lock: [FLASH-SPECIFIC] lock a region of the SPI NOR
* @flash_unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR
* @flash_is_locked: [FLASH-SPECIFIC] check if a region of the SPI NOR is
@@ -155,7 +154,6 @@ struct spi_nor {
struct mtd_info mtd;
struct mutex lock;
struct device *dev;
- struct device_node *flash_node;
u32 page_size;
u8 addr_width;
u8 erase_opcode;
@@ -185,6 +183,17 @@ struct spi_nor {
void *priv;
};
+static inline void spi_nor_set_flash_node(struct spi_nor *nor,
+ struct device_node *np)
+{
+ mtd_set_of_node(&nor->mtd, np);
+}
+
+static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
+{
+ return mtd_get_of_node(&nor->mtd);
+}
+
/**
* spi_nor_scan() - scan the SPI NOR
* @nor: the spi_nor structure
diff --git a/include/linux/namei.h b/include/linux/namei.h
index d8c6334cd150..d0f25d81b46a 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -77,6 +77,7 @@ extern struct dentry *kern_path_locked(const char *, struct path *);
extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int);
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
+extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int);
extern int follow_down_one(struct path *);
extern int follow_down(struct path *);
diff --git a/include/linux/net.h b/include/linux/net.h
index 70ac5e28e6b7..0b4ac7da583a 100644
--- a/include/linux/net.h
+++ b/include/linux/net.h
@@ -34,8 +34,12 @@ struct inode;
struct file;
struct net;
-#define SOCK_ASYNC_NOSPACE 0
-#define SOCK_ASYNC_WAITDATA 1
+/* Historically, SOCKWQ_ASYNC_NOSPACE & SOCKWQ_ASYNC_WAITDATA were located
+ * in sock->flags, but moved into sk->sk_wq->flags to be RCU protected.
+ * Eventually all flags will be in sk->sk_wq_flags.
+ */
+#define SOCKWQ_ASYNC_NOSPACE 0
+#define SOCKWQ_ASYNC_WAITDATA 1
#define SOCK_NOSPACE 2
#define SOCK_PASSCRED 3
#define SOCK_PASSSEC 4
@@ -89,6 +93,7 @@ struct socket_wq {
/* Note: wait MUST be first field of socket_wq */
wait_queue_head_t wait;
struct fasync_struct *fasync_list;
+ unsigned long flags; /* %SOCKWQ_ASYNC_NOSPACE, etc */
struct rcu_head rcu;
} ____cacheline_aligned_in_smp;
@@ -96,7 +101,7 @@ struct socket_wq {
* struct socket - general BSD socket
* @state: socket state (%SS_CONNECTED, etc)
* @type: socket type (%SOCK_STREAM, etc)
- * @flags: socket flags (%SOCK_ASYNC_NOSPACE, etc)
+ * @flags: socket flags (%SOCK_NOSPACE, etc)
* @ops: protocol specific socket operations
* @file: File back pointer for gc
* @sk: internal networking protocol agnostic socket representation
@@ -202,7 +207,7 @@ enum {
SOCK_WAKE_URG,
};
-int sock_wake_async(struct socket *sk, int how, int band);
+int sock_wake_async(struct socket_wq *sk_wq, int how, int band);
int sock_register(const struct net_proto_family *fam);
void sock_unregister(int family);
int __sock_create(struct net *net, int family, int type, int proto,
diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h
index f0d87347df19..d9654f0eecb3 100644
--- a/include/linux/netdev_features.h
+++ b/include/linux/netdev_features.h
@@ -52,7 +52,7 @@ enum {
NETIF_F_GSO_TUNNEL_REMCSUM_BIT,
NETIF_F_FCOE_CRC_BIT, /* FCoE CRC32 */
- NETIF_F_SCTP_CSUM_BIT, /* SCTP checksum offload */
+ NETIF_F_SCTP_CRC_BIT, /* SCTP checksum offload */
NETIF_F_FCOE_MTU_BIT, /* Supports max FCoE MTU, 2158 bytes*/
NETIF_F_NTUPLE_BIT, /* N-tuple filters supported */
NETIF_F_RXHASH_BIT, /* Receive hashing offload */
@@ -103,7 +103,7 @@ enum {
#define NETIF_F_NTUPLE __NETIF_F(NTUPLE)
#define NETIF_F_RXCSUM __NETIF_F(RXCSUM)
#define NETIF_F_RXHASH __NETIF_F(RXHASH)
-#define NETIF_F_SCTP_CSUM __NETIF_F(SCTP_CSUM)
+#define NETIF_F_SCTP_CRC __NETIF_F(SCTP_CRC)
#define NETIF_F_SG __NETIF_F(SG)
#define NETIF_F_TSO6 __NETIF_F(TSO6)
#define NETIF_F_TSO_ECN __NETIF_F(TSO_ECN)
@@ -146,10 +146,12 @@ enum {
#define NETIF_F_GSO_SOFTWARE (NETIF_F_TSO | NETIF_F_TSO_ECN | \
NETIF_F_TSO6 | NETIF_F_UFO)
-#define NETIF_F_GEN_CSUM NETIF_F_HW_CSUM
-#define NETIF_F_V4_CSUM (NETIF_F_GEN_CSUM | NETIF_F_IP_CSUM)
-#define NETIF_F_V6_CSUM (NETIF_F_GEN_CSUM | NETIF_F_IPV6_CSUM)
-#define NETIF_F_ALL_CSUM (NETIF_F_V4_CSUM | NETIF_F_V6_CSUM)
+/* List of IP checksum features. Note that NETIF_F_ HW_CSUM should not be
+ * set in features when NETIF_F_IP_CSUM or NETIF_F_IPV6_CSUM are set--
+ * this would be contradictory
+ */
+#define NETIF_F_CSUM_MASK (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | \
+ NETIF_F_HW_CSUM)
#define NETIF_F_ALL_TSO (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 67bfac1abfc1..5ac140dcb789 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -132,7 +132,9 @@ static inline bool dev_xmit_complete(int rc)
* used.
*/
-#if defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25)
+#if defined(CONFIG_HYPERV_NET)
+# define LL_MAX_HEADER 128
+#elif defined(CONFIG_WLAN) || IS_ENABLED(CONFIG_AX25)
# if defined(CONFIG_MAC80211_MESH)
# define LL_MAX_HEADER 128
# else
@@ -326,7 +328,8 @@ enum {
NAPI_STATE_SCHED, /* Poll is scheduled */
NAPI_STATE_DISABLE, /* Disable pending */
NAPI_STATE_NPSVC, /* Netpoll - don't dequeue from poll_list */
- NAPI_STATE_HASHED, /* In NAPI hash */
+ NAPI_STATE_HASHED, /* In NAPI hash (busy polling possible) */
+ NAPI_STATE_NO_BUSY_POLL,/* Do not add in napi_hash, no busy polling */
};
enum gro_result {
@@ -461,19 +464,13 @@ static inline void napi_complete(struct napi_struct *n)
}
/**
- * napi_by_id - lookup a NAPI by napi_id
- * @napi_id: hashed napi_id
- *
- * lookup @napi_id in napi_hash table
- * must be called under rcu_read_lock()
- */
-struct napi_struct *napi_by_id(unsigned int napi_id);
-
-/**
* napi_hash_add - add a NAPI to global hashtable
* @napi: napi context
*
* generate a new napi_id and store a @napi under it in napi_hash
+ * Used for busy polling (CONFIG_NET_RX_BUSY_POLL)
+ * Note: This is normally automatically done from netif_napi_add(),
+ * so might disappear in a future linux version.
*/
void napi_hash_add(struct napi_struct *napi);
@@ -482,9 +479,14 @@ void napi_hash_add(struct napi_struct *napi);
* @napi: napi context
*
* Warning: caller must observe rcu grace period
- * before freeing memory containing @napi
+ * before freeing memory containing @napi, if
+ * this function returns true.
+ * Note: core networking stack automatically calls it
+ * from netif_napi_del()
+ * Drivers might want to call this helper to combine all
+ * the needed rcu grace periods into a single one.
*/
-void napi_hash_del(struct napi_struct *napi);
+bool napi_hash_del(struct napi_struct *napi);
/**
* napi_disable - prevent NAPI from scheduling
@@ -810,6 +812,12 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
* (can also return NETDEV_TX_LOCKED iff NETIF_F_LLTX)
* Required can not be NULL.
*
+ * netdev_features_t (*ndo_fix_features)(struct net_device *dev,
+ * netdev_features_t features);
+ * Adjusts the requested feature flags according to device-specific
+ * constraints, and returns the resulting flags. Must not modify
+ * the device state.
+ *
* u16 (*ndo_select_queue)(struct net_device *dev, struct sk_buff *skb,
* void *accel_priv, select_queue_fallback_t fallback);
* Called to decide which queue to when device supports multiple
@@ -957,12 +965,6 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
* Called to release previously enslaved netdev.
*
* Feature/offload setting functions.
- * netdev_features_t (*ndo_fix_features)(struct net_device *dev,
- * netdev_features_t features);
- * Adjusts the requested feature flags according to device-specific
- * constraints, and returns the resulting flags. Must not modify
- * the device state.
- *
* int (*ndo_set_features)(struct net_device *dev, netdev_features_t features);
* Called to update device configuration to new features. Passed
* feature set might be less than what was returned by ndo_fix_features()).
@@ -1011,6 +1013,19 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev,
* a new port starts listening. The operation is protected by the
* vxlan_net->sock_lock.
*
+ * void (*ndo_add_geneve_port)(struct net_device *dev,
+ * sa_family_t sa_family, __be16 port);
+ * Called by geneve to notify a driver about the UDP port and socket
+ * address family that geneve is listnening to. It is called only when
+ * a new port starts listening. The operation is protected by the
+ * geneve_net->sock_lock.
+ *
+ * void (*ndo_del_geneve_port)(struct net_device *dev,
+ * sa_family_t sa_family, __be16 port);
+ * Called by geneve to notify the driver about a UDP port and socket
+ * address family that geneve is not listening to anymore. The operation
+ * is protected by the geneve_net->sock_lock.
+ *
* void (*ndo_del_vxlan_port)(struct net_device *dev,
* sa_family_t sa_family, __be16 port);
* Called by vxlan to notify the driver about a UDP port and socket
@@ -1066,8 +1081,11 @@ struct net_device_ops {
void (*ndo_uninit)(struct net_device *dev);
int (*ndo_open)(struct net_device *dev);
int (*ndo_stop)(struct net_device *dev);
- netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb,
- struct net_device *dev);
+ netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb,
+ struct net_device *dev);
+ netdev_features_t (*ndo_features_check)(struct sk_buff *skb,
+ struct net_device *dev,
+ netdev_features_t features);
u16 (*ndo_select_queue)(struct net_device *dev,
struct sk_buff *skb,
void *accel_priv,
@@ -1215,7 +1233,12 @@ struct net_device_ops {
void (*ndo_del_vxlan_port)(struct net_device *dev,
sa_family_t sa_family,
__be16 port);
-
+ void (*ndo_add_geneve_port)(struct net_device *dev,
+ sa_family_t sa_family,
+ __be16 port);
+ void (*ndo_del_geneve_port)(struct net_device *dev,
+ sa_family_t sa_family,
+ __be16 port);
void* (*ndo_dfwd_add_station)(struct net_device *pdev,
struct net_device *dev);
void (*ndo_dfwd_del_station)(struct net_device *pdev,
@@ -1225,9 +1248,6 @@ struct net_device_ops {
struct net_device *dev,
void *priv);
int (*ndo_get_lock_subclass)(struct net_device *dev);
- netdev_features_t (*ndo_features_check) (struct sk_buff *skb,
- struct net_device *dev,
- netdev_features_t features);
int (*ndo_set_tx_maxrate)(struct net_device *dev,
int queue_index,
u32 maxrate);
@@ -1271,6 +1291,7 @@ struct net_device_ops {
* @IFF_NO_QUEUE: device can run without qdisc attached
* @IFF_OPENVSWITCH: device is a Open vSwitch master
* @IFF_L3MDEV_SLAVE: device is enslaved to an L3 master device
+ * @IFF_TEAM: device is a team device
*/
enum netdev_priv_flags {
IFF_802_1Q_VLAN = 1<<0,
@@ -1297,6 +1318,7 @@ enum netdev_priv_flags {
IFF_NO_QUEUE = 1<<21,
IFF_OPENVSWITCH = 1<<22,
IFF_L3MDEV_SLAVE = 1<<23,
+ IFF_TEAM = 1<<24,
};
#define IFF_802_1Q_VLAN IFF_802_1Q_VLAN
@@ -1323,6 +1345,7 @@ enum netdev_priv_flags {
#define IFF_NO_QUEUE IFF_NO_QUEUE
#define IFF_OPENVSWITCH IFF_OPENVSWITCH
#define IFF_L3MDEV_SLAVE IFF_L3MDEV_SLAVE
+#define IFF_TEAM IFF_TEAM
/**
* struct net_device - The DEVICE structure.
@@ -1398,7 +1421,8 @@ enum netdev_priv_flags {
* @dma: DMA channel
* @mtu: Interface MTU value
* @type: Interface hardware type
- * @hard_header_len: Hardware header length
+ * @hard_header_len: Hardware header length, which means that this is the
+ * minimum size of a packet.
*
* @needed_headroom: Extra headroom the hardware may need, but not in all
* cases can this be guaranteed
@@ -1715,7 +1739,9 @@ struct net_device {
#ifdef CONFIG_XPS
struct xps_dev_maps __rcu *xps_maps;
#endif
-
+#ifdef CONFIG_NET_CLS_ACT
+ struct tcf_proto __rcu *egress_cl_list;
+#endif
#ifdef CONFIG_NET_SWITCHDEV
u32 offload_fwd_mark;
#endif
@@ -1948,6 +1974,26 @@ void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
int (*poll)(struct napi_struct *, int), int weight);
/**
+ * netif_tx_napi_add - initialize a napi context
+ * @dev: network device
+ * @napi: napi context
+ * @poll: polling function
+ * @weight: default weight
+ *
+ * This variant of netif_napi_add() should be used from drivers using NAPI
+ * to exclusively poll a TX queue.
+ * This will avoid we add it into napi_hash[], thus polluting this hash table.
+ */
+static inline void netif_tx_napi_add(struct net_device *dev,
+ struct napi_struct *napi,
+ int (*poll)(struct napi_struct *, int),
+ int weight)
+{
+ set_bit(NAPI_STATE_NO_BUSY_POLL, &napi->state);
+ netif_napi_add(dev, napi, poll, weight);
+}
+
+/**
* netif_napi_del - remove a napi context
* @napi: napi context
*
@@ -2083,7 +2129,25 @@ struct pcpu_sw_netstats {
})
#define netdev_alloc_pcpu_stats(type) \
- __netdev_alloc_pcpu_stats(type, GFP_KERNEL);
+ __netdev_alloc_pcpu_stats(type, GFP_KERNEL)
+
+enum netdev_lag_tx_type {
+ NETDEV_LAG_TX_TYPE_UNKNOWN,
+ NETDEV_LAG_TX_TYPE_RANDOM,
+ NETDEV_LAG_TX_TYPE_BROADCAST,
+ NETDEV_LAG_TX_TYPE_ROUNDROBIN,
+ NETDEV_LAG_TX_TYPE_ACTIVEBACKUP,
+ NETDEV_LAG_TX_TYPE_HASH,
+};
+
+struct netdev_lag_upper_info {
+ enum netdev_lag_tx_type tx_type;
+};
+
+struct netdev_lag_lower_state_info {
+ u8 link_up : 1,
+ tx_enabled : 1;
+};
#include <linux/notifier.h>
@@ -2120,6 +2184,7 @@ struct pcpu_sw_netstats {
#define NETDEV_CHANGEINFODATA 0x0018
#define NETDEV_BONDING_INFO 0x0019
#define NETDEV_PRECHANGEUPPER 0x001A
+#define NETDEV_CHANGELOWERSTATE 0x001B
int register_netdevice_notifier(struct notifier_block *nb);
int unregister_netdevice_notifier(struct notifier_block *nb);
@@ -2138,6 +2203,12 @@ struct netdev_notifier_changeupper_info {
struct net_device *upper_dev; /* new upper dev */
bool master; /* is upper dev master */
bool linking; /* is the nofication for link or unlink */
+ void *upper_info; /* upper dev info */
+};
+
+struct netdev_notifier_changelowerstate_info {
+ struct netdev_notifier_info info; /* must be first */
+ void *lower_state_info; /* is lower dev state */
};
static inline void netdev_notifier_info_init(struct netdev_notifier_info *info,
@@ -2471,6 +2542,71 @@ static inline void skb_gro_remcsum_cleanup(struct sk_buff *skb,
remcsum_unadjust((__sum16 *)ptr, grc->delta);
}
+struct skb_csum_offl_spec {
+ __u16 ipv4_okay:1,
+ ipv6_okay:1,
+ encap_okay:1,
+ ip_options_okay:1,
+ ext_hdrs_okay:1,
+ tcp_okay:1,
+ udp_okay:1,
+ sctp_okay:1,
+ vlan_okay:1,
+ no_encapped_ipv6:1,
+ no_not_encapped:1;
+};
+
+bool __skb_csum_offload_chk(struct sk_buff *skb,
+ const struct skb_csum_offl_spec *spec,
+ bool *csum_encapped,
+ bool csum_help);
+
+static inline bool skb_csum_offload_chk(struct sk_buff *skb,
+ const struct skb_csum_offl_spec *spec,
+ bool *csum_encapped,
+ bool csum_help)
+{
+ if (skb->ip_summed != CHECKSUM_PARTIAL)
+ return false;
+
+ return __skb_csum_offload_chk(skb, spec, csum_encapped, csum_help);
+}
+
+static inline bool skb_csum_offload_chk_help(struct sk_buff *skb,
+ const struct skb_csum_offl_spec *spec)
+{
+ bool csum_encapped;
+
+ return skb_csum_offload_chk(skb, spec, &csum_encapped, true);
+}
+
+static inline bool skb_csum_off_chk_help_cmn(struct sk_buff *skb)
+{
+ static const struct skb_csum_offl_spec csum_offl_spec = {
+ .ipv4_okay = 1,
+ .ip_options_okay = 1,
+ .ipv6_okay = 1,
+ .vlan_okay = 1,
+ .tcp_okay = 1,
+ .udp_okay = 1,
+ };
+
+ return skb_csum_offload_chk_help(skb, &csum_offl_spec);
+}
+
+static inline bool skb_csum_off_chk_help_cmn_v4_only(struct sk_buff *skb)
+{
+ static const struct skb_csum_offl_spec csum_offl_spec = {
+ .ipv4_okay = 1,
+ .ip_options_okay = 1,
+ .tcp_okay = 1,
+ .udp_okay = 1,
+ .vlan_okay = 1,
+ };
+
+ return skb_csum_offload_chk_help(skb, &csum_offl_spec);
+}
+
static inline int dev_hard_header(struct sk_buff *skb, struct net_device *dev,
unsigned short type,
const void *daddr, const void *saddr,
@@ -3594,15 +3730,15 @@ struct net_device *netdev_master_upper_dev_get(struct net_device *dev);
struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev);
int netdev_upper_dev_link(struct net_device *dev, struct net_device *upper_dev);
int netdev_master_upper_dev_link(struct net_device *dev,
- struct net_device *upper_dev);
-int netdev_master_upper_dev_link_private(struct net_device *dev,
- struct net_device *upper_dev,
- void *private);
+ struct net_device *upper_dev,
+ void *upper_priv, void *upper_info);
void netdev_upper_dev_unlink(struct net_device *dev,
struct net_device *upper_dev);
void netdev_adjacent_rename_links(struct net_device *dev, char *oldname);
void *netdev_lower_dev_get_private(struct net_device *dev,
struct net_device *lower_dev);
+void netdev_lower_state_changed(struct net_device *lower_dev,
+ void *lower_state_info);
/* RSS keys are 40 or 52 bytes long */
#define NETDEV_RSS_KEY_LEN 52
@@ -3610,7 +3746,7 @@ extern u8 netdev_rss_key[NETDEV_RSS_KEY_LEN];
void netdev_rss_key_fill(void *buffer, size_t len);
int dev_get_nest_level(struct net_device *dev,
- bool (*type_check)(struct net_device *dev));
+ bool (*type_check)(const struct net_device *dev));
int skb_checksum_help(struct sk_buff *skb);
struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
netdev_features_t features, bool tx_path);
@@ -3640,13 +3776,37 @@ __be16 skb_network_protocol(struct sk_buff *skb, int *depth);
static inline bool can_checksum_protocol(netdev_features_t features,
__be16 protocol)
{
- return ((features & NETIF_F_GEN_CSUM) ||
- ((features & NETIF_F_V4_CSUM) &&
- protocol == htons(ETH_P_IP)) ||
- ((features & NETIF_F_V6_CSUM) &&
- protocol == htons(ETH_P_IPV6)) ||
- ((features & NETIF_F_FCOE_CRC) &&
- protocol == htons(ETH_P_FCOE)));
+ if (protocol == htons(ETH_P_FCOE))
+ return !!(features & NETIF_F_FCOE_CRC);
+
+ /* Assume this is an IP checksum (not SCTP CRC) */
+
+ if (features & NETIF_F_HW_CSUM) {
+ /* Can checksum everything */
+ return true;
+ }
+
+ switch (protocol) {
+ case htons(ETH_P_IP):
+ return !!(features & NETIF_F_IP_CSUM);
+ case htons(ETH_P_IPV6):
+ return !!(features & NETIF_F_IPV6_CSUM);
+ default:
+ return false;
+ }
+}
+
+/* Map an ethertype into IP protocol if possible */
+static inline int eproto_to_ipproto(int eproto)
+{
+ switch (eproto) {
+ case htons(ETH_P_IP):
+ return IPPROTO_IP;
+ case htons(ETH_P_IPV6):
+ return IPPROTO_IPV6;
+ default:
+ return -1;
+ }
}
#ifdef CONFIG_BUG
@@ -3711,15 +3871,14 @@ void linkwatch_run_queue(void);
static inline netdev_features_t netdev_intersect_features(netdev_features_t f1,
netdev_features_t f2)
{
- if (f1 & NETIF_F_GEN_CSUM)
- f1 |= (NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM);
- if (f2 & NETIF_F_GEN_CSUM)
- f2 |= (NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM);
- f1 &= f2;
- if (f1 & NETIF_F_GEN_CSUM)
- f1 &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM);
+ if ((f1 ^ f2) & NETIF_F_HW_CSUM) {
+ if (f1 & NETIF_F_HW_CSUM)
+ f1 |= (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM);
+ else
+ f2 |= (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM);
+ }
- return f1;
+ return f1 & f2;
}
static inline netdev_features_t netdev_get_wanted_features(
@@ -3807,32 +3966,32 @@ static inline void skb_gso_error_unwind(struct sk_buff *skb, __be16 protocol,
skb->mac_len = mac_len;
}
-static inline bool netif_is_macvlan(struct net_device *dev)
+static inline bool netif_is_macvlan(const struct net_device *dev)
{
return dev->priv_flags & IFF_MACVLAN;
}
-static inline bool netif_is_macvlan_port(struct net_device *dev)
+static inline bool netif_is_macvlan_port(const struct net_device *dev)
{
return dev->priv_flags & IFF_MACVLAN_PORT;
}
-static inline bool netif_is_ipvlan(struct net_device *dev)
+static inline bool netif_is_ipvlan(const struct net_device *dev)
{
return dev->priv_flags & IFF_IPVLAN_SLAVE;
}
-static inline bool netif_is_ipvlan_port(struct net_device *dev)
+static inline bool netif_is_ipvlan_port(const struct net_device *dev)
{
return dev->priv_flags & IFF_IPVLAN_MASTER;
}
-static inline bool netif_is_bond_master(struct net_device *dev)
+static inline bool netif_is_bond_master(const struct net_device *dev)
{
return dev->flags & IFF_MASTER && dev->priv_flags & IFF_BONDING;
}
-static inline bool netif_is_bond_slave(struct net_device *dev)
+static inline bool netif_is_bond_slave(const struct net_device *dev)
{
return dev->flags & IFF_SLAVE && dev->priv_flags & IFF_BONDING;
}
@@ -3867,6 +4026,26 @@ static inline bool netif_is_ovs_master(const struct net_device *dev)
return dev->priv_flags & IFF_OPENVSWITCH;
}
+static inline bool netif_is_team_master(const struct net_device *dev)
+{
+ return dev->priv_flags & IFF_TEAM;
+}
+
+static inline bool netif_is_team_port(const struct net_device *dev)
+{
+ return dev->priv_flags & IFF_TEAM_PORT;
+}
+
+static inline bool netif_is_lag_master(const struct net_device *dev)
+{
+ return netif_is_bond_master(dev) || netif_is_team_master(dev);
+}
+
+static inline bool netif_is_lag_port(const struct net_device *dev)
+{
+ return netif_is_bond_slave(dev) || netif_is_team_port(dev);
+}
+
/* This device needs to keep skb dst for qdisc enqueue or ndo_start_xmit() */
static inline void netif_keep_dst(struct net_device *dev)
{
diff --git a/include/linux/netfilter/nf_conntrack_sctp.h b/include/linux/netfilter/nf_conntrack_sctp.h
new file mode 100644
index 000000000000..22a16a23cd8a
--- /dev/null
+++ b/include/linux/netfilter/nf_conntrack_sctp.h
@@ -0,0 +1,13 @@
+#ifndef _NF_CONNTRACK_SCTP_H
+#define _NF_CONNTRACK_SCTP_H
+/* SCTP tracking. */
+
+#include <uapi/linux/netfilter/nf_conntrack_sctp.h>
+
+struct ip_ct_sctp {
+ enum sctp_conntrack state;
+
+ __be32 vtag[IP_CT_DIR_MAX];
+};
+
+#endif /* _NF_CONNTRACK_SCTP_H */
diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h
index 249d1bb01e03..ba0d9789eb6e 100644
--- a/include/linux/netfilter/nfnetlink.h
+++ b/include/linux/netfilter/nfnetlink.h
@@ -8,13 +8,13 @@
#include <uapi/linux/netfilter/nfnetlink.h>
struct nfnl_callback {
- int (*call)(struct sock *nl, struct sk_buff *skb,
+ int (*call)(struct net *net, struct sock *nl, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const cda[]);
- int (*call_rcu)(struct sock *nl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[]);
- int (*call_batch)(struct sock *nl, struct sk_buff *skb,
+ int (*call_rcu)(struct net *net, struct sock *nl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[]);
+ int (*call_batch)(struct net *net, struct sock *nl, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const cda[]);
const struct nla_policy *policy; /* netlink attribute policy */
@@ -26,8 +26,8 @@ struct nfnetlink_subsystem {
__u8 subsys_id; /* nfnetlink subsystem ID */
__u8 cb_count; /* number of callbacks */
const struct nfnl_callback *cb; /* callback for individual types */
- int (*commit)(struct sk_buff *skb);
- int (*abort)(struct sk_buff *skb);
+ int (*commit)(struct net *net, struct sk_buff *skb);
+ int (*abort)(struct net *net, struct sk_buff *skb);
};
int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n);
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index 639e9b8b0e4d..0b41959aab9f 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -131,6 +131,7 @@ netlink_skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
struct netlink_callback {
struct sk_buff *skb;
const struct nlmsghdr *nlh;
+ int (*start)(struct netlink_callback *);
int (*dump)(struct sk_buff * skb,
struct netlink_callback *cb);
int (*done)(struct netlink_callback *cb);
@@ -153,6 +154,7 @@ struct nlmsghdr *
__nlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, int type, int len, int flags);
struct netlink_dump_control {
+ int (*start)(struct netlink_callback *);
int (*dump)(struct sk_buff *skb, struct netlink_callback *);
int (*done)(struct netlink_callback *);
void *data;
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index e7e78537aea2..d6f9b4e6006d 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -139,10 +139,10 @@ enum nfs_opnum4 {
Needs to be updated if more operations are defined in future.*/
#define FIRST_NFS4_OP OP_ACCESS
-#define LAST_NFS4_OP OP_WRITE_SAME
#define LAST_NFS40_OP OP_RELEASE_LOCKOWNER
#define LAST_NFS41_OP OP_RECLAIM_COMPLETE
-#define LAST_NFS42_OP OP_WRITE_SAME
+#define LAST_NFS42_OP OP_CLONE
+#define LAST_NFS4_OP LAST_NFS42_OP
enum nfsstat4 {
NFS4_OK = 0,
@@ -592,4 +592,18 @@ enum data_content4 {
NFS4_CONTENT_HOLE = 1,
};
+enum pnfs_update_layout_reason {
+ PNFS_UPDATE_LAYOUT_UNKNOWN = 0,
+ PNFS_UPDATE_LAYOUT_NO_PNFS,
+ PNFS_UPDATE_LAYOUT_RD_ZEROLEN,
+ PNFS_UPDATE_LAYOUT_MDSTHRESH,
+ PNFS_UPDATE_LAYOUT_NOMEM,
+ PNFS_UPDATE_LAYOUT_BULK_RECALL,
+ PNFS_UPDATE_LAYOUT_IO_TEST_FAIL,
+ PNFS_UPDATE_LAYOUT_FOUND_CACHED,
+ PNFS_UPDATE_LAYOUT_RETURN,
+ PNFS_UPDATE_LAYOUT_BLOCKED,
+ PNFS_UPDATE_LAYOUT_SEND_LAYOUTGET,
+};
+
#endif
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index c0e961474a52..48e0320cd643 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -60,18 +60,12 @@ struct nfs_lockowner {
pid_t l_pid;
};
-#define NFS_IO_INPROGRESS 0
-struct nfs_io_counter {
- unsigned long flags;
- atomic_t io_count;
-};
-
struct nfs_lock_context {
atomic_t count;
struct list_head list;
struct nfs_open_context *open_context;
struct nfs_lockowner lockowner;
- struct nfs_io_counter io_count;
+ atomic_t io_count;
};
struct nfs4_state;
@@ -216,7 +210,6 @@ struct nfs_inode {
#define NFS_INO_FLUSHING (4) /* inode is flushing out data */
#define NFS_INO_FSCACHE (5) /* inode can be cached by FS-Cache */
#define NFS_INO_FSCACHE_LOCK (6) /* FS-Cache cookie management lock */
-#define NFS_INO_COMMIT (7) /* inode is committing unstable writes */
#define NFS_INO_LAYOUTCOMMIT (9) /* layoutcommit required */
#define NFS_INO_LAYOUTCOMMITTING (10) /* layoutcommit inflight */
#define NFS_INO_LAYOUTSTATS (11) /* layoutstats inflight */
@@ -359,6 +352,7 @@ extern int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode);
extern int nfs_revalidate_inode_rcu(struct nfs_server *server, struct inode *inode);
extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);
extern int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping);
+extern int nfs_revalidate_mapping_rcu(struct inode *inode);
extern int nfs_revalidate_mapping_protected(struct inode *inode, struct address_space *mapping);
extern int nfs_setattr(struct dentry *, struct iattr *);
extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr, struct nfs_fattr *);
@@ -517,13 +511,25 @@ extern int nfs_updatepage(struct file *, struct page *, unsigned int, unsigned
*/
extern int nfs_sync_inode(struct inode *inode);
extern int nfs_wb_all(struct inode *inode);
-extern int nfs_wb_page(struct inode *inode, struct page* page);
+extern int nfs_wb_single_page(struct inode *inode, struct page *page, bool launder);
extern int nfs_wb_page_cancel(struct inode *inode, struct page* page);
extern int nfs_commit_inode(struct inode *, int);
extern struct nfs_commit_data *nfs_commitdata_alloc(void);
extern void nfs_commit_free(struct nfs_commit_data *data);
static inline int
+nfs_wb_launder_page(struct inode *inode, struct page *page)
+{
+ return nfs_wb_single_page(inode, page, true);
+}
+
+static inline int
+nfs_wb_page(struct inode *inode, struct page *page)
+{
+ return nfs_wb_single_page(inode, page, false);
+}
+
+static inline int
nfs_have_writebacks(struct inode *inode)
{
return NFS_I(inode)->nrequests != 0;
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 2469ab0bb3a1..7fcc13c8cf1f 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -102,6 +102,7 @@ struct nfs_client {
#define NFS_SP4_MACH_CRED_STATEID 4 /* TEST_STATEID and FREE_STATEID */
#define NFS_SP4_MACH_CRED_WRITE 5 /* WRITE */
#define NFS_SP4_MACH_CRED_COMMIT 6 /* COMMIT */
+#define NFS_SP4_MACH_CRED_PNFS_CLEANUP 7 /* LAYOUTRETURN */
#endif /* CONFIG_NFS_V4 */
/* Our own IP address, as a null-terminated string.
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 570d630f98ae..791098a08a87 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -251,6 +251,7 @@ struct nfs4_layoutget {
struct nfs4_layoutget_res res;
struct rpc_cred *cred;
gfp_t gfp_flags;
+ long timeout;
};
struct nfs4_getdeviceinfo_args {
@@ -1374,6 +1375,7 @@ enum {
NFS_IOHDR_ERROR = 0,
NFS_IOHDR_EOF,
NFS_IOHDR_REDO,
+ NFS_IOHDR_STAT,
};
struct nfs_pgio_header {
@@ -1419,11 +1421,12 @@ struct nfs_mds_commit_info {
struct list_head list;
};
+struct nfs_commit_info;
struct nfs_commit_data;
struct nfs_inode;
struct nfs_commit_completion_ops {
- void (*error_cleanup) (struct nfs_inode *nfsi);
void (*completion) (struct nfs_commit_data *data);
+ void (*resched_write) (struct nfs_commit_info *, struct nfs_page *);
};
struct nfs_commit_info {
@@ -1453,12 +1456,14 @@ struct nfs_commit_data {
const struct rpc_call_ops *mds_ops;
const struct nfs_commit_completion_ops *completion_ops;
int (*commit_done_cb) (struct rpc_task *task, struct nfs_commit_data *data);
+ unsigned long flags;
};
struct nfs_pgio_completion_ops {
void (*error_cleanup)(struct list_head *head);
void (*init_hdr)(struct nfs_pgio_header *hdr);
void (*completion)(struct nfs_pgio_header *hdr);
+ void (*reschedule_io)(struct nfs_pgio_header *hdr);
};
struct nfs_unlinkdata {
diff --git a/include/linux/nwpserial.h b/include/linux/nwpserial.h
deleted file mode 100644
index 9acb21572eaf..000000000000
--- a/include/linux/nwpserial.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Serial Port driver for a NWP uart device
- *
- * Copyright (C) 2008 IBM Corp., Benjamin Krill <ben@codiert.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 of the License, or (at your option) any later version.
- *
- */
-#ifndef _NWPSERIAL_H
-#define _NWPSERIAL_H
-
-int nwpserial_register_port(struct uart_port *port);
-void nwpserial_unregister_port(int line);
-
-#endif /* _NWPSERIAL_H */
diff --git a/include/linux/of_address.h b/include/linux/of_address.h
index 507daad0bc8d..01c0a556448b 100644
--- a/include/linux/of_address.h
+++ b/include/linux/of_address.h
@@ -3,6 +3,7 @@
#include <linux/ioport.h>
#include <linux/errno.h>
#include <linux/of.h>
+#include <linux/io.h>
struct of_pci_range_parser {
struct device_node *node;
@@ -36,6 +37,8 @@ extern struct device_node *of_find_matching_node_by_address(
const struct of_device_id *matches,
u64 base_address);
extern void __iomem *of_iomap(struct device_node *device, int index);
+void __iomem *of_io_request_and_map(struct device_node *device,
+ int index, const char *name);
/* Extract an address from a device, returns the region size and
* the address space flags too. The PCI version uses a BAR number
@@ -57,6 +60,11 @@ extern int of_dma_get_range(struct device_node *np, u64 *dma_addr,
u64 *paddr, u64 *size);
extern bool of_dma_is_coherent(struct device_node *np);
#else /* CONFIG_OF_ADDRESS */
+static inline void __iomem *of_io_request_and_map(struct device_node *device,
+ int index, const char *name)
+{
+ return IOMEM_ERR_PTR(-EINVAL);
+}
static inline u64 of_translate_address(struct device_node *np,
const __be32 *addr)
@@ -112,12 +120,7 @@ static inline bool of_dma_is_coherent(struct device_node *np)
extern int of_address_to_resource(struct device_node *dev, int index,
struct resource *r);
void __iomem *of_iomap(struct device_node *node, int index);
-void __iomem *of_io_request_and_map(struct device_node *device,
- int index, const char *name);
#else
-
-#include <linux/io.h>
-
static inline int of_address_to_resource(struct device_node *dev, int index,
struct resource *r)
{
@@ -128,12 +131,6 @@ static inline void __iomem *of_iomap(struct device_node *device, int index)
{
return NULL;
}
-
-static inline void __iomem *of_io_request_and_map(struct device_node *device,
- int index, const char *name)
-{
- return IOMEM_ERR_PTR(-EINVAL);
-}
#endif
#if defined(CONFIG_OF_ADDRESS) && defined(CONFIG_PCI)
diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h
index 039f2eec49ce..1e0deb8e8494 100644
--- a/include/linux/of_irq.h
+++ b/include/linux/of_irq.h
@@ -46,12 +46,14 @@ extern int of_irq_get(struct device_node *dev, int index);
extern int of_irq_get_byname(struct device_node *dev, const char *name);
extern int of_irq_to_resource_table(struct device_node *dev,
struct resource *res, int nr_irqs);
+extern struct device_node *of_irq_find_parent(struct device_node *child);
extern struct irq_domain *of_msi_get_domain(struct device *dev,
struct device_node *np,
enum irq_domain_bus_token token);
extern struct irq_domain *of_msi_map_get_device_domain(struct device *dev,
u32 rid);
extern void of_msi_configure(struct device *dev, struct device_node *np);
+u32 of_msi_map_rid(struct device *dev, struct device_node *msi_np, u32 rid_in);
#else
static inline int of_irq_count(struct device_node *dev)
{
@@ -70,6 +72,11 @@ static inline int of_irq_to_resource_table(struct device_node *dev,
{
return 0;
}
+static inline void *of_irq_find_parent(struct device_node *child)
+{
+ return NULL;
+}
+
static inline struct irq_domain *of_msi_get_domain(struct device *dev,
struct device_node *np,
enum irq_domain_bus_token token)
@@ -84,6 +91,11 @@ static inline struct irq_domain *of_msi_map_get_device_domain(struct device *dev
static inline void of_msi_configure(struct device *dev, struct device_node *np)
{
}
+static inline u32 of_msi_map_rid(struct device *dev,
+ struct device_node *msi_np, u32 rid_in)
+{
+ return rid_in;
+}
#endif
#if defined(CONFIG_OF_IRQ) || defined(CONFIG_SPARC)
@@ -93,7 +105,6 @@ static inline void of_msi_configure(struct device *dev, struct device_node *np)
* so declare it here regardless of the CONFIG_OF_IRQ setting.
*/
extern unsigned int irq_of_parse_and_map(struct device_node *node, int index);
-u32 of_msi_map_rid(struct device *dev, struct device_node *msi_np, u32 rid_in);
#else /* !CONFIG_OF && !CONFIG_SPARC */
static inline unsigned int irq_of_parse_and_map(struct device_node *dev,
@@ -101,12 +112,6 @@ static inline unsigned int irq_of_parse_and_map(struct device_node *dev,
{
return 0;
}
-
-static inline u32 of_msi_map_rid(struct device *dev,
- struct device_node *msi_np, u32 rid_in)
-{
- return rid_in;
-}
#endif /* !CONFIG_OF */
#endif /* __OF_IRQ_H */
diff --git a/include/linux/omap-dma.h b/include/linux/omap-dma.h
index 88fa8af2b937..1d99b61adc65 100644
--- a/include/linux/omap-dma.h
+++ b/include/linux/omap-dma.h
@@ -267,6 +267,9 @@ struct omap_dma_reg {
u8 type;
};
+#define SDMA_FILTER_PARAM(hw_req) ((int[]) { (hw_req) })
+struct dma_slave_map;
+
/* System DMA platform data structure */
struct omap_system_dma_plat_info {
const struct omap_dma_reg *reg_map;
@@ -278,6 +281,9 @@ struct omap_system_dma_plat_info {
void (*clear_dma)(int lch);
void (*dma_write)(u32 val, int reg, int lch);
u32 (*dma_read)(int reg, int lch);
+
+ const struct dma_slave_map *slave_map;
+ int slavecnt;
};
#ifdef CONFIG_ARCH_OMAP2PLUS
diff --git a/include/linux/pci.h b/include/linux/pci.h
index e828e7b4afec..d86378c226fb 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -412,9 +412,18 @@ struct pci_host_bridge {
void (*release_fn)(struct pci_host_bridge *);
void *release_data;
unsigned int ignore_reset_delay:1; /* for entire hierarchy */
+ /* Resource alignment requirements */
+ resource_size_t (*align_resource)(struct pci_dev *dev,
+ const struct resource *res,
+ resource_size_t start,
+ resource_size_t size,
+ resource_size_t align);
};
#define to_pci_host_bridge(n) container_of(n, struct pci_host_bridge, dev)
+
+struct pci_host_bridge *pci_find_host_bridge(struct pci_bus *bus);
+
void pci_set_host_bridge_release(struct pci_host_bridge *bridge,
void (*release_fn)(struct pci_host_bridge *),
void *release_data);
@@ -1937,6 +1946,16 @@ static inline struct irq_domain *
pci_host_bridge_of_msi_domain(struct pci_bus *bus) { return NULL; }
#endif /* CONFIG_OF */
+#ifdef CONFIG_ACPI
+struct irq_domain *pci_host_bridge_acpi_msi_domain(struct pci_bus *bus);
+
+void
+pci_msi_register_fwnode_provider(struct fwnode_handle *(*fn)(struct device *));
+#else
+static inline struct irq_domain *
+pci_host_bridge_acpi_msi_domain(struct pci_bus *bus) { return NULL; }
+#endif
+
#ifdef CONFIG_EEH
static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev)
{
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index d9ba49cedc5d..1acbefc4bbda 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2495,6 +2495,8 @@
#define PCI_DEVICE_ID_KORENIX_JETCARDF2 0x1700
#define PCI_DEVICE_ID_KORENIX_JETCARDF3 0x17ff
+#define PCI_VENDOR_ID_NETRONOME 0x19ee
+
#define PCI_VENDOR_ID_QMI 0x1a32
#define PCI_VENDOR_ID_AZWAVE 0x1a3b
diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h
index 12c9b485beb7..84f542df7ff5 100644
--- a/include/linux/percpu-refcount.h
+++ b/include/linux/percpu-refcount.h
@@ -116,7 +116,7 @@ void percpu_ref_reinit(struct percpu_ref *ref);
*/
static inline void percpu_ref_kill(struct percpu_ref *ref)
{
- return percpu_ref_kill_and_confirm(ref, NULL);
+ percpu_ref_kill_and_confirm(ref, NULL);
}
/*
diff --git a/include/linux/percpu.h b/include/linux/percpu.h
index caebf2a758dc..4bc6dafb703e 100644
--- a/include/linux/percpu.h
+++ b/include/linux/percpu.h
@@ -18,12 +18,6 @@
#define PERCPU_MODULE_RESERVE 0
#endif
-#ifndef PERCPU_ENOUGH_ROOM
-#define PERCPU_ENOUGH_ROOM \
- (ALIGN(__per_cpu_end - __per_cpu_start, SMP_CACHE_BYTES) + \
- PERCPU_MODULE_RESERVE)
-#endif
-
/* minimum unit size, also is the maximum supported allocation size */
#define PCPU_MIN_UNIT_SIZE PFN_ALIGN(32 << 10)
diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index bfa673bb822d..83b5e34c6580 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -111,8 +111,6 @@ struct arm_pmu {
#define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu))
-int armpmu_register(struct arm_pmu *armpmu, int type);
-
u64 armpmu_event_update(struct perf_event *event);
int armpmu_event_set_period(struct perf_event *event);
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index d841d33bcdc9..f9828a48f16a 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -697,9 +697,11 @@ struct perf_cgroup {
* if there is no cgroup event for the current CPU context.
*/
static inline struct perf_cgroup *
-perf_cgroup_from_task(struct task_struct *task)
+perf_cgroup_from_task(struct task_struct *task, struct perf_event_context *ctx)
{
- return container_of(task_css(task, perf_event_cgrp_id),
+ return container_of(task_css_check(task, perf_event_cgrp_id,
+ ctx ? lockdep_is_held(&ctx->lock)
+ : true),
struct perf_cgroup, css);
}
#endif /* CONFIG_CGROUP_PERF */
diff --git a/include/linux/pfn.h b/include/linux/pfn.h
index 7646637221f3..97f3e88aead4 100644
--- a/include/linux/pfn.h
+++ b/include/linux/pfn.h
@@ -9,5 +9,6 @@
#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
#define PFN_DOWN(x) ((x) >> PAGE_SHIFT)
#define PFN_PHYS(x) ((phys_addr_t)(x) << PAGE_SHIFT)
+#define PHYS_PFN(x) ((unsigned long)((x) >> PAGE_SHIFT))
#endif
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 05fde31b6dc6..d6f3641e7933 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -16,8 +16,10 @@
#ifndef __PHY_H
#define __PHY_H
+#include <linux/compiler.h>
#include <linux/spinlock.h>
#include <linux/ethtool.h>
+#include <linux/mdio.h>
#include <linux/mii.h>
#include <linux/module.h>
#include <linux/timer.h>
@@ -58,6 +60,7 @@
#define PHY_HAS_INTERRUPT 0x00000001
#define PHY_HAS_MAGICANEG 0x00000002
#define PHY_IS_INTERNAL 0x00000004
+#define MDIO_DEVICE_IS_PHY 0x80000000
/* Interface Mode definitions */
typedef enum {
@@ -158,8 +161,8 @@ struct mii_bus {
const char *name;
char id[MII_BUS_ID_SIZE];
void *priv;
- int (*read)(struct mii_bus *bus, int phy_id, int regnum);
- int (*write)(struct mii_bus *bus, int phy_id, int regnum, u16 val);
+ int (*read)(struct mii_bus *bus, int addr, int regnum);
+ int (*write)(struct mii_bus *bus, int addr, int regnum, u16 val);
int (*reset)(struct mii_bus *bus);
/*
@@ -178,7 +181,7 @@ struct mii_bus {
struct device dev;
/* list of all PHYs on bus */
- struct phy_device *phy_map[PHY_MAX_ADDR];
+ struct mdio_device *mdio_map[PHY_MAX_ADDR];
/* PHY addresses to be ignored when probing */
u32 phy_mask;
@@ -187,10 +190,10 @@ struct mii_bus {
u32 phy_ignore_ta_mask;
/*
- * Pointer to an array of interrupts, each PHY's
- * interrupt at the index matching its address
+ * An array of interrupts, each PHY's interrupt at the index
+ * matching its address
*/
- int *irq;
+ int irq[PHY_MAX_ADDR];
};
#define to_mii_bus(d) container_of(d, struct mii_bus, dev)
@@ -212,11 +215,6 @@ static inline struct mii_bus *devm_mdiobus_alloc(struct device *dev)
void devm_mdiobus_free(struct device *dev, struct mii_bus *bus);
struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr);
-int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
-int mdiobus_read_nested(struct mii_bus *bus, int addr, u32 regnum);
-int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
-int mdiobus_write_nested(struct mii_bus *bus, int addr, u32 regnum, u16 val);
-
#define PHY_INTERRUPT_DISABLED 0x0
#define PHY_INTERRUPT_ENABLED 0x80000000
@@ -361,14 +359,12 @@ struct phy_c45_device_ids {
* handling, as well as handling shifts in PHY hardware state
*/
struct phy_device {
+ struct mdio_device mdio;
+
/* Information about the PHY type */
/* And management functions */
struct phy_driver *drv;
- struct mii_bus *bus;
-
- struct device dev;
-
u32 phy_id;
struct phy_c45_device_ids c45_ids;
@@ -384,9 +380,6 @@ struct phy_device {
phy_interface_t interface;
- /* Bus address of the PHY (0-31) */
- int addr;
-
/*
* forced speed & duplex (no autoneg)
* partner speed & duplex & pause (autoneg)
@@ -435,10 +428,12 @@ struct phy_device {
void (*adjust_link)(struct net_device *dev);
};
-#define to_phy_device(d) container_of(d, struct phy_device, dev)
+#define to_phy_device(d) container_of(to_mdio_device(d), \
+ struct phy_device, mdio)
/* struct phy_driver: Driver structure for a particular PHY type
*
+ * driver_data: static driver data
* phy_id: The result of reading the UID registers of this PHY
* type, and ANDing them with the phy_id_mask. This driver
* only works for PHYs with IDs which match this field
@@ -448,7 +443,6 @@ struct phy_device {
* by this PHY
* flags: A bitfield defining certain other features this PHY
* supports (like interrupts)
- * driver_data: static driver data
*
* The drivers must implement config_aneg and read_status. All
* other functions are optional. Note that none of these
@@ -459,6 +453,7 @@ struct phy_device {
* supported in the driver).
*/
struct phy_driver {
+ struct mdio_driver_common mdiodrv;
u32 phy_id;
char *name;
unsigned int phy_id_mask;
@@ -589,9 +584,14 @@ struct phy_driver {
int (*module_eeprom)(struct phy_device *dev,
struct ethtool_eeprom *ee, u8 *data);
- struct device_driver driver;
+ /* Get statistics from the phy using ethtool */
+ int (*get_sset_count)(struct phy_device *dev);
+ void (*get_strings)(struct phy_device *dev, u8 *data);
+ void (*get_stats)(struct phy_device *dev,
+ struct ethtool_stats *stats, u64 *data);
};
-#define to_phy_driver(d) container_of(d, struct phy_driver, driver)
+#define to_phy_driver(d) container_of(to_mdio_common_driver(d), \
+ struct phy_driver, mdiodrv)
#define PHY_ANY_ID "MATCH ANY PHY"
#define PHY_ANY_UID 0xffffffff
@@ -619,7 +619,7 @@ static inline int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
if (!phydev->is_c45)
return -EOPNOTSUPP;
- return mdiobus_read(phydev->bus, phydev->addr,
+ return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr,
MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff));
}
@@ -627,14 +627,12 @@ static inline int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
* phy_read_mmd_indirect - reads data from the MMD registers
* @phydev: The PHY device bus
* @prtad: MMD Address
- * @devad: MMD DEVAD
* @addr: PHY address on the MII bus
*
* Description: it reads data from the MMD registers (clause 22 to access to
* clause 45) of the specified phy address.
*/
-int phy_read_mmd_indirect(struct phy_device *phydev, int prtad,
- int devad, int addr);
+int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad);
/**
* phy_read - Convenience function for reading a given PHY register
@@ -647,7 +645,7 @@ int phy_read_mmd_indirect(struct phy_device *phydev, int prtad,
*/
static inline int phy_read(struct phy_device *phydev, u32 regnum)
{
- return mdiobus_read(phydev->bus, phydev->addr, regnum);
+ return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, regnum);
}
/**
@@ -662,7 +660,7 @@ static inline int phy_read(struct phy_device *phydev, u32 regnum)
*/
static inline int phy_write(struct phy_device *phydev, u32 regnum, u16 val)
{
- return mdiobus_write(phydev->bus, phydev->addr, regnum, val);
+ return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, regnum, val);
}
/**
@@ -725,7 +723,7 @@ static inline int phy_write_mmd(struct phy_device *phydev, int devad,
regnum = MII_ADDR_C45 | ((devad & 0x1f) << 16) | (regnum & 0xffff);
- return mdiobus_write(phydev->bus, phydev->addr, regnum, val);
+ return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, regnum, val);
}
/**
@@ -733,14 +731,13 @@ static inline int phy_write_mmd(struct phy_device *phydev, int devad,
* @phydev: The PHY device
* @prtad: MMD Address
* @devad: MMD DEVAD
- * @addr: PHY address on the MII bus
* @data: data to write in the MMD register
*
* Description: Write data from the MMD registers of the specified
* phy address.
*/
void phy_write_mmd_indirect(struct phy_device *phydev, int prtad,
- int devad, int addr, u32 data);
+ int devad, u32 data);
struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
bool is_c45,
@@ -775,6 +772,20 @@ static inline int phy_read_status(struct phy_device *phydev)
return phydev->drv->read_status(phydev);
}
+#define phydev_err(_phydev, format, args...) \
+ dev_err(&_phydev->mdio.dev, format, ##args)
+
+#define phydev_dbg(_phydev, format, args...) \
+ dev_dbg(&_phydev->mdio.dev, format, ##args);
+
+static inline const char *phydev_name(const struct phy_device *phydev)
+{
+ return dev_name(&phydev->mdio.dev);
+}
+
+void phy_attached_print(struct phy_device *phydev, const char *fmt, ...)
+ __printf(2, 3);
+void phy_attached_info(struct phy_device *phydev);
int genphy_config_init(struct phy_device *phydev);
int genphy_setup_forced(struct phy_device *phydev);
int genphy_restart_aneg(struct phy_device *phydev);
@@ -787,8 +798,9 @@ int genphy_resume(struct phy_device *phydev);
int genphy_soft_reset(struct phy_device *phydev);
void phy_driver_unregister(struct phy_driver *drv);
void phy_drivers_unregister(struct phy_driver *drv, int n);
-int phy_driver_register(struct phy_driver *new_driver);
-int phy_drivers_register(struct phy_driver *new_driver, int n);
+int phy_driver_register(struct phy_driver *new_driver, struct module *owner);
+int phy_drivers_register(struct phy_driver *new_driver, int n,
+ struct module *owner);
void phy_state_machine(struct work_struct *work);
void phy_change(struct work_struct *work);
void phy_mac_interrupt(struct phy_device *phydev, int new_link);
@@ -833,7 +845,7 @@ extern struct bus_type mdio_bus_type;
#define phy_module_driver(__phy_drivers, __count) \
static int __init phy_module_init(void) \
{ \
- return phy_drivers_register(__phy_drivers, __count); \
+ return phy_drivers_register(__phy_drivers, __count, THIS_MODULE); \
} \
module_init(phy_module_init); \
static void __exit phy_module_exit(void) \
diff --git a/include/linux/phy/omap_usb.h b/include/linux/phy/omap_usb.h
index dc2c541a619b..2e5fb870efa9 100644
--- a/include/linux/phy/omap_usb.h
+++ b/include/linux/phy/omap_usb.h
@@ -30,6 +30,12 @@ struct usb_dpll_params {
u32 mf;
};
+enum omap_usb_phy_type {
+ TYPE_USB2, /* USB2_PHY, power down in CONTROL_DEV_CONF */
+ TYPE_DRA7USB2, /* USB2 PHY, power and power_aux e.g. DRA7 */
+ TYPE_AM437USB2, /* USB2 PHY, power e.g. AM437x */
+};
+
struct omap_usb {
struct usb_phy phy;
struct phy_companion *comparator;
@@ -40,11 +46,20 @@ struct omap_usb {
struct clk *wkupclk;
struct clk *optclk;
u8 flags;
+ enum omap_usb_phy_type type;
+ struct regmap *syscon_phy_power; /* ctrl. reg. acces */
+ unsigned int power_reg; /* power reg. index within syscon */
+ u32 mask;
+ u32 power_on;
+ u32 power_off;
};
struct usb_phy_data {
const char *label;
u8 flags;
+ u32 mask;
+ u32 power_on;
+ u32 power_off;
};
/* Driver Flags */
@@ -52,6 +67,14 @@ struct usb_phy_data {
#define OMAP_USB2_HAS_SET_VBUS (1 << 1)
#define OMAP_USB2_CALIBRATE_FALSE_DISCONNECT (1 << 2)
+#define OMAP_DEV_PHY_PD BIT(0)
+#define OMAP_USB2_PHY_PD BIT(28)
+
+#define AM437X_USB2_PHY_PD BIT(0)
+#define AM437X_USB2_OTG_PD BIT(1)
+#define AM437X_USB2_OTGVDET_EN BIT(19)
+#define AM437X_USB2_OTGSESSEND_EN BIT(20)
+
#define phy_to_omapusb(x) container_of((x), struct omap_usb, phy)
#if defined(CONFIG_OMAP_USB2) || defined(CONFIG_OMAP_USB2_MODULE)
diff --git a/include/linux/pim.h b/include/linux/pim.h
index 252bf6644c51..e1d756f81348 100644
--- a/include/linux/pim.h
+++ b/include/linux/pim.h
@@ -13,6 +13,11 @@
#define PIM_NULL_REGISTER cpu_to_be32(0x40000000)
+static inline bool ipmr_pimsm_enabled(void)
+{
+ return IS_BUILTIN(CONFIG_IP_PIMSM_V1) || IS_BUILTIN(CONFIG_IP_PIMSM_V2);
+}
+
/* PIMv2 register message header layout (ietf-draft-idmr-pimvsm-v2-00.ps */
struct pimreghdr
{
diff --git a/include/linux/platform_data/camera-rcar.h b/include/linux/platform_data/camera-rcar.h
deleted file mode 100644
index dfc83c581593..000000000000
--- a/include/linux/platform_data/camera-rcar.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Platform data for Renesas R-Car VIN soc-camera driver
- *
- * Copyright (C) 2011-2013 Renesas Solutions Corp.
- * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.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 __CAMERA_RCAR_H_
-#define __CAMERA_RCAR_H_
-
-#define RCAR_VIN_HSYNC_ACTIVE_LOW (1 << 0)
-#define RCAR_VIN_VSYNC_ACTIVE_LOW (1 << 1)
-#define RCAR_VIN_BT601 (1 << 2)
-#define RCAR_VIN_BT656 (1 << 3)
-
-struct rcar_vin_platform_data {
- unsigned int flags;
-};
-
-#endif /* __CAMERA_RCAR_H_ */
diff --git a/include/linux/platform_data/dma-rcar-hpbdma.h b/include/linux/platform_data/dma-rcar-hpbdma.h
deleted file mode 100644
index 648b8ea61a22..000000000000
--- a/include/linux/platform_data/dma-rcar-hpbdma.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2011-2013 Renesas Electronics Corporation
- * Copyright (C) 2013 Cogent Embedded, 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.
- */
-
-#ifndef __DMA_RCAR_HPBDMA_H
-#define __DMA_RCAR_HPBDMA_H
-
-#include <linux/bitops.h>
-#include <linux/types.h>
-
-/* Transmit sizes and respective register values */
-enum {
- XMIT_SZ_8BIT = 0,
- XMIT_SZ_16BIT = 1,
- XMIT_SZ_32BIT = 2,
- XMIT_SZ_MAX
-};
-
-/* DMA control register (DCR) bits */
-#define HPB_DMAE_DCR_DTAMD (1u << 26)
-#define HPB_DMAE_DCR_DTAC (1u << 25)
-#define HPB_DMAE_DCR_DTAU (1u << 24)
-#define HPB_DMAE_DCR_DTAU1 (1u << 23)
-#define HPB_DMAE_DCR_SWMD (1u << 22)
-#define HPB_DMAE_DCR_BTMD (1u << 21)
-#define HPB_DMAE_DCR_PKMD (1u << 20)
-#define HPB_DMAE_DCR_CT (1u << 18)
-#define HPB_DMAE_DCR_ACMD (1u << 17)
-#define HPB_DMAE_DCR_DIP (1u << 16)
-#define HPB_DMAE_DCR_SMDL (1u << 13)
-#define HPB_DMAE_DCR_SPDAM (1u << 12)
-#define HPB_DMAE_DCR_SDRMD_MASK (3u << 10)
-#define HPB_DMAE_DCR_SDRMD_MOD (0u << 10)
-#define HPB_DMAE_DCR_SDRMD_AUTO (1u << 10)
-#define HPB_DMAE_DCR_SDRMD_TIMER (2u << 10)
-#define HPB_DMAE_DCR_SPDS_MASK (3u << 8)
-#define HPB_DMAE_DCR_SPDS_8BIT (0u << 8)
-#define HPB_DMAE_DCR_SPDS_16BIT (1u << 8)
-#define HPB_DMAE_DCR_SPDS_32BIT (2u << 8)
-#define HPB_DMAE_DCR_DMDL (1u << 5)
-#define HPB_DMAE_DCR_DPDAM (1u << 4)
-#define HPB_DMAE_DCR_DDRMD_MASK (3u << 2)
-#define HPB_DMAE_DCR_DDRMD_MOD (0u << 2)
-#define HPB_DMAE_DCR_DDRMD_AUTO (1u << 2)
-#define HPB_DMAE_DCR_DDRMD_TIMER (2u << 2)
-#define HPB_DMAE_DCR_DPDS_MASK (3u << 0)
-#define HPB_DMAE_DCR_DPDS_8BIT (0u << 0)
-#define HPB_DMAE_DCR_DPDS_16BIT (1u << 0)
-#define HPB_DMAE_DCR_DPDS_32BIT (2u << 0)
-
-/* Asynchronous reset register (ASYNCRSTR) bits */
-#define HPB_DMAE_ASYNCRSTR_ASRST41 BIT(10)
-#define HPB_DMAE_ASYNCRSTR_ASRST40 BIT(9)
-#define HPB_DMAE_ASYNCRSTR_ASRST39 BIT(8)
-#define HPB_DMAE_ASYNCRSTR_ASRST27 BIT(7)
-#define HPB_DMAE_ASYNCRSTR_ASRST26 BIT(6)
-#define HPB_DMAE_ASYNCRSTR_ASRST25 BIT(5)
-#define HPB_DMAE_ASYNCRSTR_ASRST24 BIT(4)
-#define HPB_DMAE_ASYNCRSTR_ASRST23 BIT(3)
-#define HPB_DMAE_ASYNCRSTR_ASRST22 BIT(2)
-#define HPB_DMAE_ASYNCRSTR_ASRST21 BIT(1)
-#define HPB_DMAE_ASYNCRSTR_ASRST20 BIT(0)
-
-struct hpb_dmae_slave_config {
- unsigned int id;
- dma_addr_t addr;
- u32 dcr;
- u32 port;
- u32 rstr;
- u32 mdr;
- u32 mdm;
- u32 flags;
-#define HPB_DMAE_SET_ASYNC_RESET BIT(0)
-#define HPB_DMAE_SET_ASYNC_MODE BIT(1)
- u32 dma_ch;
-};
-
-#define HPB_DMAE_CHANNEL(_irq, _s_id) \
-{ \
- .ch_irq = _irq, \
- .s_id = _s_id, \
-}
-
-struct hpb_dmae_channel {
- unsigned int ch_irq;
- unsigned int s_id;
-};
-
-struct hpb_dmae_pdata {
- const struct hpb_dmae_slave_config *slaves;
- int num_slaves;
- const struct hpb_dmae_channel *channels;
- int num_channels;
- const unsigned int ts_shift[XMIT_SZ_MAX];
- int num_hw_channels;
-};
-
-#endif
diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h
index e2878baeb90e..0a533f94438f 100644
--- a/include/linux/platform_data/edma.h
+++ b/include/linux/platform_data/edma.h
@@ -53,12 +53,16 @@ enum dma_event_q {
#define EDMA_CTLR(i) ((i) >> 16)
#define EDMA_CHAN_SLOT(i) ((i) & 0xffff)
+#define EDMA_FILTER_PARAM(ctlr, chan) ((int[]) { EDMA_CTLR_CHAN(ctlr, chan) })
+
struct edma_rsv_info {
const s16 (*rsv_chans)[2];
const s16 (*rsv_slots)[2];
};
+struct dma_slave_map;
+
/* platform_data for EDMA driver */
struct edma_soc_info {
/*
@@ -72,10 +76,13 @@ struct edma_soc_info {
struct edma_rsv_info *rsv;
/* List of channels allocated for memcpy, terminated with -1 */
- s16 *memcpy_channels;
+ s32 *memcpy_channels;
s8 (*queue_priority_mapping)[2];
const s16 (*xbar_chans)[2];
+
+ const struct dma_slave_map *slave_map;
+ int slavecnt;
};
#endif
diff --git a/include/linux/platform_data/irq-renesas-intc-irqpin.h b/include/linux/platform_data/irq-renesas-intc-irqpin.h
deleted file mode 100644
index e4cb911066a6..000000000000
--- a/include/linux/platform_data/irq-renesas-intc-irqpin.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Renesas INTC External IRQ Pin 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_INTC_IRQPIN_H__
-#define __IRQ_RENESAS_INTC_IRQPIN_H__
-
-struct renesas_intc_irqpin_config {
- unsigned int sense_bitfield_width;
- unsigned int irq_base;
- bool control_parent;
-};
-
-#endif /* __IRQ_RENESAS_INTC_IRQPIN_H__ */
diff --git a/include/linux/platform_data/camera-mx2.h b/include/linux/platform_data/media/camera-mx2.h
index 7ded6f1f74bc..7ded6f1f74bc 100644
--- a/include/linux/platform_data/camera-mx2.h
+++ b/include/linux/platform_data/media/camera-mx2.h
diff --git a/include/linux/platform_data/camera-mx3.h b/include/linux/platform_data/media/camera-mx3.h
index a910dadc8258..a910dadc8258 100644
--- a/include/linux/platform_data/camera-mx3.h
+++ b/include/linux/platform_data/media/camera-mx3.h
diff --git a/include/linux/platform_data/camera-pxa.h b/include/linux/platform_data/media/camera-pxa.h
index 6709b1cd7c77..6709b1cd7c77 100644
--- a/include/linux/platform_data/camera-pxa.h
+++ b/include/linux/platform_data/media/camera-pxa.h
diff --git a/include/linux/platform_data/coda.h b/include/linux/platform_data/media/coda.h
index 6ad4410d9e20..6ad4410d9e20 100644
--- a/include/linux/platform_data/coda.h
+++ b/include/linux/platform_data/media/coda.h
diff --git a/include/media/gpio-ir-recv.h b/include/linux/platform_data/media/gpio-ir-recv.h
index 0142736a59db..0c298f569d5a 100644
--- a/include/media/gpio-ir-recv.h
+++ b/include/linux/platform_data/media/gpio-ir-recv.h
@@ -21,4 +21,3 @@ struct gpio_ir_recv_platform_data {
};
#endif /* __GPIO_IR_RECV_H__ */
-
diff --git a/include/media/ir-rx51.h b/include/linux/platform_data/media/ir-rx51.h
index 104aa892f31b..104aa892f31b 100644
--- a/include/media/ir-rx51.h
+++ b/include/linux/platform_data/media/ir-rx51.h
diff --git a/include/media/mmp-camera.h b/include/linux/platform_data/media/mmp-camera.h
index 7611963a257f..7611963a257f 100644
--- a/include/media/mmp-camera.h
+++ b/include/linux/platform_data/media/mmp-camera.h
diff --git a/include/media/omap1_camera.h b/include/linux/platform_data/media/omap1_camera.h
index 819767cf04d4..819767cf04d4 100644
--- a/include/media/omap1_camera.h
+++ b/include/linux/platform_data/media/omap1_camera.h
diff --git a/include/media/omap4iss.h b/include/linux/platform_data/media/omap4iss.h
index 0d7620db5e32..0d7620db5e32 100644
--- a/include/media/omap4iss.h
+++ b/include/linux/platform_data/media/omap4iss.h
diff --git a/include/media/s5p_hdmi.h b/include/linux/platform_data/media/s5p_hdmi.h
index 181642b8d0a5..bb9cacb0cbb0 100644
--- a/include/media/s5p_hdmi.h
+++ b/include/linux/platform_data/media/s5p_hdmi.h
@@ -34,4 +34,3 @@ struct s5p_hdmi_platform_data {
};
#endif /* S5P_HDMI_H */
-
diff --git a/include/media/si4713.h b/include/linux/platform_data/media/si4713.h
index be4f58e2440b..932668ad54f7 100644
--- a/include/media/si4713.h
+++ b/include/linux/platform_data/media/si4713.h
@@ -1,5 +1,5 @@
/*
- * include/media/si4713.h
+ * include/linux/platform_data/media/si4713.h
*
* Board related data definitions for Si4713 i2c device driver.
*
diff --git a/include/media/sii9234.h b/include/linux/platform_data/media/sii9234.h
index 6a4a809fe9a3..6a4a809fe9a3 100644
--- a/include/media/sii9234.h
+++ b/include/linux/platform_data/media/sii9234.h
diff --git a/include/media/soc_camera_platform.h b/include/linux/platform_data/media/soc_camera_platform.h
index 1e5065dab430..1e5065dab430 100644
--- a/include/media/soc_camera_platform.h
+++ b/include/linux/platform_data/media/soc_camera_platform.h
diff --git a/include/media/timb_radio.h b/include/linux/platform_data/media/timb_radio.h
index a40a6a348d21..a40a6a348d21 100644
--- a/include/media/timb_radio.h
+++ b/include/linux/platform_data/media/timb_radio.h
diff --git a/include/media/timb_video.h b/include/linux/platform_data/media/timb_video.h
index 70ae43970a49..70ae43970a49 100644
--- a/include/media/timb_video.h
+++ b/include/linux/platform_data/media/timb_video.h
diff --git a/include/linux/platform_data/microread.h b/include/linux/platform_data/microread.h
index cfda59b226ee..ca13992089b8 100644
--- a/include/linux/platform_data/microread.h
+++ b/include/linux/platform_data/microread.h
@@ -1,5 +1,5 @@
/*
- * Driver include for the PN544 NFC chip.
+ * Driver include for the Inside Secure microread NFC Chip.
*
* Copyright (C) 2011 Tieto Poland
* Copyright (C) 2012 Intel Corporation. All rights reserved.
diff --git a/include/linux/platform_data/mmc-mvsdio.h b/include/linux/platform_data/mmc-mvsdio.h
deleted file mode 100644
index d02704cd3695..000000000000
--- a/include/linux/platform_data/mmc-mvsdio.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#ifndef __MMC_MVSDIO_H
-#define __MMC_MVSDIO_H
-
-#include <linux/mbus.h>
-
-struct mvsdio_platform_data {
- unsigned int clock;
- int gpio_card_detect;
- int gpio_write_protect;
-};
-
-#endif
diff --git a/include/linux/platform_data/spi-s3c64xx.h b/include/linux/platform_data/spi-s3c64xx.h
index d3889b98a1a1..fb5625bcca9a 100644
--- a/include/linux/platform_data/spi-s3c64xx.h
+++ b/include/linux/platform_data/spi-s3c64xx.h
@@ -40,6 +40,8 @@ struct s3c64xx_spi_info {
int num_cs;
int (*cfg_gpio)(void);
dma_filter_fn filter;
+ void *dma_tx;
+ void *dma_rx;
};
/**
diff --git a/include/linux/platform_data/usb-rcar-phy.h b/include/linux/platform_data/usb-rcar-phy.h
deleted file mode 100644
index 8ec6964a32a5..000000000000
--- a/include/linux/platform_data/usb-rcar-phy.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2013 Renesas Solutions Corp.
- * Copyright (C) 2013 Cogent Embedded, 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.
- */
-
-#ifndef __USB_RCAR_PHY_H
-#define __USB_RCAR_PHY_H
-
-#include <linux/types.h>
-
-struct rcar_phy_platform_data {
- bool ferrite_bead:1; /* (R8A7778 only) */
-
- bool port1_func:1; /* true: port 1 used by function, false: host */
- unsigned penc1:1; /* Output of the PENC1 pin in function mode */
- struct { /* Overcurrent pin control for ports 0..2 */
- bool select_3_3v:1; /* true: USB_OVCn pin, false: OVCn pin */
- /* Set to false on port 1 in function mode */
- bool active_high:1; /* true: active high, false: active low */
- /* Set to true on port 1 in function mode */
- } ovc_pin[3]; /* (R8A7778 only has 2 ports) */
-};
-
-#endif /* __USB_RCAR_PHY_H */
diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h
index dc777be5f2e1..03b755521fd9 100644
--- a/include/linux/platform_device.h
+++ b/include/linux/platform_device.h
@@ -18,6 +18,7 @@
#define PLATFORM_DEVID_AUTO (-2)
struct mfd_cell;
+struct property_set;
struct platform_device {
const char *name;
@@ -51,6 +52,7 @@ extern void arch_setup_pdev_archdata(struct platform_device *);
extern struct resource *platform_get_resource(struct platform_device *,
unsigned int, unsigned int);
extern int platform_get_irq(struct platform_device *, unsigned int);
+extern int platform_irq_count(struct platform_device *);
extern struct resource *platform_get_resource_byname(struct platform_device *,
unsigned int,
const char *);
@@ -70,6 +72,8 @@ struct platform_device_info {
const void *data;
size_t size_data;
u64 dma_mask;
+
+ const struct property_set *pset;
};
extern struct platform_device *platform_device_register_full(
const struct platform_device_info *pdevinfo);
@@ -167,6 +171,8 @@ extern int platform_device_add_resources(struct platform_device *pdev,
unsigned int num);
extern int platform_device_add_data(struct platform_device *pdev,
const void *data, size_t size);
+extern int platform_device_add_properties(struct platform_device *pdev,
+ const struct property_set *pset);
extern int platform_device_add(struct platform_device *pdev);
extern void platform_device_del(struct platform_device *pdev);
extern void platform_device_put(struct platform_device *pdev);
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
index 9a2e50337af9..95403d2ccaf5 100644
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -55,6 +55,11 @@ int dev_pm_opp_enable(struct device *dev, unsigned long freq);
int dev_pm_opp_disable(struct device *dev, unsigned long freq);
struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev);
+int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions,
+ unsigned int count);
+void dev_pm_opp_put_supported_hw(struct device *dev);
+int dev_pm_opp_set_prop_name(struct device *dev, const char *name);
+void dev_pm_opp_put_prop_name(struct device *dev);
#else
static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
{
@@ -129,6 +134,23 @@ static inline struct srcu_notifier_head *dev_pm_opp_get_notifier(
{
return ERR_PTR(-EINVAL);
}
+
+static inline int dev_pm_opp_set_supported_hw(struct device *dev,
+ const u32 *versions,
+ unsigned int count)
+{
+ return -EINVAL;
+}
+
+static inline void dev_pm_opp_put_supported_hw(struct device *dev) {}
+
+static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name)
+{
+ return -EINVAL;
+}
+
+static inline void dev_pm_opp_put_prop_name(struct device *dev) {}
+
#endif /* CONFIG_PM_OPP */
#if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)
diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h
index 3bdbb4189780..7af093d6a4dd 100644
--- a/include/linux/pm_runtime.h
+++ b/include/linux/pm_runtime.h
@@ -39,6 +39,7 @@ extern int pm_runtime_force_resume(struct device *dev);
extern int __pm_runtime_idle(struct device *dev, int rpmflags);
extern int __pm_runtime_suspend(struct device *dev, int rpmflags);
extern int __pm_runtime_resume(struct device *dev, int rpmflags);
+extern int pm_runtime_get_if_in_use(struct device *dev);
extern int pm_schedule_suspend(struct device *dev, unsigned int delay);
extern int __pm_runtime_set_status(struct device *dev, unsigned int status);
extern int pm_runtime_barrier(struct device *dev);
@@ -143,6 +144,10 @@ static inline int pm_schedule_suspend(struct device *dev, unsigned int delay)
{
return -ENOSYS;
}
+static inline int pm_runtime_get_if_in_use(struct device *dev)
+{
+ return -EINVAL;
+}
static inline int __pm_runtime_set_status(struct device *dev,
unsigned int status) { return 0; }
static inline int pm_runtime_barrier(struct device *dev) { return 0; }
diff --git a/include/linux/posix_acl_xattr.h b/include/linux/posix_acl_xattr.h
index 6f14ee295822..e5e8ec40278d 100644
--- a/include/linux/posix_acl_xattr.h
+++ b/include/linux/posix_acl_xattr.h
@@ -9,16 +9,12 @@
#ifndef _POSIX_ACL_XATTR_H
#define _POSIX_ACL_XATTR_H
+#include <uapi/linux/xattr.h>
#include <linux/posix_acl.h>
-/* Extended attribute names */
-#define POSIX_ACL_XATTR_ACCESS "system.posix_acl_access"
-#define POSIX_ACL_XATTR_DEFAULT "system.posix_acl_default"
-
/* Supported ACL a_version fields */
#define POSIX_ACL_XATTR_VERSION 0x0002
-
/* An undefined entry e_id value */
#define ACL_UNDEFINED_ID (-1)
diff --git a/include/linux/power/bq27xxx_battery.h b/include/linux/power/bq27xxx_battery.h
index 45f6a7b5b3cb..998d8f1c3c91 100644
--- a/include/linux/power/bq27xxx_battery.h
+++ b/include/linux/power/bq27xxx_battery.h
@@ -1,6 +1,16 @@
#ifndef __LINUX_BQ27X00_BATTERY_H__
#define __LINUX_BQ27X00_BATTERY_H__
+enum bq27xxx_chip {
+ BQ27000 = 1, /* bq27000, bq27200 */
+ BQ27010, /* bq27010, bq27210 */
+ BQ27500, /* bq27500, bq27510, bq27520 */
+ BQ27530, /* bq27530, bq27531 */
+ BQ27541, /* bq27541, bq27542, bq27546, bq27742 */
+ BQ27545, /* bq27545 */
+ BQ27421, /* bq27421, bq27425, bq27441, bq27621 */
+};
+
/**
* struct bq27xxx_plaform_data - Platform data for bq27xxx devices
* @name: Name of the battery.
@@ -12,20 +22,47 @@
* register to be read. The return value should either be the content of
* the passed register or an error value.
*/
-enum bq27xxx_chip {
- BQ27000 = 1, /* bq27000, bq27200 */
- BQ27010, /* bq27010, bq27210 */
- BQ27500, /* bq27500, bq27510, bq27520 */
- BQ27530, /* bq27530, bq27531 */
- BQ27541, /* bq27541, bq27542, bq27546, bq27742 */
- BQ27545, /* bq27545 */
- BQ27421, /* bq27421, bq27425, bq27441, bq27621 */
-};
-
struct bq27xxx_platform_data {
const char *name;
enum bq27xxx_chip chip;
int (*read)(struct device *dev, unsigned int);
};
+struct bq27xxx_device_info;
+struct bq27xxx_access_methods {
+ int (*read)(struct bq27xxx_device_info *di, u8 reg, bool single);
+};
+
+struct bq27xxx_reg_cache {
+ int temperature;
+ int time_to_empty;
+ int time_to_empty_avg;
+ int time_to_full;
+ int charge_full;
+ int cycle_count;
+ int capacity;
+ int energy;
+ int flags;
+ int power_avg;
+ int health;
+};
+
+struct bq27xxx_device_info {
+ struct device *dev;
+ enum bq27xxx_chip chip;
+ const char *name;
+ struct bq27xxx_access_methods bus;
+ struct bq27xxx_reg_cache cache;
+ int charge_design_full;
+ unsigned long last_update;
+ struct delayed_work work;
+ struct power_supply *bat;
+ struct mutex lock;
+ u8 *regs;
+};
+
+void bq27xxx_battery_update(struct bq27xxx_device_info *di);
+int bq27xxx_battery_setup(struct bq27xxx_device_info *di);
+void bq27xxx_battery_teardown(struct bq27xxx_device_info *di);
+
#endif
diff --git a/include/linux/powercap.h b/include/linux/powercap.h
index 4e250417ee30..f0a4e6257dcc 100644
--- a/include/linux/powercap.h
+++ b/include/linux/powercap.h
@@ -208,7 +208,7 @@ struct powercap_zone_constraint_ops {
struct powercap_zone_constraint {
int id;
struct powercap_zone *power_zone;
- struct powercap_zone_constraint_ops *ops;
+ const struct powercap_zone_constraint_ops *ops;
};
@@ -309,7 +309,7 @@ struct powercap_zone *powercap_register_zone(
struct powercap_zone *parent,
const struct powercap_zone_ops *ops,
int nr_constraints,
- struct powercap_zone_constraint_ops *const_ops);
+ const struct powercap_zone_constraint_ops *const_ops);
/**
* powercap_unregister_zone() - Unregister a zone device
diff --git a/include/linux/property.h b/include/linux/property.h
index 0a3705a7c9f2..b51fcd36d892 100644
--- a/include/linux/property.h
+++ b/include/linux/property.h
@@ -73,8 +73,8 @@ int fwnode_property_match_string(struct fwnode_handle *fwnode,
struct fwnode_handle *device_get_next_child_node(struct device *dev,
struct fwnode_handle *child);
-#define device_for_each_child_node(dev, child) \
- for (child = device_get_next_child_node(dev, NULL); child; \
+#define device_for_each_child_node(dev, child) \
+ for (child = device_get_next_child_node(dev, NULL); child; \
child = device_get_next_child_node(dev, child))
void fwnode_handle_put(struct fwnode_handle *fwnode);
@@ -144,24 +144,100 @@ static inline int fwnode_property_read_u64(struct fwnode_handle *fwnode,
/**
* struct property_entry - "Built-in" device property representation.
* @name: Name of the property.
- * @type: Type of the property.
- * @nval: Number of items of type @type making up the value.
- * @value: Value of the property (an array of @nval items of type @type).
+ * @length: Length of data making up the value.
+ * @is_array: True when the property is an array.
+ * @is_string: True when property is a string.
+ * @pointer: Pointer to the property (an array of items of the given type).
+ * @value: Value of the property (when it is a single item of the given type).
*/
struct property_entry {
const char *name;
- enum dev_prop_type type;
- size_t nval;
+ size_t length;
+ bool is_array;
+ bool is_string;
union {
- void *raw_data;
- u8 *u8_data;
- u16 *u16_data;
- u32 *u32_data;
- u64 *u64_data;
- const char **str;
- } value;
+ union {
+ void *raw_data;
+ u8 *u8_data;
+ u16 *u16_data;
+ u32 *u32_data;
+ u64 *u64_data;
+ const char **str;
+ } pointer;
+ union {
+ unsigned long long raw_data;
+ u8 u8_data;
+ u16 u16_data;
+ u32 u32_data;
+ u64 u64_data;
+ const char *str;
+ } value;
+ };
};
+/*
+ * Note: the below four initializers for the anonymous union are carefully
+ * crafted to avoid gcc-4.4.4's problems with initialization of anon unions
+ * and structs.
+ */
+
+#define PROPERTY_ENTRY_INTEGER_ARRAY(_name_, _type_, _val_) \
+{ \
+ .name = _name_, \
+ .length = ARRAY_SIZE(_val_) * sizeof(_type_), \
+ .is_array = true, \
+ .is_string = false, \
+ { .pointer = { _type_##_data = _val_ } }, \
+}
+
+#define PROPERTY_ENTRY_U8_ARRAY(_name_, _val_) \
+ PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u8, _val_)
+#define PROPERTY_ENTRY_U16_ARRAY(_name_, _val_) \
+ PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u16, _val_)
+#define PROPERTY_ENTRY_U32_ARRAY(_name_, _val_) \
+ PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u32, _val_)
+#define PROPERTY_ENTRY_U64_ARRAY(_name_, _val_) \
+ PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u64, _val_)
+
+#define PROPERTY_ENTRY_STRING_ARRAY(_name_, _val_) \
+{ \
+ .name = _name_, \
+ .length = ARRAY_SIZE(_val_) * sizeof(const char *), \
+ .is_array = true, \
+ .is_string = true, \
+ { .pointer = { .str = _val_ } }, \
+}
+
+#define PROPERTY_ENTRY_INTEGER(_name_, _type_, _val_) \
+{ \
+ .name = _name_, \
+ .length = sizeof(_type_), \
+ .is_string = false, \
+ { .value = { ._type_##_data = _val_ } }, \
+}
+
+#define PROPERTY_ENTRY_U8(_name_, _val_) \
+ PROPERTY_ENTRY_INTEGER(_name_, u8, _val_)
+#define PROPERTY_ENTRY_U16(_name_, _val_) \
+ PROPERTY_ENTRY_INTEGER(_name_, u16, _val_)
+#define PROPERTY_ENTRY_U32(_name_, _val_) \
+ PROPERTY_ENTRY_INTEGER(_name_, u32, _val_)
+#define PROPERTY_ENTRY_U64(_name_, _val_) \
+ PROPERTY_ENTRY_INTEGER(_name_, u64, _val_)
+
+#define PROPERTY_ENTRY_STRING(_name_, _val_) \
+{ \
+ .name = _name_, \
+ .length = sizeof(_val_), \
+ .is_string = true, \
+ { .value = { .str = _val_ } }, \
+}
+
+#define PROPERTY_ENTRY_BOOL(_name_) \
+{ \
+ .name = _name_, \
+}
+
/**
* struct property_set - Collection of "built-in" device properties.
* @fwnode: Handle to be pointed to by the fwnode field of struct device.
@@ -172,7 +248,8 @@ struct property_set {
struct property_entry *properties;
};
-void device_add_property_set(struct device *dev, struct property_set *pset);
+int device_add_property_set(struct device *dev, const struct property_set *pset);
+void device_remove_property_set(struct device *dev);
bool device_dma_supported(struct device *dev);
diff --git a/include/linux/proportions.h b/include/linux/proportions.h
index 5440f64d2942..21221338ad18 100644
--- a/include/linux/proportions.h
+++ b/include/linux/proportions.h
@@ -1,7 +1,7 @@
/*
* FLoating proportions
*
- * Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra
*
* This file contains the public data structure and API definitions.
*/
diff --git a/include/linux/qed/common_hsi.h b/include/linux/qed/common_hsi.h
index 6a4347639c03..1d1ba2c5ee7a 100644
--- a/include/linux/qed/common_hsi.h
+++ b/include/linux/qed/common_hsi.h
@@ -9,6 +9,8 @@
#ifndef __COMMON_HSI__
#define __COMMON_HSI__
+#define CORE_SPQE_PAGE_SIZE_BYTES 4096
+
#define FW_MAJOR_VERSION 8
#define FW_MINOR_VERSION 4
#define FW_REVISION_VERSION 2
diff --git a/include/linux/qed/qed_chain.h b/include/linux/qed/qed_chain.h
index b920c3605c46..41b9049b57e2 100644
--- a/include/linux/qed/qed_chain.h
+++ b/include/linux/qed/qed_chain.h
@@ -111,7 +111,8 @@ static inline u16 qed_chain_get_elem_left(struct qed_chain *p_chain)
used = ((u32)0x10000u + (u32)(p_chain->prod_idx)) -
(u32)p_chain->cons_idx;
if (p_chain->mode == QED_CHAIN_MODE_NEXT_PTR)
- used -= (used / p_chain->elem_per_page);
+ used -= p_chain->prod_idx / p_chain->elem_per_page -
+ p_chain->cons_idx / p_chain->elem_per_page;
return p_chain->capacity - used;
}
diff --git a/include/linux/qed/qed_if.h b/include/linux/qed/qed_if.h
index dc9a1353f971..d4a32e878180 100644
--- a/include/linux/qed/qed_if.h
+++ b/include/linux/qed/qed_if.h
@@ -25,6 +25,12 @@
#include <linux/qed/common_hsi.h>
#include <linux/qed/qed_chain.h>
+enum qed_led_mode {
+ QED_LED_MODE_OFF,
+ QED_LED_MODE_ON,
+ QED_LED_MODE_RESTORE
+};
+
#define DIRECT_REG_WR(reg_addr, val) writel((u32)val, \
(void __iomem *)(reg_addr))
@@ -252,6 +258,17 @@ struct qed_common_ops {
void (*chain_free)(struct qed_dev *cdev,
struct qed_chain *p_chain);
+
+/**
+ * @brief set_led - Configure LED mode
+ *
+ * @param cdev
+ * @param mode - LED mode
+ *
+ * @return 0 on success, error otherwise.
+ */
+ int (*set_led)(struct qed_dev *cdev,
+ enum qed_led_mode mode);
};
/**
diff --git a/include/linux/rculist.h b/include/linux/rculist.h
index 5ed540986019..14ec1652daf4 100644
--- a/include/linux/rculist.h
+++ b/include/linux/rculist.h
@@ -179,32 +179,31 @@ static inline void list_replace_rcu(struct list_head *old,
}
/**
- * list_splice_init_rcu - splice an RCU-protected list into an existing list.
+ * __list_splice_init_rcu - join an RCU-protected list into an existing list.
* @list: the RCU-protected list to splice
- * @head: the place in the list to splice the first list into
+ * @prev: points to the last element of the existing list
+ * @next: points to the first element of the existing list
* @sync: function to sync: synchronize_rcu(), synchronize_sched(), ...
*
- * @head can be RCU-read traversed concurrently with this function.
+ * The list pointed to by @prev and @next can be RCU-read traversed
+ * concurrently with this function.
*
* Note that this function blocks.
*
- * Important note: the caller must take whatever action is necessary to
- * prevent any other updates to @head. In principle, it is possible
- * to modify the list as soon as sync() begins execution.
- * If this sort of thing becomes necessary, an alternative version
- * based on call_rcu() could be created. But only if -really-
- * needed -- there is no shortage of RCU API members.
+ * Important note: the caller must take whatever action is necessary to prevent
+ * any other updates to the existing list. In principle, it is possible to
+ * modify the list as soon as sync() begins execution. If this sort of thing
+ * becomes necessary, an alternative version based on call_rcu() could be
+ * created. But only if -really- needed -- there is no shortage of RCU API
+ * members.
*/
-static inline void list_splice_init_rcu(struct list_head *list,
- struct list_head *head,
- void (*sync)(void))
+static inline void __list_splice_init_rcu(struct list_head *list,
+ struct list_head *prev,
+ struct list_head *next,
+ void (*sync)(void))
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
- struct list_head *at = head->next;
-
- if (list_empty(list))
- return;
/*
* "first" and "last" tracking list, so initialize it. RCU readers
@@ -231,10 +230,40 @@ static inline void list_splice_init_rcu(struct list_head *list,
* this function.
*/
- last->next = at;
- rcu_assign_pointer(list_next_rcu(head), first);
- first->prev = head;
- at->prev = last;
+ last->next = next;
+ rcu_assign_pointer(list_next_rcu(prev), first);
+ first->prev = prev;
+ next->prev = last;
+}
+
+/**
+ * list_splice_init_rcu - splice an RCU-protected list into an existing list,
+ * designed for stacks.
+ * @list: the RCU-protected list to splice
+ * @head: the place in the existing list to splice the first list into
+ * @sync: function to sync: synchronize_rcu(), synchronize_sched(), ...
+ */
+static inline void list_splice_init_rcu(struct list_head *list,
+ struct list_head *head,
+ void (*sync)(void))
+{
+ if (!list_empty(list))
+ __list_splice_init_rcu(list, head, head->next, sync);
+}
+
+/**
+ * list_splice_tail_init_rcu - splice an RCU-protected list into an existing
+ * list, designed for queues.
+ * @list: the RCU-protected list to splice
+ * @head: the place in the existing list to splice the first list into
+ * @sync: function to sync: synchronize_rcu(), synchronize_sched(), ...
+ */
+static inline void list_splice_tail_init_rcu(struct list_head *list,
+ struct list_head *head,
+ void (*sync)(void))
+{
+ if (!list_empty(list))
+ __list_splice_init_rcu(list, head->prev, head, sync);
}
/**
@@ -305,6 +334,42 @@ static inline void list_splice_init_rcu(struct list_head *list,
pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
/**
+ * list_entry_lockless - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_head within the struct.
+ *
+ * This primitive may safely run concurrently with the _rcu list-mutation
+ * primitives such as list_add_rcu(), but requires some implicit RCU
+ * read-side guarding. One example is running within a special
+ * exception-time environment where preemption is disabled and where
+ * lockdep cannot be invoked (in which case updaters must use RCU-sched,
+ * as in synchronize_sched(), call_rcu_sched(), and friends). Another
+ * example is when items are added to the list, but never deleted.
+ */
+#define list_entry_lockless(ptr, type, member) \
+ container_of((typeof(ptr))lockless_dereference(ptr), type, member)
+
+/**
+ * list_for_each_entry_lockless - iterate over rcu list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * This primitive may safely run concurrently with the _rcu list-mutation
+ * primitives such as list_add_rcu(), but requires some implicit RCU
+ * read-side guarding. One example is running within a special
+ * exception-time environment where preemption is disabled and where
+ * lockdep cannot be invoked (in which case updaters must use RCU-sched,
+ * as in synchronize_sched(), call_rcu_sched(), and friends). Another
+ * example is when items are added to the list, but never deleted.
+ */
+#define list_for_each_entry_lockless(pos, head, member) \
+ for (pos = list_entry_lockless((head)->next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry_lockless(pos->member.next, typeof(*pos), member))
+
+/**
* list_for_each_entry_continue_rcu - continue iteration over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h
index a0189ba67fde..14e6f47ee16f 100644
--- a/include/linux/rcupdate.h
+++ b/include/linux/rcupdate.h
@@ -48,10 +48,17 @@
#include <asm/barrier.h>
+#ifndef CONFIG_TINY_RCU
extern int rcu_expedited; /* for sysctl */
+extern int rcu_normal; /* also for sysctl */
+#endif /* #ifndef CONFIG_TINY_RCU */
#ifdef CONFIG_TINY_RCU
/* Tiny RCU doesn't expedite, as its purpose in life is instead to be tiny. */
+static inline bool rcu_gp_is_normal(void) /* Internal RCU use. */
+{
+ return true;
+}
static inline bool rcu_gp_is_expedited(void) /* Internal RCU use. */
{
return false;
@@ -65,6 +72,7 @@ static inline void rcu_unexpedite_gp(void)
{
}
#else /* #ifdef CONFIG_TINY_RCU */
+bool rcu_gp_is_normal(void); /* Internal RCU use. */
bool rcu_gp_is_expedited(void); /* Internal RCU use. */
void rcu_expedite_gp(void);
void rcu_unexpedite_gp(void);
@@ -321,7 +329,6 @@ static inline int rcu_preempt_depth(void)
/* Internal to kernel */
void rcu_init(void);
-void rcu_end_inkernel_boot(void);
void rcu_sched_qs(void);
void rcu_bh_qs(void);
void rcu_check_callbacks(int user);
@@ -329,6 +336,12 @@ struct notifier_block;
int rcu_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu);
+#ifndef CONFIG_TINY_RCU
+void rcu_end_inkernel_boot(void);
+#else /* #ifndef CONFIG_TINY_RCU */
+static inline void rcu_end_inkernel_boot(void) { }
+#endif /* #ifndef CONFIG_TINY_RCU */
+
#ifdef CONFIG_RCU_STALL_COMMON
void rcu_sysrq_start(void);
void rcu_sysrq_end(void);
@@ -379,9 +392,9 @@ static inline void rcu_init_nohz(void)
*/
#define RCU_NONIDLE(a) \
do { \
- rcu_irq_enter(); \
+ rcu_irq_enter_irqson(); \
do { a; } while (0); \
- rcu_irq_exit(); \
+ rcu_irq_exit_irqson(); \
} while (0)
/*
@@ -741,7 +754,7 @@ static inline void rcu_preempt_sleep_check(void)
* The tracing infrastructure traces RCU (we want that), but unfortunately
* some of the RCU checks causes tracing to lock up the system.
*
- * The tracing version of rcu_dereference_raw() must not call
+ * The no-tracing version of rcu_dereference_raw() must not call
* rcu_read_lock_held().
*/
#define rcu_dereference_raw_notrace(p) __rcu_dereference_check((p), 1, __rcu)
diff --git a/include/linux/rcutiny.h b/include/linux/rcutiny.h
index 4c1aaf9cce7b..64809aea661c 100644
--- a/include/linux/rcutiny.h
+++ b/include/linux/rcutiny.h
@@ -181,6 +181,14 @@ static inline void rcu_irq_enter(void)
{
}
+static inline void rcu_irq_exit_irqson(void)
+{
+}
+
+static inline void rcu_irq_enter_irqson(void)
+{
+}
+
static inline void rcu_irq_exit(void)
{
}
diff --git a/include/linux/rcutree.h b/include/linux/rcutree.h
index 60d15a080d7c..ad1eda9fa4da 100644
--- a/include/linux/rcutree.h
+++ b/include/linux/rcutree.h
@@ -37,7 +37,7 @@ void rcu_cpu_stall_reset(void);
/*
* Note a virtualization-based context switch. This is simply a
* wrapper around rcu_note_context_switch(), which allows TINY_RCU
- * to save a few bytes.
+ * to save a few bytes. The caller must have disabled interrupts.
*/
static inline void rcu_virt_note_context_switch(int cpu)
{
@@ -97,6 +97,8 @@ void rcu_idle_enter(void);
void rcu_idle_exit(void);
void rcu_irq_enter(void);
void rcu_irq_exit(void);
+void rcu_irq_enter_irqson(void);
+void rcu_irq_exit_irqson(void);
void exit_rcu(void);
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index d68bb402120e..18394343f489 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -788,10 +788,16 @@ int regmap_fields_update_bits(struct regmap_field *field, unsigned int id,
*
* @reg_offset: Offset of the status/mask register within the bank
* @mask: Mask used to flag/control the register.
+ * @type_reg_offset: Offset register for the irq type setting.
+ * @type_rising_mask: Mask bit to configure RISING type irq.
+ * @type_falling_mask: Mask bit to configure FALLING type irq.
*/
struct regmap_irq {
unsigned int reg_offset;
unsigned int mask;
+ unsigned int type_reg_offset;
+ unsigned int type_rising_mask;
+ unsigned int type_falling_mask;
};
#define REGMAP_IRQ_REG(_irq, _off, _mask) \
@@ -811,18 +817,23 @@ struct regmap_irq {
* @ack_base: Base ack address. If zero then the chip is clear on read.
* Using zero value is possible with @use_ack bit.
* @wake_base: Base address for wake enables. If zero unsupported.
+ * @type_base: Base address for irq type. If zero unsupported.
* @irq_reg_stride: Stride to use for chips where registers are not contiguous.
* @init_ack_masked: Ack all masked interrupts once during initalization.
* @mask_invert: Inverted mask register: cleared bits are masked out.
* @use_ack: Use @ack register even if it is zero.
* @ack_invert: Inverted ack register: cleared bits for ack.
* @wake_invert: Inverted wake register: cleared bits are wake enabled.
+ * @type_invert: Invert the type flags.
* @runtime_pm: Hold a runtime PM lock on the device when accessing it.
*
* @num_regs: Number of registers in each control bank.
* @irqs: Descriptors for individual IRQs. Interrupt numbers are
* assigned based on the index in the array of the interrupt.
* @num_irqs: Number of descriptors.
+ * @num_type_reg: Number of type registers.
+ * @type_reg_stride: Stride to use for chips where type registers are not
+ * contiguous.
*/
struct regmap_irq_chip {
const char *name;
@@ -832,6 +843,7 @@ struct regmap_irq_chip {
unsigned int unmask_base;
unsigned int ack_base;
unsigned int wake_base;
+ unsigned int type_base;
unsigned int irq_reg_stride;
bool init_ack_masked:1;
bool mask_invert:1;
@@ -839,11 +851,15 @@ struct regmap_irq_chip {
bool ack_invert:1;
bool wake_invert:1;
bool runtime_pm:1;
+ bool type_invert:1;
int num_regs;
const struct regmap_irq *irqs;
int num_irqs;
+
+ int num_type_reg;
+ unsigned int type_reg_stride;
};
struct regmap_irq_chip_data;
@@ -1021,7 +1037,7 @@ static inline void regmap_async_complete(struct regmap *map)
}
static inline int regmap_register_patch(struct regmap *map,
- const struct reg_default *regs,
+ const struct reg_sequence *regs,
int num_regs)
{
WARN_ONCE(1, "regmap API is disabled");
diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h
index 9e0e76992be0..48603506f8de 100644
--- a/include/linux/regulator/consumer.h
+++ b/include/linux/regulator/consumer.h
@@ -140,6 +140,8 @@ struct regulator;
*
* @supply: The name of the supply. Initialised by the user before
* using the bulk regulator APIs.
+ * @optional: The supply should be considered optional. Initialised by the user
+ * before using the bulk regulator APIs.
* @consumer: The regulator consumer for the supply. This will be managed
* by the bulk API.
*
@@ -149,6 +151,7 @@ struct regulator;
*/
struct regulator_bulk_data {
const char *supply;
+ bool optional;
struct regulator *consumer;
/* private: Internal use */
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index 9c2903e58adb..16ac9e108806 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -302,6 +302,8 @@ struct regulator_desc {
unsigned int vsel_reg;
unsigned int vsel_mask;
+ unsigned int csel_reg;
+ unsigned int csel_mask;
unsigned int apply_reg;
unsigned int apply_bit;
unsigned int enable_reg;
diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h
index 843ceca9a21e..63bd7601b6de 100644
--- a/include/linux/rhashtable.h
+++ b/include/linux/rhashtable.h
@@ -19,6 +19,7 @@
#include <linux/atomic.h>
#include <linux/compiler.h>
+#include <linux/err.h>
#include <linux/errno.h>
#include <linux/jhash.h>
#include <linux/list_nulls.h>
@@ -339,10 +340,11 @@ static inline int lockdep_rht_bucket_is_held(const struct bucket_table *tbl,
int rhashtable_init(struct rhashtable *ht,
const struct rhashtable_params *params);
-int rhashtable_insert_slow(struct rhashtable *ht, const void *key,
- struct rhash_head *obj,
- struct bucket_table *old_tbl);
-int rhashtable_insert_rehash(struct rhashtable *ht);
+struct bucket_table *rhashtable_insert_slow(struct rhashtable *ht,
+ const void *key,
+ struct rhash_head *obj,
+ struct bucket_table *old_tbl);
+int rhashtable_insert_rehash(struct rhashtable *ht, struct bucket_table *tbl);
int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter);
void rhashtable_walk_exit(struct rhashtable_iter *iter);
@@ -598,9 +600,11 @@ restart:
new_tbl = rht_dereference_rcu(tbl->future_tbl, ht);
if (unlikely(new_tbl)) {
- err = rhashtable_insert_slow(ht, key, obj, new_tbl);
- if (err == -EAGAIN)
+ tbl = rhashtable_insert_slow(ht, key, obj, new_tbl);
+ if (!IS_ERR_OR_NULL(tbl))
goto slow_path;
+
+ err = PTR_ERR(tbl);
goto out;
}
@@ -611,7 +615,7 @@ restart:
if (unlikely(rht_grow_above_100(ht, tbl))) {
slow_path:
spin_unlock_bh(lock);
- err = rhashtable_insert_rehash(ht);
+ err = rhashtable_insert_rehash(ht, tbl);
rcu_read_unlock();
if (err)
return err;
@@ -819,4 +823,86 @@ out:
return err;
}
+/* Internal function, please use rhashtable_replace_fast() instead */
+static inline int __rhashtable_replace_fast(
+ struct rhashtable *ht, struct bucket_table *tbl,
+ struct rhash_head *obj_old, struct rhash_head *obj_new,
+ const struct rhashtable_params params)
+{
+ struct rhash_head __rcu **pprev;
+ struct rhash_head *he;
+ spinlock_t *lock;
+ unsigned int hash;
+ int err = -ENOENT;
+
+ /* Minimally, the old and new objects must have same hash
+ * (which should mean identifiers are the same).
+ */
+ hash = rht_head_hashfn(ht, tbl, obj_old, params);
+ if (hash != rht_head_hashfn(ht, tbl, obj_new, params))
+ return -EINVAL;
+
+ lock = rht_bucket_lock(tbl, hash);
+
+ spin_lock_bh(lock);
+
+ pprev = &tbl->buckets[hash];
+ rht_for_each(he, tbl, hash) {
+ if (he != obj_old) {
+ pprev = &he->next;
+ continue;
+ }
+
+ rcu_assign_pointer(obj_new->next, obj_old->next);
+ rcu_assign_pointer(*pprev, obj_new);
+ err = 0;
+ break;
+ }
+
+ spin_unlock_bh(lock);
+
+ return err;
+}
+
+/**
+ * rhashtable_replace_fast - replace an object in hash table
+ * @ht: hash table
+ * @obj_old: pointer to hash head inside object being replaced
+ * @obj_new: pointer to hash head inside object which is new
+ * @params: hash table parameters
+ *
+ * Replacing an object doesn't affect the number of elements in the hash table
+ * or bucket, so we don't need to worry about shrinking or expanding the
+ * table here.
+ *
+ * Returns zero on success, -ENOENT if the entry could not be found,
+ * -EINVAL if hash is not the same for the old and new objects.
+ */
+static inline int rhashtable_replace_fast(
+ struct rhashtable *ht, struct rhash_head *obj_old,
+ struct rhash_head *obj_new,
+ const struct rhashtable_params params)
+{
+ struct bucket_table *tbl;
+ int err;
+
+ rcu_read_lock();
+
+ tbl = rht_dereference_rcu(ht->tbl, ht);
+
+ /* Because we have already taken (and released) the bucket
+ * lock in old_tbl, if we find that future_tbl is not yet
+ * visible then that guarantees the entry to still be in
+ * the old tbl if it exists.
+ */
+ while ((err = __rhashtable_replace_fast(ht, tbl, obj_old,
+ obj_new, params)) &&
+ (tbl = rht_dereference_rcu(tbl->future_tbl, ht)))
+ ;
+
+ rcu_read_unlock();
+
+ return err;
+}
+
#endif /* _LINUX_RHASHTABLE_H */
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index 4be5048b1fbe..c006cc900c44 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -84,6 +84,11 @@ void net_inc_ingress_queue(void);
void net_dec_ingress_queue(void);
#endif
+#ifdef CONFIG_NET_EGRESS
+void net_inc_egress_queue(void);
+void net_dec_egress_queue(void);
+#endif
+
extern void rtnetlink_init(void);
extern void __rtnl_unlock(void);
diff --git a/include/linux/sched.h b/include/linux/sched.h
index edad7a43edea..61aa9bbea871 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -177,9 +177,9 @@ extern void get_iowait_load(unsigned long *nr_waiters, unsigned long *load);
extern void calc_global_load(unsigned long ticks);
#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON)
-extern void update_cpu_load_nohz(void);
+extern void update_cpu_load_nohz(int active);
#else
-static inline void update_cpu_load_nohz(void) { }
+static inline void update_cpu_load_nohz(int active) { }
#endif
extern unsigned long get_parent_ip(unsigned long addr);
@@ -377,6 +377,7 @@ extern void scheduler_tick(void);
extern void sched_show_task(struct task_struct *p);
#ifdef CONFIG_LOCKUP_DETECTOR
+extern void touch_softlockup_watchdog_sched(void);
extern void touch_softlockup_watchdog(void);
extern void touch_softlockup_watchdog_sync(void);
extern void touch_all_softlockup_watchdogs(void);
@@ -387,6 +388,9 @@ extern unsigned int softlockup_panic;
extern unsigned int hardlockup_panic;
void lockup_detector_init(void);
#else
+static inline void touch_softlockup_watchdog_sched(void)
+{
+}
static inline void touch_softlockup_watchdog(void)
{
}
@@ -830,6 +834,7 @@ struct user_struct {
unsigned long mq_bytes; /* How many bytes can be allocated to mqueue? */
#endif
unsigned long locked_shm; /* How many pages of mlocked shm ? */
+ unsigned long unix_inflight; /* How many files in flight in unix sockets */
#ifdef CONFIG_KEYS
struct key *uid_keyring; /* UID specific keyring */
@@ -1268,8 +1273,13 @@ struct sched_entity {
#endif
#ifdef CONFIG_SMP
- /* Per entity load average tracking */
- struct sched_avg avg;
+ /*
+ * Per entity load average tracking.
+ *
+ * Put into separate cache line so it does not
+ * collide with read-mostly values above.
+ */
+ struct sched_avg avg ____cacheline_aligned_in_smp;
#endif
};
@@ -1455,14 +1465,15 @@ struct task_struct {
/* Used for emulating ABI behavior of previous Linux versions */
unsigned int personality;
- unsigned in_execve:1; /* Tell the LSMs that the process is doing an
- * execve */
- unsigned in_iowait:1;
-
- /* Revert to default priority/policy when forking */
+ /* scheduler bits, serialized by scheduler locks */
unsigned sched_reset_on_fork:1;
unsigned sched_contributes_to_load:1;
unsigned sched_migrated:1;
+ unsigned :0; /* force alignment to the next boundary */
+
+ /* unserialized, strictly 'current' */
+ unsigned in_execve:1; /* bit to tell LSMs we're in execve */
+ unsigned in_iowait:1;
#ifdef CONFIG_MEMCG
unsigned memcg_may_oom:1;
#endif
@@ -1519,11 +1530,14 @@ struct task_struct {
cputime_t gtime;
struct prev_cputime prev_cputime;
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
- seqlock_t vtime_seqlock;
+ seqcount_t vtime_seqcount;
unsigned long long vtime_snap;
enum {
- VTIME_SLEEPING = 0,
+ /* Task is sleeping or running in a CPU with VTIME inactive */
+ VTIME_INACTIVE = 0,
+ /* Task runs in userspace in a CPU with VTIME active */
VTIME_USER,
+ /* Task runs in kernelspace in a CPU with VTIME active */
VTIME_SYS,
} vtime_snap_whence;
#endif
@@ -2002,7 +2016,8 @@ static inline int pid_alive(const struct task_struct *p)
}
/**
- * is_global_init - check if a task structure is init
+ * is_global_init - check if a task structure is init. Since init
+ * is free to have sub-threads we need to check tgid.
* @tsk: Task structure to be checked.
*
* Check if a task structure is the first user space task the kernel created.
@@ -2011,7 +2026,7 @@ static inline int pid_alive(const struct task_struct *p)
*/
static inline int is_global_init(struct task_struct *tsk)
{
- return tsk->pid == 1;
+ return task_tgid_nr(tsk) == 1;
}
extern struct pid *cad_pid;
diff --git a/include/linux/sched_clock.h b/include/linux/sched_clock.h
index efa931c5cef1..411b52e424e1 100644
--- a/include/linux/sched_clock.h
+++ b/include/linux/sched_clock.h
@@ -10,11 +10,17 @@
#ifdef CONFIG_GENERIC_SCHED_CLOCK
extern void sched_clock_postinit(void);
-#else
-static inline void sched_clock_postinit(void) { }
-#endif
extern void sched_clock_register(u64 (*read)(void), int bits,
unsigned long rate);
+#else
+static inline void sched_clock_postinit(void) { }
+
+static inline void sched_clock_register(u64 (*read)(void), int bits,
+ unsigned long rate)
+{
+ ;
+}
+#endif
#endif
diff --git a/include/linux/scpi_protocol.h b/include/linux/scpi_protocol.h
index 80af3cd35ae4..72ce932c69b2 100644
--- a/include/linux/scpi_protocol.h
+++ b/include/linux/scpi_protocol.h
@@ -71,7 +71,7 @@ struct scpi_ops {
int (*sensor_get_value)(u16, u32 *);
};
-#if IS_ENABLED(CONFIG_ARM_SCPI_PROTOCOL)
+#if IS_REACHABLE(CONFIG_ARM_SCPI_PROTOCOL)
struct scpi_ops *get_scpi_ops(void);
#else
static inline struct scpi_ops *get_scpi_ops(void) { return NULL; }
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 297d4fa1cfe5..e03d6ba5e5b4 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -145,11 +145,12 @@ struct uart_port {
#define UPIO_PORT (SERIAL_IO_PORT) /* 8b I/O port access */
#define UPIO_HUB6 (SERIAL_IO_HUB6) /* Hub6 ISA card */
-#define UPIO_MEM (SERIAL_IO_MEM) /* 8b MMIO access */
+#define UPIO_MEM (SERIAL_IO_MEM) /* driver-specific */
#define UPIO_MEM32 (SERIAL_IO_MEM32) /* 32b little endian */
#define UPIO_AU (SERIAL_IO_AU) /* Au1x00 and RT288x type IO */
#define UPIO_TSI (SERIAL_IO_TSI) /* Tsi108/109 type IO */
#define UPIO_MEM32BE (SERIAL_IO_MEM32BE) /* 32b big endian */
+#define UPIO_MEM16 (SERIAL_IO_MEM16) /* 16b little endian */
unsigned int read_status_mask; /* driver specific */
unsigned int ignore_status_mask; /* driver specific */
diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h
index 7c536ac5be05..9f2bfd055742 100644
--- a/include/linux/serial_sci.h
+++ b/include/linux/serial_sci.h
@@ -32,6 +32,7 @@ enum {
SCIx_SH2_SCIF_FIFODATA_REGTYPE,
SCIx_SH3_SCIF_REGTYPE,
SCIx_SH4_SCIF_REGTYPE,
+ SCIx_SH4_SCIF_BRG_REGTYPE,
SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
SCIx_SH4_SCIF_FIFODATA_REGTYPE,
SCIx_SH7705_SCIF_REGTYPE,
diff --git a/include/linux/sh_eth.h b/include/linux/sh_eth.h
index 8c9131db2b25..f2e27e078362 100644
--- a/include/linux/sh_eth.h
+++ b/include/linux/sh_eth.h
@@ -4,7 +4,7 @@
#include <linux/phy.h>
#include <linux/if_ether.h>
-enum {EDMAC_LITTLE_ENDIAN, EDMAC_BIG_ENDIAN};
+enum {EDMAC_LITTLE_ENDIAN};
struct sh_eth_plat_data {
int phy;
diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
index 50777b5b1e4c..a43f41cb3c43 100644
--- a/include/linux/shmem_fs.h
+++ b/include/linux/shmem_fs.h
@@ -60,6 +60,10 @@ extern struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
extern void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end);
extern int shmem_unuse(swp_entry_t entry, struct page *page);
+extern unsigned long shmem_swap_usage(struct vm_area_struct *vma);
+extern unsigned long shmem_partial_swap_usage(struct address_space *mapping,
+ pgoff_t start, pgoff_t end);
+
static inline struct page *shmem_read_mapping_page(
struct address_space *mapping, pgoff_t index)
{
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 4355129fff91..07f9ccd28654 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -39,11 +39,55 @@
#include <linux/in6.h>
#include <net/flow.h>
-/* A. Checksumming of received packets by device.
+/* The interface for checksum offload between the stack and networking drivers
+ * is as follows...
+ *
+ * A. IP checksum related features
+ *
+ * Drivers advertise checksum offload capabilities in the features of a device.
+ * From the stack's point of view these are capabilities offered by the driver,
+ * a driver typically only advertises features that it is capable of offloading
+ * to its device.
+ *
+ * The checksum related features are:
+ *
+ * NETIF_F_HW_CSUM - The driver (or its device) is able to compute one
+ * IP (one's complement) checksum for any combination
+ * of protocols or protocol layering. The checksum is
+ * computed and set in a packet per the CHECKSUM_PARTIAL
+ * interface (see below).
+ *
+ * NETIF_F_IP_CSUM - Driver (device) is only able to checksum plain
+ * TCP or UDP packets over IPv4. These are specifically
+ * unencapsulated packets of the form IPv4|TCP or
+ * IPv4|UDP where the Protocol field in the IPv4 header
+ * is TCP or UDP. The IPv4 header may contain IP options
+ * This feature cannot be set in features for a device
+ * with NETIF_F_HW_CSUM also set. This feature is being
+ * DEPRECATED (see below).
+ *
+ * NETIF_F_IPV6_CSUM - Driver (device) is only able to checksum plain
+ * TCP or UDP packets over IPv6. These are specifically
+ * unencapsulated packets of the form IPv6|TCP or
+ * IPv4|UDP where the Next Header field in the IPv6
+ * header is either TCP or UDP. IPv6 extension headers
+ * are not supported with this feature. This feature
+ * cannot be set in features for a device with
+ * NETIF_F_HW_CSUM also set. This feature is being
+ * DEPRECATED (see below).
+ *
+ * NETIF_F_RXCSUM - Driver (device) performs receive checksum offload.
+ * This flag is used only used to disable the RX checksum
+ * feature for a device. The stack will accept receive
+ * checksum indication in packets received on a device
+ * regardless of whether NETIF_F_RXCSUM is set.
+ *
+ * B. Checksumming of received packets by device. Indication of checksum
+ * verification is in set skb->ip_summed. Possible values are:
*
* CHECKSUM_NONE:
*
- * Device failed to checksum this packet e.g. due to lack of capabilities.
+ * Device did not checksum this packet e.g. due to lack of capabilities.
* The packet contains full (though not verified) checksum in packet but
* not in skb->csum. Thus, skb->csum is undefined in this case.
*
@@ -53,9 +97,8 @@
* (as in CHECKSUM_COMPLETE), but it does parse headers and verify checksums
* for specific protocols. For such packets it will set CHECKSUM_UNNECESSARY
* if their checksums are okay. skb->csum is still undefined in this case
- * though. It is a bad option, but, unfortunately, nowadays most vendors do
- * this. Apparently with the secret goal to sell you new devices, when you
- * will add new protocol to your host, f.e. IPv6 8)
+ * though. A driver or device must never modify the checksum field in the
+ * packet even if checksum is verified.
*
* CHECKSUM_UNNECESSARY is applicable to following protocols:
* TCP: IPv6 and IPv4.
@@ -96,40 +139,77 @@
* packet that are after the checksum being offloaded are not considered to
* be verified.
*
- * B. Checksumming on output.
- *
- * CHECKSUM_NONE:
- *
- * The skb was already checksummed by the protocol, or a checksum is not
- * required.
+ * C. Checksumming on transmit for non-GSO. The stack requests checksum offload
+ * in the skb->ip_summed for a packet. Values are:
*
* CHECKSUM_PARTIAL:
*
- * The device is required to checksum the packet as seen by hard_start_xmit()
+ * The driver is required to checksum the packet as seen by hard_start_xmit()
* from skb->csum_start up to the end, and to record/write the checksum at
- * offset skb->csum_start + skb->csum_offset.
+ * offset skb->csum_start + skb->csum_offset. A driver may verify that the
+ * csum_start and csum_offset values are valid values given the length and
+ * offset of the packet, however they should not attempt to validate that the
+ * checksum refers to a legitimate transport layer checksum-- it is the
+ * purview of the stack to validate that csum_start and csum_offset are set
+ * correctly.
+ *
+ * When the stack requests checksum offload for a packet, the driver MUST
+ * ensure that the checksum is set correctly. A driver can either offload the
+ * checksum calculation to the device, or call skb_checksum_help (in the case
+ * that the device does not support offload for a particular checksum).
+ *
+ * NETIF_F_IP_CSUM and NETIF_F_IPV6_CSUM are being deprecated in favor of
+ * NETIF_F_HW_CSUM. New devices should use NETIF_F_HW_CSUM to indicate
+ * checksum offload capability. If a device has limited checksum capabilities
+ * (for instance can only perform NETIF_F_IP_CSUM or NETIF_F_IPV6_CSUM as
+ * described above) a helper function can be called to resolve
+ * CHECKSUM_PARTIAL. The helper functions are skb_csum_off_chk*. The helper
+ * function takes a spec argument that describes the protocol layer that is
+ * supported for checksum offload and can be called for each packet. If a
+ * packet does not match the specification for offload, skb_checksum_help
+ * is called to resolve the checksum.
*
- * The device must show its capabilities in dev->features, set up at device
- * setup time, e.g. netdev_features.h:
+ * CHECKSUM_NONE:
*
- * NETIF_F_HW_CSUM - It's a clever device, it's able to checksum everything.
- * NETIF_F_IP_CSUM - Device is dumb, it's able to checksum only TCP/UDP over
- * IPv4. Sigh. Vendors like this way for an unknown reason.
- * Though, see comment above about CHECKSUM_UNNECESSARY. 8)
- * NETIF_F_IPV6_CSUM - About as dumb as the last one but does IPv6 instead.
- * NETIF_F_... - Well, you get the picture.
+ * The skb was already checksummed by the protocol, or a checksum is not
+ * required.
*
* CHECKSUM_UNNECESSARY:
*
- * Normally, the device will do per protocol specific checksumming. Protocol
- * implementations that do not want the NIC to perform the checksum
- * calculation should use this flag in their outgoing skbs.
- *
- * NETIF_F_FCOE_CRC - This indicates that the device can do FCoE FC CRC
- * offload. Correspondingly, the FCoE protocol driver
- * stack should use CHECKSUM_UNNECESSARY.
+ * This has the same meaning on as CHECKSUM_NONE for checksum offload on
+ * output.
*
- * Any questions? No questions, good. --ANK
+ * CHECKSUM_COMPLETE:
+ * Not used in checksum output. If a driver observes a packet with this value
+ * set in skbuff, if should treat as CHECKSUM_NONE being set.
+ *
+ * D. Non-IP checksum (CRC) offloads
+ *
+ * NETIF_F_SCTP_CRC - This feature indicates that a device is capable of
+ * offloading the SCTP CRC in a packet. To perform this offload the stack
+ * will set ip_summed to CHECKSUM_PARTIAL and set csum_start and csum_offset
+ * accordingly. Note the there is no indication in the skbuff that the
+ * CHECKSUM_PARTIAL refers to an SCTP checksum, a driver that supports
+ * both IP checksum offload and SCTP CRC offload must verify which offload
+ * is configured for a packet presumably by inspecting packet headers.
+ *
+ * NETIF_F_FCOE_CRC - This feature indicates that a device is capable of
+ * offloading the FCOE CRC in a packet. To perform this offload the stack
+ * will set ip_summed to CHECKSUM_PARTIAL and set csum_start and csum_offset
+ * accordingly. Note the there is no indication in the skbuff that the
+ * CHECKSUM_PARTIAL refers to an FCOE checksum, a driver that supports
+ * both IP checksum offload and FCOE CRC offload must verify which offload
+ * is configured for a packet presumably by inspecting packet headers.
+ *
+ * E. Checksumming on output with GSO.
+ *
+ * In the case of a GSO packet (skb_is_gso(skb) is true), checksum offload
+ * is implied by the SKB_GSO_* flags in gso_type. Most obviously, if the
+ * gso_type is SKB_GSO_TCPV4 or SKB_GSO_TCPV6, TCP checksum offload as
+ * part of the GSO operation is implied. If a checksum is being offloaded
+ * with GSO then ip_summed is CHECKSUM_PARTIAL, csum_start and csum_offset
+ * are set to refer to the outermost checksum being offload (two offloaded
+ * checksums are possible with UDP encapsulation).
*/
/* Don't change this without changing skb_csum_unnecessary! */
@@ -833,7 +913,7 @@ struct sk_buff_fclones {
* skb_fclone_busy - check if fclone is busy
* @skb: buffer
*
- * Returns true is skb is a fast clone, and its clone is not freed.
+ * Returns true if skb is a fast clone, and its clone is not freed.
* Some drivers call skb_orphan() in their ndo_start_xmit(),
* so we also check that this didnt happen.
*/
@@ -1082,9 +1162,6 @@ static inline void skb_copy_hash(struct sk_buff *to, const struct sk_buff *from)
static inline void skb_sender_cpu_clear(struct sk_buff *skb)
{
-#ifdef CONFIG_XPS
- skb->sender_cpu = 0;
-#endif
}
#ifdef NET_SKBUFF_DATA_USES_OFFSET
@@ -1942,6 +2019,11 @@ static inline unsigned char *skb_inner_transport_header(const struct sk_buff
return skb->head + skb->inner_transport_header;
}
+static inline int skb_inner_transport_offset(const struct sk_buff *skb)
+{
+ return skb_inner_transport_header(skb) - skb->data;
+}
+
static inline void skb_reset_inner_transport_header(struct sk_buff *skb)
{
skb->inner_transport_header = skb->data - skb->head;
@@ -2723,6 +2805,23 @@ static inline void skb_postpull_rcsum(struct sk_buff *skb,
unsigned char *skb_pull_rcsum(struct sk_buff *skb, unsigned int len);
+static inline void skb_postpush_rcsum(struct sk_buff *skb,
+ const void *start, unsigned int len)
+{
+ /* For performing the reverse operation to skb_postpull_rcsum(),
+ * we can instead of ...
+ *
+ * skb->csum = csum_add(skb->csum, csum_partial(start, len, 0));
+ *
+ * ... just use this equivalent version here to save a few
+ * instructions. Feeding csum of 0 in csum_partial() and later
+ * on adding skb->csum is equivalent to feed skb->csum in the
+ * first place.
+ */
+ if (skb->ip_summed == CHECKSUM_COMPLETE)
+ skb->csum = csum_partial(start, len, skb->csum);
+}
+
/**
* pskb_trim_rcsum - trim received skb and update checksum
* @skb: buffer to trim
@@ -2788,6 +2887,12 @@ static inline void skb_frag_list_init(struct sk_buff *skb)
#define skb_walk_frags(skb, iter) \
for (iter = skb_shinfo(skb)->frag_list; iter; iter = iter->next)
+
+int __skb_wait_for_more_packets(struct sock *sk, int *err, long *timeo_p,
+ const struct sk_buff *skb);
+struct sk_buff *__skb_try_recv_datagram(struct sock *sk, unsigned flags,
+ int *peeked, int *off, int *err,
+ struct sk_buff **last);
struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags,
int *peeked, int *off, int *err);
struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock,
diff --git a/include/linux/slab.h b/include/linux/slab.h
index 2037a861e367..3ffee7422012 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -86,6 +86,11 @@
#else
# define SLAB_FAILSLAB 0x00000000UL
#endif
+#ifdef CONFIG_MEMCG_KMEM
+# define SLAB_ACCOUNT 0x04000000UL /* Account to memcg */
+#else
+# define SLAB_ACCOUNT 0x00000000UL
+#endif
/* The following flags affect the page allocator grouping pages by mobility */
#define SLAB_RECLAIM_ACCOUNT 0x00020000UL /* Objects are reclaimable */
diff --git a/include/linux/soc/ti/knav_dma.h b/include/linux/soc/ti/knav_dma.h
index dad035c16d94..343c13ac4f71 100644
--- a/include/linux/soc/ti/knav_dma.h
+++ b/include/linux/soc/ti/knav_dma.h
@@ -144,17 +144,17 @@ struct knav_dma_cfg {
* @psdata: Protocol specific
*/
struct knav_dma_desc {
- u32 desc_info;
- u32 tag_info;
- u32 packet_info;
- u32 buff_len;
- u32 buff;
- u32 next_desc;
- u32 orig_len;
- u32 orig_buff;
- u32 epib[KNAV_DMA_NUM_EPIB_WORDS];
- u32 psdata[KNAV_DMA_NUM_PS_WORDS];
- u32 pad[4];
+ __le32 desc_info;
+ __le32 tag_info;
+ __le32 packet_info;
+ __le32 buff_len;
+ __le32 buff;
+ __le32 next_desc;
+ __le32 orig_len;
+ __le32 orig_buff;
+ __le32 epib[KNAV_DMA_NUM_EPIB_WORDS];
+ __le32 psdata[KNAV_DMA_NUM_PS_WORDS];
+ __le32 pad[4];
} ____cacheline_aligned;
#if IS_ENABLED(CONFIG_KEYSTONE_NAVIGATOR_DMA)
diff --git a/include/linux/sock_diag.h b/include/linux/sock_diag.h
index fddebc617469..4018b48f2b3b 100644
--- a/include/linux/sock_diag.h
+++ b/include/linux/sock_diag.h
@@ -15,6 +15,7 @@ struct sock_diag_handler {
__u8 family;
int (*dump)(struct sk_buff *skb, struct nlmsghdr *nlh);
int (*get_info)(struct sk_buff *skb, struct sock *sk);
+ int (*destroy)(struct sk_buff *skb, struct nlmsghdr *nlh);
};
int sock_diag_register(const struct sock_diag_handler *h);
@@ -68,4 +69,5 @@ bool sock_diag_has_destroy_listeners(const struct sock *sk)
}
void sock_diag_broadcast_destroy(struct sock *sk);
+int sock_diag_destroy(struct sock *sk, int err);
#endif
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index cce80e6dc7d1..53be3a4c60cb 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -425,6 +425,12 @@ struct spi_master {
#define SPI_MASTER_MUST_RX BIT(3) /* requires rx */
#define SPI_MASTER_MUST_TX BIT(4) /* requires tx */
+ /*
+ * on some hardware transfer size may be constrained
+ * the limit may depend on device transfer settings
+ */
+ size_t (*max_transfer_size)(struct spi_device *spi);
+
/* lock and mutex for SPI bus locking */
spinlock_t bus_lock_spinlock;
struct mutex bus_lock_mutex;
@@ -762,10 +768,15 @@ struct spi_message {
void *state;
};
+static inline void spi_message_init_no_memset(struct spi_message *m)
+{
+ INIT_LIST_HEAD(&m->transfers);
+}
+
static inline void spi_message_init(struct spi_message *m)
{
memset(m, 0, sizeof *m);
- INIT_LIST_HEAD(&m->transfers);
+ spi_message_init_no_memset(m);
}
static inline void
@@ -832,6 +843,15 @@ extern int spi_async(struct spi_device *spi, struct spi_message *message);
extern int spi_async_locked(struct spi_device *spi,
struct spi_message *message);
+static inline size_t
+spi_max_transfer_size(struct spi_device *spi)
+{
+ struct spi_master *master = spi->master;
+ if (!master->max_transfer_size)
+ return SIZE_MAX;
+ return master->max_transfer_size(spi);
+}
+
/*---------------------------------------------------------------------------*/
/* All these synchronous SPI transfer routines are utilities layered
@@ -1115,12 +1135,7 @@ spi_add_device(struct spi_device *spi);
extern struct spi_device *
spi_new_device(struct spi_master *, struct spi_board_info *);
-static inline void
-spi_unregister_device(struct spi_device *spi)
-{
- if (spi)
- device_unregister(&spi->dev);
-}
+extern void spi_unregister_device(struct spi_device *spi);
extern const struct spi_device_id *
spi_get_device_id(const struct spi_device *sdev);
diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h
index c3d1a525bacc..26a0b3c3ce5f 100644
--- a/include/linux/ssb/ssb.h
+++ b/include/linux/ssb/ssb.h
@@ -524,13 +524,9 @@ struct ssb_init_invariants {
typedef int (*ssb_invariants_func_t)(struct ssb_bus *bus,
struct ssb_init_invariants *iv);
-/* Register a SSB system bus. get_invariants() is called after the
- * basic system devices are initialized.
- * The invariants are usually fetched from some NVRAM.
- * Put the invariants into the struct pointed to by iv. */
-extern int ssb_bus_ssbbus_register(struct ssb_bus *bus,
- unsigned long baseaddr,
- ssb_invariants_func_t get_invariants);
+/* Register SoC bus. */
+extern int ssb_bus_host_soc_register(struct ssb_bus *bus,
+ unsigned long baseaddr);
#ifdef CONFIG_SSB_PCIHOST
extern int ssb_bus_pcibus_register(struct ssb_bus *bus,
struct pci_dev *host_pci);
diff --git a/include/linux/stop_machine.h b/include/linux/stop_machine.h
index 0adedca24c5b..3cc9632dcc2a 100644
--- a/include/linux/stop_machine.h
+++ b/include/linux/stop_machine.h
@@ -29,7 +29,7 @@ struct cpu_stop_work {
int stop_one_cpu(unsigned int cpu, cpu_stop_fn_t fn, void *arg);
int stop_two_cpus(unsigned int cpu1, unsigned int cpu2, cpu_stop_fn_t fn, void *arg);
-void stop_one_cpu_nowait(unsigned int cpu, cpu_stop_fn_t fn, void *arg,
+bool stop_one_cpu_nowait(unsigned int cpu, cpu_stop_fn_t fn, void *arg,
struct cpu_stop_work *work_buf);
int stop_cpus(const struct cpumask *cpumask, cpu_stop_fn_t fn, void *arg);
int try_stop_cpus(const struct cpumask *cpumask, cpu_stop_fn_t fn, void *arg);
@@ -65,7 +65,7 @@ static void stop_one_cpu_nowait_workfn(struct work_struct *work)
preempt_enable();
}
-static inline void stop_one_cpu_nowait(unsigned int cpu,
+static inline bool stop_one_cpu_nowait(unsigned int cpu,
cpu_stop_fn_t fn, void *arg,
struct cpu_stop_work *work_buf)
{
@@ -74,7 +74,10 @@ static inline void stop_one_cpu_nowait(unsigned int cpu,
work_buf->fn = fn;
work_buf->arg = arg;
schedule_work(&work_buf->work);
+ return true;
}
+
+ return false;
}
static inline int stop_cpus(const struct cpumask *cpumask,
@@ -99,7 +102,7 @@ static inline int try_stop_cpus(const struct cpumask *cpumask,
* grabbing every spinlock (and more). So the "read" side to such a
* lock is anything which disables preemption.
*/
-#if defined(CONFIG_STOP_MACHINE) && defined(CONFIG_SMP)
+#if defined(CONFIG_SMP) || defined(CONFIG_HOTPLUG_CPU)
/**
* stop_machine: freeze the machine on all CPUs and run this function
@@ -118,7 +121,7 @@ int stop_machine(cpu_stop_fn_t fn, void *data, const struct cpumask *cpus);
int stop_machine_from_inactive_cpu(cpu_stop_fn_t fn, void *data,
const struct cpumask *cpus);
-#else /* CONFIG_STOP_MACHINE && CONFIG_SMP */
+#else /* CONFIG_SMP || CONFIG_HOTPLUG_CPU */
static inline int stop_machine(cpu_stop_fn_t fn, void *data,
const struct cpumask *cpus)
@@ -137,5 +140,5 @@ static inline int stop_machine_from_inactive_cpu(cpu_stop_fn_t fn, void *data,
return stop_machine(fn, data, cpus);
}
-#endif /* CONFIG_STOP_MACHINE && CONFIG_SMP */
+#endif /* CONFIG_SMP || CONFIG_HOTPLUG_CPU */
#endif /* _LINUX_STOP_MACHINE */
diff --git a/include/linux/string.h b/include/linux/string.h
index 9ef7795e65e4..9eebc66d957a 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -10,6 +10,7 @@
extern char *strndup_user(const char __user *, long);
extern void *memdup_user(const void __user *, size_t);
+extern void *memdup_user_nul(const void __user *, size_t);
/*
* Include machine specific inline routines
diff --git a/include/linux/sunrpc/svc_xprt.h b/include/linux/sunrpc/svc_xprt.h
index 78512cfe1fe6..b7dabc4baafd 100644
--- a/include/linux/sunrpc/svc_xprt.h
+++ b/include/linux/sunrpc/svc_xprt.h
@@ -128,6 +128,7 @@ struct svc_xprt *svc_find_xprt(struct svc_serv *serv, const char *xcl_name,
const unsigned short port);
int svc_xprt_names(struct svc_serv *serv, char *buf, const int buflen);
void svc_add_new_perm_xprt(struct svc_serv *serv, struct svc_xprt *xprt);
+void svc_age_temp_xprts_now(struct svc_serv *, struct sockaddr *);
static inline void svc_xprt_get(struct svc_xprt *xprt)
{
diff --git a/include/linux/sunrpc/svcauth.h b/include/linux/sunrpc/svcauth.h
index 8d71d6577459..c00f53a4ccdd 100644
--- a/include/linux/sunrpc/svcauth.h
+++ b/include/linux/sunrpc/svcauth.h
@@ -23,13 +23,19 @@ struct svc_cred {
kgid_t cr_gid;
struct group_info *cr_group_info;
u32 cr_flavor; /* pseudoflavor */
- char *cr_principal; /* for gss */
+ /* name of form servicetype/hostname@REALM, passed down by
+ * gss-proxy: */
+ char *cr_raw_principal;
+ /* name of form servicetype@hostname, passed down by
+ * rpc.svcgssd, or computed from the above: */
+ char *cr_principal;
struct gss_api_mech *cr_gss_mech;
};
static inline void init_svc_cred(struct svc_cred *cred)
{
cred->cr_group_info = NULL;
+ cred->cr_raw_principal = NULL;
cred->cr_principal = NULL;
cred->cr_gss_mech = NULL;
}
@@ -38,6 +44,7 @@ static inline void free_svc_cred(struct svc_cred *cred)
{
if (cred->cr_group_info)
put_group_info(cred->cr_group_info);
+ kfree(cred->cr_raw_principal);
kfree(cred->cr_principal);
gss_mech_put(cred->cr_gss_mech);
init_svc_cred(cred);
diff --git a/include/linux/swap.h b/include/linux/swap.h
index 7ba7dccaf0e7..066bd21765ad 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -287,7 +287,6 @@ static inline void workingset_node_shadows_dec(struct radix_tree_node *node)
/* linux/mm/page_alloc.c */
extern unsigned long totalram_pages;
extern unsigned long totalreserve_pages;
-extern unsigned long dirty_balance_reserve;
extern unsigned long nr_free_buffer_pages(void);
extern unsigned long nr_free_pagecache_pages(void);
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index a156b82dd14c..185815c96433 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -524,7 +524,7 @@ asmlinkage long sys_chown(const char __user *filename,
asmlinkage long sys_lchown(const char __user *filename,
uid_t user, gid_t group);
asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group);
-#ifdef CONFIG_UID16
+#ifdef CONFIG_HAVE_UID16
asmlinkage long sys_chown16(const char __user *filename,
old_uid_t user, old_gid_t group);
asmlinkage long sys_lchown16(const char __user *filename,
@@ -886,6 +886,9 @@ asmlinkage long sys_execveat(int dfd, const char __user *filename,
const char __user *const __user *envp, int flags);
asmlinkage long sys_membarrier(int cmd, int flags);
+asmlinkage long sys_copy_file_range(int fd_in, loff_t __user *off_in,
+ int fd_out, loff_t __user *off_out,
+ size_t len, unsigned int flags);
asmlinkage long sys_mlock2(unsigned long start, size_t len, int flags);
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 4014a59828fc..613c29bd6baf 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -438,7 +438,8 @@ static inline void thermal_zone_device_unregister(
static inline int thermal_zone_bind_cooling_device(
struct thermal_zone_device *tz, int trip,
struct thermal_cooling_device *cdev,
- unsigned long upper, unsigned long lower)
+ unsigned long upper, unsigned long lower,
+ unsigned int weight)
{ return -ENODEV; }
static inline int thermal_zone_unbind_cooling_device(
struct thermal_zone_device *tz, int trip,
diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h
index ff307b548ed3..b4c2a485b28a 100644
--- a/include/linux/thread_info.h
+++ b/include/linux/thread_info.h
@@ -56,9 +56,10 @@ extern long do_no_restart_syscall(struct restart_block *parm);
#ifdef __KERNEL__
#ifdef CONFIG_DEBUG_STACK_USAGE
-# define THREADINFO_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_ZERO)
+# define THREADINFO_GFP (GFP_KERNEL_ACCOUNT | __GFP_NOTRACK | \
+ __GFP_ZERO)
#else
-# define THREADINFO_GFP (GFP_KERNEL | __GFP_NOTRACK)
+# define THREADINFO_GFP (GFP_KERNEL_ACCOUNT | __GFP_NOTRACK)
#endif
/*
diff --git a/include/linux/time.h b/include/linux/time.h
index beebe3a02d43..297f09f23896 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -125,6 +125,32 @@ static inline bool timeval_valid(const struct timeval *tv)
extern struct timespec timespec_trunc(struct timespec t, unsigned gran);
+/*
+ * Validates if a timespec/timeval used to inject a time offset is valid.
+ * Offsets can be postive or negative. The value of the timeval/timespec
+ * is the sum of its fields, but *NOTE*: the field tv_usec/tv_nsec must
+ * always be non-negative.
+ */
+static inline bool timeval_inject_offset_valid(const struct timeval *tv)
+{
+ /* We don't check the tv_sec as it can be positive or negative */
+
+ /* Can't have more microseconds then a second */
+ if (tv->tv_usec < 0 || tv->tv_usec >= USEC_PER_SEC)
+ return false;
+ return true;
+}
+
+static inline bool timespec_inject_offset_valid(const struct timespec *ts)
+{
+ /* We don't check the tv_sec as it can be positive or negative */
+
+ /* Can't have more nanoseconds then a second */
+ if (ts->tv_nsec < 0 || ts->tv_nsec >= NSEC_PER_SEC)
+ return false;
+ return true;
+}
+
#define CURRENT_TIME (current_kernel_time())
#define CURRENT_TIME_SEC ((struct timespec) { get_seconds(), 0 })
diff --git a/include/linux/tracepoint-defs.h b/include/linux/tracepoint-defs.h
new file mode 100644
index 000000000000..e1ee97c713bf
--- /dev/null
+++ b/include/linux/tracepoint-defs.h
@@ -0,0 +1,27 @@
+#ifndef TRACEPOINT_DEFS_H
+#define TRACEPOINT_DEFS_H 1
+
+/*
+ * File can be included directly by headers who only want to access
+ * tracepoint->key to guard out of line trace calls. Otherwise
+ * linux/tracepoint.h should be used.
+ */
+
+#include <linux/atomic.h>
+#include <linux/static_key.h>
+
+struct tracepoint_func {
+ void *func;
+ void *data;
+ int prio;
+};
+
+struct tracepoint {
+ const char *name; /* Tracepoint name */
+ struct static_key key;
+ void (*regfunc)(void);
+ void (*unregfunc)(void);
+ struct tracepoint_func __rcu *funcs;
+};
+
+#endif
diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h
index 696a339c592c..acd522a91539 100644
--- a/include/linux/tracepoint.h
+++ b/include/linux/tracepoint.h
@@ -17,26 +17,12 @@
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/rcupdate.h>
-#include <linux/static_key.h>
+#include <linux/tracepoint-defs.h>
struct module;
struct tracepoint;
struct notifier_block;
-struct tracepoint_func {
- void *func;
- void *data;
- int prio;
-};
-
-struct tracepoint {
- const char *name; /* Tracepoint name */
- struct static_key key;
- void (*regfunc)(void);
- void (*unregfunc)(void);
- struct tracepoint_func __rcu *funcs;
-};
-
struct trace_enum_map {
const char *system;
const char *enum_string;
@@ -171,8 +157,8 @@ extern void syscall_unregfunc(void);
TP_PROTO(data_proto), \
TP_ARGS(data_args), \
TP_CONDITION(cond), \
- rcu_irq_enter(), \
- rcu_irq_exit()); \
+ rcu_irq_enter_irqson(), \
+ rcu_irq_exit_irqson()); \
}
#else
#define __DECLARE_TRACE_RCU(name, proto, args, cond, data_proto, data_args)
@@ -493,6 +479,10 @@ extern void syscall_unregfunc(void);
#define TRACE_EVENT_FN(name, proto, args, struct, \
assign, print, reg, unreg) \
DECLARE_TRACE(name, PARAMS(proto), PARAMS(args))
+#define TRACE_EVENT_FN_COND(name, proto, args, cond, struct, \
+ assign, print, reg, unreg) \
+ DECLARE_TRACE_CONDITION(name, PARAMS(proto), \
+ PARAMS(args), PARAMS(cond))
#define TRACE_EVENT_CONDITION(name, proto, args, cond, \
struct, assign, print) \
DECLARE_TRACE_CONDITION(name, PARAMS(proto), \
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 5e31f1b99037..2fd8708ea888 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -345,8 +345,6 @@ struct tty_file_private {
#define TTY_HUPPED 18 /* Post driver->hangup() */
#define TTY_LDISC_HALTED 22 /* Line discipline is halted */
-#define TTY_WRITE_FLUSH(tty) tty_write_flush((tty))
-
/* Values for tty->flow_change */
#define TTY_THROTTLE_SAFE 1
#define TTY_UNTHROTTLE_SAFE 2
@@ -395,8 +393,6 @@ static inline int __init tty_init(void)
{ return 0; }
#endif
-extern void tty_write_flush(struct tty_struct *);
-
extern struct ktermios tty_std_termios;
extern int vcs_init(void);
@@ -419,9 +415,8 @@ static inline struct tty_struct *tty_kref_get(struct tty_struct *tty)
return tty;
}
-extern int tty_paranoia_check(struct tty_struct *tty, struct inode *inode,
- const char *routine);
extern const char *tty_name(const struct tty_struct *tty);
+extern const char *tty_driver_name(const struct tty_struct *tty);
extern void tty_wait_until_sent(struct tty_struct *tty, long timeout);
extern int __tty_check_change(struct tty_struct *tty, int sig);
extern int tty_check_change(struct tty_struct *tty);
@@ -667,10 +662,16 @@ static inline void proc_tty_register_driver(struct tty_driver *d) {}
static inline void proc_tty_unregister_driver(struct tty_driver *d) {}
#endif
-#define tty_debug(tty, f, args...) \
- do { \
- printk(KERN_DEBUG "%s: %s: " f, __func__, \
- tty_name(tty), ##args); \
- } while (0)
+#define tty_msg(fn, tty, f, ...) \
+ fn("%s %s: " f, tty_driver_name(tty), tty_name(tty), ##__VA_ARGS__)
+
+#define tty_debug(tty, f, ...) tty_msg(pr_debug, tty, f, ##__VA_ARGS__)
+#define tty_info(tty, f, ...) tty_msg(pr_info, tty, f, ##__VA_ARGS__)
+#define tty_notice(tty, f, ...) tty_msg(pr_notice, tty, f, ##__VA_ARGS__)
+#define tty_warn(tty, f, ...) tty_msg(pr_warn, tty, f, ##__VA_ARGS__)
+#define tty_err(tty, f, ...) tty_msg(pr_err, tty, f, ##__VA_ARGS__)
+
+#define tty_info_ratelimited(tty, f, ...) \
+ tty_msg(pr_info_ratelimited, tty, f, ##__VA_ARGS__)
#endif
diff --git a/include/linux/types.h b/include/linux/types.h
index 70d8500bddf1..70dd3dfde631 100644
--- a/include/linux/types.h
+++ b/include/linux/types.h
@@ -35,7 +35,7 @@ typedef __kernel_gid16_t gid16_t;
typedef unsigned long uintptr_t;
-#ifdef CONFIG_UID16
+#ifdef CONFIG_HAVE_UID16
/* This is defined by include/asm-{arch}/posix_types.h */
typedef __kernel_old_uid_t old_uid_t;
typedef __kernel_old_gid_t old_gid_t;
diff --git a/include/linux/uinput.h b/include/linux/uinput.h
index 0994c0d01a09..75de43da2301 100644
--- a/include/linux/uinput.h
+++ b/include/linux/uinput.h
@@ -20,6 +20,11 @@
* Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
*
* Changes/Revisions:
+ * 0.5 08/13/2015 (David Herrmann <dh.herrmann@gmail.com> &
+ * Benjamin Tissoires <benjamin.tissoires@redhat.com>)
+ * - add UI_DEV_SETUP ioctl
+ * - add UI_ABS_SETUP ioctl
+ * - add UI_GET_VERSION ioctl
* 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
* - add UI_GET_SYSNAME ioctl
* 0.3 24/05/2006 (Anssi Hannula <anssi.hannulagmail.com>)
diff --git a/include/linux/uio.h b/include/linux/uio.h
index 8b01e1c3c614..fd9bcfedad42 100644
--- a/include/linux/uio.h
+++ b/include/linux/uio.h
@@ -82,7 +82,7 @@ size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
struct iov_iter *i);
size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes,
struct iov_iter *i);
-size_t copy_to_iter(void *addr, size_t bytes, struct iov_iter *i);
+size_t copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i);
size_t copy_from_iter(void *addr, size_t bytes, struct iov_iter *i);
size_t copy_from_iter_nocache(void *addr, size_t bytes, struct iov_iter *i);
size_t iov_iter_zero(size_t bytes, struct iov_iter *);
@@ -145,7 +145,7 @@ static inline void iov_iter_reexpand(struct iov_iter *i, size_t count)
{
i->count = count;
}
-size_t csum_and_copy_to_iter(void *addr, size_t bytes, __wsum *csum, struct iov_iter *i);
+size_t csum_and_copy_to_iter(const void *addr, size_t bytes, __wsum *csum, struct iov_iter *i);
size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum, struct iov_iter *i);
int import_iovec(int type, const struct iovec __user * uvector,
diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index 0bdc72f36905..4a29c75b146e 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -21,7 +21,7 @@
* Authors:
* Srikar Dronamraju
* Jim Keniston
- * Copyright (C) 2011-2012 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2011-2012 Red Hat, Inc., Peter Zijlstra
*/
#include <linux/errno.h>
diff --git a/include/linux/usb.h b/include/linux/usb.h
index b9a28074210f..89533ba38691 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -510,7 +510,8 @@ struct usb3_lpm_parameters {
* @usb2_hw_lpm_besl_capable: device can perform USB2 hardware BESL LPM
* @usb2_hw_lpm_enabled: USB2 hardware LPM is enabled
* @usb2_hw_lpm_allowed: Userspace allows USB 2.0 LPM to be enabled
- * @usb3_lpm_enabled: USB3 hardware LPM enabled
+ * @usb3_lpm_u1_enabled: USB3 hardware U1 LPM enabled
+ * @usb3_lpm_u2_enabled: USB3 hardware U2 LPM enabled
* @string_langid: language ID for strings
* @product: iProduct string, if present (static)
* @manufacturer: iManufacturer string, if present (static)
@@ -583,7 +584,8 @@ struct usb_device {
unsigned usb2_hw_lpm_besl_capable:1;
unsigned usb2_hw_lpm_enabled:1;
unsigned usb2_hw_lpm_allowed:1;
- unsigned usb3_lpm_enabled:1;
+ unsigned usb3_lpm_u1_enabled:1;
+ unsigned usb3_lpm_u2_enabled:1;
int string_langid;
/* static strings from the device */
diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h
index 1f6526c76ee8..3a375d07d0dc 100644
--- a/include/linux/usb/cdc_ncm.h
+++ b/include/linux/usb/cdc_ncm.h
@@ -138,6 +138,7 @@ struct cdc_ncm_ctx {
};
u8 cdc_ncm_select_altsetting(struct usb_interface *intf);
+int cdc_ncm_change_mtu(struct net_device *net, int new_mtu);
int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting, int drvflags);
void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf);
struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign);
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 3d583a10b926..d82d0068872b 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -402,6 +402,9 @@ static inline void usb_ep_free_request(struct usb_ep *ep,
static inline int usb_ep_queue(struct usb_ep *ep,
struct usb_request *req, gfp_t gfp_flags)
{
+ if (WARN_ON_ONCE(!ep->enabled && ep->address))
+ return -ESHUTDOWN;
+
return ep->ops->queue(ep, req, gfp_flags);
}
@@ -1012,6 +1015,9 @@ static inline int usb_gadget_activate(struct usb_gadget *gadget)
* @reset: Invoked on USB bus reset. It is mandatory for all gadget drivers
* and should be called in_interrupt.
* @driver: Driver model state for this driver.
+ * @udc_name: A name of UDC this driver should be bound to. If udc_name is NULL,
+ * this driver will be bound to any available UDC.
+ * @pending: UDC core private data used for deferred probe of this driver.
*
* Devices are disabled till a gadget driver successfully bind()s, which
* means the driver will handle setup() requests needed to enumerate (and
@@ -1072,6 +1078,9 @@ struct usb_gadget_driver {
/* FIXME support safe rmmod */
struct device_driver driver;
+
+ char *udc_name;
+ struct list_head pending;
};
@@ -1117,8 +1126,6 @@ extern int usb_add_gadget_udc_release(struct device *parent,
struct usb_gadget *gadget, void (*release)(struct device *dev));
extern int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget);
extern void usb_del_gadget_udc(struct usb_gadget *gadget);
-extern int usb_udc_attach_driver(const char *name,
- struct usb_gadget_driver *driver);
/*-------------------------------------------------------------------------*/
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index f89c24bd53a4..4dcf8446dbcd 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -660,7 +660,7 @@ struct usb_mon_operations {
/* void (*urb_unlink)(struct usb_bus *bus, struct urb *urb); */
};
-extern struct usb_mon_operations *mon_ops;
+extern const struct usb_mon_operations *mon_ops;
static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb)
{
@@ -682,7 +682,7 @@ static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb,
(*mon_ops->urb_complete)(bus, urb, status);
}
-int usb_mon_register(struct usb_mon_operations *ops);
+int usb_mon_register(const struct usb_mon_operations *ops);
void usb_mon_deregister(void);
#else
diff --git a/include/linux/usb/musb-omap.h b/include/linux/usb/musb-omap.h
deleted file mode 100644
index 7774c5986f07..000000000000
--- a/include/linux/usb/musb-omap.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2011-2012 by Texas Instruments
- *
- * The Inventra Controller Driver for Linux 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 __MUSB_OMAP_H__
-#define __MUSB_OMAP_H__
-
-enum omap_musb_vbus_id_status {
- OMAP_MUSB_UNKNOWN = 0,
- OMAP_MUSB_ID_GROUND,
- OMAP_MUSB_ID_FLOAT,
- OMAP_MUSB_VBUS_VALID,
- OMAP_MUSB_VBUS_OFF,
-};
-
-#if (defined(CONFIG_USB_MUSB_OMAP2PLUS) || \
- defined(CONFIG_USB_MUSB_OMAP2PLUS_MODULE))
-void omap_musb_mailbox(enum omap_musb_vbus_id_status status);
-#else
-static inline void omap_musb_mailbox(enum omap_musb_vbus_id_status status)
-{
-}
-#endif
-
-#endif /* __MUSB_OMAP_H__ */
diff --git a/include/linux/usb/musb.h b/include/linux/usb/musb.h
index fa6dc132bd1b..96ddfb7ab018 100644
--- a/include/linux/usb/musb.h
+++ b/include/linux/usb/musb.h
@@ -133,6 +133,21 @@ struct musb_hdrc_platform_data {
const void *platform_ops;
};
+enum musb_vbus_id_status {
+ MUSB_UNKNOWN = 0,
+ MUSB_ID_GROUND,
+ MUSB_ID_FLOAT,
+ MUSB_VBUS_VALID,
+ MUSB_VBUS_OFF,
+};
+
+#if IS_ENABLED(CONFIG_USB_MUSB_HDRC)
+void musb_mailbox(enum musb_vbus_id_status status);
+#else
+static inline void musb_mailbox(enum musb_vbus_id_status status)
+{
+}
+#endif
/* TUSB 6010 support */
diff --git a/include/linux/usb/of.h b/include/linux/usb/of.h
index c3fe9e48ce27..974bce93aa28 100644
--- a/include/linux/usb/of.h
+++ b/include/linux/usb/of.h
@@ -12,10 +12,16 @@
#include <linux/usb/phy.h>
#if IS_ENABLED(CONFIG_OF)
+enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *phy_np);
bool of_usb_host_tpl_support(struct device_node *np);
int of_usb_update_otg_caps(struct device_node *np,
struct usb_otg_caps *otg_caps);
#else
+static inline enum usb_dr_mode
+of_usb_get_dr_mode_by_phy(struct device_node *phy_np)
+{
+ return USB_DR_MODE_UNKNOWN;
+}
static inline bool of_usb_host_tpl_support(struct device_node *np)
{
return false;
diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h
index 9948c874e3f1..1d0043dc34e4 100644
--- a/include/linux/usb/quirks.h
+++ b/include/linux/usb/quirks.h
@@ -47,4 +47,7 @@
/* device generates spurious wakeup, ignore remote wakeup capability */
#define USB_QUIRK_IGNORE_REMOTE_WAKEUP BIT(9)
+/* device can't handle Link Power Management */
+#define USB_QUIRK_NO_LPM BIT(10)
+
#endif /* __LINUX_USB_QUIRKS_H */
diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h
index bfb74723f151..4db191fe8c2c 100644
--- a/include/linux/usb/renesas_usbhs.h
+++ b/include/linux/usb/renesas_usbhs.h
@@ -105,12 +105,26 @@ struct renesas_usbhs_platform_callback {
* some register needs USB chip specific parameters.
* This struct show it to driver
*/
+
+struct renesas_usbhs_driver_pipe_config {
+ u8 type; /* USB_ENDPOINT_XFER_xxx */
+ u16 bufsize;
+ u8 bufnum;
+ bool double_buf;
+};
+#define RENESAS_USBHS_PIPE(_type, _size, _num, _double_buf) { \
+ .type = (_type), \
+ .bufsize = (_size), \
+ .bufnum = (_num), \
+ .double_buf = (_double_buf), \
+ }
+
struct renesas_usbhs_driver_param {
/*
* pipe settings
*/
- u32 *pipe_type; /* array of USB_ENDPOINT_XFER_xxx (from ep0) */
- int pipe_size; /* pipe_type array size */
+ struct renesas_usbhs_driver_pipe_config *pipe_configs;
+ int pipe_size; /* pipe_configs array size */
/*
* option:
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 73ea2fb04731..16c0ed6c50a7 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -46,7 +46,7 @@
* All kernel-specific stuff were moved to media/v4l2-dev.h, so
* no #if __KERNEL tests are allowed here
*
- * See http://linuxtv.org for more info
+ * See https://linuxtv.org for more info
*
* Author: Bill Dirks <bill@thedirks.org>
* Justin Schoeman
diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h
index 3bff87a25a42..d1f1d338af20 100644
--- a/include/linux/vmalloc.h
+++ b/include/linux/vmalloc.h
@@ -14,7 +14,6 @@ struct vm_area_struct; /* vma defining user mapping in mm_types.h */
#define VM_ALLOC 0x00000002 /* vmalloc() */
#define VM_MAP 0x00000004 /* vmap()ed pages */
#define VM_USERMAP 0x00000008 /* suitable for remap_vmalloc_range */
-#define VM_VPAGES 0x00000010 /* buffer for pages was vmalloc'ed */
#define VM_UNINITIALIZED 0x00000020 /* vm_struct is not fully initialized */
#define VM_NO_GUARD 0x00000040 /* don't add guard page */
#define VM_KASAN 0x00000080 /* has allocated kasan shadow memory */
diff --git a/include/linux/vmpressure.h b/include/linux/vmpressure.h
index 3e4535876d37..3347cc3ec0ab 100644
--- a/include/linux/vmpressure.h
+++ b/include/linux/vmpressure.h
@@ -12,6 +12,9 @@
struct vmpressure {
unsigned long scanned;
unsigned long reclaimed;
+
+ unsigned long tree_scanned;
+ unsigned long tree_reclaimed;
/* The lock is used to keep the scanned/reclaimed above in sync. */
struct spinlock sr_lock;
@@ -26,7 +29,7 @@ struct vmpressure {
struct mem_cgroup;
#ifdef CONFIG_MEMCG
-extern void vmpressure(gfp_t gfp, struct mem_cgroup *memcg,
+extern void vmpressure(gfp_t gfp, struct mem_cgroup *memcg, bool tree,
unsigned long scanned, unsigned long reclaimed);
extern void vmpressure_prio(gfp_t gfp, struct mem_cgroup *memcg, int prio);
@@ -40,7 +43,7 @@ extern int vmpressure_register_event(struct mem_cgroup *memcg,
extern void vmpressure_unregister_event(struct mem_cgroup *memcg,
struct eventfd_ctx *eventfd);
#else
-static inline void vmpressure(gfp_t gfp, struct mem_cgroup *memcg,
+static inline void vmpressure(gfp_t gfp, struct mem_cgroup *memcg, bool tree,
unsigned long scanned, unsigned long reclaimed) {}
static inline void vmpressure_prio(gfp_t gfp, struct mem_cgroup *memcg,
int prio) {}
diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h
index 5dbc8b0ee567..73fae8c4a5fb 100644
--- a/include/linux/vmstat.h
+++ b/include/linux/vmstat.h
@@ -176,11 +176,11 @@ extern void zone_statistics(struct zone *, struct zone *, gfp_t gfp);
#define sub_zone_page_state(__z, __i, __d) mod_zone_page_state(__z, __i, -(__d))
#ifdef CONFIG_SMP
-void __mod_zone_page_state(struct zone *, enum zone_stat_item item, int);
+void __mod_zone_page_state(struct zone *, enum zone_stat_item item, long);
void __inc_zone_page_state(struct page *, enum zone_stat_item);
void __dec_zone_page_state(struct page *, enum zone_stat_item);
-void mod_zone_page_state(struct zone *, enum zone_stat_item, int);
+void mod_zone_page_state(struct zone *, enum zone_stat_item, long);
void inc_zone_page_state(struct page *, enum zone_stat_item);
void dec_zone_page_state(struct page *, enum zone_stat_item);
@@ -189,6 +189,7 @@ extern void __inc_zone_state(struct zone *, enum zone_stat_item);
extern void dec_zone_state(struct zone *, enum zone_stat_item);
extern void __dec_zone_state(struct zone *, enum zone_stat_item);
+void quiet_vmstat(void);
void cpu_vm_stats_fold(int cpu);
void refresh_zone_stat_thresholds(void);
@@ -205,7 +206,7 @@ void set_pgdat_percpu_threshold(pg_data_t *pgdat,
* The functions directly modify the zone and global counters.
*/
static inline void __mod_zone_page_state(struct zone *zone,
- enum zone_stat_item item, int delta)
+ enum zone_stat_item item, long delta)
{
zone_page_state_add(delta, zone, item);
}
@@ -249,6 +250,7 @@ static inline void __dec_zone_page_state(struct page *page,
static inline void refresh_zone_stat_thresholds(void) { }
static inline void cpu_vm_stats_fold(int cpu) { }
+static inline void quiet_vmstat(void) { }
static inline void drain_zonestat(struct zone *zone,
struct per_cpu_pageset *pset) { }
diff --git a/include/linux/vtime.h b/include/linux/vtime.h
index c5165fd256f9..fa2196990f84 100644
--- a/include/linux/vtime.h
+++ b/include/linux/vtime.h
@@ -10,16 +10,27 @@
struct task_struct;
/*
- * vtime_accounting_enabled() definitions/declarations
+ * vtime_accounting_cpu_enabled() definitions/declarations
*/
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
-static inline bool vtime_accounting_enabled(void) { return true; }
+static inline bool vtime_accounting_cpu_enabled(void) { return true; }
#endif /* CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
+/*
+ * Checks if vtime is enabled on some CPU. Cputime readers want to be careful
+ * in that case and compute the tickless cputime.
+ * For now vtime state is tied to context tracking. We might want to decouple
+ * those later if necessary.
+ */
static inline bool vtime_accounting_enabled(void)
{
- if (context_tracking_is_enabled()) {
+ return context_tracking_is_enabled();
+}
+
+static inline bool vtime_accounting_cpu_enabled(void)
+{
+ if (vtime_accounting_enabled()) {
if (context_tracking_cpu_is_enabled())
return true;
}
@@ -29,7 +40,7 @@ static inline bool vtime_accounting_enabled(void)
#endif /* CONFIG_VIRT_CPU_ACCOUNTING_GEN */
#ifndef CONFIG_VIRT_CPU_ACCOUNTING
-static inline bool vtime_accounting_enabled(void) { return false; }
+static inline bool vtime_accounting_cpu_enabled(void) { return false; }
#endif /* !CONFIG_VIRT_CPU_ACCOUNTING */
@@ -44,7 +55,7 @@ extern void vtime_task_switch(struct task_struct *prev);
extern void vtime_common_task_switch(struct task_struct *prev);
static inline void vtime_task_switch(struct task_struct *prev)
{
- if (vtime_accounting_enabled())
+ if (vtime_accounting_cpu_enabled())
vtime_common_task_switch(prev);
}
#endif /* __ARCH_HAS_VTIME_TASK_SWITCH */
@@ -59,7 +70,7 @@ extern void vtime_account_irq_enter(struct task_struct *tsk);
extern void vtime_common_account_irq_enter(struct task_struct *tsk);
static inline void vtime_account_irq_enter(struct task_struct *tsk)
{
- if (vtime_accounting_enabled())
+ if (vtime_accounting_cpu_enabled())
vtime_common_account_irq_enter(tsk);
}
#endif /* __ARCH_HAS_VTIME_ACCOUNT */
@@ -78,7 +89,7 @@ extern void vtime_gen_account_irq_exit(struct task_struct *tsk);
static inline void vtime_account_irq_exit(struct task_struct *tsk)
{
- if (vtime_accounting_enabled())
+ if (vtime_accounting_cpu_enabled())
vtime_gen_account_irq_exit(tsk);
}
diff --git a/include/linux/wait.h b/include/linux/wait.h
index 1e1bf9f963a9..ae71a769b89e 100644
--- a/include/linux/wait.h
+++ b/include/linux/wait.h
@@ -102,11 +102,62 @@ init_waitqueue_func_entry(wait_queue_t *q, wait_queue_func_t func)
q->func = func;
}
+/**
+ * waitqueue_active -- locklessly test for waiters on the queue
+ * @q: the waitqueue to test for waiters
+ *
+ * returns true if the wait list is not empty
+ *
+ * NOTE: this function is lockless and requires care, incorrect usage _will_
+ * lead to sporadic and non-obvious failure.
+ *
+ * Use either while holding wait_queue_head_t::lock or when used for wakeups
+ * with an extra smp_mb() like:
+ *
+ * CPU0 - waker CPU1 - waiter
+ *
+ * for (;;) {
+ * @cond = true; prepare_to_wait(&wq, &wait, state);
+ * smp_mb(); // smp_mb() from set_current_state()
+ * if (waitqueue_active(wq)) if (@cond)
+ * wake_up(wq); break;
+ * schedule();
+ * }
+ * finish_wait(&wq, &wait);
+ *
+ * Because without the explicit smp_mb() it's possible for the
+ * waitqueue_active() load to get hoisted over the @cond store such that we'll
+ * observe an empty wait list while the waiter might not observe @cond.
+ *
+ * Also note that this 'optimization' trades a spin_lock() for an smp_mb(),
+ * which (when the lock is uncontended) are of roughly equal cost.
+ */
static inline int waitqueue_active(wait_queue_head_t *q)
{
return !list_empty(&q->task_list);
}
+/**
+ * wq_has_sleeper - check if there are any waiting processes
+ * @wq: wait queue head
+ *
+ * Returns true if wq has waiting processes
+ *
+ * Please refer to the comment for waitqueue_active.
+ */
+static inline bool wq_has_sleeper(wait_queue_head_t *wq)
+{
+ /*
+ * We need to be sure we are in sync with the
+ * add_wait_queue modifications to the wait queue.
+ *
+ * This memory barrier should be paired with one on the
+ * waiting side.
+ */
+ smp_mb();
+ return waitqueue_active(wq);
+}
+
extern void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
extern void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait);
extern void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
@@ -145,7 +196,7 @@ __remove_wait_queue(wait_queue_head_t *head, wait_queue_t *old)
list_del(&old->task_list);
}
-typedef int wait_bit_action_f(struct wait_bit_key *);
+typedef int wait_bit_action_f(struct wait_bit_key *, int mode);
void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr, void *key);
void __wake_up_locked_key(wait_queue_head_t *q, unsigned int mode, void *key);
void __wake_up_sync_key(wait_queue_head_t *q, unsigned int mode, int nr, void *key);
@@ -960,10 +1011,10 @@ int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *key);
} while (0)
-extern int bit_wait(struct wait_bit_key *);
-extern int bit_wait_io(struct wait_bit_key *);
-extern int bit_wait_timeout(struct wait_bit_key *);
-extern int bit_wait_io_timeout(struct wait_bit_key *);
+extern int bit_wait(struct wait_bit_key *, int);
+extern int bit_wait_io(struct wait_bit_key *, int);
+extern int bit_wait_timeout(struct wait_bit_key *, int);
+extern int bit_wait_io_timeout(struct wait_bit_key *, int);
/**
* wait_on_bit - wait for a bit to be cleared
diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h
index 0197358f1e81..0e32bc71245e 100644
--- a/include/linux/workqueue.h
+++ b/include/linux/workqueue.h
@@ -618,4 +618,10 @@ static inline int workqueue_sysfs_register(struct workqueue_struct *wq)
{ return 0; }
#endif /* CONFIG_SYSFS */
+#ifdef CONFIG_WQ_WATCHDOG
+void wq_watchdog_touch(int cpu);
+#else /* CONFIG_WQ_WATCHDOG */
+static inline void wq_watchdog_touch(int cpu) { }
+#endif /* CONFIG_WQ_WATCHDOG */
+
#endif
diff --git a/include/linux/xattr.h b/include/linux/xattr.h
index 89474b9d260c..4457541de3c9 100644
--- a/include/linux/xattr.h
+++ b/include/linux/xattr.h
@@ -19,12 +19,16 @@
struct inode;
struct dentry;
+/*
+ * struct xattr_handler: When @name is set, match attributes with exactly that
+ * name. When @prefix is set instead, match attributes with that prefix and
+ * with a non-empty suffix.
+ */
struct xattr_handler {
+ const char *name;
const char *prefix;
int flags; /* fs private flags */
- size_t (*list)(const struct xattr_handler *, struct dentry *dentry,
- char *list, size_t list_size, const char *name,
- size_t name_len);
+ bool (*list)(struct dentry *dentry);
int (*get)(const struct xattr_handler *, struct dentry *dentry,
const char *name, void *buffer, size_t size);
int (*set)(const struct xattr_handler *, struct dentry *dentry,
@@ -53,8 +57,11 @@ int generic_setxattr(struct dentry *dentry, const char *name, const void *value,
int generic_removexattr(struct dentry *dentry, const char *name);
ssize_t vfs_getxattr_alloc(struct dentry *dentry, const char *name,
char **xattr_value, size_t size, gfp_t flags);
-int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name,
- const char *value, size_t size, gfp_t flags);
+
+static inline const char *xattr_prefix(const struct xattr_handler *handler)
+{
+ return handler->prefix ?: handler->name;
+}
struct simple_xattrs {
struct list_head head;
@@ -95,8 +102,7 @@ int simple_xattr_get(struct simple_xattrs *xattrs, const char *name,
void *buffer, size_t size);
int simple_xattr_set(struct simple_xattrs *xattrs, const char *name,
const void *value, size_t size, int flags);
-int simple_xattr_remove(struct simple_xattrs *xattrs, const char *name);
-ssize_t simple_xattr_list(struct simple_xattrs *xattrs, char *buffer,
+ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs *xattrs, char *buffer,
size_t size);
void simple_xattr_list_add(struct simple_xattrs *xattrs,
struct simple_xattr *new_xattr);
diff --git a/include/media/cx2341x.h b/include/media/drv-intf/cx2341x.h
index 9635eebaab09..9635eebaab09 100644
--- a/include/media/cx2341x.h
+++ b/include/media/drv-intf/cx2341x.h
diff --git a/include/media/cx25840.h b/include/media/drv-intf/cx25840.h
index 783c5bdd63eb..783c5bdd63eb 100644
--- a/include/media/cx25840.h
+++ b/include/media/drv-intf/cx25840.h
diff --git a/include/media/exynos-fimc.h b/include/media/drv-intf/exynos-fimc.h
index 69bcd2a07d5c..69bcd2a07d5c 100644
--- a/include/media/exynos-fimc.h
+++ b/include/media/drv-intf/exynos-fimc.h
diff --git a/include/media/msp3400.h b/include/media/drv-intf/msp3400.h
index 90cf22ada8b4..1e6e80213a77 100644
--- a/include/media/msp3400.h
+++ b/include/media/drv-intf/msp3400.h
@@ -223,4 +223,3 @@
*/
#endif /* MSP3400_H */
-
diff --git a/include/media/s3c_camif.h b/include/media/drv-intf/s3c_camif.h
index df96c2c789b4..df96c2c789b4 100644
--- a/include/media/s3c_camif.h
+++ b/include/media/drv-intf/s3c_camif.h
diff --git a/include/media/saa7146.h b/include/media/drv-intf/saa7146.h
index 96058a5a4acc..96058a5a4acc 100644
--- a/include/media/saa7146.h
+++ b/include/media/drv-intf/saa7146.h
diff --git a/include/media/saa7146_vv.h b/include/media/drv-intf/saa7146_vv.h
index 92766f77a5de..0da6ccc0615b 100644
--- a/include/media/saa7146_vv.h
+++ b/include/media/drv-intf/saa7146_vv.h
@@ -4,7 +4,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-fh.h>
-#include <media/saa7146.h>
+#include <media/drv-intf/saa7146.h>
#include <media/videobuf-dma-sg.h>
#define MAX_SAA7146_CAPTURE_BUFFERS 32 /* arbitrary */
diff --git a/include/media/sh_mobile_ceu.h b/include/media/drv-intf/sh_mobile_ceu.h
index 7f57056c22ba..7f57056c22ba 100644
--- a/include/media/sh_mobile_ceu.h
+++ b/include/media/drv-intf/sh_mobile_ceu.h
diff --git a/include/media/sh_mobile_csi2.h b/include/media/drv-intf/sh_mobile_csi2.h
index 14030db51f13..14030db51f13 100644
--- a/include/media/sh_mobile_csi2.h
+++ b/include/media/drv-intf/sh_mobile_csi2.h
diff --git a/include/media/sh_vou.h b/include/media/drv-intf/sh_vou.h
index ec3ba9a597a2..ec3ba9a597a2 100644
--- a/include/media/sh_vou.h
+++ b/include/media/drv-intf/sh_vou.h
diff --git a/include/media/si476x.h b/include/media/drv-intf/si476x.h
index e02e241e2d22..ad87fa8483b2 100644
--- a/include/media/si476x.h
+++ b/include/media/drv-intf/si476x.h
@@ -1,5 +1,5 @@
/*
- * include/media/si476x.h -- Common definitions for si476x driver
+ * include/media/drv-intf/si476x.h -- Common definitions for si476x driver
*
* Copyright (C) 2012 Innovative Converged Devices(ICD)
* Copyright (C) 2013 Andrey Smirnov
diff --git a/include/media/soc_mediabus.h b/include/media/drv-intf/soc_mediabus.h
index 2ff773785fb6..2ff773785fb6 100644
--- a/include/media/soc_mediabus.h
+++ b/include/media/drv-intf/soc_mediabus.h
diff --git a/include/media/tea575x.h b/include/media/drv-intf/tea575x.h
index 5d096578b736..fb272d48ba33 100644
--- a/include/media/tea575x.h
+++ b/include/media/drv-intf/tea575x.h
@@ -63,7 +63,7 @@ struct snd_tea575x {
u32 band; /* 0: FM, 1: FM-Japan, 2: AM */
u32 freq; /* frequency */
struct mutex mutex;
- struct snd_tea575x_ops *ops;
+ const struct snd_tea575x_ops *ops;
void *private_data;
u8 card[32];
u8 bus_info[32];
diff --git a/include/media/ad9389b.h b/include/media/i2c/ad9389b.h
index 5ba9af869b8b..5ba9af869b8b 100644
--- a/include/media/ad9389b.h
+++ b/include/media/i2c/ad9389b.h
diff --git a/include/media/adp1653.h b/include/media/i2c/adp1653.h
index 9779c8549eb4..0b6709335dff 100644
--- a/include/media/adp1653.h
+++ b/include/media/i2c/adp1653.h
@@ -1,5 +1,5 @@
/*
- * include/media/adp1653.h
+ * include/media/i2c/adp1653.h
*
* Copyright (C) 2008--2011 Nokia Corporation
*
diff --git a/include/media/adv7183.h b/include/media/i2c/adv7183.h
index c5c2d377c0a6..c5c2d377c0a6 100644
--- a/include/media/adv7183.h
+++ b/include/media/i2c/adv7183.h
diff --git a/include/media/adv7343.h b/include/media/i2c/adv7343.h
index e4142b1ef8cd..e4142b1ef8cd 100644
--- a/include/media/adv7343.h
+++ b/include/media/i2c/adv7343.h
diff --git a/include/media/adv7393.h b/include/media/i2c/adv7393.h
index b28edf351842..b28edf351842 100644
--- a/include/media/adv7393.h
+++ b/include/media/i2c/adv7393.h
diff --git a/include/media/adv7511.h b/include/media/i2c/adv7511.h
index d83b91d80764..d83b91d80764 100644
--- a/include/media/adv7511.h
+++ b/include/media/i2c/adv7511.h
diff --git a/include/media/adv7604.h b/include/media/i2c/adv7604.h
index a913859bfd30..a913859bfd30 100644
--- a/include/media/adv7604.h
+++ b/include/media/i2c/adv7604.h
diff --git a/include/media/adv7842.h b/include/media/i2c/adv7842.h
index bc249709bf35..bc249709bf35 100644
--- a/include/media/adv7842.h
+++ b/include/media/i2c/adv7842.h
diff --git a/include/media/ak881x.h b/include/media/i2c/ak881x.h
index b7f2add5ce7b..b7f2add5ce7b 100644
--- a/include/media/ak881x.h
+++ b/include/media/i2c/ak881x.h
diff --git a/include/media/as3645a.h b/include/media/i2c/as3645a.h
index 5075496d2c9e..0e07484ddc33 100644
--- a/include/media/as3645a.h
+++ b/include/media/i2c/as3645a.h
@@ -1,5 +1,5 @@
/*
- * include/media/as3645a.h
+ * include/media/i2c/as3645a.h
*
* Copyright (C) 2008-2011 Nokia Corporation
*
diff --git a/include/media/bt819.h b/include/media/i2c/bt819.h
index 8025f4bc2bb6..8025f4bc2bb6 100644
--- a/include/media/bt819.h
+++ b/include/media/i2c/bt819.h
diff --git a/include/media/cs5345.h b/include/media/i2c/cs5345.h
index 6ccae24e65ed..6ccae24e65ed 100644
--- a/include/media/cs5345.h
+++ b/include/media/i2c/cs5345.h
diff --git a/include/media/cs53l32a.h b/include/media/i2c/cs53l32a.h
index bf76197d3790..bf76197d3790 100644
--- a/include/media/cs53l32a.h
+++ b/include/media/i2c/cs53l32a.h
diff --git a/include/media/ir-kbd-i2c.h b/include/media/i2c/ir-kbd-i2c.h
index d8564354debb..d8564354debb 100644
--- a/include/media/ir-kbd-i2c.h
+++ b/include/media/i2c/ir-kbd-i2c.h
diff --git a/include/media/lm3560.h b/include/media/i2c/lm3560.h
index 46670706d6f8..5ed942a8ac32 100644
--- a/include/media/lm3560.h
+++ b/include/media/i2c/lm3560.h
@@ -1,5 +1,5 @@
/*
- * include/media/lm3560.h
+ * include/media/i2c/lm3560.h
*
* Copyright (C) 2013 Texas Instruments
*
diff --git a/include/media/lm3646.h b/include/media/i2c/lm3646.h
index c6acf5a1d640..724c10003a28 100644
--- a/include/media/lm3646.h
+++ b/include/media/i2c/lm3646.h
@@ -1,5 +1,5 @@
/*
- * include/media/lm3646.h
+ * include/media/i2c/lm3646.h
*
* Copyright (C) 2014 Texas Instruments
*
diff --git a/include/media/m52790.h b/include/media/i2c/m52790.h
index 7ddffae31a67..7ddffae31a67 100644
--- a/include/media/m52790.h
+++ b/include/media/i2c/m52790.h
diff --git a/include/media/m5mols.h b/include/media/i2c/m5mols.h
index 4a825ae5c6c8..4a825ae5c6c8 100644
--- a/include/media/m5mols.h
+++ b/include/media/i2c/m5mols.h
diff --git a/include/media/mt9m032.h b/include/media/i2c/mt9m032.h
index c3a78114d7a6..c3a78114d7a6 100644
--- a/include/media/mt9m032.h
+++ b/include/media/i2c/mt9m032.h
diff --git a/include/media/mt9p031.h b/include/media/i2c/mt9p031.h
index 1ba361205af1..1ba361205af1 100644
--- a/include/media/mt9p031.h
+++ b/include/media/i2c/mt9p031.h
diff --git a/include/media/mt9t001.h b/include/media/i2c/mt9t001.h
index 03fd63edd133..03fd63edd133 100644
--- a/include/media/mt9t001.h
+++ b/include/media/i2c/mt9t001.h
diff --git a/include/media/mt9t112.h b/include/media/i2c/mt9t112.h
index a43c74ab05ec..a43c74ab05ec 100644
--- a/include/media/mt9t112.h
+++ b/include/media/i2c/mt9t112.h
diff --git a/include/media/mt9v011.h b/include/media/i2c/mt9v011.h
index ea29fc74cd06..ea29fc74cd06 100644
--- a/include/media/mt9v011.h
+++ b/include/media/i2c/mt9v011.h
diff --git a/include/media/mt9v022.h b/include/media/i2c/mt9v022.h
index 40561801321a..40561801321a 100644
--- a/include/media/mt9v022.h
+++ b/include/media/i2c/mt9v022.h
diff --git a/include/media/mt9v032.h b/include/media/i2c/mt9v032.h
index 12175a63c5b2..12175a63c5b2 100644
--- a/include/media/mt9v032.h
+++ b/include/media/i2c/mt9v032.h
diff --git a/include/media/noon010pc30.h b/include/media/i2c/noon010pc30.h
index 58eafee36b30..58eafee36b30 100644
--- a/include/media/noon010pc30.h
+++ b/include/media/i2c/noon010pc30.h
diff --git a/include/media/ov2659.h b/include/media/i2c/ov2659.h
index 4216adc1ede2..4216adc1ede2 100644
--- a/include/media/ov2659.h
+++ b/include/media/i2c/ov2659.h
diff --git a/include/media/ov7670.h b/include/media/i2c/ov7670.h
index 1913d5123072..1913d5123072 100644
--- a/include/media/ov7670.h
+++ b/include/media/i2c/ov7670.h
diff --git a/include/media/ov772x.h b/include/media/i2c/ov772x.h
index 00dbb7c4feae..00dbb7c4feae 100644
--- a/include/media/ov772x.h
+++ b/include/media/i2c/ov772x.h
diff --git a/include/media/ov9650.h b/include/media/i2c/ov9650.h
index d630cf9e028d..d630cf9e028d 100644
--- a/include/media/ov9650.h
+++ b/include/media/i2c/ov9650.h
diff --git a/include/media/rj54n1cb0c.h b/include/media/i2c/rj54n1cb0c.h
index 8ae3288ae925..8ae3288ae925 100644
--- a/include/media/rj54n1cb0c.h
+++ b/include/media/i2c/rj54n1cb0c.h
diff --git a/include/media/s5c73m3.h b/include/media/i2c/s5c73m3.h
index ccb9e5448762..ccb9e5448762 100644
--- a/include/media/s5c73m3.h
+++ b/include/media/i2c/s5c73m3.h
diff --git a/include/media/s5k4ecgx.h b/include/media/i2c/s5k4ecgx.h
index 90c1be792ffe..90c1be792ffe 100644
--- a/include/media/s5k4ecgx.h
+++ b/include/media/i2c/s5k4ecgx.h
diff --git a/include/media/s5k6aa.h b/include/media/i2c/s5k6aa.h
index ba34f7055e55..ba34f7055e55 100644
--- a/include/media/s5k6aa.h
+++ b/include/media/i2c/s5k6aa.h
diff --git a/include/media/saa6588.h b/include/media/i2c/saa6588.h
index b5ec1aa60ed5..b5ec1aa60ed5 100644
--- a/include/media/saa6588.h
+++ b/include/media/i2c/saa6588.h
diff --git a/include/media/saa7115.h b/include/media/i2c/saa7115.h
index 76911e71de17..53954c90e7f6 100644
--- a/include/media/saa7115.h
+++ b/include/media/i2c/saa7115.h
@@ -138,4 +138,3 @@ struct saa7115_platform_data {
};
#endif
-
diff --git a/include/media/saa7127.h b/include/media/i2c/saa7127.h
index bbcf862141af..7005ba7daa9e 100644
--- a/include/media/saa7127.h
+++ b/include/media/i2c/saa7127.h
@@ -38,4 +38,3 @@ enum saa7127_output_type {
};
#endif
-
diff --git a/include/media/smiapp.h b/include/media/i2c/smiapp.h
index 268a3cdbf6cb..029142ddb95c 100644
--- a/include/media/smiapp.h
+++ b/include/media/i2c/smiapp.h
@@ -1,5 +1,5 @@
/*
- * include/media/smiapp.h
+ * include/media/i2c/smiapp.h
*
* Generic driver for SMIA/SMIA++ compliant camera modules
*
diff --git a/include/media/sr030pc30.h b/include/media/i2c/sr030pc30.h
index 6f901a653ba2..6f901a653ba2 100644
--- a/include/media/sr030pc30.h
+++ b/include/media/i2c/sr030pc30.h
diff --git a/include/media/tc358743.h b/include/media/i2c/tc358743.h
index 4513f2f9cfbc..4513f2f9cfbc 100644
--- a/include/media/tc358743.h
+++ b/include/media/i2c/tc358743.h
diff --git a/include/media/ths7303.h b/include/media/i2c/ths7303.h
index a7b49297da82..a7b49297da82 100644
--- a/include/media/ths7303.h
+++ b/include/media/i2c/ths7303.h
diff --git a/include/media/tvaudio.h b/include/media/i2c/tvaudio.h
index 1ac8184693f8..1ac8184693f8 100644
--- a/include/media/tvaudio.h
+++ b/include/media/i2c/tvaudio.h
diff --git a/include/media/tvp514x.h b/include/media/i2c/tvp514x.h
index 86ed7e806830..86ed7e806830 100644
--- a/include/media/tvp514x.h
+++ b/include/media/i2c/tvp514x.h
diff --git a/include/media/tvp5150.h b/include/media/i2c/tvp5150.h
index 72bd2a2b8bfd..649908a25605 100644
--- a/include/media/tvp5150.h
+++ b/include/media/i2c/tvp5150.h
@@ -31,4 +31,3 @@
#define TVP5150_BLACK_SCREEN 1
#endif
-
diff --git a/include/media/tvp7002.h b/include/media/i2c/tvp7002.h
index fadb6afe9ef0..fadb6afe9ef0 100644
--- a/include/media/tvp7002.h
+++ b/include/media/i2c/tvp7002.h
diff --git a/include/media/tw9910.h b/include/media/i2c/tw9910.h
index 90bcf1fa5421..90bcf1fa5421 100644
--- a/include/media/tw9910.h
+++ b/include/media/i2c/tw9910.h
diff --git a/include/media/uda1342.h b/include/media/i2c/uda1342.h
index cd156403a368..cd156403a368 100644
--- a/include/media/uda1342.h
+++ b/include/media/i2c/uda1342.h
diff --git a/include/media/upd64031a.h b/include/media/i2c/upd64031a.h
index 3ad6a32e1bce..3ad6a32e1bce 100644
--- a/include/media/upd64031a.h
+++ b/include/media/i2c/upd64031a.h
diff --git a/include/media/upd64083.h b/include/media/i2c/upd64083.h
index 59b6f32ba300..59b6f32ba300 100644
--- a/include/media/upd64083.h
+++ b/include/media/i2c/upd64083.h
diff --git a/include/media/wm8775.h b/include/media/i2c/wm8775.h
index d0e801a9935c..d0e801a9935c 100644
--- a/include/media/wm8775.h
+++ b/include/media/i2c/wm8775.h
diff --git a/include/media/lirc.h b/include/media/lirc.h
index 4b3ab2966b5a..554988c860c1 100644
--- a/include/media/lirc.h
+++ b/include/media/lirc.h
@@ -1,168 +1 @@
-/*
- * lirc.h - linux infrared remote control header file
- * last modified 2010/07/13 by Jarod Wilson
- */
-
-#ifndef _LINUX_LIRC_H
-#define _LINUX_LIRC_H
-
-#include <linux/types.h>
-#include <linux/ioctl.h>
-
-#define PULSE_BIT 0x01000000
-#define PULSE_MASK 0x00FFFFFF
-
-#define LIRC_MODE2_SPACE 0x00000000
-#define LIRC_MODE2_PULSE 0x01000000
-#define LIRC_MODE2_FREQUENCY 0x02000000
-#define LIRC_MODE2_TIMEOUT 0x03000000
-
-#define LIRC_VALUE_MASK 0x00FFFFFF
-#define LIRC_MODE2_MASK 0xFF000000
-
-#define LIRC_SPACE(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_SPACE)
-#define LIRC_PULSE(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_PULSE)
-#define LIRC_FREQUENCY(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_FREQUENCY)
-#define LIRC_TIMEOUT(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_TIMEOUT)
-
-#define LIRC_VALUE(val) ((val)&LIRC_VALUE_MASK)
-#define LIRC_MODE2(val) ((val)&LIRC_MODE2_MASK)
-
-#define LIRC_IS_SPACE(val) (LIRC_MODE2(val) == LIRC_MODE2_SPACE)
-#define LIRC_IS_PULSE(val) (LIRC_MODE2(val) == LIRC_MODE2_PULSE)
-#define LIRC_IS_FREQUENCY(val) (LIRC_MODE2(val) == LIRC_MODE2_FREQUENCY)
-#define LIRC_IS_TIMEOUT(val) (LIRC_MODE2(val) == LIRC_MODE2_TIMEOUT)
-
-/* used heavily by lirc userspace */
-#define lirc_t int
-
-/*** lirc compatible hardware features ***/
-
-#define LIRC_MODE2SEND(x) (x)
-#define LIRC_SEND2MODE(x) (x)
-#define LIRC_MODE2REC(x) ((x) << 16)
-#define LIRC_REC2MODE(x) ((x) >> 16)
-
-#define LIRC_MODE_RAW 0x00000001
-#define LIRC_MODE_PULSE 0x00000002
-#define LIRC_MODE_MODE2 0x00000004
-#define LIRC_MODE_LIRCCODE 0x00000010
-
-
-#define LIRC_CAN_SEND_RAW LIRC_MODE2SEND(LIRC_MODE_RAW)
-#define LIRC_CAN_SEND_PULSE LIRC_MODE2SEND(LIRC_MODE_PULSE)
-#define LIRC_CAN_SEND_MODE2 LIRC_MODE2SEND(LIRC_MODE_MODE2)
-#define LIRC_CAN_SEND_LIRCCODE LIRC_MODE2SEND(LIRC_MODE_LIRCCODE)
-
-#define LIRC_CAN_SEND_MASK 0x0000003f
-
-#define LIRC_CAN_SET_SEND_CARRIER 0x00000100
-#define LIRC_CAN_SET_SEND_DUTY_CYCLE 0x00000200
-#define LIRC_CAN_SET_TRANSMITTER_MASK 0x00000400
-
-#define LIRC_CAN_REC_RAW LIRC_MODE2REC(LIRC_MODE_RAW)
-#define LIRC_CAN_REC_PULSE LIRC_MODE2REC(LIRC_MODE_PULSE)
-#define LIRC_CAN_REC_MODE2 LIRC_MODE2REC(LIRC_MODE_MODE2)
-#define LIRC_CAN_REC_LIRCCODE LIRC_MODE2REC(LIRC_MODE_LIRCCODE)
-
-#define LIRC_CAN_REC_MASK LIRC_MODE2REC(LIRC_CAN_SEND_MASK)
-
-#define LIRC_CAN_SET_REC_CARRIER (LIRC_CAN_SET_SEND_CARRIER << 16)
-#define LIRC_CAN_SET_REC_DUTY_CYCLE (LIRC_CAN_SET_SEND_DUTY_CYCLE << 16)
-
-#define LIRC_CAN_SET_REC_DUTY_CYCLE_RANGE 0x40000000
-#define LIRC_CAN_SET_REC_CARRIER_RANGE 0x80000000
-#define LIRC_CAN_GET_REC_RESOLUTION 0x20000000
-#define LIRC_CAN_SET_REC_TIMEOUT 0x10000000
-#define LIRC_CAN_SET_REC_FILTER 0x08000000
-
-#define LIRC_CAN_MEASURE_CARRIER 0x02000000
-#define LIRC_CAN_USE_WIDEBAND_RECEIVER 0x04000000
-
-#define LIRC_CAN_SEND(x) ((x)&LIRC_CAN_SEND_MASK)
-#define LIRC_CAN_REC(x) ((x)&LIRC_CAN_REC_MASK)
-
-#define LIRC_CAN_NOTIFY_DECODE 0x01000000
-
-/*** IOCTL commands for lirc driver ***/
-
-#define LIRC_GET_FEATURES _IOR('i', 0x00000000, __u32)
-
-#define LIRC_GET_SEND_MODE _IOR('i', 0x00000001, __u32)
-#define LIRC_GET_REC_MODE _IOR('i', 0x00000002, __u32)
-#define LIRC_GET_SEND_CARRIER _IOR('i', 0x00000003, __u32)
-#define LIRC_GET_REC_CARRIER _IOR('i', 0x00000004, __u32)
-#define LIRC_GET_SEND_DUTY_CYCLE _IOR('i', 0x00000005, __u32)
-#define LIRC_GET_REC_DUTY_CYCLE _IOR('i', 0x00000006, __u32)
-#define LIRC_GET_REC_RESOLUTION _IOR('i', 0x00000007, __u32)
-
-#define LIRC_GET_MIN_TIMEOUT _IOR('i', 0x00000008, __u32)
-#define LIRC_GET_MAX_TIMEOUT _IOR('i', 0x00000009, __u32)
-
-#define LIRC_GET_MIN_FILTER_PULSE _IOR('i', 0x0000000a, __u32)
-#define LIRC_GET_MAX_FILTER_PULSE _IOR('i', 0x0000000b, __u32)
-#define LIRC_GET_MIN_FILTER_SPACE _IOR('i', 0x0000000c, __u32)
-#define LIRC_GET_MAX_FILTER_SPACE _IOR('i', 0x0000000d, __u32)
-
-/* code length in bits, currently only for LIRC_MODE_LIRCCODE */
-#define LIRC_GET_LENGTH _IOR('i', 0x0000000f, __u32)
-
-#define LIRC_SET_SEND_MODE _IOW('i', 0x00000011, __u32)
-#define LIRC_SET_REC_MODE _IOW('i', 0x00000012, __u32)
-/* Note: these can reset the according pulse_width */
-#define LIRC_SET_SEND_CARRIER _IOW('i', 0x00000013, __u32)
-#define LIRC_SET_REC_CARRIER _IOW('i', 0x00000014, __u32)
-#define LIRC_SET_SEND_DUTY_CYCLE _IOW('i', 0x00000015, __u32)
-#define LIRC_SET_REC_DUTY_CYCLE _IOW('i', 0x00000016, __u32)
-#define LIRC_SET_TRANSMITTER_MASK _IOW('i', 0x00000017, __u32)
-
-/*
- * when a timeout != 0 is set the driver will send a
- * LIRC_MODE2_TIMEOUT data packet, otherwise LIRC_MODE2_TIMEOUT is
- * never sent, timeout is disabled by default
- */
-#define LIRC_SET_REC_TIMEOUT _IOW('i', 0x00000018, __u32)
-
-/* 1 enables, 0 disables timeout reports in MODE2 */
-#define LIRC_SET_REC_TIMEOUT_REPORTS _IOW('i', 0x00000019, __u32)
-
-/*
- * pulses shorter than this are filtered out by hardware (software
- * emulation in lirc_dev?)
- */
-#define LIRC_SET_REC_FILTER_PULSE _IOW('i', 0x0000001a, __u32)
-/*
- * spaces shorter than this are filtered out by hardware (software
- * emulation in lirc_dev?)
- */
-#define LIRC_SET_REC_FILTER_SPACE _IOW('i', 0x0000001b, __u32)
-/*
- * if filter cannot be set independently for pulse/space, this should
- * be used
- */
-#define LIRC_SET_REC_FILTER _IOW('i', 0x0000001c, __u32)
-
-/*
- * if enabled from the next key press on the driver will send
- * LIRC_MODE2_FREQUENCY packets
- */
-#define LIRC_SET_MEASURE_CARRIER_MODE _IOW('i', 0x0000001d, __u32)
-
-/*
- * to set a range use
- * LIRC_SET_REC_DUTY_CYCLE_RANGE/LIRC_SET_REC_CARRIER_RANGE with the
- * lower bound first and later
- * LIRC_SET_REC_DUTY_CYCLE/LIRC_SET_REC_CARRIER with the upper bound
- */
-
-#define LIRC_SET_REC_DUTY_CYCLE_RANGE _IOW('i', 0x0000001e, __u32)
-#define LIRC_SET_REC_CARRIER_RANGE _IOW('i', 0x0000001f, __u32)
-
-#define LIRC_NOTIFY_DECODE _IO('i', 0x00000020)
-
-#define LIRC_SETUP_START _IO('i', 0x00000021)
-#define LIRC_SETUP_END _IO('i', 0x00000022)
-
-#define LIRC_SET_WIDEBAND_RECEIVER _IOW('i', 0x00000023, __u32)
-
-#endif
+#include <uapi/linux/lirc.h>
diff --git a/include/media/media-device.h b/include/media/media-device.h
index 6e6db78f1ee2..d3855898c3fc 100644
--- a/include/media/media-device.h
+++ b/include/media/media-device.h
@@ -30,6 +30,238 @@
#include <media/media-devnode.h>
#include <media/media-entity.h>
+/**
+ * DOC: Media Controller
+ *
+ * The media controller userspace API is documented in DocBook format in
+ * Documentation/DocBook/media/v4l/media-controller.xml. This document focus
+ * on the kernel-side implementation of the media framework.
+ *
+ * * Abstract media device model:
+ *
+ * Discovering a device internal topology, and configuring it at runtime, is one
+ * of the goals of the media framework. To achieve this, hardware devices are
+ * modelled as an oriented graph of building blocks called entities connected
+ * through pads.
+ *
+ * An entity is a basic media hardware building block. It can correspond to
+ * a large variety of logical blocks such as physical hardware devices
+ * (CMOS sensor for instance), logical hardware devices (a building block
+ * in a System-on-Chip image processing pipeline), DMA channels or physical
+ * connectors.
+ *
+ * A pad is a connection endpoint through which an entity can interact with
+ * other entities. Data (not restricted to video) produced by an entity
+ * flows from the entity's output to one or more entity inputs. Pads should
+ * not be confused with physical pins at chip boundaries.
+ *
+ * A link is a point-to-point oriented connection between two pads, either
+ * on the same entity or on different entities. Data flows from a source
+ * pad to a sink pad.
+ *
+ *
+ * * Media device:
+ *
+ * A media device is represented by a struct &media_device instance, defined in
+ * include/media/media-device.h. Allocation of the structure is handled by the
+ * media device driver, usually by embedding the &media_device instance in a
+ * larger driver-specific structure.
+ *
+ * Drivers register media device instances by calling
+ * __media_device_register() via the macro media_device_register()
+ * and unregistered by calling
+ * media_device_unregister().
+ *
+ * * Entities, pads and links:
+ *
+ * - Entities
+ *
+ * Entities are represented by a struct &media_entity instance, defined in
+ * include/media/media-entity.h. The structure is usually embedded into a
+ * higher-level structure, such as a v4l2_subdev or video_device instance,
+ * although drivers can allocate entities directly.
+ *
+ * Drivers initialize entity pads by calling
+ * media_entity_pads_init().
+ *
+ * Drivers register entities with a media device by calling
+ * media_device_register_entity()
+ * and unregistred by calling
+ * media_device_unregister_entity().
+ *
+ * - Interfaces
+ *
+ * Interfaces are represented by a struct &media_interface instance, defined in
+ * include/media/media-entity.h. Currently, only one type of interface is
+ * defined: a device node. Such interfaces are represented by a struct
+ * &media_intf_devnode.
+ *
+ * Drivers initialize and create device node interfaces by calling
+ * media_devnode_create()
+ * and remove them by calling:
+ * media_devnode_remove().
+ *
+ * - Pads
+ *
+ * Pads are represented by a struct &media_pad instance, defined in
+ * include/media/media-entity.h. Each entity stores its pads in a pads array
+ * managed by the entity driver. Drivers usually embed the array in a
+ * driver-specific structure.
+ *
+ * Pads are identified by their entity and their 0-based index in the pads
+ * array.
+ * Both information are stored in the &media_pad structure, making the
+ * &media_pad pointer the canonical way to store and pass link references.
+ *
+ * Pads have flags that describe the pad capabilities and state.
+ *
+ * %MEDIA_PAD_FL_SINK indicates that the pad supports sinking data.
+ * %MEDIA_PAD_FL_SOURCE indicates that the pad supports sourcing data.
+ *
+ * NOTE: One and only one of %MEDIA_PAD_FL_SINK and %MEDIA_PAD_FL_SOURCE must
+ * be set for each pad.
+ *
+ * - Links
+ *
+ * Links are represented by a struct &media_link instance, defined in
+ * include/media/media-entity.h. There are two types of links:
+ *
+ * 1. pad to pad links:
+ *
+ * Associate two entities via their PADs. Each entity has a list that points
+ * to all links originating at or targeting any of its pads.
+ * A given link is thus stored twice, once in the source entity and once in
+ * the target entity.
+ *
+ * Drivers create pad to pad links by calling:
+ * media_create_pad_link() and remove with media_entity_remove_links().
+ *
+ * 2. interface to entity links:
+ *
+ * Associate one interface to a Link.
+ *
+ * Drivers create interface to entity links by calling:
+ * media_create_intf_link() and remove with media_remove_intf_links().
+ *
+ * NOTE:
+ *
+ * Links can only be created after having both ends already created.
+ *
+ * Links have flags that describe the link capabilities and state. The
+ * valid values are described at media_create_pad_link() and
+ * media_create_intf_link().
+ *
+ * Graph traversal:
+ *
+ * The media framework provides APIs to iterate over entities in a graph.
+ *
+ * To iterate over all entities belonging to a media device, drivers can use
+ * the media_device_for_each_entity macro, defined in
+ * include/media/media-device.h.
+ *
+ * struct media_entity *entity;
+ *
+ * media_device_for_each_entity(entity, mdev) {
+ * // entity will point to each entity in turn
+ * ...
+ * }
+ *
+ * Drivers might also need to iterate over all entities in a graph that can be
+ * reached only through enabled links starting at a given entity. The media
+ * framework provides a depth-first graph traversal API for that purpose.
+ *
+ * Note that graphs with cycles (whether directed or undirected) are *NOT*
+ * supported by the graph traversal API. To prevent infinite loops, the graph
+ * traversal code limits the maximum depth to MEDIA_ENTITY_ENUM_MAX_DEPTH,
+ * currently defined as 16.
+ *
+ * Drivers initiate a graph traversal by calling
+ * media_entity_graph_walk_start()
+ *
+ * The graph structure, provided by the caller, is initialized to start graph
+ * traversal at the given entity.
+ *
+ * Drivers can then retrieve the next entity by calling
+ * media_entity_graph_walk_next()
+ *
+ * When the graph traversal is complete the function will return NULL.
+ *
+ * Graph traversal can be interrupted at any moment. No cleanup function call
+ * is required and the graph structure can be freed normally.
+ *
+ * Helper functions can be used to find a link between two given pads, or a pad
+ * connected to another pad through an enabled link
+ * media_entity_find_link() and media_entity_remote_pad()
+ *
+ * Use count and power handling:
+ *
+ * Due to the wide differences between drivers regarding power management
+ * needs, the media controller does not implement power management. However,
+ * the &media_entity structure includes a use_count field that media drivers
+ * can use to track the number of users of every entity for power management
+ * needs.
+ *
+ * The &media_entity.@use_count field is owned by media drivers and must not be
+ * touched by entity drivers. Access to the field must be protected by the
+ * &media_device.@graph_mutex lock.
+ *
+ * Links setup:
+ *
+ * Link properties can be modified at runtime by calling
+ * media_entity_setup_link()
+ *
+ * Pipelines and media streams:
+ *
+ * When starting streaming, drivers must notify all entities in the pipeline to
+ * prevent link states from being modified during streaming by calling
+ * media_entity_pipeline_start().
+ *
+ * The function will mark all entities connected to the given entity through
+ * enabled links, either directly or indirectly, as streaming.
+ *
+ * The &media_pipeline instance pointed to by the pipe argument will be stored
+ * in every entity in the pipeline. Drivers should embed the &media_pipeline
+ * structure in higher-level pipeline structures and can then access the
+ * pipeline through the &media_entity pipe field.
+ *
+ * Calls to media_entity_pipeline_start() can be nested. The pipeline pointer
+ * must be identical for all nested calls to the function.
+ *
+ * media_entity_pipeline_start() may return an error. In that case, it will
+ * clean up any of the changes it did by itself.
+ *
+ * When stopping the stream, drivers must notify the entities with
+ * media_entity_pipeline_stop().
+ *
+ * If multiple calls to media_entity_pipeline_start() have been made the same
+ * number of media_entity_pipeline_stop() calls are required to stop streaming.
+ * The &media_entity pipe field is reset to NULL on the last nested stop call.
+ *
+ * Link configuration will fail with -%EBUSY by default if either end of the
+ * link is a streaming entity. Links that can be modified while streaming must
+ * be marked with the %MEDIA_LNK_FL_DYNAMIC flag.
+ *
+ * If other operations need to be disallowed on streaming entities (such as
+ * changing entities configuration parameters) drivers can explicitly check the
+ * media_entity stream_count field to find out if an entity is streaming. This
+ * operation must be done with the media_device graph_mutex held.
+ *
+ * Link validation:
+ *
+ * Link validation is performed by media_entity_pipeline_start() for any
+ * entity which has sink pads in the pipeline. The
+ * &media_entity.@link_validate() callback is used for that purpose. In
+ * @link_validate() callback, entity driver should check that the properties of
+ * the source pad of the connected entity and its own sink pad match. It is up
+ * to the type of the entity (and in the end, the properties of the hardware)
+ * what matching actually means.
+ *
+ * Subsystems should facilitate link validation by providing subsystem specific
+ * helper functions to provide easy access for commonly needed information, and
+ * in the end provide a way to use driver-specific callbacks.
+ */
+
+struct ida;
struct device;
/**
@@ -41,8 +273,16 @@ struct device;
* @bus_info: Unique and stable device location identifier
* @hw_revision: Hardware device revision
* @driver_version: Device driver version
- * @entity_id: ID of the next entity to be registered
+ * @topology_version: Monotonic counter for storing the version of the graph
+ * topology. Should be incremented each time the topology changes.
+ * @id: Unique ID used on the last registered graph object
+ * @entity_internal_idx: Unique internal entity ID used by the graph traversal
+ * algorithms
+ * @entity_internal_idx_max: Allocated internal entity indices
* @entities: List of registered entities
+ * @interfaces: List of registered interfaces
+ * @pads: List of registered pads
+ * @links: List of registered links
* @lock: Entities list lock
* @graph_mutex: Entities graph operation lock
* @link_notify: Link state change notification callback
@@ -68,10 +308,18 @@ struct media_device {
u32 hw_revision;
u32 driver_version;
- u32 entity_id;
+ u32 topology_version;
+
+ u32 id;
+ struct ida entity_internal_idx;
+ int entity_internal_idx_max;
+
struct list_head entities;
+ struct list_head interfaces;
+ struct list_head pads;
+ struct list_head links;
- /* Protects the entities list */
+ /* Protects the graph objects creation/removal */
spinlock_t lock;
/* Serializes graph operations. */
struct mutex graph_mutex;
@@ -80,6 +328,8 @@ struct media_device {
unsigned int notification);
};
+#ifdef CONFIG_MEDIA_CONTROLLER
+
/* Supported link_notify @notification values. */
#define MEDIA_DEV_NOTIFY_PRE_LINK_CH 0
#define MEDIA_DEV_NOTIFY_POST_LINK_CH 1
@@ -87,17 +337,228 @@ struct media_device {
/* media_devnode to media_device */
#define to_media_device(node) container_of(node, struct media_device, devnode)
+/**
+ * media_entity_enum_init - Initialise an entity enumeration
+ *
+ * @ent_enum: Entity enumeration to be initialised
+ * @mdev: The related media device
+ *
+ * Returns zero on success or a negative error code.
+ */
+static inline __must_check int media_entity_enum_init(
+ struct media_entity_enum *ent_enum, struct media_device *mdev)
+{
+ return __media_entity_enum_init(ent_enum,
+ mdev->entity_internal_idx_max + 1);
+}
+
+/**
+ * media_device_init() - Initializes a media device element
+ *
+ * @mdev: pointer to struct &media_device
+ *
+ * This function initializes the media device prior to its registration.
+ * The media device initialization and registration is split in two functions
+ * to avoid race conditions and make the media device available to user-space
+ * before the media graph has been completed.
+ *
+ * So drivers need to first initialize the media device, register any entity
+ * within the media device, create pad to pad links and then finally register
+ * the media device by calling media_device_register() as a final step.
+ */
+void media_device_init(struct media_device *mdev);
+
+/**
+ * media_device_cleanup() - Cleanups a media device element
+ *
+ * @mdev: pointer to struct &media_device
+ *
+ * This function that will destroy the graph_mutex that is
+ * initialized in media_device_init().
+ */
+void media_device_cleanup(struct media_device *mdev);
+
+/**
+ * __media_device_register() - Registers a media device element
+ *
+ * @mdev: pointer to struct &media_device
+ * @owner: should be filled with %THIS_MODULE
+ *
+ * Users, should, instead, call the media_device_register() macro.
+ *
+ * The caller is responsible for initializing the media_device structure before
+ * registration. The following fields must be set:
+ *
+ * - dev must point to the parent device (usually a &pci_dev, &usb_interface or
+ * &platform_device instance).
+ *
+ * - model must be filled with the device model name as a NUL-terminated UTF-8
+ * string. The device/model revision must not be stored in this field.
+ *
+ * The following fields are optional:
+ *
+ * - serial is a unique serial number stored as a NUL-terminated ASCII string.
+ * The field is big enough to store a GUID in text form. If the hardware
+ * doesn't provide a unique serial number this field must be left empty.
+ *
+ * - bus_info represents the location of the device in the system as a
+ * NUL-terminated ASCII string. For PCI/PCIe devices bus_info must be set to
+ * "PCI:" (or "PCIe:") followed by the value of pci_name(). For USB devices,
+ * the usb_make_path() function must be used. This field is used by
+ * applications to distinguish between otherwise identical devices that don't
+ * provide a serial number.
+ *
+ * - hw_revision is the hardware device revision in a driver-specific format.
+ * When possible the revision should be formatted with the KERNEL_VERSION
+ * macro.
+ *
+ * - driver_version is formatted with the KERNEL_VERSION macro. The version
+ * minor must be incremented when new features are added to the userspace API
+ * without breaking binary compatibility. The version major must be
+ * incremented when binary compatibility is broken.
+ *
+ * Notes:
+ *
+ * Upon successful registration a character device named media[0-9]+ is created.
+ * The device major and minor numbers are dynamic. The model name is exported as
+ * a sysfs attribute.
+ *
+ * Unregistering a media device that hasn't been registered is *NOT* safe.
+ *
+ * Return: returns zero on success or a negative error code.
+ */
int __must_check __media_device_register(struct media_device *mdev,
struct module *owner);
#define media_device_register(mdev) __media_device_register(mdev, THIS_MODULE)
+
+/**
+ * __media_device_unregister() - Unegisters a media device element
+ *
+ * @mdev: pointer to struct &media_device
+ *
+ *
+ * It is safe to call this function on an unregistered (but initialised)
+ * media device.
+ */
void media_device_unregister(struct media_device *mdev);
+/**
+ * media_device_register_entity() - registers a media entity inside a
+ * previously registered media device.
+ *
+ * @mdev: pointer to struct &media_device
+ * @entity: pointer to struct &media_entity to be registered
+ *
+ * Entities are identified by a unique positive integer ID. The media
+ * controller framework will such ID automatically. IDs are not guaranteed
+ * to be contiguous, and the ID number can change on newer Kernel versions.
+ * So, neither the driver nor userspace should hardcode ID numbers to refer
+ * to the entities, but, instead, use the framework to find the ID, when
+ * needed.
+ *
+ * The media_entity name, type and flags fields should be initialized before
+ * calling media_device_register_entity(). Entities embedded in higher-level
+ * standard structures can have some of those fields set by the higher-level
+ * framework.
+ *
+ * If the device has pads, media_entity_pads_init() should be called before
+ * this function. Otherwise, the &media_entity.@pad and &media_entity.@num_pads
+ * should be zeroed before calling this function.
+ *
+ * Entities have flags that describe the entity capabilities and state:
+ *
+ * %MEDIA_ENT_FL_DEFAULT indicates the default entity for a given type.
+ * This can be used to report the default audio and video devices or the
+ * default camera sensor.
+ *
+ * NOTE: Drivers should set the entity function before calling this function.
+ * Please notice that the values %MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN and
+ * %MEDIA_ENT_F_UNKNOWN should not be used by the drivers.
+ */
int __must_check media_device_register_entity(struct media_device *mdev,
struct media_entity *entity);
+
+/*
+ * media_device_unregister_entity() - unregisters a media entity.
+ *
+ * @entity: pointer to struct &media_entity to be unregistered
+ *
+ * All links associated with the entity and all PADs are automatically
+ * unregistered from the media_device when this function is called.
+ *
+ * Unregistering an entity will not change the IDs of the other entities and
+ * the previoully used ID will never be reused for a newly registered entities.
+ *
+ * When a media device is unregistered, all its entities are unregistered
+ * automatically. No manual entities unregistration is then required.
+ *
+ * Note: the media_entity instance itself must be freed explicitly by
+ * the driver if required.
+ */
void media_device_unregister_entity(struct media_entity *entity);
+/**
+ * media_device_get_devres() - get media device as device resource
+ * creates if one doesn't exist
+ *
+ * @dev: pointer to struct &device.
+ *
+ * Sometimes, the media controller &media_device needs to be shared by more
+ * than one driver. This function adds support for that, by dynamically
+ * allocating the &media_device and allowing it to be obtained from the
+ * struct &device associated with the common device where all sub-device
+ * components belong. So, for example, on an USB device with multiple
+ * interfaces, each interface may be handled by a separate per-interface
+ * drivers. While each interface have its own &device, they all share a
+ * common &device associated with the hole USB device.
+ */
+struct media_device *media_device_get_devres(struct device *dev);
+
+/**
+ * media_device_find_devres() - find media device as device resource
+ *
+ * @dev: pointer to struct &device.
+ */
+struct media_device *media_device_find_devres(struct device *dev);
+
/* Iterate over all entities. */
#define media_device_for_each_entity(entity, mdev) \
- list_for_each_entry(entity, &(mdev)->entities, list)
+ list_for_each_entry(entity, &(mdev)->entities, graph_obj.list)
+
+/* Iterate over all interfaces. */
+#define media_device_for_each_intf(intf, mdev) \
+ list_for_each_entry(intf, &(mdev)->interfaces, graph_obj.list)
+
+/* Iterate over all pads. */
+#define media_device_for_each_pad(pad, mdev) \
+ list_for_each_entry(pad, &(mdev)->pads, graph_obj.list)
+/* Iterate over all links. */
+#define media_device_for_each_link(link, mdev) \
+ list_for_each_entry(link, &(mdev)->links, graph_obj.list)
+#else
+static inline int media_device_register(struct media_device *mdev)
+{
+ return 0;
+}
+static inline void media_device_unregister(struct media_device *mdev)
+{
+}
+static inline int media_device_register_entity(struct media_device *mdev,
+ struct media_entity *entity)
+{
+ return 0;
+}
+static inline void media_device_unregister_entity(struct media_entity *entity)
+{
+}
+static inline struct media_device *media_device_get_devres(struct device *dev)
+{
+ return NULL;
+}
+static inline struct media_device *media_device_find_devres(struct device *dev)
+{
+ return NULL;
+}
+#endif /* CONFIG_MEDIA_CONTROLLER */
#endif
diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
index 17ddae32060d..fe42f08e72bd 100644
--- a/include/media/media-devnode.h
+++ b/include/media/media-devnode.h
@@ -40,6 +40,20 @@
*/
#define MEDIA_FLAG_REGISTERED 0
+/**
+ * struct media_file_operations - Media device file operations
+ *
+ * @owner: should be filled with %THIS_MODULE
+ * @read: pointer to the function that implements read() syscall
+ * @write: pointer to the function that implements write() syscall
+ * @poll: pointer to the function that implements poll() syscall
+ * @ioctl: pointer to the function that implements ioctl() syscall
+ * @compat_ioctl: pointer to the function that will handle 32 bits userspace
+ * calls to the the ioctl() syscall on a Kernel compiled with 64 bits.
+ * @open: pointer to the function that implements open() syscall
+ * @release: pointer to the function that will release the resources allocated
+ * by the @open function.
+ */
struct media_file_operations {
struct module *owner;
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
@@ -53,7 +67,7 @@ struct media_file_operations {
/**
* struct media_devnode - Media device node
- * @fops: pointer to struct media_file_operations with media device ops
+ * @fops: pointer to struct &media_file_operations with media device ops
* @dev: struct device pointer for the media controller device
* @cdev: struct cdev pointer character device
* @parent: parent device
@@ -86,15 +100,53 @@ struct media_devnode {
/* dev to media_devnode */
#define to_media_devnode(cd) container_of(cd, struct media_devnode, dev)
+/**
+ * media_devnode_register - register a media device node
+ *
+ * @mdev: media device node structure we want to register
+ * @owner: should be filled with %THIS_MODULE
+ *
+ * The registration code assigns minor numbers and registers the new device node
+ * with the kernel. An error is returned if no free minor number can be found,
+ * or if the registration of the device node fails.
+ *
+ * Zero is returned on success.
+ *
+ * Note that if the media_devnode_register call fails, the release() callback of
+ * the media_devnode structure is *not* called, so the caller is responsible for
+ * freeing any data.
+ */
int __must_check media_devnode_register(struct media_devnode *mdev,
struct module *owner);
+
+/**
+ * media_devnode_unregister - unregister a media device node
+ * @mdev: the device node to unregister
+ *
+ * This unregisters the passed device. Future open calls will be met with
+ * errors.
+ *
+ * This function can safely be called if the device node has never been
+ * registered or has already been unregistered.
+ */
void media_devnode_unregister(struct media_devnode *mdev);
+/**
+ * media_devnode_data - returns a pointer to the &media_devnode
+ *
+ * @filp: pointer to struct &file
+ */
static inline struct media_devnode *media_devnode_data(struct file *filp)
{
return filp->private_data;
}
+/**
+ * media_devnode_is_registered - returns true if &media_devnode is registered;
+ * false otherwise.
+ *
+ * @mdev: pointer to struct &media_devnode.
+ */
static inline int media_devnode_is_registered(struct media_devnode *mdev)
{
return test_bit(MEDIA_FLAG_REGISTERED, &mdev->flags);
diff --git a/include/media/media-entity.h b/include/media/media-entity.h
index 197f93799753..fe485d367985 100644
--- a/include/media/media-entity.h
+++ b/include/media/media-entity.h
@@ -23,25 +23,151 @@
#ifndef _MEDIA_ENTITY_H
#define _MEDIA_ENTITY_H
-#include <linux/bitops.h>
+#include <linux/bitmap.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/media.h>
+/* Enums used internally at the media controller to represent graphs */
+
+/**
+ * enum media_gobj_type - type of a graph object
+ *
+ * @MEDIA_GRAPH_ENTITY: Identify a media entity
+ * @MEDIA_GRAPH_PAD: Identify a media pad
+ * @MEDIA_GRAPH_LINK: Identify a media link
+ * @MEDIA_GRAPH_INTF_DEVNODE: Identify a media Kernel API interface via
+ * a device node
+ */
+enum media_gobj_type {
+ MEDIA_GRAPH_ENTITY,
+ MEDIA_GRAPH_PAD,
+ MEDIA_GRAPH_LINK,
+ MEDIA_GRAPH_INTF_DEVNODE,
+};
+
+#define MEDIA_BITS_PER_TYPE 8
+#define MEDIA_BITS_PER_ID (32 - MEDIA_BITS_PER_TYPE)
+#define MEDIA_ID_MASK GENMASK_ULL(MEDIA_BITS_PER_ID - 1, 0)
+
+/* Structs to represent the objects that belong to a media graph */
+
+/**
+ * struct media_gobj - Define a graph object.
+ *
+ * @mdev: Pointer to the struct media_device that owns the object
+ * @id: Non-zero object ID identifier. The ID should be unique
+ * inside a media_device, as it is composed by
+ * %MEDIA_BITS_PER_TYPE to store the type plus
+ * %MEDIA_BITS_PER_ID to store the ID
+ * @list: List entry stored in one of the per-type mdev object lists
+ *
+ * All objects on the media graph should have this struct embedded
+ */
+struct media_gobj {
+ struct media_device *mdev;
+ u32 id;
+ struct list_head list;
+};
+
+#define MEDIA_ENTITY_ENUM_MAX_DEPTH 16
+
+/**
+ * struct media_entity_enum - An enumeration of media entities.
+ *
+ * @bmap: Bit map in which each bit represents one entity at struct
+ * media_entity->internal_idx.
+ * @idx_max: Number of bits in bmap
+ */
+struct media_entity_enum {
+ unsigned long *bmap;
+ int idx_max;
+};
+
+/**
+ * struct media_entity_graph - Media graph traversal state
+ *
+ * @stack: Graph traversal stack; the stack contains information
+ * on the path the media entities to be walked and the
+ * links through which they were reached.
+ * @ent_enum: Visited entities
+ * @top: The top of the stack
+ */
+struct media_entity_graph {
+ struct {
+ struct media_entity *entity;
+ struct list_head *link;
+ } stack[MEDIA_ENTITY_ENUM_MAX_DEPTH];
+
+ struct media_entity_enum ent_enum;
+ int top;
+};
+
+/*
+ * struct media_pipeline - Media pipeline related information
+ *
+ * @streaming_count: Streaming start count - streaming stop count
+ * @graph: Media graph walk during pipeline start / stop
+ */
struct media_pipeline {
+ int streaming_count;
+ struct media_entity_graph graph;
};
+/**
+ * struct media_link - A link object part of a media graph.
+ *
+ * @graph_obj: Embedded structure containing the media object common data
+ * @list: Linked list associated with an entity or an interface that
+ * owns the link.
+ * @gobj0: Part of a union. Used to get the pointer for the first
+ * graph_object of the link.
+ * @source: Part of a union. Used only if the first object (gobj0) is
+ * a pad. In that case, it represents the source pad.
+ * @intf: Part of a union. Used only if the first object (gobj0) is
+ * an interface.
+ * @gobj1: Part of a union. Used to get the pointer for the second
+ * graph_object of the link.
+ * @source: Part of a union. Used only if the second object (gobj1) is
+ * a pad. In that case, it represents the sink pad.
+ * @entity: Part of a union. Used only if the second object (gobj1) is
+ * an entity.
+ * @reverse: Pointer to the link for the reverse direction of a pad to pad
+ * link.
+ * @flags: Link flags, as defined in uapi/media.h (MEDIA_LNK_FL_*)
+ * @is_backlink: Indicate if the link is a backlink.
+ */
struct media_link {
- struct media_pad *source; /* Source pad */
- struct media_pad *sink; /* Sink pad */
- struct media_link *reverse; /* Link in the reverse direction */
- unsigned long flags; /* Link flags (MEDIA_LNK_FL_*) */
+ struct media_gobj graph_obj;
+ struct list_head list;
+ union {
+ struct media_gobj *gobj0;
+ struct media_pad *source;
+ struct media_interface *intf;
+ };
+ union {
+ struct media_gobj *gobj1;
+ struct media_pad *sink;
+ struct media_entity *entity;
+ };
+ struct media_link *reverse;
+ unsigned long flags;
+ bool is_backlink;
};
+/**
+ * struct media_pad - A media pad graph object.
+ *
+ * @graph_obj: Embedded structure containing the media object common data
+ * @entity: Entity this pad belongs to
+ * @index: Pad index in the entity pads array, numbered from 0 to n
+ * @flags: Pad flags, as defined in uapi/media.h (MEDIA_PAD_FL_*)
+ */
struct media_pad {
- struct media_entity *entity; /* Entity this pad belongs to */
- u16 index; /* Pad index in the entity pads array */
- unsigned long flags; /* Pad flags (MEDIA_PAD_FL_*) */
+ struct media_gobj graph_obj; /* must be first field in struct */
+ struct media_entity *entity;
+ u16 index;
+ unsigned long flags;
};
/**
@@ -60,105 +186,763 @@ struct media_entity_operations {
int (*link_validate)(struct media_link *link);
};
+/**
+ * struct media_entity - A media entity graph object.
+ *
+ * @graph_obj: Embedded structure containing the media object common data.
+ * @name: Entity name.
+ * @function: Entity main function, as defined in uapi/media.h
+ * (MEDIA_ENT_F_*)
+ * @flags: Entity flags, as defined in uapi/media.h (MEDIA_ENT_FL_*)
+ * @num_pads: Number of sink and source pads.
+ * @num_links: Total number of links, forward and back, enabled and disabled.
+ * @num_backlinks: Number of backlinks
+ * @internal_idx: An unique internal entity specific number. The numbers are
+ * re-used if entities are unregistered or registered again.
+ * @pads: Pads array with the size defined by @num_pads.
+ * @links: List of data links.
+ * @ops: Entity operations.
+ * @stream_count: Stream count for the entity.
+ * @use_count: Use count for the entity.
+ * @pipe: Pipeline this entity belongs to.
+ * @info: Union with devnode information. Kept just for backward
+ * compatibility.
+ * @major: Devnode major number (zero if not applicable). Kept just
+ * for backward compatibility.
+ * @minor: Devnode minor number (zero if not applicable). Kept just
+ * for backward compatibility.
+ *
+ * NOTE: @stream_count and @use_count reference counts must never be
+ * negative, but are signed integers on purpose: a simple WARN_ON(<0) check
+ * can be used to detect reference count bugs that would make them negative.
+ */
struct media_entity {
- struct list_head list;
- struct media_device *parent; /* Media device this entity belongs to*/
- u32 id; /* Entity ID, unique in the parent media
- * device context */
- const char *name; /* Entity name */
- u32 type; /* Entity type (MEDIA_ENT_T_*) */
- u32 revision; /* Entity revision, driver specific */
- unsigned long flags; /* Entity flags (MEDIA_ENT_FL_*) */
- u32 group_id; /* Entity group ID */
-
- u16 num_pads; /* Number of sink and source pads */
- u16 num_links; /* Number of existing links, both
- * enabled and disabled */
- u16 num_backlinks; /* Number of backlinks */
- u16 max_links; /* Maximum number of links */
-
- struct media_pad *pads; /* Pads array (num_pads elements) */
- struct media_link *links; /* Links array (max_links elements)*/
-
- const struct media_entity_operations *ops; /* Entity operations */
+ struct media_gobj graph_obj; /* must be first field in struct */
+ const char *name;
+ u32 function;
+ unsigned long flags;
+
+ u16 num_pads;
+ u16 num_links;
+ u16 num_backlinks;
+ int internal_idx;
+
+ struct media_pad *pads;
+ struct list_head links;
+
+ const struct media_entity_operations *ops;
/* Reference counts must never be negative, but are signed integers on
* purpose: a simple WARN_ON(<0) check can be used to detect reference
* count bugs that would make them negative.
*/
- int stream_count; /* Stream count for the entity. */
- int use_count; /* Use count for the entity. */
+ int stream_count;
+ int use_count;
- struct media_pipeline *pipe; /* Pipeline this entity belongs to. */
+ struct media_pipeline *pipe;
union {
- /* Node specifications */
struct {
u32 major;
u32 minor;
} dev;
-
- /* Sub-device specifications */
- /* Nothing needed yet */
} info;
};
-static inline u32 media_entity_type(struct media_entity *entity)
+/**
+ * struct media_interface - A media interface graph object.
+ *
+ * @graph_obj: embedded graph object
+ * @links: List of links pointing to graph entities
+ * @type: Type of the interface as defined in the
+ * uapi/media/media.h header, e. g.
+ * MEDIA_INTF_T_*
+ * @flags: Interface flags as defined in uapi/media/media.h
+ */
+struct media_interface {
+ struct media_gobj graph_obj;
+ struct list_head links;
+ u32 type;
+ u32 flags;
+};
+
+/**
+ * struct media_intf_devnode - A media interface via a device node.
+ *
+ * @intf: embedded interface object
+ * @major: Major number of a device node
+ * @minor: Minor number of a device node
+ */
+struct media_intf_devnode {
+ struct media_interface intf;
+
+ /* Should match the fields at media_v2_intf_devnode */
+ u32 major;
+ u32 minor;
+};
+
+/**
+ * media_entity_id() - return the media entity graph object id
+ *
+ * @entity: pointer to entity
+ */
+static inline u32 media_entity_id(struct media_entity *entity)
{
- return entity->type & MEDIA_ENT_TYPE_MASK;
+ return entity->graph_obj.id;
}
-static inline u32 media_entity_subtype(struct media_entity *entity)
+/**
+ * media_type() - return the media object type
+ *
+ * @gobj: pointer to the media graph object
+ */
+static inline enum media_gobj_type media_type(struct media_gobj *gobj)
{
- return entity->type & MEDIA_ENT_SUBTYPE_MASK;
+ return gobj->id >> MEDIA_BITS_PER_ID;
}
-#define MEDIA_ENTITY_ENUM_MAX_DEPTH 16
-#define MEDIA_ENTITY_ENUM_MAX_ID 64
+/**
+ * media_id() - return the media object ID
+ *
+ * @gobj: pointer to the media graph object
+ */
+static inline u32 media_id(struct media_gobj *gobj)
+{
+ return gobj->id & MEDIA_ID_MASK;
+}
-/*
- * The number of pads can't be bigger than the number of entities,
- * as the worse-case scenario is to have one entity linked up to
- * MEDIA_ENTITY_ENUM_MAX_ID - 1 entities.
+/**
+ * media_gobj_gen_id() - encapsulates type and ID on at the object ID
+ *
+ * @type: object type as define at enum &media_gobj_type.
+ * @local_id: next ID, from struct &media_device.@id.
*/
-#define MEDIA_ENTITY_MAX_PADS (MEDIA_ENTITY_ENUM_MAX_ID - 1)
+static inline u32 media_gobj_gen_id(enum media_gobj_type type, u64 local_id)
+{
+ u32 id;
-struct media_entity_graph {
- struct {
- struct media_entity *entity;
- int link;
- } stack[MEDIA_ENTITY_ENUM_MAX_DEPTH];
+ id = type << MEDIA_BITS_PER_ID;
+ id |= local_id & MEDIA_ID_MASK;
- DECLARE_BITMAP(entities, MEDIA_ENTITY_ENUM_MAX_ID);
- int top;
-};
+ return id;
+}
+
+/**
+ * is_media_entity_v4l2_io() - identify if the entity main function
+ * is a V4L2 I/O
+ *
+ * @entity: pointer to entity
+ *
+ * Return: true if the entity main function is one of the V4L2 I/O types
+ * (video, VBI or SDR radio); false otherwise.
+ */
+static inline bool is_media_entity_v4l2_io(struct media_entity *entity)
+{
+ if (!entity)
+ return false;
+
+ switch (entity->function) {
+ case MEDIA_ENT_F_IO_V4L:
+ case MEDIA_ENT_F_IO_VBI:
+ case MEDIA_ENT_F_IO_SWRADIO:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * is_media_entity_v4l2_subdev - return true if the entity main function is
+ * associated with the V4L2 API subdev usage
+ *
+ * @entity: pointer to entity
+ *
+ * This is an ancillary function used by subdev-based V4L2 drivers.
+ * It checks if the entity function is one of functions used by a V4L2 subdev,
+ * e. g. camera-relatef functions, analog TV decoder, TV tuner, V4L2 DSPs.
+ */
+static inline bool is_media_entity_v4l2_subdev(struct media_entity *entity)
+{
+ if (!entity)
+ return false;
+
+ switch (entity->function) {
+ case MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN:
+ case MEDIA_ENT_F_CAM_SENSOR:
+ case MEDIA_ENT_F_FLASH:
+ case MEDIA_ENT_F_LENS:
+ case MEDIA_ENT_F_ATV_DECODER:
+ case MEDIA_ENT_F_TUNER:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/**
+ * __media_entity_enum_init - Initialise an entity enumeration
+ *
+ * @ent_enum: Entity enumeration to be initialised
+ * @idx_max: Maximum number of entities in the enumeration
+ *
+ * Return: Returns zero on success or a negative error code.
+ */
+__must_check int __media_entity_enum_init(struct media_entity_enum *ent_enum,
+ int idx_max);
+
+/**
+ * media_entity_enum_cleanup - Release resources of an entity enumeration
+ *
+ * @ent_enum: Entity enumeration to be released
+ */
+void media_entity_enum_cleanup(struct media_entity_enum *ent_enum);
+
+/**
+ * media_entity_enum_zero - Clear the entire enum
+ *
+ * @ent_enum: Entity enumeration to be cleared
+ */
+static inline void media_entity_enum_zero(struct media_entity_enum *ent_enum)
+{
+ bitmap_zero(ent_enum->bmap, ent_enum->idx_max);
+}
+
+/**
+ * media_entity_enum_set - Mark a single entity in the enum
+ *
+ * @ent_enum: Entity enumeration
+ * @entity: Entity to be marked
+ */
+static inline void media_entity_enum_set(struct media_entity_enum *ent_enum,
+ struct media_entity *entity)
+{
+ if (WARN_ON(entity->internal_idx >= ent_enum->idx_max))
+ return;
+
+ __set_bit(entity->internal_idx, ent_enum->bmap);
+}
+
+/**
+ * media_entity_enum_clear - Unmark a single entity in the enum
+ *
+ * @ent_enum: Entity enumeration
+ * @entity: Entity to be unmarked
+ */
+static inline void media_entity_enum_clear(struct media_entity_enum *ent_enum,
+ struct media_entity *entity)
+{
+ if (WARN_ON(entity->internal_idx >= ent_enum->idx_max))
+ return;
-int media_entity_init(struct media_entity *entity, u16 num_pads,
- struct media_pad *pads, u16 extra_links);
-void media_entity_cleanup(struct media_entity *entity);
+ __clear_bit(entity->internal_idx, ent_enum->bmap);
+}
+
+/**
+ * media_entity_enum_test - Test whether the entity is marked
+ *
+ * @ent_enum: Entity enumeration
+ * @entity: Entity to be tested
+ *
+ * Returns true if the entity was marked.
+ */
+static inline bool media_entity_enum_test(struct media_entity_enum *ent_enum,
+ struct media_entity *entity)
+{
+ if (WARN_ON(entity->internal_idx >= ent_enum->idx_max))
+ return true;
+
+ return test_bit(entity->internal_idx, ent_enum->bmap);
+}
+
+/**
+ * media_entity_enum_test - Test whether the entity is marked, and mark it
+ *
+ * @ent_enum: Entity enumeration
+ * @entity: Entity to be tested
+ *
+ * Returns true if the entity was marked, and mark it before doing so.
+ */
+static inline bool
+media_entity_enum_test_and_set(struct media_entity_enum *ent_enum,
+ struct media_entity *entity)
+{
+ if (WARN_ON(entity->internal_idx >= ent_enum->idx_max))
+ return true;
+
+ return __test_and_set_bit(entity->internal_idx, ent_enum->bmap);
+}
+
+/**
+ * media_entity_enum_empty - Test whether the entire enum is empty
+ *
+ * @ent_enum: Entity enumeration
+ *
+ * Returns true if the entity was marked.
+ */
+static inline bool media_entity_enum_empty(struct media_entity_enum *ent_enum)
+{
+ return bitmap_empty(ent_enum->bmap, ent_enum->idx_max);
+}
+
+/**
+ * media_entity_enum_intersects - Test whether two enums intersect
+ *
+ * @ent_enum1: First entity enumeration
+ * @ent_enum2: Second entity enumeration
+ *
+ * Returns true if entity enumerations e and f intersect, otherwise false.
+ */
+static inline bool media_entity_enum_intersects(
+ struct media_entity_enum *ent_enum1,
+ struct media_entity_enum *ent_enum2)
+{
+ WARN_ON(ent_enum1->idx_max != ent_enum2->idx_max);
+
+ return bitmap_intersects(ent_enum1->bmap, ent_enum2->bmap,
+ min(ent_enum1->idx_max, ent_enum2->idx_max));
+}
+
+#define gobj_to_entity(gobj) \
+ container_of(gobj, struct media_entity, graph_obj)
+
+#define gobj_to_pad(gobj) \
+ container_of(gobj, struct media_pad, graph_obj)
+
+#define gobj_to_link(gobj) \
+ container_of(gobj, struct media_link, graph_obj)
+
+#define gobj_to_link(gobj) \
+ container_of(gobj, struct media_link, graph_obj)
+
+#define gobj_to_pad(gobj) \
+ container_of(gobj, struct media_pad, graph_obj)
+
+#define gobj_to_intf(gobj) \
+ container_of(gobj, struct media_interface, graph_obj)
+
+#define intf_to_devnode(intf) \
+ container_of(intf, struct media_intf_devnode, intf)
+
+/**
+ * media_gobj_create - Initialize a graph object
+ *
+ * @mdev: Pointer to the media_device that contains the object
+ * @type: Type of the object
+ * @gobj: Pointer to the graph object
+ *
+ * This routine initializes the embedded struct media_gobj inside a
+ * media graph object. It is called automatically if media_*_create()
+ * calls are used. However, if the object (entity, link, pad, interface)
+ * is embedded on some other object, this function should be called before
+ * registering the object at the media controller.
+ */
+void media_gobj_create(struct media_device *mdev,
+ enum media_gobj_type type,
+ struct media_gobj *gobj);
+
+/**
+ * media_gobj_destroy - Stop using a graph object on a media device
+ *
+ * @gobj: Pointer to the graph object
+ *
+ * This should be called by all routines like media_device_unregister()
+ * that remove/destroy media graph objects.
+ */
+void media_gobj_destroy(struct media_gobj *gobj);
+
+/**
+ * media_entity_pads_init() - Initialize the entity pads
+ *
+ * @entity: entity where the pads belong
+ * @num_pads: total number of sink and source pads
+ * @pads: Array of @num_pads pads.
+ *
+ * The pads array is managed by the entity driver and passed to
+ * media_entity_pads_init() where its pointer will be stored in the entity
+ * structure.
+ *
+ * If no pads are needed, drivers could either directly fill
+ * &media_entity->@num_pads with 0 and &media_entity->@pads with NULL or call
+ * this function that will do the same.
+ *
+ * As the number of pads is known in advance, the pads array is not allocated
+ * dynamically but is managed by the entity driver. Most drivers will embed the
+ * pads array in a driver-specific structure, avoiding dynamic allocation.
+ *
+ * Drivers must set the direction of every pad in the pads array before calling
+ * media_entity_pads_init(). The function will initialize the other pads fields.
+ */
+int media_entity_pads_init(struct media_entity *entity, u16 num_pads,
+ struct media_pad *pads);
+
+/**
+ * media_entity_cleanup() - free resources associated with an entity
+ *
+ * @entity: entity where the pads belong
+ *
+ * This function must be called during the cleanup phase after unregistering
+ * the entity (currently, it does nothing).
+ */
+static inline void media_entity_cleanup(struct media_entity *entity) {};
+
+/**
+ * media_create_pad_link() - creates a link between two entities.
+ *
+ * @source: pointer to &media_entity of the source pad.
+ * @source_pad: number of the source pad in the pads array
+ * @sink: pointer to &media_entity of the sink pad.
+ * @sink_pad: number of the sink pad in the pads array.
+ * @flags: Link flags, as defined in include/uapi/linux/media.h.
+ *
+ * Valid values for flags:
+ * A %MEDIA_LNK_FL_ENABLED flag indicates that the link is enabled and can be
+ * used to transfer media data. When two or more links target a sink pad,
+ * only one of them can be enabled at a time.
+ *
+ * A %MEDIA_LNK_FL_IMMUTABLE flag indicates that the link enabled state can't
+ * be modified at runtime. If %MEDIA_LNK_FL_IMMUTABLE is set, then
+ * %MEDIA_LNK_FL_ENABLED must also be set since an immutable link is
+ * always enabled.
+ *
+ * NOTE:
+ *
+ * Before calling this function, media_entity_pads_init() and
+ * media_device_register_entity() should be called previously for both ends.
+ */
+__must_check int media_create_pad_link(struct media_entity *source,
+ u16 source_pad, struct media_entity *sink,
+ u16 sink_pad, u32 flags);
+
+/**
+ * media_create_pad_links() - creates a link between two entities.
+ *
+ * @mdev: Pointer to the media_device that contains the object
+ * @source_function: Function of the source entities. Used only if @source is
+ * NULL.
+ * @source: pointer to &media_entity of the source pad. If NULL, it will use
+ * all entities that matches the @sink_function.
+ * @source_pad: number of the source pad in the pads array
+ * @sink_function: Function of the sink entities. Used only if @sink is NULL.
+ * @sink: pointer to &media_entity of the sink pad. If NULL, it will use
+ * all entities that matches the @sink_function.
+ * @sink_pad: number of the sink pad in the pads array.
+ * @flags: Link flags, as defined in include/uapi/linux/media.h.
+ * @allow_both_undefined: if true, then both @source and @sink can be NULL.
+ * In such case, it will create a crossbar between all entities that
+ * matches @source_function to all entities that matches @sink_function.
+ * If false, it will return 0 and won't create any link if both @source
+ * and @sink are NULL.
+ *
+ * Valid values for flags:
+ * A %MEDIA_LNK_FL_ENABLED flag indicates that the link is enabled and can be
+ * used to transfer media data. If multiple links are created and this
+ * flag is passed as an argument, only the first created link will have
+ * this flag.
+ *
+ * A %MEDIA_LNK_FL_IMMUTABLE flag indicates that the link enabled state can't
+ * be modified at runtime. If %MEDIA_LNK_FL_IMMUTABLE is set, then
+ * %MEDIA_LNK_FL_ENABLED must also be set since an immutable link is
+ * always enabled.
+ *
+ * It is common for some devices to have multiple source and/or sink entities
+ * of the same type that should be linked. While media_create_pad_link()
+ * creates link by link, this function is meant to allow 1:n, n:1 and even
+ * cross-bar (n:n) links.
+ *
+ * NOTE: Before calling this function, media_entity_pads_init() and
+ * media_device_register_entity() should be called previously for the entities
+ * to be linked.
+ */
+int media_create_pad_links(const struct media_device *mdev,
+ const u32 source_function,
+ struct media_entity *source,
+ const u16 source_pad,
+ const u32 sink_function,
+ struct media_entity *sink,
+ const u16 sink_pad,
+ u32 flags,
+ const bool allow_both_undefined);
-int media_entity_create_link(struct media_entity *source, u16 source_pad,
- struct media_entity *sink, u16 sink_pad, u32 flags);
void __media_entity_remove_links(struct media_entity *entity);
+
+/**
+ * media_entity_remove_links() - remove all links associated with an entity
+ *
+ * @entity: pointer to &media_entity
+ *
+ * Note: this is called automatically when an entity is unregistered via
+ * media_device_register_entity().
+ */
void media_entity_remove_links(struct media_entity *entity);
+/**
+ * __media_entity_setup_link - Configure a media link without locking
+ * @link: The link being configured
+ * @flags: Link configuration flags
+ *
+ * The bulk of link setup is handled by the two entities connected through the
+ * link. This function notifies both entities of the link configuration change.
+ *
+ * If the link is immutable or if the current and new configuration are
+ * identical, return immediately.
+ *
+ * The user is expected to hold link->source->parent->mutex. If not,
+ * media_entity_setup_link() should be used instead.
+ */
int __media_entity_setup_link(struct media_link *link, u32 flags);
+
+/**
+ * media_entity_setup_link() - changes the link flags properties in runtime
+ *
+ * @link: pointer to &media_link
+ * @flags: the requested new link flags
+ *
+ * The only configurable property is the %MEDIA_LNK_FL_ENABLED link flag
+ * flag to enable/disable a link. Links marked with the
+ * %MEDIA_LNK_FL_IMMUTABLE link flag can not be enabled or disabled.
+ *
+ * When a link is enabled or disabled, the media framework calls the
+ * link_setup operation for the two entities at the source and sink of the
+ * link, in that order. If the second link_setup call fails, another
+ * link_setup call is made on the first entity to restore the original link
+ * flags.
+ *
+ * Media device drivers can be notified of link setup operations by setting the
+ * media_device::link_notify pointer to a callback function. If provided, the
+ * notification callback will be called before enabling and after disabling
+ * links.
+ *
+ * Entity drivers must implement the link_setup operation if any of their links
+ * is non-immutable. The operation must either configure the hardware or store
+ * the configuration information to be applied later.
+ *
+ * Link configuration must not have any side effect on other links. If an
+ * enabled link at a sink pad prevents another link at the same pad from
+ * being enabled, the link_setup operation must return -EBUSY and can't
+ * implicitly disable the first enabled link.
+ *
+ * NOTE: the valid values of the flags for the link is the same as described
+ * on media_create_pad_link(), for pad to pad links or the same as described
+ * on media_create_intf_link(), for interface to entity links.
+ */
int media_entity_setup_link(struct media_link *link, u32 flags);
+
+/**
+ * media_entity_find_link - Find a link between two pads
+ * @source: Source pad
+ * @sink: Sink pad
+ *
+ * Return a pointer to the link between the two entities. If no such link
+ * exists, return NULL.
+ */
struct media_link *media_entity_find_link(struct media_pad *source,
struct media_pad *sink);
+
+/**
+ * media_entity_remote_pad - Find the pad at the remote end of a link
+ * @pad: Pad at the local end of the link
+ *
+ * Search for a remote pad connected to the given pad by iterating over all
+ * links originating or terminating at that pad until an enabled link is found.
+ *
+ * Return a pointer to the pad at the remote end of the first found enabled
+ * link, or NULL if no enabled link has been found.
+ */
struct media_pad *media_entity_remote_pad(struct media_pad *pad);
+/**
+ * media_entity_get - Get a reference to the parent module
+ *
+ * @entity: The entity
+ *
+ * Get a reference to the parent media device module.
+ *
+ * The function will return immediately if @entity is NULL.
+ *
+ * Return a pointer to the entity on success or NULL on failure.
+ */
struct media_entity *media_entity_get(struct media_entity *entity);
+
+__must_check int media_entity_graph_walk_init(
+ struct media_entity_graph *graph, struct media_device *mdev);
+
+/**
+ * media_entity_graph_walk_cleanup - Release resources used by graph walk.
+ *
+ * @graph: Media graph structure that will be used to walk the graph
+ */
+void media_entity_graph_walk_cleanup(struct media_entity_graph *graph);
+
+/**
+ * media_entity_put - Release the reference to the parent module
+ *
+ * @entity: The entity
+ *
+ * Release the reference count acquired by media_entity_get().
+ *
+ * The function will return immediately if @entity is NULL.
+ */
void media_entity_put(struct media_entity *entity);
+/**
+ * media_entity_graph_walk_start - Start walking the media graph at a given entity
+ * @graph: Media graph structure that will be used to walk the graph
+ * @entity: Starting entity
+ *
+ * Before using this function, media_entity_graph_walk_init() must be
+ * used to allocate resources used for walking the graph. This
+ * function initializes the graph traversal structure to walk the
+ * entities graph starting at the given entity. The traversal
+ * structure must not be modified by the caller during graph
+ * traversal. After the graph walk, the resources must be released
+ * using media_entity_graph_walk_cleanup().
+ */
void media_entity_graph_walk_start(struct media_entity_graph *graph,
- struct media_entity *entity);
+ struct media_entity *entity);
+
+/**
+ * media_entity_graph_walk_next - Get the next entity in the graph
+ * @graph: Media graph structure
+ *
+ * Perform a depth-first traversal of the given media entities graph.
+ *
+ * The graph structure must have been previously initialized with a call to
+ * media_entity_graph_walk_start().
+ *
+ * Return the next entity in the graph or NULL if the whole graph have been
+ * traversed.
+ */
struct media_entity *
media_entity_graph_walk_next(struct media_entity_graph *graph);
+
+/**
+ * media_entity_pipeline_start - Mark a pipeline as streaming
+ * @entity: Starting entity
+ * @pipe: Media pipeline to be assigned to all entities in the pipeline.
+ *
+ * Mark all entities connected to a given entity through enabled links, either
+ * directly or indirectly, as streaming. The given pipeline object is assigned to
+ * every entity in the pipeline and stored in the media_entity pipe field.
+ *
+ * Calls to this function can be nested, in which case the same number of
+ * media_entity_pipeline_stop() calls will be required to stop streaming. The
+ * pipeline pointer must be identical for all nested calls to
+ * media_entity_pipeline_start().
+ */
__must_check int media_entity_pipeline_start(struct media_entity *entity,
struct media_pipeline *pipe);
+
+/**
+ * media_entity_pipeline_stop - Mark a pipeline as not streaming
+ * @entity: Starting entity
+ *
+ * Mark all entities connected to a given entity through enabled links, either
+ * directly or indirectly, as not streaming. The media_entity pipe field is
+ * reset to NULL.
+ *
+ * If multiple calls to media_entity_pipeline_start() have been made, the same
+ * number of calls to this function are required to mark the pipeline as not
+ * streaming.
+ */
void media_entity_pipeline_stop(struct media_entity *entity);
+/**
+ * media_devnode_create() - creates and initializes a device node interface
+ *
+ * @mdev: pointer to struct &media_device
+ * @type: type of the interface, as given by MEDIA_INTF_T_* macros
+ * as defined in the uapi/media/media.h header.
+ * @flags: Interface flags as defined in uapi/media/media.h.
+ * @major: Device node major number.
+ * @minor: Device node minor number.
+ *
+ * Return: if succeeded, returns a pointer to the newly allocated
+ * &media_intf_devnode pointer.
+ */
+struct media_intf_devnode *
+__must_check media_devnode_create(struct media_device *mdev,
+ u32 type, u32 flags,
+ u32 major, u32 minor);
+/**
+ * media_devnode_remove() - removes a device node interface
+ *
+ * @devnode: pointer to &media_intf_devnode to be freed.
+ *
+ * When a device node interface is removed, all links to it are automatically
+ * removed.
+ */
+void media_devnode_remove(struct media_intf_devnode *devnode);
+struct media_link *
+
+/**
+ * media_create_intf_link() - creates a link between an entity and an interface
+ *
+ * @entity: pointer to %media_entity
+ * @intf: pointer to %media_interface
+ * @flags: Link flags, as defined in include/uapi/linux/media.h.
+ *
+ *
+ * Valid values for flags:
+ * The %MEDIA_LNK_FL_ENABLED flag indicates that the interface is connected to
+ * the entity hardware. That's the default value for interfaces. An
+ * interface may be disabled if the hardware is busy due to the usage
+ * of some other interface that it is currently controlling the hardware.
+ * A typical example is an hybrid TV device that handle only one type of
+ * stream on a given time. So, when the digital TV is streaming,
+ * the V4L2 interfaces won't be enabled, as such device is not able to
+ * also stream analog TV or radio.
+ *
+ * Note:
+ *
+ * Before calling this function, media_devnode_create() should be called for
+ * the interface and media_device_register_entity() should be called for the
+ * interface that will be part of the link.
+ */
+__must_check media_create_intf_link(struct media_entity *entity,
+ struct media_interface *intf,
+ u32 flags);
+/**
+ * __media_remove_intf_link() - remove a single interface link
+ *
+ * @link: pointer to &media_link.
+ *
+ * Note: this is an unlocked version of media_remove_intf_link()
+ */
+void __media_remove_intf_link(struct media_link *link);
+
+/**
+ * media_remove_intf_link() - remove a single interface link
+ *
+ * @link: pointer to &media_link.
+ *
+ * Note: prefer to use this one, instead of __media_remove_intf_link()
+ */
+void media_remove_intf_link(struct media_link *link);
+
+/**
+ * __media_remove_intf_links() - remove all links associated with an interface
+ *
+ * @intf: pointer to &media_interface
+ *
+ * Note: this is an unlocked version of media_remove_intf_links().
+ */
+void __media_remove_intf_links(struct media_interface *intf);
+
+/**
+ * media_remove_intf_links() - remove all links associated with an interface
+ *
+ * @intf: pointer to &media_interface
+ *
+ * Notes:
+ *
+ * this is called automatically when an entity is unregistered via
+ * media_device_register_entity() and by media_devnode_remove().
+ *
+ * Prefer to use this one, instead of __media_remove_intf_links().
+ */
+void media_remove_intf_links(struct media_interface *intf);
+
#define media_entity_call(entity, operation, args...) \
(((entity)->ops && (entity)->ops->operation) ? \
(entity)->ops->operation((entity) , ##args) : -ENOIOCTLCMD)
diff --git a/include/media/rc-core.h b/include/media/rc-core.h
index ec921f6538c7..f6494709e230 100644
--- a/include/media/rc-core.h
+++ b/include/media/rc-core.h
@@ -239,6 +239,7 @@ static inline void init_ir_raw_event(struct ir_raw_event *ev)
memset(ev, 0, sizeof(*ev));
}
+#define IR_DEFAULT_TIMEOUT MS_TO_NS(125)
#define IR_MAX_DURATION 500000000 /* 500 ms */
#define US_TO_NS(usec) ((usec) * 1000)
#define MS_TO_US(msec) ((msec) * 1000)
diff --git a/include/media/rc-map.h b/include/media/rc-map.h
index 7c4bbc4dfab4..7844e9879497 100644
--- a/include/media/rc-map.h
+++ b/include/media/rc-map.h
@@ -33,26 +33,26 @@ enum rc_type {
RC_TYPE_XMP = 18, /* XMP protocol */
};
-#define RC_BIT_NONE 0
-#define RC_BIT_UNKNOWN (1 << RC_TYPE_UNKNOWN)
-#define RC_BIT_OTHER (1 << RC_TYPE_OTHER)
-#define RC_BIT_RC5 (1 << RC_TYPE_RC5)
-#define RC_BIT_RC5X (1 << RC_TYPE_RC5X)
-#define RC_BIT_RC5_SZ (1 << RC_TYPE_RC5_SZ)
-#define RC_BIT_JVC (1 << RC_TYPE_JVC)
-#define RC_BIT_SONY12 (1 << RC_TYPE_SONY12)
-#define RC_BIT_SONY15 (1 << RC_TYPE_SONY15)
-#define RC_BIT_SONY20 (1 << RC_TYPE_SONY20)
-#define RC_BIT_NEC (1 << RC_TYPE_NEC)
-#define RC_BIT_SANYO (1 << RC_TYPE_SANYO)
-#define RC_BIT_MCE_KBD (1 << RC_TYPE_MCE_KBD)
-#define RC_BIT_RC6_0 (1 << RC_TYPE_RC6_0)
-#define RC_BIT_RC6_6A_20 (1 << RC_TYPE_RC6_6A_20)
-#define RC_BIT_RC6_6A_24 (1 << RC_TYPE_RC6_6A_24)
-#define RC_BIT_RC6_6A_32 (1 << RC_TYPE_RC6_6A_32)
-#define RC_BIT_RC6_MCE (1 << RC_TYPE_RC6_MCE)
-#define RC_BIT_SHARP (1 << RC_TYPE_SHARP)
-#define RC_BIT_XMP (1 << RC_TYPE_XMP)
+#define RC_BIT_NONE 0ULL
+#define RC_BIT_UNKNOWN (1ULL << RC_TYPE_UNKNOWN)
+#define RC_BIT_OTHER (1ULL << RC_TYPE_OTHER)
+#define RC_BIT_RC5 (1ULL << RC_TYPE_RC5)
+#define RC_BIT_RC5X (1ULL << RC_TYPE_RC5X)
+#define RC_BIT_RC5_SZ (1ULL << RC_TYPE_RC5_SZ)
+#define RC_BIT_JVC (1ULL << RC_TYPE_JVC)
+#define RC_BIT_SONY12 (1ULL << RC_TYPE_SONY12)
+#define RC_BIT_SONY15 (1ULL << RC_TYPE_SONY15)
+#define RC_BIT_SONY20 (1ULL << RC_TYPE_SONY20)
+#define RC_BIT_NEC (1ULL << RC_TYPE_NEC)
+#define RC_BIT_SANYO (1ULL << RC_TYPE_SANYO)
+#define RC_BIT_MCE_KBD (1ULL << RC_TYPE_MCE_KBD)
+#define RC_BIT_RC6_0 (1ULL << RC_TYPE_RC6_0)
+#define RC_BIT_RC6_6A_20 (1ULL << RC_TYPE_RC6_6A_20)
+#define RC_BIT_RC6_6A_24 (1ULL << RC_TYPE_RC6_6A_24)
+#define RC_BIT_RC6_6A_32 (1ULL << RC_TYPE_RC6_6A_32)
+#define RC_BIT_RC6_MCE (1ULL << RC_TYPE_RC6_MCE)
+#define RC_BIT_SHARP (1ULL << RC_TYPE_SHARP)
+#define RC_BIT_XMP (1ULL << RC_TYPE_XMP)
#define RC_BIT_ALL (RC_BIT_UNKNOWN | RC_BIT_OTHER | \
RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ | \
diff --git a/include/media/tuner.h b/include/media/tuner.h
index 486b6a54363b..e5321fda5489 100644
--- a/include/media/tuner.h
+++ b/include/media/tuner.h
@@ -21,6 +21,14 @@
#include <linux/videodev2.h>
+/* Tuner PADs */
+/* FIXME: is this the right place for it? */
+enum tuner_pad_index {
+ TUNER_PAD_RF_INPUT,
+ TUNER_PAD_IF_OUTPUT,
+ TUNER_NUM_PADS
+};
+
#define ADDR_UNSET (255)
#define TUNER_TEMIC_PAL 0 /* 4002 FH5 (3X 7756, 9483) */
diff --git a/include/media/v4l2-clk.h b/include/media/v4l2-clk.h
index 3ef6e3d5ed6c..2b94662d005c 100644
--- a/include/media/v4l2-clk.h
+++ b/include/media/v4l2-clk.h
@@ -65,7 +65,12 @@ static inline struct v4l2_clk *v4l2_clk_register_fixed(const char *dev_id,
return __v4l2_clk_register_fixed(dev_id, rate, THIS_MODULE);
}
+#define V4L2_CLK_NAME_SIZE 64
+
#define v4l2_clk_name_i2c(name, size, adap, client) snprintf(name, size, \
"%d-%04x", adap, client)
+#define v4l2_clk_name_of(name, size, of_full_name) snprintf(name, size, \
+ "of-%s", of_full_name)
+
#endif
diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
index acbcd2f5fe7f..eeabf20e87a6 100644
--- a/include/media/v4l2-dev.h
+++ b/include/media/v4l2-dev.h
@@ -86,6 +86,7 @@ struct video_device
{
#if defined(CONFIG_MEDIA_CONTROLLER)
struct media_entity entity;
+ struct media_intf_devnode *intf_devnode;
#endif
/* device ops */
const struct v4l2_file_operations *fops;
diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h
index a209526b6014..1113c8874c26 100644
--- a/include/media/v4l2-dv-timings.h
+++ b/include/media/v4l2-dv-timings.h
@@ -107,12 +107,14 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t,
* @standard: the timings according to the standard.
* @pclock_delta: maximum delta in Hz between standard->pixelclock and
* the measured timings.
+ * @match_reduced_fps: if true, then fail if V4L2_DV_FL_REDUCED_FPS does not
+ * match.
*
* Returns true if the two timings match, returns false otherwise.
*/
bool v4l2_match_dv_timings(const struct v4l2_dv_timings *measured,
const struct v4l2_dv_timings *standard,
- unsigned pclock_delta);
+ unsigned pclock_delta, bool match_reduced_fps);
/**
* v4l2_print_dv_timings() - log the contents of a dv_timings struct
@@ -183,4 +185,25 @@ bool v4l2_detect_gtf(unsigned frame_height, unsigned hfreq, unsigned vsync,
*/
struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait);
+/*
+ * reduce_fps - check if conditions for reduced fps are true.
+ * bt - v4l2 timing structure
+ * For different timings reduced fps is allowed if following conditions
+ * are met -
+ * For CVT timings: if reduced blanking v2 (vsync == 8) is true.
+ * For CEA861 timings: if V4L2_DV_FL_CAN_REDUCE_FPS flag is true.
+ */
+static inline bool can_reduce_fps(struct v4l2_bt_timings *bt)
+{
+ if ((bt->standards & V4L2_DV_BT_STD_CVT) && (bt->vsync == 8))
+ return true;
+
+ if ((bt->standards & V4L2_DV_BT_STD_CEA861) &&
+ (bt->flags & V4L2_DV_FL_CAN_REDUCE_FPS))
+ return true;
+
+ return false;
+}
+
+
#endif
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index 647ebfe5174f..ef03ae56b1c1 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -129,6 +129,8 @@ struct vb2_mem_ops {
* @dbuf_mapped: flag to show whether dbuf is mapped or not
* @bytesused: number of bytes occupied by data in the plane (payload)
* @length: size of this plane (NOT the payload) in bytes
+ * @min_length: minimum required size of this plane (NOT the payload) in bytes.
+ * @length is always greater or equal to @min_length.
* @offset: when memory in the associated struct vb2_buffer is
* VB2_MEMORY_MMAP, equals the offset from the start of
* the device memory for this plane (or is a "cookie" that
@@ -150,6 +152,7 @@ struct vb2_plane {
unsigned int dbuf_mapped;
unsigned int bytesused;
unsigned int length;
+ unsigned int min_length;
union {
unsigned int offset;
unsigned long userptr;
@@ -211,6 +214,7 @@ struct vb2_queue;
* @num_planes: number of planes in the buffer
* on an internal driver queue
* @planes: private per-plane information; do not change
+ * @timestamp: frame timestamp in ns
*/
struct vb2_buffer {
struct vb2_queue *vb2_queue;
@@ -219,6 +223,7 @@ struct vb2_buffer {
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VB2_MAX_PLANES];
+ u64 timestamp;
/* private: internal use only
*
@@ -268,21 +273,26 @@ struct vb2_buffer {
* struct vb2_ops - driver-specific callbacks
*
* @queue_setup: called from VIDIOC_REQBUFS and VIDIOC_CREATE_BUFS
- * handlers before memory allocation, or, if
- * *num_planes != 0, after the allocation to verify a
- * smaller number of buffers. Driver should return
- * the required number of buffers in *num_buffers, the
- * required number of planes per buffer in *num_planes; the
- * size of each plane should be set in the sizes[] array
- * and optional per-plane allocator specific context in the
- * alloc_ctxs[] array. When called from VIDIOC_REQBUFS,
- * fmt == NULL, the driver has to use the currently
- * configured format and *num_buffers is the total number
- * of buffers, that are being allocated. When called from
- * VIDIOC_CREATE_BUFS, fmt != NULL and it describes the
- * target frame format (if the format isn't valid the
- * callback must return -EINVAL). In this case *num_buffers
- * are being allocated additionally to q->num_buffers.
+ * handlers before memory allocation. It can be called
+ * twice: if the original number of requested buffers
+ * could not be allocated, then it will be called a
+ * second time with the actually allocated number of
+ * buffers to verify if that is OK.
+ * The driver should return the required number of buffers
+ * in *num_buffers, the required number of planes per
+ * buffer in *num_planes, the size of each plane should be
+ * set in the sizes[] array and optional per-plane
+ * allocator specific context in the alloc_ctxs[] array.
+ * When called from VIDIOC_REQBUFS, *num_planes == 0, the
+ * driver has to use the currently configured format to
+ * determine the plane sizes and *num_buffers is the total
+ * number of buffers that are being allocated. When called
+ * from VIDIOC_CREATE_BUFS, *num_planes != 0 and it
+ * describes the requested number of planes and sizes[]
+ * contains the requested plane sizes. If either
+ * *num_planes or the requested sizes are invalid callback
+ * must return -EINVAL. In this case *num_buffers are
+ * being allocated additionally to q->num_buffers.
* @wait_prepare: release any locks taken while calling vb2 functions;
* it is called before an ioctl needs to wait for a new
* buffer to arrive; required to avoid a deadlock in
@@ -344,7 +354,7 @@ struct vb2_buffer {
* pre-queued buffers before calling STREAMON.
*/
struct vb2_ops {
- int (*queue_setup)(struct vb2_queue *q, const void *parg,
+ int (*queue_setup)(struct vb2_queue *q,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], void *alloc_ctxs[]);
@@ -362,11 +372,22 @@ struct vb2_ops {
void (*buf_queue)(struct vb2_buffer *vb);
};
+/**
+ * struct vb2_ops - driver-specific callbacks
+ *
+ * @fill_user_buffer: given a vb2_buffer fill in the userspace structure.
+ * For V4L2 this is a struct v4l2_buffer.
+ * @fill_vb2_buffer: given a userspace structure, fill in the vb2_buffer.
+ * If the userspace structure is invalid, then this op
+ * will return an error.
+ * @copy_timestamp: copy the timestamp from a userspace structure to
+ * the vb2_buffer struct.
+ */
struct vb2_buf_ops {
- int (*fill_user_buffer)(struct vb2_buffer *vb, void *pb);
+ void (*fill_user_buffer)(struct vb2_buffer *vb, void *pb);
int (*fill_vb2_buffer)(struct vb2_buffer *vb, const void *pb,
struct vb2_plane *planes);
- int (*set_timestamp)(struct vb2_buffer *vb, const void *pb);
+ void (*copy_timestamp)(struct vb2_buffer *vb, const void *pb);
};
/**
@@ -429,6 +450,7 @@ struct vb2_buf_ops {
* called since poll() needs to return POLLERR in that situation.
* @is_multiplanar: set if buffer type is multiplanar
* @is_output: set if buffer type is output
+ * @copy_timestamp: set if vb2-core should set timestamps
* @last_buffer_dequeued: used in poll() and DQBUF to immediately return if the
* last decoded buffer was already dequeued. Set for capture queues
* when a buffer with the V4L2_BUF_FLAG_LAST is dequeued.
@@ -470,7 +492,6 @@ struct vb2_queue {
wait_queue_head_t done_wq;
void *alloc_ctx[VB2_MAX_PLANES];
- unsigned int plane_sizes[VB2_MAX_PLANES];
unsigned int streaming:1;
unsigned int start_streaming_called:1;
@@ -478,6 +499,7 @@ struct vb2_queue {
unsigned int waiting_for_buffers:1;
unsigned int is_multiplanar:1;
unsigned int is_output:1;
+ unsigned int copy_timestamp:1;
unsigned int last_buffer_dequeued:1;
struct vb2_fileio_data *fileio;
@@ -503,11 +525,12 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state);
void vb2_discard_done(struct vb2_queue *q);
int vb2_wait_for_all_buffers(struct vb2_queue *q);
-int vb2_core_querybuf(struct vb2_queue *q, unsigned int index, void *pb);
+void vb2_core_querybuf(struct vb2_queue *q, unsigned int index, void *pb);
int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory,
unsigned int *count);
int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory,
- unsigned int *count, const void *parg);
+ unsigned int *count, unsigned requested_planes,
+ const unsigned int requested_sizes[]);
int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb);
int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb);
int vb2_core_dqbuf(struct vb2_queue *q, void *pb, bool nonblocking);
@@ -531,6 +554,42 @@ unsigned long vb2_get_unmapped_area(struct vb2_queue *q,
unsigned long pgoff,
unsigned long flags);
#endif
+unsigned int vb2_core_poll(struct vb2_queue *q, struct file *file,
+ poll_table *wait);
+size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
+ loff_t *ppos, int nonblock);
+size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
+ loff_t *ppos, int nonblock);
+
+/*
+ * vb2_thread_fnc - callback function for use with vb2_thread
+ *
+ * This is called whenever a buffer is dequeued in the thread.
+ */
+typedef int (*vb2_thread_fnc)(struct vb2_buffer *vb, void *priv);
+
+/**
+ * vb2_thread_start() - start a thread for the given queue.
+ * @q: videobuf queue
+ * @fnc: callback function
+ * @priv: priv pointer passed to the callback function
+ * @thread_name:the name of the thread. This will be prefixed with "vb2-".
+ *
+ * This starts a thread that will queue and dequeue until an error occurs
+ * or @vb2_thread_stop is called.
+ *
+ * This function should not be used for anything else but the videobuf2-dvb
+ * support. If you think you have another good use-case for this, then please
+ * contact the linux-media mailinglist first.
+ */
+int vb2_thread_start(struct vb2_queue *q, vb2_thread_fnc fnc, void *priv,
+ const char *thread_name);
+
+/**
+ * vb2_thread_stop() - stop the thread for the given queue.
+ * @q: videobuf queue
+ */
+int vb2_thread_stop(struct vb2_queue *q);
/**
* vb2_is_streaming() - return streaming status of the queue
@@ -635,4 +694,11 @@ static inline void vb2_clear_last_buffer_dequeued(struct vb2_queue *q)
q->last_buffer_dequeued = false;
}
+/*
+ * The following functions are not part of the vb2 core API, but are useful
+ * functions for videobuf2-*.
+ */
+bool vb2_buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb);
+int vb2_verify_memory_type(struct vb2_queue *q,
+ enum vb2_memory memory, unsigned int type);
#endif /* _MEDIA_VIDEOBUF2_CORE_H */
diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h
index 5abab1e7c7e8..3cc836f76675 100644
--- a/include/media/videobuf2-v4l2.h
+++ b/include/media/videobuf2-v4l2.h
@@ -28,7 +28,6 @@
* @vb2_buf: video buffer 2
* @flags: buffer informational flags
* @field: enum v4l2_field; field order of the image in the buffer
- * @timestamp: frame timestamp
* @timecode: frame timecode
* @sequence: sequence count of this frame
* Should contain enough information to be able to cover all the fields
@@ -39,7 +38,6 @@ struct vb2_v4l2_buffer {
__u32 flags;
__u32 field;
- struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
@@ -65,42 +63,8 @@ int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type);
int __must_check vb2_queue_init(struct vb2_queue *q);
void vb2_queue_release(struct vb2_queue *q);
-
-unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait);
-size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
- loff_t *ppos, int nonblock);
-size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
- loff_t *ppos, int nonblock);
-
-/*
- * vb2_thread_fnc - callback function for use with vb2_thread
- *
- * This is called whenever a buffer is dequeued in the thread.
- */
-typedef int (*vb2_thread_fnc)(struct vb2_buffer *vb, void *priv);
-
-/**
- * vb2_thread_start() - start a thread for the given queue.
- * @q: videobuf queue
- * @fnc: callback function
- * @priv: priv pointer passed to the callback function
- * @thread_name:the name of the thread. This will be prefixed with "vb2-".
- *
- * This starts a thread that will queue and dequeue until an error occurs
- * or @vb2_thread_stop is called.
- *
- * This function should not be used for anything else but the videobuf2-dvb
- * support. If you think you have another good use-case for this, then please
- * contact the linux-media mailinglist first.
- */
-int vb2_thread_start(struct vb2_queue *q, vb2_thread_fnc fnc, void *priv,
- const char *thread_name);
-
-/**
- * vb2_thread_stop() - stop the thread for the given queue.
- * @q: videobuf queue
- */
-int vb2_thread_stop(struct vb2_queue *q);
+unsigned int vb2_poll(struct vb2_queue *q, struct file *file,
+ poll_table *wait);
/*
* The following functions are not part of the vb2 core API, but are simple
diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
index cf3bc564ac03..2f6a3f2233ed 100644
--- a/include/net/6lowpan.h
+++ b/include/net/6lowpan.h
@@ -53,6 +53,8 @@
#ifndef __6LOWPAN_H__
#define __6LOWPAN_H__
+#include <linux/debugfs.h>
+
#include <net/ipv6.h>
#include <net/net_namespace.h>
@@ -98,6 +100,7 @@ enum lowpan_lltypes {
struct lowpan_priv {
enum lowpan_lltypes lltype;
+ struct dentry *iface_debugfs;
/* must be last */
u8 priv[0] __aligned(sizeof(void *));
@@ -185,7 +188,12 @@ static inline void lowpan_push_hc_data(u8 **hc_ptr, const void *data,
*hc_ptr += len;
}
-void lowpan_netdev_setup(struct net_device *dev, enum lowpan_lltypes lltype);
+int lowpan_register_netdevice(struct net_device *dev,
+ enum lowpan_lltypes lltype);
+int lowpan_register_netdev(struct net_device *dev,
+ enum lowpan_lltypes lltype);
+void lowpan_unregister_netdevice(struct net_device *dev);
+void lowpan_unregister_netdev(struct net_device *dev);
/**
* lowpan_header_decompress - replace 6LoWPAN header with IPv6 header
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 78003dfb8539..47f52d3cd8df 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -87,7 +87,8 @@ int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr,
u32 banned_flags);
int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
u32 banned_flags);
-int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2);
+int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2,
+ bool match_wildcard);
void addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr);
void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr);
diff --git a/include/net/af_unix.h b/include/net/af_unix.h
index b36d837c701e..2a91a0561a47 100644
--- a/include/net/af_unix.h
+++ b/include/net/af_unix.h
@@ -62,6 +62,7 @@ struct unix_sock {
#define UNIX_GC_CANDIDATE 0
#define UNIX_GC_MAYBE_CYCLE 1
struct socket_wq peer_wq;
+ wait_queue_t peer_wake;
};
static inline struct unix_sock *unix_sk(const struct sock *sk)
diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index 42844d7b154a..bfd1590821d6 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -29,6 +29,8 @@
#include <net/sock.h>
#include <linux/seq_file.h>
+#define BT_SUBSYS_VERSION "2.21"
+
#ifndef AF_BLUETOOTH
#define AF_BLUETOOTH 31
#define PF_BLUETOOTH AF_BLUETOOTH
@@ -296,12 +298,17 @@ typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status, u16 opcode);
typedef void (*hci_req_complete_skb_t)(struct hci_dev *hdev, u8 status,
u16 opcode, struct sk_buff *skb);
+#define HCI_REQ_START BIT(0)
+#define HCI_REQ_SKB BIT(1)
+
struct hci_ctrl {
__u16 opcode;
- bool req_start;
+ u8 req_flags;
u8 req_event;
- hci_req_complete_t req_complete;
- hci_req_complete_skb_t req_complete_skb;
+ union {
+ hci_req_complete_t req_complete;
+ hci_req_complete_skb_t req_complete_skb;
+ };
};
struct bt_skb_cb {
@@ -316,15 +323,17 @@ struct bt_skb_cb {
};
#define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb))
+#define hci_skb_pkt_type(skb) bt_cb((skb))->pkt_type
+#define hci_skb_expect(skb) bt_cb((skb))->expect
+#define hci_skb_opcode(skb) bt_cb((skb))->hci.opcode
+
static inline struct sk_buff *bt_skb_alloc(unsigned int len, gfp_t how)
{
struct sk_buff *skb;
skb = alloc_skb(len + BT_SKB_RESERVE, how);
- if (skb) {
+ if (skb)
skb_reserve(skb, BT_SKB_RESERVE);
- bt_cb(skb)->incoming = 0;
- }
return skb;
}
@@ -334,10 +343,8 @@ static inline struct sk_buff *bt_skb_send_alloc(struct sock *sk,
struct sk_buff *skb;
skb = sock_alloc_send_skb(sk, len + BT_SKB_RESERVE, nb, err);
- if (skb) {
+ if (skb)
skb_reserve(skb, BT_SKB_RESERVE);
- bt_cb(skb)->incoming = 0;
- }
if (!skb && *err)
return NULL;
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 0205b80cc90b..339ea57be423 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -239,7 +239,6 @@ enum {
HCI_LE_ENABLED,
HCI_ADVERTISING,
HCI_ADVERTISING_CONNECTABLE,
- HCI_ADVERTISING_INSTANCE,
HCI_CONNECTABLE,
HCI_DISCOVERABLE,
HCI_LIMITED_DISCOVERABLE,
@@ -452,7 +451,8 @@ enum {
#define HCI_ERROR_REMOTE_POWER_OFF 0x15
#define HCI_ERROR_LOCAL_HOST_TERM 0x16
#define HCI_ERROR_PAIRING_NOT_ALLOWED 0x18
-#define HCI_ERROR_INVALID_LL_PARAMS 0x1E
+#define HCI_ERROR_INVALID_LL_PARAMS 0x1e
+#define HCI_ERROR_UNSPECIFIED 0x1f
#define HCI_ERROR_ADVERTISING_TIMEOUT 0x3c
/* Flow control modes */
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 1878d0a96333..d4f82edb5cff 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -77,6 +77,7 @@ struct discovery_state {
u8 last_adv_data_len;
bool report_invalid_rssi;
bool result_filtering;
+ bool limited;
s8 rssi;
u16 uuid_count;
u8 (*uuids)[16];
@@ -327,6 +328,14 @@ struct hci_dev {
struct work_struct cmd_work;
struct work_struct tx_work;
+ struct work_struct discov_update;
+ struct work_struct bg_scan_update;
+ struct work_struct scan_update;
+ struct work_struct connectable_update;
+ struct work_struct discoverable_update;
+ struct delayed_work le_scan_disable;
+ struct delayed_work le_scan_restart;
+
struct sk_buff_head rx_q;
struct sk_buff_head raw_q;
struct sk_buff_head cmd_q;
@@ -370,9 +379,6 @@ struct hci_dev {
DECLARE_BITMAP(dev_flags, __HCI_NUM_FLAGS);
- struct delayed_work le_scan_disable;
- struct delayed_work le_scan_restart;
-
__s8 adv_tx_power;
__u8 adv_data[HCI_MAX_AD_LENGTH];
__u8 adv_data_len;
@@ -875,7 +881,7 @@ struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle);
struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
u8 dst_type, u8 sec_level,
- u16 conn_timeout, u8 role);
+ u16 conn_timeout);
struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
u8 dst_type, u8 sec_level, u16 conn_timeout,
u8 role);
@@ -1036,7 +1042,6 @@ struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev,
struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev,
bdaddr_t *addr, u8 addr_type);
void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type);
-void hci_conn_params_clear_all(struct hci_dev *hdev);
void hci_conn_params_clear_disabled(struct hci_dev *hdev);
struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list,
@@ -1279,31 +1284,41 @@ static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status,
mutex_unlock(&hci_cb_list_lock);
}
-static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type)
+static inline void *eir_get_data(u8 *eir, size_t eir_len, u8 type,
+ size_t *data_len)
{
size_t parsed = 0;
- if (data_len < 2)
- return false;
+ if (eir_len < 2)
+ return NULL;
- while (parsed < data_len - 1) {
- u8 field_len = data[0];
+ while (parsed < eir_len - 1) {
+ u8 field_len = eir[0];
if (field_len == 0)
break;
parsed += field_len + 1;
- if (parsed > data_len)
+ if (parsed > eir_len)
break;
- if (data[1] == type)
- return true;
+ if (eir[1] != type) {
+ eir += field_len + 1;
+ continue;
+ }
+
+ /* Zero length data */
+ if (field_len == 1)
+ return NULL;
- data += field_len + 1;
+ if (data_len)
+ *data_len = field_len - 1;
+
+ return &eir[2];
}
- return false;
+ return NULL;
}
static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type)
@@ -1431,10 +1446,8 @@ int mgmt_new_settings(struct hci_dev *hdev);
void mgmt_index_added(struct hci_dev *hdev);
void mgmt_index_removed(struct hci_dev *hdev);
void mgmt_set_powered_failed(struct hci_dev *hdev, int err);
-int mgmt_powered(struct hci_dev *hdev, u8 powered);
-int mgmt_update_adv_data(struct hci_dev *hdev);
-void mgmt_discoverable_timeout(struct hci_dev *hdev);
-void mgmt_adv_timeout_expired(struct hci_dev *hdev);
+void mgmt_power_on(struct hci_dev *hdev, int err);
+void __mgmt_power_off(struct hci_dev *hdev);
void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
bool persistent);
void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
@@ -1473,6 +1486,8 @@ void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
u8 status);
void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
+void mgmt_start_discovery_complete(struct hci_dev *hdev, u8 status);
+void mgmt_stop_discovery_complete(struct hci_dev *hdev, u8 status);
void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len);
@@ -1487,8 +1502,15 @@ void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk,
void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 bdaddr_type, u8 store_hint, u16 min_interval,
u16 max_interval, u16 latency, u16 timeout);
-void mgmt_reenable_advertising(struct hci_dev *hdev);
void mgmt_smp_complete(struct hci_conn *conn, bool complete);
+bool mgmt_get_connectable(struct hci_dev *hdev);
+void mgmt_set_connectable_complete(struct hci_dev *hdev, u8 status);
+void mgmt_set_discoverable_complete(struct hci_dev *hdev, u8 status);
+u8 mgmt_get_adv_discov_flags(struct hci_dev *hdev);
+void mgmt_advertising_added(struct sock *sk, struct hci_dev *hdev,
+ u8 instance);
+void mgmt_advertising_removed(struct sock *sk, struct hci_dev *hdev,
+ u8 instance);
u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
u16 to_multiplier);
diff --git a/include/net/bluetooth/hci_mon.h b/include/net/bluetooth/hci_mon.h
index 2b67567cf28d..587d0131b349 100644
--- a/include/net/bluetooth/hci_mon.h
+++ b/include/net/bluetooth/hci_mon.h
@@ -43,6 +43,8 @@ struct hci_mon_hdr {
#define HCI_MON_CLOSE_INDEX 9
#define HCI_MON_INDEX_INFO 10
#define HCI_MON_VENDOR_DIAG 11
+#define HCI_MON_SYSTEM_NOTE 12
+#define HCI_MON_USER_LOGGING 13
struct hci_mon_new_index {
__u8 type;
diff --git a/include/net/bluetooth/hci_sock.h b/include/net/bluetooth/hci_sock.h
index 9a46d665c1b5..8e9138acdae1 100644
--- a/include/net/bluetooth/hci_sock.h
+++ b/include/net/bluetooth/hci_sock.h
@@ -45,6 +45,7 @@ struct sockaddr_hci {
#define HCI_CHANNEL_USER 1
#define HCI_CHANNEL_MONITOR 2
#define HCI_CHANNEL_CONTROL 3
+#define HCI_CHANNEL_LOGGING 4
struct hci_filter {
unsigned long type_mask;
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index b831242d48a4..ea73e0826aa7 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -571,6 +571,21 @@ struct mgmt_rp_remove_advertising {
__u8 instance;
} __packed;
+#define MGMT_OP_GET_ADV_SIZE_INFO 0x0040
+struct mgmt_cp_get_adv_size_info {
+ __u8 instance;
+ __le32 flags;
+} __packed;
+#define MGMT_GET_ADV_SIZE_INFO_SIZE 5
+struct mgmt_rp_get_adv_size_info {
+ __u8 instance;
+ __le32 flags;
+ __u8 max_adv_data_len;
+ __u8 max_scan_rsp_len;
+} __packed;
+
+#define MGMT_OP_START_LIMITED_DISCOVERY 0x0041
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
diff --git a/include/net/bonding.h b/include/net/bonding.h
index c1740a2794a3..ee6c52053aa3 100644
--- a/include/net/bonding.h
+++ b/include/net/bonding.h
@@ -165,7 +165,8 @@ struct slave {
u8 backup:1, /* indicates backup slave. Value corresponds with
BOND_STATE_ACTIVE and BOND_STATE_BACKUP */
inactive:1, /* indicates inactive slave */
- should_notify:1; /* indicateds whether the state changed */
+ should_notify:1, /* indicates whether the state changed */
+ should_notify_link:1; /* indicates whether the link changed */
u8 duplex;
u32 original_mtu;
u32 link_failure_count;
@@ -246,6 +247,7 @@ struct bonding {
((struct slave *) rtnl_dereference(dev->rx_handler_data))
void bond_queue_slave_event(struct slave *slave);
+void bond_lower_state_changed(struct slave *slave);
struct bond_vlan_tag {
__be16 vlan_proto;
@@ -327,6 +329,7 @@ static inline void bond_set_active_slave(struct slave *slave)
if (slave->backup) {
slave->backup = 0;
bond_queue_slave_event(slave);
+ bond_lower_state_changed(slave);
rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_ATOMIC);
}
}
@@ -336,6 +339,7 @@ static inline void bond_set_backup_slave(struct slave *slave)
if (!slave->backup) {
slave->backup = 1;
bond_queue_slave_event(slave);
+ bond_lower_state_changed(slave);
rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_ATOMIC);
}
}
@@ -348,6 +352,7 @@ static inline void bond_set_slave_state(struct slave *slave,
slave->backup = slave_state;
if (notify) {
+ bond_lower_state_changed(slave);
rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_ATOMIC);
bond_queue_slave_event(slave);
slave->should_notify = 0;
@@ -379,6 +384,7 @@ static inline void bond_slave_state_notify(struct bonding *bond)
bond_for_each_slave(bond, tmp, iter) {
if (tmp->should_notify) {
+ bond_lower_state_changed(tmp);
rtmsg_ifinfo(RTM_NEWLINK, tmp->dev, 0, GFP_ATOMIC);
tmp->should_notify = 0;
}
@@ -504,10 +510,37 @@ static inline bool bond_is_slave_inactive(struct slave *slave)
return slave->inactive;
}
-static inline void bond_set_slave_link_state(struct slave *slave, int state)
+static inline void bond_set_slave_link_state(struct slave *slave, int state,
+ bool notify)
{
+ if (slave->link == state)
+ return;
+
slave->link = state;
- bond_queue_slave_event(slave);
+ if (notify) {
+ bond_queue_slave_event(slave);
+ bond_lower_state_changed(slave);
+ slave->should_notify_link = 0;
+ } else {
+ if (slave->should_notify_link)
+ slave->should_notify_link = 0;
+ else
+ slave->should_notify_link = 1;
+ }
+}
+
+static inline void bond_slave_link_notify(struct bonding *bond)
+{
+ struct list_head *iter;
+ struct slave *tmp;
+
+ bond_for_each_slave(bond, tmp, iter) {
+ if (tmp->should_notify_link) {
+ bond_queue_slave_event(tmp);
+ bond_lower_state_changed(tmp);
+ tmp->should_notify_link = 0;
+ }
+ }
}
static inline __be32 bond_confirm_addr(struct net_device *dev, __be32 dst, __be32 local)
diff --git a/include/net/busy_poll.h b/include/net/busy_poll.h
index 1d67fb6b23a0..2fbeb1313c0f 100644
--- a/include/net/busy_poll.h
+++ b/include/net/busy_poll.h
@@ -72,50 +72,7 @@ static inline bool busy_loop_timeout(unsigned long end_time)
return time_after(now, end_time);
}
-/* when used in sock_poll() nonblock is known at compile time to be true
- * so the loop and end_time will be optimized out
- */
-static inline bool sk_busy_loop(struct sock *sk, int nonblock)
-{
- unsigned long end_time = !nonblock ? sk_busy_loop_end_time(sk) : 0;
- const struct net_device_ops *ops;
- struct napi_struct *napi;
- int rc = false;
-
- /*
- * rcu read lock for napi hash
- * bh so we don't race with net_rx_action
- */
- rcu_read_lock_bh();
-
- napi = napi_by_id(sk->sk_napi_id);
- if (!napi)
- goto out;
-
- ops = napi->dev->netdev_ops;
- if (!ops->ndo_busy_poll)
- goto out;
-
- do {
- rc = ops->ndo_busy_poll(napi);
-
- if (rc == LL_FLUSH_FAILED)
- break; /* permanent failure */
-
- if (rc > 0)
- /* local bh are disabled so it is ok to use _BH */
- NET_ADD_STATS_BH(sock_net(sk),
- LINUX_MIB_BUSYPOLLRXPACKETS, rc);
- cpu_relax();
-
- } while (!nonblock && skb_queue_empty(&sk->sk_receive_queue) &&
- !need_resched() && !busy_loop_timeout(end_time));
-
- rc = !skb_queue_empty(&sk->sk_receive_queue);
-out:
- rcu_read_unlock_bh();
- return rc;
-}
+bool sk_busy_loop(struct sock *sk, int nonblock);
/* used in the NIC receive handler to mark the skb */
static inline void skb_mark_napi_id(struct sk_buff *skb,
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 2c7bdb81d30c..9bcaaf7cd15a 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -2321,6 +2321,8 @@ struct cfg80211_qos_map {
* the driver, and will be valid until passed to cfg80211_scan_done().
* For scan results, call cfg80211_inform_bss(); you can call this outside
* the scan/scan_done bracket too.
+ * @abort_scan: Tell the driver to abort an ongoing scan. The driver shall
+ * indicate the status of the scan through cfg80211_scan_done().
*
* @auth: Request to authenticate with the specified peer
* (invoked with the wireless_dev mutex held)
@@ -2593,6 +2595,7 @@ struct cfg80211_ops {
int (*scan)(struct wiphy *wiphy,
struct cfg80211_scan_request *request);
+ void (*abort_scan)(struct wiphy *wiphy, struct wireless_dev *wdev);
int (*auth)(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_auth_request *req);
@@ -5173,8 +5176,11 @@ size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
* buffer starts, which may be @ielen if the entire (remainder)
* of the buffer should be used.
*/
-size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
- const u8 *ids, int n_ids, size_t offset);
+static inline size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
+ const u8 *ids, int n_ids, size_t offset)
+{
+ return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset);
+}
/**
* cfg80211_report_wowlan_wakeup - report wakeup from WoWLAN
diff --git a/include/net/checksum.h b/include/net/checksum.h
index 9fcaedf994ee..10a16b5bd1c7 100644
--- a/include/net/checksum.h
+++ b/include/net/checksum.h
@@ -165,7 +165,8 @@ static inline __wsum remcsum_adjust(void *ptr, __wsum csum,
csum = csum_sub(csum, csum_partial(ptr, start, 0));
/* Set derived checksum in packet */
- delta = csum_sub(csum_fold(csum), *psum);
+ delta = csum_sub((__force __wsum)csum_fold(csum),
+ (__force __wsum)*psum);
*psum = csum_fold(csum);
return delta;
diff --git a/include/net/cls_cgroup.h b/include/net/cls_cgroup.h
index ccd6d8bffa4d..c0a92e2c286d 100644
--- a/include/net/cls_cgroup.h
+++ b/include/net/cls_cgroup.h
@@ -41,13 +41,12 @@ static inline u32 task_cls_classid(struct task_struct *p)
return classid;
}
-static inline void sock_update_classid(struct sock *sk)
+static inline void sock_update_classid(struct sock_cgroup_data *skcd)
{
u32 classid;
classid = task_cls_classid(current);
- if (classid != sk->sk_classid)
- sk->sk_classid = classid;
+ sock_cgroup_set_classid(skcd, classid);
}
static inline u32 task_get_classid(const struct sk_buff *skb)
@@ -64,17 +63,17 @@ static inline u32 task_get_classid(const struct sk_buff *skb)
* softirqs always disables bh.
*/
if (in_serving_softirq()) {
- /* If there is an sk_classid we'll use that. */
+ /* If there is an sock_cgroup_classid we'll use that. */
if (!skb->sk)
return 0;
- classid = skb->sk->sk_classid;
+ classid = sock_cgroup_classid(&skb->sk->sk_cgrp_data);
}
return classid;
}
#else /* !CONFIG_CGROUP_NET_CLASSID */
-static inline void sock_update_classid(struct sock *sk)
+static inline void sock_update_classid(struct sock_cgroup_data *skcd)
{
}
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 82a4c6011173..26a0e86e611e 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -16,6 +16,7 @@
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <linux/of.h>
+#include <linux/of_gpio.h>
#include <linux/phy.h>
#include <linux/phy_fixed.h>
#include <linux/ethtool.h>
@@ -64,6 +65,13 @@ struct dsa_chip_data {
* NULL if there is only one switch chip.
*/
s8 *rtable;
+
+ /*
+ * A switch may have a GPIO line tied to its reset pin. Parse
+ * this from the device tree, and use it before performing
+ * switch soft reset.
+ */
+ struct gpio_desc *reset;
};
struct dsa_platform_data {
@@ -109,13 +117,6 @@ struct dsa_switch_tree {
s8 cpu_port;
/*
- * Link state polling.
- */
- int link_poll_needed;
- struct work_struct link_poll_work;
- struct timer_list link_poll_timer;
-
- /*
* Data for the individual switch chips.
*/
struct dsa_switch *ds[DSA_MAX_SWITCHES];
@@ -224,11 +225,6 @@ struct dsa_switch_driver {
int regnum, u16 val);
/*
- * Link state polling and IRQ handling.
- */
- void (*poll_link)(struct dsa_switch *ds);
-
- /*
* Link state adjustment (called from libphy)
*/
void (*adjust_link)(struct dsa_switch *ds, int port,
diff --git a/include/net/dst.h b/include/net/dst.h
index 1279f9b09791..c7329dcd90cc 100644
--- a/include/net/dst.h
+++ b/include/net/dst.h
@@ -322,6 +322,39 @@ static inline void skb_dst_force(struct sk_buff *skb)
}
}
+/**
+ * dst_hold_safe - Take a reference on a dst if possible
+ * @dst: pointer to dst entry
+ *
+ * This helper returns false if it could not safely
+ * take a reference on a dst.
+ */
+static inline bool dst_hold_safe(struct dst_entry *dst)
+{
+ if (dst->flags & DST_NOCACHE)
+ return atomic_inc_not_zero(&dst->__refcnt);
+ dst_hold(dst);
+ return true;
+}
+
+/**
+ * skb_dst_force_safe - makes sure skb dst is refcounted
+ * @skb: buffer
+ *
+ * If dst is not yet refcounted and not destroyed, grab a ref on it.
+ */
+static inline void skb_dst_force_safe(struct sk_buff *skb)
+{
+ if (skb_dst_is_noref(skb)) {
+ struct dst_entry *dst = skb_dst(skb);
+
+ if (!dst_hold_safe(dst))
+ dst = NULL;
+
+ skb->_skb_refdst = (unsigned long)dst;
+ }
+}
+
/**
* __skb_tunnel_rx - prepare skb for rx reinsert
diff --git a/include/net/genetlink.h b/include/net/genetlink.h
index 1b6b6dcb018d..43c0e771f417 100644
--- a/include/net/genetlink.h
+++ b/include/net/genetlink.h
@@ -114,6 +114,7 @@ static inline void genl_info_net_set(struct genl_info *info, struct net *net)
* @flags: flags
* @policy: attribute validation policy
* @doit: standard command callback
+ * @start: start callback for dumps
* @dumpit: callback for dumpers
* @done: completion callback for dumps
* @ops_list: operations list
@@ -122,6 +123,7 @@ struct genl_ops {
const struct nla_policy *policy;
int (*doit)(struct sk_buff *skb,
struct genl_info *info);
+ int (*start)(struct netlink_callback *cb);
int (*dumpit)(struct sk_buff *skb,
struct netlink_callback *cb);
int (*done)(struct netlink_callback *cb);
diff --git a/include/net/geneve.h b/include/net/geneve.h
index 3106ed6eae0d..e6c23dc765f7 100644
--- a/include/net/geneve.h
+++ b/include/net/geneve.h
@@ -62,6 +62,14 @@ struct genevehdr {
struct geneve_opt options[];
};
+#if IS_ENABLED(CONFIG_GENEVE)
+void geneve_get_rx_port(struct net_device *netdev);
+#else
+static inline void geneve_get_rx_port(struct net_device *netdev)
+{
+}
+#endif
+
#ifdef CONFIG_INET
struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
u8 name_assign_type, u16 dst_port);
diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h
index a62a051a3a2f..c4b31601cd53 100644
--- a/include/net/ieee802154_netdev.h
+++ b/include/net/ieee802154_netdev.h
@@ -337,7 +337,7 @@ struct ieee802154_mlme_ops {
void (*get_mac_params)(struct net_device *dev,
struct ieee802154_mac_params *params);
- struct ieee802154_llsec_ops *llsec;
+ const struct ieee802154_llsec_ops *llsec;
};
static inline struct ieee802154_mlme_ops *
diff --git a/include/net/ila.h b/include/net/ila.h
new file mode 100644
index 000000000000..9f4f43e94ae4
--- /dev/null
+++ b/include/net/ila.h
@@ -0,0 +1,18 @@
+/*
+ * ILA kernel interface
+ *
+ * Copyright (c) 2015 Tom Herbert <tom@herbertland.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#ifndef _NET_ILA_H
+#define _NET_ILA_H
+
+int ila_xlat_outgoing(struct sk_buff *skb);
+int ila_xlat_incoming(struct sk_buff *skb);
+
+#endif /* _NET_ILA_H */
diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h
index ac42bbb37b2d..12aac0fd6ee7 100644
--- a/include/net/inet_frag.h
+++ b/include/net/inet_frag.h
@@ -99,7 +99,6 @@ struct inet_frags {
void (*constructor)(struct inet_frag_queue *q,
const void *arg);
void (*destructor)(struct inet_frag_queue *);
- void (*skb_free)(struct sk_buff *);
void (*frag_expire)(unsigned long data);
struct kmem_cache *frags_cachep;
const char *frags_cache_name;
diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h
index 2134e6d815bc..012b1f91f3ec 100644
--- a/include/net/inet_sock.h
+++ b/include/net/inet_sock.h
@@ -28,6 +28,7 @@
#include <net/request_sock.h>
#include <net/netns/hash.h>
#include <net/tcp_states.h>
+#include <net/l3mdev.h>
/** struct ip_options - IP Options
*
@@ -113,6 +114,19 @@ static inline u32 inet_request_mark(const struct sock *sk, struct sk_buff *skb)
return sk->sk_mark;
}
+static inline int inet_request_bound_dev_if(const struct sock *sk,
+ struct sk_buff *skb)
+{
+#ifdef CONFIG_NET_L3_MASTER_DEV
+ struct net *net = sock_net(sk);
+
+ if (!sk->sk_bound_dev_if && net->ipv4.sysctl_tcp_l3mdev_accept)
+ return l3mdev_master_ifindex_by_index(net, skb->skb_iif);
+#endif
+
+ return sk->sk_bound_dev_if;
+}
+
struct inet_cork {
unsigned int flags;
__be32 addr;
@@ -210,18 +224,37 @@ struct inet_sock {
#define IP_CMSG_ORIGDSTADDR BIT(6)
#define IP_CMSG_CHECKSUM BIT(7)
-/* SYNACK messages might be attached to request sockets.
+/**
+ * sk_to_full_sk - Access to a full socket
+ * @sk: pointer to a socket
+ *
+ * SYNACK messages might be attached to request sockets.
* Some places want to reach the listener in this case.
*/
-static inline struct sock *skb_to_full_sk(const struct sk_buff *skb)
+static inline struct sock *sk_to_full_sk(struct sock *sk)
{
- struct sock *sk = skb->sk;
-
+#ifdef CONFIG_INET
if (sk && sk->sk_state == TCP_NEW_SYN_RECV)
sk = inet_reqsk(sk)->rsk_listener;
+#endif
return sk;
}
+/* sk_to_full_sk() variant with a const argument */
+static inline const struct sock *sk_const_to_full_sk(const struct sock *sk)
+{
+#ifdef CONFIG_INET
+ if (sk && sk->sk_state == TCP_NEW_SYN_RECV)
+ sk = ((const struct request_sock *)sk)->rsk_listener;
+#endif
+ return sk;
+}
+
+static inline struct sock *skb_to_full_sk(const struct sk_buff *skb)
+{
+ return sk_to_full_sk(skb->sk);
+}
+
static inline struct inet_sock *inet_sk(const struct sock *sk)
{
return (struct inet_sock *)sk;
diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h
index 4a6009d4486b..235c7811a86a 100644
--- a/include/net/inetpeer.h
+++ b/include/net/inetpeer.h
@@ -78,6 +78,7 @@ void inet_initpeers(void) __init;
static inline void inetpeer_set_addr_v4(struct inetpeer_addr *iaddr, __be32 ip)
{
iaddr->a4.addr = ip;
+ iaddr->a4.vif = 0;
iaddr->family = AF_INET;
}
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index 2bfb2ad2fab1..877f682989b8 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -133,27 +133,18 @@ void rt6_clean_tohost(struct net *net, struct in6_addr *gateway);
/*
* Store a destination cache entry in a socket
*/
-static inline void __ip6_dst_store(struct sock *sk, struct dst_entry *dst,
- const struct in6_addr *daddr,
- const struct in6_addr *saddr)
+static inline void ip6_dst_store(struct sock *sk, struct dst_entry *dst,
+ const struct in6_addr *daddr,
+ const struct in6_addr *saddr)
{
struct ipv6_pinfo *np = inet6_sk(sk);
- struct rt6_info *rt = (struct rt6_info *) dst;
+ np->dst_cookie = rt6_get_cookie((struct rt6_info *)dst);
sk_setup_caps(sk, dst);
np->daddr_cache = daddr;
#ifdef CONFIG_IPV6_SUBTREES
np->saddr_cache = saddr;
#endif
- np->dst_cookie = rt6_get_cookie(rt);
-}
-
-static inline void ip6_dst_store(struct sock *sk, struct dst_entry *dst,
- struct in6_addr *daddr, struct in6_addr *saddr)
-{
- spin_lock(&sk->sk_dst_lock);
- __ip6_dst_store(sk, dst, daddr, saddr);
- spin_unlock(&sk->sk_dst_lock);
}
static inline bool ipv6_unicast_destination(const struct sk_buff *skb)
diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h
index ff788b665277..0d0ce0b2d870 100644
--- a/include/net/ip6_tunnel.h
+++ b/include/net/ip6_tunnel.h
@@ -5,6 +5,7 @@
#include <linux/netdevice.h>
#include <linux/if_tunnel.h>
#include <linux/ip6_tunnel.h>
+#include <net/ip_tunnels.h>
#define IP6TUNNEL_ERR_TIMEO (30*HZ)
@@ -80,25 +81,17 @@ __u32 ip6_tnl_get_cap(struct ip6_tnl *t, const struct in6_addr *laddr,
struct net *ip6_tnl_get_link_net(const struct net_device *dev);
int ip6_tnl_get_iflink(const struct net_device *dev);
+#ifdef CONFIG_INET
static inline void ip6tunnel_xmit(struct sock *sk, struct sk_buff *skb,
struct net_device *dev)
{
- struct net_device_stats *stats = &dev->stats;
int pkt_len, err;
pkt_len = skb->len - skb_inner_network_offset(skb);
err = ip6_local_out(dev_net(skb_dst(skb)->dev), sk, skb);
-
- if (net_xmit_eval(err) == 0) {
- struct pcpu_sw_netstats *tstats = get_cpu_ptr(dev->tstats);
- u64_stats_update_begin(&tstats->syncp);
- tstats->tx_bytes += pkt_len;
- tstats->tx_packets++;
- u64_stats_update_end(&tstats->syncp);
- put_cpu_ptr(tstats);
- } else {
- stats->tx_errors++;
- stats->tx_aborted_errors++;
- }
+ if (unlikely(net_xmit_eval(err)))
+ pkt_len = -1;
+ iptunnel_xmit_stats(dev, pkt_len);
}
#endif
+#endif
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index 9f4df68105ab..7029527725dd 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -325,7 +325,8 @@ extern u32 fib_multipath_secret __read_mostly;
static inline int fib_multipath_hash(__be32 saddr, __be32 daddr)
{
- return jhash_2words(saddr, daddr, fib_multipath_secret) >> 1;
+ return jhash_2words((__force u32)saddr, (__force u32)daddr,
+ fib_multipath_secret) >> 1;
}
void fib_select_multipath(struct fib_result *res, int hash);
diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
index 62a750a6a8f8..6db96ea0144f 100644
--- a/include/net/ip_tunnels.h
+++ b/include/net/ip_tunnels.h
@@ -273,32 +273,34 @@ static inline u8 ip_tunnel_ecn_encap(u8 tos, const struct iphdr *iph,
}
int iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto);
-int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
- __be32 src, __be32 dst, u8 proto,
- u8 tos, u8 ttl, __be16 df, bool xnet);
+void iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
+ __be32 src, __be32 dst, u8 proto,
+ u8 tos, u8 ttl, __be16 df, bool xnet);
struct metadata_dst *iptunnel_metadata_reply(struct metadata_dst *md,
gfp_t flags);
struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb, bool gre_csum,
int gso_type_mask);
-static inline void iptunnel_xmit_stats(int err,
- struct net_device_stats *err_stats,
- struct pcpu_sw_netstats __percpu *stats)
+static inline void iptunnel_xmit_stats(struct net_device *dev, int pkt_len)
{
- if (err > 0) {
- struct pcpu_sw_netstats *tstats = get_cpu_ptr(stats);
+ if (pkt_len > 0) {
+ struct pcpu_sw_netstats *tstats = get_cpu_ptr(dev->tstats);
u64_stats_update_begin(&tstats->syncp);
- tstats->tx_bytes += err;
+ tstats->tx_bytes += pkt_len;
tstats->tx_packets++;
u64_stats_update_end(&tstats->syncp);
put_cpu_ptr(tstats);
- } else if (err < 0) {
- err_stats->tx_errors++;
- err_stats->tx_aborted_errors++;
} else {
- err_stats->tx_dropped++;
+ struct net_device_stats *err_stats = &dev->stats;
+
+ if (pkt_len < 0) {
+ err_stats->tx_errors++;
+ err_stats->tx_aborted_errors++;
+ } else {
+ err_stats->tx_dropped++;
+ }
}
}
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index e1a10b0ac0b0..6570f379aba2 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -205,6 +205,7 @@ extern rwlock_t ip6_ra_lock;
*/
struct ipv6_txoptions {
+ atomic_t refcnt;
/* Length of this structure */
int tot_len;
@@ -217,7 +218,7 @@ struct ipv6_txoptions {
struct ipv6_opt_hdr *dst0opt;
struct ipv6_rt_hdr *srcrt; /* Routing Header */
struct ipv6_opt_hdr *dst1opt;
-
+ struct rcu_head rcu;
/* Option buffer, as read by IPV6_PKTOPTIONS, starts here. */
};
@@ -252,6 +253,24 @@ struct ipv6_fl_socklist {
struct rcu_head rcu;
};
+static inline struct ipv6_txoptions *txopt_get(const struct ipv6_pinfo *np)
+{
+ struct ipv6_txoptions *opt;
+
+ rcu_read_lock();
+ opt = rcu_dereference(np->opt);
+ if (opt && !atomic_inc_not_zero(&opt->refcnt))
+ opt = NULL;
+ rcu_read_unlock();
+ return opt;
+}
+
+static inline void txopt_put(struct ipv6_txoptions *opt)
+{
+ if (opt && atomic_dec_and_test(&opt->refcnt))
+ kfree_rcu(opt, rcu);
+}
+
struct ip6_flowlabel *fl6_sock_lookup(struct sock *sk, __be32 label);
struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions *opt_space,
struct ip6_flowlabel *fl,
@@ -382,6 +401,21 @@ static inline void ipv6_addr_prefix(struct in6_addr *pfx,
pfx->s6_addr[o] = addr->s6_addr[o] & (0xff00 >> b);
}
+static inline void ipv6_addr_prefix_copy(struct in6_addr *addr,
+ const struct in6_addr *pfx,
+ int plen)
+{
+ /* caller must guarantee 0 <= plen <= 128 */
+ int o = plen >> 3,
+ b = plen & 0x7;
+
+ memcpy(addr->s6_addr, pfx, o);
+ if (b != 0) {
+ addr->s6_addr[o] &= ~(0xff00 >> b);
+ addr->s6_addr[o] |= (pfx->s6_addr[o] & (0xff00 >> b));
+ }
+}
+
static inline void __ipv6_addr_set_half(__be32 *addr,
__be32 wh, __be32 wl)
{
@@ -490,6 +524,7 @@ struct ip6_create_arg {
u32 user;
const struct in6_addr *src;
const struct in6_addr *dst;
+ int iif;
u8 ecn;
};
diff --git a/include/net/l3mdev.h b/include/net/l3mdev.h
index 774d85b2d5d9..5567d46b3cff 100644
--- a/include/net/l3mdev.h
+++ b/include/net/l3mdev.h
@@ -29,7 +29,7 @@ struct l3mdev_ops {
/* IPv4 ops */
struct rtable * (*l3mdev_get_rtable)(const struct net_device *dev,
const struct flowi4 *fl4);
- void (*l3mdev_get_saddr)(struct net_device *dev,
+ int (*l3mdev_get_saddr)(struct net_device *dev,
struct flowi4 *fl4);
/* IPv6 ops */
@@ -51,6 +51,24 @@ static inline int l3mdev_master_ifindex(struct net_device *dev)
return ifindex;
}
+static inline int l3mdev_master_ifindex_by_index(struct net *net, int ifindex)
+{
+ struct net_device *dev;
+ int rc = 0;
+
+ if (likely(ifindex)) {
+ rcu_read_lock();
+
+ dev = dev_get_by_index_rcu(net, ifindex);
+ if (dev)
+ rc = l3mdev_master_ifindex_rcu(dev);
+
+ rcu_read_unlock();
+ }
+
+ return rc;
+}
+
/* get index of an interface to use for FIB lookups. For devices
* enslaved to an L3 master device FIB lookups are based on the
* master index
@@ -112,10 +130,11 @@ static inline bool netif_index_is_l3_master(struct net *net, int ifindex)
return rc;
}
-static inline void l3mdev_get_saddr(struct net *net, int ifindex,
- struct flowi4 *fl4)
+static inline int l3mdev_get_saddr(struct net *net, int ifindex,
+ struct flowi4 *fl4)
{
struct net_device *dev;
+ int rc = 0;
if (ifindex) {
@@ -124,11 +143,13 @@ static inline void l3mdev_get_saddr(struct net *net, int ifindex,
dev = dev_get_by_index_rcu(net, ifindex);
if (dev && netif_is_l3_master(dev) &&
dev->l3mdev_ops->l3mdev_get_saddr) {
- dev->l3mdev_ops->l3mdev_get_saddr(dev, fl4);
+ rc = dev->l3mdev_ops->l3mdev_get_saddr(dev, fl4);
}
rcu_read_unlock();
}
+
+ return rc;
}
static inline struct dst_entry *l3mdev_get_rt6_dst(const struct net_device *dev,
@@ -167,6 +188,11 @@ static inline int l3mdev_master_ifindex(struct net_device *dev)
return 0;
}
+static inline int l3mdev_master_ifindex_by_index(struct net *net, int ifindex)
+{
+ return 0;
+}
+
static inline int l3mdev_fib_oif_rcu(struct net_device *dev)
{
return dev ? dev->ifindex : 0;
@@ -200,9 +226,10 @@ static inline bool netif_index_is_l3_master(struct net *net, int ifindex)
return false;
}
-static inline void l3mdev_get_saddr(struct net *net, int ifindex,
- struct flowi4 *fl4)
+static inline int l3mdev_get_saddr(struct net *net, int ifindex,
+ struct flowi4 *fl4)
{
+ return 0;
}
static inline
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 82045fca388b..7c30faff245f 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1321,11 +1321,15 @@ struct ieee80211_channel_switch {
* interface. This flag should be set during interface addition,
* but may be set/cleared as late as authentication to an AP. It is
* only valid for managed/station mode interfaces.
+ * @IEEE80211_VIF_GET_NOA_UPDATE: request to handle NOA attributes
+ * and send P2P_PS notification to the driver if NOA changed, even
+ * this is not pure P2P vif.
*/
enum ieee80211_vif_flags {
IEEE80211_VIF_BEACON_FILTER = BIT(0),
IEEE80211_VIF_SUPPORTS_CQM_RSSI = BIT(1),
IEEE80211_VIF_SUPPORTS_UAPSD = BIT(2),
+ IEEE80211_VIF_GET_NOA_UPDATE = BIT(3),
};
/**
@@ -1901,6 +1905,11 @@ struct ieee80211_txq {
* @IEEE80211_HW_BEACON_TX_STATUS: The device/driver provides TX status
* for sent beacons.
*
+ * @IEEE80211_HW_NEEDS_UNIQUE_STA_ADDR: Hardware (or driver) requires that each
+ * station has a unique address, i.e. each station entry can be identified
+ * by just its MAC address; this prevents, for example, the same station
+ * from connecting to two virtual AP interfaces at the same time.
+ *
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
*/
enum ieee80211_hw_flags {
@@ -1936,6 +1945,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_TDLS_WIDER_BW,
IEEE80211_HW_SUPPORTS_AMSDU_IN_AMPDU,
IEEE80211_HW_BEACON_TX_STATUS,
+ IEEE80211_HW_NEEDS_UNIQUE_STA_ADDR,
/* keep last, obviously */
NUM_IEEE80211_HW_FLAGS
@@ -2003,8 +2013,10 @@ enum ieee80211_hw_flags {
* it shouldn't be set.
*
* @max_tx_aggregation_subframes: maximum number of subframes in an
- * aggregate an HT driver will transmit, used by the peer as a
- * hint to size its reorder buffer.
+ * aggregate an HT driver will transmit. Though ADDBA will advertise
+ * a constant value of 64 as some older APs can crash if the window
+ * size is smaller (an example is LinkSys WRT120N with FW v1.0.07
+ * build 002 Jun 18 2012).
*
* @offchannel_tx_hw_queue: HW queue ID to use for offchannel TX
* (if %IEEE80211_HW_QUEUE_CONTROL is set)
@@ -4861,6 +4873,28 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
void ieee80211_sta_eosp(struct ieee80211_sta *pubsta);
/**
+ * ieee80211_send_eosp_nullfunc - ask mac80211 to send NDP with EOSP
+ * @pubsta: the station
+ * @tid: the tid of the NDP
+ *
+ * Sometimes the device understands that it needs to close
+ * the Service Period unexpectedly. This can happen when
+ * sending frames that are filling holes in the BA window.
+ * In this case, the device can ask mac80211 to send a
+ * Nullfunc frame with EOSP set. When that happens, the
+ * driver must have called ieee80211_sta_set_buffered() to
+ * let mac80211 know that there are no buffered frames any
+ * more, otherwise mac80211 will get the more_data bit wrong.
+ * The low level driver must have made sure that the frame
+ * will be sent despite the station being in power-save.
+ * Mac80211 won't call allow_buffered_frames().
+ * Note that calling this function, doesn't exempt the driver
+ * from closing the EOSP properly, it will still have to call
+ * ieee80211_sta_eosp when the NDP is sent.
+ */
+void ieee80211_send_eosp_nullfunc(struct ieee80211_sta *pubsta, int tid);
+
+/**
* ieee80211_iter_keys - iterate keys programmed into the device
* @hw: pointer obtained from ieee80211_alloc_hw()
* @vif: virtual interface to iterate, may be %NULL for all
@@ -4888,6 +4922,30 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,
void *iter_data);
/**
+ * ieee80211_iter_keys_rcu - iterate keys programmed into the device
+ * @hw: pointer obtained from ieee80211_alloc_hw()
+ * @vif: virtual interface to iterate, may be %NULL for all
+ * @iter: iterator function that will be called for each key
+ * @iter_data: custom data to pass to the iterator function
+ *
+ * This function can be used to iterate all the keys known to
+ * mac80211, even those that weren't previously programmed into
+ * the device. Note that due to locking reasons, keys of station
+ * in removal process will be skipped.
+ *
+ * This function requires being called in an RCU critical section,
+ * and thus iter must be atomic.
+ */
+void ieee80211_iter_keys_rcu(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ void (*iter)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *data),
+ void *iter_data);
+
+/**
* ieee80211_iter_chan_contexts_atomic - iterate channel contexts
* @hw: pointre obtained from ieee80211_alloc_hw().
* @iter: iterator function
diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index bf3937431030..2d8edaad29cb 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -181,8 +181,7 @@ void ndisc_cleanup(void);
int ndisc_rcv(struct sk_buff *skb);
void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
- const struct in6_addr *daddr, const struct in6_addr *saddr,
- struct sk_buff *oskb);
+ const struct in6_addr *daddr, const struct in6_addr *saddr);
void ndisc_send_rs(struct net_device *dev,
const struct in6_addr *saddr, const struct in6_addr *daddr);
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index 2dcea635ecce..4089abc6e9c0 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -121,6 +121,9 @@ struct net {
#if IS_ENABLED(CONFIG_NETFILTER_NETLINK_ACCT)
struct list_head nfnl_acct_list;
#endif
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
+ struct list_head nfct_timeout_list;
+#endif
#endif
#ifdef CONFIG_WEXT_CORE
struct sk_buff_head wext_nlevents;
diff --git a/include/net/netfilter/ipv6/nf_defrag_ipv6.h b/include/net/netfilter/ipv6/nf_defrag_ipv6.h
index fb7da5bb76cc..ddf162f7966f 100644
--- a/include/net/netfilter/ipv6/nf_defrag_ipv6.h
+++ b/include/net/netfilter/ipv6/nf_defrag_ipv6.h
@@ -5,8 +5,7 @@ void nf_defrag_ipv6_enable(void);
int nf_ct_frag6_init(void);
void nf_ct_frag6_cleanup(void);
-struct sk_buff *nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user);
-void nf_ct_frag6_consume_orig(struct sk_buff *skb);
+int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user);
struct inet_frags_ctl;
diff --git a/include/net/netfilter/nf_conntrack_timeout.h b/include/net/netfilter/nf_conntrack_timeout.h
index f72be38860a7..5cc5e9e6171a 100644
--- a/include/net/netfilter/nf_conntrack_timeout.h
+++ b/include/net/netfilter/nf_conntrack_timeout.h
@@ -104,7 +104,7 @@ static inline void nf_conntrack_timeout_fini(void)
#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
-extern struct ctnl_timeout *(*nf_ct_timeout_find_get_hook)(const char *name);
+extern struct ctnl_timeout *(*nf_ct_timeout_find_get_hook)(struct net *net, const char *name);
extern void (*nf_ct_timeout_put_hook)(struct ctnl_timeout *timeout);
#endif
diff --git a/include/net/netfilter/nf_dup_netdev.h b/include/net/netfilter/nf_dup_netdev.h
new file mode 100644
index 000000000000..397dcae349f9
--- /dev/null
+++ b/include/net/netfilter/nf_dup_netdev.h
@@ -0,0 +1,6 @@
+#ifndef _NF_DUP_NETDEV_H_
+#define _NF_DUP_NETDEV_H_
+
+void nf_dup_netdev_egress(const struct nft_pktinfo *pkt, int oif);
+
+#endif
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index 4bd7508bedc9..f6b1daf2e698 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -19,8 +19,6 @@ struct nft_pktinfo {
const struct net_device *out;
u8 pf;
u8 hook;
- u8 nhoff;
- u8 thoff;
u8 tprot;
/* for x_tables compatibility */
struct xt_action_param xt;
@@ -293,6 +291,8 @@ void nft_unregister_set(struct nft_set_ops *ops);
* @timeout: default timeout value in msecs
* @gc_int: garbage collection interval in msecs
* @policy: set parameterization (see enum nft_set_policies)
+ * @udlen: user data length
+ * @udata: user data
* @ops: set ops
* @pnet: network namespace
* @flags: set flags
@@ -312,6 +312,8 @@ struct nft_set {
u64 timeout;
u32 gc_int;
u16 policy;
+ u16 udlen;
+ unsigned char *udata;
/* runtime data below here */
const struct nft_set_ops *ops ____cacheline_aligned;
possible_net_t pnet;
@@ -823,10 +825,7 @@ static inline struct nft_base_chain *nft_base_chain(const struct nft_chain *chai
return container_of(chain, struct nft_base_chain, chain);
}
-int nft_register_basechain(struct nft_base_chain *basechain,
- unsigned int hook_nops);
-void nft_unregister_basechain(struct nft_base_chain *basechain,
- unsigned int hook_nops);
+int __nft_release_basechain(struct nft_ctx *ctx);
unsigned int nft_do_chain(struct nft_pktinfo *pkt, void *priv);
@@ -882,7 +881,7 @@ struct nft_af_info {
};
int nft_register_afinfo(struct net *, struct nft_af_info *);
-void nft_unregister_afinfo(struct nft_af_info *);
+void nft_unregister_afinfo(struct net *, struct nft_af_info *);
int nft_register_chain_type(const struct nf_chain_type *);
void nft_unregister_chain_type(const struct nf_chain_type *);
@@ -890,6 +889,38 @@ void nft_unregister_chain_type(const struct nf_chain_type *);
int nft_register_expr(struct nft_expr_type *);
void nft_unregister_expr(struct nft_expr_type *);
+int nft_verdict_dump(struct sk_buff *skb, int type,
+ const struct nft_verdict *v);
+
+/**
+ * struct nft_traceinfo - nft tracing information and state
+ *
+ * @pkt: pktinfo currently processed
+ * @basechain: base chain currently processed
+ * @chain: chain currently processed
+ * @rule: rule that was evaluated
+ * @verdict: verdict given by rule
+ * @type: event type (enum nft_trace_types)
+ * @packet_dumped: packet headers sent in a previous traceinfo message
+ * @trace: other struct members are initialised
+ */
+struct nft_traceinfo {
+ const struct nft_pktinfo *pkt;
+ const struct nft_base_chain *basechain;
+ const struct nft_chain *chain;
+ const struct nft_rule *rule;
+ const struct nft_verdict *verdict;
+ enum nft_trace_types type;
+ bool packet_dumped;
+ bool trace;
+};
+
+void nft_trace_init(struct nft_traceinfo *info, const struct nft_pktinfo *pkt,
+ const struct nft_verdict *verdict,
+ const struct nft_chain *basechain);
+
+void nft_trace_notify(struct nft_traceinfo *info);
+
#define nft_dereference(p) \
nfnl_dereference(p, NFNL_SUBSYS_NFTABLES)
diff --git a/include/net/netfilter/nf_tables_core.h b/include/net/netfilter/nf_tables_core.h
index c6f400cfaac8..a9060dd99db7 100644
--- a/include/net/netfilter/nf_tables_core.h
+++ b/include/net/netfilter/nf_tables_core.h
@@ -47,7 +47,17 @@ struct nft_payload {
enum nft_registers dreg:8;
};
+struct nft_payload_set {
+ enum nft_payload_bases base:8;
+ u8 offset;
+ u8 len;
+ enum nft_registers sreg:8;
+ u8 csum_type;
+ u8 csum_offset;
+};
+
extern const struct nft_expr_ops nft_payload_fast_ops;
+extern struct static_key_false nft_trace_enabled;
int nft_payload_module_init(void);
void nft_payload_module_exit(void);
diff --git a/include/net/netfilter/nft_meta.h b/include/net/netfilter/nft_meta.h
index 711887a09e91..d27588c8dbd9 100644
--- a/include/net/netfilter/nft_meta.h
+++ b/include/net/netfilter/nft_meta.h
@@ -33,4 +33,7 @@ void nft_meta_set_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt);
+void nft_meta_set_destroy(const struct nft_ctx *ctx,
+ const struct nft_expr *expr);
+
#endif
diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h
index c68926b4899c..2b7907a35568 100644
--- a/include/net/netns/ipv4.h
+++ b/include/net/netns/ipv4.h
@@ -86,11 +86,18 @@ struct netns_ipv4 {
int sysctl_fwmark_reflect;
int sysctl_tcp_fwmark_accept;
+#ifdef CONFIG_NET_L3_MASTER_DEV
+ int sysctl_tcp_l3mdev_accept;
+#endif
int sysctl_tcp_mtu_probing;
int sysctl_tcp_base_mss;
int sysctl_tcp_probe_threshold;
u32 sysctl_tcp_probe_interval;
+ int sysctl_tcp_keepalive_time;
+ int sysctl_tcp_keepalive_probes;
+ int sysctl_tcp_keepalive_intvl;
+
struct ping_group_range ping_group_range;
atomic_t dev_addr_genid;
diff --git a/include/net/netns/sctp.h b/include/net/netns/sctp.h
index 8ba379f9e467..c501d67172b1 100644
--- a/include/net/netns/sctp.h
+++ b/include/net/netns/sctp.h
@@ -89,6 +89,13 @@ struct netns_sctp {
int pf_retrans;
/*
+ * Disable Potentially-Failed feature, the feature is enabled by default
+ * pf_enable - 0 : disable pf
+ * - >0 : enable pf
+ */
+ int pf_enable;
+
+ /*
* Policy for preforming sctp/socket accounting
* 0 - do socket level accounting, all assocs share sk_sndbuf
* 1 - do sctp accounting, each asoc may use sk_sndbuf bytes
diff --git a/include/net/netprio_cgroup.h b/include/net/netprio_cgroup.h
index f2a9597ff53c..604190596cde 100644
--- a/include/net/netprio_cgroup.h
+++ b/include/net/netprio_cgroup.h
@@ -25,8 +25,6 @@ struct netprio_map {
u32 priomap[];
};
-void sock_update_netprioidx(struct sock *sk);
-
static inline u32 task_netprioidx(struct task_struct *p)
{
struct cgroup_subsys_state *css;
@@ -38,13 +36,25 @@ static inline u32 task_netprioidx(struct task_struct *p)
rcu_read_unlock();
return idx;
}
+
+static inline void sock_update_netprioidx(struct sock_cgroup_data *skcd)
+{
+ if (in_interrupt())
+ return;
+
+ sock_cgroup_set_prioidx(skcd, task_netprioidx(current));
+}
+
#else /* !CONFIG_CGROUP_NET_PRIO */
+
static inline u32 task_netprioidx(struct task_struct *p)
{
return 0;
}
-#define sock_update_netprioidx(sk)
+static inline void sock_update_netprioidx(struct sock_cgroup_data *skcd)
+{
+}
#endif /* CONFIG_CGROUP_NET_PRIO */
#endif /* _NET_CLS_CGROUP_H */
diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h
index dcfcfc9c00bf..1a3de8b34ad2 100644
--- a/include/net/nfc/nfc.h
+++ b/include/net/nfc/nfc.h
@@ -299,6 +299,7 @@ void nfc_driver_failure(struct nfc_dev *dev, int err);
int nfc_se_transaction(struct nfc_dev *dev, u8 se_idx,
struct nfc_evt_transaction *evt_transaction);
+int nfc_se_connectivity(struct nfc_dev *dev, u8 se_idx);
int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type);
int nfc_remove_se(struct nfc_dev *dev, u32 se_idx);
struct nfc_se *nfc_find_se(struct nfc_dev *dev, u32 se_idx);
diff --git a/include/net/protocol.h b/include/net/protocol.h
index d6fcc1fcdb5b..da689f5432de 100644
--- a/include/net/protocol.h
+++ b/include/net/protocol.h
@@ -107,7 +107,7 @@ int inet_del_offload(const struct net_offload *prot, unsigned char num);
void inet_register_protosw(struct inet_protosw *p);
void inet_unregister_protosw(struct inet_protosw *p);
-int udp_add_offload(struct udp_offload *prot);
+int udp_add_offload(struct net *net, struct udp_offload *prot);
void udp_del_offload(struct udp_offload *prot);
#if IS_ENABLED(CONFIG_IPV6)
diff --git a/include/net/request_sock.h b/include/net/request_sock.h
index a0dde04eb178..f49759decb28 100644
--- a/include/net/request_sock.h
+++ b/include/net/request_sock.h
@@ -68,7 +68,7 @@ struct request_sock {
u32 peer_secid;
};
-static inline struct request_sock *inet_reqsk(struct sock *sk)
+static inline struct request_sock *inet_reqsk(const struct sock *sk)
{
return (struct request_sock *)sk;
}
diff --git a/include/net/route.h b/include/net/route.h
index ee81307863d5..a3b9ef74a389 100644
--- a/include/net/route.h
+++ b/include/net/route.h
@@ -283,7 +283,12 @@ static inline struct rtable *ip_route_connect(struct flowi4 *fl4,
sport, dport, sk);
if (!src && oif) {
- l3mdev_get_saddr(net, oif, fl4);
+ int rc;
+
+ rc = l3mdev_get_saddr(net, oif, fl4);
+ if (rc < 0)
+ return ERR_PTR(rc);
+
src = fl4->saddr;
}
if (!dst || !src) {
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index 4c79ce8c1f92..636a362a0e03 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -61,6 +61,9 @@ struct Qdisc {
*/
#define TCQ_F_WARN_NONWC (1 << 16)
#define TCQ_F_CPUSTATS 0x20 /* run using percpu statistics */
+#define TCQ_F_NOPARENT 0x40 /* root of its hierarchy :
+ * qdisc_tree_decrease_qlen() should stop.
+ */
u32 limit;
const struct Qdisc_ops *ops;
struct qdisc_size_table __rcu *stab;
@@ -404,6 +407,15 @@ bool tcf_destroy(struct tcf_proto *tp, bool force);
void tcf_destroy_chain(struct tcf_proto __rcu **fl);
int skb_do_redirect(struct sk_buff *);
+static inline bool skb_at_tc_ingress(const struct sk_buff *skb)
+{
+#ifdef CONFIG_NET_CLS_ACT
+ return G_TC_AT(skb->tc_verd) & AT_INGRESS;
+#else
+ return false;
+#endif
+}
+
/* Reset all TX qdiscs greater then index of a device. */
static inline void qdisc_reset_all_tx_gt(struct net_device *dev, unsigned int i)
{
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
index ce13cf20f625..835aa2ed9870 100644
--- a/include/net/sctp/sctp.h
+++ b/include/net/sctp/sctp.h
@@ -126,8 +126,6 @@ int sctp_primitive_ASCONF(struct net *, struct sctp_association *, void *arg);
*/
int sctp_rcv(struct sk_buff *skb);
void sctp_v4_err(struct sk_buff *skb, u32 info);
-void sctp_hash_established(struct sctp_association *);
-void sctp_unhash_established(struct sctp_association *);
void sctp_hash_endpoint(struct sctp_endpoint *);
void sctp_unhash_endpoint(struct sctp_endpoint *);
struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *,
@@ -143,6 +141,17 @@ void sctp_icmp_proto_unreachable(struct sock *sk,
struct sctp_transport *t);
void sctp_backlog_migrate(struct sctp_association *assoc,
struct sock *oldsk, struct sock *newsk);
+int sctp_transport_hashtable_init(void);
+void sctp_transport_hashtable_destroy(void);
+void sctp_hash_transport(struct sctp_transport *t);
+void sctp_unhash_transport(struct sctp_transport *t);
+struct sctp_transport *sctp_addrs_lookup_transport(
+ struct net *net,
+ const union sctp_addr *laddr,
+ const union sctp_addr *paddr);
+struct sctp_transport *sctp_epaddr_lookup_transport(
+ const struct sctp_endpoint *ep,
+ const union sctp_addr *paddr);
/*
* sctp/proc.c
@@ -519,25 +528,6 @@ static inline int sctp_ep_hashfn(struct net *net, __u16 lport)
return (net_hash_mix(net) + lport) & (sctp_ep_hashsize - 1);
}
-/* This is the hash function for the association hash table. */
-static inline int sctp_assoc_hashfn(struct net *net, __u16 lport, __u16 rport)
-{
- int h = (lport << 16) + rport + net_hash_mix(net);
- h ^= h>>8;
- return h & (sctp_assoc_hashsize - 1);
-}
-
-/* This is the hash function for the association hash table. This is
- * not used yet, but could be used as a better hash function when
- * we have a vtag.
- */
-static inline int sctp_vtag_hashfn(__u16 lport, __u16 rport, __u32 vtag)
-{
- int h = (lport << 16) + rport;
- h ^= vtag;
- return h & (sctp_assoc_hashsize - 1);
-}
-
#define sctp_for_each_hentry(epb, head) \
hlist_for_each_entry(epb, head, node)
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index 495c87e367b3..20e72129be1c 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -48,6 +48,7 @@
#define __sctp_structs_h__
#include <linux/ktime.h>
+#include <linux/rhashtable.h>
#include <linux/socket.h> /* linux/in.h needs this!! */
#include <linux/in.h> /* We get struct sockaddr_in. */
#include <linux/in6.h> /* We get struct in6_addr */
@@ -119,14 +120,13 @@ extern struct sctp_globals {
/* This is the hash of all endpoints. */
struct sctp_hashbucket *ep_hashtable;
- /* This is the hash of all associations. */
- struct sctp_hashbucket *assoc_hashtable;
/* This is the sctp port control hash. */
struct sctp_bind_hashbucket *port_hashtable;
+ /* This is the hash of all transports. */
+ struct rhashtable transport_hashtable;
/* Sizes of above hashtables. */
int ep_hashsize;
- int assoc_hashsize;
int port_hashsize;
/* Default initialization values to be applied to new associations. */
@@ -143,10 +143,9 @@ extern struct sctp_globals {
#define sctp_address_families (sctp_globals.address_families)
#define sctp_ep_hashsize (sctp_globals.ep_hashsize)
#define sctp_ep_hashtable (sctp_globals.ep_hashtable)
-#define sctp_assoc_hashsize (sctp_globals.assoc_hashsize)
-#define sctp_assoc_hashtable (sctp_globals.assoc_hashtable)
#define sctp_port_hashsize (sctp_globals.port_hashsize)
#define sctp_port_hashtable (sctp_globals.port_hashtable)
+#define sctp_transport_hashtable (sctp_globals.transport_hashtable)
#define sctp_checksum_disable (sctp_globals.checksum_disable)
/* SCTP Socket type: UDP or TCP style. */
@@ -753,6 +752,7 @@ static inline int sctp_packet_empty(struct sctp_packet *packet)
struct sctp_transport {
/* A list of transports. */
struct list_head transports;
+ struct rhash_head node;
/* Reference counting. */
atomic_t refcnt;
@@ -775,10 +775,10 @@ struct sctp_transport {
hb_sent:1,
/* Is the Path MTU update pending on this tranport */
- pmtu_pending:1;
+ pmtu_pending:1,
- /* Has this transport moved the ctsn since we last sacked */
- __u32 sack_generation;
+ /* Has this transport moved the ctsn since we last sacked */
+ sack_generation:1;
u32 dst_cookie;
struct flowi fl;
@@ -1482,19 +1482,20 @@ struct sctp_association {
prsctp_capable:1, /* Can peer do PR-SCTP? */
auth_capable:1; /* Is peer doing SCTP-AUTH? */
- /* Ack State : This flag indicates if the next received
+ /* sack_needed : This flag indicates if the next received
* : packet is to be responded to with a
- * : SACK. This is initializedto 0. When a packet
- * : is received it is incremented. If this value
+ * : SACK. This is initialized to 0. When a packet
+ * : is received sack_cnt is incremented. If this value
* : reaches 2 or more, a SACK is sent and the
* : value is reset to 0. Note: This is used only
* : when no DATA chunks are received out of
* : order. When DATA chunks are out of order,
* : SACK's are not delayed (see Section 6).
*/
- __u8 sack_needed; /* Do we need to sack the peer? */
+ __u8 sack_needed:1, /* Do we need to sack the peer? */
+ sack_generation:1,
+ zero_window_announced:1;
__u32 sack_cnt;
- __u32 sack_generation;
__u32 adaptation_ind; /* Adaptation Code point. */
diff --git a/include/net/sock.h b/include/net/sock.h
index 7f89e4ba18d1..b9e7b3d863a0 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -58,6 +58,8 @@
#include <linux/memcontrol.h>
#include <linux/static_key.h>
#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/cgroup-defs.h>
#include <linux/filter.h>
#include <linux/rculist_nulls.h>
@@ -69,22 +71,6 @@
#include <net/tcp_states.h>
#include <linux/net_tstamp.h>
-struct cgroup;
-struct cgroup_subsys;
-#ifdef CONFIG_NET
-int mem_cgroup_sockets_init(struct mem_cgroup *memcg, struct cgroup_subsys *ss);
-void mem_cgroup_sockets_destroy(struct mem_cgroup *memcg);
-#else
-static inline
-int mem_cgroup_sockets_init(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
-{
- return 0;
-}
-static inline
-void mem_cgroup_sockets_destroy(struct mem_cgroup *memcg)
-{
-}
-#endif
/*
* This structure really needs to be cleaned up.
* Most of it is for TCP, and not used by any of
@@ -243,7 +229,6 @@ struct sock_common {
/* public: */
};
-struct cg_proto;
/**
* struct sock - network layer representation of sockets
* @__sk_common: shared layout with inet_timewait_sock
@@ -254,7 +239,6 @@ struct cg_proto;
* @sk_wq: sock wait queue and async head
* @sk_rx_dst: receive input route used by early demux
* @sk_dst_cache: destination cache
- * @sk_dst_lock: destination cache lock
* @sk_policy: flow policy
* @sk_receive_queue: incoming packets
* @sk_wmem_alloc: transmit queue bytes committed
@@ -288,7 +272,6 @@ struct cg_proto;
* @sk_ack_backlog: current listen backlog
* @sk_max_ack_backlog: listen backlog set in listen()
* @sk_priority: %SO_PRIORITY setting
- * @sk_cgrp_prioidx: socket group's priority map index
* @sk_type: socket type (%SOCK_STREAM, etc)
* @sk_protocol: which protocol this socket belongs in this network family
* @sk_peer_pid: &struct pid for this socket's peer
@@ -309,8 +292,8 @@ struct cg_proto;
* @sk_send_head: front of stuff to transmit
* @sk_security: used by security modules
* @sk_mark: generic packet mark
- * @sk_classid: this socket's cgroup classid
- * @sk_cgrp: this socket's cgroup-specific proto data
+ * @sk_cgrp_data: cgroup data for this cgroup
+ * @sk_memcg: this socket's memory cgroup association
* @sk_write_pending: a write to stream socket waits to start
* @sk_state_change: callback to indicate change in the state of the sock
* @sk_data_ready: callback to indicate there is data to be processed
@@ -318,6 +301,7 @@ struct cg_proto;
* @sk_error_report: callback to indicate errors (e.g. %MSG_ERRQUEUE)
* @sk_backlog_rcv: callback to process the backlog
* @sk_destruct: called at sock freeing time, i.e. when all refcnt == 0
+ * @sk_reuseport_cb: reuseport group container
*/
struct sock {
/*
@@ -384,14 +368,16 @@ struct sock {
int sk_rcvbuf;
struct sk_filter __rcu *sk_filter;
- struct socket_wq __rcu *sk_wq;
-
+ union {
+ struct socket_wq __rcu *sk_wq;
+ struct socket_wq *sk_wq_raw;
+ };
#ifdef CONFIG_XFRM
- struct xfrm_policy *sk_policy[2];
+ struct xfrm_policy __rcu *sk_policy[2];
#endif
struct dst_entry *sk_rx_dst;
struct dst_entry __rcu *sk_dst_cache;
- spinlock_t sk_dst_lock;
+ /* Note: 32bit hole on 64bit arches */
atomic_t sk_wmem_alloc;
atomic_t sk_omem_alloc;
int sk_sndbuf;
@@ -403,6 +389,7 @@ struct sock {
sk_userlocks : 4,
sk_protocol : 8,
sk_type : 16;
+#define SK_PROTOCOL_MAX U8_MAX
kmemcheck_bitfield_end(flags);
int sk_wmem_queued;
gfp_t sk_allocation;
@@ -423,9 +410,7 @@ struct sock {
u32 sk_ack_backlog;
u32 sk_max_ack_backlog;
__u32 sk_priority;
-#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO)
- __u32 sk_cgrp_prioidx;
-#endif
+ __u32 sk_mark;
struct pid *sk_peer_pid;
const struct cred *sk_peer_cred;
long sk_rcvtimeo;
@@ -443,11 +428,8 @@ struct sock {
#ifdef CONFIG_SECURITY
void *sk_security;
#endif
- __u32 sk_mark;
-#ifdef CONFIG_CGROUP_NET_CLASSID
- u32 sk_classid;
-#endif
- struct cg_proto *sk_cgrp;
+ struct sock_cgroup_data sk_cgrp_data;
+ struct mem_cgroup *sk_memcg;
void (*sk_state_change)(struct sock *sk);
void (*sk_data_ready)(struct sock *sk);
void (*sk_write_space)(struct sock *sk);
@@ -455,6 +437,7 @@ struct sock {
int (*sk_backlog_rcv)(struct sock *sk,
struct sk_buff *skb);
void (*sk_destruct)(struct sock *sk);
+ struct sock_reuseport __rcu *sk_reuseport_cb;
};
#define __sk_user_data(sk) ((*((void __rcu **)&(sk)->sk_user_data)))
@@ -739,6 +722,8 @@ enum sock_flags {
SOCK_SELECT_ERR_QUEUE, /* Wake select on error queue */
};
+#define SK_FLAGS_TIMESTAMP ((1UL << SOCK_TIMESTAMP) | (1UL << SOCK_TIMESTAMPING_RX_SOFTWARE))
+
static inline void sock_copy_flags(struct sock *nsk, struct sock *osk)
{
nsk->sk_flags = osk->sk_flags;
@@ -774,9 +759,9 @@ static inline int sk_memalloc_socks(void)
#endif
-static inline gfp_t sk_gfp_atomic(const struct sock *sk, gfp_t gfp_mask)
+static inline gfp_t sk_gfp_mask(const struct sock *sk, gfp_t gfp_mask)
{
- return GFP_ATOMIC | (sk->sk_allocation & __GFP_MEMALLOC);
+ return gfp_mask | (sk->sk_allocation & __GFP_MEMALLOC);
}
static inline void sk_acceptq_removed(struct sock *sk)
@@ -813,7 +798,7 @@ void sk_stream_write_space(struct sock *sk);
static inline void __sk_add_backlog(struct sock *sk, struct sk_buff *skb)
{
/* dont let skb dst not refcounted, we are going to leave rcu lock */
- skb_dst_force(skb);
+ skb_dst_force_safe(skb);
if (!sk->sk_backlog.tail)
sk->sk_backlog.head = skb;
@@ -1063,6 +1048,7 @@ struct proto {
void (*destroy_cgroup)(struct mem_cgroup *memcg);
struct cg_proto *(*proto_cgroup)(struct mem_cgroup *memcg);
#endif
+ int (*diag_destroy)(struct sock *sk, int err);
};
int proto_register(struct proto *prot, int alloc_slab);
@@ -1093,23 +1079,6 @@ static inline void sk_refcnt_debug_release(const struct sock *sk)
#define sk_refcnt_debug_release(sk) do { } while (0)
#endif /* SOCK_REFCNT_DEBUG */
-#if defined(CONFIG_MEMCG_KMEM) && defined(CONFIG_NET)
-extern struct static_key memcg_socket_limit_enabled;
-static inline struct cg_proto *parent_cg_proto(struct proto *proto,
- struct cg_proto *cg_proto)
-{
- return proto->proto_cgroup(parent_mem_cgroup(cg_proto->memcg));
-}
-#define mem_cgroup_sockets_enabled static_key_false(&memcg_socket_limit_enabled)
-#else
-#define mem_cgroup_sockets_enabled 0
-static inline struct cg_proto *parent_cg_proto(struct proto *proto,
- struct cg_proto *cg_proto)
-{
- return NULL;
-}
-#endif
-
static inline bool sk_stream_memory_free(const struct sock *sk)
{
if (sk->sk_wmem_queued >= sk->sk_sndbuf)
@@ -1136,8 +1105,9 @@ static inline bool sk_under_memory_pressure(const struct sock *sk)
if (!sk->sk_prot->memory_pressure)
return false;
- if (mem_cgroup_sockets_enabled && sk->sk_cgrp)
- return !!sk->sk_cgrp->memory_pressure;
+ if (mem_cgroup_sockets_enabled && sk->sk_memcg &&
+ mem_cgroup_under_socket_pressure(sk->sk_memcg))
+ return true;
return !!*sk->sk_prot->memory_pressure;
}
@@ -1151,15 +1121,6 @@ static inline void sk_leave_memory_pressure(struct sock *sk)
if (*memory_pressure)
*memory_pressure = 0;
-
- if (mem_cgroup_sockets_enabled && sk->sk_cgrp) {
- struct cg_proto *cg_proto = sk->sk_cgrp;
- struct proto *prot = sk->sk_prot;
-
- for (; cg_proto; cg_proto = parent_cg_proto(prot, cg_proto))
- cg_proto->memory_pressure = 0;
- }
-
}
static inline void sk_enter_memory_pressure(struct sock *sk)
@@ -1167,116 +1128,46 @@ static inline void sk_enter_memory_pressure(struct sock *sk)
if (!sk->sk_prot->enter_memory_pressure)
return;
- if (mem_cgroup_sockets_enabled && sk->sk_cgrp) {
- struct cg_proto *cg_proto = sk->sk_cgrp;
- struct proto *prot = sk->sk_prot;
-
- for (; cg_proto; cg_proto = parent_cg_proto(prot, cg_proto))
- cg_proto->memory_pressure = 1;
- }
-
sk->sk_prot->enter_memory_pressure(sk);
}
static inline long sk_prot_mem_limits(const struct sock *sk, int index)
{
- long *prot = sk->sk_prot->sysctl_mem;
- if (mem_cgroup_sockets_enabled && sk->sk_cgrp)
- prot = sk->sk_cgrp->sysctl_mem;
- return prot[index];
-}
-
-static inline void memcg_memory_allocated_add(struct cg_proto *prot,
- unsigned long amt,
- int *parent_status)
-{
- page_counter_charge(&prot->memory_allocated, amt);
-
- if (page_counter_read(&prot->memory_allocated) >
- prot->memory_allocated.limit)
- *parent_status = OVER_LIMIT;
-}
-
-static inline void memcg_memory_allocated_sub(struct cg_proto *prot,
- unsigned long amt)
-{
- page_counter_uncharge(&prot->memory_allocated, amt);
+ return sk->sk_prot->sysctl_mem[index];
}
static inline long
sk_memory_allocated(const struct sock *sk)
{
- struct proto *prot = sk->sk_prot;
-
- if (mem_cgroup_sockets_enabled && sk->sk_cgrp)
- return page_counter_read(&sk->sk_cgrp->memory_allocated);
-
- return atomic_long_read(prot->memory_allocated);
+ return atomic_long_read(sk->sk_prot->memory_allocated);
}
static inline long
-sk_memory_allocated_add(struct sock *sk, int amt, int *parent_status)
+sk_memory_allocated_add(struct sock *sk, int amt)
{
- struct proto *prot = sk->sk_prot;
-
- if (mem_cgroup_sockets_enabled && sk->sk_cgrp) {
- memcg_memory_allocated_add(sk->sk_cgrp, amt, parent_status);
- /* update the root cgroup regardless */
- atomic_long_add_return(amt, prot->memory_allocated);
- return page_counter_read(&sk->sk_cgrp->memory_allocated);
- }
-
- return atomic_long_add_return(amt, prot->memory_allocated);
+ return atomic_long_add_return(amt, sk->sk_prot->memory_allocated);
}
static inline void
sk_memory_allocated_sub(struct sock *sk, int amt)
{
- struct proto *prot = sk->sk_prot;
-
- if (mem_cgroup_sockets_enabled && sk->sk_cgrp)
- memcg_memory_allocated_sub(sk->sk_cgrp, amt);
-
- atomic_long_sub(amt, prot->memory_allocated);
+ atomic_long_sub(amt, sk->sk_prot->memory_allocated);
}
static inline void sk_sockets_allocated_dec(struct sock *sk)
{
- struct proto *prot = sk->sk_prot;
-
- if (mem_cgroup_sockets_enabled && sk->sk_cgrp) {
- struct cg_proto *cg_proto = sk->sk_cgrp;
-
- for (; cg_proto; cg_proto = parent_cg_proto(prot, cg_proto))
- percpu_counter_dec(&cg_proto->sockets_allocated);
- }
-
- percpu_counter_dec(prot->sockets_allocated);
+ percpu_counter_dec(sk->sk_prot->sockets_allocated);
}
static inline void sk_sockets_allocated_inc(struct sock *sk)
{
- struct proto *prot = sk->sk_prot;
-
- if (mem_cgroup_sockets_enabled && sk->sk_cgrp) {
- struct cg_proto *cg_proto = sk->sk_cgrp;
-
- for (; cg_proto; cg_proto = parent_cg_proto(prot, cg_proto))
- percpu_counter_inc(&cg_proto->sockets_allocated);
- }
-
- percpu_counter_inc(prot->sockets_allocated);
+ percpu_counter_inc(sk->sk_prot->sockets_allocated);
}
static inline int
sk_sockets_allocated_read_positive(struct sock *sk)
{
- struct proto *prot = sk->sk_prot;
-
- if (mem_cgroup_sockets_enabled && sk->sk_cgrp)
- return percpu_counter_read_positive(&sk->sk_cgrp->sockets_allocated);
-
- return percpu_counter_read_positive(prot->sockets_allocated);
+ return percpu_counter_read_positive(sk->sk_prot->sockets_allocated);
}
static inline int
@@ -1794,6 +1685,15 @@ static inline void sk_nocaps_add(struct sock *sk, netdev_features_t flags)
sk->sk_route_caps &= ~flags;
}
+static inline bool sk_check_csum_caps(struct sock *sk)
+{
+ return (sk->sk_route_caps & NETIF_F_HW_CSUM) ||
+ (sk->sk_family == PF_INET &&
+ (sk->sk_route_caps & NETIF_F_IP_CSUM)) ||
+ (sk->sk_family == PF_INET6 &&
+ (sk->sk_route_caps & NETIF_F_IPV6_CSUM));
+}
+
static inline int skb_do_copy_data_nocache(struct sock *sk, struct sk_buff *skb,
struct iov_iter *from, char *to,
int copy, int offset)
@@ -1879,12 +1779,12 @@ static inline bool sk_has_allocations(const struct sock *sk)
}
/**
- * wq_has_sleeper - check if there are any waiting processes
+ * skwq_has_sleeper - check if there are any waiting processes
* @wq: struct socket_wq
*
* Returns true if socket_wq has waiting processes
*
- * The purpose of the wq_has_sleeper and sock_poll_wait is to wrap the memory
+ * The purpose of the skwq_has_sleeper and sock_poll_wait is to wrap the memory
* barrier call. They were added due to the race found within the tcp code.
*
* Consider following tcp code paths:
@@ -1910,15 +1810,9 @@ static inline bool sk_has_allocations(const struct sock *sk)
* data on the socket.
*
*/
-static inline bool wq_has_sleeper(struct socket_wq *wq)
+static inline bool skwq_has_sleeper(struct socket_wq *wq)
{
- /* We need to be sure we are in sync with the
- * add_wait_queue modifications to the wait queue.
- *
- * This memory barrier is paired in the sock_poll_wait.
- */
- smp_mb();
- return wq && waitqueue_active(&wq->wait);
+ return wq && wq_has_sleeper(&wq->wait);
}
/**
@@ -2005,10 +1899,27 @@ static inline unsigned long sock_wspace(struct sock *sk)
return amt;
}
-static inline void sk_wake_async(struct sock *sk, int how, int band)
+/* Note:
+ * We use sk->sk_wq_raw, from contexts knowing this
+ * pointer is not NULL and cannot disappear/change.
+ */
+static inline void sk_set_bit(int nr, struct sock *sk)
+{
+ set_bit(nr, &sk->sk_wq_raw->flags);
+}
+
+static inline void sk_clear_bit(int nr, struct sock *sk)
{
- if (sock_flag(sk, SOCK_FASYNC))
- sock_wake_async(sk->sk_socket, how, band);
+ clear_bit(nr, &sk->sk_wq_raw->flags);
+}
+
+static inline void sk_wake_async(const struct sock *sk, int how, int band)
+{
+ if (sock_flag(sk, SOCK_FASYNC)) {
+ rcu_read_lock();
+ sock_wake_async(rcu_dereference(sk->sk_wq), how, band);
+ rcu_read_unlock();
+ }
}
/* Since sk_{r,w}mem_alloc sums skb->truesize, even a small frame might
diff --git a/include/net/sock_reuseport.h b/include/net/sock_reuseport.h
new file mode 100644
index 000000000000..7dda3d7adba8
--- /dev/null
+++ b/include/net/sock_reuseport.h
@@ -0,0 +1,28 @@
+#ifndef _SOCK_REUSEPORT_H
+#define _SOCK_REUSEPORT_H
+
+#include <linux/filter.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <net/sock.h>
+
+struct sock_reuseport {
+ struct rcu_head rcu;
+
+ u16 max_socks; /* length of socks */
+ u16 num_socks; /* elements in socks */
+ struct bpf_prog __rcu *prog; /* optional BPF sock selector */
+ struct sock *socks[0]; /* array of sock pointers */
+};
+
+extern int reuseport_alloc(struct sock *sk);
+extern int reuseport_add_sock(struct sock *sk, const struct sock *sk2);
+extern void reuseport_detach_sock(struct sock *sk);
+extern struct sock *reuseport_select_sock(struct sock *sk,
+ u32 hash,
+ struct sk_buff *skb,
+ int hdr_len);
+extern struct bpf_prog *reuseport_attach_prog(struct sock *sk,
+ struct bpf_prog *prog);
+
+#endif /* _SOCK_REUSEPORT_H */
diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index 1d22ce9f352e..d451122e8404 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -47,9 +47,11 @@ enum switchdev_attr_id {
SWITCHDEV_ATTR_ID_PORT_STP_STATE,
SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME,
+ SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING,
};
struct switchdev_attr {
+ struct net_device *orig_dev;
enum switchdev_attr_id id;
u32 flags;
union {
@@ -57,6 +59,7 @@ struct switchdev_attr {
u8 stp_state; /* PORT_STP_STATE */
unsigned long brport_flags; /* PORT_BRIDGE_FLAGS */
u32 ageing_time; /* BRIDGE_AGEING_TIME */
+ bool vlan_filtering; /* BRIDGE_VLAN_FILTERING */
} u;
};
@@ -65,9 +68,11 @@ enum switchdev_obj_id {
SWITCHDEV_OBJ_ID_PORT_VLAN,
SWITCHDEV_OBJ_ID_IPV4_FIB,
SWITCHDEV_OBJ_ID_PORT_FDB,
+ SWITCHDEV_OBJ_ID_PORT_MDB,
};
struct switchdev_obj {
+ struct net_device *orig_dev;
enum switchdev_obj_id id;
u32 flags;
};
@@ -109,6 +114,16 @@ struct switchdev_obj_port_fdb {
#define SWITCHDEV_OBJ_PORT_FDB(obj) \
container_of(obj, struct switchdev_obj_port_fdb, obj)
+/* SWITCHDEV_OBJ_ID_PORT_MDB */
+struct switchdev_obj_port_mdb {
+ struct switchdev_obj obj;
+ unsigned char addr[ETH_ALEN];
+ u16 vid;
+};
+
+#define SWITCHDEV_OBJ_PORT_MDB(obj) \
+ container_of(obj, struct switchdev_obj_port_mdb, obj)
+
void switchdev_trans_item_enqueue(struct switchdev_trans *trans,
void *data, void (*destructor)(void const *),
struct switchdev_trans_item *tritem);
diff --git a/include/net/tcp.h b/include/net/tcp.h
index f80e74c5ad18..8ea19977ea53 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -240,9 +240,6 @@ extern int sysctl_tcp_timestamps;
extern int sysctl_tcp_window_scaling;
extern int sysctl_tcp_sack;
extern int sysctl_tcp_fin_timeout;
-extern int sysctl_tcp_keepalive_time;
-extern int sysctl_tcp_keepalive_probes;
-extern int sysctl_tcp_keepalive_intvl;
extern int sysctl_tcp_syn_retries;
extern int sysctl_tcp_synack_retries;
extern int sysctl_tcp_retries1;
@@ -292,8 +289,9 @@ extern int tcp_memory_pressure;
/* optimized version of sk_under_memory_pressure() for TCP sockets */
static inline bool tcp_under_memory_pressure(const struct sock *sk)
{
- if (mem_cgroup_sockets_enabled && sk->sk_cgrp)
- return !!sk->sk_cgrp->memory_pressure;
+ if (mem_cgroup_sockets_enabled && sk->sk_memcg &&
+ mem_cgroup_under_socket_pressure(sk->sk_memcg))
+ return true;
return tcp_memory_pressure;
}
@@ -1170,6 +1168,8 @@ void tcp_set_state(struct sock *sk, int state);
void tcp_done(struct sock *sk);
+int tcp_abort(struct sock *sk, int err);
+
static inline void tcp_sack_reset(struct tcp_options_received *rx_opt)
{
rx_opt->dsack = 0;
@@ -1223,17 +1223,23 @@ void tcp_enter_memory_pressure(struct sock *sk);
static inline int keepalive_intvl_when(const struct tcp_sock *tp)
{
- return tp->keepalive_intvl ? : sysctl_tcp_keepalive_intvl;
+ struct net *net = sock_net((struct sock *)tp);
+
+ return tp->keepalive_intvl ? : net->ipv4.sysctl_tcp_keepalive_intvl;
}
static inline int keepalive_time_when(const struct tcp_sock *tp)
{
- return tp->keepalive_time ? : sysctl_tcp_keepalive_time;
+ struct net *net = sock_net((struct sock *)tp);
+
+ return tp->keepalive_time ? : net->ipv4.sysctl_tcp_keepalive_time;
}
static inline int keepalive_probes(const struct tcp_sock *tp)
{
- return tp->keepalive_probes ? : sysctl_tcp_keepalive_probes;
+ struct net *net = sock_net((struct sock *)tp);
+
+ return tp->keepalive_probes ? : net->ipv4.sysctl_tcp_keepalive_probes;
}
static inline u32 keepalive_time_elapsed(const struct tcp_sock *tp)
@@ -1618,6 +1624,18 @@ static inline void tcp_highest_sack_combine(struct sock *sk,
tcp_sk(sk)->highest_sack = new;
}
+/* This helper checks if socket has IP_TRANSPARENT set */
+static inline bool inet_sk_transparent(const struct sock *sk)
+{
+ switch (sk->sk_state) {
+ case TCP_TIME_WAIT:
+ return inet_twsk(sk)->tw_transparent;
+ case TCP_NEW_SYN_RECV:
+ return inet_rsk(inet_reqsk(sk))->no_srccheck;
+ }
+ return inet_sk(sk)->transparent;
+}
+
/* Determines whether this is a thin stream (which may suffer from
* increased latency). Used to trigger latency-reducing mechanisms.
*/
diff --git a/include/net/tcp_memcontrol.h b/include/net/tcp_memcontrol.h
index 05b94d9453de..3a17b16ae8aa 100644
--- a/include/net/tcp_memcontrol.h
+++ b/include/net/tcp_memcontrol.h
@@ -1,7 +1,6 @@
#ifndef _TCP_MEMCG_H
#define _TCP_MEMCG_H
-struct cg_proto *tcp_proto_cgroup(struct mem_cgroup *memcg);
int tcp_init_cgroup(struct mem_cgroup *memcg, struct cgroup_subsys *ss);
void tcp_destroy_cgroup(struct mem_cgroup *memcg);
#endif /* _TCP_MEMCG_H */
diff --git a/include/net/udp.h b/include/net/udp.h
index 6d4ed18e1427..2842541e28e7 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -191,7 +191,7 @@ static inline void udp_lib_close(struct sock *sk, long timeout)
}
int udp_lib_get_port(struct sock *sk, unsigned short snum,
- int (*)(const struct sock *, const struct sock *),
+ int (*)(const struct sock *, const struct sock *, bool),
unsigned int hash2_nulladdr);
u32 udp_flow_hashrnd(void);
@@ -258,7 +258,7 @@ struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport,
__be32 daddr, __be16 dport, int dif);
struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport,
__be32 daddr, __be16 dport, int dif,
- struct udp_table *tbl);
+ struct udp_table *tbl, struct sk_buff *skb);
struct sock *udp6_lib_lookup(struct net *net,
const struct in6_addr *saddr, __be16 sport,
const struct in6_addr *daddr, __be16 dport,
@@ -266,7 +266,8 @@ struct sock *udp6_lib_lookup(struct net *net,
struct sock *__udp6_lib_lookup(struct net *net,
const struct in6_addr *saddr, __be16 sport,
const struct in6_addr *daddr, __be16 dport,
- int dif, struct udp_table *tbl);
+ int dif, struct udp_table *tbl,
+ struct sk_buff *skb);
/*
* SNMP statistics for UDP and UDP-Lite
diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h
index cb2f89f20f5c..cca2ad3082c3 100644
--- a/include/net/udp_tunnel.h
+++ b/include/net/udp_tunnel.h
@@ -78,10 +78,10 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock,
struct udp_tunnel_sock_cfg *sock_cfg);
/* Transmit the skb using UDP encapsulation. */
-int udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
- __be32 src, __be32 dst, __u8 tos, __u8 ttl,
- __be16 df, __be16 src_port, __be16 dst_port,
- bool xnet, bool nocheck);
+void udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
+ __be32 src, __be32 dst, __u8 tos, __u8 ttl,
+ __be16 df, __be16 src_port, __be16 dst_port,
+ bool xnet, bool nocheck);
#if IS_ENABLED(CONFIG_IPV6)
int udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sock *sk,
diff --git a/include/net/vxlan.h b/include/net/vxlan.h
index c1c899c3a51b..0fb86442544b 100644
--- a/include/net/vxlan.h
+++ b/include/net/vxlan.h
@@ -79,7 +79,7 @@ struct vxlanhdr {
};
/* VXLAN header flags. */
-#define VXLAN_HF_RCO BIT(24)
+#define VXLAN_HF_RCO BIT(21)
#define VXLAN_HF_VNI BIT(27)
#define VXLAN_HF_GBP BIT(31)
@@ -232,7 +232,7 @@ static inline netdev_features_t vxlan_features_check(struct sk_buff *skb,
skb->inner_protocol != htons(ETH_P_TEB) ||
(skb_inner_mac_header(skb) - skb_transport_header(skb) !=
sizeof(struct udphdr) + sizeof(struct vxlanhdr))))
- return features & ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK);
+ return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
return features;
}
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 4a9c21f9b4ea..d6f6e5006ee9 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -548,6 +548,7 @@ struct xfrm_policy {
u16 family;
struct xfrm_sec_ctx *security;
struct xfrm_tmpl xfrm_vec[XFRM_MAX_DEPTH];
+ struct rcu_head rcu;
};
static inline struct net *xp_net(const struct xfrm_policy *xp)
@@ -1141,12 +1142,14 @@ static inline int xfrm6_route_forward(struct sk_buff *skb)
return xfrm_route_forward(skb, AF_INET6);
}
-int __xfrm_sk_clone_policy(struct sock *sk);
+int __xfrm_sk_clone_policy(struct sock *sk, const struct sock *osk);
-static inline int xfrm_sk_clone_policy(struct sock *sk)
+static inline int xfrm_sk_clone_policy(struct sock *sk, const struct sock *osk)
{
- if (unlikely(sk->sk_policy[0] || sk->sk_policy[1]))
- return __xfrm_sk_clone_policy(sk);
+ sk->sk_policy[0] = NULL;
+ sk->sk_policy[1] = NULL;
+ if (unlikely(osk->sk_policy[0] || osk->sk_policy[1]))
+ return __xfrm_sk_clone_policy(sk, osk);
return 0;
}
@@ -1154,12 +1157,16 @@ int xfrm_policy_delete(struct xfrm_policy *pol, int dir);
static inline void xfrm_sk_free_policy(struct sock *sk)
{
- if (unlikely(sk->sk_policy[0] != NULL)) {
- xfrm_policy_delete(sk->sk_policy[0], XFRM_POLICY_MAX);
+ struct xfrm_policy *pol;
+
+ pol = rcu_dereference_protected(sk->sk_policy[0], 1);
+ if (unlikely(pol != NULL)) {
+ xfrm_policy_delete(pol, XFRM_POLICY_MAX);
sk->sk_policy[0] = NULL;
}
- if (unlikely(sk->sk_policy[1] != NULL)) {
- xfrm_policy_delete(sk->sk_policy[1], XFRM_POLICY_MAX+1);
+ pol = rcu_dereference_protected(sk->sk_policy[1], 1);
+ if (unlikely(pol != NULL)) {
+ xfrm_policy_delete(pol, XFRM_POLICY_MAX+1);
sk->sk_policy[1] = NULL;
}
}
@@ -1169,7 +1176,7 @@ void xfrm_garbage_collect(struct net *net);
#else
static inline void xfrm_sk_free_policy(struct sock *sk) {}
-static inline int xfrm_sk_clone_policy(struct sock *sk) { return 0; }
+static inline int xfrm_sk_clone_policy(struct sock *sk, const struct sock *osk) { return 0; }
static inline int xfrm6_route_forward(struct sk_buff *skb) { return 1; }
static inline int xfrm4_route_forward(struct sk_buff *skb) { return 1; }
static inline int xfrm6_policy_check(struct sock *sk, int dir, struct sk_buff *skb)
diff --git a/include/rdma/ib_mad.h b/include/rdma/ib_mad.h
index 188df91d5851..ec9b44dd3d80 100644
--- a/include/rdma/ib_mad.h
+++ b/include/rdma/ib_mad.h
@@ -237,6 +237,8 @@ struct ib_vendor_mad {
u8 data[IB_MGMT_VENDOR_DATA];
};
+#define IB_MGMT_CLASSPORTINFO_ATTR_ID cpu_to_be16(0x0001)
+
struct ib_class_port_info {
u8 base_version;
u8 class_version;
diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h
index 9a68a19532ba..120da1d7f57e 100644
--- a/include/rdma/ib_verbs.h
+++ b/include/rdma/ib_verbs.h
@@ -1271,6 +1271,7 @@ struct ib_uobject {
int id; /* index into kernel idr */
struct kref ref;
struct rw_semaphore mutex; /* protects .live */
+ struct rcu_head rcu; /* kfree_rcu() overhead */
int live;
};
diff --git a/include/scsi/sas.h b/include/scsi/sas.h
index 0d2607d12387..42a84ef42683 100644
--- a/include/scsi/sas.h
+++ b/include/scsi/sas.h
@@ -344,6 +344,43 @@ struct ssp_response_iu {
u8 sense_data[0];
} __attribute__ ((packed));
+struct ssp_command_iu {
+ u8 lun[8];
+ u8 _r_a;
+
+ union {
+ struct {
+ u8 attr:3;
+ u8 prio:4;
+ u8 efb:1;
+ };
+ u8 efb_prio_attr;
+ };
+
+ u8 _r_b;
+
+ u8 _r_c:2;
+ u8 add_cdb_len:6;
+
+ u8 cdb[16];
+ u8 add_cdb[0];
+} __attribute__ ((packed));
+
+struct xfer_rdy_iu {
+ __be32 requested_offset;
+ __be32 write_data_len;
+ __be32 _r_a;
+} __attribute__ ((packed));
+
+struct ssp_tmf_iu {
+ u8 lun[8];
+ u16 _r_a;
+ u8 tmf;
+ u8 _r_b;
+ __be16 tag;
+ u8 _r_c[14];
+} __attribute__ ((packed));
+
/* ---------- SMP ---------- */
struct report_general_resp {
@@ -538,6 +575,43 @@ struct ssp_response_iu {
u8 sense_data[0];
} __attribute__ ((packed));
+struct ssp_command_iu {
+ u8 lun[8];
+ u8 _r_a;
+
+ union {
+ struct {
+ u8 efb:1;
+ u8 prio:4;
+ u8 attr:3;
+ };
+ u8 efb_prio_attr;
+ };
+
+ u8 _r_b;
+
+ u8 add_cdb_len:6;
+ u8 _r_c:2;
+
+ u8 cdb[16];
+ u8 add_cdb[0];
+} __attribute__ ((packed));
+
+struct xfer_rdy_iu {
+ __be32 requested_offset;
+ __be32 write_data_len;
+ __be32 _r_a;
+} __attribute__ ((packed));
+
+struct ssp_tmf_iu {
+ u8 lun[8];
+ u16 _r_a;
+ u8 tmf;
+ u8 _r_b;
+ __be16 tag;
+ u8 _r_c[14];
+} __attribute__ ((packed));
+
/* ---------- SMP ---------- */
struct report_general_resp {
diff --git a/include/scsi/scsi_dbg.h b/include/scsi/scsi_dbg.h
index f8170e90b49d..56710e03101c 100644
--- a/include/scsi/scsi_dbg.h
+++ b/include/scsi/scsi_dbg.h
@@ -12,8 +12,6 @@ extern size_t __scsi_format_command(char *, size_t,
const unsigned char *, size_t);
extern void scsi_show_extd_sense(const struct scsi_device *, const char *,
unsigned char, unsigned char);
-extern void scsi_show_sense_hdr(const struct scsi_device *, const char *,
- const struct scsi_sense_hdr *);
extern void scsi_print_sense_hdr(const struct scsi_device *, const char *,
const struct scsi_sense_hdr *);
extern void scsi_print_sense(const struct scsi_cmnd *);
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index fe89d7cd67b9..f63a16760ae9 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -109,6 +109,7 @@ struct scsi_device {
char type;
char scsi_level;
char inq_periph_qual; /* PQ from INQUIRY data */
+ struct mutex inquiry_mutex;
unsigned char inquiry_len; /* valid bytes in 'inquiry' */
unsigned char * inquiry; /* INQUIRY response data */
const char * vendor; /* [back_compat] point into 'inquiry' ... */
@@ -117,9 +118,9 @@ struct scsi_device {
#define SCSI_VPD_PG_LEN 255
int vpd_pg83_len;
- unsigned char *vpd_pg83;
+ unsigned char __rcu *vpd_pg83;
int vpd_pg80_len;
- unsigned char *vpd_pg80;
+ unsigned char __rcu *vpd_pg80;
unsigned char current_tag; /* current tag */
struct scsi_target *sdev_target; /* used only for single_lun */
@@ -414,6 +415,8 @@ static inline int scsi_execute_req(struct scsi_device *sdev,
}
extern void sdev_disable_disk_events(struct scsi_device *sdev);
extern void sdev_enable_disk_events(struct scsi_device *sdev);
+extern int scsi_vpd_lun_id(struct scsi_device *, char *, size_t);
+extern int scsi_vpd_tpg_id(struct scsi_device *, int *);
#ifdef CONFIG_PM
extern int scsi_autopm_get_device(struct scsi_device *);
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
index ed527121031d..fcfa3d7f5e7e 100644
--- a/include/scsi/scsi_host.h
+++ b/include/scsi/scsi_host.h
@@ -668,6 +668,9 @@ struct Scsi_Host {
unsigned use_blk_mq:1;
unsigned use_cmd_list:1;
+ /* Host responded with short (<36 bytes) INQUIRY result */
+ unsigned short_inquiry:1;
+
/*
* Optional work queue to be utilized by the transport
*/
diff --git a/include/scsi/scsi_transport_sas.h b/include/scsi/scsi_transport_sas.h
index 0bd71e2702e3..13c0b2ba1b6c 100644
--- a/include/scsi/scsi_transport_sas.h
+++ b/include/scsi/scsi_transport_sas.h
@@ -10,6 +10,15 @@ struct scsi_transport_template;
struct sas_rphy;
struct request;
+#if !IS_ENABLED(CONFIG_SCSI_SAS_ATTRS)
+static inline int is_sas_attached(struct scsi_device *sdev)
+{
+ return 0;
+}
+#else
+extern int is_sas_attached(struct scsi_device *sdev);
+#endif
+
static inline int sas_protocol_ata(enum sas_protocol proto)
{
return ((proto & SAS_PROTOCOL_SATA) ||
@@ -180,6 +189,7 @@ extern int sas_phy_add(struct sas_phy *);
extern void sas_phy_delete(struct sas_phy *);
extern int scsi_is_sas_phy(const struct device *);
+u64 sas_get_address(struct scsi_device *);
unsigned int sas_tlr_supported(struct scsi_device *);
unsigned int sas_is_tlr_enabled(struct scsi_device *);
void sas_disable_tlr(struct scsi_device *);
diff --git a/include/sound/hda_register.h b/include/sound/hda_register.h
index 2ae8812d7b1a..94dc6a9772e0 100644
--- a/include/sound/hda_register.h
+++ b/include/sound/hda_register.h
@@ -93,6 +93,9 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
#define AZX_REG_HSW_EM4 0x100c
#define AZX_REG_HSW_EM5 0x1010
+/* Skylake/Broxton display HD-A controller Extended Mode registers */
+#define AZX_REG_SKL_EM4L 0x1040
+
/* PCI space */
#define AZX_PCIREG_TCSEL 0x44
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index 7855cfe46b69..95a937eafb79 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -398,6 +398,7 @@ int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm,
int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route, int num);
void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w);
+void snd_soc_dapm_reset_cache(struct snd_soc_dapm_context *dapm);
/* dapm events */
void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
diff --git a/include/sound/soc.h b/include/sound/soc.h
index a8b4b9c8b1d2..fb955e69a78e 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -1655,7 +1655,7 @@ extern const struct dev_pm_ops snd_soc_pm_ops;
/* Helper functions */
static inline void snd_soc_dapm_mutex_lock(struct snd_soc_dapm_context *dapm)
{
- mutex_lock(&dapm->card->dapm_mutex);
+ mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
}
static inline void snd_soc_dapm_mutex_unlock(struct snd_soc_dapm_context *dapm)
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index 0a2c74008e53..aabf0aca0171 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -474,7 +474,7 @@ struct se_cmd {
struct completion cmd_wait_comp;
const struct target_core_fabric_ops *se_tfo;
sense_reason_t (*execute_cmd)(struct se_cmd *);
- sense_reason_t (*transport_complete_callback)(struct se_cmd *, bool);
+ sense_reason_t (*transport_complete_callback)(struct se_cmd *, bool, int *);
void *protocol_data;
unsigned char *t_task_cdb;
diff --git a/include/trace/define_trace.h b/include/trace/define_trace.h
index 2d8639ea64d5..6e3945f64102 100644
--- a/include/trace/define_trace.h
+++ b/include/trace/define_trace.h
@@ -40,6 +40,11 @@
assign, print, reg, unreg) \
DEFINE_TRACE_FN(name, reg, unreg)
+#undef TRACE_EVENT_FN_COND
+#define TRACE_EVENT_FN_COND(name, proto, args, cond, tstruct, \
+ assign, print, reg, unreg) \
+ DEFINE_TRACE_FN(name, reg, unreg)
+
#undef DEFINE_EVENT
#define DEFINE_EVENT(template, name, proto, args) \
DEFINE_TRACE(name)
@@ -93,6 +98,7 @@
#undef TRACE_EVENT
#undef TRACE_EVENT_FN
+#undef TRACE_EVENT_FN_COND
#undef TRACE_EVENT_CONDITION
#undef DECLARE_EVENT_CLASS
#undef DEFINE_EVENT
diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h
index 00b4a6308249..a1b488809f06 100644
--- a/include/trace/events/f2fs.h
+++ b/include/trace/events/f2fs.h
@@ -1265,6 +1265,44 @@ TRACE_EVENT(f2fs_destroy_extent_tree,
__entry->node_cnt)
);
+DECLARE_EVENT_CLASS(f2fs_sync_dirty_inodes,
+
+ TP_PROTO(struct super_block *sb, int type, int count),
+
+ TP_ARGS(sb, type, count),
+
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(int, type)
+ __field(int, count)
+ ),
+
+ TP_fast_assign(
+ __entry->dev = sb->s_dev;
+ __entry->type = type;
+ __entry->count = count;
+ ),
+
+ TP_printk("dev = (%d,%d), %s, dirty count = %d",
+ show_dev(__entry),
+ show_file_type(__entry->type),
+ __entry->count)
+);
+
+DEFINE_EVENT(f2fs_sync_dirty_inodes, f2fs_sync_dirty_inodes_enter,
+
+ TP_PROTO(struct super_block *sb, int type, int count),
+
+ TP_ARGS(sb, type, count)
+);
+
+DEFINE_EVENT(f2fs_sync_dirty_inodes, f2fs_sync_dirty_inodes_exit,
+
+ TP_PROTO(struct super_block *sb, int type, int count),
+
+ TP_ARGS(sb, type, count)
+);
+
#endif /* _TRACE_F2FS_H */
/* This part must be outside protection */
diff --git a/include/trace/events/fib6.h b/include/trace/events/fib6.h
new file mode 100644
index 000000000000..4cf6bac4686d
--- /dev/null
+++ b/include/trace/events/fib6.h
@@ -0,0 +1,76 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM fib6
+
+#if !defined(_TRACE_FIB6_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_FIB6_H
+
+#include <linux/in6.h>
+#include <net/flow.h>
+#include <net/ip6_fib.h>
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(fib6_table_lookup,
+
+ TP_PROTO(const struct net *net, const struct rt6_info *rt,
+ u32 tb_id, const struct flowi6 *flp),
+
+ TP_ARGS(net, rt, tb_id, flp),
+
+ TP_STRUCT__entry(
+ __field( u32, tb_id )
+
+ __field( int, oif )
+ __field( int, iif )
+ __field( __u8, tos )
+ __field( __u8, scope )
+ __field( __u8, flags )
+ __array( __u8, src, 16 )
+ __array( __u8, dst, 16 )
+
+ __dynamic_array( char, name, IFNAMSIZ )
+ __array( __u8, gw, 16 )
+ ),
+
+ TP_fast_assign(
+ struct in6_addr *in6;
+
+ __entry->tb_id = tb_id;
+ __entry->oif = flp->flowi6_oif;
+ __entry->iif = flp->flowi6_iif;
+ __entry->tos = flp->flowi6_tos;
+ __entry->scope = flp->flowi6_scope;
+ __entry->flags = flp->flowi6_flags;
+
+ in6 = (struct in6_addr *)__entry->src;
+ *in6 = flp->saddr;
+
+ in6 = (struct in6_addr *)__entry->dst;
+ *in6 = flp->daddr;
+
+ if (rt->rt6i_idev) {
+ __assign_str(name, rt->rt6i_idev->dev->name);
+ } else {
+ __assign_str(name, "");
+ }
+ if (rt == net->ipv6.ip6_null_entry) {
+ struct in6_addr in6_zero = {};
+
+ in6 = (struct in6_addr *)__entry->gw;
+ *in6 = in6_zero;
+
+ } else if (rt) {
+ in6 = (struct in6_addr *)__entry->gw;
+ *in6 = rt->rt6i_gateway;
+ }
+ ),
+
+ TP_printk("table %3u oif %d iif %d src %pI6c dst %pI6c tos %d scope %d flags %x ==> dev %s gw %pI6c",
+ __entry->tb_id, __entry->oif, __entry->iif,
+ __entry->src, __entry->dst, __entry->tos, __entry->scope,
+ __entry->flags, __get_str(name), __entry->gw)
+);
+
+#endif /* _TRACE_FIB6_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/trace/events/filelock.h b/include/trace/events/filelock.h
index c72f2dc01d0b..63a7680347cb 100644
--- a/include/trace/events/filelock.h
+++ b/include/trace/events/filelock.h
@@ -34,6 +34,83 @@
{ F_WRLCK, "F_WRLCK" }, \
{ F_UNLCK, "F_UNLCK" })
+TRACE_EVENT(locks_get_lock_context,
+ TP_PROTO(struct inode *inode, int type, struct file_lock_context *ctx),
+
+ TP_ARGS(inode, type, ctx),
+
+ TP_STRUCT__entry(
+ __field(unsigned long, i_ino)
+ __field(dev_t, s_dev)
+ __field(unsigned char, type)
+ __field(struct file_lock_context *, ctx)
+ ),
+
+ TP_fast_assign(
+ __entry->s_dev = inode->i_sb->s_dev;
+ __entry->i_ino = inode->i_ino;
+ __entry->type = type;
+ __entry->ctx = ctx;
+ ),
+
+ TP_printk("dev=0x%x:0x%x ino=0x%lx type=%s ctx=%p",
+ MAJOR(__entry->s_dev), MINOR(__entry->s_dev),
+ __entry->i_ino, show_fl_type(__entry->type), __entry->ctx)
+);
+
+DECLARE_EVENT_CLASS(filelock_lock,
+ TP_PROTO(struct inode *inode, struct file_lock *fl, int ret),
+
+ TP_ARGS(inode, fl, ret),
+
+ TP_STRUCT__entry(
+ __field(struct file_lock *, fl)
+ __field(unsigned long, i_ino)
+ __field(dev_t, s_dev)
+ __field(struct file_lock *, fl_next)
+ __field(fl_owner_t, fl_owner)
+ __field(unsigned int, fl_pid)
+ __field(unsigned int, fl_flags)
+ __field(unsigned char, fl_type)
+ __field(loff_t, fl_start)
+ __field(loff_t, fl_end)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ __entry->fl = fl ? fl : NULL;
+ __entry->s_dev = inode->i_sb->s_dev;
+ __entry->i_ino = inode->i_ino;
+ __entry->fl_next = fl ? fl->fl_next : NULL;
+ __entry->fl_owner = fl ? fl->fl_owner : NULL;
+ __entry->fl_pid = fl ? fl->fl_pid : 0;
+ __entry->fl_flags = fl ? fl->fl_flags : 0;
+ __entry->fl_type = fl ? fl->fl_type : 0;
+ __entry->fl_start = fl ? fl->fl_start : 0;
+ __entry->fl_end = fl ? fl->fl_end : 0;
+ __entry->ret = ret;
+ ),
+
+ TP_printk("fl=0x%p dev=0x%x:0x%x ino=0x%lx fl_next=0x%p fl_owner=0x%p fl_pid=%u fl_flags=%s fl_type=%s fl_start=%lld fl_end=%lld ret=%d",
+ __entry->fl, MAJOR(__entry->s_dev), MINOR(__entry->s_dev),
+ __entry->i_ino, __entry->fl_next, __entry->fl_owner,
+ __entry->fl_pid, show_fl_flags(__entry->fl_flags),
+ show_fl_type(__entry->fl_type),
+ __entry->fl_start, __entry->fl_end, __entry->ret)
+);
+
+DEFINE_EVENT(filelock_lock, posix_lock_inode,
+ TP_PROTO(struct inode *inode, struct file_lock *fl, int ret),
+ TP_ARGS(inode, fl, ret));
+
+DEFINE_EVENT(filelock_lock, fcntl_setlk,
+ TP_PROTO(struct inode *inode, struct file_lock *fl, int ret),
+ TP_ARGS(inode, fl, ret));
+
+DEFINE_EVENT(filelock_lock, locks_remove_posix,
+ TP_PROTO(struct inode *inode, struct file_lock *fl, int ret),
+ TP_ARGS(inode, fl, ret));
+
DECLARE_EVENT_CLASS(filelock_lease,
TP_PROTO(struct inode *inode, struct file_lock *fl),
diff --git a/include/trace/events/huge_memory.h b/include/trace/events/huge_memory.h
new file mode 100644
index 000000000000..97d635cabac8
--- /dev/null
+++ b/include/trace/events/huge_memory.h
@@ -0,0 +1,136 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM huge_memory
+
+#if !defined(__HUGE_MEMORY_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __HUGE_MEMORY_H
+
+#include <linux/tracepoint.h>
+
+#include <trace/events/gfpflags.h>
+
+#define SCAN_STATUS \
+ EM( SCAN_FAIL, "failed") \
+ EM( SCAN_SUCCEED, "succeeded") \
+ EM( SCAN_PMD_NULL, "pmd_null") \
+ EM( SCAN_EXCEED_NONE_PTE, "exceed_none_pte") \
+ EM( SCAN_PTE_NON_PRESENT, "pte_non_present") \
+ EM( SCAN_PAGE_RO, "no_writable_page") \
+ EM( SCAN_NO_REFERENCED_PAGE, "no_referenced_page") \
+ EM( SCAN_PAGE_NULL, "page_null") \
+ EM( SCAN_SCAN_ABORT, "scan_aborted") \
+ EM( SCAN_PAGE_COUNT, "not_suitable_page_count") \
+ EM( SCAN_PAGE_LRU, "page_not_in_lru") \
+ EM( SCAN_PAGE_LOCK, "page_locked") \
+ EM( SCAN_PAGE_ANON, "page_not_anon") \
+ EM( SCAN_ANY_PROCESS, "no_process_for_page") \
+ EM( SCAN_VMA_NULL, "vma_null") \
+ EM( SCAN_VMA_CHECK, "vma_check_failed") \
+ EM( SCAN_ADDRESS_RANGE, "not_suitable_address_range") \
+ EM( SCAN_SWAP_CACHE_PAGE, "page_swap_cache") \
+ EM( SCAN_DEL_PAGE_LRU, "could_not_delete_page_from_lru")\
+ EM( SCAN_ALLOC_HUGE_PAGE_FAIL, "alloc_huge_page_failed") \
+ EMe( SCAN_CGROUP_CHARGE_FAIL, "ccgroup_charge_failed")
+
+#undef EM
+#undef EMe
+#define EM(a, b) TRACE_DEFINE_ENUM(a);
+#define EMe(a, b) TRACE_DEFINE_ENUM(a);
+
+SCAN_STATUS
+
+#undef EM
+#undef EMe
+#define EM(a, b) {a, b},
+#define EMe(a, b) {a, b}
+
+TRACE_EVENT(mm_khugepaged_scan_pmd,
+
+ TP_PROTO(struct mm_struct *mm, unsigned long pfn, bool writable,
+ bool referenced, int none_or_zero, int status),
+
+ TP_ARGS(mm, pfn, writable, referenced, none_or_zero, status),
+
+ TP_STRUCT__entry(
+ __field(struct mm_struct *, mm)
+ __field(unsigned long, pfn)
+ __field(bool, writable)
+ __field(bool, referenced)
+ __field(int, none_or_zero)
+ __field(int, status)
+ ),
+
+ TP_fast_assign(
+ __entry->mm = mm;
+ __entry->pfn = pfn;
+ __entry->writable = writable;
+ __entry->referenced = referenced;
+ __entry->none_or_zero = none_or_zero;
+ __entry->status = status;
+ ),
+
+ TP_printk("mm=%p, scan_pfn=0x%lx, writable=%d, referenced=%d, none_or_zero=%d, status=%s",
+ __entry->mm,
+ __entry->pfn,
+ __entry->writable,
+ __entry->referenced,
+ __entry->none_or_zero,
+ __print_symbolic(__entry->status, SCAN_STATUS))
+);
+
+TRACE_EVENT(mm_collapse_huge_page,
+
+ TP_PROTO(struct mm_struct *mm, int isolated, int status),
+
+ TP_ARGS(mm, isolated, status),
+
+ TP_STRUCT__entry(
+ __field(struct mm_struct *, mm)
+ __field(int, isolated)
+ __field(int, status)
+ ),
+
+ TP_fast_assign(
+ __entry->mm = mm;
+ __entry->isolated = isolated;
+ __entry->status = status;
+ ),
+
+ TP_printk("mm=%p, isolated=%d, status=%s",
+ __entry->mm,
+ __entry->isolated,
+ __print_symbolic(__entry->status, SCAN_STATUS))
+);
+
+TRACE_EVENT(mm_collapse_huge_page_isolate,
+
+ TP_PROTO(unsigned long pfn, int none_or_zero,
+ bool referenced, bool writable, int status),
+
+ TP_ARGS(pfn, none_or_zero, referenced, writable, status),
+
+ TP_STRUCT__entry(
+ __field(unsigned long, pfn)
+ __field(int, none_or_zero)
+ __field(bool, referenced)
+ __field(bool, writable)
+ __field(int, status)
+ ),
+
+ TP_fast_assign(
+ __entry->pfn = pfn;
+ __entry->none_or_zero = none_or_zero;
+ __entry->referenced = referenced;
+ __entry->writable = writable;
+ __entry->status = status;
+ ),
+
+ TP_printk("scan_pfn=0x%lx, none_or_zero=%d, referenced=%d, writable=%d, status=%s",
+ __entry->pfn,
+ __entry->none_or_zero,
+ __entry->referenced,
+ __entry->writable,
+ __print_symbolic(__entry->status, SCAN_STATUS))
+);
+
+#endif /* __HUGE_MEMORY_H */
+#include <trace/define_trace.h>
diff --git a/include/trace/events/page_isolation.h b/include/trace/events/page_isolation.h
new file mode 100644
index 000000000000..6fb644029c80
--- /dev/null
+++ b/include/trace/events/page_isolation.h
@@ -0,0 +1,38 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM page_isolation
+
+#if !defined(_TRACE_PAGE_ISOLATION_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_PAGE_ISOLATION_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(test_pages_isolated,
+
+ TP_PROTO(
+ unsigned long start_pfn,
+ unsigned long end_pfn,
+ unsigned long fin_pfn),
+
+ TP_ARGS(start_pfn, end_pfn, fin_pfn),
+
+ TP_STRUCT__entry(
+ __field(unsigned long, start_pfn)
+ __field(unsigned long, end_pfn)
+ __field(unsigned long, fin_pfn)
+ ),
+
+ TP_fast_assign(
+ __entry->start_pfn = start_pfn;
+ __entry->end_pfn = end_pfn;
+ __entry->fin_pfn = fin_pfn;
+ ),
+
+ TP_printk("start_pfn=0x%lx end_pfn=0x%lx fin_pfn=0x%lx ret=%s",
+ __entry->start_pfn, __entry->end_pfn, __entry->fin_pfn,
+ __entry->end_pfn == __entry->fin_pfn ? "success" : "fail")
+);
+
+#endif /* _TRACE_PAGE_ISOLATION_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/trace/events/v4l2.h b/include/trace/events/v4l2.h
index 22afa26e34b2..ee7754c6e4a1 100644
--- a/include/trace/events/v4l2.h
+++ b/include/trace/events/v4l2.h
@@ -184,7 +184,7 @@ DECLARE_EVENT_CLASS(vb2_v4l2_event_class,
__field(int, minor)
__field(u32, flags)
__field(u32, field)
- __field(s64, timestamp)
+ __field(u64, timestamp)
__field(u32, timecode_type)
__field(u32, timecode_flags)
__field(u8, timecode_frames)
@@ -205,7 +205,7 @@ DECLARE_EVENT_CLASS(vb2_v4l2_event_class,
__entry->minor = owner ? owner->vdev->minor : -1;
__entry->flags = vbuf->flags;
__entry->field = vbuf->field;
- __entry->timestamp = timeval_to_ns(&vbuf->timestamp);
+ __entry->timestamp = vb->timestamp;
__entry->timecode_type = vbuf->timecode.type;
__entry->timecode_flags = vbuf->timecode.flags;
__entry->timecode_frames = vbuf->timecode.frames;
diff --git a/include/trace/events/vb2.h b/include/trace/events/vb2.h
index bfeceeba3744..c1a22416ed05 100644
--- a/include/trace/events/vb2.h
+++ b/include/trace/events/vb2.h
@@ -18,6 +18,7 @@ DECLARE_EVENT_CLASS(vb2_event_class,
__field(u32, index)
__field(u32, type)
__field(u32, bytesused)
+ __field(u64, timestamp)
),
TP_fast_assign(
@@ -28,14 +29,16 @@ DECLARE_EVENT_CLASS(vb2_event_class,
__entry->index = vb->index;
__entry->type = vb->type;
__entry->bytesused = vb->planes[0].bytesused;
+ __entry->timestamp = vb->timestamp;
),
TP_printk("owner = %p, queued = %u, owned_by_drv = %d, index = %u, "
- "type = %u, bytesused = %u", __entry->owner,
+ "type = %u, bytesused = %u, timestamp = %llu", __entry->owner,
__entry->queued_count,
__entry->owned_by_drv_count,
__entry->index, __entry->type,
- __entry->bytesused
+ __entry->bytesused,
+ __entry->timestamp
)
)
diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h
index f66476b96264..31763dd8db1c 100644
--- a/include/trace/events/vmscan.h
+++ b/include/trace/events/vmscan.h
@@ -330,10 +330,9 @@ DEFINE_EVENT(mm_vmscan_lru_isolate_template, mm_vmscan_memcg_isolate,
TRACE_EVENT(mm_vmscan_writepage,
- TP_PROTO(struct page *page,
- int reclaim_flags),
+ TP_PROTO(struct page *page),
- TP_ARGS(page, reclaim_flags),
+ TP_ARGS(page),
TP_STRUCT__entry(
__field(unsigned long, pfn)
@@ -342,7 +341,7 @@ TRACE_EVENT(mm_vmscan_writepage,
TP_fast_assign(
__entry->pfn = page_to_pfn(page);
- __entry->reclaim_flags = reclaim_flags;
+ __entry->reclaim_flags = trace_reclaim_flags(page);
),
TP_printk("page=%p pfn=%lu flags=%s",
@@ -353,11 +352,11 @@ TRACE_EVENT(mm_vmscan_writepage,
TRACE_EVENT(mm_vmscan_lru_shrink_inactive,
- TP_PROTO(int nid, int zid,
- unsigned long nr_scanned, unsigned long nr_reclaimed,
- int priority, int reclaim_flags),
+ TP_PROTO(struct zone *zone,
+ unsigned long nr_scanned, unsigned long nr_reclaimed,
+ int priority, int file),
- TP_ARGS(nid, zid, nr_scanned, nr_reclaimed, priority, reclaim_flags),
+ TP_ARGS(zone, nr_scanned, nr_reclaimed, priority, file),
TP_STRUCT__entry(
__field(int, nid)
@@ -369,12 +368,12 @@ TRACE_EVENT(mm_vmscan_lru_shrink_inactive,
),
TP_fast_assign(
- __entry->nid = nid;
- __entry->zid = zid;
+ __entry->nid = zone_to_nid(zone);
+ __entry->zid = zone_idx(zone);
__entry->nr_scanned = nr_scanned;
__entry->nr_reclaimed = nr_reclaimed;
__entry->priority = priority;
- __entry->reclaim_flags = reclaim_flags;
+ __entry->reclaim_flags = trace_shrink_flags(file);
),
TP_printk("nid=%d zid=%d nr_scanned=%ld nr_reclaimed=%ld priority=%d flags=%s",
diff --git a/include/trace/trace_events.h b/include/trace/trace_events.h
index de996cf61053..170c93bbdbb7 100644
--- a/include/trace/trace_events.h
+++ b/include/trace/trace_events.h
@@ -123,6 +123,12 @@ TRACE_MAKE_SYSTEM_STR();
TRACE_EVENT(name, PARAMS(proto), PARAMS(args), \
PARAMS(tstruct), PARAMS(assign), PARAMS(print)) \
+#undef TRACE_EVENT_FN_COND
+#define TRACE_EVENT_FN_COND(name, proto, args, cond, tstruct, \
+ assign, print, reg, unreg) \
+ TRACE_EVENT_CONDITION(name, PARAMS(proto), PARAMS(args), PARAMS(cond), \
+ PARAMS(tstruct), PARAMS(assign), PARAMS(print)) \
+
#undef TRACE_EVENT_FLAGS
#define TRACE_EVENT_FLAGS(name, value) \
__TRACE_EVENT_FLAGS(name, value)
diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h
index 5c15c2a5c123..fb8a41668382 100644
--- a/include/uapi/asm-generic/socket.h
+++ b/include/uapi/asm-generic/socket.h
@@ -87,4 +87,7 @@
#define SO_ATTACH_BPF 50
#define SO_DETACH_BPF SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+
#endif /* __ASM_GENERIC_SOCKET_H */
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
index 1324b0292ec2..2622b33fb2ec 100644
--- a/include/uapi/asm-generic/unistd.h
+++ b/include/uapi/asm-generic/unistd.h
@@ -715,9 +715,11 @@ __SYSCALL(__NR_userfaultfd, sys_userfaultfd)
__SYSCALL(__NR_membarrier, sys_membarrier)
#define __NR_mlock2 284
__SYSCALL(__NR_mlock2, sys_mlock2)
+#define __NR_copy_file_range 285
+__SYSCALL(__NR_copy_file_range, sys_copy_file_range)
#undef __NR_syscalls
-#define __NR_syscalls 285
+#define __NR_syscalls 286
/*
* All syscalls below here should go away really,
diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h
index 0b69a7753558..ee2d542c65f5 100644
--- a/include/uapi/drm/drm_fourcc.h
+++ b/include/uapi/drm/drm_fourcc.h
@@ -225,7 +225,7 @@
* - multiple of 128 pixels for the width
* - multiple of 32 pixels for the height
*
- * For more information: see http://linuxtv.org/downloads/v4l-dvb-apis/re32.html
+ * For more information: see https://linuxtv.org/downloads/v4l-dvb-apis/re32.html
*/
#define DRM_FORMAT_MOD_SAMSUNG_64_32_TILE fourcc_mod_code(SAMSUNG, 1)
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 628e6e64c2fb..c2e5d6cb34e3 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -186,6 +186,7 @@ header-y += if_tunnel.h
header-y += if_vlan.h
header-y += if_x25.h
header-y += igmp.h
+header-y += ila.h
header-y += in6.h
header-y += inet_diag.h
header-y += in.h
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 9ea2d22fa2cb..aa6f8571de13 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -269,9 +269,29 @@ enum bpf_func_id {
* Return: 0 on success
*/
BPF_FUNC_perf_event_output,
+ BPF_FUNC_skb_load_bytes,
__BPF_FUNC_MAX_ID,
};
+/* All flags used by eBPF helper functions, placed here. */
+
+/* BPF_FUNC_skb_store_bytes flags. */
+#define BPF_F_RECOMPUTE_CSUM (1ULL << 0)
+
+/* BPF_FUNC_l3_csum_replace and BPF_FUNC_l4_csum_replace flags.
+ * First 4 bits are for passing the header field size.
+ */
+#define BPF_F_HDR_FIELD_MASK 0xfULL
+
+/* BPF_FUNC_l4_csum_replace flags. */
+#define BPF_F_PSEUDO_HDR (1ULL << 4)
+
+/* BPF_FUNC_clone_redirect and BPF_FUNC_redirect flags. */
+#define BPF_F_INGRESS (1ULL << 0)
+
+/* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */
+#define BPF_F_TUNINFO_IPV6 (1ULL << 0)
+
/* user accessible mirror of in-kernel sk_buff.
* new fields can only be added to the end of this structure
*/
@@ -295,7 +315,12 @@ struct __sk_buff {
struct bpf_tunnel_key {
__u32 tunnel_id;
- __u32 remote_ipv4;
+ union {
+ __u32 remote_ipv4;
+ __u32 remote_ipv6[4];
+ };
+ __u8 tunnel_tos;
+ __u8 tunnel_ttl;
};
#endif /* _UAPI__LINUX_BPF_H__ */
diff --git a/include/uapi/linux/dvb/video.h b/include/uapi/linux/dvb/video.h
index d3d14a59d2d5..49392564f9d6 100644
--- a/include/uapi/linux/dvb/video.h
+++ b/include/uapi/linux/dvb/video.h
@@ -26,7 +26,6 @@
#include <linux/types.h>
#ifndef __KERNEL__
-#include <stdint.h>
#include <time.h>
#endif
diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h
index cd1629170103..57fa39005e79 100644
--- a/include/uapi/linux/ethtool.h
+++ b/include/uapi/linux/ethtool.h
@@ -542,6 +542,7 @@ struct ethtool_pauseparam {
* now deprecated
* @ETH_SS_FEATURES: Device feature names
* @ETH_SS_RSS_HASH_FUNCS: RSS hush function names
+ * @ETH_SS_PHY_STATS: Statistic names, for use with %ETHTOOL_GPHYSTATS
*/
enum ethtool_stringset {
ETH_SS_TEST = 0,
@@ -551,6 +552,7 @@ enum ethtool_stringset {
ETH_SS_FEATURES,
ETH_SS_RSS_HASH_FUNCS,
ETH_SS_TUNABLES,
+ ETH_SS_PHY_STATS,
};
/**
@@ -1225,6 +1227,7 @@ enum ethtool_sfeatures_retval_bits {
#define ETHTOOL_SRSSH 0x00000047 /* Set RX flow hash configuration */
#define ETHTOOL_GTUNABLE 0x00000048 /* Get tunable configuration */
#define ETHTOOL_STUNABLE 0x00000049 /* Set tunable configuration */
+#define ETHTOOL_GPHYSTATS 0x0000004a /* get PHY-specific statistics */
/* compatibility with older code */
#define SPARC_ETH_GSET ETHTOOL_GSET
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index f15d980249b5..b8a5b3b86ad6 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -39,12 +39,48 @@
#define RENAME_EXCHANGE (1 << 1) /* Exchange source and dest */
#define RENAME_WHITEOUT (1 << 2) /* Whiteout source */
+struct file_clone_range {
+ __s64 src_fd;
+ __u64 src_offset;
+ __u64 src_length;
+ __u64 dest_offset;
+};
+
struct fstrim_range {
__u64 start;
__u64 len;
__u64 minlen;
};
+/* extent-same (dedupe) ioctls; these MUST match the btrfs ioctl definitions */
+#define FILE_DEDUPE_RANGE_SAME 0
+#define FILE_DEDUPE_RANGE_DIFFERS 1
+
+/* from struct btrfs_ioctl_file_extent_same_info */
+struct file_dedupe_range_info {
+ __s64 dest_fd; /* in - destination file */
+ __u64 dest_offset; /* in - start of extent in destination */
+ __u64 bytes_deduped; /* out - total # of bytes we were able
+ * to dedupe from this file. */
+ /* status of this dedupe operation:
+ * < 0 for error
+ * == FILE_DEDUPE_RANGE_SAME if dedupe succeeds
+ * == FILE_DEDUPE_RANGE_DIFFERS if data differs
+ */
+ __s32 status; /* out - see above description */
+ __u32 reserved; /* must be zero */
+};
+
+/* from struct btrfs_ioctl_file_extent_same_args */
+struct file_dedupe_range {
+ __u64 src_offset; /* in - start of extent in source */
+ __u64 src_length; /* in - length of extent */
+ __u16 dest_count; /* in - total elements in info array */
+ __u16 reserved1; /* must be zero */
+ __u32 reserved2; /* must be zero */
+ struct file_dedupe_range_info info[0];
+};
+
/* And dynamically-tunable limits and defaults: */
struct files_stat_struct {
unsigned long nr_files; /* read only */
@@ -152,6 +188,8 @@ struct inodes_stat_t {
#define BLKSECDISCARD _IO(0x12,125)
#define BLKROTATIONAL _IO(0x12,126)
#define BLKZEROOUT _IO(0x12,127)
+#define BLKDAXSET _IO(0x12,128)
+#define BLKDAXGET _IO(0x12,129)
#define BMAP_IOCTL 1 /* obsolete - kept for compatibility */
#define FIBMAP _IO(0x00,1) /* bmap access */
@@ -159,6 +197,9 @@ struct inodes_stat_t {
#define FIFREEZE _IOWR('X', 119, int) /* Freeze */
#define FITHAW _IOWR('X', 120, int) /* Thaw */
#define FITRIM _IOWR('X', 121, struct fstrim_range) /* Trim */
+#define FICLONE _IOW(0x94, 9, int)
+#define FICLONERANGE _IOW(0x94, 13, struct file_clone_range)
+#define FIDEDUPERANGE _IOWR(0x94, 54, struct file_dedupe_range)
#define FS_IOC_GETFLAGS _IOR('f', 1, long)
#define FS_IOC_SETFLAGS _IOW('f', 2, long)
diff --git a/include/uapi/linux/gfs2_ondisk.h b/include/uapi/linux/gfs2_ondisk.h
index 1a763eaae0bb..7c4be7711c81 100644
--- a/include/uapi/linux/gfs2_ondisk.h
+++ b/include/uapi/linux/gfs2_ondisk.h
@@ -297,6 +297,8 @@ struct gfs2_dinode {
#define GFS2_FNAMESIZE 255
#define GFS2_DIRENT_SIZE(name_len) ((sizeof(struct gfs2_dirent) + (name_len) + 7) & ~7)
+#define GFS2_MIN_DIRENT_SIZE (GFS2_DIRENT_SIZE(1))
+
struct gfs2_dirent {
struct gfs2_inum de_inum;
@@ -304,11 +306,12 @@ struct gfs2_dirent {
__be16 de_rec_len;
__be16 de_name_len;
__be16 de_type;
+ __be16 de_rahead;
union {
- __u8 __pad[14];
+ __u8 __pad[12];
struct {
- __be16 de_rahead;
- __u8 pad2[12];
+ __u32 de_cookie; /* ondisk value not used */
+ __u8 pad3[8];
};
};
};
diff --git a/include/uapi/linux/hyperv.h b/include/uapi/linux/hyperv.h
index e4c0a35d6417..e347b24ef9fb 100644
--- a/include/uapi/linux/hyperv.h
+++ b/include/uapi/linux/hyperv.h
@@ -313,6 +313,7 @@ enum hv_kvp_exchg_pool {
#define HV_INVALIDARG 0x80070057
#define HV_GUID_NOTFOUND 0x80041002
#define HV_ERROR_ALREADY_EXISTS 0x80070050
+#define HV_ERROR_DISK_FULL 0x80070070
#define ADDR_FAMILY_NONE 0x00
#define ADDR_FAMILY_IPV4 0x01
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 5ad57375a99f..a30b78090594 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -218,6 +218,7 @@ enum in6_addr_gen_mode {
IN6_ADDR_GEN_MODE_EUI64,
IN6_ADDR_GEN_MODE_NONE,
IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
+ IN6_ADDR_GEN_MODE_RANDOM,
};
/* Bridge section */
@@ -462,6 +463,9 @@ enum {
IFLA_GENEVE_PORT, /* destination port */
IFLA_GENEVE_COLLECT_METADATA,
IFLA_GENEVE_REMOTE6,
+ IFLA_GENEVE_UDP_CSUM,
+ IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
+ IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
__IFLA_GENEVE_MAX
};
#define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1)
diff --git a/include/uapi/linux/ila.h b/include/uapi/linux/ila.h
index 7ed9e670814e..abde7bbd6f3b 100644
--- a/include/uapi/linux/ila.h
+++ b/include/uapi/linux/ila.h
@@ -3,13 +3,35 @@
#ifndef _UAPI_LINUX_ILA_H
#define _UAPI_LINUX_ILA_H
+/* NETLINK_GENERIC related info */
+#define ILA_GENL_NAME "ila"
+#define ILA_GENL_VERSION 0x1
+
enum {
ILA_ATTR_UNSPEC,
ILA_ATTR_LOCATOR, /* u64 */
+ ILA_ATTR_IDENTIFIER, /* u64 */
+ ILA_ATTR_LOCATOR_MATCH, /* u64 */
+ ILA_ATTR_IFINDEX, /* s32 */
+ ILA_ATTR_DIR, /* u32 */
__ILA_ATTR_MAX,
};
#define ILA_ATTR_MAX (__ILA_ATTR_MAX - 1)
+enum {
+ ILA_CMD_UNSPEC,
+ ILA_CMD_ADD,
+ ILA_CMD_DEL,
+ ILA_CMD_GET,
+
+ __ILA_CMD_MAX,
+};
+
+#define ILA_CMD_MAX (__ILA_CMD_MAX - 1)
+
+#define ILA_DIR_IN (1 << 0)
+#define ILA_DIR_OUT (1 << 1)
+
#endif /* _UAPI_LINUX_ILA_H */
diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h
index 79b12b004ade..318a4828bf98 100644
--- a/include/uapi/linux/in6.h
+++ b/include/uapi/linux/in6.h
@@ -196,6 +196,7 @@ struct in6_flowlabel_req {
#define IPV6_IPSEC_POLICY 34
#define IPV6_XFRM_POLICY 35
+#define IPV6_HDRINCL 36
#endif
/*
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 03f3618612aa..9da905157cee 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -154,6 +154,20 @@ struct kvm_s390_skeys {
__u32 flags;
__u32 reserved[9];
};
+
+struct kvm_hyperv_exit {
+#define KVM_EXIT_HYPERV_SYNIC 1
+ __u32 type;
+ union {
+ struct {
+ __u32 msr;
+ __u64 control;
+ __u64 evt_page;
+ __u64 msg_page;
+ } synic;
+ } u;
+};
+
#define KVM_S390_GET_SKEYS_NONE 1
#define KVM_S390_SKEYS_MAX 1048576
@@ -184,6 +198,7 @@ struct kvm_s390_skeys {
#define KVM_EXIT_SYSTEM_EVENT 24
#define KVM_EXIT_S390_STSI 25
#define KVM_EXIT_IOAPIC_EOI 26
+#define KVM_EXIT_HYPERV 27
/* For KVM_EXIT_INTERNAL_ERROR */
/* Emulate instruction failed. */
@@ -338,6 +353,8 @@ struct kvm_run {
struct {
__u8 vector;
} eoi;
+ /* KVM_EXIT_HYPERV */
+ struct kvm_hyperv_exit hyperv;
/* Fix the size of the union. */
char padding[256];
};
@@ -831,6 +848,8 @@ struct kvm_ppc_smmu_info {
#define KVM_CAP_GUEST_DEBUG_HW_WPS 120
#define KVM_CAP_SPLIT_IRQCHIP 121
#define KVM_CAP_IOEVENTFD_ANY_LENGTH 122
+#define KVM_CAP_HYPERV_SYNIC 123
+#define KVM_CAP_S390_RI 124
#ifdef KVM_CAP_IRQ_ROUTING
@@ -854,10 +873,16 @@ struct kvm_irq_routing_s390_adapter {
__u32 adapter_id;
};
+struct kvm_irq_routing_hv_sint {
+ __u32 vcpu;
+ __u32 sint;
+};
+
/* gsi routing entry types */
#define KVM_IRQ_ROUTING_IRQCHIP 1
#define KVM_IRQ_ROUTING_MSI 2
#define KVM_IRQ_ROUTING_S390_ADAPTER 3
+#define KVM_IRQ_ROUTING_HV_SINT 4
struct kvm_irq_routing_entry {
__u32 gsi;
@@ -868,6 +893,7 @@ struct kvm_irq_routing_entry {
struct kvm_irq_routing_irqchip irqchip;
struct kvm_irq_routing_msi msi;
struct kvm_irq_routing_s390_adapter adapter;
+ struct kvm_irq_routing_hv_sint hv_sint;
__u32 pad[8];
} u;
};
diff --git a/include/uapi/linux/lirc.h b/include/uapi/linux/lirc.h
new file mode 100644
index 000000000000..4b3ab2966b5a
--- /dev/null
+++ b/include/uapi/linux/lirc.h
@@ -0,0 +1,168 @@
+/*
+ * lirc.h - linux infrared remote control header file
+ * last modified 2010/07/13 by Jarod Wilson
+ */
+
+#ifndef _LINUX_LIRC_H
+#define _LINUX_LIRC_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define PULSE_BIT 0x01000000
+#define PULSE_MASK 0x00FFFFFF
+
+#define LIRC_MODE2_SPACE 0x00000000
+#define LIRC_MODE2_PULSE 0x01000000
+#define LIRC_MODE2_FREQUENCY 0x02000000
+#define LIRC_MODE2_TIMEOUT 0x03000000
+
+#define LIRC_VALUE_MASK 0x00FFFFFF
+#define LIRC_MODE2_MASK 0xFF000000
+
+#define LIRC_SPACE(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_SPACE)
+#define LIRC_PULSE(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_PULSE)
+#define LIRC_FREQUENCY(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_FREQUENCY)
+#define LIRC_TIMEOUT(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_TIMEOUT)
+
+#define LIRC_VALUE(val) ((val)&LIRC_VALUE_MASK)
+#define LIRC_MODE2(val) ((val)&LIRC_MODE2_MASK)
+
+#define LIRC_IS_SPACE(val) (LIRC_MODE2(val) == LIRC_MODE2_SPACE)
+#define LIRC_IS_PULSE(val) (LIRC_MODE2(val) == LIRC_MODE2_PULSE)
+#define LIRC_IS_FREQUENCY(val) (LIRC_MODE2(val) == LIRC_MODE2_FREQUENCY)
+#define LIRC_IS_TIMEOUT(val) (LIRC_MODE2(val) == LIRC_MODE2_TIMEOUT)
+
+/* used heavily by lirc userspace */
+#define lirc_t int
+
+/*** lirc compatible hardware features ***/
+
+#define LIRC_MODE2SEND(x) (x)
+#define LIRC_SEND2MODE(x) (x)
+#define LIRC_MODE2REC(x) ((x) << 16)
+#define LIRC_REC2MODE(x) ((x) >> 16)
+
+#define LIRC_MODE_RAW 0x00000001
+#define LIRC_MODE_PULSE 0x00000002
+#define LIRC_MODE_MODE2 0x00000004
+#define LIRC_MODE_LIRCCODE 0x00000010
+
+
+#define LIRC_CAN_SEND_RAW LIRC_MODE2SEND(LIRC_MODE_RAW)
+#define LIRC_CAN_SEND_PULSE LIRC_MODE2SEND(LIRC_MODE_PULSE)
+#define LIRC_CAN_SEND_MODE2 LIRC_MODE2SEND(LIRC_MODE_MODE2)
+#define LIRC_CAN_SEND_LIRCCODE LIRC_MODE2SEND(LIRC_MODE_LIRCCODE)
+
+#define LIRC_CAN_SEND_MASK 0x0000003f
+
+#define LIRC_CAN_SET_SEND_CARRIER 0x00000100
+#define LIRC_CAN_SET_SEND_DUTY_CYCLE 0x00000200
+#define LIRC_CAN_SET_TRANSMITTER_MASK 0x00000400
+
+#define LIRC_CAN_REC_RAW LIRC_MODE2REC(LIRC_MODE_RAW)
+#define LIRC_CAN_REC_PULSE LIRC_MODE2REC(LIRC_MODE_PULSE)
+#define LIRC_CAN_REC_MODE2 LIRC_MODE2REC(LIRC_MODE_MODE2)
+#define LIRC_CAN_REC_LIRCCODE LIRC_MODE2REC(LIRC_MODE_LIRCCODE)
+
+#define LIRC_CAN_REC_MASK LIRC_MODE2REC(LIRC_CAN_SEND_MASK)
+
+#define LIRC_CAN_SET_REC_CARRIER (LIRC_CAN_SET_SEND_CARRIER << 16)
+#define LIRC_CAN_SET_REC_DUTY_CYCLE (LIRC_CAN_SET_SEND_DUTY_CYCLE << 16)
+
+#define LIRC_CAN_SET_REC_DUTY_CYCLE_RANGE 0x40000000
+#define LIRC_CAN_SET_REC_CARRIER_RANGE 0x80000000
+#define LIRC_CAN_GET_REC_RESOLUTION 0x20000000
+#define LIRC_CAN_SET_REC_TIMEOUT 0x10000000
+#define LIRC_CAN_SET_REC_FILTER 0x08000000
+
+#define LIRC_CAN_MEASURE_CARRIER 0x02000000
+#define LIRC_CAN_USE_WIDEBAND_RECEIVER 0x04000000
+
+#define LIRC_CAN_SEND(x) ((x)&LIRC_CAN_SEND_MASK)
+#define LIRC_CAN_REC(x) ((x)&LIRC_CAN_REC_MASK)
+
+#define LIRC_CAN_NOTIFY_DECODE 0x01000000
+
+/*** IOCTL commands for lirc driver ***/
+
+#define LIRC_GET_FEATURES _IOR('i', 0x00000000, __u32)
+
+#define LIRC_GET_SEND_MODE _IOR('i', 0x00000001, __u32)
+#define LIRC_GET_REC_MODE _IOR('i', 0x00000002, __u32)
+#define LIRC_GET_SEND_CARRIER _IOR('i', 0x00000003, __u32)
+#define LIRC_GET_REC_CARRIER _IOR('i', 0x00000004, __u32)
+#define LIRC_GET_SEND_DUTY_CYCLE _IOR('i', 0x00000005, __u32)
+#define LIRC_GET_REC_DUTY_CYCLE _IOR('i', 0x00000006, __u32)
+#define LIRC_GET_REC_RESOLUTION _IOR('i', 0x00000007, __u32)
+
+#define LIRC_GET_MIN_TIMEOUT _IOR('i', 0x00000008, __u32)
+#define LIRC_GET_MAX_TIMEOUT _IOR('i', 0x00000009, __u32)
+
+#define LIRC_GET_MIN_FILTER_PULSE _IOR('i', 0x0000000a, __u32)
+#define LIRC_GET_MAX_FILTER_PULSE _IOR('i', 0x0000000b, __u32)
+#define LIRC_GET_MIN_FILTER_SPACE _IOR('i', 0x0000000c, __u32)
+#define LIRC_GET_MAX_FILTER_SPACE _IOR('i', 0x0000000d, __u32)
+
+/* code length in bits, currently only for LIRC_MODE_LIRCCODE */
+#define LIRC_GET_LENGTH _IOR('i', 0x0000000f, __u32)
+
+#define LIRC_SET_SEND_MODE _IOW('i', 0x00000011, __u32)
+#define LIRC_SET_REC_MODE _IOW('i', 0x00000012, __u32)
+/* Note: these can reset the according pulse_width */
+#define LIRC_SET_SEND_CARRIER _IOW('i', 0x00000013, __u32)
+#define LIRC_SET_REC_CARRIER _IOW('i', 0x00000014, __u32)
+#define LIRC_SET_SEND_DUTY_CYCLE _IOW('i', 0x00000015, __u32)
+#define LIRC_SET_REC_DUTY_CYCLE _IOW('i', 0x00000016, __u32)
+#define LIRC_SET_TRANSMITTER_MASK _IOW('i', 0x00000017, __u32)
+
+/*
+ * when a timeout != 0 is set the driver will send a
+ * LIRC_MODE2_TIMEOUT data packet, otherwise LIRC_MODE2_TIMEOUT is
+ * never sent, timeout is disabled by default
+ */
+#define LIRC_SET_REC_TIMEOUT _IOW('i', 0x00000018, __u32)
+
+/* 1 enables, 0 disables timeout reports in MODE2 */
+#define LIRC_SET_REC_TIMEOUT_REPORTS _IOW('i', 0x00000019, __u32)
+
+/*
+ * pulses shorter than this are filtered out by hardware (software
+ * emulation in lirc_dev?)
+ */
+#define LIRC_SET_REC_FILTER_PULSE _IOW('i', 0x0000001a, __u32)
+/*
+ * spaces shorter than this are filtered out by hardware (software
+ * emulation in lirc_dev?)
+ */
+#define LIRC_SET_REC_FILTER_SPACE _IOW('i', 0x0000001b, __u32)
+/*
+ * if filter cannot be set independently for pulse/space, this should
+ * be used
+ */
+#define LIRC_SET_REC_FILTER _IOW('i', 0x0000001c, __u32)
+
+/*
+ * if enabled from the next key press on the driver will send
+ * LIRC_MODE2_FREQUENCY packets
+ */
+#define LIRC_SET_MEASURE_CARRIER_MODE _IOW('i', 0x0000001d, __u32)
+
+/*
+ * to set a range use
+ * LIRC_SET_REC_DUTY_CYCLE_RANGE/LIRC_SET_REC_CARRIER_RANGE with the
+ * lower bound first and later
+ * LIRC_SET_REC_DUTY_CYCLE/LIRC_SET_REC_CARRIER with the upper bound
+ */
+
+#define LIRC_SET_REC_DUTY_CYCLE_RANGE _IOW('i', 0x0000001e, __u32)
+#define LIRC_SET_REC_CARRIER_RANGE _IOW('i', 0x0000001f, __u32)
+
+#define LIRC_NOTIFY_DECODE _IO('i', 0x00000020)
+
+#define LIRC_SETUP_START _IO('i', 0x00000021)
+#define LIRC_SETUP_END _IO('i', 0x00000022)
+
+#define LIRC_SET_WIDEBAND_RECEIVER _IOW('i', 0x00000023, __u32)
+
+#endif
diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
index accb036bbc9c..b283d56c1db9 100644
--- a/include/uapi/linux/magic.h
+++ b/include/uapi/linux/magic.h
@@ -54,6 +54,7 @@
#define SMB_SUPER_MAGIC 0x517B
#define CGROUP_SUPER_MAGIC 0x27e0eb
+#define CGROUP2_SUPER_MAGIC 0x63677270
#define STACK_END_MAGIC 0x57AC6E9D
diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h
index 4e816be3de39..1e3c8cb43bd7 100644
--- a/include/uapi/linux/media.h
+++ b/include/uapi/linux/media.h
@@ -23,6 +23,9 @@
#ifndef __LINUX_MEDIA_H
#define __LINUX_MEDIA_H
+#ifndef __KERNEL__
+#include <stdint.h>
+#endif
#include <linux/ioctl.h>
#include <linux/types.h>
#include <linux/version.h>
@@ -42,33 +45,107 @@ struct media_device_info {
#define MEDIA_ENT_ID_FLAG_NEXT (1 << 31)
+/*
+ * Initial value to be used when a new entity is created
+ * Drivers should change it to something useful
+ */
+#define MEDIA_ENT_F_UNKNOWN 0x00000000
+
+/*
+ * Base number ranges for entity functions
+ *
+ * NOTE: those ranges and entity function number are phased just to
+ * make it easier to maintain this file. Userspace should not rely on
+ * the ranges to identify a group of function types, as newer
+ * functions can be added with any name within the full u32 range.
+ */
+#define MEDIA_ENT_F_BASE 0x00000000
+#define MEDIA_ENT_F_OLD_BASE 0x00010000
+#define MEDIA_ENT_F_OLD_SUBDEV_BASE 0x00020000
+
+/*
+ * DVB entities
+ */
+#define MEDIA_ENT_F_DTV_DEMOD (MEDIA_ENT_F_BASE + 1)
+#define MEDIA_ENT_F_TS_DEMUX (MEDIA_ENT_F_BASE + 2)
+#define MEDIA_ENT_F_DTV_CA (MEDIA_ENT_F_BASE + 3)
+#define MEDIA_ENT_F_DTV_NET_DECAP (MEDIA_ENT_F_BASE + 4)
+
+/*
+ * Connectors
+ */
+/* It is a responsibility of the entity drivers to add connectors and links */
+#define MEDIA_ENT_F_CONN_RF (MEDIA_ENT_F_BASE + 21)
+#define MEDIA_ENT_F_CONN_SVIDEO (MEDIA_ENT_F_BASE + 22)
+#define MEDIA_ENT_F_CONN_COMPOSITE (MEDIA_ENT_F_BASE + 23)
+/* For internal test signal generators and other debug connectors */
+#define MEDIA_ENT_F_CONN_TEST (MEDIA_ENT_F_BASE + 24)
+
+/*
+ * I/O entities
+ */
+#define MEDIA_ENT_F_IO_DTV (MEDIA_ENT_F_BASE + 31)
+#define MEDIA_ENT_F_IO_VBI (MEDIA_ENT_F_BASE + 32)
+#define MEDIA_ENT_F_IO_SWRADIO (MEDIA_ENT_F_BASE + 33)
+
+/*
+ * Don't touch on those. The ranges MEDIA_ENT_F_OLD_BASE and
+ * MEDIA_ENT_F_OLD_SUBDEV_BASE are kept to keep backward compatibility
+ * with the legacy v1 API.The number range is out of range by purpose:
+ * several previously reserved numbers got excluded from this range.
+ *
+ * Subdevs are initialized with MEDIA_ENT_T_V4L2_SUBDEV_UNKNOWN,
+ * in order to preserve backward compatibility.
+ * Drivers must change to the proper subdev type before
+ * registering the entity.
+ */
+
+#define MEDIA_ENT_F_IO_V4L (MEDIA_ENT_F_OLD_BASE + 1)
+
+#define MEDIA_ENT_F_CAM_SENSOR (MEDIA_ENT_F_OLD_SUBDEV_BASE + 1)
+#define MEDIA_ENT_F_FLASH (MEDIA_ENT_F_OLD_SUBDEV_BASE + 2)
+#define MEDIA_ENT_F_LENS (MEDIA_ENT_F_OLD_SUBDEV_BASE + 3)
+#define MEDIA_ENT_F_ATV_DECODER (MEDIA_ENT_F_OLD_SUBDEV_BASE + 4)
+/*
+ * It is a responsibility of the entity drivers to add connectors and links
+ * for the tuner entities.
+ */
+#define MEDIA_ENT_F_TUNER (MEDIA_ENT_F_OLD_SUBDEV_BASE + 5)
+
+#define MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN MEDIA_ENT_F_OLD_SUBDEV_BASE
+
+#ifndef __KERNEL__
+
+/*
+ * Legacy symbols used to avoid userspace compilation breakages
+ *
+ * Those symbols map the entity function into types and should be
+ * used only on legacy programs for legacy hardware. Don't rely
+ * on those for MEDIA_IOC_G_TOPOLOGY.
+ */
#define MEDIA_ENT_TYPE_SHIFT 16
#define MEDIA_ENT_TYPE_MASK 0x00ff0000
#define MEDIA_ENT_SUBTYPE_MASK 0x0000ffff
-#define MEDIA_ENT_T_DEVNODE (1 << MEDIA_ENT_TYPE_SHIFT)
-#define MEDIA_ENT_T_DEVNODE_V4L (MEDIA_ENT_T_DEVNODE + 1)
+#define MEDIA_ENT_T_DEVNODE MEDIA_ENT_F_OLD_BASE
+#define MEDIA_ENT_T_DEVNODE_V4L MEDIA_ENT_F_IO_V4L
#define MEDIA_ENT_T_DEVNODE_FB (MEDIA_ENT_T_DEVNODE + 2)
#define MEDIA_ENT_T_DEVNODE_ALSA (MEDIA_ENT_T_DEVNODE + 3)
-#define MEDIA_ENT_T_DEVNODE_DVB_FE (MEDIA_ENT_T_DEVNODE + 4)
-#define MEDIA_ENT_T_DEVNODE_DVB_DEMUX (MEDIA_ENT_T_DEVNODE + 5)
-#define MEDIA_ENT_T_DEVNODE_DVB_DVR (MEDIA_ENT_T_DEVNODE + 6)
-#define MEDIA_ENT_T_DEVNODE_DVB_CA (MEDIA_ENT_T_DEVNODE + 7)
-#define MEDIA_ENT_T_DEVNODE_DVB_NET (MEDIA_ENT_T_DEVNODE + 8)
-
-/* Legacy symbol. Use it to avoid userspace compilation breakages */
-#define MEDIA_ENT_T_DEVNODE_DVB MEDIA_ENT_T_DEVNODE_DVB_FE
-
-#define MEDIA_ENT_T_V4L2_SUBDEV (2 << MEDIA_ENT_TYPE_SHIFT)
-#define MEDIA_ENT_T_V4L2_SUBDEV_SENSOR (MEDIA_ENT_T_V4L2_SUBDEV + 1)
-#define MEDIA_ENT_T_V4L2_SUBDEV_FLASH (MEDIA_ENT_T_V4L2_SUBDEV + 2)
-#define MEDIA_ENT_T_V4L2_SUBDEV_LENS (MEDIA_ENT_T_V4L2_SUBDEV + 3)
-/* A converter of analogue video to its digital representation. */
-#define MEDIA_ENT_T_V4L2_SUBDEV_DECODER (MEDIA_ENT_T_V4L2_SUBDEV + 4)
+#define MEDIA_ENT_T_DEVNODE_DVB (MEDIA_ENT_T_DEVNODE + 4)
-#define MEDIA_ENT_T_V4L2_SUBDEV_TUNER (MEDIA_ENT_T_V4L2_SUBDEV + 5)
+#define MEDIA_ENT_T_UNKNOWN MEDIA_ENT_F_UNKNOWN
+#define MEDIA_ENT_T_V4L2_VIDEO MEDIA_ENT_F_IO_V4L
+#define MEDIA_ENT_T_V4L2_SUBDEV MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN
+#define MEDIA_ENT_T_V4L2_SUBDEV_SENSOR MEDIA_ENT_F_CAM_SENSOR
+#define MEDIA_ENT_T_V4L2_SUBDEV_FLASH MEDIA_ENT_F_FLASH
+#define MEDIA_ENT_T_V4L2_SUBDEV_LENS MEDIA_ENT_F_LENS
+#define MEDIA_ENT_T_V4L2_SUBDEV_DECODER MEDIA_ENT_F_ATV_DECODER
+#define MEDIA_ENT_T_V4L2_SUBDEV_TUNER MEDIA_ENT_F_TUNER
+#endif
+/* Entity flags */
#define MEDIA_ENT_FL_DEFAULT (1 << 0)
+#define MEDIA_ENT_FL_CONNECTOR (1 << 1)
struct media_entity_desc {
__u32 id;
@@ -151,6 +228,10 @@ struct media_pad_desc {
#define MEDIA_LNK_FL_IMMUTABLE (1 << 1)
#define MEDIA_LNK_FL_DYNAMIC (1 << 2)
+#define MEDIA_LNK_FL_LINK_TYPE (0xf << 28)
+# define MEDIA_LNK_FL_DATA_LINK (0 << 28)
+# define MEDIA_LNK_FL_INTERFACE_LINK (1 << 28)
+
struct media_link_desc {
struct media_pad_desc source;
struct media_pad_desc sink;
@@ -167,9 +248,120 @@ struct media_links_enum {
__u32 reserved[4];
};
+/* Interface type ranges */
+
+#define MEDIA_INTF_T_DVB_BASE 0x00000100
+#define MEDIA_INTF_T_V4L_BASE 0x00000200
+
+/* Interface types */
+
+#define MEDIA_INTF_T_DVB_FE (MEDIA_INTF_T_DVB_BASE)
+#define MEDIA_INTF_T_DVB_DEMUX (MEDIA_INTF_T_DVB_BASE + 1)
+#define MEDIA_INTF_T_DVB_DVR (MEDIA_INTF_T_DVB_BASE + 2)
+#define MEDIA_INTF_T_DVB_CA (MEDIA_INTF_T_DVB_BASE + 3)
+#define MEDIA_INTF_T_DVB_NET (MEDIA_INTF_T_DVB_BASE + 4)
+
+#define MEDIA_INTF_T_V4L_VIDEO (MEDIA_INTF_T_V4L_BASE)
+#define MEDIA_INTF_T_V4L_VBI (MEDIA_INTF_T_V4L_BASE + 1)
+#define MEDIA_INTF_T_V4L_RADIO (MEDIA_INTF_T_V4L_BASE + 2)
+#define MEDIA_INTF_T_V4L_SUBDEV (MEDIA_INTF_T_V4L_BASE + 3)
+#define MEDIA_INTF_T_V4L_SWRADIO (MEDIA_INTF_T_V4L_BASE + 4)
+
+/*
+ * MC next gen API definitions
+ *
+ * NOTE: The declarations below are close to the MC RFC for the Media
+ * Controller, the next generation. Yet, there are a few adjustments
+ * to do, as we want to be able to have a functional API before
+ * the MC properties change. Those will be properly marked below.
+ * Please also notice that I removed "num_pads", "num_links",
+ * from the proposal, as a proper userspace application will likely
+ * use lists for pads/links, just as we intend to do in Kernelspace.
+ * The API definition should be freed from fields that are bound to
+ * some specific data structure.
+ *
+ * FIXME: Currently, I opted to name the new types as "media_v2", as this
+ * won't cause any conflict with the Kernelspace namespace, nor with
+ * the previous kAPI media_*_desc namespace. This can be changed
+ * later, before the adding this API upstream.
+ */
+
+#if 0 /* Let's postpone it to Kernel 4.6 */
+struct media_v2_entity {
+ __u32 id;
+ char name[64]; /* FIXME: move to a property? (RFC says so) */
+ __u32 function; /* Main function of the entity */
+ __u16 reserved[12];
+};
+
+/* Should match the specific fields at media_intf_devnode */
+struct media_v2_intf_devnode {
+ __u32 major;
+ __u32 minor;
+};
+
+struct media_v2_interface {
+ __u32 id;
+ __u32 intf_type;
+ __u32 flags;
+ __u32 reserved[9];
+
+ union {
+ struct media_v2_intf_devnode devnode;
+ __u32 raw[16];
+ };
+};
+
+struct media_v2_pad {
+ __u32 id;
+ __u32 entity_id;
+ __u32 flags;
+ __u16 reserved[9];
+};
+
+struct media_v2_link {
+ __u32 id;
+ __u32 source_id;
+ __u32 sink_id;
+ __u32 flags;
+ __u32 reserved[5];
+};
+
+struct media_v2_topology {
+ __u64 topology_version;
+
+ __u32 num_entities;
+ __u32 reserved1;
+ __u64 ptr_entities;
+
+ __u32 num_interfaces;
+ __u32 reserved2;
+ __u64 ptr_interfaces;
+
+ __u32 num_pads;
+ __u32 reserved3;
+ __u64 ptr_pads;
+
+ __u32 num_links;
+ __u32 reserved4;
+ __u64 ptr_links;
+};
+
+static inline void __user *media_get_uptr(__u64 arg)
+{
+ return (void __user *)(uintptr_t)arg;
+}
+#endif
+
+/* ioctls */
+
#define MEDIA_IOC_DEVICE_INFO _IOWR('|', 0x00, struct media_device_info)
#define MEDIA_IOC_ENUM_ENTITIES _IOWR('|', 0x01, struct media_entity_desc)
#define MEDIA_IOC_ENUM_LINKS _IOWR('|', 0x02, struct media_links_enum)
#define MEDIA_IOC_SETUP_LINK _IOWR('|', 0x03, struct media_link_desc)
+#if 0 /* Let's postpone it to Kernel 4.6 */
+#define MEDIA_IOC_G_TOPOLOGY _IOWR('|', 0x04, struct media_v2_topology)
+#endif
+
#endif /* __LINUX_MEDIA_H */
diff --git a/include/uapi/linux/mroute.h b/include/uapi/linux/mroute.h
index a382d2c04a42..cf943016930f 100644
--- a/include/uapi/linux/mroute.h
+++ b/include/uapi/linux/mroute.h
@@ -4,15 +4,13 @@
#include <linux/sockios.h>
#include <linux/types.h>
-/*
- * Based on the MROUTING 3.5 defines primarily to keep
- * source compatibility with BSD.
+/* Based on the MROUTING 3.5 defines primarily to keep
+ * source compatibility with BSD.
*
- * See the mrouted code for the original history.
- *
- * Protocol Independent Multicast (PIM) data structures included
- * Carlos Picoto (cap@di.fc.ul.pt)
+ * See the mrouted code for the original history.
*
+ * Protocol Independent Multicast (PIM) data structures included
+ * Carlos Picoto (cap@di.fc.ul.pt)
*/
#define MRT_BASE 200
@@ -34,15 +32,13 @@
#define SIOCGETSGCNT (SIOCPROTOPRIVATE+1)
#define SIOCGETRPF (SIOCPROTOPRIVATE+2)
-#define MAXVIFS 32
+#define MAXVIFS 32
typedef unsigned long vifbitmap_t; /* User mode code depends on this lot */
typedef unsigned short vifi_t;
#define ALL_VIFS ((vifi_t)(-1))
-/*
- * Same idea as select
- */
-
+/* Same idea as select */
+
#define VIFM_SET(n,m) ((m)|=(1<<(n)))
#define VIFM_CLR(n,m) ((m)&=~(1<<(n)))
#define VIFM_ISSET(n,m) ((m)&(1<<(n)))
@@ -50,11 +46,9 @@ typedef unsigned short vifi_t;
#define VIFM_COPY(mfrom,mto) ((mto)=(mfrom))
#define VIFM_SAME(m1,m2) ((m1)==(m2))
-/*
- * Passed by mrouted for an MRT_ADD_VIF - again we use the
- * mrouted 3.6 structures for compatibility
+/* Passed by mrouted for an MRT_ADD_VIF - again we use the
+ * mrouted 3.6 structures for compatibility
*/
-
struct vifctl {
vifi_t vifc_vifi; /* Index of VIF */
unsigned char vifc_flags; /* VIFF_ flags */
@@ -73,10 +67,7 @@ struct vifctl {
#define VIFF_USE_IFINDEX 0x8 /* use vifc_lcl_ifindex instead of
vifc_lcl_addr to find an interface */
-/*
- * Cache manipulation structures for mrouted and PIMd
- */
-
+/* Cache manipulation structures for mrouted and PIMd */
struct mfcctl {
struct in_addr mfcc_origin; /* Origin of mcast */
struct in_addr mfcc_mcastgrp; /* Group in question */
@@ -88,10 +79,7 @@ struct mfcctl {
int mfcc_expire;
};
-/*
- * Group count retrieval for mrouted
- */
-
+/* Group count retrieval for mrouted */
struct sioc_sg_req {
struct in_addr src;
struct in_addr grp;
@@ -100,10 +88,7 @@ struct sioc_sg_req {
unsigned long wrong_if;
};
-/*
- * To get vif packet counts
- */
-
+/* To get vif packet counts */
struct sioc_vif_req {
vifi_t vifi; /* Which iface */
unsigned long icount; /* In packets */
@@ -112,11 +97,9 @@ struct sioc_vif_req {
unsigned long obytes; /* Out bytes */
};
-/*
- * This is the format the mroute daemon expects to see IGMP control
- * data. Magically happens to be like an IP packet as per the original
+/* This is the format the mroute daemon expects to see IGMP control
+ * data. Magically happens to be like an IP packet as per the original
*/
-
struct igmpmsg {
__u32 unused1,unused2;
unsigned char im_msgtype; /* What is this */
@@ -126,21 +109,13 @@ struct igmpmsg {
struct in_addr im_src,im_dst;
};
-/*
- * That's all usermode folks
- */
-
-
+/* That's all usermode folks */
#define MFC_ASSERT_THRESH (3*HZ) /* Maximal freq. of asserts */
-/*
- * Pseudo messages used by mrouted
- */
-
+/* Pseudo messages used by mrouted */
#define IGMPMSG_NOCACHE 1 /* Kern cache fill request to mrouted */
#define IGMPMSG_WRONGVIF 2 /* For PIM assert processing (unused) */
#define IGMPMSG_WHOLEPKT 3 /* For PIM Register processing */
-
#endif /* _UAPI__LINUX_MROUTE_H */
diff --git a/include/uapi/linux/netfilter/ipset/ip_set_bitmap.h b/include/uapi/linux/netfilter/ipset/ip_set_bitmap.h
index 6a2c038d1888..fd5024d26269 100644
--- a/include/uapi/linux/netfilter/ipset/ip_set_bitmap.h
+++ b/include/uapi/linux/netfilter/ipset/ip_set_bitmap.h
@@ -1,6 +1,8 @@
#ifndef _UAPI__IP_SET_BITMAP_H
#define _UAPI__IP_SET_BITMAP_H
+#include <linux/netfilter/ipset/ip_set.h>
+
/* Bitmap type specific error codes */
enum {
/* The element is out of the range of the set */
diff --git a/include/uapi/linux/netfilter/ipset/ip_set_hash.h b/include/uapi/linux/netfilter/ipset/ip_set_hash.h
index 352eeccdc7f2..82deeb883ac4 100644
--- a/include/uapi/linux/netfilter/ipset/ip_set_hash.h
+++ b/include/uapi/linux/netfilter/ipset/ip_set_hash.h
@@ -1,6 +1,8 @@
#ifndef _UAPI__IP_SET_HASH_H
#define _UAPI__IP_SET_HASH_H
+#include <linux/netfilter/ipset/ip_set.h>
+
/* Hash type specific error codes */
enum {
/* Hash is full */
diff --git a/include/uapi/linux/netfilter/ipset/ip_set_list.h b/include/uapi/linux/netfilter/ipset/ip_set_list.h
index a44efaa98213..84d430368266 100644
--- a/include/uapi/linux/netfilter/ipset/ip_set_list.h
+++ b/include/uapi/linux/netfilter/ipset/ip_set_list.h
@@ -1,6 +1,8 @@
#ifndef _UAPI__IP_SET_LIST_H
#define _UAPI__IP_SET_LIST_H
+#include <linux/netfilter/ipset/ip_set.h>
+
/* List type specific error codes */
enum {
/* Set name to be added/deleted/tested does not exist. */
diff --git a/include/uapi/linux/netfilter/nf_conntrack_sctp.h b/include/uapi/linux/netfilter/nf_conntrack_sctp.h
index ed4e776e1242..2cbc366c3fb4 100644
--- a/include/uapi/linux/netfilter/nf_conntrack_sctp.h
+++ b/include/uapi/linux/netfilter/nf_conntrack_sctp.h
@@ -1,5 +1,5 @@
-#ifndef _NF_CONNTRACK_SCTP_H
-#define _NF_CONNTRACK_SCTP_H
+#ifndef _UAPI_NF_CONNTRACK_SCTP_H
+#define _UAPI_NF_CONNTRACK_SCTP_H
/* SCTP tracking. */
#include <linux/netfilter/nf_conntrack_tuple_common.h>
@@ -18,10 +18,4 @@ enum sctp_conntrack {
SCTP_CONNTRACK_MAX
};
-struct ip_ct_sctp {
- enum sctp_conntrack state;
-
- __be32 vtag[IP_CT_DIR_MAX];
-};
-
-#endif /* _NF_CONNTRACK_SCTP_H */
+#endif /* _UAPI_NF_CONNTRACK_SCTP_H */
diff --git a/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h b/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h
index 2f6bbc5b8125..a9c3834abdd4 100644
--- a/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h
+++ b/include/uapi/linux/netfilter/nf_conntrack_tuple_common.h
@@ -1,6 +1,9 @@
#ifndef _NF_CONNTRACK_TUPLE_COMMON_H
#define _NF_CONNTRACK_TUPLE_COMMON_H
+#include <linux/types.h>
+#include <linux/netfilter.h>
+
enum ip_conntrack_dir {
IP_CT_DIR_ORIGINAL,
IP_CT_DIR_REPLY,
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index d8c8a7c9d88a..be41ffc128b8 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -83,6 +83,7 @@ enum nft_verdicts {
* @NFT_MSG_DELSETELEM: delete a set element (enum nft_set_elem_attributes)
* @NFT_MSG_NEWGEN: announce a new generation, only for events (enum nft_gen_attributes)
* @NFT_MSG_GETGEN: get the rule-set generation (enum nft_gen_attributes)
+ * @NFT_MSG_TRACE: trace event (enum nft_trace_attributes)
*/
enum nf_tables_msg_types {
NFT_MSG_NEWTABLE,
@@ -102,6 +103,7 @@ enum nf_tables_msg_types {
NFT_MSG_DELSETELEM,
NFT_MSG_NEWGEN,
NFT_MSG_GETGEN,
+ NFT_MSG_TRACE,
NFT_MSG_MAX,
};
@@ -289,6 +291,7 @@ enum nft_set_desc_attributes {
* @NFTA_SET_ID: uniquely identifies a set in a transaction (NLA_U32)
* @NFTA_SET_TIMEOUT: default timeout value (NLA_U64)
* @NFTA_SET_GC_INTERVAL: garbage collection interval (NLA_U32)
+ * @NFTA_SET_USERDATA: user data (NLA_BINARY)
*/
enum nft_set_attributes {
NFTA_SET_UNSPEC,
@@ -304,6 +307,7 @@ enum nft_set_attributes {
NFTA_SET_ID,
NFTA_SET_TIMEOUT,
NFTA_SET_GC_INTERVAL,
+ NFTA_SET_USERDATA,
__NFTA_SET_MAX
};
#define NFTA_SET_MAX (__NFTA_SET_MAX - 1)
@@ -598,12 +602,26 @@ enum nft_payload_bases {
};
/**
+ * enum nft_payload_csum_types - nf_tables payload expression checksum types
+ *
+ * @NFT_PAYLOAD_CSUM_NONE: no checksumming
+ * @NFT_PAYLOAD_CSUM_INET: internet checksum (RFC 791)
+ */
+enum nft_payload_csum_types {
+ NFT_PAYLOAD_CSUM_NONE,
+ NFT_PAYLOAD_CSUM_INET,
+};
+
+/**
* enum nft_payload_attributes - nf_tables payload expression netlink attributes
*
* @NFTA_PAYLOAD_DREG: destination register to load data into (NLA_U32: nft_registers)
* @NFTA_PAYLOAD_BASE: payload base (NLA_U32: nft_payload_bases)
* @NFTA_PAYLOAD_OFFSET: payload offset relative to base (NLA_U32)
* @NFTA_PAYLOAD_LEN: payload length (NLA_U32)
+ * @NFTA_PAYLOAD_SREG: source register to load data from (NLA_U32: nft_registers)
+ * @NFTA_PAYLOAD_CSUM_TYPE: checksum type (NLA_U32)
+ * @NFTA_PAYLOAD_CSUM_OFFSET: checksum offset relative to base (NLA_U32)
*/
enum nft_payload_attributes {
NFTA_PAYLOAD_UNSPEC,
@@ -611,6 +629,9 @@ enum nft_payload_attributes {
NFTA_PAYLOAD_BASE,
NFTA_PAYLOAD_OFFSET,
NFTA_PAYLOAD_LEN,
+ NFTA_PAYLOAD_SREG,
+ NFTA_PAYLOAD_CSUM_TYPE,
+ NFTA_PAYLOAD_CSUM_OFFSET,
__NFTA_PAYLOAD_MAX
};
#define NFTA_PAYLOAD_MAX (__NFTA_PAYLOAD_MAX - 1)
@@ -736,6 +757,8 @@ enum nft_ct_keys {
NFT_CT_PROTO_SRC,
NFT_CT_PROTO_DST,
NFT_CT_LABELS,
+ NFT_CT_PKTS,
+ NFT_CT_BYTES,
};
/**
@@ -761,6 +784,10 @@ enum nft_limit_type {
NFT_LIMIT_PKT_BYTES
};
+enum nft_limit_flags {
+ NFT_LIMIT_F_INV = (1 << 0),
+};
+
/**
* enum nft_limit_attributes - nf_tables limit expression netlink attributes
*
@@ -768,6 +795,7 @@ enum nft_limit_type {
* @NFTA_LIMIT_UNIT: refill unit (NLA_U64)
* @NFTA_LIMIT_BURST: burst (NLA_U32)
* @NFTA_LIMIT_TYPE: type of limit (NLA_U32: enum nft_limit_type)
+ * @NFTA_LIMIT_FLAGS: flags (NLA_U32: enum nft_limit_flags)
*/
enum nft_limit_attributes {
NFTA_LIMIT_UNSPEC,
@@ -775,6 +803,7 @@ enum nft_limit_attributes {
NFTA_LIMIT_UNIT,
NFTA_LIMIT_BURST,
NFTA_LIMIT_TYPE,
+ NFTA_LIMIT_FLAGS,
__NFTA_LIMIT_MAX
};
#define NFTA_LIMIT_MAX (__NFTA_LIMIT_MAX - 1)
@@ -959,6 +988,18 @@ enum nft_dup_attributes {
#define NFTA_DUP_MAX (__NFTA_DUP_MAX - 1)
/**
+ * enum nft_fwd_attributes - nf_tables fwd expression netlink attributes
+ *
+ * @NFTA_FWD_SREG_DEV: source register of output interface (NLA_U32: nft_register)
+ */
+enum nft_fwd_attributes {
+ NFTA_FWD_UNSPEC,
+ NFTA_FWD_SREG_DEV,
+ __NFTA_FWD_MAX
+};
+#define NFTA_FWD_MAX (__NFTA_FWD_MAX - 1)
+
+/**
* enum nft_gen_attributes - nf_tables ruleset generation attributes
*
* @NFTA_GEN_ID: Ruleset generation ID (NLA_U32)
@@ -970,4 +1011,54 @@ enum nft_gen_attributes {
};
#define NFTA_GEN_MAX (__NFTA_GEN_MAX - 1)
+/**
+ * enum nft_trace_attributes - nf_tables trace netlink attributes
+ *
+ * @NFTA_TRACE_TABLE: name of the table (NLA_STRING)
+ * @NFTA_TRACE_CHAIN: name of the chain (NLA_STRING)
+ * @NFTA_TRACE_RULE_HANDLE: numeric handle of the rule (NLA_U64)
+ * @NFTA_TRACE_TYPE: type of the event (NLA_U32: nft_trace_types)
+ * @NFTA_TRACE_VERDICT: verdict returned by hook (NLA_NESTED: nft_verdicts)
+ * @NFTA_TRACE_ID: pseudo-id, same for each skb traced (NLA_U32)
+ * @NFTA_TRACE_LL_HEADER: linklayer header (NLA_BINARY)
+ * @NFTA_TRACE_NETWORK_HEADER: network header (NLA_BINARY)
+ * @NFTA_TRACE_TRANSPORT_HEADER: transport header (NLA_BINARY)
+ * @NFTA_TRACE_IIF: indev ifindex (NLA_U32)
+ * @NFTA_TRACE_IIFTYPE: netdev->type of indev (NLA_U16)
+ * @NFTA_TRACE_OIF: outdev ifindex (NLA_U32)
+ * @NFTA_TRACE_OIFTYPE: netdev->type of outdev (NLA_U16)
+ * @NFTA_TRACE_MARK: nfmark (NLA_U32)
+ * @NFTA_TRACE_NFPROTO: nf protocol processed (NLA_U32)
+ * @NFTA_TRACE_POLICY: policy that decided fate of packet (NLA_U32)
+ */
+enum nft_trace_attibutes {
+ NFTA_TRACE_UNSPEC,
+ NFTA_TRACE_TABLE,
+ NFTA_TRACE_CHAIN,
+ NFTA_TRACE_RULE_HANDLE,
+ NFTA_TRACE_TYPE,
+ NFTA_TRACE_VERDICT,
+ NFTA_TRACE_ID,
+ NFTA_TRACE_LL_HEADER,
+ NFTA_TRACE_NETWORK_HEADER,
+ NFTA_TRACE_TRANSPORT_HEADER,
+ NFTA_TRACE_IIF,
+ NFTA_TRACE_IIFTYPE,
+ NFTA_TRACE_OIF,
+ NFTA_TRACE_OIFTYPE,
+ NFTA_TRACE_MARK,
+ NFTA_TRACE_NFPROTO,
+ NFTA_TRACE_POLICY,
+ __NFTA_TRACE_MAX
+};
+#define NFTA_TRACE_MAX (__NFTA_TRACE_MAX - 1)
+
+enum nft_trace_types {
+ NFT_TRACETYPE_UNSPEC,
+ NFT_TRACETYPE_POLICY,
+ NFT_TRACETYPE_RETURN,
+ NFT_TRACETYPE_RULE,
+ __NFT_TRACETYPE_MAX
+};
+#define NFT_TRACETYPE_MAX (__NFT_TRACETYPE_MAX - 1)
#endif /* _LINUX_NF_TABLES_H */
diff --git a/include/uapi/linux/netfilter/nfnetlink.h b/include/uapi/linux/netfilter/nfnetlink.h
index 354a7e5e50f2..4bb8cb7730e7 100644
--- a/include/uapi/linux/netfilter/nfnetlink.h
+++ b/include/uapi/linux/netfilter/nfnetlink.h
@@ -22,6 +22,8 @@ enum nfnetlink_groups {
#define NFNLGRP_NFTABLES NFNLGRP_NFTABLES
NFNLGRP_ACCT_QUOTA,
#define NFNLGRP_ACCT_QUOTA NFNLGRP_ACCT_QUOTA
+ NFNLGRP_NFTRACE,
+#define NFNLGRP_NFTRACE NFNLGRP_NFTRACE
__NFNLGRP_MAX,
};
#define NFNLGRP_MAX (__NFNLGRP_MAX - 1)
diff --git a/include/uapi/linux/netfilter/xt_HMARK.h b/include/uapi/linux/netfilter/xt_HMARK.h
index 826fc5807577..3fb48c8d8d78 100644
--- a/include/uapi/linux/netfilter/xt_HMARK.h
+++ b/include/uapi/linux/netfilter/xt_HMARK.h
@@ -2,6 +2,7 @@
#define XT_HMARK_H_
#include <linux/types.h>
+#include <linux/netfilter.h>
enum {
XT_HMARK_SADDR_MASK,
diff --git a/include/uapi/linux/netfilter/xt_RATEEST.h b/include/uapi/linux/netfilter/xt_RATEEST.h
index 6605e20ad8cf..ec1b57047e03 100644
--- a/include/uapi/linux/netfilter/xt_RATEEST.h
+++ b/include/uapi/linux/netfilter/xt_RATEEST.h
@@ -2,6 +2,7 @@
#define _XT_RATEEST_TARGET_H
#include <linux/types.h>
+#include <linux/if.h>
struct xt_rateest_target_info {
char name[IFNAMSIZ];
diff --git a/include/uapi/linux/netfilter/xt_TEE.h b/include/uapi/linux/netfilter/xt_TEE.h
index 5c21d5c829af..01092023404b 100644
--- a/include/uapi/linux/netfilter/xt_TEE.h
+++ b/include/uapi/linux/netfilter/xt_TEE.h
@@ -1,6 +1,8 @@
#ifndef _XT_TEE_TARGET_H
#define _XT_TEE_TARGET_H
+#include <linux/netfilter.h>
+
struct xt_tee_tginfo {
union nf_inet_addr gw;
char oif[16];
diff --git a/include/uapi/linux/netfilter/xt_TPROXY.h b/include/uapi/linux/netfilter/xt_TPROXY.h
index 902043c2073f..8d693eefdc1f 100644
--- a/include/uapi/linux/netfilter/xt_TPROXY.h
+++ b/include/uapi/linux/netfilter/xt_TPROXY.h
@@ -2,6 +2,7 @@
#define _XT_TPROXY_H
#include <linux/types.h>
+#include <linux/netfilter.h>
/* TPROXY target is capable of marking the packet to perform
* redirection. We can get rid of that whenever we get support for
diff --git a/include/uapi/linux/netfilter/xt_cgroup.h b/include/uapi/linux/netfilter/xt_cgroup.h
index 43acb7e175f6..1e4b37b93bef 100644
--- a/include/uapi/linux/netfilter/xt_cgroup.h
+++ b/include/uapi/linux/netfilter/xt_cgroup.h
@@ -2,10 +2,23 @@
#define _UAPI_XT_CGROUP_H
#include <linux/types.h>
+#include <linux/limits.h>
-struct xt_cgroup_info {
+struct xt_cgroup_info_v0 {
__u32 id;
__u32 invert;
};
+struct xt_cgroup_info_v1 {
+ __u8 has_path;
+ __u8 has_classid;
+ __u8 invert_path;
+ __u8 invert_classid;
+ char path[PATH_MAX];
+ __u32 classid;
+
+ /* kernel internal data */
+ void *priv __attribute__((aligned(8)));
+};
+
#endif /* _UAPI_XT_CGROUP_H */
diff --git a/include/uapi/linux/netfilter/xt_hashlimit.h b/include/uapi/linux/netfilter/xt_hashlimit.h
index cbfc43d1af68..6db90372f09c 100644
--- a/include/uapi/linux/netfilter/xt_hashlimit.h
+++ b/include/uapi/linux/netfilter/xt_hashlimit.h
@@ -2,6 +2,7 @@
#define _UAPI_XT_HASHLIMIT_H
#include <linux/types.h>
+#include <linux/if.h>
/* timings are in milliseconds. */
#define XT_HASHLIMIT_SCALE 10000
diff --git a/include/uapi/linux/netfilter/xt_ipvs.h b/include/uapi/linux/netfilter/xt_ipvs.h
index eff34ac18808..e03b9c31a39d 100644
--- a/include/uapi/linux/netfilter/xt_ipvs.h
+++ b/include/uapi/linux/netfilter/xt_ipvs.h
@@ -2,6 +2,7 @@
#define _XT_IPVS_H
#include <linux/types.h>
+#include <linux/netfilter.h>
enum {
XT_IPVS_IPVS_PROPERTY = 1 << 0, /* all other options imply this one */
diff --git a/include/uapi/linux/netfilter/xt_mac.h b/include/uapi/linux/netfilter/xt_mac.h
index b892cdc67e06..9a19a08a9181 100644
--- a/include/uapi/linux/netfilter/xt_mac.h
+++ b/include/uapi/linux/netfilter/xt_mac.h
@@ -1,6 +1,8 @@
#ifndef _XT_MAC_H
#define _XT_MAC_H
+#include <linux/if_ether.h>
+
struct xt_mac_info {
unsigned char srcaddr[ETH_ALEN];
int invert;
diff --git a/include/uapi/linux/netfilter/xt_osf.h b/include/uapi/linux/netfilter/xt_osf.h
index 5d66caeba3ee..e6159958b2fb 100644
--- a/include/uapi/linux/netfilter/xt_osf.h
+++ b/include/uapi/linux/netfilter/xt_osf.h
@@ -20,6 +20,8 @@
#define _XT_OSF_H
#include <linux/types.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
#define MAXGENRELEN 32
diff --git a/include/uapi/linux/netfilter/xt_physdev.h b/include/uapi/linux/netfilter/xt_physdev.h
index db7a2982e9c0..ccdde87da214 100644
--- a/include/uapi/linux/netfilter/xt_physdev.h
+++ b/include/uapi/linux/netfilter/xt_physdev.h
@@ -2,7 +2,7 @@
#define _UAPI_XT_PHYSDEV_H
#include <linux/types.h>
-
+#include <linux/if.h>
#define XT_PHYSDEV_OP_IN 0x01
#define XT_PHYSDEV_OP_OUT 0x02
diff --git a/include/uapi/linux/netfilter/xt_policy.h b/include/uapi/linux/netfilter/xt_policy.h
index be8ead05c316..d8a9800dce61 100644
--- a/include/uapi/linux/netfilter/xt_policy.h
+++ b/include/uapi/linux/netfilter/xt_policy.h
@@ -2,6 +2,8 @@
#define _XT_POLICY_H
#include <linux/types.h>
+#include <linux/in.h>
+#include <linux/in6.h>
#define XT_POLICY_MAX_ELEM 4
diff --git a/include/uapi/linux/netfilter/xt_rateest.h b/include/uapi/linux/netfilter/xt_rateest.h
index d40a6196842a..13fe50d4e4b3 100644
--- a/include/uapi/linux/netfilter/xt_rateest.h
+++ b/include/uapi/linux/netfilter/xt_rateest.h
@@ -2,6 +2,7 @@
#define _XT_RATEEST_MATCH_H
#include <linux/types.h>
+#include <linux/if.h>
enum xt_rateest_match_flags {
XT_RATEEST_MATCH_INVERT = 1<<0,
diff --git a/include/uapi/linux/netfilter/xt_recent.h b/include/uapi/linux/netfilter/xt_recent.h
index 6ef36c113e89..955d562031cc 100644
--- a/include/uapi/linux/netfilter/xt_recent.h
+++ b/include/uapi/linux/netfilter/xt_recent.h
@@ -2,6 +2,7 @@
#define _LINUX_NETFILTER_XT_RECENT_H 1
#include <linux/types.h>
+#include <linux/netfilter.h>
enum {
XT_RECENT_CHECK = 1 << 0,
diff --git a/include/uapi/linux/netfilter/xt_sctp.h b/include/uapi/linux/netfilter/xt_sctp.h
index 29287be696a2..58ffcfb7978e 100644
--- a/include/uapi/linux/netfilter/xt_sctp.h
+++ b/include/uapi/linux/netfilter/xt_sctp.h
@@ -66,26 +66,26 @@ struct xt_sctp_info {
#define SCTP_CHUNKMAP_IS_CLEAR(chunkmap) \
__sctp_chunkmap_is_clear((chunkmap), ARRAY_SIZE(chunkmap))
-static inline bool
+static inline _Bool
__sctp_chunkmap_is_clear(const __u32 *chunkmap, unsigned int n)
{
unsigned int i;
for (i = 0; i < n; ++i)
if (chunkmap[i])
- return false;
- return true;
+ return 0;
+ return 1;
}
#define SCTP_CHUNKMAP_IS_ALL_SET(chunkmap) \
__sctp_chunkmap_is_all_set((chunkmap), ARRAY_SIZE(chunkmap))
-static inline bool
+static inline _Bool
__sctp_chunkmap_is_all_set(const __u32 *chunkmap, unsigned int n)
{
unsigned int i;
for (i = 0; i < n; ++i)
if (chunkmap[i] != ~0U)
- return false;
- return true;
+ return 0;
+ return 1;
}
#endif /* _XT_SCTP_H_ */
diff --git a/include/uapi/linux/netfilter_arp/arp_tables.h b/include/uapi/linux/netfilter_arp/arp_tables.h
index a5a86a4db6b3..ece3ad4eecda 100644
--- a/include/uapi/linux/netfilter_arp/arp_tables.h
+++ b/include/uapi/linux/netfilter_arp/arp_tables.h
@@ -11,6 +11,7 @@
#include <linux/types.h>
#include <linux/compiler.h>
+#include <linux/if.h>
#include <linux/netfilter_arp.h>
#include <linux/netfilter/x_tables.h>
diff --git a/include/uapi/linux/netfilter_bridge.h b/include/uapi/linux/netfilter_bridge.h
index a5eda6db8d79..514519b47651 100644
--- a/include/uapi/linux/netfilter_bridge.h
+++ b/include/uapi/linux/netfilter_bridge.h
@@ -4,6 +4,7 @@
/* bridge-specific defines for netfilter.
*/
+#include <linux/in.h>
#include <linux/netfilter.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
diff --git a/include/uapi/linux/netfilter_bridge/ebt_arp.h b/include/uapi/linux/netfilter_bridge/ebt_arp.h
index 522f3e427f49..dd4df25330e8 100644
--- a/include/uapi/linux/netfilter_bridge/ebt_arp.h
+++ b/include/uapi/linux/netfilter_bridge/ebt_arp.h
@@ -2,6 +2,7 @@
#define __LINUX_BRIDGE_EBT_ARP_H
#include <linux/types.h>
+#include <linux/if_ether.h>
#define EBT_ARP_OPCODE 0x01
#define EBT_ARP_HTYPE 0x02
diff --git a/include/uapi/linux/netfilter_bridge/ebt_arpreply.h b/include/uapi/linux/netfilter_bridge/ebt_arpreply.h
index 7e77896e1fbf..6fee3402e307 100644
--- a/include/uapi/linux/netfilter_bridge/ebt_arpreply.h
+++ b/include/uapi/linux/netfilter_bridge/ebt_arpreply.h
@@ -1,6 +1,8 @@
#ifndef __LINUX_BRIDGE_EBT_ARPREPLY_H
#define __LINUX_BRIDGE_EBT_ARPREPLY_H
+#include <linux/if_ether.h>
+
struct ebt_arpreply_info {
unsigned char mac[ETH_ALEN];
int target;
diff --git a/include/uapi/linux/netfilter_bridge/ebt_ip6.h b/include/uapi/linux/netfilter_bridge/ebt_ip6.h
index 42b889682721..a062f0ce95f9 100644
--- a/include/uapi/linux/netfilter_bridge/ebt_ip6.h
+++ b/include/uapi/linux/netfilter_bridge/ebt_ip6.h
@@ -13,6 +13,7 @@
#define __LINUX_BRIDGE_EBT_IP6_H
#include <linux/types.h>
+#include <linux/in6.h>
#define EBT_IP6_SOURCE 0x01
#define EBT_IP6_DEST 0x02
diff --git a/include/uapi/linux/netfilter_bridge/ebt_nat.h b/include/uapi/linux/netfilter_bridge/ebt_nat.h
index 5e74e3b03bd6..c990d74ee966 100644
--- a/include/uapi/linux/netfilter_bridge/ebt_nat.h
+++ b/include/uapi/linux/netfilter_bridge/ebt_nat.h
@@ -1,6 +1,8 @@
#ifndef __LINUX_BRIDGE_EBT_NAT_H
#define __LINUX_BRIDGE_EBT_NAT_H
+#include <linux/if_ether.h>
+
#define NAT_ARP_BIT (0x00000010)
struct ebt_nat_info {
unsigned char mac[ETH_ALEN];
diff --git a/include/uapi/linux/netfilter_bridge/ebtables.h b/include/uapi/linux/netfilter_bridge/ebtables.h
index fd2ee501726d..e3cdf9f1a259 100644
--- a/include/uapi/linux/netfilter_bridge/ebtables.h
+++ b/include/uapi/linux/netfilter_bridge/ebtables.h
@@ -12,6 +12,8 @@
#ifndef _UAPI__LINUX_BRIDGE_EFF_H
#define _UAPI__LINUX_BRIDGE_EFF_H
+#include <linux/types.h>
+#include <linux/if.h>
#include <linux/netfilter_bridge.h>
#define EBT_TABLE_MAXNAMELEN 32
@@ -33,8 +35,8 @@ struct xt_match;
struct xt_target;
struct ebt_counter {
- uint64_t pcnt;
- uint64_t bcnt;
+ __u64 pcnt;
+ __u64 bcnt;
};
struct ebt_replace {
diff --git a/include/uapi/linux/netfilter_ipv4/ip_tables.h b/include/uapi/linux/netfilter_ipv4/ip_tables.h
index f1e6ef256034..d0da53d96d93 100644
--- a/include/uapi/linux/netfilter_ipv4/ip_tables.h
+++ b/include/uapi/linux/netfilter_ipv4/ip_tables.h
@@ -17,6 +17,7 @@
#include <linux/types.h>
#include <linux/compiler.h>
+#include <linux/if.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter/x_tables.h>
diff --git a/include/uapi/linux/netfilter_ipv6/ip6_tables.h b/include/uapi/linux/netfilter_ipv6/ip6_tables.h
index 649c68062dca..d1b22653daf2 100644
--- a/include/uapi/linux/netfilter_ipv6/ip6_tables.h
+++ b/include/uapi/linux/netfilter_ipv6/ip6_tables.h
@@ -17,6 +17,7 @@
#include <linux/types.h>
#include <linux/compiler.h>
+#include <linux/if.h>
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter/x_tables.h>
diff --git a/include/uapi/linux/netfilter_ipv6/ip6t_rt.h b/include/uapi/linux/netfilter_ipv6/ip6t_rt.h
index 7605a5ff81cd..558f81e46fb9 100644
--- a/include/uapi/linux/netfilter_ipv6/ip6t_rt.h
+++ b/include/uapi/linux/netfilter_ipv6/ip6t_rt.h
@@ -2,7 +2,7 @@
#define _IP6T_RT_H
#include <linux/types.h>
-/*#include <linux/in6.h>*/
+#include <linux/in6.h>
#define IP6T_RT_HOPS 16
diff --git a/include/uapi/linux/nfs.h b/include/uapi/linux/nfs.h
index 654bae3f1a38..5e6296160361 100644
--- a/include/uapi/linux/nfs.h
+++ b/include/uapi/linux/nfs.h
@@ -33,17 +33,6 @@
#define NFS_PIPE_DIRNAME "nfs"
-/* NFS ioctls */
-/* Let's follow btrfs lead on CLONE to avoid messing userspace */
-#define NFS_IOC_CLONE _IOW(0x94, 9, int)
-#define NFS_IOC_CLONE_RANGE _IOW(0x94, 13, int)
-
-struct nfs_ioctl_clone_range_args {
- __s64 src_fd;
- __u64 src_off, count;
- __u64 dst_off;
-};
-
/*
* NFS stats. The good thing with these values is that NFSv3 errors are
* a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH which
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 1f0b4cf5dd03..5b7b5ebe7ca8 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -820,6 +820,10 @@
* as an event to indicate changes for devices with wiphy-specific regdom
* management.
*
+ * @NL80211_CMD_ABORT_SCAN: Stop an ongoing scan. Returns -ENOENT if a scan is
+ * not running. The driver indicates the status of the scan through
+ * cfg80211_scan_done().
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -1006,6 +1010,8 @@ enum nl80211_commands {
NL80211_CMD_WIPHY_REG_CHANGE,
+ NL80211_CMD_ABORT_SCAN,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -1764,8 +1770,9 @@ enum nl80211_commands {
* over all channels.
*
* @NL80211_ATTR_SCHED_SCAN_DELAY: delay before the first cycle of a
- * scheduled scan (or a WoWLAN net-detect scan) is started, u32
- * in seconds.
+ * scheduled scan is started. Or the delay before a WoWLAN
+ * net-detect scan is started, counting from the moment the
+ * system is suspended. This value is a u32, in seconds.
* @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
* is operating in an indoor environment.
diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h
index 28ccedd000f5..a27222d5b413 100644
--- a/include/uapi/linux/openvswitch.h
+++ b/include/uapi/linux/openvswitch.h
@@ -628,7 +628,7 @@ struct ovs_action_hash {
* @OVS_CT_ATTR_MARK: u32 value followed by u32 mask. For each bit set in the
* mask, the corresponding bit in the value is copied to the connection
* tracking mark field in the connection.
- * @OVS_CT_ATTR_LABEL: %OVS_CT_LABELS_LEN value followed by %OVS_CT_LABELS_LEN
+ * @OVS_CT_ATTR_LABELS: %OVS_CT_LABELS_LEN value followed by %OVS_CT_LABELS_LEN
* mask. For each bit set in the mask, the corresponding bit in the value is
* copied to the connection tracking label field in the connection.
* @OVS_CT_ATTR_HELPER: variable length string defining conntrack ALG.
diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h
index d801bb0d9f6d..1afe9623c1a7 100644
--- a/include/uapi/linux/perf_event.h
+++ b/include/uapi/linux/perf_event.h
@@ -171,6 +171,9 @@ enum perf_branch_sample_type_shift {
PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT = 12, /* indirect jumps */
PERF_SAMPLE_BRANCH_CALL_SHIFT = 13, /* direct call */
+ PERF_SAMPLE_BRANCH_NO_FLAGS_SHIFT = 14, /* no flags */
+ PERF_SAMPLE_BRANCH_NO_CYCLES_SHIFT = 15, /* no cycles */
+
PERF_SAMPLE_BRANCH_MAX_SHIFT /* non-ABI */
};
@@ -192,6 +195,9 @@ enum perf_branch_sample_type {
PERF_SAMPLE_BRANCH_IND_JUMP = 1U << PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT,
PERF_SAMPLE_BRANCH_CALL = 1U << PERF_SAMPLE_BRANCH_CALL_SHIFT,
+ PERF_SAMPLE_BRANCH_NO_FLAGS = 1U << PERF_SAMPLE_BRANCH_NO_FLAGS_SHIFT,
+ PERF_SAMPLE_BRANCH_NO_CYCLES = 1U << PERF_SAMPLE_BRANCH_NO_CYCLES_SHIFT,
+
PERF_SAMPLE_BRANCH_MAX = 1U << PERF_SAMPLE_BRANCH_MAX_SHIFT,
};
diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h
index 8d2530daca9f..8cb18b44968e 100644
--- a/include/uapi/linux/pkt_sched.h
+++ b/include/uapi/linux/pkt_sched.h
@@ -72,6 +72,10 @@ struct tc_estimator {
#define TC_H_UNSPEC (0U)
#define TC_H_ROOT (0xFFFFFFFFU)
#define TC_H_INGRESS (0xFFFFFFF1U)
+#define TC_H_CLSACT TC_H_INGRESS
+
+#define TC_H_MIN_INGRESS 0xFFF2U
+#define TC_H_MIN_EGRESS 0xFFF3U
/* Need to corrospond to iproute2 tc/tc_core.h "enum link_layer" */
enum tc_link_layer {
diff --git a/include/uapi/linux/raid/md_u.h b/include/uapi/linux/raid/md_u.h
index 1cb8aa6850b5..36cd8210a5d1 100644
--- a/include/uapi/linux/raid/md_u.h
+++ b/include/uapi/linux/raid/md_u.h
@@ -80,7 +80,7 @@ typedef struct mdu_array_info_s {
int major_version;
int minor_version;
int patch_version;
- int ctime;
+ unsigned int ctime;
int level;
int size;
int nr_disks;
@@ -91,7 +91,7 @@ typedef struct mdu_array_info_s {
/*
* Generic state information
*/
- int utime; /* 0 Superblock update time */
+ unsigned int utime; /* 0 Superblock update time */
int state; /* 1 State bits (clean, ...) */
int active_disks; /* 2 Number of currently active disks */
int working_disks; /* 3 Number of working disks */
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
index 123a5af4e8bb..ca764b5da86d 100644
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -311,6 +311,7 @@ enum rtattr_type_t {
RTA_PREF,
RTA_ENCAP_TYPE,
RTA_ENCAP,
+ RTA_EXPIRES,
__RTA_MAX
};
diff --git a/include/uapi/linux/serial.h b/include/uapi/linux/serial.h
index 25331f9faa76..5d59c3ebf459 100644
--- a/include/uapi/linux/serial.h
+++ b/include/uapi/linux/serial.h
@@ -69,6 +69,7 @@ struct serial_struct {
#define SERIAL_IO_AU 4
#define SERIAL_IO_TSI 5
#define SERIAL_IO_MEM32BE 6
+#define SERIAL_IO_MEM16 7
#define UART_CLEAR_FIFO 0x01
#define UART_USE_FIFO 0x02
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 93ba148f923e..3e5d757407fb 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -176,7 +176,7 @@
#define PORT_S3C6400 84
-/* NWPSERIAL */
+/* NWPSERIAL, now removed */
#define PORT_NWPSERIAL 85
/* MAX3100 */
diff --git a/include/uapi/linux/serio.h b/include/uapi/linux/serio.h
index becdd78295cc..c2ea1698257f 100644
--- a/include/uapi/linux/serio.h
+++ b/include/uapi/linux/serio.h
@@ -77,5 +77,6 @@
#define SERIO_PS2MULT 0x3c
#define SERIO_TSC40 0x3d
#define SERIO_WACOM_IV 0x3e
+#define SERIO_EGALAX 0x3f
#endif /* _UAPI_SERIO_H */
diff --git a/include/uapi/linux/sock_diag.h b/include/uapi/linux/sock_diag.h
index 49230d36f9ce..bae2d80034d4 100644
--- a/include/uapi/linux/sock_diag.h
+++ b/include/uapi/linux/sock_diag.h
@@ -4,6 +4,7 @@
#include <linux/types.h>
#define SOCK_DIAG_BY_FAMILY 20
+#define SOCK_DESTROY 21
struct sock_diag_req {
__u8 sdiag_family;
diff --git a/include/uapi/linux/sockios.h b/include/uapi/linux/sockios.h
index e888b1aed69f..8e7890b26d9a 100644
--- a/include/uapi/linux/sockios.h
+++ b/include/uapi/linux/sockios.h
@@ -27,7 +27,7 @@
/* Routing table calls. */
#define SIOCADDRT 0x890B /* add routing table entry */
#define SIOCDELRT 0x890C /* delete routing table entry */
-#define SIOCRTMSG 0x890D /* call to routing system */
+#define SIOCRTMSG 0x890D /* unused */
/* Socket configuration controls. */
#define SIOCGIFNAME 0x8910 /* get iface name */
diff --git a/include/uapi/linux/uinput.h b/include/uapi/linux/uinput.h
index 013c9d8db372..dc652e224b67 100644
--- a/include/uapi/linux/uinput.h
+++ b/include/uapi/linux/uinput.h
@@ -20,6 +20,11 @@
* Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
*
* Changes/Revisions:
+ * 0.5 08/13/2015 (David Herrmann <dh.herrmann@gmail.com> &
+ * Benjamin Tissoires <benjamin.tissoires@redhat.com>)
+ * - add UI_DEV_SETUP ioctl
+ * - add UI_ABS_SETUP ioctl
+ * - add UI_GET_VERSION ioctl
* 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
* - add UI_GET_SYSNAME ioctl
* 0.3 24/05/2006 (Anssi Hannula <anssi.hannulagmail.com>)
@@ -37,8 +42,8 @@
#include <linux/types.h>
#include <linux/input.h>
-#define UINPUT_VERSION 4
-
+#define UINPUT_VERSION 5
+#define UINPUT_MAX_NAME_SIZE 80
struct uinput_ff_upload {
__u32 request_id;
@@ -58,6 +63,76 @@ struct uinput_ff_erase {
#define UI_DEV_CREATE _IO(UINPUT_IOCTL_BASE, 1)
#define UI_DEV_DESTROY _IO(UINPUT_IOCTL_BASE, 2)
+struct uinput_setup {
+ struct input_id id;
+ char name[UINPUT_MAX_NAME_SIZE];
+ __u32 ff_effects_max;
+};
+
+/**
+ * UI_DEV_SETUP - Set device parameters for setup
+ *
+ * This ioctl sets parameters for the input device to be created. It
+ * supersedes the old "struct uinput_user_dev" method, which wrote this data
+ * via write(). To actually set the absolute axes UI_ABS_SETUP should be
+ * used.
+ *
+ * The ioctl takes a "struct uinput_setup" object as argument. The fields of
+ * this object are as follows:
+ * id: See the description of "struct input_id". This field is
+ * copied unchanged into the new device.
+ * name: This is used unchanged as name for the new device.
+ * ff_effects_max: This limits the maximum numbers of force-feedback effects.
+ * See below for a description of FF with uinput.
+ *
+ * This ioctl can be called multiple times and will overwrite previous values.
+ * If this ioctl fails with -EINVAL, it is recommended to use the old
+ * "uinput_user_dev" method via write() as a fallback, in case you run on an
+ * old kernel that does not support this ioctl.
+ *
+ * This ioctl may fail with -EINVAL if it is not supported or if you passed
+ * incorrect values, -ENOMEM if the kernel runs out of memory or -EFAULT if the
+ * passed uinput_setup object cannot be read/written.
+ * If this call fails, partial data may have already been applied to the
+ * internal device.
+ */
+#define UI_DEV_SETUP _IOW(UINPUT_IOCTL_BASE, 3, struct uinput_setup)
+
+struct uinput_abs_setup {
+ __u16 code; /* axis code */
+ /* __u16 filler; */
+ struct input_absinfo absinfo;
+};
+
+/**
+ * UI_ABS_SETUP - Set absolute axis information for the device to setup
+ *
+ * This ioctl sets one absolute axis information for the input device to be
+ * created. It supersedes the old "struct uinput_user_dev" method, which wrote
+ * part of this data and the content of UI_DEV_SETUP via write().
+ *
+ * The ioctl takes a "struct uinput_abs_setup" object as argument. The fields
+ * of this object are as follows:
+ * code: The corresponding input code associated with this axis
+ * (ABS_X, ABS_Y, etc...)
+ * absinfo: See "struct input_absinfo" for a description of this field.
+ * This field is copied unchanged into the kernel for the
+ * specified axis. If the axis is not enabled via
+ * UI_SET_ABSBIT, this ioctl will enable it.
+ *
+ * This ioctl can be called multiple times and will overwrite previous values.
+ * If this ioctl fails with -EINVAL, it is recommended to use the old
+ * "uinput_user_dev" method via write() as a fallback, in case you run on an
+ * old kernel that does not support this ioctl.
+ *
+ * This ioctl may fail with -EINVAL if it is not supported or if you passed
+ * incorrect values, -ENOMEM if the kernel runs out of memory or -EFAULT if the
+ * passed uinput_setup object cannot be read/written.
+ * If this call fails, partial data may have already been applied to the
+ * internal device.
+ */
+#define UI_ABS_SETUP _IOW(UINPUT_IOCTL_BASE, 4, struct uinput_abs_setup)
+
#define UI_SET_EVBIT _IOW(UINPUT_IOCTL_BASE, 100, int)
#define UI_SET_KEYBIT _IOW(UINPUT_IOCTL_BASE, 101, int)
#define UI_SET_RELBIT _IOW(UINPUT_IOCTL_BASE, 102, int)
@@ -144,7 +219,6 @@ struct uinput_ff_erase {
#define UI_FF_UPLOAD 1
#define UI_FF_ERASE 2
-#define UINPUT_MAX_NAME_SIZE 80
struct uinput_user_dev {
char name[UINPUT_MAX_NAME_SIZE];
struct input_id id;
diff --git a/include/uapi/linux/usb/video.h b/include/uapi/linux/usb/video.h
index 3b3b95e01f71..69ab695fad2e 100644
--- a/include/uapi/linux/usb/video.h
+++ b/include/uapi/linux/usb/video.h
@@ -28,6 +28,7 @@
/* A.3. Video Interface Protocol Codes */
#define UVC_PC_PROTOCOL_UNDEFINED 0x00
+#define UVC_PC_PROTOCOL_15 0x01
/* A.5. Video Class-Specific VC Interface Descriptor Subtypes */
#define UVC_VC_DESCRIPTOR_UNDEFINED 0x00
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 1bdce501ad6b..2d225bcdb831 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -158,8 +158,10 @@ enum v4l2_colorfx {
* We reserve 16 controls for this driver. */
#define V4L2_CID_USER_S2255_BASE (V4L2_CID_USER_BASE + 0x1030)
-/* The base for the si476x driver controls. See include/media/si476x.h for the list
- * of controls. Total of 16 controls is reserved for this driver */
+/*
+ * The base for the si476x driver controls. See include/media/drv-intf/si476x.h
+ * for the list of controls. Total of 16 controls is reserved for this driver
+ */
#define V4L2_CID_USER_SI476X_BASE (V4L2_CID_USER_BASE + 0x1040)
/* The base for the TI VPE driver controls. Total of 16 controls is reserved for
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index 751b69f858c8..7d7a4c6f2090 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -575,8 +575,10 @@ struct vfio_iommu_spapr_tce_create {
__u32 flags;
/* in */
__u32 page_shift;
+ __u32 __resv1;
__u64 window_size;
__u32 levels;
+ __u32 __resv2;
/* out */
__u64 start_addr;
};
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index a0e87d16b726..14cd5ebfee6d 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -46,7 +46,7 @@
* All kernel-specific stuff were moved to media/v4l2-dev.h, so
* no #if __KERNEL tests are allowed here
*
- * See http://linuxtv.org for more info
+ * See https://linuxtv.org for more info
*
* Author: Bill Dirks <bill@thedirks.org>
* Justin Schoeman
@@ -1476,7 +1476,12 @@ struct v4l2_ext_control {
} __attribute__ ((packed));
struct v4l2_ext_controls {
- __u32 ctrl_class;
+ union {
+#ifndef __KERNEL__
+ __u32 ctrl_class;
+#endif
+ __u32 which;
+ };
__u32 count;
__u32 error_idx;
__u32 reserved[2];
@@ -1484,9 +1489,14 @@ struct v4l2_ext_controls {
};
#define V4L2_CTRL_ID_MASK (0x0fffffff)
+#ifndef __KERNEL__
#define V4L2_CTRL_ID2CLASS(id) ((id) & 0x0fff0000UL)
+#endif
+#define V4L2_CTRL_ID2WHICH(id) ((id) & 0x0fff0000UL)
#define V4L2_CTRL_DRIVER_PRIV(id) (((id) & 0xffff) >= 0x1000)
#define V4L2_CTRL_MAX_DIMS (4)
+#define V4L2_CTRL_WHICH_CUR_VAL 0
+#define V4L2_CTRL_WHICH_DEF_VAL 0x0f000000
enum v4l2_ctrl_type {
V4L2_CTRL_TYPE_INTEGER = 1,
diff --git a/include/uapi/rdma/hfi/hfi1_user.h b/include/uapi/rdma/hfi/hfi1_user.h
index 599562fe5d57..288694e422fb 100644
--- a/include/uapi/rdma/hfi/hfi1_user.h
+++ b/include/uapi/rdma/hfi/hfi1_user.h
@@ -127,35 +127,33 @@
#define HFI1_CMD_TID_UPDATE 4 /* update expected TID entries */
#define HFI1_CMD_TID_FREE 5 /* free expected TID entries */
#define HFI1_CMD_CREDIT_UPD 6 /* force an update of PIO credit */
-#define HFI1_CMD_SDMA_STATUS_UPD 7 /* force update of SDMA status ring */
+#define HFI1_CMD_SDMA_STATUS_UPD 7 /* force update of SDMA status ring */
#define HFI1_CMD_RECV_CTRL 8 /* control receipt of packets */
#define HFI1_CMD_POLL_TYPE 9 /* set the kind of polling we want */
#define HFI1_CMD_ACK_EVENT 10 /* ack & clear user status bits */
-#define HFI1_CMD_SET_PKEY 11 /* set context's pkey */
-#define HFI1_CMD_CTXT_RESET 12 /* reset context's HW send context */
+#define HFI1_CMD_SET_PKEY 11 /* set context's pkey */
+#define HFI1_CMD_CTXT_RESET 12 /* reset context's HW send context */
/* separate EPROM commands from normal PSM commands */
#define HFI1_CMD_EP_INFO 64 /* read EPROM device ID */
#define HFI1_CMD_EP_ERASE_CHIP 65 /* erase whole EPROM */
-#define HFI1_CMD_EP_ERASE_P0 66 /* erase EPROM partition 0 */
-#define HFI1_CMD_EP_ERASE_P1 67 /* erase EPROM partition 1 */
-#define HFI1_CMD_EP_READ_P0 68 /* read EPROM partition 0 */
-#define HFI1_CMD_EP_READ_P1 69 /* read EPROM partition 1 */
-#define HFI1_CMD_EP_WRITE_P0 70 /* write EPROM partition 0 */
-#define HFI1_CMD_EP_WRITE_P1 71 /* write EPROM partition 1 */
-
-#define _HFI1_EVENT_FROZEN_BIT 0
-#define _HFI1_EVENT_LINKDOWN_BIT 1
-#define _HFI1_EVENT_LID_CHANGE_BIT 2
-#define _HFI1_EVENT_LMC_CHANGE_BIT 3
-#define _HFI1_EVENT_SL2VL_CHANGE_BIT 4
+/* range 66-74 no longer used */
+#define HFI1_CMD_EP_ERASE_RANGE 75 /* erase EPROM range */
+#define HFI1_CMD_EP_READ_RANGE 76 /* read EPROM range */
+#define HFI1_CMD_EP_WRITE_RANGE 77 /* write EPROM range */
+
+#define _HFI1_EVENT_FROZEN_BIT 0
+#define _HFI1_EVENT_LINKDOWN_BIT 1
+#define _HFI1_EVENT_LID_CHANGE_BIT 2
+#define _HFI1_EVENT_LMC_CHANGE_BIT 3
+#define _HFI1_EVENT_SL2VL_CHANGE_BIT 4
#define _HFI1_MAX_EVENT_BIT _HFI1_EVENT_SL2VL_CHANGE_BIT
-#define HFI1_EVENT_FROZEN (1UL << _HFI1_EVENT_FROZEN_BIT)
-#define HFI1_EVENT_LINKDOWN_BIT (1UL << _HFI1_EVENT_LINKDOWN_BIT)
-#define HFI1_EVENT_LID_CHANGE_BIT (1UL << _HFI1_EVENT_LID_CHANGE_BIT)
-#define HFI1_EVENT_LMC_CHANGE_BIT (1UL << _HFI1_EVENT_LMC_CHANGE_BIT)
-#define HFI1_EVENT_SL2VL_CHANGE_BIT (1UL << _HFI1_EVENT_SL2VL_CHANGE_BIT)
+#define HFI1_EVENT_FROZEN (1UL << _HFI1_EVENT_FROZEN_BIT)
+#define HFI1_EVENT_LINKDOWN (1UL << _HFI1_EVENT_LINKDOWN_BIT)
+#define HFI1_EVENT_LID_CHANGE (1UL << _HFI1_EVENT_LID_CHANGE_BIT)
+#define HFI1_EVENT_LMC_CHANGE (1UL << _HFI1_EVENT_LMC_CHANGE_BIT)
+#define HFI1_EVENT_SL2VL_CHANGE (1UL << _HFI1_EVENT_SL2VL_CHANGE_BIT)
/*
* These are the status bits readable (in ASCII form, 64bit value)
diff --git a/include/uapi/scsi/cxlflash_ioctl.h b/include/uapi/scsi/cxlflash_ioctl.h
index 831351b2e660..2302f3ce5f86 100644
--- a/include/uapi/scsi/cxlflash_ioctl.h
+++ b/include/uapi/scsi/cxlflash_ioctl.h
@@ -31,6 +31,16 @@ struct dk_cxlflash_hdr {
};
/*
+ * Return flag definitions available to all ioctls
+ *
+ * Similar to the input flags, these are grown from the bottom-up with the
+ * intention that ioctl-specific return flag definitions would grow from the
+ * top-down, allowing the two sets to co-exist. While not required/enforced
+ * at this time, this provides future flexibility.
+ */
+#define DK_CXLFLASH_ALL_PORTS_ACTIVE 0x0000000000000001ULL
+
+/*
* Notes:
* -----
* The 'context_id' field of all ioctl structures contains the context
diff --git a/include/uapi/xen/gntdev.h b/include/uapi/xen/gntdev.h
index aa7610a9b867..d0661977667e 100644
--- a/include/uapi/xen/gntdev.h
+++ b/include/uapi/xen/gntdev.h
@@ -144,6 +144,56 @@ struct ioctl_gntdev_unmap_notify {
__u32 event_channel_port;
};
+struct gntdev_grant_copy_segment {
+ union {
+ void __user *virt;
+ struct {
+ grant_ref_t ref;
+ __u16 offset;
+ domid_t domid;
+ } foreign;
+ } source, dest;
+ __u16 len;
+
+ __u16 flags; /* GNTCOPY_* */
+ __s16 status; /* GNTST_* */
+};
+
+/*
+ * Copy between grant references and local buffers.
+ *
+ * The copy is split into @count @segments, each of which can copy
+ * to/from one grant reference.
+ *
+ * Each segment is similar to struct gnttab_copy in the hypervisor ABI
+ * except the local buffer is specified using a virtual address
+ * (instead of a GFN and offset).
+ *
+ * The local buffer may cross a Xen page boundary -- the driver will
+ * split segments into multiple ops if required.
+ *
+ * Returns 0 if all segments have been processed and @status in each
+ * segment is valid. Note that one or more segments may have failed
+ * (status != GNTST_okay).
+ *
+ * If the driver had to split a segment into two or more ops, @status
+ * includes the status of the first failed op for that segment (or
+ * GNTST_okay if all ops were successful).
+ *
+ * If -1 is returned, the status of all segments is undefined.
+ *
+ * EINVAL: A segment has local buffers for both source and
+ * destination.
+ * EINVAL: A segment crosses the boundary of a foreign page.
+ * EFAULT: A segment's local buffer is not accessible.
+ */
+#define IOCTL_GNTDEV_GRANT_COPY \
+ _IOC(_IOC_NONE, 'G', 8, sizeof(struct ioctl_gntdev_grant_copy))
+struct ioctl_gntdev_grant_copy {
+ unsigned int count;
+ struct gntdev_grant_copy_segment __user *segments;
+};
+
/* Clear (set to zero) the byte specified by index */
#define UNMAP_NOTIFY_CLEAR_BYTE 0x1
/* Send an interrupt on the indicated event channel */
diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h
index 85dedca3dcfb..eeba75395f7d 100644
--- a/include/video/imx-ipu-v3.h
+++ b/include/video/imx-ipu-v3.h
@@ -343,7 +343,6 @@ struct ipu_client_platformdata {
int di;
int dc;
int dp;
- int dmfc;
int dma[2];
};
diff --git a/include/xen/interface/io/ring.h b/include/xen/interface/io/ring.h
index 7d28aff605c7..7dc685b4057d 100644
--- a/include/xen/interface/io/ring.h
+++ b/include/xen/interface/io/ring.h
@@ -181,6 +181,20 @@ struct __name##_back_ring { \
#define RING_GET_REQUEST(_r, _idx) \
(&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))
+/*
+ * Get a local copy of a request.
+ *
+ * Use this in preference to RING_GET_REQUEST() so all processing is
+ * done on a local copy that cannot be modified by the other end.
+ *
+ * Note that https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58145 may cause this
+ * to be ineffective where _req is a struct which consists of only bitfields.
+ */
+#define RING_COPY_REQUEST(_r, _idx, _req) do { \
+ /* Use volatile to force the copy into _req. */ \
+ *(_req) = *(volatile typeof(_req))RING_GET_REQUEST(_r, _idx); \
+} while (0)
+
#define RING_GET_RESPONSE(_r, _idx) \
(&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].rsp))
diff --git a/include/xen/interface/platform.h b/include/xen/interface/platform.h
index 8e035871360e..732efb08c3e1 100644
--- a/include/xen/interface/platform.h
+++ b/include/xen/interface/platform.h
@@ -35,14 +35,23 @@
* Set clock such that it would read <secs,nsecs> after 00:00:00 UTC,
* 1 January, 1970 if the current system time was <system_time>.
*/
-#define XENPF_settime 17
-struct xenpf_settime {
+#define XENPF_settime32 17
+struct xenpf_settime32 {
/* IN variables. */
uint32_t secs;
uint32_t nsecs;
uint64_t system_time;
};
-DEFINE_GUEST_HANDLE_STRUCT(xenpf_settime_t);
+DEFINE_GUEST_HANDLE_STRUCT(xenpf_settime32_t);
+#define XENPF_settime64 62
+struct xenpf_settime64 {
+ /* IN variables. */
+ uint64_t secs;
+ uint32_t nsecs;
+ uint32_t mbz;
+ uint64_t system_time;
+};
+DEFINE_GUEST_HANDLE_STRUCT(xenpf_settime64_t);
/*
* Request memory range (@mfn, @mfn+@nr_mfns-1) to have type @type.
@@ -495,7 +504,8 @@ struct xen_platform_op {
uint32_t cmd;
uint32_t interface_version; /* XENPF_INTERFACE_VERSION */
union {
- struct xenpf_settime settime;
+ struct xenpf_settime32 settime32;
+ struct xenpf_settime64 settime64;
struct xenpf_add_memtype add_memtype;
struct xenpf_del_memtype del_memtype;
struct xenpf_read_memtype read_memtype;
diff --git a/include/xen/interface/xen.h b/include/xen/interface/xen.h
index 167071c290b3..d1331121c0bd 100644
--- a/include/xen/interface/xen.h
+++ b/include/xen/interface/xen.h
@@ -48,7 +48,7 @@
#define __HYPERVISOR_set_callbacks 4
#define __HYPERVISOR_fpu_taskswitch 5
#define __HYPERVISOR_sched_op_compat 6
-#define __HYPERVISOR_dom0_op 7
+#define __HYPERVISOR_platform_op 7
#define __HYPERVISOR_set_debugreg 8
#define __HYPERVISOR_get_debugreg 9
#define __HYPERVISOR_update_descriptor 10
diff --git a/include/xen/xen-ops.h b/include/xen/xen-ops.h
index e4e214a5abd5..86abe07b20ec 100644
--- a/include/xen/xen-ops.h
+++ b/include/xen/xen-ops.h
@@ -5,6 +5,7 @@
#include <linux/notifier.h>
#include <linux/efi.h>
#include <asm/xen/interface.h>
+#include <xen/interface/vcpu.h>
DECLARE_PER_CPU(struct vcpu_info *, xen_vcpu);
@@ -18,6 +19,10 @@ void xen_arch_suspend(void);
void xen_resume_notifier_register(struct notifier_block *nb);
void xen_resume_notifier_unregister(struct notifier_block *nb);
+bool xen_vcpu_stolen(int vcpu);
+void xen_setup_runstate_info(int cpu);
+void xen_get_runstate_snapshot(struct vcpu_runstate_info *res);
+
int xen_setup_shutdown_event(void);
extern unsigned long *xen_contiguous_bitmap;
diff --git a/init/Kconfig b/init/Kconfig
index c24b6f767bf0..5481b49e8c3f 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -940,95 +940,24 @@ menuconfig CGROUPS
if CGROUPS
-config CGROUP_DEBUG
- bool "Example debug cgroup subsystem"
- default n
- help
- This option enables a simple cgroup subsystem that
- exports useful debugging information about the cgroups
- framework.
-
- Say N if unsure.
-
-config CGROUP_FREEZER
- bool "Freezer cgroup subsystem"
- help
- Provides a way to freeze and unfreeze all tasks in a
- cgroup.
-
-config CGROUP_PIDS
- bool "PIDs cgroup subsystem"
- help
- Provides enforcement of process number limits in the scope of a
- cgroup. Any attempt to fork more processes than is allowed in the
- cgroup will fail. PIDs are fundamentally a global resource because it
- is fairly trivial to reach PID exhaustion before you reach even a
- conservative kmemcg limit. As a result, it is possible to grind a
- system to halt without being limited by other cgroup policies. The
- PIDs cgroup subsystem is designed to stop this from happening.
-
- It should be noted that organisational operations (such as attaching
- to a cgroup hierarchy will *not* be blocked by the PIDs subsystem),
- since the PIDs limit only affects a process's ability to fork, not to
- attach to a cgroup.
-
-config CGROUP_DEVICE
- bool "Device controller for cgroups"
- help
- Provides a cgroup implementing whitelists for devices which
- a process in the cgroup can mknod or open.
-
-config CPUSETS
- bool "Cpuset support"
- help
- This option will let you create and manage CPUSETs which
- allow dynamically partitioning a system into sets of CPUs and
- Memory Nodes and assigning tasks to run only within those sets.
- This is primarily useful on large SMP or NUMA systems.
-
- Say N if unsure.
-
-config PROC_PID_CPUSET
- bool "Include legacy /proc/<pid>/cpuset file"
- depends on CPUSETS
- default y
-
-config CGROUP_CPUACCT
- bool "Simple CPU accounting cgroup subsystem"
- help
- Provides a simple Resource Controller for monitoring the
- total CPU consumed by the tasks in a cgroup.
-
config PAGE_COUNTER
bool
config MEMCG
- bool "Memory Resource Controller for Control Groups"
+ bool "Memory controller"
select PAGE_COUNTER
select EVENTFD
help
- Provides a memory resource controller that manages both anonymous
- memory and page cache. (See Documentation/cgroups/memory.txt)
+ Provides control over the memory footprint of tasks in a cgroup.
config MEMCG_SWAP
- bool "Memory Resource Controller Swap Extension"
+ bool "Swap controller"
depends on MEMCG && SWAP
help
- Add swap management feature to memory resource controller. When you
- enable this, you can limit mem+swap usage per cgroup. In other words,
- when you disable this, memory resource controller has no cares to
- usage of swap...a process can exhaust all of the swap. This extension
- is useful when you want to avoid exhaustion swap but this itself
- adds more overheads and consumes memory for remembering information.
- Especially if you use 32bit system or small memory system, please
- be careful about enabling this. When memory resource controller
- is disabled by boot option, this will be automatically disabled and
- there will be no overhead from this. Even when you set this config=y,
- if boot option "swapaccount=0" is set, swap will not be accounted.
- Now, memory usage of swap_cgroup is 2 bytes per entry. If swap page
- size is 4096bytes, 512k per 1Gbytes of swap.
+ Provides control over the swap space consumed by tasks in a cgroup.
+
config MEMCG_SWAP_ENABLED
- bool "Memory Resource Controller Swap Extension enabled by default"
+ bool "Swap controller enabled by default"
depends on MEMCG_SWAP
default y
help
@@ -1052,34 +981,43 @@ config MEMCG_KMEM
the kmem extension can use it to guarantee that no group of processes
will ever exhaust kernel resources alone.
-config CGROUP_HUGETLB
- bool "HugeTLB Resource Controller for Control Groups"
- depends on HUGETLB_PAGE
- select PAGE_COUNTER
+config BLK_CGROUP
+ bool "IO controller"
+ depends on BLOCK
default n
- help
- Provides a cgroup Resource Controller for HugeTLB pages.
- When you enable this, you can put a per cgroup limit on HugeTLB usage.
- The limit is enforced during page fault. Since HugeTLB doesn't
- support page reclaim, enforcing the limit at page fault time implies
- that, the application will get SIGBUS signal if it tries to access
- HugeTLB pages beyond its limit. This requires the application to know
- beforehand how much HugeTLB pages it would require for its use. The
- control group is tracked in the third page lru pointer. This means
- that we cannot use the controller with huge page less than 3 pages.
+ ---help---
+ Generic block IO controller cgroup interface. This is the common
+ cgroup interface which should be used by various IO controlling
+ policies.
-config CGROUP_PERF
- bool "Enable perf_event per-cpu per-container group (cgroup) monitoring"
- depends on PERF_EVENTS && CGROUPS
- help
- This option extends the per-cpu mode to restrict monitoring to
- threads which belong to the cgroup specified and run on the
- designated cpu.
+ Currently, CFQ IO scheduler uses it to recognize task groups and
+ control disk bandwidth allocation (proportional time slice allocation)
+ to such task groups. It is also used by bio throttling logic in
+ block layer to implement upper limit in IO rates on a device.
- Say N if unsure.
+ This option only enables generic Block IO controller infrastructure.
+ One needs to also enable actual IO controlling logic/policy. For
+ enabling proportional weight division of disk bandwidth in CFQ, set
+ CONFIG_CFQ_GROUP_IOSCHED=y; for enabling throttling policy, set
+ CONFIG_BLK_DEV_THROTTLING=y.
+
+ See Documentation/cgroups/blkio-controller.txt for more information.
+
+config DEBUG_BLK_CGROUP
+ bool "IO controller debugging"
+ depends on BLK_CGROUP
+ default n
+ ---help---
+ Enable some debugging help. Currently it exports additional stat
+ files in a cgroup which can be useful for debugging.
+
+config CGROUP_WRITEBACK
+ bool
+ depends on MEMCG && BLK_CGROUP
+ default y
menuconfig CGROUP_SCHED
- bool "Group CPU scheduler"
+ bool "CPU controller"
default n
help
This feature lets CPU scheduler recognize task groups and control CPU
@@ -1116,40 +1054,89 @@ config RT_GROUP_SCHED
endif #CGROUP_SCHED
-config BLK_CGROUP
- bool "Block IO controller"
- depends on BLOCK
+config CGROUP_PIDS
+ bool "PIDs controller"
+ help
+ Provides enforcement of process number limits in the scope of a
+ cgroup. Any attempt to fork more processes than is allowed in the
+ cgroup will fail. PIDs are fundamentally a global resource because it
+ is fairly trivial to reach PID exhaustion before you reach even a
+ conservative kmemcg limit. As a result, it is possible to grind a
+ system to halt without being limited by other cgroup policies. The
+ PIDs cgroup subsystem is designed to stop this from happening.
+
+ It should be noted that organisational operations (such as attaching
+ to a cgroup hierarchy will *not* be blocked by the PIDs subsystem),
+ since the PIDs limit only affects a process's ability to fork, not to
+ attach to a cgroup.
+
+config CGROUP_FREEZER
+ bool "Freezer controller"
+ help
+ Provides a way to freeze and unfreeze all tasks in a
+ cgroup.
+
+config CGROUP_HUGETLB
+ bool "HugeTLB controller"
+ depends on HUGETLB_PAGE
+ select PAGE_COUNTER
default n
- ---help---
- Generic block IO controller cgroup interface. This is the common
- cgroup interface which should be used by various IO controlling
- policies.
+ help
+ Provides a cgroup controller for HugeTLB pages.
+ When you enable this, you can put a per cgroup limit on HugeTLB usage.
+ The limit is enforced during page fault. Since HugeTLB doesn't
+ support page reclaim, enforcing the limit at page fault time implies
+ that, the application will get SIGBUS signal if it tries to access
+ HugeTLB pages beyond its limit. This requires the application to know
+ beforehand how much HugeTLB pages it would require for its use. The
+ control group is tracked in the third page lru pointer. This means
+ that we cannot use the controller with huge page less than 3 pages.
- Currently, CFQ IO scheduler uses it to recognize task groups and
- control disk bandwidth allocation (proportional time slice allocation)
- to such task groups. It is also used by bio throttling logic in
- block layer to implement upper limit in IO rates on a device.
+config CPUSETS
+ bool "Cpuset controller"
+ help
+ This option will let you create and manage CPUSETs which
+ allow dynamically partitioning a system into sets of CPUs and
+ Memory Nodes and assigning tasks to run only within those sets.
+ This is primarily useful on large SMP or NUMA systems.
- This option only enables generic Block IO controller infrastructure.
- One needs to also enable actual IO controlling logic/policy. For
- enabling proportional weight division of disk bandwidth in CFQ, set
- CONFIG_CFQ_GROUP_IOSCHED=y; for enabling throttling policy, set
- CONFIG_BLK_DEV_THROTTLING=y.
+ Say N if unsure.
- See Documentation/cgroups/blkio-controller.txt for more information.
+config PROC_PID_CPUSET
+ bool "Include legacy /proc/<pid>/cpuset file"
+ depends on CPUSETS
+ default y
-config DEBUG_BLK_CGROUP
- bool "Enable Block IO controller debugging"
- depends on BLK_CGROUP
+config CGROUP_DEVICE
+ bool "Device controller"
+ help
+ Provides a cgroup controller implementing whitelists for
+ devices which a process in the cgroup can mknod or open.
+
+config CGROUP_CPUACCT
+ bool "Simple CPU accounting controller"
+ help
+ Provides a simple controller for monitoring the
+ total CPU consumed by the tasks in a cgroup.
+
+config CGROUP_PERF
+ bool "Perf controller"
+ depends on PERF_EVENTS
+ help
+ This option extends the perf per-cpu mode to restrict monitoring
+ to threads which belong to the cgroup specified and run on the
+ designated cpu.
+
+ Say N if unsure.
+
+config CGROUP_DEBUG
+ bool "Example controller"
default n
- ---help---
- Enable some debugging help. Currently it exports additional stat
- files in a cgroup which can be useful for debugging.
+ help
+ This option enables a simple controller that exports
+ debugging information about the cgroups framework.
-config CGROUP_WRITEBACK
- bool
- depends on MEMCG && BLK_CGROUP
- default y
+ Say N.
endif # CGROUPS
@@ -2030,13 +2017,6 @@ config INIT_ALL_POSSIBLE
it was better to provide this option than to break all the archs
and have several arch maintainers pursuing me down dark alleys.
-config STOP_MACHINE
- bool
- default y
- depends on (SMP && MODULE_UNLOAD) || HOTPLUG_CPU
- help
- Need stop_machine() primitive.
-
source "block/Kconfig"
config PREEMPT_NOTIFIERS
diff --git a/init/main.c b/init/main.c
index 9e64d7097f1a..c6ebefafa496 100644
--- a/init/main.c
+++ b/init/main.c
@@ -943,6 +943,8 @@ static int __ref kernel_init(void *unused)
flush_delayed_fput();
+ rcu_end_inkernel_boot();
+
if (ramdisk_execute_command) {
ret = run_init_process(ramdisk_execute_command);
if (!ret)
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 161a1807e6ef..f4617cf07069 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -1438,7 +1438,7 @@ static int __init init_mqueue_fs(void)
mqueue_inode_cachep = kmem_cache_create("mqueue_inode_cache",
sizeof(struct mqueue_inode_info), 0,
- SLAB_HWCACHE_ALIGN, init_once);
+ SLAB_HWCACHE_ALIGN|SLAB_ACCOUNT, init_once);
if (mqueue_inode_cachep == NULL)
return -ENOMEM;
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 3f4c99e06c6b..b0799bced518 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -28,11 +28,17 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr)
attr->value_size == 0)
return ERR_PTR(-EINVAL);
+ if (attr->value_size >= 1 << (KMALLOC_SHIFT_MAX - 1))
+ /* if value_size is bigger, the user space won't be able to
+ * access the elements.
+ */
+ return ERR_PTR(-E2BIG);
+
elem_size = round_up(attr->value_size, 8);
/* check round_up into zero and u32 overflow */
if (elem_size == 0 ||
- attr->max_entries > (U32_MAX - sizeof(*array)) / elem_size)
+ attr->max_entries > (U32_MAX - PAGE_SIZE - sizeof(*array)) / elem_size)
return ERR_PTR(-ENOMEM);
array_size = sizeof(*array) + attr->max_entries * elem_size;
@@ -105,7 +111,7 @@ static int array_map_update_elem(struct bpf_map *map, void *key, void *value,
/* all elements already exist */
return -EEXIST;
- memcpy(array->value + array->elem_size * index, value, array->elem_size);
+ memcpy(array->value + array->elem_size * index, value, map->value_size);
return 0;
}
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 334b1bdd572c..972d9a8e4ac4 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -306,10 +306,6 @@ static unsigned int __bpf_prog_run(void *ctx, const struct bpf_insn *insn)
FP = (u64) (unsigned long) &stack[ARRAY_SIZE(stack)];
ARG1 = (u64) (unsigned long) ctx;
- /* Registers used in classic BPF programs need to be reset first. */
- regs[BPF_REG_A] = 0;
- regs[BPF_REG_X] = 0;
-
select_insn:
goto *jumptable[insn->code];
diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
index 19909b22b4f8..c5b30fd8a315 100644
--- a/kernel/bpf/hashtab.c
+++ b/kernel/bpf/hashtab.c
@@ -14,11 +14,15 @@
#include <linux/filter.h>
#include <linux/vmalloc.h>
+struct bucket {
+ struct hlist_head head;
+ raw_spinlock_t lock;
+};
+
struct bpf_htab {
struct bpf_map map;
- struct hlist_head *buckets;
- raw_spinlock_t lock;
- u32 count; /* number of elements in this hashtable */
+ struct bucket *buckets;
+ atomic_t count; /* number of elements in this hashtable */
u32 n_buckets; /* number of hash buckets */
u32 elem_size; /* size of each element in bytes */
};
@@ -64,34 +68,51 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
*/
goto free_htab;
- err = -ENOMEM;
+ if (htab->map.value_size >= (1 << (KMALLOC_SHIFT_MAX - 1)) -
+ MAX_BPF_STACK - sizeof(struct htab_elem))
+ /* if value_size is bigger, the user space won't be able to
+ * access the elements via bpf syscall. This check also makes
+ * sure that the elem_size doesn't overflow and it's
+ * kmalloc-able later in htab_map_update_elem()
+ */
+ goto free_htab;
+
+ htab->elem_size = sizeof(struct htab_elem) +
+ round_up(htab->map.key_size, 8) +
+ htab->map.value_size;
+
/* prevent zero size kmalloc and check for u32 overflow */
if (htab->n_buckets == 0 ||
- htab->n_buckets > U32_MAX / sizeof(struct hlist_head))
+ htab->n_buckets > U32_MAX / sizeof(struct bucket))
+ goto free_htab;
+
+ if ((u64) htab->n_buckets * sizeof(struct bucket) +
+ (u64) htab->elem_size * htab->map.max_entries >=
+ U32_MAX - PAGE_SIZE)
+ /* make sure page count doesn't overflow */
goto free_htab;
- htab->buckets = kmalloc_array(htab->n_buckets, sizeof(struct hlist_head),
+ htab->map.pages = round_up(htab->n_buckets * sizeof(struct bucket) +
+ htab->elem_size * htab->map.max_entries,
+ PAGE_SIZE) >> PAGE_SHIFT;
+
+ err = -ENOMEM;
+ htab->buckets = kmalloc_array(htab->n_buckets, sizeof(struct bucket),
GFP_USER | __GFP_NOWARN);
if (!htab->buckets) {
- htab->buckets = vmalloc(htab->n_buckets * sizeof(struct hlist_head));
+ htab->buckets = vmalloc(htab->n_buckets * sizeof(struct bucket));
if (!htab->buckets)
goto free_htab;
}
- for (i = 0; i < htab->n_buckets; i++)
- INIT_HLIST_HEAD(&htab->buckets[i]);
+ for (i = 0; i < htab->n_buckets; i++) {
+ INIT_HLIST_HEAD(&htab->buckets[i].head);
+ raw_spin_lock_init(&htab->buckets[i].lock);
+ }
- raw_spin_lock_init(&htab->lock);
- htab->count = 0;
+ atomic_set(&htab->count, 0);
- htab->elem_size = sizeof(struct htab_elem) +
- round_up(htab->map.key_size, 8) +
- htab->map.value_size;
-
- htab->map.pages = round_up(htab->n_buckets * sizeof(struct hlist_head) +
- htab->elem_size * htab->map.max_entries,
- PAGE_SIZE) >> PAGE_SHIFT;
return &htab->map;
free_htab:
@@ -104,11 +125,16 @@ static inline u32 htab_map_hash(const void *key, u32 key_len)
return jhash(key, key_len, 0);
}
-static inline struct hlist_head *select_bucket(struct bpf_htab *htab, u32 hash)
+static inline struct bucket *__select_bucket(struct bpf_htab *htab, u32 hash)
{
return &htab->buckets[hash & (htab->n_buckets - 1)];
}
+static inline struct hlist_head *select_bucket(struct bpf_htab *htab, u32 hash)
+{
+ return &__select_bucket(htab, hash)->head;
+}
+
static struct htab_elem *lookup_elem_raw(struct hlist_head *head, u32 hash,
void *key, u32 key_size)
{
@@ -211,6 +237,7 @@ static int htab_map_update_elem(struct bpf_map *map, void *key, void *value,
struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
struct htab_elem *l_new, *l_old;
struct hlist_head *head;
+ struct bucket *b;
unsigned long flags;
u32 key_size;
int ret;
@@ -222,7 +249,7 @@ static int htab_map_update_elem(struct bpf_map *map, void *key, void *value,
WARN_ON_ONCE(!rcu_read_lock_held());
/* allocate new element outside of lock */
- l_new = kmalloc(htab->elem_size, GFP_ATOMIC);
+ l_new = kmalloc(htab->elem_size, GFP_ATOMIC | __GFP_NOWARN);
if (!l_new)
return -ENOMEM;
@@ -232,15 +259,15 @@ static int htab_map_update_elem(struct bpf_map *map, void *key, void *value,
memcpy(l_new->key + round_up(key_size, 8), value, map->value_size);
l_new->hash = htab_map_hash(l_new->key, key_size);
+ b = __select_bucket(htab, l_new->hash);
+ head = &b->head;
/* bpf_map_update_elem() can be called in_irq() */
- raw_spin_lock_irqsave(&htab->lock, flags);
-
- head = select_bucket(htab, l_new->hash);
+ raw_spin_lock_irqsave(&b->lock, flags);
l_old = lookup_elem_raw(head, l_new->hash, key, key_size);
- if (!l_old && unlikely(htab->count >= map->max_entries)) {
+ if (!l_old && unlikely(atomic_read(&htab->count) >= map->max_entries)) {
/* if elem with this 'key' doesn't exist and we've reached
* max_entries limit, fail insertion of new elem
*/
@@ -268,13 +295,13 @@ static int htab_map_update_elem(struct bpf_map *map, void *key, void *value,
hlist_del_rcu(&l_old->hash_node);
kfree_rcu(l_old, rcu);
} else {
- htab->count++;
+ atomic_inc(&htab->count);
}
- raw_spin_unlock_irqrestore(&htab->lock, flags);
+ raw_spin_unlock_irqrestore(&b->lock, flags);
return 0;
err:
- raw_spin_unlock_irqrestore(&htab->lock, flags);
+ raw_spin_unlock_irqrestore(&b->lock, flags);
kfree(l_new);
return ret;
}
@@ -284,6 +311,7 @@ static int htab_map_delete_elem(struct bpf_map *map, void *key)
{
struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
struct hlist_head *head;
+ struct bucket *b;
struct htab_elem *l;
unsigned long flags;
u32 hash, key_size;
@@ -294,21 +322,21 @@ static int htab_map_delete_elem(struct bpf_map *map, void *key)
key_size = map->key_size;
hash = htab_map_hash(key, key_size);
+ b = __select_bucket(htab, hash);
+ head = &b->head;
- raw_spin_lock_irqsave(&htab->lock, flags);
-
- head = select_bucket(htab, hash);
+ raw_spin_lock_irqsave(&b->lock, flags);
l = lookup_elem_raw(head, hash, key, key_size);
if (l) {
hlist_del_rcu(&l->hash_node);
- htab->count--;
+ atomic_dec(&htab->count);
kfree_rcu(l, rcu);
ret = 0;
}
- raw_spin_unlock_irqrestore(&htab->lock, flags);
+ raw_spin_unlock_irqrestore(&b->lock, flags);
return ret;
}
@@ -323,7 +351,7 @@ static void delete_all_elements(struct bpf_htab *htab)
hlist_for_each_entry_safe(l, n, head, hash_node) {
hlist_del_rcu(&l->hash_node);
- htab->count--;
+ atomic_dec(&htab->count);
kfree(l);
}
}
diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c
index be6d726e31c9..f2ece3c174a5 100644
--- a/kernel/bpf/inode.c
+++ b/kernel/bpf/inode.c
@@ -34,7 +34,7 @@ static void *bpf_any_get(void *raw, enum bpf_type type)
atomic_inc(&((struct bpf_prog *)raw)->aux->refcnt);
break;
case BPF_TYPE_MAP:
- atomic_inc(&((struct bpf_map *)raw)->refcnt);
+ bpf_map_inc(raw, true);
break;
default:
WARN_ON_ONCE(1);
@@ -51,7 +51,7 @@ static void bpf_any_put(void *raw, enum bpf_type type)
bpf_prog_put(raw);
break;
case BPF_TYPE_MAP:
- bpf_map_put(raw);
+ bpf_map_put_with_uref(raw);
break;
default:
WARN_ON_ONCE(1);
@@ -64,7 +64,7 @@ static void *bpf_fd_probe_obj(u32 ufd, enum bpf_type *type)
void *raw;
*type = BPF_TYPE_MAP;
- raw = bpf_map_get(ufd);
+ raw = bpf_map_get_with_uref(ufd);
if (IS_ERR(raw)) {
*type = BPF_TYPE_PROG;
raw = bpf_prog_get(ufd);
@@ -187,11 +187,31 @@ static int bpf_mkobj(struct inode *dir, struct dentry *dentry, umode_t mode,
}
}
+static int bpf_link(struct dentry *old_dentry, struct inode *dir,
+ struct dentry *new_dentry)
+{
+ if (bpf_dname_reserved(new_dentry))
+ return -EPERM;
+
+ return simple_link(old_dentry, dir, new_dentry);
+}
+
+static int bpf_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ if (bpf_dname_reserved(new_dentry))
+ return -EPERM;
+
+ return simple_rename(old_dir, old_dentry, new_dir, new_dentry);
+}
+
static const struct inode_operations bpf_dir_iops = {
.lookup = simple_lookup,
.mknod = bpf_mkobj,
.mkdir = bpf_mkdir,
.rmdir = simple_rmdir,
+ .rename = bpf_rename,
+ .link = bpf_link,
.unlink = simple_unlink,
};
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 0d3313d02a7e..637397059f76 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -82,6 +82,14 @@ static void bpf_map_free_deferred(struct work_struct *work)
map->ops->map_free(map);
}
+static void bpf_map_put_uref(struct bpf_map *map)
+{
+ if (atomic_dec_and_test(&map->usercnt)) {
+ if (map->map_type == BPF_MAP_TYPE_PROG_ARRAY)
+ bpf_fd_array_map_clear(map);
+ }
+}
+
/* decrement map refcnt and schedule it for freeing via workqueue
* (unrelying map implementation ops->map_free() might sleep)
*/
@@ -93,22 +101,40 @@ void bpf_map_put(struct bpf_map *map)
}
}
+void bpf_map_put_with_uref(struct bpf_map *map)
+{
+ bpf_map_put_uref(map);
+ bpf_map_put(map);
+}
+
static int bpf_map_release(struct inode *inode, struct file *filp)
{
- struct bpf_map *map = filp->private_data;
+ bpf_map_put_with_uref(filp->private_data);
+ return 0;
+}
- if (map->map_type == BPF_MAP_TYPE_PROG_ARRAY)
- /* prog_array stores refcnt-ed bpf_prog pointers
- * release them all when user space closes prog_array_fd
- */
- bpf_fd_array_map_clear(map);
+#ifdef CONFIG_PROC_FS
+static void bpf_map_show_fdinfo(struct seq_file *m, struct file *filp)
+{
+ const struct bpf_map *map = filp->private_data;
- bpf_map_put(map);
- return 0;
+ seq_printf(m,
+ "map_type:\t%u\n"
+ "key_size:\t%u\n"
+ "value_size:\t%u\n"
+ "max_entries:\t%u\n",
+ map->map_type,
+ map->key_size,
+ map->value_size,
+ map->max_entries);
}
+#endif
static const struct file_operations bpf_map_fops = {
- .release = bpf_map_release,
+#ifdef CONFIG_PROC_FS
+ .show_fdinfo = bpf_map_show_fdinfo,
+#endif
+ .release = bpf_map_release,
};
int bpf_map_new_fd(struct bpf_map *map)
@@ -142,6 +168,7 @@ static int map_create(union bpf_attr *attr)
return PTR_ERR(map);
atomic_set(&map->refcnt, 1);
+ atomic_set(&map->usercnt, 1);
err = bpf_map_charge_memlock(map);
if (err)
@@ -174,7 +201,14 @@ struct bpf_map *__bpf_map_get(struct fd f)
return f.file->private_data;
}
-struct bpf_map *bpf_map_get(u32 ufd)
+void bpf_map_inc(struct bpf_map *map, bool uref)
+{
+ atomic_inc(&map->refcnt);
+ if (uref)
+ atomic_inc(&map->usercnt);
+}
+
+struct bpf_map *bpf_map_get_with_uref(u32 ufd)
{
struct fd f = fdget(ufd);
struct bpf_map *map;
@@ -183,7 +217,7 @@ struct bpf_map *bpf_map_get(u32 ufd)
if (IS_ERR(map))
return map;
- atomic_inc(&map->refcnt);
+ bpf_map_inc(map, true);
fdput(f);
return map;
@@ -226,7 +260,7 @@ static int map_lookup_elem(union bpf_attr *attr)
goto free_key;
err = -ENOMEM;
- value = kmalloc(map->value_size, GFP_USER);
+ value = kmalloc(map->value_size, GFP_USER | __GFP_NOWARN);
if (!value)
goto free_key;
@@ -285,7 +319,7 @@ static int map_update_elem(union bpf_attr *attr)
goto free_key;
err = -ENOMEM;
- value = kmalloc(map->value_size, GFP_USER);
+ value = kmalloc(map->value_size, GFP_USER | __GFP_NOWARN);
if (!value)
goto free_key;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index c6073056badf..d1d3e8f57de9 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -1121,6 +1121,16 @@ static int check_alu_op(struct verifier_env *env, struct bpf_insn *insn)
return -EINVAL;
}
+ if ((opcode == BPF_LSH || opcode == BPF_RSH ||
+ opcode == BPF_ARSH) && BPF_SRC(insn->code) == BPF_K) {
+ int size = BPF_CLASS(insn->code) == BPF_ALU64 ? 64 : 32;
+
+ if (insn->imm < 0 || insn->imm >= size) {
+ verbose("invalid shift %d\n", insn->imm);
+ return -EINVAL;
+ }
+ }
+
/* pattern match 'bpf_add Rx, imm' instruction */
if (opcode == BPF_ADD && BPF_CLASS(insn->code) == BPF_ALU64 &&
regs[insn->dst_reg].type == FRAME_PTR &&
@@ -2021,8 +2031,7 @@ static int replace_map_fd_with_map_ptr(struct verifier_env *env)
* will be used by the valid program until it's unloaded
* and all maps are released in free_bpf_prog_info()
*/
- atomic_inc(&map->refcnt);
-
+ bpf_map_inc(map, false);
fdput(f);
next_insn:
insn++;
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index f1603c153890..c03a640ef6da 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -57,8 +57,8 @@
#include <linux/vmalloc.h> /* TODO: replace with more sophisticated array */
#include <linux/kthread.h>
#include <linux/delay.h>
-
#include <linux/atomic.h>
+#include <net/sock.h>
/*
* pidlists linger the following amount before being destroyed. The goal
@@ -98,6 +98,12 @@ static DEFINE_SPINLOCK(css_set_lock);
static DEFINE_SPINLOCK(cgroup_idr_lock);
/*
+ * Protects cgroup_file->kn for !self csses. It synchronizes notifications
+ * against file removal/re-creation across css hiding.
+ */
+static DEFINE_SPINLOCK(cgroup_file_kn_lock);
+
+/*
* Protects cgroup_subsys->release_agent_path. Modifying it also requires
* cgroup_mutex. Reading requires either cgroup_mutex or this spinlock.
*/
@@ -205,6 +211,7 @@ static unsigned long have_free_callback __read_mostly;
/* Ditto for the can_fork callback. */
static unsigned long have_canfork_callback __read_mostly;
+static struct file_system_type cgroup2_fs_type;
static struct cftype cgroup_dfl_base_files[];
static struct cftype cgroup_legacy_base_files[];
@@ -434,11 +441,6 @@ static bool cgroup_tryget(struct cgroup *cgrp)
return css_tryget(&cgrp->self);
}
-static void cgroup_put(struct cgroup *cgrp)
-{
- css_put(&cgrp->self);
-}
-
struct cgroup_subsys_state *of_css(struct kernfs_open_file *of)
{
struct cgroup *cgrp = of->kn->parent->priv;
@@ -459,25 +461,6 @@ struct cgroup_subsys_state *of_css(struct kernfs_open_file *of)
}
EXPORT_SYMBOL_GPL(of_css);
-/**
- * cgroup_is_descendant - test ancestry
- * @cgrp: the cgroup to be tested
- * @ancestor: possible ancestor of @cgrp
- *
- * Test whether @cgrp is a descendant of @ancestor. It also returns %true
- * if @cgrp == @ancestor. This function is safe to call as long as @cgrp
- * and @ancestor are accessible.
- */
-bool cgroup_is_descendant(struct cgroup *cgrp, struct cgroup *ancestor)
-{
- while (cgrp) {
- if (cgrp == ancestor)
- return true;
- cgrp = cgroup_parent(cgrp);
- }
- return false;
-}
-
static int notify_on_release(const struct cgroup *cgrp)
{
return test_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags);
@@ -754,9 +737,11 @@ static void put_css_set_locked(struct css_set *cset)
if (!atomic_dec_and_test(&cset->refcount))
return;
- /* This css_set is dead. unlink it and release cgroup refcounts */
- for_each_subsys(ss, ssid)
+ /* This css_set is dead. unlink it and release cgroup and css refs */
+ for_each_subsys(ss, ssid) {
list_del(&cset->e_cset_node[ssid]);
+ css_put(cset->subsys[ssid]);
+ }
hash_del(&cset->hlist);
css_set_count--;
@@ -1056,9 +1041,13 @@ static struct css_set *find_css_set(struct css_set *old_cset,
key = css_set_hash(cset->subsys);
hash_add(css_set_table, &cset->hlist, key);
- for_each_subsys(ss, ssid)
+ for_each_subsys(ss, ssid) {
+ struct cgroup_subsys_state *css = cset->subsys[ssid];
+
list_add_tail(&cset->e_cset_node[ssid],
- &cset->subsys[ssid]->cgroup->e_csets[ssid]);
+ &css->cgroup->e_csets[ssid]);
+ css_get(css);
+ }
spin_unlock_bh(&css_set_lock);
@@ -1393,6 +1382,16 @@ static void cgroup_rm_file(struct cgroup *cgrp, const struct cftype *cft)
char name[CGROUP_FILE_NAME_MAX];
lockdep_assert_held(&cgroup_mutex);
+
+ if (cft->file_offset) {
+ struct cgroup_subsys_state *css = cgroup_css(cgrp, cft->ss);
+ struct cgroup_file *cfile = (void *)css + cft->file_offset;
+
+ spin_lock_irq(&cgroup_file_kn_lock);
+ cfile->kn = NULL;
+ spin_unlock_irq(&cgroup_file_kn_lock);
+ }
+
kernfs_remove_by_name(cgrp->kn, cgroup_file_name(cgrp, cft, name));
}
@@ -1625,10 +1624,6 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts)
all_ss = true;
continue;
}
- if (!strcmp(token, "__DEVEL__sane_behavior")) {
- opts->flags |= CGRP_ROOT_SANE_BEHAVIOR;
- continue;
- }
if (!strcmp(token, "noprefix")) {
opts->flags |= CGRP_ROOT_NOPREFIX;
continue;
@@ -1695,15 +1690,6 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts)
return -ENOENT;
}
- if (opts->flags & CGRP_ROOT_SANE_BEHAVIOR) {
- pr_warn("sane_behavior: this is still under development and its behaviors will change, proceed at your own risk\n");
- if (nr_opts != 1) {
- pr_err("sane_behavior: no other mount options allowed\n");
- return -EINVAL;
- }
- return 0;
- }
-
/*
* If the 'all' option was specified select all the subsystems,
* otherwise if 'none', 'name=' and a subsystem name options were
@@ -1856,7 +1842,6 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp)
INIT_LIST_HEAD(&cgrp->self.sibling);
INIT_LIST_HEAD(&cgrp->self.children);
- INIT_LIST_HEAD(&cgrp->self.files);
INIT_LIST_HEAD(&cgrp->cset_links);
INIT_LIST_HEAD(&cgrp->pidlists);
mutex_init(&cgrp->pidlist_mutex);
@@ -1903,6 +1888,7 @@ static int cgroup_setup_root(struct cgroup_root *root, unsigned long ss_mask)
if (ret < 0)
goto out;
root_cgrp->id = ret;
+ root_cgrp->ancestor_ids[0] = ret;
ret = percpu_ref_init(&root_cgrp->self.refcnt, css_release, 0,
GFP_KERNEL);
@@ -1983,6 +1969,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
int flags, const char *unused_dev_name,
void *data)
{
+ bool is_v2 = fs_type == &cgroup2_fs_type;
struct super_block *pinned_sb = NULL;
struct cgroup_subsys *ss;
struct cgroup_root *root;
@@ -1999,6 +1986,17 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
if (!use_task_css_set_links)
cgroup_enable_task_cg_lists();
+ if (is_v2) {
+ if (data) {
+ pr_err("cgroup2: unknown option \"%s\"\n", (char *)data);
+ return ERR_PTR(-EINVAL);
+ }
+ cgrp_dfl_root_visible = true;
+ root = &cgrp_dfl_root;
+ cgroup_get(&root->cgrp);
+ goto out_mount;
+ }
+
mutex_lock(&cgroup_mutex);
/* First find the desired set of subsystems */
@@ -2006,15 +2004,6 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
if (ret)
goto out_unlock;
- /* look for a matching existing root */
- if (opts.flags & CGRP_ROOT_SANE_BEHAVIOR) {
- cgrp_dfl_root_visible = true;
- root = &cgrp_dfl_root;
- cgroup_get(&root->cgrp);
- ret = 0;
- goto out_unlock;
- }
-
/*
* Destruction of cgroup root is asynchronous, so subsystems may
* still be dying after the previous unmount. Let's drain the
@@ -2125,9 +2114,10 @@ out_free:
if (ret)
return ERR_PTR(ret);
-
+out_mount:
dentry = kernfs_mount(fs_type, flags, root->kf_root,
- CGROUP_SUPER_MAGIC, &new_sb);
+ is_v2 ? CGROUP2_SUPER_MAGIC : CGROUP_SUPER_MAGIC,
+ &new_sb);
if (IS_ERR(dentry) || !new_sb)
cgroup_put(&root->cgrp);
@@ -2170,6 +2160,12 @@ static struct file_system_type cgroup_fs_type = {
.kill_sb = cgroup_kill_sb,
};
+static struct file_system_type cgroup2_fs_type = {
+ .name = "cgroup2",
+ .mount = cgroup_mount,
+ .kill_sb = cgroup_kill_sb,
+};
+
/**
* task_cgroup_path - cgroup path of a task in the first cgroup hierarchy
* @task: target task
@@ -2216,6 +2212,9 @@ struct cgroup_taskset {
struct list_head src_csets;
struct list_head dst_csets;
+ /* the subsys currently being processed */
+ int ssid;
+
/*
* Fields for cgroup_taskset_*() iteration.
*
@@ -2278,25 +2277,29 @@ static void cgroup_taskset_add(struct task_struct *task,
/**
* cgroup_taskset_first - reset taskset and return the first task
* @tset: taskset of interest
+ * @dst_cssp: output variable for the destination css
*
* @tset iteration is initialized and the first task is returned.
*/
-struct task_struct *cgroup_taskset_first(struct cgroup_taskset *tset)
+struct task_struct *cgroup_taskset_first(struct cgroup_taskset *tset,
+ struct cgroup_subsys_state **dst_cssp)
{
tset->cur_cset = list_first_entry(tset->csets, struct css_set, mg_node);
tset->cur_task = NULL;
- return cgroup_taskset_next(tset);
+ return cgroup_taskset_next(tset, dst_cssp);
}
/**
* cgroup_taskset_next - iterate to the next task in taskset
* @tset: taskset of interest
+ * @dst_cssp: output variable for the destination css
*
* Return the next task in @tset. Iteration must have been initialized
* with cgroup_taskset_first().
*/
-struct task_struct *cgroup_taskset_next(struct cgroup_taskset *tset)
+struct task_struct *cgroup_taskset_next(struct cgroup_taskset *tset,
+ struct cgroup_subsys_state **dst_cssp)
{
struct css_set *cset = tset->cur_cset;
struct task_struct *task = tset->cur_task;
@@ -2311,6 +2314,18 @@ struct task_struct *cgroup_taskset_next(struct cgroup_taskset *tset)
if (&task->cg_list != &cset->mg_tasks) {
tset->cur_cset = cset;
tset->cur_task = task;
+
+ /*
+ * This function may be called both before and
+ * after cgroup_taskset_migrate(). The two cases
+ * can be distinguished by looking at whether @cset
+ * has its ->mg_dst_cset set.
+ */
+ if (cset->mg_dst_cset)
+ *dst_cssp = cset->mg_dst_cset->subsys[tset->ssid];
+ else
+ *dst_cssp = cset->subsys[tset->ssid];
+
return task;
}
@@ -2346,7 +2361,8 @@ static int cgroup_taskset_migrate(struct cgroup_taskset *tset,
/* check that we can legitimately attach to the cgroup */
for_each_e_css(css, i, dst_cgrp) {
if (css->ss->can_attach) {
- ret = css->ss->can_attach(css, tset);
+ tset->ssid = i;
+ ret = css->ss->can_attach(tset);
if (ret) {
failed_css = css;
goto out_cancel_attach;
@@ -2379,9 +2395,12 @@ static int cgroup_taskset_migrate(struct cgroup_taskset *tset,
*/
tset->csets = &tset->dst_csets;
- for_each_e_css(css, i, dst_cgrp)
- if (css->ss->attach)
- css->ss->attach(css, tset);
+ for_each_e_css(css, i, dst_cgrp) {
+ if (css->ss->attach) {
+ tset->ssid = i;
+ css->ss->attach(tset);
+ }
+ }
ret = 0;
goto out_release_tset;
@@ -2390,8 +2409,10 @@ out_cancel_attach:
for_each_e_css(css, i, dst_cgrp) {
if (css == failed_css)
break;
- if (css->ss->cancel_attach)
- css->ss->cancel_attach(css, tset);
+ if (css->ss->cancel_attach) {
+ tset->ssid = i;
+ css->ss->cancel_attach(tset);
+ }
}
out_release_tset:
spin_lock_bh(&css_set_lock);
@@ -3313,9 +3334,9 @@ static int cgroup_add_file(struct cgroup_subsys_state *css, struct cgroup *cgrp,
if (cft->file_offset) {
struct cgroup_file *cfile = (void *)css + cft->file_offset;
- kernfs_get(kn);
+ spin_lock_irq(&cgroup_file_kn_lock);
cfile->kn = kn;
- list_add(&cfile->node, &css->files);
+ spin_unlock_irq(&cgroup_file_kn_lock);
}
return 0;
@@ -3553,6 +3574,22 @@ int cgroup_add_legacy_cftypes(struct cgroup_subsys *ss, struct cftype *cfts)
}
/**
+ * cgroup_file_notify - generate a file modified event for a cgroup_file
+ * @cfile: target cgroup_file
+ *
+ * @cfile must have been obtained by setting cftype->file_offset.
+ */
+void cgroup_file_notify(struct cgroup_file *cfile)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cgroup_file_kn_lock, flags);
+ if (cfile->kn)
+ kernfs_notify(cfile->kn);
+ spin_unlock_irqrestore(&cgroup_file_kn_lock, flags);
+}
+
+/**
* cgroup_task_count - count the number of tasks in a cgroup.
* @cgrp: the cgroup in question
*
@@ -4000,7 +4037,7 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)
goto out_err;
/*
- * Migrate tasks one-by-one until @form is empty. This fails iff
+ * Migrate tasks one-by-one until @from is empty. This fails iff
* ->can_attach() fails.
*/
do {
@@ -4613,13 +4650,9 @@ static void css_free_work_fn(struct work_struct *work)
container_of(work, struct cgroup_subsys_state, destroy_work);
struct cgroup_subsys *ss = css->ss;
struct cgroup *cgrp = css->cgroup;
- struct cgroup_file *cfile;
percpu_ref_exit(&css->refcnt);
- list_for_each_entry(cfile, &css->files, node)
- kernfs_put(cfile->kn);
-
if (ss) {
/* css free path */
int id = css->id;
@@ -4724,7 +4757,6 @@ static void init_and_link_css(struct cgroup_subsys_state *css,
css->ss = ss;
INIT_LIST_HEAD(&css->sibling);
INIT_LIST_HEAD(&css->children);
- INIT_LIST_HEAD(&css->files);
css->serial_nr = css_serial_nr_next++;
if (cgroup_parent(cgrp)) {
@@ -4846,11 +4878,11 @@ err_free_css:
static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
umode_t mode)
{
- struct cgroup *parent, *cgrp;
+ struct cgroup *parent, *cgrp, *tcgrp;
struct cgroup_root *root;
struct cgroup_subsys *ss;
struct kernfs_node *kn;
- int ssid, ret;
+ int level, ssid, ret;
/* Do not accept '\n' to prevent making /proc/<pid>/cgroup unparsable.
*/
@@ -4861,9 +4893,11 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
if (!parent)
return -ENODEV;
root = parent->root;
+ level = parent->level + 1;
/* allocate the cgroup and its ID, 0 is reserved for the root */
- cgrp = kzalloc(sizeof(*cgrp), GFP_KERNEL);
+ cgrp = kzalloc(sizeof(*cgrp) +
+ sizeof(cgrp->ancestor_ids[0]) * (level + 1), GFP_KERNEL);
if (!cgrp) {
ret = -ENOMEM;
goto out_unlock;
@@ -4887,6 +4921,10 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
cgrp->self.parent = &parent->self;
cgrp->root = root;
+ cgrp->level = level;
+
+ for (tcgrp = cgrp; tcgrp; tcgrp = cgroup_parent(tcgrp))
+ cgrp->ancestor_ids[tcgrp->level] = tcgrp->id;
if (notify_on_release(parent))
set_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags);
@@ -5131,7 +5169,7 @@ static void __init cgroup_init_subsys(struct cgroup_subsys *ss, bool early)
{
struct cgroup_subsys_state *css;
- printk(KERN_INFO "Initializing cgroup subsys %s\n", ss->name);
+ pr_debug("Initializing cgroup subsys %s\n", ss->name);
mutex_lock(&cgroup_mutex);
@@ -5289,6 +5327,7 @@ int __init cgroup_init(void)
WARN_ON(sysfs_create_mount_point(fs_kobj, "cgroup"));
WARN_ON(register_filesystem(&cgroup_fs_type));
+ WARN_ON(register_filesystem(&cgroup2_fs_type));
WARN_ON(!proc_create("cgroups", 0, NULL, &proc_cgroupstats_operations));
return 0;
@@ -5432,19 +5471,6 @@ static const struct file_operations proc_cgroupstats_operations = {
.release = single_release,
};
-static void **subsys_canfork_priv_p(void *ss_priv[CGROUP_CANFORK_COUNT], int i)
-{
- if (CGROUP_CANFORK_START <= i && i < CGROUP_CANFORK_END)
- return &ss_priv[i - CGROUP_CANFORK_START];
- return NULL;
-}
-
-static void *subsys_canfork_priv(void *ss_priv[CGROUP_CANFORK_COUNT], int i)
-{
- void **private = subsys_canfork_priv_p(ss_priv, i);
- return private ? *private : NULL;
-}
-
/**
* cgroup_fork - initialize cgroup related fields during copy_process()
* @child: pointer to task_struct of forking parent process.
@@ -5467,14 +5493,13 @@ void cgroup_fork(struct task_struct *child)
* returns an error, the fork aborts with that error code. This allows for
* a cgroup subsystem to conditionally allow or deny new forks.
*/
-int cgroup_can_fork(struct task_struct *child,
- void *ss_priv[CGROUP_CANFORK_COUNT])
+int cgroup_can_fork(struct task_struct *child)
{
struct cgroup_subsys *ss;
int i, j, ret;
for_each_subsys_which(ss, i, &have_canfork_callback) {
- ret = ss->can_fork(child, subsys_canfork_priv_p(ss_priv, i));
+ ret = ss->can_fork(child);
if (ret)
goto out_revert;
}
@@ -5486,7 +5511,7 @@ out_revert:
if (j >= i)
break;
if (ss->cancel_fork)
- ss->cancel_fork(child, subsys_canfork_priv(ss_priv, j));
+ ss->cancel_fork(child);
}
return ret;
@@ -5499,15 +5524,14 @@ out_revert:
* This calls the cancel_fork() callbacks if a fork failed *after*
* cgroup_can_fork() succeded.
*/
-void cgroup_cancel_fork(struct task_struct *child,
- void *ss_priv[CGROUP_CANFORK_COUNT])
+void cgroup_cancel_fork(struct task_struct *child)
{
struct cgroup_subsys *ss;
int i;
for_each_subsys(ss, i)
if (ss->cancel_fork)
- ss->cancel_fork(child, subsys_canfork_priv(ss_priv, i));
+ ss->cancel_fork(child);
}
/**
@@ -5520,8 +5544,7 @@ void cgroup_cancel_fork(struct task_struct *child,
* cgroup_task_iter_start() - to guarantee that the new task ends up on its
* list.
*/
-void cgroup_post_fork(struct task_struct *child,
- void *old_ss_priv[CGROUP_CANFORK_COUNT])
+void cgroup_post_fork(struct task_struct *child)
{
struct cgroup_subsys *ss;
int i;
@@ -5565,7 +5588,7 @@ void cgroup_post_fork(struct task_struct *child,
* and addition to css_set.
*/
for_each_subsys_which(ss, i, &have_fork_callback)
- ss->fork(child, subsys_canfork_priv(old_ss_priv, i));
+ ss->fork(child);
}
/**
@@ -5765,6 +5788,93 @@ struct cgroup_subsys_state *css_from_id(int id, struct cgroup_subsys *ss)
return id > 0 ? idr_find(&ss->css_idr, id) : NULL;
}
+/**
+ * cgroup_get_from_path - lookup and get a cgroup from its default hierarchy path
+ * @path: path on the default hierarchy
+ *
+ * Find the cgroup at @path on the default hierarchy, increment its
+ * reference count and return it. Returns pointer to the found cgroup on
+ * success, ERR_PTR(-ENOENT) if @path doens't exist and ERR_PTR(-ENOTDIR)
+ * if @path points to a non-directory.
+ */
+struct cgroup *cgroup_get_from_path(const char *path)
+{
+ struct kernfs_node *kn;
+ struct cgroup *cgrp;
+
+ mutex_lock(&cgroup_mutex);
+
+ kn = kernfs_walk_and_get(cgrp_dfl_root.cgrp.kn, path);
+ if (kn) {
+ if (kernfs_type(kn) == KERNFS_DIR) {
+ cgrp = kn->priv;
+ cgroup_get(cgrp);
+ } else {
+ cgrp = ERR_PTR(-ENOTDIR);
+ }
+ kernfs_put(kn);
+ } else {
+ cgrp = ERR_PTR(-ENOENT);
+ }
+
+ mutex_unlock(&cgroup_mutex);
+ return cgrp;
+}
+EXPORT_SYMBOL_GPL(cgroup_get_from_path);
+
+/*
+ * sock->sk_cgrp_data handling. For more info, see sock_cgroup_data
+ * definition in cgroup-defs.h.
+ */
+#ifdef CONFIG_SOCK_CGROUP_DATA
+
+#if defined(CONFIG_CGROUP_NET_PRIO) || defined(CONFIG_CGROUP_NET_CLASSID)
+
+DEFINE_SPINLOCK(cgroup_sk_update_lock);
+static bool cgroup_sk_alloc_disabled __read_mostly;
+
+void cgroup_sk_alloc_disable(void)
+{
+ if (cgroup_sk_alloc_disabled)
+ return;
+ pr_info("cgroup: disabling cgroup2 socket matching due to net_prio or net_cls activation\n");
+ cgroup_sk_alloc_disabled = true;
+}
+
+#else
+
+#define cgroup_sk_alloc_disabled false
+
+#endif
+
+void cgroup_sk_alloc(struct sock_cgroup_data *skcd)
+{
+ if (cgroup_sk_alloc_disabled)
+ return;
+
+ rcu_read_lock();
+
+ while (true) {
+ struct css_set *cset;
+
+ cset = task_css_set(current);
+ if (likely(cgroup_tryget(cset->dfl_cgrp))) {
+ skcd->val = (unsigned long)cset->dfl_cgrp;
+ break;
+ }
+ cpu_relax();
+ }
+
+ rcu_read_unlock();
+}
+
+void cgroup_sk_free(struct sock_cgroup_data *skcd)
+{
+ cgroup_put(sock_cgroup_ptr(skcd));
+}
+
+#endif /* CONFIG_SOCK_CGROUP_DATA */
+
#ifdef CONFIG_CGROUP_DEBUG
static struct cgroup_subsys_state *
debug_css_alloc(struct cgroup_subsys_state *parent_css)
diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c
index f1b30ad5dc6d..1b72d56edce5 100644
--- a/kernel/cgroup_freezer.c
+++ b/kernel/cgroup_freezer.c
@@ -155,12 +155,10 @@ static void freezer_css_free(struct cgroup_subsys_state *css)
* @freezer->lock. freezer_attach() makes the new tasks conform to the
* current state and all following state changes can see the new tasks.
*/
-static void freezer_attach(struct cgroup_subsys_state *new_css,
- struct cgroup_taskset *tset)
+static void freezer_attach(struct cgroup_taskset *tset)
{
- struct freezer *freezer = css_freezer(new_css);
struct task_struct *task;
- bool clear_frozen = false;
+ struct cgroup_subsys_state *new_css;
mutex_lock(&freezer_mutex);
@@ -174,22 +172,21 @@ static void freezer_attach(struct cgroup_subsys_state *new_css,
* current state before executing the following - !frozen tasks may
* be visible in a FROZEN cgroup and frozen tasks in a THAWED one.
*/
- cgroup_taskset_for_each(task, tset) {
+ cgroup_taskset_for_each(task, new_css, tset) {
+ struct freezer *freezer = css_freezer(new_css);
+
if (!(freezer->state & CGROUP_FREEZING)) {
__thaw_task(task);
} else {
freeze_task(task);
- freezer->state &= ~CGROUP_FROZEN;
- clear_frozen = true;
+ /* clear FROZEN and propagate upwards */
+ while (freezer && (freezer->state & CGROUP_FROZEN)) {
+ freezer->state &= ~CGROUP_FROZEN;
+ freezer = parent_freezer(freezer);
+ }
}
}
- /* propagate FROZEN clearing upwards */
- while (clear_frozen && (freezer = parent_freezer(freezer))) {
- freezer->state &= ~CGROUP_FROZEN;
- clear_frozen = freezer->state & CGROUP_FREEZING;
- }
-
mutex_unlock(&freezer_mutex);
}
@@ -203,7 +200,7 @@ static void freezer_attach(struct cgroup_subsys_state *new_css,
* to do anything as freezer_attach() will put @task into the appropriate
* state.
*/
-static void freezer_fork(struct task_struct *task, void *private)
+static void freezer_fork(struct task_struct *task)
{
struct freezer *freezer;
diff --git a/kernel/cgroup_pids.c b/kernel/cgroup_pids.c
index cdd8df4e991c..303097b37429 100644
--- a/kernel/cgroup_pids.c
+++ b/kernel/cgroup_pids.c
@@ -106,7 +106,7 @@ static void pids_uncharge(struct pids_cgroup *pids, int num)
{
struct pids_cgroup *p;
- for (p = pids; p; p = parent_pids(p))
+ for (p = pids; parent_pids(p); p = parent_pids(p))
pids_cancel(p, num);
}
@@ -123,7 +123,7 @@ static void pids_charge(struct pids_cgroup *pids, int num)
{
struct pids_cgroup *p;
- for (p = pids; p; p = parent_pids(p))
+ for (p = pids; parent_pids(p); p = parent_pids(p))
atomic64_add(num, &p->counter);
}
@@ -134,13 +134,13 @@ static void pids_charge(struct pids_cgroup *pids, int num)
*
* This function follows the set limit. It will fail if the charge would cause
* the new value to exceed the hierarchical limit. Returns 0 if the charge
- * succeded, otherwise -EAGAIN.
+ * succeeded, otherwise -EAGAIN.
*/
static int pids_try_charge(struct pids_cgroup *pids, int num)
{
struct pids_cgroup *p, *q;
- for (p = pids; p; p = parent_pids(p)) {
+ for (p = pids; parent_pids(p); p = parent_pids(p)) {
int64_t new = atomic64_add_return(num, &p->counter);
/*
@@ -162,13 +162,13 @@ revert:
return -EAGAIN;
}
-static int pids_can_attach(struct cgroup_subsys_state *css,
- struct cgroup_taskset *tset)
+static int pids_can_attach(struct cgroup_taskset *tset)
{
- struct pids_cgroup *pids = css_pids(css);
struct task_struct *task;
+ struct cgroup_subsys_state *dst_css;
- cgroup_taskset_for_each(task, tset) {
+ cgroup_taskset_for_each(task, dst_css, tset) {
+ struct pids_cgroup *pids = css_pids(dst_css);
struct cgroup_subsys_state *old_css;
struct pids_cgroup *old_pids;
@@ -187,13 +187,13 @@ static int pids_can_attach(struct cgroup_subsys_state *css,
return 0;
}
-static void pids_cancel_attach(struct cgroup_subsys_state *css,
- struct cgroup_taskset *tset)
+static void pids_cancel_attach(struct cgroup_taskset *tset)
{
- struct pids_cgroup *pids = css_pids(css);
struct task_struct *task;
+ struct cgroup_subsys_state *dst_css;
- cgroup_taskset_for_each(task, tset) {
+ cgroup_taskset_for_each(task, dst_css, tset) {
+ struct pids_cgroup *pids = css_pids(dst_css);
struct cgroup_subsys_state *old_css;
struct pids_cgroup *old_pids;
@@ -205,65 +205,28 @@ static void pids_cancel_attach(struct cgroup_subsys_state *css,
}
}
-static int pids_can_fork(struct task_struct *task, void **priv_p)
+/*
+ * task_css_check(true) in pids_can_fork() and pids_cancel_fork() relies
+ * on threadgroup_change_begin() held by the copy_process().
+ */
+static int pids_can_fork(struct task_struct *task)
{
struct cgroup_subsys_state *css;
struct pids_cgroup *pids;
- int err;
- /*
- * Use the "current" task_css for the pids subsystem as the tentative
- * css. It is possible we will charge the wrong hierarchy, in which
- * case we will forcefully revert/reapply the charge on the right
- * hierarchy after it is committed to the task proper.
- */
- css = task_get_css(current, pids_cgrp_id);
+ css = task_css_check(current, pids_cgrp_id, true);
pids = css_pids(css);
-
- err = pids_try_charge(pids, 1);
- if (err)
- goto err_css_put;
-
- *priv_p = css;
- return 0;
-
-err_css_put:
- css_put(css);
- return err;
+ return pids_try_charge(pids, 1);
}
-static void pids_cancel_fork(struct task_struct *task, void *priv)
-{
- struct cgroup_subsys_state *css = priv;
- struct pids_cgroup *pids = css_pids(css);
-
- pids_uncharge(pids, 1);
- css_put(css);
-}
-
-static void pids_fork(struct task_struct *task, void *priv)
+static void pids_cancel_fork(struct task_struct *task)
{
struct cgroup_subsys_state *css;
- struct cgroup_subsys_state *old_css = priv;
struct pids_cgroup *pids;
- struct pids_cgroup *old_pids = css_pids(old_css);
- css = task_get_css(task, pids_cgrp_id);
+ css = task_css_check(current, pids_cgrp_id, true);
pids = css_pids(css);
-
- /*
- * If the association has changed, we have to revert and reapply the
- * charge/uncharge on the wrong hierarchy to the current one. Since
- * the association can only change due to an organisation event, its
- * okay for us to ignore the limit in this case.
- */
- if (pids != old_pids) {
- pids_uncharge(old_pids, 1);
- pids_charge(pids, 1);
- }
-
- css_put(css);
- css_put(old_css);
+ pids_uncharge(pids, 1);
}
static void pids_free(struct task_struct *task)
@@ -335,6 +298,7 @@ static struct cftype pids_files[] = {
{
.name = "current",
.read_s64 = pids_current_read,
+ .flags = CFTYPE_NOT_ON_ROOT,
},
{ } /* terminate */
};
@@ -346,7 +310,6 @@ struct cgroup_subsys pids_cgrp_subsys = {
.cancel_attach = pids_cancel_attach,
.can_fork = pids_can_fork,
.cancel_fork = pids_cancel_fork,
- .fork = pids_fork,
.free = pids_free,
.legacy_cftypes = pids_files,
.dfl_cftypes = pids_files,
diff --git a/kernel/context_tracking.c b/kernel/context_tracking.c
index d8560ee3bab7..9ad37b9e44a7 100644
--- a/kernel/context_tracking.c
+++ b/kernel/context_tracking.c
@@ -24,7 +24,7 @@
#define CREATE_TRACE_POINTS
#include <trace/events/context_tracking.h>
-struct static_key context_tracking_enabled = STATIC_KEY_INIT_FALSE;
+DEFINE_STATIC_KEY_FALSE(context_tracking_enabled);
EXPORT_SYMBOL_GPL(context_tracking_enabled);
DEFINE_PER_CPU(struct context_tracking, context_tracking);
@@ -191,7 +191,7 @@ void __init context_tracking_cpu_set(int cpu)
if (!per_cpu(context_tracking.active, cpu)) {
per_cpu(context_tracking.active, cpu) = true;
- static_key_slow_inc(&context_tracking_enabled);
+ static_branch_inc(&context_tracking_enabled);
}
if (initialized)
diff --git a/kernel/cpuset.c b/kernel/cpuset.c
index 10ae73611d80..3e945fcd8179 100644
--- a/kernel/cpuset.c
+++ b/kernel/cpuset.c
@@ -51,6 +51,7 @@
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/time.h>
+#include <linux/time64.h>
#include <linux/backing-dev.h>
#include <linux/sort.h>
@@ -68,7 +69,7 @@ struct static_key cpusets_enabled_key __read_mostly = STATIC_KEY_INIT_FALSE;
struct fmeter {
int cnt; /* unprocessed events count */
int val; /* most recent output value */
- time_t time; /* clock (secs) when val computed */
+ time64_t time; /* clock (secs) when val computed */
spinlock_t lock; /* guards read or write of above */
};
@@ -1374,7 +1375,7 @@ out:
*/
#define FM_COEF 933 /* coefficient for half-life of 10 secs */
-#define FM_MAXTICKS ((time_t)99) /* useless computing more ticks than this */
+#define FM_MAXTICKS ((u32)99) /* useless computing more ticks than this */
#define FM_MAXCNT 1000000 /* limit cnt to avoid overflow */
#define FM_SCALE 1000 /* faux fixed point scale */
@@ -1390,8 +1391,11 @@ static void fmeter_init(struct fmeter *fmp)
/* Internal meter update - process cnt events and update value */
static void fmeter_update(struct fmeter *fmp)
{
- time_t now = get_seconds();
- time_t ticks = now - fmp->time;
+ time64_t now;
+ u32 ticks;
+
+ now = ktime_get_seconds();
+ ticks = now - fmp->time;
if (ticks == 0)
return;
@@ -1429,15 +1433,16 @@ static int fmeter_getrate(struct fmeter *fmp)
static struct cpuset *cpuset_attach_old_cs;
/* Called by cgroups to determine if a cpuset is usable; cpuset_mutex held */
-static int cpuset_can_attach(struct cgroup_subsys_state *css,
- struct cgroup_taskset *tset)
+static int cpuset_can_attach(struct cgroup_taskset *tset)
{
- struct cpuset *cs = css_cs(css);
+ struct cgroup_subsys_state *css;
+ struct cpuset *cs;
struct task_struct *task;
int ret;
/* used later by cpuset_attach() */
- cpuset_attach_old_cs = task_cs(cgroup_taskset_first(tset));
+ cpuset_attach_old_cs = task_cs(cgroup_taskset_first(tset, &css));
+ cs = css_cs(css);
mutex_lock(&cpuset_mutex);
@@ -1447,7 +1452,7 @@ static int cpuset_can_attach(struct cgroup_subsys_state *css,
(cpumask_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed)))
goto out_unlock;
- cgroup_taskset_for_each(task, tset) {
+ cgroup_taskset_for_each(task, css, tset) {
ret = task_can_attach(task, cs->cpus_allowed);
if (ret)
goto out_unlock;
@@ -1467,9 +1472,14 @@ out_unlock:
return ret;
}
-static void cpuset_cancel_attach(struct cgroup_subsys_state *css,
- struct cgroup_taskset *tset)
+static void cpuset_cancel_attach(struct cgroup_taskset *tset)
{
+ struct cgroup_subsys_state *css;
+ struct cpuset *cs;
+
+ cgroup_taskset_first(tset, &css);
+ cs = css_cs(css);
+
mutex_lock(&cpuset_mutex);
css_cs(css)->attach_in_progress--;
mutex_unlock(&cpuset_mutex);
@@ -1482,16 +1492,19 @@ static void cpuset_cancel_attach(struct cgroup_subsys_state *css,
*/
static cpumask_var_t cpus_attach;
-static void cpuset_attach(struct cgroup_subsys_state *css,
- struct cgroup_taskset *tset)
+static void cpuset_attach(struct cgroup_taskset *tset)
{
/* static buf protected by cpuset_mutex */
static nodemask_t cpuset_attach_nodemask_to;
struct task_struct *task;
struct task_struct *leader;
- struct cpuset *cs = css_cs(css);
+ struct cgroup_subsys_state *css;
+ struct cpuset *cs;
struct cpuset *oldcs = cpuset_attach_old_cs;
+ cgroup_taskset_first(tset, &css);
+ cs = css_cs(css);
+
mutex_lock(&cpuset_mutex);
/* prepare for attach */
@@ -1502,7 +1515,7 @@ static void cpuset_attach(struct cgroup_subsys_state *css,
guarantee_online_mems(cs, &cpuset_attach_nodemask_to);
- cgroup_taskset_for_each(task, tset) {
+ cgroup_taskset_for_each(task, css, tset) {
/*
* can_attach beforehand should guarantee that this doesn't
* fail. TODO: have a better way to handle failure here
@@ -1518,7 +1531,7 @@ static void cpuset_attach(struct cgroup_subsys_state *css,
* sleep and should be moved outside migration path proper.
*/
cpuset_attach_nodemask_to = cs->effective_mems;
- cgroup_taskset_for_each_leader(leader, tset) {
+ cgroup_taskset_for_each_leader(leader, css, tset) {
struct mm_struct *mm = get_task_mm(leader);
if (mm) {
diff --git a/kernel/cred.c b/kernel/cred.c
index 71179a09c1d6..0c0cd8a62285 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -569,8 +569,8 @@ EXPORT_SYMBOL(revert_creds);
void __init cred_init(void)
{
/* allocate a slab in which we can store credentials */
- cred_jar = kmem_cache_create("cred_jar", sizeof(struct cred),
- 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
+ cred_jar = kmem_cache_create("cred_jar", sizeof(struct cred), 0,
+ SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT, NULL);
}
/**
diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c
index 4121345498e0..2a20c0dfdafc 100644
--- a/kernel/debug/kdb/kdb_main.c
+++ b/kernel/debug/kdb/kdb_main.c
@@ -2021,7 +2021,7 @@ static int kdb_lsmod(int argc, const char **argv)
continue;
kdb_printf("%-20s%8u 0x%p ", mod->name,
- mod->core_size, (void *)mod);
+ mod->core_layout.size, (void *)mod);
#ifdef CONFIG_MODULE_UNLOAD
kdb_printf("%4d ", module_refcount(mod));
#endif
@@ -2031,7 +2031,7 @@ static int kdb_lsmod(int argc, const char **argv)
kdb_printf(" (Loading)");
else
kdb_printf(" (Live)");
- kdb_printf(" 0x%p", mod->module_core);
+ kdb_printf(" 0x%p", mod->core_layout.base);
#ifdef CONFIG_MODULE_UNLOAD
{
diff --git a/kernel/delayacct.c b/kernel/delayacct.c
index ef90b04d783f..435c14a45118 100644
--- a/kernel/delayacct.c
+++ b/kernel/delayacct.c
@@ -34,7 +34,7 @@ __setup("nodelayacct", delayacct_setup_disable);
void delayacct_init(void)
{
- delayacct_cache = KMEM_CACHE(task_delay_info, SLAB_PANIC);
+ delayacct_cache = KMEM_CACHE(task_delay_info, SLAB_PANIC|SLAB_ACCOUNT);
delayacct_tsk_init(&init_task);
}
diff --git a/kernel/events/callchain.c b/kernel/events/callchain.c
index d659487254d5..9c418002b8c1 100644
--- a/kernel/events/callchain.c
+++ b/kernel/events/callchain.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2008 Thomas Gleixner <tglx@linutronix.de>
* Copyright (C) 2008-2011 Red Hat, Inc., Ingo Molnar
- * Copyright (C) 2008-2011 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2008-2011 Red Hat, Inc., Peter Zijlstra
* Copyright © 2009 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
*
* For licensing details see kernel-base/COPYING
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 36babfd20648..bf8244190d0f 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2008 Thomas Gleixner <tglx@linutronix.de>
* Copyright (C) 2008-2011 Red Hat, Inc., Ingo Molnar
- * Copyright (C) 2008-2011 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2008-2011 Red Hat, Inc., Peter Zijlstra
* Copyright © 2009 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
*
* For licensing details see kernel-base/COPYING
@@ -126,6 +126,37 @@ static int cpu_function_call(int cpu, remote_function_f func, void *info)
return data.ret;
}
+static void event_function_call(struct perf_event *event,
+ int (*active)(void *),
+ void (*inactive)(void *),
+ void *data)
+{
+ struct perf_event_context *ctx = event->ctx;
+ struct task_struct *task = ctx->task;
+
+ if (!task) {
+ cpu_function_call(event->cpu, active, data);
+ return;
+ }
+
+again:
+ if (!task_function_call(task, active, data))
+ return;
+
+ raw_spin_lock_irq(&ctx->lock);
+ if (ctx->is_active) {
+ /*
+ * Reload the task pointer, it might have been changed by
+ * a concurrent perf_event_context_sched_out().
+ */
+ task = ctx->task;
+ raw_spin_unlock_irq(&ctx->lock);
+ goto again;
+ }
+ inactive(data);
+ raw_spin_unlock_irq(&ctx->lock);
+}
+
#define EVENT_OWNER_KERNEL ((void *) -1)
static bool is_kernel_event(struct perf_event *event)
@@ -435,7 +466,7 @@ static inline void update_cgrp_time_from_event(struct perf_event *event)
if (!is_cgroup_event(event))
return;
- cgrp = perf_cgroup_from_task(current);
+ cgrp = perf_cgroup_from_task(current, event->ctx);
/*
* Do not update time when cgroup is not active
*/
@@ -458,7 +489,7 @@ perf_cgroup_set_timestamp(struct task_struct *task,
if (!task || !ctx->nr_cgroups)
return;
- cgrp = perf_cgroup_from_task(task);
+ cgrp = perf_cgroup_from_task(task, ctx);
info = this_cpu_ptr(cgrp->info);
info->timestamp = ctx->timestamp;
}
@@ -489,7 +520,6 @@ static void perf_cgroup_switch(struct task_struct *task, int mode)
* we reschedule only in the presence of cgroup
* constrained events.
*/
- rcu_read_lock();
list_for_each_entry_rcu(pmu, &pmus, entry) {
cpuctx = this_cpu_ptr(pmu->pmu_cpu_context);
@@ -522,8 +552,10 @@ static void perf_cgroup_switch(struct task_struct *task, int mode)
* set cgrp before ctxsw in to allow
* event_filter_match() to not have to pass
* task around
+ * we pass the cpuctx->ctx to perf_cgroup_from_task()
+ * because cgorup events are only per-cpu
*/
- cpuctx->cgrp = perf_cgroup_from_task(task);
+ cpuctx->cgrp = perf_cgroup_from_task(task, &cpuctx->ctx);
cpu_ctx_sched_in(cpuctx, EVENT_ALL, task);
}
perf_pmu_enable(cpuctx->ctx.pmu);
@@ -531,8 +563,6 @@ static void perf_cgroup_switch(struct task_struct *task, int mode)
}
}
- rcu_read_unlock();
-
local_irq_restore(flags);
}
@@ -542,17 +572,20 @@ static inline void perf_cgroup_sched_out(struct task_struct *task,
struct perf_cgroup *cgrp1;
struct perf_cgroup *cgrp2 = NULL;
+ rcu_read_lock();
/*
* we come here when we know perf_cgroup_events > 0
+ * we do not need to pass the ctx here because we know
+ * we are holding the rcu lock
*/
- cgrp1 = perf_cgroup_from_task(task);
+ cgrp1 = perf_cgroup_from_task(task, NULL);
/*
* next is NULL when called from perf_event_enable_on_exec()
* that will systematically cause a cgroup_switch()
*/
if (next)
- cgrp2 = perf_cgroup_from_task(next);
+ cgrp2 = perf_cgroup_from_task(next, NULL);
/*
* only schedule out current cgroup events if we know
@@ -561,6 +594,8 @@ static inline void perf_cgroup_sched_out(struct task_struct *task,
*/
if (cgrp1 != cgrp2)
perf_cgroup_switch(task, PERF_CGROUP_SWOUT);
+
+ rcu_read_unlock();
}
static inline void perf_cgroup_sched_in(struct task_struct *prev,
@@ -569,13 +604,16 @@ static inline void perf_cgroup_sched_in(struct task_struct *prev,
struct perf_cgroup *cgrp1;
struct perf_cgroup *cgrp2 = NULL;
+ rcu_read_lock();
/*
* we come here when we know perf_cgroup_events > 0
+ * we do not need to pass the ctx here because we know
+ * we are holding the rcu lock
*/
- cgrp1 = perf_cgroup_from_task(task);
+ cgrp1 = perf_cgroup_from_task(task, NULL);
/* prev can never be NULL */
- cgrp2 = perf_cgroup_from_task(prev);
+ cgrp2 = perf_cgroup_from_task(prev, NULL);
/*
* only need to schedule in cgroup events if we are changing
@@ -584,6 +622,8 @@ static inline void perf_cgroup_sched_in(struct task_struct *prev,
*/
if (cgrp1 != cgrp2)
perf_cgroup_switch(task, PERF_CGROUP_SWIN);
+
+ rcu_read_unlock();
}
static inline int perf_cgroup_connect(int fd, struct perf_event *event,
@@ -1620,6 +1660,17 @@ struct remove_event {
bool detach_group;
};
+static void ___perf_remove_from_context(void *info)
+{
+ struct remove_event *re = info;
+ struct perf_event *event = re->event;
+ struct perf_event_context *ctx = event->ctx;
+
+ if (re->detach_group)
+ perf_group_detach(event);
+ list_del_event(event, ctx);
+}
+
/*
* Cross CPU call to remove a performance event
*
@@ -1647,7 +1698,6 @@ static int __perf_remove_from_context(void *info)
return 0;
}
-
/*
* Remove the event from a task's (or a CPU's) list of events.
*
@@ -1664,7 +1714,6 @@ static int __perf_remove_from_context(void *info)
static void perf_remove_from_context(struct perf_event *event, bool detach_group)
{
struct perf_event_context *ctx = event->ctx;
- struct task_struct *task = ctx->task;
struct remove_event re = {
.event = event,
.detach_group = detach_group,
@@ -1672,44 +1721,8 @@ static void perf_remove_from_context(struct perf_event *event, bool detach_group
lockdep_assert_held(&ctx->mutex);
- if (!task) {
- /*
- * Per cpu events are removed via an smp call. The removal can
- * fail if the CPU is currently offline, but in that case we
- * already called __perf_remove_from_context from
- * perf_event_exit_cpu.
- */
- cpu_function_call(event->cpu, __perf_remove_from_context, &re);
- return;
- }
-
-retry:
- if (!task_function_call(task, __perf_remove_from_context, &re))
- return;
-
- raw_spin_lock_irq(&ctx->lock);
- /*
- * If we failed to find a running task, but find the context active now
- * that we've acquired the ctx->lock, retry.
- */
- if (ctx->is_active) {
- raw_spin_unlock_irq(&ctx->lock);
- /*
- * Reload the task pointer, it might have been changed by
- * a concurrent perf_event_context_sched_out().
- */
- task = ctx->task;
- goto retry;
- }
-
- /*
- * Since the task isn't running, its safe to remove the event, us
- * holding the ctx->lock ensures the task won't get scheduled in.
- */
- if (detach_group)
- perf_group_detach(event);
- list_del_event(event, ctx);
- raw_spin_unlock_irq(&ctx->lock);
+ event_function_call(event, __perf_remove_from_context,
+ ___perf_remove_from_context, &re);
}
/*
@@ -1753,6 +1766,20 @@ int __perf_event_disable(void *info)
return 0;
}
+void ___perf_event_disable(void *info)
+{
+ struct perf_event *event = info;
+
+ /*
+ * Since we have the lock this context can't be scheduled
+ * in, so we can change the state safely.
+ */
+ if (event->state == PERF_EVENT_STATE_INACTIVE) {
+ update_group_times(event);
+ event->state = PERF_EVENT_STATE_OFF;
+ }
+}
+
/*
* Disable a event.
*
@@ -1769,43 +1796,16 @@ int __perf_event_disable(void *info)
static void _perf_event_disable(struct perf_event *event)
{
struct perf_event_context *ctx = event->ctx;
- struct task_struct *task = ctx->task;
-
- if (!task) {
- /*
- * Disable the event on the cpu that it's on
- */
- cpu_function_call(event->cpu, __perf_event_disable, event);
- return;
- }
-
-retry:
- if (!task_function_call(task, __perf_event_disable, event))
- return;
raw_spin_lock_irq(&ctx->lock);
- /*
- * If the event is still active, we need to retry the cross-call.
- */
- if (event->state == PERF_EVENT_STATE_ACTIVE) {
+ if (event->state <= PERF_EVENT_STATE_OFF) {
raw_spin_unlock_irq(&ctx->lock);
- /*
- * Reload the task pointer, it might have been changed by
- * a concurrent perf_event_context_sched_out().
- */
- task = ctx->task;
- goto retry;
- }
-
- /*
- * Since we have the lock this context can't be scheduled
- * in, so we can change the state safely.
- */
- if (event->state == PERF_EVENT_STATE_INACTIVE) {
- update_group_times(event);
- event->state = PERF_EVENT_STATE_OFF;
+ return;
}
raw_spin_unlock_irq(&ctx->lock);
+
+ event_function_call(event, __perf_event_disable,
+ ___perf_event_disable, event);
}
/*
@@ -2058,6 +2058,18 @@ static void perf_event_sched_in(struct perf_cpu_context *cpuctx,
ctx_sched_in(ctx, cpuctx, EVENT_FLEXIBLE, task);
}
+static void ___perf_install_in_context(void *info)
+{
+ struct perf_event *event = info;
+ struct perf_event_context *ctx = event->ctx;
+
+ /*
+ * Since the task isn't running, its safe to add the event, us holding
+ * the ctx->lock ensures the task won't get scheduled in.
+ */
+ add_event_to_ctx(event, ctx);
+}
+
/*
* Cross CPU call to install and enable a performance event
*
@@ -2134,48 +2146,14 @@ perf_install_in_context(struct perf_event_context *ctx,
struct perf_event *event,
int cpu)
{
- struct task_struct *task = ctx->task;
-
lockdep_assert_held(&ctx->mutex);
event->ctx = ctx;
if (event->cpu != -1)
event->cpu = cpu;
- if (!task) {
- /*
- * Per cpu events are installed via an smp call and
- * the install is always successful.
- */
- cpu_function_call(cpu, __perf_install_in_context, event);
- return;
- }
-
-retry:
- if (!task_function_call(task, __perf_install_in_context, event))
- return;
-
- raw_spin_lock_irq(&ctx->lock);
- /*
- * If we failed to find a running task, but find the context active now
- * that we've acquired the ctx->lock, retry.
- */
- if (ctx->is_active) {
- raw_spin_unlock_irq(&ctx->lock);
- /*
- * Reload the task pointer, it might have been changed by
- * a concurrent perf_event_context_sched_out().
- */
- task = ctx->task;
- goto retry;
- }
-
- /*
- * Since the task isn't running, its safe to add the event, us holding
- * the ctx->lock ensures the task won't get scheduled in.
- */
- add_event_to_ctx(event, ctx);
- raw_spin_unlock_irq(&ctx->lock);
+ event_function_call(event, __perf_install_in_context,
+ ___perf_install_in_context, event);
}
/*
@@ -2278,6 +2256,11 @@ unlock:
return 0;
}
+void ___perf_event_enable(void *info)
+{
+ __perf_event_mark_enabled((struct perf_event *)info);
+}
+
/*
* Enable a event.
*
@@ -2290,58 +2273,26 @@ unlock:
static void _perf_event_enable(struct perf_event *event)
{
struct perf_event_context *ctx = event->ctx;
- struct task_struct *task = ctx->task;
- if (!task) {
- /*
- * Enable the event on the cpu that it's on
- */
- cpu_function_call(event->cpu, __perf_event_enable, event);
+ raw_spin_lock_irq(&ctx->lock);
+ if (event->state >= PERF_EVENT_STATE_INACTIVE) {
+ raw_spin_unlock_irq(&ctx->lock);
return;
}
- raw_spin_lock_irq(&ctx->lock);
- if (event->state >= PERF_EVENT_STATE_INACTIVE)
- goto out;
-
/*
* If the event is in error state, clear that first.
- * That way, if we see the event in error state below, we
- * know that it has gone back into error state, as distinct
- * from the task having been scheduled away before the
- * cross-call arrived.
+ *
+ * That way, if we see the event in error state below, we know that it
+ * has gone back into error state, as distinct from the task having
+ * been scheduled away before the cross-call arrived.
*/
if (event->state == PERF_EVENT_STATE_ERROR)
event->state = PERF_EVENT_STATE_OFF;
-
-retry:
- if (!ctx->is_active) {
- __perf_event_mark_enabled(event);
- goto out;
- }
-
raw_spin_unlock_irq(&ctx->lock);
- if (!task_function_call(task, __perf_event_enable, event))
- return;
-
- raw_spin_lock_irq(&ctx->lock);
-
- /*
- * If the context is active and the event is still off,
- * we need to retry the cross-call.
- */
- if (ctx->is_active && event->state == PERF_EVENT_STATE_OFF) {
- /*
- * task could have been flipped by a concurrent
- * perf_event_context_sched_out()
- */
- task = ctx->task;
- goto retry;
- }
-
-out:
- raw_spin_unlock_irq(&ctx->lock);
+ event_function_call(event, __perf_event_enable,
+ ___perf_event_enable, event);
}
/*
@@ -3145,15 +3096,16 @@ static int event_enable_on_exec(struct perf_event *event,
* Enable all of a task's events that have been marked enable-on-exec.
* This expects task == current.
*/
-static void perf_event_enable_on_exec(struct perf_event_context *ctx)
+static void perf_event_enable_on_exec(int ctxn)
{
- struct perf_event_context *clone_ctx = NULL;
+ struct perf_event_context *ctx, *clone_ctx = NULL;
struct perf_event *event;
unsigned long flags;
int enabled = 0;
int ret;
local_irq_save(flags);
+ ctx = current->perf_event_ctxp[ctxn];
if (!ctx || !ctx->nr_events)
goto out;
@@ -3196,17 +3148,11 @@ out:
void perf_event_exec(void)
{
- struct perf_event_context *ctx;
int ctxn;
rcu_read_lock();
- for_each_task_context_nr(ctxn) {
- ctx = current->perf_event_ctxp[ctxn];
- if (!ctx)
- continue;
-
- perf_event_enable_on_exec(ctx);
- }
+ for_each_task_context_nr(ctxn)
+ perf_event_enable_on_exec(ctxn);
rcu_read_unlock();
}
@@ -4145,6 +4091,22 @@ struct period_event {
u64 value;
};
+static void ___perf_event_period(void *info)
+{
+ struct period_event *pe = info;
+ struct perf_event *event = pe->event;
+ u64 value = pe->value;
+
+ if (event->attr.freq) {
+ event->attr.sample_freq = value;
+ } else {
+ event->attr.sample_period = value;
+ event->hw.sample_period = value;
+ }
+
+ local64_set(&event->hw.period_left, 0);
+}
+
static int __perf_event_period(void *info)
{
struct period_event *pe = info;
@@ -4181,8 +4143,6 @@ static int __perf_event_period(void *info)
static int perf_event_period(struct perf_event *event, u64 __user *arg)
{
struct period_event pe = { .event = event, };
- struct perf_event_context *ctx = event->ctx;
- struct task_struct *task;
u64 value;
if (!is_sampling_event(event))
@@ -4197,27 +4157,10 @@ static int perf_event_period(struct perf_event *event, u64 __user *arg)
if (event->attr.freq && value > sysctl_perf_event_sample_rate)
return -EINVAL;
- task = ctx->task;
pe.value = value;
- if (!task) {
- cpu_function_call(event->cpu, __perf_event_period, &pe);
- return 0;
- }
-
-retry:
- if (!task_function_call(task, __perf_event_period, &pe))
- return 0;
-
- raw_spin_lock_irq(&ctx->lock);
- if (ctx->is_active) {
- raw_spin_unlock_irq(&ctx->lock);
- task = ctx->task;
- goto retry;
- }
-
- __perf_event_period(&pe);
- raw_spin_unlock_irq(&ctx->lock);
+ event_function_call(event, __perf_event_period,
+ ___perf_event_period, &pe);
return 0;
}
@@ -5667,6 +5610,17 @@ perf_event_aux_ctx(struct perf_event_context *ctx,
}
static void
+perf_event_aux_task_ctx(perf_event_aux_output_cb output, void *data,
+ struct perf_event_context *task_ctx)
+{
+ rcu_read_lock();
+ preempt_disable();
+ perf_event_aux_ctx(task_ctx, output, data);
+ preempt_enable();
+ rcu_read_unlock();
+}
+
+static void
perf_event_aux(perf_event_aux_output_cb output, void *data,
struct perf_event_context *task_ctx)
{
@@ -5675,14 +5629,23 @@ perf_event_aux(perf_event_aux_output_cb output, void *data,
struct pmu *pmu;
int ctxn;
+ /*
+ * If we have task_ctx != NULL we only notify
+ * the task context itself. The task_ctx is set
+ * only for EXIT events before releasing task
+ * context.
+ */
+ if (task_ctx) {
+ perf_event_aux_task_ctx(output, data, task_ctx);
+ return;
+ }
+
rcu_read_lock();
list_for_each_entry_rcu(pmu, &pmus, entry) {
cpuctx = get_cpu_ptr(pmu->pmu_cpu_context);
if (cpuctx->unique_pmu != pmu)
goto next;
perf_event_aux_ctx(&cpuctx->ctx, output, data);
- if (task_ctx)
- goto next;
ctxn = pmu->task_ctx_nr;
if (ctxn < 0)
goto next;
@@ -5692,12 +5655,6 @@ perf_event_aux(perf_event_aux_output_cb output, void *data,
next:
put_cpu_ptr(pmu->pmu_cpu_context);
}
-
- if (task_ctx) {
- preempt_disable();
- perf_event_aux_ctx(task_ctx, output, data);
- preempt_enable();
- }
rcu_read_unlock();
}
@@ -6463,9 +6420,6 @@ struct swevent_htable {
/* Recursion avoidance in each contexts */
int recursion[PERF_NR_CONTEXTS];
-
- /* Keeps track of cpu being initialized/exited */
- bool online;
};
static DEFINE_PER_CPU(struct swevent_htable, swevent_htable);
@@ -6723,14 +6677,8 @@ static int perf_swevent_add(struct perf_event *event, int flags)
hwc->state = !(flags & PERF_EF_START);
head = find_swevent_head(swhash, event);
- if (!head) {
- /*
- * We can race with cpu hotplug code. Do not
- * WARN if the cpu just got unplugged.
- */
- WARN_ON_ONCE(swhash->online);
+ if (WARN_ON_ONCE(!head))
return -EINVAL;
- }
hlist_add_head_rcu(&event->hlist_entry, head);
perf_event_update_userpage(event);
@@ -6798,7 +6746,6 @@ static int swevent_hlist_get_cpu(struct perf_event *event, int cpu)
int err = 0;
mutex_lock(&swhash->hlist_mutex);
-
if (!swevent_hlist_deref(swhash) && cpu_online(cpu)) {
struct swevent_hlist *hlist;
@@ -8787,10 +8734,8 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn)
struct perf_event_context *child_ctx, *clone_ctx = NULL;
unsigned long flags;
- if (likely(!child->perf_event_ctxp[ctxn])) {
- perf_event_task(child, NULL, 0);
+ if (likely(!child->perf_event_ctxp[ctxn]))
return;
- }
local_irq_save(flags);
/*
@@ -8874,6 +8819,14 @@ void perf_event_exit_task(struct task_struct *child)
for_each_task_context_nr(ctxn)
perf_event_exit_task_context(child, ctxn);
+
+ /*
+ * The perf_event_exit_task_context calls perf_event_task
+ * with child's task_ctx, which generates EXIT events for
+ * child contexts and sets child->perf_event_ctxp[] to NULL.
+ * At this point we need to send EXIT events to cpu contexts.
+ */
+ perf_event_task(child, NULL, 0);
}
static void perf_free_event(struct perf_event *event,
@@ -9255,7 +9208,6 @@ static void perf_event_init_cpu(int cpu)
struct swevent_htable *swhash = &per_cpu(swevent_htable, cpu);
mutex_lock(&swhash->hlist_mutex);
- swhash->online = true;
if (swhash->hlist_refcount > 0) {
struct swevent_hlist *hlist;
@@ -9297,14 +9249,7 @@ static void perf_event_exit_cpu_context(int cpu)
static void perf_event_exit_cpu(int cpu)
{
- struct swevent_htable *swhash = &per_cpu(swevent_htable, cpu);
-
perf_event_exit_cpu_context(cpu);
-
- mutex_lock(&swhash->hlist_mutex);
- swhash->online = false;
- swevent_hlist_release(swhash);
- mutex_unlock(&swhash->hlist_mutex);
}
#else
static inline void perf_event_exit_cpu(int cpu) { }
@@ -9452,16 +9397,18 @@ static void perf_cgroup_css_free(struct cgroup_subsys_state *css)
static int __perf_cgroup_move(void *info)
{
struct task_struct *task = info;
+ rcu_read_lock();
perf_cgroup_switch(task, PERF_CGROUP_SWOUT | PERF_CGROUP_SWIN);
+ rcu_read_unlock();
return 0;
}
-static void perf_cgroup_attach(struct cgroup_subsys_state *css,
- struct cgroup_taskset *tset)
+static void perf_cgroup_attach(struct cgroup_taskset *tset)
{
struct task_struct *task;
+ struct cgroup_subsys_state *css;
- cgroup_taskset_for_each(task, tset)
+ cgroup_taskset_for_each(task, css, tset)
task_function_call(task, __perf_cgroup_move, task);
}
diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c
index b5d1ea79c595..adfdc0536117 100644
--- a/kernel/events/ring_buffer.c
+++ b/kernel/events/ring_buffer.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2008 Thomas Gleixner <tglx@linutronix.de>
* Copyright (C) 2008-2011 Red Hat, Inc., Ingo Molnar
- * Copyright (C) 2008-2011 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2008-2011 Red Hat, Inc., Peter Zijlstra
* Copyright © 2009 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
*
* For licensing details see kernel-base/COPYING
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 4e5e9798aa0c..bb0669169716 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -19,7 +19,7 @@
* Authors:
* Srikar Dronamraju
* Jim Keniston
- * Copyright (C) 2011-2012 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2011-2012 Red Hat, Inc., Peter Zijlstra
*/
#include <linux/kernel.h>
@@ -180,7 +180,7 @@ static int __replace_page(struct vm_area_struct *vma, unsigned long addr,
lru_cache_add_active_or_unevictable(kpage, vma);
if (!PageAnon(page)) {
- dec_mm_counter(mm, MM_FILEPAGES);
+ dec_mm_counter(mm, mm_counter_file(page));
inc_mm_counter(mm, MM_ANONPAGES);
}
diff --git a/kernel/fork.c b/kernel/fork.c
index f97f2c449f5c..2e391c754ae7 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -300,9 +300,9 @@ void __init fork_init(void)
#define ARCH_MIN_TASKALIGN L1_CACHE_BYTES
#endif
/* create a slab on which task_structs can be allocated */
- task_struct_cachep =
- kmem_cache_create("task_struct", arch_task_struct_size,
- ARCH_MIN_TASKALIGN, SLAB_PANIC | SLAB_NOTRACK, NULL);
+ task_struct_cachep = kmem_cache_create("task_struct",
+ arch_task_struct_size, ARCH_MIN_TASKALIGN,
+ SLAB_PANIC|SLAB_NOTRACK|SLAB_ACCOUNT, NULL);
#endif
/* do the arch specific task caches init */
@@ -380,6 +380,7 @@ static struct task_struct *dup_task_struct(struct task_struct *orig)
#endif
tsk->splice_pipe = NULL;
tsk->task_frag.page = NULL;
+ tsk->wake_q.next = NULL;
account_kernel_stack(ti, 1);
@@ -413,7 +414,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
RCU_INIT_POINTER(mm->exe_file, get_mm_exe_file(oldmm));
mm->total_vm = oldmm->total_vm;
- mm->shared_vm = oldmm->shared_vm;
+ mm->data_vm = oldmm->data_vm;
mm->exec_vm = oldmm->exec_vm;
mm->stack_vm = oldmm->stack_vm;
@@ -432,8 +433,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
struct file *file;
if (mpnt->vm_flags & VM_DONTCOPY) {
- vm_stat_account(mm, mpnt->vm_flags, mpnt->vm_file,
- -vma_pages(mpnt));
+ vm_stat_account(mm, mpnt->vm_flags, -vma_pages(mpnt));
continue;
}
charge = 0;
@@ -1249,7 +1249,6 @@ static struct task_struct *copy_process(unsigned long clone_flags,
{
int retval;
struct task_struct *p;
- void *cgrp_ss_priv[CGROUP_CANFORK_COUNT] = {};
if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
return ERR_PTR(-EINVAL);
@@ -1348,9 +1347,9 @@ static struct task_struct *copy_process(unsigned long clone_flags,
prev_cputime_init(&p->prev_cputime);
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
- seqlock_init(&p->vtime_seqlock);
+ seqcount_init(&p->vtime_seqcount);
p->vtime_snap = 0;
- p->vtime_snap_whence = VTIME_SLEEPING;
+ p->vtime_snap_whence = VTIME_INACTIVE;
#endif
#if defined(SPLIT_RSS_COUNTING)
@@ -1368,8 +1367,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
p->real_start_time = ktime_get_boot_ns();
p->io_context = NULL;
p->audit_context = NULL;
- if (clone_flags & CLONE_THREAD)
- threadgroup_change_begin(current);
+ threadgroup_change_begin(current);
cgroup_fork(p);
#ifdef CONFIG_NUMA
p->mempolicy = mpol_dup(p->mempolicy);
@@ -1527,7 +1525,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
* between here and cgroup_post_fork() if an organisation operation is in
* progress.
*/
- retval = cgroup_can_fork(p, cgrp_ss_priv);
+ retval = cgroup_can_fork(p);
if (retval)
goto bad_fork_free_pid;
@@ -1609,9 +1607,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
write_unlock_irq(&tasklist_lock);
proc_fork_connector(p);
- cgroup_post_fork(p, cgrp_ss_priv);
- if (clone_flags & CLONE_THREAD)
- threadgroup_change_end(current);
+ cgroup_post_fork(p);
+ threadgroup_change_end(current);
perf_event_fork(p);
trace_task_newtask(p, clone_flags);
@@ -1620,7 +1617,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
return p;
bad_fork_cancel_cgroup:
- cgroup_cancel_fork(p, cgrp_ss_priv);
+ cgroup_cancel_fork(p);
bad_fork_free_pid:
if (pid != &init_struct_pid)
free_pid(pid);
@@ -1652,8 +1649,7 @@ bad_fork_cleanup_policy:
mpol_put(p->mempolicy);
bad_fork_cleanup_threadgroup_lock:
#endif
- if (clone_flags & CLONE_THREAD)
- threadgroup_change_end(current);
+ threadgroup_change_end(current);
delayacct_tsk_free(p);
bad_fork_cleanup_count:
atomic_dec(&p->cred->user->processes);
@@ -1851,16 +1847,19 @@ void __init proc_caches_init(void)
sighand_cachep = kmem_cache_create("sighand_cache",
sizeof(struct sighand_struct), 0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_DESTROY_BY_RCU|
- SLAB_NOTRACK, sighand_ctor);
+ SLAB_NOTRACK|SLAB_ACCOUNT, sighand_ctor);
signal_cachep = kmem_cache_create("signal_cache",
sizeof(struct signal_struct), 0,
- SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_NOTRACK, NULL);
+ SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_NOTRACK|SLAB_ACCOUNT,
+ NULL);
files_cachep = kmem_cache_create("files_cache",
sizeof(struct files_struct), 0,
- SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_NOTRACK, NULL);
+ SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_NOTRACK|SLAB_ACCOUNT,
+ NULL);
fs_cachep = kmem_cache_create("fs_cache",
sizeof(struct fs_struct), 0,
- SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_NOTRACK, NULL);
+ SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_NOTRACK|SLAB_ACCOUNT,
+ NULL);
/*
* FIXME! The "sizeof(struct mm_struct)" currently includes the
* whole struct cpumask for the OFFSTACK case. We could change
@@ -1870,8 +1869,9 @@ void __init proc_caches_init(void)
*/
mm_cachep = kmem_cache_create("mm_struct",
sizeof(struct mm_struct), ARCH_MIN_MMSTRUCT_ALIGN,
- SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_NOTRACK, NULL);
- vm_area_cachep = KMEM_CACHE(vm_area_struct, SLAB_PANIC);
+ SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_NOTRACK|SLAB_ACCOUNT,
+ NULL);
+ vm_area_cachep = KMEM_CACHE(vm_area_struct, SLAB_PANIC|SLAB_ACCOUNT);
mmap_init();
nsproxy_cache_init();
}
diff --git a/kernel/futex.c b/kernel/futex.c
index 684d7549825a..8a310e240cda 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -725,9 +725,12 @@ static struct futex_pi_state * alloc_pi_state(void)
}
/*
+ * Drops a reference to the pi_state object and frees or caches it
+ * when the last reference is gone.
+ *
* Must be called with the hb lock held.
*/
-static void free_pi_state(struct futex_pi_state *pi_state)
+static void put_pi_state(struct futex_pi_state *pi_state)
{
if (!pi_state)
return;
@@ -1706,31 +1709,35 @@ retry_private:
* exist yet, look it up one more time to ensure we have a
* reference to it. If the lock was taken, ret contains the
* vpid of the top waiter task.
+ * If the lock was not taken, we have pi_state and an initial
+ * refcount on it. In case of an error we have nothing.
*/
if (ret > 0) {
WARN_ON(pi_state);
drop_count++;
task_count++;
/*
- * If we acquired the lock, then the user
- * space value of uaddr2 should be vpid. It
- * cannot be changed by the top waiter as it
- * is blocked on hb2 lock if it tries to do
- * so. If something fiddled with it behind our
- * back the pi state lookup might unearth
- * it. So we rather use the known value than
- * rereading and handing potential crap to
- * lookup_pi_state.
+ * If we acquired the lock, then the user space value
+ * of uaddr2 should be vpid. It cannot be changed by
+ * the top waiter as it is blocked on hb2 lock if it
+ * tries to do so. If something fiddled with it behind
+ * our back the pi state lookup might unearth it. So
+ * we rather use the known value than rereading and
+ * handing potential crap to lookup_pi_state.
+ *
+ * If that call succeeds then we have pi_state and an
+ * initial refcount on it.
*/
ret = lookup_pi_state(ret, hb2, &key2, &pi_state);
}
switch (ret) {
case 0:
+ /* We hold a reference on the pi state. */
break;
+
+ /* If the above failed, then pi_state is NULL */
case -EFAULT:
- free_pi_state(pi_state);
- pi_state = NULL;
double_unlock_hb(hb1, hb2);
hb_waiters_dec(hb2);
put_futex_key(&key2);
@@ -1746,8 +1753,6 @@ retry_private:
* exit to complete.
* - The user space value changed.
*/
- free_pi_state(pi_state);
- pi_state = NULL;
double_unlock_hb(hb1, hb2);
hb_waiters_dec(hb2);
put_futex_key(&key2);
@@ -1801,30 +1806,58 @@ retry_private:
* of requeue_pi if we couldn't acquire the lock atomically.
*/
if (requeue_pi) {
- /* Prepare the waiter to take the rt_mutex. */
+ /*
+ * Prepare the waiter to take the rt_mutex. Take a
+ * refcount on the pi_state and store the pointer in
+ * the futex_q object of the waiter.
+ */
atomic_inc(&pi_state->refcount);
this->pi_state = pi_state;
ret = rt_mutex_start_proxy_lock(&pi_state->pi_mutex,
this->rt_waiter,
this->task);
if (ret == 1) {
- /* We got the lock. */
+ /*
+ * We got the lock. We do neither drop the
+ * refcount on pi_state nor clear
+ * this->pi_state because the waiter needs the
+ * pi_state for cleaning up the user space
+ * value. It will drop the refcount after
+ * doing so.
+ */
requeue_pi_wake_futex(this, &key2, hb2);
drop_count++;
continue;
} else if (ret) {
- /* -EDEADLK */
+ /*
+ * rt_mutex_start_proxy_lock() detected a
+ * potential deadlock when we tried to queue
+ * that waiter. Drop the pi_state reference
+ * which we took above and remove the pointer
+ * to the state from the waiters futex_q
+ * object.
+ */
this->pi_state = NULL;
- free_pi_state(pi_state);
- goto out_unlock;
+ put_pi_state(pi_state);
+ /*
+ * We stop queueing more waiters and let user
+ * space deal with the mess.
+ */
+ break;
}
}
requeue_futex(this, hb1, hb2, &key2);
drop_count++;
}
+ /*
+ * We took an extra initial reference to the pi_state either
+ * in futex_proxy_trylock_atomic() or in lookup_pi_state(). We
+ * need to drop it here again.
+ */
+ put_pi_state(pi_state);
+
out_unlock:
- free_pi_state(pi_state);
double_unlock_hb(hb1, hb2);
wake_up_q(&wake_q);
hb_waiters_dec(hb2);
@@ -1973,7 +2006,7 @@ static void unqueue_me_pi(struct futex_q *q)
__unqueue_futex(q);
BUG_ON(!q->pi_state);
- free_pi_state(q->pi_state);
+ put_pi_state(q->pi_state);
q->pi_state = NULL;
spin_unlock(q->lock_ptr);
@@ -2755,6 +2788,11 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
if (q.pi_state && (q.pi_state->owner != current)) {
spin_lock(q.lock_ptr);
ret = fixup_pi_state_owner(uaddr2, &q, current);
+ /*
+ * Drop the reference to the pi state which
+ * the requeue_pi() code acquired for us.
+ */
+ put_pi_state(q.pi_state);
spin_unlock(q.lock_ptr);
}
} else {
@@ -3046,7 +3084,8 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
if (op & FUTEX_CLOCK_REALTIME) {
flags |= FLAGS_CLOCKRT;
- if (cmd != FUTEX_WAIT_BITSET && cmd != FUTEX_WAIT_REQUEUE_PI)
+ if (cmd != FUTEX_WAIT && cmd != FUTEX_WAIT_BITSET && \
+ cmd != FUTEX_WAIT_REQUEUE_PI)
return -ENOSYS;
}
diff --git a/kernel/gcov/base.c b/kernel/gcov/base.c
index 7080ae1eb6c1..2f9df37940a0 100644
--- a/kernel/gcov/base.c
+++ b/kernel/gcov/base.c
@@ -123,11 +123,6 @@ void gcov_enable_events(void)
}
#ifdef CONFIG_MODULES
-static inline int within(void *addr, void *start, unsigned long size)
-{
- return ((addr >= start) && (addr < start + size));
-}
-
/* Update list and generate events when modules are unloaded. */
static int gcov_module_notifier(struct notifier_block *nb, unsigned long event,
void *data)
@@ -142,7 +137,7 @@ static int gcov_module_notifier(struct notifier_block *nb, unsigned long event,
/* Remove entries located in module from linked list. */
while ((info = gcov_info_next(info))) {
- if (within(info, mod->module_core, mod->core_size)) {
+ if (within_module((unsigned long)info, mod)) {
gcov_info_unlink(prev, info);
if (gcov_events_enabled)
gcov_event(GCOV_REMOVE, info);
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index 15206453b12a..5797909f4e5b 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -338,7 +338,6 @@ void handle_nested_irq(unsigned int irq)
raw_spin_lock_irq(&desc->lock);
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
- kstat_incr_irqs_this_cpu(desc);
action = desc->action;
if (unlikely(!action || irqd_irq_disabled(&desc->irq_data))) {
@@ -346,6 +345,7 @@ void handle_nested_irq(unsigned int irq)
goto out_unlock;
}
+ kstat_incr_irqs_this_cpu(desc);
irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
raw_spin_unlock_irq(&desc->lock);
@@ -412,13 +412,13 @@ void handle_simple_irq(struct irq_desc *desc)
goto out_unlock;
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
- kstat_incr_irqs_this_cpu(desc);
if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
desc->istate |= IRQS_PENDING;
goto out_unlock;
}
+ kstat_incr_irqs_this_cpu(desc);
handle_irq_event(desc);
out_unlock:
@@ -462,7 +462,6 @@ void handle_level_irq(struct irq_desc *desc)
goto out_unlock;
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
- kstat_incr_irqs_this_cpu(desc);
/*
* If its disabled or no action available
@@ -473,6 +472,7 @@ void handle_level_irq(struct irq_desc *desc)
goto out_unlock;
}
+ kstat_incr_irqs_this_cpu(desc);
handle_irq_event(desc);
cond_unmask_irq(desc);
@@ -532,7 +532,6 @@ void handle_fasteoi_irq(struct irq_desc *desc)
goto out;
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
- kstat_incr_irqs_this_cpu(desc);
/*
* If its disabled or no action available
@@ -544,6 +543,7 @@ void handle_fasteoi_irq(struct irq_desc *desc)
goto out;
}
+ kstat_incr_irqs_this_cpu(desc);
if (desc->istate & IRQS_ONESHOT)
mask_irq(desc);
@@ -950,6 +950,7 @@ void irq_chip_ack_parent(struct irq_data *data)
data = data->parent_data;
data->chip->irq_ack(data);
}
+EXPORT_SYMBOL_GPL(irq_chip_ack_parent);
/**
* irq_chip_mask_parent - Mask the parent interrupt
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index 239e2ae2c947..0409da0bcc33 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -159,6 +159,7 @@ static struct irq_desc *alloc_desc(int irq, int node, struct module *owner)
raw_spin_lock_init(&desc->lock);
lockdep_set_class(&desc->lock, &irq_desc_lock_class);
+ init_rcu_head(&desc->rcu);
desc_set_defaults(irq, desc, node, owner);
@@ -171,6 +172,15 @@ err_desc:
return NULL;
}
+static void delayed_free_desc(struct rcu_head *rhp)
+{
+ struct irq_desc *desc = container_of(rhp, struct irq_desc, rcu);
+
+ free_masks(desc);
+ free_percpu(desc->kstat_irqs);
+ kfree(desc);
+}
+
static void free_desc(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
@@ -187,9 +197,12 @@ static void free_desc(unsigned int irq)
delete_irq_desc(irq);
mutex_unlock(&sparse_irq_lock);
- free_masks(desc);
- free_percpu(desc->kstat_irqs);
- kfree(desc);
+ /*
+ * We free the descriptor, masks and stat fields via RCU. That
+ * allows demultiplex interrupts to do rcu based management of
+ * the child interrupts.
+ */
+ call_rcu(&desc->rcu, delayed_free_desc);
}
static int alloc_descs(unsigned int start, unsigned int cnt, int node,
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 22aa9612ef7c..8cf95de1ab3f 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -60,6 +60,7 @@ struct fwnode_handle *irq_domain_alloc_fwnode(void *data)
fwid->fwnode.type = FWNODE_IRQCHIP;
return &fwid->fwnode;
}
+EXPORT_SYMBOL_GPL(irq_domain_alloc_fwnode);
/**
* irq_domain_free_fwnode - Free a non-OF-backed fwnode_handle
@@ -70,13 +71,14 @@ void irq_domain_free_fwnode(struct fwnode_handle *fwnode)
{
struct irqchip_fwid *fwid;
- if (WARN_ON(fwnode->type != FWNODE_IRQCHIP))
+ if (WARN_ON(!is_fwnode_irqchip(fwnode)))
return;
fwid = container_of(fwnode, struct irqchip_fwid, fwnode);
kfree(fwid->name);
kfree(fwid);
}
+EXPORT_SYMBOL_GPL(irq_domain_free_fwnode);
/**
* __irq_domain_add() - Allocate a new irq_domain data structure
@@ -1013,6 +1015,7 @@ struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain,
return NULL;
}
+EXPORT_SYMBOL_GPL(irq_domain_get_irq_data);
/**
* irq_domain_set_hwirq_and_chip - Set hwirq and irqchip of @virq at @domain
@@ -1125,9 +1128,9 @@ static void irq_domain_free_irqs_recursive(struct irq_domain *domain,
}
}
-static int irq_domain_alloc_irqs_recursive(struct irq_domain *domain,
- unsigned int irq_base,
- unsigned int nr_irqs, void *arg)
+int irq_domain_alloc_irqs_recursive(struct irq_domain *domain,
+ unsigned int irq_base,
+ unsigned int nr_irqs, void *arg)
{
int ret = 0;
struct irq_domain *parent = domain->parent;
@@ -1343,6 +1346,7 @@ struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain,
return (irq_data && irq_data->domain == domain) ? irq_data : NULL;
}
+EXPORT_SYMBOL_GPL(irq_domain_get_irq_data);
/**
* irq_domain_set_info - Set the complete data for a @virq in @domain
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 0eebaeef317b..841187239adc 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -1434,6 +1434,7 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
if (!desc)
return NULL;
+ chip_bus_lock(desc);
raw_spin_lock_irqsave(&desc->lock, flags);
/*
@@ -1447,7 +1448,7 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
if (!action) {
WARN(1, "Trying to free already-free IRQ %d\n", irq);
raw_spin_unlock_irqrestore(&desc->lock, flags);
-
+ chip_bus_sync_unlock(desc);
return NULL;
}
@@ -1475,6 +1476,7 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
#endif
raw_spin_unlock_irqrestore(&desc->lock, flags);
+ chip_bus_sync_unlock(desc);
unregister_handler_proc(irq, action);
@@ -1553,9 +1555,7 @@ void free_irq(unsigned int irq, void *dev_id)
desc->affinity_notify = NULL;
#endif
- chip_bus_lock(desc);
kfree(__free_irq(irq, dev_id));
- chip_bus_sync_unlock(desc);
}
EXPORT_SYMBOL(free_irq);
@@ -1743,6 +1743,31 @@ out:
}
EXPORT_SYMBOL_GPL(enable_percpu_irq);
+/**
+ * irq_percpu_is_enabled - Check whether the per cpu irq is enabled
+ * @irq: Linux irq number to check for
+ *
+ * Must be called from a non migratable context. Returns the enable
+ * state of a per cpu interrupt on the current cpu.
+ */
+bool irq_percpu_is_enabled(unsigned int irq)
+{
+ unsigned int cpu = smp_processor_id();
+ struct irq_desc *desc;
+ unsigned long flags;
+ bool is_enabled;
+
+ desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_PERCPU);
+ if (!desc)
+ return false;
+
+ is_enabled = cpumask_test_cpu(cpu, desc->percpu_enabled);
+ irq_put_desc_unlock(desc, flags);
+
+ return is_enabled;
+}
+EXPORT_SYMBOL_GPL(irq_percpu_is_enabled);
+
void disable_percpu_irq(unsigned int irq)
{
unsigned int cpu = smp_processor_id();
diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
index 6b0c0b74a2a1..15b249e7c673 100644
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -252,6 +252,60 @@ struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode,
&msi_domain_ops, info);
}
+int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev,
+ int nvec, msi_alloc_info_t *arg)
+{
+ struct msi_domain_info *info = domain->host_data;
+ struct msi_domain_ops *ops = info->ops;
+ int ret;
+
+ ret = ops->msi_check(domain, info, dev);
+ if (ret == 0)
+ ret = ops->msi_prepare(domain, dev, nvec, arg);
+
+ return ret;
+}
+
+int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev,
+ int virq, int nvec, msi_alloc_info_t *arg)
+{
+ struct msi_domain_info *info = domain->host_data;
+ struct msi_domain_ops *ops = info->ops;
+ struct msi_desc *desc;
+ int ret = 0;
+
+ for_each_msi_entry(desc, dev) {
+ /* Don't even try the multi-MSI brain damage. */
+ if (WARN_ON(!desc->irq || desc->nvec_used != 1)) {
+ ret = -EINVAL;
+ break;
+ }
+
+ if (!(desc->irq >= virq && desc->irq < (virq + nvec)))
+ continue;
+
+ ops->set_desc(arg, desc);
+ /* Assumes the domain mutex is held! */
+ ret = irq_domain_alloc_irqs_recursive(domain, virq, 1, arg);
+ if (ret)
+ break;
+
+ irq_set_msi_desc_off(virq, 0, desc);
+ }
+
+ if (ret) {
+ /* Mop up the damage */
+ for_each_msi_entry(desc, dev) {
+ if (!(desc->irq >= virq && desc->irq < (virq + nvec)))
+ continue;
+
+ irq_domain_free_irqs_common(domain, desc->irq, 1);
+ }
+ }
+
+ return ret;
+}
+
/**
* msi_domain_alloc_irqs - Allocate interrupts from a MSI interrupt domain
* @domain: The domain to allocate from
@@ -270,9 +324,7 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
struct msi_desc *desc;
int i, ret, virq = -1;
- ret = ops->msi_check(domain, info, dev);
- if (ret == 0)
- ret = ops->msi_prepare(domain, dev, nvec, &arg);
+ ret = msi_domain_prepare_irqs(domain, dev, nvec, &arg);
if (ret)
return ret;
diff --git a/kernel/irq_work.c b/kernel/irq_work.c
index cbf9fb899d92..bcf107ce0854 100644
--- a/kernel/irq_work.c
+++ b/kernel/irq_work.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2010 Red Hat, Inc., Peter Zijlstra
*
* Provides a framework for enqueueing and running callbacks from hardirq
* context. The enqueueing is NMI-safe.
diff --git a/kernel/jump_label.c b/kernel/jump_label.c
index f7dd15d537f9..05254eeb4b4e 100644
--- a/kernel/jump_label.c
+++ b/kernel/jump_label.c
@@ -2,7 +2,7 @@
* jump label support
*
* Copyright (C) 2009 Jason Baron <jbaron@redhat.com>
- * Copyright (C) 2011 Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2011 Peter Zijlstra
*
*/
#include <linux/memory.h>
diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c
index 11b64a63c0f8..c823f3001e12 100644
--- a/kernel/kexec_core.c
+++ b/kernel/kexec_core.c
@@ -853,7 +853,12 @@ struct kimage *kexec_image;
struct kimage *kexec_crash_image;
int kexec_load_disabled;
-void crash_kexec(struct pt_regs *regs)
+/*
+ * No panic_cpu check version of crash_kexec(). This function is called
+ * only when panic_cpu holds the current CPU number; this is the only CPU
+ * which processes crash_kexec routines.
+ */
+void __crash_kexec(struct pt_regs *regs)
{
/* Take the kexec_mutex here to prevent sys_kexec_load
* running on one cpu from replacing the crash kernel
@@ -876,6 +881,29 @@ void crash_kexec(struct pt_regs *regs)
}
}
+void crash_kexec(struct pt_regs *regs)
+{
+ int old_cpu, this_cpu;
+
+ /*
+ * Only one CPU is allowed to execute the crash_kexec() code as with
+ * panic(). Otherwise parallel calls of panic() and crash_kexec()
+ * may stop each other. To exclude them, we use panic_cpu here too.
+ */
+ this_cpu = raw_smp_processor_id();
+ old_cpu = atomic_cmpxchg(&panic_cpu, PANIC_CPU_INVALID, this_cpu);
+ if (old_cpu == PANIC_CPU_INVALID) {
+ /* This is the 1st CPU which comes here, so go ahead. */
+ __crash_kexec(regs);
+
+ /*
+ * Reset panic_cpu to allow another panic()/crash_kexec()
+ * call.
+ */
+ atomic_set(&panic_cpu, PANIC_CPU_INVALID);
+ }
+}
+
size_t crash_get_memory_size(void)
{
size_t size = 0;
diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c
index e83b26464061..152da4a48867 100644
--- a/kernel/ksysfs.c
+++ b/kernel/ksysfs.c
@@ -20,7 +20,7 @@
#include <linux/capability.h>
#include <linux/compiler.h>
-#include <linux/rcupdate.h> /* rcu_expedited */
+#include <linux/rcupdate.h> /* rcu_expedited and rcu_normal */
#define KERNEL_ATTR_RO(_name) \
static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
@@ -144,11 +144,12 @@ static ssize_t fscaps_show(struct kobject *kobj,
}
KERNEL_ATTR_RO(fscaps);
+#ifndef CONFIG_TINY_RCU
int rcu_expedited;
static ssize_t rcu_expedited_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
- return sprintf(buf, "%d\n", rcu_expedited);
+ return sprintf(buf, "%d\n", READ_ONCE(rcu_expedited));
}
static ssize_t rcu_expedited_store(struct kobject *kobj,
struct kobj_attribute *attr,
@@ -161,6 +162,24 @@ static ssize_t rcu_expedited_store(struct kobject *kobj,
}
KERNEL_ATTR_RW(rcu_expedited);
+int rcu_normal;
+static ssize_t rcu_normal_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", READ_ONCE(rcu_normal));
+}
+static ssize_t rcu_normal_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ if (kstrtoint(buf, 0, &rcu_normal))
+ return -EINVAL;
+
+ return count;
+}
+KERNEL_ATTR_RW(rcu_normal);
+#endif /* #ifndef CONFIG_TINY_RCU */
+
/*
* Make /sys/kernel/notes give the raw contents of our kernel .notes section.
*/
@@ -202,7 +221,10 @@ static struct attribute * kernel_attrs[] = {
&kexec_crash_size_attr.attr,
&vmcoreinfo_attr.attr,
#endif
+#ifndef CONFIG_TINY_RCU
&rcu_expedited_attr.attr,
+ &rcu_normal_attr.attr,
+#endif
NULL
};
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index db545cbcdb89..bc2c85c064c1 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -28,6 +28,7 @@
#include <linux/list.h>
#include <linux/kallsyms.h>
#include <linux/livepatch.h>
+#include <asm/cacheflush.h>
/**
* struct klp_ops - structure for tracking registered ftrace ops structs
@@ -135,13 +136,8 @@ struct klp_find_arg {
const char *objname;
const char *name;
unsigned long addr;
- /*
- * If count == 0, the symbol was not found. If count == 1, a unique
- * match was found and addr is set. If count > 1, there is
- * unresolvable ambiguity among "count" number of symbols with the same
- * name in the same object.
- */
unsigned long count;
+ unsigned long pos;
};
static int klp_find_callback(void *data, const char *name,
@@ -158,37 +154,48 @@ static int klp_find_callback(void *data, const char *name,
if (args->objname && strcmp(args->objname, mod->name))
return 0;
- /*
- * args->addr might be overwritten if another match is found
- * but klp_find_object_symbol() handles this and only returns the
- * addr if count == 1.
- */
args->addr = addr;
args->count++;
+ /*
+ * Finish the search when the symbol is found for the desired position
+ * or the position is not defined for a non-unique symbol.
+ */
+ if ((args->pos && (args->count == args->pos)) ||
+ (!args->pos && (args->count > 1)))
+ return 1;
+
return 0;
}
static int klp_find_object_symbol(const char *objname, const char *name,
- unsigned long *addr)
+ unsigned long sympos, unsigned long *addr)
{
struct klp_find_arg args = {
.objname = objname,
.name = name,
.addr = 0,
- .count = 0
+ .count = 0,
+ .pos = sympos,
};
mutex_lock(&module_mutex);
kallsyms_on_each_symbol(klp_find_callback, &args);
mutex_unlock(&module_mutex);
- if (args.count == 0)
+ /*
+ * Ensure an address was found. If sympos is 0, ensure symbol is unique;
+ * otherwise ensure the symbol position count matches sympos.
+ */
+ if (args.addr == 0)
pr_err("symbol '%s' not found in symbol table\n", name);
- else if (args.count > 1)
+ else if (args.count > 1 && sympos == 0) {
pr_err("unresolvable ambiguity (%lu matches) on symbol '%s' in object '%s'\n",
args.count, name, objname);
- else {
+ } else if (sympos != args.count && sympos > 0) {
+ pr_err("symbol position %lu for symbol '%s' in object '%s' not found\n",
+ sympos, name, objname ? objname : "vmlinux");
+ } else {
*addr = args.addr;
return 0;
}
@@ -197,66 +204,6 @@ static int klp_find_object_symbol(const char *objname, const char *name,
return -EINVAL;
}
-struct klp_verify_args {
- const char *name;
- const unsigned long addr;
-};
-
-static int klp_verify_callback(void *data, const char *name,
- struct module *mod, unsigned long addr)
-{
- struct klp_verify_args *args = data;
-
- if (!mod &&
- !strcmp(args->name, name) &&
- args->addr == addr)
- return 1;
-
- return 0;
-}
-
-static int klp_verify_vmlinux_symbol(const char *name, unsigned long addr)
-{
- struct klp_verify_args args = {
- .name = name,
- .addr = addr,
- };
- int ret;
-
- mutex_lock(&module_mutex);
- ret = kallsyms_on_each_symbol(klp_verify_callback, &args);
- mutex_unlock(&module_mutex);
-
- 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,
- struct klp_func *func)
-{
- int ret;
-
-#if defined(CONFIG_RANDOMIZE_BASE)
- /* 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))
- ret = klp_find_object_symbol(obj->name, func->old_name,
- &func->old_addr);
- else
- ret = klp_verify_vmlinux_symbol(func->old_name,
- func->old_addr);
-
- return ret;
-}
-
/*
* external symbols are located outside the parent object (where the parent
* object is either vmlinux or the kmod being patched).
@@ -276,14 +223,18 @@ static int klp_find_external_symbol(struct module *pmod, const char *name,
}
preempt_enable();
- /* otherwise check if it's in another .o within the patch module */
- return klp_find_object_symbol(pmod->name, name, addr);
+ /*
+ * Check if it's in another .o within the patch module. This also
+ * checks that the external symbol is unique.
+ */
+ return klp_find_object_symbol(pmod->name, name, 0, addr);
}
static int klp_write_object_relocations(struct module *pmod,
struct klp_object *obj)
{
- int ret;
+ int ret = 0;
+ unsigned long val;
struct klp_reloc *reloc;
if (WARN_ON(!klp_is_object_loaded(obj)))
@@ -292,41 +243,38 @@ static int klp_write_object_relocations(struct module *pmod,
if (WARN_ON(!obj->relocs))
return -EINVAL;
+ module_disable_ro(pmod);
+
for (reloc = obj->relocs; reloc->name; reloc++) {
- if (!klp_is_module(obj)) {
-
-#if defined(CONFIG_RANDOMIZE_BASE)
- /* If KASLR has been enabled, adjust old value accordingly */
- if (kaslr_enabled())
- reloc->val += kaslr_offset();
-#endif
- ret = klp_verify_vmlinux_symbol(reloc->name,
- reloc->val);
- if (ret)
- return ret;
- } else {
- /* module, reloc->val needs to be discovered */
- if (reloc->external)
- ret = klp_find_external_symbol(pmod,
- reloc->name,
- &reloc->val);
- else
- ret = klp_find_object_symbol(obj->mod->name,
- reloc->name,
- &reloc->val);
- if (ret)
- return ret;
- }
+ /* discover the address of the referenced symbol */
+ if (reloc->external) {
+ if (reloc->sympos > 0) {
+ pr_err("non-zero sympos for external reloc symbol '%s' is not supported\n",
+ reloc->name);
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = klp_find_external_symbol(pmod, reloc->name, &val);
+ } else
+ ret = klp_find_object_symbol(obj->name,
+ reloc->name,
+ reloc->sympos,
+ &val);
+ if (ret)
+ goto out;
+
ret = klp_write_module_reloc(pmod, reloc->type, reloc->loc,
- reloc->val + reloc->addend);
+ val + reloc->addend);
if (ret) {
pr_err("relocation failed for symbol '%s' at 0x%016lx (%d)\n",
- reloc->name, reloc->val, ret);
- return ret;
+ reloc->name, val, ret);
+ goto out;
}
}
- return 0;
+out:
+ module_enable_ro(pmod);
+ return ret;
}
static void notrace klp_ftrace_handler(unsigned long ip,
@@ -593,7 +541,7 @@ EXPORT_SYMBOL_GPL(klp_enable_patch);
* /sys/kernel/livepatch/<patch>
* /sys/kernel/livepatch/<patch>/enabled
* /sys/kernel/livepatch/<patch>/<object>
- * /sys/kernel/livepatch/<patch>/<object>/<func>
+ * /sys/kernel/livepatch/<patch>/<object>/<function,sympos>
*/
static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr,
@@ -738,8 +686,14 @@ static int klp_init_func(struct klp_object *obj, struct klp_func *func)
INIT_LIST_HEAD(&func->stack_node);
func->state = KLP_DISABLED;
+ /* The format for the sysfs directory is <function,sympos> where sympos
+ * is the nth occurrence of this symbol in kallsyms for the patched
+ * object. If the user selects 0 for old_sympos, then 1 will be used
+ * since a unique symbol will be the first occurrence.
+ */
return kobject_init_and_add(&func->kobj, &klp_ktype_func,
- &obj->kobj, "%s", func->old_name);
+ &obj->kobj, "%s,%lu", func->old_name,
+ func->old_sympos ? func->old_sympos : 1);
}
/* parts of the initialization that is done only when the object is loaded */
@@ -756,7 +710,9 @@ static int klp_init_object_loaded(struct klp_patch *patch,
}
klp_for_each_func(obj, func) {
- ret = klp_find_verify_func_addr(obj, func);
+ ret = klp_find_object_symbol(obj->name, func->old_name,
+ func->old_sympos,
+ &func->old_addr);
if (ret)
return ret;
}
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index deae3907ac1e..60ace56618f6 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -6,7 +6,7 @@
* Started by Ingo Molnar:
*
* Copyright (C) 2006,2007 Red Hat, Inc., Ingo Molnar <mingo@redhat.com>
- * Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra
*
* this code maps all the lock dependencies as they occur in a live kernel
* and will warn about the following classes of locking bugs:
diff --git a/kernel/locking/lockdep_proc.c b/kernel/locking/lockdep_proc.c
index d83d798bef95..dbb61a302548 100644
--- a/kernel/locking/lockdep_proc.c
+++ b/kernel/locking/lockdep_proc.c
@@ -6,7 +6,7 @@
* Started by Ingo Molnar:
*
* Copyright (C) 2006,2007 Red Hat, Inc., Ingo Molnar <mingo@redhat.com>
- * Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra
*
* Code for /proc/lockdep and /proc/lockdep_stats:
*
diff --git a/kernel/locking/osq_lock.c b/kernel/locking/osq_lock.c
index d092a0c9c2d4..05a37857ab55 100644
--- a/kernel/locking/osq_lock.c
+++ b/kernel/locking/osq_lock.c
@@ -93,10 +93,12 @@ bool osq_lock(struct optimistic_spin_queue *lock)
node->cpu = curr;
/*
- * ACQUIRE semantics, pairs with corresponding RELEASE
- * in unlock() uncontended, or fastpath.
+ * We need both ACQUIRE (pairs with corresponding RELEASE in
+ * unlock() uncontended, or fastpath) and RELEASE (to publish
+ * the node fields we just initialised) semantics when updating
+ * the lock tail.
*/
- old = atomic_xchg_acquire(&lock->tail, curr);
+ old = atomic_xchg(&lock->tail, curr);
if (old == OSQ_UNLOCKED_VAL)
return true;
diff --git a/kernel/locking/qspinlock.c b/kernel/locking/qspinlock.c
index 87e9ce6a63c5..393d1874b9e0 100644
--- a/kernel/locking/qspinlock.c
+++ b/kernel/locking/qspinlock.c
@@ -14,8 +14,9 @@
* (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.
* (C) Copyright 2013-2014 Red Hat, Inc.
* (C) Copyright 2015 Intel Corp.
+ * (C) Copyright 2015 Hewlett-Packard Enterprise Development LP
*
- * Authors: Waiman Long <waiman.long@hp.com>
+ * Authors: Waiman Long <waiman.long@hpe.com>
* Peter Zijlstra <peterz@infradead.org>
*/
@@ -176,7 +177,12 @@ 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;
+ /*
+ * Use release semantics to make sure that the MCS node is properly
+ * initialized before changing the tail code.
+ */
+ return (u32)xchg_release(&l->tail,
+ tail >> _Q_TAIL_OFFSET) << _Q_TAIL_OFFSET;
}
#else /* _Q_PENDING_BITS == 8 */
@@ -208,7 +214,11 @@ static __always_inline u32 xchg_tail(struct qspinlock *lock, u32 tail)
for (;;) {
new = (val & _Q_LOCKED_PENDING_MASK) | tail;
- old = atomic_cmpxchg(&lock->val, val, new);
+ /*
+ * Use release semantics to make sure that the MCS node is
+ * properly initialized before changing the tail code.
+ */
+ old = atomic_cmpxchg_release(&lock->val, val, new);
if (old == val)
break;
@@ -238,18 +248,20 @@ static __always_inline void set_locked(struct qspinlock *lock)
*/
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_wait_node(struct mcs_spinlock *node,
+ struct mcs_spinlock *prev) { }
static __always_inline void __pv_kick_node(struct qspinlock *lock,
struct mcs_spinlock *node) { }
-static __always_inline void __pv_wait_head(struct qspinlock *lock,
- struct mcs_spinlock *node) { }
+static __always_inline u32 __pv_wait_head_or_lock(struct qspinlock *lock,
+ struct mcs_spinlock *node)
+ { return 0; }
#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
+#define pv_wait_head_or_lock __pv_wait_head_or_lock
#ifdef CONFIG_PARAVIRT_SPINLOCKS
#define queued_spin_lock_slowpath native_queued_spin_lock_slowpath
@@ -319,7 +331,11 @@ void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val)
if (val == new)
new |= _Q_PENDING_VAL;
- old = atomic_cmpxchg(&lock->val, val, new);
+ /*
+ * Acquire semantic is required here as the function may
+ * return immediately if the lock was free.
+ */
+ old = atomic_cmpxchg_acquire(&lock->val, val, new);
if (old == val)
break;
@@ -382,6 +398,7 @@ queue:
* p,*,* -> n,*,*
*/
old = xchg_tail(lock, tail);
+ next = NULL;
/*
* if there was a previous node; link it and wait until reaching the
@@ -391,8 +408,18 @@ queue:
prev = decode_tail(old);
WRITE_ONCE(prev->next, node);
- pv_wait_node(node);
+ pv_wait_node(node, prev);
arch_mcs_spin_lock_contended(&node->locked);
+
+ /*
+ * While waiting for the MCS lock, the next pointer may have
+ * been set by another lock waiter. We optimistically load
+ * the next pointer & prefetch the cacheline for writing
+ * to reduce latency in the upcoming MCS unlock operation.
+ */
+ next = READ_ONCE(node->next);
+ if (next)
+ prefetchw(next);
}
/*
@@ -406,11 +433,22 @@ queue:
* sequentiality; this is because the set_locked() function below
* does not imply a full barrier.
*
+ * The PV pv_wait_head_or_lock function, if active, will acquire
+ * the lock and return a non-zero value. So we have to skip the
+ * smp_load_acquire() call. As the next PV queue head hasn't been
+ * designated yet, there is no way for the locked value to become
+ * _Q_SLOW_VAL. So both the set_locked() and the
+ * atomic_cmpxchg_relaxed() calls will be safe.
+ *
+ * If PV isn't active, 0 will be returned instead.
+ *
*/
- pv_wait_head(lock, node);
- while ((val = smp_load_acquire(&lock->val.counter)) & _Q_LOCKED_PENDING_MASK)
- cpu_relax();
+ if ((val = pv_wait_head_or_lock(lock, node)))
+ goto locked;
+ smp_cond_acquire(!((val = atomic_read(&lock->val)) & _Q_LOCKED_PENDING_MASK));
+
+locked:
/*
* claim the lock:
*
@@ -422,11 +460,17 @@ queue:
* to grab the lock.
*/
for (;;) {
- if (val != tail) {
+ /* In the PV case we might already have _Q_LOCKED_VAL set */
+ if ((val & _Q_TAIL_MASK) != tail) {
set_locked(lock);
break;
}
- old = atomic_cmpxchg(&lock->val, val, _Q_LOCKED_VAL);
+ /*
+ * The smp_load_acquire() call above has provided the necessary
+ * acquire semantics required for locking. At most two
+ * iterations of this loop may be ran.
+ */
+ old = atomic_cmpxchg_relaxed(&lock->val, val, _Q_LOCKED_VAL);
if (old == val)
goto release; /* No contention */
@@ -434,10 +478,12 @@ queue:
}
/*
- * contended path; wait for next, release.
+ * contended path; wait for next if not observed yet, release.
*/
- while (!(next = READ_ONCE(node->next)))
- cpu_relax();
+ if (!next) {
+ while (!(next = READ_ONCE(node->next)))
+ cpu_relax();
+ }
arch_mcs_spin_unlock_contended(&next->locked);
pv_kick_node(lock, next);
@@ -462,7 +508,7 @@ EXPORT_SYMBOL(queued_spin_lock_slowpath);
#undef pv_init_node
#undef pv_wait_node
#undef pv_kick_node
-#undef pv_wait_head
+#undef pv_wait_head_or_lock
#undef queued_spin_lock_slowpath
#define queued_spin_lock_slowpath __pv_queued_spin_lock_slowpath
diff --git a/kernel/locking/qspinlock_paravirt.h b/kernel/locking/qspinlock_paravirt.h
index f0450ff4829b..87bb235c3448 100644
--- a/kernel/locking/qspinlock_paravirt.h
+++ b/kernel/locking/qspinlock_paravirt.h
@@ -23,6 +23,20 @@
#define _Q_SLOW_VAL (3U << _Q_LOCKED_OFFSET)
/*
+ * Queue Node Adaptive Spinning
+ *
+ * A queue node vCPU will stop spinning if the vCPU in the previous node is
+ * not running. The one lock stealing attempt allowed at slowpath entry
+ * mitigates the slight slowdown for non-overcommitted guest with this
+ * aggressive wait-early mechanism.
+ *
+ * The status of the previous node will be checked at fixed interval
+ * controlled by PV_PREV_CHECK_MASK. This is to ensure that we won't
+ * pound on the cacheline of the previous node too heavily.
+ */
+#define PV_PREV_CHECK_MASK 0xff
+
+/*
* Queue node uses: vcpu_running & vcpu_halted.
* Queue head uses: vcpu_running & vcpu_hashed.
*/
@@ -41,6 +55,94 @@ struct pv_node {
};
/*
+ * By replacing the regular queued_spin_trylock() with the function below,
+ * it will be called once when a lock waiter enter the PV slowpath before
+ * being queued. By allowing one lock stealing attempt here when the pending
+ * bit is off, it helps to reduce the performance impact of lock waiter
+ * preemption without the drawback of lock starvation.
+ */
+#define queued_spin_trylock(l) pv_queued_spin_steal_lock(l)
+static inline bool pv_queued_spin_steal_lock(struct qspinlock *lock)
+{
+ struct __qspinlock *l = (void *)lock;
+
+ return !(atomic_read(&lock->val) & _Q_LOCKED_PENDING_MASK) &&
+ (cmpxchg(&l->locked, 0, _Q_LOCKED_VAL) == 0);
+}
+
+/*
+ * The pending bit is used by the queue head vCPU to indicate that it
+ * is actively spinning on the lock and no lock stealing is allowed.
+ */
+#if _Q_PENDING_BITS == 8
+static __always_inline void set_pending(struct qspinlock *lock)
+{
+ struct __qspinlock *l = (void *)lock;
+
+ WRITE_ONCE(l->pending, 1);
+}
+
+static __always_inline void clear_pending(struct qspinlock *lock)
+{
+ struct __qspinlock *l = (void *)lock;
+
+ WRITE_ONCE(l->pending, 0);
+}
+
+/*
+ * The pending bit check in pv_queued_spin_steal_lock() isn't a memory
+ * barrier. Therefore, an atomic cmpxchg() is used to acquire the lock
+ * just to be sure that it will get it.
+ */
+static __always_inline int trylock_clear_pending(struct qspinlock *lock)
+{
+ struct __qspinlock *l = (void *)lock;
+
+ return !READ_ONCE(l->locked) &&
+ (cmpxchg(&l->locked_pending, _Q_PENDING_VAL, _Q_LOCKED_VAL)
+ == _Q_PENDING_VAL);
+}
+#else /* _Q_PENDING_BITS == 8 */
+static __always_inline void set_pending(struct qspinlock *lock)
+{
+ atomic_set_mask(_Q_PENDING_VAL, &lock->val);
+}
+
+static __always_inline void clear_pending(struct qspinlock *lock)
+{
+ atomic_clear_mask(_Q_PENDING_VAL, &lock->val);
+}
+
+static __always_inline int trylock_clear_pending(struct qspinlock *lock)
+{
+ int val = atomic_read(&lock->val);
+
+ for (;;) {
+ int old, new;
+
+ if (val & _Q_LOCKED_MASK)
+ break;
+
+ /*
+ * Try to clear pending bit & set locked bit
+ */
+ old = val;
+ new = (val & ~_Q_PENDING_MASK) | _Q_LOCKED_VAL;
+ val = atomic_cmpxchg(&lock->val, old, new);
+
+ if (val == old)
+ return 1;
+ }
+ return 0;
+}
+#endif /* _Q_PENDING_BITS == 8 */
+
+/*
+ * Include queued spinlock statistics code
+ */
+#include "qspinlock_stat.h"
+
+/*
* Lock and MCS node addresses hash table for fast lookup
*
* Hashing is done on a per-cacheline basis to minimize the need to access
@@ -100,10 +202,13 @@ 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;
+ int hopcnt = 0;
for_each_hash_entry(he, offset, hash) {
+ hopcnt++;
if (!cmpxchg(&he->lock, NULL, lock)) {
WRITE_ONCE(he->node, node);
+ qstat_hop(hopcnt);
return &he->lock;
}
}
@@ -144,6 +249,20 @@ static struct pv_node *pv_unhash(struct qspinlock *lock)
}
/*
+ * Return true if when it is time to check the previous node which is not
+ * in a running state.
+ */
+static inline bool
+pv_wait_early(struct pv_node *prev, int loop)
+{
+
+ if ((loop & PV_PREV_CHECK_MASK) != 0)
+ return false;
+
+ return READ_ONCE(prev->state) != vcpu_running;
+}
+
+/*
* Initialize the PV part of the mcs_spinlock node.
*/
static void pv_init_node(struct mcs_spinlock *node)
@@ -161,15 +280,23 @@ static void pv_init_node(struct mcs_spinlock *node)
* pv_kick_node() is used to set _Q_SLOW_VAL and fill in hash table on its
* behalf.
*/
-static void pv_wait_node(struct mcs_spinlock *node)
+static void pv_wait_node(struct mcs_spinlock *node, struct mcs_spinlock *prev)
{
struct pv_node *pn = (struct pv_node *)node;
+ struct pv_node *pp = (struct pv_node *)prev;
+ int waitcnt = 0;
int loop;
+ bool wait_early;
- for (;;) {
- for (loop = SPIN_THRESHOLD; loop; loop--) {
+ /* waitcnt processing will be compiled out if !QUEUED_LOCK_STAT */
+ for (;; waitcnt++) {
+ for (wait_early = false, loop = SPIN_THRESHOLD; loop; loop--) {
if (READ_ONCE(node->locked))
return;
+ if (pv_wait_early(pp, loop)) {
+ wait_early = true;
+ break;
+ }
cpu_relax();
}
@@ -184,12 +311,17 @@ static void pv_wait_node(struct mcs_spinlock *node)
*/
smp_store_mb(pn->state, vcpu_halted);
- if (!READ_ONCE(node->locked))
+ if (!READ_ONCE(node->locked)) {
+ qstat_inc(qstat_pv_wait_node, true);
+ qstat_inc(qstat_pv_wait_again, waitcnt);
+ qstat_inc(qstat_pv_wait_early, wait_early);
pv_wait(&pn->state, vcpu_halted);
+ }
/*
- * If pv_kick_node() changed us to vcpu_hashed, retain that value
- * so that pv_wait_head() knows to not also try to hash this lock.
+ * If pv_kick_node() changed us to vcpu_hashed, retain that
+ * value so that pv_wait_head_or_lock() knows to not also try
+ * to hash this lock.
*/
cmpxchg(&pn->state, vcpu_halted, vcpu_running);
@@ -200,6 +332,7 @@ static void pv_wait_node(struct mcs_spinlock *node)
* So it is better to spin for a while in the hope that the
* MCS lock will be released soon.
*/
+ qstat_inc(qstat_pv_spurious_wakeup, !READ_ONCE(node->locked));
}
/*
@@ -212,8 +345,9 @@ static void pv_wait_node(struct mcs_spinlock *node)
/*
* Called after setting next->locked = 1 when we're the lock owner.
*
- * Instead of waking the waiters stuck in pv_wait_node() advance their state such
- * that they're waiting in pv_wait_head(), this avoids a wake/sleep cycle.
+ * Instead of waking the waiters stuck in pv_wait_node() advance their state
+ * such that they're waiting in pv_wait_head_or_lock(), this avoids a
+ * wake/sleep cycle.
*/
static void pv_kick_node(struct qspinlock *lock, struct mcs_spinlock *node)
{
@@ -242,14 +376,19 @@ static void pv_kick_node(struct qspinlock *lock, struct mcs_spinlock *node)
}
/*
- * Wait for l->locked to become clear; halt the vcpu after a short spin.
+ * Wait for l->locked to become clear and acquire the lock;
+ * halt the vcpu after a short spin.
* __pv_queued_spin_unlock() will wake us.
+ *
+ * The current value of the lock will be returned for additional processing.
*/
-static void pv_wait_head(struct qspinlock *lock, struct mcs_spinlock *node)
+static u32
+pv_wait_head_or_lock(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 waitcnt = 0;
int loop;
/*
@@ -259,12 +398,25 @@ static void pv_wait_head(struct qspinlock *lock, struct mcs_spinlock *node)
if (READ_ONCE(pn->state) == vcpu_hashed)
lp = (struct qspinlock **)1;
- for (;;) {
+ for (;; waitcnt++) {
+ /*
+ * Set correct vCPU state to be used by queue node wait-early
+ * mechanism.
+ */
+ WRITE_ONCE(pn->state, vcpu_running);
+
+ /*
+ * Set the pending bit in the active lock spinning loop to
+ * disable lock stealing before attempting to acquire the lock.
+ */
+ set_pending(lock);
for (loop = SPIN_THRESHOLD; loop; loop--) {
- if (!READ_ONCE(l->locked))
- return;
+ if (trylock_clear_pending(lock))
+ goto gotlock;
cpu_relax();
}
+ clear_pending(lock);
+
if (!lp) { /* ONCE */
lp = pv_hash(lock, pn);
@@ -280,51 +432,50 @@ static void pv_wait_head(struct qspinlock *lock, struct mcs_spinlock *node)
*
* Matches the smp_rmb() in __pv_queued_spin_unlock().
*/
- if (!cmpxchg(&l->locked, _Q_LOCKED_VAL, _Q_SLOW_VAL)) {
+ if (xchg(&l->locked, _Q_SLOW_VAL) == 0) {
/*
- * The lock is free and _Q_SLOW_VAL has never
- * been set. Therefore we need to unhash before
- * getting the lock.
+ * The lock was free and now we own the lock.
+ * Change the lock value back to _Q_LOCKED_VAL
+ * and unhash the table.
*/
+ WRITE_ONCE(l->locked, _Q_LOCKED_VAL);
WRITE_ONCE(*lp, NULL);
- return;
+ goto gotlock;
}
}
+ WRITE_ONCE(pn->state, vcpu_halted);
+ qstat_inc(qstat_pv_wait_head, true);
+ qstat_inc(qstat_pv_wait_again, waitcnt);
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.
+ * wakeup or another vCPU has stolen the lock. The current
+ * vCPU should spin again.
*/
+ qstat_inc(qstat_pv_spurious_wakeup, READ_ONCE(l->locked));
}
/*
- * 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.
+ * The cmpxchg() or xchg() call before coming here provides the
+ * acquire semantics for locking. The dummy ORing of _Q_LOCKED_VAL
+ * here is to indicate to the compiler that the value will always
+ * be nozero to enable better code optimization.
*/
+gotlock:
+ return (u32)(atomic_read(&lock->val) | _Q_LOCKED_VAL);
}
/*
- * PV version of the unlock function to be used in stead of
- * queued_spin_unlock().
+ * PV versions of the unlock fastpath and slowpath functions to be used
+ * instead of queued_spin_unlock().
*/
-__visible void __pv_queued_spin_unlock(struct qspinlock *lock)
+__visible void
+__pv_queued_spin_unlock_slowpath(struct qspinlock *lock, u8 locked)
{
struct __qspinlock *l = (void *)lock;
struct pv_node *node;
- u8 locked;
-
- /*
- * 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.
- */
- locked = cmpxchg(&l->locked, _Q_LOCKED_VAL, 0);
- if (likely(locked == _Q_LOCKED_VAL))
- return;
if (unlikely(locked != _Q_SLOW_VAL)) {
WARN(!debug_locks_silent,
@@ -338,7 +489,7 @@ __visible void __pv_queued_spin_unlock(struct qspinlock *lock)
* so we need a barrier to order the read of the node data in
* pv_unhash *after* we've read the lock being _Q_SLOW_VAL.
*
- * Matches the cmpxchg() in pv_wait_head() setting _Q_SLOW_VAL.
+ * Matches the cmpxchg() in pv_wait_head_or_lock() setting _Q_SLOW_VAL.
*/
smp_rmb();
@@ -361,14 +512,35 @@ __visible void __pv_queued_spin_unlock(struct qspinlock *lock)
* vCPU is harmless other than the additional latency in completing
* the unlock.
*/
+ qstat_inc(qstat_pv_kick_unlock, true);
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.
+ * __pv_queued_spin_unlock() to make the callee-save thunk and the real unlock
+ * function close to each other sharing consecutive instruction cachelines.
+ * Alternatively, architecture specific version of __pv_queued_spin_unlock()
+ * can be defined.
*/
#include <asm/qspinlock_paravirt.h>
+#ifndef __pv_queued_spin_unlock
+__visible void __pv_queued_spin_unlock(struct qspinlock *lock)
+{
+ struct __qspinlock *l = (void *)lock;
+ u8 locked;
+
+ /*
+ * 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.
+ */
+ locked = cmpxchg(&l->locked, _Q_LOCKED_VAL, 0);
+ if (likely(locked == _Q_LOCKED_VAL))
+ return;
+
+ __pv_queued_spin_unlock_slowpath(lock, locked);
+}
+#endif /* __pv_queued_spin_unlock */
diff --git a/kernel/locking/qspinlock_stat.h b/kernel/locking/qspinlock_stat.h
new file mode 100644
index 000000000000..640dcecdd1df
--- /dev/null
+++ b/kernel/locking/qspinlock_stat.h
@@ -0,0 +1,300 @@
+/*
+ * 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.
+ *
+ * Authors: Waiman Long <waiman.long@hpe.com>
+ */
+
+/*
+ * When queued spinlock statistical counters are enabled, the following
+ * debugfs files will be created for reporting the counter values:
+ *
+ * <debugfs>/qlockstat/
+ * pv_hash_hops - average # of hops per hashing operation
+ * pv_kick_unlock - # of vCPU kicks issued at unlock time
+ * pv_kick_wake - # of vCPU kicks used for computing pv_latency_wake
+ * pv_latency_kick - average latency (ns) of vCPU kick operation
+ * pv_latency_wake - average latency (ns) from vCPU kick to wakeup
+ * pv_lock_stealing - # of lock stealing operations
+ * pv_spurious_wakeup - # of spurious wakeups
+ * pv_wait_again - # of vCPU wait's that happened after a vCPU kick
+ * pv_wait_early - # of early vCPU wait's
+ * pv_wait_head - # of vCPU wait's at the queue head
+ * pv_wait_node - # of vCPU wait's at a non-head queue node
+ *
+ * Writing to the "reset_counters" file will reset all the above counter
+ * values.
+ *
+ * These statistical counters are implemented as per-cpu variables which are
+ * summed and computed whenever the corresponding debugfs files are read. This
+ * minimizes added overhead making the counters usable even in a production
+ * environment.
+ *
+ * There may be slight difference between pv_kick_wake and pv_kick_unlock.
+ */
+enum qlock_stats {
+ qstat_pv_hash_hops,
+ qstat_pv_kick_unlock,
+ qstat_pv_kick_wake,
+ qstat_pv_latency_kick,
+ qstat_pv_latency_wake,
+ qstat_pv_lock_stealing,
+ qstat_pv_spurious_wakeup,
+ qstat_pv_wait_again,
+ qstat_pv_wait_early,
+ qstat_pv_wait_head,
+ qstat_pv_wait_node,
+ qstat_num, /* Total number of statistical counters */
+ qstat_reset_cnts = qstat_num,
+};
+
+#ifdef CONFIG_QUEUED_LOCK_STAT
+/*
+ * Collect pvqspinlock statistics
+ */
+#include <linux/debugfs.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+
+static const char * const qstat_names[qstat_num + 1] = {
+ [qstat_pv_hash_hops] = "pv_hash_hops",
+ [qstat_pv_kick_unlock] = "pv_kick_unlock",
+ [qstat_pv_kick_wake] = "pv_kick_wake",
+ [qstat_pv_spurious_wakeup] = "pv_spurious_wakeup",
+ [qstat_pv_latency_kick] = "pv_latency_kick",
+ [qstat_pv_latency_wake] = "pv_latency_wake",
+ [qstat_pv_lock_stealing] = "pv_lock_stealing",
+ [qstat_pv_wait_again] = "pv_wait_again",
+ [qstat_pv_wait_early] = "pv_wait_early",
+ [qstat_pv_wait_head] = "pv_wait_head",
+ [qstat_pv_wait_node] = "pv_wait_node",
+ [qstat_reset_cnts] = "reset_counters",
+};
+
+/*
+ * Per-cpu counters
+ */
+static DEFINE_PER_CPU(unsigned long, qstats[qstat_num]);
+static DEFINE_PER_CPU(u64, pv_kick_time);
+
+/*
+ * Function to read and return the qlock statistical counter values
+ *
+ * The following counters are handled specially:
+ * 1. qstat_pv_latency_kick
+ * Average kick latency (ns) = pv_latency_kick/pv_kick_unlock
+ * 2. qstat_pv_latency_wake
+ * Average wake latency (ns) = pv_latency_wake/pv_kick_wake
+ * 3. qstat_pv_hash_hops
+ * Average hops/hash = pv_hash_hops/pv_kick_unlock
+ */
+static ssize_t qstat_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[64];
+ int cpu, counter, len;
+ u64 stat = 0, kicks = 0;
+
+ /*
+ * Get the counter ID stored in file->f_inode->i_private
+ */
+ if (!file->f_inode) {
+ WARN_ON_ONCE(1);
+ return -EBADF;
+ }
+ counter = (long)(file->f_inode->i_private);
+
+ if (counter >= qstat_num)
+ return -EBADF;
+
+ for_each_possible_cpu(cpu) {
+ stat += per_cpu(qstats[counter], cpu);
+ /*
+ * Need to sum additional counter for some of them
+ */
+ switch (counter) {
+
+ case qstat_pv_latency_kick:
+ case qstat_pv_hash_hops:
+ kicks += per_cpu(qstats[qstat_pv_kick_unlock], cpu);
+ break;
+
+ case qstat_pv_latency_wake:
+ kicks += per_cpu(qstats[qstat_pv_kick_wake], cpu);
+ break;
+ }
+ }
+
+ if (counter == qstat_pv_hash_hops) {
+ u64 frac;
+
+ frac = 100ULL * do_div(stat, kicks);
+ frac = DIV_ROUND_CLOSEST_ULL(frac, kicks);
+
+ /*
+ * Return a X.XX decimal number
+ */
+ len = snprintf(buf, sizeof(buf) - 1, "%llu.%02llu\n", stat, frac);
+ } else {
+ /*
+ * Round to the nearest ns
+ */
+ if ((counter == qstat_pv_latency_kick) ||
+ (counter == qstat_pv_latency_wake)) {
+ stat = 0;
+ if (kicks)
+ stat = DIV_ROUND_CLOSEST_ULL(stat, kicks);
+ }
+ len = snprintf(buf, sizeof(buf) - 1, "%llu\n", stat);
+ }
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+/*
+ * Function to handle write request
+ *
+ * When counter = reset_cnts, reset all the counter values.
+ * Since the counter updates aren't atomic, the resetting is done twice
+ * to make sure that the counters are very likely to be all cleared.
+ */
+static ssize_t qstat_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int cpu;
+
+ /*
+ * Get the counter ID stored in file->f_inode->i_private
+ */
+ if (!file->f_inode) {
+ WARN_ON_ONCE(1);
+ return -EBADF;
+ }
+ if ((long)(file->f_inode->i_private) != qstat_reset_cnts)
+ return count;
+
+ for_each_possible_cpu(cpu) {
+ int i;
+ unsigned long *ptr = per_cpu_ptr(qstats, cpu);
+
+ for (i = 0 ; i < qstat_num; i++)
+ WRITE_ONCE(ptr[i], 0);
+ for (i = 0 ; i < qstat_num; i++)
+ WRITE_ONCE(ptr[i], 0);
+ }
+ return count;
+}
+
+/*
+ * Debugfs data structures
+ */
+static const struct file_operations fops_qstat = {
+ .read = qstat_read,
+ .write = qstat_write,
+ .llseek = default_llseek,
+};
+
+/*
+ * Initialize debugfs for the qspinlock statistical counters
+ */
+static int __init init_qspinlock_stat(void)
+{
+ struct dentry *d_qstat = debugfs_create_dir("qlockstat", NULL);
+ int i;
+
+ if (!d_qstat) {
+ pr_warn("Could not create 'qlockstat' debugfs directory\n");
+ return 0;
+ }
+
+ /*
+ * Create the debugfs files
+ *
+ * As reading from and writing to the stat files can be slow, only
+ * root is allowed to do the read/write to limit impact to system
+ * performance.
+ */
+ for (i = 0; i < qstat_num; i++)
+ debugfs_create_file(qstat_names[i], 0400, d_qstat,
+ (void *)(long)i, &fops_qstat);
+
+ debugfs_create_file(qstat_names[qstat_reset_cnts], 0200, d_qstat,
+ (void *)(long)qstat_reset_cnts, &fops_qstat);
+ return 0;
+}
+fs_initcall(init_qspinlock_stat);
+
+/*
+ * Increment the PV qspinlock statistical counters
+ */
+static inline void qstat_inc(enum qlock_stats stat, bool cond)
+{
+ if (cond)
+ this_cpu_inc(qstats[stat]);
+}
+
+/*
+ * PV hash hop count
+ */
+static inline void qstat_hop(int hopcnt)
+{
+ this_cpu_add(qstats[qstat_pv_hash_hops], hopcnt);
+}
+
+/*
+ * Replacement function for pv_kick()
+ */
+static inline void __pv_kick(int cpu)
+{
+ u64 start = sched_clock();
+
+ per_cpu(pv_kick_time, cpu) = start;
+ pv_kick(cpu);
+ this_cpu_add(qstats[qstat_pv_latency_kick], sched_clock() - start);
+}
+
+/*
+ * Replacement function for pv_wait()
+ */
+static inline void __pv_wait(u8 *ptr, u8 val)
+{
+ u64 *pkick_time = this_cpu_ptr(&pv_kick_time);
+
+ *pkick_time = 0;
+ pv_wait(ptr, val);
+ if (*pkick_time) {
+ this_cpu_add(qstats[qstat_pv_latency_wake],
+ sched_clock() - *pkick_time);
+ qstat_inc(qstat_pv_kick_wake, true);
+ }
+}
+
+#define pv_kick(c) __pv_kick(c)
+#define pv_wait(p, v) __pv_wait(p, v)
+
+/*
+ * PV unfair trylock count tracking function
+ */
+static inline int qstat_spin_steal_lock(struct qspinlock *lock)
+{
+ int ret = pv_queued_spin_steal_lock(lock);
+
+ qstat_inc(qstat_pv_lock_stealing, ret);
+ return ret;
+}
+#undef queued_spin_trylock
+#define queued_spin_trylock(l) qstat_spin_steal_lock(l)
+
+#else /* CONFIG_QUEUED_LOCK_STAT */
+
+static inline void qstat_inc(enum qlock_stats stat, bool cond) { }
+static inline void qstat_hop(int hopcnt) { }
+
+#endif /* CONFIG_QUEUED_LOCK_STAT */
diff --git a/kernel/module.c b/kernel/module.c
index 8f051a106676..8358f4697c0c 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -80,15 +80,6 @@
# define debug_align(X) (X)
#endif
-/*
- * Given BASE and SIZE this macro calculates the number of pages the
- * memory regions occupies
- */
-#define MOD_NUMBER_OF_PAGES(BASE, SIZE) (((SIZE) > 0) ? \
- (PFN_DOWN((unsigned long)(BASE) + (SIZE) - 1) - \
- PFN_DOWN((unsigned long)BASE) + 1) \
- : (0UL))
-
/* If this is set, the section belongs in the init part of the module */
#define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
@@ -108,13 +99,6 @@ static LIST_HEAD(modules);
* Use a latched RB-tree for __module_address(); this allows us to use
* RCU-sched lookups of the address from any context.
*
- * Because modules have two address ranges: init and core, we need two
- * latch_tree_nodes entries. Therefore we need the back-pointer from
- * mod_tree_node.
- *
- * Because init ranges are short lived we mark them unlikely and have placed
- * them outside the critical cacheline in struct module.
- *
* This is conditional on PERF_EVENTS || TRACING because those can really hit
* __module_address() hard by doing a lot of stack unwinding; potentially from
* NMI context.
@@ -122,24 +106,16 @@ static LIST_HEAD(modules);
static __always_inline unsigned long __mod_tree_val(struct latch_tree_node *n)
{
- struct mod_tree_node *mtn = container_of(n, struct mod_tree_node, node);
- struct module *mod = mtn->mod;
+ struct module_layout *layout = container_of(n, struct module_layout, mtn.node);
- if (unlikely(mtn == &mod->mtn_init))
- return (unsigned long)mod->module_init;
-
- return (unsigned long)mod->module_core;
+ return (unsigned long)layout->base;
}
static __always_inline unsigned long __mod_tree_size(struct latch_tree_node *n)
{
- struct mod_tree_node *mtn = container_of(n, struct mod_tree_node, node);
- struct module *mod = mtn->mod;
-
- if (unlikely(mtn == &mod->mtn_init))
- return (unsigned long)mod->init_size;
+ struct module_layout *layout = container_of(n, struct module_layout, mtn.node);
- return (unsigned long)mod->core_size;
+ return (unsigned long)layout->size;
}
static __always_inline bool
@@ -197,23 +173,23 @@ static void __mod_tree_remove(struct mod_tree_node *node)
*/
static void mod_tree_insert(struct module *mod)
{
- mod->mtn_core.mod = mod;
- mod->mtn_init.mod = mod;
+ mod->core_layout.mtn.mod = mod;
+ mod->init_layout.mtn.mod = mod;
- __mod_tree_insert(&mod->mtn_core);
- if (mod->init_size)
- __mod_tree_insert(&mod->mtn_init);
+ __mod_tree_insert(&mod->core_layout.mtn);
+ if (mod->init_layout.size)
+ __mod_tree_insert(&mod->init_layout.mtn);
}
static void mod_tree_remove_init(struct module *mod)
{
- if (mod->init_size)
- __mod_tree_remove(&mod->mtn_init);
+ if (mod->init_layout.size)
+ __mod_tree_remove(&mod->init_layout.mtn);
}
static void mod_tree_remove(struct module *mod)
{
- __mod_tree_remove(&mod->mtn_core);
+ __mod_tree_remove(&mod->core_layout.mtn);
mod_tree_remove_init(mod);
}
@@ -267,9 +243,9 @@ static void __mod_update_bounds(void *base, unsigned int size)
static void mod_update_bounds(struct module *mod)
{
- __mod_update_bounds(mod->module_core, mod->core_size);
- if (mod->init_size)
- __mod_update_bounds(mod->module_init, mod->init_size);
+ __mod_update_bounds(mod->core_layout.base, mod->core_layout.size);
+ if (mod->init_layout.size)
+ __mod_update_bounds(mod->init_layout.base, mod->init_layout.size);
}
#ifdef CONFIG_KGDB_KDB
@@ -1214,7 +1190,7 @@ struct module_attribute module_uevent =
static ssize_t show_coresize(struct module_attribute *mattr,
struct module_kobject *mk, char *buffer)
{
- return sprintf(buffer, "%u\n", mk->mod->core_size);
+ return sprintf(buffer, "%u\n", mk->mod->core_layout.size);
}
static struct module_attribute modinfo_coresize =
@@ -1223,7 +1199,7 @@ static struct module_attribute modinfo_coresize =
static ssize_t show_initsize(struct module_attribute *mattr,
struct module_kobject *mk, char *buffer)
{
- return sprintf(buffer, "%u\n", mk->mod->init_size);
+ return sprintf(buffer, "%u\n", mk->mod->init_layout.size);
}
static struct module_attribute modinfo_initsize =
@@ -1873,64 +1849,75 @@ static void mod_sysfs_teardown(struct module *mod)
/*
* LKM RO/NX protection: protect module's text/ro-data
* from modification and any data from execution.
+ *
+ * General layout of module is:
+ * [text] [read-only-data] [writable data]
+ * text_size -----^ ^ ^
+ * ro_size ------------------------| |
+ * size -------------------------------------------|
+ *
+ * These values are always page-aligned (as is base)
*/
-void set_page_attributes(void *start, void *end, int (*set)(unsigned long start, int num_pages))
+static void frob_text(const struct module_layout *layout,
+ int (*set_memory)(unsigned long start, int num_pages))
{
- unsigned long begin_pfn = PFN_DOWN((unsigned long)start);
- unsigned long end_pfn = PFN_DOWN((unsigned long)end);
+ BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
+ BUG_ON((unsigned long)layout->text_size & (PAGE_SIZE-1));
+ set_memory((unsigned long)layout->base,
+ layout->text_size >> PAGE_SHIFT);
+}
- if (end_pfn > begin_pfn)
- set(begin_pfn << PAGE_SHIFT, end_pfn - begin_pfn);
+static void frob_rodata(const struct module_layout *layout,
+ int (*set_memory)(unsigned long start, int num_pages))
+{
+ BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
+ BUG_ON((unsigned long)layout->text_size & (PAGE_SIZE-1));
+ BUG_ON((unsigned long)layout->ro_size & (PAGE_SIZE-1));
+ set_memory((unsigned long)layout->base + layout->text_size,
+ (layout->ro_size - layout->text_size) >> PAGE_SHIFT);
}
-static void set_section_ro_nx(void *base,
- unsigned long text_size,
- unsigned long ro_size,
- unsigned long total_size)
+static void frob_writable_data(const struct module_layout *layout,
+ int (*set_memory)(unsigned long start, int num_pages))
{
- /* begin and end PFNs of the current subsection */
- unsigned long begin_pfn;
- unsigned long end_pfn;
+ BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
+ BUG_ON((unsigned long)layout->ro_size & (PAGE_SIZE-1));
+ BUG_ON((unsigned long)layout->size & (PAGE_SIZE-1));
+ set_memory((unsigned long)layout->base + layout->ro_size,
+ (layout->size - layout->ro_size) >> PAGE_SHIFT);
+}
- /*
- * Set RO for module text and RO-data:
- * - Always protect first page.
- * - Do not protect last partial page.
- */
- if (ro_size > 0)
- set_page_attributes(base, base + ro_size, set_memory_ro);
+/* livepatching wants to disable read-only so it can frob module. */
+void module_disable_ro(const struct module *mod)
+{
+ frob_text(&mod->core_layout, set_memory_rw);
+ frob_rodata(&mod->core_layout, set_memory_rw);
+ frob_text(&mod->init_layout, set_memory_rw);
+ frob_rodata(&mod->init_layout, set_memory_rw);
+}
- /*
- * Set NX permissions for module data:
- * - Do not protect first partial page.
- * - Always protect last page.
- */
- if (total_size > text_size) {
- begin_pfn = PFN_UP((unsigned long)base + text_size);
- end_pfn = PFN_UP((unsigned long)base + total_size);
- if (end_pfn > begin_pfn)
- set_memory_nx(begin_pfn << PAGE_SHIFT, end_pfn - begin_pfn);
- }
+void module_enable_ro(const struct module *mod)
+{
+ frob_text(&mod->core_layout, set_memory_ro);
+ frob_rodata(&mod->core_layout, set_memory_ro);
+ frob_text(&mod->init_layout, set_memory_ro);
+ frob_rodata(&mod->init_layout, set_memory_ro);
}
-static void unset_module_core_ro_nx(struct module *mod)
+static void module_enable_nx(const struct module *mod)
{
- set_page_attributes(mod->module_core + mod->core_text_size,
- mod->module_core + mod->core_size,
- set_memory_x);
- set_page_attributes(mod->module_core,
- mod->module_core + mod->core_ro_size,
- set_memory_rw);
+ frob_rodata(&mod->core_layout, set_memory_nx);
+ frob_writable_data(&mod->core_layout, set_memory_nx);
+ frob_rodata(&mod->init_layout, set_memory_nx);
+ frob_writable_data(&mod->init_layout, set_memory_nx);
}
-static void unset_module_init_ro_nx(struct module *mod)
+static void module_disable_nx(const struct module *mod)
{
- set_page_attributes(mod->module_init + mod->init_text_size,
- mod->module_init + mod->init_size,
- set_memory_x);
- set_page_attributes(mod->module_init,
- mod->module_init + mod->init_ro_size,
- set_memory_rw);
+ frob_rodata(&mod->core_layout, set_memory_x);
+ frob_writable_data(&mod->core_layout, set_memory_x);
+ frob_rodata(&mod->init_layout, set_memory_x);
+ frob_writable_data(&mod->init_layout, set_memory_x);
}
/* Iterate through all modules and set each module's text as RW */
@@ -1942,16 +1929,9 @@ void set_all_modules_text_rw(void)
list_for_each_entry_rcu(mod, &modules, list) {
if (mod->state == MODULE_STATE_UNFORMED)
continue;
- if ((mod->module_core) && (mod->core_text_size)) {
- set_page_attributes(mod->module_core,
- mod->module_core + mod->core_text_size,
- set_memory_rw);
- }
- if ((mod->module_init) && (mod->init_text_size)) {
- set_page_attributes(mod->module_init,
- mod->module_init + mod->init_text_size,
- set_memory_rw);
- }
+
+ frob_text(&mod->core_layout, set_memory_rw);
+ frob_text(&mod->init_layout, set_memory_rw);
}
mutex_unlock(&module_mutex);
}
@@ -1965,23 +1945,25 @@ void set_all_modules_text_ro(void)
list_for_each_entry_rcu(mod, &modules, list) {
if (mod->state == MODULE_STATE_UNFORMED)
continue;
- if ((mod->module_core) && (mod->core_text_size)) {
- set_page_attributes(mod->module_core,
- mod->module_core + mod->core_text_size,
- set_memory_ro);
- }
- if ((mod->module_init) && (mod->init_text_size)) {
- set_page_attributes(mod->module_init,
- mod->module_init + mod->init_text_size,
- set_memory_ro);
- }
+
+ frob_text(&mod->core_layout, set_memory_ro);
+ frob_text(&mod->init_layout, set_memory_ro);
}
mutex_unlock(&module_mutex);
}
+
+static void disable_ro_nx(const struct module_layout *layout)
+{
+ frob_text(layout, set_memory_rw);
+ frob_rodata(layout, set_memory_rw);
+ frob_rodata(layout, set_memory_x);
+ frob_writable_data(layout, set_memory_x);
+}
+
#else
-static inline void set_section_ro_nx(void *base, unsigned long text_size, unsigned long ro_size, unsigned long total_size) { }
-static void unset_module_core_ro_nx(struct module *mod) { }
-static void unset_module_init_ro_nx(struct module *mod) { }
+static void disable_ro_nx(const struct module_layout *layout) { }
+static void module_enable_nx(const struct module *mod) { }
+static void module_disable_nx(const struct module *mod) { }
#endif
void __weak module_memfree(void *module_region)
@@ -2033,19 +2015,19 @@ static void free_module(struct module *mod)
synchronize_sched();
mutex_unlock(&module_mutex);
- /* This may be NULL, but that's OK */
- unset_module_init_ro_nx(mod);
+ /* This may be empty, but that's OK */
+ disable_ro_nx(&mod->init_layout);
module_arch_freeing_init(mod);
- module_memfree(mod->module_init);
+ module_memfree(mod->init_layout.base);
kfree(mod->args);
percpu_modfree(mod);
/* Free lock-classes; relies on the preceding sync_rcu(). */
- lockdep_free_key_range(mod->module_core, mod->core_size);
+ lockdep_free_key_range(mod->core_layout.base, mod->core_layout.size);
/* Finally, free the core (containing the module structure) */
- unset_module_core_ro_nx(mod);
- module_memfree(mod->module_core);
+ disable_ro_nx(&mod->core_layout);
+ module_memfree(mod->core_layout.base);
#ifdef CONFIG_MPU
update_protections(current->mm);
@@ -2248,20 +2230,20 @@ static void layout_sections(struct module *mod, struct load_info *info)
|| s->sh_entsize != ~0UL
|| strstarts(sname, ".init"))
continue;
- s->sh_entsize = get_offset(mod, &mod->core_size, s, i);
+ s->sh_entsize = get_offset(mod, &mod->core_layout.size, s, i);
pr_debug("\t%s\n", sname);
}
switch (m) {
case 0: /* executable */
- mod->core_size = debug_align(mod->core_size);
- mod->core_text_size = mod->core_size;
+ mod->core_layout.size = debug_align(mod->core_layout.size);
+ mod->core_layout.text_size = mod->core_layout.size;
break;
case 1: /* RO: text and ro-data */
- mod->core_size = debug_align(mod->core_size);
- mod->core_ro_size = mod->core_size;
+ mod->core_layout.size = debug_align(mod->core_layout.size);
+ mod->core_layout.ro_size = mod->core_layout.size;
break;
case 3: /* whole core */
- mod->core_size = debug_align(mod->core_size);
+ mod->core_layout.size = debug_align(mod->core_layout.size);
break;
}
}
@@ -2277,21 +2259,21 @@ static void layout_sections(struct module *mod, struct load_info *info)
|| s->sh_entsize != ~0UL
|| !strstarts(sname, ".init"))
continue;
- s->sh_entsize = (get_offset(mod, &mod->init_size, s, i)
+ s->sh_entsize = (get_offset(mod, &mod->init_layout.size, s, i)
| INIT_OFFSET_MASK);
pr_debug("\t%s\n", sname);
}
switch (m) {
case 0: /* executable */
- mod->init_size = debug_align(mod->init_size);
- mod->init_text_size = mod->init_size;
+ mod->init_layout.size = debug_align(mod->init_layout.size);
+ mod->init_layout.text_size = mod->init_layout.size;
break;
case 1: /* RO: text and ro-data */
- mod->init_size = debug_align(mod->init_size);
- mod->init_ro_size = mod->init_size;
+ mod->init_layout.size = debug_align(mod->init_layout.size);
+ mod->init_layout.ro_size = mod->init_layout.size;
break;
case 3: /* whole init */
- mod->init_size = debug_align(mod->init_size);
+ mod->init_layout.size = debug_align(mod->init_layout.size);
break;
}
}
@@ -2401,7 +2383,7 @@ static char elf_type(const Elf_Sym *sym, const struct load_info *info)
}
if (sym->st_shndx == SHN_UNDEF)
return 'U';
- if (sym->st_shndx == SHN_ABS)
+ if (sym->st_shndx == SHN_ABS || sym->st_shndx == info->index.pcpu)
return 'a';
if (sym->st_shndx >= SHN_LORESERVE)
return '?';
@@ -2430,7 +2412,7 @@ static char elf_type(const Elf_Sym *sym, const struct load_info *info)
}
static bool is_core_symbol(const Elf_Sym *src, const Elf_Shdr *sechdrs,
- unsigned int shnum)
+ unsigned int shnum, unsigned int pcpundx)
{
const Elf_Shdr *sec;
@@ -2439,6 +2421,11 @@ static bool is_core_symbol(const Elf_Sym *src, const Elf_Shdr *sechdrs,
|| !src->st_name)
return false;
+#ifdef CONFIG_KALLSYMS_ALL
+ if (src->st_shndx == pcpundx)
+ return true;
+#endif
+
sec = sechdrs + src->st_shndx;
if (!(sec->sh_flags & SHF_ALLOC)
#ifndef CONFIG_KALLSYMS_ALL
@@ -2466,7 +2453,7 @@ static void layout_symtab(struct module *mod, struct load_info *info)
/* Put symbol section at end of init part of module. */
symsect->sh_flags |= SHF_ALLOC;
- symsect->sh_entsize = get_offset(mod, &mod->init_size, symsect,
+ symsect->sh_entsize = get_offset(mod, &mod->init_layout.size, symsect,
info->index.sym) | INIT_OFFSET_MASK;
pr_debug("\t%s\n", info->secstrings + symsect->sh_name);
@@ -2476,23 +2463,24 @@ static void layout_symtab(struct module *mod, struct load_info *info)
/* Compute total space required for the core symbols' strtab. */
for (ndst = i = 0; i < nsrc; i++) {
if (i == 0 ||
- is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum)) {
+ is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum,
+ info->index.pcpu)) {
strtab_size += strlen(&info->strtab[src[i].st_name])+1;
ndst++;
}
}
/* Append room for core symbols at end of core part. */
- info->symoffs = ALIGN(mod->core_size, symsect->sh_addralign ?: 1);
- info->stroffs = mod->core_size = info->symoffs + ndst * sizeof(Elf_Sym);
- mod->core_size += strtab_size;
- mod->core_size = debug_align(mod->core_size);
+ info->symoffs = ALIGN(mod->core_layout.size, symsect->sh_addralign ?: 1);
+ info->stroffs = mod->core_layout.size = info->symoffs + ndst * sizeof(Elf_Sym);
+ mod->core_layout.size += strtab_size;
+ mod->core_layout.size = debug_align(mod->core_layout.size);
/* Put string table section at end of init part of module. */
strsect->sh_flags |= SHF_ALLOC;
- strsect->sh_entsize = get_offset(mod, &mod->init_size, strsect,
+ strsect->sh_entsize = get_offset(mod, &mod->init_layout.size, strsect,
info->index.str) | INIT_OFFSET_MASK;
- mod->init_size = debug_align(mod->init_size);
+ mod->init_layout.size = debug_align(mod->init_layout.size);
pr_debug("\t%s\n", info->secstrings + strsect->sh_name);
}
@@ -2513,12 +2501,13 @@ static void add_kallsyms(struct module *mod, const struct load_info *info)
for (i = 0; i < mod->num_symtab; i++)
mod->symtab[i].st_info = elf_type(&mod->symtab[i], info);
- mod->core_symtab = dst = mod->module_core + info->symoffs;
- mod->core_strtab = s = mod->module_core + info->stroffs;
+ mod->core_symtab = dst = mod->core_layout.base + info->symoffs;
+ mod->core_strtab = s = mod->core_layout.base + info->stroffs;
src = mod->symtab;
for (ndst = i = 0; i < mod->num_symtab; i++) {
if (i == 0 ||
- is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum)) {
+ is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum,
+ info->index.pcpu)) {
dst[ndst] = src[i];
dst[ndst++].st_name = s - mod->core_strtab;
s += strlcpy(s, &mod->strtab[src[i].st_name],
@@ -2964,7 +2953,7 @@ static int move_module(struct module *mod, struct load_info *info)
void *ptr;
/* Do the allocs. */
- ptr = module_alloc(mod->core_size);
+ ptr = module_alloc(mod->core_layout.size);
/*
* The pointer to this block is stored in the module structure
* which is inside the block. Just mark it as not being a
@@ -2974,11 +2963,11 @@ static int move_module(struct module *mod, struct load_info *info)
if (!ptr)
return -ENOMEM;
- memset(ptr, 0, mod->core_size);
- mod->module_core = ptr;
+ memset(ptr, 0, mod->core_layout.size);
+ mod->core_layout.base = ptr;
- if (mod->init_size) {
- ptr = module_alloc(mod->init_size);
+ if (mod->init_layout.size) {
+ ptr = module_alloc(mod->init_layout.size);
/*
* The pointer to this block is stored in the module structure
* which is inside the block. This block doesn't need to be
@@ -2987,13 +2976,13 @@ static int move_module(struct module *mod, struct load_info *info)
*/
kmemleak_ignore(ptr);
if (!ptr) {
- module_memfree(mod->module_core);
+ module_memfree(mod->core_layout.base);
return -ENOMEM;
}
- memset(ptr, 0, mod->init_size);
- mod->module_init = ptr;
+ memset(ptr, 0, mod->init_layout.size);
+ mod->init_layout.base = ptr;
} else
- mod->module_init = NULL;
+ mod->init_layout.base = NULL;
/* Transfer each section which specifies SHF_ALLOC */
pr_debug("final section addresses:\n");
@@ -3005,10 +2994,10 @@ static int move_module(struct module *mod, struct load_info *info)
continue;
if (shdr->sh_entsize & INIT_OFFSET_MASK)
- dest = mod->module_init
+ dest = mod->init_layout.base
+ (shdr->sh_entsize & ~INIT_OFFSET_MASK);
else
- dest = mod->module_core + shdr->sh_entsize;
+ dest = mod->core_layout.base + shdr->sh_entsize;
if (shdr->sh_type != SHT_NOBITS)
memcpy(dest, (void *)shdr->sh_addr, shdr->sh_size);
@@ -3070,12 +3059,12 @@ static void flush_module_icache(const struct module *mod)
* Do it before processing of module parameters, so the module
* can provide parameter accessor functions of its own.
*/
- if (mod->module_init)
- flush_icache_range((unsigned long)mod->module_init,
- (unsigned long)mod->module_init
- + mod->init_size);
- flush_icache_range((unsigned long)mod->module_core,
- (unsigned long)mod->module_core + mod->core_size);
+ if (mod->init_layout.base)
+ flush_icache_range((unsigned long)mod->init_layout.base,
+ (unsigned long)mod->init_layout.base
+ + mod->init_layout.size);
+ flush_icache_range((unsigned long)mod->core_layout.base,
+ (unsigned long)mod->core_layout.base + mod->core_layout.size);
set_fs(old_fs);
}
@@ -3133,8 +3122,8 @@ static void module_deallocate(struct module *mod, struct load_info *info)
{
percpu_modfree(mod);
module_arch_freeing_init(mod);
- module_memfree(mod->module_init);
- module_memfree(mod->module_core);
+ module_memfree(mod->init_layout.base);
+ module_memfree(mod->core_layout.base);
}
int __weak module_finalize(const Elf_Ehdr *hdr,
@@ -3221,7 +3210,7 @@ static noinline int do_init_module(struct module *mod)
ret = -ENOMEM;
goto fail;
}
- freeinit->module_init = mod->module_init;
+ freeinit->module_init = mod->init_layout.base;
/*
* We want to find out whether @mod uses async during init. Clear
@@ -3279,12 +3268,12 @@ static noinline int do_init_module(struct module *mod)
mod->strtab = mod->core_strtab;
#endif
mod_tree_remove_init(mod);
- unset_module_init_ro_nx(mod);
+ disable_ro_nx(&mod->init_layout);
module_arch_freeing_init(mod);
- mod->module_init = NULL;
- mod->init_size = 0;
- mod->init_ro_size = 0;
- mod->init_text_size = 0;
+ mod->init_layout.base = NULL;
+ mod->init_layout.size = 0;
+ mod->init_layout.ro_size = 0;
+ mod->init_layout.text_size = 0;
/*
* We want to free module_init, but be aware that kallsyms may be
* walking this with preempt disabled. In all the failure paths, we
@@ -3373,17 +3362,9 @@ static int complete_formation(struct module *mod, struct load_info *info)
/* This relies on module_mutex for list integrity. */
module_bug_finalize(info->hdr, info->sechdrs, mod);
- /* Set RO and NX regions for core */
- set_section_ro_nx(mod->module_core,
- mod->core_text_size,
- mod->core_ro_size,
- mod->core_size);
-
- /* Set RO and NX regions for init */
- set_section_ro_nx(mod->module_init,
- mod->init_text_size,
- mod->init_ro_size,
- mod->init_size);
+ /* Set RO and NX regions */
+ module_enable_ro(mod);
+ module_enable_nx(mod);
/* Mark state as coming so strong_try_module_get() ignores us,
* but kallsyms etc. can see us. */
@@ -3548,8 +3529,8 @@ static int load_module(struct load_info *info, const char __user *uargs,
MODULE_STATE_GOING, mod);
/* we can't deallocate the module until we clear memory protection */
- unset_module_init_ro_nx(mod);
- unset_module_core_ro_nx(mod);
+ module_disable_ro(mod);
+ module_disable_nx(mod);
ddebug_cleanup:
dynamic_debug_remove(info->debug);
@@ -3571,8 +3552,14 @@ static int load_module(struct load_info *info, const char __user *uargs,
synchronize_sched();
mutex_unlock(&module_mutex);
free_module:
+ /*
+ * Ftrace needs to clean up what it initialized.
+ * This does nothing if ftrace_module_init() wasn't called,
+ * but it must be called outside of module_mutex.
+ */
+ ftrace_release_mod(mod);
/* Free lock-classes; relies on the preceding sync_rcu() */
- lockdep_free_key_range(mod->module_core, mod->core_size);
+ lockdep_free_key_range(mod->core_layout.base, mod->core_layout.size);
module_deallocate(mod, info);
free_copy:
@@ -3650,9 +3637,9 @@ static const char *get_ksymbol(struct module *mod,
/* At worse, next value is at end of module */
if (within_module_init(addr, mod))
- nextval = (unsigned long)mod->module_init+mod->init_text_size;
+ nextval = (unsigned long)mod->init_layout.base+mod->init_layout.text_size;
else
- nextval = (unsigned long)mod->module_core+mod->core_text_size;
+ nextval = (unsigned long)mod->core_layout.base+mod->core_layout.text_size;
/* Scan for closest preceding symbol, and next symbol. (ELF
starts real symbols at 1). */
@@ -3899,7 +3886,7 @@ static int m_show(struct seq_file *m, void *p)
return 0;
seq_printf(m, "%s %u",
- mod->name, mod->init_size + mod->core_size);
+ mod->name, mod->init_layout.size + mod->core_layout.size);
print_unload_info(m, mod);
/* Informative for users. */
@@ -3908,7 +3895,7 @@ static int m_show(struct seq_file *m, void *p)
mod->state == MODULE_STATE_COMING ? "Loading" :
"Live");
/* Used by oprofile and other similar tools. */
- seq_printf(m, " 0x%pK", mod->module_core);
+ seq_printf(m, " 0x%pK", mod->core_layout.base);
/* Taints info */
if (mod->taints)
@@ -4051,8 +4038,8 @@ struct module *__module_text_address(unsigned long addr)
struct module *mod = __module_address(addr);
if (mod) {
/* Make sure it's within the text section. */
- if (!within(addr, mod->module_init, mod->init_text_size)
- && !within(addr, mod->module_core, mod->core_text_size))
+ if (!within(addr, mod->init_layout.base, mod->init_layout.text_size)
+ && !within(addr, mod->core_layout.base, mod->core_layout.text_size))
mod = NULL;
}
return mod;
diff --git a/kernel/panic.c b/kernel/panic.c
index 4b150bc0c6c1..b333380c6bb2 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -61,6 +61,17 @@ void __weak panic_smp_self_stop(void)
cpu_relax();
}
+/*
+ * Stop ourselves in NMI context if another CPU has already panicked. Arch code
+ * may override this to prepare for crash dumping, e.g. save regs info.
+ */
+void __weak nmi_panic_self_stop(struct pt_regs *regs)
+{
+ panic_smp_self_stop();
+}
+
+atomic_t panic_cpu = ATOMIC_INIT(PANIC_CPU_INVALID);
+
/**
* panic - halt the system
* @fmt: The text string to print
@@ -71,17 +82,17 @@ void __weak panic_smp_self_stop(void)
*/
void panic(const char *fmt, ...)
{
- static DEFINE_SPINLOCK(panic_lock);
static char buf[1024];
va_list args;
long i, i_next = 0;
int state = 0;
+ int old_cpu, this_cpu;
/*
* Disable local interrupts. This will prevent panic_smp_self_stop
* from deadlocking the first cpu that invokes the panic, since
* there is nothing to prevent an interrupt handler (that runs
- * after the panic_lock is acquired) from invoking panic again.
+ * after setting panic_cpu) from invoking panic() again.
*/
local_irq_disable();
@@ -94,8 +105,16 @@ void panic(const char *fmt, ...)
* multiple parallel invocations of panic, all other CPUs either
* stop themself or will wait until they are stopped by the 1st CPU
* with smp_send_stop().
+ *
+ * `old_cpu == PANIC_CPU_INVALID' means this is the 1st CPU which
+ * comes here, so go ahead.
+ * `old_cpu == this_cpu' means we came from nmi_panic() which sets
+ * panic_cpu to this CPU. In this case, this is also the 1st CPU.
*/
- if (!spin_trylock(&panic_lock))
+ this_cpu = raw_smp_processor_id();
+ old_cpu = atomic_cmpxchg(&panic_cpu, PANIC_CPU_INVALID, this_cpu);
+
+ if (old_cpu != PANIC_CPU_INVALID && old_cpu != this_cpu)
panic_smp_self_stop();
console_verbose();
@@ -117,9 +136,11 @@ void panic(const char *fmt, ...)
* everything else.
* If we want to run this after calling panic_notifiers, pass
* the "crash_kexec_post_notifiers" option to the kernel.
+ *
+ * Bypass the panic_cpu check and call __crash_kexec directly.
*/
if (!crash_kexec_post_notifiers)
- crash_kexec(NULL);
+ __crash_kexec(NULL);
/*
* Note smp_send_stop is the usual smp shutdown function, which
@@ -142,9 +163,11 @@ void panic(const char *fmt, ...)
* panic_notifiers and dumping kmsg before kdump.
* Note: since some panic_notifiers can make crashed kernel
* more unstable, it can increase risks of the kdump failure too.
+ *
+ * Bypass the panic_cpu check and call __crash_kexec directly.
*/
if (crash_kexec_post_notifiers)
- crash_kexec(NULL);
+ __crash_kexec(NULL);
bust_spinlocks(0);
diff --git a/kernel/pid.c b/kernel/pid.c
index ca368793808e..f4ad91b746f1 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -467,7 +467,7 @@ struct pid *get_task_pid(struct task_struct *task, enum pid_type type)
rcu_read_lock();
if (type != PIDTYPE_PID)
task = task->group_leader;
- pid = get_pid(task->pids[type].pid);
+ pid = get_pid(rcu_dereference(task->pids[type].pid));
rcu_read_unlock();
return pid;
}
@@ -528,7 +528,7 @@ pid_t __task_pid_nr_ns(struct task_struct *task, enum pid_type type,
if (likely(pid_alive(task))) {
if (type != PIDTYPE_PID)
task = task->group_leader;
- nr = pid_nr_ns(task->pids[type].pid, ns);
+ nr = pid_nr_ns(rcu_dereference(task->pids[type].pid), ns);
}
rcu_read_unlock();
@@ -604,5 +604,5 @@ void __init pidmap_init(void)
atomic_dec(&init_pid_ns.pidmap[0].nr_free);
init_pid_ns.pid_cachep = KMEM_CACHE(pid,
- SLAB_HWCACHE_ALIGN | SLAB_PANIC);
+ SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_ACCOUNT);
}
diff --git a/kernel/power/main.c b/kernel/power/main.c
index b2dd4d999900..27946975eff0 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -280,13 +280,7 @@ static ssize_t pm_wakeup_irq_show(struct kobject *kobj,
return pm_wakeup_irq ? sprintf(buf, "%u\n", pm_wakeup_irq) : -ENODATA;
}
-static ssize_t pm_wakeup_irq_store(struct kobject *kobj,
- struct kobj_attribute *attr,
- const char *buf, size_t n)
-{
- return -EINVAL;
-}
-power_attr(pm_wakeup_irq);
+power_attr_ro(pm_wakeup_irq);
#else /* !CONFIG_PM_SLEEP_DEBUG */
static inline void pm_print_times_init(void) {}
@@ -564,14 +558,7 @@ static ssize_t pm_trace_dev_match_show(struct kobject *kobj,
return show_trace_dev_match(buf, PAGE_SIZE);
}
-static ssize_t
-pm_trace_dev_match_store(struct kobject *kobj, struct kobj_attribute *attr,
- const char *buf, size_t n)
-{
- return -EINVAL;
-}
-
-power_attr(pm_trace_dev_match);
+power_attr_ro(pm_trace_dev_match);
#endif /* CONFIG_PM_TRACE */
diff --git a/kernel/power/power.h b/kernel/power/power.h
index caadb566e82b..efe1b3b17c88 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -77,6 +77,15 @@ static struct kobj_attribute _name##_attr = { \
.store = _name##_store, \
}
+#define power_attr_ro(_name) \
+static struct kobj_attribute _name##_attr = { \
+ .attr = { \
+ .name = __stringify(_name), \
+ .mode = S_IRUGO, \
+ }, \
+ .show = _name##_show, \
+}
+
/* Preferred image size in bytes (default 500 MB) */
extern unsigned long image_size;
/* Size of memory reserved for drivers (default SPARE_PAGES x PAGE_SIZE) */
diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c
index d89328e260df..d2988d047d66 100644
--- a/kernel/rcu/rcutorture.c
+++ b/kernel/rcu/rcutorture.c
@@ -162,6 +162,27 @@ static int rcu_torture_writer_state;
#define RTWS_SYNC 7
#define RTWS_STUTTER 8
#define RTWS_STOPPING 9
+static const char * const rcu_torture_writer_state_names[] = {
+ "RTWS_FIXED_DELAY",
+ "RTWS_DELAY",
+ "RTWS_REPLACE",
+ "RTWS_DEF_FREE",
+ "RTWS_EXP_SYNC",
+ "RTWS_COND_GET",
+ "RTWS_COND_SYNC",
+ "RTWS_SYNC",
+ "RTWS_STUTTER",
+ "RTWS_STOPPING",
+};
+
+static const char *rcu_torture_writer_state_getname(void)
+{
+ unsigned int i = READ_ONCE(rcu_torture_writer_state);
+
+ if (i >= ARRAY_SIZE(rcu_torture_writer_state_names))
+ return "???";
+ return rcu_torture_writer_state_names[i];
+}
#if defined(MODULE) || defined(CONFIG_RCU_TORTURE_TEST_RUNNABLE)
#define RCUTORTURE_RUNNABLE_INIT 1
@@ -1307,7 +1328,8 @@ rcu_torture_stats_print(void)
rcutorture_get_gp_data(cur_ops->ttype,
&flags, &gpnum, &completed);
- pr_alert("??? Writer stall state %d g%lu c%lu f%#x\n",
+ pr_alert("??? Writer stall state %s(%d) g%lu c%lu f%#x\n",
+ rcu_torture_writer_state_getname(),
rcu_torture_writer_state,
gpnum, completed, flags);
show_rcu_gp_kthreads();
diff --git a/kernel/rcu/srcu.c b/kernel/rcu/srcu.c
index a63a1ea5a41b..9b9cdd549caa 100644
--- a/kernel/rcu/srcu.c
+++ b/kernel/rcu/srcu.c
@@ -489,7 +489,7 @@ static void __synchronize_srcu(struct srcu_struct *sp, int trycount)
*/
void synchronize_srcu(struct srcu_struct *sp)
{
- __synchronize_srcu(sp, rcu_gp_is_expedited()
+ __synchronize_srcu(sp, (rcu_gp_is_expedited() && !rcu_gp_is_normal())
? SYNCHRONIZE_SRCU_EXP_TRYCOUNT
: SYNCHRONIZE_SRCU_TRYCOUNT);
}
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index f07343b54fe5..e41dd4131f7a 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -68,10 +68,6 @@ MODULE_ALIAS("rcutree");
/* Data structures. */
-static struct lock_class_key rcu_node_class[RCU_NUM_LVLS];
-static struct lock_class_key rcu_fqs_class[RCU_NUM_LVLS];
-static struct lock_class_key rcu_exp_class[RCU_NUM_LVLS];
-
/*
* In order to export the rcu_state name to the tracing tools, it
* needs to be added in the __tracepoint_string section.
@@ -246,24 +242,17 @@ static int rcu_gp_in_progress(struct rcu_state *rsp)
*/
void rcu_sched_qs(void)
{
- unsigned long flags;
-
- if (__this_cpu_read(rcu_sched_data.cpu_no_qs.s)) {
- trace_rcu_grace_period(TPS("rcu_sched"),
- __this_cpu_read(rcu_sched_data.gpnum),
- TPS("cpuqs"));
- __this_cpu_write(rcu_sched_data.cpu_no_qs.b.norm, false);
- if (!__this_cpu_read(rcu_sched_data.cpu_no_qs.b.exp))
- return;
- local_irq_save(flags);
- if (__this_cpu_read(rcu_sched_data.cpu_no_qs.b.exp)) {
- __this_cpu_write(rcu_sched_data.cpu_no_qs.b.exp, false);
- rcu_report_exp_rdp(&rcu_sched_state,
- this_cpu_ptr(&rcu_sched_data),
- true);
- }
- local_irq_restore(flags);
- }
+ if (!__this_cpu_read(rcu_sched_data.cpu_no_qs.s))
+ return;
+ trace_rcu_grace_period(TPS("rcu_sched"),
+ __this_cpu_read(rcu_sched_data.gpnum),
+ TPS("cpuqs"));
+ __this_cpu_write(rcu_sched_data.cpu_no_qs.b.norm, false);
+ if (!__this_cpu_read(rcu_sched_data.cpu_no_qs.b.exp))
+ return;
+ __this_cpu_write(rcu_sched_data.cpu_no_qs.b.exp, false);
+ rcu_report_exp_rdp(&rcu_sched_state,
+ this_cpu_ptr(&rcu_sched_data), true);
}
void rcu_bh_qs(void)
@@ -300,17 +289,16 @@ EXPORT_PER_CPU_SYMBOL_GPL(rcu_qs_ctr);
* We inform the RCU core by emulating a zero-duration dyntick-idle
* period, which we in turn do by incrementing the ->dynticks counter
* by two.
+ *
+ * The caller must have disabled interrupts.
*/
static void rcu_momentary_dyntick_idle(void)
{
- unsigned long flags;
struct rcu_data *rdp;
struct rcu_dynticks *rdtp;
int resched_mask;
struct rcu_state *rsp;
- local_irq_save(flags);
-
/*
* Yes, we can lose flag-setting operations. This is OK, because
* the flag will be set again after some delay.
@@ -340,13 +328,12 @@ static void rcu_momentary_dyntick_idle(void)
smp_mb__after_atomic(); /* Later stuff after QS. */
break;
}
- local_irq_restore(flags);
}
/*
* Note a context switch. This is a quiescent state for RCU-sched,
* and requires special handling for preemptible RCU.
- * The caller must have disabled preemption.
+ * The caller must have disabled interrupts.
*/
void rcu_note_context_switch(void)
{
@@ -376,9 +363,14 @@ EXPORT_SYMBOL_GPL(rcu_note_context_switch);
*/
void rcu_all_qs(void)
{
+ unsigned long flags;
+
barrier(); /* Avoid RCU read-side critical sections leaking down. */
- if (unlikely(raw_cpu_read(rcu_sched_qs_mask)))
+ if (unlikely(raw_cpu_read(rcu_sched_qs_mask))) {
+ local_irq_save(flags);
rcu_momentary_dyntick_idle();
+ local_irq_restore(flags);
+ }
this_cpu_inc(rcu_qs_ctr);
barrier(); /* Avoid RCU read-side critical sections leaking up. */
}
@@ -605,25 +597,25 @@ static int rcu_future_needs_gp(struct rcu_state *rsp)
* The caller must have disabled interrupts to prevent races with
* normal callback registry.
*/
-static int
+static bool
cpu_needs_another_gp(struct rcu_state *rsp, struct rcu_data *rdp)
{
int i;
if (rcu_gp_in_progress(rsp))
- return 0; /* No, a grace period is already in progress. */
+ return false; /* No, a grace period is already in progress. */
if (rcu_future_needs_gp(rsp))
- return 1; /* Yes, a no-CBs CPU needs one. */
+ return true; /* Yes, a no-CBs CPU needs one. */
if (!rdp->nxttail[RCU_NEXT_TAIL])
- return 0; /* No, this is a no-CBs (or offline) CPU. */
+ return false; /* No, this is a no-CBs (or offline) CPU. */
if (*rdp->nxttail[RCU_NEXT_READY_TAIL])
- return 1; /* Yes, this CPU has newly registered callbacks. */
+ return true; /* Yes, 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(READ_ONCE(rsp->completed),
rdp->nxtcompleted[i]))
- return 1; /* Yes, CBs for future grace period. */
- return 0; /* No grace period needed. */
+ return true; /* Yes, CBs for future grace period. */
+ return false; /* No grace period needed. */
}
/*
@@ -740,7 +732,7 @@ void rcu_user_enter(void)
*
* Exit from an interrupt handler, which might possibly result in entering
* idle mode, in other words, leaving the mode in which read-side critical
- * sections can occur.
+ * sections can occur. The caller must have disabled interrupts.
*
* This code assumes that the idle loop never does anything that might
* result in unbalanced calls to irq_enter() and irq_exit(). If your
@@ -753,11 +745,10 @@ void rcu_user_enter(void)
*/
void rcu_irq_exit(void)
{
- unsigned long flags;
long long oldval;
struct rcu_dynticks *rdtp;
- local_irq_save(flags);
+ RCU_LOCKDEP_WARN(!irqs_disabled(), "rcu_irq_exit() invoked with irqs enabled!!!");
rdtp = this_cpu_ptr(&rcu_dynticks);
oldval = rdtp->dynticks_nesting;
rdtp->dynticks_nesting--;
@@ -768,6 +759,17 @@ void rcu_irq_exit(void)
else
rcu_eqs_enter_common(oldval, true);
rcu_sysidle_enter(1);
+}
+
+/*
+ * Wrapper for rcu_irq_exit() where interrupts are enabled.
+ */
+void rcu_irq_exit_irqson(void)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ rcu_irq_exit();
local_irq_restore(flags);
}
@@ -865,7 +867,7 @@ void rcu_user_exit(void)
*
* Enter an interrupt handler, which might possibly result in exiting
* idle mode, in other words, entering the mode in which read-side critical
- * sections can occur.
+ * sections can occur. The caller must have disabled interrupts.
*
* Note that the Linux kernel is fully capable of entering an interrupt
* handler that it never exits, for example when doing upcalls to
@@ -881,11 +883,10 @@ void rcu_user_exit(void)
*/
void rcu_irq_enter(void)
{
- unsigned long flags;
struct rcu_dynticks *rdtp;
long long oldval;
- local_irq_save(flags);
+ RCU_LOCKDEP_WARN(!irqs_disabled(), "rcu_irq_enter() invoked with irqs enabled!!!");
rdtp = this_cpu_ptr(&rcu_dynticks);
oldval = rdtp->dynticks_nesting;
rdtp->dynticks_nesting++;
@@ -896,6 +897,17 @@ void rcu_irq_enter(void)
else
rcu_eqs_exit_common(oldval, true);
rcu_sysidle_exit(1);
+}
+
+/*
+ * Wrapper for rcu_irq_enter() where interrupts are enabled.
+ */
+void rcu_irq_enter_irqson(void)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ rcu_irq_enter();
local_irq_restore(flags);
}
@@ -1187,6 +1199,16 @@ static void record_gp_stall_check_time(struct rcu_state *rsp)
}
/*
+ * Convert a ->gp_state value to a character string.
+ */
+static const char *gp_state_getname(short gs)
+{
+ if (gs < 0 || gs >= ARRAY_SIZE(gp_state_names))
+ return "???";
+ return gp_state_names[gs];
+}
+
+/*
* Complain about starvation of grace-period kthread.
*/
static void rcu_check_gp_kthread_starvation(struct rcu_state *rsp)
@@ -1196,12 +1218,16 @@ static void rcu_check_gp_kthread_starvation(struct rcu_state *rsp)
j = jiffies;
gpa = READ_ONCE(rsp->gp_activity);
- if (j - gpa > 2 * HZ)
- pr_err("%s kthread starved for %ld jiffies! g%lu c%lu f%#x s%d ->state=%#lx\n",
+ if (j - gpa > 2 * HZ) {
+ pr_err("%s kthread starved for %ld jiffies! g%lu c%lu f%#x %s(%d) ->state=%#lx\n",
rsp->name, j - gpa,
rsp->gpnum, rsp->completed,
- rsp->gp_flags, rsp->gp_state,
- rsp->gp_kthread ? rsp->gp_kthread->state : 0);
+ rsp->gp_flags,
+ gp_state_getname(rsp->gp_state), rsp->gp_state,
+ rsp->gp_kthread ? rsp->gp_kthread->state : ~0);
+ if (rsp->gp_kthread)
+ sched_show_task(rsp->gp_kthread);
+ }
}
/*
@@ -1214,7 +1240,7 @@ static void rcu_dump_cpu_stacks(struct rcu_state *rsp)
struct rcu_node *rnp;
rcu_for_each_leaf_node(rsp, rnp) {
- raw_spin_lock_irqsave(&rnp->lock, flags);
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
if (rnp->qsmask != 0) {
for (cpu = 0; cpu <= rnp->grphi - rnp->grplo; cpu++)
if (rnp->qsmask & (1UL << cpu))
@@ -1237,7 +1263,7 @@ 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);
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
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);
@@ -1256,7 +1282,7 @@ static void print_other_cpu_stall(struct rcu_state *rsp, unsigned long gpnum)
rsp->name);
print_cpu_stall_info_begin();
rcu_for_each_leaf_node(rsp, rnp) {
- raw_spin_lock_irqsave(&rnp->lock, flags);
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
ndetected += rcu_print_task_stall(rnp);
if (rnp->qsmask != 0) {
for (cpu = 0; cpu <= rnp->grphi - rnp->grplo; cpu++)
@@ -1327,7 +1353,7 @@ static void print_cpu_stall(struct rcu_state *rsp)
rcu_dump_cpu_stacks(rsp);
- raw_spin_lock_irqsave(&rnp->lock, flags);
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
if (ULONG_CMP_GE(jiffies, READ_ONCE(rsp->jiffies_stall)))
WRITE_ONCE(rsp->jiffies_stall,
jiffies + 3 * rcu_jiffies_till_stall_check() + 3);
@@ -1534,10 +1560,8 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,
* hold it, acquire the root rcu_node structure's lock in order to
* start one (if needed).
*/
- if (rnp != rnp_root) {
- raw_spin_lock(&rnp_root->lock);
- smp_mb__after_unlock_lock();
- }
+ if (rnp != rnp_root)
+ raw_spin_lock_rcu_node(rnp_root);
/*
* Get a new grace-period number. If there really is no grace
@@ -1786,11 +1810,10 @@ static void note_gp_changes(struct rcu_state *rsp, struct rcu_data *rdp)
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. */
+ !raw_spin_trylock_rcu_node(rnp)) { /* irqs already off, so later. */
local_irq_restore(flags);
return;
}
- smp_mb__after_unlock_lock();
needwake = __note_gp_changes(rsp, rnp, rdp);
raw_spin_unlock_irqrestore(&rnp->lock, flags);
if (needwake)
@@ -1805,21 +1828,20 @@ static void rcu_gp_slow(struct rcu_state *rsp, int delay)
}
/*
- * Initialize a new grace period. Return 0 if no grace period required.
+ * Initialize a new grace period. Return false if no grace period required.
*/
-static int rcu_gp_init(struct rcu_state *rsp)
+static bool rcu_gp_init(struct rcu_state *rsp)
{
unsigned long oldmask;
struct rcu_data *rdp;
struct rcu_node *rnp = rcu_get_root(rsp);
WRITE_ONCE(rsp->gp_activity, jiffies);
- raw_spin_lock_irq(&rnp->lock);
- smp_mb__after_unlock_lock();
+ raw_spin_lock_irq_rcu_node(rnp);
if (!READ_ONCE(rsp->gp_flags)) {
/* Spurious wakeup, tell caller to go back to sleep. */
raw_spin_unlock_irq(&rnp->lock);
- return 0;
+ return false;
}
WRITE_ONCE(rsp->gp_flags, 0); /* Clear all flags: New grace period. */
@@ -1829,7 +1851,7 @@ static int rcu_gp_init(struct rcu_state *rsp)
* Not supposed to be able to happen.
*/
raw_spin_unlock_irq(&rnp->lock);
- return 0;
+ return false;
}
/* Advance to a new grace period and initialize state. */
@@ -1847,8 +1869,7 @@ static int rcu_gp_init(struct rcu_state *rsp)
*/
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();
+ raw_spin_lock_irq_rcu_node(rnp);
if (rnp->qsmaskinit == rnp->qsmaskinitnext &&
!rnp->wait_blkd_tasks) {
/* Nothing to do on this leaf rcu_node structure. */
@@ -1904,8 +1925,7 @@ static int rcu_gp_init(struct rcu_state *rsp)
*/
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();
+ raw_spin_lock_irq_rcu_node(rnp);
rdp = this_cpu_ptr(rsp->rda);
rcu_preempt_check_blocked_tasks(rnp);
rnp->qsmask = rnp->qsmaskinit;
@@ -1923,7 +1943,7 @@ static int rcu_gp_init(struct rcu_state *rsp)
WRITE_ONCE(rsp->gp_activity, jiffies);
}
- return 1;
+ return true;
}
/*
@@ -1973,8 +1993,7 @@ static void rcu_gp_fqs(struct rcu_state *rsp, bool first_time)
}
/* Clear flag to prevent immediate re-entry. */
if (READ_ONCE(rsp->gp_flags) & RCU_GP_FLAG_FQS) {
- raw_spin_lock_irq(&rnp->lock);
- smp_mb__after_unlock_lock();
+ raw_spin_lock_irq_rcu_node(rnp);
WRITE_ONCE(rsp->gp_flags,
READ_ONCE(rsp->gp_flags) & ~RCU_GP_FLAG_FQS);
raw_spin_unlock_irq(&rnp->lock);
@@ -1993,8 +2012,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)
struct rcu_node *rnp = rcu_get_root(rsp);
WRITE_ONCE(rsp->gp_activity, jiffies);
- raw_spin_lock_irq(&rnp->lock);
- smp_mb__after_unlock_lock();
+ raw_spin_lock_irq_rcu_node(rnp);
gp_duration = jiffies - rsp->gp_start;
if (gp_duration > rsp->gp_max)
rsp->gp_max = gp_duration;
@@ -2019,8 +2037,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)
* grace period is recorded in any of the rcu_node structures.
*/
rcu_for_each_node_breadth_first(rsp, rnp) {
- raw_spin_lock_irq(&rnp->lock);
- smp_mb__after_unlock_lock();
+ raw_spin_lock_irq_rcu_node(rnp);
WARN_ON_ONCE(rcu_preempt_blocked_readers_cgp(rnp));
WARN_ON_ONCE(rnp->qsmask);
WRITE_ONCE(rnp->completed, rsp->gpnum);
@@ -2035,8 +2052,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)
rcu_gp_slow(rsp, gp_cleanup_delay);
}
rnp = rcu_get_root(rsp);
- raw_spin_lock_irq(&rnp->lock);
- smp_mb__after_unlock_lock(); /* Order GP before ->completed update. */
+ raw_spin_lock_irq_rcu_node(rnp); /* Order GP before ->completed update. */
rcu_nocb_gp_set(rnp, nocb);
/* Declare grace period done. */
@@ -2284,8 +2300,7 @@ rcu_report_qs_rnp(unsigned long mask, struct rcu_state *rsp,
raw_spin_unlock_irqrestore(&rnp->lock, flags);
rnp_c = rnp;
rnp = rnp->parent;
- raw_spin_lock_irqsave(&rnp->lock, flags);
- smp_mb__after_unlock_lock();
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
oldmask = rnp_c->qsmask;
}
@@ -2332,8 +2347,7 @@ static void rcu_report_unblock_qs_rnp(struct rcu_state *rsp,
gps = rnp->gpnum;
mask = rnp->grpmask;
raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */
- raw_spin_lock(&rnp_p->lock); /* irqs already disabled. */
- smp_mb__after_unlock_lock();
+ raw_spin_lock_rcu_node(rnp_p); /* irqs already disabled. */
rcu_report_qs_rnp(mask, rsp, rnp_p, gps, flags);
}
@@ -2355,8 +2369,7 @@ rcu_report_qs_rdp(int cpu, struct rcu_state *rsp, struct rcu_data *rdp)
struct rcu_node *rnp;
rnp = rdp->mynode;
- raw_spin_lock_irqsave(&rnp->lock, flags);
- smp_mb__after_unlock_lock();
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
if ((rdp->cpu_no_qs.b.norm &&
rdp->rcu_qs_ctr_snap == __this_cpu_read(rcu_qs_ctr)) ||
rdp->gpnum != rnp->gpnum || rnp->completed == rnp->gpnum ||
@@ -2582,8 +2595,7 @@ static void rcu_cleanup_dead_rnp(struct rcu_node *rnp_leaf)
rnp = rnp->parent;
if (!rnp)
break;
- raw_spin_lock(&rnp->lock); /* irqs already disabled. */
- smp_mb__after_unlock_lock(); /* GP memory ordering. */
+ raw_spin_lock_rcu_node(rnp); /* irqs already disabled. */
rnp->qsmaskinit &= ~mask;
rnp->qsmask &= ~mask;
if (rnp->qsmaskinit) {
@@ -2611,8 +2623,7 @@ static void rcu_cleanup_dying_idle_cpu(int cpu, struct rcu_state *rsp)
/* Remove outgoing CPU from mask in the leaf rcu_node structure. */
mask = rdp->grpmask;
- raw_spin_lock_irqsave(&rnp->lock, flags);
- smp_mb__after_unlock_lock(); /* Enforce GP memory-order guarantee. */
+ raw_spin_lock_irqsave_rcu_node(rnp, flags); /* Enforce GP memory-order guarantee. */
rnp->qsmaskinitnext &= ~mask;
raw_spin_unlock_irqrestore(&rnp->lock, flags);
}
@@ -2809,8 +2820,7 @@ static void force_qs_rnp(struct rcu_state *rsp,
rcu_for_each_leaf_node(rsp, rnp) {
cond_resched_rcu_qs();
mask = 0;
- raw_spin_lock_irqsave(&rnp->lock, flags);
- smp_mb__after_unlock_lock();
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
if (rnp->qsmask == 0) {
if (rcu_state_p == &rcu_sched_state ||
rsp != rcu_state_p ||
@@ -2881,8 +2891,7 @@ static void force_quiescent_state(struct rcu_state *rsp)
/* rnp_old == rcu_get_root(rsp), rnp == NULL. */
/* Reached the root of the rcu_node tree, acquire lock. */
- raw_spin_lock_irqsave(&rnp_old->lock, flags);
- smp_mb__after_unlock_lock();
+ raw_spin_lock_irqsave_rcu_node(rnp_old, flags);
raw_spin_unlock(&rnp_old->fqslock);
if (READ_ONCE(rsp->gp_flags) & RCU_GP_FLAG_FQS) {
rsp->n_force_qs_lh++;
@@ -2914,7 +2923,7 @@ __rcu_process_callbacks(struct rcu_state *rsp)
/* Does this CPU require a not-yet-started grace period? */
local_irq_save(flags);
if (cpu_needs_another_gp(rsp, rdp)) {
- raw_spin_lock(&rcu_get_root(rsp)->lock); /* irqs disabled. */
+ raw_spin_lock_rcu_node(rcu_get_root(rsp)); /* irqs disabled. */
needwake = rcu_start_gp(rsp);
raw_spin_unlock_irqrestore(&rcu_get_root(rsp)->lock, flags);
if (needwake)
@@ -3005,8 +3014,7 @@ static void __call_rcu_core(struct rcu_state *rsp, struct rcu_data *rdp,
if (!rcu_gp_in_progress(rsp)) {
struct rcu_node *rnp_root = rcu_get_root(rsp);
- raw_spin_lock(&rnp_root->lock);
- smp_mb__after_unlock_lock();
+ raw_spin_lock_rcu_node(rnp_root);
needwake = rcu_start_gp(rsp);
raw_spin_unlock(&rnp_root->lock);
if (needwake)
@@ -3365,7 +3373,6 @@ static unsigned long rcu_seq_snap(unsigned long *sp)
{
unsigned long s;
- smp_mb(); /* Caller's modifications seen first by other CPUs. */
s = (READ_ONCE(*sp) + 3) & ~0x1;
smp_mb(); /* Above access must not bleed into critical section. */
return s;
@@ -3392,6 +3399,7 @@ static void rcu_exp_gp_seq_end(struct rcu_state *rsp)
}
static unsigned long rcu_exp_gp_seq_snap(struct rcu_state *rsp)
{
+ smp_mb(); /* Caller's modifications seen first by other CPUs. */
return rcu_seq_snap(&rsp->expedited_sequence);
}
static bool rcu_exp_gp_seq_done(struct rcu_state *rsp, unsigned long s)
@@ -3426,8 +3434,7 @@ static void sync_exp_reset_tree_hotplug(struct rcu_state *rsp)
* CPUs for the current rcu_node structure up the rcu_node tree.
*/
rcu_for_each_leaf_node(rsp, rnp) {
- raw_spin_lock_irqsave(&rnp->lock, flags);
- smp_mb__after_unlock_lock();
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
if (rnp->expmaskinit == rnp->expmaskinitnext) {
raw_spin_unlock_irqrestore(&rnp->lock, flags);
continue; /* No new CPUs, nothing to do. */
@@ -3447,8 +3454,7 @@ static void sync_exp_reset_tree_hotplug(struct rcu_state *rsp)
rnp_up = rnp->parent;
done = false;
while (rnp_up) {
- raw_spin_lock_irqsave(&rnp_up->lock, flags);
- smp_mb__after_unlock_lock();
+ raw_spin_lock_irqsave_rcu_node(rnp_up, flags);
if (rnp_up->expmaskinit)
done = true;
rnp_up->expmaskinit |= mask;
@@ -3472,8 +3478,7 @@ static void __maybe_unused sync_exp_reset_tree(struct rcu_state *rsp)
sync_exp_reset_tree_hotplug(rsp);
rcu_for_each_node_breadth_first(rsp, rnp) {
- raw_spin_lock_irqsave(&rnp->lock, flags);
- smp_mb__after_unlock_lock();
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
WARN_ON_ONCE(rnp->expmask);
rnp->expmask = rnp->expmaskinit;
raw_spin_unlock_irqrestore(&rnp->lock, flags);
@@ -3531,8 +3536,7 @@ static void __rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp,
mask = rnp->grpmask;
raw_spin_unlock(&rnp->lock); /* irqs remain disabled */
rnp = rnp->parent;
- raw_spin_lock(&rnp->lock); /* irqs already disabled */
- smp_mb__after_unlock_lock();
+ raw_spin_lock_rcu_node(rnp); /* irqs already disabled */
WARN_ON_ONCE(!(rnp->expmask & mask));
rnp->expmask &= ~mask;
}
@@ -3549,8 +3553,7 @@ static void __maybe_unused rcu_report_exp_rnp(struct rcu_state *rsp,
{
unsigned long flags;
- raw_spin_lock_irqsave(&rnp->lock, flags);
- smp_mb__after_unlock_lock();
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
__rcu_report_exp_rnp(rsp, rnp, wake, flags);
}
@@ -3564,8 +3567,7 @@ static void rcu_report_exp_cpu_mult(struct rcu_state *rsp, struct rcu_node *rnp,
{
unsigned long flags;
- raw_spin_lock_irqsave(&rnp->lock, flags);
- smp_mb__after_unlock_lock();
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
if (!(rnp->expmask & mask)) {
raw_spin_unlock_irqrestore(&rnp->lock, flags);
return;
@@ -3609,7 +3611,7 @@ static bool sync_exp_work_done(struct rcu_state *rsp, struct rcu_node *rnp,
*/
static struct rcu_node *exp_funnel_lock(struct rcu_state *rsp, unsigned long s)
{
- struct rcu_data *rdp;
+ struct rcu_data *rdp = per_cpu_ptr(rsp->rda, raw_smp_processor_id());
struct rcu_node *rnp0;
struct rcu_node *rnp1 = NULL;
@@ -3623,7 +3625,7 @@ static struct rcu_node *exp_funnel_lock(struct rcu_state *rsp, unsigned long s)
if (!mutex_is_locked(&rnp0->exp_funnel_mutex)) {
if (mutex_trylock(&rnp0->exp_funnel_mutex)) {
if (sync_exp_work_done(rsp, rnp0, NULL,
- &rsp->expedited_workdone0, s))
+ &rdp->expedited_workdone0, s))
return NULL;
return rnp0;
}
@@ -3637,14 +3639,13 @@ static struct rcu_node *exp_funnel_lock(struct rcu_state *rsp, unsigned long s)
* can be inexact, as it is just promoting locality and is not
* strictly needed for correctness.
*/
- rdp = per_cpu_ptr(rsp->rda, raw_smp_processor_id());
- if (sync_exp_work_done(rsp, NULL, NULL, &rsp->expedited_workdone1, s))
+ if (sync_exp_work_done(rsp, NULL, NULL, &rdp->expedited_workdone1, s))
return NULL;
mutex_lock(&rdp->exp_funnel_mutex);
rnp0 = rdp->mynode;
for (; rnp0 != NULL; rnp0 = rnp0->parent) {
if (sync_exp_work_done(rsp, rnp1, rdp,
- &rsp->expedited_workdone2, s))
+ &rdp->expedited_workdone2, s))
return NULL;
mutex_lock(&rnp0->exp_funnel_mutex);
if (rnp1)
@@ -3654,7 +3655,7 @@ static struct rcu_node *exp_funnel_lock(struct rcu_state *rsp, unsigned long s)
rnp1 = rnp0;
}
if (sync_exp_work_done(rsp, rnp1, rdp,
- &rsp->expedited_workdone3, s))
+ &rdp->expedited_workdone3, s))
return NULL;
return rnp1;
}
@@ -3708,8 +3709,7 @@ static void sync_rcu_exp_select_cpus(struct rcu_state *rsp,
sync_exp_reset_tree(rsp);
rcu_for_each_leaf_node(rsp, rnp) {
- raw_spin_lock_irqsave(&rnp->lock, flags);
- smp_mb__after_unlock_lock();
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
/* Each pass checks a CPU for identity, offline, and idle. */
mask_ofl_test = 0;
@@ -3741,24 +3741,22 @@ retry_ipi:
ret = smp_call_function_single(cpu, func, rsp, 0);
if (!ret) {
mask_ofl_ipi &= ~mask;
- } else {
- /* Failed, raced with offline. */
- raw_spin_lock_irqsave(&rnp->lock, flags);
- if (cpu_online(cpu) &&
- (rnp->expmask & mask)) {
- raw_spin_unlock_irqrestore(&rnp->lock,
- flags);
- schedule_timeout_uninterruptible(1);
- if (cpu_online(cpu) &&
- (rnp->expmask & mask))
- goto retry_ipi;
- raw_spin_lock_irqsave(&rnp->lock,
- flags);
- }
- if (!(rnp->expmask & mask))
- mask_ofl_ipi &= ~mask;
+ continue;
+ }
+ /* Failed, raced with offline. */
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
+ if (cpu_online(cpu) &&
+ (rnp->expmask & mask)) {
raw_spin_unlock_irqrestore(&rnp->lock, flags);
+ schedule_timeout_uninterruptible(1);
+ if (cpu_online(cpu) &&
+ (rnp->expmask & mask))
+ goto retry_ipi;
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
}
+ if (!(rnp->expmask & mask))
+ mask_ofl_ipi &= ~mask;
+ raw_spin_unlock_irqrestore(&rnp->lock, flags);
}
/* Report quiescent states for those that went offline. */
mask_ofl_test |= mask_ofl_ipi;
@@ -3773,6 +3771,7 @@ static void synchronize_sched_expedited_wait(struct rcu_state *rsp)
unsigned long jiffies_stall;
unsigned long jiffies_start;
unsigned long mask;
+ int ndetected;
struct rcu_node *rnp;
struct rcu_node *rnp_root = rcu_get_root(rsp);
int ret;
@@ -3785,7 +3784,7 @@ static void synchronize_sched_expedited_wait(struct rcu_state *rsp)
rsp->expedited_wq,
sync_rcu_preempt_exp_done(rnp_root),
jiffies_stall);
- if (ret > 0)
+ if (ret > 0 || sync_rcu_preempt_exp_done(rnp_root))
return;
if (ret < 0) {
/* Hit a signal, disable CPU stall warnings. */
@@ -3795,14 +3794,16 @@ static void synchronize_sched_expedited_wait(struct rcu_state *rsp)
}
pr_err("INFO: %s detected expedited stalls on CPUs/tasks: {",
rsp->name);
+ ndetected = 0;
rcu_for_each_leaf_node(rsp, rnp) {
- (void)rcu_print_task_exp_stall(rnp);
+ ndetected = rcu_print_task_exp_stall(rnp);
mask = 1;
for (cpu = rnp->grplo; cpu <= rnp->grphi; cpu++, mask <<= 1) {
struct rcu_data *rdp;
if (!(rnp->expmask & mask))
continue;
+ ndetected++;
rdp = per_cpu_ptr(rsp->rda, cpu);
pr_cont(" %d-%c%c%c", cpu,
"O."[cpu_online(cpu)],
@@ -3811,8 +3812,23 @@ static void synchronize_sched_expedited_wait(struct rcu_state *rsp)
}
mask <<= 1;
}
- pr_cont(" } %lu jiffies s: %lu\n",
- jiffies - jiffies_start, rsp->expedited_sequence);
+ pr_cont(" } %lu jiffies s: %lu root: %#lx/%c\n",
+ jiffies - jiffies_start, rsp->expedited_sequence,
+ rnp_root->expmask, ".T"[!!rnp_root->exp_tasks]);
+ if (!ndetected) {
+ pr_err("blocking rcu_node structures:");
+ rcu_for_each_node_breadth_first(rsp, rnp) {
+ if (rnp == rnp_root)
+ continue; /* printed unconditionally */
+ if (sync_rcu_preempt_exp_done(rnp))
+ continue;
+ pr_cont(" l=%u:%d-%d:%#lx/%c",
+ rnp->level, rnp->grplo, rnp->grphi,
+ rnp->expmask,
+ ".T"[!!rnp->exp_tasks]);
+ }
+ pr_cont("\n");
+ }
rcu_for_each_leaf_node(rsp, rnp) {
mask = 1;
for (cpu = rnp->grplo; cpu <= rnp->grphi; cpu++, mask <<= 1) {
@@ -3847,6 +3863,16 @@ void synchronize_sched_expedited(void)
struct rcu_node *rnp;
struct rcu_state *rsp = &rcu_sched_state;
+ /* If only one CPU, this is automatically a grace period. */
+ if (rcu_blocking_is_gp())
+ return;
+
+ /* If expedited grace periods are prohibited, fall back to normal. */
+ if (rcu_gp_is_normal()) {
+ wait_rcu_gp(call_rcu_sched);
+ return;
+ }
+
/* Take a snapshot of the sequence number. */
s = rcu_exp_gp_seq_snap(rsp);
@@ -4135,7 +4161,7 @@ static void rcu_init_new_rnp(struct rcu_node *rnp_leaf)
rnp = rnp->parent;
if (rnp == NULL)
return;
- raw_spin_lock(&rnp->lock); /* Interrupts already disabled. */
+ raw_spin_lock_rcu_node(rnp); /* Interrupts already disabled. */
rnp->qsmaskinit |= mask;
raw_spin_unlock(&rnp->lock); /* Interrupts remain disabled. */
}
@@ -4152,7 +4178,7 @@ rcu_boot_init_percpu_data(int cpu, struct rcu_state *rsp)
struct rcu_node *rnp = rcu_get_root(rsp);
/* Set up local state, ensuring consistent view of global state. */
- raw_spin_lock_irqsave(&rnp->lock, flags);
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
rdp->grpmask = 1UL << (cpu - rdp->mynode->grplo);
rdp->dynticks = &per_cpu(rcu_dynticks, cpu);
WARN_ON_ONCE(rdp->dynticks->dynticks_nesting != DYNTICK_TASK_EXIT_IDLE);
@@ -4179,7 +4205,7 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp)
struct rcu_node *rnp = rcu_get_root(rsp);
/* Set up local state, ensuring consistent view of global state. */
- raw_spin_lock_irqsave(&rnp->lock, flags);
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
rdp->qlen_last_fqs_check = 0;
rdp->n_force_qs_snap = rsp->n_force_qs;
rdp->blimit = blimit;
@@ -4198,8 +4224,7 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp)
*/
rnp = rdp->mynode;
mask = rdp->grpmask;
- raw_spin_lock(&rnp->lock); /* irqs already disabled. */
- smp_mb__after_unlock_lock();
+ raw_spin_lock_rcu_node(rnp); /* irqs already disabled. */
rnp->qsmaskinitnext |= mask;
rnp->expmaskinitnext |= mask;
if (!rdp->beenonline)
@@ -4327,14 +4352,14 @@ static int __init rcu_spawn_gp_kthread(void)
t = kthread_create(rcu_gp_kthread, rsp, "%s", rsp->name);
BUG_ON(IS_ERR(t));
rnp = rcu_get_root(rsp);
- raw_spin_lock_irqsave(&rnp->lock, flags);
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
rsp->gp_kthread = t;
if (kthread_prio) {
sp.sched_priority = kthread_prio;
sched_setscheduler_nocheck(t, SCHED_FIFO, &sp);
}
- wake_up_process(t);
raw_spin_unlock_irqrestore(&rnp->lock, flags);
+ wake_up_process(t);
}
rcu_spawn_nocb_kthreads();
rcu_spawn_boost_kthreads();
@@ -4385,12 +4410,14 @@ static void __init rcu_init_levelspread(int *levelspread, const int *levelcnt)
/*
* Helper function for rcu_init() that initializes one rcu_state structure.
*/
-static void __init rcu_init_one(struct rcu_state *rsp,
- struct rcu_data __percpu *rda)
+static void __init rcu_init_one(struct rcu_state *rsp)
{
static const char * const buf[] = RCU_NODE_NAME_INIT;
static const char * const fqs[] = RCU_FQS_NAME_INIT;
static const char * const exp[] = RCU_EXP_NAME_INIT;
+ static struct lock_class_key rcu_node_class[RCU_NUM_LVLS];
+ static struct lock_class_key rcu_fqs_class[RCU_NUM_LVLS];
+ static struct lock_class_key rcu_exp_class[RCU_NUM_LVLS];
static u8 fl_mask = 0x1;
int levelcnt[RCU_NUM_LVLS]; /* # nodes in each level. */
@@ -4576,8 +4603,8 @@ void __init rcu_init(void)
rcu_bootup_announce();
rcu_init_geometry();
- rcu_init_one(&rcu_bh_state, &rcu_bh_data);
- rcu_init_one(&rcu_sched_state, &rcu_sched_data);
+ rcu_init_one(&rcu_bh_state);
+ rcu_init_one(&rcu_sched_state);
if (dump_tree)
rcu_dump_rcu_node_tree(&rcu_sched_state);
__rcu_init_preempt();
diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h
index 9fb4e238d4dc..83360b4f4352 100644
--- a/kernel/rcu/tree.h
+++ b/kernel/rcu/tree.h
@@ -178,6 +178,8 @@ struct rcu_node {
/* beginning of each expedited GP. */
unsigned long expmaskinitnext;
/* Online CPUs for next expedited GP. */
+ /* Any CPU that has ever been online will */
+ /* have its bit set. */
unsigned long grpmask; /* Mask to apply to parent qsmask. */
/* Only one bit will be set in this mask. */
int grplo; /* lowest-numbered CPU or group here. */
@@ -384,6 +386,10 @@ struct rcu_data {
struct rcu_head oom_head;
#endif /* #ifdef CONFIG_RCU_FAST_NO_HZ */
struct mutex exp_funnel_mutex;
+ atomic_long_t expedited_workdone0; /* # done by others #0. */
+ atomic_long_t expedited_workdone1; /* # done by others #1. */
+ atomic_long_t expedited_workdone2; /* # done by others #2. */
+ atomic_long_t expedited_workdone3; /* # done by others #3. */
/* 7) Callback offloading. */
#ifdef CONFIG_RCU_NOCB_CPU
@@ -498,10 +504,6 @@ struct rcu_state {
/* End of fields guarded by barrier_mutex. */
unsigned long expedited_sequence; /* Take a ticket. */
- atomic_long_t expedited_workdone0; /* # done by others #0. */
- atomic_long_t expedited_workdone1; /* # done by others #1. */
- atomic_long_t expedited_workdone2; /* # done by others #2. */
- atomic_long_t expedited_workdone3; /* # done by others #3. */
atomic_long_t expedited_normal; /* # fallbacks to normal. */
atomic_t expedited_need_qs; /* # CPUs left to check in. */
wait_queue_head_t expedited_wq; /* Wait for check-ins. */
@@ -545,6 +547,18 @@ struct rcu_state {
#define RCU_GP_CLEANUP 5 /* Grace-period cleanup started. */
#define RCU_GP_CLEANED 6 /* Grace-period cleanup complete. */
+#ifndef RCU_TREE_NONCORE
+static const char * const gp_state_names[] = {
+ "RCU_GP_IDLE",
+ "RCU_GP_WAIT_GPS",
+ "RCU_GP_DONE_GPS",
+ "RCU_GP_WAIT_FQS",
+ "RCU_GP_DOING_FQS",
+ "RCU_GP_CLEANUP",
+ "RCU_GP_CLEANED",
+};
+#endif /* #ifndef RCU_TREE_NONCORE */
+
extern struct list_head rcu_struct_flavors;
/* Sequence through rcu_state structures for each RCU flavor. */
@@ -664,3 +678,42 @@ static inline void rcu_nocb_q_lengths(struct rcu_data *rdp, long *ql, long *qll)
#else /* #ifdef CONFIG_PPC */
#define smp_mb__after_unlock_lock() do { } while (0)
#endif /* #else #ifdef CONFIG_PPC */
+
+/*
+ * Wrappers for the rcu_node::lock acquire.
+ *
+ * Because the rcu_nodes form a tree, the tree traversal locking will observe
+ * different lock values, this in turn means that an UNLOCK of one level
+ * followed by a LOCK of another level does not imply a full memory barrier;
+ * and most importantly transitivity is lost.
+ *
+ * In order to restore full ordering between tree levels, augment the regular
+ * lock acquire functions with smp_mb__after_unlock_lock().
+ */
+static inline void raw_spin_lock_rcu_node(struct rcu_node *rnp)
+{
+ raw_spin_lock(&rnp->lock);
+ smp_mb__after_unlock_lock();
+}
+
+static inline void raw_spin_lock_irq_rcu_node(struct rcu_node *rnp)
+{
+ raw_spin_lock_irq(&rnp->lock);
+ smp_mb__after_unlock_lock();
+}
+
+#define raw_spin_lock_irqsave_rcu_node(rnp, flags) \
+do { \
+ typecheck(unsigned long, flags); \
+ raw_spin_lock_irqsave(&(rnp)->lock, flags); \
+ smp_mb__after_unlock_lock(); \
+} while (0)
+
+static inline bool raw_spin_trylock_rcu_node(struct rcu_node *rnp)
+{
+ bool locked = raw_spin_trylock(&rnp->lock);
+
+ if (locked)
+ smp_mb__after_unlock_lock();
+ return locked;
+}
diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h
index 630c19772630..9467a8b7e756 100644
--- a/kernel/rcu/tree_plugin.h
+++ b/kernel/rcu/tree_plugin.h
@@ -63,8 +63,7 @@ static bool __read_mostly rcu_nocb_poll; /* Offload kthread are to poll. */
/*
* Check the RCU kernel configuration parameters and print informative
- * messages about anything out of the ordinary. If you like #ifdef, you
- * will love this function.
+ * messages about anything out of the ordinary.
*/
static void __init rcu_bootup_announce_oddness(void)
{
@@ -147,8 +146,8 @@ static void __init rcu_bootup_announce(void)
* the corresponding expedited grace period will also be the end of the
* normal grace period.
*/
-static void rcu_preempt_ctxt_queue(struct rcu_node *rnp, struct rcu_data *rdp,
- unsigned long flags) __releases(rnp->lock)
+static void rcu_preempt_ctxt_queue(struct rcu_node *rnp, struct rcu_data *rdp)
+ __releases(rnp->lock) /* But leaves rrupts disabled. */
{
int blkd_state = (rnp->gp_tasks ? RCU_GP_TASKS : 0) +
(rnp->exp_tasks ? RCU_EXP_TASKS : 0) +
@@ -236,7 +235,7 @@ static void rcu_preempt_ctxt_queue(struct rcu_node *rnp, struct rcu_data *rdp,
rnp->gp_tasks = &t->rcu_node_entry;
if (!rnp->exp_tasks && (blkd_state & RCU_EXP_BLKD))
rnp->exp_tasks = &t->rcu_node_entry;
- raw_spin_unlock(&rnp->lock);
+ raw_spin_unlock(&rnp->lock); /* rrupts remain disabled. */
/*
* Report the quiescent state for the expedited GP. This expedited
@@ -251,7 +250,6 @@ static void rcu_preempt_ctxt_queue(struct rcu_node *rnp, struct rcu_data *rdp,
} else {
WARN_ON_ONCE(t->rcu_read_unlock_special.b.exp_need_qs);
}
- local_irq_restore(flags);
}
/*
@@ -286,12 +284,11 @@ static void rcu_preempt_qs(void)
* predating the current grace period drain, in other words, until
* rnp->gp_tasks becomes NULL.
*
- * Caller must disable preemption.
+ * Caller must disable interrupts.
*/
static void rcu_preempt_note_context_switch(void)
{
struct task_struct *t = current;
- unsigned long flags;
struct rcu_data *rdp;
struct rcu_node *rnp;
@@ -301,8 +298,7 @@ static void rcu_preempt_note_context_switch(void)
/* Possibly blocking in an RCU read-side critical section. */
rdp = this_cpu_ptr(rcu_state_p->rda);
rnp = rdp->mynode;
- raw_spin_lock_irqsave(&rnp->lock, flags);
- smp_mb__after_unlock_lock();
+ raw_spin_lock_rcu_node(rnp);
t->rcu_read_unlock_special.b.blocked = true;
t->rcu_blocked_node = rnp;
@@ -318,7 +314,7 @@ static void rcu_preempt_note_context_switch(void)
(rnp->qsmask & rdp->grpmask)
? rnp->gpnum
: rnp->gpnum + 1);
- rcu_preempt_ctxt_queue(rnp, rdp, flags);
+ rcu_preempt_ctxt_queue(rnp, rdp);
} else if (t->rcu_read_lock_nesting < 0 &&
t->rcu_read_unlock_special.s) {
@@ -450,20 +446,13 @@ void rcu_read_unlock_special(struct task_struct *t)
/*
* 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.
+ * now remains queued on the rcu_node corresponding to the
+ * CPU it first blocked on, so there is no longer any need
+ * to loop. Retain a WARN_ON_ONCE() out of sheer paranoia.
*/
- for (;;) {
- rnp = t->rcu_blocked_node;
- raw_spin_lock(&rnp->lock); /* irqs already disabled. */
- smp_mb__after_unlock_lock();
- if (rnp == t->rcu_blocked_node)
- break;
- WARN_ON_ONCE(1);
- raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */
- }
+ rnp = t->rcu_blocked_node;
+ raw_spin_lock_rcu_node(rnp); /* irqs already disabled. */
+ WARN_ON_ONCE(rnp != t->rcu_blocked_node);
empty_norm = !rcu_preempt_blocked_readers_cgp(rnp);
empty_exp = sync_rcu_preempt_exp_done(rnp);
smp_mb(); /* ensure expedited fastpath sees end of RCU c-s. */
@@ -527,7 +516,7 @@ static void rcu_print_detail_task_stall_rnp(struct rcu_node *rnp)
unsigned long flags;
struct task_struct *t;
- raw_spin_lock_irqsave(&rnp->lock, flags);
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
if (!rcu_preempt_blocked_readers_cgp(rnp)) {
raw_spin_unlock_irqrestore(&rnp->lock, flags);
return;
@@ -748,6 +737,12 @@ void synchronize_rcu_expedited(void)
struct rcu_state *rsp = rcu_state_p;
unsigned long s;
+ /* If expedited grace periods are prohibited, fall back to normal. */
+ if (rcu_gp_is_normal()) {
+ wait_rcu_gp(call_rcu);
+ return;
+ }
+
s = rcu_exp_gp_seq_snap(rsp);
rnp_unlock = exp_funnel_lock(rsp, s);
@@ -788,7 +783,7 @@ EXPORT_SYMBOL_GPL(rcu_barrier);
*/
static void __init __rcu_init_preempt(void)
{
- rcu_init_one(rcu_state_p, rcu_data_p);
+ rcu_init_one(rcu_state_p);
}
/*
@@ -989,8 +984,7 @@ static int rcu_boost(struct rcu_node *rnp)
READ_ONCE(rnp->boost_tasks) == NULL)
return 0; /* Nothing left to boost. */
- raw_spin_lock_irqsave(&rnp->lock, flags);
- smp_mb__after_unlock_lock();
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
/*
* Recheck under the lock: all tasks in need of boosting
@@ -1176,8 +1170,7 @@ static int rcu_spawn_one_boost_kthread(struct rcu_state *rsp,
"rcub/%d", rnp_index);
if (IS_ERR(t))
return PTR_ERR(t);
- raw_spin_lock_irqsave(&rnp->lock, flags);
- smp_mb__after_unlock_lock();
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
rnp->boost_kthread_task = t;
raw_spin_unlock_irqrestore(&rnp->lock, flags);
sp.sched_priority = kthread_prio;
@@ -1524,7 +1517,8 @@ static void rcu_prepare_for_idle(void)
struct rcu_state *rsp;
int tne;
- if (IS_ENABLED(CONFIG_RCU_NOCB_CPU_ALL))
+ if (IS_ENABLED(CONFIG_RCU_NOCB_CPU_ALL) ||
+ rcu_is_nocb_cpu(smp_processor_id()))
return;
/* Handle nohz enablement switches conservatively. */
@@ -1538,10 +1532,6 @@ static void rcu_prepare_for_idle(void)
if (!tne)
return;
- /* If this is a no-CBs CPU, no callbacks, just return. */
- if (rcu_is_nocb_cpu(smp_processor_id()))
- return;
-
/*
* If a non-lazy callback arrived at a CPU having only lazy
* callbacks, invoke RCU core for the side-effect of recalculating
@@ -1567,8 +1557,7 @@ static void rcu_prepare_for_idle(void)
if (!*rdp->nxttail[RCU_DONE_TAIL])
continue;
rnp = rdp->mynode;
- raw_spin_lock(&rnp->lock); /* irqs already disabled. */
- smp_mb__after_unlock_lock();
+ raw_spin_lock_rcu_node(rnp); /* irqs already disabled. */
needwake = rcu_accelerate_cbs(rsp, rnp, rdp);
raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */
if (needwake)
@@ -2068,8 +2057,7 @@ static void rcu_nocb_wait_gp(struct rcu_data *rdp)
bool needwake;
struct rcu_node *rnp = rdp->mynode;
- raw_spin_lock_irqsave(&rnp->lock, flags);
- smp_mb__after_unlock_lock();
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
needwake = rcu_start_future_gp(rnp, rdp, &c);
raw_spin_unlock_irqrestore(&rnp->lock, flags);
if (needwake)
diff --git a/kernel/rcu/tree_trace.c b/kernel/rcu/tree_trace.c
index ef7093cc9b5c..1088e64f01ad 100644
--- a/kernel/rcu/tree_trace.c
+++ b/kernel/rcu/tree_trace.c
@@ -1,5 +1,5 @@
/*
- * Read-Copy Update tracing for classic implementation
+ * Read-Copy Update tracing for hierarchical implementation.
*
* 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
@@ -16,6 +16,7 @@
* http://www.gnu.org/licenses/gpl-2.0.html.
*
* Copyright IBM Corporation, 2008
+ * Author: Paul E. McKenney
*
* Papers: http://www.rdrop.com/users/paulmck/RCU
*
@@ -33,9 +34,7 @@
#include <linux/sched.h>
#include <linux/atomic.h>
#include <linux/bitops.h>
-#include <linux/module.h>
#include <linux/completion.h>
-#include <linux/moduleparam.h>
#include <linux/percpu.h>
#include <linux/notifier.h>
#include <linux/cpu.h>
@@ -183,14 +182,20 @@ static const struct file_operations rcudata_fops = {
static int show_rcuexp(struct seq_file *m, void *v)
{
+ int cpu;
struct rcu_state *rsp = (struct rcu_state *)m->private;
-
+ struct rcu_data *rdp;
+ unsigned long s0 = 0, s1 = 0, s2 = 0, s3 = 0;
+
+ for_each_possible_cpu(cpu) {
+ rdp = per_cpu_ptr(rsp->rda, cpu);
+ s0 += atomic_long_read(&rdp->expedited_workdone0);
+ s1 += atomic_long_read(&rdp->expedited_workdone1);
+ s2 += atomic_long_read(&rdp->expedited_workdone2);
+ s3 += atomic_long_read(&rdp->expedited_workdone3);
+ }
seq_printf(m, "s=%lu wd0=%lu wd1=%lu wd2=%lu wd3=%lu n=%lu enq=%d sc=%lu\n",
- rsp->expedited_sequence,
- atomic_long_read(&rsp->expedited_workdone0),
- atomic_long_read(&rsp->expedited_workdone1),
- atomic_long_read(&rsp->expedited_workdone2),
- atomic_long_read(&rsp->expedited_workdone3),
+ rsp->expedited_sequence, s0, s1, s2, s3,
atomic_long_read(&rsp->expedited_normal),
atomic_read(&rsp->expedited_need_qs),
rsp->expedited_sequence / 2);
@@ -319,7 +324,7 @@ static void show_one_rcugp(struct seq_file *m, struct rcu_state *rsp)
unsigned long gpmax;
struct rcu_node *rnp = &rsp->node[0];
- raw_spin_lock_irqsave(&rnp->lock, flags);
+ raw_spin_lock_irqsave_rcu_node(rnp, flags);
completed = READ_ONCE(rsp->completed);
gpnum = READ_ONCE(rsp->gpnum);
if (completed == gpnum)
@@ -487,16 +492,4 @@ free_out:
debugfs_remove_recursive(rcudir);
return 1;
}
-
-static void __exit rcutree_trace_cleanup(void)
-{
- debugfs_remove_recursive(rcudir);
-}
-
-
-module_init(rcutree_trace_init);
-module_exit(rcutree_trace_cleanup);
-
-MODULE_AUTHOR("Paul E. McKenney");
-MODULE_DESCRIPTION("Read-Copy Update tracing for hierarchical implementation");
-MODULE_LICENSE("GPL");
+device_initcall(rcutree_trace_init);
diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c
index 5f748c5a40f0..76b94e19430b 100644
--- a/kernel/rcu/update.c
+++ b/kernel/rcu/update.c
@@ -60,7 +60,12 @@ MODULE_ALIAS("rcupdate");
#endif
#define MODULE_PARAM_PREFIX "rcupdate."
+#ifndef CONFIG_TINY_RCU
module_param(rcu_expedited, int, 0);
+module_param(rcu_normal, int, 0);
+static int rcu_normal_after_boot;
+module_param(rcu_normal_after_boot, int, 0);
+#endif /* #ifndef CONFIG_TINY_RCU */
#if defined(CONFIG_DEBUG_LOCK_ALLOC) && defined(CONFIG_PREEMPT_COUNT)
/**
@@ -113,6 +118,17 @@ EXPORT_SYMBOL(rcu_read_lock_sched_held);
#ifndef CONFIG_TINY_RCU
+/*
+ * Should expedited grace-period primitives always fall back to their
+ * non-expedited counterparts? Intended for use within RCU. Note
+ * that if the user specifies both rcu_expedited and rcu_normal, then
+ * rcu_normal wins.
+ */
+bool rcu_gp_is_normal(void)
+{
+ return READ_ONCE(rcu_normal);
+}
+
static atomic_t rcu_expedited_nesting =
ATOMIC_INIT(IS_ENABLED(CONFIG_RCU_EXPEDITE_BOOT) ? 1 : 0);
@@ -157,8 +173,6 @@ void rcu_unexpedite_gp(void)
}
EXPORT_SYMBOL_GPL(rcu_unexpedite_gp);
-#endif /* #ifndef CONFIG_TINY_RCU */
-
/*
* Inform RCU of the end of the in-kernel boot sequence.
*/
@@ -166,8 +180,12 @@ void rcu_end_inkernel_boot(void)
{
if (IS_ENABLED(CONFIG_RCU_EXPEDITE_BOOT))
rcu_unexpedite_gp();
+ if (rcu_normal_after_boot)
+ WRITE_ONCE(rcu_normal, 1);
}
+#endif /* #ifndef CONFIG_TINY_RCU */
+
#ifdef CONFIG_PREEMPT_RCU
/*
diff --git a/kernel/resource.c b/kernel/resource.c
index f150dbbe6f62..09c0597840b0 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -1498,8 +1498,15 @@ int iomem_is_exclusive(u64 addr)
break;
if (p->end < addr)
continue;
- if (p->flags & IORESOURCE_BUSY &&
- p->flags & IORESOURCE_EXCLUSIVE) {
+ /*
+ * A resource is exclusive if IORESOURCE_EXCLUSIVE is set
+ * or CONFIG_IO_STRICT_DEVMEM is enabled and the
+ * resource is busy.
+ */
+ if ((p->flags & IORESOURCE_BUSY) == 0)
+ continue;
+ if (IS_ENABLED(CONFIG_IO_STRICT_DEVMEM)
+ || p->flags & IORESOURCE_EXCLUSIVE) {
err = 1;
break;
}
diff --git a/kernel/sched/auto_group.c b/kernel/sched/auto_group.c
index 750ed601ddf7..a5d966cb8891 100644
--- a/kernel/sched/auto_group.c
+++ b/kernel/sched/auto_group.c
@@ -212,7 +212,7 @@ int proc_sched_autogroup_set_nice(struct task_struct *p, int nice)
ag = autogroup_task_get(p);
down_write(&ag->lock);
- err = sched_group_set_shares(ag->tg, prio_to_weight[nice + 20]);
+ err = sched_group_set_shares(ag->tg, sched_prio_to_weight[nice + 20]);
if (!err)
ag->nice = nice;
up_write(&ag->lock);
diff --git a/kernel/sched/clock.c b/kernel/sched/clock.c
index c0a205101c23..bc54e84675da 100644
--- a/kernel/sched/clock.c
+++ b/kernel/sched/clock.c
@@ -1,7 +1,7 @@
/*
* sched_clock for unstable cpu clocks
*
- * Copyright (C) 2008 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2008 Red Hat, Inc., Peter Zijlstra
*
* Updates and enhancements:
* Copyright (C) 2008 Red Hat, Inc. Steven Rostedt <srostedt@redhat.com>
@@ -354,7 +354,7 @@ void sched_clock_idle_wakeup_event(u64 delta_ns)
return;
sched_clock_tick();
- touch_softlockup_watchdog();
+ touch_softlockup_watchdog_sched();
}
EXPORT_SYMBOL_GPL(sched_clock_idle_wakeup_event);
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 4d568ac9319e..44253adb3c36 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -731,7 +731,7 @@ bool sched_can_stop_tick(void)
if (current->policy == SCHED_RR) {
struct sched_rt_entity *rt_se = &current->rt;
- return rt_se->run_list.prev == rt_se->run_list.next;
+ return list_is_singular(&rt_se->run_list);
}
/*
@@ -823,8 +823,8 @@ static void set_load_weight(struct task_struct *p)
return;
}
- load->weight = scale_load(prio_to_weight[prio]);
- load->inv_weight = prio_to_wmult[prio];
+ load->weight = scale_load(sched_prio_to_weight[prio]);
+ load->inv_weight = sched_prio_to_wmult[prio];
}
static inline void enqueue_task(struct rq *rq, struct task_struct *p, int flags)
@@ -1071,8 +1071,8 @@ static struct rq *move_queued_task(struct rq *rq, struct task_struct *p, int new
{
lockdep_assert_held(&rq->lock);
- dequeue_task(rq, p, 0);
p->on_rq = TASK_ON_RQ_MIGRATING;
+ dequeue_task(rq, p, 0);
set_task_cpu(p, new_cpu);
raw_spin_unlock(&rq->lock);
@@ -1080,8 +1080,8 @@ static struct rq *move_queued_task(struct rq *rq, struct task_struct *p, int new
raw_spin_lock(&rq->lock);
BUG_ON(task_cpu(p) != new_cpu);
- p->on_rq = TASK_ON_RQ_QUEUED;
enqueue_task(rq, p, 0);
+ p->on_rq = TASK_ON_RQ_QUEUED;
check_preempt_curr(rq, p, 0);
return rq;
@@ -1274,6 +1274,15 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu)
WARN_ON_ONCE(p->state != TASK_RUNNING && p->state != TASK_WAKING &&
!p->on_rq);
+ /*
+ * Migrating fair class task must have p->on_rq = TASK_ON_RQ_MIGRATING,
+ * because schedstat_wait_{start,end} rebase migrating task's wait_start
+ * time relying on p->on_rq.
+ */
+ WARN_ON_ONCE(p->state == TASK_RUNNING &&
+ p->sched_class == &fair_sched_class &&
+ (p->on_rq && !task_on_rq_migrating(p)));
+
#ifdef CONFIG_LOCKDEP
/*
* The caller should hold either p->pi_lock or rq->lock, when changing
@@ -1310,9 +1319,11 @@ static void __migrate_swap_task(struct task_struct *p, int cpu)
src_rq = task_rq(p);
dst_rq = cpu_rq(cpu);
+ p->on_rq = TASK_ON_RQ_MIGRATING;
deactivate_task(src_rq, p, 0);
set_task_cpu(p, cpu);
activate_task(dst_rq, p, 0);
+ p->on_rq = TASK_ON_RQ_QUEUED;
check_preempt_curr(dst_rq, p, 0);
} else {
/*
@@ -1905,6 +1916,97 @@ static void ttwu_queue(struct task_struct *p, int cpu)
raw_spin_unlock(&rq->lock);
}
+/*
+ * Notes on Program-Order guarantees on SMP systems.
+ *
+ * MIGRATION
+ *
+ * The basic program-order guarantee on SMP systems is that when a task [t]
+ * migrates, all its activity on its old cpu [c0] happens-before any subsequent
+ * execution on its new cpu [c1].
+ *
+ * For migration (of runnable tasks) this is provided by the following means:
+ *
+ * A) UNLOCK of the rq(c0)->lock scheduling out task t
+ * B) migration for t is required to synchronize *both* rq(c0)->lock and
+ * rq(c1)->lock (if not at the same time, then in that order).
+ * C) LOCK of the rq(c1)->lock scheduling in task
+ *
+ * Transitivity guarantees that B happens after A and C after B.
+ * Note: we only require RCpc transitivity.
+ * Note: the cpu doing B need not be c0 or c1
+ *
+ * Example:
+ *
+ * CPU0 CPU1 CPU2
+ *
+ * LOCK rq(0)->lock
+ * sched-out X
+ * sched-in Y
+ * UNLOCK rq(0)->lock
+ *
+ * LOCK rq(0)->lock // orders against CPU0
+ * dequeue X
+ * UNLOCK rq(0)->lock
+ *
+ * LOCK rq(1)->lock
+ * enqueue X
+ * UNLOCK rq(1)->lock
+ *
+ * LOCK rq(1)->lock // orders against CPU2
+ * sched-out Z
+ * sched-in X
+ * UNLOCK rq(1)->lock
+ *
+ *
+ * BLOCKING -- aka. SLEEP + WAKEUP
+ *
+ * For blocking we (obviously) need to provide the same guarantee as for
+ * migration. However the means are completely different as there is no lock
+ * chain to provide order. Instead we do:
+ *
+ * 1) smp_store_release(X->on_cpu, 0)
+ * 2) smp_cond_acquire(!X->on_cpu)
+ *
+ * Example:
+ *
+ * CPU0 (schedule) CPU1 (try_to_wake_up) CPU2 (schedule)
+ *
+ * LOCK rq(0)->lock LOCK X->pi_lock
+ * dequeue X
+ * sched-out X
+ * smp_store_release(X->on_cpu, 0);
+ *
+ * smp_cond_acquire(!X->on_cpu);
+ * X->state = WAKING
+ * set_task_cpu(X,2)
+ *
+ * LOCK rq(2)->lock
+ * enqueue X
+ * X->state = RUNNING
+ * UNLOCK rq(2)->lock
+ *
+ * LOCK rq(2)->lock // orders against CPU1
+ * sched-out Z
+ * sched-in X
+ * UNLOCK rq(2)->lock
+ *
+ * UNLOCK X->pi_lock
+ * UNLOCK rq(0)->lock
+ *
+ *
+ * However; for wakeups there is a second guarantee we must provide, namely we
+ * must observe the state that lead to our wakeup. That is, not only must our
+ * task observe its own prior state, it must also observe the stores prior to
+ * its wakeup.
+ *
+ * This means that any means of doing remote wakeups must order the CPU doing
+ * the wakeup against the CPU the task is going to end up running on. This,
+ * however, is already required for the regular Program-Order guarantee above,
+ * since the waking CPU is the one issueing the ACQUIRE (smp_cond_acquire).
+ *
+ */
+
/**
* try_to_wake_up - wake up a thread
* @p: the thread to be awakened
@@ -1947,15 +2049,34 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
#ifdef CONFIG_SMP
/*
- * If the owning (remote) cpu is still in the middle of schedule() with
- * this task as prev, wait until its done referencing the task.
+ * Ensure we load p->on_cpu _after_ p->on_rq, otherwise it would be
+ * possible to, falsely, observe p->on_cpu == 0.
+ *
+ * One must be running (->on_cpu == 1) in order to remove oneself
+ * from the runqueue.
+ *
+ * [S] ->on_cpu = 1; [L] ->on_rq
+ * UNLOCK rq->lock
+ * RMB
+ * LOCK rq->lock
+ * [S] ->on_rq = 0; [L] ->on_cpu
+ *
+ * Pairs with the full barrier implied in the UNLOCK+LOCK on rq->lock
+ * from the consecutive calls to schedule(); the first switching to our
+ * task, the second putting it to sleep.
*/
- while (p->on_cpu)
- cpu_relax();
+ smp_rmb();
+
/*
- * Pairs with the smp_wmb() in finish_lock_switch().
+ * If the owning (remote) cpu is still in the middle of schedule() with
+ * this task as prev, wait until its done referencing the task.
+ *
+ * Pairs with the smp_store_release() in finish_lock_switch().
+ *
+ * This ensures that tasks getting woken will be fully ordered against
+ * their previous state and preserve Program Order.
*/
- smp_rmb();
+ smp_cond_acquire(!p->on_cpu);
p->sched_contributes_to_load = !!task_contributes_to_load(p);
p->state = TASK_WAKING;
@@ -2039,7 +2160,6 @@ out:
*/
int wake_up_process(struct task_struct *p)
{
- WARN_ON(task_is_stopped_or_traced(p));
return try_to_wake_up(p, TASK_NORMAL, 0);
}
EXPORT_SYMBOL(wake_up_process);
@@ -2085,6 +2205,10 @@ static void __sched_fork(unsigned long clone_flags, struct task_struct *p)
p->se.vruntime = 0;
INIT_LIST_HEAD(&p->se.group_node);
+#ifdef CONFIG_FAIR_GROUP_SCHED
+ p->se.cfs_rq = NULL;
+#endif
+
#ifdef CONFIG_SCHEDSTATS
memset(&p->se.statistics, 0, sizeof(p->se.statistics));
#endif
@@ -3085,7 +3209,6 @@ static void __sched notrace __schedule(bool preempt)
cpu = smp_processor_id();
rq = cpu_rq(cpu);
- rcu_note_context_switch();
prev = rq->curr;
/*
@@ -3104,13 +3227,16 @@ static void __sched notrace __schedule(bool preempt)
if (sched_feat(HRTICK))
hrtick_clear(rq);
+ local_irq_disable();
+ rcu_note_context_switch();
+
/*
* Make sure that signal_pending_state()->signal_pending() below
* can't be reordered with __set_current_state(TASK_INTERRUPTIBLE)
* done by the caller to avoid the race with signal_wake_up().
*/
smp_mb__before_spinlock();
- raw_spin_lock_irq(&rq->lock);
+ raw_spin_lock(&rq->lock);
lockdep_pin_lock(&rq->lock);
rq->clock_skip_update <<= 1; /* promote REQ to ACT */
@@ -5847,13 +5973,13 @@ static int init_rootdomain(struct root_domain *rd)
{
memset(rd, 0, sizeof(*rd));
- if (!alloc_cpumask_var(&rd->span, GFP_KERNEL))
+ if (!zalloc_cpumask_var(&rd->span, GFP_KERNEL))
goto out;
- if (!alloc_cpumask_var(&rd->online, GFP_KERNEL))
+ if (!zalloc_cpumask_var(&rd->online, GFP_KERNEL))
goto free_span;
- if (!alloc_cpumask_var(&rd->dlo_mask, GFP_KERNEL))
+ if (!zalloc_cpumask_var(&rd->dlo_mask, GFP_KERNEL))
goto free_online;
- if (!alloc_cpumask_var(&rd->rto_mask, GFP_KERNEL))
+ if (!zalloc_cpumask_var(&rd->rto_mask, GFP_KERNEL))
goto free_dlo_mask;
init_dl_bw(&rd->dl_bw);
@@ -7331,6 +7457,9 @@ int in_sched_functions(unsigned long addr)
*/
struct task_group root_task_group;
LIST_HEAD(task_groups);
+
+/* Cacheline aligned slab cache for task_group */
+static struct kmem_cache *task_group_cache __read_mostly;
#endif
DECLARE_PER_CPU(cpumask_var_t, load_balance_mask);
@@ -7388,11 +7517,12 @@ void __init sched_init(void)
#endif /* CONFIG_RT_GROUP_SCHED */
#ifdef CONFIG_CGROUP_SCHED
+ task_group_cache = KMEM_CACHE(task_group, 0);
+
list_add(&root_task_group.list, &task_groups);
INIT_LIST_HEAD(&root_task_group.children);
INIT_LIST_HEAD(&root_task_group.siblings);
autogroup_init(&init_task);
-
#endif /* CONFIG_CGROUP_SCHED */
for_each_possible_cpu(i) {
@@ -7673,7 +7803,7 @@ static void free_sched_group(struct task_group *tg)
free_fair_sched_group(tg);
free_rt_sched_group(tg);
autogroup_free(tg);
- kfree(tg);
+ kmem_cache_free(task_group_cache, tg);
}
/* allocate runqueue etc for a new task group */
@@ -7681,7 +7811,7 @@ struct task_group *sched_create_group(struct task_group *parent)
{
struct task_group *tg;
- tg = kzalloc(sizeof(*tg), GFP_KERNEL);
+ tg = kmem_cache_alloc(task_group_cache, GFP_KERNEL | __GFP_ZERO);
if (!tg)
return ERR_PTR(-ENOMEM);
@@ -8212,17 +8342,17 @@ static void cpu_cgroup_css_offline(struct cgroup_subsys_state *css)
sched_offline_group(tg);
}
-static void cpu_cgroup_fork(struct task_struct *task, void *private)
+static void cpu_cgroup_fork(struct task_struct *task)
{
sched_move_task(task);
}
-static int cpu_cgroup_can_attach(struct cgroup_subsys_state *css,
- struct cgroup_taskset *tset)
+static int cpu_cgroup_can_attach(struct cgroup_taskset *tset)
{
struct task_struct *task;
+ struct cgroup_subsys_state *css;
- cgroup_taskset_for_each(task, tset) {
+ cgroup_taskset_for_each(task, css, tset) {
#ifdef CONFIG_RT_GROUP_SCHED
if (!sched_rt_can_attach(css_tg(css), task))
return -EINVAL;
@@ -8235,12 +8365,12 @@ static int cpu_cgroup_can_attach(struct cgroup_subsys_state *css,
return 0;
}
-static void cpu_cgroup_attach(struct cgroup_subsys_state *css,
- struct cgroup_taskset *tset)
+static void cpu_cgroup_attach(struct cgroup_taskset *tset)
{
struct task_struct *task;
+ struct cgroup_subsys_state *css;
- cgroup_taskset_for_each(task, tset)
+ cgroup_taskset_for_each(task, css, tset)
sched_move_task(task);
}
@@ -8586,3 +8716,44 @@ void dump_cpu_task(int cpu)
pr_info("Task dump for CPU %d:\n", cpu);
sched_show_task(cpu_curr(cpu));
}
+
+/*
+ * Nice levels are multiplicative, with a gentle 10% change for every
+ * nice level changed. I.e. when a CPU-bound task goes from nice 0 to
+ * nice 1, it will get ~10% less CPU time than another CPU-bound task
+ * that remained on nice 0.
+ *
+ * The "10% effect" is relative and cumulative: from _any_ nice level,
+ * if you go up 1 level, it's -10% CPU usage, if you go down 1 level
+ * it's +10% CPU usage. (to achieve that we use a multiplier of 1.25.
+ * If a task goes up by ~10% and another task goes down by ~10% then
+ * the relative distance between them is ~25%.)
+ */
+const int sched_prio_to_weight[40] = {
+ /* -20 */ 88761, 71755, 56483, 46273, 36291,
+ /* -15 */ 29154, 23254, 18705, 14949, 11916,
+ /* -10 */ 9548, 7620, 6100, 4904, 3906,
+ /* -5 */ 3121, 2501, 1991, 1586, 1277,
+ /* 0 */ 1024, 820, 655, 526, 423,
+ /* 5 */ 335, 272, 215, 172, 137,
+ /* 10 */ 110, 87, 70, 56, 45,
+ /* 15 */ 36, 29, 23, 18, 15,
+};
+
+/*
+ * Inverse (2^32/x) values of the sched_prio_to_weight[] array, precalculated.
+ *
+ * In cases where the weight does not change often, we can use the
+ * precalculated inverse to speed up arithmetics by turning divisions
+ * into multiplications:
+ */
+const u32 sched_prio_to_wmult[40] = {
+ /* -20 */ 48388, 59856, 76040, 92818, 118348,
+ /* -15 */ 147320, 184698, 229616, 287308, 360437,
+ /* -10 */ 449829, 563644, 704093, 875809, 1099582,
+ /* -5 */ 1376151, 1717300, 2157191, 2708050, 3363326,
+ /* 0 */ 4194304, 5237765, 6557202, 8165337, 10153587,
+ /* 5 */ 12820798, 15790321, 19976592, 24970740, 31350126,
+ /* 10 */ 39045157, 49367440, 61356676, 76695844, 95443717,
+ /* 15 */ 119304647, 148102320, 186737708, 238609294, 286331153,
+};
diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c
index 26a54461bf59..b2ab2ffb1adc 100644
--- a/kernel/sched/cputime.c
+++ b/kernel/sched/cputime.c
@@ -5,6 +5,9 @@
#include <linux/static_key.h>
#include <linux/context_tracking.h>
#include "sched.h"
+#ifdef CONFIG_PARAVIRT
+#include <asm/paravirt.h>
+#endif
#ifdef CONFIG_IRQ_TIME_ACCOUNTING
@@ -466,7 +469,7 @@ void account_process_tick(struct task_struct *p, int user_tick)
cputime_t one_jiffy_scaled = cputime_to_scaled(cputime_one_jiffy);
struct rq *rq = this_rq();
- if (vtime_accounting_enabled())
+ if (vtime_accounting_cpu_enabled())
return;
if (sched_clock_irqtime) {
@@ -680,7 +683,7 @@ static cputime_t get_vtime_delta(struct task_struct *tsk)
{
unsigned long long delta = vtime_delta(tsk);
- WARN_ON_ONCE(tsk->vtime_snap_whence == VTIME_SLEEPING);
+ WARN_ON_ONCE(tsk->vtime_snap_whence == VTIME_INACTIVE);
tsk->vtime_snap += delta;
/* CHECKME: always safe to convert nsecs to cputime? */
@@ -696,37 +699,37 @@ static void __vtime_account_system(struct task_struct *tsk)
void vtime_account_system(struct task_struct *tsk)
{
- write_seqlock(&tsk->vtime_seqlock);
+ write_seqcount_begin(&tsk->vtime_seqcount);
__vtime_account_system(tsk);
- write_sequnlock(&tsk->vtime_seqlock);
+ write_seqcount_end(&tsk->vtime_seqcount);
}
void vtime_gen_account_irq_exit(struct task_struct *tsk)
{
- write_seqlock(&tsk->vtime_seqlock);
+ write_seqcount_begin(&tsk->vtime_seqcount);
__vtime_account_system(tsk);
if (context_tracking_in_user())
tsk->vtime_snap_whence = VTIME_USER;
- write_sequnlock(&tsk->vtime_seqlock);
+ write_seqcount_end(&tsk->vtime_seqcount);
}
void vtime_account_user(struct task_struct *tsk)
{
cputime_t delta_cpu;
- write_seqlock(&tsk->vtime_seqlock);
+ write_seqcount_begin(&tsk->vtime_seqcount);
delta_cpu = get_vtime_delta(tsk);
tsk->vtime_snap_whence = VTIME_SYS;
account_user_time(tsk, delta_cpu, cputime_to_scaled(delta_cpu));
- write_sequnlock(&tsk->vtime_seqlock);
+ write_seqcount_end(&tsk->vtime_seqcount);
}
void vtime_user_enter(struct task_struct *tsk)
{
- write_seqlock(&tsk->vtime_seqlock);
+ write_seqcount_begin(&tsk->vtime_seqcount);
__vtime_account_system(tsk);
tsk->vtime_snap_whence = VTIME_USER;
- write_sequnlock(&tsk->vtime_seqlock);
+ write_seqcount_end(&tsk->vtime_seqcount);
}
void vtime_guest_enter(struct task_struct *tsk)
@@ -738,19 +741,19 @@ void vtime_guest_enter(struct task_struct *tsk)
* synchronization against the reader (task_gtime())
* that can thus safely catch up with a tickless delta.
*/
- write_seqlock(&tsk->vtime_seqlock);
+ write_seqcount_begin(&tsk->vtime_seqcount);
__vtime_account_system(tsk);
current->flags |= PF_VCPU;
- write_sequnlock(&tsk->vtime_seqlock);
+ write_seqcount_end(&tsk->vtime_seqcount);
}
EXPORT_SYMBOL_GPL(vtime_guest_enter);
void vtime_guest_exit(struct task_struct *tsk)
{
- write_seqlock(&tsk->vtime_seqlock);
+ write_seqcount_begin(&tsk->vtime_seqcount);
__vtime_account_system(tsk);
current->flags &= ~PF_VCPU;
- write_sequnlock(&tsk->vtime_seqlock);
+ write_seqcount_end(&tsk->vtime_seqcount);
}
EXPORT_SYMBOL_GPL(vtime_guest_exit);
@@ -763,24 +766,26 @@ void vtime_account_idle(struct task_struct *tsk)
void arch_vtime_task_switch(struct task_struct *prev)
{
- write_seqlock(&prev->vtime_seqlock);
- prev->vtime_snap_whence = VTIME_SLEEPING;
- write_sequnlock(&prev->vtime_seqlock);
+ write_seqcount_begin(&prev->vtime_seqcount);
+ prev->vtime_snap_whence = VTIME_INACTIVE;
+ write_seqcount_end(&prev->vtime_seqcount);
- write_seqlock(&current->vtime_seqlock);
+ write_seqcount_begin(&current->vtime_seqcount);
current->vtime_snap_whence = VTIME_SYS;
current->vtime_snap = sched_clock_cpu(smp_processor_id());
- write_sequnlock(&current->vtime_seqlock);
+ write_seqcount_end(&current->vtime_seqcount);
}
void vtime_init_idle(struct task_struct *t, int cpu)
{
unsigned long flags;
- write_seqlock_irqsave(&t->vtime_seqlock, flags);
+ local_irq_save(flags);
+ write_seqcount_begin(&t->vtime_seqcount);
t->vtime_snap_whence = VTIME_SYS;
t->vtime_snap = sched_clock_cpu(cpu);
- write_sequnlock_irqrestore(&t->vtime_seqlock, flags);
+ write_seqcount_end(&t->vtime_seqcount);
+ local_irq_restore(flags);
}
cputime_t task_gtime(struct task_struct *t)
@@ -788,14 +793,17 @@ cputime_t task_gtime(struct task_struct *t)
unsigned int seq;
cputime_t gtime;
+ if (!vtime_accounting_enabled())
+ return t->gtime;
+
do {
- seq = read_seqbegin(&t->vtime_seqlock);
+ seq = read_seqcount_begin(&t->vtime_seqcount);
gtime = t->gtime;
- if (t->flags & PF_VCPU)
+ if (t->vtime_snap_whence == VTIME_SYS && t->flags & PF_VCPU)
gtime += vtime_delta(t);
- } while (read_seqretry(&t->vtime_seqlock, seq));
+ } while (read_seqcount_retry(&t->vtime_seqcount, seq));
return gtime;
}
@@ -818,7 +826,7 @@ fetch_task_cputime(struct task_struct *t,
*udelta = 0;
*sdelta = 0;
- seq = read_seqbegin(&t->vtime_seqlock);
+ seq = read_seqcount_begin(&t->vtime_seqcount);
if (u_dst)
*u_dst = *u_src;
@@ -826,7 +834,7 @@ fetch_task_cputime(struct task_struct *t,
*s_dst = *s_src;
/* Task is sleeping, nothing to add */
- if (t->vtime_snap_whence == VTIME_SLEEPING ||
+ if (t->vtime_snap_whence == VTIME_INACTIVE ||
is_idle_task(t))
continue;
@@ -842,7 +850,7 @@ fetch_task_cputime(struct task_struct *t,
if (t->vtime_snap_whence == VTIME_SYS)
*sdelta = delta;
}
- } while (read_seqretry(&t->vtime_seqlock, seq));
+ } while (read_seqcount_retry(&t->vtime_seqcount, seq));
}
@@ -850,6 +858,14 @@ void task_cputime(struct task_struct *t, cputime_t *utime, cputime_t *stime)
{
cputime_t udelta, sdelta;
+ if (!vtime_accounting_enabled()) {
+ if (utime)
+ *utime = t->utime;
+ if (stime)
+ *stime = t->stime;
+ return;
+ }
+
fetch_task_cputime(t, utime, stime, &t->utime,
&t->stime, &udelta, &sdelta);
if (utime)
@@ -863,6 +879,14 @@ void task_cputime_scaled(struct task_struct *t,
{
cputime_t udelta, sdelta;
+ if (!vtime_accounting_enabled()) {
+ if (utimescaled)
+ *utimescaled = t->utimescaled;
+ if (stimescaled)
+ *stimescaled = t->stimescaled;
+ return;
+ }
+
fetch_task_cputime(t, utimescaled, stimescaled,
&t->utimescaled, &t->stimescaled, &udelta, &sdelta);
if (utimescaled)
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index 8b0a15e285f9..cd64c979d0e1 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -176,8 +176,10 @@ static void enqueue_pushable_dl_task(struct rq *rq, struct task_struct *p)
}
}
- if (leftmost)
+ if (leftmost) {
dl_rq->pushable_dl_tasks_leftmost = &p->pushable_dl_tasks;
+ dl_rq->earliest_dl.next = p->dl.deadline;
+ }
rb_link_node(&p->pushable_dl_tasks, parent, link);
rb_insert_color(&p->pushable_dl_tasks, &dl_rq->pushable_dl_tasks_root);
@@ -195,6 +197,10 @@ static void dequeue_pushable_dl_task(struct rq *rq, struct task_struct *p)
next_node = rb_next(&p->pushable_dl_tasks);
dl_rq->pushable_dl_tasks_leftmost = next_node;
+ if (next_node) {
+ dl_rq->earliest_dl.next = rb_entry(next_node,
+ struct task_struct, pushable_dl_tasks)->dl.deadline;
+ }
}
rb_erase(&p->pushable_dl_tasks, &dl_rq->pushable_dl_tasks_root);
@@ -782,42 +788,14 @@ static void update_curr_dl(struct rq *rq)
#ifdef CONFIG_SMP
-static struct task_struct *pick_next_earliest_dl_task(struct rq *rq, int cpu);
-
-static inline u64 next_deadline(struct rq *rq)
-{
- struct task_struct *next = pick_next_earliest_dl_task(rq, rq->cpu);
-
- if (next && dl_prio(next->prio))
- return next->dl.deadline;
- else
- return 0;
-}
-
static void inc_dl_deadline(struct dl_rq *dl_rq, u64 deadline)
{
struct rq *rq = rq_of_dl_rq(dl_rq);
if (dl_rq->earliest_dl.curr == 0 ||
dl_time_before(deadline, dl_rq->earliest_dl.curr)) {
- /*
- * If the dl_rq had no -deadline tasks, or if the new task
- * has shorter deadline than the current one on dl_rq, we
- * know that the previous earliest becomes our next earliest,
- * as the new task becomes the earliest itself.
- */
- dl_rq->earliest_dl.next = dl_rq->earliest_dl.curr;
dl_rq->earliest_dl.curr = deadline;
cpudl_set(&rq->rd->cpudl, rq->cpu, deadline, 1);
- } else if (dl_rq->earliest_dl.next == 0 ||
- dl_time_before(deadline, dl_rq->earliest_dl.next)) {
- /*
- * On the other hand, if the new -deadline task has a
- * a later deadline than the earliest one on dl_rq, but
- * it is earlier than the next (if any), we must
- * recompute the next-earliest.
- */
- dl_rq->earliest_dl.next = next_deadline(rq);
}
}
@@ -839,7 +817,6 @@ static void dec_dl_deadline(struct dl_rq *dl_rq, u64 deadline)
entry = rb_entry(leftmost, struct sched_dl_entity, rb_node);
dl_rq->earliest_dl.curr = entry->deadline;
- dl_rq->earliest_dl.next = next_deadline(rq);
cpudl_set(&rq->rd->cpudl, rq->cpu, entry->deadline, 1);
}
}
@@ -1274,28 +1251,6 @@ static int pick_dl_task(struct rq *rq, struct task_struct *p, int cpu)
return 0;
}
-/* Returns the second earliest -deadline task, NULL otherwise */
-static struct task_struct *pick_next_earliest_dl_task(struct rq *rq, int cpu)
-{
- struct rb_node *next_node = rq->dl.rb_leftmost;
- struct sched_dl_entity *dl_se;
- struct task_struct *p = NULL;
-
-next_node:
- next_node = rb_next(next_node);
- if (next_node) {
- dl_se = rb_entry(next_node, struct sched_dl_entity, rb_node);
- p = dl_task_of(dl_se);
-
- if (pick_dl_task(rq, p, cpu))
- return p;
-
- goto next_node;
- }
-
- return NULL;
-}
-
/*
* Return the earliest pushable rq's task, which is suitable to be executed
* on the CPU, NULL otherwise:
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index f04fda8f669c..1926606ece80 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -17,7 +17,7 @@
* Copyright (C) 2007, Thomas Gleixner <tglx@linutronix.de>
*
* Adaptive scheduling granularity, math enhancements by Peter Zijlstra
- * Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra
*/
#include <linux/latencytop.h>
@@ -738,12 +738,56 @@ static void update_curr_fair(struct rq *rq)
update_curr(cfs_rq_of(&rq->curr->se));
}
+#ifdef CONFIG_SCHEDSTATS
+static inline void
+update_stats_wait_start(struct cfs_rq *cfs_rq, struct sched_entity *se)
+{
+ u64 wait_start = rq_clock(rq_of(cfs_rq));
+
+ if (entity_is_task(se) && task_on_rq_migrating(task_of(se)) &&
+ likely(wait_start > se->statistics.wait_start))
+ wait_start -= se->statistics.wait_start;
+
+ se->statistics.wait_start = wait_start;
+}
+
+static void
+update_stats_wait_end(struct cfs_rq *cfs_rq, struct sched_entity *se)
+{
+ struct task_struct *p;
+ u64 delta = rq_clock(rq_of(cfs_rq)) - se->statistics.wait_start;
+
+ if (entity_is_task(se)) {
+ p = task_of(se);
+ if (task_on_rq_migrating(p)) {
+ /*
+ * Preserve migrating task's wait time so wait_start
+ * time stamp can be adjusted to accumulate wait time
+ * prior to migration.
+ */
+ se->statistics.wait_start = delta;
+ return;
+ }
+ trace_sched_stat_wait(p, delta);
+ }
+
+ se->statistics.wait_max = max(se->statistics.wait_max, delta);
+ se->statistics.wait_count++;
+ se->statistics.wait_sum += delta;
+ se->statistics.wait_start = 0;
+}
+#else
static inline void
update_stats_wait_start(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
- schedstat_set(se->statistics.wait_start, rq_clock(rq_of(cfs_rq)));
}
+static inline void
+update_stats_wait_end(struct cfs_rq *cfs_rq, struct sched_entity *se)
+{
+}
+#endif
+
/*
* Task is being enqueued - update stats:
*/
@@ -757,23 +801,6 @@ static void update_stats_enqueue(struct cfs_rq *cfs_rq, struct sched_entity *se)
update_stats_wait_start(cfs_rq, se);
}
-static void
-update_stats_wait_end(struct cfs_rq *cfs_rq, struct sched_entity *se)
-{
- schedstat_set(se->statistics.wait_max, max(se->statistics.wait_max,
- rq_clock(rq_of(cfs_rq)) - se->statistics.wait_start));
- schedstat_set(se->statistics.wait_count, se->statistics.wait_count + 1);
- schedstat_set(se->statistics.wait_sum, se->statistics.wait_sum +
- rq_clock(rq_of(cfs_rq)) - se->statistics.wait_start);
-#ifdef CONFIG_SCHEDSTATS
- if (entity_is_task(se)) {
- trace_sched_stat_wait(task_of(se),
- rq_clock(rq_of(cfs_rq)) - se->statistics.wait_start);
- }
-#endif
- schedstat_set(se->statistics.wait_start, 0);
-}
-
static inline void
update_stats_dequeue(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
@@ -2155,6 +2182,7 @@ void task_numa_work(struct callback_head *work)
unsigned long migrate, next_scan, now = jiffies;
struct task_struct *p = current;
struct mm_struct *mm = p->mm;
+ u64 runtime = p->se.sum_exec_runtime;
struct vm_area_struct *vma;
unsigned long start, end;
unsigned long nr_pte_updates = 0;
@@ -2277,6 +2305,17 @@ out:
else
reset_ptenuma_scan(p);
up_read(&mm->mmap_sem);
+
+ /*
+ * Make sure tasks use at least 32x as much time to run other code
+ * than they used here, to limit NUMA PTE scanning overhead to 3% max.
+ * Usually update_task_scan_period slows down scanning enough; on an
+ * overloaded system we need to limit overhead on a per task basis.
+ */
+ if (unlikely(p->se.sum_exec_runtime != runtime)) {
+ u64 diff = p->se.sum_exec_runtime - runtime;
+ p->node_stamp += 32 * diff;
+ }
}
/*
@@ -2670,12 +2709,64 @@ static inline void update_tg_load_avg(struct cfs_rq *cfs_rq, int force)
{
long delta = cfs_rq->avg.load_avg - cfs_rq->tg_load_avg_contrib;
+ /*
+ * No need to update load_avg for root_task_group as it is not used.
+ */
+ if (cfs_rq->tg == &root_task_group)
+ return;
+
if (force || abs(delta) > cfs_rq->tg_load_avg_contrib / 64) {
atomic_long_add(delta, &cfs_rq->tg->load_avg);
cfs_rq->tg_load_avg_contrib = cfs_rq->avg.load_avg;
}
}
+/*
+ * Called within set_task_rq() right before setting a task's cpu. The
+ * caller only guarantees p->pi_lock is held; no other assumptions,
+ * including the state of rq->lock, should be made.
+ */
+void set_task_rq_fair(struct sched_entity *se,
+ struct cfs_rq *prev, struct cfs_rq *next)
+{
+ if (!sched_feat(ATTACH_AGE_LOAD))
+ return;
+
+ /*
+ * We are supposed to update the task to "current" time, then its up to
+ * date and ready to go to new CPU/cfs_rq. But we have difficulty in
+ * getting what current time is, so simply throw away the out-of-date
+ * time. This will result in the wakee task is less decayed, but giving
+ * the wakee more load sounds not bad.
+ */
+ if (se->avg.last_update_time && prev) {
+ u64 p_last_update_time;
+ u64 n_last_update_time;
+
+#ifndef CONFIG_64BIT
+ u64 p_last_update_time_copy;
+ u64 n_last_update_time_copy;
+
+ do {
+ p_last_update_time_copy = prev->load_last_update_time_copy;
+ n_last_update_time_copy = next->load_last_update_time_copy;
+
+ smp_rmb();
+
+ p_last_update_time = prev->avg.last_update_time;
+ n_last_update_time = next->avg.last_update_time;
+
+ } while (p_last_update_time != p_last_update_time_copy ||
+ n_last_update_time != n_last_update_time_copy);
+#else
+ p_last_update_time = prev->avg.last_update_time;
+ n_last_update_time = next->avg.last_update_time;
+#endif
+ __update_load_avg(p_last_update_time, cpu_of(rq_of(prev)),
+ &se->avg, 0, 0, NULL);
+ se->avg.last_update_time = n_last_update_time;
+ }
+}
#else /* CONFIG_FAIR_GROUP_SCHED */
static inline void update_tg_load_avg(struct cfs_rq *cfs_rq, int force) {}
#endif /* CONFIG_FAIR_GROUP_SCHED */
@@ -2689,7 +2780,7 @@ static inline int update_cfs_rq_load_avg(u64 now, struct cfs_rq *cfs_rq)
int decayed, removed = 0;
if (atomic_long_read(&cfs_rq->removed_load_avg)) {
- long r = atomic_long_xchg(&cfs_rq->removed_load_avg, 0);
+ s64 r = atomic_long_xchg(&cfs_rq->removed_load_avg, 0);
sa->load_avg = max_t(long, sa->load_avg - r, 0);
sa->load_sum = max_t(s64, sa->load_sum - r * LOAD_AVG_MAX, 0);
removed = 1;
@@ -2809,48 +2900,48 @@ dequeue_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se)
max_t(s64, cfs_rq->runnable_load_sum - se->avg.load_sum, 0);
}
-/*
- * Task first catches up with cfs_rq, and then subtract
- * itself from the cfs_rq (task must be off the queue now).
- */
-void remove_entity_load_avg(struct sched_entity *se)
-{
- struct cfs_rq *cfs_rq = cfs_rq_of(se);
- u64 last_update_time;
-
#ifndef CONFIG_64BIT
+static inline u64 cfs_rq_last_update_time(struct cfs_rq *cfs_rq)
+{
u64 last_update_time_copy;
+ u64 last_update_time;
do {
last_update_time_copy = cfs_rq->load_last_update_time_copy;
smp_rmb();
last_update_time = cfs_rq->avg.last_update_time;
} while (last_update_time != last_update_time_copy);
-#else
- last_update_time = cfs_rq->avg.last_update_time;
-#endif
- __update_load_avg(last_update_time, cpu_of(rq_of(cfs_rq)), &se->avg, 0, 0, NULL);
- atomic_long_add(se->avg.load_avg, &cfs_rq->removed_load_avg);
- atomic_long_add(se->avg.util_avg, &cfs_rq->removed_util_avg);
+ return last_update_time;
}
-
-/*
- * Update the rq's load with the elapsed running time before entering
- * idle. if the last scheduled task is not a CFS task, idle_enter will
- * be the only way to update the runnable statistic.
- */
-void idle_enter_fair(struct rq *this_rq)
+#else
+static inline u64 cfs_rq_last_update_time(struct cfs_rq *cfs_rq)
{
+ return cfs_rq->avg.last_update_time;
}
+#endif
/*
- * Update the rq's load with the elapsed idle time before a task is
- * scheduled. if the newly scheduled task is not a CFS task, idle_exit will
- * be the only way to update the runnable statistic.
+ * Task first catches up with cfs_rq, and then subtract
+ * itself from the cfs_rq (task must be off the queue now).
*/
-void idle_exit_fair(struct rq *this_rq)
+void remove_entity_load_avg(struct sched_entity *se)
{
+ struct cfs_rq *cfs_rq = cfs_rq_of(se);
+ u64 last_update_time;
+
+ /*
+ * Newly created task or never used group entity should not be removed
+ * from its (source) cfs_rq
+ */
+ if (se->avg.last_update_time == 0)
+ return;
+
+ last_update_time = cfs_rq_last_update_time(cfs_rq);
+
+ __update_load_avg(last_update_time, cpu_of(rq_of(cfs_rq)), &se->avg, 0, 0, NULL);
+ atomic_long_add(se->avg.load_avg, &cfs_rq->removed_load_avg);
+ atomic_long_add(se->avg.util_avg, &cfs_rq->removed_util_avg);
}
static inline unsigned long cfs_rq_runnable_load_avg(struct cfs_rq *cfs_rq)
@@ -4240,42 +4331,37 @@ static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags)
*/
/*
- * The exact cpuload at various idx values, calculated at every tick would be
- * load = (2^idx - 1) / 2^idx * load + 1 / 2^idx * cur_load
+ * The exact cpuload calculated at every tick would be:
+ *
+ * load' = (1 - 1/2^i) * load + (1/2^i) * cur_load
*
- * If a cpu misses updates for n-1 ticks (as it was idle) and update gets called
- * on nth tick when cpu may be busy, then we have:
- * load = ((2^idx - 1) / 2^idx)^(n-1) * load
- * load = (2^idx - 1) / 2^idx) * load + 1 / 2^idx * cur_load
+ * If a cpu misses updates for n ticks (as it was idle) and update gets
+ * called on the n+1-th tick when cpu may be busy, then we have:
+ *
+ * load_n = (1 - 1/2^i)^n * load_0
+ * load_n+1 = (1 - 1/2^i) * load_n + (1/2^i) * cur_load
*
* decay_load_missed() below does efficient calculation of
- * load = ((2^idx - 1) / 2^idx)^(n-1) * load
- * avoiding 0..n-1 loop doing load = ((2^idx - 1) / 2^idx) * load
+ *
+ * load' = (1 - 1/2^i)^n * load
+ *
+ * Because x^(n+m) := x^n * x^m we can decompose any x^n in power-of-2 factors.
+ * This allows us to precompute the above in said factors, thereby allowing the
+ * reduction of an arbitrary n in O(log_2 n) steps. (See also
+ * fixed_power_int())
*
* The calculation is approximated on a 128 point scale.
- * degrade_zero_ticks is the number of ticks after which load at any
- * particular idx is approximated to be zero.
- * degrade_factor is a precomputed table, a row for each load idx.
- * Each column corresponds to degradation factor for a power of two ticks,
- * based on 128 point scale.
- * Example:
- * row 2, col 3 (=12) says that the degradation at load idx 2 after
- * 8 ticks is 12/128 (which is an approximation of exact factor 3^8/4^8).
- *
- * With this power of 2 load factors, we can degrade the load n times
- * by looking at 1 bits in n and doing as many mult/shift instead of
- * n mult/shifts needed by the exact degradation.
*/
#define DEGRADE_SHIFT 7
-static const unsigned char
- degrade_zero_ticks[CPU_LOAD_IDX_MAX] = {0, 8, 32, 64, 128};
-static const unsigned char
- degrade_factor[CPU_LOAD_IDX_MAX][DEGRADE_SHIFT + 1] = {
- {0, 0, 0, 0, 0, 0, 0, 0},
- {64, 32, 8, 0, 0, 0, 0, 0},
- {96, 72, 40, 12, 1, 0, 0},
- {112, 98, 75, 43, 15, 1, 0},
- {120, 112, 98, 76, 45, 16, 2} };
+
+static const u8 degrade_zero_ticks[CPU_LOAD_IDX_MAX] = {0, 8, 32, 64, 128};
+static const u8 degrade_factor[CPU_LOAD_IDX_MAX][DEGRADE_SHIFT + 1] = {
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 64, 32, 8, 0, 0, 0, 0, 0 },
+ { 96, 72, 40, 12, 1, 0, 0, 0 },
+ { 112, 98, 75, 43, 15, 1, 0, 0 },
+ { 120, 112, 98, 76, 45, 16, 2, 0 }
+};
/*
* Update cpu_load for any missed ticks, due to tickless idle. The backlog
@@ -4306,14 +4392,46 @@ decay_load_missed(unsigned long load, unsigned long missed_updates, int idx)
return load;
}
-/*
+/**
+ * __update_cpu_load - update the rq->cpu_load[] statistics
+ * @this_rq: The rq to update statistics for
+ * @this_load: The current load
+ * @pending_updates: The number of missed updates
+ * @active: !0 for NOHZ_FULL
+ *
* Update rq->cpu_load[] statistics. This function is usually called every
- * scheduler tick (TICK_NSEC). With tickless idle this will not be called
- * every tick. We fix it up based on jiffies.
+ * scheduler tick (TICK_NSEC).
+ *
+ * This function computes a decaying average:
+ *
+ * load[i]' = (1 - 1/2^i) * load[i] + (1/2^i) * load
+ *
+ * Because of NOHZ it might not get called on every tick which gives need for
+ * the @pending_updates argument.
+ *
+ * load[i]_n = (1 - 1/2^i) * load[i]_n-1 + (1/2^i) * load_n-1
+ * = A * load[i]_n-1 + B ; A := (1 - 1/2^i), B := (1/2^i) * load
+ * = A * (A * load[i]_n-2 + B) + B
+ * = A * (A * (A * load[i]_n-3 + B) + B) + B
+ * = A^3 * load[i]_n-3 + (A^2 + A + 1) * B
+ * = A^n * load[i]_0 + (A^(n-1) + A^(n-2) + ... + 1) * B
+ * = A^n * load[i]_0 + ((1 - A^n) / (1 - A)) * B
+ * = (1 - 1/2^i)^n * (load[i]_0 - load) + load
+ *
+ * In the above we've assumed load_n := load, which is true for NOHZ_FULL as
+ * any change in load would have resulted in the tick being turned back on.
+ *
+ * For regular NOHZ, this reduces to:
+ *
+ * load[i]_n = (1 - 1/2^i)^n * load[i]_0
+ *
+ * see decay_load_misses(). For NOHZ_FULL we get to subtract and add the extra
+ * term. See the @active paramter.
*/
static void __update_cpu_load(struct rq *this_rq, unsigned long this_load,
- unsigned long pending_updates)
+ unsigned long pending_updates, int active)
{
+ unsigned long tickless_load = active ? this_rq->cpu_load[0] : 0;
int i, scale;
this_rq->nr_load_updates++;
@@ -4325,8 +4443,9 @@ static void __update_cpu_load(struct rq *this_rq, unsigned long this_load,
/* scale is effectively 1 << i now, and >> i divides by scale */
- old_load = this_rq->cpu_load[i];
+ old_load = this_rq->cpu_load[i] - tickless_load;
old_load = decay_load_missed(old_load, pending_updates - 1, i);
+ old_load += tickless_load;
new_load = this_load;
/*
* Round up the averaging division if load is increasing. This
@@ -4381,16 +4500,17 @@ static void update_idle_cpu_load(struct rq *this_rq)
pending_updates = curr_jiffies - this_rq->last_load_update_tick;
this_rq->last_load_update_tick = curr_jiffies;
- __update_cpu_load(this_rq, load, pending_updates);
+ __update_cpu_load(this_rq, load, pending_updates, 0);
}
/*
* Called from tick_nohz_idle_exit() -- try and fix up the ticks we missed.
*/
-void update_cpu_load_nohz(void)
+void update_cpu_load_nohz(int active)
{
struct rq *this_rq = this_rq();
unsigned long curr_jiffies = READ_ONCE(jiffies);
+ unsigned long load = active ? weighted_cpuload(cpu_of(this_rq)) : 0;
unsigned long pending_updates;
if (curr_jiffies == this_rq->last_load_update_tick)
@@ -4401,10 +4521,11 @@ void update_cpu_load_nohz(void)
if (pending_updates) {
this_rq->last_load_update_tick = curr_jiffies;
/*
- * We were idle, this means load 0, the current load might be
- * !0 due to remote wakeups and the sort.
+ * In the regular NOHZ case, we were idle, this means load 0.
+ * In the NOHZ_FULL case, we were non-idle, we should consider
+ * its weighted load.
*/
- __update_cpu_load(this_rq, 0, pending_updates);
+ __update_cpu_load(this_rq, load, pending_updates, active);
}
raw_spin_unlock(&this_rq->lock);
}
@@ -4420,7 +4541,7 @@ void update_cpu_load_active(struct rq *this_rq)
* See the mess around update_idle_cpu_load() / update_cpu_load_nohz().
*/
this_rq->last_load_update_tick = jiffies;
- __update_cpu_load(this_rq, load, 1);
+ __update_cpu_load(this_rq, load, 1, 1);
}
/*
@@ -5007,8 +5128,7 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f
/*
* Called immediately before a task is migrated to a new cpu; task_cpu(p) and
* cfs_rq_of(p) references at time of call are still valid and identify the
- * previous cpu. However, the caller only guarantees p->pi_lock is held; no
- * other assumptions, including the state of rq->lock, should be made.
+ * previous cpu. The caller guarantees p->pi_lock or task_rq(p)->lock is held.
*/
static void migrate_task_rq_fair(struct task_struct *p)
{
@@ -5721,8 +5841,8 @@ static void detach_task(struct task_struct *p, struct lb_env *env)
{
lockdep_assert_held(&env->src_rq->lock);
- deactivate_task(env->src_rq, p, 0);
p->on_rq = TASK_ON_RQ_MIGRATING;
+ deactivate_task(env->src_rq, p, 0);
set_task_cpu(p, env->dst_cpu);
}
@@ -5855,8 +5975,8 @@ static void attach_task(struct rq *rq, struct task_struct *p)
lockdep_assert_held(&rq->lock);
BUG_ON(task_rq(p) != rq);
- p->on_rq = TASK_ON_RQ_QUEUED;
activate_task(rq, p, 0);
+ p->on_rq = TASK_ON_RQ_QUEUED;
check_preempt_curr(rq, p, 0);
}
@@ -6302,7 +6422,7 @@ static inline void update_sg_lb_stats(struct lb_env *env,
bool *overload)
{
unsigned long load;
- int i;
+ int i, nr_running;
memset(sgs, 0, sizeof(*sgs));
@@ -6319,7 +6439,8 @@ static inline void update_sg_lb_stats(struct lb_env *env,
sgs->group_util += cpu_util(i);
sgs->sum_nr_running += rq->cfs.h_nr_running;
- if (rq->nr_running > 1)
+ nr_running = rq->nr_running;
+ if (nr_running > 1)
*overload = true;
#ifdef CONFIG_NUMA_BALANCING
@@ -6327,7 +6448,10 @@ static inline void update_sg_lb_stats(struct lb_env *env,
sgs->nr_preferred_running += rq->nr_preferred_running;
#endif
sgs->sum_weighted_load += weighted_cpuload(i);
- if (idle_cpu(i))
+ /*
+ * No need to call idle_cpu() if nr_running is not 0
+ */
+ if (!nr_running && idle_cpu(i))
sgs->idle_cpus++;
}
@@ -7248,8 +7372,6 @@ static int idle_balance(struct rq *this_rq)
int pulled_task = 0;
u64 curr_cost = 0;
- idle_enter_fair(this_rq);
-
/*
* We must set idle_stamp _before_ calling idle_balance(), such that we
* measure the duration of idle_balance() as idle time.
@@ -7330,10 +7452,8 @@ out:
if (this_rq->nr_running != this_rq->cfs.h_nr_running)
pulled_task = -1;
- if (pulled_task) {
- idle_exit_fair(this_rq);
+ if (pulled_task)
this_rq->idle_stamp = 0;
- }
return pulled_task;
}
diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c
index 4a2ef5a02fd3..2489140a7c51 100644
--- a/kernel/sched/idle.c
+++ b/kernel/sched/idle.c
@@ -219,6 +219,7 @@ static void cpu_idle_loop(void)
*/
__current_set_polling();
+ quiet_vmstat();
tick_nohz_idle_enter();
while (!need_resched()) {
diff --git a/kernel/sched/idle_task.c b/kernel/sched/idle_task.c
index c4ae0f1fdf9b..47ce94931f1b 100644
--- a/kernel/sched/idle_task.c
+++ b/kernel/sched/idle_task.c
@@ -47,7 +47,6 @@ dequeue_task_idle(struct rq *rq, struct task_struct *p, int flags)
static void put_prev_task_idle(struct rq *rq, struct task_struct *prev)
{
- idle_exit_fair(rq);
rq_last_tick_reset(rq);
}
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index e3cc16312046..8ec86abe0ea1 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -64,7 +64,7 @@ static void start_rt_bandwidth(struct rt_bandwidth *rt_b)
raw_spin_unlock(&rt_b->rt_runtime_lock);
}
-#ifdef CONFIG_SMP
+#if defined(CONFIG_SMP) && defined(HAVE_RT_PUSH_IPI)
static void push_irq_work_func(struct irq_work *work);
#endif
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index efd3bfc7e347..10f16374df7f 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -248,7 +248,12 @@ struct task_group {
unsigned long shares;
#ifdef CONFIG_SMP
- atomic_long_t load_avg;
+ /*
+ * load_avg can be heavily contended at clock tick time, so put
+ * it in its own cacheline separated from the fields above which
+ * will also be accessed at each tick.
+ */
+ atomic_long_t load_avg ____cacheline_aligned;
#endif
#endif
@@ -335,7 +340,15 @@ extern void sched_move_task(struct task_struct *tsk);
#ifdef CONFIG_FAIR_GROUP_SCHED
extern int sched_group_set_shares(struct task_group *tg, unsigned long shares);
-#endif
+
+#ifdef CONFIG_SMP
+extern void set_task_rq_fair(struct sched_entity *se,
+ struct cfs_rq *prev, struct cfs_rq *next);
+#else /* !CONFIG_SMP */
+static inline void set_task_rq_fair(struct sched_entity *se,
+ struct cfs_rq *prev, struct cfs_rq *next) { }
+#endif /* CONFIG_SMP */
+#endif /* CONFIG_FAIR_GROUP_SCHED */
#else /* CONFIG_CGROUP_SCHED */
@@ -933,6 +946,7 @@ static inline void set_task_rq(struct task_struct *p, unsigned int cpu)
#endif
#ifdef CONFIG_FAIR_GROUP_SCHED
+ set_task_rq_fair(&p->se, p->se.cfs_rq, tg->cfs_rq[cpu]);
p->se.cfs_rq = tg->cfs_rq[cpu];
p->se.parent = tg->se[cpu];
#endif
@@ -1073,7 +1087,10 @@ static inline void finish_lock_switch(struct rq *rq, struct task_struct *prev)
* We must ensure this doesn't happen until the switch is completely
* finished.
*
- * Pairs with the control dependency and rmb in try_to_wake_up().
+ * In particular, the load of prev->state in finish_task_switch() must
+ * happen before this.
+ *
+ * Pairs with the smp_cond_acquire() in try_to_wake_up().
*/
smp_store_release(&prev->on_cpu, 0);
#endif
@@ -1110,46 +1127,8 @@ static inline void finish_lock_switch(struct rq *rq, struct task_struct *prev)
#define WEIGHT_IDLEPRIO 3
#define WMULT_IDLEPRIO 1431655765
-/*
- * Nice levels are multiplicative, with a gentle 10% change for every
- * nice level changed. I.e. when a CPU-bound task goes from nice 0 to
- * nice 1, it will get ~10% less CPU time than another CPU-bound task
- * that remained on nice 0.
- *
- * The "10% effect" is relative and cumulative: from _any_ nice level,
- * if you go up 1 level, it's -10% CPU usage, if you go down 1 level
- * it's +10% CPU usage. (to achieve that we use a multiplier of 1.25.
- * If a task goes up by ~10% and another task goes down by ~10% then
- * the relative distance between them is ~25%.)
- */
-static const int prio_to_weight[40] = {
- /* -20 */ 88761, 71755, 56483, 46273, 36291,
- /* -15 */ 29154, 23254, 18705, 14949, 11916,
- /* -10 */ 9548, 7620, 6100, 4904, 3906,
- /* -5 */ 3121, 2501, 1991, 1586, 1277,
- /* 0 */ 1024, 820, 655, 526, 423,
- /* 5 */ 335, 272, 215, 172, 137,
- /* 10 */ 110, 87, 70, 56, 45,
- /* 15 */ 36, 29, 23, 18, 15,
-};
-
-/*
- * Inverse (2^32/x) values of the prio_to_weight[] array, precalculated.
- *
- * In cases where the weight does not change often, we can use the
- * precalculated inverse to speed up arithmetics by turning divisions
- * into multiplications:
- */
-static const u32 prio_to_wmult[40] = {
- /* -20 */ 48388, 59856, 76040, 92818, 118348,
- /* -15 */ 147320, 184698, 229616, 287308, 360437,
- /* -10 */ 449829, 563644, 704093, 875809, 1099582,
- /* -5 */ 1376151, 1717300, 2157191, 2708050, 3363326,
- /* 0 */ 4194304, 5237765, 6557202, 8165337, 10153587,
- /* 5 */ 12820798, 15790321, 19976592, 24970740, 31350126,
- /* 10 */ 39045157, 49367440, 61356676, 76695844, 95443717,
- /* 15 */ 119304647, 148102320, 186737708, 238609294, 286331153,
-};
+extern const int sched_prio_to_weight[40];
+extern const u32 sched_prio_to_wmult[40];
#define ENQUEUE_WAKEUP 0x01
#define ENQUEUE_HEAD 0x02
@@ -1249,16 +1228,8 @@ extern void update_group_capacity(struct sched_domain *sd, int cpu);
extern void trigger_load_balance(struct rq *rq);
-extern void idle_enter_fair(struct rq *this_rq);
-extern void idle_exit_fair(struct rq *this_rq);
-
extern void set_cpus_allowed_common(struct task_struct *p, const struct cpumask *new_mask);
-#else
-
-static inline void idle_enter_fair(struct rq *rq) { }
-static inline void idle_exit_fair(struct rq *rq) { }
-
#endif
#ifdef CONFIG_CPU_IDLE
diff --git a/kernel/sched/wait.c b/kernel/sched/wait.c
index 052e02672d12..f15d6b6a538a 100644
--- a/kernel/sched/wait.c
+++ b/kernel/sched/wait.c
@@ -392,7 +392,7 @@ __wait_on_bit(wait_queue_head_t *wq, struct wait_bit_queue *q,
do {
prepare_to_wait(wq, &q->wait, mode);
if (test_bit(q->key.bit_nr, q->key.flags))
- ret = (*action)(&q->key);
+ ret = (*action)(&q->key, mode);
} while (test_bit(q->key.bit_nr, q->key.flags) && !ret);
finish_wait(wq, &q->wait);
return ret;
@@ -431,7 +431,7 @@ __wait_on_bit_lock(wait_queue_head_t *wq, struct wait_bit_queue *q,
prepare_to_wait_exclusive(wq, &q->wait, mode);
if (!test_bit(q->key.bit_nr, q->key.flags))
continue;
- ret = action(&q->key);
+ ret = action(&q->key, mode);
if (!ret)
continue;
abort_exclusive_wait(wq, &q->wait, mode, &q->key);
@@ -581,44 +581,44 @@ void wake_up_atomic_t(atomic_t *p)
}
EXPORT_SYMBOL(wake_up_atomic_t);
-__sched int bit_wait(struct wait_bit_key *word)
+__sched int bit_wait(struct wait_bit_key *word, int mode)
{
- if (signal_pending_state(current->state, current))
- return 1;
schedule();
+ if (signal_pending_state(mode, current))
+ return -EINTR;
return 0;
}
EXPORT_SYMBOL(bit_wait);
-__sched int bit_wait_io(struct wait_bit_key *word)
+__sched int bit_wait_io(struct wait_bit_key *word, int mode)
{
- if (signal_pending_state(current->state, current))
- return 1;
io_schedule();
+ if (signal_pending_state(mode, current))
+ return -EINTR;
return 0;
}
EXPORT_SYMBOL(bit_wait_io);
-__sched int bit_wait_timeout(struct wait_bit_key *word)
+__sched int bit_wait_timeout(struct wait_bit_key *word, int mode)
{
unsigned long now = READ_ONCE(jiffies);
- if (signal_pending_state(current->state, current))
- return 1;
if (time_after_eq(now, word->timeout))
return -EAGAIN;
schedule_timeout(word->timeout - now);
+ if (signal_pending_state(mode, current))
+ return -EINTR;
return 0;
}
EXPORT_SYMBOL_GPL(bit_wait_timeout);
-__sched int bit_wait_io_timeout(struct wait_bit_key *word)
+__sched int bit_wait_io_timeout(struct wait_bit_key *word, int mode)
{
unsigned long now = READ_ONCE(jiffies);
- if (signal_pending_state(current->state, current))
- return 1;
if (time_after_eq(now, word->timeout))
return -EAGAIN;
io_schedule_timeout(word->timeout - now);
+ if (signal_pending_state(mode, current))
+ return -EINTR;
return 0;
}
EXPORT_SYMBOL_GPL(bit_wait_io_timeout);
diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c
index 867bc20e1ef1..edb6de4f5908 100644
--- a/kernel/stop_machine.c
+++ b/kernel/stop_machine.c
@@ -28,7 +28,6 @@
*/
struct cpu_stop_done {
atomic_t nr_todo; /* nr left to execute */
- bool executed; /* actually executed? */
int ret; /* collected return value */
struct completion completion; /* fired if nr_todo reaches 0 */
};
@@ -63,14 +62,10 @@ static void cpu_stop_init_done(struct cpu_stop_done *done, unsigned int nr_todo)
}
/* signal completion unless @done is NULL */
-static void cpu_stop_signal_done(struct cpu_stop_done *done, bool executed)
+static void cpu_stop_signal_done(struct cpu_stop_done *done)
{
- if (done) {
- if (executed)
- done->executed = true;
- if (atomic_dec_and_test(&done->nr_todo))
- complete(&done->completion);
- }
+ if (atomic_dec_and_test(&done->nr_todo))
+ complete(&done->completion);
}
static void __cpu_stop_queue_work(struct cpu_stopper *stopper,
@@ -81,17 +76,21 @@ static void __cpu_stop_queue_work(struct cpu_stopper *stopper,
}
/* queue @work to @stopper. if offline, @work is completed immediately */
-static void cpu_stop_queue_work(unsigned int cpu, struct cpu_stop_work *work)
+static bool cpu_stop_queue_work(unsigned int cpu, struct cpu_stop_work *work)
{
struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu);
unsigned long flags;
+ bool enabled;
spin_lock_irqsave(&stopper->lock, flags);
- if (stopper->enabled)
+ enabled = stopper->enabled;
+ if (enabled)
__cpu_stop_queue_work(stopper, work);
- else
- cpu_stop_signal_done(work->done, false);
+ else if (work->done)
+ cpu_stop_signal_done(work->done);
spin_unlock_irqrestore(&stopper->lock, flags);
+
+ return enabled;
}
/**
@@ -124,9 +123,10 @@ int stop_one_cpu(unsigned int cpu, cpu_stop_fn_t fn, void *arg)
struct cpu_stop_work work = { .fn = fn, .arg = arg, .done = &done };
cpu_stop_init_done(&done, 1);
- cpu_stop_queue_work(cpu, &work);
+ if (!cpu_stop_queue_work(cpu, &work))
+ return -ENOENT;
wait_for_completion(&done.completion);
- return done.executed ? done.ret : -ENOENT;
+ return done.ret;
}
/* This controls the threads on each CPU. */
@@ -258,7 +258,6 @@ int stop_two_cpus(unsigned int cpu1, unsigned int cpu2, cpu_stop_fn_t fn, void *
struct cpu_stop_work work1, work2;
struct multi_stop_data msdata;
- preempt_disable();
msdata = (struct multi_stop_data){
.fn = fn,
.data = arg,
@@ -277,16 +276,11 @@ int stop_two_cpus(unsigned int cpu1, unsigned int cpu2, cpu_stop_fn_t fn, void *
if (cpu1 > cpu2)
swap(cpu1, cpu2);
- if (cpu_stop_queue_two_works(cpu1, &work1, cpu2, &work2)) {
- preempt_enable();
+ if (cpu_stop_queue_two_works(cpu1, &work1, cpu2, &work2))
return -ENOENT;
- }
-
- preempt_enable();
wait_for_completion(&done.completion);
-
- return done.executed ? done.ret : -ENOENT;
+ return done.ret;
}
/**
@@ -302,23 +296,28 @@ int stop_two_cpus(unsigned int cpu1, unsigned int cpu2, cpu_stop_fn_t fn, void *
*
* CONTEXT:
* Don't care.
+ *
+ * RETURNS:
+ * true if cpu_stop_work was queued successfully and @fn will be called,
+ * false otherwise.
*/
-void stop_one_cpu_nowait(unsigned int cpu, cpu_stop_fn_t fn, void *arg,
+bool stop_one_cpu_nowait(unsigned int cpu, cpu_stop_fn_t fn, void *arg,
struct cpu_stop_work *work_buf)
{
*work_buf = (struct cpu_stop_work){ .fn = fn, .arg = arg, };
- cpu_stop_queue_work(cpu, work_buf);
+ return cpu_stop_queue_work(cpu, work_buf);
}
/* static data for stop_cpus */
static DEFINE_MUTEX(stop_cpus_mutex);
-static void queue_stop_cpus_work(const struct cpumask *cpumask,
+static bool queue_stop_cpus_work(const struct cpumask *cpumask,
cpu_stop_fn_t fn, void *arg,
struct cpu_stop_done *done)
{
struct cpu_stop_work *work;
unsigned int cpu;
+ bool queued = false;
/*
* Disable preemption while queueing to avoid getting
@@ -331,9 +330,12 @@ static void queue_stop_cpus_work(const struct cpumask *cpumask,
work->fn = fn;
work->arg = arg;
work->done = done;
- cpu_stop_queue_work(cpu, work);
+ if (cpu_stop_queue_work(cpu, work))
+ queued = true;
}
lg_global_unlock(&stop_cpus_lock);
+
+ return queued;
}
static int __stop_cpus(const struct cpumask *cpumask,
@@ -342,9 +344,10 @@ static int __stop_cpus(const struct cpumask *cpumask,
struct cpu_stop_done done;
cpu_stop_init_done(&done, cpumask_weight(cpumask));
- queue_stop_cpus_work(cpumask, fn, arg, &done);
+ if (!queue_stop_cpus_work(cpumask, fn, arg, &done))
+ return -ENOENT;
wait_for_completion(&done.completion);
- return done.executed ? done.ret : -ENOENT;
+ return done.ret;
}
/**
@@ -432,7 +435,6 @@ static void cpu_stopper_thread(unsigned int cpu)
{
struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu);
struct cpu_stop_work *work;
- int ret;
repeat:
work = NULL;
@@ -448,23 +450,19 @@ repeat:
cpu_stop_fn_t fn = work->fn;
void *arg = work->arg;
struct cpu_stop_done *done = work->done;
- char ksym_buf[KSYM_NAME_LEN] __maybe_unused;
-
- /* cpu stop callbacks are not allowed to sleep */
- preempt_disable();
+ int ret;
+ /* cpu stop callbacks must not sleep, make in_atomic() == T */
+ preempt_count_inc();
ret = fn(arg);
- if (ret)
- done->ret = ret;
-
- /* restore preemption and check it's still balanced */
- preempt_enable();
+ if (done) {
+ if (ret)
+ done->ret = ret;
+ cpu_stop_signal_done(done);
+ }
+ preempt_count_dec();
WARN_ONCE(preempt_count(),
- "cpu_stop: %s(%p) leaked preempt count\n",
- kallsyms_lookup((unsigned long)fn, NULL, NULL, NULL,
- ksym_buf), arg);
-
- cpu_stop_signal_done(done, true);
+ "cpu_stop: %pf(%p) leaked preempt count\n", fn, arg);
goto repeat;
}
}
@@ -531,7 +529,7 @@ static int __init cpu_stop_init(void)
}
early_initcall(cpu_stop_init);
-#ifdef CONFIG_STOP_MACHINE
+#if defined(CONFIG_SMP) || defined(CONFIG_HOTPLUG_CPU)
static int __stop_machine(cpu_stop_fn_t fn, void *data, const struct cpumask *cpus)
{
@@ -631,4 +629,4 @@ int stop_machine_from_inactive_cpu(cpu_stop_fn_t fn, void *data,
return ret ?: done.ret;
}
-#endif /* CONFIG_STOP_MACHINE */
+#endif /* CONFIG_SMP || CONFIG_HOTPLUG_CPU */
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
index 0623787ec67a..2c5e3a8e00d7 100644
--- a/kernel/sys_ni.c
+++ b/kernel/sys_ni.c
@@ -174,6 +174,7 @@ cond_syscall(sys_setfsuid);
cond_syscall(sys_setfsgid);
cond_syscall(sys_capget);
cond_syscall(sys_capset);
+cond_syscall(sys_copy_file_range);
/* arch-specific weak syscall entries */
cond_syscall(sys_pciconfig_read);
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index dc6858d6639e..c810f8afdb7f 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1568,6 +1568,28 @@ static struct ctl_table vm_table[] = {
.mode = 0644,
.proc_handler = proc_doulongvec_minmax,
},
+#ifdef CONFIG_HAVE_ARCH_MMAP_RND_BITS
+ {
+ .procname = "mmap_rnd_bits",
+ .data = &mmap_rnd_bits,
+ .maxlen = sizeof(mmap_rnd_bits),
+ .mode = 0600,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = (void *)&mmap_rnd_bits_min,
+ .extra2 = (void *)&mmap_rnd_bits_max,
+ },
+#endif
+#ifdef CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS
+ {
+ .procname = "mmap_rnd_compat_bits",
+ .data = &mmap_rnd_compat_bits,
+ .maxlen = sizeof(mmap_rnd_compat_bits),
+ .mode = 0600,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = (void *)&mmap_rnd_compat_bits_min,
+ .extra2 = (void *)&mmap_rnd_compat_bits_max,
+ },
+#endif
{ }
};
@@ -2047,9 +2069,8 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
void *data)
{
int *i, vleft, first = 1, err = 0;
- unsigned long page = 0;
size_t left;
- char *kbuf;
+ char *kbuf = NULL, *p;
if (!tbl_data || !table->maxlen || !*lenp || (*ppos && !write)) {
*lenp = 0;
@@ -2078,15 +2099,9 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
if (left > PAGE_SIZE - 1)
left = PAGE_SIZE - 1;
- page = __get_free_page(GFP_TEMPORARY);
- kbuf = (char *) page;
- if (!kbuf)
- return -ENOMEM;
- if (copy_from_user(kbuf, buffer, left)) {
- err = -EFAULT;
- goto free;
- }
- kbuf[left] = 0;
+ p = kbuf = memdup_user_nul(buffer, left);
+ if (IS_ERR(kbuf))
+ return PTR_ERR(kbuf);
}
for (; left && vleft--; i++, first=0) {
@@ -2094,11 +2109,11 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
bool neg;
if (write) {
- left -= proc_skip_spaces(&kbuf);
+ left -= proc_skip_spaces(&p);
if (!left)
break;
- err = proc_get_long(&kbuf, &left, &lval, &neg,
+ err = proc_get_long(&p, &left, &lval, &neg,
proc_wspace_sep,
sizeof(proc_wspace_sep), NULL);
if (err)
@@ -2125,10 +2140,9 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
if (!write && !first && left && !err)
err = proc_put_char(&buffer, &left, '\n');
if (write && !err && left)
- left -= proc_skip_spaces(&kbuf);
-free:
+ left -= proc_skip_spaces(&p);
if (write) {
- free_page(page);
+ kfree(kbuf);
if (first)
return err ? : -EINVAL;
}
@@ -2310,9 +2324,8 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
{
unsigned long *i, *min, *max;
int vleft, first = 1, err = 0;
- unsigned long page = 0;
size_t left;
- char *kbuf;
+ char *kbuf = NULL, *p;
if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
*lenp = 0;
@@ -2340,15 +2353,9 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
if (left > PAGE_SIZE - 1)
left = PAGE_SIZE - 1;
- page = __get_free_page(GFP_TEMPORARY);
- kbuf = (char *) page;
- if (!kbuf)
- return -ENOMEM;
- if (copy_from_user(kbuf, buffer, left)) {
- err = -EFAULT;
- goto free;
- }
- kbuf[left] = 0;
+ p = kbuf = memdup_user_nul(buffer, left);
+ if (IS_ERR(kbuf))
+ return PTR_ERR(kbuf);
}
for (; left && vleft--; i++, first = 0) {
@@ -2357,9 +2364,9 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
if (write) {
bool neg;
- left -= proc_skip_spaces(&kbuf);
+ left -= proc_skip_spaces(&p);
- err = proc_get_long(&kbuf, &left, &val, &neg,
+ err = proc_get_long(&p, &left, &val, &neg,
proc_wspace_sep,
sizeof(proc_wspace_sep), NULL);
if (err)
@@ -2385,10 +2392,9 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
if (!write && !first && left && !err)
err = proc_put_char(&buffer, &left, '\n');
if (write && !err)
- left -= proc_skip_spaces(&kbuf);
-free:
+ left -= proc_skip_spaces(&p);
if (write) {
- free_page(page);
+ kfree(kbuf);
if (first)
return err ? : -EINVAL;
}
@@ -2650,34 +2656,27 @@ int proc_do_large_bitmap(struct ctl_table *table, int write,
}
if (write) {
- unsigned long page = 0;
- char *kbuf;
+ char *kbuf, *p;
if (left > PAGE_SIZE - 1)
left = PAGE_SIZE - 1;
- page = __get_free_page(GFP_TEMPORARY);
- kbuf = (char *) page;
- if (!kbuf)
- return -ENOMEM;
- if (copy_from_user(kbuf, buffer, left)) {
- free_page(page);
- return -EFAULT;
- }
- kbuf[left] = 0;
+ p = kbuf = memdup_user_nul(buffer, left);
+ if (IS_ERR(kbuf))
+ return PTR_ERR(kbuf);
tmp_bitmap = kzalloc(BITS_TO_LONGS(bitmap_len) * sizeof(unsigned long),
GFP_KERNEL);
if (!tmp_bitmap) {
- free_page(page);
+ kfree(kbuf);
return -ENOMEM;
}
- proc_skip_char(&kbuf, &left, '\n');
+ proc_skip_char(&p, &left, '\n');
while (!err && left) {
unsigned long val_a, val_b;
bool neg;
- err = proc_get_long(&kbuf, &left, &val_a, &neg, tr_a,
+ err = proc_get_long(&p, &left, &val_a, &neg, tr_a,
sizeof(tr_a), &c);
if (err)
break;
@@ -2688,12 +2687,12 @@ int proc_do_large_bitmap(struct ctl_table *table, int write,
val_b = val_a;
if (left) {
- kbuf++;
+ p++;
left--;
}
if (c == '-') {
- err = proc_get_long(&kbuf, &left, &val_b,
+ err = proc_get_long(&p, &left, &val_b,
&neg, tr_b, sizeof(tr_b),
&c);
if (err)
@@ -2704,16 +2703,16 @@ int proc_do_large_bitmap(struct ctl_table *table, int write,
break;
}
if (left) {
- kbuf++;
+ p++;
left--;
}
}
bitmap_set(tmp_bitmap, val_a, val_b - val_a + 1);
first = 0;
- proc_skip_char(&kbuf, &left, '\n');
+ proc_skip_char(&p, &left, '\n');
}
- free_page(page);
+ kfree(kbuf);
} else {
unsigned long bit_a, bit_b = 0;
diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c
index 7fbba635a549..e840ed867a5d 100644
--- a/kernel/time/alarmtimer.c
+++ b/kernel/time/alarmtimer.c
@@ -271,11 +271,27 @@ static int alarmtimer_suspend(struct device *dev)
__pm_wakeup_event(ws, MSEC_PER_SEC);
return ret;
}
+
+static int alarmtimer_resume(struct device *dev)
+{
+ struct rtc_device *rtc;
+
+ rtc = alarmtimer_get_rtcdev();
+ if (rtc)
+ rtc_timer_cancel(rtc, &rtctimer);
+ return 0;
+}
+
#else
static int alarmtimer_suspend(struct device *dev)
{
return 0;
}
+
+static int alarmtimer_resume(struct device *dev)
+{
+ return 0;
+}
#endif
static void alarmtimer_freezerset(ktime_t absexp, enum alarmtimer_type type)
@@ -800,6 +816,7 @@ out:
/* Suspend hook structures */
static const struct dev_pm_ops alarmtimer_pm_ops = {
.suspend = alarmtimer_suspend,
+ .resume = alarmtimer_resume,
};
static struct platform_driver alarmtimer_driver = {
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
index 1347882d131e..664de539299b 100644
--- a/kernel/time/clocksource.c
+++ b/kernel/time/clocksource.c
@@ -218,8 +218,8 @@ 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("timekeeping watchdog on CPU%d: Marking clocksource '%s' as unstable because the skew is too large:\n",
+ smp_processor_id(), 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",
diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c
index 149cc8086aea..36f2ca09aa5e 100644
--- a/kernel/time/ntp.c
+++ b/kernel/time/ntp.c
@@ -16,8 +16,11 @@
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/rtc.h>
+#include <linux/math64.h>
#include "ntp_internal.h"
+#include "timekeeping_internal.h"
+
/*
* NTP timekeeping variables:
@@ -70,7 +73,7 @@ static long time_esterror = NTP_PHASE_LIMIT;
static s64 time_freq;
/* time at last adjustment (secs): */
-static long time_reftime;
+static time64_t time_reftime;
static long time_adjust;
@@ -297,25 +300,27 @@ static void ntp_update_offset(long offset)
if (!(time_status & STA_PLL))
return;
- if (!(time_status & STA_NANO))
+ if (!(time_status & STA_NANO)) {
+ /* Make sure the multiplication below won't overflow */
+ offset = clamp(offset, -USEC_PER_SEC, USEC_PER_SEC);
offset *= NSEC_PER_USEC;
+ }
/*
* Scale the phase adjustment and
* clamp to the operating range.
*/
- offset = min(offset, MAXPHASE);
- offset = max(offset, -MAXPHASE);
+ offset = clamp(offset, -MAXPHASE, MAXPHASE);
/*
* Select how the frequency is to be controlled
* and in which mode (PLL or FLL).
*/
- secs = get_seconds() - time_reftime;
+ secs = (long)(__ktime_get_real_seconds() - time_reftime);
if (unlikely(time_status & STA_FREQHOLD))
secs = 0;
- time_reftime = get_seconds();
+ time_reftime = __ktime_get_real_seconds();
offset64 = offset;
freq_adj = ntp_update_offset_fll(offset64, secs);
@@ -390,10 +395,11 @@ ktime_t ntp_get_next_leap(void)
*
* Also handles leap second processing, and returns leap offset
*/
-int second_overflow(unsigned long secs)
+int second_overflow(time64_t secs)
{
s64 delta;
int leap = 0;
+ s32 rem;
/*
* Leap second processing. If in leap-insert state at the end of the
@@ -404,19 +410,19 @@ int second_overflow(unsigned long secs)
case TIME_OK:
if (time_status & STA_INS) {
time_state = TIME_INS;
- ntp_next_leap_sec = secs + SECS_PER_DAY -
- (secs % SECS_PER_DAY);
+ div_s64_rem(secs, SECS_PER_DAY, &rem);
+ ntp_next_leap_sec = secs + SECS_PER_DAY - rem;
} else if (time_status & STA_DEL) {
time_state = TIME_DEL;
- ntp_next_leap_sec = secs + SECS_PER_DAY -
- ((secs+1) % SECS_PER_DAY);
+ div_s64_rem(secs + 1, SECS_PER_DAY, &rem);
+ ntp_next_leap_sec = secs + SECS_PER_DAY - rem;
}
break;
case TIME_INS:
if (!(time_status & STA_INS)) {
ntp_next_leap_sec = TIME64_MAX;
time_state = TIME_OK;
- } else if (secs % SECS_PER_DAY == 0) {
+ } else if (secs == ntp_next_leap_sec) {
leap = -1;
time_state = TIME_OOP;
printk(KERN_NOTICE
@@ -427,7 +433,7 @@ int second_overflow(unsigned long secs)
if (!(time_status & STA_DEL)) {
ntp_next_leap_sec = TIME64_MAX;
time_state = TIME_OK;
- } else if ((secs + 1) % SECS_PER_DAY == 0) {
+ } else if (secs == ntp_next_leap_sec) {
leap = 1;
ntp_next_leap_sec = TIME64_MAX;
time_state = TIME_WAIT;
@@ -590,7 +596,7 @@ static inline void process_adj_status(struct timex *txc, struct timespec64 *ts)
* reference time to current time.
*/
if (!(time_status & STA_PLL) && (txc->status & STA_PLL))
- time_reftime = get_seconds();
+ time_reftime = __ktime_get_real_seconds();
/* only set allowed bits */
time_status &= STA_RONLY;
@@ -674,8 +680,14 @@ int ntp_validate_timex(struct timex *txc)
return -EINVAL;
}
- if ((txc->modes & ADJ_SETOFFSET) && (!capable(CAP_SYS_TIME)))
- return -EPERM;
+ if (txc->modes & ADJ_SETOFFSET) {
+ /* In order to inject time, you gotta be super-user! */
+ if (!capable(CAP_SYS_TIME))
+ return -EPERM;
+
+ if (!timeval_inject_offset_valid(&txc->time))
+ return -EINVAL;
+ }
/*
* Check for potential multiplication overflows that can
diff --git a/kernel/time/ntp_internal.h b/kernel/time/ntp_internal.h
index af924470eac0..d8a7c11fa71a 100644
--- a/kernel/time/ntp_internal.h
+++ b/kernel/time/ntp_internal.h
@@ -6,7 +6,7 @@ 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 second_overflow(time64_t secs);
extern int ntp_validate_timex(struct timex *);
extern int __do_adjtimex(struct timex *, struct timespec64 *, s32 *);
extern void __hardpps(const struct timespec64 *, const struct timespec64 *);
diff --git a/kernel/time/posix-clock.c b/kernel/time/posix-clock.c
index ce033c7aa2e8..9cff0ab82b63 100644
--- a/kernel/time/posix-clock.c
+++ b/kernel/time/posix-clock.c
@@ -69,10 +69,10 @@ static ssize_t posix_clock_read(struct file *fp, char __user *buf,
static unsigned int posix_clock_poll(struct file *fp, poll_table *wait)
{
struct posix_clock *clk = get_posix_clock(fp);
- int result = 0;
+ unsigned int result = 0;
if (!clk)
- return -ENODEV;
+ return POLLERR;
if (clk->ops.poll)
result = clk->ops.poll(clk, fp, wait);
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index 7c7ec4515983..9cc20af58c76 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -143,7 +143,7 @@ static void tick_sched_handle(struct tick_sched *ts, struct pt_regs *regs)
* when we go busy again does not account too much ticks.
*/
if (ts->tick_stopped) {
- touch_softlockup_watchdog();
+ touch_softlockup_watchdog_sched();
if (is_idle_task(current))
ts->idle_jiffies++;
}
@@ -430,7 +430,7 @@ static void tick_nohz_update_jiffies(ktime_t now)
tick_do_update_jiffies64(now);
local_irq_restore(flags);
- touch_softlockup_watchdog();
+ touch_softlockup_watchdog_sched();
}
/*
@@ -603,15 +603,31 @@ static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
/*
* If the tick is due in the next period, keep it ticking or
- * restart it proper.
+ * force prod the timer.
*/
delta = next_tick - basemono;
if (delta <= (u64)TICK_NSEC) {
tick.tv64 = 0;
+ /*
+ * We've not stopped the tick yet, and there's a timer in the
+ * next period, so no point in stopping it either, bail.
+ */
if (!ts->tick_stopped)
goto out;
+
+ /*
+ * If, OTOH, we did stop it, but there's a pending (expired)
+ * timer reprogram the timer hardware to fire now.
+ *
+ * We will not restart the tick proper, just prod the timer
+ * hardware into firing an interrupt to process the pending
+ * timers. Just like tick_irq_exit() will not restart the tick
+ * for 'normal' interrupts.
+ *
+ * Only once we exit the idle loop will we re-enable the tick,
+ * see tick_nohz_idle_exit().
+ */
if (delta == 0) {
- /* Tick is stopped, but required now. Enforce it */
tick_nohz_restart(ts, now);
goto out;
}
@@ -694,14 +710,14 @@ out:
return tick;
}
-static void tick_nohz_restart_sched_tick(struct tick_sched *ts, ktime_t now)
+static void tick_nohz_restart_sched_tick(struct tick_sched *ts, ktime_t now, int active)
{
/* Update jiffies first */
tick_do_update_jiffies64(now);
- update_cpu_load_nohz();
+ update_cpu_load_nohz(active);
calc_load_exit_idle();
- touch_softlockup_watchdog();
+ touch_softlockup_watchdog_sched();
/*
* Cancel the scheduled timer and restore the tick
*/
@@ -725,7 +741,7 @@ static void tick_nohz_full_update_tick(struct tick_sched *ts)
if (can_stop_full_tick())
tick_nohz_stop_sched_tick(ts, ktime_get(), cpu);
else if (ts->tick_stopped)
- tick_nohz_restart_sched_tick(ts, ktime_get());
+ tick_nohz_restart_sched_tick(ts, ktime_get(), 1);
#endif
}
@@ -875,7 +891,7 @@ static void tick_nohz_account_idle_ticks(struct tick_sched *ts)
#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
unsigned long ticks;
- if (vtime_accounting_enabled())
+ if (vtime_accounting_cpu_enabled())
return;
/*
* We stopped the tick in idle. Update process times would miss the
@@ -916,7 +932,7 @@ void tick_nohz_idle_exit(void)
tick_nohz_stop_idle(ts, now);
if (ts->tick_stopped) {
- tick_nohz_restart_sched_tick(ts, now);
+ tick_nohz_restart_sched_tick(ts, now, 0);
tick_nohz_account_idle_ticks(ts);
}
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index d563c1960302..34b4cedfa80d 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -305,8 +305,7 @@ static inline s64 timekeeping_get_ns(struct tk_read_base *tkr)
delta = timekeeping_get_delta(tkr);
- nsec = delta * tkr->mult + tkr->xtime_nsec;
- nsec >>= tkr->shift;
+ nsec = (delta * tkr->mult + tkr->xtime_nsec) >> tkr->shift;
/* If arch requires, add in get_arch_timeoffset() */
return nsec + arch_gettimeoffset();
@@ -846,6 +845,19 @@ time64_t ktime_get_real_seconds(void)
}
EXPORT_SYMBOL_GPL(ktime_get_real_seconds);
+/**
+ * __ktime_get_real_seconds - The same as ktime_get_real_seconds
+ * but without the sequence counter protect. This internal function
+ * is called just when timekeeping lock is already held.
+ */
+time64_t __ktime_get_real_seconds(void)
+{
+ struct timekeeper *tk = &tk_core.timekeeper;
+
+ return tk->xtime_sec;
+}
+
+
#ifdef CONFIG_NTP_PPS
/**
@@ -959,7 +971,7 @@ int timekeeping_inject_offset(struct timespec *ts)
struct timespec64 ts64, tmp;
int ret = 0;
- if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC)
+ if (!timespec_inject_offset_valid(ts))
return -EINVAL;
ts64 = timespec_to_timespec64(*ts);
@@ -1592,9 +1604,12 @@ static __always_inline void timekeeping_freqadjust(struct timekeeper *tk,
{
s64 interval = tk->cycle_interval;
s64 xinterval = tk->xtime_interval;
+ u32 base = tk->tkr_mono.clock->mult;
+ u32 max = tk->tkr_mono.clock->maxadj;
+ u32 cur_adj = tk->tkr_mono.mult;
s64 tick_error;
bool negative;
- u32 adj;
+ u32 adj_scale;
/* Remove any current error adj from freq calculation */
if (tk->ntp_err_mult)
@@ -1613,13 +1628,33 @@ static __always_inline void timekeeping_freqadjust(struct timekeeper *tk,
/* preserve the direction of correction */
negative = (tick_error < 0);
- /* Sort out the magnitude of the correction */
+ /* If any adjustment would pass the max, just return */
+ if (negative && (cur_adj - 1) <= (base - max))
+ return;
+ if (!negative && (cur_adj + 1) >= (base + max))
+ return;
+ /*
+ * Sort out the magnitude of the correction, but
+ * avoid making so large a correction that we go
+ * over the max adjustment.
+ */
+ adj_scale = 0;
tick_error = abs(tick_error);
- for (adj = 0; tick_error > interval; adj++)
+ while (tick_error > interval) {
+ u32 adj = 1 << (adj_scale + 1);
+
+ /* Check if adjustment gets us within 1 unit from the max */
+ if (negative && (cur_adj - adj) <= (base - max))
+ break;
+ if (!negative && (cur_adj + adj) >= (base + max))
+ break;
+
+ adj_scale++;
tick_error >>= 1;
+ }
/* scale the corrections */
- timekeeping_apply_adjustment(tk, offset, negative, adj);
+ timekeeping_apply_adjustment(tk, offset, negative, adj_scale);
}
/*
diff --git a/kernel/time/timekeeping_internal.h b/kernel/time/timekeeping_internal.h
index 4ea005a7f9da..5be76270ec4a 100644
--- a/kernel/time/timekeeping_internal.h
+++ b/kernel/time/timekeeping_internal.h
@@ -17,7 +17,11 @@ static inline cycle_t clocksource_delta(cycle_t now, cycle_t last, cycle_t mask)
{
cycle_t ret = (now - last) & mask;
- return (s64) ret > 0 ? ret : 0;
+ /*
+ * Prevent time going backwards by checking the MSB of mask in
+ * the result. If set, return 0.
+ */
+ return ret & ~(mask >> 1) ? 0 : ret;
}
#else
static inline cycle_t clocksource_delta(cycle_t now, cycle_t last, cycle_t mask)
@@ -26,4 +30,6 @@ static inline cycle_t clocksource_delta(cycle_t now, cycle_t last, cycle_t mask)
}
#endif
+extern time64_t __ktime_get_real_seconds(void);
+
#endif /* _TIMEKEEPING_INTERNAL_H */
diff --git a/kernel/trace/blktrace.c b/kernel/trace/blktrace.c
index a990824c8604..2aeb6ffc0a1e 100644
--- a/kernel/trace/blktrace.c
+++ b/kernel/trace/blktrace.c
@@ -349,16 +349,10 @@ static ssize_t blk_msg_write(struct file *filp, const char __user *buffer,
if (count >= BLK_TN_MAX_MSG)
return -EINVAL;
- msg = kmalloc(count + 1, GFP_KERNEL);
- if (msg == NULL)
- return -ENOMEM;
-
- if (copy_from_user(msg, buffer, count)) {
- kfree(msg);
- return -EFAULT;
- }
+ msg = memdup_user_nul(buffer, count);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
- msg[count] = '\0';
bt = filp->private_data;
__trace_note_message(bt, "%s", msg);
kfree(msg);
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 4228fd3682c3..45dd798bcd37 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -316,7 +316,7 @@ static bool kprobe_prog_is_valid_access(int off, int size, enum bpf_access_type
return true;
}
-static struct bpf_verifier_ops kprobe_prog_ops = {
+static const struct bpf_verifier_ops kprobe_prog_ops = {
.get_func_proto = kprobe_prog_func_proto,
.is_valid_access = kprobe_prog_is_valid_access,
};
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 3f743b147247..eca592f977b2 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -62,8 +62,6 @@
#define FTRACE_HASH_DEFAULT_BITS 10
#define FTRACE_HASH_MAX_BITS 12
-#define FL_GLOBAL_CONTROL_MASK (FTRACE_OPS_FL_CONTROL)
-
#ifdef CONFIG_DYNAMIC_FTRACE
#define INIT_OPS_HASH(opsname) \
.func_hash = &opsname.local_hash, \
@@ -113,14 +111,9 @@ static int ftrace_disabled __read_mostly;
static DEFINE_MUTEX(ftrace_lock);
-static struct ftrace_ops *ftrace_control_list __read_mostly = &ftrace_list_end;
static struct ftrace_ops *ftrace_ops_list __read_mostly = &ftrace_list_end;
ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub;
static struct ftrace_ops global_ops;
-static struct ftrace_ops control_ops;
-
-static void ftrace_ops_recurs_func(unsigned long ip, unsigned long parent_ip,
- struct ftrace_ops *op, struct pt_regs *regs);
#if ARCH_SUPPORTS_FTRACE_OPS
static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
@@ -203,7 +196,7 @@ void clear_ftrace_function(void)
ftrace_trace_function = ftrace_stub;
}
-static void control_ops_disable_all(struct ftrace_ops *ops)
+static void per_cpu_ops_disable_all(struct ftrace_ops *ops)
{
int cpu;
@@ -211,16 +204,19 @@ static void control_ops_disable_all(struct ftrace_ops *ops)
*per_cpu_ptr(ops->disabled, cpu) = 1;
}
-static int control_ops_alloc(struct ftrace_ops *ops)
+static int per_cpu_ops_alloc(struct ftrace_ops *ops)
{
int __percpu *disabled;
+ if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_PER_CPU)))
+ return -EINVAL;
+
disabled = alloc_percpu(int);
if (!disabled)
return -ENOMEM;
ops->disabled = disabled;
- control_ops_disable_all(ops);
+ per_cpu_ops_disable_all(ops);
return 0;
}
@@ -256,10 +252,11 @@ static inline void update_function_graph_func(void) { }
static ftrace_func_t ftrace_ops_get_list_func(struct ftrace_ops *ops)
{
/*
- * If this is a dynamic ops or we force list func,
+ * If this is a dynamic, RCU, or per CPU ops, or we force list func,
* then it needs to call the list anyway.
*/
- if (ops->flags & FTRACE_OPS_FL_DYNAMIC || FTRACE_FORCE_LIST_FUNC)
+ if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_PER_CPU |
+ FTRACE_OPS_FL_RCU) || FTRACE_FORCE_LIST_FUNC)
return ftrace_ops_list_func;
return ftrace_ops_get_func(ops);
@@ -383,26 +380,6 @@ static int remove_ftrace_ops(struct ftrace_ops **list, struct ftrace_ops *ops)
return 0;
}
-static void add_ftrace_list_ops(struct ftrace_ops **list,
- struct ftrace_ops *main_ops,
- struct ftrace_ops *ops)
-{
- int first = *list == &ftrace_list_end;
- add_ftrace_ops(list, ops);
- if (first)
- add_ftrace_ops(&ftrace_ops_list, main_ops);
-}
-
-static int remove_ftrace_list_ops(struct ftrace_ops **list,
- struct ftrace_ops *main_ops,
- struct ftrace_ops *ops)
-{
- int ret = remove_ftrace_ops(list, ops);
- if (!ret && *list == &ftrace_list_end)
- ret = remove_ftrace_ops(&ftrace_ops_list, main_ops);
- return ret;
-}
-
static void ftrace_update_trampoline(struct ftrace_ops *ops);
static int __register_ftrace_function(struct ftrace_ops *ops)
@@ -430,14 +407,12 @@ static int __register_ftrace_function(struct ftrace_ops *ops)
if (!core_kernel_data((unsigned long)ops))
ops->flags |= FTRACE_OPS_FL_DYNAMIC;
- if (ops->flags & FTRACE_OPS_FL_CONTROL) {
- if (control_ops_alloc(ops))
+ if (ops->flags & FTRACE_OPS_FL_PER_CPU) {
+ if (per_cpu_ops_alloc(ops))
return -ENOMEM;
- add_ftrace_list_ops(&ftrace_control_list, &control_ops, ops);
- /* The control_ops needs the trampoline update */
- ops = &control_ops;
- } else
- add_ftrace_ops(&ftrace_ops_list, ops);
+ }
+
+ add_ftrace_ops(&ftrace_ops_list, ops);
/* Always save the function, and reset at unregistering */
ops->saved_func = ops->func;
@@ -460,11 +435,7 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops)
if (WARN_ON(!(ops->flags & FTRACE_OPS_FL_ENABLED)))
return -EBUSY;
- if (ops->flags & FTRACE_OPS_FL_CONTROL) {
- ret = remove_ftrace_list_ops(&ftrace_control_list,
- &control_ops, ops);
- } else
- ret = remove_ftrace_ops(&ftrace_ops_list, ops);
+ ret = remove_ftrace_ops(&ftrace_ops_list, ops);
if (ret < 0)
return ret;
@@ -1687,6 +1658,9 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
int in_hash = 0;
int match = 0;
+ if (rec->flags & FTRACE_FL_DISABLED)
+ continue;
+
if (all) {
/*
* Only the filter_hash affects all records.
@@ -1940,7 +1914,7 @@ static int ftrace_hash_ipmodify_update(struct ftrace_ops *ops,
return __ftrace_hash_update_ipmodify(ops, old_hash, new_hash);
}
-static void print_ip_ins(const char *fmt, unsigned char *p)
+static void print_ip_ins(const char *fmt, const unsigned char *p)
{
int i;
@@ -1952,6 +1926,31 @@ static void print_ip_ins(const char *fmt, unsigned char *p)
static struct ftrace_ops *
ftrace_find_tramp_ops_any(struct dyn_ftrace *rec);
+static struct ftrace_ops *
+ftrace_find_tramp_ops_next(struct dyn_ftrace *rec, struct ftrace_ops *ops);
+
+enum ftrace_bug_type ftrace_bug_type;
+const void *ftrace_expected;
+
+static void print_bug_type(void)
+{
+ switch (ftrace_bug_type) {
+ case FTRACE_BUG_UNKNOWN:
+ break;
+ case FTRACE_BUG_INIT:
+ pr_info("Initializing ftrace call sites\n");
+ break;
+ case FTRACE_BUG_NOP:
+ pr_info("Setting ftrace call site to NOP\n");
+ break;
+ case FTRACE_BUG_CALL:
+ pr_info("Setting ftrace call site to call ftrace function\n");
+ break;
+ case FTRACE_BUG_UPDATE:
+ pr_info("Updating ftrace call site to call a different ftrace function\n");
+ break;
+ }
+}
/**
* ftrace_bug - report and shutdown function tracer
@@ -1979,8 +1978,12 @@ void ftrace_bug(int failed, struct dyn_ftrace *rec)
FTRACE_WARN_ON_ONCE(1);
pr_info("ftrace failed to modify ");
print_ip_sym(ip);
- print_ip_ins(" actual: ", (unsigned char *)ip);
+ print_ip_ins(" actual: ", (unsigned char *)ip);
pr_cont("\n");
+ if (ftrace_expected) {
+ print_ip_ins(" expected: ", ftrace_expected);
+ pr_cont("\n");
+ }
break;
case -EPERM:
FTRACE_WARN_ON_ONCE(1);
@@ -1992,6 +1995,7 @@ void ftrace_bug(int failed, struct dyn_ftrace *rec)
pr_info("ftrace faulted on unknown error ");
print_ip_sym(ip);
}
+ print_bug_type();
if (rec) {
struct ftrace_ops *ops = NULL;
@@ -2000,15 +2004,19 @@ void ftrace_bug(int failed, struct dyn_ftrace *rec)
rec->flags & FTRACE_FL_REGS ? " R" : " ");
if (rec->flags & FTRACE_FL_TRAMP_EN) {
ops = ftrace_find_tramp_ops_any(rec);
- if (ops)
- pr_cont("\ttramp: %pS",
- (void *)ops->trampoline);
- else
+ if (ops) {
+ do {
+ pr_cont("\ttramp: %pS (%pS)",
+ (void *)ops->trampoline,
+ (void *)ops->func);
+ ops = ftrace_find_tramp_ops_next(rec, ops);
+ } while (ops);
+ } else
pr_cont("\ttramp: ERROR!");
}
ip = ftrace_get_addr_curr(rec);
- pr_cont(" expected tramp: %lx\n", ip);
+ pr_cont("\n expected tramp: %lx\n", ip);
}
}
@@ -2016,6 +2024,11 @@ static int ftrace_check_record(struct dyn_ftrace *rec, int enable, int update)
{
unsigned long flag = 0UL;
+ ftrace_bug_type = FTRACE_BUG_UNKNOWN;
+
+ if (rec->flags & FTRACE_FL_DISABLED)
+ return FTRACE_UPDATE_IGNORE;
+
/*
* If we are updating calls:
*
@@ -2077,9 +2090,12 @@ static int ftrace_check_record(struct dyn_ftrace *rec, int enable, int update)
* from the save regs, to a non-save regs function or
* vice versa, or from a trampoline call.
*/
- if (flag & FTRACE_FL_ENABLED)
+ if (flag & FTRACE_FL_ENABLED) {
+ ftrace_bug_type = FTRACE_BUG_CALL;
return FTRACE_UPDATE_MAKE_CALL;
+ }
+ ftrace_bug_type = FTRACE_BUG_UPDATE;
return FTRACE_UPDATE_MODIFY_CALL;
}
@@ -2096,6 +2112,7 @@ static int ftrace_check_record(struct dyn_ftrace *rec, int enable, int update)
FTRACE_FL_REGS_EN);
}
+ ftrace_bug_type = FTRACE_BUG_NOP;
return FTRACE_UPDATE_MAKE_NOP;
}
@@ -2145,6 +2162,24 @@ ftrace_find_tramp_ops_any(struct dyn_ftrace *rec)
}
static struct ftrace_ops *
+ftrace_find_tramp_ops_next(struct dyn_ftrace *rec,
+ struct ftrace_ops *op)
+{
+ unsigned long ip = rec->ip;
+
+ while_for_each_ftrace_op(op) {
+
+ if (!op->trampoline)
+ continue;
+
+ if (hash_contains_ip(ip, op->func_hash))
+ return op;
+ }
+
+ return NULL;
+}
+
+static struct ftrace_ops *
ftrace_find_tramp_ops_curr(struct dyn_ftrace *rec)
{
struct ftrace_ops *op;
@@ -2307,17 +2342,22 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable)
ret = ftrace_update_record(rec, enable);
+ ftrace_bug_type = FTRACE_BUG_UNKNOWN;
+
switch (ret) {
case FTRACE_UPDATE_IGNORE:
return 0;
case FTRACE_UPDATE_MAKE_CALL:
+ ftrace_bug_type = FTRACE_BUG_CALL;
return ftrace_make_call(rec, ftrace_addr);
case FTRACE_UPDATE_MAKE_NOP:
+ ftrace_bug_type = FTRACE_BUG_NOP;
return ftrace_make_nop(NULL, rec, ftrace_old_addr);
case FTRACE_UPDATE_MODIFY_CALL:
+ ftrace_bug_type = FTRACE_BUG_UPDATE;
return ftrace_modify_call(rec, ftrace_old_addr, ftrace_addr);
}
@@ -2425,6 +2465,7 @@ ftrace_code_disable(struct module *mod, struct dyn_ftrace *rec)
ret = ftrace_make_nop(mod, rec, MCOUNT_ADDR);
if (ret) {
+ ftrace_bug_type = FTRACE_BUG_INIT;
ftrace_bug(ret, rec);
return 0;
}
@@ -2566,7 +2607,7 @@ void __weak arch_ftrace_trampoline_free(struct ftrace_ops *ops)
{
}
-static void control_ops_free(struct ftrace_ops *ops)
+static void per_cpu_ops_free(struct ftrace_ops *ops)
{
free_percpu(ops->disabled);
}
@@ -2667,13 +2708,13 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)
if (!command || !ftrace_enabled) {
/*
- * If these are control ops, they still need their
+ * If these are per_cpu ops, they still need their
* per_cpu field freed. Since, function tracing is
* not currently active, we can just free them
* without synchronizing all CPUs.
*/
- if (ops->flags & FTRACE_OPS_FL_CONTROL)
- control_ops_free(ops);
+ if (ops->flags & FTRACE_OPS_FL_PER_CPU)
+ per_cpu_ops_free(ops);
return 0;
}
@@ -2714,7 +2755,7 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)
/*
* Dynamic ops may be freed, we must make sure that all
* callers are done before leaving this function.
- * The same goes for freeing the per_cpu data of the control
+ * The same goes for freeing the per_cpu data of the per_cpu
* ops.
*
* Again, normal synchronize_sched() is not good enough.
@@ -2725,13 +2766,13 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)
* infrastructure to do the synchronization, thus we must do it
* ourselves.
*/
- if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_CONTROL)) {
+ if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_PER_CPU)) {
schedule_on_each_cpu(ftrace_sync);
arch_ftrace_trampoline_free(ops);
- if (ops->flags & FTRACE_OPS_FL_CONTROL)
- control_ops_free(ops);
+ if (ops->flags & FTRACE_OPS_FL_PER_CPU)
+ per_cpu_ops_free(ops);
}
return 0;
@@ -2798,9 +2839,9 @@ ops_references_rec(struct ftrace_ops *ops, struct dyn_ftrace *rec)
if (!(ops->flags & FTRACE_OPS_FL_ENABLED))
return 0;
- /* If ops traces all mods, we already accounted for it */
+ /* If ops traces all then it includes this function */
if (ops_traces_mod(ops))
- return 0;
+ return 1;
/* The function must be in the filter */
if (!ftrace_hash_empty(ops->func_hash->filter_hash) &&
@@ -2814,64 +2855,41 @@ ops_references_rec(struct ftrace_ops *ops, struct dyn_ftrace *rec)
return 1;
}
-static int referenced_filters(struct dyn_ftrace *rec)
-{
- struct ftrace_ops *ops;
- int cnt = 0;
-
- for (ops = ftrace_ops_list; ops != &ftrace_list_end; ops = ops->next) {
- if (ops_references_rec(ops, rec))
- cnt++;
- }
-
- return cnt;
-}
-
static int ftrace_update_code(struct module *mod, struct ftrace_page *new_pgs)
{
struct ftrace_page *pg;
struct dyn_ftrace *p;
cycle_t start, stop;
unsigned long update_cnt = 0;
- unsigned long ref = 0;
- bool test = false;
+ unsigned long rec_flags = 0;
int i;
+ start = ftrace_now(raw_smp_processor_id());
+
/*
- * When adding a module, we need to check if tracers are
- * currently enabled and if they are set to trace all functions.
- * If they are, we need to enable the module functions as well
- * as update the reference counts for those function records.
+ * When a module is loaded, this function is called to convert
+ * the calls to mcount in its text to nops, and also to create
+ * an entry in the ftrace data. Now, if ftrace is activated
+ * after this call, but before the module sets its text to
+ * read-only, the modification of enabling ftrace can fail if
+ * the read-only is done while ftrace is converting the calls.
+ * To prevent this, the module's records are set as disabled
+ * and will be enabled after the call to set the module's text
+ * to read-only.
*/
- if (mod) {
- struct ftrace_ops *ops;
-
- for (ops = ftrace_ops_list;
- ops != &ftrace_list_end; ops = ops->next) {
- if (ops->flags & FTRACE_OPS_FL_ENABLED) {
- if (ops_traces_mod(ops))
- ref++;
- else
- test = true;
- }
- }
- }
-
- start = ftrace_now(raw_smp_processor_id());
+ if (mod)
+ rec_flags |= FTRACE_FL_DISABLED;
for (pg = new_pgs; pg; pg = pg->next) {
for (i = 0; i < pg->index; i++) {
- int cnt = ref;
/* If something went wrong, bail without enabling anything */
if (unlikely(ftrace_disabled))
return -1;
p = &pg->records[i];
- if (test)
- cnt += referenced_filters(p);
- p->flags = cnt;
+ p->flags = rec_flags;
/*
* Do the initial record conversion from mcount jump
@@ -2881,21 +2899,6 @@ static int ftrace_update_code(struct module *mod, struct ftrace_page *new_pgs)
break;
update_cnt++;
-
- /*
- * If the tracing is enabled, go ahead and enable the record.
- *
- * The reason not to enable the record immediatelly is the
- * inherent check of ftrace_make_nop/ftrace_make_call for
- * correct previous instructions. Making first the NOP
- * conversion puts the module to the correct state, thus
- * passing the ftrace_make_call check.
- */
- if (ftrace_start_up && cnt) {
- int failed = __ftrace_replace_code(p, 1);
- if (failed)
- ftrace_bug(failed, p);
- }
}
}
@@ -3258,7 +3261,7 @@ static int t_show(struct seq_file *m, void *v)
seq_printf(m, "%ps", (void *)rec->ip);
if (iter->flags & FTRACE_ITER_ENABLED) {
- struct ftrace_ops *ops = NULL;
+ struct ftrace_ops *ops;
seq_printf(m, " (%ld)%s%s",
ftrace_rec_count(rec),
@@ -3266,14 +3269,19 @@ static int t_show(struct seq_file *m, void *v)
rec->flags & FTRACE_FL_IPMODIFY ? " I" : " ");
if (rec->flags & FTRACE_FL_TRAMP_EN) {
ops = ftrace_find_tramp_ops_any(rec);
- if (ops)
- seq_printf(m, "\ttramp: %pS",
- (void *)ops->trampoline);
- else
+ if (ops) {
+ do {
+ seq_printf(m, "\ttramp: %pS (%pS)",
+ (void *)ops->trampoline,
+ (void *)ops->func);
+ add_trampoline_func(m, ops, rec);
+ ops = ftrace_find_tramp_ops_next(rec, ops);
+ } while (ops);
+ } else
seq_puts(m, "\ttramp: ERROR!");
-
+ } else {
+ add_trampoline_func(m, NULL, rec);
}
- add_trampoline_func(m, ops, rec);
}
seq_putc(m, '\n');
@@ -4898,6 +4906,19 @@ static int ftrace_process_locs(struct module *mod,
#define next_to_ftrace_page(p) container_of(p, struct ftrace_page, next)
+static int referenced_filters(struct dyn_ftrace *rec)
+{
+ struct ftrace_ops *ops;
+ int cnt = 0;
+
+ for (ops = ftrace_ops_list; ops != &ftrace_list_end; ops = ops->next) {
+ if (ops_references_rec(ops, rec))
+ cnt++;
+ }
+
+ return cnt;
+}
+
void ftrace_release_mod(struct module *mod)
{
struct dyn_ftrace *rec;
@@ -4940,41 +4961,112 @@ void ftrace_release_mod(struct module *mod)
mutex_unlock(&ftrace_lock);
}
-static void ftrace_init_module(struct module *mod,
- unsigned long *start, unsigned long *end)
+static void ftrace_module_enable(struct module *mod)
{
- if (ftrace_disabled || start == end)
- return;
- ftrace_process_locs(mod, start, end);
+ struct dyn_ftrace *rec;
+ struct ftrace_page *pg;
+
+ mutex_lock(&ftrace_lock);
+
+ if (ftrace_disabled)
+ goto out_unlock;
+
+ /*
+ * If the tracing is enabled, go ahead and enable the record.
+ *
+ * The reason not to enable the record immediatelly is the
+ * inherent check of ftrace_make_nop/ftrace_make_call for
+ * correct previous instructions. Making first the NOP
+ * conversion puts the module to the correct state, thus
+ * passing the ftrace_make_call check.
+ *
+ * We also delay this to after the module code already set the
+ * text to read-only, as we now need to set it back to read-write
+ * so that we can modify the text.
+ */
+ if (ftrace_start_up)
+ ftrace_arch_code_modify_prepare();
+
+ do_for_each_ftrace_rec(pg, rec) {
+ int cnt;
+ /*
+ * do_for_each_ftrace_rec() is a double loop.
+ * module text shares the pg. If a record is
+ * not part of this module, then skip this pg,
+ * which the "break" will do.
+ */
+ if (!within_module_core(rec->ip, mod))
+ break;
+
+ cnt = 0;
+
+ /*
+ * When adding a module, we need to check if tracers are
+ * currently enabled and if they are, and can trace this record,
+ * we need to enable the module functions as well as update the
+ * reference counts for those function records.
+ */
+ if (ftrace_start_up)
+ cnt += referenced_filters(rec);
+
+ /* This clears FTRACE_FL_DISABLED */
+ rec->flags = cnt;
+
+ if (ftrace_start_up && cnt) {
+ int failed = __ftrace_replace_code(rec, 1);
+ if (failed) {
+ ftrace_bug(failed, rec);
+ goto out_loop;
+ }
+ }
+
+ } while_for_each_ftrace_rec();
+
+ out_loop:
+ if (ftrace_start_up)
+ ftrace_arch_code_modify_post_process();
+
+ out_unlock:
+ mutex_unlock(&ftrace_lock);
}
void ftrace_module_init(struct module *mod)
{
- ftrace_init_module(mod, mod->ftrace_callsites,
- mod->ftrace_callsites +
- mod->num_ftrace_callsites);
+ if (ftrace_disabled || !mod->num_ftrace_callsites)
+ return;
+
+ ftrace_process_locs(mod, mod->ftrace_callsites,
+ mod->ftrace_callsites + mod->num_ftrace_callsites);
}
-static int ftrace_module_notify_exit(struct notifier_block *self,
- unsigned long val, void *data)
+static int ftrace_module_notify(struct notifier_block *self,
+ unsigned long val, void *data)
{
struct module *mod = data;
- if (val == MODULE_STATE_GOING)
+ switch (val) {
+ case MODULE_STATE_COMING:
+ ftrace_module_enable(mod);
+ break;
+ case MODULE_STATE_GOING:
ftrace_release_mod(mod);
+ break;
+ default:
+ break;
+ }
return 0;
}
#else
-static int ftrace_module_notify_exit(struct notifier_block *self,
- unsigned long val, void *data)
+static int ftrace_module_notify(struct notifier_block *self,
+ unsigned long val, void *data)
{
return 0;
}
#endif /* CONFIG_MODULES */
-struct notifier_block ftrace_module_exit_nb = {
- .notifier_call = ftrace_module_notify_exit,
+struct notifier_block ftrace_module_nb = {
+ .notifier_call = ftrace_module_notify,
.priority = INT_MIN, /* Run after anything that can remove kprobes */
};
@@ -5006,7 +5098,7 @@ void __init ftrace_init(void)
__start_mcount_loc,
__stop_mcount_loc);
- ret = register_module_notifier(&ftrace_module_exit_nb);
+ ret = register_module_notifier(&ftrace_module_nb);
if (ret)
pr_warning("Failed to register trace ftrace module exit notifier\n");
@@ -5116,44 +5208,6 @@ void ftrace_reset_array_ops(struct trace_array *tr)
tr->ops->func = ftrace_stub;
}
-static void
-ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip,
- struct ftrace_ops *op, struct pt_regs *regs)
-{
- if (unlikely(trace_recursion_test(TRACE_CONTROL_BIT)))
- return;
-
- /*
- * Some of the ops may be dynamically allocated,
- * they must be freed after a synchronize_sched().
- */
- preempt_disable_notrace();
- trace_recursion_set(TRACE_CONTROL_BIT);
-
- /*
- * Control funcs (perf) uses RCU. Only trace if
- * RCU is currently active.
- */
- if (!rcu_is_watching())
- goto out;
-
- do_for_each_ftrace_op(op, ftrace_control_list) {
- if (!(op->flags & FTRACE_OPS_FL_STUB) &&
- !ftrace_function_local_disabled(op) &&
- ftrace_ops_test(op, ip, regs))
- op->func(ip, parent_ip, op, regs);
- } while_for_each_ftrace_op(op);
- out:
- trace_recursion_clear(TRACE_CONTROL_BIT);
- preempt_enable_notrace();
-}
-
-static struct ftrace_ops control_ops = {
- .func = ftrace_ops_control_func,
- .flags = FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_INITIALIZED,
- INIT_OPS_HASH(control_ops)
-};
-
static inline void
__ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *ignored, struct pt_regs *regs)
@@ -5170,8 +5224,22 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
* they must be freed after a synchronize_sched().
*/
preempt_disable_notrace();
+
do_for_each_ftrace_op(op, ftrace_ops_list) {
- if (ftrace_ops_test(op, ip, regs)) {
+ /*
+ * Check the following for each ops before calling their func:
+ * if RCU flag is set, then rcu_is_watching() must be true
+ * if PER_CPU is set, then ftrace_function_local_disable()
+ * must be false
+ * Otherwise test if the ip matches the ops filter
+ *
+ * If any of the above fails then the op->func() is not executed.
+ */
+ if ((!(op->flags & FTRACE_OPS_FL_RCU) || rcu_is_watching()) &&
+ (!(op->flags & FTRACE_OPS_FL_PER_CPU) ||
+ !ftrace_function_local_disabled(op)) &&
+ ftrace_ops_test(op, ip, regs)) {
+
if (FTRACE_WARN_ON(!op->func)) {
pr_warn("op=%p %pS\n", op, op);
goto out;
@@ -5195,7 +5263,7 @@ out:
* being NULL, or CONFIG_DYNAMIC_FTRACE_WITH_REGS.
* Note, CONFIG_DYNAMIC_FTRACE_WITH_REGS expects a full regs to be saved.
* An architecture can pass partial regs with ftrace_ops and still
- * set the ARCH_SUPPORT_FTARCE_OPS.
+ * set the ARCH_SUPPORTS_FTRACE_OPS.
*/
#if ARCH_SUPPORTS_FTRACE_OPS
static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
@@ -5212,20 +5280,29 @@ static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip)
/*
* If there's only one function registered but it does not support
- * recursion, this function will be called by the mcount trampoline.
- * This function will handle recursion protection.
+ * recursion, needs RCU protection and/or requires per cpu handling, then
+ * this function will be called by the mcount trampoline.
*/
-static void ftrace_ops_recurs_func(unsigned long ip, unsigned long parent_ip,
+static void ftrace_ops_assist_func(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *op, struct pt_regs *regs)
{
int bit;
+ if ((op->flags & FTRACE_OPS_FL_RCU) && !rcu_is_watching())
+ return;
+
bit = trace_test_and_set_recursion(TRACE_LIST_START, TRACE_LIST_MAX);
if (bit < 0)
return;
- op->func(ip, parent_ip, op, regs);
+ preempt_disable_notrace();
+ if (!(op->flags & FTRACE_OPS_FL_PER_CPU) ||
+ !ftrace_function_local_disabled(op)) {
+ op->func(ip, parent_ip, op, regs);
+ }
+
+ preempt_enable_notrace();
trace_clear_recursion(bit);
}
@@ -5243,12 +5320,12 @@ static void ftrace_ops_recurs_func(unsigned long ip, unsigned long parent_ip,
ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops)
{
/*
- * If the func handles its own recursion, call it directly.
- * Otherwise call the recursion protected function that
- * will call the ftrace ops function.
+ * If the function does not handle recursion, needs to be RCU safe,
+ * or does per cpu logic, then we need to call the assist handler.
*/
- if (!(ops->flags & FTRACE_OPS_FL_RECURSION_SAFE))
- return ftrace_ops_recurs_func;
+ if (!(ops->flags & FTRACE_OPS_FL_RECURSION_SAFE) ||
+ ops->flags & (FTRACE_OPS_FL_RCU | FTRACE_OPS_FL_PER_CPU))
+ return ftrace_ops_assist_func;
return ops->func;
}
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 75f1d05ea82d..95181e36891a 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -1001,17 +1001,13 @@ static int rb_head_page_replace(struct buffer_page *old,
/*
* rb_tail_page_update - move the tail page forward
- *
- * Returns 1 if moved tail page, 0 if someone else did.
*/
-static int rb_tail_page_update(struct ring_buffer_per_cpu *cpu_buffer,
+static void rb_tail_page_update(struct ring_buffer_per_cpu *cpu_buffer,
struct buffer_page *tail_page,
struct buffer_page *next_page)
{
- struct buffer_page *old_tail;
unsigned long old_entries;
unsigned long old_write;
- int ret = 0;
/*
* The tail page now needs to be moved forward.
@@ -1036,7 +1032,7 @@ static int rb_tail_page_update(struct ring_buffer_per_cpu *cpu_buffer,
* it is, then it is up to us to update the tail
* pointer.
*/
- if (tail_page == cpu_buffer->tail_page) {
+ if (tail_page == READ_ONCE(cpu_buffer->tail_page)) {
/* Zero the write counter */
unsigned long val = old_write & ~RB_WRITE_MASK;
unsigned long eval = old_entries & ~RB_WRITE_MASK;
@@ -1061,14 +1057,9 @@ static int rb_tail_page_update(struct ring_buffer_per_cpu *cpu_buffer,
*/
local_set(&next_page->page->commit, 0);
- old_tail = cmpxchg(&cpu_buffer->tail_page,
- tail_page, next_page);
-
- if (old_tail == tail_page)
- ret = 1;
+ /* Again, either we update tail_page or an interrupt does */
+ (void)cmpxchg(&cpu_buffer->tail_page, tail_page, next_page);
}
-
- return ret;
}
static int rb_check_bpage(struct ring_buffer_per_cpu *cpu_buffer,
@@ -1887,12 +1878,6 @@ rb_event_index(struct ring_buffer_event *event)
return (addr & ~PAGE_MASK) - BUF_PAGE_HDR_SIZE;
}
-static void rb_reset_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
-{
- cpu_buffer->read_stamp = cpu_buffer->reader_page->page->time_stamp;
- cpu_buffer->reader_page->read = 0;
-}
-
static void rb_inc_iter(struct ring_buffer_iter *iter)
{
struct ring_buffer_per_cpu *cpu_buffer = iter->cpu_buffer;
@@ -2042,12 +2027,15 @@ rb_handle_head_page(struct ring_buffer_per_cpu *cpu_buffer,
* the tail page would have moved.
*/
if (ret == RB_PAGE_NORMAL) {
+ struct buffer_page *buffer_tail_page;
+
+ buffer_tail_page = READ_ONCE(cpu_buffer->tail_page);
/*
* If the tail had moved passed next, then we need
* to reset the pointer.
*/
- if (cpu_buffer->tail_page != tail_page &&
- cpu_buffer->tail_page != next_page)
+ if (buffer_tail_page != tail_page &&
+ buffer_tail_page != next_page)
rb_head_page_set_normal(cpu_buffer, new_head,
next_page,
RB_PAGE_HEAD);
@@ -2141,6 +2129,8 @@ rb_reset_tail(struct ring_buffer_per_cpu *cpu_buffer,
local_sub(length, &tail_page->write);
}
+static inline void rb_end_commit(struct ring_buffer_per_cpu *cpu_buffer);
+
/*
* This is the slow path, force gcc not to inline it.
*/
@@ -2153,7 +2143,6 @@ rb_move_tail(struct ring_buffer_per_cpu *cpu_buffer,
struct ring_buffer *buffer = cpu_buffer->buffer;
struct buffer_page *next_page;
int ret;
- u64 ts;
next_page = tail_page;
@@ -2227,20 +2216,17 @@ rb_move_tail(struct ring_buffer_per_cpu *cpu_buffer,
}
}
- ret = rb_tail_page_update(cpu_buffer, tail_page, next_page);
- if (ret) {
- /*
- * Nested commits always have zero deltas, so
- * just reread the time stamp
- */
- ts = rb_time_stamp(buffer);
- next_page->page->time_stamp = ts;
- }
+ rb_tail_page_update(cpu_buffer, tail_page, next_page);
out_again:
rb_reset_tail(cpu_buffer, tail, info);
+ /* Commit what we have for now. */
+ rb_end_commit(cpu_buffer);
+ /* rb_end_commit() decs committing */
+ local_inc(&cpu_buffer->committing);
+
/* fail and let the caller try again */
return ERR_PTR(-EAGAIN);
@@ -2368,7 +2354,7 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer,
addr = (unsigned long)event;
addr &= PAGE_MASK;
- bpage = cpu_buffer->tail_page;
+ bpage = READ_ONCE(cpu_buffer->tail_page);
if (bpage->page == (void *)addr && rb_page_write(bpage) == old_index) {
unsigned long write_mask =
@@ -2416,7 +2402,7 @@ rb_set_commit_to_write(struct ring_buffer_per_cpu *cpu_buffer)
again:
max_count = cpu_buffer->nr_pages * 100;
- while (cpu_buffer->commit_page != cpu_buffer->tail_page) {
+ while (cpu_buffer->commit_page != READ_ONCE(cpu_buffer->tail_page)) {
if (RB_WARN_ON(cpu_buffer, !(--max_count)))
return;
if (RB_WARN_ON(cpu_buffer,
@@ -2425,8 +2411,10 @@ rb_set_commit_to_write(struct ring_buffer_per_cpu *cpu_buffer)
local_set(&cpu_buffer->commit_page->page->commit,
rb_page_write(cpu_buffer->commit_page));
rb_inc_page(cpu_buffer, &cpu_buffer->commit_page);
- cpu_buffer->write_stamp =
- cpu_buffer->commit_page->page->time_stamp;
+ /* Only update the write stamp if the page has an event */
+ if (rb_page_write(cpu_buffer->commit_page))
+ cpu_buffer->write_stamp =
+ cpu_buffer->commit_page->page->time_stamp;
/* add barrier to keep gcc from optimizing too much */
barrier();
}
@@ -2449,7 +2437,7 @@ rb_set_commit_to_write(struct ring_buffer_per_cpu *cpu_buffer)
* and pushed the tail page forward, we will be left with
* a dangling commit that will never go forward.
*/
- if (unlikely(cpu_buffer->commit_page != cpu_buffer->tail_page))
+ if (unlikely(cpu_buffer->commit_page != READ_ONCE(cpu_buffer->tail_page)))
goto again;
}
@@ -2705,7 +2693,8 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer,
if (unlikely(info->add_timestamp))
info->length += RB_LEN_TIME_EXTEND;
- tail_page = info->tail_page = cpu_buffer->tail_page;
+ /* Don't let the compiler play games with cpu_buffer->tail_page */
+ tail_page = info->tail_page = READ_ONCE(cpu_buffer->tail_page);
write = local_add_return(info->length, &tail_page->write);
/* set write to only the index of the write */
@@ -2803,8 +2792,11 @@ rb_reserve_next_event(struct ring_buffer *buffer,
event = __rb_reserve_next(cpu_buffer, &info);
- if (unlikely(PTR_ERR(event) == -EAGAIN))
+ if (unlikely(PTR_ERR(event) == -EAGAIN)) {
+ if (info.add_timestamp)
+ info.length -= RB_LEN_TIME_EXTEND;
goto again;
+ }
if (!event)
goto out_fail;
@@ -3626,7 +3618,7 @@ rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
/* Finally update the reader page to the new head */
cpu_buffer->reader_page = reader;
- rb_reset_reader_page(cpu_buffer);
+ cpu_buffer->reader_page->read = 0;
if (overwrite != cpu_buffer->last_overrun) {
cpu_buffer->lost_events = overwrite - cpu_buffer->last_overrun;
@@ -3636,6 +3628,10 @@ rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
goto again;
out:
+ /* Update the read_stamp on the first event */
+ if (reader && reader->read == 0)
+ cpu_buffer->read_stamp = reader->page->time_stamp;
+
arch_spin_unlock(&cpu_buffer->lock);
local_irq_restore(flags);
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 919d9d07686f..8414fa40bf27 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -363,8 +363,8 @@ struct trace_option_dentry {
* @name: the name chosen to select it on the available_tracers file
* @init: called when one switches to this tracer (echo name > current_tracer)
* @reset: called when one switches to another tracer
- * @start: called when tracing is unpaused (echo 1 > tracing_enabled)
- * @stop: called when tracing is paused (echo 0 > tracing_enabled)
+ * @start: called when tracing is unpaused (echo 1 > tracing_on)
+ * @stop: called when tracing is paused (echo 0 > tracing_on)
* @update_thresh: called when tracing_thresh is updated
* @open: called when the trace file is opened
* @pipe_open: called when the trace_pipe file is opened
@@ -467,8 +467,6 @@ enum {
TRACE_INTERNAL_IRQ_BIT,
TRACE_INTERNAL_SIRQ_BIT,
- TRACE_CONTROL_BIT,
-
TRACE_BRANCH_BIT,
/*
* Abuse of the trace_recursion.
diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c
index abfc903e741e..00df25fd86ef 100644
--- a/kernel/trace/trace_event_perf.c
+++ b/kernel/trace/trace_event_perf.c
@@ -1,7 +1,7 @@
/*
* trace event based perf event profiling/tracing
*
- * Copyright (C) 2009 Red Hat Inc, Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2009 Red Hat Inc, Peter Zijlstra
* Copyright (C) 2009-2010 Frederic Weisbecker <fweisbec@gmail.com>
*/
@@ -334,7 +334,7 @@ static int perf_ftrace_function_register(struct perf_event *event)
{
struct ftrace_ops *ops = &event->ftrace_ops;
- ops->flags |= FTRACE_OPS_FL_CONTROL;
+ ops->flags |= FTRACE_OPS_FL_PER_CPU | FTRACE_OPS_FL_RCU;
ops->func = perf_ftrace_function_call;
return register_ftrace_function(ops);
}
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 6bbc5f652355..f333e57c4614 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -582,6 +582,12 @@ static void __ftrace_clear_event_pids(struct trace_array *tr)
unregister_trace_sched_wakeup(event_filter_pid_sched_wakeup_probe_pre, tr);
unregister_trace_sched_wakeup(event_filter_pid_sched_wakeup_probe_post, tr);
+ unregister_trace_sched_wakeup_new(event_filter_pid_sched_wakeup_probe_pre, tr);
+ unregister_trace_sched_wakeup_new(event_filter_pid_sched_wakeup_probe_post, tr);
+
+ unregister_trace_sched_waking(event_filter_pid_sched_wakeup_probe_pre, tr);
+ unregister_trace_sched_waking(event_filter_pid_sched_wakeup_probe_post, tr);
+
list_for_each_entry(file, &tr->events, list) {
clear_bit(EVENT_FILE_FL_PID_FILTER_BIT, &file->flags);
}
@@ -1334,15 +1340,9 @@ event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
if (cnt >= PAGE_SIZE)
return -EINVAL;
- buf = (char *)__get_free_page(GFP_TEMPORARY);
- if (!buf)
- return -ENOMEM;
-
- if (copy_from_user(buf, ubuf, cnt)) {
- free_page((unsigned long) buf);
- return -EFAULT;
- }
- buf[cnt] = '\0';
+ buf = memdup_user_nul(ubuf, cnt);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
mutex_lock(&event_mutex);
file = event_file_data(filp);
@@ -1350,7 +1350,7 @@ event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
err = apply_event_filter(file, buf);
mutex_unlock(&event_mutex);
- free_page((unsigned long) buf);
+ kfree(buf);
if (err < 0)
return err;
@@ -1501,18 +1501,12 @@ subsystem_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
if (cnt >= PAGE_SIZE)
return -EINVAL;
- buf = (char *)__get_free_page(GFP_TEMPORARY);
- if (!buf)
- return -ENOMEM;
-
- if (copy_from_user(buf, ubuf, cnt)) {
- free_page((unsigned long) buf);
- return -EFAULT;
- }
- buf[cnt] = '\0';
+ buf = memdup_user_nul(ubuf, cnt);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
err = apply_subsystem_event_filter(dir, buf);
- free_page((unsigned long) buf);
+ kfree(buf);
if (err < 0)
return err;
@@ -1729,6 +1723,16 @@ ftrace_event_pid_write(struct file *filp, const char __user *ubuf,
tr, INT_MAX);
register_trace_prio_sched_wakeup(event_filter_pid_sched_wakeup_probe_post,
tr, 0);
+
+ register_trace_prio_sched_wakeup_new(event_filter_pid_sched_wakeup_probe_pre,
+ tr, INT_MAX);
+ register_trace_prio_sched_wakeup_new(event_filter_pid_sched_wakeup_probe_post,
+ tr, 0);
+
+ register_trace_prio_sched_waking(event_filter_pid_sched_wakeup_probe_pre,
+ tr, INT_MAX);
+ register_trace_prio_sched_waking(event_filter_pid_sched_wakeup_probe_post,
+ tr, 0);
}
/*
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index 42a4009fd75a..b38f617b6181 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -237,28 +237,23 @@ static ssize_t event_trigger_regex_write(struct file *file,
if (cnt >= PAGE_SIZE)
return -EINVAL;
- buf = (char *)__get_free_page(GFP_TEMPORARY);
- if (!buf)
- return -ENOMEM;
+ buf = memdup_user_nul(ubuf, cnt);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
- if (copy_from_user(buf, ubuf, cnt)) {
- free_page((unsigned long)buf);
- return -EFAULT;
- }
- buf[cnt] = '\0';
strim(buf);
mutex_lock(&event_mutex);
event_file = event_file_data(file);
if (unlikely(!event_file)) {
mutex_unlock(&event_mutex);
- free_page((unsigned long)buf);
+ kfree(buf);
return -ENODEV;
}
ret = trigger_process_regex(event_file, buf);
mutex_unlock(&event_mutex);
- free_page((unsigned long)buf);
+ kfree(buf);
if (ret < 0)
goto out;
@@ -543,11 +538,12 @@ static int register_trigger(char *glob, struct event_trigger_ops *ops,
list_add_rcu(&data->list, &file->triggers);
ret++;
+ update_cond_flag(file);
if (trace_event_trigger_enable_disable(file, 1) < 0) {
list_del_rcu(&data->list);
+ update_cond_flag(file);
ret--;
}
- update_cond_flag(file);
out:
return ret;
}
@@ -575,8 +571,8 @@ static void unregister_trigger(char *glob, struct event_trigger_ops *ops,
if (data->cmd_ops->trigger_type == test->cmd_ops->trigger_type) {
unregistered = true;
list_del_rcu(&data->list);
- update_cond_flag(file);
trace_event_trigger_enable_disable(file, 0);
+ update_cond_flag(file);
break;
}
}
@@ -1319,11 +1315,12 @@ static int event_enable_register_trigger(char *glob,
list_add_rcu(&data->list, &file->triggers);
ret++;
+ update_cond_flag(file);
if (trace_event_trigger_enable_disable(file, 1) < 0) {
list_del_rcu(&data->list);
+ update_cond_flag(file);
ret--;
}
- update_cond_flag(file);
out:
return ret;
}
@@ -1344,8 +1341,8 @@ static void event_enable_unregister_trigger(char *glob,
(enable_data->file == test_enable_data->file)) {
unregistered = true;
list_del_rcu(&data->list);
- update_cond_flag(file);
trace_event_trigger_enable_disable(file, 0);
+ update_cond_flag(file);
break;
}
}
diff --git a/kernel/trace/trace_printk.c b/kernel/trace/trace_printk.c
index 1c2b28536feb..060df67dbdd1 100644
--- a/kernel/trace/trace_printk.c
+++ b/kernel/trace/trace_printk.c
@@ -273,6 +273,7 @@ static const char **find_next(void *v, loff_t *pos)
if (*pos < last_index + start_index)
return __start___tracepoint_str + (*pos - last_index);
+ start_index += last_index;
return find_next_mod_format(start_index, v, fmt, pos);
}
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 88fefa68c516..9bafc211930c 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -602,8 +602,7 @@ static ssize_t map_write(struct file *file, const char __user *buf,
struct uid_gid_map new_map;
unsigned idx;
struct uid_gid_extent *extent = NULL;
- unsigned long page = 0;
- char *kbuf, *pos, *next_line;
+ char *kbuf = NULL, *pos, *next_line;
ssize_t ret = -EINVAL;
/*
@@ -638,23 +637,18 @@ static ssize_t map_write(struct file *file, const char __user *buf,
if (cap_valid(cap_setid) && !file_ns_capable(file, ns, CAP_SYS_ADMIN))
goto out;
- /* Get a buffer */
- ret = -ENOMEM;
- page = __get_free_page(GFP_TEMPORARY);
- kbuf = (char *) page;
- if (!page)
- goto out;
-
/* Only allow < page size writes at the beginning of the file */
ret = -EINVAL;
if ((*ppos != 0) || (count >= PAGE_SIZE))
goto out;
/* Slurp in the user data */
- ret = -EFAULT;
- if (copy_from_user(kbuf, buf, count))
+ kbuf = memdup_user_nul(buf, count);
+ if (IS_ERR(kbuf)) {
+ ret = PTR_ERR(kbuf);
+ kbuf = NULL;
goto out;
- kbuf[count] = '\0';
+ }
/* Parse the user data */
ret = -EINVAL;
@@ -756,8 +750,7 @@ static ssize_t map_write(struct file *file, const char __user *buf,
ret = count;
out:
mutex_unlock(&userns_state_mutex);
- if (page)
- free_page(page);
+ kfree(kbuf);
return ret;
}
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index 18f34cf75f74..b3ace6ebbba3 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -20,6 +20,7 @@
#include <linux/smpboot.h>
#include <linux/sched/rt.h>
#include <linux/tick.h>
+#include <linux/workqueue.h>
#include <asm/irq_regs.h>
#include <linux/kvm_para.h>
@@ -225,7 +226,15 @@ static void __touch_watchdog(void)
__this_cpu_write(watchdog_touch_ts, get_timestamp());
}
-void touch_softlockup_watchdog(void)
+/**
+ * touch_softlockup_watchdog_sched - touch watchdog on scheduler stalls
+ *
+ * Call when the scheduler may have stalled for legitimate reasons
+ * preventing the watchdog task from executing - e.g. the scheduler
+ * entering idle state. This should only be used for scheduler events.
+ * Use touch_softlockup_watchdog() for everything else.
+ */
+void touch_softlockup_watchdog_sched(void)
{
/*
* Preemption can be enabled. It doesn't matter which CPU's timestamp
@@ -233,6 +242,12 @@ void touch_softlockup_watchdog(void)
*/
raw_cpu_write(watchdog_touch_ts, 0);
}
+
+void touch_softlockup_watchdog(void)
+{
+ touch_softlockup_watchdog_sched();
+ wq_watchdog_touch(raw_smp_processor_id());
+}
EXPORT_SYMBOL(touch_softlockup_watchdog);
void touch_all_softlockup_watchdogs(void)
@@ -246,6 +261,7 @@ void touch_all_softlockup_watchdogs(void)
*/
for_each_watchdog_cpu(cpu)
per_cpu(watchdog_touch_ts, cpu) = 0;
+ wq_watchdog_touch(-1);
}
#ifdef CONFIG_HARDLOCKUP_DETECTOR
@@ -351,7 +367,7 @@ static void watchdog_overflow_callback(struct perf_event *event,
trigger_allbutself_cpu_backtrace();
if (hardlockup_panic)
- panic("Hard LOCKUP");
+ nmi_panic(regs, "Hard LOCKUP");
__this_cpu_write(hard_watchdog_warn, true);
return;
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index c579dbab2e36..61a0264e28f9 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -148,6 +148,8 @@ struct worker_pool {
int id; /* I: pool ID */
unsigned int flags; /* X: flags */
+ unsigned long watchdog_ts; /* L: watchdog timestamp */
+
struct list_head worklist; /* L: list of pending works */
int nr_workers; /* L: total number of workers */
@@ -1083,6 +1085,8 @@ static void pwq_activate_delayed_work(struct work_struct *work)
struct pool_workqueue *pwq = get_work_pwq(work);
trace_workqueue_activate_work(work);
+ if (list_empty(&pwq->pool->worklist))
+ pwq->pool->watchdog_ts = jiffies;
move_linked_works(work, &pwq->pool->worklist, NULL);
__clear_bit(WORK_STRUCT_DELAYED_BIT, work_data_bits(work));
pwq->nr_active++;
@@ -1385,6 +1389,8 @@ retry:
trace_workqueue_activate_work(work);
pwq->nr_active++;
worklist = &pwq->pool->worklist;
+ if (list_empty(worklist))
+ pwq->pool->watchdog_ts = jiffies;
} else {
work_flags |= WORK_STRUCT_DELAYED;
worklist = &pwq->delayed_works;
@@ -2157,6 +2163,8 @@ recheck:
list_first_entry(&pool->worklist,
struct work_struct, entry);
+ pool->watchdog_ts = jiffies;
+
if (likely(!(*work_data_bits(work) & WORK_STRUCT_LINKED))) {
/* optimization path, not strictly necessary */
process_one_work(worker, work);
@@ -2240,6 +2248,7 @@ repeat:
struct pool_workqueue, mayday_node);
struct worker_pool *pool = pwq->pool;
struct work_struct *work, *n;
+ bool first = true;
__set_current_state(TASK_RUNNING);
list_del_init(&pwq->mayday_node);
@@ -2256,9 +2265,14 @@ repeat:
* process'em.
*/
WARN_ON_ONCE(!list_empty(scheduled));
- list_for_each_entry_safe(work, n, &pool->worklist, entry)
- if (get_work_pwq(work) == pwq)
+ list_for_each_entry_safe(work, n, &pool->worklist, entry) {
+ if (get_work_pwq(work) == pwq) {
+ if (first)
+ pool->watchdog_ts = jiffies;
move_linked_works(work, scheduled, &n);
+ }
+ first = false;
+ }
if (!list_empty(scheduled)) {
process_scheduled_works(rescuer);
@@ -2316,6 +2330,37 @@ repeat:
goto repeat;
}
+/**
+ * check_flush_dependency - check for flush dependency sanity
+ * @target_wq: workqueue being flushed
+ * @target_work: work item being flushed (NULL for workqueue flushes)
+ *
+ * %current is trying to flush the whole @target_wq or @target_work on it.
+ * If @target_wq doesn't have %WQ_MEM_RECLAIM, verify that %current is not
+ * reclaiming memory or running on a workqueue which doesn't have
+ * %WQ_MEM_RECLAIM as that can break forward-progress guarantee leading to
+ * a deadlock.
+ */
+static void check_flush_dependency(struct workqueue_struct *target_wq,
+ struct work_struct *target_work)
+{
+ work_func_t target_func = target_work ? target_work->func : NULL;
+ struct worker *worker;
+
+ if (target_wq->flags & WQ_MEM_RECLAIM)
+ return;
+
+ worker = current_wq_worker();
+
+ WARN_ONCE(current->flags & PF_MEMALLOC,
+ "workqueue: PF_MEMALLOC task %d(%s) is flushing !WQ_MEM_RECLAIM %s:%pf",
+ current->pid, current->comm, target_wq->name, target_func);
+ WARN_ONCE(worker && (worker->current_pwq->wq->flags & WQ_MEM_RECLAIM),
+ "workqueue: WQ_MEM_RECLAIM %s:%pf is flushing !WQ_MEM_RECLAIM %s:%pf",
+ worker->current_pwq->wq->name, worker->current_func,
+ target_wq->name, target_func);
+}
+
struct wq_barrier {
struct work_struct work;
struct completion done;
@@ -2525,6 +2570,8 @@ void flush_workqueue(struct workqueue_struct *wq)
list_add_tail(&this_flusher.list, &wq->flusher_overflow);
}
+ check_flush_dependency(wq, NULL);
+
mutex_unlock(&wq->mutex);
wait_for_completion(&this_flusher.done);
@@ -2697,6 +2744,8 @@ static bool start_flush_work(struct work_struct *work, struct wq_barrier *barr)
pwq = worker->current_pwq;
}
+ check_flush_dependency(pwq->wq, work);
+
insert_wq_barrier(pwq, barr, work, worker);
spin_unlock_irq(&pool->lock);
@@ -3069,6 +3118,7 @@ static int init_worker_pool(struct worker_pool *pool)
pool->cpu = -1;
pool->node = NUMA_NO_NODE;
pool->flags |= POOL_DISASSOCIATED;
+ pool->watchdog_ts = jiffies;
INIT_LIST_HEAD(&pool->worklist);
INIT_LIST_HEAD(&pool->idle_list);
hash_init(pool->busy_hash);
@@ -3601,7 +3651,6 @@ static int apply_workqueue_attrs_locked(struct workqueue_struct *wq,
const struct workqueue_attrs *attrs)
{
struct apply_wqattrs_ctx *ctx;
- int ret = -ENOMEM;
/* only unbound workqueues can change attributes */
if (WARN_ON(!(wq->flags & WQ_UNBOUND)))
@@ -3612,16 +3661,14 @@ static int apply_workqueue_attrs_locked(struct workqueue_struct *wq,
return -EINVAL;
ctx = apply_wqattrs_prepare(wq, attrs);
+ if (!ctx)
+ return -ENOMEM;
/* the ctx has been prepared successfully, let's commit it */
- if (ctx) {
- apply_wqattrs_commit(ctx);
- ret = 0;
- }
-
+ apply_wqattrs_commit(ctx);
apply_wqattrs_cleanup(ctx);
- return ret;
+ return 0;
}
/**
@@ -4308,7 +4355,9 @@ void show_workqueue_state(void)
pr_info("pool %d:", pool->id);
pr_cont_pool_info(pool);
- pr_cont(" workers=%d", pool->nr_workers);
+ pr_cont(" hung=%us workers=%d",
+ jiffies_to_msecs(jiffies - pool->watchdog_ts) / 1000,
+ pool->nr_workers);
if (pool->manager)
pr_cont(" manager: %d",
task_pid_nr(pool->manager->task));
@@ -5167,6 +5216,154 @@ static void workqueue_sysfs_unregister(struct workqueue_struct *wq)
static void workqueue_sysfs_unregister(struct workqueue_struct *wq) { }
#endif /* CONFIG_SYSFS */
+/*
+ * Workqueue watchdog.
+ *
+ * Stall may be caused by various bugs - missing WQ_MEM_RECLAIM, illegal
+ * flush dependency, a concurrency managed work item which stays RUNNING
+ * indefinitely. Workqueue stalls can be very difficult to debug as the
+ * usual warning mechanisms don't trigger and internal workqueue state is
+ * largely opaque.
+ *
+ * Workqueue watchdog monitors all worker pools periodically and dumps
+ * state if some pools failed to make forward progress for a while where
+ * forward progress is defined as the first item on ->worklist changing.
+ *
+ * This mechanism is controlled through the kernel parameter
+ * "workqueue.watchdog_thresh" which can be updated at runtime through the
+ * corresponding sysfs parameter file.
+ */
+#ifdef CONFIG_WQ_WATCHDOG
+
+static void wq_watchdog_timer_fn(unsigned long data);
+
+static unsigned long wq_watchdog_thresh = 30;
+static struct timer_list wq_watchdog_timer =
+ TIMER_DEFERRED_INITIALIZER(wq_watchdog_timer_fn, 0, 0);
+
+static unsigned long wq_watchdog_touched = INITIAL_JIFFIES;
+static DEFINE_PER_CPU(unsigned long, wq_watchdog_touched_cpu) = INITIAL_JIFFIES;
+
+static void wq_watchdog_reset_touched(void)
+{
+ int cpu;
+
+ wq_watchdog_touched = jiffies;
+ for_each_possible_cpu(cpu)
+ per_cpu(wq_watchdog_touched_cpu, cpu) = jiffies;
+}
+
+static void wq_watchdog_timer_fn(unsigned long data)
+{
+ unsigned long thresh = READ_ONCE(wq_watchdog_thresh) * HZ;
+ bool lockup_detected = false;
+ struct worker_pool *pool;
+ int pi;
+
+ if (!thresh)
+ return;
+
+ rcu_read_lock();
+
+ for_each_pool(pool, pi) {
+ unsigned long pool_ts, touched, ts;
+
+ if (list_empty(&pool->worklist))
+ continue;
+
+ /* get the latest of pool and touched timestamps */
+ pool_ts = READ_ONCE(pool->watchdog_ts);
+ touched = READ_ONCE(wq_watchdog_touched);
+
+ if (time_after(pool_ts, touched))
+ ts = pool_ts;
+ else
+ ts = touched;
+
+ if (pool->cpu >= 0) {
+ unsigned long cpu_touched =
+ READ_ONCE(per_cpu(wq_watchdog_touched_cpu,
+ pool->cpu));
+ if (time_after(cpu_touched, ts))
+ ts = cpu_touched;
+ }
+
+ /* did we stall? */
+ if (time_after(jiffies, ts + thresh)) {
+ lockup_detected = true;
+ pr_emerg("BUG: workqueue lockup - pool");
+ pr_cont_pool_info(pool);
+ pr_cont(" stuck for %us!\n",
+ jiffies_to_msecs(jiffies - pool_ts) / 1000);
+ }
+ }
+
+ rcu_read_unlock();
+
+ if (lockup_detected)
+ show_workqueue_state();
+
+ wq_watchdog_reset_touched();
+ mod_timer(&wq_watchdog_timer, jiffies + thresh);
+}
+
+void wq_watchdog_touch(int cpu)
+{
+ if (cpu >= 0)
+ per_cpu(wq_watchdog_touched_cpu, cpu) = jiffies;
+ else
+ wq_watchdog_touched = jiffies;
+}
+
+static void wq_watchdog_set_thresh(unsigned long thresh)
+{
+ wq_watchdog_thresh = 0;
+ del_timer_sync(&wq_watchdog_timer);
+
+ if (thresh) {
+ wq_watchdog_thresh = thresh;
+ wq_watchdog_reset_touched();
+ mod_timer(&wq_watchdog_timer, jiffies + thresh * HZ);
+ }
+}
+
+static int wq_watchdog_param_set_thresh(const char *val,
+ const struct kernel_param *kp)
+{
+ unsigned long thresh;
+ int ret;
+
+ ret = kstrtoul(val, 0, &thresh);
+ if (ret)
+ return ret;
+
+ if (system_wq)
+ wq_watchdog_set_thresh(thresh);
+ else
+ wq_watchdog_thresh = thresh;
+
+ return 0;
+}
+
+static const struct kernel_param_ops wq_watchdog_thresh_ops = {
+ .set = wq_watchdog_param_set_thresh,
+ .get = param_get_ulong,
+};
+
+module_param_cb(watchdog_thresh, &wq_watchdog_thresh_ops, &wq_watchdog_thresh,
+ 0644);
+
+static void wq_watchdog_init(void)
+{
+ wq_watchdog_set_thresh(wq_watchdog_thresh);
+}
+
+#else /* CONFIG_WQ_WATCHDOG */
+
+static inline void wq_watchdog_init(void) { }
+
+#endif /* CONFIG_WQ_WATCHDOG */
+
static void __init wq_numa_init(void)
{
cpumask_var_t *tbl;
@@ -5290,6 +5487,9 @@ static int __init init_workqueues(void)
!system_unbound_wq || !system_freezable_wq ||
!system_power_efficient_wq ||
!system_freezable_power_efficient_wq);
+
+ wq_watchdog_init();
+
return 0;
}
early_initcall(init_workqueues);
diff --git a/lib/842/842_decompress.c b/lib/842/842_decompress.c
index 8881dad2a6a0..a7f278d2ed8f 100644
--- a/lib/842/842_decompress.c
+++ b/lib/842/842_decompress.c
@@ -69,7 +69,7 @@ struct sw842_param {
((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))
+ 0)
static int next_bits(struct sw842_param *p, u64 *d, u8 n);
@@ -202,10 +202,14 @@ static int __do_index(struct sw842_param *p, u8 size, u8 bits, u64 fsize)
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));
+ if (size != 2 && size != 4 && size != 8)
+ WARN(1, "__do_index invalid size %x\n", size);
+ else
+ 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;
diff --git a/lib/Kconfig b/lib/Kconfig
index f0df318104e7..5a0c1c83cdf0 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -512,9 +512,9 @@ source "lib/fonts/Kconfig"
config SG_SPLIT
def_bool n
help
- Provides a heler to split scatterlists into chunks, each chunk being a
- scatterlist. This should be selected by a driver or an API which
- whishes to split a scatterlist amongst multiple DMA channel.
+ Provides a helper to split scatterlists into chunks, each chunk being
+ a scatterlist. This should be selected by a driver or an API which
+ whishes to split a scatterlist amongst multiple DMA channels.
#
# sg chaining option
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 8c15b29d5adc..ee1ac1cc082c 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -812,6 +812,17 @@ config BOOTPARAM_HUNG_TASK_PANIC_VALUE
default 0 if !BOOTPARAM_HUNG_TASK_PANIC
default 1 if BOOTPARAM_HUNG_TASK_PANIC
+config WQ_WATCHDOG
+ bool "Detect Workqueue Stalls"
+ depends on DEBUG_KERNEL
+ help
+ Say Y here to enable stall detection on workqueues. If a
+ worker pool doesn't make forward progress on a pending work
+ item for over a given amount of time, 30s by default, a
+ warning message is printed along with dump of workqueue
+ state. This can be configured through kernel parameter
+ "workqueue.watchdog_thresh" and its sysfs counterpart.
+
endmenu # "Debug lockups and hangs"
config PANIC_ON_OOPS
@@ -1484,6 +1495,29 @@ config OF_RECONFIG_NOTIFIER_ERROR_INJECT
If unsure, say N.
+config NETDEV_NOTIFIER_ERROR_INJECT
+ tristate "Netdev notifier error injection module"
+ depends on NET && NOTIFIER_ERROR_INJECTION
+ help
+ This option provides the ability to inject artificial errors to
+ netdevice notifier chain callbacks. It is controlled through debugfs
+ interface /sys/kernel/debug/notifier-error-inject/netdev
+
+ If the notifier call chain should be failed with some events
+ notified, write the error code to "actions/<notifier event>/error".
+
+ Example: Inject netdevice mtu change error (-22 = -EINVAL)
+
+ # cd /sys/kernel/debug/notifier-error-inject/netdev
+ # echo -22 > actions/NETDEV_CHANGEMTU/error
+ # ip link set eth0 mtu 1024
+ RTNETLINK answers: Invalid argument
+
+ To compile this code as a module, choose M here: the module will
+ be called netdev-notifier-error-inject.
+
+ If unsure, say N.
+
config FAULT_INJECTION
bool "Fault-injection framework"
depends on DEBUG_KERNEL
@@ -1523,8 +1557,7 @@ config FAIL_IO_TIMEOUT
config FAIL_MMC_REQUEST
bool "Fault-injection capability for MMC IO"
- select DEBUG_FS
- depends on FAULT_INJECTION && MMC
+ depends on FAULT_INJECTION_DEBUG_FS && MMC
help
Provide fault-injection capability for MMC IO.
This will make the mmc core return data errors. This is
@@ -1853,3 +1886,42 @@ source "samples/Kconfig"
source "lib/Kconfig.kgdb"
+config ARCH_HAS_DEVMEM_IS_ALLOWED
+ bool
+
+config STRICT_DEVMEM
+ bool "Filter access to /dev/mem"
+ depends on MMU
+ depends on ARCH_HAS_DEVMEM_IS_ALLOWED
+ default y if TILE || PPC
+ ---help---
+ If this option is disabled, you allow userspace (root) access to all
+ of memory, including kernel and userspace memory. Accidental
+ access to this is obviously disastrous, but specific access can
+ be used by people debugging the kernel. Note that with PAT support
+ enabled, even in this case there are restrictions on /dev/mem
+ use due to the cache aliasing requirements.
+
+ If this option is switched on, and IO_STRICT_DEVMEM=n, the /dev/mem
+ file only allows userspace access to PCI space and the BIOS code and
+ data regions. This is sufficient for dosemu and X and all common
+ users of /dev/mem.
+
+ If in doubt, say Y.
+
+config IO_STRICT_DEVMEM
+ bool "Filter I/O access to /dev/mem"
+ depends on STRICT_DEVMEM
+ default STRICT_DEVMEM
+ ---help---
+ If this option is disabled, you allow userspace (root) access to all
+ io-memory regardless of whether a driver is actively using that
+ range. Accidental access to this is obviously disastrous, but
+ specific access can be used by people debugging kernel drivers.
+
+ If this option is switched on, the /dev/mem file only allows
+ userspace access to *idle* io-memory ranges (see /proc/iomem) This
+ may break traditional users of /dev/mem (dosemu, legacy X, etc...)
+ if the driver using a given range cannot be disabled.
+
+ If in doubt, say Y.
diff --git a/lib/Makefile b/lib/Makefile
index 7f1de26613d2..180dd4d0dd41 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -120,6 +120,7 @@ obj-$(CONFIG_FAULT_INJECTION) += fault-inject.o
obj-$(CONFIG_NOTIFIER_ERROR_INJECTION) += notifier-error-inject.o
obj-$(CONFIG_CPU_NOTIFIER_ERROR_INJECT) += cpu-notifier-error-inject.o
obj-$(CONFIG_PM_NOTIFIER_ERROR_INJECT) += pm-notifier-error-inject.o
+obj-$(CONFIG_NETDEV_NOTIFIER_ERROR_INJECT) += netdev-notifier-error-inject.o
obj-$(CONFIG_MEMORY_NOTIFIER_ERROR_INJECT) += memory-notifier-error-inject.o
obj-$(CONFIG_OF_RECONFIG_NOTIFIER_ERROR_INJECT) += \
of-reconfig-notifier-error-inject.o
diff --git a/lib/atomic64_test.c b/lib/atomic64_test.c
index 83c33a5bcffb..d62de8bf022d 100644
--- a/lib/atomic64_test.c
+++ b/lib/atomic64_test.c
@@ -16,6 +16,10 @@
#include <linux/kernel.h>
#include <linux/atomic.h>
+#ifdef CONFIG_X86
+#include <asm/processor.h> /* for boot_cpu_has below */
+#endif
+
#define TEST(bit, op, c_op, val) \
do { \
atomic##bit##_set(&v, v0); \
@@ -27,6 +31,65 @@ do { \
(unsigned long long)r); \
} while (0)
+/*
+ * Test for a atomic operation family,
+ * @test should be a macro accepting parameters (bit, op, ...)
+ */
+
+#define FAMILY_TEST(test, bit, op, args...) \
+do { \
+ test(bit, op, ##args); \
+ test(bit, op##_acquire, ##args); \
+ test(bit, op##_release, ##args); \
+ test(bit, op##_relaxed, ##args); \
+} while (0)
+
+#define TEST_RETURN(bit, op, c_op, val) \
+do { \
+ atomic##bit##_set(&v, v0); \
+ r = v0; \
+ r c_op val; \
+ BUG_ON(atomic##bit##_##op(val, &v) != r); \
+ BUG_ON(atomic##bit##_read(&v) != r); \
+} while (0)
+
+#define RETURN_FAMILY_TEST(bit, op, c_op, val) \
+do { \
+ FAMILY_TEST(TEST_RETURN, bit, op, c_op, val); \
+} while (0)
+
+#define TEST_ARGS(bit, op, init, ret, expect, args...) \
+do { \
+ atomic##bit##_set(&v, init); \
+ BUG_ON(atomic##bit##_##op(&v, ##args) != ret); \
+ BUG_ON(atomic##bit##_read(&v) != expect); \
+} while (0)
+
+#define XCHG_FAMILY_TEST(bit, init, new) \
+do { \
+ FAMILY_TEST(TEST_ARGS, bit, xchg, init, init, new, new); \
+} while (0)
+
+#define CMPXCHG_FAMILY_TEST(bit, init, new, wrong) \
+do { \
+ FAMILY_TEST(TEST_ARGS, bit, cmpxchg, \
+ init, init, new, init, new); \
+ FAMILY_TEST(TEST_ARGS, bit, cmpxchg, \
+ init, init, init, wrong, new); \
+} while (0)
+
+#define INC_RETURN_FAMILY_TEST(bit, i) \
+do { \
+ FAMILY_TEST(TEST_ARGS, bit, inc_return, \
+ i, (i) + one, (i) + one); \
+} while (0)
+
+#define DEC_RETURN_FAMILY_TEST(bit, i) \
+do { \
+ FAMILY_TEST(TEST_ARGS, bit, dec_return, \
+ i, (i) - one, (i) - one); \
+} while (0)
+
static __init void test_atomic(void)
{
int v0 = 0xaaa31337;
@@ -45,6 +108,18 @@ static __init void test_atomic(void)
TEST(, and, &=, v1);
TEST(, xor, ^=, v1);
TEST(, andnot, &= ~, v1);
+
+ RETURN_FAMILY_TEST(, add_return, +=, onestwos);
+ RETURN_FAMILY_TEST(, add_return, +=, -one);
+ RETURN_FAMILY_TEST(, sub_return, -=, onestwos);
+ RETURN_FAMILY_TEST(, sub_return, -=, -one);
+
+ INC_RETURN_FAMILY_TEST(, v0);
+ DEC_RETURN_FAMILY_TEST(, v0);
+
+ XCHG_FAMILY_TEST(, v0, v1);
+ CMPXCHG_FAMILY_TEST(, v0, v1, onestwos);
+
}
#define INIT(c) do { atomic64_set(&v, c); r = c; } while (0)
@@ -74,25 +149,10 @@ static __init void test_atomic64(void)
TEST(64, xor, ^=, v1);
TEST(64, andnot, &= ~, v1);
- INIT(v0);
- r += onestwos;
- BUG_ON(atomic64_add_return(onestwos, &v) != r);
- BUG_ON(v.counter != r);
-
- INIT(v0);
- r += -one;
- BUG_ON(atomic64_add_return(-one, &v) != r);
- BUG_ON(v.counter != r);
-
- INIT(v0);
- r -= onestwos;
- BUG_ON(atomic64_sub_return(onestwos, &v) != r);
- BUG_ON(v.counter != r);
-
- INIT(v0);
- r -= -one;
- BUG_ON(atomic64_sub_return(-one, &v) != r);
- BUG_ON(v.counter != r);
+ RETURN_FAMILY_TEST(64, add_return, +=, onestwos);
+ RETURN_FAMILY_TEST(64, add_return, +=, -one);
+ RETURN_FAMILY_TEST(64, sub_return, -=, onestwos);
+ RETURN_FAMILY_TEST(64, sub_return, -=, -one);
INIT(v0);
atomic64_inc(&v);
@@ -100,33 +160,15 @@ static __init void test_atomic64(void)
BUG_ON(v.counter != r);
INIT(v0);
- r += one;
- BUG_ON(atomic64_inc_return(&v) != r);
- BUG_ON(v.counter != r);
-
- INIT(v0);
atomic64_dec(&v);
r -= one;
BUG_ON(v.counter != r);
- INIT(v0);
- r -= one;
- BUG_ON(atomic64_dec_return(&v) != r);
- BUG_ON(v.counter != r);
+ INC_RETURN_FAMILY_TEST(64, v0);
+ DEC_RETURN_FAMILY_TEST(64, v0);
- INIT(v0);
- BUG_ON(atomic64_xchg(&v, v1) != v0);
- r = v1;
- BUG_ON(v.counter != r);
-
- INIT(v0);
- BUG_ON(atomic64_cmpxchg(&v, v0, v1) != v0);
- r = v1;
- BUG_ON(v.counter != r);
-
- INIT(v0);
- BUG_ON(atomic64_cmpxchg(&v, v2, v1) != v0);
- BUG_ON(v.counter != r);
+ XCHG_FAMILY_TEST(64, v0, v1);
+ CMPXCHG_FAMILY_TEST(64, v0, v1, v2);
INIT(v0);
BUG_ON(atomic64_add_unless(&v, one, v0));
diff --git a/lib/btree.c b/lib/btree.c
index 4264871ea1a0..f93a945274af 100644
--- a/lib/btree.c
+++ b/lib/btree.c
@@ -5,7 +5,7 @@
*
* Copyright (c) 2007-2008 Joern Engel <joern@logfs.org>
* Bits and pieces stolen from Peter Zijlstra's code, which is
- * Copyright 2007, Red Hat Inc. Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright 2007, Red Hat Inc. Peter Zijlstra
* GPLv2
*
* see http://programming.kicks-ass.net/kernel-patches/vma_lookup/btree.patch
diff --git a/lib/dma-debug.c b/lib/dma-debug.c
index 8855f019ebe8..4a1515f4b452 100644
--- a/lib/dma-debug.c
+++ b/lib/dma-debug.c
@@ -1181,7 +1181,7 @@ static inline bool overlap(void *addr, unsigned long len, void *start, void *end
static void check_for_illegal_area(struct device *dev, void *addr, unsigned long len)
{
- if (overlap(addr, len, _text, _etext) ||
+ if (overlap(addr, len, _stext, _etext) ||
overlap(addr, len, __start_rodata, __end_rodata))
err_printk(dev, NULL, "DMA-API: device driver maps memory from kernel text or rodata [addr=%p] [len=%lu]\n", addr, len);
}
@@ -1464,7 +1464,7 @@ void debug_dma_alloc_coherent(struct device *dev, size_t size,
entry->type = dma_debug_coherent;
entry->dev = dev;
entry->pfn = page_to_pfn(virt_to_page(virt));
- entry->offset = (size_t) virt & PAGE_MASK;
+ entry->offset = (size_t) virt & ~PAGE_MASK;
entry->size = size;
entry->dev_addr = dma_addr;
entry->direction = DMA_BIDIRECTIONAL;
@@ -1480,7 +1480,7 @@ void debug_dma_free_coherent(struct device *dev, size_t size,
.type = dma_debug_coherent,
.dev = dev,
.pfn = page_to_pfn(virt_to_page(virt)),
- .offset = (size_t) virt & PAGE_MASK,
+ .offset = (size_t) virt & ~PAGE_MASK,
.dev_addr = addr,
.size = size,
.direction = DMA_BIDIRECTIONAL,
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index e3952e9c8ec0..fe42b6ec3f0c 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -657,14 +657,9 @@ static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf,
pr_warn("expected <%d bytes into control\n", USER_BUF_PAGE);
return -E2BIG;
}
- tmpbuf = kmalloc(len + 1, GFP_KERNEL);
- if (!tmpbuf)
- return -ENOMEM;
- if (copy_from_user(tmpbuf, ubuf, len)) {
- kfree(tmpbuf);
- return -EFAULT;
- }
- tmpbuf[len] = '\0';
+ tmpbuf = memdup_user_nul(ubuf, len);
+ if (IS_ERR(tmpbuf))
+ return PTR_ERR(tmpbuf);
vpr_info("read %d bytes from userspace\n", (int)len);
ret = ddebug_exec_queries(tmpbuf, NULL);
diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index 75232ad0a5e7..5fecddc32b1b 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -369,7 +369,7 @@ static void memcpy_from_page(char *to, struct page *page, size_t offset, size_t
kunmap_atomic(from);
}
-static void memcpy_to_page(struct page *page, size_t offset, char *from, size_t len)
+static void memcpy_to_page(struct page *page, size_t offset, const char *from, size_t len)
{
char *to = kmap_atomic(page);
memcpy(to + offset, from, len);
@@ -383,9 +383,9 @@ static void memzero_page(struct page *page, size_t offset, size_t len)
kunmap_atomic(addr);
}
-size_t copy_to_iter(void *addr, size_t bytes, struct iov_iter *i)
+size_t copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i)
{
- char *from = addr;
+ const char *from = addr;
if (unlikely(bytes > i->count))
bytes = i->count;
@@ -704,10 +704,10 @@ size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum,
}
EXPORT_SYMBOL(csum_and_copy_from_iter);
-size_t csum_and_copy_to_iter(void *addr, size_t bytes, __wsum *csum,
+size_t csum_and_copy_to_iter(const void *addr, size_t bytes, __wsum *csum,
struct iov_iter *i)
{
- char *from = addr;
+ const char *from = addr;
__wsum sum, next;
size_t off = 0;
if (unlikely(bytes > i->count))
@@ -849,3 +849,4 @@ int import_single_range(int rw, void __user *buf, size_t len,
iov_iter_init(i, rw, iov, 1, len);
return 0;
}
+EXPORT_SYMBOL(import_single_range);
diff --git a/lib/list_debug.c b/lib/list_debug.c
index c24c2f7e296f..3859bf63561c 100644
--- a/lib/list_debug.c
+++ b/lib/list_debug.c
@@ -37,7 +37,7 @@ void __list_add(struct list_head *new,
next->prev = new;
new->next = next;
new->prev = prev;
- prev->next = new;
+ WRITE_ONCE(prev->next, new);
}
EXPORT_SYMBOL(__list_add);
diff --git a/lib/mpi/mpicoder.c b/lib/mpi/mpicoder.c
index 3db76b8c1115..ec533a6c77b5 100644
--- a/lib/mpi/mpicoder.c
+++ b/lib/mpi/mpicoder.c
@@ -135,7 +135,9 @@ EXPORT_SYMBOL_GPL(mpi_read_from_buffer);
* @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.
+ * @nbytes: receives the actual length of the data written on success and
+ * the data to-be-written on -EOVERFLOW in case buf_len was too
+ * small.
* @sign: if not NULL, it will be set to the sign of a.
*
* Return: 0 on success or error code in case of error
@@ -148,7 +150,7 @@ int mpi_read_buffer(MPI a, uint8_t *buf, unsigned buf_len, unsigned *nbytes,
unsigned int n = mpi_get_size(a);
int i, lzeros = 0;
- if (buf_len < n || !buf || !nbytes)
+ if (!buf || !nbytes)
return -EINVAL;
if (sign)
@@ -163,6 +165,11 @@ int mpi_read_buffer(MPI a, uint8_t *buf, unsigned buf_len, unsigned *nbytes,
break;
}
+ if (buf_len < n - lzeros) {
+ *nbytes = n - lzeros;
+ return -EOVERFLOW;
+ }
+
p = buf;
*nbytes = n - lzeros;
@@ -332,7 +339,8 @@ EXPORT_SYMBOL_GPL(mpi_set_buffer);
* @nbytes: in/out param - it has the be set to the maximum number of
* bytes that can be written to sgl. This has to be at least
* the size of the integer a. On return it receives the actual
- * length of the data written.
+ * length of the data written on success or the data that would
+ * be written if buffer was too small.
* @sign: if not NULL, it will be set to the sign of a.
*
* Return: 0 on success or error code in case of error
@@ -345,7 +353,7 @@ int mpi_write_to_sgl(MPI a, struct scatterlist *sgl, unsigned *nbytes,
unsigned int n = mpi_get_size(a);
int i, x, y = 0, lzeros = 0, buf_len;
- if (!nbytes || *nbytes < n)
+ if (!nbytes)
return -EINVAL;
if (sign)
@@ -360,6 +368,11 @@ int mpi_write_to_sgl(MPI a, struct scatterlist *sgl, unsigned *nbytes,
break;
}
+ if (*nbytes < n - lzeros) {
+ *nbytes = n - lzeros;
+ return -EOVERFLOW;
+ }
+
*nbytes = n - lzeros;
buf_len = sgl->length;
p2 = sg_virt(sgl);
diff --git a/lib/netdev-notifier-error-inject.c b/lib/netdev-notifier-error-inject.c
new file mode 100644
index 000000000000..13e9c62e216f
--- /dev/null
+++ b/lib/netdev-notifier-error-inject.c
@@ -0,0 +1,55 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+
+#include "notifier-error-inject.h"
+
+static int priority;
+module_param(priority, int, 0);
+MODULE_PARM_DESC(priority, "specify netdevice notifier priority");
+
+static struct notifier_err_inject netdev_notifier_err_inject = {
+ .actions = {
+ { NOTIFIER_ERR_INJECT_ACTION(NETDEV_REGISTER) },
+ { NOTIFIER_ERR_INJECT_ACTION(NETDEV_CHANGEMTU) },
+ { NOTIFIER_ERR_INJECT_ACTION(NETDEV_CHANGENAME) },
+ { NOTIFIER_ERR_INJECT_ACTION(NETDEV_PRE_UP) },
+ { NOTIFIER_ERR_INJECT_ACTION(NETDEV_PRE_TYPE_CHANGE) },
+ { NOTIFIER_ERR_INJECT_ACTION(NETDEV_POST_INIT) },
+ { NOTIFIER_ERR_INJECT_ACTION(NETDEV_PRECHANGEMTU) },
+ { NOTIFIER_ERR_INJECT_ACTION(NETDEV_PRECHANGEUPPER) },
+ { NOTIFIER_ERR_INJECT_ACTION(NETDEV_CHANGEUPPER) },
+ {}
+ }
+};
+
+static struct dentry *dir;
+
+static int netdev_err_inject_init(void)
+{
+ int err;
+
+ dir = notifier_err_inject_init("netdev", notifier_err_inject_dir,
+ &netdev_notifier_err_inject, priority);
+ if (IS_ERR(dir))
+ return PTR_ERR(dir);
+
+ err = register_netdevice_notifier(&netdev_notifier_err_inject.nb);
+ if (err)
+ debugfs_remove_recursive(dir);
+
+ return err;
+}
+
+static void netdev_err_inject_exit(void)
+{
+ unregister_netdevice_notifier(&netdev_notifier_err_inject.nb);
+ debugfs_remove_recursive(dir);
+}
+
+module_init(netdev_err_inject_init);
+module_exit(netdev_err_inject_exit);
+
+MODULE_DESCRIPTION("Netdevice notifier error injection module");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nikolay Aleksandrov <razor@blackwall.org>");
diff --git a/lib/proportions.c b/lib/proportions.c
index 6f724298f67a..efa54f259ea9 100644
--- a/lib/proportions.c
+++ b/lib/proportions.c
@@ -1,7 +1,7 @@
/*
* Floating proportions
*
- * Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra
*
* Description:
*
diff --git a/lib/rhashtable.c b/lib/rhashtable.c
index a54ff8949f91..cc808707d1cf 100644
--- a/lib/rhashtable.c
+++ b/lib/rhashtable.c
@@ -231,9 +231,6 @@ static int rhashtable_rehash_attach(struct rhashtable *ht,
*/
rcu_assign_pointer(old_tbl->future_tbl, new_tbl);
- /* Ensure the new table is visible to readers. */
- smp_wmb();
-
spin_unlock_bh(old_tbl->locks);
return 0;
@@ -389,33 +386,31 @@ static bool rhashtable_check_elasticity(struct rhashtable *ht,
return false;
}
-int rhashtable_insert_rehash(struct rhashtable *ht)
+int rhashtable_insert_rehash(struct rhashtable *ht,
+ struct bucket_table *tbl)
{
struct bucket_table *old_tbl;
struct bucket_table *new_tbl;
- struct bucket_table *tbl;
unsigned int size;
int err;
old_tbl = rht_dereference_rcu(ht->tbl, ht);
- tbl = rhashtable_last_table(ht, old_tbl);
size = tbl->size;
+ err = -EBUSY;
+
if (rht_grow_above_75(ht, tbl))
size *= 2;
/* Do not schedule more than one rehash */
else if (old_tbl != tbl)
- return -EBUSY;
+ goto fail;
+
+ err = -ENOMEM;
new_tbl = bucket_table_alloc(ht, size, GFP_ATOMIC);
- if (new_tbl == NULL) {
- /* Schedule async resize/rehash to try allocation
- * non-atomic context.
- */
- schedule_work(&ht->run_work);
- return -ENOMEM;
- }
+ if (new_tbl == NULL)
+ goto fail;
err = rhashtable_rehash_attach(ht, tbl, new_tbl);
if (err) {
@@ -426,12 +421,24 @@ int rhashtable_insert_rehash(struct rhashtable *ht)
schedule_work(&ht->run_work);
return err;
+
+fail:
+ /* Do not fail the insert if someone else did a rehash. */
+ if (likely(rcu_dereference_raw(tbl->future_tbl)))
+ return 0;
+
+ /* Schedule async rehash to retry allocation in process context. */
+ if (err == -ENOMEM)
+ schedule_work(&ht->run_work);
+
+ return err;
}
EXPORT_SYMBOL_GPL(rhashtable_insert_rehash);
-int rhashtable_insert_slow(struct rhashtable *ht, const void *key,
- struct rhash_head *obj,
- struct bucket_table *tbl)
+struct bucket_table *rhashtable_insert_slow(struct rhashtable *ht,
+ const void *key,
+ struct rhash_head *obj,
+ struct bucket_table *tbl)
{
struct rhash_head *head;
unsigned int hash;
@@ -467,7 +474,12 @@ int rhashtable_insert_slow(struct rhashtable *ht, const void *key,
exit:
spin_unlock(rht_bucket_lock(tbl, hash));
- return err;
+ if (err == 0)
+ return NULL;
+ else if (err == -EAGAIN)
+ return tbl;
+ else
+ return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(rhashtable_insert_slow);
@@ -503,10 +515,11 @@ int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter)
if (!iter->walker)
return -ENOMEM;
- mutex_lock(&ht->mutex);
- iter->walker->tbl = rht_dereference(ht->tbl, ht);
+ spin_lock(&ht->lock);
+ iter->walker->tbl =
+ rcu_dereference_protected(ht->tbl, lockdep_is_held(&ht->lock));
list_add(&iter->walker->list, &iter->walker->tbl->walkers);
- mutex_unlock(&ht->mutex);
+ spin_unlock(&ht->lock);
return 0;
}
@@ -520,10 +533,10 @@ EXPORT_SYMBOL_GPL(rhashtable_walk_init);
*/
void rhashtable_walk_exit(struct rhashtable_iter *iter)
{
- mutex_lock(&iter->ht->mutex);
+ spin_lock(&iter->ht->lock);
if (iter->walker->tbl)
list_del(&iter->walker->list);
- mutex_unlock(&iter->ht->mutex);
+ spin_unlock(&iter->ht->lock);
kfree(iter->walker);
}
EXPORT_SYMBOL_GPL(rhashtable_walk_exit);
@@ -547,14 +560,12 @@ int rhashtable_walk_start(struct rhashtable_iter *iter)
{
struct rhashtable *ht = iter->ht;
- mutex_lock(&ht->mutex);
+ rcu_read_lock();
+ spin_lock(&ht->lock);
if (iter->walker->tbl)
list_del(&iter->walker->list);
-
- rcu_read_lock();
-
- mutex_unlock(&ht->mutex);
+ spin_unlock(&ht->lock);
if (!iter->walker->tbl) {
iter->walker->tbl = rht_dereference_rcu(ht->tbl, ht);
@@ -723,9 +734,6 @@ int rhashtable_init(struct rhashtable *ht,
if (params->nulls_base && params->nulls_base < (1U << RHT_BASE_SHIFT))
return -EINVAL;
- if (params->nelem_hint)
- size = rounded_hashtable_size(params);
-
memset(ht, 0, sizeof(*ht));
mutex_init(&ht->mutex);
spin_lock_init(&ht->lock);
@@ -745,6 +753,9 @@ int rhashtable_init(struct rhashtable *ht,
ht->p.min_size = max(ht->p.min_size, HASH_MIN_SIZE);
+ if (params->nelem_hint)
+ size = rounded_hashtable_size(&ht->p);
+
/* The maximum (not average) chain length grows with the
* size of the hash table, at a rate of (log N)/(log log N).
* The value of 16 is selected so that even if the hash
diff --git a/lib/seq_buf.c b/lib/seq_buf.c
index 5c94e1012a91..cb18469e1f49 100644
--- a/lib/seq_buf.c
+++ b/lib/seq_buf.c
@@ -306,10 +306,12 @@ int seq_buf_to_user(struct seq_buf *s, char __user *ubuf, int cnt)
if (!cnt)
return 0;
- if (s->len <= s->readpos)
+ len = seq_buf_used(s);
+
+ if (len <= s->readpos)
return -EBUSY;
- len = seq_buf_used(s) - s->readpos;
+ len -= s->readpos;
if (cnt > len)
cnt = len;
ret = copy_to_user(ubuf, s->buffer + s->readpos, cnt);
diff --git a/lib/test_bpf.c b/lib/test_bpf.c
index 10cd1860e5b0..27a7a26b1ece 100644
--- a/lib/test_bpf.c
+++ b/lib/test_bpf.c
@@ -1685,6 +1685,126 @@ static struct bpf_test tests[] = {
{ },
{ { 0, 0x35d97ef2 } }
},
+ { /* Mainly checking JIT here. */
+ "MOV REG64",
+ .u.insns_int = {
+ BPF_LD_IMM64(R0, 0xffffffffffffffffLL),
+ BPF_MOV64_REG(R1, R0),
+ BPF_MOV64_REG(R2, R1),
+ BPF_MOV64_REG(R3, R2),
+ BPF_MOV64_REG(R4, R3),
+ BPF_MOV64_REG(R5, R4),
+ BPF_MOV64_REG(R6, R5),
+ BPF_MOV64_REG(R7, R6),
+ BPF_MOV64_REG(R8, R7),
+ BPF_MOV64_REG(R9, R8),
+ BPF_ALU64_IMM(BPF_MOV, R0, 0),
+ BPF_ALU64_IMM(BPF_MOV, R1, 0),
+ BPF_ALU64_IMM(BPF_MOV, R2, 0),
+ BPF_ALU64_IMM(BPF_MOV, R3, 0),
+ BPF_ALU64_IMM(BPF_MOV, R4, 0),
+ BPF_ALU64_IMM(BPF_MOV, R5, 0),
+ BPF_ALU64_IMM(BPF_MOV, R6, 0),
+ BPF_ALU64_IMM(BPF_MOV, R7, 0),
+ BPF_ALU64_IMM(BPF_MOV, R8, 0),
+ BPF_ALU64_IMM(BPF_MOV, R9, 0),
+ BPF_ALU64_REG(BPF_ADD, R0, R0),
+ BPF_ALU64_REG(BPF_ADD, R0, R1),
+ BPF_ALU64_REG(BPF_ADD, R0, R2),
+ BPF_ALU64_REG(BPF_ADD, R0, R3),
+ BPF_ALU64_REG(BPF_ADD, R0, R4),
+ BPF_ALU64_REG(BPF_ADD, R0, R5),
+ BPF_ALU64_REG(BPF_ADD, R0, R6),
+ BPF_ALU64_REG(BPF_ADD, R0, R7),
+ BPF_ALU64_REG(BPF_ADD, R0, R8),
+ BPF_ALU64_REG(BPF_ADD, R0, R9),
+ BPF_ALU64_IMM(BPF_ADD, R0, 0xfefe),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 0xfefe } }
+ },
+ { /* Mainly checking JIT here. */
+ "MOV REG32",
+ .u.insns_int = {
+ BPF_LD_IMM64(R0, 0xffffffffffffffffLL),
+ BPF_MOV64_REG(R1, R0),
+ BPF_MOV64_REG(R2, R1),
+ BPF_MOV64_REG(R3, R2),
+ BPF_MOV64_REG(R4, R3),
+ BPF_MOV64_REG(R5, R4),
+ BPF_MOV64_REG(R6, R5),
+ BPF_MOV64_REG(R7, R6),
+ BPF_MOV64_REG(R8, R7),
+ BPF_MOV64_REG(R9, R8),
+ BPF_ALU32_IMM(BPF_MOV, R0, 0),
+ BPF_ALU32_IMM(BPF_MOV, R1, 0),
+ BPF_ALU32_IMM(BPF_MOV, R2, 0),
+ BPF_ALU32_IMM(BPF_MOV, R3, 0),
+ BPF_ALU32_IMM(BPF_MOV, R4, 0),
+ BPF_ALU32_IMM(BPF_MOV, R5, 0),
+ BPF_ALU32_IMM(BPF_MOV, R6, 0),
+ BPF_ALU32_IMM(BPF_MOV, R7, 0),
+ BPF_ALU32_IMM(BPF_MOV, R8, 0),
+ BPF_ALU32_IMM(BPF_MOV, R9, 0),
+ BPF_ALU64_REG(BPF_ADD, R0, R0),
+ BPF_ALU64_REG(BPF_ADD, R0, R1),
+ BPF_ALU64_REG(BPF_ADD, R0, R2),
+ BPF_ALU64_REG(BPF_ADD, R0, R3),
+ BPF_ALU64_REG(BPF_ADD, R0, R4),
+ BPF_ALU64_REG(BPF_ADD, R0, R5),
+ BPF_ALU64_REG(BPF_ADD, R0, R6),
+ BPF_ALU64_REG(BPF_ADD, R0, R7),
+ BPF_ALU64_REG(BPF_ADD, R0, R8),
+ BPF_ALU64_REG(BPF_ADD, R0, R9),
+ BPF_ALU64_IMM(BPF_ADD, R0, 0xfefe),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 0xfefe } }
+ },
+ { /* Mainly checking JIT here. */
+ "LD IMM64",
+ .u.insns_int = {
+ BPF_LD_IMM64(R0, 0xffffffffffffffffLL),
+ BPF_MOV64_REG(R1, R0),
+ BPF_MOV64_REG(R2, R1),
+ BPF_MOV64_REG(R3, R2),
+ BPF_MOV64_REG(R4, R3),
+ BPF_MOV64_REG(R5, R4),
+ BPF_MOV64_REG(R6, R5),
+ BPF_MOV64_REG(R7, R6),
+ BPF_MOV64_REG(R8, R7),
+ BPF_MOV64_REG(R9, R8),
+ BPF_LD_IMM64(R0, 0x0LL),
+ BPF_LD_IMM64(R1, 0x0LL),
+ BPF_LD_IMM64(R2, 0x0LL),
+ BPF_LD_IMM64(R3, 0x0LL),
+ BPF_LD_IMM64(R4, 0x0LL),
+ BPF_LD_IMM64(R5, 0x0LL),
+ BPF_LD_IMM64(R6, 0x0LL),
+ BPF_LD_IMM64(R7, 0x0LL),
+ BPF_LD_IMM64(R8, 0x0LL),
+ BPF_LD_IMM64(R9, 0x0LL),
+ BPF_ALU64_REG(BPF_ADD, R0, R0),
+ BPF_ALU64_REG(BPF_ADD, R0, R1),
+ BPF_ALU64_REG(BPF_ADD, R0, R2),
+ BPF_ALU64_REG(BPF_ADD, R0, R3),
+ BPF_ALU64_REG(BPF_ADD, R0, R4),
+ BPF_ALU64_REG(BPF_ADD, R0, R5),
+ BPF_ALU64_REG(BPF_ADD, R0, R6),
+ BPF_ALU64_REG(BPF_ADD, R0, R7),
+ BPF_ALU64_REG(BPF_ADD, R0, R8),
+ BPF_ALU64_REG(BPF_ADD, R0, R9),
+ BPF_ALU64_IMM(BPF_ADD, R0, 0xfefe),
+ BPF_EXIT_INSN(),
+ },
+ INTERNAL,
+ { },
+ { { 0, 0xfefe } }
+ },
{
"INT: ALU MIX",
.u.insns_int = {
diff --git a/lib/test_rhashtable.c b/lib/test_rhashtable.c
index 8c1ad1ced72c..270bf7289b1e 100644
--- a/lib/test_rhashtable.c
+++ b/lib/test_rhashtable.c
@@ -36,9 +36,9 @@ static int runs = 4;
module_param(runs, int, 0);
MODULE_PARM_DESC(runs, "Number of test runs per variant (default: 4)");
-static int max_size = 65536;
+static int max_size = 0;
module_param(max_size, int, 0);
-MODULE_PARM_DESC(runs, "Maximum table size (default: 65536)");
+MODULE_PARM_DESC(runs, "Maximum table size (default: calculated)");
static bool shrinking = false;
module_param(shrinking, bool, 0);
@@ -52,6 +52,10 @@ static int tcount = 10;
module_param(tcount, int, 0);
MODULE_PARM_DESC(tcount, "Number of threads to spawn (default: 10)");
+static bool enomem_retry = false;
+module_param(enomem_retry, bool, 0);
+MODULE_PARM_DESC(enomem_retry, "Retry insert even if -ENOMEM was returned (default: off)");
+
struct test_obj {
int value;
struct rhash_head node;
@@ -76,6 +80,28 @@ static struct rhashtable_params test_rht_params = {
static struct semaphore prestart_sem;
static struct semaphore startup_sem = __SEMAPHORE_INITIALIZER(startup_sem, 0);
+static int insert_retry(struct rhashtable *ht, struct rhash_head *obj,
+ const struct rhashtable_params params)
+{
+ int err, retries = -1, enomem_retries = 0;
+
+ do {
+ retries++;
+ cond_resched();
+ err = rhashtable_insert_fast(ht, obj, params);
+ if (err == -ENOMEM && enomem_retry) {
+ enomem_retries++;
+ err = -EBUSY;
+ }
+ } while (err == -EBUSY);
+
+ if (enomem_retries)
+ pr_info(" %u insertions retried after -ENOMEM\n",
+ enomem_retries);
+
+ return err ? : retries;
+}
+
static int __init test_rht_lookup(struct rhashtable *ht)
{
unsigned int i;
@@ -157,7 +183,7 @@ static s64 __init test_rhashtable(struct rhashtable *ht)
{
struct test_obj *obj;
int err;
- unsigned int i, insert_fails = 0;
+ unsigned int i, insert_retries = 0;
s64 start, end;
/*
@@ -170,22 +196,16 @@ static s64 __init test_rhashtable(struct rhashtable *ht)
struct test_obj *obj = &array[i];
obj->value = i * 2;
-
- err = rhashtable_insert_fast(ht, &obj->node, test_rht_params);
- if (err == -ENOMEM || err == -EBUSY) {
- /* Mark failed inserts but continue */
- obj->value = TEST_INSERT_FAIL;
- insert_fails++;
- } else if (err) {
+ err = insert_retry(ht, &obj->node, test_rht_params);
+ if (err > 0)
+ insert_retries += err;
+ else if (err)
return err;
- }
-
- cond_resched();
}
- if (insert_fails)
- pr_info(" %u insertions failed due to memory pressure\n",
- insert_fails);
+ if (insert_retries)
+ pr_info(" %u insertions retried due to memory pressure\n",
+ insert_retries);
test_bucket_stats(ht);
rcu_read_lock();
@@ -236,13 +256,15 @@ static int thread_lookup_test(struct thread_data *tdata)
obj->value, key);
err++;
}
+
+ cond_resched();
}
return err;
}
static int threadfunc(void *data)
{
- int i, step, err = 0, insert_fails = 0;
+ int i, step, err = 0, insert_retries = 0;
struct thread_data *tdata = data;
up(&prestart_sem);
@@ -251,20 +273,18 @@ static int threadfunc(void *data)
for (i = 0; i < entries; i++) {
tdata->objs[i].value = (tdata->id << 16) | i;
- err = rhashtable_insert_fast(&ht, &tdata->objs[i].node,
- test_rht_params);
- if (err == -ENOMEM || err == -EBUSY) {
- tdata->objs[i].value = TEST_INSERT_FAIL;
- insert_fails++;
+ err = insert_retry(&ht, &tdata->objs[i].node, test_rht_params);
+ if (err > 0) {
+ insert_retries += err;
} else if (err) {
pr_err(" thread[%d]: rhashtable_insert_fast failed\n",
tdata->id);
goto out;
}
}
- if (insert_fails)
- pr_info(" thread[%d]: %d insert failures\n",
- tdata->id, insert_fails);
+ if (insert_retries)
+ pr_info(" thread[%d]: %u insertions retried due to memory pressure\n",
+ tdata->id, insert_retries);
err = thread_lookup_test(tdata);
if (err) {
@@ -285,6 +305,8 @@ static int threadfunc(void *data)
goto out;
}
tdata->objs[i].value = TEST_INSERT_FAIL;
+
+ cond_resched();
}
err = thread_lookup_test(tdata);
if (err) {
@@ -311,7 +333,7 @@ static int __init test_rht_init(void)
entries = min(entries, MAX_ENTRIES);
test_rht_params.automatic_shrinking = shrinking;
- test_rht_params.max_size = max_size;
+ test_rht_params.max_size = max_size ? : roundup_pow_of_two(entries);
test_rht_params.nelem_hint = size;
pr_info("Running rhashtable test nelem=%d, max_size=%d, shrinking=%d\n",
@@ -357,6 +379,8 @@ static int __init test_rht_init(void)
return -ENOMEM;
}
+ test_rht_params.max_size = max_size ? :
+ roundup_pow_of_two(tcount * entries);
err = rhashtable_init(&ht, &test_rht_params);
if (err < 0) {
pr_warn("Test failed: Unable to initialize hashtable: %d\n",
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index f9cee8e1233c..ac3f9476b776 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -31,6 +31,9 @@
#include <linux/dcache.h>
#include <linux/cred.h>
#include <net/addrconf.h>
+#ifdef CONFIG_BLOCK
+#include <linux/blkdev.h>
+#endif
#include <asm/page.h> /* for PAGE_SIZE */
#include <asm/sections.h> /* for dereference_function_descriptor() */
@@ -613,6 +616,26 @@ char *dentry_name(char *buf, char *end, const struct dentry *d, struct printf_sp
return buf;
}
+#ifdef CONFIG_BLOCK
+static noinline_for_stack
+char *bdev_name(char *buf, char *end, struct block_device *bdev,
+ struct printf_spec spec, const char *fmt)
+{
+ struct gendisk *hd = bdev->bd_disk;
+
+ buf = string(buf, end, hd->disk_name, spec);
+ if (bdev->bd_part->partno) {
+ if (isdigit(hd->disk_name[strlen(hd->disk_name)-1])) {
+ if (buf < end)
+ *buf = 'p';
+ buf++;
+ }
+ buf = number(buf, end, bdev->bd_part->partno, spec);
+ }
+ return buf;
+}
+#endif
+
static noinline_for_stack
char *symbol_string(char *buf, char *end, void *ptr,
struct printf_spec spec, const char *fmt)
@@ -1443,6 +1466,7 @@ int kptr_restrict __read_mostly;
* (default assumed to be phys_addr_t, passed by reference)
* - 'd[234]' For a dentry name (optionally 2-4 last components)
* - 'D[234]' Same as 'd' but for a struct file
+ * - 'g' For block_device name (gendisk + partition number)
* - 'C' For a clock, it prints the name (Common Clock Framework) or address
* (legacy clock framework) of the clock
* - 'Cn' For a clock, it prints the name (Common Clock Framework) or address
@@ -1600,6 +1624,11 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
return dentry_name(buf, end,
((const struct file *)ptr)->f_path.dentry,
spec, fmt);
+#ifdef CONFIG_BLOCK
+ case 'g':
+ return bdev_name(buf, end, ptr, spec, fmt);
+#endif
+
}
spec.flags |= SMALL;
if (spec.field_width == -1) {
diff --git a/mm/backing-dev.c b/mm/backing-dev.c
index 8ed2ffd963c5..cc5d29d2da9b 100644
--- a/mm/backing-dev.c
+++ b/mm/backing-dev.c
@@ -672,7 +672,7 @@ static int cgwb_bdi_init(struct backing_dev_info *bdi)
ret = wb_init(&bdi->wb, bdi, 1, GFP_KERNEL);
if (!ret) {
- bdi->wb.memcg_css = mem_cgroup_root_css;
+ bdi->wb.memcg_css = &root_mem_cgroup->css;
bdi->wb.blkcg_css = blkcg_root_css;
}
return ret;
@@ -957,8 +957,9 @@ EXPORT_SYMBOL(congestion_wait);
* jiffies for either a BDI to exit congestion of the given @sync queue
* or a write to complete.
*
- * In the absence of zone congestion, cond_resched() is called to yield
- * the processor if necessary but otherwise does not sleep.
+ * In the absence of zone congestion, a short sleep or a cond_resched is
+ * performed to yield the processor and to allow other subsystems to make
+ * a forward progress.
*
* The return value is 0 if the sleep is for the full timeout. Otherwise,
* it is the number of jiffies that were still remaining when the function
@@ -978,7 +979,19 @@ long wait_iff_congested(struct zone *zone, int sync, long timeout)
*/
if (atomic_read(&nr_wb_congested[sync]) == 0 ||
!test_bit(ZONE_CONGESTED, &zone->flags)) {
- cond_resched();
+
+ /*
+ * Memory allocation/reclaim might be called from a WQ
+ * context and the current implementation of the WQ
+ * concurrency control doesn't recognize that a particular
+ * WQ is congested if the worker thread is looping without
+ * ever sleeping. Therefore we have to do a short sleep
+ * here rather than calling cond_resched().
+ */
+ if (current->flags & PF_WQ_WORKER)
+ schedule_timeout(1);
+ else
+ cond_resched();
/* In case we scheduled, work out time remaining */
ret = timeout - (jiffies - start);
diff --git a/mm/bootmem.c b/mm/bootmem.c
index 3b6380784c28..91e32bc8517f 100644
--- a/mm/bootmem.c
+++ b/mm/bootmem.c
@@ -33,6 +33,7 @@ EXPORT_SYMBOL(contig_page_data);
unsigned long max_low_pfn;
unsigned long min_low_pfn;
unsigned long max_pfn;
+unsigned long long max_possible_pfn;
bootmem_data_t bootmem_node_data[MAX_NUMNODES] __initdata;
diff --git a/mm/compaction.c b/mm/compaction.c
index de3e1e71cd9f..585de54dbe8c 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -1658,14 +1658,15 @@ static void __compact_pgdat(pg_data_t *pgdat, struct compact_control *cc)
!compaction_deferred(zone, cc->order))
compact_zone(zone, cc);
- if (cc->order > 0) {
- if (zone_watermark_ok(zone, cc->order,
- low_wmark_pages(zone), 0, 0))
- compaction_defer_reset(zone, cc->order, false);
- }
-
VM_BUG_ON(!list_empty(&cc->freepages));
VM_BUG_ON(!list_empty(&cc->migratepages));
+
+ if (is_via_compact_memory(cc->order))
+ continue;
+
+ if (zone_watermark_ok(zone, cc->order,
+ low_wmark_pages(zone), 0, 0))
+ compaction_defer_reset(zone, cc->order, false);
}
}
@@ -1708,7 +1709,10 @@ static void compact_nodes(void)
/* The written value is actually unused, all memory is compacted */
int sysctl_compact_memory;
-/* This is the entry point for compacting all nodes via /proc/sys/vm */
+/*
+ * This is the entry point for compacting all nodes via
+ * /proc/sys/vm/compact_memory
+ */
int sysctl_compaction_handler(struct ctl_table *table, int write,
void __user *buffer, size_t *length, loff_t *ppos)
{
diff --git a/mm/debug.c b/mm/debug.c
index 668aa35191ca..5d2072ed8d5e 100644
--- a/mm/debug.c
+++ b/mm/debug.c
@@ -175,7 +175,7 @@ void dump_mm(const struct mm_struct *mm)
"mmap_base %lu mmap_legacy_base %lu highest_vm_end %lu\n"
"pgd %p mm_users %d mm_count %d nr_ptes %lu nr_pmds %lu map_count %d\n"
"hiwater_rss %lx hiwater_vm %lx total_vm %lx locked_vm %lx\n"
- "pinned_vm %lx shared_vm %lx exec_vm %lx stack_vm %lx\n"
+ "pinned_vm %lx data_vm %lx exec_vm %lx stack_vm %lx\n"
"start_code %lx end_code %lx start_data %lx end_data %lx\n"
"start_brk %lx brk %lx start_stack %lx\n"
"arg_start %lx arg_end %lx env_start %lx env_end %lx\n"
@@ -209,7 +209,7 @@ void dump_mm(const struct mm_struct *mm)
mm_nr_pmds((struct mm_struct *)mm),
mm->map_count,
mm->hiwater_rss, mm->hiwater_vm, mm->total_vm, mm->locked_vm,
- mm->pinned_vm, mm->shared_vm, mm->exec_vm, mm->stack_vm,
+ mm->pinned_vm, mm->data_vm, mm->exec_vm, mm->stack_vm,
mm->start_code, mm->end_code, mm->start_data, mm->end_data,
mm->start_brk, mm->brk, mm->start_stack,
mm->arg_start, mm->arg_end, mm->env_start, mm->env_end,
diff --git a/mm/filemap.c b/mm/filemap.c
index 1bb007624b53..ff42d31c891a 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -1812,19 +1812,18 @@ EXPORT_SYMBOL(generic_file_read_iter);
* This adds the requested page to the page cache if it isn't already there,
* and schedules an I/O to read in its contents from disk.
*/
-static int page_cache_read(struct file *file, pgoff_t offset)
+static int page_cache_read(struct file *file, pgoff_t offset, gfp_t gfp_mask)
{
struct address_space *mapping = file->f_mapping;
struct page *page;
int ret;
do {
- page = page_cache_alloc_cold(mapping);
+ page = __page_cache_alloc(gfp_mask|__GFP_COLD);
if (!page)
return -ENOMEM;
- ret = add_to_page_cache_lru(page, mapping, offset,
- mapping_gfp_constraint(mapping, GFP_KERNEL));
+ ret = add_to_page_cache_lru(page, mapping, offset, gfp_mask & GFP_KERNEL);
if (ret == 0)
ret = mapping->a_ops->readpage(file, page);
else if (ret == -EEXIST)
@@ -2005,7 +2004,7 @@ no_cached_page:
* We're only likely to ever get here if MADV_RANDOM is in
* effect.
*/
- error = page_cache_read(file, offset);
+ error = page_cache_read(file, offset, vmf->gfp_mask);
/*
* The page we want has now been added to the page cache.
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 62fe06bb7d04..f952f055fdcf 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -31,6 +31,33 @@
#include <asm/pgalloc.h>
#include "internal.h"
+enum scan_result {
+ SCAN_FAIL,
+ SCAN_SUCCEED,
+ SCAN_PMD_NULL,
+ SCAN_EXCEED_NONE_PTE,
+ SCAN_PTE_NON_PRESENT,
+ SCAN_PAGE_RO,
+ SCAN_NO_REFERENCED_PAGE,
+ SCAN_PAGE_NULL,
+ SCAN_SCAN_ABORT,
+ SCAN_PAGE_COUNT,
+ SCAN_PAGE_LRU,
+ SCAN_PAGE_LOCK,
+ SCAN_PAGE_ANON,
+ SCAN_ANY_PROCESS,
+ SCAN_VMA_NULL,
+ SCAN_VMA_CHECK,
+ SCAN_ADDRESS_RANGE,
+ SCAN_SWAP_CACHE_PAGE,
+ SCAN_DEL_PAGE_LRU,
+ SCAN_ALLOC_HUGE_PAGE_FAIL,
+ SCAN_CGROUP_CHARGE_FAIL
+};
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/huge_memory.h>
+
/*
* By default transparent hugepage support is disabled in order that avoid
* to risk increase the memory footprint of applications without a guaranteed
@@ -2198,26 +2225,33 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma,
unsigned long address,
pte_t *pte)
{
- struct page *page;
+ struct page *page = NULL;
pte_t *_pte;
- int none_or_zero = 0;
+ int none_or_zero = 0, result = 0;
bool referenced = false, writable = false;
+
for (_pte = pte; _pte < pte+HPAGE_PMD_NR;
_pte++, address += PAGE_SIZE) {
pte_t pteval = *_pte;
if (pte_none(pteval) || (pte_present(pteval) &&
is_zero_pfn(pte_pfn(pteval)))) {
if (!userfaultfd_armed(vma) &&
- ++none_or_zero <= khugepaged_max_ptes_none)
+ ++none_or_zero <= khugepaged_max_ptes_none) {
continue;
- else
+ } else {
+ result = SCAN_EXCEED_NONE_PTE;
goto out;
+ }
}
- if (!pte_present(pteval))
+ if (!pte_present(pteval)) {
+ result = SCAN_PTE_NON_PRESENT;
goto out;
+ }
page = vm_normal_page(vma, address, pteval);
- if (unlikely(!page))
+ if (unlikely(!page)) {
+ result = SCAN_PAGE_NULL;
goto out;
+ }
VM_BUG_ON_PAGE(PageCompound(page), page);
VM_BUG_ON_PAGE(!PageAnon(page), page);
@@ -2229,8 +2263,10 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma,
* is needed to serialize against split_huge_page
* when invoked from the VM.
*/
- if (!trylock_page(page))
+ if (!trylock_page(page)) {
+ result = SCAN_PAGE_LOCK;
goto out;
+ }
/*
* cannot use mapcount: can't collapse if there's a gup pin.
@@ -2239,6 +2275,7 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma,
*/
if (page_count(page) != 1 + !!PageSwapCache(page)) {
unlock_page(page);
+ result = SCAN_PAGE_COUNT;
goto out;
}
if (pte_write(pteval)) {
@@ -2246,6 +2283,7 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma,
} else {
if (PageSwapCache(page) && !reuse_swap_page(page)) {
unlock_page(page);
+ result = SCAN_SWAP_CACHE_PAGE;
goto out;
}
/*
@@ -2260,6 +2298,7 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma,
*/
if (isolate_lru_page(page)) {
unlock_page(page);
+ result = SCAN_DEL_PAGE_LRU;
goto out;
}
/* 0 stands for page_is_file_cache(page) == false */
@@ -2273,10 +2312,21 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma,
mmu_notifier_test_young(vma->vm_mm, address))
referenced = true;
}
- if (likely(referenced && writable))
- return 1;
+ if (likely(writable)) {
+ if (likely(referenced)) {
+ result = SCAN_SUCCEED;
+ trace_mm_collapse_huge_page_isolate(page_to_pfn(page), none_or_zero,
+ referenced, writable, result);
+ return 1;
+ }
+ } else {
+ result = SCAN_PAGE_RO;
+ }
+
out:
release_pte_pages(pte, _pte);
+ trace_mm_collapse_huge_page_isolate(page_to_pfn(page), none_or_zero,
+ referenced, writable, result);
return 0;
}
@@ -2513,7 +2563,7 @@ static void collapse_huge_page(struct mm_struct *mm,
pgtable_t pgtable;
struct page *new_page;
spinlock_t *pmd_ptl, *pte_ptl;
- int isolated;
+ int isolated, result = 0;
unsigned long hstart, hend;
struct mem_cgroup *memcg;
unsigned long mmun_start; /* For mmu_notifiers */
@@ -2528,12 +2578,15 @@ static void collapse_huge_page(struct mm_struct *mm,
/* release the mmap_sem read lock. */
new_page = khugepaged_alloc_page(hpage, gfp, mm, address, node);
- if (!new_page)
- return;
+ if (!new_page) {
+ result = SCAN_ALLOC_HUGE_PAGE_FAIL;
+ goto out_nolock;
+ }
- if (unlikely(mem_cgroup_try_charge(new_page, mm,
- gfp, &memcg)))
- return;
+ if (unlikely(mem_cgroup_try_charge(new_page, mm, gfp, &memcg))) {
+ result = SCAN_CGROUP_CHARGE_FAIL;
+ goto out_nolock;
+ }
/*
* Prevent all access to pagetables with the exception of
@@ -2541,21 +2594,31 @@ static void collapse_huge_page(struct mm_struct *mm,
* handled by the anon_vma lock + PG_lock.
*/
down_write(&mm->mmap_sem);
- if (unlikely(khugepaged_test_exit(mm)))
+ if (unlikely(khugepaged_test_exit(mm))) {
+ result = SCAN_ANY_PROCESS;
goto out;
+ }
vma = find_vma(mm, address);
- if (!vma)
+ if (!vma) {
+ result = SCAN_VMA_NULL;
goto out;
+ }
hstart = (vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK;
hend = vma->vm_end & HPAGE_PMD_MASK;
- if (address < hstart || address + HPAGE_PMD_SIZE > hend)
+ if (address < hstart || address + HPAGE_PMD_SIZE > hend) {
+ result = SCAN_ADDRESS_RANGE;
goto out;
- if (!hugepage_vma_check(vma))
+ }
+ if (!hugepage_vma_check(vma)) {
+ result = SCAN_VMA_CHECK;
goto out;
+ }
pmd = mm_find_pmd(mm, address);
- if (!pmd)
+ if (!pmd) {
+ result = SCAN_PMD_NULL;
goto out;
+ }
anon_vma_lock_write(vma->anon_vma);
@@ -2592,6 +2655,7 @@ static void collapse_huge_page(struct mm_struct *mm,
pmd_populate(mm, pmd, pmd_pgtable(_pmd));
spin_unlock(pmd_ptl);
anon_vma_unlock_write(vma->anon_vma);
+ result = SCAN_FAIL;
goto out;
}
@@ -2629,10 +2693,15 @@ static void collapse_huge_page(struct mm_struct *mm,
*hpage = NULL;
khugepaged_pages_collapsed++;
+ result = SCAN_SUCCEED;
out_up_write:
up_write(&mm->mmap_sem);
+ trace_mm_collapse_huge_page(mm, isolated, result);
return;
+out_nolock:
+ trace_mm_collapse_huge_page(mm, isolated, result);
+ return;
out:
mem_cgroup_cancel_charge(new_page, memcg);
goto out_up_write;
@@ -2645,8 +2714,8 @@ static int khugepaged_scan_pmd(struct mm_struct *mm,
{
pmd_t *pmd;
pte_t *pte, *_pte;
- int ret = 0, none_or_zero = 0;
- struct page *page;
+ int ret = 0, none_or_zero = 0, result = 0;
+ struct page *page = NULL;
unsigned long _address;
spinlock_t *ptl;
int node = NUMA_NO_NODE;
@@ -2655,8 +2724,10 @@ static int khugepaged_scan_pmd(struct mm_struct *mm,
VM_BUG_ON(address & ~HPAGE_PMD_MASK);
pmd = mm_find_pmd(mm, address);
- if (!pmd)
+ if (!pmd) {
+ result = SCAN_PMD_NULL;
goto out;
+ }
memset(khugepaged_node_load, 0, sizeof(khugepaged_node_load));
pte = pte_offset_map_lock(mm, pmd, address, &ptl);
@@ -2665,19 +2736,25 @@ static int khugepaged_scan_pmd(struct mm_struct *mm,
pte_t pteval = *_pte;
if (pte_none(pteval) || is_zero_pfn(pte_pfn(pteval))) {
if (!userfaultfd_armed(vma) &&
- ++none_or_zero <= khugepaged_max_ptes_none)
+ ++none_or_zero <= khugepaged_max_ptes_none) {
continue;
- else
+ } else {
+ result = SCAN_EXCEED_NONE_PTE;
goto out_unmap;
+ }
}
- if (!pte_present(pteval))
+ if (!pte_present(pteval)) {
+ result = SCAN_PTE_NON_PRESENT;
goto out_unmap;
+ }
if (pte_write(pteval))
writable = true;
page = vm_normal_page(vma, _address, pteval);
- if (unlikely(!page))
+ if (unlikely(!page)) {
+ result = SCAN_PAGE_NULL;
goto out_unmap;
+ }
/*
* Record which node the original page is from and save this
* information to khugepaged_node_load[].
@@ -2685,26 +2762,49 @@ static int khugepaged_scan_pmd(struct mm_struct *mm,
* hit record.
*/
node = page_to_nid(page);
- if (khugepaged_scan_abort(node))
+ if (khugepaged_scan_abort(node)) {
+ result = SCAN_SCAN_ABORT;
goto out_unmap;
+ }
khugepaged_node_load[node]++;
VM_BUG_ON_PAGE(PageCompound(page), page);
- if (!PageLRU(page) || PageLocked(page) || !PageAnon(page))
+ if (!PageLRU(page)) {
+ result = SCAN_SCAN_ABORT;
+ goto out_unmap;
+ }
+ if (PageLocked(page)) {
+ result = SCAN_PAGE_LOCK;
+ goto out_unmap;
+ }
+ if (!PageAnon(page)) {
+ result = SCAN_PAGE_ANON;
goto out_unmap;
+ }
+
/*
* cannot use mapcount: can't collapse if there's a gup pin.
* The page must only be referenced by the scanned process
* and page swap cache.
*/
- if (page_count(page) != 1 + !!PageSwapCache(page))
+ if (page_count(page) != 1 + !!PageSwapCache(page)) {
+ result = SCAN_PAGE_COUNT;
goto out_unmap;
+ }
if (pte_young(pteval) ||
page_is_young(page) || PageReferenced(page) ||
mmu_notifier_test_young(vma->vm_mm, address))
referenced = true;
}
- if (referenced && writable)
- ret = 1;
+ if (writable) {
+ if (referenced) {
+ result = SCAN_SUCCEED;
+ ret = 1;
+ } else {
+ result = SCAN_NO_REFERENCED_PAGE;
+ }
+ } else {
+ result = SCAN_PAGE_RO;
+ }
out_unmap:
pte_unmap_unlock(pte, ptl);
if (ret) {
@@ -2713,6 +2813,8 @@ out_unmap:
collapse_huge_page(mm, address, hpage, vma, node);
}
out:
+ trace_mm_khugepaged_scan_pmd(mm, page_to_pfn(page), writable, referenced,
+ none_or_zero, result);
return ret;
}
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 827bb02a43a4..be934df69b85 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -4,7 +4,6 @@
*/
#include <linux/list.h>
#include <linux/init.h>
-#include <linux/module.h>
#include <linux/mm.h>
#include <linux/seq_file.h>
#include <linux/sysctl.h>
@@ -372,8 +371,10 @@ retry_locked:
spin_unlock(&resv->lock);
trg = kmalloc(sizeof(*trg), GFP_KERNEL);
- if (!trg)
+ if (!trg) {
+ kfree(nrg);
return -ENOMEM;
+ }
spin_lock(&resv->lock);
list_add(&trg->link, &resv->region_cache);
@@ -483,8 +484,16 @@ static long region_del(struct resv_map *resv, long f, long t)
retry:
spin_lock(&resv->lock);
list_for_each_entry_safe(rg, trg, head, link) {
- if (rg->to <= f)
+ /*
+ * Skip regions before the range to be deleted. file_region
+ * ranges are normally of the form [from, to). However, there
+ * may be a "placeholder" entry in the map which is of the form
+ * (from, to) with from == to. Check for placeholder entries
+ * at the beginning of the range to be deleted.
+ */
+ if (rg->to <= f && (rg->to != rg->from || rg->to != f))
continue;
+
if (rg->from >= t)
break;
@@ -1886,7 +1895,10 @@ struct page *alloc_huge_page(struct vm_area_struct *vma,
page = __alloc_buddy_huge_page_with_mpol(h, vma, addr);
if (!page)
goto out_uncharge_cgroup;
-
+ if (!avoid_reserve && vma_has_reserves(vma, gbl_chg)) {
+ SetPagePrivate(page);
+ h->resv_huge_pages--;
+ }
spin_lock(&hugetlb_lock);
list_move(&page->lru, &h->hugepage_activelist);
/* Fall through */
@@ -2536,25 +2548,6 @@ static void hugetlb_unregister_node(struct node *node)
nhs->hugepages_kobj = NULL;
}
-/*
- * hugetlb module exit: unregister hstate attributes from node devices
- * that have them.
- */
-static void hugetlb_unregister_all_nodes(void)
-{
- int nid;
-
- /*
- * disable node device registrations.
- */
- register_hugetlbfs_with_node(NULL, NULL);
-
- /*
- * remove hstate attributes from any nodes that have them.
- */
- for (nid = 0; nid < nr_node_ids; nid++)
- hugetlb_unregister_node(node_devices[nid]);
-}
/*
* Register hstate attributes for a single node device.
@@ -2619,27 +2612,10 @@ static struct hstate *kobj_to_node_hstate(struct kobject *kobj, int *nidp)
return NULL;
}
-static void hugetlb_unregister_all_nodes(void) { }
-
static void hugetlb_register_all_nodes(void) { }
#endif
-static void __exit hugetlb_exit(void)
-{
- struct hstate *h;
-
- hugetlb_unregister_all_nodes();
-
- for_each_hstate(h) {
- kobject_put(hstate_kobjs[hstate_index(h)]);
- }
-
- kobject_put(hugepages_kobj);
- kfree(hugetlb_fault_mutex_table);
-}
-module_exit(hugetlb_exit);
-
static int __init hugetlb_init(void)
{
int i;
@@ -2677,7 +2653,7 @@ static int __init hugetlb_init(void)
mutex_init(&hugetlb_fault_mutex_table[i]);
return 0;
}
-module_init(hugetlb_init);
+subsys_initcall(hugetlb_init);
/* Should be called on processing a hugepagesz=... option */
void __init hugetlb_add_hstate(unsigned int order)
@@ -3693,12 +3669,12 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,
} else if (unlikely(is_hugetlb_entry_hwpoisoned(entry)))
return VM_FAULT_HWPOISON_LARGE |
VM_FAULT_SET_HINDEX(hstate_index(h));
+ } else {
+ ptep = huge_pte_alloc(mm, address, huge_page_size(h));
+ if (!ptep)
+ return VM_FAULT_OOM;
}
- ptep = huge_pte_alloc(mm, address, huge_page_size(h));
- if (!ptep)
- return VM_FAULT_OOM;
-
mapping = vma->vm_file->f_mapping;
idx = vma_hugecache_offset(h, vma, address);
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index 19423a45d7d7..25c0ad36fe38 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -122,8 +122,7 @@
#define BYTES_PER_POINTER sizeof(void *)
/* GFP bitmask for kmemleak internal allocations */
-#define gfp_kmemleak_mask(gfp) (((gfp) & (GFP_KERNEL | GFP_ATOMIC | \
- __GFP_NOACCOUNT)) | \
+#define gfp_kmemleak_mask(gfp) (((gfp) & (GFP_KERNEL | GFP_ATOMIC)) | \
__GFP_NORETRY | __GFP_NOMEMALLOC | \
__GFP_NOWARN)
diff --git a/mm/ksm.c b/mm/ksm.c
index b5cd647daa52..2d162c5625f6 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -740,8 +740,7 @@ static int remove_stable_node(struct stable_node *stable_node)
static int remove_all_stable_nodes(void)
{
- struct stable_node *stable_node;
- struct list_head *this, *next;
+ struct stable_node *stable_node, *next;
int nid;
int err = 0;
@@ -756,8 +755,7 @@ static int remove_all_stable_nodes(void)
cond_resched();
}
}
- list_for_each_safe(this, next, &migrate_nodes) {
- stable_node = list_entry(this, struct stable_node, list);
+ list_for_each_entry_safe(stable_node, next, &migrate_nodes, list) {
if (remove_stable_node(stable_node))
err = -EBUSY;
cond_resched();
@@ -1583,13 +1581,11 @@ static struct rmap_item *scan_get_next_rmap_item(struct page **page)
* so prune them once before each full scan.
*/
if (!ksm_merge_across_nodes) {
- struct stable_node *stable_node;
- struct list_head *this, *next;
+ struct stable_node *stable_node, *next;
struct page *page;
- list_for_each_safe(this, next, &migrate_nodes) {
- stable_node = list_entry(this,
- struct stable_node, list);
+ list_for_each_entry_safe(stable_node, next,
+ &migrate_nodes, list) {
page = get_ksm_page(stable_node, false);
if (page)
put_page(page);
@@ -2012,8 +2008,7 @@ static void wait_while_offlining(void)
static void ksm_check_stable_tree(unsigned long start_pfn,
unsigned long end_pfn)
{
- struct stable_node *stable_node;
- struct list_head *this, *next;
+ struct stable_node *stable_node, *next;
struct rb_node *node;
int nid;
@@ -2034,8 +2029,7 @@ static void ksm_check_stable_tree(unsigned long start_pfn,
cond_resched();
}
}
- list_for_each_safe(this, next, &migrate_nodes) {
- stable_node = list_entry(this, struct stable_node, list);
+ list_for_each_entry_safe(stable_node, next, &migrate_nodes, list) {
if (stable_node->kpfn >= start_pfn &&
stable_node->kpfn < end_pfn)
remove_node_from_stable_tree(stable_node);
diff --git a/mm/memblock.c b/mm/memblock.c
index d300f1329814..d2ed81e59a94 100644
--- a/mm/memblock.c
+++ b/mm/memblock.c
@@ -96,13 +96,10 @@ bool __init_memblock memblock_overlaps_region(struct memblock_type *type,
{
unsigned long i;
- for (i = 0; i < type->cnt; i++) {
- phys_addr_t rgnbase = type->regions[i].base;
- phys_addr_t rgnsize = type->regions[i].size;
- if (memblock_addrs_overlap(base, size, rgnbase, rgnsize))
+ for (i = 0; i < type->cnt; i++)
+ if (memblock_addrs_overlap(base, size, type->regions[i].base,
+ type->regions[i].size))
break;
- }
-
return i < type->cnt;
}
@@ -528,7 +525,8 @@ int __init_memblock memblock_add_range(struct memblock_type *type,
bool insert = false;
phys_addr_t obase = base;
phys_addr_t end = base + memblock_cap_size(base, &size);
- int i, nr_new;
+ int idx, nr_new;
+ struct memblock_region *rgn;
if (!size)
return 0;
@@ -552,8 +550,7 @@ repeat:
base = obase;
nr_new = 0;
- for (i = 0; i < type->cnt; i++) {
- struct memblock_region *rgn = &type->regions[i];
+ for_each_memblock_type(type, rgn) {
phys_addr_t rbase = rgn->base;
phys_addr_t rend = rbase + rgn->size;
@@ -572,7 +569,7 @@ repeat:
WARN_ON(flags != rgn->flags);
nr_new++;
if (insert)
- memblock_insert_region(type, i++, base,
+ memblock_insert_region(type, idx++, base,
rbase - base, nid,
flags);
}
@@ -584,7 +581,7 @@ repeat:
if (base < end) {
nr_new++;
if (insert)
- memblock_insert_region(type, i, base, end - base,
+ memblock_insert_region(type, idx, base, end - base,
nid, flags);
}
@@ -651,7 +648,8 @@ static int __init_memblock memblock_isolate_range(struct memblock_type *type,
int *start_rgn, int *end_rgn)
{
phys_addr_t end = base + memblock_cap_size(base, &size);
- int i;
+ int idx;
+ struct memblock_region *rgn;
*start_rgn = *end_rgn = 0;
@@ -663,8 +661,7 @@ static int __init_memblock memblock_isolate_range(struct memblock_type *type,
if (memblock_double_array(type, base, size) < 0)
return -ENOMEM;
- for (i = 0; i < type->cnt; i++) {
- struct memblock_region *rgn = &type->regions[i];
+ for_each_memblock_type(type, rgn) {
phys_addr_t rbase = rgn->base;
phys_addr_t rend = rbase + rgn->size;
@@ -681,7 +678,7 @@ static int __init_memblock memblock_isolate_range(struct memblock_type *type,
rgn->base = base;
rgn->size -= base - rbase;
type->total_size -= base - rbase;
- memblock_insert_region(type, i, rbase, base - rbase,
+ memblock_insert_region(type, idx, rbase, base - rbase,
memblock_get_region_node(rgn),
rgn->flags);
} else if (rend > end) {
@@ -692,14 +689,14 @@ static int __init_memblock memblock_isolate_range(struct memblock_type *type,
rgn->base = end;
rgn->size -= end - rbase;
type->total_size -= end - rbase;
- memblock_insert_region(type, i--, rbase, end - rbase,
+ memblock_insert_region(type, idx--, rbase, end - rbase,
memblock_get_region_node(rgn),
rgn->flags);
} else {
/* @rgn is fully contained, record it */
if (!*end_rgn)
- *start_rgn = i;
- *end_rgn = i + 1;
+ *start_rgn = idx;
+ *end_rgn = idx + 1;
}
}
@@ -822,6 +819,17 @@ int __init_memblock memblock_mark_mirror(phys_addr_t base, phys_addr_t size)
return memblock_setclr_flag(base, size, 1, MEMBLOCK_MIRROR);
}
+/**
+ * memblock_mark_nomap - Mark a memory region with flag MEMBLOCK_NOMAP.
+ * @base: the base phys addr of the region
+ * @size: the size of the region
+ *
+ * Return 0 on success, -errno on failure.
+ */
+int __init_memblock memblock_mark_nomap(phys_addr_t base, phys_addr_t size)
+{
+ return memblock_setclr_flag(base, size, 1, MEMBLOCK_NOMAP);
+}
/**
* __next_reserved_mem_region - next function for for_each_reserved_region()
@@ -913,6 +921,10 @@ void __init_memblock __next_mem_range(u64 *idx, int nid, ulong flags,
if ((flags & MEMBLOCK_MIRROR) && !memblock_is_mirror(m))
continue;
+ /* skip nomap memory unless we were asked for it explicitly */
+ if (!(flags & MEMBLOCK_NOMAP) && memblock_is_nomap(m))
+ continue;
+
if (!type_b) {
if (out_start)
*out_start = m_start;
@@ -1022,6 +1034,10 @@ void __init_memblock __next_mem_range_rev(u64 *idx, int nid, ulong flags,
if ((flags & MEMBLOCK_MIRROR) && !memblock_is_mirror(m))
continue;
+ /* skip nomap memory unless we were asked for it explicitly */
+ if (!(flags & MEMBLOCK_NOMAP) && memblock_is_nomap(m))
+ continue;
+
if (!type_b) {
if (out_start)
*out_start = m_start;
@@ -1509,16 +1525,25 @@ static int __init_memblock memblock_search(struct memblock_type *type, phys_addr
return -1;
}
-int __init memblock_is_reserved(phys_addr_t addr)
+bool __init memblock_is_reserved(phys_addr_t addr)
{
return memblock_search(&memblock.reserved, addr) != -1;
}
-int __init_memblock memblock_is_memory(phys_addr_t addr)
+bool __init_memblock memblock_is_memory(phys_addr_t addr)
{
return memblock_search(&memblock.memory, addr) != -1;
}
+int __init_memblock memblock_is_map_memory(phys_addr_t addr)
+{
+ int i = memblock_search(&memblock.memory, addr);
+
+ if (i == -1)
+ return false;
+ return !memblock_is_nomap(&memblock.memory.regions[i]);
+}
+
#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
int __init_memblock memblock_search_pfn_nid(unsigned long pfn,
unsigned long *start_pfn, unsigned long *end_pfn)
@@ -1613,12 +1638,12 @@ static void __init_memblock memblock_dump(struct memblock_type *type, char *name
{
unsigned long long base, size;
unsigned long flags;
- int i;
+ int idx;
+ struct memblock_region *rgn;
pr_info(" %s.cnt = 0x%lx\n", name, type->cnt);
- for (i = 0; i < type->cnt; i++) {
- struct memblock_region *rgn = &type->regions[i];
+ for_each_memblock_type(type, rgn) {
char nid_buf[32] = "";
base = rgn->base;
@@ -1630,7 +1655,7 @@ static void __init_memblock memblock_dump(struct memblock_type *type, char *name
memblock_get_region_node(rgn));
#endif
pr_info(" %s[%#x]\t[%#016llx-%#016llx], %#llx bytes%s flags: %#lx\n",
- name, i, base, base + size - 1, size, nid_buf, flags);
+ name, idx, base, base + size - 1, size, nid_buf, flags);
}
}
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 9acfb165eb52..54eae4f19d80 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -76,9 +76,12 @@
struct cgroup_subsys memory_cgrp_subsys __read_mostly;
EXPORT_SYMBOL(memory_cgrp_subsys);
+struct mem_cgroup *root_mem_cgroup __read_mostly;
+
#define MEM_CGROUP_RECLAIM_RETRIES 5
-static struct mem_cgroup *root_mem_cgroup __read_mostly;
-struct cgroup_subsys_state *mem_cgroup_root_css __read_mostly;
+
+/* Socket memory accounting disabled? */
+static bool cgroup_memory_nosocket;
/* Whether the swap controller is active */
#ifdef CONFIG_MEMCG_SWAP
@@ -87,6 +90,12 @@ int do_swap_account __read_mostly;
#define do_swap_account 0
#endif
+/* Whether legacy memory+swap accounting is active */
+static bool do_memsw_account(void)
+{
+ return !cgroup_subsys_on_dfl(memory_cgrp_subsys) && do_swap_account;
+}
+
static const char * const mem_cgroup_stat_names[] = {
"cache",
"rss",
@@ -288,64 +297,6 @@ static inline struct mem_cgroup *mem_cgroup_from_id(unsigned short id)
return mem_cgroup_from_css(css);
}
-/* Writing them here to avoid exposing memcg's inner layout */
-#if defined(CONFIG_INET) && defined(CONFIG_MEMCG_KMEM)
-
-void sock_update_memcg(struct sock *sk)
-{
- if (mem_cgroup_sockets_enabled) {
- struct mem_cgroup *memcg;
- struct cg_proto *cg_proto;
-
- BUG_ON(!sk->sk_prot->proto_cgroup);
-
- /* Socket cloning can throw us here with sk_cgrp already
- * filled. It won't however, necessarily happen from
- * process context. So the test for root memcg given
- * the current task's memcg won't help us in this case.
- *
- * Respecting the original socket's memcg is a better
- * decision in this case.
- */
- if (sk->sk_cgrp) {
- BUG_ON(mem_cgroup_is_root(sk->sk_cgrp->memcg));
- css_get(&sk->sk_cgrp->memcg->css);
- return;
- }
-
- rcu_read_lock();
- memcg = mem_cgroup_from_task(current);
- cg_proto = sk->sk_prot->proto_cgroup(memcg);
- if (cg_proto && test_bit(MEMCG_SOCK_ACTIVE, &cg_proto->flags) &&
- css_tryget_online(&memcg->css)) {
- sk->sk_cgrp = cg_proto;
- }
- rcu_read_unlock();
- }
-}
-EXPORT_SYMBOL(sock_update_memcg);
-
-void sock_release_memcg(struct sock *sk)
-{
- if (mem_cgroup_sockets_enabled && sk->sk_cgrp) {
- struct mem_cgroup *memcg;
- WARN_ON(!sk->sk_cgrp->memcg);
- memcg = sk->sk_cgrp->memcg;
- css_put(&sk->sk_cgrp->memcg->css);
- }
-}
-
-struct cg_proto *tcp_proto_cgroup(struct mem_cgroup *memcg)
-{
- if (!memcg || mem_cgroup_is_root(memcg))
- return NULL;
-
- return &memcg->tcp_mem;
-}
-EXPORT_SYMBOL(tcp_proto_cgroup);
-
-#endif
-
#ifdef CONFIG_MEMCG_KMEM
/*
* This will be the memcg's index in each cache's ->memcg_params.memcg_caches.
@@ -395,7 +346,7 @@ void memcg_put_cache_ids(void)
* conditional to this static branch, we'll have to allow modules that does
* kmem_cache_alloc and the such to see this symbol as well
*/
-struct static_key memcg_kmem_enabled_key;
+DEFINE_STATIC_KEY_FALSE(memcg_kmem_enabled_key);
EXPORT_SYMBOL(memcg_kmem_enabled_key);
#endif /* CONFIG_MEMCG_KMEM */
@@ -903,14 +854,20 @@ struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *root,
if (prev && reclaim->generation != iter->generation)
goto out_unlock;
- do {
+ while (1) {
pos = READ_ONCE(iter->position);
+ if (!pos || css_tryget(&pos->css))
+ break;
/*
- * A racing update may change the position and
- * put the last reference, hence css_tryget(),
- * or retry to see the updated position.
+ * css reference reached zero, so iter->position will
+ * be cleared by ->css_released. However, we should not
+ * rely on this happening soon, because ->css_released
+ * is called from a work queue, and by busy-waiting we
+ * might block it. So we clear iter->position right
+ * away.
*/
- } while (pos && !css_tryget(&pos->css));
+ (void)cmpxchg(&iter->position, pos, NULL);
+ }
}
if (pos)
@@ -956,17 +913,13 @@ struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *root,
}
if (reclaim) {
- if (cmpxchg(&iter->position, pos, memcg) == pos) {
- if (memcg)
- css_get(&memcg->css);
- if (pos)
- css_put(&pos->css);
- }
-
/*
- * pairs with css_tryget when dereferencing iter->position
- * above.
+ * The position could have already been updated by a competing
+ * thread, so check that the value hasn't changed since we read
+ * it to avoid reclaiming from the same cgroup twice.
*/
+ (void)cmpxchg(&iter->position, pos, memcg);
+
if (pos)
css_put(&pos->css);
@@ -999,6 +952,28 @@ void mem_cgroup_iter_break(struct mem_cgroup *root,
css_put(&prev->css);
}
+static void invalidate_reclaim_iterators(struct mem_cgroup *dead_memcg)
+{
+ struct mem_cgroup *memcg = dead_memcg;
+ struct mem_cgroup_reclaim_iter *iter;
+ struct mem_cgroup_per_zone *mz;
+ int nid, zid;
+ int i;
+
+ while ((memcg = parent_mem_cgroup(memcg))) {
+ for_each_node(nid) {
+ for (zid = 0; zid < MAX_NR_ZONES; zid++) {
+ mz = &memcg->nodeinfo[nid]->zoneinfo[zid];
+ for (i = 0; i <= DEF_PRIORITY; i++) {
+ iter = &mz->iter[i];
+ cmpxchg(&iter->position,
+ dead_memcg, NULL);
+ }
+ }
+ }
+ }
+}
+
/*
* Iteration constructs for visiting all cgroups (under a tree). If
* loops are exited prematurely (break), mem_cgroup_iter_break() must
@@ -1138,9 +1113,6 @@ bool task_in_mem_cgroup(struct task_struct *task, struct mem_cgroup *memcg)
return ret;
}
-#define mem_cgroup_from_counter(counter, member) \
- container_of(counter, struct mem_cgroup, member)
-
/**
* mem_cgroup_margin - calculate chargeable space of a memory cgroup
* @memcg: the memory cgroup
@@ -1159,7 +1131,7 @@ static unsigned long mem_cgroup_margin(struct mem_cgroup *memcg)
if (count < limit)
margin = limit - count;
- if (do_swap_account) {
+ if (do_memsw_account()) {
count = page_counter_read(&memcg->memsw);
limit = READ_ONCE(memcg->memsw.limit);
if (count <= limit)
@@ -1262,7 +1234,7 @@ void mem_cgroup_print_oom_info(struct mem_cgroup *memcg, struct task_struct *p)
pr_cont(":");
for (i = 0; i < MEM_CGROUP_STAT_NSTATS; i++) {
- if (i == MEM_CGROUP_STAT_SWAP && !do_swap_account)
+ if (i == MEM_CGROUP_STAT_SWAP && !do_memsw_account())
continue;
pr_cont(" %s:%luKB", mem_cgroup_stat_names[i],
K(mem_cgroup_read_stat(iter, i)));
@@ -1885,7 +1857,7 @@ static void drain_stock(struct memcg_stock_pcp *stock)
if (stock->nr_pages) {
page_counter_uncharge(&old->memory, stock->nr_pages);
- if (do_swap_account)
+ if (do_memsw_account())
page_counter_uncharge(&old->memsw, stock->nr_pages);
css_put_many(&old->css, stock->nr_pages);
stock->nr_pages = 0;
@@ -1973,6 +1945,26 @@ static int memcg_cpu_hotplug_callback(struct notifier_block *nb,
return NOTIFY_OK;
}
+static void reclaim_high(struct mem_cgroup *memcg,
+ unsigned int nr_pages,
+ gfp_t gfp_mask)
+{
+ do {
+ if (page_counter_read(&memcg->memory) <= memcg->high)
+ continue;
+ mem_cgroup_events(memcg, MEMCG_HIGH, 1);
+ try_to_free_mem_cgroup_pages(memcg, nr_pages, gfp_mask, true);
+ } while ((memcg = parent_mem_cgroup(memcg)));
+}
+
+static void high_work_func(struct work_struct *work)
+{
+ struct mem_cgroup *memcg;
+
+ memcg = container_of(work, struct mem_cgroup, high_work);
+ reclaim_high(memcg, CHARGE_BATCH, GFP_KERNEL);
+}
+
/*
* Scheduled by try_charge() to be executed from the userland return path
* and reclaims memory over the high limit.
@@ -1980,20 +1972,13 @@ static int memcg_cpu_hotplug_callback(struct notifier_block *nb,
void mem_cgroup_handle_over_high(void)
{
unsigned int nr_pages = current->memcg_nr_pages_over_high;
- struct mem_cgroup *memcg, *pos;
+ struct mem_cgroup *memcg;
if (likely(!nr_pages))
return;
- pos = memcg = get_mem_cgroup_from_mm(current->mm);
-
- do {
- if (page_counter_read(&pos->memory) <= pos->high)
- continue;
- mem_cgroup_events(pos, MEMCG_HIGH, 1);
- try_to_free_mem_cgroup_pages(pos, nr_pages, GFP_KERNEL, true);
- } while ((pos = parent_mem_cgroup(pos)));
-
+ memcg = get_mem_cgroup_from_mm(current->mm);
+ reclaim_high(memcg, nr_pages, GFP_KERNEL);
css_put(&memcg->css);
current->memcg_nr_pages_over_high = 0;
}
@@ -2015,11 +2000,11 @@ retry:
if (consume_stock(memcg, nr_pages))
return 0;
- if (!do_swap_account ||
+ if (!do_memsw_account() ||
page_counter_try_charge(&memcg->memsw, batch, &counter)) {
if (page_counter_try_charge(&memcg->memory, batch, &counter))
goto done_restock;
- if (do_swap_account)
+ if (do_memsw_account())
page_counter_uncharge(&memcg->memsw, batch);
mem_over_limit = mem_cgroup_from_counter(counter, memory);
} else {
@@ -2106,7 +2091,7 @@ force:
* temporarily by force charging it.
*/
page_counter_charge(&memcg->memory, nr_pages);
- if (do_swap_account)
+ if (do_memsw_account())
page_counter_charge(&memcg->memsw, nr_pages);
css_get_many(&memcg->css, nr_pages);
@@ -2128,7 +2113,12 @@ done_restock:
*/
do {
if (page_counter_read(&memcg->memory) > memcg->high) {
- current->memcg_nr_pages_over_high += nr_pages;
+ /* Don't bother a random interrupted task */
+ if (in_interrupt()) {
+ schedule_work(&memcg->high_work);
+ break;
+ }
+ current->memcg_nr_pages_over_high += batch;
set_notify_resume(current);
break;
}
@@ -2143,7 +2133,7 @@ static void cancel_charge(struct mem_cgroup *memcg, unsigned int nr_pages)
return;
page_counter_uncharge(&memcg->memory, nr_pages);
- if (do_swap_account)
+ if (do_memsw_account())
page_counter_uncharge(&memcg->memsw, nr_pages);
css_put_many(&memcg->css, nr_pages);
@@ -2332,7 +2322,7 @@ static void memcg_schedule_kmem_cache_create(struct mem_cgroup *memcg,
* Can't be called in interrupt context or from kernel threads.
* This function needs to be called with rcu_read_lock() held.
*/
-struct kmem_cache *__memcg_kmem_get_cache(struct kmem_cache *cachep)
+struct kmem_cache *__memcg_kmem_get_cache(struct kmem_cache *cachep, gfp_t gfp)
{
struct mem_cgroup *memcg;
struct kmem_cache *memcg_cachep;
@@ -2340,6 +2330,12 @@ struct kmem_cache *__memcg_kmem_get_cache(struct kmem_cache *cachep)
VM_BUG_ON(!is_root_cache(cachep));
+ if (cachep->flags & SLAB_ACCOUNT)
+ gfp |= __GFP_ACCOUNT;
+
+ if (!(gfp & __GFP_ACCOUNT))
+ return cachep;
+
if (current->memcg_kmem_skip_account)
return cachep;
@@ -2423,7 +2419,7 @@ void __memcg_kmem_uncharge(struct page *page, int order)
page_counter_uncharge(&memcg->kmem, nr_pages);
page_counter_uncharge(&memcg->memory, nr_pages);
- if (do_swap_account)
+ if (do_memsw_account())
page_counter_uncharge(&memcg->memsw, nr_pages);
page->mem_cgroup = NULL;
@@ -2911,7 +2907,7 @@ static int memcg_activate_kmem(struct mem_cgroup *memcg,
err = page_counter_limit(&memcg->kmem, nr_pages);
VM_BUG_ON(err);
- static_key_slow_inc(&memcg_kmem_enabled_key);
+ static_branch_inc(&memcg_kmem_enabled_key);
/*
* A memory cgroup is considered kmem-active as soon as it gets
* kmemcg_id. Setting the id after enabling static branching will
@@ -3138,7 +3134,7 @@ static int memcg_stat_show(struct seq_file *m, void *v)
BUILD_BUG_ON(ARRAY_SIZE(mem_cgroup_lru_names) != NR_LRU_LISTS);
for (i = 0; i < MEM_CGROUP_STAT_NSTATS; i++) {
- if (i == MEM_CGROUP_STAT_SWAP && !do_swap_account)
+ if (i == MEM_CGROUP_STAT_SWAP && !do_memsw_account())
continue;
seq_printf(m, "%s %lu\n", mem_cgroup_stat_names[i],
mem_cgroup_read_stat(memcg, i) * PAGE_SIZE);
@@ -3160,14 +3156,14 @@ static int memcg_stat_show(struct seq_file *m, void *v)
}
seq_printf(m, "hierarchical_memory_limit %llu\n",
(u64)memory * PAGE_SIZE);
- if (do_swap_account)
+ if (do_memsw_account())
seq_printf(m, "hierarchical_memsw_limit %llu\n",
(u64)memsw * PAGE_SIZE);
for (i = 0; i < MEM_CGROUP_STAT_NSTATS; i++) {
unsigned long long val = 0;
- if (i == MEM_CGROUP_STAT_SWAP && !do_swap_account)
+ if (i == MEM_CGROUP_STAT_SWAP && !do_memsw_account())
continue;
for_each_mem_cgroup_tree(mi, memcg)
val += mem_cgroup_read_stat(mi, i) * PAGE_SIZE;
@@ -3298,7 +3294,7 @@ static void mem_cgroup_threshold(struct mem_cgroup *memcg)
{
while (memcg) {
__mem_cgroup_threshold(memcg, false);
- if (do_swap_account)
+ if (do_memsw_account())
__mem_cgroup_threshold(memcg, true);
memcg = parent_mem_cgroup(memcg);
@@ -3597,7 +3593,7 @@ static int memcg_init_kmem(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
if (ret)
return ret;
- return mem_cgroup_sockets_init(memcg, ss);
+ return tcp_init_cgroup(memcg, ss);
}
static void memcg_deactivate_kmem(struct mem_cgroup *memcg)
@@ -3650,10 +3646,10 @@ static void memcg_destroy_kmem(struct mem_cgroup *memcg)
{
if (memcg->kmem_acct_activated) {
memcg_destroy_kmem_caches(memcg);
- static_key_slow_dec(&memcg_kmem_enabled_key);
+ static_branch_dec(&memcg_kmem_enabled_key);
WARN_ON(page_counter_read(&memcg->kmem));
}
- mem_cgroup_sockets_destroy(memcg);
+ tcp_destroy_cgroup(memcg);
}
#else
static int memcg_init_kmem(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
@@ -4172,6 +4168,8 @@ static void __mem_cgroup_free(struct mem_cgroup *memcg)
{
int node;
+ cancel_work_sync(&memcg->high_work);
+
mem_cgroup_remove_from_trees(memcg);
for_each_node(node)
@@ -4182,17 +4180,6 @@ static void __mem_cgroup_free(struct mem_cgroup *memcg)
kfree(memcg);
}
-/*
- * Returns the parent mem_cgroup in memcgroup hierarchy with hierarchy enabled.
- */
-struct mem_cgroup *parent_mem_cgroup(struct mem_cgroup *memcg)
-{
- if (!memcg->memory.parent)
- return NULL;
- return mem_cgroup_from_counter(memcg->memory.parent, memory);
-}
-EXPORT_SYMBOL(parent_mem_cgroup);
-
static struct cgroup_subsys_state * __ref
mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
{
@@ -4211,7 +4198,6 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
/* root ? */
if (parent_css == NULL) {
root_mem_cgroup = memcg;
- mem_cgroup_root_css = &memcg->css;
page_counter_init(&memcg->memory, NULL);
memcg->high = PAGE_COUNTER_MAX;
memcg->soft_limit = PAGE_COUNTER_MAX;
@@ -4219,6 +4205,7 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
page_counter_init(&memcg->kmem, NULL);
}
+ INIT_WORK(&memcg->high_work, high_work_func);
memcg->last_scanned_node = MAX_NUMNODES;
INIT_LIST_HEAD(&memcg->oom_notify);
memcg->move_charge_at_immigrate = 0;
@@ -4233,6 +4220,9 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
#ifdef CONFIG_CGROUP_WRITEBACK
INIT_LIST_HEAD(&memcg->cgwb_list);
#endif
+#ifdef CONFIG_INET
+ memcg->socket_pressure = jiffies;
+#endif
return &memcg->css;
free_out:
@@ -4290,6 +4280,11 @@ mem_cgroup_css_online(struct cgroup_subsys_state *css)
if (ret)
return ret;
+#ifdef CONFIG_INET
+ if (cgroup_subsys_on_dfl(memory_cgrp_subsys) && !cgroup_memory_nosocket)
+ static_branch_inc(&memcg_sockets_enabled_key);
+#endif
+
/*
* Make sure the memcg is initialized: mem_cgroup_iter()
* orders reading memcg->initialized against its callers
@@ -4324,11 +4319,22 @@ static void mem_cgroup_css_offline(struct cgroup_subsys_state *css)
wb_memcg_offline(memcg);
}
+static void mem_cgroup_css_released(struct cgroup_subsys_state *css)
+{
+ struct mem_cgroup *memcg = mem_cgroup_from_css(css);
+
+ invalidate_reclaim_iterators(memcg);
+}
+
static void mem_cgroup_css_free(struct cgroup_subsys_state *css)
{
struct mem_cgroup *memcg = mem_cgroup_from_css(css);
memcg_destroy_kmem(memcg);
+#ifdef CONFIG_INET
+ if (cgroup_subsys_on_dfl(memory_cgrp_subsys) && !cgroup_memory_nosocket)
+ static_branch_dec(&memcg_sockets_enabled_key);
+#endif
__mem_cgroup_free(memcg);
}
@@ -4445,7 +4451,7 @@ static struct page *mc_handle_swap_pte(struct vm_area_struct *vma,
* we call find_get_page() with swapper_space directly.
*/
page = find_get_page(swap_address_space(ent), ent.val);
- if (do_swap_account)
+ if (do_memsw_account())
entry->val = ent.val;
return page;
@@ -4480,7 +4486,7 @@ static struct page *mc_handle_file_pte(struct vm_area_struct *vma,
page = find_get_entry(mapping, pgoff);
if (radix_tree_exceptional_entry(page)) {
swp_entry_t swp = radix_to_swp_entry(page);
- if (do_swap_account)
+ if (do_memsw_account())
*entry = swp;
page = find_get_page(swap_address_space(swp), swp.val);
}
@@ -4779,23 +4785,18 @@ static void mem_cgroup_clear_mc(void)
spin_unlock(&mc.lock);
}
-static int mem_cgroup_can_attach(struct cgroup_subsys_state *css,
- struct cgroup_taskset *tset)
+static int mem_cgroup_can_attach(struct cgroup_taskset *tset)
{
- struct mem_cgroup *memcg = mem_cgroup_from_css(css);
+ struct cgroup_subsys_state *css;
+ struct mem_cgroup *memcg = NULL; /* unneeded init to make gcc happy */
struct mem_cgroup *from;
struct task_struct *leader, *p;
struct mm_struct *mm;
unsigned long move_flags;
int ret = 0;
- /*
- * We are now commited to this value whatever it is. Changes in this
- * tunable will only affect upcoming migrations, not the current one.
- * So we need to save it, and keep it going.
- */
- move_flags = READ_ONCE(memcg->move_charge_at_immigrate);
- if (!move_flags)
+ /* charge immigration isn't supported on the default hierarchy */
+ if (cgroup_subsys_on_dfl(memory_cgrp_subsys))
return 0;
/*
@@ -4805,13 +4806,23 @@ static int mem_cgroup_can_attach(struct cgroup_subsys_state *css,
* multiple.
*/
p = NULL;
- cgroup_taskset_for_each_leader(leader, tset) {
+ cgroup_taskset_for_each_leader(leader, css, tset) {
WARN_ON_ONCE(p);
p = leader;
+ memcg = mem_cgroup_from_css(css);
}
if (!p)
return 0;
+ /*
+ * We are now commited to this value whatever it is. Changes in this
+ * tunable will only affect upcoming migrations, not the current one.
+ * So we need to save it, and keep it going.
+ */
+ move_flags = READ_ONCE(memcg->move_charge_at_immigrate);
+ if (!move_flags)
+ return 0;
+
from = mem_cgroup_from_task(p);
VM_BUG_ON(from == memcg);
@@ -4842,8 +4853,7 @@ static int mem_cgroup_can_attach(struct cgroup_subsys_state *css,
return ret;
}
-static void mem_cgroup_cancel_attach(struct cgroup_subsys_state *css,
- struct cgroup_taskset *tset)
+static void mem_cgroup_cancel_attach(struct cgroup_taskset *tset)
{
if (mc.to)
mem_cgroup_clear_mc();
@@ -4985,10 +4995,10 @@ retry:
atomic_dec(&mc.from->moving_account);
}
-static void mem_cgroup_move_task(struct cgroup_subsys_state *css,
- struct cgroup_taskset *tset)
+static void mem_cgroup_move_task(struct cgroup_taskset *tset)
{
- struct task_struct *p = cgroup_taskset_first(tset);
+ struct cgroup_subsys_state *css;
+ struct task_struct *p = cgroup_taskset_first(tset, &css);
struct mm_struct *mm = get_task_mm(p);
if (mm) {
@@ -5000,17 +5010,14 @@ static void mem_cgroup_move_task(struct cgroup_subsys_state *css,
mem_cgroup_clear_mc();
}
#else /* !CONFIG_MMU */
-static int mem_cgroup_can_attach(struct cgroup_subsys_state *css,
- struct cgroup_taskset *tset)
+static int mem_cgroup_can_attach(struct cgroup_taskset *tset)
{
return 0;
}
-static void mem_cgroup_cancel_attach(struct cgroup_subsys_state *css,
- struct cgroup_taskset *tset)
+static void mem_cgroup_cancel_attach(struct cgroup_taskset *tset)
{
}
-static void mem_cgroup_move_task(struct cgroup_subsys_state *css,
- struct cgroup_taskset *tset)
+static void mem_cgroup_move_task(struct cgroup_taskset *tset)
{
}
#endif
@@ -5184,6 +5191,7 @@ struct cgroup_subsys memory_cgrp_subsys = {
.css_alloc = mem_cgroup_css_alloc,
.css_online = mem_cgroup_css_online,
.css_offline = mem_cgroup_css_offline,
+ .css_released = mem_cgroup_css_released,
.css_free = mem_cgroup_css_free,
.css_reset = mem_cgroup_css_reset,
.can_attach = mem_cgroup_can_attach,
@@ -5271,7 +5279,7 @@ int mem_cgroup_try_charge(struct page *page, struct mm_struct *mm,
if (page->mem_cgroup)
goto out;
- if (do_swap_account) {
+ if (do_memsw_account()) {
swp_entry_t ent = { .val = page_private(page), };
unsigned short id = lookup_swap_cgroup_id(ent);
@@ -5345,7 +5353,7 @@ void mem_cgroup_commit_charge(struct page *page, struct mem_cgroup *memcg,
memcg_check_events(memcg, page);
local_irq_enable();
- if (do_swap_account && PageSwapCache(page)) {
+ if (do_memsw_account() && PageSwapCache(page)) {
swp_entry_t entry = { .val = page_private(page) };
/*
* The swap entry might not get freed for a long time,
@@ -5394,7 +5402,7 @@ static void uncharge_batch(struct mem_cgroup *memcg, unsigned long pgpgout,
if (!mem_cgroup_is_root(memcg)) {
page_counter_uncharge(&memcg->memory, nr_pages);
- if (do_swap_account)
+ if (do_memsw_account())
page_counter_uncharge(&memcg->memsw, nr_pages);
memcg_oom_recover(memcg);
}
@@ -5511,11 +5519,11 @@ void mem_cgroup_uncharge_list(struct list_head *page_list)
* mem_cgroup_replace_page - migrate a charge to another page
* @oldpage: currently charged page
* @newpage: page to transfer the charge to
- * @lrucare: either or both pages might be on the LRU already
*
* Migrate the charge from @oldpage to @newpage.
*
* Both pages must be locked, @newpage->mapping must be set up.
+ * Either or both pages might be on the LRU already.
*/
void mem_cgroup_replace_page(struct page *oldpage, struct page *newpage)
{
@@ -5547,6 +5555,121 @@ void mem_cgroup_replace_page(struct page *oldpage, struct page *newpage)
commit_charge(newpage, memcg, true);
}
+#ifdef CONFIG_INET
+
+DEFINE_STATIC_KEY_FALSE(memcg_sockets_enabled_key);
+EXPORT_SYMBOL(memcg_sockets_enabled_key);
+
+void sock_update_memcg(struct sock *sk)
+{
+ struct mem_cgroup *memcg;
+
+ /* Socket cloning can throw us here with sk_cgrp already
+ * filled. It won't however, necessarily happen from
+ * process context. So the test for root memcg given
+ * the current task's memcg won't help us in this case.
+ *
+ * Respecting the original socket's memcg is a better
+ * decision in this case.
+ */
+ if (sk->sk_memcg) {
+ BUG_ON(mem_cgroup_is_root(sk->sk_memcg));
+ css_get(&sk->sk_memcg->css);
+ return;
+ }
+
+ rcu_read_lock();
+ memcg = mem_cgroup_from_task(current);
+ if (memcg == root_mem_cgroup)
+ goto out;
+#ifdef CONFIG_MEMCG_KMEM
+ if (!cgroup_subsys_on_dfl(memory_cgrp_subsys) && !memcg->tcp_mem.active)
+ goto out;
+#endif
+ if (css_tryget_online(&memcg->css))
+ sk->sk_memcg = memcg;
+out:
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL(sock_update_memcg);
+
+void sock_release_memcg(struct sock *sk)
+{
+ WARN_ON(!sk->sk_memcg);
+ css_put(&sk->sk_memcg->css);
+}
+
+/**
+ * mem_cgroup_charge_skmem - charge socket memory
+ * @memcg: memcg to charge
+ * @nr_pages: number of pages to charge
+ *
+ * Charges @nr_pages to @memcg. Returns %true if the charge fit within
+ * @memcg's configured limit, %false if the charge had to be forced.
+ */
+bool mem_cgroup_charge_skmem(struct mem_cgroup *memcg, unsigned int nr_pages)
+{
+ gfp_t gfp_mask = GFP_KERNEL;
+
+#ifdef CONFIG_MEMCG_KMEM
+ if (!cgroup_subsys_on_dfl(memory_cgrp_subsys)) {
+ struct page_counter *counter;
+
+ if (page_counter_try_charge(&memcg->tcp_mem.memory_allocated,
+ nr_pages, &counter)) {
+ memcg->tcp_mem.memory_pressure = 0;
+ return true;
+ }
+ page_counter_charge(&memcg->tcp_mem.memory_allocated, nr_pages);
+ memcg->tcp_mem.memory_pressure = 1;
+ return false;
+ }
+#endif
+ /* Don't block in the packet receive path */
+ if (in_softirq())
+ gfp_mask = GFP_NOWAIT;
+
+ if (try_charge(memcg, gfp_mask, nr_pages) == 0)
+ return true;
+
+ try_charge(memcg, gfp_mask|__GFP_NOFAIL, nr_pages);
+ return false;
+}
+
+/**
+ * mem_cgroup_uncharge_skmem - uncharge socket memory
+ * @memcg - memcg to uncharge
+ * @nr_pages - number of pages to uncharge
+ */
+void mem_cgroup_uncharge_skmem(struct mem_cgroup *memcg, unsigned int nr_pages)
+{
+#ifdef CONFIG_MEMCG_KMEM
+ if (!cgroup_subsys_on_dfl(memory_cgrp_subsys)) {
+ page_counter_uncharge(&memcg->tcp_mem.memory_allocated,
+ nr_pages);
+ return;
+ }
+#endif
+ page_counter_uncharge(&memcg->memory, nr_pages);
+ css_put_many(&memcg->css, nr_pages);
+}
+
+#endif /* CONFIG_INET */
+
+static int __init cgroup_memory(char *s)
+{
+ char *token;
+
+ while ((token = strsep(&s, ",")) != NULL) {
+ if (!*token)
+ continue;
+ if (!strcmp(token, "nosocket"))
+ cgroup_memory_nosocket = true;
+ }
+ return 0;
+}
+__setup("cgroup.memory=", cgroup_memory);
+
/*
* subsys_initcall() for memory controller.
*
@@ -5602,7 +5725,7 @@ void mem_cgroup_swapout(struct page *page, swp_entry_t entry)
VM_BUG_ON_PAGE(PageLRU(page), page);
VM_BUG_ON_PAGE(page_count(page), page);
- if (!do_swap_account)
+ if (!do_memsw_account())
return;
memcg = page->mem_cgroup;
@@ -5642,7 +5765,7 @@ void mem_cgroup_uncharge_swap(swp_entry_t entry)
struct mem_cgroup *memcg;
unsigned short id;
- if (!do_swap_account)
+ if (!do_memsw_account())
return;
id = swap_cgroup_record(entry, 0);
diff --git a/mm/memory.c b/mm/memory.c
index c387430f06c3..d4e4d37c1989 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -832,10 +832,7 @@ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
} else if (is_migration_entry(entry)) {
page = migration_entry_to_page(entry);
- if (PageAnon(page))
- rss[MM_ANONPAGES]++;
- else
- rss[MM_FILEPAGES]++;
+ rss[mm_counter(page)]++;
if (is_write_migration_entry(entry) &&
is_cow_mapping(vm_flags)) {
@@ -874,10 +871,7 @@ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
if (page) {
get_page(page);
page_dup_rmap(page);
- if (PageAnon(page))
- rss[MM_ANONPAGES]++;
- else
- rss[MM_FILEPAGES]++;
+ rss[mm_counter(page)]++;
}
out_set_pte:
@@ -1113,9 +1107,8 @@ again:
tlb_remove_tlb_entry(tlb, pte, addr);
if (unlikely(!page))
continue;
- if (PageAnon(page))
- rss[MM_ANONPAGES]--;
- else {
+
+ if (!PageAnon(page)) {
if (pte_dirty(ptent)) {
force_flush = 1;
set_page_dirty(page);
@@ -1123,8 +1116,8 @@ again:
if (pte_young(ptent) &&
likely(!(vma->vm_flags & VM_SEQ_READ)))
mark_page_accessed(page);
- rss[MM_FILEPAGES]--;
}
+ rss[mm_counter(page)]--;
page_remove_rmap(page);
if (unlikely(page_mapcount(page) < 0))
print_bad_pte(vma, addr, ptent, page);
@@ -1146,11 +1139,7 @@ again:
struct page *page;
page = migration_entry_to_page(entry);
-
- if (PageAnon(page))
- rss[MM_ANONPAGES]--;
- else
- rss[MM_FILEPAGES]--;
+ rss[mm_counter(page)]--;
}
if (unlikely(!free_swap_and_cache(entry)))
print_bad_pte(vma, addr, ptent, NULL);
@@ -1460,7 +1449,7 @@ static int insert_page(struct vm_area_struct *vma, unsigned long addr,
/* Ok, finally just insert the thing.. */
get_page(page);
- inc_mm_counter_fast(mm, MM_FILEPAGES);
+ inc_mm_counter_fast(mm, mm_counter_file(page));
page_add_file_rmap(page);
set_pte_at(mm, addr, pte, mk_pte(page, prot));
@@ -1949,6 +1938,20 @@ static inline void cow_user_page(struct page *dst, struct page *src, unsigned lo
copy_user_highpage(dst, src, va, vma);
}
+static gfp_t __get_fault_gfp_mask(struct vm_area_struct *vma)
+{
+ struct file *vm_file = vma->vm_file;
+
+ if (vm_file)
+ return mapping_gfp_mask(vm_file->f_mapping) | __GFP_FS | __GFP_IO;
+
+ /*
+ * Special mappings (e.g. VDSO) do not have any file so fake
+ * a default GFP_KERNEL for them.
+ */
+ return GFP_KERNEL;
+}
+
/*
* Notify the address space that the page is about to become writable so that
* it can prohibit this or wait for the page to get into an appropriate state.
@@ -1964,6 +1967,7 @@ static int do_page_mkwrite(struct vm_area_struct *vma, struct page *page,
vmf.virtual_address = (void __user *)(address & PAGE_MASK);
vmf.pgoff = page->index;
vmf.flags = FAULT_FLAG_WRITE|FAULT_FLAG_MKWRITE;
+ vmf.gfp_mask = __get_fault_gfp_mask(vma);
vmf.page = page;
vmf.cow_page = NULL;
@@ -2097,7 +2101,8 @@ static int wp_page_copy(struct mm_struct *mm, struct vm_area_struct *vma,
if (likely(pte_same(*page_table, orig_pte))) {
if (old_page) {
if (!PageAnon(old_page)) {
- dec_mm_counter_fast(mm, MM_FILEPAGES);
+ dec_mm_counter_fast(mm,
+ mm_counter_file(old_page));
inc_mm_counter_fast(mm, MM_ANONPAGES);
}
} else {
@@ -2767,6 +2772,7 @@ static int __do_fault(struct vm_area_struct *vma, unsigned long address,
vmf.pgoff = pgoff;
vmf.flags = flags;
vmf.page = NULL;
+ vmf.gfp_mask = __get_fault_gfp_mask(vma);
vmf.cow_page = cow_page;
ret = vma->vm_ops->fault(vma, &vmf);
@@ -2820,7 +2826,7 @@ void do_set_pte(struct vm_area_struct *vma, unsigned long address,
inc_mm_counter_fast(vma->vm_mm, MM_ANONPAGES);
page_add_new_anon_rmap(page, vma, address);
} else {
- inc_mm_counter_fast(vma->vm_mm, MM_FILEPAGES);
+ inc_mm_counter_fast(vma->vm_mm, mm_counter_file(page));
page_add_file_rmap(page);
}
set_pte_at(vma->vm_mm, address, pte, entry);
@@ -2933,6 +2939,7 @@ static void do_fault_around(struct vm_area_struct *vma, unsigned long address,
vmf.pgoff = pgoff;
vmf.max_pgoff = max_pgoff;
vmf.flags = flags;
+ vmf.gfp_mask = __get_fault_gfp_mask(vma);
vma->vm_ops->map_pages(vma, &vmf);
}
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 67d488ab495e..92f95952692b 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -131,7 +131,8 @@ static struct resource *register_memory_resource(u64 start, u64 size)
{
struct resource *res;
res = kzalloc(sizeof(struct resource), GFP_KERNEL);
- BUG_ON(!res);
+ if (!res)
+ return ERR_PTR(-ENOMEM);
res->name = "System RAM";
res->start = start;
@@ -140,7 +141,7 @@ static struct resource *register_memory_resource(u64 start, u64 size)
if (request_resource(&iomem_resource, res) < 0) {
pr_debug("System RAM resource %pR cannot be added\n", res);
kfree(res);
- res = NULL;
+ return ERR_PTR(-EEXIST);
}
return res;
}
@@ -1312,8 +1313,8 @@ int __ref add_memory(int nid, u64 start, u64 size)
int ret;
res = register_memory_resource(start, size);
- if (!res)
- return -EEXIST;
+ if (IS_ERR(res))
+ return PTR_ERR(res);
ret = add_memory_resource(nid, res);
if (ret < 0)
@@ -1375,23 +1376,30 @@ int is_mem_section_removable(unsigned long start_pfn, unsigned long nr_pages)
*/
int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn)
{
- unsigned long pfn;
+ unsigned long pfn, sec_end_pfn;
struct zone *zone = NULL;
struct page *page;
int i;
- for (pfn = start_pfn;
+ for (pfn = start_pfn, sec_end_pfn = SECTION_ALIGN_UP(start_pfn);
pfn < end_pfn;
- pfn += MAX_ORDER_NR_PAGES) {
- i = 0;
- /* This is just a CONFIG_HOLES_IN_ZONE check.*/
- while ((i < MAX_ORDER_NR_PAGES) && !pfn_valid_within(pfn + i))
- i++;
- if (i == MAX_ORDER_NR_PAGES)
+ pfn = sec_end_pfn + 1, sec_end_pfn += PAGES_PER_SECTION) {
+ /* Make sure the memory section is present first */
+ if (!present_section_nr(pfn_to_section_nr(pfn)))
continue;
- page = pfn_to_page(pfn + i);
- if (zone && page_zone(page) != zone)
- return 0;
- zone = page_zone(page);
+ for (; pfn < sec_end_pfn && pfn < end_pfn;
+ pfn += MAX_ORDER_NR_PAGES) {
+ i = 0;
+ /* This is just a CONFIG_HOLES_IN_ZONE check.*/
+ while ((i < MAX_ORDER_NR_PAGES) &&
+ !pfn_valid_within(pfn + i))
+ i++;
+ if (i == MAX_ORDER_NR_PAGES)
+ continue;
+ page = pfn_to_page(pfn + i);
+ if (zone && page_zone(page) != zone)
+ return 0;
+ zone = page_zone(page);
+ }
}
return 1;
}
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 87a177917cb2..d8caff071a30 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -2142,12 +2142,14 @@ bool __mpol_equal(struct mempolicy *a, struct mempolicy *b)
*
* Remember policies even when nobody has shared memory mapped.
* The policies are kept in Red-Black tree linked from the inode.
- * They are protected by the sp->lock spinlock, which should be held
+ * They are protected by the sp->lock rwlock, which should be held
* for any accesses to the tree.
*/
-/* lookup first element intersecting start-end */
-/* Caller holds sp->lock */
+/*
+ * lookup first element intersecting start-end. Caller holds sp->lock for
+ * reading or for writing
+ */
static struct sp_node *
sp_lookup(struct shared_policy *sp, unsigned long start, unsigned long end)
{
@@ -2178,8 +2180,10 @@ sp_lookup(struct shared_policy *sp, unsigned long start, unsigned long end)
return rb_entry(n, struct sp_node, nd);
}
-/* Insert a new shared policy into the list. */
-/* Caller holds sp->lock */
+/*
+ * Insert a new shared policy into the list. Caller holds sp->lock for
+ * writing.
+ */
static void sp_insert(struct shared_policy *sp, struct sp_node *new)
{
struct rb_node **p = &sp->root.rb_node;
@@ -2211,13 +2215,13 @@ mpol_shared_policy_lookup(struct shared_policy *sp, unsigned long idx)
if (!sp->root.rb_node)
return NULL;
- spin_lock(&sp->lock);
+ read_lock(&sp->lock);
sn = sp_lookup(sp, idx, idx+1);
if (sn) {
mpol_get(sn->policy);
pol = sn->policy;
}
- spin_unlock(&sp->lock);
+ read_unlock(&sp->lock);
return pol;
}
@@ -2360,7 +2364,7 @@ static int shared_policy_replace(struct shared_policy *sp, unsigned long start,
int ret = 0;
restart:
- spin_lock(&sp->lock);
+ write_lock(&sp->lock);
n = sp_lookup(sp, start, end);
/* Take care of old policies in the same range. */
while (n && n->start < end) {
@@ -2393,7 +2397,7 @@ restart:
}
if (new)
sp_insert(sp, new);
- spin_unlock(&sp->lock);
+ write_unlock(&sp->lock);
ret = 0;
err_out:
@@ -2405,7 +2409,7 @@ err_out:
return ret;
alloc_new:
- spin_unlock(&sp->lock);
+ write_unlock(&sp->lock);
ret = -ENOMEM;
n_new = kmem_cache_alloc(sn_cache, GFP_KERNEL);
if (!n_new)
@@ -2431,7 +2435,7 @@ void mpol_shared_policy_init(struct shared_policy *sp, struct mempolicy *mpol)
int ret;
sp->root = RB_ROOT; /* empty tree == default mempolicy */
- spin_lock_init(&sp->lock);
+ rwlock_init(&sp->lock);
if (mpol) {
struct vm_area_struct pvma;
@@ -2497,14 +2501,14 @@ void mpol_free_shared_policy(struct shared_policy *p)
if (!p->root.rb_node)
return;
- spin_lock(&p->lock);
+ write_lock(&p->lock);
next = rb_first(&p->root);
while (next) {
n = rb_entry(next, struct sp_node, nd);
next = rb_next(&n->nd);
sp_delete(p, n);
}
- spin_unlock(&p->lock);
+ write_unlock(&p->lock);
}
#ifdef CONFIG_NUMA_BALANCING
diff --git a/mm/mlock.c b/mm/mlock.c
index 339d9e0949b6..9cb87cbc4071 100644
--- a/mm/mlock.c
+++ b/mm/mlock.c
@@ -425,7 +425,7 @@ void munlock_vma_pages_range(struct vm_area_struct *vma,
vma->vm_flags &= VM_LOCKED_CLEAR_MASK;
while (start < end) {
- struct page *page = NULL;
+ struct page *page;
unsigned int page_mask;
unsigned long page_increm;
struct pagevec pvec;
diff --git a/mm/mmap.c b/mm/mmap.c
index 2ce04a649f6b..b3f00b616b81 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -58,6 +58,18 @@
#define arch_rebalance_pgtables(addr, len) (addr)
#endif
+#ifdef CONFIG_HAVE_ARCH_MMAP_RND_BITS
+const int mmap_rnd_bits_min = CONFIG_ARCH_MMAP_RND_BITS_MIN;
+const int mmap_rnd_bits_max = CONFIG_ARCH_MMAP_RND_BITS_MAX;
+int mmap_rnd_bits __read_mostly = CONFIG_ARCH_MMAP_RND_BITS;
+#endif
+#ifdef CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS
+const int mmap_rnd_compat_bits_min = CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN;
+const int mmap_rnd_compat_bits_max = CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX;
+int mmap_rnd_compat_bits __read_mostly = CONFIG_ARCH_MMAP_RND_COMPAT_BITS;
+#endif
+
+
static void unmap_region(struct mm_struct *mm,
struct vm_area_struct *vma, struct vm_area_struct *prev,
unsigned long start, unsigned long end);
@@ -1208,24 +1220,6 @@ none:
return NULL;
}
-#ifdef CONFIG_PROC_FS
-void vm_stat_account(struct mm_struct *mm, unsigned long flags,
- struct file *file, long pages)
-{
- const unsigned long stack_flags
- = VM_STACK_FLAGS & (VM_GROWSUP|VM_GROWSDOWN);
-
- mm->total_vm += pages;
-
- if (file) {
- mm->shared_vm += pages;
- if ((flags & (VM_EXEC|VM_WRITE)) == VM_EXEC)
- mm->exec_vm += pages;
- } else if (flags & stack_flags)
- mm->stack_vm += pages;
-}
-#endif /* CONFIG_PROC_FS */
-
/*
* If a hint addr is less than mmap_min_addr change hint to be as
* low as possible but still greater than mmap_min_addr
@@ -1544,19 +1538,17 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
unsigned long charged = 0;
/* Check against address space limit. */
- if (!may_expand_vm(mm, len >> PAGE_SHIFT)) {
+ if (!may_expand_vm(mm, vm_flags, len >> PAGE_SHIFT)) {
unsigned long nr_pages;
/*
* MAP_FIXED may remove pages of mappings that intersects with
* requested mapping. Account for the pages it would unmap.
*/
- if (!(vm_flags & MAP_FIXED))
- return -ENOMEM;
-
nr_pages = count_vma_pages_range(mm, addr, addr + len);
- if (!may_expand_vm(mm, (len >> PAGE_SHIFT) - nr_pages))
+ if (!may_expand_vm(mm, vm_flags,
+ (len >> PAGE_SHIFT) - nr_pages))
return -ENOMEM;
}
@@ -1655,7 +1647,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
out:
perf_event_mmap(vma);
- vm_stat_account(mm, vm_flags, file, len >> PAGE_SHIFT);
+ vm_stat_account(mm, vm_flags, len >> PAGE_SHIFT);
if (vm_flags & VM_LOCKED) {
if (!((vm_flags & VM_SPECIAL) || is_vm_hugetlb_page(vma) ||
vma == get_gate_vma(current->mm)))
@@ -2102,7 +2094,7 @@ static int acct_stack_growth(struct vm_area_struct *vma, unsigned long size, uns
unsigned long new_start, actual_size;
/* address space limit tests */
- if (!may_expand_vm(mm, grow))
+ if (!may_expand_vm(mm, vma->vm_flags, grow))
return -ENOMEM;
/* Stack limit test */
@@ -2199,8 +2191,7 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address)
spin_lock(&mm->page_table_lock);
if (vma->vm_flags & VM_LOCKED)
mm->locked_vm += grow;
- vm_stat_account(mm, vma->vm_flags,
- vma->vm_file, grow);
+ vm_stat_account(mm, vma->vm_flags, grow);
anon_vma_interval_tree_pre_update_vma(vma);
vma->vm_end = address;
anon_vma_interval_tree_post_update_vma(vma);
@@ -2275,8 +2266,7 @@ int expand_downwards(struct vm_area_struct *vma,
spin_lock(&mm->page_table_lock);
if (vma->vm_flags & VM_LOCKED)
mm->locked_vm += grow;
- vm_stat_account(mm, vma->vm_flags,
- vma->vm_file, grow);
+ vm_stat_account(mm, vma->vm_flags, grow);
anon_vma_interval_tree_pre_update_vma(vma);
vma->vm_start = address;
vma->vm_pgoff -= grow;
@@ -2390,7 +2380,7 @@ static void remove_vma_list(struct mm_struct *mm, struct vm_area_struct *vma)
if (vma->vm_flags & VM_ACCOUNT)
nr_accounted += nrpages;
- vm_stat_account(mm, vma->vm_flags, vma->vm_file, -nrpages);
+ vm_stat_account(mm, vma->vm_flags, -nrpages);
vma = remove_vma(vma);
} while (vma);
vm_unacct_memory(nr_accounted);
@@ -2760,7 +2750,7 @@ static unsigned long do_brk(unsigned long addr, unsigned long len)
}
/* Check against address space limits *after* clearing old maps... */
- if (!may_expand_vm(mm, len >> PAGE_SHIFT))
+ if (!may_expand_vm(mm, flags, len >> PAGE_SHIFT))
return -ENOMEM;
if (mm->map_count > sysctl_max_map_count)
@@ -2795,6 +2785,7 @@ static unsigned long do_brk(unsigned long addr, unsigned long len)
out:
perf_event_mmap(vma);
mm->total_vm += len >> PAGE_SHIFT;
+ mm->data_vm += len >> PAGE_SHIFT;
if (flags & VM_LOCKED)
mm->locked_vm += (len >> PAGE_SHIFT);
vma->vm_flags |= VM_SOFTDIRTY;
@@ -2986,16 +2977,28 @@ out:
* Return true if the calling process may expand its vm space by the passed
* number of pages
*/
-int may_expand_vm(struct mm_struct *mm, unsigned long npages)
+bool may_expand_vm(struct mm_struct *mm, vm_flags_t flags, unsigned long npages)
{
- unsigned long cur = mm->total_vm; /* pages */
- unsigned long lim;
+ if (mm->total_vm + npages > rlimit(RLIMIT_AS) >> PAGE_SHIFT)
+ return false;
- lim = rlimit(RLIMIT_AS) >> PAGE_SHIFT;
+ if ((flags & (VM_WRITE | VM_SHARED | (VM_STACK_FLAGS &
+ (VM_GROWSUP | VM_GROWSDOWN)))) == VM_WRITE)
+ return mm->data_vm + npages <= rlimit(RLIMIT_DATA);
- if (cur + npages > lim)
- return 0;
- return 1;
+ return true;
+}
+
+void vm_stat_account(struct mm_struct *mm, vm_flags_t flags, long npages)
+{
+ mm->total_vm += npages;
+
+ if ((flags & (VM_EXEC | VM_WRITE)) == VM_EXEC)
+ mm->exec_vm += npages;
+ else if (flags & (VM_STACK_FLAGS & (VM_GROWSUP | VM_GROWSDOWN)))
+ mm->stack_vm += npages;
+ else if ((flags & (VM_WRITE | VM_SHARED)) == VM_WRITE)
+ mm->data_vm += npages;
}
static int special_mapping_fault(struct vm_area_struct *vma,
@@ -3077,7 +3080,7 @@ static struct vm_area_struct *__install_special_mapping(
if (ret)
goto out;
- mm->total_vm += len >> PAGE_SHIFT;
+ vm_stat_account(mm, vma->vm_flags, len >> PAGE_SHIFT);
perf_event_mmap(vma);
diff --git a/mm/mmzone.c b/mm/mmzone.c
index 7d87ebb0d632..52687fb4de6f 100644
--- a/mm/mmzone.c
+++ b/mm/mmzone.c
@@ -72,16 +72,16 @@ struct zoneref *next_zones_zonelist(struct zoneref *z,
}
#ifdef CONFIG_ARCH_HAS_HOLES_MEMORYMODEL
-int memmap_valid_within(unsigned long pfn,
+bool memmap_valid_within(unsigned long pfn,
struct page *page, struct zone *zone)
{
if (page_to_pfn(page) != pfn)
- return 0;
+ return false;
if (page_zone(page) != zone)
- return 0;
+ return false;
- return 1;
+ return true;
}
#endif /* CONFIG_ARCH_HAS_HOLES_MEMORYMODEL */
diff --git a/mm/mprotect.c b/mm/mprotect.c
index ef5be8eaab00..c764402c464f 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -278,6 +278,10 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev,
* even if read-only so there is no need to account for them here
*/
if (newflags & VM_WRITE) {
+ /* Check space limits when area turns into data. */
+ if (!may_expand_vm(mm, newflags, nrpages) &&
+ may_expand_vm(mm, oldflags, nrpages))
+ return -ENOMEM;
if (!(oldflags & (VM_ACCOUNT|VM_WRITE|VM_HUGETLB|
VM_SHARED|VM_NORESERVE))) {
charged = nrpages;
@@ -334,8 +338,8 @@ success:
populate_vma_page_range(vma, start, end, NULL);
}
- vm_stat_account(mm, oldflags, vma->vm_file, -nrpages);
- vm_stat_account(mm, newflags, vma->vm_file, nrpages);
+ vm_stat_account(mm, oldflags, -nrpages);
+ vm_stat_account(mm, newflags, nrpages);
perf_event_mmap(vma);
return 0;
diff --git a/mm/mremap.c b/mm/mremap.c
index c25bc6268e46..e55b157865d5 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -317,7 +317,11 @@ static unsigned long move_vma(struct vm_area_struct *vma,
* If this were a serious issue, we'd add a flag to do_munmap().
*/
hiwater_vm = mm->hiwater_vm;
- vm_stat_account(mm, vma->vm_flags, vma->vm_file, new_len>>PAGE_SHIFT);
+ vm_stat_account(mm, vma->vm_flags, new_len >> PAGE_SHIFT);
+
+ /* Tell pfnmap has moved from this vma */
+ if (unlikely(vma->vm_flags & VM_PFNMAP))
+ untrack_pfn_moved(vma);
if (do_munmap(mm, old_addr, old_len) < 0) {
/* OOM: unable to split vma, just get accounts right */
@@ -379,7 +383,8 @@ static struct vm_area_struct *vma_to_resize(unsigned long addr,
return ERR_PTR(-EAGAIN);
}
- if (!may_expand_vm(mm, (new_len - old_len) >> PAGE_SHIFT))
+ if (!may_expand_vm(mm, vma->vm_flags,
+ (new_len - old_len) >> PAGE_SHIFT))
return ERR_PTR(-ENOMEM);
if (vma->vm_flags & VM_ACCOUNT) {
@@ -541,7 +546,7 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
goto out;
}
- vm_stat_account(mm, vma->vm_flags, vma->vm_file, pages);
+ vm_stat_account(mm, vma->vm_flags, pages);
if (vma->vm_flags & VM_LOCKED) {
mm->locked_vm += pages;
locked = true;
diff --git a/mm/nobootmem.c b/mm/nobootmem.c
index e57cf24babd6..99feb2b07fc5 100644
--- a/mm/nobootmem.c
+++ b/mm/nobootmem.c
@@ -31,6 +31,7 @@ EXPORT_SYMBOL(contig_page_data);
unsigned long max_low_pfn;
unsigned long min_low_pfn;
unsigned long max_pfn;
+unsigned long long max_possible_pfn;
static void * __init __alloc_memory_core_early(int nid, u64 size, u64 align,
u64 goal, u64 limit)
diff --git a/mm/nommu.c b/mm/nommu.c
index 92be862c859b..fbf6f0f1d6c9 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -560,7 +560,7 @@ void __init mmap_init(void)
ret = percpu_counter_init(&vm_committed_as, 0, GFP_KERNEL);
VM_BUG_ON(ret);
- vm_region_jar = KMEM_CACHE(vm_region, SLAB_PANIC);
+ vm_region_jar = KMEM_CACHE(vm_region, SLAB_PANIC|SLAB_ACCOUNT);
}
/*
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index d13a33918fa2..dc490c06941b 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -585,10 +585,11 @@ void oom_kill_process(struct oom_control *oc, struct task_struct *p,
*/
do_send_sig_info(SIGKILL, SEND_SIG_FORCED, victim, true);
mark_oom_victim(victim);
- pr_err("Killed process %d (%s) total-vm:%lukB, anon-rss:%lukB, file-rss:%lukB\n",
+ pr_err("Killed process %d (%s) total-vm:%lukB, anon-rss:%lukB, file-rss:%lukB, shmem-rss:%lukB\n",
task_pid_nr(victim), victim->comm, K(victim->mm->total_vm),
K(get_mm_counter(victim->mm, MM_ANONPAGES)),
- K(get_mm_counter(victim->mm, MM_FILEPAGES)));
+ K(get_mm_counter(victim->mm, MM_FILEPAGES)),
+ K(get_mm_counter(victim->mm, MM_SHMEMPAGES)));
task_unlock(victim);
/*
@@ -608,6 +609,8 @@ void oom_kill_process(struct oom_control *oc, struct task_struct *p,
continue;
if (unlikely(p->flags & PF_KTHREAD))
continue;
+ if (is_global_init(p))
+ continue;
if (p->signal->oom_score_adj == OOM_SCORE_ADJ_MIN)
continue;
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index 3e4d65445fa7..6fe7d15bd1f7 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -2,7 +2,7 @@
* mm/page-writeback.c
*
* Copyright (C) 2002, Linus Torvalds.
- * Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ * Copyright (C) 2007 Red Hat, Inc., Peter Zijlstra
*
* Contains functions related to writing back dirty pages at the
* address_space level.
@@ -278,7 +278,12 @@ static unsigned long zone_dirtyable_memory(struct zone *zone)
unsigned long nr_pages;
nr_pages = zone_page_state(zone, NR_FREE_PAGES);
- nr_pages -= min(nr_pages, zone->dirty_balance_reserve);
+ /*
+ * Pages reserved for the kernel should not be considered
+ * dirtyable, to prevent a situation where reclaim has to
+ * clean pages in order to balance the zones.
+ */
+ nr_pages -= min(nr_pages, zone->totalreserve_pages);
nr_pages += zone_page_state(zone, NR_INACTIVE_FILE);
nr_pages += zone_page_state(zone, NR_ACTIVE_FILE);
@@ -332,7 +337,12 @@ static unsigned long global_dirtyable_memory(void)
unsigned long x;
x = global_page_state(NR_FREE_PAGES);
- x -= min(x, dirty_balance_reserve);
+ /*
+ * Pages reserved for the kernel should not be considered
+ * dirtyable, to prevent a situation where reclaim has to
+ * clean pages in order to balance the zones.
+ */
+ x -= min(x, totalreserve_pages);
x += global_page_state(NR_INACTIVE_FILE);
x += global_page_state(NR_ACTIVE_FILE);
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 17a3c66639a9..ce63d603820f 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -114,13 +114,6 @@ static DEFINE_SPINLOCK(managed_page_count_lock);
unsigned long totalram_pages __read_mostly;
unsigned long totalreserve_pages __read_mostly;
unsigned long totalcma_pages __read_mostly;
-/*
- * When calculating the number of globally allowed dirty pages, there
- * is a certain number of per-zone reserves that should not be
- * considered dirtyable memory. This is the sum of those reserves
- * over all existing zones that contribute dirtyable memory.
- */
-unsigned long dirty_balance_reserve __read_mostly;
int percpu_pagelist_fraction;
gfp_t gfp_allowed_mask __read_mostly = GFP_BOOT_MASK;
@@ -812,7 +805,7 @@ static void free_pcppages_bulk(struct zone *zone, int count,
do {
int mt; /* migratetype of the to-be-freed page */
- page = list_entry(list->prev, struct page, lru);
+ page = list_last_entry(list, struct page, lru);
/* must delete as __free_one_page list manipulates */
list_del(&page->lru);
@@ -1417,11 +1410,10 @@ struct page *__rmqueue_smallest(struct zone *zone, unsigned int order,
/* Find a page of the appropriate size in the preferred list */
for (current_order = order; current_order < MAX_ORDER; ++current_order) {
area = &(zone->free_area[current_order]);
- if (list_empty(&area->free_list[migratetype]))
- continue;
-
- page = list_entry(area->free_list[migratetype].next,
+ page = list_first_entry_or_null(&area->free_list[migratetype],
struct page, lru);
+ if (!page)
+ continue;
list_del(&page->lru);
rmv_page_order(page);
area->nr_free--;
@@ -1700,12 +1692,12 @@ static void unreserve_highatomic_pageblock(const struct alloc_context *ac)
for (order = 0; order < MAX_ORDER; order++) {
struct free_area *area = &(zone->free_area[order]);
- if (list_empty(&area->free_list[MIGRATE_HIGHATOMIC]))
+ page = list_first_entry_or_null(
+ &area->free_list[MIGRATE_HIGHATOMIC],
+ struct page, lru);
+ if (!page)
continue;
- page = list_entry(area->free_list[MIGRATE_HIGHATOMIC].next,
- struct page, lru);
-
/*
* It should never happen but changes to locking could
* inadvertently allow a per-cpu drain to add pages
@@ -1753,7 +1745,7 @@ __rmqueue_fallback(struct zone *zone, unsigned int order, int start_migratetype)
if (fallback_mt == -1)
continue;
- page = list_entry(area->free_list[fallback_mt].next,
+ page = list_first_entry(&area->free_list[fallback_mt],
struct page, lru);
if (can_steal)
steal_suitable_fallback(zone, page, start_migratetype);
@@ -1788,7 +1780,7 @@ __rmqueue_fallback(struct zone *zone, unsigned int order, int start_migratetype)
* Call me with the zone->lock already held.
*/
static struct page *__rmqueue(struct zone *zone, unsigned int order,
- int migratetype, gfp_t gfp_flags)
+ int migratetype)
{
struct page *page;
@@ -1818,7 +1810,7 @@ static int rmqueue_bulk(struct zone *zone, unsigned int order,
spin_lock(&zone->lock);
for (i = 0; i < count; ++i) {
- struct page *page = __rmqueue(zone, order, migratetype, 0);
+ struct page *page = __rmqueue(zone, order, migratetype);
if (unlikely(page == NULL))
break;
@@ -1988,7 +1980,7 @@ void mark_free_pages(struct zone *zone)
unsigned long pfn, max_zone_pfn;
unsigned long flags;
unsigned int order, t;
- struct list_head *curr;
+ struct page *page;
if (zone_is_empty(zone))
return;
@@ -1998,17 +1990,17 @@ void mark_free_pages(struct zone *zone)
max_zone_pfn = zone_end_pfn(zone);
for (pfn = zone->zone_start_pfn; pfn < max_zone_pfn; pfn++)
if (pfn_valid(pfn)) {
- struct page *page = pfn_to_page(pfn);
-
+ page = pfn_to_page(pfn);
if (!swsusp_page_is_forbidden(page))
swsusp_unset_page_free(page);
}
for_each_migratetype_order(order, t) {
- list_for_each(curr, &zone->free_area[order].free_list[t]) {
+ list_for_each_entry(page,
+ &zone->free_area[order].free_list[t], lru) {
unsigned long i;
- pfn = page_to_pfn(list_entry(curr, struct page, lru));
+ pfn = page_to_pfn(page);
for (i = 0; i < (1UL << order); i++)
swsusp_set_page_free(pfn_to_page(pfn + i));
}
@@ -2212,9 +2204,9 @@ struct page *buffered_rmqueue(struct zone *preferred_zone,
}
if (cold)
- page = list_entry(list->prev, struct page, lru);
+ page = list_last_entry(list, struct page, lru);
else
- page = list_entry(list->next, struct page, lru);
+ page = list_first_entry(list, struct page, lru);
list_del(&page->lru);
pcp->count--;
@@ -2241,7 +2233,7 @@ struct page *buffered_rmqueue(struct zone *preferred_zone,
trace_mm_page_alloc_zone_locked(page, order, migratetype);
}
if (!page)
- page = __rmqueue(zone, order, migratetype, gfp_flags);
+ page = __rmqueue(zone, order, migratetype);
spin_unlock(&zone->lock);
if (!page)
goto failed;
@@ -2740,8 +2732,21 @@ __alloc_pages_may_oom(gfp_t gfp_mask, unsigned int order,
goto out;
}
/* Exhausted what can be done so it's blamo time */
- if (out_of_memory(&oc) || WARN_ON_ONCE(gfp_mask & __GFP_NOFAIL))
+ if (out_of_memory(&oc) || WARN_ON_ONCE(gfp_mask & __GFP_NOFAIL)) {
*did_some_progress = 1;
+
+ if (gfp_mask & __GFP_NOFAIL) {
+ page = get_page_from_freelist(gfp_mask, order,
+ ALLOC_NO_WATERMARKS|ALLOC_CPUSET, ac);
+ /*
+ * fallback to ignore cpuset restriction if our nodes
+ * are depleted
+ */
+ if (!page)
+ page = get_page_from_freelist(gfp_mask, order,
+ ALLOC_NO_WATERMARKS, ac);
+ }
+ }
out:
mutex_unlock(&oom_lock);
return page;
@@ -2876,28 +2881,6 @@ retry:
return page;
}
-/*
- * This is called in the allocator slow-path if the allocation request is of
- * sufficient urgency to ignore watermarks and take other desperate measures
- */
-static inline struct page *
-__alloc_pages_high_priority(gfp_t gfp_mask, unsigned int order,
- const struct alloc_context *ac)
-{
- struct page *page;
-
- do {
- page = get_page_from_freelist(gfp_mask, order,
- ALLOC_NO_WATERMARKS, ac);
-
- if (!page && gfp_mask & __GFP_NOFAIL)
- wait_iff_congested(ac->preferred_zone, BLK_RW_ASYNC,
- HZ/50);
- } while (!page && (gfp_mask & __GFP_NOFAIL));
-
- return page;
-}
-
static void wake_all_kswapds(unsigned int order, const struct alloc_context *ac)
{
struct zoneref *z;
@@ -3042,28 +3025,36 @@ retry:
* allocations are system rather than user orientated
*/
ac->zonelist = node_zonelist(numa_node_id(), gfp_mask);
-
- page = __alloc_pages_high_priority(gfp_mask, order, ac);
-
- if (page) {
+ page = get_page_from_freelist(gfp_mask, order,
+ ALLOC_NO_WATERMARKS, ac);
+ if (page)
goto got_pg;
- }
}
/* Caller is not willing to reclaim, we can't balance anything */
if (!can_direct_reclaim) {
/*
- * All existing users of the deprecated __GFP_NOFAIL are
- * blockable, so warn of any new users that actually allow this
- * type of allocation to fail.
+ * All existing users of the __GFP_NOFAIL are blockable, so warn
+ * of any new users that actually allow this type of allocation
+ * to fail.
*/
WARN_ON_ONCE(gfp_mask & __GFP_NOFAIL);
goto nopage;
}
/* Avoid recursion of direct reclaim */
- if (current->flags & PF_MEMALLOC)
+ if (current->flags & PF_MEMALLOC) {
+ /*
+ * __GFP_NOFAIL request from this context is rather bizarre
+ * because we cannot reclaim anything and only can loop waiting
+ * for somebody to do a work for us.
+ */
+ if (WARN_ON_ONCE(gfp_mask & __GFP_NOFAIL)) {
+ cond_resched();
+ goto retry;
+ }
goto nopage;
+ }
/* Avoid allocations with no watermarks from looping endlessly */
if (test_thread_flag(TIF_MEMDIE) && !(gfp_mask & __GFP_NOFAIL))
@@ -3402,7 +3393,8 @@ EXPORT_SYMBOL(__free_page_frag);
/*
* alloc_kmem_pages charges newly allocated pages to the kmem resource counter
- * of the current memory cgroup.
+ * of the current memory cgroup if __GFP_ACCOUNT is set, other than that it is
+ * equivalent to alloc_pages.
*
* It should be used when the caller would like to use kmalloc, but since the
* allocation is large, it has to fall back to the page allocator.
@@ -3647,8 +3639,9 @@ static void show_migration_types(unsigned char type)
{
static const char types[MIGRATE_TYPES] = {
[MIGRATE_UNMOVABLE] = 'U',
- [MIGRATE_RECLAIMABLE] = 'E',
[MIGRATE_MOVABLE] = 'M',
+ [MIGRATE_RECLAIMABLE] = 'E',
+ [MIGRATE_HIGHATOMIC] = 'H',
#ifdef CONFIG_CMA
[MIGRATE_CMA] = 'C',
#endif
@@ -4146,8 +4139,7 @@ static void set_zonelist_order(void)
static void build_zonelists(pg_data_t *pgdat)
{
- int j, node, load;
- enum zone_type i;
+ int i, node, load;
nodemask_t used_mask;
int local_node, prev_node;
struct zonelist *zonelist;
@@ -4167,7 +4159,7 @@ static void build_zonelists(pg_data_t *pgdat)
nodes_clear(used_mask);
memset(node_order, 0, sizeof(node_order));
- j = 0;
+ i = 0;
while ((node = find_next_best_node(local_node, &used_mask)) >= 0) {
/*
@@ -4184,12 +4176,12 @@ static void build_zonelists(pg_data_t *pgdat)
if (order == ZONELIST_ORDER_NODE)
build_zonelists_in_node_order(pgdat, node);
else
- node_order[j++] = node; /* remember order */
+ node_order[i++] = node; /* remember order */
}
if (order == ZONELIST_ORDER_ZONE) {
/* calculate node order -- i.e., DMA last! */
- build_zonelists_in_zone_order(pgdat, j);
+ build_zonelists_in_zone_order(pgdat, i);
}
build_thisnode_zonelists(pgdat);
@@ -5955,20 +5947,12 @@ static void calculate_totalreserve_pages(void)
if (max > zone->managed_pages)
max = zone->managed_pages;
+
+ zone->totalreserve_pages = max;
+
reserve_pages += max;
- /*
- * Lowmem reserves are not available to
- * GFP_HIGHUSER page cache allocations and
- * kswapd tries to balance zones to their high
- * watermark. As a result, neither should be
- * regarded as dirtyable memory, to prevent a
- * situation where reclaim has to clean pages
- * in order to balance the zones.
- */
- zone->dirty_balance_reserve = max;
}
}
- dirty_balance_reserve = reserve_pages;
totalreserve_pages = reserve_pages;
}
@@ -6723,8 +6707,12 @@ int alloc_contig_range(unsigned long start, unsigned long end,
if (ret)
return ret;
+ /*
+ * In case of -EBUSY, we'd like to know which page causes problem.
+ * So, just fall through. We will check it in test_pages_isolated().
+ */
ret = __alloc_contig_migrate_range(&cc, start, end);
- if (ret)
+ if (ret && ret != -EBUSY)
goto done;
/*
@@ -6751,12 +6739,25 @@ int alloc_contig_range(unsigned long start, unsigned long end,
outer_start = start;
while (!PageBuddy(pfn_to_page(outer_start))) {
if (++order >= MAX_ORDER) {
- ret = -EBUSY;
- goto done;
+ outer_start = start;
+ break;
}
outer_start &= ~0UL << order;
}
+ if (outer_start != start) {
+ order = page_order(pfn_to_page(outer_start));
+
+ /*
+ * outer_start page could be small order buddy page and
+ * it doesn't include start page. Adjust outer_start
+ * in this case to report failed page properly
+ * on tracepoint in test_pages_isolated()
+ */
+ if (outer_start + (1UL << order) <= start)
+ outer_start = start;
+ }
+
/* Make sure the range is really isolated. */
if (test_pages_isolated(outer_start, end, false)) {
pr_info("%s: [%lx, %lx) PFNs busy\n",
diff --git a/mm/page_isolation.c b/mm/page_isolation.c
index 4568fd58f70a..5e139fec6c6c 100644
--- a/mm/page_isolation.c
+++ b/mm/page_isolation.c
@@ -9,6 +9,9 @@
#include <linux/hugetlb.h>
#include "internal.h"
+#define CREATE_TRACE_POINTS
+#include <trace/events/page_isolation.h>
+
static int set_migratetype_isolate(struct page *page,
bool skip_hwpoisoned_pages)
{
@@ -162,8 +165,8 @@ int start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
unsigned long undo_pfn;
struct page *page;
- BUG_ON((start_pfn) & (pageblock_nr_pages - 1));
- BUG_ON((end_pfn) & (pageblock_nr_pages - 1));
+ BUG_ON(!IS_ALIGNED(start_pfn, pageblock_nr_pages));
+ BUG_ON(!IS_ALIGNED(end_pfn, pageblock_nr_pages));
for (pfn = start_pfn;
pfn < end_pfn;
@@ -212,7 +215,7 @@ int undo_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
*
* Returns 1 if all pages in the range are isolated.
*/
-static int
+static unsigned long
__test_page_isolated_in_pageblock(unsigned long pfn, unsigned long end_pfn,
bool skip_hwpoisoned_pages)
{
@@ -237,9 +240,8 @@ __test_page_isolated_in_pageblock(unsigned long pfn, unsigned long end_pfn,
else
break;
}
- if (pfn < end_pfn)
- return 0;
- return 1;
+
+ return pfn;
}
int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn,
@@ -248,7 +250,6 @@ int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn,
unsigned long pfn, flags;
struct page *page;
struct zone *zone;
- int ret;
/*
* Note: pageblock_nr_pages != MAX_ORDER. Then, chunks of free pages
@@ -266,10 +267,13 @@ int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn,
/* Check all pages are free or marked as ISOLATED */
zone = page_zone(page);
spin_lock_irqsave(&zone->lock, flags);
- ret = __test_page_isolated_in_pageblock(start_pfn, end_pfn,
+ pfn = __test_page_isolated_in_pageblock(start_pfn, end_pfn,
skip_hwpoisoned_pages);
spin_unlock_irqrestore(&zone->lock, flags);
- return ret ? 0 : -EBUSY;
+
+ trace_test_pages_isolated(start_pfn, end_pfn, pfn);
+
+ return pfn < end_pfn ? -EBUSY : 0;
}
struct page *alloc_migrate_target(struct page *page, unsigned long private,
diff --git a/mm/pgtable-generic.c b/mm/pgtable-generic.c
index 7d3db0247983..4c681baff363 100644
--- a/mm/pgtable-generic.c
+++ b/mm/pgtable-generic.c
@@ -176,13 +176,10 @@ pgtable_t pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp)
/* FIFO */
pgtable = pmd_huge_pte(mm, pmdp);
- if (list_empty(&pgtable->lru))
- pmd_huge_pte(mm, pmdp) = NULL;
- else {
- pmd_huge_pte(mm, pmdp) = list_entry(pgtable->lru.next,
- struct page, lru);
+ pmd_huge_pte(mm, pmdp) = list_first_entry_or_null(&pgtable->lru,
+ struct page, lru);
+ if (pmd_huge_pte(mm, pmdp))
list_del(&pgtable->lru);
- }
return pgtable;
}
#endif
diff --git a/mm/readahead.c b/mm/readahead.c
index ba22d7fe0afb..20e58e820e44 100644
--- a/mm/readahead.c
+++ b/mm/readahead.c
@@ -17,6 +17,7 @@
#include <linux/pagemap.h>
#include <linux/syscalls.h>
#include <linux/file.h>
+#include <linux/mm_inline.h>
#include "internal.h"
@@ -32,8 +33,6 @@ file_ra_state_init(struct file_ra_state *ra, struct address_space *mapping)
}
EXPORT_SYMBOL_GPL(file_ra_state_init);
-#define list_to_page(head) (list_entry((head)->prev, struct page, lru))
-
/*
* see if a page needs releasing upon read_cache_pages() failure
* - the caller of read_cache_pages() may have set PG_private or PG_fscache
@@ -64,7 +63,7 @@ static void read_cache_pages_invalidate_pages(struct address_space *mapping,
struct page *victim;
while (!list_empty(pages)) {
- victim = list_to_page(pages);
+ victim = lru_to_page(pages);
list_del(&victim->lru);
read_cache_pages_invalidate_page(mapping, victim);
}
@@ -87,7 +86,7 @@ int read_cache_pages(struct address_space *mapping, struct list_head *pages,
int ret = 0;
while (!list_empty(pages)) {
- page = list_to_page(pages);
+ page = lru_to_page(pages);
list_del(&page->lru);
if (add_to_page_cache_lru(page, mapping, page->index,
mapping_gfp_constraint(mapping, GFP_KERNEL))) {
@@ -125,7 +124,7 @@ static int read_pages(struct address_space *mapping, struct file *filp,
}
for (page_idx = 0; page_idx < nr_pages; page_idx++) {
- struct page *page = list_to_page(pages);
+ struct page *page = lru_to_page(pages);
list_del(&page->lru);
if (!add_to_page_cache_lru(page, mapping, page->index,
mapping_gfp_constraint(mapping, GFP_KERNEL))) {
diff --git a/mm/rmap.c b/mm/rmap.c
index b577fbb98d4b..622756c16ac8 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -428,8 +428,10 @@ static void anon_vma_ctor(void *data)
void __init anon_vma_init(void)
{
anon_vma_cachep = kmem_cache_create("anon_vma", sizeof(struct anon_vma),
- 0, SLAB_DESTROY_BY_RCU|SLAB_PANIC, anon_vma_ctor);
- anon_vma_chain_cachep = KMEM_CACHE(anon_vma_chain, SLAB_PANIC);
+ 0, SLAB_DESTROY_BY_RCU|SLAB_PANIC|SLAB_ACCOUNT,
+ anon_vma_ctor);
+ anon_vma_chain_cachep = KMEM_CACHE(anon_vma_chain,
+ SLAB_PANIC|SLAB_ACCOUNT);
}
/*
@@ -1362,10 +1364,7 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
if (PageHuge(page)) {
hugetlb_count_sub(1 << compound_order(page), mm);
} else {
- if (PageAnon(page))
- dec_mm_counter(mm, MM_ANONPAGES);
- else
- dec_mm_counter(mm, MM_FILEPAGES);
+ dec_mm_counter(mm, mm_counter(page));
}
set_pte_at(mm, address, pte,
swp_entry_to_pte(make_hwpoison_entry(page)));
@@ -1375,10 +1374,7 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
* interest anymore. Simply discard the pte, vmscan
* will take care of the rest.
*/
- if (PageAnon(page))
- dec_mm_counter(mm, MM_ANONPAGES);
- else
- dec_mm_counter(mm, MM_FILEPAGES);
+ dec_mm_counter(mm, mm_counter(page));
} else if (IS_ENABLED(CONFIG_MIGRATION) && (flags & TTU_MIGRATION)) {
swp_entry_t entry;
pte_t swp_pte;
@@ -1418,7 +1414,7 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
swp_pte = pte_swp_mksoft_dirty(swp_pte);
set_pte_at(mm, address, pte, swp_pte);
} else
- dec_mm_counter(mm, MM_FILEPAGES);
+ dec_mm_counter(mm, mm_counter_file(page));
page_remove_rmap(page);
page_cache_release(page);
diff --git a/mm/shmem.c b/mm/shmem.c
index 9187eee4128b..970ff5b80853 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -360,6 +360,87 @@ static int shmem_free_swap(struct address_space *mapping,
}
/*
+ * Determine (in bytes) how many of the shmem object's pages mapped by the
+ * given offsets are swapped out.
+ *
+ * This is safe to call without i_mutex or mapping->tree_lock thanks to RCU,
+ * as long as the inode doesn't go away and racy results are not a problem.
+ */
+unsigned long shmem_partial_swap_usage(struct address_space *mapping,
+ pgoff_t start, pgoff_t end)
+{
+ struct radix_tree_iter iter;
+ void **slot;
+ struct page *page;
+ unsigned long swapped = 0;
+
+ rcu_read_lock();
+
+restart:
+ radix_tree_for_each_slot(slot, &mapping->page_tree, &iter, start) {
+ if (iter.index >= end)
+ break;
+
+ page = radix_tree_deref_slot(slot);
+
+ /*
+ * This should only be possible to happen at index 0, so we
+ * don't need to reset the counter, nor do we risk infinite
+ * restarts.
+ */
+ if (radix_tree_deref_retry(page))
+ goto restart;
+
+ if (radix_tree_exceptional_entry(page))
+ swapped++;
+
+ if (need_resched()) {
+ cond_resched_rcu();
+ start = iter.index + 1;
+ goto restart;
+ }
+ }
+
+ rcu_read_unlock();
+
+ return swapped << PAGE_SHIFT;
+}
+
+/*
+ * Determine (in bytes) how many of the shmem object's pages mapped by the
+ * given vma is swapped out.
+ *
+ * This is safe to call without i_mutex or mapping->tree_lock thanks to RCU,
+ * as long as the inode doesn't go away and racy results are not a problem.
+ */
+unsigned long shmem_swap_usage(struct vm_area_struct *vma)
+{
+ struct inode *inode = file_inode(vma->vm_file);
+ struct shmem_inode_info *info = SHMEM_I(inode);
+ struct address_space *mapping = inode->i_mapping;
+ unsigned long swapped;
+
+ /* Be careful as we don't hold info->lock */
+ swapped = READ_ONCE(info->swapped);
+
+ /*
+ * The easier cases are when the shmem object has nothing in swap, or
+ * the vma maps it whole. Then we can simply use the stats that we
+ * already track.
+ */
+ if (!swapped)
+ return 0;
+
+ if (!vma->vm_pgoff && vma->vm_end - vma->vm_start >= inode->i_size)
+ return swapped << PAGE_SHIFT;
+
+ /* Here comes the more involved part */
+ return shmem_partial_swap_usage(mapping,
+ linear_page_index(vma, vma->vm_start),
+ linear_page_index(vma, vma->vm_end));
+}
+
+/*
* SysV IPC SHM_UNLOCK restore Unevictable pages to their evictable lists.
*/
void shmem_unlock_mapping(struct address_space *mapping)
@@ -843,14 +924,14 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc)
list_add_tail(&info->swaplist, &shmem_swaplist);
if (add_to_swap_cache(page, swap, GFP_ATOMIC) == 0) {
- swap_shmem_alloc(swap);
- shmem_delete_from_page_cache(page, swp_to_radix_entry(swap));
-
spin_lock(&info->lock);
- info->swapped++;
shmem_recalc_inode(inode);
+ info->swapped++;
spin_unlock(&info->lock);
+ swap_shmem_alloc(swap);
+ shmem_delete_from_page_cache(page, swp_to_radix_entry(swap));
+
mutex_unlock(&shmem_swaplist_mutex);
BUG_ON(page_mapped(page));
swap_writepage(page, wbc);
@@ -1078,7 +1159,7 @@ repeat:
if (sgp != SGP_WRITE && sgp != SGP_FALLOC &&
((loff_t)index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) {
error = -EINVAL;
- goto failed;
+ goto unlock;
}
if (page && sgp == SGP_WRITE)
@@ -1246,11 +1327,15 @@ clear:
/* Perhaps the file has been truncated since we checked */
if (sgp != SGP_WRITE && sgp != SGP_FALLOC &&
((loff_t)index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) {
+ if (alloced) {
+ ClearPageDirty(page);
+ delete_from_page_cache(page);
+ spin_lock(&info->lock);
+ shmem_recalc_inode(inode);
+ spin_unlock(&info->lock);
+ }
error = -EINVAL;
- if (alloced)
- goto trunc;
- else
- goto failed;
+ goto unlock;
}
*pagep = page;
return 0;
@@ -1258,23 +1343,13 @@ clear:
/*
* Error recovery.
*/
-trunc:
- info = SHMEM_I(inode);
- ClearPageDirty(page);
- delete_from_page_cache(page);
- spin_lock(&info->lock);
- info->alloced--;
- inode->i_blocks -= BLOCKS_PER_PAGE;
- spin_unlock(&info->lock);
decused:
- sbinfo = SHMEM_SB(inode->i_sb);
if (sbinfo->max_blocks)
percpu_counter_add(&sbinfo->used_blocks, -1);
unacct:
shmem_unacct_blocks(info->flags, 1);
failed:
- if (swap.val && error != -EINVAL &&
- !shmem_confirm_swap(mapping, index, swap))
+ if (swap.val && !shmem_confirm_swap(mapping, index, swap))
error = -EEXIST;
unlock:
if (page) {
@@ -2444,7 +2519,6 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
int len;
struct inode *inode;
struct page *page;
- char *kaddr;
struct shmem_inode_info *info;
len = strlen(symname) + 1;
@@ -2476,6 +2550,7 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
inode->i_op = &shmem_short_symlink_operations;
inode->i_link = info->symlink;
} else {
+ inode_nohighmem(inode);
error = shmem_getpage(inode, 0, &page, SGP_WRITE, NULL);
if (error) {
iput(inode);
@@ -2483,9 +2558,7 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
}
inode->i_mapping->a_ops = &shmem_aops;
inode->i_op = &shmem_symlink_inode_operations;
- kaddr = kmap_atomic(page);
- memcpy(kaddr, symname, len);
- kunmap_atomic(kaddr);
+ memcpy(page_address(page), symname, len);
SetPageUptodate(page);
set_page_dirty(page);
unlock_page(page);
@@ -2498,23 +2571,34 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
return 0;
}
-static const char *shmem_follow_link(struct dentry *dentry, void **cookie)
+static void shmem_put_link(void *arg)
{
- struct page *page = NULL;
- int error = shmem_getpage(d_inode(dentry), 0, &page, SGP_READ, NULL);
- if (error)
- return ERR_PTR(error);
- unlock_page(page);
- *cookie = page;
- return kmap(page);
+ mark_page_accessed(arg);
+ put_page(arg);
}
-static void shmem_put_link(struct inode *unused, void *cookie)
+static const char *shmem_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
{
- struct page *page = cookie;
- kunmap(page);
- mark_page_accessed(page);
- page_cache_release(page);
+ struct page *page = NULL;
+ int error;
+ if (!dentry) {
+ page = find_get_page(inode->i_mapping, 0);
+ if (!page)
+ return ERR_PTR(-ECHILD);
+ if (!PageUptodate(page)) {
+ put_page(page);
+ return ERR_PTR(-ECHILD);
+ }
+ } else {
+ error = shmem_getpage(inode, 0, &page, SGP_READ, NULL);
+ if (error)
+ return ERR_PTR(error);
+ unlock_page(page);
+ }
+ set_delayed_call(done, shmem_put_link, page);
+ return page_address(page);
}
#ifdef CONFIG_TMPFS_XATTR
@@ -2561,122 +2645,74 @@ static int shmem_initxattrs(struct inode *inode,
return 0;
}
-static const struct xattr_handler *shmem_xattr_handlers[] = {
-#ifdef CONFIG_TMPFS_POSIX_ACL
- &posix_acl_access_xattr_handler,
- &posix_acl_default_xattr_handler,
-#endif
- NULL
-};
-
-static int shmem_xattr_validate(const char *name)
-{
- struct { const char *prefix; size_t len; } arr[] = {
- { XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN },
- { XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN }
- };
- int i;
-
- for (i = 0; i < ARRAY_SIZE(arr); i++) {
- size_t preflen = arr[i].len;
- if (strncmp(name, arr[i].prefix, preflen) == 0) {
- if (!name[preflen])
- return -EINVAL;
- return 0;
- }
- }
- return -EOPNOTSUPP;
-}
-
-static ssize_t shmem_getxattr(struct dentry *dentry, const char *name,
- void *buffer, size_t size)
+static int shmem_xattr_handler_get(const struct xattr_handler *handler,
+ struct dentry *dentry, const char *name,
+ void *buffer, size_t size)
{
struct shmem_inode_info *info = SHMEM_I(d_inode(dentry));
- int err;
-
- /*
- * If this is a request for a synthetic attribute in the system.*
- * namespace use the generic infrastructure to resolve a handler
- * for it via sb->s_xattr.
- */
- if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
- return generic_getxattr(dentry, name, buffer, size);
-
- err = shmem_xattr_validate(name);
- if (err)
- return err;
+ name = xattr_full_name(handler, name);
return simple_xattr_get(&info->xattrs, name, buffer, size);
}
-static int shmem_setxattr(struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags)
+static int shmem_xattr_handler_set(const struct xattr_handler *handler,
+ struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
{
struct shmem_inode_info *info = SHMEM_I(d_inode(dentry));
- int err;
-
- /*
- * If this is a request for a synthetic attribute in the system.*
- * namespace use the generic infrastructure to resolve a handler
- * for it via sb->s_xattr.
- */
- if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
- return generic_setxattr(dentry, name, value, size, flags);
-
- err = shmem_xattr_validate(name);
- if (err)
- return err;
+ name = xattr_full_name(handler, name);
return simple_xattr_set(&info->xattrs, name, value, size, flags);
}
-static int shmem_removexattr(struct dentry *dentry, const char *name)
-{
- struct shmem_inode_info *info = SHMEM_I(d_inode(dentry));
- int err;
-
- /*
- * If this is a request for a synthetic attribute in the system.*
- * namespace use the generic infrastructure to resolve a handler
- * for it via sb->s_xattr.
- */
- if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
- return generic_removexattr(dentry, name);
+static const struct xattr_handler shmem_security_xattr_handler = {
+ .prefix = XATTR_SECURITY_PREFIX,
+ .get = shmem_xattr_handler_get,
+ .set = shmem_xattr_handler_set,
+};
- err = shmem_xattr_validate(name);
- if (err)
- return err;
+static const struct xattr_handler shmem_trusted_xattr_handler = {
+ .prefix = XATTR_TRUSTED_PREFIX,
+ .get = shmem_xattr_handler_get,
+ .set = shmem_xattr_handler_set,
+};
- return simple_xattr_remove(&info->xattrs, name);
-}
+static const struct xattr_handler *shmem_xattr_handlers[] = {
+#ifdef CONFIG_TMPFS_POSIX_ACL
+ &posix_acl_access_xattr_handler,
+ &posix_acl_default_xattr_handler,
+#endif
+ &shmem_security_xattr_handler,
+ &shmem_trusted_xattr_handler,
+ NULL
+};
static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size)
{
struct shmem_inode_info *info = SHMEM_I(d_inode(dentry));
- return simple_xattr_list(&info->xattrs, buffer, size);
+ return simple_xattr_list(d_inode(dentry), &info->xattrs, buffer, size);
}
#endif /* CONFIG_TMPFS_XATTR */
static const struct inode_operations shmem_short_symlink_operations = {
.readlink = generic_readlink,
- .follow_link = simple_follow_link,
+ .get_link = simple_get_link,
#ifdef CONFIG_TMPFS_XATTR
- .setxattr = shmem_setxattr,
- .getxattr = shmem_getxattr,
+ .setxattr = generic_setxattr,
+ .getxattr = generic_getxattr,
.listxattr = shmem_listxattr,
- .removexattr = shmem_removexattr,
+ .removexattr = generic_removexattr,
#endif
};
static const struct inode_operations shmem_symlink_inode_operations = {
.readlink = generic_readlink,
- .follow_link = shmem_follow_link,
- .put_link = shmem_put_link,
+ .get_link = shmem_get_link,
#ifdef CONFIG_TMPFS_XATTR
- .setxattr = shmem_setxattr,
- .getxattr = shmem_getxattr,
+ .setxattr = generic_setxattr,
+ .getxattr = generic_getxattr,
.listxattr = shmem_listxattr,
- .removexattr = shmem_removexattr,
+ .removexattr = generic_removexattr,
#endif
};
@@ -3109,7 +3145,7 @@ static int shmem_init_inodecache(void)
{
shmem_inode_cachep = kmem_cache_create("shmem_inode_cache",
sizeof(struct shmem_inode_info),
- 0, SLAB_PANIC, shmem_init_inode);
+ 0, SLAB_PANIC|SLAB_ACCOUNT, shmem_init_inode);
return 0;
}
@@ -3148,10 +3184,10 @@ static const struct inode_operations shmem_inode_operations = {
.getattr = shmem_getattr,
.setattr = shmem_setattr,
#ifdef CONFIG_TMPFS_XATTR
- .setxattr = shmem_setxattr,
- .getxattr = shmem_getxattr,
+ .setxattr = generic_setxattr,
+ .getxattr = generic_getxattr,
.listxattr = shmem_listxattr,
- .removexattr = shmem_removexattr,
+ .removexattr = generic_removexattr,
.set_acl = simple_set_acl,
#endif
};
@@ -3170,10 +3206,10 @@ static const struct inode_operations shmem_dir_inode_operations = {
.tmpfile = shmem_tmpfile,
#endif
#ifdef CONFIG_TMPFS_XATTR
- .setxattr = shmem_setxattr,
- .getxattr = shmem_getxattr,
+ .setxattr = generic_setxattr,
+ .getxattr = generic_getxattr,
.listxattr = shmem_listxattr,
- .removexattr = shmem_removexattr,
+ .removexattr = generic_removexattr,
#endif
#ifdef CONFIG_TMPFS_POSIX_ACL
.setattr = shmem_setattr,
@@ -3183,10 +3219,10 @@ static const struct inode_operations shmem_dir_inode_operations = {
static const struct inode_operations shmem_special_inode_operations = {
#ifdef CONFIG_TMPFS_XATTR
- .setxattr = shmem_setxattr,
- .getxattr = shmem_getxattr,
+ .setxattr = generic_setxattr,
+ .getxattr = generic_getxattr,
.listxattr = shmem_listxattr,
- .removexattr = shmem_removexattr,
+ .removexattr = generic_removexattr,
#endif
#ifdef CONFIG_TMPFS_POSIX_ACL
.setattr = shmem_setattr,
diff --git a/mm/slab.c b/mm/slab.c
index 4765c97ce690..6ecc697a8bc4 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -2756,6 +2756,21 @@ static void *cache_free_debugcheck(struct kmem_cache *cachep, void *objp,
#define cache_free_debugcheck(x,objp,z) (objp)
#endif
+static struct page *get_first_slab(struct kmem_cache_node *n)
+{
+ struct page *page;
+
+ page = list_first_entry_or_null(&n->slabs_partial,
+ struct page, lru);
+ if (!page) {
+ n->free_touched = 1;
+ page = list_first_entry_or_null(&n->slabs_free,
+ struct page, lru);
+ }
+
+ return page;
+}
+
static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags,
bool force_refill)
{
@@ -2791,18 +2806,12 @@ retry:
}
while (batchcount > 0) {
- struct list_head *entry;
struct page *page;
/* Get slab alloc is to come from. */
- entry = n->slabs_partial.next;
- if (entry == &n->slabs_partial) {
- n->free_touched = 1;
- entry = n->slabs_free.next;
- if (entry == &n->slabs_free)
- goto must_grow;
- }
+ page = get_first_slab(n);
+ if (!page)
+ goto must_grow;
- page = list_entry(entry, struct page, lru);
check_spinlock_acquired(cachep);
/*
@@ -3085,7 +3094,6 @@ retry:
static void *____cache_alloc_node(struct kmem_cache *cachep, gfp_t flags,
int nodeid)
{
- struct list_head *entry;
struct page *page;
struct kmem_cache_node *n;
void *obj;
@@ -3098,15 +3106,10 @@ static void *____cache_alloc_node(struct kmem_cache *cachep, gfp_t flags,
retry:
check_irq_off();
spin_lock(&n->list_lock);
- entry = n->slabs_partial.next;
- if (entry == &n->slabs_partial) {
- n->free_touched = 1;
- entry = n->slabs_free.next;
- if (entry == &n->slabs_free)
- goto must_grow;
- }
+ page = get_first_slab(n);
+ if (!page)
+ goto must_grow;
- page = list_entry(entry, struct page, lru);
check_spinlock_acquired_node(cachep, nodeid);
STATS_INC_NODEALLOCS(cachep);
@@ -3338,17 +3341,12 @@ free_done:
#if STATS
{
int i = 0;
- struct list_head *p;
-
- p = n->slabs_free.next;
- while (p != &(n->slabs_free)) {
- struct page *page;
+ struct page *page;
- page = list_entry(p, struct page, lru);
+ list_for_each_entry(page, &n->slabs_free, lru) {
BUG_ON(page->active);
i++;
- p = p->next;
}
STATS_SET_FREEABLE(cachep, i);
}
diff --git a/mm/slab.h b/mm/slab.h
index 7b6087197997..c63b8699cfa3 100644
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -128,10 +128,11 @@ static inline unsigned long kmem_cache_flags(unsigned long object_size,
#if defined(CONFIG_SLAB)
#define SLAB_CACHE_FLAGS (SLAB_MEM_SPREAD | SLAB_NOLEAKTRACE | \
- SLAB_RECLAIM_ACCOUNT | SLAB_TEMPORARY | SLAB_NOTRACK)
+ SLAB_RECLAIM_ACCOUNT | SLAB_TEMPORARY | \
+ SLAB_NOTRACK | SLAB_ACCOUNT)
#elif defined(CONFIG_SLUB)
#define SLAB_CACHE_FLAGS (SLAB_NOLEAKTRACE | SLAB_RECLAIM_ACCOUNT | \
- SLAB_TEMPORARY | SLAB_NOTRACK)
+ SLAB_TEMPORARY | SLAB_NOTRACK | SLAB_ACCOUNT)
#else
#define SLAB_CACHE_FLAGS (0)
#endif
diff --git a/mm/slab_common.c b/mm/slab_common.c
index 3c6a86b4ec25..e016178063e1 100644
--- a/mm/slab_common.c
+++ b/mm/slab_common.c
@@ -37,7 +37,8 @@ struct kmem_cache *kmem_cache;
SLAB_TRACE | SLAB_DESTROY_BY_RCU | SLAB_NOLEAKTRACE | \
SLAB_FAILSLAB)
-#define SLAB_MERGE_SAME (SLAB_RECLAIM_ACCOUNT | SLAB_CACHE_DMA | SLAB_NOTRACK)
+#define SLAB_MERGE_SAME (SLAB_RECLAIM_ACCOUNT | SLAB_CACHE_DMA | \
+ SLAB_NOTRACK | SLAB_ACCOUNT)
/*
* Merge control. If this is set then no merging of slab caches will occur.
diff --git a/mm/slub.c b/mm/slub.c
index 46997517406e..2d0e610d195a 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -5362,6 +5362,8 @@ static char *create_unique_id(struct kmem_cache *s)
*p++ = 'F';
if (!(s->flags & SLAB_NOTRACK))
*p++ = 't';
+ if (s->flags & SLAB_ACCOUNT)
+ *p++ = 'A';
if (p != name + 1)
*p++ = '-';
p += sprintf(p, "%07d", s->size);
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 58877312cf6b..e6b8591a3ed2 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -165,8 +165,6 @@ static void discard_swap_cluster(struct swap_info_struct *si,
int found_extent = 0;
while (nr_pages) {
- struct list_head *lh;
-
if (se->start_page <= start_page &&
start_page < se->start_page + se->nr_pages) {
pgoff_t offset = start_page - se->start_page;
@@ -188,8 +186,7 @@ static void discard_swap_cluster(struct swap_info_struct *si,
break;
}
- lh = se->list.next;
- se = list_entry(lh, struct swap_extent, list);
+ se = list_next_entry(se, list);
}
}
@@ -903,7 +900,7 @@ int swp_swapcount(swp_entry_t entry)
VM_BUG_ON(page_private(page) != SWP_CONTINUED);
do {
- page = list_entry(page->lru.next, struct page, lru);
+ page = list_next_entry(page, lru);
map = kmap_atomic(page);
tmp_count = map[offset];
kunmap_atomic(map);
@@ -1633,14 +1630,11 @@ static sector_t map_swap_entry(swp_entry_t entry, struct block_device **bdev)
se = start_se;
for ( ; ; ) {
- struct list_head *lh;
-
if (se->start_page <= offset &&
offset < (se->start_page + se->nr_pages)) {
return se->start_block + (offset - se->start_page);
}
- lh = se->list.next;
- se = list_entry(lh, struct swap_extent, list);
+ se = list_next_entry(se, list);
sis->curr_swap_extent = se;
BUG_ON(se == start_se); /* It *must* be present */
}
@@ -1664,7 +1658,7 @@ static void destroy_swap_extents(struct swap_info_struct *sis)
while (!list_empty(&sis->first_swap_extent.list)) {
struct swap_extent *se;
- se = list_entry(sis->first_swap_extent.list.next,
+ se = list_first_entry(&sis->first_swap_extent.list,
struct swap_extent, list);
list_del(&se->list);
kfree(se);
@@ -2959,11 +2953,10 @@ static void free_swap_count_continuations(struct swap_info_struct *si)
struct page *head;
head = vmalloc_to_page(si->swap_map + offset);
if (page_private(head)) {
- struct list_head *this, *next;
- list_for_each_safe(this, next, &head->lru) {
- struct page *page;
- page = list_entry(this, struct page, lru);
- list_del(this);
+ struct page *page, *next;
+
+ list_for_each_entry_safe(page, next, &head->lru, lru) {
+ list_del(&page->lru);
__free_page(page);
}
}
diff --git a/mm/util.c b/mm/util.c
index 9af1c12b310c..2d28f7930043 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -176,6 +176,37 @@ char *strndup_user(const char __user *s, long n)
}
EXPORT_SYMBOL(strndup_user);
+/**
+ * memdup_user_nul - duplicate memory region from user space and NUL-terminate
+ *
+ * @src: source address in user space
+ * @len: number of bytes to copy
+ *
+ * Returns an ERR_PTR() on failure.
+ */
+void *memdup_user_nul(const void __user *src, size_t len)
+{
+ char *p;
+
+ /*
+ * Always use GFP_KERNEL, since copy_from_user() can sleep and
+ * cause pagefault, which makes it pointless to use GFP_NOFS
+ * or GFP_ATOMIC.
+ */
+ p = kmalloc_track_caller(len + 1, GFP_KERNEL);
+ if (!p)
+ return ERR_PTR(-ENOMEM);
+
+ if (copy_from_user(p, src, len)) {
+ kfree(p);
+ return ERR_PTR(-EFAULT);
+ }
+ p[len] = '\0';
+
+ return p;
+}
+EXPORT_SYMBOL(memdup_user_nul);
+
void __vma_link_list(struct mm_struct *mm, struct vm_area_struct *vma,
struct vm_area_struct *prev, struct rb_node *rb_parent)
{
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 8e3c9c5a3042..58ceeb107960 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -441,8 +441,7 @@ nocache:
if (list_is_last(&first->list, &vmap_area_list))
goto found;
- first = list_entry(first->list.next,
- struct vmap_area, list);
+ first = list_next_entry(first, list);
}
found:
@@ -1477,13 +1476,10 @@ static void __vunmap(const void *addr, int deallocate_pages)
struct page *page = area->pages[i];
BUG_ON(!page);
- __free_page(page);
+ __free_kmem_pages(page, 0);
}
- if (area->flags & VM_VPAGES)
- vfree(area->pages);
- else
- kfree(area->pages);
+ kvfree(area->pages);
}
kfree(area);
@@ -1593,7 +1589,6 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
if (array_size > PAGE_SIZE) {
pages = __vmalloc_node(array_size, 1, nested_gfp|__GFP_HIGHMEM,
PAGE_KERNEL, node, area->caller);
- area->flags |= VM_VPAGES;
} else {
pages = kmalloc_node(array_size, nested_gfp, node);
}
@@ -1608,9 +1603,9 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
struct page *page;
if (node == NUMA_NO_NODE)
- page = alloc_page(alloc_mask);
+ page = alloc_kmem_pages(alloc_mask, order);
else
- page = alloc_pages_node(node, alloc_mask, order);
+ page = alloc_kmem_pages_node(node, alloc_mask, order);
if (unlikely(!page)) {
/* Successfully allocated i pages, free them in __vunmap() */
@@ -2559,10 +2554,10 @@ static void *s_start(struct seq_file *m, loff_t *pos)
struct vmap_area *va;
spin_lock(&vmap_area_lock);
- va = list_entry((&vmap_area_list)->next, typeof(*va), list);
+ va = list_first_entry(&vmap_area_list, typeof(*va), list);
while (n > 0 && &va->list != &vmap_area_list) {
n--;
- va = list_entry(va->list.next, typeof(*va), list);
+ va = list_next_entry(va, list);
}
if (!n && &va->list != &vmap_area_list)
return va;
@@ -2576,7 +2571,7 @@ static void *s_next(struct seq_file *m, void *p, loff_t *pos)
struct vmap_area *va = p, *next;
++*pos;
- next = list_entry(va->list.next, typeof(*va), list);
+ next = list_next_entry(va, list);
if (&next->list != &vmap_area_list)
return next;
@@ -2651,7 +2646,7 @@ static int s_show(struct seq_file *m, void *p)
if (v->flags & VM_USERMAP)
seq_puts(m, " user");
- if (v->flags & VM_VPAGES)
+ if (is_vmalloc_addr(v->pages))
seq_puts(m, " vpages");
show_numa_info(m, v);
diff --git a/mm/vmpressure.c b/mm/vmpressure.c
index c5afd573d7da..9a6c0704211c 100644
--- a/mm/vmpressure.c
+++ b/mm/vmpressure.c
@@ -137,14 +137,11 @@ struct vmpressure_event {
};
static bool vmpressure_event(struct vmpressure *vmpr,
- unsigned long scanned, unsigned long reclaimed)
+ enum vmpressure_levels level)
{
struct vmpressure_event *ev;
- enum vmpressure_levels level;
bool signalled = false;
- level = vmpressure_calc_level(scanned, reclaimed);
-
mutex_lock(&vmpr->events_lock);
list_for_each_entry(ev, &vmpr->events, node) {
@@ -164,6 +161,7 @@ static void vmpressure_work_fn(struct work_struct *work)
struct vmpressure *vmpr = work_to_vmpressure(work);
unsigned long scanned;
unsigned long reclaimed;
+ enum vmpressure_levels level;
spin_lock(&vmpr->sr_lock);
/*
@@ -174,19 +172,21 @@ static void vmpressure_work_fn(struct work_struct *work)
* here. No need for any locks here since we don't care if
* vmpr->reclaimed is in sync.
*/
- scanned = vmpr->scanned;
+ scanned = vmpr->tree_scanned;
if (!scanned) {
spin_unlock(&vmpr->sr_lock);
return;
}
- reclaimed = vmpr->reclaimed;
- vmpr->scanned = 0;
- vmpr->reclaimed = 0;
+ reclaimed = vmpr->tree_reclaimed;
+ vmpr->tree_scanned = 0;
+ vmpr->tree_reclaimed = 0;
spin_unlock(&vmpr->sr_lock);
+ level = vmpressure_calc_level(scanned, reclaimed);
+
do {
- if (vmpressure_event(vmpr, scanned, reclaimed))
+ if (vmpressure_event(vmpr, level))
break;
/*
* If not handled, propagate the event upward into the
@@ -199,6 +199,7 @@ static void vmpressure_work_fn(struct work_struct *work)
* vmpressure() - Account memory pressure through scanned/reclaimed ratio
* @gfp: reclaimer's gfp mask
* @memcg: cgroup memory controller handle
+ * @tree: legacy subtree mode
* @scanned: number of pages scanned
* @reclaimed: number of pages reclaimed
*
@@ -206,9 +207,16 @@ static void vmpressure_work_fn(struct work_struct *work)
* "instantaneous" memory pressure (scanned/reclaimed ratio). The raw
* pressure index is then further refined and averaged over time.
*
+ * If @tree is set, vmpressure is in traditional userspace reporting
+ * mode: @memcg is considered the pressure root and userspace is
+ * notified of the entire subtree's reclaim efficiency.
+ *
+ * If @tree is not set, reclaim efficiency is recorded for @memcg, and
+ * only in-kernel users are notified.
+ *
* This function does not return any value.
*/
-void vmpressure(gfp_t gfp, struct mem_cgroup *memcg,
+void vmpressure(gfp_t gfp, struct mem_cgroup *memcg, bool tree,
unsigned long scanned, unsigned long reclaimed)
{
struct vmpressure *vmpr = memcg_to_vmpressure(memcg);
@@ -238,15 +246,47 @@ void vmpressure(gfp_t gfp, struct mem_cgroup *memcg,
if (!scanned)
return;
- spin_lock(&vmpr->sr_lock);
- vmpr->scanned += scanned;
- vmpr->reclaimed += reclaimed;
- scanned = vmpr->scanned;
- spin_unlock(&vmpr->sr_lock);
+ if (tree) {
+ spin_lock(&vmpr->sr_lock);
+ vmpr->tree_scanned += scanned;
+ vmpr->tree_reclaimed += reclaimed;
+ scanned = vmpr->scanned;
+ spin_unlock(&vmpr->sr_lock);
- if (scanned < vmpressure_win)
- return;
- schedule_work(&vmpr->work);
+ if (scanned < vmpressure_win)
+ return;
+ schedule_work(&vmpr->work);
+ } else {
+ enum vmpressure_levels level;
+
+ /* For now, no users for root-level efficiency */
+ if (!memcg || memcg == root_mem_cgroup)
+ return;
+
+ spin_lock(&vmpr->sr_lock);
+ scanned = vmpr->scanned += scanned;
+ reclaimed = vmpr->reclaimed += reclaimed;
+ if (scanned < vmpressure_win) {
+ spin_unlock(&vmpr->sr_lock);
+ return;
+ }
+ vmpr->scanned = vmpr->reclaimed = 0;
+ spin_unlock(&vmpr->sr_lock);
+
+ level = vmpressure_calc_level(scanned, reclaimed);
+
+ if (level > VMPRESSURE_LOW) {
+ /*
+ * Let the socket buffer allocator know that
+ * we are having trouble reclaiming LRU pages.
+ *
+ * For hysteresis keep the pressure state
+ * asserted for a second in which subsequent
+ * pressure events can occur.
+ */
+ memcg->socket_pressure = jiffies + HZ;
+ }
+ }
}
/**
@@ -276,7 +316,7 @@ void vmpressure_prio(gfp_t gfp, struct mem_cgroup *memcg, int prio)
* to the vmpressure() basically means that we signal 'critical'
* level.
*/
- vmpressure(gfp, memcg, vmpressure_win, 0);
+ vmpressure(gfp, memcg, true, vmpressure_win, 0);
}
/**
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 2aec4241b42a..108bd119f2f6 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -106,8 +106,6 @@ struct scan_control {
unsigned long nr_reclaimed;
};
-#define lru_to_page(_head) (list_entry((_head)->prev, struct page, lru))
-
#ifdef ARCH_HAS_PREFETCH
#define prefetch_prev_lru_page(_page, _base, _field) \
do { \
@@ -197,11 +195,13 @@ static unsigned long zone_reclaimable_pages(struct zone *zone)
unsigned long nr;
nr = zone_page_state(zone, NR_ACTIVE_FILE) +
- zone_page_state(zone, NR_INACTIVE_FILE);
+ zone_page_state(zone, NR_INACTIVE_FILE) +
+ zone_page_state(zone, NR_ISOLATED_FILE);
if (get_nr_swap_pages() > 0)
nr += zone_page_state(zone, NR_ACTIVE_ANON) +
- zone_page_state(zone, NR_INACTIVE_ANON);
+ zone_page_state(zone, NR_INACTIVE_ANON) +
+ zone_page_state(zone, NR_ISOLATED_ANON);
return nr;
}
@@ -594,7 +594,7 @@ static pageout_t pageout(struct page *page, struct address_space *mapping,
/* synchronous write or broken a_ops? */
ClearPageReclaim(page);
}
- trace_mm_vmscan_writepage(page, trace_reclaim_flags(page));
+ trace_mm_vmscan_writepage(page);
inc_zone_page_state(page, NR_VMSCAN_WRITE);
return PAGE_SUCCESS;
}
@@ -1426,6 +1426,7 @@ int isolate_lru_page(struct page *page)
int ret = -EBUSY;
VM_BUG_ON_PAGE(!page_count(page), page);
+ VM_BUG_ON_PAGE(PageTail(page), page);
if (PageLRU(page)) {
struct zone *zone = page_zone(page);
@@ -1691,11 +1692,8 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec,
current_may_throttle())
wait_iff_congested(zone, BLK_RW_ASYNC, HZ/10);
- trace_mm_vmscan_lru_shrink_inactive(zone->zone_pgdat->node_id,
- zone_idx(zone),
- nr_scanned, nr_reclaimed,
- sc->priority,
- trace_shrink_flags(file));
+ trace_mm_vmscan_lru_shrink_inactive(zone, nr_scanned, nr_reclaimed,
+ sc->priority, file);
return nr_reclaimed;
}
@@ -2046,10 +2044,16 @@ static void get_scan_count(struct lruvec *lruvec, int swappiness,
}
/*
- * There is enough inactive page cache, do not reclaim
- * anything from the anonymous working set right now.
+ * If there is enough inactive page cache, i.e. if the size of the
+ * inactive list is greater than that of the active list *and* the
+ * inactive list actually has some pages to scan on this priority, we
+ * do not reclaim anything from the anonymous working set right now.
+ * Without the second condition we could end up never scanning an
+ * lruvec even if it has plenty of old anonymous pages unless the
+ * system is under heavy pressure.
*/
- if (!inactive_file_is_low(lruvec)) {
+ if (!inactive_file_is_low(lruvec) &&
+ get_lru_size(lruvec, LRU_INACTIVE_FILE) >> sc->priority) {
scan_balance = SCAN_FILE;
goto out;
}
@@ -2393,6 +2397,7 @@ static bool shrink_zone(struct zone *zone, struct scan_control *sc,
memcg = mem_cgroup_iter(root, NULL, &reclaim);
do {
unsigned long lru_pages;
+ unsigned long reclaimed;
unsigned long scanned;
struct lruvec *lruvec;
int swappiness;
@@ -2405,6 +2410,7 @@ static bool shrink_zone(struct zone *zone, struct scan_control *sc,
lruvec = mem_cgroup_zone_lruvec(zone, memcg);
swappiness = mem_cgroup_swappiness(memcg);
+ reclaimed = sc->nr_reclaimed;
scanned = sc->nr_scanned;
shrink_lruvec(lruvec, swappiness, sc, &lru_pages);
@@ -2415,6 +2421,11 @@ static bool shrink_zone(struct zone *zone, struct scan_control *sc,
memcg, sc->nr_scanned - scanned,
lru_pages);
+ /* Record the group's reclaim efficiency */
+ vmpressure(sc->gfp_mask, memcg, false,
+ sc->nr_scanned - scanned,
+ sc->nr_reclaimed - reclaimed);
+
/*
* Direct reclaim and kswapd have to scan all memory
* cgroups to fulfill the overall scan target for the
@@ -2446,7 +2457,8 @@ static bool shrink_zone(struct zone *zone, struct scan_control *sc,
reclaim_state->reclaimed_slab = 0;
}
- vmpressure(sc->gfp_mask, sc->target_mem_cgroup,
+ /* Record the subtree's reclaim efficiency */
+ vmpressure(sc->gfp_mask, sc->target_mem_cgroup, true,
sc->nr_scanned - nr_scanned,
sc->nr_reclaimed - nr_reclaimed);
diff --git a/mm/vmstat.c b/mm/vmstat.c
index 879a2be23325..83a003bc3cae 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -219,7 +219,7 @@ void set_pgdat_percpu_threshold(pg_data_t *pgdat,
* particular counter cannot be updated from interrupt context.
*/
void __mod_zone_page_state(struct zone *zone, enum zone_stat_item item,
- int delta)
+ long delta)
{
struct per_cpu_pageset __percpu *pcp = zone->pageset;
s8 __percpu *p = pcp->vm_stat_diff + item;
@@ -318,8 +318,8 @@ EXPORT_SYMBOL(__dec_zone_page_state);
* 1 Overstepping half of threshold
* -1 Overstepping minus half of threshold
*/
-static inline void mod_state(struct zone *zone,
- enum zone_stat_item item, int delta, int overstep_mode)
+static inline void mod_state(struct zone *zone, enum zone_stat_item item,
+ long delta, int overstep_mode)
{
struct per_cpu_pageset __percpu *pcp = zone->pageset;
s8 __percpu *p = pcp->vm_stat_diff + item;
@@ -357,7 +357,7 @@ static inline void mod_state(struct zone *zone,
}
void mod_zone_page_state(struct zone *zone, enum zone_stat_item item,
- int delta)
+ long delta)
{
mod_state(zone, item, delta, 0);
}
@@ -384,7 +384,7 @@ EXPORT_SYMBOL(dec_zone_page_state);
* Use interrupt disable to serialize counter updates
*/
void mod_zone_page_state(struct zone *zone, enum zone_stat_item item,
- int delta)
+ long delta)
{
unsigned long flags;
@@ -460,7 +460,7 @@ static int fold_diff(int *diff)
*
* The function returns the number of global counters updated.
*/
-static int refresh_cpu_vm_stats(void)
+static int refresh_cpu_vm_stats(bool do_pagesets)
{
struct zone *zone;
int i;
@@ -484,33 +484,35 @@ static int refresh_cpu_vm_stats(void)
#endif
}
}
- cond_resched();
#ifdef CONFIG_NUMA
- /*
- * Deal with draining the remote pageset of this
- * processor
- *
- * Check if there are pages remaining in this pageset
- * if not then there is nothing to expire.
- */
- if (!__this_cpu_read(p->expire) ||
+ if (do_pagesets) {
+ cond_resched();
+ /*
+ * Deal with draining the remote pageset of this
+ * processor
+ *
+ * Check if there are pages remaining in this pageset
+ * if not then there is nothing to expire.
+ */
+ if (!__this_cpu_read(p->expire) ||
!__this_cpu_read(p->pcp.count))
- continue;
+ continue;
- /*
- * We never drain zones local to this processor.
- */
- if (zone_to_nid(zone) == numa_node_id()) {
- __this_cpu_write(p->expire, 0);
- continue;
- }
+ /*
+ * We never drain zones local to this processor.
+ */
+ if (zone_to_nid(zone) == numa_node_id()) {
+ __this_cpu_write(p->expire, 0);
+ continue;
+ }
- if (__this_cpu_dec_return(p->expire))
- continue;
+ if (__this_cpu_dec_return(p->expire))
+ continue;
- if (__this_cpu_read(p->pcp.count)) {
- drain_zone_pages(zone, this_cpu_ptr(&p->pcp));
- changes++;
+ if (__this_cpu_read(p->pcp.count)) {
+ drain_zone_pages(zone, this_cpu_ptr(&p->pcp));
+ changes++;
+ }
}
#endif
}
@@ -921,8 +923,8 @@ static void walk_zones_in_node(struct seq_file *m, pg_data_t *pgdat,
#ifdef CONFIG_PROC_FS
static char * const migratetype_names[MIGRATE_TYPES] = {
"Unmovable",
- "Reclaimable",
"Movable",
+ "Reclaimable",
"HighAtomic",
#ifdef CONFIG_CMA
"CMA",
@@ -1379,19 +1381,20 @@ static const struct file_operations proc_vmstat_file_operations = {
#endif /* CONFIG_PROC_FS */
#ifdef CONFIG_SMP
+static struct workqueue_struct *vmstat_wq;
static DEFINE_PER_CPU(struct delayed_work, vmstat_work);
int sysctl_stat_interval __read_mostly = HZ;
static cpumask_var_t cpu_stat_off;
static void vmstat_update(struct work_struct *w)
{
- if (refresh_cpu_vm_stats()) {
+ if (refresh_cpu_vm_stats(true)) {
/*
* Counters were updated so we expect more updates
* to occur in the future. Keep on running the
* update worker thread.
*/
- schedule_delayed_work_on(smp_processor_id(),
+ queue_delayed_work_on(smp_processor_id(), vmstat_wq,
this_cpu_ptr(&vmstat_work),
round_jiffies_relative(sysctl_stat_interval));
} else {
@@ -1417,6 +1420,23 @@ static void vmstat_update(struct work_struct *w)
}
/*
+ * Switch off vmstat processing and then fold all the remaining differentials
+ * until the diffs stay at zero. The function is used by NOHZ and can only be
+ * invoked when tick processing is not active.
+ */
+void quiet_vmstat(void)
+{
+ if (system_state != SYSTEM_RUNNING)
+ return;
+
+ do {
+ if (!cpumask_test_and_set_cpu(smp_processor_id(), cpu_stat_off))
+ cancel_delayed_work(this_cpu_ptr(&vmstat_work));
+
+ } while (refresh_cpu_vm_stats(false));
+}
+
+/*
* Check if the diffs for a certain cpu indicate that
* an update is needed.
*/
@@ -1448,7 +1468,7 @@ static bool need_update(int cpu)
*/
static void vmstat_shepherd(struct work_struct *w);
-static DECLARE_DELAYED_WORK(shepherd, vmstat_shepherd);
+static DECLARE_DEFERRABLE_WORK(shepherd, vmstat_shepherd);
static void vmstat_shepherd(struct work_struct *w)
{
@@ -1460,7 +1480,7 @@ static void vmstat_shepherd(struct work_struct *w)
if (need_update(cpu) &&
cpumask_test_and_clear_cpu(cpu, cpu_stat_off))
- schedule_delayed_work_on(cpu,
+ queue_delayed_work_on(cpu, vmstat_wq,
&per_cpu(vmstat_work, cpu), 0);
put_online_cpus();
@@ -1482,6 +1502,7 @@ static void __init start_shepherd_timer(void)
BUG();
cpumask_copy(cpu_stat_off, cpu_online_mask);
+ vmstat_wq = alloc_workqueue("vmstat", WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
schedule_delayed_work(&shepherd,
round_jiffies_relative(sysctl_stat_interval));
}
diff --git a/mm/zbud.c b/mm/zbud.c
index d8a181fd779b..b42322e50f63 100644
--- a/mm/zbud.c
+++ b/mm/zbud.c
@@ -463,9 +463,6 @@ void zbud_free(struct zbud_pool *pool, unsigned long handle)
spin_unlock(&pool->lock);
}
-#define list_tail_entry(ptr, type, member) \
- list_entry((ptr)->prev, type, member)
-
/**
* zbud_reclaim_page() - evicts allocations from a pool page and frees it
* @pool: pool from which a page will attempt to be evicted
@@ -514,7 +511,7 @@ int zbud_reclaim_page(struct zbud_pool *pool, unsigned int retries)
return -EINVAL;
}
for (i = 0; i < retries; i++) {
- zhdr = list_tail_entry(&pool->lru, struct zbud_header, lru);
+ zhdr = list_last_entry(&pool->lru, struct zbud_header, lru);
list_del(&zhdr->lru);
list_del(&zhdr->buddy);
/* Protect zbud page against free */
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index 9f15bdd9163c..e7414cec220b 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -213,10 +213,10 @@ struct size_class {
int size;
unsigned int index;
- /* Number of PAGE_SIZE sized pages to combine to form a 'zspage' */
- int pages_per_zspage;
struct zs_size_stat stats;
+ /* Number of PAGE_SIZE sized pages to combine to form a 'zspage' */
+ int pages_per_zspage;
/* huge object: pages_per_zspage == 1 && maxobj_per_zspage == 1 */
bool huge;
};
diff --git a/mm/zswap.c b/mm/zswap.c
index 025f8dc723de..bf14508afd64 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -541,6 +541,7 @@ static struct zswap_pool *zswap_pool_last_get(void)
return last;
}
+/* type and compressor must be null-terminated */
static struct zswap_pool *zswap_pool_find_get(char *type, char *compressor)
{
struct zswap_pool *pool;
@@ -548,10 +549,9 @@ static struct zswap_pool *zswap_pool_find_get(char *type, char *compressor)
assert_spin_locked(&zswap_pools_lock);
list_for_each_entry_rcu(pool, &zswap_pools, list) {
- if (strncmp(pool->tfm_name, compressor, sizeof(pool->tfm_name)))
+ if (strcmp(pool->tfm_name, compressor))
continue;
- if (strncmp(zpool_get_type(pool->zpool), type,
- sizeof(zswap_zpool_type)))
+ if (strcmp(zpool_get_type(pool->zpool), type))
continue;
/* if we can't get it, it's about to be destroyed */
if (!zswap_pool_get(pool))
diff --git a/net/6lowpan/6lowpan_i.h b/net/6lowpan/6lowpan_i.h
new file mode 100644
index 000000000000..d16bb4b14aa1
--- /dev/null
+++ b/net/6lowpan/6lowpan_i.h
@@ -0,0 +1,28 @@
+#ifndef __6LOWPAN_I_H
+#define __6LOWPAN_I_H
+
+#include <linux/netdevice.h>
+
+#ifdef CONFIG_6LOWPAN_DEBUGFS
+int lowpan_dev_debugfs_init(struct net_device *dev);
+void lowpan_dev_debugfs_exit(struct net_device *dev);
+
+int __init lowpan_debugfs_init(void);
+void lowpan_debugfs_exit(void);
+#else
+static inline int lowpan_dev_debugfs_init(struct net_device *dev)
+{
+ return 0;
+}
+
+static inline void lowpan_dev_debugfs_exit(struct net_device *dev) { }
+
+static inline int __init lowpan_debugfs_init(void)
+{
+ return 0;
+}
+
+static inline void lowpan_debugfs_exit(void) { }
+#endif /* CONFIG_6LOWPAN_DEBUGFS */
+
+#endif /* __6LOWPAN_I_H */
diff --git a/net/6lowpan/Kconfig b/net/6lowpan/Kconfig
index 7fa0f382e7d1..9c051512d14f 100644
--- a/net/6lowpan/Kconfig
+++ b/net/6lowpan/Kconfig
@@ -5,12 +5,21 @@ menuconfig 6LOWPAN
This enables IPv6 over Low power Wireless Personal Area Network -
"6LoWPAN" which is supported by IEEE 802.15.4 or Bluetooth stacks.
+config 6LOWPAN_DEBUGFS
+ bool "6LoWPAN debugfs support"
+ depends on 6LOWPAN
+ depends on DEBUG_FS
+ ---help---
+ This enables 6LoWPAN debugfs support. For example to manipulate
+ IPHC context information at runtime.
+
menuconfig 6LOWPAN_NHC
- tristate "Next Header Compression Support"
+ tristate "Next Header and Generic Header Compression Support"
depends on 6LOWPAN
default y
---help---
- Support for next header compression.
+ Support for next header and generic header compression defined in
+ RFC6282 and RFC7400.
if 6LOWPAN_NHC
@@ -58,4 +67,38 @@ config 6LOWPAN_NHC_UDP
---help---
6LoWPAN IPv6 UDP Header compression according to RFC6282.
+config 6LOWPAN_GHC_EXT_HDR_HOP
+ tristate "GHC Hop-by-Hop Options Header Support"
+ ---help---
+ 6LoWPAN IPv6 Hop-by-Hop option generic header compression according
+ to RFC7400.
+
+config 6LOWPAN_GHC_UDP
+ tristate "GHC UDP Support"
+ ---help---
+ 6LoWPAN IPv6 UDP generic header compression according to RFC7400.
+
+config 6LOWPAN_GHC_ICMPV6
+ tristate "GHC ICMPv6 Support"
+ ---help---
+ 6LoWPAN IPv6 ICMPv6 generic header compression according to RFC7400.
+
+config 6LOWPAN_GHC_EXT_HDR_DEST
+ tristate "GHC Destination Options Header Support"
+ ---help---
+ 6LoWPAN IPv6 destination option generic header compression according
+ to RFC7400.
+
+config 6LOWPAN_GHC_EXT_HDR_FRAG
+ tristate "GHC Fragmentation Options Header Support"
+ ---help---
+ 6LoWPAN IPv6 fragmentation option generic header compression
+ according to RFC7400.
+
+config 6LOWPAN_GHC_EXT_HDR_ROUTE
+ tristate "GHC Routing Options Header Support"
+ ---help---
+ 6LoWPAN IPv6 routing option generic header compression according
+ to RFC7400.
+
endif
diff --git a/net/6lowpan/Makefile b/net/6lowpan/Makefile
index c6ffc55ee0d7..e44f3bf2dd42 100644
--- a/net/6lowpan/Makefile
+++ b/net/6lowpan/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_6LOWPAN) += 6lowpan.o
6lowpan-y := core.o iphc.o nhc.o
+6lowpan-$(CONFIG_6LOWPAN_DEBUGFS) += debugfs.o
#rfc6282 nhcs
obj-$(CONFIG_6LOWPAN_NHC_DEST) += nhc_dest.o
@@ -10,3 +11,11 @@ obj-$(CONFIG_6LOWPAN_NHC_IPV6) += nhc_ipv6.o
obj-$(CONFIG_6LOWPAN_NHC_MOBILITY) += nhc_mobility.o
obj-$(CONFIG_6LOWPAN_NHC_ROUTING) += nhc_routing.o
obj-$(CONFIG_6LOWPAN_NHC_UDP) += nhc_udp.o
+
+#rfc7400 ghcs
+obj-$(CONFIG_6LOWPAN_GHC_EXT_HDR_HOP) += nhc_ghc_ext_hop.o
+obj-$(CONFIG_6LOWPAN_GHC_UDP) += nhc_ghc_udp.o
+obj-$(CONFIG_6LOWPAN_GHC_ICMPV6) += nhc_ghc_icmpv6.o
+obj-$(CONFIG_6LOWPAN_GHC_EXT_HDR_DEST) += nhc_ghc_ext_dest.o
+obj-$(CONFIG_6LOWPAN_GHC_EXT_HDR_FRAG) += nhc_ghc_ext_frag.o
+obj-$(CONFIG_6LOWPAN_GHC_EXT_HDR_ROUTE) += nhc_ghc_ext_route.o
diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c
index 83b19e072224..faf65baed617 100644
--- a/net/6lowpan/core.c
+++ b/net/6lowpan/core.c
@@ -15,19 +15,67 @@
#include <net/6lowpan.h>
-void lowpan_netdev_setup(struct net_device *dev, enum lowpan_lltypes lltype)
+#include "6lowpan_i.h"
+
+int lowpan_register_netdevice(struct net_device *dev,
+ enum lowpan_lltypes lltype)
{
+ int ret;
+
dev->addr_len = EUI64_ADDR_LEN;
dev->type = ARPHRD_6LOWPAN;
dev->mtu = IPV6_MIN_MTU;
dev->priv_flags |= IFF_NO_QUEUE;
lowpan_priv(dev)->lltype = lltype;
+
+ ret = register_netdevice(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = lowpan_dev_debugfs_init(dev);
+ if (ret < 0)
+ unregister_netdevice(dev);
+
+ return ret;
}
-EXPORT_SYMBOL(lowpan_netdev_setup);
+EXPORT_SYMBOL(lowpan_register_netdevice);
+
+int lowpan_register_netdev(struct net_device *dev,
+ enum lowpan_lltypes lltype)
+{
+ int ret;
+
+ rtnl_lock();
+ ret = lowpan_register_netdevice(dev, lltype);
+ rtnl_unlock();
+ return ret;
+}
+EXPORT_SYMBOL(lowpan_register_netdev);
+
+void lowpan_unregister_netdevice(struct net_device *dev)
+{
+ unregister_netdevice(dev);
+ lowpan_dev_debugfs_exit(dev);
+}
+EXPORT_SYMBOL(lowpan_unregister_netdevice);
+
+void lowpan_unregister_netdev(struct net_device *dev)
+{
+ rtnl_lock();
+ lowpan_unregister_netdevice(dev);
+ rtnl_unlock();
+}
+EXPORT_SYMBOL(lowpan_unregister_netdev);
static int __init lowpan_module_init(void)
{
+ int ret;
+
+ ret = lowpan_debugfs_init();
+ if (ret < 0)
+ return ret;
+
request_module_nowait("ipv6");
request_module_nowait("nhc_dest");
@@ -40,6 +88,13 @@ static int __init lowpan_module_init(void)
return 0;
}
+
+static void __exit lowpan_module_exit(void)
+{
+ lowpan_debugfs_exit();
+}
+
module_init(lowpan_module_init);
+module_exit(lowpan_module_exit);
MODULE_LICENSE("GPL");
diff --git a/net/6lowpan/debugfs.c b/net/6lowpan/debugfs.c
new file mode 100644
index 000000000000..88eef84df0fc
--- /dev/null
+++ b/net/6lowpan/debugfs.c
@@ -0,0 +1,53 @@
+/* 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.
+ *
+ * Authors:
+ * (C) 2015 Pengutronix, Alexander Aring <aar@pengutronix.de>
+ * Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
+ */
+
+#include <net/6lowpan.h>
+
+#include "6lowpan_i.h"
+
+static struct dentry *lowpan_debugfs;
+
+int lowpan_dev_debugfs_init(struct net_device *dev)
+{
+ struct lowpan_priv *lpriv = lowpan_priv(dev);
+
+ /* creating the root */
+ lpriv->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs);
+ if (!lpriv->iface_debugfs)
+ goto fail;
+
+ return 0;
+
+fail:
+ return -EINVAL;
+}
+
+void lowpan_dev_debugfs_exit(struct net_device *dev)
+{
+ debugfs_remove_recursive(lowpan_priv(dev)->iface_debugfs);
+}
+
+int __init lowpan_debugfs_init(void)
+{
+ lowpan_debugfs = debugfs_create_dir("6lowpan", NULL);
+ if (!lowpan_debugfs)
+ return -EINVAL;
+
+ return 0;
+}
+
+void lowpan_debugfs_exit(void)
+{
+ debugfs_remove_recursive(lowpan_debugfs);
+}
diff --git a/net/6lowpan/nhc_ghc_ext_dest.c b/net/6lowpan/nhc_ghc_ext_dest.c
new file mode 100644
index 000000000000..9887b3a15348
--- /dev/null
+++ b/net/6lowpan/nhc_ghc_ext_dest.c
@@ -0,0 +1,27 @@
+/*
+ * 6LoWPAN Extension Header compression according to RFC7400
+ *
+ * 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 "nhc.h"
+
+#define LOWPAN_GHC_EXT_DEST_IDLEN 1
+#define LOWPAN_GHC_EXT_DEST_ID_0 0xb6
+#define LOWPAN_GHC_EXT_DEST_MASK_0 0xfe
+
+static void dest_ghid_setup(struct lowpan_nhc *nhc)
+{
+ nhc->id[0] = LOWPAN_GHC_EXT_DEST_ID_0;
+ nhc->idmask[0] = LOWPAN_GHC_EXT_DEST_MASK_0;
+}
+
+LOWPAN_NHC(ghc_ext_dest, "RFC7400 Destination Extension Header", NEXTHDR_DEST,
+ 0, dest_ghid_setup, LOWPAN_GHC_EXT_DEST_IDLEN, NULL, NULL);
+
+module_lowpan_nhc(ghc_ext_dest);
+MODULE_DESCRIPTION("6LoWPAN generic header destination extension compression");
+MODULE_LICENSE("GPL");
diff --git a/net/6lowpan/nhc_ghc_ext_frag.c b/net/6lowpan/nhc_ghc_ext_frag.c
new file mode 100644
index 000000000000..1308b79e939d
--- /dev/null
+++ b/net/6lowpan/nhc_ghc_ext_frag.c
@@ -0,0 +1,28 @@
+/*
+ * 6LoWPAN Extension Header compression according to RFC7400
+ *
+ * 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 "nhc.h"
+
+#define LOWPAN_GHC_EXT_FRAG_IDLEN 1
+#define LOWPAN_GHC_EXT_FRAG_ID_0 0xb4
+#define LOWPAN_GHC_EXT_FRAG_MASK_0 0xfe
+
+static void frag_ghid_setup(struct lowpan_nhc *nhc)
+{
+ nhc->id[0] = LOWPAN_GHC_EXT_FRAG_ID_0;
+ nhc->idmask[0] = LOWPAN_GHC_EXT_FRAG_MASK_0;
+}
+
+LOWPAN_NHC(ghc_ext_frag, "RFC7400 Fragmentation Extension Header",
+ NEXTHDR_FRAGMENT, 0, frag_ghid_setup,
+ LOWPAN_GHC_EXT_FRAG_IDLEN, NULL, NULL);
+
+module_lowpan_nhc(ghc_ext_frag);
+MODULE_DESCRIPTION("6LoWPAN generic header fragmentation extension compression");
+MODULE_LICENSE("GPL");
diff --git a/net/6lowpan/nhc_ghc_ext_hop.c b/net/6lowpan/nhc_ghc_ext_hop.c
new file mode 100644
index 000000000000..baec86fd1974
--- /dev/null
+++ b/net/6lowpan/nhc_ghc_ext_hop.c
@@ -0,0 +1,27 @@
+/*
+ * 6LoWPAN Extension Header compression according to RFC7400
+ *
+ * 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 "nhc.h"
+
+#define LOWPAN_GHC_EXT_HOP_IDLEN 1
+#define LOWPAN_GHC_EXT_HOP_ID_0 0xb0
+#define LOWPAN_GHC_EXT_HOP_MASK_0 0xfe
+
+static void hop_ghid_setup(struct lowpan_nhc *nhc)
+{
+ nhc->id[0] = LOWPAN_GHC_EXT_HOP_ID_0;
+ nhc->idmask[0] = LOWPAN_GHC_EXT_HOP_MASK_0;
+}
+
+LOWPAN_NHC(ghc_ext_hop, "RFC7400 Hop-by-Hop Extension Header", NEXTHDR_HOP, 0,
+ hop_ghid_setup, LOWPAN_GHC_EXT_HOP_IDLEN, NULL, NULL);
+
+module_lowpan_nhc(ghc_ext_hop);
+MODULE_DESCRIPTION("6LoWPAN generic header hop-by-hop extension compression");
+MODULE_LICENSE("GPL");
diff --git a/net/6lowpan/nhc_ghc_ext_route.c b/net/6lowpan/nhc_ghc_ext_route.c
new file mode 100644
index 000000000000..d7e5bd791c62
--- /dev/null
+++ b/net/6lowpan/nhc_ghc_ext_route.c
@@ -0,0 +1,27 @@
+/*
+ * 6LoWPAN Extension Header compression according to RFC7400
+ *
+ * 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 "nhc.h"
+
+#define LOWPAN_GHC_EXT_ROUTE_IDLEN 1
+#define LOWPAN_GHC_EXT_ROUTE_ID_0 0xb2
+#define LOWPAN_GHC_EXT_ROUTE_MASK_0 0xfe
+
+static void route_ghid_setup(struct lowpan_nhc *nhc)
+{
+ nhc->id[0] = LOWPAN_GHC_EXT_ROUTE_ID_0;
+ nhc->idmask[0] = LOWPAN_GHC_EXT_ROUTE_MASK_0;
+}
+
+LOWPAN_NHC(ghc_ext_route, "RFC7400 Routing Extension Header", NEXTHDR_ROUTING,
+ 0, route_ghid_setup, LOWPAN_GHC_EXT_ROUTE_IDLEN, NULL, NULL);
+
+module_lowpan_nhc(ghc_ext_route);
+MODULE_DESCRIPTION("6LoWPAN generic header routing extension compression");
+MODULE_LICENSE("GPL");
diff --git a/net/6lowpan/nhc_ghc_icmpv6.c b/net/6lowpan/nhc_ghc_icmpv6.c
new file mode 100644
index 000000000000..32e7c2c66bbc
--- /dev/null
+++ b/net/6lowpan/nhc_ghc_icmpv6.c
@@ -0,0 +1,27 @@
+/*
+ * 6LoWPAN ICMPv6 compression according to RFC7400
+ *
+ * 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 "nhc.h"
+
+#define LOWPAN_GHC_ICMPV6_IDLEN 1
+#define LOWPAN_GHC_ICMPV6_ID_0 0xdf
+#define LOWPAN_GHC_ICMPV6_MASK_0 0xff
+
+static void icmpv6_ghid_setup(struct lowpan_nhc *nhc)
+{
+ nhc->id[0] = LOWPAN_GHC_ICMPV6_ID_0;
+ nhc->idmask[0] = LOWPAN_GHC_ICMPV6_MASK_0;
+}
+
+LOWPAN_NHC(ghc_icmpv6, "RFC7400 ICMPv6", NEXTHDR_ICMP, 0,
+ icmpv6_ghid_setup, LOWPAN_GHC_ICMPV6_IDLEN, NULL, NULL);
+
+module_lowpan_nhc(ghc_icmpv6);
+MODULE_DESCRIPTION("6LoWPAN generic header ICMPv6 compression");
+MODULE_LICENSE("GPL");
diff --git a/net/6lowpan/nhc_ghc_udp.c b/net/6lowpan/nhc_ghc_udp.c
new file mode 100644
index 000000000000..17beefa52ca8
--- /dev/null
+++ b/net/6lowpan/nhc_ghc_udp.c
@@ -0,0 +1,27 @@
+/*
+ * 6LoWPAN UDP compression according to RFC7400
+ *
+ * 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 "nhc.h"
+
+#define LOWPAN_GHC_UDP_IDLEN 1
+#define LOWPAN_GHC_UDP_ID_0 0xd0
+#define LOWPAN_GHC_UDP_MASK_0 0xf8
+
+static void udp_ghid_setup(struct lowpan_nhc *nhc)
+{
+ nhc->id[0] = LOWPAN_GHC_UDP_ID_0;
+ nhc->idmask[0] = LOWPAN_GHC_UDP_MASK_0;
+}
+
+LOWPAN_NHC(ghc_udp, "RFC7400 UDP", NEXTHDR_UDP, 0,
+ udp_ghid_setup, LOWPAN_GHC_UDP_IDLEN, NULL, NULL);
+
+module_lowpan_nhc(ghc_udp);
+MODULE_DESCRIPTION("6LoWPAN generic header UDP compression");
+MODULE_LICENSE("GPL");
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index fded86508117..ad5e2fd1012c 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -30,6 +30,7 @@
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <net/arp.h>
+#include <net/switchdev.h>
#include "vlan.h"
#include "vlanproc.h"
@@ -542,9 +543,9 @@ static int vlan_dev_init(struct net_device *dev)
(1<<__LINK_STATE_DORMANT))) |
(1<<__LINK_STATE_PRESENT);
- dev->hw_features = NETIF_F_ALL_CSUM | NETIF_F_SG |
+ dev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG |
NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE |
- NETIF_F_HIGHDMA | NETIF_F_SCTP_CSUM |
+ NETIF_F_HIGHDMA | NETIF_F_SCTP_CRC |
NETIF_F_ALL_FCOE;
dev->features |= real_dev->vlan_features | NETIF_F_LLTX |
@@ -774,6 +775,12 @@ static const struct net_device_ops vlan_netdev_ops = {
.ndo_netpoll_cleanup = vlan_dev_netpoll_cleanup,
#endif
.ndo_fix_features = vlan_dev_fix_features,
+ .ndo_fdb_add = switchdev_port_fdb_add,
+ .ndo_fdb_del = switchdev_port_fdb_del,
+ .ndo_fdb_dump = switchdev_port_fdb_dump,
+ .ndo_bridge_setlink = switchdev_port_bridge_setlink,
+ .ndo_bridge_getlink = switchdev_port_bridge_getlink,
+ .ndo_bridge_dellink = switchdev_port_bridge_dellink,
.ndo_get_lock_subclass = vlan_dev_get_lock_subclass,
.ndo_get_iflink = vlan_dev_get_iflink,
};
diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c
index 6e70ddb158b4..199bc76202d2 100644
--- a/net/9p/trans_virtio.c
+++ b/net/9p/trans_virtio.c
@@ -105,7 +105,7 @@ static struct list_head virtio_chan_list;
/* How many bytes left in this page. */
static unsigned int rest_of_page(void *data)
{
- return PAGE_SIZE - ((unsigned long)data % PAGE_SIZE);
+ return PAGE_SIZE - offset_in_page(data);
}
/**
@@ -143,7 +143,6 @@ static void p9_virtio_close(struct p9_client *client)
static void req_done(struct virtqueue *vq)
{
struct virtio_chan *chan = vq->vdev->priv;
- struct p9_fcall *rc;
unsigned int len;
struct p9_req_t *req;
unsigned long flags;
@@ -152,8 +151,8 @@ static void req_done(struct virtqueue *vq)
while (1) {
spin_lock_irqsave(&chan->lock, flags);
- rc = virtqueue_get_buf(chan->vq, &len);
- if (rc == NULL) {
+ req = virtqueue_get_buf(chan->vq, &len);
+ if (req == NULL) {
spin_unlock_irqrestore(&chan->lock, flags);
break;
}
@@ -161,9 +160,6 @@ static void req_done(struct virtqueue *vq)
spin_unlock_irqrestore(&chan->lock, flags);
/* Wakeup if anyone waiting for VirtIO ring space. */
wake_up(chan->vc_wq);
- p9_debug(P9_DEBUG_TRANS, ": rc %p\n", rc);
- p9_debug(P9_DEBUG_TRANS, ": lookup tag %d\n", rc->tag);
- req = p9_tag_lookup(chan->client, rc->tag);
p9_client_cb(chan->client, req, REQ_STATUS_RCVD);
}
}
@@ -284,7 +280,7 @@ req_retry:
if (in)
sgs[out_sgs + in_sgs++] = chan->sg + out;
- err = virtqueue_add_sgs(chan->vq, sgs, out_sgs, in_sgs, req->tc,
+ err = virtqueue_add_sgs(chan->vq, sgs, out_sgs, in_sgs, req,
GFP_ATOMIC);
if (err < 0) {
if (err == -ENOSPC) {
@@ -369,7 +365,7 @@ static int p9_get_mapped_pages(struct virtio_chan *chan,
return -ENOMEM;
*need_drop = 0;
- p -= (*offs = (unsigned long)p % PAGE_SIZE);
+ p -= (*offs = offset_in_page(p));
for (index = 0; index < nr_pages; index++) {
if (is_vmalloc_addr(p))
(*pages)[index] = vmalloc_to_page(p);
@@ -469,7 +465,7 @@ req_retry_pinned:
}
BUG_ON(out_sgs + in_sgs > ARRAY_SIZE(sgs));
- err = virtqueue_add_sgs(chan->vq, sgs, out_sgs, in_sgs, req->tc,
+ err = virtqueue_add_sgs(chan->vq, sgs, out_sgs, in_sgs, req,
GFP_ATOMIC);
if (err < 0) {
if (err == -ENOSPC) {
diff --git a/net/Kconfig b/net/Kconfig
index 127da94ae25e..174354618f8a 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -48,6 +48,9 @@ config COMPAT_NETLINK_MESSAGES
config NET_INGRESS
bool
+config NET_EGRESS
+ bool
+
menu "Networking options"
source "net/packet/Kconfig"
@@ -250,9 +253,14 @@ config XPS
depends on SMP
default y
+config SOCK_CGROUP_DATA
+ bool
+ default n
+
config CGROUP_NET_PRIO
bool "Network priority cgroup"
depends on CGROUPS
+ select SOCK_CGROUP_DATA
---help---
Cgroup subsystem for use in assigning processes to network priorities on
a per-interface basis.
@@ -260,6 +268,7 @@ config CGROUP_NET_PRIO
config CGROUP_NET_CLASSID
bool "Network classid cgroup"
depends on CGROUPS
+ select SOCK_CGROUP_DATA
---help---
Cgroup subsystem for use as general purpose socket classid marker that is
being used in cls_cgroup and for netfilter matching.
diff --git a/net/atm/common.c b/net/atm/common.c
index 49a872db7e42..6dc12305799e 100644
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -96,7 +96,7 @@ static void vcc_def_wakeup(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up(&wq->wait);
rcu_read_unlock();
}
@@ -117,7 +117,7 @@ static void vcc_write_space(struct sock *sk)
if (vcc_writable(sk)) {
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible(&wq->wait);
sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
diff --git a/net/atm/mpc.h b/net/atm/mpc.h
index 0919a88bbc70..cfc7b745aa91 100644
--- a/net/atm/mpc.h
+++ b/net/atm/mpc.h
@@ -21,11 +21,11 @@ struct mpoa_client {
uint8_t our_ctrl_addr[ATM_ESA_LEN]; /* MPC's control ATM address */
rwlock_t ingress_lock;
- struct in_cache_ops *in_ops; /* ingress cache operations */
+ const struct in_cache_ops *in_ops; /* ingress cache operations */
in_cache_entry *in_cache; /* the ingress cache of this MPC */
rwlock_t egress_lock;
- struct eg_cache_ops *eg_ops; /* egress cache operations */
+ const struct eg_cache_ops *eg_ops; /* egress cache operations */
eg_cache_entry *eg_cache; /* the egress cache of this MPC */
uint8_t *mps_macs; /* array of MPS MAC addresses, >=1 */
diff --git a/net/atm/mpoa_caches.c b/net/atm/mpoa_caches.c
index d1b2d9a03144..9e60e74c807d 100644
--- a/net/atm/mpoa_caches.c
+++ b/net/atm/mpoa_caches.c
@@ -534,7 +534,7 @@ static void eg_destroy_cache(struct mpoa_client *mpc)
}
-static struct in_cache_ops ingress_ops = {
+static const struct in_cache_ops ingress_ops = {
in_cache_add_entry, /* add_entry */
in_cache_get, /* get */
in_cache_get_with_mask, /* get_with_mask */
@@ -548,7 +548,7 @@ static struct in_cache_ops ingress_ops = {
in_destroy_cache /* destroy_cache */
};
-static struct eg_cache_ops egress_ops = {
+static const struct eg_cache_ops egress_ops = {
eg_cache_add_entry, /* add_entry */
eg_cache_get_by_cache_id, /* get_by_cache_id */
eg_cache_get_by_tag, /* get_by_tag */
diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c
index ae3a47f9d1d5..fbd0acf80b13 100644
--- a/net/ax25/af_ax25.c
+++ b/net/ax25/af_ax25.c
@@ -805,6 +805,9 @@ static int ax25_create(struct net *net, struct socket *sock, int protocol,
struct sock *sk;
ax25_cb *ax25;
+ if (protocol < 0 || protocol > SK_PROTOCOL_MAX)
+ return -EINVAL;
+
if (!net_eq(net, &init_net))
return -EAFNOSUPPORT;
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index 912d9c36fb1c..df625de55ef2 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -185,7 +185,8 @@ unlock:
static int batadv_iv_ogm_orig_del_if(struct batadv_orig_node *orig_node,
int max_if_num, int del_if_num)
{
- int chunk_size, ret = -ENOMEM, if_offset;
+ int ret = -ENOMEM;
+ size_t chunk_size, if_offset;
void *data_ptr = NULL;
spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock);
@@ -203,8 +204,9 @@ static int batadv_iv_ogm_orig_del_if(struct batadv_orig_node *orig_node,
memcpy(data_ptr, orig_node->bat_iv.bcast_own, del_if_num * chunk_size);
/* copy second part */
+ if_offset = (del_if_num + 1) * chunk_size;
memcpy((char *)data_ptr + del_if_num * chunk_size,
- orig_node->bat_iv.bcast_own + ((del_if_num + 1) * chunk_size),
+ (uint8_t *)orig_node->bat_iv.bcast_own + if_offset,
(max_if_num - del_if_num) * chunk_size);
free_bcast_own:
@@ -361,7 +363,6 @@ batadv_iv_ogm_primary_iface_set(struct batadv_hard_iface *hard_iface)
unsigned char *ogm_buff = hard_iface->bat_iv.ogm_buff;
batadv_ogm_packet = (struct batadv_ogm_packet *)ogm_buff;
- batadv_ogm_packet->flags = BATADV_PRIMARIES_FIRST_HOP;
batadv_ogm_packet->ttl = BATADV_TTL;
}
@@ -842,8 +843,6 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node,
"Forwarding packet: tq: %i, ttl: %i\n",
batadv_ogm_packet->tq, batadv_ogm_packet->ttl);
- /* switch of primaries first hop flag when forwarding */
- batadv_ogm_packet->flags &= ~BATADV_PRIMARIES_FIRST_HOP;
if (is_single_hop_neigh)
batadv_ogm_packet->flags |= BATADV_DIRECTLINK;
else
@@ -1379,6 +1378,7 @@ batadv_iv_ogm_process_per_outif(const struct sk_buff *skb, int ogm_offset,
struct batadv_hard_iface *if_outgoing)
{
struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
+ struct batadv_hardif_neigh_node *hardif_neigh = NULL;
struct batadv_neigh_node *router = NULL;
struct batadv_neigh_node *router_router = NULL;
struct batadv_orig_node *orig_neigh_node;
@@ -1423,6 +1423,13 @@ batadv_iv_ogm_process_per_outif(const struct sk_buff *skb, int ogm_offset,
goto out;
}
+ if (is_single_hop_neigh) {
+ hardif_neigh = batadv_hardif_neigh_get(if_incoming,
+ ethhdr->h_source);
+ if (hardif_neigh)
+ hardif_neigh->last_seen = jiffies;
+ }
+
router = batadv_orig_router_get(orig_node, if_outgoing);
if (router) {
router_router = batadv_orig_router_get(router->orig_node,
@@ -1557,6 +1564,8 @@ out:
batadv_neigh_node_free_ref(router_router);
if (orig_neigh_router)
batadv_neigh_node_free_ref(orig_neigh_router);
+ if (hardif_neigh)
+ batadv_hardif_neigh_free_ref(hardif_neigh);
kfree_skb(skb_priv);
}
@@ -1862,6 +1871,58 @@ next:
}
/**
+ * batadv_iv_hardif_neigh_print - print a single hop neighbour node
+ * @seq: neighbour table seq_file struct
+ * @hardif_neigh: hardif neighbour information
+ */
+static void
+batadv_iv_hardif_neigh_print(struct seq_file *seq,
+ struct batadv_hardif_neigh_node *hardif_neigh)
+{
+ int last_secs, last_msecs;
+
+ last_secs = jiffies_to_msecs(jiffies - hardif_neigh->last_seen) / 1000;
+ last_msecs = jiffies_to_msecs(jiffies - hardif_neigh->last_seen) % 1000;
+
+ seq_printf(seq, " %10s %pM %4i.%03is\n",
+ hardif_neigh->if_incoming->net_dev->name,
+ hardif_neigh->addr, last_secs, last_msecs);
+}
+
+/**
+ * batadv_iv_ogm_neigh_print - print the single hop neighbour list
+ * @bat_priv: the bat priv with all the soft interface information
+ * @seq: neighbour table seq_file struct
+ */
+static void batadv_iv_neigh_print(struct batadv_priv *bat_priv,
+ struct seq_file *seq)
+{
+ struct net_device *net_dev = (struct net_device *)seq->private;
+ struct batadv_hardif_neigh_node *hardif_neigh;
+ struct batadv_hard_iface *hard_iface;
+ int batman_count = 0;
+
+ seq_printf(seq, " %10s %-13s %s\n",
+ "IF", "Neighbor", "last-seen");
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
+ if (hard_iface->soft_iface != net_dev)
+ continue;
+
+ hlist_for_each_entry_rcu(hardif_neigh,
+ &hard_iface->neigh_list, list) {
+ batadv_iv_hardif_neigh_print(seq, hardif_neigh);
+ batman_count++;
+ }
+ }
+ rcu_read_unlock();
+
+ if (batman_count == 0)
+ seq_puts(seq, "No batman nodes in range ...\n");
+}
+
+/**
* batadv_iv_ogm_neigh_cmp - compare the metrics of two neighbors
* @neigh1: the first neighbor object of the comparison
* @if_outgoing1: outgoing interface for the first neighbor
@@ -1902,8 +1963,8 @@ out:
}
/**
- * batadv_iv_ogm_neigh_is_eob - check if neigh1 is equally good or better than
- * neigh2 from the metric prospective
+ * batadv_iv_ogm_neigh_is_sob - check if neigh1 is similarly good or better
+ * than neigh2 from the metric prospective
* @neigh1: the first neighbor object of the comparison
* @if_outgoing1: outgoing interface for the first neighbor
* @neigh2: the second neighbor object of the comparison
@@ -1913,7 +1974,7 @@ out:
* the metric via neigh2, false otherwise.
*/
static bool
-batadv_iv_ogm_neigh_is_eob(struct batadv_neigh_node *neigh1,
+batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1,
struct batadv_hard_iface *if_outgoing1,
struct batadv_neigh_node *neigh2,
struct batadv_hard_iface *if_outgoing2)
@@ -1953,7 +2014,8 @@ static struct batadv_algo_ops batadv_batman_iv __read_mostly = {
.bat_ogm_schedule = batadv_iv_ogm_schedule,
.bat_ogm_emit = batadv_iv_ogm_emit,
.bat_neigh_cmp = batadv_iv_ogm_neigh_cmp,
- .bat_neigh_is_equiv_or_better = batadv_iv_ogm_neigh_is_eob,
+ .bat_neigh_is_similar_or_better = batadv_iv_ogm_neigh_is_sob,
+ .bat_neigh_print = batadv_iv_neigh_print,
.bat_orig_print = batadv_iv_ogm_orig_print,
.bat_orig_free = batadv_iv_ogm_orig_free,
.bat_orig_add_if = batadv_iv_ogm_orig_add_if,
diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
index 191a70290dca..d5d71ac96c8a 100644
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -260,7 +260,9 @@ batadv_bla_del_backbone_claims(struct batadv_bla_backbone_gw *backbone_gw)
}
/* all claims gone, initialize CRC */
+ spin_lock_bh(&backbone_gw->crc_lock);
backbone_gw->crc = BATADV_BLA_CRC_INIT;
+ spin_unlock_bh(&backbone_gw->crc_lock);
}
/**
@@ -408,6 +410,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, u8 *orig,
entry->lasttime = jiffies;
entry->crc = BATADV_BLA_CRC_INIT;
entry->bat_priv = bat_priv;
+ spin_lock_init(&entry->crc_lock);
atomic_set(&entry->request_sent, 0);
atomic_set(&entry->wait_periods, 0);
ether_addr_copy(entry->orig, orig);
@@ -557,7 +560,9 @@ static void batadv_bla_send_announce(struct batadv_priv *bat_priv,
__be16 crc;
memcpy(mac, batadv_announce_mac, 4);
+ spin_lock_bh(&backbone_gw->crc_lock);
crc = htons(backbone_gw->crc);
+ spin_unlock_bh(&backbone_gw->crc_lock);
memcpy(&mac[4], &crc, 2);
batadv_bla_send_claim(bat_priv, mac, backbone_gw->vid,
@@ -618,14 +623,18 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
"bla_add_claim(): changing ownership for %pM, vid %d\n",
mac, BATADV_PRINT_VID(vid));
+ spin_lock_bh(&claim->backbone_gw->crc_lock);
claim->backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+ spin_unlock_bh(&claim->backbone_gw->crc_lock);
batadv_backbone_gw_free_ref(claim->backbone_gw);
}
/* set (new) backbone gw */
atomic_inc(&backbone_gw->refcount);
claim->backbone_gw = backbone_gw;
+ spin_lock_bh(&backbone_gw->crc_lock);
backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+ spin_unlock_bh(&backbone_gw->crc_lock);
backbone_gw->lasttime = jiffies;
claim_free_ref:
@@ -653,7 +662,9 @@ static void batadv_bla_del_claim(struct batadv_priv *bat_priv,
batadv_choose_claim, claim);
batadv_claim_free_ref(claim); /* reference from the hash is gone */
+ spin_lock_bh(&claim->backbone_gw->crc_lock);
claim->backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+ spin_unlock_bh(&claim->backbone_gw->crc_lock);
/* don't need the reference from hash_find() anymore */
batadv_claim_free_ref(claim);
@@ -664,7 +675,7 @@ static int batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr,
u8 *backbone_addr, unsigned short vid)
{
struct batadv_bla_backbone_gw *backbone_gw;
- u16 crc;
+ u16 backbone_crc, crc;
if (memcmp(an_addr, batadv_announce_mac, 4) != 0)
return 0;
@@ -683,12 +694,16 @@ static int batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr,
"handle_announce(): ANNOUNCE vid %d (sent by %pM)... CRC = %#.4x\n",
BATADV_PRINT_VID(vid), backbone_gw->orig, crc);
- if (backbone_gw->crc != crc) {
+ spin_lock_bh(&backbone_gw->crc_lock);
+ backbone_crc = backbone_gw->crc;
+ spin_unlock_bh(&backbone_gw->crc_lock);
+
+ if (backbone_crc != crc) {
batadv_dbg(BATADV_DBG_BLA, backbone_gw->bat_priv,
"handle_announce(): CRC FAILED for %pM/%d (my = %#.4x, sent = %#.4x)\n",
backbone_gw->orig,
BATADV_PRINT_VID(backbone_gw->vid),
- backbone_gw->crc, crc);
+ backbone_crc, crc);
batadv_bla_send_request(backbone_gw);
} else {
@@ -1153,6 +1168,26 @@ void batadv_bla_update_orig_address(struct batadv_priv *bat_priv,
}
}
+/**
+ * batadv_bla_status_update - purge bla interfaces if necessary
+ * @net_dev: the soft interface net device
+ */
+void batadv_bla_status_update(struct net_device *net_dev)
+{
+ struct batadv_priv *bat_priv = netdev_priv(net_dev);
+ struct batadv_hard_iface *primary_if;
+
+ primary_if = batadv_primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ return;
+
+ /* this function already purges everything when bla is disabled,
+ * so just call that one.
+ */
+ batadv_bla_update_orig_address(bat_priv, primary_if, primary_if);
+ batadv_hardif_free_ref(primary_if);
+}
+
/* periodic work to do:
* * purge structures when they are too old
* * send announcements
@@ -1647,6 +1682,7 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset)
struct batadv_bla_claim *claim;
struct batadv_hard_iface *primary_if;
struct hlist_head *head;
+ u16 backbone_crc;
u32 i;
bool is_own;
u8 *primary_addr;
@@ -1669,11 +1705,15 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset)
hlist_for_each_entry_rcu(claim, head, hash_entry) {
is_own = batadv_compare_eth(claim->backbone_gw->orig,
primary_addr);
+
+ spin_lock_bh(&claim->backbone_gw->crc_lock);
+ backbone_crc = claim->backbone_gw->crc;
+ spin_unlock_bh(&claim->backbone_gw->crc_lock);
seq_printf(seq, " * %pM on %5d by %pM [%c] (%#.4x)\n",
claim->addr, BATADV_PRINT_VID(claim->vid),
claim->backbone_gw->orig,
(is_own ? 'x' : ' '),
- claim->backbone_gw->crc);
+ backbone_crc);
}
rcu_read_unlock();
}
@@ -1692,6 +1732,7 @@ int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset)
struct batadv_hard_iface *primary_if;
struct hlist_head *head;
int secs, msecs;
+ u16 backbone_crc;
u32 i;
bool is_own;
u8 *primary_addr;
@@ -1722,10 +1763,14 @@ int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset)
if (is_own)
continue;
+ spin_lock_bh(&backbone_gw->crc_lock);
+ backbone_crc = backbone_gw->crc;
+ spin_unlock_bh(&backbone_gw->crc_lock);
+
seq_printf(seq, " * %pM on %5d %4i.%03is (%#.4x)\n",
backbone_gw->orig,
BATADV_PRINT_VID(backbone_gw->vid), secs,
- msecs, backbone_gw->crc);
+ msecs, backbone_crc);
}
rcu_read_unlock();
}
diff --git a/net/batman-adv/bridge_loop_avoidance.h b/net/batman-adv/bridge_loop_avoidance.h
index 025152b34282..7ea199b8b5ab 100644
--- a/net/batman-adv/bridge_loop_avoidance.h
+++ b/net/batman-adv/bridge_loop_avoidance.h
@@ -22,6 +22,7 @@
#include <linux/types.h>
+struct net_device;
struct seq_file;
struct sk_buff;
@@ -42,6 +43,7 @@ int batadv_bla_check_bcast_duplist(struct batadv_priv *bat_priv,
void batadv_bla_update_orig_address(struct batadv_priv *bat_priv,
struct batadv_hard_iface *primary_if,
struct batadv_hard_iface *oldif);
+void batadv_bla_status_update(struct net_device *net_dev);
int batadv_bla_init(struct batadv_priv *bat_priv);
void batadv_bla_free(struct batadv_priv *bat_priv);
diff --git a/net/batman-adv/debugfs.c b/net/batman-adv/debugfs.c
index c4c1e8030ba0..037ad0a5f485 100644
--- a/net/batman-adv/debugfs.c
+++ b/net/batman-adv/debugfs.c
@@ -262,6 +262,13 @@ static int batadv_algorithms_open(struct inode *inode, struct file *file)
return single_open(file, batadv_algo_seq_print_text, NULL);
}
+static int neighbors_open(struct inode *inode, struct file *file)
+{
+ struct net_device *net_dev = (struct net_device *)inode->i_private;
+
+ return single_open(file, batadv_hardif_neigh_seq_print_text, net_dev);
+}
+
static int batadv_originators_open(struct inode *inode, struct file *file)
{
struct net_device *net_dev = (struct net_device *)inode->i_private;
@@ -375,6 +382,7 @@ static struct batadv_debuginfo *batadv_general_debuginfos[] = {
};
/* The following attributes are per soft interface */
+static BATADV_DEBUGINFO(neighbors, S_IRUGO, neighbors_open);
static BATADV_DEBUGINFO(originators, S_IRUGO, batadv_originators_open);
static BATADV_DEBUGINFO(gateways, S_IRUGO, batadv_gateways_open);
static BATADV_DEBUGINFO(transtable_global, S_IRUGO,
@@ -394,6 +402,7 @@ static BATADV_DEBUGINFO(nc_nodes, S_IRUGO, batadv_nc_nodes_open);
#endif
static struct batadv_debuginfo *batadv_mesh_debuginfos[] = {
+ &batadv_debuginfo_neighbors,
&batadv_debuginfo_originators,
&batadv_debuginfo_gateways,
&batadv_debuginfo_transtable_global,
diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c
index 83bc1aaf5800..a49c705fb86b 100644
--- a/net/batman-adv/distributed-arp-table.c
+++ b/net/batman-adv/distributed-arp-table.c
@@ -566,6 +566,7 @@ batadv_dat_select_candidates(struct batadv_priv *bat_priv, __be32 ip_dst)
int select;
batadv_dat_addr_t last_max = BATADV_DAT_ADDR_MAX, ip_key;
struct batadv_dat_candidate *res;
+ struct batadv_dat_entry dat;
if (!bat_priv->orig_hash)
return NULL;
@@ -575,7 +576,9 @@ batadv_dat_select_candidates(struct batadv_priv *bat_priv, __be32 ip_dst)
if (!res)
return NULL;
- ip_key = (batadv_dat_addr_t)batadv_hash_dat(&ip_dst,
+ dat.ip = ip_dst;
+ dat.vid = 0;
+ ip_key = (batadv_dat_addr_t)batadv_hash_dat(&dat,
BATADV_DAT_ADDR_MAX);
batadv_dbg(BATADV_DBG_DAT, bat_priv,
diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
index 700c96c82a15..20d9282f895b 100644
--- a/net/batman-adv/fragmentation.c
+++ b/net/batman-adv/fragmentation.c
@@ -71,14 +71,14 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig_node,
for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) {
chain = &orig_node->fragments[i];
- spin_lock_bh(&orig_node->fragments[i].lock);
+ spin_lock_bh(&chain->lock);
if (!check_cb || check_cb(chain)) {
- batadv_frag_clear_chain(&orig_node->fragments[i].head);
- orig_node->fragments[i].size = 0;
+ batadv_frag_clear_chain(&chain->head);
+ chain->size = 0;
}
- spin_unlock_bh(&orig_node->fragments[i].lock);
+ spin_unlock_bh(&chain->lock);
}
}
diff --git a/net/batman-adv/gateway_common.c b/net/batman-adv/gateway_common.c
index 0cb5e6b6f6d4..b51bface8bdd 100644
--- a/net/batman-adv/gateway_common.c
+++ b/net/batman-adv/gateway_common.c
@@ -31,27 +31,23 @@
#include "packet.h"
/**
- * batadv_parse_gw_bandwidth - parse supplied string buffer to extract download
- * and upload bandwidth information
+ * batadv_parse_throughput - parse supplied string buffer to extract throughput
+ * information
* @net_dev: the soft interface net device
* @buff: string buffer to parse
- * @down: pointer holding the returned download bandwidth information
- * @up: pointer holding the returned upload bandwidth information
+ * @description: text shown when throughput string cannot be parsed
+ * @throughput: pointer holding the returned throughput information
*
* Returns false on parse error and true otherwise.
*/
-static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
- u32 *down, u32 *up)
+static bool batadv_parse_throughput(struct net_device *net_dev, char *buff,
+ const char *description, u32 *throughput)
{
enum batadv_bandwidth_units bw_unit_type = BATADV_BW_UNIT_KBIT;
- char *slash_ptr, *tmp_ptr;
- u64 ldown, lup;
+ u64 lthroughput;
+ char *tmp_ptr;
int ret;
- slash_ptr = strchr(buff, '/');
- if (slash_ptr)
- *slash_ptr = 0;
-
if (strlen(buff) > 4) {
tmp_ptr = buff + strlen(buff) - 4;
@@ -63,90 +59,75 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
*tmp_ptr = '\0';
}
- ret = kstrtou64(buff, 10, &ldown);
+ ret = kstrtou64(buff, 10, &lthroughput);
if (ret) {
batadv_err(net_dev,
- "Download speed of gateway mode invalid: %s\n",
- buff);
+ "Invalid throughput speed for %s: %s\n",
+ description, buff);
return false;
}
switch (bw_unit_type) {
case BATADV_BW_UNIT_MBIT:
/* prevent overflow */
- if (U64_MAX / 10 < ldown) {
+ if (U64_MAX / 10 < lthroughput) {
batadv_err(net_dev,
- "Download speed of gateway mode too large: %s\n",
- buff);
+ "Throughput speed for %s too large: %s\n",
+ description, buff);
return false;
}
- ldown *= 10;
+ lthroughput *= 10;
break;
case BATADV_BW_UNIT_KBIT:
default:
- ldown = div_u64(ldown, 100);
+ lthroughput = div_u64(lthroughput, 100);
break;
}
- if (U32_MAX < ldown) {
+ if (lthroughput > U32_MAX) {
batadv_err(net_dev,
- "Download speed of gateway mode too large: %s\n",
- buff);
+ "Throughput speed for %s too large: %s\n",
+ description, buff);
return false;
}
- *down = ldown;
-
- /* we also got some upload info */
- if (slash_ptr) {
- bw_unit_type = BATADV_BW_UNIT_KBIT;
-
- if (strlen(slash_ptr + 1) > 4) {
- tmp_ptr = slash_ptr + 1 - 4 + strlen(slash_ptr + 1);
+ *throughput = lthroughput;
- if (strncasecmp(tmp_ptr, "mbit", 4) == 0)
- bw_unit_type = BATADV_BW_UNIT_MBIT;
+ return true;
+}
- if ((strncasecmp(tmp_ptr, "kbit", 4) == 0) ||
- (bw_unit_type == BATADV_BW_UNIT_MBIT))
- *tmp_ptr = '\0';
- }
+/**
+ * batadv_parse_gw_bandwidth - parse supplied string buffer to extract download
+ * and upload bandwidth information
+ * @net_dev: the soft interface net device
+ * @buff: string buffer to parse
+ * @down: pointer holding the returned download bandwidth information
+ * @up: pointer holding the returned upload bandwidth information
+ *
+ * Return: false on parse error and true otherwise.
+ */
+static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
+ u32 *down, u32 *up)
+{
+ char *slash_ptr;
+ bool ret;
- ret = kstrtou64(slash_ptr + 1, 10, &lup);
- if (ret) {
- batadv_err(net_dev,
- "Upload speed of gateway mode invalid: %s\n",
- slash_ptr + 1);
- return false;
- }
+ slash_ptr = strchr(buff, '/');
+ if (slash_ptr)
+ *slash_ptr = 0;
- switch (bw_unit_type) {
- case BATADV_BW_UNIT_MBIT:
- /* prevent overflow */
- if (U64_MAX / 10 < lup) {
- batadv_err(net_dev,
- "Upload speed of gateway mode too large: %s\n",
- slash_ptr + 1);
- return false;
- }
-
- lup *= 10;
- break;
- case BATADV_BW_UNIT_KBIT:
- default:
- lup = div_u64(lup, 100);
- break;
- }
+ ret = batadv_parse_throughput(net_dev, buff, "download gateway speed",
+ down);
+ if (!ret)
+ return false;
- if (U32_MAX < lup) {
- batadv_err(net_dev,
- "Upload speed of gateway mode too large: %s\n",
- slash_ptr + 1);
+ /* we also got some upload info */
+ if (slash_ptr) {
+ ret = batadv_parse_throughput(net_dev, slash_ptr + 1,
+ "upload gateway speed", up);
+ if (!ret)
return false;
- }
-
- *up = lup;
}
return true;
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index f11345e163d7..01acccc4d218 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -32,6 +32,7 @@
#include <linux/rculist.h>
#include <linux/rtnetlink.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <net/net_namespace.h>
@@ -464,7 +465,8 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
hard_iface->soft_iface = soft_iface;
bat_priv = netdev_priv(hard_iface->soft_iface);
- ret = netdev_master_upper_dev_link(hard_iface->net_dev, soft_iface);
+ ret = netdev_master_upper_dev_link(hard_iface->net_dev,
+ soft_iface, NULL, NULL);
if (ret)
goto err_dev;
@@ -638,9 +640,12 @@ batadv_hardif_add_interface(struct net_device *net_dev)
goto free_sysfs;
INIT_LIST_HEAD(&hard_iface->list);
+ INIT_HLIST_HEAD(&hard_iface->neigh_list);
INIT_WORK(&hard_iface->cleanup_work,
batadv_hardif_remove_interface_finish);
+ spin_lock_init(&hard_iface->neigh_list_lock);
+
hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
if (batadv_is_wifi_netdev(net_dev))
hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
@@ -708,7 +713,8 @@ static int batadv_hard_if_event(struct notifier_block *this,
}
hard_iface = batadv_hardif_get_by_netdev(net_dev);
- if (!hard_iface && event == NETDEV_REGISTER)
+ if (!hard_iface && (event == NETDEV_REGISTER ||
+ event == NETDEV_POST_TYPE_CHANGE))
hard_iface = batadv_hardif_add_interface(net_dev);
if (!hard_iface)
@@ -723,6 +729,7 @@ static int batadv_hard_if_event(struct notifier_block *this,
batadv_hardif_deactivate_interface(hard_iface);
break;
case NETDEV_UNREGISTER:
+ case NETDEV_PRE_TYPE_CHANGE:
list_del_rcu(&hard_iface->list);
batadv_hardif_remove_interface(hard_iface);
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index d7f17c1aa4a4..4b5d61fbadb1 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -552,7 +552,7 @@ int batadv_algo_register(struct batadv_algo_ops *bat_algo_ops)
!bat_algo_ops->bat_ogm_schedule ||
!bat_algo_ops->bat_ogm_emit ||
!bat_algo_ops->bat_neigh_cmp ||
- !bat_algo_ops->bat_neigh_is_equiv_or_better) {
+ !bat_algo_ops->bat_neigh_is_similar_or_better) {
pr_info("Routing algo '%s' does not implement required ops\n",
bat_algo_ops->name);
return -EINVAL;
@@ -747,7 +747,7 @@ static u16 batadv_tvlv_container_list_size(struct batadv_priv *bat_priv)
static void batadv_tvlv_container_remove(struct batadv_priv *bat_priv,
struct batadv_tvlv_container *tvlv)
{
- lockdep_assert_held(&bat_priv->tvlv.handler_list_lock);
+ lockdep_assert_held(&bat_priv->tvlv.container_list_lock);
if (!tvlv)
return;
@@ -908,7 +908,7 @@ end:
* appropriate handlers
* @bat_priv: the bat priv with all the soft interface information
* @tvlv_handler: tvlv callback function handling the tvlv content
- * @ogm_source: flag indicating wether the tvlv is an ogm or a unicast packet
+ * @ogm_source: flag indicating whether the tvlv is an ogm or a unicast packet
* @orig_node: orig node emitting the ogm packet
* @src: source mac address of the unicast packet
* @dst: destination mac address of the unicast packet
@@ -961,7 +961,7 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
* batadv_tvlv_containers_process - parse the given tvlv buffer to call the
* appropriate handlers
* @bat_priv: the bat priv with all the soft interface information
- * @ogm_source: flag indicating wether the tvlv is an ogm or a unicast packet
+ * @ogm_source: flag indicating whether the tvlv is an ogm or a unicast packet
* @orig_node: orig node emitting the ogm packet
* @src: source mac address of the unicast packet
* @dst: destination mac address of the unicast packet
@@ -1143,15 +1143,14 @@ void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, u8 *src,
struct batadv_unicast_tvlv_packet *unicast_tvlv_packet;
struct batadv_tvlv_hdr *tvlv_hdr;
struct batadv_orig_node *orig_node;
- struct sk_buff *skb = NULL;
+ struct sk_buff *skb;
unsigned char *tvlv_buff;
unsigned int tvlv_len;
ssize_t hdr_len = sizeof(*unicast_tvlv_packet);
- bool ret = false;
orig_node = batadv_orig_hash_find(bat_priv, dst);
if (!orig_node)
- goto out;
+ return;
tvlv_len = sizeof(*tvlv_hdr) + tvlv_value_len;
@@ -1180,14 +1179,10 @@ void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, u8 *src,
tvlv_buff += sizeof(*tvlv_hdr);
memcpy(tvlv_buff, tvlv_value, tvlv_value_len);
- if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP)
- ret = true;
-
-out:
- if (skb && !ret)
+ if (batadv_send_skb_to_orig(skb, orig_node, NULL) == NET_XMIT_DROP)
kfree_skb(skb);
- if (orig_node)
- batadv_orig_node_free_ref(orig_node);
+out:
+ batadv_orig_node_free_ref(orig_node);
}
/**
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index ebd8af0a1eb0..9dbd9107e7e1 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -24,7 +24,7 @@
#define BATADV_DRIVER_DEVICE "batman-adv"
#ifndef BATADV_SOURCE_VERSION
-#define BATADV_SOURCE_VERSION "2015.2"
+#define BATADV_SOURCE_VERSION "2016.0"
#endif
/* B.A.T.M.A.N. parameters */
@@ -109,7 +109,7 @@
#define BATADV_MAX_AGGREGATION_MS 100
#define BATADV_BLA_PERIOD_LENGTH 10000 /* 10 seconds */
-#define BATADV_BLA_BACKBONE_TIMEOUT (BATADV_BLA_PERIOD_LENGTH * 3)
+#define BATADV_BLA_BACKBONE_TIMEOUT (BATADV_BLA_PERIOD_LENGTH * 6)
#define BATADV_BLA_CLAIM_TIMEOUT (BATADV_BLA_PERIOD_LENGTH * 10)
#define BATADV_BLA_WAIT_PERIODS 3
diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c
index f5276be2c77c..c98b0ab85449 100644
--- a/net/batman-adv/network-coding.c
+++ b/net/batman-adv/network-coding.c
@@ -244,9 +244,7 @@ static void batadv_nc_path_free_ref(struct batadv_nc_path *nc_path)
*/
static void batadv_nc_packet_free(struct batadv_nc_packet *nc_packet)
{
- if (nc_packet->skb)
- kfree_skb(nc_packet->skb);
-
+ kfree_skb(nc_packet->skb);
batadv_nc_path_free_ref(nc_packet->nc_path);
kfree(nc_packet);
}
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c
index 7486df9ed48d..3c782a33bdac 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -202,6 +202,47 @@ void batadv_neigh_ifinfo_free_ref(struct batadv_neigh_ifinfo *neigh_ifinfo)
}
/**
+ * batadv_hardif_neigh_free_rcu - free the hardif neigh_node
+ * @rcu: rcu pointer of the neigh_node
+ */
+static void batadv_hardif_neigh_free_rcu(struct rcu_head *rcu)
+{
+ struct batadv_hardif_neigh_node *hardif_neigh;
+
+ hardif_neigh = container_of(rcu, struct batadv_hardif_neigh_node, rcu);
+
+ spin_lock_bh(&hardif_neigh->if_incoming->neigh_list_lock);
+ hlist_del_init_rcu(&hardif_neigh->list);
+ spin_unlock_bh(&hardif_neigh->if_incoming->neigh_list_lock);
+
+ batadv_hardif_free_ref_now(hardif_neigh->if_incoming);
+ kfree(hardif_neigh);
+}
+
+/**
+ * batadv_hardif_neigh_free_now - decrement the hardif neighbors refcounter
+ * and possibly free it (without rcu callback)
+ * @hardif_neigh: hardif neigh neighbor to free
+ */
+static void
+batadv_hardif_neigh_free_now(struct batadv_hardif_neigh_node *hardif_neigh)
+{
+ if (atomic_dec_and_test(&hardif_neigh->refcount))
+ batadv_hardif_neigh_free_rcu(&hardif_neigh->rcu);
+}
+
+/**
+ * batadv_hardif_neigh_free_ref - decrement the hardif neighbors refcounter
+ * and possibly free it
+ * @hardif_neigh: hardif neigh neighbor to free
+ */
+void batadv_hardif_neigh_free_ref(struct batadv_hardif_neigh_node *hardif_neigh)
+{
+ if (atomic_dec_and_test(&hardif_neigh->refcount))
+ call_rcu(&hardif_neigh->rcu, batadv_hardif_neigh_free_rcu);
+}
+
+/**
* batadv_neigh_node_free_rcu - free the neigh_node
* @rcu: rcu pointer of the neigh_node
*/
@@ -209,6 +250,7 @@ static void batadv_neigh_node_free_rcu(struct rcu_head *rcu)
{
struct hlist_node *node_tmp;
struct batadv_neigh_node *neigh_node;
+ struct batadv_hardif_neigh_node *hardif_neigh;
struct batadv_neigh_ifinfo *neigh_ifinfo;
struct batadv_algo_ops *bao;
@@ -220,6 +262,14 @@ static void batadv_neigh_node_free_rcu(struct rcu_head *rcu)
batadv_neigh_ifinfo_free_ref_now(neigh_ifinfo);
}
+ hardif_neigh = batadv_hardif_neigh_get(neigh_node->if_incoming,
+ neigh_node->addr);
+ if (hardif_neigh) {
+ /* batadv_hardif_neigh_get() increases refcount too */
+ batadv_hardif_neigh_free_now(hardif_neigh);
+ batadv_hardif_neigh_free_now(hardif_neigh);
+ }
+
if (bao->bat_neigh_free)
bao->bat_neigh_free(neigh_node);
@@ -479,6 +529,106 @@ batadv_neigh_node_get(const struct batadv_orig_node *orig_node,
}
/**
+ * batadv_hardif_neigh_create - create a hardif neighbour node
+ * @hard_iface: the interface this neighbour is connected to
+ * @neigh_addr: the interface address of the neighbour to retrieve
+ *
+ * Returns the hardif neighbour node if found or created or NULL otherwise.
+ */
+static struct batadv_hardif_neigh_node *
+batadv_hardif_neigh_create(struct batadv_hard_iface *hard_iface,
+ const u8 *neigh_addr)
+{
+ struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
+ struct batadv_hardif_neigh_node *hardif_neigh = NULL;
+
+ spin_lock_bh(&hard_iface->neigh_list_lock);
+
+ /* check if neighbor hasn't been added in the meantime */
+ hardif_neigh = batadv_hardif_neigh_get(hard_iface, neigh_addr);
+ if (hardif_neigh)
+ goto out;
+
+ if (!atomic_inc_not_zero(&hard_iface->refcount))
+ goto out;
+
+ hardif_neigh = kzalloc(sizeof(*hardif_neigh), GFP_ATOMIC);
+ if (!hardif_neigh) {
+ batadv_hardif_free_ref(hard_iface);
+ goto out;
+ }
+
+ INIT_HLIST_NODE(&hardif_neigh->list);
+ ether_addr_copy(hardif_neigh->addr, neigh_addr);
+ hardif_neigh->if_incoming = hard_iface;
+ hardif_neigh->last_seen = jiffies;
+
+ atomic_set(&hardif_neigh->refcount, 1);
+
+ if (bat_priv->bat_algo_ops->bat_hardif_neigh_init)
+ bat_priv->bat_algo_ops->bat_hardif_neigh_init(hardif_neigh);
+
+ hlist_add_head(&hardif_neigh->list, &hard_iface->neigh_list);
+
+out:
+ spin_unlock_bh(&hard_iface->neigh_list_lock);
+ return hardif_neigh;
+}
+
+/**
+ * batadv_hardif_neigh_get_or_create - retrieve or create a hardif neighbour
+ * node
+ * @hard_iface: the interface this neighbour is connected to
+ * @neigh_addr: the interface address of the neighbour to retrieve
+ *
+ * Returns the hardif neighbour node if found or created or NULL otherwise.
+ */
+static struct batadv_hardif_neigh_node *
+batadv_hardif_neigh_get_or_create(struct batadv_hard_iface *hard_iface,
+ const u8 *neigh_addr)
+{
+ struct batadv_hardif_neigh_node *hardif_neigh = NULL;
+
+ /* first check without locking to avoid the overhead */
+ hardif_neigh = batadv_hardif_neigh_get(hard_iface, neigh_addr);
+ if (hardif_neigh)
+ return hardif_neigh;
+
+ return batadv_hardif_neigh_create(hard_iface, neigh_addr);
+}
+
+/**
+ * batadv_hardif_neigh_get - retrieve a hardif neighbour from the list
+ * @hard_iface: the interface where this neighbour is connected to
+ * @neigh_addr: the address of the neighbour
+ *
+ * Looks for and possibly returns a neighbour belonging to this hard interface.
+ * Returns NULL if the neighbour is not found.
+ */
+struct batadv_hardif_neigh_node *
+batadv_hardif_neigh_get(const struct batadv_hard_iface *hard_iface,
+ const u8 *neigh_addr)
+{
+ struct batadv_hardif_neigh_node *tmp_hardif_neigh, *hardif_neigh = NULL;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(tmp_hardif_neigh,
+ &hard_iface->neigh_list, list) {
+ if (!batadv_compare_eth(tmp_hardif_neigh->addr, neigh_addr))
+ continue;
+
+ if (!atomic_inc_not_zero(&tmp_hardif_neigh->refcount))
+ continue;
+
+ hardif_neigh = tmp_hardif_neigh;
+ break;
+ }
+ rcu_read_unlock();
+
+ return hardif_neigh;
+}
+
+/**
* batadv_neigh_node_new - create and init a new neigh_node object
* @orig_node: originator object representing the neighbour
* @hard_iface: the interface where the neighbour is connected to
@@ -493,11 +643,17 @@ batadv_neigh_node_new(struct batadv_orig_node *orig_node,
const u8 *neigh_addr)
{
struct batadv_neigh_node *neigh_node;
+ struct batadv_hardif_neigh_node *hardif_neigh = NULL;
neigh_node = batadv_neigh_node_get(orig_node, hard_iface, neigh_addr);
if (neigh_node)
goto out;
+ hardif_neigh = batadv_hardif_neigh_get_or_create(hard_iface,
+ neigh_addr);
+ if (!hardif_neigh)
+ goto out;
+
neigh_node = kzalloc(sizeof(*neigh_node), GFP_ATOMIC);
if (!neigh_node)
goto out;
@@ -523,15 +679,54 @@ batadv_neigh_node_new(struct batadv_orig_node *orig_node,
hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list);
spin_unlock_bh(&orig_node->neigh_list_lock);
+ /* increment unique neighbor refcount */
+ atomic_inc(&hardif_neigh->refcount);
+
batadv_dbg(BATADV_DBG_BATMAN, orig_node->bat_priv,
"Creating new neighbor %pM for orig_node %pM on interface %s\n",
neigh_addr, orig_node->orig, hard_iface->net_dev->name);
out:
+ if (hardif_neigh)
+ batadv_hardif_neigh_free_ref(hardif_neigh);
return neigh_node;
}
/**
+ * batadv_hardif_neigh_seq_print_text - print the single hop neighbour list
+ * @seq: neighbour table seq_file struct
+ * @offset: not used
+ *
+ * Always returns 0.
+ */
+int batadv_hardif_neigh_seq_print_text(struct seq_file *seq, void *offset)
+{
+ struct net_device *net_dev = (struct net_device *)seq->private;
+ struct batadv_priv *bat_priv = netdev_priv(net_dev);
+ struct batadv_hard_iface *primary_if;
+
+ primary_if = batadv_seq_print_text_primary_if_get(seq);
+ if (!primary_if)
+ return 0;
+
+ seq_printf(seq, "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s %s)]\n",
+ BATADV_SOURCE_VERSION, primary_if->net_dev->name,
+ primary_if->net_dev->dev_addr, net_dev->name,
+ bat_priv->bat_algo_ops->name);
+
+ batadv_hardif_free_ref(primary_if);
+
+ if (!bat_priv->bat_algo_ops->bat_neigh_print) {
+ seq_puts(seq,
+ "No printing function for this routing protocol\n");
+ return 0;
+ }
+
+ bat_priv->bat_algo_ops->bat_neigh_print(bat_priv, seq);
+ return 0;
+}
+
+/**
* batadv_orig_ifinfo_free_rcu - free the orig_ifinfo object
* @rcu: rcu pointer of the orig_ifinfo object
*/
diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h
index fa18f9bf266b..29557753d552 100644
--- a/net/batman-adv/originator.h
+++ b/net/batman-adv/originator.h
@@ -41,6 +41,11 @@ void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node);
void batadv_orig_node_free_ref_now(struct batadv_orig_node *orig_node);
struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv,
const u8 *addr);
+struct batadv_hardif_neigh_node *
+batadv_hardif_neigh_get(const struct batadv_hard_iface *hard_iface,
+ const u8 *neigh_addr);
+void
+batadv_hardif_neigh_free_ref(struct batadv_hardif_neigh_node *hardif_neigh);
struct batadv_neigh_node *
batadv_neigh_node_new(struct batadv_orig_node *orig_node,
struct batadv_hard_iface *hard_iface,
@@ -57,6 +62,8 @@ batadv_neigh_ifinfo_get(struct batadv_neigh_node *neigh,
struct batadv_hard_iface *if_outgoing);
void batadv_neigh_ifinfo_free_ref(struct batadv_neigh_ifinfo *neigh_ifinfo);
+int batadv_hardif_neigh_seq_print_text(struct seq_file *seq, void *offset);
+
struct batadv_orig_ifinfo *
batadv_orig_ifinfo_get(struct batadv_orig_node *orig_node,
struct batadv_hard_iface *if_outgoing);
diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h
index 11f996b39fef..0558e3237e0e 100644
--- a/net/batman-adv/packet.h
+++ b/net/batman-adv/packet.h
@@ -72,8 +72,7 @@ enum batadv_subtype {
* enum batadv_iv_flags - flags used in B.A.T.M.A.N. IV OGM packets
* @BATADV_NOT_BEST_NEXT_HOP: flag is set when ogm packet is forwarded and was
* previously received from someone else than the best neighbor.
- * @BATADV_PRIMARIES_FIRST_HOP: flag is set when the primary interface address
- * is used, and the packet travels its first hop.
+ * @BATADV_PRIMARIES_FIRST_HOP: flag unused.
* @BATADV_DIRECTLINK: flag is for the first hop or if rebroadcasted from a
* one hop neighbor on the interface where it was originally received.
*/
diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
index 8d990b070a2e..e4f2646d9246 100644
--- a/net/batman-adv/routing.c
+++ b/net/batman-adv/routing.c
@@ -497,9 +497,9 @@ batadv_find_router(struct batadv_priv *bat_priv,
/* alternative candidate should be good enough to be
* considered
*/
- if (!bao->bat_neigh_is_equiv_or_better(cand_router,
- cand->if_outgoing,
- router, recv_if))
+ if (!bao->bat_neigh_is_similar_or_better(cand_router,
+ cand->if_outgoing,
+ router, recv_if))
goto next;
/* don't use the same router twice */
@@ -836,6 +836,7 @@ int batadv_recv_unicast_packet(struct sk_buff *skb,
u8 *orig_addr;
struct batadv_orig_node *orig_node = NULL;
int check, hdr_size = sizeof(*unicast_packet);
+ enum batadv_subtype subtype;
bool is4addr;
unicast_packet = (struct batadv_unicast_packet *)skb->data;
@@ -863,10 +864,20 @@ int batadv_recv_unicast_packet(struct sk_buff *skb,
/* packet for me */
if (batadv_is_my_mac(bat_priv, unicast_packet->dest)) {
if (is4addr) {
- batadv_dat_inc_counter(bat_priv,
- unicast_4addr_packet->subtype);
- orig_addr = unicast_4addr_packet->src;
- orig_node = batadv_orig_hash_find(bat_priv, orig_addr);
+ subtype = unicast_4addr_packet->subtype;
+ batadv_dat_inc_counter(bat_priv, subtype);
+
+ /* Only payload data should be considered for speedy
+ * join. For example, DAT also uses unicast 4addr
+ * types, but those packets should not be considered
+ * for speedy join, since the clients do not actually
+ * reside at the sending originator.
+ */
+ if (subtype == BATADV_P_DATA) {
+ orig_addr = unicast_4addr_packet->src;
+ orig_node = batadv_orig_hash_find(bat_priv,
+ orig_addr);
+ }
}
if (batadv_dat_snoop_incoming_arp_request(bat_priv, skb,
diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c
index f664324805eb..782fa33ec296 100644
--- a/net/batman-adv/send.c
+++ b/net/batman-adv/send.c
@@ -407,8 +407,7 @@ void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface)
static void batadv_forw_packet_free(struct batadv_forw_packet *forw_packet)
{
- if (forw_packet->skb)
- kfree_skb(forw_packet->skb);
+ kfree_skb(forw_packet->skb);
if (forw_packet->if_incoming)
batadv_hardif_free_ref(forw_packet->if_incoming);
if (forw_packet->if_outgoing)
diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c
index 9de3c8804ff4..fe87777fda8a 100644
--- a/net/batman-adv/sysfs.c
+++ b/net/batman-adv/sysfs.c
@@ -40,6 +40,7 @@
#include "distributed-arp-table.h"
#include "gateway_client.h"
#include "gateway_common.h"
+#include "bridge_loop_avoidance.h"
#include "hard-interface.h"
#include "network-coding.h"
#include "packet.h"
@@ -241,10 +242,13 @@ ssize_t batadv_show_vlan_##_name(struct kobject *kobj, \
static int batadv_store_bool_attr(char *buff, size_t count,
struct net_device *net_dev,
- const char *attr_name, atomic_t *attr)
+ const char *attr_name, atomic_t *attr,
+ bool *changed)
{
int enabled = -1;
+ *changed = false;
+
if (buff[count - 1] == '\n')
buff[count - 1] = '\0';
@@ -271,6 +275,8 @@ static int batadv_store_bool_attr(char *buff, size_t count,
atomic_read(attr) == 1 ? "enabled" : "disabled",
enabled == 1 ? "enabled" : "disabled");
+ *changed = true;
+
atomic_set(attr, (unsigned int)enabled);
return count;
}
@@ -281,11 +287,12 @@ __batadv_store_bool_attr(char *buff, size_t count,
struct attribute *attr,
atomic_t *attr_store, struct net_device *net_dev)
{
+ bool changed;
int ret;
ret = batadv_store_bool_attr(buff, count, net_dev, attr->name,
- attr_store);
- if (post_func && ret)
+ attr_store, &changed);
+ if (post_func && changed)
post_func(net_dev);
return ret;
@@ -549,7 +556,8 @@ static ssize_t batadv_store_isolation_mark(struct kobject *kobj,
BATADV_ATTR_SIF_BOOL(aggregated_ogms, S_IRUGO | S_IWUSR, NULL);
BATADV_ATTR_SIF_BOOL(bonding, S_IRUGO | S_IWUSR, NULL);
#ifdef CONFIG_BATMAN_ADV_BLA
-BATADV_ATTR_SIF_BOOL(bridge_loop_avoidance, S_IRUGO | S_IWUSR, NULL);
+BATADV_ATTR_SIF_BOOL(bridge_loop_avoidance, S_IRUGO | S_IWUSR,
+ batadv_bla_status_update);
#endif
#ifdef CONFIG_BATMAN_ADV_DAT
BATADV_ATTR_SIF_BOOL(distributed_arp_table, S_IRUGO | S_IWUSR,
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index 4228b10c47ea..a22080c53401 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -68,13 +68,15 @@ static void batadv_tt_global_del(struct batadv_priv *bat_priv,
unsigned short vid, const char *message,
bool roaming);
-/* returns 1 if they are the same mac addr */
+/* returns 1 if they are the same mac addr and vid */
static int batadv_compare_tt(const struct hlist_node *node, const void *data2)
{
const void *data1 = container_of(node, struct batadv_tt_common_entry,
hash_entry);
+ const struct batadv_tt_common_entry *tt1 = data1;
+ const struct batadv_tt_common_entry *tt2 = data2;
- return batadv_compare_eth(data1, data2);
+ return (tt1->vid == tt2->vid) && batadv_compare_eth(data1, data2);
}
/**
@@ -1427,15 +1429,21 @@ static bool batadv_tt_global_add(struct batadv_priv *bat_priv,
}
/* if the client was temporary added before receiving the first
- * OGM announcing it, we have to clear the TEMP flag
+ * OGM announcing it, we have to clear the TEMP flag. Also,
+ * remove the previous temporary orig node and re-add it
+ * if required. If the orig entry changed, the new one which
+ * is a non-temporary entry is preferred.
*/
- common->flags &= ~BATADV_TT_CLIENT_TEMP;
+ if (common->flags & BATADV_TT_CLIENT_TEMP) {
+ batadv_tt_global_del_orig_list(tt_global_entry);
+ common->flags &= ~BATADV_TT_CLIENT_TEMP;
+ }
/* the change can carry possible "attribute" flags like the
* TT_CLIENT_WIFI, therefore they have to be copied in the
* client entry
*/
- tt_global_entry->common.flags |= flags;
+ common->flags |= flags;
/* If there is the BATADV_TT_CLIENT_ROAM flag set, there is only
* one originator left in the list and we previously received a
@@ -2411,8 +2419,8 @@ static bool batadv_tt_global_check_crc(struct batadv_orig_node *orig_node,
{
struct batadv_tvlv_tt_vlan_data *tt_vlan_tmp;
struct batadv_orig_node_vlan *vlan;
+ int i, orig_num_vlan;
u32 crc;
- int i;
/* check if each received CRC matches the locally stored one */
for (i = 0; i < num_vlan; i++) {
@@ -2438,6 +2446,18 @@ static bool batadv_tt_global_check_crc(struct batadv_orig_node *orig_node,
return false;
}
+ /* check if any excess VLANs exist locally for the originator
+ * which are not mentioned in the TVLV from the originator.
+ */
+ rcu_read_lock();
+ orig_num_vlan = 0;
+ hlist_for_each_entry_rcu(vlan, &orig_node->vlan_list, list)
+ orig_num_vlan++;
+ rcu_read_unlock();
+
+ if (orig_num_vlan > num_vlan)
+ return false;
+
return true;
}
@@ -3319,7 +3339,10 @@ bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, u8 *src, u8 *dst,
bool ret = false;
vlan = batadv_softif_vlan_get(bat_priv, vid);
- if (!vlan || !atomic_read(&vlan->ap_isolation))
+ if (!vlan)
+ return false;
+
+ if (!atomic_read(&vlan->ap_isolation))
goto out;
tt_local_entry = batadv_tt_local_hash_find(bat_priv, dst, vid);
@@ -3336,8 +3359,7 @@ bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, u8 *src, u8 *dst,
ret = true;
out:
- if (vlan)
- batadv_softif_vlan_free_ref(vlan);
+ batadv_softif_vlan_free_ref(vlan);
if (tt_global_entry)
batadv_tt_global_entry_free_ref(tt_global_entry);
if (tt_local_entry)
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index d260efd70499..3437b667a2cd 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -100,6 +100,8 @@ struct batadv_hard_iface_bat_iv {
* @bat_iv: BATMAN IV specific per hard interface data
* @cleanup_work: work queue callback item for hard interface deinit
* @debug_dir: dentry for nc subdir in batman-adv directory in debugfs
+ * @neigh_list: list of unique single hop neighbors via this interface
+ * @neigh_list_lock: lock protecting neigh_list
*/
struct batadv_hard_iface {
struct list_head list;
@@ -115,6 +117,9 @@ struct batadv_hard_iface {
struct batadv_hard_iface_bat_iv bat_iv;
struct work_struct cleanup_work;
struct dentry *debug_dir;
+ struct hlist_head neigh_list;
+ /* neigh_list_lock protects: neigh_list */
+ spinlock_t neigh_list_lock;
};
/**
@@ -218,12 +223,12 @@ struct batadv_orig_bat_iv {
* @orig: originator ethernet address
* @ifinfo_list: list for routers per outgoing interface
* @last_bonding_candidate: pointer to last ifinfo of last used router
- * @batadv_dat_addr_t: address of the orig node in the distributed hash
+ * @dat_addr: address of the orig node in the distributed hash
* @last_seen: time when last packet from this node was received
* @bcast_seqno_reset: time when the broadcast seqno window was reset
* @mcast_handler_lock: synchronizes mcast-capability and -flag changes
* @mcast_flags: multicast flags announced by the orig node
- * @mcast_want_all_unsnoop_node: a list node for the
+ * @mcast_want_all_unsnoopables_node: a list node for the
* mcast.want_all_unsnoopables list
* @mcast_want_all_ipv4_node: a list node for the mcast.want_all_ipv4 list
* @mcast_want_all_ipv6_node: a list node for the mcast.want_all_ipv6 list
@@ -341,6 +346,23 @@ struct batadv_gw_node {
};
/**
+ * batadv_hardif_neigh_node - unique neighbor per hard interface
+ * @list: list node for batadv_hard_iface::neigh_list
+ * @addr: the MAC address of the neighboring interface
+ * @if_incoming: pointer to incoming hard interface
+ * @refcount: number of contexts the object is used
+ * @rcu: struct used for freeing in a RCU-safe manner
+ */
+struct batadv_hardif_neigh_node {
+ struct hlist_node list;
+ u8 addr[ETH_ALEN];
+ struct batadv_hard_iface *if_incoming;
+ unsigned long last_seen;
+ atomic_t refcount;
+ struct rcu_head rcu;
+};
+
+/**
* struct batadv_neigh_node - structure for single hops neighbors
* @list: list node for batadv_orig_node::neigh_list
* @orig_node: pointer to corresponding orig_node
@@ -349,9 +371,8 @@ struct batadv_gw_node {
* @ifinfo_lock: lock protecting private ifinfo members and list
* @if_incoming: pointer to incoming hard interface
* @last_seen: when last packet via this neighbor was received
- * @last_ttl: last received ttl from this neigh node
+ * @refcount: number of contexts the object is used
* @rcu: struct used for freeing in an RCU-safe manner
- * @bat_iv: B.A.T.M.A.N. IV private structure
*/
struct batadv_neigh_node {
struct hlist_node list;
@@ -401,13 +422,14 @@ struct batadv_neigh_ifinfo {
struct rcu_head rcu;
};
+#ifdef CONFIG_BATMAN_ADV_BLA
+
/**
* struct batadv_bcast_duplist_entry - structure for LAN broadcast suppression
- * @orig[ETH_ALEN]: mac address of orig node orginating the broadcast
+ * @orig: mac address of orig node orginating the broadcast
* @crc: crc32 checksum of broadcast payload
* @entrytime: time when the broadcast packet was received
*/
-#ifdef CONFIG_BATMAN_ADV_BLA
struct batadv_bcast_duplist_entry {
u8 orig[ETH_ALEN];
__be32 crc;
@@ -549,9 +571,11 @@ struct batadv_priv_tt {
struct delayed_work work;
};
+#ifdef CONFIG_BATMAN_ADV_BLA
+
/**
* struct batadv_priv_bla - per mesh interface bridge loope avoidance data
- * @num_requests; number of bla requests in flight
+ * @num_requests: number of bla requests in flight
* @claim_hash: hash table containing mesh nodes this host has claimed
* @backbone_hash: hash table containing all detected backbone gateways
* @bcast_duplist: recently received broadcast packets array (for broadcast
@@ -561,7 +585,6 @@ struct batadv_priv_tt {
* @claim_dest: local claim data (e.g. claim group)
* @work: work queue callback item for cleanups & bla announcements
*/
-#ifdef CONFIG_BATMAN_ADV_BLA
struct batadv_priv_bla {
atomic_t num_requests;
struct batadv_hashtable *claim_hash;
@@ -575,6 +598,8 @@ struct batadv_priv_bla {
};
#endif
+#ifdef CONFIG_BATMAN_ADV_DEBUG
+
/**
* struct batadv_priv_debug_log - debug logging data
* @log_buff: buffer holding the logs (ring bufer)
@@ -583,7 +608,6 @@ struct batadv_priv_bla {
* @lock: lock protecting log_buff, log_start & log_end
* @queue_wait: log reader's wait queue
*/
-#ifdef CONFIG_BATMAN_ADV_DEBUG
struct batadv_priv_debug_log {
char log_buff[BATADV_LOG_BUF_LEN];
unsigned long log_start;
@@ -625,13 +649,14 @@ struct batadv_priv_tvlv {
spinlock_t handler_list_lock; /* protects handler_list */
};
+#ifdef CONFIG_BATMAN_ADV_DAT
+
/**
* struct batadv_priv_dat - per mesh interface DAT private data
* @addr: node DAT address
* @hash: hashtable representing the local ARP cache
* @work: work queue callback item for cache purging
*/
-#ifdef CONFIG_BATMAN_ADV_DAT
struct batadv_priv_dat {
batadv_dat_addr_t addr;
struct batadv_hashtable *hash;
@@ -773,7 +798,7 @@ struct batadv_softif_vlan {
* @dat: distributed arp table data
* @mcast: multicast data
* @network_coding: bool indicating whether network coding is enabled
- * @batadv_priv_nc: network coding data
+ * @nc: network coding data
*/
struct batadv_priv {
atomic_t mesh_state;
@@ -871,6 +896,8 @@ struct batadv_socket_packet {
u8 icmp_packet[BATADV_ICMP_MAX_PACKET_SIZE];
};
+#ifdef CONFIG_BATMAN_ADV_BLA
+
/**
* struct batadv_bla_backbone_gw - batman-adv gateway bridged into the LAN
* @orig: originator address of backbone node (mac address of primary iface)
@@ -884,10 +911,10 @@ struct batadv_socket_packet {
* backbone gateway - no bcast traffic is formwared until the situation was
* resolved
* @crc: crc16 checksum over all claims
+ * @crc_lock: lock protecting crc
* @refcount: number of contexts the object is used
* @rcu: struct used for freeing in an RCU-safe manner
*/
-#ifdef CONFIG_BATMAN_ADV_BLA
struct batadv_bla_backbone_gw {
u8 orig[ETH_ALEN];
unsigned short vid;
@@ -897,6 +924,7 @@ struct batadv_bla_backbone_gw {
atomic_t wait_periods;
atomic_t request_sent;
u16 crc;
+ spinlock_t crc_lock; /* protects crc */
atomic_t refcount;
struct rcu_head rcu;
};
@@ -905,7 +933,7 @@ struct batadv_bla_backbone_gw {
* struct batadv_bla_claim - claimed non-mesh client structure
* @addr: mac address of claimed non-mesh client
* @vid: vlan id this client was detected on
- * @batadv_bla_backbone_gw: pointer to backbone gw claiming this client
+ * @backbone_gw: pointer to backbone gw claiming this client
* @lasttime: last time we heard of claim (locals only)
* @hash_entry: hlist node for batadv_priv_bla::claim_hash
* @refcount: number of contexts the object is used
@@ -1131,11 +1159,13 @@ struct batadv_forw_packet {
* @bat_primary_iface_set: called when primary interface is selected / changed
* @bat_ogm_schedule: prepare a new outgoing OGM for the send queue
* @bat_ogm_emit: send scheduled OGM
+ * @bat_hardif_neigh_init: called on creation of single hop entry
* @bat_neigh_cmp: compare the metrics of two neighbors for their respective
* outgoing interfaces
- * @bat_neigh_is_equiv_or_better: check if neigh1 is equally good or better
- * than neigh2 for their respective outgoing interface from the metric
+ * @bat_neigh_is_similar_or_better: check if neigh1 is equally similar or
+ * better than neigh2 for their respective outgoing interface from the metric
* prospective
+ * @bat_neigh_print: print the single hop neighbor list (optional)
* @bat_neigh_free: free the resources allocated by the routing algorithm for a
* neigh_node object
* @bat_orig_print: print the originator table (optional)
@@ -1156,15 +1186,17 @@ struct batadv_algo_ops {
void (*bat_ogm_schedule)(struct batadv_hard_iface *hard_iface);
void (*bat_ogm_emit)(struct batadv_forw_packet *forw_packet);
/* neigh_node handling API */
+ void (*bat_hardif_neigh_init)(struct batadv_hardif_neigh_node *neigh);
int (*bat_neigh_cmp)(struct batadv_neigh_node *neigh1,
struct batadv_hard_iface *if_outgoing1,
struct batadv_neigh_node *neigh2,
struct batadv_hard_iface *if_outgoing2);
- bool (*bat_neigh_is_equiv_or_better)
+ bool (*bat_neigh_is_similar_or_better)
(struct batadv_neigh_node *neigh1,
struct batadv_hard_iface *if_outgoing1,
struct batadv_neigh_node *neigh2,
struct batadv_hard_iface *if_outgoing2);
+ void (*bat_neigh_print)(struct batadv_priv *priv, struct seq_file *seq);
void (*bat_neigh_free)(struct batadv_neigh_node *neigh);
/* orig_node handling API */
void (*bat_orig_print)(struct batadv_priv *priv, struct seq_file *seq,
@@ -1224,8 +1256,6 @@ struct batadv_dat_candidate {
* struct batadv_tvlv_container - container for tvlv appended to OGMs
* @list: hlist node for batadv_priv_tvlv::container_list
* @tvlv_hdr: tvlv header information needed to construct the tvlv
- * @value_len: length of the buffer following this struct which contains
- * the actual tvlv payload
* @refcount: number of contexts the object is used
*/
struct batadv_tvlv_container {
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 9e9cca3689a0..d040365ba98e 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -825,9 +825,7 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev)
list_add_rcu(&(*dev)->list, &bt_6lowpan_devices);
spin_unlock(&devices_lock);
- lowpan_netdev_setup(netdev, LOWPAN_LLTYPE_BTLE);
-
- err = register_netdev(netdev);
+ err = lowpan_register_netdev(netdev, LOWPAN_LLTYPE_BTLE);
if (err < 0) {
BT_INFO("register_netdev failed %d", err);
spin_lock(&devices_lock);
@@ -890,7 +888,7 @@ static void delete_netdev(struct work_struct *work)
struct lowpan_dev *entry = container_of(work, struct lowpan_dev,
delete_netdev);
- unregister_netdev(entry->netdev);
+ lowpan_unregister_netdev(entry->netdev);
/* The entry pointer is deleted by the netdev destructor. */
}
@@ -1348,7 +1346,7 @@ static void disconnect_devices(void)
ifdown(entry->netdev);
BT_DBG("Unregistering netdev %s %p",
entry->netdev->name, entry->netdev);
- unregister_netdev(entry->netdev);
+ lowpan_unregister_netdev(entry->netdev);
kfree(entry);
}
}
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index a3bffd1ec2b4..955eda93e66f 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -33,8 +33,6 @@
#include "selftest.h"
-#define VERSION "2.21"
-
/* Bluetooth sockets */
#define BT_MAX_PROTO 8
static const struct net_proto_family *bt_proto[BT_MAX_PROTO];
@@ -176,20 +174,20 @@ EXPORT_SYMBOL(bt_accept_unlink);
struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
{
- struct list_head *p, *n;
+ struct bt_sock *s, *n;
struct sock *sk;
BT_DBG("parent %p", parent);
- list_for_each_safe(p, n, &bt_sk(parent)->accept_q) {
- sk = (struct sock *) list_entry(p, struct bt_sock, accept_q);
+ list_for_each_entry_safe(s, n, &bt_sk(parent)->accept_q, accept_q) {
+ sk = (struct sock *)s;
lock_sock(sk);
/* FIXME: Is this check still needed */
if (sk->sk_state == BT_CLOSED) {
- release_sock(sk);
bt_accept_unlink(sk);
+ release_sock(sk);
continue;
}
@@ -271,11 +269,11 @@ static long bt_sock_data_wait(struct sock *sk, long timeo)
if (signal_pending(current) || !timeo)
break;
- set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+ sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);
release_sock(sk);
timeo = schedule_timeout(timeo);
lock_sock(sk);
- clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+ sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk);
}
__set_current_state(TASK_RUNNING);
@@ -390,11 +388,11 @@ EXPORT_SYMBOL(bt_sock_stream_recvmsg);
static inline unsigned int bt_accept_poll(struct sock *parent)
{
- struct list_head *p, *n;
+ struct bt_sock *s, *n;
struct sock *sk;
- list_for_each_safe(p, n, &bt_sk(parent)->accept_q) {
- sk = (struct sock *) list_entry(p, struct bt_sock, accept_q);
+ list_for_each_entry_safe(s, n, &bt_sk(parent)->accept_q, accept_q) {
+ sk = (struct sock *)s;
if (sk->sk_state == BT_CONNECTED ||
(test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags) &&
sk->sk_state == BT_CONNECT2))
@@ -441,7 +439,7 @@ unsigned int bt_sock_poll(struct file *file, struct socket *sock,
if (!test_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags) && sock_writeable(sk))
mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
else
- set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+ sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
return mask;
}
@@ -671,7 +669,7 @@ static const struct file_operations bt_fops = {
};
int bt_procfs_init(struct net *net, const char *name,
- struct bt_sock_list* sk_list,
+ struct bt_sock_list *sk_list,
int (* seq_show)(struct seq_file *, void *))
{
sk_list->custom_seq_show = seq_show;
@@ -687,7 +685,7 @@ void bt_procfs_cleanup(struct net *net, const char *name)
}
#else
int bt_procfs_init(struct net *net, const char *name,
- struct bt_sock_list* sk_list,
+ struct bt_sock_list *sk_list,
int (* seq_show)(struct seq_file *, void *))
{
return 0;
@@ -715,7 +713,7 @@ static int __init bt_init(void)
sock_skb_cb_check_size(sizeof(struct bt_skb_cb));
- BT_INFO("Core ver %s", VERSION);
+ BT_INFO("Core ver %s", BT_SUBSYS_VERSION);
err = bt_selftest();
if (err < 0)
@@ -789,7 +787,7 @@ subsys_initcall(bt_init);
module_exit(bt_exit);
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
-MODULE_DESCRIPTION("Bluetooth Core ver " VERSION);
-MODULE_VERSION(VERSION);
+MODULE_DESCRIPTION("Bluetooth Core ver " BT_SUBSYS_VERSION);
+MODULE_VERSION(BT_SUBSYS_VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS_NETPROTO(PF_BLUETOOTH);
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index 1641367e54ca..fbf251fef70f 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -608,8 +608,11 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
s->msg.msg_flags = MSG_NOSIGNAL;
#ifdef CONFIG_BT_BNEP_MC_FILTER
- /* Set default mc filter */
- set_bit(bnep_mc_hash(dev->broadcast), (ulong *) &s->mc_filter);
+ /* Set default mc filter to not filter out any mc addresses
+ * as defined in the BNEP specification (revision 0.95a)
+ * http://grouper.ieee.org/groups/802/15/Bluetooth/BNEP.pdf
+ */
+ s->mc_filter = ~0LL;
#endif
#ifdef CONFIG_BT_BNEP_PROTO_FILTER
diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c
index 9a50338772f3..46ac686c8911 100644
--- a/net/bluetooth/cmtp/capi.c
+++ b/net/bluetooth/cmtp/capi.c
@@ -100,10 +100,8 @@ static void cmtp_application_del(struct cmtp_session *session, struct cmtp_appli
static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value)
{
struct cmtp_application *app;
- struct list_head *p;
- list_for_each(p, &session->applications) {
- app = list_entry(p, struct cmtp_application, list);
+ list_for_each_entry(app, &session->applications, list) {
switch (pattern) {
case CMTP_MSGNUM:
if (app->msgnum == value)
@@ -511,14 +509,12 @@ static int cmtp_proc_show(struct seq_file *m, void *v)
struct capi_ctr *ctrl = m->private;
struct cmtp_session *session = ctrl->driverdata;
struct cmtp_application *app;
- struct list_head *p;
seq_printf(m, "%s\n\n", cmtp_procinfo(ctrl));
seq_printf(m, "addr %s\n", session->name);
seq_printf(m, "ctrl %d\n", session->num);
- list_for_each(p, &session->applications) {
- app = list_entry(p, struct cmtp_application, list);
+ list_for_each_entry(app, &session->applications, list) {
seq_printf(m, "appl %d -> %d\n", app->appl, app->mapping);
}
diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c
index 298ed37010e6..9e59b6654126 100644
--- a/net/bluetooth/cmtp/core.c
+++ b/net/bluetooth/cmtp/core.c
@@ -178,8 +178,7 @@ static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *
cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
break;
default:
- if (session->reassembly[id] != NULL)
- kfree_skb(session->reassembly[id]);
+ kfree_skb(session->reassembly[id]);
session->reassembly[id] = NULL;
break;
}
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 85b82f7adbd2..32575b49f4a0 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -178,6 +178,10 @@ static void hci_connect_le_scan_remove(struct hci_conn *conn)
hci_dev_hold(conn->hdev);
hci_conn_get(conn);
+ /* Even though we hold a reference to the hdev, many other
+ * things might get cleaned up meanwhile, including the hdev's
+ * own workqueue, so we can't use that for scheduling.
+ */
schedule_work(&conn->le_scan_cleanup);
}
@@ -664,8 +668,16 @@ void hci_le_conn_failed(struct hci_conn *conn, u8 status)
conn->state = BT_CLOSED;
- mgmt_connect_failed(hdev, &conn->dst, conn->type, conn->dst_type,
- status);
+ /* If the status indicates successful cancellation of
+ * the attempt (i.e. Unkown Connection Id) there's no point of
+ * notifying failure since we'll go back to keep trying to
+ * connect. The only exception is explicit connect requests
+ * where a timeout + cancel does indicate an actual failure.
+ */
+ if (status != HCI_ERROR_UNKNOWN_CONN_ID ||
+ (params && params->explicit_connect))
+ mgmt_connect_failed(hdev, &conn->dst, conn->type,
+ conn->dst_type, status);
hci_connect_cfm(conn, status);
@@ -679,7 +691,7 @@ void hci_le_conn_failed(struct hci_conn *conn, u8 status)
/* Re-enable advertising in case this was a failed connection
* attempt as a peripheral.
*/
- mgmt_reenable_advertising(hdev);
+ hci_req_reenable_advertising(hdev);
}
static void create_le_conn_complete(struct hci_dev *hdev, u8 status, u16 opcode)
@@ -722,8 +734,12 @@ static void hci_req_add_le_create_conn(struct hci_request *req,
if (hci_update_random_address(req, false, &own_addr_type))
return;
+ /* Set window to be the same value as the interval to enable
+ * continuous scanning.
+ */
cp.scan_interval = cpu_to_le16(hdev->le_scan_interval);
- cp.scan_window = cpu_to_le16(hdev->le_scan_window);
+ cp.scan_window = cp.scan_interval;
+
bacpy(&cp.peer_addr, &conn->dst);
cp.peer_addr_type = conn->dst_type;
cp.own_address_type = own_addr_type;
@@ -781,7 +797,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
u8 role)
{
struct hci_conn_params *params;
- struct hci_conn *conn, *conn_unfinished;
+ struct hci_conn *conn;
struct smp_irk *irk;
struct hci_request req;
int err;
@@ -794,35 +810,22 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
return ERR_PTR(-EOPNOTSUPP);
}
- /* Some devices send ATT messages as soon as the physical link is
- * established. To be able to handle these ATT messages, the user-
- * space first establishes the connection and then starts the pairing
- * process.
- *
- * So if a hci_conn object already exists for the following connection
- * attempt, we simply update pending_sec_level and auth_type fields
- * and return the object found.
- */
- conn = hci_conn_hash_lookup_le(hdev, dst, dst_type);
- conn_unfinished = NULL;
- if (conn) {
- if (conn->state == BT_CONNECT &&
- test_bit(HCI_CONN_SCANNING, &conn->flags)) {
- BT_DBG("will continue unfinished conn %pMR", dst);
- conn_unfinished = conn;
- } else {
- if (conn->pending_sec_level < sec_level)
- conn->pending_sec_level = sec_level;
- goto done;
- }
- }
-
/* Since the controller supports only one LE connection attempt at a
* time, we return -EBUSY if there is any connection attempt running.
*/
if (hci_lookup_le_connect(hdev))
return ERR_PTR(-EBUSY);
+ /* If there's already a connection object but it's not in
+ * scanning state it means it must already be established, in
+ * which case we can't do anything else except report a failure
+ * to connect.
+ */
+ conn = hci_conn_hash_lookup_le(hdev, dst, dst_type);
+ if (conn && !test_bit(HCI_CONN_SCANNING, &conn->flags)) {
+ return ERR_PTR(-EBUSY);
+ }
+
/* When given an identity address with existing identity
* resolving key, the connection needs to be established
* to a resolvable random address.
@@ -838,23 +841,20 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
dst_type = ADDR_LE_DEV_RANDOM;
}
- if (conn_unfinished) {
- conn = conn_unfinished;
+ if (conn) {
bacpy(&conn->dst, dst);
} else {
conn = hci_conn_add(hdev, LE_LINK, dst, role);
+ if (!conn)
+ return ERR_PTR(-ENOMEM);
+ hci_conn_hold(conn);
+ conn->pending_sec_level = sec_level;
}
- if (!conn)
- return ERR_PTR(-ENOMEM);
-
conn->dst_type = dst_type;
conn->sec_level = BT_SECURITY_LOW;
conn->conn_timeout = conn_timeout;
- if (!conn_unfinished)
- conn->pending_sec_level = sec_level;
-
hci_req_init(&req, hdev);
/* Disable advertising if we're active. For master role
@@ -918,37 +918,9 @@ create_conn:
return ERR_PTR(err);
}
-done:
- /* If this is continuation of connect started by hci_connect_le_scan,
- * it already called hci_conn_hold and calling it again would mess the
- * counter.
- */
- if (!conn_unfinished)
- hci_conn_hold(conn);
-
return conn;
}
-static void hci_connect_le_scan_complete(struct hci_dev *hdev, u8 status,
- u16 opcode)
-{
- struct hci_conn *conn;
-
- if (!status)
- return;
-
- BT_ERR("Failed to add device to auto conn whitelist: status 0x%2.2x",
- status);
-
- hci_dev_lock(hdev);
-
- conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
- if (conn)
- hci_le_conn_failed(conn, status);
-
- hci_dev_unlock(hdev);
-}
-
static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
{
struct hci_conn *conn;
@@ -964,10 +936,9 @@ static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
}
/* This function requires the caller holds hdev->lock */
-static int hci_explicit_conn_params_set(struct hci_request *req,
+static int hci_explicit_conn_params_set(struct hci_dev *hdev,
bdaddr_t *addr, u8 addr_type)
{
- struct hci_dev *hdev = req->hdev;
struct hci_conn_params *params;
if (is_connected(hdev, addr, addr_type))
@@ -995,7 +966,6 @@ static int hci_explicit_conn_params_set(struct hci_request *req,
}
params->explicit_connect = true;
- __hci_update_background_scan(req);
BT_DBG("addr %pMR (type %u) auto_connect %u", addr, addr_type,
params->auto_connect);
@@ -1006,11 +976,9 @@ static int hci_explicit_conn_params_set(struct hci_request *req,
/* This function requires the caller holds hdev->lock */
struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
u8 dst_type, u8 sec_level,
- u16 conn_timeout, u8 role)
+ u16 conn_timeout)
{
struct hci_conn *conn;
- struct hci_request req;
- int err;
/* Let's make sure that le is enabled.*/
if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) {
@@ -1038,29 +1006,22 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
BT_DBG("requesting refresh of dst_addr");
- conn = hci_conn_add(hdev, LE_LINK, dst, role);
+ conn = hci_conn_add(hdev, LE_LINK, dst, HCI_ROLE_MASTER);
if (!conn)
return ERR_PTR(-ENOMEM);
- hci_req_init(&req, hdev);
-
- if (hci_explicit_conn_params_set(&req, dst, dst_type) < 0)
+ if (hci_explicit_conn_params_set(hdev, dst, dst_type) < 0)
return ERR_PTR(-EBUSY);
conn->state = BT_CONNECT;
set_bit(HCI_CONN_SCANNING, &conn->flags);
-
- err = hci_req_run(&req, hci_connect_le_scan_complete);
- if (err && err != -ENODATA) {
- hci_conn_del(conn);
- return ERR_PTR(err);
- }
-
conn->dst_type = dst_type;
conn->sec_level = BT_SECURITY_LOW;
conn->pending_sec_level = sec_level;
conn->conn_timeout = conn_timeout;
+ hci_update_background_scan(hdev);
+
done:
hci_conn_hold(conn);
return conn;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 62edbf1b114e..47bcef754796 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -56,15 +56,6 @@ DEFINE_MUTEX(hci_cb_list_lock);
/* HCI ID Numbering */
static DEFINE_IDA(hci_index_ida);
-/* ----- HCI requests ----- */
-
-#define HCI_REQ_DONE 0
-#define HCI_REQ_PEND 1
-#define HCI_REQ_CANCELED 2
-
-#define hci_req_lock(d) mutex_lock(&d->req_lock)
-#define hci_req_unlock(d) mutex_unlock(&d->req_lock)
-
/* ---- HCI debugfs entries ---- */
static ssize_t dut_mode_read(struct file *file, char __user *user_buf,
@@ -73,7 +64,7 @@ static ssize_t dut_mode_read(struct file *file, char __user *user_buf,
struct hci_dev *hdev = file->private_data;
char buf[3];
- buf[0] = hci_dev_test_flag(hdev, HCI_DUT_MODE) ? 'Y': 'N';
+ buf[0] = hci_dev_test_flag(hdev, HCI_DUT_MODE) ? 'Y' : 'N';
buf[1] = '\n';
buf[2] = '\0';
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
@@ -101,14 +92,14 @@ static ssize_t dut_mode_write(struct file *file, const char __user *user_buf,
if (enable == hci_dev_test_flag(hdev, HCI_DUT_MODE))
return -EALREADY;
- hci_req_lock(hdev);
+ hci_req_sync_lock(hdev);
if (enable)
skb = __hci_cmd_sync(hdev, HCI_OP_ENABLE_DUT_MODE, 0, NULL,
HCI_CMD_TIMEOUT);
else
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL,
HCI_CMD_TIMEOUT);
- hci_req_unlock(hdev);
+ hci_req_sync_unlock(hdev);
if (IS_ERR(skb))
return PTR_ERR(skb);
@@ -133,7 +124,7 @@ static ssize_t vendor_diag_read(struct file *file, char __user *user_buf,
struct hci_dev *hdev = file->private_data;
char buf[3];
- buf[0] = hci_dev_test_flag(hdev, HCI_VENDOR_DIAG) ? 'Y': 'N';
+ buf[0] = hci_dev_test_flag(hdev, HCI_VENDOR_DIAG) ? 'Y' : 'N';
buf[1] = '\n';
buf[2] = '\0';
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
@@ -165,9 +156,9 @@ static ssize_t vendor_diag_write(struct file *file, const char __user *user_buf,
!test_bit(HCI_RUNNING, &hdev->flags))
goto done;
- hci_req_lock(hdev);
+ hci_req_sync_lock(hdev);
err = hdev->set_diag(hdev, enable);
- hci_req_unlock(hdev);
+ hci_req_sync_unlock(hdev);
if (err < 0)
return err;
@@ -198,197 +189,14 @@ static void hci_debugfs_create_basic(struct hci_dev *hdev)
&vendor_diag_fops);
}
-/* ---- HCI requests ---- */
-
-static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
- struct sk_buff *skb)
-{
- BT_DBG("%s result 0x%2.2x", hdev->name, result);
-
- if (hdev->req_status == HCI_REQ_PEND) {
- hdev->req_result = result;
- hdev->req_status = HCI_REQ_DONE;
- if (skb)
- hdev->req_skb = skb_get(skb);
- wake_up_interruptible(&hdev->req_wait_q);
- }
-}
-
-static void hci_req_cancel(struct hci_dev *hdev, int err)
-{
- BT_DBG("%s err 0x%2.2x", hdev->name, err);
-
- if (hdev->req_status == HCI_REQ_PEND) {
- hdev->req_result = err;
- hdev->req_status = HCI_REQ_CANCELED;
- wake_up_interruptible(&hdev->req_wait_q);
- }
-}
-
-struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
- const void *param, u8 event, u32 timeout)
-{
- DECLARE_WAITQUEUE(wait, current);
- struct hci_request req;
- struct sk_buff *skb;
- int err = 0;
-
- BT_DBG("%s", hdev->name);
-
- hci_req_init(&req, hdev);
-
- hci_req_add_ev(&req, opcode, plen, param, event);
-
- hdev->req_status = HCI_REQ_PEND;
-
- add_wait_queue(&hdev->req_wait_q, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
-
- err = hci_req_run_skb(&req, hci_req_sync_complete);
- if (err < 0) {
- remove_wait_queue(&hdev->req_wait_q, &wait);
- set_current_state(TASK_RUNNING);
- return ERR_PTR(err);
- }
-
- schedule_timeout(timeout);
-
- remove_wait_queue(&hdev->req_wait_q, &wait);
-
- if (signal_pending(current))
- return ERR_PTR(-EINTR);
-
- switch (hdev->req_status) {
- case HCI_REQ_DONE:
- err = -bt_to_errno(hdev->req_result);
- break;
-
- case HCI_REQ_CANCELED:
- err = -hdev->req_result;
- break;
-
- default:
- err = -ETIMEDOUT;
- break;
- }
-
- hdev->req_status = hdev->req_result = 0;
- skb = hdev->req_skb;
- hdev->req_skb = NULL;
-
- BT_DBG("%s end: err %d", hdev->name, err);
-
- if (err < 0) {
- kfree_skb(skb);
- return ERR_PTR(err);
- }
-
- if (!skb)
- return ERR_PTR(-ENODATA);
-
- return skb;
-}
-EXPORT_SYMBOL(__hci_cmd_sync_ev);
-
-struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
- const void *param, u32 timeout)
-{
- return __hci_cmd_sync_ev(hdev, opcode, plen, param, 0, timeout);
-}
-EXPORT_SYMBOL(__hci_cmd_sync);
-
-/* Execute request and wait for completion. */
-static int __hci_req_sync(struct hci_dev *hdev,
- void (*func)(struct hci_request *req,
- unsigned long opt),
- unsigned long opt, __u32 timeout)
-{
- struct hci_request req;
- DECLARE_WAITQUEUE(wait, current);
- int err = 0;
-
- BT_DBG("%s start", hdev->name);
-
- hci_req_init(&req, hdev);
-
- hdev->req_status = HCI_REQ_PEND;
-
- func(&req, opt);
-
- add_wait_queue(&hdev->req_wait_q, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
-
- err = hci_req_run_skb(&req, hci_req_sync_complete);
- if (err < 0) {
- hdev->req_status = 0;
-
- remove_wait_queue(&hdev->req_wait_q, &wait);
- set_current_state(TASK_RUNNING);
-
- /* ENODATA means the HCI request command queue is empty.
- * This can happen when a request with conditionals doesn't
- * trigger any commands to be sent. This is normal behavior
- * and should not trigger an error return.
- */
- if (err == -ENODATA)
- return 0;
-
- return err;
- }
-
- schedule_timeout(timeout);
-
- remove_wait_queue(&hdev->req_wait_q, &wait);
-
- if (signal_pending(current))
- return -EINTR;
-
- switch (hdev->req_status) {
- case HCI_REQ_DONE:
- err = -bt_to_errno(hdev->req_result);
- break;
-
- case HCI_REQ_CANCELED:
- err = -hdev->req_result;
- break;
-
- default:
- err = -ETIMEDOUT;
- break;
- }
-
- hdev->req_status = hdev->req_result = 0;
-
- BT_DBG("%s end: err %d", hdev->name, err);
-
- return err;
-}
-
-static int hci_req_sync(struct hci_dev *hdev,
- void (*req)(struct hci_request *req,
- unsigned long opt),
- unsigned long opt, __u32 timeout)
-{
- int ret;
-
- if (!test_bit(HCI_UP, &hdev->flags))
- return -ENETDOWN;
-
- /* Serialize all requests */
- hci_req_lock(hdev);
- ret = __hci_req_sync(hdev, req, opt, timeout);
- hci_req_unlock(hdev);
-
- return ret;
-}
-
-static void hci_reset_req(struct hci_request *req, unsigned long opt)
+static int hci_reset_req(struct hci_request *req, unsigned long opt)
{
BT_DBG("%s %ld", req->hdev->name, opt);
/* Reset device */
set_bit(HCI_RESET, &req->hdev->flags);
hci_req_add(req, HCI_OP_RESET, 0, NULL);
+ return 0;
}
static void bredr_init(struct hci_request *req)
@@ -428,7 +236,7 @@ static void amp_init1(struct hci_request *req)
hci_req_add(req, HCI_OP_READ_LOCATION_DATA, 0, NULL);
}
-static void amp_init2(struct hci_request *req)
+static int amp_init2(struct hci_request *req)
{
/* Read Local Supported Features. Not all AMP controllers
* support this so it's placed conditionally in the second
@@ -436,9 +244,11 @@ static void amp_init2(struct hci_request *req)
*/
if (req->hdev->commands[14] & 0x20)
hci_req_add(req, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
+
+ return 0;
}
-static void hci_init1_req(struct hci_request *req, unsigned long opt)
+static int hci_init1_req(struct hci_request *req, unsigned long opt)
{
struct hci_dev *hdev = req->hdev;
@@ -461,6 +271,8 @@ static void hci_init1_req(struct hci_request *req, unsigned long opt)
BT_ERR("Unknown device type %d", hdev->dev_type);
break;
}
+
+ return 0;
}
static void bredr_setup(struct hci_request *req)
@@ -531,20 +343,30 @@ static void hci_setup_event_mask(struct hci_request *req)
if (lmp_bredr_capable(hdev)) {
events[4] |= 0x01; /* Flow Specification Complete */
- events[4] |= 0x02; /* Inquiry Result with RSSI */
- events[4] |= 0x04; /* Read Remote Extended Features Complete */
- events[5] |= 0x08; /* Synchronous Connection Complete */
- events[5] |= 0x10; /* Synchronous Connection Changed */
} else {
/* Use a different default for LE-only devices */
memset(events, 0, sizeof(events));
- events[0] |= 0x10; /* Disconnection Complete */
- events[1] |= 0x08; /* Read Remote Version Information Complete */
events[1] |= 0x20; /* Command Complete */
events[1] |= 0x40; /* Command Status */
events[1] |= 0x80; /* Hardware Error */
- events[2] |= 0x04; /* Number of Completed Packets */
- events[3] |= 0x02; /* Data Buffer Overflow */
+
+ /* If the controller supports the Disconnect command, enable
+ * the corresponding event. In addition enable packet flow
+ * control related events.
+ */
+ if (hdev->commands[0] & 0x20) {
+ events[0] |= 0x10; /* Disconnection Complete */
+ events[2] |= 0x04; /* Number of Completed Packets */
+ events[3] |= 0x02; /* Data Buffer Overflow */
+ }
+
+ /* If the controller supports the Read Remote Version
+ * Information command, enable the corresponding event.
+ */
+ if (hdev->commands[2] & 0x80)
+ events[1] |= 0x08; /* Read Remote Version Information
+ * Complete
+ */
if (hdev->le_features[0] & HCI_LE_ENCRYPTION) {
events[0] |= 0x80; /* Encryption Change */
@@ -552,9 +374,18 @@ static void hci_setup_event_mask(struct hci_request *req)
}
}
- if (lmp_inq_rssi_capable(hdev))
+ if (lmp_inq_rssi_capable(hdev) ||
+ test_bit(HCI_QUIRK_FIXUP_INQUIRY_MODE, &hdev->quirks))
events[4] |= 0x02; /* Inquiry Result with RSSI */
+ if (lmp_ext_feat_capable(hdev))
+ events[4] |= 0x04; /* Read Remote Extended Features Complete */
+
+ if (lmp_esco_capable(hdev)) {
+ events[5] |= 0x08; /* Synchronous Connection Complete */
+ events[5] |= 0x10; /* Synchronous Connection Changed */
+ }
+
if (lmp_sniffsubr_capable(hdev))
events[5] |= 0x20; /* Sniff Subrating */
@@ -590,7 +421,7 @@ static void hci_setup_event_mask(struct hci_request *req)
hci_req_add(req, HCI_OP_SET_EVENT_MASK, sizeof(events), events);
}
-static void hci_init2_req(struct hci_request *req, unsigned long opt)
+static int hci_init2_req(struct hci_request *req, unsigned long opt)
{
struct hci_dev *hdev = req->hdev;
@@ -670,6 +501,8 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt)
hci_req_add(req, HCI_OP_WRITE_AUTH_ENABLE, sizeof(enable),
&enable);
}
+
+ return 0;
}
static void hci_setup_link_policy(struct hci_request *req)
@@ -744,7 +577,7 @@ static void hci_set_event_mask_page_2(struct hci_request *req)
hci_req_add(req, HCI_OP_SET_EVENT_MASK_PAGE_2, sizeof(events), events);
}
-static void hci_init3_req(struct hci_request *req, unsigned long opt)
+static int hci_init3_req(struct hci_request *req, unsigned long opt)
{
struct hci_dev *hdev = req->hdev;
u8 p;
@@ -777,7 +610,6 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
u8 events[8];
memset(events, 0, sizeof(events));
- events[0] = 0x0f;
if (hdev->le_features[0] & HCI_LE_ENCRYPTION)
events[0] |= 0x10; /* LE Long Term Key Request */
@@ -804,6 +636,34 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
* Report
*/
+ /* If the controller supports the LE Set Scan Enable command,
+ * enable the corresponding advertising report event.
+ */
+ if (hdev->commands[26] & 0x08)
+ events[0] |= 0x02; /* LE Advertising Report */
+
+ /* If the controller supports the LE Create Connection
+ * command, enable the corresponding event.
+ */
+ if (hdev->commands[26] & 0x10)
+ events[0] |= 0x01; /* LE Connection Complete */
+
+ /* If the controller supports the LE Connection Update
+ * command, enable the corresponding event.
+ */
+ if (hdev->commands[27] & 0x04)
+ events[0] |= 0x04; /* LE Connection Update
+ * Complete
+ */
+
+ /* If the controller supports the LE Read Remote Used Features
+ * command, enable the corresponding event.
+ */
+ if (hdev->commands[27] & 0x20)
+ events[0] |= 0x08; /* LE Read Remote Used
+ * Features Complete
+ */
+
/* If the controller supports the LE Read Local P-256
* Public Key command, enable the corresponding event.
*/
@@ -856,9 +716,11 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
hci_req_add(req, HCI_OP_READ_LOCAL_EXT_FEATURES,
sizeof(cp), &cp);
}
+
+ return 0;
}
-static void hci_init4_req(struct hci_request *req, unsigned long opt)
+static int hci_init4_req(struct hci_request *req, unsigned long opt)
{
struct hci_dev *hdev = req->hdev;
@@ -909,20 +771,22 @@ static void hci_init4_req(struct hci_request *req, unsigned long opt)
hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT,
sizeof(support), &support);
}
+
+ return 0;
}
static int __hci_init(struct hci_dev *hdev)
{
int err;
- err = __hci_req_sync(hdev, hci_init1_req, 0, HCI_INIT_TIMEOUT);
+ err = __hci_req_sync(hdev, hci_init1_req, 0, HCI_INIT_TIMEOUT, NULL);
if (err < 0)
return err;
if (hci_dev_test_flag(hdev, HCI_SETUP))
hci_debugfs_create_basic(hdev);
- err = __hci_req_sync(hdev, hci_init2_req, 0, HCI_INIT_TIMEOUT);
+ err = __hci_req_sync(hdev, hci_init2_req, 0, HCI_INIT_TIMEOUT, NULL);
if (err < 0)
return err;
@@ -933,11 +797,11 @@ static int __hci_init(struct hci_dev *hdev)
if (hdev->dev_type != HCI_BREDR)
return 0;
- err = __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT);
+ err = __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT, NULL);
if (err < 0)
return err;
- err = __hci_req_sync(hdev, hci_init4_req, 0, HCI_INIT_TIMEOUT);
+ err = __hci_req_sync(hdev, hci_init4_req, 0, HCI_INIT_TIMEOUT, NULL);
if (err < 0)
return err;
@@ -968,7 +832,7 @@ static int __hci_init(struct hci_dev *hdev)
return 0;
}
-static void hci_init0_req(struct hci_request *req, unsigned long opt)
+static int hci_init0_req(struct hci_request *req, unsigned long opt)
{
struct hci_dev *hdev = req->hdev;
@@ -984,6 +848,8 @@ static void hci_init0_req(struct hci_request *req, unsigned long opt)
/* Read BD Address */
if (hdev->set_bdaddr)
hci_req_add(req, HCI_OP_READ_BD_ADDR, 0, NULL);
+
+ return 0;
}
static int __hci_unconf_init(struct hci_dev *hdev)
@@ -993,7 +859,7 @@ static int __hci_unconf_init(struct hci_dev *hdev)
if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
return 0;
- err = __hci_req_sync(hdev, hci_init0_req, 0, HCI_INIT_TIMEOUT);
+ err = __hci_req_sync(hdev, hci_init0_req, 0, HCI_INIT_TIMEOUT, NULL);
if (err < 0)
return err;
@@ -1003,7 +869,7 @@ static int __hci_unconf_init(struct hci_dev *hdev)
return 0;
}
-static void hci_scan_req(struct hci_request *req, unsigned long opt)
+static int hci_scan_req(struct hci_request *req, unsigned long opt)
{
__u8 scan = opt;
@@ -1011,9 +877,10 @@ static void hci_scan_req(struct hci_request *req, unsigned long opt)
/* Inquiry and Page scans */
hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+ return 0;
}
-static void hci_auth_req(struct hci_request *req, unsigned long opt)
+static int hci_auth_req(struct hci_request *req, unsigned long opt)
{
__u8 auth = opt;
@@ -1021,9 +888,10 @@ static void hci_auth_req(struct hci_request *req, unsigned long opt)
/* Authentication */
hci_req_add(req, HCI_OP_WRITE_AUTH_ENABLE, 1, &auth);
+ return 0;
}
-static void hci_encrypt_req(struct hci_request *req, unsigned long opt)
+static int hci_encrypt_req(struct hci_request *req, unsigned long opt)
{
__u8 encrypt = opt;
@@ -1031,9 +899,10 @@ static void hci_encrypt_req(struct hci_request *req, unsigned long opt)
/* Encryption */
hci_req_add(req, HCI_OP_WRITE_ENCRYPT_MODE, 1, &encrypt);
+ return 0;
}
-static void hci_linkpol_req(struct hci_request *req, unsigned long opt)
+static int hci_linkpol_req(struct hci_request *req, unsigned long opt)
{
__le16 policy = cpu_to_le16(opt);
@@ -1041,6 +910,7 @@ static void hci_linkpol_req(struct hci_request *req, unsigned long opt)
/* Default link policy */
hci_req_add(req, HCI_OP_WRITE_DEF_LINK_POLICY, 2, &policy);
+ return 0;
}
/* Get HCI device by index.
@@ -1285,7 +1155,7 @@ static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf)
return copied;
}
-static void hci_inq_req(struct hci_request *req, unsigned long opt)
+static int hci_inq_req(struct hci_request *req, unsigned long opt)
{
struct hci_inquiry_req *ir = (struct hci_inquiry_req *) opt;
struct hci_dev *hdev = req->hdev;
@@ -1294,13 +1164,15 @@ static void hci_inq_req(struct hci_request *req, unsigned long opt)
BT_DBG("%s", hdev->name);
if (test_bit(HCI_INQUIRY, &hdev->flags))
- return;
+ return 0;
/* Start Inquiry */
memcpy(&cp.lap, &ir->lap, 3);
cp.length = ir->length;
cp.num_rsp = ir->num_rsp;
hci_req_add(req, HCI_OP_INQUIRY, sizeof(cp), &cp);
+
+ return 0;
}
int hci_inquiry(void __user *arg)
@@ -1351,7 +1223,7 @@ int hci_inquiry(void __user *arg)
if (do_inquiry) {
err = hci_req_sync(hdev, hci_inq_req, (unsigned long) &ir,
- timeo);
+ timeo, NULL);
if (err < 0)
goto done;
@@ -1404,7 +1276,7 @@ static int hci_dev_do_open(struct hci_dev *hdev)
BT_DBG("%s %p", hdev->name, hdev);
- hci_req_lock(hdev);
+ hci_req_sync_lock(hdev);
if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
ret = -ENODEV;
@@ -1527,10 +1399,10 @@ static int hci_dev_do_open(struct hci_dev *hdev)
!hci_dev_test_flag(hdev, HCI_CONFIG) &&
!hci_dev_test_flag(hdev, HCI_UNCONFIGURED) &&
!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
+ hci_dev_test_flag(hdev, HCI_MGMT) &&
hdev->dev_type == HCI_BREDR) {
- hci_dev_lock(hdev);
- mgmt_powered(hdev, 1);
- hci_dev_unlock(hdev);
+ ret = __hci_req_hci_power_on(hdev);
+ mgmt_power_on(hdev, ret);
}
} else {
/* Init failed, cleanup */
@@ -1557,7 +1429,7 @@ static int hci_dev_do_open(struct hci_dev *hdev)
}
done:
- hci_req_unlock(hdev);
+ hci_req_sync_unlock(hdev);
return ret;
}
@@ -1651,12 +1523,12 @@ int hci_dev_do_close(struct hci_dev *hdev)
cancel_delayed_work(&hdev->power_off);
- hci_req_cancel(hdev, ENODEV);
- hci_req_lock(hdev);
+ hci_request_cancel_all(hdev);
+ hci_req_sync_lock(hdev);
if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
cancel_delayed_work_sync(&hdev->cmd_timer);
- hci_req_unlock(hdev);
+ hci_req_sync_unlock(hdev);
return 0;
}
@@ -1665,7 +1537,6 @@ int hci_dev_do_close(struct hci_dev *hdev)
flush_work(&hdev->rx_work);
if (hdev->discov_timeout > 0) {
- cancel_delayed_work(&hdev->discov_off);
hdev->discov_timeout = 0;
hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
@@ -1674,17 +1545,9 @@ int hci_dev_do_close(struct hci_dev *hdev)
if (hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE))
cancel_delayed_work(&hdev->service_cache);
- cancel_delayed_work_sync(&hdev->le_scan_disable);
- cancel_delayed_work_sync(&hdev->le_scan_restart);
-
if (hci_dev_test_flag(hdev, HCI_MGMT))
cancel_delayed_work_sync(&hdev->rpa_expired);
- if (hdev->adv_instance_timeout) {
- cancel_delayed_work_sync(&hdev->adv_instance_expire);
- hdev->adv_instance_timeout = 0;
- }
-
/* Avoid potential lockdep warnings from the *_flush() calls by
* ensuring the workqueue is empty up front.
*/
@@ -1696,8 +1559,9 @@ int hci_dev_do_close(struct hci_dev *hdev)
auto_off = hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF);
- if (!auto_off && hdev->dev_type == HCI_BREDR)
- mgmt_powered(hdev, 0);
+ if (!auto_off && hdev->dev_type == HCI_BREDR &&
+ hci_dev_test_flag(hdev, HCI_MGMT))
+ __mgmt_power_off(hdev);
hci_inquiry_cache_flush(hdev);
hci_pend_le_actions_clear(hdev);
@@ -1717,7 +1581,7 @@ int hci_dev_do_close(struct hci_dev *hdev)
if (test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks) &&
!auto_off && !hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
set_bit(HCI_INIT, &hdev->flags);
- __hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT);
+ __hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT, NULL);
clear_bit(HCI_INIT, &hdev->flags);
}
@@ -1754,7 +1618,7 @@ int hci_dev_do_close(struct hci_dev *hdev)
memset(hdev->dev_class, 0, sizeof(hdev->dev_class));
bacpy(&hdev->random_addr, BDADDR_ANY);
- hci_req_unlock(hdev);
+ hci_req_sync_unlock(hdev);
hci_dev_put(hdev);
return 0;
@@ -1790,7 +1654,7 @@ static int hci_dev_do_reset(struct hci_dev *hdev)
BT_DBG("%s %p", hdev->name, hdev);
- hci_req_lock(hdev);
+ hci_req_sync_lock(hdev);
/* Drop queues */
skb_queue_purge(&hdev->rx_q);
@@ -1812,9 +1676,9 @@ static int hci_dev_do_reset(struct hci_dev *hdev)
atomic_set(&hdev->cmd_cnt, 1);
hdev->acl_cnt = 0; hdev->sco_cnt = 0; hdev->le_cnt = 0;
- ret = __hci_req_sync(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT);
+ ret = __hci_req_sync(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT, NULL);
- hci_req_unlock(hdev);
+ hci_req_sync_unlock(hdev);
return ret;
}
@@ -1905,7 +1769,7 @@ static void hci_update_scan_state(struct hci_dev *hdev, u8 scan)
hci_dev_set_flag(hdev, HCI_BREDR_ENABLED);
if (hci_dev_test_flag(hdev, HCI_LE_ENABLED))
- mgmt_update_adv_data(hdev);
+ hci_req_update_adv_data(hdev, hdev->cur_adv_instance);
mgmt_new_settings(hdev);
}
@@ -1947,7 +1811,7 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
switch (cmd) {
case HCISETAUTH:
err = hci_req_sync(hdev, hci_auth_req, dr.dev_opt,
- HCI_INIT_TIMEOUT);
+ HCI_INIT_TIMEOUT, NULL);
break;
case HCISETENCRYPT:
@@ -1959,18 +1823,18 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
if (!test_bit(HCI_AUTH, &hdev->flags)) {
/* Auth must be enabled first */
err = hci_req_sync(hdev, hci_auth_req, dr.dev_opt,
- HCI_INIT_TIMEOUT);
+ HCI_INIT_TIMEOUT, NULL);
if (err)
break;
}
err = hci_req_sync(hdev, hci_encrypt_req, dr.dev_opt,
- HCI_INIT_TIMEOUT);
+ HCI_INIT_TIMEOUT, NULL);
break;
case HCISETSCAN:
err = hci_req_sync(hdev, hci_scan_req, dr.dev_opt,
- HCI_INIT_TIMEOUT);
+ HCI_INIT_TIMEOUT, NULL);
/* Ensure that the connectable and discoverable states
* get correctly modified as this was a non-mgmt change.
@@ -1981,7 +1845,7 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
case HCISETLINKPOL:
err = hci_req_sync(hdev, hci_linkpol_req, dr.dev_opt,
- HCI_INIT_TIMEOUT);
+ HCI_INIT_TIMEOUT, NULL);
break;
case HCISETLINKMODE:
@@ -2150,6 +2014,16 @@ static void hci_power_on(struct work_struct *work)
BT_DBG("%s", hdev->name);
+ if (test_bit(HCI_UP, &hdev->flags) &&
+ hci_dev_test_flag(hdev, HCI_MGMT) &&
+ hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) {
+ hci_req_sync_lock(hdev);
+ err = __hci_req_hci_power_on(hdev);
+ hci_req_sync_unlock(hdev);
+ mgmt_power_on(hdev, err);
+ return;
+ }
+
err = hci_dev_do_open(hdev);
if (err < 0) {
hci_dev_lock(hdev);
@@ -2232,28 +2106,6 @@ static void hci_error_reset(struct work_struct *work)
hci_dev_do_open(hdev);
}
-static void hci_discov_off(struct work_struct *work)
-{
- struct hci_dev *hdev;
-
- hdev = container_of(work, struct hci_dev, discov_off.work);
-
- BT_DBG("%s", hdev->name);
-
- mgmt_discoverable_timeout(hdev);
-}
-
-static void hci_adv_timeout_expire(struct work_struct *work)
-{
- struct hci_dev *hdev;
-
- hdev = container_of(work, struct hci_dev, adv_instance_expire.work);
-
- BT_DBG("%s", hdev->name);
-
- mgmt_adv_timeout_expired(hdev);
-}
-
void hci_uuids_clear(struct hci_dev *hdev)
{
struct bt_uuid *uuid, *tmp;
@@ -2731,7 +2583,8 @@ struct adv_info *hci_find_adv_instance(struct hci_dev *hdev, u8 instance)
}
/* This function requires the caller holds hdev->lock */
-struct adv_info *hci_get_next_instance(struct hci_dev *hdev, u8 instance) {
+struct adv_info *hci_get_next_instance(struct hci_dev *hdev, u8 instance)
+{
struct adv_info *cur_instance;
cur_instance = hci_find_adv_instance(hdev, instance);
@@ -2757,9 +2610,12 @@ int hci_remove_adv_instance(struct hci_dev *hdev, u8 instance)
BT_DBG("%s removing %dMR", hdev->name, instance);
- if (hdev->cur_adv_instance == instance && hdev->adv_instance_timeout) {
- cancel_delayed_work(&hdev->adv_instance_expire);
- hdev->adv_instance_timeout = 0;
+ if (hdev->cur_adv_instance == instance) {
+ if (hdev->adv_instance_timeout) {
+ cancel_delayed_work(&hdev->adv_instance_expire);
+ hdev->adv_instance_timeout = 0;
+ }
+ hdev->cur_adv_instance = 0x00;
}
list_del(&adv_instance->list);
@@ -2786,6 +2642,7 @@ void hci_adv_instances_clear(struct hci_dev *hdev)
}
hdev->adv_instance_cnt = 0;
+ hdev->cur_adv_instance = 0x00;
}
/* This function requires the caller holds hdev->lock */
@@ -2856,12 +2713,10 @@ struct bdaddr_list *hci_bdaddr_list_lookup(struct list_head *bdaddr_list,
void hci_bdaddr_list_clear(struct list_head *bdaddr_list)
{
- struct list_head *p, *n;
+ struct bdaddr_list *b, *n;
- list_for_each_safe(p, n, bdaddr_list) {
- struct bdaddr_list *b = list_entry(p, struct bdaddr_list, list);
-
- list_del(p);
+ list_for_each_entry_safe(b, n, bdaddr_list, list) {
+ list_del(&b->list);
kfree(b);
}
}
@@ -3024,181 +2879,16 @@ void hci_conn_params_clear_disabled(struct hci_dev *hdev)
}
/* This function requires the caller holds hdev->lock */
-void hci_conn_params_clear_all(struct hci_dev *hdev)
+static void hci_conn_params_clear_all(struct hci_dev *hdev)
{
struct hci_conn_params *params, *tmp;
list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list)
hci_conn_params_free(params);
- hci_update_background_scan(hdev);
-
BT_DBG("All LE connection parameters were removed");
}
-static void inquiry_complete(struct hci_dev *hdev, u8 status, u16 opcode)
-{
- if (status) {
- BT_ERR("Failed to start inquiry: status %d", status);
-
- hci_dev_lock(hdev);
- hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
- hci_dev_unlock(hdev);
- return;
- }
-}
-
-static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status,
- u16 opcode)
-{
- /* General inquiry access code (GIAC) */
- u8 lap[3] = { 0x33, 0x8b, 0x9e };
- struct hci_cp_inquiry cp;
- int err;
-
- if (status) {
- BT_ERR("Failed to disable LE scanning: status %d", status);
- return;
- }
-
- hdev->discovery.scan_start = 0;
-
- switch (hdev->discovery.type) {
- case DISCOV_TYPE_LE:
- hci_dev_lock(hdev);
- hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
- hci_dev_unlock(hdev);
- break;
-
- case DISCOV_TYPE_INTERLEAVED:
- hci_dev_lock(hdev);
-
- if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
- &hdev->quirks)) {
- /* If we were running LE only scan, change discovery
- * state. If we were running both LE and BR/EDR inquiry
- * simultaneously, and BR/EDR inquiry is already
- * finished, stop discovery, otherwise BR/EDR inquiry
- * will stop discovery when finished. If we will resolve
- * remote device name, do not change discovery state.
- */
- if (!test_bit(HCI_INQUIRY, &hdev->flags) &&
- hdev->discovery.state != DISCOVERY_RESOLVING)
- hci_discovery_set_state(hdev,
- DISCOVERY_STOPPED);
- } else {
- struct hci_request req;
-
- hci_inquiry_cache_flush(hdev);
-
- hci_req_init(&req, hdev);
-
- memset(&cp, 0, sizeof(cp));
- memcpy(&cp.lap, lap, sizeof(cp.lap));
- cp.length = DISCOV_INTERLEAVED_INQUIRY_LEN;
- hci_req_add(&req, HCI_OP_INQUIRY, sizeof(cp), &cp);
-
- err = hci_req_run(&req, inquiry_complete);
- if (err) {
- BT_ERR("Inquiry request failed: err %d", err);
- hci_discovery_set_state(hdev,
- DISCOVERY_STOPPED);
- }
- }
-
- hci_dev_unlock(hdev);
- break;
- }
-}
-
-static void le_scan_disable_work(struct work_struct *work)
-{
- struct hci_dev *hdev = container_of(work, struct hci_dev,
- le_scan_disable.work);
- struct hci_request req;
- int err;
-
- BT_DBG("%s", hdev->name);
-
- cancel_delayed_work_sync(&hdev->le_scan_restart);
-
- hci_req_init(&req, hdev);
-
- hci_req_add_le_scan_disable(&req);
-
- err = hci_req_run(&req, le_scan_disable_work_complete);
- if (err)
- BT_ERR("Disable LE scanning request failed: err %d", err);
-}
-
-static void le_scan_restart_work_complete(struct hci_dev *hdev, u8 status,
- u16 opcode)
-{
- unsigned long timeout, duration, scan_start, now;
-
- BT_DBG("%s", hdev->name);
-
- if (status) {
- BT_ERR("Failed to restart LE scan: status %d", status);
- return;
- }
-
- if (!test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks) ||
- !hdev->discovery.scan_start)
- return;
-
- /* When the scan was started, hdev->le_scan_disable has been queued
- * after duration from scan_start. During scan restart this job
- * has been canceled, and we need to queue it again after proper
- * timeout, to make sure that scan does not run indefinitely.
- */
- duration = hdev->discovery.scan_duration;
- scan_start = hdev->discovery.scan_start;
- now = jiffies;
- if (now - scan_start <= duration) {
- int elapsed;
-
- if (now >= scan_start)
- elapsed = now - scan_start;
- else
- elapsed = ULONG_MAX - scan_start + now;
-
- timeout = duration - elapsed;
- } else {
- timeout = 0;
- }
- queue_delayed_work(hdev->workqueue,
- &hdev->le_scan_disable, timeout);
-}
-
-static void le_scan_restart_work(struct work_struct *work)
-{
- struct hci_dev *hdev = container_of(work, struct hci_dev,
- le_scan_restart.work);
- struct hci_request req;
- struct hci_cp_le_set_scan_enable cp;
- int err;
-
- BT_DBG("%s", hdev->name);
-
- /* If controller is not scanning we are done. */
- if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
- return;
-
- hci_req_init(&req, hdev);
-
- hci_req_add_le_scan_disable(&req);
-
- memset(&cp, 0, sizeof(cp));
- cp.enable = LE_SCAN_ENABLE;
- cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
- hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
-
- err = hci_req_run(&req, le_scan_restart_work_complete);
- if (err)
- BT_ERR("Restart LE scan request failed: err %d", err);
-}
-
/* Copy the Identity Address of the controller.
*
* If the controller has a public BD_ADDR, then by default use that one.
@@ -3297,10 +2987,6 @@ struct hci_dev *hci_alloc_dev(void)
INIT_WORK(&hdev->error_reset, hci_error_reset);
INIT_DELAYED_WORK(&hdev->power_off, hci_power_off);
- INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off);
- INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work);
- INIT_DELAYED_WORK(&hdev->le_scan_restart, le_scan_restart_work);
- INIT_DELAYED_WORK(&hdev->adv_instance_expire, hci_adv_timeout_expire);
skb_queue_head_init(&hdev->rx_q);
skb_queue_head_init(&hdev->cmd_q);
@@ -3310,6 +2996,8 @@ struct hci_dev *hci_alloc_dev(void)
INIT_DELAYED_WORK(&hdev->cmd_timer, hci_cmd_timeout);
+ hci_request_setup(hdev);
+
hci_init_sysfs(hdev);
discovery_init(hdev);
@@ -3520,7 +3208,7 @@ int hci_reset_dev(struct hci_dev *hdev)
if (!skb)
return -ENOMEM;
- bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+ hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
memcpy(skb_put(skb, 3), hw_err, 3);
/* Send Hardware Error to upper stack */
@@ -3537,9 +3225,9 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
return -ENXIO;
}
- if (bt_cb(skb)->pkt_type != HCI_EVENT_PKT &&
- bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
- bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) {
+ if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT &&
+ hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
+ hci_skb_pkt_type(skb) != HCI_SCODATA_PKT) {
kfree_skb(skb);
return -EINVAL;
}
@@ -3561,7 +3249,7 @@ EXPORT_SYMBOL(hci_recv_frame);
int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb)
{
/* Mark as diagnostic packet */
- bt_cb(skb)->pkt_type = HCI_DIAG_PKT;
+ hci_skb_pkt_type(skb) = HCI_DIAG_PKT;
/* Time stamp */
__net_timestamp(skb);
@@ -3603,7 +3291,8 @@ static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
int err;
- BT_DBG("%s type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
+ BT_DBG("%s type %d len %d", hdev->name, hci_skb_pkt_type(skb),
+ skb->len);
/* Time stamp */
__net_timestamp(skb);
@@ -3648,7 +3337,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
/* Stand-alone HCI commands must be flagged as
* single-command requests.
*/
- bt_cb(skb)->hci.req_start = true;
+ bt_cb(skb)->hci.req_flags |= HCI_REQ_START;
skb_queue_tail(&hdev->cmd_q, skb);
queue_work(hdev->workqueue, &hdev->cmd_work);
@@ -3685,9 +3374,9 @@ struct sk_buff *hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
bt_dev_dbg(hdev, "opcode 0x%4.4x plen %d", opcode, plen);
- hci_req_lock(hdev);
+ hci_req_sync_lock(hdev);
skb = __hci_cmd_sync(hdev, opcode, plen, param, timeout);
- hci_req_unlock(hdev);
+ hci_req_sync_unlock(hdev);
return skb;
}
@@ -3716,7 +3405,7 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,
skb->len = skb_headlen(skb);
skb->data_len = 0;
- bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
+ hci_skb_pkt_type(skb) = HCI_ACLDATA_PKT;
switch (hdev->dev_type) {
case HCI_BREDR:
@@ -3756,7 +3445,7 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,
do {
skb = list; list = list->next;
- bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
+ hci_skb_pkt_type(skb) = HCI_ACLDATA_PKT;
hci_add_acl_hdr(skb, conn->handle, flags);
BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
@@ -3794,7 +3483,7 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
skb_reset_transport_header(skb);
memcpy(skb_transport_header(skb), &hdr, HCI_SCO_HDR_SIZE);
- bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
+ hci_skb_pkt_type(skb) = HCI_SCODATA_PKT;
skb_queue_tail(&conn->data_q, skb);
queue_work(hdev->workqueue, &hdev->tx_work);
@@ -4345,7 +4034,7 @@ static bool hci_req_is_complete(struct hci_dev *hdev)
if (!skb)
return true;
- return bt_cb(skb)->hci.req_start;
+ return (bt_cb(skb)->hci.req_flags & HCI_REQ_START);
}
static void hci_resend_last(struct hci_dev *hdev)
@@ -4405,20 +4094,20 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
* callback would be found in hdev->sent_cmd instead of the
* command queue (hdev->cmd_q).
*/
- if (bt_cb(hdev->sent_cmd)->hci.req_complete) {
- *req_complete = bt_cb(hdev->sent_cmd)->hci.req_complete;
+ if (bt_cb(hdev->sent_cmd)->hci.req_flags & HCI_REQ_SKB) {
+ *req_complete_skb = bt_cb(hdev->sent_cmd)->hci.req_complete_skb;
return;
}
- if (bt_cb(hdev->sent_cmd)->hci.req_complete_skb) {
- *req_complete_skb = bt_cb(hdev->sent_cmd)->hci.req_complete_skb;
+ if (bt_cb(hdev->sent_cmd)->hci.req_complete) {
+ *req_complete = bt_cb(hdev->sent_cmd)->hci.req_complete;
return;
}
/* Remove all pending commands belonging to this request */
spin_lock_irqsave(&hdev->cmd_q.lock, flags);
while ((skb = __skb_dequeue(&hdev->cmd_q))) {
- if (bt_cb(skb)->hci.req_start) {
+ if (bt_cb(skb)->hci.req_flags & HCI_REQ_START) {
__skb_queue_head(&hdev->cmd_q, skb);
break;
}
@@ -4453,7 +4142,7 @@ static void hci_rx_work(struct work_struct *work)
if (test_bit(HCI_INIT, &hdev->flags)) {
/* Don't process data packets in this states. */
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_ACLDATA_PKT:
case HCI_SCODATA_PKT:
kfree_skb(skb);
@@ -4462,7 +4151,7 @@ static void hci_rx_work(struct work_struct *work)
}
/* Process frame */
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_EVENT_PKT:
BT_DBG("%s Event packet", hdev->name);
hci_event_packet(hdev, skb);
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index d57c11c1c6b5..c162af5d16bf 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -1183,7 +1183,7 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
else if (!hci_dev_test_flag(hdev, HCI_LE_ADV) &&
hdev->discovery.state == DISCOVERY_FINDING)
- mgmt_reenable_advertising(hdev);
+ hci_req_reenable_advertising(hdev);
break;
@@ -2176,7 +2176,7 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES,
sizeof(cp), &cp);
- hci_update_page_scan(hdev);
+ hci_req_update_scan(hdev);
}
/* Set packet type for incoming connection */
@@ -2362,7 +2362,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
if (test_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
hci_remove_link_key(hdev, &conn->dst);
- hci_update_page_scan(hdev);
+ hci_req_update_scan(hdev);
}
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
@@ -2401,7 +2401,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
* is timed out due to Directed Advertising."
*/
if (type == LE_LINK)
- mgmt_reenable_advertising(hdev);
+ hci_req_reenable_advertising(hdev);
unlock:
hci_dev_unlock(hdev);
@@ -3833,9 +3833,9 @@ static void hci_extended_inquiry_result_evt(struct hci_dev *hdev,
data.ssp_mode = 0x01;
if (hci_dev_test_flag(hdev, HCI_MGMT))
- name_known = eir_has_data_type(info->data,
- sizeof(info->data),
- EIR_NAME_COMPLETE);
+ name_known = eir_get_data(info->data,
+ sizeof(info->data),
+ EIR_NAME_COMPLETE, NULL);
else
name_known = true;
diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
index 981f8a202c27..41b5f3813f02 100644
--- a/net/bluetooth/hci_request.c
+++ b/net/bluetooth/hci_request.c
@@ -21,12 +21,19 @@
SOFTWARE IS DISCLAIMED.
*/
+#include <asm/unaligned.h>
+
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/mgmt.h>
#include "smp.h"
#include "hci_request.h"
+#define HCI_REQ_DONE 0
+#define HCI_REQ_PEND 1
+#define HCI_REQ_CANCELED 2
+
void hci_req_init(struct hci_request *req, struct hci_dev *hdev)
{
skb_queue_head_init(&req->cmd_q);
@@ -56,8 +63,12 @@ static int req_run(struct hci_request *req, hci_req_complete_t complete,
return -ENODATA;
skb = skb_peek_tail(&req->cmd_q);
- bt_cb(skb)->hci.req_complete = complete;
- bt_cb(skb)->hci.req_complete_skb = complete_skb;
+ if (complete) {
+ bt_cb(skb)->hci.req_complete = complete;
+ } else if (complete_skb) {
+ bt_cb(skb)->hci.req_complete_skb = complete_skb;
+ bt_cb(skb)->hci.req_flags |= HCI_REQ_SKB;
+ }
spin_lock_irqsave(&hdev->cmd_q.lock, flags);
skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q);
@@ -78,6 +89,203 @@ int hci_req_run_skb(struct hci_request *req, hci_req_complete_skb_t complete)
return req_run(req, NULL, complete);
}
+static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
+ struct sk_buff *skb)
+{
+ BT_DBG("%s result 0x%2.2x", hdev->name, result);
+
+ if (hdev->req_status == HCI_REQ_PEND) {
+ hdev->req_result = result;
+ hdev->req_status = HCI_REQ_DONE;
+ if (skb)
+ hdev->req_skb = skb_get(skb);
+ wake_up_interruptible(&hdev->req_wait_q);
+ }
+}
+
+void hci_req_sync_cancel(struct hci_dev *hdev, int err)
+{
+ BT_DBG("%s err 0x%2.2x", hdev->name, err);
+
+ if (hdev->req_status == HCI_REQ_PEND) {
+ hdev->req_result = err;
+ hdev->req_status = HCI_REQ_CANCELED;
+ wake_up_interruptible(&hdev->req_wait_q);
+ }
+}
+
+struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
+ const void *param, u8 event, u32 timeout)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct hci_request req;
+ struct sk_buff *skb;
+ int err = 0;
+
+ BT_DBG("%s", hdev->name);
+
+ hci_req_init(&req, hdev);
+
+ hci_req_add_ev(&req, opcode, plen, param, event);
+
+ hdev->req_status = HCI_REQ_PEND;
+
+ add_wait_queue(&hdev->req_wait_q, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ err = hci_req_run_skb(&req, hci_req_sync_complete);
+ if (err < 0) {
+ remove_wait_queue(&hdev->req_wait_q, &wait);
+ set_current_state(TASK_RUNNING);
+ return ERR_PTR(err);
+ }
+
+ schedule_timeout(timeout);
+
+ remove_wait_queue(&hdev->req_wait_q, &wait);
+
+ if (signal_pending(current))
+ return ERR_PTR(-EINTR);
+
+ switch (hdev->req_status) {
+ case HCI_REQ_DONE:
+ err = -bt_to_errno(hdev->req_result);
+ break;
+
+ case HCI_REQ_CANCELED:
+ err = -hdev->req_result;
+ break;
+
+ default:
+ err = -ETIMEDOUT;
+ break;
+ }
+
+ hdev->req_status = hdev->req_result = 0;
+ skb = hdev->req_skb;
+ hdev->req_skb = NULL;
+
+ BT_DBG("%s end: err %d", hdev->name, err);
+
+ if (err < 0) {
+ kfree_skb(skb);
+ return ERR_PTR(err);
+ }
+
+ if (!skb)
+ return ERR_PTR(-ENODATA);
+
+ return skb;
+}
+EXPORT_SYMBOL(__hci_cmd_sync_ev);
+
+struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
+ const void *param, u32 timeout)
+{
+ return __hci_cmd_sync_ev(hdev, opcode, plen, param, 0, timeout);
+}
+EXPORT_SYMBOL(__hci_cmd_sync);
+
+/* Execute request and wait for completion. */
+int __hci_req_sync(struct hci_dev *hdev, int (*func)(struct hci_request *req,
+ unsigned long opt),
+ unsigned long opt, u32 timeout, u8 *hci_status)
+{
+ struct hci_request req;
+ DECLARE_WAITQUEUE(wait, current);
+ int err = 0;
+
+ BT_DBG("%s start", hdev->name);
+
+ hci_req_init(&req, hdev);
+
+ hdev->req_status = HCI_REQ_PEND;
+
+ err = func(&req, opt);
+ if (err) {
+ if (hci_status)
+ *hci_status = HCI_ERROR_UNSPECIFIED;
+ return err;
+ }
+
+ add_wait_queue(&hdev->req_wait_q, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ err = hci_req_run_skb(&req, hci_req_sync_complete);
+ if (err < 0) {
+ hdev->req_status = 0;
+
+ remove_wait_queue(&hdev->req_wait_q, &wait);
+ set_current_state(TASK_RUNNING);
+
+ /* ENODATA means the HCI request command queue is empty.
+ * This can happen when a request with conditionals doesn't
+ * trigger any commands to be sent. This is normal behavior
+ * and should not trigger an error return.
+ */
+ if (err == -ENODATA) {
+ if (hci_status)
+ *hci_status = 0;
+ return 0;
+ }
+
+ if (hci_status)
+ *hci_status = HCI_ERROR_UNSPECIFIED;
+
+ return err;
+ }
+
+ schedule_timeout(timeout);
+
+ remove_wait_queue(&hdev->req_wait_q, &wait);
+
+ if (signal_pending(current))
+ return -EINTR;
+
+ switch (hdev->req_status) {
+ case HCI_REQ_DONE:
+ err = -bt_to_errno(hdev->req_result);
+ if (hci_status)
+ *hci_status = hdev->req_result;
+ break;
+
+ case HCI_REQ_CANCELED:
+ err = -hdev->req_result;
+ if (hci_status)
+ *hci_status = HCI_ERROR_UNSPECIFIED;
+ break;
+
+ default:
+ err = -ETIMEDOUT;
+ if (hci_status)
+ *hci_status = HCI_ERROR_UNSPECIFIED;
+ break;
+ }
+
+ hdev->req_status = hdev->req_result = 0;
+
+ BT_DBG("%s end: err %d", hdev->name, err);
+
+ return err;
+}
+
+int hci_req_sync(struct hci_dev *hdev, int (*req)(struct hci_request *req,
+ unsigned long opt),
+ unsigned long opt, u32 timeout, u8 *hci_status)
+{
+ int ret;
+
+ if (!test_bit(HCI_UP, &hdev->flags))
+ return -ENETDOWN;
+
+ /* Serialize all requests */
+ hci_req_sync_lock(hdev);
+ ret = __hci_req_sync(hdev, req, opt, timeout, hci_status);
+ hci_req_sync_unlock(hdev);
+
+ return ret;
+}
+
struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param)
{
@@ -98,8 +306,8 @@ struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
BT_DBG("skb len %d", skb->len);
- bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
- bt_cb(skb)->hci.opcode = opcode;
+ hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
+ hci_skb_opcode(skb) = opcode;
return skb;
}
@@ -128,7 +336,7 @@ void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
}
if (skb_queue_empty(&req->cmd_q))
- bt_cb(skb)->hci.req_start = true;
+ bt_cb(skb)->hci.req_flags |= HCI_REQ_START;
bt_cb(skb)->hci.req_event = event;
@@ -141,6 +349,311 @@ void hci_req_add(struct hci_request *req, u16 opcode, u32 plen,
hci_req_add_ev(req, opcode, plen, param, 0);
}
+void __hci_req_write_fast_connectable(struct hci_request *req, bool enable)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_write_page_scan_activity acp;
+ u8 type;
+
+ if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
+ return;
+
+ if (hdev->hci_ver < BLUETOOTH_VER_1_2)
+ return;
+
+ if (enable) {
+ type = PAGE_SCAN_TYPE_INTERLACED;
+
+ /* 160 msec page scan interval */
+ acp.interval = cpu_to_le16(0x0100);
+ } else {
+ type = PAGE_SCAN_TYPE_STANDARD; /* default */
+
+ /* default 1.28 sec page scan */
+ acp.interval = cpu_to_le16(0x0800);
+ }
+
+ acp.window = cpu_to_le16(0x0012);
+
+ if (__cpu_to_le16(hdev->page_scan_interval) != acp.interval ||
+ __cpu_to_le16(hdev->page_scan_window) != acp.window)
+ hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY,
+ sizeof(acp), &acp);
+
+ if (hdev->page_scan_type != type)
+ hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
+}
+
+/* This function controls the background scanning based on hdev->pend_le_conns
+ * list. If there are pending LE connection we start the background scanning,
+ * otherwise we stop it.
+ *
+ * This function requires the caller holds hdev->lock.
+ */
+static void __hci_update_background_scan(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+
+ if (!test_bit(HCI_UP, &hdev->flags) ||
+ test_bit(HCI_INIT, &hdev->flags) ||
+ hci_dev_test_flag(hdev, HCI_SETUP) ||
+ hci_dev_test_flag(hdev, HCI_CONFIG) ||
+ hci_dev_test_flag(hdev, HCI_AUTO_OFF) ||
+ hci_dev_test_flag(hdev, HCI_UNREGISTER))
+ return;
+
+ /* No point in doing scanning if LE support hasn't been enabled */
+ if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
+ return;
+
+ /* If discovery is active don't interfere with it */
+ if (hdev->discovery.state != DISCOVERY_STOPPED)
+ return;
+
+ /* Reset RSSI and UUID filters when starting background scanning
+ * since these filters are meant for service discovery only.
+ *
+ * The Start Discovery and Start Service Discovery operations
+ * ensure to set proper values for RSSI threshold and UUID
+ * filter list. So it is safe to just reset them here.
+ */
+ hci_discovery_filter_clear(hdev);
+
+ if (list_empty(&hdev->pend_le_conns) &&
+ list_empty(&hdev->pend_le_reports)) {
+ /* If there is no pending LE connections or devices
+ * to be scanned for, we should stop the background
+ * scanning.
+ */
+
+ /* If controller is not scanning we are done. */
+ if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
+ return;
+
+ hci_req_add_le_scan_disable(req);
+
+ BT_DBG("%s stopping background scanning", hdev->name);
+ } else {
+ /* If there is at least one pending LE connection, we should
+ * keep the background scan running.
+ */
+
+ /* If controller is connecting, we should not start scanning
+ * since some controllers are not able to scan and connect at
+ * the same time.
+ */
+ if (hci_lookup_le_connect(hdev))
+ return;
+
+ /* If controller is currently scanning, we stop it to ensure we
+ * don't miss any advertising (due to duplicates filter).
+ */
+ if (hci_dev_test_flag(hdev, HCI_LE_SCAN))
+ hci_req_add_le_scan_disable(req);
+
+ hci_req_add_le_passive_scan(req);
+
+ BT_DBG("%s starting background scanning", hdev->name);
+ }
+}
+
+void __hci_req_update_name(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_write_local_name cp;
+
+ memcpy(cp.name, hdev->dev_name, sizeof(cp.name));
+
+ hci_req_add(req, HCI_OP_WRITE_LOCAL_NAME, sizeof(cp), &cp);
+}
+
+#define PNP_INFO_SVCLASS_ID 0x1200
+
+static u8 *create_uuid16_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
+{
+ u8 *ptr = data, *uuids_start = NULL;
+ struct bt_uuid *uuid;
+
+ if (len < 4)
+ return ptr;
+
+ list_for_each_entry(uuid, &hdev->uuids, list) {
+ u16 uuid16;
+
+ if (uuid->size != 16)
+ continue;
+
+ uuid16 = get_unaligned_le16(&uuid->uuid[12]);
+ if (uuid16 < 0x1100)
+ continue;
+
+ if (uuid16 == PNP_INFO_SVCLASS_ID)
+ continue;
+
+ if (!uuids_start) {
+ uuids_start = ptr;
+ uuids_start[0] = 1;
+ uuids_start[1] = EIR_UUID16_ALL;
+ ptr += 2;
+ }
+
+ /* Stop if not enough space to put next UUID */
+ if ((ptr - data) + sizeof(u16) > len) {
+ uuids_start[1] = EIR_UUID16_SOME;
+ break;
+ }
+
+ *ptr++ = (uuid16 & 0x00ff);
+ *ptr++ = (uuid16 & 0xff00) >> 8;
+ uuids_start[0] += sizeof(uuid16);
+ }
+
+ return ptr;
+}
+
+static u8 *create_uuid32_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
+{
+ u8 *ptr = data, *uuids_start = NULL;
+ struct bt_uuid *uuid;
+
+ if (len < 6)
+ return ptr;
+
+ list_for_each_entry(uuid, &hdev->uuids, list) {
+ if (uuid->size != 32)
+ continue;
+
+ if (!uuids_start) {
+ uuids_start = ptr;
+ uuids_start[0] = 1;
+ uuids_start[1] = EIR_UUID32_ALL;
+ ptr += 2;
+ }
+
+ /* Stop if not enough space to put next UUID */
+ if ((ptr - data) + sizeof(u32) > len) {
+ uuids_start[1] = EIR_UUID32_SOME;
+ break;
+ }
+
+ memcpy(ptr, &uuid->uuid[12], sizeof(u32));
+ ptr += sizeof(u32);
+ uuids_start[0] += sizeof(u32);
+ }
+
+ return ptr;
+}
+
+static u8 *create_uuid128_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
+{
+ u8 *ptr = data, *uuids_start = NULL;
+ struct bt_uuid *uuid;
+
+ if (len < 18)
+ return ptr;
+
+ list_for_each_entry(uuid, &hdev->uuids, list) {
+ if (uuid->size != 128)
+ continue;
+
+ if (!uuids_start) {
+ uuids_start = ptr;
+ uuids_start[0] = 1;
+ uuids_start[1] = EIR_UUID128_ALL;
+ ptr += 2;
+ }
+
+ /* Stop if not enough space to put next UUID */
+ if ((ptr - data) + 16 > len) {
+ uuids_start[1] = EIR_UUID128_SOME;
+ break;
+ }
+
+ memcpy(ptr, uuid->uuid, 16);
+ ptr += 16;
+ uuids_start[0] += 16;
+ }
+
+ return ptr;
+}
+
+static void create_eir(struct hci_dev *hdev, u8 *data)
+{
+ u8 *ptr = data;
+ size_t name_len;
+
+ name_len = strlen(hdev->dev_name);
+
+ if (name_len > 0) {
+ /* EIR Data type */
+ if (name_len > 48) {
+ name_len = 48;
+ ptr[1] = EIR_NAME_SHORT;
+ } else
+ ptr[1] = EIR_NAME_COMPLETE;
+
+ /* EIR Data length */
+ ptr[0] = name_len + 1;
+
+ memcpy(ptr + 2, hdev->dev_name, name_len);
+
+ ptr += (name_len + 2);
+ }
+
+ if (hdev->inq_tx_power != HCI_TX_POWER_INVALID) {
+ ptr[0] = 2;
+ ptr[1] = EIR_TX_POWER;
+ ptr[2] = (u8) hdev->inq_tx_power;
+
+ ptr += 3;
+ }
+
+ if (hdev->devid_source > 0) {
+ ptr[0] = 9;
+ ptr[1] = EIR_DEVICE_ID;
+
+ put_unaligned_le16(hdev->devid_source, ptr + 2);
+ put_unaligned_le16(hdev->devid_vendor, ptr + 4);
+ put_unaligned_le16(hdev->devid_product, ptr + 6);
+ put_unaligned_le16(hdev->devid_version, ptr + 8);
+
+ ptr += 10;
+ }
+
+ ptr = create_uuid16_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
+ ptr = create_uuid32_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
+ ptr = create_uuid128_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
+}
+
+void __hci_req_update_eir(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_write_eir cp;
+
+ if (!hdev_is_powered(hdev))
+ return;
+
+ if (!lmp_ext_inq_capable(hdev))
+ return;
+
+ if (!hci_dev_test_flag(hdev, HCI_SSP_ENABLED))
+ return;
+
+ if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE))
+ return;
+
+ memset(&cp, 0, sizeof(cp));
+
+ create_eir(hdev, cp.data);
+
+ if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0)
+ return;
+
+ memcpy(hdev->eir, cp.data, sizeof(cp.data));
+
+ hci_req_add(req, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
+}
+
void hci_req_add_le_scan_disable(struct hci_request *req)
{
struct hci_cp_le_set_scan_enable cp;
@@ -302,6 +815,483 @@ void hci_req_add_le_passive_scan(struct hci_request *req)
&enable_cp);
}
+static u8 get_cur_adv_instance_scan_rsp_len(struct hci_dev *hdev)
+{
+ u8 instance = hdev->cur_adv_instance;
+ struct adv_info *adv_instance;
+
+ /* Ignore instance 0 */
+ if (instance == 0x00)
+ return 0;
+
+ adv_instance = hci_find_adv_instance(hdev, instance);
+ if (!adv_instance)
+ return 0;
+
+ /* TODO: Take into account the "appearance" and "local-name" flags here.
+ * These are currently being ignored as they are not supported.
+ */
+ return adv_instance->scan_rsp_len;
+}
+
+void __hci_req_disable_advertising(struct hci_request *req)
+{
+ u8 enable = 0x00;
+
+ hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
+}
+
+static u32 get_adv_instance_flags(struct hci_dev *hdev, u8 instance)
+{
+ u32 flags;
+ struct adv_info *adv_instance;
+
+ if (instance == 0x00) {
+ /* Instance 0 always manages the "Tx Power" and "Flags"
+ * fields
+ */
+ flags = MGMT_ADV_FLAG_TX_POWER | MGMT_ADV_FLAG_MANAGED_FLAGS;
+
+ /* For instance 0, the HCI_ADVERTISING_CONNECTABLE setting
+ * corresponds to the "connectable" instance flag.
+ */
+ if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE))
+ flags |= MGMT_ADV_FLAG_CONNECTABLE;
+
+ return flags;
+ }
+
+ adv_instance = hci_find_adv_instance(hdev, instance);
+
+ /* Return 0 when we got an invalid instance identifier. */
+ if (!adv_instance)
+ return 0;
+
+ return adv_instance->flags;
+}
+
+void __hci_req_enable_advertising(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_le_set_adv_param cp;
+ u8 own_addr_type, enable = 0x01;
+ bool connectable;
+ u32 flags;
+
+ if (hci_conn_num(hdev, LE_LINK) > 0)
+ return;
+
+ if (hci_dev_test_flag(hdev, HCI_LE_ADV))
+ __hci_req_disable_advertising(req);
+
+ /* Clear the HCI_LE_ADV bit temporarily so that the
+ * hci_update_random_address knows that it's safe to go ahead
+ * and write a new random address. The flag will be set back on
+ * as soon as the SET_ADV_ENABLE HCI command completes.
+ */
+ hci_dev_clear_flag(hdev, HCI_LE_ADV);
+
+ flags = get_adv_instance_flags(hdev, hdev->cur_adv_instance);
+
+ /* If the "connectable" instance flag was not set, then choose between
+ * ADV_IND and ADV_NONCONN_IND based on the global connectable setting.
+ */
+ connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE) ||
+ mgmt_get_connectable(hdev);
+
+ /* Set require_privacy to true only when non-connectable
+ * advertising is used. In that case it is fine to use a
+ * non-resolvable private address.
+ */
+ if (hci_update_random_address(req, !connectable, &own_addr_type) < 0)
+ return;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.min_interval = cpu_to_le16(hdev->le_adv_min_interval);
+ cp.max_interval = cpu_to_le16(hdev->le_adv_max_interval);
+
+ if (connectable)
+ cp.type = LE_ADV_IND;
+ else if (get_cur_adv_instance_scan_rsp_len(hdev))
+ cp.type = LE_ADV_SCAN_IND;
+ else
+ cp.type = LE_ADV_NONCONN_IND;
+
+ cp.own_address_type = own_addr_type;
+ cp.channel_map = hdev->le_adv_channel_map;
+
+ hci_req_add(req, HCI_OP_LE_SET_ADV_PARAM, sizeof(cp), &cp);
+
+ hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
+}
+
+static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
+{
+ u8 ad_len = 0;
+ size_t name_len;
+
+ name_len = strlen(hdev->dev_name);
+ if (name_len > 0) {
+ size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
+
+ if (name_len > max_len) {
+ name_len = max_len;
+ ptr[1] = EIR_NAME_SHORT;
+ } else
+ ptr[1] = EIR_NAME_COMPLETE;
+
+ ptr[0] = name_len + 1;
+
+ memcpy(ptr + 2, hdev->dev_name, name_len);
+
+ ad_len += (name_len + 2);
+ ptr += (name_len + 2);
+ }
+
+ return ad_len;
+}
+
+static u8 create_instance_scan_rsp_data(struct hci_dev *hdev, u8 instance,
+ u8 *ptr)
+{
+ struct adv_info *adv_instance;
+
+ adv_instance = hci_find_adv_instance(hdev, instance);
+ if (!adv_instance)
+ return 0;
+
+ /* TODO: Set the appropriate entries based on advertising instance flags
+ * here once flags other than 0 are supported.
+ */
+ memcpy(ptr, adv_instance->scan_rsp_data,
+ adv_instance->scan_rsp_len);
+
+ return adv_instance->scan_rsp_len;
+}
+
+void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_le_set_scan_rsp_data cp;
+ u8 len;
+
+ if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
+ return;
+
+ memset(&cp, 0, sizeof(cp));
+
+ if (instance)
+ len = create_instance_scan_rsp_data(hdev, instance, cp.data);
+ else
+ len = create_default_scan_rsp_data(hdev, cp.data);
+
+ if (hdev->scan_rsp_data_len == len &&
+ !memcmp(cp.data, hdev->scan_rsp_data, len))
+ return;
+
+ memcpy(hdev->scan_rsp_data, cp.data, sizeof(cp.data));
+ hdev->scan_rsp_data_len = len;
+
+ cp.length = len;
+
+ hci_req_add(req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(cp), &cp);
+}
+
+static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
+{
+ struct adv_info *adv_instance = NULL;
+ u8 ad_len = 0, flags = 0;
+ u32 instance_flags;
+
+ /* Return 0 when the current instance identifier is invalid. */
+ if (instance) {
+ adv_instance = hci_find_adv_instance(hdev, instance);
+ if (!adv_instance)
+ return 0;
+ }
+
+ instance_flags = get_adv_instance_flags(hdev, instance);
+
+ /* The Add Advertising command allows userspace to set both the general
+ * and limited discoverable flags.
+ */
+ if (instance_flags & MGMT_ADV_FLAG_DISCOV)
+ flags |= LE_AD_GENERAL;
+
+ if (instance_flags & MGMT_ADV_FLAG_LIMITED_DISCOV)
+ flags |= LE_AD_LIMITED;
+
+ if (flags || (instance_flags & MGMT_ADV_FLAG_MANAGED_FLAGS)) {
+ /* If a discovery flag wasn't provided, simply use the global
+ * settings.
+ */
+ if (!flags)
+ flags |= mgmt_get_adv_discov_flags(hdev);
+
+ if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
+ flags |= LE_AD_NO_BREDR;
+
+ /* If flags would still be empty, then there is no need to
+ * include the "Flags" AD field".
+ */
+ if (flags) {
+ ptr[0] = 0x02;
+ ptr[1] = EIR_FLAGS;
+ ptr[2] = flags;
+
+ ad_len += 3;
+ ptr += 3;
+ }
+ }
+
+ if (adv_instance) {
+ memcpy(ptr, adv_instance->adv_data,
+ adv_instance->adv_data_len);
+ ad_len += adv_instance->adv_data_len;
+ ptr += adv_instance->adv_data_len;
+ }
+
+ /* Provide Tx Power only if we can provide a valid value for it */
+ if (hdev->adv_tx_power != HCI_TX_POWER_INVALID &&
+ (instance_flags & MGMT_ADV_FLAG_TX_POWER)) {
+ ptr[0] = 0x02;
+ ptr[1] = EIR_TX_POWER;
+ ptr[2] = (u8)hdev->adv_tx_power;
+
+ ad_len += 3;
+ ptr += 3;
+ }
+
+ return ad_len;
+}
+
+void __hci_req_update_adv_data(struct hci_request *req, u8 instance)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_le_set_adv_data cp;
+ u8 len;
+
+ if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
+ return;
+
+ memset(&cp, 0, sizeof(cp));
+
+ len = create_instance_adv_data(hdev, instance, cp.data);
+
+ /* There's nothing to do if the data hasn't changed */
+ if (hdev->adv_data_len == len &&
+ memcmp(cp.data, hdev->adv_data, len) == 0)
+ return;
+
+ memcpy(hdev->adv_data, cp.data, sizeof(cp.data));
+ hdev->adv_data_len = len;
+
+ cp.length = len;
+
+ hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
+}
+
+int hci_req_update_adv_data(struct hci_dev *hdev, u8 instance)
+{
+ struct hci_request req;
+
+ hci_req_init(&req, hdev);
+ __hci_req_update_adv_data(&req, instance);
+
+ return hci_req_run(&req, NULL);
+}
+
+static void adv_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode)
+{
+ BT_DBG("%s status %u", hdev->name, status);
+}
+
+void hci_req_reenable_advertising(struct hci_dev *hdev)
+{
+ struct hci_request req;
+
+ if (!hci_dev_test_flag(hdev, HCI_ADVERTISING) &&
+ list_empty(&hdev->adv_instances))
+ return;
+
+ hci_req_init(&req, hdev);
+
+ if (hdev->cur_adv_instance) {
+ __hci_req_schedule_adv_instance(&req, hdev->cur_adv_instance,
+ true);
+ } else {
+ __hci_req_update_adv_data(&req, 0x00);
+ __hci_req_update_scan_rsp_data(&req, 0x00);
+ __hci_req_enable_advertising(&req);
+ }
+
+ hci_req_run(&req, adv_enable_complete);
+}
+
+static void adv_timeout_expire(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ adv_instance_expire.work);
+
+ struct hci_request req;
+ u8 instance;
+
+ BT_DBG("%s", hdev->name);
+
+ hci_dev_lock(hdev);
+
+ hdev->adv_instance_timeout = 0;
+
+ instance = hdev->cur_adv_instance;
+ if (instance == 0x00)
+ goto unlock;
+
+ hci_req_init(&req, hdev);
+
+ hci_req_clear_adv_instance(hdev, &req, instance, false);
+
+ if (list_empty(&hdev->adv_instances))
+ __hci_req_disable_advertising(&req);
+
+ hci_req_run(&req, NULL);
+
+unlock:
+ hci_dev_unlock(hdev);
+}
+
+int __hci_req_schedule_adv_instance(struct hci_request *req, u8 instance,
+ bool force)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct adv_info *adv_instance = NULL;
+ u16 timeout;
+
+ if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
+ list_empty(&hdev->adv_instances))
+ return -EPERM;
+
+ if (hdev->adv_instance_timeout)
+ return -EBUSY;
+
+ adv_instance = hci_find_adv_instance(hdev, instance);
+ if (!adv_instance)
+ return -ENOENT;
+
+ /* A zero timeout means unlimited advertising. As long as there is
+ * only one instance, duration should be ignored. We still set a timeout
+ * in case further instances are being added later on.
+ *
+ * If the remaining lifetime of the instance is more than the duration
+ * then the timeout corresponds to the duration, otherwise it will be
+ * reduced to the remaining instance lifetime.
+ */
+ if (adv_instance->timeout == 0 ||
+ adv_instance->duration <= adv_instance->remaining_time)
+ timeout = adv_instance->duration;
+ else
+ timeout = adv_instance->remaining_time;
+
+ /* The remaining time is being reduced unless the instance is being
+ * advertised without time limit.
+ */
+ if (adv_instance->timeout)
+ adv_instance->remaining_time =
+ adv_instance->remaining_time - timeout;
+
+ hdev->adv_instance_timeout = timeout;
+ queue_delayed_work(hdev->req_workqueue,
+ &hdev->adv_instance_expire,
+ msecs_to_jiffies(timeout * 1000));
+
+ /* If we're just re-scheduling the same instance again then do not
+ * execute any HCI commands. This happens when a single instance is
+ * being advertised.
+ */
+ if (!force && hdev->cur_adv_instance == instance &&
+ hci_dev_test_flag(hdev, HCI_LE_ADV))
+ return 0;
+
+ hdev->cur_adv_instance = instance;
+ __hci_req_update_adv_data(req, instance);
+ __hci_req_update_scan_rsp_data(req, instance);
+ __hci_req_enable_advertising(req);
+
+ return 0;
+}
+
+static void cancel_adv_timeout(struct hci_dev *hdev)
+{
+ if (hdev->adv_instance_timeout) {
+ hdev->adv_instance_timeout = 0;
+ cancel_delayed_work(&hdev->adv_instance_expire);
+ }
+}
+
+/* For a single instance:
+ * - force == true: The instance will be removed even when its remaining
+ * lifetime is not zero.
+ * - force == false: the instance will be deactivated but kept stored unless
+ * the remaining lifetime is zero.
+ *
+ * For instance == 0x00:
+ * - force == true: All instances will be removed regardless of their timeout
+ * setting.
+ * - force == false: Only instances that have a timeout will be removed.
+ */
+void hci_req_clear_adv_instance(struct hci_dev *hdev, struct hci_request *req,
+ u8 instance, bool force)
+{
+ struct adv_info *adv_instance, *n, *next_instance = NULL;
+ int err;
+ u8 rem_inst;
+
+ /* Cancel any timeout concerning the removed instance(s). */
+ if (!instance || hdev->cur_adv_instance == instance)
+ cancel_adv_timeout(hdev);
+
+ /* Get the next instance to advertise BEFORE we remove
+ * the current one. This can be the same instance again
+ * if there is only one instance.
+ */
+ if (instance && hdev->cur_adv_instance == instance)
+ next_instance = hci_get_next_instance(hdev, instance);
+
+ if (instance == 0x00) {
+ list_for_each_entry_safe(adv_instance, n, &hdev->adv_instances,
+ list) {
+ if (!(force || adv_instance->timeout))
+ continue;
+
+ rem_inst = adv_instance->instance;
+ err = hci_remove_adv_instance(hdev, rem_inst);
+ if (!err)
+ mgmt_advertising_removed(NULL, hdev, rem_inst);
+ }
+ } else {
+ adv_instance = hci_find_adv_instance(hdev, instance);
+
+ if (force || (adv_instance && adv_instance->timeout &&
+ !adv_instance->remaining_time)) {
+ /* Don't advertise a removed instance. */
+ if (next_instance &&
+ next_instance->instance == instance)
+ next_instance = NULL;
+
+ err = hci_remove_adv_instance(hdev, instance);
+ if (!err)
+ mgmt_advertising_removed(NULL, hdev, instance);
+ }
+ }
+
+ if (!req || !hdev_is_powered(hdev) ||
+ hci_dev_test_flag(hdev, HCI_ADVERTISING))
+ return;
+
+ if (next_instance)
+ __hci_req_schedule_adv_instance(req, next_instance->instance,
+ false);
+}
+
static void set_random_addr(struct hci_request *req, bdaddr_t *rpa)
{
struct hci_dev *hdev = req->hdev;
@@ -432,7 +1422,7 @@ static bool disconnected_whitelist_entries(struct hci_dev *hdev)
return false;
}
-void __hci_update_page_scan(struct hci_request *req)
+void __hci_req_update_scan(struct hci_request *req)
{
struct hci_dev *hdev = req->hdev;
u8 scan;
@@ -452,117 +1442,168 @@ void __hci_update_page_scan(struct hci_request *req)
else
scan = SCAN_DISABLED;
- if (test_bit(HCI_PSCAN, &hdev->flags) == !!(scan & SCAN_PAGE))
- return;
-
if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE))
scan |= SCAN_INQUIRY;
+ if (test_bit(HCI_PSCAN, &hdev->flags) == !!(scan & SCAN_PAGE) &&
+ test_bit(HCI_ISCAN, &hdev->flags) == !!(scan & SCAN_INQUIRY))
+ return;
+
hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
}
-void hci_update_page_scan(struct hci_dev *hdev)
+static int update_scan(struct hci_request *req, unsigned long opt)
{
- struct hci_request req;
+ hci_dev_lock(req->hdev);
+ __hci_req_update_scan(req);
+ hci_dev_unlock(req->hdev);
+ return 0;
+}
- hci_req_init(&req, hdev);
- __hci_update_page_scan(&req);
- hci_req_run(&req, NULL);
+static void scan_update_work(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev, scan_update);
+
+ hci_req_sync(hdev, update_scan, 0, HCI_CMD_TIMEOUT, NULL);
}
-/* This function controls the background scanning based on hdev->pend_le_conns
- * list. If there are pending LE connection we start the background scanning,
- * otherwise we stop it.
- *
- * This function requires the caller holds hdev->lock.
- */
-void __hci_update_background_scan(struct hci_request *req)
+static int connectable_update(struct hci_request *req, unsigned long opt)
{
struct hci_dev *hdev = req->hdev;
- if (!test_bit(HCI_UP, &hdev->flags) ||
- test_bit(HCI_INIT, &hdev->flags) ||
- hci_dev_test_flag(hdev, HCI_SETUP) ||
- hci_dev_test_flag(hdev, HCI_CONFIG) ||
- hci_dev_test_flag(hdev, HCI_AUTO_OFF) ||
- hci_dev_test_flag(hdev, HCI_UNREGISTER))
+ hci_dev_lock(hdev);
+
+ __hci_req_update_scan(req);
+
+ /* If BR/EDR is not enabled and we disable advertising as a
+ * by-product of disabling connectable, we need to update the
+ * advertising flags.
+ */
+ if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
+ __hci_req_update_adv_data(req, hdev->cur_adv_instance);
+
+ /* Update the advertising parameters if necessary */
+ if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
+ !list_empty(&hdev->adv_instances))
+ __hci_req_enable_advertising(req);
+
+ __hci_update_background_scan(req);
+
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static void connectable_update_work(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ connectable_update);
+ u8 status;
+
+ hci_req_sync(hdev, connectable_update, 0, HCI_CMD_TIMEOUT, &status);
+ mgmt_set_connectable_complete(hdev, status);
+}
+
+static u8 get_service_classes(struct hci_dev *hdev)
+{
+ struct bt_uuid *uuid;
+ u8 val = 0;
+
+ list_for_each_entry(uuid, &hdev->uuids, list)
+ val |= uuid->svc_hint;
+
+ return val;
+}
+
+void __hci_req_update_class(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+ u8 cod[3];
+
+ BT_DBG("%s", hdev->name);
+
+ if (!hdev_is_powered(hdev))
return;
- /* No point in doing scanning if LE support hasn't been enabled */
- if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
+ if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
return;
- /* If discovery is active don't interfere with it */
- if (hdev->discovery.state != DISCOVERY_STOPPED)
+ if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE))
return;
- /* Reset RSSI and UUID filters when starting background scanning
- * since these filters are meant for service discovery only.
- *
- * The Start Discovery and Start Service Discovery operations
- * ensure to set proper values for RSSI threshold and UUID
- * filter list. So it is safe to just reset them here.
- */
- hci_discovery_filter_clear(hdev);
+ cod[0] = hdev->minor_class;
+ cod[1] = hdev->major_class;
+ cod[2] = get_service_classes(hdev);
- if (list_empty(&hdev->pend_le_conns) &&
- list_empty(&hdev->pend_le_reports)) {
- /* If there is no pending LE connections or devices
- * to be scanned for, we should stop the background
- * scanning.
- */
+ if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE))
+ cod[1] |= 0x20;
- /* If controller is not scanning we are done. */
- if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
- return;
+ if (memcmp(cod, hdev->dev_class, 3) == 0)
+ return;
- hci_req_add_le_scan_disable(req);
+ hci_req_add(req, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
+}
- BT_DBG("%s stopping background scanning", hdev->name);
+static void write_iac(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_write_current_iac_lap cp;
+
+ if (!hci_dev_test_flag(hdev, HCI_DISCOVERABLE))
+ return;
+
+ if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE)) {
+ /* Limited discoverable mode */
+ cp.num_iac = min_t(u8, hdev->num_iac, 2);
+ cp.iac_lap[0] = 0x00; /* LIAC */
+ cp.iac_lap[1] = 0x8b;
+ cp.iac_lap[2] = 0x9e;
+ cp.iac_lap[3] = 0x33; /* GIAC */
+ cp.iac_lap[4] = 0x8b;
+ cp.iac_lap[5] = 0x9e;
} else {
- /* If there is at least one pending LE connection, we should
- * keep the background scan running.
- */
+ /* General discoverable mode */
+ cp.num_iac = 1;
+ cp.iac_lap[0] = 0x33; /* GIAC */
+ cp.iac_lap[1] = 0x8b;
+ cp.iac_lap[2] = 0x9e;
+ }
- /* If controller is connecting, we should not start scanning
- * since some controllers are not able to scan and connect at
- * the same time.
- */
- if (hci_lookup_le_connect(hdev))
- return;
+ hci_req_add(req, HCI_OP_WRITE_CURRENT_IAC_LAP,
+ (cp.num_iac * 3) + 1, &cp);
+}
- /* If controller is currently scanning, we stop it to ensure we
- * don't miss any advertising (due to duplicates filter).
- */
- if (hci_dev_test_flag(hdev, HCI_LE_SCAN))
- hci_req_add_le_scan_disable(req);
+static int discoverable_update(struct hci_request *req, unsigned long opt)
+{
+ struct hci_dev *hdev = req->hdev;
- hci_req_add_le_passive_scan(req);
+ hci_dev_lock(hdev);
- BT_DBG("%s starting background scanning", hdev->name);
+ if (hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
+ write_iac(req);
+ __hci_req_update_scan(req);
+ __hci_req_update_class(req);
}
-}
-static void update_background_scan_complete(struct hci_dev *hdev, u8 status,
- u16 opcode)
-{
- if (status)
- BT_DBG("HCI request failed to update background scanning: "
- "status 0x%2.2x", status);
-}
+ /* Advertising instances don't use the global discoverable setting, so
+ * only update AD if advertising was enabled using Set Advertising.
+ */
+ if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
+ __hci_req_update_adv_data(req, 0x00);
-void hci_update_background_scan(struct hci_dev *hdev)
-{
- int err;
- struct hci_request req;
+ hci_dev_unlock(hdev);
- hci_req_init(&req, hdev);
+ return 0;
+}
- __hci_update_background_scan(&req);
+static void discoverable_update_work(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ discoverable_update);
+ u8 status;
- err = hci_req_run(&req, update_background_scan_complete);
- if (err && err != -ENODATA)
- BT_ERR("Failed to run HCI request: err %d", err);
+ hci_req_sync(hdev, discoverable_update, 0, HCI_CMD_TIMEOUT, &status);
+ mgmt_set_discoverable_complete(hdev, status);
}
void __hci_abort_conn(struct hci_request *req, struct hci_conn *conn,
@@ -657,3 +1698,574 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
return 0;
}
+
+static int update_bg_scan(struct hci_request *req, unsigned long opt)
+{
+ hci_dev_lock(req->hdev);
+ __hci_update_background_scan(req);
+ hci_dev_unlock(req->hdev);
+ return 0;
+}
+
+static void bg_scan_update(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ bg_scan_update);
+ struct hci_conn *conn;
+ u8 status;
+ int err;
+
+ err = hci_req_sync(hdev, update_bg_scan, 0, HCI_CMD_TIMEOUT, &status);
+ if (!err)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
+ if (conn)
+ hci_le_conn_failed(conn, status);
+
+ hci_dev_unlock(hdev);
+}
+
+static int le_scan_disable(struct hci_request *req, unsigned long opt)
+{
+ hci_req_add_le_scan_disable(req);
+ return 0;
+}
+
+static int bredr_inquiry(struct hci_request *req, unsigned long opt)
+{
+ u8 length = opt;
+ const u8 giac[3] = { 0x33, 0x8b, 0x9e };
+ const u8 liac[3] = { 0x00, 0x8b, 0x9e };
+ struct hci_cp_inquiry cp;
+
+ BT_DBG("%s", req->hdev->name);
+
+ hci_dev_lock(req->hdev);
+ hci_inquiry_cache_flush(req->hdev);
+ hci_dev_unlock(req->hdev);
+
+ memset(&cp, 0, sizeof(cp));
+
+ if (req->hdev->discovery.limited)
+ memcpy(&cp.lap, liac, sizeof(cp.lap));
+ else
+ memcpy(&cp.lap, giac, sizeof(cp.lap));
+
+ cp.length = length;
+
+ hci_req_add(req, HCI_OP_INQUIRY, sizeof(cp), &cp);
+
+ return 0;
+}
+
+static void le_scan_disable_work(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ le_scan_disable.work);
+ u8 status;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
+ return;
+
+ cancel_delayed_work(&hdev->le_scan_restart);
+
+ hci_req_sync(hdev, le_scan_disable, 0, HCI_CMD_TIMEOUT, &status);
+ if (status) {
+ BT_ERR("Failed to disable LE scan: status 0x%02x", status);
+ return;
+ }
+
+ hdev->discovery.scan_start = 0;
+
+ /* If we were running LE only scan, change discovery state. If
+ * we were running both LE and BR/EDR inquiry simultaneously,
+ * and BR/EDR inquiry is already finished, stop discovery,
+ * otherwise BR/EDR inquiry will stop discovery when finished.
+ * If we will resolve remote device name, do not change
+ * discovery state.
+ */
+
+ if (hdev->discovery.type == DISCOV_TYPE_LE)
+ goto discov_stopped;
+
+ if (hdev->discovery.type != DISCOV_TYPE_INTERLEAVED)
+ return;
+
+ if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks)) {
+ if (!test_bit(HCI_INQUIRY, &hdev->flags) &&
+ hdev->discovery.state != DISCOVERY_RESOLVING)
+ goto discov_stopped;
+
+ return;
+ }
+
+ hci_req_sync(hdev, bredr_inquiry, DISCOV_INTERLEAVED_INQUIRY_LEN,
+ HCI_CMD_TIMEOUT, &status);
+ if (status) {
+ BT_ERR("Inquiry failed: status 0x%02x", status);
+ goto discov_stopped;
+ }
+
+ return;
+
+discov_stopped:
+ hci_dev_lock(hdev);
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+ hci_dev_unlock(hdev);
+}
+
+static int le_scan_restart(struct hci_request *req, unsigned long opt)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_le_set_scan_enable cp;
+
+ /* If controller is not scanning we are done. */
+ if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
+ return 0;
+
+ hci_req_add_le_scan_disable(req);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.enable = LE_SCAN_ENABLE;
+ cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
+ hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
+
+ return 0;
+}
+
+static void le_scan_restart_work(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ le_scan_restart.work);
+ unsigned long timeout, duration, scan_start, now;
+ u8 status;
+
+ BT_DBG("%s", hdev->name);
+
+ hci_req_sync(hdev, le_scan_restart, 0, HCI_CMD_TIMEOUT, &status);
+ if (status) {
+ BT_ERR("Failed to restart LE scan: status %d", status);
+ return;
+ }
+
+ hci_dev_lock(hdev);
+
+ if (!test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks) ||
+ !hdev->discovery.scan_start)
+ goto unlock;
+
+ /* When the scan was started, hdev->le_scan_disable has been queued
+ * after duration from scan_start. During scan restart this job
+ * has been canceled, and we need to queue it again after proper
+ * timeout, to make sure that scan does not run indefinitely.
+ */
+ duration = hdev->discovery.scan_duration;
+ scan_start = hdev->discovery.scan_start;
+ now = jiffies;
+ if (now - scan_start <= duration) {
+ int elapsed;
+
+ if (now >= scan_start)
+ elapsed = now - scan_start;
+ else
+ elapsed = ULONG_MAX - scan_start + now;
+
+ timeout = duration - elapsed;
+ } else {
+ timeout = 0;
+ }
+
+ queue_delayed_work(hdev->req_workqueue,
+ &hdev->le_scan_disable, timeout);
+
+unlock:
+ hci_dev_unlock(hdev);
+}
+
+static void disable_advertising(struct hci_request *req)
+{
+ u8 enable = 0x00;
+
+ hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
+}
+
+static int active_scan(struct hci_request *req, unsigned long opt)
+{
+ uint16_t interval = opt;
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_le_set_scan_param param_cp;
+ struct hci_cp_le_set_scan_enable enable_cp;
+ u8 own_addr_type;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ if (hci_dev_test_flag(hdev, HCI_LE_ADV)) {
+ hci_dev_lock(hdev);
+
+ /* Don't let discovery abort an outgoing connection attempt
+ * that's using directed advertising.
+ */
+ if (hci_lookup_le_connect(hdev)) {
+ hci_dev_unlock(hdev);
+ return -EBUSY;
+ }
+
+ cancel_adv_timeout(hdev);
+ hci_dev_unlock(hdev);
+
+ disable_advertising(req);
+ }
+
+ /* If controller is scanning, it means the background scanning is
+ * running. Thus, we should temporarily stop it in order to set the
+ * discovery scanning parameters.
+ */
+ if (hci_dev_test_flag(hdev, HCI_LE_SCAN))
+ hci_req_add_le_scan_disable(req);
+
+ /* All active scans will be done with either a resolvable private
+ * address (when privacy feature has been enabled) or non-resolvable
+ * private address.
+ */
+ err = hci_update_random_address(req, true, &own_addr_type);
+ if (err < 0)
+ own_addr_type = ADDR_LE_DEV_PUBLIC;
+
+ memset(&param_cp, 0, sizeof(param_cp));
+ param_cp.type = LE_SCAN_ACTIVE;
+ param_cp.interval = cpu_to_le16(interval);
+ param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
+ param_cp.own_address_type = own_addr_type;
+
+ hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
+ &param_cp);
+
+ memset(&enable_cp, 0, sizeof(enable_cp));
+ enable_cp.enable = LE_SCAN_ENABLE;
+ enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
+
+ hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
+ &enable_cp);
+
+ return 0;
+}
+
+static int interleaved_discov(struct hci_request *req, unsigned long opt)
+{
+ int err;
+
+ BT_DBG("%s", req->hdev->name);
+
+ err = active_scan(req, opt);
+ if (err)
+ return err;
+
+ return bredr_inquiry(req, DISCOV_BREDR_INQUIRY_LEN);
+}
+
+static void start_discovery(struct hci_dev *hdev, u8 *status)
+{
+ unsigned long timeout;
+
+ BT_DBG("%s type %u", hdev->name, hdev->discovery.type);
+
+ switch (hdev->discovery.type) {
+ case DISCOV_TYPE_BREDR:
+ if (!hci_dev_test_flag(hdev, HCI_INQUIRY))
+ hci_req_sync(hdev, bredr_inquiry,
+ DISCOV_BREDR_INQUIRY_LEN, HCI_CMD_TIMEOUT,
+ status);
+ return;
+ case DISCOV_TYPE_INTERLEAVED:
+ /* When running simultaneous discovery, the LE scanning time
+ * should occupy the whole discovery time sine BR/EDR inquiry
+ * and LE scanning are scheduled by the controller.
+ *
+ * For interleaving discovery in comparison, BR/EDR inquiry
+ * and LE scanning are done sequentially with separate
+ * timeouts.
+ */
+ if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
+ &hdev->quirks)) {
+ timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
+ /* During simultaneous discovery, we double LE scan
+ * interval. We must leave some time for the controller
+ * to do BR/EDR inquiry.
+ */
+ hci_req_sync(hdev, interleaved_discov,
+ DISCOV_LE_SCAN_INT * 2, HCI_CMD_TIMEOUT,
+ status);
+ break;
+ }
+
+ timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout);
+ hci_req_sync(hdev, active_scan, DISCOV_LE_SCAN_INT,
+ HCI_CMD_TIMEOUT, status);
+ break;
+ case DISCOV_TYPE_LE:
+ timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
+ hci_req_sync(hdev, active_scan, DISCOV_LE_SCAN_INT,
+ HCI_CMD_TIMEOUT, status);
+ break;
+ default:
+ *status = HCI_ERROR_UNSPECIFIED;
+ return;
+ }
+
+ if (*status)
+ return;
+
+ BT_DBG("%s timeout %u ms", hdev->name, jiffies_to_msecs(timeout));
+
+ /* When service discovery is used and the controller has a
+ * strict duplicate filter, it is important to remember the
+ * start and duration of the scan. This is required for
+ * restarting scanning during the discovery phase.
+ */
+ if (test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks) &&
+ hdev->discovery.result_filtering) {
+ hdev->discovery.scan_start = jiffies;
+ hdev->discovery.scan_duration = timeout;
+ }
+
+ queue_delayed_work(hdev->req_workqueue, &hdev->le_scan_disable,
+ timeout);
+}
+
+bool hci_req_stop_discovery(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct discovery_state *d = &hdev->discovery;
+ struct hci_cp_remote_name_req_cancel cp;
+ struct inquiry_entry *e;
+ bool ret = false;
+
+ BT_DBG("%s state %u", hdev->name, hdev->discovery.state);
+
+ if (d->state == DISCOVERY_FINDING || d->state == DISCOVERY_STOPPING) {
+ if (test_bit(HCI_INQUIRY, &hdev->flags))
+ hci_req_add(req, HCI_OP_INQUIRY_CANCEL, 0, NULL);
+
+ if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
+ cancel_delayed_work(&hdev->le_scan_disable);
+ hci_req_add_le_scan_disable(req);
+ }
+
+ ret = true;
+ } else {
+ /* Passive scanning */
+ if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
+ hci_req_add_le_scan_disable(req);
+ ret = true;
+ }
+ }
+
+ /* No further actions needed for LE-only discovery */
+ if (d->type == DISCOV_TYPE_LE)
+ return ret;
+
+ if (d->state == DISCOVERY_RESOLVING || d->state == DISCOVERY_STOPPING) {
+ e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY,
+ NAME_PENDING);
+ if (!e)
+ return ret;
+
+ bacpy(&cp.bdaddr, &e->data.bdaddr);
+ hci_req_add(req, HCI_OP_REMOTE_NAME_REQ_CANCEL, sizeof(cp),
+ &cp);
+ ret = true;
+ }
+
+ return ret;
+}
+
+static int stop_discovery(struct hci_request *req, unsigned long opt)
+{
+ hci_dev_lock(req->hdev);
+ hci_req_stop_discovery(req);
+ hci_dev_unlock(req->hdev);
+
+ return 0;
+}
+
+static void discov_update(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ discov_update);
+ u8 status = 0;
+
+ switch (hdev->discovery.state) {
+ case DISCOVERY_STARTING:
+ start_discovery(hdev, &status);
+ mgmt_start_discovery_complete(hdev, status);
+ if (status)
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+ else
+ hci_discovery_set_state(hdev, DISCOVERY_FINDING);
+ break;
+ case DISCOVERY_STOPPING:
+ hci_req_sync(hdev, stop_discovery, 0, HCI_CMD_TIMEOUT, &status);
+ mgmt_stop_discovery_complete(hdev, status);
+ if (!status)
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+ break;
+ case DISCOVERY_STOPPED:
+ default:
+ return;
+ }
+}
+
+static void discov_off(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ discov_off.work);
+
+ BT_DBG("%s", hdev->name);
+
+ hci_dev_lock(hdev);
+
+ /* When discoverable timeout triggers, then just make sure
+ * the limited discoverable flag is cleared. Even in the case
+ * of a timeout triggered from general discoverable, it is
+ * safe to unconditionally clear the flag.
+ */
+ hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
+ hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
+ hdev->discov_timeout = 0;
+
+ hci_dev_unlock(hdev);
+
+ hci_req_sync(hdev, discoverable_update, 0, HCI_CMD_TIMEOUT, NULL);
+ mgmt_new_settings(hdev);
+}
+
+static int powered_update_hci(struct hci_request *req, unsigned long opt)
+{
+ struct hci_dev *hdev = req->hdev;
+ u8 link_sec;
+
+ hci_dev_lock(hdev);
+
+ if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED) &&
+ !lmp_host_ssp_capable(hdev)) {
+ u8 mode = 0x01;
+
+ hci_req_add(req, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode);
+
+ if (bredr_sc_enabled(hdev) && !lmp_host_sc_capable(hdev)) {
+ u8 support = 0x01;
+
+ hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT,
+ sizeof(support), &support);
+ }
+ }
+
+ if (hci_dev_test_flag(hdev, HCI_LE_ENABLED) &&
+ lmp_bredr_capable(hdev)) {
+ struct hci_cp_write_le_host_supported cp;
+
+ cp.le = 0x01;
+ cp.simul = 0x00;
+
+ /* Check first if we already have the right
+ * host state (host features set)
+ */
+ if (cp.le != lmp_host_le_capable(hdev) ||
+ cp.simul != lmp_host_le_br_capable(hdev))
+ hci_req_add(req, HCI_OP_WRITE_LE_HOST_SUPPORTED,
+ sizeof(cp), &cp);
+ }
+
+ if (hci_dev_test_flag(hdev, HCI_LE_ENABLED)) {
+ /* Make sure the controller has a good default for
+ * advertising data. This also applies to the case
+ * where BR/EDR was toggled during the AUTO_OFF phase.
+ */
+ if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
+ list_empty(&hdev->adv_instances)) {
+ __hci_req_update_adv_data(req, 0x00);
+ __hci_req_update_scan_rsp_data(req, 0x00);
+
+ if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
+ __hci_req_enable_advertising(req);
+ } else if (!list_empty(&hdev->adv_instances)) {
+ struct adv_info *adv_instance;
+
+ adv_instance = list_first_entry(&hdev->adv_instances,
+ struct adv_info, list);
+ __hci_req_schedule_adv_instance(req,
+ adv_instance->instance,
+ true);
+ }
+ }
+
+ link_sec = hci_dev_test_flag(hdev, HCI_LINK_SECURITY);
+ if (link_sec != test_bit(HCI_AUTH, &hdev->flags))
+ hci_req_add(req, HCI_OP_WRITE_AUTH_ENABLE,
+ sizeof(link_sec), &link_sec);
+
+ if (lmp_bredr_capable(hdev)) {
+ if (hci_dev_test_flag(hdev, HCI_FAST_CONNECTABLE))
+ __hci_req_write_fast_connectable(req, true);
+ else
+ __hci_req_write_fast_connectable(req, false);
+ __hci_req_update_scan(req);
+ __hci_req_update_class(req);
+ __hci_req_update_name(req);
+ __hci_req_update_eir(req);
+ }
+
+ hci_dev_unlock(hdev);
+ return 0;
+}
+
+int __hci_req_hci_power_on(struct hci_dev *hdev)
+{
+ /* Register the available SMP channels (BR/EDR and LE) only when
+ * successfully powering on the controller. This late
+ * registration is required so that LE SMP can clearly decide if
+ * the public address or static address is used.
+ */
+ smp_register(hdev);
+
+ return __hci_req_sync(hdev, powered_update_hci, 0, HCI_CMD_TIMEOUT,
+ NULL);
+}
+
+void hci_request_setup(struct hci_dev *hdev)
+{
+ INIT_WORK(&hdev->discov_update, discov_update);
+ INIT_WORK(&hdev->bg_scan_update, bg_scan_update);
+ INIT_WORK(&hdev->scan_update, scan_update_work);
+ INIT_WORK(&hdev->connectable_update, connectable_update_work);
+ INIT_WORK(&hdev->discoverable_update, discoverable_update_work);
+ INIT_DELAYED_WORK(&hdev->discov_off, discov_off);
+ INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work);
+ INIT_DELAYED_WORK(&hdev->le_scan_restart, le_scan_restart_work);
+ INIT_DELAYED_WORK(&hdev->adv_instance_expire, adv_timeout_expire);
+}
+
+void hci_request_cancel_all(struct hci_dev *hdev)
+{
+ hci_req_sync_cancel(hdev, ENODEV);
+
+ cancel_work_sync(&hdev->discov_update);
+ cancel_work_sync(&hdev->bg_scan_update);
+ cancel_work_sync(&hdev->scan_update);
+ cancel_work_sync(&hdev->connectable_update);
+ cancel_work_sync(&hdev->discoverable_update);
+ cancel_delayed_work_sync(&hdev->discov_off);
+ cancel_delayed_work_sync(&hdev->le_scan_disable);
+ cancel_delayed_work_sync(&hdev->le_scan_restart);
+
+ if (hdev->adv_instance_timeout) {
+ cancel_delayed_work_sync(&hdev->adv_instance_expire);
+ hdev->adv_instance_timeout = 0;
+ }
+}
diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h
index 25c7f1305dcb..64ff8c040d50 100644
--- a/net/bluetooth/hci_request.h
+++ b/net/bluetooth/hci_request.h
@@ -20,6 +20,9 @@
SOFTWARE IS DISCLAIMED.
*/
+#define hci_req_sync_lock(hdev) mutex_lock(&hdev->req_lock)
+#define hci_req_sync_unlock(hdev) mutex_unlock(&hdev->req_lock)
+
struct hci_request {
struct hci_dev *hdev;
struct sk_buff_head cmd_q;
@@ -41,21 +44,61 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
hci_req_complete_t *req_complete,
hci_req_complete_skb_t *req_complete_skb);
+int hci_req_sync(struct hci_dev *hdev, int (*req)(struct hci_request *req,
+ unsigned long opt),
+ unsigned long opt, u32 timeout, u8 *hci_status);
+int __hci_req_sync(struct hci_dev *hdev, int (*func)(struct hci_request *req,
+ unsigned long opt),
+ unsigned long opt, u32 timeout, u8 *hci_status);
+void hci_req_sync_cancel(struct hci_dev *hdev, int err);
+
struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param);
+int __hci_req_hci_power_on(struct hci_dev *hdev);
+
+void __hci_req_write_fast_connectable(struct hci_request *req, bool enable);
+void __hci_req_update_name(struct hci_request *req);
+void __hci_req_update_eir(struct hci_request *req);
+
void hci_req_add_le_scan_disable(struct hci_request *req);
void hci_req_add_le_passive_scan(struct hci_request *req);
-void hci_update_page_scan(struct hci_dev *hdev);
-void __hci_update_page_scan(struct hci_request *req);
+void hci_req_reenable_advertising(struct hci_dev *hdev);
+void __hci_req_enable_advertising(struct hci_request *req);
+void __hci_req_disable_advertising(struct hci_request *req);
+void __hci_req_update_adv_data(struct hci_request *req, u8 instance);
+int hci_req_update_adv_data(struct hci_dev *hdev, u8 instance);
+void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance);
+
+int __hci_req_schedule_adv_instance(struct hci_request *req, u8 instance,
+ bool force);
+void hci_req_clear_adv_instance(struct hci_dev *hdev, struct hci_request *req,
+ u8 instance, bool force);
+
+void __hci_req_update_class(struct hci_request *req);
+
+/* Returns true if HCI commands were queued */
+bool hci_req_stop_discovery(struct hci_request *req);
+
+static inline void hci_req_update_scan(struct hci_dev *hdev)
+{
+ queue_work(hdev->req_workqueue, &hdev->scan_update);
+}
+
+void __hci_req_update_scan(struct hci_request *req);
int hci_update_random_address(struct hci_request *req, bool require_privacy,
u8 *own_addr_type);
-void hci_update_background_scan(struct hci_dev *hdev);
-void __hci_update_background_scan(struct hci_request *req);
-
int hci_abort_conn(struct hci_conn *conn, u8 reason);
void __hci_abort_conn(struct hci_request *req, struct hci_conn *conn,
u8 reason);
+
+static inline void hci_update_background_scan(struct hci_dev *hdev)
+{
+ queue_work(hdev->req_workqueue, &hdev->bg_scan_update);
+}
+
+void hci_request_setup(struct hci_dev *hdev);
+void hci_request_cancel_all(struct hci_dev *hdev);
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index b1eb8c09a660..1298d723c0e0 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -25,6 +25,7 @@
/* Bluetooth HCI sockets. */
#include <linux/export.h>
+#include <linux/utsname.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
@@ -120,13 +121,13 @@ static bool is_filtered_packet(struct sock *sk, struct sk_buff *skb)
/* Apply filter */
flt = &hci_pi(sk)->filter;
- flt_type = bt_cb(skb)->pkt_type & HCI_FLT_TYPE_BITS;
+ flt_type = hci_skb_pkt_type(skb) & HCI_FLT_TYPE_BITS;
if (!test_bit(flt_type, &flt->type_mask))
return true;
/* Extra filter for event packets only */
- if (bt_cb(skb)->pkt_type != HCI_EVENT_PKT)
+ if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT)
return false;
flt_event = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS);
@@ -170,19 +171,19 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
continue;
if (hci_pi(sk)->channel == HCI_CHANNEL_RAW) {
- if (bt_cb(skb)->pkt_type != HCI_COMMAND_PKT &&
- bt_cb(skb)->pkt_type != HCI_EVENT_PKT &&
- bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
- bt_cb(skb)->pkt_type != HCI_SCODATA_PKT)
+ if (hci_skb_pkt_type(skb) != HCI_COMMAND_PKT &&
+ hci_skb_pkt_type(skb) != HCI_EVENT_PKT &&
+ hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
+ hci_skb_pkt_type(skb) != HCI_SCODATA_PKT)
continue;
if (is_filtered_packet(sk, skb))
continue;
} else if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
if (!bt_cb(skb)->incoming)
continue;
- if (bt_cb(skb)->pkt_type != HCI_EVENT_PKT &&
- bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
- bt_cb(skb)->pkt_type != HCI_SCODATA_PKT)
+ if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT &&
+ hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
+ hci_skb_pkt_type(skb) != HCI_SCODATA_PKT)
continue;
} else {
/* Don't send frame to other channel types */
@@ -196,7 +197,7 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
continue;
/* Put type byte before the data */
- memcpy(skb_push(skb_copy, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(skb_push(skb_copy, 1), &hci_skb_pkt_type(skb), 1);
}
nskb = skb_clone(skb_copy, GFP_ATOMIC);
@@ -262,7 +263,7 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("hdev %p len %d", hdev, skb->len);
- switch (bt_cb(skb)->pkt_type) {
+ switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
opcode = cpu_to_le16(HCI_MON_COMMAND_PKT);
break;
@@ -294,7 +295,7 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
return;
/* Put header before the data */
- hdr = (void *) skb_push(skb_copy, HCI_MON_HDR_SIZE);
+ hdr = (void *)skb_push(skb_copy, HCI_MON_HDR_SIZE);
hdr->opcode = opcode;
hdr->index = cpu_to_le16(hdev->id);
hdr->len = cpu_to_le16(skb->len);
@@ -375,7 +376,7 @@ static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event)
__net_timestamp(skb);
- hdr = (void *) skb_push(skb, HCI_MON_HDR_SIZE);
+ hdr = (void *)skb_push(skb, HCI_MON_HDR_SIZE);
hdr->opcode = opcode;
hdr->index = cpu_to_le16(hdev->id);
hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE);
@@ -383,6 +384,38 @@ static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event)
return skb;
}
+static void __printf(2, 3)
+send_monitor_note(struct sock *sk, const char *fmt, ...)
+{
+ size_t len;
+ struct hci_mon_hdr *hdr;
+ struct sk_buff *skb;
+ va_list args;
+
+ va_start(args, fmt);
+ len = vsnprintf(NULL, 0, fmt, args);
+ va_end(args);
+
+ skb = bt_skb_alloc(len + 1, GFP_ATOMIC);
+ if (!skb)
+ return;
+
+ va_start(args, fmt);
+ vsprintf(skb_put(skb, len), fmt, args);
+ *skb_put(skb, 1) = 0;
+ va_end(args);
+
+ __net_timestamp(skb);
+
+ hdr = (void *)skb_push(skb, HCI_MON_HDR_SIZE);
+ hdr->opcode = cpu_to_le16(HCI_MON_SYSTEM_NOTE);
+ hdr->index = cpu_to_le16(HCI_DEV_NONE);
+ hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE);
+
+ if (sock_queue_rcv_skb(sk, skb))
+ kfree_skb(skb);
+}
+
static void send_monitor_replay(struct sock *sk)
{
struct hci_dev *hdev;
@@ -436,18 +469,18 @@ static void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
if (!skb)
return;
- hdr = (void *) skb_put(skb, HCI_EVENT_HDR_SIZE);
+ hdr = (void *)skb_put(skb, HCI_EVENT_HDR_SIZE);
hdr->evt = HCI_EV_STACK_INTERNAL;
hdr->plen = sizeof(*ev) + dlen;
- ev = (void *) skb_put(skb, sizeof(*ev) + dlen);
+ ev = (void *)skb_put(skb, sizeof(*ev) + dlen);
ev->type = type;
memcpy(ev->data, data, dlen);
bt_cb(skb)->incoming = 1;
__net_timestamp(skb);
- bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+ hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
hci_send_to_sock(hdev, skb);
kfree_skb(skb);
}
@@ -653,20 +686,20 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
return -EOPNOTSUPP;
case HCIGETCONNINFO:
- return hci_get_conn_info(hdev, (void __user *) arg);
+ return hci_get_conn_info(hdev, (void __user *)arg);
case HCIGETAUTHINFO:
- return hci_get_auth_info(hdev, (void __user *) arg);
+ return hci_get_auth_info(hdev, (void __user *)arg);
case HCIBLOCKADDR:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- return hci_sock_blacklist_add(hdev, (void __user *) arg);
+ return hci_sock_blacklist_add(hdev, (void __user *)arg);
case HCIUNBLOCKADDR:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- return hci_sock_blacklist_del(hdev, (void __user *) arg);
+ return hci_sock_blacklist_del(hdev, (void __user *)arg);
}
return -ENOIOCTLCMD;
@@ -675,7 +708,7 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
unsigned long arg)
{
- void __user *argp = (void __user *) arg;
+ void __user *argp = (void __user *)arg;
struct sock *sk = sock->sk;
int err;
@@ -872,11 +905,28 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
*/
hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
+ send_monitor_note(sk, "Linux version %s (%s)",
+ init_utsname()->release,
+ init_utsname()->machine);
+ send_monitor_note(sk, "Bluetooth subsystem version %s",
+ BT_SUBSYS_VERSION);
send_monitor_replay(sk);
atomic_inc(&monitor_promisc);
break;
+ case HCI_CHANNEL_LOGGING:
+ if (haddr.hci_dev != HCI_DEV_NONE) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (!capable(CAP_NET_ADMIN)) {
+ err = -EPERM;
+ goto done;
+ }
+ break;
+
default:
if (!hci_mgmt_chan_find(haddr.hci_channel)) {
err = -EINVAL;
@@ -926,7 +976,7 @@ done:
static int hci_sock_getname(struct socket *sock, struct sockaddr *addr,
int *addr_len, int peer)
{
- struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr;
+ struct sockaddr_hci *haddr = (struct sockaddr_hci *)addr;
struct sock *sk = sock->sk;
struct hci_dev *hdev;
int err = 0;
@@ -991,8 +1041,8 @@ static void hci_sock_cmsg(struct sock *sk, struct msghdr *msg,
}
}
-static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
- int flags)
+static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg,
+ size_t len, int flags)
{
int noblock = flags & MSG_DONTWAIT;
struct sock *sk = sock->sk;
@@ -1004,6 +1054,9 @@ static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
if (flags & MSG_OOB)
return -EOPNOTSUPP;
+ if (hci_pi(sk)->channel == HCI_CHANNEL_LOGGING)
+ return -EOPNOTSUPP;
+
if (sk->sk_state == BT_CLOSED)
return 0;
@@ -1150,6 +1203,90 @@ done:
return err;
}
+static int hci_logging_frame(struct sock *sk, struct msghdr *msg, int len)
+{
+ struct hci_mon_hdr *hdr;
+ struct sk_buff *skb;
+ struct hci_dev *hdev;
+ u16 index;
+ int err;
+
+ /* The logging frame consists at minimum of the standard header,
+ * the priority byte, the ident length byte and at least one string
+ * terminator NUL byte. Anything shorter are invalid packets.
+ */
+ if (len < sizeof(*hdr) + 3)
+ return -EINVAL;
+
+ skb = bt_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err);
+ if (!skb)
+ return err;
+
+ if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
+ err = -EFAULT;
+ goto drop;
+ }
+
+ hdr = (void *)skb->data;
+
+ if (__le16_to_cpu(hdr->len) != len - sizeof(*hdr)) {
+ err = -EINVAL;
+ goto drop;
+ }
+
+ if (__le16_to_cpu(hdr->opcode) == 0x0000) {
+ __u8 priority = skb->data[sizeof(*hdr)];
+ __u8 ident_len = skb->data[sizeof(*hdr) + 1];
+
+ /* Only the priorities 0-7 are valid and with that any other
+ * value results in an invalid packet.
+ *
+ * The priority byte is followed by an ident length byte and
+ * the NUL terminated ident string. Check that the ident
+ * length is not overflowing the packet and also that the
+ * ident string itself is NUL terminated. In case the ident
+ * length is zero, the length value actually doubles as NUL
+ * terminator identifier.
+ *
+ * The message follows the ident string (if present) and
+ * must be NUL terminated. Otherwise it is not a valid packet.
+ */
+ if (priority > 7 || skb->data[len - 1] != 0x00 ||
+ ident_len > len - sizeof(*hdr) - 3 ||
+ skb->data[sizeof(*hdr) + ident_len + 1] != 0x00) {
+ err = -EINVAL;
+ goto drop;
+ }
+ } else {
+ err = -EINVAL;
+ goto drop;
+ }
+
+ index = __le16_to_cpu(hdr->index);
+
+ if (index != MGMT_INDEX_NONE) {
+ hdev = hci_dev_get(index);
+ if (!hdev) {
+ err = -ENODEV;
+ goto drop;
+ }
+ } else {
+ hdev = NULL;
+ }
+
+ hdr->opcode = cpu_to_le16(HCI_MON_USER_LOGGING);
+
+ hci_send_to_channel(HCI_CHANNEL_MONITOR, skb, HCI_SOCK_TRUSTED, NULL);
+ err = len;
+
+ if (hdev)
+ hci_dev_put(hdev);
+
+drop:
+ kfree_skb(skb);
+ return err;
+}
+
static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
size_t len)
{
@@ -1179,6 +1316,9 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
case HCI_CHANNEL_MONITOR:
err = -EOPNOTSUPP;
goto done;
+ case HCI_CHANNEL_LOGGING:
+ err = hci_logging_frame(sk, msg, len);
+ goto done;
default:
mutex_lock(&mgmt_chan_list_lock);
chan = __hci_mgmt_chan_find(hci_pi(sk)->channel);
@@ -1211,7 +1351,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
goto drop;
}
- bt_cb(skb)->pkt_type = *((unsigned char *) skb->data);
+ hci_skb_pkt_type(skb) = skb->data[0];
skb_pull(skb, 1);
if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
@@ -1220,16 +1360,16 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
*
* However check that the packet type is valid.
*/
- if (bt_cb(skb)->pkt_type != HCI_COMMAND_PKT &&
- bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
- bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) {
+ if (hci_skb_pkt_type(skb) != HCI_COMMAND_PKT &&
+ hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
+ hci_skb_pkt_type(skb) != HCI_SCODATA_PKT) {
err = -EINVAL;
goto drop;
}
skb_queue_tail(&hdev->raw_q, skb);
queue_work(hdev->workqueue, &hdev->tx_work);
- } else if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
+ } else if (hci_skb_pkt_type(skb) == HCI_COMMAND_PKT) {
u16 opcode = get_unaligned_le16(skb->data);
u16 ogf = hci_opcode_ogf(opcode);
u16 ocf = hci_opcode_ocf(opcode);
@@ -1242,6 +1382,11 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
goto drop;
}
+ /* Since the opcode has already been extracted here, store
+ * a copy of the value for later use by the drivers.
+ */
+ hci_skb_opcode(skb) = opcode;
+
if (ogf == 0x3f) {
skb_queue_tail(&hdev->raw_q, skb);
queue_work(hdev->workqueue, &hdev->tx_work);
@@ -1249,7 +1394,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
/* Stand-alone HCI commands must be flagged as
* single-command requests.
*/
- bt_cb(skb)->hci.req_start = true;
+ bt_cb(skb)->hci.req_flags |= HCI_REQ_START;
skb_queue_tail(&hdev->cmd_q, skb);
queue_work(hdev->workqueue, &hdev->cmd_work);
@@ -1260,8 +1405,8 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
goto drop;
}
- if (bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
- bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) {
+ if (hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
+ hci_skb_pkt_type(skb) != HCI_SCODATA_PKT) {
err = -EINVAL;
goto drop;
}
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 66e8b6ee19a5..39a5149f3010 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -6538,8 +6538,6 @@ static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
static int l2cap_stream_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
struct sk_buff *skb)
{
- int err = 0;
-
BT_DBG("chan %p, control %p, skb %p, state %d", chan, control, skb,
chan->rx_state);
@@ -6570,7 +6568,7 @@ static int l2cap_stream_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
chan->last_acked_seq = control->txseq;
chan->expected_tx_seq = __next_seq(chan, control->txseq);
- return err;
+ return 0;
}
static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
@@ -7113,8 +7111,6 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
chan->dcid = cid;
if (bdaddr_type_is_le(dst_type)) {
- u8 role;
-
/* Convert from L2CAP channel address type to HCI address type
*/
if (dst_type == BDADDR_LE_PUBLIC)
@@ -7123,14 +7119,15 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
dst_type = ADDR_LE_DEV_RANDOM;
if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
- role = HCI_ROLE_SLAVE;
+ hcon = hci_connect_le(hdev, dst, dst_type,
+ chan->sec_level,
+ HCI_LE_CONN_TIMEOUT,
+ HCI_ROLE_SLAVE);
else
- role = HCI_ROLE_MASTER;
+ hcon = hci_connect_le_scan(hdev, dst, dst_type,
+ chan->sec_level,
+ HCI_LE_CONN_TIMEOUT);
- hcon = hci_connect_le_scan(hdev, dst, dst_type,
- chan->sec_level,
- HCI_LE_CONN_TIMEOUT,
- role);
} else {
u8 auth_type = l2cap_get_auth_type(chan);
hcon = hci_connect_acl(hdev, dst, chan->sec_level, auth_type);
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 7f22119276f3..5a5089cb6570 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -38,7 +38,7 @@
#include "mgmt_util.h"
#define MGMT_VERSION 1
-#define MGMT_REVISION 10
+#define MGMT_REVISION 11
static const u16 mgmt_commands[] = {
MGMT_OP_READ_INDEX_LIST,
@@ -102,6 +102,8 @@ static const u16 mgmt_commands[] = {
MGMT_OP_READ_ADV_FEATURES,
MGMT_OP_ADD_ADVERTISING,
MGMT_OP_REMOVE_ADVERTISING,
+ MGMT_OP_GET_ADV_SIZE_INFO,
+ MGMT_OP_START_LIMITED_DISCOVERY,
};
static const u16 mgmt_events[] = {
@@ -718,116 +720,6 @@ static u32 get_current_settings(struct hci_dev *hdev)
return settings;
}
-#define PNP_INFO_SVCLASS_ID 0x1200
-
-static u8 *create_uuid16_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
-{
- u8 *ptr = data, *uuids_start = NULL;
- struct bt_uuid *uuid;
-
- if (len < 4)
- return ptr;
-
- list_for_each_entry(uuid, &hdev->uuids, list) {
- u16 uuid16;
-
- if (uuid->size != 16)
- continue;
-
- uuid16 = get_unaligned_le16(&uuid->uuid[12]);
- if (uuid16 < 0x1100)
- continue;
-
- if (uuid16 == PNP_INFO_SVCLASS_ID)
- continue;
-
- if (!uuids_start) {
- uuids_start = ptr;
- uuids_start[0] = 1;
- uuids_start[1] = EIR_UUID16_ALL;
- ptr += 2;
- }
-
- /* Stop if not enough space to put next UUID */
- if ((ptr - data) + sizeof(u16) > len) {
- uuids_start[1] = EIR_UUID16_SOME;
- break;
- }
-
- *ptr++ = (uuid16 & 0x00ff);
- *ptr++ = (uuid16 & 0xff00) >> 8;
- uuids_start[0] += sizeof(uuid16);
- }
-
- return ptr;
-}
-
-static u8 *create_uuid32_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
-{
- u8 *ptr = data, *uuids_start = NULL;
- struct bt_uuid *uuid;
-
- if (len < 6)
- return ptr;
-
- list_for_each_entry(uuid, &hdev->uuids, list) {
- if (uuid->size != 32)
- continue;
-
- if (!uuids_start) {
- uuids_start = ptr;
- uuids_start[0] = 1;
- uuids_start[1] = EIR_UUID32_ALL;
- ptr += 2;
- }
-
- /* Stop if not enough space to put next UUID */
- if ((ptr - data) + sizeof(u32) > len) {
- uuids_start[1] = EIR_UUID32_SOME;
- break;
- }
-
- memcpy(ptr, &uuid->uuid[12], sizeof(u32));
- ptr += sizeof(u32);
- uuids_start[0] += sizeof(u32);
- }
-
- return ptr;
-}
-
-static u8 *create_uuid128_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
-{
- u8 *ptr = data, *uuids_start = NULL;
- struct bt_uuid *uuid;
-
- if (len < 18)
- return ptr;
-
- list_for_each_entry(uuid, &hdev->uuids, list) {
- if (uuid->size != 128)
- continue;
-
- if (!uuids_start) {
- uuids_start = ptr;
- uuids_start[0] = 1;
- uuids_start[1] = EIR_UUID128_ALL;
- ptr += 2;
- }
-
- /* Stop if not enough space to put next UUID */
- if ((ptr - data) + 16 > len) {
- uuids_start[1] = EIR_UUID128_SOME;
- break;
- }
-
- memcpy(ptr, uuid->uuid, 16);
- ptr += 16;
- uuids_start[0] += 16;
- }
-
- return ptr;
-}
-
static struct mgmt_pending_cmd *pending_find(u16 opcode, struct hci_dev *hdev)
{
return mgmt_pending_find(HCI_CHANNEL_CONTROL, opcode, hdev);
@@ -840,98 +732,7 @@ static struct mgmt_pending_cmd *pending_find_data(u16 opcode,
return mgmt_pending_find_data(HCI_CHANNEL_CONTROL, opcode, hdev, data);
}
-static u8 get_current_adv_instance(struct hci_dev *hdev)
-{
- /* The "Set Advertising" setting supersedes the "Add Advertising"
- * setting. Here we set the advertising data based on which
- * setting was set. When neither apply, default to the global settings,
- * represented by instance "0".
- */
- if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
- !hci_dev_test_flag(hdev, HCI_ADVERTISING))
- return hdev->cur_adv_instance;
-
- return 0x00;
-}
-
-static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
-{
- u8 ad_len = 0;
- size_t name_len;
-
- name_len = strlen(hdev->dev_name);
- if (name_len > 0) {
- size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
-
- if (name_len > max_len) {
- name_len = max_len;
- ptr[1] = EIR_NAME_SHORT;
- } else
- ptr[1] = EIR_NAME_COMPLETE;
-
- ptr[0] = name_len + 1;
-
- memcpy(ptr + 2, hdev->dev_name, name_len);
-
- ad_len += (name_len + 2);
- ptr += (name_len + 2);
- }
-
- return ad_len;
-}
-
-static u8 create_instance_scan_rsp_data(struct hci_dev *hdev, u8 instance,
- u8 *ptr)
-{
- struct adv_info *adv_instance;
-
- adv_instance = hci_find_adv_instance(hdev, instance);
- if (!adv_instance)
- return 0;
-
- /* TODO: Set the appropriate entries based on advertising instance flags
- * here once flags other than 0 are supported.
- */
- memcpy(ptr, adv_instance->scan_rsp_data,
- adv_instance->scan_rsp_len);
-
- return adv_instance->scan_rsp_len;
-}
-
-static void update_inst_scan_rsp_data(struct hci_request *req, u8 instance)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_le_set_scan_rsp_data cp;
- u8 len;
-
- if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
- return;
-
- memset(&cp, 0, sizeof(cp));
-
- if (instance)
- len = create_instance_scan_rsp_data(hdev, instance, cp.data);
- else
- len = create_default_scan_rsp_data(hdev, cp.data);
-
- if (hdev->scan_rsp_data_len == len &&
- !memcmp(cp.data, hdev->scan_rsp_data, len))
- return;
-
- memcpy(hdev->scan_rsp_data, cp.data, sizeof(cp.data));
- hdev->scan_rsp_data_len = len;
-
- cp.length = len;
-
- hci_req_add(req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(cp), &cp);
-}
-
-static void update_scan_rsp_data(struct hci_request *req)
-{
- update_inst_scan_rsp_data(req, get_current_adv_instance(req->hdev));
-}
-
-static u8 get_adv_discov_flags(struct hci_dev *hdev)
+u8 mgmt_get_adv_discov_flags(struct hci_dev *hdev)
{
struct mgmt_pending_cmd *cmd;
@@ -955,7 +756,7 @@ static u8 get_adv_discov_flags(struct hci_dev *hdev)
return 0;
}
-static bool get_connectable(struct hci_dev *hdev)
+bool mgmt_get_connectable(struct hci_dev *hdev)
{
struct mgmt_pending_cmd *cmd;
@@ -972,344 +773,6 @@ static bool get_connectable(struct hci_dev *hdev)
return hci_dev_test_flag(hdev, HCI_CONNECTABLE);
}
-static u32 get_adv_instance_flags(struct hci_dev *hdev, u8 instance)
-{
- u32 flags;
- struct adv_info *adv_instance;
-
- if (instance == 0x00) {
- /* Instance 0 always manages the "Tx Power" and "Flags"
- * fields
- */
- flags = MGMT_ADV_FLAG_TX_POWER | MGMT_ADV_FLAG_MANAGED_FLAGS;
-
- /* For instance 0, the HCI_ADVERTISING_CONNECTABLE setting
- * corresponds to the "connectable" instance flag.
- */
- if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE))
- flags |= MGMT_ADV_FLAG_CONNECTABLE;
-
- return flags;
- }
-
- adv_instance = hci_find_adv_instance(hdev, instance);
-
- /* Return 0 when we got an invalid instance identifier. */
- if (!adv_instance)
- return 0;
-
- return adv_instance->flags;
-}
-
-static u8 get_cur_adv_instance_scan_rsp_len(struct hci_dev *hdev)
-{
- u8 instance = get_current_adv_instance(hdev);
- struct adv_info *adv_instance;
-
- /* Ignore instance 0 */
- if (instance == 0x00)
- return 0;
-
- adv_instance = hci_find_adv_instance(hdev, instance);
- if (!adv_instance)
- return 0;
-
- /* TODO: Take into account the "appearance" and "local-name" flags here.
- * These are currently being ignored as they are not supported.
- */
- return adv_instance->scan_rsp_len;
-}
-
-static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
-{
- struct adv_info *adv_instance = NULL;
- u8 ad_len = 0, flags = 0;
- u32 instance_flags;
-
- /* Return 0 when the current instance identifier is invalid. */
- if (instance) {
- adv_instance = hci_find_adv_instance(hdev, instance);
- if (!adv_instance)
- return 0;
- }
-
- instance_flags = get_adv_instance_flags(hdev, instance);
-
- /* The Add Advertising command allows userspace to set both the general
- * and limited discoverable flags.
- */
- if (instance_flags & MGMT_ADV_FLAG_DISCOV)
- flags |= LE_AD_GENERAL;
-
- if (instance_flags & MGMT_ADV_FLAG_LIMITED_DISCOV)
- flags |= LE_AD_LIMITED;
-
- if (flags || (instance_flags & MGMT_ADV_FLAG_MANAGED_FLAGS)) {
- /* If a discovery flag wasn't provided, simply use the global
- * settings.
- */
- if (!flags)
- flags |= get_adv_discov_flags(hdev);
-
- if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
- flags |= LE_AD_NO_BREDR;
-
- /* If flags would still be empty, then there is no need to
- * include the "Flags" AD field".
- */
- if (flags) {
- ptr[0] = 0x02;
- ptr[1] = EIR_FLAGS;
- ptr[2] = flags;
-
- ad_len += 3;
- ptr += 3;
- }
- }
-
- if (adv_instance) {
- memcpy(ptr, adv_instance->adv_data,
- adv_instance->adv_data_len);
- ad_len += adv_instance->adv_data_len;
- ptr += adv_instance->adv_data_len;
- }
-
- /* Provide Tx Power only if we can provide a valid value for it */
- if (hdev->adv_tx_power != HCI_TX_POWER_INVALID &&
- (instance_flags & MGMT_ADV_FLAG_TX_POWER)) {
- ptr[0] = 0x02;
- ptr[1] = EIR_TX_POWER;
- ptr[2] = (u8)hdev->adv_tx_power;
-
- ad_len += 3;
- ptr += 3;
- }
-
- return ad_len;
-}
-
-static void update_inst_adv_data(struct hci_request *req, u8 instance)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_le_set_adv_data cp;
- u8 len;
-
- if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
- return;
-
- memset(&cp, 0, sizeof(cp));
-
- len = create_instance_adv_data(hdev, instance, cp.data);
-
- /* There's nothing to do if the data hasn't changed */
- if (hdev->adv_data_len == len &&
- memcmp(cp.data, hdev->adv_data, len) == 0)
- return;
-
- memcpy(hdev->adv_data, cp.data, sizeof(cp.data));
- hdev->adv_data_len = len;
-
- cp.length = len;
-
- hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
-}
-
-static void update_adv_data(struct hci_request *req)
-{
- update_inst_adv_data(req, get_current_adv_instance(req->hdev));
-}
-
-int mgmt_update_adv_data(struct hci_dev *hdev)
-{
- struct hci_request req;
-
- hci_req_init(&req, hdev);
- update_adv_data(&req);
-
- return hci_req_run(&req, NULL);
-}
-
-static void create_eir(struct hci_dev *hdev, u8 *data)
-{
- u8 *ptr = data;
- size_t name_len;
-
- name_len = strlen(hdev->dev_name);
-
- if (name_len > 0) {
- /* EIR Data type */
- if (name_len > 48) {
- name_len = 48;
- ptr[1] = EIR_NAME_SHORT;
- } else
- ptr[1] = EIR_NAME_COMPLETE;
-
- /* EIR Data length */
- ptr[0] = name_len + 1;
-
- memcpy(ptr + 2, hdev->dev_name, name_len);
-
- ptr += (name_len + 2);
- }
-
- if (hdev->inq_tx_power != HCI_TX_POWER_INVALID) {
- ptr[0] = 2;
- ptr[1] = EIR_TX_POWER;
- ptr[2] = (u8) hdev->inq_tx_power;
-
- ptr += 3;
- }
-
- if (hdev->devid_source > 0) {
- ptr[0] = 9;
- ptr[1] = EIR_DEVICE_ID;
-
- put_unaligned_le16(hdev->devid_source, ptr + 2);
- put_unaligned_le16(hdev->devid_vendor, ptr + 4);
- put_unaligned_le16(hdev->devid_product, ptr + 6);
- put_unaligned_le16(hdev->devid_version, ptr + 8);
-
- ptr += 10;
- }
-
- ptr = create_uuid16_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
- ptr = create_uuid32_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
- ptr = create_uuid128_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
-}
-
-static void update_eir(struct hci_request *req)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_write_eir cp;
-
- if (!hdev_is_powered(hdev))
- return;
-
- if (!lmp_ext_inq_capable(hdev))
- return;
-
- if (!hci_dev_test_flag(hdev, HCI_SSP_ENABLED))
- return;
-
- if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE))
- return;
-
- memset(&cp, 0, sizeof(cp));
-
- create_eir(hdev, cp.data);
-
- if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0)
- return;
-
- memcpy(hdev->eir, cp.data, sizeof(cp.data));
-
- hci_req_add(req, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
-}
-
-static u8 get_service_classes(struct hci_dev *hdev)
-{
- struct bt_uuid *uuid;
- u8 val = 0;
-
- list_for_each_entry(uuid, &hdev->uuids, list)
- val |= uuid->svc_hint;
-
- return val;
-}
-
-static void update_class(struct hci_request *req)
-{
- struct hci_dev *hdev = req->hdev;
- u8 cod[3];
-
- BT_DBG("%s", hdev->name);
-
- if (!hdev_is_powered(hdev))
- return;
-
- if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
- return;
-
- if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE))
- return;
-
- cod[0] = hdev->minor_class;
- cod[1] = hdev->major_class;
- cod[2] = get_service_classes(hdev);
-
- if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE))
- cod[1] |= 0x20;
-
- if (memcmp(cod, hdev->dev_class, 3) == 0)
- return;
-
- hci_req_add(req, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
-}
-
-static void disable_advertising(struct hci_request *req)
-{
- u8 enable = 0x00;
-
- hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
-}
-
-static void enable_advertising(struct hci_request *req)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_le_set_adv_param cp;
- u8 own_addr_type, enable = 0x01;
- bool connectable;
- u8 instance;
- u32 flags;
-
- if (hci_conn_num(hdev, LE_LINK) > 0)
- return;
-
- if (hci_dev_test_flag(hdev, HCI_LE_ADV))
- disable_advertising(req);
-
- /* Clear the HCI_LE_ADV bit temporarily so that the
- * hci_update_random_address knows that it's safe to go ahead
- * and write a new random address. The flag will be set back on
- * as soon as the SET_ADV_ENABLE HCI command completes.
- */
- hci_dev_clear_flag(hdev, HCI_LE_ADV);
-
- instance = get_current_adv_instance(hdev);
- flags = get_adv_instance_flags(hdev, instance);
-
- /* If the "connectable" instance flag was not set, then choose between
- * ADV_IND and ADV_NONCONN_IND based on the global connectable setting.
- */
- connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE) ||
- get_connectable(hdev);
-
- /* Set require_privacy to true only when non-connectable
- * advertising is used. In that case it is fine to use a
- * non-resolvable private address.
- */
- if (hci_update_random_address(req, !connectable, &own_addr_type) < 0)
- return;
-
- memset(&cp, 0, sizeof(cp));
- cp.min_interval = cpu_to_le16(hdev->le_adv_min_interval);
- cp.max_interval = cpu_to_le16(hdev->le_adv_max_interval);
-
- if (connectable)
- cp.type = LE_ADV_IND;
- else if (get_cur_adv_instance_scan_rsp_len(hdev))
- cp.type = LE_ADV_SCAN_IND;
- else
- cp.type = LE_ADV_NONCONN_IND;
-
- cp.own_address_type = own_addr_type;
- cp.channel_map = hdev->le_adv_channel_map;
-
- hci_req_add(req, HCI_OP_LE_SET_ADV_PARAM, sizeof(cp), &cp);
-
- hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
-}
-
static void service_cache_off(struct work_struct *work)
{
struct hci_dev *hdev = container_of(work, struct hci_dev,
@@ -1323,8 +786,8 @@ static void service_cache_off(struct work_struct *work)
hci_dev_lock(hdev);
- update_eir(&req);
- update_class(&req);
+ __hci_req_update_eir(&req);
+ __hci_req_update_class(&req);
hci_dev_unlock(hdev);
@@ -1345,10 +808,11 @@ static void rpa_expired(struct work_struct *work)
return;
/* The generation of a new RPA and programming it into the
- * controller happens in the enable_advertising() function.
+ * controller happens in the hci_req_enable_advertising()
+ * function.
*/
hci_req_init(&req, hdev);
- enable_advertising(&req);
+ __hci_req_enable_advertising(&req);
hci_req_run(&req, NULL);
}
@@ -1416,51 +880,7 @@ static void clean_up_hci_complete(struct hci_dev *hdev, u8 status, u16 opcode)
}
}
-static bool hci_stop_discovery(struct hci_request *req)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_remote_name_req_cancel cp;
- struct inquiry_entry *e;
-
- switch (hdev->discovery.state) {
- case DISCOVERY_FINDING:
- if (test_bit(HCI_INQUIRY, &hdev->flags))
- hci_req_add(req, HCI_OP_INQUIRY_CANCEL, 0, NULL);
-
- if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
- cancel_delayed_work(&hdev->le_scan_disable);
- hci_req_add_le_scan_disable(req);
- }
-
- return true;
-
- case DISCOVERY_RESOLVING:
- e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY,
- NAME_PENDING);
- if (!e)
- break;
-
- bacpy(&cp.bdaddr, &e->data.bdaddr);
- hci_req_add(req, HCI_OP_REMOTE_NAME_REQ_CANCEL, sizeof(cp),
- &cp);
-
- return true;
-
- default:
- /* Passive scanning */
- if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
- hci_req_add_le_scan_disable(req);
- return true;
- }
-
- break;
- }
-
- return false;
-}
-
-static void advertising_added(struct sock *sk, struct hci_dev *hdev,
- u8 instance)
+void mgmt_advertising_added(struct sock *sk, struct hci_dev *hdev, u8 instance)
{
struct mgmt_ev_advertising_added ev;
@@ -1469,8 +889,8 @@ static void advertising_added(struct sock *sk, struct hci_dev *hdev,
mgmt_event(MGMT_EV_ADVERTISING_ADDED, hdev, &ev, sizeof(ev), sk);
}
-static void advertising_removed(struct sock *sk, struct hci_dev *hdev,
- u8 instance)
+void mgmt_advertising_removed(struct sock *sk, struct hci_dev *hdev,
+ u8 instance)
{
struct mgmt_ev_advertising_removed ev;
@@ -1479,65 +899,6 @@ static void advertising_removed(struct sock *sk, struct hci_dev *hdev,
mgmt_event(MGMT_EV_ADVERTISING_REMOVED, hdev, &ev, sizeof(ev), sk);
}
-static int schedule_adv_instance(struct hci_request *req, u8 instance,
- bool force) {
- struct hci_dev *hdev = req->hdev;
- struct adv_info *adv_instance = NULL;
- u16 timeout;
-
- if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
- !hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
- return -EPERM;
-
- if (hdev->adv_instance_timeout)
- return -EBUSY;
-
- adv_instance = hci_find_adv_instance(hdev, instance);
- if (!adv_instance)
- return -ENOENT;
-
- /* A zero timeout means unlimited advertising. As long as there is
- * only one instance, duration should be ignored. We still set a timeout
- * in case further instances are being added later on.
- *
- * If the remaining lifetime of the instance is more than the duration
- * then the timeout corresponds to the duration, otherwise it will be
- * reduced to the remaining instance lifetime.
- */
- if (adv_instance->timeout == 0 ||
- adv_instance->duration <= adv_instance->remaining_time)
- timeout = adv_instance->duration;
- else
- timeout = adv_instance->remaining_time;
-
- /* The remaining time is being reduced unless the instance is being
- * advertised without time limit.
- */
- if (adv_instance->timeout)
- adv_instance->remaining_time =
- adv_instance->remaining_time - timeout;
-
- hdev->adv_instance_timeout = timeout;
- queue_delayed_work(hdev->workqueue,
- &hdev->adv_instance_expire,
- msecs_to_jiffies(timeout * 1000));
-
- /* If we're just re-scheduling the same instance again then do not
- * execute any HCI commands. This happens when a single instance is
- * being advertised.
- */
- if (!force && hdev->cur_adv_instance == instance &&
- hci_dev_test_flag(hdev, HCI_LE_ADV))
- return 0;
-
- hdev->cur_adv_instance = instance;
- update_adv_data(req);
- update_scan_rsp_data(req);
- enable_advertising(req);
-
- return 0;
-}
-
static void cancel_adv_timeout(struct hci_dev *hdev)
{
if (hdev->adv_instance_timeout) {
@@ -1546,76 +907,6 @@ static void cancel_adv_timeout(struct hci_dev *hdev)
}
}
-/* For a single instance:
- * - force == true: The instance will be removed even when its remaining
- * lifetime is not zero.
- * - force == false: the instance will be deactivated but kept stored unless
- * the remaining lifetime is zero.
- *
- * For instance == 0x00:
- * - force == true: All instances will be removed regardless of their timeout
- * setting.
- * - force == false: Only instances that have a timeout will be removed.
- */
-static void clear_adv_instance(struct hci_dev *hdev, struct hci_request *req,
- u8 instance, bool force)
-{
- struct adv_info *adv_instance, *n, *next_instance = NULL;
- int err;
- u8 rem_inst;
-
- /* Cancel any timeout concerning the removed instance(s). */
- if (!instance || hdev->cur_adv_instance == instance)
- cancel_adv_timeout(hdev);
-
- /* Get the next instance to advertise BEFORE we remove
- * the current one. This can be the same instance again
- * if there is only one instance.
- */
- if (instance && hdev->cur_adv_instance == instance)
- next_instance = hci_get_next_instance(hdev, instance);
-
- if (instance == 0x00) {
- list_for_each_entry_safe(adv_instance, n, &hdev->adv_instances,
- list) {
- if (!(force || adv_instance->timeout))
- continue;
-
- rem_inst = adv_instance->instance;
- err = hci_remove_adv_instance(hdev, rem_inst);
- if (!err)
- advertising_removed(NULL, hdev, rem_inst);
- }
- hdev->cur_adv_instance = 0x00;
- } else {
- adv_instance = hci_find_adv_instance(hdev, instance);
-
- if (force || (adv_instance && adv_instance->timeout &&
- !adv_instance->remaining_time)) {
- /* Don't advertise a removed instance. */
- if (next_instance &&
- next_instance->instance == instance)
- next_instance = NULL;
-
- err = hci_remove_adv_instance(hdev, instance);
- if (!err)
- advertising_removed(NULL, hdev, instance);
- }
- }
-
- if (list_empty(&hdev->adv_instances)) {
- hdev->cur_adv_instance = 0x00;
- hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE);
- }
-
- if (!req || !hdev_is_powered(hdev) ||
- hci_dev_test_flag(hdev, HCI_ADVERTISING))
- return;
-
- if (next_instance)
- schedule_adv_instance(req, next_instance->instance, false);
-}
-
static int clean_up_hci_state(struct hci_dev *hdev)
{
struct hci_request req;
@@ -1631,12 +922,12 @@ static int clean_up_hci_state(struct hci_dev *hdev)
hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
}
- clear_adv_instance(hdev, NULL, 0x00, false);
+ hci_req_clear_adv_instance(hdev, NULL, 0x00, false);
if (hci_dev_test_flag(hdev, HCI_LE_ADV))
- disable_advertising(&req);
+ __hci_req_disable_advertising(&req);
- discov_stopped = hci_stop_discovery(&req);
+ discov_stopped = hci_req_stop_discovery(&req);
list_for_each_entry(conn, &hdev->conn_hash.list, list) {
/* 0x15 == Terminated due to Power Off */
@@ -1671,17 +962,6 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}
- if (hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) {
- cancel_delayed_work(&hdev->power_off);
-
- if (cp->val) {
- mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev,
- data, len);
- err = mgmt_powered(hdev, 1);
- goto failed;
- }
- }
-
if (!!cp->val == hdev_is_powered(hdev)) {
err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);
goto failed;
@@ -1805,13 +1085,9 @@ static u8 mgmt_le_support(struct hci_dev *hdev)
return MGMT_STATUS_SUCCESS;
}
-static void set_discoverable_complete(struct hci_dev *hdev, u8 status,
- u16 opcode)
+void mgmt_set_discoverable_complete(struct hci_dev *hdev, u8 status)
{
struct mgmt_pending_cmd *cmd;
- struct mgmt_mode *cp;
- struct hci_request req;
- bool changed;
BT_DBG("status 0x%02x", status);
@@ -1828,33 +1104,14 @@ static void set_discoverable_complete(struct hci_dev *hdev, u8 status,
goto remove_cmd;
}
- cp = cmd->param;
- if (cp->val) {
- changed = !hci_dev_test_and_set_flag(hdev, HCI_DISCOVERABLE);
-
- if (hdev->discov_timeout > 0) {
- int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
- queue_delayed_work(hdev->workqueue, &hdev->discov_off,
- to);
- }
- } else {
- changed = hci_dev_test_and_clear_flag(hdev, HCI_DISCOVERABLE);
+ if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE) &&
+ hdev->discov_timeout > 0) {
+ int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
+ queue_delayed_work(hdev->req_workqueue, &hdev->discov_off, to);
}
send_settings_rsp(cmd->sk, MGMT_OP_SET_DISCOVERABLE, hdev);
-
- if (changed)
- new_settings(hdev, cmd->sk);
-
- /* When the discoverable mode gets changed, make sure
- * that class of device has the limited discoverable
- * bit correctly set. Also update page scan based on whitelist
- * entries.
- */
- hci_req_init(&req, hdev);
- __hci_update_page_scan(&req);
- update_class(&req);
- hci_req_run(&req, NULL);
+ new_settings(hdev, cmd->sk);
remove_cmd:
mgmt_pending_remove(cmd);
@@ -1868,9 +1125,7 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
{
struct mgmt_cp_set_discoverable *cp = data;
struct mgmt_pending_cmd *cmd;
- struct hci_request req;
u16 timeout;
- u8 scan;
int err;
BT_DBG("request for %s", hdev->name);
@@ -1949,8 +1204,8 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
if (cp->val && hdev->discov_timeout > 0) {
int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
- queue_delayed_work(hdev->workqueue, &hdev->discov_off,
- to);
+ queue_delayed_work(hdev->req_workqueue,
+ &hdev->discov_off, to);
}
err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev);
@@ -1970,105 +1225,28 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
cancel_delayed_work(&hdev->discov_off);
hdev->discov_timeout = timeout;
+ if (cp->val)
+ hci_dev_set_flag(hdev, HCI_DISCOVERABLE);
+ else
+ hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
+
/* Limited discoverable mode */
if (cp->val == 0x02)
hci_dev_set_flag(hdev, HCI_LIMITED_DISCOVERABLE);
else
hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
- hci_req_init(&req, hdev);
-
- /* The procedure for LE-only controllers is much simpler - just
- * update the advertising data.
- */
- if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
- goto update_ad;
-
- scan = SCAN_PAGE;
-
- if (cp->val) {
- struct hci_cp_write_current_iac_lap hci_cp;
-
- if (cp->val == 0x02) {
- /* Limited discoverable mode */
- hci_cp.num_iac = min_t(u8, hdev->num_iac, 2);
- hci_cp.iac_lap[0] = 0x00; /* LIAC */
- hci_cp.iac_lap[1] = 0x8b;
- hci_cp.iac_lap[2] = 0x9e;
- hci_cp.iac_lap[3] = 0x33; /* GIAC */
- hci_cp.iac_lap[4] = 0x8b;
- hci_cp.iac_lap[5] = 0x9e;
- } else {
- /* General discoverable mode */
- hci_cp.num_iac = 1;
- hci_cp.iac_lap[0] = 0x33; /* GIAC */
- hci_cp.iac_lap[1] = 0x8b;
- hci_cp.iac_lap[2] = 0x9e;
- }
-
- hci_req_add(&req, HCI_OP_WRITE_CURRENT_IAC_LAP,
- (hci_cp.num_iac * 3) + 1, &hci_cp);
-
- scan |= SCAN_INQUIRY;
- } else {
- hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
- }
-
- hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan);
-
-update_ad:
- update_adv_data(&req);
-
- err = hci_req_run(&req, set_discoverable_complete);
- if (err < 0)
- mgmt_pending_remove(cmd);
+ queue_work(hdev->req_workqueue, &hdev->discoverable_update);
+ err = 0;
failed:
hci_dev_unlock(hdev);
return err;
}
-static void write_fast_connectable(struct hci_request *req, bool enable)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_write_page_scan_activity acp;
- u8 type;
-
- if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
- return;
-
- if (hdev->hci_ver < BLUETOOTH_VER_1_2)
- return;
-
- if (enable) {
- type = PAGE_SCAN_TYPE_INTERLACED;
-
- /* 160 msec page scan interval */
- acp.interval = cpu_to_le16(0x0100);
- } else {
- type = PAGE_SCAN_TYPE_STANDARD; /* default */
-
- /* default 1.28 sec page scan */
- acp.interval = cpu_to_le16(0x0800);
- }
-
- acp.window = cpu_to_le16(0x0012);
-
- if (__cpu_to_le16(hdev->page_scan_interval) != acp.interval ||
- __cpu_to_le16(hdev->page_scan_window) != acp.window)
- hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY,
- sizeof(acp), &acp);
-
- if (hdev->page_scan_type != type)
- hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
-}
-
-static void set_connectable_complete(struct hci_dev *hdev, u8 status,
- u16 opcode)
+void mgmt_set_connectable_complete(struct hci_dev *hdev, u8 status)
{
struct mgmt_pending_cmd *cmd;
- struct mgmt_mode *cp;
- bool conn_changed, discov_changed;
BT_DBG("status 0x%02x", status);
@@ -2084,27 +1262,8 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status,
goto remove_cmd;
}
- cp = cmd->param;
- if (cp->val) {
- conn_changed = !hci_dev_test_and_set_flag(hdev,
- HCI_CONNECTABLE);
- discov_changed = false;
- } else {
- conn_changed = hci_dev_test_and_clear_flag(hdev,
- HCI_CONNECTABLE);
- discov_changed = hci_dev_test_and_clear_flag(hdev,
- HCI_DISCOVERABLE);
- }
-
send_settings_rsp(cmd->sk, MGMT_OP_SET_CONNECTABLE, hdev);
-
- if (conn_changed || discov_changed) {
- new_settings(hdev, cmd->sk);
- hci_update_page_scan(hdev);
- if (discov_changed)
- mgmt_update_adv_data(hdev);
- hci_update_background_scan(hdev);
- }
+ new_settings(hdev, cmd->sk);
remove_cmd:
mgmt_pending_remove(cmd);
@@ -2134,7 +1293,7 @@ static int set_connectable_update_settings(struct hci_dev *hdev,
return err;
if (changed) {
- hci_update_page_scan(hdev);
+ hci_req_update_scan(hdev);
hci_update_background_scan(hdev);
return new_settings(hdev, sk);
}
@@ -2147,8 +1306,6 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
{
struct mgmt_mode *cp = data;
struct mgmt_pending_cmd *cmd;
- struct hci_request req;
- u8 scan;
int err;
BT_DBG("request for %s", hdev->name);
@@ -2182,57 +1339,19 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}
- hci_req_init(&req, hdev);
-
- /* If BR/EDR is not enabled and we disable advertising as a
- * by-product of disabling connectable, we need to update the
- * advertising flags.
- */
- if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
- if (!cp->val) {
- hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
- hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
- }
- update_adv_data(&req);
- } else if (cp->val != test_bit(HCI_PSCAN, &hdev->flags)) {
- if (cp->val) {
- scan = SCAN_PAGE;
- } else {
- /* If we don't have any whitelist entries just
- * disable all scanning. If there are entries
- * and we had both page and inquiry scanning
- * enabled then fall back to only page scanning.
- * Otherwise no changes are needed.
- */
- if (list_empty(&hdev->whitelist))
- scan = SCAN_DISABLED;
- else if (test_bit(HCI_ISCAN, &hdev->flags))
- scan = SCAN_PAGE;
- else
- goto no_scan_update;
-
- if (test_bit(HCI_ISCAN, &hdev->flags) &&
- hdev->discov_timeout > 0)
- cancel_delayed_work(&hdev->discov_off);
- }
+ if (cp->val) {
+ hci_dev_set_flag(hdev, HCI_CONNECTABLE);
+ } else {
+ if (hdev->discov_timeout > 0)
+ cancel_delayed_work(&hdev->discov_off);
- hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+ hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
+ hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
+ hci_dev_clear_flag(hdev, HCI_CONNECTABLE);
}
-no_scan_update:
- /* Update the advertising parameters if necessary */
- if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
- hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
- enable_advertising(&req);
-
- err = hci_req_run(&req, set_connectable_complete);
- if (err < 0) {
- mgmt_pending_remove(cmd);
- if (err == -ENODATA)
- err = set_connectable_update_settings(hdev, sk,
- cp->val);
- goto failed;
- }
+ queue_work(hdev->req_workqueue, &hdev->connectable_update);
+ err = 0;
failed:
hci_dev_unlock(hdev);
@@ -2508,10 +1627,10 @@ static void le_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode)
struct hci_request req;
hci_req_init(&req, hdev);
- update_adv_data(&req);
- update_scan_rsp_data(&req);
- __hci_update_background_scan(&req);
+ __hci_req_update_adv_data(&req, 0x00);
+ __hci_req_update_scan_rsp_data(&req, 0x00);
hci_req_run(&req, NULL);
+ hci_update_background_scan(hdev);
}
unlock:
@@ -2560,7 +1679,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
enabled = lmp_host_le_capable(hdev);
if (!val)
- clear_adv_instance(hdev, NULL, 0x00, true);
+ hci_req_clear_adv_instance(hdev, NULL, 0x00, true);
if (!hdev_is_powered(hdev) || val == enabled) {
bool changed = false;
@@ -2607,7 +1726,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
hci_cp.simul = 0x00;
} else {
if (hci_dev_test_flag(hdev, HCI_LE_ADV))
- disable_advertising(&req);
+ __hci_req_disable_advertising(&req);
}
hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp),
@@ -2722,8 +1841,8 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
hci_req_init(&req, hdev);
- update_class(&req);
- update_eir(&req);
+ __hci_req_update_class(&req);
+ __hci_req_update_eir(&req);
err = hci_req_run(&req, add_uuid_complete);
if (err < 0) {
@@ -2822,8 +1941,8 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data,
update_class:
hci_req_init(&req, hdev);
- update_class(&req);
- update_eir(&req);
+ __hci_req_update_class(&req);
+ __hci_req_update_eir(&req);
err = hci_req_run(&req, remove_uuid_complete);
if (err < 0) {
@@ -2898,10 +2017,10 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data,
hci_dev_unlock(hdev);
cancel_delayed_work_sync(&hdev->service_cache);
hci_dev_lock(hdev);
- update_eir(&req);
+ __hci_req_update_eir(&req);
}
- update_class(&req);
+ __hci_req_update_class(&req);
err = hci_req_run(&req, set_class_complete);
if (err < 0) {
@@ -3561,8 +2680,7 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
conn = hci_connect_le_scan(hdev, &cp->addr.bdaddr,
addr_type, sec_level,
- HCI_LE_CONN_TIMEOUT,
- HCI_ROLE_MASTER);
+ HCI_LE_CONN_TIMEOUT);
}
if (IS_ERR(conn)) {
@@ -3803,16 +2921,6 @@ static int user_passkey_neg_reply(struct sock *sk, struct hci_dev *hdev,
HCI_OP_USER_PASSKEY_NEG_REPLY, 0);
}
-static void update_name(struct hci_request *req)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_write_local_name cp;
-
- memcpy(cp.name, hdev->dev_name, sizeof(cp.name));
-
- hci_req_add(req, HCI_OP_WRITE_LOCAL_NAME, sizeof(cp), &cp);
-}
-
static void set_name_complete(struct hci_dev *hdev, u8 status, u16 opcode)
{
struct mgmt_cp_set_local_name *cp;
@@ -3891,15 +2999,15 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
hci_req_init(&req, hdev);
if (lmp_bredr_capable(hdev)) {
- update_name(&req);
- update_eir(&req);
+ __hci_req_update_name(&req);
+ __hci_req_update_eir(&req);
}
/* The name is stored in the scan response data and so
* no need to udpate the advertising data here.
*/
if (lmp_le_capable(hdev))
- update_scan_rsp_data(&req);
+ __hci_req_update_scan_rsp_data(&req, hdev->cur_adv_instance);
err = hci_req_run(&req, set_name_complete);
if (err < 0)
@@ -4164,145 +3272,9 @@ done:
return err;
}
-static bool trigger_bredr_inquiry(struct hci_request *req, u8 *status)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_inquiry cp;
- /* General inquiry access code (GIAC) */
- u8 lap[3] = { 0x33, 0x8b, 0x9e };
-
- *status = mgmt_bredr_support(hdev);
- if (*status)
- return false;
-
- if (hci_dev_test_flag(hdev, HCI_INQUIRY)) {
- *status = MGMT_STATUS_BUSY;
- return false;
- }
-
- hci_inquiry_cache_flush(hdev);
-
- memset(&cp, 0, sizeof(cp));
- memcpy(&cp.lap, lap, sizeof(cp.lap));
- cp.length = DISCOV_BREDR_INQUIRY_LEN;
-
- hci_req_add(req, HCI_OP_INQUIRY, sizeof(cp), &cp);
-
- return true;
-}
-
-static bool trigger_le_scan(struct hci_request *req, u16 interval, u8 *status)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_le_set_scan_param param_cp;
- struct hci_cp_le_set_scan_enable enable_cp;
- u8 own_addr_type;
- int err;
-
- *status = mgmt_le_support(hdev);
- if (*status)
- return false;
-
- if (hci_dev_test_flag(hdev, HCI_LE_ADV)) {
- /* Don't let discovery abort an outgoing connection attempt
- * that's using directed advertising.
- */
- if (hci_lookup_le_connect(hdev)) {
- *status = MGMT_STATUS_REJECTED;
- return false;
- }
-
- cancel_adv_timeout(hdev);
- disable_advertising(req);
- }
-
- /* If controller is scanning, it means the background scanning is
- * running. Thus, we should temporarily stop it in order to set the
- * discovery scanning parameters.
- */
- if (hci_dev_test_flag(hdev, HCI_LE_SCAN))
- hci_req_add_le_scan_disable(req);
-
- /* All active scans will be done with either a resolvable private
- * address (when privacy feature has been enabled) or non-resolvable
- * private address.
- */
- err = hci_update_random_address(req, true, &own_addr_type);
- if (err < 0) {
- *status = MGMT_STATUS_FAILED;
- return false;
- }
-
- memset(&param_cp, 0, sizeof(param_cp));
- param_cp.type = LE_SCAN_ACTIVE;
- param_cp.interval = cpu_to_le16(interval);
- param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
- param_cp.own_address_type = own_addr_type;
-
- hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
- &param_cp);
-
- memset(&enable_cp, 0, sizeof(enable_cp));
- enable_cp.enable = LE_SCAN_ENABLE;
- enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
-
- hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
- &enable_cp);
-
- return true;
-}
-
-static bool trigger_discovery(struct hci_request *req, u8 *status)
-{
- struct hci_dev *hdev = req->hdev;
-
- switch (hdev->discovery.type) {
- case DISCOV_TYPE_BREDR:
- if (!trigger_bredr_inquiry(req, status))
- return false;
- break;
-
- case DISCOV_TYPE_INTERLEAVED:
- if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
- &hdev->quirks)) {
- /* During simultaneous discovery, we double LE scan
- * interval. We must leave some time for the controller
- * to do BR/EDR inquiry.
- */
- if (!trigger_le_scan(req, DISCOV_LE_SCAN_INT * 2,
- status))
- return false;
-
- if (!trigger_bredr_inquiry(req, status))
- return false;
-
- return true;
- }
-
- if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
- *status = MGMT_STATUS_NOT_SUPPORTED;
- return false;
- }
- /* fall through */
-
- case DISCOV_TYPE_LE:
- if (!trigger_le_scan(req, DISCOV_LE_SCAN_INT, status))
- return false;
- break;
-
- default:
- *status = MGMT_STATUS_INVALID_PARAMS;
- return false;
- }
-
- return true;
-}
-
-static void start_discovery_complete(struct hci_dev *hdev, u8 status,
- u16 opcode)
+void mgmt_start_discovery_complete(struct hci_dev *hdev, u8 status)
{
struct mgmt_pending_cmd *cmd;
- unsigned long timeout;
BT_DBG("status %d", status);
@@ -4312,75 +3284,49 @@ static void start_discovery_complete(struct hci_dev *hdev, u8 status,
if (!cmd)
cmd = pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev);
+ if (!cmd)
+ cmd = pending_find(MGMT_OP_START_LIMITED_DISCOVERY, hdev);
+
if (cmd) {
cmd->cmd_complete(cmd, mgmt_status(status));
mgmt_pending_remove(cmd);
}
- if (status) {
- hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
- goto unlock;
- }
-
- hci_discovery_set_state(hdev, DISCOVERY_FINDING);
+ hci_dev_unlock(hdev);
+}
- /* If the scan involves LE scan, pick proper timeout to schedule
- * hdev->le_scan_disable that will stop it.
- */
- switch (hdev->discovery.type) {
+static bool discovery_type_is_valid(struct hci_dev *hdev, uint8_t type,
+ uint8_t *mgmt_status)
+{
+ switch (type) {
case DISCOV_TYPE_LE:
- timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
+ *mgmt_status = mgmt_le_support(hdev);
+ if (*mgmt_status)
+ return false;
break;
case DISCOV_TYPE_INTERLEAVED:
- /* When running simultaneous discovery, the LE scanning time
- * should occupy the whole discovery time sine BR/EDR inquiry
- * and LE scanning are scheduled by the controller.
- *
- * For interleaving discovery in comparison, BR/EDR inquiry
- * and LE scanning are done sequentially with separate
- * timeouts.
- */
- if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks))
- timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
- else
- timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout);
- break;
+ *mgmt_status = mgmt_le_support(hdev);
+ if (*mgmt_status)
+ return false;
+ /* Intentional fall-through */
case DISCOV_TYPE_BREDR:
- timeout = 0;
+ *mgmt_status = mgmt_bredr_support(hdev);
+ if (*mgmt_status)
+ return false;
break;
default:
- BT_ERR("Invalid discovery type %d", hdev->discovery.type);
- timeout = 0;
- break;
- }
-
- if (timeout) {
- /* When service discovery is used and the controller has
- * a strict duplicate filter, it is important to remember
- * the start and duration of the scan. This is required
- * for restarting scanning during the discovery phase.
- */
- if (test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER,
- &hdev->quirks) &&
- hdev->discovery.result_filtering) {
- hdev->discovery.scan_start = jiffies;
- hdev->discovery.scan_duration = timeout;
- }
-
- queue_delayed_work(hdev->workqueue,
- &hdev->le_scan_disable, timeout);
+ *mgmt_status = MGMT_STATUS_INVALID_PARAMS;
+ return false;
}
-unlock:
- hci_dev_unlock(hdev);
+ return true;
}
-static int start_discovery(struct sock *sk, struct hci_dev *hdev,
- void *data, u16 len)
+static int start_discovery_internal(struct sock *sk, struct hci_dev *hdev,
+ u16 op, void *data, u16 len)
{
struct mgmt_cp_start_discovery *cp = data;
struct mgmt_pending_cmd *cmd;
- struct hci_request req;
u8 status;
int err;
@@ -4389,7 +3335,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
hci_dev_lock(hdev);
if (!hdev_is_powered(hdev)) {
- err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+ err = mgmt_cmd_complete(sk, hdev->id, op,
MGMT_STATUS_NOT_POWERED,
&cp->type, sizeof(cp->type));
goto failed;
@@ -4397,20 +3343,17 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
if (hdev->discovery.state != DISCOVERY_STOPPED ||
hci_dev_test_flag(hdev, HCI_PERIODIC_INQ)) {
- err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_BUSY, &cp->type,
- sizeof(cp->type));
+ err = mgmt_cmd_complete(sk, hdev->id, op, MGMT_STATUS_BUSY,
+ &cp->type, sizeof(cp->type));
goto failed;
}
- cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, data, len);
- if (!cmd) {
- err = -ENOMEM;
+ if (!discovery_type_is_valid(hdev, cp->type, &status)) {
+ err = mgmt_cmd_complete(sk, hdev->id, op, status,
+ &cp->type, sizeof(cp->type));
goto failed;
}
- cmd->cmd_complete = generic_cmd_complete;
-
/* Clear the discovery filter first to free any previously
* allocated memory for the UUID list.
*/
@@ -4418,29 +3361,43 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
hdev->discovery.type = cp->type;
hdev->discovery.report_invalid_rssi = false;
+ if (op == MGMT_OP_START_LIMITED_DISCOVERY)
+ hdev->discovery.limited = true;
+ else
+ hdev->discovery.limited = false;
- hci_req_init(&req, hdev);
-
- if (!trigger_discovery(&req, &status)) {
- err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- status, &cp->type, sizeof(cp->type));
- mgmt_pending_remove(cmd);
+ cmd = mgmt_pending_add(sk, op, hdev, data, len);
+ if (!cmd) {
+ err = -ENOMEM;
goto failed;
}
- err = hci_req_run(&req, start_discovery_complete);
- if (err < 0) {
- mgmt_pending_remove(cmd);
- goto failed;
- }
+ cmd->cmd_complete = generic_cmd_complete;
hci_discovery_set_state(hdev, DISCOVERY_STARTING);
+ queue_work(hdev->req_workqueue, &hdev->discov_update);
+ err = 0;
failed:
hci_dev_unlock(hdev);
return err;
}
+static int start_discovery(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ return start_discovery_internal(sk, hdev, MGMT_OP_START_DISCOVERY,
+ data, len);
+}
+
+static int start_limited_discovery(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ return start_discovery_internal(sk, hdev,
+ MGMT_OP_START_LIMITED_DISCOVERY,
+ data, len);
+}
+
static int service_discovery_cmd_complete(struct mgmt_pending_cmd *cmd,
u8 status)
{
@@ -4453,7 +3410,6 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
{
struct mgmt_cp_start_service_discovery *cp = data;
struct mgmt_pending_cmd *cmd;
- struct hci_request req;
const u16 max_uuid_count = ((U16_MAX - sizeof(*cp)) / 16);
u16 uuid_count, expected_len;
u8 status;
@@ -4502,6 +3458,13 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
goto failed;
}
+ if (!discovery_type_is_valid(hdev, cp->type, &status)) {
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_START_SERVICE_DISCOVERY,
+ status, &cp->type, sizeof(cp->type));
+ goto failed;
+ }
+
cmd = mgmt_pending_add(sk, MGMT_OP_START_SERVICE_DISCOVERY,
hdev, data, len);
if (!cmd) {
@@ -4534,30 +3497,16 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
}
}
- hci_req_init(&req, hdev);
-
- if (!trigger_discovery(&req, &status)) {
- err = mgmt_cmd_complete(sk, hdev->id,
- MGMT_OP_START_SERVICE_DISCOVERY,
- status, &cp->type, sizeof(cp->type));
- mgmt_pending_remove(cmd);
- goto failed;
- }
-
- err = hci_req_run(&req, start_discovery_complete);
- if (err < 0) {
- mgmt_pending_remove(cmd);
- goto failed;
- }
-
hci_discovery_set_state(hdev, DISCOVERY_STARTING);
+ queue_work(hdev->req_workqueue, &hdev->discov_update);
+ err = 0;
failed:
hci_dev_unlock(hdev);
return err;
}
-static void stop_discovery_complete(struct hci_dev *hdev, u8 status, u16 opcode)
+void mgmt_stop_discovery_complete(struct hci_dev *hdev, u8 status)
{
struct mgmt_pending_cmd *cmd;
@@ -4571,9 +3520,6 @@ static void stop_discovery_complete(struct hci_dev *hdev, u8 status, u16 opcode)
mgmt_pending_remove(cmd);
}
- if (!status)
- hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
-
hci_dev_unlock(hdev);
}
@@ -4582,7 +3528,6 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
{
struct mgmt_cp_stop_discovery *mgmt_cp = data;
struct mgmt_pending_cmd *cmd;
- struct hci_request req;
int err;
BT_DBG("%s", hdev->name);
@@ -4611,24 +3556,9 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
cmd->cmd_complete = generic_cmd_complete;
- hci_req_init(&req, hdev);
-
- hci_stop_discovery(&req);
-
- err = hci_req_run(&req, stop_discovery_complete);
- if (!err) {
- hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
- goto unlock;
- }
-
- mgmt_pending_remove(cmd);
-
- /* If no HCI commands were sent we're done */
- if (err == -ENODATA) {
- err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, 0,
- &mgmt_cp->type, sizeof(mgmt_cp->type));
- hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
- }
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
+ queue_work(hdev->req_workqueue, &hdev->discov_update);
+ err = 0;
unlock:
hci_dev_unlock(hdev);
@@ -4776,7 +3706,7 @@ static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data,
NULL, 0);
hci_req_init(&req, hdev);
- update_eir(&req);
+ __hci_req_update_eir(&req);
hci_req_run(&req, NULL);
hci_dev_unlock(hdev);
@@ -4826,7 +3756,6 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status,
* set up earlier, then re-enable multi-instance advertising.
*/
if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
- !hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) ||
list_empty(&hdev->adv_instances))
goto unlock;
@@ -4842,7 +3771,7 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status,
hci_req_init(&req, hdev);
- err = schedule_adv_instance(&req, instance, true);
+ err = __hci_req_schedule_adv_instance(&req, instance, true);
if (!err)
err = hci_req_run(&req, enable_advertising_instance);
@@ -4892,6 +3821,7 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data,
bool changed;
if (cp->val) {
+ hdev->cur_adv_instance = 0x00;
changed = !hci_dev_test_and_set_flag(hdev, HCI_ADVERTISING);
if (cp->val == 0x02)
hci_dev_set_flag(hdev, HCI_ADVERTISING_CONNECTABLE);
@@ -4939,11 +3869,12 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data,
* We cannot use update_[adv|scan_rsp]_data() here as the
* HCI_ADVERTISING flag is not yet set.
*/
- update_inst_adv_data(&req, 0x00);
- update_inst_scan_rsp_data(&req, 0x00);
- enable_advertising(&req);
+ hdev->cur_adv_instance = 0x00;
+ __hci_req_update_adv_data(&req, 0x00);
+ __hci_req_update_scan_rsp_data(&req, 0x00);
+ __hci_req_enable_advertising(&req);
} else {
- disable_advertising(&req);
+ __hci_req_disable_advertising(&req);
}
err = hci_req_run(&req, set_advertising_complete);
@@ -5140,7 +4071,7 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
hci_req_init(&req, hdev);
- write_fast_connectable(&req, cp->val);
+ __hci_req_write_fast_connectable(&req, cp->val);
err = hci_req_run(&req, fast_connectable_complete);
if (err < 0) {
@@ -5275,20 +4206,20 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
goto unlock;
}
- /* We need to flip the bit already here so that update_adv_data
- * generates the correct flags.
+ /* We need to flip the bit already here so that
+ * hci_req_update_adv_data generates the correct flags.
*/
hci_dev_set_flag(hdev, HCI_BREDR_ENABLED);
hci_req_init(&req, hdev);
- write_fast_connectable(&req, false);
- __hci_update_page_scan(&req);
+ __hci_req_write_fast_connectable(&req, false);
+ __hci_req_update_scan(&req);
/* Since only the advertising data flags will change, there
* is no need to update the scan response data.
*/
- update_adv_data(&req);
+ __hci_req_update_adv_data(&req, hdev->cur_adv_instance);
err = hci_req_run(&req, set_bredr_complete);
if (err < 0)
@@ -6076,10 +5007,9 @@ static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
}
/* This function requires the caller holds hdev->lock */
-static int hci_conn_params_set(struct hci_request *req, bdaddr_t *addr,
+static int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr,
u8 addr_type, u8 auto_connect)
{
- struct hci_dev *hdev = req->hdev;
struct hci_conn_params *params;
params = hci_conn_params_add(hdev, addr, addr_type);
@@ -6099,26 +5029,17 @@ static int hci_conn_params_set(struct hci_request *req, bdaddr_t *addr,
*/
if (params->explicit_connect)
list_add(&params->action, &hdev->pend_le_conns);
-
- __hci_update_background_scan(req);
break;
case HCI_AUTO_CONN_REPORT:
if (params->explicit_connect)
list_add(&params->action, &hdev->pend_le_conns);
else
list_add(&params->action, &hdev->pend_le_reports);
- __hci_update_background_scan(req);
break;
case HCI_AUTO_CONN_DIRECT:
case HCI_AUTO_CONN_ALWAYS:
- if (!is_connected(hdev, addr, addr_type)) {
+ if (!is_connected(hdev, addr, addr_type))
list_add(&params->action, &hdev->pend_le_conns);
- /* If we are in scan phase of connecting, we were
- * already added to pend_le_conns and scanning.
- */
- if (params->auto_connect != HCI_AUTO_CONN_EXPLICIT)
- __hci_update_background_scan(req);
- }
break;
}
@@ -6142,31 +5063,10 @@ static void device_added(struct sock *sk, struct hci_dev *hdev,
mgmt_event(MGMT_EV_DEVICE_ADDED, hdev, &ev, sizeof(ev), sk);
}
-static void add_device_complete(struct hci_dev *hdev, u8 status, u16 opcode)
-{
- struct mgmt_pending_cmd *cmd;
-
- BT_DBG("status 0x%02x", status);
-
- hci_dev_lock(hdev);
-
- cmd = pending_find(MGMT_OP_ADD_DEVICE, hdev);
- if (!cmd)
- goto unlock;
-
- cmd->cmd_complete(cmd, mgmt_status(status));
- mgmt_pending_remove(cmd);
-
-unlock:
- hci_dev_unlock(hdev);
-}
-
static int add_device(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
struct mgmt_cp_add_device *cp = data;
- struct mgmt_pending_cmd *cmd;
- struct hci_request req;
u8 auto_conn, addr_type;
int err;
@@ -6183,24 +5083,15 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
MGMT_STATUS_INVALID_PARAMS,
&cp->addr, sizeof(cp->addr));
- hci_req_init(&req, hdev);
-
hci_dev_lock(hdev);
- cmd = mgmt_pending_add(sk, MGMT_OP_ADD_DEVICE, hdev, data, len);
- if (!cmd) {
- err = -ENOMEM;
- goto unlock;
- }
-
- cmd->cmd_complete = addr_cmd_complete;
-
if (cp->addr.type == BDADDR_BREDR) {
/* Only incoming connections action is supported for now */
if (cp->action != 0x01) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_ADD_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
@@ -6209,7 +5100,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
if (err)
goto unlock;
- __hci_update_page_scan(&req);
+ hci_req_update_scan(hdev);
goto added;
}
@@ -6229,33 +5120,31 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
* hci_conn_params_lookup.
*/
if (!hci_is_identity_address(&cp->addr.bdaddr, addr_type)) {
- err = cmd->cmd_complete(cmd, MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
/* If the connection parameters don't exist for this device,
* they will be created and configured with defaults.
*/
- if (hci_conn_params_set(&req, &cp->addr.bdaddr, addr_type,
+ if (hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type,
auto_conn) < 0) {
- err = cmd->cmd_complete(cmd, MGMT_STATUS_FAILED);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
+ MGMT_STATUS_FAILED, &cp->addr,
+ sizeof(cp->addr));
goto unlock;
}
+ hci_update_background_scan(hdev);
+
added:
device_added(sk, hdev, &cp->addr.bdaddr, cp->addr.type, cp->action);
- err = hci_req_run(&req, add_device_complete);
- if (err < 0) {
- /* ENODATA means no HCI commands were needed (e.g. if
- * the adapter is powered off).
- */
- if (err == -ENODATA)
- err = cmd->cmd_complete(cmd, MGMT_STATUS_SUCCESS);
- mgmt_pending_remove(cmd);
- }
+ err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
+ MGMT_STATUS_SUCCESS, &cp->addr,
+ sizeof(cp->addr));
unlock:
hci_dev_unlock(hdev);
@@ -6273,55 +5162,25 @@ static void device_removed(struct sock *sk, struct hci_dev *hdev,
mgmt_event(MGMT_EV_DEVICE_REMOVED, hdev, &ev, sizeof(ev), sk);
}
-static void remove_device_complete(struct hci_dev *hdev, u8 status, u16 opcode)
-{
- struct mgmt_pending_cmd *cmd;
-
- BT_DBG("status 0x%02x", status);
-
- hci_dev_lock(hdev);
-
- cmd = pending_find(MGMT_OP_REMOVE_DEVICE, hdev);
- if (!cmd)
- goto unlock;
-
- cmd->cmd_complete(cmd, mgmt_status(status));
- mgmt_pending_remove(cmd);
-
-unlock:
- hci_dev_unlock(hdev);
-}
-
static int remove_device(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
struct mgmt_cp_remove_device *cp = data;
- struct mgmt_pending_cmd *cmd;
- struct hci_request req;
int err;
BT_DBG("%s", hdev->name);
- hci_req_init(&req, hdev);
-
hci_dev_lock(hdev);
- cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_DEVICE, hdev, data, len);
- if (!cmd) {
- err = -ENOMEM;
- goto unlock;
- }
-
- cmd->cmd_complete = addr_cmd_complete;
-
if (bacmp(&cp->addr.bdaddr, BDADDR_ANY)) {
struct hci_conn_params *params;
u8 addr_type;
if (!bdaddr_type_is_valid(cp->addr.type)) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
@@ -6330,13 +5189,15 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
&cp->addr.bdaddr,
cp->addr.type);
if (err) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr,
+ sizeof(cp->addr));
goto unlock;
}
- __hci_update_page_scan(&req);
+ hci_req_update_scan(hdev);
device_removed(sk, hdev, &cp->addr.bdaddr,
cp->addr.type);
@@ -6351,33 +5212,36 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
* hci_conn_params_lookup.
*/
if (!hci_is_identity_address(&cp->addr.bdaddr, addr_type)) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
addr_type);
if (!params) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
if (params->auto_connect == HCI_AUTO_CONN_DISABLED ||
params->auto_connect == HCI_AUTO_CONN_EXPLICIT) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
list_del(&params->action);
list_del(&params->list);
kfree(params);
- __hci_update_background_scan(&req);
+ hci_update_background_scan(hdev);
device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type);
} else {
@@ -6385,9 +5249,10 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
struct bdaddr_list *b, *btmp;
if (cp->addr.type) {
- err = cmd->cmd_complete(cmd,
- MGMT_STATUS_INVALID_PARAMS);
- mgmt_pending_remove(cmd);
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
goto unlock;
}
@@ -6397,7 +5262,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
kfree(b);
}
- __hci_update_page_scan(&req);
+ hci_req_update_scan(hdev);
list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) {
if (p->auto_connect == HCI_AUTO_CONN_DISABLED)
@@ -6414,20 +5279,13 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
BT_DBG("All LE connection parameters were removed");
- __hci_update_background_scan(&req);
+ hci_update_background_scan(hdev);
}
complete:
- err = hci_req_run(&req, remove_device_complete);
- if (err < 0) {
- /* ENODATA means no HCI commands were needed (e.g. if
- * the adapter is powered off).
- */
- if (err == -ENODATA)
- err = cmd->cmd_complete(cmd, MGMT_STATUS_SUCCESS);
- mgmt_pending_remove(cmd);
- }
-
+ err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
+ MGMT_STATUS_SUCCESS, &cp->addr,
+ sizeof(cp->addr));
unlock:
hci_dev_unlock(hdev);
return err;
@@ -6898,7 +5756,7 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev,
rand, sizeof(rand));
}
- flags = get_adv_discov_flags(hdev);
+ flags = mgmt_get_adv_discov_flags(hdev);
if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED))
flags |= LE_AD_NO_BREDR;
@@ -6953,10 +5811,10 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
{
struct mgmt_rp_read_adv_features *rp;
size_t rp_len;
- int err, i;
- bool instance;
+ int err;
struct adv_info *adv_instance;
u32 supported_flags;
+ u8 *instance;
BT_DBG("%s", hdev->name);
@@ -6966,12 +5824,7 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
hci_dev_lock(hdev);
- rp_len = sizeof(*rp);
-
- instance = hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE);
- if (instance)
- rp_len += hdev->adv_instance_cnt;
-
+ rp_len = sizeof(*rp) + hdev->adv_instance_cnt;
rp = kmalloc(rp_len, GFP_ATOMIC);
if (!rp) {
hci_dev_unlock(hdev);
@@ -6984,19 +5837,12 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
rp->max_adv_data_len = HCI_MAX_AD_LENGTH;
rp->max_scan_rsp_len = HCI_MAX_AD_LENGTH;
rp->max_instances = HCI_MAX_ADV_INSTANCES;
+ rp->num_instances = hdev->adv_instance_cnt;
- if (instance) {
- i = 0;
- list_for_each_entry(adv_instance, &hdev->adv_instances, list) {
- if (i >= hdev->adv_instance_cnt)
- break;
-
- rp->instance[i] = adv_instance->instance;
- i++;
- }
- rp->num_instances = hdev->adv_instance_cnt;
- } else {
- rp->num_instances = 0;
+ instance = rp->instance;
+ list_for_each_entry(adv_instance, &hdev->adv_instances, list) {
+ *instance = adv_instance->instance;
+ instance++;
}
hci_dev_unlock(hdev);
@@ -7016,17 +5862,19 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data,
int i, cur_len;
bool flags_managed = false;
bool tx_power_managed = false;
- u32 flags_params = MGMT_ADV_FLAG_DISCOV | MGMT_ADV_FLAG_LIMITED_DISCOV |
- MGMT_ADV_FLAG_MANAGED_FLAGS;
- if (is_adv_data && (adv_flags & flags_params)) {
- flags_managed = true;
- max_len -= 3;
- }
+ if (is_adv_data) {
+ if (adv_flags & (MGMT_ADV_FLAG_DISCOV |
+ MGMT_ADV_FLAG_LIMITED_DISCOV |
+ MGMT_ADV_FLAG_MANAGED_FLAGS)) {
+ flags_managed = true;
+ max_len -= 3;
+ }
- if (is_adv_data && (adv_flags & MGMT_ADV_FLAG_TX_POWER)) {
- tx_power_managed = true;
- max_len -= 3;
+ if (adv_flags & MGMT_ADV_FLAG_TX_POWER) {
+ tx_power_managed = true;
+ max_len -= 3;
+ }
}
if (len > max_len)
@@ -7067,9 +5915,6 @@ static void add_advertising_complete(struct hci_dev *hdev, u8 status,
cmd = pending_find(MGMT_OP_ADD_ADVERTISING, hdev);
- if (status)
- hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE);
-
list_for_each_entry_safe(adv_instance, n, &hdev->adv_instances, list) {
if (!adv_instance->pending)
continue;
@@ -7085,7 +5930,7 @@ static void add_advertising_complete(struct hci_dev *hdev, u8 status,
cancel_adv_timeout(hdev);
hci_remove_adv_instance(hdev, instance);
- advertising_removed(cmd ? cmd->sk : NULL, hdev, instance);
+ mgmt_advertising_removed(cmd ? cmd->sk : NULL, hdev, instance);
}
if (!cmd)
@@ -7107,31 +5952,6 @@ unlock:
hci_dev_unlock(hdev);
}
-void mgmt_adv_timeout_expired(struct hci_dev *hdev)
-{
- u8 instance;
- struct hci_request req;
-
- hdev->adv_instance_timeout = 0;
-
- instance = get_current_adv_instance(hdev);
- if (instance == 0x00)
- return;
-
- hci_dev_lock(hdev);
- hci_req_init(&req, hdev);
-
- clear_adv_instance(hdev, &req, instance, false);
-
- if (list_empty(&hdev->adv_instances))
- disable_advertising(&req);
-
- if (!skb_queue_empty(&req.cmd_q))
- hci_req_run(&req, NULL);
-
- hci_dev_unlock(hdev);
-}
-
static int add_advertising(struct sock *sk, struct hci_dev *hdev,
void *data, u16 data_len)
{
@@ -7155,6 +5975,10 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
status);
+ if (cp->instance < 1 || cp->instance > HCI_MAX_ADV_INSTANCES)
+ return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
+ MGMT_STATUS_INVALID_PARAMS);
+
flags = __le32_to_cpu(cp->flags);
timeout = __le16_to_cpu(cp->timeout);
duration = __le16_to_cpu(cp->duration);
@@ -7206,9 +6030,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
* actually added.
*/
if (hdev->adv_instance_cnt > prev_instance_cnt)
- advertising_added(sk, hdev, cp->instance);
-
- hci_dev_set_flag(hdev, HCI_ADVERTISING_INSTANCE);
+ mgmt_advertising_added(sk, hdev, cp->instance);
if (hdev->cur_adv_instance == cp->instance) {
/* If the currently advertised instance is being changed then
@@ -7253,7 +6075,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
hci_req_init(&req, hdev);
- err = schedule_adv_instance(&req, schedule_instance, true);
+ err = __hci_req_schedule_adv_instance(&req, schedule_instance, true);
if (!err)
err = hci_req_run(&req, add_advertising_complete);
@@ -7325,7 +6147,7 @@ static int remove_advertising(struct sock *sk, struct hci_dev *hdev,
goto unlock;
}
- if (!hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE)) {
+ if (list_empty(&hdev->adv_instances)) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_ADVERTISING,
MGMT_STATUS_INVALID_PARAMS);
goto unlock;
@@ -7333,10 +6155,10 @@ static int remove_advertising(struct sock *sk, struct hci_dev *hdev,
hci_req_init(&req, hdev);
- clear_adv_instance(hdev, &req, cp->instance, true);
+ hci_req_clear_adv_instance(hdev, &req, cp->instance, true);
if (list_empty(&hdev->adv_instances))
- disable_advertising(&req);
+ __hci_req_disable_advertising(&req);
/* If no HCI commands have been collected so far or the HCI_ADVERTISING
* flag is set or the device isn't powered then we have no HCI
@@ -7369,6 +6191,62 @@ unlock:
return err;
}
+static u8 tlv_data_max_len(u32 adv_flags, bool is_adv_data)
+{
+ u8 max_len = HCI_MAX_AD_LENGTH;
+
+ if (is_adv_data) {
+ if (adv_flags & (MGMT_ADV_FLAG_DISCOV |
+ MGMT_ADV_FLAG_LIMITED_DISCOV |
+ MGMT_ADV_FLAG_MANAGED_FLAGS))
+ max_len -= 3;
+
+ if (adv_flags & MGMT_ADV_FLAG_TX_POWER)
+ max_len -= 3;
+ }
+
+ return max_len;
+}
+
+static int get_adv_size_info(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 data_len)
+{
+ struct mgmt_cp_get_adv_size_info *cp = data;
+ struct mgmt_rp_get_adv_size_info rp;
+ u32 flags, supported_flags;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!lmp_le_capable(hdev))
+ return mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO,
+ MGMT_STATUS_REJECTED);
+
+ if (cp->instance < 1 || cp->instance > HCI_MAX_ADV_INSTANCES)
+ return mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ flags = __le32_to_cpu(cp->flags);
+
+ /* The current implementation only supports a subset of the specified
+ * flags.
+ */
+ supported_flags = get_supported_adv_flags(hdev);
+ if (flags & ~supported_flags)
+ return mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ rp.instance = cp->instance;
+ rp.flags = cp->flags;
+ rp.max_adv_data_len = tlv_data_max_len(flags, true);
+ rp.max_scan_rsp_len = tlv_data_max_len(flags, false);
+
+ err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO,
+ MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
+
+ return err;
+}
+
static const struct hci_mgmt_handler mgmt_handlers[] = {
{ NULL }, /* 0x0000 (no command) */
{ read_version, MGMT_READ_VERSION_SIZE,
@@ -7456,6 +6334,8 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
{ add_advertising, MGMT_ADD_ADVERTISING_SIZE,
HCI_MGMT_VAR_LEN },
{ remove_advertising, MGMT_REMOVE_ADVERTISING_SIZE },
+ { get_adv_size_info, MGMT_GET_ADV_SIZE_INFO_SIZE },
+ { start_limited_discovery, MGMT_START_DISCOVERY_SIZE },
};
void mgmt_index_added(struct hci_dev *hdev)
@@ -7526,9 +6406,8 @@ void mgmt_index_removed(struct hci_dev *hdev)
}
/* This function requires the caller holds hdev->lock */
-static void restart_le_actions(struct hci_request *req)
+static void restart_le_actions(struct hci_dev *hdev)
{
- struct hci_dev *hdev = req->hdev;
struct hci_conn_params *p;
list_for_each_entry(p, &hdev->le_conn_params, list) {
@@ -7549,141 +6428,35 @@ static void restart_le_actions(struct hci_request *req)
break;
}
}
-
- __hci_update_background_scan(req);
}
-static void powered_complete(struct hci_dev *hdev, u8 status, u16 opcode)
+void mgmt_power_on(struct hci_dev *hdev, int err)
{
struct cmd_lookup match = { NULL, hdev };
- BT_DBG("status 0x%02x", status);
-
- if (!status) {
- /* Register the available SMP channels (BR/EDR and LE) only
- * when successfully powering on the controller. This late
- * registration is required so that LE SMP can clearly
- * decide if the public address or static address is used.
- */
- smp_register(hdev);
- }
+ BT_DBG("err %d", err);
hci_dev_lock(hdev);
+ if (!err) {
+ restart_le_actions(hdev);
+ hci_update_background_scan(hdev);
+ }
+
mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
new_settings(hdev, match.sk);
- hci_dev_unlock(hdev);
-
if (match.sk)
sock_put(match.sk);
-}
-
-static int powered_update_hci(struct hci_dev *hdev)
-{
- struct hci_request req;
- struct adv_info *adv_instance;
- u8 link_sec;
-
- hci_req_init(&req, hdev);
-
- if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED) &&
- !lmp_host_ssp_capable(hdev)) {
- u8 mode = 0x01;
-
- hci_req_add(&req, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode);
-
- if (bredr_sc_enabled(hdev) && !lmp_host_sc_capable(hdev)) {
- u8 support = 0x01;
-
- hci_req_add(&req, HCI_OP_WRITE_SC_SUPPORT,
- sizeof(support), &support);
- }
- }
-
- if (hci_dev_test_flag(hdev, HCI_LE_ENABLED) &&
- lmp_bredr_capable(hdev)) {
- struct hci_cp_write_le_host_supported cp;
-
- cp.le = 0x01;
- cp.simul = 0x00;
-
- /* Check first if we already have the right
- * host state (host features set)
- */
- if (cp.le != lmp_host_le_capable(hdev) ||
- cp.simul != lmp_host_le_br_capable(hdev))
- hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED,
- sizeof(cp), &cp);
- }
-
- if (lmp_le_capable(hdev)) {
- /* Make sure the controller has a good default for
- * advertising data. This also applies to the case
- * where BR/EDR was toggled during the AUTO_OFF phase.
- */
- if (hci_dev_test_flag(hdev, HCI_LE_ENABLED) &&
- (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
- !hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))) {
- update_adv_data(&req);
- update_scan_rsp_data(&req);
- }
-
- if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
- hdev->cur_adv_instance == 0x00 &&
- !list_empty(&hdev->adv_instances)) {
- adv_instance = list_first_entry(&hdev->adv_instances,
- struct adv_info, list);
- hdev->cur_adv_instance = adv_instance->instance;
- }
-
- if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
- enable_advertising(&req);
- else if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
- hdev->cur_adv_instance)
- schedule_adv_instance(&req, hdev->cur_adv_instance,
- true);
- restart_le_actions(&req);
- }
-
- link_sec = hci_dev_test_flag(hdev, HCI_LINK_SECURITY);
- if (link_sec != test_bit(HCI_AUTH, &hdev->flags))
- hci_req_add(&req, HCI_OP_WRITE_AUTH_ENABLE,
- sizeof(link_sec), &link_sec);
-
- if (lmp_bredr_capable(hdev)) {
- if (hci_dev_test_flag(hdev, HCI_FAST_CONNECTABLE))
- write_fast_connectable(&req, true);
- else
- write_fast_connectable(&req, false);
- __hci_update_page_scan(&req);
- update_class(&req);
- update_name(&req);
- update_eir(&req);
- }
-
- return hci_req_run(&req, powered_complete);
+ hci_dev_unlock(hdev);
}
-int mgmt_powered(struct hci_dev *hdev, u8 powered)
+void __mgmt_power_off(struct hci_dev *hdev)
{
struct cmd_lookup match = { NULL, hdev };
u8 status, zero_cod[] = { 0, 0, 0 };
- int err;
-
- if (!hci_dev_test_flag(hdev, HCI_MGMT))
- return 0;
-
- if (powered) {
- if (powered_update_hci(hdev) == 0)
- return 0;
-
- mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp,
- &match);
- goto new_settings;
- }
mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
@@ -7705,13 +6478,10 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
mgmt_generic_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
zero_cod, sizeof(zero_cod), NULL);
-new_settings:
- err = new_settings(hdev, match.sk);
+ new_settings(hdev, match.sk);
if (match.sk)
sock_put(match.sk);
-
- return err;
}
void mgmt_set_powered_failed(struct hci_dev *hdev, int err)
@@ -7733,43 +6503,6 @@ void mgmt_set_powered_failed(struct hci_dev *hdev, int err)
mgmt_pending_remove(cmd);
}
-void mgmt_discoverable_timeout(struct hci_dev *hdev)
-{
- struct hci_request req;
-
- hci_dev_lock(hdev);
-
- /* When discoverable timeout triggers, then just make sure
- * the limited discoverable flag is cleared. Even in the case
- * of a timeout triggered from general discoverable, it is
- * safe to unconditionally clear the flag.
- */
- hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
- hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
-
- hci_req_init(&req, hdev);
- if (hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
- u8 scan = SCAN_PAGE;
- hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE,
- sizeof(scan), &scan);
- }
- update_class(&req);
-
- /* Advertising instances don't use the global discoverable setting, so
- * only update AD if advertising was enabled using Set Advertising.
- */
- if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
- update_adv_data(&req);
-
- hci_req_run(&req, NULL);
-
- hdev->discov_timeout = 0;
-
- new_settings(hdev, NULL);
-
- hci_dev_unlock(hdev);
-}
-
void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
bool persistent)
{
@@ -8312,7 +7045,7 @@ void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
if (hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS))
hci_req_add(&req, HCI_OP_WRITE_SSP_DEBUG_MODE,
sizeof(enable), &enable);
- update_eir(&req);
+ __hci_req_update_eir(&req);
} else {
clear_eir(&req);
}
@@ -8452,7 +7185,7 @@ static void restart_le_scan(struct hci_dev *hdev)
hdev->discovery.scan_duration))
return;
- queue_delayed_work(hdev->workqueue, &hdev->le_scan_restart,
+ queue_delayed_work(hdev->req_workqueue, &hdev->le_scan_restart,
DISCOV_LE_RESTART_DELAY);
}
@@ -8527,6 +7260,18 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
return;
}
+ if (hdev->discovery.limited) {
+ /* Check for limited discoverable bit */
+ if (dev_class) {
+ if (!(dev_class[1] & 0x20))
+ return;
+ } else {
+ u8 *flags = eir_get_data(eir, eir_len, EIR_FLAGS, NULL);
+ if (!flags || !(flags[0] & LE_AD_LIMITED))
+ return;
+ }
+ }
+
/* Make sure that the buffer is big enough. The 5 extra bytes
* are for the potential CoD field.
*/
@@ -8556,7 +7301,8 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
/* Copy EIR or advertising data into event */
memcpy(ev->eir, eir, eir_len);
- if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV))
+ if (dev_class && !eir_get_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
+ NULL))
eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
dev_class, 3);
@@ -8606,35 +7352,6 @@ void mgmt_discovering(struct hci_dev *hdev, u8 discovering)
mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL);
}
-static void adv_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode)
-{
- BT_DBG("%s status %u", hdev->name, status);
-}
-
-void mgmt_reenable_advertising(struct hci_dev *hdev)
-{
- struct hci_request req;
- u8 instance;
-
- if (!hci_dev_test_flag(hdev, HCI_ADVERTISING) &&
- !hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
- return;
-
- instance = get_current_adv_instance(hdev);
-
- hci_req_init(&req, hdev);
-
- if (instance) {
- schedule_adv_instance(&req, instance, true);
- } else {
- update_adv_data(&req);
- update_scan_rsp_data(&req);
- enable_advertising(&req);
- }
-
- hci_req_run(&req, adv_enable_complete);
-}
-
static struct hci_mgmt_chan chan = {
.channel = HCI_CHANNEL_CONTROL,
.handler_count = ARRAY_SIZE(mgmt_handlers),
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index 29709fbfd1f5..f7eb02f09b54 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -692,11 +692,9 @@ static struct rfcomm_session *rfcomm_session_del(struct rfcomm_session *s)
static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)
{
- struct rfcomm_session *s;
- struct list_head *p, *n;
+ struct rfcomm_session *s, *n;
struct l2cap_chan *chan;
- list_for_each_safe(p, n, &session_list) {
- s = list_entry(p, struct rfcomm_session, list);
+ list_for_each_entry_safe(s, n, &session_list, list) {
chan = l2cap_pi(s->sock->sk)->chan;
if ((!bacmp(src, BDADDR_ANY) || !bacmp(&chan->src, src)) &&
@@ -709,16 +707,14 @@ static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)
static struct rfcomm_session *rfcomm_session_close(struct rfcomm_session *s,
int err)
{
- struct rfcomm_dlc *d;
- struct list_head *p, *n;
+ struct rfcomm_dlc *d, *n;
s->state = BT_CLOSED;
BT_DBG("session %p state %ld err %d", s, s->state, err);
/* Close all dlcs */
- list_for_each_safe(p, n, &s->dlcs) {
- d = list_entry(p, struct rfcomm_dlc, list);
+ list_for_each_entry_safe(d, n, &s->dlcs, list) {
d->state = BT_CLOSED;
__rfcomm_dlc_close(d, err);
}
@@ -1771,13 +1767,11 @@ static struct rfcomm_session *rfcomm_recv_frame(struct rfcomm_session *s,
static void rfcomm_process_connect(struct rfcomm_session *s)
{
- struct rfcomm_dlc *d;
- struct list_head *p, *n;
+ struct rfcomm_dlc *d, *n;
BT_DBG("session %p state %ld", s, s->state);
- list_for_each_safe(p, n, &s->dlcs) {
- d = list_entry(p, struct rfcomm_dlc, list);
+ list_for_each_entry_safe(d, n, &s->dlcs, list) {
if (d->state == BT_CONFIG) {
d->mtu = s->mtu;
if (rfcomm_check_security(d)) {
@@ -1843,14 +1837,11 @@ static int rfcomm_process_tx(struct rfcomm_dlc *d)
static void rfcomm_process_dlcs(struct rfcomm_session *s)
{
- struct rfcomm_dlc *d;
- struct list_head *p, *n;
+ struct rfcomm_dlc *d, *n;
BT_DBG("session %p state %ld", s, s->state);
- list_for_each_safe(p, n, &s->dlcs) {
- d = list_entry(p, struct rfcomm_dlc, list);
-
+ list_for_each_entry_safe(d, n, &s->dlcs, list) {
if (test_bit(RFCOMM_TIMED_OUT, &d->flags)) {
__rfcomm_dlc_close(d, ETIMEDOUT);
continue;
@@ -1985,14 +1976,11 @@ static struct rfcomm_session *rfcomm_check_connection(struct rfcomm_session *s)
static void rfcomm_process_sessions(void)
{
- struct list_head *p, *n;
+ struct rfcomm_session *s, *n;
rfcomm_lock();
- list_for_each_safe(p, n, &session_list) {
- struct rfcomm_session *s;
- s = list_entry(p, struct rfcomm_session, list);
-
+ list_for_each_entry_safe(s, n, &session_list, list) {
if (test_and_clear_bit(RFCOMM_TIMED_OUT, &s->flags)) {
s->state = BT_DISCONN;
rfcomm_send_disc(s, 0);
@@ -2075,15 +2063,12 @@ failed:
static void rfcomm_kill_listener(void)
{
- struct rfcomm_session *s;
- struct list_head *p, *n;
+ struct rfcomm_session *s, *n;
BT_DBG("");
- list_for_each_safe(p, n, &session_list) {
- s = list_entry(p, struct rfcomm_session, list);
+ list_for_each_entry_safe(s, n, &session_list, list)
rfcomm_session_del(s);
- }
}
static int rfcomm_run(void *unused)
@@ -2113,8 +2098,7 @@ static int rfcomm_run(void *unused)
static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
{
struct rfcomm_session *s;
- struct rfcomm_dlc *d;
- struct list_head *p, *n;
+ struct rfcomm_dlc *d, *n;
BT_DBG("conn %p status 0x%02x encrypt 0x%02x", conn, status, encrypt);
@@ -2122,9 +2106,7 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
if (!s)
return;
- list_for_each_safe(p, n, &s->dlcs) {
- d = list_entry(p, struct rfcomm_dlc, list);
-
+ list_for_each_entry_safe(d, n, &s->dlcs, list) {
if (test_and_clear_bit(RFCOMM_SEC_PENDING, &d->flags)) {
rfcomm_dlc_clear_timer(d);
if (status || encrypt == 0x00) {
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index fe129663bd3f..f52bcbf2e58c 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -526,6 +526,9 @@ static int sco_sock_bind(struct socket *sock, struct sockaddr *addr,
if (!addr || addr->sa_family != AF_BLUETOOTH)
return -EINVAL;
+ if (addr_len < sizeof(struct sockaddr_sco))
+ return -EINVAL;
+
lock_sock(sk);
if (sk->sk_state != BT_OPEN) {
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index c91353841e40..ffed8a1d4f27 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -3027,8 +3027,13 @@ static void smp_ready_cb(struct l2cap_chan *chan)
BT_DBG("chan %p", chan);
+ /* No need to call l2cap_chan_hold() here since we already own
+ * the reference taken in smp_new_conn_cb(). This is just the
+ * first time that we tie it to a specific pointer. The code in
+ * l2cap_core.c ensures that there's no risk this function wont
+ * get called if smp_new_conn_cb was previously called.
+ */
conn->smp = chan;
- l2cap_chan_hold(chan);
if (hcon->type == ACL_LINK && test_bit(HCI_CONN_ENCRYPT, &hcon->flags))
bredr_pairing(chan);
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index a642bb829d09..82e3e9705017 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -135,6 +135,7 @@ static void fdb_del_external_learn(struct net_bridge_fdb_entry *f)
{
struct switchdev_obj_port_fdb fdb = {
.obj = {
+ .orig_dev = f->dst->dev,
.id = SWITCHDEV_OBJ_ID_PORT_FDB,
.flags = SWITCHDEV_F_DEFER,
},
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index ec02f5869a78..c367b3e1b5ac 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -493,7 +493,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
dev->priv_flags |= IFF_BRIDGE_PORT;
- err = netdev_master_upper_dev_link(dev, br->dev);
+ err = netdev_master_upper_dev_link(dev, br->dev, NULL, NULL);
if (err)
goto err5;
@@ -511,8 +511,11 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
if (br_fdb_insert(br, p, dev->dev_addr, 0))
netdev_err(dev, "failed insert local address bridge forwarding table\n");
- if (nbp_vlan_init(p))
+ err = nbp_vlan_init(p);
+ if (err) {
netdev_err(dev, "failed to initialize vlan filtering on this port\n");
+ goto err6;
+ }
spin_lock_bh(&br->lock);
changed_addr = br_stp_recalculate_bridge_id(br);
@@ -533,6 +536,12 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
return 0;
+err6:
+ list_del_rcu(&p->list);
+ br_fdb_delete_by_port(br, p, 0, 1);
+ nbp_update_port_count(br);
+ netdev_upper_dev_unlink(dev, br->dev);
+
err5:
dev->priv_flags &= ~IFF_BRIDGE_PORT;
netdev_rx_handler_unregister(dev);
diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c
index cd8deea2d074..30e105f57f0d 100644
--- a/net/bridge/br_mdb.c
+++ b/net/bridge/br_mdb.c
@@ -7,6 +7,7 @@
#include <linux/if_ether.h>
#include <net/ip.h>
#include <net/netlink.h>
+#include <net/switchdev.h>
#if IS_ENABLED(CONFIG_IPV6)
#include <net/ipv6.h>
#include <net/addrconf.h>
@@ -210,10 +211,32 @@ static inline size_t rtnl_mdb_nlmsg_size(void)
static void __br_mdb_notify(struct net_device *dev, struct br_mdb_entry *entry,
int type)
{
+ struct switchdev_obj_port_mdb mdb = {
+ .obj = {
+ .id = SWITCHDEV_OBJ_ID_PORT_MDB,
+ .flags = SWITCHDEV_F_DEFER,
+ },
+ .vid = entry->vid,
+ };
+ struct net_device *port_dev;
struct net *net = dev_net(dev);
struct sk_buff *skb;
int err = -ENOBUFS;
+ port_dev = __dev_get_by_index(net, entry->ifindex);
+ if (entry->addr.proto == htons(ETH_P_IP))
+ ip_eth_mc_map(entry->addr.u.ip4, mdb.addr);
+#if IS_ENABLED(CONFIG_IPV6)
+ else
+ ipv6_eth_mc_map(&entry->addr.u.ip6, mdb.addr);
+#endif
+
+ mdb.obj.orig_dev = port_dev;
+ if (port_dev && type == RTM_NEWMDB)
+ switchdev_port_obj_add(port_dev, &mdb.obj);
+ else if (port_dev && type == RTM_DELMDB)
+ switchdev_port_obj_del(port_dev, &mdb.obj);
+
skb = nlmsg_new(rtnl_mdb_nlmsg_size(), GFP_ATOMIC);
if (!skb)
goto errout;
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index 5f3f64553179..b3cca126b103 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -40,6 +40,7 @@ void br_log_state(const struct net_bridge_port *p)
void br_set_state(struct net_bridge_port *p, unsigned int state)
{
struct switchdev_attr attr = {
+ .orig_dev = p->dev,
.id = SWITCHDEV_ATTR_ID_PORT_STP_STATE,
.flags = SWITCHDEV_F_DEFER,
.u.stp_state = state,
@@ -570,6 +571,7 @@ int br_set_max_age(struct net_bridge *br, unsigned long val)
int br_set_ageing_time(struct net_bridge *br, u32 ageing_time)
{
struct switchdev_attr attr = {
+ .orig_dev = br->dev,
.id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME,
.flags = SWITCHDEV_F_SKIP_EOPNOTSUPP,
.u.ageing_time = ageing_time,
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
index 5396ff08af32..a31ac6ad76a2 100644
--- a/net/bridge/br_stp_if.c
+++ b/net/bridge/br_stp_if.c
@@ -37,9 +37,10 @@ static inline port_id br_make_port_id(__u8 priority, __u16 port_no)
void br_init_port(struct net_bridge_port *p)
{
struct switchdev_attr attr = {
+ .orig_dev = p->dev,
.id = SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME,
.flags = SWITCHDEV_F_SKIP_EOPNOTSUPP | SWITCHDEV_F_DEFER,
- .u.ageing_time = p->br->ageing_time,
+ .u.ageing_time = jiffies_to_clock_t(p->br->ageing_time),
};
int err;
@@ -142,7 +143,10 @@ static void br_stp_start(struct net_bridge *br)
char *envp[] = { NULL };
struct net_bridge_port *p;
- r = call_usermodehelper(BR_STP_PROG, argv, envp, UMH_WAIT_PROC);
+ if (net_eq(dev_net(br->dev), &init_net))
+ r = call_usermodehelper(BR_STP_PROG, argv, envp, UMH_WAIT_PROC);
+ else
+ r = -ENOENT;
spin_lock_bh(&br->lock);
diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
index 8365bd53c421..6b8091407ca3 100644
--- a/net/bridge/br_sysfs_br.c
+++ b/net/bridge/br_sysfs_br.c
@@ -22,7 +22,6 @@
#include "br_private.h"
-#define to_dev(obj) container_of(obj, struct device, kobj)
#define to_bridge(cd) ((struct net_bridge *)netdev_priv(to_net_dev(cd)))
/*
@@ -814,7 +813,7 @@ static ssize_t brforward_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
{
- struct device *dev = to_dev(kobj);
+ struct device *dev = kobj_to_dev(kobj);
struct net_bridge *br = to_bridge(dev);
int n;
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 1394da63614a..85e43af4af7a 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -73,6 +73,7 @@ static int __vlan_vid_add(struct net_device *dev, struct net_bridge *br,
u16 vid, u16 flags)
{
struct switchdev_obj_port_vlan v = {
+ .obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.flags = flags,
.vid_begin = vid,
@@ -120,6 +121,7 @@ static int __vlan_vid_del(struct net_device *dev, struct net_bridge *br,
u16 vid)
{
struct switchdev_obj_port_vlan v = {
+ .obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.vid_begin = vid,
.vid_end = vid,
@@ -624,9 +626,21 @@ void br_recalculate_fwd_mask(struct net_bridge *br)
int __br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
{
+ struct switchdev_attr attr = {
+ .orig_dev = br->dev,
+ .id = SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING,
+ .flags = SWITCHDEV_F_SKIP_EOPNOTSUPP,
+ .u.vlan_filtering = val,
+ };
+ int err;
+
if (br->vlan_enabled == val)
return 0;
+ err = switchdev_port_attr_set(br->dev, &attr);
+ if (err && err != -EOPNOTSUPP)
+ return err;
+
br->vlan_enabled = val;
br_manage_promisc(br);
recalculate_group_addr(br);
@@ -637,13 +651,15 @@ int __br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val)
{
+ int err;
+
if (!rtnl_trylock())
return restart_syscall();
- __br_vlan_filter_toggle(br, val);
+ err = __br_vlan_filter_toggle(br, val);
rtnl_unlock();
- return 0;
+ return err;
}
int __br_vlan_set_proto(struct net_bridge *br, __be16 proto)
@@ -891,6 +907,12 @@ err_rhtbl:
int nbp_vlan_init(struct net_bridge_port *p)
{
+ struct switchdev_attr attr = {
+ .orig_dev = p->br->dev,
+ .id = SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING,
+ .flags = SWITCHDEV_F_SKIP_EOPNOTSUPP,
+ .u.vlan_filtering = p->br->vlan_enabled,
+ };
struct net_bridge_vlan_group *vg;
int ret = -ENOMEM;
@@ -898,6 +920,10 @@ int nbp_vlan_init(struct net_bridge_port *p)
if (!vg)
goto out;
+ ret = switchdev_port_attr_set(p->dev, &attr);
+ if (ret && ret != -EOPNOTSUPP)
+ goto err_vlan_enabled;
+
ret = rhashtable_init(&vg->vlan_hash, &br_vlan_rht_params);
if (ret)
goto err_rhtbl;
@@ -917,6 +943,7 @@ err_vlan_add:
RCU_INIT_POINTER(p->vlgrp, NULL);
synchronize_rcu();
rhashtable_destroy(&vg->vlan_hash);
+err_vlan_enabled:
err_rhtbl:
kfree(vg);
diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c
index 17fd5f2cb4b8..98de6e7fd86d 100644
--- a/net/bridge/netfilter/ebt_ip6.c
+++ b/net/bridge/netfilter/ebt_ip6.c
@@ -65,8 +65,8 @@ ebt_ip6_mt(const struct sk_buff *skb, struct xt_action_param *par)
return false;
if (FWINV(info->protocol != nexthdr, EBT_IP6_PROTO))
return false;
- if (!(info->bitmask & ( EBT_IP6_DPORT |
- EBT_IP6_SPORT | EBT_IP6_ICMP6)))
+ if (!(info->bitmask & (EBT_IP6_DPORT |
+ EBT_IP6_SPORT | EBT_IP6_ICMP6)))
return true;
/* min icmpv6 headersize is 4, so sizeof(_pkthdr) is ok. */
diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c
index 0ad639a96142..152300d164ac 100644
--- a/net/bridge/netfilter/ebt_log.c
+++ b/net/bridge/netfilter/ebt_log.c
@@ -36,14 +36,12 @@ static int ebt_log_tg_check(const struct xt_tgchk_param *par)
return 0;
}
-struct tcpudphdr
-{
+struct tcpudphdr {
__be16 src;
__be16 dst;
};
-struct arppayload
-{
+struct arppayload {
unsigned char mac_src[ETH_ALEN];
unsigned char ip_src[4];
unsigned char mac_dst[ETH_ALEN];
@@ -152,7 +150,8 @@ ebt_log_packet(struct net *net, u_int8_t pf, unsigned int hooknum,
ntohs(ah->ar_op));
/* If it's for Ethernet and the lengths are OK,
- * then log the ARP payload */
+ * then log the ARP payload
+ */
if (ah->ar_hrd == htons(1) &&
ah->ar_hln == ETH_ALEN &&
ah->ar_pln == sizeof(__be32)) {
diff --git a/net/bridge/netfilter/ebt_stp.c b/net/bridge/netfilter/ebt_stp.c
index 0c40570069ba..6b731e12ecfa 100644
--- a/net/bridge/netfilter/ebt_stp.c
+++ b/net/bridge/netfilter/ebt_stp.c
@@ -41,7 +41,7 @@ struct stp_config_pdu {
#define NR32(p) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3])
static bool ebt_filter_config(const struct ebt_stp_info *info,
- const struct stp_config_pdu *stpc)
+ const struct stp_config_pdu *stpc)
{
const struct ebt_stp_config_info *c;
uint16_t v16;
diff --git a/net/bridge/netfilter/ebt_vlan.c b/net/bridge/netfilter/ebt_vlan.c
index 618568888128..98c221dbf059 100644
--- a/net/bridge/netfilter/ebt_vlan.c
+++ b/net/bridge/netfilter/ebt_vlan.c
@@ -66,7 +66,8 @@ ebt_vlan_mt(const struct sk_buff *skb, struct xt_action_param *par)
* - Canonical Format Indicator (CFI). The Canonical Format Indicator
* (CFI) is a single bit flag value. Currently ignored.
* - VLAN Identifier (VID). The VID is encoded as
- * an unsigned binary number. */
+ * an unsigned binary number.
+ */
id = TCI & VLAN_VID_MASK;
prio = (TCI >> 13) & 0x7;
@@ -98,7 +99,8 @@ static int ebt_vlan_mt_check(const struct xt_mtchk_param *par)
}
/* Check for bitmask range
- * True if even one bit is out of mask */
+ * True if even one bit is out of mask
+ */
if (info->bitmask & ~EBT_VLAN_MASK) {
pr_debug("bitmask %2X is out of mask (%2X)\n",
info->bitmask, EBT_VLAN_MASK);
@@ -117,7 +119,8 @@ static int ebt_vlan_mt_check(const struct xt_mtchk_param *par)
* 0 - The null VLAN ID.
* 1 - The default Port VID (PVID)
* 0x0FFF - Reserved for implementation use.
- * if_vlan.h: VLAN_N_VID 4096. */
+ * if_vlan.h: VLAN_N_VID 4096.
+ */
if (GET_BITMASK(EBT_VLAN_ID)) {
if (!!info->id) { /* if id!=0 => check vid range */
if (info->id > VLAN_N_VID) {
@@ -128,7 +131,8 @@ static int ebt_vlan_mt_check(const struct xt_mtchk_param *par)
/* Note: This is valid VLAN-tagged frame point.
* Any value of user_priority are acceptable,
* but should be ignored according to 802.1Q Std.
- * So we just drop the prio flag. */
+ * So we just drop the prio flag.
+ */
info->bitmask &= ~EBT_VLAN_PRIO;
}
/* Else, id=0 (null VLAN ID) => user_priority range (any?) */
@@ -143,7 +147,8 @@ static int ebt_vlan_mt_check(const struct xt_mtchk_param *par)
}
/* Check for encapsulated proto range - it is possible to be
* any value for u_short range.
- * if_ether.h: ETH_ZLEN 60 - Min. octets in frame sans FCS */
+ * if_ether.h: ETH_ZLEN 60 - Min. octets in frame sans FCS
+ */
if (GET_BITMASK(EBT_VLAN_ENCAP)) {
if ((unsigned short) ntohs(info->encap) < ETH_ZLEN) {
pr_debug("encap frame length %d is less than "
diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c
index 32eccd101f26..593a1bdc079e 100644
--- a/net/bridge/netfilter/ebtable_filter.c
+++ b/net/bridge/netfilter/ebtable_filter.c
@@ -12,7 +12,7 @@
#include <linux/module.h>
#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
- (1 << NF_BR_LOCAL_OUT))
+ (1 << NF_BR_LOCAL_OUT))
static struct ebt_entries initial_chains[] = {
{
diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c
index ec55358f00c8..eb33919821ee 100644
--- a/net/bridge/netfilter/ebtable_nat.c
+++ b/net/bridge/netfilter/ebtable_nat.c
@@ -12,7 +12,7 @@
#include <linux/module.h>
#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
- (1 << NF_BR_POST_ROUTING))
+ (1 << NF_BR_POST_ROUTING))
static struct ebt_entries initial_chains[] = {
{
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index f46ca417bf2d..67b2e27999aa 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -35,8 +35,7 @@
"report to author: "format, ## args)
/* #define BUGPRINT(format, args...) */
-/*
- * Each cpu has its own set of counters, so there is no need for write_lock in
+/* Each cpu has its own set of counters, so there is no need for write_lock in
* the softirq
* For reading or updating the counters, the user context needs to
* get a write_lock
@@ -46,7 +45,7 @@
#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
#define COUNTER_OFFSET(n) (SMP_ALIGN(n * sizeof(struct ebt_counter)))
#define COUNTER_BASE(c, n, cpu) ((struct ebt_counter *)(((char *)c) + \
- COUNTER_OFFSET(n) * cpu))
+ COUNTER_OFFSET(n) * cpu))
@@ -126,7 +125,7 @@ ebt_dev_check(const char *entry, const struct net_device *device)
/* process standard matches */
static inline int
ebt_basic_match(const struct ebt_entry *e, const struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out)
+ const struct net_device *in, const struct net_device *out)
{
const struct ethhdr *h = eth_hdr(skb);
const struct net_bridge_port *p;
@@ -162,7 +161,7 @@ ebt_basic_match(const struct ebt_entry *e, const struct sk_buff *skb,
for (i = 0; i < 6; i++)
verdict |= (h->h_source[i] ^ e->sourcemac[i]) &
e->sourcemsk[i];
- if (FWINV2(verdict != 0, EBT_ISOURCE) )
+ if (FWINV2(verdict != 0, EBT_ISOURCE))
return 1;
}
if (e->bitmask & EBT_DESTMAC) {
@@ -170,7 +169,7 @@ ebt_basic_match(const struct ebt_entry *e, const struct sk_buff *skb,
for (i = 0; i < 6; i++)
verdict |= (h->h_dest[i] ^ e->destmac[i]) &
e->destmsk[i];
- if (FWINV2(verdict != 0, EBT_IDEST) )
+ if (FWINV2(verdict != 0, EBT_IDEST))
return 1;
}
return 0;
@@ -237,7 +236,8 @@ unsigned int ebt_do_table(struct sk_buff *skb,
(*(counter_base + i)).bcnt += skb->len;
/* these should only watch: not modify, nor tell us
- what to do with the packet */
+ * what to do with the packet
+ */
EBT_WATCHER_ITERATE(point, ebt_do_watcher, skb, &acpar);
t = (struct ebt_entry_target *)
@@ -323,7 +323,7 @@ letscontinue:
/* If it succeeds, returns element and locks mutex */
static inline void *
find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
- struct mutex *mutex)
+ struct mutex *mutex)
{
struct {
struct list_head list;
@@ -342,7 +342,7 @@ find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
static void *
find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
- int *error, struct mutex *mutex)
+ int *error, struct mutex *mutex)
{
return try_then_request_module(
find_inlist_lock_noload(head, name, error, mutex),
@@ -451,7 +451,8 @@ static int ebt_verify_pointers(const struct ebt_replace *repl,
if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
if (e->bitmask != 0) {
/* we make userspace set this right,
- so there is no misunderstanding */
+ * so there is no misunderstanding
+ */
BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
"in distinguisher\n");
return -EINVAL;
@@ -487,15 +488,14 @@ static int ebt_verify_pointers(const struct ebt_replace *repl,
return 0;
}
-/*
- * this one is very careful, as it is the first function
+/* this one is very careful, as it is the first function
* to parse the userspace data
*/
static inline int
ebt_check_entry_size_and_hooks(const struct ebt_entry *e,
- const struct ebt_table_info *newinfo,
- unsigned int *n, unsigned int *cnt,
- unsigned int *totalcnt, unsigned int *udc_cnt)
+ const struct ebt_table_info *newinfo,
+ unsigned int *n, unsigned int *cnt,
+ unsigned int *totalcnt, unsigned int *udc_cnt)
{
int i;
@@ -504,10 +504,12 @@ ebt_check_entry_size_and_hooks(const struct ebt_entry *e,
break;
}
/* beginning of a new chain
- if i == NF_BR_NUMHOOKS it must be a user defined chain */
+ * if i == NF_BR_NUMHOOKS it must be a user defined chain
+ */
if (i != NF_BR_NUMHOOKS || !e->bitmask) {
/* this checks if the previous chain has as many entries
- as it said it has */
+ * as it said it has
+ */
if (*n != *cnt) {
BUGPRINT("nentries does not equal the nr of entries "
"in the chain\n");
@@ -549,20 +551,18 @@ ebt_check_entry_size_and_hooks(const struct ebt_entry *e,
return 0;
}
-struct ebt_cl_stack
-{
+struct ebt_cl_stack {
struct ebt_chainstack cs;
int from;
unsigned int hookmask;
};
-/*
- * we need these positions to check that the jumps to a different part of the
+/* We need these positions to check that the jumps to a different part of the
* entries is a jump to the beginning of a new chain.
*/
static inline int
ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
- unsigned int *n, struct ebt_cl_stack *udc)
+ unsigned int *n, struct ebt_cl_stack *udc)
{
int i;
@@ -649,9 +649,9 @@ ebt_cleanup_entry(struct ebt_entry *e, struct net *net, unsigned int *cnt)
static inline int
ebt_check_entry(struct ebt_entry *e, struct net *net,
- const struct ebt_table_info *newinfo,
- const char *name, unsigned int *cnt,
- struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
+ const struct ebt_table_info *newinfo,
+ const char *name, unsigned int *cnt,
+ struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
{
struct ebt_entry_target *t;
struct xt_target *target;
@@ -673,7 +673,7 @@ ebt_check_entry(struct ebt_entry *e, struct net *net,
BUGPRINT("Unknown flag for inv bitmask\n");
return -EINVAL;
}
- if ( (e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3) ) {
+ if ((e->bitmask & EBT_NOPROTO) && (e->bitmask & EBT_802_3)) {
BUGPRINT("NOPROTO & 802_3 not allowed\n");
return -EINVAL;
}
@@ -687,7 +687,8 @@ ebt_check_entry(struct ebt_entry *e, struct net *net,
break;
}
/* (1 << NF_BR_NUMHOOKS) tells the check functions the rule is on
- a base chain */
+ * a base chain
+ */
if (i < NF_BR_NUMHOOKS)
hookmask = (1 << hook) | (1 << NF_BR_NUMHOOKS);
else {
@@ -758,13 +759,12 @@ cleanup_matches:
return ret;
}
-/*
- * checks for loops and sets the hook mask for udc
+/* checks for loops and sets the hook mask for udc
* the hook mask for udc tells us from which base chains the udc can be
* accessed. This mask is a parameter to the check() functions of the extensions
*/
static int check_chainloops(const struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
- unsigned int udc_cnt, unsigned int hooknr, char *base)
+ unsigned int udc_cnt, unsigned int hooknr, char *base)
{
int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
const struct ebt_entry *e = (struct ebt_entry *)chain->data;
@@ -853,7 +853,8 @@ static int translate_table(struct net *net, const char *name,
return -EINVAL;
}
/* make sure chains are ordered after each other in same order
- as their corresponding hooks */
+ * as their corresponding hooks
+ */
for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
if (!newinfo->hook_entry[j])
continue;
@@ -868,7 +869,8 @@ static int translate_table(struct net *net, const char *name,
i = 0; /* holds the expected nr. of entries for the chain */
j = 0; /* holds the up to now counted entries for the chain */
k = 0; /* holds the total nr. of entries, should equal
- newinfo->nentries afterwards */
+ * newinfo->nentries afterwards
+ */
udc_cnt = 0; /* will hold the nr. of user defined chains (udc) */
ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
ebt_check_entry_size_and_hooks, newinfo,
@@ -888,10 +890,12 @@ static int translate_table(struct net *net, const char *name,
}
/* get the location of the udc, put them in an array
- while we're at it, allocate the chainstack */
+ * while we're at it, allocate the chainstack
+ */
if (udc_cnt) {
/* this will get free'd in do_replace()/ebt_register_table()
- if an error occurs */
+ * if an error occurs
+ */
newinfo->chainstack =
vmalloc(nr_cpu_ids * sizeof(*(newinfo->chainstack)));
if (!newinfo->chainstack)
@@ -932,14 +936,15 @@ static int translate_table(struct net *net, const char *name,
}
/* we now know the following (along with E=mc²):
- - the nr of entries in each chain is right
- - the size of the allocated space is right
- - all valid hooks have a corresponding chain
- - there are no loops
- - wrong data can still be on the level of a single entry
- - could be there are jumps to places that are not the
- beginning of a chain. This can only occur in chains that
- are not accessible from any base chains, so we don't care. */
+ * - the nr of entries in each chain is right
+ * - the size of the allocated space is right
+ * - all valid hooks have a corresponding chain
+ * - there are no loops
+ * - wrong data can still be on the level of a single entry
+ * - could be there are jumps to places that are not the
+ * beginning of a chain. This can only occur in chains that
+ * are not accessible from any base chains, so we don't care.
+ */
/* used to know what we need to clean up if something goes wrong */
i = 0;
@@ -955,7 +960,7 @@ static int translate_table(struct net *net, const char *name,
/* called under write_lock */
static void get_counters(const struct ebt_counter *oldcounters,
- struct ebt_counter *counters, unsigned int nentries)
+ struct ebt_counter *counters, unsigned int nentries)
{
int i, cpu;
struct ebt_counter *counter_base;
@@ -986,7 +991,8 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl,
struct ebt_table *t;
/* the user wants counters back
- the check on the size is done later, when we have the lock */
+ * the check on the size is done later, when we have the lock
+ */
if (repl->num_counters) {
unsigned long size = repl->num_counters * sizeof(*counterstmp);
counterstmp = vmalloc(size);
@@ -1038,9 +1044,10 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl,
write_unlock_bh(&t->lock);
mutex_unlock(&ebt_mutex);
/* so, a user can change the chains while having messed up her counter
- allocation. Only reason why this is done is because this way the lock
- is held only once, while this doesn't bring the kernel into a
- dangerous state. */
+ * allocation. Only reason why this is done is because this way the lock
+ * is held only once, while this doesn't bring the kernel into a
+ * dangerous state.
+ */
if (repl->num_counters &&
copy_to_user(repl->counters, counterstmp,
repl->num_counters * sizeof(struct ebt_counter))) {
@@ -1342,13 +1349,14 @@ static int update_counters(struct net *net, const void __user *user,
}
static inline int ebt_make_matchname(const struct ebt_entry_match *m,
- const char *base, char __user *ubase)
+ const char *base, char __user *ubase)
{
char __user *hlp = ubase + ((char *)m - base);
char name[EBT_FUNCTION_MAXNAMELEN] = {};
/* ebtables expects 32 bytes long names but xt_match names are 29 bytes
- long. Copy 29 bytes and fill remaining bytes with zeroes. */
+ * long. Copy 29 bytes and fill remaining bytes with zeroes.
+ */
strlcpy(name, m->u.match->name, sizeof(name));
if (copy_to_user(hlp, name, EBT_FUNCTION_MAXNAMELEN))
return -EFAULT;
@@ -1356,19 +1364,19 @@ static inline int ebt_make_matchname(const struct ebt_entry_match *m,
}
static inline int ebt_make_watchername(const struct ebt_entry_watcher *w,
- const char *base, char __user *ubase)
+ const char *base, char __user *ubase)
{
char __user *hlp = ubase + ((char *)w - base);
char name[EBT_FUNCTION_MAXNAMELEN] = {};
strlcpy(name, w->u.watcher->name, sizeof(name));
- if (copy_to_user(hlp , name, EBT_FUNCTION_MAXNAMELEN))
+ if (copy_to_user(hlp, name, EBT_FUNCTION_MAXNAMELEN))
return -EFAULT;
return 0;
}
-static inline int
-ebt_make_names(struct ebt_entry *e, const char *base, char __user *ubase)
+static inline int ebt_make_names(struct ebt_entry *e, const char *base,
+ char __user *ubase)
{
int ret;
char __user *hlp;
@@ -1394,9 +1402,9 @@ ebt_make_names(struct ebt_entry *e, const char *base, char __user *ubase)
}
static int copy_counters_to_user(struct ebt_table *t,
- const struct ebt_counter *oldcounters,
- void __user *user, unsigned int num_counters,
- unsigned int nentries)
+ const struct ebt_counter *oldcounters,
+ void __user *user, unsigned int num_counters,
+ unsigned int nentries)
{
struct ebt_counter *counterstmp;
int ret = 0;
@@ -1427,7 +1435,7 @@ static int copy_counters_to_user(struct ebt_table *t,
/* called with ebt_mutex locked */
static int copy_everything_to_user(struct ebt_table *t, void __user *user,
- const int *len, int cmd)
+ const int *len, int cmd)
{
struct ebt_replace tmp;
const struct ebt_counter *oldcounters;
@@ -1595,8 +1603,7 @@ static int ebt_compat_entry_padsize(void)
static int ebt_compat_match_offset(const struct xt_match *match,
unsigned int userlen)
{
- /*
- * ebt_among needs special handling. The kernel .matchsize is
+ /* ebt_among needs special handling. The kernel .matchsize is
* set to -1 at registration time; at runtime an EBT_ALIGN()ed
* value is expected.
* Example: userspace sends 4500, ebt_among.c wants 4504.
@@ -1966,8 +1973,7 @@ static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt,
return off + match_size;
}
-/*
- * return size of all matches, watchers or target, including necessary
+/* return size of all matches, watchers or target, including necessary
* alignment and padding.
*/
static int ebt_size_mwt(struct compat_ebt_entry_mwt *match32,
@@ -2070,8 +2076,7 @@ static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base,
if (ret < 0)
return ret;
buf_start = (char *) entry;
- /*
- * 0: matches offset, always follows ebt_entry.
+ /* 0: matches offset, always follows ebt_entry.
* 1: watchers offset, from ebt_entry structure
* 2: target offset, from ebt_entry structure
* 3: next ebt_entry offset, from ebt_entry structure
@@ -2115,8 +2120,7 @@ static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base,
return 0;
}
-/*
- * repl->entries_size is the size of the ebt_entry blob in userspace.
+/* repl->entries_size is the size of the ebt_entry blob in userspace.
* It might need more memory when copied to a 64 bit kernel in case
* userspace is 32-bit. So, first task: find out how much memory is needed.
*
@@ -2305,7 +2309,7 @@ static int compat_do_ebt_set_ctl(struct sock *sk,
break;
default:
ret = -EINVAL;
- }
+ }
return ret;
}
@@ -2360,8 +2364,7 @@ static int compat_do_ebt_get_ctl(struct sock *sk, int cmd,
break;
case EBT_SO_GET_ENTRIES:
case EBT_SO_GET_INIT_ENTRIES:
- /*
- * try real handler first in case of userland-side padding.
+ /* try real handler first in case of userland-side padding.
* in case we are dealing with an 'ordinary' 32 bit binary
* without 64bit compatibility padding, this will fail right
* after copy_from_user when the *len argument is validated.
diff --git a/net/bridge/netfilter/nf_tables_bridge.c b/net/bridge/netfilter/nf_tables_bridge.c
index 62f6b1b19589..7fcdd7261d88 100644
--- a/net/bridge/netfilter/nf_tables_bridge.c
+++ b/net/bridge/netfilter/nf_tables_bridge.c
@@ -141,7 +141,7 @@ err:
static void nf_tables_bridge_exit_net(struct net *net)
{
- nft_unregister_afinfo(net->nft.bridge);
+ nft_unregister_afinfo(net, net->nft.bridge);
kfree(net->nft.bridge);
}
diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c
index a21269b83f16..4b901d9f2e7c 100644
--- a/net/bridge/netfilter/nft_meta_bridge.c
+++ b/net/bridge/netfilter/nft_meta_bridge.c
@@ -84,6 +84,7 @@ static const struct nft_expr_ops nft_meta_bridge_set_ops = {
.size = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
.eval = nft_meta_set_eval,
.init = nft_meta_set_init,
+ .destroy = nft_meta_set_destroy,
.dump = nft_meta_set_dump,
};
diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c
index cc858919108e..aa209b1066c9 100644
--- a/net/caif/caif_socket.c
+++ b/net/caif/caif_socket.c
@@ -323,7 +323,7 @@ static long caif_stream_data_wait(struct sock *sk, long timeo)
!timeo)
break;
- set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+ sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);
release_sock(sk);
timeo = schedule_timeout(timeo);
lock_sock(sk);
@@ -331,7 +331,7 @@ static long caif_stream_data_wait(struct sock *sk, long timeo)
if (sock_flag(sk, SOCK_DEAD))
break;
- clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+ sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk);
}
finish_wait(sk_sleep(sk), &wait);
diff --git a/net/core/Makefile b/net/core/Makefile
index 086b01fbe1bd..0b835de04de3 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -9,7 +9,7 @@ obj-$(CONFIG_SYSCTL) += sysctl_net_core.o
obj-y += dev.o ethtool.o dev_addr_lists.o dst.o netevent.o \
neighbour.o rtnetlink.o utils.o link_watch.o filter.o \
- sock_diag.o dev_ioctl.o tso.o
+ sock_diag.o dev_ioctl.o tso.o sock_reuseport.o
obj-$(CONFIG_XFRM) += flow.o
obj-y += net-sysfs.o
diff --git a/net/core/datagram.c b/net/core/datagram.c
index 617088aee21d..fa9dc6450b08 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -83,8 +83,8 @@ static int receiver_wake_function(wait_queue_t *wait, unsigned int mode, int syn
/*
* Wait for the last received packet to be different from skb
*/
-static int wait_for_more_packets(struct sock *sk, int *err, long *timeo_p,
- const struct sk_buff *skb)
+int __skb_wait_for_more_packets(struct sock *sk, int *err, long *timeo_p,
+ const struct sk_buff *skb)
{
int error;
DEFINE_WAIT_FUNC(wait, receiver_wake_function);
@@ -130,6 +130,7 @@ out_noerr:
error = 1;
goto out;
}
+EXPORT_SYMBOL(__skb_wait_for_more_packets);
static struct sk_buff *skb_set_peeked(struct sk_buff *skb)
{
@@ -161,13 +162,15 @@ done:
}
/**
- * __skb_recv_datagram - Receive a datagram skbuff
+ * __skb_try_recv_datagram - Receive a datagram skbuff
* @sk: socket
* @flags: MSG_ flags
* @peeked: returns non-zero if this packet has been seen before
* @off: an offset in bytes to peek skb from. Returns an offset
* within an skb where data actually starts
* @err: error code returned
+ * @last: set to last peeked message to inform the wait function
+ * what to look for when peeking
*
* Get a datagram skbuff, understands the peeking, nonblocking wakeups
* and possible races. This replaces identical code in packet, raw and
@@ -175,9 +178,11 @@ done:
* the long standing peek and read race for datagram sockets. If you
* alter this routine remember it must be re-entrant.
*
- * This function will lock the socket if a skb is returned, so the caller
- * needs to unlock the socket in that case (usually by calling
- * skb_free_datagram)
+ * This function will lock the socket if a skb is returned, so
+ * the caller needs to unlock the socket in that case (usually by
+ * calling skb_free_datagram). Returns NULL with *err set to
+ * -EAGAIN if no data was available or to some other value if an
+ * error was detected.
*
* * It does not lock socket since today. This function is
* * free of race conditions. This measure should/can improve
@@ -191,13 +196,13 @@ done:
* quite explicitly by POSIX 1003.1g, don't change them without having
* the standard around please.
*/
-struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags,
- int *peeked, int *off, int *err)
+struct sk_buff *__skb_try_recv_datagram(struct sock *sk, unsigned int flags,
+ int *peeked, int *off, int *err,
+ struct sk_buff **last)
{
struct sk_buff_head *queue = &sk->sk_receive_queue;
- struct sk_buff *skb, *last;
+ struct sk_buff *skb;
unsigned long cpu_flags;
- long timeo;
/*
* Caller is allowed not to check sk->sk_err before skb_recv_datagram()
*/
@@ -206,8 +211,6 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags,
if (error)
goto no_packet;
- timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
-
do {
/* Again only user level code calls this function, so nothing
* interrupt level will suddenly eat the receive_queue.
@@ -217,10 +220,10 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags,
*/
int _off = *off;
- last = (struct sk_buff *)queue;
+ *last = (struct sk_buff *)queue;
spin_lock_irqsave(&queue->lock, cpu_flags);
skb_queue_walk(queue, skb) {
- last = skb;
+ *last = skb;
*peeked = skb->peeked;
if (flags & MSG_PEEK) {
if (_off >= skb->len && (skb->len || _off ||
@@ -231,8 +234,11 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags,
skb = skb_set_peeked(skb);
error = PTR_ERR(skb);
- if (IS_ERR(skb))
- goto unlock_err;
+ if (IS_ERR(skb)) {
+ spin_unlock_irqrestore(&queue->lock,
+ cpu_flags);
+ goto no_packet;
+ }
atomic_inc(&skb->users);
} else
@@ -242,25 +248,38 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags,
*off = _off;
return skb;
}
+
spin_unlock_irqrestore(&queue->lock, cpu_flags);
+ } while (sk_can_busy_loop(sk) &&
+ sk_busy_loop(sk, flags & MSG_DONTWAIT));
- if (sk_can_busy_loop(sk) &&
- sk_busy_loop(sk, flags & MSG_DONTWAIT))
- continue;
+ error = -EAGAIN;
- /* User doesn't want to wait */
- error = -EAGAIN;
- if (!timeo)
- goto no_packet;
+no_packet:
+ *err = error;
+ return NULL;
+}
+EXPORT_SYMBOL(__skb_try_recv_datagram);
- } while (!wait_for_more_packets(sk, err, &timeo, last));
+struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags,
+ int *peeked, int *off, int *err)
+{
+ struct sk_buff *skb, *last;
+ long timeo;
- return NULL;
+ timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
+
+ do {
+ skb = __skb_try_recv_datagram(sk, flags, peeked, off, err,
+ &last);
+ if (skb)
+ return skb;
+
+ if (*err != -EAGAIN)
+ break;
+ } while (timeo &&
+ !__skb_wait_for_more_packets(sk, err, &timeo, last));
-unlock_err:
- spin_unlock_irqrestore(&queue->lock, cpu_flags);
-no_packet:
- *err = error;
return NULL;
}
EXPORT_SYMBOL(__skb_recv_datagram);
@@ -785,7 +804,7 @@ unsigned int datagram_poll(struct file *file, struct socket *sock,
if (sock_writeable(sk))
mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
else
- set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+ sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
return mask;
}
diff --git a/net/core/dev.c b/net/core/dev.c
index ae00b894e675..0ca95d5d7af0 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -96,6 +96,7 @@
#include <linux/skbuff.h>
#include <net/net_namespace.h>
#include <net/sock.h>
+#include <net/busy_poll.h>
#include <linux/rtnetlink.h>
#include <linux/stat.h>
#include <net/dst.h>
@@ -137,6 +138,7 @@
#include <linux/errqueue.h>
#include <linux/hrtimer.h>
#include <linux/netfilter_ingress.h>
+#include <linux/sctp.h>
#include "net-sysfs.h"
@@ -182,8 +184,8 @@ EXPORT_SYMBOL(dev_base_lock);
/* protects napi_hash addition/deletion and napi_gen_id */
static DEFINE_SPINLOCK(napi_hash_lock);
-static unsigned int napi_gen_id;
-static DEFINE_HASHTABLE(napi_hash, 8);
+static unsigned int napi_gen_id = NR_CPUS;
+static DEFINE_READ_MOSTLY_HASHTABLE(napi_hash, 8);
static seqcount_t devnet_rename_seq;
@@ -1674,6 +1676,22 @@ void net_dec_ingress_queue(void)
EXPORT_SYMBOL_GPL(net_dec_ingress_queue);
#endif
+#ifdef CONFIG_NET_EGRESS
+static struct static_key egress_needed __read_mostly;
+
+void net_inc_egress_queue(void)
+{
+ static_key_slow_inc(&egress_needed);
+}
+EXPORT_SYMBOL_GPL(net_inc_egress_queue);
+
+void net_dec_egress_queue(void)
+{
+ static_key_slow_dec(&egress_needed);
+}
+EXPORT_SYMBOL_GPL(net_dec_egress_queue);
+#endif
+
static struct static_key netstamp_needed __read_mostly;
#ifdef HAVE_JUMP_LABEL
/* We are not allowed to call static_key_slow_dec() from irq context
@@ -2470,6 +2488,141 @@ out:
}
EXPORT_SYMBOL(skb_checksum_help);
+/* skb_csum_offload_check - Driver helper function to determine if a device
+ * with limited checksum offload capabilities is able to offload the checksum
+ * for a given packet.
+ *
+ * Arguments:
+ * skb - sk_buff for the packet in question
+ * spec - contains the description of what device can offload
+ * csum_encapped - returns true if the checksum being offloaded is
+ * encpasulated. That is it is checksum for the transport header
+ * in the inner headers.
+ * checksum_help - when set indicates that helper function should
+ * call skb_checksum_help if offload checks fail
+ *
+ * Returns:
+ * true: Packet has passed the checksum checks and should be offloadable to
+ * the device (a driver may still need to check for additional
+ * restrictions of its device)
+ * false: Checksum is not offloadable. If checksum_help was set then
+ * skb_checksum_help was called to resolve checksum for non-GSO
+ * packets and when IP protocol is not SCTP
+ */
+bool __skb_csum_offload_chk(struct sk_buff *skb,
+ const struct skb_csum_offl_spec *spec,
+ bool *csum_encapped,
+ bool csum_help)
+{
+ struct iphdr *iph;
+ struct ipv6hdr *ipv6;
+ void *nhdr;
+ int protocol;
+ u8 ip_proto;
+
+ if (skb->protocol == htons(ETH_P_8021Q) ||
+ skb->protocol == htons(ETH_P_8021AD)) {
+ if (!spec->vlan_okay)
+ goto need_help;
+ }
+
+ /* We check whether the checksum refers to a transport layer checksum in
+ * the outermost header or an encapsulated transport layer checksum that
+ * corresponds to the inner headers of the skb. If the checksum is for
+ * something else in the packet we need help.
+ */
+ if (skb_checksum_start_offset(skb) == skb_transport_offset(skb)) {
+ /* Non-encapsulated checksum */
+ protocol = eproto_to_ipproto(vlan_get_protocol(skb));
+ nhdr = skb_network_header(skb);
+ *csum_encapped = false;
+ if (spec->no_not_encapped)
+ goto need_help;
+ } else if (skb->encapsulation && spec->encap_okay &&
+ skb_checksum_start_offset(skb) ==
+ skb_inner_transport_offset(skb)) {
+ /* Encapsulated checksum */
+ *csum_encapped = true;
+ switch (skb->inner_protocol_type) {
+ case ENCAP_TYPE_ETHER:
+ protocol = eproto_to_ipproto(skb->inner_protocol);
+ break;
+ case ENCAP_TYPE_IPPROTO:
+ protocol = skb->inner_protocol;
+ break;
+ }
+ nhdr = skb_inner_network_header(skb);
+ } else {
+ goto need_help;
+ }
+
+ switch (protocol) {
+ case IPPROTO_IP:
+ if (!spec->ipv4_okay)
+ goto need_help;
+ iph = nhdr;
+ ip_proto = iph->protocol;
+ if (iph->ihl != 5 && !spec->ip_options_okay)
+ goto need_help;
+ break;
+ case IPPROTO_IPV6:
+ if (!spec->ipv6_okay)
+ goto need_help;
+ if (spec->no_encapped_ipv6 && *csum_encapped)
+ goto need_help;
+ ipv6 = nhdr;
+ nhdr += sizeof(*ipv6);
+ ip_proto = ipv6->nexthdr;
+ break;
+ default:
+ goto need_help;
+ }
+
+ip_proto_again:
+ switch (ip_proto) {
+ case IPPROTO_TCP:
+ if (!spec->tcp_okay ||
+ skb->csum_offset != offsetof(struct tcphdr, check))
+ goto need_help;
+ break;
+ case IPPROTO_UDP:
+ if (!spec->udp_okay ||
+ skb->csum_offset != offsetof(struct udphdr, check))
+ goto need_help;
+ break;
+ case IPPROTO_SCTP:
+ if (!spec->sctp_okay ||
+ skb->csum_offset != offsetof(struct sctphdr, checksum))
+ goto cant_help;
+ break;
+ case NEXTHDR_HOP:
+ case NEXTHDR_ROUTING:
+ case NEXTHDR_DEST: {
+ u8 *opthdr = nhdr;
+
+ if (protocol != IPPROTO_IPV6 || !spec->ext_hdrs_okay)
+ goto need_help;
+
+ ip_proto = opthdr[0];
+ nhdr += (opthdr[1] + 1) << 3;
+
+ goto ip_proto_again;
+ }
+ default:
+ goto need_help;
+ }
+
+ /* Passed the tests for offloading checksum */
+ return true;
+
+need_help:
+ if (csum_help && !skb_shinfo(skb)->gso_size)
+ skb_checksum_help(skb);
+cant_help:
+ return false;
+}
+EXPORT_SYMBOL(__skb_csum_offload_chk);
+
__be16 skb_network_protocol(struct sk_buff *skb, int *depth)
{
__be16 type = skb->protocol;
@@ -2644,7 +2797,7 @@ static netdev_features_t harmonize_features(struct sk_buff *skb,
if (skb->ip_summed != CHECKSUM_NONE &&
!can_checksum_protocol(features, type)) {
- features &= ~NETIF_F_ALL_CSUM;
+ features &= ~NETIF_F_CSUM_MASK;
} else if (illegal_highdma(skb->dev, skb)) {
features &= ~NETIF_F_SG;
}
@@ -2791,7 +2944,7 @@ static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device
else
skb_set_transport_header(skb,
skb_checksum_start_offset(skb));
- if (!(features & NETIF_F_ALL_CSUM) &&
+ if (!(features & NETIF_F_CSUM_MASK) &&
skb_checksum_help(skb))
goto out_kfree_skb;
}
@@ -2870,7 +3023,6 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q,
bool contended;
int rc;
- qdisc_pkt_len_init(skb);
qdisc_calculate_pkt_len(skb, q);
/*
* Heuristic to force contended enqueues to serialize on a
@@ -2928,7 +3080,8 @@ static void skb_update_prio(struct sk_buff *skb)
struct netprio_map *map = rcu_dereference_bh(skb->dev->priomap);
if (!skb->priority && skb->sk && map) {
- unsigned int prioidx = skb->sk->sk_cgrp_prioidx;
+ unsigned int prioidx =
+ sock_cgroup_prioidx(&skb->sk->sk_cgrp_data);
if (prioidx < map->priomap_len)
skb->priority = map->priomap[prioidx];
@@ -2962,6 +3115,49 @@ int dev_loopback_xmit(struct net *net, struct sock *sk, struct sk_buff *skb)
}
EXPORT_SYMBOL(dev_loopback_xmit);
+#ifdef CONFIG_NET_EGRESS
+static struct sk_buff *
+sch_handle_egress(struct sk_buff *skb, int *ret, struct net_device *dev)
+{
+ struct tcf_proto *cl = rcu_dereference_bh(dev->egress_cl_list);
+ struct tcf_result cl_res;
+
+ if (!cl)
+ return skb;
+
+ /* skb->tc_verd and qdisc_skb_cb(skb)->pkt_len were already set
+ * earlier by the caller.
+ */
+ qdisc_bstats_cpu_update(cl->q, skb);
+
+ switch (tc_classify(skb, cl, &cl_res, false)) {
+ case TC_ACT_OK:
+ case TC_ACT_RECLASSIFY:
+ skb->tc_index = TC_H_MIN(cl_res.classid);
+ break;
+ case TC_ACT_SHOT:
+ qdisc_qstats_cpu_drop(cl->q);
+ *ret = NET_XMIT_DROP;
+ goto drop;
+ case TC_ACT_STOLEN:
+ case TC_ACT_QUEUED:
+ *ret = NET_XMIT_SUCCESS;
+drop:
+ kfree_skb(skb);
+ return NULL;
+ case TC_ACT_REDIRECT:
+ /* No need to push/pop skb's mac_header here on egress! */
+ skb_do_redirect(skb);
+ *ret = NET_XMIT_SUCCESS;
+ return NULL;
+ default:
+ break;
+ }
+
+ return skb;
+}
+#endif /* CONFIG_NET_EGRESS */
+
static inline int get_xps_queue(struct net_device *dev, struct sk_buff *skb)
{
#ifdef CONFIG_XPS
@@ -3021,7 +3217,9 @@ struct netdev_queue *netdev_pick_tx(struct net_device *dev,
int queue_index = 0;
#ifdef CONFIG_XPS
- if (skb->sender_cpu == 0)
+ u32 sender_cpu = skb->sender_cpu - 1;
+
+ if (sender_cpu >= (u32)NR_CPUS)
skb->sender_cpu = raw_smp_processor_id() + 1;
#endif
@@ -3086,6 +3284,17 @@ static int __dev_queue_xmit(struct sk_buff *skb, void *accel_priv)
skb_update_prio(skb);
+ qdisc_pkt_len_init(skb);
+#ifdef CONFIG_NET_CLS_ACT
+ skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_EGRESS);
+# ifdef CONFIG_NET_EGRESS
+ if (static_key_false(&egress_needed)) {
+ skb = sch_handle_egress(skb, &rc, dev);
+ if (!skb)
+ goto out;
+ }
+# endif
+#endif
/* If device/qdisc don't need skb->dst, release it right now while
* its hot in this cpu cache.
*/
@@ -3107,9 +3316,6 @@ static int __dev_queue_xmit(struct sk_buff *skb, void *accel_priv)
txq = netdev_pick_tx(dev, skb, accel_priv);
q = rcu_dereference_bh(txq->qdisc);
-#ifdef CONFIG_NET_CLS_ACT
- skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_EGRESS);
-#endif
trace_net_dev_queue(skb);
if (q->enqueue) {
rc = __dev_xmit_skb(skb, q, dev, txq);
@@ -3666,9 +3872,9 @@ int (*br_fdb_test_addr_hook)(struct net_device *dev,
EXPORT_SYMBOL_GPL(br_fdb_test_addr_hook);
#endif
-static inline struct sk_buff *handle_ing(struct sk_buff *skb,
- struct packet_type **pt_prev,
- int *ret, struct net_device *orig_dev)
+static inline struct sk_buff *
+sch_handle_ingress(struct sk_buff *skb, struct packet_type **pt_prev, int *ret,
+ struct net_device *orig_dev)
{
#ifdef CONFIG_NET_CLS_ACT
struct tcf_proto *cl = rcu_dereference_bh(skb->dev->ingress_cl_list);
@@ -3862,7 +4068,7 @@ another_round:
skip_taps:
#ifdef CONFIG_NET_INGRESS
if (static_key_false(&ingress_needed)) {
- skb = handle_ing(skb, &pt_prev, &ret, orig_dev);
+ skb = sch_handle_ingress(skb, &pt_prev, &ret, orig_dev);
if (!skb)
goto out;
@@ -4353,6 +4559,7 @@ static gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb)
gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
{
+ skb_mark_napi_id(skb, napi);
trace_napi_gro_receive_entry(skb);
skb_gro_reset_offset(skb);
@@ -4386,7 +4593,10 @@ struct sk_buff *napi_get_frags(struct napi_struct *napi)
if (!skb) {
skb = napi_alloc_skb(napi, GRO_MAX_HEAD);
- napi->skb = skb;
+ if (skb) {
+ napi->skb = skb;
+ skb_mark_napi_id(skb, napi);
+ }
}
return skb;
}
@@ -4661,7 +4871,7 @@ void napi_complete_done(struct napi_struct *n, int work_done)
EXPORT_SYMBOL(napi_complete_done);
/* must be called under rcu_read_lock(), as we dont take a reference */
-struct napi_struct *napi_by_id(unsigned int napi_id)
+static struct napi_struct *napi_by_id(unsigned int napi_id)
{
unsigned int hash = napi_id % HASH_SIZE(napi_hash);
struct napi_struct *napi;
@@ -4672,43 +4882,101 @@ struct napi_struct *napi_by_id(unsigned int napi_id)
return NULL;
}
-EXPORT_SYMBOL_GPL(napi_by_id);
-void napi_hash_add(struct napi_struct *napi)
+#if defined(CONFIG_NET_RX_BUSY_POLL)
+#define BUSY_POLL_BUDGET 8
+bool sk_busy_loop(struct sock *sk, int nonblock)
{
- if (!test_and_set_bit(NAPI_STATE_HASHED, &napi->state)) {
+ unsigned long end_time = !nonblock ? sk_busy_loop_end_time(sk) : 0;
+ int (*busy_poll)(struct napi_struct *dev);
+ struct napi_struct *napi;
+ int rc = false;
- spin_lock(&napi_hash_lock);
+ rcu_read_lock();
- /* 0 is not a valid id, we also skip an id that is taken
- * we expect both events to be extremely rare
- */
- napi->napi_id = 0;
- while (!napi->napi_id) {
- napi->napi_id = ++napi_gen_id;
- if (napi_by_id(napi->napi_id))
- napi->napi_id = 0;
+ napi = napi_by_id(sk->sk_napi_id);
+ if (!napi)
+ goto out;
+
+ /* Note: ndo_busy_poll method is optional in linux-4.5 */
+ busy_poll = napi->dev->netdev_ops->ndo_busy_poll;
+
+ do {
+ rc = 0;
+ local_bh_disable();
+ if (busy_poll) {
+ rc = busy_poll(napi);
+ } else if (napi_schedule_prep(napi)) {
+ void *have = netpoll_poll_lock(napi);
+
+ if (test_bit(NAPI_STATE_SCHED, &napi->state)) {
+ rc = napi->poll(napi, BUSY_POLL_BUDGET);
+ trace_napi_poll(napi);
+ if (rc == BUSY_POLL_BUDGET) {
+ napi_complete_done(napi, rc);
+ napi_schedule(napi);
+ }
+ }
+ netpoll_poll_unlock(have);
}
+ if (rc > 0)
+ NET_ADD_STATS_BH(sock_net(sk),
+ LINUX_MIB_BUSYPOLLRXPACKETS, rc);
+ local_bh_enable();
- hlist_add_head_rcu(&napi->napi_hash_node,
- &napi_hash[napi->napi_id % HASH_SIZE(napi_hash)]);
+ if (rc == LL_FLUSH_FAILED)
+ break; /* permanent failure */
- spin_unlock(&napi_hash_lock);
- }
+ cpu_relax();
+ } while (!nonblock && skb_queue_empty(&sk->sk_receive_queue) &&
+ !need_resched() && !busy_loop_timeout(end_time));
+
+ rc = !skb_queue_empty(&sk->sk_receive_queue);
+out:
+ rcu_read_unlock();
+ return rc;
+}
+EXPORT_SYMBOL(sk_busy_loop);
+
+#endif /* CONFIG_NET_RX_BUSY_POLL */
+
+void napi_hash_add(struct napi_struct *napi)
+{
+ if (test_bit(NAPI_STATE_NO_BUSY_POLL, &napi->state) ||
+ test_and_set_bit(NAPI_STATE_HASHED, &napi->state))
+ return;
+
+ spin_lock(&napi_hash_lock);
+
+ /* 0..NR_CPUS+1 range is reserved for sender_cpu use */
+ do {
+ if (unlikely(++napi_gen_id < NR_CPUS + 1))
+ napi_gen_id = NR_CPUS + 1;
+ } while (napi_by_id(napi_gen_id));
+ napi->napi_id = napi_gen_id;
+
+ hlist_add_head_rcu(&napi->napi_hash_node,
+ &napi_hash[napi->napi_id % HASH_SIZE(napi_hash)]);
+
+ spin_unlock(&napi_hash_lock);
}
EXPORT_SYMBOL_GPL(napi_hash_add);
/* Warning : caller is responsible to make sure rcu grace period
* is respected before freeing memory containing @napi
*/
-void napi_hash_del(struct napi_struct *napi)
+bool napi_hash_del(struct napi_struct *napi)
{
+ bool rcu_sync_needed = false;
+
spin_lock(&napi_hash_lock);
- if (test_and_clear_bit(NAPI_STATE_HASHED, &napi->state))
+ if (test_and_clear_bit(NAPI_STATE_HASHED, &napi->state)) {
+ rcu_sync_needed = true;
hlist_del_rcu(&napi->napi_hash_node);
-
+ }
spin_unlock(&napi_hash_lock);
+ return rcu_sync_needed;
}
EXPORT_SYMBOL_GPL(napi_hash_del);
@@ -4744,6 +5012,7 @@ void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
napi->poll_owner = -1;
#endif
set_bit(NAPI_STATE_SCHED, &napi->state);
+ napi_hash_add(napi);
}
EXPORT_SYMBOL(netif_napi_add);
@@ -4763,8 +5032,12 @@ void napi_disable(struct napi_struct *n)
}
EXPORT_SYMBOL(napi_disable);
+/* Must be called in process context */
void netif_napi_del(struct napi_struct *napi)
{
+ might_sleep();
+ if (napi_hash_del(napi))
+ synchronize_net();
list_del_init(&napi->dev_list);
napi_free_frags(napi);
@@ -5351,7 +5624,7 @@ static void __netdev_adjacent_dev_unlink_neighbour(struct net_device *dev,
static int __netdev_upper_dev_link(struct net_device *dev,
struct net_device *upper_dev, bool master,
- void *private)
+ void *upper_priv, void *upper_info)
{
struct netdev_notifier_changeupper_info changeupper_info;
struct netdev_adjacent *i, *j, *to_i, *to_j;
@@ -5375,6 +5648,7 @@ static int __netdev_upper_dev_link(struct net_device *dev,
changeupper_info.upper_dev = upper_dev;
changeupper_info.master = master;
changeupper_info.linking = true;
+ changeupper_info.upper_info = upper_info;
ret = call_netdevice_notifiers_info(NETDEV_PRECHANGEUPPER, dev,
&changeupper_info.info);
@@ -5382,7 +5656,7 @@ static int __netdev_upper_dev_link(struct net_device *dev,
if (ret)
return ret;
- ret = __netdev_adjacent_dev_link_neighbour(dev, upper_dev, private,
+ ret = __netdev_adjacent_dev_link_neighbour(dev, upper_dev, upper_priv,
master);
if (ret)
return ret;
@@ -5420,8 +5694,12 @@ static int __netdev_upper_dev_link(struct net_device *dev,
goto rollback_lower_mesh;
}
- call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, dev,
- &changeupper_info.info);
+ ret = call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, dev,
+ &changeupper_info.info);
+ ret = notifier_to_errno(ret);
+ if (ret)
+ goto rollback_lower_mesh;
+
return 0;
rollback_lower_mesh:
@@ -5475,7 +5753,7 @@ rollback_mesh:
int netdev_upper_dev_link(struct net_device *dev,
struct net_device *upper_dev)
{
- return __netdev_upper_dev_link(dev, upper_dev, false, NULL);
+ return __netdev_upper_dev_link(dev, upper_dev, false, NULL, NULL);
}
EXPORT_SYMBOL(netdev_upper_dev_link);
@@ -5483,6 +5761,8 @@ EXPORT_SYMBOL(netdev_upper_dev_link);
* netdev_master_upper_dev_link - Add a master link to the upper device
* @dev: device
* @upper_dev: new upper device
+ * @upper_priv: upper device private
+ * @upper_info: upper info to be passed down via notifier
*
* Adds a link to device which is upper to this one. In this case, only
* one master upper device can be linked, although other non-master devices
@@ -5491,20 +5771,14 @@ EXPORT_SYMBOL(netdev_upper_dev_link);
* counts are adjusted and the function returns zero.
*/
int netdev_master_upper_dev_link(struct net_device *dev,
- struct net_device *upper_dev)
+ struct net_device *upper_dev,
+ void *upper_priv, void *upper_info)
{
- return __netdev_upper_dev_link(dev, upper_dev, true, NULL);
+ return __netdev_upper_dev_link(dev, upper_dev, true,
+ upper_priv, upper_info);
}
EXPORT_SYMBOL(netdev_master_upper_dev_link);
-int netdev_master_upper_dev_link_private(struct net_device *dev,
- struct net_device *upper_dev,
- void *private)
-{
- return __netdev_upper_dev_link(dev, upper_dev, true, private);
-}
-EXPORT_SYMBOL(netdev_master_upper_dev_link_private);
-
/**
* netdev_upper_dev_unlink - Removes a link to upper device
* @dev: device
@@ -5663,7 +5937,7 @@ EXPORT_SYMBOL(netdev_lower_dev_get_private);
int dev_get_nest_level(struct net_device *dev,
- bool (*type_check)(struct net_device *dev))
+ bool (*type_check)(const struct net_device *dev))
{
struct net_device *lower = NULL;
struct list_head *iter;
@@ -5685,6 +5959,26 @@ int dev_get_nest_level(struct net_device *dev,
}
EXPORT_SYMBOL(dev_get_nest_level);
+/**
+ * netdev_lower_change - Dispatch event about lower device state change
+ * @lower_dev: device
+ * @lower_state_info: state to dispatch
+ *
+ * Send NETDEV_CHANGELOWERSTATE to netdev notifiers with info.
+ * The caller must hold the RTNL lock.
+ */
+void netdev_lower_state_changed(struct net_device *lower_dev,
+ void *lower_state_info)
+{
+ struct netdev_notifier_changelowerstate_info changelowerstate_info;
+
+ ASSERT_RTNL();
+ changelowerstate_info.lower_state_info = lower_state_info;
+ call_netdevice_notifiers_info(NETDEV_CHANGELOWERSTATE, lower_dev,
+ &changelowerstate_info.info);
+}
+EXPORT_SYMBOL(netdev_lower_state_changed);
+
static void dev_change_rx_flags(struct net_device *dev, int flags)
{
const struct net_device_ops *ops = dev->netdev_ops;
@@ -6375,9 +6669,9 @@ static netdev_features_t netdev_fix_features(struct net_device *dev,
/* UFO needs SG and checksumming */
if (features & NETIF_F_UFO) {
/* maybe split UFO into V4 and V6? */
- if (!((features & NETIF_F_GEN_CSUM) ||
- (features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))
- == (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {
+ if (!(features & NETIF_F_HW_CSUM) &&
+ ((features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) !=
+ (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))) {
netdev_dbg(dev,
"Dropping NETIF_F_UFO since no checksum offload features.\n");
features &= ~NETIF_F_UFO;
@@ -7164,11 +7458,13 @@ EXPORT_SYMBOL(alloc_netdev_mqs);
* This function does the last stage of destroying an allocated device
* interface. The reference to the device object is released.
* If this is the last reference then it will be freed.
+ * Must be called in process context.
*/
void free_netdev(struct net_device *dev)
{
struct napi_struct *p, *n;
+ might_sleep();
netif_free_tx_queues(dev);
#ifdef CONFIG_SYSFS
kvfree(dev->_rx);
@@ -7477,16 +7773,16 @@ static int dev_cpu_callback(struct notifier_block *nfb,
netdev_features_t netdev_increment_features(netdev_features_t all,
netdev_features_t one, netdev_features_t mask)
{
- if (mask & NETIF_F_GEN_CSUM)
- mask |= NETIF_F_ALL_CSUM;
+ if (mask & NETIF_F_HW_CSUM)
+ mask |= NETIF_F_CSUM_MASK;
mask |= NETIF_F_VLAN_CHALLENGED;
- all |= one & (NETIF_F_ONE_FOR_ALL|NETIF_F_ALL_CSUM) & mask;
+ all |= one & (NETIF_F_ONE_FOR_ALL | NETIF_F_CSUM_MASK) & mask;
all &= one | ~NETIF_F_ALL_FOR_ALL;
/* If one device supports hw checksumming, set for all. */
- if (all & NETIF_F_GEN_CSUM)
- all &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_GEN_CSUM);
+ if (all & NETIF_F_HW_CSUM)
+ all &= ~(NETIF_F_CSUM_MASK & ~NETIF_F_HW_CSUM);
return all;
}
diff --git a/net/core/dst.c b/net/core/dst.c
index e6dc77252fe9..a1656e3b8d72 100644
--- a/net/core/dst.c
+++ b/net/core/dst.c
@@ -301,12 +301,13 @@ void dst_release(struct dst_entry *dst)
{
if (dst) {
int newrefcnt;
+ unsigned short nocache = dst->flags & DST_NOCACHE;
newrefcnt = atomic_dec_return(&dst->__refcnt);
if (unlikely(newrefcnt < 0))
net_warn_ratelimited("%s: dst:%p refcnt:%d\n",
__func__, dst, newrefcnt);
- if (!newrefcnt && unlikely(dst->flags & DST_NOCACHE))
+ if (!newrefcnt && unlikely(nocache))
call_rcu(&dst->rcu_head, dst_destroy_rcu);
}
}
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 29edf74846fc..daf04709dd3c 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -87,7 +87,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN]
[NETIF_F_GSO_UDP_TUNNEL_BIT] = "tx-udp_tnl-segmentation",
[NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc",
- [NETIF_F_SCTP_CSUM_BIT] = "tx-checksum-sctp",
+ [NETIF_F_SCTP_CRC_BIT] = "tx-checksum-sctp",
[NETIF_F_FCOE_MTU_BIT] = "fcoe-mtu",
[NETIF_F_NTUPLE_BIT] = "rx-ntuple-filter",
[NETIF_F_RXHASH_BIT] = "rx-hashing",
@@ -191,6 +191,23 @@ static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
return ret;
}
+static int phy_get_sset_count(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phydev->drv->get_sset_count &&
+ phydev->drv->get_strings &&
+ phydev->drv->get_stats) {
+ mutex_lock(&phydev->lock);
+ ret = phydev->drv->get_sset_count(phydev);
+ mutex_unlock(&phydev->lock);
+
+ return ret;
+ }
+
+ return -EOPNOTSUPP;
+}
+
static int __ethtool_get_sset_count(struct net_device *dev, int sset)
{
const struct ethtool_ops *ops = dev->ethtool_ops;
@@ -204,6 +221,13 @@ static int __ethtool_get_sset_count(struct net_device *dev, int sset)
if (sset == ETH_SS_TUNABLES)
return ARRAY_SIZE(tunable_strings);
+ if (sset == ETH_SS_PHY_STATS) {
+ if (dev->phydev)
+ return phy_get_sset_count(dev->phydev);
+ else
+ return -EOPNOTSUPP;
+ }
+
if (ops->get_sset_count && ops->get_strings)
return ops->get_sset_count(dev, sset);
else
@@ -223,7 +247,17 @@ static void __ethtool_get_strings(struct net_device *dev,
sizeof(rss_hash_func_strings));
else if (stringset == ETH_SS_TUNABLES)
memcpy(data, tunable_strings, sizeof(tunable_strings));
- else
+ else if (stringset == ETH_SS_PHY_STATS) {
+ struct phy_device *phydev = dev->phydev;
+
+ if (phydev) {
+ mutex_lock(&phydev->lock);
+ phydev->drv->get_strings(phydev, data);
+ mutex_unlock(&phydev->lock);
+ } else {
+ return;
+ }
+ } else
/* ops->get_strings is valid because checked earlier */
ops->get_strings(dev, stringset, data);
}
@@ -235,7 +269,7 @@ static netdev_features_t ethtool_get_feature_mask(u32 eth_cmd)
switch (eth_cmd) {
case ETHTOOL_GTXCSUM:
case ETHTOOL_STXCSUM:
- return NETIF_F_ALL_CSUM | NETIF_F_SCTP_CSUM;
+ return NETIF_F_CSUM_MASK | NETIF_F_SCTP_CRC;
case ETHTOOL_GRXCSUM:
case ETHTOOL_SRXCSUM:
return NETIF_F_RXCSUM;
@@ -1401,6 +1435,47 @@ static int ethtool_get_stats(struct net_device *dev, void __user *useraddr)
return ret;
}
+static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr)
+{
+ struct ethtool_stats stats;
+ struct phy_device *phydev = dev->phydev;
+ u64 *data;
+ int ret, n_stats;
+
+ if (!phydev)
+ return -EOPNOTSUPP;
+
+ n_stats = phy_get_sset_count(phydev);
+
+ if (n_stats < 0)
+ return n_stats;
+ WARN_ON(n_stats == 0);
+
+ if (copy_from_user(&stats, useraddr, sizeof(stats)))
+ return -EFAULT;
+
+ stats.n_stats = n_stats;
+ data = kmalloc_array(n_stats, sizeof(u64), GFP_USER);
+ if (!data)
+ return -ENOMEM;
+
+ mutex_lock(&phydev->lock);
+ phydev->drv->get_stats(phydev, &stats, data);
+ mutex_unlock(&phydev->lock);
+
+ ret = -EFAULT;
+ if (copy_to_user(useraddr, &stats, sizeof(stats)))
+ goto out;
+ useraddr += sizeof(stats);
+ if (copy_to_user(useraddr, data, stats.n_stats * sizeof(u64)))
+ goto out;
+ ret = 0;
+
+ out:
+ kfree(data);
+ return ret;
+}
+
static int ethtool_get_perm_addr(struct net_device *dev, void __user *useraddr)
{
struct ethtool_perm_addr epaddr;
@@ -1779,6 +1854,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
case ETHTOOL_GSSET_INFO:
case ETHTOOL_GSTRINGS:
case ETHTOOL_GSTATS:
+ case ETHTOOL_GPHYSTATS:
case ETHTOOL_GTSO:
case ETHTOOL_GPERMADDR:
case ETHTOOL_GUFO:
@@ -1991,6 +2067,9 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
case ETHTOOL_STUNABLE:
rc = ethtool_set_tunable(dev, useraddr);
break;
+ case ETHTOOL_GPHYSTATS:
+ rc = ethtool_get_phy_stats(dev, useraddr);
+ break;
default:
rc = -EOPNOTSUPP;
}
diff --git a/net/core/filter.c b/net/core/filter.c
index 672eefbfbe99..94d26201080d 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -50,6 +50,7 @@
#include <net/cls_cgroup.h>
#include <net/dst_metadata.h>
#include <net/dst.h>
+#include <net/sock_reuseport.h>
/**
* sk_filter - run a packet through a socket filter
@@ -348,12 +349,6 @@ static bool convert_bpf_extensions(struct sock_filter *fp,
* jump offsets, 2nd pass remapping:
* new_prog = kmalloc(sizeof(struct bpf_insn) * new_len);
* bpf_convert_filter(old_prog, old_len, new_prog, &new_len);
- *
- * User BPF's register A is mapped to our BPF register 6, user BPF
- * register X is mapped to BPF register 7; frame pointer is always
- * register 10; Context 'void *ctx' is stored in register 1, that is,
- * for socket filters: ctx == 'struct sk_buff *', for seccomp:
- * ctx == 'struct seccomp_data *'.
*/
static int bpf_convert_filter(struct sock_filter *prog, int len,
struct bpf_insn *new_prog, int *new_len)
@@ -381,9 +376,22 @@ do_pass:
new_insn = new_prog;
fp = prog;
- if (new_insn)
- *new_insn = BPF_MOV64_REG(BPF_REG_CTX, BPF_REG_ARG1);
- new_insn++;
+ /* Classic BPF related prologue emission. */
+ if (new_insn) {
+ /* Classic BPF expects A and X to be reset first. These need
+ * to be guaranteed to be the first two instructions.
+ */
+ *new_insn++ = BPF_ALU64_REG(BPF_XOR, BPF_REG_A, BPF_REG_A);
+ *new_insn++ = BPF_ALU64_REG(BPF_XOR, BPF_REG_X, BPF_REG_X);
+
+ /* All programs must keep CTX in callee saved BPF_REG_CTX.
+ * In eBPF case it's done by the compiler, here we need to
+ * do this ourself. Initial CTX is present in BPF_REG_ARG1.
+ */
+ *new_insn++ = BPF_MOV64_REG(BPF_REG_CTX, BPF_REG_ARG1);
+ } else {
+ new_insn += 3;
+ }
for (i = 0; i < len; fp++, i++) {
struct bpf_insn tmp_insns[6] = { };
@@ -777,6 +785,11 @@ static int bpf_check_classic(const struct sock_filter *filter,
if (ftest->k == 0)
return -EINVAL;
break;
+ case BPF_ALU | BPF_LSH | BPF_K:
+ case BPF_ALU | BPF_RSH | BPF_K:
+ if (ftest->k >= 32)
+ return -EINVAL;
+ break;
case BPF_LD | BPF_MEM:
case BPF_LDX | BPF_MEM:
case BPF_ST:
@@ -1160,17 +1173,32 @@ static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk)
return 0;
}
-/**
- * sk_attach_filter - attach a socket filter
- * @fprog: the filter program
- * @sk: the socket to use
- *
- * Attach the user's filter code. We first run some sanity checks on
- * it to make sure it does not explode on us later. If an error
- * occurs or there is insufficient memory for the filter a negative
- * errno code is returned. On success the return is zero.
- */
-int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
+static int __reuseport_attach_prog(struct bpf_prog *prog, struct sock *sk)
+{
+ struct bpf_prog *old_prog;
+ int err;
+
+ if (bpf_prog_size(prog->len) > sysctl_optmem_max)
+ return -ENOMEM;
+
+ if (sk_unhashed(sk)) {
+ err = reuseport_alloc(sk);
+ if (err)
+ return err;
+ } else if (!rcu_access_pointer(sk->sk_reuseport_cb)) {
+ /* The socket wasn't bound with SO_REUSEPORT */
+ return -EINVAL;
+ }
+
+ old_prog = reuseport_attach_prog(sk, prog);
+ if (old_prog)
+ bpf_prog_destroy(old_prog);
+
+ return 0;
+}
+
+static
+struct bpf_prog *__get_filter(struct sock_fprog *fprog, struct sock *sk)
{
unsigned int fsize = bpf_classic_proglen(fprog);
unsigned int bpf_fsize = bpf_prog_size(fprog->len);
@@ -1178,19 +1206,19 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
int err;
if (sock_flag(sk, SOCK_FILTER_LOCKED))
- return -EPERM;
+ return ERR_PTR(-EPERM);
/* Make sure new filter is there and in the right amounts. */
if (fprog->filter == NULL)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
prog = bpf_prog_alloc(bpf_fsize, 0);
if (!prog)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
if (copy_from_user(prog->insns, fprog->filter, fsize)) {
__bpf_prog_free(prog);
- return -EFAULT;
+ return ERR_PTR(-EFAULT);
}
prog->len = fprog->len;
@@ -1198,13 +1226,30 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
err = bpf_prog_store_orig_filter(prog, fprog);
if (err) {
__bpf_prog_free(prog);
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
}
/* bpf_prepare_filter() already takes care of freeing
* memory in case something goes wrong.
*/
- prog = bpf_prepare_filter(prog, NULL);
+ return bpf_prepare_filter(prog, NULL);
+}
+
+/**
+ * sk_attach_filter - attach a socket filter
+ * @fprog: the filter program
+ * @sk: the socket to use
+ *
+ * Attach the user's filter code. We first run some sanity checks on
+ * it to make sure it does not explode on us later. If an error
+ * occurs or there is insufficient memory for the filter a negative
+ * errno code is returned. On success the return is zero.
+ */
+int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
+{
+ struct bpf_prog *prog = __get_filter(fprog, sk);
+ int err;
+
if (IS_ERR(prog))
return PTR_ERR(prog);
@@ -1218,23 +1263,50 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
}
EXPORT_SYMBOL_GPL(sk_attach_filter);
-int sk_attach_bpf(u32 ufd, struct sock *sk)
+int sk_reuseport_attach_filter(struct sock_fprog *fprog, struct sock *sk)
{
- struct bpf_prog *prog;
+ struct bpf_prog *prog = __get_filter(fprog, sk);
int err;
+ if (IS_ERR(prog))
+ return PTR_ERR(prog);
+
+ err = __reuseport_attach_prog(prog, sk);
+ if (err < 0) {
+ __bpf_prog_release(prog);
+ return err;
+ }
+
+ return 0;
+}
+
+static struct bpf_prog *__get_bpf(u32 ufd, struct sock *sk)
+{
+ struct bpf_prog *prog;
+
if (sock_flag(sk, SOCK_FILTER_LOCKED))
- return -EPERM;
+ return ERR_PTR(-EPERM);
prog = bpf_prog_get(ufd);
if (IS_ERR(prog))
- return PTR_ERR(prog);
+ return prog;
if (prog->type != BPF_PROG_TYPE_SOCKET_FILTER) {
bpf_prog_put(prog);
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
}
+ return prog;
+}
+
+int sk_attach_bpf(u32 ufd, struct sock *sk)
+{
+ struct bpf_prog *prog = __get_bpf(ufd, sk);
+ int err;
+
+ if (IS_ERR(prog))
+ return PTR_ERR(prog);
+
err = __sk_attach_prog(prog, sk);
if (err < 0) {
bpf_prog_put(prog);
@@ -1244,7 +1316,24 @@ int sk_attach_bpf(u32 ufd, struct sock *sk)
return 0;
}
-#define BPF_RECOMPUTE_CSUM(flags) ((flags) & 1)
+int sk_reuseport_attach_bpf(u32 ufd, struct sock *sk)
+{
+ struct bpf_prog *prog = __get_bpf(ufd, sk);
+ int err;
+
+ if (IS_ERR(prog))
+ return PTR_ERR(prog);
+
+ err = __reuseport_attach_prog(prog, sk);
+ if (err < 0) {
+ bpf_prog_put(prog);
+ return err;
+ }
+
+ return 0;
+}
+
+#define BPF_LDST_LEN 16U
static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
{
@@ -1252,9 +1341,12 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
int offset = (int) r2;
void *from = (void *) (long) r3;
unsigned int len = (unsigned int) r4;
- char buf[16];
+ char buf[BPF_LDST_LEN];
void *ptr;
+ if (unlikely(flags & ~(BPF_F_RECOMPUTE_CSUM)))
+ return -EINVAL;
+
/* bpf verifier guarantees that:
* 'from' pointer points to bpf program stack
* 'len' bytes of it were initialized
@@ -1274,7 +1366,7 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
if (unlikely(!ptr))
return -EFAULT;
- if (BPF_RECOMPUTE_CSUM(flags))
+ if (flags & BPF_F_RECOMPUTE_CSUM)
skb_postpull_rcsum(skb, ptr, len);
memcpy(ptr, from, len);
@@ -1283,8 +1375,9 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
/* skb_store_bits cannot return -EFAULT here */
skb_store_bits(skb, offset, ptr, len);
- if (BPF_RECOMPUTE_CSUM(flags) && skb->ip_summed == CHECKSUM_COMPLETE)
- skb->csum = csum_add(skb->csum, csum_partial(ptr, len, 0));
+ if (flags & BPF_F_RECOMPUTE_CSUM)
+ skb_postpush_rcsum(skb, ptr, len);
+
return 0;
}
@@ -1299,8 +1392,35 @@ const struct bpf_func_proto bpf_skb_store_bytes_proto = {
.arg5_type = ARG_ANYTHING,
};
-#define BPF_HEADER_FIELD_SIZE(flags) ((flags) & 0x0f)
-#define BPF_IS_PSEUDO_HEADER(flags) ((flags) & 0x10)
+static u64 bpf_skb_load_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
+{
+ const struct sk_buff *skb = (const struct sk_buff *)(unsigned long) r1;
+ int offset = (int) r2;
+ void *to = (void *)(unsigned long) r3;
+ unsigned int len = (unsigned int) r4;
+ void *ptr;
+
+ if (unlikely((u32) offset > 0xffff || len > BPF_LDST_LEN))
+ return -EFAULT;
+
+ ptr = skb_header_pointer(skb, offset, len, to);
+ if (unlikely(!ptr))
+ return -EFAULT;
+ if (ptr != to)
+ memcpy(to, ptr, len);
+
+ return 0;
+}
+
+const struct bpf_func_proto bpf_skb_load_bytes_proto = {
+ .func = bpf_skb_load_bytes,
+ .gpl_only = false,
+ .ret_type = RET_INTEGER,
+ .arg1_type = ARG_PTR_TO_CTX,
+ .arg2_type = ARG_ANYTHING,
+ .arg3_type = ARG_PTR_TO_STACK,
+ .arg4_type = ARG_CONST_STACK_SIZE,
+};
static u64 bpf_l3_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
{
@@ -1308,6 +1428,8 @@ static u64 bpf_l3_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
int offset = (int) r2;
__sum16 sum, *ptr;
+ if (unlikely(flags & ~(BPF_F_HDR_FIELD_MASK)))
+ return -EINVAL;
if (unlikely((u32) offset > 0xffff))
return -EFAULT;
@@ -1319,7 +1441,7 @@ static u64 bpf_l3_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
if (unlikely(!ptr))
return -EFAULT;
- switch (BPF_HEADER_FIELD_SIZE(flags)) {
+ switch (flags & BPF_F_HDR_FIELD_MASK) {
case 2:
csum_replace2(ptr, from, to);
break;
@@ -1351,10 +1473,12 @@ const struct bpf_func_proto bpf_l3_csum_replace_proto = {
static u64 bpf_l4_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
{
struct sk_buff *skb = (struct sk_buff *) (long) r1;
- bool is_pseudo = !!BPF_IS_PSEUDO_HEADER(flags);
+ bool is_pseudo = flags & BPF_F_PSEUDO_HDR;
int offset = (int) r2;
__sum16 sum, *ptr;
+ if (unlikely(flags & ~(BPF_F_PSEUDO_HDR | BPF_F_HDR_FIELD_MASK)))
+ return -EINVAL;
if (unlikely((u32) offset > 0xffff))
return -EFAULT;
@@ -1366,7 +1490,7 @@ static u64 bpf_l4_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
if (unlikely(!ptr))
return -EFAULT;
- switch (BPF_HEADER_FIELD_SIZE(flags)) {
+ switch (flags & BPF_F_HDR_FIELD_MASK) {
case 2:
inet_proto_csum_replace2(ptr, skb, from, to, is_pseudo);
break;
@@ -1395,13 +1519,14 @@ const struct bpf_func_proto bpf_l4_csum_replace_proto = {
.arg5_type = ARG_ANYTHING,
};
-#define BPF_IS_REDIRECT_INGRESS(flags) ((flags) & 1)
-
static u64 bpf_clone_redirect(u64 r1, u64 ifindex, u64 flags, u64 r4, u64 r5)
{
struct sk_buff *skb = (struct sk_buff *) (long) r1, *skb2;
struct net_device *dev;
+ if (unlikely(flags & ~(BPF_F_INGRESS)))
+ return -EINVAL;
+
dev = dev_get_by_index_rcu(dev_net(skb->dev), ifindex);
if (unlikely(!dev))
return -EINVAL;
@@ -1410,8 +1535,12 @@ static u64 bpf_clone_redirect(u64 r1, u64 ifindex, u64 flags, u64 r4, u64 r5)
if (unlikely(!skb2))
return -ENOMEM;
- if (BPF_IS_REDIRECT_INGRESS(flags))
+ if (flags & BPF_F_INGRESS) {
+ if (skb_at_tc_ingress(skb2))
+ skb_postpush_rcsum(skb2, skb_mac_header(skb2),
+ skb2->mac_len);
return dev_forward_skb(dev, skb2);
+ }
skb2->dev = dev;
skb_sender_cpu_clear(skb2);
@@ -1433,12 +1562,17 @@ struct redirect_info {
};
static DEFINE_PER_CPU(struct redirect_info, redirect_info);
+
static u64 bpf_redirect(u64 ifindex, u64 flags, u64 r3, u64 r4, u64 r5)
{
struct redirect_info *ri = this_cpu_ptr(&redirect_info);
+ if (unlikely(flags & ~(BPF_F_INGRESS)))
+ return TC_ACT_SHOT;
+
ri->ifindex = ifindex;
ri->flags = flags;
+
return TC_ACT_REDIRECT;
}
@@ -1454,8 +1588,12 @@ int skb_do_redirect(struct sk_buff *skb)
return -EINVAL;
}
- if (BPF_IS_REDIRECT_INGRESS(ri->flags))
+ if (ri->flags & BPF_F_INGRESS) {
+ if (skb_at_tc_ingress(skb))
+ skb_postpush_rcsum(skb, skb_mac_header(skb),
+ skb->mac_len);
return dev_forward_skb(dev, skb);
+ }
skb->dev = dev;
skb_sender_cpu_clear(skb);
@@ -1547,19 +1685,49 @@ bool bpf_helper_changes_skb_data(void *func)
return false;
}
+static unsigned short bpf_tunnel_key_af(u64 flags)
+{
+ return flags & BPF_F_TUNINFO_IPV6 ? AF_INET6 : AF_INET;
+}
+
static u64 bpf_skb_get_tunnel_key(u64 r1, u64 r2, u64 size, u64 flags, u64 r5)
{
struct sk_buff *skb = (struct sk_buff *) (long) r1;
struct bpf_tunnel_key *to = (struct bpf_tunnel_key *) (long) r2;
- struct ip_tunnel_info *info = skb_tunnel_info(skb);
+ const struct ip_tunnel_info *info = skb_tunnel_info(skb);
+ u8 compat[sizeof(struct bpf_tunnel_key)];
- if (unlikely(size != sizeof(struct bpf_tunnel_key) || flags || !info))
- return -EINVAL;
- if (ip_tunnel_info_af(info) != AF_INET)
+ if (unlikely(!info || (flags & ~(BPF_F_TUNINFO_IPV6))))
return -EINVAL;
+ if (ip_tunnel_info_af(info) != bpf_tunnel_key_af(flags))
+ return -EPROTO;
+ if (unlikely(size != sizeof(struct bpf_tunnel_key))) {
+ switch (size) {
+ case offsetof(struct bpf_tunnel_key, remote_ipv6[1]):
+ /* Fixup deprecated structure layouts here, so we have
+ * a common path later on.
+ */
+ if (ip_tunnel_info_af(info) != AF_INET)
+ return -EINVAL;
+ to = (struct bpf_tunnel_key *)compat;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
to->tunnel_id = be64_to_cpu(info->key.tun_id);
- to->remote_ipv4 = be32_to_cpu(info->key.u.ipv4.src);
+ to->tunnel_tos = info->key.tos;
+ to->tunnel_ttl = info->key.ttl;
+
+ if (flags & BPF_F_TUNINFO_IPV6)
+ memcpy(to->remote_ipv6, &info->key.u.ipv6.src,
+ sizeof(to->remote_ipv6));
+ else
+ to->remote_ipv4 = be32_to_cpu(info->key.u.ipv4.src);
+
+ if (unlikely(size != sizeof(struct bpf_tunnel_key)))
+ memcpy((void *)(long) r2, to, size);
return 0;
}
@@ -1581,10 +1749,25 @@ static u64 bpf_skb_set_tunnel_key(u64 r1, u64 r2, u64 size, u64 flags, u64 r5)
struct sk_buff *skb = (struct sk_buff *) (long) r1;
struct bpf_tunnel_key *from = (struct bpf_tunnel_key *) (long) r2;
struct metadata_dst *md = this_cpu_ptr(md_dst);
+ u8 compat[sizeof(struct bpf_tunnel_key)];
struct ip_tunnel_info *info;
- if (unlikely(size != sizeof(struct bpf_tunnel_key) || flags))
+ if (unlikely(flags & ~(BPF_F_TUNINFO_IPV6)))
return -EINVAL;
+ if (unlikely(size != sizeof(struct bpf_tunnel_key))) {
+ switch (size) {
+ case offsetof(struct bpf_tunnel_key, remote_ipv6[1]):
+ /* Fixup deprecated structure layouts here, so we have
+ * a common path later on.
+ */
+ memcpy(compat, from, size);
+ memset(compat + size, 0, sizeof(compat) - size);
+ from = (struct bpf_tunnel_key *)compat;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
skb_dst_drop(skb);
dst_hold((struct dst_entry *) md);
@@ -1592,9 +1775,19 @@ static u64 bpf_skb_set_tunnel_key(u64 r1, u64 r2, u64 size, u64 flags, u64 r5)
info = &md->u.tun_info;
info->mode = IP_TUNNEL_INFO_TX;
+
info->key.tun_flags = TUNNEL_KEY;
info->key.tun_id = cpu_to_be64(from->tunnel_id);
- info->key.u.ipv4.dst = cpu_to_be32(from->remote_ipv4);
+ info->key.tos = from->tunnel_tos;
+ info->key.ttl = from->tunnel_ttl;
+
+ if (flags & BPF_F_TUNINFO_IPV6) {
+ info->mode |= IP_TUNNEL_INFO_IPV6;
+ memcpy(&info->key.u.ipv6.dst, from->remote_ipv6,
+ sizeof(from->remote_ipv6));
+ } else {
+ info->key.u.ipv4.dst = cpu_to_be32(from->remote_ipv4);
+ }
return 0;
}
@@ -1654,6 +1847,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id)
switch (func_id) {
case BPF_FUNC_skb_store_bytes:
return &bpf_skb_store_bytes_proto;
+ case BPF_FUNC_skb_load_bytes:
+ return &bpf_skb_load_bytes_proto;
case BPF_FUNC_l3_csum_replace:
return &bpf_l3_csum_replace_proto;
case BPF_FUNC_l4_csum_replace:
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index e6af42da28d9..f18ae91b652e 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -2215,7 +2215,7 @@ static int pneigh_fill_info(struct sk_buff *skb, struct pneigh_entry *pn,
ndm->ndm_pad2 = 0;
ndm->ndm_flags = pn->flags | NTF_PROXY;
ndm->ndm_type = RTN_UNICAST;
- ndm->ndm_ifindex = pn->dev->ifindex;
+ ndm->ndm_ifindex = pn->dev ? pn->dev->ifindex : 0;
ndm->ndm_state = NUD_NONE;
if (nla_put(skb, NDA_DST, tbl->key_len, pn->key))
@@ -2333,7 +2333,7 @@ static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
if (h > s_h)
s_idx = 0;
for (n = tbl->phash_buckets[h], idx = 0; n; n = n->next) {
- if (dev_net(n->dev) != net)
+ if (pneigh_net(n) != net)
continue;
if (idx < s_idx)
goto next;
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index f88a62ab019d..b6c8a6629b39 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -471,6 +471,7 @@ static ssize_t phys_switch_id_show(struct device *dev,
if (dev_isalive(netdev)) {
struct switchdev_attr attr = {
+ .orig_dev = netdev,
.id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
.flags = SWITCHDEV_F_NO_RECURSE,
};
@@ -1452,8 +1453,8 @@ static void netdev_release(struct device *d)
static const void *net_namespace(struct device *d)
{
- struct net_device *dev;
- dev = container_of(d, struct net_device, dev);
+ struct net_device *dev = to_net_dev(d);
+
return dev_net(dev);
}
diff --git a/net/core/net-traces.c b/net/core/net-traces.c
index adef015b2f41..92da5e4ceb4f 100644
--- a/net/core/net-traces.c
+++ b/net/core/net-traces.c
@@ -32,6 +32,10 @@
#include <trace/events/sock.h>
#include <trace/events/udp.h>
#include <trace/events/fib.h>
+#if IS_ENABLED(CONFIG_IPV6)
+#include <trace/events/fib6.h>
+EXPORT_TRACEPOINT_SYMBOL_GPL(fib6_table_lookup);
+#endif
EXPORT_TRACEPOINT_SYMBOL_GPL(kfree_skb);
diff --git a/net/core/netclassid_cgroup.c b/net/core/netclassid_cgroup.c
index 6441f47b1a8f..0260c84ed83c 100644
--- a/net/core/netclassid_cgroup.c
+++ b/net/core/netclassid_cgroup.c
@@ -56,29 +56,41 @@ static void cgrp_css_free(struct cgroup_subsys_state *css)
kfree(css_cls_state(css));
}
-static int update_classid(const void *v, struct file *file, unsigned n)
+static int update_classid_sock(const void *v, struct file *file, unsigned n)
{
int err;
struct socket *sock = sock_from_file(file, &err);
- if (sock)
- sock->sk->sk_classid = (u32)(unsigned long)v;
-
+ if (sock) {
+ spin_lock(&cgroup_sk_update_lock);
+ sock_cgroup_set_classid(&sock->sk->sk_cgrp_data,
+ (unsigned long)v);
+ spin_unlock(&cgroup_sk_update_lock);
+ }
return 0;
}
-static void cgrp_attach(struct cgroup_subsys_state *css,
- struct cgroup_taskset *tset)
+static void update_classid(struct cgroup_subsys_state *css, void *v)
{
- struct cgroup_cls_state *cs = css_cls_state(css);
- void *v = (void *)(unsigned long)cs->classid;
+ struct css_task_iter it;
struct task_struct *p;
- cgroup_taskset_for_each(p, tset) {
+ css_task_iter_start(css, &it);
+ while ((p = css_task_iter_next(&it))) {
task_lock(p);
- iterate_fd(p->files, 0, update_classid, v);
+ iterate_fd(p->files, 0, update_classid_sock, v);
task_unlock(p);
}
+ css_task_iter_end(&it);
+}
+
+static void cgrp_attach(struct cgroup_taskset *tset)
+{
+ struct cgroup_subsys_state *css;
+
+ cgroup_taskset_first(tset, &css);
+ update_classid(css,
+ (void *)(unsigned long)css_cls_state(css)->classid);
}
static u64 read_classid(struct cgroup_subsys_state *css, struct cftype *cft)
@@ -89,8 +101,13 @@ static u64 read_classid(struct cgroup_subsys_state *css, struct cftype *cft)
static int write_classid(struct cgroup_subsys_state *css, struct cftype *cft,
u64 value)
{
- css_cls_state(css)->classid = (u32) value;
+ struct cgroup_cls_state *cs = css_cls_state(css);
+
+ cgroup_sk_alloc_disable();
+
+ cs->classid = (u32)value;
+ update_classid(css, (void *)(unsigned long)cs->classid);
return 0;
}
diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c
index cbd0a199bf52..f1efbc39ef6b 100644
--- a/net/core/netprio_cgroup.c
+++ b/net/core/netprio_cgroup.c
@@ -27,6 +27,12 @@
#include <linux/fdtable.h>
+/*
+ * netprio allocates per-net_device priomap array which is indexed by
+ * css->id. Limiting css ID to 16bits doesn't lose anything.
+ */
+#define NETPRIO_ID_MAX USHRT_MAX
+
#define PRIOMAP_MIN_SZ 128
/*
@@ -144,6 +150,9 @@ static int cgrp_css_online(struct cgroup_subsys_state *css)
struct net_device *dev;
int ret = 0;
+ if (css->id > NETPRIO_ID_MAX)
+ return -ENOSPC;
+
if (!parent_css)
return 0;
@@ -200,6 +209,8 @@ static ssize_t write_priomap(struct kernfs_open_file *of,
if (!dev)
return -ENODEV;
+ cgroup_sk_alloc_disable();
+
rtnl_lock();
ret = netprio_set_prio(of_css(of), dev, prio);
@@ -213,18 +224,23 @@ static int update_netprio(const void *v, struct file *file, unsigned n)
{
int err;
struct socket *sock = sock_from_file(file, &err);
- if (sock)
- sock->sk->sk_cgrp_prioidx = (u32)(unsigned long)v;
+ if (sock) {
+ spin_lock(&cgroup_sk_update_lock);
+ sock_cgroup_set_prioidx(&sock->sk->sk_cgrp_data,
+ (unsigned long)v);
+ spin_unlock(&cgroup_sk_update_lock);
+ }
return 0;
}
-static void net_prio_attach(struct cgroup_subsys_state *css,
- struct cgroup_taskset *tset)
+static void net_prio_attach(struct cgroup_taskset *tset)
{
struct task_struct *p;
- void *v = (void *)(unsigned long)css->cgroup->id;
+ struct cgroup_subsys_state *css;
+
+ cgroup_taskset_for_each(p, css, tset) {
+ void *v = (void *)(unsigned long)css->cgroup->id;
- cgroup_taskset_for_each(p, tset) {
task_lock(p);
iterate_fd(p->files, 0, update_netprio, v);
task_unlock(p);
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index de8d5cc5eb24..1474cfd2dc1c 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -2787,7 +2787,9 @@ static struct sk_buff *pktgen_alloc_skb(struct net_device *dev,
} else {
skb = __netdev_alloc_skb(dev, size, GFP_NOWAIT);
}
- skb_reserve(skb, LL_RESERVED_SPACE(dev));
+
+ if (likely(skb))
+ skb_reserve(skb, LL_RESERVED_SPACE(dev));
return skb;
}
@@ -2898,7 +2900,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
if (!(pkt_dev->flags & F_UDPCSUM)) {
skb->ip_summed = CHECKSUM_NONE;
- } else if (odev->features & NETIF_F_V4_CSUM) {
+ } else if (odev->features & (NETIF_F_HW_CSUM | NETIF_F_IP_CSUM)) {
skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum = 0;
udp4_hwcsum(skb, iph->saddr, iph->daddr);
@@ -3032,7 +3034,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
if (!(pkt_dev->flags & F_UDPCSUM)) {
skb->ip_summed = CHECKSUM_NONE;
- } else if (odev->features & NETIF_F_V6_CSUM) {
+ } else if (odev->features & (NETIF_F_HW_CSUM | NETIF_F_IPV6_CSUM)) {
skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum_start = skb_transport_header(skb) - skb->head;
skb->csum_offset = offsetof(struct udphdr, check);
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 34ba7a08876d..d735e854f916 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -1027,6 +1027,7 @@ static int rtnl_phys_switch_id_fill(struct sk_buff *skb, struct net_device *dev)
{
int err;
struct switchdev_attr attr = {
+ .orig_dev = dev,
.id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
.flags = SWITCHDEV_F_NO_RECURSE,
};
@@ -2563,7 +2564,7 @@ static int nlmsg_populate_fdb_fill(struct sk_buff *skb,
struct net_device *dev,
u8 *addr, u16 vid, u32 pid, u32 seq,
int type, unsigned int flags,
- int nlflags)
+ int nlflags, u16 ndm_state)
{
struct nlmsghdr *nlh;
struct ndmsg *ndm;
@@ -2579,7 +2580,7 @@ static int nlmsg_populate_fdb_fill(struct sk_buff *skb,
ndm->ndm_flags = flags;
ndm->ndm_type = 0;
ndm->ndm_ifindex = dev->ifindex;
- ndm->ndm_state = NUD_PERMANENT;
+ ndm->ndm_state = ndm_state;
if (nla_put(skb, NDA_LLADDR, ETH_ALEN, addr))
goto nla_put_failure;
@@ -2600,7 +2601,8 @@ static inline size_t rtnl_fdb_nlmsg_size(void)
return NLMSG_ALIGN(sizeof(struct ndmsg)) + nla_total_size(ETH_ALEN);
}
-static void rtnl_fdb_notify(struct net_device *dev, u8 *addr, u16 vid, int type)
+static void rtnl_fdb_notify(struct net_device *dev, u8 *addr, u16 vid, int type,
+ u16 ndm_state)
{
struct net *net = dev_net(dev);
struct sk_buff *skb;
@@ -2611,7 +2613,7 @@ static void rtnl_fdb_notify(struct net_device *dev, u8 *addr, u16 vid, int type)
goto errout;
err = nlmsg_populate_fdb_fill(skb, dev, addr, vid,
- 0, 0, type, NTF_SELF, 0);
+ 0, 0, type, NTF_SELF, 0, ndm_state);
if (err < 0) {
kfree_skb(skb);
goto errout;
@@ -2746,7 +2748,8 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
nlh->nlmsg_flags);
if (!err) {
- rtnl_fdb_notify(dev, addr, vid, RTM_NEWNEIGH);
+ rtnl_fdb_notify(dev, addr, vid, RTM_NEWNEIGH,
+ ndm->ndm_state);
ndm->ndm_flags &= ~NTF_SELF;
}
}
@@ -2847,7 +2850,8 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
err = ndo_dflt_fdb_del(ndm, tb, dev, addr, vid);
if (!err) {
- rtnl_fdb_notify(dev, addr, vid, RTM_DELNEIGH);
+ rtnl_fdb_notify(dev, addr, vid, RTM_DELNEIGH,
+ ndm->ndm_state);
ndm->ndm_flags &= ~NTF_SELF;
}
}
@@ -2875,7 +2879,7 @@ static int nlmsg_populate_fdb(struct sk_buff *skb,
err = nlmsg_populate_fdb_fill(skb, dev, ha->addr, 0,
portid, seq,
RTM_NEWNEIGH, NTF_SELF,
- NLM_F_MULTI);
+ NLM_F_MULTI, NUD_PERMANENT);
if (err < 0)
return err;
skip:
@@ -3347,7 +3351,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
struct net *net = sock_net(skb->sk);
rtnl_doit_func doit;
- int sz_idx, kind;
+ int kind;
int family;
int type;
int err;
@@ -3363,7 +3367,6 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
return 0;
family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family;
- sz_idx = type>>2;
kind = type&3;
if (kind != 2 && !netlink_net_capable(skb, CAP_NET_ADMIN))
diff --git a/net/core/scm.c b/net/core/scm.c
index 3b6899b7d810..14596fb37172 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -289,8 +289,8 @@ void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
/* Bump the usage count and install the file. */
sock = sock_from_file(fp[i], &err);
if (sock) {
- sock_update_netprioidx(sock->sk);
- sock_update_classid(sock->sk);
+ sock_update_netprioidx(&sock->sk->sk_cgrp_data);
+ sock_update_classid(&sock->sk->sk_cgrp_data);
}
fd_install(new_fd, get_file(fp[i]));
}
@@ -305,6 +305,8 @@ void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
err = put_user(cmlen, &cm->cmsg_len);
if (!err) {
cmlen = CMSG_SPACE(i*sizeof(int));
+ if (msg->msg_controllen < cmlen)
+ cmlen = msg->msg_controllen;
msg->msg_control += cmlen;
msg->msg_controllen -= cmlen;
}
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 152b9c70e252..b2df375ec9c2 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -3643,7 +3643,8 @@ static void __skb_complete_tx_timestamp(struct sk_buff *skb,
serr->ee.ee_info = tstype;
if (sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID) {
serr->ee.ee_data = skb_shinfo(skb)->tskey;
- if (sk->sk_protocol == IPPROTO_TCP)
+ if (sk->sk_protocol == IPPROTO_TCP &&
+ sk->sk_type == SOCK_STREAM)
serr->ee.ee_data -= sk->sk_tskey;
}
@@ -4268,7 +4269,7 @@ static struct sk_buff *skb_reorder_vlan_header(struct sk_buff *skb)
return NULL;
}
- memmove(skb->data - ETH_HLEN, skb->data - skb->mac_len,
+ memmove(skb->data - ETH_HLEN, skb->data - skb->mac_len - VLAN_HLEN,
2 * ETH_ALEN);
skb->mac_header += VLAN_HLEN;
return skb;
diff --git a/net/core/sock.c b/net/core/sock.c
index 1e4dd54bfb5a..6c1c8bc93412 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -134,6 +134,7 @@
#include <linux/sock_diag.h>
#include <linux/filter.h>
+#include <net/sock_reuseport.h>
#include <trace/events/sock.h>
@@ -194,44 +195,6 @@ bool sk_net_capable(const struct sock *sk, int cap)
}
EXPORT_SYMBOL(sk_net_capable);
-
-#ifdef CONFIG_MEMCG_KMEM
-int mem_cgroup_sockets_init(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
-{
- struct proto *proto;
- int ret = 0;
-
- mutex_lock(&proto_list_mutex);
- list_for_each_entry(proto, &proto_list, node) {
- if (proto->init_cgroup) {
- ret = proto->init_cgroup(memcg, ss);
- if (ret)
- goto out;
- }
- }
-
- mutex_unlock(&proto_list_mutex);
- return ret;
-out:
- list_for_each_entry_continue_reverse(proto, &proto_list, node)
- if (proto->destroy_cgroup)
- proto->destroy_cgroup(memcg);
- mutex_unlock(&proto_list_mutex);
- return ret;
-}
-
-void mem_cgroup_sockets_destroy(struct mem_cgroup *memcg)
-{
- struct proto *proto;
-
- mutex_lock(&proto_list_mutex);
- list_for_each_entry_reverse(proto, &proto_list, node)
- if (proto->destroy_cgroup)
- proto->destroy_cgroup(memcg);
- mutex_unlock(&proto_list_mutex);
-}
-#endif
-
/*
* Each address family might have different locking rules, so we have
* one slock key per address family:
@@ -239,11 +202,6 @@ void mem_cgroup_sockets_destroy(struct mem_cgroup *memcg)
static struct lock_class_key af_family_keys[AF_MAX];
static struct lock_class_key af_family_slock_keys[AF_MAX];
-#if defined(CONFIG_MEMCG_KMEM)
-struct static_key memcg_socket_limit_enabled;
-EXPORT_SYMBOL(memcg_socket_limit_enabled);
-#endif
-
/*
* Make lock validator output more readable. (we pre-construct these
* strings build-time, so that runtime initialization of socket
@@ -433,8 +391,6 @@ static bool sock_needs_netstamp(const struct sock *sk)
}
}
-#define SK_FLAGS_TIMESTAMP ((1UL << SOCK_TIMESTAMP) | (1UL << SOCK_TIMESTAMPING_RX_SOFTWARE))
-
static void sock_disable_timestamp(struct sock *sk, unsigned long flags)
{
if (sk->sk_flags & flags) {
@@ -874,7 +830,8 @@ set_rcvbuf:
if (val & SOF_TIMESTAMPING_OPT_ID &&
!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID)) {
- if (sk->sk_protocol == IPPROTO_TCP) {
+ if (sk->sk_protocol == IPPROTO_TCP &&
+ sk->sk_type == SOCK_STREAM) {
if (sk->sk_state != TCP_ESTABLISHED) {
ret = -EINVAL;
break;
@@ -933,6 +890,32 @@ set_rcvbuf:
}
break;
+ case SO_ATTACH_REUSEPORT_CBPF:
+ ret = -EINVAL;
+ if (optlen == sizeof(struct sock_fprog)) {
+ struct sock_fprog fprog;
+
+ ret = -EFAULT;
+ if (copy_from_user(&fprog, optval, sizeof(fprog)))
+ break;
+
+ ret = sk_reuseport_attach_filter(&fprog, sk);
+ }
+ break;
+
+ case SO_ATTACH_REUSEPORT_EBPF:
+ ret = -EINVAL;
+ if (optlen == sizeof(u32)) {
+ u32 ufd;
+
+ ret = -EFAULT;
+ if (copy_from_user(&ufd, optval, sizeof(ufd)))
+ break;
+
+ ret = sk_reuseport_attach_bpf(ufd, sk);
+ }
+ break;
+
case SO_DETACH_FILTER:
ret = sk_detach_filter(sk);
break;
@@ -1363,6 +1346,7 @@ static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority,
if (!try_module_get(prot->owner))
goto out_free_sec;
sk_tx_queue_clear(sk);
+ cgroup_sk_alloc(&sk->sk_cgrp_data);
}
return sk;
@@ -1385,6 +1369,7 @@ static void sk_prot_free(struct proto *prot, struct sock *sk)
owner = prot->owner;
slab = prot->slab;
+ cgroup_sk_free(&sk->sk_cgrp_data);
security_sk_free(sk);
if (slab != NULL)
kmem_cache_free(slab, sk);
@@ -1393,17 +1378,6 @@ static void sk_prot_free(struct proto *prot, struct sock *sk)
module_put(owner);
}
-#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO)
-void sock_update_netprioidx(struct sock *sk)
-{
- if (in_interrupt())
- return;
-
- sk->sk_cgrp_prioidx = task_netprioidx(current);
-}
-EXPORT_SYMBOL_GPL(sock_update_netprioidx);
-#endif
-
/**
* sk_alloc - All socket objects are allocated here
* @net: the applicable net namespace
@@ -1432,8 +1406,8 @@ struct sock *sk_alloc(struct net *net, int family, gfp_t priority,
sock_net_set(sk, net);
atomic_set(&sk->sk_wmem_alloc, 1);
- sock_update_classid(sk);
- sock_update_netprioidx(sk);
+ sock_update_classid(&sk->sk_cgrp_data);
+ sock_update_netprioidx(&sk->sk_cgrp_data);
}
return sk;
@@ -1453,6 +1427,8 @@ void sk_destruct(struct sock *sk)
sk_filter_uncharge(sk, filter);
RCU_INIT_POINTER(sk->sk_filter, NULL);
}
+ if (rcu_access_pointer(sk->sk_reuseport_cb))
+ reuseport_detach_sock(sk);
sock_disable_timestamp(sk, SK_FLAGS_TIMESTAMP);
@@ -1488,12 +1464,6 @@ void sk_free(struct sock *sk)
}
EXPORT_SYMBOL(sk_free);
-static void sk_update_clone(const struct sock *sk, struct sock *newsk)
-{
- if (mem_cgroup_sockets_enabled && sk->sk_cgrp)
- sock_update_memcg(newsk);
-}
-
/**
* sk_clone_lock - clone a socket, and lock its clone
* @sk: the socket to clone
@@ -1530,7 +1500,6 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
skb_queue_head_init(&newsk->sk_receive_queue);
skb_queue_head_init(&newsk->sk_write_queue);
- spin_lock_init(&newsk->sk_dst_lock);
rwlock_init(&newsk->sk_callback_lock);
lockdep_set_class_and_name(&newsk->sk_callback_lock,
af_callback_keys + newsk->sk_family,
@@ -1553,7 +1522,7 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
*/
is_charged = sk_filter_charge(newsk, filter);
- if (unlikely(!is_charged || xfrm_sk_clone_policy(newsk))) {
+ if (unlikely(!is_charged || xfrm_sk_clone_policy(newsk, sk))) {
/* It is still raw copy of parent, so invalidate
* destructor and make plain sk_free() */
newsk->sk_destruct = NULL;
@@ -1589,7 +1558,8 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
sk_set_socket(newsk, NULL);
newsk->sk_wq = NULL;
- sk_update_clone(sk, newsk);
+ if (mem_cgroup_sockets_enabled && sk->sk_memcg)
+ sock_update_memcg(newsk);
if (newsk->sk_prot->sockets_allocated)
sk_sockets_allocated_inc(newsk);
@@ -1607,7 +1577,7 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
{
u32 max_segs = 1;
- __sk_dst_set(sk, dst);
+ sk_dst_set(sk, dst);
sk->sk_route_caps = dst->dev->features;
if (sk->sk_route_caps & NETIF_F_GSO)
sk->sk_route_caps |= NETIF_F_GSO_SOFTWARE;
@@ -1815,7 +1785,7 @@ static long sock_wait_for_wmem(struct sock *sk, long timeo)
{
DEFINE_WAIT(wait);
- clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+ sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk);
for (;;) {
if (!timeo)
break;
@@ -1861,7 +1831,7 @@ struct sk_buff *sock_alloc_send_pskb(struct sock *sk, unsigned long header_len,
if (sk_wmem_alloc_get(sk) < sk->sk_sndbuf)
break;
- set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+ sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
err = -EAGAIN;
if (!timeo)
@@ -2048,9 +2018,9 @@ int sk_wait_data(struct sock *sk, long *timeo, const struct sk_buff *skb)
DEFINE_WAIT(wait);
prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
- set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+ sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);
rc = sk_wait_event(sk, timeo, skb_peek_tail(&sk->sk_receive_queue) != skb);
- clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+ sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk);
finish_wait(sk_sleep(sk), &wait);
return rc;
}
@@ -2071,27 +2041,27 @@ int __sk_mem_schedule(struct sock *sk, int size, int kind)
struct proto *prot = sk->sk_prot;
int amt = sk_mem_pages(size);
long allocated;
- int parent_status = UNDER_LIMIT;
sk->sk_forward_alloc += amt * SK_MEM_QUANTUM;
- allocated = sk_memory_allocated_add(sk, amt, &parent_status);
+ allocated = sk_memory_allocated_add(sk, amt);
+
+ if (mem_cgroup_sockets_enabled && sk->sk_memcg &&
+ !mem_cgroup_charge_skmem(sk->sk_memcg, amt))
+ goto suppress_allocation;
/* Under limit. */
- if (parent_status == UNDER_LIMIT &&
- allocated <= sk_prot_mem_limits(sk, 0)) {
+ if (allocated <= sk_prot_mem_limits(sk, 0)) {
sk_leave_memory_pressure(sk);
return 1;
}
- /* Under pressure. (we or our parents) */
- if ((parent_status > SOFT_LIMIT) ||
- allocated > sk_prot_mem_limits(sk, 1))
+ /* Under pressure. */
+ if (allocated > sk_prot_mem_limits(sk, 1))
sk_enter_memory_pressure(sk);
- /* Over hard limit (we or our parents) */
- if ((parent_status == OVER_LIMIT) ||
- (allocated > sk_prot_mem_limits(sk, 2)))
+ /* Over hard limit. */
+ if (allocated > sk_prot_mem_limits(sk, 2))
goto suppress_allocation;
/* guarantee minimum buffer size under pressure */
@@ -2140,6 +2110,9 @@ suppress_allocation:
sk_memory_allocated_sub(sk, amt);
+ if (mem_cgroup_sockets_enabled && sk->sk_memcg)
+ mem_cgroup_uncharge_skmem(sk->sk_memcg, amt);
+
return 0;
}
EXPORT_SYMBOL(__sk_mem_schedule);
@@ -2155,6 +2128,9 @@ void __sk_mem_reclaim(struct sock *sk, int amount)
sk_memory_allocated_sub(sk, amount);
sk->sk_forward_alloc -= amount << SK_MEM_QUANTUM_SHIFT;
+ if (mem_cgroup_sockets_enabled && sk->sk_memcg)
+ mem_cgroup_uncharge_skmem(sk->sk_memcg, amount);
+
if (sk_under_memory_pressure(sk) &&
(sk_memory_allocated(sk) < sk_prot_mem_limits(sk, 0)))
sk_leave_memory_pressure(sk);
@@ -2283,7 +2259,7 @@ static void sock_def_wakeup(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_all(&wq->wait);
rcu_read_unlock();
}
@@ -2294,7 +2270,7 @@ static void sock_def_error_report(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_poll(&wq->wait, POLLERR);
sk_wake_async(sk, SOCK_WAKE_IO, POLL_ERR);
rcu_read_unlock();
@@ -2306,7 +2282,7 @@ static void sock_def_readable(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait, POLLIN | POLLPRI |
POLLRDNORM | POLLRDBAND);
sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
@@ -2324,7 +2300,7 @@ static void sock_def_write_space(struct sock *sk)
*/
if ((atomic_read(&sk->sk_wmem_alloc) << 1) <= sk->sk_sndbuf) {
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait, POLLOUT |
POLLWRNORM | POLLWRBAND);
@@ -2388,7 +2364,6 @@ void sock_init_data(struct socket *sock, struct sock *sk)
} else
sk->sk_wq = NULL;
- spin_lock_init(&sk->sk_dst_lock);
rwlock_init(&sk->sk_callback_lock);
lockdep_set_class_and_name(&sk->sk_callback_lock,
af_callback_keys + sk->sk_family,
diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c
index 0c1d58d43f67..a996ce8c8fb2 100644
--- a/net/core/sock_diag.c
+++ b/net/core/sock_diag.c
@@ -214,7 +214,7 @@ void sock_diag_unregister(const struct sock_diag_handler *hnld)
}
EXPORT_SYMBOL_GPL(sock_diag_unregister);
-static int __sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+static int __sock_diag_cmd(struct sk_buff *skb, struct nlmsghdr *nlh)
{
int err;
struct sock_diag_req *req = nlmsg_data(nlh);
@@ -234,8 +234,12 @@ static int __sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
hndl = sock_diag_handlers[req->sdiag_family];
if (hndl == NULL)
err = -ENOENT;
- else
+ else if (nlh->nlmsg_type == SOCK_DIAG_BY_FAMILY)
err = hndl->dump(skb, nlh);
+ else if (nlh->nlmsg_type == SOCK_DESTROY && hndl->destroy)
+ err = hndl->destroy(skb, nlh);
+ else
+ err = -EOPNOTSUPP;
mutex_unlock(&sock_diag_table_mutex);
return err;
@@ -261,7 +265,8 @@ static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
return ret;
case SOCK_DIAG_BY_FAMILY:
- return __sock_diag_rcv_msg(skb, nlh);
+ case SOCK_DESTROY:
+ return __sock_diag_cmd(skb, nlh);
default:
return -EINVAL;
}
@@ -295,6 +300,18 @@ static int sock_diag_bind(struct net *net, int group)
return 0;
}
+int sock_diag_destroy(struct sock *sk, int err)
+{
+ if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (!sk->sk_prot->diag_destroy)
+ return -EOPNOTSUPP;
+
+ return sk->sk_prot->diag_destroy(sk, err);
+}
+EXPORT_SYMBOL_GPL(sock_diag_destroy);
+
static int __net_init diag_net_init(struct net *net)
{
struct netlink_kernel_cfg cfg = {
diff --git a/net/core/sock_reuseport.c b/net/core/sock_reuseport.c
new file mode 100644
index 000000000000..1df98c557440
--- /dev/null
+++ b/net/core/sock_reuseport.c
@@ -0,0 +1,251 @@
+/*
+ * To speed up listener socket lookup, create an array to store all sockets
+ * listening on the same port. This allows a decision to be made after finding
+ * the first socket. An optional BPF program can also be configured for
+ * selecting the socket index from the array of available sockets.
+ */
+
+#include <net/sock_reuseport.h>
+#include <linux/bpf.h>
+#include <linux/rcupdate.h>
+
+#define INIT_SOCKS 128
+
+static DEFINE_SPINLOCK(reuseport_lock);
+
+static struct sock_reuseport *__reuseport_alloc(u16 max_socks)
+{
+ size_t size = sizeof(struct sock_reuseport) +
+ sizeof(struct sock *) * max_socks;
+ struct sock_reuseport *reuse = kzalloc(size, GFP_ATOMIC);
+
+ if (!reuse)
+ return NULL;
+
+ reuse->max_socks = max_socks;
+
+ RCU_INIT_POINTER(reuse->prog, NULL);
+ return reuse;
+}
+
+int reuseport_alloc(struct sock *sk)
+{
+ struct sock_reuseport *reuse;
+
+ /* bh lock used since this function call may precede hlist lock in
+ * soft irq of receive path or setsockopt from process context
+ */
+ spin_lock_bh(&reuseport_lock);
+ WARN_ONCE(rcu_dereference_protected(sk->sk_reuseport_cb,
+ lockdep_is_held(&reuseport_lock)),
+ "multiple allocations for the same socket");
+ reuse = __reuseport_alloc(INIT_SOCKS);
+ if (!reuse) {
+ spin_unlock_bh(&reuseport_lock);
+ return -ENOMEM;
+ }
+
+ reuse->socks[0] = sk;
+ reuse->num_socks = 1;
+ rcu_assign_pointer(sk->sk_reuseport_cb, reuse);
+
+ spin_unlock_bh(&reuseport_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(reuseport_alloc);
+
+static struct sock_reuseport *reuseport_grow(struct sock_reuseport *reuse)
+{
+ struct sock_reuseport *more_reuse;
+ u32 more_socks_size, i;
+
+ more_socks_size = reuse->max_socks * 2U;
+ if (more_socks_size > U16_MAX)
+ return NULL;
+
+ more_reuse = __reuseport_alloc(more_socks_size);
+ if (!more_reuse)
+ return NULL;
+
+ more_reuse->max_socks = more_socks_size;
+ more_reuse->num_socks = reuse->num_socks;
+ more_reuse->prog = reuse->prog;
+
+ memcpy(more_reuse->socks, reuse->socks,
+ reuse->num_socks * sizeof(struct sock *));
+
+ for (i = 0; i < reuse->num_socks; ++i)
+ rcu_assign_pointer(reuse->socks[i]->sk_reuseport_cb,
+ more_reuse);
+
+ /* Note: we use kfree_rcu here instead of reuseport_free_rcu so
+ * that reuse and more_reuse can temporarily share a reference
+ * to prog.
+ */
+ kfree_rcu(reuse, rcu);
+ return more_reuse;
+}
+
+/**
+ * reuseport_add_sock - Add a socket to the reuseport group of another.
+ * @sk: New socket to add to the group.
+ * @sk2: Socket belonging to the existing reuseport group.
+ * May return ENOMEM and not add socket to group under memory pressure.
+ */
+int reuseport_add_sock(struct sock *sk, const struct sock *sk2)
+{
+ struct sock_reuseport *reuse;
+
+ spin_lock_bh(&reuseport_lock);
+ reuse = rcu_dereference_protected(sk2->sk_reuseport_cb,
+ lockdep_is_held(&reuseport_lock)),
+ WARN_ONCE(rcu_dereference_protected(sk->sk_reuseport_cb,
+ lockdep_is_held(&reuseport_lock)),
+ "socket already in reuseport group");
+
+ if (reuse->num_socks == reuse->max_socks) {
+ reuse = reuseport_grow(reuse);
+ if (!reuse) {
+ spin_unlock_bh(&reuseport_lock);
+ return -ENOMEM;
+ }
+ }
+
+ reuse->socks[reuse->num_socks] = sk;
+ /* paired with smp_rmb() in reuseport_select_sock() */
+ smp_wmb();
+ reuse->num_socks++;
+ rcu_assign_pointer(sk->sk_reuseport_cb, reuse);
+
+ spin_unlock_bh(&reuseport_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(reuseport_add_sock);
+
+static void reuseport_free_rcu(struct rcu_head *head)
+{
+ struct sock_reuseport *reuse;
+
+ reuse = container_of(head, struct sock_reuseport, rcu);
+ if (reuse->prog)
+ bpf_prog_destroy(reuse->prog);
+ kfree(reuse);
+}
+
+void reuseport_detach_sock(struct sock *sk)
+{
+ struct sock_reuseport *reuse;
+ int i;
+
+ spin_lock_bh(&reuseport_lock);
+ reuse = rcu_dereference_protected(sk->sk_reuseport_cb,
+ lockdep_is_held(&reuseport_lock));
+ rcu_assign_pointer(sk->sk_reuseport_cb, NULL);
+
+ for (i = 0; i < reuse->num_socks; i++) {
+ if (reuse->socks[i] == sk) {
+ reuse->socks[i] = reuse->socks[reuse->num_socks - 1];
+ reuse->num_socks--;
+ if (reuse->num_socks == 0)
+ call_rcu(&reuse->rcu, reuseport_free_rcu);
+ break;
+ }
+ }
+ spin_unlock_bh(&reuseport_lock);
+}
+EXPORT_SYMBOL(reuseport_detach_sock);
+
+static struct sock *run_bpf(struct sock_reuseport *reuse, u16 socks,
+ struct bpf_prog *prog, struct sk_buff *skb,
+ int hdr_len)
+{
+ struct sk_buff *nskb = NULL;
+ u32 index;
+
+ if (skb_shared(skb)) {
+ nskb = skb_clone(skb, GFP_ATOMIC);
+ if (!nskb)
+ return NULL;
+ skb = nskb;
+ }
+
+ /* temporarily advance data past protocol header */
+ if (!pskb_pull(skb, hdr_len)) {
+ kfree_skb(nskb);
+ return NULL;
+ }
+ index = bpf_prog_run_save_cb(prog, skb);
+ __skb_push(skb, hdr_len);
+
+ consume_skb(nskb);
+
+ if (index >= socks)
+ return NULL;
+
+ return reuse->socks[index];
+}
+
+/**
+ * reuseport_select_sock - Select a socket from an SO_REUSEPORT group.
+ * @sk: First socket in the group.
+ * @hash: When no BPF filter is available, use this hash to select.
+ * @skb: skb to run through BPF filter.
+ * @hdr_len: BPF filter expects skb data pointer at payload data. If
+ * the skb does not yet point at the payload, this parameter represents
+ * how far the pointer needs to advance to reach the payload.
+ * Returns a socket that should receive the packet (or NULL on error).
+ */
+struct sock *reuseport_select_sock(struct sock *sk,
+ u32 hash,
+ struct sk_buff *skb,
+ int hdr_len)
+{
+ struct sock_reuseport *reuse;
+ struct bpf_prog *prog;
+ struct sock *sk2 = NULL;
+ u16 socks;
+
+ rcu_read_lock();
+ reuse = rcu_dereference(sk->sk_reuseport_cb);
+
+ /* if memory allocation failed or add call is not yet complete */
+ if (!reuse)
+ goto out;
+
+ prog = rcu_dereference(reuse->prog);
+ socks = READ_ONCE(reuse->num_socks);
+ if (likely(socks)) {
+ /* paired with smp_wmb() in reuseport_add_sock() */
+ smp_rmb();
+
+ if (prog && skb)
+ sk2 = run_bpf(reuse, socks, prog, skb, hdr_len);
+ else
+ sk2 = reuse->socks[reciprocal_scale(hash, socks)];
+ }
+
+out:
+ rcu_read_unlock();
+ return sk2;
+}
+EXPORT_SYMBOL(reuseport_select_sock);
+
+struct bpf_prog *
+reuseport_attach_prog(struct sock *sk, struct bpf_prog *prog)
+{
+ struct sock_reuseport *reuse;
+ struct bpf_prog *old_prog;
+
+ spin_lock_bh(&reuseport_lock);
+ reuse = rcu_dereference_protected(sk->sk_reuseport_cb,
+ lockdep_is_held(&reuseport_lock));
+ old_prog = rcu_dereference_protected(reuse->prog,
+ lockdep_is_held(&reuseport_lock));
+ rcu_assign_pointer(reuse->prog, prog);
+ spin_unlock_bh(&reuseport_lock);
+
+ return old_prog;
+}
+EXPORT_SYMBOL(reuseport_attach_prog);
diff --git a/net/core/stream.c b/net/core/stream.c
index d70f77a0c889..159516a11b7e 100644
--- a/net/core/stream.c
+++ b/net/core/stream.c
@@ -35,11 +35,11 @@ void sk_stream_write_space(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_poll(&wq->wait, POLLOUT |
POLLWRNORM | POLLWRBAND);
if (wq && wq->fasync_list && !(sk->sk_shutdown & SEND_SHUTDOWN))
- sock_wake_async(sock, SOCK_WAKE_SPACE, POLL_OUT);
+ sock_wake_async(wq, SOCK_WAKE_SPACE, POLL_OUT);
rcu_read_unlock();
}
}
@@ -126,7 +126,7 @@ int sk_stream_wait_memory(struct sock *sk, long *timeo_p)
current_timeo = vm_wait = (prandom_u32() % (HZ / 5)) + 2;
while (1) {
- set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+ sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
@@ -139,7 +139,7 @@ int sk_stream_wait_memory(struct sock *sk, long *timeo_p)
}
if (signal_pending(current))
goto do_interrupted;
- clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+ sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk);
if (sk_stream_memory_free(sk) && !vm_wait)
break;
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index db5fc2440a23..9c6d0508e63a 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -202,7 +202,9 @@ static int dccp_v6_send_response(const struct sock *sk, struct request_sock *req
security_req_classify_flow(req, flowi6_to_flowi(&fl6));
- final_p = fl6_update_dst(&fl6, np->opt, &final);
+ rcu_read_lock();
+ final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), &final);
+ rcu_read_unlock();
dst = ip6_dst_lookup_flow(sk, &fl6, final_p);
if (IS_ERR(dst)) {
@@ -219,7 +221,10 @@ static int dccp_v6_send_response(const struct sock *sk, struct request_sock *req
&ireq->ir_v6_loc_addr,
&ireq->ir_v6_rmt_addr);
fl6.daddr = ireq->ir_v6_rmt_addr;
- err = ip6_xmit(sk, skb, &fl6, np->opt, np->tclass);
+ rcu_read_lock();
+ err = ip6_xmit(sk, skb, &fl6, rcu_dereference(np->opt),
+ np->tclass);
+ rcu_read_unlock();
err = net_xmit_eval(err);
}
@@ -387,6 +392,7 @@ static struct sock *dccp_v6_request_recv_sock(const struct sock *sk,
struct inet_request_sock *ireq = inet_rsk(req);
struct ipv6_pinfo *newnp;
const struct ipv6_pinfo *np = inet6_sk(sk);
+ struct ipv6_txoptions *opt;
struct inet_sock *newinet;
struct dccp6_sock *newdp6;
struct sock *newsk;
@@ -453,7 +459,7 @@ static struct sock *dccp_v6_request_recv_sock(const struct sock *sk,
* comment in that function for the gory details. -acme
*/
- __ip6_dst_store(newsk, dst, NULL, NULL);
+ ip6_dst_store(newsk, dst, NULL, NULL);
newsk->sk_route_caps = dst->dev->features & ~(NETIF_F_IP_CSUM |
NETIF_F_TSO);
newdp6 = (struct dccp6_sock *)newsk;
@@ -488,13 +494,15 @@ static struct sock *dccp_v6_request_recv_sock(const struct sock *sk,
* Yes, keeping reference count would be much more clever, but we make
* one more one thing there: reattach optmem to newsk.
*/
- if (np->opt != NULL)
- newnp->opt = ipv6_dup_options(newsk, np->opt);
-
+ opt = rcu_dereference(np->opt);
+ if (opt) {
+ opt = ipv6_dup_options(newsk, opt);
+ RCU_INIT_POINTER(newnp->opt, opt);
+ }
inet_csk(newsk)->icsk_ext_hdr_len = 0;
- if (newnp->opt != NULL)
- inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen +
- newnp->opt->opt_flen);
+ if (opt)
+ inet_csk(newsk)->icsk_ext_hdr_len = opt->opt_nflen +
+ opt->opt_flen;
dccp_sync_mss(newsk, dst_mtu(dst));
@@ -757,6 +765,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
struct ipv6_pinfo *np = inet6_sk(sk);
struct dccp_sock *dp = dccp_sk(sk);
struct in6_addr *saddr = NULL, *final_p, final;
+ struct ipv6_txoptions *opt;
struct flowi6 fl6;
struct dst_entry *dst;
int addr_type;
@@ -856,7 +865,8 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
fl6.fl6_sport = inet->inet_sport;
security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
- final_p = fl6_update_dst(&fl6, np->opt, &final);
+ opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk));
+ final_p = fl6_update_dst(&fl6, opt, &final);
dst = ip6_dst_lookup_flow(sk, &fl6, final_p);
if (IS_ERR(dst)) {
@@ -873,12 +883,11 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
np->saddr = *saddr;
inet->inet_rcv_saddr = LOOPBACK4_IPV6;
- __ip6_dst_store(sk, dst, NULL, NULL);
+ ip6_dst_store(sk, dst, NULL, NULL);
icsk->icsk_ext_hdr_len = 0;
- if (np->opt != NULL)
- icsk->icsk_ext_hdr_len = (np->opt->opt_flen +
- np->opt->opt_nflen);
+ if (opt)
+ icsk->icsk_ext_hdr_len = opt->opt_flen + opt->opt_nflen;
inet->inet_dport = usin->sin6_port;
diff --git a/net/dccp/output.c b/net/dccp/output.c
index 4ce912e691d0..b66c84db0766 100644
--- a/net/dccp/output.c
+++ b/net/dccp/output.c
@@ -201,7 +201,7 @@ void dccp_write_space(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible(&wq->wait);
/* Should agree with poll, otherwise some programs break */
if (sock_writeable(sk))
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index b5cf13a28009..41e65804ddf5 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -339,8 +339,7 @@ unsigned int dccp_poll(struct file *file, struct socket *sock,
if (sk_stream_is_writeable(sk)) {
mask |= POLLOUT | POLLWRNORM;
} else { /* send SIGIO later */
- set_bit(SOCK_ASYNC_NOSPACE,
- &sk->sk_socket->flags);
+ sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
/* Race breaker. If space is freed after
diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c
index 675cf94e04f8..13d6b1a6e0fc 100644
--- a/net/decnet/af_decnet.c
+++ b/net/decnet/af_decnet.c
@@ -678,6 +678,9 @@ static int dn_create(struct net *net, struct socket *sock, int protocol,
{
struct sock *sk;
+ if (protocol < 0 || protocol > SK_PROTOCOL_MAX)
+ return -EINVAL;
+
if (!net_eq(net, &init_net))
return -EAFNOSUPPORT;
@@ -1747,9 +1750,9 @@ static int dn_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
}
prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
- set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+ sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);
sk_wait_event(sk, &timeo, dn_data_ready(sk, queue, flags, target));
- clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+ sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk);
finish_wait(sk_sleep(sk), &wait);
}
@@ -2004,10 +2007,10 @@ static int dn_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
}
prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
- set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+ sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);
sk_wait_event(sk, &timeo,
!dn_queue_too_long(scp, queue, flags));
- clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+ sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk);
finish_wait(sk_sleep(sk), &wait);
continue;
}
diff --git a/net/dns_resolver/dns_query.c b/net/dns_resolver/dns_query.c
index 4677b6fa6dda..ecc28cff08ab 100644
--- a/net/dns_resolver/dns_query.c
+++ b/net/dns_resolver/dns_query.c
@@ -67,7 +67,7 @@
* Returns the size of the result on success, -ve error code otherwise.
*/
int dns_query(const char *type, const char *name, size_t namelen,
- const char *options, char **_result, time_t *_expiry)
+ const char *options, char **_result, time64_t *_expiry)
{
struct key *rkey;
const struct user_key_payload *upayload;
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index 1eba07feb34a..fa4daba8db55 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -21,8 +21,10 @@
#include <linux/of_mdio.h>
#include <linux/of_platform.h>
#include <linux/of_net.h>
+#include <linux/of_gpio.h>
#include <linux/sysfs.h>
#include <linux/phy_fixed.h>
+#include <linux/gpio/consumer.h>
#include "dsa_priv.h"
char dsa_driver_version[] = "0.1";
@@ -437,7 +439,7 @@ static void dsa_switch_destroy(struct dsa_switch *ds)
if (of_phy_is_fixed_link(port_dn)) {
phydev = of_phy_find_device(port_dn);
if (phydev) {
- int addr = phydev->addr;
+ int addr = phydev->mdio.addr;
phy_device_free(phydev);
of_node_put(port_dn);
@@ -454,8 +456,7 @@ static void dsa_switch_destroy(struct dsa_switch *ds)
if (!ds->ports[port])
continue;
- unregister_netdev(ds->ports[port]);
- free_netdev(ds->ports[port]);
+ dsa_slave_destroy(ds->ports[port]);
}
mdiobus_unregister(ds->slave_mii_bus);
@@ -506,33 +507,6 @@ static int dsa_switch_resume(struct dsa_switch *ds)
}
#endif
-
-/* link polling *************************************************************/
-static void dsa_link_poll_work(struct work_struct *ugly)
-{
- struct dsa_switch_tree *dst;
- int i;
-
- dst = container_of(ugly, struct dsa_switch_tree, link_poll_work);
-
- for (i = 0; i < dst->pd->nr_chips; i++) {
- struct dsa_switch *ds = dst->ds[i];
-
- if (ds != NULL && ds->drv->poll_link != NULL)
- ds->drv->poll_link(ds);
- }
-
- mod_timer(&dst->link_poll_timer, round_jiffies(jiffies + HZ));
-}
-
-static void dsa_link_poll_timer(unsigned long _dst)
-{
- struct dsa_switch_tree *dst = (void *)_dst;
-
- schedule_work(&dst->link_poll_work);
-}
-
-
/* platform driver init and cleanup *****************************************/
static int dev_is_class(struct device *dev, void *class)
{
@@ -688,6 +662,9 @@ static int dsa_of_probe(struct device *dev)
const char *port_name;
int chip_index, port_index;
const unsigned int *sw_addr, *port_reg;
+ int gpio;
+ enum of_gpio_flags of_flags;
+ unsigned long flags;
u32 eeprom_len;
int ret;
@@ -766,6 +743,19 @@ static int dsa_of_probe(struct device *dev)
put_device(cd->host_dev);
cd->host_dev = &mdio_bus_switch->dev;
}
+ gpio = of_get_named_gpio_flags(child, "reset-gpios", 0,
+ &of_flags);
+ if (gpio_is_valid(gpio)) {
+ flags = (of_flags == OF_GPIO_ACTIVE_LOW ?
+ GPIOF_ACTIVE_LOW : 0);
+ ret = devm_gpio_request_one(dev, gpio, flags,
+ "switch_reset");
+ if (ret)
+ goto out_free_chip;
+
+ cd->reset = gpio_to_desc(gpio);
+ gpiod_direction_output(cd->reset, 0);
+ }
for_each_available_child_of_node(child, port) {
port_reg = of_get_property(port, "reg", NULL);
@@ -859,8 +849,6 @@ static int dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev,
}
dst->ds[i] = ds;
- if (ds->drv->poll_link != NULL)
- dst->link_poll_needed = 1;
++configured;
}
@@ -879,15 +867,6 @@ static int dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev,
wmb();
dev->dsa_ptr = (void *)dst;
- if (dst->link_poll_needed) {
- INIT_WORK(&dst->link_poll_work, dsa_link_poll_work);
- init_timer(&dst->link_poll_timer);
- dst->link_poll_timer.data = (unsigned long)dst;
- dst->link_poll_timer.function = dsa_link_poll_timer;
- dst->link_poll_timer.expires = round_jiffies(jiffies + HZ);
- add_timer(&dst->link_poll_timer);
- }
-
return 0;
}
@@ -939,8 +918,10 @@ static int dsa_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dst);
ret = dsa_setup_dst(dst, dev, &pdev->dev, pd);
- if (ret)
+ if (ret) {
+ dev_put(dev);
goto out;
+ }
return 0;
@@ -954,17 +935,14 @@ static void dsa_remove_dst(struct dsa_switch_tree *dst)
{
int i;
- if (dst->link_poll_needed)
- del_timer_sync(&dst->link_poll_timer);
-
- flush_work(&dst->link_poll_work);
-
for (i = 0; i < dst->pd->nr_chips; i++) {
struct dsa_switch *ds = dst->ds[i];
if (ds)
dsa_switch_destroy(ds);
}
+
+ dev_put(dst->master_netdev);
}
static int dsa_remove(struct platform_device *pdev)
@@ -1010,6 +988,14 @@ static int dsa_suspend(struct device *d)
struct dsa_switch_tree *dst = platform_get_drvdata(pdev);
int i, ret = 0;
+ dst->master_netdev->dsa_ptr = NULL;
+
+ /* If we used a tagging format that doesn't have an ethertype
+ * field, make sure that all packets from this point get sent
+ * without the tag and go through the regular receive path.
+ */
+ wmb();
+
for (i = 0; i < dst->pd->nr_chips; i++) {
struct dsa_switch *ds = dst->ds[i];
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index 311796c809af..1d1a54687e4a 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -61,6 +61,7 @@ extern const struct dsa_device_ops notag_netdev_ops;
void dsa_slave_mii_bus_init(struct dsa_switch *ds);
int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
int port, char *name);
+void dsa_slave_destroy(struct net_device *slave_dev);
int dsa_slave_suspend(struct net_device *slave_dev);
int dsa_slave_resume(struct net_device *slave_dev);
int dsa_slave_netdevice_event(struct notifier_block *unused,
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 7bc787b095c8..40b9ca72aae3 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -15,6 +15,7 @@
#include <linux/phy_fixed.h>
#include <linux/of_net.h>
#include <linux/of_mdio.h>
+#include <linux/mdio.h>
#include <net/rtnetlink.h>
#include <net/switchdev.h>
#include <linux/if_bridge.h>
@@ -997,7 +998,7 @@ static int dsa_slave_phy_connect(struct dsa_slave_priv *p,
{
struct dsa_switch *ds = p->parent;
- p->phy = ds->slave_mii_bus->phy_map[addr];
+ p->phy = mdiobus_get_phy(ds->slave_mii_bus, addr);
if (!p->phy) {
netdev_err(slave_dev, "no phy at %d\n", addr);
return -ENODEV;
@@ -1080,11 +1081,10 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p,
netdev_err(slave_dev, "failed to connect to port %d: %d\n", p->port, ret);
return ret;
}
- } else {
- netdev_info(slave_dev, "attached PHY at address %d [%s]\n",
- p->phy->addr, p->phy->drv->name);
}
+ phy_attached_info(p->phy);
+
return 0;
}
@@ -1189,13 +1189,6 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
p->old_link = -1;
p->old_duplex = -1;
- ret = dsa_slave_phy_setup(p, slave_dev);
- if (ret) {
- netdev_err(master, "error %d setting up slave phy\n", ret);
- free_netdev(slave_dev);
- return ret;
- }
-
ds->ports[port] = slave_dev;
ret = register_netdev(slave_dev);
if (ret) {
@@ -1209,9 +1202,27 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
netif_carrier_off(slave_dev);
+ ret = dsa_slave_phy_setup(p, slave_dev);
+ if (ret) {
+ netdev_err(master, "error %d setting up slave phy\n", ret);
+ free_netdev(slave_dev);
+ return ret;
+ }
+
return 0;
}
+void dsa_slave_destroy(struct net_device *slave_dev)
+{
+ struct dsa_slave_priv *p = netdev_priv(slave_dev);
+
+ netif_carrier_off(slave_dev);
+ if (p->phy)
+ phy_disconnect(p->phy);
+ unregister_netdev(slave_dev);
+ free_netdev(slave_dev);
+}
+
static bool dsa_slave_dev_check(struct net_device *dev)
{
return dev->netdev_ops == &dsa_slave_netdev_ops;
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index 9e63f252a89e..103871784e50 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -52,6 +52,8 @@
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/if_ether.h>
+#include <linux/of_net.h>
+#include <linux/pci.h>
#include <net/dst.h>
#include <net/arp.h>
#include <net/sock.h>
@@ -485,3 +487,32 @@ static int __init eth_offload_init(void)
}
fs_initcall(eth_offload_init);
+
+unsigned char * __weak arch_get_platform_mac_address(void)
+{
+ return NULL;
+}
+
+int eth_platform_get_mac_address(struct device *dev, u8 *mac_addr)
+{
+ const unsigned char *addr;
+ struct device_node *dp;
+
+ if (dev_is_pci(dev))
+ dp = pci_device_to_OF_node(to_pci_dev(dev));
+ else
+ dp = dev->of_node;
+
+ addr = NULL;
+ if (dp)
+ addr = of_get_mac_address(dp);
+ if (!addr)
+ addr = arch_get_platform_mac_address();
+
+ if (!addr)
+ return -ENODEV;
+
+ ether_addr_copy(mac_addr, addr);
+ return 0;
+}
+EXPORT_SYMBOL(eth_platform_get_mac_address);
diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c
index 35a9788bb3ae..c7d1adca30d8 100644
--- a/net/hsr/hsr_device.c
+++ b/net/hsr/hsr_device.c
@@ -312,7 +312,7 @@ static void send_hsr_supervision_frame(struct hsr_port *master, u8 type)
return;
out:
- WARN_ON_ONCE("HSR: Could not send supervision frame\n");
+ WARN_ONCE(1, "HSR: Could not send supervision frame\n");
kfree_skb(skb);
}
diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c
index 20c49c724ba0..737c87a2a41e 100644
--- a/net/ieee802154/6lowpan/core.c
+++ b/net/ieee802154/6lowpan/core.c
@@ -161,9 +161,7 @@ static int lowpan_newlink(struct net *src_net, struct net_device *ldev,
wdev->needed_headroom;
ldev->needed_tailroom = wdev->needed_tailroom;
- lowpan_netdev_setup(ldev, LOWPAN_LLTYPE_IEEE802154);
-
- ret = register_netdevice(ldev);
+ ret = lowpan_register_netdevice(ldev, LOWPAN_LLTYPE_IEEE802154);
if (ret < 0) {
dev_put(wdev);
return ret;
@@ -180,7 +178,7 @@ static void lowpan_dellink(struct net_device *ldev, struct list_head *head)
ASSERT_RTNL();
wdev->ieee802154_ptr->lowpan_dev = NULL;
- unregister_netdevice(ldev);
+ lowpan_unregister_netdevice(ldev);
dev_put(wdev);
}
diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c
index 6b437e8760d3..30d875dff6b5 100644
--- a/net/ieee802154/6lowpan/reassembly.c
+++ b/net/ieee802154/6lowpan/reassembly.c
@@ -624,7 +624,6 @@ int __init lowpan_net_frag_init(void)
lowpan_frags.hashfn = lowpan_hashfn;
lowpan_frags.constructor = lowpan_frag_init;
lowpan_frags.destructor = NULL;
- lowpan_frags.skb_free = NULL;
lowpan_frags.qsize = sizeof(struct frag_queue);
lowpan_frags.match = lowpan_frag_match;
lowpan_frags.frag_expire = lowpan_frag_expire;
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index 416dfa004cfb..c22920525e5d 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -436,6 +436,19 @@ config INET_UDP_DIAG
Support for UDP socket monitoring interface used by the ss tool.
If unsure, say Y.
+config INET_DIAG_DESTROY
+ bool "INET: allow privileged process to administratively close sockets"
+ depends on INET_DIAG
+ default n
+ ---help---
+ Provides a SOCK_DESTROY operation that allows privileged processes
+ (e.g., a connection manager or a network administration tool such as
+ ss) to close sockets opened by other processes. Closing a socket in
+ this way interrupts any blocking read/write/connect operations on
+ the socket and causes future socket calls to behave as if the socket
+ had been disconnected.
+ If unsure, say N.
+
menuconfig TCP_CONG_ADVANCED
bool "TCP: advanced congestion control"
---help---
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 11c4ca13ec3b..5c5db6636704 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -257,6 +257,9 @@ static int inet_create(struct net *net, struct socket *sock, int protocol,
int try_loading_module = 0;
int err;
+ if (protocol < 0 || protocol >= IPPROTO_MAX)
+ return -EINVAL;
+
sock->state = SS_UNCONNECTED;
/* Look for the requested type/protocol pair. */
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index cc8f3e506cde..473447593060 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -1155,6 +1155,7 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event,
static int fib_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct netdev_notifier_changeupper_info *info;
struct in_device *in_dev;
struct net *net = dev_net(dev);
unsigned int flags;
@@ -1193,6 +1194,14 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo
case NETDEV_CHANGEMTU:
rt_cache_flush(net);
break;
+ case NETDEV_CHANGEUPPER:
+ info = ptr;
+ /* flush all routes if dev is linked to or unlinked from
+ * an L3 master device (e.g., VRF)
+ */
+ if (info->upper_dev && netif_is_l3_master(info->upper_dev))
+ fib_disable_ip(dev, NETDEV_DOWN, true);
+ break;
}
return NOTIFY_DONE;
}
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c
index e0fcbbbcfe54..976f0dcf6991 100644
--- a/net/ipv4/fou.c
+++ b/net/ipv4/fou.c
@@ -24,6 +24,7 @@ struct fou {
u16 type;
struct udp_offload udp_offloads;
struct list_head list;
+ struct rcu_head rcu;
};
#define FOU_F_REMCSUM_NOPARTIAL BIT(0)
@@ -417,7 +418,7 @@ static void fou_release(struct fou *fou)
list_del(&fou->list);
udp_tunnel_sock_release(sock);
- kfree(fou);
+ kfree_rcu(fou, rcu);
}
static int fou_encap_init(struct sock *sk, struct fou *fou, struct fou_cfg *cfg)
@@ -497,7 +498,7 @@ static int fou_create(struct net *net, struct fou_cfg *cfg,
sk->sk_allocation = GFP_ATOMIC;
if (cfg->udp_config.family == AF_INET) {
- err = udp_add_offload(&fou->udp_offloads);
+ err = udp_add_offload(net, &fou->udp_offloads);
if (err)
goto error;
}
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 6baf36e11808..05e4cba14162 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -2126,7 +2126,7 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
ASSERT_RTNL();
in_dev = ip_mc_find_dev(net, imr);
- if (!in_dev) {
+ if (!imr->imr_ifindex && !imr->imr_address.s_addr && !in_dev) {
ret = -ENODEV;
goto out;
}
@@ -2147,7 +2147,8 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
*imlp = iml->next_rcu;
- ip_mc_dec_group(in_dev, group);
+ if (in_dev)
+ ip_mc_dec_group(in_dev, group);
/* decrease mem now to avoid the memleak warning */
atomic_sub(sizeof(*iml), &sk->sk_omem_alloc);
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
index ab9f8a66615d..8bb8e7ad8548 100644
--- a/net/ipv4/inet_diag.c
+++ b/net/ipv4/inet_diag.c
@@ -350,17 +350,12 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb,
nlmsg_flags, unlh);
}
-int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo,
- struct sk_buff *in_skb,
- const struct nlmsghdr *nlh,
- const struct inet_diag_req_v2 *req)
+struct sock *inet_diag_find_one_icsk(struct net *net,
+ struct inet_hashinfo *hashinfo,
+ const struct inet_diag_req_v2 *req)
{
- struct net *net = sock_net(in_skb->sk);
- struct sk_buff *rep;
struct sock *sk;
- int err;
- err = -EINVAL;
if (req->sdiag_family == AF_INET)
sk = inet_lookup(net, hashinfo, req->id.idiag_dst[0],
req->id.idiag_dport, req->id.idiag_src[0],
@@ -375,15 +370,33 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo,
req->id.idiag_if);
#endif
else
- goto out_nosk;
+ return ERR_PTR(-EINVAL);
- err = -ENOENT;
if (!sk)
- goto out_nosk;
+ return ERR_PTR(-ENOENT);
- err = sock_diag_check_cookie(sk, req->id.idiag_cookie);
- if (err)
- goto out;
+ if (sock_diag_check_cookie(sk, req->id.idiag_cookie)) {
+ sock_gen_put(sk);
+ return ERR_PTR(-ENOENT);
+ }
+
+ return sk;
+}
+EXPORT_SYMBOL_GPL(inet_diag_find_one_icsk);
+
+int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo,
+ struct sk_buff *in_skb,
+ const struct nlmsghdr *nlh,
+ const struct inet_diag_req_v2 *req)
+{
+ struct net *net = sock_net(in_skb->sk);
+ struct sk_buff *rep;
+ struct sock *sk;
+ int err;
+
+ sk = inet_diag_find_one_icsk(net, hashinfo, req);
+ if (IS_ERR(sk))
+ return PTR_ERR(sk);
rep = nlmsg_new(inet_sk_attr_size(), GFP_KERNEL);
if (!rep) {
@@ -409,12 +422,11 @@ out:
if (sk)
sock_gen_put(sk);
-out_nosk:
return err;
}
EXPORT_SYMBOL_GPL(inet_diag_dump_one_icsk);
-static int inet_diag_get_exact(struct sk_buff *in_skb,
+static int inet_diag_cmd_exact(int cmd, struct sk_buff *in_skb,
const struct nlmsghdr *nlh,
const struct inet_diag_req_v2 *req)
{
@@ -424,8 +436,12 @@ static int inet_diag_get_exact(struct sk_buff *in_skb,
handler = inet_diag_lock_handler(req->sdiag_protocol);
if (IS_ERR(handler))
err = PTR_ERR(handler);
- else
+ else if (cmd == SOCK_DIAG_BY_FAMILY)
err = handler->dump_one(in_skb, nlh, req);
+ else if (cmd == SOCK_DESTROY && handler->destroy)
+ err = handler->destroy(in_skb, req);
+ else
+ err = -EOPNOTSUPP;
inet_diag_unlock_handler(handler);
return err;
@@ -938,7 +954,7 @@ static int inet_diag_get_exact_compat(struct sk_buff *in_skb,
req.idiag_states = rc->idiag_states;
req.id = rc->id;
- return inet_diag_get_exact(in_skb, nlh, &req);
+ return inet_diag_cmd_exact(SOCK_DIAG_BY_FAMILY, in_skb, nlh, &req);
}
static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh)
@@ -972,7 +988,7 @@ static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh)
return inet_diag_get_exact_compat(skb, nlh);
}
-static int inet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
+static int inet_diag_handler_cmd(struct sk_buff *skb, struct nlmsghdr *h)
{
int hdrlen = sizeof(struct inet_diag_req_v2);
struct net *net = sock_net(skb->sk);
@@ -980,7 +996,8 @@ static int inet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
if (nlmsg_len(h) < hdrlen)
return -EINVAL;
- if (h->nlmsg_flags & NLM_F_DUMP) {
+ if (h->nlmsg_type == SOCK_DIAG_BY_FAMILY &&
+ h->nlmsg_flags & NLM_F_DUMP) {
if (nlmsg_attrlen(h, hdrlen)) {
struct nlattr *attr;
@@ -999,7 +1016,7 @@ static int inet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
}
}
- return inet_diag_get_exact(skb, h, nlmsg_data(h));
+ return inet_diag_cmd_exact(h->nlmsg_type, skb, h, nlmsg_data(h));
}
static
@@ -1050,14 +1067,16 @@ int inet_diag_handler_get_info(struct sk_buff *skb, struct sock *sk)
static const struct sock_diag_handler inet_diag_handler = {
.family = AF_INET,
- .dump = inet_diag_handler_dump,
+ .dump = inet_diag_handler_cmd,
.get_info = inet_diag_handler_get_info,
+ .destroy = inet_diag_handler_cmd,
};
static const struct sock_diag_handler inet6_diag_handler = {
.family = AF_INET6,
- .dump = inet_diag_handler_dump,
+ .dump = inet_diag_handler_cmd,
.get_info = inet_diag_handler_get_info,
+ .destroy = inet_diag_handler_cmd,
};
int inet_diag_register(const struct inet_diag_handler *h)
diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c
index fe144dae7372..3a88b0c73797 100644
--- a/net/ipv4/inet_fragment.c
+++ b/net/ipv4/inet_fragment.c
@@ -285,14 +285,6 @@ void inet_frag_kill(struct inet_frag_queue *fq, struct inet_frags *f)
}
EXPORT_SYMBOL(inet_frag_kill);
-static inline void frag_kfree_skb(struct netns_frags *nf, struct inet_frags *f,
- struct sk_buff *skb)
-{
- if (f->skb_free)
- f->skb_free(skb);
- kfree_skb(skb);
-}
-
void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f)
{
struct sk_buff *fp;
@@ -309,7 +301,7 @@ void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f)
struct sk_buff *xp = fp->next;
sum_truesize += fp->truesize;
- frag_kfree_skb(nf, f, fp);
+ kfree_skb(fp);
fp = xp;
}
sum = sum_truesize + f->qsize;
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index 1fe55ae81781..3f00810b7288 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -891,7 +891,6 @@ void __init ipfrag_init(void)
ip4_frags.hashfn = ip4_hashfn;
ip4_frags.constructor = ip4_frag_init;
ip4_frags.destructor = ip4_frag_free;
- ip4_frags.skb_free = NULL;
ip4_frags.qsize = sizeof(struct ipq);
ip4_frags.match = ip4_frag_match;
ip4_frags.frag_expire = ip_expire;
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 614521437e30..7c51c4e1661f 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -24,7 +24,6 @@
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/if_arp.h>
-#include <linux/mroute.h>
#include <linux/if_vlan.h>
#include <linux/init.h>
#include <linux/in6.h>
@@ -562,10 +561,9 @@ static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev)
tunnel_id_to_key(tun_info->key.tun_id), 0);
df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0;
- err = iptunnel_xmit(skb->sk, rt, skb, fl.saddr,
- key->u.ipv4.dst, IPPROTO_GRE,
- key->tos, key->ttl, df, false);
- iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
+
+ iptunnel_xmit(skb->sk, rt, skb, fl.saddr, key->u.ipv4.dst, IPPROTO_GRE,
+ key->tos, key->ttl, df, false);
return;
err_free_rt:
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 4233cbe47052..512a44778cf2 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -76,7 +76,6 @@
#include <linux/igmp.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_bridge.h>
-#include <linux/mroute.h>
#include <linux/netlink.h>
#include <linux/tcp.h>
@@ -912,7 +911,7 @@ static int __ip_append_data(struct sock *sk,
*/
if (transhdrlen &&
length + fragheaderlen <= mtu &&
- rt->dst.dev->features & NETIF_F_V4_CSUM &&
+ rt->dst.dev->features & (NETIF_F_HW_CSUM | NETIF_F_IP_CSUM) &&
!(flags & MSG_MORE) &&
!exthdrlen)
csummode = CHECKSUM_PARTIAL;
@@ -921,7 +920,7 @@ static int __ip_append_data(struct sock *sk,
if (((length > mtu) || (skb && skb_is_gso(skb))) &&
(sk->sk_protocol == IPPROTO_UDP) &&
(rt->dst.dev->features & NETIF_F_UFO) && !rt->dst.header_len &&
- (sk->sk_type == SOCK_DGRAM)) {
+ (sk->sk_type == SOCK_DGRAM) && !sk->sk_no_check_tx) {
err = ip_ufo_append_data(sk, queue, getfrag, from, length,
hh_len, fragheaderlen, transhdrlen,
maxfraglen, flags);
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index cbb51f3fac06..c7bd72e9b544 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -30,7 +30,6 @@
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/if_arp.h>
-#include <linux/mroute.h>
#include <linux/init.h>
#include <linux/in6.h>
#include <linux/inetdevice.h>
@@ -657,7 +656,6 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
struct rtable *rt; /* Route to the other host */
unsigned int max_headroom; /* The extra header space needed */
__be32 dst;
- int err;
bool connected;
inner_iph = (const struct iphdr *)skb_inner_network_header(skb);
@@ -795,10 +793,8 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
return;
}
- err = iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, protocol,
- tos, ttl, df, !net_eq(tunnel->net, dev_net(dev)));
- iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
-
+ iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, protocol, tos, ttl,
+ df, !net_eq(tunnel->net, dev_net(dev)));
return;
#if IS_ENABLED(CONFIG_IPV6)
diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c
index 6cb9009c3d96..859d415c0b2d 100644
--- a/net/ipv4/ip_tunnel_core.c
+++ b/net/ipv4/ip_tunnel_core.c
@@ -24,7 +24,6 @@
#include <linux/netdevice.h>
#include <linux/in.h>
#include <linux/if_arp.h>
-#include <linux/mroute.h>
#include <linux/init.h>
#include <linux/in6.h>
#include <linux/inetdevice.h>
@@ -48,12 +47,13 @@
#include <net/rtnetlink.h>
#include <net/dst_metadata.h>
-int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
- __be32 src, __be32 dst, __u8 proto,
- __u8 tos, __u8 ttl, __be16 df, bool xnet)
+void iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
+ __be32 src, __be32 dst, __u8 proto,
+ __u8 tos, __u8 ttl, __be16 df, bool xnet)
{
int pkt_len = skb->len - skb_inner_network_offset(skb);
struct net *net = dev_net(rt->dst.dev);
+ struct net_device *dev = skb->dev;
struct iphdr *iph;
int err;
@@ -82,7 +82,7 @@ int iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
err = ip_local_out(net, sk, skb);
if (unlikely(net_xmit_eval(err)))
pkt_len = 0;
- return pkt_len;
+ iptunnel_xmit_stats(dev, pkt_len);
}
EXPORT_SYMBOL_GPL(iptunnel_xmit);
@@ -251,7 +251,7 @@ static int ip_tun_build_state(struct net_device *dev, struct nlattr *attr,
tun_info = lwt_tun_info(new_state);
if (tb[LWTUNNEL_IP_ID])
- tun_info->key.tun_id = nla_get_u64(tb[LWTUNNEL_IP_ID]);
+ tun_info->key.tun_id = nla_get_be64(tb[LWTUNNEL_IP_ID]);
if (tb[LWTUNNEL_IP_DST])
tun_info->key.u.ipv4.dst = nla_get_be32(tb[LWTUNNEL_IP_DST]);
@@ -266,7 +266,7 @@ static int ip_tun_build_state(struct net_device *dev, struct nlattr *attr,
tun_info->key.tos = nla_get_u8(tb[LWTUNNEL_IP_TOS]);
if (tb[LWTUNNEL_IP_FLAGS])
- tun_info->key.tun_flags = nla_get_u16(tb[LWTUNNEL_IP_FLAGS]);
+ tun_info->key.tun_flags = nla_get_be16(tb[LWTUNNEL_IP_FLAGS]);
tun_info->mode = IP_TUNNEL_INFO_TX;
tun_info->options_len = 0;
@@ -281,12 +281,12 @@ static int ip_tun_fill_encap_info(struct sk_buff *skb,
{
struct ip_tunnel_info *tun_info = lwt_tun_info(lwtstate);
- if (nla_put_u64(skb, LWTUNNEL_IP_ID, tun_info->key.tun_id) ||
+ if (nla_put_be64(skb, LWTUNNEL_IP_ID, tun_info->key.tun_id) ||
nla_put_be32(skb, LWTUNNEL_IP_DST, tun_info->key.u.ipv4.dst) ||
nla_put_be32(skb, LWTUNNEL_IP_SRC, tun_info->key.u.ipv4.src) ||
nla_put_u8(skb, LWTUNNEL_IP_TOS, tun_info->key.tos) ||
nla_put_u8(skb, LWTUNNEL_IP_TTL, tun_info->key.ttl) ||
- nla_put_u16(skb, LWTUNNEL_IP_FLAGS, tun_info->key.tun_flags))
+ nla_put_be16(skb, LWTUNNEL_IP_FLAGS, tun_info->key.tun_flags))
return -ENOMEM;
return 0;
@@ -346,7 +346,7 @@ static int ip6_tun_build_state(struct net_device *dev, struct nlattr *attr,
tun_info = lwt_tun_info(new_state);
if (tb[LWTUNNEL_IP6_ID])
- tun_info->key.tun_id = nla_get_u64(tb[LWTUNNEL_IP6_ID]);
+ tun_info->key.tun_id = nla_get_be64(tb[LWTUNNEL_IP6_ID]);
if (tb[LWTUNNEL_IP6_DST])
tun_info->key.u.ipv6.dst = nla_get_in6_addr(tb[LWTUNNEL_IP6_DST]);
@@ -361,7 +361,7 @@ static int ip6_tun_build_state(struct net_device *dev, struct nlattr *attr,
tun_info->key.tos = nla_get_u8(tb[LWTUNNEL_IP6_TC]);
if (tb[LWTUNNEL_IP6_FLAGS])
- tun_info->key.tun_flags = nla_get_u16(tb[LWTUNNEL_IP6_FLAGS]);
+ tun_info->key.tun_flags = nla_get_be16(tb[LWTUNNEL_IP6_FLAGS]);
tun_info->mode = IP_TUNNEL_INFO_TX | IP_TUNNEL_INFO_IPV6;
tun_info->options_len = 0;
@@ -376,12 +376,12 @@ static int ip6_tun_fill_encap_info(struct sk_buff *skb,
{
struct ip_tunnel_info *tun_info = lwt_tun_info(lwtstate);
- if (nla_put_u64(skb, LWTUNNEL_IP6_ID, tun_info->key.tun_id) ||
+ if (nla_put_be64(skb, LWTUNNEL_IP6_ID, tun_info->key.tun_id) ||
nla_put_in6_addr(skb, LWTUNNEL_IP6_DST, &tun_info->key.u.ipv6.dst) ||
nla_put_in6_addr(skb, LWTUNNEL_IP6_SRC, &tun_info->key.u.ipv6.src) ||
nla_put_u8(skb, LWTUNNEL_IP6_HOPLIMIT, tun_info->key.tos) ||
nla_put_u8(skb, LWTUNNEL_IP6_TC, tun_info->key.ttl) ||
- nla_put_u16(skb, LWTUNNEL_IP6_FLAGS, tun_info->key.tun_flags))
+ nla_put_be16(skb, LWTUNNEL_IP6_FLAGS, tun_info->key.tun_flags))
return -ENOMEM;
return 0;
diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c
index 4d8f0b698777..5cf10b777b7e 100644
--- a/net/ipv4/ip_vti.c
+++ b/net/ipv4/ip_vti.c
@@ -30,7 +30,6 @@
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/if_arp.h>
-#include <linux/mroute.h>
#include <linux/init.h>
#include <linux/netfilter_ipv4.h>
#include <linux/if_ether.h>
@@ -200,7 +199,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev,
err = dst_output(tunnel->net, skb->sk, skb);
if (net_xmit_eval(err) == 0)
err = skb->len;
- iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
+ iptunnel_xmit_stats(dev, err);
return NETDEV_TX_OK;
tx_error_icmp:
diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c
index 0bc7412d9e14..67f7c9de0b16 100644
--- a/net/ipv4/ipconfig.c
+++ b/net/ipv4/ipconfig.c
@@ -65,15 +65,6 @@
#include <net/checksum.h>
#include <asm/processor.h>
-/* Define this to allow debugging output */
-#undef IPCONFIG_DEBUG
-
-#ifdef IPCONFIG_DEBUG
-#define DBG(x) printk x
-#else
-#define DBG(x) do { } while(0)
-#endif
-
#if defined(CONFIG_IP_PNP_DHCP)
#define IPCONFIG_DHCP
#endif
@@ -227,7 +218,7 @@ static int __init ic_open_devs(void)
if (dev->mtu >= 364)
able |= IC_BOOTP;
else
- pr_warn("DHCP/BOOTP: Ignoring device %s, MTU %d too small",
+ pr_warn("DHCP/BOOTP: Ignoring device %s, MTU %d too small\n",
dev->name, dev->mtu);
if (!(dev->flags & IFF_NOARP))
able |= IC_RARP;
@@ -254,8 +245,8 @@ static int __init ic_open_devs(void)
else
d->xid = 0;
ic_proto_have_if |= able;
- DBG(("IP-Config: %s UP (able=%d, xid=%08x)\n",
- dev->name, able, d->xid));
+ pr_debug("IP-Config: %s UP (able=%d, xid=%08x)\n",
+ dev->name, able, d->xid);
}
}
@@ -311,7 +302,7 @@ static void __init ic_close_devs(void)
next = d->next;
dev = d->dev;
if (dev != ic_dev && !netdev_uses_dsa(dev)) {
- DBG(("IP-Config: Downing %s\n", dev->name));
+ pr_debug("IP-Config: Downing %s\n", dev->name);
dev_change_flags(dev, d->flags);
}
kfree(d);
@@ -464,7 +455,8 @@ static int __init ic_defaults(void)
&ic_myaddr);
return -1;
}
- printk("IP-Config: Guessing netmask %pI4\n", &ic_netmask);
+ pr_notice("IP-Config: Guessing netmask %pI4\n",
+ &ic_netmask);
}
return 0;
@@ -675,9 +667,7 @@ ic_dhcp_init_options(u8 *options)
u8 *e = options;
int len;
-#ifdef IPCONFIG_DEBUG
- printk("DHCP: Sending message type %d\n", mt);
-#endif
+ pr_debug("DHCP: Sending message type %d\n", mt);
memcpy(e, ic_bootp_cookie, 4); /* RFC1048 Magic Cookie */
e += 4;
@@ -847,7 +837,8 @@ static void __init ic_bootp_send_if(struct ic_device *d, unsigned long jiffies_d
else if (dev->type == ARPHRD_FDDI)
b->htype = ARPHRD_ETHER;
else {
- printk("Unknown ARP type 0x%04x for device %s\n", dev->type, dev->name);
+ pr_warn("Unknown ARP type 0x%04x for device %s\n", dev->type,
+ dev->name);
b->htype = dev->type; /* can cause undefined behavior */
}
@@ -904,14 +895,12 @@ static void __init ic_do_bootp_ext(u8 *ext)
int i;
__be16 mtu;
-#ifdef IPCONFIG_DEBUG
u8 *c;
- printk("DHCP/BOOTP: Got extension %d:",*ext);
+ pr_debug("DHCP/BOOTP: Got extension %d:", *ext);
for (c=ext+2; c<ext+2+ext[1]; c++)
- printk(" %02x", *c);
- printk("\n");
-#endif
+ pr_debug(" %02x", *c);
+ pr_debug("\n");
switch (*ext++) {
case 1: /* Subnet mask */
@@ -1080,9 +1069,7 @@ static int __init ic_bootp_recv(struct sk_buff *skb, struct net_device *dev, str
}
}
-#ifdef IPCONFIG_DEBUG
- printk("DHCP: Got message type %d\n", mt);
-#endif
+ pr_debug("DHCP: Got message type %d\n", mt);
switch (mt) {
case DHCPOFFER:
@@ -1095,10 +1082,8 @@ static int __init ic_bootp_recv(struct sk_buff *skb, struct net_device *dev, str
/* Let's accept that offer. */
ic_myaddr = b->your_ip;
ic_servaddr = server_id;
-#ifdef IPCONFIG_DEBUG
- printk("DHCP: Offered address %pI4 by server %pI4\n",
- &ic_myaddr, &b->iph.saddr);
-#endif
+ pr_debug("DHCP: Offered address %pI4 by server %pI4\n",
+ &ic_myaddr, &b->iph.saddr);
/* The DHCP indicated server address takes
* precedence over the bootp header one if
* they are different.
@@ -1295,11 +1280,10 @@ static int __init ic_dynamic(void)
return -1;
}
- printk("IP-Config: Got %s answer from %pI4, ",
+ pr_info("IP-Config: Got %s answer from %pI4, my address is %pI4\n",
((ic_got_reply & IC_RARP) ? "RARP"
- : (ic_proto_enabled & IC_USE_DHCP) ? "DHCP" : "BOOTP"),
- &ic_addrservaddr);
- pr_cont("my address is %pI4\n", &ic_myaddr);
+ : (ic_proto_enabled & IC_USE_DHCP) ? "DHCP" : "BOOTP"),
+ &ic_addrservaddr, &ic_myaddr);
return 0;
}
@@ -1426,7 +1410,7 @@ static int __init ip_auto_config(void)
if (!ic_enable)
return 0;
- DBG(("IP-Config: Entered.\n"));
+ pr_debug("IP-Config: Entered.\n");
#ifdef IPCONFIG_DYNAMIC
try_try_again:
#endif
@@ -1542,7 +1526,7 @@ static int __init ip_auto_config(void)
pr_cont(", mtu=%d", ic_dev_mtu);
for (i = 0; i < CONF_NAMESERVERS_MAX; i++)
if (ic_nameservers[i] != NONE) {
- pr_info(" nameserver%u=%pI4",
+ pr_cont(" nameserver%u=%pI4",
i, &ic_nameservers[i]);
break;
}
@@ -1585,7 +1569,7 @@ static int __init ic_proto_name(char *name)
return 1;
*v = 0;
if (kstrtou8(client_id, 0, dhcp_client_identifier))
- DBG("DHCP: Invalid client identifier type\n");
+ pr_debug("DHCP: Invalid client identifier type\n");
strncpy(dhcp_client_identifier + 1, v + 1, 251);
*v = ',';
}
@@ -1644,7 +1628,7 @@ static int __init ip_auto_config_setup(char *addrs)
if ((cp = strchr(ip, ':')))
*cp++ = '\0';
if (strlen(ip) > 0) {
- DBG(("IP-Config: Parameter #%d: `%s'\n", num, ip));
+ pr_debug("IP-Config: Parameter #%d: `%s'\n", num, ip);
switch (num) {
case 0:
if ((ic_myaddr = in_aton(ip)) == ANY)
@@ -1716,7 +1700,7 @@ static int __init vendor_class_identifier_setup(char *addrs)
if (strlcpy(vendor_class_identifier, addrs,
sizeof(vendor_class_identifier))
>= sizeof(vendor_class_identifier))
- pr_warn("DHCP: vendorclass too long, truncated to \"%s\"",
+ pr_warn("DHCP: vendorclass too long, truncated to \"%s\"\n",
vendor_class_identifier);
return 1;
}
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index f34c31defafe..4044da61e747 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -103,7 +103,6 @@
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/if_arp.h>
-#include <linux/mroute.h>
#include <linux/init.h>
#include <linux/netfilter_ipv4.h>
#include <linux/if_ether.h>
@@ -253,9 +252,6 @@ ipip_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
p.i_key = p.o_key = 0;
p.i_flags = p.o_flags = 0;
- if (p.iph.ttl)
- p.iph.frag_off |= htons(IP_DF);
-
err = ip_tunnel_ioctl(dev, &p, cmd);
if (err)
return err;
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 92dd4b74d513..395e2814a46d 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -66,28 +66,7 @@
#include <net/netlink.h>
#include <net/fib_rules.h>
#include <linux/netconf.h>
-
-#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
-#define CONFIG_IP_PIMSM 1
-#endif
-
-struct mr_table {
- struct list_head list;
- possible_net_t net;
- u32 id;
- struct sock __rcu *mroute_sk;
- struct timer_list ipmr_expire_timer;
- struct list_head mfc_unres_queue;
- struct list_head mfc_cache_array[MFC_LINES];
- struct vif_device vif_table[MAXVIFS];
- int maxvif;
- atomic_t cache_resolve_queue_len;
- bool mroute_do_assert;
- bool mroute_do_pim;
-#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
- int mroute_reg_vif_num;
-#endif
-};
+#include <net/nexthop.h>
struct ipmr_rule {
struct fib_rule common;
@@ -103,11 +82,7 @@ struct ipmr_result {
static DEFINE_RWLOCK(mrt_lock);
-/*
- * Multicast router control variables
- */
-
-#define VIF_EXISTS(_mrt, _idx) ((_mrt)->vif_table[_idx].dev != NULL)
+/* Multicast router control variables */
/* Special spinlock for queue of unresolved entries */
static DEFINE_SPINLOCK(mfc_unres_lock);
@@ -134,7 +109,7 @@ static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
struct mfc_cache *c, struct rtmsg *rtm);
static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc,
int cmd);
-static void mroute_clean_tables(struct mr_table *mrt);
+static void mroute_clean_tables(struct mr_table *mrt, bool all);
static void ipmr_expire_process(unsigned long arg);
#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
@@ -252,8 +227,8 @@ static int __net_init ipmr_rules_init(struct net *net)
INIT_LIST_HEAD(&net->ipv4.mr_tables);
mrt = ipmr_new_table(net, RT_TABLE_DEFAULT);
- if (!mrt) {
- err = -ENOMEM;
+ if (IS_ERR(mrt)) {
+ err = PTR_ERR(mrt);
goto err1;
}
@@ -301,8 +276,13 @@ static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4,
static int __net_init ipmr_rules_init(struct net *net)
{
- net->ipv4.mrt = ipmr_new_table(net, RT_TABLE_DEFAULT);
- return net->ipv4.mrt ? 0 : -ENOMEM;
+ struct mr_table *mrt;
+
+ mrt = ipmr_new_table(net, RT_TABLE_DEFAULT);
+ if (IS_ERR(mrt))
+ return PTR_ERR(mrt);
+ net->ipv4.mrt = mrt;
+ return 0;
}
static void __net_exit ipmr_rules_exit(struct net *net)
@@ -319,13 +299,17 @@ static struct mr_table *ipmr_new_table(struct net *net, u32 id)
struct mr_table *mrt;
unsigned int i;
+ /* "pimreg%u" should not exceed 16 bytes (IFNAMSIZ) */
+ if (id != RT_TABLE_DEFAULT && id >= 1000000000)
+ return ERR_PTR(-EINVAL);
+
mrt = ipmr_get_table(net, id);
if (mrt)
return mrt;
mrt = kzalloc(sizeof(*mrt), GFP_KERNEL);
if (!mrt)
- return NULL;
+ return ERR_PTR(-ENOMEM);
write_pnet(&mrt->net, net);
mrt->id = id;
@@ -338,9 +322,7 @@ static struct mr_table *ipmr_new_table(struct net *net, u32 id)
setup_timer(&mrt->ipmr_expire_timer, ipmr_expire_process,
(unsigned long)mrt);
-#ifdef CONFIG_IP_PIMSM
mrt->mroute_reg_vif_num = -1;
-#endif
#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
list_add_tail_rcu(&mrt->list, &net->ipv4.mr_tables);
#endif
@@ -350,7 +332,7 @@ static struct mr_table *ipmr_new_table(struct net *net, u32 id)
static void ipmr_free_table(struct mr_table *mrt)
{
del_timer_sync(&mrt->ipmr_expire_timer);
- mroute_clean_tables(mrt);
+ mroute_clean_tables(mrt, true);
kfree(mrt);
}
@@ -387,8 +369,24 @@ static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v)
}
}
-static
-struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v)
+/* Initialize ipmr pimreg/tunnel in_device */
+static bool ipmr_init_vif_indev(const struct net_device *dev)
+{
+ struct in_device *in_dev;
+
+ ASSERT_RTNL();
+
+ in_dev = __in_dev_get_rtnl(dev);
+ if (!in_dev)
+ return false;
+ ipv4_devconf_setall(in_dev);
+ neigh_parms_data_state_setall(in_dev->arp_parms);
+ IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
+
+ return true;
+}
+
+static struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v)
{
struct net_device *dev;
@@ -399,7 +397,6 @@ struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v)
int err;
struct ifreq ifr;
struct ip_tunnel_parm p;
- struct in_device *in_dev;
memset(&p, 0, sizeof(p));
p.iph.daddr = v->vifc_rmt_addr.s_addr;
@@ -424,15 +421,8 @@ struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v)
if (err == 0 &&
(dev = __dev_get_by_name(net, p.name)) != NULL) {
dev->flags |= IFF_MULTICAST;
-
- in_dev = __in_dev_get_rtnl(dev);
- if (!in_dev)
+ if (!ipmr_init_vif_indev(dev))
goto failure;
-
- ipv4_devconf_setall(in_dev);
- neigh_parms_data_state_setall(in_dev->arp_parms);
- IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
-
if (dev_open(dev))
goto failure;
dev_hold(dev);
@@ -441,16 +431,11 @@ struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v)
return dev;
failure:
- /* allow the register to be completed before unregistering. */
- rtnl_unlock();
- rtnl_lock();
-
unregister_netdevice(dev);
return NULL;
}
-#ifdef CONFIG_IP_PIMSM
-
+#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct net *net = dev_net(dev);
@@ -500,7 +485,6 @@ static void reg_vif_setup(struct net_device *dev)
static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)
{
struct net_device *dev;
- struct in_device *in_dev;
char name[IFNAMSIZ];
if (mrt->id == RT_TABLE_DEFAULT)
@@ -520,18 +504,8 @@ static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)
return NULL;
}
- rcu_read_lock();
- in_dev = __in_dev_get_rcu(dev);
- if (!in_dev) {
- rcu_read_unlock();
+ if (!ipmr_init_vif_indev(dev))
goto failure;
- }
-
- ipv4_devconf_setall(in_dev);
- neigh_parms_data_state_setall(in_dev->arp_parms);
- IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
- rcu_read_unlock();
-
if (dev_open(dev))
goto failure;
@@ -540,20 +514,59 @@ static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)
return dev;
failure:
- /* allow the register to be completed before unregistering. */
- rtnl_unlock();
- rtnl_lock();
-
unregister_netdevice(dev);
return NULL;
}
+
+/* called with rcu_read_lock() */
+static int __pim_rcv(struct mr_table *mrt, struct sk_buff *skb,
+ unsigned int pimlen)
+{
+ struct net_device *reg_dev = NULL;
+ struct iphdr *encap;
+
+ encap = (struct iphdr *)(skb_transport_header(skb) + pimlen);
+ /* Check that:
+ * a. packet is really sent to a multicast group
+ * b. packet is not a NULL-REGISTER
+ * c. packet is not truncated
+ */
+ if (!ipv4_is_multicast(encap->daddr) ||
+ encap->tot_len == 0 ||
+ ntohs(encap->tot_len) + pimlen > skb->len)
+ return 1;
+
+ read_lock(&mrt_lock);
+ if (mrt->mroute_reg_vif_num >= 0)
+ reg_dev = mrt->vif_table[mrt->mroute_reg_vif_num].dev;
+ read_unlock(&mrt_lock);
+
+ if (!reg_dev)
+ return 1;
+
+ skb->mac_header = skb->network_header;
+ skb_pull(skb, (u8 *)encap - skb->data);
+ skb_reset_network_header(skb);
+ skb->protocol = htons(ETH_P_IP);
+ skb->ip_summed = CHECKSUM_NONE;
+
+ skb_tunnel_rx(skb, reg_dev, dev_net(reg_dev));
+
+ netif_rx(skb);
+
+ return NET_RX_SUCCESS;
+}
+#else
+static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)
+{
+ return NULL;
+}
#endif
/**
* vif_delete - Delete a VIF entry
* @notify: Set to 1, if the caller is a notifier_call
*/
-
static int vif_delete(struct mr_table *mrt, int vifi, int notify,
struct list_head *head)
{
@@ -575,10 +588,8 @@ static int vif_delete(struct mr_table *mrt, int vifi, int notify,
return -EADDRNOTAVAIL;
}
-#ifdef CONFIG_IP_PIMSM
if (vifi == mrt->mroute_reg_vif_num)
mrt->mroute_reg_vif_num = -1;
-#endif
if (vifi + 1 == mrt->maxvif) {
int tmp;
@@ -625,7 +636,6 @@ static inline void ipmr_cache_free(struct mfc_cache *c)
/* Destroy an unresolved cache entry, killing queued skbs
* and reporting error to netlink readers.
*/
-
static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c)
{
struct net *net = read_pnet(&mrt->net);
@@ -653,9 +663,7 @@ static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c)
ipmr_cache_free(c);
}
-
/* Timer process for the unresolved queue. */
-
static void ipmr_expire_process(unsigned long arg)
{
struct mr_table *mrt = (struct mr_table *)arg;
@@ -695,7 +703,6 @@ out:
}
/* Fill oifs list. It is called under write locked mrt_lock. */
-
static void ipmr_update_thresholds(struct mr_table *mrt, struct mfc_cache *cache,
unsigned char *ttls)
{
@@ -731,10 +738,10 @@ static int vif_add(struct net *net, struct mr_table *mrt,
return -EADDRINUSE;
switch (vifc->vifc_flags) {
-#ifdef CONFIG_IP_PIMSM
case VIFF_REGISTER:
- /*
- * Special Purpose VIF in PIM
+ if (!ipmr_pimsm_enabled())
+ return -EINVAL;
+ /* Special Purpose VIF in PIM
* All the packets will be sent to the daemon
*/
if (mrt->mroute_reg_vif_num >= 0)
@@ -749,7 +756,6 @@ static int vif_add(struct net *net, struct mr_table *mrt,
return err;
}
break;
-#endif
case VIFF_TUNNEL:
dev = ipmr_new_tunnel(net, vifc);
if (!dev)
@@ -761,7 +767,6 @@ static int vif_add(struct net *net, struct mr_table *mrt,
return err;
}
break;
-
case VIFF_USE_IFINDEX:
case 0:
if (vifc->vifc_flags == VIFF_USE_IFINDEX) {
@@ -815,10 +820,8 @@ static int vif_add(struct net *net, struct mr_table *mrt,
/* And finish update writing critical data */
write_lock_bh(&mrt_lock);
v->dev = dev;
-#ifdef CONFIG_IP_PIMSM
if (v->flags & VIFF_REGISTER)
mrt->mroute_reg_vif_num = vifi;
-#endif
if (vifi+1 > mrt->maxvif)
mrt->maxvif = vifi+1;
write_unlock_bh(&mrt_lock);
@@ -883,9 +886,7 @@ skip:
return ipmr_cache_find_any_parent(mrt, vifi);
}
-/*
- * Allocate a multicast cache entry
- */
+/* Allocate a multicast cache entry */
static struct mfc_cache *ipmr_cache_alloc(void)
{
struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
@@ -906,10 +907,7 @@ static struct mfc_cache *ipmr_cache_alloc_unres(void)
return c;
}
-/*
- * A cache entry has gone into a resolved state from queued
- */
-
+/* A cache entry has gone into a resolved state from queued */
static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,
struct mfc_cache *uc, struct mfc_cache *c)
{
@@ -917,7 +915,6 @@ static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,
struct nlmsgerr *e;
/* Play the pending entries through our router */
-
while ((skb = __skb_dequeue(&uc->mfc_un.unres.unresolved))) {
if (ip_hdr(skb)->version == 0) {
struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
@@ -941,34 +938,29 @@ static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,
}
}
-/*
- * Bounce a cache query up to mrouted. We could use netlink for this but mrouted
- * expects the following bizarre scheme.
+/* Bounce a cache query up to mrouted. We could use netlink for this but mrouted
+ * expects the following bizarre scheme.
*
- * Called under mrt_lock.
+ * Called under mrt_lock.
*/
-
static int ipmr_cache_report(struct mr_table *mrt,
struct sk_buff *pkt, vifi_t vifi, int assert)
{
- struct sk_buff *skb;
const int ihl = ip_hdrlen(pkt);
+ struct sock *mroute_sk;
struct igmphdr *igmp;
struct igmpmsg *msg;
- struct sock *mroute_sk;
+ struct sk_buff *skb;
int ret;
-#ifdef CONFIG_IP_PIMSM
if (assert == IGMPMSG_WHOLEPKT)
skb = skb_realloc_headroom(pkt, sizeof(struct iphdr));
else
-#endif
skb = alloc_skb(128, GFP_ATOMIC);
if (!skb)
return -ENOBUFS;
-#ifdef CONFIG_IP_PIMSM
if (assert == IGMPMSG_WHOLEPKT) {
/* Ugly, but we have no choice with this interface.
* Duplicate old header, fix ihl, length etc.
@@ -986,28 +978,23 @@ static int ipmr_cache_report(struct mr_table *mrt,
ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2;
ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) +
sizeof(struct iphdr));
- } else
-#endif
- {
-
- /* Copy the IP header */
-
- skb_set_network_header(skb, skb->len);
- skb_put(skb, ihl);
- skb_copy_to_linear_data(skb, pkt->data, ihl);
- ip_hdr(skb)->protocol = 0; /* Flag to the kernel this is a route add */
- msg = (struct igmpmsg *)skb_network_header(skb);
- msg->im_vif = vifi;
- skb_dst_set(skb, dst_clone(skb_dst(pkt)));
-
- /* Add our header */
-
- igmp = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
- igmp->type =
- msg->im_msgtype = assert;
- igmp->code = 0;
- ip_hdr(skb)->tot_len = htons(skb->len); /* Fix the length */
- skb->transport_header = skb->network_header;
+ } else {
+ /* Copy the IP header */
+ skb_set_network_header(skb, skb->len);
+ skb_put(skb, ihl);
+ skb_copy_to_linear_data(skb, pkt->data, ihl);
+ /* Flag to the kernel this is a route add */
+ ip_hdr(skb)->protocol = 0;
+ msg = (struct igmpmsg *)skb_network_header(skb);
+ msg->im_vif = vifi;
+ skb_dst_set(skb, dst_clone(skb_dst(pkt)));
+ /* Add our header */
+ igmp = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
+ igmp->type = assert;
+ msg->im_msgtype = assert;
+ igmp->code = 0;
+ ip_hdr(skb)->tot_len = htons(skb->len); /* Fix the length */
+ skb->transport_header = skb->network_header;
}
rcu_read_lock();
@@ -1019,7 +1006,6 @@ static int ipmr_cache_report(struct mr_table *mrt,
}
/* Deliver to mrouted */
-
ret = sock_queue_rcv_skb(mroute_sk, skb);
rcu_read_unlock();
if (ret < 0) {
@@ -1030,12 +1016,9 @@ static int ipmr_cache_report(struct mr_table *mrt,
return ret;
}
-/*
- * Queue a packet for resolution. It gets locked cache entry!
- */
-
-static int
-ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb)
+/* Queue a packet for resolution. It gets locked cache entry! */
+static int ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi,
+ struct sk_buff *skb)
{
bool found = false;
int err;
@@ -1053,7 +1036,6 @@ ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb)
if (!found) {
/* Create a new entry if allowable */
-
if (atomic_read(&mrt->cache_resolve_queue_len) >= 10 ||
(c = ipmr_cache_alloc_unres()) == NULL) {
spin_unlock_bh(&mfc_unres_lock);
@@ -1063,13 +1045,11 @@ ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb)
}
/* Fill in the new cache entry */
-
c->mfc_parent = -1;
c->mfc_origin = iph->saddr;
c->mfc_mcastgrp = iph->daddr;
/* Reflect first query at mrouted. */
-
err = ipmr_cache_report(mrt, skb, vifi, IGMPMSG_NOCACHE);
if (err < 0) {
/* If the report failed throw the cache entry
@@ -1091,7 +1071,6 @@ ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb)
}
/* See if we can append the packet */
-
if (c->mfc_un.unres.unresolved.qlen > 3) {
kfree_skb(skb);
err = -ENOBUFS;
@@ -1104,9 +1083,7 @@ ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb)
return err;
}
-/*
- * MFC cache manipulation by user space mroute daemon
- */
+/* MFC cache manipulation by user space mroute daemon */
static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc, int parent)
{
@@ -1177,9 +1154,8 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
list_add_rcu(&c->list, &mrt->mfc_cache_array[line]);
- /*
- * Check to see if we resolved a queued list. If so we
- * need to send on the frames and tidy up.
+ /* Check to see if we resolved a queued list. If so we
+ * need to send on the frames and tidy up.
*/
found = false;
spin_lock_bh(&mfc_unres_lock);
@@ -1204,29 +1180,25 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
return 0;
}
-/*
- * Close the multicast socket, and clear the vif tables etc
- */
-
-static void mroute_clean_tables(struct mr_table *mrt)
+/* Close the multicast socket, and clear the vif tables etc */
+static void mroute_clean_tables(struct mr_table *mrt, bool all)
{
int i;
LIST_HEAD(list);
struct mfc_cache *c, *next;
/* Shut down all active vif entries */
-
for (i = 0; i < mrt->maxvif; i++) {
- if (!(mrt->vif_table[i].flags & VIFF_STATIC))
- vif_delete(mrt, i, 0, &list);
+ if (!all && (mrt->vif_table[i].flags & VIFF_STATIC))
+ continue;
+ vif_delete(mrt, i, 0, &list);
}
unregister_netdevice_many(&list);
/* Wipe the cache */
-
for (i = 0; i < MFC_LINES; i++) {
list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[i], list) {
- if (c->mfc_flags & MFC_STATIC)
+ if (!all && (c->mfc_flags & MFC_STATIC))
continue;
list_del_rcu(&c->list);
mroute_netlink_event(mrt, c, RTM_DELROUTE);
@@ -1261,50 +1233,58 @@ static void mrtsock_destruct(struct sock *sk)
NETCONFA_IFINDEX_ALL,
net->ipv4.devconf_all);
RCU_INIT_POINTER(mrt->mroute_sk, NULL);
- mroute_clean_tables(mrt);
+ mroute_clean_tables(mrt, false);
}
}
rtnl_unlock();
}
-/*
- * Socket options and virtual interface manipulation. The whole
- * virtual interface system is a complete heap, but unfortunately
- * that's how BSD mrouted happens to think. Maybe one day with a proper
- * MOSPF/PIM router set up we can clean this up.
+/* Socket options and virtual interface manipulation. The whole
+ * virtual interface system is a complete heap, but unfortunately
+ * that's how BSD mrouted happens to think. Maybe one day with a proper
+ * MOSPF/PIM router set up we can clean this up.
*/
-int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsigned int optlen)
+int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval,
+ unsigned int optlen)
{
- int ret, parent = 0;
- struct vifctl vif;
- struct mfcctl mfc;
struct net *net = sock_net(sk);
+ int val, ret = 0, parent = 0;
struct mr_table *mrt;
+ struct vifctl vif;
+ struct mfcctl mfc;
+ u32 uval;
+ /* There's one exception to the lock - MRT_DONE which needs to unlock */
+ rtnl_lock();
if (sk->sk_type != SOCK_RAW ||
- inet_sk(sk)->inet_num != IPPROTO_IGMP)
- return -EOPNOTSUPP;
+ inet_sk(sk)->inet_num != IPPROTO_IGMP) {
+ ret = -EOPNOTSUPP;
+ goto out_unlock;
+ }
mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
- if (!mrt)
- return -ENOENT;
-
+ if (!mrt) {
+ ret = -ENOENT;
+ goto out_unlock;
+ }
if (optname != MRT_INIT) {
if (sk != rcu_access_pointer(mrt->mroute_sk) &&
- !ns_capable(net->user_ns, CAP_NET_ADMIN))
- return -EACCES;
+ !ns_capable(net->user_ns, CAP_NET_ADMIN)) {
+ ret = -EACCES;
+ goto out_unlock;
+ }
}
switch (optname) {
case MRT_INIT:
- if (optlen != sizeof(int))
- return -EINVAL;
-
- rtnl_lock();
+ if (optlen != sizeof(int)) {
+ ret = -EINVAL;
+ break;
+ }
if (rtnl_dereference(mrt->mroute_sk)) {
- rtnl_unlock();
- return -EADDRINUSE;
+ ret = -EADDRINUSE;
+ break;
}
ret = ip_ra_control(sk, 1, mrtsock_destruct);
@@ -1315,129 +1295,133 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsi
NETCONFA_IFINDEX_ALL,
net->ipv4.devconf_all);
}
- rtnl_unlock();
- return ret;
+ break;
case MRT_DONE:
- if (sk != rcu_access_pointer(mrt->mroute_sk))
- return -EACCES;
- return ip_ra_control(sk, 0, NULL);
+ if (sk != rcu_access_pointer(mrt->mroute_sk)) {
+ ret = -EACCES;
+ } else {
+ /* We need to unlock here because mrtsock_destruct takes
+ * care of rtnl itself and we can't change that due to
+ * the IP_ROUTER_ALERT setsockopt which runs without it.
+ */
+ rtnl_unlock();
+ ret = ip_ra_control(sk, 0, NULL);
+ goto out;
+ }
+ break;
case MRT_ADD_VIF:
case MRT_DEL_VIF:
- if (optlen != sizeof(vif))
- return -EINVAL;
- if (copy_from_user(&vif, optval, sizeof(vif)))
- return -EFAULT;
- if (vif.vifc_vifi >= MAXVIFS)
- return -ENFILE;
- rtnl_lock();
+ if (optlen != sizeof(vif)) {
+ ret = -EINVAL;
+ break;
+ }
+ if (copy_from_user(&vif, optval, sizeof(vif))) {
+ ret = -EFAULT;
+ break;
+ }
+ if (vif.vifc_vifi >= MAXVIFS) {
+ ret = -ENFILE;
+ break;
+ }
if (optname == MRT_ADD_VIF) {
ret = vif_add(net, mrt, &vif,
sk == rtnl_dereference(mrt->mroute_sk));
} else {
ret = vif_delete(mrt, vif.vifc_vifi, 0, NULL);
}
- rtnl_unlock();
- return ret;
-
- /*
- * Manipulate the forwarding caches. These live
- * in a sort of kernel/user symbiosis.
- */
+ break;
+ /* Manipulate the forwarding caches. These live
+ * in a sort of kernel/user symbiosis.
+ */
case MRT_ADD_MFC:
case MRT_DEL_MFC:
parent = -1;
case MRT_ADD_MFC_PROXY:
case MRT_DEL_MFC_PROXY:
- if (optlen != sizeof(mfc))
- return -EINVAL;
- if (copy_from_user(&mfc, optval, sizeof(mfc)))
- return -EFAULT;
+ if (optlen != sizeof(mfc)) {
+ ret = -EINVAL;
+ break;
+ }
+ if (copy_from_user(&mfc, optval, sizeof(mfc))) {
+ ret = -EFAULT;
+ break;
+ }
if (parent == 0)
parent = mfc.mfcc_parent;
- rtnl_lock();
if (optname == MRT_DEL_MFC || optname == MRT_DEL_MFC_PROXY)
ret = ipmr_mfc_delete(mrt, &mfc, parent);
else
ret = ipmr_mfc_add(net, mrt, &mfc,
sk == rtnl_dereference(mrt->mroute_sk),
parent);
- rtnl_unlock();
- return ret;
- /*
- * Control PIM assert.
- */
+ break;
+ /* Control PIM assert. */
case MRT_ASSERT:
- {
- int v;
- if (optlen != sizeof(v))
- return -EINVAL;
- if (get_user(v, (int __user *)optval))
- return -EFAULT;
- mrt->mroute_do_assert = v;
- return 0;
- }
-#ifdef CONFIG_IP_PIMSM
+ if (optlen != sizeof(val)) {
+ ret = -EINVAL;
+ break;
+ }
+ if (get_user(val, (int __user *)optval)) {
+ ret = -EFAULT;
+ break;
+ }
+ mrt->mroute_do_assert = val;
+ break;
case MRT_PIM:
- {
- int v;
-
- if (optlen != sizeof(v))
- return -EINVAL;
- if (get_user(v, (int __user *)optval))
- return -EFAULT;
- v = !!v;
+ if (!ipmr_pimsm_enabled()) {
+ ret = -ENOPROTOOPT;
+ break;
+ }
+ if (optlen != sizeof(val)) {
+ ret = -EINVAL;
+ break;
+ }
+ if (get_user(val, (int __user *)optval)) {
+ ret = -EFAULT;
+ break;
+ }
- rtnl_lock();
- ret = 0;
- if (v != mrt->mroute_do_pim) {
- mrt->mroute_do_pim = v;
- mrt->mroute_do_assert = v;
+ val = !!val;
+ if (val != mrt->mroute_do_pim) {
+ mrt->mroute_do_pim = val;
+ mrt->mroute_do_assert = val;
}
- rtnl_unlock();
- return ret;
- }
-#endif
-#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
+ break;
case MRT_TABLE:
- {
- u32 v;
-
- if (optlen != sizeof(u32))
- return -EINVAL;
- if (get_user(v, (u32 __user *)optval))
- return -EFAULT;
-
- /* "pimreg%u" should not exceed 16 bytes (IFNAMSIZ) */
- if (v != RT_TABLE_DEFAULT && v >= 1000000000)
- return -EINVAL;
+ if (!IS_BUILTIN(CONFIG_IP_MROUTE_MULTIPLE_TABLES)) {
+ ret = -ENOPROTOOPT;
+ break;
+ }
+ if (optlen != sizeof(uval)) {
+ ret = -EINVAL;
+ break;
+ }
+ if (get_user(uval, (u32 __user *)optval)) {
+ ret = -EFAULT;
+ break;
+ }
- rtnl_lock();
- ret = 0;
if (sk == rtnl_dereference(mrt->mroute_sk)) {
ret = -EBUSY;
} else {
- if (!ipmr_new_table(net, v))
- ret = -ENOMEM;
+ mrt = ipmr_new_table(net, uval);
+ if (IS_ERR(mrt))
+ ret = PTR_ERR(mrt);
else
- raw_sk(sk)->ipmr_table = v;
+ raw_sk(sk)->ipmr_table = uval;
}
- rtnl_unlock();
- return ret;
- }
-#endif
- /*
- * Spurious command, or MRT_VERSION which you cannot
- * set.
- */
+ break;
+ /* Spurious command, or MRT_VERSION which you cannot set. */
default:
- return -ENOPROTOOPT;
+ ret = -ENOPROTOOPT;
}
+out_unlock:
+ rtnl_unlock();
+out:
+ return ret;
}
-/*
- * Getsock opt support for the multicast routing system.
- */
-
+/* Getsock opt support for the multicast routing system. */
int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int __user *optlen)
{
int olr;
@@ -1453,39 +1437,35 @@ int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int
if (!mrt)
return -ENOENT;
- if (optname != MRT_VERSION &&
-#ifdef CONFIG_IP_PIMSM
- optname != MRT_PIM &&
-#endif
- optname != MRT_ASSERT)
+ switch (optname) {
+ case MRT_VERSION:
+ val = 0x0305;
+ break;
+ case MRT_PIM:
+ if (!ipmr_pimsm_enabled())
+ return -ENOPROTOOPT;
+ val = mrt->mroute_do_pim;
+ break;
+ case MRT_ASSERT:
+ val = mrt->mroute_do_assert;
+ break;
+ default:
return -ENOPROTOOPT;
+ }
if (get_user(olr, optlen))
return -EFAULT;
-
olr = min_t(unsigned int, olr, sizeof(int));
if (olr < 0)
return -EINVAL;
-
if (put_user(olr, optlen))
return -EFAULT;
- if (optname == MRT_VERSION)
- val = 0x0305;
-#ifdef CONFIG_IP_PIMSM
- else if (optname == MRT_PIM)
- val = mrt->mroute_do_pim;
-#endif
- else
- val = mrt->mroute_do_assert;
if (copy_to_user(optval, &val, olr))
return -EFAULT;
return 0;
}
-/*
- * The IP multicast ioctl support routines.
- */
-
+/* The IP multicast ioctl support routines. */
int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
{
struct sioc_sg_req sr;
@@ -1618,7 +1598,6 @@ int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
}
#endif
-
static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
@@ -1640,17 +1619,14 @@ static int ipmr_device_event(struct notifier_block *this, unsigned long event, v
return NOTIFY_DONE;
}
-
static struct notifier_block ip_mr_notifier = {
.notifier_call = ipmr_device_event,
};
-/*
- * Encapsulate a packet by attaching a valid IPIP header to it.
- * This avoids tunnel drivers and other mess and gives us the speed so
- * important for multicast video.
+/* Encapsulate a packet by attaching a valid IPIP header to it.
+ * This avoids tunnel drivers and other mess and gives us the speed so
+ * important for multicast video.
*/
-
static void ip_encap(struct net *net, struct sk_buff *skb,
__be32 saddr, __be32 daddr)
{
@@ -1692,9 +1668,7 @@ static inline int ipmr_forward_finish(struct net *net, struct sock *sk,
return dst_output(net, sk, skb);
}
-/*
- * Processing handlers for ipmr_forward
- */
+/* Processing handlers for ipmr_forward */
static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
struct sk_buff *skb, struct mfc_cache *c, int vifi)
@@ -1709,7 +1683,6 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
if (!vif->dev)
goto out_free;
-#ifdef CONFIG_IP_PIMSM
if (vif->flags & VIFF_REGISTER) {
vif->pkt_out++;
vif->bytes_out += skb->len;
@@ -1718,7 +1691,6 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
ipmr_cache_report(mrt, skb, vifi, IGMPMSG_WHOLEPKT);
goto out_free;
}
-#endif
if (vif->flags & VIFF_TUNNEL) {
rt = ip_route_output_ports(net, &fl4, NULL,
@@ -1745,7 +1717,6 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
* allow to send ICMP, so that packets will disappear
* to blackhole.
*/
-
IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
ip_rt_put(rt);
goto out_free;
@@ -1777,8 +1748,7 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
IPCB(skb)->flags |= IPSKB_FORWARDED;
- /*
- * RFC1584 teaches, that DVMRP/PIM router must deliver packets locally
+ /* RFC1584 teaches, that DVMRP/PIM router must deliver packets locally
* not only before forwarding, but after forwarding on all output
* interfaces. It is clear, if mrouter runs a multicasting
* program, it should receive packets not depending to what interface
@@ -1809,7 +1779,6 @@ static int ipmr_find_vif(struct mr_table *mrt, struct net_device *dev)
}
/* "local" means that we should preserve one skb (for local delivery) */
-
static void ip_mr_forward(struct net *net, struct mr_table *mrt,
struct sk_buff *skb, struct mfc_cache *cache,
int local)
@@ -1834,9 +1803,7 @@ static void ip_mr_forward(struct net *net, struct mr_table *mrt,
goto forward;
}
- /*
- * Wrong interface: drop packet and (maybe) send PIM assert.
- */
+ /* Wrong interface: drop packet and (maybe) send PIM assert. */
if (mrt->vif_table[vif].dev != skb->dev) {
if (rt_is_output_route(skb_rtable(skb))) {
/* It is our own packet, looped back.
@@ -1875,9 +1842,7 @@ forward:
mrt->vif_table[vif].pkt_in++;
mrt->vif_table[vif].bytes_in += skb->len;
- /*
- * Forward the frame
- */
+ /* Forward the frame */
if (cache->mfc_origin == htonl(INADDR_ANY) &&
cache->mfc_mcastgrp == htonl(INADDR_ANY)) {
if (true_vifi >= 0 &&
@@ -1951,11 +1916,9 @@ static struct mr_table *ipmr_rt_fib_lookup(struct net *net, struct sk_buff *skb)
return mrt;
}
-/*
- * Multicast packets for forwarding arrive here
- * Called with rcu_read_lock();
+/* Multicast packets for forwarding arrive here
+ * Called with rcu_read_lock();
*/
-
int ip_mr_input(struct sk_buff *skb)
{
struct mfc_cache *cache;
@@ -2006,9 +1969,7 @@ int ip_mr_input(struct sk_buff *skb)
vif);
}
- /*
- * No usable cache entry
- */
+ /* No usable cache entry */
if (!cache) {
int vif;
@@ -2049,53 +2010,8 @@ dont_forward:
return 0;
}
-#ifdef CONFIG_IP_PIMSM
-/* called with rcu_read_lock() */
-static int __pim_rcv(struct mr_table *mrt, struct sk_buff *skb,
- unsigned int pimlen)
-{
- struct net_device *reg_dev = NULL;
- struct iphdr *encap;
-
- encap = (struct iphdr *)(skb_transport_header(skb) + pimlen);
- /*
- * Check that:
- * a. packet is really sent to a multicast group
- * b. packet is not a NULL-REGISTER
- * c. packet is not truncated
- */
- if (!ipv4_is_multicast(encap->daddr) ||
- encap->tot_len == 0 ||
- ntohs(encap->tot_len) + pimlen > skb->len)
- return 1;
-
- read_lock(&mrt_lock);
- if (mrt->mroute_reg_vif_num >= 0)
- reg_dev = mrt->vif_table[mrt->mroute_reg_vif_num].dev;
- read_unlock(&mrt_lock);
-
- if (!reg_dev)
- return 1;
-
- skb->mac_header = skb->network_header;
- skb_pull(skb, (u8 *)encap - skb->data);
- skb_reset_network_header(skb);
- skb->protocol = htons(ETH_P_IP);
- skb->ip_summed = CHECKSUM_NONE;
-
- skb_tunnel_rx(skb, reg_dev, dev_net(reg_dev));
-
- netif_rx(skb);
-
- return NET_RX_SUCCESS;
-}
-#endif
-
#ifdef CONFIG_IP_PIMSM_V1
-/*
- * Handle IGMP messages of PIMv1
- */
-
+/* Handle IGMP messages of PIMv1 */
int pim_rcv_v1(struct sk_buff *skb)
{
struct igmphdr *pim;
@@ -2256,8 +2172,6 @@ int ipmr_get_route(struct net *net, struct sk_buff *skb,
}
read_lock(&mrt_lock);
- if (!nowait && (rtm->rtm_flags & RTM_F_NOTIFY))
- cache->mfc_flags |= MFC_NOTIFY;
err = __ipmr_fill_mroute(mrt, skb, cache, rtm);
read_unlock(&mrt_lock);
rcu_read_unlock();
@@ -2419,10 +2333,133 @@ done:
return skb->len;
}
+static const struct nla_policy rtm_ipmr_policy[RTA_MAX + 1] = {
+ [RTA_SRC] = { .type = NLA_U32 },
+ [RTA_DST] = { .type = NLA_U32 },
+ [RTA_IIF] = { .type = NLA_U32 },
+ [RTA_TABLE] = { .type = NLA_U32 },
+ [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) },
+};
+
+static bool ipmr_rtm_validate_proto(unsigned char rtm_protocol)
+{
+ switch (rtm_protocol) {
+ case RTPROT_STATIC:
+ case RTPROT_MROUTED:
+ return true;
+ }
+ return false;
+}
+
+static int ipmr_nla_get_ttls(const struct nlattr *nla, struct mfcctl *mfcc)
+{
+ struct rtnexthop *rtnh = nla_data(nla);
+ int remaining = nla_len(nla), vifi = 0;
+
+ while (rtnh_ok(rtnh, remaining)) {
+ mfcc->mfcc_ttls[vifi] = rtnh->rtnh_hops;
+ if (++vifi == MAXVIFS)
+ break;
+ rtnh = rtnh_next(rtnh, &remaining);
+ }
+
+ return remaining > 0 ? -EINVAL : vifi;
+}
+
+/* returns < 0 on error, 0 for ADD_MFC and 1 for ADD_MFC_PROXY */
+static int rtm_to_ipmr_mfcc(struct net *net, struct nlmsghdr *nlh,
+ struct mfcctl *mfcc, int *mrtsock,
+ struct mr_table **mrtret)
+{
+ struct net_device *dev = NULL;
+ u32 tblid = RT_TABLE_DEFAULT;
+ struct mr_table *mrt;
+ struct nlattr *attr;
+ struct rtmsg *rtm;
+ int ret, rem;
+
+ ret = nlmsg_validate(nlh, sizeof(*rtm), RTA_MAX, rtm_ipmr_policy);
+ if (ret < 0)
+ goto out;
+ rtm = nlmsg_data(nlh);
+
+ ret = -EINVAL;
+ if (rtm->rtm_family != RTNL_FAMILY_IPMR || rtm->rtm_dst_len != 32 ||
+ rtm->rtm_type != RTN_MULTICAST ||
+ rtm->rtm_scope != RT_SCOPE_UNIVERSE ||
+ !ipmr_rtm_validate_proto(rtm->rtm_protocol))
+ goto out;
+
+ memset(mfcc, 0, sizeof(*mfcc));
+ mfcc->mfcc_parent = -1;
+ ret = 0;
+ nlmsg_for_each_attr(attr, nlh, sizeof(struct rtmsg), rem) {
+ switch (nla_type(attr)) {
+ case RTA_SRC:
+ mfcc->mfcc_origin.s_addr = nla_get_be32(attr);
+ break;
+ case RTA_DST:
+ mfcc->mfcc_mcastgrp.s_addr = nla_get_be32(attr);
+ break;
+ case RTA_IIF:
+ dev = __dev_get_by_index(net, nla_get_u32(attr));
+ if (!dev) {
+ ret = -ENODEV;
+ goto out;
+ }
+ break;
+ case RTA_MULTIPATH:
+ if (ipmr_nla_get_ttls(attr, mfcc) < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+ break;
+ case RTA_PREFSRC:
+ ret = 1;
+ break;
+ case RTA_TABLE:
+ tblid = nla_get_u32(attr);
+ break;
+ }
+ }
+ mrt = ipmr_get_table(net, tblid);
+ if (!mrt) {
+ ret = -ENOENT;
+ goto out;
+ }
+ *mrtret = mrt;
+ *mrtsock = rtm->rtm_protocol == RTPROT_MROUTED ? 1 : 0;
+ if (dev)
+ mfcc->mfcc_parent = ipmr_find_vif(mrt, dev);
+
+out:
+ return ret;
+}
+
+/* takes care of both newroute and delroute */
+static int ipmr_rtm_route(struct sk_buff *skb, struct nlmsghdr *nlh)
+{
+ struct net *net = sock_net(skb->sk);
+ int ret, mrtsock, parent;
+ struct mr_table *tbl;
+ struct mfcctl mfcc;
+
+ mrtsock = 0;
+ tbl = NULL;
+ ret = rtm_to_ipmr_mfcc(net, nlh, &mfcc, &mrtsock, &tbl);
+ if (ret < 0)
+ return ret;
+
+ parent = ret ? mfcc.mfcc_parent : -1;
+ if (nlh->nlmsg_type == RTM_NEWROUTE)
+ return ipmr_mfc_add(net, tbl, &mfcc, mrtsock, parent);
+ else
+ return ipmr_mfc_delete(tbl, &mfcc, parent);
+}
+
#ifdef CONFIG_PROC_FS
-/*
- * The /proc interfaces to multicast routing :
- * /proc/net/ip_mr_cache & /proc/net/ip_mr_vif
+/* The /proc interfaces to multicast routing :
+ * /proc/net/ip_mr_cache & /proc/net/ip_mr_vif
*/
struct ipmr_vif_iter {
struct seq_net_private p;
@@ -2706,10 +2743,7 @@ static const struct net_protocol pim_protocol = {
};
#endif
-
-/*
- * Setup for IP multicast routing
- */
+/* Setup for IP multicast routing */
static int __net_init ipmr_net_init(struct net *net)
{
int err;
@@ -2759,8 +2793,6 @@ int __init ip_mr_init(void)
sizeof(struct mfc_cache),
0, SLAB_HWCACHE_ALIGN | SLAB_PANIC,
NULL);
- if (!mrt_cachep)
- return -ENOMEM;
err = register_pernet_subsys(&ipmr_net_ops);
if (err)
@@ -2778,6 +2810,10 @@ int __init ip_mr_init(void)
#endif
rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE,
NULL, ipmr_rtm_dumproute, NULL);
+ rtnl_register(RTNL_FAMILY_IPMR, RTM_NEWROUTE,
+ ipmr_rtm_route, NULL, NULL);
+ rtnl_register(RTNL_FAMILY_IPMR, RTM_DELROUTE,
+ ipmr_rtm_route, NULL, NULL);
return 0;
#ifdef CONFIG_IP_PIMSM_V2
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
index a35584176535..c187c60e3e0c 100644
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
@@ -60,6 +60,7 @@ config NFT_REJECT_IPV4
config NFT_DUP_IPV4
tristate "IPv4 nf_tables packet duplication support"
+ depends on !NF_CONNTRACK || NF_CONNTRACK
select NF_DUP_IPV4
help
This module enables IPv4 packet duplication support for nf_tables.
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
index 11dccba474b7..b488cac9c5ca 100644
--- a/net/ipv4/netfilter/arp_tables.c
+++ b/net/ipv4/netfilter/arp_tables.c
@@ -38,13 +38,13 @@ MODULE_DESCRIPTION("arptables core");
/*#define DEBUG_ARP_TABLES_USER*/
#ifdef DEBUG_ARP_TABLES
-#define dprintf(format, args...) printk(format , ## args)
+#define dprintf(format, args...) pr_debug(format, ## args)
#else
#define dprintf(format, args...)
#endif
#ifdef DEBUG_ARP_TABLES_USER
-#define duprintf(format, args...) printk(format , ## args)
+#define duprintf(format, args...) pr_debug(format, ## args)
#else
#define duprintf(format, args...)
#endif
@@ -1905,7 +1905,7 @@ static int __init arp_tables_init(void)
if (ret < 0)
goto err4;
- printk(KERN_INFO "arp_tables: (C) 2002 David S. Miller\n");
+ pr_info("arp_tables: (C) 2002 David S. Miller\n");
return 0;
err4:
diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
index 461ca926fd39..e3c46e8e2762 100644
--- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
+++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
@@ -451,7 +451,7 @@ static int __init nf_conntrack_l3proto_ipv4_init(void)
ret = nf_register_sockopt(&so_getorigdst);
if (ret < 0) {
- printk(KERN_ERR "Unable to register netfilter socket option\n");
+ pr_err("Unable to register netfilter socket option\n");
return ret;
}
diff --git a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
index 5075b7ecd26d..61c7cc22ea68 100644
--- a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
+++ b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c
@@ -132,7 +132,8 @@ static void nf_nat_ipv4_csum_recalc(struct sk_buff *skb,
if (skb->ip_summed != CHECKSUM_PARTIAL) {
if (!(rt->rt_flags & RTCF_LOCAL) &&
- (!skb->dev || skb->dev->features & NETIF_F_V4_CSUM)) {
+ (!skb->dev || skb->dev->features &
+ (NETIF_F_IP_CSUM | NETIF_F_HW_CSUM))) {
skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum_start = skb_headroom(skb) +
skb_network_offset(skb) +
diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic.c b/net/ipv4/netfilter/nf_nat_snmp_basic.c
index ddb894ac1458..c9b52c361da2 100644
--- a/net/ipv4/netfilter/nf_nat_snmp_basic.c
+++ b/net/ipv4/netfilter/nf_nat_snmp_basic.c
@@ -1048,7 +1048,7 @@ static int snmp_parse_mangle(unsigned char *msg,
if (!asn1_uint_decode (&ctx, end, &vers))
return 0;
if (debug > 1)
- printk(KERN_DEBUG "bsalg: snmp version: %u\n", vers + 1);
+ pr_debug("bsalg: snmp version: %u\n", vers + 1);
if (vers > 1)
return 1;
@@ -1064,10 +1064,10 @@ static int snmp_parse_mangle(unsigned char *msg,
if (debug > 1) {
unsigned int i;
- printk(KERN_DEBUG "bsalg: community: ");
+ pr_debug("bsalg: community: ");
for (i = 0; i < comm.len; i++)
- printk("%c", comm.data[i]);
- printk("\n");
+ pr_cont("%c", comm.data[i]);
+ pr_cont("\n");
}
kfree(comm.data);
@@ -1091,9 +1091,9 @@ static int snmp_parse_mangle(unsigned char *msg,
};
if (pdutype > SNMP_PDU_TRAP2)
- printk(KERN_DEBUG "bsalg: bad pdu type %u\n", pdutype);
+ pr_debug("bsalg: bad pdu type %u\n", pdutype);
else
- printk(KERN_DEBUG "bsalg: pdu: %s\n", pdus[pdutype]);
+ pr_debug("bsalg: pdu: %s\n", pdus[pdutype]);
}
if (pdutype != SNMP_PDU_RESPONSE &&
pdutype != SNMP_PDU_TRAP1 && pdutype != SNMP_PDU_TRAP2)
@@ -1119,7 +1119,7 @@ static int snmp_parse_mangle(unsigned char *msg,
return 0;
if (debug > 1)
- printk(KERN_DEBUG "bsalg: request: id=0x%lx error_status=%u "
+ pr_debug("bsalg: request: id=0x%lx error_status=%u "
"error_index=%u\n", req.id, req.error_status,
req.error_index);
}
@@ -1145,13 +1145,13 @@ static int snmp_parse_mangle(unsigned char *msg,
}
if (debug > 1) {
- printk(KERN_DEBUG "bsalg: object: ");
+ pr_debug("bsalg: object: ");
for (i = 0; i < obj->id_len; i++) {
if (i > 0)
- printk(".");
- printk("%lu", obj->id[i]);
+ pr_cont(".");
+ pr_cont("%lu", obj->id[i]);
}
- printk(": type=%u\n", obj->type);
+ pr_cont(": type=%u\n", obj->type);
}
diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c
index c747b2d9eb77..b6ea57ec5e14 100644
--- a/net/ipv4/netfilter/nf_reject_ipv4.c
+++ b/net/ipv4/netfilter/nf_reject_ipv4.c
@@ -14,7 +14,6 @@
#include <net/netfilter/ipv4/nf_reject.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_bridge.h>
-#include <net/netfilter/ipv4/nf_reject.h>
const struct tcphdr *nf_reject_ip_tcphdr_get(struct sk_buff *oldskb,
struct tcphdr *_oth, int hook)
diff --git a/net/ipv4/netfilter/nf_tables_arp.c b/net/ipv4/netfilter/nf_tables_arp.c
index 9d09d4f59545..cd84d4295a20 100644
--- a/net/ipv4/netfilter/nf_tables_arp.c
+++ b/net/ipv4/netfilter/nf_tables_arp.c
@@ -57,7 +57,7 @@ err:
static void nf_tables_arp_exit_net(struct net *net)
{
- nft_unregister_afinfo(net->nft.arp);
+ nft_unregister_afinfo(net, net->nft.arp);
kfree(net->nft.arp);
}
diff --git a/net/ipv4/netfilter/nf_tables_ipv4.c b/net/ipv4/netfilter/nf_tables_ipv4.c
index ca9dc3c46c4f..e44ba3b12fbb 100644
--- a/net/ipv4/netfilter/nf_tables_ipv4.c
+++ b/net/ipv4/netfilter/nf_tables_ipv4.c
@@ -78,7 +78,7 @@ err:
static void nf_tables_ipv4_exit_net(struct net *net)
{
- nft_unregister_afinfo(net->nft.ipv4);
+ nft_unregister_afinfo(net, net->nft.ipv4);
kfree(net->nft.ipv4);
}
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index e89094ab5ddb..c117b21b937d 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -1063,6 +1063,7 @@ static struct sock *ping_get_idx(struct seq_file *seq, loff_t pos)
}
void *ping_seq_start(struct seq_file *seq, loff_t *pos, sa_family_t family)
+ __acquires(ping_table.lock)
{
struct ping_iter_state *state = seq->private;
state->bucket = 0;
@@ -1094,6 +1095,7 @@ void *ping_seq_next(struct seq_file *seq, void *v, loff_t *pos)
EXPORT_SYMBOL_GPL(ping_seq_next);
void ping_seq_stop(struct seq_file *seq, void *v)
+ __releases(ping_table.lock)
{
read_unlock_bh(&ping_table.lock);
}
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 63e5be0abd86..bc35f1842512 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -601,8 +601,11 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
(inet->hdrincl ? FLOWI_FLAG_KNOWN_NH : 0),
daddr, saddr, 0, 0);
- if (!saddr && ipc.oif)
- l3mdev_get_saddr(net, ipc.oif, &fl4);
+ if (!saddr && ipc.oif) {
+ err = l3mdev_get_saddr(net, ipc.oif, &fl4);
+ if (err < 0)
+ goto done;
+ }
if (!inet->hdrincl) {
rfv.msg = msg;
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index 4cbe9f0a4281..643a86c49020 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -351,7 +351,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
treq->snt_synack.v64 = 0;
treq->tfo_listener = false;
- ireq->ir_iif = sk->sk_bound_dev_if;
+ ireq->ir_iif = inet_request_bound_dev_if(sk, skb);
/* We throwed the options of the initial SYN away, so we hope
* the ACK carries the same options again (see RFC1122 4.2.3.8)
@@ -371,7 +371,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
* hasn't changed since we received the original syn, but I see
* no easy way to do this.
*/
- flowi4_init_output(&fl4, sk->sk_bound_dev_if, ireq->ir_mark,
+ flowi4_init_output(&fl4, ireq->ir_iif, ireq->ir_mark,
RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, IPPROTO_TCP,
inet_sk_flowi_flags(sk),
opt->srr ? opt->faddr : ireq->ir_rmt_addr,
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index a0bd7a55193e..46ce410703b1 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -337,27 +337,6 @@ static struct ctl_table ipv4_table[] = {
.proc_handler = proc_dointvec
},
{
- .procname = "tcp_keepalive_time",
- .data = &sysctl_tcp_keepalive_time,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_jiffies,
- },
- {
- .procname = "tcp_keepalive_probes",
- .data = &sysctl_tcp_keepalive_probes,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec
- },
- {
- .procname = "tcp_keepalive_intvl",
- .data = &sysctl_tcp_keepalive_intvl,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_jiffies,
- },
- {
.procname = "tcp_retries1",
.data = &sysctl_tcp_retries1,
.maxlen = sizeof(int),
@@ -915,6 +894,17 @@ static struct ctl_table ipv4_net_table[] = {
.mode = 0644,
.proc_handler = proc_dointvec,
},
+#ifdef CONFIG_NET_L3_MASTER_DEV
+ {
+ .procname = "tcp_l3mdev_accept",
+ .data = &init_net.ipv4.sysctl_tcp_l3mdev_accept,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &zero,
+ .extra2 = &one,
+ },
+#endif
{
.procname = "tcp_mtu_probing",
.data = &init_net.ipv4.sysctl_tcp_mtu_probing,
@@ -950,6 +940,27 @@ static struct ctl_table ipv4_net_table[] = {
.mode = 0644,
.proc_handler = proc_dointvec
},
+ {
+ .procname = "tcp_keepalive_time",
+ .data = &init_net.ipv4.sysctl_tcp_keepalive_time,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_jiffies,
+ },
+ {
+ .procname = "tcp_keepalive_probes",
+ .data = &init_net.ipv4.sysctl_tcp_keepalive_probes,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec
+ },
+ {
+ .procname = "tcp_keepalive_intvl",
+ .data = &init_net.ipv4.sysctl_tcp_keepalive_intvl,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_jiffies,
+ },
{ }
};
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index c1728771cf89..fd17eec93525 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -422,7 +422,8 @@ void tcp_init_sock(struct sock *sk)
sk->sk_rcvbuf = sysctl_tcp_rmem[1];
local_bh_disable();
- sock_update_memcg(sk);
+ if (mem_cgroup_sockets_enabled)
+ sock_update_memcg(sk);
sk_sockets_allocated_inc(sk);
local_bh_enable();
}
@@ -517,8 +518,7 @@ unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
if (sk_stream_is_writeable(sk)) {
mask |= POLLOUT | POLLWRNORM;
} else { /* send SIGIO later */
- set_bit(SOCK_ASYNC_NOSPACE,
- &sk->sk_socket->flags);
+ sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
/* Race breaker. If space is freed after
@@ -906,7 +906,7 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset,
goto out_err;
}
- clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+ sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk);
mss_now = tcp_send_mss(sk, &size_goal, flags);
copied = 0;
@@ -1019,7 +1019,7 @@ int tcp_sendpage(struct sock *sk, struct page *page, int offset,
ssize_t res;
if (!(sk->sk_route_caps & NETIF_F_SG) ||
- !(sk->sk_route_caps & NETIF_F_ALL_CSUM))
+ !sk_check_csum_caps(sk))
return sock_no_sendpage(sk->sk_socket, page, offset, size,
flags);
@@ -1134,7 +1134,7 @@ int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
}
/* This should be in poll */
- clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+ sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk);
mss_now = tcp_send_mss(sk, &size_goal, flags);
@@ -1176,7 +1176,7 @@ new_segment:
/*
* Check whether we can use HW checksum.
*/
- if (sk->sk_route_caps & NETIF_F_ALL_CSUM)
+ if (sk_check_csum_caps(sk))
skb->ip_summed = CHECKSUM_PARTIAL;
skb_entail(sk, skb);
@@ -3081,6 +3081,52 @@ void tcp_done(struct sock *sk)
}
EXPORT_SYMBOL_GPL(tcp_done);
+int tcp_abort(struct sock *sk, int err)
+{
+ if (!sk_fullsock(sk)) {
+ if (sk->sk_state == TCP_NEW_SYN_RECV) {
+ struct request_sock *req = inet_reqsk(sk);
+
+ local_bh_disable();
+ inet_csk_reqsk_queue_drop_and_put(req->rsk_listener,
+ req);
+ local_bh_enable();
+ return 0;
+ }
+ sock_gen_put(sk);
+ return -EOPNOTSUPP;
+ }
+
+ /* Don't race with userspace socket closes such as tcp_close. */
+ lock_sock(sk);
+
+ if (sk->sk_state == TCP_LISTEN) {
+ tcp_set_state(sk, TCP_CLOSE);
+ inet_csk_listen_stop(sk);
+ }
+
+ /* Don't race with BH socket closes such as inet_csk_listen_stop. */
+ local_bh_disable();
+ bh_lock_sock(sk);
+
+ if (!sock_flag(sk, SOCK_DEAD)) {
+ sk->sk_err = err;
+ /* This barrier is coupled with smp_rmb() in tcp_poll() */
+ smp_wmb();
+ sk->sk_error_report(sk);
+ if (tcp_need_reset(sk->sk_state))
+ tcp_send_active_reset(sk, GFP_ATOMIC);
+ tcp_done(sk);
+ }
+
+ bh_unlock_sock(sk);
+ local_bh_enable();
+ release_sock(sk);
+ sock_put(sk);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tcp_abort);
+
extern struct tcp_congestion_ops tcp_reno;
static __initdata unsigned long thash_entries;
diff --git a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c
index b31604086edd..4d610934fb39 100644
--- a/net/ipv4/tcp_diag.c
+++ b/net/ipv4/tcp_diag.c
@@ -10,6 +10,8 @@
*/
#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/sock_diag.h>
#include <linux/inet_diag.h>
#include <linux/tcp.h>
@@ -46,12 +48,29 @@ static int tcp_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh,
return inet_diag_dump_one_icsk(&tcp_hashinfo, in_skb, nlh, req);
}
+#ifdef CONFIG_INET_DIAG_DESTROY
+static int tcp_diag_destroy(struct sk_buff *in_skb,
+ const struct inet_diag_req_v2 *req)
+{
+ struct net *net = sock_net(in_skb->sk);
+ struct sock *sk = inet_diag_find_one_icsk(net, &tcp_hashinfo, req);
+
+ if (IS_ERR(sk))
+ return PTR_ERR(sk);
+
+ return sock_diag_destroy(sk, ECONNABORTED);
+}
+#endif
+
static const struct inet_diag_handler tcp_diag_handler = {
.dump = tcp_diag_dump,
.dump_one = tcp_diag_dump_one,
.idiag_get_info = tcp_diag_get_info,
.idiag_type = IPPROTO_TCP,
.idiag_info_size = sizeof(struct tcp_info),
+#ifdef CONFIG_INET_DIAG_DESTROY
+ .destroy = tcp_diag_destroy,
+#endif
};
static int __init tcp_diag_init(void)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index fdd88c3803a6..0003d409fec5 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -2478,6 +2478,9 @@ static void tcp_cwnd_reduction(struct sock *sk, const int prior_unsacked,
int newly_acked_sacked = prior_unsacked -
(tp->packets_out - tp->sacked_out);
+ if (newly_acked_sacked <= 0 || WARN_ON_ONCE(!tp->prior_cwnd))
+ return;
+
tp->prr_delivered += newly_acked_sacked;
if (delta < 0) {
u64 dividend = (u64)tp->snd_ssthresh * tp->prr_delivered +
@@ -4481,19 +4484,34 @@ static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int
int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size)
{
struct sk_buff *skb;
+ int err = -ENOMEM;
+ int data_len = 0;
bool fragstolen;
if (size == 0)
return 0;
- skb = alloc_skb(size, sk->sk_allocation);
+ if (size > PAGE_SIZE) {
+ int npages = min_t(size_t, size >> PAGE_SHIFT, MAX_SKB_FRAGS);
+
+ data_len = npages << PAGE_SHIFT;
+ size = data_len + (size & ~PAGE_MASK);
+ }
+ skb = alloc_skb_with_frags(size - data_len, data_len,
+ PAGE_ALLOC_COSTLY_ORDER,
+ &err, sk->sk_allocation);
if (!skb)
goto err;
+ skb_put(skb, size - data_len);
+ skb->data_len = data_len;
+ skb->len = size;
+
if (tcp_try_rmem_schedule(sk, skb, skb->truesize))
goto err_free;
- if (memcpy_from_msg(skb_put(skb, size), msg, size))
+ err = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, size);
+ if (err)
goto err_free;
TCP_SKB_CB(skb)->seq = tcp_sk(sk)->rcv_nxt;
@@ -4509,7 +4527,8 @@ int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size)
err_free:
kfree_skb(skb);
err:
- return -ENOMEM;
+ return err;
+
}
static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
@@ -5667,6 +5686,7 @@ discard:
}
tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1;
+ tp->copied_seq = tp->rcv_nxt;
tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1;
/* RFC1323: The window in SYN & SYN/ACK segments is
@@ -6187,7 +6207,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
tcp_openreq_init(req, &tmp_opt, skb, sk);
/* Note: tcp_v6_init_req() might override ir_iif for link locals */
- inet_rsk(req)->ir_iif = sk->sk_bound_dev_if;
+ inet_rsk(req)->ir_iif = inet_request_bound_dev_if(sk, skb);
af_ops->init_req(req, sk, skb);
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index ba09016d1bfd..c7d1fb50f381 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -587,7 +587,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
} rep;
struct ip_reply_arg arg;
#ifdef CONFIG_TCP_MD5SIG
- struct tcp_md5sig_key *key;
+ struct tcp_md5sig_key *key = NULL;
const __u8 *hash_location = NULL;
unsigned char newhash[16];
int genhash;
@@ -627,7 +627,10 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev);
#ifdef CONFIG_TCP_MD5SIG
hash_location = tcp_parse_md5sig_option(th);
- if (!sk && hash_location) {
+ if (sk && sk_fullsock(sk)) {
+ key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)
+ &ip_hdr(skb)->saddr, AF_INET);
+ } else if (hash_location) {
/*
* active side is lost. Try to find listening socket through
* source port, and then find md5 key through listening socket.
@@ -651,10 +654,6 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, skb);
if (genhash || memcmp(hash_location, newhash, 16) != 0)
goto release_sk1;
- } else {
- key = sk ? tcp_md5_do_lookup(sk, (union tcp_md5_addr *)
- &ip_hdr(skb)->saddr,
- AF_INET) : NULL;
}
if (key) {
@@ -675,7 +674,8 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
ip_hdr(skb)->saddr, /* XXX */
arg.iov[0].iov_len, IPPROTO_TCP, 0);
arg.csumoffset = offsetof(struct tcphdr, check) / 2;
- arg.flags = (sk && inet_sk(sk)->transparent) ? IP_REPLY_ARG_NOSRCCHECK : 0;
+ arg.flags = (sk && inet_sk_transparent(sk)) ? IP_REPLY_ARG_NOSRCCHECK : 0;
+
/* When socket is gone, all binding information is lost.
* routing might fail in this case. No choice here, if we choose to force
* input interface, we will misroute in case of asymmetric route.
@@ -683,6 +683,9 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
if (sk)
arg.bound_dev_if = sk->sk_bound_dev_if;
+ BUILD_BUG_ON(offsetof(struct sock, sk_bound_dev_if) !=
+ offsetof(struct inet_timewait_sock, tw_bound_dev_if));
+
arg.tos = ip_hdr(skb)->tos;
ip_send_unicast_reply(*this_cpu_ptr(net->ipv4.tcp_sk),
skb, &TCP_SKB_CB(skb)->header.h4.opt,
@@ -921,7 +924,8 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
}
md5sig = rcu_dereference_protected(tp->md5sig_info,
- sock_owned_by_user(sk));
+ sock_owned_by_user(sk) ||
+ lockdep_is_held(&sk->sk_lock.slock));
if (!md5sig) {
md5sig = kmalloc(sizeof(*md5sig), gfp);
if (!md5sig)
@@ -1275,6 +1279,7 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
ireq = inet_rsk(req);
sk_daddr_set(newsk, ireq->ir_rmt_addr);
sk_rcv_saddr_set(newsk, ireq->ir_loc_addr);
+ newsk->sk_bound_dev_if = ireq->ir_iif;
newinet->inet_saddr = ireq->ir_loc_addr;
inet_opt = ireq->opt;
rcu_assign_pointer(newinet->inet_opt, inet_opt);
@@ -1492,7 +1497,7 @@ bool tcp_prequeue(struct sock *sk, struct sk_buff *skb)
if (likely(sk->sk_rx_dst))
skb_dst_drop(skb);
else
- skb_dst_force(skb);
+ skb_dst_force_safe(skb);
__skb_queue_tail(&tp->ucopy.prequeue, skb);
tp->ucopy.memory += skb->truesize;
@@ -1704,7 +1709,9 @@ do_time_wait:
tcp_v4_timewait_ack(sk, skb);
break;
case TCP_TW_RST:
- goto no_tcp_socket;
+ tcp_v4_send_reset(sk, skb);
+ inet_twsk_deschedule_put(inet_twsk(sk));
+ goto discard_it;
case TCP_TW_SUCCESS:;
}
goto discard_it;
@@ -1720,8 +1727,7 @@ void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)
{
struct dst_entry *dst = skb_dst(skb);
- if (dst) {
- dst_hold(dst);
+ if (dst && dst_hold_safe(dst)) {
sk->sk_rx_dst = dst;
inet_sk(sk)->rx_dst_ifindex = skb->skb_iif;
}
@@ -1812,7 +1818,9 @@ void tcp_v4_destroy_sock(struct sock *sk)
tcp_saved_syn_free(tp);
sk_sockets_allocated_dec(sk);
- sock_release_memcg(sk);
+
+ if (mem_cgroup_sockets_enabled && sk->sk_memcg)
+ sock_release_memcg(sk);
}
EXPORT_SYMBOL(tcp_v4_destroy_sock);
@@ -2336,11 +2344,7 @@ struct proto tcp_prot = {
.compat_setsockopt = compat_tcp_setsockopt,
.compat_getsockopt = compat_tcp_getsockopt,
#endif
-#ifdef CONFIG_MEMCG_KMEM
- .init_cgroup = tcp_init_cgroup,
- .destroy_cgroup = tcp_destroy_cgroup,
- .proto_cgroup = tcp_proto_cgroup,
-#endif
+ .diag_destroy = tcp_abort,
};
EXPORT_SYMBOL(tcp_prot);
@@ -2378,6 +2382,10 @@ static int __net_init tcp_sk_init(struct net *net)
net->ipv4.sysctl_tcp_probe_threshold = TCP_PROBE_THRESHOLD;
net->ipv4.sysctl_tcp_probe_interval = TCP_PROBE_INTERVAL;
+ net->ipv4.sysctl_tcp_keepalive_time = TCP_KEEPALIVE_TIME;
+ net->ipv4.sysctl_tcp_keepalive_probes = TCP_KEEPALIVE_PROBES;
+ net->ipv4.sysctl_tcp_keepalive_intvl = TCP_KEEPALIVE_INTVL;
+
return 0;
fail:
tcp_sk_exit(net);
diff --git a/net/ipv4/tcp_memcontrol.c b/net/ipv4/tcp_memcontrol.c
index 2379c1b4efb2..18bc7f745e9c 100644
--- a/net/ipv4/tcp_memcontrol.c
+++ b/net/ipv4/tcp_memcontrol.c
@@ -8,75 +8,49 @@
int tcp_init_cgroup(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
{
+ struct mem_cgroup *parent = parent_mem_cgroup(memcg);
+ struct page_counter *counter_parent = NULL;
/*
* The root cgroup does not use page_counters, but rather,
* rely on the data already collected by the network
* subsystem
*/
- struct mem_cgroup *parent = parent_mem_cgroup(memcg);
- struct page_counter *counter_parent = NULL;
- struct cg_proto *cg_proto, *parent_cg;
-
- cg_proto = tcp_prot.proto_cgroup(memcg);
- if (!cg_proto)
+ if (memcg == root_mem_cgroup)
return 0;
- cg_proto->sysctl_mem[0] = sysctl_tcp_mem[0];
- cg_proto->sysctl_mem[1] = sysctl_tcp_mem[1];
- cg_proto->sysctl_mem[2] = sysctl_tcp_mem[2];
- cg_proto->memory_pressure = 0;
- cg_proto->memcg = memcg;
+ memcg->tcp_mem.memory_pressure = 0;
- parent_cg = tcp_prot.proto_cgroup(parent);
- if (parent_cg)
- counter_parent = &parent_cg->memory_allocated;
+ if (parent)
+ counter_parent = &parent->tcp_mem.memory_allocated;
- page_counter_init(&cg_proto->memory_allocated, counter_parent);
- percpu_counter_init(&cg_proto->sockets_allocated, 0, GFP_KERNEL);
+ page_counter_init(&memcg->tcp_mem.memory_allocated, counter_parent);
return 0;
}
-EXPORT_SYMBOL(tcp_init_cgroup);
void tcp_destroy_cgroup(struct mem_cgroup *memcg)
{
- struct cg_proto *cg_proto;
-
- cg_proto = tcp_prot.proto_cgroup(memcg);
- if (!cg_proto)
+ if (memcg == root_mem_cgroup)
return;
- percpu_counter_destroy(&cg_proto->sockets_allocated);
-
- if (test_bit(MEMCG_SOCK_ACTIVATED, &cg_proto->flags))
- static_key_slow_dec(&memcg_socket_limit_enabled);
-
+ if (memcg->tcp_mem.active)
+ static_branch_dec(&memcg_sockets_enabled_key);
}
-EXPORT_SYMBOL(tcp_destroy_cgroup);
static int tcp_update_limit(struct mem_cgroup *memcg, unsigned long nr_pages)
{
- struct cg_proto *cg_proto;
- int i;
int ret;
- cg_proto = tcp_prot.proto_cgroup(memcg);
- if (!cg_proto)
+ if (memcg == root_mem_cgroup)
return -EINVAL;
- ret = page_counter_limit(&cg_proto->memory_allocated, nr_pages);
+ ret = page_counter_limit(&memcg->tcp_mem.memory_allocated, nr_pages);
if (ret)
return ret;
- for (i = 0; i < 3; i++)
- cg_proto->sysctl_mem[i] = min_t(long, nr_pages,
- sysctl_tcp_mem[i]);
-
- if (nr_pages == PAGE_COUNTER_MAX)
- clear_bit(MEMCG_SOCK_ACTIVE, &cg_proto->flags);
- else {
+ if (!memcg->tcp_mem.active) {
/*
- * The active bit needs to be written after the static_key
+ * The active flag needs to be written after the static_key
* update. This is what guarantees that the socket activation
* function is the last one to run. See sock_update_memcg() for
* details, and note that we don't mark any socket as belonging
@@ -90,14 +64,9 @@ static int tcp_update_limit(struct mem_cgroup *memcg, unsigned long nr_pages)
* We never race with the readers in sock_update_memcg(),
* because when this value change, the code to process it is not
* patched in yet.
- *
- * The activated bit is used to guarantee that no two writers
- * will do the update in the same memcg. Without that, we can't
- * properly shutdown the static key.
*/
- if (!test_and_set_bit(MEMCG_SOCK_ACTIVATED, &cg_proto->flags))
- static_key_slow_inc(&memcg_socket_limit_enabled);
- set_bit(MEMCG_SOCK_ACTIVE, &cg_proto->flags);
+ static_branch_inc(&memcg_sockets_enabled_key);
+ memcg->tcp_mem.active = true;
}
return 0;
@@ -141,32 +110,32 @@ static ssize_t tcp_cgroup_write(struct kernfs_open_file *of,
static u64 tcp_cgroup_read(struct cgroup_subsys_state *css, struct cftype *cft)
{
struct mem_cgroup *memcg = mem_cgroup_from_css(css);
- struct cg_proto *cg_proto = tcp_prot.proto_cgroup(memcg);
u64 val;
switch (cft->private) {
case RES_LIMIT:
- if (!cg_proto)
- return PAGE_COUNTER_MAX;
- val = cg_proto->memory_allocated.limit;
+ if (memcg == root_mem_cgroup)
+ val = PAGE_COUNTER_MAX;
+ else
+ val = memcg->tcp_mem.memory_allocated.limit;
val *= PAGE_SIZE;
break;
case RES_USAGE:
- if (!cg_proto)
+ if (memcg == root_mem_cgroup)
val = atomic_long_read(&tcp_memory_allocated);
else
- val = page_counter_read(&cg_proto->memory_allocated);
+ val = page_counter_read(&memcg->tcp_mem.memory_allocated);
val *= PAGE_SIZE;
break;
case RES_FAILCNT:
- if (!cg_proto)
+ if (memcg == root_mem_cgroup)
return 0;
- val = cg_proto->memory_allocated.failcnt;
+ val = memcg->tcp_mem.memory_allocated.failcnt;
break;
case RES_MAX_USAGE:
- if (!cg_proto)
+ if (memcg == root_mem_cgroup)
return 0;
- val = cg_proto->memory_allocated.watermark;
+ val = memcg->tcp_mem.memory_allocated.watermark;
val *= PAGE_SIZE;
break;
default:
@@ -179,19 +148,17 @@ static ssize_t tcp_cgroup_reset(struct kernfs_open_file *of,
char *buf, size_t nbytes, loff_t off)
{
struct mem_cgroup *memcg;
- struct cg_proto *cg_proto;
memcg = mem_cgroup_from_css(of_css(of));
- cg_proto = tcp_prot.proto_cgroup(memcg);
- if (!cg_proto)
+ if (memcg == root_mem_cgroup)
return nbytes;
switch (of_cft(of)->private) {
case RES_MAX_USAGE:
- page_counter_reset_watermark(&cg_proto->memory_allocated);
+ page_counter_reset_watermark(&memcg->tcp_mem.memory_allocated);
break;
case RES_FAILCNT:
- cg_proto->memory_allocated.failcnt = 0;
+ memcg->tcp_mem.memory_allocated.failcnt = 0;
break;
}
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index ac6b1961ffeb..75632a925824 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -131,7 +131,7 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb,
goto kill;
if (th->syn && !before(TCP_SKB_CB(skb)->seq, tcptw->tw_rcv_nxt))
- goto kill_with_rst;
+ return TCP_TW_RST;
/* Dup ACK? */
if (!th->ack ||
@@ -145,11 +145,8 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb,
* reset.
*/
if (!th->fin ||
- TCP_SKB_CB(skb)->end_seq != tcptw->tw_rcv_nxt + 1) {
-kill_with_rst:
- inet_twsk_deschedule_put(tw);
+ TCP_SKB_CB(skb)->end_seq != tcptw->tw_rcv_nxt + 1)
return TCP_TW_RST;
- }
/* FIN arrived, enter true time-wait state. */
tw->tw_substate = TCP_TIME_WAIT;
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index cb7ca569052c..fda379cd600d 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2296,7 +2296,7 @@ void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss,
return;
if (tcp_write_xmit(sk, cur_mss, nonagle, 0,
- sk_gfp_atomic(sk, GFP_ATOMIC)))
+ sk_gfp_mask(sk, GFP_ATOMIC)))
tcp_check_probe_timer(sk);
}
@@ -2813,13 +2813,16 @@ begin_fwd:
*/
void sk_forced_mem_schedule(struct sock *sk, int size)
{
- int amt, status;
+ int amt;
if (size <= sk->sk_forward_alloc)
return;
amt = sk_mem_pages(size);
sk->sk_forward_alloc += amt * SK_MEM_QUANTUM;
- sk_memory_allocated_add(sk, amt, &status);
+ sk_memory_allocated_add(sk, amt);
+
+ if (mem_cgroup_sockets_enabled && sk->sk_memcg)
+ mem_cgroup_charge_skmem(sk->sk_memcg, amt);
}
/* Send a FIN. The caller locks the socket for us.
@@ -3150,7 +3153,7 @@ static int tcp_send_syn_data(struct sock *sk, struct sk_buff *syn)
{
struct tcp_sock *tp = tcp_sk(sk);
struct tcp_fastopen_request *fo = tp->fastopen_req;
- int syn_loss = 0, space, err = 0, copied;
+ int syn_loss = 0, space, err = 0;
unsigned long last_syn_loss = 0;
struct sk_buff *syn_data;
@@ -3188,17 +3191,18 @@ static int tcp_send_syn_data(struct sock *sk, struct sk_buff *syn)
goto fallback;
syn_data->ip_summed = CHECKSUM_PARTIAL;
memcpy(syn_data->cb, syn->cb, sizeof(syn->cb));
- copied = copy_from_iter(skb_put(syn_data, space), space,
- &fo->data->msg_iter);
- if (unlikely(!copied)) {
- kfree_skb(syn_data);
- goto fallback;
- }
- if (copied != space) {
- skb_trim(syn_data, copied);
- space = copied;
+ if (space) {
+ int copied = copy_from_iter(skb_put(syn_data, space), space,
+ &fo->data->msg_iter);
+ if (unlikely(!copied)) {
+ kfree_skb(syn_data);
+ goto fallback;
+ }
+ if (copied != space) {
+ skb_trim(syn_data, copied);
+ space = copied;
+ }
}
-
/* No more data pending in inet_wait_for_connect() */
if (space == fo->size)
fo->data = NULL;
@@ -3352,8 +3356,9 @@ void tcp_send_ack(struct sock *sk)
* tcp_transmit_skb() will set the ownership to this
* sock.
*/
- buff = alloc_skb(MAX_TCP_HEADER, sk_gfp_atomic(sk, GFP_ATOMIC));
- if (!buff) {
+ buff = alloc_skb(MAX_TCP_HEADER,
+ sk_gfp_mask(sk, GFP_ATOMIC | __GFP_NOWARN));
+ if (unlikely(!buff)) {
inet_csk_schedule_ack(sk);
inet_csk(sk)->icsk_ack.ato = TCP_ATO_MIN;
inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
@@ -3375,7 +3380,7 @@ void tcp_send_ack(struct sock *sk)
/* Send it off, this clears delayed acks for us. */
skb_mstamp_get(&buff->skb_mstamp);
- tcp_transmit_skb(sk, buff, 0, sk_gfp_atomic(sk, GFP_ATOMIC));
+ tcp_transmit_skb(sk, buff, 0, (__force gfp_t)0);
}
EXPORT_SYMBOL_GPL(tcp_send_ack);
@@ -3396,7 +3401,8 @@ static int tcp_xmit_probe_skb(struct sock *sk, int urgent, int mib)
struct sk_buff *skb;
/* We don't queue it, tcp_transmit_skb() sets ownership. */
- skb = alloc_skb(MAX_TCP_HEADER, sk_gfp_atomic(sk, GFP_ATOMIC));
+ skb = alloc_skb(MAX_TCP_HEADER,
+ sk_gfp_mask(sk, GFP_ATOMIC | __GFP_NOWARN));
if (!skb)
return -1;
@@ -3409,7 +3415,7 @@ static int tcp_xmit_probe_skb(struct sock *sk, int urgent, int mib)
tcp_init_nondata_skb(skb, tp->snd_una - !urgent, TCPHDR_ACK);
skb_mstamp_get(&skb->skb_mstamp);
NET_INC_STATS(sock_net(sk), mib);
- return tcp_transmit_skb(sk, skb, 0, GFP_ATOMIC);
+ return tcp_transmit_skb(sk, skb, 0, (__force gfp_t)0);
}
void tcp_send_window_probe(struct sock *sk)
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index c9c716a483e4..a4730a28b220 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -24,9 +24,6 @@
int sysctl_tcp_syn_retries __read_mostly = TCP_SYN_RETRIES;
int sysctl_tcp_synack_retries __read_mostly = TCP_SYNACK_RETRIES;
-int sysctl_tcp_keepalive_time __read_mostly = TCP_KEEPALIVE_TIME;
-int sysctl_tcp_keepalive_probes __read_mostly = TCP_KEEPALIVE_PROBES;
-int sysctl_tcp_keepalive_intvl __read_mostly = TCP_KEEPALIVE_INTVL;
int sysctl_tcp_retries1 __read_mostly = TCP_RETR1;
int sysctl_tcp_retries2 __read_mostly = TCP_RETR2;
int sysctl_tcp_orphan_retries __read_mostly;
@@ -168,7 +165,7 @@ static int tcp_write_timeout(struct sock *sk)
dst_negative_advice(sk);
if (tp->syn_fastopen || tp->syn_data)
tcp_fastopen_cache_set(sk, 0, NULL, true, 0);
- if (tp->syn_data)
+ if (tp->syn_data && icsk->icsk_retransmits == 1)
NET_INC_STATS_BH(sock_net(sk),
LINUX_MIB_TCPFASTOPENACTIVEFAIL);
}
@@ -176,6 +173,18 @@ static int tcp_write_timeout(struct sock *sk)
syn_set = true;
} else {
if (retransmits_timed_out(sk, sysctl_tcp_retries1, 0, 0)) {
+ /* Some middle-boxes may black-hole Fast Open _after_
+ * the handshake. Therefore we conservatively disable
+ * Fast Open on this path on recurring timeouts with
+ * few or zero bytes acked after Fast Open.
+ */
+ if (tp->syn_data_acked &&
+ tp->bytes_acked <= tp->rx_opt.mss_clamp) {
+ tcp_fastopen_cache_set(sk, 0, NULL, true, 0);
+ if (icsk->icsk_retransmits == sysctl_tcp_retries1)
+ NET_INC_STATS_BH(sock_net(sk),
+ LINUX_MIB_TCPFASTOPENACTIVEFAIL);
+ }
/* Black hole detection */
tcp_mtu_probing(icsk, sk);
diff --git a/net/ipv4/tcp_yeah.c b/net/ipv4/tcp_yeah.c
index 17d35662930d..3e6a472e6b88 100644
--- a/net/ipv4/tcp_yeah.c
+++ b/net/ipv4/tcp_yeah.c
@@ -219,7 +219,7 @@ static u32 tcp_yeah_ssthresh(struct sock *sk)
yeah->fast_count = 0;
yeah->reno_count = max(yeah->reno_count>>1, 2U);
- return tp->snd_cwnd - reduction;
+ return max_t(int, tp->snd_cwnd - reduction, 2);
}
static struct tcp_congestion_ops tcp_yeah __read_mostly = {
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 24ec14f9825c..dc45b538e237 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -100,7 +100,6 @@
#include <linux/slab.h>
#include <net/tcp_states.h>
#include <linux/skbuff.h>
-#include <linux/netdevice.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <net/net_namespace.h>
@@ -114,6 +113,7 @@
#include <trace/events/skb.h>
#include <net/busy_poll.h>
#include "udp_impl.h"
+#include <net/sock_reuseport.h>
struct udp_table udp_table __read_mostly;
EXPORT_SYMBOL(udp_table);
@@ -138,7 +138,8 @@ static int udp_lib_lport_inuse(struct net *net, __u16 num,
unsigned long *bitmap,
struct sock *sk,
int (*saddr_comp)(const struct sock *sk1,
- const struct sock *sk2),
+ const struct sock *sk2,
+ bool match_wildcard),
unsigned int log)
{
struct sock *sk2;
@@ -153,8 +154,9 @@ static int udp_lib_lport_inuse(struct net *net, __u16 num,
(!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if ||
sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
(!sk2->sk_reuseport || !sk->sk_reuseport ||
+ rcu_access_pointer(sk->sk_reuseport_cb) ||
!uid_eq(uid, sock_i_uid(sk2))) &&
- saddr_comp(sk, sk2)) {
+ saddr_comp(sk, sk2, true)) {
if (!bitmap)
return 1;
__set_bit(udp_sk(sk2)->udp_port_hash >> log, bitmap);
@@ -171,7 +173,8 @@ static int udp_lib_lport_inuse2(struct net *net, __u16 num,
struct udp_hslot *hslot2,
struct sock *sk,
int (*saddr_comp)(const struct sock *sk1,
- const struct sock *sk2))
+ const struct sock *sk2,
+ bool match_wildcard))
{
struct sock *sk2;
struct hlist_nulls_node *node;
@@ -187,8 +190,9 @@ static int udp_lib_lport_inuse2(struct net *net, __u16 num,
(!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if ||
sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
(!sk2->sk_reuseport || !sk->sk_reuseport ||
+ rcu_access_pointer(sk->sk_reuseport_cb) ||
!uid_eq(uid, sock_i_uid(sk2))) &&
- saddr_comp(sk, sk2)) {
+ saddr_comp(sk, sk2, true)) {
res = 1;
break;
}
@@ -197,6 +201,35 @@ static int udp_lib_lport_inuse2(struct net *net, __u16 num,
return res;
}
+static int udp_reuseport_add_sock(struct sock *sk, struct udp_hslot *hslot,
+ int (*saddr_same)(const struct sock *sk1,
+ const struct sock *sk2,
+ bool match_wildcard))
+{
+ struct net *net = sock_net(sk);
+ struct hlist_nulls_node *node;
+ kuid_t uid = sock_i_uid(sk);
+ struct sock *sk2;
+
+ sk_nulls_for_each(sk2, node, &hslot->head) {
+ if (net_eq(sock_net(sk2), net) &&
+ sk2 != sk &&
+ sk2->sk_family == sk->sk_family &&
+ ipv6_only_sock(sk2) == ipv6_only_sock(sk) &&
+ (udp_sk(sk2)->udp_port_hash == udp_sk(sk)->udp_port_hash) &&
+ (sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
+ sk2->sk_reuseport && uid_eq(uid, sock_i_uid(sk2)) &&
+ (*saddr_same)(sk, sk2, false)) {
+ return reuseport_add_sock(sk, sk2);
+ }
+ }
+
+ /* Initial allocation may have already happened via setsockopt */
+ if (!rcu_access_pointer(sk->sk_reuseport_cb))
+ return reuseport_alloc(sk);
+ return 0;
+}
+
/**
* udp_lib_get_port - UDP/-Lite port lookup for IPv4 and IPv6
*
@@ -208,7 +241,8 @@ static int udp_lib_lport_inuse2(struct net *net, __u16 num,
*/
int udp_lib_get_port(struct sock *sk, unsigned short snum,
int (*saddr_comp)(const struct sock *sk1,
- const struct sock *sk2),
+ const struct sock *sk2,
+ bool match_wildcard),
unsigned int hash2_nulladdr)
{
struct udp_hslot *hslot, *hslot2;
@@ -291,6 +325,14 @@ found:
udp_sk(sk)->udp_port_hash = snum;
udp_sk(sk)->udp_portaddr_hash ^= snum;
if (sk_unhashed(sk)) {
+ if (sk->sk_reuseport &&
+ udp_reuseport_add_sock(sk, hslot, saddr_comp)) {
+ inet_sk(sk)->inet_num = 0;
+ udp_sk(sk)->udp_port_hash = 0;
+ udp_sk(sk)->udp_portaddr_hash ^= snum;
+ goto fail_unlock;
+ }
+
sk_nulls_add_node_rcu(sk, &hslot->head);
hslot->count++;
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
@@ -310,13 +352,22 @@ fail:
}
EXPORT_SYMBOL(udp_lib_get_port);
-static int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2)
+/* match_wildcard == true: 0.0.0.0 equals to any IPv4 addresses
+ * match_wildcard == false: addresses must be exactly the same, i.e.
+ * 0.0.0.0 only equals to 0.0.0.0
+ */
+static int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2,
+ bool match_wildcard)
{
struct inet_sock *inet1 = inet_sk(sk1), *inet2 = inet_sk(sk2);
- return (!ipv6_only_sock(sk2) &&
- (!inet1->inet_rcv_saddr || !inet2->inet_rcv_saddr ||
- inet1->inet_rcv_saddr == inet2->inet_rcv_saddr));
+ if (!ipv6_only_sock(sk2)) {
+ if (inet1->inet_rcv_saddr == inet2->inet_rcv_saddr)
+ return 1;
+ if (!inet1->inet_rcv_saddr || !inet2->inet_rcv_saddr)
+ return match_wildcard;
+ }
+ return 0;
}
static u32 udp4_portaddr_hash(const struct net *net, __be32 saddr,
@@ -442,7 +493,8 @@ static u32 udp_ehashfn(const struct net *net, const __be32 laddr,
static struct sock *udp4_lib_lookup2(struct net *net,
__be32 saddr, __be16 sport,
__be32 daddr, unsigned int hnum, int dif,
- struct udp_hslot *hslot2, unsigned int slot2)
+ struct udp_hslot *hslot2, unsigned int slot2,
+ struct sk_buff *skb)
{
struct sock *sk, *result;
struct hlist_nulls_node *node;
@@ -460,8 +512,15 @@ begin:
badness = score;
reuseport = sk->sk_reuseport;
if (reuseport) {
+ struct sock *sk2;
hash = udp_ehashfn(net, daddr, hnum,
saddr, sport);
+ sk2 = reuseport_select_sock(sk, hash, skb,
+ sizeof(struct udphdr));
+ if (sk2) {
+ result = sk2;
+ goto found;
+ }
matches = 1;
}
} else if (score == badness && reuseport) {
@@ -479,6 +538,7 @@ begin:
if (get_nulls_value(node) != slot2)
goto begin;
if (result) {
+found:
if (unlikely(!atomic_inc_not_zero_hint(&result->sk_refcnt, 2)))
result = NULL;
else if (unlikely(compute_score2(result, net, saddr, sport,
@@ -495,7 +555,7 @@ begin:
*/
struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr,
__be16 sport, __be32 daddr, __be16 dport,
- int dif, struct udp_table *udptable)
+ int dif, struct udp_table *udptable, struct sk_buff *skb)
{
struct sock *sk, *result;
struct hlist_nulls_node *node;
@@ -515,7 +575,7 @@ struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr,
result = udp4_lib_lookup2(net, saddr, sport,
daddr, hnum, dif,
- hslot2, slot2);
+ hslot2, slot2, skb);
if (!result) {
hash2 = udp4_portaddr_hash(net, htonl(INADDR_ANY), hnum);
slot2 = hash2 & udptable->mask;
@@ -525,7 +585,7 @@ struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr,
result = udp4_lib_lookup2(net, saddr, sport,
htonl(INADDR_ANY), hnum, dif,
- hslot2, slot2);
+ hslot2, slot2, skb);
}
rcu_read_unlock();
return result;
@@ -541,8 +601,15 @@ begin:
badness = score;
reuseport = sk->sk_reuseport;
if (reuseport) {
+ struct sock *sk2;
hash = udp_ehashfn(net, daddr, hnum,
saddr, sport);
+ sk2 = reuseport_select_sock(sk, hash, skb,
+ sizeof(struct udphdr));
+ if (sk2) {
+ result = sk2;
+ goto found;
+ }
matches = 1;
}
} else if (score == badness && reuseport) {
@@ -561,6 +628,7 @@ begin:
goto begin;
if (result) {
+found:
if (unlikely(!atomic_inc_not_zero_hint(&result->sk_refcnt, 2)))
result = NULL;
else if (unlikely(compute_score(result, net, saddr, hnum, sport,
@@ -582,13 +650,14 @@ static inline struct sock *__udp4_lib_lookup_skb(struct sk_buff *skb,
return __udp4_lib_lookup(dev_net(skb_dst(skb)->dev), iph->saddr, sport,
iph->daddr, dport, inet_iif(skb),
- udptable);
+ udptable, skb);
}
struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport,
__be32 daddr, __be16 dport, int dif)
{
- return __udp4_lib_lookup(net, saddr, sport, daddr, dport, dif, &udp_table);
+ return __udp4_lib_lookup(net, saddr, sport, daddr, dport, dif,
+ &udp_table, NULL);
}
EXPORT_SYMBOL_GPL(udp4_lib_lookup);
@@ -636,7 +705,8 @@ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
struct net *net = dev_net(skb->dev);
sk = __udp4_lib_lookup(net, iph->daddr, uh->dest,
- iph->saddr, uh->source, skb->dev->ifindex, udptable);
+ iph->saddr, uh->source, skb->dev->ifindex, udptable,
+ NULL);
if (!sk) {
ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS);
return; /* No socket for error */
@@ -773,7 +843,8 @@ void udp_set_csum(bool nocheck, struct sk_buff *skb,
else if (skb_is_gso(skb))
uh->check = ~udp_v4_check(len, saddr, daddr, 0);
else if (skb_dst(skb) && skb_dst(skb)->dev &&
- (skb_dst(skb)->dev->features & NETIF_F_V4_CSUM)) {
+ (skb_dst(skb)->dev->features &
+ (NETIF_F_IP_CSUM | NETIF_F_HW_CSUM))) {
BUG_ON(skb->ip_summed == CHECKSUM_PARTIAL);
@@ -1026,8 +1097,11 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
flow_flags,
faddr, saddr, dport, inet->inet_sport);
- if (!saddr && ipc.oif)
- l3mdev_get_saddr(net, ipc.oif, fl4);
+ if (!saddr && ipc.oif) {
+ err = l3mdev_get_saddr(net, ipc.oif, fl4);
+ if (err < 0)
+ goto out;
+ }
security_sk_classify_flow(sk, flowi4_to_flowi(fl4));
rt = ip_route_output_flow(net, fl4, sk);
@@ -1271,6 +1345,7 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock,
int peeked, off = 0;
int err;
int is_udplite = IS_UDPLITE(sk);
+ bool checksum_valid = false;
bool slow;
if (flags & MSG_ERRQUEUE)
@@ -1296,11 +1371,12 @@ try_again:
*/
if (copied < ulen || UDP_SKB_CB(skb)->partial_cov) {
- if (udp_lib_checksum_complete(skb))
+ checksum_valid = !udp_lib_checksum_complete(skb);
+ if (!checksum_valid)
goto csum_copy_err;
}
- if (skb_csum_unnecessary(skb))
+ if (checksum_valid || skb_csum_unnecessary(skb))
err = skb_copy_datagram_msg(skb, sizeof(struct udphdr),
msg, copied);
else {
@@ -1396,6 +1472,8 @@ void udp_lib_unhash(struct sock *sk)
hslot2 = udp_hashslot2(udptable, udp_sk(sk)->udp_portaddr_hash);
spin_lock_bh(&hslot->lock);
+ if (rcu_access_pointer(sk->sk_reuseport_cb))
+ reuseport_detach_sock(sk);
if (sk_nulls_del_node_init_rcu(sk)) {
hslot->count--;
inet_sk(sk)->inet_num = 0;
@@ -1423,22 +1501,28 @@ void udp_lib_rehash(struct sock *sk, u16 newhash)
hslot2 = udp_hashslot2(udptable, udp_sk(sk)->udp_portaddr_hash);
nhslot2 = udp_hashslot2(udptable, newhash);
udp_sk(sk)->udp_portaddr_hash = newhash;
- if (hslot2 != nhslot2) {
+
+ if (hslot2 != nhslot2 ||
+ rcu_access_pointer(sk->sk_reuseport_cb)) {
hslot = udp_hashslot(udptable, sock_net(sk),
udp_sk(sk)->udp_port_hash);
/* we must lock primary chain too */
spin_lock_bh(&hslot->lock);
-
- spin_lock(&hslot2->lock);
- hlist_nulls_del_init_rcu(&udp_sk(sk)->udp_portaddr_node);
- hslot2->count--;
- spin_unlock(&hslot2->lock);
-
- spin_lock(&nhslot2->lock);
- hlist_nulls_add_head_rcu(&udp_sk(sk)->udp_portaddr_node,
- &nhslot2->head);
- nhslot2->count++;
- spin_unlock(&nhslot2->lock);
+ if (rcu_access_pointer(sk->sk_reuseport_cb))
+ reuseport_detach_sock(sk);
+
+ if (hslot2 != nhslot2) {
+ spin_lock(&hslot2->lock);
+ hlist_nulls_del_init_rcu(&udp_sk(sk)->udp_portaddr_node);
+ hslot2->count--;
+ spin_unlock(&hslot2->lock);
+
+ spin_lock(&nhslot2->lock);
+ hlist_nulls_add_head_rcu(&udp_sk(sk)->udp_portaddr_node,
+ &nhslot2->head);
+ nhslot2->count++;
+ spin_unlock(&nhslot2->lock);
+ }
spin_unlock_bh(&hslot->lock);
}
diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c
index 6116604bf6e8..df1966f3b6ec 100644
--- a/net/ipv4/udp_diag.c
+++ b/net/ipv4/udp_diag.c
@@ -44,7 +44,7 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb,
sk = __udp4_lib_lookup(net,
req->id.idiag_src[0], req->id.idiag_sport,
req->id.idiag_dst[0], req->id.idiag_dport,
- req->id.idiag_if, tbl);
+ req->id.idiag_if, tbl, NULL);
#if IS_ENABLED(CONFIG_IPV6)
else if (req->sdiag_family == AF_INET6)
sk = __udp6_lib_lookup(net,
@@ -52,7 +52,7 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb,
req->id.idiag_sport,
(struct in6_addr *)req->id.idiag_dst,
req->id.idiag_dport,
- req->id.idiag_if, tbl);
+ req->id.idiag_if, tbl, NULL);
#endif
else
goto out_nosk;
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index f9386160cbee..4c519c1dc161 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -21,6 +21,7 @@ static struct udp_offload_priv __rcu *udp_offload_base __read_mostly;
struct udp_offload_priv {
struct udp_offload *offload;
+ possible_net_t net;
struct rcu_head rcu;
struct udp_offload_priv __rcu *next;
};
@@ -60,8 +61,9 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
/* Try to offload checksum if possible */
offload_csum = !!(need_csum &&
- (skb->dev->features &
- (is_ipv6 ? NETIF_F_V6_CSUM : NETIF_F_V4_CSUM)));
+ ((skb->dev->features & NETIF_F_HW_CSUM) ||
+ (skb->dev->features & (is_ipv6 ?
+ NETIF_F_IPV6_CSUM : NETIF_F_IP_CSUM))));
/* segment inner packet. */
enc_features = skb->dev->hw_enc_features & features;
@@ -241,13 +243,14 @@ out:
return segs;
}
-int udp_add_offload(struct udp_offload *uo)
+int udp_add_offload(struct net *net, struct udp_offload *uo)
{
struct udp_offload_priv *new_offload = kzalloc(sizeof(*new_offload), GFP_ATOMIC);
if (!new_offload)
return -ENOMEM;
+ write_pnet(&new_offload->net, net);
new_offload->offload = uo;
spin_lock(&udp_offload_lock);
@@ -311,7 +314,8 @@ struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb,
rcu_read_lock();
uo_priv = rcu_dereference(udp_offload_base);
for (; uo_priv != NULL; uo_priv = rcu_dereference(uo_priv->next)) {
- if (uo_priv->offload->port == uh->dest &&
+ if (net_eq(read_pnet(&uo_priv->net), dev_net(skb->dev)) &&
+ uo_priv->offload->port == uh->dest &&
uo_priv->offload->callbacks.gro_receive)
goto unflush;
}
@@ -389,7 +393,8 @@ int udp_gro_complete(struct sk_buff *skb, int nhoff)
uo_priv = rcu_dereference(udp_offload_base);
for (; uo_priv != NULL; uo_priv = rcu_dereference(uo_priv->next)) {
- if (uo_priv->offload->port == uh->dest &&
+ if (net_eq(read_pnet(&uo_priv->net), dev_net(skb->dev)) &&
+ uo_priv->offload->port == uh->dest &&
uo_priv->offload->callbacks.gro_complete)
break;
}
diff --git a/net/ipv4/udp_tunnel.c b/net/ipv4/udp_tunnel.c
index aba428626b52..0ec08814f37d 100644
--- a/net/ipv4/udp_tunnel.c
+++ b/net/ipv4/udp_tunnel.c
@@ -74,10 +74,10 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock,
}
EXPORT_SYMBOL_GPL(setup_udp_tunnel_sock);
-int udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
- __be32 src, __be32 dst, __u8 tos, __u8 ttl,
- __be16 df, __be16 src_port, __be16 dst_port,
- bool xnet, bool nocheck)
+void udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
+ __be32 src, __be32 dst, __u8 tos, __u8 ttl,
+ __be16 df, __be16 src_port, __be16 dst_port,
+ bool xnet, bool nocheck)
{
struct udphdr *uh;
@@ -91,8 +91,7 @@ int udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
udp_set_csum(nocheck, skb, src, dst, skb->len);
- return iptunnel_xmit(sk, rt, skb, src, dst, IPPROTO_UDP,
- tos, ttl, df, xnet);
+ iptunnel_xmit(sk, rt, skb, src, dst, IPPROTO_UDP, tos, ttl, df, xnet);
}
EXPORT_SYMBOL_GPL(udp_tunnel_xmit_skb);
diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
index 1e0c3c835a63..7b0edb37a115 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -259,7 +259,7 @@ static void xfrm4_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
xfrm_dst_ifdown(dst, dev);
}
-static struct dst_ops xfrm4_dst_ops = {
+static struct dst_ops xfrm4_dst_ops_template = {
.family = AF_INET,
.gc = xfrm4_garbage_collect,
.update_pmtu = xfrm4_update_pmtu,
@@ -273,7 +273,7 @@ static struct dst_ops xfrm4_dst_ops = {
static struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
.family = AF_INET,
- .dst_ops = &xfrm4_dst_ops,
+ .dst_ops = &xfrm4_dst_ops_template,
.dst_lookup = xfrm4_dst_lookup,
.get_saddr = xfrm4_get_saddr,
.decode_session = _decode_session4,
@@ -295,7 +295,7 @@ static struct ctl_table xfrm4_policy_table[] = {
{ }
};
-static int __net_init xfrm4_net_init(struct net *net)
+static int __net_init xfrm4_net_sysctl_init(struct net *net)
{
struct ctl_table *table;
struct ctl_table_header *hdr;
@@ -323,7 +323,7 @@ err_alloc:
return -ENOMEM;
}
-static void __net_exit xfrm4_net_exit(struct net *net)
+static void __net_exit xfrm4_net_sysctl_exit(struct net *net)
{
struct ctl_table *table;
@@ -335,12 +335,44 @@ static void __net_exit xfrm4_net_exit(struct net *net)
if (!net_eq(net, &init_net))
kfree(table);
}
+#else /* CONFIG_SYSCTL */
+static int inline xfrm4_net_sysctl_init(struct net *net)
+{
+ return 0;
+}
+
+static void inline xfrm4_net_sysctl_exit(struct net *net)
+{
+}
+#endif
+
+static int __net_init xfrm4_net_init(struct net *net)
+{
+ int ret;
+
+ memcpy(&net->xfrm.xfrm4_dst_ops, &xfrm4_dst_ops_template,
+ sizeof(xfrm4_dst_ops_template));
+ ret = dst_entries_init(&net->xfrm.xfrm4_dst_ops);
+ if (ret)
+ return ret;
+
+ ret = xfrm4_net_sysctl_init(net);
+ if (ret)
+ dst_entries_destroy(&net->xfrm.xfrm4_dst_ops);
+
+ return ret;
+}
+
+static void __net_exit xfrm4_net_exit(struct net *net)
+{
+ xfrm4_net_sysctl_exit(net);
+ dst_entries_destroy(&net->xfrm.xfrm4_dst_ops);
+}
static struct pernet_operations __net_initdata xfrm4_net_ops = {
.init = xfrm4_net_init,
.exit = xfrm4_net_exit,
};
-#endif
static void __init xfrm4_policy_init(void)
{
@@ -349,13 +381,9 @@ static void __init xfrm4_policy_init(void)
void __init xfrm4_init(void)
{
- dst_entries_init(&xfrm4_dst_ops);
-
xfrm4_state_init();
xfrm4_policy_init();
xfrm4_protocol_init();
-#ifdef CONFIG_SYSCTL
register_pernet_subsys(&xfrm4_net_ops);
-#endif
}
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index 983bb999738c..bb7dabe2ebbf 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -94,6 +94,7 @@ config IPV6_MIP6
config IPV6_ILA
tristate "IPv6: Identifier Locator Addressing (ILA)"
+ depends on NETFILTER
select LWTUNNEL
---help---
Support for IPv6 Identifier Locator Addressing (ILA).
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 2c900c7b7eb1..2fbd90bf8d33 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -34,7 +34,7 @@ obj-$(CONFIG_INET6_XFRM_MODE_TUNNEL) += xfrm6_mode_tunnel.o
obj-$(CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION) += xfrm6_mode_ro.o
obj-$(CONFIG_INET6_XFRM_MODE_BEET) += xfrm6_mode_beet.o
obj-$(CONFIG_IPV6_MIP6) += mip6.o
-obj-$(CONFIG_IPV6_ILA) += ila.o
+obj-$(CONFIG_IPV6_ILA) += ila/
obj-$(CONFIG_NETFILTER) += netfilter/
obj-$(CONFIG_IPV6_VTI) += ip6_vti.o
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index d84742f003a9..38eeddedfc21 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -70,7 +70,7 @@
#include <net/sock.h>
#include <net/snmp.h>
-#include <net/af_ieee802154.h>
+#include <net/6lowpan.h>
#include <net/firewire.h>
#include <net/ipv6.h>
#include <net/protocol.h>
@@ -350,6 +350,12 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
setup_timer(&ndev->rs_timer, addrconf_rs_timer,
(unsigned long)ndev);
memcpy(&ndev->cnf, dev_net(dev)->ipv6.devconf_dflt, sizeof(ndev->cnf));
+
+ if (ndev->cnf.stable_secret.initialized)
+ ndev->addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
+ else
+ ndev->addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64;
+
ndev->cnf.mtu6 = dev->mtu;
ndev->cnf.sysctl = NULL;
ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl);
@@ -1766,12 +1772,13 @@ struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *add
static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed)
{
+ if (dad_failed)
+ ifp->flags |= IFA_F_DADFAILED;
+
if (ifp->flags&IFA_F_PERMANENT) {
spin_lock_bh(&ifp->lock);
addrconf_del_dad_work(ifp);
ifp->flags |= IFA_F_TENTATIVE;
- if (dad_failed)
- ifp->flags |= IFA_F_DADFAILED;
spin_unlock_bh(&ifp->lock);
if (dad_failed)
ipv6_ifa_notify(0, ifp);
@@ -1947,9 +1954,9 @@ static void addrconf_leave_anycast(struct inet6_ifaddr *ifp)
static int addrconf_ifid_eui64(u8 *eui, struct net_device *dev)
{
- if (dev->addr_len != IEEE802154_ADDR_LEN)
+ if (dev->addr_len != EUI64_ADDR_LEN)
return -1;
- memcpy(eui, dev->dev_addr, 8);
+ memcpy(eui, dev->dev_addr, EUI64_ADDR_LEN);
eui[0] ^= 2;
return 0;
}
@@ -2041,7 +2048,6 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
case ARPHRD_IPGRE:
return addrconf_ifid_gre(eui, dev);
case ARPHRD_6LOWPAN:
- case ARPHRD_IEEE802154:
return addrconf_ifid_eui64(eui, dev);
case ARPHRD_IEEE1394:
return addrconf_ifid_ieee1394(eui, dev);
@@ -2314,6 +2320,12 @@ static void manage_tempaddrs(struct inet6_dev *idev,
}
}
+static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
+{
+ return idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY ||
+ idev->addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
+}
+
void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
{
struct prefix_info *pinfo;
@@ -2427,8 +2439,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
in6_dev->token.s6_addr + 8, 8);
read_unlock_bh(&in6_dev->lock);
tokenized = true;
- } else if (in6_dev->addr_gen_mode ==
- IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
+ } else if (is_addr_mode_generate_stable(in6_dev) &&
!ipv6_generate_stable_address(&addr, 0,
in6_dev)) {
addr_flags |= IFA_F_STABLE_PRIVACY;
@@ -2455,7 +2466,7 @@ ok:
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
if (in6_dev->cnf.optimistic_dad &&
!net->ipv6.devconf_all->forwarding && sllao)
- addr_flags = IFA_F_OPTIMISTIC;
+ addr_flags |= IFA_F_OPTIMISTIC;
#endif
/* Do not allow to create too much of autoconfigured
@@ -3028,6 +3039,17 @@ retry:
return 0;
}
+static void ipv6_gen_mode_random_init(struct inet6_dev *idev)
+{
+ struct ipv6_stable_secret *s = &idev->cnf.stable_secret;
+
+ if (s->initialized)
+ return;
+ s = &idev->cnf.stable_secret;
+ get_random_bytes(&s->secret, sizeof(s->secret));
+ s->initialized = true;
+}
+
static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
{
struct in6_addr addr;
@@ -3038,13 +3060,18 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0);
- if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
+ switch (idev->addr_gen_mode) {
+ case IN6_ADDR_GEN_MODE_RANDOM:
+ ipv6_gen_mode_random_init(idev);
+ /* fallthrough */
+ case IN6_ADDR_GEN_MODE_STABLE_PRIVACY:
if (!ipv6_generate_stable_address(&addr, 0, idev))
addrconf_add_linklocal(idev, &addr,
IFA_F_STABLE_PRIVACY);
else if (prefix_route)
addrconf_prefix_route(&addr, 64, idev->dev, 0, 0);
- } else if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) {
+ break;
+ case IN6_ADDR_GEN_MODE_EUI64:
/* addrconf_add_linklocal also adds a prefix_route and we
* only need to care about prefix routes if ipv6_generate_eui64
* couldn't generate one.
@@ -3053,6 +3080,11 @@ static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
addrconf_add_linklocal(idev, &addr, 0);
else if (prefix_route)
addrconf_prefix_route(&addr, 64, idev->dev, 0, 0);
+ break;
+ case IN6_ADDR_GEN_MODE_NONE:
+ default:
+ /* will not add any link local address */
+ break;
}
}
@@ -3066,10 +3098,10 @@ static void addrconf_dev_config(struct net_device *dev)
(dev->type != ARPHRD_FDDI) &&
(dev->type != ARPHRD_ARCNET) &&
(dev->type != ARPHRD_INFINIBAND) &&
- (dev->type != ARPHRD_IEEE802154) &&
(dev->type != ARPHRD_IEEE1394) &&
(dev->type != ARPHRD_TUNNEL6) &&
- (dev->type != ARPHRD_6LOWPAN)) {
+ (dev->type != ARPHRD_6LOWPAN) &&
+ (dev->type != ARPHRD_NONE)) {
/* Alas, we support only Ethernet autoconfiguration. */
return;
}
@@ -3078,6 +3110,11 @@ static void addrconf_dev_config(struct net_device *dev)
if (IS_ERR(idev))
return;
+ /* this device type has no EUI support */
+ if (dev->type == ARPHRD_NONE &&
+ idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64)
+ idev->addr_gen_mode = IN6_ADDR_GEN_MODE_RANDOM;
+
addrconf_addr_gen(idev, false);
}
@@ -3287,7 +3324,8 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
case NETDEV_PRE_TYPE_CHANGE:
case NETDEV_POST_TYPE_CHANGE:
- addrconf_type_change(dev, event);
+ if (idev)
+ addrconf_type_change(dev, event);
break;
}
@@ -3642,7 +3680,7 @@ static void addrconf_dad_work(struct work_struct *w)
/* send a neighbour solicitation for our addr */
addrconf_addr_solict_mult(&ifp->addr, &mcaddr);
- ndisc_send_ns(ifp->idev->dev, &ifp->addr, &mcaddr, &in6addr_any, NULL);
+ ndisc_send_ns(ifp->idev->dev, &ifp->addr, &mcaddr, &in6addr_any);
out:
in6_ifa_put(ifp);
rtnl_unlock();
@@ -4921,7 +4959,8 @@ static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla)
if (mode != IN6_ADDR_GEN_MODE_EUI64 &&
mode != IN6_ADDR_GEN_MODE_NONE &&
- mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY)
+ mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
+ mode != IN6_ADDR_GEN_MODE_RANDOM)
return -EINVAL;
if (mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
@@ -5200,6 +5239,20 @@ int addrconf_sysctl_forward(struct ctl_table *ctl, int write,
}
static
+int addrconf_sysctl_hop_limit(struct ctl_table *ctl, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ struct ctl_table lctl;
+ int min_hl = 1, max_hl = 255;
+
+ lctl = *ctl;
+ lctl.extra1 = &min_hl;
+ lctl.extra2 = &max_hl;
+
+ return proc_dointvec_minmax(&lctl, write, buffer, lenp, ppos);
+}
+
+static
int addrconf_sysctl_mtu(struct ctl_table *ctl, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
@@ -5363,13 +5416,10 @@ static int addrconf_sysctl_stable_secret(struct ctl_table *ctl, int write,
goto out;
}
- if (!write) {
- err = snprintf(str, sizeof(str), "%pI6",
- &secret->secret);
- if (err >= sizeof(str)) {
- err = -EIO;
- goto out;
- }
+ err = snprintf(str, sizeof(str), "%pI6", &secret->secret);
+ if (err >= sizeof(str)) {
+ err = -EIO;
+ goto out;
}
err = proc_dostring(&lctl, write, buffer, lenp, ppos);
@@ -5454,7 +5504,7 @@ static struct addrconf_sysctl_table
.data = &ipv6_devconf.hop_limit,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = proc_dointvec,
+ .proc_handler = addrconf_sysctl_hop_limit,
},
{
.procname = "mtu",
diff --git a/net/ipv6/addrlabel.c b/net/ipv6/addrlabel.c
index 882124ebb438..a8f6986dcbe5 100644
--- a/net/ipv6/addrlabel.c
+++ b/net/ipv6/addrlabel.c
@@ -552,7 +552,7 @@ static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr *nlh)
rcu_read_lock();
p = __ipv6_addr_label(net, addr, ipv6_addr_type(addr), ifal->ifal_index);
- if (p && ip6addrlbl_hold(p))
+ if (p && !ip6addrlbl_hold(p))
p = NULL;
lseq = ip6addrlbl_table.seq;
rcu_read_unlock();
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 44bb66bde0e2..9f5137cd604e 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -109,6 +109,9 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol,
int try_loading_module = 0;
int err;
+ if (protocol < 0 || protocol >= IPPROTO_MAX)
+ return -EINVAL;
+
/* Look for the requested type/protocol pair. */
lookup_protocol:
err = -ESOCKTNOSUPPORT;
@@ -428,9 +431,11 @@ void inet6_destroy_sock(struct sock *sk)
/* Free tx options */
- opt = xchg(&np->opt, NULL);
- if (opt)
- sock_kfree_s(sk, opt, opt->tot_len);
+ opt = xchg((__force struct ipv6_txoptions **)&np->opt, NULL);
+ if (opt) {
+ atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
+ txopt_put(opt);
+ }
}
EXPORT_SYMBOL_GPL(inet6_destroy_sock);
@@ -659,7 +664,10 @@ int inet6_sk_rebuild_header(struct sock *sk)
fl6.fl6_sport = inet->inet_sport;
security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
- final_p = fl6_update_dst(&fl6, np->opt, &final);
+ rcu_read_lock();
+ final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt),
+ &final);
+ rcu_read_unlock();
dst = ip6_dst_lookup_flow(sk, &fl6, final_p);
if (IS_ERR(dst)) {
@@ -668,7 +676,7 @@ int inet6_sk_rebuild_header(struct sock *sk)
return PTR_ERR(dst);
}
- __ip6_dst_store(sk, dst, NULL, NULL);
+ ip6_dst_store(sk, dst, NULL, NULL);
}
return 0;
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index d70b0238f468..517c55b01ba8 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -167,8 +167,10 @@ ipv4_connected:
security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
- opt = flowlabel ? flowlabel->opt : np->opt;
+ rcu_read_lock();
+ opt = flowlabel ? flowlabel->opt : rcu_dereference(np->opt);
final_p = fl6_update_dst(&fl6, opt, &final);
+ rcu_read_unlock();
dst = ip6_dst_lookup_flow(sk, &fl6, final_p);
err = 0;
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index ce203b0402be..ea7c4d64a00a 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -727,6 +727,7 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
*((char **)&opt2->dst1opt) += dif;
if (opt2->srcrt)
*((char **)&opt2->srcrt) += dif;
+ atomic_set(&opt2->refcnt, 1);
}
return opt2;
}
@@ -790,7 +791,7 @@ ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
return ERR_PTR(-ENOBUFS);
memset(opt2, 0, tot_len);
-
+ atomic_set(&opt2->refcnt, 1);
opt2->tot_len = tot_len;
p = (char *)(opt2 + 1);
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 36c5a98b0472..0a37ddc7af51 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -834,11 +834,6 @@ void icmpv6_flow_init(struct sock *sk, struct flowi6 *fl6,
security_sk_classify_flow(sk, flowi6_to_flowi(fl6));
}
-/*
- * Special lock-class for __icmpv6_sk:
- */
-static struct lock_class_key icmpv6_socket_sk_dst_lock_key;
-
static int __net_init icmpv6_sk_init(struct net *net)
{
struct sock *sk;
@@ -860,15 +855,6 @@ static int __net_init icmpv6_sk_init(struct net *net)
net->ipv6.icmp_sk[i] = sk;
- /*
- * Split off their lock-class, because sk->sk_dst_lock
- * gets used from softirqs, which is safe for
- * __icmpv6_sk (because those never get directly used
- * via userspace syscalls), but unsafe for normal sockets.
- */
- lockdep_set_class(&sk->sk_dst_lock,
- &icmpv6_socket_sk_dst_lock_key);
-
/* Enough space for 2 64K ICMP packets, including
* sk_buff struct overhead.
*/
diff --git a/net/ipv6/ila/Makefile b/net/ipv6/ila/Makefile
new file mode 100644
index 000000000000..4b32e5921e5c
--- /dev/null
+++ b/net/ipv6/ila/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for ILA module
+#
+
+obj-$(CONFIG_IPV6_ILA) += ila.o
+
+ila-objs := ila_common.o ila_lwt.o ila_xlat.o
diff --git a/net/ipv6/ila/ila.h b/net/ipv6/ila/ila.h
new file mode 100644
index 000000000000..28542cb2b387
--- /dev/null
+++ b/net/ipv6/ila/ila.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2015 Tom Herbert <tom@herbertland.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 __ILA_H
+#define __ILA_H
+
+#include <linux/errno.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <net/checksum.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <uapi/linux/ila.h>
+
+struct ila_params {
+ __be64 locator;
+ __be64 locator_match;
+ __wsum csum_diff;
+};
+
+static inline __wsum compute_csum_diff8(const __be32 *from, const __be32 *to)
+{
+ __be32 diff[] = {
+ ~from[0], ~from[1], to[0], to[1],
+ };
+
+ return csum_partial(diff, sizeof(diff), 0);
+}
+
+void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p);
+
+int ila_lwt_init(void);
+void ila_lwt_fini(void);
+int ila_xlat_init(void);
+void ila_xlat_fini(void);
+
+#endif /* __ILA_H */
diff --git a/net/ipv6/ila/ila_common.c b/net/ipv6/ila/ila_common.c
new file mode 100644
index 000000000000..32dc9aab7297
--- /dev/null
+++ b/net/ipv6/ila/ila_common.c
@@ -0,0 +1,103 @@
+#include <linux/errno.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/types.h>
+#include <net/checksum.h>
+#include <net/ip.h>
+#include <net/ip6_fib.h>
+#include <net/lwtunnel.h>
+#include <net/protocol.h>
+#include <uapi/linux/ila.h>
+#include "ila.h"
+
+static __wsum get_csum_diff(struct ipv6hdr *ip6h, struct ila_params *p)
+{
+ if (*(__be64 *)&ip6h->daddr == p->locator_match)
+ return p->csum_diff;
+ else
+ return compute_csum_diff8((__be32 *)&ip6h->daddr,
+ (__be32 *)&p->locator);
+}
+
+void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p)
+{
+ __wsum diff;
+ struct ipv6hdr *ip6h = ipv6_hdr(skb);
+ size_t nhoff = sizeof(struct ipv6hdr);
+
+ /* First update checksum */
+ switch (ip6h->nexthdr) {
+ case NEXTHDR_TCP:
+ if (likely(pskb_may_pull(skb, nhoff + sizeof(struct tcphdr)))) {
+ struct tcphdr *th = (struct tcphdr *)
+ (skb_network_header(skb) + nhoff);
+
+ diff = get_csum_diff(ip6h, p);
+ inet_proto_csum_replace_by_diff(&th->check, skb,
+ diff, true);
+ }
+ break;
+ case NEXTHDR_UDP:
+ if (likely(pskb_may_pull(skb, nhoff + sizeof(struct udphdr)))) {
+ struct udphdr *uh = (struct udphdr *)
+ (skb_network_header(skb) + nhoff);
+
+ if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) {
+ diff = get_csum_diff(ip6h, p);
+ inet_proto_csum_replace_by_diff(&uh->check, skb,
+ diff, true);
+ if (!uh->check)
+ uh->check = CSUM_MANGLED_0;
+ }
+ }
+ break;
+ case NEXTHDR_ICMP:
+ if (likely(pskb_may_pull(skb,
+ nhoff + sizeof(struct icmp6hdr)))) {
+ struct icmp6hdr *ih = (struct icmp6hdr *)
+ (skb_network_header(skb) + nhoff);
+
+ diff = get_csum_diff(ip6h, p);
+ inet_proto_csum_replace_by_diff(&ih->icmp6_cksum, skb,
+ diff, true);
+ }
+ break;
+ }
+
+ /* Now change destination address */
+ *(__be64 *)&ip6h->daddr = p->locator;
+}
+
+static int __init ila_init(void)
+{
+ int ret;
+
+ ret = ila_lwt_init();
+
+ if (ret)
+ goto fail_lwt;
+
+ ret = ila_xlat_init();
+ if (ret)
+ goto fail_xlat;
+
+ return 0;
+fail_xlat:
+ ila_lwt_fini();
+fail_lwt:
+ return ret;
+}
+
+static void __exit ila_fini(void)
+{
+ ila_xlat_fini();
+ ila_lwt_fini();
+}
+
+module_init(ila_init);
+module_exit(ila_fini);
+MODULE_AUTHOR("Tom Herbert <tom@herbertland.com>");
+MODULE_LICENSE("GPL");
diff --git a/net/ipv6/ila.c b/net/ipv6/ila/ila_lwt.c
index 1a6852e1ac69..2ae3c4fd8aab 100644
--- a/net/ipv6/ila.c
+++ b/net/ipv6/ila/ila_lwt.c
@@ -11,12 +11,7 @@
#include <net/lwtunnel.h>
#include <net/protocol.h>
#include <uapi/linux/ila.h>
-
-struct ila_params {
- __be64 locator;
- __be64 locator_match;
- __wsum csum_diff;
-};
+#include "ila.h"
static inline struct ila_params *ila_params_lwtunnel(
struct lwtunnel_state *lwstate)
@@ -24,73 +19,6 @@ static inline struct ila_params *ila_params_lwtunnel(
return (struct ila_params *)lwstate->data;
}
-static inline __wsum compute_csum_diff8(const __be32 *from, const __be32 *to)
-{
- __be32 diff[] = {
- ~from[0], ~from[1], to[0], to[1],
- };
-
- return csum_partial(diff, sizeof(diff), 0);
-}
-
-static inline __wsum get_csum_diff(struct ipv6hdr *ip6h, struct ila_params *p)
-{
- if (*(__be64 *)&ip6h->daddr == p->locator_match)
- return p->csum_diff;
- else
- return compute_csum_diff8((__be32 *)&ip6h->daddr,
- (__be32 *)&p->locator);
-}
-
-static void update_ipv6_locator(struct sk_buff *skb, struct ila_params *p)
-{
- __wsum diff;
- struct ipv6hdr *ip6h = ipv6_hdr(skb);
- size_t nhoff = sizeof(struct ipv6hdr);
-
- /* First update checksum */
- switch (ip6h->nexthdr) {
- case NEXTHDR_TCP:
- if (likely(pskb_may_pull(skb, nhoff + sizeof(struct tcphdr)))) {
- struct tcphdr *th = (struct tcphdr *)
- (skb_network_header(skb) + nhoff);
-
- diff = get_csum_diff(ip6h, p);
- inet_proto_csum_replace_by_diff(&th->check, skb,
- diff, true);
- }
- break;
- case NEXTHDR_UDP:
- if (likely(pskb_may_pull(skb, nhoff + sizeof(struct udphdr)))) {
- struct udphdr *uh = (struct udphdr *)
- (skb_network_header(skb) + nhoff);
-
- if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) {
- diff = get_csum_diff(ip6h, p);
- inet_proto_csum_replace_by_diff(&uh->check, skb,
- diff, true);
- if (!uh->check)
- uh->check = CSUM_MANGLED_0;
- }
- }
- break;
- case NEXTHDR_ICMP:
- if (likely(pskb_may_pull(skb,
- nhoff + sizeof(struct icmp6hdr)))) {
- struct icmp6hdr *ih = (struct icmp6hdr *)
- (skb_network_header(skb) + nhoff);
-
- diff = get_csum_diff(ip6h, p);
- inet_proto_csum_replace_by_diff(&ih->icmp6_cksum, skb,
- diff, true);
- }
- break;
- }
-
- /* Now change destination address */
- *(__be64 *)&ip6h->daddr = p->locator;
-}
-
static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct dst_entry *dst = skb_dst(skb);
@@ -213,17 +141,12 @@ static const struct lwtunnel_encap_ops ila_encap_ops = {
.cmp_encap = ila_encap_cmp,
};
-static int __init ila_init(void)
+int ila_lwt_init(void)
{
return lwtunnel_encap_add_ops(&ila_encap_ops, LWTUNNEL_ENCAP_ILA);
}
-static void __exit ila_fini(void)
+void ila_lwt_fini(void)
{
lwtunnel_encap_del_ops(&ila_encap_ops, LWTUNNEL_ENCAP_ILA);
}
-
-module_init(ila_init);
-module_exit(ila_fini);
-MODULE_AUTHOR("Tom Herbert <tom@herbertland.com>");
-MODULE_LICENSE("GPL");
diff --git a/net/ipv6/ila/ila_xlat.c b/net/ipv6/ila/ila_xlat.c
new file mode 100644
index 000000000000..295ca29a23c3
--- /dev/null
+++ b/net/ipv6/ila/ila_xlat.c
@@ -0,0 +1,680 @@
+#include <linux/jhash.h>
+#include <linux/netfilter.h>
+#include <linux/rcupdate.h>
+#include <linux/rhashtable.h>
+#include <linux/vmalloc.h>
+#include <net/genetlink.h>
+#include <net/ila.h>
+#include <net/netns/generic.h>
+#include <uapi/linux/genetlink.h>
+#include "ila.h"
+
+struct ila_xlat_params {
+ struct ila_params ip;
+ __be64 identifier;
+ int ifindex;
+ unsigned int dir;
+};
+
+struct ila_map {
+ struct ila_xlat_params p;
+ struct rhash_head node;
+ struct ila_map __rcu *next;
+ struct rcu_head rcu;
+};
+
+static unsigned int ila_net_id;
+
+struct ila_net {
+ struct rhashtable rhash_table;
+ spinlock_t *locks; /* Bucket locks for entry manipulation */
+ unsigned int locks_mask;
+ bool hooks_registered;
+};
+
+#define LOCKS_PER_CPU 10
+
+static int alloc_ila_locks(struct ila_net *ilan)
+{
+ unsigned int i, size;
+ unsigned int nr_pcpus = num_possible_cpus();
+
+ nr_pcpus = min_t(unsigned int, nr_pcpus, 32UL);
+ size = roundup_pow_of_two(nr_pcpus * LOCKS_PER_CPU);
+
+ if (sizeof(spinlock_t) != 0) {
+#ifdef CONFIG_NUMA
+ if (size * sizeof(spinlock_t) > PAGE_SIZE)
+ ilan->locks = vmalloc(size * sizeof(spinlock_t));
+ else
+#endif
+ ilan->locks = kmalloc_array(size, sizeof(spinlock_t),
+ GFP_KERNEL);
+ if (!ilan->locks)
+ return -ENOMEM;
+ for (i = 0; i < size; i++)
+ spin_lock_init(&ilan->locks[i]);
+ }
+ ilan->locks_mask = size - 1;
+
+ return 0;
+}
+
+static u32 hashrnd __read_mostly;
+static __always_inline void __ila_hash_secret_init(void)
+{
+ net_get_random_once(&hashrnd, sizeof(hashrnd));
+}
+
+static inline u32 ila_identifier_hash(__be64 identifier)
+{
+ u32 *v = (u32 *)&identifier;
+
+ return jhash_2words(v[0], v[1], hashrnd);
+}
+
+static inline spinlock_t *ila_get_lock(struct ila_net *ilan, __be64 identifier)
+{
+ return &ilan->locks[ila_identifier_hash(identifier) & ilan->locks_mask];
+}
+
+static inline int ila_cmp_wildcards(struct ila_map *ila, __be64 loc,
+ int ifindex, unsigned int dir)
+{
+ return (ila->p.ip.locator_match && ila->p.ip.locator_match != loc) ||
+ (ila->p.ifindex && ila->p.ifindex != ifindex) ||
+ !(ila->p.dir & dir);
+}
+
+static inline int ila_cmp_params(struct ila_map *ila, struct ila_xlat_params *p)
+{
+ return (ila->p.ip.locator_match != p->ip.locator_match) ||
+ (ila->p.ifindex != p->ifindex) ||
+ (ila->p.dir != p->dir);
+}
+
+static int ila_cmpfn(struct rhashtable_compare_arg *arg,
+ const void *obj)
+{
+ const struct ila_map *ila = obj;
+
+ return (ila->p.identifier != *(__be64 *)arg->key);
+}
+
+static inline int ila_order(struct ila_map *ila)
+{
+ int score = 0;
+
+ if (ila->p.ip.locator_match)
+ score += 1 << 0;
+
+ if (ila->p.ifindex)
+ score += 1 << 1;
+
+ return score;
+}
+
+static const struct rhashtable_params rht_params = {
+ .nelem_hint = 1024,
+ .head_offset = offsetof(struct ila_map, node),
+ .key_offset = offsetof(struct ila_map, p.identifier),
+ .key_len = sizeof(u64), /* identifier */
+ .max_size = 1048576,
+ .min_size = 256,
+ .automatic_shrinking = true,
+ .obj_cmpfn = ila_cmpfn,
+};
+
+static struct genl_family ila_nl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = ILA_GENL_NAME,
+ .version = ILA_GENL_VERSION,
+ .maxattr = ILA_ATTR_MAX,
+ .netnsok = true,
+ .parallel_ops = true,
+};
+
+static struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = {
+ [ILA_ATTR_IDENTIFIER] = { .type = NLA_U64, },
+ [ILA_ATTR_LOCATOR] = { .type = NLA_U64, },
+ [ILA_ATTR_LOCATOR_MATCH] = { .type = NLA_U64, },
+ [ILA_ATTR_IFINDEX] = { .type = NLA_U32, },
+ [ILA_ATTR_DIR] = { .type = NLA_U32, },
+};
+
+static int parse_nl_config(struct genl_info *info,
+ struct ila_xlat_params *p)
+{
+ memset(p, 0, sizeof(*p));
+
+ if (info->attrs[ILA_ATTR_IDENTIFIER])
+ p->identifier = (__force __be64)nla_get_u64(
+ info->attrs[ILA_ATTR_IDENTIFIER]);
+
+ if (info->attrs[ILA_ATTR_LOCATOR])
+ p->ip.locator = (__force __be64)nla_get_u64(
+ info->attrs[ILA_ATTR_LOCATOR]);
+
+ if (info->attrs[ILA_ATTR_LOCATOR_MATCH])
+ p->ip.locator_match = (__force __be64)nla_get_u64(
+ info->attrs[ILA_ATTR_LOCATOR_MATCH]);
+
+ if (info->attrs[ILA_ATTR_IFINDEX])
+ p->ifindex = nla_get_s32(info->attrs[ILA_ATTR_IFINDEX]);
+
+ if (info->attrs[ILA_ATTR_DIR])
+ p->dir = nla_get_u32(info->attrs[ILA_ATTR_DIR]);
+
+ return 0;
+}
+
+/* Must be called with rcu readlock */
+static inline struct ila_map *ila_lookup_wildcards(__be64 id, __be64 loc,
+ int ifindex,
+ unsigned int dir,
+ struct ila_net *ilan)
+{
+ struct ila_map *ila;
+
+ ila = rhashtable_lookup_fast(&ilan->rhash_table, &id, rht_params);
+ while (ila) {
+ if (!ila_cmp_wildcards(ila, loc, ifindex, dir))
+ return ila;
+ ila = rcu_access_pointer(ila->next);
+ }
+
+ return NULL;
+}
+
+/* Must be called with rcu readlock */
+static inline struct ila_map *ila_lookup_by_params(struct ila_xlat_params *p,
+ struct ila_net *ilan)
+{
+ struct ila_map *ila;
+
+ ila = rhashtable_lookup_fast(&ilan->rhash_table, &p->identifier,
+ rht_params);
+ while (ila) {
+ if (!ila_cmp_params(ila, p))
+ return ila;
+ ila = rcu_access_pointer(ila->next);
+ }
+
+ return NULL;
+}
+
+static inline void ila_release(struct ila_map *ila)
+{
+ kfree_rcu(ila, rcu);
+}
+
+static void ila_free_cb(void *ptr, void *arg)
+{
+ struct ila_map *ila = (struct ila_map *)ptr, *next;
+
+ /* Assume rcu_readlock held */
+ while (ila) {
+ next = rcu_access_pointer(ila->next);
+ ila_release(ila);
+ ila = next;
+ }
+}
+
+static int ila_xlat_addr(struct sk_buff *skb, int dir);
+
+static unsigned int
+ila_nf_input(void *priv,
+ struct sk_buff *skb,
+ const struct nf_hook_state *state)
+{
+ ila_xlat_addr(skb, ILA_DIR_IN);
+ return NF_ACCEPT;
+}
+
+static struct nf_hook_ops ila_nf_hook_ops[] __read_mostly = {
+ {
+ .hook = ila_nf_input,
+ .pf = NFPROTO_IPV6,
+ .hooknum = NF_INET_PRE_ROUTING,
+ .priority = -1,
+ },
+};
+
+static int ila_add_mapping(struct net *net, struct ila_xlat_params *p)
+{
+ struct ila_net *ilan = net_generic(net, ila_net_id);
+ struct ila_map *ila, *head;
+ spinlock_t *lock = ila_get_lock(ilan, p->identifier);
+ int err = 0, order;
+
+ if (!ilan->hooks_registered) {
+ /* We defer registering net hooks in the namespace until the
+ * first mapping is added.
+ */
+ err = nf_register_net_hooks(net, ila_nf_hook_ops,
+ ARRAY_SIZE(ila_nf_hook_ops));
+ if (err)
+ return err;
+
+ ilan->hooks_registered = true;
+ }
+
+ ila = kzalloc(sizeof(*ila), GFP_KERNEL);
+ if (!ila)
+ return -ENOMEM;
+
+ ila->p = *p;
+
+ if (p->ip.locator_match) {
+ /* Precompute checksum difference for translation since we
+ * know both the old identifier and the new one.
+ */
+ ila->p.ip.csum_diff = compute_csum_diff8(
+ (__be32 *)&p->ip.locator_match,
+ (__be32 *)&p->ip.locator);
+ }
+
+ order = ila_order(ila);
+
+ spin_lock(lock);
+
+ head = rhashtable_lookup_fast(&ilan->rhash_table, &p->identifier,
+ rht_params);
+ if (!head) {
+ /* New entry for the rhash_table */
+ err = rhashtable_lookup_insert_fast(&ilan->rhash_table,
+ &ila->node, rht_params);
+ } else {
+ struct ila_map *tila = head, *prev = NULL;
+
+ do {
+ if (!ila_cmp_params(tila, p)) {
+ err = -EEXIST;
+ goto out;
+ }
+
+ if (order > ila_order(tila))
+ break;
+
+ prev = tila;
+ tila = rcu_dereference_protected(tila->next,
+ lockdep_is_held(lock));
+ } while (tila);
+
+ if (prev) {
+ /* Insert in sub list of head */
+ RCU_INIT_POINTER(ila->next, tila);
+ rcu_assign_pointer(prev->next, ila);
+ } else {
+ /* Make this ila new head */
+ RCU_INIT_POINTER(ila->next, head);
+ err = rhashtable_replace_fast(&ilan->rhash_table,
+ &head->node,
+ &ila->node, rht_params);
+ if (err)
+ goto out;
+ }
+ }
+
+out:
+ spin_unlock(lock);
+
+ if (err)
+ kfree(ila);
+
+ return err;
+}
+
+static int ila_del_mapping(struct net *net, struct ila_xlat_params *p)
+{
+ struct ila_net *ilan = net_generic(net, ila_net_id);
+ struct ila_map *ila, *head, *prev;
+ spinlock_t *lock = ila_get_lock(ilan, p->identifier);
+ int err = -ENOENT;
+
+ spin_lock(lock);
+
+ head = rhashtable_lookup_fast(&ilan->rhash_table,
+ &p->identifier, rht_params);
+ ila = head;
+
+ prev = NULL;
+
+ while (ila) {
+ if (ila_cmp_params(ila, p)) {
+ prev = ila;
+ ila = rcu_dereference_protected(ila->next,
+ lockdep_is_held(lock));
+ continue;
+ }
+
+ err = 0;
+
+ if (prev) {
+ /* Not head, just delete from list */
+ rcu_assign_pointer(prev->next, ila->next);
+ } else {
+ /* It is the head. If there is something in the
+ * sublist we need to make a new head.
+ */
+ head = rcu_dereference_protected(ila->next,
+ lockdep_is_held(lock));
+ if (head) {
+ /* Put first entry in the sublist into the
+ * table
+ */
+ err = rhashtable_replace_fast(
+ &ilan->rhash_table, &ila->node,
+ &head->node, rht_params);
+ if (err)
+ goto out;
+ } else {
+ /* Entry no longer used */
+ err = rhashtable_remove_fast(&ilan->rhash_table,
+ &ila->node,
+ rht_params);
+ }
+ }
+
+ ila_release(ila);
+
+ break;
+ }
+
+out:
+ spin_unlock(lock);
+
+ return err;
+}
+
+static int ila_nl_cmd_add_mapping(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ struct ila_xlat_params p;
+ int err;
+
+ err = parse_nl_config(info, &p);
+ if (err)
+ return err;
+
+ return ila_add_mapping(net, &p);
+}
+
+static int ila_nl_cmd_del_mapping(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ struct ila_xlat_params p;
+ int err;
+
+ err = parse_nl_config(info, &p);
+ if (err)
+ return err;
+
+ ila_del_mapping(net, &p);
+
+ return 0;
+}
+
+static int ila_fill_info(struct ila_map *ila, struct sk_buff *msg)
+{
+ if (nla_put_u64(msg, ILA_ATTR_IDENTIFIER,
+ (__force u64)ila->p.identifier) ||
+ nla_put_u64(msg, ILA_ATTR_LOCATOR,
+ (__force u64)ila->p.ip.locator) ||
+ nla_put_u64(msg, ILA_ATTR_LOCATOR_MATCH,
+ (__force u64)ila->p.ip.locator_match) ||
+ nla_put_s32(msg, ILA_ATTR_IFINDEX, ila->p.ifindex) ||
+ nla_put_u32(msg, ILA_ATTR_DIR, ila->p.dir))
+ return -1;
+
+ return 0;
+}
+
+static int ila_dump_info(struct ila_map *ila,
+ u32 portid, u32 seq, u32 flags,
+ struct sk_buff *skb, u8 cmd)
+{
+ void *hdr;
+
+ hdr = genlmsg_put(skb, portid, seq, &ila_nl_family, flags, cmd);
+ if (!hdr)
+ return -ENOMEM;
+
+ if (ila_fill_info(ila, skb) < 0)
+ goto nla_put_failure;
+
+ genlmsg_end(skb, hdr);
+ return 0;
+
+nla_put_failure:
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+}
+
+static int ila_nl_cmd_get_mapping(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ struct ila_net *ilan = net_generic(net, ila_net_id);
+ struct sk_buff *msg;
+ struct ila_xlat_params p;
+ struct ila_map *ila;
+ int ret;
+
+ ret = parse_nl_config(info, &p);
+ if (ret)
+ return ret;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ rcu_read_lock();
+
+ ila = ila_lookup_by_params(&p, ilan);
+ if (ila) {
+ ret = ila_dump_info(ila,
+ info->snd_portid,
+ info->snd_seq, 0, msg,
+ info->genlhdr->cmd);
+ }
+
+ rcu_read_unlock();
+
+ if (ret < 0)
+ goto out_free;
+
+ return genlmsg_reply(msg, info);
+
+out_free:
+ nlmsg_free(msg);
+ return ret;
+}
+
+struct ila_dump_iter {
+ struct rhashtable_iter rhiter;
+};
+
+static int ila_nl_dump_start(struct netlink_callback *cb)
+{
+ struct net *net = sock_net(cb->skb->sk);
+ struct ila_net *ilan = net_generic(net, ila_net_id);
+ struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args;
+
+ return rhashtable_walk_init(&ilan->rhash_table, &iter->rhiter);
+}
+
+static int ila_nl_dump_done(struct netlink_callback *cb)
+{
+ struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args;
+
+ rhashtable_walk_exit(&iter->rhiter);
+
+ return 0;
+}
+
+static int ila_nl_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args;
+ struct rhashtable_iter *rhiter = &iter->rhiter;
+ struct ila_map *ila;
+ int ret;
+
+ ret = rhashtable_walk_start(rhiter);
+ if (ret && ret != -EAGAIN)
+ goto done;
+
+ for (;;) {
+ ila = rhashtable_walk_next(rhiter);
+
+ if (IS_ERR(ila)) {
+ if (PTR_ERR(ila) == -EAGAIN)
+ continue;
+ ret = PTR_ERR(ila);
+ goto done;
+ } else if (!ila) {
+ break;
+ }
+
+ while (ila) {
+ ret = ila_dump_info(ila, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ skb, ILA_CMD_GET);
+ if (ret)
+ goto done;
+
+ ila = rcu_access_pointer(ila->next);
+ }
+ }
+
+ ret = skb->len;
+
+done:
+ rhashtable_walk_stop(rhiter);
+ return ret;
+}
+
+static const struct genl_ops ila_nl_ops[] = {
+ {
+ .cmd = ILA_CMD_ADD,
+ .doit = ila_nl_cmd_add_mapping,
+ .policy = ila_nl_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = ILA_CMD_DEL,
+ .doit = ila_nl_cmd_del_mapping,
+ .policy = ila_nl_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = ILA_CMD_GET,
+ .doit = ila_nl_cmd_get_mapping,
+ .start = ila_nl_dump_start,
+ .dumpit = ila_nl_dump,
+ .done = ila_nl_dump_done,
+ .policy = ila_nl_policy,
+ },
+};
+
+#define ILA_HASH_TABLE_SIZE 1024
+
+static __net_init int ila_init_net(struct net *net)
+{
+ int err;
+ struct ila_net *ilan = net_generic(net, ila_net_id);
+
+ err = alloc_ila_locks(ilan);
+ if (err)
+ return err;
+
+ rhashtable_init(&ilan->rhash_table, &rht_params);
+
+ return 0;
+}
+
+static __net_exit void ila_exit_net(struct net *net)
+{
+ struct ila_net *ilan = net_generic(net, ila_net_id);
+
+ rhashtable_free_and_destroy(&ilan->rhash_table, ila_free_cb, NULL);
+
+ kvfree(ilan->locks);
+
+ if (ilan->hooks_registered)
+ nf_unregister_net_hooks(net, ila_nf_hook_ops,
+ ARRAY_SIZE(ila_nf_hook_ops));
+}
+
+static struct pernet_operations ila_net_ops = {
+ .init = ila_init_net,
+ .exit = ila_exit_net,
+ .id = &ila_net_id,
+ .size = sizeof(struct ila_net),
+};
+
+static int ila_xlat_addr(struct sk_buff *skb, int dir)
+{
+ struct ila_map *ila;
+ struct ipv6hdr *ip6h = ipv6_hdr(skb);
+ struct net *net = dev_net(skb->dev);
+ struct ila_net *ilan = net_generic(net, ila_net_id);
+ __be64 identifier, locator_match;
+ size_t nhoff;
+
+ /* Assumes skb contains a valid IPv6 header that is pulled */
+
+ identifier = *(__be64 *)&ip6h->daddr.in6_u.u6_addr8[8];
+ locator_match = *(__be64 *)&ip6h->daddr.in6_u.u6_addr8[0];
+ nhoff = sizeof(struct ipv6hdr);
+
+ rcu_read_lock();
+
+ ila = ila_lookup_wildcards(identifier, locator_match,
+ skb->dev->ifindex, dir, ilan);
+ if (ila)
+ update_ipv6_locator(skb, &ila->p.ip);
+
+ rcu_read_unlock();
+
+ return 0;
+}
+
+int ila_xlat_incoming(struct sk_buff *skb)
+{
+ return ila_xlat_addr(skb, ILA_DIR_IN);
+}
+EXPORT_SYMBOL(ila_xlat_incoming);
+
+int ila_xlat_outgoing(struct sk_buff *skb)
+{
+ return ila_xlat_addr(skb, ILA_DIR_OUT);
+}
+EXPORT_SYMBOL(ila_xlat_outgoing);
+
+int ila_xlat_init(void)
+{
+ int ret;
+
+ ret = register_pernet_device(&ila_net_ops);
+ if (ret)
+ goto exit;
+
+ ret = genl_register_family_with_ops(&ila_nl_family,
+ ila_nl_ops);
+ if (ret < 0)
+ goto unregister;
+
+ return 0;
+
+unregister:
+ unregister_pernet_device(&ila_net_ops);
+exit:
+ return ret;
+}
+
+void ila_xlat_fini(void)
+{
+ genl_unregister_family(&ila_nl_family);
+ unregister_pernet_device(&ila_net_ops);
+}
diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c
index 5d1c7cee2cb2..36c3f0155010 100644
--- a/net/ipv6/inet6_connection_sock.c
+++ b/net/ipv6/inet6_connection_sock.c
@@ -51,12 +51,12 @@ int inet6_csk_bind_conflict(const struct sock *sk,
(sk2->sk_state != TCP_TIME_WAIT &&
!uid_eq(uid,
sock_i_uid((struct sock *)sk2))))) {
- if (ipv6_rcv_saddr_equal(sk, sk2))
+ if (ipv6_rcv_saddr_equal(sk, sk2, true))
break;
}
if (!relax && reuse && sk2->sk_reuse &&
sk2->sk_state != TCP_LISTEN &&
- ipv6_rcv_saddr_equal(sk, sk2))
+ ipv6_rcv_saddr_equal(sk, sk2, true))
break;
}
}
@@ -78,7 +78,9 @@ struct dst_entry *inet6_csk_route_req(const struct sock *sk,
memset(fl6, 0, sizeof(*fl6));
fl6->flowi6_proto = proto;
fl6->daddr = ireq->ir_v6_rmt_addr;
- final_p = fl6_update_dst(fl6, np->opt, &final);
+ rcu_read_lock();
+ final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final);
+ rcu_read_unlock();
fl6->saddr = ireq->ir_v6_loc_addr;
fl6->flowi6_oif = ireq->ir_iif;
fl6->flowi6_mark = ireq->ir_mark;
@@ -109,14 +111,6 @@ void inet6_csk_addr2sockaddr(struct sock *sk, struct sockaddr *uaddr)
EXPORT_SYMBOL_GPL(inet6_csk_addr2sockaddr);
static inline
-void __inet6_csk_dst_store(struct sock *sk, struct dst_entry *dst,
- const struct in6_addr *daddr,
- const struct in6_addr *saddr)
-{
- __ip6_dst_store(sk, dst, daddr, saddr);
-}
-
-static inline
struct dst_entry *__inet6_csk_dst_check(struct sock *sk, u32 cookie)
{
return __sk_dst_check(sk, cookie);
@@ -142,14 +136,16 @@ static struct dst_entry *inet6_csk_route_socket(struct sock *sk,
fl6->fl6_dport = inet->inet_dport;
security_sk_classify_flow(sk, flowi6_to_flowi(fl6));
- final_p = fl6_update_dst(fl6, np->opt, &final);
+ rcu_read_lock();
+ final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final);
+ rcu_read_unlock();
dst = __inet6_csk_dst_check(sk, np->dst_cookie);
if (!dst) {
dst = ip6_dst_lookup_flow(sk, fl6, final_p);
if (!IS_ERR(dst))
- __inet6_csk_dst_store(sk, dst, NULL, NULL);
+ ip6_dst_store(sk, dst, NULL, NULL);
}
return dst;
}
@@ -175,7 +171,8 @@ int inet6_csk_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl_unused
/* Restore final destination back after routing done */
fl6.daddr = sk->sk_v6_daddr;
- res = ip6_xmit(sk, skb, &fl6, np->opt, np->tclass);
+ res = ip6_xmit(sk, skb, &fl6, rcu_dereference(np->opt),
+ np->tclass);
rcu_read_unlock();
return res;
}
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 3c7b9310b33f..f37f18b6b40c 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -24,7 +24,6 @@
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/if_arp.h>
-#include <linux/mroute.h>
#include <linux/init.h>
#include <linux/in6.h>
#include <linux/inetdevice.h>
@@ -1571,13 +1570,11 @@ static int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[],
return -EEXIST;
} else {
t = nt;
-
- ip6gre_tunnel_unlink(ign, t);
- ip6gre_tnl_change(t, &p, !tb[IFLA_MTU]);
- ip6gre_tunnel_link(ign, t);
- netdev_state_change(dev);
}
+ ip6gre_tunnel_unlink(ign, t);
+ ip6gre_tnl_change(t, &p, !tb[IFLA_MTU]);
+ ip6gre_tunnel_link(ign, t);
return 0;
}
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index e6a7bd15b9b7..23de98f976d5 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1322,7 +1322,7 @@ emsgsize:
headersize == sizeof(struct ipv6hdr) &&
length < mtu - headersize &&
!(flags & MSG_MORE) &&
- rt->dst.dev->features & NETIF_F_V6_CSUM)
+ rt->dst.dev->features & (NETIF_F_IPV6_CSUM | NETIF_F_HW_CSUM))
csummode = CHECKSUM_PARTIAL;
if (sk->sk_type == SOCK_DGRAM || sk->sk_type == SOCK_RAW) {
@@ -1353,7 +1353,7 @@ emsgsize:
(skb && skb_is_gso(skb))) &&
(sk->sk_protocol == IPPROTO_UDP) &&
(rt->dst.dev->features & NETIF_F_UFO) &&
- (sk->sk_type == SOCK_DGRAM)) {
+ (sk->sk_type == SOCK_DGRAM) && !udp_get_no_check6_tx(sk)) {
err = ip6_ufo_append_data(sk, queue, getfrag, from, length,
hh_len, fragheaderlen,
transhdrlen, mtu, flags, fl6);
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index eabffbb89795..137fca42aaa6 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -177,7 +177,7 @@ void ip6_tnl_dst_reset(struct ip6_tnl *t)
int i;
for_each_possible_cpu(i)
- ip6_tnl_per_cpu_dst_set(raw_cpu_ptr(t->dst_cache), NULL);
+ ip6_tnl_per_cpu_dst_set(per_cpu_ptr(t->dst_cache, i), NULL);
}
EXPORT_SYMBOL_GPL(ip6_tnl_dst_reset);
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index ad19136086dd..a10e77103c88 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -118,7 +118,7 @@ static void mr6_netlink_event(struct mr6_table *mrt, struct mfc6_cache *mfc,
int cmd);
static int ip6mr_rtm_dumproute(struct sk_buff *skb,
struct netlink_callback *cb);
-static void mroute_clean_tables(struct mr6_table *mrt);
+static void mroute_clean_tables(struct mr6_table *mrt, bool all);
static void ipmr_expire_process(unsigned long arg);
#ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES
@@ -334,7 +334,7 @@ static struct mr6_table *ip6mr_new_table(struct net *net, u32 id)
static void ip6mr_free_table(struct mr6_table *mrt)
{
del_timer_sync(&mrt->ipmr_expire_timer);
- mroute_clean_tables(mrt);
+ mroute_clean_tables(mrt, true);
kfree(mrt);
}
@@ -765,10 +765,6 @@ static struct net_device *ip6mr_reg_vif(struct net *net, struct mr6_table *mrt)
return dev;
failure:
- /* allow the register to be completed before unregistering. */
- rtnl_unlock();
- rtnl_lock();
-
unregister_netdevice(dev);
return NULL;
}
@@ -1542,7 +1538,7 @@ static int ip6mr_mfc_add(struct net *net, struct mr6_table *mrt,
* Close the multicast socket, and clear the vif tables etc
*/
-static void mroute_clean_tables(struct mr6_table *mrt)
+static void mroute_clean_tables(struct mr6_table *mrt, bool all)
{
int i;
LIST_HEAD(list);
@@ -1552,8 +1548,9 @@ static void mroute_clean_tables(struct mr6_table *mrt)
* Shut down all active vif entries
*/
for (i = 0; i < mrt->maxvif; i++) {
- if (!(mrt->vif6_table[i].flags & VIFF_STATIC))
- mif6_delete(mrt, i, &list);
+ if (!all && (mrt->vif6_table[i].flags & VIFF_STATIC))
+ continue;
+ mif6_delete(mrt, i, &list);
}
unregister_netdevice_many(&list);
@@ -1562,7 +1559,7 @@ static void mroute_clean_tables(struct mr6_table *mrt)
*/
for (i = 0; i < MFC6_LINES; i++) {
list_for_each_entry_safe(c, next, &mrt->mfc6_cache_array[i], list) {
- if (c->mfc_flags & MFC_STATIC)
+ if (!all && (c->mfc_flags & MFC_STATIC))
continue;
write_lock_bh(&mrt_lock);
list_del(&c->list);
@@ -1625,7 +1622,7 @@ int ip6mr_sk_done(struct sock *sk)
net->ipv6.devconf_all);
write_unlock_bh(&mrt_lock);
- mroute_clean_tables(mrt);
+ mroute_clean_tables(mrt, false);
err = 0;
break;
}
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 63e6956917c9..4449ad1f8114 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -111,7 +111,8 @@ struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
}
}
- opt = xchg(&inet6_sk(sk)->opt, opt);
+ opt = xchg((__force struct ipv6_txoptions **)&inet6_sk(sk)->opt,
+ opt);
sk_dst_reset(sk);
return opt;
@@ -231,9 +232,12 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
sk->sk_socket->ops = &inet_dgram_ops;
sk->sk_family = PF_INET;
}
- opt = xchg(&np->opt, NULL);
- if (opt)
- sock_kfree_s(sk, opt, opt->tot_len);
+ opt = xchg((__force struct ipv6_txoptions **)&np->opt,
+ NULL);
+ if (opt) {
+ atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
+ txopt_put(opt);
+ }
pktopt = xchg(&np->pktoptions, NULL);
kfree_skb(pktopt);
@@ -403,7 +407,8 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
if (optname != IPV6_RTHDR && !ns_capable(net->user_ns, CAP_NET_RAW))
break;
- opt = ipv6_renew_options(sk, np->opt, optname,
+ opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk));
+ opt = ipv6_renew_options(sk, opt, optname,
(struct ipv6_opt_hdr __user *)optval,
optlen);
if (IS_ERR(opt)) {
@@ -432,8 +437,10 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
retv = 0;
opt = ipv6_update_options(sk, opt);
sticky_done:
- if (opt)
- sock_kfree_s(sk, opt, opt->tot_len);
+ if (opt) {
+ atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
+ txopt_put(opt);
+ }
break;
}
@@ -486,6 +493,7 @@ sticky_done:
break;
memset(opt, 0, sizeof(*opt));
+ atomic_set(&opt->refcnt, 1);
opt->tot_len = sizeof(*opt) + optlen;
retv = -EFAULT;
if (copy_from_user(opt+1, optval, optlen))
@@ -502,8 +510,10 @@ update:
retv = 0;
opt = ipv6_update_options(sk, opt);
done:
- if (opt)
- sock_kfree_s(sk, opt, opt->tot_len);
+ if (opt) {
+ atomic_sub(opt->tot_len, &sk->sk_omem_alloc);
+ txopt_put(opt);
+ }
break;
}
case IPV6_UNICAST_HOPS:
@@ -1110,10 +1120,11 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
case IPV6_RTHDR:
case IPV6_DSTOPTS:
{
+ struct ipv6_txoptions *opt;
lock_sock(sk);
- len = ipv6_getsockopt_sticky(sk, np->opt,
- optname, optval, len);
+ opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk));
+ len = ipv6_getsockopt_sticky(sk, opt, optname, optval, len);
release_sock(sk);
/* check if ipv6_getsockopt_sticky() returns err code */
if (len < 0)
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 3e0f855e1bea..84afb9a77278 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -556,8 +556,7 @@ static void ndisc_send_unsol_na(struct net_device *dev)
}
void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
- const struct in6_addr *daddr, const struct in6_addr *saddr,
- struct sk_buff *oskb)
+ const struct in6_addr *daddr, const struct in6_addr *saddr)
{
struct sk_buff *skb;
struct in6_addr addr_buf;
@@ -593,9 +592,6 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
dev->dev_addr);
- if (!(dev->priv_flags & IFF_XMIT_DST_RELEASE) && oskb)
- skb_dst_copy(skb, oskb);
-
ndisc_send_skb(skb, daddr, saddr);
}
@@ -682,12 +678,12 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
"%s: trying to ucast probe in NUD_INVALID: %pI6\n",
__func__, target);
}
- ndisc_send_ns(dev, target, target, saddr, skb);
+ ndisc_send_ns(dev, target, target, saddr);
} else if ((probes -= NEIGH_VAR(neigh->parms, APP_PROBES)) < 0) {
neigh_app_ns(neigh);
} else {
addrconf_addr_solict_mult(target, &mcaddr);
- ndisc_send_ns(dev, target, &mcaddr, saddr, skb);
+ ndisc_send_ns(dev, target, &mcaddr, saddr);
}
}
@@ -1187,7 +1183,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
*/
if (!in6_dev->cnf.accept_ra_from_local &&
ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr,
- NULL, 0)) {
+ in6_dev->dev, 0)) {
ND_PRINTK(2, info,
"RA from local address detected on dev: %s: default router ignored\n",
skb->dev->name);
@@ -1341,7 +1337,7 @@ skip_linkparms:
#ifdef CONFIG_IPV6_ROUTE_INFO
if (!in6_dev->cnf.accept_ra_from_local &&
ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr,
- NULL, 0)) {
+ in6_dev->dev, 0)) {
ND_PRINTK(2, info,
"RA from local address detected on dev: %s: router info ignored.\n",
skb->dev->name);
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
index f6a024e141e5..e10a04c9cdc7 100644
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
@@ -49,6 +49,7 @@ config NFT_REJECT_IPV6
config NFT_DUP_IPV6
tristate "IPv6 nf_tables packet duplication support"
+ depends on !NF_CONNTRACK || NF_CONNTRACK
select NF_DUP_IPV6
help
This module enables IPv6 packet duplication support for nf_tables.
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index d5efeb87350e..e4347aeb2e65 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -56,7 +56,6 @@ struct nf_ct_frag6_skb_cb
{
struct inet6_skb_parm h;
int offset;
- struct sk_buff *orig;
};
#define NFCT_FRAG6_CB(skb) ((struct nf_ct_frag6_skb_cb *)((skb)->cb))
@@ -170,12 +169,6 @@ static unsigned int nf_hashfn(const struct inet_frag_queue *q)
return nf_hash_frag(nq->id, &nq->saddr, &nq->daddr);
}
-static void nf_skb_free(struct sk_buff *skb)
-{
- if (NFCT_FRAG6_CB(skb)->orig)
- kfree_skb(NFCT_FRAG6_CB(skb)->orig);
-}
-
static void nf_ct_frag6_expire(unsigned long data)
{
struct frag_queue *fq;
@@ -190,7 +183,7 @@ static void nf_ct_frag6_expire(unsigned long data)
/* Creation primitives. */
static inline struct frag_queue *fq_find(struct net *net, __be32 id,
u32 user, struct in6_addr *src,
- struct in6_addr *dst, u8 ecn)
+ struct in6_addr *dst, int iif, u8 ecn)
{
struct inet_frag_queue *q;
struct ip6_create_arg arg;
@@ -200,6 +193,7 @@ static inline struct frag_queue *fq_find(struct net *net, __be32 id,
arg.user = user;
arg.src = src;
arg.dst = dst;
+ arg.iif = iif;
arg.ecn = ecn;
local_bh_disable();
@@ -368,17 +362,18 @@ err:
/*
* Check if this packet is complete.
- * Returns NULL on failure by any reason, and pointer
- * to current nexthdr field in reassembled frame.
*
* It is called with locked fq, and caller must check that
* queue is eligible for reassembly i.e. it is not COMPLETE,
* the last and the first frames arrived and all the bits are here.
+ *
+ * returns true if *prev skb has been transformed into the reassembled
+ * skb, false otherwise.
*/
-static struct sk_buff *
-nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
+static bool
+nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *prev, struct net_device *dev)
{
- struct sk_buff *fp, *op, *head = fq->q.fragments;
+ struct sk_buff *fp, *head = fq->q.fragments;
int payload_len;
u8 ecn;
@@ -389,22 +384,21 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
ecn = ip_frag_ecn_table[fq->ecn];
if (unlikely(ecn == 0xff))
- goto out_fail;
+ return false;
/* Unfragmented part is taken from the first segment. */
payload_len = ((head->data - skb_network_header(head)) -
sizeof(struct ipv6hdr) + fq->q.len -
sizeof(struct frag_hdr));
if (payload_len > IPV6_MAXPLEN) {
- pr_debug("payload len is too large.\n");
- goto out_oversize;
+ net_dbg_ratelimited("nf_ct_frag6_reasm: payload len = %d\n",
+ payload_len);
+ return false;
}
/* Head of list must not be cloned. */
- if (skb_unclone(head, GFP_ATOMIC)) {
- pr_debug("skb is cloned but can't expand head");
- goto out_oom;
- }
+ if (skb_unclone(head, GFP_ATOMIC))
+ return false;
/* If the first fragment is fragmented itself, we split
* it to two chunks: the first with data and paged part
@@ -415,7 +409,7 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
clone = alloc_skb(0, GFP_ATOMIC);
if (clone == NULL)
- goto out_oom;
+ return false;
clone->next = head->next;
head->next = clone;
@@ -429,10 +423,41 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
clone->csum = 0;
clone->ip_summed = head->ip_summed;
- NFCT_FRAG6_CB(clone)->orig = NULL;
add_frag_mem_limit(fq->q.net, clone->truesize);
}
+ /* morph head into last received skb: prev.
+ *
+ * This allows callers of ipv6 conntrack defrag to continue
+ * to use the last skb(frag) passed into the reasm engine.
+ * The last skb frag 'silently' turns into the full reassembled skb.
+ *
+ * Since prev is also part of q->fragments we have to clone it first.
+ */
+ if (head != prev) {
+ struct sk_buff *iter;
+
+ fp = skb_clone(prev, GFP_ATOMIC);
+ if (!fp)
+ return false;
+
+ fp->next = prev->next;
+
+ iter = head;
+ while (iter) {
+ if (iter->next == prev) {
+ iter->next = fp;
+ break;
+ }
+ iter = iter->next;
+ }
+
+ skb_morph(prev, head);
+ prev->next = head->next;
+ consume_skb(head);
+ head = prev;
+ }
+
/* We have to remove fragment header from datagram and to relocate
* header in order to calculate ICV correctly. */
skb_network_header(head)[fq->nhoffset] = skb_transport_header(head)[0];
@@ -473,31 +498,7 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
fq->q.fragments = NULL;
fq->q.fragments_tail = NULL;
- /* all original skbs are linked into the NFCT_FRAG6_CB(head).orig */
- fp = skb_shinfo(head)->frag_list;
- if (fp && NFCT_FRAG6_CB(fp)->orig == NULL)
- /* at above code, head skb is divided into two skbs. */
- fp = fp->next;
-
- op = NFCT_FRAG6_CB(head)->orig;
- for (; fp; fp = fp->next) {
- struct sk_buff *orig = NFCT_FRAG6_CB(fp)->orig;
-
- op->next = orig;
- op = orig;
- NFCT_FRAG6_CB(fp)->orig = NULL;
- }
-
- return head;
-
-out_oversize:
- net_dbg_ratelimited("nf_ct_frag6_reasm: payload len = %d\n",
- payload_len);
- goto out_fail;
-out_oom:
- net_dbg_ratelimited("nf_ct_frag6_reasm: no memory for reassembly\n");
-out_fail:
- return NULL;
+ return true;
}
/*
@@ -563,89 +564,61 @@ find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff)
return 0;
}
-struct sk_buff *nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)
+int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)
{
- struct sk_buff *clone;
struct net_device *dev = skb->dev;
+ int fhoff, nhoff, ret;
struct frag_hdr *fhdr;
struct frag_queue *fq;
struct ipv6hdr *hdr;
- int fhoff, nhoff;
u8 prevhdr;
- struct sk_buff *ret_skb = NULL;
/* Jumbo payload inhibits frag. header */
if (ipv6_hdr(skb)->payload_len == 0) {
pr_debug("payload len = 0\n");
- return skb;
+ return -EINVAL;
}
if (find_prev_fhdr(skb, &prevhdr, &nhoff, &fhoff) < 0)
- return skb;
+ return -EINVAL;
- clone = skb_clone(skb, GFP_ATOMIC);
- if (clone == NULL) {
- pr_debug("Can't clone skb\n");
- return skb;
- }
+ if (!pskb_may_pull(skb, fhoff + sizeof(*fhdr)))
+ return -ENOMEM;
- NFCT_FRAG6_CB(clone)->orig = skb;
-
- if (!pskb_may_pull(clone, fhoff + sizeof(*fhdr))) {
- pr_debug("message is too short.\n");
- goto ret_orig;
- }
-
- skb_set_transport_header(clone, fhoff);
- hdr = ipv6_hdr(clone);
- fhdr = (struct frag_hdr *)skb_transport_header(clone);
+ skb_set_transport_header(skb, fhoff);
+ hdr = ipv6_hdr(skb);
+ fhdr = (struct frag_hdr *)skb_transport_header(skb);
fq = fq_find(net, fhdr->identification, user, &hdr->saddr, &hdr->daddr,
- ip6_frag_ecn(hdr));
+ skb->dev ? skb->dev->ifindex : 0, ip6_frag_ecn(hdr));
if (fq == NULL) {
pr_debug("Can't find and can't create new queue\n");
- goto ret_orig;
+ return -ENOMEM;
}
spin_lock_bh(&fq->q.lock);
- if (nf_ct_frag6_queue(fq, clone, fhdr, nhoff) < 0) {
- spin_unlock_bh(&fq->q.lock);
- pr_debug("Can't insert skb to queue\n");
- inet_frag_put(&fq->q, &nf_frags);
- goto ret_orig;
+ if (nf_ct_frag6_queue(fq, skb, fhdr, nhoff) < 0) {
+ ret = -EINVAL;
+ goto out_unlock;
}
+ /* after queue has assumed skb ownership, only 0 or -EINPROGRESS
+ * must be returned.
+ */
+ ret = -EINPROGRESS;
if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
- fq->q.meat == fq->q.len) {
- ret_skb = nf_ct_frag6_reasm(fq, dev);
- if (ret_skb == NULL)
- pr_debug("Can't reassemble fragmented packets\n");
- }
- spin_unlock_bh(&fq->q.lock);
+ fq->q.meat == fq->q.len &&
+ nf_ct_frag6_reasm(fq, skb, dev))
+ ret = 0;
+out_unlock:
+ spin_unlock_bh(&fq->q.lock);
inet_frag_put(&fq->q, &nf_frags);
- return ret_skb;
-
-ret_orig:
- kfree_skb(clone);
- return skb;
+ return ret;
}
EXPORT_SYMBOL_GPL(nf_ct_frag6_gather);
-void nf_ct_frag6_consume_orig(struct sk_buff *skb)
-{
- struct sk_buff *s, *s2;
-
- for (s = NFCT_FRAG6_CB(skb)->orig; s;) {
- s2 = s->next;
- s->next = NULL;
- consume_skb(s);
- s = s2;
- }
-}
-EXPORT_SYMBOL_GPL(nf_ct_frag6_consume_orig);
-
static int nf_ct_net_init(struct net *net)
{
int res;
@@ -680,7 +653,6 @@ int nf_ct_frag6_init(void)
nf_frags.hashfn = nf_hashfn;
nf_frags.constructor = ip6_frag_init;
nf_frags.destructor = NULL;
- nf_frags.skb_free = nf_skb_free;
nf_frags.qsize = sizeof(struct frag_queue);
nf_frags.match = ip6_frag_match;
nf_frags.frag_expire = nf_ct_frag6_expire;
diff --git a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
index 4fdbed5ebfb6..f7aab5ab93a5 100644
--- a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
+++ b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
@@ -55,7 +55,7 @@ static unsigned int ipv6_defrag(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
- struct sk_buff *reasm;
+ int err;
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
/* Previously seen (loopback)? */
@@ -63,23 +63,13 @@ static unsigned int ipv6_defrag(void *priv,
return NF_ACCEPT;
#endif
- reasm = nf_ct_frag6_gather(state->net, skb,
- nf_ct6_defrag_user(state->hook, skb));
+ err = nf_ct_frag6_gather(state->net, skb,
+ nf_ct6_defrag_user(state->hook, skb));
/* queued */
- if (reasm == NULL)
+ if (err == -EINPROGRESS)
return NF_STOLEN;
- /* error occurred or not fragmented */
- if (reasm == skb)
- return NF_ACCEPT;
-
- nf_ct_frag6_consume_orig(reasm);
-
- NF_HOOK_THRESH(NFPROTO_IPV6, state->hook, state->net, state->sk, reasm,
- state->in, state->out,
- state->okfn, NF_IP6_PRI_CONNTRACK_DEFRAG + 1);
-
- return NF_STOLEN;
+ return NF_ACCEPT;
}
static struct nf_hook_ops ipv6_defrag_ops[] = {
diff --git a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
index 238e70c3f7b7..6ce309928841 100644
--- a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
+++ b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
@@ -136,7 +136,8 @@ static void nf_nat_ipv6_csum_recalc(struct sk_buff *skb,
if (skb->ip_summed != CHECKSUM_PARTIAL) {
if (!(rt->rt6i_flags & RTF_LOCAL) &&
- (!skb->dev || skb->dev->features & NETIF_F_V6_CSUM)) {
+ (!skb->dev || skb->dev->features &
+ (NETIF_F_IPV6_CSUM | NETIF_F_HW_CSUM))) {
skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum_start = skb_headroom(skb) +
skb_network_offset(skb) +
diff --git a/net/ipv6/netfilter/nf_reject_ipv6.c b/net/ipv6/netfilter/nf_reject_ipv6.c
index e0f922b777e3..4709f657b7b6 100644
--- a/net/ipv6/netfilter/nf_reject_ipv6.c
+++ b/net/ipv6/netfilter/nf_reject_ipv6.c
@@ -14,7 +14,6 @@
#include <net/netfilter/ipv6/nf_reject.h>
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter_bridge.h>
-#include <net/netfilter/ipv6/nf_reject.h>
const struct tcphdr *nf_reject_ip6_tcphdr_get(struct sk_buff *oldskb,
struct tcphdr *otcph,
diff --git a/net/ipv6/netfilter/nf_tables_ipv6.c b/net/ipv6/netfilter/nf_tables_ipv6.c
index 120ea9131be0..30b22f4dff55 100644
--- a/net/ipv6/netfilter/nf_tables_ipv6.c
+++ b/net/ipv6/netfilter/nf_tables_ipv6.c
@@ -77,7 +77,7 @@ err:
static void nf_tables_ipv6_exit_net(struct net *net)
{
- nft_unregister_afinfo(net->nft.ipv6);
+ nft_unregister_afinfo(net, net->nft.ipv6);
kfree(net->nft.ipv6);
}
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index dc65ec198f7c..fa59dd7a427e 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -733,6 +733,7 @@ static int raw6_getfrag(void *from, char *to, int offset, int len, int odd,
static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
{
+ struct ipv6_txoptions *opt_to_free = NULL;
struct ipv6_txoptions opt_space;
DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name);
struct in6_addr *daddr, *final_p, final;
@@ -839,8 +840,10 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
if (!(opt->opt_nflen|opt->opt_flen))
opt = NULL;
}
- if (!opt)
- opt = np->opt;
+ if (!opt) {
+ opt = txopt_get(np);
+ opt_to_free = opt;
+ }
if (flowlabel)
opt = fl6_merge_options(&opt_space, flowlabel, opt);
opt = ipv6_fixup_options(&opt_space, opt);
@@ -906,6 +909,7 @@ done:
dst_release(dst);
out:
fl6_sock_release(flowlabel);
+ txopt_put(opt_to_free);
return err < 0 ? err : len;
do_confirm:
dst_confirm(dst);
@@ -968,6 +972,11 @@ static int do_rawv6_setsockopt(struct sock *sk, int level, int optname,
return -EFAULT;
switch (optname) {
+ case IPV6_HDRINCL:
+ if (sk->sk_type != SOCK_RAW)
+ return -EINVAL;
+ inet_sk(sk)->hdrincl = !!val;
+ return 0;
case IPV6_CHECKSUM:
if (inet_sk(sk)->inet_num == IPPROTO_ICMPV6 &&
level == IPPROTO_IPV6) {
@@ -1012,7 +1021,8 @@ static int rawv6_setsockopt(struct sock *sk, int level, int optname,
return -EOPNOTSUPP;
return rawv6_seticmpfilter(sk, level, optname, optval, optlen);
case SOL_IPV6:
- if (optname == IPV6_CHECKSUM)
+ if (optname == IPV6_CHECKSUM ||
+ optname == IPV6_HDRINCL)
break;
default:
return ipv6_setsockopt(sk, level, optname, optval, optlen);
@@ -1033,7 +1043,8 @@ static int compat_rawv6_setsockopt(struct sock *sk, int level, int optname,
return -EOPNOTSUPP;
return rawv6_seticmpfilter(sk, level, optname, optval, optlen);
case SOL_IPV6:
- if (optname == IPV6_CHECKSUM)
+ if (optname == IPV6_CHECKSUM ||
+ optname == IPV6_HDRINCL)
break;
default:
return compat_ipv6_setsockopt(sk, level, optname,
@@ -1053,6 +1064,9 @@ static int do_rawv6_getsockopt(struct sock *sk, int level, int optname,
return -EFAULT;
switch (optname) {
+ case IPV6_HDRINCL:
+ val = inet_sk(sk)->hdrincl;
+ break;
case IPV6_CHECKSUM:
/*
* We allow getsockopt() for IPPROTO_IPV6-level
@@ -1090,7 +1104,8 @@ static int rawv6_getsockopt(struct sock *sk, int level, int optname,
return -EOPNOTSUPP;
return rawv6_geticmpfilter(sk, level, optname, optval, optlen);
case SOL_IPV6:
- if (optname == IPV6_CHECKSUM)
+ if (optname == IPV6_CHECKSUM ||
+ optname == IPV6_HDRINCL)
break;
default:
return ipv6_getsockopt(sk, level, optname, optval, optlen);
@@ -1111,7 +1126,8 @@ static int compat_rawv6_getsockopt(struct sock *sk, int level, int optname,
return -EOPNOTSUPP;
return rawv6_geticmpfilter(sk, level, optname, optval, optlen);
case SOL_IPV6:
- if (optname == IPV6_CHECKSUM)
+ if (optname == IPV6_CHECKSUM ||
+ optname == IPV6_HDRINCL)
break;
default:
return compat_ipv6_getsockopt(sk, level, optname,
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index 44e21a03cfc3..18f3498a6c80 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -108,7 +108,10 @@ bool ip6_frag_match(const struct inet_frag_queue *q, const void *a)
return fq->id == arg->id &&
fq->user == arg->user &&
ipv6_addr_equal(&fq->saddr, arg->src) &&
- ipv6_addr_equal(&fq->daddr, arg->dst);
+ ipv6_addr_equal(&fq->daddr, arg->dst) &&
+ (arg->iif == fq->iif ||
+ !(ipv6_addr_type(arg->dst) & (IPV6_ADDR_MULTICAST |
+ IPV6_ADDR_LINKLOCAL)));
}
EXPORT_SYMBOL(ip6_frag_match);
@@ -180,7 +183,7 @@ static void ip6_frag_expire(unsigned long data)
static struct frag_queue *
fq_find(struct net *net, __be32 id, const struct in6_addr *src,
- const struct in6_addr *dst, u8 ecn)
+ const struct in6_addr *dst, int iif, u8 ecn)
{
struct inet_frag_queue *q;
struct ip6_create_arg arg;
@@ -190,6 +193,7 @@ fq_find(struct net *net, __be32 id, const struct in6_addr *src,
arg.user = IP6_DEFRAG_LOCAL_DELIVER;
arg.src = src;
arg.dst = dst;
+ arg.iif = iif;
arg.ecn = ecn;
hash = inet6_hash_frag(id, src, dst);
@@ -551,7 +555,7 @@ static int ipv6_frag_rcv(struct sk_buff *skb)
}
fq = fq_find(net, fhdr->identification, &hdr->saddr, &hdr->daddr,
- ip6_frag_ecn(hdr));
+ skb->dev ? skb->dev->ifindex : 0, ip6_frag_ecn(hdr));
if (fq) {
int ret;
@@ -751,7 +755,6 @@ int __init ipv6_frag_init(void)
ip6_frags.hashfn = ip6_hashfn;
ip6_frags.constructor = ip6_frag_init;
ip6_frags.destructor = NULL;
- ip6_frags.skb_free = NULL;
ip6_frags.qsize = sizeof(struct frag_queue);
ip6_frags.match = ip6_frag_match;
ip6_frags.frag_expire = ip6_frag_expire;
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 6f01fe122abd..3c8834bc822d 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -62,6 +62,7 @@
#include <net/lwtunnel.h>
#include <net/ip_tunnels.h>
#include <net/l3mdev.h>
+#include <trace/events/fib6.h>
#include <asm/uaccess.h>
@@ -523,7 +524,7 @@ static void rt6_probe_deferred(struct work_struct *w)
container_of(w, struct __rt6_probe_work, work);
addrconf_addr_solict_mult(&work->target, &mcaddr);
- ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, NULL);
+ ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL);
dev_put(work->dev);
kfree(work);
}
@@ -865,6 +866,9 @@ restart:
}
dst_use(&rt->dst, jiffies);
read_unlock_bh(&table->tb6_lock);
+
+ trace_fib6_table_lookup(net, rt, table->tb6_id, fl6);
+
return rt;
}
@@ -1078,6 +1082,8 @@ redo_rt6_select:
read_unlock_bh(&table->tb6_lock);
rt6_dst_from_metrics_check(rt);
+
+ trace_fib6_table_lookup(net, rt, table->tb6_id, fl6);
return rt;
} else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) &&
!(rt->rt6i_flags & RTF_GATEWAY))) {
@@ -1101,6 +1107,8 @@ redo_rt6_select:
uncached_rt = net->ipv6.ip6_null_entry;
dst_hold(&uncached_rt->dst);
+
+ trace_fib6_table_lookup(net, uncached_rt, table->tb6_id, fl6);
return uncached_rt;
} else {
@@ -1125,6 +1133,7 @@ redo_rt6_select:
dst_release(&rt->dst);
}
+ trace_fib6_table_lookup(net, pcpu_rt, table->tb6_id, fl6);
return pcpu_rt;
}
@@ -1474,6 +1483,7 @@ out:
read_unlock_bh(&table->tb6_lock);
+ trace_fib6_table_lookup(net, rt, table->tb6_id, fl6);
return rt;
};
@@ -2699,6 +2709,7 @@ static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
[RTA_PREF] = { .type = NLA_U8 },
[RTA_ENCAP_TYPE] = { .type = NLA_U16 },
[RTA_ENCAP] = { .type = NLA_NESTED },
+ [RTA_EXPIRES] = { .type = NLA_U32 },
};
static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
@@ -2799,6 +2810,15 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
if (tb[RTA_ENCAP_TYPE])
cfg->fc_encap_type = nla_get_u16(tb[RTA_ENCAP_TYPE]);
+ if (tb[RTA_EXPIRES]) {
+ unsigned long timeout = addrconf_timeout_fixup(nla_get_u32(tb[RTA_EXPIRES]), HZ);
+
+ if (addrconf_finite_timeout(timeout)) {
+ cfg->fc_expires = jiffies_to_clock_t(timeout * HZ);
+ cfg->fc_flags |= RTF_EXPIRES;
+ }
+ }
+
err = 0;
errout:
return err;
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index dcccae86190f..e794ef66a401 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -820,7 +820,6 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
const struct in6_addr *addr6;
int addr_type;
u8 ttl;
- int err;
u8 protocol = IPPROTO_IPV6;
int t_hlen = tunnel->hlen + sizeof(struct iphdr);
@@ -983,10 +982,8 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
skb_set_inner_ipproto(skb, IPPROTO_IPV6);
- err = iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr,
- protocol, tos, ttl, df,
- !net_eq(tunnel->net, dev_net(dev)));
- iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
+ iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, protocol, tos, ttl,
+ df, !net_eq(tunnel->net, dev_net(dev)));
return NETDEV_TX_OK;
tx_error_icmp:
diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
index bb8f2fa1c7fb..2906ef20795e 100644
--- a/net/ipv6/syncookies.c
+++ b/net/ipv6/syncookies.c
@@ -193,7 +193,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
ireq->pktopts = skb;
}
- ireq->ir_iif = sk->sk_bound_dev_if;
+ ireq->ir_iif = inet_request_bound_dev_if(sk, skb);
/* So that link locals have meaning */
if (!sk->sk_bound_dev_if &&
ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL)
@@ -222,9 +222,9 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
memset(&fl6, 0, sizeof(fl6));
fl6.flowi6_proto = IPPROTO_TCP;
fl6.daddr = ireq->ir_v6_rmt_addr;
- final_p = fl6_update_dst(&fl6, np->opt, &final);
+ final_p = fl6_update_dst(&fl6, rcu_dereference(np->opt), &final);
fl6.saddr = ireq->ir_v6_loc_addr;
- fl6.flowi6_oif = sk->sk_bound_dev_if;
+ fl6.flowi6_oif = ireq->ir_iif;
fl6.flowi6_mark = ireq->ir_mark;
fl6.fl6_dport = ireq->ir_rmt_port;
fl6.fl6_sport = inet_sk(sk)->inet_sport;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index c5429a636f1a..4ad8edb46f7c 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -93,10 +93,9 @@ static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)
{
struct dst_entry *dst = skb_dst(skb);
- if (dst) {
+ if (dst && dst_hold_safe(dst)) {
const struct rt6_info *rt = (const struct rt6_info *)dst;
- dst_hold(dst);
sk->sk_rx_dst = dst;
inet_sk(sk)->rx_dst_ifindex = skb->skb_iif;
inet6_sk(sk)->rx_dst_cookie = rt6_get_cookie(rt);
@@ -120,6 +119,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
struct ipv6_pinfo *np = inet6_sk(sk);
struct tcp_sock *tp = tcp_sk(sk);
struct in6_addr *saddr = NULL, *final_p, final;
+ struct ipv6_txoptions *opt;
struct flowi6 fl6;
struct dst_entry *dst;
int addr_type;
@@ -235,7 +235,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
fl6.fl6_dport = usin->sin6_port;
fl6.fl6_sport = inet->inet_sport;
- final_p = fl6_update_dst(&fl6, np->opt, &final);
+ opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk));
+ final_p = fl6_update_dst(&fl6, opt, &final);
security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
@@ -255,7 +256,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
inet->inet_rcv_saddr = LOOPBACK4_IPV6;
sk->sk_gso_type = SKB_GSO_TCPV6;
- __ip6_dst_store(sk, dst, NULL, NULL);
+ ip6_dst_store(sk, dst, NULL, NULL);
if (tcp_death_row.sysctl_tw_recycle &&
!tp->rx_opt.ts_recent_stamp &&
@@ -263,9 +264,9 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
tcp_fetch_timewait_stamp(sk, dst);
icsk->icsk_ext_hdr_len = 0;
- if (np->opt)
- icsk->icsk_ext_hdr_len = (np->opt->opt_flen +
- np->opt->opt_nflen);
+ if (opt)
+ icsk->icsk_ext_hdr_len = opt->opt_flen +
+ opt->opt_nflen;
tp->rx_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);
@@ -461,7 +462,10 @@ static int tcp_v6_send_synack(const struct sock *sk, struct dst_entry *dst,
if (np->repflow && ireq->pktopts)
fl6->flowlabel = ip6_flowlabel(ipv6_hdr(ireq->pktopts));
- err = ip6_xmit(sk, skb, fl6, np->opt, np->tclass);
+ rcu_read_lock();
+ err = ip6_xmit(sk, skb, fl6, rcu_dereference(np->opt),
+ np->tclass);
+ rcu_read_unlock();
err = net_xmit_eval(err);
}
@@ -852,7 +856,9 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
#ifdef CONFIG_TCP_MD5SIG
hash_location = tcp_parse_md5sig_option(th);
- if (!sk && hash_location) {
+ if (sk && sk_fullsock(sk)) {
+ key = tcp_v6_md5_do_lookup(sk, &ipv6h->saddr);
+ } else if (hash_location) {
/*
* active side is lost. Try to find listening socket through
* source port, and then find md5 key through listening socket.
@@ -875,8 +881,6 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
genhash = tcp_v6_md5_hash_skb(newhash, key, NULL, skb);
if (genhash || memcmp(hash_location, newhash, 16) != 0)
goto release_sk1;
- } else {
- key = sk ? tcp_v6_md5_do_lookup(sk, &ipv6h->saddr) : NULL;
}
#endif
@@ -972,6 +976,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
struct inet_request_sock *ireq;
struct ipv6_pinfo *newnp;
const struct ipv6_pinfo *np = inet6_sk(sk);
+ struct ipv6_txoptions *opt;
struct tcp6_sock *newtcp6sk;
struct inet_sock *newinet;
struct tcp_sock *newtp;
@@ -1056,7 +1061,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
*/
newsk->sk_gso_type = SKB_GSO_TCPV6;
- __ip6_dst_store(newsk, dst, NULL, NULL);
+ ip6_dst_store(newsk, dst, NULL, NULL);
inet6_sk_rx_dst_set(newsk, skb);
newtcp6sk = (struct tcp6_sock *)newsk;
@@ -1098,13 +1103,15 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
but we make one more one thing there: reattach optmem
to newsk.
*/
- if (np->opt)
- newnp->opt = ipv6_dup_options(newsk, np->opt);
-
+ opt = rcu_dereference(np->opt);
+ if (opt) {
+ opt = ipv6_dup_options(newsk, opt);
+ RCU_INIT_POINTER(newnp->opt, opt);
+ }
inet_csk(newsk)->icsk_ext_hdr_len = 0;
- if (newnp->opt)
- inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen +
- newnp->opt->opt_flen);
+ if (opt)
+ inet_csk(newsk)->icsk_ext_hdr_len = opt->opt_nflen +
+ opt->opt_flen;
tcp_ca_openreq_child(newsk, dst);
@@ -1130,7 +1137,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
*/
tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr,
AF_INET6, key->key, key->keylen,
- sk_gfp_atomic(sk, GFP_ATOMIC));
+ sk_gfp_mask(sk, GFP_ATOMIC));
}
#endif
@@ -1146,7 +1153,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
/* Clone pktoptions received with SYN, if we own the req */
if (ireq->pktopts) {
newnp->pktoptions = skb_clone(ireq->pktopts,
- sk_gfp_atomic(sk, GFP_ATOMIC));
+ sk_gfp_mask(sk, GFP_ATOMIC));
consume_skb(ireq->pktopts);
ireq->pktopts = NULL;
if (newnp->pktoptions)
@@ -1212,7 +1219,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
--ANK (980728)
*/
if (np->rxopt.all)
- opt_skb = skb_clone(skb, sk_gfp_atomic(sk, GFP_ATOMIC));
+ opt_skb = skb_clone(skb, sk_gfp_mask(sk, GFP_ATOMIC));
if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
struct dst_entry *dst = sk->sk_rx_dst;
@@ -1511,7 +1518,9 @@ do_time_wait:
break;
case TCP_TW_RST:
tcp_v6_restore_cb(skb);
- goto no_tcp_socket;
+ tcp_v6_send_reset(sk, skb);
+ inet_twsk_deschedule_put(inet_twsk(sk));
+ goto discard_it;
case TCP_TW_SUCCESS:
;
}
@@ -1880,10 +1889,8 @@ struct proto tcpv6_prot = {
.compat_setsockopt = compat_tcp_setsockopt,
.compat_getsockopt = compat_tcp_getsockopt,
#endif
-#ifdef CONFIG_MEMCG_KMEM
- .proto_cgroup = tcp_proto_cgroup,
-#endif
.clear_sk = tcp_v6_clear_sk,
+ .diag_destroy = tcp_abort,
};
static const struct inet6_protocol tcpv6_protocol = {
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 01bcb49619ee..5d2c2afffe7b 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -47,6 +47,7 @@
#include <net/xfrm.h>
#include <net/inet6_hashtables.h>
#include <net/busy_poll.h>
+#include <net/sock_reuseport.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
@@ -76,7 +77,14 @@ static u32 udp6_ehashfn(const struct net *net,
udp_ipv6_hash_secret + net_hash_mix(net));
}
-int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)
+/* match_wildcard == true: IPV6_ADDR_ANY equals to any IPv6 addresses if IPv6
+ * only, and any IPv4 addresses if not IPv6 only
+ * match_wildcard == false: addresses must be exactly the same, i.e.
+ * IPV6_ADDR_ANY only equals to IPV6_ADDR_ANY,
+ * and 0.0.0.0 equals to 0.0.0.0 only
+ */
+int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2,
+ bool match_wildcard)
{
const struct in6_addr *sk2_rcv_saddr6 = inet6_rcv_saddr(sk2);
int sk2_ipv6only = inet_v6_ipv6only(sk2);
@@ -84,16 +92,24 @@ int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)
int addr_type2 = sk2_rcv_saddr6 ? ipv6_addr_type(sk2_rcv_saddr6) : IPV6_ADDR_MAPPED;
/* if both are mapped, treat as IPv4 */
- if (addr_type == IPV6_ADDR_MAPPED && addr_type2 == IPV6_ADDR_MAPPED)
- return (!sk2_ipv6only &&
- (!sk->sk_rcv_saddr || !sk2->sk_rcv_saddr ||
- sk->sk_rcv_saddr == sk2->sk_rcv_saddr));
+ if (addr_type == IPV6_ADDR_MAPPED && addr_type2 == IPV6_ADDR_MAPPED) {
+ if (!sk2_ipv6only) {
+ if (sk->sk_rcv_saddr == sk2->sk_rcv_saddr)
+ return 1;
+ if (!sk->sk_rcv_saddr || !sk2->sk_rcv_saddr)
+ return match_wildcard;
+ }
+ return 0;
+ }
- if (addr_type2 == IPV6_ADDR_ANY &&
+ if (addr_type == IPV6_ADDR_ANY && addr_type2 == IPV6_ADDR_ANY)
+ return 1;
+
+ if (addr_type2 == IPV6_ADDR_ANY && match_wildcard &&
!(sk2_ipv6only && addr_type == IPV6_ADDR_MAPPED))
return 1;
- if (addr_type == IPV6_ADDR_ANY &&
+ if (addr_type == IPV6_ADDR_ANY && match_wildcard &&
!(ipv6_only_sock(sk) && addr_type2 == IPV6_ADDR_MAPPED))
return 1;
@@ -235,7 +251,8 @@ static inline int compute_score2(struct sock *sk, struct net *net,
static struct sock *udp6_lib_lookup2(struct net *net,
const struct in6_addr *saddr, __be16 sport,
const struct in6_addr *daddr, unsigned int hnum, int dif,
- struct udp_hslot *hslot2, unsigned int slot2)
+ struct udp_hslot *hslot2, unsigned int slot2,
+ struct sk_buff *skb)
{
struct sock *sk, *result;
struct hlist_nulls_node *node;
@@ -253,8 +270,15 @@ begin:
badness = score;
reuseport = sk->sk_reuseport;
if (reuseport) {
+ struct sock *sk2;
hash = udp6_ehashfn(net, daddr, hnum,
saddr, sport);
+ sk2 = reuseport_select_sock(sk, hash, skb,
+ sizeof(struct udphdr));
+ if (sk2) {
+ result = sk2;
+ goto found;
+ }
matches = 1;
}
} else if (score == badness && reuseport) {
@@ -273,6 +297,7 @@ begin:
goto begin;
if (result) {
+found:
if (unlikely(!atomic_inc_not_zero_hint(&result->sk_refcnt, 2)))
result = NULL;
else if (unlikely(compute_score2(result, net, saddr, sport,
@@ -287,7 +312,8 @@ begin:
struct sock *__udp6_lib_lookup(struct net *net,
const struct in6_addr *saddr, __be16 sport,
const struct in6_addr *daddr, __be16 dport,
- int dif, struct udp_table *udptable)
+ int dif, struct udp_table *udptable,
+ struct sk_buff *skb)
{
struct sock *sk, *result;
struct hlist_nulls_node *node;
@@ -307,7 +333,7 @@ struct sock *__udp6_lib_lookup(struct net *net,
result = udp6_lib_lookup2(net, saddr, sport,
daddr, hnum, dif,
- hslot2, slot2);
+ hslot2, slot2, skb);
if (!result) {
hash2 = udp6_portaddr_hash(net, &in6addr_any, hnum);
slot2 = hash2 & udptable->mask;
@@ -317,7 +343,7 @@ struct sock *__udp6_lib_lookup(struct net *net,
result = udp6_lib_lookup2(net, saddr, sport,
&in6addr_any, hnum, dif,
- hslot2, slot2);
+ hslot2, slot2, skb);
}
rcu_read_unlock();
return result;
@@ -332,8 +358,15 @@ begin:
badness = score;
reuseport = sk->sk_reuseport;
if (reuseport) {
+ struct sock *sk2;
hash = udp6_ehashfn(net, daddr, hnum,
saddr, sport);
+ sk2 = reuseport_select_sock(sk, hash, skb,
+ sizeof(struct udphdr));
+ if (sk2) {
+ result = sk2;
+ goto found;
+ }
matches = 1;
}
} else if (score == badness && reuseport) {
@@ -352,6 +385,7 @@ begin:
goto begin;
if (result) {
+found:
if (unlikely(!atomic_inc_not_zero_hint(&result->sk_refcnt, 2)))
result = NULL;
else if (unlikely(compute_score(result, net, hnum, saddr, sport,
@@ -377,13 +411,13 @@ static struct sock *__udp6_lib_lookup_skb(struct sk_buff *skb,
return sk;
return __udp6_lib_lookup(dev_net(skb_dst(skb)->dev), &iph->saddr, sport,
&iph->daddr, dport, inet6_iif(skb),
- udptable);
+ udptable, skb);
}
struct sock *udp6_lib_lookup(struct net *net, const struct in6_addr *saddr, __be16 sport,
const struct in6_addr *daddr, __be16 dport, int dif)
{
- return __udp6_lib_lookup(net, saddr, sport, daddr, dport, dif, &udp_table);
+ return __udp6_lib_lookup(net, saddr, sport, daddr, dport, dif, &udp_table, NULL);
}
EXPORT_SYMBOL_GPL(udp6_lib_lookup);
@@ -402,6 +436,7 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
int peeked, off = 0;
int err;
int is_udplite = IS_UDPLITE(sk);
+ bool checksum_valid = false;
int is_udp4;
bool slow;
@@ -433,11 +468,12 @@ try_again:
*/
if (copied < ulen || UDP_SKB_CB(skb)->partial_cov) {
- if (udp_lib_checksum_complete(skb))
+ checksum_valid = !udp_lib_checksum_complete(skb);
+ if (!checksum_valid)
goto csum_copy_err;
}
- if (skb_csum_unnecessary(skb))
+ if (checksum_valid || skb_csum_unnecessary(skb))
err = skb_copy_datagram_msg(skb, sizeof(struct udphdr),
msg, copied);
else {
@@ -547,8 +583,8 @@ void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
int err;
struct net *net = dev_net(skb->dev);
- sk = __udp6_lib_lookup(net, daddr, uh->dest,
- saddr, uh->source, inet6_iif(skb), udptable);
+ sk = __udp6_lib_lookup(net, daddr, uh->dest, saddr, uh->source,
+ inet6_iif(skb), udptable, skb);
if (!sk) {
ICMP6_INC_STATS_BH(net, __in6_dev_get(skb->dev),
ICMP6_MIB_INERRORS);
@@ -1110,6 +1146,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name);
struct in6_addr *daddr, *final_p, final;
struct ipv6_txoptions *opt = NULL;
+ struct ipv6_txoptions *opt_to_free = NULL;
struct ip6_flowlabel *flowlabel = NULL;
struct flowi6 fl6;
struct dst_entry *dst;
@@ -1263,8 +1300,10 @@ do_udp_sendmsg:
opt = NULL;
connected = 0;
}
- if (!opt)
- opt = np->opt;
+ if (!opt) {
+ opt = txopt_get(np);
+ opt_to_free = opt;
+ }
if (flowlabel)
opt = fl6_merge_options(&opt_space, flowlabel, opt);
opt = ipv6_fixup_options(&opt_space, opt);
@@ -1373,6 +1412,7 @@ release_dst:
out:
dst_release(dst);
fl6_sock_release(flowlabel);
+ txopt_put(opt_to_free);
if (!err)
return len;
/*
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index 5643423fe67a..c074771a10f7 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -279,7 +279,7 @@ static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
xfrm_dst_ifdown(dst, dev);
}
-static struct dst_ops xfrm6_dst_ops = {
+static struct dst_ops xfrm6_dst_ops_template = {
.family = AF_INET6,
.gc = xfrm6_garbage_collect,
.update_pmtu = xfrm6_update_pmtu,
@@ -293,7 +293,7 @@ static struct dst_ops xfrm6_dst_ops = {
static struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
.family = AF_INET6,
- .dst_ops = &xfrm6_dst_ops,
+ .dst_ops = &xfrm6_dst_ops_template,
.dst_lookup = xfrm6_dst_lookup,
.get_saddr = xfrm6_get_saddr,
.decode_session = _decode_session6,
@@ -325,7 +325,7 @@ static struct ctl_table xfrm6_policy_table[] = {
{ }
};
-static int __net_init xfrm6_net_init(struct net *net)
+static int __net_init xfrm6_net_sysctl_init(struct net *net)
{
struct ctl_table *table;
struct ctl_table_header *hdr;
@@ -353,7 +353,7 @@ err_alloc:
return -ENOMEM;
}
-static void __net_exit xfrm6_net_exit(struct net *net)
+static void __net_exit xfrm6_net_sysctl_exit(struct net *net)
{
struct ctl_table *table;
@@ -365,24 +365,52 @@ static void __net_exit xfrm6_net_exit(struct net *net)
if (!net_eq(net, &init_net))
kfree(table);
}
+#else /* CONFIG_SYSCTL */
+static int inline xfrm6_net_sysctl_init(struct net *net)
+{
+ return 0;
+}
+
+static void inline xfrm6_net_sysctl_exit(struct net *net)
+{
+}
+#endif
+
+static int __net_init xfrm6_net_init(struct net *net)
+{
+ int ret;
+
+ memcpy(&net->xfrm.xfrm6_dst_ops, &xfrm6_dst_ops_template,
+ sizeof(xfrm6_dst_ops_template));
+ ret = dst_entries_init(&net->xfrm.xfrm6_dst_ops);
+ if (ret)
+ return ret;
+
+ ret = xfrm6_net_sysctl_init(net);
+ if (ret)
+ dst_entries_destroy(&net->xfrm.xfrm6_dst_ops);
+
+ return ret;
+}
+
+static void __net_exit xfrm6_net_exit(struct net *net)
+{
+ xfrm6_net_sysctl_exit(net);
+ dst_entries_destroy(&net->xfrm.xfrm6_dst_ops);
+}
static struct pernet_operations xfrm6_net_ops = {
.init = xfrm6_net_init,
.exit = xfrm6_net_exit,
};
-#endif
int __init xfrm6_init(void)
{
int ret;
- dst_entries_init(&xfrm6_dst_ops);
-
ret = xfrm6_policy_init();
- if (ret) {
- dst_entries_destroy(&xfrm6_dst_ops);
+ if (ret)
goto out;
- }
ret = xfrm6_state_init();
if (ret)
goto out_policy;
@@ -391,9 +419,7 @@ int __init xfrm6_init(void)
if (ret)
goto out_state;
-#ifdef CONFIG_SYSCTL
register_pernet_subsys(&xfrm6_net_ops);
-#endif
out:
return ret;
out_state:
@@ -405,11 +431,8 @@ out_policy:
void xfrm6_fini(void)
{
-#ifdef CONFIG_SYSCTL
unregister_pernet_subsys(&xfrm6_net_ops);
-#endif
xfrm6_protocol_fini();
xfrm6_policy_fini();
xfrm6_state_fini();
- dst_entries_destroy(&xfrm6_dst_ops);
}
diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c
index e6aa48b5395c..923abd6b3064 100644
--- a/net/irda/af_irda.c
+++ b/net/irda/af_irda.c
@@ -1086,6 +1086,9 @@ static int irda_create(struct net *net, struct socket *sock, int protocol,
struct sock *sk;
struct irda_sock *self;
+ if (protocol < 0 || protocol > SK_PROTOCOL_MAX)
+ return -EINVAL;
+
if (net != &init_net)
return -EAFNOSUPPORT;
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index fcb2752419c6..ef50a94d3eb7 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -303,7 +303,7 @@ static void iucv_sock_wake_msglim(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_all(&wq->wait);
sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
rcu_read_unlock();
@@ -1031,7 +1031,7 @@ static int iucv_sock_sendmsg(struct socket *sock, struct msghdr *msg,
struct sock *sk = sock->sk;
struct iucv_sock *iucv = iucv_sk(sk);
struct sk_buff *skb;
- struct iucv_message txmsg;
+ struct iucv_message txmsg = {0};
struct cmsghdr *cmsg;
int cmsg_done;
long timeo;
@@ -1483,7 +1483,7 @@ unsigned int iucv_sock_poll(struct file *file, struct socket *sock,
if (sock_writeable(sk) && iucv_below_msglim(sk))
mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
else
- set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+ sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
return mask;
}
@@ -2084,11 +2084,7 @@ static int afiucv_hs_callback_rx(struct sock *sk, struct sk_buff *skb)
return NET_RX_SUCCESS;
}
- /* write stuff from iucv_msg to skb cb */
- if (skb->len < sizeof(struct af_iucv_trans_hdr)) {
- kfree_skb(skb);
- return NET_RX_SUCCESS;
- }
+ /* write stuff from iucv_msg to skb cb */
skb_pull(skb, sizeof(struct af_iucv_trans_hdr));
skb_reset_transport_header(skb);
skb_reset_network_header(skb);
@@ -2119,6 +2115,20 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
char nullstring[8];
int err = 0;
+ if (skb->len < (ETH_HLEN + sizeof(struct af_iucv_trans_hdr))) {
+ WARN_ONCE(1, "AF_IUCV too short skb, len=%d, min=%d",
+ (int)skb->len,
+ (int)(ETH_HLEN + sizeof(struct af_iucv_trans_hdr)));
+ kfree_skb(skb);
+ return NET_RX_SUCCESS;
+ }
+ if (skb_headlen(skb) < (ETH_HLEN + sizeof(struct af_iucv_trans_hdr)))
+ if (skb_linearize(skb)) {
+ WARN_ONCE(1, "AF_IUCV skb_linearize failed, len=%d",
+ (int)skb->len);
+ kfree_skb(skb);
+ return NET_RX_SUCCESS;
+ }
skb_pull(skb, ETH_HLEN);
trans_hdr = (struct af_iucv_trans_hdr *)skb->data;
EBCASC(trans_hdr->destAppName, sizeof(trans_hdr->destAppName));
diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c
index aca38d8aed8e..a2c8747d2936 100644
--- a/net/l2tp/l2tp_ip6.c
+++ b/net/l2tp/l2tp_ip6.c
@@ -486,6 +486,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
DECLARE_SOCKADDR(struct sockaddr_l2tpip6 *, lsa, msg->msg_name);
struct in6_addr *daddr, *final_p, final;
struct ipv6_pinfo *np = inet6_sk(sk);
+ struct ipv6_txoptions *opt_to_free = NULL;
struct ipv6_txoptions *opt = NULL;
struct ip6_flowlabel *flowlabel = NULL;
struct dst_entry *dst = NULL;
@@ -575,8 +576,10 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
opt = NULL;
}
- if (opt == NULL)
- opt = np->opt;
+ if (!opt) {
+ opt = txopt_get(np);
+ opt_to_free = opt;
+ }
if (flowlabel)
opt = fl6_merge_options(&opt_space, flowlabel, opt);
opt = ipv6_fixup_options(&opt_space, opt);
@@ -631,6 +634,7 @@ done:
dst_release(dst);
out:
fl6_sock_release(flowlabel);
+ txopt_put(opt_to_free);
return err < 0 ? err : len;
diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index 1ad18c55064c..652c250b9a3b 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -230,26 +230,11 @@ static void pppol2tp_recv(struct l2tp_session *session, struct sk_buff *skb, int
if (sk->sk_state & PPPOX_BOUND) {
struct pppox_sock *po;
+
l2tp_dbg(session, PPPOL2TP_MSG_DATA,
"%s: recv %d byte data frame, passing to ppp\n",
session->name, data_len);
- /* We need to forget all info related to the L2TP packet
- * gathered in the skb as we are going to reuse the same
- * skb for the inner packet.
- * Namely we need to:
- * - reset xfrm (IPSec) information as it applies to
- * the outer L2TP packet and not to the inner one
- * - release the dst to force a route lookup on the inner
- * IP packet since skb->dst currently points to the dst
- * of the UDP tunnel
- * - reset netfilter information as it doesn't apply
- * to the inner packet either
- */
- secpath_reset(skb);
- skb_dst_drop(skb);
- nf_reset(skb);
-
po = pppox_sk(sk);
ppp_input(&po->chan, skb);
} else {
@@ -1862,5 +1847,5 @@ MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
MODULE_DESCRIPTION("PPP over L2TP over UDP");
MODULE_LICENSE("GPL");
MODULE_VERSION(PPPOL2TP_DRV_VERSION);
-MODULE_ALIAS("pppox-proto-" __stringify(PX_PROTO_OL2TP));
+MODULE_ALIAS_NET_PF_PROTO(PF_PPPOX, PX_PROTO_OL2TP);
MODULE_ALIAS_L2TP_PWTYPE(11);
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index a758eb84e8f0..ff757181b0a8 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -500,7 +500,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
/* send AddBA request */
ieee80211_send_addba_request(sdata, sta->sta.addr, tid,
tid_tx->dialog_token, start_seq_num,
- local->hw.max_tx_aggregation_subframes,
+ IEEE80211_MAX_AMPDU_BUF,
tid_tx->timeout);
}
@@ -926,6 +926,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
amsdu = capab & IEEE80211_ADDBA_PARAM_AMSDU_MASK;
tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
+ buf_size = min(buf_size, local->hw.max_tx_aggregation_subframes);
mutex_lock(&sta->ampdu_mlme.mtx);
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index c2bd1b6a6922..166a29fe6c35 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1169,8 +1169,7 @@ static int sta_apply_parameters(struct ieee80211_local *local,
* rc isn't initialized here yet, so ignore it
*/
__ieee80211_vht_handle_opmode(sdata, sta,
- params->opmode_notif,
- band, false);
+ params->opmode_notif, band);
}
if (ieee80211_vif_is_mesh(&sdata->vif))
@@ -1216,16 +1215,6 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
if (!sta)
return -ENOMEM;
- /*
- * defaults -- if userspace wants something else we'll
- * change it accordingly in sta_apply_parameters()
- */
- if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
- !(params->sta_flags_set & (BIT(NL80211_STA_FLAG_AUTHENTICATED) |
- BIT(NL80211_STA_FLAG_ASSOCIATED)))) {
- sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
- sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
- }
if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
sta->sta.tdls = true;
@@ -1994,6 +1983,11 @@ static int ieee80211_scan(struct wiphy *wiphy,
return ieee80211_request_scan(sdata, req);
}
+static void ieee80211_abort_scan(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ ieee80211_scan_cancel(wiphy_priv(wiphy));
+}
+
static int
ieee80211_sched_scan_start(struct wiphy *wiphy,
struct net_device *dev,
@@ -2509,294 +2503,6 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
return 0;
}
-static bool ieee80211_coalesce_started_roc(struct ieee80211_local *local,
- struct ieee80211_roc_work *new_roc,
- struct ieee80211_roc_work *cur_roc)
-{
- unsigned long now = jiffies;
- unsigned long remaining = cur_roc->hw_start_time +
- msecs_to_jiffies(cur_roc->duration) -
- now;
-
- if (WARN_ON(!cur_roc->started || !cur_roc->hw_begun))
- return false;
-
- /* if it doesn't fit entirely, schedule a new one */
- if (new_roc->duration > jiffies_to_msecs(remaining))
- return false;
-
- ieee80211_handle_roc_started(new_roc);
-
- /* add to dependents so we send the expired event properly */
- list_add_tail(&new_roc->list, &cur_roc->dependents);
- return true;
-}
-
-static u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local)
-{
- lockdep_assert_held(&local->mtx);
-
- local->roc_cookie_counter++;
-
- /* wow, you wrapped 64 bits ... more likely a bug */
- if (WARN_ON(local->roc_cookie_counter == 0))
- local->roc_cookie_counter++;
-
- return local->roc_cookie_counter;
-}
-
-static int ieee80211_start_roc_work(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- struct ieee80211_channel *channel,
- unsigned int duration, u64 *cookie,
- struct sk_buff *txskb,
- enum ieee80211_roc_type type)
-{
- struct ieee80211_roc_work *roc, *tmp;
- bool queued = false;
- int ret;
-
- lockdep_assert_held(&local->mtx);
-
- if (local->use_chanctx && !local->ops->remain_on_channel)
- return -EOPNOTSUPP;
-
- roc = kzalloc(sizeof(*roc), GFP_KERNEL);
- if (!roc)
- return -ENOMEM;
-
- /*
- * If the duration is zero, then the driver
- * wouldn't actually do anything. Set it to
- * 10 for now.
- *
- * TODO: cancel the off-channel operation
- * when we get the SKB's TX status and
- * the wait time was zero before.
- */
- if (!duration)
- duration = 10;
-
- roc->chan = channel;
- roc->duration = duration;
- roc->req_duration = duration;
- roc->frame = txskb;
- roc->type = type;
- roc->sdata = sdata;
- INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work);
- INIT_LIST_HEAD(&roc->dependents);
-
- /*
- * cookie is either the roc cookie (for normal roc)
- * or the SKB (for mgmt TX)
- */
- if (!txskb) {
- roc->cookie = ieee80211_mgmt_tx_cookie(local);
- *cookie = roc->cookie;
- } else {
- roc->mgmt_tx_cookie = *cookie;
- }
-
- /* if there's one pending or we're scanning, queue this one */
- if (!list_empty(&local->roc_list) ||
- local->scanning || ieee80211_is_radar_required(local))
- goto out_check_combine;
-
- /* if not HW assist, just queue & schedule work */
- if (!local->ops->remain_on_channel) {
- ieee80211_queue_delayed_work(&local->hw, &roc->work, 0);
- goto out_queue;
- }
-
- /* otherwise actually kick it off here (for error handling) */
-
- ret = drv_remain_on_channel(local, sdata, channel, duration, type);
- if (ret) {
- kfree(roc);
- return ret;
- }
-
- roc->started = true;
- goto out_queue;
-
- out_check_combine:
- list_for_each_entry(tmp, &local->roc_list, list) {
- if (tmp->chan != channel || tmp->sdata != sdata)
- continue;
-
- /*
- * Extend this ROC if possible:
- *
- * If it hasn't started yet, just increase the duration
- * and add the new one to the list of dependents.
- * If the type of the new ROC has higher priority, modify the
- * type of the previous one to match that of the new one.
- */
- if (!tmp->started) {
- list_add_tail(&roc->list, &tmp->dependents);
- tmp->duration = max(tmp->duration, roc->duration);
- tmp->type = max(tmp->type, roc->type);
- queued = true;
- break;
- }
-
- /* If it has already started, it's more difficult ... */
- if (local->ops->remain_on_channel) {
- /*
- * In the offloaded ROC case, if it hasn't begun, add
- * this new one to the dependent list to be handled
- * when the master one begins. If it has begun,
- * check if it fits entirely within the existing one,
- * in which case it will just be dependent as well.
- * Otherwise, schedule it by itself.
- */
- if (!tmp->hw_begun) {
- list_add_tail(&roc->list, &tmp->dependents);
- queued = true;
- break;
- }
-
- if (ieee80211_coalesce_started_roc(local, roc, tmp))
- queued = true;
- } else if (del_timer_sync(&tmp->work.timer)) {
- unsigned long new_end;
-
- /*
- * In the software ROC case, cancel the timer, if
- * that fails then the finish work is already
- * queued/pending and thus we queue the new ROC
- * normally, if that succeeds then we can extend
- * the timer duration and TX the frame (if any.)
- */
-
- list_add_tail(&roc->list, &tmp->dependents);
- queued = true;
-
- new_end = jiffies + msecs_to_jiffies(roc->duration);
-
- /* ok, it was started & we canceled timer */
- if (time_after(new_end, tmp->work.timer.expires))
- mod_timer(&tmp->work.timer, new_end);
- else
- add_timer(&tmp->work.timer);
-
- ieee80211_handle_roc_started(roc);
- }
- break;
- }
-
- out_queue:
- if (!queued)
- list_add_tail(&roc->list, &local->roc_list);
-
- return 0;
-}
-
-static int ieee80211_remain_on_channel(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- struct ieee80211_channel *chan,
- unsigned int duration,
- u64 *cookie)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
- struct ieee80211_local *local = sdata->local;
- int ret;
-
- mutex_lock(&local->mtx);
- ret = ieee80211_start_roc_work(local, sdata, chan,
- duration, cookie, NULL,
- IEEE80211_ROC_TYPE_NORMAL);
- mutex_unlock(&local->mtx);
-
- return ret;
-}
-
-static int ieee80211_cancel_roc(struct ieee80211_local *local,
- u64 cookie, bool mgmt_tx)
-{
- struct ieee80211_roc_work *roc, *tmp, *found = NULL;
- int ret;
-
- mutex_lock(&local->mtx);
- list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
- struct ieee80211_roc_work *dep, *tmp2;
-
- list_for_each_entry_safe(dep, tmp2, &roc->dependents, list) {
- if (!mgmt_tx && dep->cookie != cookie)
- continue;
- else if (mgmt_tx && dep->mgmt_tx_cookie != cookie)
- continue;
- /* found dependent item -- just remove it */
- list_del(&dep->list);
- mutex_unlock(&local->mtx);
-
- ieee80211_roc_notify_destroy(dep, true);
- return 0;
- }
-
- if (!mgmt_tx && roc->cookie != cookie)
- continue;
- else if (mgmt_tx && roc->mgmt_tx_cookie != cookie)
- continue;
-
- found = roc;
- break;
- }
-
- if (!found) {
- mutex_unlock(&local->mtx);
- return -ENOENT;
- }
-
- /*
- * We found the item to cancel, so do that. Note that it
- * may have dependents, which we also cancel (and send
- * the expired signal for.) Not doing so would be quite
- * tricky here, but we may need to fix it later.
- */
-
- if (local->ops->remain_on_channel) {
- if (found->started) {
- ret = drv_cancel_remain_on_channel(local);
- if (WARN_ON_ONCE(ret)) {
- mutex_unlock(&local->mtx);
- return ret;
- }
- }
-
- list_del(&found->list);
-
- if (found->started)
- ieee80211_start_next_roc(local);
- mutex_unlock(&local->mtx);
-
- ieee80211_roc_notify_destroy(found, true);
- } else {
- /* work may be pending so use it all the time */
- found->abort = true;
- ieee80211_queue_delayed_work(&local->hw, &found->work, 0);
-
- mutex_unlock(&local->mtx);
-
- /* work will clean up etc */
- flush_delayed_work(&found->work);
- WARN_ON(!found->to_be_freed);
- kfree(found);
- }
-
- return 0;
-}
-
-static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- u64 cookie)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
- struct ieee80211_local *local = sdata->local;
-
- return ieee80211_cancel_roc(local, cookie, false);
-}
-
static int ieee80211_start_radar_detection(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_chan_def *chandef,
@@ -3267,9 +2973,21 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
return err;
}
-static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local,
- struct sk_buff *skb, u64 *cookie,
- gfp_t gfp)
+u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local)
+{
+ lockdep_assert_held(&local->mtx);
+
+ local->roc_cookie_counter++;
+
+ /* wow, you wrapped 64 bits ... more likely a bug */
+ if (WARN_ON(local->roc_cookie_counter == 0))
+ local->roc_cookie_counter++;
+
+ return local->roc_cookie_counter;
+}
+
+int ieee80211_attach_ack_skb(struct ieee80211_local *local, struct sk_buff *skb,
+ u64 *cookie, gfp_t gfp)
{
unsigned long spin_flags;
struct sk_buff *ack_skb;
@@ -3277,7 +2995,7 @@ static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local,
ack_skb = skb_copy(skb, gfp);
if (!ack_skb)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
spin_lock_irqsave(&local->ack_status_lock, spin_flags);
id = idr_alloc(&local->ack_status_frames, ack_skb,
@@ -3286,7 +3004,7 @@ static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local,
if (id < 0) {
kfree_skb(ack_skb);
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
}
IEEE80211_SKB_CB(skb)->ack_frame_id = id;
@@ -3294,200 +3012,7 @@ static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local,
*cookie = ieee80211_mgmt_tx_cookie(local);
IEEE80211_SKB_CB(ack_skb)->ack.cookie = *cookie;
- return ack_skb;
-}
-
-static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
- struct cfg80211_mgmt_tx_params *params,
- u64 *cookie)
-{
- struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
- struct ieee80211_local *local = sdata->local;
- struct sk_buff *skb, *ack_skb;
- struct sta_info *sta;
- const struct ieee80211_mgmt *mgmt = (void *)params->buf;
- bool need_offchan = false;
- u32 flags;
- int ret;
- u8 *data;
-
- if (params->dont_wait_for_ack)
- flags = IEEE80211_TX_CTL_NO_ACK;
- else
- flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX |
- IEEE80211_TX_CTL_REQ_TX_STATUS;
-
- if (params->no_cck)
- flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
-
- switch (sdata->vif.type) {
- case NL80211_IFTYPE_ADHOC:
- if (!sdata->vif.bss_conf.ibss_joined)
- need_offchan = true;
- /* fall through */
-#ifdef CONFIG_MAC80211_MESH
- case NL80211_IFTYPE_MESH_POINT:
- if (ieee80211_vif_is_mesh(&sdata->vif) &&
- !sdata->u.mesh.mesh_id_len)
- need_offchan = true;
- /* fall through */
-#endif
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_AP_VLAN:
- case NL80211_IFTYPE_P2P_GO:
- if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
- !ieee80211_vif_is_mesh(&sdata->vif) &&
- !rcu_access_pointer(sdata->bss->beacon))
- need_offchan = true;
- if (!ieee80211_is_action(mgmt->frame_control) ||
- mgmt->u.action.category == WLAN_CATEGORY_PUBLIC ||
- mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED ||
- mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT)
- break;
- rcu_read_lock();
- sta = sta_info_get(sdata, mgmt->da);
- rcu_read_unlock();
- if (!sta)
- return -ENOLINK;
- break;
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_P2P_CLIENT:
- sdata_lock(sdata);
- if (!sdata->u.mgd.associated ||
- (params->offchan && params->wait &&
- local->ops->remain_on_channel &&
- memcmp(sdata->u.mgd.associated->bssid,
- mgmt->bssid, ETH_ALEN)))
- need_offchan = true;
- sdata_unlock(sdata);
- break;
- case NL80211_IFTYPE_P2P_DEVICE:
- need_offchan = true;
- break;
- default:
- return -EOPNOTSUPP;
- }
-
- /* configurations requiring offchan cannot work if no channel has been
- * specified
- */
- if (need_offchan && !params->chan)
- return -EINVAL;
-
- mutex_lock(&local->mtx);
-
- /* Check if the operating channel is the requested channel */
- if (!need_offchan) {
- struct ieee80211_chanctx_conf *chanctx_conf;
-
- rcu_read_lock();
- chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
-
- if (chanctx_conf) {
- need_offchan = params->chan &&
- (params->chan !=
- chanctx_conf->def.chan);
- } else if (!params->chan) {
- ret = -EINVAL;
- rcu_read_unlock();
- goto out_unlock;
- } else {
- need_offchan = true;
- }
- rcu_read_unlock();
- }
-
- if (need_offchan && !params->offchan) {
- ret = -EBUSY;
- goto out_unlock;
- }
-
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + params->len);
- if (!skb) {
- ret = -ENOMEM;
- goto out_unlock;
- }
- skb_reserve(skb, local->hw.extra_tx_headroom);
-
- data = skb_put(skb, params->len);
- memcpy(data, params->buf, params->len);
-
- /* Update CSA counters */
- if (sdata->vif.csa_active &&
- (sdata->vif.type == NL80211_IFTYPE_AP ||
- sdata->vif.type == NL80211_IFTYPE_MESH_POINT ||
- sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
- params->n_csa_offsets) {
- int i;
- struct beacon_data *beacon = NULL;
-
- rcu_read_lock();
-
- if (sdata->vif.type == NL80211_IFTYPE_AP)
- beacon = rcu_dereference(sdata->u.ap.beacon);
- else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
- beacon = rcu_dereference(sdata->u.ibss.presp);
- else if (ieee80211_vif_is_mesh(&sdata->vif))
- beacon = rcu_dereference(sdata->u.mesh.beacon);
-
- if (beacon)
- for (i = 0; i < params->n_csa_offsets; i++)
- data[params->csa_offsets[i]] =
- beacon->csa_current_counter;
-
- rcu_read_unlock();
- }
-
- IEEE80211_SKB_CB(skb)->flags = flags;
-
- skb->dev = sdata->dev;
-
- if (!params->dont_wait_for_ack) {
- /* make a copy to preserve the frame contents
- * in case of encryption.
- */
- ack_skb = ieee80211_make_ack_skb(local, skb, cookie,
- GFP_KERNEL);
- if (IS_ERR(ack_skb)) {
- ret = PTR_ERR(ack_skb);
- kfree_skb(skb);
- goto out_unlock;
- }
- } else {
- /* for cookie below */
- ack_skb = skb;
- }
-
- if (!need_offchan) {
- ieee80211_tx_skb(sdata, skb);
- ret = 0;
- goto out_unlock;
- }
-
- IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN |
- IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
- if (ieee80211_hw_check(&local->hw, QUEUE_CONTROL))
- IEEE80211_SKB_CB(skb)->hw_queue =
- local->hw.offchannel_tx_hw_queue;
-
- /* This will handle all kinds of coalescing and immediate TX */
- ret = ieee80211_start_roc_work(local, sdata, params->chan,
- params->wait, cookie, skb,
- IEEE80211_ROC_TYPE_MGMT_TX);
- if (ret)
- kfree_skb(skb);
- out_unlock:
- mutex_unlock(&local->mtx);
- return ret;
-}
-
-static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- u64 cookie)
-{
- struct ieee80211_local *local = wiphy_priv(wiphy);
-
- return ieee80211_cancel_roc(local, cookie, true);
+ return 0;
}
static void ieee80211_mgmt_frame_register(struct wiphy *wiphy,
@@ -3565,7 +3090,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
struct ieee80211_qos_hdr *nullfunc;
- struct sk_buff *skb, *ack_skb;
+ struct sk_buff *skb;
int size = sizeof(*nullfunc);
__le16 fc;
bool qos;
@@ -3633,10 +3158,9 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
if (qos)
nullfunc->qos_ctrl = cpu_to_le16(7);
- ack_skb = ieee80211_make_ack_skb(local, skb, cookie, GFP_ATOMIC);
- if (IS_ERR(ack_skb)) {
+ ret = ieee80211_attach_ack_skb(local, skb, cookie, GFP_ATOMIC);
+ if (ret) {
kfree_skb(skb);
- ret = PTR_ERR(ack_skb);
goto unlock;
}
@@ -3838,6 +3362,7 @@ const struct cfg80211_ops mac80211_config_ops = {
.suspend = ieee80211_suspend,
.resume = ieee80211_resume,
.scan = ieee80211_scan,
+ .abort_scan = ieee80211_abort_scan,
.sched_scan_start = ieee80211_sched_scan_start,
.sched_scan_stop = ieee80211_sched_scan_stop,
.auth = ieee80211_auth,
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 4d2aaebd4f97..abbdff03ce92 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -125,6 +125,7 @@ static const char *hw_flag_names[NUM_IEEE80211_HW_FLAGS + 1] = {
FLAG(TDLS_WIDER_BW),
FLAG(SUPPORTS_AMSDU_IN_AMPDU),
FLAG(BEACON_TX_STATUS),
+ FLAG(NEEDS_UNIQUE_STA_ADDR),
/* keep last for the build bug below */
(void *)0x1
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 337bb5d78003..f7fc0e00497f 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -428,6 +428,7 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
chandef.width = sdata->u.ibss.chandef.width;
break;
case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_80P80:
case NL80211_CHAN_WIDTH_160:
chandef = sdata->u.ibss.chandef;
chandef.chan = cbss->channel;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index d832bd59236b..b84f6aa32c08 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -325,19 +325,15 @@ struct mesh_preq_queue {
struct ieee80211_roc_work {
struct list_head list;
- struct list_head dependents;
-
- struct delayed_work work;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *chan;
bool started, abort, hw_begun, notified;
- bool to_be_freed;
bool on_channel;
- unsigned long hw_start_time;
+ unsigned long start_time;
u32 duration, req_duration;
struct sk_buff *frame;
@@ -1335,6 +1331,7 @@ struct ieee80211_local {
/*
* Remain-on-channel support
*/
+ struct delayed_work roc_work;
struct list_head roc_list;
struct work_struct hw_roc_start, hw_roc_done;
unsigned long hw_roc_start_time;
@@ -1483,6 +1480,10 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
void ieee80211_configure_filter(struct ieee80211_local *local);
u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata);
+u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local);
+int ieee80211_attach_ack_skb(struct ieee80211_local *local, struct sk_buff *skb,
+ u64 *cookie, gfp_t gfp);
+
/* STA code */
void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata);
int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
@@ -1577,16 +1578,22 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_local *local);
void ieee80211_sched_scan_end(struct ieee80211_local *local);
void ieee80211_sched_scan_stopped_work(struct work_struct *work);
-/* off-channel helpers */
+/* off-channel/mgmt-tx */
void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local);
void ieee80211_offchannel_return(struct ieee80211_local *local);
void ieee80211_roc_setup(struct ieee80211_local *local);
void ieee80211_start_next_roc(struct ieee80211_local *local);
void ieee80211_roc_purge(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata);
-void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free);
-void ieee80211_sw_roc_work(struct work_struct *work);
-void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
+int ieee80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct ieee80211_channel *chan,
+ unsigned int duration, u64 *cookie);
+int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev, u64 cookie);
+int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params, u64 *cookie);
+int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
+ struct wireless_dev *wdev, u64 cookie);
/* channel switch handling */
void ieee80211_csa_finalize_work(struct work_struct *work);
@@ -1709,10 +1716,10 @@ enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta);
void ieee80211_sta_set_rx_nss(struct sta_info *sta);
u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, u8 opmode,
- enum ieee80211_band band, bool nss_only);
+ enum ieee80211_band band);
void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, u8 opmode,
- enum ieee80211_band band, bool nss_only);
+ enum ieee80211_band band);
void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta_vht_cap *vht_cap);
void ieee80211_get_vht_mask_from_cap(__le16 vht_cap,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index d0dc1bfaeec2..c9e325d2e120 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -76,7 +76,8 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
bool update_bss)
{
- if (__ieee80211_recalc_txpower(sdata) || update_bss)
+ if (__ieee80211_recalc_txpower(sdata) ||
+ (update_bss && ieee80211_sdata_running(sdata)))
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_TXPOWER);
}
@@ -1861,6 +1862,7 @@ void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata)
unregister_netdevice(sdata->dev);
} else {
cfg80211_unregister_wdev(&sdata->wdev);
+ ieee80211_teardown_sdata(sdata);
kfree(sdata);
}
}
@@ -1870,7 +1872,6 @@ void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata)
if (WARN_ON_ONCE(!test_bit(SDATA_STATE_RUNNING, &sdata->state)))
return;
ieee80211_do_stop(sdata, true);
- ieee80211_teardown_sdata(sdata);
}
void ieee80211_remove_interfaces(struct ieee80211_local *local)
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 44388d6a1d8e..5e5bc599da4c 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -4,6 +4,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007-2008 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
+ * Copyright 2015 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -320,7 +321,7 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
return;
if (new)
- list_add_tail(&new->list, &sdata->key_list);
+ list_add_tail_rcu(&new->list, &sdata->key_list);
WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx);
@@ -368,7 +369,7 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
}
if (old)
- list_del(&old->list);
+ list_del_rcu(&old->list);
}
struct ieee80211_key *
@@ -592,8 +593,8 @@ static void ieee80211_key_destroy(struct ieee80211_key *key,
return;
/*
- * Synchronize so the TX path can no longer be using
- * this key before we free/remove it.
+ * Synchronize so the TX path and rcu key iterators
+ * can no longer be using this key before we free/remove it.
*/
synchronize_net();
@@ -744,6 +745,53 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_iter_keys);
+static void
+_ieee80211_iter_keys_rcu(struct ieee80211_hw *hw,
+ struct ieee80211_sub_if_data *sdata,
+ void (*iter)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *data),
+ void *iter_data)
+{
+ struct ieee80211_key *key;
+
+ list_for_each_entry_rcu(key, &sdata->key_list, list) {
+ /* skip keys of station in removal process */
+ if (key->sta && key->sta->removed)
+ continue;
+ if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
+ continue;
+
+ iter(hw, &sdata->vif,
+ key->sta ? &key->sta->sta : NULL,
+ &key->conf, iter_data);
+ }
+}
+
+void ieee80211_iter_keys_rcu(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ void (*iter)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *data),
+ void *iter_data)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_sub_if_data *sdata;
+
+ if (vif) {
+ sdata = vif_to_sdata(vif);
+ _ieee80211_iter_keys_rcu(hw, sdata, iter, iter_data);
+ } else {
+ list_for_each_entry_rcu(sdata, &local->interfaces, list)
+ _ieee80211_iter_keys_rcu(hw, sdata, iter, iter_data);
+ }
+}
+EXPORT_SYMBOL(ieee80211_iter_keys_rcu);
+
static void ieee80211_free_keys_iface(struct ieee80211_sub_if_data *sdata,
struct list_head *keys)
{
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 858f6b1cb149..6bcf0faa4a89 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -1149,6 +1149,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
rtnl_unlock();
+ cancel_delayed_work_sync(&local->roc_work);
cancel_work_sync(&local->restart_work);
cancel_work_sync(&local->reconfig_filter);
cancel_work_sync(&local->tdls_chsw_work);
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
index b890e225a8f1..dadf8dc6f1cf 100644
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -779,10 +779,8 @@ void mesh_plink_broken(struct sta_info *sta)
static void mesh_path_node_reclaim(struct rcu_head *rp)
{
struct mpath_node *node = container_of(rp, struct mpath_node, rcu);
- struct ieee80211_sub_if_data *sdata = node->mpath->sdata;
del_timer_sync(&node->mpath->timer);
- atomic_dec(&sdata->u.mesh.mpaths);
kfree(node->mpath);
kfree(node);
}
@@ -790,8 +788,9 @@ static void mesh_path_node_reclaim(struct rcu_head *rp)
/* needs to be called with the corresponding hashwlock taken */
static void __mesh_path_del(struct mesh_table *tbl, struct mpath_node *node)
{
- struct mesh_path *mpath;
- mpath = node->mpath;
+ struct mesh_path *mpath = node->mpath;
+ struct ieee80211_sub_if_data *sdata = node->mpath->sdata;
+
spin_lock(&mpath->state_lock);
mpath->flags |= MESH_PATH_RESOLVING;
if (mpath->is_gate)
@@ -799,6 +798,7 @@ static void __mesh_path_del(struct mesh_table *tbl, struct mpath_node *node)
hlist_del_rcu(&node->list);
call_rcu(&node->rcu, mesh_path_node_reclaim);
spin_unlock(&mpath->state_lock);
+ atomic_dec(&sdata->u.mesh.mpaths);
atomic_dec(&tbl->entries);
}
@@ -968,8 +968,8 @@ int mesh_path_send_to_gates(struct mesh_path *mpath)
copy = true;
} else {
mpath_dbg(sdata,
- "Not forwarding %p (flags %#x)\n",
- gate->mpath, gate->mpath->flags);
+ "Not forwarding to %pM (flags %#x)\n",
+ gate->mpath->dst, gate->mpath->flags);
}
}
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index b140cc6651f4..1c342e2592c4 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1379,21 +1379,26 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
*/
if (has_80211h_pwr &&
(!has_cisco_pwr || pwr_level_80211h <= pwr_level_cisco)) {
+ new_ap_level = pwr_level_80211h;
+
+ if (sdata->ap_power_level == new_ap_level)
+ return 0;
+
sdata_dbg(sdata,
"Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
pwr_level_80211h, chan_pwr, pwr_reduction_80211h,
sdata->u.mgd.bssid);
- new_ap_level = pwr_level_80211h;
} else { /* has_cisco_pwr is always true here. */
+ new_ap_level = pwr_level_cisco;
+
+ if (sdata->ap_power_level == new_ap_level)
+ return 0;
+
sdata_dbg(sdata,
"Limiting TX power to %d dBm as advertised by %pM\n",
pwr_level_cisco, sdata->u.mgd.bssid);
- new_ap_level = pwr_level_cisco;
}
- if (sdata->ap_power_level == new_ap_level)
- return 0;
-
sdata->ap_power_level = new_ap_level;
if (__ieee80211_recalc_txpower(sdata))
return BSS_CHANGED_TXPOWER;
@@ -1930,7 +1935,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE;
- if (sdata->vif.p2p) {
+ if (sdata->vif.p2p ||
+ sdata->vif.driver_flags & IEEE80211_VIF_GET_NOA_UPDATE) {
const struct cfg80211_bss_ies *ies;
rcu_read_lock();
@@ -3458,7 +3464,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
}
}
- if (sdata->vif.p2p) {
+ if (sdata->vif.p2p ||
+ sdata->vif.driver_flags & IEEE80211_VIF_GET_NOA_UPDATE) {
struct ieee80211_p2p_noa_attr noa = {};
int ret;
@@ -3575,7 +3582,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (sta && elems.opmode_notif)
ieee80211_vht_handle_opmode(sdata, sta, *elems.opmode_notif,
- rx_status->band, true);
+ rx_status->band);
mutex_unlock(&local->sta_mtx);
changed |= ieee80211_handle_pwr_constr(sdata, chan, mgmt,
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index 04401037140e..8b2f4eaac2ba 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -187,11 +187,80 @@ void ieee80211_offchannel_return(struct ieee80211_local *local)
false);
}
-void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc)
+static void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc)
{
- if (roc->notified)
+ /* was never transmitted */
+ if (roc->frame) {
+ cfg80211_mgmt_tx_status(&roc->sdata->wdev, roc->mgmt_tx_cookie,
+ roc->frame->data, roc->frame->len,
+ false, GFP_KERNEL);
+ ieee80211_free_txskb(&roc->sdata->local->hw, roc->frame);
+ }
+
+ if (!roc->mgmt_tx_cookie)
+ cfg80211_remain_on_channel_expired(&roc->sdata->wdev,
+ roc->cookie, roc->chan,
+ GFP_KERNEL);
+
+ list_del(&roc->list);
+ kfree(roc);
+}
+
+static unsigned long ieee80211_end_finished_rocs(struct ieee80211_local *local,
+ unsigned long now)
+{
+ struct ieee80211_roc_work *roc, *tmp;
+ long remaining_dur_min = LONG_MAX;
+
+ lockdep_assert_held(&local->mtx);
+
+ list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
+ long remaining;
+
+ if (!roc->started)
+ break;
+
+ remaining = roc->start_time +
+ msecs_to_jiffies(roc->duration) -
+ now;
+
+ /* In case of HW ROC, it is possible that the HW finished the
+ * ROC session before the actual requested time. In such a case
+ * end the ROC session (disregarding the remaining time).
+ */
+ if (roc->abort || roc->hw_begun || remaining <= 0)
+ ieee80211_roc_notify_destroy(roc);
+ else
+ remaining_dur_min = min(remaining_dur_min, remaining);
+ }
+
+ return remaining_dur_min;
+}
+
+static bool ieee80211_recalc_sw_work(struct ieee80211_local *local,
+ unsigned long now)
+{
+ long dur = ieee80211_end_finished_rocs(local, now);
+
+ if (dur == LONG_MAX)
+ return false;
+
+ mod_delayed_work(local->workqueue, &local->roc_work, dur);
+ return true;
+}
+
+static void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc,
+ unsigned long start_time)
+{
+ struct ieee80211_local *local = roc->sdata->local;
+
+ if (WARN_ON(roc->notified))
return;
+ roc->start_time = start_time;
+ roc->started = true;
+ roc->hw_begun = true;
+
if (roc->mgmt_tx_cookie) {
if (!WARN_ON(!roc->frame)) {
ieee80211_tx_skb_tid_band(roc->sdata, roc->frame, 7,
@@ -205,40 +274,26 @@ void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc)
}
roc->notified = true;
+
+ if (!local->ops->remain_on_channel)
+ ieee80211_recalc_sw_work(local, start_time);
}
static void ieee80211_hw_roc_start(struct work_struct *work)
{
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, hw_roc_start);
- struct ieee80211_roc_work *roc, *dep, *tmp;
+ struct ieee80211_roc_work *roc;
mutex_lock(&local->mtx);
- if (list_empty(&local->roc_list))
- goto out_unlock;
-
- roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
- list);
-
- if (!roc->started)
- goto out_unlock;
-
- roc->hw_begun = true;
- roc->hw_start_time = local->hw_roc_start_time;
+ list_for_each_entry(roc, &local->roc_list, list) {
+ if (!roc->started)
+ break;
- ieee80211_handle_roc_started(roc);
- list_for_each_entry_safe(dep, tmp, &roc->dependents, list) {
- ieee80211_handle_roc_started(dep);
-
- if (dep->duration > roc->duration) {
- u32 dur = dep->duration;
- dep->duration = dur - roc->duration;
- roc->duration = dur;
- list_move(&dep->list, &roc->list);
- }
+ ieee80211_handle_roc_started(roc, local->hw_roc_start_time);
}
- out_unlock:
+
mutex_unlock(&local->mtx);
}
@@ -254,34 +309,40 @@ void ieee80211_ready_on_channel(struct ieee80211_hw *hw)
}
EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel);
-void ieee80211_start_next_roc(struct ieee80211_local *local)
+static void _ieee80211_start_next_roc(struct ieee80211_local *local)
{
- struct ieee80211_roc_work *roc;
+ struct ieee80211_roc_work *roc, *tmp;
+ enum ieee80211_roc_type type;
+ u32 min_dur, max_dur;
lockdep_assert_held(&local->mtx);
- if (list_empty(&local->roc_list)) {
- ieee80211_run_deferred_scan(local);
+ if (WARN_ON(list_empty(&local->roc_list)))
return;
- }
roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
list);
- if (WARN_ON_ONCE(roc->started))
+ if (WARN_ON(roc->started))
return;
- if (local->ops->remain_on_channel) {
- int ret, duration = roc->duration;
-
- /* XXX: duplicated, see ieee80211_start_roc_work() */
- if (!duration)
- duration = 10;
+ min_dur = roc->duration;
+ max_dur = roc->duration;
+ type = roc->type;
- ret = drv_remain_on_channel(local, roc->sdata, roc->chan,
- duration, roc->type);
+ list_for_each_entry(tmp, &local->roc_list, list) {
+ if (tmp == roc)
+ continue;
+ if (tmp->sdata != roc->sdata || tmp->chan != roc->chan)
+ break;
+ max_dur = max(tmp->duration, max_dur);
+ min_dur = min(tmp->duration, min_dur);
+ type = max(tmp->type, type);
+ }
- roc->started = true;
+ if (local->ops->remain_on_channel) {
+ int ret = drv_remain_on_channel(local, roc->sdata, roc->chan,
+ max_dur, type);
if (ret) {
wiphy_warn(local->hw.wiphy,
@@ -290,74 +351,24 @@ void ieee80211_start_next_roc(struct ieee80211_local *local)
* queue the work struct again to avoid recursion
* when multiple failures occur
*/
- ieee80211_remain_on_channel_expired(&local->hw);
+ list_for_each_entry(tmp, &local->roc_list, list) {
+ if (tmp->sdata != roc->sdata ||
+ tmp->chan != roc->chan)
+ break;
+ tmp->started = true;
+ tmp->abort = true;
+ }
+ ieee80211_queue_work(&local->hw, &local->hw_roc_done);
+ return;
}
- } else {
- /* delay it a bit */
- ieee80211_queue_delayed_work(&local->hw, &roc->work,
- round_jiffies_relative(HZ/2));
- }
-}
-
-void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free)
-{
- struct ieee80211_roc_work *dep, *tmp;
-
- if (WARN_ON(roc->to_be_freed))
- return;
-
- /* was never transmitted */
- if (roc->frame) {
- cfg80211_mgmt_tx_status(&roc->sdata->wdev,
- (unsigned long)roc->frame,
- roc->frame->data, roc->frame->len,
- false, GFP_KERNEL);
- kfree_skb(roc->frame);
- }
-
- if (!roc->mgmt_tx_cookie)
- cfg80211_remain_on_channel_expired(&roc->sdata->wdev,
- roc->cookie, roc->chan,
- GFP_KERNEL);
-
- list_for_each_entry_safe(dep, tmp, &roc->dependents, list)
- ieee80211_roc_notify_destroy(dep, true);
-
- if (free)
- kfree(roc);
- else
- roc->to_be_freed = true;
-}
-
-void ieee80211_sw_roc_work(struct work_struct *work)
-{
- struct ieee80211_roc_work *roc =
- container_of(work, struct ieee80211_roc_work, work.work);
- struct ieee80211_sub_if_data *sdata = roc->sdata;
- struct ieee80211_local *local = sdata->local;
- bool started, on_channel;
-
- mutex_lock(&local->mtx);
-
- if (roc->to_be_freed)
- goto out_unlock;
-
- if (roc->abort)
- goto finish;
-
- if (WARN_ON(list_empty(&local->roc_list)))
- goto out_unlock;
-
- if (WARN_ON(roc != list_first_entry(&local->roc_list,
- struct ieee80211_roc_work,
- list)))
- goto out_unlock;
-
- if (!roc->started) {
- struct ieee80211_roc_work *dep;
-
- WARN_ON(local->use_chanctx);
+ /* we'll notify about the start once the HW calls back */
+ list_for_each_entry(tmp, &local->roc_list, list) {
+ if (tmp->sdata != roc->sdata || tmp->chan != roc->chan)
+ break;
+ tmp->started = true;
+ }
+ } else {
/* If actually operating on the desired channel (with at least
* 20 MHz channel width) don't stop all the operations but still
* treat it as though the ROC operation started properly, so
@@ -377,27 +388,72 @@ void ieee80211_sw_roc_work(struct work_struct *work)
ieee80211_hw_config(local, 0);
}
- /* tell userspace or send frame */
- ieee80211_handle_roc_started(roc);
- list_for_each_entry(dep, &roc->dependents, list)
- ieee80211_handle_roc_started(dep);
+ ieee80211_queue_delayed_work(&local->hw, &local->roc_work,
+ msecs_to_jiffies(min_dur));
- /* if it was pure TX, just finish right away */
- if (!roc->duration)
- goto finish;
+ /* tell userspace or send frame(s) */
+ list_for_each_entry(tmp, &local->roc_list, list) {
+ if (tmp->sdata != roc->sdata || tmp->chan != roc->chan)
+ break;
- roc->started = true;
- ieee80211_queue_delayed_work(&local->hw, &roc->work,
- msecs_to_jiffies(roc->duration));
+ tmp->on_channel = roc->on_channel;
+ ieee80211_handle_roc_started(tmp, jiffies);
+ }
+ }
+}
+
+void ieee80211_start_next_roc(struct ieee80211_local *local)
+{
+ struct ieee80211_roc_work *roc;
+
+ lockdep_assert_held(&local->mtx);
+
+ if (list_empty(&local->roc_list)) {
+ ieee80211_run_deferred_scan(local);
+ return;
+ }
+
+ roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
+ list);
+
+ if (WARN_ON_ONCE(roc->started))
+ return;
+
+ if (local->ops->remain_on_channel) {
+ _ieee80211_start_next_roc(local);
+ } else {
+ /* delay it a bit */
+ ieee80211_queue_delayed_work(&local->hw, &local->roc_work,
+ round_jiffies_relative(HZ/2));
+ }
+}
+
+static void __ieee80211_roc_work(struct ieee80211_local *local)
+{
+ struct ieee80211_roc_work *roc;
+ bool on_channel;
+
+ lockdep_assert_held(&local->mtx);
+
+ if (WARN_ON(local->ops->remain_on_channel))
+ return;
+
+ roc = list_first_entry_or_null(&local->roc_list,
+ struct ieee80211_roc_work, list);
+ if (!roc)
+ return;
+
+ if (!roc->started) {
+ WARN_ON(local->use_chanctx);
+ _ieee80211_start_next_roc(local);
} else {
- /* finish this ROC */
- finish:
- list_del(&roc->list);
- started = roc->started;
on_channel = roc->on_channel;
- ieee80211_roc_notify_destroy(roc, !roc->abort);
+ if (ieee80211_recalc_sw_work(local, jiffies))
+ return;
+
+ /* careful - roc pointer became invalid during recalc */
- if (started && !on_channel) {
+ if (!on_channel) {
ieee80211_flush_queues(local, NULL, false);
local->tmp_channel = NULL;
@@ -407,14 +463,17 @@ void ieee80211_sw_roc_work(struct work_struct *work)
}
ieee80211_recalc_idle(local);
-
- if (started)
- ieee80211_start_next_roc(local);
- else if (list_empty(&local->roc_list))
- ieee80211_run_deferred_scan(local);
+ ieee80211_start_next_roc(local);
}
+}
- out_unlock:
+static void ieee80211_roc_work(struct work_struct *work)
+{
+ struct ieee80211_local *local =
+ container_of(work, struct ieee80211_local, roc_work.work);
+
+ mutex_lock(&local->mtx);
+ __ieee80211_roc_work(local);
mutex_unlock(&local->mtx);
}
@@ -422,27 +481,14 @@ static void ieee80211_hw_roc_done(struct work_struct *work)
{
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, hw_roc_done);
- struct ieee80211_roc_work *roc;
mutex_lock(&local->mtx);
- if (list_empty(&local->roc_list))
- goto out_unlock;
-
- roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
- list);
-
- if (!roc->started)
- goto out_unlock;
-
- list_del(&roc->list);
-
- ieee80211_roc_notify_destroy(roc, true);
+ ieee80211_end_finished_rocs(local, jiffies);
/* if there's another roc, start it now */
ieee80211_start_next_roc(local);
- out_unlock:
mutex_unlock(&local->mtx);
}
@@ -456,47 +502,500 @@ void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw)
}
EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired);
-void ieee80211_roc_setup(struct ieee80211_local *local)
+static bool
+ieee80211_coalesce_hw_started_roc(struct ieee80211_local *local,
+ struct ieee80211_roc_work *new_roc,
+ struct ieee80211_roc_work *cur_roc)
{
- INIT_WORK(&local->hw_roc_start, ieee80211_hw_roc_start);
- INIT_WORK(&local->hw_roc_done, ieee80211_hw_roc_done);
- INIT_LIST_HEAD(&local->roc_list);
+ unsigned long now = jiffies;
+ unsigned long remaining;
+
+ if (WARN_ON(!cur_roc->started))
+ return false;
+
+ /* if it was scheduled in the hardware, but not started yet,
+ * we can only combine if the older one had a longer duration
+ */
+ if (!cur_roc->hw_begun && new_roc->duration > cur_roc->duration)
+ return false;
+
+ remaining = cur_roc->start_time +
+ msecs_to_jiffies(cur_roc->duration) -
+ now;
+
+ /* if it doesn't fit entirely, schedule a new one */
+ if (new_roc->duration > jiffies_to_msecs(remaining))
+ return false;
+
+ /* add just after the current one so we combine their finish later */
+ list_add(&new_roc->list, &cur_roc->list);
+
+ /* if the existing one has already begun then let this one also
+ * begin, otherwise they'll both be marked properly by the work
+ * struct that runs once the driver notifies us of the beginning
+ */
+ if (cur_roc->hw_begun)
+ ieee80211_handle_roc_started(new_roc, now);
+
+ return true;
}
-void ieee80211_roc_purge(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata)
+static int ieee80211_start_roc_work(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_channel *channel,
+ unsigned int duration, u64 *cookie,
+ struct sk_buff *txskb,
+ enum ieee80211_roc_type type)
{
struct ieee80211_roc_work *roc, *tmp;
- LIST_HEAD(tmp_list);
+ bool queued = false, combine_started = true;
+ int ret;
+
+ lockdep_assert_held(&local->mtx);
+
+ if (local->use_chanctx && !local->ops->remain_on_channel)
+ return -EOPNOTSUPP;
+
+ roc = kzalloc(sizeof(*roc), GFP_KERNEL);
+ if (!roc)
+ return -ENOMEM;
+
+ /*
+ * If the duration is zero, then the driver
+ * wouldn't actually do anything. Set it to
+ * 10 for now.
+ *
+ * TODO: cancel the off-channel operation
+ * when we get the SKB's TX status and
+ * the wait time was zero before.
+ */
+ if (!duration)
+ duration = 10;
+
+ roc->chan = channel;
+ roc->duration = duration;
+ roc->req_duration = duration;
+ roc->frame = txskb;
+ roc->type = type;
+ roc->sdata = sdata;
+
+ /*
+ * cookie is either the roc cookie (for normal roc)
+ * or the SKB (for mgmt TX)
+ */
+ if (!txskb) {
+ roc->cookie = ieee80211_mgmt_tx_cookie(local);
+ *cookie = roc->cookie;
+ } else {
+ roc->mgmt_tx_cookie = *cookie;
+ }
+
+ /* if there's no need to queue, handle it immediately */
+ if (list_empty(&local->roc_list) &&
+ !local->scanning && !ieee80211_is_radar_required(local)) {
+ /* if not HW assist, just queue & schedule work */
+ if (!local->ops->remain_on_channel) {
+ list_add_tail(&roc->list, &local->roc_list);
+ ieee80211_queue_delayed_work(&local->hw,
+ &local->roc_work, 0);
+ } else {
+ /* otherwise actually kick it off here
+ * (for error handling)
+ */
+ ret = drv_remain_on_channel(local, sdata, channel,
+ duration, type);
+ if (ret) {
+ kfree(roc);
+ return ret;
+ }
+ roc->started = true;
+ list_add_tail(&roc->list, &local->roc_list);
+ }
+
+ return 0;
+ }
+
+ /* otherwise handle queueing */
+
+ list_for_each_entry(tmp, &local->roc_list, list) {
+ if (tmp->chan != channel || tmp->sdata != sdata)
+ continue;
+
+ /*
+ * Extend this ROC if possible: If it hasn't started, add
+ * just after the new one to combine.
+ */
+ if (!tmp->started) {
+ list_add(&roc->list, &tmp->list);
+ queued = true;
+ break;
+ }
+
+ if (!combine_started)
+ continue;
+
+ if (!local->ops->remain_on_channel) {
+ /* If there's no hardware remain-on-channel, and
+ * doing so won't push us over the maximum r-o-c
+ * we allow, then we can just add the new one to
+ * the list and mark it as having started now.
+ * If it would push over the limit, don't try to
+ * combine with other started ones (that haven't
+ * been running as long) but potentially sort it
+ * with others that had the same fate.
+ */
+ unsigned long now = jiffies;
+ u32 elapsed = jiffies_to_msecs(now - tmp->start_time);
+ struct wiphy *wiphy = local->hw.wiphy;
+ u32 max_roc = wiphy->max_remain_on_channel_duration;
+
+ if (elapsed + roc->duration > max_roc) {
+ combine_started = false;
+ continue;
+ }
+
+ list_add(&roc->list, &tmp->list);
+ queued = true;
+ roc->on_channel = tmp->on_channel;
+ ieee80211_handle_roc_started(roc, now);
+ break;
+ }
+
+ queued = ieee80211_coalesce_hw_started_roc(local, roc, tmp);
+ if (queued)
+ break;
+ /* if it wasn't queued, perhaps it can be combined with
+ * another that also couldn't get combined previously,
+ * but no need to check for already started ones, since
+ * that can't work.
+ */
+ combine_started = false;
+ }
+
+ if (!queued)
+ list_add_tail(&roc->list, &local->roc_list);
+
+ return 0;
+}
+
+int ieee80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct ieee80211_channel *chan,
+ unsigned int duration, u64 *cookie)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ struct ieee80211_local *local = sdata->local;
+ int ret;
+
+ mutex_lock(&local->mtx);
+ ret = ieee80211_start_roc_work(local, sdata, chan,
+ duration, cookie, NULL,
+ IEEE80211_ROC_TYPE_NORMAL);
+ mutex_unlock(&local->mtx);
+
+ return ret;
+}
+
+static int ieee80211_cancel_roc(struct ieee80211_local *local,
+ u64 cookie, bool mgmt_tx)
+{
+ struct ieee80211_roc_work *roc, *tmp, *found = NULL;
+ int ret;
+
+ if (!cookie)
+ return -ENOENT;
mutex_lock(&local->mtx);
list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
- if (sdata && roc->sdata != sdata)
+ if (!mgmt_tx && roc->cookie != cookie)
continue;
+ else if (mgmt_tx && roc->mgmt_tx_cookie != cookie)
+ continue;
+
+ found = roc;
+ break;
+ }
+
+ if (!found) {
+ mutex_unlock(&local->mtx);
+ return -ENOENT;
+ }
+
+ if (!found->started) {
+ ieee80211_roc_notify_destroy(found);
+ goto out_unlock;
+ }
- if (roc->started && local->ops->remain_on_channel) {
- /* can race, so ignore return value */
- drv_cancel_remain_on_channel(local);
+ if (local->ops->remain_on_channel) {
+ ret = drv_cancel_remain_on_channel(local);
+ if (WARN_ON_ONCE(ret)) {
+ mutex_unlock(&local->mtx);
+ return ret;
+ }
+
+ /* TODO:
+ * if multiple items were combined here then we really shouldn't
+ * cancel them all - we should wait for as much time as needed
+ * for the longest remaining one, and only then cancel ...
+ */
+ list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
+ if (!roc->started)
+ break;
+ if (roc == found)
+ found = NULL;
+ ieee80211_roc_notify_destroy(roc);
}
- list_move_tail(&roc->list, &tmp_list);
- roc->abort = true;
+ /* that really must not happen - it was started */
+ WARN_ON(found);
+
+ ieee80211_start_next_roc(local);
+ } else {
+ /* go through work struct to return to the operating channel */
+ found->abort = true;
+ mod_delayed_work(local->workqueue, &local->roc_work, 0);
}
+
+ out_unlock:
mutex_unlock(&local->mtx);
- list_for_each_entry_safe(roc, tmp, &tmp_list, list) {
- if (local->ops->remain_on_channel) {
- list_del(&roc->list);
- ieee80211_roc_notify_destroy(roc, true);
+ return 0;
+}
+
+int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev, u64 cookie)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ struct ieee80211_local *local = sdata->local;
+
+ return ieee80211_cancel_roc(local, cookie, false);
+}
+
+int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params, u64 *cookie)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ struct ieee80211_local *local = sdata->local;
+ struct sk_buff *skb;
+ struct sta_info *sta;
+ const struct ieee80211_mgmt *mgmt = (void *)params->buf;
+ bool need_offchan = false;
+ u32 flags;
+ int ret;
+ u8 *data;
+
+ if (params->dont_wait_for_ack)
+ flags = IEEE80211_TX_CTL_NO_ACK;
+ else
+ flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX |
+ IEEE80211_TX_CTL_REQ_TX_STATUS;
+
+ if (params->no_cck)
+ flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
+
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_ADHOC:
+ if (!sdata->vif.bss_conf.ibss_joined)
+ need_offchan = true;
+ /* fall through */
+#ifdef CONFIG_MAC80211_MESH
+ case NL80211_IFTYPE_MESH_POINT:
+ if (ieee80211_vif_is_mesh(&sdata->vif) &&
+ !sdata->u.mesh.mesh_id_len)
+ need_offchan = true;
+ /* fall through */
+#endif
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_P2P_GO:
+ if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+ !ieee80211_vif_is_mesh(&sdata->vif) &&
+ !rcu_access_pointer(sdata->bss->beacon))
+ need_offchan = true;
+ if (!ieee80211_is_action(mgmt->frame_control) ||
+ mgmt->u.action.category == WLAN_CATEGORY_PUBLIC ||
+ mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED ||
+ mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT)
+ break;
+ rcu_read_lock();
+ sta = sta_info_get(sdata, mgmt->da);
+ rcu_read_unlock();
+ if (!sta)
+ return -ENOLINK;
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ sdata_lock(sdata);
+ if (!sdata->u.mgd.associated ||
+ (params->offchan && params->wait &&
+ local->ops->remain_on_channel &&
+ memcmp(sdata->u.mgd.associated->bssid,
+ mgmt->bssid, ETH_ALEN)))
+ need_offchan = true;
+ sdata_unlock(sdata);
+ break;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ need_offchan = true;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ /* configurations requiring offchan cannot work if no channel has been
+ * specified
+ */
+ if (need_offchan && !params->chan)
+ return -EINVAL;
+
+ mutex_lock(&local->mtx);
+
+ /* Check if the operating channel is the requested channel */
+ if (!need_offchan) {
+ struct ieee80211_chanctx_conf *chanctx_conf;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+
+ if (chanctx_conf) {
+ need_offchan = params->chan &&
+ (params->chan !=
+ chanctx_conf->def.chan);
+ } else if (!params->chan) {
+ ret = -EINVAL;
+ rcu_read_unlock();
+ goto out_unlock;
} else {
- ieee80211_queue_delayed_work(&local->hw, &roc->work, 0);
+ need_offchan = true;
+ }
+ rcu_read_unlock();
+ }
+
+ if (need_offchan && !params->offchan) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + params->len);
+ if (!skb) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+
+ data = skb_put(skb, params->len);
+ memcpy(data, params->buf, params->len);
+
+ /* Update CSA counters */
+ if (sdata->vif.csa_active &&
+ (sdata->vif.type == NL80211_IFTYPE_AP ||
+ sdata->vif.type == NL80211_IFTYPE_MESH_POINT ||
+ sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
+ params->n_csa_offsets) {
+ int i;
+ struct beacon_data *beacon = NULL;
+
+ rcu_read_lock();
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP)
+ beacon = rcu_dereference(sdata->u.ap.beacon);
+ else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+ beacon = rcu_dereference(sdata->u.ibss.presp);
+ else if (ieee80211_vif_is_mesh(&sdata->vif))
+ beacon = rcu_dereference(sdata->u.mesh.beacon);
+
+ if (beacon)
+ for (i = 0; i < params->n_csa_offsets; i++)
+ data[params->csa_offsets[i]] =
+ beacon->csa_current_counter;
+
+ rcu_read_unlock();
+ }
- /* work will clean up etc */
- flush_delayed_work(&roc->work);
- WARN_ON(!roc->to_be_freed);
- kfree(roc);
+ IEEE80211_SKB_CB(skb)->flags = flags;
+
+ skb->dev = sdata->dev;
+
+ if (!params->dont_wait_for_ack) {
+ /* make a copy to preserve the frame contents
+ * in case of encryption.
+ */
+ ret = ieee80211_attach_ack_skb(local, skb, cookie, GFP_KERNEL);
+ if (ret) {
+ kfree_skb(skb);
+ goto out_unlock;
}
+ } else {
+ /* Assign a dummy non-zero cookie, it's not sent to
+ * userspace in this case but we rely on its value
+ * internally in the need_offchan case to distinguish
+ * mgmt-tx from remain-on-channel.
+ */
+ *cookie = 0xffffffff;
}
- WARN_ON_ONCE(!list_empty(&tmp_list));
+ if (!need_offchan) {
+ ieee80211_tx_skb(sdata, skb);
+ ret = 0;
+ goto out_unlock;
+ }
+
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN |
+ IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
+ if (ieee80211_hw_check(&local->hw, QUEUE_CONTROL))
+ IEEE80211_SKB_CB(skb)->hw_queue =
+ local->hw.offchannel_tx_hw_queue;
+
+ /* This will handle all kinds of coalescing and immediate TX */
+ ret = ieee80211_start_roc_work(local, sdata, params->chan,
+ params->wait, cookie, skb,
+ IEEE80211_ROC_TYPE_MGMT_TX);
+ if (ret)
+ ieee80211_free_txskb(&local->hw, skb);
+ out_unlock:
+ mutex_unlock(&local->mtx);
+ return ret;
+}
+
+int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
+ struct wireless_dev *wdev, u64 cookie)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+
+ return ieee80211_cancel_roc(local, cookie, true);
+}
+
+void ieee80211_roc_setup(struct ieee80211_local *local)
+{
+ INIT_WORK(&local->hw_roc_start, ieee80211_hw_roc_start);
+ INIT_WORK(&local->hw_roc_done, ieee80211_hw_roc_done);
+ INIT_DELAYED_WORK(&local->roc_work, ieee80211_roc_work);
+ INIT_LIST_HEAD(&local->roc_list);
+}
+
+void ieee80211_roc_purge(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_roc_work *roc, *tmp;
+ bool work_to_do = false;
+
+ mutex_lock(&local->mtx);
+ list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
+ if (sdata && roc->sdata != sdata)
+ continue;
+
+ if (roc->started) {
+ if (local->ops->remain_on_channel) {
+ /* can race, so ignore return value */
+ drv_cancel_remain_on_channel(local);
+ ieee80211_roc_notify_destroy(roc);
+ } else {
+ roc->abort = true;
+ work_to_do = true;
+ }
+ } else {
+ ieee80211_roc_notify_destroy(roc);
+ }
+ }
+ if (work_to_do)
+ __ieee80211_roc_work(local);
+ mutex_unlock(&local->mtx);
}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 8bae5de0dc44..bc081850ac0e 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -661,8 +661,7 @@ static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
static void ieee80211_verify_alignment(struct ieee80211_rx_data *rx)
{
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- WARN_ONCE((unsigned long)rx->skb->data & 1,
- "unaligned packet at 0x%p\n", rx->skb->data);
+ WARN_ON_ONCE((unsigned long)rx->skb->data & 1);
#endif
}
@@ -2736,8 +2735,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
opmode = mgmt->u.action.u.vht_opmode_notif.operating_mode;
ieee80211_vht_handle_opmode(rx->sdata, rx->sta,
- opmode, status->band,
- false);
+ opmode, status->band);
goto handled;
}
default:
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 4aeca4b0c3cb..a413e52f7691 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -597,8 +597,8 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
/* We need to ensure power level is at max for scanning. */
ieee80211_hw_config(local, 0);
- if ((req->channels[0]->flags &
- IEEE80211_CHAN_NO_IR) ||
+ if ((req->channels[0]->flags & (IEEE80211_CHAN_NO_IR |
+ IEEE80211_CHAN_RADAR)) ||
!req->n_ssids) {
next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
} else {
@@ -645,7 +645,7 @@ ieee80211_scan_get_channel_time(struct ieee80211_channel *chan)
* TODO: channel switching also consumes quite some time,
* add that delay as well to get a better estimation
*/
- if (chan->flags & IEEE80211_CHAN_NO_IR)
+ if (chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR))
return IEEE80211_PASSIVE_CHANNEL_TIME;
return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME;
}
@@ -777,7 +777,8 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
*
* In any case, it is not necessary for a passive scan.
*/
- if (chan->flags & IEEE80211_CHAN_NO_IR || !scan_req->n_ssids) {
+ if ((chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR)) ||
+ !scan_req->n_ssids) {
*next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
local->next_scan_state = SCAN_DECISION;
return;
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index f91d1873218c..4402ad5b27d1 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -2,6 +2,7 @@
* Copyright 2002-2005, Instant802 Networks, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2013-2014 Intel Mobile Communications GmbH
+ * Copyright (C) 2015 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -435,6 +436,19 @@ static int sta_info_insert_check(struct sta_info *sta)
is_multicast_ether_addr(sta->sta.addr)))
return -EINVAL;
+ /* Strictly speaking this isn't necessary as we hold the mutex, but
+ * the rhashtable code can't really deal with that distinction. We
+ * do require the mutex for correctness though.
+ */
+ rcu_read_lock();
+ lockdep_assert_held(&sdata->local->sta_mtx);
+ if (ieee80211_hw_check(&sdata->local->hw, NEEDS_UNIQUE_STA_ADDR) &&
+ ieee80211_find_sta_by_ifaddr(&sdata->local->hw, sta->addr, NULL)) {
+ rcu_read_unlock();
+ return -ENOTUNIQ;
+ }
+ rcu_read_unlock();
+
return 0;
}
@@ -554,14 +568,15 @@ int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
might_sleep();
+ mutex_lock(&local->sta_mtx);
+
err = sta_info_insert_check(sta);
if (err) {
+ mutex_unlock(&local->sta_mtx);
rcu_read_lock();
goto out_free;
}
- mutex_lock(&local->sta_mtx);
-
err = sta_info_insert_finish(sta);
if (err)
goto out_free;
@@ -868,6 +883,7 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta)
}
list_del_rcu(&sta->list);
+ sta->removed = true;
drv_sta_pre_rcu_remove(local, sta->sdata, sta);
@@ -1230,11 +1246,11 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
ieee80211_check_fast_xmit(sta);
}
-static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
- struct sta_info *sta, int tid,
+static void ieee80211_send_null_response(struct sta_info *sta, int tid,
enum ieee80211_frame_release_type reason,
- bool call_driver)
+ bool call_driver, bool more_data)
{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
struct ieee80211_qos_hdr *nullfunc;
struct sk_buff *skb;
@@ -1274,9 +1290,13 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
if (qos) {
nullfunc->qos_ctrl = cpu_to_le16(tid);
- if (reason == IEEE80211_FRAME_RELEASE_UAPSD)
+ if (reason == IEEE80211_FRAME_RELEASE_UAPSD) {
nullfunc->qos_ctrl |=
cpu_to_le16(IEEE80211_QOS_CTL_EOSP);
+ if (more_data)
+ nullfunc->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ }
}
info = IEEE80211_SKB_CB(skb);
@@ -1323,22 +1343,48 @@ static int find_highest_prio_tid(unsigned long tids)
return fls(tids) - 1;
}
+/* Indicates if the MORE_DATA bit should be set in the last
+ * frame obtained by ieee80211_sta_ps_get_frames.
+ * Note that driver_release_tids is relevant only if
+ * reason = IEEE80211_FRAME_RELEASE_PSPOLL
+ */
+static bool
+ieee80211_sta_ps_more_data(struct sta_info *sta, u8 ignored_acs,
+ enum ieee80211_frame_release_type reason,
+ unsigned long driver_release_tids)
+{
+ int ac;
+
+ /* If the driver has data on more than one TID then
+ * certainly there's more data if we release just a
+ * single frame now (from a single TID). This will
+ * only happen for PS-Poll.
+ */
+ if (reason == IEEE80211_FRAME_RELEASE_PSPOLL &&
+ hweight16(driver_release_tids) > 1)
+ return true;
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ if (ignored_acs & BIT(ac))
+ continue;
+
+ if (!skb_queue_empty(&sta->tx_filtered[ac]) ||
+ !skb_queue_empty(&sta->ps_tx_buf[ac]))
+ return true;
+ }
+
+ return false;
+}
+
static void
-ieee80211_sta_ps_deliver_response(struct sta_info *sta,
- int n_frames, u8 ignored_acs,
- enum ieee80211_frame_release_type reason)
+ieee80211_sta_ps_get_frames(struct sta_info *sta, int n_frames, u8 ignored_acs,
+ enum ieee80211_frame_release_type reason,
+ struct sk_buff_head *frames,
+ unsigned long *driver_release_tids)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
- bool more_data = false;
int ac;
- unsigned long driver_release_tids = 0;
- struct sk_buff_head frames;
-
- /* Service or PS-Poll period starts */
- set_sta_flag(sta, WLAN_STA_SP);
-
- __skb_queue_head_init(&frames);
/* Get response frame(s) and more data bit for the last one. */
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
@@ -1352,26 +1398,13 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
/* if we already have frames from software, then we can't also
* release from hardware queues
*/
- if (skb_queue_empty(&frames)) {
- driver_release_tids |= sta->driver_buffered_tids & tids;
- driver_release_tids |= sta->txq_buffered_tids & tids;
+ if (skb_queue_empty(frames)) {
+ *driver_release_tids |=
+ sta->driver_buffered_tids & tids;
+ *driver_release_tids |= sta->txq_buffered_tids & tids;
}
- if (driver_release_tids) {
- /* If the driver has data on more than one TID then
- * certainly there's more data if we release just a
- * single frame now (from a single TID). This will
- * only happen for PS-Poll.
- */
- if (reason == IEEE80211_FRAME_RELEASE_PSPOLL &&
- hweight16(driver_release_tids) > 1) {
- more_data = true;
- driver_release_tids =
- BIT(find_highest_prio_tid(
- driver_release_tids));
- break;
- }
- } else {
+ if (!*driver_release_tids) {
struct sk_buff *skb;
while (n_frames > 0) {
@@ -1385,20 +1418,44 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
if (!skb)
break;
n_frames--;
- __skb_queue_tail(&frames, skb);
+ __skb_queue_tail(frames, skb);
}
}
- /* If we have more frames buffered on this AC, then set the
- * more-data bit and abort the loop since we can't send more
- * data from other ACs before the buffered frames from this.
+ /* If we have more frames buffered on this AC, then abort the
+ * loop since we can't send more data from other ACs before
+ * the buffered frames from this.
*/
if (!skb_queue_empty(&sta->tx_filtered[ac]) ||
- !skb_queue_empty(&sta->ps_tx_buf[ac])) {
- more_data = true;
+ !skb_queue_empty(&sta->ps_tx_buf[ac]))
break;
- }
}
+}
+
+static void
+ieee80211_sta_ps_deliver_response(struct sta_info *sta,
+ int n_frames, u8 ignored_acs,
+ enum ieee80211_frame_release_type reason)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ieee80211_local *local = sdata->local;
+ unsigned long driver_release_tids = 0;
+ struct sk_buff_head frames;
+ bool more_data;
+
+ /* Service or PS-Poll period starts */
+ set_sta_flag(sta, WLAN_STA_SP);
+
+ __skb_queue_head_init(&frames);
+
+ ieee80211_sta_ps_get_frames(sta, n_frames, ignored_acs, reason,
+ &frames, &driver_release_tids);
+
+ more_data = ieee80211_sta_ps_more_data(sta, ignored_acs, reason, driver_release_tids);
+
+ if (reason == IEEE80211_FRAME_RELEASE_PSPOLL)
+ driver_release_tids =
+ BIT(find_highest_prio_tid(driver_release_tids));
if (skb_queue_empty(&frames) && !driver_release_tids) {
int tid;
@@ -1421,7 +1478,7 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
/* This will evaluate to 1, 3, 5 or 7. */
tid = 7 - ((ffs(~ignored_acs) - 1) << 1);
- ieee80211_send_null_response(sdata, sta, tid, reason, true);
+ ieee80211_send_null_response(sta, tid, reason, true, false);
} else if (!driver_release_tids) {
struct sk_buff_head pending;
struct sk_buff *skb;
@@ -1521,8 +1578,8 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
if (need_null)
ieee80211_send_null_response(
- sdata, sta, find_highest_prio_tid(tids),
- reason, false);
+ sta, find_highest_prio_tid(tids),
+ reason, false, false);
sta_info_recalc_tim(sta);
} else {
@@ -1660,6 +1717,22 @@ void ieee80211_sta_eosp(struct ieee80211_sta *pubsta)
}
EXPORT_SYMBOL(ieee80211_sta_eosp);
+void ieee80211_send_eosp_nullfunc(struct ieee80211_sta *pubsta, int tid)
+{
+ struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+ enum ieee80211_frame_release_type reason;
+ bool more_data;
+
+ trace_api_send_eosp_nullfunc(sta->local, pubsta, tid);
+
+ reason = IEEE80211_FRAME_RELEASE_UAPSD;
+ more_data = ieee80211_sta_ps_more_data(sta, ~sta->sta.uapsd_queues,
+ reason, 0);
+
+ ieee80211_send_null_response(sta, tid, reason, false, more_data);
+}
+EXPORT_SYMBOL(ieee80211_send_eosp_nullfunc);
+
void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
u8 tid, bool buffered)
{
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 2cafb21b422f..d6051629ed15 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -367,6 +367,7 @@ DECLARE_EWMA(signal, 1024, 8)
* @mesh: mesh STA information
* @debugfs: debug filesystem info
* @dead: set to true when sta is unlinked
+ * @removed: set to true when sta is being removed from sta_list
* @uploaded: set to true when sta is uploaded to the driver
* @sta: station information we share with the driver
* @sta_state: duplicates information about station state (for debug)
@@ -412,6 +413,7 @@ struct sta_info {
u16 listen_interval;
bool dead;
+ bool removed;
bool uploaded;
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 56c6d6cfa5a1..a6b4442776a0 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -2027,6 +2027,31 @@ TRACE_EVENT(api_eosp,
)
);
+TRACE_EVENT(api_send_eosp_nullfunc,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sta *sta,
+ u8 tid),
+
+ TP_ARGS(local, sta, tid),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ STA_ENTRY
+ __field(u8, tid)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ STA_ASSIGN;
+ __entry->tid = tid;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT STA_PR_FMT " tid:%d",
+ LOCAL_PR_ARG, STA_PR_ARG, __entry->tid
+ )
+);
+
TRACE_EVENT(api_sta_set_buffered,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sta *sta,
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index bdc224d5053a..3311ce0f3d6c 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1431,7 +1431,7 @@ static bool __ieee80211_tx(struct ieee80211_local *local,
info->hw_queue =
vif->hw_queue[skb_get_queue_mapping(skb)];
} else if (ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) {
- dev_kfree_skb(skb);
+ ieee80211_purge_tx_queue(&local->hw, skbs);
return true;
} else
vif = NULL;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 74058020b7d6..3943d4bf289c 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -288,10 +288,13 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
if (!test_bit(reason, &local->queue_stop_reasons[queue]))
return;
- if (!refcounted)
+ if (!refcounted) {
local->q_stop_reasons[queue][reason] = 0;
- else
+ } else {
local->q_stop_reasons[queue][reason]--;
+ if (WARN_ON(local->q_stop_reasons[queue][reason] < 0))
+ local->q_stop_reasons[queue][reason] = 0;
+ }
if (local->q_stop_reasons[queue][reason] == 0)
__clear_bit(reason, &local->queue_stop_reasons[queue]);
@@ -1641,6 +1644,29 @@ void ieee80211_stop_device(struct ieee80211_local *local)
drv_stop(local);
}
+static void ieee80211_flush_completed_scan(struct ieee80211_local *local,
+ bool aborted)
+{
+ /* It's possible that we don't handle the scan completion in
+ * time during suspend, so if it's still marked as completed
+ * here, queue the work and flush it to clean things up.
+ * Instead of calling the worker function directly here, we
+ * really queue it to avoid potential races with other flows
+ * scheduling the same work.
+ */
+ if (test_bit(SCAN_COMPLETED, &local->scanning)) {
+ /* If coming from reconfiguration failure, abort the scan so
+ * we don't attempt to continue a partial HW scan - which is
+ * possible otherwise if (e.g.) the 2.4 GHz portion was the
+ * completed scan, and a 5 GHz portion is still pending.
+ */
+ if (aborted)
+ set_bit(SCAN_ABORTED, &local->scanning);
+ ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0);
+ flush_delayed_work(&local->scan_work);
+ }
+}
+
static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;
@@ -1660,6 +1686,8 @@ static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local)
local->suspended = false;
local->in_reconfig = false;
+ ieee80211_flush_completed_scan(local, true);
+
/* scheduled scan clearly can't be running any more, but tell
* cfg80211 and clear local state
*/
@@ -1698,6 +1726,27 @@ static void ieee80211_assign_chanctx(struct ieee80211_local *local,
mutex_unlock(&local->chanctx_mtx);
}
+static void ieee80211_reconfig_stations(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct sta_info *sta;
+
+ /* add STAs back */
+ mutex_lock(&local->sta_mtx);
+ list_for_each_entry(sta, &local->sta_list, list) {
+ enum ieee80211_sta_state state;
+
+ if (!sta->uploaded || sta->sdata != sdata)
+ continue;
+
+ for (state = IEEE80211_STA_NOTEXIST;
+ state < sta->sta_state; state++)
+ WARN_ON(drv_sta_state(local, sta->sdata, sta, state,
+ state + 1));
+ }
+ mutex_unlock(&local->sta_mtx);
+}
+
int ieee80211_reconfig(struct ieee80211_local *local)
{
struct ieee80211_hw *hw = &local->hw;
@@ -1833,50 +1882,11 @@ int ieee80211_reconfig(struct ieee80211_local *local)
WARN_ON(drv_add_chanctx(local, ctx));
mutex_unlock(&local->chanctx_mtx);
- list_for_each_entry(sdata, &local->interfaces, list) {
- if (!ieee80211_sdata_running(sdata))
- continue;
- ieee80211_assign_chanctx(local, sdata);
- }
-
sdata = rtnl_dereference(local->monitor_sdata);
if (sdata && ieee80211_sdata_running(sdata))
ieee80211_assign_chanctx(local, sdata);
}
- /* add STAs back */
- mutex_lock(&local->sta_mtx);
- list_for_each_entry(sta, &local->sta_list, list) {
- enum ieee80211_sta_state state;
-
- if (!sta->uploaded)
- continue;
-
- /* AP-mode stations will be added later */
- if (sta->sdata->vif.type == NL80211_IFTYPE_AP)
- continue;
-
- for (state = IEEE80211_STA_NOTEXIST;
- state < sta->sta_state; state++)
- WARN_ON(drv_sta_state(local, sta->sdata, sta, state,
- state + 1));
- }
- mutex_unlock(&local->sta_mtx);
-
- /* reconfigure tx conf */
- if (hw->queues >= IEEE80211_NUM_ACS) {
- list_for_each_entry(sdata, &local->interfaces, list) {
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
- sdata->vif.type == NL80211_IFTYPE_MONITOR ||
- !ieee80211_sdata_running(sdata))
- continue;
-
- for (i = 0; i < IEEE80211_NUM_ACS; i++)
- drv_conf_tx(local, sdata, i,
- &sdata->tx_conf[i]);
- }
- }
-
/* reconfigure hardware */
ieee80211_hw_config(local, ~0);
@@ -1889,6 +1899,22 @@ int ieee80211_reconfig(struct ieee80211_local *local)
if (!ieee80211_sdata_running(sdata))
continue;
+ ieee80211_assign_chanctx(local, sdata);
+
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_MONITOR:
+ break;
+ default:
+ ieee80211_reconfig_stations(sdata);
+ /* fall through */
+ case NL80211_IFTYPE_AP: /* AP stations are handled later */
+ for (i = 0; i < IEEE80211_NUM_ACS; i++)
+ drv_conf_tx(local, sdata, i,
+ &sdata->tx_conf[i]);
+ break;
+ }
+
/* common change flags for all interface types */
changed = BSS_CHANGED_ERP_CTS_PROT |
BSS_CHANGED_ERP_PREAMBLE |
@@ -2074,17 +2100,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
mb();
local->resuming = false;
- /* It's possible that we don't handle the scan completion in
- * time during suspend, so if it's still marked as completed
- * here, queue the work and flush it to clean things up.
- * Instead of calling the worker function directly here, we
- * really queue it to avoid potential races with other flows
- * scheduling the same work.
- */
- if (test_bit(SCAN_COMPLETED, &local->scanning)) {
- ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0);
- flush_delayed_work(&local->scan_work);
- }
+ ieee80211_flush_completed_scan(local, false);
if (local->open_count && !reconfig_due_to_wowlan)
drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND);
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c
index ff1c798921a6..c38b2f07a919 100644
--- a/net/mac80211/vht.c
+++ b/net/mac80211/vht.c
@@ -378,7 +378,7 @@ void ieee80211_sta_set_rx_nss(struct sta_info *sta)
u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, u8 opmode,
- enum ieee80211_band band, bool nss_only)
+ enum ieee80211_band band)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
@@ -401,9 +401,6 @@ u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
changed |= IEEE80211_RC_NSS_CHANGED;
}
- if (nss_only)
- return changed;
-
switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) {
case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ:
sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_20;
@@ -430,13 +427,12 @@ u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, u8 opmode,
- enum ieee80211_band band, bool nss_only)
+ enum ieee80211_band band)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
- u32 changed = __ieee80211_vht_handle_opmode(sdata, sta, opmode,
- band, nss_only);
+ u32 changed = __ieee80211_vht_handle_opmode(sdata, sta, opmode, band);
if (changed > 0)
rate_control_rate_update(local, sband, sta, changed);
diff --git a/net/mac802154/driver-ops.h b/net/mac802154/driver-ops.h
index 0550f3365e33..fd9daf2ecec9 100644
--- a/net/mac802154/driver-ops.h
+++ b/net/mac802154/driver-ops.h
@@ -18,9 +18,6 @@ drv_xmit_async(struct ieee802154_local *local, struct sk_buff *skb)
static inline int
drv_xmit_sync(struct ieee802154_local *local, struct sk_buff *skb)
{
- /* don't allow other operations while sync xmit */
- ASSERT_RTNL();
-
might_sleep();
return local->ops->xmit_sync(&local->hw, skb);
diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c
index 8606da459ff3..3db16346cab3 100644
--- a/net/mac802154/mac_cmd.c
+++ b/net/mac802154/mac_cmd.c
@@ -126,7 +126,7 @@ static void mac802154_get_mac_params(struct net_device *dev,
params->lbt = wpan_dev->lbt;
}
-static struct ieee802154_llsec_ops mac802154_llsec_ops = {
+static const struct ieee802154_llsec_ops mac802154_llsec_ops = {
.get_params = mac802154_get_params,
.set_params = mac802154_set_params,
.add_key = mac802154_add_key,
diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c
index 42e96729dae6..446e1300383e 100644
--- a/net/mac802154/rx.c
+++ b/net/mac802154/rx.c
@@ -217,8 +217,7 @@ __ieee802154_rx_handle_packet(struct ieee802154_local *local,
break;
}
- if (skb)
- kfree_skb(skb);
+ kfree_skb(skb);
}
static void
diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c
index 3827f359b336..7e253455f9dd 100644
--- a/net/mac802154/tx.c
+++ b/net/mac802154/tx.c
@@ -38,12 +38,6 @@ void ieee802154_xmit_worker(struct work_struct *work)
struct net_device *dev = skb->dev;
int res;
- rtnl_lock();
-
- /* check if ifdown occurred while schedule */
- if (!netif_running(dev))
- goto err_tx;
-
res = drv_xmit_sync(local, skb);
if (res)
goto err_tx;
@@ -53,14 +47,11 @@ void ieee802154_xmit_worker(struct work_struct *work)
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
- rtnl_unlock();
-
return;
err_tx:
/* Restart the netif queue on each sub_if_data object. */
ieee802154_wake_queue(&local->hw);
- rtnl_unlock();
kfree_skb(skb);
netdev_dbg(dev, "transmission failed\n");
}
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index c70d750148b6..b18c5ed42d95 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -27,6 +27,8 @@
*/
#define MAX_MP_SELECT_LABELS 4
+#define MPLS_NEIGH_TABLE_UNSPEC (NEIGH_LINK_TABLE + 1)
+
static int zero = 0;
static int label_limit = (1 << 20) - 1;
@@ -96,22 +98,15 @@ bool mpls_pkt_too_big(const struct sk_buff *skb, unsigned int mtu)
}
EXPORT_SYMBOL_GPL(mpls_pkt_too_big);
-static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt,
- struct sk_buff *skb, bool bos)
+static u32 mpls_multipath_hash(struct mpls_route *rt,
+ struct sk_buff *skb, bool bos)
{
struct mpls_entry_decoded dec;
struct mpls_shim_hdr *hdr;
bool eli_seen = false;
int label_index;
- int nh_index = 0;
u32 hash = 0;
- /* No need to look further into packet if there's only
- * one path
- */
- if (rt->rt_nhn == 1)
- goto out;
-
for (label_index = 0; label_index < MAX_MP_SELECT_LABELS && !bos;
label_index++) {
if (!pskb_may_pull(skb, sizeof(*hdr) * label_index))
@@ -165,7 +160,38 @@ static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt,
}
}
- nh_index = hash % rt->rt_nhn;
+ return hash;
+}
+
+static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt,
+ struct sk_buff *skb, bool bos)
+{
+ int alive = ACCESS_ONCE(rt->rt_nhn_alive);
+ u32 hash = 0;
+ int nh_index = 0;
+ int n = 0;
+
+ /* No need to look further into packet if there's only
+ * one path
+ */
+ if (rt->rt_nhn == 1)
+ goto out;
+
+ if (alive <= 0)
+ return NULL;
+
+ hash = mpls_multipath_hash(rt, skb, bos);
+ nh_index = hash % alive;
+ if (alive == rt->rt_nhn)
+ goto out;
+ for_nexthops(rt) {
+ if (nh->nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
+ continue;
+ if (n == nh_index)
+ return nh;
+ n++;
+ } endfor_nexthops(rt);
+
out:
return &rt->rt_nh[nh_index];
}
@@ -317,7 +343,13 @@ static int mpls_forward(struct sk_buff *skb, struct net_device *dev,
}
}
- err = neigh_xmit(nh->nh_via_table, out_dev, mpls_nh_via(rt, nh), skb);
+ /* If via wasn't specified then send out using device address */
+ if (nh->nh_via_table == MPLS_NEIGH_TABLE_UNSPEC)
+ err = neigh_xmit(NEIGH_LINK_TABLE, out_dev,
+ out_dev->dev_addr, skb);
+ else
+ err = neigh_xmit(nh->nh_via_table, out_dev,
+ mpls_nh_via(rt, nh), skb);
if (err)
net_dbg_ratelimited("%s: packet transmission failed: %d\n",
__func__, err);
@@ -365,6 +397,7 @@ static struct mpls_route *mpls_rt_alloc(int num_nh, u8 max_alen)
GFP_KERNEL);
if (rt) {
rt->rt_nhn = num_nh;
+ rt->rt_nhn_alive = num_nh;
rt->rt_max_alen = max_alen_aligned;
}
@@ -534,8 +567,22 @@ static int mpls_nh_assign_dev(struct net *net, struct mpls_route *rt,
if (!mpls_dev_get(dev))
goto errout;
+ if ((nh->nh_via_table == NEIGH_LINK_TABLE) &&
+ (dev->addr_len != nh->nh_via_alen))
+ goto errout;
+
RCU_INIT_POINTER(nh->nh_dev, dev);
+ if (!(dev->flags & IFF_UP)) {
+ nh->nh_flags |= RTNH_F_DEAD;
+ } else {
+ unsigned int flags;
+
+ flags = dev_get_flags(dev);
+ if (!(flags & (IFF_RUNNING | IFF_LOWER_UP)))
+ nh->nh_flags |= RTNH_F_LINKDOWN;
+ }
+
return 0;
errout:
@@ -570,6 +617,9 @@ static int mpls_nh_build_from_cfg(struct mpls_route_config *cfg,
if (err)
goto errout;
+ if (nh->nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
+ rt->rt_nhn_alive--;
+
return 0;
errout:
@@ -577,8 +627,8 @@ errout:
}
static int mpls_nh_build(struct net *net, struct mpls_route *rt,
- struct mpls_nh *nh, int oif,
- struct nlattr *via, struct nlattr *newdst)
+ struct mpls_nh *nh, int oif, struct nlattr *via,
+ struct nlattr *newdst)
{
int err = -ENOMEM;
@@ -592,10 +642,14 @@ static int mpls_nh_build(struct net *net, struct mpls_route *rt,
goto errout;
}
- err = nla_get_via(via, &nh->nh_via_alen, &nh->nh_via_table,
- __mpls_nh_via(rt, nh));
- if (err)
- goto errout;
+ if (via) {
+ err = nla_get_via(via, &nh->nh_via_alen, &nh->nh_via_table,
+ __mpls_nh_via(rt, nh));
+ if (err)
+ goto errout;
+ } else {
+ nh->nh_via_table = MPLS_NEIGH_TABLE_UNSPEC;
+ }
err = mpls_nh_assign_dev(net, rt, nh, oif);
if (err)
@@ -677,15 +731,14 @@ static int mpls_nh_build_multi(struct mpls_route_config *cfg,
nla_newdst = nla_find(attrs, attrlen, RTA_NEWDST);
}
- if (!nla_via)
- goto errout;
-
err = mpls_nh_build(cfg->rc_nlinfo.nl_net, rt, nh,
- rtnh->rtnh_ifindex, nla_via,
- nla_newdst);
+ rtnh->rtnh_ifindex, nla_via, nla_newdst);
if (err)
goto errout;
+ if (nh->nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
+ rt->rt_nhn_alive--;
+
rtnh = rtnh_next(rtnh, &remaining);
nhs++;
} endfor_nexthops(rt);
@@ -875,34 +928,74 @@ free:
return ERR_PTR(err);
}
-static void mpls_ifdown(struct net_device *dev)
+static void mpls_ifdown(struct net_device *dev, int event)
{
struct mpls_route __rcu **platform_label;
struct net *net = dev_net(dev);
- struct mpls_dev *mdev;
unsigned index;
platform_label = rtnl_dereference(net->mpls.platform_label);
for (index = 0; index < net->mpls.platform_labels; index++) {
struct mpls_route *rt = rtnl_dereference(platform_label[index]);
+
if (!rt)
continue;
- for_nexthops(rt) {
+
+ change_nexthops(rt) {
if (rtnl_dereference(nh->nh_dev) != dev)
continue;
- nh->nh_dev = NULL;
+ switch (event) {
+ case NETDEV_DOWN:
+ case NETDEV_UNREGISTER:
+ nh->nh_flags |= RTNH_F_DEAD;
+ /* fall through */
+ case NETDEV_CHANGE:
+ nh->nh_flags |= RTNH_F_LINKDOWN;
+ ACCESS_ONCE(rt->rt_nhn_alive) = rt->rt_nhn_alive - 1;
+ break;
+ }
+ if (event == NETDEV_UNREGISTER)
+ RCU_INIT_POINTER(nh->nh_dev, NULL);
} endfor_nexthops(rt);
}
- mdev = mpls_dev_get(dev);
- if (!mdev)
- return;
- mpls_dev_sysctl_unregister(mdev);
+ return;
+}
+
+static void mpls_ifup(struct net_device *dev, unsigned int nh_flags)
+{
+ struct mpls_route __rcu **platform_label;
+ struct net *net = dev_net(dev);
+ unsigned index;
+ int alive;
+
+ platform_label = rtnl_dereference(net->mpls.platform_label);
+ for (index = 0; index < net->mpls.platform_labels; index++) {
+ struct mpls_route *rt = rtnl_dereference(platform_label[index]);
+
+ if (!rt)
+ continue;
+
+ alive = 0;
+ change_nexthops(rt) {
+ struct net_device *nh_dev =
+ rtnl_dereference(nh->nh_dev);
- RCU_INIT_POINTER(dev->mpls_ptr, NULL);
+ if (!(nh->nh_flags & nh_flags)) {
+ alive++;
+ continue;
+ }
+ if (nh_dev != dev)
+ continue;
+ alive++;
+ nh->nh_flags &= ~nh_flags;
+ } endfor_nexthops(rt);
+
+ ACCESS_ONCE(rt->rt_nhn_alive) = alive;
+ }
- kfree_rcu(mdev, rcu);
+ return;
}
static int mpls_dev_notify(struct notifier_block *this, unsigned long event,
@@ -910,9 +1003,9 @@ static int mpls_dev_notify(struct notifier_block *this, unsigned long event,
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct mpls_dev *mdev;
+ unsigned int flags;
- switch(event) {
- case NETDEV_REGISTER:
+ if (event == NETDEV_REGISTER) {
/* For now just support ethernet devices */
if ((dev->type == ARPHRD_ETHER) ||
(dev->type == ARPHRD_LOOPBACK)) {
@@ -920,10 +1013,39 @@ static int mpls_dev_notify(struct notifier_block *this, unsigned long event,
if (IS_ERR(mdev))
return notifier_from_errno(PTR_ERR(mdev));
}
- break;
+ return NOTIFY_OK;
+ }
+
+ mdev = mpls_dev_get(dev);
+ if (!mdev)
+ return NOTIFY_OK;
+ switch (event) {
+ case NETDEV_DOWN:
+ mpls_ifdown(dev, event);
+ break;
+ case NETDEV_UP:
+ flags = dev_get_flags(dev);
+ if (flags & (IFF_RUNNING | IFF_LOWER_UP))
+ mpls_ifup(dev, RTNH_F_DEAD | RTNH_F_LINKDOWN);
+ else
+ mpls_ifup(dev, RTNH_F_DEAD);
+ break;
+ case NETDEV_CHANGE:
+ flags = dev_get_flags(dev);
+ if (flags & (IFF_RUNNING | IFF_LOWER_UP))
+ mpls_ifup(dev, RTNH_F_DEAD | RTNH_F_LINKDOWN);
+ else
+ mpls_ifdown(dev, event);
+ break;
case NETDEV_UNREGISTER:
- mpls_ifdown(dev);
+ mpls_ifdown(dev, event);
+ mdev = mpls_dev_get(dev);
+ if (mdev) {
+ mpls_dev_sysctl_unregister(mdev);
+ RCU_INIT_POINTER(dev->mpls_ptr, NULL);
+ kfree_rcu(mdev, rcu);
+ }
break;
case NETDEV_CHANGENAME:
mdev = mpls_dev_get(dev);
@@ -1118,6 +1240,7 @@ static int rtm_to_route_config(struct sk_buff *skb, struct nlmsghdr *nlh,
cfg->rc_label = LABEL_NOT_SPECIFIED;
cfg->rc_protocol = rtm->rtm_protocol;
+ cfg->rc_via_table = MPLS_NEIGH_TABLE_UNSPEC;
cfg->rc_nlflags = nlh->nlmsg_flags;
cfg->rc_nlinfo.portid = NETLINK_CB(skb).portid;
cfg->rc_nlinfo.nlh = nlh;
@@ -1231,15 +1354,22 @@ static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event,
nla_put_labels(skb, RTA_NEWDST, nh->nh_labels,
nh->nh_label))
goto nla_put_failure;
- if (nla_put_via(skb, nh->nh_via_table, mpls_nh_via(rt, nh),
+ if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC &&
+ nla_put_via(skb, nh->nh_via_table, mpls_nh_via(rt, nh),
nh->nh_via_alen))
goto nla_put_failure;
dev = rtnl_dereference(nh->nh_dev);
if (dev && nla_put_u32(skb, RTA_OIF, dev->ifindex))
goto nla_put_failure;
+ if (nh->nh_flags & RTNH_F_LINKDOWN)
+ rtm->rtm_flags |= RTNH_F_LINKDOWN;
+ if (nh->nh_flags & RTNH_F_DEAD)
+ rtm->rtm_flags |= RTNH_F_DEAD;
} else {
struct rtnexthop *rtnh;
struct nlattr *mp;
+ int dead = 0;
+ int linkdown = 0;
mp = nla_nest_start(skb, RTA_MULTIPATH);
if (!mp)
@@ -1253,11 +1383,21 @@ static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event,
dev = rtnl_dereference(nh->nh_dev);
if (dev)
rtnh->rtnh_ifindex = dev->ifindex;
+ if (nh->nh_flags & RTNH_F_LINKDOWN) {
+ rtnh->rtnh_flags |= RTNH_F_LINKDOWN;
+ linkdown++;
+ }
+ if (nh->nh_flags & RTNH_F_DEAD) {
+ rtnh->rtnh_flags |= RTNH_F_DEAD;
+ dead++;
+ }
+
if (nh->nh_labels && nla_put_labels(skb, RTA_NEWDST,
nh->nh_labels,
nh->nh_label))
goto nla_put_failure;
- if (nla_put_via(skb, nh->nh_via_table,
+ if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC &&
+ nla_put_via(skb, nh->nh_via_table,
mpls_nh_via(rt, nh),
nh->nh_via_alen))
goto nla_put_failure;
@@ -1266,6 +1406,11 @@ static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event,
rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *)rtnh;
} endfor_nexthops(rt);
+ if (linkdown == rt->rt_nhn)
+ rtm->rtm_flags |= RTNH_F_LINKDOWN;
+ if (dead == rt->rt_nhn)
+ rtm->rtm_flags |= RTNH_F_DEAD;
+
nla_nest_end(skb, mp);
}
@@ -1319,7 +1464,8 @@ static inline size_t lfib_nlmsg_size(struct mpls_route *rt)
if (nh->nh_dev)
payload += nla_total_size(4); /* RTA_OIF */
- payload += nla_total_size(2 + nh->nh_via_alen); /* RTA_VIA */
+ if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC) /* RTA_VIA */
+ payload += nla_total_size(2 + nh->nh_via_alen);
if (nh->nh_labels) /* RTA_NEWDST */
payload += nla_total_size(nh->nh_labels * 4);
} else {
@@ -1328,7 +1474,9 @@ static inline size_t lfib_nlmsg_size(struct mpls_route *rt)
for_nexthops(rt) {
nhsize += nla_total_size(sizeof(struct rtnexthop));
- nhsize += nla_total_size(2 + nh->nh_via_alen);
+ /* RTA_VIA */
+ if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC)
+ nhsize += nla_total_size(2 + nh->nh_via_alen);
if (nh->nh_labels)
nhsize += nla_total_size(nh->nh_labels * 4);
} endfor_nexthops(rt);
diff --git a/net/mpls/internal.h b/net/mpls/internal.h
index bde52ce88c94..732a5c17e986 100644
--- a/net/mpls/internal.h
+++ b/net/mpls/internal.h
@@ -41,6 +41,7 @@ enum mpls_payload_type {
struct mpls_nh { /* next hop label forwarding entry */
struct net_device __rcu *nh_dev;
+ unsigned int nh_flags;
u32 nh_label[MAX_NEW_LABELS];
u8 nh_labels;
u8 nh_via_alen;
@@ -74,6 +75,7 @@ struct mpls_route { /* next hop label forwarding entry */
u8 rt_payload_type;
u8 rt_max_alen;
unsigned int rt_nhn;
+ unsigned int rt_nhn_alive;
struct mpls_nh rt_nh[0];
};
diff --git a/net/mpls/mpls_iptunnel.c b/net/mpls/mpls_iptunnel.c
index 67591aef9cae..fb31aa87de81 100644
--- a/net/mpls/mpls_iptunnel.c
+++ b/net/mpls/mpls_iptunnel.c
@@ -37,7 +37,7 @@ static unsigned int mpls_encap_size(struct mpls_iptunnel_encap *en)
return en->labels * sizeof(struct mpls_shim_hdr);
}
-int mpls_output(struct net *net, struct sock *sk, struct sk_buff *skb)
+static int mpls_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct mpls_iptunnel_encap *tun_encap_info;
struct mpls_shim_hdr *hdr;
@@ -54,10 +54,10 @@ int mpls_output(struct net *net, struct sock *sk, struct sk_buff *skb)
unsigned int ttl;
/* Obtain the ttl */
- if (skb->protocol == htons(ETH_P_IP)) {
+ if (dst->ops->family == AF_INET) {
ttl = ip_hdr(skb)->ttl;
rt = (struct rtable *)dst;
- } else if (skb->protocol == htons(ETH_P_IPV6)) {
+ } else if (dst->ops->family == AF_INET6) {
ttl = ipv6_hdr(skb)->hop_limit;
rt6 = (struct rt6_info *)dst;
} else {
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index 4692782b5280..8c067e6663a1 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -563,6 +563,28 @@ config NFT_COMPAT
x_tables match/target extensions over the nf_tables
framework.
+if NF_TABLES_NETDEV
+
+config NF_DUP_NETDEV
+ tristate "Netfilter packet duplication support"
+ help
+ This option enables the generic packet duplication infrastructure
+ for Netfilter.
+
+config NFT_DUP_NETDEV
+ tristate "Netfilter nf_tables netdev packet duplication support"
+ select NF_DUP_NETDEV
+ help
+ This option enables packet duplication for the "netdev" family.
+
+config NFT_FWD_NETDEV
+ tristate "Netfilter nf_tables netdev packet forwarding support"
+ select NF_DUP_NETDEV
+ help
+ This option enables packet forwarding for the "netdev" family.
+
+endif # NF_TABLES_NETDEV
+
endif # NF_TABLES
config NETFILTER_XTABLES
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 7638c36b498c..69134541d65b 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -66,8 +66,11 @@ obj-$(CONFIG_NF_NAT_TFTP) += nf_nat_tftp.o
# SYNPROXY
obj-$(CONFIG_NETFILTER_SYNPROXY) += nf_synproxy_core.o
+# generic packet duplication from netdev family
+obj-$(CONFIG_NF_DUP_NETDEV) += nf_dup_netdev.o
+
# nf_tables
-nf_tables-objs += nf_tables_core.o nf_tables_api.o
+nf_tables-objs += nf_tables_core.o nf_tables_api.o nf_tables_trace.o
nf_tables-objs += nft_immediate.o nft_cmp.o nft_lookup.o nft_dynset.o
nf_tables-objs += nft_bitwise.o nft_byteorder.o nft_payload.o
@@ -90,6 +93,10 @@ obj-$(CONFIG_NFT_LOG) += nft_log.o
obj-$(CONFIG_NFT_MASQ) += nft_masq.o
obj-$(CONFIG_NFT_REDIR) += nft_redir.o
+# nf_tables netdev
+obj-$(CONFIG_NFT_DUP_NETDEV) += nft_dup_netdev.o
+obj-$(CONFIG_NFT_FWD_NETDEV) += nft_fwd_netdev.o
+
# generic X tables
obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index 54f3d7cb23e6..95db43fc0303 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -825,20 +825,17 @@ find_free_id(struct ip_set_net *inst, const char *name, ip_set_id_t *index,
return 0;
}
-static int
-ip_set_none(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_none(struct net *net, struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
return -EOPNOTSUPP;
}
-static int
-ip_set_create(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_create(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
- struct net *net = sock_net(ctnl);
struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set *set, *clash = NULL;
ip_set_id_t index = IPSET_INVALID_ID;
@@ -976,12 +973,11 @@ ip_set_destroy_set(struct ip_set *set)
kfree(set);
}
-static int
-ip_set_destroy(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_destroy(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
- struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
+ struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set *s;
ip_set_id_t i;
int ret = 0;
@@ -1052,12 +1048,11 @@ ip_set_flush_set(struct ip_set *set)
spin_unlock_bh(&set->lock);
}
-static int
-ip_set_flush(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_flush(struct net *net, struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
- struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
+ struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set *s;
ip_set_id_t i;
@@ -1092,12 +1087,11 @@ ip_set_setname2_policy[IPSET_ATTR_CMD_MAX + 1] = {
.len = IPSET_MAXNAMELEN - 1 },
};
-static int
-ip_set_rename(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_rename(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
- struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
+ struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set *set, *s;
const char *name2;
ip_set_id_t i;
@@ -1142,12 +1136,11 @@ out:
* so the ip_set_list always contains valid pointers to the sets.
*/
-static int
-ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_swap(struct net *net, struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
- struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
+ struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set *from, *to;
ip_set_id_t from_id, to_id;
char from_name[IPSET_MAXNAMELEN];
@@ -1413,10 +1406,9 @@ out:
return ret < 0 ? ret : skb->len;
}
-static int
-ip_set_dump(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_dump(struct net *net, struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
if (unlikely(protocol_failed(attr)))
return -IPSET_ERR_PROTOCOL;
@@ -1500,12 +1492,11 @@ call_ad(struct sock *ctnl, struct sk_buff *skb, struct ip_set *set,
return ret;
}
-static int
-ip_set_uadd(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_uadd(struct net *net, struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
- struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
+ struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set *set;
struct nlattr *tb[IPSET_ATTR_ADT_MAX + 1] = {};
const struct nlattr *nla;
@@ -1555,12 +1546,11 @@ ip_set_uadd(struct sock *ctnl, struct sk_buff *skb,
return ret;
}
-static int
-ip_set_udel(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_udel(struct net *net, struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
- struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
+ struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set *set;
struct nlattr *tb[IPSET_ATTR_ADT_MAX + 1] = {};
const struct nlattr *nla;
@@ -1610,12 +1600,11 @@ ip_set_udel(struct sock *ctnl, struct sk_buff *skb,
return ret;
}
-static int
-ip_set_utest(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_utest(struct net *net, struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
- struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
+ struct ip_set_net *inst = ip_set_pernet(net);
struct ip_set *set;
struct nlattr *tb[IPSET_ATTR_ADT_MAX + 1] = {};
int ret = 0;
@@ -1646,12 +1635,11 @@ ip_set_utest(struct sock *ctnl, struct sk_buff *skb,
/* Get headed data of a set */
-static int
-ip_set_header(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_header(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
- struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl));
+ struct ip_set_net *inst = ip_set_pernet(net);
const struct ip_set *set;
struct sk_buff *skb2;
struct nlmsghdr *nlh2;
@@ -1703,10 +1691,9 @@ static const struct nla_policy ip_set_type_policy[IPSET_ATTR_CMD_MAX + 1] = {
[IPSET_ATTR_FAMILY] = { .type = NLA_U8 },
};
-static int
-ip_set_type(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_type(struct net *net, struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
struct sk_buff *skb2;
struct nlmsghdr *nlh2;
@@ -1762,10 +1749,9 @@ ip_set_protocol_policy[IPSET_ATTR_CMD_MAX + 1] = {
[IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 },
};
-static int
-ip_set_protocol(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const attr[])
+static int ip_set_protocol(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const attr[])
{
struct sk_buff *skb2;
struct nlmsghdr *nlh2;
diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c
index 010ddeec135f..d952d67f904d 100644
--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
@@ -169,7 +169,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
/* Only update csum if we really have to */
if (sctph->dest != cp->dport || payload_csum ||
(skb->ip_summed == CHECKSUM_PARTIAL &&
- !(skb_dst(skb)->dev->features & NETIF_F_SCTP_CSUM))) {
+ !(skb_dst(skb)->dev->features & NETIF_F_SCTP_CRC))) {
sctph->dest = cp->dport;
sctp_nat_csum(skb, sctph, sctphoff);
} else if (skb->ip_summed != CHECKSUM_PARTIAL) {
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index acf5c7b3f378..278927ab0948 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -596,11 +596,18 @@ static int exp_proc_init(struct net *net)
{
#ifdef CONFIG_NF_CONNTRACK_PROCFS
struct proc_dir_entry *proc;
+ kuid_t root_uid;
+ kgid_t root_gid;
proc = proc_create("nf_conntrack_expect", 0440, net->proc_net,
&exp_file_ops);
if (!proc)
return -ENOMEM;
+
+ root_uid = make_kuid(net->user_ns, 0);
+ root_gid = make_kgid(net->user_ns, 0);
+ if (uid_valid(root_uid) && gid_valid(root_gid))
+ proc_set_user(proc, root_uid, root_gid);
#endif /* CONFIG_NF_CONNTRACK_PROCFS */
return 0;
}
diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c
index b666959f17c0..883c691ec8d0 100644
--- a/net/netfilter/nf_conntrack_ftp.c
+++ b/net/netfilter/nf_conntrack_ftp.c
@@ -10,6 +10,8 @@
* published by the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/netfilter.h>
@@ -505,11 +507,11 @@ skip_nl_seq:
different IP address. Simply don't record it for
NAT. */
if (cmd.l3num == PF_INET) {
- pr_debug("conntrack_ftp: NOT RECORDING: %pI4 != %pI4\n",
+ pr_debug("NOT RECORDING: %pI4 != %pI4\n",
&cmd.u3.ip,
&ct->tuplehash[dir].tuple.src.u3.ip);
} else {
- pr_debug("conntrack_ftp: NOT RECORDING: %pI6 != %pI6\n",
+ pr_debug("NOT RECORDING: %pI6 != %pI6\n",
cmd.u3.ip6,
ct->tuplehash[dir].tuple.src.u3.ip6);
}
@@ -586,8 +588,7 @@ static void nf_conntrack_ftp_fini(void)
if (ftp[i][j].me == NULL)
continue;
- pr_debug("nf_ct_ftp: unregistering helper for pf: %d "
- "port: %d\n",
+ pr_debug("unregistering helper for pf: %d port: %d\n",
ftp[i][j].tuple.src.l3num, ports[i]);
nf_conntrack_helper_unregister(&ftp[i][j]);
}
@@ -625,14 +626,12 @@ static int __init nf_conntrack_ftp_init(void)
else
sprintf(ftp[i][j].name, "ftp-%d", ports[i]);
- pr_debug("nf_ct_ftp: registering helper for pf: %d "
- "port: %d\n",
+ pr_debug("registering helper for pf: %d port: %d\n",
ftp[i][j].tuple.src.l3num, ports[i]);
ret = nf_conntrack_helper_register(&ftp[i][j]);
if (ret) {
- printk(KERN_ERR "nf_ct_ftp: failed to register"
- " helper for pf: %d port: %d\n",
- ftp[i][j].tuple.src.l3num, ports[i]);
+ pr_err("failed to register helper for pf: %d port: %d\n",
+ ftp[i][j].tuple.src.l3num, ports[i]);
nf_conntrack_ftp_fini();
return ret;
}
diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c
index 0fd2976db7ee..8b6da2719600 100644
--- a/net/netfilter/nf_conntrack_irc.c
+++ b/net/netfilter/nf_conntrack_irc.c
@@ -9,6 +9,8 @@
* 2 of the License, or (at your option) any later version.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/skbuff.h>
@@ -237,7 +239,7 @@ static int __init nf_conntrack_irc_init(void)
int i, ret;
if (max_dcc_channels < 1) {
- printk(KERN_ERR "nf_ct_irc: max_dcc_channels must not be zero\n");
+ pr_err("max_dcc_channels must not be zero\n");
return -EINVAL;
}
@@ -267,8 +269,7 @@ static int __init nf_conntrack_irc_init(void)
ret = nf_conntrack_helper_register(&irc[i]);
if (ret) {
- printk(KERN_ERR "nf_ct_irc: failed to register helper "
- "for pf: %u port: %u\n",
+ pr_err("failed to register helper for pf: %u port: %u\n",
irc[i].tuple.src.l3num, ports[i]);
nf_conntrack_irc_fini();
return ret;
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 9f5272968abb..dbb1bb3edb45 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -1113,12 +1113,11 @@ static int ctnetlink_flush_conntrack(struct net *net,
return 0;
}
-static int
-ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_del_conntrack(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
- struct net *net = sock_net(ctnl);
struct nf_conntrack_tuple_hash *h;
struct nf_conntrack_tuple tuple;
struct nf_conn *ct;
@@ -1168,12 +1167,11 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
return 0;
}
-static int
-ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_get_conntrack(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
- struct net *net = sock_net(ctnl);
struct nf_conntrack_tuple_hash *h;
struct nf_conntrack_tuple tuple;
struct nf_conn *ct;
@@ -1330,10 +1328,10 @@ ctnetlink_dump_dying(struct sk_buff *skb, struct netlink_callback *cb)
return ctnetlink_dump_list(skb, cb, true);
}
-static int
-ctnetlink_get_ct_dying(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_get_ct_dying(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
if (nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = {
@@ -1352,10 +1350,10 @@ ctnetlink_dump_unconfirmed(struct sk_buff *skb, struct netlink_callback *cb)
return ctnetlink_dump_list(skb, cb, false);
}
-static int
-ctnetlink_get_ct_unconfirmed(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_get_ct_unconfirmed(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
if (nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = {
@@ -1865,12 +1863,11 @@ err1:
return ERR_PTR(err);
}
-static int
-ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_new_conntrack(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
- struct net *net = sock_net(ctnl);
struct nf_conntrack_tuple otuple, rtuple;
struct nf_conntrack_tuple_hash *h = NULL;
struct nfgenmsg *nfmsg = nlmsg_data(nlh);
@@ -2034,10 +2031,10 @@ ctnetlink_ct_stat_cpu_dump(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len;
}
-static int
-ctnetlink_stat_ct_cpu(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_stat_ct_cpu(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
if (nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = {
@@ -2080,10 +2077,9 @@ nlmsg_failure:
return -1;
}
-static int
-ctnetlink_stat_ct(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_stat_ct(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
struct sk_buff *skb2;
int err;
@@ -2729,12 +2725,12 @@ out:
return skb->len;
}
-static int ctnetlink_dump_exp_ct(struct sock *ctnl, struct sk_buff *skb,
+static int ctnetlink_dump_exp_ct(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const cda[])
{
int err;
- struct net *net = sock_net(ctnl);
struct nfgenmsg *nfmsg = nlmsg_data(nlh);
u_int8_t u3 = nfmsg->nfgen_family;
struct nf_conntrack_tuple tuple;
@@ -2768,12 +2764,10 @@ static int ctnetlink_dump_exp_ct(struct sock *ctnl, struct sk_buff *skb,
return err;
}
-static int
-ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_get_expect(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
- struct net *net = sock_net(ctnl);
struct nf_conntrack_tuple tuple;
struct nf_conntrack_expect *exp;
struct sk_buff *skb2;
@@ -2784,7 +2778,7 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
if (nlh->nlmsg_flags & NLM_F_DUMP) {
if (cda[CTA_EXPECT_MASTER])
- return ctnetlink_dump_exp_ct(ctnl, skb, nlh, cda);
+ return ctnetlink_dump_exp_ct(net, ctnl, skb, nlh, cda);
else {
struct netlink_dump_control c = {
.dump = ctnetlink_exp_dump_table,
@@ -2850,12 +2844,10 @@ out:
return err == -EAGAIN ? -ENOBUFS : err;
}
-static int
-ctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_del_expect(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
- struct net *net = sock_net(ctnl);
struct nf_conntrack_expect *exp;
struct nf_conntrack_tuple tuple;
struct nfgenmsg *nfmsg = nlmsg_data(nlh);
@@ -3136,12 +3128,10 @@ err_ct:
return err;
}
-static int
-ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_new_expect(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
- struct net *net = sock_net(ctnl);
struct nf_conntrack_tuple tuple;
struct nf_conntrack_expect *exp;
struct nfgenmsg *nfmsg = nlmsg_data(nlh);
@@ -3242,10 +3232,10 @@ ctnetlink_exp_stat_cpu_dump(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len;
}
-static int
-ctnetlink_stat_exp_cpu(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int ctnetlink_stat_exp_cpu(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
if (nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = {
diff --git a/net/netfilter/nf_conntrack_sane.c b/net/netfilter/nf_conntrack_sane.c
index 4a2134fd3fcb..7523a575f6d1 100644
--- a/net/netfilter/nf_conntrack_sane.c
+++ b/net/netfilter/nf_conntrack_sane.c
@@ -17,6 +17,8 @@
* published by the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/netfilter.h>
@@ -120,14 +122,14 @@ static int help(struct sk_buff *skb,
ct_sane_info->state = SANE_STATE_NORMAL;
if (datalen < sizeof(struct sane_reply_net_start)) {
- pr_debug("nf_ct_sane: NET_START reply too short\n");
+ pr_debug("NET_START reply too short\n");
goto out;
}
reply = sb_ptr;
if (reply->status != htonl(SANE_STATUS_SUCCESS)) {
/* saned refused the command */
- pr_debug("nf_ct_sane: unsuccessful SANE_STATUS = %u\n",
+ pr_debug("unsuccessful SANE_STATUS = %u\n",
ntohl(reply->status));
goto out;
}
@@ -148,7 +150,7 @@ static int help(struct sk_buff *skb,
&tuple->src.u3, &tuple->dst.u3,
IPPROTO_TCP, NULL, &reply->port);
- pr_debug("nf_ct_sane: expect: ");
+ pr_debug("expect: ");
nf_ct_dump_tuple(&exp->tuple);
/* Can't expect this? Best to drop packet now. */
@@ -178,8 +180,7 @@ static void nf_conntrack_sane_fini(void)
for (i = 0; i < ports_c; i++) {
for (j = 0; j < 2; j++) {
- pr_debug("nf_ct_sane: unregistering helper for pf: %d "
- "port: %d\n",
+ pr_debug("unregistering helper for pf: %d port: %d\n",
sane[i][j].tuple.src.l3num, ports[i]);
nf_conntrack_helper_unregister(&sane[i][j]);
}
@@ -216,14 +217,12 @@ static int __init nf_conntrack_sane_init(void)
else
sprintf(sane[i][j].name, "sane-%d", ports[i]);
- pr_debug("nf_ct_sane: registering helper for pf: %d "
- "port: %d\n",
+ pr_debug("registering helper for pf: %d port: %d\n",
sane[i][j].tuple.src.l3num, ports[i]);
ret = nf_conntrack_helper_register(&sane[i][j]);
if (ret) {
- printk(KERN_ERR "nf_ct_sane: failed to "
- "register helper for pf: %d port: %d\n",
- sane[i][j].tuple.src.l3num, ports[i]);
+ pr_err("failed to register helper for pf: %d port: %d\n",
+ sane[i][j].tuple.src.l3num, ports[i]);
nf_conntrack_sane_fini();
return ret;
}
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index 885b4aba3695..3e06402739e0 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -10,6 +10,8 @@
* published by the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/skbuff.h>
@@ -1665,8 +1667,7 @@ static int __init nf_conntrack_sip_init(void)
ret = nf_conntrack_helper_register(&sip[i][j]);
if (ret) {
- printk(KERN_ERR "nf_ct_sip: failed to register"
- " helper for pf: %u port: %u\n",
+ pr_err("failed to register helper for pf: %u port: %u\n",
sip[i][j].tuple.src.l3num, ports[i]);
nf_conntrack_sip_fini();
return ret;
diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c
index 1fb3cacc04e1..0f1a45bcacb2 100644
--- a/net/netfilter/nf_conntrack_standalone.c
+++ b/net/netfilter/nf_conntrack_standalone.c
@@ -392,11 +392,18 @@ static const struct file_operations ct_cpu_seq_fops = {
static int nf_conntrack_standalone_init_proc(struct net *net)
{
struct proc_dir_entry *pde;
+ kuid_t root_uid;
+ kgid_t root_gid;
pde = proc_create("nf_conntrack", 0440, net->proc_net, &ct_file_ops);
if (!pde)
goto out_nf_conntrack;
+ root_uid = make_kuid(net->user_ns, 0);
+ root_gid = make_kgid(net->user_ns, 0);
+ if (uid_valid(root_uid) && gid_valid(root_gid))
+ proc_set_user(pde, root_uid, root_gid);
+
pde = proc_create("nf_conntrack", S_IRUGO, net->proc_net_stat,
&ct_cpu_seq_fops);
if (!pde)
diff --git a/net/netfilter/nf_conntrack_tftp.c b/net/netfilter/nf_conntrack_tftp.c
index e68ab4fbd71f..36f964066461 100644
--- a/net/netfilter/nf_conntrack_tftp.c
+++ b/net/netfilter/nf_conntrack_tftp.c
@@ -5,6 +5,8 @@
* published by the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/in.h>
@@ -138,9 +140,8 @@ static int __init nf_conntrack_tftp_init(void)
ret = nf_conntrack_helper_register(&tftp[i][j]);
if (ret) {
- printk(KERN_ERR "nf_ct_tftp: failed to register"
- " helper for pf: %u port: %u\n",
- tftp[i][j].tuple.src.l3num, ports[i]);
+ pr_err("failed to register helper for pf: %u port: %u\n",
+ tftp[i][j].tuple.src.l3num, ports[i]);
nf_conntrack_tftp_fini();
return ret;
}
diff --git a/net/netfilter/nf_conntrack_timeout.c b/net/netfilter/nf_conntrack_timeout.c
index 93da609d9d29..26e742006c48 100644
--- a/net/netfilter/nf_conntrack_timeout.c
+++ b/net/netfilter/nf_conntrack_timeout.c
@@ -25,7 +25,7 @@
#include <net/netfilter/nf_conntrack_timeout.h>
struct ctnl_timeout *
-(*nf_ct_timeout_find_get_hook)(const char *name) __read_mostly;
+(*nf_ct_timeout_find_get_hook)(struct net *net, const char *name) __read_mostly;
EXPORT_SYMBOL_GPL(nf_ct_timeout_find_get_hook);
void (*nf_ct_timeout_put_hook)(struct ctnl_timeout *timeout) __read_mostly;
diff --git a/net/netfilter/nf_dup_netdev.c b/net/netfilter/nf_dup_netdev.c
new file mode 100644
index 000000000000..8414ee1a0319
--- /dev/null
+++ b/net/netfilter/nf_dup_netdev.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+
+void nf_dup_netdev_egress(const struct nft_pktinfo *pkt, int oif)
+{
+ struct net_device *dev;
+ struct sk_buff *skb;
+
+ dev = dev_get_by_index_rcu(pkt->net, oif);
+ if (dev == NULL)
+ return;
+
+ skb = skb_clone(pkt->skb, GFP_ATOMIC);
+ if (skb == NULL)
+ return;
+
+ if (skb_mac_header_was_set(skb))
+ skb_push(skb, skb->mac_len);
+
+ skb->dev = dev;
+ skb_sender_cpu_clear(skb);
+ dev_queue_xmit(skb);
+}
+EXPORT_SYMBOL_GPL(nf_dup_netdev_egress);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 93cc4737018f..2011977cd79d 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -41,6 +41,8 @@ int nft_register_afinfo(struct net *net, struct nft_af_info *afi)
}
EXPORT_SYMBOL_GPL(nft_register_afinfo);
+static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi);
+
/**
* nft_unregister_afinfo - unregister nf_tables address family info
*
@@ -48,9 +50,10 @@ EXPORT_SYMBOL_GPL(nft_register_afinfo);
*
* Unregister the address family for use with nf_tables.
*/
-void nft_unregister_afinfo(struct nft_af_info *afi)
+void nft_unregister_afinfo(struct net *net, struct nft_af_info *afi)
{
nfnl_lock(NFNL_SUBSYS_NFTABLES);
+ __nft_release_afinfo(net, afi);
list_del_rcu(&afi->list);
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
}
@@ -89,6 +92,7 @@ nf_tables_afinfo_lookup(struct net *net, int family, bool autoload)
}
static void nft_ctx_init(struct nft_ctx *ctx,
+ struct net *net,
const struct sk_buff *skb,
const struct nlmsghdr *nlh,
struct nft_af_info *afi,
@@ -96,7 +100,7 @@ static void nft_ctx_init(struct nft_ctx *ctx,
struct nft_chain *chain,
const struct nlattr * const *nla)
{
- ctx->net = sock_net(skb->sk);
+ ctx->net = net;
ctx->afi = afi;
ctx->table = table;
ctx->chain = chain;
@@ -127,8 +131,8 @@ static void nft_trans_destroy(struct nft_trans *trans)
kfree(trans);
}
-int nft_register_basechain(struct nft_base_chain *basechain,
- unsigned int hook_nops)
+static int nft_register_basechain(struct nft_base_chain *basechain,
+ unsigned int hook_nops)
{
struct net *net = read_pnet(&basechain->pnet);
@@ -137,10 +141,9 @@ int nft_register_basechain(struct nft_base_chain *basechain,
return nf_register_net_hooks(net, basechain->ops, hook_nops);
}
-EXPORT_SYMBOL_GPL(nft_register_basechain);
-void nft_unregister_basechain(struct nft_base_chain *basechain,
- unsigned int hook_nops)
+static void nft_unregister_basechain(struct nft_base_chain *basechain,
+ unsigned int hook_nops)
{
struct net *net = read_pnet(&basechain->pnet);
@@ -149,7 +152,6 @@ void nft_unregister_basechain(struct nft_base_chain *basechain,
nf_unregister_net_hooks(net, basechain->ops, hook_nops);
}
-EXPORT_SYMBOL_GPL(nft_unregister_basechain);
static int nf_tables_register_hooks(const struct nft_table *table,
struct nft_chain *chain,
@@ -541,15 +543,14 @@ done:
return skb->len;
}
-static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_gettable(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
const struct nft_af_info *afi;
const struct nft_table *table;
struct sk_buff *skb2;
- struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
int err;
@@ -672,15 +673,14 @@ err:
return ret;
}
-static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_newtable(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
const struct nlattr *name;
struct nft_af_info *afi;
struct nft_table *table;
- struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
u32 flags = 0;
struct nft_ctx ctx;
@@ -706,7 +706,7 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
if (nlh->nlmsg_flags & NLM_F_REPLACE)
return -EOPNOTSUPP;
- nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
+ nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);
return nf_tables_updtable(&ctx);
}
@@ -730,7 +730,7 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
INIT_LIST_HEAD(&table->sets);
table->flags = flags;
- nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
+ nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);
err = nft_trans_table_add(&ctx, NFT_MSG_NEWTABLE);
if (err < 0)
goto err3;
@@ -810,18 +810,17 @@ out:
return err;
}
-static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_deltable(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
struct nft_af_info *afi;
struct nft_table *table;
- struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
struct nft_ctx ctx;
- nft_ctx_init(&ctx, skb, nlh, NULL, NULL, NULL, nla);
+ nft_ctx_init(&ctx, net, skb, nlh, NULL, NULL, NULL, nla);
if (family == AF_UNSPEC || nla[NFTA_TABLE_NAME] == NULL)
return nft_flush(&ctx, family);
@@ -832,8 +831,6 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
if (IS_ERR(table))
return PTR_ERR(table);
- if (table->flags & NFT_TABLE_INACTIVE)
- return -ENOENT;
ctx.afi = afi;
ctx.table = table;
@@ -1099,8 +1096,8 @@ done:
return skb->len;
}
-static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_getchain(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
@@ -1108,7 +1105,6 @@ static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
const struct nft_table *table;
const struct nft_chain *chain;
struct sk_buff *skb2;
- struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
int err;
@@ -1221,8 +1217,8 @@ static void nf_tables_chain_destroy(struct nft_chain *chain)
}
}
-static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_newchain(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
@@ -1232,7 +1228,6 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
struct nft_chain *chain;
struct nft_base_chain *basechain = NULL;
struct nlattr *ha[NFTA_HOOK_MAX + 1];
- struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
struct net_device *dev = NULL;
u8 policy = NF_ACCEPT;
@@ -1313,7 +1308,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
return PTR_ERR(stats);
}
- nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
+ nft_ctx_init(&ctx, net, skb, nlh, afi, table, chain, nla);
trans = nft_trans_alloc(&ctx, NFT_MSG_NEWCHAIN,
sizeof(struct nft_trans_chain));
if (trans == NULL) {
@@ -1461,7 +1456,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
if (err < 0)
goto err1;
- nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
+ nft_ctx_init(&ctx, net, skb, nlh, afi, table, chain, nla);
err = nft_trans_chain_add(&ctx, NFT_MSG_NEWCHAIN);
if (err < 0)
goto err2;
@@ -1476,15 +1471,14 @@ err1:
return err;
}
-static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_delchain(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
struct nft_af_info *afi;
struct nft_table *table;
struct nft_chain *chain;
- struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
struct nft_ctx ctx;
@@ -1495,18 +1489,14 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
if (IS_ERR(table))
return PTR_ERR(table);
- if (table->flags & NFT_TABLE_INACTIVE)
- return -ENOENT;
chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
if (IS_ERR(chain))
return PTR_ERR(chain);
- if (chain->flags & NFT_CHAIN_INACTIVE)
- return -ENOENT;
if (chain->use > 0)
return -EBUSY;
- nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
+ nft_ctx_init(&ctx, net, skb, nlh, afi, table, chain, nla);
return nft_delchain(&ctx);
}
@@ -1931,8 +1921,8 @@ done:
return skb->len;
}
-static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_getrule(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
@@ -1941,7 +1931,6 @@ static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb,
const struct nft_chain *chain;
const struct nft_rule *rule;
struct sk_buff *skb2;
- struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
int err;
@@ -2010,13 +1999,12 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
static struct nft_expr_info *info;
-static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_newrule(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
struct nft_af_info *afi;
- struct net *net = sock_net(skb->sk);
struct nft_table *table;
struct nft_chain *chain;
struct nft_rule *rule, *old_rule = NULL;
@@ -2075,7 +2063,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
return PTR_ERR(old_rule);
}
- nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
+ nft_ctx_init(&ctx, net, skb, nlh, afi, table, chain, nla);
n = 0;
size = 0;
@@ -2176,13 +2164,12 @@ err1:
return err;
}
-static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_delrule(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
struct nft_af_info *afi;
- struct net *net = sock_net(skb->sk);
struct nft_table *table;
struct nft_chain *chain = NULL;
struct nft_rule *rule;
@@ -2196,8 +2183,6 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
if (IS_ERR(table))
return PTR_ERR(table);
- if (table->flags & NFT_TABLE_INACTIVE)
- return -ENOENT;
if (nla[NFTA_RULE_CHAIN]) {
chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
@@ -2205,7 +2190,7 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
return PTR_ERR(chain);
}
- nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
+ nft_ctx_init(&ctx, net, skb, nlh, afi, table, chain, nla);
if (chain) {
if (nla[NFTA_RULE_HANDLE]) {
@@ -2338,18 +2323,19 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
[NFTA_SET_ID] = { .type = NLA_U32 },
[NFTA_SET_TIMEOUT] = { .type = NLA_U64 },
[NFTA_SET_GC_INTERVAL] = { .type = NLA_U32 },
+ [NFTA_SET_USERDATA] = { .type = NLA_BINARY,
+ .len = NFT_USERDATA_MAXLEN },
};
static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = {
[NFTA_SET_DESC_SIZE] = { .type = NLA_U32 },
};
-static int nft_ctx_init_from_setattr(struct nft_ctx *ctx,
+static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, struct net *net,
const struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
- struct net *net = sock_net(skb->sk);
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
struct nft_af_info *afi = NULL;
struct nft_table *table = NULL;
@@ -2367,11 +2353,9 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx,
table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]);
if (IS_ERR(table))
return PTR_ERR(table);
- if (table->flags & NFT_TABLE_INACTIVE)
- return -ENOENT;
}
- nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
+ nft_ctx_init(ctx, net, skb, nlh, afi, table, NULL, nla);
return 0;
}
@@ -2500,6 +2484,9 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
goto nla_put_failure;
}
+ if (nla_put(skb, NFTA_SET_USERDATA, set->udlen, set->udata))
+ goto nla_put_failure;
+
desc = nla_nest_start(skb, NFTA_SET_DESC);
if (desc == NULL)
goto nla_put_failure;
@@ -2619,8 +2606,8 @@ static int nf_tables_dump_sets_done(struct netlink_callback *cb)
return 0;
}
-static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_getset(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
const struct nft_set *set;
@@ -2630,7 +2617,7 @@ static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
int err;
/* Verify existence before starting dump */
- err = nft_ctx_init_from_setattr(&ctx, skb, nlh, nla);
+ err = nft_ctx_init_from_setattr(&ctx, net, skb, nlh, nla);
if (err < 0)
return err;
@@ -2693,14 +2680,13 @@ static int nf_tables_set_desc_parse(const struct nft_ctx *ctx,
return 0;
}
-static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_newset(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
const struct nft_set_ops *ops;
struct nft_af_info *afi;
- struct net *net = sock_net(skb->sk);
struct nft_table *table;
struct nft_set *set;
struct nft_ctx ctx;
@@ -2710,6 +2696,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
u64 timeout;
u32 ktype, dtype, flags, policy, gc_int;
struct nft_set_desc desc;
+ unsigned char *udata;
+ u16 udlen;
int err;
if (nla[NFTA_SET_TABLE] == NULL ||
@@ -2798,7 +2786,7 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
if (IS_ERR(table))
return PTR_ERR(table);
- nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
+ nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);
set = nf_tables_set_lookup(table, nla[NFTA_SET_NAME]);
if (IS_ERR(set)) {
@@ -2822,12 +2810,16 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
if (IS_ERR(ops))
return PTR_ERR(ops);
+ udlen = 0;
+ if (nla[NFTA_SET_USERDATA])
+ udlen = nla_len(nla[NFTA_SET_USERDATA]);
+
size = 0;
if (ops->privsize != NULL)
size = ops->privsize(nla);
err = -ENOMEM;
- set = kzalloc(sizeof(*set) + size, GFP_KERNEL);
+ set = kzalloc(sizeof(*set) + size + udlen, GFP_KERNEL);
if (set == NULL)
goto err1;
@@ -2836,6 +2828,12 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
if (err < 0)
goto err2;
+ udata = NULL;
+ if (udlen) {
+ udata = set->data + size;
+ nla_memcpy(udata, nla[NFTA_SET_USERDATA], udlen);
+ }
+
INIT_LIST_HEAD(&set->bindings);
write_pnet(&set->pnet, net);
set->ops = ops;
@@ -2846,6 +2844,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
set->flags = flags;
set->size = desc.size;
set->policy = policy;
+ set->udlen = udlen;
+ set->udata = udata;
set->timeout = timeout;
set->gc_int = gc_int;
@@ -2882,8 +2882,8 @@ static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set
nft_set_destroy(set);
}
-static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_delset(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
@@ -2896,15 +2896,13 @@ static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
if (nla[NFTA_SET_TABLE] == NULL)
return -EINVAL;
- err = nft_ctx_init_from_setattr(&ctx, skb, nlh, nla);
+ err = nft_ctx_init_from_setattr(&ctx, net, skb, nlh, nla);
if (err < 0)
return err;
set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
if (IS_ERR(set))
return PTR_ERR(set);
- if (set->flags & NFT_SET_INACTIVE)
- return -ENOENT;
if (!list_empty(&set->bindings))
return -EBUSY;
@@ -3024,16 +3022,14 @@ static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX +
[NFTA_SET_ELEM_LIST_SET_ID] = { .type = NLA_U32 },
};
-static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx,
+static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, struct net *net,
const struct sk_buff *skb,
const struct nlmsghdr *nlh,
- const struct nlattr * const nla[],
- bool trans)
+ const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
struct nft_af_info *afi;
struct nft_table *table;
- struct net *net = sock_net(skb->sk);
afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false);
if (IS_ERR(afi))
@@ -3042,10 +3038,8 @@ static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx,
table = nf_tables_table_lookup(afi, nla[NFTA_SET_ELEM_LIST_TABLE]);
if (IS_ERR(table))
return PTR_ERR(table);
- if (!trans && (table->flags & NFT_TABLE_INACTIVE))
- return -ENOENT;
- nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
+ nft_ctx_init(ctx, net, skb, nlh, afi, table, NULL, nla);
return 0;
}
@@ -3135,6 +3129,7 @@ static int nf_tables_dump_setelem(const struct nft_ctx *ctx,
static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
{
+ struct net *net = sock_net(skb->sk);
const struct nft_set *set;
struct nft_set_dump_args args;
struct nft_ctx ctx;
@@ -3150,10 +3145,12 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
if (err < 0)
return err;
- err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla,
- false);
+ err = nft_ctx_init_from_elemattr(&ctx, net, cb->skb, cb->nlh,
+ (void *)nla);
if (err < 0)
return err;
+ if (ctx.table->flags & NFT_TABLE_INACTIVE)
+ return -ENOENT;
set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
if (IS_ERR(set))
@@ -3208,17 +3205,19 @@ nla_put_failure:
return -ENOSPC;
}
-static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_getsetelem(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
const struct nft_set *set;
struct nft_ctx ctx;
int err;
- err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false);
+ err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla);
if (err < 0)
return err;
+ if (ctx.table->flags & NFT_TABLE_INACTIVE)
+ return -ENOENT;
set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
if (IS_ERR(set))
@@ -3528,11 +3527,10 @@ err1:
return err;
}
-static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_newsetelem(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
- struct net *net = sock_net(skb->sk);
const struct nlattr *attr;
struct nft_set *set;
struct nft_ctx ctx;
@@ -3541,7 +3539,7 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb,
if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL)
return -EINVAL;
- err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, true);
+ err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla);
if (err < 0)
return err;
@@ -3623,8 +3621,8 @@ err1:
return err;
}
-static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_delsetelem(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
const struct nlattr *attr;
@@ -3635,7 +3633,7 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL)
return -EINVAL;
- err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false);
+ err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla);
if (err < 0)
return err;
@@ -3739,11 +3737,10 @@ err:
return err;
}
-static int nf_tables_getgen(struct sock *nlsk, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int nf_tables_getgen(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[])
{
- struct net *net = sock_net(skb->sk);
struct sk_buff *skb2;
int err;
@@ -3887,9 +3884,8 @@ static void nf_tables_commit_release(struct nft_trans *trans)
kfree(trans);
}
-static int nf_tables_commit(struct sk_buff *skb)
+static int nf_tables_commit(struct net *net, struct sk_buff *skb)
{
- struct net *net = sock_net(skb->sk);
struct nft_trans *trans, *next;
struct nft_trans_elem *te;
@@ -4024,13 +4020,13 @@ static void nf_tables_abort_release(struct nft_trans *trans)
kfree(trans);
}
-static int nf_tables_abort(struct sk_buff *skb)
+static int nf_tables_abort(struct net *net, struct sk_buff *skb)
{
- struct net *net = sock_net(skb->sk);
struct nft_trans *trans, *next;
struct nft_trans_elem *te;
- list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
+ list_for_each_entry_safe_reverse(trans, next, &net->nft.commit_list,
+ list) {
switch (trans->msg_type) {
case NFT_MSG_NEWTABLE:
if (nft_trans_table_update(trans)) {
@@ -4446,22 +4442,22 @@ static void nft_verdict_uninit(const struct nft_data *data)
}
}
-static int nft_verdict_dump(struct sk_buff *skb, const struct nft_data *data)
+int nft_verdict_dump(struct sk_buff *skb, int type, const struct nft_verdict *v)
{
struct nlattr *nest;
- nest = nla_nest_start(skb, NFTA_DATA_VERDICT);
+ nest = nla_nest_start(skb, type);
if (!nest)
goto nla_put_failure;
- if (nla_put_be32(skb, NFTA_VERDICT_CODE, htonl(data->verdict.code)))
+ if (nla_put_be32(skb, NFTA_VERDICT_CODE, htonl(v->code)))
goto nla_put_failure;
- switch (data->verdict.code) {
+ switch (v->code) {
case NFT_JUMP:
case NFT_GOTO:
if (nla_put_string(skb, NFTA_VERDICT_CHAIN,
- data->verdict.chain->name))
+ v->chain->name))
goto nla_put_failure;
}
nla_nest_end(skb, nest);
@@ -4572,7 +4568,7 @@ int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data,
err = nft_value_dump(skb, data, len);
break;
case NFT_DATA_VERDICT:
- err = nft_verdict_dump(skb, data);
+ err = nft_verdict_dump(skb, NFTA_DATA_VERDICT, &data->verdict);
break;
default:
err = -EINVAL;
@@ -4584,7 +4580,7 @@ int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data,
}
EXPORT_SYMBOL_GPL(nft_data_dump);
-static int nf_tables_init_net(struct net *net)
+static int __net_init nf_tables_init_net(struct net *net)
{
INIT_LIST_HEAD(&net->nft.af_info);
INIT_LIST_HEAD(&net->nft.commit_list);
@@ -4592,6 +4588,67 @@ static int nf_tables_init_net(struct net *net)
return 0;
}
+int __nft_release_basechain(struct nft_ctx *ctx)
+{
+ struct nft_rule *rule, *nr;
+
+ BUG_ON(!(ctx->chain->flags & NFT_BASE_CHAIN));
+
+ nf_tables_unregister_hooks(ctx->chain->table, ctx->chain,
+ ctx->afi->nops);
+ list_for_each_entry_safe(rule, nr, &ctx->chain->rules, list) {
+ list_del(&rule->list);
+ ctx->chain->use--;
+ nf_tables_rule_destroy(ctx, rule);
+ }
+ list_del(&ctx->chain->list);
+ ctx->table->use--;
+ nf_tables_chain_destroy(ctx->chain);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__nft_release_basechain);
+
+/* Called by nft_unregister_afinfo() from __net_exit path, nfnl_lock is held. */
+static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi)
+{
+ struct nft_table *table, *nt;
+ struct nft_chain *chain, *nc;
+ struct nft_rule *rule, *nr;
+ struct nft_set *set, *ns;
+ struct nft_ctx ctx = {
+ .net = net,
+ .afi = afi,
+ };
+
+ list_for_each_entry_safe(table, nt, &afi->tables, list) {
+ list_for_each_entry(chain, &table->chains, list)
+ nf_tables_unregister_hooks(table, chain, afi->nops);
+ /* No packets are walking on these chains anymore. */
+ ctx.table = table;
+ list_for_each_entry(chain, &table->chains, list) {
+ ctx.chain = chain;
+ list_for_each_entry_safe(rule, nr, &chain->rules, list) {
+ list_del(&rule->list);
+ chain->use--;
+ nf_tables_rule_destroy(&ctx, rule);
+ }
+ }
+ list_for_each_entry_safe(set, ns, &table->sets, list) {
+ list_del(&set->list);
+ table->use--;
+ nft_set_destroy(set);
+ }
+ list_for_each_entry_safe(chain, nc, &table->chains, list) {
+ list_del(&chain->list);
+ table->use--;
+ nf_tables_chain_destroy(chain);
+ }
+ list_del(&table->list);
+ nf_tables_table_destroy(&ctx);
+ }
+}
+
static struct pernet_operations nf_tables_net_ops = {
.init = nf_tables_init_net,
};
diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c
index f3695a497408..e9f8dffcc244 100644
--- a/net/netfilter/nf_tables_core.c
+++ b/net/netfilter/nf_tables_core.c
@@ -16,22 +16,17 @@
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
+#include <linux/static_key.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_log.h>
-enum nft_trace {
- NFT_TRACE_RULE,
- NFT_TRACE_RETURN,
- NFT_TRACE_POLICY,
-};
-
-static const char *const comments[] = {
- [NFT_TRACE_RULE] = "rule",
- [NFT_TRACE_RETURN] = "return",
- [NFT_TRACE_POLICY] = "policy",
+static const char *const comments[__NFT_TRACETYPE_MAX] = {
+ [NFT_TRACETYPE_POLICY] = "policy",
+ [NFT_TRACETYPE_RETURN] = "return",
+ [NFT_TRACETYPE_RULE] = "rule",
};
static struct nf_loginfo trace_loginfo = {
@@ -44,22 +39,36 @@ static struct nf_loginfo trace_loginfo = {
},
};
-static void __nft_trace_packet(const struct nft_pktinfo *pkt,
- const struct nft_chain *chain,
- int rulenum, enum nft_trace type)
+static noinline void __nft_trace_packet(struct nft_traceinfo *info,
+ const struct nft_chain *chain,
+ int rulenum, enum nft_trace_types type)
{
+ const struct nft_pktinfo *pkt = info->pkt;
+
+ if (!info->trace || !pkt->skb->nf_trace)
+ return;
+
+ info->chain = chain;
+ info->type = type;
+
+ nft_trace_notify(info);
+
nf_log_trace(pkt->net, pkt->pf, pkt->hook, pkt->skb, pkt->in,
pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ",
chain->table->name, chain->name, comments[type],
rulenum);
}
-static inline void nft_trace_packet(const struct nft_pktinfo *pkt,
+static inline void nft_trace_packet(struct nft_traceinfo *info,
const struct nft_chain *chain,
- int rulenum, enum nft_trace type)
+ const struct nft_rule *rule,
+ int rulenum,
+ enum nft_trace_types type)
{
- if (unlikely(pkt->skb->nf_trace))
- __nft_trace_packet(pkt, chain, rulenum, type);
+ if (static_branch_unlikely(&nft_trace_enabled)) {
+ info->rule = rule;
+ __nft_trace_packet(info, chain, rulenum, type);
+ }
}
static void nft_cmp_fast_eval(const struct nft_expr *expr,
@@ -121,7 +130,11 @@ nft_do_chain(struct nft_pktinfo *pkt, void *priv)
struct nft_stats *stats;
int rulenum;
unsigned int gencursor = nft_genmask_cur(net);
+ struct nft_traceinfo info;
+ info.trace = false;
+ if (static_branch_unlikely(&nft_trace_enabled))
+ nft_trace_init(&info, pkt, &regs.verdict, basechain);
do_chain:
rulenum = 0;
rule = list_entry(&chain->rules, struct nft_rule, list);
@@ -151,7 +164,8 @@ next_rule:
regs.verdict.code = NFT_CONTINUE;
continue;
case NFT_CONTINUE:
- nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE);
+ nft_trace_packet(&info, chain, rule,
+ rulenum, NFT_TRACETYPE_RULE);
continue;
}
break;
@@ -161,7 +175,8 @@ next_rule:
case NF_ACCEPT:
case NF_DROP:
case NF_QUEUE:
- nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE);
+ nft_trace_packet(&info, chain, rule,
+ rulenum, NFT_TRACETYPE_RULE);
return regs.verdict.code;
}
@@ -174,7 +189,8 @@ next_rule:
stackptr++;
/* fall through */
case NFT_GOTO:
- nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE);
+ nft_trace_packet(&info, chain, rule,
+ rulenum, NFT_TRACETYPE_RULE);
chain = regs.verdict.chain;
goto do_chain;
@@ -182,7 +198,8 @@ next_rule:
rulenum++;
/* fall through */
case NFT_RETURN:
- nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RETURN);
+ nft_trace_packet(&info, chain, rule,
+ rulenum, NFT_TRACETYPE_RETURN);
break;
default:
WARN_ON(1);
@@ -196,7 +213,8 @@ next_rule:
goto next_rule;
}
- nft_trace_packet(pkt, basechain, -1, NFT_TRACE_POLICY);
+ nft_trace_packet(&info, basechain, NULL, -1,
+ NFT_TRACETYPE_POLICY);
rcu_read_lock_bh();
stats = this_cpu_ptr(rcu_dereference(nft_base_chain(basechain)->stats));
diff --git a/net/netfilter/nf_tables_inet.c b/net/netfilter/nf_tables_inet.c
index 9dd2d216cfc1..6b5f76295d3d 100644
--- a/net/netfilter/nf_tables_inet.c
+++ b/net/netfilter/nf_tables_inet.c
@@ -57,7 +57,7 @@ err:
static void __net_exit nf_tables_inet_exit_net(struct net *net)
{
- nft_unregister_afinfo(net->nft.inet);
+ nft_unregister_afinfo(net, net->nft.inet);
kfree(net->nft.inet);
}
diff --git a/net/netfilter/nf_tables_netdev.c b/net/netfilter/nf_tables_netdev.c
index 7b9c053ba750..b6605e000801 100644
--- a/net/netfilter/nf_tables_netdev.c
+++ b/net/netfilter/nf_tables_netdev.c
@@ -94,7 +94,7 @@ nft_do_chain_netdev(void *priv, struct sk_buff *skb,
{
struct nft_pktinfo pkt;
- switch (eth_hdr(skb)->h_proto) {
+ switch (skb->protocol) {
case htons(ETH_P_IP):
nft_netdev_set_pktinfo_ipv4(&pkt, skb, state);
break;
@@ -139,7 +139,7 @@ err:
static void nf_tables_netdev_exit_net(struct net *net)
{
- nft_unregister_afinfo(net->nft.netdev);
+ nft_unregister_afinfo(net, net->nft.netdev);
kfree(net->nft.netdev);
}
@@ -156,35 +156,17 @@ static const struct nf_chain_type nft_filter_chain_netdev = {
.hook_mask = (1 << NF_NETDEV_INGRESS),
};
-static void nft_netdev_event(unsigned long event, struct nft_af_info *afi,
- struct net_device *dev, struct nft_table *table,
- struct nft_base_chain *basechain)
+static void nft_netdev_event(unsigned long event, struct net_device *dev,
+ struct nft_ctx *ctx)
{
- switch (event) {
- case NETDEV_REGISTER:
- if (strcmp(basechain->dev_name, dev->name) != 0)
- return;
+ struct nft_base_chain *basechain = nft_base_chain(ctx->chain);
- BUG_ON(!(basechain->flags & NFT_BASECHAIN_DISABLED));
-
- dev_hold(dev);
- basechain->ops[0].dev = dev;
- basechain->flags &= ~NFT_BASECHAIN_DISABLED;
- if (!(table->flags & NFT_TABLE_F_DORMANT))
- nft_register_basechain(basechain, afi->nops);
- break;
+ switch (event) {
case NETDEV_UNREGISTER:
if (strcmp(basechain->dev_name, dev->name) != 0)
return;
- BUG_ON(basechain->flags & NFT_BASECHAIN_DISABLED);
-
- if (!(table->flags & NFT_TABLE_F_DORMANT))
- nft_unregister_basechain(basechain, afi->nops);
-
- dev_put(basechain->ops[0].dev);
- basechain->ops[0].dev = NULL;
- basechain->flags |= NFT_BASECHAIN_DISABLED;
+ __nft_release_basechain(ctx);
break;
case NETDEV_CHANGENAME:
if (dev->ifindex != basechain->ops[0].dev->ifindex)
@@ -201,20 +183,29 @@ static int nf_tables_netdev_event(struct notifier_block *this,
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct nft_af_info *afi;
struct nft_table *table;
- struct nft_chain *chain;
+ struct nft_chain *chain, *nr;
+ struct nft_ctx ctx = {
+ .net = dev_net(dev),
+ };
+
+ if (event != NETDEV_UNREGISTER &&
+ event != NETDEV_CHANGENAME)
+ return NOTIFY_DONE;
nfnl_lock(NFNL_SUBSYS_NFTABLES);
list_for_each_entry(afi, &dev_net(dev)->nft.af_info, list) {
+ ctx.afi = afi;
if (afi->family != NFPROTO_NETDEV)
continue;
list_for_each_entry(table, &afi->tables, list) {
- list_for_each_entry(chain, &table->chains, list) {
+ ctx.table = table;
+ list_for_each_entry_safe(chain, nr, &table->chains, list) {
if (!(chain->flags & NFT_BASE_CHAIN))
continue;
- nft_netdev_event(event, afi, dev, table,
- nft_base_chain(chain));
+ ctx.chain = chain;
+ nft_netdev_event(event, dev, &ctx);
}
}
}
diff --git a/net/netfilter/nf_tables_trace.c b/net/netfilter/nf_tables_trace.c
new file mode 100644
index 000000000000..e9e959f65d91
--- /dev/null
+++ b/net/netfilter/nf_tables_trace.c
@@ -0,0 +1,275 @@
+/*
+ * (C) 2015 Red Hat GmbH
+ * Author: Florian Westphal <fw@strlen.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/static_key.h>
+#include <linux/hash.h>
+#include <linux/jhash.h>
+#include <linux/if_vlan.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_core.h>
+#include <net/netfilter/nf_tables.h>
+
+#define NFT_TRACETYPE_LL_HSIZE 20
+#define NFT_TRACETYPE_NETWORK_HSIZE 40
+#define NFT_TRACETYPE_TRANSPORT_HSIZE 20
+
+DEFINE_STATIC_KEY_FALSE(nft_trace_enabled);
+EXPORT_SYMBOL_GPL(nft_trace_enabled);
+
+static int trace_fill_id(struct sk_buff *nlskb, struct sk_buff *skb)
+{
+ __be32 id;
+
+ /* using skb address as ID results in a limited number of
+ * values (and quick reuse).
+ *
+ * So we attempt to use as many skb members that will not
+ * change while skb is with netfilter.
+ */
+ id = (__be32)jhash_2words(hash32_ptr(skb), skb_get_hash(skb),
+ skb->skb_iif);
+
+ return nla_put_be32(nlskb, NFTA_TRACE_ID, id);
+}
+
+static int trace_fill_header(struct sk_buff *nlskb, u16 type,
+ const struct sk_buff *skb,
+ int off, unsigned int len)
+{
+ struct nlattr *nla;
+
+ if (len == 0)
+ return 0;
+
+ nla = nla_reserve(nlskb, type, len);
+ if (!nla || skb_copy_bits(skb, off, nla_data(nla), len))
+ return -1;
+
+ return 0;
+}
+
+static int nf_trace_fill_ll_header(struct sk_buff *nlskb,
+ const struct sk_buff *skb)
+{
+ struct vlan_ethhdr veth;
+ int off;
+
+ BUILD_BUG_ON(sizeof(veth) > NFT_TRACETYPE_LL_HSIZE);
+
+ off = skb_mac_header(skb) - skb->data;
+ if (off != -ETH_HLEN)
+ return -1;
+
+ if (skb_copy_bits(skb, off, &veth, ETH_HLEN))
+ return -1;
+
+ veth.h_vlan_proto = skb->vlan_proto;
+ veth.h_vlan_TCI = htons(skb_vlan_tag_get(skb));
+ veth.h_vlan_encapsulated_proto = skb->protocol;
+
+ return nla_put(nlskb, NFTA_TRACE_LL_HEADER, sizeof(veth), &veth);
+}
+
+static int nf_trace_fill_dev_info(struct sk_buff *nlskb,
+ const struct net_device *indev,
+ const struct net_device *outdev)
+{
+ if (indev) {
+ if (nla_put_be32(nlskb, NFTA_TRACE_IIF,
+ htonl(indev->ifindex)))
+ return -1;
+
+ if (nla_put_be16(nlskb, NFTA_TRACE_IIFTYPE,
+ htons(indev->type)))
+ return -1;
+ }
+
+ if (outdev) {
+ if (nla_put_be32(nlskb, NFTA_TRACE_OIF,
+ htonl(outdev->ifindex)))
+ return -1;
+
+ if (nla_put_be16(nlskb, NFTA_TRACE_OIFTYPE,
+ htons(outdev->type)))
+ return -1;
+ }
+
+ return 0;
+}
+
+static int nf_trace_fill_pkt_info(struct sk_buff *nlskb,
+ const struct nft_pktinfo *pkt)
+{
+ const struct sk_buff *skb = pkt->skb;
+ unsigned int len = min_t(unsigned int,
+ pkt->xt.thoff - skb_network_offset(skb),
+ NFT_TRACETYPE_NETWORK_HSIZE);
+ int off = skb_network_offset(skb);
+
+ if (trace_fill_header(nlskb, NFTA_TRACE_NETWORK_HEADER, skb, off, len))
+ return -1;
+
+ len = min_t(unsigned int, skb->len - pkt->xt.thoff,
+ NFT_TRACETYPE_TRANSPORT_HSIZE);
+
+ if (trace_fill_header(nlskb, NFTA_TRACE_TRANSPORT_HEADER, skb,
+ pkt->xt.thoff, len))
+ return -1;
+
+ if (!skb_mac_header_was_set(skb))
+ return 0;
+
+ if (skb_vlan_tag_get(skb))
+ return nf_trace_fill_ll_header(nlskb, skb);
+
+ off = skb_mac_header(skb) - skb->data;
+ len = min_t(unsigned int, -off, NFT_TRACETYPE_LL_HSIZE);
+ return trace_fill_header(nlskb, NFTA_TRACE_LL_HEADER,
+ skb, off, len);
+}
+
+static int nf_trace_fill_rule_info(struct sk_buff *nlskb,
+ const struct nft_traceinfo *info)
+{
+ if (!info->rule)
+ return 0;
+
+ /* a continue verdict with ->type == RETURN means that this is
+ * an implicit return (end of chain reached).
+ *
+ * Since no rule matched, the ->rule pointer is invalid.
+ */
+ if (info->type == NFT_TRACETYPE_RETURN &&
+ info->verdict->code == NFT_CONTINUE)
+ return 0;
+
+ return nla_put_be64(nlskb, NFTA_TRACE_RULE_HANDLE,
+ cpu_to_be64(info->rule->handle));
+}
+
+void nft_trace_notify(struct nft_traceinfo *info)
+{
+ const struct nft_pktinfo *pkt = info->pkt;
+ struct nfgenmsg *nfmsg;
+ struct nlmsghdr *nlh;
+ struct sk_buff *skb;
+ unsigned int size;
+ int event = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_TRACE;
+
+ if (!nfnetlink_has_listeners(pkt->net, NFNLGRP_NFTRACE))
+ return;
+
+ size = nlmsg_total_size(sizeof(struct nfgenmsg)) +
+ nla_total_size(NFT_TABLE_MAXNAMELEN) +
+ nla_total_size(NFT_CHAIN_MAXNAMELEN) +
+ nla_total_size(sizeof(__be64)) + /* rule handle */
+ nla_total_size(sizeof(__be32)) + /* trace type */
+ nla_total_size(0) + /* VERDICT, nested */
+ nla_total_size(sizeof(u32)) + /* verdict code */
+ nla_total_size(NFT_CHAIN_MAXNAMELEN) + /* jump target */
+ nla_total_size(sizeof(u32)) + /* id */
+ nla_total_size(NFT_TRACETYPE_LL_HSIZE) +
+ nla_total_size(NFT_TRACETYPE_NETWORK_HSIZE) +
+ nla_total_size(NFT_TRACETYPE_TRANSPORT_HSIZE) +
+ nla_total_size(sizeof(u32)) + /* iif */
+ nla_total_size(sizeof(__be16)) + /* iiftype */
+ nla_total_size(sizeof(u32)) + /* oif */
+ nla_total_size(sizeof(__be16)) + /* oiftype */
+ nla_total_size(sizeof(u32)) + /* mark */
+ nla_total_size(sizeof(u32)) + /* nfproto */
+ nla_total_size(sizeof(u32)); /* policy */
+
+ skb = nlmsg_new(size, GFP_ATOMIC);
+ if (!skb)
+ return;
+
+ nlh = nlmsg_put(skb, 0, 0, event, sizeof(struct nfgenmsg), 0);
+ if (!nlh)
+ goto nla_put_failure;
+
+ nfmsg = nlmsg_data(nlh);
+ nfmsg->nfgen_family = info->basechain->type->family;
+ nfmsg->version = NFNETLINK_V0;
+ nfmsg->res_id = 0;
+
+ if (nla_put_be32(skb, NFTA_TRACE_NFPROTO, htonl(pkt->pf)))
+ goto nla_put_failure;
+
+ if (nla_put_be32(skb, NFTA_TRACE_TYPE, htonl(info->type)))
+ goto nla_put_failure;
+
+ if (trace_fill_id(skb, pkt->skb))
+ goto nla_put_failure;
+
+ if (info->chain) {
+ if (nla_put_string(skb, NFTA_TRACE_CHAIN,
+ info->chain->name))
+ goto nla_put_failure;
+ if (nla_put_string(skb, NFTA_TRACE_TABLE,
+ info->chain->table->name))
+ goto nla_put_failure;
+ }
+
+ if (nf_trace_fill_rule_info(skb, info))
+ goto nla_put_failure;
+
+ switch (info->type) {
+ case NFT_TRACETYPE_UNSPEC:
+ case __NFT_TRACETYPE_MAX:
+ break;
+ case NFT_TRACETYPE_RETURN:
+ case NFT_TRACETYPE_RULE:
+ if (nft_verdict_dump(skb, NFTA_TRACE_VERDICT, info->verdict))
+ goto nla_put_failure;
+ break;
+ case NFT_TRACETYPE_POLICY:
+ if (nla_put_be32(skb, NFTA_TRACE_POLICY,
+ info->basechain->policy))
+ goto nla_put_failure;
+ break;
+ }
+
+ if (pkt->skb->mark &&
+ nla_put_be32(skb, NFTA_TRACE_MARK, htonl(pkt->skb->mark)))
+ goto nla_put_failure;
+
+ if (!info->packet_dumped) {
+ if (nf_trace_fill_dev_info(skb, pkt->in, pkt->out))
+ goto nla_put_failure;
+
+ if (nf_trace_fill_pkt_info(skb, pkt))
+ goto nla_put_failure;
+ info->packet_dumped = true;
+ }
+
+ nlmsg_end(skb, nlh);
+ nfnetlink_send(skb, pkt->net, 0, NFNLGRP_NFTRACE, 0, GFP_ATOMIC);
+ return;
+
+ nla_put_failure:
+ WARN_ON_ONCE(1);
+ kfree_skb(skb);
+}
+
+void nft_trace_init(struct nft_traceinfo *info, const struct nft_pktinfo *pkt,
+ const struct nft_verdict *verdict,
+ const struct nft_chain *chain)
+{
+ info->basechain = nft_base_chain(chain);
+ info->trace = true;
+ info->packet_dumped = false;
+ info->pkt = pkt;
+ info->verdict = verdict;
+}
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c
index 46453ab318db..a7ba23353dab 100644
--- a/net/netfilter/nfnetlink.c
+++ b/net/netfilter/nfnetlink.c
@@ -33,6 +33,10 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER);
+#define nfnl_dereference_protected(id) \
+ rcu_dereference_protected(table[(id)].subsys, \
+ lockdep_nfnl_is_held((id)))
+
static char __initdata nfversion[] = "0.30";
static struct {
@@ -49,6 +53,7 @@ static const int nfnl_group2type[NFNLGRP_MAX+1] = {
[NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP,
[NFNLGRP_NFTABLES] = NFNL_SUBSYS_NFTABLES,
[NFNLGRP_ACCT_QUOTA] = NFNL_SUBSYS_ACCT,
+ [NFNLGRP_NFTRACE] = NFNL_SUBSYS_NFTABLES,
};
void nfnl_lock(__u8 subsys_id)
@@ -201,19 +206,18 @@ replay:
}
if (nc->call_rcu) {
- err = nc->call_rcu(net->nfnl, skb, nlh,
+ err = nc->call_rcu(net, net->nfnl, skb, nlh,
(const struct nlattr **)cda);
rcu_read_unlock();
} else {
rcu_read_unlock();
nfnl_lock(subsys_id);
- if (rcu_dereference_protected(table[subsys_id].subsys,
- lockdep_is_held(&table[subsys_id].mutex)) != ss ||
+ if (nfnl_dereference_protected(subsys_id) != ss ||
nfnetlink_find_client(type, ss) != nc)
err = -EAGAIN;
else if (nc->call)
- err = nc->call(net->nfnl, skb, nlh,
- (const struct nlattr **)cda);
+ err = nc->call(net, net->nfnl, skb, nlh,
+ (const struct nlattr **)cda);
else
err = -EINVAL;
nfnl_unlock(subsys_id);
@@ -295,18 +299,14 @@ replay:
if (!skb)
return netlink_ack(oskb, nlh, -ENOMEM);
- skb->sk = oskb->sk;
-
nfnl_lock(subsys_id);
- ss = rcu_dereference_protected(table[subsys_id].subsys,
- lockdep_is_held(&table[subsys_id].mutex));
+ ss = nfnl_dereference_protected(subsys_id);
if (!ss) {
#ifdef CONFIG_MODULES
nfnl_unlock(subsys_id);
request_module("nfnetlink-subsys-%d", subsys_id);
nfnl_lock(subsys_id);
- ss = rcu_dereference_protected(table[subsys_id].subsys,
- lockdep_is_held(&table[subsys_id].mutex));
+ ss = nfnl_dereference_protected(subsys_id);
if (!ss)
#endif
{
@@ -381,7 +381,7 @@ replay:
goto ack;
if (nc->call_batch) {
- err = nc->call_batch(net->nfnl, skb, nlh,
+ err = nc->call_batch(net, net->nfnl, skb, nlh,
(const struct nlattr **)cda);
}
@@ -425,15 +425,15 @@ next:
}
done:
if (status & NFNL_BATCH_REPLAY) {
- ss->abort(oskb);
+ ss->abort(net, oskb);
nfnl_err_reset(&err_list);
nfnl_unlock(subsys_id);
kfree_skb(skb);
goto replay;
} else if (status == NFNL_BATCH_DONE) {
- ss->commit(oskb);
+ ss->commit(net, oskb);
} else {
- ss->abort(oskb);
+ ss->abort(net, oskb);
}
nfnl_err_deliver(&err_list, oskb);
diff --git a/net/netfilter/nfnetlink_acct.c b/net/netfilter/nfnetlink_acct.c
index fefbf5f0b28d..5274b04c42a6 100644
--- a/net/netfilter/nfnetlink_acct.c
+++ b/net/netfilter/nfnetlink_acct.c
@@ -46,12 +46,11 @@ struct nfacct_filter {
#define NFACCT_F_QUOTA (NFACCT_F_QUOTA_PKTS | NFACCT_F_QUOTA_BYTES)
#define NFACCT_OVERQUOTA_BIT 2 /* NFACCT_F_OVERQUOTA */
-static int
-nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh, const struct nlattr * const tb[])
+static int nfnl_acct_new(struct net *net, struct sock *nfnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const tb[])
{
struct nf_acct *nfacct, *matching = NULL;
- struct net *net = sock_net(nfnl);
char *acct_name;
unsigned int size = 0;
u32 flags = 0;
@@ -253,11 +252,10 @@ nfacct_filter_alloc(const struct nlattr * const attr)
return filter;
}
-static int
-nfnl_acct_get(struct sock *nfnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh, const struct nlattr * const tb[])
+static int nfnl_acct_get(struct net *net, struct sock *nfnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const tb[])
{
- struct net *net = sock_net(nfnl);
int ret = -ENOENT;
struct nf_acct *cur;
char *acct_name;
@@ -333,11 +331,10 @@ static int nfnl_acct_try_del(struct nf_acct *cur)
return ret;
}
-static int
-nfnl_acct_del(struct sock *nfnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh, const struct nlattr * const tb[])
+static int nfnl_acct_del(struct net *net, struct sock *nfnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const tb[])
{
- struct net *net = sock_net(nfnl);
char *acct_name;
struct nf_acct *cur;
int ret = -ENOENT;
diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c
index 54330fb5efaf..e924e95fcc7f 100644
--- a/net/netfilter/nfnetlink_cthelper.c
+++ b/net/netfilter/nfnetlink_cthelper.c
@@ -286,9 +286,9 @@ nfnl_cthelper_update(const struct nlattr * const tb[],
return 0;
}
-static int
-nfnl_cthelper_new(struct sock *nfnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh, const struct nlattr * const tb[])
+static int nfnl_cthelper_new(struct net *net, struct sock *nfnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const tb[])
{
const char *helper_name;
struct nf_conntrack_helper *cur, *helper = NULL;
@@ -498,9 +498,9 @@ out:
return skb->len;
}
-static int
-nfnl_cthelper_get(struct sock *nfnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh, const struct nlattr * const tb[])
+static int nfnl_cthelper_get(struct net *net, struct sock *nfnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const tb[])
{
int ret = -ENOENT, i;
struct nf_conntrack_helper *cur;
@@ -570,9 +570,9 @@ nfnl_cthelper_get(struct sock *nfnl, struct sk_buff *skb,
return ret;
}
-static int
-nfnl_cthelper_del(struct sock *nfnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh, const struct nlattr * const tb[])
+static int nfnl_cthelper_del(struct net *net, struct sock *nfnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const tb[])
{
char *helper_name = NULL;
struct nf_conntrack_helper *cur;
diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c
index c7a2d0e1c462..5d010f27ac01 100644
--- a/net/netfilter/nfnetlink_cttimeout.c
+++ b/net/netfilter/nfnetlink_cttimeout.c
@@ -38,8 +38,6 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
MODULE_DESCRIPTION("cttimeout: Extended Netfilter Connection Tracking timeout tuning");
-static LIST_HEAD(cttimeout_list);
-
static const struct nla_policy cttimeout_nla_policy[CTA_TIMEOUT_MAX+1] = {
[CTA_TIMEOUT_NAME] = { .type = NLA_NUL_STRING,
.len = CTNL_TIMEOUT_NAME_MAX - 1},
@@ -67,16 +65,15 @@ ctnl_timeout_parse_policy(void *timeouts, struct nf_conntrack_l4proto *l4proto,
return ret;
}
-static int
-cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int cttimeout_new_timeout(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
__u16 l3num;
__u8 l4num;
struct nf_conntrack_l4proto *l4proto;
struct ctnl_timeout *timeout, *matching = NULL;
- struct net *net = sock_net(skb->sk);
char *name;
int ret;
@@ -90,7 +87,7 @@ cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb,
l3num = ntohs(nla_get_be16(cda[CTA_TIMEOUT_L3PROTO]));
l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]);
- list_for_each_entry(timeout, &cttimeout_list, head) {
+ list_for_each_entry(timeout, &net->nfct_timeout_list, head) {
if (strncmp(timeout->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
continue;
@@ -145,7 +142,7 @@ cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb,
timeout->l3num = l3num;
timeout->l4proto = l4proto;
atomic_set(&timeout->refcnt, 1);
- list_add_tail_rcu(&timeout->head, &cttimeout_list);
+ list_add_tail_rcu(&timeout->head, &net->nfct_timeout_list);
return 0;
err:
@@ -209,6 +206,7 @@ nla_put_failure:
static int
ctnl_timeout_dump(struct sk_buff *skb, struct netlink_callback *cb)
{
+ struct net *net = sock_net(skb->sk);
struct ctnl_timeout *cur, *last;
if (cb->args[2])
@@ -219,7 +217,7 @@ ctnl_timeout_dump(struct sk_buff *skb, struct netlink_callback *cb)
cb->args[1] = 0;
rcu_read_lock();
- list_for_each_entry_rcu(cur, &cttimeout_list, head) {
+ list_for_each_entry_rcu(cur, &net->nfct_timeout_list, head) {
if (last) {
if (cur != last)
continue;
@@ -240,10 +238,10 @@ ctnl_timeout_dump(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len;
}
-static int
-cttimeout_get_timeout(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int cttimeout_get_timeout(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
int ret = -ENOENT;
char *name;
@@ -260,7 +258,7 @@ cttimeout_get_timeout(struct sock *ctnl, struct sk_buff *skb,
return -EINVAL;
name = nla_data(cda[CTA_TIMEOUT_NAME]);
- list_for_each_entry(cur, &cttimeout_list, head) {
+ list_for_each_entry(cur, &net->nfct_timeout_list, head) {
struct sk_buff *skb2;
if (strncmp(cur->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
@@ -301,17 +299,17 @@ static void untimeout(struct nf_conntrack_tuple_hash *i,
RCU_INIT_POINTER(timeout_ext->timeout, NULL);
}
-static void ctnl_untimeout(struct ctnl_timeout *timeout)
+static void ctnl_untimeout(struct net *net, struct ctnl_timeout *timeout)
{
struct nf_conntrack_tuple_hash *h;
const struct hlist_nulls_node *nn;
int i;
local_bh_disable();
- for (i = 0; i < init_net.ct.htable_size; i++) {
+ for (i = 0; i < net->ct.htable_size; i++) {
spin_lock(&nf_conntrack_locks[i % CONNTRACK_LOCKS]);
- if (i < init_net.ct.htable_size) {
- hlist_nulls_for_each_entry(h, nn, &init_net.ct.hash[i], hnnode)
+ if (i < net->ct.htable_size) {
+ hlist_nulls_for_each_entry(h, nn, &net->ct.hash[i], hnnode)
untimeout(h, timeout);
}
spin_unlock(&nf_conntrack_locks[i % CONNTRACK_LOCKS]);
@@ -320,7 +318,7 @@ static void ctnl_untimeout(struct ctnl_timeout *timeout)
}
/* try to delete object, fail if it is still in use. */
-static int ctnl_timeout_try_del(struct ctnl_timeout *timeout)
+static int ctnl_timeout_try_del(struct net *net, struct ctnl_timeout *timeout)
{
int ret = 0;
@@ -329,7 +327,7 @@ static int ctnl_timeout_try_del(struct ctnl_timeout *timeout)
/* We are protected by nfnl mutex. */
list_del_rcu(&timeout->head);
nf_ct_l4proto_put(timeout->l4proto);
- ctnl_untimeout(timeout);
+ ctnl_untimeout(net, timeout);
kfree_rcu(timeout, rcu_head);
} else {
/* still in use, restore reference counter. */
@@ -339,28 +337,28 @@ static int ctnl_timeout_try_del(struct ctnl_timeout *timeout)
return ret;
}
-static int
-cttimeout_del_timeout(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int cttimeout_del_timeout(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
- char *name;
struct ctnl_timeout *cur;
int ret = -ENOENT;
+ char *name;
if (!cda[CTA_TIMEOUT_NAME]) {
- list_for_each_entry(cur, &cttimeout_list, head)
- ctnl_timeout_try_del(cur);
+ list_for_each_entry(cur, &net->nfct_timeout_list, head)
+ ctnl_timeout_try_del(net, cur);
return 0;
}
name = nla_data(cda[CTA_TIMEOUT_NAME]);
- list_for_each_entry(cur, &cttimeout_list, head) {
+ list_for_each_entry(cur, &net->nfct_timeout_list, head) {
if (strncmp(cur->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
continue;
- ret = ctnl_timeout_try_del(cur);
+ ret = ctnl_timeout_try_del(net, cur);
if (ret < 0)
return ret;
@@ -369,15 +367,14 @@ cttimeout_del_timeout(struct sock *ctnl, struct sk_buff *skb,
return ret;
}
-static int
-cttimeout_default_set(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const cda[])
+static int cttimeout_default_set(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
{
__u16 l3num;
__u8 l4num;
struct nf_conntrack_l4proto *l4proto;
- struct net *net = sock_net(skb->sk);
unsigned int *timeouts;
int ret;
@@ -459,14 +456,14 @@ nla_put_failure:
return -1;
}
-static int cttimeout_default_get(struct sock *ctnl, struct sk_buff *skb,
+static int cttimeout_default_get(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const cda[])
{
__u16 l3num;
__u8 l4num;
struct nf_conntrack_l4proto *l4proto;
- struct net *net = sock_net(skb->sk);
struct sk_buff *skb2;
int ret, err;
@@ -511,12 +508,13 @@ err:
}
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
-static struct ctnl_timeout *ctnl_timeout_find_get(const char *name)
+static struct ctnl_timeout *
+ctnl_timeout_find_get(struct net *net, const char *name)
{
struct ctnl_timeout *timeout, *matching = NULL;
rcu_read_lock();
- list_for_each_entry_rcu(timeout, &cttimeout_list, head) {
+ list_for_each_entry_rcu(timeout, &net->nfct_timeout_list, head) {
if (strncmp(timeout->name, name, CTNL_TIMEOUT_NAME_MAX) != 0)
continue;
@@ -569,10 +567,39 @@ static const struct nfnetlink_subsystem cttimeout_subsys = {
MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK_TIMEOUT);
+static int __net_init cttimeout_net_init(struct net *net)
+{
+ INIT_LIST_HEAD(&net->nfct_timeout_list);
+
+ return 0;
+}
+
+static void __net_exit cttimeout_net_exit(struct net *net)
+{
+ struct ctnl_timeout *cur, *tmp;
+
+ ctnl_untimeout(net, NULL);
+
+ list_for_each_entry_safe(cur, tmp, &net->nfct_timeout_list, head) {
+ list_del_rcu(&cur->head);
+ nf_ct_l4proto_put(cur->l4proto);
+ kfree_rcu(cur, rcu_head);
+ }
+}
+
+static struct pernet_operations cttimeout_ops = {
+ .init = cttimeout_net_init,
+ .exit = cttimeout_net_exit,
+};
+
static int __init cttimeout_init(void)
{
int ret;
+ ret = register_pernet_subsys(&cttimeout_ops);
+ if (ret < 0)
+ return ret;
+
ret = nfnetlink_subsys_register(&cttimeout_subsys);
if (ret < 0) {
pr_err("cttimeout_init: cannot register cttimeout with "
@@ -586,28 +613,17 @@ static int __init cttimeout_init(void)
return 0;
err_out:
+ unregister_pernet_subsys(&cttimeout_ops);
return ret;
}
static void __exit cttimeout_exit(void)
{
- struct ctnl_timeout *cur, *tmp;
-
pr_info("cttimeout: unregistering from nfnetlink.\n");
nfnetlink_subsys_unregister(&cttimeout_subsys);
- /* Make sure no conntrack objects refer to custom timeouts anymore. */
- ctnl_untimeout(NULL);
-
- list_for_each_entry_safe(cur, tmp, &cttimeout_list, head) {
- list_del_rcu(&cur->head);
- /* We are sure that our objects have no clients at this point,
- * it's safe to release them all without checking refcnt.
- */
- nf_ct_l4proto_put(cur->l4proto);
- kfree_rcu(cur, rcu_head);
- }
+ unregister_pernet_subsys(&cttimeout_ops);
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, NULL);
RCU_INIT_POINTER(nf_ct_timeout_put_hook, NULL);
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
index 740cce4685ac..8ca932057c13 100644
--- a/net/netfilter/nfnetlink_log.c
+++ b/net/netfilter/nfnetlink_log.c
@@ -293,24 +293,20 @@ nfulnl_set_nlbufsiz(struct nfulnl_instance *inst, u_int32_t nlbufsiz)
return status;
}
-static int
+static void
nfulnl_set_timeout(struct nfulnl_instance *inst, u_int32_t timeout)
{
spin_lock_bh(&inst->lock);
inst->flushtimeout = timeout;
spin_unlock_bh(&inst->lock);
-
- return 0;
}
-static int
+static void
nfulnl_set_qthresh(struct nfulnl_instance *inst, u_int32_t qthresh)
{
spin_lock_bh(&inst->lock);
inst->qthreshold = qthresh;
spin_unlock_bh(&inst->lock);
-
- return 0;
}
static int
@@ -789,10 +785,9 @@ static struct notifier_block nfulnl_rtnl_notifier = {
.notifier_call = nfulnl_rcv_nl_event,
};
-static int
-nfulnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const nfqa[])
+static int nfulnl_recv_unsupp(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const nfqa[])
{
return -ENOTSUPP;
}
@@ -813,16 +808,14 @@ static const struct nla_policy nfula_cfg_policy[NFULA_CFG_MAX+1] = {
[NFULA_CFG_FLAGS] = { .type = NLA_U16 },
};
-static int
-nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const nfula[])
+static int nfulnl_recv_config(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const nfula[])
{
struct nfgenmsg *nfmsg = nlmsg_data(nlh);
u_int16_t group_num = ntohs(nfmsg->res_id);
struct nfulnl_instance *inst;
struct nfulnl_msg_config_cmd *cmd = NULL;
- struct net *net = sock_net(ctnl);
struct nfnl_log_net *log = nfnl_log_pernet(net);
int ret = 0;
u16 flags = 0;
@@ -895,7 +888,7 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
goto out_put;
default:
ret = -ENOTSUPP;
- break;
+ goto out_put;
}
} else if (!inst) {
ret = -ENODEV;
@@ -1064,15 +1057,26 @@ static int __net_init nfnl_log_net_init(struct net *net)
{
unsigned int i;
struct nfnl_log_net *log = nfnl_log_pernet(net);
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *proc;
+ kuid_t root_uid;
+ kgid_t root_gid;
+#endif
for (i = 0; i < INSTANCE_BUCKETS; i++)
INIT_HLIST_HEAD(&log->instance_table[i]);
spin_lock_init(&log->instances_lock);
#ifdef CONFIG_PROC_FS
- if (!proc_create("nfnetlink_log", 0440,
- net->nf.proc_netfilter, &nful_file_ops))
+ proc = proc_create("nfnetlink_log", 0440,
+ net->nf.proc_netfilter, &nful_file_ops);
+ if (!proc)
return -ENOMEM;
+
+ root_uid = make_kuid(net->user_ns, 0);
+ root_gid = make_kgid(net->user_ns, 0);
+ if (uid_valid(root_uid) && gid_valid(root_gid))
+ proc_set_user(proc, root_uid, root_gid);
#endif
return 0;
}
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
index 7d81d280cb4f..1d3936587ace 100644
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
@@ -365,8 +365,9 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
break;
}
+ nfnl_ct = rcu_dereference(nfnl_ct_hook);
+
if (queue->flags & NFQA_CFG_F_CONNTRACK) {
- nfnl_ct = rcu_dereference(nfnl_ct_hook);
if (nfnl_ct != NULL) {
ct = nfnl_ct->get_ct(entskb, &ctinfo);
if (ct != NULL)
@@ -956,10 +957,10 @@ static int nfq_id_after(unsigned int id, unsigned int max)
return (int)(id - max) > 0;
}
-static int
-nfqnl_recv_verdict_batch(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const nfqa[])
+static int nfqnl_recv_verdict_batch(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const nfqa[])
{
struct nfgenmsg *nfmsg = nlmsg_data(nlh);
struct nf_queue_entry *entry, *tmp;
@@ -968,8 +969,6 @@ nfqnl_recv_verdict_batch(struct sock *ctnl, struct sk_buff *skb,
struct nfqnl_instance *queue;
LIST_HEAD(batch_list);
u16 queue_num = ntohs(nfmsg->res_id);
-
- struct net *net = sock_net(ctnl);
struct nfnl_queue_net *q = nfnl_queue_pernet(net);
queue = verdict_instance_lookup(q, queue_num,
@@ -1028,14 +1027,13 @@ static struct nf_conn *nfqnl_ct_parse(struct nfnl_ct_hook *nfnl_ct,
return ct;
}
-static int
-nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const nfqa[])
+static int nfqnl_recv_verdict(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const nfqa[])
{
struct nfgenmsg *nfmsg = nlmsg_data(nlh);
u_int16_t queue_num = ntohs(nfmsg->res_id);
-
struct nfqnl_msg_verdict_hdr *vhdr;
struct nfqnl_instance *queue;
unsigned int verdict;
@@ -1043,8 +1041,6 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
enum ip_conntrack_info uninitialized_var(ctinfo);
struct nfnl_ct_hook *nfnl_ct;
struct nf_conn *ct = NULL;
-
- struct net *net = sock_net(ctnl);
struct nfnl_queue_net *q = nfnl_queue_pernet(net);
queue = instance_lookup(q, queue_num);
@@ -1064,9 +1060,10 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
if (entry == NULL)
return -ENOENT;
+ /* rcu lock already held from nfnl->call_rcu. */
+ nfnl_ct = rcu_dereference(nfnl_ct_hook);
+
if (nfqa[NFQA_CT]) {
- /* rcu lock already held from nfnl->call_rcu. */
- nfnl_ct = rcu_dereference(nfnl_ct_hook);
if (nfnl_ct != NULL)
ct = nfqnl_ct_parse(nfnl_ct, nlh, nfqa, entry, &ctinfo);
}
@@ -1090,10 +1087,9 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb,
return 0;
}
-static int
-nfqnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const nfqa[])
+static int nfqnl_recv_unsupp(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const nfqa[])
{
return -ENOTSUPP;
}
@@ -1108,17 +1104,16 @@ static const struct nf_queue_handler nfqh = {
.nf_hook_drop = &nfqnl_nf_hook_drop,
};
-static int
-nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
- const struct nlattr * const nfqa[])
+static int nfqnl_recv_config(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const nfqa[])
{
struct nfgenmsg *nfmsg = nlmsg_data(nlh);
u_int16_t queue_num = ntohs(nfmsg->res_id);
struct nfqnl_instance *queue;
struct nfqnl_msg_config_cmd *cmd = NULL;
- struct net *net = sock_net(ctnl);
struct nfnl_queue_net *q = nfnl_queue_pernet(net);
+ __u32 flags = 0, mask = 0;
int ret = 0;
if (nfqa[NFQA_CFG_CMD]) {
@@ -1131,6 +1126,40 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
}
}
+ /* Check if we support these flags in first place, dependencies should
+ * be there too not to break atomicity.
+ */
+ if (nfqa[NFQA_CFG_FLAGS]) {
+ if (!nfqa[NFQA_CFG_MASK]) {
+ /* A mask is needed to specify which flags are being
+ * changed.
+ */
+ return -EINVAL;
+ }
+
+ flags = ntohl(nla_get_be32(nfqa[NFQA_CFG_FLAGS]));
+ mask = ntohl(nla_get_be32(nfqa[NFQA_CFG_MASK]));
+
+ if (flags >= NFQA_CFG_F_MAX)
+ return -EOPNOTSUPP;
+
+#if !IS_ENABLED(CONFIG_NETWORK_SECMARK)
+ if (flags & mask & NFQA_CFG_F_SECCTX)
+ return -EOPNOTSUPP;
+#endif
+ if ((flags & mask & NFQA_CFG_F_CONNTRACK) &&
+ !rcu_access_pointer(nfnl_ct_hook)) {
+#ifdef CONFIG_MODULES
+ nfnl_unlock(NFNL_SUBSYS_QUEUE);
+ request_module("ip_conntrack_netlink");
+ nfnl_lock(NFNL_SUBSYS_QUEUE);
+ if (rcu_access_pointer(nfnl_ct_hook))
+ return -EAGAIN;
+#endif
+ return -EOPNOTSUPP;
+ }
+ }
+
rcu_read_lock();
queue = instance_lookup(q, queue_num);
if (queue && queue->peer_portid != NETLINK_CB(skb).portid) {
@@ -1158,70 +1187,38 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
goto err_out_unlock;
}
instance_destroy(q, queue);
- break;
+ goto err_out_unlock;
case NFQNL_CFG_CMD_PF_BIND:
case NFQNL_CFG_CMD_PF_UNBIND:
break;
default:
ret = -ENOTSUPP;
- break;
+ goto err_out_unlock;
}
}
+ if (!queue) {
+ ret = -ENODEV;
+ goto err_out_unlock;
+ }
+
if (nfqa[NFQA_CFG_PARAMS]) {
- struct nfqnl_msg_config_params *params;
+ struct nfqnl_msg_config_params *params =
+ nla_data(nfqa[NFQA_CFG_PARAMS]);
- if (!queue) {
- ret = -ENODEV;
- goto err_out_unlock;
- }
- params = nla_data(nfqa[NFQA_CFG_PARAMS]);
nfqnl_set_mode(queue, params->copy_mode,
ntohl(params->copy_range));
}
if (nfqa[NFQA_CFG_QUEUE_MAXLEN]) {
- __be32 *queue_maxlen;
+ __be32 *queue_maxlen = nla_data(nfqa[NFQA_CFG_QUEUE_MAXLEN]);
- if (!queue) {
- ret = -ENODEV;
- goto err_out_unlock;
- }
- queue_maxlen = nla_data(nfqa[NFQA_CFG_QUEUE_MAXLEN]);
spin_lock_bh(&queue->lock);
queue->queue_maxlen = ntohl(*queue_maxlen);
spin_unlock_bh(&queue->lock);
}
if (nfqa[NFQA_CFG_FLAGS]) {
- __u32 flags, mask;
-
- if (!queue) {
- ret = -ENODEV;
- goto err_out_unlock;
- }
-
- if (!nfqa[NFQA_CFG_MASK]) {
- /* A mask is needed to specify which flags are being
- * changed.
- */
- ret = -EINVAL;
- goto err_out_unlock;
- }
-
- flags = ntohl(nla_get_be32(nfqa[NFQA_CFG_FLAGS]));
- mask = ntohl(nla_get_be32(nfqa[NFQA_CFG_MASK]));
-
- if (flags >= NFQA_CFG_F_MAX) {
- ret = -EOPNOTSUPP;
- goto err_out_unlock;
- }
-#if !IS_ENABLED(CONFIG_NETWORK_SECMARK)
- if (flags & mask & NFQA_CFG_F_SECCTX) {
- ret = -EOPNOTSUPP;
- goto err_out_unlock;
- }
-#endif
spin_lock_bh(&queue->lock);
queue->flags &= ~mask;
queue->flags |= flags & mask;
@@ -1417,6 +1414,7 @@ static int __init nfnetlink_queue_init(void)
cleanup_netlink_notifier:
netlink_unregister_notifier(&nfqnl_rtnl_notifier);
+ unregister_pernet_subsys(&nfnl_queue_net_ops);
out:
return status;
}
diff --git a/net/netfilter/nft_byteorder.c b/net/netfilter/nft_byteorder.c
index fde5145f2e36..383c17138399 100644
--- a/net/netfilter/nft_byteorder.c
+++ b/net/netfilter/nft_byteorder.c
@@ -8,6 +8,7 @@
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
+#include <asm/unaligned.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
@@ -39,6 +40,27 @@ static void nft_byteorder_eval(const struct nft_expr *expr,
d = (void *)dst;
switch (priv->size) {
+ case 8: {
+ u64 src64;
+
+ switch (priv->op) {
+ case NFT_BYTEORDER_NTOH:
+ for (i = 0; i < priv->len / 8; i++) {
+ src64 = get_unaligned_be64(&src[i]);
+ src64 = be64_to_cpu((__force __be64)src64);
+ put_unaligned_be64(src64, &dst[i]);
+ }
+ break;
+ case NFT_BYTEORDER_HTON:
+ for (i = 0; i < priv->len / 8; i++) {
+ src64 = get_unaligned_be64(&src[i]);
+ src64 = (__force u64)cpu_to_be64(src64);
+ put_unaligned_be64(src64, &dst[i]);
+ }
+ break;
+ }
+ break;
+ }
case 4:
switch (priv->op) {
case NFT_BYTEORDER_NTOH:
@@ -101,6 +123,7 @@ static int nft_byteorder_init(const struct nft_ctx *ctx,
switch (priv->size) {
case 2:
case 4:
+ case 8:
break;
default:
return -EINVAL;
diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
index 9c8fab00164b..454841baa4d0 100644
--- a/net/netfilter/nft_compat.c
+++ b/net/netfilter/nft_compat.c
@@ -519,9 +519,9 @@ nla_put_failure:
return -1;
}
-static int
-nfnl_compat_get(struct sock *nfnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh, const struct nlattr * const tb[])
+static int nfnl_compat_get(struct net *net, struct sock *nfnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const tb[])
{
int ret = 0, target;
struct nfgenmsg *nfmsg;
diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c
index 8cbca3432f90..a0eb2161e3ef 100644
--- a/net/netfilter/nft_ct.c
+++ b/net/netfilter/nft_ct.c
@@ -16,6 +16,7 @@
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_acct.h>
#include <net/netfilter/nf_conntrack_tuple.h>
#include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_ecache.h>
@@ -30,6 +31,18 @@ struct nft_ct {
};
};
+static u64 nft_ct_get_eval_counter(const struct nf_conn_counter *c,
+ enum nft_ct_keys k,
+ enum ip_conntrack_dir d)
+{
+ if (d < IP_CT_DIR_MAX)
+ return k == NFT_CT_BYTES ? atomic64_read(&c[d].bytes) :
+ atomic64_read(&c[d].packets);
+
+ return nft_ct_get_eval_counter(c, k, IP_CT_DIR_ORIGINAL) +
+ nft_ct_get_eval_counter(c, k, IP_CT_DIR_REPLY);
+}
+
static void nft_ct_get_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
@@ -114,6 +127,17 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
NF_CT_LABELS_MAX_SIZE - size);
return;
}
+ case NFT_CT_BYTES: /* fallthrough */
+ case NFT_CT_PKTS: {
+ const struct nf_conn_acct *acct = nf_conn_acct_find(ct);
+ u64 count = 0;
+
+ if (acct)
+ count = nft_ct_get_eval_counter(acct->counter,
+ priv->key, priv->dir);
+ memcpy(dest, &count, sizeof(count));
+ return;
+ }
#endif
default:
break;
@@ -291,6 +315,13 @@ static int nft_ct_get_init(const struct nft_ctx *ctx,
return -EINVAL;
len = FIELD_SIZEOF(struct nf_conntrack_tuple, src.u.all);
break;
+ case NFT_CT_BYTES:
+ case NFT_CT_PKTS:
+ /* no direction? return sum of original + reply */
+ if (tb[NFTA_CT_DIRECTION] == NULL)
+ priv->dir = IP_CT_DIR_MAX;
+ len = sizeof(u64);
+ break;
default:
return -EOPNOTSUPP;
}
@@ -366,6 +397,7 @@ static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr)
goto nla_put_failure;
switch (priv->key) {
+ case NFT_CT_L3PROTOCOL:
case NFT_CT_PROTOCOL:
case NFT_CT_SRC:
case NFT_CT_DST:
@@ -373,6 +405,13 @@ static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr)
case NFT_CT_PROTO_DST:
if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir))
goto nla_put_failure;
+ break;
+ case NFT_CT_BYTES:
+ case NFT_CT_PKTS:
+ if (priv->dir < IP_CT_DIR_MAX &&
+ nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir))
+ goto nla_put_failure;
+ break;
default:
break;
}
diff --git a/net/netfilter/nft_dup_netdev.c b/net/netfilter/nft_dup_netdev.c
new file mode 100644
index 000000000000..2cc1e0ef56e8
--- /dev/null
+++ b/net/netfilter/nft_dup_netdev.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2015 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_dup_netdev.h>
+
+struct nft_dup_netdev {
+ enum nft_registers sreg_dev:8;
+};
+
+static void nft_dup_netdev_eval(const struct nft_expr *expr,
+ struct nft_regs *regs,
+ const struct nft_pktinfo *pkt)
+{
+ struct nft_dup_netdev *priv = nft_expr_priv(expr);
+ int oif = regs->data[priv->sreg_dev];
+
+ nf_dup_netdev_egress(pkt, oif);
+}
+
+static const struct nla_policy nft_dup_netdev_policy[NFTA_DUP_MAX + 1] = {
+ [NFTA_DUP_SREG_DEV] = { .type = NLA_U32 },
+};
+
+static int nft_dup_netdev_init(const struct nft_ctx *ctx,
+ const struct nft_expr *expr,
+ const struct nlattr * const tb[])
+{
+ struct nft_dup_netdev *priv = nft_expr_priv(expr);
+
+ if (tb[NFTA_DUP_SREG_DEV] == NULL)
+ return -EINVAL;
+
+ priv->sreg_dev = nft_parse_register(tb[NFTA_DUP_SREG_DEV]);
+ return nft_validate_register_load(priv->sreg_dev, sizeof(int));
+}
+
+static const struct nft_expr_ops nft_dup_netdev_ingress_ops;
+
+static int nft_dup_netdev_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+ struct nft_dup_netdev *priv = nft_expr_priv(expr);
+
+ if (nft_dump_register(skb, NFTA_DUP_SREG_DEV, priv->sreg_dev))
+ goto nla_put_failure;
+
+ return 0;
+
+nla_put_failure:
+ return -1;
+}
+
+static struct nft_expr_type nft_dup_netdev_type;
+static const struct nft_expr_ops nft_dup_netdev_ops = {
+ .type = &nft_dup_netdev_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_dup_netdev)),
+ .eval = nft_dup_netdev_eval,
+ .init = nft_dup_netdev_init,
+ .dump = nft_dup_netdev_dump,
+};
+
+static struct nft_expr_type nft_dup_netdev_type __read_mostly = {
+ .family = NFPROTO_NETDEV,
+ .name = "dup",
+ .ops = &nft_dup_netdev_ops,
+ .policy = nft_dup_netdev_policy,
+ .maxattr = NFTA_DUP_MAX,
+ .owner = THIS_MODULE,
+};
+
+static int __init nft_dup_netdev_module_init(void)
+{
+ return nft_register_expr(&nft_dup_netdev_type);
+}
+
+static void __exit nft_dup_netdev_module_exit(void)
+{
+ nft_unregister_expr(&nft_dup_netdev_type);
+}
+
+module_init(nft_dup_netdev_module_init);
+module_exit(nft_dup_netdev_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
+MODULE_ALIAS_NFT_AF_EXPR(5, "dup");
diff --git a/net/netfilter/nft_fwd_netdev.c b/net/netfilter/nft_fwd_netdev.c
new file mode 100644
index 000000000000..763ebc3e0b2b
--- /dev/null
+++ b/net/netfilter/nft_fwd_netdev.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2015 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_dup_netdev.h>
+
+struct nft_fwd_netdev {
+ enum nft_registers sreg_dev:8;
+};
+
+static void nft_fwd_netdev_eval(const struct nft_expr *expr,
+ struct nft_regs *regs,
+ const struct nft_pktinfo *pkt)
+{
+ struct nft_fwd_netdev *priv = nft_expr_priv(expr);
+ int oif = regs->data[priv->sreg_dev];
+
+ nf_dup_netdev_egress(pkt, oif);
+ regs->verdict.code = NF_DROP;
+}
+
+static const struct nla_policy nft_fwd_netdev_policy[NFTA_FWD_MAX + 1] = {
+ [NFTA_FWD_SREG_DEV] = { .type = NLA_U32 },
+};
+
+static int nft_fwd_netdev_init(const struct nft_ctx *ctx,
+ const struct nft_expr *expr,
+ const struct nlattr * const tb[])
+{
+ struct nft_fwd_netdev *priv = nft_expr_priv(expr);
+
+ if (tb[NFTA_FWD_SREG_DEV] == NULL)
+ return -EINVAL;
+
+ priv->sreg_dev = nft_parse_register(tb[NFTA_FWD_SREG_DEV]);
+ return nft_validate_register_load(priv->sreg_dev, sizeof(int));
+}
+
+static const struct nft_expr_ops nft_fwd_netdev_ingress_ops;
+
+static int nft_fwd_netdev_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+ struct nft_fwd_netdev *priv = nft_expr_priv(expr);
+
+ if (nft_dump_register(skb, NFTA_FWD_SREG_DEV, priv->sreg_dev))
+ goto nla_put_failure;
+
+ return 0;
+
+nla_put_failure:
+ return -1;
+}
+
+static struct nft_expr_type nft_fwd_netdev_type;
+static const struct nft_expr_ops nft_fwd_netdev_ops = {
+ .type = &nft_fwd_netdev_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_fwd_netdev)),
+ .eval = nft_fwd_netdev_eval,
+ .init = nft_fwd_netdev_init,
+ .dump = nft_fwd_netdev_dump,
+};
+
+static struct nft_expr_type nft_fwd_netdev_type __read_mostly = {
+ .family = NFPROTO_NETDEV,
+ .name = "fwd",
+ .ops = &nft_fwd_netdev_ops,
+ .policy = nft_fwd_netdev_policy,
+ .maxattr = NFTA_FWD_MAX,
+ .owner = THIS_MODULE,
+};
+
+static int __init nft_fwd_netdev_module_init(void)
+{
+ return nft_register_expr(&nft_fwd_netdev_type);
+}
+
+static void __exit nft_fwd_netdev_module_exit(void)
+{
+ nft_unregister_expr(&nft_fwd_netdev_type);
+}
+
+module_init(nft_fwd_netdev_module_init);
+module_exit(nft_fwd_netdev_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
+MODULE_ALIAS_NFT_AF_EXPR(5, "fwd");
diff --git a/net/netfilter/nft_limit.c b/net/netfilter/nft_limit.c
index 5d67938f8b2f..99d18578afc6 100644
--- a/net/netfilter/nft_limit.c
+++ b/net/netfilter/nft_limit.c
@@ -26,6 +26,7 @@ struct nft_limit {
u64 rate;
u64 nsecs;
u32 burst;
+ bool invert;
};
static inline bool nft_limit_eval(struct nft_limit *limit, u64 cost)
@@ -44,11 +45,11 @@ static inline bool nft_limit_eval(struct nft_limit *limit, u64 cost)
if (delta >= 0) {
limit->tokens = delta;
spin_unlock_bh(&limit_lock);
- return false;
+ return limit->invert;
}
limit->tokens = tokens;
spin_unlock_bh(&limit_lock);
- return true;
+ return !limit->invert;
}
static int nft_limit_init(struct nft_limit *limit,
@@ -78,6 +79,12 @@ static int nft_limit_init(struct nft_limit *limit,
limit->rate = rate;
}
+ if (tb[NFTA_LIMIT_FLAGS]) {
+ u32 flags = ntohl(nla_get_be32(tb[NFTA_LIMIT_FLAGS]));
+
+ if (flags & NFT_LIMIT_F_INV)
+ limit->invert = true;
+ }
limit->last = ktime_get_ns();
return 0;
@@ -86,13 +93,15 @@ static int nft_limit_init(struct nft_limit *limit,
static int nft_limit_dump(struct sk_buff *skb, const struct nft_limit *limit,
enum nft_limit_type type)
{
+ u32 flags = limit->invert ? NFT_LIMIT_F_INV : 0;
u64 secs = div_u64(limit->nsecs, NSEC_PER_SEC);
u64 rate = limit->rate - limit->burst;
if (nla_put_be64(skb, NFTA_LIMIT_RATE, cpu_to_be64(rate)) ||
nla_put_be64(skb, NFTA_LIMIT_UNIT, cpu_to_be64(secs)) ||
nla_put_be32(skb, NFTA_LIMIT_BURST, htonl(limit->burst)) ||
- nla_put_be32(skb, NFTA_LIMIT_TYPE, htonl(type)))
+ nla_put_be32(skb, NFTA_LIMIT_TYPE, htonl(type)) ||
+ nla_put_be32(skb, NFTA_LIMIT_FLAGS, htonl(flags)))
goto nla_put_failure;
return 0;
@@ -120,6 +129,7 @@ static const struct nla_policy nft_limit_policy[NFTA_LIMIT_MAX + 1] = {
[NFTA_LIMIT_UNIT] = { .type = NLA_U64 },
[NFTA_LIMIT_BURST] = { .type = NLA_U32 },
[NFTA_LIMIT_TYPE] = { .type = NLA_U32 },
+ [NFTA_LIMIT_FLAGS] = { .type = NLA_U32 },
};
static int nft_limit_pkts_init(const struct nft_ctx *ctx,
diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c
index 9dfaf4d55ee0..fe885bf271c5 100644
--- a/net/netfilter/nft_meta.c
+++ b/net/netfilter/nft_meta.c
@@ -18,12 +18,16 @@
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/smp.h>
+#include <linux/static_key.h>
#include <net/dst.h>
#include <net/sock.h>
#include <net/tcp_states.h> /* for TCP_TIME_WAIT */
#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nft_meta.h>
+#include <uapi/linux/netfilter_bridge.h> /* NF_BR_PRE_ROUTING */
+
void nft_meta_get_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
@@ -174,7 +178,7 @@ void nft_meta_get_eval(const struct nft_expr *expr,
sk = skb_to_full_sk(skb);
if (!sk || !sk_fullsock(sk))
goto err;
- *dest = sk->sk_classid;
+ *dest = sock_cgroup_classid(&sk->sk_cgrp_data);
break;
#endif
default:
@@ -188,6 +192,13 @@ err:
}
EXPORT_SYMBOL_GPL(nft_meta_get_eval);
+/* don't change or set _LOOPBACK, _USER, etc. */
+static bool pkt_type_ok(u32 p)
+{
+ return p == PACKET_HOST || p == PACKET_BROADCAST ||
+ p == PACKET_MULTICAST || p == PACKET_OTHERHOST;
+}
+
void nft_meta_set_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
@@ -203,6 +214,11 @@ void nft_meta_set_eval(const struct nft_expr *expr,
case NFT_META_PRIORITY:
skb->priority = value;
break;
+ case NFT_META_PKTTYPE:
+ if (skb->pkt_type != value &&
+ pkt_type_ok(value) && pkt_type_ok(skb->pkt_type))
+ skb->pkt_type = value;
+ break;
case NFT_META_NFTRACE:
skb->nf_trace = 1;
break;
@@ -271,6 +287,24 @@ int nft_meta_get_init(const struct nft_ctx *ctx,
}
EXPORT_SYMBOL_GPL(nft_meta_get_init);
+static int nft_meta_set_init_pkttype(const struct nft_ctx *ctx)
+{
+ unsigned int hooks;
+
+ switch (ctx->afi->family) {
+ case NFPROTO_BRIDGE:
+ hooks = 1 << NF_BR_PRE_ROUTING;
+ break;
+ case NFPROTO_NETDEV:
+ hooks = 1 << NF_NETDEV_INGRESS;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return nft_chain_validate_hooks(ctx->chain, hooks);
+}
+
int nft_meta_set_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
@@ -288,6 +322,12 @@ int nft_meta_set_init(const struct nft_ctx *ctx,
case NFT_META_NFTRACE:
len = sizeof(u8);
break;
+ case NFT_META_PKTTYPE:
+ err = nft_meta_set_init_pkttype(ctx);
+ if (err)
+ return err;
+ len = sizeof(u8);
+ break;
default:
return -EOPNOTSUPP;
}
@@ -297,6 +337,9 @@ int nft_meta_set_init(const struct nft_ctx *ctx,
if (err < 0)
return err;
+ if (priv->key == NFT_META_NFTRACE)
+ static_branch_inc(&nft_trace_enabled);
+
return 0;
}
EXPORT_SYMBOL_GPL(nft_meta_set_init);
@@ -334,6 +377,16 @@ nla_put_failure:
}
EXPORT_SYMBOL_GPL(nft_meta_set_dump);
+void nft_meta_set_destroy(const struct nft_ctx *ctx,
+ const struct nft_expr *expr)
+{
+ const struct nft_meta *priv = nft_expr_priv(expr);
+
+ if (priv->key == NFT_META_NFTRACE)
+ static_branch_dec(&nft_trace_enabled);
+}
+EXPORT_SYMBOL_GPL(nft_meta_set_destroy);
+
static struct nft_expr_type nft_meta_type;
static const struct nft_expr_ops nft_meta_get_ops = {
.type = &nft_meta_type,
@@ -348,6 +401,7 @@ static const struct nft_expr_ops nft_meta_set_ops = {
.size = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
.eval = nft_meta_set_eval,
.init = nft_meta_set_init,
+ .destroy = nft_meta_set_destroy,
.dump = nft_meta_set_dump,
};
diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c
index 09b4b07eb676..12cd4bf16d17 100644
--- a/net/netfilter/nft_payload.c
+++ b/net/netfilter/nft_payload.c
@@ -107,10 +107,13 @@ err:
}
static const struct nla_policy nft_payload_policy[NFTA_PAYLOAD_MAX + 1] = {
- [NFTA_PAYLOAD_DREG] = { .type = NLA_U32 },
- [NFTA_PAYLOAD_BASE] = { .type = NLA_U32 },
- [NFTA_PAYLOAD_OFFSET] = { .type = NLA_U32 },
- [NFTA_PAYLOAD_LEN] = { .type = NLA_U32 },
+ [NFTA_PAYLOAD_SREG] = { .type = NLA_U32 },
+ [NFTA_PAYLOAD_DREG] = { .type = NLA_U32 },
+ [NFTA_PAYLOAD_BASE] = { .type = NLA_U32 },
+ [NFTA_PAYLOAD_OFFSET] = { .type = NLA_U32 },
+ [NFTA_PAYLOAD_LEN] = { .type = NLA_U32 },
+ [NFTA_PAYLOAD_CSUM_TYPE] = { .type = NLA_U32 },
+ [NFTA_PAYLOAD_CSUM_OFFSET] = { .type = NLA_U32 },
};
static int nft_payload_init(const struct nft_ctx *ctx,
@@ -160,6 +163,118 @@ const struct nft_expr_ops nft_payload_fast_ops = {
.dump = nft_payload_dump,
};
+static void nft_payload_set_eval(const struct nft_expr *expr,
+ struct nft_regs *regs,
+ const struct nft_pktinfo *pkt)
+{
+ const struct nft_payload_set *priv = nft_expr_priv(expr);
+ struct sk_buff *skb = pkt->skb;
+ const u32 *src = &regs->data[priv->sreg];
+ int offset, csum_offset;
+ __wsum fsum, tsum;
+ __sum16 sum;
+
+ switch (priv->base) {
+ case NFT_PAYLOAD_LL_HEADER:
+ if (!skb_mac_header_was_set(skb))
+ goto err;
+ offset = skb_mac_header(skb) - skb->data;
+ break;
+ case NFT_PAYLOAD_NETWORK_HEADER:
+ offset = skb_network_offset(skb);
+ break;
+ case NFT_PAYLOAD_TRANSPORT_HEADER:
+ offset = pkt->xt.thoff;
+ break;
+ default:
+ BUG();
+ }
+
+ csum_offset = offset + priv->csum_offset;
+ offset += priv->offset;
+
+ if (priv->csum_type == NFT_PAYLOAD_CSUM_INET &&
+ (priv->base != NFT_PAYLOAD_TRANSPORT_HEADER ||
+ skb->ip_summed != CHECKSUM_PARTIAL)) {
+ if (skb_copy_bits(skb, csum_offset, &sum, sizeof(sum)) < 0)
+ goto err;
+
+ fsum = skb_checksum(skb, offset, priv->len, 0);
+ tsum = csum_partial(src, priv->len, 0);
+ sum = csum_fold(csum_add(csum_sub(~csum_unfold(sum), fsum),
+ tsum));
+ if (sum == 0)
+ sum = CSUM_MANGLED_0;
+
+ if (!skb_make_writable(skb, csum_offset + sizeof(sum)) ||
+ skb_store_bits(skb, csum_offset, &sum, sizeof(sum)) < 0)
+ goto err;
+ }
+
+ if (!skb_make_writable(skb, max(offset + priv->len, 0)) ||
+ skb_store_bits(skb, offset, src, priv->len) < 0)
+ goto err;
+
+ return;
+err:
+ regs->verdict.code = NFT_BREAK;
+}
+
+static int nft_payload_set_init(const struct nft_ctx *ctx,
+ const struct nft_expr *expr,
+ const struct nlattr * const tb[])
+{
+ struct nft_payload_set *priv = nft_expr_priv(expr);
+
+ priv->base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE]));
+ priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET]));
+ priv->len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN]));
+ priv->sreg = nft_parse_register(tb[NFTA_PAYLOAD_SREG]);
+
+ if (tb[NFTA_PAYLOAD_CSUM_TYPE])
+ priv->csum_type =
+ ntohl(nla_get_be32(tb[NFTA_PAYLOAD_CSUM_TYPE]));
+ if (tb[NFTA_PAYLOAD_CSUM_OFFSET])
+ priv->csum_offset =
+ ntohl(nla_get_be32(tb[NFTA_PAYLOAD_CSUM_OFFSET]));
+
+ switch (priv->csum_type) {
+ case NFT_PAYLOAD_CSUM_NONE:
+ case NFT_PAYLOAD_CSUM_INET:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return nft_validate_register_load(priv->sreg, priv->len);
+}
+
+static int nft_payload_set_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+ const struct nft_payload_set *priv = nft_expr_priv(expr);
+
+ if (nft_dump_register(skb, NFTA_PAYLOAD_SREG, priv->sreg) ||
+ nla_put_be32(skb, NFTA_PAYLOAD_BASE, htonl(priv->base)) ||
+ nla_put_be32(skb, NFTA_PAYLOAD_OFFSET, htonl(priv->offset)) ||
+ nla_put_be32(skb, NFTA_PAYLOAD_LEN, htonl(priv->len)) ||
+ nla_put_be32(skb, NFTA_PAYLOAD_CSUM_TYPE, htonl(priv->csum_type)) ||
+ nla_put_be32(skb, NFTA_PAYLOAD_CSUM_OFFSET,
+ htonl(priv->csum_offset)))
+ goto nla_put_failure;
+ return 0;
+
+nla_put_failure:
+ return -1;
+}
+
+static const struct nft_expr_ops nft_payload_set_ops = {
+ .type = &nft_payload_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_payload_set)),
+ .eval = nft_payload_set_eval,
+ .init = nft_payload_set_init,
+ .dump = nft_payload_set_dump,
+};
+
static const struct nft_expr_ops *
nft_payload_select_ops(const struct nft_ctx *ctx,
const struct nlattr * const tb[])
@@ -167,8 +282,7 @@ nft_payload_select_ops(const struct nft_ctx *ctx,
enum nft_payload_bases base;
unsigned int offset, len;
- if (tb[NFTA_PAYLOAD_DREG] == NULL ||
- tb[NFTA_PAYLOAD_BASE] == NULL ||
+ if (tb[NFTA_PAYLOAD_BASE] == NULL ||
tb[NFTA_PAYLOAD_OFFSET] == NULL ||
tb[NFTA_PAYLOAD_LEN] == NULL)
return ERR_PTR(-EINVAL);
@@ -183,6 +297,15 @@ nft_payload_select_ops(const struct nft_ctx *ctx,
return ERR_PTR(-EOPNOTSUPP);
}
+ if (tb[NFTA_PAYLOAD_SREG] != NULL) {
+ if (tb[NFTA_PAYLOAD_DREG] != NULL)
+ return ERR_PTR(-EINVAL);
+ return &nft_payload_set_ops;
+ }
+
+ if (tb[NFTA_PAYLOAD_DREG] == NULL)
+ return ERR_PTR(-EINVAL);
+
offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET]));
len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN]));
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c
index d4aaad747ea9..c8a0b7da5ff4 100644
--- a/net/netfilter/x_tables.c
+++ b/net/netfilter/x_tables.c
@@ -26,6 +26,7 @@
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/audit.h>
+#include <linux/user_namespace.h>
#include <net/net_namespace.h>
#include <linux/netfilter/x_tables.h>
@@ -1226,6 +1227,8 @@ int xt_proto_init(struct net *net, u_int8_t af)
#ifdef CONFIG_PROC_FS
char buf[XT_FUNCTION_MAXNAMELEN];
struct proc_dir_entry *proc;
+ kuid_t root_uid;
+ kgid_t root_gid;
#endif
if (af >= ARRAY_SIZE(xt_prefix))
@@ -1233,12 +1236,17 @@ int xt_proto_init(struct net *net, u_int8_t af)
#ifdef CONFIG_PROC_FS
+ root_uid = make_kuid(net->user_ns, 0);
+ root_gid = make_kgid(net->user_ns, 0);
+
strlcpy(buf, xt_prefix[af], sizeof(buf));
strlcat(buf, FORMAT_TABLES, sizeof(buf));
proc = proc_create_data(buf, 0440, net->proc_net, &xt_table_ops,
(void *)(unsigned long)af);
if (!proc)
goto out;
+ if (uid_valid(root_uid) && gid_valid(root_gid))
+ proc_set_user(proc, root_uid, root_gid);
strlcpy(buf, xt_prefix[af], sizeof(buf));
strlcat(buf, FORMAT_MATCHES, sizeof(buf));
@@ -1246,6 +1254,8 @@ int xt_proto_init(struct net *net, u_int8_t af)
(void *)(unsigned long)af);
if (!proc)
goto out_remove_tables;
+ if (uid_valid(root_uid) && gid_valid(root_gid))
+ proc_set_user(proc, root_uid, root_gid);
strlcpy(buf, xt_prefix[af], sizeof(buf));
strlcat(buf, FORMAT_TARGETS, sizeof(buf));
@@ -1253,6 +1263,8 @@ int xt_proto_init(struct net *net, u_int8_t af)
(void *)(unsigned long)af);
if (!proc)
goto out_remove_matches;
+ if (uid_valid(root_uid) && gid_valid(root_gid))
+ proc_set_user(proc, root_uid, root_gid);
#endif
return 0;
diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c
index e7ac07e53b59..6669e68d589e 100644
--- a/net/netfilter/xt_CT.c
+++ b/net/netfilter/xt_CT.c
@@ -143,7 +143,7 @@ xt_ct_set_timeout(struct nf_conn *ct, const struct xt_tgchk_param *par,
goto out;
}
- timeout = timeout_find_get(timeout_name);
+ timeout = timeout_find_get(par->net, timeout_name);
if (timeout == NULL) {
ret = -ENOENT;
pr_info("No such timeout policy \"%s\"\n", timeout_name);
diff --git a/net/netfilter/xt_cgroup.c b/net/netfilter/xt_cgroup.c
index a1d126f29463..a086a914865f 100644
--- a/net/netfilter/xt_cgroup.c
+++ b/net/netfilter/xt_cgroup.c
@@ -24,9 +24,9 @@ MODULE_DESCRIPTION("Xtables: process control group matching");
MODULE_ALIAS("ipt_cgroup");
MODULE_ALIAS("ip6t_cgroup");
-static int cgroup_mt_check(const struct xt_mtchk_param *par)
+static int cgroup_mt_check_v0(const struct xt_mtchk_param *par)
{
- struct xt_cgroup_info *info = par->matchinfo;
+ struct xt_cgroup_info_v0 *info = par->matchinfo;
if (info->invert & ~1)
return -EINVAL;
@@ -34,38 +34,110 @@ static int cgroup_mt_check(const struct xt_mtchk_param *par)
return 0;
}
+static int cgroup_mt_check_v1(const struct xt_mtchk_param *par)
+{
+ struct xt_cgroup_info_v1 *info = par->matchinfo;
+ struct cgroup *cgrp;
+
+ if ((info->invert_path & ~1) || (info->invert_classid & ~1))
+ return -EINVAL;
+
+ if (!info->has_path && !info->has_classid) {
+ pr_info("xt_cgroup: no path or classid specified\n");
+ return -EINVAL;
+ }
+
+ if (info->has_path && info->has_classid) {
+ pr_info("xt_cgroup: both path and classid specified\n");
+ return -EINVAL;
+ }
+
+ if (info->has_path) {
+ cgrp = cgroup_get_from_path(info->path);
+ if (IS_ERR(cgrp)) {
+ pr_info("xt_cgroup: invalid path, errno=%ld\n",
+ PTR_ERR(cgrp));
+ return -EINVAL;
+ }
+ info->priv = cgrp;
+ }
+
+ return 0;
+}
+
static bool
-cgroup_mt(const struct sk_buff *skb, struct xt_action_param *par)
+cgroup_mt_v0(const struct sk_buff *skb, struct xt_action_param *par)
{
- const struct xt_cgroup_info *info = par->matchinfo;
+ const struct xt_cgroup_info_v0 *info = par->matchinfo;
if (skb->sk == NULL || !sk_fullsock(skb->sk))
return false;
- return (info->id == skb->sk->sk_classid) ^ info->invert;
+ return (info->id == sock_cgroup_classid(&skb->sk->sk_cgrp_data)) ^
+ info->invert;
+}
+
+static bool cgroup_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)
+{
+ const struct xt_cgroup_info_v1 *info = par->matchinfo;
+ struct sock_cgroup_data *skcd = &skb->sk->sk_cgrp_data;
+ struct cgroup *ancestor = info->priv;
+
+ if (!skb->sk || !sk_fullsock(skb->sk))
+ return false;
+
+ if (ancestor)
+ return cgroup_is_descendant(sock_cgroup_ptr(skcd), ancestor) ^
+ info->invert_path;
+ else
+ return (info->classid == sock_cgroup_classid(skcd)) ^
+ info->invert_classid;
+}
+
+static void cgroup_mt_destroy_v1(const struct xt_mtdtor_param *par)
+{
+ struct xt_cgroup_info_v1 *info = par->matchinfo;
+
+ if (info->priv)
+ cgroup_put(info->priv);
}
-static struct xt_match cgroup_mt_reg __read_mostly = {
- .name = "cgroup",
- .revision = 0,
- .family = NFPROTO_UNSPEC,
- .checkentry = cgroup_mt_check,
- .match = cgroup_mt,
- .matchsize = sizeof(struct xt_cgroup_info),
- .me = THIS_MODULE,
- .hooks = (1 << NF_INET_LOCAL_OUT) |
- (1 << NF_INET_POST_ROUTING) |
- (1 << NF_INET_LOCAL_IN),
+static struct xt_match cgroup_mt_reg[] __read_mostly = {
+ {
+ .name = "cgroup",
+ .revision = 0,
+ .family = NFPROTO_UNSPEC,
+ .checkentry = cgroup_mt_check_v0,
+ .match = cgroup_mt_v0,
+ .matchsize = sizeof(struct xt_cgroup_info_v0),
+ .me = THIS_MODULE,
+ .hooks = (1 << NF_INET_LOCAL_OUT) |
+ (1 << NF_INET_POST_ROUTING) |
+ (1 << NF_INET_LOCAL_IN),
+ },
+ {
+ .name = "cgroup",
+ .revision = 1,
+ .family = NFPROTO_UNSPEC,
+ .checkentry = cgroup_mt_check_v1,
+ .match = cgroup_mt_v1,
+ .matchsize = sizeof(struct xt_cgroup_info_v1),
+ .destroy = cgroup_mt_destroy_v1,
+ .me = THIS_MODULE,
+ .hooks = (1 << NF_INET_LOCAL_OUT) |
+ (1 << NF_INET_POST_ROUTING) |
+ (1 << NF_INET_LOCAL_IN),
+ },
};
static int __init cgroup_mt_init(void)
{
- return xt_register_match(&cgroup_mt_reg);
+ return xt_register_matches(cgroup_mt_reg, ARRAY_SIZE(cgroup_mt_reg));
}
static void __exit cgroup_mt_exit(void)
{
- xt_unregister_match(&cgroup_mt_reg);
+ xt_unregister_matches(cgroup_mt_reg, ARRAY_SIZE(cgroup_mt_reg));
}
module_init(cgroup_mt_init);
diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c
index df8801e02a32..4e3c3affd285 100644
--- a/net/netfilter/xt_osf.c
+++ b/net/netfilter/xt_osf.c
@@ -61,8 +61,8 @@ static const struct nla_policy xt_osf_policy[OSF_ATTR_MAX + 1] = {
[OSF_ATTR_FINGER] = { .len = sizeof(struct xt_osf_user_finger) },
};
-static int xt_osf_add_callback(struct sock *ctnl, struct sk_buff *skb,
- const struct nlmsghdr *nlh,
+static int xt_osf_add_callback(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const osf_attrs[])
{
struct xt_osf_user_finger *f;
@@ -104,7 +104,8 @@ static int xt_osf_add_callback(struct sock *ctnl, struct sk_buff *skb,
return err;
}
-static int xt_osf_remove_callback(struct sock *ctnl, struct sk_buff *skb,
+static int xt_osf_remove_callback(struct net *net, struct sock *ctnl,
+ struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const osf_attrs[])
{
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 59651af8cc27..81dc1bb6e016 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -2915,6 +2915,7 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
cb = &nlk->cb;
memset(cb, 0, sizeof(*cb));
+ cb->start = control->start;
cb->dump = control->dump;
cb->done = control->done;
cb->nlh = nlh;
@@ -2927,6 +2928,9 @@ int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
mutex_unlock(nlk->cb_mutex);
+ if (cb->start)
+ cb->start(cb);
+
ret = netlink_dump(sk);
sock_put(sk);
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index bc0e504f33a6..8e63662c6fb0 100644
--- a/net/netlink/genetlink.c
+++ b/net/netlink/genetlink.c
@@ -513,6 +513,20 @@ void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq,
}
EXPORT_SYMBOL(genlmsg_put);
+static int genl_lock_start(struct netlink_callback *cb)
+{
+ /* our ops are always const - netlink API doesn't propagate that */
+ const struct genl_ops *ops = cb->data;
+ int rc = 0;
+
+ if (ops->start) {
+ genl_lock();
+ rc = ops->start(cb);
+ genl_unlock();
+ }
+ return rc;
+}
+
static int genl_lock_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
{
/* our ops are always const - netlink API doesn't propagate that */
@@ -577,6 +591,7 @@ static int genl_family_rcv_msg(struct genl_family *family,
.module = family->module,
/* we have const, but the netlink API doesn't */
.data = (void *)ops,
+ .start = genl_lock_start,
.dump = genl_lock_dumpit,
.done = genl_lock_done,
};
@@ -588,6 +603,7 @@ static int genl_family_rcv_msg(struct genl_family *family,
} else {
struct netlink_dump_control c = {
.module = family->module,
+ .start = ops->start,
.dump = ops->dumpit,
.done = ops->done,
};
diff --git a/net/nfc/core.c b/net/nfc/core.c
index 1fe3d3b362c0..122bb81da918 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -953,6 +953,19 @@ out:
}
EXPORT_SYMBOL(nfc_se_transaction);
+int nfc_se_connectivity(struct nfc_dev *dev, u8 se_idx)
+{
+ int rc;
+
+ pr_debug("connectivity: %x\n", se_idx);
+
+ device_lock(&dev->dev);
+ rc = nfc_genl_se_connectivity(dev, se_idx);
+ device_unlock(&dev->dev);
+ return rc;
+}
+EXPORT_SYMBOL(nfc_se_connectivity);
+
static void nfc_release(struct device *d)
{
struct nfc_dev *dev = to_nfc_dev(d);
diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c
index 23c2a118ac9f..dd9003f38822 100644
--- a/net/nfc/digital_core.c
+++ b/net/nfc/digital_core.c
@@ -20,7 +20,8 @@
#include "digital.h"
#define DIGITAL_PROTO_NFCA_RF_TECH \
- (NFC_PROTO_JEWEL_MASK | NFC_PROTO_MIFARE_MASK | NFC_PROTO_NFC_DEP_MASK)
+ (NFC_PROTO_JEWEL_MASK | NFC_PROTO_MIFARE_MASK | \
+ NFC_PROTO_NFC_DEP_MASK | NFC_PROTO_ISO14443_MASK)
#define DIGITAL_PROTO_NFCB_RF_TECH NFC_PROTO_ISO14443_B_MASK
diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c
index b7de0da46acd..ecf0a0196f18 100644
--- a/net/nfc/llcp_sock.c
+++ b/net/nfc/llcp_sock.c
@@ -572,7 +572,7 @@ static unsigned int llcp_sock_poll(struct file *file, struct socket *sock,
if (sock_writeable(sk) && sk->sk_state == LLCP_CONNECTED)
mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
else
- set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+ sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
pr_debug("mask 0x%x\n", mask);
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 10c99a578421..fbb7a2b57b44 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -610,14 +610,14 @@ int nci_core_conn_create(struct nci_dev *ndev, u8 destination_type,
struct nci_core_conn_create_cmd *cmd;
struct core_conn_create_data data;
+ if (!number_destination_params)
+ return -EINVAL;
+
data.length = params_len + sizeof(struct nci_core_conn_create_cmd);
cmd = kzalloc(data.length, GFP_KERNEL);
if (!cmd)
return -ENOMEM;
- if (!number_destination_params)
- return -EINVAL;
-
cmd->destination_type = destination_type;
cmd->number_destination_params = number_destination_params;
memcpy(cmd->params, params, params_len);
diff --git a/net/nfc/nci/hci.c b/net/nfc/nci/hci.c
index 2aedac15cb59..a0ab26d535dc 100644
--- a/net/nfc/nci/hci.c
+++ b/net/nfc/nci/hci.c
@@ -676,7 +676,7 @@ int nci_hci_connect_gate(struct nci_dev *ndev,
break;
default:
pipe = nci_hci_create_pipe(ndev, dest_host, dest_gate, &r);
- if (pipe < 0)
+ if (pipe == NCI_HCI_INVALID_PIPE)
return r;
pipe_created = true;
break;
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index f58c1fba1026..ea023b35f1c2 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -552,6 +552,43 @@ free_msg:
return -EMSGSIZE;
}
+int nfc_genl_se_connectivity(struct nfc_dev *dev, u8 se_idx)
+{
+ struct nfc_se *se;
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+ NFC_EVENT_SE_CONNECTIVITY);
+ if (!hdr)
+ goto free_msg;
+
+ se = nfc_find_se(dev, se_idx);
+ if (!se)
+ goto free_msg;
+
+ if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
+ nla_put_u32(msg, NFC_ATTR_SE_INDEX, se_idx) ||
+ nla_put_u8(msg, NFC_ATTR_SE_TYPE, se->type))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast(&nfc_genl_family, msg, 0, 0, GFP_KERNEL);
+
+ return 0;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+free_msg:
+ nlmsg_free(msg);
+ return -EMSGSIZE;
+}
+
static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
u32 portid, u32 seq,
struct netlink_callback *cb,
diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h
index c20b784ad720..6c6f76b370b1 100644
--- a/net/nfc/nfc.h
+++ b/net/nfc/nfc.h
@@ -105,6 +105,7 @@ int nfc_genl_se_added(struct nfc_dev *dev, u32 se_idx, u16 type);
int nfc_genl_se_removed(struct nfc_dev *dev, u32 se_idx);
int nfc_genl_se_transaction(struct nfc_dev *dev, u8 se_idx,
struct nfc_evt_transaction *evt_transaction);
+int nfc_genl_se_connectivity(struct nfc_dev *dev, u8 se_idx);
struct nfc_dev *nfc_get_device(unsigned int idx);
diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c
index c2cc11168fd5..ee6ff8ffc12d 100644
--- a/net/openvswitch/conntrack.c
+++ b/net/openvswitch/conntrack.c
@@ -53,6 +53,8 @@ struct ovs_conntrack_info {
struct md_labels labels;
};
+static void __ovs_ct_free_action(struct ovs_conntrack_info *ct_info);
+
static u16 key_to_nfproto(const struct sw_flow_key *key)
{
switch (ntohs(key->eth.type)) {
@@ -141,6 +143,7 @@ static void __ovs_ct_update_key(struct sw_flow_key *key, u8 state,
* previously sent the packet to conntrack via the ct action.
*/
static void ovs_ct_update_key(const struct sk_buff *skb,
+ const struct ovs_conntrack_info *info,
struct sw_flow_key *key, bool post_ct)
{
const struct nf_conntrack_zone *zone = &nf_ct_zone_dflt;
@@ -158,13 +161,15 @@ static void ovs_ct_update_key(const struct sk_buff *skb,
zone = nf_ct_zone(ct);
} else if (post_ct) {
state = OVS_CS_F_TRACKED | OVS_CS_F_INVALID;
+ if (info)
+ zone = &info->zone;
}
__ovs_ct_update_key(key, state, zone, ct);
}
void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key)
{
- ovs_ct_update_key(skb, key, false);
+ ovs_ct_update_key(skb, NULL, key, false);
}
int ovs_ct_put_key(const struct sw_flow_key *key, struct sk_buff *skb)
@@ -300,10 +305,10 @@ static int handle_fragments(struct net *net, struct sw_flow_key *key,
u16 zone, struct sk_buff *skb)
{
struct ovs_skb_cb ovs_cb = *OVS_CB(skb);
+ int err;
if (key->eth.type == htons(ETH_P_IP)) {
enum ip_defrag_users user = IP_DEFRAG_CONNTRACK_IN + zone;
- int err;
memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
err = ip_defrag(net, skb, user);
@@ -314,28 +319,13 @@ static int handle_fragments(struct net *net, struct sw_flow_key *key,
#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
} else if (key->eth.type == htons(ETH_P_IPV6)) {
enum ip6_defrag_users user = IP6_DEFRAG_CONNTRACK_IN + zone;
- struct sk_buff *reasm;
memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm));
- reasm = nf_ct_frag6_gather(net, skb, user);
- if (!reasm)
- return -EINPROGRESS;
-
- if (skb == reasm) {
- kfree_skb(skb);
- return -EINVAL;
- }
-
- /* Don't free 'skb' even though it is one of the original
- * fragments, as we're going to morph it into the head.
- */
- skb_get(skb);
- nf_ct_frag6_consume_orig(reasm);
+ err = nf_ct_frag6_gather(net, skb, user);
+ if (err)
+ return err;
- key->ip.proto = ipv6_hdr(reasm)->nexthdr;
- skb_morph(skb, reasm);
- skb->next = reasm->next;
- consume_skb(reasm);
+ key->ip.proto = ipv6_hdr(skb)->nexthdr;
ovs_cb.mru = IP6CB(skb)->frag_max_size;
#endif
} else {
@@ -418,7 +408,7 @@ static int __ovs_ct_lookup(struct net *net, struct sw_flow_key *key,
}
}
- ovs_ct_update_key(skb, key, true);
+ ovs_ct_update_key(skb, info, key, true);
return 0;
}
@@ -693,6 +683,10 @@ int ovs_ct_copy_action(struct net *net, const struct nlattr *attr,
OVS_NLERR(log, "Failed to allocate conntrack template");
return -ENOMEM;
}
+
+ __set_bit(IPS_CONFIRMED_BIT, &ct_info.ct->status);
+ nf_conntrack_get(&ct_info.ct->ct_general);
+
if (helper) {
err = ovs_ct_add_helper(&ct_info, helper, key, log);
if (err)
@@ -704,11 +698,9 @@ int ovs_ct_copy_action(struct net *net, const struct nlattr *attr,
if (err)
goto err_free_ct;
- __set_bit(IPS_CONFIRMED_BIT, &ct_info.ct->status);
- nf_conntrack_get(&ct_info.ct->ct_general);
return 0;
err_free_ct:
- nf_conntrack_free(ct_info.ct);
+ __ovs_ct_free_action(&ct_info);
return err;
}
@@ -750,6 +742,11 @@ void ovs_ct_free_action(const struct nlattr *a)
{
struct ovs_conntrack_info *ct_info = nla_data(a);
+ __ovs_ct_free_action(ct_info);
+}
+
+static void __ovs_ct_free_action(struct ovs_conntrack_info *ct_info)
+{
if (ct_info->helper)
module_put(ct_info->helper->me);
if (ct_info->ct)
diff --git a/net/openvswitch/dp_notify.c b/net/openvswitch/dp_notify.c
index a7a80a6b77b0..653d073bae45 100644
--- a/net/openvswitch/dp_notify.c
+++ b/net/openvswitch/dp_notify.c
@@ -58,7 +58,7 @@ void ovs_dp_notify_wq(struct work_struct *work)
struct hlist_node *n;
hlist_for_each_entry_safe(vport, n, &dp->ports[i], dp_hash_node) {
- if (vport->ops->type != OVS_VPORT_TYPE_NETDEV)
+ if (vport->ops->type == OVS_VPORT_TYPE_INTERNAL)
continue;
if (!(vport->dev->priv_flags & IFF_OVS_DATAPATH))
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index 907d6fd28ede..d1bd4a45ca2d 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -2434,7 +2434,10 @@ static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb)
if (!start)
return -EMSGSIZE;
- err = ovs_nla_put_tunnel_info(skb, tun_info);
+ err = ip_tun_to_nlattr(skb, &tun_info->key,
+ ip_tunnel_info_opts(tun_info),
+ tun_info->options_len,
+ ip_tunnel_info_af(tun_info));
if (err)
return err;
nla_nest_end(skb, start);
diff --git a/net/openvswitch/vport-geneve.c b/net/openvswitch/vport-geneve.c
index efb736bb6855..30ab8e127288 100644
--- a/net/openvswitch/vport-geneve.c
+++ b/net/openvswitch/vport-geneve.c
@@ -34,7 +34,7 @@ static struct vport_ops ovs_geneve_vport_ops;
* @dst_port: destination port.
*/
struct geneve_port {
- u16 port_no;
+ u16 dst_port;
};
static inline struct geneve_port *geneve_vport(const struct vport *vport)
@@ -47,7 +47,7 @@ static int geneve_get_options(const struct vport *vport,
{
struct geneve_port *geneve_port = geneve_vport(vport);
- if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, geneve_port->port_no))
+ if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, geneve_port->dst_port))
return -EMSGSIZE;
return 0;
}
@@ -83,7 +83,7 @@ static struct vport *geneve_tnl_create(const struct vport_parms *parms)
return vport;
geneve_port = geneve_vport(vport);
- geneve_port->port_no = dst_port;
+ geneve_port->dst_port = dst_port;
rtnl_lock();
dev = geneve_dev_create_fb(net, parms->name, NET_NAME_USER, dst_port);
@@ -117,7 +117,6 @@ static struct vport_ops ovs_geneve_vport_ops = {
.destroy = ovs_netdev_tunnel_destroy,
.get_options = geneve_get_options,
.send = dev_queue_xmit,
- .owner = THIS_MODULE,
};
static int __init ovs_geneve_tnl_init(void)
diff --git a/net/openvswitch/vport-gre.c b/net/openvswitch/vport-gre.c
index c3257d78d3d2..7f8897f33a67 100644
--- a/net/openvswitch/vport-gre.c
+++ b/net/openvswitch/vport-gre.c
@@ -89,7 +89,6 @@ static struct vport_ops ovs_gre_vport_ops = {
.create = gre_create,
.send = dev_queue_xmit,
.destroy = ovs_netdev_tunnel_destroy,
- .owner = THIS_MODULE,
};
static int __init ovs_gre_tnl_init(void)
diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c
index b327368a3848..6a6adf314363 100644
--- a/net/openvswitch/vport-netdev.c
+++ b/net/openvswitch/vport-netdev.c
@@ -105,7 +105,7 @@ struct vport *ovs_netdev_link(struct vport *vport, const char *name)
rtnl_lock();
err = netdev_master_upper_dev_link(vport->dev,
- get_dpdev(vport->dp));
+ get_dpdev(vport->dp), NULL, NULL);
if (err)
goto error_unlock;
@@ -180,9 +180,13 @@ void ovs_netdev_tunnel_destroy(struct vport *vport)
if (vport->dev->priv_flags & IFF_OVS_DATAPATH)
ovs_netdev_detach_dev(vport);
- /* Early release so we can unregister the device */
+ /* We can be invoked by both explicit vport deletion and
+ * underlying netdev deregistration; delete the link only
+ * if it's not already shutting down.
+ */
+ if (vport->dev->reg_state == NETREG_REGISTERED)
+ rtnl_delete_link(vport->dev);
dev_put(vport->dev);
- rtnl_delete_link(vport->dev);
vport->dev = NULL;
rtnl_unlock();
diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c
index 0ac0fd004d7e..31cbc8c5c7db 100644
--- a/net/openvswitch/vport.c
+++ b/net/openvswitch/vport.c
@@ -71,7 +71,7 @@ static struct hlist_head *hash_bucket(const struct net *net, const char *name)
return &dev_table[hash & (VPORT_HASH_BUCKETS - 1)];
}
-int ovs_vport_ops_register(struct vport_ops *ops)
+int __ovs_vport_ops_register(struct vport_ops *ops)
{
int err = -EEXIST;
struct vport_ops *o;
@@ -87,7 +87,7 @@ errout:
ovs_unlock();
return err;
}
-EXPORT_SYMBOL_GPL(ovs_vport_ops_register);
+EXPORT_SYMBOL_GPL(__ovs_vport_ops_register);
void ovs_vport_ops_unregister(struct vport_ops *ops)
{
@@ -256,8 +256,8 @@ int ovs_vport_set_options(struct vport *vport, struct nlattr *options)
*
* @vport: vport to delete.
*
- * Detaches @vport from its datapath and destroys it. It is possible to fail
- * for reasons such as lack of memory. ovs_mutex must be held.
+ * Detaches @vport from its datapath and destroys it. ovs_mutex must
+ * be held.
*/
void ovs_vport_del(struct vport *vport)
{
diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h
index bdfd82a7c064..c10899cb9040 100644
--- a/net/openvswitch/vport.h
+++ b/net/openvswitch/vport.h
@@ -70,7 +70,7 @@ struct vport_portids {
/**
* struct vport - one port within a datapath
- * @rcu: RCU callback head for deferred destruction.
+ * @dev: Pointer to net_device.
* @dp: Datapath to which this port belongs.
* @upcall_portids: RCU protected 'struct vport_portids'.
* @port_no: Index into @dp's @ports array.
@@ -78,6 +78,7 @@ struct vport_portids {
* @dp_hash_node: Element in @datapath->ports hash table in datapath.c.
* @ops: Class structure.
* @detach_list: list used for detaching vport in net-exit call.
+ * @rcu: RCU callback head for deferred destruction.
*/
struct vport {
struct net_device *dev;
@@ -196,28 +197,14 @@ static inline const char *ovs_vport_name(struct vport *vport)
return vport->dev->name;
}
-int ovs_vport_ops_register(struct vport_ops *ops);
-void ovs_vport_ops_unregister(struct vport_ops *ops);
-
-static inline struct rtable *ovs_tunnel_route_lookup(struct net *net,
- const struct ip_tunnel_key *key,
- u32 mark,
- struct flowi4 *fl,
- u8 protocol)
-{
- struct rtable *rt;
-
- memset(fl, 0, sizeof(*fl));
- fl->daddr = key->u.ipv4.dst;
- fl->saddr = key->u.ipv4.src;
- fl->flowi4_tos = RT_TOS(key->tos);
- fl->flowi4_mark = mark;
- fl->flowi4_proto = protocol;
-
- rt = ip_route_output_key(net, fl);
- return rt;
-}
+int __ovs_vport_ops_register(struct vport_ops *ops);
+#define ovs_vport_ops_register(ops) \
+ ({ \
+ (ops)->owner = THIS_MODULE; \
+ __ovs_vport_ops_register(ops); \
+ })
+void ovs_vport_ops_unregister(struct vport_ops *ops);
void ovs_vport_send(struct vport *vport, struct sk_buff *skb);
#endif /* vport.h */
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 1cf928fb573e..992396aa635c 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -2329,8 +2329,8 @@ static void tpacket_destruct_skb(struct sk_buff *skb)
static bool ll_header_truncated(const struct net_device *dev, int len)
{
/* net device doesn't like empty head */
- if (unlikely(len <= dev->hard_header_len)) {
- net_warn_ratelimited("%s: packet size is too short (%d <= %d)\n",
+ if (unlikely(len < dev->hard_header_len)) {
+ net_warn_ratelimited("%s: packet size is too short (%d < %d)\n",
current->comm, len, dev->hard_header_len);
return true;
}
diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c
index 10d42f3220ab..f925753668a7 100644
--- a/net/phonet/af_phonet.c
+++ b/net/phonet/af_phonet.c
@@ -377,6 +377,10 @@ static int phonet_rcv(struct sk_buff *skb, struct net_device *dev,
struct sockaddr_pn sa;
u16 len;
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ return NET_RX_DROP;
+
/* check we have at least a full Phonet header */
if (!pskb_pull(skb, sizeof(struct phonethdr)))
goto out;
diff --git a/net/rds/connection.c b/net/rds/connection.c
index d4564036a339..e3b118cae81d 100644
--- a/net/rds/connection.c
+++ b/net/rds/connection.c
@@ -186,12 +186,6 @@ static struct rds_connection *__rds_conn_create(struct net *net,
}
}
- if (trans == NULL) {
- kmem_cache_free(rds_conn_slab, conn);
- conn = ERR_PTR(-ENODEV);
- goto out;
- }
-
conn->c_trans = trans;
ret = trans->conn_alloc(conn, gfp);
diff --git a/net/rds/page.c b/net/rds/page.c
index 9005a2c920ee..5a14e6d6a926 100644
--- a/net/rds/page.c
+++ b/net/rds/page.c
@@ -179,37 +179,18 @@ out:
}
EXPORT_SYMBOL_GPL(rds_page_remainder_alloc);
-static int rds_page_remainder_cpu_notify(struct notifier_block *self,
- unsigned long action, void *hcpu)
+void rds_page_exit(void)
{
- struct rds_page_remainder *rem;
- long cpu = (long)hcpu;
+ unsigned int cpu;
- rem = &per_cpu(rds_page_remainders, cpu);
+ for_each_possible_cpu(cpu) {
+ struct rds_page_remainder *rem;
- rdsdebug("cpu %ld action 0x%lx\n", cpu, action);
+ rem = &per_cpu(rds_page_remainders, cpu);
+ rdsdebug("cpu %u\n", cpu);
- switch (action) {
- case CPU_DEAD:
if (rem->r_page)
__free_page(rem->r_page);
rem->r_page = NULL;
- break;
}
-
- return 0;
-}
-
-static struct notifier_block rds_page_remainder_nb = {
- .notifier_call = rds_page_remainder_cpu_notify,
-};
-
-void rds_page_exit(void)
-{
- int i;
-
- for_each_possible_cpu(i)
- rds_page_remainder_cpu_notify(&rds_page_remainder_nb,
- (unsigned long)CPU_DEAD,
- (void *)(long)i);
}
diff --git a/net/rds/send.c b/net/rds/send.c
index 827155c2ead1..c9cdb358ea88 100644
--- a/net/rds/send.c
+++ b/net/rds/send.c
@@ -1013,11 +1013,13 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
release_sock(sk);
}
- /* racing with another thread binding seems ok here */
+ lock_sock(sk);
if (daddr == 0 || rs->rs_bound_addr == 0) {
+ release_sock(sk);
ret = -ENOTCONN; /* XXX not a great errno */
goto out;
}
+ release_sock(sk);
if (payload_len > rds_sk_sndbuf(rs)) {
ret = -EMSGSIZE;
diff --git a/net/rfkill/core.c b/net/rfkill/core.c
index b41e9ea2ffff..f53bf3b6558b 100644
--- a/net/rfkill/core.c
+++ b/net/rfkill/core.c
@@ -49,7 +49,6 @@
struct rfkill {
spinlock_t lock;
- const char *name;
enum rfkill_type type;
unsigned long state;
@@ -73,6 +72,7 @@ struct rfkill {
struct delayed_work poll_work;
struct work_struct uevent_work;
struct work_struct sync_work;
+ char name[];
};
#define to_rfkill(d) container_of(d, struct rfkill, dev)
@@ -876,14 +876,14 @@ struct rfkill * __must_check rfkill_alloc(const char *name,
if (WARN_ON(type == RFKILL_TYPE_ALL || type >= NUM_RFKILL_TYPES))
return NULL;
- rfkill = kzalloc(sizeof(*rfkill), GFP_KERNEL);
+ rfkill = kzalloc(sizeof(*rfkill) + strlen(name) + 1, GFP_KERNEL);
if (!rfkill)
return NULL;
spin_lock_init(&rfkill->lock);
INIT_LIST_HEAD(&rfkill->node);
rfkill->type = type;
- rfkill->name = name;
+ strcpy(rfkill->name, name);
rfkill->ops = ops;
rfkill->data = ops_data;
diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c
index 93127220cb54..4b1e3f35f06c 100644
--- a/net/rfkill/rfkill-gpio.c
+++ b/net/rfkill/rfkill-gpio.c
@@ -163,10 +163,6 @@ static int rfkill_gpio_remove(struct platform_device *pdev)
#ifdef CONFIG_ACPI
static const struct acpi_device_id rfkill_acpi_match[] = {
- { "BCM2E1A", RFKILL_TYPE_BLUETOOTH },
- { "BCM2E3D", RFKILL_TYPE_BLUETOOTH },
- { "BCM2E40", RFKILL_TYPE_BLUETOOTH },
- { "BCM2E64", RFKILL_TYPE_BLUETOOTH },
{ "BCM4752", RFKILL_TYPE_GPS },
{ "LNV4752", RFKILL_TYPE_GPS },
{ },
diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c
index 1f8a144a5dc2..7e2d1057d8bc 100644
--- a/net/rxrpc/af_rxrpc.c
+++ b/net/rxrpc/af_rxrpc.c
@@ -67,7 +67,7 @@ static void rxrpc_write_space(struct sock *sk)
if (rxrpc_writable(sk)) {
struct socket_wq *wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible(&wq->wait);
sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
}
diff --git a/net/rxrpc/ar-ack.c b/net/rxrpc/ar-ack.c
index e0547f521f20..adc555e0323d 100644
--- a/net/rxrpc/ar-ack.c
+++ b/net/rxrpc/ar-ack.c
@@ -723,8 +723,10 @@ process_further:
if ((call->state == RXRPC_CALL_CLIENT_AWAIT_REPLY ||
call->state == RXRPC_CALL_SERVER_AWAIT_ACK) &&
- hard > tx)
+ hard > tx) {
+ call->acks_hard = tx;
goto all_acked;
+ }
smp_rmb();
rxrpc_rotate_tx_window(call, hard - 1);
diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c
index da3cc09f683e..3f6571651d32 100644
--- a/net/rxrpc/ar-key.c
+++ b/net/rxrpc/ar-key.c
@@ -896,15 +896,9 @@ int rxrpc_request_key(struct rxrpc_sock *rx, char __user *optval, int optlen)
if (optlen <= 0 || optlen > PAGE_SIZE - 1)
return -EINVAL;
- description = kmalloc(optlen + 1, GFP_KERNEL);
- if (!description)
- return -ENOMEM;
-
- if (copy_from_user(description, optval, optlen)) {
- kfree(description);
- return -EFAULT;
- }
- description[optlen] = 0;
+ description = memdup_user_nul(optval, optlen);
+ if (IS_ERR(description))
+ return PTR_ERR(description);
key = request_key(&key_type_rxrpc, description, NULL);
if (IS_ERR(key)) {
@@ -933,15 +927,9 @@ int rxrpc_server_keyring(struct rxrpc_sock *rx, char __user *optval,
if (optlen <= 0 || optlen > PAGE_SIZE - 1)
return -EINVAL;
- description = kmalloc(optlen + 1, GFP_KERNEL);
- if (!description)
- return -ENOMEM;
-
- if (copy_from_user(description, optval, optlen)) {
- kfree(description);
- return -EFAULT;
- }
- description[optlen] = 0;
+ description = memdup_user_nul(optval, optlen);
+ if (IS_ERR(description))
+ return PTR_ERR(description);
key = request_key(&key_type_keyring, description, NULL);
if (IS_ERR(key)) {
diff --git a/net/rxrpc/ar-output.c b/net/rxrpc/ar-output.c
index a40d3afe93b7..14c4e12c47b0 100644
--- a/net/rxrpc/ar-output.c
+++ b/net/rxrpc/ar-output.c
@@ -531,7 +531,7 @@ static int rxrpc_send_data(struct rxrpc_sock *rx,
timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
/* this should be in poll */
- clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+ sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk);
if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
return -EPIPE;
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index daa33432b716..82830824fb1f 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -310,15 +310,21 @@ config NET_SCH_PIE
If unsure, say N.
config NET_SCH_INGRESS
- tristate "Ingress Qdisc"
+ tristate "Ingress/classifier-action Qdisc"
depends on NET_CLS_ACT
select NET_INGRESS
+ select NET_EGRESS
---help---
- Say Y here if you want to use classifiers for incoming packets.
+ Say Y here if you want to use classifiers for incoming and/or outgoing
+ packets. This qdisc doesn't do anything else besides running classifiers,
+ which can also have actions attached to them. In case of outgoing packets,
+ classifiers that this qdisc holds are executed in the transmit path
+ before real enqueuing to an egress qdisc happens.
+
If unsure, say Y.
- To compile this code as a module, choose M here: the
- module will be called sch_ingress.
+ To compile this code as a module, choose M here: the module will be
+ called sch_ingress with alias of sch_clsact.
config NET_SCH_PLUG
tristate "Plug network traffic until release (PLUG)"
diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c
index 5faaa5425f7b..8dc84300ee79 100644
--- a/net/sched/cls_bpf.c
+++ b/net/sched/cls_bpf.c
@@ -79,12 +79,8 @@ static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
struct tcf_result *res)
{
struct cls_bpf_head *head = rcu_dereference_bh(tp->root);
+ bool at_ingress = skb_at_tc_ingress(skb);
struct cls_bpf_prog *prog;
-#ifdef CONFIG_NET_CLS_ACT
- bool at_ingress = G_TC_AT(skb->tc_verd) & AT_INGRESS;
-#else
- bool at_ingress = false;
-#endif
int ret = -1;
if (unlikely(!skb_mac_header_was_set(skb)))
@@ -295,7 +291,7 @@ static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog,
prog->bpf_name = name;
prog->filter = fp;
- if (fp->dst_needed)
+ if (fp->dst_needed && !(tp->q->flags & TCQ_F_INGRESS))
netif_keep_dst(qdisc_dev(tp->q));
return 0;
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 57692947ebbe..95b021243233 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -252,23 +252,28 @@ static int fl_set_key(struct net *net, struct nlattr **tb,
fl_set_key_val(tb, key->eth.src, TCA_FLOWER_KEY_ETH_SRC,
mask->eth.src, TCA_FLOWER_KEY_ETH_SRC_MASK,
sizeof(key->eth.src));
+
fl_set_key_val(tb, &key->basic.n_proto, TCA_FLOWER_KEY_ETH_TYPE,
&mask->basic.n_proto, TCA_FLOWER_UNSPEC,
sizeof(key->basic.n_proto));
+
if (key->basic.n_proto == htons(ETH_P_IP) ||
key->basic.n_proto == htons(ETH_P_IPV6)) {
fl_set_key_val(tb, &key->basic.ip_proto, TCA_FLOWER_KEY_IP_PROTO,
&mask->basic.ip_proto, TCA_FLOWER_UNSPEC,
sizeof(key->basic.ip_proto));
}
- if (key->control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
+
+ if (tb[TCA_FLOWER_KEY_IPV4_SRC] || tb[TCA_FLOWER_KEY_IPV4_DST]) {
+ key->control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
fl_set_key_val(tb, &key->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC,
&mask->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC_MASK,
sizeof(key->ipv4.src));
fl_set_key_val(tb, &key->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST,
&mask->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST_MASK,
sizeof(key->ipv4.dst));
- } else if (key->control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
+ } else if (tb[TCA_FLOWER_KEY_IPV6_SRC] || tb[TCA_FLOWER_KEY_IPV6_DST]) {
+ key->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
fl_set_key_val(tb, &key->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC,
&mask->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC_MASK,
sizeof(key->ipv6.src));
@@ -276,6 +281,7 @@ static int fl_set_key(struct net *net, struct nlattr **tb,
&mask->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST_MASK,
sizeof(key->ipv6.dst));
}
+
if (key->basic.ip_proto == IPPROTO_TCP) {
fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_TCP_SRC,
&mask->tp.src, TCA_FLOWER_UNSPEC,
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index f43c8f33f09e..b5c2cf2aa6d4 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -253,7 +253,8 @@ int qdisc_set_default(const char *name)
}
/* We know handle. Find qdisc among all qdisc's attached to device
- (root qdisc, all its children, children of children etc.)
+ * (root qdisc, all its children, children of children etc.)
+ * Note: caller either uses rtnl or rcu_read_lock()
*/
static struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle)
@@ -264,7 +265,7 @@ static struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle)
root->handle == handle)
return root;
- list_for_each_entry(q, &root->list, list) {
+ list_for_each_entry_rcu(q, &root->list, list) {
if (q->handle == handle)
return q;
}
@@ -277,15 +278,18 @@ void qdisc_list_add(struct Qdisc *q)
struct Qdisc *root = qdisc_dev(q)->qdisc;
WARN_ON_ONCE(root == &noop_qdisc);
- list_add_tail(&q->list, &root->list);
+ ASSERT_RTNL();
+ list_add_tail_rcu(&q->list, &root->list);
}
}
EXPORT_SYMBOL(qdisc_list_add);
void qdisc_list_del(struct Qdisc *q)
{
- if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS))
- list_del(&q->list);
+ if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) {
+ ASSERT_RTNL();
+ list_del_rcu(&q->list);
+ }
}
EXPORT_SYMBOL(qdisc_list_del);
@@ -750,14 +754,18 @@ void qdisc_tree_decrease_qlen(struct Qdisc *sch, unsigned int n)
if (n == 0)
return;
drops = max_t(int, n, 0);
+ rcu_read_lock();
while ((parentid = sch->parent)) {
if (TC_H_MAJ(parentid) == TC_H_MAJ(TC_H_INGRESS))
- return;
+ break;
+ if (sch->flags & TCQ_F_NOPARENT)
+ break;
+ /* TODO: perform the search on a per txq basis */
sch = qdisc_lookup(qdisc_dev(sch), TC_H_MAJ(parentid));
if (sch == NULL) {
- WARN_ON(parentid != TC_H_ROOT);
- return;
+ WARN_ON_ONCE(parentid != TC_H_ROOT);
+ break;
}
cops = sch->ops->cl_ops;
if (cops->qlen_notify) {
@@ -768,6 +776,7 @@ void qdisc_tree_decrease_qlen(struct Qdisc *sch, unsigned int n)
sch->q.qlen -= n;
__qdisc_qstats_drop(sch, drops);
}
+ rcu_read_unlock();
}
EXPORT_SYMBOL(qdisc_tree_decrease_qlen);
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index cb5d4ad32946..16bc83b2842a 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -658,8 +658,10 @@ static void qdisc_rcu_free(struct rcu_head *head)
{
struct Qdisc *qdisc = container_of(head, struct Qdisc, rcu_head);
- if (qdisc_is_percpu_stats(qdisc))
+ if (qdisc_is_percpu_stats(qdisc)) {
free_percpu(qdisc->cpu_bstats);
+ free_percpu(qdisc->cpu_qstats);
+ }
kfree((char *) qdisc - qdisc->padded);
}
@@ -737,7 +739,7 @@ static void attach_one_default_qdisc(struct net_device *dev,
return;
}
if (!netif_is_multiqueue(dev))
- qdisc->flags |= TCQ_F_ONETXQUEUE;
+ qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
dev_queue->qdisc_sleeping = qdisc;
}
diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c
index e7c648fa9dc3..10adbc617905 100644
--- a/net/sched/sch_ingress.c
+++ b/net/sched/sch_ingress.c
@@ -1,4 +1,5 @@
-/* net/sched/sch_ingress.c - Ingress qdisc
+/* net/sched/sch_ingress.c - Ingress and clsact qdisc
+ *
* 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
@@ -98,17 +99,100 @@ static struct Qdisc_ops ingress_qdisc_ops __read_mostly = {
.owner = THIS_MODULE,
};
+static unsigned long clsact_get(struct Qdisc *sch, u32 classid)
+{
+ switch (TC_H_MIN(classid)) {
+ case TC_H_MIN(TC_H_MIN_INGRESS):
+ case TC_H_MIN(TC_H_MIN_EGRESS):
+ return TC_H_MIN(classid);
+ default:
+ return 0;
+ }
+}
+
+static unsigned long clsact_bind_filter(struct Qdisc *sch,
+ unsigned long parent, u32 classid)
+{
+ return clsact_get(sch, classid);
+}
+
+static struct tcf_proto __rcu **clsact_find_tcf(struct Qdisc *sch,
+ unsigned long cl)
+{
+ struct net_device *dev = qdisc_dev(sch);
+
+ switch (cl) {
+ case TC_H_MIN(TC_H_MIN_INGRESS):
+ return &dev->ingress_cl_list;
+ case TC_H_MIN(TC_H_MIN_EGRESS):
+ return &dev->egress_cl_list;
+ default:
+ return NULL;
+ }
+}
+
+static int clsact_init(struct Qdisc *sch, struct nlattr *opt)
+{
+ net_inc_ingress_queue();
+ net_inc_egress_queue();
+
+ sch->flags |= TCQ_F_CPUSTATS;
+
+ return 0;
+}
+
+static void clsact_destroy(struct Qdisc *sch)
+{
+ struct net_device *dev = qdisc_dev(sch);
+
+ tcf_destroy_chain(&dev->ingress_cl_list);
+ tcf_destroy_chain(&dev->egress_cl_list);
+
+ net_dec_ingress_queue();
+ net_dec_egress_queue();
+}
+
+static const struct Qdisc_class_ops clsact_class_ops = {
+ .leaf = ingress_leaf,
+ .get = clsact_get,
+ .put = ingress_put,
+ .walk = ingress_walk,
+ .tcf_chain = clsact_find_tcf,
+ .bind_tcf = clsact_bind_filter,
+ .unbind_tcf = ingress_put,
+};
+
+static struct Qdisc_ops clsact_qdisc_ops __read_mostly = {
+ .cl_ops = &clsact_class_ops,
+ .id = "clsact",
+ .init = clsact_init,
+ .destroy = clsact_destroy,
+ .dump = ingress_dump,
+ .owner = THIS_MODULE,
+};
+
static int __init ingress_module_init(void)
{
- return register_qdisc(&ingress_qdisc_ops);
+ int ret;
+
+ ret = register_qdisc(&ingress_qdisc_ops);
+ if (!ret) {
+ ret = register_qdisc(&clsact_qdisc_ops);
+ if (ret)
+ unregister_qdisc(&ingress_qdisc_ops);
+ }
+
+ return ret;
}
static void __exit ingress_module_exit(void)
{
unregister_qdisc(&ingress_qdisc_ops);
+ unregister_qdisc(&clsact_qdisc_ops);
}
module_init(ingress_module_init);
module_exit(ingress_module_exit);
+MODULE_ALIAS("sch_clsact");
MODULE_LICENSE("GPL");
diff --git a/net/sched/sch_mq.c b/net/sched/sch_mq.c
index f3cbaecd283a..3e82f047caaf 100644
--- a/net/sched/sch_mq.c
+++ b/net/sched/sch_mq.c
@@ -63,7 +63,7 @@ static int mq_init(struct Qdisc *sch, struct nlattr *opt)
if (qdisc == NULL)
goto err;
priv->qdiscs[ntx] = qdisc;
- qdisc->flags |= TCQ_F_ONETXQUEUE;
+ qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
}
sch->flags |= TCQ_F_MQROOT;
@@ -156,7 +156,7 @@ static int mq_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new,
*old = dev_graft_qdisc(dev_queue, new);
if (new)
- new->flags |= TCQ_F_ONETXQUEUE;
+ new->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
if (dev->flags & IFF_UP)
dev_activate(dev);
return 0;
diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c
index 3811a745452c..ad70ecf57ce7 100644
--- a/net/sched/sch_mqprio.c
+++ b/net/sched/sch_mqprio.c
@@ -132,7 +132,7 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt)
goto err;
}
priv->qdiscs[i] = qdisc;
- qdisc->flags |= TCQ_F_ONETXQUEUE;
+ qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
}
/* If the mqprio options indicate that hardware should own
@@ -209,7 +209,7 @@ static int mqprio_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new,
*old = dev_graft_qdisc(dev_queue, new);
if (new)
- new->flags |= TCQ_F_ONETXQUEUE;
+ new->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
if (dev->flags & IFF_UP)
dev_activate(dev);
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 559afd0ee7de..2bf8ec92dde4 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -383,6 +383,7 @@ void sctp_association_free(struct sctp_association *asoc)
list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, struct sctp_transport, transports);
list_del_rcu(pos);
+ sctp_unhash_transport(transport);
sctp_transport_free(transport);
}
@@ -500,6 +501,8 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc,
/* Remove this peer from the list. */
list_del_rcu(&peer->transports);
+ /* Remove this peer from the transport hashtable */
+ sctp_unhash_transport(peer);
/* Get the first transport of asoc. */
pos = asoc->peer.transport_addr_list.next;
@@ -699,6 +702,8 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
/* Attach the remote transport to our asoc. */
list_add_tail_rcu(&peer->transports, &asoc->peer.transport_addr_list);
asoc->peer.transport_count++;
+ /* Add this peer into the transport hashtable */
+ sctp_hash_transport(peer);
/* If we do not yet have a primary path, set one. */
if (!asoc->peer.primary_path) {
diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c
index 9da76ba4d10f..52838eaa1582 100644
--- a/net/sctp/endpointola.c
+++ b/net/sctp/endpointola.c
@@ -314,21 +314,16 @@ struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *ep,
}
/* Find the association that goes with this chunk.
- * We do a linear search of the associations for this endpoint.
- * We return the matching transport address too.
+ * We lookup the transport from hashtable at first, then get association
+ * through t->assoc.
*/
-static struct sctp_association *__sctp_endpoint_lookup_assoc(
+struct sctp_association *sctp_endpoint_lookup_assoc(
const struct sctp_endpoint *ep,
const union sctp_addr *paddr,
struct sctp_transport **transport)
{
struct sctp_association *asoc = NULL;
- struct sctp_association *tmp;
- struct sctp_transport *t = NULL;
- struct sctp_hashbucket *head;
- struct sctp_ep_common *epb;
- int hash;
- int rport;
+ struct sctp_transport *t;
*transport = NULL;
@@ -337,45 +332,16 @@ static struct sctp_association *__sctp_endpoint_lookup_assoc(
*/
if (!ep->base.bind_addr.port)
goto out;
+ t = sctp_epaddr_lookup_transport(ep, paddr);
+ if (!t || t->asoc->temp)
+ goto out;
- rport = ntohs(paddr->v4.sin_port);
-
- hash = sctp_assoc_hashfn(sock_net(ep->base.sk), ep->base.bind_addr.port,
- rport);
- head = &sctp_assoc_hashtable[hash];
- read_lock(&head->lock);
- sctp_for_each_hentry(epb, &head->chain) {
- tmp = sctp_assoc(epb);
- if (tmp->ep != ep || rport != tmp->peer.port)
- continue;
-
- t = sctp_assoc_lookup_paddr(tmp, paddr);
- if (t) {
- asoc = tmp;
- *transport = t;
- break;
- }
- }
- read_unlock(&head->lock);
+ *transport = t;
+ asoc = t->asoc;
out:
return asoc;
}
-/* Lookup association on an endpoint based on a peer address. BH-safe. */
-struct sctp_association *sctp_endpoint_lookup_assoc(
- const struct sctp_endpoint *ep,
- const union sctp_addr *paddr,
- struct sctp_transport **transport)
-{
- struct sctp_association *asoc;
-
- local_bh_disable();
- asoc = __sctp_endpoint_lookup_assoc(ep, paddr, transport);
- local_bh_enable();
-
- return asoc;
-}
-
/* Look for any peeled off association from the endpoint that matches the
* given peer address.
*/
diff --git a/net/sctp/input.c b/net/sctp/input.c
index b6493b3f11a9..d9a6e66c5c8a 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -782,65 +782,135 @@ hit:
return ep;
}
-/* Insert association into the hash table. */
-static void __sctp_hash_established(struct sctp_association *asoc)
+/* rhashtable for transport */
+struct sctp_hash_cmp_arg {
+ const union sctp_addr *laddr;
+ const union sctp_addr *paddr;
+ const struct net *net;
+};
+
+static inline int sctp_hash_cmp(struct rhashtable_compare_arg *arg,
+ const void *ptr)
{
- struct net *net = sock_net(asoc->base.sk);
- struct sctp_ep_common *epb;
- struct sctp_hashbucket *head;
+ const struct sctp_hash_cmp_arg *x = arg->key;
+ const struct sctp_transport *t = ptr;
+ struct sctp_association *asoc = t->asoc;
+ const struct net *net = x->net;
- epb = &asoc->base;
+ if (x->laddr->v4.sin_port != htons(asoc->base.bind_addr.port))
+ return 1;
+ if (!sctp_cmp_addr_exact(&t->ipaddr, x->paddr))
+ return 1;
+ if (!net_eq(sock_net(asoc->base.sk), net))
+ return 1;
+ if (!sctp_bind_addr_match(&asoc->base.bind_addr,
+ x->laddr, sctp_sk(asoc->base.sk)))
+ return 1;
- /* Calculate which chain this entry will belong to. */
- epb->hashent = sctp_assoc_hashfn(net, epb->bind_addr.port,
- asoc->peer.port);
+ return 0;
+}
- head = &sctp_assoc_hashtable[epb->hashent];
+static inline u32 sctp_hash_obj(const void *data, u32 len, u32 seed)
+{
+ const struct sctp_transport *t = data;
+ const union sctp_addr *paddr = &t->ipaddr;
+ const struct net *net = sock_net(t->asoc->base.sk);
+ u16 lport = htons(t->asoc->base.bind_addr.port);
+ u32 addr;
+
+ if (paddr->sa.sa_family == AF_INET6)
+ addr = jhash(&paddr->v6.sin6_addr, 16, seed);
+ else
+ addr = paddr->v4.sin_addr.s_addr;
- write_lock(&head->lock);
- hlist_add_head(&epb->node, &head->chain);
- write_unlock(&head->lock);
+ return jhash_3words(addr, ((__u32)paddr->v4.sin_port) << 16 |
+ (__force __u32)lport, net_hash_mix(net), seed);
}
-/* Add an association to the hash. Local BH-safe. */
-void sctp_hash_established(struct sctp_association *asoc)
+static inline u32 sctp_hash_key(const void *data, u32 len, u32 seed)
{
- if (asoc->temp)
- return;
+ const struct sctp_hash_cmp_arg *x = data;
+ const union sctp_addr *paddr = x->paddr;
+ const struct net *net = x->net;
+ u16 lport = x->laddr->v4.sin_port;
+ u32 addr;
+
+ if (paddr->sa.sa_family == AF_INET6)
+ addr = jhash(&paddr->v6.sin6_addr, 16, seed);
+ else
+ addr = paddr->v4.sin_addr.s_addr;
- local_bh_disable();
- __sctp_hash_established(asoc);
- local_bh_enable();
+ return jhash_3words(addr, ((__u32)paddr->v4.sin_port) << 16 |
+ (__force __u32)lport, net_hash_mix(net), seed);
}
-/* Remove association from the hash table. */
-static void __sctp_unhash_established(struct sctp_association *asoc)
+static const struct rhashtable_params sctp_hash_params = {
+ .head_offset = offsetof(struct sctp_transport, node),
+ .hashfn = sctp_hash_key,
+ .obj_hashfn = sctp_hash_obj,
+ .obj_cmpfn = sctp_hash_cmp,
+ .automatic_shrinking = true,
+};
+
+int sctp_transport_hashtable_init(void)
{
- struct net *net = sock_net(asoc->base.sk);
- struct sctp_hashbucket *head;
- struct sctp_ep_common *epb;
+ return rhashtable_init(&sctp_transport_hashtable, &sctp_hash_params);
+}
- epb = &asoc->base;
+void sctp_transport_hashtable_destroy(void)
+{
+ rhashtable_destroy(&sctp_transport_hashtable);
+}
- epb->hashent = sctp_assoc_hashfn(net, epb->bind_addr.port,
- asoc->peer.port);
+void sctp_hash_transport(struct sctp_transport *t)
+{
+ struct sctp_sockaddr_entry *addr;
+ struct sctp_hash_cmp_arg arg;
+
+ addr = list_entry(t->asoc->base.bind_addr.address_list.next,
+ struct sctp_sockaddr_entry, list);
+ arg.laddr = &addr->a;
+ arg.paddr = &t->ipaddr;
+ arg.net = sock_net(t->asoc->base.sk);
+
+reinsert:
+ if (rhashtable_lookup_insert_key(&sctp_transport_hashtable, &arg,
+ &t->node, sctp_hash_params) == -EBUSY)
+ goto reinsert;
+}
- head = &sctp_assoc_hashtable[epb->hashent];
+void sctp_unhash_transport(struct sctp_transport *t)
+{
+ rhashtable_remove_fast(&sctp_transport_hashtable, &t->node,
+ sctp_hash_params);
+}
- write_lock(&head->lock);
- hlist_del_init(&epb->node);
- write_unlock(&head->lock);
+struct sctp_transport *sctp_addrs_lookup_transport(
+ struct net *net,
+ const union sctp_addr *laddr,
+ const union sctp_addr *paddr)
+{
+ struct sctp_hash_cmp_arg arg = {
+ .laddr = laddr,
+ .paddr = paddr,
+ .net = net,
+ };
+
+ return rhashtable_lookup_fast(&sctp_transport_hashtable, &arg,
+ sctp_hash_params);
}
-/* Remove association from the hash table. Local BH-safe. */
-void sctp_unhash_established(struct sctp_association *asoc)
+struct sctp_transport *sctp_epaddr_lookup_transport(
+ const struct sctp_endpoint *ep,
+ const union sctp_addr *paddr)
{
- if (asoc->temp)
- return;
+ struct sctp_sockaddr_entry *addr;
+ struct net *net = sock_net(ep->base.sk);
- local_bh_disable();
- __sctp_unhash_established(asoc);
- local_bh_enable();
+ addr = list_entry(ep->base.bind_addr.address_list.next,
+ struct sctp_sockaddr_entry, list);
+
+ return sctp_addrs_lookup_transport(net, &addr->a, paddr);
}
/* Look up an association. */
@@ -850,38 +920,19 @@ static struct sctp_association *__sctp_lookup_association(
const union sctp_addr *peer,
struct sctp_transport **pt)
{
- struct sctp_hashbucket *head;
- struct sctp_ep_common *epb;
- struct sctp_association *asoc;
- struct sctp_transport *transport;
- int hash;
+ struct sctp_transport *t;
- /* Optimize here for direct hit, only listening connections can
- * have wildcards anyways.
- */
- hash = sctp_assoc_hashfn(net, ntohs(local->v4.sin_port),
- ntohs(peer->v4.sin_port));
- head = &sctp_assoc_hashtable[hash];
- read_lock(&head->lock);
- sctp_for_each_hentry(epb, &head->chain) {
- asoc = sctp_assoc(epb);
- transport = sctp_assoc_is_match(asoc, net, local, peer);
- if (transport)
- goto hit;
- }
-
- read_unlock(&head->lock);
+ t = sctp_addrs_lookup_transport(net, local, peer);
+ if (!t || t->dead || t->asoc->temp)
+ return NULL;
- return NULL;
+ sctp_association_hold(t->asoc);
+ *pt = t;
-hit:
- *pt = transport;
- sctp_association_hold(asoc);
- read_unlock(&head->lock);
- return asoc;
+ return t->asoc;
}
-/* Look up an association. BH-safe. */
+/* Look up an association. protected by RCU read lock */
static
struct sctp_association *sctp_lookup_association(struct net *net,
const union sctp_addr *laddr,
@@ -890,9 +941,9 @@ struct sctp_association *sctp_lookup_association(struct net *net,
{
struct sctp_association *asoc;
- local_bh_disable();
+ rcu_read_lock();
asoc = __sctp_lookup_association(net, laddr, paddr, transportp);
- local_bh_enable();
+ rcu_read_unlock();
return asoc;
}
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index e917d27328ea..ec529121f38a 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -209,6 +209,7 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport)
struct sock *sk = skb->sk;
struct ipv6_pinfo *np = inet6_sk(sk);
struct flowi6 *fl6 = &transport->fl.u.ip6;
+ int res;
pr_debug("%s: skb:%p, len:%d, src:%pI6 dst:%pI6\n", __func__, skb,
skb->len, &fl6->saddr, &fl6->daddr);
@@ -220,7 +221,10 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport)
SCTP_INC_STATS(sock_net(sk), SCTP_MIB_OUTSCTPPACKS);
- return ip6_xmit(sk, skb, fl6, np->opt, np->tclass);
+ rcu_read_lock();
+ res = ip6_xmit(sk, skb, fl6, rcu_dereference(np->opt), np->tclass);
+ rcu_read_unlock();
+ return res;
}
/* Returns the dst cache entry for the given source and destination ip
@@ -262,7 +266,10 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
pr_debug("src=%pI6 - ", &fl6->saddr);
}
- final_p = fl6_update_dst(fl6, np->opt, &final);
+ rcu_read_lock();
+ final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final);
+ rcu_read_unlock();
+
dst = ip6_dst_lookup_flow(sk, fl6, final_p);
if (!asoc || saddr)
goto out;
@@ -316,14 +323,13 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
}
}
}
- rcu_read_unlock();
-
if (baddr) {
fl6->saddr = baddr->v6.sin6_addr;
fl6->fl6_sport = baddr->v6.sin6_port;
- final_p = fl6_update_dst(fl6, np->opt, &final);
+ final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final);
dst = ip6_dst_lookup_flow(sk, fl6, final_p);
}
+ rcu_read_unlock();
out:
if (!IS_ERR_OR_NULL(dst)) {
@@ -635,6 +641,7 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk,
struct sock *newsk;
struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
struct sctp6_sock *newsctp6sk;
+ struct ipv6_txoptions *opt;
newsk = sk_alloc(sock_net(sk), PF_INET6, GFP_KERNEL, sk->sk_prot, 0);
if (!newsk)
@@ -654,6 +661,13 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk,
memcpy(newnp, np, sizeof(struct ipv6_pinfo));
+ rcu_read_lock();
+ opt = rcu_dereference(np->opt);
+ if (opt)
+ opt = ipv6_dup_options(newsk, opt);
+ RCU_INIT_POINTER(newnp->opt, opt);
+ rcu_read_unlock();
+
/* Initialize sk's sport, dport, rcv_saddr and daddr for getsockname()
* and getpeername().
*/
diff --git a/net/sctp/output.c b/net/sctp/output.c
index abe7c2db2412..9d610eddd19e 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -534,7 +534,7 @@ int sctp_packet_transmit(struct sctp_packet *packet)
* by CRC32-C as described in <draft-ietf-tsvwg-sctpcsum-02.txt>.
*/
if (!sctp_checksum_disable) {
- if (!(dst->dev->features & NETIF_F_SCTP_CSUM) ||
+ if (!(dst->dev->features & NETIF_F_SCTP_CRC) ||
(dst_xfrm(dst) != NULL) || packet->ipfragok) {
sh->checksum = sctp_compute_cksum(nskb, 0);
} else {
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
index 7e8f0a117106..c0380cfb16ae 100644
--- a/net/sctp/outqueue.c
+++ b/net/sctp/outqueue.c
@@ -324,6 +324,7 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk)
sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) :
"illegal chunk");
+ sctp_chunk_hold(chunk);
sctp_outq_tail_data(q, chunk);
if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
SCTP_INC_STATS(net, SCTP_MIB_OUTUNORDERCHUNKS);
@@ -1251,6 +1252,7 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_chunk *chunk)
*/
sack_a_rwnd = ntohl(sack->a_rwnd);
+ asoc->peer.zero_window_announced = !sack_a_rwnd;
outstanding = q->outstanding_bytes;
if (outstanding < sack_a_rwnd)
diff --git a/net/sctp/proc.c b/net/sctp/proc.c
index 0697eda5aed8..dfa7eeccb537 100644
--- a/net/sctp/proc.c
+++ b/net/sctp/proc.c
@@ -281,88 +281,136 @@ void sctp_eps_proc_exit(struct net *net)
remove_proc_entry("eps", net->sctp.proc_net_sctp);
}
+struct sctp_ht_iter {
+ struct seq_net_private p;
+ struct rhashtable_iter hti;
+};
-static void *sctp_assocs_seq_start(struct seq_file *seq, loff_t *pos)
+static struct sctp_transport *sctp_transport_get_next(struct seq_file *seq)
{
- if (*pos >= sctp_assoc_hashsize)
- return NULL;
+ struct sctp_ht_iter *iter = seq->private;
+ struct sctp_transport *t;
- if (*pos < 0)
- *pos = 0;
+ t = rhashtable_walk_next(&iter->hti);
+ for (; t; t = rhashtable_walk_next(&iter->hti)) {
+ if (IS_ERR(t)) {
+ if (PTR_ERR(t) == -EAGAIN)
+ continue;
+ break;
+ }
- if (*pos == 0)
- seq_printf(seq, " ASSOC SOCK STY SST ST HBKT "
- "ASSOC-ID TX_QUEUE RX_QUEUE UID INODE LPORT "
- "RPORT LADDRS <-> RADDRS "
- "HBINT INS OUTS MAXRT T1X T2X RTXC "
- "wmema wmemq sndbuf rcvbuf\n");
+ if (net_eq(sock_net(t->asoc->base.sk), seq_file_net(seq)) &&
+ t->asoc->peer.primary_path == t)
+ break;
+ }
- return (void *)pos;
+ return t;
}
-static void sctp_assocs_seq_stop(struct seq_file *seq, void *v)
+static struct sctp_transport *sctp_transport_get_idx(struct seq_file *seq,
+ loff_t pos)
+{
+ void *obj;
+
+ while (pos && (obj = sctp_transport_get_next(seq)) && !IS_ERR(obj))
+ pos--;
+
+ return obj;
+}
+
+static int sctp_transport_walk_start(struct seq_file *seq)
{
+ struct sctp_ht_iter *iter = seq->private;
+ int err;
+
+ err = rhashtable_walk_init(&sctp_transport_hashtable, &iter->hti);
+ if (err)
+ return err;
+
+ err = rhashtable_walk_start(&iter->hti);
+
+ return err == -EAGAIN ? 0 : err;
}
+static void sctp_transport_walk_stop(struct seq_file *seq)
+{
+ struct sctp_ht_iter *iter = seq->private;
+
+ rhashtable_walk_stop(&iter->hti);
+ rhashtable_walk_exit(&iter->hti);
+}
+
+static void *sctp_assocs_seq_start(struct seq_file *seq, loff_t *pos)
+{
+ int err = sctp_transport_walk_start(seq);
+
+ if (err)
+ return ERR_PTR(err);
+
+ return *pos ? sctp_transport_get_idx(seq, *pos) : SEQ_START_TOKEN;
+}
+
+static void sctp_assocs_seq_stop(struct seq_file *seq, void *v)
+{
+ sctp_transport_walk_stop(seq);
+}
static void *sctp_assocs_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
- if (++*pos >= sctp_assoc_hashsize)
- return NULL;
+ ++*pos;
- return pos;
+ return sctp_transport_get_next(seq);
}
/* Display sctp associations (/proc/net/sctp/assocs). */
static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
{
- struct sctp_hashbucket *head;
- struct sctp_ep_common *epb;
+ struct sctp_transport *transport;
struct sctp_association *assoc;
+ struct sctp_ep_common *epb;
struct sock *sk;
- int hash = *(loff_t *)v;
-
- if (hash >= sctp_assoc_hashsize)
- return -ENOMEM;
- head = &sctp_assoc_hashtable[hash];
- local_bh_disable();
- read_lock(&head->lock);
- sctp_for_each_hentry(epb, &head->chain) {
- assoc = sctp_assoc(epb);
- sk = epb->sk;
- if (!net_eq(sock_net(sk), seq_file_net(seq)))
- continue;
- seq_printf(seq,
- "%8pK %8pK %-3d %-3d %-2d %-4d "
- "%4d %8d %8d %7u %5lu %-5d %5d ",
- assoc, sk, sctp_sk(sk)->type, sk->sk_state,
- assoc->state, hash,
- assoc->assoc_id,
- assoc->sndbuf_used,
- atomic_read(&assoc->rmem_alloc),
- from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
- sock_i_ino(sk),
- epb->bind_addr.port,
- assoc->peer.port);
- seq_printf(seq, " ");
- sctp_seq_dump_local_addrs(seq, epb);
- seq_printf(seq, "<-> ");
- sctp_seq_dump_remote_addrs(seq, assoc);
- seq_printf(seq, "\t%8lu %5d %5d %4d %4d %4d %8d "
- "%8d %8d %8d %8d",
- assoc->hbinterval, assoc->c.sinit_max_instreams,
- assoc->c.sinit_num_ostreams, assoc->max_retrans,
- assoc->init_retries, assoc->shutdown_retries,
- assoc->rtx_data_chunks,
- atomic_read(&sk->sk_wmem_alloc),
- sk->sk_wmem_queued,
- sk->sk_sndbuf,
- sk->sk_rcvbuf);
- seq_printf(seq, "\n");
+ if (v == SEQ_START_TOKEN) {
+ seq_printf(seq, " ASSOC SOCK STY SST ST HBKT "
+ "ASSOC-ID TX_QUEUE RX_QUEUE UID INODE LPORT "
+ "RPORT LADDRS <-> RADDRS "
+ "HBINT INS OUTS MAXRT T1X T2X RTXC "
+ "wmema wmemq sndbuf rcvbuf\n");
+ return 0;
}
- read_unlock(&head->lock);
- local_bh_enable();
+
+ transport = (struct sctp_transport *)v;
+ assoc = transport->asoc;
+ epb = &assoc->base;
+ sk = epb->sk;
+
+ seq_printf(seq,
+ "%8pK %8pK %-3d %-3d %-2d %-4d "
+ "%4d %8d %8d %7u %5lu %-5d %5d ",
+ assoc, sk, sctp_sk(sk)->type, sk->sk_state,
+ assoc->state, 0,
+ assoc->assoc_id,
+ assoc->sndbuf_used,
+ atomic_read(&assoc->rmem_alloc),
+ from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
+ sock_i_ino(sk),
+ epb->bind_addr.port,
+ assoc->peer.port);
+ seq_printf(seq, " ");
+ sctp_seq_dump_local_addrs(seq, epb);
+ seq_printf(seq, "<-> ");
+ sctp_seq_dump_remote_addrs(seq, assoc);
+ seq_printf(seq, "\t%8lu %5d %5d %4d %4d %4d %8d "
+ "%8d %8d %8d %8d",
+ assoc->hbinterval, assoc->c.sinit_max_instreams,
+ assoc->c.sinit_num_ostreams, assoc->max_retrans,
+ assoc->init_retries, assoc->shutdown_retries,
+ assoc->rtx_data_chunks,
+ atomic_read(&sk->sk_wmem_alloc),
+ sk->sk_wmem_queued,
+ sk->sk_sndbuf,
+ sk->sk_rcvbuf);
+ seq_printf(seq, "\n");
return 0;
}
@@ -378,7 +426,7 @@ static const struct seq_operations sctp_assoc_ops = {
static int sctp_assocs_seq_open(struct inode *inode, struct file *file)
{
return seq_open_net(inode, file, &sctp_assoc_ops,
- sizeof(struct seq_net_private));
+ sizeof(struct sctp_ht_iter));
}
static const struct file_operations sctp_assocs_seq_fops = {
@@ -409,112 +457,94 @@ void sctp_assocs_proc_exit(struct net *net)
static void *sctp_remaddr_seq_start(struct seq_file *seq, loff_t *pos)
{
- if (*pos >= sctp_assoc_hashsize)
- return NULL;
-
- if (*pos < 0)
- *pos = 0;
+ int err = sctp_transport_walk_start(seq);
- if (*pos == 0)
- seq_printf(seq, "ADDR ASSOC_ID HB_ACT RTO MAX_PATH_RTX "
- "REM_ADDR_RTX START STATE\n");
+ if (err)
+ return ERR_PTR(err);
- return (void *)pos;
+ return *pos ? sctp_transport_get_idx(seq, *pos) : SEQ_START_TOKEN;
}
static void *sctp_remaddr_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
- if (++*pos >= sctp_assoc_hashsize)
- return NULL;
+ ++*pos;
- return pos;
+ return sctp_transport_get_next(seq);
}
static void sctp_remaddr_seq_stop(struct seq_file *seq, void *v)
{
+ sctp_transport_walk_stop(seq);
}
static int sctp_remaddr_seq_show(struct seq_file *seq, void *v)
{
- struct sctp_hashbucket *head;
- struct sctp_ep_common *epb;
struct sctp_association *assoc;
struct sctp_transport *tsp;
- int hash = *(loff_t *)v;
- if (hash >= sctp_assoc_hashsize)
- return -ENOMEM;
+ if (v == SEQ_START_TOKEN) {
+ seq_printf(seq, "ADDR ASSOC_ID HB_ACT RTO MAX_PATH_RTX "
+ "REM_ADDR_RTX START STATE\n");
+ return 0;
+ }
- head = &sctp_assoc_hashtable[hash];
- local_bh_disable();
- read_lock(&head->lock);
- rcu_read_lock();
- sctp_for_each_hentry(epb, &head->chain) {
- if (!net_eq(sock_net(epb->sk), seq_file_net(seq)))
+ tsp = (struct sctp_transport *)v;
+ assoc = tsp->asoc;
+
+ list_for_each_entry_rcu(tsp, &assoc->peer.transport_addr_list,
+ transports) {
+ if (tsp->dead)
continue;
- assoc = sctp_assoc(epb);
- list_for_each_entry_rcu(tsp, &assoc->peer.transport_addr_list,
- transports) {
- if (tsp->dead)
- continue;
+ /*
+ * The remote address (ADDR)
+ */
+ tsp->af_specific->seq_dump_addr(seq, &tsp->ipaddr);
+ seq_printf(seq, " ");
+ /*
+ * The association ID (ASSOC_ID)
+ */
+ seq_printf(seq, "%d ", tsp->asoc->assoc_id);
+
+ /*
+ * If the Heartbeat is active (HB_ACT)
+ * Note: 1 = Active, 0 = Inactive
+ */
+ seq_printf(seq, "%d ", timer_pending(&tsp->hb_timer));
+
+ /*
+ * Retransmit time out (RTO)
+ */
+ seq_printf(seq, "%lu ", tsp->rto);
+
+ /*
+ * Maximum path retransmit count (PATH_MAX_RTX)
+ */
+ seq_printf(seq, "%d ", tsp->pathmaxrxt);
+
+ /*
+ * remote address retransmit count (REM_ADDR_RTX)
+ * Note: We don't have a way to tally this at the moment
+ * so lets just leave it as zero for the moment
+ */
+ seq_puts(seq, "0 ");
+
+ /*
+ * remote address start time (START). This is also not
+ * currently implemented, but we can record it with a
+ * jiffies marker in a subsequent patch
+ */
+ seq_puts(seq, "0 ");
+
+ /*
+ * The current state of this destination. I.e.
+ * SCTP_ACTIVE, SCTP_INACTIVE, ...
+ */
+ seq_printf(seq, "%d", tsp->state);
- /*
- * The remote address (ADDR)
- */
- tsp->af_specific->seq_dump_addr(seq, &tsp->ipaddr);
- seq_printf(seq, " ");
-
- /*
- * The association ID (ASSOC_ID)
- */
- seq_printf(seq, "%d ", tsp->asoc->assoc_id);
-
- /*
- * If the Heartbeat is active (HB_ACT)
- * Note: 1 = Active, 0 = Inactive
- */
- seq_printf(seq, "%d ", timer_pending(&tsp->hb_timer));
-
- /*
- * Retransmit time out (RTO)
- */
- seq_printf(seq, "%lu ", tsp->rto);
-
- /*
- * Maximum path retransmit count (PATH_MAX_RTX)
- */
- seq_printf(seq, "%d ", tsp->pathmaxrxt);
-
- /*
- * remote address retransmit count (REM_ADDR_RTX)
- * Note: We don't have a way to tally this at the moment
- * so lets just leave it as zero for the moment
- */
- seq_puts(seq, "0 ");
-
- /*
- * remote address start time (START). This is also not
- * currently implemented, but we can record it with a
- * jiffies marker in a subsequent patch
- */
- seq_puts(seq, "0 ");
-
- /*
- * The current state of this destination. I.e.
- * SCTP_ACTIVE, SCTP_INACTIVE, ...
- */
- seq_printf(seq, "%d", tsp->state);
-
- seq_printf(seq, "\n");
- }
+ seq_printf(seq, "\n");
}
- rcu_read_unlock();
- read_unlock(&head->lock);
- local_bh_enable();
-
return 0;
-
}
static const struct seq_operations sctp_remaddr_ops = {
@@ -533,7 +563,7 @@ void sctp_remaddr_proc_exit(struct net *net)
static int sctp_remaddr_seq_open(struct inode *inode, struct file *file)
{
return seq_open_net(inode, file, &sctp_remaddr_ops,
- sizeof(struct seq_net_private));
+ sizeof(struct sctp_ht_iter));
}
static const struct file_operations sctp_remaddr_seq_fops = {
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 3d9ea9a48289..ab0d538a74ed 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -1223,6 +1223,9 @@ static int __net_init sctp_defaults_init(struct net *net)
/* Max.Burst - 4 */
net->sctp.max_burst = SCTP_DEFAULT_MAX_BURST;
+ /* Enable pf state by default */
+ net->sctp.pf_enable = 1;
+
/* Association.Max.Retrans - 10 attempts
* Path.Max.Retrans - 5 attempts (per destination address)
* Max.Init.Retransmits - 8 attempts
@@ -1413,24 +1416,6 @@ static __init int sctp_init(void)
for (order = 0; (1UL << order) < goal; order++)
;
- do {
- sctp_assoc_hashsize = (1UL << order) * PAGE_SIZE /
- sizeof(struct sctp_hashbucket);
- if ((sctp_assoc_hashsize > (64 * 1024)) && order > 0)
- continue;
- sctp_assoc_hashtable = (struct sctp_hashbucket *)
- __get_free_pages(GFP_ATOMIC|__GFP_NOWARN, order);
- } while (!sctp_assoc_hashtable && --order > 0);
- if (!sctp_assoc_hashtable) {
- pr_err("Failed association hash alloc\n");
- status = -ENOMEM;
- goto err_ahash_alloc;
- }
- for (i = 0; i < sctp_assoc_hashsize; i++) {
- rwlock_init(&sctp_assoc_hashtable[i].lock);
- INIT_HLIST_HEAD(&sctp_assoc_hashtable[i].chain);
- }
-
/* Allocate and initialize the endpoint hash table. */
sctp_ep_hashsize = 64;
sctp_ep_hashtable =
@@ -1452,7 +1437,7 @@ static __init int sctp_init(void)
if ((sctp_port_hashsize > (64 * 1024)) && order > 0)
continue;
sctp_port_hashtable = (struct sctp_bind_hashbucket *)
- __get_free_pages(GFP_ATOMIC|__GFP_NOWARN, order);
+ __get_free_pages(GFP_KERNEL | __GFP_NOWARN, order);
} while (!sctp_port_hashtable && --order > 0);
if (!sctp_port_hashtable) {
pr_err("Failed bind hash alloc\n");
@@ -1464,8 +1449,10 @@ static __init int sctp_init(void)
INIT_HLIST_HEAD(&sctp_port_hashtable[i].chain);
}
- pr_info("Hash tables configured (established %d bind %d)\n",
- sctp_assoc_hashsize, sctp_port_hashsize);
+ if (sctp_transport_hashtable_init())
+ goto err_thash_alloc;
+
+ pr_info("Hash tables configured (bind %d)\n", sctp_port_hashsize);
sctp_sysctl_register();
@@ -1518,12 +1505,10 @@ err_register_defaults:
get_order(sctp_port_hashsize *
sizeof(struct sctp_bind_hashbucket)));
err_bhash_alloc:
+ sctp_transport_hashtable_destroy();
+err_thash_alloc:
kfree(sctp_ep_hashtable);
err_ehash_alloc:
- free_pages((unsigned long)sctp_assoc_hashtable,
- get_order(sctp_assoc_hashsize *
- sizeof(struct sctp_hashbucket)));
-err_ahash_alloc:
percpu_counter_destroy(&sctp_sockets_allocated);
err_percpu_counter_init:
kmem_cache_destroy(sctp_chunk_cachep);
@@ -1557,13 +1542,11 @@ static __exit void sctp_exit(void)
sctp_sysctl_unregister();
- free_pages((unsigned long)sctp_assoc_hashtable,
- get_order(sctp_assoc_hashsize *
- sizeof(struct sctp_hashbucket)));
- kfree(sctp_ep_hashtable);
free_pages((unsigned long)sctp_port_hashtable,
get_order(sctp_port_hashsize *
sizeof(struct sctp_bind_hashbucket)));
+ kfree(sctp_ep_hashtable);
+ sctp_transport_hashtable_destroy();
percpu_counter_destroy(&sctp_sockets_allocated);
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 763e06a55155..5d6a03fad378 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -1652,7 +1652,7 @@ static sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep,
/* Set an expiration time for the cookie. */
cookie->c.expiration = ktime_add(asoc->cookie_life,
- ktime_get());
+ ktime_get_real());
/* Copy the peer's init packet. */
memcpy(&cookie->c.peer_init[0], init_chunk->chunk_hdr,
@@ -1780,7 +1780,7 @@ no_hmac:
if (sock_flag(ep->base.sk, SOCK_TIMESTAMP))
kt = skb_get_ktime(skb);
else
- kt = ktime_get();
+ kt = ktime_get_real();
if (!asoc && ktime_before(bear_cookie->expiration, kt)) {
/*
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index 6098d4c42fa9..2e21384697c2 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -63,7 +63,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_state_t state,
struct sctp_endpoint *ep,
- struct sctp_association *asoc,
+ struct sctp_association **asoc,
void *event_arg,
sctp_disposition_t status,
sctp_cmd_seq_t *commands,
@@ -477,6 +477,8 @@ static void sctp_do_8_2_transport_strike(sctp_cmd_seq_t *commands,
struct sctp_transport *transport,
int is_hb)
{
+ struct net *net = sock_net(asoc->base.sk);
+
/* The check for association's overall error counter exceeding the
* threshold is done in the state function.
*/
@@ -503,7 +505,8 @@ static void sctp_do_8_2_transport_strike(sctp_cmd_seq_t *commands,
* is SCTP_ACTIVE, then mark this transport as Partially Failed,
* see SCTP Quick Failover Draft, section 5.1
*/
- if ((transport->state == SCTP_ACTIVE) &&
+ if (net->sctp.pf_enable &&
+ (transport->state == SCTP_ACTIVE) &&
(asoc->pf_retrans < transport->pathmaxrxt) &&
(transport->error_count > asoc->pf_retrans)) {
@@ -863,7 +866,6 @@ static void sctp_cmd_delete_tcb(sctp_cmd_seq_t *cmds,
(!asoc->temp) && (sk->sk_shutdown != SHUTDOWN_MASK))
return;
- sctp_unhash_established(asoc);
sctp_association_free(asoc);
}
@@ -1123,7 +1125,7 @@ int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype,
debug_post_sfn();
error = sctp_side_effects(event_type, subtype, state,
- ep, asoc, event_arg, status,
+ ep, &asoc, event_arg, status,
&commands, gfp);
debug_post_sfx();
@@ -1136,7 +1138,7 @@ int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype,
static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_state_t state,
struct sctp_endpoint *ep,
- struct sctp_association *asoc,
+ struct sctp_association **asoc,
void *event_arg,
sctp_disposition_t status,
sctp_cmd_seq_t *commands,
@@ -1151,7 +1153,7 @@ static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
* disposition SCTP_DISPOSITION_CONSUME.
*/
if (0 != (error = sctp_cmd_interpreter(event_type, subtype, state,
- ep, asoc,
+ ep, *asoc,
event_arg, status,
commands, gfp)))
goto bail;
@@ -1174,11 +1176,12 @@ static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
break;
case SCTP_DISPOSITION_DELETE_TCB:
+ case SCTP_DISPOSITION_ABORT:
/* This should now be a command. */
+ *asoc = NULL;
break;
case SCTP_DISPOSITION_CONSUME:
- case SCTP_DISPOSITION_ABORT:
/*
* We should no longer have much work to do here as the
* real work has been done as explicit commands above.
@@ -1266,7 +1269,6 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
asoc = cmd->obj.asoc;
BUG_ON(asoc->peer.primary_path == NULL);
sctp_endpoint_add_asoc(ep, asoc);
- sctp_hash_established(asoc);
break;
case SCTP_CMD_UPDATE_ASSOC:
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 6f46aa16cb76..f1f08c8f277b 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -2976,7 +2976,7 @@ sctp_disposition_t sctp_sf_eat_data_6_2(struct net *net,
SCTP_INC_STATS(net, SCTP_MIB_IN_DATA_CHUNK_DISCARDS);
goto discard_force;
case SCTP_IERROR_NO_DATA:
- goto consume;
+ return SCTP_DISPOSITION_ABORT;
case SCTP_IERROR_PROTO_VIOLATION:
return sctp_sf_abort_violation(net, ep, asoc, chunk, commands,
(u8 *)chunk->subh.data_hdr, sizeof(sctp_datahdr_t));
@@ -3043,9 +3043,6 @@ discard_noforce:
sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, force);
return SCTP_DISPOSITION_DISCARD;
-consume:
- return SCTP_DISPOSITION_CONSUME;
-
}
/*
@@ -3093,7 +3090,7 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(struct net *net,
case SCTP_IERROR_BAD_STREAM:
break;
case SCTP_IERROR_NO_DATA:
- goto consume;
+ return SCTP_DISPOSITION_ABORT;
case SCTP_IERROR_PROTO_VIOLATION:
return sctp_sf_abort_violation(net, ep, asoc, chunk, commands,
(u8 *)chunk->subh.data_hdr, sizeof(sctp_datahdr_t));
@@ -3119,7 +3116,6 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(struct net *net,
SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN));
}
-consume:
return SCTP_DISPOSITION_CONSUME;
}
@@ -4825,11 +4821,9 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort(
* if necessary to fill gaps.
*/
struct sctp_chunk *abort = arg;
- sctp_disposition_t retval;
- retval = SCTP_DISPOSITION_CONSUME;
-
- sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
+ if (abort)
+ sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
/* Even if we can't send the ABORT due to low memory delete the
* TCB. This is a departure from our typical NOMEM handling.
@@ -4844,7 +4838,7 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort(
SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS);
SCTP_DEC_STATS(net, SCTP_MIB_CURRESTAB);
- return retval;
+ return SCTP_DISPOSITION_ABORT;
}
/* We tried an illegal operation on an association which is closed. */
@@ -4959,14 +4953,13 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort(
sctp_cmd_seq_t *commands)
{
struct sctp_chunk *abort = arg;
- sctp_disposition_t retval;
/* Stop T1-init timer */
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
- retval = SCTP_DISPOSITION_CONSUME;
- sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
+ if (abort)
+ sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort));
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
SCTP_STATE(SCTP_STATE_CLOSED));
@@ -4983,7 +4976,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort(
sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
SCTP_PERR(SCTP_ERROR_USER_ABORT));
- return retval;
+ return SCTP_DISPOSITION_ABORT;
}
/*
@@ -5412,7 +5405,8 @@ sctp_disposition_t sctp_sf_do_6_3_3_rtx(struct net *net,
SCTP_INC_STATS(net, SCTP_MIB_T3_RTX_EXPIREDS);
if (asoc->overall_error_count >= asoc->max_retrans) {
- if (asoc->state == SCTP_STATE_SHUTDOWN_PENDING) {
+ if (asoc->peer.zero_window_announced &&
+ asoc->state == SCTP_STATE_SHUTDOWN_PENDING) {
/*
* We are here likely because the receiver had its rwnd
* closed for a while and we have not been able to
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 897c01c029ca..9bb80ec4c08f 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -972,7 +972,7 @@ static int sctp_setsockopt_bindx(struct sock *sk,
return -EFAULT;
/* Alloc space for the address array in kernel memory. */
- kaddrs = kmalloc(addrs_size, GFP_KERNEL);
+ kaddrs = kmalloc(addrs_size, GFP_USER | __GFP_NOWARN);
if (unlikely(!kaddrs))
return -ENOMEM;
@@ -1228,7 +1228,6 @@ out_free:
* To the hash table, try to unhash it, just in case, its a noop
* if it wasn't hashed so we're safe
*/
- sctp_unhash_established(asoc);
sctp_association_free(asoc);
}
return err;
@@ -1301,8 +1300,9 @@ static int __sctp_setsockopt_connectx(struct sock *sk,
int addrs_size,
sctp_assoc_t *assoc_id)
{
- int err = 0;
struct sockaddr *kaddrs;
+ gfp_t gfp = GFP_KERNEL;
+ int err = 0;
pr_debug("%s: sk:%p addrs:%p addrs_size:%d\n",
__func__, sk, addrs, addrs_size);
@@ -1315,7 +1315,9 @@ static int __sctp_setsockopt_connectx(struct sock *sk,
return -EFAULT;
/* Alloc space for the address array in kernel memory. */
- kaddrs = kmalloc(addrs_size, GFP_KERNEL);
+ if (sk->sk_socket->file)
+ gfp = GFP_USER | __GFP_NOWARN;
+ kaddrs = kmalloc(addrs_size, gfp);
if (unlikely(!kaddrs))
return -ENOMEM;
@@ -1501,7 +1503,6 @@ static void sctp_close(struct sock *sk, long timeout)
* ABORT or SHUTDOWN based on the linger options.
*/
if (sctp_state(asoc, CLOSED)) {
- sctp_unhash_established(asoc);
sctp_association_free(asoc);
continue;
}
@@ -1513,8 +1514,7 @@ static void sctp_close(struct sock *sk, long timeout)
struct sctp_chunk *chunk;
chunk = sctp_make_abort_user(asoc, NULL, 0);
- if (chunk)
- sctp_primitive_ABORT(net, asoc, chunk);
+ sctp_primitive_ABORT(net, asoc, chunk);
} else
sctp_primitive_SHUTDOWN(net, asoc, NULL);
}
@@ -1952,8 +1952,6 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
/* Now send the (possibly) fragmented message. */
list_for_each_entry(chunk, &datamsg->chunks, frag_list) {
- sctp_chunk_hold(chunk);
-
/* Do accounting for the write space. */
sctp_set_owner_w(chunk);
@@ -1966,15 +1964,13 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
* breaks.
*/
err = sctp_primitive_SEND(net, asoc, datamsg);
+ sctp_datamsg_put(datamsg);
/* Did the lower layer accept the chunk? */
- if (err) {
- sctp_datamsg_free(datamsg);
+ if (err)
goto out_free;
- }
pr_debug("%s: we sent primitively\n", __func__);
- sctp_datamsg_put(datamsg);
err = msg_len;
if (unlikely(wait_connect)) {
@@ -1988,10 +1984,8 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
goto out_unlock;
out_free:
- if (new_asoc) {
- sctp_unhash_established(asoc);
+ if (new_asoc)
sctp_association_free(asoc);
- }
out_unlock:
release_sock(sk);
@@ -4928,7 +4922,7 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
to = optval + offsetof(struct sctp_getaddrs, addrs);
space_left = len - offsetof(struct sctp_getaddrs, addrs);
- addrs = kmalloc(space_left, GFP_KERNEL);
+ addrs = kmalloc(space_left, GFP_USER | __GFP_NOWARN);
if (!addrs)
return -ENOMEM;
@@ -5777,7 +5771,7 @@ static int sctp_getsockopt_assoc_ids(struct sock *sk, int len,
len = sizeof(struct sctp_assoc_ids) + sizeof(sctp_assoc_t) * num;
- ids = kmalloc(len, GFP_KERNEL);
+ ids = kmalloc(len, GFP_USER | __GFP_NOWARN);
if (unlikely(!ids))
return -ENOMEM;
@@ -6458,7 +6452,7 @@ unsigned int sctp_poll(struct file *file, struct socket *sock, poll_table *wait)
if (sctp_writeable(sk)) {
mask |= POLLOUT | POLLWRNORM;
} else {
- set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+ sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
/*
* Since the socket is not locked, the buffer
* might be made available after the writeable check and
@@ -6801,26 +6795,30 @@ no_packet:
static void __sctp_write_space(struct sctp_association *asoc)
{
struct sock *sk = asoc->base.sk;
- struct socket *sock = sk->sk_socket;
- if ((sctp_wspace(asoc) > 0) && sock) {
- if (waitqueue_active(&asoc->wait))
- wake_up_interruptible(&asoc->wait);
+ if (sctp_wspace(asoc) <= 0)
+ return;
- if (sctp_writeable(sk)) {
- wait_queue_head_t *wq = sk_sleep(sk);
+ if (waitqueue_active(&asoc->wait))
+ wake_up_interruptible(&asoc->wait);
- if (wq && waitqueue_active(wq))
- wake_up_interruptible(wq);
+ if (sctp_writeable(sk)) {
+ struct socket_wq *wq;
+
+ rcu_read_lock();
+ wq = rcu_dereference(sk->sk_wq);
+ if (wq) {
+ if (waitqueue_active(&wq->wait))
+ wake_up_interruptible(&wq->wait);
/* Note that we try to include the Async I/O support
* here by modeling from the current TCP/UDP code.
* We have not tested with it yet.
*/
if (!(sk->sk_shutdown & SEND_SHUTDOWN))
- sock_wake_async(sock,
- SOCK_WAKE_SPACE, POLL_OUT);
+ sock_wake_async(wq, SOCK_WAKE_SPACE, POLL_OUT);
}
+ rcu_read_unlock();
}
}
@@ -6978,7 +6976,7 @@ void sctp_data_ready(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait, POLLIN |
POLLRDNORM | POLLRDBAND);
sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
@@ -7163,6 +7161,7 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk,
newsk->sk_type = sk->sk_type;
newsk->sk_bound_dev_if = sk->sk_bound_dev_if;
newsk->sk_flags = sk->sk_flags;
+ newsk->sk_tsflags = sk->sk_tsflags;
newsk->sk_no_check_tx = sk->sk_no_check_tx;
newsk->sk_no_check_rx = sk->sk_no_check_rx;
newsk->sk_reuse = sk->sk_reuse;
@@ -7195,6 +7194,11 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk,
newinet->mc_ttl = 1;
newinet->mc_index = 0;
newinet->mc_list = NULL;
+
+ if (newsk->sk_flags & SK_FLAGS_TIMESTAMP)
+ net_enable_timestamp();
+
+ security_sk_clone(sk, newsk);
}
static inline void sctp_copy_descendant(struct sock *sk_to,
@@ -7375,6 +7379,13 @@ struct proto sctp_prot = {
#if IS_ENABLED(CONFIG_IPV6)
+#include <net/transp_v6.h>
+static void sctp_v6_destroy_sock(struct sock *sk)
+{
+ sctp_destroy_sock(sk);
+ inet6_destroy_sock(sk);
+}
+
struct proto sctpv6_prot = {
.name = "SCTPv6",
.owner = THIS_MODULE,
@@ -7384,7 +7395,7 @@ struct proto sctpv6_prot = {
.accept = sctp_accept,
.ioctl = sctp_ioctl,
.init = sctp_init_sock,
- .destroy = sctp_destroy_sock,
+ .destroy = sctp_v6_destroy_sock,
.shutdown = sctp_shutdown,
.setsockopt = sctp_setsockopt,
.getsockopt = sctp_getsockopt,
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index 26d50c565f54..daf8554fd42a 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -308,6 +308,13 @@ static struct ctl_table sctp_net_table[] = {
.extra1 = &max_autoclose_min,
.extra2 = &max_autoclose_max,
},
+ {
+ .procname = "pf_enable",
+ .data = &init_net.sctp.pf_enable,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
{ /* sentinel */ }
};
@@ -320,7 +327,7 @@ static int proc_sctp_do_hmac_alg(struct ctl_table *ctl, int write,
struct ctl_table tbl;
bool changed = false;
char *none = "none";
- char tmp[8];
+ char tmp[8] = {0};
int ret;
memset(&tbl, 0, sizeof(struct ctl_table));
diff --git a/net/socket.c b/net/socket.c
index dd2c247c99e3..c044d1e8508c 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -257,6 +257,7 @@ static struct inode *sock_alloc_inode(struct super_block *sb)
}
init_waitqueue_head(&wq->wait);
wq->fasync_list = NULL;
+ wq->flags = 0;
RCU_INIT_POINTER(ei->socket.wq, wq);
ei->socket.state = SS_UNCONNECTED;
@@ -293,7 +294,7 @@ static int init_inodecache(void)
0,
(SLAB_HWCACHE_ALIGN |
SLAB_RECLAIM_ACCOUNT |
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD | SLAB_ACCOUNT),
init_once);
if (sock_inode_cachep == NULL)
return -ENOMEM;
@@ -1056,27 +1057,20 @@ static int sock_fasync(int fd, struct file *filp, int on)
return 0;
}
-/* This function may be called only under socket lock or callback_lock or rcu_lock */
+/* This function may be called only under rcu_lock */
-int sock_wake_async(struct socket *sock, int how, int band)
+int sock_wake_async(struct socket_wq *wq, int how, int band)
{
- struct socket_wq *wq;
-
- if (!sock)
- return -1;
- rcu_read_lock();
- wq = rcu_dereference(sock->wq);
- if (!wq || !wq->fasync_list) {
- rcu_read_unlock();
+ if (!wq || !wq->fasync_list)
return -1;
- }
+
switch (how) {
case SOCK_WAKE_WAITD:
- if (test_bit(SOCK_ASYNC_WAITDATA, &sock->flags))
+ if (test_bit(SOCKWQ_ASYNC_WAITDATA, &wq->flags))
break;
goto call_kill;
case SOCK_WAKE_SPACE:
- if (!test_and_clear_bit(SOCK_ASYNC_NOSPACE, &sock->flags))
+ if (!test_and_clear_bit(SOCKWQ_ASYNC_NOSPACE, &wq->flags))
break;
/* fall through */
case SOCK_WAKE_IO:
@@ -1086,7 +1080,7 @@ call_kill:
case SOCK_WAKE_URG:
kill_fasync(&wq->fasync_list, SIGURG, band);
}
- rcu_read_unlock();
+
return 0;
}
EXPORT_SYMBOL(sock_wake_async);
@@ -1702,6 +1696,7 @@ SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size,
msg.msg_name = addr ? (struct sockaddr *)&address : NULL;
/* We assume all kernel code knows the size of sockaddr_storage */
msg.msg_namelen = 0;
+ msg.msg_iocb = NULL;
if (sock->file->f_flags & O_NONBLOCK)
flags |= MSG_DONTWAIT;
err = sock_recvmsg(sock, &msg, iov_iter_count(&msg.msg_iter), flags);
@@ -2046,6 +2041,7 @@ int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
if (err)
break;
++datagrams;
+ cond_resched();
}
fput_light(sock->file, fput_needed);
@@ -2241,6 +2237,7 @@ int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
/* Out of band data, return right away */
if (msg_sys.msg_flags & MSG_OOB)
break;
+ cond_resched();
}
out_put:
diff --git a/net/sunrpc/auth_gss/gss_rpc_upcall.c b/net/sunrpc/auth_gss/gss_rpc_upcall.c
index 59eeed43eda2..f0c6a8c78a56 100644
--- a/net/sunrpc/auth_gss/gss_rpc_upcall.c
+++ b/net/sunrpc/auth_gss/gss_rpc_upcall.c
@@ -326,6 +326,9 @@ int gssp_accept_sec_context_upcall(struct net *net,
if (data->found_creds && client_name.data != NULL) {
char *c;
+ data->creds.cr_raw_principal = kstrndup(client_name.data,
+ client_name.len, GFP_KERNEL);
+
data->creds.cr_principal = kstrndup(client_name.data,
client_name.len, GFP_KERNEL);
if (data->creds.cr_principal) {
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 23608eb0ded2..b7f21044f4d8 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -1217,6 +1217,7 @@ static int rpc_anyaddr(int family, struct sockaddr *buf, size_t buflen)
return -EINVAL;
memcpy(buf, &rpc_in6addr_loopback,
sizeof(rpc_in6addr_loopback));
+ break;
default:
dprintk("RPC: %s: address family not supported\n",
__func__);
diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c
index d81186d34558..14f45bf0410c 100644
--- a/net/sunrpc/rpc_pipe.c
+++ b/net/sunrpc/rpc_pipe.c
@@ -1500,7 +1500,7 @@ int register_rpc_pipefs(void)
rpc_inode_cachep = kmem_cache_create("rpc_inode_cache",
sizeof(struct rpc_inode),
0, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
- SLAB_MEM_SPREAD),
+ SLAB_MEM_SPREAD|SLAB_ACCOUNT),
init_once);
if (!rpc_inode_cachep)
return -ENOMEM;
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c
index f14f24ee9983..73ad57a59989 100644
--- a/net/sunrpc/sched.c
+++ b/net/sunrpc/sched.c
@@ -250,11 +250,11 @@ void rpc_destroy_wait_queue(struct rpc_wait_queue *queue)
}
EXPORT_SYMBOL_GPL(rpc_destroy_wait_queue);
-static int rpc_wait_bit_killable(struct wait_bit_key *key)
+static int rpc_wait_bit_killable(struct wait_bit_key *key, int mode)
{
- if (fatal_signal_pending(current))
- return -ERESTARTSYS;
freezable_schedule_unsafe();
+ if (signal_pending_state(mode, current))
+ return -ERESTARTSYS;
return 0;
}
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index bc5b7b5032ca..cc9852897395 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -1364,6 +1364,19 @@ bc_svc_process(struct svc_serv *serv, struct rpc_rqst *req,
memcpy(&rqstp->rq_arg, &req->rq_rcv_buf, sizeof(rqstp->rq_arg));
memcpy(&rqstp->rq_res, &req->rq_snd_buf, sizeof(rqstp->rq_res));
+ /* Adjust the argument buffer length */
+ rqstp->rq_arg.len = req->rq_private_buf.len;
+ if (rqstp->rq_arg.len <= rqstp->rq_arg.head[0].iov_len) {
+ rqstp->rq_arg.head[0].iov_len = rqstp->rq_arg.len;
+ rqstp->rq_arg.page_len = 0;
+ } else if (rqstp->rq_arg.len <= rqstp->rq_arg.head[0].iov_len +
+ rqstp->rq_arg.page_len)
+ rqstp->rq_arg.page_len = rqstp->rq_arg.len -
+ rqstp->rq_arg.head[0].iov_len;
+ else
+ rqstp->rq_arg.len = rqstp->rq_arg.head[0].iov_len +
+ rqstp->rq_arg.page_len;
+
/* reset result send buffer "put" position */
resv->iov_len = 0;
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
index a6cbb2104667..7422f28818b2 100644
--- a/net/sunrpc/svc_xprt.c
+++ b/net/sunrpc/svc_xprt.c
@@ -10,11 +10,13 @@
#include <linux/kthread.h>
#include <linux/slab.h>
#include <net/sock.h>
+#include <linux/sunrpc/addr.h>
#include <linux/sunrpc/stats.h>
#include <linux/sunrpc/svc_xprt.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/sunrpc/xprt.h>
#include <linux/module.h>
+#include <linux/netdevice.h>
#include <trace/events/sunrpc.h>
#define RPCDBG_FACILITY RPCDBG_SVCXPRT
@@ -938,6 +940,49 @@ static void svc_age_temp_xprts(unsigned long closure)
mod_timer(&serv->sv_temptimer, jiffies + svc_conn_age_period * HZ);
}
+/* Close temporary transports whose xpt_local matches server_addr immediately
+ * instead of waiting for them to be picked up by the timer.
+ *
+ * This is meant to be called from a notifier_block that runs when an ip
+ * address is deleted.
+ */
+void svc_age_temp_xprts_now(struct svc_serv *serv, struct sockaddr *server_addr)
+{
+ struct svc_xprt *xprt;
+ struct svc_sock *svsk;
+ struct socket *sock;
+ struct list_head *le, *next;
+ LIST_HEAD(to_be_closed);
+ struct linger no_linger = {
+ .l_onoff = 1,
+ .l_linger = 0,
+ };
+
+ spin_lock_bh(&serv->sv_lock);
+ list_for_each_safe(le, next, &serv->sv_tempsocks) {
+ xprt = list_entry(le, struct svc_xprt, xpt_list);
+ if (rpc_cmp_addr(server_addr, (struct sockaddr *)
+ &xprt->xpt_local)) {
+ dprintk("svc_age_temp_xprts_now: found %p\n", xprt);
+ list_move(le, &to_be_closed);
+ }
+ }
+ spin_unlock_bh(&serv->sv_lock);
+
+ while (!list_empty(&to_be_closed)) {
+ le = to_be_closed.next;
+ list_del_init(le);
+ xprt = list_entry(le, struct svc_xprt, xpt_list);
+ dprintk("svc_age_temp_xprts_now: closing %p\n", xprt);
+ svsk = container_of(xprt, struct svc_sock, sk_xprt);
+ sock = svsk->sk_sock;
+ kernel_setsockopt(sock, SOL_SOCKET, SO_LINGER,
+ (char *)&no_linger, sizeof(no_linger));
+ svc_close_xprt(xprt);
+ }
+}
+EXPORT_SYMBOL_GPL(svc_age_temp_xprts_now);
+
static void call_xpt_users(struct svc_xprt *xprt)
{
struct svc_xpt_user *u;
diff --git a/net/sunrpc/svcauth.c b/net/sunrpc/svcauth.c
index 79c0f3459b5c..69841db1f533 100644
--- a/net/sunrpc/svcauth.c
+++ b/net/sunrpc/svcauth.c
@@ -55,6 +55,7 @@ svc_authenticate(struct svc_rqst *rqstp, __be32 *authp)
spin_unlock(&authtab_lock);
rqstp->rq_auth_slack = 0;
+ init_svc_cred(&rqstp->rq_cred);
rqstp->rq_authop = aops;
return aops->accept(rqstp, authp);
@@ -63,6 +64,7 @@ EXPORT_SYMBOL_GPL(svc_authenticate);
int svc_set_client(struct svc_rqst *rqstp)
{
+ rqstp->rq_client = NULL;
return rqstp->rq_authop->set_client(rqstp);
}
EXPORT_SYMBOL_GPL(svc_set_client);
diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
index 621ca7b4a155..dfacdc95b3f5 100644
--- a/net/sunrpc/svcauth_unix.c
+++ b/net/sunrpc/svcauth_unix.c
@@ -728,10 +728,6 @@ svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp)
struct kvec *resv = &rqstp->rq_res.head[0];
struct svc_cred *cred = &rqstp->rq_cred;
- cred->cr_group_info = NULL;
- cred->cr_principal = NULL;
- rqstp->rq_client = NULL;
-
if (argv->iov_len < 3*4)
return SVC_GARBAGE;
@@ -794,10 +790,6 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp)
u32 slen, i;
int len = argv->iov_len;
- cred->cr_group_info = NULL;
- cred->cr_principal = NULL;
- rqstp->rq_client = NULL;
-
if ((len -= 3*4) < 0)
return SVC_GARBAGE;
diff --git a/net/sunrpc/xprtrdma/backchannel.c b/net/sunrpc/xprtrdma/backchannel.c
index 2dcb44f69e53..cc1251d07297 100644
--- a/net/sunrpc/xprtrdma/backchannel.c
+++ b/net/sunrpc/xprtrdma/backchannel.c
@@ -15,7 +15,7 @@
# define RPCDBG_FACILITY RPCDBG_TRANS
#endif
-#define RPCRDMA_BACKCHANNEL_DEBUG
+#undef RPCRDMA_BACKCHANNEL_DEBUG
static void rpcrdma_bc_free_rqst(struct rpcrdma_xprt *r_xprt,
struct rpc_rqst *rqst)
@@ -42,8 +42,8 @@ static int rpcrdma_bc_setup_rqst(struct rpcrdma_xprt *r_xprt,
size_t size;
req = rpcrdma_create_req(r_xprt);
- if (!req)
- return -ENOMEM;
+ if (IS_ERR(req))
+ return PTR_ERR(req);
req->rl_backchannel = true;
size = RPCRDMA_INLINE_WRITE_THRESHOLD(rqst);
@@ -84,9 +84,7 @@ out_fail:
static int rpcrdma_bc_setup_reps(struct rpcrdma_xprt *r_xprt,
unsigned int count)
{
- struct rpcrdma_buffer *buffers = &r_xprt->rx_buf;
struct rpcrdma_rep *rep;
- unsigned long flags;
int rc = 0;
while (count--) {
@@ -98,9 +96,7 @@ static int rpcrdma_bc_setup_reps(struct rpcrdma_xprt *r_xprt,
break;
}
- spin_lock_irqsave(&buffers->rb_lock, flags);
- list_add(&rep->rr_list, &buffers->rb_recv_bufs);
- spin_unlock_irqrestore(&buffers->rb_lock, flags);
+ rpcrdma_recv_buffer_put(rep);
}
return rc;
@@ -140,6 +136,7 @@ int xprt_rdma_bc_setup(struct rpc_xprt *xprt, unsigned int reqs)
__func__);
goto out_free;
}
+ dprintk("RPC: %s: new rqst %p\n", __func__, rqst);
rqst->rq_xprt = &r_xprt->rx_xprt;
INIT_LIST_HEAD(&rqst->rq_list);
@@ -220,12 +217,14 @@ int rpcrdma_bc_marshal_reply(struct rpc_rqst *rqst)
rpclen = rqst->rq_svec[0].iov_len;
+#ifdef RPCRDMA_BACKCHANNEL_DEBUG
pr_info("RPC: %s: rpclen %zd headerp 0x%p lkey 0x%x\n",
__func__, rpclen, headerp, rdmab_lkey(req->rl_rdmabuf));
pr_info("RPC: %s: RPC/RDMA: %*ph\n",
__func__, (int)RPCRDMA_HDRLEN_MIN, headerp);
pr_info("RPC: %s: RPC: %*ph\n",
__func__, (int)rpclen, rqst->rq_svec[0].iov_base);
+#endif
req->rl_send_iov[0].addr = rdmab_addr(req->rl_rdmabuf);
req->rl_send_iov[0].length = RPCRDMA_HDRLEN_MIN;
@@ -269,6 +268,9 @@ void xprt_rdma_bc_free_rqst(struct rpc_rqst *rqst)
{
struct rpc_xprt *xprt = rqst->rq_xprt;
+ dprintk("RPC: %s: freeing rqst %p (req %p)\n",
+ __func__, rqst, rpcr_to_rdmar(rqst));
+
smp_mb__before_atomic();
WARN_ON_ONCE(!test_bit(RPC_BC_PA_IN_USE, &rqst->rq_bc_pa_state));
clear_bit(RPC_BC_PA_IN_USE, &rqst->rq_bc_pa_state);
@@ -333,9 +335,7 @@ void rpcrdma_bc_receive_call(struct rpcrdma_xprt *r_xprt,
struct rpc_rqst, rq_bc_pa_list);
list_del(&rqst->rq_bc_pa_list);
spin_unlock(&xprt->bc_pa_lock);
-#ifdef RPCRDMA_BACKCHANNEL_DEBUG
- pr_info("RPC: %s: using rqst %p\n", __func__, rqst);
-#endif
+ dprintk("RPC: %s: using rqst %p\n", __func__, rqst);
/* Prepare rqst */
rqst->rq_reply_bytes_recvd = 0;
@@ -355,10 +355,8 @@ void rpcrdma_bc_receive_call(struct rpcrdma_xprt *r_xprt,
* direction reply.
*/
req = rpcr_to_rdmar(rqst);
-#ifdef RPCRDMA_BACKCHANNEL_DEBUG
- pr_info("RPC: %s: attaching rep %p to req %p\n",
+ dprintk("RPC: %s: attaching rep %p to req %p\n",
__func__, rep, req);
-#endif
req->rl_reply = rep;
/* Defeat the retransmit detection logic in send_request */
diff --git a/net/sunrpc/xprtrdma/fmr_ops.c b/net/sunrpc/xprtrdma/fmr_ops.c
index f1e8dafbd507..c14f3a4bff68 100644
--- a/net/sunrpc/xprtrdma/fmr_ops.c
+++ b/net/sunrpc/xprtrdma/fmr_ops.c
@@ -179,6 +179,69 @@ out_maperr:
return rc;
}
+static void
+__fmr_dma_unmap(struct rpcrdma_xprt *r_xprt, struct rpcrdma_mr_seg *seg)
+{
+ struct ib_device *device = r_xprt->rx_ia.ri_device;
+ struct rpcrdma_mw *mw = seg->rl_mw;
+ int nsegs = seg->mr_nsegs;
+
+ seg->rl_mw = NULL;
+
+ while (nsegs--)
+ rpcrdma_unmap_one(device, seg++);
+
+ rpcrdma_put_mw(r_xprt, mw);
+}
+
+/* Invalidate all memory regions that were registered for "req".
+ *
+ * Sleeps until it is safe for the host CPU to access the
+ * previously mapped memory regions.
+ */
+static void
+fmr_op_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
+{
+ struct rpcrdma_mr_seg *seg;
+ unsigned int i, nchunks;
+ struct rpcrdma_mw *mw;
+ LIST_HEAD(unmap_list);
+ int rc;
+
+ dprintk("RPC: %s: req %p\n", __func__, req);
+
+ /* ORDER: Invalidate all of the req's MRs first
+ *
+ * ib_unmap_fmr() is slow, so use a single call instead
+ * of one call per mapped MR.
+ */
+ for (i = 0, nchunks = req->rl_nchunks; nchunks; nchunks--) {
+ seg = &req->rl_segments[i];
+ mw = seg->rl_mw;
+
+ list_add(&mw->r.fmr.fmr->list, &unmap_list);
+
+ i += seg->mr_nsegs;
+ }
+ rc = ib_unmap_fmr(&unmap_list);
+ if (rc)
+ pr_warn("%s: ib_unmap_fmr failed (%i)\n", __func__, rc);
+
+ /* ORDER: Now DMA unmap all of the req's MRs, and return
+ * them to the free MW list.
+ */
+ for (i = 0, nchunks = req->rl_nchunks; nchunks; nchunks--) {
+ seg = &req->rl_segments[i];
+
+ __fmr_dma_unmap(r_xprt, seg);
+
+ i += seg->mr_nsegs;
+ seg->mr_nsegs = 0;
+ }
+
+ req->rl_nchunks = 0;
+}
+
/* Use the ib_unmap_fmr() verb to prevent further remote
* access via RDMA READ or RDMA WRITE.
*/
@@ -231,6 +294,7 @@ fmr_op_destroy(struct rpcrdma_buffer *buf)
const struct rpcrdma_memreg_ops rpcrdma_fmr_memreg_ops = {
.ro_map = fmr_op_map,
+ .ro_unmap_sync = fmr_op_unmap_sync,
.ro_unmap = fmr_op_unmap,
.ro_open = fmr_op_open,
.ro_maxpages = fmr_op_maxpages,
diff --git a/net/sunrpc/xprtrdma/frwr_ops.c b/net/sunrpc/xprtrdma/frwr_ops.c
index 88cf9e7269c2..c6836844bd0e 100644
--- a/net/sunrpc/xprtrdma/frwr_ops.c
+++ b/net/sunrpc/xprtrdma/frwr_ops.c
@@ -245,12 +245,14 @@ frwr_op_maxpages(struct rpcrdma_xprt *r_xprt)
rpcrdma_max_segments(r_xprt) * ia->ri_max_frmr_depth);
}
-/* If FAST_REG or LOCAL_INV failed, indicate the frmr needs to be reset. */
+/* If FAST_REG or LOCAL_INV failed, indicate the frmr needs
+ * to be reset.
+ *
+ * WARNING: Only wr_id and status are reliable at this point
+ */
static void
-frwr_sendcompletion(struct ib_wc *wc)
+__frwr_sendcompletion_flush(struct ib_wc *wc, struct rpcrdma_mw *r)
{
- struct rpcrdma_mw *r;
-
if (likely(wc->status == IB_WC_SUCCESS))
return;
@@ -261,9 +263,23 @@ frwr_sendcompletion(struct ib_wc *wc)
else
pr_warn("RPC: %s: frmr %p error, status %s (%d)\n",
__func__, r, ib_wc_status_msg(wc->status), wc->status);
+
r->r.frmr.fr_state = FRMR_IS_STALE;
}
+static void
+frwr_sendcompletion(struct ib_wc *wc)
+{
+ struct rpcrdma_mw *r = (struct rpcrdma_mw *)(unsigned long)wc->wr_id;
+ struct rpcrdma_frmr *f = &r->r.frmr;
+
+ if (unlikely(wc->status != IB_WC_SUCCESS))
+ __frwr_sendcompletion_flush(wc, r);
+
+ if (f->fr_waiter)
+ complete(&f->fr_linv_done);
+}
+
static int
frwr_op_init(struct rpcrdma_xprt *r_xprt)
{
@@ -319,7 +335,7 @@ frwr_op_map(struct rpcrdma_xprt *r_xprt, struct rpcrdma_mr_seg *seg,
struct rpcrdma_mw *mw;
struct rpcrdma_frmr *frmr;
struct ib_mr *mr;
- struct ib_reg_wr reg_wr;
+ struct ib_reg_wr *reg_wr;
struct ib_send_wr *bad_wr;
int rc, i, n, dma_nents;
u8 key;
@@ -335,7 +351,9 @@ frwr_op_map(struct rpcrdma_xprt *r_xprt, struct rpcrdma_mr_seg *seg,
} while (mw->r.frmr.fr_state != FRMR_IS_INVALID);
frmr = &mw->r.frmr;
frmr->fr_state = FRMR_IS_VALID;
+ frmr->fr_waiter = false;
mr = frmr->fr_mr;
+ reg_wr = &frmr->fr_regwr;
if (nsegs > ia->ri_max_frmr_depth)
nsegs = ia->ri_max_frmr_depth;
@@ -381,19 +399,19 @@ frwr_op_map(struct rpcrdma_xprt *r_xprt, struct rpcrdma_mr_seg *seg,
key = (u8)(mr->rkey & 0x000000FF);
ib_update_fast_reg_key(mr, ++key);
- reg_wr.wr.next = NULL;
- reg_wr.wr.opcode = IB_WR_REG_MR;
- reg_wr.wr.wr_id = (uintptr_t)mw;
- reg_wr.wr.num_sge = 0;
- reg_wr.wr.send_flags = 0;
- reg_wr.mr = mr;
- reg_wr.key = mr->rkey;
- reg_wr.access = writing ?
- IB_ACCESS_REMOTE_WRITE | IB_ACCESS_LOCAL_WRITE :
- IB_ACCESS_REMOTE_READ;
+ reg_wr->wr.next = NULL;
+ reg_wr->wr.opcode = IB_WR_REG_MR;
+ reg_wr->wr.wr_id = (uintptr_t)mw;
+ reg_wr->wr.num_sge = 0;
+ reg_wr->wr.send_flags = 0;
+ reg_wr->mr = mr;
+ reg_wr->key = mr->rkey;
+ reg_wr->access = writing ?
+ IB_ACCESS_REMOTE_WRITE | IB_ACCESS_LOCAL_WRITE :
+ IB_ACCESS_REMOTE_READ;
DECR_CQCOUNT(&r_xprt->rx_ep);
- rc = ib_post_send(ia->ri_id->qp, &reg_wr.wr, &bad_wr);
+ rc = ib_post_send(ia->ri_id->qp, &reg_wr->wr, &bad_wr);
if (rc)
goto out_senderr;
@@ -413,6 +431,116 @@ out_senderr:
return rc;
}
+static struct ib_send_wr *
+__frwr_prepare_linv_wr(struct rpcrdma_mr_seg *seg)
+{
+ struct rpcrdma_mw *mw = seg->rl_mw;
+ struct rpcrdma_frmr *f = &mw->r.frmr;
+ struct ib_send_wr *invalidate_wr;
+
+ f->fr_waiter = false;
+ f->fr_state = FRMR_IS_INVALID;
+ invalidate_wr = &f->fr_invwr;
+
+ memset(invalidate_wr, 0, sizeof(*invalidate_wr));
+ invalidate_wr->wr_id = (unsigned long)(void *)mw;
+ invalidate_wr->opcode = IB_WR_LOCAL_INV;
+ invalidate_wr->ex.invalidate_rkey = f->fr_mr->rkey;
+
+ return invalidate_wr;
+}
+
+static void
+__frwr_dma_unmap(struct rpcrdma_xprt *r_xprt, struct rpcrdma_mr_seg *seg,
+ int rc)
+{
+ struct ib_device *device = r_xprt->rx_ia.ri_device;
+ struct rpcrdma_mw *mw = seg->rl_mw;
+ struct rpcrdma_frmr *f = &mw->r.frmr;
+
+ seg->rl_mw = NULL;
+
+ ib_dma_unmap_sg(device, f->sg, f->sg_nents, seg->mr_dir);
+
+ if (!rc)
+ rpcrdma_put_mw(r_xprt, mw);
+ else
+ __frwr_queue_recovery(mw);
+}
+
+/* Invalidate all memory regions that were registered for "req".
+ *
+ * Sleeps until it is safe for the host CPU to access the
+ * previously mapped memory regions.
+ */
+static void
+frwr_op_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
+{
+ struct ib_send_wr *invalidate_wrs, *pos, *prev, *bad_wr;
+ struct rpcrdma_ia *ia = &r_xprt->rx_ia;
+ struct rpcrdma_mr_seg *seg;
+ unsigned int i, nchunks;
+ struct rpcrdma_frmr *f;
+ int rc;
+
+ dprintk("RPC: %s: req %p\n", __func__, req);
+
+ /* ORDER: Invalidate all of the req's MRs first
+ *
+ * Chain the LOCAL_INV Work Requests and post them with
+ * a single ib_post_send() call.
+ */
+ invalidate_wrs = pos = prev = NULL;
+ seg = NULL;
+ for (i = 0, nchunks = req->rl_nchunks; nchunks; nchunks--) {
+ seg = &req->rl_segments[i];
+
+ pos = __frwr_prepare_linv_wr(seg);
+
+ if (!invalidate_wrs)
+ invalidate_wrs = pos;
+ else
+ prev->next = pos;
+ prev = pos;
+
+ i += seg->mr_nsegs;
+ }
+ f = &seg->rl_mw->r.frmr;
+
+ /* Strong send queue ordering guarantees that when the
+ * last WR in the chain completes, all WRs in the chain
+ * are complete.
+ */
+ f->fr_invwr.send_flags = IB_SEND_SIGNALED;
+ f->fr_waiter = true;
+ init_completion(&f->fr_linv_done);
+ INIT_CQCOUNT(&r_xprt->rx_ep);
+
+ /* Transport disconnect drains the receive CQ before it
+ * replaces the QP. The RPC reply handler won't call us
+ * unless ri_id->qp is a valid pointer.
+ */
+ rc = ib_post_send(ia->ri_id->qp, invalidate_wrs, &bad_wr);
+ if (rc)
+ pr_warn("%s: ib_post_send failed %i\n", __func__, rc);
+
+ wait_for_completion(&f->fr_linv_done);
+
+ /* ORDER: Now DMA unmap all of the req's MRs, and return
+ * them to the free MW list.
+ */
+ for (i = 0, nchunks = req->rl_nchunks; nchunks; nchunks--) {
+ seg = &req->rl_segments[i];
+
+ __frwr_dma_unmap(r_xprt, seg, rc);
+
+ i += seg->mr_nsegs;
+ seg->mr_nsegs = 0;
+ }
+
+ req->rl_nchunks = 0;
+}
+
/* Post a LOCAL_INV Work Request to prevent further remote access
* via RDMA READ or RDMA WRITE.
*/
@@ -423,23 +551,24 @@ frwr_op_unmap(struct rpcrdma_xprt *r_xprt, struct rpcrdma_mr_seg *seg)
struct rpcrdma_ia *ia = &r_xprt->rx_ia;
struct rpcrdma_mw *mw = seg1->rl_mw;
struct rpcrdma_frmr *frmr = &mw->r.frmr;
- struct ib_send_wr invalidate_wr, *bad_wr;
+ struct ib_send_wr *invalidate_wr, *bad_wr;
int rc, nsegs = seg->mr_nsegs;
dprintk("RPC: %s: FRMR %p\n", __func__, mw);
seg1->rl_mw = NULL;
frmr->fr_state = FRMR_IS_INVALID;
+ invalidate_wr = &mw->r.frmr.fr_invwr;
- memset(&invalidate_wr, 0, sizeof(invalidate_wr));
- invalidate_wr.wr_id = (unsigned long)(void *)mw;
- invalidate_wr.opcode = IB_WR_LOCAL_INV;
- invalidate_wr.ex.invalidate_rkey = frmr->fr_mr->rkey;
+ memset(invalidate_wr, 0, sizeof(*invalidate_wr));
+ invalidate_wr->wr_id = (uintptr_t)mw;
+ invalidate_wr->opcode = IB_WR_LOCAL_INV;
+ invalidate_wr->ex.invalidate_rkey = frmr->fr_mr->rkey;
DECR_CQCOUNT(&r_xprt->rx_ep);
ib_dma_unmap_sg(ia->ri_device, frmr->sg, frmr->sg_nents, seg1->mr_dir);
read_lock(&ia->ri_qplock);
- rc = ib_post_send(ia->ri_id->qp, &invalidate_wr, &bad_wr);
+ rc = ib_post_send(ia->ri_id->qp, invalidate_wr, &bad_wr);
read_unlock(&ia->ri_qplock);
if (rc)
goto out_err;
@@ -471,6 +600,7 @@ frwr_op_destroy(struct rpcrdma_buffer *buf)
const struct rpcrdma_memreg_ops rpcrdma_frwr_memreg_ops = {
.ro_map = frwr_op_map,
+ .ro_unmap_sync = frwr_op_unmap_sync,
.ro_unmap = frwr_op_unmap,
.ro_open = frwr_op_open,
.ro_maxpages = frwr_op_maxpages,
diff --git a/net/sunrpc/xprtrdma/physical_ops.c b/net/sunrpc/xprtrdma/physical_ops.c
index 617b76f22154..dbb302ecf590 100644
--- a/net/sunrpc/xprtrdma/physical_ops.c
+++ b/net/sunrpc/xprtrdma/physical_ops.c
@@ -83,6 +83,18 @@ physical_op_unmap(struct rpcrdma_xprt *r_xprt, struct rpcrdma_mr_seg *seg)
return 1;
}
+/* DMA unmap all memory regions that were mapped for "req".
+ */
+static void
+physical_op_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
+{
+ struct ib_device *device = r_xprt->rx_ia.ri_device;
+ unsigned int i;
+
+ for (i = 0; req->rl_nchunks; --req->rl_nchunks)
+ rpcrdma_unmap_one(device, &req->rl_segments[i++]);
+}
+
static void
physical_op_destroy(struct rpcrdma_buffer *buf)
{
@@ -90,6 +102,7 @@ physical_op_destroy(struct rpcrdma_buffer *buf)
const struct rpcrdma_memreg_ops rpcrdma_physical_memreg_ops = {
.ro_map = physical_op_map,
+ .ro_unmap_sync = physical_op_unmap_sync,
.ro_unmap = physical_op_unmap,
.ro_open = physical_op_open,
.ro_maxpages = physical_op_maxpages,
diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c
index c10d9699441c..0f28f2d743ed 100644
--- a/net/sunrpc/xprtrdma/rpc_rdma.c
+++ b/net/sunrpc/xprtrdma/rpc_rdma.c
@@ -804,6 +804,11 @@ rpcrdma_reply_handler(struct rpcrdma_rep *rep)
if (req->rl_reply)
goto out_duplicate;
+ /* Sanity checking has passed. We are now committed
+ * to complete this transaction.
+ */
+ list_del_init(&rqst->rq_list);
+ spin_unlock_bh(&xprt->transport_lock);
dprintk("RPC: %s: reply 0x%p completes request 0x%p\n"
" RPC request 0x%p xid 0x%08x\n",
__func__, rep, req, rqst,
@@ -888,12 +893,23 @@ badheader:
break;
}
+ /* Invalidate and flush the data payloads before waking the
+ * waiting application. This guarantees the memory region is
+ * properly fenced from the server before the application
+ * accesses the data. It also ensures proper send flow
+ * control: waking the next RPC waits until this RPC has
+ * relinquished all its Send Queue entries.
+ */
+ if (req->rl_nchunks)
+ r_xprt->rx_ia.ri_ops->ro_unmap_sync(r_xprt, req);
+
credits = be32_to_cpu(headerp->rm_credit);
if (credits == 0)
credits = 1; /* don't deadlock */
else if (credits > r_xprt->rx_buf.rb_max_requests)
credits = r_xprt->rx_buf.rb_max_requests;
+ spin_lock_bh(&xprt->transport_lock);
cwnd = xprt->cwnd;
xprt->cwnd = credits << RPC_CWNDSHIFT;
if (xprt->cwnd > cwnd)
diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c
index 8c545f7d7525..740bddcf3488 100644
--- a/net/sunrpc/xprtrdma/transport.c
+++ b/net/sunrpc/xprtrdma/transport.c
@@ -576,6 +576,9 @@ xprt_rdma_free(void *buffer)
rb = container_of(buffer, struct rpcrdma_regbuf, rg_base[0]);
req = rb->rg_owner;
+ if (req->rl_backchannel)
+ return;
+
r_xprt = container_of(req->rl_buffer, struct rpcrdma_xprt, rx_buf);
dprintk("RPC: %s: called on 0x%p\n", __func__, req->rl_reply);
diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c
index eadd1655145a..732c71ce5dca 100644
--- a/net/sunrpc/xprtrdma/verbs.c
+++ b/net/sunrpc/xprtrdma/verbs.c
@@ -616,10 +616,8 @@ rpcrdma_ep_create(struct rpcrdma_ep *ep, struct rpcrdma_ia *ia,
/* set trigger for requesting send completion */
ep->rep_cqinit = ep->rep_attr.cap.max_send_wr/2 - 1;
- if (ep->rep_cqinit > RPCRDMA_MAX_UNSIGNALED_SENDS)
- ep->rep_cqinit = RPCRDMA_MAX_UNSIGNALED_SENDS;
- else if (ep->rep_cqinit <= 2)
- ep->rep_cqinit = 0;
+ if (ep->rep_cqinit <= 2)
+ ep->rep_cqinit = 0; /* always signal? */
INIT_CQCOUNT(ep);
init_waitqueue_head(&ep->rep_connect_wait);
INIT_DELAYED_WORK(&ep->rep_connect_worker, rpcrdma_connect_worker);
@@ -852,10 +850,11 @@ retry:
if (extras) {
rc = rpcrdma_ep_post_extra_recv(r_xprt, extras);
- if (rc)
+ if (rc) {
pr_warn("%s: rpcrdma_ep_post_extra_recv: %i\n",
__func__, rc);
rc = 0;
+ }
}
}
@@ -1337,15 +1336,14 @@ rpcrdma_ep_post_extra_recv(struct rpcrdma_xprt *r_xprt, unsigned int count)
struct rpcrdma_ia *ia = &r_xprt->rx_ia;
struct rpcrdma_ep *ep = &r_xprt->rx_ep;
struct rpcrdma_rep *rep;
- unsigned long flags;
int rc;
while (count--) {
- spin_lock_irqsave(&buffers->rb_lock, flags);
+ spin_lock(&buffers->rb_lock);
if (list_empty(&buffers->rb_recv_bufs))
goto out_reqbuf;
rep = rpcrdma_buffer_get_rep_locked(buffers);
- spin_unlock_irqrestore(&buffers->rb_lock, flags);
+ spin_unlock(&buffers->rb_lock);
rc = rpcrdma_ep_post_recv(ia, ep, rep);
if (rc)
@@ -1355,7 +1353,7 @@ rpcrdma_ep_post_extra_recv(struct rpcrdma_xprt *r_xprt, unsigned int count)
return 0;
out_reqbuf:
- spin_unlock_irqrestore(&buffers->rb_lock, flags);
+ spin_unlock(&buffers->rb_lock);
pr_warn("%s: no extra receive buffers\n", __func__);
return -ENOMEM;
diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h
index ac7f8d4f632a..728101ddc44b 100644
--- a/net/sunrpc/xprtrdma/xprt_rdma.h
+++ b/net/sunrpc/xprtrdma/xprt_rdma.h
@@ -88,12 +88,6 @@ struct rpcrdma_ep {
struct delayed_work rep_connect_worker;
};
-/*
- * Force a signaled SEND Work Request every so often,
- * in case the provider needs to do some housekeeping.
- */
-#define RPCRDMA_MAX_UNSIGNALED_SENDS (32)
-
#define INIT_CQCOUNT(ep) atomic_set(&(ep)->rep_cqcount, (ep)->rep_cqinit)
#define DECR_CQCOUNT(ep) atomic_sub_return(1, &(ep)->rep_cqcount)
@@ -207,6 +201,12 @@ struct rpcrdma_frmr {
enum rpcrdma_frmr_state fr_state;
struct work_struct fr_work;
struct rpcrdma_xprt *fr_xprt;
+ bool fr_waiter;
+ struct completion fr_linv_done;;
+ union {
+ struct ib_reg_wr fr_regwr;
+ struct ib_send_wr fr_invwr;
+ };
};
struct rpcrdma_fmr {
@@ -364,6 +364,8 @@ struct rpcrdma_xprt;
struct rpcrdma_memreg_ops {
int (*ro_map)(struct rpcrdma_xprt *,
struct rpcrdma_mr_seg *, int, bool);
+ void (*ro_unmap_sync)(struct rpcrdma_xprt *,
+ struct rpcrdma_req *);
int (*ro_unmap)(struct rpcrdma_xprt *,
struct rpcrdma_mr_seg *);
int (*ro_open)(struct rpcrdma_ia *,
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 1d1a70498910..fde2138b81e7 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -398,7 +398,6 @@ static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen,
if (unlikely(!sock))
return -ENOTSOCK;
- clear_bit(SOCK_ASYNC_NOSPACE, &sock->flags);
if (base != 0) {
addr = NULL;
addrlen = 0;
@@ -442,7 +441,6 @@ static void xs_nospace_callback(struct rpc_task *task)
struct sock_xprt *transport = container_of(task->tk_rqstp->rq_xprt, struct sock_xprt, xprt);
transport->inet->sk_write_pending--;
- clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags);
}
/**
@@ -467,20 +465,11 @@ static int xs_nospace(struct rpc_task *task)
/* Don't race with disconnect */
if (xprt_connected(xprt)) {
- if (test_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags)) {
- /*
- * Notify TCP that we're limited by the application
- * window size
- */
- set_bit(SOCK_NOSPACE, &transport->sock->flags);
- sk->sk_write_pending++;
- /* ...and wait for more buffer space */
- xprt_wait_for_buffer_space(task, xs_nospace_callback);
- }
- } else {
- clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags);
+ /* wait for more buffer space */
+ sk->sk_write_pending++;
+ xprt_wait_for_buffer_space(task, xs_nospace_callback);
+ } else
ret = -ENOTCONN;
- }
spin_unlock_bh(&xprt->transport_lock);
@@ -616,9 +605,6 @@ process_status:
case -EAGAIN:
status = xs_nospace(task);
break;
- default:
- dprintk("RPC: sendmsg returned unrecognized error %d\n",
- -status);
case -ENETUNREACH:
case -ENOBUFS:
case -EPIPE:
@@ -626,7 +612,10 @@ process_status:
case -EPERM:
/* When the server has died, an ICMP port unreachable message
* prompts ECONNREFUSED. */
- clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags);
+ break;
+ default:
+ dprintk("RPC: sendmsg returned unrecognized error %d\n",
+ -status);
}
return status;
@@ -706,16 +695,16 @@ static int xs_tcp_send_request(struct rpc_task *task)
case -EAGAIN:
status = xs_nospace(task);
break;
- default:
- dprintk("RPC: sendmsg returned unrecognized error %d\n",
- -status);
case -ECONNRESET:
case -ECONNREFUSED:
case -ENOTCONN:
case -EADDRINUSE:
case -ENOBUFS:
case -EPIPE:
- clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags);
+ break;
+ default:
+ dprintk("RPC: sendmsg returned unrecognized error %d\n",
+ -status);
}
return status;
@@ -1609,19 +1598,23 @@ static void xs_tcp_state_change(struct sock *sk)
static void xs_write_space(struct sock *sk)
{
- struct socket *sock;
+ struct socket_wq *wq;
struct rpc_xprt *xprt;
- if (unlikely(!(sock = sk->sk_socket)))
+ if (!sk->sk_socket)
return;
- clear_bit(SOCK_NOSPACE, &sock->flags);
+ clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
if (unlikely(!(xprt = xprt_from_sock(sk))))
return;
- if (test_and_clear_bit(SOCK_ASYNC_NOSPACE, &sock->flags) == 0)
- return;
+ rcu_read_lock();
+ wq = rcu_dereference(sk->sk_wq);
+ if (!wq || test_and_clear_bit(SOCKWQ_ASYNC_NOSPACE, &wq->flags) == 0)
+ goto out;
xprt_write_space(xprt);
+out:
+ rcu_read_unlock();
}
/**
@@ -1907,18 +1900,6 @@ static inline void xs_reclassify_socket(int family, struct socket *sock)
}
}
#else
-static inline void xs_reclassify_socketu(struct socket *sock)
-{
-}
-
-static inline void xs_reclassify_socket4(struct socket *sock)
-{
-}
-
-static inline void xs_reclassify_socket6(struct socket *sock)
-{
-}
-
static inline void xs_reclassify_socket(int family, struct socket *sock)
{
}
@@ -2008,7 +1989,7 @@ static int xs_local_setup_socket(struct sock_xprt *transport)
"transport socket (%d).\n", -status);
goto out;
}
- xs_reclassify_socketu(sock);
+ xs_reclassify_socket(AF_LOCAL, sock);
dprintk("RPC: worker connecting xprt %p via AF_LOCAL to %s\n",
xprt, xprt->address_strings[RPC_DISPLAY_ADDR]);
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c
index f34e535e93bd..ebc661d3b6e3 100644
--- a/net/switchdev/switchdev.c
+++ b/net/switchdev/switchdev.c
@@ -345,6 +345,8 @@ static size_t switchdev_obj_size(const struct switchdev_obj *obj)
return sizeof(struct switchdev_obj_ipv4_fib);
case SWITCHDEV_OBJ_ID_PORT_FDB:
return sizeof(struct switchdev_obj_port_fdb);
+ case SWITCHDEV_OBJ_ID_PORT_MDB:
+ return sizeof(struct switchdev_obj_port_mdb);
default:
BUG();
}
@@ -723,6 +725,7 @@ static int switchdev_port_vlan_fill(struct sk_buff *skb, struct net_device *dev,
u32 filter_mask)
{
struct switchdev_vlan_dump dump = {
+ .vlan.obj.orig_dev = dev,
.vlan.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.skb = skb,
.filter_mask = filter_mask,
@@ -757,6 +760,7 @@ int switchdev_port_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
int nlflags)
{
struct switchdev_attr attr = {
+ .orig_dev = dev,
.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
};
u16 mode = BRIDGE_MODE_UNDEF;
@@ -778,6 +782,7 @@ static int switchdev_port_br_setflag(struct net_device *dev,
unsigned long brport_flag)
{
struct switchdev_attr attr = {
+ .orig_dev = dev,
.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
};
u8 flag = nla_get_u8(nlattr);
@@ -853,6 +858,7 @@ static int switchdev_port_br_afspec(struct net_device *dev,
struct nlattr *attr;
struct bridge_vlan_info *vinfo;
struct switchdev_obj_port_vlan vlan = {
+ .obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
};
int rem;
@@ -975,6 +981,7 @@ int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
u16 vid, u16 nlm_flags)
{
struct switchdev_obj_port_fdb fdb = {
+ .obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_FDB,
.vid = vid,
};
@@ -1000,6 +1007,7 @@ int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
u16 vid)
{
struct switchdev_obj_port_fdb fdb = {
+ .obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_FDB,
.vid = vid,
};
@@ -1077,6 +1085,7 @@ int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
struct net_device *filter_dev, int idx)
{
struct switchdev_fdb_dump dump = {
+ .fdb.obj.orig_dev = dev,
.fdb.obj.id = SWITCHDEV_OBJ_ID_PORT_FDB,
.dev = dev,
.skb = skb,
@@ -1135,6 +1144,7 @@ static struct net_device *switchdev_get_dev_by_nhs(struct fib_info *fi)
if (!dev)
return NULL;
+ attr.orig_dev = dev;
if (switchdev_port_attr_get(dev, &attr))
return NULL;
@@ -1194,6 +1204,7 @@ int switchdev_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi,
if (!dev)
return 0;
+ ipv4_fib.obj.orig_dev = dev;
err = switchdev_port_obj_add(dev, &ipv4_fib.obj);
if (!err)
fi->fib_flags |= RTNH_F_OFFLOAD;
@@ -1238,6 +1249,7 @@ int switchdev_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi,
if (!dev)
return 0;
+ ipv4_fib.obj.orig_dev = dev;
err = switchdev_port_obj_del(dev, &ipv4_fib.obj);
if (!err)
fi->fib_flags &= ~RTNH_F_OFFLOAD;
@@ -1270,10 +1282,12 @@ static bool switchdev_port_same_parent_id(struct net_device *a,
struct net_device *b)
{
struct switchdev_attr a_attr = {
+ .orig_dev = a,
.id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
.flags = SWITCHDEV_F_NO_RECURSE,
};
struct switchdev_attr b_attr = {
+ .orig_dev = b,
.id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
.flags = SWITCHDEV_F_NO_RECURSE,
};
diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c
index 9dc239dfe192..e401108360a2 100644
--- a/net/tipc/bcast.c
+++ b/net/tipc/bcast.c
@@ -332,131 +332,15 @@ void tipc_bcast_remove_peer(struct net *net, struct tipc_link *rcv_l)
tipc_sk_rcv(net, inputq);
}
-static int __tipc_nl_add_bc_link_stat(struct sk_buff *skb,
- struct tipc_stats *stats)
-{
- int i;
- struct nlattr *nest;
-
- struct nla_map {
- __u32 key;
- __u32 val;
- };
-
- struct nla_map map[] = {
- {TIPC_NLA_STATS_RX_INFO, stats->recv_info},
- {TIPC_NLA_STATS_RX_FRAGMENTS, stats->recv_fragments},
- {TIPC_NLA_STATS_RX_FRAGMENTED, stats->recv_fragmented},
- {TIPC_NLA_STATS_RX_BUNDLES, stats->recv_bundles},
- {TIPC_NLA_STATS_RX_BUNDLED, stats->recv_bundled},
- {TIPC_NLA_STATS_TX_INFO, stats->sent_info},
- {TIPC_NLA_STATS_TX_FRAGMENTS, stats->sent_fragments},
- {TIPC_NLA_STATS_TX_FRAGMENTED, stats->sent_fragmented},
- {TIPC_NLA_STATS_TX_BUNDLES, stats->sent_bundles},
- {TIPC_NLA_STATS_TX_BUNDLED, stats->sent_bundled},
- {TIPC_NLA_STATS_RX_NACKS, stats->recv_nacks},
- {TIPC_NLA_STATS_RX_DEFERRED, stats->deferred_recv},
- {TIPC_NLA_STATS_TX_NACKS, stats->sent_nacks},
- {TIPC_NLA_STATS_TX_ACKS, stats->sent_acks},
- {TIPC_NLA_STATS_RETRANSMITTED, stats->retransmitted},
- {TIPC_NLA_STATS_DUPLICATES, stats->duplicates},
- {TIPC_NLA_STATS_LINK_CONGS, stats->link_congs},
- {TIPC_NLA_STATS_MAX_QUEUE, stats->max_queue_sz},
- {TIPC_NLA_STATS_AVG_QUEUE, stats->queue_sz_counts ?
- (stats->accu_queue_sz / stats->queue_sz_counts) : 0}
- };
-
- nest = nla_nest_start(skb, TIPC_NLA_LINK_STATS);
- if (!nest)
- return -EMSGSIZE;
-
- for (i = 0; i < ARRAY_SIZE(map); i++)
- if (nla_put_u32(skb, map[i].key, map[i].val))
- goto msg_full;
-
- nla_nest_end(skb, nest);
-
- return 0;
-msg_full:
- nla_nest_cancel(skb, nest);
-
- return -EMSGSIZE;
-}
-
-int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg)
-{
- int err;
- void *hdr;
- struct nlattr *attrs;
- struct nlattr *prop;
- struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_link *bcl = tn->bcl;
-
- if (!bcl)
- return 0;
-
- tipc_bcast_lock(net);
-
- hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family,
- NLM_F_MULTI, TIPC_NL_LINK_GET);
- if (!hdr)
- return -EMSGSIZE;
-
- attrs = nla_nest_start(msg->skb, TIPC_NLA_LINK);
- if (!attrs)
- goto msg_full;
-
- /* The broadcast link is always up */
- if (nla_put_flag(msg->skb, TIPC_NLA_LINK_UP))
- goto attr_msg_full;
-
- if (nla_put_flag(msg->skb, TIPC_NLA_LINK_BROADCAST))
- goto attr_msg_full;
- if (nla_put_string(msg->skb, TIPC_NLA_LINK_NAME, bcl->name))
- goto attr_msg_full;
- if (nla_put_u32(msg->skb, TIPC_NLA_LINK_RX, bcl->rcv_nxt))
- goto attr_msg_full;
- if (nla_put_u32(msg->skb, TIPC_NLA_LINK_TX, bcl->snd_nxt))
- goto attr_msg_full;
-
- prop = nla_nest_start(msg->skb, TIPC_NLA_LINK_PROP);
- if (!prop)
- goto attr_msg_full;
- if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN, bcl->window))
- goto prop_msg_full;
- nla_nest_end(msg->skb, prop);
-
- err = __tipc_nl_add_bc_link_stat(msg->skb, &bcl->stats);
- if (err)
- goto attr_msg_full;
-
- tipc_bcast_unlock(net);
- nla_nest_end(msg->skb, attrs);
- genlmsg_end(msg->skb, hdr);
-
- return 0;
-
-prop_msg_full:
- nla_nest_cancel(msg->skb, prop);
-attr_msg_full:
- nla_nest_cancel(msg->skb, attrs);
-msg_full:
- tipc_bcast_unlock(net);
- genlmsg_cancel(msg->skb, hdr);
-
- return -EMSGSIZE;
-}
-
int tipc_bclink_reset_stats(struct net *net)
{
- struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_link *bcl = tn->bcl;
+ struct tipc_link *l = tipc_bc_sndlink(net);
- if (!bcl)
+ if (!l)
return -ENOPROTOOPT;
tipc_bcast_lock(net);
- memset(&bcl->stats, 0, sizeof(bcl->stats));
+ tipc_link_reset_stats(l);
tipc_bcast_unlock(net);
return 0;
}
@@ -530,9 +414,7 @@ enomem:
void tipc_bcast_reinit(struct net *net)
{
- struct tipc_bc_base *b = tipc_bc_base(net);
-
- msg_set_prevnode(b->link->pmsg, tipc_own_addr(net));
+ tipc_link_reinit(tipc_bc_sndlink(net), tipc_own_addr(net));
}
void tipc_bcast_stop(struct net *net)
diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h
index 2855b9356a15..1944c6c00bb9 100644
--- a/net/tipc/bcast.h
+++ b/net/tipc/bcast.h
@@ -43,6 +43,7 @@ struct tipc_node;
struct tipc_msg;
struct tipc_nl_msg;
struct tipc_node_map;
+extern const char tipc_bclink_name[];
int tipc_bcast_init(struct net *net);
void tipc_bcast_reinit(struct net *net);
diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c
index 648f2a67f314..802ffad3200d 100644
--- a/net/tipc/bearer.c
+++ b/net/tipc/bearer.c
@@ -71,7 +71,7 @@ static const struct nla_policy tipc_nl_media_policy[TIPC_NLA_MEDIA_MAX + 1] = {
[TIPC_NLA_MEDIA_PROP] = { .type = NLA_NESTED }
};
-static void bearer_disable(struct net *net, struct tipc_bearer *b_ptr);
+static void bearer_disable(struct net *net, struct tipc_bearer *b);
/**
* tipc_media_find - locates specified media object by name
@@ -107,13 +107,13 @@ static struct tipc_media *media_find_id(u8 type)
void tipc_media_addr_printf(char *buf, int len, struct tipc_media_addr *a)
{
char addr_str[MAX_ADDR_STR];
- struct tipc_media *m_ptr;
+ struct tipc_media *m;
int ret;
- m_ptr = media_find_id(a->media_id);
+ m = media_find_id(a->media_id);
- if (m_ptr && !m_ptr->addr2str(a, addr_str, sizeof(addr_str)))
- ret = scnprintf(buf, len, "%s(%s)", m_ptr->name, addr_str);
+ if (m && !m->addr2str(a, addr_str, sizeof(addr_str)))
+ ret = scnprintf(buf, len, "%s(%s)", m->name, addr_str);
else {
u32 i;
@@ -175,13 +175,13 @@ static int bearer_name_validate(const char *name,
struct tipc_bearer *tipc_bearer_find(struct net *net, const char *name)
{
struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_bearer *b_ptr;
+ struct tipc_bearer *b;
u32 i;
for (i = 0; i < MAX_BEARERS; i++) {
- b_ptr = rtnl_dereference(tn->bearer_list[i]);
- if (b_ptr && (!strcmp(b_ptr->name, name)))
- return b_ptr;
+ b = rtnl_dereference(tn->bearer_list[i]);
+ if (b && (!strcmp(b->name, name)))
+ return b;
}
return NULL;
}
@@ -189,24 +189,24 @@ struct tipc_bearer *tipc_bearer_find(struct net *net, const char *name)
void tipc_bearer_add_dest(struct net *net, u32 bearer_id, u32 dest)
{
struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_bearer *b_ptr;
+ struct tipc_bearer *b;
rcu_read_lock();
- b_ptr = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);
- if (b_ptr)
- tipc_disc_add_dest(b_ptr->link_req);
+ b = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);
+ if (b)
+ tipc_disc_add_dest(b->link_req);
rcu_read_unlock();
}
void tipc_bearer_remove_dest(struct net *net, u32 bearer_id, u32 dest)
{
struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_bearer *b_ptr;
+ struct tipc_bearer *b;
rcu_read_lock();
- b_ptr = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);
- if (b_ptr)
- tipc_disc_remove_dest(b_ptr->link_req);
+ b = rcu_dereference_rtnl(tn->bearer_list[bearer_id]);
+ if (b)
+ tipc_disc_remove_dest(b->link_req);
rcu_read_unlock();
}
@@ -218,8 +218,8 @@ static int tipc_enable_bearer(struct net *net, const char *name,
struct nlattr *attr[])
{
struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_bearer *b_ptr;
- struct tipc_media *m_ptr;
+ struct tipc_bearer *b;
+ struct tipc_media *m;
struct tipc_bearer_names b_names;
char addr_string[16];
u32 bearer_id;
@@ -255,31 +255,31 @@ static int tipc_enable_bearer(struct net *net, const char *name,
return -EINVAL;
}
- m_ptr = tipc_media_find(b_names.media_name);
- if (!m_ptr) {
+ m = tipc_media_find(b_names.media_name);
+ if (!m) {
pr_warn("Bearer <%s> rejected, media <%s> not registered\n",
name, b_names.media_name);
return -EINVAL;
}
if (priority == TIPC_MEDIA_LINK_PRI)
- priority = m_ptr->priority;
+ priority = m->priority;
restart:
bearer_id = MAX_BEARERS;
with_this_prio = 1;
for (i = MAX_BEARERS; i-- != 0; ) {
- b_ptr = rtnl_dereference(tn->bearer_list[i]);
- if (!b_ptr) {
+ b = rtnl_dereference(tn->bearer_list[i]);
+ if (!b) {
bearer_id = i;
continue;
}
- if (!strcmp(name, b_ptr->name)) {
+ if (!strcmp(name, b->name)) {
pr_warn("Bearer <%s> rejected, already enabled\n",
name);
return -EINVAL;
}
- if ((b_ptr->priority == priority) &&
+ if ((b->priority == priority) &&
(++with_this_prio > 2)) {
if (priority-- == 0) {
pr_warn("Bearer <%s> rejected, duplicate priority\n",
@@ -297,35 +297,35 @@ restart:
return -EINVAL;
}
- b_ptr = kzalloc(sizeof(*b_ptr), GFP_ATOMIC);
- if (!b_ptr)
+ b = kzalloc(sizeof(*b), GFP_ATOMIC);
+ if (!b)
return -ENOMEM;
- strcpy(b_ptr->name, name);
- b_ptr->media = m_ptr;
- res = m_ptr->enable_media(net, b_ptr, attr);
+ strcpy(b->name, name);
+ b->media = m;
+ res = m->enable_media(net, b, attr);
if (res) {
pr_warn("Bearer <%s> rejected, enable failure (%d)\n",
name, -res);
return -EINVAL;
}
- b_ptr->identity = bearer_id;
- b_ptr->tolerance = m_ptr->tolerance;
- b_ptr->window = m_ptr->window;
- b_ptr->domain = disc_domain;
- b_ptr->net_plane = bearer_id + 'A';
- b_ptr->priority = priority;
+ b->identity = bearer_id;
+ b->tolerance = m->tolerance;
+ b->window = m->window;
+ b->domain = disc_domain;
+ b->net_plane = bearer_id + 'A';
+ b->priority = priority;
- res = tipc_disc_create(net, b_ptr, &b_ptr->bcast_addr);
+ res = tipc_disc_create(net, b, &b->bcast_addr);
if (res) {
- bearer_disable(net, b_ptr);
+ bearer_disable(net, b);
pr_warn("Bearer <%s> rejected, discovery object creation failed\n",
name);
return -EINVAL;
}
- rcu_assign_pointer(tn->bearer_list[bearer_id], b_ptr);
+ rcu_assign_pointer(tn->bearer_list[bearer_id], b);
pr_info("Enabled bearer <%s>, discovery domain %s, priority %u\n",
name,
@@ -336,11 +336,11 @@ restart:
/**
* tipc_reset_bearer - Reset all links established over this bearer
*/
-static int tipc_reset_bearer(struct net *net, struct tipc_bearer *b_ptr)
+static int tipc_reset_bearer(struct net *net, struct tipc_bearer *b)
{
- pr_info("Resetting bearer <%s>\n", b_ptr->name);
- tipc_node_delete_links(net, b_ptr->identity);
- tipc_disc_reset(net, b_ptr);
+ pr_info("Resetting bearer <%s>\n", b->name);
+ tipc_node_delete_links(net, b->identity);
+ tipc_disc_reset(net, b);
return 0;
}
@@ -349,26 +349,26 @@ static int tipc_reset_bearer(struct net *net, struct tipc_bearer *b_ptr)
*
* Note: This routine assumes caller holds RTNL lock.
*/
-static void bearer_disable(struct net *net, struct tipc_bearer *b_ptr)
+static void bearer_disable(struct net *net, struct tipc_bearer *b)
{
struct tipc_net *tn = net_generic(net, tipc_net_id);
u32 i;
- pr_info("Disabling bearer <%s>\n", b_ptr->name);
- b_ptr->media->disable_media(b_ptr);
+ pr_info("Disabling bearer <%s>\n", b->name);
+ b->media->disable_media(b);
- tipc_node_delete_links(net, b_ptr->identity);
- RCU_INIT_POINTER(b_ptr->media_ptr, NULL);
- if (b_ptr->link_req)
- tipc_disc_delete(b_ptr->link_req);
+ tipc_node_delete_links(net, b->identity);
+ RCU_INIT_POINTER(b->media_ptr, NULL);
+ if (b->link_req)
+ tipc_disc_delete(b->link_req);
for (i = 0; i < MAX_BEARERS; i++) {
- if (b_ptr == rtnl_dereference(tn->bearer_list[i])) {
+ if (b == rtnl_dereference(tn->bearer_list[i])) {
RCU_INIT_POINTER(tn->bearer_list[i], NULL);
break;
}
}
- kfree_rcu(b_ptr, rcu);
+ kfree_rcu(b, rcu);
}
int tipc_enable_l2_media(struct net *net, struct tipc_bearer *b,
@@ -411,7 +411,7 @@ void tipc_disable_l2_media(struct tipc_bearer *b)
/**
* tipc_l2_send_msg - send a TIPC packet out over an L2 interface
* @buf: the packet to be sent
- * @b_ptr: the bearer through which the packet is to be sent
+ * @b: the bearer through which the packet is to be sent
* @dest: peer destination address
*/
int tipc_l2_send_msg(struct net *net, struct sk_buff *skb,
@@ -532,14 +532,14 @@ void tipc_bearer_bc_xmit(struct net *net, u32 bearer_id,
static int tipc_l2_rcv_msg(struct sk_buff *buf, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev)
{
- struct tipc_bearer *b_ptr;
+ struct tipc_bearer *b;
rcu_read_lock();
- b_ptr = rcu_dereference_rtnl(dev->tipc_ptr);
- if (likely(b_ptr)) {
+ b = rcu_dereference_rtnl(dev->tipc_ptr);
+ if (likely(b)) {
if (likely(buf->pkt_type <= PACKET_BROADCAST)) {
buf->next = NULL;
- tipc_rcv(dev_net(dev), buf, b_ptr);
+ tipc_rcv(dev_net(dev), buf, b);
rcu_read_unlock();
return NET_RX_SUCCESS;
}
@@ -564,13 +564,13 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt,
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct net *net = dev_net(dev);
- struct tipc_bearer *b_ptr;
+ struct tipc_bearer *b;
- b_ptr = rtnl_dereference(dev->tipc_ptr);
- if (!b_ptr)
+ b = rtnl_dereference(dev->tipc_ptr);
+ if (!b)
return NOTIFY_DONE;
- b_ptr->mtu = dev->mtu;
+ b->mtu = dev->mtu;
switch (evt) {
case NETDEV_CHANGE:
@@ -578,16 +578,16 @@ static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt,
break;
case NETDEV_GOING_DOWN:
case NETDEV_CHANGEMTU:
- tipc_reset_bearer(net, b_ptr);
+ tipc_reset_bearer(net, b);
break;
case NETDEV_CHANGEADDR:
- b_ptr->media->raw2addr(b_ptr, &b_ptr->addr,
+ b->media->raw2addr(b, &b->addr,
(char *)dev->dev_addr);
- tipc_reset_bearer(net, b_ptr);
+ tipc_reset_bearer(net, b);
break;
case NETDEV_UNREGISTER:
case NETDEV_CHANGENAME:
- bearer_disable(dev_net(dev), b_ptr);
+ bearer_disable(dev_net(dev), b);
break;
}
return NOTIFY_OK;
@@ -623,13 +623,13 @@ void tipc_bearer_cleanup(void)
void tipc_bearer_stop(struct net *net)
{
struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_bearer *b_ptr;
+ struct tipc_bearer *b;
u32 i;
for (i = 0; i < MAX_BEARERS; i++) {
- b_ptr = rtnl_dereference(tn->bearer_list[i]);
- if (b_ptr) {
- bearer_disable(net, b_ptr);
+ b = rtnl_dereference(tn->bearer_list[i]);
+ if (b) {
+ bearer_disable(net, b);
tn->bearer_list[i] = NULL;
}
}
diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h
index 552185bc4773..e31820516774 100644
--- a/net/tipc/bearer.h
+++ b/net/tipc/bearer.h
@@ -103,11 +103,11 @@ struct tipc_bearer;
*/
struct tipc_media {
int (*send_msg)(struct net *net, struct sk_buff *buf,
- struct tipc_bearer *b_ptr,
+ struct tipc_bearer *b,
struct tipc_media_addr *dest);
- int (*enable_media)(struct net *net, struct tipc_bearer *b_ptr,
+ int (*enable_media)(struct net *net, struct tipc_bearer *b,
struct nlattr *attr[]);
- void (*disable_media)(struct tipc_bearer *b_ptr);
+ void (*disable_media)(struct tipc_bearer *b);
int (*addr2str)(struct tipc_media_addr *addr,
char *strbuf,
int bufsz);
@@ -176,7 +176,7 @@ struct tipc_bearer_names {
* TIPC routines available to supported media types
*/
-void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b_ptr);
+void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b);
/*
* Routines made available to TIPC by supported media types
diff --git a/net/tipc/core.h b/net/tipc/core.h
index 18e95a8020cd..5504d63503df 100644
--- a/net/tipc/core.h
+++ b/net/tipc/core.h
@@ -118,6 +118,11 @@ static inline int tipc_netid(struct net *net)
return tipc_net(net)->net_id;
}
+static inline struct list_head *tipc_nodes(struct net *net)
+{
+ return &tipc_net(net)->node_list;
+}
+
static inline u16 mod(u16 x)
{
return x & 0xffffu;
diff --git a/net/tipc/discover.c b/net/tipc/discover.c
index afe8c47c4085..f1e738e80535 100644
--- a/net/tipc/discover.c
+++ b/net/tipc/discover.c
@@ -75,14 +75,14 @@ struct tipc_link_req {
* tipc_disc_init_msg - initialize a link setup message
* @net: the applicable net namespace
* @type: message type (request or response)
- * @b_ptr: ptr to bearer issuing message
+ * @b: ptr to bearer issuing message
*/
static void tipc_disc_init_msg(struct net *net, struct sk_buff *buf, u32 type,
- struct tipc_bearer *b_ptr)
+ struct tipc_bearer *b)
{
struct tipc_net *tn = net_generic(net, tipc_net_id);
struct tipc_msg *msg;
- u32 dest_domain = b_ptr->domain;
+ u32 dest_domain = b->domain;
msg = buf_msg(buf);
tipc_msg_init(tn->own_addr, msg, LINK_CONFIG, type,
@@ -92,16 +92,16 @@ static void tipc_disc_init_msg(struct net *net, struct sk_buff *buf, u32 type,
msg_set_node_capabilities(msg, TIPC_NODE_CAPABILITIES);
msg_set_dest_domain(msg, dest_domain);
msg_set_bc_netid(msg, tn->net_id);
- b_ptr->media->addr2msg(msg_media_addr(msg), &b_ptr->addr);
+ b->media->addr2msg(msg_media_addr(msg), &b->addr);
}
/**
* disc_dupl_alert - issue node address duplication alert
- * @b_ptr: pointer to bearer detecting duplication
+ * @b: pointer to bearer detecting duplication
* @node_addr: duplicated node address
* @media_addr: media address advertised by duplicated node
*/
-static void disc_dupl_alert(struct tipc_bearer *b_ptr, u32 node_addr,
+static void disc_dupl_alert(struct tipc_bearer *b, u32 node_addr,
struct tipc_media_addr *media_addr)
{
char node_addr_str[16];
@@ -111,7 +111,7 @@ static void disc_dupl_alert(struct tipc_bearer *b_ptr, u32 node_addr,
tipc_media_addr_printf(media_addr_str, sizeof(media_addr_str),
media_addr);
pr_warn("Duplicate %s using %s seen on <%s>\n", node_addr_str,
- media_addr_str, b_ptr->name);
+ media_addr_str, b->name);
}
/**
@@ -261,13 +261,13 @@ exit:
/**
* tipc_disc_create - create object to send periodic link setup requests
* @net: the applicable net namespace
- * @b_ptr: ptr to bearer issuing requests
+ * @b: ptr to bearer issuing requests
* @dest: destination address for request messages
* @dest_domain: network domain to which links can be established
*
* Returns 0 if successful, otherwise -errno.
*/
-int tipc_disc_create(struct net *net, struct tipc_bearer *b_ptr,
+int tipc_disc_create(struct net *net, struct tipc_bearer *b,
struct tipc_media_addr *dest)
{
struct tipc_link_req *req;
@@ -282,17 +282,17 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b_ptr,
return -ENOMEM;
}
- tipc_disc_init_msg(net, req->buf, DSC_REQ_MSG, b_ptr);
+ tipc_disc_init_msg(net, req->buf, DSC_REQ_MSG, b);
memcpy(&req->dest, dest, sizeof(*dest));
req->net = net;
- req->bearer_id = b_ptr->identity;
- req->domain = b_ptr->domain;
+ req->bearer_id = b->identity;
+ req->domain = b->domain;
req->num_nodes = 0;
req->timer_intv = TIPC_LINK_REQ_INIT;
spin_lock_init(&req->lock);
setup_timer(&req->timer, disc_timeout, (unsigned long)req);
mod_timer(&req->timer, jiffies + req->timer_intv);
- b_ptr->link_req = req;
+ b->link_req = req;
skb = skb_clone(req->buf, GFP_ATOMIC);
if (skb)
tipc_bearer_xmit_skb(net, req->bearer_id, skb, &req->dest);
@@ -313,19 +313,19 @@ void tipc_disc_delete(struct tipc_link_req *req)
/**
* tipc_disc_reset - reset object to send periodic link setup requests
* @net: the applicable net namespace
- * @b_ptr: ptr to bearer issuing requests
+ * @b: ptr to bearer issuing requests
* @dest_domain: network domain to which links can be established
*/
-void tipc_disc_reset(struct net *net, struct tipc_bearer *b_ptr)
+void tipc_disc_reset(struct net *net, struct tipc_bearer *b)
{
- struct tipc_link_req *req = b_ptr->link_req;
+ struct tipc_link_req *req = b->link_req;
struct sk_buff *skb;
spin_lock_bh(&req->lock);
- tipc_disc_init_msg(net, req->buf, DSC_REQ_MSG, b_ptr);
+ tipc_disc_init_msg(net, req->buf, DSC_REQ_MSG, b);
req->net = net;
- req->bearer_id = b_ptr->identity;
- req->domain = b_ptr->domain;
+ req->bearer_id = b->identity;
+ req->domain = b->domain;
req->num_nodes = 0;
req->timer_intv = TIPC_LINK_REQ_INIT;
mod_timer(&req->timer, jiffies + req->timer_intv);
diff --git a/net/tipc/link.c b/net/tipc/link.c
index 9efbdbde2b08..0c2944fb9ae0 100644
--- a/net/tipc/link.c
+++ b/net/tipc/link.c
@@ -45,28 +45,156 @@
#include <linux/pkt_sched.h>
+struct tipc_stats {
+ u32 sent_info; /* used in counting # sent packets */
+ u32 recv_info; /* used in counting # recv'd packets */
+ u32 sent_states;
+ u32 recv_states;
+ u32 sent_probes;
+ u32 recv_probes;
+ u32 sent_nacks;
+ u32 recv_nacks;
+ u32 sent_acks;
+ u32 sent_bundled;
+ u32 sent_bundles;
+ u32 recv_bundled;
+ u32 recv_bundles;
+ u32 retransmitted;
+ u32 sent_fragmented;
+ u32 sent_fragments;
+ u32 recv_fragmented;
+ u32 recv_fragments;
+ u32 link_congs; /* # port sends blocked by congestion */
+ u32 deferred_recv;
+ u32 duplicates;
+ u32 max_queue_sz; /* send queue size high water mark */
+ u32 accu_queue_sz; /* used for send queue size profiling */
+ u32 queue_sz_counts; /* used for send queue size profiling */
+ u32 msg_length_counts; /* used for message length profiling */
+ u32 msg_lengths_total; /* used for message length profiling */
+ u32 msg_length_profile[7]; /* used for msg. length profiling */
+};
+
+/**
+ * struct tipc_link - TIPC link data structure
+ * @addr: network address of link's peer node
+ * @name: link name character string
+ * @media_addr: media address to use when sending messages over link
+ * @timer: link timer
+ * @net: pointer to namespace struct
+ * @refcnt: reference counter for permanent references (owner node & timer)
+ * @peer_session: link session # being used by peer end of link
+ * @peer_bearer_id: bearer id used by link's peer endpoint
+ * @bearer_id: local bearer id used by link
+ * @tolerance: minimum link continuity loss needed to reset link [in ms]
+ * @keepalive_intv: link keepalive timer interval
+ * @abort_limit: # of unacknowledged continuity probes needed to reset link
+ * @state: current state of link FSM
+ * @peer_caps: bitmap describing capabilities of peer node
+ * @silent_intv_cnt: # of timer intervals without any reception from peer
+ * @proto_msg: template for control messages generated by link
+ * @pmsg: convenience pointer to "proto_msg" field
+ * @priority: current link priority
+ * @net_plane: current link network plane ('A' through 'H')
+ * @backlog_limit: backlog queue congestion thresholds (indexed by importance)
+ * @exp_msg_count: # of tunnelled messages expected during link changeover
+ * @reset_rcv_checkpt: seq # of last acknowledged message at time of link reset
+ * @mtu: current maximum packet size for this link
+ * @advertised_mtu: advertised own mtu when link is being established
+ * @transmitq: queue for sent, non-acked messages
+ * @backlogq: queue for messages waiting to be sent
+ * @snt_nxt: next sequence number to use for outbound messages
+ * @last_retransmitted: sequence number of most recently retransmitted message
+ * @stale_count: # of identical retransmit requests made by peer
+ * @ackers: # of peers that needs to ack each packet before it can be released
+ * @acked: # last packet acked by a certain peer. Used for broadcast.
+ * @rcv_nxt: next sequence number to expect for inbound messages
+ * @deferred_queue: deferred queue saved OOS b'cast message received from node
+ * @unacked_window: # of inbound messages rx'd without ack'ing back to peer
+ * @inputq: buffer queue for messages to be delivered upwards
+ * @namedq: buffer queue for name table messages to be delivered upwards
+ * @next_out: ptr to first unsent outbound message in queue
+ * @wakeupq: linked list of wakeup msgs waiting for link congestion to abate
+ * @long_msg_seq_no: next identifier to use for outbound fragmented messages
+ * @reasm_buf: head of partially reassembled inbound message fragments
+ * @bc_rcvr: marks that this is a broadcast receiver link
+ * @stats: collects statistics regarding link activity
+ */
+struct tipc_link {
+ u32 addr;
+ char name[TIPC_MAX_LINK_NAME];
+ struct tipc_media_addr *media_addr;
+ struct net *net;
+
+ /* Management and link supervision data */
+ u32 peer_session;
+ u32 peer_bearer_id;
+ u32 bearer_id;
+ u32 tolerance;
+ unsigned long keepalive_intv;
+ u32 abort_limit;
+ u32 state;
+ u16 peer_caps;
+ bool active;
+ u32 silent_intv_cnt;
+ struct {
+ unchar hdr[INT_H_SIZE];
+ unchar body[TIPC_MAX_IF_NAME];
+ } proto_msg;
+ struct tipc_msg *pmsg;
+ u32 priority;
+ char net_plane;
+
+ /* Failover/synch */
+ u16 drop_point;
+ struct sk_buff *failover_reasm_skb;
+
+ /* Max packet negotiation */
+ u16 mtu;
+ u16 advertised_mtu;
+
+ /* Sending */
+ struct sk_buff_head transmq;
+ struct sk_buff_head backlogq;
+ struct {
+ u16 len;
+ u16 limit;
+ } backlog[5];
+ u16 snd_nxt;
+ u16 last_retransm;
+ u16 window;
+ u32 stale_count;
+
+ /* Reception */
+ u16 rcv_nxt;
+ u32 rcv_unacked;
+ struct sk_buff_head deferdq;
+ struct sk_buff_head *inputq;
+ struct sk_buff_head *namedq;
+
+ /* Congestion handling */
+ struct sk_buff_head wakeupq;
+
+ /* Fragmentation/reassembly */
+ struct sk_buff *reasm_buf;
+
+ /* Broadcast */
+ u16 ackers;
+ u16 acked;
+ struct tipc_link *bc_rcvlink;
+ struct tipc_link *bc_sndlink;
+ int nack_state;
+ bool bc_peer_is_up;
+
+ /* Statistics */
+ struct tipc_stats stats;
+};
+
/*
* Error message prefixes
*/
static const char *link_co_err = "Link tunneling error, ";
static const char *link_rst_msg = "Resetting link ";
-static const char tipc_bclink_name[] = "broadcast-link";
-
-static const struct nla_policy tipc_nl_link_policy[TIPC_NLA_LINK_MAX + 1] = {
- [TIPC_NLA_LINK_UNSPEC] = { .type = NLA_UNSPEC },
- [TIPC_NLA_LINK_NAME] = {
- .type = NLA_STRING,
- .len = TIPC_MAX_LINK_NAME
- },
- [TIPC_NLA_LINK_MTU] = { .type = NLA_U32 },
- [TIPC_NLA_LINK_BROADCAST] = { .type = NLA_FLAG },
- [TIPC_NLA_LINK_UP] = { .type = NLA_FLAG },
- [TIPC_NLA_LINK_ACTIVE] = { .type = NLA_FLAG },
- [TIPC_NLA_LINK_PROP] = { .type = NLA_NESTED },
- [TIPC_NLA_LINK_STATS] = { .type = NLA_NESTED },
- [TIPC_NLA_LINK_RX] = { .type = NLA_U32 },
- [TIPC_NLA_LINK_TX] = { .type = NLA_U32 }
-};
/* Properties valid for media, bearar and link */
static const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = {
@@ -117,8 +245,7 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,
u16 rcvgap, int tolerance, int priority,
struct sk_buff_head *xmitq);
-static void link_reset_statistics(struct tipc_link *l_ptr);
-static void link_print(struct tipc_link *l_ptr, const char *str);
+static void link_print(struct tipc_link *l, const char *str);
static void tipc_link_build_nack_msg(struct tipc_link *l,
struct sk_buff_head *xmitq);
static void tipc_link_build_bc_init_msg(struct tipc_link *l,
@@ -183,6 +310,36 @@ void tipc_link_set_active(struct tipc_link *l, bool active)
l->active = active;
}
+u32 tipc_link_id(struct tipc_link *l)
+{
+ return l->peer_bearer_id << 16 | l->bearer_id;
+}
+
+int tipc_link_window(struct tipc_link *l)
+{
+ return l->window;
+}
+
+int tipc_link_prio(struct tipc_link *l)
+{
+ return l->priority;
+}
+
+unsigned long tipc_link_tolerance(struct tipc_link *l)
+{
+ return l->tolerance;
+}
+
+struct sk_buff_head *tipc_link_inputq(struct tipc_link *l)
+{
+ return l->inputq;
+}
+
+char tipc_link_plane(struct tipc_link *l)
+{
+ return l->net_plane;
+}
+
void tipc_link_add_bc_peer(struct tipc_link *snd_l,
struct tipc_link *uc_l,
struct sk_buff_head *xmitq)
@@ -191,6 +348,7 @@ void tipc_link_add_bc_peer(struct tipc_link *snd_l,
snd_l->ackers++;
rcv_l->acked = snd_l->snd_nxt - 1;
+ snd_l->state = LINK_ESTABLISHED;
tipc_link_build_bc_init_msg(uc_l, xmitq);
}
@@ -206,6 +364,7 @@ void tipc_link_remove_bc_peer(struct tipc_link *snd_l,
rcv_l->state = LINK_RESET;
if (!snd_l->ackers) {
tipc_link_reset(snd_l);
+ snd_l->state = LINK_RESET;
__skb_queue_purge(xmitq);
}
}
@@ -225,11 +384,31 @@ int tipc_link_mtu(struct tipc_link *l)
return l->mtu;
}
+u16 tipc_link_rcv_nxt(struct tipc_link *l)
+{
+ return l->rcv_nxt;
+}
+
+u16 tipc_link_acked(struct tipc_link *l)
+{
+ return l->acked;
+}
+
+char *tipc_link_name(struct tipc_link *l)
+{
+ return l->name;
+}
+
static u32 link_own_addr(struct tipc_link *l)
{
return msg_prevnode(l->pmsg);
}
+void tipc_link_reinit(struct tipc_link *l, u32 addr)
+{
+ msg_set_prevnode(l->pmsg, addr);
+}
+
/**
* tipc_link_create - create a new link
* @n: pointer to associated node
@@ -692,7 +871,7 @@ void tipc_link_reset(struct tipc_link *l)
l->stats.recv_info = 0;
l->stale_count = 0;
l->bc_peer_is_up = false;
- link_reset_statistics(l);
+ tipc_link_reset_stats(l);
}
/**
@@ -1085,8 +1264,9 @@ drop:
/*
* Send protocol message to the other endpoint.
*/
-void tipc_link_proto_xmit(struct tipc_link *l, u32 msg_typ, int probe_msg,
- u32 gap, u32 tolerance, u32 priority)
+static void tipc_link_proto_xmit(struct tipc_link *l, u32 msg_typ,
+ int probe_msg, u32 gap, u32 tolerance,
+ u32 priority)
{
struct sk_buff *skb = NULL;
struct sk_buff_head xmitq;
@@ -1260,6 +1440,8 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb,
/* fall thru' */
case ACTIVATE_MSG:
+ skb_linearize(skb);
+ hdr = buf_msg(skb);
/* Complete own link name with peer's interface name */
if_name = strrchr(l->name, ':') + 1;
@@ -1525,53 +1707,17 @@ void tipc_link_set_queue_limits(struct tipc_link *l, u32 win)
l->backlog[TIPC_SYSTEM_IMPORTANCE].limit = max_bulk;
}
-/* tipc_link_find_owner - locate owner node of link by link's name
- * @net: the applicable net namespace
- * @name: pointer to link name string
- * @bearer_id: pointer to index in 'node->links' array where the link was found.
- *
- * Returns pointer to node owning the link, or 0 if no matching link is found.
- */
-static struct tipc_node *tipc_link_find_owner(struct net *net,
- const char *link_name,
- unsigned int *bearer_id)
-{
- struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_link *l_ptr;
- struct tipc_node *n_ptr;
- struct tipc_node *found_node = NULL;
- int i;
-
- *bearer_id = 0;
- rcu_read_lock();
- list_for_each_entry_rcu(n_ptr, &tn->node_list, list) {
- tipc_node_lock(n_ptr);
- for (i = 0; i < MAX_BEARERS; i++) {
- l_ptr = n_ptr->links[i].link;
- if (l_ptr && !strcmp(l_ptr->name, link_name)) {
- *bearer_id = i;
- found_node = n_ptr;
- break;
- }
- }
- tipc_node_unlock(n_ptr);
- if (found_node)
- break;
- }
- rcu_read_unlock();
-
- return found_node;
-}
-
/**
- * link_reset_statistics - reset link statistics
- * @l_ptr: pointer to link
+ * link_reset_stats - reset link statistics
+ * @l: pointer to link
*/
-static void link_reset_statistics(struct tipc_link *l_ptr)
+void tipc_link_reset_stats(struct tipc_link *l)
{
- memset(&l_ptr->stats, 0, sizeof(l_ptr->stats));
- l_ptr->stats.sent_info = l_ptr->snd_nxt;
- l_ptr->stats.recv_info = l_ptr->rcv_nxt;
+ memset(&l->stats, 0, sizeof(l->stats));
+ if (!link_is_bc_sndlink(l)) {
+ l->stats.sent_info = l->snd_nxt;
+ l->stats.recv_info = l->rcv_nxt;
+ }
}
static void link_print(struct tipc_link *l, const char *str)
@@ -1624,84 +1770,6 @@ int tipc_nl_parse_link_prop(struct nlattr *prop, struct nlattr *props[])
return 0;
}
-int tipc_nl_link_set(struct sk_buff *skb, struct genl_info *info)
-{
- int err;
- int res = 0;
- int bearer_id;
- char *name;
- struct tipc_link *link;
- struct tipc_node *node;
- struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1];
- struct net *net = sock_net(skb->sk);
-
- if (!info->attrs[TIPC_NLA_LINK])
- return -EINVAL;
-
- err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX,
- info->attrs[TIPC_NLA_LINK],
- tipc_nl_link_policy);
- if (err)
- return err;
-
- if (!attrs[TIPC_NLA_LINK_NAME])
- return -EINVAL;
-
- name = nla_data(attrs[TIPC_NLA_LINK_NAME]);
-
- if (strcmp(name, tipc_bclink_name) == 0)
- return tipc_nl_bc_link_set(net, attrs);
-
- node = tipc_link_find_owner(net, name, &bearer_id);
- if (!node)
- return -EINVAL;
-
- tipc_node_lock(node);
-
- link = node->links[bearer_id].link;
- if (!link) {
- res = -EINVAL;
- goto out;
- }
-
- if (attrs[TIPC_NLA_LINK_PROP]) {
- struct nlattr *props[TIPC_NLA_PROP_MAX + 1];
-
- err = tipc_nl_parse_link_prop(attrs[TIPC_NLA_LINK_PROP],
- props);
- if (err) {
- res = err;
- goto out;
- }
-
- if (props[TIPC_NLA_PROP_TOL]) {
- u32 tol;
-
- tol = nla_get_u32(props[TIPC_NLA_PROP_TOL]);
- link->tolerance = tol;
- tipc_link_proto_xmit(link, STATE_MSG, 0, 0, tol, 0);
- }
- if (props[TIPC_NLA_PROP_PRIO]) {
- u32 prio;
-
- prio = nla_get_u32(props[TIPC_NLA_PROP_PRIO]);
- link->priority = prio;
- tipc_link_proto_xmit(link, STATE_MSG, 0, 0, 0, prio);
- }
- if (props[TIPC_NLA_PROP_WIN]) {
- u32 win;
-
- win = nla_get_u32(props[TIPC_NLA_PROP_WIN]);
- tipc_link_set_queue_limits(link, win);
- }
- }
-
-out:
- tipc_node_unlock(node);
-
- return res;
-}
-
static int __tipc_nl_add_stats(struct sk_buff *skb, struct tipc_stats *s)
{
int i;
@@ -1768,8 +1836,8 @@ msg_full:
}
/* Caller should hold appropriate locks to protect the link */
-static int __tipc_nl_add_link(struct net *net, struct tipc_nl_msg *msg,
- struct tipc_link *link, int nlflags)
+int __tipc_nl_add_link(struct net *net, struct tipc_nl_msg *msg,
+ struct tipc_link *link, int nlflags)
{
int err;
void *hdr;
@@ -1838,198 +1906,134 @@ msg_full:
return -EMSGSIZE;
}
-/* Caller should hold node lock */
-static int __tipc_nl_add_node_links(struct net *net, struct tipc_nl_msg *msg,
- struct tipc_node *node, u32 *prev_link)
+static int __tipc_nl_add_bc_link_stat(struct sk_buff *skb,
+ struct tipc_stats *stats)
{
- u32 i;
- int err;
-
- for (i = *prev_link; i < MAX_BEARERS; i++) {
- *prev_link = i;
-
- if (!node->links[i].link)
- continue;
+ int i;
+ struct nlattr *nest;
- err = __tipc_nl_add_link(net, msg,
- node->links[i].link, NLM_F_MULTI);
- if (err)
- return err;
- }
- *prev_link = 0;
+ struct nla_map {
+ __u32 key;
+ __u32 val;
+ };
- return 0;
-}
+ struct nla_map map[] = {
+ {TIPC_NLA_STATS_RX_INFO, stats->recv_info},
+ {TIPC_NLA_STATS_RX_FRAGMENTS, stats->recv_fragments},
+ {TIPC_NLA_STATS_RX_FRAGMENTED, stats->recv_fragmented},
+ {TIPC_NLA_STATS_RX_BUNDLES, stats->recv_bundles},
+ {TIPC_NLA_STATS_RX_BUNDLED, stats->recv_bundled},
+ {TIPC_NLA_STATS_TX_INFO, stats->sent_info},
+ {TIPC_NLA_STATS_TX_FRAGMENTS, stats->sent_fragments},
+ {TIPC_NLA_STATS_TX_FRAGMENTED, stats->sent_fragmented},
+ {TIPC_NLA_STATS_TX_BUNDLES, stats->sent_bundles},
+ {TIPC_NLA_STATS_TX_BUNDLED, stats->sent_bundled},
+ {TIPC_NLA_STATS_RX_NACKS, stats->recv_nacks},
+ {TIPC_NLA_STATS_RX_DEFERRED, stats->deferred_recv},
+ {TIPC_NLA_STATS_TX_NACKS, stats->sent_nacks},
+ {TIPC_NLA_STATS_TX_ACKS, stats->sent_acks},
+ {TIPC_NLA_STATS_RETRANSMITTED, stats->retransmitted},
+ {TIPC_NLA_STATS_DUPLICATES, stats->duplicates},
+ {TIPC_NLA_STATS_LINK_CONGS, stats->link_congs},
+ {TIPC_NLA_STATS_MAX_QUEUE, stats->max_queue_sz},
+ {TIPC_NLA_STATS_AVG_QUEUE, stats->queue_sz_counts ?
+ (stats->accu_queue_sz / stats->queue_sz_counts) : 0}
+ };
-int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb)
-{
- struct net *net = sock_net(skb->sk);
- struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_node *node;
- struct tipc_nl_msg msg;
- u32 prev_node = cb->args[0];
- u32 prev_link = cb->args[1];
- int done = cb->args[2];
- int err;
+ nest = nla_nest_start(skb, TIPC_NLA_LINK_STATS);
+ if (!nest)
+ return -EMSGSIZE;
- if (done)
- return 0;
+ for (i = 0; i < ARRAY_SIZE(map); i++)
+ if (nla_put_u32(skb, map[i].key, map[i].val))
+ goto msg_full;
- msg.skb = skb;
- msg.portid = NETLINK_CB(cb->skb).portid;
- msg.seq = cb->nlh->nlmsg_seq;
-
- rcu_read_lock();
- if (prev_node) {
- node = tipc_node_find(net, prev_node);
- if (!node) {
- /* We never set seq or call nl_dump_check_consistent()
- * this means that setting prev_seq here will cause the
- * consistence check to fail in the netlink callback
- * handler. Resulting in the last NLMSG_DONE message
- * having the NLM_F_DUMP_INTR flag set.
- */
- cb->prev_seq = 1;
- goto out;
- }
- tipc_node_put(node);
-
- list_for_each_entry_continue_rcu(node, &tn->node_list,
- list) {
- tipc_node_lock(node);
- err = __tipc_nl_add_node_links(net, &msg, node,
- &prev_link);
- tipc_node_unlock(node);
- if (err)
- goto out;
-
- prev_node = node->addr;
- }
- } else {
- err = tipc_nl_add_bc_link(net, &msg);
- if (err)
- goto out;
-
- list_for_each_entry_rcu(node, &tn->node_list, list) {
- tipc_node_lock(node);
- err = __tipc_nl_add_node_links(net, &msg, node,
- &prev_link);
- tipc_node_unlock(node);
- if (err)
- goto out;
-
- prev_node = node->addr;
- }
- }
- done = 1;
-out:
- rcu_read_unlock();
+ nla_nest_end(skb, nest);
- cb->args[0] = prev_node;
- cb->args[1] = prev_link;
- cb->args[2] = done;
+ return 0;
+msg_full:
+ nla_nest_cancel(skb, nest);
- return skb->len;
+ return -EMSGSIZE;
}
-int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info)
+int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg)
{
- struct net *net = genl_info_net(info);
- struct tipc_nl_msg msg;
- char *name;
int err;
+ void *hdr;
+ struct nlattr *attrs;
+ struct nlattr *prop;
+ struct tipc_net *tn = net_generic(net, tipc_net_id);
+ struct tipc_link *bcl = tn->bcl;
- msg.portid = info->snd_portid;
- msg.seq = info->snd_seq;
-
- if (!info->attrs[TIPC_NLA_LINK_NAME])
- return -EINVAL;
- name = nla_data(info->attrs[TIPC_NLA_LINK_NAME]);
+ if (!bcl)
+ return 0;
- msg.skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
- if (!msg.skb)
- return -ENOMEM;
+ tipc_bcast_lock(net);
- if (strcmp(name, tipc_bclink_name) == 0) {
- err = tipc_nl_add_bc_link(net, &msg);
- if (err) {
- nlmsg_free(msg.skb);
- return err;
- }
- } else {
- int bearer_id;
- struct tipc_node *node;
- struct tipc_link *link;
+ hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_family,
+ NLM_F_MULTI, TIPC_NL_LINK_GET);
+ if (!hdr)
+ return -EMSGSIZE;
- node = tipc_link_find_owner(net, name, &bearer_id);
- if (!node)
- return -EINVAL;
+ attrs = nla_nest_start(msg->skb, TIPC_NLA_LINK);
+ if (!attrs)
+ goto msg_full;
- tipc_node_lock(node);
- link = node->links[bearer_id].link;
- if (!link) {
- tipc_node_unlock(node);
- nlmsg_free(msg.skb);
- return -EINVAL;
- }
+ /* The broadcast link is always up */
+ if (nla_put_flag(msg->skb, TIPC_NLA_LINK_UP))
+ goto attr_msg_full;
- err = __tipc_nl_add_link(net, &msg, link, 0);
- tipc_node_unlock(node);
- if (err) {
- nlmsg_free(msg.skb);
- return err;
- }
- }
+ if (nla_put_flag(msg->skb, TIPC_NLA_LINK_BROADCAST))
+ goto attr_msg_full;
+ if (nla_put_string(msg->skb, TIPC_NLA_LINK_NAME, bcl->name))
+ goto attr_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_LINK_RX, bcl->rcv_nxt))
+ goto attr_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_LINK_TX, bcl->snd_nxt))
+ goto attr_msg_full;
- return genlmsg_reply(msg.skb, info);
-}
+ prop = nla_nest_start(msg->skb, TIPC_NLA_LINK_PROP);
+ if (!prop)
+ goto attr_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN, bcl->window))
+ goto prop_msg_full;
+ nla_nest_end(msg->skb, prop);
-int tipc_nl_link_reset_stats(struct sk_buff *skb, struct genl_info *info)
-{
- int err;
- char *link_name;
- unsigned int bearer_id;
- struct tipc_link *link;
- struct tipc_node *node;
- struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1];
- struct net *net = sock_net(skb->sk);
-
- if (!info->attrs[TIPC_NLA_LINK])
- return -EINVAL;
-
- err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX,
- info->attrs[TIPC_NLA_LINK],
- tipc_nl_link_policy);
+ err = __tipc_nl_add_bc_link_stat(msg->skb, &bcl->stats);
if (err)
- return err;
-
- if (!attrs[TIPC_NLA_LINK_NAME])
- return -EINVAL;
-
- link_name = nla_data(attrs[TIPC_NLA_LINK_NAME]);
+ goto attr_msg_full;
- if (strcmp(link_name, tipc_bclink_name) == 0) {
- err = tipc_bclink_reset_stats(net);
- if (err)
- return err;
- return 0;
- }
+ tipc_bcast_unlock(net);
+ nla_nest_end(msg->skb, attrs);
+ genlmsg_end(msg->skb, hdr);
- node = tipc_link_find_owner(net, link_name, &bearer_id);
- if (!node)
- return -EINVAL;
+ return 0;
- tipc_node_lock(node);
+prop_msg_full:
+ nla_nest_cancel(msg->skb, prop);
+attr_msg_full:
+ nla_nest_cancel(msg->skb, attrs);
+msg_full:
+ tipc_bcast_unlock(net);
+ genlmsg_cancel(msg->skb, hdr);
- link = node->links[bearer_id].link;
- if (!link) {
- tipc_node_unlock(node);
- return -EINVAL;
- }
+ return -EMSGSIZE;
+}
- link_reset_statistics(link);
+void tipc_link_set_tolerance(struct tipc_link *l, u32 tol)
+{
+ l->tolerance = tol;
+ tipc_link_proto_xmit(l, STATE_MSG, 0, 0, tol, 0);
+}
- tipc_node_unlock(node);
+void tipc_link_set_prio(struct tipc_link *l, u32 prio)
+{
+ l->priority = prio;
+ tipc_link_proto_xmit(l, STATE_MSG, 0, 0, 0, prio);
+}
- return 0;
+void tipc_link_set_abort_limit(struct tipc_link *l, u32 limit)
+{
+ l->abort_limit = limit;
}
diff --git a/net/tipc/link.h b/net/tipc/link.h
index 66d859b66c84..b2ae0f4276af 100644
--- a/net/tipc/link.h
+++ b/net/tipc/link.h
@@ -45,10 +45,6 @@
*/
#define ELINKCONG EAGAIN /* link congestion <=> resource unavailable */
-/* Out-of-range value for link sequence numbers
- */
-#define INVALID_LINK_SEQ 0x10000
-
/* Link FSM events:
*/
enum {
@@ -75,151 +71,6 @@ enum {
*/
#define MAX_PKT_DEFAULT 1500
-struct tipc_stats {
- u32 sent_info; /* used in counting # sent packets */
- u32 recv_info; /* used in counting # recv'd packets */
- u32 sent_states;
- u32 recv_states;
- u32 sent_probes;
- u32 recv_probes;
- u32 sent_nacks;
- u32 recv_nacks;
- u32 sent_acks;
- u32 sent_bundled;
- u32 sent_bundles;
- u32 recv_bundled;
- u32 recv_bundles;
- u32 retransmitted;
- u32 sent_fragmented;
- u32 sent_fragments;
- u32 recv_fragmented;
- u32 recv_fragments;
- u32 link_congs; /* # port sends blocked by congestion */
- u32 deferred_recv;
- u32 duplicates;
- u32 max_queue_sz; /* send queue size high water mark */
- u32 accu_queue_sz; /* used for send queue size profiling */
- u32 queue_sz_counts; /* used for send queue size profiling */
- u32 msg_length_counts; /* used for message length profiling */
- u32 msg_lengths_total; /* used for message length profiling */
- u32 msg_length_profile[7]; /* used for msg. length profiling */
-};
-
-/**
- * struct tipc_link - TIPC link data structure
- * @addr: network address of link's peer node
- * @name: link name character string
- * @media_addr: media address to use when sending messages over link
- * @timer: link timer
- * @net: pointer to namespace struct
- * @refcnt: reference counter for permanent references (owner node & timer)
- * @peer_session: link session # being used by peer end of link
- * @peer_bearer_id: bearer id used by link's peer endpoint
- * @bearer_id: local bearer id used by link
- * @tolerance: minimum link continuity loss needed to reset link [in ms]
- * @keepalive_intv: link keepalive timer interval
- * @abort_limit: # of unacknowledged continuity probes needed to reset link
- * @state: current state of link FSM
- * @peer_caps: bitmap describing capabilities of peer node
- * @silent_intv_cnt: # of timer intervals without any reception from peer
- * @proto_msg: template for control messages generated by link
- * @pmsg: convenience pointer to "proto_msg" field
- * @priority: current link priority
- * @net_plane: current link network plane ('A' through 'H')
- * @backlog_limit: backlog queue congestion thresholds (indexed by importance)
- * @exp_msg_count: # of tunnelled messages expected during link changeover
- * @reset_rcv_checkpt: seq # of last acknowledged message at time of link reset
- * @mtu: current maximum packet size for this link
- * @advertised_mtu: advertised own mtu when link is being established
- * @transmitq: queue for sent, non-acked messages
- * @backlogq: queue for messages waiting to be sent
- * @snt_nxt: next sequence number to use for outbound messages
- * @last_retransmitted: sequence number of most recently retransmitted message
- * @stale_count: # of identical retransmit requests made by peer
- * @ackers: # of peers that needs to ack each packet before it can be released
- * @acked: # last packet acked by a certain peer. Used for broadcast.
- * @rcv_nxt: next sequence number to expect for inbound messages
- * @deferred_queue: deferred queue saved OOS b'cast message received from node
- * @unacked_window: # of inbound messages rx'd without ack'ing back to peer
- * @inputq: buffer queue for messages to be delivered upwards
- * @namedq: buffer queue for name table messages to be delivered upwards
- * @next_out: ptr to first unsent outbound message in queue
- * @wakeupq: linked list of wakeup msgs waiting for link congestion to abate
- * @long_msg_seq_no: next identifier to use for outbound fragmented messages
- * @reasm_buf: head of partially reassembled inbound message fragments
- * @bc_rcvr: marks that this is a broadcast receiver link
- * @stats: collects statistics regarding link activity
- */
-struct tipc_link {
- u32 addr;
- char name[TIPC_MAX_LINK_NAME];
- struct tipc_media_addr *media_addr;
- struct net *net;
-
- /* Management and link supervision data */
- u32 peer_session;
- u32 peer_bearer_id;
- u32 bearer_id;
- u32 tolerance;
- unsigned long keepalive_intv;
- u32 abort_limit;
- u32 state;
- u16 peer_caps;
- bool active;
- u32 silent_intv_cnt;
- struct {
- unchar hdr[INT_H_SIZE];
- unchar body[TIPC_MAX_IF_NAME];
- } proto_msg;
- struct tipc_msg *pmsg;
- u32 priority;
- char net_plane;
-
- /* Failover/synch */
- u16 drop_point;
- struct sk_buff *failover_reasm_skb;
-
- /* Max packet negotiation */
- u16 mtu;
- u16 advertised_mtu;
-
- /* Sending */
- struct sk_buff_head transmq;
- struct sk_buff_head backlogq;
- struct {
- u16 len;
- u16 limit;
- } backlog[5];
- u16 snd_nxt;
- u16 last_retransm;
- u16 window;
- u32 stale_count;
-
- /* Reception */
- u16 rcv_nxt;
- u32 rcv_unacked;
- struct sk_buff_head deferdq;
- struct sk_buff_head *inputq;
- struct sk_buff_head *namedq;
-
- /* Congestion handling */
- struct sk_buff_head wakeupq;
-
- /* Fragmentation/reassembly */
- struct sk_buff *reasm_buf;
-
- /* Broadcast */
- u16 ackers;
- u16 acked;
- struct tipc_link *bc_rcvlink;
- struct tipc_link *bc_sndlink;
- int nack_state;
- bool bc_peer_is_up;
-
- /* Statistics */
- struct tipc_stats stats;
-};
-
bool tipc_link_create(struct net *net, char *if_name, int bearer_id,
int tolerance, char net_plane, u32 mtu, int priority,
int window, u32 session, u32 ownnode, u32 peer,
@@ -235,11 +86,11 @@ bool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer,
struct sk_buff_head *namedq,
struct tipc_link *bc_sndlink,
struct tipc_link **link);
+void tipc_link_reinit(struct tipc_link *l, u32 addr);
void tipc_link_tnl_prepare(struct tipc_link *l, struct tipc_link *tnl,
int mtyp, struct sk_buff_head *xmitq);
void tipc_link_build_reset_msg(struct tipc_link *l, struct sk_buff_head *xmitq);
int tipc_link_fsm_evt(struct tipc_link *l, int evt);
-void tipc_link_reset_fragments(struct tipc_link *l_ptr);
bool tipc_link_is_up(struct tipc_link *l);
bool tipc_link_peer_is_down(struct tipc_link *l);
bool tipc_link_is_reset(struct tipc_link *l);
@@ -248,15 +99,25 @@ bool tipc_link_is_synching(struct tipc_link *l);
bool tipc_link_is_failingover(struct tipc_link *l);
bool tipc_link_is_blocked(struct tipc_link *l);
void tipc_link_set_active(struct tipc_link *l, bool active);
-void tipc_link_reset(struct tipc_link *l_ptr);
-int tipc_link_xmit(struct tipc_link *link, struct sk_buff_head *list,
+void tipc_link_reset(struct tipc_link *l);
+void tipc_link_reset_stats(struct tipc_link *l);
+int tipc_link_xmit(struct tipc_link *link, struct sk_buff_head *list,
struct sk_buff_head *xmitq);
+struct sk_buff_head *tipc_link_inputq(struct tipc_link *l);
+u16 tipc_link_rcv_nxt(struct tipc_link *l);
+u16 tipc_link_acked(struct tipc_link *l);
+u32 tipc_link_id(struct tipc_link *l);
+char *tipc_link_name(struct tipc_link *l);
+char tipc_link_plane(struct tipc_link *l);
+int tipc_link_prio(struct tipc_link *l);
+int tipc_link_window(struct tipc_link *l);
+unsigned long tipc_link_tolerance(struct tipc_link *l);
+void tipc_link_set_tolerance(struct tipc_link *l, u32 tol);
+void tipc_link_set_prio(struct tipc_link *l, u32 prio);
+void tipc_link_set_abort_limit(struct tipc_link *l, u32 limit);
void tipc_link_set_queue_limits(struct tipc_link *l, u32 window);
-
-int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb);
-int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info);
-int tipc_nl_link_set(struct sk_buff *skb, struct genl_info *info);
-int tipc_nl_link_reset_stats(struct sk_buff *skb, struct genl_info *info);
+int __tipc_nl_add_link(struct net *net, struct tipc_nl_msg *msg,
+ struct tipc_link *link, int nlflags);
int tipc_nl_parse_link_prop(struct nlattr *prop, struct nlattr *props[]);
int tipc_link_timeout(struct tipc_link *l, struct sk_buff_head *xmitq);
int tipc_link_rcv(struct tipc_link *l, struct sk_buff *skb,
diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c
index c07612bab95c..ebe9d0ff6e9e 100644
--- a/net/tipc/name_distr.c
+++ b/net/tipc/name_distr.c
@@ -84,31 +84,6 @@ static struct sk_buff *named_prepare_buf(struct net *net, u32 type, u32 size,
return buf;
}
-void named_cluster_distribute(struct net *net, struct sk_buff *skb)
-{
- struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct sk_buff *oskb;
- struct tipc_node *node;
- u32 dnode;
-
- rcu_read_lock();
- list_for_each_entry_rcu(node, &tn->node_list, list) {
- dnode = node->addr;
- if (in_own_node(net, dnode))
- continue;
- if (!tipc_node_is_up(node))
- continue;
- oskb = pskb_copy(skb, GFP_ATOMIC);
- if (!oskb)
- break;
- msg_set_destnode(buf_msg(oskb), dnode);
- tipc_node_xmit_skb(net, oskb, dnode, 0);
- }
- rcu_read_unlock();
-
- kfree_skb(skb);
-}
-
/**
* tipc_named_publish - tell other nodes about a new publication by this node
*/
@@ -226,42 +201,6 @@ void tipc_named_node_up(struct net *net, u32 dnode)
tipc_node_xmit(net, &head, dnode, 0);
}
-static void tipc_publ_subscribe(struct net *net, struct publication *publ,
- u32 addr)
-{
- struct tipc_node *node;
-
- if (in_own_node(net, addr))
- return;
-
- node = tipc_node_find(net, addr);
- if (!node) {
- pr_warn("Node subscription rejected, unknown node 0x%x\n",
- addr);
- return;
- }
-
- tipc_node_lock(node);
- list_add_tail(&publ->nodesub_list, &node->publ_list);
- tipc_node_unlock(node);
- tipc_node_put(node);
-}
-
-static void tipc_publ_unsubscribe(struct net *net, struct publication *publ,
- u32 addr)
-{
- struct tipc_node *node;
-
- node = tipc_node_find(net, addr);
- if (!node)
- return;
-
- tipc_node_lock(node);
- list_del_init(&publ->nodesub_list);
- tipc_node_unlock(node);
- tipc_node_put(node);
-}
-
/**
* tipc_publ_purge - remove publication associated with a failed node
*
@@ -277,7 +216,7 @@ static void tipc_publ_purge(struct net *net, struct publication *publ, u32 addr)
p = tipc_nametbl_remove_publ(net, publ->type, publ->lower,
publ->node, publ->ref, publ->key);
if (p)
- tipc_publ_unsubscribe(net, p, addr);
+ tipc_node_unsubscribe(net, &p->nodesub_list, addr);
spin_unlock_bh(&tn->nametbl_lock);
if (p != publ) {
@@ -317,7 +256,7 @@ static bool tipc_update_nametbl(struct net *net, struct distr_item *i,
TIPC_CLUSTER_SCOPE, node,
ntohl(i->ref), ntohl(i->key));
if (publ) {
- tipc_publ_subscribe(net, publ, node);
+ tipc_node_subscribe(net, &publ->nodesub_list, node);
return true;
}
} else if (dtype == WITHDRAWAL) {
@@ -326,7 +265,7 @@ static bool tipc_update_nametbl(struct net *net, struct distr_item *i,
node, ntohl(i->ref),
ntohl(i->key));
if (publ) {
- tipc_publ_unsubscribe(net, publ, node);
+ tipc_node_unsubscribe(net, &publ->nodesub_list, node);
kfree_rcu(publ, rcu);
return true;
}
@@ -397,6 +336,7 @@ void tipc_named_rcv(struct net *net, struct sk_buff_head *inputq)
spin_lock_bh(&tn->nametbl_lock);
for (skb = skb_dequeue(inputq); skb; skb = skb_dequeue(inputq)) {
+ skb_linearize(skb);
msg = buf_msg(skb);
mtype = msg_type(msg);
item = (struct distr_item *)msg_data(msg);
diff --git a/net/tipc/name_distr.h b/net/tipc/name_distr.h
index dd2d9fd80da2..1264ba0af937 100644
--- a/net/tipc/name_distr.h
+++ b/net/tipc/name_distr.h
@@ -69,7 +69,6 @@ struct distr_item {
struct sk_buff *tipc_named_publish(struct net *net, struct publication *publ);
struct sk_buff *tipc_named_withdraw(struct net *net, struct publication *publ);
-void named_cluster_distribute(struct net *net, struct sk_buff *buf);
void tipc_named_node_up(struct net *net, u32 dnode);
void tipc_named_rcv(struct net *net, struct sk_buff_head *msg_queue);
void tipc_named_reinit(struct net *net);
diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c
index 0f47f08bf38f..91fce70291a8 100644
--- a/net/tipc/name_table.c
+++ b/net/tipc/name_table.c
@@ -42,6 +42,7 @@
#include "subscr.h"
#include "bcast.h"
#include "addr.h"
+#include "node.h"
#include <net/genetlink.h>
#define TIPC_NAMETBL_SIZE 1024 /* must be a power of 2 */
@@ -677,7 +678,7 @@ struct publication *tipc_nametbl_publish(struct net *net, u32 type, u32 lower,
spin_unlock_bh(&tn->nametbl_lock);
if (buf)
- named_cluster_distribute(net, buf);
+ tipc_node_broadcast(net, buf);
return publ;
}
@@ -709,7 +710,7 @@ int tipc_nametbl_withdraw(struct net *net, u32 type, u32 lower, u32 ref,
spin_unlock_bh(&tn->nametbl_lock);
if (skb) {
- named_cluster_distribute(net, skb);
+ tipc_node_broadcast(net, skb);
return 1;
}
return 0;
diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c
index 7f6475efc984..8975b0135b76 100644
--- a/net/tipc/netlink.c
+++ b/net/tipc/netlink.c
@@ -101,18 +101,18 @@ static const struct genl_ops tipc_genl_v2_ops[] = {
},
{
.cmd = TIPC_NL_LINK_GET,
- .doit = tipc_nl_link_get,
- .dumpit = tipc_nl_link_dump,
+ .doit = tipc_nl_node_get_link,
+ .dumpit = tipc_nl_node_dump_link,
.policy = tipc_nl_policy,
},
{
.cmd = TIPC_NL_LINK_SET,
- .doit = tipc_nl_link_set,
+ .doit = tipc_nl_node_set_link,
.policy = tipc_nl_policy,
},
{
.cmd = TIPC_NL_LINK_RESET_STATS,
- .doit = tipc_nl_link_reset_stats,
+ .doit = tipc_nl_node_reset_link_stats,
.policy = tipc_nl_policy,
},
{
diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c
index 1eadc95e1132..2c016fdefe97 100644
--- a/net/tipc/netlink_compat.c
+++ b/net/tipc/netlink_compat.c
@@ -1023,25 +1023,25 @@ static int tipc_nl_compat_handle(struct tipc_nl_compat_msg *msg)
msg->req_type = TIPC_TLV_LINK_NAME;
msg->rep_size = ULTRA_STRING_MAX_LEN;
msg->rep_type = TIPC_TLV_ULTRA_STRING;
- dump.dumpit = tipc_nl_link_dump;
+ dump.dumpit = tipc_nl_node_dump_link;
dump.format = tipc_nl_compat_link_stat_dump;
return tipc_nl_compat_dumpit(&dump, msg);
case TIPC_CMD_GET_LINKS:
msg->req_type = TIPC_TLV_NET_ADDR;
msg->rep_size = ULTRA_STRING_MAX_LEN;
- dump.dumpit = tipc_nl_link_dump;
+ dump.dumpit = tipc_nl_node_dump_link;
dump.format = tipc_nl_compat_link_dump;
return tipc_nl_compat_dumpit(&dump, msg);
case TIPC_CMD_SET_LINK_TOL:
case TIPC_CMD_SET_LINK_PRI:
case TIPC_CMD_SET_LINK_WINDOW:
msg->req_type = TIPC_TLV_LINK_CONFIG;
- doit.doit = tipc_nl_link_set;
+ doit.doit = tipc_nl_node_set_link;
doit.transcode = tipc_nl_compat_link_set;
return tipc_nl_compat_doit(&doit, msg);
case TIPC_CMD_RESET_LINK_STATS:
msg->req_type = TIPC_TLV_LINK_NAME;
- doit.doit = tipc_nl_link_reset_stats;
+ doit.doit = tipc_nl_node_reset_link_stats;
doit.transcode = tipc_nl_compat_link_reset_stats;
return tipc_nl_compat_doit(&doit, msg);
case TIPC_CMD_SHOW_NAME_TABLE:
diff --git a/net/tipc/node.c b/net/tipc/node.c
index 20cddec0a43c..fa97d9649a28 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -42,6 +42,84 @@
#include "bcast.h"
#include "discover.h"
+#define INVALID_NODE_SIG 0x10000
+
+/* Flags used to take different actions according to flag type
+ * TIPC_NOTIFY_NODE_DOWN: notify node is down
+ * TIPC_NOTIFY_NODE_UP: notify node is up
+ * TIPC_DISTRIBUTE_NAME: publish or withdraw link state name type
+ */
+enum {
+ TIPC_NOTIFY_NODE_DOWN = (1 << 3),
+ TIPC_NOTIFY_NODE_UP = (1 << 4),
+ TIPC_NOTIFY_LINK_UP = (1 << 6),
+ TIPC_NOTIFY_LINK_DOWN = (1 << 7)
+};
+
+struct tipc_link_entry {
+ struct tipc_link *link;
+ spinlock_t lock; /* per link */
+ u32 mtu;
+ struct sk_buff_head inputq;
+ struct tipc_media_addr maddr;
+};
+
+struct tipc_bclink_entry {
+ struct tipc_link *link;
+ struct sk_buff_head inputq1;
+ struct sk_buff_head arrvq;
+ struct sk_buff_head inputq2;
+ struct sk_buff_head namedq;
+};
+
+/**
+ * struct tipc_node - TIPC node structure
+ * @addr: network address of node
+ * @ref: reference counter to node object
+ * @lock: rwlock governing access to structure
+ * @net: the applicable net namespace
+ * @hash: links to adjacent nodes in unsorted hash chain
+ * @inputq: pointer to input queue containing messages for msg event
+ * @namedq: pointer to name table input queue with name table messages
+ * @active_links: bearer ids of active links, used as index into links[] array
+ * @links: array containing references to all links to node
+ * @action_flags: bit mask of different types of node actions
+ * @state: connectivity state vs peer node
+ * @sync_point: sequence number where synch/failover is finished
+ * @list: links to adjacent nodes in sorted list of cluster's nodes
+ * @working_links: number of working links to node (both active and standby)
+ * @link_cnt: number of links to node
+ * @capabilities: bitmap, indicating peer node's functional capabilities
+ * @signature: node instance identifier
+ * @link_id: local and remote bearer ids of changing link, if any
+ * @publ_list: list of publications
+ * @rcu: rcu struct for tipc_node
+ */
+struct tipc_node {
+ u32 addr;
+ struct kref kref;
+ rwlock_t lock;
+ struct net *net;
+ struct hlist_node hash;
+ int active_links[2];
+ struct tipc_link_entry links[MAX_BEARERS];
+ struct tipc_bclink_entry bc_entry;
+ int action_flags;
+ struct list_head list;
+ int state;
+ u16 sync_point;
+ int link_cnt;
+ u16 working_links;
+ u16 capabilities;
+ u32 signature;
+ u32 link_id;
+ struct list_head publ_list;
+ struct list_head conn_sks;
+ unsigned long keepalive_intv;
+ struct timer_list timer;
+ struct rcu_head rcu;
+};
+
/* Node FSM states and events:
*/
enum {
@@ -75,6 +153,9 @@ static void node_lost_contact(struct tipc_node *n, struct sk_buff_head *inputq);
static void tipc_node_delete(struct tipc_node *node);
static void tipc_node_timeout(unsigned long data);
static void tipc_node_fsm_evt(struct tipc_node *n, int evt);
+static struct tipc_node *tipc_node_find(struct net *net, u32 addr);
+static void tipc_node_put(struct tipc_node *node);
+static bool tipc_node_is_up(struct tipc_node *n);
struct tipc_sock_conn {
u32 port;
@@ -83,12 +164,54 @@ struct tipc_sock_conn {
struct list_head list;
};
+static const struct nla_policy tipc_nl_link_policy[TIPC_NLA_LINK_MAX + 1] = {
+ [TIPC_NLA_LINK_UNSPEC] = { .type = NLA_UNSPEC },
+ [TIPC_NLA_LINK_NAME] = {
+ .type = NLA_STRING,
+ .len = TIPC_MAX_LINK_NAME
+ },
+ [TIPC_NLA_LINK_MTU] = { .type = NLA_U32 },
+ [TIPC_NLA_LINK_BROADCAST] = { .type = NLA_FLAG },
+ [TIPC_NLA_LINK_UP] = { .type = NLA_FLAG },
+ [TIPC_NLA_LINK_ACTIVE] = { .type = NLA_FLAG },
+ [TIPC_NLA_LINK_PROP] = { .type = NLA_NESTED },
+ [TIPC_NLA_LINK_STATS] = { .type = NLA_NESTED },
+ [TIPC_NLA_LINK_RX] = { .type = NLA_U32 },
+ [TIPC_NLA_LINK_TX] = { .type = NLA_U32 }
+};
+
static const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = {
[TIPC_NLA_NODE_UNSPEC] = { .type = NLA_UNSPEC },
[TIPC_NLA_NODE_ADDR] = { .type = NLA_U32 },
[TIPC_NLA_NODE_UP] = { .type = NLA_FLAG }
};
+static struct tipc_link *node_active_link(struct tipc_node *n, int sel)
+{
+ int bearer_id = n->active_links[sel & 1];
+
+ if (unlikely(bearer_id == INVALID_BEARER_ID))
+ return NULL;
+
+ return n->links[bearer_id].link;
+}
+
+int tipc_node_get_mtu(struct net *net, u32 addr, u32 sel)
+{
+ struct tipc_node *n;
+ int bearer_id;
+ unsigned int mtu = MAX_MSG_SIZE;
+
+ n = tipc_node_find(net, addr);
+ if (unlikely(!n))
+ return mtu;
+
+ bearer_id = n->active_links[sel & 1];
+ if (likely(bearer_id != INVALID_BEARER_ID))
+ mtu = n->links[bearer_id].mtu;
+ tipc_node_put(n);
+ return mtu;
+}
/*
* A trivial power-of-two bitmask technique is used for speed, since this
* operation is done for every incoming TIPC packet. The number of hash table
@@ -107,7 +230,7 @@ static void tipc_node_kref_release(struct kref *kref)
tipc_node_delete(node);
}
-void tipc_node_put(struct tipc_node *node)
+static void tipc_node_put(struct tipc_node *node)
{
kref_put(&node->kref, tipc_node_kref_release);
}
@@ -120,7 +243,7 @@ static void tipc_node_get(struct tipc_node *node)
/*
* tipc_node_find - locate specified node object, if it exists
*/
-struct tipc_node *tipc_node_find(struct net *net, u32 addr)
+static struct tipc_node *tipc_node_find(struct net *net, u32 addr)
{
struct tipc_net *tn = net_generic(net, tipc_net_id);
struct tipc_node *node;
@@ -141,66 +264,122 @@ struct tipc_node *tipc_node_find(struct net *net, u32 addr)
return NULL;
}
+static void tipc_node_read_lock(struct tipc_node *n)
+{
+ read_lock_bh(&n->lock);
+}
+
+static void tipc_node_read_unlock(struct tipc_node *n)
+{
+ read_unlock_bh(&n->lock);
+}
+
+static void tipc_node_write_lock(struct tipc_node *n)
+{
+ write_lock_bh(&n->lock);
+}
+
+static void tipc_node_write_unlock(struct tipc_node *n)
+{
+ struct net *net = n->net;
+ u32 addr = 0;
+ u32 flags = n->action_flags;
+ u32 link_id = 0;
+ struct list_head *publ_list;
+
+ if (likely(!flags)) {
+ write_unlock_bh(&n->lock);
+ return;
+ }
+
+ addr = n->addr;
+ link_id = n->link_id;
+ publ_list = &n->publ_list;
+
+ n->action_flags &= ~(TIPC_NOTIFY_NODE_DOWN | TIPC_NOTIFY_NODE_UP |
+ TIPC_NOTIFY_LINK_DOWN | TIPC_NOTIFY_LINK_UP);
+
+ write_unlock_bh(&n->lock);
+
+ if (flags & TIPC_NOTIFY_NODE_DOWN)
+ tipc_publ_notify(net, publ_list, addr);
+
+ if (flags & TIPC_NOTIFY_NODE_UP)
+ tipc_named_node_up(net, addr);
+
+ if (flags & TIPC_NOTIFY_LINK_UP)
+ tipc_nametbl_publish(net, TIPC_LINK_STATE, addr, addr,
+ TIPC_NODE_SCOPE, link_id, addr);
+
+ if (flags & TIPC_NOTIFY_LINK_DOWN)
+ tipc_nametbl_withdraw(net, TIPC_LINK_STATE, addr,
+ link_id, addr);
+}
+
struct tipc_node *tipc_node_create(struct net *net, u32 addr, u16 capabilities)
{
struct tipc_net *tn = net_generic(net, tipc_net_id);
- struct tipc_node *n_ptr, *temp_node;
+ struct tipc_node *n, *temp_node;
+ int i;
spin_lock_bh(&tn->node_list_lock);
- n_ptr = tipc_node_find(net, addr);
- if (n_ptr)
+ n = tipc_node_find(net, addr);
+ if (n)
goto exit;
- n_ptr = kzalloc(sizeof(*n_ptr), GFP_ATOMIC);
- if (!n_ptr) {
+ n = kzalloc(sizeof(*n), GFP_ATOMIC);
+ if (!n) {
pr_warn("Node creation failed, no memory\n");
goto exit;
}
- n_ptr->addr = addr;
- n_ptr->net = net;
- n_ptr->capabilities = capabilities;
- kref_init(&n_ptr->kref);
- spin_lock_init(&n_ptr->lock);
- INIT_HLIST_NODE(&n_ptr->hash);
- INIT_LIST_HEAD(&n_ptr->list);
- INIT_LIST_HEAD(&n_ptr->publ_list);
- INIT_LIST_HEAD(&n_ptr->conn_sks);
- skb_queue_head_init(&n_ptr->bc_entry.namedq);
- skb_queue_head_init(&n_ptr->bc_entry.inputq1);
- __skb_queue_head_init(&n_ptr->bc_entry.arrvq);
- skb_queue_head_init(&n_ptr->bc_entry.inputq2);
- hlist_add_head_rcu(&n_ptr->hash, &tn->node_htable[tipc_hashfn(addr)]);
+ n->addr = addr;
+ n->net = net;
+ n->capabilities = capabilities;
+ kref_init(&n->kref);
+ rwlock_init(&n->lock);
+ INIT_HLIST_NODE(&n->hash);
+ INIT_LIST_HEAD(&n->list);
+ INIT_LIST_HEAD(&n->publ_list);
+ INIT_LIST_HEAD(&n->conn_sks);
+ skb_queue_head_init(&n->bc_entry.namedq);
+ skb_queue_head_init(&n->bc_entry.inputq1);
+ __skb_queue_head_init(&n->bc_entry.arrvq);
+ skb_queue_head_init(&n->bc_entry.inputq2);
+ for (i = 0; i < MAX_BEARERS; i++)
+ spin_lock_init(&n->links[i].lock);
+ hlist_add_head_rcu(&n->hash, &tn->node_htable[tipc_hashfn(addr)]);
list_for_each_entry_rcu(temp_node, &tn->node_list, list) {
- if (n_ptr->addr < temp_node->addr)
+ if (n->addr < temp_node->addr)
break;
}
- list_add_tail_rcu(&n_ptr->list, &temp_node->list);
- n_ptr->state = SELF_DOWN_PEER_LEAVING;
- n_ptr->signature = INVALID_NODE_SIG;
- n_ptr->active_links[0] = INVALID_BEARER_ID;
- n_ptr->active_links[1] = INVALID_BEARER_ID;
- if (!tipc_link_bc_create(net, tipc_own_addr(net), n_ptr->addr,
- U16_MAX, tipc_bc_sndlink(net)->window,
- n_ptr->capabilities,
- &n_ptr->bc_entry.inputq1,
- &n_ptr->bc_entry.namedq,
+ list_add_tail_rcu(&n->list, &temp_node->list);
+ n->state = SELF_DOWN_PEER_LEAVING;
+ n->signature = INVALID_NODE_SIG;
+ n->active_links[0] = INVALID_BEARER_ID;
+ n->active_links[1] = INVALID_BEARER_ID;
+ if (!tipc_link_bc_create(net, tipc_own_addr(net), n->addr,
+ U16_MAX,
+ tipc_link_window(tipc_bc_sndlink(net)),
+ n->capabilities,
+ &n->bc_entry.inputq1,
+ &n->bc_entry.namedq,
tipc_bc_sndlink(net),
- &n_ptr->bc_entry.link)) {
+ &n->bc_entry.link)) {
pr_warn("Broadcast rcv link creation failed, no memory\n");
- kfree(n_ptr);
- n_ptr = NULL;
+ kfree(n);
+ n = NULL;
goto exit;
}
- tipc_node_get(n_ptr);
- setup_timer(&n_ptr->timer, tipc_node_timeout, (unsigned long)n_ptr);
- n_ptr->keepalive_intv = U32_MAX;
+ tipc_node_get(n);
+ setup_timer(&n->timer, tipc_node_timeout, (unsigned long)n);
+ n->keepalive_intv = U32_MAX;
exit:
spin_unlock_bh(&tn->node_list_lock);
- return n_ptr;
+ return n;
}
static void tipc_node_calculate_timer(struct tipc_node *n, struct tipc_link *l)
{
- unsigned long tol = l->tolerance;
+ unsigned long tol = tipc_link_tolerance(l);
unsigned long intv = ((tol / 4) > 500) ? 500 : tol / 4;
unsigned long keepalive_intv = msecs_to_jiffies(intv);
@@ -209,7 +388,7 @@ static void tipc_node_calculate_timer(struct tipc_node *n, struct tipc_link *l)
n->keepalive_intv = keepalive_intv;
/* Ensure link's abort limit corresponds to current interval */
- l->abort_limit = l->tolerance / jiffies_to_msecs(n->keepalive_intv);
+ tipc_link_set_abort_limit(l, tol / jiffies_to_msecs(n->keepalive_intv));
}
static void tipc_node_delete(struct tipc_node *node)
@@ -234,6 +413,42 @@ void tipc_node_stop(struct net *net)
spin_unlock_bh(&tn->node_list_lock);
}
+void tipc_node_subscribe(struct net *net, struct list_head *subscr, u32 addr)
+{
+ struct tipc_node *n;
+
+ if (in_own_node(net, addr))
+ return;
+
+ n = tipc_node_find(net, addr);
+ if (!n) {
+ pr_warn("Node subscribe rejected, unknown node 0x%x\n", addr);
+ return;
+ }
+ tipc_node_write_lock(n);
+ list_add_tail(subscr, &n->publ_list);
+ tipc_node_write_unlock(n);
+ tipc_node_put(n);
+}
+
+void tipc_node_unsubscribe(struct net *net, struct list_head *subscr, u32 addr)
+{
+ struct tipc_node *n;
+
+ if (in_own_node(net, addr))
+ return;
+
+ n = tipc_node_find(net, addr);
+ if (!n) {
+ pr_warn("Node unsubscribe rejected, unknown node 0x%x\n", addr);
+ return;
+ }
+ tipc_node_write_lock(n);
+ list_del_init(subscr);
+ tipc_node_write_unlock(n);
+ tipc_node_put(n);
+}
+
int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port)
{
struct tipc_node *node;
@@ -257,9 +472,9 @@ int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port)
conn->port = port;
conn->peer_port = peer_port;
- tipc_node_lock(node);
+ tipc_node_write_lock(node);
list_add_tail(&conn->list, &node->conn_sks);
- tipc_node_unlock(node);
+ tipc_node_write_unlock(node);
exit:
tipc_node_put(node);
return err;
@@ -277,14 +492,14 @@ void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port)
if (!node)
return;
- tipc_node_lock(node);
+ tipc_node_write_lock(node);
list_for_each_entry_safe(conn, safe, &node->conn_sks, list) {
if (port != conn->port)
continue;
list_del(&conn->list);
kfree(conn);
}
- tipc_node_unlock(node);
+ tipc_node_write_unlock(node);
tipc_node_put(node);
}
@@ -301,14 +516,16 @@ static void tipc_node_timeout(unsigned long data)
__skb_queue_head_init(&xmitq);
for (bearer_id = 0; bearer_id < MAX_BEARERS; bearer_id++) {
- tipc_node_lock(n);
+ tipc_node_read_lock(n);
le = &n->links[bearer_id];
+ spin_lock_bh(&le->lock);
if (le->link) {
/* Link tolerance may change asynchronously: */
tipc_node_calculate_timer(n, le->link);
rc = tipc_link_timeout(le->link, &xmitq);
}
- tipc_node_unlock(n);
+ spin_unlock_bh(&le->lock);
+ tipc_node_read_unlock(n);
tipc_bearer_xmit(n->net, bearer_id, &xmitq, &le->maddr);
if (rc & TIPC_LINK_DOWN_EVT)
tipc_node_link_down(n, bearer_id, false);
@@ -340,16 +557,16 @@ static void __tipc_node_link_up(struct tipc_node *n, int bearer_id,
n->working_links++;
n->action_flags |= TIPC_NOTIFY_LINK_UP;
- n->link_id = nl->peer_bearer_id << 16 | bearer_id;
+ n->link_id = tipc_link_id(nl);
/* Leave room for tunnel header when returning 'mtu' to users: */
- n->links[bearer_id].mtu = nl->mtu - INT_H_SIZE;
+ n->links[bearer_id].mtu = tipc_link_mtu(nl) - INT_H_SIZE;
tipc_bearer_add_dest(n->net, bearer_id, n->addr);
tipc_bcast_inc_bearer_dst_cnt(n->net, bearer_id);
pr_debug("Established link <%s> on network plane %c\n",
- nl->name, nl->net_plane);
+ tipc_link_name(nl), tipc_link_plane(nl));
/* First link? => give it both slots */
if (!ol) {
@@ -362,17 +579,17 @@ static void __tipc_node_link_up(struct tipc_node *n, int bearer_id,
}
/* Second link => redistribute slots */
- if (nl->priority > ol->priority) {
- pr_debug("Old link <%s> becomes standby\n", ol->name);
+ if (tipc_link_prio(nl) > tipc_link_prio(ol)) {
+ pr_debug("Old link <%s> becomes standby\n", tipc_link_name(ol));
*slot0 = bearer_id;
*slot1 = bearer_id;
tipc_link_set_active(nl, true);
tipc_link_set_active(ol, false);
- } else if (nl->priority == ol->priority) {
+ } else if (tipc_link_prio(nl) == tipc_link_prio(ol)) {
tipc_link_set_active(nl, true);
*slot1 = bearer_id;
} else {
- pr_debug("New link <%s> is standby\n", nl->name);
+ pr_debug("New link <%s> is standby\n", tipc_link_name(nl));
}
/* Prepare synchronization with first link */
@@ -387,9 +604,9 @@ static void __tipc_node_link_up(struct tipc_node *n, int bearer_id,
static void tipc_node_link_up(struct tipc_node *n, int bearer_id,
struct sk_buff_head *xmitq)
{
- tipc_node_lock(n);
+ tipc_node_write_lock(n);
__tipc_node_link_up(n, bearer_id, xmitq);
- tipc_node_unlock(n);
+ tipc_node_write_unlock(n);
}
/**
@@ -402,7 +619,7 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id,
struct tipc_link_entry *le = &n->links[*bearer_id];
int *slot0 = &n->active_links[0];
int *slot1 = &n->active_links[1];
- int i, highest = 0;
+ int i, highest = 0, prio;
struct tipc_link *l, *_l, *tnl;
l = n->links[*bearer_id].link;
@@ -411,12 +628,12 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id,
n->working_links--;
n->action_flags |= TIPC_NOTIFY_LINK_DOWN;
- n->link_id = l->peer_bearer_id << 16 | *bearer_id;
+ n->link_id = tipc_link_id(l);
tipc_bearer_remove_dest(n->net, *bearer_id, n->addr);
pr_debug("Lost link <%s> on network plane %c\n",
- l->name, l->net_plane);
+ tipc_link_name(l), tipc_link_plane(l));
/* Select new active link if any available */
*slot0 = INVALID_BEARER_ID;
@@ -427,10 +644,11 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id,
continue;
if (_l == l)
continue;
- if (_l->priority < highest)
+ prio = tipc_link_prio(_l);
+ if (prio < highest)
continue;
- if (_l->priority > highest) {
- highest = _l->priority;
+ if (prio > highest) {
+ highest = prio;
*slot0 = i;
*slot1 = i;
continue;
@@ -453,17 +671,17 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id,
tipc_bcast_dec_bearer_dst_cnt(n->net, *bearer_id);
/* There is still a working link => initiate failover */
- tnl = node_active_link(n, 0);
+ *bearer_id = n->active_links[0];
+ tnl = n->links[*bearer_id].link;
tipc_link_fsm_evt(tnl, LINK_SYNCH_END_EVT);
tipc_node_fsm_evt(n, NODE_SYNCH_END_EVT);
- n->sync_point = tnl->rcv_nxt + (U16_MAX / 2 - 1);
+ n->sync_point = tipc_link_rcv_nxt(tnl) + (U16_MAX / 2 - 1);
tipc_link_tnl_prepare(l, tnl, FAILOVER_MSG, xmitq);
tipc_link_reset(l);
tipc_link_fsm_evt(l, LINK_RESET_EVT);
tipc_link_fsm_evt(l, LINK_FAILOVER_BEGIN_EVT);
tipc_node_fsm_evt(n, NODE_FAILOVER_BEGIN_EVT);
- *maddr = &n->links[tnl->bearer_id].maddr;
- *bearer_id = tnl->bearer_id;
+ *maddr = &n->links[*bearer_id].maddr;
}
static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete)
@@ -478,7 +696,7 @@ static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete)
__skb_queue_head_init(&xmitq);
- tipc_node_lock(n);
+ tipc_node_write_lock(n);
if (!tipc_link_is_establishing(l)) {
__tipc_node_link_down(n, &bearer_id, &xmitq, &maddr);
if (delete) {
@@ -490,12 +708,12 @@ static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete)
/* Defuse pending tipc_node_link_up() */
tipc_link_fsm_evt(l, LINK_RESET_EVT);
}
- tipc_node_unlock(n);
+ tipc_node_write_unlock(n);
tipc_bearer_xmit(n->net, bearer_id, &xmitq, maddr);
tipc_sk_rcv(n->net, &le->inputq);
}
-bool tipc_node_is_up(struct tipc_node *n)
+static bool tipc_node_is_up(struct tipc_node *n)
{
return n->active_links[0] != INVALID_BEARER_ID;
}
@@ -523,7 +741,7 @@ void tipc_node_check_dest(struct net *net, u32 onode,
if (!n)
return;
- tipc_node_lock(n);
+ tipc_node_write_lock(n);
le = &n->links[b->identity];
@@ -626,7 +844,7 @@ void tipc_node_check_dest(struct net *net, u32 onode,
}
memcpy(&le->maddr, maddr, sizeof(*maddr));
exit:
- tipc_node_unlock(n);
+ tipc_node_write_unlock(n);
if (reset && !tipc_link_is_reset(l))
tipc_node_link_down(n, b->identity, false);
tipc_node_put(n);
@@ -834,24 +1052,6 @@ illegal_evt:
pr_err("Illegal node fsm evt %x in state %x\n", evt, state);
}
-bool tipc_node_filter_pkt(struct tipc_node *n, struct tipc_msg *hdr)
-{
- int state = n->state;
-
- if (likely(state == SELF_UP_PEER_UP))
- return true;
-
- if (state == SELF_LEAVING_PEER_DOWN)
- return false;
-
- if (state == SELF_DOWN_PEER_LEAVING) {
- if (msg_peer_node_is_up(hdr))
- return false;
- }
-
- return true;
-}
-
static void node_lost_contact(struct tipc_node *n,
struct sk_buff_head *inputq)
{
@@ -913,56 +1113,18 @@ int tipc_node_get_linkname(struct net *net, u32 bearer_id, u32 addr,
if (bearer_id >= MAX_BEARERS)
goto exit;
- tipc_node_lock(node);
+ tipc_node_read_lock(node);
link = node->links[bearer_id].link;
if (link) {
- strncpy(linkname, link->name, len);
+ strncpy(linkname, tipc_link_name(link), len);
err = 0;
}
exit:
- tipc_node_unlock(node);
+ tipc_node_read_unlock(node);
tipc_node_put(node);
return err;
}
-void tipc_node_unlock(struct tipc_node *node)
-{
- struct net *net = node->net;
- u32 addr = 0;
- u32 flags = node->action_flags;
- u32 link_id = 0;
- struct list_head *publ_list;
-
- if (likely(!flags)) {
- spin_unlock_bh(&node->lock);
- return;
- }
-
- addr = node->addr;
- link_id = node->link_id;
- publ_list = &node->publ_list;
-
- node->action_flags &= ~(TIPC_NOTIFY_NODE_DOWN | TIPC_NOTIFY_NODE_UP |
- TIPC_NOTIFY_LINK_DOWN | TIPC_NOTIFY_LINK_UP);
-
- spin_unlock_bh(&node->lock);
-
- if (flags & TIPC_NOTIFY_NODE_DOWN)
- tipc_publ_notify(net, publ_list, addr);
-
- if (flags & TIPC_NOTIFY_NODE_UP)
- tipc_named_node_up(net, addr);
-
- if (flags & TIPC_NOTIFY_LINK_UP)
- tipc_nametbl_publish(net, TIPC_LINK_STATE, addr, addr,
- TIPC_NODE_SCOPE, link_id, addr);
-
- if (flags & TIPC_NOTIFY_LINK_DOWN)
- tipc_nametbl_withdraw(net, TIPC_LINK_STATE, addr,
- link_id, addr);
-
-}
-
/* Caller should hold node lock for the passed node */
static int __tipc_nl_add_node(struct tipc_nl_msg *msg, struct tipc_node *node)
{
@@ -997,20 +1159,6 @@ msg_full:
return -EMSGSIZE;
}
-static struct tipc_link *tipc_node_select_link(struct tipc_node *n, int sel,
- int *bearer_id,
- struct tipc_media_addr **maddr)
-{
- int id = n->active_links[sel & 1];
-
- if (unlikely(id < 0))
- return NULL;
-
- *bearer_id = id;
- *maddr = &n->links[id].maddr;
- return n->links[id].link;
-}
-
/**
* tipc_node_xmit() is the general link level function for message sending
* @net: the applicable net namespace
@@ -1023,29 +1171,32 @@ static struct tipc_link *tipc_node_select_link(struct tipc_node *n, int sel,
int tipc_node_xmit(struct net *net, struct sk_buff_head *list,
u32 dnode, int selector)
{
- struct tipc_link *l = NULL;
+ struct tipc_link_entry *le = NULL;
struct tipc_node *n;
struct sk_buff_head xmitq;
- struct tipc_media_addr *maddr;
- int bearer_id;
+ int bearer_id = -1;
int rc = -EHOSTUNREACH;
__skb_queue_head_init(&xmitq);
n = tipc_node_find(net, dnode);
if (likely(n)) {
- tipc_node_lock(n);
- l = tipc_node_select_link(n, selector, &bearer_id, &maddr);
- if (likely(l))
- rc = tipc_link_xmit(l, list, &xmitq);
- tipc_node_unlock(n);
- if (unlikely(rc == -ENOBUFS))
+ tipc_node_read_lock(n);
+ bearer_id = n->active_links[selector & 1];
+ if (bearer_id >= 0) {
+ le = &n->links[bearer_id];
+ spin_lock_bh(&le->lock);
+ rc = tipc_link_xmit(le->link, list, &xmitq);
+ spin_unlock_bh(&le->lock);
+ }
+ tipc_node_read_unlock(n);
+ if (likely(!rc))
+ tipc_bearer_xmit(net, bearer_id, &xmitq, &le->maddr);
+ else if (rc == -ENOBUFS)
tipc_node_link_down(n, bearer_id, false);
tipc_node_put(n);
+ return rc;
}
- if (likely(!rc)) {
- tipc_bearer_xmit(net, bearer_id, &xmitq, maddr);
- return 0;
- }
+
if (likely(in_own_node(net, dnode))) {
tipc_sk_rcv(net, list);
return 0;
@@ -1075,6 +1226,30 @@ int tipc_node_xmit_skb(struct net *net, struct sk_buff *skb, u32 dnode,
return 0;
}
+void tipc_node_broadcast(struct net *net, struct sk_buff *skb)
+{
+ struct sk_buff *txskb;
+ struct tipc_node *n;
+ u32 dst;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(n, tipc_nodes(net), list) {
+ dst = n->addr;
+ if (in_own_node(net, dst))
+ continue;
+ if (!tipc_node_is_up(n))
+ continue;
+ txskb = pskb_copy(skb, GFP_ATOMIC);
+ if (!txskb)
+ break;
+ msg_set_destnode(buf_msg(txskb), dst);
+ tipc_node_xmit_skb(net, txskb, dst, 0);
+ }
+ rcu_read_unlock();
+
+ kfree_skb(skb);
+}
+
/**
* tipc_node_bc_rcv - process TIPC broadcast packet arriving from off-node
* @net: the applicable net namespace
@@ -1116,9 +1291,9 @@ static void tipc_node_bc_rcv(struct net *net, struct sk_buff *skb, int bearer_id
/* Broadcast ACKs are sent on a unicast link */
if (rc & TIPC_LINK_SND_BC_ACK) {
- tipc_node_lock(n);
+ tipc_node_read_lock(n);
tipc_link_build_ack_msg(le->link, &xmitq);
- tipc_node_unlock(n);
+ tipc_node_read_unlock(n);
}
if (!skb_queue_empty(&xmitq))
@@ -1151,30 +1326,30 @@ static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb,
u16 oseqno = msg_seqno(hdr);
u16 iseqno = msg_seqno(msg_get_wrapped(hdr));
u16 exp_pkts = msg_msgcnt(hdr);
- u16 rcv_nxt, syncpt, dlv_nxt;
+ u16 rcv_nxt, syncpt, dlv_nxt, inputq_len;
int state = n->state;
struct tipc_link *l, *tnl, *pl = NULL;
struct tipc_media_addr *maddr;
- int i, pb_id;
+ int pb_id;
l = n->links[bearer_id].link;
if (!l)
return false;
- rcv_nxt = l->rcv_nxt;
+ rcv_nxt = tipc_link_rcv_nxt(l);
if (likely((state == SELF_UP_PEER_UP) && (usr != TUNNEL_PROTOCOL)))
return true;
/* Find parallel link, if any */
- for (i = 0; i < MAX_BEARERS; i++) {
- if ((i != bearer_id) && n->links[i].link) {
- pl = n->links[i].link;
+ for (pb_id = 0; pb_id < MAX_BEARERS; pb_id++) {
+ if ((pb_id != bearer_id) && n->links[pb_id].link) {
+ pl = n->links[pb_id].link;
break;
}
}
- /* Update node accesibility if applicable */
+ /* Check and update node accesibility if applicable */
if (state == SELF_UP_PEER_COMING) {
if (!tipc_link_is_up(l))
return true;
@@ -1187,8 +1362,12 @@ static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb,
if (msg_peer_node_is_up(hdr))
return false;
tipc_node_fsm_evt(n, PEER_LOST_CONTACT_EVT);
+ return true;
}
+ if (state == SELF_LEAVING_PEER_DOWN)
+ return false;
+
/* Ignore duplicate packets */
if ((usr != LINK_PROTOCOL) && less(oseqno, rcv_nxt))
return true;
@@ -1197,9 +1376,9 @@ static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb,
if ((usr == TUNNEL_PROTOCOL) && (mtyp == FAILOVER_MSG)) {
syncpt = oseqno + exp_pkts - 1;
if (pl && tipc_link_is_up(pl)) {
- pb_id = pl->bearer_id;
__tipc_node_link_down(n, &pb_id, xmitq, &maddr);
- tipc_skb_queue_splice_tail_init(pl->inputq, l->inputq);
+ tipc_skb_queue_splice_tail_init(tipc_link_inputq(pl),
+ tipc_link_inputq(l));
}
/* If pkts arrive out of order, use lowest calculated syncpt */
if (less(syncpt, n->sync_point))
@@ -1232,19 +1411,18 @@ static bool tipc_node_check_state(struct tipc_node *n, struct sk_buff *skb,
tipc_link_fsm_evt(l, LINK_SYNCH_BEGIN_EVT);
tipc_node_fsm_evt(n, NODE_SYNCH_BEGIN_EVT);
}
- if (less(syncpt, n->sync_point))
- n->sync_point = syncpt;
}
/* Open tunnel link when parallel link reaches synch point */
- if ((n->state == NODE_SYNCHING) && tipc_link_is_synching(l)) {
+ if (n->state == NODE_SYNCHING) {
if (tipc_link_is_synching(l)) {
tnl = l;
} else {
tnl = pl;
pl = l;
}
- dlv_nxt = pl->rcv_nxt - mod(skb_queue_len(pl->inputq));
+ inputq_len = skb_queue_len(tipc_link_inputq(pl));
+ dlv_nxt = tipc_link_rcv_nxt(pl) - inputq_len;
if (more(dlv_nxt, n->sync_point)) {
tipc_link_fsm_evt(tnl, LINK_SYNCH_END_EVT);
tipc_node_fsm_evt(n, NODE_SYNCH_END_EVT);
@@ -1304,22 +1482,32 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b)
/* Ensure broadcast reception is in synch with peer's send state */
if (unlikely(usr == LINK_PROTOCOL))
tipc_bcast_sync_rcv(net, n->bc_entry.link, hdr);
- else if (unlikely(n->bc_entry.link->acked != bc_ack))
+ else if (unlikely(tipc_link_acked(n->bc_entry.link) != bc_ack))
tipc_bcast_ack_rcv(net, n->bc_entry.link, bc_ack);
- tipc_node_lock(n);
-
- /* Is reception permitted at the moment ? */
- if (!tipc_node_filter_pkt(n, hdr))
- goto unlock;
-
- /* Check and if necessary update node state */
- if (likely(tipc_node_check_state(n, skb, bearer_id, &xmitq))) {
- rc = tipc_link_rcv(le->link, skb, &xmitq);
- skb = NULL;
+ /* Receive packet directly if conditions permit */
+ tipc_node_read_lock(n);
+ if (likely((n->state == SELF_UP_PEER_UP) && (usr != TUNNEL_PROTOCOL))) {
+ spin_lock_bh(&le->lock);
+ if (le->link) {
+ rc = tipc_link_rcv(le->link, skb, &xmitq);
+ skb = NULL;
+ }
+ spin_unlock_bh(&le->lock);
+ }
+ tipc_node_read_unlock(n);
+
+ /* Check/update node state before receiving */
+ if (unlikely(skb)) {
+ tipc_node_write_lock(n);
+ if (tipc_node_check_state(n, skb, bearer_id, &xmitq)) {
+ if (le->link) {
+ rc = tipc_link_rcv(le->link, skb, &xmitq);
+ skb = NULL;
+ }
+ }
+ tipc_node_write_unlock(n);
}
-unlock:
- tipc_node_unlock(n);
if (unlikely(rc & TIPC_LINK_UP_EVT))
tipc_node_link_up(n, bearer_id, &xmitq);
@@ -1384,15 +1572,15 @@ int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb)
continue;
}
- tipc_node_lock(node);
+ tipc_node_read_lock(node);
err = __tipc_nl_add_node(&msg, node);
if (err) {
last_addr = node->addr;
- tipc_node_unlock(node);
+ tipc_node_read_unlock(node);
goto out;
}
- tipc_node_unlock(node);
+ tipc_node_read_unlock(node);
}
done = 1;
out:
@@ -1402,3 +1590,314 @@ out:
return skb->len;
}
+
+/* tipc_node_find_by_name - locate owner node of link by link's name
+ * @net: the applicable net namespace
+ * @name: pointer to link name string
+ * @bearer_id: pointer to index in 'node->links' array where the link was found.
+ *
+ * Returns pointer to node owning the link, or 0 if no matching link is found.
+ */
+static struct tipc_node *tipc_node_find_by_name(struct net *net,
+ const char *link_name,
+ unsigned int *bearer_id)
+{
+ struct tipc_net *tn = net_generic(net, tipc_net_id);
+ struct tipc_link *l;
+ struct tipc_node *n;
+ struct tipc_node *found_node = NULL;
+ int i;
+
+ *bearer_id = 0;
+ rcu_read_lock();
+ list_for_each_entry_rcu(n, &tn->node_list, list) {
+ tipc_node_read_lock(n);
+ for (i = 0; i < MAX_BEARERS; i++) {
+ l = n->links[i].link;
+ if (l && !strcmp(tipc_link_name(l), link_name)) {
+ *bearer_id = i;
+ found_node = n;
+ break;
+ }
+ }
+ tipc_node_read_unlock(n);
+ if (found_node)
+ break;
+ }
+ rcu_read_unlock();
+
+ return found_node;
+}
+
+int tipc_nl_node_set_link(struct sk_buff *skb, struct genl_info *info)
+{
+ int err;
+ int res = 0;
+ int bearer_id;
+ char *name;
+ struct tipc_link *link;
+ struct tipc_node *node;
+ struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1];
+ struct net *net = sock_net(skb->sk);
+
+ if (!info->attrs[TIPC_NLA_LINK])
+ return -EINVAL;
+
+ err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX,
+ info->attrs[TIPC_NLA_LINK],
+ tipc_nl_link_policy);
+ if (err)
+ return err;
+
+ if (!attrs[TIPC_NLA_LINK_NAME])
+ return -EINVAL;
+
+ name = nla_data(attrs[TIPC_NLA_LINK_NAME]);
+
+ if (strcmp(name, tipc_bclink_name) == 0)
+ return tipc_nl_bc_link_set(net, attrs);
+
+ node = tipc_node_find_by_name(net, name, &bearer_id);
+ if (!node)
+ return -EINVAL;
+
+ tipc_node_read_lock(node);
+
+ link = node->links[bearer_id].link;
+ if (!link) {
+ res = -EINVAL;
+ goto out;
+ }
+
+ if (attrs[TIPC_NLA_LINK_PROP]) {
+ struct nlattr *props[TIPC_NLA_PROP_MAX + 1];
+
+ err = tipc_nl_parse_link_prop(attrs[TIPC_NLA_LINK_PROP],
+ props);
+ if (err) {
+ res = err;
+ goto out;
+ }
+
+ if (props[TIPC_NLA_PROP_TOL]) {
+ u32 tol;
+
+ tol = nla_get_u32(props[TIPC_NLA_PROP_TOL]);
+ tipc_link_set_tolerance(link, tol);
+ }
+ if (props[TIPC_NLA_PROP_PRIO]) {
+ u32 prio;
+
+ prio = nla_get_u32(props[TIPC_NLA_PROP_PRIO]);
+ tipc_link_set_prio(link, prio);
+ }
+ if (props[TIPC_NLA_PROP_WIN]) {
+ u32 win;
+
+ win = nla_get_u32(props[TIPC_NLA_PROP_WIN]);
+ tipc_link_set_queue_limits(link, win);
+ }
+ }
+
+out:
+ tipc_node_read_unlock(node);
+
+ return res;
+}
+
+int tipc_nl_node_get_link(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ struct tipc_nl_msg msg;
+ char *name;
+ int err;
+
+ msg.portid = info->snd_portid;
+ msg.seq = info->snd_seq;
+
+ if (!info->attrs[TIPC_NLA_LINK_NAME])
+ return -EINVAL;
+ name = nla_data(info->attrs[TIPC_NLA_LINK_NAME]);
+
+ msg.skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg.skb)
+ return -ENOMEM;
+
+ if (strcmp(name, tipc_bclink_name) == 0) {
+ err = tipc_nl_add_bc_link(net, &msg);
+ if (err) {
+ nlmsg_free(msg.skb);
+ return err;
+ }
+ } else {
+ int bearer_id;
+ struct tipc_node *node;
+ struct tipc_link *link;
+
+ node = tipc_node_find_by_name(net, name, &bearer_id);
+ if (!node)
+ return -EINVAL;
+
+ tipc_node_read_lock(node);
+ link = node->links[bearer_id].link;
+ if (!link) {
+ tipc_node_read_unlock(node);
+ nlmsg_free(msg.skb);
+ return -EINVAL;
+ }
+
+ err = __tipc_nl_add_link(net, &msg, link, 0);
+ tipc_node_read_unlock(node);
+ if (err) {
+ nlmsg_free(msg.skb);
+ return err;
+ }
+ }
+
+ return genlmsg_reply(msg.skb, info);
+}
+
+int tipc_nl_node_reset_link_stats(struct sk_buff *skb, struct genl_info *info)
+{
+ int err;
+ char *link_name;
+ unsigned int bearer_id;
+ struct tipc_link *link;
+ struct tipc_node *node;
+ struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1];
+ struct net *net = sock_net(skb->sk);
+ struct tipc_link_entry *le;
+
+ if (!info->attrs[TIPC_NLA_LINK])
+ return -EINVAL;
+
+ err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX,
+ info->attrs[TIPC_NLA_LINK],
+ tipc_nl_link_policy);
+ if (err)
+ return err;
+
+ if (!attrs[TIPC_NLA_LINK_NAME])
+ return -EINVAL;
+
+ link_name = nla_data(attrs[TIPC_NLA_LINK_NAME]);
+
+ if (strcmp(link_name, tipc_bclink_name) == 0) {
+ err = tipc_bclink_reset_stats(net);
+ if (err)
+ return err;
+ return 0;
+ }
+
+ node = tipc_node_find_by_name(net, link_name, &bearer_id);
+ if (!node)
+ return -EINVAL;
+
+ le = &node->links[bearer_id];
+ tipc_node_read_lock(node);
+ spin_lock_bh(&le->lock);
+ link = node->links[bearer_id].link;
+ if (!link) {
+ spin_unlock_bh(&le->lock);
+ tipc_node_read_unlock(node);
+ return -EINVAL;
+ }
+ tipc_link_reset_stats(link);
+ spin_unlock_bh(&le->lock);
+ tipc_node_read_unlock(node);
+ return 0;
+}
+
+/* Caller should hold node lock */
+static int __tipc_nl_add_node_links(struct net *net, struct tipc_nl_msg *msg,
+ struct tipc_node *node, u32 *prev_link)
+{
+ u32 i;
+ int err;
+
+ for (i = *prev_link; i < MAX_BEARERS; i++) {
+ *prev_link = i;
+
+ if (!node->links[i].link)
+ continue;
+
+ err = __tipc_nl_add_link(net, msg,
+ node->links[i].link, NLM_F_MULTI);
+ if (err)
+ return err;
+ }
+ *prev_link = 0;
+
+ return 0;
+}
+
+int tipc_nl_node_dump_link(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct net *net = sock_net(skb->sk);
+ struct tipc_net *tn = net_generic(net, tipc_net_id);
+ struct tipc_node *node;
+ struct tipc_nl_msg msg;
+ u32 prev_node = cb->args[0];
+ u32 prev_link = cb->args[1];
+ int done = cb->args[2];
+ int err;
+
+ if (done)
+ return 0;
+
+ msg.skb = skb;
+ msg.portid = NETLINK_CB(cb->skb).portid;
+ msg.seq = cb->nlh->nlmsg_seq;
+
+ rcu_read_lock();
+ if (prev_node) {
+ node = tipc_node_find(net, prev_node);
+ if (!node) {
+ /* We never set seq or call nl_dump_check_consistent()
+ * this means that setting prev_seq here will cause the
+ * consistence check to fail in the netlink callback
+ * handler. Resulting in the last NLMSG_DONE message
+ * having the NLM_F_DUMP_INTR flag set.
+ */
+ cb->prev_seq = 1;
+ goto out;
+ }
+ tipc_node_put(node);
+
+ list_for_each_entry_continue_rcu(node, &tn->node_list,
+ list) {
+ tipc_node_read_lock(node);
+ err = __tipc_nl_add_node_links(net, &msg, node,
+ &prev_link);
+ tipc_node_read_unlock(node);
+ if (err)
+ goto out;
+
+ prev_node = node->addr;
+ }
+ } else {
+ err = tipc_nl_add_bc_link(net, &msg);
+ if (err)
+ goto out;
+
+ list_for_each_entry_rcu(node, &tn->node_list, list) {
+ tipc_node_read_lock(node);
+ err = __tipc_nl_add_node_links(net, &msg, node,
+ &prev_link);
+ tipc_node_read_unlock(node);
+ if (err)
+ goto out;
+
+ prev_node = node->addr;
+ }
+ }
+ done = 1;
+out:
+ rcu_read_unlock();
+
+ cb->args[0] = prev_node;
+ cb->args[1] = prev_link;
+ cb->args[2] = done;
+
+ return skb->len;
+}
diff --git a/net/tipc/node.h b/net/tipc/node.h
index 6734562d3c6e..f39d9d06e8bb 100644
--- a/net/tipc/node.h
+++ b/net/tipc/node.h
@@ -42,23 +42,6 @@
#include "bearer.h"
#include "msg.h"
-/* Out-of-range value for node signature */
-#define INVALID_NODE_SIG 0x10000
-
-#define INVALID_BEARER_ID -1
-
-/* Flags used to take different actions according to flag type
- * TIPC_NOTIFY_NODE_DOWN: notify node is down
- * TIPC_NOTIFY_NODE_UP: notify node is up
- * TIPC_DISTRIBUTE_NAME: publish or withdraw link state name type
- */
-enum {
- TIPC_NOTIFY_NODE_DOWN = (1 << 3),
- TIPC_NOTIFY_NODE_UP = (1 << 4),
- TIPC_NOTIFY_LINK_UP = (1 << 6),
- TIPC_NOTIFY_LINK_DOWN = (1 << 7)
-};
-
/* Optional capabilities supported by this code version
*/
enum {
@@ -66,72 +49,8 @@ enum {
};
#define TIPC_NODE_CAPABILITIES TIPC_BCAST_SYNCH
+#define INVALID_BEARER_ID -1
-struct tipc_link_entry {
- struct tipc_link *link;
- u32 mtu;
- struct sk_buff_head inputq;
- struct tipc_media_addr maddr;
-};
-
-struct tipc_bclink_entry {
- struct tipc_link *link;
- struct sk_buff_head inputq1;
- struct sk_buff_head arrvq;
- struct sk_buff_head inputq2;
- struct sk_buff_head namedq;
-};
-
-/**
- * struct tipc_node - TIPC node structure
- * @addr: network address of node
- * @ref: reference counter to node object
- * @lock: spinlock governing access to structure
- * @net: the applicable net namespace
- * @hash: links to adjacent nodes in unsorted hash chain
- * @inputq: pointer to input queue containing messages for msg event
- * @namedq: pointer to name table input queue with name table messages
- * @active_links: bearer ids of active links, used as index into links[] array
- * @links: array containing references to all links to node
- * @action_flags: bit mask of different types of node actions
- * @state: connectivity state vs peer node
- * @sync_point: sequence number where synch/failover is finished
- * @list: links to adjacent nodes in sorted list of cluster's nodes
- * @working_links: number of working links to node (both active and standby)
- * @link_cnt: number of links to node
- * @capabilities: bitmap, indicating peer node's functional capabilities
- * @signature: node instance identifier
- * @link_id: local and remote bearer ids of changing link, if any
- * @publ_list: list of publications
- * @rcu: rcu struct for tipc_node
- */
-struct tipc_node {
- u32 addr;
- struct kref kref;
- spinlock_t lock;
- struct net *net;
- struct hlist_node hash;
- int active_links[2];
- struct tipc_link_entry links[MAX_BEARERS];
- struct tipc_bclink_entry bc_entry;
- int action_flags;
- struct list_head list;
- int state;
- u16 sync_point;
- int link_cnt;
- u16 working_links;
- u16 capabilities;
- u32 signature;
- u32 link_id;
- struct list_head publ_list;
- struct list_head conn_sks;
- unsigned long keepalive_intv;
- struct timer_list timer;
- struct rcu_head rcu;
-};
-
-struct tipc_node *tipc_node_find(struct net *net, u32 addr);
-void tipc_node_put(struct tipc_node *node);
void tipc_node_stop(struct net *net);
void tipc_node_check_dest(struct net *net, u32 onode,
struct tipc_bearer *bearer,
@@ -139,50 +58,22 @@ void tipc_node_check_dest(struct net *net, u32 onode,
struct tipc_media_addr *maddr,
bool *respond, bool *dupl_addr);
void tipc_node_delete_links(struct net *net, int bearer_id);
-void tipc_node_attach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr);
-void tipc_node_detach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr);
-bool tipc_node_is_up(struct tipc_node *n);
int tipc_node_get_linkname(struct net *net, u32 bearer_id, u32 node,
char *linkname, size_t len);
-void tipc_node_unlock(struct tipc_node *node);
int tipc_node_xmit(struct net *net, struct sk_buff_head *list, u32 dnode,
int selector);
int tipc_node_xmit_skb(struct net *net, struct sk_buff *skb, u32 dest,
u32 selector);
+void tipc_node_subscribe(struct net *net, struct list_head *subscr, u32 addr);
+void tipc_node_unsubscribe(struct net *net, struct list_head *subscr, u32 addr);
+void tipc_node_broadcast(struct net *net, struct sk_buff *skb);
int tipc_node_add_conn(struct net *net, u32 dnode, u32 port, u32 peer_port);
void tipc_node_remove_conn(struct net *net, u32 dnode, u32 port);
+int tipc_node_get_mtu(struct net *net, u32 addr, u32 sel);
int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb);
-
-static inline void tipc_node_lock(struct tipc_node *node)
-{
- spin_lock_bh(&node->lock);
-}
-
-static inline struct tipc_link *node_active_link(struct tipc_node *n, int sel)
-{
- int bearer_id = n->active_links[sel & 1];
-
- if (unlikely(bearer_id == INVALID_BEARER_ID))
- return NULL;
-
- return n->links[bearer_id].link;
-}
-
-static inline unsigned int tipc_node_get_mtu(struct net *net, u32 addr, u32 sel)
-{
- struct tipc_node *n;
- int bearer_id;
- unsigned int mtu = MAX_MSG_SIZE;
-
- n = tipc_node_find(net, addr);
- if (unlikely(!n))
- return mtu;
-
- bearer_id = n->active_links[sel & 1];
- if (likely(bearer_id != INVALID_BEARER_ID))
- mtu = n->links[bearer_id].mtu;
- tipc_node_put(n);
- return mtu;
-}
+int tipc_nl_node_dump_link(struct sk_buff *skb, struct netlink_callback *cb);
+int tipc_nl_node_reset_link_stats(struct sk_buff *skb, struct genl_info *info);
+int tipc_nl_node_get_link(struct sk_buff *skb, struct genl_info *info);
+int tipc_nl_node_set_link(struct sk_buff *skb, struct genl_info *info);
#endif
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index 552dbaba9cf3..69c29050f14a 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -105,6 +105,7 @@ struct tipc_sock {
static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *skb);
static void tipc_data_ready(struct sock *sk);
static void tipc_write_space(struct sock *sk);
+static void tipc_sock_destruct(struct sock *sk);
static int tipc_release(struct socket *sock);
static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags);
static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p);
@@ -381,6 +382,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock,
sk->sk_rcvbuf = sysctl_tipc_rmem[1];
sk->sk_data_ready = tipc_data_ready;
sk->sk_write_space = tipc_write_space;
+ sk->sk_destruct = tipc_sock_destruct;
tsk->conn_timeout = CONN_TIMEOUT_DEFAULT;
tsk->sent_unacked = 0;
atomic_set(&tsk->dupl_rcvcnt, 0);
@@ -470,9 +472,6 @@ static int tipc_release(struct socket *sock)
tipc_node_remove_conn(net, dnode, tsk->portid);
}
- /* Discard any remaining (connection-based) messages in receive queue */
- __skb_queue_purge(&sk->sk_receive_queue);
-
/* Reject any messages that accumulated in backlog queue */
sock->state = SS_DISCONNECTING;
release_sock(sk);
@@ -1492,7 +1491,7 @@ static void tipc_write_space(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait, POLLOUT |
POLLWRNORM | POLLWRBAND);
rcu_read_unlock();
@@ -1509,12 +1508,17 @@ static void tipc_data_ready(struct sock *sk)
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait, POLLIN |
POLLRDNORM | POLLRDBAND);
rcu_read_unlock();
}
+static void tipc_sock_destruct(struct sock *sk)
+{
+ __skb_queue_purge(&sk->sk_receive_queue);
+}
+
/**
* filter_connect - Handle all incoming messages for a connection-based socket
* @tsk: TIPC socket
diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c
index ad2719ad4c1b..d63a911e7fe2 100644
--- a/net/tipc/udp_media.c
+++ b/net/tipc/udp_media.c
@@ -48,7 +48,6 @@
#include <linux/tipc_netlink.h>
#include "core.h"
#include "bearer.h"
-#include "msg.h"
/* IANA assigned UDP port */
#define UDP_PORT_DEFAULT 6118
@@ -158,8 +157,11 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
struct udp_media_addr *src = (struct udp_media_addr *)&b->addr.value;
struct rtable *rt;
- if (skb_headroom(skb) < UDP_MIN_HEADROOM)
- pskb_expand_head(skb, UDP_MIN_HEADROOM, 0, GFP_ATOMIC);
+ if (skb_headroom(skb) < UDP_MIN_HEADROOM) {
+ err = pskb_expand_head(skb, UDP_MIN_HEADROOM, 0, GFP_ATOMIC);
+ if (err)
+ goto tx_error;
+ }
skb_set_inner_protocol(skb, htons(ETH_P_TIPC));
ub = rcu_dereference_rtnl(b->media_ptr);
@@ -180,15 +182,9 @@ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
goto tx_error;
}
ttl = ip4_dst_hoplimit(&rt->dst);
- err = udp_tunnel_xmit_skb(rt, ub->ubsock->sk, skb,
- src->ipv4.s_addr,
- dst->ipv4.s_addr, 0, ttl, 0,
- src->udp_port, dst->udp_port,
- false, true);
- if (err < 0) {
- ip_rt_put(rt);
- goto tx_error;
- }
+ udp_tunnel_xmit_skb(rt, ub->ubsock->sk, skb, src->ipv4.s_addr,
+ dst->ipv4.s_addr, 0, ttl, 0, src->udp_port,
+ dst->udp_port, false, true);
#if IS_ENABLED(CONFIG_IPV6)
} else {
struct dst_entry *ndst;
@@ -221,10 +217,6 @@ static int tipc_udp_recv(struct sock *sk, struct sk_buff *skb)
{
struct udp_bearer *ub;
struct tipc_bearer *b;
- int usr = msg_user(buf_msg(skb));
-
- if ((usr == LINK_PROTOCOL) || (usr == NAME_DISTRIBUTOR))
- skb_linearize(skb);
ub = rcu_dereference_sk_user_data(sk);
if (!ub) {
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 955ec152cb71..c5bf5ef2bf89 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -326,6 +326,118 @@ found:
return s;
}
+/* Support code for asymmetrically connected dgram sockets
+ *
+ * If a datagram socket is connected to a socket not itself connected
+ * to the first socket (eg, /dev/log), clients may only enqueue more
+ * messages if the present receive queue of the server socket is not
+ * "too large". This means there's a second writeability condition
+ * poll and sendmsg need to test. The dgram recv code will do a wake
+ * up on the peer_wait wait queue of a socket upon reception of a
+ * datagram which needs to be propagated to sleeping would-be writers
+ * since these might not have sent anything so far. This can't be
+ * accomplished via poll_wait because the lifetime of the server
+ * socket might be less than that of its clients if these break their
+ * association with it or if the server socket is closed while clients
+ * are still connected to it and there's no way to inform "a polling
+ * implementation" that it should let go of a certain wait queue
+ *
+ * In order to propagate a wake up, a wait_queue_t of the client
+ * socket is enqueued on the peer_wait queue of the server socket
+ * whose wake function does a wake_up on the ordinary client socket
+ * wait queue. This connection is established whenever a write (or
+ * poll for write) hit the flow control condition and broken when the
+ * association to the server socket is dissolved or after a wake up
+ * was relayed.
+ */
+
+static int unix_dgram_peer_wake_relay(wait_queue_t *q, unsigned mode, int flags,
+ void *key)
+{
+ struct unix_sock *u;
+ wait_queue_head_t *u_sleep;
+
+ u = container_of(q, struct unix_sock, peer_wake);
+
+ __remove_wait_queue(&unix_sk(u->peer_wake.private)->peer_wait,
+ q);
+ u->peer_wake.private = NULL;
+
+ /* relaying can only happen while the wq still exists */
+ u_sleep = sk_sleep(&u->sk);
+ if (u_sleep)
+ wake_up_interruptible_poll(u_sleep, key);
+
+ return 0;
+}
+
+static int unix_dgram_peer_wake_connect(struct sock *sk, struct sock *other)
+{
+ struct unix_sock *u, *u_other;
+ int rc;
+
+ u = unix_sk(sk);
+ u_other = unix_sk(other);
+ rc = 0;
+ spin_lock(&u_other->peer_wait.lock);
+
+ if (!u->peer_wake.private) {
+ u->peer_wake.private = other;
+ __add_wait_queue(&u_other->peer_wait, &u->peer_wake);
+
+ rc = 1;
+ }
+
+ spin_unlock(&u_other->peer_wait.lock);
+ return rc;
+}
+
+static void unix_dgram_peer_wake_disconnect(struct sock *sk,
+ struct sock *other)
+{
+ struct unix_sock *u, *u_other;
+
+ u = unix_sk(sk);
+ u_other = unix_sk(other);
+ spin_lock(&u_other->peer_wait.lock);
+
+ if (u->peer_wake.private == other) {
+ __remove_wait_queue(&u_other->peer_wait, &u->peer_wake);
+ u->peer_wake.private = NULL;
+ }
+
+ spin_unlock(&u_other->peer_wait.lock);
+}
+
+static void unix_dgram_peer_wake_disconnect_wakeup(struct sock *sk,
+ struct sock *other)
+{
+ unix_dgram_peer_wake_disconnect(sk, other);
+ wake_up_interruptible_poll(sk_sleep(sk),
+ POLLOUT |
+ POLLWRNORM |
+ POLLWRBAND);
+}
+
+/* preconditions:
+ * - unix_peer(sk) == other
+ * - association is stable
+ */
+static int unix_dgram_peer_wake_me(struct sock *sk, struct sock *other)
+{
+ int connected;
+
+ connected = unix_dgram_peer_wake_connect(sk, other);
+
+ if (unix_recvq_full(other))
+ return 1;
+
+ if (connected)
+ unix_dgram_peer_wake_disconnect(sk, other);
+
+ return 0;
+}
+
static int unix_writable(const struct sock *sk)
{
return sk->sk_state != TCP_LISTEN &&
@@ -339,7 +451,7 @@ static void unix_write_space(struct sock *sk)
rcu_read_lock();
if (unix_writable(sk)) {
wq = rcu_dereference(sk->sk_wq);
- if (wq_has_sleeper(wq))
+ if (skwq_has_sleeper(wq))
wake_up_interruptible_sync_poll(&wq->wait,
POLLOUT | POLLWRNORM | POLLWRBAND);
sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
@@ -431,6 +543,8 @@ static void unix_release_sock(struct sock *sk, int embrion)
skpair->sk_state_change(skpair);
sk_wake_async(skpair, SOCK_WAKE_WAITD, POLL_HUP);
}
+
+ unix_dgram_peer_wake_disconnect(sk, skpair);
sock_put(skpair); /* It may now die */
unix_peer(sk) = NULL;
}
@@ -666,6 +780,7 @@ static struct sock *unix_create1(struct net *net, struct socket *sock, int kern)
INIT_LIST_HEAD(&u->link);
mutex_init(&u->readlock); /* single task reading lock */
init_waitqueue_head(&u->peer_wait);
+ init_waitqueue_func_entry(&u->peer_wake, unix_dgram_peer_wake_relay);
unix_insert_socket(unix_sockets_unbound(sk), sk);
out:
if (sk == NULL)
@@ -838,32 +953,20 @@ fail:
return NULL;
}
-static int unix_mknod(const char *sun_path, umode_t mode, struct path *res)
+static int unix_mknod(struct dentry *dentry, struct path *path, umode_t mode,
+ struct path *res)
{
- struct dentry *dentry;
- struct path path;
- int err = 0;
- /*
- * Get the parent directory, calculate the hash for last
- * component.
- */
- dentry = kern_path_create(AT_FDCWD, sun_path, &path, 0);
- err = PTR_ERR(dentry);
- if (IS_ERR(dentry))
- return err;
+ int err;
- /*
- * All right, let's create it.
- */
- err = security_path_mknod(&path, dentry, mode, 0);
+ err = security_path_mknod(path, dentry, mode, 0);
if (!err) {
- err = vfs_mknod(d_inode(path.dentry), dentry, mode, 0);
+ err = vfs_mknod(d_inode(path->dentry), dentry, mode, 0);
if (!err) {
- res->mnt = mntget(path.mnt);
+ res->mnt = mntget(path->mnt);
res->dentry = dget(dentry);
}
}
- done_path_create(&path, dentry);
+
return err;
}
@@ -874,10 +977,12 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
struct unix_sock *u = unix_sk(sk);
struct sockaddr_un *sunaddr = (struct sockaddr_un *)uaddr;
char *sun_path = sunaddr->sun_path;
- int err;
+ int err, name_err;
unsigned int hash;
struct unix_address *addr;
struct hlist_head *list;
+ struct path path;
+ struct dentry *dentry;
err = -EINVAL;
if (sunaddr->sun_family != AF_UNIX)
@@ -893,14 +998,34 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
goto out;
addr_len = err;
+ name_err = 0;
+ dentry = NULL;
+ if (sun_path[0]) {
+ /* Get the parent directory, calculate the hash for last
+ * component.
+ */
+ dentry = kern_path_create(AT_FDCWD, sun_path, &path, 0);
+
+ if (IS_ERR(dentry)) {
+ /* delay report until after 'already bound' check */
+ name_err = PTR_ERR(dentry);
+ dentry = NULL;
+ }
+ }
+
err = mutex_lock_interruptible(&u->readlock);
if (err)
- goto out;
+ goto out_path;
err = -EINVAL;
if (u->addr)
goto out_up;
+ if (name_err) {
+ err = name_err == -EEXIST ? -EADDRINUSE : name_err;
+ goto out_up;
+ }
+
err = -ENOMEM;
addr = kmalloc(sizeof(*addr)+addr_len, GFP_KERNEL);
if (!addr)
@@ -911,11 +1036,11 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
addr->hash = hash ^ sk->sk_type;
atomic_set(&addr->refcnt, 1);
- if (sun_path[0]) {
- struct path path;
+ if (dentry) {
+ struct path u_path;
umode_t mode = S_IFSOCK |
(SOCK_INODE(sock)->i_mode & ~current_umask());
- err = unix_mknod(sun_path, mode, &path);
+ err = unix_mknod(dentry, &path, mode, &u_path);
if (err) {
if (err == -EEXIST)
err = -EADDRINUSE;
@@ -923,9 +1048,9 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
goto out_up;
}
addr->hash = UNIX_HASH_SIZE;
- hash = d_backing_inode(path.dentry)->i_ino & (UNIX_HASH_SIZE-1);
+ hash = d_backing_inode(dentry)->i_ino & (UNIX_HASH_SIZE - 1);
spin_lock(&unix_table_lock);
- u->path = path;
+ u->path = u_path;
list = &unix_socket_table[hash];
} else {
spin_lock(&unix_table_lock);
@@ -948,6 +1073,10 @@ out_unlock:
spin_unlock(&unix_table_lock);
out_up:
mutex_unlock(&u->readlock);
+out_path:
+ if (dentry)
+ done_path_create(&path, dentry);
+
out:
return err;
}
@@ -1033,6 +1162,8 @@ restart:
if (unix_peer(sk)) {
struct sock *old_peer = unix_peer(sk);
unix_peer(sk) = other;
+ unix_dgram_peer_wake_disconnect_wakeup(sk, old_peer);
+
unix_state_double_unlock(sk, other);
if (other != old_peer)
@@ -1382,6 +1513,21 @@ static void unix_destruct_scm(struct sk_buff *skb)
sock_wfree(skb);
}
+/*
+ * The "user->unix_inflight" variable is protected by the garbage
+ * collection lock, and we just read it locklessly here. If you go
+ * over the limit, there might be a tiny race in actually noticing
+ * it across threads. Tough.
+ */
+static inline bool too_many_unix_fds(struct task_struct *p)
+{
+ struct user_struct *user = current_user();
+
+ if (unlikely(user->unix_inflight > task_rlimit(p, RLIMIT_NOFILE)))
+ return !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN);
+ return false;
+}
+
#define MAX_RECURSION_LEVEL 4
static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
@@ -1390,6 +1536,9 @@ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
unsigned char max_level = 0;
int unix_sock_count = 0;
+ if (too_many_unix_fds(current))
+ return -ETOOMANYREFS;
+
for (i = scm->fp->count - 1; i >= 0; i--) {
struct sock *sk = unix_get_socket(scm->fp->fp[i]);
@@ -1411,10 +1560,8 @@ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
if (!UNIXCB(skb).fp)
return -ENOMEM;
- if (unix_sock_count) {
- for (i = scm->fp->count - 1; i >= 0; i--)
- unix_inflight(scm->fp->fp[i]);
- }
+ for (i = scm->fp->count - 1; i >= 0; i--)
+ unix_inflight(scm->fp->fp[i]);
return max_level;
}
@@ -1434,6 +1581,14 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool sen
return err;
}
+static bool unix_passcred_enabled(const struct socket *sock,
+ const struct sock *other)
+{
+ return test_bit(SOCK_PASSCRED, &sock->flags) ||
+ !other->sk_socket ||
+ test_bit(SOCK_PASSCRED, &other->sk_socket->flags);
+}
+
/*
* Some apps rely on write() giving SCM_CREDENTIALS
* We include credentials if source or destination socket
@@ -1444,14 +1599,41 @@ static void maybe_add_creds(struct sk_buff *skb, const struct socket *sock,
{
if (UNIXCB(skb).pid)
return;
- if (test_bit(SOCK_PASSCRED, &sock->flags) ||
- !other->sk_socket ||
- test_bit(SOCK_PASSCRED, &other->sk_socket->flags)) {
+ if (unix_passcred_enabled(sock, other)) {
UNIXCB(skb).pid = get_pid(task_tgid(current));
current_uid_gid(&UNIXCB(skb).uid, &UNIXCB(skb).gid);
}
}
+static int maybe_init_creds(struct scm_cookie *scm,
+ struct socket *socket,
+ const struct sock *other)
+{
+ int err;
+ struct msghdr msg = { .msg_controllen = 0 };
+
+ err = scm_send(socket, &msg, scm, false);
+ if (err)
+ return err;
+
+ if (unix_passcred_enabled(socket, other)) {
+ scm->pid = get_pid(task_tgid(current));
+ current_uid_gid(&scm->creds.uid, &scm->creds.gid);
+ }
+ return err;
+}
+
+static bool unix_skb_scm_eq(struct sk_buff *skb,
+ struct scm_cookie *scm)
+{
+ const struct unix_skb_parms *u = &UNIXCB(skb);
+
+ return u->pid == scm->pid &&
+ uid_eq(u->uid, scm->creds.uid) &&
+ gid_eq(u->gid, scm->creds.gid) &&
+ unix_secdata_eq(scm, skb);
+}
+
/*
* Send AF_UNIX data.
*/
@@ -1472,6 +1654,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg,
struct scm_cookie scm;
int max_level;
int data_len = 0;
+ int sk_locked;
wait_for_unix_gc();
err = scm_send(sock, msg, &scm, false);
@@ -1550,12 +1733,14 @@ restart:
goto out_free;
}
+ sk_locked = 0;
unix_state_lock(other);
+restart_locked:
err = -EPERM;
if (!unix_may_send(sk, other))
goto out_unlock;
- if (sock_flag(other, SOCK_DEAD)) {
+ if (unlikely(sock_flag(other, SOCK_DEAD))) {
/*
* Check with 1003.1g - what should
* datagram error
@@ -1563,10 +1748,14 @@ restart:
unix_state_unlock(other);
sock_put(other);
+ if (!sk_locked)
+ unix_state_lock(sk);
+
err = 0;
- unix_state_lock(sk);
if (unix_peer(sk) == other) {
unix_peer(sk) = NULL;
+ unix_dgram_peer_wake_disconnect_wakeup(sk, other);
+
unix_state_unlock(sk);
unix_dgram_disconnected(sk, other);
@@ -1592,21 +1781,38 @@ restart:
goto out_unlock;
}
- if (unix_peer(other) != sk && unix_recvq_full(other)) {
- if (!timeo) {
- err = -EAGAIN;
- goto out_unlock;
+ if (unlikely(unix_peer(other) != sk && unix_recvq_full(other))) {
+ if (timeo) {
+ timeo = unix_wait_for_peer(other, timeo);
+
+ err = sock_intr_errno(timeo);
+ if (signal_pending(current))
+ goto out_free;
+
+ goto restart;
}
- timeo = unix_wait_for_peer(other, timeo);
+ if (!sk_locked) {
+ unix_state_unlock(other);
+ unix_state_double_lock(sk, other);
+ }
- err = sock_intr_errno(timeo);
- if (signal_pending(current))
- goto out_free;
+ if (unix_peer(sk) != other ||
+ unix_dgram_peer_wake_me(sk, other)) {
+ err = -EAGAIN;
+ sk_locked = 1;
+ goto out_unlock;
+ }
- goto restart;
+ if (!sk_locked) {
+ sk_locked = 1;
+ goto restart_locked;
+ }
}
+ if (unlikely(sk_locked))
+ unix_state_unlock(sk);
+
if (sock_flag(other, SOCK_RCVTSTAMP))
__net_timestamp(skb);
maybe_add_creds(skb, sock, other);
@@ -1620,6 +1826,8 @@ restart:
return len;
out_unlock:
+ if (sk_locked)
+ unix_state_unlock(sk);
unix_state_unlock(other);
out_free:
kfree_skb(skb);
@@ -1741,8 +1949,10 @@ out_err:
static ssize_t unix_stream_sendpage(struct socket *socket, struct page *page,
int offset, size_t size, int flags)
{
- int err = 0;
- bool send_sigpipe = true;
+ int err;
+ bool send_sigpipe = false;
+ bool init_scm = true;
+ struct scm_cookie scm;
struct sock *other, *sk = socket->sk;
struct sk_buff *skb, *newskb = NULL, *tail = NULL;
@@ -1760,7 +1970,7 @@ alloc_skb:
newskb = sock_alloc_send_pskb(sk, 0, 0, flags & MSG_DONTWAIT,
&err, 0);
if (!newskb)
- return err;
+ goto err;
}
/* we must acquire readlock as we modify already present
@@ -1769,12 +1979,12 @@ alloc_skb:
err = mutex_lock_interruptible(&unix_sk(other)->readlock);
if (err) {
err = flags & MSG_DONTWAIT ? -EAGAIN : -ERESTARTSYS;
- send_sigpipe = false;
goto err;
}
if (sk->sk_shutdown & SEND_SHUTDOWN) {
err = -EPIPE;
+ send_sigpipe = true;
goto err_unlock;
}
@@ -1783,17 +1993,27 @@ alloc_skb:
if (sock_flag(other, SOCK_DEAD) ||
other->sk_shutdown & RCV_SHUTDOWN) {
err = -EPIPE;
+ send_sigpipe = true;
goto err_state_unlock;
}
+ if (init_scm) {
+ err = maybe_init_creds(&scm, socket, other);
+ if (err)
+ goto err_state_unlock;
+ init_scm = false;
+ }
+
skb = skb_peek_tail(&other->sk_receive_queue);
if (tail && tail == skb) {
skb = newskb;
- } else if (!skb) {
- if (newskb)
+ } else if (!skb || !unix_skb_scm_eq(skb, &scm)) {
+ if (newskb) {
skb = newskb;
- else
+ } else {
+ tail = skb;
goto alloc_skb;
+ }
} else if (newskb) {
/* this is fast path, we don't necessarily need to
* call to kfree_skb even though with newskb == NULL
@@ -1814,6 +2034,9 @@ alloc_skb:
atomic_add(size, &sk->sk_wmem_alloc);
if (newskb) {
+ err = unix_scm_to_skb(&scm, skb, false);
+ if (err)
+ goto err_state_unlock;
spin_lock(&other->sk_receive_queue.lock);
__skb_queue_tail(&other->sk_receive_queue, newskb);
spin_unlock(&other->sk_receive_queue.lock);
@@ -1823,7 +2046,7 @@ alloc_skb:
mutex_unlock(&unix_sk(other)->readlock);
other->sk_data_ready(other);
-
+ scm_destroy(&scm);
return size;
err_state_unlock:
@@ -1834,6 +2057,8 @@ err:
kfree_skb(newskb);
if (send_sigpipe && !(flags & MSG_NOSIGNAL))
send_sig(SIGPIPE, current, 0);
+ if (!init_scm)
+ scm_destroy(&scm);
return err;
}
@@ -1883,8 +2108,8 @@ static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg,
struct scm_cookie scm;
struct sock *sk = sock->sk;
struct unix_sock *u = unix_sk(sk);
- int noblock = flags & MSG_DONTWAIT;
- struct sk_buff *skb;
+ struct sk_buff *skb, *last;
+ long timeo;
int err;
int peeked, skip;
@@ -1892,30 +2117,38 @@ static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg,
if (flags&MSG_OOB)
goto out;
- err = mutex_lock_interruptible(&u->readlock);
- if (unlikely(err)) {
- /* recvmsg() in non blocking mode is supposed to return -EAGAIN
- * sk_rcvtimeo is not honored by mutex_lock_interruptible()
- */
- err = noblock ? -EAGAIN : -ERESTARTSYS;
- goto out;
- }
+ timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
- skip = sk_peek_offset(sk, flags);
+ do {
+ mutex_lock(&u->readlock);
- skb = __skb_recv_datagram(sk, flags, &peeked, &skip, &err);
- if (!skb) {
+ skip = sk_peek_offset(sk, flags);
+ skb = __skb_try_recv_datagram(sk, flags, &peeked, &skip, &err,
+ &last);
+ if (skb)
+ break;
+
+ mutex_unlock(&u->readlock);
+
+ if (err != -EAGAIN)
+ break;
+ } while (timeo &&
+ !__skb_wait_for_more_packets(sk, &err, &timeo, last));
+
+ if (!skb) { /* implies readlock unlocked */
unix_state_lock(sk);
/* Signal EOF on disconnected non-blocking SEQPACKET socket. */
if (sk->sk_type == SOCK_SEQPACKET && err == -EAGAIN &&
(sk->sk_shutdown & RCV_SHUTDOWN))
err = 0;
unix_state_unlock(sk);
- goto out_unlock;
+ goto out;
}
- wake_up_interruptible_sync_poll(&u->peer_wait,
- POLLOUT | POLLWRNORM | POLLWRBAND);
+ if (wq_has_sleeper(&u->peer_wait))
+ wake_up_interruptible_sync_poll(&u->peer_wait,
+ POLLOUT | POLLWRNORM |
+ POLLWRBAND);
if (msg->msg_name)
unix_copy_addr(msg, skb->sk);
@@ -1967,7 +2200,6 @@ static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg,
out_free:
skb_free_datagram(sk, skb);
-out_unlock:
mutex_unlock(&u->readlock);
out:
return err;
@@ -1996,7 +2228,7 @@ static long unix_stream_data_wait(struct sock *sk, long timeo,
!timeo)
break;
- set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+ sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);
unix_state_unlock(sk);
timeo = freezable_schedule_timeout(timeo);
unix_state_lock(sk);
@@ -2004,7 +2236,7 @@ static long unix_stream_data_wait(struct sock *sk, long timeo,
if (sock_flag(sk, SOCK_DEAD))
break;
- clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
+ sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk);
}
finish_wait(sk_sleep(sk), &wait);
@@ -2061,14 +2293,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state)
/* Lock the socket to prevent queue disordering
* while sleeps in memcpy_tomsg
*/
- err = mutex_lock_interruptible(&u->readlock);
- if (unlikely(err)) {
- /* recvmsg() in non blocking mode is supposed to return -EAGAIN
- * sk_rcvtimeo is not honored by mutex_lock_interruptible()
- */
- err = noblock ? -EAGAIN : -ERESTARTSYS;
- goto out;
- }
+ mutex_lock(&u->readlock);
if (flags & MSG_PEEK)
skip = sk_peek_offset(sk, flags);
@@ -2112,12 +2337,12 @@ again:
timeo = unix_stream_data_wait(sk, timeo, last,
last_len);
- if (signal_pending(current) ||
- mutex_lock_interruptible(&u->readlock)) {
+ if (signal_pending(current)) {
err = sock_intr_errno(timeo);
goto out;
}
+ mutex_lock(&u->readlock);
continue;
unlock:
unix_state_unlock(sk);
@@ -2137,10 +2362,7 @@ unlock:
if (check_creds) {
/* Never glue messages from different writers */
- if ((UNIXCB(skb).pid != scm.pid) ||
- !uid_eq(UNIXCB(skb).uid, scm.creds.uid) ||
- !gid_eq(UNIXCB(skb).gid, scm.creds.gid) ||
- !unix_secdata_eq(&scm, skb))
+ if (!unix_skb_scm_eq(skb, &scm))
break;
} else if (test_bit(SOCK_PASSCRED, &sock->flags)) {
/* Copy credentials */
@@ -2476,20 +2698,22 @@ static unsigned int unix_dgram_poll(struct file *file, struct socket *sock,
return mask;
writable = unix_writable(sk);
- other = unix_peer_get(sk);
- if (other) {
- if (unix_peer(other) != sk) {
- sock_poll_wait(file, &unix_sk(other)->peer_wait, wait);
- if (unix_recvq_full(other))
- writable = 0;
- }
- sock_put(other);
+ if (writable) {
+ unix_state_lock(sk);
+
+ other = unix_peer(sk);
+ if (other && unix_peer(other) != sk &&
+ unix_recvq_full(other) &&
+ unix_dgram_peer_wake_me(sk, other))
+ writable = 0;
+
+ unix_state_unlock(sk);
}
if (writable)
mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
else
- set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+ sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);
return mask;
}
diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index a73a226f2d33..8fcdc2283af5 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -120,11 +120,11 @@ void unix_inflight(struct file *fp)
{
struct sock *s = unix_get_socket(fp);
+ spin_lock(&unix_gc_lock);
+
if (s) {
struct unix_sock *u = unix_sk(s);
- spin_lock(&unix_gc_lock);
-
if (atomic_long_inc_return(&u->inflight) == 1) {
BUG_ON(!list_empty(&u->link));
list_add_tail(&u->link, &gc_inflight_list);
@@ -132,25 +132,28 @@ void unix_inflight(struct file *fp)
BUG_ON(list_empty(&u->link));
}
unix_tot_inflight++;
- spin_unlock(&unix_gc_lock);
}
+ fp->f_cred->user->unix_inflight++;
+ spin_unlock(&unix_gc_lock);
}
void unix_notinflight(struct file *fp)
{
struct sock *s = unix_get_socket(fp);
+ spin_lock(&unix_gc_lock);
+
if (s) {
struct unix_sock *u = unix_sk(s);
- spin_lock(&unix_gc_lock);
BUG_ON(list_empty(&u->link));
if (atomic_long_dec_and_test(&u->inflight))
list_del_init(&u->link);
unix_tot_inflight--;
- spin_unlock(&unix_gc_lock);
}
+ fp->f_cred->user->unix_inflight--;
+ spin_unlock(&unix_gc_lock);
}
static void scan_inflight(struct sock *x, void (*func)(struct unix_sock *),
diff --git a/net/vmw_vsock/vmci_transport.h b/net/vmw_vsock/vmci_transport.h
index 2ad46f39649f..1820e74a5752 100644
--- a/net/vmw_vsock/vmci_transport.h
+++ b/net/vmw_vsock/vmci_transport.h
@@ -121,7 +121,7 @@ struct vmci_transport {
u64 queue_pair_max_size;
u32 detach_sub_id;
union vmci_transport_notify notify;
- struct vmci_transport_notify_ops *notify_ops;
+ const struct vmci_transport_notify_ops *notify_ops;
struct list_head elem;
struct sock *sk;
spinlock_t lock; /* protects sk. */
diff --git a/net/vmw_vsock/vmci_transport_notify.c b/net/vmw_vsock/vmci_transport_notify.c
index 9b7f207f2bee..fd8cf0214d51 100644
--- a/net/vmw_vsock/vmci_transport_notify.c
+++ b/net/vmw_vsock/vmci_transport_notify.c
@@ -661,7 +661,7 @@ static void vmci_transport_notify_pkt_process_negotiate(struct sock *sk)
}
/* Socket control packet based operations. */
-struct vmci_transport_notify_ops vmci_transport_notify_pkt_ops = {
+const struct vmci_transport_notify_ops vmci_transport_notify_pkt_ops = {
vmci_transport_notify_pkt_socket_init,
vmci_transport_notify_pkt_socket_destruct,
vmci_transport_notify_pkt_poll_in,
diff --git a/net/vmw_vsock/vmci_transport_notify.h b/net/vmw_vsock/vmci_transport_notify.h
index 7df793249b6c..3c464d394a8f 100644
--- a/net/vmw_vsock/vmci_transport_notify.h
+++ b/net/vmw_vsock/vmci_transport_notify.h
@@ -77,7 +77,8 @@ struct vmci_transport_notify_ops {
void (*process_negotiate) (struct sock *sk);
};
-extern struct vmci_transport_notify_ops vmci_transport_notify_pkt_ops;
-extern struct vmci_transport_notify_ops vmci_transport_notify_pkt_q_state_ops;
+extern const struct vmci_transport_notify_ops vmci_transport_notify_pkt_ops;
+extern const
+struct vmci_transport_notify_ops vmci_transport_notify_pkt_q_state_ops;
#endif /* __VMCI_TRANSPORT_NOTIFY_H__ */
diff --git a/net/vmw_vsock/vmci_transport_notify_qstate.c b/net/vmw_vsock/vmci_transport_notify_qstate.c
index dc9c7929a2f9..21e591dafb03 100644
--- a/net/vmw_vsock/vmci_transport_notify_qstate.c
+++ b/net/vmw_vsock/vmci_transport_notify_qstate.c
@@ -419,7 +419,7 @@ vmci_transport_notify_pkt_send_pre_enqueue(
}
/* Socket always on control packet based operations. */
-struct vmci_transport_notify_ops vmci_transport_notify_pkt_q_state_ops = {
+const struct vmci_transport_notify_ops vmci_transport_notify_pkt_q_state_ops = {
vmci_transport_notify_pkt_socket_init,
vmci_transport_notify_pkt_socket_destruct,
vmci_transport_notify_pkt_poll_in,
diff --git a/net/wireless/core.h b/net/wireless/core.h
index a618b4b86fa4..022ccad06cbe 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -416,13 +416,6 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
void cfg80211_process_wdev_events(struct wireless_dev *wdev);
-int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev,
- enum nl80211_iftype iftype,
- struct ieee80211_channel *chan,
- enum cfg80211_chan_mode chanmode,
- u8 radar_detect);
-
/**
* cfg80211_chandef_dfs_usable - checks if chandef is DFS usable
* @wiphy: the wiphy to validate against
diff --git a/net/wireless/lib80211_crypt_ccmp.c b/net/wireless/lib80211_crypt_ccmp.c
index dc0e59e53dbf..6beab0cfcb99 100644
--- a/net/wireless/lib80211_crypt_ccmp.c
+++ b/net/wireless/lib80211_crypt_ccmp.c
@@ -311,8 +311,8 @@ static int lib80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
}
keyidx >>= 6;
if (key->key_idx != keyidx) {
- printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame "
- "keyidx=%d priv=%p\n", key->key_idx, keyidx, priv);
+ net_dbg_ratelimited("CCMP: RX tkey->key_idx=%d frame keyidx=%d\n",
+ key->key_idx, keyidx);
return -6;
}
if (!key->key_set) {
diff --git a/net/wireless/lib80211_crypt_tkip.c b/net/wireless/lib80211_crypt_tkip.c
index 8c90ba79e56e..3cd819539241 100644
--- a/net/wireless/lib80211_crypt_tkip.c
+++ b/net/wireless/lib80211_crypt_tkip.c
@@ -434,8 +434,8 @@ static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
}
keyidx >>= 6;
if (tkey->key_idx != keyidx) {
- printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame "
- "keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv);
+ net_dbg_ratelimited("TKIP: RX tkey->key_idx=%d frame keyidx=%d\n",
+ tkey->key_idx, keyidx);
return -6;
}
if (!tkey->key_set) {
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index c71e274c810a..d4786f2802aa 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -4256,8 +4256,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
* station. Include these parameters here and will check them in
* cfg80211_check_station_change().
*/
- if (info->attrs[NL80211_ATTR_PEER_AID])
- params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
+ if (info->attrs[NL80211_ATTR_STA_AID])
+ params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
params.listen_interval =
@@ -4359,6 +4359,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
struct net_device *dev = info->user_ptr[1];
struct station_parameters params;
u8 *mac_addr = NULL;
+ u32 auth_assoc = BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+ BIT(NL80211_STA_FLAG_ASSOCIATED);
memset(&params, 0, sizeof(params));
@@ -4470,10 +4472,23 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
/* allow authenticated/associated only if driver handles it */
if (!(rdev->wiphy.features &
NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
- params.sta_flags_mask &
- (BIT(NL80211_STA_FLAG_AUTHENTICATED) |
- BIT(NL80211_STA_FLAG_ASSOCIATED)))
- return -EINVAL;
+ params.sta_flags_mask & auth_assoc)
+ return -EINVAL;
+
+ /* Older userspace, or userspace wanting to be compatible with
+ * !NL80211_FEATURE_FULL_AP_CLIENT_STATE, will not set the auth
+ * and assoc flags in the mask, but assumes the station will be
+ * added as associated anyway since this was the required driver
+ * behaviour before NL80211_FEATURE_FULL_AP_CLIENT_STATE was
+ * introduced.
+ * In order to not bother drivers with this quirk in the API
+ * set the flags in both the mask and set for new stations in
+ * this case.
+ */
+ if (!(params.sta_flags_mask & auth_assoc)) {
+ params.sta_flags_mask |= auth_assoc;
+ params.sta_flags_set |= auth_assoc;
+ }
/* must be last in here for error handling */
params.vlan = get_vlan(info, rdev);
@@ -5997,6 +6012,24 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
return err;
}
+static int nl80211_abort_scan(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+
+ if (!rdev->ops->abort_scan)
+ return -EOPNOTSUPP;
+
+ if (rdev->scan_msg)
+ return 0;
+
+ if (!rdev->scan_req)
+ return -ENOENT;
+
+ rdev_abort_scan(rdev, wdev);
+ return 0;
+}
+
static int
nl80211_parse_sched_scan_plans(struct wiphy *wiphy, int n_plans,
struct cfg80211_sched_scan_request *request,
@@ -6507,8 +6540,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
if (WARN_ON(!cac_time_ms))
cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
- err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef,
- cac_time_ms);
+ err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms);
if (!err) {
wdev->chandef = chandef;
wdev->cac_started = true;
@@ -7571,7 +7603,7 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
if (!nl80211_parse_mcast_rate(rdev, mcast_rate, nla_rate))
return -EINVAL;
- err = rdev->ops->set_mcast_rate(&rdev->wiphy, dev, mcast_rate);
+ err = rdev_set_mcast_rate(rdev, dev, mcast_rate);
return err;
}
@@ -7941,8 +7973,10 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
if (nla_get_flag(info->attrs[NL80211_ATTR_USE_RRM])) {
if (!(rdev->wiphy.features &
NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) ||
- !(rdev->wiphy.features & NL80211_FEATURE_QUIET))
+ !(rdev->wiphy.features & NL80211_FEATURE_QUIET)) {
+ kzfree(connkeys);
return -EINVAL;
+ }
connect.flags |= ASSOC_REQ_USE_RRM;
}
@@ -9503,6 +9537,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
if (new_triggers.tcp && new_triggers.tcp->sock)
sock_release(new_triggers.tcp->sock);
kfree(new_triggers.tcp);
+ kfree(new_triggers.nd_config);
return err;
}
#endif
@@ -9716,7 +9751,7 @@ static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info)
if (!info->attrs[NL80211_ATTR_COALESCE_RULE]) {
cfg80211_rdev_free_coalesce(rdev);
- rdev->ops->set_coalesce(&rdev->wiphy, NULL);
+ rdev_set_coalesce(rdev, NULL);
return 0;
}
@@ -9744,7 +9779,7 @@ static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info)
i++;
}
- err = rdev->ops->set_coalesce(&rdev->wiphy, &new_coalesce);
+ err = rdev_set_coalesce(rdev, &new_coalesce);
if (err)
goto error;
@@ -10946,6 +10981,14 @@ static const struct genl_ops nl80211_ops[] = {
NL80211_FLAG_NEED_RTNL,
},
{
+ .cmd = NL80211_CMD_ABORT_SCAN,
+ .doit = nl80211_abort_scan,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
.cmd = NL80211_CMD_GET_SCAN,
.policy = nl80211_policy,
.dumpit = nl80211_dump_scan,
diff --git a/net/wireless/ocb.c b/net/wireless/ocb.c
index c00d4a792319..e64dbf16330c 100644
--- a/net/wireless/ocb.c
+++ b/net/wireless/ocb.c
@@ -29,6 +29,9 @@ int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
return -EOPNOTSUPP;
+ if (!rdev->ops->join_ocb)
+ return -EOPNOTSUPP;
+
if (WARN_ON(!setup->chandef.chan))
return -EINVAL;
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index c23516d0f807..8ae0c04f9fc7 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -427,6 +427,14 @@ static inline int rdev_scan(struct cfg80211_registered_device *rdev,
return ret;
}
+static inline void rdev_abort_scan(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ trace_rdev_abort_scan(&rdev->wiphy, wdev);
+ rdev->ops->abort_scan(&rdev->wiphy, wdev);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
static inline int rdev_auth(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_auth_request *req)
@@ -1020,4 +1028,47 @@ rdev_tdls_cancel_channel_switch(struct cfg80211_registered_device *rdev,
trace_rdev_return_void(&rdev->wiphy);
}
+static inline int
+rdev_start_radar_detection(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_chan_def *chandef,
+ u32 cac_time_ms)
+{
+ int ret = -ENOTSUPP;
+
+ trace_rdev_start_radar_detection(&rdev->wiphy, dev, chandef,
+ cac_time_ms);
+ if (rdev->ops->start_radar_detection)
+ ret = rdev->ops->start_radar_detection(&rdev->wiphy, dev,
+ chandef, cac_time_ms);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_set_mcast_rate(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ int mcast_rate[IEEE80211_NUM_BANDS])
+{
+ int ret = -ENOTSUPP;
+
+ trace_rdev_set_mcast_rate(&rdev->wiphy, dev, mcast_rate);
+ if (rdev->ops->set_mcast_rate)
+ ret = rdev->ops->set_mcast_rate(&rdev->wiphy, dev, mcast_rate);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_set_coalesce(struct cfg80211_registered_device *rdev,
+ struct cfg80211_coalesce *coalesce)
+{
+ int ret = -ENOTSUPP;
+
+ trace_rdev_set_coalesce(&rdev->wiphy, coalesce);
+ if (rdev->ops->set_coalesce)
+ ret = rdev->ops->set_coalesce(&rdev->wiphy, coalesce);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
#endif /* __CFG80211_RDEV_OPS */
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 2e8d6f39ed56..3b0ce1c484a3 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -1052,7 +1052,7 @@ static u32 map_regdom_flags(u32 rd_flags)
}
static const struct ieee80211_reg_rule *
-freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
+freq_reg_info_regd(u32 center_freq,
const struct ieee80211_regdomain *regd, u32 bw)
{
int i;
@@ -1097,7 +1097,7 @@ __freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 min_bw)
u32 bw;
for (bw = MHZ_TO_KHZ(20); bw >= min_bw; bw = bw / 2) {
- reg_rule = freq_reg_info_regd(wiphy, center_freq, regd, bw);
+ reg_rule = freq_reg_info_regd(center_freq, regd, bw);
if (!IS_ERR(reg_rule))
return reg_rule;
}
@@ -1166,6 +1166,41 @@ static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd,
#endif
}
+static uint32_t reg_rule_to_chan_bw_flags(const struct ieee80211_regdomain *regd,
+ const struct ieee80211_reg_rule *reg_rule,
+ const struct ieee80211_channel *chan)
+{
+ const struct ieee80211_freq_range *freq_range = NULL;
+ u32 max_bandwidth_khz, bw_flags = 0;
+
+ freq_range = &reg_rule->freq_range;
+
+ max_bandwidth_khz = freq_range->max_bandwidth_khz;
+ /* Check if auto calculation requested */
+ if (reg_rule->flags & NL80211_RRF_AUTO_BW)
+ max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
+
+ /* If we get a reg_rule we can assume that at least 5Mhz fit */
+ if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
+ MHZ_TO_KHZ(10)))
+ bw_flags |= IEEE80211_CHAN_NO_10MHZ;
+ if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
+ MHZ_TO_KHZ(20)))
+ bw_flags |= IEEE80211_CHAN_NO_20MHZ;
+
+ if (max_bandwidth_khz < MHZ_TO_KHZ(10))
+ bw_flags |= IEEE80211_CHAN_NO_10MHZ;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(20))
+ bw_flags |= IEEE80211_CHAN_NO_20MHZ;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(40))
+ bw_flags |= IEEE80211_CHAN_NO_HT40;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(80))
+ bw_flags |= IEEE80211_CHAN_NO_80MHZ;
+ if (max_bandwidth_khz < MHZ_TO_KHZ(160))
+ bw_flags |= IEEE80211_CHAN_NO_160MHZ;
+ return bw_flags;
+}
+
/*
* Note that right now we assume the desired channel bandwidth
* is always 20 MHz for each individual channel (HT40 uses 20 MHz
@@ -1178,11 +1213,9 @@ static void handle_channel(struct wiphy *wiphy,
u32 flags, bw_flags = 0;
const struct ieee80211_reg_rule *reg_rule = NULL;
const struct ieee80211_power_rule *power_rule = NULL;
- const struct ieee80211_freq_range *freq_range = NULL;
struct wiphy *request_wiphy = NULL;
struct regulatory_request *lr = get_last_request();
const struct ieee80211_regdomain *regd;
- u32 max_bandwidth_khz;
request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
@@ -1223,31 +1256,7 @@ static void handle_channel(struct wiphy *wiphy,
chan_reg_rule_print_dbg(regd, chan, reg_rule);
power_rule = &reg_rule->power_rule;
- freq_range = &reg_rule->freq_range;
-
- max_bandwidth_khz = freq_range->max_bandwidth_khz;
- /* Check if auto calculation requested */
- if (reg_rule->flags & NL80211_RRF_AUTO_BW)
- max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
-
- /* If we get a reg_rule we can assume that at least 5Mhz fit */
- if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
- MHZ_TO_KHZ(10)))
- bw_flags |= IEEE80211_CHAN_NO_10MHZ;
- if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
- MHZ_TO_KHZ(20)))
- bw_flags |= IEEE80211_CHAN_NO_20MHZ;
-
- if (max_bandwidth_khz < MHZ_TO_KHZ(10))
- bw_flags |= IEEE80211_CHAN_NO_10MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(20))
- bw_flags |= IEEE80211_CHAN_NO_20MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(40))
- bw_flags |= IEEE80211_CHAN_NO_HT40;
- if (max_bandwidth_khz < MHZ_TO_KHZ(80))
- bw_flags |= IEEE80211_CHAN_NO_80MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(160))
- bw_flags |= IEEE80211_CHAN_NO_160MHZ;
+ bw_flags = reg_rule_to_chan_bw_flags(regd, reg_rule, chan);
if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
request_wiphy && request_wiphy == wiphy &&
@@ -1760,13 +1769,10 @@ static void handle_channel_custom(struct wiphy *wiphy,
u32 bw_flags = 0;
const struct ieee80211_reg_rule *reg_rule = NULL;
const struct ieee80211_power_rule *power_rule = NULL;
- const struct ieee80211_freq_range *freq_range = NULL;
- u32 max_bandwidth_khz;
u32 bw;
for (bw = MHZ_TO_KHZ(20); bw >= MHZ_TO_KHZ(5); bw = bw / 2) {
- reg_rule = freq_reg_info_regd(wiphy,
- MHZ_TO_KHZ(chan->center_freq),
+ reg_rule = freq_reg_info_regd(MHZ_TO_KHZ(chan->center_freq),
regd, bw);
if (!IS_ERR(reg_rule))
break;
@@ -1787,31 +1793,7 @@ static void handle_channel_custom(struct wiphy *wiphy,
chan_reg_rule_print_dbg(regd, chan, reg_rule);
power_rule = &reg_rule->power_rule;
- freq_range = &reg_rule->freq_range;
-
- max_bandwidth_khz = freq_range->max_bandwidth_khz;
- /* Check if auto calculation requested */
- if (reg_rule->flags & NL80211_RRF_AUTO_BW)
- max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
-
- /* If we get a reg_rule we can assume that at least 5Mhz fit */
- if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
- MHZ_TO_KHZ(10)))
- bw_flags |= IEEE80211_CHAN_NO_10MHZ;
- if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
- MHZ_TO_KHZ(20)))
- bw_flags |= IEEE80211_CHAN_NO_20MHZ;
-
- if (max_bandwidth_khz < MHZ_TO_KHZ(10))
- bw_flags |= IEEE80211_CHAN_NO_10MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(20))
- bw_flags |= IEEE80211_CHAN_NO_20MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(40))
- bw_flags |= IEEE80211_CHAN_NO_HT40;
- if (max_bandwidth_khz < MHZ_TO_KHZ(80))
- bw_flags |= IEEE80211_CHAN_NO_80MHZ;
- if (max_bandwidth_khz < MHZ_TO_KHZ(160))
- bw_flags |= IEEE80211_CHAN_NO_160MHZ;
+ bw_flags = reg_rule_to_chan_bw_flags(regd, reg_rule, chan);
chan->dfs_state_entered = jiffies;
chan->dfs_state = NL80211_DFS_USABLE;
@@ -3029,6 +3011,7 @@ int set_regdom(const struct ieee80211_regdomain *rd,
break;
default:
WARN(1, "invalid initiator %d\n", lr->initiator);
+ kfree(rd);
return -EINVAL;
}
@@ -3221,8 +3204,10 @@ int __init regulatory_init(void)
/* We always try to get an update for the static regdomain */
err = regulatory_hint_core(cfg80211_world_regdom->alpha2);
if (err) {
- if (err == -ENOMEM)
+ if (err == -ENOMEM) {
+ platform_device_unregister(reg_pdev);
return err;
+ }
/*
* N.B. kobject_uevent_env() can fail mainly for when we're out
* memory which is handled and propagated appropriately above
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 0c392d36781b..09b242b09bed 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -623,12 +623,24 @@ DECLARE_EVENT_CLASS(station_add_change,
__field(u32, sta_flags_set)
__field(u32, sta_modify_mask)
__field(int, listen_interval)
+ __field(u16, capability)
__field(u16, aid)
__field(u8, plink_action)
__field(u8, plink_state)
__field(u8, uapsd_queues)
+ __field(u8, max_sp)
+ __field(u8, opmode_notif)
+ __field(bool, opmode_notif_used)
__array(u8, ht_capa, (int)sizeof(struct ieee80211_ht_cap))
+ __array(u8, vht_capa, (int)sizeof(struct ieee80211_vht_cap))
__array(char, vlan, IFNAMSIZ)
+ __dynamic_array(u8, supported_rates,
+ params->supported_rates_len)
+ __dynamic_array(u8, ext_capab, params->ext_capab_len)
+ __dynamic_array(u8, supported_channels,
+ params->supported_channels_len)
+ __dynamic_array(u8, supported_oper_classes,
+ params->supported_oper_classes_len)
),
TP_fast_assign(
WIPHY_ASSIGN;
@@ -646,9 +658,35 @@ DECLARE_EVENT_CLASS(station_add_change,
if (params->ht_capa)
memcpy(__entry->ht_capa, params->ht_capa,
sizeof(struct ieee80211_ht_cap));
+ memset(__entry->vht_capa, 0, sizeof(struct ieee80211_vht_cap));
+ if (params->vht_capa)
+ memcpy(__entry->vht_capa, params->vht_capa,
+ sizeof(struct ieee80211_vht_cap));
memset(__entry->vlan, 0, sizeof(__entry->vlan));
if (params->vlan)
memcpy(__entry->vlan, params->vlan->name, IFNAMSIZ);
+ if (params->supported_rates && params->supported_rates_len)
+ memcpy(__get_dynamic_array(supported_rates),
+ params->supported_rates,
+ params->supported_rates_len);
+ if (params->ext_capab && params->ext_capab_len)
+ memcpy(__get_dynamic_array(ext_capab),
+ params->ext_capab,
+ params->ext_capab_len);
+ if (params->supported_channels &&
+ params->supported_channels_len)
+ memcpy(__get_dynamic_array(supported_channels),
+ params->supported_channels,
+ params->supported_channels_len);
+ if (params->supported_oper_classes &&
+ params->supported_oper_classes_len)
+ memcpy(__get_dynamic_array(supported_oper_classes),
+ params->supported_oper_classes,
+ params->supported_oper_classes_len);
+ __entry->max_sp = params->max_sp;
+ __entry->capability = params->capability;
+ __entry->opmode_notif = params->opmode_notif;
+ __entry->opmode_notif_used = params->opmode_notif_used;
),
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT
", station flags mask: %u, station flags set: %u, "
@@ -2818,6 +2856,71 @@ TRACE_EVENT(cfg80211_stop_iface,
WIPHY_PR_ARG, WDEV_PR_ARG)
);
+TRACE_EVENT(rdev_start_radar_detection,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_chan_def *chandef,
+ u32 cac_time_ms),
+ TP_ARGS(wiphy, netdev, chandef, cac_time_ms),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ CHAN_DEF_ENTRY
+ __field(u32, cac_time_ms)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ CHAN_DEF_ASSIGN(chandef);
+ __entry->cac_time_ms = cac_time_ms;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT
+ ", cac_time_ms=%u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG,
+ __entry->cac_time_ms)
+);
+
+TRACE_EVENT(rdev_set_mcast_rate,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ int mcast_rate[IEEE80211_NUM_BANDS]),
+ TP_ARGS(wiphy, netdev, mcast_rate),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __array(int, mcast_rate, IEEE80211_NUM_BANDS)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ memcpy(__entry->mcast_rate, mcast_rate,
+ sizeof(int) * IEEE80211_NUM_BANDS);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", "
+ "mcast_rates [2.4GHz=0x%x, 5.2GHz=0x%x, 60GHz=0x%x]",
+ WIPHY_PR_ARG, NETDEV_PR_ARG,
+ __entry->mcast_rate[IEEE80211_BAND_2GHZ],
+ __entry->mcast_rate[IEEE80211_BAND_5GHZ],
+ __entry->mcast_rate[IEEE80211_BAND_60GHZ])
+);
+
+TRACE_EVENT(rdev_set_coalesce,
+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_coalesce *coalesce),
+ TP_ARGS(wiphy, coalesce),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(int, n_rules)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->n_rules = coalesce ? coalesce->n_rules : 0;
+ ),
+ TP_printk(WIPHY_PR_FMT ", n_rules=%d",
+ WIPHY_PR_ARG, __entry->n_rules)
+);
+
+DEFINE_EVENT(wiphy_wdev_evt, rdev_abort_scan,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH
diff --git a/net/wireless/util.c b/net/wireless/util.c
index baf7218cec15..92770427b211 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -1325,13 +1325,6 @@ size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
}
EXPORT_SYMBOL(ieee80211_ie_split_ric);
-size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
- const u8 *ids, int n_ids, size_t offset)
-{
- return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset);
-}
-EXPORT_SYMBOL(ieee80211_ie_split);
-
bool ieee80211_operating_class_to_band(u8 operating_class,
enum ieee80211_band *band)
{
@@ -1620,120 +1613,6 @@ int cfg80211_check_combinations(struct wiphy *wiphy,
}
EXPORT_SYMBOL(cfg80211_check_combinations);
-int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev,
- enum nl80211_iftype iftype,
- struct ieee80211_channel *chan,
- enum cfg80211_chan_mode chanmode,
- u8 radar_detect)
-{
- struct wireless_dev *wdev_iter;
- int num[NUM_NL80211_IFTYPES];
- struct ieee80211_channel
- *used_channels[CFG80211_MAX_NUM_DIFFERENT_CHANNELS];
- struct ieee80211_channel *ch;
- enum cfg80211_chan_mode chmode;
- int num_different_channels = 0;
- int total = 1;
- int i;
-
- ASSERT_RTNL();
-
- if (WARN_ON(hweight32(radar_detect) > 1))
- return -EINVAL;
-
- if (WARN_ON(iftype >= NUM_NL80211_IFTYPES))
- return -EINVAL;
-
- /* Always allow software iftypes */
- if (rdev->wiphy.software_iftypes & BIT(iftype)) {
- if (radar_detect)
- return -EINVAL;
- return 0;
- }
-
- memset(num, 0, sizeof(num));
- memset(used_channels, 0, sizeof(used_channels));
-
- num[iftype] = 1;
-
- /* TODO: We'll probably not need this anymore, since this
- * should only be called with CHAN_MODE_UNDEFINED. There are
- * still a couple of pending calls where other chanmodes are
- * used, but we should get rid of them.
- */
- switch (chanmode) {
- case CHAN_MODE_UNDEFINED:
- break;
- case CHAN_MODE_SHARED:
- WARN_ON(!chan);
- used_channels[0] = chan;
- num_different_channels++;
- break;
- case CHAN_MODE_EXCLUSIVE:
- num_different_channels++;
- break;
- }
-
- list_for_each_entry(wdev_iter, &rdev->wdev_list, list) {
- if (wdev_iter == wdev)
- continue;
- if (wdev_iter->iftype == NL80211_IFTYPE_P2P_DEVICE) {
- if (!wdev_iter->p2p_started)
- continue;
- } else if (wdev_iter->netdev) {
- if (!netif_running(wdev_iter->netdev))
- continue;
- } else {
- WARN_ON(1);
- }
-
- if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype))
- continue;
-
- /*
- * We may be holding the "wdev" mutex, but now need to lock
- * wdev_iter. This is OK because once we get here wdev_iter
- * is not wdev (tested above), but we need to use the nested
- * locking for lockdep.
- */
- mutex_lock_nested(&wdev_iter->mtx, 1);
- __acquire(wdev_iter->mtx);
- cfg80211_get_chan_state(wdev_iter, &ch, &chmode, &radar_detect);
- wdev_unlock(wdev_iter);
-
- switch (chmode) {
- case CHAN_MODE_UNDEFINED:
- break;
- case CHAN_MODE_SHARED:
- for (i = 0; i < CFG80211_MAX_NUM_DIFFERENT_CHANNELS; i++)
- if (!used_channels[i] || used_channels[i] == ch)
- break;
-
- if (i == CFG80211_MAX_NUM_DIFFERENT_CHANNELS)
- return -EBUSY;
-
- if (used_channels[i] == NULL) {
- used_channels[i] = ch;
- num_different_channels++;
- }
- break;
- case CHAN_MODE_EXCLUSIVE:
- num_different_channels++;
- break;
- }
-
- num[wdev_iter->iftype]++;
- total++;
- }
-
- if (total == 1 && !radar_detect)
- return 0;
-
- return cfg80211_check_combinations(&rdev->wiphy, num_different_channels,
- radar_detect, num);
-}
-
int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,
const u8 *rates, unsigned int n_rates,
u32 *mask)
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 09bfcbac63bb..b5e665b3cfb0 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -303,6 +303,14 @@ struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp)
}
EXPORT_SYMBOL(xfrm_policy_alloc);
+static void xfrm_policy_destroy_rcu(struct rcu_head *head)
+{
+ struct xfrm_policy *policy = container_of(head, struct xfrm_policy, rcu);
+
+ security_xfrm_policy_free(policy->security);
+ kfree(policy);
+}
+
/* Destroy xfrm_policy: descendant resources must be released to this moment. */
void xfrm_policy_destroy(struct xfrm_policy *policy)
@@ -312,8 +320,7 @@ void xfrm_policy_destroy(struct xfrm_policy *policy)
if (del_timer(&policy->timer) || del_timer(&policy->polq.hold_timer))
BUG();
- security_xfrm_policy_free(policy->security);
- kfree(policy);
+ call_rcu(&policy->rcu, xfrm_policy_destroy_rcu);
}
EXPORT_SYMBOL(xfrm_policy_destroy);
@@ -1214,8 +1221,10 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
struct xfrm_policy *pol;
struct net *net = sock_net(sk);
+ rcu_read_lock();
read_lock_bh(&net->xfrm.xfrm_policy_lock);
- if ((pol = sk->sk_policy[dir]) != NULL) {
+ pol = rcu_dereference(sk->sk_policy[dir]);
+ if (pol != NULL) {
bool match = xfrm_selector_match(&pol->selector, fl,
sk->sk_family);
int err = 0;
@@ -1239,6 +1248,7 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
}
out:
read_unlock_bh(&net->xfrm.xfrm_policy_lock);
+ rcu_read_unlock();
return pol;
}
@@ -1307,13 +1317,14 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol)
#endif
write_lock_bh(&net->xfrm.xfrm_policy_lock);
- old_pol = sk->sk_policy[dir];
- sk->sk_policy[dir] = pol;
+ old_pol = rcu_dereference_protected(sk->sk_policy[dir],
+ lockdep_is_held(&net->xfrm.xfrm_policy_lock));
if (pol) {
pol->curlft.add_time = get_seconds();
pol->index = xfrm_gen_index(net, XFRM_POLICY_MAX+dir, 0);
xfrm_sk_policy_link(pol, dir);
}
+ rcu_assign_pointer(sk->sk_policy[dir], pol);
if (old_pol) {
if (pol)
xfrm_policy_requeue(old_pol, pol);
@@ -1361,17 +1372,26 @@ static struct xfrm_policy *clone_policy(const struct xfrm_policy *old, int dir)
return newp;
}
-int __xfrm_sk_clone_policy(struct sock *sk)
+int __xfrm_sk_clone_policy(struct sock *sk, const struct sock *osk)
{
- struct xfrm_policy *p0 = sk->sk_policy[0],
- *p1 = sk->sk_policy[1];
+ const struct xfrm_policy *p;
+ struct xfrm_policy *np;
+ int i, ret = 0;
- sk->sk_policy[0] = sk->sk_policy[1] = NULL;
- if (p0 && (sk->sk_policy[0] = clone_policy(p0, 0)) == NULL)
- return -ENOMEM;
- if (p1 && (sk->sk_policy[1] = clone_policy(p1, 1)) == NULL)
- return -ENOMEM;
- return 0;
+ rcu_read_lock();
+ for (i = 0; i < 2; i++) {
+ p = rcu_dereference(osk->sk_policy[i]);
+ if (p) {
+ np = clone_policy(p, i);
+ if (unlikely(!np)) {
+ ret = -ENOMEM;
+ break;
+ }
+ rcu_assign_pointer(sk->sk_policy[i], np);
+ }
+ }
+ rcu_read_unlock();
+ return ret;
}
static int
@@ -2198,6 +2218,7 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
xdst = NULL;
route = NULL;
+ sk = sk_const_to_full_sk(sk);
if (sk && sk->sk_policy[XFRM_POLICY_OUT]) {
num_pols = 1;
pols[0] = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl);
@@ -2477,6 +2498,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
}
pol = NULL;
+ sk = sk_to_full_sk(sk);
if (sk && sk->sk_policy[dir]) {
pol = xfrm_sk_policy_lookup(sk, dir, &fl);
if (IS_ERR(pol)) {
@@ -2804,7 +2826,6 @@ static struct neighbour *xfrm_neigh_lookup(const struct dst_entry *dst,
int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
{
- struct net *net;
int err = 0;
if (unlikely(afinfo == NULL))
return -EINVAL;
@@ -2835,26 +2856,6 @@ int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo)
}
spin_unlock(&xfrm_policy_afinfo_lock);
- rtnl_lock();
- for_each_net(net) {
- struct dst_ops *xfrm_dst_ops;
-
- switch (afinfo->family) {
- case AF_INET:
- xfrm_dst_ops = &net->xfrm.xfrm4_dst_ops;
- break;
-#if IS_ENABLED(CONFIG_IPV6)
- case AF_INET6:
- xfrm_dst_ops = &net->xfrm.xfrm6_dst_ops;
- break;
-#endif
- default:
- BUG();
- }
- *xfrm_dst_ops = *afinfo->dst_ops;
- }
- rtnl_unlock();
-
return err;
}
EXPORT_SYMBOL(xfrm_policy_register_afinfo);
@@ -2890,22 +2891,6 @@ int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo)
}
EXPORT_SYMBOL(xfrm_policy_unregister_afinfo);
-static void __net_init xfrm_dst_ops_init(struct net *net)
-{
- struct xfrm_policy_afinfo *afinfo;
-
- rcu_read_lock();
- afinfo = rcu_dereference(xfrm_policy_afinfo[AF_INET]);
- if (afinfo)
- net->xfrm.xfrm4_dst_ops = *afinfo->dst_ops;
-#if IS_ENABLED(CONFIG_IPV6)
- afinfo = rcu_dereference(xfrm_policy_afinfo[AF_INET6]);
- if (afinfo)
- net->xfrm.xfrm6_dst_ops = *afinfo->dst_ops;
-#endif
- rcu_read_unlock();
-}
-
static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
@@ -3054,7 +3039,6 @@ static int __net_init xfrm_net_init(struct net *net)
rv = xfrm_policy_init(net);
if (rv < 0)
goto out_policy;
- xfrm_dst_ops_init(net);
rv = xfrm_sysctl_init(net);
if (rv < 0)
goto out_sysctl;
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 79e86613712f..26a48d76eb9d 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -104,8 +104,9 @@ modname_flags = $(if $(filter 1,$(words $(modname))),\
orig_c_flags = $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(KBUILD_SUBDIR_CCFLAGS) \
$(ccflags-y) $(CFLAGS_$(basetarget).o)
_c_flags = $(filter-out $(CFLAGS_REMOVE_$(basetarget).o), $(orig_c_flags))
-_a_flags = $(KBUILD_CPPFLAGS) $(KBUILD_AFLAGS) $(KBUILD_SUBDIR_ASFLAGS) \
+orig_a_flags = $(KBUILD_CPPFLAGS) $(KBUILD_AFLAGS) $(KBUILD_SUBDIR_ASFLAGS) \
$(asflags-y) $(AFLAGS_$(basetarget).o)
+_a_flags = $(filter-out $(AFLAGS_REMOVE_$(basetarget).o), $(orig_a_flags))
_cpp_flags = $(KBUILD_CPPFLAGS) $(cppflags-y) $(CPPFLAGS_$(@F))
#
diff --git a/scripts/bloat-o-meter b/scripts/bloat-o-meter
index 23e78dcd12bf..38b64f487315 100755
--- a/scripts/bloat-o-meter
+++ b/scripts/bloat-o-meter
@@ -58,8 +58,8 @@ for name in common:
delta.sort()
delta.reverse()
-print "add/remove: %s/%s grow/shrink: %s/%s up/down: %s/%s (%s)" % \
- (add, remove, grow, shrink, up, -down, up-down)
-print "%-40s %7s %7s %+7s" % ("function", "old", "new", "delta")
+print("add/remove: %s/%s grow/shrink: %s/%s up/down: %s/%s (%s)" % \
+ (add, remove, grow, shrink, up, -down, up-down))
+print("%-40s %7s %7s %+7s" % ("function", "old", "new", "delta"))
for d, n in delta:
- if d: print "%-40s %7s %7s %+7d" % (n, old.get(n,"-"), new.get(n,"-"), d)
+ if d: print("%-40s %7s %7s %+7d" % (n, old.get(n,"-"), new.get(n,"-"), d))
diff --git a/scripts/checkkconfigsymbols.py b/scripts/checkkconfigsymbols.py
index 2f4b7ffd5570..d8f6c094cce5 100755
--- a/scripts/checkkconfigsymbols.py
+++ b/scripts/checkkconfigsymbols.py
@@ -8,11 +8,14 @@
# Licensed under the terms of the GNU GPL License version 2
+import difflib
import os
import re
+import signal
import sys
-from subprocess import Popen, PIPE, STDOUT
+from multiprocessing import Pool, cpu_count
from optparse import OptionParser
+from subprocess import Popen, PIPE, STDOUT
# regex expressions
@@ -26,7 +29,7 @@ SOURCE_FEATURE = r"(?:\W|\b)+[D]{,1}CONFIG_(" + FEATURE + r")"
# regex objects
REGEX_FILE_KCONFIG = re.compile(r".*Kconfig[\.\w+\-]*$")
-REGEX_FEATURE = re.compile(r'(?!\B"[^"]*)' + FEATURE + r'(?![^"]*"\B)')
+REGEX_FEATURE = re.compile(r'(?!\B)' + FEATURE + r'(?!\B)')
REGEX_SOURCE_FEATURE = re.compile(SOURCE_FEATURE)
REGEX_KCONFIG_DEF = re.compile(DEF)
REGEX_KCONFIG_EXPR = re.compile(EXPR)
@@ -34,6 +37,7 @@ REGEX_KCONFIG_STMT = re.compile(STMT)
REGEX_KCONFIG_HELP = re.compile(r"^\s+(help|---help---)\s*$")
REGEX_FILTER_FEATURES = re.compile(r"[A-Za-z0-9]$")
REGEX_NUMERIC = re.compile(r"0[xX][0-9a-fA-F]+|[0-9]+")
+REGEX_QUOTES = re.compile("(\"(.*?)\")")
def parse_options():
@@ -71,6 +75,9 @@ def parse_options():
"the pattern needs to be a Python regex. To "
"ignore defconfigs, specify -i '.*defconfig'.")
+ parser.add_option('-s', '--sim', dest='sim', action='store', default="",
+ help="Print a list of maximum 10 string-similar symbols.")
+
parser.add_option('', '--force', dest='force', action='store_true',
default=False,
help="Reset current Git tree even when it's dirty.")
@@ -109,6 +116,18 @@ def main():
"""Main function of this module."""
opts = parse_options()
+ if opts.sim and not opts.commit and not opts.diff:
+ sims = find_sims(opts.sim, opts.ignore)
+ if sims:
+ print "%s: %s" % (yel("Similar symbols"), ', '.join(sims))
+ else:
+ print "%s: no similar symbols found" % yel("Similar symbols")
+ sys.exit(0)
+
+ # dictionary of (un)defined symbols
+ defined = {}
+ undefined = {}
+
if opts.commit or opts.diff:
head = get_head()
@@ -127,40 +146,56 @@ def main():
# get undefined items before the commit
execute("git reset --hard %s" % commit_a)
- undefined_a = check_symbols(opts.ignore)
+ undefined_a, _ = check_symbols(opts.ignore)
# get undefined items for the commit
execute("git reset --hard %s" % commit_b)
- undefined_b = check_symbols(opts.ignore)
+ undefined_b, defined = check_symbols(opts.ignore)
# report cases that are present for the commit but not before
for feature in sorted(undefined_b):
# feature has not been undefined before
if not feature in undefined_a:
files = sorted(undefined_b.get(feature))
- print "%s\t%s" % (yel(feature), ", ".join(files))
- if opts.find:
- commits = find_commits(feature, opts.diff)
- print red(commits)
+ undefined[feature] = files
# check if there are new files that reference the undefined feature
else:
files = sorted(undefined_b.get(feature) -
undefined_a.get(feature))
if files:
- print "%s\t%s" % (yel(feature), ", ".join(files))
- if opts.find:
- commits = find_commits(feature, opts.diff)
- print red(commits)
+ undefined[feature] = files
# reset to head
execute("git reset --hard %s" % head)
# default to check the entire tree
else:
- undefined = check_symbols(opts.ignore)
- for feature in sorted(undefined):
- files = sorted(undefined.get(feature))
- print "%s\t%s" % (yel(feature), ", ".join(files))
+ undefined, defined = check_symbols(opts.ignore)
+
+ # now print the output
+ for feature in sorted(undefined):
+ print red(feature)
+
+ files = sorted(undefined.get(feature))
+ print "%s: %s" % (yel("Referencing files"), ", ".join(files))
+
+ sims = find_sims(feature, opts.ignore, defined)
+ sims_out = yel("Similar symbols")
+ if sims:
+ print "%s: %s" % (sims_out, ', '.join(sims))
+ else:
+ print "%s: %s" % (sims_out, "no similar symbols found")
+
+ if opts.find:
+ print "%s:" % yel("Commits changing symbol")
+ commits = find_commits(feature, opts.diff)
+ if commits:
+ for commit in commits:
+ commit = commit.split(" ", 1)
+ print "\t- %s (\"%s\")" % (yel(commit[0]), commit[1])
+ else:
+ print "\t- no commit found"
+ print # new line
def yel(string):
@@ -190,7 +225,7 @@ def find_commits(symbol, diff):
"""Find commits changing %symbol in the given range of %diff."""
commits = execute("git log --pretty=oneline --abbrev-commit -G %s %s"
% (symbol, diff))
- return commits
+ return [x for x in commits.split("\n") if x]
def tree_is_dirty():
@@ -209,43 +244,107 @@ def get_head():
return stdout.strip('\n')
-def check_symbols(ignore):
- """Find undefined Kconfig symbols and return a dict with the symbol as key
- and a list of referencing files as value. Files matching %ignore are not
- checked for undefined symbols."""
- source_files = []
- kconfig_files = []
- defined_features = set()
- referenced_features = dict() # {feature: [files]}
+def partition(lst, size):
+ """Partition list @lst into eveni-sized lists of size @size."""
+ return [lst[i::size] for i in xrange(size)]
+
+
+def init_worker():
+ """Set signal handler to ignore SIGINT."""
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
+
+
+def find_sims(symbol, ignore, defined = []):
+ """Return a list of max. ten Kconfig symbols that are string-similar to
+ @symbol."""
+ if defined:
+ return sorted(difflib.get_close_matches(symbol, set(defined), 10))
+
+ pool = Pool(cpu_count(), init_worker)
+ kfiles = []
+ for gitfile in get_files():
+ if REGEX_FILE_KCONFIG.match(gitfile):
+ kfiles.append(gitfile)
+ arglist = []
+ for part in partition(kfiles, cpu_count()):
+ arglist.append((part, ignore))
+
+ for res in pool.map(parse_kconfig_files, arglist):
+ defined.extend(res[0])
+
+ return sorted(difflib.get_close_matches(symbol, set(defined), 10))
+
+
+def get_files():
+ """Return a list of all files in the current git directory."""
# use 'git ls-files' to get the worklist
stdout = execute("git ls-files")
if len(stdout) > 0 and stdout[-1] == "\n":
stdout = stdout[:-1]
+ files = []
for gitfile in stdout.rsplit("\n"):
if ".git" in gitfile or "ChangeLog" in gitfile or \
".log" in gitfile or os.path.isdir(gitfile) or \
gitfile.startswith("tools/"):
continue
+ files.append(gitfile)
+ return files
+
+
+def check_symbols(ignore):
+ """Find undefined Kconfig symbols and return a dict with the symbol as key
+ and a list of referencing files as value. Files matching %ignore are not
+ checked for undefined symbols."""
+ pool = Pool(cpu_count(), init_worker)
+ try:
+ return check_symbols_helper(pool, ignore)
+ except KeyboardInterrupt:
+ pool.terminate()
+ pool.join()
+ sys.exit(1)
+
+
+def check_symbols_helper(pool, ignore):
+ """Helper method for check_symbols(). Used to catch keyboard interrupts in
+ check_symbols() in order to properly terminate running worker processes."""
+ source_files = []
+ kconfig_files = []
+ defined_features = []
+ referenced_features = dict() # {file: [features]}
+
+ for gitfile in get_files():
if REGEX_FILE_KCONFIG.match(gitfile):
kconfig_files.append(gitfile)
else:
- # all non-Kconfig files are checked for consistency
+ if ignore and not re.match(ignore, gitfile):
+ continue
+ # add source files that do not match the ignore pattern
source_files.append(gitfile)
- for sfile in source_files:
- if ignore and re.match(ignore, sfile):
- # do not check files matching %ignore
- continue
- parse_source_file(sfile, referenced_features)
+ # parse source files
+ arglist = partition(source_files, cpu_count())
+ for res in pool.map(parse_source_files, arglist):
+ referenced_features.update(res)
- for kfile in kconfig_files:
- if ignore and re.match(ignore, kfile):
- # do not collect references for files matching %ignore
- parse_kconfig_file(kfile, defined_features, dict())
- else:
- parse_kconfig_file(kfile, defined_features, referenced_features)
+
+ # parse kconfig files
+ arglist = []
+ for part in partition(kconfig_files, cpu_count()):
+ arglist.append((part, ignore))
+ for res in pool.map(parse_kconfig_files, arglist):
+ defined_features.extend(res[0])
+ referenced_features.update(res[1])
+ defined_features = set(defined_features)
+
+ # inverse mapping of referenced_features to dict(feature: [files])
+ inv_map = dict()
+ for _file, features in referenced_features.iteritems():
+ for feature in features:
+ inv_map[feature] = inv_map.get(feature, set())
+ inv_map[feature].add(_file)
+ referenced_features = inv_map
undefined = {} # {feature: [files]}
for feature in sorted(referenced_features):
@@ -259,12 +358,26 @@ def check_symbols(ignore):
if feature[:-len("_MODULE")] in defined_features:
continue
undefined[feature] = referenced_features.get(feature)
- return undefined
+ return undefined, defined_features
-def parse_source_file(sfile, referenced_features):
- """Parse @sfile for referenced Kconfig features."""
+def parse_source_files(source_files):
+ """Parse each source file in @source_files and return dictionary with source
+ files as keys and lists of references Kconfig symbols as values."""
+ referenced_features = dict()
+ for sfile in source_files:
+ referenced_features[sfile] = parse_source_file(sfile)
+ return referenced_features
+
+
+def parse_source_file(sfile):
+ """Parse @sfile and return a list of referenced Kconfig features."""
lines = []
+ references = []
+
+ if not os.path.exists(sfile):
+ return references
+
with open(sfile, "r") as stream:
lines = stream.readlines()
@@ -275,9 +388,9 @@ def parse_source_file(sfile, referenced_features):
for feature in features:
if not REGEX_FILTER_FEATURES.search(feature):
continue
- sfiles = referenced_features.get(feature, set())
- sfiles.add(sfile)
- referenced_features[feature] = sfiles
+ references.append(feature)
+
+ return references
def get_features_in_line(line):
@@ -285,11 +398,35 @@ def get_features_in_line(line):
return REGEX_FEATURE.findall(line)
-def parse_kconfig_file(kfile, defined_features, referenced_features):
+def parse_kconfig_files(args):
+ """Parse kconfig files and return tuple of defined and references Kconfig
+ symbols. Note, @args is a tuple of a list of files and the @ignore
+ pattern."""
+ kconfig_files = args[0]
+ ignore = args[1]
+ defined_features = []
+ referenced_features = dict()
+
+ for kfile in kconfig_files:
+ defined, references = parse_kconfig_file(kfile)
+ defined_features.extend(defined)
+ if ignore and re.match(ignore, kfile):
+ # do not collect references for files that match the ignore pattern
+ continue
+ referenced_features[kfile] = references
+ return (defined_features, referenced_features)
+
+
+def parse_kconfig_file(kfile):
"""Parse @kfile and update feature definitions and references."""
lines = []
+ defined = []
+ references = []
skip = False
+ if not os.path.exists(kfile):
+ return defined, references
+
with open(kfile, "r") as stream:
lines = stream.readlines()
@@ -300,7 +437,7 @@ def parse_kconfig_file(kfile, defined_features, referenced_features):
if REGEX_KCONFIG_DEF.match(line):
feature_def = REGEX_KCONFIG_DEF.findall(line)
- defined_features.add(feature_def[0])
+ defined.append(feature_def[0])
skip = False
elif REGEX_KCONFIG_HELP.match(line):
skip = True
@@ -308,6 +445,7 @@ def parse_kconfig_file(kfile, defined_features, referenced_features):
# ignore content of help messages
pass
elif REGEX_KCONFIG_STMT.match(line):
+ line = REGEX_QUOTES.sub("", line)
features = get_features_in_line(line)
# multi-line statements
while line.endswith("\\"):
@@ -319,9 +457,9 @@ def parse_kconfig_file(kfile, defined_features, referenced_features):
if REGEX_NUMERIC.match(feature):
# ignore numeric values
continue
- paths = referenced_features.get(feature, set())
- paths.add(kfile)
- referenced_features[feature] = paths
+ references.append(feature)
+
+ return defined, references
if __name__ == "__main__":
diff --git a/scripts/ld-version.sh b/scripts/ld-version.sh
index 198580d245e0..d154f0877fd8 100755
--- a/scripts/ld-version.sh
+++ b/scripts/ld-version.sh
@@ -2,7 +2,9 @@
# extract linker version number from stdin and turn into single number
{
gsub(".*)", "");
+ gsub(".*version ", "");
+ gsub("-.*", "");
split($1,a, ".");
- print a[1]*10000000 + a[2]*100000 + a[3]*10000 + a[4]*100 + a[5];
+ print a[1]*100000000 + a[2]*1000000 + a[3]*10000 + a[4]*100 + a[5];
exit
}
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index 1a10d8ac8162..ba6c34ea5429 100755
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -62,7 +62,7 @@ vmlinux_link()
-Wl,--start-group \
${KBUILD_VMLINUX_MAIN} \
-Wl,--end-group \
- -lutil ${1}
+ -lutil -lrt -lpthread ${1}
rm -f linux
fi
}
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index 5b96206e9aab..161dd0d67da8 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -125,7 +125,7 @@ do { \
sprintf(str + strlen(str), "*"); \
} while(0)
-/* Always end in a wildcard, for future extension */
+/* End in a wildcard, for future extension */
static inline void add_wildcard(char *str)
{
int len = strlen(str);
@@ -704,7 +704,6 @@ static int do_of_entry (const char *filename, void *symval, char *alias)
if (isspace (*tmp))
*tmp = '_';
- add_wildcard(alias);
return 1;
}
ADD_TO_DEVTABLE("of", of_device_id, do_of_entry);
@@ -917,7 +916,7 @@ static int do_vmbus_entry(const char *filename, void *symval,
char guid_name[(sizeof(*guid) + 1) * 2];
for (i = 0; i < (sizeof(*guid) * 2); i += 2)
- sprintf(&guid_name[i], "%02x", TO_NATIVE((*guid)[i/2]));
+ sprintf(&guid_name[i], "%02x", TO_NATIVE((guid->b)[i/2]));
strcpy(alias, "vmbus:");
strcat(alias, guid_name);
diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c
index 698768bdc581..e167592793a7 100644
--- a/scripts/recordmcount.c
+++ b/scripts/recordmcount.c
@@ -48,12 +48,17 @@
static int fd_map; /* File descriptor for file being modified. */
static int mmap_failed; /* Boolean flag. */
-static void *ehdr_curr; /* current ElfXX_Ehdr * for resource cleanup */
static char gpfx; /* prefix for global symbol name (sometimes '_') */
static struct stat sb; /* Remember .st_size, etc. */
static jmp_buf jmpenv; /* setjmp/longjmp per-file error escape */
static const char *altmcount; /* alternate mcount symbol name */
static int warn_on_notrace_sect; /* warn when section has mcount not being recorded */
+static void *file_map; /* pointer of the mapped file */
+static void *file_end; /* pointer to the end of the mapped file */
+static int file_updated; /* flag to state file was changed */
+static void *file_ptr; /* current file pointer location */
+static void *file_append; /* added to the end of the file */
+static size_t file_append_size; /* how much is added to end of file */
/* setjmp() return values */
enum {
@@ -67,10 +72,14 @@ static void
cleanup(void)
{
if (!mmap_failed)
- munmap(ehdr_curr, sb.st_size);
+ munmap(file_map, sb.st_size);
else
- free(ehdr_curr);
- close(fd_map);
+ free(file_map);
+ file_map = NULL;
+ free(file_append);
+ file_append = NULL;
+ file_append_size = 0;
+ file_updated = 0;
}
static void __attribute__((noreturn))
@@ -92,12 +101,22 @@ succeed_file(void)
static off_t
ulseek(int const fd, off_t const offset, int const whence)
{
- off_t const w = lseek(fd, offset, whence);
- if (w == (off_t)-1) {
- perror("lseek");
+ switch (whence) {
+ case SEEK_SET:
+ file_ptr = file_map + offset;
+ break;
+ case SEEK_CUR:
+ file_ptr += offset;
+ break;
+ case SEEK_END:
+ file_ptr = file_map + (sb.st_size - offset);
+ break;
+ }
+ if (file_ptr < file_map) {
+ fprintf(stderr, "lseek: seek before file\n");
fail_file();
}
- return w;
+ return file_ptr - file_map;
}
static size_t
@@ -114,12 +133,38 @@ uread(int const fd, void *const buf, size_t const count)
static size_t
uwrite(int const fd, void const *const buf, size_t const count)
{
- size_t const n = write(fd, buf, count);
- if (n != count) {
- perror("write");
- fail_file();
+ size_t cnt = count;
+ off_t idx = 0;
+
+ file_updated = 1;
+
+ if (file_ptr + count >= file_end) {
+ off_t aoffset = (file_ptr + count) - file_end;
+
+ if (aoffset > file_append_size) {
+ file_append = realloc(file_append, aoffset);
+ file_append_size = aoffset;
+ }
+ if (!file_append) {
+ perror("write");
+ fail_file();
+ }
+ if (file_ptr < file_end) {
+ cnt = file_end - file_ptr;
+ } else {
+ cnt = 0;
+ idx = aoffset - count;
+ }
}
- return n;
+
+ if (cnt)
+ memcpy(file_ptr, buf, cnt);
+
+ if (cnt < count)
+ memcpy(file_append + idx, buf + cnt, count - cnt);
+
+ file_ptr += count;
+ return count;
}
static void *
@@ -192,9 +237,7 @@ static int make_nop_arm64(void *map, size_t const offset)
*/
static void *mmap_file(char const *fname)
{
- void *addr;
-
- fd_map = open(fname, O_RDWR);
+ fd_map = open(fname, O_RDONLY);
if (fd_map < 0 || fstat(fd_map, &sb) < 0) {
perror(fname);
fail_file();
@@ -203,15 +246,58 @@ static void *mmap_file(char const *fname)
fprintf(stderr, "not a regular file: %s\n", fname);
fail_file();
}
- addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE,
- fd_map, 0);
+ file_map = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE,
+ fd_map, 0);
mmap_failed = 0;
- if (addr == MAP_FAILED) {
+ if (file_map == MAP_FAILED) {
mmap_failed = 1;
- addr = umalloc(sb.st_size);
- uread(fd_map, addr, sb.st_size);
+ file_map = umalloc(sb.st_size);
+ uread(fd_map, file_map, sb.st_size);
+ }
+ close(fd_map);
+
+ file_end = file_map + sb.st_size;
+
+ return file_map;
+}
+
+static void write_file(const char *fname)
+{
+ char tmp_file[strlen(fname) + 4];
+ size_t n;
+
+ if (!file_updated)
+ return;
+
+ sprintf(tmp_file, "%s.rc", fname);
+
+ /*
+ * After reading the entire file into memory, delete it
+ * and write it back, to prevent weird side effects of modifying
+ * an object file in place.
+ */
+ fd_map = open(tmp_file, O_WRONLY | O_TRUNC | O_CREAT, sb.st_mode);
+ if (fd_map < 0) {
+ perror(fname);
+ fail_file();
+ }
+ n = write(fd_map, file_map, sb.st_size);
+ if (n != sb.st_size) {
+ perror("write");
+ fail_file();
+ }
+ if (file_append_size) {
+ n = write(fd_map, file_append, file_append_size);
+ if (n != file_append_size) {
+ perror("write");
+ fail_file();
+ }
+ }
+ close(fd_map);
+ if (rename(tmp_file, fname) < 0) {
+ perror(fname);
+ fail_file();
}
- return addr;
}
/* w8rev, w8nat, ...: Handle endianness. */
@@ -318,7 +404,6 @@ do_file(char const *const fname)
Elf32_Ehdr *const ehdr = mmap_file(fname);
unsigned int reltype = 0;
- ehdr_curr = ehdr;
w = w4nat;
w2 = w2nat;
w8 = w8nat;
@@ -441,6 +526,7 @@ do_file(char const *const fname)
}
} /* end switch */
+ write_file(fname);
cleanup();
}
@@ -493,11 +579,14 @@ main(int argc, char *argv[])
case SJ_SETJMP: /* normal sequence */
/* Avoid problems if early cleanup() */
fd_map = -1;
- ehdr_curr = NULL;
mmap_failed = 1;
+ file_map = NULL;
+ file_ptr = NULL;
+ file_updated = 0;
do_file(file);
break;
case SJ_FAIL: /* error in do_file or below */
+ fprintf(stderr, "%s: failed\n", file);
++n_error;
break;
case SJ_SUCCEED: /* premature success */
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index 3d2f5b45c8cb..c2e3ccd4b510 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -234,12 +234,13 @@ int __init integrity_read_file(const char *path, char **data)
}
rc = integrity_kernel_read(file, 0, buf, size);
- if (rc < 0)
- kfree(buf);
- else if (rc != size)
- rc = -EIO;
- else
+ if (rc == size) {
*data = buf;
+ } else {
+ kfree(buf);
+ if (rc >= 0)
+ rc = -EIO;
+ }
out:
fput(file);
return rc;
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
index 927db9f35ad6..696ccfa08d10 100644
--- a/security/keys/encrypted-keys/encrypted.c
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -845,6 +845,8 @@ static int encrypted_update(struct key *key, struct key_preparsed_payload *prep)
size_t datalen = prep->datalen;
int ret = 0;
+ if (test_bit(KEY_FLAG_NEGATIVE, &key->flags))
+ return -ENOKEY;
if (datalen <= 0 || datalen > 32767 || !prep->data)
return -EINVAL;
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index fb111eafcb89..1c3872aeed14 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -751,16 +751,16 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
/* the key is probably readable - now try to read it */
can_read_key:
- ret = key_validate(key);
- if (ret == 0) {
- ret = -EOPNOTSUPP;
- if (key->type->read) {
- /* read the data with the semaphore held (since we
- * might sleep) */
- down_read(&key->sem);
+ ret = -EOPNOTSUPP;
+ if (key->type->read) {
+ /* Read the data with the semaphore held (since we might sleep)
+ * to protect against the key being updated or revoked.
+ */
+ down_read(&key->sem);
+ ret = key_validate(key);
+ if (ret == 0)
ret = key->type->read(key, buffer, buflen);
- up_read(&key->sem);
- }
+ up_read(&key->sem);
}
error2:
diff --git a/security/keys/trusted.c b/security/keys/trusted.c
index 903dace648a1..16dec53184b6 100644
--- a/security/keys/trusted.c
+++ b/security/keys/trusted.c
@@ -1007,13 +1007,16 @@ static void trusted_rcu_free(struct rcu_head *rcu)
*/
static int trusted_update(struct key *key, struct key_preparsed_payload *prep)
{
- struct trusted_key_payload *p = key->payload.data[0];
+ struct trusted_key_payload *p;
struct trusted_key_payload *new_p;
struct trusted_key_options *new_o;
size_t datalen = prep->datalen;
char *datablob;
int ret = 0;
+ if (test_bit(KEY_FLAG_NEGATIVE, &key->flags))
+ return -ENOKEY;
+ p = key->payload.data[0];
if (!p->migratable)
return -EPERM;
if (datalen <= 0 || datalen > 32767 || !prep->data)
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
index 28cb30f80256..8705d79b2c6f 100644
--- a/security/keys/user_defined.c
+++ b/security/keys/user_defined.c
@@ -120,7 +120,10 @@ int user_update(struct key *key, struct key_preparsed_payload *prep)
if (ret == 0) {
/* attach the new data, displacing the old */
- zap = key->payload.data[0];
+ if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags))
+ zap = key->payload.data[0];
+ else
+ zap = NULL;
rcu_assign_keypointer(key, upayload);
key->expiry = 0;
}
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index c02da25d7b63..73c60baa90a4 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -147,23 +147,16 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
ssize_t length;
int new_value;
- length = -ENOMEM;
if (count >= PAGE_SIZE)
- goto out;
+ return -ENOMEM;
/* No partial writes. */
- length = -EINVAL;
if (*ppos != 0)
- goto out;
-
- length = -ENOMEM;
- page = (char *)get_zeroed_page(GFP_KERNEL);
- if (!page)
- goto out;
+ return -EINVAL;
- length = -EFAULT;
- if (copy_from_user(page, buf, count))
- goto out;
+ page = memdup_user_nul(buf, count);
+ if (IS_ERR(page))
+ return PTR_ERR(page);
length = -EINVAL;
if (sscanf(page, "%d", &new_value) != 1)
@@ -186,7 +179,7 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
}
length = count;
out:
- free_page((unsigned long) page);
+ kfree(page);
return length;
}
#else
@@ -275,27 +268,20 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- char *page = NULL;
+ char *page;
ssize_t length;
int new_value;
- length = -ENOMEM;
if (count >= PAGE_SIZE)
- goto out;
+ return -ENOMEM;
/* No partial writes. */
- length = -EINVAL;
if (*ppos != 0)
- goto out;
-
- length = -ENOMEM;
- page = (char *)get_zeroed_page(GFP_KERNEL);
- if (!page)
- goto out;
+ return -EINVAL;
- length = -EFAULT;
- if (copy_from_user(page, buf, count))
- goto out;
+ page = memdup_user_nul(buf, count);
+ if (IS_ERR(page))
+ return PTR_ERR(page);
length = -EINVAL;
if (sscanf(page, "%d", &new_value) != 1)
@@ -313,7 +299,7 @@ static ssize_t sel_write_disable(struct file *file, const char __user *buf,
length = count;
out:
- free_page((unsigned long) page);
+ kfree(page);
return length;
}
#else
@@ -611,31 +597,24 @@ static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf,
static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- char *page = NULL;
+ char *page;
ssize_t length;
unsigned int new_value;
length = task_has_security(current, SECURITY__SETCHECKREQPROT);
if (length)
- goto out;
+ return length;
- length = -ENOMEM;
if (count >= PAGE_SIZE)
- goto out;
+ return -ENOMEM;
/* No partial writes. */
- length = -EINVAL;
if (*ppos != 0)
- goto out;
-
- length = -ENOMEM;
- page = (char *)get_zeroed_page(GFP_KERNEL);
- if (!page)
- goto out;
+ return -EINVAL;
- length = -EFAULT;
- if (copy_from_user(page, buf, count))
- goto out;
+ page = memdup_user_nul(buf, count);
+ if (IS_ERR(page))
+ return PTR_ERR(page);
length = -EINVAL;
if (sscanf(page, "%u", &new_value) != 1)
@@ -644,7 +623,7 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf,
selinux_checkreqprot = new_value ? 1 : 0;
length = count;
out:
- free_page((unsigned long) page);
+ kfree(page);
return length;
}
static const struct file_operations sel_checkreqprot_ops = {
@@ -1100,14 +1079,12 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
if (*ppos != 0)
goto out;
- length = -ENOMEM;
- page = (char *)get_zeroed_page(GFP_KERNEL);
- if (!page)
- goto out;
-
- length = -EFAULT;
- if (copy_from_user(page, buf, count))
+ page = memdup_user_nul(buf, count);
+ if (IS_ERR(page)) {
+ length = PTR_ERR(page);
+ page = NULL;
goto out;
+ }
length = -EINVAL;
if (sscanf(page, "%d", &new_value) != 1)
@@ -1121,7 +1098,7 @@ static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
out:
mutex_unlock(&sel_mutex);
- free_page((unsigned long) page);
+ kfree(page);
return length;
}
@@ -1154,14 +1131,12 @@ static ssize_t sel_commit_bools_write(struct file *filep,
if (*ppos != 0)
goto out;
- length = -ENOMEM;
- page = (char *)get_zeroed_page(GFP_KERNEL);
- if (!page)
- goto out;
-
- length = -EFAULT;
- if (copy_from_user(page, buf, count))
+ page = memdup_user_nul(buf, count);
+ if (IS_ERR(page)) {
+ length = PTR_ERR(page);
+ page = NULL;
goto out;
+ }
length = -EINVAL;
if (sscanf(page, "%d", &new_value) != 1)
@@ -1176,7 +1151,7 @@ static ssize_t sel_commit_bools_write(struct file *filep,
out:
mutex_unlock(&sel_mutex);
- free_page((unsigned long) page);
+ kfree(page);
return length;
}
@@ -1292,31 +1267,24 @@ static ssize_t sel_write_avc_cache_threshold(struct file *file,
size_t count, loff_t *ppos)
{
- char *page = NULL;
+ char *page;
ssize_t ret;
int new_value;
ret = task_has_security(current, SECURITY__SETSECPARAM);
if (ret)
- goto out;
+ return ret;
- ret = -ENOMEM;
if (count >= PAGE_SIZE)
- goto out;
+ return -ENOMEM;
/* No partial writes. */
- ret = -EINVAL;
if (*ppos != 0)
- goto out;
-
- ret = -ENOMEM;
- page = (char *)get_zeroed_page(GFP_KERNEL);
- if (!page)
- goto out;
+ return -EINVAL;
- ret = -EFAULT;
- if (copy_from_user(page, buf, count))
- goto out;
+ page = memdup_user_nul(buf, count);
+ if (IS_ERR(page))
+ return PTR_ERR(page);
ret = -EINVAL;
if (sscanf(page, "%u", &new_value) != 1)
@@ -1326,7 +1294,7 @@ static ssize_t sel_write_avc_cache_threshold(struct file *file,
ret = count;
out:
- free_page((unsigned long)page);
+ kfree(page);
return ret;
}
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c
index 18643bf9894d..456e1a9bcfde 100644
--- a/security/selinux/ss/conditional.c
+++ b/security/selinux/ss/conditional.c
@@ -638,7 +638,7 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key,
{
struct avtab_node *node;
- if (!ctab || !key || !avd || !xperms)
+ if (!ctab || !key || !avd)
return;
for (node = avtab_search_node(ctab, key); node;
@@ -657,7 +657,7 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key,
if ((u16)(AVTAB_AUDITALLOW|AVTAB_ENABLED) ==
(node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED)))
avd->auditallow |= node->datum.u.data;
- if ((node->key.specified & AVTAB_ENABLED) &&
+ if (xperms && (node->key.specified & AVTAB_ENABLED) &&
(node->key.specified & AVTAB_XPERMS))
services_compute_xperms_drivers(xperms, node);
}
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index ff81026f6ddb..37fdd5416a64 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1519,8 +1519,6 @@ static int smack_inode_getsecurity(const struct inode *inode,
* @inode: the object
* @buffer: where they go
* @buffer_size: size of buffer
- *
- * Returns 0 on success, -EINVAL otherwise
*/
static int smack_inode_listsecurity(struct inode *inode, char *buffer,
size_t buffer_size)
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index 94bd9e41c9ec..e249a66db533 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -497,14 +497,9 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf,
}
}
- data = kmalloc(count + 1, GFP_KERNEL);
- if (data == NULL)
- return -ENOMEM;
-
- if (copy_from_user(data, buf, count) != 0) {
- rc = -EFAULT;
- goto out;
- }
+ data = memdup_user_nul(buf, count);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
/*
* In case of parsing only part of user buf,
@@ -884,16 +879,10 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf,
(count < SMK_CIPSOMIN || count > SMK_CIPSOMAX))
return -EINVAL;
- data = kzalloc(count + 1, GFP_KERNEL);
- if (data == NULL)
- return -ENOMEM;
-
- if (copy_from_user(data, buf, count) != 0) {
- rc = -EFAULT;
- goto unlockedout;
- }
+ data = memdup_user_nul(buf, count);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
- data[count] = '\0';
rule = data;
/*
* Only allow one writer at a time. Writes should be
@@ -946,7 +935,6 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf,
out:
mutex_unlock(&smack_cipso_lock);
-unlockedout:
kfree(data);
return rc;
}
@@ -1187,14 +1175,9 @@ static ssize_t smk_write_net4addr(struct file *file, const char __user *buf,
if (count < SMK_NETLBLADDRMIN)
return -EINVAL;
- data = kzalloc(count + 1, GFP_KERNEL);
- if (data == NULL)
- return -ENOMEM;
-
- if (copy_from_user(data, buf, count) != 0) {
- rc = -EFAULT;
- goto free_data_out;
- }
+ data = memdup_user_nul(buf, count);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
smack = kzalloc(count + 1, GFP_KERNEL);
if (smack == NULL) {
@@ -1202,8 +1185,6 @@ static ssize_t smk_write_net4addr(struct file *file, const char __user *buf,
goto free_data_out;
}
- data[count] = '\0';
-
rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd/%u %s",
&host[0], &host[1], &host[2], &host[3], &masks, smack);
if (rc != 6) {
@@ -1454,14 +1435,9 @@ static ssize_t smk_write_net6addr(struct file *file, const char __user *buf,
if (count < SMK_NETLBLADDRMIN)
return -EINVAL;
- data = kzalloc(count + 1, GFP_KERNEL);
- if (data == NULL)
- return -ENOMEM;
-
- if (copy_from_user(data, buf, count) != 0) {
- rc = -EFAULT;
- goto free_data_out;
- }
+ data = memdup_user_nul(buf, count);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
smack = kzalloc(count + 1, GFP_KERNEL);
if (smack == NULL) {
@@ -1469,8 +1445,6 @@ static ssize_t smk_write_net6addr(struct file *file, const char __user *buf,
goto free_data_out;
}
- data[count] = '\0';
-
i = sscanf(data, "%x:%x:%x:%x:%x:%x:%x:%x/%u %s",
&scanned[0], &scanned[1], &scanned[2], &scanned[3],
&scanned[4], &scanned[5], &scanned[6], &scanned[7],
@@ -1865,14 +1839,9 @@ static ssize_t smk_write_ambient(struct file *file, const char __user *buf,
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
- data = kzalloc(count + 1, GFP_KERNEL);
- if (data == NULL)
- return -ENOMEM;
-
- if (copy_from_user(data, buf, count) != 0) {
- rc = -EFAULT;
- goto out;
- }
+ data = memdup_user_nul(buf, count);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
skp = smk_import_entry(data, count);
if (IS_ERR(skp)) {
@@ -2041,14 +2010,9 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
- data = kzalloc(count + 1, GFP_KERNEL);
- if (data == NULL)
- return -ENOMEM;
-
- if (copy_from_user(data, buf, count) != 0) {
- kfree(data);
- return -EFAULT;
- }
+ data = memdup_user_nul(buf, count);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
rc = smk_parse_label_list(data, &list_tmp);
kfree(data);
@@ -2133,14 +2097,9 @@ static ssize_t smk_write_unconfined(struct file *file, const char __user *buf,
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
- data = kzalloc(count + 1, GFP_KERNEL);
- if (data == NULL)
- return -ENOMEM;
-
- if (copy_from_user(data, buf, count) != 0) {
- rc = -EFAULT;
- goto freeout;
- }
+ data = memdup_user_nul(buf, count);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
/*
* Clear the smack_unconfined on invalid label errors. This means
@@ -2696,19 +2655,15 @@ static ssize_t smk_write_syslog(struct file *file, const char __user *buf,
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
- data = kzalloc(count + 1, GFP_KERNEL);
- if (data == NULL)
- return -ENOMEM;
+ data = memdup_user_nul(buf, count);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
- if (copy_from_user(data, buf, count) != 0)
- rc = -EFAULT;
- else {
- skp = smk_import_entry(data, count);
- if (IS_ERR(skp))
- rc = PTR_ERR(skp);
- else
- smack_syslog_label = skp;
- }
+ skp = smk_import_entry(data, count);
+ if (IS_ERR(skp))
+ rc = PTR_ERR(skp);
+ else
+ smack_syslog_label = skp;
kfree(data);
return rc;
@@ -2798,14 +2753,9 @@ static ssize_t smk_write_relabel_self(struct file *file, const char __user *buf,
if (*ppos != 0)
return -EINVAL;
- data = kzalloc(count + 1, GFP_KERNEL);
- if (data == NULL)
- return -ENOMEM;
-
- if (copy_from_user(data, buf, count) != 0) {
- kfree(data);
- return -EFAULT;
- }
+ data = memdup_user_nul(buf, count);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
rc = smk_parse_label_list(data, &list_tmp);
kfree(data);
diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c
index 179a955b319d..06ab41b1ff28 100644
--- a/security/tomoyo/securityfs_if.c
+++ b/security/tomoyo/securityfs_if.c
@@ -43,13 +43,9 @@ static ssize_t tomoyo_write_self(struct file *file, const char __user *buf,
int error;
if (!count || count >= TOMOYO_EXEC_TMPSIZE - 10)
return -ENOMEM;
- data = kzalloc(count + 1, GFP_NOFS);
- if (!data)
- return -ENOMEM;
- if (copy_from_user(data, buf, count)) {
- error = -EFAULT;
- goto out;
- }
+ data = memdup_user_nul(buf, count);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
tomoyo_normalize_line(data);
if (tomoyo_correct_domain(data)) {
const int idx = tomoyo_read_lock();
@@ -87,7 +83,6 @@ static ssize_t tomoyo_write_self(struct file *file, const char __user *buf,
tomoyo_read_unlock(idx);
} else
error = -EINVAL;
-out:
kfree(data);
return error ? error : count;
}
diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c
index fba365a78390..697c166acf05 100644
--- a/sound/core/pcm_dmaengine.c
+++ b/sound/core/pcm_dmaengine.c
@@ -202,13 +202,13 @@ int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
if (runtime->info & SNDRV_PCM_INFO_PAUSE)
dmaengine_pause(prtd->dma_chan);
else
- dmaengine_terminate_all(prtd->dma_chan);
+ dmaengine_terminate_async(prtd->dma_chan);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
dmaengine_pause(prtd->dma_chan);
break;
case SNDRV_PCM_TRIGGER_STOP:
- dmaengine_terminate_all(prtd->dma_chan);
+ dmaengine_terminate_async(prtd->dma_chan);
break;
default:
return -EINVAL;
@@ -346,6 +346,7 @@ int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream)
{
struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+ dmaengine_synchronize(prtd->dma_chan);
kfree(prtd);
return 0;
@@ -362,9 +363,11 @@ int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream)
{
struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+ dmaengine_synchronize(prtd->dma_chan);
dma_release_channel(prtd->dma_chan);
+ kfree(prtd);
- return snd_dmaengine_pcm_close(substream);
+ return 0;
}
EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan);
diff --git a/sound/drivers/pcm-indirect2.c b/sound/drivers/pcm-indirect2.c
index e73fafd761b3..d16bc14a0f0e 100644
--- a/sound/drivers/pcm-indirect2.c
+++ b/sound/drivers/pcm-indirect2.c
@@ -47,7 +47,7 @@ void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream,
int seconds = (rec->lastbytetime - rec->firstbytetime) / HZ;
snd_printk(KERN_DEBUG "STAT: mul_elapsed: %u, mul_elapsed_real: %d, "
- "irq_occured: %d\n",
+ "irq_occurred: %d\n",
rec->mul_elapsed, rec->mul_elapsed_real, rec->irq_occured);
snd_printk(KERN_DEBUG "STAT: min_multiple: %d (irqs/period)\n",
rec->min_multiple);
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index 5d99436dfcae..0cda05c72f50 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -12,9 +12,11 @@ MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_LICENSE("GPL v2");
#define OUI_WEISS 0x001c6a
+#define OUI_LOUD 0x000ff2
#define DICE_CATEGORY_ID 0x04
#define WEISS_CATEGORY_ID 0x00
+#define LOUD_CATEGORY_ID 0x10
static int dice_interface_check(struct fw_unit *unit)
{
@@ -57,6 +59,8 @@ static int dice_interface_check(struct fw_unit *unit)
}
if (vendor == OUI_WEISS)
category = WEISS_CATEGORY_ID;
+ else if (vendor == OUI_LOUD)
+ category = LOUD_CATEGORY_ID;
else
category = DICE_CATEGORY_ID;
if (device->config_rom[3] != ((vendor << 8) | category) ||
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index e0d9363dc7fd..514f2604086e 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -113,7 +113,7 @@
#include <sound/initval.h>
#ifdef CONFIG_SND_ES1968_RADIO
-#include <media/tea575x.h>
+#include <media/drv-intf/tea575x.h>
#endif
#define CARD_NAME "ESS Maestro1/2"
@@ -2605,7 +2605,7 @@ static void snd_es1968_tea575x_set_direction(struct snd_tea575x *tea, bool outpu
}
}
-static struct snd_tea575x_ops snd_es1968_tea_ops = {
+static const struct snd_tea575x_ops snd_es1968_tea_ops = {
.set_pins = snd_es1968_tea575x_set_pins,
.get_pins = snd_es1968_tea575x_get_pins,
.set_direction = snd_es1968_tea575x_set_direction,
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index 1fdd92b6f18f..759295aa8366 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -30,7 +30,7 @@
#include <sound/initval.h>
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
-#include <media/tea575x.h>
+#include <media/drv-intf/tea575x.h>
#endif
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -815,7 +815,7 @@ static void snd_fm801_tea575x_set_direction(struct snd_tea575x *tea, bool output
fm801_writew(chip, GPIO_CTRL, reg);
}
-static struct snd_tea575x_ops snd_fm801_tea_ops = {
+static const struct snd_tea575x_ops snd_fm801_tea_ops = {
.set_pins = snd_fm801_tea575x_set_pins,
.get_pins = snd_fm801_tea575x_get_pins,
.set_direction = snd_fm801_tea575x_set_direction,
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 8a7fbdcb4072..3b3658297070 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -312,6 +312,10 @@ enum {
(AZX_DCAPS_INTEL_PCH | AZX_DCAPS_SEPARATE_STREAM_TAG |\
AZX_DCAPS_I915_POWERWELL)
+#define AZX_DCAPS_INTEL_BROXTON \
+ (AZX_DCAPS_INTEL_PCH | AZX_DCAPS_SEPARATE_STREAM_TAG |\
+ AZX_DCAPS_I915_POWERWELL)
+
/* quirks for ATI SB / AMD Hudson */
#define AZX_DCAPS_PRESET_ATI_SB \
(AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB |\
@@ -351,6 +355,8 @@ enum {
((pci)->device == 0x0d0c) || \
((pci)->device == 0x160c))
+#define IS_BROXTON(pci) ((pci)->device == 0x5a98)
+
static char *driver_short_names[] = {
[AZX_DRIVER_ICH] = "HDA Intel",
[AZX_DRIVER_PCH] = "HDA Intel PCH",
@@ -502,15 +508,36 @@ static void azx_init_pci(struct azx *chip)
}
}
+/*
+ * In BXT-P A0, HD-Audio DMA requests is later than expected,
+ * and makes an audio stream sensitive to system latencies when
+ * 24/32 bits are playing.
+ * Adjusting threshold of DMA fifo to force the DMA request
+ * sooner to improve latency tolerance at the expense of power.
+ */
+static void bxt_reduce_dma_latency(struct azx *chip)
+{
+ u32 val;
+
+ val = azx_readl(chip, SKL_EM4L);
+ val &= (0x3 << 20);
+ azx_writel(chip, SKL_EM4L, val);
+}
+
static void hda_intel_init_chip(struct azx *chip, bool full_reset)
{
struct hdac_bus *bus = azx_bus(chip);
+ struct pci_dev *pci = chip->pci;
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
snd_hdac_set_codec_wakeup(bus, true);
azx_init_chip(chip, full_reset);
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
snd_hdac_set_codec_wakeup(bus, false);
+
+ /* reduce dma latency to avoid noise */
+ if (IS_BROXTON(pci))
+ bxt_reduce_dma_latency(chip);
}
/* calculate runtime delay from LPIB */
@@ -927,6 +954,36 @@ static int azx_resume(struct device *dev)
}
#endif /* CONFIG_PM_SLEEP || SUPPORT_VGA_SWITCHEROO */
+#ifdef CONFIG_PM_SLEEP
+/* put codec down to D3 at hibernation for Intel SKL+;
+ * otherwise BIOS may still access the codec and screw up the driver
+ */
+#define IS_SKL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa170)
+#define IS_SKL_LP(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x9d70)
+#define IS_BXT(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x5a98)
+#define IS_SKL_PLUS(pci) (IS_SKL(pci) || IS_SKL_LP(pci) || IS_BXT(pci))
+
+static int azx_freeze_noirq(struct device *dev)
+{
+ struct pci_dev *pci = to_pci_dev(dev);
+
+ if (IS_SKL_PLUS(pci))
+ pci_set_power_state(pci, PCI_D3hot);
+
+ return 0;
+}
+
+static int azx_thaw_noirq(struct device *dev)
+{
+ struct pci_dev *pci = to_pci_dev(dev);
+
+ if (IS_SKL_PLUS(pci))
+ pci_set_power_state(pci, PCI_D0);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
#ifdef CONFIG_PM
static int azx_runtime_suspend(struct device *dev)
{
@@ -1036,6 +1093,10 @@ static int azx_runtime_idle(struct device *dev)
static const struct dev_pm_ops azx_pm = {
SET_SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume)
+#ifdef CONFIG_PM_SLEEP
+ .freeze_noirq = azx_freeze_noirq,
+ .thaw_noirq = azx_thaw_noirq,
+#endif
SET_RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, azx_runtime_idle)
};
@@ -2124,6 +2185,9 @@ static const struct pci_device_id azx_ids[] = {
/* Sunrise Point-LP */
{ PCI_DEVICE(0x8086, 0x9d70),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE },
+ /* Broxton-P(Apollolake) */
+ { PCI_DEVICE(0x8086, 0x5a98),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BROXTON },
/* Haswell */
{ PCI_DEVICE(0x8086, 0x0a0c),
.driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index f8a12ca477f1..4ef2259f88ca 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -778,7 +778,8 @@ static const struct hda_pintbl alienware_pincfgs[] = {
};
static const struct snd_pci_quirk ca0132_quirks[] = {
- SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15", QUIRK_ALIENWARE),
+ SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15 2015", QUIRK_ALIENWARE),
+ SND_PCI_QUIRK(0x1028, 0x0688, "Alienware 17 2015", QUIRK_ALIENWARE),
{}
};
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index c8b8ef5246a6..ef198903c0c3 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -955,6 +955,7 @@ static int patch_conexant_auto(struct hda_codec *codec)
*/
static const struct hda_device_id snd_hda_id_conexant[] = {
+ HDA_CODEC_ENTRY(0x14f12008, "CX8200", patch_conexant_auto),
HDA_CODEC_ENTRY(0x14f15045, "CX20549 (Venice)", patch_conexant_auto),
HDA_CODEC_ENTRY(0x14f15047, "CX20551 (Waikiki)", patch_conexant_auto),
HDA_CODEC_ENTRY(0x14f15051, "CX20561 (Hermosa)", patch_conexant_auto),
@@ -972,9 +973,9 @@ static const struct hda_device_id snd_hda_id_conexant[] = {
HDA_CODEC_ENTRY(0x14f150ac, "CX20652", patch_conexant_auto),
HDA_CODEC_ENTRY(0x14f150b8, "CX20664", patch_conexant_auto),
HDA_CODEC_ENTRY(0x14f150b9, "CX20665", patch_conexant_auto),
- HDA_CODEC_ENTRY(0x14f150f1, "CX20721", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150f1, "CX21722", patch_conexant_auto),
HDA_CODEC_ENTRY(0x14f150f2, "CX20722", patch_conexant_auto),
- HDA_CODEC_ENTRY(0x14f150f3, "CX20723", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f150f3, "CX21724", patch_conexant_auto),
HDA_CODEC_ENTRY(0x14f150f4, "CX20724", patch_conexant_auto),
HDA_CODEC_ENTRY(0x14f1510f, "CX20751/2", patch_conexant_auto),
HDA_CODEC_ENTRY(0x14f15110, "CX20751/2", patch_conexant_auto),
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 60cd9e700909..4b6fb668c91c 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -2352,6 +2352,12 @@ static void intel_pin_eld_notify(void *audio_ptr, int port)
struct hda_codec *codec = audio_ptr;
int pin_nid = port + 0x04;
+ /* skip notification during system suspend (but not in runtime PM);
+ * the state will be updated at resume
+ */
+ if (snd_power_get_state(codec->card) != SNDRV_CTL_POWER_D0)
+ return;
+
check_presence_and_report(codec, pin_nid);
}
@@ -2378,7 +2384,8 @@ static int patch_generic_hdmi(struct hda_codec *codec)
* can cover the codec power request, and so need not set this flag.
* For previous platforms, there is no such power well feature.
*/
- if (is_valleyview_plus(codec) || is_skylake(codec))
+ if (is_valleyview_plus(codec) || is_skylake(codec) ||
+ is_broxton(codec))
codec->core.link_power_control = 1;
if (is_haswell_plus(codec) || is_valleyview_plus(codec)) {
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 2f7b065f9ac4..3a89d82f8057 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -67,6 +67,10 @@ enum {
ALC_HEADSET_TYPE_OMTP,
};
+enum {
+ ALC_KEY_MICMUTE_INDEX,
+};
+
struct alc_customize_define {
unsigned int sku_cfg;
unsigned char port_connectivity;
@@ -111,6 +115,7 @@ struct alc_spec {
void (*power_hook)(struct hda_codec *codec);
#endif
void (*shutup)(struct hda_codec *codec);
+ void (*reboot_notify)(struct hda_codec *codec);
int init_amp;
int codec_variant; /* flag for other variants */
@@ -122,6 +127,7 @@ struct alc_spec {
unsigned int pll_coef_idx, pll_coef_bit;
unsigned int coef0;
struct input_dev *kb_dev;
+ u8 alc_mute_keycode_map[1];
};
/*
@@ -773,6 +779,25 @@ static inline void alc_shutup(struct hda_codec *codec)
snd_hda_shutup_pins(codec);
}
+static void alc_reboot_notify(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec && spec->reboot_notify)
+ spec->reboot_notify(codec);
+ else
+ alc_shutup(codec);
+}
+
+/* power down codec to D3 at reboot/shutdown; set as reboot_notify ops */
+static void alc_d3_at_reboot(struct hda_codec *codec)
+{
+ snd_hda_codec_set_power_to_all(codec, codec->core.afg, AC_PWRST_D3);
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ msleep(10);
+}
+
#define alc_free snd_hda_gen_free
#ifdef CONFIG_PM
@@ -818,7 +843,7 @@ static const struct hda_codec_ops alc_patch_ops = {
.suspend = alc_suspend,
.check_power_status = snd_hda_gen_check_power_status,
#endif
- .reboot_notify = alc_shutup,
+ .reboot_notify = alc_reboot_notify,
};
@@ -1755,10 +1780,12 @@ enum {
ALC889_FIXUP_MBA11_VREF,
ALC889_FIXUP_MBA21_VREF,
ALC889_FIXUP_MP11_VREF,
+ ALC889_FIXUP_MP41_VREF,
ALC882_FIXUP_INV_DMIC,
ALC882_FIXUP_NO_PRIMARY_HP,
ALC887_FIXUP_ASUS_BASS,
ALC887_FIXUP_BASS_CHMAP,
+ ALC882_FIXUP_DISABLE_AAMIX,
};
static void alc889_fixup_coef(struct hda_codec *codec,
@@ -1842,7 +1869,7 @@ static void alc889_fixup_mbp_vref(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- static hda_nid_t nids[2] = { 0x14, 0x15 };
+ static hda_nid_t nids[3] = { 0x14, 0x15, 0x19 };
int i;
if (action != HDA_FIXUP_ACT_INIT)
@@ -1920,6 +1947,8 @@ static void alc882_fixup_no_primary_hp(struct hda_codec *codec,
static void alc_fixup_bass_chmap(struct hda_codec *codec,
const struct hda_fixup *fix, int action);
+static void alc_fixup_disable_aamix(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
static const struct hda_fixup alc882_fixups[] = {
[ALC882_FIXUP_ABIT_AW9D_MAX] = {
@@ -2130,6 +2159,12 @@ static const struct hda_fixup alc882_fixups[] = {
.chained = true,
.chain_id = ALC885_FIXUP_MACPRO_GPIO,
},
+ [ALC889_FIXUP_MP41_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_mbp_vref,
+ .chained = true,
+ .chain_id = ALC885_FIXUP_MACPRO_GPIO,
+ },
[ALC882_FIXUP_INV_DMIC] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_inv_dmic,
@@ -2151,6 +2186,10 @@ static const struct hda_fixup alc882_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_bass_chmap,
},
+ [ALC882_FIXUP_DISABLE_AAMIX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ },
};
static const struct snd_pci_quirk alc882_fixup_tbl[] = {
@@ -2208,7 +2247,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC889_FIXUP_IMAC91_VREF),
SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC889_FIXUP_IMAC91_VREF),
SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC889_FIXUP_IMAC91_VREF),
- SND_PCI_QUIRK(0x106b, 0x4200, "Mac Pro 5,1", ALC885_FIXUP_MACPRO_GPIO),
+ SND_PCI_QUIRK(0x106b, 0x4200, "Mac Pro 4,1/5,1", ALC889_FIXUP_MP41_VREF),
SND_PCI_QUIRK(0x106b, 0x4300, "iMac 9,1", ALC889_FIXUP_IMAC91_VREF),
SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC889_FIXUP_IMAC91_VREF),
SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC889_FIXUP_IMAC91_VREF),
@@ -2218,6 +2257,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD),
SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3),
SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1458, 0xa182, "Gigabyte Z170X-UD3", ALC882_FIXUP_DISABLE_AAMIX),
SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX),
SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD),
SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD),
@@ -3427,12 +3467,43 @@ static void gpio2_mic_hotkey_event(struct hda_codec *codec,
/* GPIO2 just toggles on a keypress/keyrelease cycle. Therefore
send both key on and key off event for every interrupt. */
- input_report_key(spec->kb_dev, KEY_MICMUTE, 1);
+ input_report_key(spec->kb_dev, spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX], 1);
input_sync(spec->kb_dev);
- input_report_key(spec->kb_dev, KEY_MICMUTE, 0);
+ input_report_key(spec->kb_dev, spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX], 0);
input_sync(spec->kb_dev);
}
+static int alc_register_micmute_input_device(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ spec->kb_dev = input_allocate_device();
+ if (!spec->kb_dev) {
+ codec_err(codec, "Out of memory (input_allocate_device)\n");
+ return -ENOMEM;
+ }
+
+ spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX] = KEY_MICMUTE;
+
+ spec->kb_dev->name = "Microphone Mute Button";
+ spec->kb_dev->evbit[0] = BIT_MASK(EV_KEY);
+ spec->kb_dev->keycodesize = sizeof(spec->alc_mute_keycode_map[0]);
+ spec->kb_dev->keycodemax = ARRAY_SIZE(spec->alc_mute_keycode_map);
+ spec->kb_dev->keycode = spec->alc_mute_keycode_map;
+ for (i = 0; i < ARRAY_SIZE(spec->alc_mute_keycode_map); i++)
+ set_bit(spec->alc_mute_keycode_map[i], spec->kb_dev->keybit);
+
+ if (input_register_device(spec->kb_dev)) {
+ codec_err(codec, "input_register_device failed\n");
+ input_free_device(spec->kb_dev);
+ spec->kb_dev = NULL;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -3450,20 +3521,8 @@ static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec,
struct alc_spec *spec = codec->spec;
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->kb_dev = input_allocate_device();
- if (!spec->kb_dev) {
- codec_err(codec, "Out of memory (input_allocate_device)\n");
- return;
- }
- spec->kb_dev->name = "Microphone Mute Button";
- spec->kb_dev->evbit[0] = BIT_MASK(EV_KEY);
- spec->kb_dev->keybit[BIT_WORD(KEY_MICMUTE)] = BIT_MASK(KEY_MICMUTE);
- if (input_register_device(spec->kb_dev)) {
- codec_err(codec, "input_register_device failed\n");
- input_free_device(spec->kb_dev);
- spec->kb_dev = NULL;
+ if (alc_register_micmute_input_device(codec) != 0)
return;
- }
snd_hda_add_verbs(codec, gpio_init);
snd_hda_codec_write_cache(codec, codec->core.afg, 0,
@@ -3493,6 +3552,47 @@ static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec,
}
}
+static void alc233_fixup_lenovo_line2_mic_hotkey(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /* Line2 = mic mute hotkey
+ GPIO2 = mic mute LED */
+ static const struct hda_verb gpio_init[] = {
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x04 },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04 },
+ {}
+ };
+
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ if (alc_register_micmute_input_device(codec) != 0)
+ return;
+
+ snd_hda_add_verbs(codec, gpio_init);
+ snd_hda_jack_detect_enable_callback(codec, 0x1b,
+ gpio2_mic_hotkey_event);
+
+ spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook;
+ spec->gpio_led = 0;
+ spec->mute_led_polarity = 0;
+ spec->gpio_mic_led_mask = 0x04;
+ return;
+ }
+
+ if (!spec->kb_dev)
+ return;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PROBE:
+ spec->init_amp = ALC_INIT_DEFAULT;
+ break;
+ case HDA_FIXUP_ACT_FREE:
+ input_unregister_device(spec->kb_dev);
+ spec->kb_dev = NULL;
+ }
+}
+
static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -4190,6 +4290,8 @@ static void alc_fixup_tpt440_dock(struct hda_codec *codec,
struct alc_spec *spec = codec->spec;
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->shutup = alc_no_shutup; /* reduce click noise */
+ spec->reboot_notify = alc_d3_at_reboot; /* reduce noise */
spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
codec->power_save_node = 0; /* avoid click noises */
snd_hda_apply_pincfgs(codec, pincfgs);
@@ -4570,6 +4672,7 @@ enum {
ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC,
ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC292_FIXUP_TPT440_DOCK,
+ ALC292_FIXUP_TPT440,
ALC283_FIXUP_BXBT2807_MIC,
ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED,
ALC282_FIXUP_ASPIRE_V5_PINS,
@@ -4585,8 +4688,12 @@ enum {
ALC288_FIXUP_DISABLE_AAMIX,
ALC292_FIXUP_DELL_E7X,
ALC292_FIXUP_DISABLE_AAMIX,
+ ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK,
ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC275_FIXUP_DELL_XPS,
+ ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE,
+ ALC293_FIXUP_LENOVO_SPK_NOISE,
+ ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY,
};
static const struct hda_fixup alc269_fixups[] = {
@@ -5041,6 +5148,12 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
},
+ [ALC292_FIXUP_TPT440] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC292_FIXUP_TPT440_DOCK,
+ },
[ALC283_FIXUP_BXBT2807_MIC] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
@@ -5140,6 +5253,12 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE
},
+ [ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC293_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
[ALC292_FIXUP_DELL_E7X] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_dell_xps13,
@@ -5167,6 +5286,27 @@ static const struct hda_fixup alc269_fixups[] = {
{}
}
},
+ [ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Disable pass-through path for FRONT 14h */
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x36},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x1737},
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC293_FIXUP_LENOVO_SPK_NOISE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI
+ },
+ [ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc233_fixup_lenovo_line2_mic_hotkey,
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -5180,8 +5320,10 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK),
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(0x1025, 0x106d, "Acer Cloudbook 14", ALC283_FIXUP_CHROME_BOOK),
SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS),
+ SND_PCI_QUIRK(0x1028, 0x05bd, "Dell Latitude E6440", ALC292_FIXUP_DELL_E7X),
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),
@@ -5199,11 +5341,12 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
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),
- SND_PCI_QUIRK(0x1028, 0x06db, "Dell", ALC292_FIXUP_DISABLE_AAMIX),
- SND_PCI_QUIRK(0x1028, 0x06dd, "Dell", ALC292_FIXUP_DISABLE_AAMIX),
- SND_PCI_QUIRK(0x1028, 0x06de, "Dell", ALC292_FIXUP_DISABLE_AAMIX),
- SND_PCI_QUIRK(0x1028, 0x06df, "Dell", ALC292_FIXUP_DISABLE_AAMIX),
- SND_PCI_QUIRK(0x1028, 0x06e0, "Dell", ALC292_FIXUP_DISABLE_AAMIX),
+ SND_PCI_QUIRK(0x1028, 0x06db, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
+ SND_PCI_QUIRK(0x1028, 0x06dd, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
+ SND_PCI_QUIRK(0x1028, 0x06de, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
+ SND_PCI_QUIRK(0x1028, 0x06df, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
+ SND_PCI_QUIRK(0x1028, 0x06e0, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
+ SND_PCI_QUIRK(0x1028, 0x0704, "Dell XPS 13", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
@@ -5302,15 +5445,18 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK),
- SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad T440s", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad T440s", ALC292_FIXUP_TPT440),
SND_PCI_QUIRK(0x17aa, 0x220e, "Thinkpad T440p", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2210, "Thinkpad T540p", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2211, "Thinkpad W541", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x2218, "Thinkpad X1 Carbon 2nd", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2223, "ThinkPad T550", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE),
+ SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP),
SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
@@ -5320,6 +5466,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x5034, "Thinkpad T450", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x5036, "Thinkpad T450s", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x503c, "Thinkpad L450", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x504b, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE),
SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
@@ -5400,6 +5547,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{.id = ALC283_FIXUP_CHROME_BOOK, .name = "alc283-dac-wcaps"},
{.id = ALC283_FIXUP_SENSE_COMBO_JACK, .name = "alc283-sense-combo"},
{.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"},
+ {.id = ALC292_FIXUP_TPT440, .name = "tpt440"},
{}
};
@@ -6386,6 +6534,7 @@ static const struct hda_fixup alc662_fixups[] = {
static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2),
SND_PCI_QUIRK(0x1025, 0x022f, "Acer Aspire One", ALC662_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x0241, "Packard Bell DOTS", ALC662_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE),
SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC),
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 826122d8acee..2c7c5eb8b1e9 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -3110,6 +3110,29 @@ static void stac92hd71bxx_fixup_hp_hdx(struct hda_codec *codec,
spec->gpio_led = 0x08;
}
+static bool is_hp_output(struct hda_codec *codec, hda_nid_t pin)
+{
+ unsigned int pin_cfg = snd_hda_codec_get_pincfg(codec, pin);
+
+ /* count line-out, too, as BIOS sets often so */
+ return get_defcfg_connect(pin_cfg) != AC_JACK_PORT_NONE &&
+ (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT ||
+ get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT);
+}
+
+static void fixup_hp_headphone(struct hda_codec *codec, hda_nid_t pin)
+{
+ unsigned int pin_cfg = snd_hda_codec_get_pincfg(codec, pin);
+
+ /* It was changed in the BIOS to just satisfy MS DTM.
+ * Lets turn it back into slaved HP
+ */
+ pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE)) |
+ (AC_JACK_HP_OUT << AC_DEFCFG_DEVICE_SHIFT);
+ pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC | AC_DEFCFG_SEQUENCE))) |
+ 0x1f;
+ snd_hda_codec_set_pincfg(codec, pin, pin_cfg);
+}
static void stac92hd71bxx_fixup_hp(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
@@ -3119,22 +3142,12 @@ static void stac92hd71bxx_fixup_hp(struct hda_codec *codec,
if (action != HDA_FIXUP_ACT_PRE_PROBE)
return;
- if (hp_blike_system(codec->core.subsystem_id)) {
- unsigned int pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f);
- if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT ||
- get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER ||
- get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT) {
- /* It was changed in the BIOS to just satisfy MS DTM.
- * Lets turn it back into slaved HP
- */
- pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE))
- | (AC_JACK_HP_OUT <<
- AC_DEFCFG_DEVICE_SHIFT);
- pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC
- | AC_DEFCFG_SEQUENCE)))
- | 0x1f;
- snd_hda_codec_set_pincfg(codec, 0x0f, pin_cfg);
- }
+ /* when both output A and F are assigned, these are supposedly
+ * dock and built-in headphones; fix both pin configs
+ */
+ if (is_hp_output(codec, 0x0a) && is_hp_output(codec, 0x0f)) {
+ fixup_hp_headphone(codec, 0x0a);
+ fixup_hp_headphone(codec, 0x0f);
}
if (find_mute_led_cfg(codec, 1))
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index 714df906249e..41c31db65039 100644
--- a/sound/pci/rme96.c
+++ b/sound/pci/rme96.c
@@ -741,10 +741,11 @@ snd_rme96_playback_setrate(struct rme96 *rme96,
{
/* change to/from double-speed: reset the DAC (if available) */
snd_rme96_reset_dac(rme96);
+ return 1; /* need to restore volume */
} else {
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
+ return 0;
}
- return 0;
}
static int
@@ -980,6 +981,7 @@ snd_rme96_playback_hw_params(struct snd_pcm_substream *substream,
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int err, rate, dummy;
+ bool apply_dac_volume = false;
runtime->dma_area = (void __force *)(rme96->iobase +
RME96_IO_PLAY_BUFFER);
@@ -993,24 +995,26 @@ snd_rme96_playback_hw_params(struct snd_pcm_substream *substream,
{
/* slave clock */
if ((int)params_rate(params) != rate) {
- spin_unlock_irq(&rme96->lock);
- return -EIO;
- }
- } else if ((err = snd_rme96_playback_setrate(rme96, params_rate(params))) < 0) {
- spin_unlock_irq(&rme96->lock);
- return err;
- }
- if ((err = snd_rme96_playback_setformat(rme96, params_format(params))) < 0) {
- spin_unlock_irq(&rme96->lock);
- return err;
+ err = -EIO;
+ goto error;
+ }
+ } else {
+ err = snd_rme96_playback_setrate(rme96, params_rate(params));
+ if (err < 0)
+ goto error;
+ apply_dac_volume = err > 0; /* need to restore volume later? */
}
+
+ err = snd_rme96_playback_setformat(rme96, params_format(params));
+ if (err < 0)
+ goto error;
snd_rme96_setframelog(rme96, params_channels(params), 1);
if (rme96->capture_periodsize != 0) {
if (params_period_size(params) << rme96->playback_frlog !=
rme96->capture_periodsize)
{
- spin_unlock_irq(&rme96->lock);
- return -EBUSY;
+ err = -EBUSY;
+ goto error;
}
}
rme96->playback_periodsize =
@@ -1021,9 +1025,16 @@ snd_rme96_playback_hw_params(struct snd_pcm_substream *substream,
rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP);
writel(rme96->wcreg |= rme96->wcreg_spdif_stream, rme96->iobase + RME96_IO_CONTROL_REGISTER);
}
+
+ err = 0;
+ error:
spin_unlock_irq(&rme96->lock);
-
- return 0;
+ if (apply_dac_volume) {
+ usleep_range(3000, 10000);
+ snd_rme96_apply_dac_volume(rme96);
+ }
+
+ return err;
}
static int
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 9929efc6b9aa..93b400800905 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -1023,24 +1023,18 @@ void arizona_init_dvfs(struct arizona_priv *priv)
}
EXPORT_SYMBOL_GPL(arizona_init_dvfs);
-static unsigned int arizona_sysclk_48k_rates[] = {
+static unsigned int arizona_opclk_ref_48k_rates[] = {
6144000,
12288000,
24576000,
49152000,
- 73728000,
- 98304000,
- 147456000,
};
-static unsigned int arizona_sysclk_44k1_rates[] = {
+static unsigned int arizona_opclk_ref_44k1_rates[] = {
5644800,
11289600,
22579200,
45158400,
- 67737600,
- 90316800,
- 135475200,
};
static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
@@ -1065,11 +1059,11 @@ static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
}
if (refclk % 8000)
- rates = arizona_sysclk_44k1_rates;
+ rates = arizona_opclk_ref_44k1_rates;
else
- rates = arizona_sysclk_48k_rates;
+ rates = arizona_opclk_ref_48k_rates;
- for (ref = 0; ref < ARRAY_SIZE(arizona_sysclk_48k_rates) &&
+ for (ref = 0; ref < ARRAY_SIZE(arizona_opclk_ref_48k_rates) &&
rates[ref] <= refclk; ref++) {
div = 1;
while (rates[ref] / div >= freq && div < 32) {
@@ -1543,7 +1537,7 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
bool reconfig;
unsigned int aif_tx_state, aif_rx_state;
- if (params_rate(params) % 8000)
+ if (params_rate(params) % 4000)
rates = &arizona_44k1_bclk_rates[0];
else
rates = &arizona_48k_bclk_rates[0];
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
index 969e337dc17c..afa6c5db9dcc 100644
--- a/sound/soc/codecs/es8328.c
+++ b/sound/soc/codecs/es8328.c
@@ -85,7 +85,15 @@ static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0);
static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 300, 0);
-static const int deemph_settings[] = { 0, 32000, 44100, 48000 };
+static const struct {
+ int rate;
+ unsigned int val;
+} deemph_settings[] = {
+ { 0, ES8328_DACCONTROL6_DEEMPH_OFF },
+ { 32000, ES8328_DACCONTROL6_DEEMPH_32k },
+ { 44100, ES8328_DACCONTROL6_DEEMPH_44_1k },
+ { 48000, ES8328_DACCONTROL6_DEEMPH_48k },
+};
static int es8328_set_deemph(struct snd_soc_codec *codec)
{
@@ -97,21 +105,22 @@ static int es8328_set_deemph(struct snd_soc_codec *codec)
* rate.
*/
if (es8328->deemph) {
- best = 1;
- for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
- if (abs(deemph_settings[i] - es8328->playback_fs) <
- abs(deemph_settings[best] - es8328->playback_fs))
+ best = 0;
+ for (i = 1; i < ARRAY_SIZE(deemph_settings); i++) {
+ if (abs(deemph_settings[i].rate - es8328->playback_fs) <
+ abs(deemph_settings[best].rate - es8328->playback_fs))
best = i;
}
- val = best << 1;
+ val = deemph_settings[best].val;
} else {
- val = 0;
+ val = ES8328_DACCONTROL6_DEEMPH_OFF;
}
dev_dbg(codec->dev, "Set deemphasis %d\n", val);
- return snd_soc_update_bits(codec, ES8328_DACCONTROL6, 0x6, val);
+ return snd_soc_update_bits(codec, ES8328_DACCONTROL6,
+ ES8328_DACCONTROL6_DEEMPH_MASK, val);
}
static int es8328_get_deemph(struct snd_kcontrol *kcontrol,
@@ -205,18 +214,18 @@ static const struct snd_kcontrol_new es8328_right_line_controls =
/* Left Mixer */
static const struct snd_kcontrol_new es8328_left_mixer_controls[] = {
- SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL17, 8, 1, 0),
- SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 7, 1, 0),
- SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 8, 1, 0),
- SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 7, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL17, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 6, 1, 0),
+ SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 6, 1, 0),
};
/* Right Mixer */
static const struct snd_kcontrol_new es8328_right_mixer_controls[] = {
- SOC_DAPM_SINGLE("Left Playback Switch", ES8328_DACCONTROL19, 8, 1, 0),
- SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 7, 1, 0),
- SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 8, 1, 0),
- SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left Playback Switch", ES8328_DACCONTROL19, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 6, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 6, 1, 0),
};
static const char * const es8328_pga_sel[] = {
diff --git a/sound/soc/codecs/es8328.h b/sound/soc/codecs/es8328.h
index cb36afe10c0e..156c748c89c7 100644
--- a/sound/soc/codecs/es8328.h
+++ b/sound/soc/codecs/es8328.h
@@ -153,6 +153,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
#define ES8328_DACCONTROL6_CLICKFREE (1 << 3)
#define ES8328_DACCONTROL6_DAC_INVR (1 << 4)
#define ES8328_DACCONTROL6_DAC_INVL (1 << 5)
+#define ES8328_DACCONTROL6_DEEMPH_MASK (3 << 6)
#define ES8328_DACCONTROL6_DEEMPH_OFF (0 << 6)
#define ES8328_DACCONTROL6_DEEMPH_32k (1 << 6)
#define ES8328_DACCONTROL6_DEEMPH_44_1k (2 << 6)
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
index 7fc7b4e3f444..c1b87c5800b1 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -1271,6 +1271,36 @@ static int nau8825_i2c_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int nau8825_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct nau8825 *nau8825 = dev_get_drvdata(dev);
+
+ disable_irq(client->irq);
+ regcache_cache_only(nau8825->regmap, true);
+ regcache_mark_dirty(nau8825->regmap);
+
+ return 0;
+}
+
+static int nau8825_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct nau8825 *nau8825 = dev_get_drvdata(dev);
+
+ regcache_cache_only(nau8825->regmap, false);
+ regcache_sync(nau8825->regmap);
+ enable_irq(client->irq);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops nau8825_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(nau8825_suspend, nau8825_resume)
+};
+
static const struct i2c_device_id nau8825_i2c_ids[] = {
{ "nau8825", 0 },
{ }
@@ -1297,6 +1327,7 @@ static struct i2c_driver nau8825_driver = {
.name = "nau8825",
.of_match_table = of_match_ptr(nau8825_of_ids),
.acpi_match_table = ACPI_PTR(nau8825_acpi_match),
+ .pm = &nau8825_pm,
},
.probe = nau8825_i2c_probe,
.remove = nau8825_i2c_remove,
diff --git a/sound/soc/codecs/rl6231.c b/sound/soc/codecs/rl6231.c
index aca479fa7670..1dc68ab08a17 100644
--- a/sound/soc/codecs/rl6231.c
+++ b/sound/soc/codecs/rl6231.c
@@ -80,8 +80,10 @@ int rl6231_calc_dmic_clk(int rate)
}
for (i = 0; i < ARRAY_SIZE(div); i++) {
- /* find divider that gives DMIC frequency below 3MHz */
- if (3000000 * div[i] >= rate)
+ if ((div[i] % 3) == 0)
+ continue;
+ /* find divider that gives DMIC frequency below 3.072MHz */
+ if (3072000 * div[i] >= rate)
return i;
}
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 28132375e427..3e3c7f6be29d 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -245,7 +245,7 @@ struct rt5645_priv {
struct snd_soc_jack *hp_jack;
struct snd_soc_jack *mic_jack;
struct snd_soc_jack *btn_jack;
- struct delayed_work jack_detect_work;
+ struct delayed_work jack_detect_work, rcclock_work;
struct regulator_bulk_data supplies[ARRAY_SIZE(rt5645_supply_names)];
struct rt5645_eq_param_s *eq_param;
@@ -565,12 +565,33 @@ static int rt5645_hweq_put(struct snd_kcontrol *kcontrol,
.put = rt5645_hweq_put \
}
+static int rt5645_spk_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ cancel_delayed_work_sync(&rt5645->rcclock_work);
+
+ regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
+ RT5645_PWR_CLK25M_MASK, RT5645_PWR_CLK25M_PU);
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+ queue_delayed_work(system_power_efficient_wq, &rt5645->rcclock_work,
+ msecs_to_jiffies(200));
+
+ return ret;
+}
+
static const struct snd_kcontrol_new rt5645_snd_controls[] = {
/* Speaker Output Volume */
SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL,
RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1),
- SOC_DOUBLE_TLV("Speaker Playback Volume", RT5645_SPK_VOL,
- RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv),
+ SOC_DOUBLE_EXT_TLV("Speaker Playback Volume", RT5645_SPK_VOL,
+ RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, snd_soc_get_volsw,
+ rt5645_spk_put_volsw, out_vol_tlv),
/* ClassD modulator Speaker Gain Ratio */
SOC_SINGLE_TLV("Speaker ClassD Playback Volume", RT5645_SPO_CLSD_RATIO,
@@ -1498,7 +1519,7 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on)
regmap_write(rt5645->regmap, RT5645_PR_BASE +
RT5645_MAMP_INT_REG2, 0xfc00);
snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140);
- msleep(40);
+ msleep(70);
rt5645->hp_on = true;
} else {
/* depop parameters */
@@ -1646,9 +1667,13 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w,
RT5645_PWR_CLS_D_L,
RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
RT5645_PWR_CLS_D_L);
+ snd_soc_update_bits(codec, RT5645_GEN_CTRL3,
+ RT5645_DET_CLK_MASK, RT5645_DET_CLK_MODE1);
break;
case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, RT5645_GEN_CTRL3,
+ RT5645_DET_CLK_MASK, RT5645_DET_CLK_DIS);
snd_soc_write(codec, RT5645_EQ_CTRL2, 0);
snd_soc_update_bits(codec, RT5645_PWR_DIG1,
RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
@@ -3122,6 +3147,15 @@ static void rt5645_jack_detect_work(struct work_struct *work)
SND_JACK_BTN_2 | SND_JACK_BTN_3);
}
+static void rt5645_rcclock_work(struct work_struct *work)
+{
+ struct rt5645_priv *rt5645 =
+ container_of(work, struct rt5645_priv, rcclock_work.work);
+
+ regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
+ RT5645_PWR_CLK25M_MASK, RT5645_PWR_CLK25M_PD);
+}
+
static irqreturn_t rt5645_irq(int irq, void *data)
{
struct rt5645_priv *rt5645 = data;
@@ -3348,6 +3382,27 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Reks"),
},
},
+ {
+ .ident = "Google Edgar",
+ .callback = strago_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Edgar"),
+ },
+ },
+ {
+ .ident = "Google Wizpig",
+ .callback = strago_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Wizpig"),
+ },
+ },
+ {
+ .ident = "Google Terra",
+ .callback = strago_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Terra"),
+ },
+ },
{ }
};
@@ -3587,6 +3642,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
}
INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work);
+ INIT_DELAYED_WORK(&rt5645->rcclock_work, rt5645_rcclock_work);
if (rt5645->i2c->irq) {
ret = request_threaded_irq(rt5645->i2c->irq, NULL, rt5645_irq,
@@ -3621,6 +3677,7 @@ static int rt5645_i2c_remove(struct i2c_client *i2c)
free_irq(i2c->irq, rt5645);
cancel_delayed_work_sync(&rt5645->jack_detect_work);
+ cancel_delayed_work_sync(&rt5645->rcclock_work);
snd_soc_unregister_codec(&i2c->dev);
regulator_bulk_disable(ARRAY_SIZE(rt5645->supplies), rt5645->supplies);
diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h
index 093e46d559fb..205e0715c99a 100644
--- a/sound/soc/codecs/rt5645.h
+++ b/sound/soc/codecs/rt5645.h
@@ -2122,6 +2122,10 @@ enum {
/* General Control3 (0xfc) */
#define RT5645_JD_PSV_MODE (0x1 << 12)
#define RT5645_IRQ_CLK_GATE_CTRL (0x1 << 11)
+#define RT5645_DET_CLK_MASK (0x3 << 9)
+#define RT5645_DET_CLK_DIS (0x0 << 9)
+#define RT5645_DET_CLK_MODE1 (0x1 << 9)
+#define RT5645_DET_CLK_MODE2 (0x2 << 9)
#define RT5645_MICINDET_MANU (0x1 << 7)
#define RT5645_RING2_SLEEVE_GND (0x1 << 5)
diff --git a/sound/soc/codecs/rt5670.h b/sound/soc/codecs/rt5670.h
index dc2b46236c5c..3f1b0f1df809 100644
--- a/sound/soc/codecs/rt5670.h
+++ b/sound/soc/codecs/rt5670.h
@@ -973,12 +973,12 @@
#define RT5670_SCLK_SRC_MCLK (0x0 << 14)
#define RT5670_SCLK_SRC_PLL1 (0x1 << 14)
#define RT5670_SCLK_SRC_RCCLK (0x2 << 14) /* 15MHz */
-#define RT5670_PLL1_SRC_MASK (0x3 << 12)
-#define RT5670_PLL1_SRC_SFT 12
-#define RT5670_PLL1_SRC_MCLK (0x0 << 12)
-#define RT5670_PLL1_SRC_BCLK1 (0x1 << 12)
-#define RT5670_PLL1_SRC_BCLK2 (0x2 << 12)
-#define RT5670_PLL1_SRC_BCLK3 (0x3 << 12)
+#define RT5670_PLL1_SRC_MASK (0x7 << 11)
+#define RT5670_PLL1_SRC_SFT 11
+#define RT5670_PLL1_SRC_MCLK (0x0 << 11)
+#define RT5670_PLL1_SRC_BCLK1 (0x1 << 11)
+#define RT5670_PLL1_SRC_BCLK2 (0x2 << 11)
+#define RT5670_PLL1_SRC_BCLK3 (0x3 << 11)
#define RT5670_PLL1_PD_MASK (0x1 << 3)
#define RT5670_PLL1_PD_SFT 3
#define RT5670_PLL1_PD_1 (0x0 << 3)
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index b4cd7e3bf5f8..69d987a9935c 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -1386,90 +1386,90 @@ static const struct snd_kcontrol_new rt5677_dac_r_mix[] = {
};
static const struct snd_kcontrol_new rt5677_sto1_dac_l_mix[] = {
- SOC_DAPM_SINGLE("ST L Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("ST L Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_ST_DAC1_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC1_L_STO_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 L Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC2_L_STO_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC1_R_STO_L_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_sto1_dac_r_mix[] = {
- SOC_DAPM_SINGLE("ST R Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("ST R Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_ST_DAC1_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC1_R_STO_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 R Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC2_R_STO_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC1_L_STO_R_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_mono_dac_l_mix[] = {
- SOC_DAPM_SINGLE("ST L Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("ST L Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_ST_DAC2_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 L Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC1_L_MONO_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC2_L_MONO_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC2_R_MONO_L_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_mono_dac_r_mix[] = {
- SOC_DAPM_SINGLE("ST R Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("ST R Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_ST_DAC2_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 R Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC1_R_MONO_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC2_R_MONO_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC2_L_MONO_R_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_dd1_l_mix[] = {
- SOC_DAPM_SINGLE("Sto DAC Mix L Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix L Switch", RT5677_DD1_MIXER,
RT5677_M_STO_L_DD1_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("Mono DAC Mix L Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix L Switch", RT5677_DD1_MIXER,
RT5677_M_MONO_L_DD1_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC3 L Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC3 L Switch", RT5677_DD1_MIXER,
RT5677_M_DAC3_L_DD1_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC3 R Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC3 R Switch", RT5677_DD1_MIXER,
RT5677_M_DAC3_R_DD1_L_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_dd1_r_mix[] = {
- SOC_DAPM_SINGLE("Sto DAC Mix R Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix R Switch", RT5677_DD1_MIXER,
RT5677_M_STO_R_DD1_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("Mono DAC Mix R Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix R Switch", RT5677_DD1_MIXER,
RT5677_M_MONO_R_DD1_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC3 R Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC3 R Switch", RT5677_DD1_MIXER,
RT5677_M_DAC3_R_DD1_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC3 L Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC3 L Switch", RT5677_DD1_MIXER,
RT5677_M_DAC3_L_DD1_R_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_dd2_l_mix[] = {
- SOC_DAPM_SINGLE("Sto DAC Mix L Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix L Switch", RT5677_DD2_MIXER,
RT5677_M_STO_L_DD2_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("Mono DAC Mix L Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix L Switch", RT5677_DD2_MIXER,
RT5677_M_MONO_L_DD2_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC4 L Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC4 L Switch", RT5677_DD2_MIXER,
RT5677_M_DAC4_L_DD2_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC4 R Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC4 R Switch", RT5677_DD2_MIXER,
RT5677_M_DAC4_R_DD2_L_SFT, 1, 1),
};
static const struct snd_kcontrol_new rt5677_dd2_r_mix[] = {
- SOC_DAPM_SINGLE("Sto DAC Mix R Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix R Switch", RT5677_DD2_MIXER,
RT5677_M_STO_R_DD2_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("Mono DAC Mix R Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix R Switch", RT5677_DD2_MIXER,
RT5677_M_MONO_R_DD2_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC4 R Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC4 R Switch", RT5677_DD2_MIXER,
RT5677_M_DAC4_R_DD2_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC4 L Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC4 L Switch", RT5677_DD2_MIXER,
RT5677_M_DAC4_L_DD2_R_SFT, 1, 1),
};
@@ -2596,6 +2596,21 @@ static int rt5677_vref_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static int rt5677_filter_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(50);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("PLL1", RT5677_PWR_ANLG2, RT5677_PWR_PLL1_BIT,
0, rt5677_set_pll1_event, SND_SOC_DAPM_PRE_PMU |
@@ -3072,19 +3087,26 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
/* DAC Mixer */
SND_SOC_DAPM_SUPPLY("dac stereo1 filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_S1F_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_S1F_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("dac mono2 left filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_M2F_L_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_M2F_L_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("dac mono2 right filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_M2F_R_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_M2F_R_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("dac mono3 left filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_M3F_L_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_M3F_L_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("dac mono3 right filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_M3F_R_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_M3F_R_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("dac mono4 left filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_M4F_L_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_M4F_L_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("dac mono4 right filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_M4F_R_BIT, 0, NULL, 0),
+ RT5677_PWR_DAC_M4F_R_BIT, 0, rt5677_filter_power_event,
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
rt5677_sto1_dac_l_mix, ARRAY_SIZE(rt5677_sto1_dac_l_mix)),
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index f540f82b1f27..08b40460663c 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -189,6 +189,7 @@ static int power_vag_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_POST_PMU:
snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP);
+ msleep(400);
break;
case SND_SOC_DAPM_PRE_PMD:
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index 056375339ea3..5380798883b5 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -229,7 +229,7 @@ SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL,
6, 1, 0),
SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL,
- 7, 1, 0),
+ 7, 1, 1),
SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume",
WM8960_INBMIX1, 4, 7, 0, lineinboost_tlv),
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 39ebd7bf4f53..a7e79784fc16 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -365,8 +365,8 @@ static const struct reg_default wm8962_reg[] = {
{ 16924, 0x0059 }, /* R16924 - HDBASS_PG_1 */
{ 16925, 0x999A }, /* R16925 - HDBASS_PG_0 */
- { 17048, 0x0083 }, /* R17408 - HPF_C_1 */
- { 17049, 0x98AD }, /* R17409 - HPF_C_0 */
+ { 17408, 0x0083 }, /* R17408 - HPF_C_1 */
+ { 17409, 0x98AD }, /* R17409 - HPF_C_0 */
{ 17920, 0x007F }, /* R17920 - ADCL_RETUNE_C1_1 */
{ 17921, 0xFFFF }, /* R17921 - ADCL_RETUNE_C1_0 */
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index 0a60677397b3..4c29bd2ae75c 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -574,6 +574,7 @@ static const struct regmap_config wm8974_regmap = {
.max_register = WM8974_MONOMIX,
.reg_defaults = wm8974_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8974_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
};
static int wm8974_probe(struct snd_soc_codec *codec)
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 4495a40a9468..2ccb8bccc9d4 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -223,8 +223,8 @@ static void mcasp_start_tx(struct davinci_mcasp *mcasp)
/* wait for XDATA to be cleared */
cnt = 0;
- while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) &
- ~XRDATA) && (cnt < 100000))
+ while ((mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) & XRDATA) &&
+ (cnt < 100000))
cnt++;
/* Release TX state machine */
@@ -681,8 +681,8 @@ static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai,
}
mcasp->tdm_slots = slots;
- mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = rx_mask;
- mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = tx_mask;
+ mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
+ mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
mcasp->slot_width = slot_width;
return davinci_mcasp_set_ch_constraints(mcasp);
@@ -908,6 +908,14 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
FSRMOD(total_slots), FSRMOD(0x1FF));
+ /*
+ * If McASP is set to be TX/RX synchronous and the playback is
+ * not running already we need to configure the TX slots in
+ * order to have correct FSX on the bus
+ */
+ if (mcasp_is_synchronous(mcasp) && !mcasp->channels)
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
+ FSXMOD(total_slots), FSXMOD(0x1FF));
}
return 0;
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 19c302b0d763..14dfdee05fd5 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -283,6 +283,8 @@ config SND_SOC_IMX_MC13783
config SND_SOC_FSL_ASOC_CARD
tristate "Generic ASoC Sound Card with ASRC support"
depends on OF && I2C
+ # enforce SND_SOC_FSL_ASOC_CARD=m if SND_AC97_CODEC=m:
+ depends on SND_AC97_CODEC || SND_AC97_CODEC=n
select SND_SOC_IMX_AUDMUX
select SND_SOC_IMX_PCM_DMA
select SND_SOC_FSL_ESAI
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index a4435f5e3be9..08b460ba06ef 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -454,7 +454,8 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
* Rx sync with Tx clocks: Clear SYNC for Tx, set it for Rx.
* Tx sync with Rx clocks: Clear SYNC for Rx, set it for Tx.
*/
- regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC, 0);
+ regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC,
+ sai->synchronous[TX] ? FSL_SAI_CR2_SYNC : 0);
regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC,
sai->synchronous[RX] ? FSL_SAI_CR2_SYNC : 0);
@@ -504,6 +505,24 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
+
+ /*
+ * For sai master mode, after several open/close sai,
+ * there will be no frame clock, and can't recover
+ * anymore. Add software reset to fix this issue.
+ * This is a hardware bug, and will be fix in the
+ * next sai version.
+ */
+ if (!sai->is_slave_mode) {
+ /* Software Reset for both Tx and Rx */
+ regmap_write(sai->regmap,
+ FSL_SAI_TCSR, FSL_SAI_CSR_SR);
+ regmap_write(sai->regmap,
+ FSL_SAI_RCSR, FSL_SAI_CSR_SR);
+ /* Clear SR bit to finish the reset */
+ regmap_write(sai->regmap, FSL_SAI_TCSR, 0);
+ regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
+ }
}
break;
default:
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index 7b778ab85f8b..d430ef5a4f38 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -144,7 +144,7 @@ config SND_SOC_INTEL_SKYLAKE
config SND_SOC_INTEL_SKL_RT286_MACH
tristate "ASoC Audio driver for SKL with RT286 I2S mode"
- depends on X86 && ACPI
+ depends on X86 && ACPI && I2C
select SND_SOC_INTEL_SST
select SND_SOC_INTEL_SKYLAKE
select SND_SOC_RT286
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c
index a7854c8fc523..ad4d0f82603e 100644
--- a/sound/soc/intel/skylake/skl-topology.c
+++ b/sound/soc/intel/skylake/skl-topology.c
@@ -1248,5 +1248,7 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
skl->resource.max_mcps = SKL_MAX_MCPS;
skl->resource.max_mem = SKL_FW_MAX_MEM;
+ skl->tplg = fw;
+
return 0;
}
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c
index 5319529aedf7..caa69c4598a6 100644
--- a/sound/soc/intel/skylake/skl.c
+++ b/sound/soc/intel/skylake/skl.c
@@ -25,6 +25,7 @@
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
+#include <linux/firmware.h>
#include <sound/pcm.h>
#include "skl.h"
@@ -520,6 +521,9 @@ static void skl_remove(struct pci_dev *pci)
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
struct skl *skl = ebus_to_skl(ebus);
+ if (skl->tplg)
+ release_firmware(skl->tplg);
+
if (pci_dev_run_wake(pci))
pm_runtime_get_noresume(&pci->dev);
pci_dev_put(pci);
diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h
index dd2e79ae45a8..a0709e344d44 100644
--- a/sound/soc/intel/skylake/skl.h
+++ b/sound/soc/intel/skylake/skl.h
@@ -68,6 +68,8 @@ struct skl {
struct skl_dsp_resource resource;
struct list_head ppl_list;
struct list_head dapm_path_list;
+
+ const struct firmware *tplg;
};
#define skl_to_ebus(s) (&(s)->ebus)
diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c
index a38a3029062c..5a806da89f42 100644
--- a/sound/soc/rockchip/rockchip_spdif.c
+++ b/sound/soc/rockchip/rockchip_spdif.c
@@ -152,8 +152,10 @@ static int rk_spdif_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR,
- SPDIF_DMACR_TDE_ENABLE,
- SPDIF_DMACR_TDE_ENABLE);
+ SPDIF_DMACR_TDE_ENABLE |
+ SPDIF_DMACR_TDL_MASK,
+ SPDIF_DMACR_TDE_ENABLE |
+ SPDIF_DMACR_TDL(16));
if (ret != 0)
return ret;
@@ -280,7 +282,7 @@ static int rk_spdif_probe(struct platform_device *pdev)
int ret;
match = of_match_node(rk_spdif_match, np);
- if ((int) match->data == RK_SPDIF_RK3288) {
+ if (match->data == (void *)RK_SPDIF_RK3288) {
struct regmap *grf;
grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
diff --git a/sound/soc/rockchip/rockchip_spdif.h b/sound/soc/rockchip/rockchip_spdif.h
index 07f86a21046a..3ef12770ae12 100644
--- a/sound/soc/rockchip/rockchip_spdif.h
+++ b/sound/soc/rockchip/rockchip_spdif.h
@@ -28,9 +28,9 @@
#define SPDIF_CFGR_VDW(x) (x << SPDIF_CFGR_VDW_SHIFT)
#define SDPIF_CFGR_VDW_MASK (0xf << SPDIF_CFGR_VDW_SHIFT)
-#define SPDIF_CFGR_VDW_16 SPDIF_CFGR_VDW(0x00)
-#define SPDIF_CFGR_VDW_20 SPDIF_CFGR_VDW(0x01)
-#define SPDIF_CFGR_VDW_24 SPDIF_CFGR_VDW(0x10)
+#define SPDIF_CFGR_VDW_16 SPDIF_CFGR_VDW(0x0)
+#define SPDIF_CFGR_VDW_20 SPDIF_CFGR_VDW(0x1)
+#define SPDIF_CFGR_VDW_24 SPDIF_CFGR_VDW(0x2)
/*
* DMACR
@@ -42,7 +42,7 @@
#define SPDIF_DMACR_TDL_SHIFT 0
#define SPDIF_DMACR_TDL(x) ((x) << SPDIF_DMACR_TDL_SHIFT)
-#define SPDIF_DMACR_TDL_MASK (0x1f << SDPIF_DMACR_TDL_SHIFT)
+#define SPDIF_DMACR_TDL_MASK (0x1f << SPDIF_DMACR_TDL_SHIFT)
/*
* XFER
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index 76da7620904c..edcf4cc2e84f 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -235,7 +235,7 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
RSND_GEN_S_REG(SCU_SYS_STATUS0, 0x1c8),
RSND_GEN_S_REG(SCU_SYS_INT_EN0, 0x1cc),
RSND_GEN_S_REG(SCU_SYS_STATUS1, 0x1d0),
- RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1c4),
+ RSND_GEN_S_REG(SCU_SYS_INT_EN1, 0x1d4),
RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40),
RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40),
RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40),
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index 261b50217c48..68b439ed22d7 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -923,6 +923,7 @@ static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod,
struct snd_soc_pcm_runtime *rtd)
{
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+ struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
int ret;
@@ -937,6 +938,12 @@ static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod,
return 0;
/*
+ * SRC In doesn't work if DVC was enabled
+ */
+ if (dvc && !rsnd_io_is_play(io))
+ return 0;
+
+ /*
* enable sync convert
*/
ret = rsnd_kctrl_new_s(mod, io, rtd,
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 24b096066a07..a1305f827a98 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -795,12 +795,12 @@ static void soc_resume_deferred(struct work_struct *work)
dev_dbg(card->dev, "ASoC: resume work completed\n");
- /* userspace can access us now we are back as we were before */
- snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0);
-
/* Recheck all endpoints too, their state is affected by suspend */
dapm_mark_endpoints_dirty(card);
snd_soc_dapm_sync(&card->dapm);
+
+ /* userspace can access us now we are back as we were before */
+ snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0);
}
/* powers up audio subsystem after a suspend */
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 016eba10b1ec..7d009428934a 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -2293,6 +2293,12 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w)
kfree(w);
}
+void snd_soc_dapm_reset_cache(struct snd_soc_dapm_context *dapm)
+{
+ dapm->path_sink_cache.widget = NULL;
+ dapm->path_source_cache.widget = NULL;
+}
+
/* free all dapm widgets and resources */
static void dapm_free_widgets(struct snd_soc_dapm_context *dapm)
{
@@ -2303,6 +2309,7 @@ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm)
continue;
snd_soc_dapm_free_widget(w);
}
+ snd_soc_dapm_reset_cache(dapm);
}
static struct snd_soc_dapm_widget *dapm_find_widget(
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
index ecd38e52285a..2f67ba6d7a8f 100644
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -404,7 +404,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
/**
* snd_soc_put_volsw_sx - double mixer set callback
* @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
*
* Callback to set the value of a double mixer control that spans 2 registers.
*
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 8d7ec80af51b..6963ba20991c 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -531,7 +531,7 @@ static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr,
/* TLV bytes controls need standard kcontrol info handler,
* TLV callback and extended put/get handlers.
*/
- k->info = snd_soc_bytes_info;
+ k->info = snd_soc_bytes_info_ext;
k->tlv.c = snd_soc_bytes_tlv_callback;
ext_ops = tplg->bytes_ext_ops;
@@ -1805,6 +1805,7 @@ void snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm,
snd_soc_tplg_widget_remove(w);
snd_soc_dapm_free_widget(w);
}
+ snd_soc_dapm_reset_cache(dapm);
}
EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_remove_all);
diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c
index 843f037a317d..5c2bc53f0a9b 100644
--- a/sound/soc/sti/uniperif_player.c
+++ b/sound/soc/sti/uniperif_player.c
@@ -669,6 +669,7 @@ static int uni_player_startup(struct snd_pcm_substream *substream,
{
struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
struct uniperif *player = priv->dai_data.uni;
+ player->substream = substream;
player->clk_adj = 0;
@@ -950,6 +951,8 @@ static void uni_player_shutdown(struct snd_pcm_substream *substream,
if (player->state != UNIPERIF_STATE_STOPPED)
/* Stop the player */
uni_player_stop(player);
+
+ player->substream = NULL;
}
static int uni_player_parse_dt_clk_glue(struct platform_device *pdev,
@@ -989,7 +992,7 @@ static int uni_player_parse_dt(struct platform_device *pdev,
if (!info)
return -ENOMEM;
- if (of_property_read_u32(pnode, "version", &player->ver) ||
+ if (of_property_read_u32(pnode, "st,version", &player->ver) ||
player->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) {
dev_err(dev, "Unknown uniperipheral version ");
return -EINVAL;
@@ -998,13 +1001,13 @@ static int uni_player_parse_dt(struct platform_device *pdev,
if (player->ver >= SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
info->underflow_enabled = 1;
- if (of_property_read_u32(pnode, "uniperiph-id", &info->id)) {
+ if (of_property_read_u32(pnode, "st,uniperiph-id", &info->id)) {
dev_err(dev, "uniperipheral id not defined");
return -EINVAL;
}
/* Read the device mode property */
- if (of_property_read_string(pnode, "mode", &mode)) {
+ if (of_property_read_string(pnode, "st,mode", &mode)) {
dev_err(dev, "uniperipheral mode not defined");
return -EINVAL;
}
diff --git a/sound/soc/sti/uniperif_reader.c b/sound/soc/sti/uniperif_reader.c
index f791239a3087..8a0eb2050169 100644
--- a/sound/soc/sti/uniperif_reader.c
+++ b/sound/soc/sti/uniperif_reader.c
@@ -316,7 +316,7 @@ static int uni_reader_parse_dt(struct platform_device *pdev,
if (!info)
return -ENOMEM;
- if (of_property_read_u32(node, "version", &reader->ver) ||
+ if (of_property_read_u32(node, "st,version", &reader->ver) ||
reader->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) {
dev_err(&pdev->dev, "Unknown uniperipheral version ");
return -EINVAL;
@@ -346,7 +346,6 @@ int uni_reader_init(struct platform_device *pdev,
reader->hw = &uni_reader_pcm_hw;
reader->dai_ops = &uni_reader_dai_ops;
- dev_err(reader->dev, "%s: enter\n", __func__);
ret = uni_reader_parse_dt(pdev, reader);
if (ret < 0) {
dev_err(reader->dev, "Failed to parse DeviceTree");
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index bcbf4da168b6..1bb896d78d09 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -2,6 +2,7 @@
* Copyright 2014 Emilio López <emilio@elopez.com.ar>
* Copyright 2014 Jon Smirl <jonsmirl@gmail.com>
* Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com>
+ * Copyright 2015 Adam Sampson <ats@offog.org>
*
* Based on the Allwinner SDK driver, released under the GPL.
*
@@ -404,7 +405,7 @@ static const struct snd_kcontrol_new sun4i_codec_pa_mute =
static DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1);
static const struct snd_kcontrol_new sun4i_codec_widgets[] = {
- SOC_SINGLE_TLV("PA Volume", SUN4I_CODEC_DAC_ACTL,
+ SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL,
SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0,
sun4i_codec_pa_volume_scale),
};
@@ -452,12 +453,12 @@ static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL,
SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0),
- /* Pre-Amplifier */
- SND_SOC_DAPM_MIXER("Pre-Amplifier", SUN4I_CODEC_ADC_ACTL,
+ /* Power Amplifier */
+ SND_SOC_DAPM_MIXER("Power Amplifier", SUN4I_CODEC_ADC_ACTL,
SUN4I_CODEC_ADC_ACTL_PA_EN, 0,
sun4i_codec_pa_mixer_controls,
ARRAY_SIZE(sun4i_codec_pa_mixer_controls)),
- SND_SOC_DAPM_SWITCH("Pre-Amplifier Mute", SND_SOC_NOPM, 0, 0,
+ SND_SOC_DAPM_SWITCH("Power Amplifier Mute", SND_SOC_NOPM, 0, 0,
&sun4i_codec_pa_mute),
SND_SOC_DAPM_OUTPUT("HP Right"),
@@ -480,16 +481,16 @@ static const struct snd_soc_dapm_route sun4i_codec_dapm_routes[] = {
{ "Left Mixer", NULL, "Mixer Enable" },
{ "Left Mixer", "Left DAC Playback Switch", "Left DAC" },
- /* Pre-Amplifier Mixer Routes */
- { "Pre-Amplifier", "Mixer Playback Switch", "Left Mixer" },
- { "Pre-Amplifier", "Mixer Playback Switch", "Right Mixer" },
- { "Pre-Amplifier", "DAC Playback Switch", "Left DAC" },
- { "Pre-Amplifier", "DAC Playback Switch", "Right DAC" },
+ /* Power Amplifier Routes */
+ { "Power Amplifier", "Mixer Playback Switch", "Left Mixer" },
+ { "Power Amplifier", "Mixer Playback Switch", "Right Mixer" },
+ { "Power Amplifier", "DAC Playback Switch", "Left DAC" },
+ { "Power Amplifier", "DAC Playback Switch", "Right DAC" },
- /* PA -> HP path */
- { "Pre-Amplifier Mute", "Switch", "Pre-Amplifier" },
- { "HP Right", NULL, "Pre-Amplifier Mute" },
- { "HP Left", NULL, "Pre-Amplifier Mute" },
+ /* Headphone Output Routes */
+ { "Power Amplifier Mute", "Switch", "Power Amplifier" },
+ { "HP Right", NULL, "Power Amplifier Mute" },
+ { "HP Left", NULL, "Power Amplifier Mute" },
};
static struct snd_soc_codec_driver sun4i_codec_codec = {
diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c
index 62c25e74f0e5..9520b4cd7038 100644
--- a/sound/usb/6fire/firmware.c
+++ b/sound/usb/6fire/firmware.c
@@ -350,7 +350,7 @@ static int usb6fire_fw_check(struct usb_interface *intf, const u8 *version)
if (!memcmp(version, known_fw_versions + i, 2))
return 0;
- dev_err(&intf->dev, "invalid fimware version in device: %4ph. "
+ dev_err(&intf->dev, "invalid firmware version in device: %4ph. "
"please reconnect to power. if this failure "
"still happens, check your firmware installation.",
version);
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index 7661616f3636..5b4c58c3e2c5 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -174,6 +174,8 @@ struct snd_usb_midi_in_endpoint {
u8 running_status_length;
} ports[0x10];
u8 seen_f5;
+ bool in_sysex;
+ u8 last_cin;
u8 error_resubmit;
int current_port;
};
@@ -468,6 +470,39 @@ static void snd_usbmidi_maudio_broken_running_status_input(
}
/*
+ * QinHeng CH345 is buggy: every second packet inside a SysEx has not CIN 4
+ * but the previously seen CIN, but still with three data bytes.
+ */
+static void ch345_broken_sysex_input(struct snd_usb_midi_in_endpoint *ep,
+ uint8_t *buffer, int buffer_length)
+{
+ unsigned int i, cin, length;
+
+ for (i = 0; i + 3 < buffer_length; i += 4) {
+ if (buffer[i] == 0 && i > 0)
+ break;
+ cin = buffer[i] & 0x0f;
+ if (ep->in_sysex &&
+ cin == ep->last_cin &&
+ (buffer[i + 1 + (cin == 0x6)] & 0x80) == 0)
+ cin = 0x4;
+#if 0
+ if (buffer[i + 1] == 0x90) {
+ /*
+ * Either a corrupted running status or a real note-on
+ * message; impossible to detect reliably.
+ */
+ }
+#endif
+ length = snd_usbmidi_cin_length[cin];
+ snd_usbmidi_input_data(ep, 0, &buffer[i + 1], length);
+ ep->in_sysex = cin == 0x4;
+ if (!ep->in_sysex)
+ ep->last_cin = cin;
+ }
+}
+
+/*
* CME protocol: like the standard protocol, but SysEx commands are sent as a
* single USB packet preceded by a 0x0F byte.
*/
@@ -660,6 +695,12 @@ static struct usb_protocol_ops snd_usbmidi_cme_ops = {
.output_packet = snd_usbmidi_output_standard_packet,
};
+static struct usb_protocol_ops snd_usbmidi_ch345_broken_sysex_ops = {
+ .input = ch345_broken_sysex_input,
+ .output = snd_usbmidi_standard_output,
+ .output_packet = snd_usbmidi_output_standard_packet,
+};
+
/*
* AKAI MPD16 protocol:
*
@@ -1341,6 +1382,7 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi *umidi,
* Various chips declare a packet size larger than 4 bytes, but
* do not actually work with larger packets:
*/
+ case USB_ID(0x0a67, 0x5011): /* Medeli DD305 */
case USB_ID(0x0a92, 0x1020): /* ESI M4U */
case USB_ID(0x1430, 0x474b): /* RedOctane GH MIDI INTERFACE */
case USB_ID(0x15ca, 0x0101): /* Textech USB Midi Cable */
@@ -2378,6 +2420,10 @@ int snd_usbmidi_create(struct snd_card *card,
err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints);
break;
+ case QUIRK_MIDI_CH345:
+ umidi->usb_protocol_ops = &snd_usbmidi_ch345_broken_sysex_ops;
+ err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints);
+ break;
default:
dev_err(&umidi->dev->dev, "invalid quirk type %d\n",
quirk->type);
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index f494dced3c11..4f85757009b3 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1354,6 +1354,8 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
}
}
+ snd_usb_mixer_fu_apply_quirk(state->mixer, cval, unitid, kctl);
+
range = (cval->max - cval->min) / cval->res;
/*
* Are there devices with volume range more than 255? I use a bit more
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index 6a803eff87f7..ddca6547399b 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -348,13 +348,6 @@ static struct usbmix_name_map bose_companion5_map[] = {
{ 0 } /* terminator */
};
-/* Dragonfly DAC 1.2, the dB conversion factor is 1 instead of 256 */
-static struct usbmix_dB_map dragonfly_1_2_dB = {0, 5000};
-static struct usbmix_name_map dragonfly_1_2_map[] = {
- { 7, NULL, .dB = &dragonfly_1_2_dB },
- { 0 } /* terminator */
-};
-
/*
* Control map entries
*/
@@ -470,11 +463,6 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
.id = USB_ID(0x05a7, 0x1020),
.map = bose_companion5_map,
},
- {
- /* Dragonfly DAC 1.2 */
- .id = USB_ID(0x21b4, 0x0081),
- .map = dragonfly_1_2_map,
- },
{ 0 } /* terminator */
};
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index fe91184ce832..0ce888dceed0 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -37,6 +37,7 @@
#include <sound/control.h>
#include <sound/hwdep.h>
#include <sound/info.h>
+#include <sound/tlv.h>
#include "usbaudio.h"
#include "mixer.h"
@@ -1825,3 +1826,39 @@ void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
}
}
+static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer,
+ struct snd_kcontrol *kctl)
+{
+ /* Approximation using 10 ranges based on output measurement on hw v1.2.
+ * This seems close to the cubic mapping e.g. alsamixer uses. */
+ static const DECLARE_TLV_DB_RANGE(scale,
+ 0, 1, TLV_DB_MINMAX_ITEM(-5300, -4970),
+ 2, 5, TLV_DB_MINMAX_ITEM(-4710, -4160),
+ 6, 7, TLV_DB_MINMAX_ITEM(-3884, -3710),
+ 8, 14, TLV_DB_MINMAX_ITEM(-3443, -2560),
+ 15, 16, TLV_DB_MINMAX_ITEM(-2475, -2324),
+ 17, 19, TLV_DB_MINMAX_ITEM(-2228, -2031),
+ 20, 26, TLV_DB_MINMAX_ITEM(-1910, -1393),
+ 27, 31, TLV_DB_MINMAX_ITEM(-1322, -1032),
+ 32, 40, TLV_DB_MINMAX_ITEM(-968, -490),
+ 41, 50, TLV_DB_MINMAX_ITEM(-441, 0),
+ );
+
+ usb_audio_info(mixer->chip, "applying DragonFly dB scale quirk\n");
+ kctl->tlv.p = scale;
+ kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+}
+
+void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
+ struct usb_mixer_elem_info *cval, int unitid,
+ struct snd_kcontrol *kctl)
+{
+ switch (mixer->chip->usb_id) {
+ case USB_ID(0x21b4, 0x0081): /* AudioQuest DragonFly */
+ if (unitid == 7 && cval->min == 0 && cval->max == 50)
+ snd_dragonfly_quirk_db_scale(mixer, kctl);
+ break;
+ }
+}
+
diff --git a/sound/usb/mixer_quirks.h b/sound/usb/mixer_quirks.h
index bdbfab093816..177c329cd4dd 100644
--- a/sound/usb/mixer_quirks.h
+++ b/sound/usb/mixer_quirks.h
@@ -9,5 +9,9 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
int unitid);
+void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
+ struct usb_mixer_elem_info *cval, int unitid,
+ struct snd_kcontrol *kctl);
+
#endif /* SND_USB_MIXER_QUIRKS_H */
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 1a1e2e4df35e..c60a776e815d 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -2829,6 +2829,17 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.idProduct = 0x1020,
},
+/* QinHeng devices */
+{
+ USB_DEVICE(0x1a86, 0x752d),
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .vendor_name = "QinHeng",
+ .product_name = "CH345",
+ .ifnum = 1,
+ .type = QUIRK_MIDI_CH345
+ }
+},
+
/* KeithMcMillen Stringport */
{
USB_DEVICE(0x1f38, 0x0001),
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 5ca80e7d30cd..b6c0c8e3b450 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -538,6 +538,7 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip,
[QUIRK_MIDI_CME] = create_any_midi_quirk,
[QUIRK_MIDI_AKAI] = create_any_midi_quirk,
[QUIRK_MIDI_FTDI] = create_any_midi_quirk,
+ [QUIRK_MIDI_CH345] = create_any_midi_quirk,
[QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk,
[QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
[QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk,
@@ -1124,6 +1125,7 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip)
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) */
+ case USB_ID(0x21B4, 0x0081): /* AudioQuest DragonFly */
return true;
}
return false;
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 15a12715bd05..b665d85555cb 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -95,6 +95,7 @@ enum quirk_type {
QUIRK_MIDI_AKAI,
QUIRK_MIDI_US122L,
QUIRK_MIDI_FTDI,
+ QUIRK_MIDI_CH345,
QUIRK_AUDIO_STANDARD_INTERFACE,
QUIRK_AUDIO_FIXED_ENDPOINT,
QUIRK_AUDIO_EDIROL_UAXX,
diff --git a/tools/Makefile b/tools/Makefile
index 7dc820a8c1f1..6339f6ac3ccb 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -8,23 +8,24 @@ include scripts/Makefile.include
help:
@echo 'Possible targets:'
@echo ''
- @echo ' acpi - ACPI tools'
- @echo ' cgroup - cgroup tools'
- @echo ' cpupower - a tool for all things x86 CPU power'
- @echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer'
- @echo ' hv - tools used when in Hyper-V clients'
- @echo ' iio - IIO tools'
- @echo ' lguest - a minimal 32-bit x86 hypervisor'
- @echo ' perf - Linux performance measurement and analysis tool'
- @echo ' selftests - various kernel selftests'
- @echo ' turbostat - Intel CPU idle stats and freq reporting tool'
- @echo ' usb - USB testing tools'
- @echo ' virtio - vhost test module'
- @echo ' net - misc networking tools'
- @echo ' vm - misc vm tools'
+ @echo ' acpi - ACPI tools'
+ @echo ' cgroup - cgroup tools'
+ @echo ' cpupower - a tool for all things x86 CPU power'
+ @echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer'
+ @echo ' freefall - laptop accelerometer program for disk protection'
+ @echo ' hv - tools used when in Hyper-V clients'
+ @echo ' iio - IIO tools'
+ @echo ' lguest - a minimal 32-bit x86 hypervisor'
+ @echo ' net - misc networking tools'
+ @echo ' perf - Linux performance measurement and analysis tool'
+ @echo ' selftests - various kernel selftests'
+ @echo ' spi - spi tools'
+ @echo ' tmon - thermal monitoring and tuning tool'
+ @echo ' turbostat - Intel CPU idle stats and freq reporting tool'
+ @echo ' usb - USB testing tools'
+ @echo ' virtio - vhost test module'
+ @echo ' vm - misc vm tools'
@echo ' x86_energy_perf_policy - Intel energy policy tool'
- @echo ' tmon - thermal monitoring and tuning tool'
- @echo ' freefall - laptop accelerometer program for disk protection'
@echo ''
@echo 'You can do:'
@echo ' $$ make -C tools/ <tool>_install'
@@ -52,7 +53,7 @@ acpi: FORCE
cpupower: FORCE
$(call descend,power/$@)
-cgroup firewire hv guest usb virtio vm net iio: FORCE
+cgroup firewire hv guest spi usb virtio vm net iio: FORCE
$(call descend,$@)
liblockdep: FORCE
@@ -96,7 +97,7 @@ cgroup_install firewire_install hv_install lguest_install perf_install usb_insta
$(call descend,$(@:_install=),install)
selftests_install:
- $(call descend,testing/$(@:_clean=),install)
+ $(call descend,testing/$(@:_install=),install)
turbostat_install x86_energy_perf_policy_install:
$(call descend,power/x86/$(@:_install=),install)
@@ -118,7 +119,7 @@ acpi_clean:
cpupower_clean:
$(call descend,power/cpupower,clean)
-cgroup_clean hv_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean net_clean iio_clean:
+cgroup_clean hv_clean firewire_clean lguest_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean:
$(call descend,$(@:_clean=),clean)
liblockdep_clean:
@@ -127,6 +128,12 @@ liblockdep_clean:
libapi_clean:
$(call descend,lib/api,clean)
+libbpf_clean:
+ $(call descend,lib/bpf,clean)
+
+libsubcmd_clean:
+ $(call descend,lib/subcmd,clean)
+
perf_clean:
$(call descend,$(@:_clean=),clean)
@@ -142,9 +149,12 @@ tmon_clean:
freefall_clean:
$(call descend,laptop/freefall,clean)
+build_clean:
+ $(call descend,build,clean)
+
clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \
- perf_clean selftests_clean turbostat_clean usb_clean virtio_clean \
+ perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
- freefall_clean
+ freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean
.PHONY: FORCE
diff --git a/tools/build/Makefile b/tools/build/Makefile
index a93036272d43..0d5a0e3a8fa9 100644
--- a/tools/build/Makefile
+++ b/tools/build/Makefile
@@ -25,7 +25,7 @@ export Q srctree CC LD
MAKEFLAGS := --no-print-directory
build := -f $(srctree)/tools/build/Makefile.build dir=. obj
-all: fixdep
+all: $(OUTPUT)fixdep
clean:
$(call QUIET_CLEAN, fixdep)
diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
index 37ff4c9f92f1..02db3cdff20f 100644
--- a/tools/build/Makefile.feature
+++ b/tools/build/Makefile.feature
@@ -7,7 +7,7 @@ endif
feature_check = $(eval $(feature_check_code))
define feature_check_code
- feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS) $(FEATURE_CHECK_CFLAGS-$(1))" LDFLAGS="$(LDFLAGS) $(FEATURE_CHECK_LDFLAGS-$(1))" -C $(feature_dir) test-$1.bin >/dev/null 2>/dev/null && echo 1 || echo 0)
+ feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS) $(FEATURE_CHECK_CFLAGS-$(1))" LDFLAGS="$(LDFLAGS) $(FEATURE_CHECK_LDFLAGS-$(1))" -C $(feature_dir) $(OUTPUT_FEATURES)test-$1.bin >/dev/null 2>/dev/null && echo 1 || echo 0)
endef
feature_set = $(eval $(feature_set_code))
@@ -101,7 +101,6 @@ ifeq ($(feature-all), 1)
#
$(foreach feat,$(FEATURE_TESTS),$(call feature_set,$(feat)))
else
- $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS)" LDFLAGS=$(LDFLAGS) -i -j -C $(feature_dir) $(addsuffix .bin,$(FEATURE_TESTS)) >/dev/null 2>&1)
$(foreach feat,$(FEATURE_TESTS),$(call feature_check,$(feat)))
endif
@@ -123,13 +122,31 @@ define feature_print_text_code
MSG = $(shell printf '...%30s: %s' $(1) $(2))
endef
+#
+# generates feature value assignment for name, like:
+# $(call feature_assign,dwarf) == feature-dwarf=1
+#
+feature_assign = feature-$(1)=$(feature-$(1))
+
FEATURE_DUMP_FILENAME = $(OUTPUT)FEATURE-DUMP$(FEATURE_USER)
-FEATURE_DUMP := $(foreach feat,$(FEATURE_DISPLAY),feature-$(feat)($(feature-$(feat))))
-FEATURE_DUMP_FILE := $(shell touch $(FEATURE_DUMP_FILENAME); cat $(FEATURE_DUMP_FILENAME))
+FEATURE_DUMP := $(shell touch $(FEATURE_DUMP_FILENAME); cat $(FEATURE_DUMP_FILENAME))
-ifeq ($(dwarf-post-unwind),1)
- FEATURE_DUMP += dwarf-post-unwind($(dwarf-post-unwind-text))
-endif
+feature_dump_check = $(eval $(feature_dump_check_code))
+define feature_dump_check_code
+ ifeq ($(findstring $(1),$(FEATURE_DUMP)),)
+ $(2) := 1
+ endif
+endef
+
+#
+# First check if any test from FEATURE_DISPLAY
+# and set feature_display := 1 if it does
+$(foreach feat,$(FEATURE_DISPLAY),$(call feature_dump_check,$(call feature_assign,$(feat)),feature_display))
+
+#
+# Now also check if any other test changed,
+# so we force FEATURE-DUMP generation
+$(foreach feat,$(FEATURE_TESTS),$(call feature_dump_check,$(call feature_assign,$(feat)),feature_dump_changed))
# The $(feature_display) controls the default detection message
# output. It's set if:
@@ -138,13 +155,13 @@ endif
# - one of the $(FEATURE_DISPLAY) is not detected
# - VF is enabled
-ifneq ("$(FEATURE_DUMP)","$(FEATURE_DUMP_FILE)")
- $(shell echo "$(FEATURE_DUMP)" > $(FEATURE_DUMP_FILENAME))
- feature_display := 1
+ifeq ($(feature_dump_changed),1)
+ $(shell rm -f $(FEATURE_DUMP_FILENAME))
+ $(foreach feat,$(FEATURE_TESTS),$(shell echo "$(call feature_assign,$(feat))" >> $(FEATURE_DUMP_FILENAME)))
endif
feature_display_check = $(eval $(feature_check_display_code))
-define feature_display_check_code
+define feature_check_display_code
ifneq ($(feature-$(1)), 1)
feature_display := 1
endif
@@ -161,11 +178,6 @@ ifeq ($(feature_display),1)
$(info )
$(info Auto-detecting system features:)
$(foreach feat,$(FEATURE_DISPLAY),$(call feature_print_status,$(feat),))
-
- ifeq ($(dwarf-post-unwind),1)
- $(call feature_print_text,"DWARF post unwind library", $(dwarf-post-unwind-text))
- endif
-
ifneq ($(feature_verbose),1)
$(info )
endif
diff --git a/tools/build/Makefile.include b/tools/build/Makefile.include
index 4e09ad617a60..be630bed66d2 100644
--- a/tools/build/Makefile.include
+++ b/tools/build/Makefile.include
@@ -4,7 +4,7 @@ ifdef CROSS_COMPILE
fixdep:
else
fixdep:
- $(Q)$(MAKE) -C $(srctree)/tools/build fixdep
+ $(Q)$(MAKE) -C $(srctree)/tools/build CFLAGS= LDFLAGS= $(OUTPUT)fixdep
endif
.PHONY: fixdep
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
index cea04ce9f35c..bf8f0352264d 100644
--- a/tools/build/feature/Makefile
+++ b/tools/build/feature/Makefile
@@ -1,4 +1,3 @@
-
FILES= \
test-all.bin \
test-backtrace.bin \
@@ -38,38 +37,40 @@ FILES= \
test-bpf.bin \
test-get_cpuid.bin
+FILES := $(addprefix $(OUTPUT),$(FILES))
+
CC := $(CROSS_COMPILE)gcc -MD
PKG_CONFIG := $(CROSS_COMPILE)pkg-config
all: $(FILES)
-__BUILD = $(CC) $(CFLAGS) -Wall -Werror -o $(OUTPUT)$@ $(patsubst %.bin,%.c,$@) $(LDFLAGS)
- BUILD = $(__BUILD) > $(OUTPUT)$(@:.bin=.make.output) 2>&1
+__BUILD = $(CC) $(CFLAGS) -Wall -Werror -o $@ $(patsubst %.bin,%.c,$(@F)) $(LDFLAGS)
+ BUILD = $(__BUILD) > $(@:.bin=.make.output) 2>&1
###############################
-test-all.bin:
+$(OUTPUT)test-all.bin:
$(BUILD) -fstack-protector-all -O2 -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl -lz -llzma
-test-hello.bin:
+$(OUTPUT)test-hello.bin:
$(BUILD)
-test-pthread-attr-setaffinity-np.bin:
+$(OUTPUT)test-pthread-attr-setaffinity-np.bin:
$(BUILD) -D_GNU_SOURCE -lpthread
-test-stackprotector-all.bin:
+$(OUTPUT)test-stackprotector-all.bin:
$(BUILD) -fstack-protector-all
-test-fortify-source.bin:
+$(OUTPUT)test-fortify-source.bin:
$(BUILD) -O2 -D_FORTIFY_SOURCE=2
-test-bionic.bin:
+$(OUTPUT)test-bionic.bin:
$(BUILD)
-test-libelf.bin:
+$(OUTPUT)test-libelf.bin:
$(BUILD) -lelf
-test-glibc.bin:
+$(OUTPUT)test-glibc.bin:
$(BUILD)
DWARFLIBS := -ldw
@@ -77,37 +78,37 @@ ifeq ($(findstring -static,${LDFLAGS}),-static)
DWARFLIBS += -lelf -lebl -lz -llzma -lbz2
endif
-test-dwarf.bin:
+$(OUTPUT)test-dwarf.bin:
$(BUILD) $(DWARFLIBS)
-test-libelf-mmap.bin:
+$(OUTPUT)test-libelf-mmap.bin:
$(BUILD) -lelf
-test-libelf-getphdrnum.bin:
+$(OUTPUT)test-libelf-getphdrnum.bin:
$(BUILD) -lelf
-test-libnuma.bin:
+$(OUTPUT)test-libnuma.bin:
$(BUILD) -lnuma
-test-numa_num_possible_cpus.bin:
+$(OUTPUT)test-numa_num_possible_cpus.bin:
$(BUILD) -lnuma
-test-libunwind.bin:
+$(OUTPUT)test-libunwind.bin:
$(BUILD) -lelf
-test-libunwind-debug-frame.bin:
+$(OUTPUT)test-libunwind-debug-frame.bin:
$(BUILD) -lelf
-test-libaudit.bin:
+$(OUTPUT)test-libaudit.bin:
$(BUILD) -laudit
-test-libslang.bin:
+$(OUTPUT)test-libslang.bin:
$(BUILD) -I/usr/include/slang -lslang
-test-gtk2.bin:
+$(OUTPUT)test-gtk2.bin:
$(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null)
-test-gtk2-infobar.bin:
+$(OUTPUT)test-gtk2-infobar.bin:
$(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null)
grep-libs = $(filter -l%,$(1))
@@ -119,63 +120,63 @@ PERL_EMBED_LIBADD = $(call grep-libs,$(PERL_EMBED_LDOPTS))
PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null`
FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS)
-test-libperl.bin:
+$(OUTPUT)test-libperl.bin:
$(BUILD) $(FLAGS_PERL_EMBED)
-test-libpython.bin:
+$(OUTPUT)test-libpython.bin:
$(BUILD)
-test-libpython-version.bin:
+$(OUTPUT)test-libpython-version.bin:
$(BUILD)
-test-libbfd.bin:
+$(OUTPUT)test-libbfd.bin:
$(BUILD) -DPACKAGE='"perf"' -lbfd -lz -liberty -ldl
-test-liberty.bin:
- $(CC) $(CFLAGS) -Wall -Werror -o $(OUTPUT)$@ test-libbfd.c -DPACKAGE='"perf"' $(LDFLAGS) -lbfd -ldl -liberty
+$(OUTPUT)test-liberty.bin:
+ $(CC) $(CFLAGS) -Wall -Werror -o $@ test-libbfd.c -DPACKAGE='"perf"' $(LDFLAGS) -lbfd -ldl -liberty
-test-liberty-z.bin:
- $(CC) $(CFLAGS) -Wall -Werror -o $(OUTPUT)$@ test-libbfd.c -DPACKAGE='"perf"' $(LDFLAGS) -lbfd -ldl -liberty -lz
+$(OUTPUT)test-liberty-z.bin:
+ $(CC) $(CFLAGS) -Wall -Werror -o $@ test-libbfd.c -DPACKAGE='"perf"' $(LDFLAGS) -lbfd -ldl -liberty -lz
-test-cplus-demangle.bin:
+$(OUTPUT)test-cplus-demangle.bin:
$(BUILD) -liberty
-test-backtrace.bin:
+$(OUTPUT)test-backtrace.bin:
$(BUILD)
-test-timerfd.bin:
+$(OUTPUT)test-timerfd.bin:
$(BUILD)
-test-libdw-dwarf-unwind.bin:
+$(OUTPUT)test-libdw-dwarf-unwind.bin:
$(BUILD) # -ldw provided by $(FEATURE_CHECK_LDFLAGS-libdw-dwarf-unwind)
-test-libbabeltrace.bin:
+$(OUTPUT)test-libbabeltrace.bin:
$(BUILD) # -lbabeltrace provided by $(FEATURE_CHECK_LDFLAGS-libbabeltrace)
-test-sync-compare-and-swap.bin:
+$(OUTPUT)test-sync-compare-and-swap.bin:
$(BUILD)
-test-compile-32.bin:
- $(CC) -m32 -o $(OUTPUT)$@ test-compile.c
+$(OUTPUT)test-compile-32.bin:
+ $(CC) -m32 -o $@ test-compile.c
-test-compile-x32.bin:
- $(CC) -mx32 -o $(OUTPUT)$@ test-compile.c
+$(OUTPUT)test-compile-x32.bin:
+ $(CC) -mx32 -o $@ test-compile.c
-test-zlib.bin:
+$(OUTPUT)test-zlib.bin:
$(BUILD) -lz
-test-lzma.bin:
+$(OUTPUT)test-lzma.bin:
$(BUILD) -llzma
-test-get_cpuid.bin:
+$(OUTPUT)test-get_cpuid.bin:
$(BUILD)
-test-bpf.bin:
+$(OUTPUT)test-bpf.bin:
$(BUILD)
--include *.d
+-include $(OUTPUT)*.d
###############################
clean:
- rm -f $(FILES) *.d $(FILES:.bin=.make.output)
+ rm -f $(FILES) $(OUTPUT)*.d $(FILES:.bin=.make.output)
diff --git a/tools/build/feature/test-all.c b/tools/build/feature/test-all.c
index 33cf6f20bd4e..81025cade45f 100644
--- a/tools/build/feature/test-all.c
+++ b/tools/build/feature/test-all.c
@@ -125,6 +125,10 @@
# include "test-get_cpuid.c"
#undef main
+#define main main_test_bpf
+# include "test-bpf.c"
+#undef main
+
int main(int argc, char *argv[])
{
main_test_libpython();
@@ -153,6 +157,7 @@ int main(int argc, char *argv[])
main_test_pthread_attr_setaffinity_np();
main_test_lzma();
main_test_get_cpuid();
+ main_test_bpf();
return 0;
}
diff --git a/tools/build/feature/test-bpf.c b/tools/build/feature/test-bpf.c
index 062bac811af9..b389026839b9 100644
--- a/tools/build/feature/test-bpf.c
+++ b/tools/build/feature/test-bpf.c
@@ -1,9 +1,23 @@
+#include <asm/unistd.h>
#include <linux/bpf.h>
+#include <unistd.h>
+
+#ifndef __NR_bpf
+# if defined(__i386__)
+# define __NR_bpf 357
+# elif defined(__x86_64__)
+# define __NR_bpf 321
+# elif defined(__aarch64__)
+# define __NR_bpf 280
+# error __NR_bpf not defined. libbpf does not support your arch.
+# endif
+#endif
int main(void)
{
union bpf_attr attr;
+ /* Check fields in attr */
attr.prog_type = BPF_PROG_TYPE_KPROBE;
attr.insn_cnt = 0;
attr.insns = 0;
@@ -14,5 +28,9 @@ int main(void)
attr.kern_version = 0;
attr = attr;
- return 0;
+ /*
+ * Test existence of __NR_bpf and BPF_PROG_LOAD.
+ * This call should fail if we run the testcase.
+ */
+ return syscall(__NR_bpf, BPF_PROG_LOAD, attr, sizeof(attr));
}
diff --git a/tools/hv/hv_fcopy_daemon.c b/tools/hv/hv_fcopy_daemon.c
index 5480e4e424eb..fdc9ca4c0356 100644
--- a/tools/hv/hv_fcopy_daemon.c
+++ b/tools/hv/hv_fcopy_daemon.c
@@ -37,12 +37,14 @@
static int target_fd;
static char target_fname[W_MAX_PATH];
+static unsigned long long filesize;
static int hv_start_fcopy(struct hv_start_fcopy *smsg)
{
int error = HV_E_FAIL;
char *q, *p;
+ filesize = 0;
p = (char *)smsg->path_name;
snprintf(target_fname, sizeof(target_fname), "%s/%s",
(char *)smsg->path_name, (char *)smsg->file_name);
@@ -98,14 +100,26 @@ done:
static int hv_copy_data(struct hv_do_fcopy *cpmsg)
{
ssize_t bytes_written;
+ int ret = 0;
bytes_written = pwrite(target_fd, cpmsg->data, cpmsg->size,
cpmsg->offset);
- if (bytes_written != cpmsg->size)
- return HV_E_FAIL;
+ filesize += cpmsg->size;
+ if (bytes_written != cpmsg->size) {
+ switch (errno) {
+ case ENOSPC:
+ ret = HV_ERROR_DISK_FULL;
+ break;
+ default:
+ ret = HV_E_FAIL;
+ break;
+ }
+ syslog(LOG_ERR, "pwrite failed to write %llu bytes: %ld (%s)",
+ filesize, (long)bytes_written, strerror(errno));
+ }
- return 0;
+ return ret;
}
static int hv_copy_finished(void)
@@ -165,7 +179,7 @@ int main(int argc, char *argv[])
}
openlog("HV_FCOPY", 0, LOG_USER);
- syslog(LOG_INFO, "HV_FCOPY starting; pid is:%d", getpid());
+ syslog(LOG_INFO, "starting; pid is:%d", getpid());
fcopy_fd = open("/dev/vmbus/hv_fcopy", O_RDWR);
@@ -201,7 +215,7 @@ int main(int argc, char *argv[])
}
kernel_modver = *(__u32 *)buffer;
in_handshake = 0;
- syslog(LOG_INFO, "HV_FCOPY: kernel module version: %d",
+ syslog(LOG_INFO, "kernel module version: %d",
kernel_modver);
continue;
}
diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c
index 96234b638249..5d51d6ff08e6 100644
--- a/tools/hv/hv_vss_daemon.c
+++ b/tools/hv/hv_vss_daemon.c
@@ -254,7 +254,7 @@ int main(int argc, char *argv[])
syslog(LOG_ERR, "Illegal op:%d\n", op);
}
vss_msg->error = error;
- len = write(vss_fd, &error, sizeof(struct hv_vss_msg));
+ len = write(vss_fd, vss_msg, sizeof(struct hv_vss_msg));
if (len != sizeof(struct hv_vss_msg)) {
syslog(LOG_ERR, "write failed; error: %d %s", errno,
strerror(errno));
diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/include/linux/bitmap.h
index 40bd21488032..28f5493da491 100644
--- a/tools/perf/util/include/linux/bitmap.h
+++ b/tools/include/linux/bitmap.h
@@ -11,6 +11,8 @@ int __bitmap_weight(const unsigned long *bitmap, int bits);
void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits);
+#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1)))
+
#define BITMAP_LAST_WORD_MASK(nbits) \
( \
((nbits) % BITS_PER_LONG) ? \
diff --git a/tools/include/linux/list.h b/tools/include/linux/list.h
index a017f1595676..1da423820ad4 100644
--- a/tools/include/linux/list.h
+++ b/tools/include/linux/list.h
@@ -1,11 +1,751 @@
-#include <linux/compiler.h>
-#include <linux/kernel.h>
+#ifndef __TOOLS_LINUX_LIST_H
+#define __TOOLS_LINUX_LIST_H
+
#include <linux/types.h>
+#include <linux/poison.h>
+#include <linux/kernel.h>
+#include <linux/compiler.h>
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+#ifndef CONFIG_DEBUG_LIST
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+#else
+extern void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next);
+#endif
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ WRITE_ONCE(prev->next, next);
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+#ifndef CONFIG_DEBUG_LIST
+static inline void __list_del_entry(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+}
+#else
+extern void __list_del_entry(struct list_head *entry);
+extern void list_del(struct list_head *entry);
+#endif
+
+/**
+ * list_replace - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * If @old was empty, it will be overwritten.
+ */
+static inline void list_replace(struct list_head *old,
+ struct list_head *new)
+{
+ new->next = old->next;
+ new->next->prev = new;
+ new->prev = old->prev;
+ new->prev->next = new;
+}
+
+static inline void list_replace_init(struct list_head *old,
+ struct list_head *new)
+{
+ list_replace(old, new);
+ INIT_LIST_HEAD(old);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+ __list_del_entry(entry);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+ __list_del_entry(list);
+ list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+ struct list_head *head)
+{
+ __list_del_entry(list);
+ list_add_tail(list, head);
+}
+
+/**
+ * list_is_last - tests whether @list is the last entry in list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_last(const struct list_head *list,
+ const struct list_head *head)
+{
+ return list->next == head;
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is empty and not being modified
+ * @head: the list to test
+ *
+ * Description:
+ * tests whether a list is empty _and_ checks that no other CPU might be
+ * in the process of modifying either member (next or prev)
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+ struct list_head *next = head->next;
+ return (next == head) && (next == head->prev);
+}
+
+/**
+ * list_rotate_left - rotate the list to the left
+ * @head: the head of the list
+ */
+static inline void list_rotate_left(struct list_head *head)
+{
+ struct list_head *first;
+
+ if (!list_empty(head)) {
+ first = head->next;
+ list_move_tail(first, head);
+ }
+}
+
+/**
+ * list_is_singular - tests whether a list has just one entry.
+ * @head: the list to test.
+ */
+static inline int list_is_singular(const struct list_head *head)
+{
+ return !list_empty(head) && (head->next == head->prev);
+}
+
+static inline void __list_cut_position(struct list_head *list,
+ struct list_head *head, struct list_head *entry)
+{
+ struct list_head *new_first = entry->next;
+ list->next = head->next;
+ list->next->prev = list;
+ list->prev = entry;
+ entry->next = list;
+ head->next = new_first;
+ new_first->prev = head;
+}
+
+/**
+ * list_cut_position - cut a list into two
+ * @list: a new list to add all removed entries
+ * @head: a list with entries
+ * @entry: an entry within head, could be the head itself
+ * and if so we won't cut the list
+ *
+ * This helper moves the initial part of @head, up to and
+ * including @entry, from @head to @list. You should
+ * pass on @entry an element you know is on @head. @list
+ * should be an empty list or a list you do not care about
+ * losing its data.
+ *
+ */
+static inline void list_cut_position(struct list_head *list,
+ struct list_head *head, struct list_head *entry)
+{
+ if (list_empty(head))
+ return;
+ if (list_is_singular(head) &&
+ (head->next != entry && head != entry))
+ return;
+ if (entry == head)
+ INIT_LIST_HEAD(list);
+ else
+ __list_cut_position(list, head, entry);
+}
+
+static inline void __list_splice(const struct list_head *list,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ struct list_head *first = list->next;
+ struct list_head *last = list->prev;
+
+ first->prev = prev;
+ prev->next = first;
+
+ last->next = next;
+ next->prev = last;
+}
+
+/**
+ * list_splice - join two lists, this is designed for stacks
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(const struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head, head->next);
+}
+
+/**
+ * list_splice_tail - join two lists, each list being a queue
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice_tail(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head->prev, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head, head->next);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_splice_tail_init - join two lists and reinitialise the emptied list
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * Each of the lists is a queue.
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_tail_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head->prev, head);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_head within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * list_first_entry - get the first element from a list
+ * @ptr: the list head to take the element from.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_head within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_first_entry(ptr, type, member) \
+ list_entry((ptr)->next, type, member)
+
+/**
+ * list_last_entry - get the last element from a list
+ * @ptr: the list head to take the element from.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_head within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_last_entry(ptr, type, member) \
+ list_entry((ptr)->prev, type, member)
+
+/**
+ * list_first_entry_or_null - get the first element from a list
+ * @ptr: the list head to take the element from.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_head within the struct.
+ *
+ * Note that if the list is empty, it returns NULL.
+ */
+#define list_first_entry_or_null(ptr, type, member) \
+ (!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL)
+
+/**
+ * list_next_entry - get the next element in list
+ * @pos: the type * to cursor
+ * @member: the name of the list_head within the struct.
+ */
+#define list_next_entry(pos, member) \
+ list_entry((pos)->member.next, typeof(*(pos)), member)
+
+/**
+ * list_prev_entry - get the prev element in list
+ * @pos: the type * to cursor
+ * @member: the name of the list_head within the struct.
+ */
+#define list_prev_entry(pos, member) \
+ list_entry((pos)->member.prev, typeof(*(pos)), member)
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev - iterate over a list backwards
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+ for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_prev_safe(pos, n, head) \
+ for (pos = (head)->prev, n = pos->prev; \
+ pos != (head); \
+ pos = n, n = pos->prev)
+
+/**
+ * list_for_each_entry - iterate over list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_head within the struct.
+ */
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_first_entry(head, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_next_entry(pos, member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_head within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member) \
+ for (pos = list_last_entry(head, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_prev_entry(pos, member))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
+ * @pos: the type * to use as a start point
+ * @head: the head of the list
+ * @member: the name of the list_head within the struct.
+ *
+ * Prepares a pos entry for use as a start point in list_for_each_entry_continue().
+ */
+#define list_prepare_entry(pos, head, member) \
+ ((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue - continue iteration over list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_head within the struct.
+ *
+ * Continue to iterate over list of given type, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue(pos, head, member) \
+ for (pos = list_next_entry(pos, member); \
+ &pos->member != (head); \
+ pos = list_next_entry(pos, member))
+
+/**
+ * list_for_each_entry_continue_reverse - iterate backwards from the given point
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_head within the struct.
+ *
+ * Start to iterate over list of given type backwards, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue_reverse(pos, head, member) \
+ for (pos = list_prev_entry(pos, member); \
+ &pos->member != (head); \
+ pos = list_prev_entry(pos, member))
+
+/**
+ * list_for_each_entry_from - iterate over list of given type from the current point
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_head within the struct.
+ *
+ * Iterate over list of given type, continuing from current position.
+ */
+#define list_for_each_entry_from(pos, head, member) \
+ for (; &pos->member != (head); \
+ pos = list_next_entry(pos, member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_head within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_first_entry(head, typeof(*pos), member), \
+ n = list_next_entry(pos, member); \
+ &pos->member != (head); \
+ pos = n, n = list_next_entry(n, member))
+
+/**
+ * list_for_each_entry_safe_continue - continue list iteration safe against removal
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_head within the struct.
+ *
+ * Iterate over list of given type, continuing after current point,
+ * safe against removal of list entry.
+ */
+#define list_for_each_entry_safe_continue(pos, n, head, member) \
+ for (pos = list_next_entry(pos, member), \
+ n = list_next_entry(pos, member); \
+ &pos->member != (head); \
+ pos = n, n = list_next_entry(n, member))
+
+/**
+ * list_for_each_entry_safe_from - iterate over list from current point safe against removal
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_head within the struct.
+ *
+ * Iterate over list of given type from current point, safe against
+ * removal of list entry.
+ */
+#define list_for_each_entry_safe_from(pos, n, head, member) \
+ for (n = list_next_entry(pos, member); \
+ &pos->member != (head); \
+ pos = n, n = list_next_entry(n, member))
+
+/**
+ * list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_head within the struct.
+ *
+ * Iterate backwards over list of given type, safe against removal
+ * of list entry.
+ */
+#define list_for_each_entry_safe_reverse(pos, n, head, member) \
+ for (pos = list_last_entry(head, typeof(*pos), member), \
+ n = list_prev_entry(pos, member); \
+ &pos->member != (head); \
+ pos = n, n = list_prev_entry(n, member))
-#include "../../../include/linux/list.h"
+/**
+ * list_safe_reset_next - reset a stale list_for_each_entry_safe loop
+ * @pos: the loop cursor used in the list_for_each_entry_safe loop
+ * @n: temporary storage used in list_for_each_entry_safe
+ * @member: the name of the list_head within the struct.
+ *
+ * list_safe_reset_next is not safe to use in general if the list may be
+ * modified concurrently (eg. the lock is dropped in the loop body). An
+ * exception to this is if the cursor element (pos) is pinned in the list,
+ * and list_safe_reset_next is called after re-taking the lock and before
+ * completing the current iteration of the loop body.
+ */
+#define list_safe_reset_next(pos, n, member) \
+ n = list_next_entry(pos, member)
+
+/*
+ * Double linked lists with a single pointer list head.
+ * Mostly useful for hash tables where the two pointer list head is
+ * too wasteful.
+ * You lose the ability to access the tail in O(1).
+ */
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+static inline void INIT_HLIST_NODE(struct hlist_node *h)
+{
+ h->next = NULL;
+ h->pprev = NULL;
+}
+
+static inline int hlist_unhashed(const struct hlist_node *h)
+{
+ return !h->pprev;
+}
+
+static inline int hlist_empty(const struct hlist_head *h)
+{
+ return !h->first;
+}
+
+static inline void __hlist_del(struct hlist_node *n)
+{
+ struct hlist_node *next = n->next;
+ struct hlist_node **pprev = n->pprev;
+
+ WRITE_ONCE(*pprev, next);
+ if (next)
+ next->pprev = pprev;
+}
+
+static inline void hlist_del(struct hlist_node *n)
+{
+ __hlist_del(n);
+ n->next = LIST_POISON1;
+ n->pprev = LIST_POISON2;
+}
+
+static inline void hlist_del_init(struct hlist_node *n)
+{
+ if (!hlist_unhashed(n)) {
+ __hlist_del(n);
+ INIT_HLIST_NODE(n);
+ }
+}
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+ struct hlist_node *first = h->first;
+ n->next = first;
+ if (first)
+ first->pprev = &n->next;
+ h->first = n;
+ n->pprev = &h->first;
+}
+
+/* next must be != NULL */
+static inline void hlist_add_before(struct hlist_node *n,
+ struct hlist_node *next)
+{
+ n->pprev = next->pprev;
+ n->next = next;
+ next->pprev = &n->next;
+ *(n->pprev) = n;
+}
+
+static inline void hlist_add_behind(struct hlist_node *n,
+ struct hlist_node *prev)
+{
+ n->next = prev->next;
+ prev->next = n;
+ n->pprev = &prev->next;
+
+ if (n->next)
+ n->next->pprev = &n->next;
+}
+
+/* after that we'll appear to be on some hlist and hlist_del will work */
+static inline void hlist_add_fake(struct hlist_node *n)
+{
+ n->pprev = &n->next;
+}
+
+static inline bool hlist_fake(struct hlist_node *h)
+{
+ return h->pprev == &h->next;
+}
+
+/*
+ * Move a list from one list head to another. Fixup the pprev
+ * reference of the first entry if it exists.
+ */
+static inline void hlist_move_list(struct hlist_head *old,
+ struct hlist_head *new)
+{
+ new->first = old->first;
+ if (new->first)
+ new->first->pprev = &new->first;
+ old->first = NULL;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+ for (pos = (head)->first; pos ; pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+ for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+ pos = n)
+
+#define hlist_entry_safe(ptr, type, member) \
+ ({ typeof(ptr) ____ptr = (ptr); \
+ ____ptr ? hlist_entry(____ptr, type, member) : NULL; \
+ })
+
+/**
+ * hlist_for_each_entry - iterate over list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry(pos, head, member) \
+ for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);\
+ pos; \
+ pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
+
+/**
+ * hlist_for_each_entry_continue - iterate over a hlist continuing after current point
+ * @pos: the type * to use as a loop cursor.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue(pos, member) \
+ for (pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member);\
+ pos; \
+ pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
+
+/**
+ * hlist_for_each_entry_from - iterate over a hlist continuing from current point
+ * @pos: the type * to use as a loop cursor.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_from(pos, member) \
+ for (; pos; \
+ pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
+
+/**
+ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos: the type * to use as a loop cursor.
+ * @n: another &struct hlist_node to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_safe(pos, n, head, member) \
+ for (pos = hlist_entry_safe((head)->first, typeof(*pos), member);\
+ pos && ({ n = pos->member.next; 1; }); \
+ pos = hlist_entry_safe(n, typeof(*pos), member))
-#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.
@@ -27,4 +767,5 @@ static inline void list_del_range(struct list_head *begin,
*/
#define list_for_each_from(pos, head) \
for (; pos != (head); pos = pos->next)
-#endif
+
+#endif /* __TOOLS_LINUX_LIST_H */
diff --git a/tools/include/linux/string.h b/tools/include/linux/string.h
new file mode 100644
index 000000000000..e26223f1f287
--- /dev/null
+++ b/tools/include/linux/string.h
@@ -0,0 +1,15 @@
+#ifndef _TOOLS_LINUX_STRING_H_
+#define _TOOLS_LINUX_STRING_H_
+
+
+#include <linux/types.h> /* for size_t */
+
+void *memdup(const void *src, size_t len);
+
+int strtobool(const char *s, bool *res);
+
+#ifndef __UCLIBC__
+extern size_t strlcpy(char *dest, const char *src, size_t size);
+#endif
+
+#endif /* _LINUX_STRING_H_ */
diff --git a/tools/perf/util/bitmap.c b/tools/lib/bitmap.c
index 0a1adc1111fd..0a1adc1111fd 100644
--- a/tools/perf/util/bitmap.c
+++ b/tools/lib/bitmap.c
diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile
index a3caaf3eafbd..fc1bc75ae56d 100644
--- a/tools/lib/bpf/Makefile
+++ b/tools/lib/bpf/Makefile
@@ -6,6 +6,12 @@ BPF_EXTRAVERSION = 1
MAKEFLAGS += --no-print-directory
+ifeq ($(srctree),)
+srctree := $(patsubst %/,%,$(dir $(shell pwd)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+#$(info Determined 'srctree' to be $(srctree))
+endif
# Makefiles suck: This macro sets a default value of $(2) for the
# variable named by $(1), unless the variable has been set by
@@ -31,7 +37,8 @@ INSTALL = install
DESTDIR ?=
DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))'
-LP64 := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1)
+include $(srctree)/tools/scripts/Makefile.arch
+
ifeq ($(LP64), 1)
libdir_relative = lib64
else
@@ -57,13 +64,6 @@ ifndef VERBOSE
VERBOSE = 0
endif
-ifeq ($(srctree),)
-srctree := $(patsubst %/,%,$(dir $(shell pwd)))
-srctree := $(patsubst %/,%,$(dir $(srctree)))
-srctree := $(patsubst %/,%,$(dir $(srctree)))
-#$(info Determined 'srctree' to be $(srctree))
-endif
-
FEATURE_USER = .libbpf
FEATURE_TESTS = libelf libelf-getphdrnum libelf-mmap bpf
FEATURE_DISPLAY = libelf bpf
@@ -71,7 +71,21 @@ FEATURE_DISPLAY = libelf bpf
INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/arch/$(ARCH)/include/uapi -I$(srctree)/include/uapi
FEATURE_CHECK_CFLAGS-bpf = $(INCLUDES)
+check_feat := 1
+NON_CHECK_FEAT_TARGETS := clean TAGS tags cscope help
+ifdef MAKECMDGOALS
+ifeq ($(filter-out $(NON_CHECK_FEAT_TARGETS),$(MAKECMDGOALS)),)
+ check_feat := 0
+endif
+endif
+
+ifeq ($(check_feat),1)
+ifeq ($(FEATURES_DUMP),)
include $(srctree)/tools/build/Makefile.feature
+else
+include $(FEATURES_DUMP)
+endif
+endif
export prefix libdir src obj
@@ -178,7 +192,7 @@ config-clean:
$(Q)$(MAKE) -C $(srctree)/tools/build/feature/ clean >/dev/null
clean:
- $(call QUIET_CLEAN, libbpf) $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d \
+ $(call QUIET_CLEAN, libbpf) $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d .*.cmd \
$(RM) LIBBPF-CFLAGS
$(call QUIET_CLEAN, core-gen) $(RM) $(OUTPUT)FEATURE-DUMP.libbpf
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index a6331050ab79..1f91cc941b7c 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -14,8 +14,8 @@
#include "bpf.h"
/*
- * When building perf, unistd.h is override. Define __NR_bpf is
- * required to be defined.
+ * When building perf, unistd.h is overrided. __NR_bpf is
+ * required to be defined explicitly.
*/
#ifndef __NR_bpf
# if defined(__i386__)
@@ -83,3 +83,17 @@ int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns,
log_buf[0] = 0;
return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
}
+
+int bpf_map_update_elem(int fd, void *key, void *value,
+ u64 flags)
+{
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.map_fd = fd;
+ attr.key = ptr_to_u64(key);
+ attr.value = ptr_to_u64(value);
+ attr.flags = flags;
+
+ return sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
+}
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
index 854b7361b784..a76465541292 100644
--- a/tools/lib/bpf/bpf.h
+++ b/tools/lib/bpf/bpf.h
@@ -20,4 +20,6 @@ int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns,
u32 kern_version, char *log_buf,
size_t log_buf_sz);
+int bpf_map_update_elem(int fd, void *key, void *value,
+ u64 flags);
#endif
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index e176bad19bcb..8334a5a9d5d7 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -152,29 +152,36 @@ struct bpf_program {
} *reloc_desc;
int nr_reloc;
- int fd;
+ struct {
+ int nr;
+ int *fds;
+ } instances;
+ bpf_program_prep_t preprocessor;
struct bpf_object *obj;
void *priv;
bpf_program_clear_priv_t clear_priv;
};
+struct bpf_map {
+ int fd;
+ char *name;
+ struct bpf_map_def def;
+ void *priv;
+ bpf_map_clear_priv_t clear_priv;
+};
+
static LIST_HEAD(bpf_objects_list);
struct bpf_object {
char license[64];
u32 kern_version;
- void *maps_buf;
- size_t maps_buf_sz;
struct bpf_program *programs;
size_t nr_programs;
- int *map_fds;
- /*
- * This field is required because maps_buf will be freed and
- * maps_buf_sz will be set to 0 after loaded.
- */
- size_t nr_map_fds;
+ struct bpf_map *maps;
+ size_t nr_maps;
+
bool loaded;
/*
@@ -188,6 +195,7 @@ struct bpf_object {
Elf *elf;
GElf_Ehdr ehdr;
Elf_Data *symbols;
+ size_t strtabidx;
struct {
GElf_Shdr shdr;
Elf_Data *data;
@@ -206,10 +214,25 @@ struct bpf_object {
static void bpf_program__unload(struct bpf_program *prog)
{
+ int i;
+
if (!prog)
return;
- zclose(prog->fd);
+ /*
+ * If the object is opened but the program was never loaded,
+ * it is possible that prog->instances.nr == -1.
+ */
+ if (prog->instances.nr > 0) {
+ for (i = 0; i < prog->instances.nr; i++)
+ zclose(prog->instances.fds[i]);
+ } else if (prog->instances.nr != -1) {
+ pr_warning("Internal error: instances.nr is %d\n",
+ prog->instances.nr);
+ }
+
+ prog->instances.nr = -1;
+ zfree(&prog->instances.fds);
}
static void bpf_program__exit(struct bpf_program *prog)
@@ -260,7 +283,8 @@ bpf_program__init(void *data, size_t size, char *name, int idx,
memcpy(prog->insns, data,
prog->insns_cnt * sizeof(struct bpf_insn));
prog->idx = idx;
- prog->fd = -1;
+ prog->instances.fds = NULL;
+ prog->instances.nr = -1;
return 0;
errout:
@@ -469,21 +493,77 @@ static int
bpf_object__init_maps(struct bpf_object *obj, void *data,
size_t size)
{
- if (size == 0) {
+ size_t nr_maps;
+ int i;
+
+ nr_maps = size / sizeof(struct bpf_map_def);
+ if (!data || !nr_maps) {
pr_debug("%s doesn't need map definition\n",
obj->path);
return 0;
}
- obj->maps_buf = malloc(size);
- if (!obj->maps_buf) {
- pr_warning("malloc maps failed: %s\n", obj->path);
+ pr_debug("maps in %s: %zd bytes\n", obj->path, size);
+
+ obj->maps = calloc(nr_maps, sizeof(obj->maps[0]));
+ if (!obj->maps) {
+ pr_warning("alloc maps for object failed\n");
return -ENOMEM;
}
+ obj->nr_maps = nr_maps;
- obj->maps_buf_sz = size;
- memcpy(obj->maps_buf, data, size);
- pr_debug("maps in %s: %ld bytes\n", obj->path, (long)size);
+ for (i = 0; i < nr_maps; i++) {
+ struct bpf_map_def *def = &obj->maps[i].def;
+
+ /*
+ * fill all fd with -1 so won't close incorrect
+ * fd (fd=0 is stdin) when failure (zclose won't close
+ * negative fd)).
+ */
+ obj->maps[i].fd = -1;
+
+ /* Save map definition into obj->maps */
+ *def = ((struct bpf_map_def *)data)[i];
+ }
+ return 0;
+}
+
+static int
+bpf_object__init_maps_name(struct bpf_object *obj, int maps_shndx)
+{
+ int i;
+ Elf_Data *symbols = obj->efile.symbols;
+
+ if (!symbols || maps_shndx < 0)
+ return -EINVAL;
+
+ for (i = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) {
+ GElf_Sym sym;
+ size_t map_idx;
+ const char *map_name;
+
+ if (!gelf_getsym(symbols, i, &sym))
+ continue;
+ if (sym.st_shndx != maps_shndx)
+ continue;
+
+ map_name = elf_strptr(obj->efile.elf,
+ obj->efile.strtabidx,
+ sym.st_name);
+ map_idx = sym.st_value / sizeof(struct bpf_map_def);
+ if (map_idx >= obj->nr_maps) {
+ pr_warning("index of map \"%s\" is buggy: %zu > %zu\n",
+ map_name, map_idx, obj->nr_maps);
+ continue;
+ }
+ obj->maps[map_idx].name = strdup(map_name);
+ if (!obj->maps[map_idx].name) {
+ pr_warning("failed to alloc map name\n");
+ return -ENOMEM;
+ }
+ pr_debug("map %zu is \"%s\"\n", map_idx,
+ obj->maps[map_idx].name);
+ }
return 0;
}
@@ -492,7 +572,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
Elf *elf = obj->efile.elf;
GElf_Ehdr *ep = &obj->efile.ehdr;
Elf_Scn *scn = NULL;
- int idx = 0, err = 0;
+ int idx = 0, err = 0, maps_shndx = -1;
/* Elf is corrupted/truncated, avoid calling elf_strptr. */
if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) {
@@ -542,16 +622,19 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
err = bpf_object__init_kversion(obj,
data->d_buf,
data->d_size);
- else if (strcmp(name, "maps") == 0)
+ else if (strcmp(name, "maps") == 0) {
err = bpf_object__init_maps(obj, data->d_buf,
data->d_size);
- else if (sh.sh_type == SHT_SYMTAB) {
+ maps_shndx = idx;
+ } else if (sh.sh_type == SHT_SYMTAB) {
if (obj->efile.symbols) {
pr_warning("bpf: multiple SYMTAB in %s\n",
obj->path);
err = -LIBBPF_ERRNO__FORMAT;
- } else
+ } else {
obj->efile.symbols = data;
+ obj->efile.strtabidx = sh.sh_link;
+ }
} else if ((sh.sh_type == SHT_PROGBITS) &&
(sh.sh_flags & SHF_EXECINSTR) &&
(data->d_size > 0)) {
@@ -586,6 +669,13 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
if (err)
goto out;
}
+
+ if (!obj->efile.strtabidx || obj->efile.strtabidx >= idx) {
+ pr_warning("Corrupted ELF file: index of strtab invalid\n");
+ return LIBBPF_ERRNO__FORMAT;
+ }
+ if (maps_shndx >= 0)
+ err = bpf_object__init_maps_name(obj, maps_shndx);
out:
return err;
}
@@ -668,37 +758,15 @@ static int
bpf_object__create_maps(struct bpf_object *obj)
{
unsigned int i;
- size_t nr_maps;
- int *pfd;
-
- nr_maps = obj->maps_buf_sz / sizeof(struct bpf_map_def);
- if (!obj->maps_buf || !nr_maps) {
- pr_debug("don't need create maps for %s\n",
- obj->path);
- return 0;
- }
-
- obj->map_fds = malloc(sizeof(int) * nr_maps);
- if (!obj->map_fds) {
- pr_warning("realloc perf_bpf_map_fds failed\n");
- return -ENOMEM;
- }
- obj->nr_map_fds = nr_maps;
- /* fill all fd with -1 */
- memset(obj->map_fds, -1, sizeof(int) * nr_maps);
-
- pfd = obj->map_fds;
- for (i = 0; i < nr_maps; i++) {
- struct bpf_map_def def;
+ for (i = 0; i < obj->nr_maps; i++) {
+ struct bpf_map_def *def = &obj->maps[i].def;
+ int *pfd = &obj->maps[i].fd;
- def = *(struct bpf_map_def *)(obj->maps_buf +
- i * sizeof(struct bpf_map_def));
-
- *pfd = bpf_create_map(def.type,
- def.key_size,
- def.value_size,
- def.max_entries);
+ *pfd = bpf_create_map(def->type,
+ def->key_size,
+ def->value_size,
+ def->max_entries);
if (*pfd < 0) {
size_t j;
int err = *pfd;
@@ -706,22 +774,17 @@ bpf_object__create_maps(struct bpf_object *obj)
pr_warning("failed to create map: %s\n",
strerror(errno));
for (j = 0; j < i; j++)
- zclose(obj->map_fds[j]);
- obj->nr_map_fds = 0;
- zfree(&obj->map_fds);
+ zclose(obj->maps[j].fd);
return err;
}
pr_debug("create map: fd=%d\n", *pfd);
- pfd++;
}
- zfree(&obj->maps_buf);
- obj->maps_buf_sz = 0;
return 0;
}
static int
-bpf_program__relocate(struct bpf_program *prog, int *map_fds)
+bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj)
{
int i;
@@ -741,7 +804,7 @@ bpf_program__relocate(struct bpf_program *prog, int *map_fds)
return -LIBBPF_ERRNO__RELOC;
}
insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD;
- insns[insn_idx].imm = map_fds[map_idx];
+ insns[insn_idx].imm = obj->maps[map_idx].fd;
}
zfree(&prog->reloc_desc);
@@ -760,7 +823,7 @@ bpf_object__relocate(struct bpf_object *obj)
for (i = 0; i < obj->nr_programs; i++) {
prog = &obj->programs[i];
- err = bpf_program__relocate(prog, obj->map_fds);
+ err = bpf_program__relocate(prog, obj);
if (err) {
pr_warning("failed to relocate '%s'\n",
prog->section_name);
@@ -784,8 +847,7 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
Elf_Data *data = obj->efile.reloc[i].data;
int idx = shdr->sh_info;
struct bpf_program *prog;
- size_t nr_maps = obj->maps_buf_sz /
- sizeof(struct bpf_map_def);
+ size_t nr_maps = obj->nr_maps;
if (shdr->sh_type != SHT_REL) {
pr_warning("internal error at %d\n", __LINE__);
@@ -860,13 +922,73 @@ static int
bpf_program__load(struct bpf_program *prog,
char *license, u32 kern_version)
{
- int err, fd;
+ int err = 0, fd, i;
- err = load_program(prog->insns, prog->insns_cnt,
- license, kern_version, &fd);
- if (!err)
- prog->fd = fd;
+ if (prog->instances.nr < 0 || !prog->instances.fds) {
+ if (prog->preprocessor) {
+ pr_warning("Internal error: can't load program '%s'\n",
+ prog->section_name);
+ return -LIBBPF_ERRNO__INTERNAL;
+ }
+
+ prog->instances.fds = malloc(sizeof(int));
+ if (!prog->instances.fds) {
+ pr_warning("Not enough memory for BPF fds\n");
+ return -ENOMEM;
+ }
+ prog->instances.nr = 1;
+ prog->instances.fds[0] = -1;
+ }
+
+ if (!prog->preprocessor) {
+ if (prog->instances.nr != 1) {
+ pr_warning("Program '%s' is inconsistent: nr(%d) != 1\n",
+ prog->section_name, prog->instances.nr);
+ }
+ err = load_program(prog->insns, prog->insns_cnt,
+ license, kern_version, &fd);
+ if (!err)
+ prog->instances.fds[0] = fd;
+ goto out;
+ }
+
+ for (i = 0; i < prog->instances.nr; i++) {
+ struct bpf_prog_prep_result result;
+ bpf_program_prep_t preprocessor = prog->preprocessor;
+
+ bzero(&result, sizeof(result));
+ err = preprocessor(prog, i, prog->insns,
+ prog->insns_cnt, &result);
+ if (err) {
+ pr_warning("Preprocessing the %dth instance of program '%s' failed\n",
+ i, prog->section_name);
+ goto out;
+ }
+
+ if (!result.new_insn_ptr || !result.new_insn_cnt) {
+ pr_debug("Skip loading the %dth instance of program '%s'\n",
+ i, prog->section_name);
+ prog->instances.fds[i] = -1;
+ if (result.pfd)
+ *result.pfd = -1;
+ continue;
+ }
+
+ err = load_program(result.new_insn_ptr,
+ result.new_insn_cnt,
+ license, kern_version, &fd);
+
+ if (err) {
+ pr_warning("Loading the %dth instance of program '%s' failed\n",
+ i, prog->section_name);
+ goto out;
+ }
+ if (result.pfd)
+ *result.pfd = fd;
+ prog->instances.fds[i] = fd;
+ }
+out:
if (err)
pr_warning("failed to load program '%s'\n",
prog->section_name);
@@ -970,10 +1092,8 @@ int bpf_object__unload(struct bpf_object *obj)
if (!obj)
return -EINVAL;
- for (i = 0; i < obj->nr_map_fds; i++)
- zclose(obj->map_fds[i]);
- zfree(&obj->map_fds);
- obj->nr_map_fds = 0;
+ for (i = 0; i < obj->nr_maps; i++)
+ zclose(obj->maps[i].fd);
for (i = 0; i < obj->nr_programs; i++)
bpf_program__unload(&obj->programs[i]);
@@ -1016,7 +1136,16 @@ void bpf_object__close(struct bpf_object *obj)
bpf_object__elf_finish(obj);
bpf_object__unload(obj);
- zfree(&obj->maps_buf);
+ for (i = 0; i < obj->nr_maps; i++) {
+ zfree(&obj->maps[i].name);
+ if (obj->maps[i].clear_priv)
+ obj->maps[i].clear_priv(&obj->maps[i],
+ obj->maps[i].priv);
+ obj->maps[i].priv = NULL;
+ obj->maps[i].clear_priv = NULL;
+ }
+ zfree(&obj->maps);
+ obj->nr_maps = 0;
if (obj->programs && obj->nr_programs) {
for (i = 0; i < obj->nr_programs; i++)
@@ -1121,5 +1250,142 @@ const char *bpf_program__title(struct bpf_program *prog, bool needs_copy)
int bpf_program__fd(struct bpf_program *prog)
{
- return prog->fd;
+ return bpf_program__nth_fd(prog, 0);
+}
+
+int bpf_program__set_prep(struct bpf_program *prog, int nr_instances,
+ bpf_program_prep_t prep)
+{
+ int *instances_fds;
+
+ if (nr_instances <= 0 || !prep)
+ return -EINVAL;
+
+ if (prog->instances.nr > 0 || prog->instances.fds) {
+ pr_warning("Can't set pre-processor after loading\n");
+ return -EINVAL;
+ }
+
+ instances_fds = malloc(sizeof(int) * nr_instances);
+ if (!instances_fds) {
+ pr_warning("alloc memory failed for fds\n");
+ return -ENOMEM;
+ }
+
+ /* fill all fd with -1 */
+ memset(instances_fds, -1, sizeof(int) * nr_instances);
+
+ prog->instances.nr = nr_instances;
+ prog->instances.fds = instances_fds;
+ prog->preprocessor = prep;
+ return 0;
+}
+
+int bpf_program__nth_fd(struct bpf_program *prog, int n)
+{
+ int fd;
+
+ if (n >= prog->instances.nr || n < 0) {
+ pr_warning("Can't get the %dth fd from program %s: only %d instances\n",
+ n, prog->section_name, prog->instances.nr);
+ return -EINVAL;
+ }
+
+ fd = prog->instances.fds[n];
+ if (fd < 0) {
+ pr_warning("%dth instance of program '%s' is invalid\n",
+ n, prog->section_name);
+ return -ENOENT;
+ }
+
+ return fd;
+}
+
+int bpf_map__get_fd(struct bpf_map *map)
+{
+ if (!map)
+ return -EINVAL;
+
+ return map->fd;
+}
+
+int bpf_map__get_def(struct bpf_map *map, struct bpf_map_def *pdef)
+{
+ if (!map || !pdef)
+ return -EINVAL;
+
+ *pdef = map->def;
+ return 0;
+}
+
+const char *bpf_map__get_name(struct bpf_map *map)
+{
+ if (!map)
+ return NULL;
+ return map->name;
+}
+
+int bpf_map__set_private(struct bpf_map *map, void *priv,
+ bpf_map_clear_priv_t clear_priv)
+{
+ if (!map)
+ return -EINVAL;
+
+ if (map->priv) {
+ if (map->clear_priv)
+ map->clear_priv(map, map->priv);
+ }
+
+ map->priv = priv;
+ map->clear_priv = clear_priv;
+ return 0;
+}
+
+int bpf_map__get_private(struct bpf_map *map, void **ppriv)
+{
+ if (!map)
+ return -EINVAL;
+
+ if (ppriv)
+ *ppriv = map->priv;
+ return 0;
+}
+
+struct bpf_map *
+bpf_map__next(struct bpf_map *prev, struct bpf_object *obj)
+{
+ size_t idx;
+ struct bpf_map *s, *e;
+
+ if (!obj || !obj->maps)
+ return NULL;
+
+ s = obj->maps;
+ e = obj->maps + obj->nr_maps;
+
+ if (prev == NULL)
+ return s;
+
+ if ((prev < s) || (prev >= e)) {
+ pr_warning("error in %s: map handler doesn't belong to object\n",
+ __func__);
+ return NULL;
+ }
+
+ idx = (prev - obj->maps) + 1;
+ if (idx >= obj->nr_maps)
+ return NULL;
+ return &obj->maps[idx];
+}
+
+struct bpf_map *
+bpf_object__get_map_by_name(struct bpf_object *obj, const char *name)
+{
+ struct bpf_map *pos;
+
+ bpf_map__for_each(pos, obj) {
+ if (pos->name && !strcmp(pos->name, name))
+ return pos;
+ }
+ return NULL;
}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index c9a9aef2806c..a51594c7b518 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -88,6 +88,70 @@ const char *bpf_program__title(struct bpf_program *prog, bool needs_copy);
int bpf_program__fd(struct bpf_program *prog);
+struct bpf_insn;
+
+/*
+ * Libbpf allows callers to adjust BPF programs before being loaded
+ * into kernel. One program in an object file can be transform into
+ * multiple variants to be attached to different code.
+ *
+ * bpf_program_prep_t, bpf_program__set_prep and bpf_program__nth_fd
+ * are APIs for this propose.
+ *
+ * - bpf_program_prep_t:
+ * It defines 'preprocessor', which is a caller defined function
+ * passed to libbpf through bpf_program__set_prep(), and will be
+ * called before program is loaded. The processor should adjust
+ * the program one time for each instances according to the number
+ * passed to it.
+ *
+ * - bpf_program__set_prep:
+ * Attachs a preprocessor to a BPF program. The number of instances
+ * whould be created is also passed through this function.
+ *
+ * - bpf_program__nth_fd:
+ * After the program is loaded, get resuling fds from bpf program for
+ * each instances.
+ *
+ * If bpf_program__set_prep() is not used, the program whould be loaded
+ * without adjustment during bpf_object__load(). The program has only
+ * one instance. In this case bpf_program__fd(prog) is equal to
+ * bpf_program__nth_fd(prog, 0).
+ */
+
+struct bpf_prog_prep_result {
+ /*
+ * If not NULL, load new instruction array.
+ * If set to NULL, don't load this instance.
+ */
+ struct bpf_insn *new_insn_ptr;
+ int new_insn_cnt;
+
+ /* If not NULL, result fd is set to it */
+ int *pfd;
+};
+
+/*
+ * Parameters of bpf_program_prep_t:
+ * - prog: The bpf_program being loaded.
+ * - n: Index of instance being generated.
+ * - insns: BPF instructions array.
+ * - insns_cnt:Number of instructions in insns.
+ * - res: Output parameter, result of transformation.
+ *
+ * Return value:
+ * - Zero: pre-processing success.
+ * - Non-zero: pre-processing, stop loading.
+ */
+typedef int (*bpf_program_prep_t)(struct bpf_program *prog, int n,
+ struct bpf_insn *insns, int insns_cnt,
+ struct bpf_prog_prep_result *res);
+
+int bpf_program__set_prep(struct bpf_program *prog, int nr_instance,
+ bpf_program_prep_t prep);
+
+int bpf_program__nth_fd(struct bpf_program *prog, int n);
+
/*
* We don't need __attribute__((packed)) now since it is
* unnecessary for 'bpf_map_def' because they are all aligned.
@@ -101,4 +165,28 @@ struct bpf_map_def {
unsigned int max_entries;
};
+/*
+ * There is another 'struct bpf_map' in include/linux/map.h. However,
+ * it is not a uapi header so no need to consider name clash.
+ */
+struct bpf_map;
+struct bpf_map *
+bpf_object__get_map_by_name(struct bpf_object *obj, const char *name);
+
+struct bpf_map *
+bpf_map__next(struct bpf_map *map, struct bpf_object *obj);
+#define bpf_map__for_each(pos, obj) \
+ for ((pos) = bpf_map__next(NULL, (obj)); \
+ (pos) != NULL; \
+ (pos) = bpf_map__next((pos), (obj)))
+
+int bpf_map__get_fd(struct bpf_map *map);
+int bpf_map__get_def(struct bpf_map *map, struct bpf_map_def *pdef);
+const char *bpf_map__get_name(struct bpf_map *map);
+
+typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *);
+int bpf_map__set_private(struct bpf_map *map, void *priv,
+ bpf_map_clear_priv_t clear_priv);
+int bpf_map__get_private(struct bpf_map *map, void **ppriv);
+
#endif
diff --git a/tools/lib/find_bit.c b/tools/lib/find_bit.c
new file mode 100644
index 000000000000..9122a9e80046
--- /dev/null
+++ b/tools/lib/find_bit.c
@@ -0,0 +1,84 @@
+/* bit search implementation
+ *
+ * Copied from lib/find_bit.c to tools/lib/find_bit.c
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * Copyright (C) 2008 IBM Corporation
+ * 'find_last_bit' is written by Rusty Russell <rusty@rustcorp.com.au>
+ * (Inspired by David Howell's find_next_bit implementation)
+ *
+ * Rewritten by Yury Norov <yury.norov@gmail.com> to decrease
+ * size and improve performance, 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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/bitmap.h>
+#include <linux/kernel.h>
+
+#if !defined(find_next_bit)
+
+/*
+ * This is a common helper function for find_next_bit and
+ * find_next_zero_bit. The difference is the "invert" argument, which
+ * is XORed with each fetched word before searching it for one bits.
+ */
+static unsigned long _find_next_bit(const unsigned long *addr,
+ unsigned long nbits, unsigned long start, unsigned long invert)
+{
+ unsigned long tmp;
+
+ if (!nbits || start >= nbits)
+ return nbits;
+
+ tmp = addr[start / BITS_PER_LONG] ^ invert;
+
+ /* Handle 1st word. */
+ tmp &= BITMAP_FIRST_WORD_MASK(start);
+ start = round_down(start, BITS_PER_LONG);
+
+ while (!tmp) {
+ start += BITS_PER_LONG;
+ if (start >= nbits)
+ return nbits;
+
+ tmp = addr[start / BITS_PER_LONG] ^ invert;
+ }
+
+ return min(start + __ffs(tmp), nbits);
+}
+#endif
+
+#ifndef find_next_bit
+/*
+ * Find the next set bit in a memory region.
+ */
+unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
+ unsigned long offset)
+{
+ return _find_next_bit(addr, size, offset, 0UL);
+}
+#endif
+
+#ifndef find_first_bit
+/*
+ * Find the first set bit in a memory region.
+ */
+unsigned long find_first_bit(const unsigned long *addr, unsigned long size)
+{
+ unsigned long idx;
+
+ for (idx = 0; idx * BITS_PER_LONG < size; idx++) {
+ if (addr[idx])
+ return min(idx * BITS_PER_LONG + __ffs(addr[idx]), size);
+ }
+
+ return size;
+}
+#endif
diff --git a/tools/lib/lockdep/Makefile b/tools/lib/lockdep/Makefile
index 7e319afac78a..90d2baeb621a 100644
--- a/tools/lib/lockdep/Makefile
+++ b/tools/lib/lockdep/Makefile
@@ -149,7 +149,7 @@ install_lib: all_cmd
install: install_lib
clean:
- $(RM) *.o *~ $(TARGETS) *.a *liblockdep*.so* $(VERSION_FILES) .*.d
+ $(RM) *.o *~ $(TARGETS) *.a *liblockdep*.so* $(VERSION_FILES) .*.d .*.cmd
$(RM) tags TAGS
PHONY += force
diff --git a/tools/lib/string.c b/tools/lib/string.c
new file mode 100644
index 000000000000..bd239bc1d557
--- /dev/null
+++ b/tools/lib/string.c
@@ -0,0 +1,89 @@
+/*
+ * linux/tools/lib/string.c
+ *
+ * Copied from linux/lib/string.c, where it is:
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * More specifically, the first copied function was strtobool, which
+ * was introduced by:
+ *
+ * d0f1fed29e6e ("Add a strtobool function matching semantics of existing in kernel equivalents")
+ * Author: Jonathan Cameron <jic23@cam.ac.uk>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <linux/string.h>
+#include <linux/compiler.h>
+
+/**
+ * memdup - duplicate region of memory
+ *
+ * @src: memory region to duplicate
+ * @len: memory region length
+ */
+void *memdup(const void *src, size_t len)
+{
+ void *p = malloc(len);
+
+ if (p)
+ memcpy(p, src, len);
+
+ return p;
+}
+
+/**
+ * strtobool - convert common user inputs into boolean values
+ * @s: input string
+ * @res: result
+ *
+ * This routine returns 0 iff the first character is one of 'Yy1Nn0'.
+ * Otherwise it will return -EINVAL. Value pointed to by res is
+ * updated upon finding a match.
+ */
+int strtobool(const char *s, bool *res)
+{
+ switch (s[0]) {
+ case 'y':
+ case 'Y':
+ case '1':
+ *res = true;
+ break;
+ case 'n':
+ case 'N':
+ case '0':
+ *res = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * strlcpy - Copy a C-string into a sized buffer
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ * @size: size of destination buffer
+ *
+ * Compatible with *BSD: the result is always a valid
+ * NUL-terminated string that fits in the buffer (unless,
+ * of course, the buffer size is zero). It does not pad
+ * out the result like strncpy() does.
+ *
+ * If libc has strlcpy() then that version will override this
+ * implementation:
+ */
+size_t __weak strlcpy(char *dest, const char *src, size_t size)
+{
+ size_t ret = strlen(src);
+
+ if (size) {
+ size_t len = (ret >= size) ? size - 1 : ret;
+ memcpy(dest, src, len);
+ dest[len] = '\0';
+ }
+ return ret;
+}
diff --git a/tools/lib/subcmd/Build b/tools/lib/subcmd/Build
new file mode 100644
index 000000000000..ee31288788c1
--- /dev/null
+++ b/tools/lib/subcmd/Build
@@ -0,0 +1,7 @@
+libsubcmd-y += exec-cmd.o
+libsubcmd-y += help.o
+libsubcmd-y += pager.o
+libsubcmd-y += parse-options.o
+libsubcmd-y += run-command.o
+libsubcmd-y += sigchain.o
+libsubcmd-y += subcmd-config.o
diff --git a/tools/lib/subcmd/Makefile b/tools/lib/subcmd/Makefile
new file mode 100644
index 000000000000..629cf8c14e68
--- /dev/null
+++ b/tools/lib/subcmd/Makefile
@@ -0,0 +1,48 @@
+include ../../scripts/Makefile.include
+include ../../perf/config/utilities.mak # QUIET_CLEAN
+
+ifeq ($(srctree),)
+srctree := $(patsubst %/,%,$(dir $(shell pwd)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+#$(info Determined 'srctree' to be $(srctree))
+endif
+
+CC = $(CROSS_COMPILE)gcc
+AR = $(CROSS_COMPILE)ar
+RM = rm -f
+
+MAKEFLAGS += --no-print-directory
+
+LIBFILE = $(OUTPUT)libsubcmd.a
+
+CFLAGS := $(EXTRA_WARNINGS) $(EXTRA_CFLAGS)
+CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC
+CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
+
+CFLAGS += -I$(srctree)/tools/include/
+CFLAGS += -I$(srctree)/include/uapi
+CFLAGS += -I$(srctree)/include
+
+SUBCMD_IN := $(OUTPUT)libsubcmd-in.o
+
+all:
+
+export srctree OUTPUT CC LD CFLAGS V
+include $(srctree)/tools/build/Makefile.include
+
+all: fixdep $(LIBFILE)
+
+$(SUBCMD_IN): FORCE
+ @$(MAKE) $(build)=libsubcmd
+
+$(LIBFILE): $(SUBCMD_IN)
+ $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(SUBCMD_IN)
+
+clean:
+ $(call QUIET_CLEAN, libsubcmd) $(RM) $(LIBFILE); \
+ find $(if $(OUTPUT),$(OUTPUT),.) -name \*.o -or -name \*.o.cmd -or -name \*.o.d | xargs $(RM)
+
+FORCE:
+
+.PHONY: clean FORCE
diff --git a/tools/lib/subcmd/exec-cmd.c b/tools/lib/subcmd/exec-cmd.c
new file mode 100644
index 000000000000..1ae833af1a4a
--- /dev/null
+++ b/tools/lib/subcmd/exec-cmd.c
@@ -0,0 +1,209 @@
+#include <linux/compiler.h>
+#include <linux/string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "subcmd-util.h"
+#include "exec-cmd.h"
+#include "subcmd-config.h"
+
+#define MAX_ARGS 32
+#define PATH_MAX 4096
+
+static const char *argv_exec_path;
+static const char *argv0_path;
+
+void exec_cmd_init(const char *exec_name, const char *prefix,
+ const char *exec_path, const char *exec_path_env)
+{
+ subcmd_config.exec_name = exec_name;
+ subcmd_config.prefix = prefix;
+ subcmd_config.exec_path = exec_path;
+ subcmd_config.exec_path_env = exec_path_env;
+}
+
+#define is_dir_sep(c) ((c) == '/')
+
+static int is_absolute_path(const char *path)
+{
+ return path[0] == '/';
+}
+
+static const char *get_pwd_cwd(void)
+{
+ static char cwd[PATH_MAX + 1];
+ char *pwd;
+ struct stat cwd_stat, pwd_stat;
+ if (getcwd(cwd, PATH_MAX) == NULL)
+ return NULL;
+ pwd = getenv("PWD");
+ if (pwd && strcmp(pwd, cwd)) {
+ stat(cwd, &cwd_stat);
+ if (!stat(pwd, &pwd_stat) &&
+ pwd_stat.st_dev == cwd_stat.st_dev &&
+ pwd_stat.st_ino == cwd_stat.st_ino) {
+ strlcpy(cwd, pwd, PATH_MAX);
+ }
+ }
+ return cwd;
+}
+
+static const char *make_nonrelative_path(const char *path)
+{
+ static char buf[PATH_MAX + 1];
+
+ if (is_absolute_path(path)) {
+ if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
+ die("Too long path: %.*s", 60, path);
+ } else {
+ const char *cwd = get_pwd_cwd();
+ if (!cwd)
+ die("Cannot determine the current working directory");
+ if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX)
+ die("Too long path: %.*s", 60, path);
+ }
+ return buf;
+}
+
+char *system_path(const char *path)
+{
+ char *buf = NULL;
+
+ if (is_absolute_path(path))
+ return strdup(path);
+
+ astrcatf(&buf, "%s/%s", subcmd_config.prefix, path);
+
+ return buf;
+}
+
+const char *extract_argv0_path(const char *argv0)
+{
+ const char *slash;
+
+ if (!argv0 || !*argv0)
+ return NULL;
+ slash = argv0 + strlen(argv0);
+
+ while (argv0 <= slash && !is_dir_sep(*slash))
+ slash--;
+
+ if (slash >= argv0) {
+ argv0_path = strndup(argv0, slash - argv0);
+ return argv0_path ? slash + 1 : NULL;
+ }
+
+ return argv0;
+}
+
+void set_argv_exec_path(const char *exec_path)
+{
+ argv_exec_path = exec_path;
+ /*
+ * Propagate this setting to external programs.
+ */
+ setenv(subcmd_config.exec_path_env, exec_path, 1);
+}
+
+
+/* Returns the highest-priority location to look for subprograms. */
+char *get_argv_exec_path(void)
+{
+ char *env;
+
+ if (argv_exec_path)
+ return strdup(argv_exec_path);
+
+ env = getenv(subcmd_config.exec_path_env);
+ if (env && *env)
+ return strdup(env);
+
+ return system_path(subcmd_config.exec_path);
+}
+
+static void add_path(char **out, const char *path)
+{
+ if (path && *path) {
+ if (is_absolute_path(path))
+ astrcat(out, path);
+ else
+ astrcat(out, make_nonrelative_path(path));
+
+ astrcat(out, ":");
+ }
+}
+
+void setup_path(void)
+{
+ const char *old_path = getenv("PATH");
+ char *new_path = NULL;
+ char *tmp = get_argv_exec_path();
+
+ add_path(&new_path, tmp);
+ add_path(&new_path, argv0_path);
+ free(tmp);
+
+ if (old_path)
+ astrcat(&new_path, old_path);
+ else
+ astrcat(&new_path, "/usr/local/bin:/usr/bin:/bin");
+
+ setenv("PATH", new_path, 1);
+
+ free(new_path);
+}
+
+static const char **prepare_exec_cmd(const char **argv)
+{
+ int argc;
+ const char **nargv;
+
+ for (argc = 0; argv[argc]; argc++)
+ ; /* just counting */
+ nargv = malloc(sizeof(*nargv) * (argc + 2));
+
+ nargv[0] = subcmd_config.exec_name;
+ for (argc = 0; argv[argc]; argc++)
+ nargv[argc + 1] = argv[argc];
+ nargv[argc + 1] = NULL;
+ return nargv;
+}
+
+int execv_cmd(const char **argv) {
+ const char **nargv = prepare_exec_cmd(argv);
+
+ /* execvp() can only ever return if it fails */
+ execvp(subcmd_config.exec_name, (char **)nargv);
+
+ free(nargv);
+ return -1;
+}
+
+
+int execl_cmd(const char *cmd,...)
+{
+ int argc;
+ const char *argv[MAX_ARGS + 1];
+ const char *arg;
+ va_list param;
+
+ va_start(param, cmd);
+ argv[0] = cmd;
+ argc = 1;
+ while (argc < MAX_ARGS) {
+ arg = argv[argc++] = va_arg(param, char *);
+ if (!arg)
+ break;
+ }
+ va_end(param);
+ if (MAX_ARGS <= argc) {
+ fprintf(stderr, " Error: too many args to run %s\n", cmd);
+ return -1;
+ }
+
+ argv[argc] = NULL;
+ return execv_cmd(argv);
+}
diff --git a/tools/lib/subcmd/exec-cmd.h b/tools/lib/subcmd/exec-cmd.h
new file mode 100644
index 000000000000..5d08bda31d90
--- /dev/null
+++ b/tools/lib/subcmd/exec-cmd.h
@@ -0,0 +1,16 @@
+#ifndef __SUBCMD_EXEC_CMD_H
+#define __SUBCMD_EXEC_CMD_H
+
+extern void exec_cmd_init(const char *exec_name, const char *prefix,
+ const char *exec_path, const char *exec_path_env);
+
+extern void set_argv_exec_path(const char *exec_path);
+extern const char *extract_argv0_path(const char *path);
+extern void setup_path(void);
+extern int execv_cmd(const char **argv); /* NULL terminated */
+extern int execl_cmd(const char *cmd, ...);
+/* get_argv_exec_path and system_path return malloc'd string, caller must free it */
+extern char *get_argv_exec_path(void);
+extern char *system_path(const char *path);
+
+#endif /* __SUBCMD_EXEC_CMD_H */
diff --git a/tools/perf/util/help.c b/tools/lib/subcmd/help.c
index 86c37c472263..e228c3cb3716 100644
--- a/tools/perf/util/help.c
+++ b/tools/lib/subcmd/help.c
@@ -1,9 +1,15 @@
-#include "cache.h"
-#include "../builtin.h"
-#include "exec_cmd.h"
-#include "levenshtein.h"
-#include "help.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <termios.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include "subcmd-util.h"
+#include "help.h"
+#include "exec-cmd.h"
void add_cmdname(struct cmdnames *cmds, const char *name, size_t len)
{
@@ -17,7 +23,7 @@ void add_cmdname(struct cmdnames *cmds, const char *name, size_t len)
cmds->names[cmds->cnt++] = ent;
}
-static void clean_cmdnames(struct cmdnames *cmds)
+void clean_cmdnames(struct cmdnames *cmds)
{
unsigned int i;
@@ -28,14 +34,14 @@ static void clean_cmdnames(struct cmdnames *cmds)
cmds->alloc = 0;
}
-static int cmdname_compare(const void *a_, const void *b_)
+int cmdname_compare(const void *a_, const void *b_)
{
struct cmdname *a = *(struct cmdname **)a_;
struct cmdname *b = *(struct cmdname **)b_;
return strcmp(a->name, b->name);
}
-static void uniq(struct cmdnames *cmds)
+void uniq(struct cmdnames *cmds)
{
unsigned int i, j;
@@ -71,6 +77,28 @@ void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
cmds->cnt = cj;
}
+static void get_term_dimensions(struct winsize *ws)
+{
+ char *s = getenv("LINES");
+
+ if (s != NULL) {
+ ws->ws_row = atoi(s);
+ s = getenv("COLUMNS");
+ if (s != NULL) {
+ ws->ws_col = atoi(s);
+ if (ws->ws_row && ws->ws_col)
+ return;
+ }
+ }
+#ifdef TIOCGWINSZ
+ if (ioctl(1, TIOCGWINSZ, ws) == 0 &&
+ ws->ws_row && ws->ws_col)
+ return;
+#endif
+ ws->ws_row = 25;
+ ws->ws_col = 80;
+}
+
static void pretty_print_string_list(struct cmdnames *cmds, int longest)
{
int cols = 1, rows;
@@ -114,6 +142,14 @@ static int is_executable(const char *name)
return st.st_mode & S_IXUSR;
}
+static int has_extension(const char *filename, const char *ext)
+{
+ size_t len = strlen(filename);
+ size_t extlen = strlen(ext);
+
+ return len > extlen && !memcmp(filename + len - extlen, ext, extlen);
+}
+
static void list_commands_in_dir(struct cmdnames *cmds,
const char *path,
const char *prefix)
@@ -121,8 +157,7 @@ static void list_commands_in_dir(struct cmdnames *cmds,
int prefix_len;
DIR *dir = opendir(path);
struct dirent *de;
- struct strbuf buf = STRBUF_INIT;
- int len;
+ char *buf = NULL;
if (!dir)
return;
@@ -130,8 +165,7 @@ static void list_commands_in_dir(struct cmdnames *cmds,
prefix = "perf-";
prefix_len = strlen(prefix);
- strbuf_addf(&buf, "%s/", path);
- len = buf.len;
+ astrcatf(&buf, "%s/", path);
while ((de = readdir(dir)) != NULL) {
int entlen;
@@ -139,9 +173,8 @@ static void list_commands_in_dir(struct cmdnames *cmds,
if (prefixcmp(de->d_name, prefix))
continue;
- strbuf_setlen(&buf, len);
- strbuf_addstr(&buf, de->d_name);
- if (!is_executable(buf.buf))
+ astrcat(&buf, de->d_name);
+ if (!is_executable(buf))
continue;
entlen = strlen(de->d_name) - prefix_len;
@@ -151,7 +184,7 @@ static void list_commands_in_dir(struct cmdnames *cmds,
add_cmdname(cmds, de->d_name + prefix_len, entlen);
}
closedir(dir);
- strbuf_release(&buf);
+ free(buf);
}
void load_command_list(const char *prefix,
@@ -159,7 +192,7 @@ void load_command_list(const char *prefix,
struct cmdnames *other_cmds)
{
const char *env_path = getenv("PATH");
- const char *exec_path = perf_exec_path();
+ char *exec_path = get_argv_exec_path();
if (exec_path) {
list_commands_in_dir(main_cmds, exec_path, prefix);
@@ -172,7 +205,7 @@ void load_command_list(const char *prefix,
char *paths, *path, *colon;
path = paths = strdup(env_path);
while (1) {
- if ((colon = strchr(path, PATH_SEP)))
+ if ((colon = strchr(path, ':')))
*colon = 0;
if (!exec_path || strcmp(path, exec_path))
list_commands_in_dir(other_cmds, path, prefix);
@@ -187,6 +220,7 @@ void load_command_list(const char *prefix,
sizeof(*other_cmds->names), cmdname_compare);
uniq(other_cmds);
}
+ free(exec_path);
exclude_cmds(other_cmds, main_cmds);
}
@@ -203,13 +237,14 @@ void list_commands(const char *title, struct cmdnames *main_cmds,
longest = other_cmds->names[i]->len;
if (main_cmds->cnt) {
- const char *exec_path = perf_exec_path();
+ char *exec_path = get_argv_exec_path();
printf("available %s in '%s'\n", title, exec_path);
printf("----------------");
mput_char('-', strlen(title) + strlen(exec_path));
putchar('\n');
pretty_print_string_list(main_cmds, longest);
putchar('\n');
+ free(exec_path);
}
if (other_cmds->cnt) {
@@ -231,109 +266,3 @@ int is_in_cmdlist(struct cmdnames *c, const char *s)
return 1;
return 0;
}
-
-static int autocorrect;
-static struct cmdnames aliases;
-
-static int perf_unknown_cmd_config(const char *var, const char *value, void *cb)
-{
- if (!strcmp(var, "help.autocorrect"))
- autocorrect = perf_config_int(var,value);
- /* Also use aliases for command lookup */
- if (!prefixcmp(var, "alias."))
- add_cmdname(&aliases, var + 6, strlen(var + 6));
-
- return perf_default_config(var, value, cb);
-}
-
-static int levenshtein_compare(const void *p1, const void *p2)
-{
- const struct cmdname *const *c1 = p1, *const *c2 = p2;
- const char *s1 = (*c1)->name, *s2 = (*c2)->name;
- int l1 = (*c1)->len;
- int l2 = (*c2)->len;
- return l1 != l2 ? l1 - l2 : strcmp(s1, s2);
-}
-
-static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
-{
- unsigned int i;
-
- ALLOC_GROW(cmds->names, cmds->cnt + old->cnt, cmds->alloc);
-
- for (i = 0; i < old->cnt; i++)
- cmds->names[cmds->cnt++] = old->names[i];
- zfree(&old->names);
- old->cnt = 0;
-}
-
-const char *help_unknown_cmd(const char *cmd)
-{
- unsigned int i, n = 0, best_similarity = 0;
- struct cmdnames main_cmds, other_cmds;
-
- memset(&main_cmds, 0, sizeof(main_cmds));
- memset(&other_cmds, 0, sizeof(main_cmds));
- memset(&aliases, 0, sizeof(aliases));
-
- perf_config(perf_unknown_cmd_config, NULL);
-
- load_command_list("perf-", &main_cmds, &other_cmds);
-
- add_cmd_list(&main_cmds, &aliases);
- add_cmd_list(&main_cmds, &other_cmds);
- qsort(main_cmds.names, main_cmds.cnt,
- sizeof(main_cmds.names), cmdname_compare);
- uniq(&main_cmds);
-
- if (main_cmds.cnt) {
- /* This reuses cmdname->len for similarity index */
- for (i = 0; i < main_cmds.cnt; ++i)
- main_cmds.names[i]->len =
- levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4);
-
- qsort(main_cmds.names, main_cmds.cnt,
- sizeof(*main_cmds.names), levenshtein_compare);
-
- best_similarity = main_cmds.names[0]->len;
- n = 1;
- while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len)
- ++n;
- }
-
- if (autocorrect && n == 1) {
- const char *assumed = main_cmds.names[0]->name;
-
- main_cmds.names[0] = NULL;
- clean_cmdnames(&main_cmds);
- fprintf(stderr, "WARNING: You called a perf program named '%s', "
- "which does not exist.\n"
- "Continuing under the assumption that you meant '%s'\n",
- cmd, assumed);
- if (autocorrect > 0) {
- fprintf(stderr, "in %0.1f seconds automatically...\n",
- (float)autocorrect/10.0);
- poll(NULL, 0, autocorrect * 100);
- }
- return assumed;
- }
-
- fprintf(stderr, "perf: '%s' is not a perf-command. See 'perf --help'.\n", cmd);
-
- if (main_cmds.cnt && best_similarity < 6) {
- fprintf(stderr, "\nDid you mean %s?\n",
- n < 2 ? "this": "one of these");
-
- for (i = 0; i < n; i++)
- fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);
- }
-
- exit(1);
-}
-
-int cmd_version(int argc __maybe_unused, const char **argv __maybe_unused,
- const char *prefix __maybe_unused)
-{
- printf("perf version %s\n", perf_version_string);
- return 0;
-}
diff --git a/tools/perf/util/help.h b/tools/lib/subcmd/help.h
index 7f5c6dedd714..e145a020780c 100644
--- a/tools/perf/util/help.h
+++ b/tools/lib/subcmd/help.h
@@ -1,12 +1,14 @@
-#ifndef __PERF_HELP_H
-#define __PERF_HELP_H
+#ifndef __SUBCMD_HELP_H
+#define __SUBCMD_HELP_H
+
+#include <sys/types.h>
struct cmdnames {
size_t alloc;
size_t cnt;
struct cmdname {
size_t len; /* also used for similarity index in help.c */
- char name[FLEX_ARRAY];
+ char name[];
} **names;
};
@@ -20,10 +22,13 @@ void load_command_list(const char *prefix,
struct cmdnames *main_cmds,
struct cmdnames *other_cmds);
void add_cmdname(struct cmdnames *cmds, const char *name, size_t len);
+void clean_cmdnames(struct cmdnames *cmds);
+int cmdname_compare(const void *a, const void *b);
+void uniq(struct cmdnames *cmds);
/* Here we require that excludes is a sorted list. */
void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
int is_in_cmdlist(struct cmdnames *c, const char *s);
void list_commands(const char *title, struct cmdnames *main_cmds,
struct cmdnames *other_cmds);
-#endif /* __PERF_HELP_H */
+#endif /* __SUBCMD_HELP_H */
diff --git a/tools/perf/util/pager.c b/tools/lib/subcmd/pager.c
index 53ef006a951c..d50f3b58606b 100644
--- a/tools/perf/util/pager.c
+++ b/tools/lib/subcmd/pager.c
@@ -1,6 +1,12 @@
-#include "cache.h"
+#include <sys/select.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include "pager.h"
#include "run-command.h"
#include "sigchain.h"
+#include "subcmd-config.h"
/*
* This is split up from the rest of git so that we can do
@@ -9,6 +15,11 @@
static int spawned_pager;
+void pager_init(const char *pager_env)
+{
+ subcmd_config.pager_env = pager_env;
+}
+
static void pager_preexec(void)
{
/*
@@ -46,7 +57,7 @@ static void wait_for_pager_signal(int signo)
void setup_pager(void)
{
- const char *pager = getenv("PERF_PAGER");
+ const char *pager = getenv(subcmd_config.pager_env);
if (!isatty(1))
return;
@@ -85,11 +96,5 @@ void setup_pager(void)
int pager_in_use(void)
{
- const char *env;
-
- if (spawned_pager)
- return 1;
-
- env = getenv("PERF_PAGER_IN_USE");
- return env ? perf_config_bool("PERF_PAGER_IN_USE", env) : 0;
+ return spawned_pager;
}
diff --git a/tools/lib/subcmd/pager.h b/tools/lib/subcmd/pager.h
new file mode 100644
index 000000000000..8b83714ecf73
--- /dev/null
+++ b/tools/lib/subcmd/pager.h
@@ -0,0 +1,9 @@
+#ifndef __SUBCMD_PAGER_H
+#define __SUBCMD_PAGER_H
+
+extern void pager_init(const char *pager_env);
+
+extern void setup_pager(void);
+extern int pager_in_use(void);
+
+#endif /* __SUBCMD_PAGER_H */
diff --git a/tools/perf/util/parse-options.c b/tools/lib/subcmd/parse-options.c
index 9fca09296eb3..981bb4481fd5 100644
--- a/tools/perf/util/parse-options.c
+++ b/tools/lib/subcmd/parse-options.c
@@ -1,37 +1,66 @@
-#include "util.h"
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <ctype.h>
+#include "subcmd-util.h"
#include "parse-options.h"
-#include "cache.h"
-#include "header.h"
-#include <linux/string.h>
+#include "subcmd-config.h"
+#include "pager.h"
#define OPT_SHORT 1
#define OPT_UNSET 2
-static struct strbuf error_buf = STRBUF_INIT;
+char *error_buf;
static int opterror(const struct option *opt, const char *reason, int flags)
{
if (flags & OPT_SHORT)
- return error("switch `%c' %s", opt->short_name, reason);
- if (flags & OPT_UNSET)
- return error("option `no-%s' %s", opt->long_name, reason);
- return error("option `%s' %s", opt->long_name, reason);
+ fprintf(stderr, " Error: switch `%c' %s", opt->short_name, reason);
+ else if (flags & OPT_UNSET)
+ fprintf(stderr, " Error: option `no-%s' %s", opt->long_name, reason);
+ else
+ fprintf(stderr, " Error: option `%s' %s", opt->long_name, reason);
+
+ return -1;
+}
+
+static const char *skip_prefix(const char *str, const char *prefix)
+{
+ size_t len = strlen(prefix);
+ return strncmp(str, prefix, len) ? NULL : str + len;
+}
+
+static void optwarning(const struct option *opt, const char *reason, int flags)
+{
+ if (flags & OPT_SHORT)
+ fprintf(stderr, " Warning: switch `%c' %s", opt->short_name, reason);
+ else if (flags & OPT_UNSET)
+ fprintf(stderr, " Warning: option `no-%s' %s", opt->long_name, reason);
+ else
+ fprintf(stderr, " Warning: option `%s' %s", opt->long_name, reason);
}
static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,
int flags, const char **arg)
{
+ const char *res;
+
if (p->opt) {
- *arg = p->opt;
+ res = p->opt;
p->opt = NULL;
} else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 ||
**(p->argv + 1) == '-')) {
- *arg = (const char *)opt->defval;
+ res = (const char *)opt->defval;
} else if (p->argc > 1) {
p->argc--;
- *arg = *++p->argv;
+ res = *++p->argv;
} else
return opterror(opt, "requires a value", flags);
+ if (arg)
+ *arg = res;
return 0;
}
@@ -55,11 +84,11 @@ static int get_value(struct parse_opt_ctx_t *p,
if (((flags & OPT_SHORT) && p->excl_opt->short_name) ||
p->excl_opt->long_name == NULL) {
- scnprintf(msg, sizeof(msg), "cannot be used with switch `%c'",
- p->excl_opt->short_name);
+ snprintf(msg, sizeof(msg), "cannot be used with switch `%c'",
+ p->excl_opt->short_name);
} else {
- scnprintf(msg, sizeof(msg), "cannot be used with %s",
- p->excl_opt->long_name);
+ snprintf(msg, sizeof(msg), "cannot be used with %s",
+ p->excl_opt->long_name);
}
opterror(opt, msg, flags);
return -3;
@@ -91,6 +120,64 @@ static int get_value(struct parse_opt_ctx_t *p,
}
}
+ if (opt->flags & PARSE_OPT_NOBUILD) {
+ char reason[128];
+ bool noarg = false;
+
+ err = snprintf(reason, sizeof(reason),
+ opt->flags & PARSE_OPT_CANSKIP ?
+ "is being ignored because %s " :
+ "is not available because %s",
+ opt->build_opt);
+ reason[sizeof(reason) - 1] = '\0';
+
+ if (err < 0)
+ strncpy(reason, opt->flags & PARSE_OPT_CANSKIP ?
+ "is being ignored" :
+ "is not available",
+ sizeof(reason));
+
+ if (!(opt->flags & PARSE_OPT_CANSKIP))
+ return opterror(opt, reason, flags);
+
+ err = 0;
+ if (unset)
+ noarg = true;
+ if (opt->flags & PARSE_OPT_NOARG)
+ noarg = true;
+ if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
+ noarg = true;
+
+ switch (opt->type) {
+ case OPTION_BOOLEAN:
+ case OPTION_INCR:
+ case OPTION_BIT:
+ case OPTION_SET_UINT:
+ case OPTION_SET_PTR:
+ case OPTION_END:
+ case OPTION_ARGUMENT:
+ case OPTION_GROUP:
+ noarg = true;
+ break;
+ case OPTION_CALLBACK:
+ case OPTION_STRING:
+ case OPTION_INTEGER:
+ case OPTION_UINTEGER:
+ case OPTION_LONG:
+ case OPTION_U64:
+ default:
+ break;
+ }
+
+ if (!noarg)
+ err = get_arg(p, opt, flags, NULL);
+ if (err)
+ return err;
+
+ optwarning(opt, reason, flags);
+ return 0;
+ }
+
switch (opt->type) {
case OPTION_BIT:
if (unset)
@@ -327,14 +414,16 @@ match:
return get_value(p, options, flags);
}
- if (ambiguous_option)
- return error("Ambiguous option: %s "
- "(could be --%s%s or --%s%s)",
- arg,
- (ambiguous_flags & OPT_UNSET) ? "no-" : "",
- ambiguous_option->long_name,
- (abbrev_flags & OPT_UNSET) ? "no-" : "",
- abbrev_option->long_name);
+ if (ambiguous_option) {
+ fprintf(stderr,
+ " Error: Ambiguous option: %s (could be --%s%s or --%s%s)",
+ arg,
+ (ambiguous_flags & OPT_UNSET) ? "no-" : "",
+ ambiguous_option->long_name,
+ (abbrev_flags & OPT_UNSET) ? "no-" : "",
+ abbrev_option->long_name);
+ return -1;
+ }
if (abbrev_option)
return get_value(p, abbrev_option, abbrev_flags);
return -2;
@@ -346,7 +435,7 @@ static void check_typos(const char *arg, const struct option *options)
return;
if (!prefixcmp(arg, "no-")) {
- error ("did you mean `--%s` (with two dashes ?)", arg);
+ fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)", arg);
exit(129);
}
@@ -354,14 +443,14 @@ static void check_typos(const char *arg, const struct option *options)
if (!options->long_name)
continue;
if (!prefixcmp(options->long_name, arg)) {
- error ("did you mean `--%s` (with two dashes ?)", arg);
+ fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)", arg);
exit(129);
}
}
}
-void parse_options_start(struct parse_opt_ctx_t *ctx,
- int argc, const char **argv, int flags)
+static void parse_options_start(struct parse_opt_ctx_t *ctx,
+ int argc, const char **argv, int flags)
{
memset(ctx, 0, sizeof(*ctx));
ctx->argc = argc - 1;
@@ -378,9 +467,9 @@ static int usage_with_options_internal(const char * const *,
const struct option *, int,
struct parse_opt_ctx_t *);
-int parse_options_step(struct parse_opt_ctx_t *ctx,
- const struct option *options,
- const char * const usagestr[])
+static int parse_options_step(struct parse_opt_ctx_t *ctx,
+ const struct option *options,
+ const char * const usagestr[])
{
int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
int excl_short_opt = 1;
@@ -489,7 +578,7 @@ exclusive:
return PARSE_OPT_HELP;
}
-int parse_options_end(struct parse_opt_ctx_t *ctx)
+static int parse_options_end(struct parse_opt_ctx_t *ctx)
{
memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out));
ctx->out[ctx->cpidx + ctx->argc] = NULL;
@@ -501,22 +590,20 @@ int parse_options_subcommand(int argc, const char **argv, const struct option *o
{
struct parse_opt_ctx_t ctx;
- perf_env__set_cmdline(&perf_env, argc, argv);
-
/* build usage string if it's not provided */
if (subcommands && !usagestr[0]) {
- struct strbuf buf = STRBUF_INIT;
+ char *buf = NULL;
+
+ astrcatf(&buf, "%s %s [<options>] {", subcmd_config.exec_name, argv[0]);
- strbuf_addf(&buf, "perf %s [<options>] {", argv[0]);
for (int i = 0; subcommands[i]; i++) {
if (i)
- strbuf_addstr(&buf, "|");
- strbuf_addstr(&buf, subcommands[i]);
+ astrcat(&buf, "|");
+ astrcat(&buf, subcommands[i]);
}
- strbuf_addstr(&buf, "}");
+ astrcat(&buf, "}");
- usagestr[0] = strdup(buf.buf);
- strbuf_release(&buf);
+ usagestr[0] = buf;
}
parse_options_start(&ctx, argc, argv, flags);
@@ -541,13 +628,11 @@ int parse_options_subcommand(int argc, const char **argv, const struct option *o
putchar('\n');
exit(130);
default: /* PARSE_OPT_UNKNOWN */
- if (ctx.argv[0][1] == '-') {
- strbuf_addf(&error_buf, "unknown option `%s'",
- ctx.argv[0] + 2);
- } else {
- strbuf_addf(&error_buf, "unknown switch `%c'",
- *ctx.opt);
- }
+ if (ctx.argv[0][1] == '-')
+ astrcatf(&error_buf, "unknown option `%s'",
+ ctx.argv[0] + 2);
+ else
+ astrcatf(&error_buf, "unknown switch `%c'", *ctx.opt);
usage_with_options(usagestr, options);
}
@@ -647,6 +732,10 @@ static void print_option_help(const struct option *opts, int full)
pad = USAGE_OPTS_WIDTH;
}
fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
+ if (opts->flags & PARSE_OPT_NOBUILD)
+ fprintf(stderr, "%*s(not built-in because %s)\n",
+ USAGE_OPTS_WIDTH + USAGE_GAP, "",
+ opts->build_opt);
}
static int option__cmp(const void *va, const void *vb)
@@ -672,16 +761,18 @@ static int option__cmp(const void *va, const void *vb)
static struct option *options__order(const struct option *opts)
{
- int nr_opts = 0;
+ int nr_opts = 0, len;
const struct option *o = opts;
struct option *ordered;
for (o = opts; o->type != OPTION_END; o++)
++nr_opts;
- ordered = memdup(opts, sizeof(*o) * (nr_opts + 1));
- if (ordered == NULL)
+ len = sizeof(*o) * (nr_opts + 1);
+ ordered = malloc(len);
+ if (!ordered)
goto out;
+ memcpy(ordered, opts, len);
qsort(ordered, nr_opts, sizeof(*o), option__cmp);
out:
@@ -719,9 +810,9 @@ static bool option__in_argv(const struct option *opt, const struct parse_opt_ctx
return false;
}
-int usage_with_options_internal(const char * const *usagestr,
- const struct option *opts, int full,
- struct parse_opt_ctx_t *ctx)
+static int usage_with_options_internal(const char * const *usagestr,
+ const struct option *opts, int full,
+ struct parse_opt_ctx_t *ctx)
{
struct option *ordered;
@@ -730,9 +821,9 @@ int usage_with_options_internal(const char * const *usagestr,
setup_pager();
- if (strbuf_avail(&error_buf)) {
- fprintf(stderr, " Error: %s\n", error_buf.buf);
- strbuf_release(&error_buf);
+ if (error_buf) {
+ fprintf(stderr, " Error: %s\n", error_buf);
+ zfree(&error_buf);
}
fprintf(stderr, "\n Usage: %s\n", *usagestr++);
@@ -768,7 +859,6 @@ int usage_with_options_internal(const char * const *usagestr,
void usage_with_options(const char * const *usagestr,
const struct option *opts)
{
- exit_browser(false);
usage_with_options_internal(usagestr, opts, 0, NULL);
exit(129);
}
@@ -777,13 +867,15 @@ void usage_with_options_msg(const char * const *usagestr,
const struct option *opts, const char *fmt, ...)
{
va_list ap;
-
- exit_browser(false);
+ char *tmp = error_buf;
va_start(ap, fmt);
- strbuf_addv(&error_buf, fmt, ap);
+ if (vasprintf(&error_buf, fmt, ap) == -1)
+ die("vasprintf failed");
va_end(ap);
+ free(tmp);
+
usage_with_options_internal(usagestr, opts, 0, NULL);
exit(129);
}
@@ -853,15 +945,39 @@ int parse_opt_verbosity_cb(const struct option *opt,
return 0;
}
-void set_option_flag(struct option *opts, int shortopt, const char *longopt,
- int flag)
+static struct option *
+find_option(struct option *opts, int shortopt, const char *longopt)
{
for (; opts->type != OPTION_END; opts++) {
if ((shortopt && opts->short_name == shortopt) ||
(opts->long_name && longopt &&
- !strcmp(opts->long_name, longopt))) {
- opts->flags |= flag;
- break;
- }
+ !strcmp(opts->long_name, longopt)))
+ return opts;
}
+ return NULL;
+}
+
+void set_option_flag(struct option *opts, int shortopt, const char *longopt,
+ int flag)
+{
+ struct option *opt = find_option(opts, shortopt, longopt);
+
+ if (opt)
+ opt->flags |= flag;
+ return;
+}
+
+void set_option_nobuild(struct option *opts, int shortopt,
+ const char *longopt,
+ const char *build_opt,
+ bool can_skip)
+{
+ struct option *opt = find_option(opts, shortopt, longopt);
+
+ if (!opt)
+ return;
+
+ opt->flags |= PARSE_OPT_NOBUILD;
+ opt->flags |= can_skip ? PARSE_OPT_CANSKIP : 0;
+ opt->build_opt = build_opt;
}
diff --git a/tools/perf/util/parse-options.h b/tools/lib/subcmd/parse-options.h
index a8e407bc251e..d60cab2726da 100644
--- a/tools/perf/util/parse-options.h
+++ b/tools/lib/subcmd/parse-options.h
@@ -1,8 +1,12 @@
-#ifndef __PERF_PARSE_OPTIONS_H
-#define __PERF_PARSE_OPTIONS_H
+#ifndef __SUBCMD_PARSE_OPTIONS_H
+#define __SUBCMD_PARSE_OPTIONS_H
-#include <linux/kernel.h>
#include <stdbool.h>
+#include <stdint.h>
+
+#ifndef NORETURN
+#define NORETURN __attribute__((__noreturn__))
+#endif
enum parse_opt_type {
/* special types */
@@ -41,6 +45,8 @@ enum parse_opt_option_flags {
PARSE_OPT_DISABLED = 32,
PARSE_OPT_EXCLUSIVE = 64,
PARSE_OPT_NOEMPTY = 128,
+ PARSE_OPT_NOBUILD = 256,
+ PARSE_OPT_CANSKIP = 512,
};
struct option;
@@ -96,6 +102,7 @@ struct option {
void *value;
const char *argh;
const char *help;
+ const char *build_opt;
int flags;
parse_opt_cb *callback;
@@ -149,6 +156,9 @@ struct option {
/* parse_options() will filter out the processed options and leave the
* non-option argments in argv[].
* Returns the number of arguments left in argv[].
+ *
+ * NOTE: parse_options() and parse_options_subcommand() may call exit() in the
+ * case of an error (or for 'special' options like --list-cmds or --list-opts).
*/
extern int parse_options(int argc, const char **argv,
const struct option *options,
@@ -195,15 +205,6 @@ extern int parse_options_usage(const char * const *usagestr,
const char *optstr,
bool short_opt);
-extern void parse_options_start(struct parse_opt_ctx_t *ctx,
- int argc, const char **argv, int flags);
-
-extern int parse_options_step(struct parse_opt_ctx_t *ctx,
- const struct option *options,
- const char * const usagestr[]);
-
-extern int parse_options_end(struct parse_opt_ctx_t *ctx);
-
/*----- some often used options -----*/
extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
@@ -226,4 +227,7 @@ extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
extern const char *parse_options_fix_filename(const char *prefix, const char *file);
void set_option_flag(struct option *opts, int sopt, const char *lopt, int flag);
-#endif /* __PERF_PARSE_OPTIONS_H */
+void set_option_nobuild(struct option *opts, int shortopt, const char *longopt,
+ const char *build_opt, bool can_skip);
+
+#endif /* __SUBCMD_PARSE_OPTIONS_H */
diff --git a/tools/perf/util/run-command.c b/tools/lib/subcmd/run-command.c
index 34622b53e733..f4f6c9eb8e59 100644
--- a/tools/perf/util/run-command.c
+++ b/tools/lib/subcmd/run-command.c
@@ -1,7 +1,15 @@
-#include "cache.h"
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include "subcmd-util.h"
#include "run-command.h"
-#include "exec_cmd.h"
-#include "debug.h"
+#include "exec-cmd.h"
+
+#define STRERR_BUFSIZE 128
static inline void close_pair(int fd[2])
{
@@ -112,8 +120,8 @@ int start_command(struct child_process *cmd)
}
if (cmd->preexec_cb)
cmd->preexec_cb();
- if (cmd->perf_cmd) {
- execv_perf_cmd(cmd->argv);
+ if (cmd->exec_cmd) {
+ execv_cmd(cmd->argv);
} else {
execvp(cmd->argv[0], (char *const*) cmd->argv);
}
@@ -164,8 +172,8 @@ static int wait_or_whine(pid_t pid)
if (waiting < 0) {
if (errno == EINTR)
continue;
- error("waitpid failed (%s)",
- strerror_r(errno, sbuf, sizeof(sbuf)));
+ fprintf(stderr, " Error: waitpid failed (%s)",
+ strerror_r(errno, sbuf, sizeof(sbuf)));
return -ERR_RUN_COMMAND_WAITPID;
}
if (waiting != pid)
@@ -207,7 +215,7 @@ static void prepare_run_command_v_opt(struct child_process *cmd,
memset(cmd, 0, sizeof(*cmd));
cmd->argv = argv;
cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
- cmd->perf_cmd = opt & RUN_PERF_CMD ? 1 : 0;
+ cmd->exec_cmd = opt & RUN_EXEC_CMD ? 1 : 0;
cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
}
diff --git a/tools/perf/util/run-command.h b/tools/lib/subcmd/run-command.h
index 1ef264d5069c..fe2befea1e73 100644
--- a/tools/perf/util/run-command.h
+++ b/tools/lib/subcmd/run-command.h
@@ -1,5 +1,7 @@
-#ifndef __PERF_RUN_COMMAND_H
-#define __PERF_RUN_COMMAND_H
+#ifndef __SUBCMD_RUN_COMMAND_H
+#define __SUBCMD_RUN_COMMAND_H
+
+#include <unistd.h>
enum {
ERR_RUN_COMMAND_FORK = 10000,
@@ -41,7 +43,7 @@ struct child_process {
unsigned no_stdin:1;
unsigned no_stdout:1;
unsigned no_stderr:1;
- unsigned perf_cmd:1; /* if this is to be perf sub-command */
+ unsigned exec_cmd:1; /* if this is to be external sub-command */
unsigned stdout_to_stderr:1;
void (*preexec_cb)(void);
};
@@ -51,8 +53,8 @@ int finish_command(struct child_process *);
int run_command(struct child_process *);
#define RUN_COMMAND_NO_STDIN 1
-#define RUN_PERF_CMD 2 /*If this is to be perf sub-command */
+#define RUN_EXEC_CMD 2 /*If this is to be external sub-command */
#define RUN_COMMAND_STDOUT_TO_STDERR 4
int run_command_v_opt(const char **argv, int opt);
-#endif /* __PERF_RUN_COMMAND_H */
+#endif /* __SUBCMD_RUN_COMMAND_H */
diff --git a/tools/perf/util/sigchain.c b/tools/lib/subcmd/sigchain.c
index ba785e9b1841..3537c348a18e 100644
--- a/tools/perf/util/sigchain.c
+++ b/tools/lib/subcmd/sigchain.c
@@ -1,5 +1,6 @@
+#include <signal.h>
+#include "subcmd-util.h"
#include "sigchain.h"
-#include "cache.h"
#define SIGCHAIN_MAX_SIGNALS 32
diff --git a/tools/perf/util/sigchain.h b/tools/lib/subcmd/sigchain.h
index 959d64eb5557..0c919f2874ca 100644
--- a/tools/perf/util/sigchain.h
+++ b/tools/lib/subcmd/sigchain.h
@@ -1,5 +1,5 @@
-#ifndef __PERF_SIGCHAIN_H
-#define __PERF_SIGCHAIN_H
+#ifndef __SUBCMD_SIGCHAIN_H
+#define __SUBCMD_SIGCHAIN_H
typedef void (*sigchain_fun)(int);
@@ -7,4 +7,4 @@ int sigchain_pop(int sig);
void sigchain_push_common(sigchain_fun f);
-#endif /* __PERF_SIGCHAIN_H */
+#endif /* __SUBCMD_SIGCHAIN_H */
diff --git a/tools/lib/subcmd/subcmd-config.c b/tools/lib/subcmd/subcmd-config.c
new file mode 100644
index 000000000000..d017c728bd1b
--- /dev/null
+++ b/tools/lib/subcmd/subcmd-config.c
@@ -0,0 +1,11 @@
+#include "subcmd-config.h"
+
+#define UNDEFINED "SUBCMD_HAS_NOT_BEEN_INITIALIZED"
+
+struct subcmd_config subcmd_config = {
+ .exec_name = UNDEFINED,
+ .prefix = UNDEFINED,
+ .exec_path = UNDEFINED,
+ .exec_path_env = UNDEFINED,
+ .pager_env = UNDEFINED,
+};
diff --git a/tools/lib/subcmd/subcmd-config.h b/tools/lib/subcmd/subcmd-config.h
new file mode 100644
index 000000000000..cc8514030b5c
--- /dev/null
+++ b/tools/lib/subcmd/subcmd-config.h
@@ -0,0 +1,14 @@
+#ifndef __PERF_SUBCMD_CONFIG_H
+#define __PERF_SUBCMD_CONFIG_H
+
+struct subcmd_config {
+ const char *exec_name;
+ const char *prefix;
+ const char *exec_path;
+ const char *exec_path_env;
+ const char *pager_env;
+};
+
+extern struct subcmd_config subcmd_config;
+
+#endif /* __PERF_SUBCMD_CONFIG_H */
diff --git a/tools/lib/subcmd/subcmd-util.h b/tools/lib/subcmd/subcmd-util.h
new file mode 100644
index 000000000000..fc2e45d8aaf1
--- /dev/null
+++ b/tools/lib/subcmd/subcmd-util.h
@@ -0,0 +1,91 @@
+#ifndef __SUBCMD_UTIL_H
+#define __SUBCMD_UTIL_H
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define NORETURN __attribute__((__noreturn__))
+
+static inline void report(const char *prefix, const char *err, va_list params)
+{
+ char msg[1024];
+ vsnprintf(msg, sizeof(msg), err, params);
+ fprintf(stderr, " %s%s\n", prefix, msg);
+}
+
+static NORETURN inline void die(const char *err, ...)
+{
+ va_list params;
+
+ va_start(params, err);
+ report(" Fatal: ", err, params);
+ exit(128);
+ va_end(params);
+}
+
+#define zfree(ptr) ({ free(*ptr); *ptr = NULL; })
+
+#define alloc_nr(x) (((x)+16)*3/2)
+
+/*
+ * Realloc the buffer pointed at by variable 'x' so that it can hold
+ * at least 'nr' entries; the number of entries currently allocated
+ * is 'alloc', using the standard growing factor alloc_nr() macro.
+ *
+ * DO NOT USE any expression with side-effect for 'x' or 'alloc'.
+ */
+#define ALLOC_GROW(x, nr, alloc) \
+ do { \
+ if ((nr) > alloc) { \
+ if (alloc_nr(alloc) < (nr)) \
+ alloc = (nr); \
+ else \
+ alloc = alloc_nr(alloc); \
+ x = xrealloc((x), alloc * sizeof(*(x))); \
+ } \
+ } while(0)
+
+static inline void *xrealloc(void *ptr, size_t size)
+{
+ void *ret = realloc(ptr, size);
+ if (!ret && !size)
+ ret = realloc(ptr, 1);
+ if (!ret) {
+ ret = realloc(ptr, size);
+ if (!ret && !size)
+ ret = realloc(ptr, 1);
+ if (!ret)
+ die("Out of memory, realloc failed");
+ }
+ return ret;
+}
+
+#define astrcatf(out, fmt, ...) \
+({ \
+ char *tmp = *(out); \
+ if (asprintf((out), "%s" fmt, tmp ?: "", ## __VA_ARGS__) == -1) \
+ die("asprintf failed"); \
+ free(tmp); \
+})
+
+static inline void astrcat(char **out, const char *add)
+{
+ char *tmp = *out;
+
+ if (asprintf(out, "%s%s", tmp ?: "", add) == -1)
+ die("asprintf failed");
+
+ free(tmp);
+}
+
+static inline int prefixcmp(const char *str, const char *prefix)
+{
+ for (; ; str++, prefix++)
+ if (!*prefix)
+ return 0;
+ else if (*str != *prefix)
+ return (unsigned char)*prefix - (unsigned char)*str;
+}
+
+#endif /* __SUBCMD_UTIL_H */
diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c
index 2a912df6771b..ea69ce35e902 100644
--- a/tools/lib/traceevent/event-parse.c
+++ b/tools/lib/traceevent/event-parse.c
@@ -4735,73 +4735,80 @@ static int is_printable_array(char *p, unsigned int len)
return 1;
}
-static void print_event_fields(struct trace_seq *s, void *data,
- int size __maybe_unused,
- struct event_format *event)
+void pevent_print_field(struct trace_seq *s, void *data,
+ struct format_field *field)
{
- struct format_field *field;
unsigned long long val;
unsigned int offset, len, i;
-
- field = event->format.fields;
- while (field) {
- trace_seq_printf(s, " %s=", field->name);
- if (field->flags & FIELD_IS_ARRAY) {
- offset = field->offset;
- len = field->size;
- if (field->flags & FIELD_IS_DYNAMIC) {
- val = pevent_read_number(event->pevent, data + offset, len);
- offset = val;
- len = offset >> 16;
- offset &= 0xffff;
- }
- if (field->flags & FIELD_IS_STRING &&
- is_printable_array(data + offset, len)) {
- trace_seq_printf(s, "%s", (char *)data + offset);
- } else {
- trace_seq_puts(s, "ARRAY[");
- for (i = 0; i < len; i++) {
- if (i)
- trace_seq_puts(s, ", ");
- trace_seq_printf(s, "%02x",
- *((unsigned char *)data + offset + i));
- }
- trace_seq_putc(s, ']');
- field->flags &= ~FIELD_IS_STRING;
- }
+ struct pevent *pevent = field->event->pevent;
+
+ if (field->flags & FIELD_IS_ARRAY) {
+ offset = field->offset;
+ len = field->size;
+ if (field->flags & FIELD_IS_DYNAMIC) {
+ val = pevent_read_number(pevent, data + offset, len);
+ offset = val;
+ len = offset >> 16;
+ offset &= 0xffff;
+ }
+ if (field->flags & FIELD_IS_STRING &&
+ is_printable_array(data + offset, len)) {
+ trace_seq_printf(s, "%s", (char *)data + offset);
} else {
- val = pevent_read_number(event->pevent, data + field->offset,
- field->size);
- if (field->flags & FIELD_IS_POINTER) {
- trace_seq_printf(s, "0x%llx", val);
- } else if (field->flags & FIELD_IS_SIGNED) {
- switch (field->size) {
- case 4:
- /*
- * If field is long then print it in hex.
- * A long usually stores pointers.
- */
- if (field->flags & FIELD_IS_LONG)
- trace_seq_printf(s, "0x%x", (int)val);
- else
- trace_seq_printf(s, "%d", (int)val);
- break;
- case 2:
- trace_seq_printf(s, "%2d", (short)val);
- break;
- case 1:
- trace_seq_printf(s, "%1d", (char)val);
- break;
- default:
- trace_seq_printf(s, "%lld", val);
- }
- } else {
+ trace_seq_puts(s, "ARRAY[");
+ for (i = 0; i < len; i++) {
+ if (i)
+ trace_seq_puts(s, ", ");
+ trace_seq_printf(s, "%02x",
+ *((unsigned char *)data + offset + i));
+ }
+ trace_seq_putc(s, ']');
+ field->flags &= ~FIELD_IS_STRING;
+ }
+ } else {
+ val = pevent_read_number(pevent, data + field->offset,
+ field->size);
+ if (field->flags & FIELD_IS_POINTER) {
+ trace_seq_printf(s, "0x%llx", val);
+ } else if (field->flags & FIELD_IS_SIGNED) {
+ switch (field->size) {
+ case 4:
+ /*
+ * If field is long then print it in hex.
+ * A long usually stores pointers.
+ */
if (field->flags & FIELD_IS_LONG)
- trace_seq_printf(s, "0x%llx", val);
+ trace_seq_printf(s, "0x%x", (int)val);
else
- trace_seq_printf(s, "%llu", val);
+ trace_seq_printf(s, "%d", (int)val);
+ break;
+ case 2:
+ trace_seq_printf(s, "%2d", (short)val);
+ break;
+ case 1:
+ trace_seq_printf(s, "%1d", (char)val);
+ break;
+ default:
+ trace_seq_printf(s, "%lld", val);
}
+ } else {
+ if (field->flags & FIELD_IS_LONG)
+ trace_seq_printf(s, "0x%llx", val);
+ else
+ trace_seq_printf(s, "%llu", val);
}
+ }
+}
+
+void pevent_print_fields(struct trace_seq *s, void *data,
+ int size __maybe_unused, struct event_format *event)
+{
+ struct format_field *field;
+
+ field = event->format.fields;
+ while (field) {
+ trace_seq_printf(s, " %s=", field->name);
+ pevent_print_field(s, data, field);
field = field->next;
}
}
@@ -4827,7 +4834,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
if (event->flags & EVENT_FL_FAILED) {
trace_seq_printf(s, "[FAILED TO PARSE]");
- print_event_fields(s, data, size, event);
+ pevent_print_fields(s, data, size, event);
return;
}
@@ -4968,13 +4975,12 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
sizeof(long) != 8) {
char *p;
- ls = 2;
/* make %l into %ll */
- p = strchr(format, 'l');
- if (p)
+ if (ls == 1 && (p = strchr(format, 'l')))
memmove(p+1, p, strlen(p)+1);
else if (strcmp(format, "%p") == 0)
strcpy(format, "0x%llx");
+ ls = 2;
}
switch (ls) {
case -2:
@@ -5302,7 +5308,7 @@ void pevent_event_info(struct trace_seq *s, struct event_format *event,
int print_pretty = 1;
if (event->pevent->print_raw || (event->flags & EVENT_FL_PRINTRAW))
- print_event_fields(s, record->data, record->size, event);
+ pevent_print_fields(s, record->data, record->size, event);
else {
if (event->handler && !(event->flags & EVENT_FL_NOHANDLE))
diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h
index 6fc83c7edbe9..706d9bc24066 100644
--- a/tools/lib/traceevent/event-parse.h
+++ b/tools/lib/traceevent/event-parse.h
@@ -705,6 +705,10 @@ struct cmdline *pevent_data_pid_from_comm(struct pevent *pevent, const char *com
struct cmdline *next);
int pevent_cmdline_pid(struct pevent *pevent, struct cmdline *cmdline);
+void pevent_print_field(struct trace_seq *s, void *data,
+ struct format_field *field);
+void pevent_print_fields(struct trace_seq *s, void *data,
+ int size __maybe_unused, struct event_format *event);
void pevent_event_info(struct trace_seq *s, struct event_format *event,
struct pevent_record *record);
int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum,
diff --git a/tools/lib/util/find_next_bit.c b/tools/lib/util/find_next_bit.c
deleted file mode 100644
index 41b44f65a79e..000000000000
--- a/tools/lib/util/find_next_bit.c
+++ /dev/null
@@ -1,89 +0,0 @@
-/* find_next_bit.c: fallback find next bit implementation
- *
- * Copied from lib/find_next_bit.c to tools/lib/next_bit.c
- *
- * Copyright (C) 2004 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 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 <asm/types.h>
-#include <asm/byteorder.h>
-
-#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG)
-
-#ifndef find_next_bit
-/*
- * Find the next set bit in a memory region.
- */
-unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
- unsigned long offset)
-{
- const unsigned long *p = addr + BITOP_WORD(offset);
- unsigned long result = offset & ~(BITS_PER_LONG-1);
- unsigned long tmp;
-
- if (offset >= size)
- return size;
- size -= result;
- offset %= BITS_PER_LONG;
- if (offset) {
- tmp = *(p++);
- tmp &= (~0UL << offset);
- if (size < BITS_PER_LONG)
- goto found_first;
- if (tmp)
- goto found_middle;
- size -= BITS_PER_LONG;
- result += BITS_PER_LONG;
- }
- while (size & ~(BITS_PER_LONG-1)) {
- if ((tmp = *(p++)))
- goto found_middle;
- result += BITS_PER_LONG;
- size -= BITS_PER_LONG;
- }
- if (!size)
- return result;
- tmp = *p;
-
-found_first:
- tmp &= (~0UL >> (BITS_PER_LONG - size));
- if (tmp == 0UL) /* Are any bits set? */
- return result + size; /* Nope. */
-found_middle:
- return result + __ffs(tmp);
-}
-#endif
-
-#ifndef find_first_bit
-/*
- * Find the first set bit in a memory region.
- */
-unsigned long find_first_bit(const unsigned long *addr, unsigned long size)
-{
- const unsigned long *p = addr;
- unsigned long result = 0;
- unsigned long tmp;
-
- while (size & ~(BITS_PER_LONG-1)) {
- if ((tmp = *(p++)))
- goto found;
- result += BITS_PER_LONG;
- size -= BITS_PER_LONG;
- }
- if (!size)
- return result;
-
- tmp = (*p) & (~0UL >> (BITS_PER_LONG - size));
- if (tmp == 0UL) /* Are any bits set? */
- return result + size; /* Nope. */
-found:
- return result + __ffs(tmp);
-}
-#endif
diff --git a/tools/perf/Build b/tools/perf/Build
index 72237455b400..a43fae7f439a 100644
--- a/tools/perf/Build
+++ b/tools/perf/Build
@@ -1,5 +1,6 @@
perf-y += builtin-bench.o
perf-y += builtin-annotate.o
+perf-y += builtin-config.o
perf-y += builtin-diff.o
perf-y += builtin-evlist.o
perf-y += builtin-help.o
@@ -19,6 +20,7 @@ perf-y += builtin-kvm.o
perf-y += builtin-inject.o
perf-y += builtin-mem.o
perf-y += builtin-data.o
+perf-y += builtin-version.o
perf-$(CONFIG_AUDIT) += builtin-trace.o
perf-$(CONFIG_LIBELF) += builtin-probe.o
@@ -34,8 +36,13 @@ paths += -DPERF_MAN_PATH="BUILD_STR($(mandir_SQ))"
CFLAGS_builtin-help.o += $(paths)
CFLAGS_builtin-timechart.o += $(paths)
-CFLAGS_perf.o += -DPERF_HTML_PATH="BUILD_STR($(htmldir_SQ))" -include $(OUTPUT)PERF-VERSION-FILE
+CFLAGS_perf.o += -DPERF_HTML_PATH="BUILD_STR($(htmldir_SQ))" \
+ -DPERF_EXEC_PATH="BUILD_STR($(perfexecdir_SQ))" \
+ -DPREFIX="BUILD_STR($(prefix_SQ))" \
+ -include $(OUTPUT)PERF-VERSION-FILE
CFLAGS_builtin-trace.o += -DSTRACE_GROUPS_DIR="BUILD_STR($(STRACE_GROUPS_DIR_SQ))"
+CFLAGS_builtin-report.o += -DTIPDIR="BUILD_STR($(tipdir_SQ))"
+CFLAGS_builtin-report.o += -DDOCDIR="BUILD_STR($(srcdir_SQ)/Documentation)"
libperf-y += util/
libperf-y += arch/
diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt
new file mode 100644
index 000000000000..b9ca1e304158
--- /dev/null
+++ b/tools/perf/Documentation/perf-config.txt
@@ -0,0 +1,103 @@
+perf-config(1)
+==============
+
+NAME
+----
+perf-config - Get and set variables in a configuration file.
+
+SYNOPSIS
+--------
+[verse]
+'perf config' -l | --list
+
+DESCRIPTION
+-----------
+You can manage variables in a configuration file with this command.
+
+OPTIONS
+-------
+
+-l::
+--list::
+ Show current config variables, name and value, for all sections.
+
+CONFIGURATION FILE
+------------------
+
+The perf configuration file contains many variables to change various
+aspects of each of its tools, including output, disk usage, etc.
+The '$HOME/.perfconfig' file is used to store a per-user configuration.
+The file '$(sysconfdir)/perfconfig' can be used to
+store a system-wide default configuration.
+
+Syntax
+~~~~~~
+
+The file consist of sections. A section starts with its name
+surrounded by square brackets and continues till the next section
+begins. Each variable must be in a section, and have the form
+'name = value', for example:
+
+ [section]
+ name1 = value1
+ name2 = value2
+
+Section names are case sensitive and can contain any characters except
+newline (double quote `"` and backslash have to be escaped as `\"` and `\\`,
+respectively). Section headers can't span multiple lines.
+
+Example
+~~~~~~~
+
+Given a $HOME/.perfconfig like this:
+
+#
+# This is the config file, and
+# a '#' and ';' character indicates a comment
+#
+
+ [colors]
+ # Color variables
+ top = red, default
+ medium = green, default
+ normal = lightgray, default
+ selected = white, lightgray
+ code = blue, default
+ addr = magenta, default
+ root = white, blue
+
+ [tui]
+ # Defaults if linked with libslang
+ report = on
+ annotate = on
+ top = on
+
+ [buildid]
+ # Default, disable using /dev/null
+ dir = ~/.debug
+
+ [annotate]
+ # Defaults
+ hide_src_code = false
+ use_offset = true
+ jump_arrows = true
+ show_nr_jumps = false
+
+ [help]
+ # Format can be man, info, web or html
+ format = man
+ autocorrect = 0
+
+ [ui]
+ show-headers = true
+
+ [call-graph]
+ # fp (framepointer), dwarf
+ record-mode = fp
+ print-type = graph
+ order = caller
+ sort-key = function
+
+SEE ALSO
+--------
+linkperf:perf[1]
diff --git a/tools/perf/Documentation/perf-evlist.txt b/tools/perf/Documentation/perf-evlist.txt
index 1ceb3700ffbb..6f7200fb85cf 100644
--- a/tools/perf/Documentation/perf-evlist.txt
+++ b/tools/perf/Documentation/perf-evlist.txt
@@ -32,6 +32,9 @@ OPTIONS
--group::
Show event group information.
+--trace-fields::
+ Show tracepoint field names.
+
SEE ALSO
--------
linkperf:perf-record[1], linkperf:perf-list[1],
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
index e630a7d2c348..fbceb631387c 100644
--- a/tools/perf/Documentation/perf-record.txt
+++ b/tools/perf/Documentation/perf-record.txt
@@ -207,11 +207,23 @@ comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-
In per-thread mode with inheritance mode on (default), samples are captured only when
the thread executes on the designated CPUs. Default is to monitor all CPUs.
+-B::
+--no-buildid::
+Do not save the build ids of binaries in the perf.data files. This skips
+post processing after recording, which sometimes makes the final step in
+the recording process to take a long time, as it needs to process all
+events looking for mmap records. The downside is that it can misresolve
+symbols if the workload binaries used when recording get locally rebuilt
+or upgraded, because the only key available in this case is the
+pathname. You can also set the "record.build-id" config variable to
+'skip to have this behaviour permanently.
+
-N::
--no-buildid-cache::
Do not update the buildid cache. This saves some overhead in situations
where the information in the perf.data file (which includes buildids)
-is sufficient.
+is sufficient. You can also set the "record.build-id" config variable to
+'no-cache' to have the same effect.
-G name,...::
--cgroup name,...::
@@ -314,11 +326,20 @@ This option sets the time out limit. The default value is 500 ms.
Record context switch events i.e. events of type PERF_RECORD_SWITCH or
PERF_RECORD_SWITCH_CPU_WIDE.
---clang-path::
+--clang-path=PATH::
Path to clang binary to use for compiling BPF scriptlets.
+(enabled when BPF support is on)
---clang-opt::
+--clang-opt=OPTIONS::
Options passed to clang when compiling BPF scriptlets.
+(enabled when BPF support is on)
+
+--vmlinux=PATH::
+Specify vmlinux path which has debuginfo.
+(enabled when BPF prologue is on)
+
+--buildid-all::
+Record build-id of all DSOs regardless whether it's actually hit or not.
SEE ALSO
--------
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 5ce8da1e1256..8a301f6afb37 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -117,6 +117,30 @@ OPTIONS
And default sort keys are changed to comm, dso_from, symbol_from, dso_to
and symbol_to, see '--branch-stack'.
+ If the data file has tracepoint event(s), following (dynamic) sort keys
+ are also available:
+ trace, trace_fields, [<event>.]<field>[/raw]
+
+ - trace: pretty printed trace output in a single column
+ - trace_fields: fields in tracepoints in separate columns
+ - <field name>: optional event and field name for a specific field
+
+ The last form consists of event and field names. If event name is
+ omitted, it searches all events for matching field name. The matched
+ field will be shown only for the event has the field. The event name
+ supports substring match so user doesn't need to specify full subsystem
+ and event name everytime. For example, 'sched:sched_switch' event can
+ be shortened to 'switch' as long as it's not ambiguous. Also event can
+ be specified by its index (starting from 1) preceded by the '%'.
+ So '%1' is the first event, '%2' is the second, and so on.
+
+ The field name can have '/raw' suffix which disables pretty printing
+ and shows raw field value like hex numbers. The --raw-trace option
+ has the same effect for all dynamic sort keys.
+
+ The default sort keys are changed to 'trace' if all events in the data
+ file are tracepoint.
+
-F::
--fields=::
Specify output field - multiple keys can be specified in CSV format.
@@ -170,17 +194,18 @@ OPTIONS
Dump raw trace in ASCII.
-g::
---call-graph=<print_type,threshold[,print_limit],order,sort_key,branch>::
+--call-graph=<print_type,threshold[,print_limit],order,sort_key[,branch],value>::
Display call chains using type, min percent threshold, print limit,
- call order, sort key and branch. Note that ordering of parameters is not
- fixed so any parement can be given in an arbitraty order. One exception
- is the print_limit which should be preceded by threshold.
+ call order, sort key, optional branch and value. Note that ordering of
+ parameters is not fixed so any parement can be given in an arbitraty order.
+ One exception is the print_limit which should be preceded by threshold.
print_type can be either:
- flat: single column, linear exposure of call chains.
- graph: use a graph tree, displaying absolute overhead rates. (default)
- fractal: like graph, but displays relative rates. Each branch of
the tree is considered as a new profiled object.
+ - folded: call chains are displayed in a line, separated by semicolons
- none: disable call chain display.
threshold is a percentage value which specifies a minimum percent to be
@@ -204,6 +229,11 @@ OPTIONS
- branch: include last branch information in callgraph when available.
Usually more convenient to use --branch-history for this.
+ value can be:
+ - percent: diplay overhead percent (default)
+ - period: display event period
+ - count: display event count
+
--children::
Accumulate callchain of children to parent entry so that then can
show up in the output. The output will have a new "Children" column
@@ -365,6 +395,9 @@ include::itrace.txt[]
--socket-filter::
Only report the samples on the processor socket that match with this filter
+--raw-trace::
+ When displaying traceevent output, do not use print fmt or plugins.
+
include::callchain-overhead-calculation.txt[]
SEE ALSO
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt
index 4e074a660826..52ef7a9d50aa 100644
--- a/tools/perf/Documentation/perf-stat.txt
+++ b/tools/perf/Documentation/perf-stat.txt
@@ -10,6 +10,8 @@ SYNOPSIS
[verse]
'perf stat' [-e <EVENT> | --event=EVENT] [-a] <command>
'perf stat' [-e <EVENT> | --event=EVENT] [-a] -- <command> [<options>]
+'perf stat' [-e <EVENT> | --event=EVENT] [-a] record [-o file] -- <command> [<options>]
+'perf stat' report [-i file]
DESCRIPTION
-----------
@@ -22,6 +24,11 @@ OPTIONS
<command>...::
Any command you can specify in a shell.
+record::
+ See STAT RECORD.
+
+report::
+ See STAT REPORT.
-e::
--event=::
@@ -159,6 +166,33 @@ filter out the startup phase of the program, which is often very different.
Print statistics of transactional execution if supported.
+STAT RECORD
+-----------
+Stores stat data into perf data file.
+
+-o file::
+--output file::
+Output file name.
+
+STAT REPORT
+-----------
+Reads and reports stat data from perf data file.
+
+-i file::
+--input file::
+Input file name.
+
+--per-socket::
+Aggregate counts per processor socket for system-wide mode measurements.
+
+--per-core::
+Aggregate counts per physical processor for system-wide mode measurements.
+
+-A::
+--no-aggr::
+Do not aggregate counts across all monitored CPUs.
+
+
EXAMPLES
--------
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt
index 556cec09bf50..b0e60e17db38 100644
--- a/tools/perf/Documentation/perf-top.txt
+++ b/tools/perf/Documentation/perf-top.txt
@@ -230,6 +230,9 @@ Default is to monitor all CPUS.
The various filters must be specified as a comma separated list: --branch-filter any_ret,u,k
Note that this feature may not be available on all processors.
+--raw-trace::
+ When displaying traceevent output, do not use print fmt or plugins.
+
INTERACTIVE PROMPTING KEYS
--------------------------
diff --git a/tools/perf/Documentation/tips.txt b/tools/perf/Documentation/tips.txt
new file mode 100644
index 000000000000..e0ce9573b79b
--- /dev/null
+++ b/tools/perf/Documentation/tips.txt
@@ -0,0 +1,29 @@
+For a higher level overview, try: perf report --sort comm,dso
+Sample related events with: perf record -e '{cycles,instructions}:S'
+Compare performance results with: perf diff [<old file> <new file>]
+Boolean options have negative forms, e.g.: perf report --no-children
+Customize output of perf script with: perf script -F event,ip,sym
+Generate a script for your data: perf script -g <lang>
+Save output of perf stat using: perf stat record <target workload>
+Create an archive with symtabs to analyse on other machine: perf archive
+Search options using a keyword: perf report -h <keyword>
+Use parent filter to see specific call path: perf report -p <regex>
+List events using substring match: perf list <keyword>
+To see list of saved events and attributes: perf evlist -v
+Use --symfs <dir> if your symbol files are in non-standard locations
+To see callchains in a more compact form: perf report -g folded
+Show individual samples with: perf script
+Limit to show entries above 5% only: perf report --percent-limit 5
+Profiling branch (mis)predictions with: perf record -b / perf report
+Treat branches as callchains: perf report --branch-history
+To count events in every 1000 msec: perf stat -I 1000
+Print event counts in CSV format with: perf stat -x,
+If you have debuginfo enabled, try: perf report -s sym,srcline
+For memory address profiling, try: perf mem record / perf mem report
+For tracepoint events, try: perf report -s trace_fields
+To record callchains for each sample: perf record -g
+To record every process run by an user: perf record -u <user>
+Skip collecing build-id when recording: perf record -B
+To change sampling frequency to 100 Hz: perf record -F 100
+See assembly instructions with percentage: perf annotate <symbol>
+If you prefer Intel style assembly, try: perf annotate -M intel
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST
index 39c38cb45b00..2e1fa2357528 100644
--- a/tools/perf/MANIFEST
+++ b/tools/perf/MANIFEST
@@ -1,6 +1,7 @@
tools/perf
tools/arch/alpha/include/asm/barrier.h
tools/arch/arm/include/asm/barrier.h
+tools/arch/arm64/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
@@ -20,14 +21,18 @@ tools/lib/traceevent
tools/lib/bpf
tools/lib/api
tools/lib/bpf
+tools/lib/subcmd
tools/lib/hweight.c
tools/lib/rbtree.c
+tools/lib/string.c
tools/lib/symbol/kallsyms.c
tools/lib/symbol/kallsyms.h
-tools/lib/util/find_next_bit.c
+tools/lib/find_bit.c
+tools/lib/bitmap.c
tools/include/asm/atomic.h
tools/include/asm/barrier.h
tools/include/asm/bug.h
+tools/include/asm-generic/atomic-gcc.h
tools/include/asm-generic/barrier.h
tools/include/asm-generic/bitops/arch_hweight.h
tools/include/asm-generic/bitops/atomic.h
@@ -50,8 +55,10 @@ tools/include/linux/log2.h
tools/include/linux/poison.h
tools/include/linux/rbtree.h
tools/include/linux/rbtree_augmented.h
+tools/include/linux/string.h
tools/include/linux/types.h
tools/include/linux/err.h
+tools/include/linux/bitmap.h
include/asm-generic/bitops/arch_hweight.h
include/asm-generic/bitops/const_hweight.h
include/asm-generic/bitops/fls64.h
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 0d19d5447d6c..0a22407e1d7d 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -145,9 +145,10 @@ BISON = bison
STRIP = strip
AWK = awk
-LIB_DIR = $(srctree)/tools/lib/api/
+LIB_DIR = $(srctree)/tools/lib/api/
TRACE_EVENT_DIR = $(srctree)/tools/lib/traceevent/
-BPF_DIR = $(srctree)/tools/lib/bpf/
+BPF_DIR = $(srctree)/tools/lib/bpf/
+SUBCMD_DIR = $(srctree)/tools/lib/subcmd/
# include config/Makefile by default and rule out
# non-config cases
@@ -184,15 +185,17 @@ strip-libs = $(filter-out -l%,$(1))
ifneq ($(OUTPUT),)
TE_PATH=$(OUTPUT)
BPF_PATH=$(OUTPUT)
+ SUBCMD_PATH=$(OUTPUT)
ifneq ($(subdir),)
- LIB_PATH=$(OUTPUT)/../lib/api/
+ API_PATH=$(OUTPUT)/../lib/api/
else
- LIB_PATH=$(OUTPUT)
+ API_PATH=$(OUTPUT)
endif
else
TE_PATH=$(TRACE_EVENT_DIR)
- LIB_PATH=$(LIB_DIR)
+ API_PATH=$(LIB_DIR)
BPF_PATH=$(BPF_DIR)
+ SUBCMD_PATH=$(SUBCMD_DIR)
endif
LIBTRACEEVENT = $(TE_PATH)libtraceevent.a
@@ -201,11 +204,13 @@ 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
+LIBAPI = $(API_PATH)libapi.a
export LIBAPI
LIBBPF = $(BPF_PATH)libbpf.a
+LIBSUBCMD = $(SUBCMD_PATH)libsubcmd.a
+
# python extension build directories
PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/
PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/
@@ -257,7 +262,7 @@ export PERL_PATH
LIB_FILE=$(OUTPUT)libperf.a
-PERFLIBS = $(LIB_FILE) $(LIBAPI) $(LIBTRACEEVENT)
+PERFLIBS = $(LIB_FILE) $(LIBAPI) $(LIBTRACEEVENT) $(LIBSUBCMD)
ifndef NO_LIBBPF
PERFLIBS += $(LIBBPF)
endif
@@ -420,7 +425,7 @@ $(LIBTRACEEVENT)-clean:
$(call QUIET_CLEAN, libtraceevent)
$(Q)$(MAKE) -C $(TRACE_EVENT_DIR) O=$(OUTPUT) clean >/dev/null
-install-traceevent-plugins: $(LIBTRACEEVENT)
+install-traceevent-plugins: libtraceevent_plugins
$(Q)$(MAKE) -C $(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) O=$(OUTPUT) install_plugins
$(LIBAPI): fixdep FORCE
@@ -431,12 +436,19 @@ $(LIBAPI)-clean:
$(Q)$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) clean >/dev/null
$(LIBBPF): fixdep FORCE
- $(Q)$(MAKE) -C $(BPF_DIR) O=$(OUTPUT) $(OUTPUT)libbpf.a
+ $(Q)$(MAKE) -C $(BPF_DIR) O=$(OUTPUT) $(OUTPUT)libbpf.a FEATURES_DUMP=$(realpath $(OUTPUT)FEATURE-DUMP)
$(LIBBPF)-clean:
$(call QUIET_CLEAN, libbpf)
$(Q)$(MAKE) -C $(BPF_DIR) O=$(OUTPUT) clean >/dev/null
+$(LIBSUBCMD): fixdep FORCE
+ $(Q)$(MAKE) -C $(SUBCMD_DIR) O=$(OUTPUT) $(OUTPUT)libsubcmd.a
+
+$(LIBSUBCMD)-clean:
+ $(call QUIET_CLEAN, libsubcmd)
+ $(Q)$(MAKE) -C $(SUBCMD_DIR) O=$(OUTPUT) clean
+
help:
@echo 'Perf make targets:'
@echo ' doc - make *all* documentation (see below)'
@@ -476,7 +488,7 @@ INSTALL_DOC_TARGETS += quick-install-doc quick-install-man quick-install-html
$(DOC_TARGETS):
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:doc=all)
-TAG_FOLDERS= . ../lib/traceevent ../lib/api ../lib/symbol ../include ../lib/bpf
+TAG_FOLDERS= . ../lib ../include
TAG_FILES= ../../include/uapi/linux/perf_event.h
TAGS:
@@ -555,6 +567,9 @@ 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'
+ $(call QUIET_INSTALL, perf-tip) \
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(tip_instdir_SQ)'; \
+ $(INSTALL) Documentation/tips.txt -t '$(DESTDIR_SQ)$(tip_instdir_SQ)'
install-tests: all install-gtk
$(call QUIET_INSTALL, tests) \
@@ -582,15 +597,16 @@ $(INSTALL_DOC_TARGETS):
#
config-clean:
$(call QUIET_CLEAN, config)
- $(Q)$(MAKE) -C $(srctree)/tools/build/feature/ clean >/dev/null
+ $(Q)$(MAKE) -C $(srctree)/tools/build/feature/ $(if $(OUTPUT),OUTPUT=$(OUTPUT)feature/,) clean >/dev/null
-clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean config-clean
+clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean config-clean
$(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS)
- $(Q)find . -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
+ $(Q)find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
$(Q)$(RM) $(OUTPUT).config-detected
$(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32
$(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* \
- $(OUTPUT)util/intel-pt-decoder/inat-tables.c
+ $(OUTPUT)util/intel-pt-decoder/inat-tables.c $(OUTPUT)fixdep \
+ $(OUTPUT)tests/llvm-src-{base,kbuild,prologue}.c
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean
$(python-clean)
diff --git a/tools/perf/arch/x86/include/arch-tests.h b/tools/perf/arch/x86/include/arch-tests.h
index 7ed00f4b0908..b48de2f5813c 100644
--- a/tools/perf/arch/x86/include/arch-tests.h
+++ b/tools/perf/arch/x86/include/arch-tests.h
@@ -2,10 +2,10 @@
#define ARCH_TESTS_H
/* Tests */
-int test__rdpmc(void);
-int test__perf_time_to_tsc(void);
-int test__insn_x86(void);
-int test__intel_cqm_count_nmi_context(void);
+int test__rdpmc(int subtest);
+int test__perf_time_to_tsc(int subtest);
+int test__insn_x86(int subtest);
+int test__intel_cqm_count_nmi_context(int subtest);
#ifdef HAVE_DWARF_UNWIND_SUPPORT
struct thread;
diff --git a/tools/perf/arch/x86/tests/insn-x86.c b/tools/perf/arch/x86/tests/insn-x86.c
index b6115dfd28f0..08d9b2bc185c 100644
--- a/tools/perf/arch/x86/tests/insn-x86.c
+++ b/tools/perf/arch/x86/tests/insn-x86.c
@@ -171,7 +171,7 @@ static int test_data_set(struct test_data *dat_set, int x86_64)
* verbose (-v) option to see all the instructions and whether or not they
* decoded successfuly.
*/
-int test__insn_x86(void)
+int test__insn_x86(int subtest __maybe_unused)
{
int ret = 0;
diff --git a/tools/perf/arch/x86/tests/intel-cqm.c b/tools/perf/arch/x86/tests/intel-cqm.c
index d28c1b6a3b54..3e89ba825f6b 100644
--- a/tools/perf/arch/x86/tests/intel-cqm.c
+++ b/tools/perf/arch/x86/tests/intel-cqm.c
@@ -33,7 +33,7 @@ static pid_t spawn(void)
* the last read counter value to avoid triggering a WARN_ON_ONCE() in
* smp_call_function_many() caused by sending IPIs from NMI context.
*/
-int test__intel_cqm_count_nmi_context(void)
+int test__intel_cqm_count_nmi_context(int subtest __maybe_unused)
{
struct perf_evlist *evlist = NULL;
struct perf_evsel *evsel = NULL;
@@ -54,7 +54,7 @@ int test__intel_cqm_count_nmi_context(void)
ret = parse_events(evlist, "intel_cqm/llc_occupancy/", NULL);
if (ret) {
- pr_debug("parse_events failed\n");
+ pr_debug("parse_events failed, is \"intel_cqm/llc_occupancy/\" available?\n");
err = TEST_SKIP;
goto out;
}
diff --git a/tools/perf/arch/x86/tests/perf-time-to-tsc.c b/tools/perf/arch/x86/tests/perf-time-to-tsc.c
index 658cd200af74..9d29ee283ac5 100644
--- a/tools/perf/arch/x86/tests/perf-time-to-tsc.c
+++ b/tools/perf/arch/x86/tests/perf-time-to-tsc.c
@@ -35,13 +35,12 @@
* %0 is returned, otherwise %-1 is returned. If TSC conversion is not
* supported then then the test passes but " (not supported)" is printed.
*/
-int test__perf_time_to_tsc(void)
+int test__perf_time_to_tsc(int subtest __maybe_unused)
{
struct record_opts opts = {
.mmap_pages = UINT_MAX,
.user_freq = UINT_MAX,
.user_interval = ULLONG_MAX,
- .freq = 4000,
.target = {
.uses_mmap = true,
},
diff --git a/tools/perf/arch/x86/tests/rdpmc.c b/tools/perf/arch/x86/tests/rdpmc.c
index e7688214c7cf..7bb0d13c235f 100644
--- a/tools/perf/arch/x86/tests/rdpmc.c
+++ b/tools/perf/arch/x86/tests/rdpmc.c
@@ -149,7 +149,7 @@ out_close:
return 0;
}
-int test__rdpmc(void)
+int test__rdpmc(int subtest __maybe_unused)
{
int status = 0;
int wret = 0;
diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build
index ff63649fa9ac..465970370f3e 100644
--- a/tools/perf/arch/x86/util/Build
+++ b/tools/perf/arch/x86/util/Build
@@ -5,6 +5,7 @@ libperf-y += kvm-stat.o
libperf-y += perf_regs.o
libperf-$(CONFIG_DWARF) += dwarf-regs.o
+libperf-$(CONFIG_BPF_PROLOGUE) += dwarf-regs.o
libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c
index 9b94ce520917..8d8150f1cf9b 100644
--- a/tools/perf/arch/x86/util/intel-bts.c
+++ b/tools/perf/arch/x86/util/intel-bts.c
@@ -327,7 +327,7 @@ static int intel_bts_snapshot_start(struct auxtrace_record *itr)
evlist__for_each(btsr->evlist, evsel) {
if (evsel->attr.type == btsr->intel_bts_pmu->type)
- return perf_evlist__disable_event(btsr->evlist, evsel);
+ return perf_evsel__disable(evsel);
}
return -EINVAL;
}
@@ -340,7 +340,7 @@ static int intel_bts_snapshot_finish(struct auxtrace_record *itr)
evlist__for_each(btsr->evlist, evsel) {
if (evsel->attr.type == btsr->intel_bts_pmu->type)
- return perf_evlist__enable_event(btsr->evlist, evsel);
+ return perf_evsel__enable(evsel);
}
return -EINVAL;
}
diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c
index b02af064f0f9..f05daacc9e78 100644
--- a/tools/perf/arch/x86/util/intel-pt.c
+++ b/tools/perf/arch/x86/util/intel-pt.c
@@ -26,7 +26,7 @@
#include "../../util/evlist.h"
#include "../../util/evsel.h"
#include "../../util/cpumap.h"
-#include "../../util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "../../util/parse-events.h"
#include "../../util/pmu.h"
#include "../../util/debug.h"
@@ -725,7 +725,7 @@ static int intel_pt_snapshot_start(struct auxtrace_record *itr)
evlist__for_each(ptr->evlist, evsel) {
if (evsel->attr.type == ptr->intel_pt_pmu->type)
- return perf_evlist__disable_event(ptr->evlist, evsel);
+ return perf_evsel__disable(evsel);
}
return -EINVAL;
}
@@ -738,7 +738,7 @@ static int intel_pt_snapshot_finish(struct auxtrace_record *itr)
evlist__for_each(ptr->evlist, evsel) {
if (evsel->attr.type == ptr->intel_pt_pmu->type)
- return perf_evlist__enable_event(ptr->evlist, evsel);
+ return perf_evsel__enable(evsel);
}
return -EINVAL;
}
diff --git a/tools/perf/bench/futex-hash.c b/tools/perf/bench/futex-hash.c
index fc9bebd2cca0..0999ac536d86 100644
--- a/tools/perf/bench/futex-hash.c
+++ b/tools/perf/bench/futex-hash.c
@@ -11,7 +11,7 @@
#include "../perf.h"
#include "../util/util.h"
#include "../util/stat.h"
-#include "../util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "../util/header.h"
#include "bench.h"
#include "futex.h"
diff --git a/tools/perf/bench/futex-lock-pi.c b/tools/perf/bench/futex-lock-pi.c
index bc6a16adbca8..6a18ce21f865 100644
--- a/tools/perf/bench/futex-lock-pi.c
+++ b/tools/perf/bench/futex-lock-pi.c
@@ -5,7 +5,7 @@
#include "../perf.h"
#include "../util/util.h"
#include "../util/stat.h"
-#include "../util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "../util/header.h"
#include "bench.h"
#include "futex.h"
diff --git a/tools/perf/bench/futex-requeue.c b/tools/perf/bench/futex-requeue.c
index ad0d9b5342fb..718238683013 100644
--- a/tools/perf/bench/futex-requeue.c
+++ b/tools/perf/bench/futex-requeue.c
@@ -11,7 +11,7 @@
#include "../perf.h"
#include "../util/util.h"
#include "../util/stat.h"
-#include "../util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "../util/header.h"
#include "bench.h"
#include "futex.h"
diff --git a/tools/perf/bench/futex-wake-parallel.c b/tools/perf/bench/futex-wake-parallel.c
index 6d8c9fa2a16c..91aaf2a1fa90 100644
--- a/tools/perf/bench/futex-wake-parallel.c
+++ b/tools/perf/bench/futex-wake-parallel.c
@@ -10,7 +10,7 @@
#include "../perf.h"
#include "../util/util.h"
#include "../util/stat.h"
-#include "../util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "../util/header.h"
#include "bench.h"
#include "futex.h"
diff --git a/tools/perf/bench/futex-wake.c b/tools/perf/bench/futex-wake.c
index e5e41d3bdce7..f416bd705f66 100644
--- a/tools/perf/bench/futex-wake.c
+++ b/tools/perf/bench/futex-wake.c
@@ -11,7 +11,7 @@
#include "../perf.h"
#include "../util/util.h"
#include "../util/stat.h"
-#include "../util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "../util/header.h"
#include "bench.h"
#include "futex.h"
diff --git a/tools/perf/bench/mem-functions.c b/tools/perf/bench/mem-functions.c
index 9419b944220f..a91aa85d80ff 100644
--- a/tools/perf/bench/mem-functions.c
+++ b/tools/perf/bench/mem-functions.c
@@ -8,7 +8,7 @@
#include "../perf.h"
#include "../util/util.h"
-#include "../util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "../util/header.h"
#include "../util/cloexec.h"
#include "bench.h"
diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c
index 492df2752a2d..5049d6357a46 100644
--- a/tools/perf/bench/numa.c
+++ b/tools/perf/bench/numa.c
@@ -7,7 +7,7 @@
#include "../perf.h"
#include "../builtin.h"
#include "../util/util.h"
-#include "../util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "../util/cloexec.h"
#include "bench.h"
diff --git a/tools/perf/bench/sched-messaging.c b/tools/perf/bench/sched-messaging.c
index d4ff1b539cfd..bfaf9503de8e 100644
--- a/tools/perf/bench/sched-messaging.c
+++ b/tools/perf/bench/sched-messaging.c
@@ -11,7 +11,7 @@
#include "../perf.h"
#include "../util/util.h"
-#include "../util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "../builtin.h"
#include "bench.h"
diff --git a/tools/perf/bench/sched-pipe.c b/tools/perf/bench/sched-pipe.c
index 005cc283790c..1dc2d13cc272 100644
--- a/tools/perf/bench/sched-pipe.c
+++ b/tools/perf/bench/sched-pipe.c
@@ -10,7 +10,7 @@
*/
#include "../perf.h"
#include "../util/util.h"
-#include "../util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "../builtin.h"
#include "bench.h"
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 2bf9b3fd9e61..cc5c1267c738 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -21,7 +21,7 @@
#include "util/evsel.h"
#include "util/annotate.h"
#include "util/event.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "util/parse-events.h"
#include "util/thread.h"
#include "util/sort.h"
@@ -47,7 +47,7 @@ struct perf_annotate {
};
static int perf_evsel__add_sample(struct perf_evsel *evsel,
- struct perf_sample *sample __maybe_unused,
+ struct perf_sample *sample,
struct addr_location *al,
struct perf_annotate *ann)
{
@@ -72,7 +72,10 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,
return 0;
}
- he = __hists__add_entry(hists, al, NULL, NULL, NULL, 1, 1, 0, true);
+ sample->period = 1;
+ sample->weight = 1;
+
+ he = __hists__add_entry(hists, al, NULL, NULL, NULL, sample, true);
if (he == NULL)
return -ENOMEM;
@@ -343,18 +346,19 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
return ret;
argc = parse_options(argc, argv, options, annotate_usage, 0);
+ if (argc) {
+ /*
+ * Special case: if there's an argument left then assume that
+ * it's a symbol filter:
+ */
+ if (argc > 1)
+ usage_with_options(annotate_usage, options);
- if (annotate.use_stdio)
- use_browser = 0;
- else if (annotate.use_tui)
- use_browser = 1;
- else if (annotate.use_gtk)
- use_browser = 2;
+ annotate.sym_hist_filter = argv[0];
+ }
file.path = input_name;
- setup_browser(true);
-
annotate.session = perf_session__new(&file, false, &annotate.tool);
if (annotate.session == NULL)
return -1;
@@ -366,19 +370,17 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
if (ret < 0)
goto out_delete;
- if (setup_sorting() < 0)
+ if (setup_sorting(NULL) < 0)
usage_with_options(annotate_usage, options);
- if (argc) {
- /*
- * Special case: if there's an argument left then assume that
- * it's a symbol filter:
- */
- if (argc > 1)
- usage_with_options(annotate_usage, options);
+ if (annotate.use_stdio)
+ use_browser = 0;
+ else if (annotate.use_tui)
+ use_browser = 1;
+ else if (annotate.use_gtk)
+ use_browser = 2;
- annotate.sym_hist_filter = argv[0];
- }
+ setup_browser(true);
ret = __cmd_annotate(&annotate);
diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c
index b17aed36ca16..a1cddc6bbf0f 100644
--- a/tools/perf/builtin-bench.c
+++ b/tools/perf/builtin-bench.c
@@ -16,7 +16,7 @@
*/
#include "perf.h"
#include "util/util.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "builtin.h"
#include "bench/bench.h"
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c
index 7b8450cd33c2..d93bff7fc0e4 100644
--- a/tools/perf/builtin-buildid-cache.c
+++ b/tools/perf/builtin-buildid-cache.c
@@ -16,7 +16,7 @@
#include "util/cache.h"
#include "util/debug.h"
#include "util/header.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "util/strlist.h"
#include "util/build-id.h"
#include "util/session.h"
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c
index 918b4de29de4..5e914ee79eb3 100644
--- a/tools/perf/builtin-buildid-list.c
+++ b/tools/perf/builtin-buildid-list.c
@@ -12,7 +12,7 @@
#include "util/build-id.h"
#include "util/cache.h"
#include "util/debug.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "util/session.h"
#include "util/symbol.h"
#include "util/data.h"
@@ -110,7 +110,7 @@ int cmd_buildid_list(int argc, const char **argv,
setup_pager();
if (show_kernel)
- return sysfs__fprintf_build_id(stdout);
+ return !(sysfs__fprintf_build_id(stdout) > 0);
return perf_session__list_build_ids(force, with_hits);
}
diff --git a/tools/perf/builtin-config.c b/tools/perf/builtin-config.c
new file mode 100644
index 000000000000..f04e804a9fad
--- /dev/null
+++ b/tools/perf/builtin-config.c
@@ -0,0 +1,66 @@
+/*
+ * builtin-config.c
+ *
+ * Copyright (C) 2015, Taeung Song <treeze.taeung@gmail.com>
+ *
+ */
+#include "builtin.h"
+
+#include "perf.h"
+
+#include "util/cache.h"
+#include <subcmd/parse-options.h>
+#include "util/util.h"
+#include "util/debug.h"
+
+static const char * const config_usage[] = {
+ "perf config [options]",
+ NULL
+};
+
+enum actions {
+ ACTION_LIST = 1
+} actions;
+
+static struct option config_options[] = {
+ OPT_SET_UINT('l', "list", &actions,
+ "show current config variables", ACTION_LIST),
+ OPT_END()
+};
+
+static int show_config(const char *key, const char *value,
+ void *cb __maybe_unused)
+{
+ if (value)
+ printf("%s=%s\n", key, value);
+ else
+ printf("%s\n", key);
+
+ return 0;
+}
+
+int cmd_config(int argc, const char **argv, const char *prefix __maybe_unused)
+{
+ int ret = 0;
+
+ argc = parse_options(argc, argv, config_options, config_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ switch (actions) {
+ case ACTION_LIST:
+ if (argc) {
+ pr_err("Error: takes no arguments\n");
+ parse_options_usage(config_usage, config_options, "l", 1);
+ } else {
+ ret = perf_config(show_config, NULL);
+ if (ret < 0)
+ pr_err("Nothing configured, "
+ "please check your ~/.perfconfig file\n");
+ }
+ break;
+ default:
+ usage_with_options(config_usage, config_options);
+ }
+
+ return ret;
+}
diff --git a/tools/perf/builtin-data.c b/tools/perf/builtin-data.c
index d6525bc54d13..b97bc1518b44 100644
--- a/tools/perf/builtin-data.c
+++ b/tools/perf/builtin-data.c
@@ -2,7 +2,7 @@
#include "builtin.h"
#include "perf.h"
#include "debug.h"
-#include "parse-options.h"
+#include <subcmd/parse-options.h>
#include "data-convert-bt.h"
typedef int (*data_cmd_fn_t)(int argc, const char **argv, const char *prefix);
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 0b180a885ba3..36ccc2b8827f 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -311,11 +311,11 @@ static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair,
}
static int hists__add_entry(struct hists *hists,
- struct addr_location *al, u64 period,
- u64 weight, u64 transaction)
+ struct addr_location *al,
+ struct perf_sample *sample)
{
- if (__hists__add_entry(hists, al, NULL, NULL, NULL, period, weight,
- transaction, true) != NULL)
+ if (__hists__add_entry(hists, al, NULL, NULL, NULL,
+ sample, true) != NULL)
return 0;
return -ENOMEM;
}
@@ -336,8 +336,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
return -1;
}
- if (hists__add_entry(hists, &al, sample->period,
- sample->weight, sample->transaction)) {
+ if (hists__add_entry(hists, &al, sample)) {
pr_warning("problem incrementing symbol period, skipping event\n");
goto out_put;
}
@@ -1208,7 +1207,7 @@ static int ui_init(void)
BUG_ON(1);
}
- list_add(&fmt->sort_list, &perf_hpp__sort_list);
+ perf_hpp__register_sort_field(fmt);
return 0;
}
@@ -1280,7 +1279,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
sort__mode = SORT_MODE__DIFF;
- if (setup_sorting() < 0)
+ if (setup_sorting(NULL) < 0)
usage_with_options(diff_usage, options);
setup_pager();
diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c
index f4d62510acbb..8a31f511e1a0 100644
--- a/tools/perf/builtin-evlist.c
+++ b/tools/perf/builtin-evlist.c
@@ -12,7 +12,7 @@
#include "util/evlist.h"
#include "util/evsel.h"
#include "util/parse-events.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "util/session.h"
#include "util/data.h"
#include "util/debug.h"
@@ -26,14 +26,22 @@ static int __cmd_evlist(const char *file_name, struct perf_attr_details *details
.mode = PERF_DATA_MODE_READ,
.force = details->force,
};
+ bool has_tracepoint = false;
session = perf_session__new(&file, 0, NULL);
if (session == NULL)
return -1;
- evlist__for_each(session->evlist, pos)
+ evlist__for_each(session->evlist, pos) {
perf_evsel__fprintf(pos, details, stdout);
+ if (pos->attr.type == PERF_TYPE_TRACEPOINT)
+ has_tracepoint = true;
+ }
+
+ if (has_tracepoint && !details->trace_fields)
+ printf("# Tip: use 'perf evlist --trace-fields' to show fields for tracepoint events\n");
+
perf_session__delete(session);
return 0;
}
@@ -49,6 +57,7 @@ int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_BOOLEAN('g', "group", &details.event_group,
"Show event group information"),
OPT_BOOLEAN('f', "force", &details.force, "don't complain, do it"),
+ OPT_BOOLEAN(0, "trace-fields", &details.trace_fields, "Show tracepoint fields"),
OPT_END()
};
const char * const evlist_usage[] = {
diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c
index a7d588bf3cdd..96c1a4cfbbbf 100644
--- a/tools/perf/builtin-help.c
+++ b/tools/perf/builtin-help.c
@@ -6,11 +6,11 @@
#include "perf.h"
#include "util/cache.h"
#include "builtin.h"
-#include "util/exec_cmd.h"
+#include <subcmd/exec-cmd.h>
#include "common-cmds.h"
-#include "util/parse-options.h"
-#include "util/run-command.h"
-#include "util/help.h"
+#include <subcmd/parse-options.h>
+#include <subcmd/run-command.h>
+#include <subcmd/help.h>
#include "util/debug.h"
static struct man_viewer_list {
@@ -407,7 +407,7 @@ static int get_html_page_path(struct strbuf *page_path, const char *page)
#ifndef open_html
static void open_html(const char *path)
{
- execl_perf_cmd("web--browse", "-c", "help.browser", path, NULL);
+ execl_cmd("web--browse", "-c", "help.browser", path, NULL);
}
#endif
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index 99d127fe9c35..0022e02ed31a 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -18,7 +18,7 @@
#include "util/data.h"
#include "util/auxtrace.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include <linux/list.h>
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index 93ce665f976f..118010553d0c 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -12,7 +12,7 @@
#include "util/tool.h"
#include "util/callchain.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "util/trace-event.h"
#include "util/data.h"
#include "util/cpumap.h"
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
index dd94b4ca2213..4418d9214872 100644
--- a/tools/perf/builtin-kvm.c
+++ b/tools/perf/builtin-kvm.c
@@ -10,7 +10,7 @@
#include "util/header.h"
#include "util/session.h"
#include "util/intlist.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "util/trace-event.h"
#include "util/debug.h"
#include "util/tool.h"
@@ -1351,7 +1351,6 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
disable_buildid_cache();
use_browser = 0;
- setup_browser(false);
if (argc) {
argc = parse_options(argc, argv, live_options,
@@ -1409,8 +1408,6 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
err = kvm_events_live_report(kvm);
out:
- exit_browser(0);
-
if (kvm->session)
perf_session__delete(kvm->session);
kvm->session = NULL;
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index bf679e2c978b..5e22db4684b8 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -14,7 +14,7 @@
#include "util/parse-events.h"
#include "util/cache.h"
#include "util/pmu.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
{
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index de16aaed516e..ce3bfb48b26f 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -9,7 +9,7 @@
#include "util/thread.h"
#include "util/header.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "util/trace-event.h"
#include "util/debug.h"
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c
index 80170aace5d4..390170041696 100644
--- a/tools/perf/builtin-mem.c
+++ b/tools/perf/builtin-mem.c
@@ -1,7 +1,7 @@
#include "builtin.h"
#include "perf.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "util/trace-event.h"
#include "util/tool.h"
#include "util/session.h"
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 132afc97676c..9af859b28b15 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -37,7 +37,7 @@
#include "util/strfilter.h"
#include "util/symbol.h"
#include "util/debug.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "util/probe-finder.h"
#include "util/probe-event.h"
#include "util/probe-file.h"
@@ -249,6 +249,9 @@ static int opt_show_vars(const struct option *opt,
return ret;
}
+#else
+# define opt_show_lines NULL
+# define opt_show_vars NULL
#endif
static int opt_add_probe_event(const struct option *opt,
const char *str, int unset __maybe_unused)
@@ -473,7 +476,6 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
opt_add_probe_event),
OPT_BOOLEAN('f', "force", &probe_conf.force_add, "forcibly add events"
" with existing name"),
-#ifdef HAVE_DWARF_SUPPORT
OPT_CALLBACK('L', "line", NULL,
"FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]",
"Show source code lines.", opt_show_lines),
@@ -490,7 +492,6 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
"directory", "path to kernel source"),
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", &probe_conf.max_probes,
"Set how many probe points can be found for a probe."),
@@ -521,6 +522,16 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
#ifdef HAVE_DWARF_SUPPORT
set_option_flag(options, 'L', "line", PARSE_OPT_EXCLUSIVE);
set_option_flag(options, 'V', "vars", PARSE_OPT_EXCLUSIVE);
+#else
+# define set_nobuild(s, l, c) set_option_nobuild(options, s, l, "NO_DWARF=1", c)
+ set_nobuild('L', "line", false);
+ set_nobuild('V', "vars", false);
+ set_nobuild('\0', "externs", false);
+ set_nobuild('\0', "range", false);
+ set_nobuild('k', "vmlinux", true);
+ set_nobuild('s', "source", true);
+ set_nobuild('\0', "no-inlines", true);
+# undef set_nobuild
#endif
set_option_flag(options, 'F', "funcs", PARSE_OPT_EXCLUSIVE);
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 199fc31e3919..319712a4e02b 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -11,7 +11,7 @@
#include "util/build-id.h"
#include "util/util.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "util/parse-events.h"
#include "util/callchain.h"
@@ -50,6 +50,7 @@ struct record {
int realtime_prio;
bool no_buildid;
bool no_buildid_cache;
+ bool buildid_all;
unsigned long long samples;
};
@@ -362,6 +363,13 @@ static int process_buildids(struct record *rec)
*/
symbol_conf.ignore_vmlinux_buildid = true;
+ /*
+ * If --buildid-all is given, it marks all DSO regardless of hits,
+ * so no need to process samples.
+ */
+ if (rec->buildid_all)
+ rec->tool.sample = NULL;
+
return perf_session__process_events(session);
}
@@ -452,6 +460,8 @@ static void record__init_features(struct record *rec)
if (!rec->opts.full_auxtrace)
perf_header__clear_feat(&session->header, HEADER_AUXTRACE);
+
+ perf_header__clear_feat(&session->header, HEADER_STAT);
}
static volatile int workload_exec_errno;
@@ -754,12 +764,8 @@ out_child:
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)
+
+ if (rec->buildid_all)
dsos__hit_all(rec->session);
}
perf_session__write_header(rec->session, rec->evlist, fd, true);
@@ -813,8 +819,12 @@ int record_parse_callchain_opt(const struct option *opt,
}
ret = parse_callchain_record_opt(arg, &callchain_param);
- if (!ret)
+ if (!ret) {
+ /* Enable data address sampling for DWARF unwind. */
+ if (callchain_param.record_mode == CALLCHAIN_DWARF)
+ record->sample_address = true;
callchain_debug();
+ }
return ret;
}
@@ -837,6 +847,19 @@ int record_callchain_opt(const struct option *opt,
static int perf_record_config(const char *var, const char *value, void *cb)
{
+ struct record *rec = cb;
+
+ if (!strcmp(var, "record.build-id")) {
+ if (!strcmp(value, "cache"))
+ rec->no_buildid_cache = false;
+ else if (!strcmp(value, "no-cache"))
+ rec->no_buildid_cache = true;
+ else if (!strcmp(value, "skip"))
+ rec->no_buildid = true;
+ else
+ return -1;
+ return 0;
+ }
if (!strcmp(var, "record.call-graph"))
var = "call-graph.record-mode"; /* fall-through */
@@ -1113,12 +1136,14 @@ struct option __record_options[] = {
"per thread proc mmap processing timeout in ms"),
OPT_BOOLEAN(0, "switch-events", &record.opts.record_switch_events,
"Record context switch events"),
-#ifdef HAVE_LIBBPF_SUPPORT
OPT_STRING(0, "clang-path", &llvm_param.clang_path, "clang path",
"clang binary to use for compiling BPF scriptlets"),
OPT_STRING(0, "clang-opt", &llvm_param.clang_opt, "clang options",
"options passed to clang when compiling BPF scriptlets"),
-#endif
+ OPT_STRING(0, "vmlinux", &symbol_conf.vmlinux_name,
+ "file", "vmlinux pathname"),
+ OPT_BOOLEAN(0, "buildid-all", &record.buildid_all,
+ "Record build-id of all DSOs regardless of hits"),
OPT_END()
};
@@ -1130,6 +1155,27 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
struct record *rec = &record;
char errbuf[BUFSIZ];
+#ifndef HAVE_LIBBPF_SUPPORT
+# define set_nobuild(s, l, c) set_option_nobuild(record_options, s, l, "NO_LIBBPF=1", c)
+ set_nobuild('\0', "clang-path", true);
+ set_nobuild('\0', "clang-opt", true);
+# undef set_nobuild
+#endif
+
+#ifndef HAVE_BPF_PROLOGUE
+# if !defined (HAVE_DWARF_SUPPORT)
+# define REASON "NO_DWARF=1"
+# elif !defined (HAVE_LIBBPF_SUPPORT)
+# define REASON "NO_LIBBPF=1"
+# else
+# define REASON "this architecture doesn't support BPF prologue"
+# endif
+# define set_nobuild(s, l, c) set_option_nobuild(record_options, s, l, REASON, c)
+ set_nobuild('\0', "vmlinux", true);
+# undef set_nobuild
+# undef REASON
+#endif
+
rec->evlist = perf_evlist__new();
if (rec->evlist == NULL)
return -ENOMEM;
@@ -1215,6 +1261,14 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
if (err)
goto out_symbol_exit;
+ /*
+ * 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)
+ rec->buildid_all = true;
+
if (record_opts__config(&rec->opts)) {
err = -EINVAL;
goto out_symbol_exit;
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index f256fac1e722..2bf537f190a0 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -27,7 +27,8 @@
#include "util/session.h"
#include "util/tool.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
+#include <subcmd/exec-cmd.h>
#include "util/parse-events.h"
#include "util/thread.h"
@@ -45,7 +46,6 @@ struct report {
struct perf_tool tool;
struct perf_session *session;
bool use_tui, use_gtk, use_stdio;
- bool hide_unresolved;
bool dont_use_callchains;
bool show_full_info;
bool show_threads;
@@ -146,7 +146,7 @@ static int process_sample_event(struct perf_tool *tool,
struct hist_entry_iter iter = {
.evsel = evsel,
.sample = sample,
- .hide_unresolved = rep->hide_unresolved,
+ .hide_unresolved = symbol_conf.hide_unresolved,
.add_entry_cb = hist_iter__report_callback,
};
int ret = 0;
@@ -157,7 +157,7 @@ static int process_sample_event(struct perf_tool *tool,
return -1;
}
- if (rep->hide_unresolved && al.sym == NULL)
+ if (symbol_conf.hide_unresolved && al.sym == NULL)
goto out_put;
if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap))
@@ -434,7 +434,14 @@ static int report__browse_hists(struct report *rep)
int ret;
struct perf_session *session = rep->session;
struct perf_evlist *evlist = session->evlist;
- const char *help = "For a higher level overview, try: perf report --sort comm,dso";
+ const char *help = perf_tip(system_path(TIPDIR));
+
+ if (help == NULL) {
+ /* fallback for people who don't install perf ;-) */
+ help = perf_tip(DOCDIR);
+ if (help == NULL)
+ help = "Cannot load tips.txt file, please install perf!";
+ }
switch (use_browser) {
case 1:
@@ -514,20 +521,26 @@ static int __cmd_report(struct report *rep)
if (rep->cpu_list) {
ret = perf_session__cpu_bitmap(session, rep->cpu_list,
rep->cpu_bitmap);
- if (ret)
+ if (ret) {
+ ui__error("failed to set cpu bitmap\n");
return ret;
+ }
}
if (rep->show_threads)
perf_read_values_init(&rep->show_threads_values);
ret = report__setup_sample_type(rep);
- if (ret)
+ if (ret) {
+ /* report__setup_sample_type() already showed error message */
return ret;
+ }
ret = perf_session__process_events(session);
- if (ret)
+ if (ret) {
+ ui__error("failed to process sample\n");
return ret;
+ }
report__warn_kptr_restrict(rep);
@@ -625,7 +638,7 @@ parse_percent_limit(const struct option *opt, const char *str,
return 0;
}
-#define CALLCHAIN_DEFAULT_OPT "graph,0.5,caller,function"
+#define CALLCHAIN_DEFAULT_OPT "graph,0.5,caller,function,percent"
const char report_callchain_help[] = "Display call graph (stack chain/backtrace):\n\n"
CALLCHAIN_REPORT_HELP
@@ -708,7 +721,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other,
"Only display entries with parent-match"),
OPT_CALLBACK_DEFAULT('g', "call-graph", &report,
- "print_type,threshold[,print_limit],order,sort_key[,branch]",
+ "print_type,threshold[,print_limit],order,sort_key[,branch],value",
report_callchain_help, &report_parse_callchain_opt,
callchain_default_opt),
OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain,
@@ -740,7 +753,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_STRING_NOEMPTY('t', "field-separator", &symbol_conf.field_sep, "separator",
"separator for columns, no spaces will be added between "
"columns '.' is reserved."),
- OPT_BOOLEAN('U', "hide-unresolved", &report.hide_unresolved,
+ OPT_BOOLEAN('U', "hide-unresolved", &symbol_conf.hide_unresolved,
"Only display entries resolved to a symbol"),
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
"Look for files with symbols relative to this directory"),
@@ -783,6 +796,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
"Show callgraph from reference event"),
OPT_INTEGER(0, "socket-filter", &report.socket_filter,
"only show processor socket that match with this filter"),
+ OPT_BOOLEAN(0, "raw-trace", &symbol_conf.raw_trace,
+ "Show raw trace event output (do not use print fmt or plugins)"),
OPT_END()
};
struct perf_data_file file = {
@@ -796,6 +811,16 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
perf_config(report__config, &report);
argc = parse_options(argc, argv, options, report_usage, 0);
+ if (argc) {
+ /*
+ * Special case: if there's an argument left then assume that
+ * it's a symbol filter:
+ */
+ if (argc > 1)
+ usage_with_options(report_usage, options);
+
+ report.symbol_filter_str = argv[0];
+ }
if (symbol_conf.vmlinux_name &&
access(symbol_conf.vmlinux_name, R_OK)) {
@@ -882,7 +907,7 @@ repeat:
symbol_conf.cumulate_callchain = false;
}
- if (setup_sorting() < 0) {
+ if (setup_sorting(session->evlist) < 0) {
if (sort_order)
parse_options_usage(report_usage, options, "s", 1);
if (field_order)
@@ -941,17 +966,6 @@ repeat:
if (symbol__init(&session->header.env) < 0)
goto error;
- if (argc) {
- /*
- * Special case: if there's an argument left then assume that
- * it's a symbol filter:
- */
- if (argc > 1)
- usage_with_options(report_usage, options);
-
- report.symbol_filter_str = argv[0];
- }
-
sort__setup_elide(stdout);
ret = __cmd_report(&report);
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index e3d3e32c0a93..871b55ae22a4 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -12,7 +12,7 @@
#include "util/tool.h"
#include "util/cloexec.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "util/trace-event.h"
#include "util/debug.h"
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index 72b5deb4bd79..c691214d820f 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -3,9 +3,9 @@
#include "perf.h"
#include "util/cache.h"
#include "util/debug.h"
-#include "util/exec_cmd.h"
+#include <subcmd/exec-cmd.h>
#include "util/header.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "util/perf_regs.h"
#include "util/session.h"
#include "util/tool.h"
@@ -18,7 +18,11 @@
#include "util/sort.h"
#include "util/data.h"
#include "util/auxtrace.h"
+#include "util/cpumap.h"
+#include "util/thread_map.h"
+#include "util/stat.h"
#include <linux/bitmap.h>
+#include "asm/bug.h"
static char const *script_name;
static char const *generate_script_lang;
@@ -32,6 +36,7 @@ static bool print_flags;
static bool nanosecs;
static const char *cpu_list;
static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
+static struct perf_stat_config stat_config;
unsigned int scripting_max_stack = PERF_MAX_STACK_DEPTH;
@@ -130,6 +135,18 @@ static struct {
.invalid_fields = PERF_OUTPUT_TRACE,
},
+
+ [PERF_TYPE_BREAKPOINT] = {
+ .user_set = false,
+
+ .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |
+ PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |
+ PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP |
+ PERF_OUTPUT_SYM | PERF_OUTPUT_DSO |
+ PERF_OUTPUT_PERIOD,
+
+ .invalid_fields = PERF_OUTPUT_TRACE,
+ },
};
static bool output_set_by_user(void)
@@ -204,6 +221,9 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
struct perf_event_attr *attr = &evsel->attr;
bool allow_user_set;
+ if (perf_header__has_feat(&session->header, HEADER_STAT))
+ return 0;
+
allow_user_set = perf_header__has_feat(&session->header,
HEADER_AUXTRACE);
@@ -588,8 +608,35 @@ static void print_sample_flags(u32 flags)
printf(" %-4s ", str);
}
-static void process_event(union perf_event *event, struct perf_sample *sample,
- struct perf_evsel *evsel, struct addr_location *al)
+struct perf_script {
+ struct perf_tool tool;
+ struct perf_session *session;
+ bool show_task_events;
+ bool show_mmap_events;
+ bool show_switch_events;
+ bool allocated;
+ struct cpu_map *cpus;
+ struct thread_map *threads;
+ int name_width;
+};
+
+static int perf_evlist__max_name_len(struct perf_evlist *evlist)
+{
+ struct perf_evsel *evsel;
+ int max = 0;
+
+ evlist__for_each(evlist, evsel) {
+ int len = strlen(perf_evsel__name(evsel));
+
+ max = MAX(len, max);
+ }
+
+ return max;
+}
+
+static void process_event(struct perf_script *script, union perf_event *event,
+ struct perf_sample *sample, struct perf_evsel *evsel,
+ struct addr_location *al)
{
struct thread *thread = al->thread;
struct perf_event_attr *attr = &evsel->attr;
@@ -604,7 +651,12 @@ static void process_event(union perf_event *event, struct perf_sample *sample,
if (PRINT_FIELD(EVNAME)) {
const char *evname = perf_evsel__name(evsel);
- printf("%s: ", evname ? evname : "[unknown]");
+
+ if (!script->name_width)
+ script->name_width = perf_evlist__max_name_len(script->session->evlist);
+
+ printf("%*s: ", script->name_width,
+ evname ? evname : "[unknown]");
}
if (print_flags)
@@ -643,65 +695,81 @@ static void process_event(union perf_event *event, struct perf_sample *sample,
printf("\n");
}
-static int default_start_script(const char *script __maybe_unused,
- int argc __maybe_unused,
- const char **argv __maybe_unused)
-{
- return 0;
-}
+static struct scripting_ops *scripting_ops;
-static int default_flush_script(void)
+static void __process_stat(struct perf_evsel *counter, u64 tstamp)
{
- return 0;
+ int nthreads = thread_map__nr(counter->threads);
+ int ncpus = perf_evsel__nr_cpus(counter);
+ int cpu, thread;
+ static int header_printed;
+
+ if (counter->system_wide)
+ nthreads = 1;
+
+ if (!header_printed) {
+ printf("%3s %8s %15s %15s %15s %15s %s\n",
+ "CPU", "THREAD", "VAL", "ENA", "RUN", "TIME", "EVENT");
+ header_printed = 1;
+ }
+
+ for (thread = 0; thread < nthreads; thread++) {
+ for (cpu = 0; cpu < ncpus; cpu++) {
+ struct perf_counts_values *counts;
+
+ counts = perf_counts(counter->counts, cpu, thread);
+
+ printf("%3d %8d %15" PRIu64 " %15" PRIu64 " %15" PRIu64 " %15" PRIu64 " %s\n",
+ counter->cpus->map[cpu],
+ thread_map__pid(counter->threads, thread),
+ counts->val,
+ counts->ena,
+ counts->run,
+ tstamp,
+ perf_evsel__name(counter));
+ }
+ }
}
-static int default_stop_script(void)
+static void process_stat(struct perf_evsel *counter, u64 tstamp)
{
- return 0;
+ if (scripting_ops && scripting_ops->process_stat)
+ scripting_ops->process_stat(&stat_config, counter, tstamp);
+ else
+ __process_stat(counter, tstamp);
}
-static int default_generate_script(struct pevent *pevent __maybe_unused,
- const char *outfile __maybe_unused)
+static void process_stat_interval(u64 tstamp)
{
- return 0;
+ if (scripting_ops && scripting_ops->process_stat_interval)
+ scripting_ops->process_stat_interval(tstamp);
}
-static struct scripting_ops default_scripting_ops = {
- .start_script = default_start_script,
- .flush_script = default_flush_script,
- .stop_script = default_stop_script,
- .process_event = process_event,
- .generate_script = default_generate_script,
-};
-
-static struct scripting_ops *scripting_ops;
-
static void setup_scripting(void)
{
setup_perl_scripting();
setup_python_scripting();
-
- scripting_ops = &default_scripting_ops;
}
static int flush_scripting(void)
{
- return scripting_ops->flush_script();
+ return scripting_ops ? scripting_ops->flush_script() : 0;
}
static int cleanup_scripting(void)
{
pr_debug("\nperf script stopped\n");
- return scripting_ops->stop_script();
+ return scripting_ops ? scripting_ops->stop_script() : 0;
}
-static int process_sample_event(struct perf_tool *tool __maybe_unused,
+static int process_sample_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
struct perf_evsel *evsel,
struct machine *machine)
{
+ struct perf_script *scr = container_of(tool, struct perf_script, tool);
struct addr_location al;
if (debug_mode) {
@@ -727,20 +795,16 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
if (cpu_list && !test_bit(sample->cpu, cpu_bitmap))
goto out_put;
- scripting_ops->process_event(event, sample, evsel, &al);
+ if (scripting_ops)
+ scripting_ops->process_event(event, sample, evsel, &al);
+ else
+ process_event(scr, event, sample, evsel, &al);
+
out_put:
addr_location__put(&al);
return 0;
}
-struct perf_script {
- struct perf_tool tool;
- struct perf_session *session;
- bool show_task_events;
- bool show_mmap_events;
- bool show_switch_events;
-};
-
static int process_attr(struct perf_tool *tool, union perf_event *event,
struct perf_evlist **pevlist)
{
@@ -1156,6 +1220,8 @@ static int parse_output_fields(const struct option *opt __maybe_unused,
type = PERF_TYPE_TRACEPOINT;
else if (!strcmp(str, "raw"))
type = PERF_TYPE_RAW;
+ else if (!strcmp(str, "break"))
+ type = PERF_TYPE_BREAKPOINT;
else {
fprintf(stderr, "Invalid event type in field string.\n");
rc = -EINVAL;
@@ -1421,7 +1487,7 @@ static int list_available_scripts(const struct option *opt __maybe_unused,
char first_half[BUFSIZ];
char *script_root;
- snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path());
+ snprintf(scripts_path, MAXPATHLEN, "%s/scripts", get_argv_exec_path());
scripts_dir = opendir(scripts_path);
if (!scripts_dir)
@@ -1542,7 +1608,7 @@ int find_scripts(char **scripts_array, char **scripts_path_array)
if (!session)
return -1;
- snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path());
+ snprintf(scripts_path, MAXPATHLEN, "%s/scripts", get_argv_exec_path());
scripts_dir = opendir(scripts_path);
if (!scripts_dir) {
@@ -1600,7 +1666,7 @@ static char *get_script_path(const char *script_root, const char *suffix)
char lang_path[MAXPATHLEN];
char *__script_root;
- snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path());
+ snprintf(scripts_path, MAXPATHLEN, "%s/scripts", get_argv_exec_path());
scripts_dir = opendir(scripts_path);
if (!scripts_dir)
@@ -1695,6 +1761,87 @@ static void script__setup_sample_type(struct perf_script *script)
}
}
+static int process_stat_round_event(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_session *session)
+{
+ struct stat_round_event *round = &event->stat_round;
+ struct perf_evsel *counter;
+
+ evlist__for_each(session->evlist, counter) {
+ perf_stat_process_counter(&stat_config, counter);
+ process_stat(counter, round->time);
+ }
+
+ process_stat_interval(round->time);
+ return 0;
+}
+
+static int process_stat_config_event(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_session *session __maybe_unused)
+{
+ perf_event__read_stat_config(&stat_config, &event->stat_config);
+ return 0;
+}
+
+static int set_maps(struct perf_script *script)
+{
+ struct perf_evlist *evlist = script->session->evlist;
+
+ if (!script->cpus || !script->threads)
+ return 0;
+
+ if (WARN_ONCE(script->allocated, "stats double allocation\n"))
+ return -EINVAL;
+
+ perf_evlist__set_maps(evlist, script->cpus, script->threads);
+
+ if (perf_evlist__alloc_stats(evlist, true))
+ return -ENOMEM;
+
+ script->allocated = true;
+ return 0;
+}
+
+static
+int process_thread_map_event(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_session *session __maybe_unused)
+{
+ struct perf_script *script = container_of(tool, struct perf_script, tool);
+
+ if (script->threads) {
+ pr_warning("Extra thread map event, ignoring.\n");
+ return 0;
+ }
+
+ script->threads = thread_map__new_event(&event->thread_map);
+ if (!script->threads)
+ return -ENOMEM;
+
+ return set_maps(script);
+}
+
+static
+int process_cpu_map_event(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_session *session __maybe_unused)
+{
+ struct perf_script *script = container_of(tool, struct perf_script, tool);
+
+ if (script->cpus) {
+ pr_warning("Extra cpu map event, ignoring.\n");
+ return 0;
+ }
+
+ script->cpus = cpu_map__new_data(&event->cpu_map.data);
+ if (!script->cpus)
+ return -ENOMEM;
+
+ return set_maps(script);
+}
+
int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
{
bool show_full_info = false;
@@ -1723,6 +1870,11 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
.auxtrace_info = perf_event__process_auxtrace_info,
.auxtrace = perf_event__process_auxtrace,
.auxtrace_error = perf_event__process_auxtrace_error,
+ .stat = perf_event__process_stat_event,
+ .stat_round = process_stat_round_event,
+ .stat_config = process_stat_config_event,
+ .thread_map = process_thread_map_event,
+ .cpu_map = process_cpu_map_event,
.ordered_events = true,
.ordering_requires_timestamps = true,
},
@@ -1836,7 +1988,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
scripting_max_stack = itrace_synth_opts.callchain_sz;
/* make sure PERF_EXEC_PATH is set for scripts */
- perf_set_argv_exec_path(perf_exec_path());
+ set_argv_exec_path(get_argv_exec_path());
if (argc && !script_name && !rec_script_path && !rep_script_path) {
int live_pipe[2];
@@ -2076,6 +2228,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
flush_scripting();
out_delete:
+ perf_evlist__free_stats(session->evlist);
perf_session__delete(session);
if (script_started)
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index e77880b5094d..038e877081b6 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -45,7 +45,7 @@
#include "builtin.h"
#include "util/cgroup.h"
#include "util/util.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "util/parse-events.h"
#include "util/pmu.h"
#include "util/event.h"
@@ -59,6 +59,9 @@
#include "util/thread.h"
#include "util/thread_map.h"
#include "util/counts.h"
+#include "util/session.h"
+#include "util/tool.h"
+#include "asm/bug.h"
#include <stdlib.h>
#include <sys/prctl.h>
@@ -126,6 +129,21 @@ static bool append_file;
static const char *output_name;
static int output_fd;
+struct perf_stat {
+ bool record;
+ struct perf_data_file file;
+ struct perf_session *session;
+ u64 bytes_written;
+ struct perf_tool tool;
+ bool maps_allocated;
+ struct cpu_map *cpus;
+ struct thread_map *threads;
+ enum aggr_mode aggr_mode;
+};
+
+static struct perf_stat perf_stat;
+#define STAT_RECORD perf_stat.record
+
static volatile int done = 0;
static struct perf_stat_config stat_config = {
@@ -161,15 +179,43 @@ static int create_perf_stat_counter(struct perf_evsel *evsel)
attr->inherit = !no_inherit;
- if (target__has_cpu(&target))
- return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel));
+ /*
+ * Some events get initialized with sample_(period/type) set,
+ * like tracepoints. Clear it up for counting.
+ */
+ attr->sample_period = 0;
+
+ /*
+ * But set sample_type to PERF_SAMPLE_IDENTIFIER, which should be harmless
+ * while avoiding that older tools show confusing messages.
+ *
+ * However for pipe sessions we need to keep it zero,
+ * because script's perf_evsel__check_attr is triggered
+ * by attr->sample_type != 0, and we can't run it on
+ * stat sessions.
+ */
+ if (!(STAT_RECORD && perf_stat.file.is_pipe))
+ attr->sample_type = PERF_SAMPLE_IDENTIFIER;
- if (!target__has_task(&target) && perf_evsel__is_group_leader(evsel)) {
+ /*
+ * Disabling all counters initially, they will be enabled
+ * either manually by us or by kernel via enable_on_exec
+ * set later.
+ */
+ if (perf_evsel__is_group_leader(evsel)) {
attr->disabled = 1;
- if (!initial_delay)
+
+ /*
+ * In case of initial_delay we enable tracee
+ * events manually.
+ */
+ if (target__none(&target) && !initial_delay)
attr->enable_on_exec = 1;
}
+ if (target__has_cpu(&target))
+ return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel));
+
return perf_evsel__open_per_thread(evsel, evsel_list->threads);
}
@@ -185,6 +231,42 @@ static inline int nsec_counter(struct perf_evsel *evsel)
return 0;
}
+static int process_synthesized_event(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine __maybe_unused)
+{
+ if (perf_data_file__write(&perf_stat.file, event, event->header.size) < 0) {
+ pr_err("failed to write perf data, error: %m\n");
+ return -1;
+ }
+
+ perf_stat.bytes_written += event->header.size;
+ return 0;
+}
+
+static int write_stat_round_event(u64 tm, u64 type)
+{
+ return perf_event__synthesize_stat_round(NULL, tm, type,
+ process_synthesized_event,
+ NULL);
+}
+
+#define WRITE_STAT_ROUND_EVENT(time, interval) \
+ write_stat_round_event(time, PERF_STAT_ROUND_TYPE__ ## interval)
+
+#define SID(e, x, y) xyarray__entry(e->sample_id, x, y)
+
+static int
+perf_evsel__write_stat_event(struct perf_evsel *counter, u32 cpu, u32 thread,
+ struct perf_counts_values *count)
+{
+ struct perf_sample_id *sid = SID(counter, cpu, thread);
+
+ return perf_event__synthesize_stat(NULL, cpu, thread, sid->id, count,
+ process_synthesized_event, NULL);
+}
+
/*
* Read out the results of a single counter:
* do not aggregate counts across CPUs in system-wide mode
@@ -208,6 +290,13 @@ static int read_counter(struct perf_evsel *counter)
count = perf_counts(counter->counts, cpu, thread);
if (perf_evsel__read(counter, cpu, thread, count))
return -1;
+
+ if (STAT_RECORD) {
+ if (perf_evsel__write_stat_event(counter, cpu, thread, count)) {
+ pr_err("failed to write stat event\n");
+ return -1;
+ }
+ }
}
}
@@ -241,21 +330,26 @@ static void process_interval(void)
clock_gettime(CLOCK_MONOTONIC, &ts);
diff_timespec(&rs, &ts, &ref_time);
+ if (STAT_RECORD) {
+ if (WRITE_STAT_ROUND_EVENT(rs.tv_sec * NSECS_PER_SEC + rs.tv_nsec, INTERVAL))
+ pr_err("failed to write stat round event\n");
+ }
+
print_counters(&rs, 0, NULL);
}
-static void handle_initial_delay(void)
+static void enable_counters(void)
{
- struct perf_evsel *counter;
-
- if (initial_delay) {
- const int ncpus = cpu_map__nr(evsel_list->cpus),
- nthreads = thread_map__nr(evsel_list->threads);
-
+ if (initial_delay)
usleep(initial_delay * 1000);
- evlist__for_each(evsel_list, counter)
- perf_evsel__enable(counter, ncpus, nthreads);
- }
+
+ /*
+ * We need to enable counters only if:
+ * - we don't have tracee (attaching to task or cpu)
+ * - we have initial delay configured
+ */
+ if (!target__none(&target) || initial_delay)
+ perf_evlist__enable(evsel_list);
}
static volatile int workload_exec_errno;
@@ -271,6 +365,135 @@ static void workload_exec_failed_signal(int signo __maybe_unused, siginfo_t *inf
workload_exec_errno = info->si_value.sival_int;
}
+static bool has_unit(struct perf_evsel *counter)
+{
+ return counter->unit && *counter->unit;
+}
+
+static bool has_scale(struct perf_evsel *counter)
+{
+ return counter->scale != 1;
+}
+
+static int perf_stat_synthesize_config(bool is_pipe)
+{
+ struct perf_evsel *counter;
+ int err;
+
+ if (is_pipe) {
+ err = perf_event__synthesize_attrs(NULL, perf_stat.session,
+ process_synthesized_event);
+ if (err < 0) {
+ pr_err("Couldn't synthesize attrs.\n");
+ return err;
+ }
+ }
+
+ /*
+ * Synthesize other events stuff not carried within
+ * attr event - unit, scale, name
+ */
+ evlist__for_each(evsel_list, counter) {
+ if (!counter->supported)
+ continue;
+
+ /*
+ * Synthesize unit and scale only if it's defined.
+ */
+ if (has_unit(counter)) {
+ err = perf_event__synthesize_event_update_unit(NULL, counter, process_synthesized_event);
+ if (err < 0) {
+ pr_err("Couldn't synthesize evsel unit.\n");
+ return err;
+ }
+ }
+
+ if (has_scale(counter)) {
+ err = perf_event__synthesize_event_update_scale(NULL, counter, process_synthesized_event);
+ if (err < 0) {
+ pr_err("Couldn't synthesize evsel scale.\n");
+ return err;
+ }
+ }
+
+ if (counter->own_cpus) {
+ err = perf_event__synthesize_event_update_cpus(NULL, counter, process_synthesized_event);
+ if (err < 0) {
+ pr_err("Couldn't synthesize evsel scale.\n");
+ return err;
+ }
+ }
+
+ /*
+ * Name is needed only for pipe output,
+ * perf.data carries event names.
+ */
+ if (is_pipe) {
+ err = perf_event__synthesize_event_update_name(NULL, counter, process_synthesized_event);
+ if (err < 0) {
+ pr_err("Couldn't synthesize evsel name.\n");
+ return err;
+ }
+ }
+ }
+
+ err = perf_event__synthesize_thread_map2(NULL, evsel_list->threads,
+ process_synthesized_event,
+ NULL);
+ if (err < 0) {
+ pr_err("Couldn't synthesize thread map.\n");
+ return err;
+ }
+
+ err = perf_event__synthesize_cpu_map(NULL, evsel_list->cpus,
+ process_synthesized_event, NULL);
+ if (err < 0) {
+ pr_err("Couldn't synthesize thread map.\n");
+ return err;
+ }
+
+ err = perf_event__synthesize_stat_config(NULL, &stat_config,
+ process_synthesized_event, NULL);
+ if (err < 0) {
+ pr_err("Couldn't synthesize config.\n");
+ return err;
+ }
+
+ return 0;
+}
+
+#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
+
+static int __store_counter_ids(struct perf_evsel *counter,
+ struct cpu_map *cpus,
+ struct thread_map *threads)
+{
+ int cpu, thread;
+
+ for (cpu = 0; cpu < cpus->nr; cpu++) {
+ for (thread = 0; thread < threads->nr; thread++) {
+ int fd = FD(counter, cpu, thread);
+
+ if (perf_evlist__id_add_fd(evsel_list, counter,
+ cpu, thread, fd) < 0)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int store_counter_ids(struct perf_evsel *counter)
+{
+ struct cpu_map *cpus = counter->cpus;
+ struct thread_map *threads = counter->threads;
+
+ if (perf_evsel__alloc_id(counter, cpus->nr, threads->nr))
+ return -ENOMEM;
+
+ return __store_counter_ids(counter, cpus, threads);
+}
+
static int __run_perf_stat(int argc, const char **argv)
{
int interval = stat_config.interval;
@@ -281,6 +504,7 @@ static int __run_perf_stat(int argc, const char **argv)
size_t l;
int status = 0;
const bool forks = (argc > 0);
+ bool is_pipe = STAT_RECORD ? perf_stat.file.is_pipe : false;
if (interval) {
ts.tv_sec = interval / 1000;
@@ -291,7 +515,7 @@ static int __run_perf_stat(int argc, const char **argv)
}
if (forks) {
- if (perf_evlist__prepare_workload(evsel_list, &target, argv, false,
+ if (perf_evlist__prepare_workload(evsel_list, &target, argv, is_pipe,
workload_exec_failed_signal) < 0) {
perror("failed to prepare workload");
return -1;
@@ -335,6 +559,9 @@ static int __run_perf_stat(int argc, const char **argv)
l = strlen(counter->unit);
if (l > unit_width)
unit_width = l;
+
+ if (STAT_RECORD && store_counter_ids(counter))
+ return -1;
}
if (perf_evlist__apply_filters(evsel_list, &counter)) {
@@ -344,6 +571,24 @@ static int __run_perf_stat(int argc, const char **argv)
return -1;
}
+ if (STAT_RECORD) {
+ int err, fd = perf_data_file__fd(&perf_stat.file);
+
+ if (is_pipe) {
+ err = perf_header__write_pipe(perf_data_file__fd(&perf_stat.file));
+ } else {
+ err = perf_session__write_header(perf_stat.session, evsel_list,
+ fd, false);
+ }
+
+ if (err < 0)
+ return err;
+
+ err = perf_stat_synthesize_config(is_pipe);
+ if (err < 0)
+ return err;
+ }
+
/*
* Enable counters and exec the command:
*/
@@ -352,7 +597,7 @@ static int __run_perf_stat(int argc, const char **argv)
if (forks) {
perf_evlist__start_workload(evsel_list);
- handle_initial_delay();
+ enable_counters();
if (interval) {
while (!waitpid(child_pid, &status, WNOHANG)) {
@@ -371,7 +616,7 @@ static int __run_perf_stat(int argc, const char **argv)
if (WIFSIGNALED(status))
psignal(WTERMSIG(status), argv[0]);
} else {
- handle_initial_delay();
+ enable_counters();
while (!done) {
nanosleep(&ts, NULL);
if (interval)
@@ -810,8 +1055,8 @@ static void print_header(int argc, const char **argv)
else if (target.cpu_list)
fprintf(output, "\'CPU(s) %s", target.cpu_list);
else if (!target__has_task(&target)) {
- fprintf(output, "\'%s", argv[0]);
- for (i = 1; i < argc; i++)
+ fprintf(output, "\'%s", argv ? argv[0] : "pipe");
+ for (i = 1; argv && (i < argc); i++)
fprintf(output, " %s", argv[i]);
} else if (target.pid)
fprintf(output, "process id \'%s", target.pid);
@@ -847,6 +1092,10 @@ static void print_counters(struct timespec *ts, int argc, const char **argv)
struct perf_evsel *counter;
char buf[64], *prefix = NULL;
+ /* Do not print anything if we record to the pipe. */
+ if (STAT_RECORD && perf_stat.file.is_pipe)
+ return;
+
if (interval)
print_interval(prefix = buf, ts);
else
@@ -1077,6 +1326,109 @@ static int perf_stat_init_aggr_mode(void)
return cpus_aggr_map ? 0 : -ENOMEM;
}
+static void perf_stat__exit_aggr_mode(void)
+{
+ cpu_map__put(aggr_map);
+ cpu_map__put(cpus_aggr_map);
+ aggr_map = NULL;
+ cpus_aggr_map = NULL;
+}
+
+static inline int perf_env__get_cpu(struct perf_env *env, struct cpu_map *map, int idx)
+{
+ int cpu;
+
+ if (idx > map->nr)
+ return -1;
+
+ cpu = map->map[idx];
+
+ if (cpu >= env->nr_cpus_online)
+ return -1;
+
+ return cpu;
+}
+
+static int perf_env__get_socket(struct cpu_map *map, int idx, void *data)
+{
+ struct perf_env *env = data;
+ int cpu = perf_env__get_cpu(env, map, idx);
+
+ return cpu == -1 ? -1 : env->cpu[cpu].socket_id;
+}
+
+static int perf_env__get_core(struct cpu_map *map, int idx, void *data)
+{
+ struct perf_env *env = data;
+ int core = -1, cpu = perf_env__get_cpu(env, map, idx);
+
+ if (cpu != -1) {
+ int socket_id = env->cpu[cpu].socket_id;
+
+ /*
+ * Encode socket in upper 16 bits
+ * core_id is relative to socket, and
+ * we need a global id. So we combine
+ * socket + core id.
+ */
+ core = (socket_id << 16) | (env->cpu[cpu].core_id & 0xffff);
+ }
+
+ return core;
+}
+
+static int perf_env__build_socket_map(struct perf_env *env, struct cpu_map *cpus,
+ struct cpu_map **sockp)
+{
+ return cpu_map__build_map(cpus, sockp, perf_env__get_socket, env);
+}
+
+static int perf_env__build_core_map(struct perf_env *env, struct cpu_map *cpus,
+ struct cpu_map **corep)
+{
+ return cpu_map__build_map(cpus, corep, perf_env__get_core, env);
+}
+
+static int perf_stat__get_socket_file(struct cpu_map *map, int idx)
+{
+ return perf_env__get_socket(map, idx, &perf_stat.session->header.env);
+}
+
+static int perf_stat__get_core_file(struct cpu_map *map, int idx)
+{
+ return perf_env__get_core(map, idx, &perf_stat.session->header.env);
+}
+
+static int perf_stat_init_aggr_mode_file(struct perf_stat *st)
+{
+ struct perf_env *env = &st->session->header.env;
+
+ switch (stat_config.aggr_mode) {
+ case AGGR_SOCKET:
+ if (perf_env__build_socket_map(env, evsel_list->cpus, &aggr_map)) {
+ perror("cannot build socket map");
+ return -1;
+ }
+ aggr_get_id = perf_stat__get_socket_file;
+ break;
+ case AGGR_CORE:
+ if (perf_env__build_core_map(env, evsel_list->cpus, &aggr_map)) {
+ perror("cannot build core map");
+ return -1;
+ }
+ aggr_get_id = perf_stat__get_core_file;
+ break;
+ case AGGR_NONE:
+ case AGGR_GLOBAL:
+ case AGGR_THREAD:
+ case AGGR_UNSET:
+ default:
+ break;
+ }
+
+ return 0;
+}
+
/*
* Add default attributes, if there were no attributes specified or
* if -d/--detailed, -d -d or -d -d -d is used:
@@ -1236,6 +1588,225 @@ static int add_default_attributes(void)
return perf_evlist__add_default_attrs(evsel_list, very_very_detailed_attrs);
}
+static const char * const stat_record_usage[] = {
+ "perf stat record [<options>]",
+ NULL,
+};
+
+static void init_features(struct perf_session *session)
+{
+ int feat;
+
+ for (feat = HEADER_FIRST_FEATURE; feat < HEADER_LAST_FEATURE; feat++)
+ perf_header__set_feat(&session->header, feat);
+
+ perf_header__clear_feat(&session->header, HEADER_BUILD_ID);
+ perf_header__clear_feat(&session->header, HEADER_TRACING_DATA);
+ perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK);
+ perf_header__clear_feat(&session->header, HEADER_AUXTRACE);
+}
+
+static int __cmd_record(int argc, const char **argv)
+{
+ struct perf_session *session;
+ struct perf_data_file *file = &perf_stat.file;
+
+ argc = parse_options(argc, argv, stat_options, stat_record_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ if (output_name)
+ file->path = output_name;
+
+ if (run_count != 1 || forever) {
+ pr_err("Cannot use -r option with perf stat record.\n");
+ return -1;
+ }
+
+ session = perf_session__new(file, false, NULL);
+ if (session == NULL) {
+ pr_err("Perf session creation failed.\n");
+ return -1;
+ }
+
+ init_features(session);
+
+ session->evlist = evsel_list;
+ perf_stat.session = session;
+ perf_stat.record = true;
+ return argc;
+}
+
+static int process_stat_round_event(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_session *session)
+{
+ struct stat_round_event *round = &event->stat_round;
+ struct perf_evsel *counter;
+ struct timespec tsh, *ts = NULL;
+ const char **argv = session->header.env.cmdline_argv;
+ int argc = session->header.env.nr_cmdline;
+
+ evlist__for_each(evsel_list, counter)
+ perf_stat_process_counter(&stat_config, counter);
+
+ if (round->type == PERF_STAT_ROUND_TYPE__FINAL)
+ update_stats(&walltime_nsecs_stats, round->time);
+
+ if (stat_config.interval && round->time) {
+ tsh.tv_sec = round->time / NSECS_PER_SEC;
+ tsh.tv_nsec = round->time % NSECS_PER_SEC;
+ ts = &tsh;
+ }
+
+ print_counters(ts, argc, argv);
+ return 0;
+}
+
+static
+int process_stat_config_event(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_session *session __maybe_unused)
+{
+ struct perf_stat *st = container_of(tool, struct perf_stat, tool);
+
+ perf_event__read_stat_config(&stat_config, &event->stat_config);
+
+ if (cpu_map__empty(st->cpus)) {
+ if (st->aggr_mode != AGGR_UNSET)
+ pr_warning("warning: processing task data, aggregation mode not set\n");
+ return 0;
+ }
+
+ if (st->aggr_mode != AGGR_UNSET)
+ stat_config.aggr_mode = st->aggr_mode;
+
+ if (perf_stat.file.is_pipe)
+ perf_stat_init_aggr_mode();
+ else
+ perf_stat_init_aggr_mode_file(st);
+
+ return 0;
+}
+
+static int set_maps(struct perf_stat *st)
+{
+ if (!st->cpus || !st->threads)
+ return 0;
+
+ if (WARN_ONCE(st->maps_allocated, "stats double allocation\n"))
+ return -EINVAL;
+
+ perf_evlist__set_maps(evsel_list, st->cpus, st->threads);
+
+ if (perf_evlist__alloc_stats(evsel_list, true))
+ return -ENOMEM;
+
+ st->maps_allocated = true;
+ return 0;
+}
+
+static
+int process_thread_map_event(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_session *session __maybe_unused)
+{
+ struct perf_stat *st = container_of(tool, struct perf_stat, tool);
+
+ if (st->threads) {
+ pr_warning("Extra thread map event, ignoring.\n");
+ return 0;
+ }
+
+ st->threads = thread_map__new_event(&event->thread_map);
+ if (!st->threads)
+ return -ENOMEM;
+
+ return set_maps(st);
+}
+
+static
+int process_cpu_map_event(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_session *session __maybe_unused)
+{
+ struct perf_stat *st = container_of(tool, struct perf_stat, tool);
+ struct cpu_map *cpus;
+
+ if (st->cpus) {
+ pr_warning("Extra cpu map event, ignoring.\n");
+ return 0;
+ }
+
+ cpus = cpu_map__new_data(&event->cpu_map.data);
+ if (!cpus)
+ return -ENOMEM;
+
+ st->cpus = cpus;
+ return set_maps(st);
+}
+
+static const char * const stat_report_usage[] = {
+ "perf stat report [<options>]",
+ NULL,
+};
+
+static struct perf_stat perf_stat = {
+ .tool = {
+ .attr = perf_event__process_attr,
+ .event_update = perf_event__process_event_update,
+ .thread_map = process_thread_map_event,
+ .cpu_map = process_cpu_map_event,
+ .stat_config = process_stat_config_event,
+ .stat = perf_event__process_stat_event,
+ .stat_round = process_stat_round_event,
+ },
+ .aggr_mode = AGGR_UNSET,
+};
+
+static int __cmd_report(int argc, const char **argv)
+{
+ struct perf_session *session;
+ const struct option options[] = {
+ OPT_STRING('i', "input", &input_name, "file", "input file name"),
+ OPT_SET_UINT(0, "per-socket", &perf_stat.aggr_mode,
+ "aggregate counts per processor socket", AGGR_SOCKET),
+ OPT_SET_UINT(0, "per-core", &perf_stat.aggr_mode,
+ "aggregate counts per physical processor core", AGGR_CORE),
+ OPT_SET_UINT('A', "no-aggr", &perf_stat.aggr_mode,
+ "disable CPU count aggregation", AGGR_NONE),
+ OPT_END()
+ };
+ struct stat st;
+ int ret;
+
+ argc = parse_options(argc, argv, options, stat_report_usage, 0);
+
+ if (!input_name || !strlen(input_name)) {
+ if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode))
+ input_name = "-";
+ else
+ input_name = "perf.data";
+ }
+
+ perf_stat.file.path = input_name;
+ perf_stat.file.mode = PERF_DATA_MODE_READ;
+
+ session = perf_session__new(&perf_stat.file, false, &perf_stat.tool);
+ if (session == NULL)
+ return -1;
+
+ perf_stat.session = session;
+ stat_config.output = stderr;
+ evsel_list = session->evlist;
+
+ ret = perf_session__process_events(session);
+ if (ret)
+ return ret;
+
+ perf_session__delete(session);
+ return 0;
+}
+
int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
{
const char * const stat_usage[] = {
@@ -1246,6 +1817,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
const char *mode;
FILE *output = stderr;
unsigned int interval;
+ const char * const stat_subcommands[] = { "record", "report" };
setlocale(LC_ALL, "");
@@ -1253,12 +1825,30 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
if (evsel_list == NULL)
return -ENOMEM;
- argc = parse_options(argc, argv, stat_options, stat_usage,
- PARSE_OPT_STOP_AT_NON_OPTION);
+ argc = parse_options_subcommand(argc, argv, stat_options, stat_subcommands,
+ (const char **) stat_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ if (csv_sep) {
+ csv_output = true;
+ if (!strcmp(csv_sep, "\\t"))
+ csv_sep = "\t";
+ } else
+ csv_sep = DEFAULT_SEPARATOR;
+
+ if (argc && !strncmp(argv[0], "rec", 3)) {
+ argc = __cmd_record(argc, argv);
+ if (argc < 0)
+ return -1;
+ } else if (argc && !strncmp(argv[0], "rep", 3))
+ return __cmd_report(argc, argv);
interval = stat_config.interval;
- if (output_name && strcmp(output_name, "-"))
+ /*
+ * For record command the -o is already taken care of.
+ */
+ if (!STAT_RECORD && output_name && strcmp(output_name, "-"))
output = NULL;
if (output_name && output_fd) {
@@ -1296,13 +1886,6 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
stat_config.output = output;
- if (csv_sep) {
- csv_output = true;
- if (!strcmp(csv_sep, "\\t"))
- csv_sep = "\t";
- } else
- csv_sep = DEFAULT_SEPARATOR;
-
/*
* let the spreadsheet do the pretty-printing
*/
@@ -1425,6 +2008,42 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
if (!forever && status != -1 && !interval)
print_counters(NULL, argc, argv);
+ if (STAT_RECORD) {
+ /*
+ * We synthesize the kernel mmap record just so that older tools
+ * don't emit warnings about not being able to resolve symbols
+ * due to /proc/sys/kernel/kptr_restrict settings and instear provide
+ * a saner message about no samples being in the perf.data file.
+ *
+ * This also serves to suppress a warning about f_header.data.size == 0
+ * in header.c at the moment 'perf stat record' gets introduced, which
+ * is not really needed once we start adding the stat specific PERF_RECORD_
+ * records, but the need to suppress the kptr_restrict messages in older
+ * tools remain -acme
+ */
+ int fd = perf_data_file__fd(&perf_stat.file);
+ int err = perf_event__synthesize_kernel_mmap((void *)&perf_stat,
+ process_synthesized_event,
+ &perf_stat.session->machines.host);
+ if (err) {
+ pr_warning("Couldn't synthesize the kernel mmap record, harmless, "
+ "older tools may produce warnings about this file\n.");
+ }
+
+ if (!interval) {
+ if (WRITE_STAT_ROUND_EVENT(walltime_nsecs_stats.max, FINAL))
+ pr_err("failed to write stat round event\n");
+ }
+
+ if (!perf_stat.file.is_pipe) {
+ perf_stat.session->header.data_size += perf_stat.bytes_written;
+ perf_session__write_header(perf_stat.session, evsel_list, fd, true);
+ }
+
+ perf_session__delete(perf_stat.session);
+ }
+
+ perf_stat__exit_aggr_mode();
perf_evlist__free_stats(evsel_list);
out:
perf_evlist__delete(evsel_list);
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 30e59620179d..bd7a7757176f 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -30,7 +30,7 @@
#include "perf.h"
#include "util/header.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "util/parse-events.h"
#include "util/event.h"
#include "util/session.h"
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 7e2e72e6d9d1..bf01cbb0ef23 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -34,7 +34,7 @@
#include "util/top.h"
#include "util/util.h"
#include <linux/rbtree.h>
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "util/parse-events.h"
#include "util/cpumap.h"
#include "util/xyarray.h"
@@ -175,42 +175,40 @@ static void perf_top__record_precise_ip(struct perf_top *top,
int counter, u64 ip)
{
struct annotation *notes;
- struct symbol *sym;
+ struct symbol *sym = he->ms.sym;
int err = 0;
- if (he == NULL || he->ms.sym == NULL ||
- ((top->sym_filter_entry == NULL ||
- top->sym_filter_entry->ms.sym != he->ms.sym) && use_browser != 1))
+ if (sym == NULL || (use_browser == 0 &&
+ (top->sym_filter_entry == NULL ||
+ top->sym_filter_entry->ms.sym != sym)))
return;
- sym = he->ms.sym;
notes = symbol__annotation(sym);
if (pthread_mutex_trylock(&notes->lock))
return;
- ip = he->ms.map->map_ip(he->ms.map, ip);
-
- if (ui__has_annotation())
- err = hist_entry__inc_addr_samples(he, counter, ip);
+ err = hist_entry__inc_addr_samples(he, counter, ip);
pthread_mutex_unlock(&notes->lock);
- /*
- * This function is now called with he->hists->lock held.
- * Release it before going to sleep.
- */
- pthread_mutex_unlock(&he->hists->lock);
+ if (unlikely(err)) {
+ /*
+ * This function is now called with he->hists->lock held.
+ * Release it before going to sleep.
+ */
+ pthread_mutex_unlock(&he->hists->lock);
+
+ if (err == -ERANGE && !he->ms.map->erange_warned)
+ ui__warn_map_erange(he->ms.map, sym, ip);
+ else if (err == -ENOMEM) {
+ pr_err("Not enough memory for annotating '%s' symbol!\n",
+ sym->name);
+ sleep(1);
+ }
- if (err == -ERANGE && !he->ms.map->erange_warned)
- ui__warn_map_erange(he->ms.map, sym, ip);
- else if (err == -ENOMEM) {
- pr_err("Not enough memory for annotating '%s' symbol!\n",
- sym->name);
- sleep(1);
+ pthread_mutex_lock(&he->hists->lock);
}
-
- pthread_mutex_lock(&he->hists->lock);
}
static void perf_top__show_details(struct perf_top *top)
@@ -687,14 +685,8 @@ static int hist_iter__top_callback(struct hist_entry_iter *iter,
struct hist_entry *he = iter->he;
struct perf_evsel *evsel = iter->evsel;
- if (sort__has_sym && single) {
- u64 ip = al->addr;
-
- if (al->map)
- ip = al->map->unmap_ip(al->map, ip);
-
- perf_top__record_precise_ip(top, he, evsel->idx, ip);
- }
+ if (sort__has_sym && single)
+ perf_top__record_precise_ip(top, he, evsel->idx, al->addr);
hist__account_cycles(iter->sample->branch_stack, al, iter->sample,
!(top->record_opts.branch_stack & PERF_SAMPLE_BRANCH_ANY));
@@ -964,7 +956,7 @@ static int __cmd_top(struct perf_top *top)
if (ret)
goto out_delete;
- if (perf_session__register_idle_thread(top->session) == NULL)
+ if (perf_session__register_idle_thread(top->session) < 0)
goto out_delete;
machine__synthesize_threads(&top->session->machines.host, &opts->target,
@@ -1218,6 +1210,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_CALLBACK('j', "branch-filter", &opts->branch_stack,
"branch filter mask", "branch stack filter modes",
parse_branch_stack),
+ OPT_BOOLEAN(0, "raw-trace", &symbol_conf.raw_trace,
+ "Show raw trace event output (do not use print fmt or plugins)"),
OPT_END()
};
const char * const top_usage[] = {
@@ -1239,11 +1233,17 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
if (argc)
usage_with_options(top_usage, options);
+ if (!top.evlist->nr_entries &&
+ perf_evlist__add_default(top.evlist) < 0) {
+ pr_err("Not enough memory for event selector list\n");
+ goto out_delete_evlist;
+ }
+
sort__mode = SORT_MODE__TOP;
/* display thread wants entries to be collapsed in a different tree */
sort__need_collapse = 1;
- if (setup_sorting() < 0) {
+ if (setup_sorting(top.evlist) < 0) {
if (sort_order)
parse_options_usage(top_usage, options, "s", 1);
if (field_order)
@@ -1279,12 +1279,9 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
if (target__none(target))
target->system_wide = true;
- if (perf_evlist__create_maps(top.evlist, target) < 0)
- usage_with_options(top_usage, options);
-
- if (!top.evlist->nr_entries &&
- perf_evlist__add_default(top.evlist) < 0) {
- ui__error("Not enough memory for event selector list\n");
+ if (perf_evlist__create_maps(top.evlist, target) < 0) {
+ ui__error("Couldn't create thread/CPU maps: %s\n",
+ errno == ENOENT ? "No such process" : strerror_r(errno, errbuf, sizeof(errbuf)));
goto out_delete_evlist;
}
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index c783d8fd3a80..20916dd77aac 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -22,11 +22,11 @@
#include "util/color.h"
#include "util/debug.h"
#include "util/evlist.h"
-#include "util/exec_cmd.h"
+#include <subcmd/exec-cmd.h>
#include "util/machine.h"
#include "util/session.h"
#include "util/thread.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "util/strlist.h"
#include "util/intlist.h"
#include "util/thread_map.h"
diff --git a/tools/perf/builtin-version.c b/tools/perf/builtin-version.c
new file mode 100644
index 000000000000..9b10cda6b6dc
--- /dev/null
+++ b/tools/perf/builtin-version.c
@@ -0,0 +1,10 @@
+#include "util/util.h"
+#include "builtin.h"
+#include "perf.h"
+
+int cmd_version(int argc __maybe_unused, const char **argv __maybe_unused,
+ const char *prefix __maybe_unused)
+{
+ printf("perf version %s\n", perf_version_string);
+ return 0;
+}
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
index 3688ad29085f..3f871b54e261 100644
--- a/tools/perf/builtin.h
+++ b/tools/perf/builtin.h
@@ -17,6 +17,7 @@ extern int cmd_annotate(int argc, const char **argv, const char *prefix);
extern int cmd_bench(int argc, const char **argv, const char *prefix);
extern int cmd_buildid_cache(int argc, const char **argv, const char *prefix);
extern int cmd_buildid_list(int argc, const char **argv, const char *prefix);
+extern int cmd_config(int argc, const char **argv, const char *prefix);
extern int cmd_diff(int argc, const char **argv, const char *prefix);
extern int cmd_evlist(int argc, const char **argv, const char *prefix);
extern int cmd_help(int argc, const char **argv, const char *prefix);
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt
index 00fcaf8a5b8d..ab5cbaa170d0 100644
--- a/tools/perf/command-list.txt
+++ b/tools/perf/command-list.txt
@@ -9,6 +9,7 @@ perf-buildid-cache mainporcelain common
perf-buildid-list mainporcelain common
perf-data mainporcelain common
perf-diff mainporcelain common
+perf-config mainporcelain common
perf-evlist mainporcelain common
perf-inject mainporcelain common
perf-kmem mainporcelain common
@@ -25,4 +26,4 @@ perf-stat mainporcelain common
perf-test mainporcelain common
perf-timechart mainporcelain common
perf-top mainporcelain common
-perf-trace mainporcelain common
+perf-trace mainporcelain audit
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index de89ec574361..e5959c136a19 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -17,7 +17,7 @@ detected_var = $(shell echo "$(1)=$($(1))" >> $(OUTPUT).config-detected)
CFLAGS := $(EXTRA_CFLAGS) $(EXTRA_WARNINGS)
-include $(src-perf)/config/Makefile.arch
+include $(srctree)/tools/scripts/Makefile.arch
$(call detected_var,ARCH)
@@ -135,8 +135,6 @@ endif
ifeq ($(DEBUG),0)
CFLAGS += -O6
-else
- CFLAGS += $(call cc-option,-Og,-O0)
endif
ifdef PARSER_DEBUG
@@ -318,6 +316,18 @@ ifndef NO_LIBELF
CFLAGS += -DHAVE_LIBBPF_SUPPORT
$(call detected,CONFIG_LIBBPF)
endif
+
+ ifndef NO_DWARF
+ ifdef PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET
+ CFLAGS += -DHAVE_BPF_PROLOGUE
+ $(call detected,CONFIG_BPF_PROLOGUE)
+ else
+ msg := $(warning BPF prologue is not supported by architecture $(ARCH), missing regs_query_register_offset());
+ endif
+ else
+ msg := $(warning DWARF support is off, BPF prologue is disabled);
+ endif
+
endif # NO_LIBBPF
endif # NO_LIBELF
@@ -483,7 +493,7 @@ else
PYTHON_EMBED_LDOPTS := $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null)
PYTHON_EMBED_LDFLAGS := $(call strip-libs,$(PYTHON_EMBED_LDOPTS))
- PYTHON_EMBED_LIBADD := $(call grep-libs,$(PYTHON_EMBED_LDOPTS))
+ PYTHON_EMBED_LIBADD := $(call grep-libs,$(PYTHON_EMBED_LDOPTS)) -lutil
PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null)
FLAGS_PYTHON_EMBED := $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS)
@@ -681,6 +691,8 @@ sharedir = $(prefix)/share
template_dir = share/perf-core/templates
STRACE_GROUPS_DIR = share/perf-core/strace/groups
htmldir = share/doc/perf-doc
+tipdir = share/doc/perf-tip
+srcdir = $(srctree)/tools/perf
ifeq ($(prefix),/usr)
sysconfdir = /etc
ETC_PERFCONFIG = $(sysconfdir)/perfconfig
@@ -707,19 +719,24 @@ infodir_SQ = $(subst ','\'',$(infodir))
perfexecdir_SQ = $(subst ','\'',$(perfexecdir))
template_dir_SQ = $(subst ','\'',$(template_dir))
htmldir_SQ = $(subst ','\'',$(htmldir))
+tipdir_SQ = $(subst ','\'',$(tipdir))
prefix_SQ = $(subst ','\'',$(prefix))
sysconfdir_SQ = $(subst ','\'',$(sysconfdir))
libdir_SQ = $(subst ','\'',$(libdir))
+srcdir_SQ = $(subst ','\'',$(srcdir))
ifneq ($(filter /%,$(firstword $(perfexecdir))),)
perfexec_instdir = $(perfexecdir)
STRACE_GROUPS_INSTDIR = $(STRACE_GROUPS_DIR)
+tip_instdir = $(tipdir)
else
perfexec_instdir = $(prefix)/$(perfexecdir)
STRACE_GROUPS_INSTDIR = $(prefix)/$(STRACE_GROUPS_DIR)
+tip_instdir = $(prefix)/$(tipdir)
endif
perfexec_instdir_SQ = $(subst ','\'',$(perfexec_instdir))
STRACE_GROUPS_INSTDIR_SQ = $(subst ','\'',$(STRACE_GROUPS_INSTDIR))
+tip_instdir_SQ = $(subst ','\'',$(tip_instdir))
# If we install to $(HOME) we keep the traceevent default:
# $(HOME)/.traceevent/plugins
@@ -741,6 +758,10 @@ ifeq ($(VF),1)
$(call print_var,sysconfdir)
$(call print_var,LIBUNWIND_DIR)
$(call print_var,LIBDW_DIR)
+
+ ifeq ($(dwarf-post-unwind),1)
+ $(call feature_print_text,"DWARF post unwind library", $(dwarf-post-unwind-text))
+ endif
$(info )
endif
@@ -756,6 +777,8 @@ $(call detected_var,ETC_PERFCONFIG_SQ)
$(call detected_var,STRACE_GROUPS_DIR_SQ)
$(call detected_var,prefix_SQ)
$(call detected_var,perfexecdir_SQ)
+$(call detected_var,tipdir_SQ)
+$(call detected_var,srcdir_SQ)
$(call detected_var,LIBDIR)
$(call detected_var,GTK_CFLAGS)
$(call detected_var,PERL_EMBED_CCOPTS)
diff --git a/tools/perf/config/utilities.mak b/tools/perf/config/utilities.mak
index 0ebef09c0842..c16ce833079c 100644
--- a/tools/perf/config/utilities.mak
+++ b/tools/perf/config/utilities.mak
@@ -177,22 +177,3 @@ $(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))
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 3d4c7c09adea..a929618b8eb6 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -9,16 +9,18 @@
#include "builtin.h"
#include "util/env.h"
-#include "util/exec_cmd.h"
+#include <subcmd/exec-cmd.h>
#include "util/cache.h"
#include "util/quote.h"
-#include "util/run-command.h"
+#include <subcmd/run-command.h>
#include "util/parse-events.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "util/bpf-loader.h"
#include "util/debug.h"
#include <api/fs/tracing_path.h>
#include <pthread.h>
+#include <stdlib.h>
+#include <time.h>
const char perf_usage_string[] =
"perf [--version] [--help] [OPTIONS] COMMAND [ARGS]";
@@ -39,6 +41,7 @@ struct cmd_struct {
static struct cmd_struct commands[] = {
{ "buildid-cache", cmd_buildid_cache, 0 },
{ "buildid-list", cmd_buildid_list, 0 },
+ { "config", cmd_config, 0 },
{ "diff", cmd_diff, 0 },
{ "evlist", cmd_evlist, 0 },
{ "help", cmd_help, 0 },
@@ -118,7 +121,7 @@ static void commit_pager_choice(void)
{
switch (use_pager) {
case 0:
- setenv("PERF_PAGER", "cat", 1);
+ setenv(PERF_PAGER_ENVIRONMENT, "cat", 1);
break;
case 1:
/* setup_pager(); */
@@ -182,9 +185,9 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
if (!prefixcmp(cmd, CMD_EXEC_PATH)) {
cmd += strlen(CMD_EXEC_PATH);
if (*cmd == '=')
- perf_set_argv_exec_path(cmd + 1);
+ set_argv_exec_path(cmd + 1);
else {
- puts(perf_exec_path());
+ puts(get_argv_exec_path());
exit(0);
}
} else if (!strcmp(cmd, "--html-path")) {
@@ -383,6 +386,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
use_pager = 1;
commit_pager_choice();
+ perf_env__set_cmdline(&perf_env, argc, argv);
status = p->fn(argc, argv, prefix);
exit_browser(status);
perf_env__exit(&perf_env);
@@ -528,14 +532,20 @@ int main(int argc, const char **argv)
const char *cmd;
char sbuf[STRERR_BUFSIZE];
+ /* libsubcmd init */
+ exec_cmd_init("perf", PREFIX, PERF_EXEC_PATH, EXEC_PATH_ENVIRONMENT);
+ pager_init(PERF_PAGER_ENVIRONMENT);
+
/* The page_size is placed in util object. */
page_size = sysconf(_SC_PAGE_SIZE);
cacheline_size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
- cmd = perf_extract_argv0_path(argv[0]);
+ cmd = extract_argv0_path(argv[0]);
if (!cmd)
cmd = "perf-help";
+ srandom(time(NULL));
+
/* get debugfs/tracefs mount point from /proc/mounts */
tracing_path_mount();
diff --git a/tools/perf/scripts/python/stat-cpi.py b/tools/perf/scripts/python/stat-cpi.py
new file mode 100644
index 000000000000..8b60f343dd07
--- /dev/null
+++ b/tools/perf/scripts/python/stat-cpi.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+
+data = {}
+times = []
+threads = []
+cpus = []
+
+def get_key(time, event, cpu, thread):
+ return "%d-%s-%d-%d" % (time, event, cpu, thread)
+
+def store_key(time, cpu, thread):
+ if (time not in times):
+ times.append(time)
+
+ if (cpu not in cpus):
+ cpus.append(cpu)
+
+ if (thread not in threads):
+ threads.append(thread)
+
+def store(time, event, cpu, thread, val, ena, run):
+ #print "event %s cpu %d, thread %d, time %d, val %d, ena %d, run %d" % \
+ # (event, cpu, thread, time, val, ena, run)
+
+ store_key(time, cpu, thread)
+ key = get_key(time, event, cpu, thread)
+ data[key] = [ val, ena, run]
+
+def get(time, event, cpu, thread):
+ key = get_key(time, event, cpu, thread)
+ return data[key][0]
+
+def stat__cycles_k(cpu, thread, time, val, ena, run):
+ store(time, "cycles", cpu, thread, val, ena, run);
+
+def stat__instructions_k(cpu, thread, time, val, ena, run):
+ store(time, "instructions", cpu, thread, val, ena, run);
+
+def stat__cycles_u(cpu, thread, time, val, ena, run):
+ store(time, "cycles", cpu, thread, val, ena, run);
+
+def stat__instructions_u(cpu, thread, time, val, ena, run):
+ store(time, "instructions", cpu, thread, val, ena, run);
+
+def stat__cycles(cpu, thread, time, val, ena, run):
+ store(time, "cycles", cpu, thread, val, ena, run);
+
+def stat__instructions(cpu, thread, time, val, ena, run):
+ store(time, "instructions", cpu, thread, val, ena, run);
+
+def stat__interval(time):
+ for cpu in cpus:
+ for thread in threads:
+ cyc = get(time, "cycles", cpu, thread)
+ ins = get(time, "instructions", cpu, thread)
+ cpi = 0
+
+ if ins != 0:
+ cpi = cyc/float(ins)
+
+ print "%15f: cpu %d, thread %d -> cpi %f (%d/%d)" % (time/(float(1000000000)), cpu, thread, cpi, cyc, ins)
+
+def trace_end():
+ pass
+# XXX trace_end callback could be used as an alternative place
+# to compute same values as in the script above:
+#
+# for time in times:
+# for cpu in cpus:
+# for thread in threads:
+# cyc = get(time, "cycles", cpu, thread)
+# ins = get(time, "instructions", cpu, thread)
+#
+# if ins != 0:
+# cpi = cyc/float(ins)
+#
+# print "time %.9f, cpu %d, thread %d -> cpi %f" % (time/(float(1000000000)), cpu, thread, cpi)
diff --git a/tools/perf/tests/.gitignore b/tools/perf/tests/.gitignore
index 489fc9ffbcb0..bf016c439fbd 100644
--- a/tools/perf/tests/.gitignore
+++ b/tools/perf/tests/.gitignore
@@ -1,2 +1,3 @@
llvm-src-base.c
llvm-src-kbuild.c
+llvm-src-prologue.c
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index f41ebf8849fe..614899b88b37 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -31,24 +31,34 @@ perf-y += sample-parsing.o
perf-y += parse-no-sample-id-all.o
perf-y += kmod-path.o
perf-y += thread-map.o
-perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o
+perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o llvm-src-prologue.o
perf-y += bpf.o
perf-y += topology.o
+perf-y += cpumap.o
+perf-y += stat.o
+perf-y += event_update.o
-$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c
+$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build
$(call rule_mkdir)
$(Q)echo '#include <tests/llvm.h>' > $@
$(Q)echo 'const char test_llvm__bpf_base_prog[] =' >> $@
$(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@
$(Q)echo ';' >> $@
-$(OUTPUT)tests/llvm-src-kbuild.c: tests/bpf-script-test-kbuild.c
+$(OUTPUT)tests/llvm-src-kbuild.c: tests/bpf-script-test-kbuild.c tests/Build
$(call rule_mkdir)
$(Q)echo '#include <tests/llvm.h>' > $@
$(Q)echo 'const char test_llvm__bpf_test_kbuild_prog[] =' >> $@
$(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@
$(Q)echo ';' >> $@
+$(OUTPUT)tests/llvm-src-prologue.c: tests/bpf-script-test-prologue.c tests/Build
+ $(call rule_mkdir)
+ $(Q)echo '#include <tests/llvm.h>' > $@
+ $(Q)echo 'const char test_llvm__bpf_test_prologue_prog[] =' >> $@
+ $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@
+ $(Q)echo ';' >> $@
+
ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64))
perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
endif
diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c
index 638875a0960a..28d1605b0338 100644
--- a/tools/perf/tests/attr.c
+++ b/tools/perf/tests/attr.c
@@ -24,7 +24,7 @@
#include <linux/kernel.h>
#include "../perf.h"
#include "util.h"
-#include "exec_cmd.h"
+#include <subcmd/exec-cmd.h>
#include "tests.h"
#define ENV "PERF_TEST_ATTR"
@@ -153,7 +153,7 @@ static int run_dir(const char *d, const char *perf)
return system(cmd);
}
-int test__attr(void)
+int test__attr(int subtest __maybe_unused)
{
struct stat st;
char path_perf[PATH_MAX];
@@ -164,7 +164,7 @@ int test__attr(void)
return run_dir("./tests", "./perf");
/* Then installed path. */
- snprintf(path_dir, PATH_MAX, "%s/tests", perf_exec_path());
+ snprintf(path_dir, PATH_MAX, "%s/tests", get_argv_exec_path());
snprintf(path_perf, PATH_MAX, "%s/perf", BINDIR);
if (!lstat(path_dir, &st) &&
diff --git a/tools/perf/tests/bp_signal.c b/tools/perf/tests/bp_signal.c
index a02b035fd5aa..fb80c9eb6a95 100644
--- a/tools/perf/tests/bp_signal.c
+++ b/tools/perf/tests/bp_signal.c
@@ -111,7 +111,7 @@ static long long bp_count(int fd)
return count;
}
-int test__bp_signal(void)
+int test__bp_signal(int subtest __maybe_unused)
{
struct sigaction sa;
long long count1, count2;
diff --git a/tools/perf/tests/bp_signal_overflow.c b/tools/perf/tests/bp_signal_overflow.c
index e76537724491..89f92fa67cc4 100644
--- a/tools/perf/tests/bp_signal_overflow.c
+++ b/tools/perf/tests/bp_signal_overflow.c
@@ -58,7 +58,7 @@ static long long bp_count(int fd)
#define EXECUTIONS 10000
#define THRESHOLD 100
-int test__bp_signal_overflow(void)
+int test__bp_signal_overflow(int subtest __maybe_unused)
{
struct perf_event_attr pe;
struct sigaction sa;
diff --git a/tools/perf/tests/bpf-script-test-prologue.c b/tools/perf/tests/bpf-script-test-prologue.c
new file mode 100644
index 000000000000..7230e62c70fc
--- /dev/null
+++ b/tools/perf/tests/bpf-script-test-prologue.c
@@ -0,0 +1,35 @@
+/*
+ * bpf-script-test-prologue.c
+ * Test BPF prologue
+ */
+#ifndef LINUX_VERSION_CODE
+# error Need LINUX_VERSION_CODE
+# error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig'
+#endif
+#define SEC(NAME) __attribute__((section(NAME), used))
+
+#include <uapi/linux/fs.h>
+
+#define FMODE_READ 0x1
+#define FMODE_WRITE 0x2
+
+static void (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) =
+ (void *) 6;
+
+SEC("func=null_lseek file->f_mode offset orig")
+int bpf_func__null_lseek(void *ctx, int err, unsigned long f_mode,
+ unsigned long offset, unsigned long orig)
+{
+ if (err)
+ return 0;
+ if (f_mode & FMODE_WRITE)
+ return 0;
+ if (offset & 1)
+ return 0;
+ if (orig == SEEK_CUR)
+ return 0;
+ return 1;
+}
+
+char _license[] SEC("license") = "GPL";
+int _version SEC("version") = LINUX_VERSION_CODE;
diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
index ec16f7812c8b..33689a0cf821 100644
--- a/tools/perf/tests/bpf.c
+++ b/tools/perf/tests/bpf.c
@@ -19,6 +19,29 @@ static int epoll_pwait_loop(void)
return 0;
}
+#ifdef HAVE_BPF_PROLOGUE
+
+static int llseek_loop(void)
+{
+ int fds[2], i;
+
+ fds[0] = open("/dev/null", O_RDONLY);
+ fds[1] = open("/dev/null", O_RDWR);
+
+ if (fds[0] < 0 || fds[1] < 0)
+ return -1;
+
+ for (i = 0; i < NR_ITERS; i++) {
+ lseek(fds[i % 2], i, (i / 2) % 2 ? SEEK_CUR : SEEK_SET);
+ lseek(fds[(i + 1) % 2], i, (i / 2) % 2 ? SEEK_CUR : SEEK_SET);
+ }
+ close(fds[0]);
+ close(fds[1]);
+ return 0;
+}
+
+#endif
+
static struct {
enum test_llvm__testcase prog_id;
const char *desc;
@@ -37,6 +60,17 @@ static struct {
&epoll_pwait_loop,
(NR_ITERS + 1) / 2,
},
+#ifdef HAVE_BPF_PROLOGUE
+ {
+ LLVM_TESTCASE_BPF_PROLOGUE,
+ "Test BPF prologue generation",
+ "[bpf_prologue_test]",
+ "fix kbuild first",
+ "check your vmlinux setting?",
+ &llseek_loop,
+ (NR_ITERS + 1) / 4,
+ },
+#endif
};
static int do_test(struct bpf_object *obj, int (*func)(void),
@@ -68,8 +102,7 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
err = parse_events_load_bpf_obj(&parse_evlist, &parse_evlist.list, obj);
if (err || list_empty(&parse_evlist.list)) {
pr_debug("Failed to add events selected by BPF\n");
- if (!err)
- return TEST_FAIL;
+ return TEST_FAIL;
}
snprintf(pid, sizeof(pid), "%d", getpid());
@@ -123,8 +156,10 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
}
}
- if (count != expect)
+ if (count != expect) {
pr_debug("BPF filter result incorrect\n");
+ goto out_delete_evlist;
+ }
ret = TEST_OK;
@@ -146,7 +181,7 @@ prepare_bpf(void *obj_buf, size_t obj_buf_sz, const char *name)
return obj;
}
-static int __test__bpf(int index)
+static int __test__bpf(int idx)
{
int ret;
void *obj_buf;
@@ -154,54 +189,72 @@ static int __test__bpf(int index)
struct bpf_object *obj;
ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz,
- bpf_testcase_table[index].prog_id,
+ bpf_testcase_table[idx].prog_id,
true);
if (ret != TEST_OK || !obj_buf || !obj_buf_sz) {
pr_debug("Unable to get BPF object, %s\n",
- bpf_testcase_table[index].msg_compile_fail);
- if (index == 0)
+ bpf_testcase_table[idx].msg_compile_fail);
+ if (idx == 0)
return TEST_SKIP;
else
return TEST_FAIL;
}
obj = prepare_bpf(obj_buf, obj_buf_sz,
- bpf_testcase_table[index].name);
+ bpf_testcase_table[idx].name);
if (!obj) {
ret = TEST_FAIL;
goto out;
}
ret = do_test(obj,
- bpf_testcase_table[index].target_func,
- bpf_testcase_table[index].expect_result);
+ bpf_testcase_table[idx].target_func,
+ bpf_testcase_table[idx].expect_result);
out:
bpf__clear();
return ret;
}
-int test__bpf(void)
+int test__bpf_subtest_get_nr(void)
+{
+ return (int)ARRAY_SIZE(bpf_testcase_table);
+}
+
+const char *test__bpf_subtest_get_desc(int i)
+{
+ if (i < 0 || i >= (int)ARRAY_SIZE(bpf_testcase_table))
+ return NULL;
+ return bpf_testcase_table[i].desc;
+}
+
+int test__bpf(int i)
{
- unsigned int i;
int err;
+ if (i < 0 || i >= (int)ARRAY_SIZE(bpf_testcase_table))
+ return TEST_FAIL;
+
if (geteuid() != 0) {
pr_debug("Only root can run BPF test\n");
return TEST_SKIP;
}
- for (i = 0; i < ARRAY_SIZE(bpf_testcase_table); i++) {
- err = __test__bpf(i);
+ err = __test__bpf(i);
+ return err;
+}
- if (err != TEST_OK)
- return err;
- }
+#else
+int test__bpf_subtest_get_nr(void)
+{
+ return 0;
+}
- return TEST_OK;
+const char *test__bpf_subtest_get_desc(int i __maybe_unused)
+{
+ return NULL;
}
-#else
-int test__bpf(void)
+int test__bpf(int i __maybe_unused)
{
pr_debug("Skip BPF test because BPF support is not compiled\n");
return TEST_SKIP;
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 80c442eab767..f2b1dcac45d3 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -11,7 +11,7 @@
#include "tests.h"
#include "debug.h"
#include "color.h"
-#include "parse-options.h"
+#include <subcmd/parse-options.h>
#include "symbol.h"
struct test __weak arch_tests[] = {
@@ -160,6 +160,11 @@ static struct test generic_tests[] = {
{
.desc = "Test LLVM searching and compiling",
.func = test__llvm,
+ .subtest = {
+ .skip_if_fail = true,
+ .get_nr = test__llvm_subtest_get_nr,
+ .get_desc = test__llvm_subtest_get_desc,
+ },
},
{
.desc = "Test topology in session",
@@ -168,6 +173,35 @@ static struct test generic_tests[] = {
{
.desc = "Test BPF filter",
.func = test__bpf,
+ .subtest = {
+ .skip_if_fail = true,
+ .get_nr = test__bpf_subtest_get_nr,
+ .get_desc = test__bpf_subtest_get_desc,
+ },
+ },
+ {
+ .desc = "Test thread map synthesize",
+ .func = test__thread_map_synthesize,
+ },
+ {
+ .desc = "Test cpu map synthesize",
+ .func = test__cpu_map_synthesize,
+ },
+ {
+ .desc = "Test stat config synthesize",
+ .func = test__synthesize_stat_config,
+ },
+ {
+ .desc = "Test stat synthesize",
+ .func = test__synthesize_stat,
+ },
+ {
+ .desc = "Test stat round synthesize",
+ .func = test__synthesize_stat_round,
+ },
+ {
+ .desc = "Test attr update synthesize",
+ .func = test__event_update,
},
{
.func = NULL,
@@ -203,7 +237,7 @@ static bool perf_test__matches(struct test *test, int curr, int argc, const char
return false;
}
-static int run_test(struct test *test)
+static int run_test(struct test *test, int subtest)
{
int status, err = -1, child = fork();
char sbuf[STRERR_BUFSIZE];
@@ -216,7 +250,22 @@ static int run_test(struct test *test)
if (!child) {
pr_debug("test child forked, pid %d\n", getpid());
- err = test->func();
+ if (!verbose) {
+ int nullfd = open("/dev/null", O_WRONLY);
+ if (nullfd >= 0) {
+ close(STDERR_FILENO);
+ close(STDOUT_FILENO);
+
+ dup2(nullfd, STDOUT_FILENO);
+ dup2(STDOUT_FILENO, STDERR_FILENO);
+ close(nullfd);
+ }
+ } else {
+ signal(SIGSEGV, sighandler_dump_stack);
+ signal(SIGFPE, sighandler_dump_stack);
+ }
+
+ err = test->func(subtest);
exit(err);
}
@@ -237,6 +286,40 @@ static int run_test(struct test *test)
for (j = 0; j < ARRAY_SIZE(tests); j++) \
for (t = &tests[j][0]; t->func; t++)
+static int test_and_print(struct test *t, bool force_skip, int subtest)
+{
+ int err;
+
+ if (!force_skip) {
+ pr_debug("\n--- start ---\n");
+ err = run_test(t, subtest);
+ pr_debug("---- end ----\n");
+ } else {
+ pr_debug("\n--- force skipped ---\n");
+ err = TEST_SKIP;
+ }
+
+ if (!t->subtest.get_nr)
+ pr_debug("%s:", t->desc);
+ else
+ pr_debug("%s subtest %d:", t->desc, subtest);
+
+ switch (err) {
+ case TEST_OK:
+ pr_info(" Ok\n");
+ break;
+ case TEST_SKIP:
+ color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip\n");
+ break;
+ case TEST_FAIL:
+ default:
+ color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n");
+ break;
+ }
+
+ return err;
+}
+
static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
{
struct test *t;
@@ -264,21 +347,43 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
continue;
}
- pr_debug("\n--- start ---\n");
- err = run_test(t);
- pr_debug("---- end ----\n%s:", t->desc);
-
- switch (err) {
- case TEST_OK:
- pr_info(" Ok\n");
- break;
- case TEST_SKIP:
- color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip\n");
- break;
- case TEST_FAIL:
- default:
- color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n");
- break;
+ if (!t->subtest.get_nr) {
+ test_and_print(t, false, -1);
+ } else {
+ int subn = t->subtest.get_nr();
+ /*
+ * minus 2 to align with normal testcases.
+ * For subtest we print additional '.x' in number.
+ * for example:
+ *
+ * 35: Test LLVM searching and compiling :
+ * 35.1: Basic BPF llvm compiling test : Ok
+ */
+ int subw = width > 2 ? width - 2 : width;
+ bool skip = false;
+ int subi;
+
+ if (subn <= 0) {
+ color_fprintf(stderr, PERF_COLOR_YELLOW,
+ " Skip (not compiled in)\n");
+ continue;
+ }
+ pr_info("\n");
+
+ for (subi = 0; subi < subn; subi++) {
+ int len = strlen(t->subtest.get_desc(subi));
+
+ if (subw < len)
+ subw = len;
+ }
+
+ for (subi = 0; subi < subn; subi++) {
+ pr_info("%2d.%1d: %-*s:", i, subi + 1, subw,
+ t->subtest.get_desc(subi));
+ err = test_and_print(t, skip, subi);
+ if (err != TEST_OK && t->subtest.skip_if_fail)
+ skip = true;
+ }
}
}
diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c
index a767a6400c5c..313a48c6b2bc 100644
--- a/tools/perf/tests/code-reading.c
+++ b/tools/perf/tests/code-reading.c
@@ -433,7 +433,6 @@ enum {
static int do_test_code_reading(bool try_kcore)
{
- struct machines machines;
struct machine *machine;
struct thread *thread;
struct record_opts opts = {
@@ -459,8 +458,7 @@ static int do_test_code_reading(bool try_kcore)
pid = getpid();
- machines__init(&machines);
- machine = &machines.host;
+ machine = machine__new_host();
ret = machine__create_kernel_maps(machine);
if (ret < 0) {
@@ -549,6 +547,13 @@ static int do_test_code_reading(bool try_kcore)
if (ret < 0) {
if (!excl_kernel) {
excl_kernel = true;
+ /*
+ * Both cpus and threads are now owned by evlist
+ * and will be freed by following perf_evlist__set_maps
+ * call. Getting refference to keep them alive.
+ */
+ cpu_map__get(cpus);
+ thread_map__get(threads);
perf_evlist__set_maps(evlist, NULL, NULL);
perf_evlist__delete(evlist);
evlist = NULL;
@@ -594,14 +599,13 @@ out_err:
cpu_map__put(cpus);
thread_map__put(threads);
}
- machines__destroy_kernel_maps(&machines);
machine__delete_threads(machine);
- machines__exit(&machines);
+ machine__delete(machine);
return err;
}
-int test__code_reading(void)
+int test__code_reading(int subtest __maybe_unused)
{
int ret;
diff --git a/tools/perf/tests/cpumap.c b/tools/perf/tests/cpumap.c
new file mode 100644
index 000000000000..4cb6418a8ffc
--- /dev/null
+++ b/tools/perf/tests/cpumap.c
@@ -0,0 +1,88 @@
+#include "tests.h"
+#include "cpumap.h"
+
+static int process_event_mask(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine __maybe_unused)
+{
+ struct cpu_map_event *map_event = &event->cpu_map;
+ struct cpu_map_mask *mask;
+ struct cpu_map_data *data;
+ struct cpu_map *map;
+ int i;
+
+ data = &map_event->data;
+
+ TEST_ASSERT_VAL("wrong type", data->type == PERF_CPU_MAP__MASK);
+
+ mask = (struct cpu_map_mask *)data->data;
+
+ TEST_ASSERT_VAL("wrong nr", mask->nr == 1);
+
+ for (i = 0; i < 20; i++) {
+ TEST_ASSERT_VAL("wrong cpu", test_bit(i, mask->mask));
+ }
+
+ map = cpu_map__new_data(data);
+ TEST_ASSERT_VAL("wrong nr", map->nr == 20);
+
+ for (i = 0; i < 20; i++) {
+ TEST_ASSERT_VAL("wrong cpu", map->map[i] == i);
+ }
+
+ cpu_map__put(map);
+ return 0;
+}
+
+static int process_event_cpus(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine __maybe_unused)
+{
+ struct cpu_map_event *map_event = &event->cpu_map;
+ struct cpu_map_entries *cpus;
+ struct cpu_map_data *data;
+ struct cpu_map *map;
+
+ data = &map_event->data;
+
+ TEST_ASSERT_VAL("wrong type", data->type == PERF_CPU_MAP__CPUS);
+
+ cpus = (struct cpu_map_entries *)data->data;
+
+ TEST_ASSERT_VAL("wrong nr", cpus->nr == 2);
+ TEST_ASSERT_VAL("wrong cpu", cpus->cpu[0] == 1);
+ TEST_ASSERT_VAL("wrong cpu", cpus->cpu[1] == 256);
+
+ map = cpu_map__new_data(data);
+ TEST_ASSERT_VAL("wrong nr", map->nr == 2);
+ TEST_ASSERT_VAL("wrong cpu", map->map[0] == 1);
+ TEST_ASSERT_VAL("wrong cpu", map->map[1] == 256);
+ TEST_ASSERT_VAL("wrong refcnt", atomic_read(&map->refcnt) == 1);
+ cpu_map__put(map);
+ return 0;
+}
+
+
+int test__cpu_map_synthesize(int subtest __maybe_unused)
+{
+ struct cpu_map *cpus;
+
+ /* This one is better stores in mask. */
+ cpus = cpu_map__new("0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19");
+
+ TEST_ASSERT_VAL("failed to synthesize map",
+ !perf_event__synthesize_cpu_map(NULL, cpus, process_event_mask, NULL));
+
+ cpu_map__put(cpus);
+
+ /* This one is better stores in cpu values. */
+ cpus = cpu_map__new("1,256");
+
+ TEST_ASSERT_VAL("failed to synthesize map",
+ !perf_event__synthesize_cpu_map(NULL, cpus, process_event_cpus, NULL));
+
+ cpu_map__put(cpus);
+ return 0;
+}
diff --git a/tools/perf/tests/dso-data.c b/tools/perf/tests/dso-data.c
index a218aeaf56a0..dc673ff7c437 100644
--- a/tools/perf/tests/dso-data.c
+++ b/tools/perf/tests/dso-data.c
@@ -110,7 +110,7 @@ static int dso__data_fd(struct dso *dso, struct machine *machine)
return fd;
}
-int test__dso_data(void)
+int test__dso_data(int subtest __maybe_unused)
{
struct machine machine;
struct dso *dso;
@@ -245,7 +245,7 @@ static int set_fd_limit(int n)
return setrlimit(RLIMIT_NOFILE, &rlim);
}
-int test__dso_data_cache(void)
+int test__dso_data_cache(int subtest __maybe_unused)
{
struct machine machine;
long nr_end, nr = open_files_cnt();
@@ -302,7 +302,7 @@ int test__dso_data_cache(void)
return 0;
}
-int test__dso_data_reopen(void)
+int test__dso_data_reopen(int subtest __maybe_unused)
{
struct machine machine;
long nr_end, nr = open_files_cnt();
diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c
index 07221793a3ac..1c5c0221cea2 100644
--- a/tools/perf/tests/dwarf-unwind.c
+++ b/tools/perf/tests/dwarf-unwind.c
@@ -51,6 +51,12 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)
"krava_1",
"test__dwarf_unwind"
};
+ /*
+ * The funcs[MAX_STACK] array index, based on the
+ * callchain order setup.
+ */
+ int idx = callchain_param.order == ORDER_CALLER ?
+ MAX_STACK - *cnt - 1 : *cnt;
if (*cnt >= MAX_STACK) {
pr_debug("failed: crossed the max stack value %d\n", MAX_STACK);
@@ -63,8 +69,10 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)
return -1;
}
- pr_debug("got: %s 0x%" PRIx64 "\n", symbol, entry->ip);
- return strcmp((const char *) symbol, funcs[(*cnt)++]);
+ (*cnt)++;
+ pr_debug("got: %s 0x%" PRIx64 ", expecting %s\n",
+ symbol, entry->ip, funcs[idx]);
+ return strcmp((const char *) symbol, funcs[idx]);
}
__attribute__ ((noinline))
@@ -105,8 +113,16 @@ static int compare(void *p1, void *p2)
/* Any possible value should be 'thread' */
struct thread *thread = *(struct thread **)p1;
- if (global_unwind_retval == -INT_MAX)
+ if (global_unwind_retval == -INT_MAX) {
+ /* Call unwinder twice for both callchain orders. */
+ callchain_param.order = ORDER_CALLER;
+
global_unwind_retval = unwind_thread(thread);
+ if (!global_unwind_retval) {
+ callchain_param.order = ORDER_CALLEE;
+ global_unwind_retval = unwind_thread(thread);
+ }
+ }
return p1 - p2;
}
@@ -142,21 +158,23 @@ static int krava_1(struct thread *thread)
return krava_2(thread);
}
-int test__dwarf_unwind(void)
+int test__dwarf_unwind(int subtest __maybe_unused)
{
- struct machines machines;
struct machine *machine;
struct thread *thread;
int err = -1;
- machines__init(&machines);
-
- machine = machines__find(&machines, HOST_KERNEL_ID);
+ machine = machine__new_host();
if (!machine) {
pr_err("Could not get machine\n");
return -1;
}
+ if (machine__create_kernel_maps(machine)) {
+ pr_err("Failed to create kernel maps\n");
+ return -1;
+ }
+
callchain_param.record_mode = CALLCHAIN_DWARF;
if (init_live_machine(machine)) {
@@ -178,7 +196,6 @@ int test__dwarf_unwind(void)
out:
machine__delete_threads(machine);
- machine__exit(machine);
- machines__exit(&machines);
+ machine__delete(machine);
return err;
}
diff --git a/tools/perf/tests/event_update.c b/tools/perf/tests/event_update.c
new file mode 100644
index 000000000000..012eab5d1df1
--- /dev/null
+++ b/tools/perf/tests/event_update.c
@@ -0,0 +1,117 @@
+#include <linux/compiler.h>
+#include "evlist.h"
+#include "evsel.h"
+#include "machine.h"
+#include "tests.h"
+#include "debug.h"
+
+static int process_event_unit(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine __maybe_unused)
+{
+ struct event_update_event *ev = (struct event_update_event *) event;
+
+ TEST_ASSERT_VAL("wrong id", ev->id == 123);
+ TEST_ASSERT_VAL("wrong id", ev->type == PERF_EVENT_UPDATE__UNIT);
+ TEST_ASSERT_VAL("wrong unit", !strcmp(ev->data, "KRAVA"));
+ return 0;
+}
+
+static int process_event_scale(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine __maybe_unused)
+{
+ struct event_update_event *ev = (struct event_update_event *) event;
+ struct event_update_event_scale *ev_data;
+
+ ev_data = (struct event_update_event_scale *) ev->data;
+
+ TEST_ASSERT_VAL("wrong id", ev->id == 123);
+ TEST_ASSERT_VAL("wrong id", ev->type == PERF_EVENT_UPDATE__SCALE);
+ TEST_ASSERT_VAL("wrong scale", ev_data->scale = 0.123);
+ return 0;
+}
+
+struct event_name {
+ struct perf_tool tool;
+ const char *name;
+};
+
+static int process_event_name(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine __maybe_unused)
+{
+ struct event_name *tmp = container_of(tool, struct event_name, tool);
+ struct event_update_event *ev = (struct event_update_event*) event;
+
+ TEST_ASSERT_VAL("wrong id", ev->id == 123);
+ TEST_ASSERT_VAL("wrong id", ev->type == PERF_EVENT_UPDATE__NAME);
+ TEST_ASSERT_VAL("wrong name", !strcmp(ev->data, tmp->name));
+ return 0;
+}
+
+static int process_event_cpus(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine __maybe_unused)
+{
+ struct event_update_event *ev = (struct event_update_event*) event;
+ struct event_update_event_cpus *ev_data;
+ struct cpu_map *map;
+
+ ev_data = (struct event_update_event_cpus*) ev->data;
+
+ map = cpu_map__new_data(&ev_data->cpus);
+
+ TEST_ASSERT_VAL("wrong id", ev->id == 123);
+ TEST_ASSERT_VAL("wrong type", ev->type == PERF_EVENT_UPDATE__CPUS);
+ TEST_ASSERT_VAL("wrong cpus", map->nr == 3);
+ TEST_ASSERT_VAL("wrong cpus", map->map[0] == 1);
+ TEST_ASSERT_VAL("wrong cpus", map->map[1] == 2);
+ TEST_ASSERT_VAL("wrong cpus", map->map[2] == 3);
+ cpu_map__put(map);
+ return 0;
+}
+
+int test__event_update(int subtest __maybe_unused)
+{
+ struct perf_evlist *evlist;
+ struct perf_evsel *evsel;
+ struct event_name tmp;
+
+ evlist = perf_evlist__new_default();
+ TEST_ASSERT_VAL("failed to get evlist", evlist);
+
+ evsel = perf_evlist__first(evlist);
+
+ TEST_ASSERT_VAL("failed to allos ids",
+ !perf_evsel__alloc_id(evsel, 1, 1));
+
+ perf_evlist__id_add(evlist, evsel, 0, 0, 123);
+
+ evsel->unit = strdup("KRAVA");
+
+ TEST_ASSERT_VAL("failed to synthesize attr update unit",
+ !perf_event__synthesize_event_update_unit(NULL, evsel, process_event_unit));
+
+ evsel->scale = 0.123;
+
+ TEST_ASSERT_VAL("failed to synthesize attr update scale",
+ !perf_event__synthesize_event_update_scale(NULL, evsel, process_event_scale));
+
+ tmp.name = perf_evsel__name(evsel);
+
+ TEST_ASSERT_VAL("failed to synthesize attr update name",
+ !perf_event__synthesize_event_update_name(&tmp.tool, evsel, process_event_name));
+
+ evsel->own_cpus = cpu_map__new("1,2,3");
+
+ TEST_ASSERT_VAL("failed to synthesize attr update cpus",
+ !perf_event__synthesize_event_update_cpus(&tmp.tool, evsel, process_event_cpus));
+
+ cpu_map__put(evsel->own_cpus);
+ return 0;
+}
diff --git a/tools/perf/tests/evsel-roundtrip-name.c b/tools/perf/tests/evsel-roundtrip-name.c
index 3fa715987a5e..2de4a4f2c3ed 100644
--- a/tools/perf/tests/evsel-roundtrip-name.c
+++ b/tools/perf/tests/evsel-roundtrip-name.c
@@ -95,7 +95,7 @@ out_delete_evlist:
#define perf_evsel__name_array_test(names) \
__perf_evsel__name_array_test(names, ARRAY_SIZE(names))
-int test__perf_evsel__roundtrip_name_test(void)
+int test__perf_evsel__roundtrip_name_test(int subtest __maybe_unused)
{
int err = 0, ret = 0;
@@ -103,7 +103,8 @@ int test__perf_evsel__roundtrip_name_test(void)
if (err)
ret = err;
- err = perf_evsel__name_array_test(perf_evsel__sw_names);
+ err = __perf_evsel__name_array_test(perf_evsel__sw_names,
+ PERF_COUNT_SW_DUMMY + 1);
if (err)
ret = err;
diff --git a/tools/perf/tests/evsel-tp-sched.c b/tools/perf/tests/evsel-tp-sched.c
index 790e413d9a1f..1984b3bbfe15 100644
--- a/tools/perf/tests/evsel-tp-sched.c
+++ b/tools/perf/tests/evsel-tp-sched.c
@@ -32,7 +32,7 @@ static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name,
return ret;
}
-int test__perf_evsel__tp_sched_test(void)
+int test__perf_evsel__tp_sched_test(int subtest __maybe_unused)
{
struct perf_evsel *evsel = perf_evsel__newtp("sched", "sched_switch");
int ret = 0;
diff --git a/tools/perf/tests/fdarray.c b/tools/perf/tests/fdarray.c
index d24b837951d4..c809463edbe5 100644
--- a/tools/perf/tests/fdarray.c
+++ b/tools/perf/tests/fdarray.c
@@ -25,7 +25,7 @@ static int fdarray__fprintf_prefix(struct fdarray *fda, const char *prefix, FILE
return printed + fdarray__fprintf(fda, fp);
}
-int test__fdarray__filter(void)
+int test__fdarray__filter(int subtest __maybe_unused)
{
int nr_fds, expected_fd[2], fd, err = TEST_FAIL;
struct fdarray *fda = fdarray__new(5, 5);
@@ -103,7 +103,7 @@ out:
return err;
}
-int test__fdarray__add(void)
+int test__fdarray__add(int subtest __maybe_unused)
{
int err = TEST_FAIL;
struct fdarray *fda = fdarray__new(2, 2);
diff --git a/tools/perf/tests/hists_common.c b/tools/perf/tests/hists_common.c
index ce80b274b097..071a8b5f5232 100644
--- a/tools/perf/tests/hists_common.c
+++ b/tools/perf/tests/hists_common.c
@@ -150,7 +150,6 @@ struct machine *setup_fake_machine(struct machines *machines)
out:
pr_debug("Not enough memory for machine setup\n");
machine__delete_threads(machine);
- machine__delete(machine);
return NULL;
}
diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c
index 7ed737019de7..5e6a86e50fb9 100644
--- a/tools/perf/tests/hists_cumulate.c
+++ b/tools/perf/tests/hists_cumulate.c
@@ -281,7 +281,7 @@ static int test1(struct perf_evsel *evsel, struct machine *machine)
symbol_conf.cumulate_callchain = false;
perf_evsel__reset_sample_bit(evsel, CALLCHAIN);
- setup_sorting();
+ setup_sorting(NULL);
callchain_register_param(&callchain_param);
err = add_hist_entries(hists, machine);
@@ -428,7 +428,7 @@ static int test2(struct perf_evsel *evsel, struct machine *machine)
symbol_conf.cumulate_callchain = false;
perf_evsel__set_sample_bit(evsel, CALLCHAIN);
- setup_sorting();
+ setup_sorting(NULL);
callchain_register_param(&callchain_param);
err = add_hist_entries(hists, machine);
@@ -486,7 +486,7 @@ static int test3(struct perf_evsel *evsel, struct machine *machine)
symbol_conf.cumulate_callchain = true;
perf_evsel__reset_sample_bit(evsel, CALLCHAIN);
- setup_sorting();
+ setup_sorting(NULL);
callchain_register_param(&callchain_param);
err = add_hist_entries(hists, machine);
@@ -670,7 +670,7 @@ static int test4(struct perf_evsel *evsel, struct machine *machine)
symbol_conf.cumulate_callchain = true;
perf_evsel__set_sample_bit(evsel, CALLCHAIN);
- setup_sorting();
+ setup_sorting(NULL);
callchain_register_param(&callchain_param);
err = add_hist_entries(hists, machine);
@@ -686,7 +686,7 @@ out:
return err;
}
-int test__hists_cumulate(void)
+int test__hists_cumulate(int subtest __maybe_unused)
{
int err = TEST_FAIL;
struct machines machines;
@@ -706,6 +706,7 @@ int test__hists_cumulate(void)
err = parse_events(evlist, "cpu-clock", NULL);
if (err)
goto out;
+ err = TEST_FAIL;
machines__init(&machines);
diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c
index 818acf875dd0..351a42463444 100644
--- a/tools/perf/tests/hists_filter.c
+++ b/tools/perf/tests/hists_filter.c
@@ -104,7 +104,7 @@ out:
return TEST_FAIL;
}
-int test__hists_filter(void)
+int test__hists_filter(int subtest __maybe_unused)
{
int err = TEST_FAIL;
struct machines machines;
@@ -120,9 +120,10 @@ int test__hists_filter(void)
err = parse_events(evlist, "task-clock", NULL);
if (err)
goto out;
+ err = TEST_FAIL;
/* default sort order (comm,dso,sym) will be used */
- if (setup_sorting() < 0)
+ if (setup_sorting(NULL) < 0)
goto out;
machines__init(&machines);
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c
index 8c102b011424..64b257d8d557 100644
--- a/tools/perf/tests/hists_link.c
+++ b/tools/perf/tests/hists_link.c
@@ -64,7 +64,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
struct perf_evsel *evsel;
struct addr_location al;
struct hist_entry *he;
- struct perf_sample sample = { .period = 1, };
+ struct perf_sample sample = { .period = 1, .weight = 1, };
size_t i = 0, k;
/*
@@ -90,7 +90,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
goto out;
he = __hists__add_entry(hists, &al, NULL,
- NULL, NULL, 1, 1, 0, true);
+ NULL, NULL, &sample, true);
if (he == NULL) {
addr_location__put(&al);
goto out;
@@ -116,7 +116,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
goto out;
he = __hists__add_entry(hists, &al, NULL,
- NULL, NULL, 1, 1, 0, true);
+ NULL, NULL, &sample, true);
if (he == NULL) {
addr_location__put(&al);
goto out;
@@ -274,7 +274,7 @@ static int validate_link(struct hists *leader, struct hists *other)
return __validate_link(leader, 0) || __validate_link(other, 1);
}
-int test__hists_link(void)
+int test__hists_link(int subtest __maybe_unused)
{
int err = -1;
struct hists *hists, *first_hists;
@@ -293,8 +293,9 @@ int test__hists_link(void)
if (err)
goto out;
+ err = TEST_FAIL;
/* default sort order (comm,dso,sym) will be used */
- if (setup_sorting() < 0)
+ if (setup_sorting(NULL) < 0)
goto out;
machines__init(&machines);
diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c
index adbebc852cc8..b231265148d8 100644
--- a/tools/perf/tests/hists_output.c
+++ b/tools/perf/tests/hists_output.c
@@ -134,7 +134,7 @@ static int test1(struct perf_evsel *evsel, struct machine *machine)
field_order = NULL;
sort_order = NULL; /* equivalent to sort_order = "comm,dso,sym" */
- setup_sorting();
+ setup_sorting(NULL);
/*
* expected output:
@@ -236,7 +236,7 @@ static int test2(struct perf_evsel *evsel, struct machine *machine)
field_order = "overhead,cpu";
sort_order = "pid";
- setup_sorting();
+ setup_sorting(NULL);
/*
* expected output:
@@ -292,7 +292,7 @@ static int test3(struct perf_evsel *evsel, struct machine *machine)
field_order = "comm,overhead,dso";
sort_order = NULL;
- setup_sorting();
+ setup_sorting(NULL);
/*
* expected output:
@@ -366,7 +366,7 @@ static int test4(struct perf_evsel *evsel, struct machine *machine)
field_order = "dso,sym,comm,overhead,dso";
sort_order = "sym";
- setup_sorting();
+ setup_sorting(NULL);
/*
* expected output:
@@ -468,7 +468,7 @@ static int test5(struct perf_evsel *evsel, struct machine *machine)
field_order = "cpu,pid,comm,dso,sym";
sort_order = "dso,pid";
- setup_sorting();
+ setup_sorting(NULL);
/*
* expected output:
@@ -576,7 +576,7 @@ out:
return err;
}
-int test__hists_output(void)
+int test__hists_output(int subtest __maybe_unused)
{
int err = TEST_FAIL;
struct machines machines;
@@ -597,6 +597,7 @@ int test__hists_output(void)
err = parse_events(evlist, "cpu-clock", NULL);
if (err)
goto out;
+ err = TEST_FAIL;
machines__init(&machines);
diff --git a/tools/perf/tests/keep-tracking.c b/tools/perf/tests/keep-tracking.c
index a2e2269aa093..ddb78fae064a 100644
--- a/tools/perf/tests/keep-tracking.c
+++ b/tools/perf/tests/keep-tracking.c
@@ -49,13 +49,12 @@ static int find_comm(struct perf_evlist *evlist, const char *comm)
* when an event is disabled but a dummy software event is not disabled. If the
* test passes %0 is returned, otherwise %-1 is returned.
*/
-int test__keep_tracking(void)
+int test__keep_tracking(int subtest __maybe_unused)
{
struct record_opts opts = {
.mmap_pages = UINT_MAX,
.user_freq = UINT_MAX,
.user_interval = ULLONG_MAX,
- .freq = 4000,
.target = {
.uses_mmap = true,
},
@@ -124,7 +123,7 @@ int test__keep_tracking(void)
evsel = perf_evlist__last(evlist);
- CHECK__(perf_evlist__disable_event(evlist, evsel));
+ CHECK__(perf_evsel__disable(evsel));
comm = "Test COMM 2";
CHECK__(prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0));
diff --git a/tools/perf/tests/kmod-path.c b/tools/perf/tests/kmod-path.c
index 08c433b4bf4f..d2af78193153 100644
--- a/tools/perf/tests/kmod-path.c
+++ b/tools/perf/tests/kmod-path.c
@@ -49,7 +49,7 @@ static int test_is_kernel_module(const char *path, int cpumode, bool expect)
#define M(path, c, e) \
TEST_ASSERT_VAL("failed", !test_is_kernel_module(path, c, e))
-int test__kmod_path__parse(void)
+int test__kmod_path__parse(int subtest __maybe_unused)
{
/* path alloc_name alloc_ext kmod comp name ext */
T("/xxxx/xxxx/x-x.ko", true , true , true, false, "[x_x]", NULL);
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
index bc4cf507cde5..06f45c1d4256 100644
--- a/tools/perf/tests/llvm.c
+++ b/tools/perf/tests/llvm.c
@@ -44,13 +44,17 @@ static struct {
.source = test_llvm__bpf_test_kbuild_prog,
.desc = "Test kbuild searching",
},
+ [LLVM_TESTCASE_BPF_PROLOGUE] = {
+ .source = test_llvm__bpf_test_prologue_prog,
+ .desc = "Compile source for BPF prologue generation test",
+ },
};
int
test_llvm__fetch_bpf_obj(void **p_obj_buf,
size_t *p_obj_buf_sz,
- enum test_llvm__testcase index,
+ enum test_llvm__testcase idx,
bool force)
{
const char *source;
@@ -59,11 +63,11 @@ test_llvm__fetch_bpf_obj(void **p_obj_buf,
char *tmpl_new = NULL, *clang_opt_new = NULL;
int err, old_verbose, ret = TEST_FAIL;
- if (index >= __LLVM_TESTCASE_MAX)
+ if (idx >= __LLVM_TESTCASE_MAX)
return TEST_FAIL;
- source = bpf_source_table[index].source;
- desc = bpf_source_table[index].desc;
+ source = bpf_source_table[idx].source;
+ desc = bpf_source_table[idx].desc;
perf_config(perf_config_cb, NULL);
@@ -127,44 +131,39 @@ out:
return ret;
}
-int test__llvm(void)
+int test__llvm(int subtest)
{
- enum test_llvm__testcase i;
+ int ret;
+ void *obj_buf = NULL;
+ size_t obj_buf_sz = 0;
- for (i = 0; i < __LLVM_TESTCASE_MAX; i++) {
- int ret;
- void *obj_buf = NULL;
- size_t obj_buf_sz = 0;
+ if ((subtest < 0) || (subtest >= __LLVM_TESTCASE_MAX))
+ return TEST_FAIL;
- ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz,
- i, false);
+ ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz,
+ subtest, false);
- if (ret == TEST_OK) {
- ret = test__bpf_parsing(obj_buf, obj_buf_sz);
- if (ret != TEST_OK)
- pr_debug("Failed to parse test case '%s'\n",
- bpf_source_table[i].desc);
- }
- free(obj_buf);
-
- switch (ret) {
- case TEST_SKIP:
- return TEST_SKIP;
- case TEST_OK:
- break;
- default:
- /*
- * Test 0 is the basic LLVM test. If test 0
- * fail, the basic LLVM support not functional
- * so the whole test should fail. If other test
- * case fail, it can be fixed by adjusting
- * config so don't report error.
- */
- if (i == 0)
- return TEST_FAIL;
- else
- return TEST_SKIP;
+ if (ret == TEST_OK) {
+ ret = test__bpf_parsing(obj_buf, obj_buf_sz);
+ if (ret != TEST_OK) {
+ pr_debug("Failed to parse test case '%s'\n",
+ bpf_source_table[subtest].desc);
}
}
- return TEST_OK;
+ free(obj_buf);
+
+ return ret;
+}
+
+int test__llvm_subtest_get_nr(void)
+{
+ return __LLVM_TESTCASE_MAX;
+}
+
+const char *test__llvm_subtest_get_desc(int subtest)
+{
+ if ((subtest < 0) || (subtest >= __LLVM_TESTCASE_MAX))
+ return NULL;
+
+ return bpf_source_table[subtest].desc;
}
diff --git a/tools/perf/tests/llvm.h b/tools/perf/tests/llvm.h
index d91d8f44efee..5150b4d6ef50 100644
--- a/tools/perf/tests/llvm.h
+++ b/tools/perf/tests/llvm.h
@@ -6,10 +6,12 @@
extern const char test_llvm__bpf_base_prog[];
extern const char test_llvm__bpf_test_kbuild_prog[];
+extern const char test_llvm__bpf_test_prologue_prog[];
enum test_llvm__testcase {
LLVM_TESTCASE_BASE,
LLVM_TESTCASE_KBUILD,
+ LLVM_TESTCASE_BPF_PROLOGUE,
__LLVM_TESTCASE_MAX,
};
diff --git a/tools/perf/tests/make b/tools/perf/tests/make
index 8ea3dffc5065..df38decc48c3 100644
--- a/tools/perf/tests/make
+++ b/tools/perf/tests/make
@@ -1,3 +1,5 @@
+include ../scripts/Makefile.include
+
ifndef MK
ifeq ($(MAKECMDGOALS),)
# no target specified, trigger the whole suite
@@ -12,7 +14,19 @@ endif
else
PERF := .
-include config/Makefile.arch
+# As per kernel Makefile, avoid funny character set dependencies
+unexport LC_ALL
+LC_COLLATE=C
+LC_NUMERIC=C
+export LC_COLLATE LC_NUMERIC
+
+ifeq ($(srctree),)
+srctree := $(patsubst %/,%,$(dir $(shell pwd)))
+srctree := $(patsubst %/,%,$(dir $(srctree)))
+#$(info Determined 'srctree' to be $(srctree))
+endif
+
+include $(srctree)/tools/scripts/Makefile.arch
# FIXME looks like x86 is the only arch running tests ;-)
# we need some IS_(32/64) flag to make this generic
@@ -259,7 +273,8 @@ $(run_O):
tarpkg:
@cmd="$(PERF)/tests/perf-targz-src-pkg $(PERF)"; \
echo "- $@: $$cmd" && echo $$cmd > $@ && \
- ( eval $$cmd ) >> $@ 2>&1
+ ( eval $$cmd ) >> $@ 2>&1 && \
+ rm -f $@
make_kernelsrc:
@echo "- make -C <kernelsrc> tools/perf"
@@ -279,5 +294,5 @@ all: $(run) $(run_O) tarpkg make_kernelsrc make_kernelsrc_tools
out: $(run_O)
@echo OK
-.PHONY: all $(run) $(run_O) tarpkg clean
+.PHONY: all $(run) $(run_O) tarpkg clean make_kernelsrc make_kernelsrc_tools
endif # ifndef MK
diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c
index 4495493c9431..359e98fcd94c 100644
--- a/tools/perf/tests/mmap-basic.c
+++ b/tools/perf/tests/mmap-basic.c
@@ -16,7 +16,7 @@
* Then it checks if the number of syscalls reported as perf events by
* the kernel corresponds to the number of syscalls made.
*/
-int test__basic_mmap(void)
+int test__basic_mmap(int subtest __maybe_unused)
{
int err = -1;
union perf_event *event;
diff --git a/tools/perf/tests/mmap-thread-lookup.c b/tools/perf/tests/mmap-thread-lookup.c
index 145050e2e544..0c5ce44f723f 100644
--- a/tools/perf/tests/mmap-thread-lookup.c
+++ b/tools/perf/tests/mmap-thread-lookup.c
@@ -149,7 +149,6 @@ static int synth_process(struct machine *machine)
static int mmap_events(synth_cb synth)
{
- struct machines machines;
struct machine *machine;
int err, i;
@@ -162,8 +161,7 @@ static int mmap_events(synth_cb synth)
*/
TEST_ASSERT_VAL("failed to create threads", !threads_create());
- machines__init(&machines);
- machine = &machines.host;
+ machine = machine__new_host();
dump_trace = verbose > 1 ? 1 : 0;
@@ -203,7 +201,7 @@ static int mmap_events(synth_cb synth)
}
machine__delete_threads(machine);
- machines__exit(&machines);
+ machine__delete(machine);
return err;
}
@@ -221,7 +219,7 @@ static int mmap_events(synth_cb synth)
*
* by using all thread objects.
*/
-int test__mmap_thread_lookup(void)
+int test__mmap_thread_lookup(int subtest __maybe_unused)
{
/* perf_event__synthesize_threads synthesize */
TEST_ASSERT_VAL("failed with sythesizing all",
diff --git a/tools/perf/tests/openat-syscall-all-cpus.c b/tools/perf/tests/openat-syscall-all-cpus.c
index 2006485a2859..53c2273e8859 100644
--- a/tools/perf/tests/openat-syscall-all-cpus.c
+++ b/tools/perf/tests/openat-syscall-all-cpus.c
@@ -7,7 +7,7 @@
#include "debug.h"
#include "stat.h"
-int test__openat_syscall_event_on_all_cpus(void)
+int test__openat_syscall_event_on_all_cpus(int subtest __maybe_unused)
{
int err = -1, fd, cpu;
struct cpu_map *cpus;
diff --git a/tools/perf/tests/openat-syscall-tp-fields.c b/tools/perf/tests/openat-syscall-tp-fields.c
index 5e811cd8f1c3..eb99a105f31c 100644
--- a/tools/perf/tests/openat-syscall-tp-fields.c
+++ b/tools/perf/tests/openat-syscall-tp-fields.c
@@ -6,7 +6,7 @@
#include "tests.h"
#include "debug.h"
-int test__syscall_openat_tp_fields(void)
+int test__syscall_openat_tp_fields(int subtest __maybe_unused)
{
struct record_opts opts = {
.target = {
diff --git a/tools/perf/tests/openat-syscall.c b/tools/perf/tests/openat-syscall.c
index 033b54797b8a..1184f9ba6499 100644
--- a/tools/perf/tests/openat-syscall.c
+++ b/tools/perf/tests/openat-syscall.c
@@ -5,7 +5,7 @@
#include "debug.h"
#include "tests.h"
-int test__openat_syscall_event(void)
+int test__openat_syscall_event(int subtest __maybe_unused)
{
int err = -1, fd;
struct perf_evsel *evsel;
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c
index 636d7b42d844..abe8849d1d70 100644
--- a/tools/perf/tests/parse-events.c
+++ b/tools/perf/tests/parse-events.c
@@ -1765,7 +1765,7 @@ static void debug_warn(const char *warn, va_list params)
fprintf(stderr, " Warning: %s\n", msg);
}
-int test__parse_events(void)
+int test__parse_events(int subtest __maybe_unused)
{
int ret1, ret2 = 0;
diff --git a/tools/perf/tests/parse-no-sample-id-all.c b/tools/perf/tests/parse-no-sample-id-all.c
index 2c63ea658541..294c76b01b41 100644
--- a/tools/perf/tests/parse-no-sample-id-all.c
+++ b/tools/perf/tests/parse-no-sample-id-all.c
@@ -67,7 +67,7 @@ struct test_attr_event {
*
* Return: %0 on success, %-1 if the test fails.
*/
-int test__parse_no_sample_id_all(void)
+int test__parse_no_sample_id_all(int subtest __maybe_unused)
{
int err;
diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c
index 7a228a2a070b..1cc78cefe399 100644
--- a/tools/perf/tests/perf-record.c
+++ b/tools/perf/tests/perf-record.c
@@ -32,7 +32,7 @@ realloc:
return cpu;
}
-int test__PERF_RECORD(void)
+int test__PERF_RECORD(int subtest __maybe_unused)
{
struct record_opts opts = {
.target = {
@@ -40,12 +40,11 @@ int test__PERF_RECORD(void)
.uses_mmap = true,
},
.no_buffering = true,
- .freq = 10,
.mmap_pages = 256,
};
cpu_set_t cpu_mask;
size_t cpu_mask_size = sizeof(cpu_mask);
- struct perf_evlist *evlist = perf_evlist__new_default();
+ struct perf_evlist *evlist = perf_evlist__new_dummy();
struct perf_evsel *evsel;
struct perf_sample sample;
const char *cmd = "sleep";
@@ -61,6 +60,9 @@ int test__PERF_RECORD(void)
int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, };
char sbuf[STRERR_BUFSIZE];
+ if (evlist == NULL) /* Fallback for kernels lacking PERF_COUNT_SW_DUMMY */
+ evlist = perf_evlist__new_default();
+
if (evlist == NULL || argv == NULL) {
pr_debug("Not enough memory to create evlist\n");
goto out;
diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c
index faa04e9d5d5f..1e2ba2602930 100644
--- a/tools/perf/tests/pmu.c
+++ b/tools/perf/tests/pmu.c
@@ -133,7 +133,7 @@ static struct list_head *test_terms_list(void)
return &terms;
}
-int test__pmu(void)
+int test__pmu(int subtest __maybe_unused)
{
char *format = test_format_dir_get();
LIST_HEAD(formats);
diff --git a/tools/perf/tests/python-use.c b/tools/perf/tests/python-use.c
index 7760277c6def..7a52834ee0d0 100644
--- a/tools/perf/tests/python-use.c
+++ b/tools/perf/tests/python-use.c
@@ -4,11 +4,12 @@
#include <stdio.h>
#include <stdlib.h>
+#include <linux/compiler.h>
#include "tests.h"
extern int verbose;
-int test__python_use(void)
+int test__python_use(int subtest __maybe_unused)
{
char *cmd;
int ret;
diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c
index 30c02181e78b..5f23710b9fee 100644
--- a/tools/perf/tests/sample-parsing.c
+++ b/tools/perf/tests/sample-parsing.c
@@ -290,7 +290,7 @@ out_free:
* checks sample format bits separately and together. If the test passes %0 is
* returned, otherwise %-1 is returned.
*/
-int test__sample_parsing(void)
+int test__sample_parsing(int subtest __maybe_unused)
{
const u64 rf[] = {4, 5, 6, 7, 12, 13, 14, 15};
u64 sample_type;
diff --git a/tools/perf/tests/stat.c b/tools/perf/tests/stat.c
new file mode 100644
index 000000000000..6a20ff2326bb
--- /dev/null
+++ b/tools/perf/tests/stat.c
@@ -0,0 +1,111 @@
+#include <linux/compiler.h>
+#include "event.h"
+#include "tests.h"
+#include "stat.h"
+#include "counts.h"
+#include "debug.h"
+
+static bool has_term(struct stat_config_event *config,
+ u64 tag, u64 val)
+{
+ unsigned i;
+
+ for (i = 0; i < config->nr; i++) {
+ if ((config->data[i].tag == tag) &&
+ (config->data[i].val == val))
+ return true;
+ }
+
+ return false;
+}
+
+static int process_stat_config_event(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine __maybe_unused)
+{
+ struct stat_config_event *config = &event->stat_config;
+ struct perf_stat_config stat_config;
+
+#define HAS(term, val) \
+ has_term(config, PERF_STAT_CONFIG_TERM__##term, val)
+
+ TEST_ASSERT_VAL("wrong nr", config->nr == PERF_STAT_CONFIG_TERM__MAX);
+ TEST_ASSERT_VAL("wrong aggr_mode", HAS(AGGR_MODE, AGGR_CORE));
+ TEST_ASSERT_VAL("wrong scale", HAS(SCALE, 1));
+ TEST_ASSERT_VAL("wrong interval", HAS(INTERVAL, 1));
+
+#undef HAS
+
+ perf_event__read_stat_config(&stat_config, config);
+
+ TEST_ASSERT_VAL("wrong aggr_mode", stat_config.aggr_mode == AGGR_CORE);
+ TEST_ASSERT_VAL("wrong scale", stat_config.scale == 1);
+ TEST_ASSERT_VAL("wrong interval", stat_config.interval == 1);
+ return 0;
+}
+
+int test__synthesize_stat_config(int subtest __maybe_unused)
+{
+ struct perf_stat_config stat_config = {
+ .aggr_mode = AGGR_CORE,
+ .scale = 1,
+ .interval = 1,
+ };
+
+ TEST_ASSERT_VAL("failed to synthesize stat_config",
+ !perf_event__synthesize_stat_config(NULL, &stat_config, process_stat_config_event, NULL));
+
+ return 0;
+}
+
+static int process_stat_event(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine __maybe_unused)
+{
+ struct stat_event *st = &event->stat;
+
+ TEST_ASSERT_VAL("wrong cpu", st->cpu == 1);
+ TEST_ASSERT_VAL("wrong thread", st->thread == 2);
+ TEST_ASSERT_VAL("wrong id", st->id == 3);
+ TEST_ASSERT_VAL("wrong val", st->val == 100);
+ TEST_ASSERT_VAL("wrong run", st->ena == 200);
+ TEST_ASSERT_VAL("wrong ena", st->run == 300);
+ return 0;
+}
+
+int test__synthesize_stat(int subtest __maybe_unused)
+{
+ struct perf_counts_values count;
+
+ count.val = 100;
+ count.ena = 200;
+ count.run = 300;
+
+ TEST_ASSERT_VAL("failed to synthesize stat_config",
+ !perf_event__synthesize_stat(NULL, 1, 2, 3, &count, process_stat_event, NULL));
+
+ return 0;
+}
+
+static int process_stat_round_event(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine __maybe_unused)
+{
+ struct stat_round_event *stat_round = &event->stat_round;
+
+ TEST_ASSERT_VAL("wrong time", stat_round->time == 0xdeadbeef);
+ TEST_ASSERT_VAL("wrong type", stat_round->type == PERF_STAT_ROUND_TYPE__INTERVAL);
+ return 0;
+}
+
+int test__synthesize_stat_round(int subtest __maybe_unused)
+{
+ TEST_ASSERT_VAL("failed to synthesize stat_config",
+ !perf_event__synthesize_stat_round(NULL, 0xdeadbeef, PERF_STAT_ROUND_TYPE__INTERVAL,
+ process_stat_round_event, NULL));
+
+ return 0;
+}
diff --git a/tools/perf/tests/sw-clock.c b/tools/perf/tests/sw-clock.c
index 5b83f56a3b6f..36e8ce1550e3 100644
--- a/tools/perf/tests/sw-clock.c
+++ b/tools/perf/tests/sw-clock.c
@@ -122,7 +122,7 @@ out_delete_evlist:
return err;
}
-int test__sw_clock_freq(void)
+int test__sw_clock_freq(int subtest __maybe_unused)
{
int ret;
diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c
index a02af503100c..ebd80168d51e 100644
--- a/tools/perf/tests/switch-tracking.c
+++ b/tools/perf/tests/switch-tracking.c
@@ -305,7 +305,7 @@ out_free_nodes:
* evsel->system_wide and evsel->tracking flags (respectively) with other events
* sometimes enabled or disabled.
*/
-int test__switch_tracking(void)
+int test__switch_tracking(int subtest __maybe_unused)
{
const char *sched_switch = "sched:sched_switch";
struct switch_tracking switch_tracking = { .tids = NULL, };
@@ -455,7 +455,7 @@ int test__switch_tracking(void)
perf_evlist__enable(evlist);
- err = perf_evlist__disable_event(evlist, cpu_clocks_evsel);
+ err = perf_evsel__disable(cpu_clocks_evsel);
if (err) {
pr_debug("perf_evlist__disable_event failed!\n");
goto out_err;
@@ -474,7 +474,7 @@ int test__switch_tracking(void)
goto out_err;
}
- err = perf_evlist__disable_event(evlist, cycles_evsel);
+ err = perf_evsel__disable(cycles_evsel);
if (err) {
pr_debug("perf_evlist__disable_event failed!\n");
goto out_err;
@@ -500,7 +500,7 @@ int test__switch_tracking(void)
goto out_err;
}
- err = perf_evlist__enable_event(evlist, cycles_evsel);
+ err = perf_evsel__enable(cycles_evsel);
if (err) {
pr_debug("perf_evlist__disable_event failed!\n");
goto out_err;
diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c
index add16385f13e..2dfff7ac8ef3 100644
--- a/tools/perf/tests/task-exit.c
+++ b/tools/perf/tests/task-exit.c
@@ -31,7 +31,7 @@ static void workload_exec_failed_signal(int signo __maybe_unused,
* if the number of exit event reported by the kernel is 1 or not
* in order to check the kernel returns correct number of event.
*/
-int test__task_exit(void)
+int test__task_exit(int subtest __maybe_unused)
{
int err = -1;
union perf_event *event;
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index 3c8734a3abbc..82b2b5e6ba7c 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -1,6 +1,8 @@
#ifndef TESTS_H
#define TESTS_H
+#include <stdbool.h>
+
#define TEST_ASSERT_VAL(text, cond) \
do { \
if (!(cond)) { \
@@ -26,48 +28,63 @@ enum {
struct test {
const char *desc;
- int (*func)(void);
+ int (*func)(int subtest);
+ struct {
+ bool skip_if_fail;
+ int (*get_nr)(void);
+ const char *(*get_desc)(int subtest);
+ } subtest;
};
/* Tests */
-int test__vmlinux_matches_kallsyms(void);
-int test__openat_syscall_event(void);
-int test__openat_syscall_event_on_all_cpus(void);
-int test__basic_mmap(void);
-int test__PERF_RECORD(void);
-int test__perf_evsel__roundtrip_name_test(void);
-int test__perf_evsel__tp_sched_test(void);
-int test__syscall_openat_tp_fields(void);
-int test__pmu(void);
-int test__attr(void);
-int test__dso_data(void);
-int test__dso_data_cache(void);
-int test__dso_data_reopen(void);
-int test__parse_events(void);
-int test__hists_link(void);
-int test__python_use(void);
-int test__bp_signal(void);
-int test__bp_signal_overflow(void);
-int test__task_exit(void);
-int test__sw_clock_freq(void);
-int test__code_reading(void);
-int test__sample_parsing(void);
-int test__keep_tracking(void);
-int test__parse_no_sample_id_all(void);
-int test__dwarf_unwind(void);
-int test__hists_filter(void);
-int test__mmap_thread_lookup(void);
-int test__thread_mg_share(void);
-int test__hists_output(void);
-int test__hists_cumulate(void);
-int test__switch_tracking(void);
-int test__fdarray__filter(void);
-int test__fdarray__add(void);
-int test__kmod_path__parse(void);
-int test__thread_map(void);
-int test__llvm(void);
-int test__bpf(void);
-int test_session_topology(void);
+int test__vmlinux_matches_kallsyms(int subtest);
+int test__openat_syscall_event(int subtest);
+int test__openat_syscall_event_on_all_cpus(int subtest);
+int test__basic_mmap(int subtest);
+int test__PERF_RECORD(int subtest);
+int test__perf_evsel__roundtrip_name_test(int subtest);
+int test__perf_evsel__tp_sched_test(int subtest);
+int test__syscall_openat_tp_fields(int subtest);
+int test__pmu(int subtest);
+int test__attr(int subtest);
+int test__dso_data(int subtest);
+int test__dso_data_cache(int subtest);
+int test__dso_data_reopen(int subtest);
+int test__parse_events(int subtest);
+int test__hists_link(int subtest);
+int test__python_use(int subtest);
+int test__bp_signal(int subtest);
+int test__bp_signal_overflow(int subtest);
+int test__task_exit(int subtest);
+int test__sw_clock_freq(int subtest);
+int test__code_reading(int subtest);
+int test__sample_parsing(int subtest);
+int test__keep_tracking(int subtest);
+int test__parse_no_sample_id_all(int subtest);
+int test__dwarf_unwind(int subtest);
+int test__hists_filter(int subtest);
+int test__mmap_thread_lookup(int subtest);
+int test__thread_mg_share(int subtest);
+int test__hists_output(int subtest);
+int test__hists_cumulate(int subtest);
+int test__switch_tracking(int subtest);
+int test__fdarray__filter(int subtest);
+int test__fdarray__add(int subtest);
+int test__kmod_path__parse(int subtest);
+int test__thread_map(int subtest);
+int test__llvm(int subtest);
+const char *test__llvm_subtest_get_desc(int subtest);
+int test__llvm_subtest_get_nr(void);
+int test__bpf(int subtest);
+const char *test__bpf_subtest_get_desc(int subtest);
+int test__bpf_subtest_get_nr(void);
+int test_session_topology(int subtest);
+int test__thread_map_synthesize(int subtest);
+int test__cpu_map_synthesize(int subtest);
+int test__synthesize_stat_config(int subtest);
+int test__synthesize_stat(int subtest);
+int test__synthesize_stat_round(int subtest);
+int test__event_update(int subtest);
#if defined(__arm__) || defined(__aarch64__)
#ifdef HAVE_DWARF_UNWIND_SUPPORT
diff --git a/tools/perf/tests/thread-map.c b/tools/perf/tests/thread-map.c
index 138a0e3431fa..fccde848fe9c 100644
--- a/tools/perf/tests/thread-map.c
+++ b/tools/perf/tests/thread-map.c
@@ -4,7 +4,7 @@
#include "thread_map.h"
#include "debug.h"
-int test__thread_map(void)
+int test__thread_map(int subtest __maybe_unused)
{
struct thread_map *map;
@@ -40,3 +40,46 @@ int test__thread_map(void)
thread_map__put(map);
return 0;
}
+
+static int process_event(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine __maybe_unused)
+{
+ struct thread_map_event *map = &event->thread_map;
+ struct thread_map *threads;
+
+ TEST_ASSERT_VAL("wrong nr", map->nr == 1);
+ TEST_ASSERT_VAL("wrong pid", map->entries[0].pid == (u64) getpid());
+ TEST_ASSERT_VAL("wrong comm", !strcmp(map->entries[0].comm, "perf"));
+
+ threads = thread_map__new_event(&event->thread_map);
+ TEST_ASSERT_VAL("failed to alloc map", threads);
+
+ TEST_ASSERT_VAL("wrong nr", threads->nr == 1);
+ TEST_ASSERT_VAL("wrong pid",
+ thread_map__pid(threads, 0) == getpid());
+ TEST_ASSERT_VAL("wrong comm",
+ thread_map__comm(threads, 0) &&
+ !strcmp(thread_map__comm(threads, 0), "perf"));
+ TEST_ASSERT_VAL("wrong refcnt",
+ atomic_read(&threads->refcnt) == 1);
+ thread_map__put(threads);
+ return 0;
+}
+
+int test__thread_map_synthesize(int subtest __maybe_unused)
+{
+ struct thread_map *threads;
+
+ /* test map on current pid */
+ threads = thread_map__new_by_pid(getpid());
+ TEST_ASSERT_VAL("failed to alloc map", threads);
+
+ thread_map__read_comms(threads);
+
+ TEST_ASSERT_VAL("failed to synthesize map",
+ !perf_event__synthesize_thread_map2(NULL, threads, process_event, NULL));
+
+ return 0;
+}
diff --git a/tools/perf/tests/thread-mg-share.c b/tools/perf/tests/thread-mg-share.c
index 01fabb19d746..188b63140fc8 100644
--- a/tools/perf/tests/thread-mg-share.c
+++ b/tools/perf/tests/thread-mg-share.c
@@ -4,7 +4,7 @@
#include "map.h"
#include "debug.h"
-int test__thread_mg_share(void)
+int test__thread_mg_share(int subtest __maybe_unused)
{
struct machines machines;
struct machine *machine;
diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c
index f5bb096c3bd9..98fe69ac553c 100644
--- a/tools/perf/tests/topology.c
+++ b/tools/perf/tests/topology.c
@@ -84,7 +84,7 @@ static int check_cpu_topology(char *path, struct cpu_map *map)
return 0;
}
-int test_session_topology(void)
+int test_session_topology(int subtest __maybe_unused)
{
char path[PATH_MAX];
struct cpu_map *map;
diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c
index d677e018e504..f0bfc9e8fd9f 100644
--- a/tools/perf/tests/vmlinux-kallsyms.c
+++ b/tools/perf/tests/vmlinux-kallsyms.c
@@ -18,7 +18,7 @@ static int vmlinux_matches_kallsyms_filter(struct map *map __maybe_unused,
#define UM(x) kallsyms_map->unmap_ip(kallsyms_map, (x))
-int test__vmlinux_matches_kallsyms(void)
+int test__vmlinux_matches_kallsyms(int subtest __maybe_unused)
{
int err = -1;
struct rb_node *nd;
diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c
index e9703c0829f1..d37202121689 100644
--- a/tools/perf/ui/browser.c
+++ b/tools/perf/ui/browser.c
@@ -528,7 +528,7 @@ static struct ui_browser_colorset {
.colorset = HE_COLORSET_SELECTED,
.name = "selected",
.fg = "black",
- .bg = "lightgray",
+ .bg = "yellow",
},
{
.colorset = HE_COLORSET_CODE,
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index fa9eb92c9e24..08c09ad755d2 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -178,12 +178,51 @@ static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
return n;
}
+static int callchain_node__count_flat_rows(struct callchain_node *node)
+{
+ struct callchain_list *chain;
+ char folded_sign = 0;
+ int n = 0;
+
+ list_for_each_entry(chain, &node->parent_val, list) {
+ if (!folded_sign) {
+ /* only check first chain list entry */
+ folded_sign = callchain_list__folded(chain);
+ if (folded_sign == '+')
+ return 1;
+ }
+ n++;
+ }
+
+ list_for_each_entry(chain, &node->val, list) {
+ if (!folded_sign) {
+ /* node->parent_val list might be empty */
+ folded_sign = callchain_list__folded(chain);
+ if (folded_sign == '+')
+ return 1;
+ }
+ n++;
+ }
+
+ return n;
+}
+
+static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
+{
+ return 1;
+}
+
static int callchain_node__count_rows(struct callchain_node *node)
{
struct callchain_list *chain;
bool unfolded = false;
int n = 0;
+ if (callchain_param.mode == CHAIN_FLAT)
+ return callchain_node__count_flat_rows(node);
+ else if (callchain_param.mode == CHAIN_FOLDED)
+ return callchain_node__count_folded_rows(node);
+
list_for_each_entry(chain, &node->val, list) {
++n;
unfolded = chain->unfolded;
@@ -263,7 +302,7 @@ static void callchain_node__init_have_children(struct callchain_node *node,
chain = list_entry(node->val.next, struct callchain_list, list);
chain->has_children = has_sibling;
- if (!list_empty(&node->val)) {
+ if (node->val.next != node->val.prev) {
chain = list_entry(node->val.prev, struct callchain_list, list);
chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
}
@@ -279,6 +318,9 @@ static void callchain__init_have_children(struct rb_root *root)
for (nd = rb_first(root); nd; nd = rb_next(nd)) {
struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
callchain_node__init_have_children(node, has_sibling);
+ if (callchain_param.mode == CHAIN_FLAT ||
+ callchain_param.mode == CHAIN_FOLDED)
+ callchain_node__make_parent_list(node);
}
}
@@ -298,6 +340,9 @@ static bool hist_browser__toggle_fold(struct hist_browser *browser)
struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
bool has_children;
+ if (!he || !ms)
+ return false;
+
if (ms == &he->ms)
has_children = hist_entry__toggle_fold(he);
else
@@ -435,7 +480,7 @@ static int hist_browser__run(struct hist_browser *browser, const char *help)
hists__browser_title(browser->hists, hbt, title, sizeof(title));
- if (ui_browser__show(&browser->b, title, help) < 0)
+ if (ui_browser__show(&browser->b, title, "%s", help) < 0)
return -1;
while (1) {
@@ -574,6 +619,231 @@ static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_u
#define LEVEL_OFFSET_STEP 3
+static int hist_browser__show_callchain_list(struct hist_browser *browser,
+ struct callchain_node *node,
+ struct callchain_list *chain,
+ unsigned short row, u64 total,
+ bool need_percent, int offset,
+ print_callchain_entry_fn print,
+ struct callchain_print_arg *arg)
+{
+ char bf[1024], *alloc_str;
+ const char *str;
+
+ if (arg->row_offset != 0) {
+ arg->row_offset--;
+ return 0;
+ }
+
+ alloc_str = NULL;
+ str = callchain_list__sym_name(chain, bf, sizeof(bf),
+ browser->show_dso);
+
+ if (need_percent) {
+ char buf[64];
+
+ callchain_node__scnprintf_value(node, buf, sizeof(buf),
+ total);
+
+ if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
+ str = "Not enough memory!";
+ else
+ str = alloc_str;
+ }
+
+ print(browser, chain, str, offset, row, arg);
+
+ free(alloc_str);
+ return 1;
+}
+
+static int hist_browser__show_callchain_flat(struct hist_browser *browser,
+ struct rb_root *root,
+ unsigned short row, u64 total,
+ print_callchain_entry_fn print,
+ struct callchain_print_arg *arg,
+ check_output_full_fn is_output_full)
+{
+ struct rb_node *node;
+ int first_row = row, offset = LEVEL_OFFSET_STEP;
+ bool need_percent;
+
+ node = rb_first(root);
+ need_percent = node && rb_next(node);
+
+ while (node) {
+ struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
+ struct rb_node *next = rb_next(node);
+ struct callchain_list *chain;
+ char folded_sign = ' ';
+ int first = true;
+ int extra_offset = 0;
+
+ list_for_each_entry(chain, &child->parent_val, list) {
+ bool was_first = first;
+
+ if (first)
+ first = false;
+ else if (need_percent)
+ extra_offset = LEVEL_OFFSET_STEP;
+
+ folded_sign = callchain_list__folded(chain);
+
+ row += hist_browser__show_callchain_list(browser, child,
+ chain, row, total,
+ was_first && need_percent,
+ offset + extra_offset,
+ print, arg);
+
+ if (is_output_full(browser, row))
+ goto out;
+
+ if (folded_sign == '+')
+ goto next;
+ }
+
+ list_for_each_entry(chain, &child->val, list) {
+ bool was_first = first;
+
+ if (first)
+ first = false;
+ else if (need_percent)
+ extra_offset = LEVEL_OFFSET_STEP;
+
+ folded_sign = callchain_list__folded(chain);
+
+ row += hist_browser__show_callchain_list(browser, child,
+ chain, row, total,
+ was_first && need_percent,
+ offset + extra_offset,
+ print, arg);
+
+ if (is_output_full(browser, row))
+ goto out;
+
+ if (folded_sign == '+')
+ break;
+ }
+
+next:
+ if (is_output_full(browser, row))
+ break;
+ node = next;
+ }
+out:
+ return row - first_row;
+}
+
+static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
+ struct callchain_list *chain,
+ char *value_str, char *old_str)
+{
+ char bf[1024];
+ const char *str;
+ char *new;
+
+ str = callchain_list__sym_name(chain, bf, sizeof(bf),
+ browser->show_dso);
+ if (old_str) {
+ if (asprintf(&new, "%s%s%s", old_str,
+ symbol_conf.field_sep ?: ";", str) < 0)
+ new = NULL;
+ } else {
+ if (value_str) {
+ if (asprintf(&new, "%s %s", value_str, str) < 0)
+ new = NULL;
+ } else {
+ if (asprintf(&new, "%s", str) < 0)
+ new = NULL;
+ }
+ }
+ return new;
+}
+
+static int hist_browser__show_callchain_folded(struct hist_browser *browser,
+ struct rb_root *root,
+ unsigned short row, u64 total,
+ print_callchain_entry_fn print,
+ struct callchain_print_arg *arg,
+ check_output_full_fn is_output_full)
+{
+ struct rb_node *node;
+ int first_row = row, offset = LEVEL_OFFSET_STEP;
+ bool need_percent;
+
+ node = rb_first(root);
+ need_percent = node && rb_next(node);
+
+ while (node) {
+ struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
+ struct rb_node *next = rb_next(node);
+ struct callchain_list *chain, *first_chain = NULL;
+ int first = true;
+ char *value_str = NULL, *value_str_alloc = NULL;
+ char *chain_str = NULL, *chain_str_alloc = NULL;
+
+ if (arg->row_offset != 0) {
+ arg->row_offset--;
+ goto next;
+ }
+
+ if (need_percent) {
+ char buf[64];
+
+ callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
+ if (asprintf(&value_str, "%s", buf) < 0) {
+ value_str = (char *)"<...>";
+ goto do_print;
+ }
+ value_str_alloc = value_str;
+ }
+
+ list_for_each_entry(chain, &child->parent_val, list) {
+ chain_str = hist_browser__folded_callchain_str(browser,
+ chain, value_str, chain_str);
+ if (first) {
+ first = false;
+ first_chain = chain;
+ }
+
+ if (chain_str == NULL) {
+ chain_str = (char *)"Not enough memory!";
+ goto do_print;
+ }
+
+ chain_str_alloc = chain_str;
+ }
+
+ list_for_each_entry(chain, &child->val, list) {
+ chain_str = hist_browser__folded_callchain_str(browser,
+ chain, value_str, chain_str);
+ if (first) {
+ first = false;
+ first_chain = chain;
+ }
+
+ if (chain_str == NULL) {
+ chain_str = (char *)"Not enough memory!";
+ goto do_print;
+ }
+
+ chain_str_alloc = chain_str;
+ }
+
+do_print:
+ print(browser, first_chain, chain_str, offset, row++, arg);
+ free(value_str_alloc);
+ free(chain_str_alloc);
+
+next:
+ if (is_output_full(browser, row))
+ break;
+ node = next;
+ }
+
+ return row - first_row;
+}
+
static int hist_browser__show_callchain(struct hist_browser *browser,
struct rb_root *root, int level,
unsigned short row, u64 total,
@@ -592,15 +862,12 @@ static int hist_browser__show_callchain(struct hist_browser *browser,
while (node) {
struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
struct rb_node *next = rb_next(node);
- u64 cumul = callchain_cumul_hits(child);
struct callchain_list *chain;
char folded_sign = ' ';
int first = true;
int extra_offset = 0;
list_for_each_entry(chain, &child->val, list) {
- char bf[1024], *alloc_str;
- const char *str;
bool was_first = first;
if (first)
@@ -609,31 +876,16 @@ static int hist_browser__show_callchain(struct hist_browser *browser,
extra_offset = LEVEL_OFFSET_STEP;
folded_sign = callchain_list__folded(chain);
- if (arg->row_offset != 0) {
- arg->row_offset--;
- goto do_next;
- }
-
- alloc_str = NULL;
- str = callchain_list__sym_name(chain, bf, sizeof(bf),
- browser->show_dso);
-
- if (was_first && need_percent) {
- double percent = cumul * 100.0 / total;
- if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
- str = "Not enough memory!";
- else
- str = alloc_str;
- }
-
- print(browser, chain, str, offset + extra_offset, row, arg);
+ row += hist_browser__show_callchain_list(browser, child,
+ chain, row, total,
+ was_first && need_percent,
+ offset + extra_offset,
+ print, arg);
- free(alloc_str);
-
- if (is_output_full(browser, ++row))
+ if (is_output_full(browser, row))
goto out;
-do_next:
+
if (folded_sign == '+')
break;
}
@@ -789,7 +1041,8 @@ static int hist_browser__show_entry(struct hist_browser *browser,
hist_browser__gotorc(browser, row, 0);
perf_hpp__for_each_format(fmt) {
- if (perf_hpp__should_skip(fmt) || column++ < browser->b.horiz_scroll)
+ if (perf_hpp__should_skip(fmt, entry->hists) ||
+ column++ < browser->b.horiz_scroll)
continue;
if (current_entry && browser->b.navkeypressed) {
@@ -844,10 +1097,22 @@ static int hist_browser__show_entry(struct hist_browser *browser,
total = entry->stat.period;
}
- printed += hist_browser__show_callchain(browser,
+ if (callchain_param.mode == CHAIN_FLAT) {
+ printed += hist_browser__show_callchain_flat(browser,
+ &entry->sorted_chain, row, total,
+ hist_browser__show_callchain_entry, &arg,
+ hist_browser__check_output_full);
+ } else if (callchain_param.mode == CHAIN_FOLDED) {
+ printed += hist_browser__show_callchain_folded(browser,
+ &entry->sorted_chain, row, total,
+ hist_browser__show_callchain_entry, &arg,
+ hist_browser__check_output_full);
+ } else {
+ printed += hist_browser__show_callchain(browser,
&entry->sorted_chain, 1, row, total,
hist_browser__show_callchain_entry, &arg,
hist_browser__check_output_full);
+ }
if (arg.is_current_entry)
browser->he_selection = entry;
@@ -880,7 +1145,7 @@ static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *
}
perf_hpp__for_each_format(fmt) {
- if (perf_hpp__should_skip(fmt) || column++ < browser->b.horiz_scroll)
+ if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
continue;
ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
@@ -928,6 +1193,8 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)
}
ui_browser__hists_init_top(browser);
+ hb->he_selection = NULL;
+ hb->selection = NULL;
for (nd = browser->top; nd; nd = rb_next(nd)) {
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
@@ -1033,6 +1300,9 @@ static void ui_browser__hists_seek(struct ui_browser *browser,
* and stop when we printed enough lines to fill the screen.
*/
do_offset:
+ if (!nd)
+ return;
+
if (offset > 0) {
do {
h = rb_entry(nd, struct hist_entry, rb_node);
@@ -1145,7 +1415,7 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
printed += fprintf(fp, "%c ", folded_sign);
perf_hpp__for_each_format(fmt) {
- if (perf_hpp__should_skip(fmt))
+ if (perf_hpp__should_skip(fmt, he->hists))
continue;
if (!first) {
@@ -1794,10 +2064,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
SLang_reset_tty();
SLang_init_tty(0, 0, 0);
- if (min_pcnt) {
+ if (min_pcnt)
browser->min_pcnt = min_pcnt;
- hist_browser__update_nr_entries(browser);
- }
+ hist_browser__update_nr_entries(browser);
browser->pstack = pstack__new(3);
if (browser->pstack == NULL)
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c
index 4b3585eed1e8..0f8dcfdfb10f 100644
--- a/tools/perf/ui/gtk/hists.c
+++ b/tools/perf/ui/gtk/hists.c
@@ -89,8 +89,8 @@ void perf_gtk__init_hpp(void)
perf_gtk__hpp_color_overhead_acc;
}
-static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store,
- GtkTreeIter *parent, int col, u64 total)
+static void perf_gtk__add_callchain_flat(struct rb_root *root, GtkTreeStore *store,
+ GtkTreeIter *parent, int col, u64 total)
{
struct rb_node *nd;
bool has_single_node = (rb_first(root) == rb_last(root));
@@ -100,13 +100,132 @@ static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store,
struct callchain_list *chain;
GtkTreeIter iter, new_parent;
bool need_new_parent;
- double percent;
- u64 hits, child_total;
node = rb_entry(nd, struct callchain_node, rb_node);
- hits = callchain_cumul_hits(node);
- percent = 100.0 * hits / total;
+ new_parent = *parent;
+ need_new_parent = !has_single_node;
+
+ callchain_node__make_parent_list(node);
+
+ list_for_each_entry(chain, &node->parent_val, list) {
+ char buf[128];
+
+ gtk_tree_store_append(store, &iter, &new_parent);
+
+ callchain_node__scnprintf_value(node, buf, sizeof(buf), total);
+ gtk_tree_store_set(store, &iter, 0, buf, -1);
+
+ callchain_list__sym_name(chain, buf, sizeof(buf), false);
+ gtk_tree_store_set(store, &iter, col, buf, -1);
+
+ if (need_new_parent) {
+ /*
+ * Only show the top-most symbol in a callchain
+ * if it's not the only callchain.
+ */
+ new_parent = iter;
+ need_new_parent = false;
+ }
+ }
+
+ list_for_each_entry(chain, &node->val, list) {
+ char buf[128];
+
+ gtk_tree_store_append(store, &iter, &new_parent);
+
+ callchain_node__scnprintf_value(node, buf, sizeof(buf), total);
+ gtk_tree_store_set(store, &iter, 0, buf, -1);
+
+ callchain_list__sym_name(chain, buf, sizeof(buf), false);
+ gtk_tree_store_set(store, &iter, col, buf, -1);
+
+ if (need_new_parent) {
+ /*
+ * Only show the top-most symbol in a callchain
+ * if it's not the only callchain.
+ */
+ new_parent = iter;
+ need_new_parent = false;
+ }
+ }
+ }
+}
+
+static void perf_gtk__add_callchain_folded(struct rb_root *root, GtkTreeStore *store,
+ GtkTreeIter *parent, int col, u64 total)
+{
+ struct rb_node *nd;
+
+ for (nd = rb_first(root); nd; nd = rb_next(nd)) {
+ struct callchain_node *node;
+ struct callchain_list *chain;
+ GtkTreeIter iter;
+ char buf[64];
+ char *str, *str_alloc = NULL;
+ bool first = true;
+
+ node = rb_entry(nd, struct callchain_node, rb_node);
+
+ callchain_node__make_parent_list(node);
+
+ list_for_each_entry(chain, &node->parent_val, list) {
+ char name[1024];
+
+ callchain_list__sym_name(chain, name, sizeof(name), false);
+
+ if (asprintf(&str, "%s%s%s",
+ first ? "" : str_alloc,
+ first ? "" : symbol_conf.field_sep ?: "; ",
+ name) < 0)
+ return;
+
+ first = false;
+ free(str_alloc);
+ str_alloc = str;
+ }
+
+ list_for_each_entry(chain, &node->val, list) {
+ char name[1024];
+
+ callchain_list__sym_name(chain, name, sizeof(name), false);
+
+ if (asprintf(&str, "%s%s%s",
+ first ? "" : str_alloc,
+ first ? "" : symbol_conf.field_sep ?: "; ",
+ name) < 0)
+ return;
+
+ first = false;
+ free(str_alloc);
+ str_alloc = str;
+ }
+
+ gtk_tree_store_append(store, &iter, parent);
+
+ callchain_node__scnprintf_value(node, buf, sizeof(buf), total);
+ gtk_tree_store_set(store, &iter, 0, buf, -1);
+
+ gtk_tree_store_set(store, &iter, col, str, -1);
+
+ free(str_alloc);
+ }
+}
+
+static void perf_gtk__add_callchain_graph(struct rb_root *root, GtkTreeStore *store,
+ GtkTreeIter *parent, int col, u64 total)
+{
+ struct rb_node *nd;
+ bool has_single_node = (rb_first(root) == rb_last(root));
+
+ for (nd = rb_first(root); nd; nd = rb_next(nd)) {
+ struct callchain_node *node;
+ struct callchain_list *chain;
+ GtkTreeIter iter, new_parent;
+ bool need_new_parent;
+ u64 child_total;
+
+ node = rb_entry(nd, struct callchain_node, rb_node);
new_parent = *parent;
need_new_parent = !has_single_node && (node->val_nr > 1);
@@ -116,7 +235,7 @@ static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store,
gtk_tree_store_append(store, &iter, &new_parent);
- scnprintf(buf, sizeof(buf), "%5.2f%%", percent);
+ callchain_node__scnprintf_value(node, buf, sizeof(buf), total);
gtk_tree_store_set(store, &iter, 0, buf, -1);
callchain_list__sym_name(chain, buf, sizeof(buf), false);
@@ -138,11 +257,22 @@ static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store,
child_total = total;
/* Now 'iter' contains info of the last callchain_list */
- perf_gtk__add_callchain(&node->rb_root, store, &iter, col,
- child_total);
+ perf_gtk__add_callchain_graph(&node->rb_root, store, &iter, col,
+ child_total);
}
}
+static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store,
+ GtkTreeIter *parent, int col, u64 total)
+{
+ if (callchain_param.mode == CHAIN_FLAT)
+ perf_gtk__add_callchain_flat(root, store, parent, col, total);
+ else if (callchain_param.mode == CHAIN_FOLDED)
+ perf_gtk__add_callchain_folded(root, store, parent, col, total);
+ else
+ perf_gtk__add_callchain_graph(root, store, parent, col, total);
+}
+
static void on_row_activated(GtkTreeView *view, GtkTreePath *path,
GtkTreeViewColumn *col __maybe_unused,
gpointer user_data __maybe_unused)
@@ -188,7 +318,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
col_idx = 0;
perf_hpp__for_each_format(fmt) {
- if (perf_hpp__should_skip(fmt))
+ if (perf_hpp__should_skip(fmt, hists))
continue;
/*
@@ -238,7 +368,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
col_idx = 0;
perf_hpp__for_each_format(fmt) {
- if (perf_hpp__should_skip(fmt))
+ if (perf_hpp__should_skip(fmt, h->hists))
continue;
if (fmt->color)
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c
index 5029ba2b55af..bf2a66e254ea 100644
--- a/tools/perf/ui/hist.c
+++ b/tools/perf/ui/hist.c
@@ -443,7 +443,6 @@ LIST_HEAD(perf_hpp__sort_list);
void perf_hpp__init(void)
{
- struct list_head *list;
int i;
for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
@@ -484,17 +483,6 @@ void perf_hpp__init(void)
if (symbol_conf.show_total_period)
hpp_dimension__add_output(PERF_HPP__PERIOD);
-
- /* prepend overhead field for backward compatiblity. */
- list = &perf_hpp__format[PERF_HPP__OVERHEAD].sort_list;
- if (list_empty(list))
- list_add(list, &perf_hpp__sort_list);
-
- if (symbol_conf.cumulate_callchain) {
- list = &perf_hpp__format[PERF_HPP__OVERHEAD_ACC].sort_list;
- if (list_empty(list))
- list_add(list, &perf_hpp__sort_list);
- }
}
void perf_hpp__column_register(struct perf_hpp_fmt *format)
@@ -619,7 +607,7 @@ unsigned int hists__sort_list_width(struct hists *hists)
struct perf_hpp dummy_hpp;
perf_hpp__for_each_format(fmt) {
- if (perf_hpp__should_skip(fmt))
+ if (perf_hpp__should_skip(fmt, hists))
continue;
if (first)
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
index dfcbc90146ef..387110d50b00 100644
--- a/tools/perf/ui/stdio/hist.c
+++ b/tools/perf/ui/stdio/hist.c
@@ -34,10 +34,10 @@ static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
return ret;
}
-static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
+static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_node *node,
+ struct callchain_list *chain,
int depth, int depth_mask, int period,
- u64 total_samples, u64 hits,
- int left_margin)
+ u64 total_samples, int left_margin)
{
int i;
size_t ret = 0;
@@ -50,10 +50,9 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
else
ret += fprintf(fp, " ");
if (!period && i == depth - 1) {
- double percent;
-
- percent = hits * 100.0 / total_samples;
- ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
+ ret += fprintf(fp, "--");
+ ret += callchain_node__fprintf_value(node, fp, total_samples);
+ ret += fprintf(fp, "--");
} else
ret += fprintf(fp, "%s", " ");
}
@@ -82,13 +81,14 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
int depth_mask, int left_margin)
{
struct rb_node *node, *next;
- struct callchain_node *child;
+ struct callchain_node *child = NULL;
struct callchain_list *chain;
int new_depth_mask = depth_mask;
u64 remaining;
size_t ret = 0;
int i;
uint entries_printed = 0;
+ int cumul_count = 0;
remaining = total_samples;
@@ -100,6 +100,7 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
child = rb_entry(node, struct callchain_node, rb_node);
cumul = callchain_cumul_hits(child);
remaining -= cumul;
+ cumul_count += callchain_cumul_counts(child);
/*
* The depth mask manages the output of pipes that show
@@ -120,10 +121,9 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
left_margin);
i = 0;
list_for_each_entry(chain, &child->val, list) {
- ret += ipchain__fprintf_graph(fp, chain, depth,
+ ret += ipchain__fprintf_graph(fp, child, chain, depth,
new_depth_mask, i++,
total_samples,
- cumul,
left_margin);
}
@@ -143,14 +143,23 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
if (callchain_param.mode == CHAIN_GRAPH_REL &&
remaining && remaining != total_samples) {
+ struct callchain_node rem_node = {
+ .hit = remaining,
+ };
if (!rem_sq_bracket)
return ret;
+ if (callchain_param.value == CCVAL_COUNT && child && child->parent) {
+ rem_node.count = child->parent->children_count - cumul_count;
+ if (rem_node.count <= 0)
+ return ret;
+ }
+
new_depth_mask &= ~(1 << (depth - 1));
- ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
+ ret += ipchain__fprintf_graph(fp, &rem_node, &rem_hits, depth,
new_depth_mask, 0, total_samples,
- remaining, left_margin);
+ left_margin);
}
return ret;
@@ -243,12 +252,11 @@ static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *tree,
struct rb_node *rb_node = rb_first(tree);
while (rb_node) {
- double percent;
-
chain = rb_entry(rb_node, struct callchain_node, rb_node);
- percent = chain->hit * 100.0 / total_samples;
- ret = percent_color_fprintf(fp, " %6.2f%%\n", percent);
+ ret += fprintf(fp, " ");
+ ret += callchain_node__fprintf_value(chain, fp, total_samples);
+ ret += fprintf(fp, "\n");
ret += __callchain__fprintf_flat(fp, chain, total_samples);
ret += fprintf(fp, "\n");
if (++entries_printed == callchain_param.print_limit)
@@ -260,6 +268,57 @@ static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *tree,
return ret;
}
+static size_t __callchain__fprintf_folded(FILE *fp, struct callchain_node *node)
+{
+ const char *sep = symbol_conf.field_sep ?: ";";
+ struct callchain_list *chain;
+ size_t ret = 0;
+ char bf[1024];
+ bool first;
+
+ if (!node)
+ return 0;
+
+ ret += __callchain__fprintf_folded(fp, node->parent);
+
+ first = (ret == 0);
+ list_for_each_entry(chain, &node->val, list) {
+ if (chain->ip >= PERF_CONTEXT_MAX)
+ continue;
+ ret += fprintf(fp, "%s%s", first ? "" : sep,
+ callchain_list__sym_name(chain,
+ bf, sizeof(bf), false));
+ first = false;
+ }
+
+ return ret;
+}
+
+static size_t callchain__fprintf_folded(FILE *fp, struct rb_root *tree,
+ u64 total_samples)
+{
+ size_t ret = 0;
+ u32 entries_printed = 0;
+ struct callchain_node *chain;
+ struct rb_node *rb_node = rb_first(tree);
+
+ while (rb_node) {
+
+ chain = rb_entry(rb_node, struct callchain_node, rb_node);
+
+ ret += callchain_node__fprintf_value(chain, fp, total_samples);
+ ret += fprintf(fp, " ");
+ ret += __callchain__fprintf_folded(fp, chain);
+ ret += fprintf(fp, "\n");
+ if (++entries_printed == callchain_param.print_limit)
+ break;
+
+ rb_node = rb_next(rb_node);
+ }
+
+ return ret;
+}
+
static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
u64 total_samples, int left_margin,
FILE *fp)
@@ -278,6 +337,9 @@ static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
case CHAIN_FLAT:
return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples);
break;
+ case CHAIN_FOLDED:
+ return callchain__fprintf_folded(fp, &he->sorted_chain, total_samples);
+ break;
case CHAIN_NONE:
break;
default:
@@ -323,7 +385,7 @@ static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp)
return 0;
perf_hpp__for_each_format(fmt) {
- if (perf_hpp__should_skip(fmt))
+ if (perf_hpp__should_skip(fmt, he->hists))
continue;
/*
@@ -402,7 +464,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
fprintf(fp, "# ");
perf_hpp__for_each_format(fmt) {
- if (perf_hpp__should_skip(fmt))
+ if (perf_hpp__should_skip(fmt, hists))
continue;
if (!first)
@@ -428,7 +490,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
perf_hpp__for_each_format(fmt) {
unsigned int i;
- if (perf_hpp__should_skip(fmt))
+ if (perf_hpp__should_skip(fmt, hists))
continue;
if (!first)
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 591b3fe3ed49..5eec53a3f4ac 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -6,24 +6,20 @@ libperf-y += config.o
libperf-y += ctype.o
libperf-y += db-export.o
libperf-y += env.o
-libperf-y += environment.o
libperf-y += event.o
libperf-y += evlist.o
libperf-y += evsel.o
-libperf-y += exec_cmd.o
-libperf-y += find_next_bit.o
-libperf-y += help.o
+libperf-y += find_bit.o
libperf-y += kallsyms.o
libperf-y += levenshtein.o
libperf-y += llvm-utils.o
-libperf-y += parse-options.o
libperf-y += parse-events.o
libperf-y += perf_regs.o
libperf-y += path.o
libperf-y += rbtree.o
+libperf-y += libstring.o
libperf-y += bitmap.o
libperf-y += hweight.o
-libperf-y += run-command.o
libperf-y += quote.o
libperf-y += strbuf.o
libperf-y += string.o
@@ -32,11 +28,9 @@ libperf-y += strfilter.o
libperf-y += top.o
libperf-y += usage.o
libperf-y += wrapper.o
-libperf-y += sigchain.o
libperf-y += dso.o
libperf-y += symbol.o
libperf-y += color.o
-libperf-y += pager.o
libperf-y += header.o
libperf-y += callchain.o
libperf-y += values.o
@@ -86,8 +80,11 @@ libperf-$(CONFIG_AUXTRACE) += intel-pt.o
libperf-$(CONFIG_AUXTRACE) += intel-bts.o
libperf-y += parse-branch-options.o
libperf-y += parse-regs-options.o
+libperf-y += term.o
+libperf-y += help-unknown-cmd.o
libperf-$(CONFIG_LIBBPF) += bpf-loader.o
+libperf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o
libperf-$(CONFIG_LIBELF) += symbol-elf.o
libperf-$(CONFIG_LIBELF) += probe-file.o
libperf-$(CONFIG_LIBELF) += probe-event.o
@@ -110,7 +107,6 @@ libperf-$(CONFIG_ZLIB) += zlib.o
libperf-$(CONFIG_LZMA) += lzma.o
CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
-CFLAGS_exec_cmd.o += -DPERF_EXEC_PATH="BUILD_STR($(perfexecdir_SQ))" -DPREFIX="BUILD_STR($(prefix_SQ))"
$(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c
$(call rule_mkdir)
@@ -136,8 +132,10 @@ 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
$(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c
-CFLAGS_find_next_bit.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
+CFLAGS_bitmap.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
+CFLAGS_find_bit.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
CFLAGS_rbtree.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
+CFLAGS_libstring.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
CFLAGS_hweight.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
CFLAGS_parse-events.o += -Wno-redundant-decls
@@ -145,7 +143,11 @@ $(OUTPUT)util/kallsyms.o: ../lib/symbol/kallsyms.c FORCE
$(call rule_mkdir)
$(call if_changed_dep,cc_o_c)
-$(OUTPUT)util/find_next_bit.o: ../lib/util/find_next_bit.c FORCE
+$(OUTPUT)util/bitmap.o: ../lib/bitmap.c FORCE
+ $(call rule_mkdir)
+ $(call if_changed_dep,cc_o_c)
+
+$(OUTPUT)util/find_bit.o: ../lib/find_bit.c FORCE
$(call rule_mkdir)
$(call if_changed_dep,cc_o_c)
@@ -153,6 +155,10 @@ $(OUTPUT)util/rbtree.o: ../lib/rbtree.c FORCE
$(call rule_mkdir)
$(call if_changed_dep,cc_o_c)
+$(OUTPUT)util/libstring.o: ../lib/string.c FORCE
+ $(call rule_mkdir)
+ $(call if_changed_dep,cc_o_c)
+
$(OUTPUT)util/hweight.o: ../lib/hweight.c FORCE
$(call rule_mkdir)
$(call if_changed_dep,cc_o_c)
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 1dd1949b0e79..b795b6994144 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -65,6 +65,11 @@ static int call__parse(struct ins_operands *ops)
name++;
+#ifdef __arm__
+ if (strchr(name, '+'))
+ return -1;
+#endif
+
tok = strchr(name, '>');
if (tok == NULL)
return -1;
@@ -246,7 +251,11 @@ static int mov__parse(struct ins_operands *ops)
return -1;
target = ++s;
+#ifdef __arm__
+ comment = strchr(s, ';');
+#else
comment = strchr(s, '#');
+#endif
if (comment != NULL)
s = comment - 1;
@@ -354,6 +363,20 @@ static struct ins instructions[] = {
{ .name = "addq", .ops = &mov_ops, },
{ .name = "addw", .ops = &mov_ops, },
{ .name = "and", .ops = &mov_ops, },
+#ifdef __arm__
+ { .name = "b", .ops = &jump_ops, }, // might also be a call
+ { .name = "bcc", .ops = &jump_ops, },
+ { .name = "bcs", .ops = &jump_ops, },
+ { .name = "beq", .ops = &jump_ops, },
+ { .name = "bge", .ops = &jump_ops, },
+ { .name = "bgt", .ops = &jump_ops, },
+ { .name = "bhi", .ops = &jump_ops, },
+ { .name = "bl", .ops = &call_ops, },
+ { .name = "blt", .ops = &jump_ops, },
+ { .name = "bls", .ops = &jump_ops, },
+ { .name = "blx", .ops = &call_ops, },
+ { .name = "bne", .ops = &jump_ops, },
+#endif
{ .name = "bts", .ops = &mov_ops, },
{ .name = "call", .ops = &call_ops, },
{ .name = "callq", .ops = &call_ops, },
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index 7f10430af39c..360fda01f3b0 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -45,7 +45,7 @@
#include "event.h"
#include "session.h"
#include "debug.h"
-#include "parse-options.h"
+#include <subcmd/parse-options.h>
#include "intel-pt.h"
#include "intel-bts.h"
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 4c50411371db..540a7efa657e 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -5,11 +5,15 @@
* Copyright (C) 2015 Huawei Inc.
*/
+#include <linux/bpf.h>
#include <bpf/libbpf.h>
#include <linux/err.h>
+#include <linux/string.h>
#include "perf.h"
#include "debug.h"
#include "bpf-loader.h"
+#include "bpf-prologue.h"
+#include "llvm-utils.h"
#include "probe-event.h"
#include "probe-finder.h" // for MAX_PROBES
#include "llvm-utils.h"
@@ -32,6 +36,10 @@ DEFINE_PRINT_FN(debug, 1)
struct bpf_prog_priv {
struct perf_probe_event pev;
+ bool need_prologue;
+ struct bpf_insn *insns_buf;
+ int nr_types;
+ int *type_mapping;
};
static bool libbpf_initialized;
@@ -106,10 +114,178 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,
struct bpf_prog_priv *priv = _priv;
cleanup_perf_probe_events(&priv->pev, 1);
+ zfree(&priv->insns_buf);
+ zfree(&priv->type_mapping);
free(priv);
}
static int
+prog_config__exec(const char *value, struct perf_probe_event *pev)
+{
+ pev->uprobes = true;
+ pev->target = strdup(value);
+ if (!pev->target)
+ return -ENOMEM;
+ return 0;
+}
+
+static int
+prog_config__module(const char *value, struct perf_probe_event *pev)
+{
+ pev->uprobes = false;
+ pev->target = strdup(value);
+ if (!pev->target)
+ return -ENOMEM;
+ return 0;
+}
+
+static int
+prog_config__bool(const char *value, bool *pbool, bool invert)
+{
+ int err;
+ bool bool_value;
+
+ if (!pbool)
+ return -EINVAL;
+
+ err = strtobool(value, &bool_value);
+ if (err)
+ return err;
+
+ *pbool = invert ? !bool_value : bool_value;
+ return 0;
+}
+
+static int
+prog_config__inlines(const char *value,
+ struct perf_probe_event *pev __maybe_unused)
+{
+ return prog_config__bool(value, &probe_conf.no_inlines, true);
+}
+
+static int
+prog_config__force(const char *value,
+ struct perf_probe_event *pev __maybe_unused)
+{
+ return prog_config__bool(value, &probe_conf.force_add, false);
+}
+
+static struct {
+ const char *key;
+ const char *usage;
+ const char *desc;
+ int (*func)(const char *, struct perf_probe_event *);
+} bpf_prog_config_terms[] = {
+ {
+ .key = "exec",
+ .usage = "exec=<full path of file>",
+ .desc = "Set uprobe target",
+ .func = prog_config__exec,
+ },
+ {
+ .key = "module",
+ .usage = "module=<module name> ",
+ .desc = "Set kprobe module",
+ .func = prog_config__module,
+ },
+ {
+ .key = "inlines",
+ .usage = "inlines=[yes|no] ",
+ .desc = "Probe at inline symbol",
+ .func = prog_config__inlines,
+ },
+ {
+ .key = "force",
+ .usage = "force=[yes|no] ",
+ .desc = "Forcibly add events with existing name",
+ .func = prog_config__force,
+ },
+};
+
+static int
+do_prog_config(const char *key, const char *value,
+ struct perf_probe_event *pev)
+{
+ unsigned int i;
+
+ pr_debug("config bpf program: %s=%s\n", key, value);
+ for (i = 0; i < ARRAY_SIZE(bpf_prog_config_terms); i++)
+ if (strcmp(key, bpf_prog_config_terms[i].key) == 0)
+ return bpf_prog_config_terms[i].func(value, pev);
+
+ pr_debug("BPF: ERROR: invalid program config option: %s=%s\n",
+ key, value);
+
+ pr_debug("\nHint: Valid options are:\n");
+ for (i = 0; i < ARRAY_SIZE(bpf_prog_config_terms); i++)
+ pr_debug("\t%s:\t%s\n", bpf_prog_config_terms[i].usage,
+ bpf_prog_config_terms[i].desc);
+ pr_debug("\n");
+
+ return -BPF_LOADER_ERRNO__PROGCONF_TERM;
+}
+
+static const char *
+parse_prog_config_kvpair(const char *config_str, struct perf_probe_event *pev)
+{
+ char *text = strdup(config_str);
+ char *sep, *line;
+ const char *main_str = NULL;
+ int err = 0;
+
+ if (!text) {
+ pr_debug("No enough memory: dup config_str failed\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ line = text;
+ while ((sep = strchr(line, ';'))) {
+ char *equ;
+
+ *sep = '\0';
+ equ = strchr(line, '=');
+ if (!equ) {
+ pr_warning("WARNING: invalid config in BPF object: %s\n",
+ line);
+ pr_warning("\tShould be 'key=value'.\n");
+ goto nextline;
+ }
+ *equ = '\0';
+
+ err = do_prog_config(line, equ + 1, pev);
+ if (err)
+ break;
+nextline:
+ line = sep + 1;
+ }
+
+ if (!err)
+ main_str = config_str + (line - text);
+ free(text);
+
+ return err ? ERR_PTR(err) : main_str;
+}
+
+static int
+parse_prog_config(const char *config_str, struct perf_probe_event *pev)
+{
+ int err;
+ const char *main_str = parse_prog_config_kvpair(config_str, pev);
+
+ if (IS_ERR(main_str))
+ return PTR_ERR(main_str);
+
+ err = parse_perf_probe_command(main_str, pev);
+ if (err < 0) {
+ pr_debug("bpf: '%s' is not a valid config string\n",
+ config_str);
+ /* parse failed, don't need clear pev. */
+ return -BPF_LOADER_ERRNO__CONFIG;
+ }
+ return 0;
+}
+
+static int
config_bpf_program(struct bpf_program *prog)
{
struct perf_probe_event *pev = NULL;
@@ -117,6 +293,10 @@ config_bpf_program(struct bpf_program *prog)
const char *config_str;
int err;
+ /* Initialize per-program probing setting */
+ probe_conf.no_inlines = false;
+ probe_conf.force_add = false;
+
config_str = bpf_program__title(prog, false);
if (IS_ERR(config_str)) {
pr_debug("bpf: unable to get title for program\n");
@@ -131,13 +311,9 @@ config_bpf_program(struct bpf_program *prog)
pev = &priv->pev;
pr_debug("bpf: config program '%s'\n", config_str);
- err = parse_perf_probe_command(config_str, pev);
- if (err < 0) {
- pr_debug("bpf: '%s' is not a valid config string\n",
- config_str);
- err = -BPF_LOADER_ERRNO__CONFIG;
+ err = parse_prog_config(config_str, pev);
+ if (err)
goto errout;
- }
if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) {
pr_debug("bpf: '%s': group for event is set and not '%s'.\n",
@@ -197,6 +373,220 @@ static int bpf__prepare_probe(void)
return err;
}
+static int
+preproc_gen_prologue(struct bpf_program *prog, int n,
+ struct bpf_insn *orig_insns, int orig_insns_cnt,
+ struct bpf_prog_prep_result *res)
+{
+ struct probe_trace_event *tev;
+ struct perf_probe_event *pev;
+ struct bpf_prog_priv *priv;
+ struct bpf_insn *buf;
+ size_t prologue_cnt = 0;
+ int i, err;
+
+ err = bpf_program__get_private(prog, (void **)&priv);
+ if (err || !priv)
+ goto errout;
+
+ pev = &priv->pev;
+
+ if (n < 0 || n >= priv->nr_types)
+ goto errout;
+
+ /* Find a tev belongs to that type */
+ for (i = 0; i < pev->ntevs; i++) {
+ if (priv->type_mapping[i] == n)
+ break;
+ }
+
+ if (i >= pev->ntevs) {
+ pr_debug("Internal error: prologue type %d not found\n", n);
+ return -BPF_LOADER_ERRNO__PROLOGUE;
+ }
+
+ tev = &pev->tevs[i];
+
+ buf = priv->insns_buf;
+ err = bpf__gen_prologue(tev->args, tev->nargs,
+ buf, &prologue_cnt,
+ BPF_MAXINSNS - orig_insns_cnt);
+ if (err) {
+ const char *title;
+
+ title = bpf_program__title(prog, false);
+ if (!title)
+ title = "[unknown]";
+
+ pr_debug("Failed to generate prologue for program %s\n",
+ title);
+ return err;
+ }
+
+ memcpy(&buf[prologue_cnt], orig_insns,
+ sizeof(struct bpf_insn) * orig_insns_cnt);
+
+ res->new_insn_ptr = buf;
+ res->new_insn_cnt = prologue_cnt + orig_insns_cnt;
+ res->pfd = NULL;
+ return 0;
+
+errout:
+ pr_debug("Internal error in preproc_gen_prologue\n");
+ return -BPF_LOADER_ERRNO__PROLOGUE;
+}
+
+/*
+ * compare_tev_args is reflexive, transitive and antisymmetric.
+ * I can proof it but this margin is too narrow to contain.
+ */
+static int compare_tev_args(const void *ptev1, const void *ptev2)
+{
+ int i, ret;
+ const struct probe_trace_event *tev1 =
+ *(const struct probe_trace_event **)ptev1;
+ const struct probe_trace_event *tev2 =
+ *(const struct probe_trace_event **)ptev2;
+
+ ret = tev2->nargs - tev1->nargs;
+ if (ret)
+ return ret;
+
+ for (i = 0; i < tev1->nargs; i++) {
+ struct probe_trace_arg *arg1, *arg2;
+ struct probe_trace_arg_ref *ref1, *ref2;
+
+ arg1 = &tev1->args[i];
+ arg2 = &tev2->args[i];
+
+ ret = strcmp(arg1->value, arg2->value);
+ if (ret)
+ return ret;
+
+ ref1 = arg1->ref;
+ ref2 = arg2->ref;
+
+ while (ref1 && ref2) {
+ ret = ref2->offset - ref1->offset;
+ if (ret)
+ return ret;
+
+ ref1 = ref1->next;
+ ref2 = ref2->next;
+ }
+
+ if (ref1 || ref2)
+ return ref2 ? 1 : -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Assign a type number to each tevs in a pev.
+ * mapping is an array with same slots as tevs in that pev.
+ * nr_types will be set to number of types.
+ */
+static int map_prologue(struct perf_probe_event *pev, int *mapping,
+ int *nr_types)
+{
+ int i, type = 0;
+ struct probe_trace_event **ptevs;
+
+ size_t array_sz = sizeof(*ptevs) * pev->ntevs;
+
+ ptevs = malloc(array_sz);
+ if (!ptevs) {
+ pr_debug("No ehough memory: alloc ptevs failed\n");
+ return -ENOMEM;
+ }
+
+ pr_debug("In map_prologue, ntevs=%d\n", pev->ntevs);
+ for (i = 0; i < pev->ntevs; i++)
+ ptevs[i] = &pev->tevs[i];
+
+ qsort(ptevs, pev->ntevs, sizeof(*ptevs),
+ compare_tev_args);
+
+ for (i = 0; i < pev->ntevs; i++) {
+ int n;
+
+ n = ptevs[i] - pev->tevs;
+ if (i == 0) {
+ mapping[n] = type;
+ pr_debug("mapping[%d]=%d\n", n, type);
+ continue;
+ }
+
+ if (compare_tev_args(ptevs + i, ptevs + i - 1) == 0)
+ mapping[n] = type;
+ else
+ mapping[n] = ++type;
+
+ pr_debug("mapping[%d]=%d\n", n, mapping[n]);
+ }
+ free(ptevs);
+ *nr_types = type + 1;
+
+ return 0;
+}
+
+static int hook_load_preprocessor(struct bpf_program *prog)
+{
+ struct perf_probe_event *pev;
+ struct bpf_prog_priv *priv;
+ bool need_prologue = false;
+ int err, i;
+
+ err = bpf_program__get_private(prog, (void **)&priv);
+ if (err || !priv) {
+ pr_debug("Internal error when hook preprocessor\n");
+ return -BPF_LOADER_ERRNO__INTERNAL;
+ }
+
+ pev = &priv->pev;
+ for (i = 0; i < pev->ntevs; i++) {
+ struct probe_trace_event *tev = &pev->tevs[i];
+
+ if (tev->nargs > 0) {
+ need_prologue = true;
+ break;
+ }
+ }
+
+ /*
+ * Since all tevs don't have argument, we don't need generate
+ * prologue.
+ */
+ if (!need_prologue) {
+ priv->need_prologue = false;
+ return 0;
+ }
+
+ priv->need_prologue = true;
+ priv->insns_buf = malloc(sizeof(struct bpf_insn) * BPF_MAXINSNS);
+ if (!priv->insns_buf) {
+ pr_debug("No enough memory: alloc insns_buf failed\n");
+ return -ENOMEM;
+ }
+
+ priv->type_mapping = malloc(sizeof(int) * pev->ntevs);
+ if (!priv->type_mapping) {
+ pr_debug("No enough memory: alloc type_mapping failed\n");
+ return -ENOMEM;
+ }
+ memset(priv->type_mapping, -1,
+ sizeof(int) * pev->ntevs);
+
+ err = map_prologue(pev, priv->type_mapping, &priv->nr_types);
+ if (err)
+ return err;
+
+ err = bpf_program__set_prep(prog, priv->nr_types,
+ preproc_gen_prologue);
+ return err;
+}
+
int bpf__probe(struct bpf_object *obj)
{
int err = 0;
@@ -231,6 +621,18 @@ int bpf__probe(struct bpf_object *obj)
pr_debug("bpf_probe: failed to apply perf probe events");
goto out;
}
+
+ /*
+ * After probing, let's consider prologue, which
+ * adds program fetcher to BPF programs.
+ *
+ * hook_load_preprocessorr() hooks pre-processor
+ * to bpf_program, let it generate prologue
+ * dynamically during loading.
+ */
+ err = hook_load_preprocessor(prog);
+ if (err)
+ goto out;
}
out:
return err < 0 ? err : 0;
@@ -314,7 +716,14 @@ int bpf__foreach_tev(struct bpf_object *obj,
for (i = 0; i < pev->ntevs; i++) {
tev = &pev->tevs[i];
- fd = bpf_program__fd(prog);
+ if (priv->need_prologue) {
+ int type = priv->type_mapping[i];
+
+ fd = bpf_program__nth_fd(prog, type);
+ } else {
+ fd = bpf_program__fd(prog);
+ }
+
if (fd < 0) {
pr_debug("bpf: failed to get file descriptor\n");
return fd;
@@ -340,6 +749,10 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = {
[ERRCODE_OFFSET(EVENTNAME)] = "No event name found in config string",
[ERRCODE_OFFSET(INTERNAL)] = "BPF loader internal error",
[ERRCODE_OFFSET(COMPILE)] = "Error when compiling BPF scriptlet",
+ [ERRCODE_OFFSET(PROGCONF_TERM)] = "Invalid program config term in config string",
+ [ERRCODE_OFFSET(PROLOGUE)] = "Failed to generate prologue",
+ [ERRCODE_OFFSET(PROLOGUE2BIG)] = "Prologue too big for program",
+ [ERRCODE_OFFSET(PROLOGUEOOB)] = "Offset out of bound for prologue",
};
static int
@@ -420,7 +833,11 @@ int bpf__strerror_probe(struct bpf_object *obj __maybe_unused,
int err, char *buf, size_t size)
{
bpf__strerror_head(err, buf, size);
- bpf__strerror_entry(EEXIST, "Probe point exist. Try use 'perf probe -d \"*\"'");
+ case BPF_LOADER_ERRNO__PROGCONF_TERM: {
+ scnprintf(buf, size, "%s (add -v to see detail)", emsg);
+ break;
+ }
+ bpf__strerror_entry(EEXIST, "Probe point exist. Try 'perf probe -d \"*\"' and set 'force=yes'");
bpf__strerror_entry(EACCES, "You need to be root");
bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0");
bpf__strerror_entry(ENOENT, "You need to check probing points in BPF file");
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index 9caf3ae4acf3..6fdc0457e2b6 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -20,6 +20,10 @@ enum bpf_loader_errno {
BPF_LOADER_ERRNO__EVENTNAME, /* Event name is missing */
BPF_LOADER_ERRNO__INTERNAL, /* BPF loader internal error */
BPF_LOADER_ERRNO__COMPILE, /* Error when compiling BPF scriptlet */
+ BPF_LOADER_ERRNO__PROGCONF_TERM,/* Invalid program config term in config string */
+ BPF_LOADER_ERRNO__PROLOGUE, /* Failed to generate prologue */
+ BPF_LOADER_ERRNO__PROLOGUE2BIG, /* Prologue too big for program */
+ BPF_LOADER_ERRNO__PROLOGUEOOB, /* Offset out of bound for prologue */
__BPF_LOADER_ERRNO__END,
};
diff --git a/tools/perf/util/bpf-prologue.c b/tools/perf/util/bpf-prologue.c
new file mode 100644
index 000000000000..6cdbee119ceb
--- /dev/null
+++ b/tools/perf/util/bpf-prologue.c
@@ -0,0 +1,455 @@
+/*
+ * bpf-prologue.c
+ *
+ * Copyright (C) 2015 He Kuang <hekuang@huawei.com>
+ * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
+ * Copyright (C) 2015 Huawei Inc.
+ */
+
+#include <bpf/libbpf.h>
+#include "perf.h"
+#include "debug.h"
+#include "bpf-loader.h"
+#include "bpf-prologue.h"
+#include "probe-finder.h"
+#include <dwarf-regs.h>
+#include <linux/filter.h>
+
+#define BPF_REG_SIZE 8
+
+#define JMP_TO_ERROR_CODE -1
+#define JMP_TO_SUCCESS_CODE -2
+#define JMP_TO_USER_CODE -3
+
+struct bpf_insn_pos {
+ struct bpf_insn *begin;
+ struct bpf_insn *end;
+ struct bpf_insn *pos;
+};
+
+static inline int
+pos_get_cnt(struct bpf_insn_pos *pos)
+{
+ return pos->pos - pos->begin;
+}
+
+static int
+append_insn(struct bpf_insn new_insn, struct bpf_insn_pos *pos)
+{
+ if (!pos->pos)
+ return -BPF_LOADER_ERRNO__PROLOGUE2BIG;
+
+ if (pos->pos + 1 >= pos->end) {
+ pr_err("bpf prologue: prologue too long\n");
+ pos->pos = NULL;
+ return -BPF_LOADER_ERRNO__PROLOGUE2BIG;
+ }
+
+ *(pos->pos)++ = new_insn;
+ return 0;
+}
+
+static int
+check_pos(struct bpf_insn_pos *pos)
+{
+ if (!pos->pos || pos->pos >= pos->end)
+ return -BPF_LOADER_ERRNO__PROLOGUE2BIG;
+ return 0;
+}
+
+/* Give it a shorter name */
+#define ins(i, p) append_insn((i), (p))
+
+/*
+ * Give a register name (in 'reg'), generate instruction to
+ * load register into an eBPF register rd:
+ * 'ldd target_reg, offset(ctx_reg)', where:
+ * ctx_reg is pre initialized to pointer of 'struct pt_regs'.
+ */
+static int
+gen_ldx_reg_from_ctx(struct bpf_insn_pos *pos, int ctx_reg,
+ const char *reg, int target_reg)
+{
+ int offset = regs_query_register_offset(reg);
+
+ if (offset < 0) {
+ pr_err("bpf: prologue: failed to get register %s\n",
+ reg);
+ return offset;
+ }
+ ins(BPF_LDX_MEM(BPF_DW, target_reg, ctx_reg, offset), pos);
+
+ return check_pos(pos);
+}
+
+/*
+ * Generate a BPF_FUNC_probe_read function call.
+ *
+ * src_base_addr_reg is a register holding base address,
+ * dst_addr_reg is a register holding dest address (on stack),
+ * result is:
+ *
+ * *[dst_addr_reg] = *([src_base_addr_reg] + offset)
+ *
+ * Arguments of BPF_FUNC_probe_read:
+ * ARG1: ptr to stack (dest)
+ * ARG2: size (8)
+ * ARG3: unsafe ptr (src)
+ */
+static int
+gen_read_mem(struct bpf_insn_pos *pos,
+ int src_base_addr_reg,
+ int dst_addr_reg,
+ long offset)
+{
+ /* mov arg3, src_base_addr_reg */
+ if (src_base_addr_reg != BPF_REG_ARG3)
+ ins(BPF_MOV64_REG(BPF_REG_ARG3, src_base_addr_reg), pos);
+ /* add arg3, #offset */
+ if (offset)
+ ins(BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, offset), pos);
+
+ /* mov arg2, #reg_size */
+ ins(BPF_ALU64_IMM(BPF_MOV, BPF_REG_ARG2, BPF_REG_SIZE), pos);
+
+ /* mov arg1, dst_addr_reg */
+ if (dst_addr_reg != BPF_REG_ARG1)
+ ins(BPF_MOV64_REG(BPF_REG_ARG1, dst_addr_reg), pos);
+
+ /* Call probe_read */
+ ins(BPF_EMIT_CALL(BPF_FUNC_probe_read), pos);
+ /*
+ * Error processing: if read fail, goto error code,
+ * will be relocated. Target should be the start of
+ * error processing code.
+ */
+ ins(BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, JMP_TO_ERROR_CODE),
+ pos);
+
+ return check_pos(pos);
+}
+
+/*
+ * Each arg should be bare register. Fetch and save them into argument
+ * registers (r3 - r5).
+ *
+ * BPF_REG_1 should have been initialized with pointer to
+ * 'struct pt_regs'.
+ */
+static int
+gen_prologue_fastpath(struct bpf_insn_pos *pos,
+ struct probe_trace_arg *args, int nargs)
+{
+ int i, err = 0;
+
+ for (i = 0; i < nargs; i++) {
+ err = gen_ldx_reg_from_ctx(pos, BPF_REG_1, args[i].value,
+ BPF_PROLOGUE_START_ARG_REG + i);
+ if (err)
+ goto errout;
+ }
+
+ return check_pos(pos);
+errout:
+ return err;
+}
+
+/*
+ * Slow path:
+ * At least one argument has the form of 'offset($rx)'.
+ *
+ * Following code first stores them into stack, then loads all of then
+ * to r2 - r5.
+ * Before final loading, the final result should be:
+ *
+ * low address
+ * BPF_REG_FP - 24 ARG3
+ * BPF_REG_FP - 16 ARG2
+ * BPF_REG_FP - 8 ARG1
+ * BPF_REG_FP
+ * high address
+ *
+ * For each argument (described as: offn(...off2(off1(reg)))),
+ * generates following code:
+ *
+ * r7 <- fp
+ * r7 <- r7 - stack_offset // Ideal code should initialize r7 using
+ * // fp before generating args. However,
+ * // eBPF won't regard r7 as stack pointer
+ * // if it is generated by minus 8 from
+ * // another stack pointer except fp.
+ * // This is why we have to set r7
+ * // to fp for each variable.
+ * r3 <- value of 'reg'-> generated using gen_ldx_reg_from_ctx()
+ * (r7) <- r3 // skip following instructions for bare reg
+ * r3 <- r3 + off1 . // skip if off1 == 0
+ * r2 <- 8 \
+ * r1 <- r7 |-> generated by gen_read_mem()
+ * call probe_read /
+ * jnei r0, 0, err ./
+ * r3 <- (r7)
+ * r3 <- r3 + off2 . // skip if off2 == 0
+ * r2 <- 8 \ // r2 may be broken by probe_read, so set again
+ * r1 <- r7 |-> generated by gen_read_mem()
+ * call probe_read /
+ * jnei r0, 0, err ./
+ * ...
+ */
+static int
+gen_prologue_slowpath(struct bpf_insn_pos *pos,
+ struct probe_trace_arg *args, int nargs)
+{
+ int err, i;
+
+ for (i = 0; i < nargs; i++) {
+ struct probe_trace_arg *arg = &args[i];
+ const char *reg = arg->value;
+ struct probe_trace_arg_ref *ref = NULL;
+ int stack_offset = (i + 1) * -8;
+
+ pr_debug("prologue: fetch arg %d, base reg is %s\n",
+ i, reg);
+
+ /* value of base register is stored into ARG3 */
+ err = gen_ldx_reg_from_ctx(pos, BPF_REG_CTX, reg,
+ BPF_REG_ARG3);
+ if (err) {
+ pr_err("prologue: failed to get offset of register %s\n",
+ reg);
+ goto errout;
+ }
+
+ /* Make r7 the stack pointer. */
+ ins(BPF_MOV64_REG(BPF_REG_7, BPF_REG_FP), pos);
+ /* r7 += -8 */
+ ins(BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, stack_offset), pos);
+ /*
+ * Store r3 (base register) onto stack
+ * Ensure fp[offset] is set.
+ * fp is the only valid base register when storing
+ * into stack. We are not allowed to use r7 as base
+ * register here.
+ */
+ ins(BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG3,
+ stack_offset), pos);
+
+ ref = arg->ref;
+ while (ref) {
+ pr_debug("prologue: arg %d: offset %ld\n",
+ i, ref->offset);
+ err = gen_read_mem(pos, BPF_REG_3, BPF_REG_7,
+ ref->offset);
+ if (err) {
+ pr_err("prologue: failed to generate probe_read function call\n");
+ goto errout;
+ }
+
+ ref = ref->next;
+ /*
+ * Load previous result into ARG3. Use
+ * BPF_REG_FP instead of r7 because verifier
+ * allows FP based addressing only.
+ */
+ if (ref)
+ ins(BPF_LDX_MEM(BPF_DW, BPF_REG_ARG3,
+ BPF_REG_FP, stack_offset), pos);
+ }
+ }
+
+ /* Final pass: read to registers */
+ for (i = 0; i < nargs; i++)
+ ins(BPF_LDX_MEM(BPF_DW, BPF_PROLOGUE_START_ARG_REG + i,
+ BPF_REG_FP, -BPF_REG_SIZE * (i + 1)), pos);
+
+ ins(BPF_JMP_IMM(BPF_JA, BPF_REG_0, 0, JMP_TO_SUCCESS_CODE), pos);
+
+ return check_pos(pos);
+errout:
+ return err;
+}
+
+static int
+prologue_relocate(struct bpf_insn_pos *pos, struct bpf_insn *error_code,
+ struct bpf_insn *success_code, struct bpf_insn *user_code)
+{
+ struct bpf_insn *insn;
+
+ if (check_pos(pos))
+ return -BPF_LOADER_ERRNO__PROLOGUE2BIG;
+
+ for (insn = pos->begin; insn < pos->pos; insn++) {
+ struct bpf_insn *target;
+ u8 class = BPF_CLASS(insn->code);
+ u8 opcode;
+
+ if (class != BPF_JMP)
+ continue;
+ opcode = BPF_OP(insn->code);
+ if (opcode == BPF_CALL)
+ continue;
+
+ switch (insn->off) {
+ case JMP_TO_ERROR_CODE:
+ target = error_code;
+ break;
+ case JMP_TO_SUCCESS_CODE:
+ target = success_code;
+ break;
+ case JMP_TO_USER_CODE:
+ target = user_code;
+ break;
+ default:
+ pr_err("bpf prologue: internal error: relocation failed\n");
+ return -BPF_LOADER_ERRNO__PROLOGUE;
+ }
+
+ insn->off = target - (insn + 1);
+ }
+ return 0;
+}
+
+int bpf__gen_prologue(struct probe_trace_arg *args, int nargs,
+ struct bpf_insn *new_prog, size_t *new_cnt,
+ size_t cnt_space)
+{
+ struct bpf_insn *success_code = NULL;
+ struct bpf_insn *error_code = NULL;
+ struct bpf_insn *user_code = NULL;
+ struct bpf_insn_pos pos;
+ bool fastpath = true;
+ int err = 0, i;
+
+ if (!new_prog || !new_cnt)
+ return -EINVAL;
+
+ if (cnt_space > BPF_MAXINSNS)
+ cnt_space = BPF_MAXINSNS;
+
+ pos.begin = new_prog;
+ pos.end = new_prog + cnt_space;
+ pos.pos = new_prog;
+
+ if (!nargs) {
+ ins(BPF_ALU64_IMM(BPF_MOV, BPF_PROLOGUE_FETCH_RESULT_REG, 0),
+ &pos);
+
+ if (check_pos(&pos))
+ goto errout;
+
+ *new_cnt = pos_get_cnt(&pos);
+ return 0;
+ }
+
+ if (nargs > BPF_PROLOGUE_MAX_ARGS) {
+ pr_warning("bpf: prologue: %d arguments are dropped\n",
+ nargs - BPF_PROLOGUE_MAX_ARGS);
+ nargs = BPF_PROLOGUE_MAX_ARGS;
+ }
+
+ /* First pass: validation */
+ for (i = 0; i < nargs; i++) {
+ struct probe_trace_arg_ref *ref = args[i].ref;
+
+ if (args[i].value[0] == '@') {
+ /* TODO: fetch global variable */
+ pr_err("bpf: prologue: global %s%+ld not support\n",
+ args[i].value, ref ? ref->offset : 0);
+ return -ENOTSUP;
+ }
+
+ while (ref) {
+ /* fastpath is true if all args has ref == NULL */
+ fastpath = false;
+
+ /*
+ * Instruction encodes immediate value using
+ * s32, ref->offset is long. On systems which
+ * can't fill long in s32, refuse to process if
+ * ref->offset too large (or small).
+ */
+#ifdef __LP64__
+#define OFFSET_MAX ((1LL << 31) - 1)
+#define OFFSET_MIN ((1LL << 31) * -1)
+ if (ref->offset > OFFSET_MAX ||
+ ref->offset < OFFSET_MIN) {
+ pr_err("bpf: prologue: offset out of bound: %ld\n",
+ ref->offset);
+ return -BPF_LOADER_ERRNO__PROLOGUEOOB;
+ }
+#endif
+ ref = ref->next;
+ }
+ }
+ pr_debug("prologue: pass validation\n");
+
+ if (fastpath) {
+ /* If all variables are registers... */
+ pr_debug("prologue: fast path\n");
+ err = gen_prologue_fastpath(&pos, args, nargs);
+ if (err)
+ goto errout;
+ } else {
+ pr_debug("prologue: slow path\n");
+
+ /* Initialization: move ctx to a callee saved register. */
+ ins(BPF_MOV64_REG(BPF_REG_CTX, BPF_REG_ARG1), &pos);
+
+ err = gen_prologue_slowpath(&pos, args, nargs);
+ if (err)
+ goto errout;
+ /*
+ * start of ERROR_CODE (only slow pass needs error code)
+ * mov r2 <- 1 // r2 is error number
+ * mov r3 <- 0 // r3, r4... should be touched or
+ * // verifier would complain
+ * mov r4 <- 0
+ * ...
+ * goto usercode
+ */
+ error_code = pos.pos;
+ ins(BPF_ALU64_IMM(BPF_MOV, BPF_PROLOGUE_FETCH_RESULT_REG, 1),
+ &pos);
+
+ for (i = 0; i < nargs; i++)
+ ins(BPF_ALU64_IMM(BPF_MOV,
+ BPF_PROLOGUE_START_ARG_REG + i,
+ 0),
+ &pos);
+ ins(BPF_JMP_IMM(BPF_JA, BPF_REG_0, 0, JMP_TO_USER_CODE),
+ &pos);
+ }
+
+ /*
+ * start of SUCCESS_CODE:
+ * mov r2 <- 0
+ * goto usercode // skip
+ */
+ success_code = pos.pos;
+ ins(BPF_ALU64_IMM(BPF_MOV, BPF_PROLOGUE_FETCH_RESULT_REG, 0), &pos);
+
+ /*
+ * start of USER_CODE:
+ * Restore ctx to r1
+ */
+ user_code = pos.pos;
+ if (!fastpath) {
+ /*
+ * Only slow path needs restoring of ctx. In fast path,
+ * register are loaded directly from r1.
+ */
+ ins(BPF_MOV64_REG(BPF_REG_ARG1, BPF_REG_CTX), &pos);
+ err = prologue_relocate(&pos, error_code, success_code,
+ user_code);
+ if (err)
+ goto errout;
+ }
+
+ err = check_pos(&pos);
+ if (err)
+ goto errout;
+
+ *new_cnt = pos_get_cnt(&pos);
+ return 0;
+errout:
+ return err;
+}
diff --git a/tools/perf/util/bpf-prologue.h b/tools/perf/util/bpf-prologue.h
new file mode 100644
index 000000000000..d94cbea12899
--- /dev/null
+++ b/tools/perf/util/bpf-prologue.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015, He Kuang <hekuang@huawei.com>
+ * Copyright (C) 2015, Huawei Inc.
+ */
+#ifndef __BPF_PROLOGUE_H
+#define __BPF_PROLOGUE_H
+
+#include <linux/compiler.h>
+#include <linux/filter.h>
+#include "probe-event.h"
+
+#define BPF_PROLOGUE_MAX_ARGS 3
+#define BPF_PROLOGUE_START_ARG_REG BPF_REG_3
+#define BPF_PROLOGUE_FETCH_RESULT_REG BPF_REG_2
+
+#ifdef HAVE_BPF_PROLOGUE
+int bpf__gen_prologue(struct probe_trace_arg *args, int nargs,
+ struct bpf_insn *new_prog, size_t *new_cnt,
+ size_t cnt_space);
+#else
+static inline int
+bpf__gen_prologue(struct probe_trace_arg *args __maybe_unused,
+ int nargs __maybe_unused,
+ struct bpf_insn *new_prog __maybe_unused,
+ size_t *new_cnt,
+ size_t cnt_space __maybe_unused)
+{
+ if (!new_cnt)
+ return -EINVAL;
+ *new_cnt = 0;
+ return -ENOTSUP;
+}
+#endif
+#endif /* __BPF_PROLOGUE_H */
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index 217b5a60e2ab..6a7e273a514a 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -91,7 +91,7 @@ int build_id__sprintf(const u8 *build_id, int len, char *bf)
bid += 2;
}
- return raw - build_id;
+ return (bid - bf) + 1;
}
int sysfs__sprintf_build_id(const char *root_dir, char *sbuild_id)
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h
index c861373aaed3..07b5d63947b1 100644
--- a/tools/perf/util/cache.h
+++ b/tools/perf/util/cache.h
@@ -4,9 +4,12 @@
#include <stdbool.h>
#include "util.h"
#include "strbuf.h"
+#include <subcmd/pager.h>
#include "../perf.h"
#include "../ui/ui.h"
+#include <linux/string.h>
+
#define CMD_EXEC_PATH "--exec-path"
#define CMD_PERF_DIR "--perf-dir="
#define CMD_WORK_TREE "--work-tree="
@@ -18,6 +21,7 @@
#define DEFAULT_PERF_DIR_ENVIRONMENT ".perf"
#define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR"
#define PERF_TRACEFS_ENVIRONMENT "PERF_TRACEFS_DIR"
+#define PERF_PAGER_ENVIRONMENT "PERF_PAGER"
typedef int (*config_fn_t)(const char *, const char *, void *);
extern int perf_default_config(const char *, const char *, void *);
@@ -28,11 +32,6 @@ extern int perf_config_bool(const char *, const char *);
extern int config_error_nonbool(const char *);
extern const char *perf_config_dirname(const char *, const char *);
-/* pager.c */
-extern void setup_pager(void);
-extern int pager_in_use(void);
-extern int pager_use_color;
-
char *alias_lookup(const char *alias);
int split_cmdline(char *cmdline, const char ***argv);
@@ -71,9 +70,4 @@ extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2
extern char *perf_pathdup(const char *fmt, ...)
__attribute__((format (printf, 1, 2)));
-#ifndef __UCLIBC__
-/* Matches the libc/libbsd function attribute so we declare this unconditionally: */
-extern size_t strlcpy(char *dest, const char *src, size_t size);
-#endif
-
#endif /* __PERF_CACHE_H */
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 735ad48e1858..53c43eb9489e 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -44,6 +44,10 @@ static int parse_callchain_mode(const char *value)
callchain_param.mode = CHAIN_GRAPH_REL;
return 0;
}
+ if (!strncmp(value, "folded", strlen(value))) {
+ callchain_param.mode = CHAIN_FOLDED;
+ return 0;
+ }
return -1;
}
@@ -79,6 +83,23 @@ static int parse_callchain_sort_key(const char *value)
return -1;
}
+static int parse_callchain_value(const char *value)
+{
+ if (!strncmp(value, "percent", strlen(value))) {
+ callchain_param.value = CCVAL_PERCENT;
+ return 0;
+ }
+ if (!strncmp(value, "period", strlen(value))) {
+ callchain_param.value = CCVAL_PERIOD;
+ return 0;
+ }
+ if (!strncmp(value, "count", strlen(value))) {
+ callchain_param.value = CCVAL_COUNT;
+ return 0;
+ }
+ return -1;
+}
+
static int
__parse_callchain_report_opt(const char *arg, bool allow_record_opt)
{
@@ -102,7 +123,8 @@ __parse_callchain_report_opt(const char *arg, bool allow_record_opt)
if (!parse_callchain_mode(tok) ||
!parse_callchain_order(tok) ||
- !parse_callchain_sort_key(tok)) {
+ !parse_callchain_sort_key(tok) ||
+ !parse_callchain_value(tok)) {
/* parsing ok - move on to the next */
try_stack_size = false;
goto next;
@@ -218,6 +240,7 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
switch (mode) {
case CHAIN_FLAT:
+ case CHAIN_FOLDED:
if (rnode->hit < chain->hit)
p = &(*p)->rb_left;
else
@@ -267,6 +290,7 @@ static void
sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root,
u64 min_hit, struct callchain_param *param __maybe_unused)
{
+ *rb_root = RB_ROOT;
__sort_chain_flat(rb_root, &root->node, min_hit);
}
@@ -338,6 +362,7 @@ int callchain_register_param(struct callchain_param *param)
param->sort = sort_chain_graph_rel;
break;
case CHAIN_FLAT:
+ case CHAIN_FOLDED:
param->sort = sort_chain_flat;
break;
case CHAIN_NONE:
@@ -363,6 +388,7 @@ create_child(struct callchain_node *parent, bool inherit_children)
}
new->parent = parent;
INIT_LIST_HEAD(&new->val);
+ INIT_LIST_HEAD(&new->parent_val);
if (inherit_children) {
struct rb_node *n;
@@ -431,6 +457,8 @@ add_child(struct callchain_node *parent,
new->children_hit = 0;
new->hit = period;
+ new->children_count = 0;
+ new->count = 1;
return new;
}
@@ -478,6 +506,9 @@ split_add_child(struct callchain_node *parent,
parent->children_hit = callchain_cumul_hits(new);
new->val_nr = parent->val_nr - idx_local;
parent->val_nr = idx_local;
+ new->count = parent->count;
+ new->children_count = parent->children_count;
+ parent->children_count = callchain_cumul_counts(new);
/* create a new child for the new branch if any */
if (idx_total < cursor->nr) {
@@ -488,6 +519,8 @@ split_add_child(struct callchain_node *parent,
parent->hit = 0;
parent->children_hit += period;
+ parent->count = 0;
+ parent->children_count += 1;
node = callchain_cursor_current(cursor);
new = add_child(parent, cursor, period);
@@ -510,6 +543,7 @@ split_add_child(struct callchain_node *parent,
rb_insert_color(&new->rb_node_in, &parent->rb_root_in);
} else {
parent->hit = period;
+ parent->count = 1;
}
}
@@ -556,6 +590,7 @@ append_chain_children(struct callchain_node *root,
inc_children_hit:
root->children_hit += period;
+ root->children_count++;
}
static int
@@ -608,6 +643,7 @@ append_chain(struct callchain_node *root,
/* we match 100% of the path, increment the hit */
if (matches == root->val_nr && cursor->pos == cursor->nr) {
root->hit += period;
+ root->count++;
return 0;
}
@@ -799,12 +835,72 @@ char *callchain_list__sym_name(struct callchain_list *cl,
return bf;
}
+char *callchain_node__scnprintf_value(struct callchain_node *node,
+ char *bf, size_t bfsize, u64 total)
+{
+ double percent = 0.0;
+ u64 period = callchain_cumul_hits(node);
+ unsigned count = callchain_cumul_counts(node);
+
+ if (callchain_param.mode == CHAIN_FOLDED) {
+ period = node->hit;
+ count = node->count;
+ }
+
+ switch (callchain_param.value) {
+ case CCVAL_PERIOD:
+ scnprintf(bf, bfsize, "%"PRIu64, period);
+ break;
+ case CCVAL_COUNT:
+ scnprintf(bf, bfsize, "%u", count);
+ break;
+ case CCVAL_PERCENT:
+ default:
+ if (total)
+ percent = period * 100.0 / total;
+ scnprintf(bf, bfsize, "%.2f%%", percent);
+ break;
+ }
+ return bf;
+}
+
+int callchain_node__fprintf_value(struct callchain_node *node,
+ FILE *fp, u64 total)
+{
+ double percent = 0.0;
+ u64 period = callchain_cumul_hits(node);
+ unsigned count = callchain_cumul_counts(node);
+
+ if (callchain_param.mode == CHAIN_FOLDED) {
+ period = node->hit;
+ count = node->count;
+ }
+
+ switch (callchain_param.value) {
+ case CCVAL_PERIOD:
+ return fprintf(fp, "%"PRIu64, period);
+ case CCVAL_COUNT:
+ return fprintf(fp, "%u", count);
+ case CCVAL_PERCENT:
+ default:
+ if (total)
+ percent = period * 100.0 / total;
+ return percent_color_fprintf(fp, "%.2f%%", percent);
+ }
+ return 0;
+}
+
static void free_callchain_node(struct callchain_node *node)
{
struct callchain_list *list, *tmp;
struct callchain_node *child;
struct rb_node *n;
+ list_for_each_entry_safe(list, tmp, &node->parent_val, list) {
+ list_del(&list->list);
+ free(list);
+ }
+
list_for_each_entry_safe(list, tmp, &node->val, list) {
list_del(&list->list);
free(list);
@@ -828,3 +924,69 @@ void free_callchain(struct callchain_root *root)
free_callchain_node(&root->node);
}
+
+static u64 decay_callchain_node(struct callchain_node *node)
+{
+ struct callchain_node *child;
+ struct rb_node *n;
+ u64 child_hits = 0;
+
+ n = rb_first(&node->rb_root_in);
+ while (n) {
+ child = container_of(n, struct callchain_node, rb_node_in);
+
+ child_hits += decay_callchain_node(child);
+ n = rb_next(n);
+ }
+
+ node->hit = (node->hit * 7) / 8;
+ node->children_hit = child_hits;
+
+ return node->hit;
+}
+
+void decay_callchain(struct callchain_root *root)
+{
+ if (!symbol_conf.use_callchain)
+ return;
+
+ decay_callchain_node(&root->node);
+}
+
+int callchain_node__make_parent_list(struct callchain_node *node)
+{
+ struct callchain_node *parent = node->parent;
+ struct callchain_list *chain, *new;
+ LIST_HEAD(head);
+
+ while (parent) {
+ list_for_each_entry_reverse(chain, &parent->val, list) {
+ new = malloc(sizeof(*new));
+ if (new == NULL)
+ goto out;
+ *new = *chain;
+ new->has_children = false;
+ list_add_tail(&new->list, &head);
+ }
+ parent = parent->parent;
+ }
+
+ list_for_each_entry_safe_reverse(chain, new, &head, list)
+ list_move_tail(&chain->list, &node->parent_val);
+
+ if (!list_empty(&node->parent_val)) {
+ chain = list_first_entry(&node->parent_val, struct callchain_list, list);
+ chain->has_children = rb_prev(&node->rb_node) || rb_next(&node->rb_node);
+
+ chain = list_first_entry(&node->val, struct callchain_list, list);
+ chain->has_children = false;
+ }
+ return 0;
+
+out:
+ list_for_each_entry_safe(chain, new, &head, list) {
+ list_del(&chain->list);
+ free(chain);
+ }
+ return -ENOMEM;
+}
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index fce8161e54db..18dd22269764 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -24,12 +24,13 @@
#define CALLCHAIN_RECORD_HELP CALLCHAIN_HELP RECORD_MODE_HELP RECORD_SIZE_HELP
#define CALLCHAIN_REPORT_HELP \
- HELP_PAD "print_type:\tcall graph printing style (graph|flat|fractal|none)\n" \
+ HELP_PAD "print_type:\tcall graph printing style (graph|flat|fractal|folded|none)\n" \
HELP_PAD "threshold:\tminimum call graph inclusion threshold (<percent>)\n" \
HELP_PAD "print_limit:\tmaximum number of call graph entry (<number>)\n" \
HELP_PAD "order:\t\tcall graph order (caller|callee)\n" \
HELP_PAD "sort_key:\tcall graph sort key (function|address)\n" \
- HELP_PAD "branch:\t\tinclude last branch info to call graph (branch)\n"
+ HELP_PAD "branch:\t\tinclude last branch info to call graph (branch)\n" \
+ HELP_PAD "value:\t\tcall graph value (percent|period|count)\n"
enum perf_call_graph_mode {
CALLCHAIN_NONE,
@@ -43,7 +44,8 @@ enum chain_mode {
CHAIN_NONE,
CHAIN_FLAT,
CHAIN_GRAPH_ABS,
- CHAIN_GRAPH_REL
+ CHAIN_GRAPH_REL,
+ CHAIN_FOLDED,
};
enum chain_order {
@@ -54,11 +56,14 @@ enum chain_order {
struct callchain_node {
struct callchain_node *parent;
struct list_head val;
+ struct list_head parent_val;
struct rb_node rb_node_in; /* to insert nodes in an rbtree */
struct rb_node rb_node; /* to sort nodes in an output tree */
struct rb_root rb_root_in; /* input tree of children */
struct rb_root rb_root; /* sorted output tree of children */
unsigned int val_nr;
+ unsigned int count;
+ unsigned int children_count;
u64 hit;
u64 children_hit;
};
@@ -78,6 +83,12 @@ enum chain_key {
CCKEY_ADDRESS
};
+enum chain_value {
+ CCVAL_PERCENT,
+ CCVAL_PERIOD,
+ CCVAL_COUNT,
+};
+
struct callchain_param {
bool enabled;
enum perf_call_graph_mode record_mode;
@@ -90,6 +101,7 @@ struct callchain_param {
bool order_set;
enum chain_key key;
bool branch_callstack;
+ enum chain_value value;
};
extern struct callchain_param callchain_param;
@@ -131,6 +143,7 @@ extern __thread struct callchain_cursor callchain_cursor;
static inline void callchain_init(struct callchain_root *root)
{
INIT_LIST_HEAD(&root->node.val);
+ INIT_LIST_HEAD(&root->node.parent_val);
root->node.parent = NULL;
root->node.hit = 0;
@@ -144,6 +157,11 @@ static inline u64 callchain_cumul_hits(struct callchain_node *node)
return node->hit + node->children_hit;
}
+static inline unsigned callchain_cumul_counts(struct callchain_node *node)
+{
+ return node->count + node->children_count;
+}
+
int callchain_register_param(struct callchain_param *param);
int callchain_append(struct callchain_root *root,
struct callchain_cursor *cursor,
@@ -229,7 +247,13 @@ static inline int arch_skip_callchain_idx(struct thread *thread __maybe_unused,
char *callchain_list__sym_name(struct callchain_list *cl,
char *bf, size_t bfsize, bool show_dso);
+char *callchain_node__scnprintf_value(struct callchain_node *node,
+ char *bf, size_t bfsize, u64 total);
+int callchain_node__fprintf_value(struct callchain_node *node,
+ FILE *fp, u64 total);
void free_callchain(struct callchain_root *root);
+void decay_callchain(struct callchain_root *root);
+int callchain_node__make_parent_list(struct callchain_node *node);
#endif /* __PERF_CALLCHAIN_H */
diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c
index 32e12ecfe9c5..90aa1b46b2e5 100644
--- a/tools/perf/util/cgroup.c
+++ b/tools/perf/util/cgroup.c
@@ -1,6 +1,6 @@
#include "util.h"
#include "../perf.h"
-#include "parse-options.h"
+#include <subcmd/parse-options.h>
#include "evsel.h"
#include "cgroup.h"
#include "evlist.h"
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c
index 9b9565416f90..e5fb88bab9e1 100644
--- a/tools/perf/util/color.c
+++ b/tools/perf/util/color.c
@@ -24,7 +24,7 @@ int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty)
auto_color:
if (stdout_is_tty < 0)
stdout_is_tty = isatty(1);
- if (stdout_is_tty || (pager_in_use() && pager_use_color)) {
+ if (stdout_is_tty || pager_in_use()) {
char *term = getenv("TERM");
if (term && strcmp(term, "dumb"))
return 1;
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index 2e452ac1353d..d3e12e30e1d5 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -10,7 +10,7 @@
*/
#include "util.h"
#include "cache.h"
-#include "exec_cmd.h"
+#include <subcmd/exec-cmd.h>
#include "util/hist.h" /* perf_hist_config */
#include "util/llvm-utils.h" /* perf_llvm_config */
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 10af1e7524fb..fa935093a599 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -5,6 +5,7 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
+#include <linux/bitmap.h>
#include "asm/bug.h"
static struct cpu_map *cpu_map__default_new(void)
@@ -179,6 +180,56 @@ out:
return cpus;
}
+static struct cpu_map *cpu_map__from_entries(struct cpu_map_entries *cpus)
+{
+ struct cpu_map *map;
+
+ map = cpu_map__empty_new(cpus->nr);
+ if (map) {
+ unsigned i;
+
+ for (i = 0; i < cpus->nr; i++) {
+ /*
+ * Special treatment for -1, which is not real cpu number,
+ * and we need to use (int) -1 to initialize map[i],
+ * otherwise it would become 65535.
+ */
+ if (cpus->cpu[i] == (u16) -1)
+ map->map[i] = -1;
+ else
+ map->map[i] = (int) cpus->cpu[i];
+ }
+ }
+
+ return map;
+}
+
+static struct cpu_map *cpu_map__from_mask(struct cpu_map_mask *mask)
+{
+ struct cpu_map *map;
+ int nr, nbits = mask->nr * mask->long_size * BITS_PER_BYTE;
+
+ nr = bitmap_weight(mask->mask, nbits);
+
+ map = cpu_map__empty_new(nr);
+ if (map) {
+ int cpu, i = 0;
+
+ for_each_set_bit(cpu, mask->mask, nbits)
+ map->map[i++] = cpu;
+ }
+ return map;
+
+}
+
+struct cpu_map *cpu_map__new_data(struct cpu_map_data *data)
+{
+ if (data->type == PERF_CPU_MAP__CPUS)
+ return cpu_map__from_entries((struct cpu_map_entries *)data->data);
+ else
+ return cpu_map__from_mask((struct cpu_map_mask *)data->data);
+}
+
size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp)
{
int i;
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index 85f7772457fa..71c41b9efabb 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -17,6 +17,7 @@ struct cpu_map {
struct cpu_map *cpu_map__new(const char *cpu_list);
struct cpu_map *cpu_map__empty_new(int nr);
struct cpu_map *cpu_map__dummy_new(void);
+struct cpu_map *cpu_map__new_data(struct cpu_map_data *data);
struct cpu_map *cpu_map__read(FILE *file);
size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp);
int cpu_map__get_socket_id(int cpu);
diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c
index 5bfc1198ab46..34cd1e4039d3 100644
--- a/tools/perf/util/data-convert-bt.c
+++ b/tools/perf/util/data-convert-bt.c
@@ -63,6 +63,7 @@ struct ctf_writer {
struct bt_ctf_field_type *s32;
struct bt_ctf_field_type *u32;
struct bt_ctf_field_type *string;
+ struct bt_ctf_field_type *u32_hex;
struct bt_ctf_field_type *u64_hex;
};
struct bt_ctf_field_type *array[6];
@@ -982,6 +983,7 @@ do { \
CREATE_INT_TYPE(cw->data.u64, 64, false, false);
CREATE_INT_TYPE(cw->data.s32, 32, true, false);
CREATE_INT_TYPE(cw->data.u32, 32, false, false);
+ CREATE_INT_TYPE(cw->data.u32_hex, 32, false, true);
CREATE_INT_TYPE(cw->data.u64_hex, 64, false, true);
cw->data.string = bt_ctf_field_type_string_create();
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 425df5c86c9c..e8e9a9dbf5e3 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1243,6 +1243,8 @@ struct dso *__dsos__addnew(struct dsos *dsos, const char *name)
if (dso != NULL) {
__dsos__add(dsos, dso);
dso__set_basename(dso);
+ /* Put dso here because __dsos_add already got it */
+ dso__put(dso);
}
return dso;
}
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index 6af4f7c36820..7dd5939dea2e 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -25,15 +25,6 @@ int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[])
{
int i;
- /*
- * If env->cmdline_argv has already been set, do not override it. This allows
- * a command to set the cmdline, parse args and then call another
- * builtin function that implements a command -- e.g, cmd_kvm calling
- * cmd_record.
- */
- if (env->cmdline_argv != NULL)
- return 0;
-
/* do not include NULL termination */
env->cmdline_argv = calloc(argc, sizeof(char *));
if (env->cmdline_argv == NULL)
diff --git a/tools/perf/util/environment.c b/tools/perf/util/environment.c
deleted file mode 100644
index 7405123692f1..000000000000
--- a/tools/perf/util/environment.c
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * We put all the perf config variables in this same object
- * file, so that programs can link against the config parser
- * without having to link against all the rest of perf.
- */
-#include "cache.h"
-
-int pager_use_color = 1;
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 8b10621b415c..85155e91b61b 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -10,6 +10,8 @@
#include "thread.h"
#include "thread_map.h"
#include "symbol/kallsyms.h"
+#include "asm/bug.h"
+#include "stat.h"
static const char *perf_event__names[] = {
[0] = "TOTAL",
@@ -37,6 +39,12 @@ static const char *perf_event__names[] = {
[PERF_RECORD_AUXTRACE_INFO] = "AUXTRACE_INFO",
[PERF_RECORD_AUXTRACE] = "AUXTRACE",
[PERF_RECORD_AUXTRACE_ERROR] = "AUXTRACE_ERROR",
+ [PERF_RECORD_THREAD_MAP] = "THREAD_MAP",
+ [PERF_RECORD_CPU_MAP] = "CPU_MAP",
+ [PERF_RECORD_STAT_CONFIG] = "STAT_CONFIG",
+ [PERF_RECORD_STAT] = "STAT",
+ [PERF_RECORD_STAT_ROUND] = "STAT_ROUND",
+ [PERF_RECORD_EVENT_UPDATE] = "EVENT_UPDATE",
};
const char *perf_event__name(unsigned int id)
@@ -495,7 +503,7 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool,
if (comm_event == NULL)
goto out;
- mmap_event = malloc(sizeof(mmap_event->mmap) + machine->id_hdr_size);
+ mmap_event = malloc(sizeof(mmap_event->mmap2) + machine->id_hdr_size);
if (mmap_event == NULL)
goto out_free_comm;
@@ -569,7 +577,7 @@ int perf_event__synthesize_threads(struct perf_tool *tool,
if (comm_event == NULL)
goto out;
- mmap_event = malloc(sizeof(mmap_event->mmap) + machine->id_hdr_size);
+ mmap_event = malloc(sizeof(mmap_event->mmap2) + machine->id_hdr_size);
if (mmap_event == NULL)
goto out_free_comm;
@@ -699,6 +707,274 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
return err;
}
+int perf_event__synthesize_thread_map2(struct perf_tool *tool,
+ struct thread_map *threads,
+ perf_event__handler_t process,
+ struct machine *machine)
+{
+ union perf_event *event;
+ int i, err, size;
+
+ size = sizeof(event->thread_map);
+ size += threads->nr * sizeof(event->thread_map.entries[0]);
+
+ event = zalloc(size);
+ if (!event)
+ return -ENOMEM;
+
+ event->header.type = PERF_RECORD_THREAD_MAP;
+ event->header.size = size;
+ event->thread_map.nr = threads->nr;
+
+ for (i = 0; i < threads->nr; i++) {
+ struct thread_map_event_entry *entry = &event->thread_map.entries[i];
+ char *comm = thread_map__comm(threads, i);
+
+ if (!comm)
+ comm = (char *) "";
+
+ entry->pid = thread_map__pid(threads, i);
+ strncpy((char *) &entry->comm, comm, sizeof(entry->comm));
+ }
+
+ err = process(tool, event, NULL, machine);
+
+ free(event);
+ return err;
+}
+
+static void synthesize_cpus(struct cpu_map_entries *cpus,
+ struct cpu_map *map)
+{
+ int i;
+
+ cpus->nr = map->nr;
+
+ for (i = 0; i < map->nr; i++)
+ cpus->cpu[i] = map->map[i];
+}
+
+static void synthesize_mask(struct cpu_map_mask *mask,
+ struct cpu_map *map, int max)
+{
+ int i;
+
+ mask->nr = BITS_TO_LONGS(max);
+ mask->long_size = sizeof(long);
+
+ for (i = 0; i < map->nr; i++)
+ set_bit(map->map[i], mask->mask);
+}
+
+static size_t cpus_size(struct cpu_map *map)
+{
+ return sizeof(struct cpu_map_entries) + map->nr * sizeof(u16);
+}
+
+static size_t mask_size(struct cpu_map *map, int *max)
+{
+ int i;
+
+ *max = 0;
+
+ for (i = 0; i < map->nr; i++) {
+ /* bit possition of the cpu is + 1 */
+ int bit = map->map[i] + 1;
+
+ if (bit > *max)
+ *max = bit;
+ }
+
+ return sizeof(struct cpu_map_mask) + BITS_TO_LONGS(*max) * sizeof(long);
+}
+
+void *cpu_map_data__alloc(struct cpu_map *map, size_t *size, u16 *type, int *max)
+{
+ size_t size_cpus, size_mask;
+ bool is_dummy = cpu_map__empty(map);
+
+ /*
+ * Both array and mask data have variable size based
+ * on the number of cpus and their actual values.
+ * The size of the 'struct cpu_map_data' is:
+ *
+ * array = size of 'struct cpu_map_entries' +
+ * number of cpus * sizeof(u64)
+ *
+ * mask = size of 'struct cpu_map_mask' +
+ * maximum cpu bit converted to size of longs
+ *
+ * and finaly + the size of 'struct cpu_map_data'.
+ */
+ size_cpus = cpus_size(map);
+ size_mask = mask_size(map, max);
+
+ if (is_dummy || (size_cpus < size_mask)) {
+ *size += size_cpus;
+ *type = PERF_CPU_MAP__CPUS;
+ } else {
+ *size += size_mask;
+ *type = PERF_CPU_MAP__MASK;
+ }
+
+ *size += sizeof(struct cpu_map_data);
+ return zalloc(*size);
+}
+
+void cpu_map_data__synthesize(struct cpu_map_data *data, struct cpu_map *map,
+ u16 type, int max)
+{
+ data->type = type;
+
+ switch (type) {
+ case PERF_CPU_MAP__CPUS:
+ synthesize_cpus((struct cpu_map_entries *) data->data, map);
+ break;
+ case PERF_CPU_MAP__MASK:
+ synthesize_mask((struct cpu_map_mask *) data->data, map, max);
+ default:
+ break;
+ };
+}
+
+static struct cpu_map_event* cpu_map_event__new(struct cpu_map *map)
+{
+ size_t size = sizeof(struct cpu_map_event);
+ struct cpu_map_event *event;
+ int max;
+ u16 type;
+
+ event = cpu_map_data__alloc(map, &size, &type, &max);
+ if (!event)
+ return NULL;
+
+ event->header.type = PERF_RECORD_CPU_MAP;
+ event->header.size = size;
+ event->data.type = type;
+
+ cpu_map_data__synthesize(&event->data, map, type, max);
+ return event;
+}
+
+int perf_event__synthesize_cpu_map(struct perf_tool *tool,
+ struct cpu_map *map,
+ perf_event__handler_t process,
+ struct machine *machine)
+{
+ struct cpu_map_event *event;
+ int err;
+
+ event = cpu_map_event__new(map);
+ if (!event)
+ return -ENOMEM;
+
+ err = process(tool, (union perf_event *) event, NULL, machine);
+
+ free(event);
+ return err;
+}
+
+int perf_event__synthesize_stat_config(struct perf_tool *tool,
+ struct perf_stat_config *config,
+ perf_event__handler_t process,
+ struct machine *machine)
+{
+ struct stat_config_event *event;
+ int size, i = 0, err;
+
+ size = sizeof(*event);
+ size += (PERF_STAT_CONFIG_TERM__MAX * sizeof(event->data[0]));
+
+ event = zalloc(size);
+ if (!event)
+ return -ENOMEM;
+
+ event->header.type = PERF_RECORD_STAT_CONFIG;
+ event->header.size = size;
+ event->nr = PERF_STAT_CONFIG_TERM__MAX;
+
+#define ADD(__term, __val) \
+ event->data[i].tag = PERF_STAT_CONFIG_TERM__##__term; \
+ event->data[i].val = __val; \
+ i++;
+
+ ADD(AGGR_MODE, config->aggr_mode)
+ ADD(INTERVAL, config->interval)
+ ADD(SCALE, config->scale)
+
+ WARN_ONCE(i != PERF_STAT_CONFIG_TERM__MAX,
+ "stat config terms unbalanced\n");
+#undef ADD
+
+ err = process(tool, (union perf_event *) event, NULL, machine);
+
+ free(event);
+ return err;
+}
+
+int perf_event__synthesize_stat(struct perf_tool *tool,
+ u32 cpu, u32 thread, u64 id,
+ struct perf_counts_values *count,
+ perf_event__handler_t process,
+ struct machine *machine)
+{
+ struct stat_event event;
+
+ event.header.type = PERF_RECORD_STAT;
+ event.header.size = sizeof(event);
+ event.header.misc = 0;
+
+ event.id = id;
+ event.cpu = cpu;
+ event.thread = thread;
+ event.val = count->val;
+ event.ena = count->ena;
+ event.run = count->run;
+
+ return process(tool, (union perf_event *) &event, NULL, machine);
+}
+
+int perf_event__synthesize_stat_round(struct perf_tool *tool,
+ u64 evtime, u64 type,
+ perf_event__handler_t process,
+ struct machine *machine)
+{
+ struct stat_round_event event;
+
+ event.header.type = PERF_RECORD_STAT_ROUND;
+ event.header.size = sizeof(event);
+ event.header.misc = 0;
+
+ event.time = evtime;
+ event.type = type;
+
+ return process(tool, (union perf_event *) &event, NULL, machine);
+}
+
+void perf_event__read_stat_config(struct perf_stat_config *config,
+ struct stat_config_event *event)
+{
+ unsigned i;
+
+ for (i = 0; i < event->nr; i++) {
+
+ switch (event->data[i].tag) {
+#define CASE(__term, __val) \
+ case PERF_STAT_CONFIG_TERM__##__term: \
+ config->__val = event->data[i].val; \
+ break;
+
+ CASE(AGGR_MODE, aggr_mode)
+ CASE(SCALE, scale)
+ CASE(INTERVAL, interval)
+#undef CASE
+ default:
+ pr_warning("unknown stat config term %" PRIu64 "\n",
+ event->data[i].tag);
+ }
+ }
+}
+
size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp)
{
const char *s;
@@ -783,6 +1059,38 @@ size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp)
event->mmap2.filename);
}
+size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp)
+{
+ struct thread_map *threads = thread_map__new_event(&event->thread_map);
+ size_t ret;
+
+ ret = fprintf(fp, " nr: ");
+
+ if (threads)
+ ret += thread_map__fprintf(threads, fp);
+ else
+ ret += fprintf(fp, "failed to get threads from event\n");
+
+ thread_map__put(threads);
+ return ret;
+}
+
+size_t perf_event__fprintf_cpu_map(union perf_event *event, FILE *fp)
+{
+ struct cpu_map *cpus = cpu_map__new_data(&event->cpu_map.data);
+ size_t ret;
+
+ ret = fprintf(fp, " nr: ");
+
+ if (cpus)
+ ret += cpu_map__fprintf(cpus, fp);
+ else
+ ret += fprintf(fp, "failed to get cpumap from event\n");
+
+ cpu_map__put(cpus);
+ return ret;
+}
+
int perf_event__process_mmap(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample,
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index a0dbcbd4f6d8..b7ffb7ee9971 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -226,6 +226,12 @@ enum perf_user_event_type { /* above any possible kernel type */
PERF_RECORD_AUXTRACE_INFO = 70,
PERF_RECORD_AUXTRACE = 71,
PERF_RECORD_AUXTRACE_ERROR = 72,
+ PERF_RECORD_THREAD_MAP = 73,
+ PERF_RECORD_CPU_MAP = 74,
+ PERF_RECORD_STAT_CONFIG = 75,
+ PERF_RECORD_STAT = 76,
+ PERF_RECORD_STAT_ROUND = 77,
+ PERF_RECORD_EVENT_UPDATE = 78,
PERF_RECORD_HEADER_MAX
};
@@ -270,12 +276,61 @@ struct events_stats {
u32 nr_proc_map_timeout;
};
+enum {
+ PERF_CPU_MAP__CPUS = 0,
+ PERF_CPU_MAP__MASK = 1,
+};
+
+struct cpu_map_entries {
+ u16 nr;
+ u16 cpu[];
+};
+
+struct cpu_map_mask {
+ u16 nr;
+ u16 long_size;
+ unsigned long mask[];
+};
+
+struct cpu_map_data {
+ u16 type;
+ char data[];
+};
+
+struct cpu_map_event {
+ struct perf_event_header header;
+ struct cpu_map_data data;
+};
+
struct attr_event {
struct perf_event_header header;
struct perf_event_attr attr;
u64 id[];
};
+enum {
+ PERF_EVENT_UPDATE__UNIT = 0,
+ PERF_EVENT_UPDATE__SCALE = 1,
+ PERF_EVENT_UPDATE__NAME = 2,
+ PERF_EVENT_UPDATE__CPUS = 3,
+};
+
+struct event_update_event_cpus {
+ struct cpu_map_data cpus;
+};
+
+struct event_update_event_scale {
+ double scale;
+};
+
+struct event_update_event {
+ struct perf_event_header header;
+ u64 type;
+ u64 id;
+
+ char data[];
+};
+
#define MAX_EVENT_NAME 64
struct perf_trace_event_type {
@@ -356,6 +411,63 @@ struct context_switch_event {
u32 next_prev_tid;
};
+struct thread_map_event_entry {
+ u64 pid;
+ char comm[16];
+};
+
+struct thread_map_event {
+ struct perf_event_header header;
+ u64 nr;
+ struct thread_map_event_entry entries[];
+};
+
+enum {
+ PERF_STAT_CONFIG_TERM__AGGR_MODE = 0,
+ PERF_STAT_CONFIG_TERM__INTERVAL = 1,
+ PERF_STAT_CONFIG_TERM__SCALE = 2,
+ PERF_STAT_CONFIG_TERM__MAX = 3,
+};
+
+struct stat_config_event_entry {
+ u64 tag;
+ u64 val;
+};
+
+struct stat_config_event {
+ struct perf_event_header header;
+ u64 nr;
+ struct stat_config_event_entry data[];
+};
+
+struct stat_event {
+ struct perf_event_header header;
+
+ u64 id;
+ u32 cpu;
+ u32 thread;
+
+ union {
+ struct {
+ u64 val;
+ u64 ena;
+ u64 run;
+ };
+ u64 values[3];
+ };
+};
+
+enum {
+ PERF_STAT_ROUND_TYPE__INTERVAL = 0,
+ PERF_STAT_ROUND_TYPE__FINAL = 1,
+};
+
+struct stat_round_event {
+ struct perf_event_header header;
+ u64 type;
+ u64 time;
+};
+
union perf_event {
struct perf_event_header header;
struct mmap_event mmap;
@@ -368,6 +480,7 @@ union perf_event {
struct throttle_event throttle;
struct sample_event sample;
struct attr_event attr;
+ struct event_update_event event_update;
struct event_type_event event_type;
struct tracing_data_event tracing_data;
struct build_id_event build_id;
@@ -378,12 +491,20 @@ union perf_event {
struct aux_event aux;
struct itrace_start_event itrace_start;
struct context_switch_event context_switch;
+ struct thread_map_event thread_map;
+ struct cpu_map_event cpu_map;
+ struct stat_config_event stat_config;
+ struct stat_event stat;
+ struct stat_round_event stat_round;
};
void perf_event__print_totals(void);
struct perf_tool;
struct thread_map;
+struct cpu_map;
+struct perf_stat_config;
+struct perf_counts_values;
typedef int (*perf_event__handler_t)(struct perf_tool *tool,
union perf_event *event,
@@ -395,6 +516,14 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool,
perf_event__handler_t process,
struct machine *machine, bool mmap_data,
unsigned int proc_map_timeout);
+int perf_event__synthesize_thread_map2(struct perf_tool *tool,
+ struct thread_map *threads,
+ perf_event__handler_t process,
+ struct machine *machine);
+int perf_event__synthesize_cpu_map(struct perf_tool *tool,
+ struct cpu_map *cpus,
+ perf_event__handler_t process,
+ struct machine *machine);
int perf_event__synthesize_threads(struct perf_tool *tool,
perf_event__handler_t process,
struct machine *machine, bool mmap_data,
@@ -402,7 +531,21 @@ int perf_event__synthesize_threads(struct perf_tool *tool,
int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
perf_event__handler_t process,
struct machine *machine);
-
+int perf_event__synthesize_stat_config(struct perf_tool *tool,
+ struct perf_stat_config *config,
+ perf_event__handler_t process,
+ struct machine *machine);
+void perf_event__read_stat_config(struct perf_stat_config *config,
+ struct stat_config_event *event);
+int perf_event__synthesize_stat(struct perf_tool *tool,
+ u32 cpu, u32 thread, u64 id,
+ struct perf_counts_values *count,
+ perf_event__handler_t process,
+ struct machine *machine);
+int perf_event__synthesize_stat_round(struct perf_tool *tool,
+ u64 time, u64 type,
+ perf_event__handler_t process,
+ struct machine *machine);
int perf_event__synthesize_modules(struct perf_tool *tool,
perf_event__handler_t process,
struct machine *machine);
@@ -499,9 +642,14 @@ 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_switch(union perf_event *event, FILE *fp);
+size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp);
+size_t perf_event__fprintf_cpu_map(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,
const char *symbol_name);
+void *cpu_map_data__alloc(struct cpu_map *map, size_t *size, u16 *type, int *max);
+void cpu_map_data__synthesize(struct cpu_map_data *data, struct cpu_map *map,
+ u16 type, int max);
#endif /* __PERF_RECORD_H */
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index d1392194a9a9..d81f13de2476 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -18,7 +18,7 @@
#include <unistd.h>
#include "parse-events.h"
-#include "parse-options.h"
+#include <subcmd/parse-options.h>
#include <sys/mman.h>
@@ -68,6 +68,18 @@ struct perf_evlist *perf_evlist__new_default(void)
return evlist;
}
+struct perf_evlist *perf_evlist__new_dummy(void)
+{
+ struct perf_evlist *evlist = perf_evlist__new();
+
+ if (evlist && perf_evlist__add_dummy(evlist)) {
+ perf_evlist__delete(evlist);
+ evlist = NULL;
+ }
+
+ return evlist;
+}
+
/**
* perf_evlist__set_id_pos - set the positions of event ids.
* @evlist: selected event list
@@ -248,6 +260,22 @@ error:
return -ENOMEM;
}
+int perf_evlist__add_dummy(struct perf_evlist *evlist)
+{
+ struct perf_event_attr attr = {
+ .type = PERF_TYPE_SOFTWARE,
+ .config = PERF_COUNT_SW_DUMMY,
+ .size = sizeof(attr), /* to capture ABI version */
+ };
+ struct perf_evsel *evsel = perf_evsel__new(&attr);
+
+ if (evsel == NULL)
+ return -ENOMEM;
+
+ perf_evlist__add(evlist, evsel);
+ return 0;
+}
+
static int perf_evlist__add_attrs(struct perf_evlist *evlist,
struct perf_event_attr *attrs, size_t nr_attrs)
{
@@ -336,20 +364,12 @@ static int perf_evlist__nr_threads(struct perf_evlist *evlist,
void perf_evlist__disable(struct perf_evlist *evlist)
{
- int cpu, thread;
struct perf_evsel *pos;
- int nr_cpus = cpu_map__nr(evlist->cpus);
- int nr_threads;
- for (cpu = 0; cpu < nr_cpus; cpu++) {
- evlist__for_each(evlist, pos) {
- if (!perf_evsel__is_group_leader(pos) || !pos->fd)
- continue;
- nr_threads = perf_evlist__nr_threads(evlist, pos);
- for (thread = 0; thread < nr_threads; thread++)
- ioctl(FD(pos, cpu, thread),
- PERF_EVENT_IOC_DISABLE, 0);
- }
+ evlist__for_each(evlist, pos) {
+ if (!perf_evsel__is_group_leader(pos) || !pos->fd)
+ continue;
+ perf_evsel__disable(pos);
}
evlist->enabled = false;
@@ -357,20 +377,12 @@ void perf_evlist__disable(struct perf_evlist *evlist)
void perf_evlist__enable(struct perf_evlist *evlist)
{
- int cpu, thread;
struct perf_evsel *pos;
- int nr_cpus = cpu_map__nr(evlist->cpus);
- int nr_threads;
- for (cpu = 0; cpu < nr_cpus; cpu++) {
- evlist__for_each(evlist, pos) {
- if (!perf_evsel__is_group_leader(pos) || !pos->fd)
- continue;
- nr_threads = perf_evlist__nr_threads(evlist, pos);
- for (thread = 0; thread < nr_threads; thread++)
- ioctl(FD(pos, cpu, thread),
- PERF_EVENT_IOC_ENABLE, 0);
- }
+ evlist__for_each(evlist, pos) {
+ if (!perf_evsel__is_group_leader(pos) || !pos->fd)
+ continue;
+ perf_evsel__enable(pos);
}
evlist->enabled = true;
@@ -381,48 +393,6 @@ 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,
- struct perf_evsel *evsel)
-{
- int cpu, thread, err;
- int nr_cpus = cpu_map__nr(evlist->cpus);
- int nr_threads = perf_evlist__nr_threads(evlist, evsel);
-
- if (!evsel->fd)
- return 0;
-
- for (cpu = 0; cpu < nr_cpus; cpu++) {
- for (thread = 0; thread < nr_threads; thread++) {
- err = ioctl(FD(evsel, cpu, thread),
- PERF_EVENT_IOC_DISABLE, 0);
- if (err)
- return err;
- }
- }
- return 0;
-}
-
-int perf_evlist__enable_event(struct perf_evlist *evlist,
- struct perf_evsel *evsel)
-{
- int cpu, thread, err;
- int nr_cpus = cpu_map__nr(evlist->cpus);
- int nr_threads = perf_evlist__nr_threads(evlist, evsel);
-
- if (!evsel->fd)
- return -EINVAL;
-
- for (cpu = 0; cpu < nr_cpus; cpu++) {
- for (thread = 0; thread < nr_threads; thread++) {
- err = ioctl(FD(evsel, cpu, thread),
- PERF_EVENT_IOC_ENABLE, 0);
- if (err)
- return err;
- }
- }
- return 0;
-}
-
static int perf_evlist__enable_event_cpu(struct perf_evlist *evlist,
struct perf_evsel *evsel, int cpu)
{
@@ -550,9 +520,9 @@ void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel,
evsel->id[evsel->ids++] = id;
}
-static int perf_evlist__id_add_fd(struct perf_evlist *evlist,
- struct perf_evsel *evsel,
- int cpu, int thread, int fd)
+int perf_evlist__id_add_fd(struct perf_evlist *evlist,
+ struct perf_evsel *evsel,
+ int cpu, int thread, int fd)
{
u64 read_data[4] = { 0, };
int id_idx = 1; /* The first entry is the counter value */
@@ -1486,7 +1456,7 @@ int perf_evlist__open(struct perf_evlist *evlist)
perf_evlist__update_id_pos(evlist);
evlist__for_each(evlist, evsel) {
- err = perf_evsel__open(evsel, evlist->cpus, evlist->threads);
+ err = perf_evsel__open(evsel, evsel->cpus, evsel->threads);
if (err < 0)
goto out_err;
}
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index a459fe71b452..7c4d9a206776 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -67,6 +67,7 @@ struct perf_evsel_str_handler {
struct perf_evlist *perf_evlist__new(void);
struct perf_evlist *perf_evlist__new_default(void);
+struct perf_evlist *perf_evlist__new_dummy(void);
void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus,
struct thread_map *threads);
void perf_evlist__exit(struct perf_evlist *evlist);
@@ -81,6 +82,8 @@ int __perf_evlist__add_default_attrs(struct perf_evlist *evlist,
#define perf_evlist__add_default_attrs(evlist, array) \
__perf_evlist__add_default_attrs(evlist, array, ARRAY_SIZE(array))
+int perf_evlist__add_dummy(struct perf_evlist *evlist);
+
int perf_evlist__add_newtp(struct perf_evlist *evlist,
const char *sys, const char *name, void *handler);
@@ -97,6 +100,9 @@ perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist,
void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel,
int cpu, int thread, u64 id);
+int perf_evlist__id_add_fd(struct perf_evlist *evlist,
+ struct perf_evsel *evsel,
+ int cpu, int thread, int fd);
int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd);
int perf_evlist__alloc_pollfd(struct perf_evlist *evlist);
@@ -149,10 +155,6 @@ 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);
-int perf_evlist__enable_event(struct perf_evlist *evlist,
- struct perf_evsel *evsel);
int perf_evlist__enable_event_idx(struct perf_evlist *evlist,
struct perf_evsel *evsel, int idx);
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 397fb4ed3c97..cdbaf9b51e42 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -36,6 +36,7 @@ static struct {
bool cloexec;
bool clockid;
bool clockid_wrong;
+ bool lbr_flags;
} perf_missing_features;
static clockid_t clockid;
@@ -574,7 +575,9 @@ perf_evsel__config_callgraph(struct perf_evsel *evsel,
} else {
perf_evsel__set_sample_bit(evsel, BRANCH_STACK);
attr->branch_sample_type = PERF_SAMPLE_BRANCH_USER |
- PERF_SAMPLE_BRANCH_CALL_STACK;
+ PERF_SAMPLE_BRANCH_CALL_STACK |
+ PERF_SAMPLE_BRANCH_NO_CYCLES |
+ PERF_SAMPLE_BRANCH_NO_FLAGS;
}
} else
pr_warning("Cannot use LBR callstack with branch stack. "
@@ -981,13 +984,26 @@ int perf_evsel__append_filter(struct perf_evsel *evsel,
return -1;
}
-int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads)
+int perf_evsel__enable(struct perf_evsel *evsel)
{
+ int nthreads = thread_map__nr(evsel->threads);
+ int ncpus = cpu_map__nr(evsel->cpus);
+
return perf_evsel__run_ioctl(evsel, ncpus, nthreads,
PERF_EVENT_IOC_ENABLE,
0);
}
+int perf_evsel__disable(struct perf_evsel *evsel)
+{
+ int nthreads = thread_map__nr(evsel->threads);
+ int ncpus = cpu_map__nr(evsel->cpus);
+
+ return perf_evsel__run_ioctl(evsel, ncpus, nthreads,
+ PERF_EVENT_IOC_DISABLE,
+ 0);
+}
+
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads)
{
if (ncpus == 0 || nthreads == 0)
@@ -1192,6 +1208,7 @@ static void __p_sample_type(char *buf, size_t size, u64 value)
bit_name(PERIOD), bit_name(STREAM_ID), bit_name(RAW),
bit_name(BRANCH_STACK), bit_name(REGS_USER), bit_name(STACK_USER),
bit_name(IDENTIFIER), bit_name(REGS_INTR), bit_name(DATA_SRC),
+ bit_name(WEIGHT),
{ .name = NULL, }
};
#undef bit_name
@@ -1323,6 +1340,9 @@ fallback_missing_features:
evsel->attr.mmap2 = 0;
if (perf_missing_features.exclude_guest)
evsel->attr.exclude_guest = evsel->attr.exclude_host = 0;
+ if (perf_missing_features.lbr_flags)
+ evsel->attr.branch_sample_type &= ~(PERF_SAMPLE_BRANCH_NO_FLAGS |
+ PERF_SAMPLE_BRANCH_NO_CYCLES);
retry_sample_id:
if (perf_missing_features.sample_id_all)
evsel->attr.sample_id_all = 0;
@@ -1441,6 +1461,12 @@ try_fallback:
} else if (!perf_missing_features.sample_id_all) {
perf_missing_features.sample_id_all = true;
goto retry_sample_id;
+ } else if (!perf_missing_features.lbr_flags &&
+ (evsel->attr.branch_sample_type &
+ (PERF_SAMPLE_BRANCH_NO_CYCLES |
+ PERF_SAMPLE_BRANCH_NO_FLAGS))) {
+ perf_missing_features.lbr_flags = true;
+ goto fallback_missing_features;
}
out_close:
@@ -2272,6 +2298,29 @@ int perf_evsel__fprintf(struct perf_evsel *evsel,
printed += comma_fprintf(fp, &first, " %s=%" PRIu64,
term, (u64)evsel->attr.sample_freq);
}
+
+ if (details->trace_fields) {
+ struct format_field *field;
+
+ if (evsel->attr.type != PERF_TYPE_TRACEPOINT) {
+ printed += comma_fprintf(fp, &first, " (not a tracepoint)");
+ goto out;
+ }
+
+ field = evsel->tp_format->format.fields;
+ if (field == NULL) {
+ printed += comma_fprintf(fp, &first, " (no trace field)");
+ goto out;
+ }
+
+ printed += comma_fprintf(fp, &first, " trace_fields: %s", field->name);
+
+ field = field->next;
+ while (field) {
+ printed += comma_fprintf(fp, &first, "%s", field->name);
+ field = field->next;
+ }
+ }
out:
fputc('\n', fp);
return ++printed;
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 0e49bd742c63..8e75434bd01c 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -227,7 +227,8 @@ int perf_evsel__append_filter(struct perf_evsel *evsel,
const char *op, const char *filter);
int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
const char *filter);
-int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads);
+int perf_evsel__enable(struct perf_evsel *evsel);
+int perf_evsel__disable(struct perf_evsel *evsel);
int perf_evsel__open_per_cpu(struct perf_evsel *evsel,
struct cpu_map *cpus);
@@ -368,6 +369,7 @@ struct perf_attr_details {
bool verbose;
bool event_group;
bool force;
+ bool trace_fields;
};
int perf_evsel__fprintf(struct perf_evsel *evsel,
diff --git a/tools/perf/util/exec_cmd.c b/tools/perf/util/exec_cmd.c
deleted file mode 100644
index 7adf4ad15d8f..000000000000
--- a/tools/perf/util/exec_cmd.c
+++ /dev/null
@@ -1,148 +0,0 @@
-#include "cache.h"
-#include "exec_cmd.h"
-#include "quote.h"
-
-#include <string.h>
-
-#define MAX_ARGS 32
-
-static const char *argv_exec_path;
-static const char *argv0_path;
-
-const char *system_path(const char *path)
-{
- static const char *prefix = PREFIX;
- struct strbuf d = STRBUF_INIT;
-
- if (is_absolute_path(path))
- return path;
-
- strbuf_addf(&d, "%s/%s", prefix, path);
- path = strbuf_detach(&d, NULL);
- return path;
-}
-
-const char *perf_extract_argv0_path(const char *argv0)
-{
- const char *slash;
-
- if (!argv0 || !*argv0)
- return NULL;
- slash = argv0 + strlen(argv0);
-
- while (argv0 <= slash && !is_dir_sep(*slash))
- slash--;
-
- if (slash >= argv0) {
- argv0_path = strndup(argv0, slash - argv0);
- return argv0_path ? slash + 1 : NULL;
- }
-
- return argv0;
-}
-
-void perf_set_argv_exec_path(const char *exec_path)
-{
- argv_exec_path = exec_path;
- /*
- * Propagate this setting to external programs.
- */
- setenv(EXEC_PATH_ENVIRONMENT, exec_path, 1);
-}
-
-
-/* Returns the highest-priority, location to look for perf programs. */
-const char *perf_exec_path(void)
-{
- const char *env;
-
- if (argv_exec_path)
- return argv_exec_path;
-
- env = getenv(EXEC_PATH_ENVIRONMENT);
- if (env && *env) {
- return env;
- }
-
- return system_path(PERF_EXEC_PATH);
-}
-
-static void add_path(struct strbuf *out, const char *path)
-{
- if (path && *path) {
- if (is_absolute_path(path))
- strbuf_addstr(out, path);
- else
- strbuf_addstr(out, make_nonrelative_path(path));
-
- strbuf_addch(out, PATH_SEP);
- }
-}
-
-void setup_path(void)
-{
- const char *old_path = getenv("PATH");
- struct strbuf new_path = STRBUF_INIT;
-
- add_path(&new_path, perf_exec_path());
- add_path(&new_path, argv0_path);
-
- if (old_path)
- strbuf_addstr(&new_path, old_path);
- else
- strbuf_addstr(&new_path, "/usr/local/bin:/usr/bin:/bin");
-
- setenv("PATH", new_path.buf, 1);
-
- strbuf_release(&new_path);
-}
-
-static const char **prepare_perf_cmd(const char **argv)
-{
- int argc;
- const char **nargv;
-
- for (argc = 0; argv[argc]; argc++)
- ; /* just counting */
- nargv = malloc(sizeof(*nargv) * (argc + 2));
-
- nargv[0] = "perf";
- for (argc = 0; argv[argc]; argc++)
- nargv[argc + 1] = argv[argc];
- nargv[argc + 1] = NULL;
- return nargv;
-}
-
-int execv_perf_cmd(const char **argv) {
- const char **nargv = prepare_perf_cmd(argv);
-
- /* execvp() can only ever return if it fails */
- execvp("perf", (char **)nargv);
-
- free(nargv);
- return -1;
-}
-
-
-int execl_perf_cmd(const char *cmd,...)
-{
- int argc;
- const char *argv[MAX_ARGS + 1];
- const char *arg;
- va_list param;
-
- va_start(param, cmd);
- argv[0] = cmd;
- argc = 1;
- while (argc < MAX_ARGS) {
- arg = argv[argc++] = va_arg(param, char *);
- if (!arg)
- break;
- }
- va_end(param);
- if (MAX_ARGS <= argc)
- return error("too many args to run %s", cmd);
-
- argv[argc] = NULL;
- return execv_perf_cmd(argv);
-}
diff --git a/tools/perf/util/exec_cmd.h b/tools/perf/util/exec_cmd.h
deleted file mode 100644
index bc4b915963f5..000000000000
--- a/tools/perf/util/exec_cmd.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef __PERF_EXEC_CMD_H
-#define __PERF_EXEC_CMD_H
-
-extern void perf_set_argv_exec_path(const char *exec_path);
-extern const char *perf_extract_argv0_path(const char *path);
-extern const char *perf_exec_path(void);
-extern void setup_path(void);
-extern int execv_perf_cmd(const char **argv); /* NULL terminated */
-extern int execl_perf_cmd(const char *cmd, ...);
-extern const char *system_path(const char *path);
-
-#endif /* __PERF_EXEC_CMD_H */
diff --git a/tools/perf/util/generate-cmdlist.sh b/tools/perf/util/generate-cmdlist.sh
index 36a885d2cd22..0ac2037c970c 100755
--- a/tools/perf/util/generate-cmdlist.sh
+++ b/tools/perf/util/generate-cmdlist.sh
@@ -36,4 +36,19 @@ do
}' "Documentation/perf-$cmd.txt"
done
echo "#endif /* HAVE_LIBELF_SUPPORT */"
+
+echo "#ifdef HAVE_LIBAUDIT_SUPPORT"
+sed -n -e 's/^perf-\([^ ]*\)[ ].* audit*/\1/p' command-list.txt |
+sort |
+while read cmd
+do
+ sed -n '
+ /^NAME/,/perf-'"$cmd"'/H
+ ${
+ x
+ s/.*perf-'"$cmd"' - \(.*\)/ {"'"$cmd"'", "\1"},/
+ p
+ }' "Documentation/perf-$cmd.txt"
+done
+echo "#endif /* HAVE_LIBELF_SUPPORT */"
echo "};"
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 43838003c1a1..f50b7235ecb6 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -724,7 +724,7 @@ static int write_numa_topology(int fd, struct perf_header *h __maybe_unused,
done:
free(buf);
fclose(fp);
- free(node_map);
+ cpu_map__put(node_map);
return ret;
}
@@ -868,6 +868,13 @@ static int write_auxtrace(int fd, struct perf_header *h,
return err;
}
+static int write_stat(int fd __maybe_unused,
+ struct perf_header *h __maybe_unused,
+ struct perf_evlist *evlist __maybe_unused)
+{
+ return 0;
+}
+
static void print_hostname(struct perf_header *ph, int fd __maybe_unused,
FILE *fp)
{
@@ -1159,6 +1166,12 @@ static void print_auxtrace(struct perf_header *ph __maybe_unused,
fprintf(fp, "# contains AUX area data (e.g. instruction trace)\n");
}
+static void print_stat(struct perf_header *ph __maybe_unused,
+ int fd __maybe_unused, FILE *fp)
+{
+ fprintf(fp, "# contains stat data\n");
+}
+
static void print_pmu_mappings(struct perf_header *ph, int fd __maybe_unused,
FILE *fp)
{
@@ -1948,6 +1961,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings),
FEAT_OPP(HEADER_GROUP_DESC, group_desc),
FEAT_OPP(HEADER_AUXTRACE, auxtrace),
+ FEAT_OPA(HEADER_STAT, stat),
};
struct header_print_data {
@@ -2686,6 +2700,152 @@ int perf_event__synthesize_attr(struct perf_tool *tool,
return err;
}
+static struct event_update_event *
+event_update_event__new(size_t size, u64 type, u64 id)
+{
+ struct event_update_event *ev;
+
+ size += sizeof(*ev);
+ size = PERF_ALIGN(size, sizeof(u64));
+
+ ev = zalloc(size);
+ if (ev) {
+ ev->header.type = PERF_RECORD_EVENT_UPDATE;
+ ev->header.size = (u16)size;
+ ev->type = type;
+ ev->id = id;
+ }
+ return ev;
+}
+
+int
+perf_event__synthesize_event_update_unit(struct perf_tool *tool,
+ struct perf_evsel *evsel,
+ perf_event__handler_t process)
+{
+ struct event_update_event *ev;
+ size_t size = strlen(evsel->unit);
+ int err;
+
+ ev = event_update_event__new(size + 1, PERF_EVENT_UPDATE__UNIT, evsel->id[0]);
+ if (ev == NULL)
+ return -ENOMEM;
+
+ strncpy(ev->data, evsel->unit, size);
+ err = process(tool, (union perf_event *)ev, NULL, NULL);
+ free(ev);
+ return err;
+}
+
+int
+perf_event__synthesize_event_update_scale(struct perf_tool *tool,
+ struct perf_evsel *evsel,
+ perf_event__handler_t process)
+{
+ struct event_update_event *ev;
+ struct event_update_event_scale *ev_data;
+ int err;
+
+ ev = event_update_event__new(sizeof(*ev_data), PERF_EVENT_UPDATE__SCALE, evsel->id[0]);
+ if (ev == NULL)
+ return -ENOMEM;
+
+ ev_data = (struct event_update_event_scale *) ev->data;
+ ev_data->scale = evsel->scale;
+ err = process(tool, (union perf_event*) ev, NULL, NULL);
+ free(ev);
+ return err;
+}
+
+int
+perf_event__synthesize_event_update_name(struct perf_tool *tool,
+ struct perf_evsel *evsel,
+ perf_event__handler_t process)
+{
+ struct event_update_event *ev;
+ size_t len = strlen(evsel->name);
+ int err;
+
+ ev = event_update_event__new(len + 1, PERF_EVENT_UPDATE__NAME, evsel->id[0]);
+ if (ev == NULL)
+ return -ENOMEM;
+
+ strncpy(ev->data, evsel->name, len);
+ err = process(tool, (union perf_event*) ev, NULL, NULL);
+ free(ev);
+ return err;
+}
+
+int
+perf_event__synthesize_event_update_cpus(struct perf_tool *tool,
+ struct perf_evsel *evsel,
+ perf_event__handler_t process)
+{
+ size_t size = sizeof(struct event_update_event);
+ struct event_update_event *ev;
+ int max, err;
+ u16 type;
+
+ if (!evsel->own_cpus)
+ return 0;
+
+ ev = cpu_map_data__alloc(evsel->own_cpus, &size, &type, &max);
+ if (!ev)
+ return -ENOMEM;
+
+ ev->header.type = PERF_RECORD_EVENT_UPDATE;
+ ev->header.size = (u16)size;
+ ev->type = PERF_EVENT_UPDATE__CPUS;
+ ev->id = evsel->id[0];
+
+ cpu_map_data__synthesize((struct cpu_map_data *) ev->data,
+ evsel->own_cpus,
+ type, max);
+
+ err = process(tool, (union perf_event*) ev, NULL, NULL);
+ free(ev);
+ return err;
+}
+
+size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp)
+{
+ struct event_update_event *ev = &event->event_update;
+ struct event_update_event_scale *ev_scale;
+ struct event_update_event_cpus *ev_cpus;
+ struct cpu_map *map;
+ size_t ret;
+
+ ret = fprintf(fp, "\n... id: %" PRIu64 "\n", ev->id);
+
+ switch (ev->type) {
+ case PERF_EVENT_UPDATE__SCALE:
+ ev_scale = (struct event_update_event_scale *) ev->data;
+ ret += fprintf(fp, "... scale: %f\n", ev_scale->scale);
+ break;
+ case PERF_EVENT_UPDATE__UNIT:
+ ret += fprintf(fp, "... unit: %s\n", ev->data);
+ break;
+ case PERF_EVENT_UPDATE__NAME:
+ ret += fprintf(fp, "... name: %s\n", ev->data);
+ break;
+ case PERF_EVENT_UPDATE__CPUS:
+ ev_cpus = (struct event_update_event_cpus *) ev->data;
+ ret += fprintf(fp, "... ");
+
+ map = cpu_map__new_data(&ev_cpus->cpus);
+ if (map)
+ ret += cpu_map__fprintf(map, fp);
+ else
+ ret += fprintf(fp, "failed to get cpus\n");
+ break;
+ default:
+ ret += fprintf(fp, "... unknown type\n");
+ break;
+ }
+
+ return ret;
+}
+
int perf_event__synthesize_attrs(struct perf_tool *tool,
struct perf_session *session,
perf_event__handler_t process)
@@ -2745,6 +2905,51 @@ int perf_event__process_attr(struct perf_tool *tool __maybe_unused,
return 0;
}
+int perf_event__process_event_update(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_evlist **pevlist)
+{
+ struct event_update_event *ev = &event->event_update;
+ struct event_update_event_scale *ev_scale;
+ struct event_update_event_cpus *ev_cpus;
+ struct perf_evlist *evlist;
+ struct perf_evsel *evsel;
+ struct cpu_map *map;
+
+ if (!pevlist || *pevlist == NULL)
+ return -EINVAL;
+
+ evlist = *pevlist;
+
+ evsel = perf_evlist__id2evsel(evlist, ev->id);
+ if (evsel == NULL)
+ return -EINVAL;
+
+ switch (ev->type) {
+ case PERF_EVENT_UPDATE__UNIT:
+ evsel->unit = strdup(ev->data);
+ break;
+ case PERF_EVENT_UPDATE__NAME:
+ evsel->name = strdup(ev->data);
+ break;
+ case PERF_EVENT_UPDATE__SCALE:
+ ev_scale = (struct event_update_event_scale *) ev->data;
+ evsel->scale = ev_scale->scale;
+ case PERF_EVENT_UPDATE__CPUS:
+ ev_cpus = (struct event_update_event_cpus *) ev->data;
+
+ map = cpu_map__new_data(&ev_cpus->cpus);
+ if (map)
+ evsel->own_cpus = map;
+ else
+ pr_err("failed to get event_update cpus\n");
+ default:
+ break;
+ }
+
+ return 0;
+}
+
int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd,
struct perf_evlist *evlist,
perf_event__handler_t process)
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 05f27cb6b7e3..cff9892452ee 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -31,6 +31,7 @@ enum {
HEADER_PMU_MAPPINGS,
HEADER_GROUP_DESC,
HEADER_AUXTRACE,
+ HEADER_STAT,
HEADER_LAST_FEATURE,
HEADER_FEAT_BITS = 256,
};
@@ -105,8 +106,24 @@ int perf_event__synthesize_attr(struct perf_tool *tool,
int perf_event__synthesize_attrs(struct perf_tool *tool,
struct perf_session *session,
perf_event__handler_t process);
+int perf_event__synthesize_event_update_unit(struct perf_tool *tool,
+ struct perf_evsel *evsel,
+ perf_event__handler_t process);
+int perf_event__synthesize_event_update_scale(struct perf_tool *tool,
+ struct perf_evsel *evsel,
+ perf_event__handler_t process);
+int perf_event__synthesize_event_update_name(struct perf_tool *tool,
+ struct perf_evsel *evsel,
+ perf_event__handler_t process);
+int perf_event__synthesize_event_update_cpus(struct perf_tool *tool,
+ struct perf_evsel *evsel,
+ perf_event__handler_t process);
int perf_event__process_attr(struct perf_tool *tool, union perf_event *event,
struct perf_evlist **pevlist);
+int perf_event__process_event_update(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_evlist **pevlist);
+size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp);
int perf_event__synthesize_tracing_data(struct perf_tool *tool,
int fd, struct perf_evlist *evlist,
diff --git a/tools/perf/util/help-unknown-cmd.c b/tools/perf/util/help-unknown-cmd.c
new file mode 100644
index 000000000000..dc1e41c9b054
--- /dev/null
+++ b/tools/perf/util/help-unknown-cmd.c
@@ -0,0 +1,103 @@
+#include "cache.h"
+#include <subcmd/help.h>
+#include "../builtin.h"
+#include "levenshtein.h"
+
+static int autocorrect;
+static struct cmdnames aliases;
+
+static int perf_unknown_cmd_config(const char *var, const char *value, void *cb)
+{
+ if (!strcmp(var, "help.autocorrect"))
+ autocorrect = perf_config_int(var,value);
+ /* Also use aliases for command lookup */
+ if (!prefixcmp(var, "alias."))
+ add_cmdname(&aliases, var + 6, strlen(var + 6));
+
+ return perf_default_config(var, value, cb);
+}
+
+static int levenshtein_compare(const void *p1, const void *p2)
+{
+ const struct cmdname *const *c1 = p1, *const *c2 = p2;
+ const char *s1 = (*c1)->name, *s2 = (*c2)->name;
+ int l1 = (*c1)->len;
+ int l2 = (*c2)->len;
+ return l1 != l2 ? l1 - l2 : strcmp(s1, s2);
+}
+
+static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
+{
+ unsigned int i;
+
+ ALLOC_GROW(cmds->names, cmds->cnt + old->cnt, cmds->alloc);
+
+ for (i = 0; i < old->cnt; i++)
+ cmds->names[cmds->cnt++] = old->names[i];
+ zfree(&old->names);
+ old->cnt = 0;
+}
+
+const char *help_unknown_cmd(const char *cmd)
+{
+ unsigned int i, n = 0, best_similarity = 0;
+ struct cmdnames main_cmds, other_cmds;
+
+ memset(&main_cmds, 0, sizeof(main_cmds));
+ memset(&other_cmds, 0, sizeof(main_cmds));
+ memset(&aliases, 0, sizeof(aliases));
+
+ perf_config(perf_unknown_cmd_config, NULL);
+
+ load_command_list("perf-", &main_cmds, &other_cmds);
+
+ add_cmd_list(&main_cmds, &aliases);
+ add_cmd_list(&main_cmds, &other_cmds);
+ qsort(main_cmds.names, main_cmds.cnt,
+ sizeof(main_cmds.names), cmdname_compare);
+ uniq(&main_cmds);
+
+ if (main_cmds.cnt) {
+ /* This reuses cmdname->len for similarity index */
+ for (i = 0; i < main_cmds.cnt; ++i)
+ main_cmds.names[i]->len =
+ levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4);
+
+ qsort(main_cmds.names, main_cmds.cnt,
+ sizeof(*main_cmds.names), levenshtein_compare);
+
+ best_similarity = main_cmds.names[0]->len;
+ n = 1;
+ while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len)
+ ++n;
+ }
+
+ if (autocorrect && n == 1) {
+ const char *assumed = main_cmds.names[0]->name;
+
+ main_cmds.names[0] = NULL;
+ clean_cmdnames(&main_cmds);
+ fprintf(stderr, "WARNING: You called a perf program named '%s', "
+ "which does not exist.\n"
+ "Continuing under the assumption that you meant '%s'\n",
+ cmd, assumed);
+ if (autocorrect > 0) {
+ fprintf(stderr, "in %0.1f seconds automatically...\n",
+ (float)autocorrect/10.0);
+ poll(NULL, 0, autocorrect * 100);
+ }
+ return assumed;
+ }
+
+ fprintf(stderr, "perf: '%s' is not a perf-command. See 'perf --help'.\n", cmd);
+
+ if (main_cmds.cnt && best_similarity < 6) {
+ fprintf(stderr, "\nDid you mean %s?\n",
+ n < 2 ? "this": "one of these");
+
+ for (i = 0; i < n; i++)
+ fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);
+ }
+
+ exit(1);
+}
diff --git a/tools/perf/util/help-unknown-cmd.h b/tools/perf/util/help-unknown-cmd.h
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/tools/perf/util/help-unknown-cmd.h
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 4fd37d6708cb..c226303e3da0 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -254,6 +254,7 @@ static bool hists__decay_entry(struct hists *hists, struct hist_entry *he)
he_stat__decay(&he->stat);
if (symbol_conf.cumulate_callchain)
he_stat__decay(he->stat_acc);
+ decay_callchain(he->callchain);
diff = prev_period - he->stat.period;
@@ -270,6 +271,8 @@ static void hists__delete_entry(struct hists *hists, struct hist_entry *he)
if (sort__need_collapse)
rb_erase(&he->rb_node_in, &hists->entries_collapsed);
+ else
+ rb_erase(&he->rb_node_in, hists->entries_in);
--hists->nr_entries;
if (!he->filtered)
@@ -367,6 +370,25 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template,
if (symbol_conf.use_callchain)
callchain_init(he->callchain);
+ if (he->raw_data) {
+ he->raw_data = memdup(he->raw_data, he->raw_size);
+
+ if (he->raw_data == NULL) {
+ map__put(he->ms.map);
+ if (he->branch_info) {
+ map__put(he->branch_info->from.map);
+ map__put(he->branch_info->to.map);
+ free(he->branch_info);
+ }
+ if (he->mem_info) {
+ map__put(he->mem_info->iaddr.map);
+ map__put(he->mem_info->daddr.map);
+ }
+ free(he->stat_acc);
+ free(he);
+ return NULL;
+ }
+ }
INIT_LIST_HEAD(&he->pairs.node);
thread__get(he->thread);
}
@@ -459,7 +481,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
struct symbol *sym_parent,
struct branch_info *bi,
struct mem_info *mi,
- u64 period, u64 weight, u64 transaction,
+ struct perf_sample *sample,
bool sample_self)
{
struct hist_entry entry = {
@@ -476,15 +498,17 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
.level = al->level,
.stat = {
.nr_events = 1,
- .period = period,
- .weight = weight,
+ .period = sample->period,
+ .weight = sample->weight,
},
.parent = sym_parent,
.filtered = symbol__parent_filter(sym_parent) | al->filtered,
.hists = hists,
.branch_info = bi,
.mem_info = mi,
- .transaction = transaction,
+ .transaction = sample->transaction,
+ .raw_data = sample->raw_data,
+ .raw_size = sample->raw_size,
};
return hists__findnew_entry(hists, &entry, al, sample_self);
@@ -524,12 +548,13 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al
u64 cost;
struct mem_info *mi = iter->priv;
struct hists *hists = evsel__hists(iter->evsel);
+ struct perf_sample *sample = iter->sample;
struct hist_entry *he;
if (mi == NULL)
return -EINVAL;
- cost = iter->sample->weight;
+ cost = sample->weight;
if (!cost)
cost = 1;
@@ -540,8 +565,10 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al
* and this is indirectly achieved by passing period=weight here
* and the he_stat__add_period() function.
*/
+ sample->period = cost;
+
he = __hists__add_entry(hists, al, iter->parent, NULL, mi,
- cost, cost, 0, true);
+ sample, true);
if (!he)
return -ENOMEM;
@@ -628,6 +655,7 @@ iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *a
struct branch_info *bi;
struct perf_evsel *evsel = iter->evsel;
struct hists *hists = evsel__hists(evsel);
+ struct perf_sample *sample = iter->sample;
struct hist_entry *he = NULL;
int i = iter->curr;
int err = 0;
@@ -641,9 +669,11 @@ iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *a
* The report shows the percentage of total branches captured
* and not events sampled. Thus we use a pseudo period of 1.
*/
+ sample->period = 1;
+ sample->weight = bi->flags.cycles ? bi->flags.cycles : 1;
+
he = __hists__add_entry(hists, al, iter->parent, &bi[i], NULL,
- 1, bi->flags.cycles ? bi->flags.cycles : 1,
- 0, true);
+ sample, true);
if (he == NULL)
return -ENOMEM;
@@ -680,8 +710,7 @@ iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location
struct hist_entry *he;
he = __hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL,
- sample->period, sample->weight,
- sample->transaction, true);
+ sample, true);
if (he == NULL)
return -ENOMEM;
@@ -742,8 +771,7 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter,
int err = 0;
he = __hists__add_entry(hists, al, iter->parent, NULL, NULL,
- sample->period, sample->weight,
- sample->transaction, true);
+ sample, true);
if (he == NULL)
return -ENOMEM;
@@ -795,6 +823,8 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
.sym = al->sym,
},
.parent = iter->parent,
+ .raw_data = sample->raw_data,
+ .raw_size = sample->raw_size,
};
int i;
struct callchain_cursor cursor;
@@ -816,8 +846,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
}
he = __hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL,
- sample->period, sample->weight,
- sample->transaction, false);
+ sample, false);
if (he == NULL)
return -ENOMEM;
@@ -924,9 +953,6 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
int64_t cmp = 0;
perf_hpp__for_each_sort_list(fmt) {
- if (perf_hpp__should_skip(fmt))
- continue;
-
cmp = fmt->cmp(fmt, left, right);
if (cmp)
break;
@@ -942,9 +968,6 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
int64_t cmp = 0;
perf_hpp__for_each_sort_list(fmt) {
- if (perf_hpp__should_skip(fmt))
- continue;
-
cmp = fmt->collapse(fmt, left, right);
if (cmp)
break;
@@ -975,6 +998,8 @@ void hist_entry__delete(struct hist_entry *he)
if (he->srcfile && he->srcfile[0])
free(he->srcfile);
free_callchain(he->callchain);
+ free(he->trace_output);
+ free(he->raw_data);
free(he);
}
@@ -982,9 +1007,8 @@ void hist_entry__delete(struct hist_entry *he)
* collapse the histogram
*/
-static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,
- struct rb_root *root,
- struct hist_entry *he)
+bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,
+ struct rb_root *root, struct hist_entry *he)
{
struct rb_node **p = &root->rb_node;
struct rb_node *parent = NULL;
@@ -1024,7 +1048,7 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,
return true;
}
-static struct rb_root *hists__get_rotate_entries_in(struct hists *hists)
+struct rb_root *hists__get_rotate_entries_in(struct hists *hists)
{
struct rb_root *root;
@@ -1088,7 +1112,7 @@ static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b)
int64_t cmp = 0;
perf_hpp__for_each_sort_list(fmt) {
- if (perf_hpp__should_skip(fmt))
+ if (perf_hpp__should_skip(fmt, a->hists))
continue;
cmp = fmt->sort(fmt, a, b);
@@ -1559,10 +1583,8 @@ int perf_hist_config(const char *var, const char *value)
return 0;
}
-static int hists_evsel__init(struct perf_evsel *evsel)
+int __hists__init(struct hists *hists)
{
- struct hists *hists = evsel__hists(evsel);
-
memset(hists, 0, sizeof(*hists));
hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT;
hists->entries_in = &hists->entries_in_array[0];
@@ -1573,6 +1595,43 @@ static int hists_evsel__init(struct perf_evsel *evsel)
return 0;
}
+static void hists__delete_remaining_entries(struct rb_root *root)
+{
+ struct rb_node *node;
+ struct hist_entry *he;
+
+ while (!RB_EMPTY_ROOT(root)) {
+ node = rb_first(root);
+ rb_erase(node, root);
+
+ he = rb_entry(node, struct hist_entry, rb_node_in);
+ hist_entry__delete(he);
+ }
+}
+
+static void hists__delete_all_entries(struct hists *hists)
+{
+ hists__delete_entries(hists);
+ hists__delete_remaining_entries(&hists->entries_in_array[0]);
+ hists__delete_remaining_entries(&hists->entries_in_array[1]);
+ hists__delete_remaining_entries(&hists->entries_collapsed);
+}
+
+static void hists_evsel__exit(struct perf_evsel *evsel)
+{
+ struct hists *hists = evsel__hists(evsel);
+
+ hists__delete_all_entries(hists);
+}
+
+static int hists_evsel__init(struct perf_evsel *evsel)
+{
+ struct hists *hists = evsel__hists(evsel);
+
+ __hists__init(hists);
+ return 0;
+}
+
/*
* XXX We probably need a hists_evsel__exit() to free the hist_entries
* stored in the rbtree...
@@ -1581,7 +1640,8 @@ static int hists_evsel__init(struct perf_evsel *evsel)
int hists__init(void)
{
int err = perf_evsel__object_config(sizeof(struct hists_evsel),
- hists_evsel__init, NULL);
+ hists_evsel__init,
+ hists_evsel__exit);
if (err)
fputs("FATAL ERROR: Couldn't setup hists class\n", stderr);
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index a48a2078d288..d4ec4822a103 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -52,6 +52,7 @@ enum hist_column {
HISTC_MEM_IADDR_SYMBOL,
HISTC_TRANSACTION,
HISTC_CYCLES,
+ HISTC_TRACE,
HISTC_NR_COLS, /* Last entry */
};
@@ -114,8 +115,8 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
struct addr_location *al,
struct symbol *parent,
struct branch_info *bi,
- struct mem_info *mi, u64 period,
- u64 weight, u64 transaction,
+ struct mem_info *mi,
+ struct perf_sample *sample,
bool sample_self);
int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
int max_stack_depth, void *arg);
@@ -184,6 +185,11 @@ static inline struct hists *evsel__hists(struct perf_evsel *evsel)
}
int hists__init(void);
+int __hists__init(struct hists *hists);
+
+struct rb_root *hists__get_rotate_entries_in(struct hists *hists);
+bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,
+ struct rb_root *root, struct hist_entry *he);
struct perf_hpp {
char *buf;
@@ -261,10 +267,20 @@ void perf_hpp__append_sort_keys(void);
bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format);
bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b);
+bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *format);
+bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt, struct hists *hists);
-static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format)
+static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format,
+ struct hists *hists)
{
- return format->elide;
+ if (format->elide)
+ return true;
+
+ if (perf_hpp__is_dynamic_entry(format) &&
+ !perf_hpp__defined_dynamic_entry(format, hists))
+ return true;
+
+ return false;
}
void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists);
diff --git a/tools/perf/util/include/linux/string.h b/tools/perf/util/include/linux/string.h
deleted file mode 100644
index 6f19c548ecc0..000000000000
--- a/tools/perf/util/include/linux/string.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#include <string.h>
-
-void *memdup(const void *src, size_t len);
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c
index 97f963a3dcb9..81a2eb77ba7f 100644
--- a/tools/perf/util/intel-pt.c
+++ b/tools/perf/util/intel-pt.c
@@ -1744,7 +1744,7 @@ static void intel_pt_free(struct perf_session *session)
auxtrace_heap__free(&pt->heap);
intel_pt_free_events(session);
session->auxtrace = NULL;
- thread__delete(pt->unknown_thread);
+ thread__put(pt->unknown_thread);
free(pt);
}
@@ -2153,7 +2153,7 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
return 0;
err_delete_thread:
- thread__delete(pt->unknown_thread);
+ thread__zput(pt->unknown_thread);
err_free_queues:
intel_pt_log_disable();
auxtrace_queues__free(&pt->queues);
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 8b303ff20289..ad79297c76c8 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -25,6 +25,7 @@ static void dsos__init(struct dsos *dsos)
int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
{
+ memset(machine, 0, sizeof(*machine));
map_groups__init(&machine->kmaps, machine);
RB_CLEAR_NODE(&machine->rb_node);
dsos__init(&machine->dsos);
@@ -44,6 +45,8 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
machine->comm_exec = false;
machine->kernel_start = 0;
+ memset(machine->vmlinux_maps, 0, sizeof(machine->vmlinux_maps));
+
machine->root_dir = strdup(root_dir);
if (machine->root_dir == NULL)
return -ENOMEM;
@@ -122,6 +125,7 @@ void machine__delete_threads(struct machine *machine)
void machine__exit(struct machine *machine)
{
+ machine__destroy_kernel_maps(machine);
map_groups__exit(&machine->kmaps);
dsos__exit(&machine->dsos);
machine__exit_vdso(machine);
@@ -348,13 +352,18 @@ static void machine__update_thread_pid(struct machine *machine,
}
th->mg = map_groups__get(leader->mg);
-
+out_put:
+ thread__put(leader);
return;
-
out_err:
pr_err("Failed to join map groups for %d:%d\n", th->pid_, th->tid);
+ goto out_put;
}
+/*
+ * Caller must eventually drop thread->refcnt returned with a successfull
+ * lookup/new thread inserted.
+ */
static struct thread *____machine__findnew_thread(struct machine *machine,
pid_t pid, pid_t tid,
bool create)
@@ -372,7 +381,7 @@ static struct thread *____machine__findnew_thread(struct machine *machine,
if (th != NULL) {
if (th->tid == tid) {
machine__update_thread_pid(machine, th, pid);
- return th;
+ return thread__get(th);
}
machine->last_match = NULL;
@@ -385,7 +394,7 @@ static struct thread *____machine__findnew_thread(struct machine *machine,
if (th->tid == tid) {
machine->last_match = th;
machine__update_thread_pid(machine, th, pid);
- return th;
+ return thread__get(th);
}
if (tid < th->tid)
@@ -413,7 +422,7 @@ static struct thread *____machine__findnew_thread(struct machine *machine,
if (thread__init_map_groups(th, machine)) {
rb_erase_init(&th->rb_node, &machine->threads);
RB_CLEAR_NODE(&th->rb_node);
- thread__delete(th);
+ thread__put(th);
return NULL;
}
/*
@@ -437,7 +446,7 @@ struct thread *machine__findnew_thread(struct machine *machine, pid_t pid,
struct thread *th;
pthread_rwlock_wrlock(&machine->threads_lock);
- th = thread__get(__machine__findnew_thread(machine, pid, tid));
+ th = __machine__findnew_thread(machine, pid, tid);
pthread_rwlock_unlock(&machine->threads_lock);
return th;
}
@@ -447,7 +456,7 @@ struct thread *machine__find_thread(struct machine *machine, pid_t pid,
{
struct thread *th;
pthread_rwlock_rdlock(&machine->threads_lock);
- th = thread__get(____machine__findnew_thread(machine, pid, tid, false));
+ th = ____machine__findnew_thread(machine, pid, tid, false);
pthread_rwlock_unlock(&machine->threads_lock);
return th;
}
@@ -560,11 +569,29 @@ int machine__process_switch_event(struct machine *machine __maybe_unused,
return 0;
}
+static void dso__adjust_kmod_long_name(struct dso *dso, const char *filename)
+{
+ const char *dup_filename;
+
+ if (!filename || !dso || !dso->long_name)
+ return;
+ if (dso->long_name[0] != '[')
+ return;
+ if (!strchr(filename, '/'))
+ return;
+
+ dup_filename = strdup(filename);
+ if (!dup_filename)
+ return;
+
+ dso__set_long_name(dso, dup_filename, true);
+}
+
struct map *machine__findnew_module_map(struct machine *machine, u64 start,
const char *filename)
{
struct map *map = NULL;
- struct dso *dso;
+ struct dso *dso = NULL;
struct kmod_path m;
if (kmod_path__parse_name(&m, filename))
@@ -572,8 +599,15 @@ struct map *machine__findnew_module_map(struct machine *machine, u64 start,
map = map_groups__find_by_name(&machine->kmaps, MAP__FUNCTION,
m.name);
- if (map)
+ if (map) {
+ /*
+ * If the map's dso is an offline module, give dso__load()
+ * a chance to find the file path of that module by fixing
+ * long_name.
+ */
+ dso__adjust_kmod_long_name(map->dso, filename);
goto out;
+ }
dso = machine__findnew_module_dso(machine, &m, filename);
if (dso == NULL)
@@ -585,7 +619,11 @@ struct map *machine__findnew_module_map(struct machine *machine, u64 start,
map_groups__insert(&machine->kmaps, map);
+ /* Put the map here because map_groups__insert alread got it */
+ map__put(map);
out:
+ /* put the dso here, corresponding to machine__findnew_module_dso */
+ dso__put(dso);
free(m.name);
return map;
}
@@ -740,6 +778,9 @@ int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel)
enum map_type type;
u64 start = machine__get_running_kernel_start(machine, NULL);
+ /* In case of renewal the kernel map, destroy previous one */
+ machine__destroy_kernel_maps(machine);
+
for (type = 0; type < MAP__NR_TYPES; ++type) {
struct kmap *kmap;
struct map *map;
@@ -788,6 +829,7 @@ void machine__destroy_kernel_maps(struct machine *machine)
kmap->ref_reloc_sym = NULL;
}
+ map__put(machine->vmlinux_maps[type]);
machine->vmlinux_maps[type] = NULL;
}
}
@@ -1084,11 +1126,14 @@ int machine__create_kernel_maps(struct machine *machine)
struct dso *kernel = machine__get_kernel(machine);
const char *name;
u64 addr = machine__get_running_kernel_start(machine, &name);
- if (!addr)
+ int ret;
+
+ if (!addr || kernel == NULL)
return -1;
- if (kernel == NULL ||
- __machine__create_kernel_maps(machine, kernel) < 0)
+ ret = __machine__create_kernel_maps(machine, kernel);
+ dso__put(kernel);
+ if (ret < 0)
return -1;
if (symbol_conf.use_modules && machine__create_modules(machine) < 0) {
@@ -1609,6 +1654,8 @@ static int add_callchain_ip(struct thread *thread,
}
}
+ if (symbol_conf.hide_unresolved && al.sym == NULL)
+ return 0;
return callchain_cursor_append(&callchain_cursor, al.addr, al.map, al.sym);
}
@@ -1863,6 +1910,9 @@ check_calls:
static int unwind_entry(struct unwind_entry *entry, void *arg)
{
struct callchain_cursor *cursor = arg;
+
+ if (symbol_conf.hide_unresolved && entry->sym == NULL)
+ return 0;
return callchain_cursor_append(cursor, entry->ip,
entry->map, entry->sym);
}
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index afc6b56cf749..171b6d10a04b 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -26,8 +26,8 @@ const char *map_type__name[MAP__NR_TYPES] = {
static inline int is_anon_memory(const char *filename)
{
return !strcmp(filename, "//anon") ||
- !strcmp(filename, "/dev/zero (deleted)") ||
- !strcmp(filename, "/anon_hugepage (deleted)");
+ !strncmp(filename, "/dev/zero", sizeof("/dev/zero") - 1) ||
+ !strncmp(filename, "/anon_hugepage", sizeof("/anon_hugepage") - 1);
}
static inline int is_no_dso_memory(const char *filename)
@@ -691,6 +691,7 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp
__map_groups__insert(pos->groups, before);
if (verbose >= 2)
map__fprintf(before, fp);
+ map__put(before);
}
if (map->end < pos->end) {
@@ -705,6 +706,7 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp
__map_groups__insert(pos->groups, after);
if (verbose >= 2)
map__fprintf(after, fp);
+ map__put(after);
}
put_map:
map__put(pos);
@@ -742,6 +744,7 @@ int map_groups__clone(struct map_groups *mg,
if (new == NULL)
goto out_unlock;
map_groups__insert(mg, new);
+ map__put(new);
}
err = 0;
diff --git a/tools/perf/util/parse-branch-options.c b/tools/perf/util/parse-branch-options.c
index 355eecf6bf59..afc088dd7d20 100644
--- a/tools/perf/util/parse-branch-options.c
+++ b/tools/perf/util/parse-branch-options.c
@@ -1,7 +1,7 @@
#include "perf.h"
#include "util/util.h"
#include "util/debug.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "util/parse-branch-options.h"
#define BRANCH_OPT(n, m) \
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index e48d9da75707..4f7b0efdde2f 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -4,9 +4,9 @@
#include "../perf.h"
#include "evlist.h"
#include "evsel.h"
-#include "parse-options.h"
+#include <subcmd/parse-options.h>
#include "parse-events.h"
-#include "exec_cmd.h"
+#include <subcmd/exec-cmd.h>
#include "string.h"
#include "symbol.h"
#include "cache.h"
@@ -124,6 +124,10 @@ struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = {
.symbol = "dummy",
.alias = "",
},
+ [PERF_COUNT_SW_BPF_OUTPUT] = {
+ .symbol = "bpf-output",
+ .alias = "",
+ },
};
#define __PERF_EVENT_FIELD(config, name) \
@@ -1879,7 +1883,7 @@ restart:
for (i = 0; i < max; i++, syms++) {
- if (event_glob != NULL &&
+ if (event_glob != NULL && syms->symbol != NULL &&
!(strglobmatch(syms->symbol, event_glob) ||
(syms->alias && strglobmatch(syms->alias, event_glob))))
continue;
diff --git a/tools/perf/util/parse-regs-options.c b/tools/perf/util/parse-regs-options.c
index 4f2c1c255d81..646ecf736aad 100644
--- a/tools/perf/util/parse-regs-options.c
+++ b/tools/perf/util/parse-regs-options.c
@@ -1,7 +1,7 @@
#include "perf.h"
#include "util/util.h"
#include "util/debug.h"
-#include "util/parse-options.h"
+#include <subcmd/parse-options.h>
#include "util/parse-regs-options.h"
int
diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c
index 5d13cb45b317..3654d964e49d 100644
--- a/tools/perf/util/path.c
+++ b/tools/perf/util/path.c
@@ -22,24 +22,6 @@ static const char *get_perf_dir(void)
return ".";
}
-/*
- * If libc has strlcpy() then that version will override this
- * implementation:
- */
-size_t __weak strlcpy(char *dest, const char *src, size_t size)
-{
- size_t ret = strlen(src);
-
- if (size) {
- size_t len = (ret >= size) ? size - 1 : ret;
-
- memcpy(dest, src, len);
- dest[len] = '\0';
- }
-
- return ret;
-}
-
static char *get_pathname(void)
{
static char pathname_array[4][PATH_MAX];
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index e4b173dec4b9..b597bcc8fc78 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -220,6 +220,7 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,
alias->scale = 1.0;
alias->unit[0] = '\0';
alias->per_pkg = false;
+ alias->snapshot = false;
ret = parse_events_terms(&alias->terms, val);
if (ret) {
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 03875f9154e7..93996ec4bbe3 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -2326,8 +2326,11 @@ static int get_new_event_name(char *buf, size_t len, const char *base,
goto out;
if (!allow_suffix) {
- pr_warning("Error: event \"%s\" already exists. "
- "(Use -f to force duplicates.)\n", buf);
+ pr_warning("Error: event \"%s\" already exists.\n"
+ " Hint: Remove existing event by 'perf probe -d'\n"
+ " or force duplicates by 'perf probe -f'\n"
+ " or set 'force=yes' in BPF source.\n",
+ buf);
ret = -EEXIST;
goto out;
}
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 05012bb178d7..2be10fb27172 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -654,6 +654,7 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod,
static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf)
{
Dwarf_Attribute fb_attr;
+ Dwarf_Frame *frame = NULL;
size_t nops;
int ret;
@@ -686,11 +687,11 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf)
#if _ELFUTILS_PREREQ(0, 142)
} else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa &&
pf->cfi != NULL) {
- Dwarf_Frame *frame;
if (dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame) != 0 ||
dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) {
pr_warning("Failed to get call frame on 0x%jx\n",
(uintmax_t)pf->addr);
+ free(frame);
return -ENOENT;
}
#endif
@@ -699,7 +700,8 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf)
/* Call finder's callback handler */
ret = pf->callback(sc_die, pf);
- /* *pf->fb_ops will be cached in libdw. Don't free it. */
+ /* Since *pf->fb_ops can be a part of frame. we should free it here. */
+ free(frame);
pf->fb_ops = NULL;
return ret;
diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources
index 51be28b1bca2..8162ba0e2e57 100644
--- a/tools/perf/util/python-ext-sources
+++ b/tools/perf/util/python-ext-sources
@@ -10,6 +10,8 @@ util/ctype.c
util/evlist.c
util/evsel.c
util/cpumap.c
+../lib/bitmap.c
+../lib/find_bit.c
../lib/hweight.c
util/thread_map.c
util/util.c
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index a8e825fca42a..d72fafc1c800 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -41,6 +41,9 @@
#include "../thread-stack.h"
#include "../trace-event.h"
#include "../machine.h"
+#include "thread_map.h"
+#include "cpumap.h"
+#include "stat.h"
PyMODINIT_FUNC initperf_trace_context(void);
@@ -859,6 +862,104 @@ static void python_process_event(union perf_event *event,
}
}
+static void get_handler_name(char *str, size_t size,
+ struct perf_evsel *evsel)
+{
+ char *p = str;
+
+ scnprintf(str, size, "stat__%s", perf_evsel__name(evsel));
+
+ while ((p = strchr(p, ':'))) {
+ *p = '_';
+ p++;
+ }
+}
+
+static void
+process_stat(struct perf_evsel *counter, int cpu, int thread, u64 tstamp,
+ struct perf_counts_values *count)
+{
+ PyObject *handler, *t;
+ static char handler_name[256];
+ int n = 0;
+
+ t = PyTuple_New(MAX_FIELDS);
+ if (!t)
+ Py_FatalError("couldn't create Python tuple");
+
+ get_handler_name(handler_name, sizeof(handler_name),
+ counter);
+
+ handler = get_handler(handler_name);
+ if (!handler) {
+ pr_debug("can't find python handler %s\n", handler_name);
+ return;
+ }
+
+ PyTuple_SetItem(t, n++, PyInt_FromLong(cpu));
+ PyTuple_SetItem(t, n++, PyInt_FromLong(thread));
+
+ tuple_set_u64(t, n++, tstamp);
+ tuple_set_u64(t, n++, count->val);
+ tuple_set_u64(t, n++, count->ena);
+ tuple_set_u64(t, n++, count->run);
+
+ if (_PyTuple_Resize(&t, n) == -1)
+ Py_FatalError("error resizing Python tuple");
+
+ call_object(handler, t, handler_name);
+
+ Py_DECREF(t);
+}
+
+static void python_process_stat(struct perf_stat_config *config,
+ struct perf_evsel *counter, u64 tstamp)
+{
+ struct thread_map *threads = counter->threads;
+ struct cpu_map *cpus = counter->cpus;
+ int cpu, thread;
+
+ if (config->aggr_mode == AGGR_GLOBAL) {
+ process_stat(counter, -1, -1, tstamp,
+ &counter->counts->aggr);
+ return;
+ }
+
+ for (thread = 0; thread < threads->nr; thread++) {
+ for (cpu = 0; cpu < cpus->nr; cpu++) {
+ process_stat(counter, cpus->map[cpu],
+ thread_map__pid(threads, thread), tstamp,
+ perf_counts(counter->counts, cpu, thread));
+ }
+ }
+}
+
+static void python_process_stat_interval(u64 tstamp)
+{
+ PyObject *handler, *t;
+ static const char handler_name[] = "stat__interval";
+ int n = 0;
+
+ t = PyTuple_New(MAX_FIELDS);
+ if (!t)
+ Py_FatalError("couldn't create Python tuple");
+
+ handler = get_handler(handler_name);
+ if (!handler) {
+ pr_debug("can't find python handler %s\n", handler_name);
+ return;
+ }
+
+ tuple_set_u64(t, n++, tstamp);
+
+ if (_PyTuple_Resize(&t, n) == -1)
+ Py_FatalError("error resizing Python tuple");
+
+ call_object(handler, t, handler_name);
+
+ Py_DECREF(t);
+}
+
static int run_start_sub(void)
{
main_module = PyImport_AddModule("__main__");
@@ -1201,10 +1302,12 @@ static int python_generate_script(struct pevent *pevent, const char *outfile)
}
struct scripting_ops python_scripting_ops = {
- .name = "Python",
- .start_script = python_start_script,
- .flush_script = python_flush_script,
- .stop_script = python_stop_script,
- .process_event = python_process_event,
- .generate_script = python_generate_script,
+ .name = "Python",
+ .start_script = python_start_script,
+ .flush_script = python_flush_script,
+ .stop_script = python_stop_script,
+ .process_event = python_process_event,
+ .process_stat = python_process_stat,
+ .process_stat_interval = python_process_stat_interval,
+ .generate_script = python_generate_script,
};
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index c35ffdd360fe..d5636ba94b20 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -17,6 +17,7 @@
#include "asm/bug.h"
#include "auxtrace.h"
#include "thread-stack.h"
+#include "stat.h"
static int perf_session__deliver_event(struct perf_session *session,
union perf_event *event,
@@ -36,6 +37,9 @@ static int perf_session__open(struct perf_session *session)
if (perf_data_file__is_pipe(file))
return 0;
+ if (perf_header__has_feat(&session->header, HEADER_STAT))
+ return 0;
+
if (!perf_evlist__valid_sample_type(session->evlist)) {
pr_err("non matching sample_type\n");
return -1;
@@ -205,6 +209,18 @@ static int process_event_synth_attr_stub(struct perf_tool *tool __maybe_unused,
return 0;
}
+static int process_event_synth_event_update_stub(struct perf_tool *tool __maybe_unused,
+ union perf_event *event __maybe_unused,
+ struct perf_evlist **pevlist
+ __maybe_unused)
+{
+ if (dump_trace)
+ perf_event__fprintf_event_update(event, stdout);
+
+ dump_printf(": unhandled!\n");
+ return 0;
+}
+
static int process_event_sample_stub(struct perf_tool *tool __maybe_unused,
union perf_event *event __maybe_unused,
struct perf_sample *sample __maybe_unused,
@@ -296,6 +312,67 @@ int process_event_auxtrace_error_stub(struct perf_tool *tool __maybe_unused,
return 0;
}
+
+static
+int process_event_thread_map_stub(struct perf_tool *tool __maybe_unused,
+ union perf_event *event __maybe_unused,
+ struct perf_session *session __maybe_unused)
+{
+ if (dump_trace)
+ perf_event__fprintf_thread_map(event, stdout);
+
+ dump_printf(": unhandled!\n");
+ return 0;
+}
+
+static
+int process_event_cpu_map_stub(struct perf_tool *tool __maybe_unused,
+ union perf_event *event __maybe_unused,
+ struct perf_session *session __maybe_unused)
+{
+ if (dump_trace)
+ perf_event__fprintf_cpu_map(event, stdout);
+
+ dump_printf(": unhandled!\n");
+ return 0;
+}
+
+static
+int process_event_stat_config_stub(struct perf_tool *tool __maybe_unused,
+ union perf_event *event __maybe_unused,
+ struct perf_session *session __maybe_unused)
+{
+ if (dump_trace)
+ perf_event__fprintf_stat_config(event, stdout);
+
+ dump_printf(": unhandled!\n");
+ return 0;
+}
+
+static int process_stat_stub(struct perf_tool *tool __maybe_unused,
+ union perf_event *event __maybe_unused,
+ struct perf_session *perf_session
+ __maybe_unused)
+{
+ if (dump_trace)
+ perf_event__fprintf_stat(event, stdout);
+
+ dump_printf(": unhandled!\n");
+ return 0;
+}
+
+static int process_stat_round_stub(struct perf_tool *tool __maybe_unused,
+ union perf_event *event __maybe_unused,
+ struct perf_session *perf_session
+ __maybe_unused)
+{
+ if (dump_trace)
+ perf_event__fprintf_stat_round(event, stdout);
+
+ dump_printf(": unhandled!\n");
+ return 0;
+}
+
void perf_tool__fill_defaults(struct perf_tool *tool)
{
if (tool->sample == NULL)
@@ -328,6 +405,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
tool->unthrottle = process_event_stub;
if (tool->attr == NULL)
tool->attr = process_event_synth_attr_stub;
+ if (tool->event_update == NULL)
+ tool->event_update = process_event_synth_event_update_stub;
if (tool->tracing_data == NULL)
tool->tracing_data = process_event_synth_tracing_data_stub;
if (tool->build_id == NULL)
@@ -346,6 +425,16 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
tool->auxtrace = process_event_auxtrace_stub;
if (tool->auxtrace_error == NULL)
tool->auxtrace_error = process_event_auxtrace_error_stub;
+ if (tool->thread_map == NULL)
+ tool->thread_map = process_event_thread_map_stub;
+ if (tool->cpu_map == NULL)
+ tool->cpu_map = process_event_cpu_map_stub;
+ if (tool->stat_config == NULL)
+ tool->stat_config = process_event_stat_config_stub;
+ if (tool->stat == NULL)
+ tool->stat = process_stat_stub;
+ if (tool->stat_round == NULL)
+ tool->stat_round = process_stat_round_stub;
}
static void swap_sample_id_all(union perf_event *event, void *data)
@@ -569,6 +658,13 @@ static void perf_event__hdr_attr_swap(union perf_event *event,
mem_bswap_64(event->attr.id, size);
}
+static void perf_event__event_update_swap(union perf_event *event,
+ bool sample_id_all __maybe_unused)
+{
+ event->event_update.type = bswap_64(event->event_update.type);
+ event->event_update.id = bswap_64(event->event_update.id);
+}
+
static void perf_event__event_type_swap(union perf_event *event,
bool sample_id_all __maybe_unused)
{
@@ -616,6 +712,81 @@ static void perf_event__auxtrace_error_swap(union perf_event *event,
event->auxtrace_error.ip = bswap_64(event->auxtrace_error.ip);
}
+static void perf_event__thread_map_swap(union perf_event *event,
+ bool sample_id_all __maybe_unused)
+{
+ unsigned i;
+
+ event->thread_map.nr = bswap_64(event->thread_map.nr);
+
+ for (i = 0; i < event->thread_map.nr; i++)
+ event->thread_map.entries[i].pid = bswap_64(event->thread_map.entries[i].pid);
+}
+
+static void perf_event__cpu_map_swap(union perf_event *event,
+ bool sample_id_all __maybe_unused)
+{
+ struct cpu_map_data *data = &event->cpu_map.data;
+ struct cpu_map_entries *cpus;
+ struct cpu_map_mask *mask;
+ unsigned i;
+
+ data->type = bswap_64(data->type);
+
+ switch (data->type) {
+ case PERF_CPU_MAP__CPUS:
+ cpus = (struct cpu_map_entries *)data->data;
+
+ cpus->nr = bswap_16(cpus->nr);
+
+ for (i = 0; i < cpus->nr; i++)
+ cpus->cpu[i] = bswap_16(cpus->cpu[i]);
+ break;
+ case PERF_CPU_MAP__MASK:
+ mask = (struct cpu_map_mask *) data->data;
+
+ mask->nr = bswap_16(mask->nr);
+ mask->long_size = bswap_16(mask->long_size);
+
+ switch (mask->long_size) {
+ case 4: mem_bswap_32(&mask->mask, mask->nr); break;
+ case 8: mem_bswap_64(&mask->mask, mask->nr); break;
+ default:
+ pr_err("cpu_map swap: unsupported long size\n");
+ }
+ default:
+ break;
+ }
+}
+
+static void perf_event__stat_config_swap(union perf_event *event,
+ bool sample_id_all __maybe_unused)
+{
+ u64 size;
+
+ size = event->stat_config.nr * sizeof(event->stat_config.data[0]);
+ size += 1; /* nr item itself */
+ mem_bswap_64(&event->stat_config.nr, size);
+}
+
+static void perf_event__stat_swap(union perf_event *event,
+ bool sample_id_all __maybe_unused)
+{
+ event->stat.id = bswap_64(event->stat.id);
+ event->stat.thread = bswap_32(event->stat.thread);
+ event->stat.cpu = bswap_32(event->stat.cpu);
+ event->stat.val = bswap_64(event->stat.val);
+ event->stat.ena = bswap_64(event->stat.ena);
+ event->stat.run = bswap_64(event->stat.run);
+}
+
+static void perf_event__stat_round_swap(union perf_event *event,
+ bool sample_id_all __maybe_unused)
+{
+ event->stat_round.type = bswap_64(event->stat_round.type);
+ event->stat_round.time = bswap_64(event->stat_round.time);
+}
+
typedef void (*perf_event__swap_op)(union perf_event *event,
bool sample_id_all);
@@ -643,6 +814,12 @@ static perf_event__swap_op perf_event__swap_ops[] = {
[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_THREAD_MAP] = perf_event__thread_map_swap,
+ [PERF_RECORD_CPU_MAP] = perf_event__cpu_map_swap,
+ [PERF_RECORD_STAT_CONFIG] = perf_event__stat_config_swap,
+ [PERF_RECORD_STAT] = perf_event__stat_swap,
+ [PERF_RECORD_STAT_ROUND] = perf_event__stat_round_swap,
+ [PERF_RECORD_EVENT_UPDATE] = perf_event__event_update_swap,
[PERF_RECORD_HEADER_MAX] = NULL,
};
@@ -1154,6 +1331,8 @@ static s64 perf_session__process_user_event(struct perf_session *session,
perf_session__set_comm_exec(session);
}
return err;
+ case PERF_RECORD_EVENT_UPDATE:
+ return tool->event_update(tool, event, &session->evlist);
case PERF_RECORD_HEADER_EVENT_TYPE:
/*
* Depreceated, but we need to handle it for sake
@@ -1179,6 +1358,16 @@ static s64 perf_session__process_user_event(struct perf_session *session,
case PERF_RECORD_AUXTRACE_ERROR:
perf_session__auxtrace_error_inc(session, event);
return tool->auxtrace_error(tool, event, session);
+ case PERF_RECORD_THREAD_MAP:
+ return tool->thread_map(tool, event, session);
+ case PERF_RECORD_CPU_MAP:
+ return tool->cpu_map(tool, event, session);
+ case PERF_RECORD_STAT_CONFIG:
+ return tool->stat_config(tool, event, session);
+ case PERF_RECORD_STAT:
+ return tool->stat(tool, event, session);
+ case PERF_RECORD_STAT_ROUND:
+ return tool->stat_round(tool, event, session);
default:
return -EINVAL;
}
@@ -1311,17 +1500,20 @@ struct thread *perf_session__findnew(struct perf_session *session, pid_t pid)
return machine__findnew_thread(&session->machines.host, -1, pid);
}
-struct thread *perf_session__register_idle_thread(struct perf_session *session)
+int perf_session__register_idle_thread(struct perf_session *session)
{
struct thread *thread;
+ int err = 0;
thread = machine__findnew_thread(&session->machines.host, 0, 0);
if (thread == NULL || thread__set_comm(thread, "swapper", 0)) {
pr_err("problem inserting idle task.\n");
- thread = NULL;
+ err = -1;
}
- return thread;
+ /* machine__findnew_thread() got the thread, so put it */
+ thread__put(thread);
+ return err;
}
static void perf_session__warn_about_errors(const struct perf_session *session)
@@ -1676,7 +1868,7 @@ int perf_session__process_events(struct perf_session *session)
u64 size = perf_data_file__size(session->file);
int err;
- if (perf_session__register_idle_thread(session) == NULL)
+ if (perf_session__register_idle_thread(session) < 0)
return -ENOMEM;
if (!perf_data_file__is_pipe(session->file))
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 3e900c0efc73..5f792e35d4c1 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -89,7 +89,7 @@ struct machine *perf_session__findnew_machine(struct perf_session *session, pid_
}
struct thread *perf_session__findnew(struct perf_session *session, pid_t pid);
-struct thread *perf_session__register_idle_thread(struct perf_session *session);
+int perf_session__register_idle_thread(struct perf_session *session);
size_t perf_session__fprintf(struct perf_session *session, FILE *fp);
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 2d8ccd4d9e1b..ec722346e6ff 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -4,6 +4,8 @@
#include "comm.h"
#include "symbol.h"
#include "evsel.h"
+#include "evlist.h"
+#include <traceevent/event-parse.h>
regex_t parent_regex;
const char default_parent_pattern[] = "^sys_|^do_page_fault";
@@ -13,6 +15,7 @@ const char default_branch_sort_order[] = "comm,dso_from,symbol_from,symbol_to,cy
const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
const char default_top_sort_order[] = "dso,symbol";
const char default_diff_sort_order[] = "dso,symbol";
+const char default_tracepoint_sort_order[] = "trace";
const char *sort_order;
const char *field_order;
regex_t ignore_callees_regex;
@@ -443,6 +446,70 @@ struct sort_entry sort_socket = {
.se_width_idx = HISTC_SOCKET,
};
+/* --sort trace */
+
+static char *get_trace_output(struct hist_entry *he)
+{
+ struct trace_seq seq;
+ struct perf_evsel *evsel;
+ struct pevent_record rec = {
+ .data = he->raw_data,
+ .size = he->raw_size,
+ };
+
+ evsel = hists_to_evsel(he->hists);
+
+ trace_seq_init(&seq);
+ if (symbol_conf.raw_trace) {
+ pevent_print_fields(&seq, he->raw_data, he->raw_size,
+ evsel->tp_format);
+ } else {
+ pevent_event_info(&seq, evsel->tp_format, &rec);
+ }
+ return seq.buffer;
+}
+
+static int64_t
+sort__trace_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ struct perf_evsel *evsel;
+
+ evsel = hists_to_evsel(left->hists);
+ if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
+ return 0;
+
+ if (left->trace_output == NULL)
+ left->trace_output = get_trace_output(left);
+ if (right->trace_output == NULL)
+ right->trace_output = get_trace_output(right);
+
+ hists__new_col_len(left->hists, HISTC_TRACE, strlen(left->trace_output));
+ hists__new_col_len(right->hists, HISTC_TRACE, strlen(right->trace_output));
+
+ return strcmp(right->trace_output, left->trace_output);
+}
+
+static int hist_entry__trace_snprintf(struct hist_entry *he, char *bf,
+ size_t size, unsigned int width)
+{
+ struct perf_evsel *evsel;
+
+ evsel = hists_to_evsel(he->hists);
+ if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
+ return scnprintf(bf, size, "%-*.*s", width, width, "N/A");
+
+ if (he->trace_output == NULL)
+ he->trace_output = get_trace_output(he);
+ return repsep_snprintf(bf, size, "%-*.*s", width, width, he->trace_output);
+}
+
+struct sort_entry sort_trace = {
+ .se_header = "Trace output",
+ .se_cmp = sort__trace_cmp,
+ .se_snprintf = hist_entry__trace_snprintf,
+ .se_width_idx = HISTC_TRACE,
+};
+
/* sort keys for branch stacks */
static int64_t
@@ -1312,6 +1379,7 @@ static struct sort_dimension common_sort_dimensions[] = {
DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
DIM(SORT_TRANSACTION, "transaction", sort_transaction),
+ DIM(SORT_TRACE, "trace", sort_trace),
};
#undef DIM
@@ -1529,6 +1597,455 @@ static int __sort_dimension__add_hpp_output(struct sort_dimension *sd)
return 0;
}
+struct hpp_dynamic_entry {
+ struct perf_hpp_fmt hpp;
+ struct perf_evsel *evsel;
+ struct format_field *field;
+ unsigned dynamic_len;
+ bool raw_trace;
+};
+
+static int hde_width(struct hpp_dynamic_entry *hde)
+{
+ if (!hde->hpp.len) {
+ int len = hde->dynamic_len;
+ int namelen = strlen(hde->field->name);
+ int fieldlen = hde->field->size;
+
+ if (namelen > len)
+ len = namelen;
+
+ if (!(hde->field->flags & FIELD_IS_STRING)) {
+ /* length for print hex numbers */
+ fieldlen = hde->field->size * 2 + 2;
+ }
+ if (fieldlen > len)
+ len = fieldlen;
+
+ hde->hpp.len = len;
+ }
+ return hde->hpp.len;
+}
+
+static void update_dynamic_len(struct hpp_dynamic_entry *hde,
+ struct hist_entry *he)
+{
+ char *str, *pos;
+ struct format_field *field = hde->field;
+ size_t namelen;
+ bool last = false;
+
+ if (hde->raw_trace)
+ return;
+
+ /* parse pretty print result and update max length */
+ if (!he->trace_output)
+ he->trace_output = get_trace_output(he);
+
+ namelen = strlen(field->name);
+ str = he->trace_output;
+
+ while (str) {
+ pos = strchr(str, ' ');
+ if (pos == NULL) {
+ last = true;
+ pos = str + strlen(str);
+ }
+
+ if (!strncmp(str, field->name, namelen)) {
+ size_t len;
+
+ str += namelen + 1;
+ len = pos - str;
+
+ if (len > hde->dynamic_len)
+ hde->dynamic_len = len;
+ break;
+ }
+
+ if (last)
+ str = NULL;
+ else
+ str = pos + 1;
+ }
+}
+
+static int __sort__hde_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
+ struct perf_evsel *evsel __maybe_unused)
+{
+ struct hpp_dynamic_entry *hde;
+ size_t len = fmt->user_len;
+
+ hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
+
+ if (!len)
+ len = hde_width(hde);
+
+ return scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, hde->field->name);
+}
+
+static int __sort__hde_width(struct perf_hpp_fmt *fmt,
+ struct perf_hpp *hpp __maybe_unused,
+ struct perf_evsel *evsel __maybe_unused)
+{
+ struct hpp_dynamic_entry *hde;
+ size_t len = fmt->user_len;
+
+ hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
+
+ if (!len)
+ len = hde_width(hde);
+
+ return len;
+}
+
+bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt, struct hists *hists)
+{
+ struct hpp_dynamic_entry *hde;
+
+ hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
+
+ return hists_to_evsel(hists) == hde->evsel;
+}
+
+static int __sort__hde_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
+ struct hist_entry *he)
+{
+ struct hpp_dynamic_entry *hde;
+ size_t len = fmt->user_len;
+ char *str, *pos;
+ struct format_field *field;
+ size_t namelen;
+ bool last = false;
+ int ret;
+
+ hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
+
+ if (!len)
+ len = hde_width(hde);
+
+ if (hde->raw_trace)
+ goto raw_field;
+
+ field = hde->field;
+ namelen = strlen(field->name);
+ str = he->trace_output;
+
+ while (str) {
+ pos = strchr(str, ' ');
+ if (pos == NULL) {
+ last = true;
+ pos = str + strlen(str);
+ }
+
+ if (!strncmp(str, field->name, namelen)) {
+ str += namelen + 1;
+ str = strndup(str, pos - str);
+
+ if (str == NULL)
+ return scnprintf(hpp->buf, hpp->size,
+ "%*.*s", len, len, "ERROR");
+ break;
+ }
+
+ if (last)
+ str = NULL;
+ else
+ str = pos + 1;
+ }
+
+ if (str == NULL) {
+ struct trace_seq seq;
+raw_field:
+ trace_seq_init(&seq);
+ pevent_print_field(&seq, he->raw_data, hde->field);
+ str = seq.buffer;
+ }
+
+ ret = scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, str);
+ free(str);
+ return ret;
+}
+
+static int64_t __sort__hde_cmp(struct perf_hpp_fmt *fmt,
+ struct hist_entry *a, struct hist_entry *b)
+{
+ struct hpp_dynamic_entry *hde;
+ struct format_field *field;
+ unsigned offset, size;
+
+ hde = container_of(fmt, struct hpp_dynamic_entry, hpp);
+
+ field = hde->field;
+ if (field->flags & FIELD_IS_DYNAMIC) {
+ unsigned long long dyn;
+
+ pevent_read_number_field(field, a->raw_data, &dyn);
+ offset = dyn & 0xffff;
+ size = (dyn >> 16) & 0xffff;
+
+ /* record max width for output */
+ if (size > hde->dynamic_len)
+ hde->dynamic_len = size;
+ } else {
+ offset = field->offset;
+ size = field->size;
+
+ update_dynamic_len(hde, a);
+ update_dynamic_len(hde, b);
+ }
+
+ return memcmp(a->raw_data + offset, b->raw_data + offset, size);
+}
+
+bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *fmt)
+{
+ return fmt->cmp == __sort__hde_cmp;
+}
+
+static struct hpp_dynamic_entry *
+__alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field)
+{
+ struct hpp_dynamic_entry *hde;
+
+ hde = malloc(sizeof(*hde));
+ if (hde == NULL) {
+ pr_debug("Memory allocation failed\n");
+ return NULL;
+ }
+
+ hde->evsel = evsel;
+ hde->field = field;
+ hde->dynamic_len = 0;
+
+ hde->hpp.name = field->name;
+ hde->hpp.header = __sort__hde_header;
+ hde->hpp.width = __sort__hde_width;
+ hde->hpp.entry = __sort__hde_entry;
+ hde->hpp.color = NULL;
+
+ hde->hpp.cmp = __sort__hde_cmp;
+ hde->hpp.collapse = __sort__hde_cmp;
+ hde->hpp.sort = __sort__hde_cmp;
+
+ INIT_LIST_HEAD(&hde->hpp.list);
+ INIT_LIST_HEAD(&hde->hpp.sort_list);
+ hde->hpp.elide = false;
+ hde->hpp.len = 0;
+ hde->hpp.user_len = 0;
+
+ return hde;
+}
+
+static int parse_field_name(char *str, char **event, char **field, char **opt)
+{
+ char *event_name, *field_name, *opt_name;
+
+ event_name = str;
+ field_name = strchr(str, '.');
+
+ if (field_name) {
+ *field_name++ = '\0';
+ } else {
+ event_name = NULL;
+ field_name = str;
+ }
+
+ opt_name = strchr(field_name, '/');
+ if (opt_name)
+ *opt_name++ = '\0';
+
+ *event = event_name;
+ *field = field_name;
+ *opt = opt_name;
+
+ return 0;
+}
+
+/* find match evsel using a given event name. The event name can be:
+ * 1. '%' + event index (e.g. '%1' for first event)
+ * 2. full event name (e.g. sched:sched_switch)
+ * 3. partial event name (should not contain ':')
+ */
+static struct perf_evsel *find_evsel(struct perf_evlist *evlist, char *event_name)
+{
+ struct perf_evsel *evsel = NULL;
+ struct perf_evsel *pos;
+ bool full_name;
+
+ /* case 1 */
+ if (event_name[0] == '%') {
+ int nr = strtol(event_name+1, NULL, 0);
+
+ if (nr > evlist->nr_entries)
+ return NULL;
+
+ evsel = perf_evlist__first(evlist);
+ while (--nr > 0)
+ evsel = perf_evsel__next(evsel);
+
+ return evsel;
+ }
+
+ full_name = !!strchr(event_name, ':');
+ evlist__for_each(evlist, pos) {
+ /* case 2 */
+ if (full_name && !strcmp(pos->name, event_name))
+ return pos;
+ /* case 3 */
+ if (!full_name && strstr(pos->name, event_name)) {
+ if (evsel) {
+ pr_debug("'%s' event is ambiguous: it can be %s or %s\n",
+ event_name, evsel->name, pos->name);
+ return NULL;
+ }
+ evsel = pos;
+ }
+ }
+
+ return evsel;
+}
+
+static int __dynamic_dimension__add(struct perf_evsel *evsel,
+ struct format_field *field,
+ bool raw_trace)
+{
+ struct hpp_dynamic_entry *hde;
+
+ hde = __alloc_dynamic_entry(evsel, field);
+ if (hde == NULL)
+ return -ENOMEM;
+
+ hde->raw_trace = raw_trace;
+
+ perf_hpp__register_sort_field(&hde->hpp);
+ return 0;
+}
+
+static int add_evsel_fields(struct perf_evsel *evsel, bool raw_trace)
+{
+ int ret;
+ struct format_field *field;
+
+ field = evsel->tp_format->format.fields;
+ while (field) {
+ ret = __dynamic_dimension__add(evsel, field, raw_trace);
+ if (ret < 0)
+ return ret;
+
+ field = field->next;
+ }
+ return 0;
+}
+
+static int add_all_dynamic_fields(struct perf_evlist *evlist, bool raw_trace)
+{
+ int ret;
+ struct perf_evsel *evsel;
+
+ evlist__for_each(evlist, evsel) {
+ if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
+ continue;
+
+ ret = add_evsel_fields(evsel, raw_trace);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int add_all_matching_fields(struct perf_evlist *evlist,
+ char *field_name, bool raw_trace)
+{
+ int ret = -ESRCH;
+ struct perf_evsel *evsel;
+ struct format_field *field;
+
+ evlist__for_each(evlist, evsel) {
+ if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
+ continue;
+
+ field = pevent_find_any_field(evsel->tp_format, field_name);
+ if (field == NULL)
+ continue;
+
+ ret = __dynamic_dimension__add(evsel, field, raw_trace);
+ if (ret < 0)
+ break;
+ }
+ return ret;
+}
+
+static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok)
+{
+ char *str, *event_name, *field_name, *opt_name;
+ struct perf_evsel *evsel;
+ struct format_field *field;
+ bool raw_trace = symbol_conf.raw_trace;
+ int ret = 0;
+
+ if (evlist == NULL)
+ return -ENOENT;
+
+ str = strdup(tok);
+ if (str == NULL)
+ return -ENOMEM;
+
+ if (parse_field_name(str, &event_name, &field_name, &opt_name) < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (opt_name) {
+ if (strcmp(opt_name, "raw")) {
+ pr_debug("unsupported field option %s\n", opt_name);
+ ret = -EINVAL;
+ goto out;
+ }
+ raw_trace = true;
+ }
+
+ if (!strcmp(field_name, "trace_fields")) {
+ ret = add_all_dynamic_fields(evlist, raw_trace);
+ goto out;
+ }
+
+ if (event_name == NULL) {
+ ret = add_all_matching_fields(evlist, field_name, raw_trace);
+ goto out;
+ }
+
+ evsel = find_evsel(evlist, event_name);
+ if (evsel == NULL) {
+ pr_debug("Cannot find event: %s\n", event_name);
+ ret = -ENOENT;
+ goto out;
+ }
+
+ if (evsel->attr.type != PERF_TYPE_TRACEPOINT) {
+ pr_debug("%s is not a tracepoint event\n", event_name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!strcmp(field_name, "*")) {
+ ret = add_evsel_fields(evsel, raw_trace);
+ } else {
+ field = pevent_find_any_field(evsel->tp_format, field_name);
+ if (field == NULL) {
+ pr_debug("Cannot find event field for %s.%s\n",
+ event_name, field_name);
+ return -ENOENT;
+ }
+
+ ret = __dynamic_dimension__add(evsel, field, raw_trace);
+ }
+
+out:
+ free(str);
+ return ret;
+}
+
static int __sort_dimension__add(struct sort_dimension *sd)
{
if (sd->taken)
@@ -1583,7 +2100,8 @@ int hpp_dimension__add_output(unsigned col)
return __hpp_dimension__add_output(&hpp_sort_dimensions[col]);
}
-int sort_dimension__add(const char *tok)
+static int sort_dimension__add(const char *tok,
+ struct perf_evlist *evlist __maybe_unused)
{
unsigned int i;
@@ -1664,10 +2182,13 @@ int sort_dimension__add(const char *tok)
return 0;
}
+ if (!add_dynamic_entry(evlist, tok))
+ return 0;
+
return -ESRCH;
}
-static const char *get_default_sort_order(void)
+static const char *get_default_sort_order(struct perf_evlist *evlist)
{
const char *default_sort_orders[] = {
default_sort_order,
@@ -1675,14 +2196,33 @@ static const char *get_default_sort_order(void)
default_mem_sort_order,
default_top_sort_order,
default_diff_sort_order,
+ default_tracepoint_sort_order,
};
+ bool use_trace = true;
+ struct perf_evsel *evsel;
BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders));
+ if (evlist == NULL)
+ goto out_no_evlist;
+
+ evlist__for_each(evlist, evsel) {
+ if (evsel->attr.type != PERF_TYPE_TRACEPOINT) {
+ use_trace = false;
+ break;
+ }
+ }
+
+ if (use_trace) {
+ sort__mode = SORT_MODE__TRACEPOINT;
+ if (symbol_conf.raw_trace)
+ return "trace_fields";
+ }
+out_no_evlist:
return default_sort_orders[sort__mode];
}
-static int setup_sort_order(void)
+static int setup_sort_order(struct perf_evlist *evlist)
{
char *new_sort_order;
@@ -1703,7 +2243,7 @@ static int setup_sort_order(void)
* because it's checked over the rest of the code.
*/
if (asprintf(&new_sort_order, "%s,%s",
- get_default_sort_order(), sort_order + 1) < 0) {
+ get_default_sort_order(evlist), sort_order + 1) < 0) {
error("Not enough memory to set up --sort");
return -ENOMEM;
}
@@ -1712,13 +2252,41 @@ static int setup_sort_order(void)
return 0;
}
-static int __setup_sorting(void)
+/*
+ * Adds 'pre,' prefix into 'str' is 'pre' is
+ * not already part of 'str'.
+ */
+static char *prefix_if_not_in(const char *pre, char *str)
+{
+ char *n;
+
+ if (!str || strstr(str, pre))
+ return str;
+
+ if (asprintf(&n, "%s,%s", pre, str) < 0)
+ return NULL;
+
+ free(str);
+ return n;
+}
+
+static char *setup_overhead(char *keys)
+{
+ keys = prefix_if_not_in("overhead", keys);
+
+ if (symbol_conf.cumulate_callchain)
+ keys = prefix_if_not_in("overhead_children", keys);
+
+ return keys;
+}
+
+static int __setup_sorting(struct perf_evlist *evlist)
{
char *tmp, *tok, *str;
const char *sort_keys;
int ret = 0;
- ret = setup_sort_order();
+ ret = setup_sort_order(evlist);
if (ret)
return ret;
@@ -1732,7 +2300,7 @@ static int __setup_sorting(void)
return 0;
}
- sort_keys = get_default_sort_order();
+ sort_keys = get_default_sort_order(evlist);
}
str = strdup(sort_keys);
@@ -1741,9 +2309,20 @@ static int __setup_sorting(void)
return -ENOMEM;
}
+ /*
+ * Prepend overhead fields for backward compatibility.
+ */
+ if (!is_strict_order(field_order)) {
+ str = setup_overhead(str);
+ if (str == NULL) {
+ error("Not enough memory to setup overhead keys");
+ return -ENOMEM;
+ }
+ }
+
for (tok = strtok_r(str, ", ", &tmp);
tok; tok = strtok_r(NULL, ", ", &tmp)) {
- ret = sort_dimension__add(tok);
+ ret = sort_dimension__add(tok, evlist);
if (ret == -EINVAL) {
error("Invalid --sort key: `%s'", tok);
break;
@@ -1954,16 +2533,16 @@ out:
return ret;
}
-int setup_sorting(void)
+int setup_sorting(struct perf_evlist *evlist)
{
int err;
- err = __setup_sorting();
+ err = __setup_sorting(evlist);
if (err < 0)
return err;
if (parent_pattern != default_parent_pattern) {
- err = sort_dimension__add("parent");
+ err = sort_dimension__add("parent", evlist);
if (err < 0)
return err;
}
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 31228851e397..687bbb124428 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -18,7 +18,7 @@
#include "debug.h"
#include "header.h"
-#include "parse-options.h"
+#include <subcmd/parse-options.h>
#include "parse-events.h"
#include "hist.h"
#include "thread.h"
@@ -122,6 +122,9 @@ struct hist_entry {
struct branch_info *branch_info;
struct hists *hists;
struct mem_info *mem_info;
+ void *raw_data;
+ u32 raw_size;
+ void *trace_output;
struct callchain_root callchain[0]; /* must be last member */
};
@@ -164,6 +167,7 @@ enum sort_mode {
SORT_MODE__MEMORY,
SORT_MODE__TOP,
SORT_MODE__DIFF,
+ SORT_MODE__TRACEPOINT,
};
enum sort_type {
@@ -180,6 +184,7 @@ enum sort_type {
SORT_LOCAL_WEIGHT,
SORT_GLOBAL_WEIGHT,
SORT_TRANSACTION,
+ SORT_TRACE,
/* branch stack specific sort keys */
__SORT_BRANCH_STACK,
@@ -209,8 +214,6 @@ enum sort_type {
*/
struct sort_entry {
- struct list_head list;
-
const char *se_header;
int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *);
@@ -224,10 +227,11 @@ struct sort_entry {
extern struct sort_entry sort_thread;
extern struct list_head hist_entry__sort_list;
-int setup_sorting(void);
+struct perf_evlist;
+struct pevent;
+int setup_sorting(struct perf_evlist *evlist);
int setup_output_field(void);
void reset_output_field(void);
-extern int sort_dimension__add(const char *);
void sort__setup_elide(FILE *fp);
void perf_hpp__set_elide(int idx, bool elide);
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c
index 2d9d8306dbd3..2f901d15e063 100644
--- a/tools/perf/util/stat.c
+++ b/tools/perf/util/stat.c
@@ -341,3 +341,65 @@ int perf_stat_process_counter(struct perf_stat_config *config,
return 0;
}
+
+int perf_event__process_stat_event(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_session *session)
+{
+ struct perf_counts_values count;
+ struct stat_event *st = &event->stat;
+ struct perf_evsel *counter;
+
+ count.val = st->val;
+ count.ena = st->ena;
+ count.run = st->run;
+
+ counter = perf_evlist__id2evsel(session->evlist, st->id);
+ if (!counter) {
+ pr_err("Failed to resolve counter for stat event.\n");
+ return -EINVAL;
+ }
+
+ *perf_counts(counter->counts, st->cpu, st->thread) = count;
+ counter->supported = true;
+ return 0;
+}
+
+size_t perf_event__fprintf_stat(union perf_event *event, FILE *fp)
+{
+ struct stat_event *st = (struct stat_event *) event;
+ size_t ret;
+
+ ret = fprintf(fp, "\n... id %" PRIu64 ", cpu %d, thread %d\n",
+ st->id, st->cpu, st->thread);
+ ret += fprintf(fp, "... value %" PRIu64 ", enabled %" PRIu64 ", running %" PRIu64 "\n",
+ st->val, st->ena, st->run);
+
+ return ret;
+}
+
+size_t perf_event__fprintf_stat_round(union perf_event *event, FILE *fp)
+{
+ struct stat_round_event *rd = (struct stat_round_event *)event;
+ size_t ret;
+
+ ret = fprintf(fp, "\n... time %" PRIu64 ", type %s\n", rd->time,
+ rd->type == PERF_STAT_ROUND_TYPE__FINAL ? "FINAL" : "INTERVAL");
+
+ return ret;
+}
+
+size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp)
+{
+ struct perf_stat_config sc;
+ size_t ret;
+
+ perf_event__read_stat_config(&sc, &event->stat_config);
+
+ ret = fprintf(fp, "\n");
+ ret += fprintf(fp, "... aggr_mode %d\n", sc.aggr_mode);
+ ret += fprintf(fp, "... scale %d\n", sc.scale);
+ ret += fprintf(fp, "... interval %u\n", sc.interval);
+
+ return ret;
+}
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h
index da1d11c4f8c1..086f4e128d63 100644
--- a/tools/perf/util/stat.h
+++ b/tools/perf/util/stat.h
@@ -90,4 +90,14 @@ void perf_evlist__reset_stats(struct perf_evlist *evlist);
int perf_stat_process_counter(struct perf_stat_config *config,
struct perf_evsel *counter);
+struct perf_tool;
+union perf_event;
+struct perf_session;
+int perf_event__process_stat_event(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_session *session);
+
+size_t perf_event__fprintf_stat(union perf_event *event, FILE *fp);
+size_t perf_event__fprintf_stat_round(union perf_event *event, FILE *fp);
+size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp);
#endif
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
index fc8781de62db..7f7e072be746 100644
--- a/tools/perf/util/string.c
+++ b/tools/perf/util/string.c
@@ -342,22 +342,6 @@ char *rtrim(char *s)
return s;
}
-/**
- * memdup - duplicate region of memory
- * @src: memory region to duplicate
- * @len: memory region length
- */
-void *memdup(const void *src, size_t len)
-{
- void *p;
-
- p = malloc(len);
- if (p)
- memcpy(p, src, len);
-
- return p;
-}
-
char *asprintf_expr_inout_ints(const char *var, bool in, size_t nints, int *ints)
{
/*
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c
index bdf98f6f27bb..0d3dfcb919b4 100644
--- a/tools/perf/util/strlist.c
+++ b/tools/perf/util/strlist.c
@@ -126,6 +126,11 @@ static int strlist__parse_list_entry(struct strlist *slist, const char *s,
err = strlist__load(slist, subst);
goto out;
}
+
+ if (slist->file_only) {
+ err = -ENOENT;
+ goto out;
+ }
}
err = strlist__add(slist, s);
@@ -157,11 +162,13 @@ struct strlist *strlist__new(const char *list, const struct strlist_config *conf
if (slist != NULL) {
bool dupstr = true;
+ bool file_only = false;
const char *dirname = NULL;
if (config) {
dupstr = !config->dont_dupstr;
dirname = config->dirname;
+ file_only = config->file_only;
}
rblist__init(&slist->rblist);
@@ -170,6 +177,7 @@ struct strlist *strlist__new(const char *list, const struct strlist_config *conf
slist->rblist.node_delete = strlist__node_delete;
slist->dupstr = dupstr;
+ slist->file_only = file_only;
if (list && strlist__parse_list(slist, list, dirname) != 0)
goto out_error;
diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h
index 297565aa7535..ca990029e243 100644
--- a/tools/perf/util/strlist.h
+++ b/tools/perf/util/strlist.h
@@ -13,11 +13,18 @@ struct str_node {
struct strlist {
struct rblist rblist;
- bool dupstr;
+ bool dupstr;
+ bool file_only;
};
+/*
+ * @file_only: When dirname is present, only consider entries as filenames,
+ * that should not be added to the list if dirname/entry is not
+ * found
+ */
struct strlist_config {
bool dont_dupstr;
+ bool file_only;
const char *dirname;
};
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 475d88d0a1c9..562b8ebeae5b 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -1026,8 +1026,8 @@ int dso__load_sym(struct dso *dso, struct map *map,
curr_dso->long_name_len = dso->long_name_len;
curr_map = map__new2(start, curr_dso,
map->type);
+ dso__put(curr_dso);
if (curr_map == NULL) {
- dso__put(curr_dso);
goto out_elf_end;
}
if (adjust_kernel_syms) {
@@ -1042,7 +1042,14 @@ int dso__load_sym(struct dso *dso, struct map *map,
}
curr_dso->symtab_type = dso->symtab_type;
map_groups__insert(kmaps, curr_map);
+ /*
+ * Add it before we drop the referece to curr_map,
+ * i.e. while we still are sure to have a reference
+ * to this DSO via curr_map->dso.
+ */
dsos__add(&map->groups->machine->dsos, curr_dso);
+ /* kmaps already got it */
+ map__put(curr_map);
dso__set_loaded(curr_dso, map->type);
} else
curr_dso = curr_map->dso;
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index cd08027a6d2c..3b2de6eb3376 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -39,6 +39,7 @@ struct symbol_conf symbol_conf = {
.cumulate_callchain = true,
.show_hist_headers = true,
.symfs = "",
+ .event_group = true,
};
static enum dso_binary_type binary_type_symtab[] = {
@@ -1860,24 +1861,44 @@ static void vmlinux_path__exit(void)
zfree(&vmlinux_path);
}
+static const char * const vmlinux_paths[] = {
+ "vmlinux",
+ "/boot/vmlinux"
+};
+
+static const char * const vmlinux_paths_upd[] = {
+ "/boot/vmlinux-%s",
+ "/usr/lib/debug/boot/vmlinux-%s",
+ "/lib/modules/%s/build/vmlinux",
+ "/usr/lib/debug/lib/modules/%s/vmlinux",
+ "/usr/lib/debug/boot/vmlinux-%s.debug"
+};
+
+static int vmlinux_path__add(const char *new_entry)
+{
+ vmlinux_path[vmlinux_path__nr_entries] = strdup(new_entry);
+ if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
+ return -1;
+ ++vmlinux_path__nr_entries;
+
+ return 0;
+}
+
static int vmlinux_path__init(struct perf_env *env)
{
struct utsname uts;
char bf[PATH_MAX];
char *kernel_version;
+ unsigned int i;
- vmlinux_path = malloc(sizeof(char *) * 6);
+ vmlinux_path = malloc(sizeof(char *) * (ARRAY_SIZE(vmlinux_paths) +
+ ARRAY_SIZE(vmlinux_paths_upd)));
if (vmlinux_path == NULL)
return -1;
- vmlinux_path[vmlinux_path__nr_entries] = strdup("vmlinux");
- if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
- goto out_fail;
- ++vmlinux_path__nr_entries;
- vmlinux_path[vmlinux_path__nr_entries] = strdup("/boot/vmlinux");
- if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
- goto out_fail;
- ++vmlinux_path__nr_entries;
+ for (i = 0; i < ARRAY_SIZE(vmlinux_paths); i++)
+ if (vmlinux_path__add(vmlinux_paths[i]) < 0)
+ goto out_fail;
/* only try kernel version if no symfs was given */
if (symbol_conf.symfs[0] != 0)
@@ -1892,28 +1913,11 @@ static int vmlinux_path__init(struct perf_env *env)
kernel_version = uts.release;
}
- snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", kernel_version);
- vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
- if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
- goto out_fail;
- ++vmlinux_path__nr_entries;
- snprintf(bf, sizeof(bf), "/usr/lib/debug/boot/vmlinux-%s",
- kernel_version);
- vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
- if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
- goto out_fail;
- ++vmlinux_path__nr_entries;
- snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", kernel_version);
- vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
- if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
- goto out_fail;
- ++vmlinux_path__nr_entries;
- snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux",
- kernel_version);
- vmlinux_path[vmlinux_path__nr_entries] = strdup(bf);
- if (vmlinux_path[vmlinux_path__nr_entries] == NULL)
- goto out_fail;
- ++vmlinux_path__nr_entries;
+ for (i = 0; i < ARRAY_SIZE(vmlinux_paths_upd); i++) {
+ snprintf(bf, sizeof(bf), vmlinux_paths_upd[i], kernel_version);
+ if (vmlinux_path__add(bf) < 0)
+ goto out_fail;
+ }
return 0;
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index dcd786e364f2..ccd1caa40e11 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -108,7 +108,9 @@ struct symbol_conf {
show_hist_headers,
branch_callstack,
has_filter,
- show_ref_callgraph;
+ show_ref_callgraph,
+ hide_unresolved,
+ raw_trace;
const char *vmlinux_name,
*kallsyms_name,
*source_prefix,
diff --git a/tools/perf/util/term.c b/tools/perf/util/term.c
new file mode 100644
index 000000000000..90b47d8aa19c
--- /dev/null
+++ b/tools/perf/util/term.c
@@ -0,0 +1,35 @@
+#include "util.h"
+
+void get_term_dimensions(struct winsize *ws)
+{
+ char *s = getenv("LINES");
+
+ if (s != NULL) {
+ ws->ws_row = atoi(s);
+ s = getenv("COLUMNS");
+ if (s != NULL) {
+ ws->ws_col = atoi(s);
+ if (ws->ws_row && ws->ws_col)
+ return;
+ }
+ }
+#ifdef TIOCGWINSZ
+ if (ioctl(1, TIOCGWINSZ, ws) == 0 &&
+ ws->ws_row && ws->ws_col)
+ return;
+#endif
+ ws->ws_row = 25;
+ ws->ws_col = 80;
+}
+
+void set_term_quiet_input(struct termios *old)
+{
+ struct termios tc;
+
+ tcgetattr(0, old);
+ tc = *old;
+ tc.c_lflag &= ~(ICANON | ECHO);
+ tc.c_cc[VMIN] = 0;
+ tc.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &tc);
+}
diff --git a/tools/perf/util/term.h b/tools/perf/util/term.h
new file mode 100644
index 000000000000..2c06a61846a1
--- /dev/null
+++ b/tools/perf/util/term.h
@@ -0,0 +1,10 @@
+#ifndef __PERF_TERM_H
+#define __PERF_TERM_H
+
+struct termios;
+struct winsize;
+
+void get_term_dimensions(struct winsize *ws);
+void set_term_quiet_input(struct termios *old);
+
+#endif /* __PERF_TERM_H */
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 0a9ae8014729..dfd00c6dad6e 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -19,8 +19,10 @@ int thread__init_map_groups(struct thread *thread, struct machine *machine)
thread->mg = map_groups__new(machine);
} else {
leader = __machine__findnew_thread(machine, pid, pid);
- if (leader)
+ if (leader) {
thread->mg = map_groups__get(leader->mg);
+ thread__put(leader);
+ }
}
return thread->mg ? 0 : -1;
@@ -53,7 +55,7 @@ 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);
+ atomic_set(&thread->refcnt, 1);
RB_CLEAR_NODE(&thread->rb_node);
}
@@ -95,6 +97,10 @@ struct thread *thread__get(struct thread *thread)
void thread__put(struct thread *thread)
{
if (thread && atomic_dec_and_test(&thread->refcnt)) {
+ /*
+ * Remove it from the dead_threads list, as last reference
+ * is gone.
+ */
list_del_init(&thread->node);
thread__delete(thread);
}
diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c
index 6ec3c5ca438f..08afc6909953 100644
--- a/tools/perf/util/thread_map.c
+++ b/tools/perf/util/thread_map.c
@@ -13,6 +13,7 @@
#include "thread_map.h"
#include "util.h"
#include "debug.h"
+#include "event.h"
/* Skip "." and ".." directories */
static int filter(const struct dirent *dir)
@@ -304,6 +305,7 @@ out:
out_free_threads:
zfree(&threads);
+ strlist__delete(slist);
goto out;
}
@@ -408,3 +410,29 @@ void thread_map__read_comms(struct thread_map *threads)
for (i = 0; i < threads->nr; ++i)
comm_init(threads, i);
}
+
+static void thread_map__copy_event(struct thread_map *threads,
+ struct thread_map_event *event)
+{
+ unsigned i;
+
+ threads->nr = (int) event->nr;
+
+ for (i = 0; i < event->nr; i++) {
+ thread_map__set_pid(threads, i, (pid_t) event->entries[i].pid);
+ threads->map[i].comm = strndup(event->entries[i].comm, 16);
+ }
+
+ atomic_set(&threads->refcnt, 1);
+}
+
+struct thread_map *thread_map__new_event(struct thread_map_event *event)
+{
+ struct thread_map *threads;
+
+ threads = thread_map__alloc(event->nr);
+ if (threads)
+ thread_map__copy_event(threads, event);
+
+ return threads;
+}
diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h
index af679d8a50f8..85e4c7c4fbde 100644
--- a/tools/perf/util/thread_map.h
+++ b/tools/perf/util/thread_map.h
@@ -16,11 +16,14 @@ struct thread_map {
struct thread_map_data map[];
};
+struct thread_map_event;
+
struct thread_map *thread_map__new_dummy(void);
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 *thread_map__new_by_uid(uid_t uid);
struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid);
+struct thread_map *thread_map__new_event(struct thread_map_event *event);
struct thread_map *thread_map__get(struct thread_map *map);
void thread_map__put(struct thread_map *map);
diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h
index cab8cc24831b..55de4cffcd4e 100644
--- a/tools/perf/util/tool.h
+++ b/tools/perf/util/tool.h
@@ -50,12 +50,18 @@ struct perf_tool {
throttle,
unthrottle;
event_attr_op attr;
+ event_attr_op event_update;
event_op2 tracing_data;
event_oe finished_round;
event_op2 build_id,
id_index,
auxtrace_info,
- auxtrace_error;
+ auxtrace_error,
+ thread_map,
+ cpu_map,
+ stat_config,
+ stat,
+ stat_round;
event_op3 auxtrace;
bool ordered_events;
bool ordering_requires_timestamps;
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index b85ee55cca0c..bce5b1dac268 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -65,6 +65,7 @@ int tracing_data_put(struct tracing_data *tdata);
struct addr_location;
struct perf_session;
+struct perf_stat_config;
struct scripting_ops {
const char *name;
@@ -75,6 +76,9 @@ struct scripting_ops {
struct perf_sample *sample,
struct perf_evsel *evsel,
struct addr_location *al);
+ void (*process_stat)(struct perf_stat_config *config,
+ struct perf_evsel *evsel, u64 tstamp);
+ void (*process_stat_interval)(u64 tstamp);
int (*generate_script) (struct pevent *pevent, const char *outfile);
};
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 2dcfe9a7c8d0..cf5e250bc78e 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -11,6 +11,7 @@
#include <linux/types.h>
#include "event.h"
#include "perf_regs.h"
+#include "callchain.h"
static char *debuginfo_path;
@@ -52,25 +53,28 @@ static int report_module(u64 ip, struct unwind_info *ui)
return __report_module(&al, ip, ui);
}
+/*
+ * Store all entries within entries array,
+ * we will process it after we finish unwind.
+ */
static int entry(u64 ip, struct unwind_info *ui)
{
- struct unwind_entry e;
+ struct unwind_entry *e = &ui->entries[ui->idx++];
struct addr_location al;
if (__report_module(&al, ip, ui))
return -1;
- e.ip = ip;
- e.map = al.map;
- e.sym = al.sym;
+ e->ip = ip;
+ e->map = al.map;
+ e->sym = al.sym;
pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
al.sym ? al.sym->name : "''",
ip,
al.map ? al.map->map_ip(al.map, ip) : (u64) 0);
-
- return ui->cb(&e, ui->arg);
+ return 0;
}
static pid_t next_thread(Dwfl *dwfl, void *arg, void **thread_argp)
@@ -92,6 +96,16 @@ static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr,
thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER,
MAP__FUNCTION, addr, &al);
if (!al.map) {
+ /*
+ * We've seen cases (softice) where DWARF unwinder went
+ * through non executable mmaps, which we need to lookup
+ * in MAP__VARIABLE tree.
+ */
+ thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER,
+ MAP__VARIABLE, addr, &al);
+ }
+
+ if (!al.map) {
pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
return -1;
}
@@ -168,7 +182,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct perf_sample *data,
int max_stack)
{
- struct unwind_info ui = {
+ struct unwind_info *ui, ui_buf = {
.sample = data,
.thread = thread,
.machine = thread->mg->machine,
@@ -177,35 +191,54 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
.max_stack = max_stack,
};
Dwarf_Word ip;
- int err = -EINVAL;
+ int err = -EINVAL, i;
if (!data->user_regs.regs)
return -EINVAL;
- ui.dwfl = dwfl_begin(&offline_callbacks);
- if (!ui.dwfl)
+ ui = zalloc(sizeof(ui_buf) + sizeof(ui_buf.entries[0]) * max_stack);
+ if (!ui)
+ return -ENOMEM;
+
+ *ui = ui_buf;
+
+ ui->dwfl = dwfl_begin(&offline_callbacks);
+ if (!ui->dwfl)
goto out;
err = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP);
if (err)
goto out;
- err = report_module(ip, &ui);
+ err = report_module(ip, ui);
if (err)
goto out;
- if (!dwfl_attach_state(ui.dwfl, EM_NONE, thread->tid, &callbacks, &ui))
+ if (!dwfl_attach_state(ui->dwfl, EM_NONE, thread->tid, &callbacks, ui))
goto out;
- err = dwfl_getthread_frames(ui.dwfl, thread->tid, frame_callback, &ui);
+ err = dwfl_getthread_frames(ui->dwfl, thread->tid, frame_callback, ui);
- if (err && !ui.max_stack)
+ if (err && !ui->max_stack)
err = 0;
+ /*
+ * Display what we got based on the order setup.
+ */
+ for (i = 0; i < ui->idx && !err; i++) {
+ int j = i;
+
+ if (callchain_param.order == ORDER_CALLER)
+ j = ui->idx - i - 1;
+
+ err = ui->entries[j].ip ? ui->cb(&ui->entries[j], ui->arg) : 0;
+ }
+
out:
if (err)
pr_debug("unwind: failed with '%s'\n", dwfl_errmsg(-1));
- dwfl_end(ui.dwfl);
+ dwfl_end(ui->dwfl);
+ free(ui);
return 0;
}
diff --git a/tools/perf/util/unwind-libdw.h b/tools/perf/util/unwind-libdw.h
index 417a1426f3ad..58328669ed16 100644
--- a/tools/perf/util/unwind-libdw.h
+++ b/tools/perf/util/unwind-libdw.h
@@ -16,6 +16,8 @@ struct unwind_info {
unwind_entry_cb_t cb;
void *arg;
int max_stack;
+ int idx;
+ struct unwind_entry entries[];
};
#endif /* __PERF_UNWIND_LIBDW_H */
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index c83832b555e5..ee7e372297e5 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -319,6 +319,15 @@ static struct map *find_map(unw_word_t ip, struct unwind_info *ui)
thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER,
MAP__FUNCTION, ip, &al);
+ if (!al.map) {
+ /*
+ * We've seen cases (softice) where DWARF unwinder went
+ * through non executable mmaps, which we need to lookup
+ * in MAP__VARIABLE tree.
+ */
+ thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER,
+ MAP__VARIABLE, ip, &al);
+ }
return al.map;
}
@@ -416,20 +425,19 @@ get_proc_name(unw_addr_space_t __maybe_unused as,
static int access_dso_mem(struct unwind_info *ui, unw_word_t addr,
unw_word_t *data)
{
- struct addr_location al;
+ struct map *map;
ssize_t size;
- thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER,
- MAP__FUNCTION, addr, &al);
- if (!al.map) {
+ map = find_map(addr, ui);
+ if (!map) {
pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
return -1;
}
- if (!al.map->dso)
+ if (!map->dso)
return -1;
- size = dso__data_read_addr(al.map->dso, al.map, ui->machine,
+ size = dso__data_read_addr(map->dso, map, ui->machine,
addr, (u8 *) data, sizeof(*data));
return !(size == sizeof(*data));
@@ -614,23 +622,48 @@ void unwind__finish_access(struct thread *thread)
static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
void *arg, int max_stack)
{
+ u64 val;
+ unw_word_t ips[max_stack];
unw_addr_space_t addr_space;
unw_cursor_t c;
- int ret;
-
- addr_space = thread__priv(ui->thread);
- if (addr_space == NULL)
- return -1;
+ int ret, i = 0;
- ret = unw_init_remote(&c, addr_space, ui);
+ ret = perf_reg_value(&val, &ui->sample->user_regs, PERF_REG_IP);
if (ret)
- display_error(ret);
+ return ret;
- while (!ret && (unw_step(&c) > 0) && max_stack--) {
- unw_word_t ip;
+ ips[i++] = (unw_word_t) val;
- unw_get_reg(&c, UNW_REG_IP, &ip);
- ret = ip ? entry(ip, ui->thread, cb, arg) : 0;
+ /*
+ * If we need more than one entry, do the DWARF
+ * unwind itself.
+ */
+ if (max_stack - 1 > 0) {
+ addr_space = thread__priv(ui->thread);
+ if (addr_space == NULL)
+ return -1;
+
+ ret = unw_init_remote(&c, addr_space, ui);
+ if (ret)
+ display_error(ret);
+
+ while (!ret && (unw_step(&c) > 0) && i < max_stack) {
+ unw_get_reg(&c, UNW_REG_IP, &ips[i]);
+ ++i;
+ }
+
+ max_stack = i;
+ }
+
+ /*
+ * Display what we got based on the order setup.
+ */
+ for (i = 0; i < max_stack && !ret; i++) {
+ int j = i;
+
+ if (callchain_param.order == ORDER_CALLER)
+ j = max_stack - i - 1;
+ ret = ips[j] ? entry(ips[j], ui->thread, cb, arg) : 0;
}
return ret;
@@ -640,24 +673,17 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct thread *thread,
struct perf_sample *data, int max_stack)
{
- u64 ip;
struct unwind_info ui = {
.sample = data,
.thread = thread,
.machine = thread->mg->machine,
};
- int ret;
if (!data->user_regs.regs)
return -EINVAL;
- ret = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP);
- if (ret)
- return ret;
-
- ret = entry(ip, thread, cb, arg);
- if (ret)
- return -ENOMEM;
+ if (max_stack <= 0)
+ return -EINVAL;
- return --max_stack > 0 ? get_entries(&ui, cb, arg, max_stack) : 0;
+ return get_entries(&ui, cb, arg, max_stack);
}
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index 47b1e36c7ea0..ead9509835d2 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -16,12 +16,14 @@
#include <linux/kernel.h>
#include <unistd.h>
#include "callchain.h"
+#include "strlist.h"
struct callchain_param callchain_param = {
.mode = CHAIN_GRAPH_ABS,
.min_percent = 0.5,
.order = ORDER_CALLEE,
- .key = CCKEY_FUNCTION
+ .key = CCKEY_FUNCTION,
+ .value = CCVAL_PERCENT,
};
/*
@@ -351,41 +353,8 @@ void sighandler_dump_stack(int sig)
{
psignal(sig, "perf");
dump_stack();
- exit(sig);
-}
-
-void get_term_dimensions(struct winsize *ws)
-{
- char *s = getenv("LINES");
-
- if (s != NULL) {
- ws->ws_row = atoi(s);
- s = getenv("COLUMNS");
- if (s != NULL) {
- ws->ws_col = atoi(s);
- if (ws->ws_row && ws->ws_col)
- return;
- }
- }
-#ifdef TIOCGWINSZ
- if (ioctl(1, TIOCGWINSZ, ws) == 0 &&
- ws->ws_row && ws->ws_col)
- return;
-#endif
- ws->ws_row = 25;
- ws->ws_col = 80;
-}
-
-void set_term_quiet_input(struct termios *old)
-{
- struct termios tc;
-
- tcgetattr(0, old);
- tc = *old;
- tc.c_lflag &= ~(ICANON | ECHO);
- tc.c_cc[VMIN] = 0;
- tc.c_cc[VTIME] = 0;
- tcsetattr(0, TCSANOW, &tc);
+ signal(sig, SIG_DFL);
+ raise(sig);
}
int parse_nsec_time(const char *str, u64 *ptime)
@@ -695,3 +664,30 @@ fetch_kernel_version(unsigned int *puint, char *str,
*puint = (version << 16) + (patchlevel << 8) + sublevel;
return 0;
}
+
+const char *perf_tip(const char *dirpath)
+{
+ struct strlist *tips;
+ struct str_node *node;
+ char *tip = NULL;
+ struct strlist_config conf = {
+ .dirname = dirpath,
+ .file_only = true,
+ };
+
+ tips = strlist__new("tips.txt", &conf);
+ if (tips == NULL)
+ return errno == ENOENT ? NULL : "Tip: get more memory! ;-p";
+
+ if (strlist__nr_entries(tips) == 0)
+ goto out;
+
+ node = strlist__entry(tips, random() % strlist__nr_entries(tips));
+ if (asprintf(&tip, "Tip: %s", node->s) < 0)
+ tip = (char *)"Tip: get more memory! ;-)";
+
+out:
+ strlist__delete(tips);
+
+ return tip;
+}
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index dcc659017976..fe915e616f9b 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -53,6 +53,7 @@
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
+#include <term.h>
#include <errno.h>
#include <limits.h>
#include <sys/param.h>
@@ -150,12 +151,6 @@ extern void set_warning_routine(void (*routine)(const char *err, va_list params)
extern int prefixcmp(const char *str, const char *prefix);
extern void set_buildid_dir(const char *dir);
-static inline const char *skip_prefix(const char *str, const char *prefix)
-{
- size_t len = strlen(prefix);
- return strncmp(str, prefix, len) ? NULL : str + len;
-}
-
#ifdef __GLIBC_PREREQ
#if __GLIBC_PREREQ(2, 1)
#define HAVE_STRCHRNUL
@@ -186,14 +181,6 @@ static inline void *zalloc(size_t size)
#define zfree(ptr) ({ free(*ptr); *ptr = NULL; })
-static inline int has_extension(const char *filename, const char *ext)
-{
- size_t len = strlen(filename);
- size_t extlen = strlen(ext);
-
- return len > extlen && !memcmp(filename + len - extlen, ext, extlen);
-}
-
/* Sane ctype - no locale, and works with signed chars */
#undef isascii
#undef isspace
@@ -282,9 +269,6 @@ void sighandler_dump_stack(int sig);
extern unsigned int page_size;
extern int cacheline_size;
-void get_term_dimensions(struct winsize *ws);
-void set_term_quiet_input(struct termios *old);
-
struct parse_tag {
char tag;
int mult;
@@ -358,4 +342,6 @@ int fetch_kernel_version(unsigned int *puint,
#define KVER_FMT "%d.%d.%d"
#define KVER_PARAM(x) KVER_VERSION(x), KVER_PATCHLEVEL(x), KVER_SUBLEVEL(x)
+const char *perf_tip(const char *dirpath);
+
#endif /* GIT_COMPAT_UTIL_H */
diff --git a/tools/power/acpi/Makefile b/tools/power/acpi/Makefile
index e882c8320135..a8bf9081512b 100644
--- a/tools/power/acpi/Makefile
+++ b/tools/power/acpi/Makefile
@@ -10,18 +10,18 @@
include ../../scripts/Makefile.include
-all: acpidump ec
-clean: acpidump_clean ec_clean
-install: acpidump_install ec_install
-uninstall: acpidump_uninstall ec_uninstall
+all: acpidbg acpidump ec
+clean: acpidbg_clean acpidump_clean ec_clean
+install: acpidbg_install acpidump_install ec_install
+uninstall: acpidbg_uninstall acpidump_uninstall ec_uninstall
-acpidump ec: FORCE
+acpidbg acpidump ec: FORCE
$(call descend,tools/$@,all)
-acpidump_clean ec_clean:
+acpidbg_clean acpidump_clean ec_clean:
$(call descend,tools/$(@:_clean=),clean)
-acpidump_install ec_install:
+acpidbg_install acpidump_install ec_install:
$(call descend,tools/$(@:_install=),install)
-acpidump_uninstall ec_uninstall:
+acpidbg_uninstall acpidump_uninstall ec_uninstall:
$(call descend,tools/$(@:_uninstall=),uninstall)
.PHONY: FORCE
diff --git a/tools/power/acpi/common/getopt.c b/tools/power/acpi/common/getopt.c
index 326e826a5d20..efefe309367a 100644
--- a/tools/power/acpi/common/getopt.c
+++ b/tools/power/acpi/common/getopt.c
@@ -47,6 +47,7 @@
* Option strings:
* "f" - Option has no arguments
* "f:" - Option requires an argument
+ * "f+" - Option has an optional argument
* "f^" - Option has optional single-char sub-options
* "f|" - Option has required single-char sub-options
*/
@@ -85,6 +86,7 @@ static int current_char_ptr = 1;
int acpi_getopt_argument(int argc, char **argv)
{
+
acpi_gbl_optind--;
current_char_ptr++;
diff --git a/tools/power/acpi/os_specific/service_layers/oslibcfs.c b/tools/power/acpi/os_specific/service_layers/oslibcfs.c
index b51e40a9a120..6df758302604 100644
--- a/tools/power/acpi/os_specific/service_layers/oslibcfs.c
+++ b/tools/power/acpi/os_specific/service_layers/oslibcfs.c
@@ -73,6 +73,7 @@ ACPI_FILE acpi_os_open_file(const char *path, u8 modes)
if (modes & ACPI_FILE_WRITING) {
modes_str[i++] = 'w';
}
+
if (modes & ACPI_FILE_BINARY) {
modes_str[i++] = 'b';
}
@@ -101,6 +102,7 @@ ACPI_FILE acpi_os_open_file(const char *path, u8 modes)
void acpi_os_close_file(ACPI_FILE file)
{
+
fclose(file);
}
@@ -202,6 +204,7 @@ acpi_status acpi_os_set_file_offset(ACPI_FILE file, long offset, u8 from)
if (from == ACPI_FILE_BEGIN) {
ret = fseek(file, offset, SEEK_SET);
}
+
if (from == ACPI_FILE_END) {
ret = fseek(file, offset, SEEK_END);
}
diff --git a/tools/power/acpi/tools/acpidbg/Makefile b/tools/power/acpi/tools/acpidbg/Makefile
new file mode 100644
index 000000000000..352df4b41ae9
--- /dev/null
+++ b/tools/power/acpi/tools/acpidbg/Makefile
@@ -0,0 +1,27 @@
+# tools/power/acpi/tools/acpidbg/Makefile - ACPI tool Makefile
+#
+# Copyright (c) 2015, Intel Corporation
+# Author: Lv Zheng <lv.zheng@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; version 2
+# of the License.
+
+include ../../Makefile.config
+
+TOOL = acpidbg
+vpath %.c \
+ ../../../../../drivers/acpi/acpica\
+ ../../common\
+ ../../os_specific/service_layers\
+ .
+CFLAGS += -DACPI_APPLICATION -DACPI_SINGLE_THREAD -DACPI_DEBUGGER\
+ -I.\
+ -I../../../../../drivers/acpi/acpica\
+ -I../../../../../include
+LDFLAGS += -lpthread
+TOOL_OBJS = \
+ acpidbg.o
+
+include ../../Makefile.rules
diff --git a/tools/power/acpi/tools/acpidbg/acpidbg.c b/tools/power/acpi/tools/acpidbg/acpidbg.c
new file mode 100644
index 000000000000..d070fccdba6d
--- /dev/null
+++ b/tools/power/acpi/tools/acpidbg/acpidbg.c
@@ -0,0 +1,438 @@
+/*
+ * ACPI AML interfacing userspace utility
+ *
+ * Copyright (C) 2015, Intel Corporation
+ * Authors: Lv Zheng <lv.zheng@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <acpi/acpi.h>
+
+/* Headers not included by include/acpi/platform/aclinux.h */
+#include <stdbool.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <linux/circ_buf.h>
+
+#define ACPI_AML_FILE "/sys/kernel/debug/acpi/acpidbg"
+#define ACPI_AML_SEC_TICK 1
+#define ACPI_AML_USEC_PEEK 200
+#define ACPI_AML_BUF_SIZE 4096
+
+#define ACPI_AML_BATCH_WRITE_CMD 0x00 /* Write command to kernel */
+#define ACPI_AML_BATCH_READ_LOG 0x01 /* Read log from kernel */
+#define ACPI_AML_BATCH_WRITE_LOG 0x02 /* Write log to console */
+
+#define ACPI_AML_LOG_START 0x00
+#define ACPI_AML_PROMPT_START 0x01
+#define ACPI_AML_PROMPT_STOP 0x02
+#define ACPI_AML_LOG_STOP 0x03
+#define ACPI_AML_PROMPT_ROLL 0x04
+
+#define ACPI_AML_INTERACTIVE 0x00
+#define ACPI_AML_BATCH 0x01
+
+#define circ_count(circ) \
+ (CIRC_CNT((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_count_to_end(circ) \
+ (CIRC_CNT_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space(circ) \
+ (CIRC_SPACE((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+#define circ_space_to_end(circ) \
+ (CIRC_SPACE_TO_END((circ)->head, (circ)->tail, ACPI_AML_BUF_SIZE))
+
+#define acpi_aml_cmd_count() circ_count(&acpi_aml_cmd_crc)
+#define acpi_aml_log_count() circ_count(&acpi_aml_log_crc)
+#define acpi_aml_cmd_space() circ_space(&acpi_aml_cmd_crc)
+#define acpi_aml_log_space() circ_space(&acpi_aml_log_crc)
+
+#define ACPI_AML_DO(_fd, _op, _buf, _ret) \
+ do { \
+ _ret = acpi_aml_##_op(_fd, &acpi_aml_##_buf##_crc); \
+ if (_ret == 0) { \
+ fprintf(stderr, \
+ "%s %s pipe closed.\n", #_buf, #_op); \
+ return; \
+ } \
+ } while (0)
+#define ACPI_AML_BATCH_DO(_fd, _op, _buf, _ret) \
+ do { \
+ _ret = acpi_aml_##_op##_batch_##_buf(_fd, \
+ &acpi_aml_##_buf##_crc); \
+ if (_ret == 0) \
+ return; \
+ } while (0)
+
+
+static char acpi_aml_cmd_buf[ACPI_AML_BUF_SIZE];
+static char acpi_aml_log_buf[ACPI_AML_BUF_SIZE];
+static struct circ_buf acpi_aml_cmd_crc = {
+ .buf = acpi_aml_cmd_buf,
+ .head = 0,
+ .tail = 0,
+};
+static struct circ_buf acpi_aml_log_crc = {
+ .buf = acpi_aml_log_buf,
+ .head = 0,
+ .tail = 0,
+};
+static const char *acpi_aml_file_path = ACPI_AML_FILE;
+static unsigned long acpi_aml_mode = ACPI_AML_INTERACTIVE;
+static bool acpi_aml_exit;
+
+static bool acpi_aml_batch_drain;
+static unsigned long acpi_aml_batch_state;
+static char acpi_aml_batch_prompt;
+static char acpi_aml_batch_roll;
+static unsigned long acpi_aml_log_state;
+static char *acpi_aml_batch_cmd = NULL;
+static char *acpi_aml_batch_pos = NULL;
+
+static int acpi_aml_set_fl(int fd, int flags)
+{
+ int ret;
+
+ ret = fcntl(fd, F_GETFL, 0);
+ if (ret < 0) {
+ perror("fcntl(F_GETFL)");
+ return ret;
+ }
+ flags |= ret;
+ ret = fcntl(fd, F_SETFL, flags);
+ if (ret < 0) {
+ perror("fcntl(F_SETFL)");
+ return ret;
+ }
+ return ret;
+}
+
+static int acpi_aml_set_fd(int fd, int maxfd, fd_set *set)
+{
+ if (fd > maxfd)
+ maxfd = fd;
+ FD_SET(fd, set);
+ return maxfd;
+}
+
+static int acpi_aml_read(int fd, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+
+ p = &crc->buf[crc->head];
+ len = circ_space_to_end(crc);
+ len = read(fd, p, len);
+ if (len < 0)
+ perror("read");
+ else if (len > 0)
+ crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
+ return len;
+}
+
+static int acpi_aml_read_batch_cmd(int unused, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+ int remained = strlen(acpi_aml_batch_pos);
+
+ p = &crc->buf[crc->head];
+ len = circ_space_to_end(crc);
+ if (len > remained) {
+ memcpy(p, acpi_aml_batch_pos, remained);
+ acpi_aml_batch_pos += remained;
+ len = remained;
+ } else {
+ memcpy(p, acpi_aml_batch_pos, len);
+ acpi_aml_batch_pos += len;
+ }
+ if (len > 0)
+ crc->head = (crc->head + len) & (ACPI_AML_BUF_SIZE - 1);
+ return len;
+}
+
+static int acpi_aml_read_batch_log(int fd, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+ int ret = 0;
+
+ p = &crc->buf[crc->head];
+ len = circ_space_to_end(crc);
+ while (ret < len && acpi_aml_log_state != ACPI_AML_LOG_STOP) {
+ if (acpi_aml_log_state == ACPI_AML_PROMPT_ROLL) {
+ *p = acpi_aml_batch_roll;
+ len = 1;
+ crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
+ ret += 1;
+ acpi_aml_log_state = ACPI_AML_LOG_START;
+ } else {
+ len = read(fd, p, 1);
+ if (len <= 0) {
+ if (len < 0)
+ perror("read");
+ ret = len;
+ break;
+ }
+ }
+ switch (acpi_aml_log_state) {
+ case ACPI_AML_LOG_START:
+ if (*p == '\n')
+ acpi_aml_log_state = ACPI_AML_PROMPT_START;
+ crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
+ ret += 1;
+ break;
+ case ACPI_AML_PROMPT_START:
+ if (*p == ACPI_DEBUGGER_COMMAND_PROMPT ||
+ *p == ACPI_DEBUGGER_EXECUTE_PROMPT) {
+ acpi_aml_batch_prompt = *p;
+ acpi_aml_log_state = ACPI_AML_PROMPT_STOP;
+ } else {
+ if (*p != '\n')
+ acpi_aml_log_state = ACPI_AML_LOG_START;
+ crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
+ ret += 1;
+ }
+ break;
+ case ACPI_AML_PROMPT_STOP:
+ if (*p == ' ') {
+ acpi_aml_log_state = ACPI_AML_LOG_STOP;
+ acpi_aml_exit = true;
+ } else {
+ /* Roll back */
+ acpi_aml_log_state = ACPI_AML_PROMPT_ROLL;
+ acpi_aml_batch_roll = *p;
+ *p = acpi_aml_batch_prompt;
+ crc->head = (crc->head + 1) & (ACPI_AML_BUF_SIZE - 1);
+ ret += 1;
+ }
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+ return ret;
+}
+
+static int acpi_aml_write(int fd, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+
+ p = &crc->buf[crc->tail];
+ len = circ_count_to_end(crc);
+ len = write(fd, p, len);
+ if (len < 0)
+ perror("write");
+ else if (len > 0)
+ crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
+ return len;
+}
+
+static int acpi_aml_write_batch_log(int fd, struct circ_buf *crc)
+{
+ char *p;
+ int len;
+
+ p = &crc->buf[crc->tail];
+ len = circ_count_to_end(crc);
+ if (!acpi_aml_batch_drain) {
+ len = write(fd, p, len);
+ if (len < 0)
+ perror("write");
+ }
+ if (len > 0)
+ crc->tail = (crc->tail + len) & (ACPI_AML_BUF_SIZE - 1);
+ return len;
+}
+
+static int acpi_aml_write_batch_cmd(int fd, struct circ_buf *crc)
+{
+ int len;
+
+ len = acpi_aml_write(fd, crc);
+ if (circ_count_to_end(crc) == 0)
+ acpi_aml_batch_state = ACPI_AML_BATCH_READ_LOG;
+ return len;
+}
+
+static void acpi_aml_loop(int fd)
+{
+ fd_set rfds;
+ fd_set wfds;
+ struct timeval tv;
+ int ret;
+ int maxfd = 0;
+
+ if (acpi_aml_mode == ACPI_AML_BATCH) {
+ acpi_aml_log_state = ACPI_AML_LOG_START;
+ acpi_aml_batch_pos = acpi_aml_batch_cmd;
+ if (acpi_aml_batch_drain)
+ acpi_aml_batch_state = ACPI_AML_BATCH_READ_LOG;
+ else
+ acpi_aml_batch_state = ACPI_AML_BATCH_WRITE_CMD;
+ }
+ acpi_aml_exit = false;
+ while (!acpi_aml_exit) {
+ tv.tv_sec = ACPI_AML_SEC_TICK;
+ tv.tv_usec = 0;
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+
+ if (acpi_aml_cmd_space()) {
+ if (acpi_aml_mode == ACPI_AML_INTERACTIVE)
+ maxfd = acpi_aml_set_fd(STDIN_FILENO, maxfd, &rfds);
+ else if (strlen(acpi_aml_batch_pos) &&
+ acpi_aml_batch_state == ACPI_AML_BATCH_WRITE_CMD)
+ ACPI_AML_BATCH_DO(STDIN_FILENO, read, cmd, ret);
+ }
+ if (acpi_aml_cmd_count() &&
+ (acpi_aml_mode == ACPI_AML_INTERACTIVE ||
+ acpi_aml_batch_state == ACPI_AML_BATCH_WRITE_CMD))
+ maxfd = acpi_aml_set_fd(fd, maxfd, &wfds);
+ if (acpi_aml_log_space() &&
+ (acpi_aml_mode == ACPI_AML_INTERACTIVE ||
+ acpi_aml_batch_state == ACPI_AML_BATCH_READ_LOG))
+ maxfd = acpi_aml_set_fd(fd, maxfd, &rfds);
+ if (acpi_aml_log_count())
+ maxfd = acpi_aml_set_fd(STDOUT_FILENO, maxfd, &wfds);
+
+ ret = select(maxfd+1, &rfds, &wfds, NULL, &tv);
+ if (ret < 0) {
+ perror("select");
+ break;
+ }
+ if (ret > 0) {
+ if (FD_ISSET(STDIN_FILENO, &rfds))
+ ACPI_AML_DO(STDIN_FILENO, read, cmd, ret);
+ if (FD_ISSET(fd, &wfds)) {
+ if (acpi_aml_mode == ACPI_AML_BATCH)
+ ACPI_AML_BATCH_DO(fd, write, cmd, ret);
+ else
+ ACPI_AML_DO(fd, write, cmd, ret);
+ }
+ if (FD_ISSET(fd, &rfds)) {
+ if (acpi_aml_mode == ACPI_AML_BATCH)
+ ACPI_AML_BATCH_DO(fd, read, log, ret);
+ else
+ ACPI_AML_DO(fd, read, log, ret);
+ }
+ if (FD_ISSET(STDOUT_FILENO, &wfds)) {
+ if (acpi_aml_mode == ACPI_AML_BATCH)
+ ACPI_AML_BATCH_DO(STDOUT_FILENO, write, log, ret);
+ else
+ ACPI_AML_DO(STDOUT_FILENO, write, log, ret);
+ }
+ }
+ }
+}
+
+static bool acpi_aml_readable(int fd)
+{
+ fd_set rfds;
+ struct timeval tv;
+ int ret;
+ int maxfd = 0;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = ACPI_AML_USEC_PEEK;
+ FD_ZERO(&rfds);
+ maxfd = acpi_aml_set_fd(fd, maxfd, &rfds);
+ ret = select(maxfd+1, &rfds, NULL, NULL, &tv);
+ if (ret < 0)
+ perror("select");
+ if (ret > 0 && FD_ISSET(fd, &rfds))
+ return true;
+ return false;
+}
+
+/*
+ * This is a userspace IO flush implementation, replying on the prompt
+ * characters and can be turned into a flush() call after kernel implements
+ * .flush() filesystem operation.
+ */
+static void acpi_aml_flush(int fd)
+{
+ while (acpi_aml_readable(fd)) {
+ acpi_aml_batch_drain = true;
+ acpi_aml_loop(fd);
+ acpi_aml_batch_drain = false;
+ }
+}
+
+void usage(FILE *file, char *progname)
+{
+ fprintf(file, "usage: %s [-b cmd] [-f file] [-h]\n", progname);
+ fprintf(file, "\nOptions:\n");
+ fprintf(file, " -b Specify command to be executed in batch mode\n");
+ fprintf(file, " -f Specify interface file other than");
+ fprintf(file, " /sys/kernel/debug/acpi/acpidbg\n");
+ fprintf(file, " -h Print this help message\n");
+}
+
+int main(int argc, char **argv)
+{
+ int fd = 0;
+ int ch;
+ int len;
+ int ret = EXIT_SUCCESS;
+
+ while ((ch = getopt(argc, argv, "b:f:h")) != -1) {
+ switch (ch) {
+ case 'b':
+ if (acpi_aml_batch_cmd) {
+ fprintf(stderr, "Already specify %s\n",
+ acpi_aml_batch_cmd);
+ ret = EXIT_FAILURE;
+ goto exit;
+ }
+ len = strlen(optarg);
+ acpi_aml_batch_cmd = calloc(len + 2, 1);
+ if (!acpi_aml_batch_cmd) {
+ perror("calloc");
+ ret = EXIT_FAILURE;
+ goto exit;
+ }
+ memcpy(acpi_aml_batch_cmd, optarg, len);
+ acpi_aml_batch_cmd[len] = '\n';
+ acpi_aml_mode = ACPI_AML_BATCH;
+ break;
+ case 'f':
+ acpi_aml_file_path = optarg;
+ break;
+ case 'h':
+ usage(stdout, argv[0]);
+ goto exit;
+ break;
+ case '?':
+ default:
+ usage(stderr, argv[0]);
+ ret = EXIT_FAILURE;
+ goto exit;
+ break;
+ }
+ }
+
+ fd = open(acpi_aml_file_path, O_RDWR | O_NONBLOCK);
+ if (fd < 0) {
+ perror("open");
+ ret = EXIT_FAILURE;
+ goto exit;
+ }
+ acpi_aml_set_fl(STDIN_FILENO, O_NONBLOCK);
+ acpi_aml_set_fl(STDOUT_FILENO, O_NONBLOCK);
+
+ if (acpi_aml_mode == ACPI_AML_BATCH)
+ acpi_aml_flush(fd);
+ acpi_aml_loop(fd);
+
+exit:
+ if (fd < 0)
+ close(fd);
+ if (acpi_aml_batch_cmd)
+ free(acpi_aml_batch_cmd);
+ return ret;
+}
diff --git a/tools/power/acpi/tools/acpidump/apfiles.c b/tools/power/acpi/tools/acpidump/apfiles.c
index a1c62de42a3b..bbdf9e8e25bc 100644
--- a/tools/power/acpi/tools/acpidump/apfiles.c
+++ b/tools/power/acpi/tools/acpidump/apfiles.c
@@ -48,6 +48,18 @@
static int ap_is_existing_file(char *pathname);
+/******************************************************************************
+ *
+ * FUNCTION: ap_is_existing_file
+ *
+ * PARAMETERS: pathname - Output filename
+ *
+ * RETURN: 0 on success
+ *
+ * DESCRIPTION: Query for file overwrite if it already exists.
+ *
+ ******************************************************************************/
+
static int ap_is_existing_file(char *pathname)
{
#ifndef _GNU_EFI
@@ -136,6 +148,7 @@ int ap_write_to_binary_file(struct acpi_table_header *table, u32 instance)
} else {
ACPI_MOVE_NAME(filename, table->signature);
}
+
filename[0] = (char)tolower((int)filename[0]);
filename[1] = (char)tolower((int)filename[1]);
filename[2] = (char)tolower((int)filename[2]);
diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile
index 2e2ba2efa0d9..0adaf0c7c03a 100644
--- a/tools/power/cpupower/Makefile
+++ b/tools/power/cpupower/Makefile
@@ -47,6 +47,11 @@ NLS ?= true
# cpufreq-bench benchmarking tool
CPUFREQ_BENCH ?= true
+# Do not build libraries, but build the code in statically
+# Libraries are still built, otherwise the Makefile code would
+# be rather ugly.
+export STATIC ?= false
+
# Prefix to the directories we're installing to
DESTDIR ?=
@@ -161,6 +166,12 @@ ifeq ($(strip $(CPUFREQ_BENCH)),true)
COMPILE_BENCH += compile-bench
endif
+ifeq ($(strip $(STATIC)),true)
+ UTIL_OBJS += $(LIB_OBJS)
+ UTIL_HEADERS += $(LIB_HEADERS)
+ UTIL_SRC += $(LIB_SRC)
+endif
+
CFLAGS += $(WARNINGS)
ifeq ($(strip $(V)),false)
@@ -209,7 +220,11 @@ $(OUTPUT)%.o: %.c
$(OUTPUT)cpupower: $(UTIL_OBJS) $(OUTPUT)libcpupower.so.$(LIB_MAJ)
$(ECHO) " CC " $@
+ifeq ($(strip $(STATIC)),true)
+ $(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) $(UTIL_OBJS) -lrt -lpci -L$(OUTPUT) -o $@
+else
$(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) $(UTIL_OBJS) -lcpupower -lrt -lpci -L$(OUTPUT) -o $@
+endif
$(QUIET) $(STRIPCMD) $@
$(OUTPUT)po/$(PACKAGE).pot: $(UTIL_SRC)
@@ -291,7 +306,11 @@ install-bench:
@#DESTDIR must be set from outside to survive
@sbindir=$(sbindir) bindir=$(bindir) docdir=$(docdir) confdir=$(confdir) $(MAKE) -C bench O=$(OUTPUT) install
+ifeq ($(strip $(STATIC)),true)
+install: all install-tools install-man $(INSTALL_NLS) $(INSTALL_BENCH)
+else
install: all install-lib install-tools install-man $(INSTALL_NLS) $(INSTALL_BENCH)
+endif
uninstall:
- rm -f $(DESTDIR)${libdir}/libcpupower.*
diff --git a/tools/power/cpupower/bench/Makefile b/tools/power/cpupower/bench/Makefile
index 7ec7021a29cd..d0f879b223fc 100644
--- a/tools/power/cpupower/bench/Makefile
+++ b/tools/power/cpupower/bench/Makefile
@@ -5,9 +5,15 @@ ifneq ($(O),)
endif
endif
+ifeq ($(strip $(STATIC)),true)
+LIBS = -L../ -L$(OUTPUT) -lm
+OBJS = $(OUTPUT)main.o $(OUTPUT)parse.o $(OUTPUT)system.o $(OUTPUT)benchmark.o \
+ $(OUTPUT)../lib/cpufreq.o $(OUTPUT)../lib/sysfs.o
+else
LIBS = -L../ -L$(OUTPUT) -lm -lcpupower
-
OBJS = $(OUTPUT)main.o $(OUTPUT)parse.o $(OUTPUT)system.o $(OUTPUT)benchmark.o
+endif
+
CFLAGS += -D_GNU_SOURCE -I../lib -DDEFAULT_CONFIG_FILE=\"$(confdir)/cpufreq-bench.conf\"
$(OUTPUT)%.o : %.c
diff --git a/tools/power/cpupower/utils/cpufreq-info.c b/tools/power/cpupower/utils/cpufreq-info.c
index 0e6764330241..8f3f5bb9c74e 100644
--- a/tools/power/cpupower/utils/cpufreq-info.c
+++ b/tools/power/cpupower/utils/cpufreq-info.c
@@ -14,6 +14,7 @@
#include <getopt.h>
#include "cpufreq.h"
+#include "helpers/sysfs.h"
#include "helpers/helpers.h"
#include "helpers/bitmask.h"
@@ -244,149 +245,21 @@ static int get_boost_mode(unsigned int cpu)
return 0;
}
-static void debug_output_one(unsigned int cpu)
-{
- char *driver;
- struct cpufreq_affected_cpus *cpus;
- struct cpufreq_available_frequencies *freqs;
- unsigned long min, max, freq_kernel, freq_hardware;
- unsigned long total_trans, latency;
- unsigned long long total_time;
- struct cpufreq_policy *policy;
- struct cpufreq_available_governors *governors;
- struct cpufreq_stats *stats;
-
- if (cpufreq_cpu_exists(cpu))
- return;
-
- freq_kernel = cpufreq_get_freq_kernel(cpu);
- freq_hardware = cpufreq_get_freq_hardware(cpu);
-
- driver = cpufreq_get_driver(cpu);
- if (!driver) {
- printf(_(" no or unknown cpufreq driver is active on this CPU\n"));
- } else {
- printf(_(" driver: %s\n"), driver);
- cpufreq_put_driver(driver);
- }
-
- cpus = cpufreq_get_related_cpus(cpu);
- if (cpus) {
- printf(_(" CPUs which run at the same hardware frequency: "));
- while (cpus->next) {
- printf("%d ", cpus->cpu);
- cpus = cpus->next;
- }
- printf("%d\n", cpus->cpu);
- cpufreq_put_related_cpus(cpus);
- }
-
- cpus = cpufreq_get_affected_cpus(cpu);
- if (cpus) {
- printf(_(" CPUs which need to have their frequency coordinated by software: "));
- while (cpus->next) {
- printf("%d ", cpus->cpu);
- cpus = cpus->next;
- }
- printf("%d\n", cpus->cpu);
- cpufreq_put_affected_cpus(cpus);
- }
-
- latency = cpufreq_get_transition_latency(cpu);
- if (latency) {
- printf(_(" maximum transition latency: "));
- print_duration(latency);
- printf(".\n");
- }
-
- if (!(cpufreq_get_hardware_limits(cpu, &min, &max))) {
- printf(_(" hardware limits: "));
- print_speed(min);
- printf(" - ");
- print_speed(max);
- printf("\n");
- }
-
- freqs = cpufreq_get_available_frequencies(cpu);
- if (freqs) {
- printf(_(" available frequency steps: "));
- while (freqs->next) {
- print_speed(freqs->frequency);
- printf(", ");
- freqs = freqs->next;
- }
- print_speed(freqs->frequency);
- printf("\n");
- cpufreq_put_available_frequencies(freqs);
- }
-
- governors = cpufreq_get_available_governors(cpu);
- if (governors) {
- printf(_(" available cpufreq governors: "));
- while (governors->next) {
- printf("%s, ", governors->governor);
- governors = governors->next;
- }
- printf("%s\n", governors->governor);
- cpufreq_put_available_governors(governors);
- }
-
- policy = cpufreq_get_policy(cpu);
- if (policy) {
- printf(_(" current policy: frequency should be within "));
- print_speed(policy->min);
- printf(_(" and "));
- print_speed(policy->max);
-
- printf(".\n ");
- printf(_("The governor \"%s\" may"
- " decide which speed to use\n within this range.\n"),
- policy->governor);
- cpufreq_put_policy(policy);
- }
-
- if (freq_kernel || freq_hardware) {
- printf(_(" current CPU frequency is "));
- if (freq_hardware) {
- print_speed(freq_hardware);
- printf(_(" (asserted by call to hardware)"));
- } else
- print_speed(freq_kernel);
- printf(".\n");
- }
- stats = cpufreq_get_stats(cpu, &total_time);
- if (stats) {
- printf(_(" cpufreq stats: "));
- while (stats) {
- print_speed(stats->frequency);
- printf(":%.2f%%", (100.0 * stats->time_in_state) / total_time);
- stats = stats->next;
- if (stats)
- printf(", ");
- }
- cpufreq_put_stats(stats);
- total_trans = cpufreq_get_transitions(cpu);
- if (total_trans)
- printf(" (%lu)\n", total_trans);
- else
- printf("\n");
- }
- get_boost_mode(cpu);
-
-}
-
/* --freq / -f */
static int get_freq_kernel(unsigned int cpu, unsigned int human)
{
unsigned long freq = cpufreq_get_freq_kernel(cpu);
- if (!freq)
+ printf(_(" current CPU frequency: "));
+ if (!freq) {
+ printf(_(" Unable to call to kernel\n"));
return -EINVAL;
+ }
if (human) {
print_speed(freq);
- printf("\n");
} else
- printf("%lu\n", freq);
+ printf("%lu", freq);
+ printf(_(" (asserted by call to kernel)\n"));
return 0;
}
@@ -396,13 +269,16 @@ static int get_freq_kernel(unsigned int cpu, unsigned int human)
static int get_freq_hardware(unsigned int cpu, unsigned int human)
{
unsigned long freq = cpufreq_get_freq_hardware(cpu);
- if (!freq)
+ printf(_(" current CPU frequency: "));
+ if (!freq) {
+ printf("Unable to call hardware\n");
return -EINVAL;
+ }
if (human) {
print_speed(freq);
- printf("\n");
} else
- printf("%lu\n", freq);
+ printf("%lu", freq);
+ printf(_(" (asserted by call to hardware)\n"));
return 0;
}
@@ -411,9 +287,17 @@ static int get_freq_hardware(unsigned int cpu, unsigned int human)
static int get_hardware_limits(unsigned int cpu)
{
unsigned long min, max;
- if (cpufreq_get_hardware_limits(cpu, &min, &max))
+
+ printf(_(" hardware limits: "));
+ if (cpufreq_get_hardware_limits(cpu, &min, &max)) {
+ printf(_("Not Available\n"));
return -EINVAL;
- printf("%lu %lu\n", min, max);
+ }
+
+ print_speed(min);
+ printf(" - ");
+ print_speed(max);
+ printf("\n");
return 0;
}
@@ -422,9 +306,11 @@ static int get_hardware_limits(unsigned int cpu)
static int get_driver(unsigned int cpu)
{
char *driver = cpufreq_get_driver(cpu);
- if (!driver)
+ if (!driver) {
+ printf(_(" no or unknown cpufreq driver is active on this CPU\n"));
return -EINVAL;
- printf("%s\n", driver);
+ }
+ printf(" driver: %s\n", driver);
cpufreq_put_driver(driver);
return 0;
}
@@ -434,9 +320,19 @@ static int get_driver(unsigned int cpu)
static int get_policy(unsigned int cpu)
{
struct cpufreq_policy *policy = cpufreq_get_policy(cpu);
- if (!policy)
+ if (!policy) {
+ printf(_(" Unable to determine current policy\n"));
return -EINVAL;
- printf("%lu %lu %s\n", policy->min, policy->max, policy->governor);
+ }
+ printf(_(" current policy: frequency should be within "));
+ print_speed(policy->min);
+ printf(_(" and "));
+ print_speed(policy->max);
+
+ printf(".\n ");
+ printf(_("The governor \"%s\" may decide which speed to use\n"
+ " within this range.\n"),
+ policy->governor);
cpufreq_put_policy(policy);
return 0;
}
@@ -447,8 +343,12 @@ static int get_available_governors(unsigned int cpu)
{
struct cpufreq_available_governors *governors =
cpufreq_get_available_governors(cpu);
- if (!governors)
+
+ printf(_(" available cpufreq governors: "));
+ if (!governors) {
+ printf(_("Not Available\n"));
return -EINVAL;
+ }
while (governors->next) {
printf("%s ", governors->governor);
@@ -465,8 +365,12 @@ static int get_available_governors(unsigned int cpu)
static int get_affected_cpus(unsigned int cpu)
{
struct cpufreq_affected_cpus *cpus = cpufreq_get_affected_cpus(cpu);
- if (!cpus)
+
+ printf(_(" CPUs which need to have their frequency coordinated by software: "));
+ if (!cpus) {
+ printf(_("Not Available\n"));
return -EINVAL;
+ }
while (cpus->next) {
printf("%d ", cpus->cpu);
@@ -482,8 +386,12 @@ static int get_affected_cpus(unsigned int cpu)
static int get_related_cpus(unsigned int cpu)
{
struct cpufreq_affected_cpus *cpus = cpufreq_get_related_cpus(cpu);
- if (!cpus)
+
+ printf(_(" CPUs which run at the same hardware frequency: "));
+ if (!cpus) {
+ printf(_("Not Available\n"));
return -EINVAL;
+ }
while (cpus->next) {
printf("%d ", cpus->cpu);
@@ -524,8 +432,12 @@ static int get_freq_stats(unsigned int cpu, unsigned int human)
static int get_latency(unsigned int cpu, unsigned int human)
{
unsigned long latency = cpufreq_get_transition_latency(cpu);
- if (!latency)
+
+ printf(_(" maximum transition latency: "));
+ if (!latency || latency == UINT_MAX) {
+ printf(_(" Cannot determine or is not supported.\n"));
return -EINVAL;
+ }
if (human) {
print_duration(latency);
@@ -535,6 +447,36 @@ static int get_latency(unsigned int cpu, unsigned int human)
return 0;
}
+static void debug_output_one(unsigned int cpu)
+{
+ struct cpufreq_available_frequencies *freqs;
+
+ get_driver(cpu);
+ get_related_cpus(cpu);
+ get_affected_cpus(cpu);
+ get_latency(cpu, 1);
+ get_hardware_limits(cpu);
+
+ freqs = cpufreq_get_available_frequencies(cpu);
+ if (freqs) {
+ printf(_(" available frequency steps: "));
+ while (freqs->next) {
+ print_speed(freqs->frequency);
+ printf(", ");
+ freqs = freqs->next;
+ }
+ print_speed(freqs->frequency);
+ printf("\n");
+ cpufreq_put_available_frequencies(freqs);
+ }
+
+ get_available_governors(cpu);
+ get_policy(cpu);
+ if (get_freq_hardware(cpu, 1) < 0)
+ get_freq_kernel(cpu, 1);
+ get_boost_mode(cpu);
+}
+
static struct option info_opts[] = {
{"debug", no_argument, NULL, 'e'},
{"boost", no_argument, NULL, 'b'},
@@ -647,11 +589,14 @@ int cmd_freq_info(int argc, char **argv)
if (!bitmask_isbitset(cpus_chosen, cpu))
continue;
- if (cpufreq_cpu_exists(cpu)) {
- printf(_("couldn't analyze CPU %d as it doesn't seem to be present\n"), cpu);
+
+ printf(_("analyzing CPU %d:\n"), cpu);
+
+ if (sysfs_is_cpu_online(cpu) != 1) {
+ printf(_(" *is offline\n"));
+ printf("\n");
continue;
}
- printf(_("analyzing CPU %d:\n"), cpu);
switch (output_param) {
case 'b':
@@ -693,6 +638,7 @@ int cmd_freq_info(int argc, char **argv)
}
if (ret)
return ret;
+ printf("\n");
}
return ret;
}
diff --git a/tools/power/cpupower/utils/cpuidle-info.c b/tools/power/cpupower/utils/cpuidle-info.c
index 750c1d82c3f7..8bf8ab5ffa25 100644
--- a/tools/power/cpupower/utils/cpuidle-info.c
+++ b/tools/power/cpupower/utils/cpuidle-info.c
@@ -12,7 +12,6 @@
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
-#include <cpufreq.h>
#include "helpers/helpers.h"
#include "helpers/sysfs.h"
@@ -25,8 +24,6 @@ static void cpuidle_cpu_output(unsigned int cpu, int verbose)
unsigned int idlestates, idlestate;
char *tmp;
- printf(_ ("Analyzing CPU %d:\n"), cpu);
-
idlestates = sysfs_get_idlestate_count(cpu);
if (idlestates == 0) {
printf(_("CPU %u: No idle states\n"), cpu);
@@ -71,7 +68,6 @@ static void cpuidle_cpu_output(unsigned int cpu, int verbose)
printf(_("Duration: %llu\n"),
sysfs_get_idlestate_time(cpu, idlestate));
}
- printf("\n");
}
static void cpuidle_general_output(void)
@@ -189,10 +185,17 @@ int cmd_idle_info(int argc, char **argv)
for (cpu = bitmask_first(cpus_chosen);
cpu <= bitmask_last(cpus_chosen); cpu++) {
- if (!bitmask_isbitset(cpus_chosen, cpu) ||
- cpufreq_cpu_exists(cpu))
+ if (!bitmask_isbitset(cpus_chosen, cpu))
continue;
+ printf(_("analyzing CPU %d:\n"), cpu);
+
+ if (sysfs_is_cpu_online(cpu) != 1) {
+ printf(_(" *is offline\n"));
+ printf("\n");
+ continue;
+ }
+
switch (output_param) {
case 'o':
@@ -203,6 +206,7 @@ int cmd_idle_info(int argc, char **argv)
cpuidle_cpu_output(cpu, verbose);
break;
}
+ printf("\n");
}
return EXIT_SUCCESS;
}
diff --git a/tools/power/cpupower/utils/cpupower-info.c b/tools/power/cpupower/utils/cpupower-info.c
index 10299f2e9d2a..c7caa8eaa6d0 100644
--- a/tools/power/cpupower/utils/cpupower-info.c
+++ b/tools/power/cpupower/utils/cpupower-info.c
@@ -12,7 +12,6 @@
#include <string.h>
#include <getopt.h>
-#include <cpufreq.h>
#include "helpers/helpers.h"
#include "helpers/sysfs.h"
@@ -83,12 +82,16 @@ int cmd_info(int argc, char **argv)
for (cpu = bitmask_first(cpus_chosen);
cpu <= bitmask_last(cpus_chosen); cpu++) {
- if (!bitmask_isbitset(cpus_chosen, cpu) ||
- cpufreq_cpu_exists(cpu))
+ if (!bitmask_isbitset(cpus_chosen, cpu))
continue;
printf(_("analyzing CPU %d:\n"), cpu);
+ if (sysfs_is_cpu_online(cpu) != 1){
+ printf(_(" *is offline\n"));
+ continue;
+ }
+
if (params.perf_bias) {
ret = msr_intel_get_perf_bias(cpu);
if (ret < 0) {
diff --git a/tools/power/cpupower/utils/cpupower-set.c b/tools/power/cpupower/utils/cpupower-set.c
index 3e6f374f8dd7..532f46b9a335 100644
--- a/tools/power/cpupower/utils/cpupower-set.c
+++ b/tools/power/cpupower/utils/cpupower-set.c
@@ -12,7 +12,6 @@
#include <string.h>
#include <getopt.h>
-#include <cpufreq.h>
#include "helpers/helpers.h"
#include "helpers/sysfs.h"
#include "helpers/bitmask.h"
@@ -78,10 +77,15 @@ int cmd_set(int argc, char **argv)
for (cpu = bitmask_first(cpus_chosen);
cpu <= bitmask_last(cpus_chosen); cpu++) {
- if (!bitmask_isbitset(cpus_chosen, cpu) ||
- cpufreq_cpu_exists(cpu))
+ if (!bitmask_isbitset(cpus_chosen, cpu))
continue;
+ if (sysfs_is_cpu_online(cpu) != 1){
+ fprintf(stderr, _("Cannot set values on CPU %d:"), cpu);
+ fprintf(stderr, _(" *is offline\n"));
+ continue;
+ }
+
if (params.perf_bias) {
ret = msr_intel_set_perf_bias(cpu, perf_bias);
if (ret) {
diff --git a/tools/power/cpupower/utils/helpers/topology.c b/tools/power/cpupower/utils/helpers/topology.c
index 9cbb7fd75171..5f9c908f4557 100644
--- a/tools/power/cpupower/utils/helpers/topology.c
+++ b/tools/power/cpupower/utils/helpers/topology.c
@@ -106,7 +106,7 @@ int get_cpu_topology(struct cpupower_topology *cpu_top)
cpu_top->pkgs++;
}
}
- if (!cpu_top->core_info[0].pkg == -1)
+ if (!(cpu_top->core_info[0].pkg == -1))
cpu_top->pkgs++;
/* Intel's cores count is not consecutively numbered, there may
diff --git a/tools/perf/config/Makefile.arch b/tools/scripts/Makefile.arch
index e11fbd6fae78..e11fbd6fae78 100644
--- a/tools/perf/config/Makefile.arch
+++ b/tools/scripts/Makefile.arch
diff --git a/Documentation/spi/.gitignore b/tools/spi/.gitignore
index 4280576397e8..4280576397e8 100644
--- a/Documentation/spi/.gitignore
+++ b/tools/spi/.gitignore
diff --git a/tools/spi/Makefile b/tools/spi/Makefile
new file mode 100644
index 000000000000..cd0db62e4d9d
--- /dev/null
+++ b/tools/spi/Makefile
@@ -0,0 +1,4 @@
+all: spidev_test spidev_fdx
+
+clean:
+ $(RM) spidev_test spidev_fdx
diff --git a/Documentation/spi/spidev_fdx.c b/tools/spi/spidev_fdx.c
index 0ea3e51292fc..0ea3e51292fc 100644
--- a/Documentation/spi/spidev_fdx.c
+++ b/tools/spi/spidev_fdx.c
diff --git a/Documentation/spi/spidev_test.c b/tools/spi/spidev_test.c
index 135b3f592b83..8a73d8185316 100644
--- a/Documentation/spi/spidev_test.c
+++ b/tools/spi/spidev_test.c
@@ -19,6 +19,7 @@
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
+#include <sys/stat.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
@@ -33,6 +34,8 @@ static void pabort(const char *s)
static const char *device = "/dev/spidev1.1";
static uint32_t mode;
static uint8_t bits = 8;
+static char *input_file;
+static char *output_file;
static uint32_t speed = 500000;
static uint16_t delay;
static int verbose;
@@ -49,7 +52,8 @@ uint8_t default_tx[] = {
uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, };
char *input_tx;
-static void hex_dump(const void *src, size_t length, size_t line_size, char *prefix)
+static void hex_dump(const void *src, size_t length, size_t line_size,
+ char *prefix)
{
int i = 0;
const unsigned char *address = src;
@@ -83,13 +87,17 @@ static void hex_dump(const void *src, size_t length, size_t line_size, char *pre
static int unescape(char *_dst, char *_src, size_t len)
{
int ret = 0;
+ int match;
char *src = _src;
char *dst = _dst;
unsigned int ch;
while (*src) {
if (*src == '\\' && *(src+1) == 'x') {
- sscanf(src + 2, "%2x", &ch);
+ match = sscanf(src + 2, "%2x", &ch);
+ if (!match)
+ pabort("malformed input string");
+
src += 4;
*dst++ = (unsigned char)ch;
} else {
@@ -103,7 +111,7 @@ static int unescape(char *_dst, char *_src, size_t len)
static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
{
int ret;
-
+ int out_fd;
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
@@ -134,7 +142,21 @@ static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
if (verbose)
hex_dump(tx, len, 32, "TX");
- hex_dump(rx, len, 32, "RX");
+
+ if (output_file) {
+ out_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (out_fd < 0)
+ pabort("could not open output file");
+
+ ret = write(out_fd, rx, len);
+ if (ret != len)
+ pabort("not all bytes written to output file");
+
+ close(out_fd);
+ }
+
+ if (verbose || !output_file)
+ hex_dump(rx, len, 32, "RX");
}
static void print_usage(const char *prog)
@@ -143,7 +165,9 @@ static void print_usage(const char *prog)
puts(" -D --device device to use (default /dev/spidev1.1)\n"
" -s --speed max speed (Hz)\n"
" -d --delay delay (usec)\n"
- " -b --bpw bits per word \n"
+ " -b --bpw bits per word\n"
+ " -i --input input data from a file (e.g. \"test.bin\")\n"
+ " -o --output output data to a file (e.g. \"results.bin\")\n"
" -l --loop loopback\n"
" -H --cpha clock phase\n"
" -O --cpol clock polarity\n"
@@ -167,6 +191,8 @@ static void parse_opts(int argc, char *argv[])
{ "speed", 1, 0, 's' },
{ "delay", 1, 0, 'd' },
{ "bpw", 1, 0, 'b' },
+ { "input", 1, 0, 'i' },
+ { "output", 1, 0, 'o' },
{ "loop", 0, 0, 'l' },
{ "cpha", 0, 0, 'H' },
{ "cpol", 0, 0, 'O' },
@@ -182,7 +208,8 @@ static void parse_opts(int argc, char *argv[])
};
int c;
- c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR24p:v", lopts, NULL);
+ c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3NR24p:v",
+ lopts, NULL);
if (c == -1)
break;
@@ -200,6 +227,12 @@ static void parse_opts(int argc, char *argv[])
case 'b':
bits = atoi(optarg);
break;
+ case 'i':
+ input_file = optarg;
+ break;
+ case 'o':
+ output_file = optarg;
+ break;
case 'l':
mode |= SPI_LOOP;
break;
@@ -249,13 +282,63 @@ static void parse_opts(int argc, char *argv[])
}
}
+static void transfer_escaped_string(int fd, char *str)
+{
+ size_t size = strlen(str + 1);
+ uint8_t *tx;
+ uint8_t *rx;
+
+ tx = malloc(size);
+ if (!tx)
+ pabort("can't allocate tx buffer");
+
+ rx = malloc(size);
+ if (!rx)
+ pabort("can't allocate rx buffer");
+
+ size = unescape((char *)tx, str, size);
+ transfer(fd, tx, rx, size);
+ free(rx);
+ free(tx);
+}
+
+static void transfer_file(int fd, char *filename)
+{
+ ssize_t bytes;
+ struct stat sb;
+ int tx_fd;
+ uint8_t *tx;
+ uint8_t *rx;
+
+ if (stat(filename, &sb) == -1)
+ pabort("can't stat input file");
+
+ tx_fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ pabort("can't open input file");
+
+ tx = malloc(sb.st_size);
+ if (!tx)
+ pabort("can't allocate tx buffer");
+
+ rx = malloc(sb.st_size);
+ if (!rx)
+ pabort("can't allocate rx buffer");
+
+ bytes = read(tx_fd, tx, sb.st_size);
+ if (bytes != sb.st_size)
+ pabort("failed to read input file");
+
+ transfer(fd, tx, rx, sb.st_size);
+ free(rx);
+ free(tx);
+ close(tx_fd);
+}
+
int main(int argc, char *argv[])
{
int ret = 0;
int fd;
- uint8_t *tx;
- uint8_t *rx;
- int size;
parse_opts(argc, argv);
@@ -300,17 +383,15 @@ int main(int argc, char *argv[])
printf("bits per word: %d\n", bits);
printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
- if (input_tx) {
- size = strlen(input_tx+1);
- tx = malloc(size);
- rx = malloc(size);
- size = unescape((char *)tx, input_tx, size);
- transfer(fd, tx, rx, size);
- free(rx);
- free(tx);
- } else {
+ if (input_tx && input_file)
+ pabort("only one of -p and --input may be selected");
+
+ if (input_tx)
+ transfer_escaped_string(fd, input_tx);
+ else if (input_file)
+ transfer_file(fd, input_file);
+ else
transfer(fd, default_tx, default_rx, sizeof(default_tx));
- }
close(fd);
diff --git a/tools/testing/nvdimm/Kbuild b/tools/testing/nvdimm/Kbuild
index 38b00ecb2ed5..a34bfd0c8928 100644
--- a/tools/testing/nvdimm/Kbuild
+++ b/tools/testing/nvdimm/Kbuild
@@ -9,6 +9,8 @@ ldflags-y += --wrap=memunmap
ldflags-y += --wrap=__devm_request_region
ldflags-y += --wrap=__request_region
ldflags-y += --wrap=__release_region
+ldflags-y += --wrap=devm_memremap_pages
+ldflags-y += --wrap=phys_to_pfn_t
DRIVERS := ../../../drivers
NVDIMM_SRC := $(DRIVERS)/nvdimm
diff --git a/tools/testing/nvdimm/test/iomap.c b/tools/testing/nvdimm/test/iomap.c
index b7251314bbc0..7ec7df9e7fc7 100644
--- a/tools/testing/nvdimm/test/iomap.c
+++ b/tools/testing/nvdimm/test/iomap.c
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/types.h>
#include <linux/io.h>
+#include <linux/mm.h>
#include "nfit_test.h"
static LIST_HEAD(iomap_head);
@@ -41,7 +42,7 @@ void nfit_test_teardown(void)
}
EXPORT_SYMBOL(nfit_test_teardown);
-static struct nfit_test_resource *get_nfit_res(resource_size_t resource)
+static struct nfit_test_resource *__get_nfit_res(resource_size_t resource)
{
struct iomap_ops *ops;
@@ -51,14 +52,22 @@ static struct nfit_test_resource *get_nfit_res(resource_size_t resource)
return NULL;
}
-void __iomem *__nfit_test_ioremap(resource_size_t offset, unsigned long size,
- void __iomem *(*fallback_fn)(resource_size_t, unsigned long))
+static struct nfit_test_resource *get_nfit_res(resource_size_t resource)
{
- struct nfit_test_resource *nfit_res;
+ struct nfit_test_resource *res;
rcu_read_lock();
- nfit_res = get_nfit_res(offset);
+ res = __get_nfit_res(resource);
rcu_read_unlock();
+
+ return res;
+}
+
+void __iomem *__nfit_test_ioremap(resource_size_t offset, unsigned long size,
+ void __iomem *(*fallback_fn)(resource_size_t, unsigned long))
+{
+ struct nfit_test_resource *nfit_res = get_nfit_res(offset);
+
if (nfit_res)
return (void __iomem *) nfit_res->buf + offset
- nfit_res->res->start;
@@ -68,11 +77,8 @@ void __iomem *__nfit_test_ioremap(resource_size_t offset, unsigned long size,
void __iomem *__wrap_devm_ioremap_nocache(struct device *dev,
resource_size_t offset, unsigned long size)
{
- struct nfit_test_resource *nfit_res;
+ struct nfit_test_resource *nfit_res = get_nfit_res(offset);
- rcu_read_lock();
- nfit_res = get_nfit_res(offset);
- rcu_read_unlock();
if (nfit_res)
return (void __iomem *) nfit_res->buf + offset
- nfit_res->res->start;
@@ -83,25 +89,58 @@ EXPORT_SYMBOL(__wrap_devm_ioremap_nocache);
void *__wrap_devm_memremap(struct device *dev, resource_size_t offset,
size_t size, unsigned long flags)
{
- struct nfit_test_resource *nfit_res;
+ struct nfit_test_resource *nfit_res = get_nfit_res(offset);
- rcu_read_lock();
- nfit_res = get_nfit_res(offset);
- rcu_read_unlock();
if (nfit_res)
return nfit_res->buf + offset - nfit_res->res->start;
return devm_memremap(dev, offset, size, flags);
}
EXPORT_SYMBOL(__wrap_devm_memremap);
+#ifdef __HAVE_ARCH_PTE_DEVMAP
+#include <linux/memremap.h>
+#include <linux/pfn_t.h>
+
+void *__wrap_devm_memremap_pages(struct device *dev, struct resource *res,
+ struct percpu_ref *ref, struct vmem_altmap *altmap)
+{
+ resource_size_t offset = res->start;
+ struct nfit_test_resource *nfit_res = get_nfit_res(offset);
+
+ if (nfit_res)
+ return nfit_res->buf + offset - nfit_res->res->start;
+ return devm_memremap_pages(dev, res, ref, altmap);
+}
+EXPORT_SYMBOL(__wrap_devm_memremap_pages);
+
+pfn_t __wrap_phys_to_pfn_t(dma_addr_t addr, unsigned long flags)
+{
+ struct nfit_test_resource *nfit_res = get_nfit_res(addr);
+
+ if (nfit_res)
+ flags &= ~PFN_MAP;
+ return phys_to_pfn_t(addr, flags);
+}
+EXPORT_SYMBOL(__wrap_phys_to_pfn_t);
+#else
+/* to be removed post 4.5-rc1 */
+void *__wrap_devm_memremap_pages(struct device *dev, struct resource *res)
+{
+ resource_size_t offset = res->start;
+ struct nfit_test_resource *nfit_res = get_nfit_res(offset);
+
+ if (nfit_res)
+ return nfit_res->buf + offset - nfit_res->res->start;
+ return devm_memremap_pages(dev, res);
+}
+EXPORT_SYMBOL(__wrap_devm_memremap_pages);
+#endif
+
void *__wrap_memremap(resource_size_t offset, size_t size,
unsigned long flags)
{
- struct nfit_test_resource *nfit_res;
+ struct nfit_test_resource *nfit_res = get_nfit_res(offset);
- rcu_read_lock();
- nfit_res = get_nfit_res(offset);
- rcu_read_unlock();
if (nfit_res)
return nfit_res->buf + offset - nfit_res->res->start;
return memremap(offset, size, flags);
@@ -110,11 +149,8 @@ EXPORT_SYMBOL(__wrap_memremap);
void __wrap_devm_memunmap(struct device *dev, void *addr)
{
- struct nfit_test_resource *nfit_res;
+ struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
- rcu_read_lock();
- nfit_res = get_nfit_res((unsigned long) addr);
- rcu_read_unlock();
if (nfit_res)
return;
return devm_memunmap(dev, addr);
@@ -135,11 +171,7 @@ EXPORT_SYMBOL(__wrap_ioremap_wc);
void __wrap_iounmap(volatile void __iomem *addr)
{
- struct nfit_test_resource *nfit_res;
-
- rcu_read_lock();
- nfit_res = get_nfit_res((unsigned long) addr);
- rcu_read_unlock();
+ struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
if (nfit_res)
return;
return iounmap(addr);
@@ -148,11 +180,8 @@ EXPORT_SYMBOL(__wrap_iounmap);
void __wrap_memunmap(void *addr)
{
- struct nfit_test_resource *nfit_res;
+ struct nfit_test_resource *nfit_res = get_nfit_res((long) addr);
- rcu_read_lock();
- nfit_res = get_nfit_res((unsigned long) addr);
- rcu_read_unlock();
if (nfit_res)
return;
return memunmap(addr);
@@ -166,9 +195,7 @@ static struct resource *nfit_test_request_region(struct device *dev,
struct nfit_test_resource *nfit_res;
if (parent == &iomem_resource) {
- rcu_read_lock();
nfit_res = get_nfit_res(start);
- rcu_read_unlock();
if (nfit_res) {
struct resource *res = nfit_res->res + 1;
@@ -218,9 +245,7 @@ void __wrap___release_region(struct resource *parent, resource_size_t start,
struct nfit_test_resource *nfit_res;
if (parent == &iomem_resource) {
- rcu_read_lock();
nfit_res = get_nfit_res(start);
- rcu_read_unlock();
if (nfit_res) {
struct resource *res = nfit_res->res + 1;
diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c
index 40ab4476c80a..90bd2ea41032 100644
--- a/tools/testing/nvdimm/test/nfit.c
+++ b/tools/testing/nvdimm/test/nfit.c
@@ -248,6 +248,8 @@ static int nfit_test_cmd_ars_status(struct nd_cmd_ars_status *nd_cmd,
nd_cmd->out_length = 256;
nd_cmd->num_records = 0;
+ nd_cmd->address = 0;
+ nd_cmd->length = -1ULL;
nd_cmd->status = 0;
return 0;
@@ -420,8 +422,7 @@ static struct nfit_test_resource *nfit_test_lookup(resource_size_t addr)
static int nfit_test0_alloc(struct nfit_test *t)
{
- size_t nfit_size = sizeof(struct acpi_table_nfit)
- + sizeof(struct acpi_nfit_system_address) * NUM_SPA
+ size_t nfit_size = sizeof(struct acpi_nfit_system_address) * NUM_SPA
+ sizeof(struct acpi_nfit_memory_map) * NUM_MEM
+ sizeof(struct acpi_nfit_control_region) * NUM_DCR
+ sizeof(struct acpi_nfit_data_region) * NUM_BDW
@@ -471,8 +472,7 @@ static int nfit_test0_alloc(struct nfit_test *t)
static int nfit_test1_alloc(struct nfit_test *t)
{
- size_t nfit_size = sizeof(struct acpi_table_nfit)
- + sizeof(struct acpi_nfit_system_address)
+ size_t nfit_size = sizeof(struct acpi_nfit_system_address)
+ sizeof(struct acpi_nfit_memory_map)
+ sizeof(struct acpi_nfit_control_region);
@@ -488,39 +488,24 @@ static int nfit_test1_alloc(struct nfit_test *t)
return 0;
}
-static void nfit_test_init_header(struct acpi_table_nfit *nfit, size_t size)
-{
- memcpy(nfit->header.signature, ACPI_SIG_NFIT, 4);
- nfit->header.length = size;
- nfit->header.revision = 1;
- memcpy(nfit->header.oem_id, "LIBND", 6);
- memcpy(nfit->header.oem_table_id, "TEST", 5);
- nfit->header.oem_revision = 1;
- memcpy(nfit->header.asl_compiler_id, "TST", 4);
- nfit->header.asl_compiler_revision = 1;
-}
-
static void nfit_test0_setup(struct nfit_test *t)
{
struct nvdimm_bus_descriptor *nd_desc;
struct acpi_nfit_desc *acpi_desc;
struct acpi_nfit_memory_map *memdev;
void *nfit_buf = t->nfit_buf;
- size_t size = t->nfit_size;
struct acpi_nfit_system_address *spa;
struct acpi_nfit_control_region *dcr;
struct acpi_nfit_data_region *bdw;
struct acpi_nfit_flush_address *flush;
unsigned int offset;
- nfit_test_init_header(nfit_buf, size);
-
/*
* spa0 (interleave first half of dimm0 and dimm1, note storage
* does not actually alias the related block-data-window
* regions)
*/
- spa = nfit_buf + sizeof(struct acpi_table_nfit);
+ spa = nfit_buf;
spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
spa->header.length = sizeof(*spa);
memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_PM), 16);
@@ -533,7 +518,7 @@ static void nfit_test0_setup(struct nfit_test *t)
* does not actually alias the related block-data-window
* regions)
*/
- spa = nfit_buf + sizeof(struct acpi_table_nfit) + sizeof(*spa);
+ spa = nfit_buf + sizeof(*spa);
spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
spa->header.length = sizeof(*spa);
memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_PM), 16);
@@ -542,7 +527,7 @@ static void nfit_test0_setup(struct nfit_test *t)
spa->length = SPA1_SIZE;
/* spa2 (dcr0) dimm0 */
- spa = nfit_buf + sizeof(struct acpi_table_nfit) + sizeof(*spa) * 2;
+ spa = nfit_buf + sizeof(*spa) * 2;
spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
spa->header.length = sizeof(*spa);
memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16);
@@ -551,7 +536,7 @@ static void nfit_test0_setup(struct nfit_test *t)
spa->length = DCR_SIZE;
/* spa3 (dcr1) dimm1 */
- spa = nfit_buf + sizeof(struct acpi_table_nfit) + sizeof(*spa) * 3;
+ spa = nfit_buf + sizeof(*spa) * 3;
spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
spa->header.length = sizeof(*spa);
memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16);
@@ -560,7 +545,7 @@ static void nfit_test0_setup(struct nfit_test *t)
spa->length = DCR_SIZE;
/* spa4 (dcr2) dimm2 */
- spa = nfit_buf + sizeof(struct acpi_table_nfit) + sizeof(*spa) * 4;
+ spa = nfit_buf + sizeof(*spa) * 4;
spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
spa->header.length = sizeof(*spa);
memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16);
@@ -569,7 +554,7 @@ static void nfit_test0_setup(struct nfit_test *t)
spa->length = DCR_SIZE;
/* spa5 (dcr3) dimm3 */
- spa = nfit_buf + sizeof(struct acpi_table_nfit) + sizeof(*spa) * 5;
+ spa = nfit_buf + sizeof(*spa) * 5;
spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
spa->header.length = sizeof(*spa);
memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16);
@@ -578,7 +563,7 @@ static void nfit_test0_setup(struct nfit_test *t)
spa->length = DCR_SIZE;
/* spa6 (bdw for dcr0) dimm0 */
- spa = nfit_buf + sizeof(struct acpi_table_nfit) + sizeof(*spa) * 6;
+ spa = nfit_buf + sizeof(*spa) * 6;
spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
spa->header.length = sizeof(*spa);
memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16);
@@ -587,7 +572,7 @@ static void nfit_test0_setup(struct nfit_test *t)
spa->length = DIMM_SIZE;
/* spa7 (bdw for dcr1) dimm1 */
- spa = nfit_buf + sizeof(struct acpi_table_nfit) + sizeof(*spa) * 7;
+ spa = nfit_buf + sizeof(*spa) * 7;
spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
spa->header.length = sizeof(*spa);
memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16);
@@ -596,7 +581,7 @@ static void nfit_test0_setup(struct nfit_test *t)
spa->length = DIMM_SIZE;
/* spa8 (bdw for dcr2) dimm2 */
- spa = nfit_buf + sizeof(struct acpi_table_nfit) + sizeof(*spa) * 8;
+ spa = nfit_buf + sizeof(*spa) * 8;
spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
spa->header.length = sizeof(*spa);
memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16);
@@ -605,7 +590,7 @@ static void nfit_test0_setup(struct nfit_test *t)
spa->length = DIMM_SIZE;
/* spa9 (bdw for dcr3) dimm3 */
- spa = nfit_buf + sizeof(struct acpi_table_nfit) + sizeof(*spa) * 9;
+ spa = nfit_buf + sizeof(*spa) * 9;
spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
spa->header.length = sizeof(*spa);
memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16);
@@ -613,7 +598,7 @@ static void nfit_test0_setup(struct nfit_test *t)
spa->address = t->dimm_dma[3];
spa->length = DIMM_SIZE;
- offset = sizeof(struct acpi_table_nfit) + sizeof(*spa) * 10;
+ offset = sizeof(*spa) * 10;
/* mem-region0 (spa0, dimm0) */
memdev = nfit_buf + offset;
memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
@@ -1100,15 +1085,15 @@ static void nfit_test0_setup(struct nfit_test *t)
static void nfit_test1_setup(struct nfit_test *t)
{
- size_t size = t->nfit_size, offset;
+ size_t offset;
void *nfit_buf = t->nfit_buf;
struct acpi_nfit_memory_map *memdev;
struct acpi_nfit_control_region *dcr;
struct acpi_nfit_system_address *spa;
+ struct nvdimm_bus_descriptor *nd_desc;
+ struct acpi_nfit_desc *acpi_desc;
- nfit_test_init_header(nfit_buf, size);
-
- offset = sizeof(struct acpi_table_nfit);
+ offset = 0;
/* spa0 (flat range with no bdw aliasing) */
spa = nfit_buf + offset;
spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
@@ -1154,6 +1139,13 @@ static void nfit_test1_setup(struct nfit_test *t)
dcr->command_size = 0;
dcr->status_offset = 0;
dcr->status_size = 0;
+
+ acpi_desc = &t->acpi_desc;
+ set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_dsm_force_en);
+ set_bit(ND_CMD_ARS_START, &acpi_desc->bus_dsm_force_en);
+ set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_dsm_force_en);
+ nd_desc = &acpi_desc->nd_desc;
+ nd_desc->ndctl = nfit_test_ctl;
}
static int nfit_test_blk_do_io(struct nd_blk_region *ndbr, resource_size_t dpa,
diff --git a/tools/testing/selftests/ftrace/test.d/instances/instance.tc b/tools/testing/selftests/ftrace/test.d/instances/instance.tc
new file mode 100644
index 000000000000..773e276ff90b
--- /dev/null
+++ b/tools/testing/selftests/ftrace/test.d/instances/instance.tc
@@ -0,0 +1,90 @@
+#!/bin/sh
+# description: Test creation and deletion of trace instances
+
+if [ ! -d instances ] ; then
+ echo "no instance directory with this kernel"
+ exit_unsupported;
+fi
+
+fail() { # mesg
+ rmdir x y z 2>/dev/null
+ echo $1
+ set -e
+ exit $FAIL
+}
+
+cd instances
+
+# we don't want to fail on error
+set +e
+
+mkdir x
+rmdir x
+result=$?
+
+if [ $result -ne 0 ]; then
+ echo "instance rmdir not supported"
+ exit_unsupported
+fi
+
+instance_slam() {
+ while :; do
+ mkdir x
+ mkdir y
+ mkdir z
+ rmdir x
+ rmdir y
+ rmdir z
+ done 2>/dev/null
+}
+
+instance_slam &
+x=`jobs -l`
+p1=`echo $x | cut -d' ' -f2`
+echo $p1
+
+instance_slam &
+x=`jobs -l | tail -1`
+p2=`echo $x | cut -d' ' -f2`
+echo $p2
+
+instance_slam &
+x=`jobs -l | tail -1`
+p3=`echo $x | cut -d' ' -f2`
+echo $p3
+
+instance_slam &
+x=`jobs -l | tail -1`
+p4=`echo $x | cut -d' ' -f2`
+echo $p4
+
+instance_slam &
+x=`jobs -l | tail -1`
+p5=`echo $x | cut -d' ' -f2`
+echo $p5
+
+ls -lR >/dev/null
+sleep 1
+
+kill -1 $p1
+kill -1 $p2
+kill -1 $p3
+kill -1 $p4
+kill -1 $p5
+
+echo "Wait for processes to finish"
+wait $p1 $p2 $p3 $p4 $p5
+echo "all processes finished, wait for cleanup"
+
+mkdir x y z
+ls x y z
+rmdir x y z
+for d in x y z; do
+ if [ -d $d ]; then
+ fail "instance $d still exists"
+ fi
+done
+
+set -e
+
+exit 0
diff --git a/tools/testing/selftests/futex/README b/tools/testing/selftests/futex/README
index 3224a049b196..0558bb9ce0a6 100644
--- a/tools/testing/selftests/futex/README
+++ b/tools/testing/selftests/futex/README
@@ -27,7 +27,7 @@ o The build system shall remain as simple as possible, avoiding any archive or
o Where possible, any helper functions or other package-wide code shall be
implemented in header files, avoiding the need to compile intermediate object
files.
-o External dependendencies shall remain as minimal as possible. Currently gcc
+o External dependencies shall remain as minimal as possible. Currently gcc
and glibc are the only dependencies.
o Tests return 0 for success and < 0 for failure.
diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore
index 00326629d4af..6fb23366b258 100644
--- a/tools/testing/selftests/net/.gitignore
+++ b/tools/testing/selftests/net/.gitignore
@@ -1,3 +1,4 @@
socket
psock_fanout
psock_tpacket
+reuseport_bpf
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index fac4782c51d8..41449b5ad0a9 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -4,7 +4,7 @@ CFLAGS = -Wall -O2 -g
CFLAGS += -I../../../../usr/include/
-NET_PROGS = socket psock_fanout psock_tpacket
+NET_PROGS = socket psock_fanout psock_tpacket reuseport_bpf
all: $(NET_PROGS)
%: %.c
diff --git a/tools/testing/selftests/net/reuseport_bpf.c b/tools/testing/selftests/net/reuseport_bpf.c
new file mode 100644
index 000000000000..bec1b5dd2530
--- /dev/null
+++ b/tools/testing/selftests/net/reuseport_bpf.c
@@ -0,0 +1,514 @@
+/*
+ * Test functionality of BPF filters for SO_REUSEPORT. The tests below will use
+ * a BPF program (both classic and extended) to read the first word from an
+ * incoming packet (expected to be in network byte-order), calculate a modulus
+ * of that number, and then dispatch the packet to the Nth socket using the
+ * result. These tests are run for each supported address family and protocol.
+ * Additionally, a few edge cases in the implementation are tested.
+ */
+
+#include <errno.h>
+#include <error.h>
+#include <linux/bpf.h>
+#include <linux/filter.h>
+#include <linux/unistd.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#endif
+
+struct test_params {
+ int recv_family;
+ int send_family;
+ int protocol;
+ size_t recv_socks;
+ uint16_t recv_port;
+ uint16_t send_port_min;
+};
+
+static size_t sockaddr_size(void)
+{
+ return sizeof(struct sockaddr_storage);
+}
+
+static struct sockaddr *new_any_sockaddr(int family, uint16_t port)
+{
+ struct sockaddr_storage *addr;
+ struct sockaddr_in *addr4;
+ struct sockaddr_in6 *addr6;
+
+ addr = malloc(sizeof(struct sockaddr_storage));
+ memset(addr, 0, sizeof(struct sockaddr_storage));
+
+ switch (family) {
+ case AF_INET:
+ addr4 = (struct sockaddr_in *)addr;
+ addr4->sin_family = AF_INET;
+ addr4->sin_addr.s_addr = htonl(INADDR_ANY);
+ addr4->sin_port = htons(port);
+ break;
+ case AF_INET6:
+ addr6 = (struct sockaddr_in6 *)addr;
+ addr6->sin6_family = AF_INET6;
+ addr6->sin6_addr = in6addr_any;
+ addr6->sin6_port = htons(port);
+ break;
+ default:
+ error(1, 0, "Unsupported family %d", family);
+ }
+ return (struct sockaddr *)addr;
+}
+
+static struct sockaddr *new_loopback_sockaddr(int family, uint16_t port)
+{
+ struct sockaddr *addr = new_any_sockaddr(family, port);
+ struct sockaddr_in *addr4;
+ struct sockaddr_in6 *addr6;
+
+ switch (family) {
+ case AF_INET:
+ addr4 = (struct sockaddr_in *)addr;
+ addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ break;
+ case AF_INET6:
+ addr6 = (struct sockaddr_in6 *)addr;
+ addr6->sin6_addr = in6addr_loopback;
+ break;
+ default:
+ error(1, 0, "Unsupported family %d", family);
+ }
+ return addr;
+}
+
+static void attach_ebpf(int fd, uint16_t mod)
+{
+ static char bpf_log_buf[65536];
+ static const char bpf_license[] = "GPL";
+
+ int bpf_fd;
+ const struct bpf_insn prog[] = {
+ /* BPF_MOV64_REG(BPF_REG_6, BPF_REG_1) */
+ { BPF_ALU64 | BPF_MOV | BPF_X, BPF_REG_6, BPF_REG_1, 0, 0 },
+ /* BPF_LD_ABS(BPF_W, 0) R0 = (uint32_t)skb[0] */
+ { BPF_LD | BPF_ABS | BPF_W, 0, 0, 0, 0 },
+ /* BPF_ALU64_IMM(BPF_MOD, BPF_REG_0, mod) */
+ { BPF_ALU64 | BPF_MOD | BPF_K, BPF_REG_0, 0, 0, mod },
+ /* BPF_EXIT_INSN() */
+ { BPF_JMP | BPF_EXIT, 0, 0, 0, 0 }
+ };
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
+ attr.insn_cnt = ARRAY_SIZE(prog);
+ attr.insns = (uint64_t)prog;
+ attr.license = (uint64_t)bpf_license;
+ attr.log_buf = (uint64_t)bpf_log_buf;
+ attr.log_size = sizeof(bpf_log_buf);
+ attr.log_level = 1;
+ attr.kern_version = 0;
+
+ bpf_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
+ if (bpf_fd < 0)
+ error(1, errno, "ebpf error. log:\n%s\n", bpf_log_buf);
+
+ if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, &bpf_fd,
+ sizeof(bpf_fd)))
+ error(1, errno, "failed to set SO_ATTACH_REUSEPORT_EBPF");
+
+ close(bpf_fd);
+}
+
+static void attach_cbpf(int fd, uint16_t mod)
+{
+ struct sock_filter code[] = {
+ /* A = (uint32_t)skb[0] */
+ { BPF_LD | BPF_W | BPF_ABS, 0, 0, 0 },
+ /* A = A % mod */
+ { BPF_ALU | BPF_MOD, 0, 0, mod },
+ /* return A */
+ { BPF_RET | BPF_A, 0, 0, 0 },
+ };
+ struct sock_fprog p = {
+ .len = ARRAY_SIZE(code),
+ .filter = code,
+ };
+
+ if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &p, sizeof(p)))
+ error(1, errno, "failed to set SO_ATTACH_REUSEPORT_CBPF");
+}
+
+static void build_recv_group(const struct test_params p, int fd[], uint16_t mod,
+ void (*attach_bpf)(int, uint16_t))
+{
+ struct sockaddr * const addr =
+ new_any_sockaddr(p.recv_family, p.recv_port);
+ int i, opt;
+
+ for (i = 0; i < p.recv_socks; ++i) {
+ fd[i] = socket(p.recv_family, p.protocol, 0);
+ if (fd[i] < 0)
+ error(1, errno, "failed to create recv %d", i);
+
+ opt = 1;
+ if (setsockopt(fd[i], SOL_SOCKET, SO_REUSEPORT, &opt,
+ sizeof(opt)))
+ error(1, errno, "failed to set SO_REUSEPORT on %d", i);
+
+ if (i == 0)
+ attach_bpf(fd[i], mod);
+
+ if (bind(fd[i], addr, sockaddr_size()))
+ error(1, errno, "failed to bind recv socket %d", i);
+
+ if (p.protocol == SOCK_STREAM)
+ if (listen(fd[i], p.recv_socks * 10))
+ error(1, errno, "failed to listen on socket");
+ }
+ free(addr);
+}
+
+static void send_from(struct test_params p, uint16_t sport, char *buf,
+ size_t len)
+{
+ struct sockaddr * const saddr = new_any_sockaddr(p.send_family, sport);
+ struct sockaddr * const daddr =
+ new_loopback_sockaddr(p.send_family, p.recv_port);
+ const int fd = socket(p.send_family, p.protocol, 0);
+
+ if (fd < 0)
+ error(1, errno, "failed to create send socket");
+
+ if (bind(fd, saddr, sockaddr_size()))
+ error(1, errno, "failed to bind send socket");
+ if (connect(fd, daddr, sockaddr_size()))
+ error(1, errno, "failed to connect");
+
+ if (send(fd, buf, len, 0) < 0)
+ error(1, errno, "failed to send message");
+
+ close(fd);
+ free(saddr);
+ free(daddr);
+}
+
+static void test_recv_order(const struct test_params p, int fd[], int mod)
+{
+ char recv_buf[8], send_buf[8];
+ struct msghdr msg;
+ struct iovec recv_io = { recv_buf, 8 };
+ struct epoll_event ev;
+ int epfd, conn, i, sport, expected;
+ uint32_t data, ndata;
+
+ epfd = epoll_create(1);
+ if (epfd < 0)
+ error(1, errno, "failed to create epoll");
+ for (i = 0; i < p.recv_socks; ++i) {
+ ev.events = EPOLLIN;
+ ev.data.fd = fd[i];
+ if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd[i], &ev))
+ error(1, errno, "failed to register sock %d epoll", i);
+ }
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = &recv_io;
+ msg.msg_iovlen = 1;
+
+ for (data = 0; data < p.recv_socks * 2; ++data) {
+ sport = p.send_port_min + data;
+ ndata = htonl(data);
+ memcpy(send_buf, &ndata, sizeof(ndata));
+ send_from(p, sport, send_buf, sizeof(ndata));
+
+ i = epoll_wait(epfd, &ev, 1, -1);
+ if (i < 0)
+ error(1, errno, "epoll wait failed");
+
+ if (p.protocol == SOCK_STREAM) {
+ conn = accept(ev.data.fd, NULL, NULL);
+ if (conn < 0)
+ error(1, errno, "error accepting");
+ i = recvmsg(conn, &msg, 0);
+ close(conn);
+ } else {
+ i = recvmsg(ev.data.fd, &msg, 0);
+ }
+ if (i < 0)
+ error(1, errno, "recvmsg error");
+ if (i != sizeof(ndata))
+ error(1, 0, "expected size %zd got %d",
+ sizeof(ndata), i);
+
+ for (i = 0; i < p.recv_socks; ++i)
+ if (ev.data.fd == fd[i])
+ break;
+ memcpy(&ndata, recv_buf, sizeof(ndata));
+ fprintf(stderr, "Socket %d: %d\n", i, ntohl(ndata));
+
+ expected = (sport % mod);
+ if (i != expected)
+ error(1, 0, "expected socket %d", expected);
+ }
+}
+
+static void test_reuseport_ebpf(const struct test_params p)
+{
+ int i, fd[p.recv_socks];
+
+ fprintf(stderr, "Testing EBPF mod %zd...\n", p.recv_socks);
+ build_recv_group(p, fd, p.recv_socks, attach_ebpf);
+ test_recv_order(p, fd, p.recv_socks);
+
+ fprintf(stderr, "Reprograming, testing mod %zd...\n", p.recv_socks / 2);
+ attach_ebpf(fd[0], p.recv_socks / 2);
+ test_recv_order(p, fd, p.recv_socks / 2);
+
+ for (i = 0; i < p.recv_socks; ++i)
+ close(fd[i]);
+}
+
+static void test_reuseport_cbpf(const struct test_params p)
+{
+ int i, fd[p.recv_socks];
+
+ fprintf(stderr, "Testing CBPF mod %zd...\n", p.recv_socks);
+ build_recv_group(p, fd, p.recv_socks, attach_cbpf);
+ test_recv_order(p, fd, p.recv_socks);
+
+ fprintf(stderr, "Reprograming, testing mod %zd...\n", p.recv_socks / 2);
+ attach_cbpf(fd[0], p.recv_socks / 2);
+ test_recv_order(p, fd, p.recv_socks / 2);
+
+ for (i = 0; i < p.recv_socks; ++i)
+ close(fd[i]);
+}
+
+static void test_extra_filter(const struct test_params p)
+{
+ struct sockaddr * const addr =
+ new_any_sockaddr(p.recv_family, p.recv_port);
+ int fd1, fd2, opt;
+
+ fprintf(stderr, "Testing too many filters...\n");
+ fd1 = socket(p.recv_family, p.protocol, 0);
+ if (fd1 < 0)
+ error(1, errno, "failed to create socket 1");
+ fd2 = socket(p.recv_family, p.protocol, 0);
+ if (fd2 < 0)
+ error(1, errno, "failed to create socket 2");
+
+ opt = 1;
+ if (setsockopt(fd1, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)))
+ error(1, errno, "failed to set SO_REUSEPORT on socket 1");
+ if (setsockopt(fd2, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)))
+ error(1, errno, "failed to set SO_REUSEPORT on socket 2");
+
+ attach_ebpf(fd1, 10);
+ attach_ebpf(fd2, 10);
+
+ if (bind(fd1, addr, sockaddr_size()))
+ error(1, errno, "failed to bind recv socket 1");
+
+ if (!bind(fd2, addr, sockaddr_size()) && errno != EADDRINUSE)
+ error(1, errno, "bind socket 2 should fail with EADDRINUSE");
+
+ free(addr);
+}
+
+static void test_filter_no_reuseport(const struct test_params p)
+{
+ struct sockaddr * const addr =
+ new_any_sockaddr(p.recv_family, p.recv_port);
+ const char bpf_license[] = "GPL";
+ struct bpf_insn ecode[] = {
+ { BPF_ALU64 | BPF_MOV | BPF_K, BPF_REG_0, 0, 0, 10 },
+ { BPF_JMP | BPF_EXIT, 0, 0, 0, 0 }
+ };
+ struct sock_filter ccode[] = {{ BPF_RET | BPF_A, 0, 0, 0 }};
+ union bpf_attr eprog;
+ struct sock_fprog cprog;
+ int fd, bpf_fd;
+
+ fprintf(stderr, "Testing filters on non-SO_REUSEPORT socket...\n");
+
+ memset(&eprog, 0, sizeof(eprog));
+ eprog.prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
+ eprog.insn_cnt = ARRAY_SIZE(ecode);
+ eprog.insns = (uint64_t)ecode;
+ eprog.license = (uint64_t)bpf_license;
+ eprog.kern_version = 0;
+
+ memset(&cprog, 0, sizeof(cprog));
+ cprog.len = ARRAY_SIZE(ccode);
+ cprog.filter = ccode;
+
+
+ bpf_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &eprog, sizeof(eprog));
+ if (bpf_fd < 0)
+ error(1, errno, "ebpf error");
+ fd = socket(p.recv_family, p.protocol, 0);
+ if (fd < 0)
+ error(1, errno, "failed to create socket 1");
+
+ if (bind(fd, addr, sockaddr_size()))
+ error(1, errno, "failed to bind recv socket 1");
+
+ errno = 0;
+ if (!setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, &bpf_fd,
+ sizeof(bpf_fd)) || errno != EINVAL)
+ error(1, errno, "setsockopt should have returned EINVAL");
+
+ errno = 0;
+ if (!setsockopt(fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &cprog,
+ sizeof(cprog)) || errno != EINVAL)
+ error(1, errno, "setsockopt should have returned EINVAL");
+
+ free(addr);
+}
+
+static void test_filter_without_bind(void)
+{
+ int fd1, fd2;
+
+ fprintf(stderr, "Testing filter add without bind...\n");
+ fd1 = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd1 < 0)
+ error(1, errno, "failed to create socket 1");
+ fd2 = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd2 < 0)
+ error(1, errno, "failed to create socket 2");
+
+ attach_ebpf(fd1, 10);
+ attach_cbpf(fd2, 10);
+
+ close(fd1);
+ close(fd2);
+}
+
+
+int main(void)
+{
+ fprintf(stderr, "---- IPv4 UDP ----\n");
+ /* NOTE: UDP socket lookups traverse a different code path when there
+ * are > 10 sockets in a group. Run the bpf test through both paths.
+ */
+ test_reuseport_ebpf((struct test_params) {
+ .recv_family = AF_INET,
+ .send_family = AF_INET,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 10,
+ .recv_port = 8000,
+ .send_port_min = 9000});
+ test_reuseport_ebpf((struct test_params) {
+ .recv_family = AF_INET,
+ .send_family = AF_INET,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 20,
+ .recv_port = 8000,
+ .send_port_min = 9000});
+ test_reuseport_cbpf((struct test_params) {
+ .recv_family = AF_INET,
+ .send_family = AF_INET,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 10,
+ .recv_port = 8001,
+ .send_port_min = 9020});
+ test_reuseport_cbpf((struct test_params) {
+ .recv_family = AF_INET,
+ .send_family = AF_INET,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 20,
+ .recv_port = 8001,
+ .send_port_min = 9020});
+ test_extra_filter((struct test_params) {
+ .recv_family = AF_INET,
+ .protocol = SOCK_DGRAM,
+ .recv_port = 8002});
+ test_filter_no_reuseport((struct test_params) {
+ .recv_family = AF_INET,
+ .protocol = SOCK_DGRAM,
+ .recv_port = 8008});
+
+ fprintf(stderr, "---- IPv6 UDP ----\n");
+ test_reuseport_ebpf((struct test_params) {
+ .recv_family = AF_INET6,
+ .send_family = AF_INET6,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 10,
+ .recv_port = 8003,
+ .send_port_min = 9040});
+ test_reuseport_ebpf((struct test_params) {
+ .recv_family = AF_INET6,
+ .send_family = AF_INET6,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 20,
+ .recv_port = 8003,
+ .send_port_min = 9040});
+ test_reuseport_cbpf((struct test_params) {
+ .recv_family = AF_INET6,
+ .send_family = AF_INET6,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 10,
+ .recv_port = 8004,
+ .send_port_min = 9060});
+ test_reuseport_cbpf((struct test_params) {
+ .recv_family = AF_INET6,
+ .send_family = AF_INET6,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 20,
+ .recv_port = 8004,
+ .send_port_min = 9060});
+ test_extra_filter((struct test_params) {
+ .recv_family = AF_INET6,
+ .protocol = SOCK_DGRAM,
+ .recv_port = 8005});
+ test_filter_no_reuseport((struct test_params) {
+ .recv_family = AF_INET6,
+ .protocol = SOCK_DGRAM,
+ .recv_port = 8009});
+
+ fprintf(stderr, "---- IPv6 UDP w/ mapped IPv4 ----\n");
+ test_reuseport_ebpf((struct test_params) {
+ .recv_family = AF_INET6,
+ .send_family = AF_INET,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 20,
+ .recv_port = 8006,
+ .send_port_min = 9080});
+ test_reuseport_ebpf((struct test_params) {
+ .recv_family = AF_INET6,
+ .send_family = AF_INET,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 10,
+ .recv_port = 8006,
+ .send_port_min = 9080});
+ test_reuseport_cbpf((struct test_params) {
+ .recv_family = AF_INET6,
+ .send_family = AF_INET,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 10,
+ .recv_port = 8007,
+ .send_port_min = 9100});
+ test_reuseport_cbpf((struct test_params) {
+ .recv_family = AF_INET6,
+ .send_family = AF_INET,
+ .protocol = SOCK_DGRAM,
+ .recv_socks = 20,
+ .recv_port = 8007,
+ .send_port_min = 9100});
+
+
+ test_filter_without_bind();
+
+ fprintf(stderr, "SUCCESS\n");
+ return 0;
+}
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh
index 5236e073919d..0f80eefb0bfd 100755
--- a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh
+++ b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh
@@ -38,8 +38,6 @@
#
# Authors: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
-grace=120
-
T=/tmp/kvm-test-1-run.sh.$$
trap 'rm -rf $T' 0
touch $T
@@ -152,7 +150,7 @@ fi
qemu_args="`specify_qemu_cpus "$QEMU" "$qemu_args" "$cpu_count"`"
# Generate architecture-specific and interaction-specific qemu arguments
-qemu_args="$qemu_args `identify_qemu_args "$QEMU" "$builddir/console.log"`"
+qemu_args="$qemu_args `identify_qemu_args "$QEMU" "$resdir/console.log"`"
# Generate qemu -append arguments
qemu_append="`identify_qemu_append "$QEMU"`"
@@ -168,7 +166,7 @@ then
touch $resdir/buildonly
exit 0
fi
-echo "NOTE: $QEMU either did not run or was interactive" > $builddir/console.log
+echo "NOTE: $QEMU either did not run or was interactive" > $resdir/console.log
echo $QEMU $qemu_args -m 512 -kernel $resdir/bzImage -append \"$qemu_append $boot_args\" > $resdir/qemu-cmd
( $QEMU $qemu_args -m 512 -kernel $resdir/bzImage -append "$qemu_append $boot_args"; echo $? > $resdir/qemu-retval ) &
qemu_pid=$!
@@ -214,7 +212,7 @@ then
else
break
fi
- if test $kruntime -ge $((seconds + grace))
+ if test $kruntime -ge $((seconds + $TORTURE_SHUTDOWN_GRACE))
then
echo "!!! PID $qemu_pid hung at $kruntime vs. $seconds seconds" >> $resdir/Warnings 2>&1
kill -KILL $qemu_pid
@@ -224,6 +222,5 @@ then
done
fi
-cp $builddir/console.log $resdir
parse-torture.sh $resdir/console.log $title
parse-console.sh $resdir/console.log $title
diff --git a/tools/testing/selftests/rcutorture/bin/kvm.sh b/tools/testing/selftests/rcutorture/bin/kvm.sh
index f6483609ebc2..4a431767f77a 100755
--- a/tools/testing/selftests/rcutorture/bin/kvm.sh
+++ b/tools/testing/selftests/rcutorture/bin/kvm.sh
@@ -42,6 +42,7 @@ TORTURE_DEFCONFIG=defconfig
TORTURE_BOOT_IMAGE=""
TORTURE_INITRD="$KVM/initrd"; export TORTURE_INITRD
TORTURE_KMAKE_ARG=""
+TORTURE_SHUTDOWN_GRACE=180
TORTURE_SUITE=rcu
resdir=""
configs=""
@@ -149,6 +150,11 @@ do
resdir=$2
shift
;;
+ --shutdown-grace)
+ checkarg --shutdown-grace "(seconds)" "$#" "$2" '^[0-9]*$' '^error'
+ TORTURE_SHUTDOWN_GRACE=$2
+ shift
+ ;;
--torture)
checkarg --torture "(suite name)" "$#" "$2" '^\(lock\|rcu\)$' '^--'
TORTURE_SUITE=$2
@@ -266,6 +272,7 @@ TORTURE_KMAKE_ARG="$TORTURE_KMAKE_ARG"; export TORTURE_KMAKE_ARG
TORTURE_QEMU_CMD="$TORTURE_QEMU_CMD"; export TORTURE_QEMU_CMD
TORTURE_QEMU_INTERACTIVE="$TORTURE_QEMU_INTERACTIVE"; export TORTURE_QEMU_INTERACTIVE
TORTURE_QEMU_MAC="$TORTURE_QEMU_MAC"; export TORTURE_QEMU_MAC
+TORTURE_SHUTDOWN_GRACE="$TORTURE_SHUTDOWN_GRACE"; export TORTURE_SHUTDOWN_GRACE
TORTURE_SUITE="$TORTURE_SUITE"; export TORTURE_SUITE
if ! test -e $resdir
then
@@ -307,10 +314,10 @@ awk < $T/cfgcpu.pack \
}
# Dump out the scripting required to run one test batch.
-function dump(first, pastlast)
+function dump(first, pastlast, batchnum)
{
- print "echo ----Start batch: `date`";
- print "echo ----Start batch: `date` >> " rd "/log";
+ print "echo ----Start batch " batchnum ": `date`";
+ print "echo ----Start batch " batchnum ": `date` >> " rd "/log";
jn=1
for (j = first; j < pastlast; j++) {
builddir=KVM "/b" jn
@@ -371,25 +378,28 @@ END {
njobs = i;
nc = ncpus;
first = 0;
+ batchnum = 1;
# Each pass through the following loop considers one test.
for (i = 0; i < njobs; i++) {
if (ncpus == 0) {
# Sequential test specified, each test its own batch.
- dump(i, i + 1);
+ dump(i, i + 1, batchnum);
first = i;
+ batchnum++;
} else if (nc < cpus[i] && i != 0) {
# Out of CPUs, dump out a batch.
- dump(first, i);
+ dump(first, i, batchnum);
first = i;
nc = ncpus;
+ batchnum++;
}
# Account for the CPUs needed by the current test.
nc -= cpus[i];
}
# Dump the last batch.
if (ncpus != 0)
- dump(first, i);
+ dump(first, i, batchnum);
}' >> $T/script
cat << ___EOF___ >> $T/script
diff --git a/tools/testing/selftests/rcutorture/bin/parse-console.sh b/tools/testing/selftests/rcutorture/bin/parse-console.sh
index d8f35cf116be..844787a0d7be 100755
--- a/tools/testing/selftests/rcutorture/bin/parse-console.sh
+++ b/tools/testing/selftests/rcutorture/bin/parse-console.sh
@@ -24,9 +24,6 @@
#
# Authors: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
-T=/tmp/abat-chk-badness.sh.$$
-trap 'rm -f $T' 0
-
file="$1"
title="$2"
@@ -36,9 +33,41 @@ if grep -Pq '\x00' < $file
then
print_warning Console output contains nul bytes, old qemu still running?
fi
-egrep 'Badness|WARNING:|Warn|BUG|===========|Call Trace:|Oops:|Stall ended before state dump start' < $file | grep -v 'ODEBUG: ' | grep -v 'Warning: unable to open an initial console' > $T
-if test -s $T
+egrep 'Badness|WARNING:|Warn|BUG|===========|Call Trace:|Oops:|detected stalls on CPUs/tasks:|Stall ended before state dump start' < $file | grep -v 'ODEBUG: ' | grep -v 'Warning: unable to open an initial console' > $1.diags
+if test -s $1.diags
then
print_warning Assertion failure in $file $title
- cat $T
+ # cat $1.diags
+ summary=""
+ n_badness=`grep -c Badness $1`
+ if test "$n_badness" -ne 0
+ then
+ summary="$summary Badness: $n_badness"
+ fi
+ n_warn=`grep -v 'Warning: unable to open an initial console' $1 | egrep -c 'WARNING:|Warn'`
+ if test "$n_warn" -ne 0
+ then
+ summary="$summary Warnings: $n_warn"
+ fi
+ n_bugs=`egrep -c 'BUG|Oops:' $1`
+ if test "$n_bugs" -ne 0
+ then
+ summary="$summary Bugs: $n_bugs"
+ fi
+ n_calltrace=`grep -c 'Call Trace:' $1`
+ if test "$n_calltrace" -ne 0
+ then
+ summary="$summary Call Traces: $n_calltrace"
+ fi
+ n_lockdep=`grep -c =========== $1`
+ if test "$n_badness" -ne 0
+ then
+ summary="$summary lockdep: $n_badness"
+ fi
+ n_stalls=`egrep -c 'detected stalls on CPUs/tasks:|Stall ended before state dump start' $1`
+ if test "$n_stalls" -ne 0
+ then
+ summary="$summary Stalls: $n_stalls"
+ fi
+ print_warning Summary: $summary
fi
diff --git a/tools/testing/selftests/rcutorture/doc/TINY_RCU.txt b/tools/testing/selftests/rcutorture/doc/TINY_RCU.txt
index 9ef33a743b73..24396ae8355b 100644
--- a/tools/testing/selftests/rcutorture/doc/TINY_RCU.txt
+++ b/tools/testing/selftests/rcutorture/doc/TINY_RCU.txt
@@ -20,7 +20,6 @@ CONFIG_PROVE_RCU
CONFIG_NO_HZ_FULL_SYSIDLE
CONFIG_RCU_NOCB_CPU
-CONFIG_RCU_USER_QS
Meaningless for TINY_RCU.
diff --git a/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt b/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt
index 657f3a035488..4e2b1893d40d 100644
--- a/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt
+++ b/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt
@@ -72,10 +72,6 @@ CONFIG_RCU_TORTURE_TEST_RUNNABLE
Always used in KVM testing.
-CONFIG_RCU_USER_QS
-
- Redundant with CONFIG_NO_HZ_FULL.
-
CONFIG_PREEMPT_RCU
CONFIG_TREE_RCU
diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c
index e38cc54942db..b9453b838162 100644
--- a/tools/testing/selftests/seccomp/seccomp_bpf.c
+++ b/tools/testing/selftests/seccomp/seccomp_bpf.c
@@ -492,6 +492,9 @@ TEST_SIGNAL(KILL_one_arg_six, SIGSYS)
pid_t parent = getppid();
int fd;
void *map1, *map2;
+ int page_size = sysconf(_SC_PAGESIZE);
+
+ ASSERT_LT(0, page_size);
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
ASSERT_EQ(0, ret);
@@ -504,16 +507,16 @@ TEST_SIGNAL(KILL_one_arg_six, SIGSYS)
EXPECT_EQ(parent, syscall(__NR_getppid));
map1 = (void *)syscall(sysno,
- NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, fd, PAGE_SIZE);
+ NULL, page_size, PROT_READ, MAP_PRIVATE, fd, page_size);
EXPECT_NE(MAP_FAILED, map1);
/* mmap2() should never return. */
map2 = (void *)syscall(sysno,
- NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, fd, 0x0C0FFEE);
+ NULL, page_size, PROT_READ, MAP_PRIVATE, fd, 0x0C0FFEE);
EXPECT_EQ(MAP_FAILED, map2);
/* The test failed, so clean up the resources. */
- munmap(map1, PAGE_SIZE);
- munmap(map2, PAGE_SIZE);
+ munmap(map1, page_size);
+ munmap(map2, page_size);
close(fd);
}
@@ -1243,11 +1246,24 @@ TEST_F(TRACE_poke, getpid_runs_normally)
# error "Do not know how to find your architecture's registers and syscalls"
#endif
+/* Use PTRACE_GETREGS and PTRACE_SETREGS when available. This is useful for
+ * architectures without HAVE_ARCH_TRACEHOOK (e.g. User-mode Linux).
+ */
+#if defined(__x86_64__) || defined(__i386__)
+#define HAVE_GETREGS
+#endif
+
/* Architecture-specific syscall fetching routine. */
int get_syscall(struct __test_metadata *_metadata, pid_t tracee)
{
- struct iovec iov;
ARCH_REGS regs;
+#ifdef HAVE_GETREGS
+ EXPECT_EQ(0, ptrace(PTRACE_GETREGS, tracee, 0, &regs)) {
+ TH_LOG("PTRACE_GETREGS failed");
+ return -1;
+ }
+#else
+ struct iovec iov;
iov.iov_base = &regs;
iov.iov_len = sizeof(regs);
@@ -1255,6 +1271,7 @@ int get_syscall(struct __test_metadata *_metadata, pid_t tracee)
TH_LOG("PTRACE_GETREGSET failed");
return -1;
}
+#endif
return regs.SYSCALL_NUM;
}
@@ -1263,13 +1280,16 @@ int get_syscall(struct __test_metadata *_metadata, pid_t tracee)
void change_syscall(struct __test_metadata *_metadata,
pid_t tracee, int syscall)
{
- struct iovec iov;
int ret;
ARCH_REGS regs;
-
+#ifdef HAVE_GETREGS
+ ret = ptrace(PTRACE_GETREGS, tracee, 0, &regs);
+#else
+ struct iovec iov;
iov.iov_base = &regs;
iov.iov_len = sizeof(regs);
ret = ptrace(PTRACE_GETREGSET, tracee, NT_PRSTATUS, &iov);
+#endif
EXPECT_EQ(0, ret);
#if defined(__x86_64__) || defined(__i386__) || defined(__powerpc__) || \
@@ -1309,9 +1329,13 @@ void change_syscall(struct __test_metadata *_metadata,
if (syscall == -1)
regs.SYSCALL_RET = 1;
+#ifdef HAVE_GETREGS
+ ret = ptrace(PTRACE_SETREGS, tracee, 0, &regs);
+#else
iov.iov_base = &regs;
iov.iov_len = sizeof(regs);
ret = ptrace(PTRACE_SETREGSET, tracee, NT_PRSTATUS, &iov);
+#endif
EXPECT_EQ(0, ret);
}
diff --git a/tools/testing/selftests/timers/clocksource-switch.c b/tools/testing/selftests/timers/clocksource-switch.c
index 627ec7425f78..fd88e3025bed 100644
--- a/tools/testing/selftests/timers/clocksource-switch.c
+++ b/tools/testing/selftests/timers/clocksource-switch.c
@@ -97,7 +97,7 @@ int get_cur_clocksource(char *buf, size_t size)
int change_clocksource(char *clocksource)
{
int fd;
- size_t size;
+ ssize_t size;
fd = open("/sys/devices/system/clocksource/clocksource0/current_clocksource", O_WRONLY);
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index eabcff411984..d0c473f65850 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -4,9 +4,11 @@ include ../lib.mk
.PHONY: all all_32 all_64 warn_32bit_failure clean
-TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs ldt_gdt syscall_nt ptrace_syscall
+TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_syscall
TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault sigreturn test_syscall_vdso unwind_vdso \
- test_FCMOV test_FCOMI test_FISTTP
+ test_FCMOV test_FCOMI test_FISTTP \
+ ldt_gdt \
+ vdso_restorer
TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY)
BINARIES_32 := $(TARGETS_C_32BIT_ALL:%=%_32)
diff --git a/tools/testing/selftests/x86/vdso_restorer.c b/tools/testing/selftests/x86/vdso_restorer.c
new file mode 100644
index 000000000000..cb038424a403
--- /dev/null
+++ b/tools/testing/selftests/x86/vdso_restorer.c
@@ -0,0 +1,88 @@
+/*
+ * vdso_restorer.c - tests vDSO-based signal restore
+ * 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.
+ *
+ * This makes sure that sa_restorer == NULL keeps working on 32-bit
+ * configurations. Modern glibc doesn't use it under any circumstances,
+ * so it's easy to overlook breakage.
+ *
+ * 64-bit userspace has never supported sa_restorer == NULL, so this is
+ * 32-bit only.
+ */
+
+#define _GNU_SOURCE
+
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <syscall.h>
+#include <sys/syscall.h>
+
+/* Open-code this -- the headers are too messy to easily use them. */
+struct real_sigaction {
+ void *handler;
+ unsigned long flags;
+ void *restorer;
+ unsigned int mask[2];
+};
+
+static volatile sig_atomic_t handler_called;
+
+static void handler_with_siginfo(int sig, siginfo_t *info, void *ctx_void)
+{
+ handler_called = 1;
+}
+
+static void handler_without_siginfo(int sig)
+{
+ handler_called = 1;
+}
+
+int main()
+{
+ int nerrs = 0;
+ struct real_sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.handler = handler_with_siginfo;
+ sa.flags = SA_SIGINFO;
+ sa.restorer = NULL; /* request kernel-provided restorer */
+
+ if (syscall(SYS_rt_sigaction, SIGUSR1, &sa, NULL, 8) != 0)
+ err(1, "raw rt_sigaction syscall");
+
+ raise(SIGUSR1);
+
+ if (handler_called) {
+ printf("[OK]\tSA_SIGINFO handler returned successfully\n");
+ } else {
+ printf("[FAIL]\tSA_SIGINFO handler was not called\n");
+ nerrs++;
+ }
+
+ sa.flags = 0;
+ sa.handler = handler_without_siginfo;
+ if (syscall(SYS_sigaction, SIGUSR1, &sa, 0) != 0)
+ err(1, "raw sigaction syscall");
+ handler_called = 0;
+
+ raise(SIGUSR1);
+
+ if (handler_called) {
+ printf("[OK]\t!SA_SIGINFO handler returned successfully\n");
+ } else {
+ printf("[FAIL]\t!SA_SIGINFO handler was not called\n");
+ nerrs++;
+ }
+}
diff --git a/tools/virtio/linux/kernel.h b/tools/virtio/linux/kernel.h
index 0a3da64638ce..4db7d5691ba7 100644
--- a/tools/virtio/linux/kernel.h
+++ b/tools/virtio/linux/kernel.h
@@ -110,4 +110,10 @@ static inline void free_page(unsigned long addr)
(void) (&_min1 == &_min2); \
_min1 < _min2 ? _min1 : _min2; })
+/* TODO: empty stubs for now. Broken but enough for virtio_ring.c */
+#define list_add_tail(a, b) do {} while (0)
+#define list_del(a) do {} while (0)
+#define list_for_each_entry(a, b, c) while (0)
+/* end of stubs */
+
#endif /* KERNEL_H */
diff --git a/tools/virtio/linux/virtio.h b/tools/virtio/linux/virtio.h
index a3e07016a440..ee125e714053 100644
--- a/tools/virtio/linux/virtio.h
+++ b/tools/virtio/linux/virtio.h
@@ -3,12 +3,6 @@
#include <linux/scatterlist.h>
#include <linux/kernel.h>
-/* TODO: empty stubs for now. Broken but enough for virtio_ring.c */
-#define list_add_tail(a, b) do {} while (0)
-#define list_del(a) do {} while (0)
-#define list_for_each_entry(a, b, c) while (0)
-/* end of stubs */
-
struct virtio_device {
void *dev;
u64 features;
diff --git a/tools/virtio/linux/virtio_config.h b/tools/virtio/linux/virtio_config.h
index 806d683ab107..57a6964a1e35 100644
--- a/tools/virtio/linux/virtio_config.h
+++ b/tools/virtio/linux/virtio_config.h
@@ -40,33 +40,39 @@ static inline void __virtio_clear_bit(struct virtio_device *vdev,
#define virtio_has_feature(dev, feature) \
(__virtio_test_bit((dev), feature))
+static inline bool virtio_is_little_endian(struct virtio_device *vdev)
+{
+ return virtio_has_feature(vdev, VIRTIO_F_VERSION_1) ||
+ virtio_legacy_is_little_endian();
+}
+
+/* Memory accessors */
static inline u16 virtio16_to_cpu(struct virtio_device *vdev, __virtio16 val)
{
- return __virtio16_to_cpu(virtio_has_feature(vdev, VIRTIO_F_VERSION_1), val);
+ return __virtio16_to_cpu(virtio_is_little_endian(vdev), val);
}
static inline __virtio16 cpu_to_virtio16(struct virtio_device *vdev, u16 val)
{
- return __cpu_to_virtio16(virtio_has_feature(vdev, VIRTIO_F_VERSION_1), val);
+ return __cpu_to_virtio16(virtio_is_little_endian(vdev), val);
}
static inline u32 virtio32_to_cpu(struct virtio_device *vdev, __virtio32 val)
{
- return __virtio32_to_cpu(virtio_has_feature(vdev, VIRTIO_F_VERSION_1), val);
+ return __virtio32_to_cpu(virtio_is_little_endian(vdev), val);
}
static inline __virtio32 cpu_to_virtio32(struct virtio_device *vdev, u32 val)
{
- return __cpu_to_virtio32(virtio_has_feature(vdev, VIRTIO_F_VERSION_1), val);
+ return __cpu_to_virtio32(virtio_is_little_endian(vdev), val);
}
static inline u64 virtio64_to_cpu(struct virtio_device *vdev, __virtio64 val)
{
- return __virtio64_to_cpu(virtio_has_feature(vdev, VIRTIO_F_VERSION_1), val);
+ return __virtio64_to_cpu(virtio_is_little_endian(vdev), val);
}
static inline __virtio64 cpu_to_virtio64(struct virtio_device *vdev, u64 val)
{
- return __cpu_to_virtio64(virtio_has_feature(vdev, VIRTIO_F_VERSION_1), val);
+ return __cpu_to_virtio64(virtio_is_little_endian(vdev), val);
}
-
diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
index 21a0ab2d8919..69bca185c471 100644
--- a/virt/kvm/arm/arch_timer.c
+++ b/virt/kvm/arm/arch_timer.c
@@ -221,17 +221,23 @@ void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
kvm_timer_update_state(vcpu);
/*
- * If we enter the guest with the virtual input level to the VGIC
- * asserted, then we have already told the VGIC what we need to, and
- * we don't need to exit from the guest until the guest deactivates
- * the already injected interrupt, so therefore we should set the
- * hardware active state to prevent unnecessary exits from the guest.
- *
- * Conversely, if the virtual input level is deasserted, then always
- * clear the hardware active state to ensure that hardware interrupts
- * from the timer triggers a guest exit.
- */
- if (timer->irq.level)
+ * If we enter the guest with the virtual input level to the VGIC
+ * asserted, then we have already told the VGIC what we need to, and
+ * we don't need to exit from the guest until the guest deactivates
+ * the already injected interrupt, so therefore we should set the
+ * hardware active state to prevent unnecessary exits from the guest.
+ *
+ * Also, if we enter the guest with the virtual timer interrupt active,
+ * then it must be active on the physical distributor, because we set
+ * the HW bit and the guest must be able to deactivate the virtual and
+ * physical interrupt at the same time.
+ *
+ * Conversely, if the virtual input level is deasserted and the virtual
+ * interrupt is not active, then always clear the hardware active state
+ * to ensure that hardware interrupts from the timer triggers a guest
+ * exit.
+ */
+ if (timer->irq.level || kvm_vgic_map_is_active(vcpu, timer->map))
phys_active = true;
else
phys_active = false;
diff --git a/virt/kvm/arm/vgic-v3.c b/virt/kvm/arm/vgic-v3.c
index 487d6357b7e7..453eafd4dd6e 100644
--- a/virt/kvm/arm/vgic-v3.c
+++ b/virt/kvm/arm/vgic-v3.c
@@ -28,6 +28,7 @@
#include <asm/kvm_emulate.h>
#include <asm/kvm_arm.h>
+#include <asm/kvm_asm.h>
#include <asm/kvm_mmu.h>
/* These are for GICv2 emulation only */
@@ -36,18 +37,12 @@
#define GICH_LR_PHYSID_CPUID (7UL << GICH_LR_PHYSID_CPUID_SHIFT)
#define ICH_LR_VIRTUALID_MASK (BIT_ULL(32) - 1)
-/*
- * LRs are stored in reverse order in memory. make sure we index them
- * correctly.
- */
-#define LR_INDEX(lr) (VGIC_V3_MAX_LRS - 1 - lr)
-
static u32 ich_vtr_el2;
static struct vgic_lr vgic_v3_get_lr(const struct kvm_vcpu *vcpu, int lr)
{
struct vgic_lr lr_desc;
- u64 val = vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[LR_INDEX(lr)];
+ u64 val = vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[VGIC_V3_LR_INDEX(lr)];
if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3)
lr_desc.irq = val & ICH_LR_VIRTUALID_MASK;
@@ -111,7 +106,7 @@ static void vgic_v3_set_lr(struct kvm_vcpu *vcpu, int lr,
lr_val |= ((u64)lr_desc.hwirq) << ICH_LR_PHYS_ID_SHIFT;
}
- vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[LR_INDEX(lr)] = lr_val;
+ vcpu->arch.vgic_cpu.vgic_v3.vgic_lr[VGIC_V3_LR_INDEX(lr)] = lr_val;
if (!(lr_desc.state & LR_STATE_MASK))
vcpu->arch.vgic_cpu.vgic_v3.vgic_elrsr |= (1U << lr);
diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c
index 533538385d5d..043032c6a5a4 100644
--- a/virt/kvm/arm/vgic.c
+++ b/virt/kvm/arm/vgic.c
@@ -878,7 +878,7 @@ static int vgic_handle_mmio_write(struct kvm_vcpu *vcpu,
true);
}
-struct kvm_io_device_ops vgic_io_ops = {
+static struct kvm_io_device_ops vgic_io_ops = {
.read = vgic_handle_mmio_read,
.write = vgic_handle_mmio_write,
};
@@ -1096,6 +1096,27 @@ static void vgic_retire_lr(int lr_nr, struct kvm_vcpu *vcpu)
vgic_set_lr(vcpu, lr_nr, vlr);
}
+static bool dist_active_irq(struct kvm_vcpu *vcpu)
+{
+ struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
+
+ return test_bit(vcpu->vcpu_id, dist->irq_active_on_cpu);
+}
+
+bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, struct irq_phys_map *map)
+{
+ int i;
+
+ for (i = 0; i < vcpu->arch.vgic_cpu.nr_lr; i++) {
+ struct vgic_lr vlr = vgic_get_lr(vcpu, i);
+
+ if (vlr.irq == map->virt_irq && vlr.state & LR_STATE_ACTIVE)
+ return true;
+ }
+
+ return vgic_irq_is_active(vcpu, map->virt_irq);
+}
+
/*
* An interrupt may have been disabled after being made pending on the
* CPU interface (the classic case is a timer running while we're
@@ -1248,7 +1269,7 @@ static void __kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
* may have been serviced from another vcpu. In all cases,
* move along.
*/
- if (!kvm_vgic_vcpu_pending_irq(vcpu) && !kvm_vgic_vcpu_active_irq(vcpu))
+ if (!kvm_vgic_vcpu_pending_irq(vcpu) && !dist_active_irq(vcpu))
goto epilog;
/* SGIs */
@@ -1396,25 +1417,13 @@ static bool vgic_process_maintenance(struct kvm_vcpu *vcpu)
static bool vgic_sync_hwirq(struct kvm_vcpu *vcpu, int lr, struct vgic_lr vlr)
{
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
- struct irq_phys_map *map;
- bool phys_active;
bool level_pending;
- int ret;
if (!(vlr.state & LR_HW))
return false;
- map = vgic_irq_map_search(vcpu, vlr.irq);
- BUG_ON(!map);
-
- ret = irq_get_irqchip_state(map->irq,
- IRQCHIP_STATE_ACTIVE,
- &phys_active);
-
- WARN_ON(ret);
-
- if (phys_active)
- return 0;
+ if (vlr.state & LR_STATE_ACTIVE)
+ return false;
spin_lock(&dist->lock);
level_pending = process_queued_irq(vcpu, lr, vlr);
@@ -1479,17 +1488,6 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
return test_bit(vcpu->vcpu_id, dist->irq_pending_on_cpu);
}
-int kvm_vgic_vcpu_active_irq(struct kvm_vcpu *vcpu)
-{
- struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
-
- if (!irqchip_in_kernel(vcpu->kvm))
- return 0;
-
- return test_bit(vcpu->vcpu_id, dist->irq_active_on_cpu);
-}
-
-
void vgic_kick_vcpus(struct kvm *kvm)
{
struct kvm_vcpu *vcpu;
diff --git a/virt/kvm/async_pf.c b/virt/kvm/async_pf.c
index 77d42be6970e..353159922456 100644
--- a/virt/kvm/async_pf.c
+++ b/virt/kvm/async_pf.c
@@ -57,8 +57,7 @@ int kvm_async_pf_init(void)
void kvm_async_pf_deinit(void)
{
- if (async_pf_cache)
- kmem_cache_destroy(async_pf_cache);
+ kmem_cache_destroy(async_pf_cache);
async_pf_cache = NULL;
}
diff --git a/virt/kvm/irqchip.c b/virt/kvm/irqchip.c
index f0b08a2a48ba..fe84e1a95dd5 100644
--- a/virt/kvm/irqchip.c
+++ b/virt/kvm/irqchip.c
@@ -166,6 +166,10 @@ out:
return r;
}
+void __attribute__((weak)) kvm_arch_irq_routing_update(struct kvm *kvm)
+{
+}
+
int kvm_set_irq_routing(struct kvm *kvm,
const struct kvm_irq_routing_entry *ue,
unsigned nr,
@@ -219,9 +223,10 @@ int kvm_set_irq_routing(struct kvm *kvm,
old = kvm->irq_routing;
rcu_assign_pointer(kvm->irq_routing, new);
kvm_irq_routing_update(kvm);
+ kvm_arch_irq_routing_update(kvm);
mutex_unlock(&kvm->irq_lock);
- kvm_arch_irq_routing_update(kvm);
+ kvm_arch_post_irq_routing_update(kvm);
synchronize_srcu_expedited(&kvm->irq_srcu);
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 484079efea5b..314c7774652e 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -206,16 +206,6 @@ void kvm_reload_remote_mmus(struct kvm *kvm)
kvm_make_all_cpus_request(kvm, KVM_REQ_MMU_RELOAD);
}
-void kvm_make_mclock_inprogress_request(struct kvm *kvm)
-{
- kvm_make_all_cpus_request(kvm, KVM_REQ_MCLOCK_INPROGRESS);
-}
-
-void kvm_make_scan_ioapic_request(struct kvm *kvm)
-{
- kvm_make_all_cpus_request(kvm, KVM_REQ_SCAN_IOAPIC);
-}
-
int kvm_vcpu_init(struct kvm_vcpu *vcpu, struct kvm *kvm, unsigned id)
{
struct page *page;
@@ -1164,15 +1154,15 @@ 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)
+bool kvm_is_visible_gfn(struct kvm *kvm, gfn_t gfn)
{
struct kvm_memory_slot *memslot = gfn_to_memslot(kvm, gfn);
if (!memslot || memslot->id >= KVM_USER_MEM_SLOTS ||
memslot->flags & KVM_MEMSLOT_INVALID)
- return 0;
+ return false;
- return 1;
+ return true;
}
EXPORT_SYMBOL_GPL(kvm_is_visible_gfn);
@@ -2257,7 +2247,7 @@ static int create_vcpu_fd(struct kvm_vcpu *vcpu)
static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, u32 id)
{
int r;
- struct kvm_vcpu *vcpu, *v;
+ struct kvm_vcpu *vcpu;
if (id >= KVM_MAX_VCPUS)
return -EINVAL;
@@ -2281,12 +2271,10 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, u32 id)
r = -EINVAL;
goto unlock_vcpu_destroy;
}
-
- kvm_for_each_vcpu(r, v, kvm)
- if (v->vcpu_id == id) {
- r = -EEXIST;
- goto unlock_vcpu_destroy;
- }
+ if (kvm_get_vcpu_by_id(kvm, id)) {
+ r = -EEXIST;
+ goto unlock_vcpu_destroy;
+ }
BUG_ON(kvm->vcpus[atomic_read(&kvm->online_vcpus)]);
@@ -3449,10 +3437,9 @@ static int kvm_init_debug(void)
goto out;
for (p = debugfs_entries; p->name; ++p) {
- p->dentry = debugfs_create_file(p->name, 0444, kvm_debugfs_dir,
- (void *)(long)p->offset,
- stat_fops[p->kind]);
- if (p->dentry == NULL)
+ if (!debugfs_create_file(p->name, 0444, kvm_debugfs_dir,
+ (void *)(long)p->offset,
+ stat_fops[p->kind]))
goto out_dir;
}
@@ -3464,15 +3451,6 @@ out:
return r;
}
-static void kvm_exit_debug(void)
-{
- struct kvm_stats_debugfs_item *p;
-
- for (p = debugfs_entries; p->name; ++p)
- debugfs_remove(p->dentry);
- debugfs_remove(kvm_debugfs_dir);
-}
-
static int kvm_suspend(void)
{
if (kvm_usage_count)
@@ -3630,7 +3608,7 @@ EXPORT_SYMBOL_GPL(kvm_init);
void kvm_exit(void)
{
- kvm_exit_debug();
+ debugfs_remove_recursive(kvm_debugfs_dir);
misc_deregister(&kvm_dev);
kmem_cache_destroy(kvm_vcpu_cache);
kvm_async_pf_deinit();